--- /dev/null
+//========================================================================
+//
+// Annot.cc
+//
+// Copyright 2000-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include "gmem.h"
+#include "Object.h"
+#include "Catalog.h"
+#include "Gfx.h"
+#include "Lexer.h"
+#include "Annot.h"
+
+//------------------------------------------------------------------------
+// Annot
+//------------------------------------------------------------------------
+
+Annot::Annot(XRef *xrefA, Dict *acroForm, Dict *dict) {
+ Object apObj, asObj, obj1, obj2;
+ GBool regen, isTextField;
+ double t;
+
+ ok = gFalse;
+ xref = xrefA;
+ appearBuf = NULL;
+
+ if (dict->lookup("Rect", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 4) {
+ //~ should check object types here
+ obj1.arrayGet(0, &obj2);
+ xMin = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(1, &obj2);
+ yMin = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(2, &obj2);
+ xMax = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(3, &obj2);
+ yMax = obj2.getNum();
+ obj2.free();
+ if (xMin > xMax) {
+ t = xMin; xMin = xMax; xMax = t;
+ }
+ if (yMin > yMax) {
+ t = yMin; yMin = yMax; yMax = t;
+ }
+ } else {
+ //~ this should return an error
+ xMin = yMin = 0;
+ xMax = yMax = 1;
+ }
+ obj1.free();
+
+ // check if field apperances need to be regenerated
+ regen = gFalse;
+ if (acroForm) {
+ acroForm->lookup("NeedAppearances", &obj1);
+ if (obj1.isBool() && obj1.getBool()) {
+ regen = gTrue;
+ }
+ obj1.free();
+ }
+
+ // check for a text-type field
+ isTextField = dict->lookup("FT", &obj1)->isName("Tx");
+ obj1.free();
+
+#if 0 //~ appearance stream generation is not finished yet
+ if (regen && isTextField) {
+ generateAppearance(acroForm, dict);
+ } else {
+#endif
+ if (dict->lookup("AP", &apObj)->isDict()) {
+ if (dict->lookup("AS", &asObj)->isName()) {
+ if (apObj.dictLookup("N", &obj1)->isDict()) {
+ if (obj1.dictLookupNF(asObj.getName(), &obj2)->isRef()) {
+ obj2.copy(&appearance);
+ ok = gTrue;
+ } else {
+ obj2.free();
+ if (obj1.dictLookupNF("Off", &obj2)->isRef()) {
+ obj2.copy(&appearance);
+ ok = gTrue;
+ }
+ }
+ obj2.free();
+ }
+ obj1.free();
+ } else {
+ if (apObj.dictLookupNF("N", &obj1)->isRef()) {
+ obj1.copy(&appearance);
+ ok = gTrue;
+ }
+ obj1.free();
+ }
+ asObj.free();
+ }
+ apObj.free();
+#if 0 //~ appearance stream generation is not finished yet
+ }
+#endif
+}
+
+Annot::~Annot() {
+ appearance.free();
+ if (appearBuf) {
+ delete appearBuf;
+ }
+}
+
+void Annot::generateAppearance(Dict *acroForm, Dict *dict) {
+ MemStream *appearStream;
+ Object daObj, vObj, drObj, appearDict, obj1, obj2;
+ GString *daStr, *daStr1, *vStr, *s;
+ char buf[256];
+ double fontSize;
+ int c;
+ int i0, i1;
+
+ //~ DA can be inherited
+ if (dict->lookup("DA", &daObj)->isString()) {
+ daStr = daObj.getString();
+
+ // look for a font size
+ //~ may want to parse the DS entry in place of this (if it exists)
+ daStr1 = NULL;
+ fontSize = 10;
+ for (i1 = daStr->getLength() - 2; i1 >= 0; --i1) {
+ if (daStr->getChar(i1) == 'T' && daStr->getChar(i1+1) == 'f') {
+ for (--i1; i1 >= 0 && Lexer::isSpace(daStr->getChar(i1)); --i1) ;
+ for (i0 = i1; i0 >= 0 && !Lexer::isSpace(daStr->getChar(i0)); --i0) ;
+ if (i0 >= 0) {
+ ++i0;
+ ++i1;
+ s = new GString(daStr, i0, i1 - i0);
+ fontSize = atof(s->getCString());
+ delete s;
+
+ // autosize the font
+ if (fontSize == 0) {
+ fontSize = 0.67 * (yMax - yMin);
+ daStr1 = new GString(daStr, 0, i0);
+ sprintf(buf, "%.2f", fontSize);
+ daStr1->append(buf);
+ daStr1->append(daStr->getCString() + i1,
+ daStr->getLength() - i1);
+ }
+ }
+ break;
+ }
+ }
+
+ // build the appearance stream contents
+ appearBuf = new GString();
+ appearBuf->append("/Tx BMC\n");
+ appearBuf->append("q BT\n");
+ appearBuf->append(daStr1 ? daStr1 : daStr)->append("\n");
+ if (dict->lookup("V", &vObj)->isString()) {
+ //~ handle quadding -- this requires finding the font and using
+ //~ the encoding and char widths
+ sprintf(buf, "1 0 0 1 %.2f %.2f Tm\n", 2.0, yMax - yMin - fontSize);
+ appearBuf->append(buf);
+ sprintf(buf, "%g TL\n", fontSize);
+ appearBuf->append(buf);
+ vStr = vObj.getString();
+ i0 = 0;
+ while (i0 < vStr->getLength()) {
+ for (i1 = i0;
+ i1 < vStr->getLength() &&
+ vStr->getChar(i1) != '\n' && vStr->getChar(i1) != '\r';
+ ++i1) ;
+ if (i0 > 0) {
+ appearBuf->append("T*\n");
+ }
+ appearBuf->append('(');
+ for (; i0 < i1; ++i0) {
+ c = vStr->getChar(i0);
+ if (c == '(' || c == ')' || c == '\\') {
+ appearBuf->append('\\');
+ appearBuf->append(c);
+ } else if (c < 0x20 || c >= 0x80) {
+ sprintf(buf, "\\%03o", c);
+ appearBuf->append(buf);
+ } else {
+ appearBuf->append(c);
+ }
+ }
+ appearBuf->append(") Tj\n");
+ if (i1 + 1 < vStr->getLength() &&
+ vStr->getChar(i1) == '\r' && vStr->getChar(i1 + 1) == '\n') {
+ i0 = i1 + 2;
+ } else {
+ i0 = i1 + 1;
+ }
+ }
+ }
+ vObj.free();
+ appearBuf->append("ET Q\n");
+ appearBuf->append("EMC\n");
+
+ // build the appearance stream dictionary
+ appearDict.initDict(xref);
+ appearDict.dictAdd(copyString("Length"),
+ obj1.initInt(appearBuf->getLength()));
+ appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
+ obj1.initArray(xref);
+ obj1.arrayAdd(obj2.initReal(0));
+ obj1.arrayAdd(obj2.initReal(0));
+ obj1.arrayAdd(obj2.initReal(xMax - xMin));
+ obj1.arrayAdd(obj2.initReal(yMax - yMin));
+ appearDict.dictAdd(copyString("BBox"), &obj1);
+
+ // find the resource dictionary
+ dict->lookup("DR", &drObj);
+ if (!drObj.isDict()) {
+ dict->lookup("Parent", &obj1);
+ while (obj1.isDict()) {
+ drObj.free();
+ obj1.dictLookup("DR", &drObj);
+ if (drObj.isDict()) {
+ break;
+ }
+ obj1.dictLookup("Parent", &obj2);
+ obj1.free();
+ obj1 = obj2;
+ }
+ obj1.free();
+ if (!drObj.isDict()) {
+ if (acroForm) {
+ drObj.free();
+ acroForm->lookup("DR", &drObj);
+ }
+ }
+ }
+ if (drObj.isDict()) {
+ appearDict.dictAdd(copyString("Resources"), drObj.copy(&obj1));
+ }
+ drObj.free();
+
+ // build the appearance stream
+ appearStream = new MemStream(appearBuf->getCString(), 0,
+ appearBuf->getLength(), &appearDict);
+ appearance.initStream(appearStream);
+ ok = gTrue;
+
+ if (daStr1) {
+ delete daStr1;
+ }
+ }
+ daObj.free();
+}
+
+void Annot::draw(Gfx *gfx) {
+ Object obj;
+
+ if (appearance.fetch(xref, &obj)->isStream()) {
+ gfx->doAnnot(&obj, xMin, yMin, xMax, yMax);
+ }
+ obj.free();
+}
+
+//------------------------------------------------------------------------
+// Annots
+//------------------------------------------------------------------------
+
+Annots::Annots(XRef *xref, Catalog *catalog, Object *annotsObj) {
+ Dict *acroForm;
+ Annot *annot;
+ Object obj1;
+ int size;
+ int i;
+
+ annots = NULL;
+ size = 0;
+ nAnnots = 0;
+
+ acroForm = catalog->getAcroForm()->isDict() ?
+ catalog->getAcroForm()->getDict() : NULL;
+ if (annotsObj->isArray()) {
+ for (i = 0; i < annotsObj->arrayGetLength(); ++i) {
+ if (annotsObj->arrayGet(i, &obj1)->isDict()) {
+ annot = new Annot(xref, acroForm, obj1.getDict());
+ if (annot->isOk()) {
+ if (nAnnots >= size) {
+ size += 16;
+ annots = (Annot **)greallocn(annots, size, sizeof(Annot *));
+ }
+ annots[nAnnots++] = annot;
+ } else {
+ delete annot;
+ }
+ }
+ obj1.free();
+ }
+ }
+}
+
+Annots::~Annots() {
+ int i;
+
+ for (i = 0; i < nAnnots; ++i) {
+ delete annots[i];
+ }
+ gfree(annots);
+}
--- /dev/null
+//========================================================================
+//
+// Annot.h
+//
+// Copyright 2000-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef ANNOT_H
+#define ANNOT_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+class XRef;
+class Catalog;
+class Gfx;
+
+//------------------------------------------------------------------------
+// Annot
+//------------------------------------------------------------------------
+
+class Annot {
+public:
+
+ Annot(XRef *xrefA, Dict *acroForm, Dict *dict);
+ ~Annot();
+ GBool isOk() { return ok; }
+
+ void draw(Gfx *gfx);
+
+ // Get appearance object.
+ Object *getAppearance(Object *obj) { return appearance.fetch(xref, obj); }
+
+private:
+
+ void generateAppearance(Dict *acroForm, Dict *dict);
+
+ XRef *xref; // the xref table for this PDF file
+ Object appearance; // a reference to the Form XObject stream
+ // for the normal appearance
+ GString *appearBuf;
+ double xMin, yMin, // annotation rectangle
+ xMax, yMax;
+ GBool ok;
+};
+
+//------------------------------------------------------------------------
+// Annots
+//------------------------------------------------------------------------
+
+class Annots {
+public:
+
+ // Extract non-link annotations from array of annotations.
+ Annots(XRef *xref, Catalog *catalog, Object *annotsObj);
+
+ ~Annots();
+
+ // Iterate through list of annotations.
+ int getNumAnnots() { return nAnnots; }
+ Annot *getAnnot(int i) { return annots[i]; }
+
+private:
+
+ Annot **annots;
+ int nAnnots;
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// Array.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <stddef.h>
+#include "gmem.h"
+#include "Object.h"
+#include "Array.h"
+
+//------------------------------------------------------------------------
+// Array
+//------------------------------------------------------------------------
+
+Array::Array(XRef *xrefA) {
+ xref = xrefA;
+ elems = NULL;
+ size = length = 0;
+ ref = 1;
+}
+
+Array::~Array() {
+ int i;
+
+ for (i = 0; i < length; ++i)
+ elems[i].free();
+ gfree(elems);
+}
+
+void Array::add(Object *elem) {
+ if (length == size) {
+ if (length == 0) {
+ size = 8;
+ } else {
+ size *= 2;
+ }
+ elems = (Object *)greallocn(elems, size, sizeof(Object));
+ }
+ elems[length] = *elem;
+ ++length;
+}
+
+Object *Array::get(int i, Object *obj) {
+ if (i < 0 || i >= length) {
+#ifdef DEBUG_MEM
+ abort();
+#else
+ return obj->initNull();
+#endif
+ }
+ return elems[i].fetch(xref, obj);
+}
+
+Object *Array::getNF(int i, Object *obj) {
+ if (i < 0 || i >= length) {
+#ifdef DEBUG_MEM
+ abort();
+#else
+ return obj->initNull();
+#endif
+ }
+ return elems[i].copy(obj);
+}
--- /dev/null
+//========================================================================
+//
+// Array.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef ARRAY_H
+#define ARRAY_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "Object.h"
+
+class XRef;
+
+//------------------------------------------------------------------------
+// Array
+//------------------------------------------------------------------------
+
+class Array {
+public:
+
+ // Constructor.
+ Array(XRef *xrefA);
+
+ // Destructor.
+ ~Array();
+
+ // Reference counting.
+ int incRef() { return ++ref; }
+ int decRef() { return --ref; }
+
+ // Get number of elements.
+ int getLength() { return length; }
+
+ // Add an element.
+ void add(Object *elem);
+
+ // Accessors.
+ Object *get(int i, Object *obj);
+ Object *getNF(int i, Object *obj);
+
+private:
+
+ XRef *xref; // the xref table for this PDF file
+ Object *elems; // array of elements
+ int size; // size of <elems> array
+ int length; // number of elements in array
+ int ref; // reference count
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// BuiltinFont.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "gmem.h"
+#include "FontEncodingTables.h"
+#include "BuiltinFont.h"
+
+//------------------------------------------------------------------------
+
+BuiltinFontWidths::BuiltinFontWidths(BuiltinFontWidth *widths, int sizeA) {
+ int i, h;
+
+ size = sizeA;
+ tab = (BuiltinFontWidth **)gmallocn(size, sizeof(BuiltinFontWidth *));
+ for (i = 0; i < size; ++i) {
+ tab[i] = NULL;
+ }
+ for (i = 0; i < sizeA; ++i) {
+ h = hash(widths[i].name);
+ widths[i].next = tab[h];
+ tab[h] = &widths[i];
+ }
+}
+
+BuiltinFontWidths::~BuiltinFontWidths() {
+ gfree(tab);
+}
+
+GBool BuiltinFontWidths::getWidth(char *name, Gushort *width) {
+ int h;
+ BuiltinFontWidth *p;
+
+ h = hash(name);
+ for (p = tab[h]; p; p = p->next) {
+ if (!strcmp(p->name, name)) {
+ *width = p->width;
+ return gTrue;
+ }
+ }
+ return gFalse;
+}
+
+int BuiltinFontWidths::hash(char *name) {
+ char *p;
+ unsigned int h;
+
+ h = 0;
+ for (p = name; *p; ++p) {
+ h = 17 * h + (int)(*p & 0xff);
+ }
+ return (int)(h % size);
+}
--- /dev/null
+//========================================================================
+//
+// BuiltinFont.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef BUILTINFONT_H
+#define BUILTINFONT_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+struct BuiltinFont;
+class BuiltinFontWidths;
+
+//------------------------------------------------------------------------
+
+struct BuiltinFont {
+ char *name;
+ char **defaultBaseEnc;
+ short ascent;
+ short descent;
+ short bbox[4];
+ BuiltinFontWidths *widths;
+};
+
+//------------------------------------------------------------------------
+
+struct BuiltinFontWidth {
+ char *name;
+ Gushort width;
+ BuiltinFontWidth *next;
+};
+
+class BuiltinFontWidths {
+public:
+
+ BuiltinFontWidths(BuiltinFontWidth *widths, int sizeA);
+ ~BuiltinFontWidths();
+ GBool getWidth(char *name, Gushort *width);
+
+private:
+
+ int hash(char *name);
+
+ BuiltinFontWidth **tab;
+ int size;
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// BuiltinFontTables.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include <stdlib.h>
+#include "FontEncodingTables.h"
+#include "BuiltinFontTables.h"
+
+static BuiltinFontWidth courierWidthsTab[] = {
+ { "Ntilde", 600, NULL },
+ { "rcaron", 600, NULL },
+ { "kcommaaccent", 600, NULL },
+ { "Ncommaaccent", 600, NULL },
+ { "Zacute", 600, NULL },
+ { "comma", 600, NULL },
+ { "cedilla", 600, NULL },
+ { "plusminus", 600, NULL },
+ { "circumflex", 600, NULL },
+ { "dotaccent", 600, NULL },
+ { "edotaccent", 600, NULL },
+ { "asciitilde", 600, NULL },
+ { "colon", 600, NULL },
+ { "onehalf", 600, NULL },
+ { "dollar", 600, NULL },
+ { "Lcaron", 600, NULL },
+ { "ntilde", 600, NULL },
+ { "Aogonek", 600, NULL },
+ { "ncommaaccent", 600, NULL },
+ { "minus", 600, NULL },
+ { "Iogonek", 600, NULL },
+ { "zacute", 600, NULL },
+ { "yen", 600, NULL },
+ { "space", 600, NULL },
+ { "Omacron", 600, NULL },
+ { "questiondown", 600, NULL },
+ { "emdash", 600, NULL },
+ { "Agrave", 600, NULL },
+ { "three", 600, NULL },
+ { "numbersign", 600, NULL },
+ { "lcaron", 600, NULL },
+ { "A", 600, NULL },
+ { "B", 600, NULL },
+ { "C", 600, NULL },
+ { "aogonek", 600, NULL },
+ { "D", 600, NULL },
+ { "E", 600, NULL },
+ { "onequarter", 600, NULL },
+ { "F", 600, NULL },
+ { "G", 600, NULL },
+ { "H", 600, NULL },
+ { "I", 600, NULL },
+ { "J", 600, NULL },
+ { "K", 600, NULL },
+ { "iogonek", 600, NULL },
+ { "L", 600, NULL },
+ { "backslash", 600, NULL },
+ { "periodcentered", 600, NULL },
+ { "M", 600, NULL },
+ { "N", 600, NULL },
+ { "omacron", 600, NULL },
+ { "Tcommaaccent", 600, NULL },
+ { "O", 600, NULL },
+ { "P", 600, NULL },
+ { "Q", 600, NULL },
+ { "Uhungarumlaut", 600, NULL },
+ { "R", 600, NULL },
+ { "Aacute", 600, NULL },
+ { "caron", 600, NULL },
+ { "S", 600, NULL },
+ { "T", 600, NULL },
+ { "U", 600, NULL },
+ { "agrave", 600, NULL },
+ { "V", 600, NULL },
+ { "W", 600, NULL },
+ { "equal", 600, NULL },
+ { "question", 600, NULL },
+ { "X", 600, NULL },
+ { "Y", 600, NULL },
+ { "Z", 600, NULL },
+ { "four", 600, NULL },
+ { "a", 600, NULL },
+ { "Gcommaaccent", 600, NULL },
+ { "b", 600, NULL },
+ { "c", 600, NULL },
+ { "d", 600, NULL },
+ { "e", 600, NULL },
+ { "f", 600, NULL },
+ { "g", 600, NULL },
+ { "bullet", 600, NULL },
+ { "h", 600, NULL },
+ { "i", 600, NULL },
+ { "Oslash", 600, NULL },
+ { "dagger", 600, NULL },
+ { "j", 600, NULL },
+ { "k", 600, NULL },
+ { "l", 600, NULL },
+ { "m", 600, NULL },
+ { "n", 600, NULL },
+ { "tcommaaccent", 600, NULL },
+ { "o", 600, NULL },
+ { "ordfeminine", 600, NULL },
+ { "ring", 600, NULL },
+ { "p", 600, NULL },
+ { "q", 600, NULL },
+ { "uhungarumlaut", 600, NULL },
+ { "r", 600, NULL },
+ { "twosuperior", 600, NULL },
+ { "aacute", 600, NULL },
+ { "s", 600, NULL },
+ { "OE", 600, NULL },
+ { "t", 600, NULL },
+ { "divide", 600, NULL },
+ { "u", 600, NULL },
+ { "Ccaron", 600, NULL },
+ { "v", 600, NULL },
+ { "w", 600, NULL },
+ { "x", 600, NULL },
+ { "y", 600, NULL },
+ { "z", 600, NULL },
+ { "Gbreve", 600, NULL },
+ { "commaaccent", 600, NULL },
+ { "hungarumlaut", 600, NULL },
+ { "Idotaccent", 600, NULL },
+ { "Nacute", 600, NULL },
+ { "quotedbl", 600, NULL },
+ { "gcommaaccent", 600, NULL },
+ { "mu", 600, NULL },
+ { "greaterequal", 600, NULL },
+ { "Scaron", 600, NULL },
+ { "Lslash", 600, NULL },
+ { "semicolon", 600, NULL },
+ { "oslash", 600, NULL },
+ { "lessequal", 600, NULL },
+ { "lozenge", 600, NULL },
+ { "parenright", 600, NULL },
+ { "ccaron", 600, NULL },
+ { "Ecircumflex", 600, NULL },
+ { "gbreve", 600, NULL },
+ { "trademark", 600, NULL },
+ { "daggerdbl", 600, NULL },
+ { "nacute", 600, NULL },
+ { "macron", 600, NULL },
+ { "Otilde", 600, NULL },
+ { "Emacron", 600, NULL },
+ { "ellipsis", 600, NULL },
+ { "scaron", 600, NULL },
+ { "AE", 600, NULL },
+ { "Ucircumflex", 600, NULL },
+ { "lslash", 600, NULL },
+ { "quotedblleft", 600, NULL },
+ { "hyphen", 600, NULL },
+ { "guilsinglright", 600, NULL },
+ { "quotesingle", 600, NULL },
+ { "eight", 600, NULL },
+ { "exclamdown", 600, NULL },
+ { "endash", 600, NULL },
+ { "oe", 600, NULL },
+ { "Abreve", 600, NULL },
+ { "Umacron", 600, NULL },
+ { "ecircumflex", 600, NULL },
+ { "Adieresis", 600, NULL },
+ { "copyright", 600, NULL },
+ { "Egrave", 600, NULL },
+ { "slash", 600, NULL },
+ { "Edieresis", 600, NULL },
+ { "otilde", 600, NULL },
+ { "Idieresis", 600, NULL },
+ { "parenleft", 600, NULL },
+ { "one", 600, NULL },
+ { "emacron", 600, NULL },
+ { "Odieresis", 600, NULL },
+ { "ucircumflex", 600, NULL },
+ { "bracketleft", 600, NULL },
+ { "Ugrave", 600, NULL },
+ { "quoteright", 600, NULL },
+ { "Udieresis", 600, NULL },
+ { "perthousand", 600, NULL },
+ { "Ydieresis", 600, NULL },
+ { "umacron", 600, NULL },
+ { "abreve", 600, NULL },
+ { "Eacute", 600, NULL },
+ { "adieresis", 600, NULL },
+ { "egrave", 600, NULL },
+ { "edieresis", 600, NULL },
+ { "idieresis", 600, NULL },
+ { "Eth", 600, NULL },
+ { "ae", 600, NULL },
+ { "asterisk", 600, NULL },
+ { "odieresis", 600, NULL },
+ { "Uacute", 600, NULL },
+ { "ugrave", 600, NULL },
+ { "five", 600, NULL },
+ { "nine", 600, NULL },
+ { "udieresis", 600, NULL },
+ { "Zcaron", 600, NULL },
+ { "Scommaaccent", 600, NULL },
+ { "threequarters", 600, NULL },
+ { "guillemotright", 600, NULL },
+ { "Ccedilla", 600, NULL },
+ { "ydieresis", 600, NULL },
+ { "tilde", 600, NULL },
+ { "at", 600, NULL },
+ { "eacute", 600, NULL },
+ { "underscore", 600, NULL },
+ { "Euro", 600, NULL },
+ { "Dcroat", 600, NULL },
+ { "zero", 600, NULL },
+ { "multiply", 600, NULL },
+ { "eth", 600, NULL },
+ { "Scedilla", 600, NULL },
+ { "Racute", 600, NULL },
+ { "Ograve", 600, NULL },
+ { "partialdiff", 600, NULL },
+ { "uacute", 600, NULL },
+ { "braceleft", 600, NULL },
+ { "Thorn", 600, NULL },
+ { "zcaron", 600, NULL },
+ { "scommaaccent", 600, NULL },
+ { "ccedilla", 600, NULL },
+ { "Dcaron", 600, NULL },
+ { "dcroat", 600, NULL },
+ { "scedilla", 600, NULL },
+ { "Oacute", 600, NULL },
+ { "Ocircumflex", 600, NULL },
+ { "ogonek", 600, NULL },
+ { "ograve", 600, NULL },
+ { "racute", 600, NULL },
+ { "Tcaron", 600, NULL },
+ { "Eogonek", 600, NULL },
+ { "thorn", 600, NULL },
+ { "degree", 600, NULL },
+ { "registered", 600, NULL },
+ { "radical", 600, NULL },
+ { "Aring", 600, NULL },
+ { "percent", 600, NULL },
+ { "six", 600, NULL },
+ { "paragraph", 600, NULL },
+ { "dcaron", 600, NULL },
+ { "Uogonek", 600, NULL },
+ { "two", 600, NULL },
+ { "summation", 600, NULL },
+ { "Igrave", 600, NULL },
+ { "Lacute", 600, NULL },
+ { "ocircumflex", 600, NULL },
+ { "oacute", 600, NULL },
+ { "Uring", 600, NULL },
+ { "Lcommaaccent", 600, NULL },
+ { "tcaron", 600, NULL },
+ { "eogonek", 600, NULL },
+ { "Delta", 600, NULL },
+ { "Ohungarumlaut", 600, NULL },
+ { "asciicircum", 600, NULL },
+ { "aring", 600, NULL },
+ { "grave", 600, NULL },
+ { "uogonek", 600, NULL },
+ { "bracketright", 600, NULL },
+ { "ampersand", 600, NULL },
+ { "Iacute", 600, NULL },
+ { "lacute", 600, NULL },
+ { "igrave", 600, NULL },
+ { "Ncaron", 600, NULL },
+ { "plus", 600, NULL },
+ { "uring", 600, NULL },
+ { "quotesinglbase", 600, NULL },
+ { "lcommaaccent", 600, NULL },
+ { "Yacute", 600, NULL },
+ { "ohungarumlaut", 600, NULL },
+ { "threesuperior", 600, NULL },
+ { "acute", 600, NULL },
+ { "section", 600, NULL },
+ { "dieresis", 600, NULL },
+ { "quotedblbase", 600, NULL },
+ { "iacute", 600, NULL },
+ { "ncaron", 600, NULL },
+ { "florin", 600, NULL },
+ { "yacute", 600, NULL },
+ { "Rcommaaccent", 600, NULL },
+ { "fi", 600, NULL },
+ { "fl", 600, NULL },
+ { "Acircumflex", 600, NULL },
+ { "Cacute", 600, NULL },
+ { "Icircumflex", 600, NULL },
+ { "guillemotleft", 600, NULL },
+ { "germandbls", 600, NULL },
+ { "seven", 600, NULL },
+ { "Amacron", 600, NULL },
+ { "Sacute", 600, NULL },
+ { "ordmasculine", 600, NULL },
+ { "dotlessi", 600, NULL },
+ { "sterling", 600, NULL },
+ { "notequal", 600, NULL },
+ { "Imacron", 600, NULL },
+ { "rcommaaccent", 600, NULL },
+ { "Zdotaccent", 600, NULL },
+ { "acircumflex", 600, NULL },
+ { "cacute", 600, NULL },
+ { "Ecaron", 600, NULL },
+ { "braceright", 600, NULL },
+ { "icircumflex", 600, NULL },
+ { "quotedblright", 600, NULL },
+ { "amacron", 600, NULL },
+ { "sacute", 600, NULL },
+ { "imacron", 600, NULL },
+ { "cent", 600, NULL },
+ { "currency", 600, NULL },
+ { "logicalnot", 600, NULL },
+ { "zdotaccent", 600, NULL },
+ { "Atilde", 600, NULL },
+ { "breve", 600, NULL },
+ { "bar", 600, NULL },
+ { "fraction", 600, NULL },
+ { "less", 600, NULL },
+ { "ecaron", 600, NULL },
+ { "guilsinglleft", 600, NULL },
+ { "exclam", 600, NULL },
+ { "period", 600, NULL },
+ { "Rcaron", 600, NULL },
+ { "Kcommaaccent", 600, NULL },
+ { "greater", 600, NULL },
+ { "atilde", 600, NULL },
+ { "brokenbar", 600, NULL },
+ { "quoteleft", 600, NULL },
+ { "Edotaccent", 600, NULL },
+ { "onesuperior", 600, NULL }
+};
+
+static BuiltinFontWidth courierBoldWidthsTab[] = {
+ { "Ntilde", 600, NULL },
+ { "rcaron", 600, NULL },
+ { "kcommaaccent", 600, NULL },
+ { "Ncommaaccent", 600, NULL },
+ { "Zacute", 600, NULL },
+ { "comma", 600, NULL },
+ { "cedilla", 600, NULL },
+ { "plusminus", 600, NULL },
+ { "circumflex", 600, NULL },
+ { "dotaccent", 600, NULL },
+ { "edotaccent", 600, NULL },
+ { "asciitilde", 600, NULL },
+ { "colon", 600, NULL },
+ { "onehalf", 600, NULL },
+ { "dollar", 600, NULL },
+ { "Lcaron", 600, NULL },
+ { "ntilde", 600, NULL },
+ { "Aogonek", 600, NULL },
+ { "ncommaaccent", 600, NULL },
+ { "minus", 600, NULL },
+ { "Iogonek", 600, NULL },
+ { "zacute", 600, NULL },
+ { "yen", 600, NULL },
+ { "space", 600, NULL },
+ { "Omacron", 600, NULL },
+ { "questiondown", 600, NULL },
+ { "emdash", 600, NULL },
+ { "Agrave", 600, NULL },
+ { "three", 600, NULL },
+ { "numbersign", 600, NULL },
+ { "lcaron", 600, NULL },
+ { "A", 600, NULL },
+ { "B", 600, NULL },
+ { "C", 600, NULL },
+ { "aogonek", 600, NULL },
+ { "D", 600, NULL },
+ { "E", 600, NULL },
+ { "onequarter", 600, NULL },
+ { "F", 600, NULL },
+ { "G", 600, NULL },
+ { "H", 600, NULL },
+ { "I", 600, NULL },
+ { "J", 600, NULL },
+ { "K", 600, NULL },
+ { "iogonek", 600, NULL },
+ { "backslash", 600, NULL },
+ { "L", 600, NULL },
+ { "periodcentered", 600, NULL },
+ { "M", 600, NULL },
+ { "N", 600, NULL },
+ { "omacron", 600, NULL },
+ { "Tcommaaccent", 600, NULL },
+ { "O", 600, NULL },
+ { "P", 600, NULL },
+ { "Q", 600, NULL },
+ { "Uhungarumlaut", 600, NULL },
+ { "R", 600, NULL },
+ { "Aacute", 600, NULL },
+ { "caron", 600, NULL },
+ { "S", 600, NULL },
+ { "T", 600, NULL },
+ { "U", 600, NULL },
+ { "agrave", 600, NULL },
+ { "V", 600, NULL },
+ { "W", 600, NULL },
+ { "X", 600, NULL },
+ { "question", 600, NULL },
+ { "equal", 600, NULL },
+ { "Y", 600, NULL },
+ { "Z", 600, NULL },
+ { "four", 600, NULL },
+ { "a", 600, NULL },
+ { "Gcommaaccent", 600, NULL },
+ { "b", 600, NULL },
+ { "c", 600, NULL },
+ { "d", 600, NULL },
+ { "e", 600, NULL },
+ { "f", 600, NULL },
+ { "g", 600, NULL },
+ { "bullet", 600, NULL },
+ { "h", 600, NULL },
+ { "i", 600, NULL },
+ { "Oslash", 600, NULL },
+ { "dagger", 600, NULL },
+ { "j", 600, NULL },
+ { "k", 600, NULL },
+ { "l", 600, NULL },
+ { "m", 600, NULL },
+ { "n", 600, NULL },
+ { "tcommaaccent", 600, NULL },
+ { "o", 600, NULL },
+ { "ordfeminine", 600, NULL },
+ { "ring", 600, NULL },
+ { "p", 600, NULL },
+ { "q", 600, NULL },
+ { "uhungarumlaut", 600, NULL },
+ { "r", 600, NULL },
+ { "twosuperior", 600, NULL },
+ { "aacute", 600, NULL },
+ { "s", 600, NULL },
+ { "OE", 600, NULL },
+ { "t", 600, NULL },
+ { "divide", 600, NULL },
+ { "u", 600, NULL },
+ { "Ccaron", 600, NULL },
+ { "v", 600, NULL },
+ { "w", 600, NULL },
+ { "x", 600, NULL },
+ { "y", 600, NULL },
+ { "z", 600, NULL },
+ { "Gbreve", 600, NULL },
+ { "commaaccent", 600, NULL },
+ { "hungarumlaut", 600, NULL },
+ { "Idotaccent", 600, NULL },
+ { "Nacute", 600, NULL },
+ { "quotedbl", 600, NULL },
+ { "gcommaaccent", 600, NULL },
+ { "mu", 600, NULL },
+ { "greaterequal", 600, NULL },
+ { "Scaron", 600, NULL },
+ { "Lslash", 600, NULL },
+ { "semicolon", 600, NULL },
+ { "oslash", 600, NULL },
+ { "lessequal", 600, NULL },
+ { "lozenge", 600, NULL },
+ { "parenright", 600, NULL },
+ { "ccaron", 600, NULL },
+ { "Ecircumflex", 600, NULL },
+ { "gbreve", 600, NULL },
+ { "trademark", 600, NULL },
+ { "daggerdbl", 600, NULL },
+ { "nacute", 600, NULL },
+ { "macron", 600, NULL },
+ { "Otilde", 600, NULL },
+ { "Emacron", 600, NULL },
+ { "ellipsis", 600, NULL },
+ { "scaron", 600, NULL },
+ { "AE", 600, NULL },
+ { "Ucircumflex", 600, NULL },
+ { "lslash", 600, NULL },
+ { "quotedblleft", 600, NULL },
+ { "guilsinglright", 600, NULL },
+ { "hyphen", 600, NULL },
+ { "quotesingle", 600, NULL },
+ { "eight", 600, NULL },
+ { "exclamdown", 600, NULL },
+ { "endash", 600, NULL },
+ { "oe", 600, NULL },
+ { "Abreve", 600, NULL },
+ { "Umacron", 600, NULL },
+ { "ecircumflex", 600, NULL },
+ { "Adieresis", 600, NULL },
+ { "copyright", 600, NULL },
+ { "Egrave", 600, NULL },
+ { "slash", 600, NULL },
+ { "Edieresis", 600, NULL },
+ { "otilde", 600, NULL },
+ { "Idieresis", 600, NULL },
+ { "parenleft", 600, NULL },
+ { "one", 600, NULL },
+ { "emacron", 600, NULL },
+ { "Odieresis", 600, NULL },
+ { "ucircumflex", 600, NULL },
+ { "bracketleft", 600, NULL },
+ { "Ugrave", 600, NULL },
+ { "quoteright", 600, NULL },
+ { "Udieresis", 600, NULL },
+ { "perthousand", 600, NULL },
+ { "Ydieresis", 600, NULL },
+ { "umacron", 600, NULL },
+ { "abreve", 600, NULL },
+ { "Eacute", 600, NULL },
+ { "adieresis", 600, NULL },
+ { "egrave", 600, NULL },
+ { "edieresis", 600, NULL },
+ { "idieresis", 600, NULL },
+ { "Eth", 600, NULL },
+ { "ae", 600, NULL },
+ { "asterisk", 600, NULL },
+ { "odieresis", 600, NULL },
+ { "Uacute", 600, NULL },
+ { "ugrave", 600, NULL },
+ { "nine", 600, NULL },
+ { "five", 600, NULL },
+ { "udieresis", 600, NULL },
+ { "Zcaron", 600, NULL },
+ { "Scommaaccent", 600, NULL },
+ { "threequarters", 600, NULL },
+ { "guillemotright", 600, NULL },
+ { "Ccedilla", 600, NULL },
+ { "ydieresis", 600, NULL },
+ { "tilde", 600, NULL },
+ { "at", 600, NULL },
+ { "eacute", 600, NULL },
+ { "underscore", 600, NULL },
+ { "Euro", 600, NULL },
+ { "Dcroat", 600, NULL },
+ { "multiply", 600, NULL },
+ { "zero", 600, NULL },
+ { "eth", 600, NULL },
+ { "Scedilla", 600, NULL },
+ { "Ograve", 600, NULL },
+ { "Racute", 600, NULL },
+ { "partialdiff", 600, NULL },
+ { "uacute", 600, NULL },
+ { "braceleft", 600, NULL },
+ { "Thorn", 600, NULL },
+ { "zcaron", 600, NULL },
+ { "scommaaccent", 600, NULL },
+ { "ccedilla", 600, NULL },
+ { "Dcaron", 600, NULL },
+ { "dcroat", 600, NULL },
+ { "Ocircumflex", 600, NULL },
+ { "Oacute", 600, NULL },
+ { "scedilla", 600, NULL },
+ { "ogonek", 600, NULL },
+ { "ograve", 600, NULL },
+ { "racute", 600, NULL },
+ { "Tcaron", 600, NULL },
+ { "Eogonek", 600, NULL },
+ { "thorn", 600, NULL },
+ { "degree", 600, NULL },
+ { "registered", 600, NULL },
+ { "radical", 600, NULL },
+ { "Aring", 600, NULL },
+ { "percent", 600, NULL },
+ { "six", 600, NULL },
+ { "paragraph", 600, NULL },
+ { "dcaron", 600, NULL },
+ { "Uogonek", 600, NULL },
+ { "two", 600, NULL },
+ { "summation", 600, NULL },
+ { "Igrave", 600, NULL },
+ { "Lacute", 600, NULL },
+ { "ocircumflex", 600, NULL },
+ { "oacute", 600, NULL },
+ { "Uring", 600, NULL },
+ { "Lcommaaccent", 600, NULL },
+ { "tcaron", 600, NULL },
+ { "eogonek", 600, NULL },
+ { "Delta", 600, NULL },
+ { "Ohungarumlaut", 600, NULL },
+ { "asciicircum", 600, NULL },
+ { "aring", 600, NULL },
+ { "grave", 600, NULL },
+ { "uogonek", 600, NULL },
+ { "bracketright", 600, NULL },
+ { "Iacute", 600, NULL },
+ { "ampersand", 600, NULL },
+ { "igrave", 600, NULL },
+ { "lacute", 600, NULL },
+ { "Ncaron", 600, NULL },
+ { "plus", 600, NULL },
+ { "uring", 600, NULL },
+ { "quotesinglbase", 600, NULL },
+ { "lcommaaccent", 600, NULL },
+ { "Yacute", 600, NULL },
+ { "ohungarumlaut", 600, NULL },
+ { "threesuperior", 600, NULL },
+ { "acute", 600, NULL },
+ { "section", 600, NULL },
+ { "dieresis", 600, NULL },
+ { "iacute", 600, NULL },
+ { "quotedblbase", 600, NULL },
+ { "ncaron", 600, NULL },
+ { "florin", 600, NULL },
+ { "yacute", 600, NULL },
+ { "Rcommaaccent", 600, NULL },
+ { "fi", 600, NULL },
+ { "fl", 600, NULL },
+ { "Acircumflex", 600, NULL },
+ { "Cacute", 600, NULL },
+ { "Icircumflex", 600, NULL },
+ { "guillemotleft", 600, NULL },
+ { "germandbls", 600, NULL },
+ { "Amacron", 600, NULL },
+ { "seven", 600, NULL },
+ { "Sacute", 600, NULL },
+ { "ordmasculine", 600, NULL },
+ { "dotlessi", 600, NULL },
+ { "sterling", 600, NULL },
+ { "notequal", 600, NULL },
+ { "Imacron", 600, NULL },
+ { "rcommaaccent", 600, NULL },
+ { "Zdotaccent", 600, NULL },
+ { "acircumflex", 600, NULL },
+ { "cacute", 600, NULL },
+ { "Ecaron", 600, NULL },
+ { "icircumflex", 600, NULL },
+ { "braceright", 600, NULL },
+ { "quotedblright", 600, NULL },
+ { "amacron", 600, NULL },
+ { "sacute", 600, NULL },
+ { "imacron", 600, NULL },
+ { "cent", 600, NULL },
+ { "currency", 600, NULL },
+ { "logicalnot", 600, NULL },
+ { "zdotaccent", 600, NULL },
+ { "Atilde", 600, NULL },
+ { "breve", 600, NULL },
+ { "bar", 600, NULL },
+ { "fraction", 600, NULL },
+ { "less", 600, NULL },
+ { "ecaron", 600, NULL },
+ { "guilsinglleft", 600, NULL },
+ { "exclam", 600, NULL },
+ { "period", 600, NULL },
+ { "Rcaron", 600, NULL },
+ { "Kcommaaccent", 600, NULL },
+ { "greater", 600, NULL },
+ { "atilde", 600, NULL },
+ { "brokenbar", 600, NULL },
+ { "quoteleft", 600, NULL },
+ { "Edotaccent", 600, NULL },
+ { "onesuperior", 600, NULL }
+};
+
+static BuiltinFontWidth courierBoldObliqueWidthsTab[] = {
+ { "Ntilde", 600, NULL },
+ { "rcaron", 600, NULL },
+ { "kcommaaccent", 600, NULL },
+ { "Ncommaaccent", 600, NULL },
+ { "Zacute", 600, NULL },
+ { "comma", 600, NULL },
+ { "cedilla", 600, NULL },
+ { "plusminus", 600, NULL },
+ { "circumflex", 600, NULL },
+ { "dotaccent", 600, NULL },
+ { "edotaccent", 600, NULL },
+ { "asciitilde", 600, NULL },
+ { "colon", 600, NULL },
+ { "onehalf", 600, NULL },
+ { "dollar", 600, NULL },
+ { "Lcaron", 600, NULL },
+ { "ntilde", 600, NULL },
+ { "Aogonek", 600, NULL },
+ { "ncommaaccent", 600, NULL },
+ { "minus", 600, NULL },
+ { "Iogonek", 600, NULL },
+ { "zacute", 600, NULL },
+ { "yen", 600, NULL },
+ { "space", 600, NULL },
+ { "Omacron", 600, NULL },
+ { "questiondown", 600, NULL },
+ { "emdash", 600, NULL },
+ { "Agrave", 600, NULL },
+ { "three", 600, NULL },
+ { "numbersign", 600, NULL },
+ { "lcaron", 600, NULL },
+ { "A", 600, NULL },
+ { "B", 600, NULL },
+ { "C", 600, NULL },
+ { "aogonek", 600, NULL },
+ { "D", 600, NULL },
+ { "E", 600, NULL },
+ { "onequarter", 600, NULL },
+ { "F", 600, NULL },
+ { "G", 600, NULL },
+ { "H", 600, NULL },
+ { "I", 600, NULL },
+ { "J", 600, NULL },
+ { "K", 600, NULL },
+ { "iogonek", 600, NULL },
+ { "backslash", 600, NULL },
+ { "L", 600, NULL },
+ { "periodcentered", 600, NULL },
+ { "M", 600, NULL },
+ { "N", 600, NULL },
+ { "omacron", 600, NULL },
+ { "Tcommaaccent", 600, NULL },
+ { "O", 600, NULL },
+ { "P", 600, NULL },
+ { "Q", 600, NULL },
+ { "Uhungarumlaut", 600, NULL },
+ { "R", 600, NULL },
+ { "Aacute", 600, NULL },
+ { "caron", 600, NULL },
+ { "S", 600, NULL },
+ { "T", 600, NULL },
+ { "U", 600, NULL },
+ { "agrave", 600, NULL },
+ { "V", 600, NULL },
+ { "W", 600, NULL },
+ { "X", 600, NULL },
+ { "question", 600, NULL },
+ { "equal", 600, NULL },
+ { "Y", 600, NULL },
+ { "Z", 600, NULL },
+ { "four", 600, NULL },
+ { "a", 600, NULL },
+ { "Gcommaaccent", 600, NULL },
+ { "b", 600, NULL },
+ { "c", 600, NULL },
+ { "d", 600, NULL },
+ { "e", 600, NULL },
+ { "f", 600, NULL },
+ { "g", 600, NULL },
+ { "bullet", 600, NULL },
+ { "h", 600, NULL },
+ { "i", 600, NULL },
+ { "Oslash", 600, NULL },
+ { "dagger", 600, NULL },
+ { "j", 600, NULL },
+ { "k", 600, NULL },
+ { "l", 600, NULL },
+ { "m", 600, NULL },
+ { "n", 600, NULL },
+ { "tcommaaccent", 600, NULL },
+ { "o", 600, NULL },
+ { "ordfeminine", 600, NULL },
+ { "ring", 600, NULL },
+ { "p", 600, NULL },
+ { "q", 600, NULL },
+ { "uhungarumlaut", 600, NULL },
+ { "r", 600, NULL },
+ { "twosuperior", 600, NULL },
+ { "aacute", 600, NULL },
+ { "s", 600, NULL },
+ { "OE", 600, NULL },
+ { "t", 600, NULL },
+ { "divide", 600, NULL },
+ { "u", 600, NULL },
+ { "Ccaron", 600, NULL },
+ { "v", 600, NULL },
+ { "w", 600, NULL },
+ { "x", 600, NULL },
+ { "y", 600, NULL },
+ { "z", 600, NULL },
+ { "Gbreve", 600, NULL },
+ { "commaaccent", 600, NULL },
+ { "hungarumlaut", 600, NULL },
+ { "Idotaccent", 600, NULL },
+ { "Nacute", 600, NULL },
+ { "quotedbl", 600, NULL },
+ { "gcommaaccent", 600, NULL },
+ { "mu", 600, NULL },
+ { "greaterequal", 600, NULL },
+ { "Scaron", 600, NULL },
+ { "Lslash", 600, NULL },
+ { "semicolon", 600, NULL },
+ { "oslash", 600, NULL },
+ { "lessequal", 600, NULL },
+ { "lozenge", 600, NULL },
+ { "parenright", 600, NULL },
+ { "ccaron", 600, NULL },
+ { "Ecircumflex", 600, NULL },
+ { "gbreve", 600, NULL },
+ { "trademark", 600, NULL },
+ { "daggerdbl", 600, NULL },
+ { "nacute", 600, NULL },
+ { "macron", 600, NULL },
+ { "Otilde", 600, NULL },
+ { "Emacron", 600, NULL },
+ { "ellipsis", 600, NULL },
+ { "scaron", 600, NULL },
+ { "AE", 600, NULL },
+ { "Ucircumflex", 600, NULL },
+ { "lslash", 600, NULL },
+ { "quotedblleft", 600, NULL },
+ { "guilsinglright", 600, NULL },
+ { "hyphen", 600, NULL },
+ { "quotesingle", 600, NULL },
+ { "eight", 600, NULL },
+ { "exclamdown", 600, NULL },
+ { "endash", 600, NULL },
+ { "oe", 600, NULL },
+ { "Abreve", 600, NULL },
+ { "Umacron", 600, NULL },
+ { "ecircumflex", 600, NULL },
+ { "Adieresis", 600, NULL },
+ { "copyright", 600, NULL },
+ { "Egrave", 600, NULL },
+ { "slash", 600, NULL },
+ { "Edieresis", 600, NULL },
+ { "otilde", 600, NULL },
+ { "Idieresis", 600, NULL },
+ { "parenleft", 600, NULL },
+ { "one", 600, NULL },
+ { "emacron", 600, NULL },
+ { "Odieresis", 600, NULL },
+ { "ucircumflex", 600, NULL },
+ { "bracketleft", 600, NULL },
+ { "Ugrave", 600, NULL },
+ { "quoteright", 600, NULL },
+ { "Udieresis", 600, NULL },
+ { "perthousand", 600, NULL },
+ { "Ydieresis", 600, NULL },
+ { "umacron", 600, NULL },
+ { "abreve", 600, NULL },
+ { "Eacute", 600, NULL },
+ { "adieresis", 600, NULL },
+ { "egrave", 600, NULL },
+ { "edieresis", 600, NULL },
+ { "idieresis", 600, NULL },
+ { "Eth", 600, NULL },
+ { "ae", 600, NULL },
+ { "asterisk", 600, NULL },
+ { "odieresis", 600, NULL },
+ { "Uacute", 600, NULL },
+ { "ugrave", 600, NULL },
+ { "nine", 600, NULL },
+ { "five", 600, NULL },
+ { "udieresis", 600, NULL },
+ { "Zcaron", 600, NULL },
+ { "Scommaaccent", 600, NULL },
+ { "threequarters", 600, NULL },
+ { "guillemotright", 600, NULL },
+ { "Ccedilla", 600, NULL },
+ { "ydieresis", 600, NULL },
+ { "tilde", 600, NULL },
+ { "at", 600, NULL },
+ { "eacute", 600, NULL },
+ { "underscore", 600, NULL },
+ { "Euro", 600, NULL },
+ { "Dcroat", 600, NULL },
+ { "multiply", 600, NULL },
+ { "zero", 600, NULL },
+ { "eth", 600, NULL },
+ { "Scedilla", 600, NULL },
+ { "Ograve", 600, NULL },
+ { "Racute", 600, NULL },
+ { "partialdiff", 600, NULL },
+ { "uacute", 600, NULL },
+ { "braceleft", 600, NULL },
+ { "Thorn", 600, NULL },
+ { "zcaron", 600, NULL },
+ { "scommaaccent", 600, NULL },
+ { "ccedilla", 600, NULL },
+ { "Dcaron", 600, NULL },
+ { "dcroat", 600, NULL },
+ { "Ocircumflex", 600, NULL },
+ { "Oacute", 600, NULL },
+ { "scedilla", 600, NULL },
+ { "ogonek", 600, NULL },
+ { "ograve", 600, NULL },
+ { "racute", 600, NULL },
+ { "Tcaron", 600, NULL },
+ { "Eogonek", 600, NULL },
+ { "thorn", 600, NULL },
+ { "degree", 600, NULL },
+ { "registered", 600, NULL },
+ { "radical", 600, NULL },
+ { "Aring", 600, NULL },
+ { "percent", 600, NULL },
+ { "six", 600, NULL },
+ { "paragraph", 600, NULL },
+ { "dcaron", 600, NULL },
+ { "Uogonek", 600, NULL },
+ { "two", 600, NULL },
+ { "summation", 600, NULL },
+ { "Igrave", 600, NULL },
+ { "Lacute", 600, NULL },
+ { "ocircumflex", 600, NULL },
+ { "oacute", 600, NULL },
+ { "Uring", 600, NULL },
+ { "Lcommaaccent", 600, NULL },
+ { "tcaron", 600, NULL },
+ { "eogonek", 600, NULL },
+ { "Delta", 600, NULL },
+ { "Ohungarumlaut", 600, NULL },
+ { "asciicircum", 600, NULL },
+ { "aring", 600, NULL },
+ { "grave", 600, NULL },
+ { "uogonek", 600, NULL },
+ { "bracketright", 600, NULL },
+ { "Iacute", 600, NULL },
+ { "ampersand", 600, NULL },
+ { "igrave", 600, NULL },
+ { "lacute", 600, NULL },
+ { "Ncaron", 600, NULL },
+ { "plus", 600, NULL },
+ { "uring", 600, NULL },
+ { "quotesinglbase", 600, NULL },
+ { "lcommaaccent", 600, NULL },
+ { "Yacute", 600, NULL },
+ { "ohungarumlaut", 600, NULL },
+ { "threesuperior", 600, NULL },
+ { "acute", 600, NULL },
+ { "section", 600, NULL },
+ { "dieresis", 600, NULL },
+ { "iacute", 600, NULL },
+ { "quotedblbase", 600, NULL },
+ { "ncaron", 600, NULL },
+ { "florin", 600, NULL },
+ { "yacute", 600, NULL },
+ { "Rcommaaccent", 600, NULL },
+ { "fi", 600, NULL },
+ { "fl", 600, NULL },
+ { "Acircumflex", 600, NULL },
+ { "Cacute", 600, NULL },
+ { "Icircumflex", 600, NULL },
+ { "guillemotleft", 600, NULL },
+ { "germandbls", 600, NULL },
+ { "Amacron", 600, NULL },
+ { "seven", 600, NULL },
+ { "Sacute", 600, NULL },
+ { "ordmasculine", 600, NULL },
+ { "dotlessi", 600, NULL },
+ { "sterling", 600, NULL },
+ { "notequal", 600, NULL },
+ { "Imacron", 600, NULL },
+ { "rcommaaccent", 600, NULL },
+ { "Zdotaccent", 600, NULL },
+ { "acircumflex", 600, NULL },
+ { "cacute", 600, NULL },
+ { "Ecaron", 600, NULL },
+ { "icircumflex", 600, NULL },
+ { "braceright", 600, NULL },
+ { "quotedblright", 600, NULL },
+ { "amacron", 600, NULL },
+ { "sacute", 600, NULL },
+ { "imacron", 600, NULL },
+ { "cent", 600, NULL },
+ { "currency", 600, NULL },
+ { "logicalnot", 600, NULL },
+ { "zdotaccent", 600, NULL },
+ { "Atilde", 600, NULL },
+ { "breve", 600, NULL },
+ { "bar", 600, NULL },
+ { "fraction", 600, NULL },
+ { "less", 600, NULL },
+ { "ecaron", 600, NULL },
+ { "guilsinglleft", 600, NULL },
+ { "exclam", 600, NULL },
+ { "period", 600, NULL },
+ { "Rcaron", 600, NULL },
+ { "Kcommaaccent", 600, NULL },
+ { "greater", 600, NULL },
+ { "atilde", 600, NULL },
+ { "brokenbar", 600, NULL },
+ { "quoteleft", 600, NULL },
+ { "Edotaccent", 600, NULL },
+ { "onesuperior", 600, NULL }
+};
+
+static BuiltinFontWidth courierObliqueWidthsTab[] = {
+ { "Ntilde", 600, NULL },
+ { "rcaron", 600, NULL },
+ { "kcommaaccent", 600, NULL },
+ { "Ncommaaccent", 600, NULL },
+ { "Zacute", 600, NULL },
+ { "comma", 600, NULL },
+ { "cedilla", 600, NULL },
+ { "plusminus", 600, NULL },
+ { "circumflex", 600, NULL },
+ { "dotaccent", 600, NULL },
+ { "edotaccent", 600, NULL },
+ { "asciitilde", 600, NULL },
+ { "colon", 600, NULL },
+ { "onehalf", 600, NULL },
+ { "dollar", 600, NULL },
+ { "Lcaron", 600, NULL },
+ { "ntilde", 600, NULL },
+ { "Aogonek", 600, NULL },
+ { "ncommaaccent", 600, NULL },
+ { "minus", 600, NULL },
+ { "Iogonek", 600, NULL },
+ { "zacute", 600, NULL },
+ { "yen", 600, NULL },
+ { "space", 600, NULL },
+ { "Omacron", 600, NULL },
+ { "questiondown", 600, NULL },
+ { "emdash", 600, NULL },
+ { "Agrave", 600, NULL },
+ { "three", 600, NULL },
+ { "numbersign", 600, NULL },
+ { "lcaron", 600, NULL },
+ { "A", 600, NULL },
+ { "B", 600, NULL },
+ { "C", 600, NULL },
+ { "aogonek", 600, NULL },
+ { "D", 600, NULL },
+ { "E", 600, NULL },
+ { "onequarter", 600, NULL },
+ { "F", 600, NULL },
+ { "G", 600, NULL },
+ { "H", 600, NULL },
+ { "I", 600, NULL },
+ { "J", 600, NULL },
+ { "K", 600, NULL },
+ { "iogonek", 600, NULL },
+ { "backslash", 600, NULL },
+ { "L", 600, NULL },
+ { "periodcentered", 600, NULL },
+ { "M", 600, NULL },
+ { "N", 600, NULL },
+ { "omacron", 600, NULL },
+ { "Tcommaaccent", 600, NULL },
+ { "O", 600, NULL },
+ { "P", 600, NULL },
+ { "Q", 600, NULL },
+ { "Uhungarumlaut", 600, NULL },
+ { "R", 600, NULL },
+ { "Aacute", 600, NULL },
+ { "caron", 600, NULL },
+ { "S", 600, NULL },
+ { "T", 600, NULL },
+ { "U", 600, NULL },
+ { "agrave", 600, NULL },
+ { "V", 600, NULL },
+ { "W", 600, NULL },
+ { "X", 600, NULL },
+ { "question", 600, NULL },
+ { "equal", 600, NULL },
+ { "Y", 600, NULL },
+ { "Z", 600, NULL },
+ { "four", 600, NULL },
+ { "a", 600, NULL },
+ { "Gcommaaccent", 600, NULL },
+ { "b", 600, NULL },
+ { "c", 600, NULL },
+ { "d", 600, NULL },
+ { "e", 600, NULL },
+ { "f", 600, NULL },
+ { "g", 600, NULL },
+ { "bullet", 600, NULL },
+ { "h", 600, NULL },
+ { "i", 600, NULL },
+ { "Oslash", 600, NULL },
+ { "dagger", 600, NULL },
+ { "j", 600, NULL },
+ { "k", 600, NULL },
+ { "l", 600, NULL },
+ { "m", 600, NULL },
+ { "n", 600, NULL },
+ { "tcommaaccent", 600, NULL },
+ { "o", 600, NULL },
+ { "ordfeminine", 600, NULL },
+ { "ring", 600, NULL },
+ { "p", 600, NULL },
+ { "q", 600, NULL },
+ { "uhungarumlaut", 600, NULL },
+ { "r", 600, NULL },
+ { "twosuperior", 600, NULL },
+ { "aacute", 600, NULL },
+ { "s", 600, NULL },
+ { "OE", 600, NULL },
+ { "t", 600, NULL },
+ { "divide", 600, NULL },
+ { "u", 600, NULL },
+ { "Ccaron", 600, NULL },
+ { "v", 600, NULL },
+ { "w", 600, NULL },
+ { "x", 600, NULL },
+ { "y", 600, NULL },
+ { "z", 600, NULL },
+ { "Gbreve", 600, NULL },
+ { "commaaccent", 600, NULL },
+ { "hungarumlaut", 600, NULL },
+ { "Idotaccent", 600, NULL },
+ { "Nacute", 600, NULL },
+ { "quotedbl", 600, NULL },
+ { "gcommaaccent", 600, NULL },
+ { "mu", 600, NULL },
+ { "greaterequal", 600, NULL },
+ { "Scaron", 600, NULL },
+ { "Lslash", 600, NULL },
+ { "semicolon", 600, NULL },
+ { "oslash", 600, NULL },
+ { "lessequal", 600, NULL },
+ { "lozenge", 600, NULL },
+ { "parenright", 600, NULL },
+ { "ccaron", 600, NULL },
+ { "Ecircumflex", 600, NULL },
+ { "gbreve", 600, NULL },
+ { "trademark", 600, NULL },
+ { "daggerdbl", 600, NULL },
+ { "nacute", 600, NULL },
+ { "macron", 600, NULL },
+ { "Otilde", 600, NULL },
+ { "Emacron", 600, NULL },
+ { "ellipsis", 600, NULL },
+ { "scaron", 600, NULL },
+ { "AE", 600, NULL },
+ { "Ucircumflex", 600, NULL },
+ { "lslash", 600, NULL },
+ { "quotedblleft", 600, NULL },
+ { "guilsinglright", 600, NULL },
+ { "hyphen", 600, NULL },
+ { "quotesingle", 600, NULL },
+ { "eight", 600, NULL },
+ { "exclamdown", 600, NULL },
+ { "endash", 600, NULL },
+ { "oe", 600, NULL },
+ { "Abreve", 600, NULL },
+ { "Umacron", 600, NULL },
+ { "ecircumflex", 600, NULL },
+ { "Adieresis", 600, NULL },
+ { "copyright", 600, NULL },
+ { "Egrave", 600, NULL },
+ { "slash", 600, NULL },
+ { "Edieresis", 600, NULL },
+ { "otilde", 600, NULL },
+ { "Idieresis", 600, NULL },
+ { "parenleft", 600, NULL },
+ { "one", 600, NULL },
+ { "emacron", 600, NULL },
+ { "Odieresis", 600, NULL },
+ { "ucircumflex", 600, NULL },
+ { "bracketleft", 600, NULL },
+ { "Ugrave", 600, NULL },
+ { "quoteright", 600, NULL },
+ { "Udieresis", 600, NULL },
+ { "perthousand", 600, NULL },
+ { "Ydieresis", 600, NULL },
+ { "umacron", 600, NULL },
+ { "abreve", 600, NULL },
+ { "Eacute", 600, NULL },
+ { "adieresis", 600, NULL },
+ { "egrave", 600, NULL },
+ { "edieresis", 600, NULL },
+ { "idieresis", 600, NULL },
+ { "Eth", 600, NULL },
+ { "ae", 600, NULL },
+ { "asterisk", 600, NULL },
+ { "odieresis", 600, NULL },
+ { "Uacute", 600, NULL },
+ { "ugrave", 600, NULL },
+ { "nine", 600, NULL },
+ { "five", 600, NULL },
+ { "udieresis", 600, NULL },
+ { "Zcaron", 600, NULL },
+ { "Scommaaccent", 600, NULL },
+ { "threequarters", 600, NULL },
+ { "guillemotright", 600, NULL },
+ { "Ccedilla", 600, NULL },
+ { "ydieresis", 600, NULL },
+ { "tilde", 600, NULL },
+ { "at", 600, NULL },
+ { "eacute", 600, NULL },
+ { "underscore", 600, NULL },
+ { "Euro", 600, NULL },
+ { "Dcroat", 600, NULL },
+ { "multiply", 600, NULL },
+ { "zero", 600, NULL },
+ { "eth", 600, NULL },
+ { "Scedilla", 600, NULL },
+ { "Ograve", 600, NULL },
+ { "Racute", 600, NULL },
+ { "partialdiff", 600, NULL },
+ { "uacute", 600, NULL },
+ { "braceleft", 600, NULL },
+ { "Thorn", 600, NULL },
+ { "zcaron", 600, NULL },
+ { "scommaaccent", 600, NULL },
+ { "ccedilla", 600, NULL },
+ { "Dcaron", 600, NULL },
+ { "dcroat", 600, NULL },
+ { "Ocircumflex", 600, NULL },
+ { "Oacute", 600, NULL },
+ { "scedilla", 600, NULL },
+ { "ogonek", 600, NULL },
+ { "ograve", 600, NULL },
+ { "racute", 600, NULL },
+ { "Tcaron", 600, NULL },
+ { "Eogonek", 600, NULL },
+ { "thorn", 600, NULL },
+ { "degree", 600, NULL },
+ { "registered", 600, NULL },
+ { "radical", 600, NULL },
+ { "Aring", 600, NULL },
+ { "percent", 600, NULL },
+ { "six", 600, NULL },
+ { "paragraph", 600, NULL },
+ { "dcaron", 600, NULL },
+ { "Uogonek", 600, NULL },
+ { "two", 600, NULL },
+ { "summation", 600, NULL },
+ { "Igrave", 600, NULL },
+ { "Lacute", 600, NULL },
+ { "ocircumflex", 600, NULL },
+ { "oacute", 600, NULL },
+ { "Uring", 600, NULL },
+ { "Lcommaaccent", 600, NULL },
+ { "tcaron", 600, NULL },
+ { "eogonek", 600, NULL },
+ { "Delta", 600, NULL },
+ { "Ohungarumlaut", 600, NULL },
+ { "asciicircum", 600, NULL },
+ { "aring", 600, NULL },
+ { "grave", 600, NULL },
+ { "uogonek", 600, NULL },
+ { "bracketright", 600, NULL },
+ { "Iacute", 600, NULL },
+ { "ampersand", 600, NULL },
+ { "igrave", 600, NULL },
+ { "lacute", 600, NULL },
+ { "Ncaron", 600, NULL },
+ { "plus", 600, NULL },
+ { "uring", 600, NULL },
+ { "quotesinglbase", 600, NULL },
+ { "lcommaaccent", 600, NULL },
+ { "Yacute", 600, NULL },
+ { "ohungarumlaut", 600, NULL },
+ { "threesuperior", 600, NULL },
+ { "acute", 600, NULL },
+ { "section", 600, NULL },
+ { "dieresis", 600, NULL },
+ { "iacute", 600, NULL },
+ { "quotedblbase", 600, NULL },
+ { "ncaron", 600, NULL },
+ { "florin", 600, NULL },
+ { "yacute", 600, NULL },
+ { "Rcommaaccent", 600, NULL },
+ { "fi", 600, NULL },
+ { "fl", 600, NULL },
+ { "Acircumflex", 600, NULL },
+ { "Cacute", 600, NULL },
+ { "Icircumflex", 600, NULL },
+ { "guillemotleft", 600, NULL },
+ { "germandbls", 600, NULL },
+ { "Amacron", 600, NULL },
+ { "seven", 600, NULL },
+ { "Sacute", 600, NULL },
+ { "ordmasculine", 600, NULL },
+ { "dotlessi", 600, NULL },
+ { "sterling", 600, NULL },
+ { "notequal", 600, NULL },
+ { "Imacron", 600, NULL },
+ { "rcommaaccent", 600, NULL },
+ { "Zdotaccent", 600, NULL },
+ { "acircumflex", 600, NULL },
+ { "cacute", 600, NULL },
+ { "Ecaron", 600, NULL },
+ { "icircumflex", 600, NULL },
+ { "braceright", 600, NULL },
+ { "quotedblright", 600, NULL },
+ { "amacron", 600, NULL },
+ { "sacute", 600, NULL },
+ { "imacron", 600, NULL },
+ { "cent", 600, NULL },
+ { "currency", 600, NULL },
+ { "logicalnot", 600, NULL },
+ { "zdotaccent", 600, NULL },
+ { "Atilde", 600, NULL },
+ { "breve", 600, NULL },
+ { "bar", 600, NULL },
+ { "fraction", 600, NULL },
+ { "less", 600, NULL },
+ { "ecaron", 600, NULL },
+ { "guilsinglleft", 600, NULL },
+ { "exclam", 600, NULL },
+ { "period", 600, NULL },
+ { "Rcaron", 600, NULL },
+ { "Kcommaaccent", 600, NULL },
+ { "greater", 600, NULL },
+ { "atilde", 600, NULL },
+ { "brokenbar", 600, NULL },
+ { "quoteleft", 600, NULL },
+ { "Edotaccent", 600, NULL },
+ { "onesuperior", 600, NULL }
+};
+
+static BuiltinFontWidth helveticaWidthsTab[] = {
+ { "Ntilde", 722, NULL },
+ { "rcaron", 333, NULL },
+ { "kcommaaccent", 500, NULL },
+ { "Ncommaaccent", 722, NULL },
+ { "Zacute", 611, NULL },
+ { "comma", 278, NULL },
+ { "cedilla", 333, NULL },
+ { "plusminus", 584, NULL },
+ { "circumflex", 333, NULL },
+ { "dotaccent", 333, NULL },
+ { "edotaccent", 556, NULL },
+ { "asciitilde", 584, NULL },
+ { "colon", 278, NULL },
+ { "onehalf", 834, NULL },
+ { "dollar", 556, NULL },
+ { "Lcaron", 556, NULL },
+ { "ntilde", 556, NULL },
+ { "Aogonek", 667, NULL },
+ { "ncommaaccent", 556, NULL },
+ { "minus", 584, NULL },
+ { "Iogonek", 278, NULL },
+ { "zacute", 500, NULL },
+ { "yen", 556, NULL },
+ { "space", 278, NULL },
+ { "Omacron", 778, NULL },
+ { "questiondown", 611, NULL },
+ { "emdash", 1000, NULL },
+ { "Agrave", 667, NULL },
+ { "three", 556, NULL },
+ { "numbersign", 556, NULL },
+ { "lcaron", 299, NULL },
+ { "A", 667, NULL },
+ { "B", 667, NULL },
+ { "C", 722, NULL },
+ { "aogonek", 556, NULL },
+ { "D", 722, NULL },
+ { "E", 667, NULL },
+ { "onequarter", 834, NULL },
+ { "F", 611, NULL },
+ { "G", 778, NULL },
+ { "H", 722, NULL },
+ { "I", 278, NULL },
+ { "J", 500, NULL },
+ { "K", 667, NULL },
+ { "iogonek", 222, NULL },
+ { "backslash", 278, NULL },
+ { "L", 556, NULL },
+ { "periodcentered", 278, NULL },
+ { "M", 833, NULL },
+ { "N", 722, NULL },
+ { "omacron", 556, NULL },
+ { "Tcommaaccent", 611, NULL },
+ { "O", 778, NULL },
+ { "P", 667, NULL },
+ { "Q", 778, NULL },
+ { "Uhungarumlaut", 722, NULL },
+ { "R", 722, NULL },
+ { "Aacute", 667, NULL },
+ { "caron", 333, NULL },
+ { "S", 667, NULL },
+ { "T", 611, NULL },
+ { "U", 722, NULL },
+ { "agrave", 556, NULL },
+ { "V", 667, NULL },
+ { "W", 944, NULL },
+ { "X", 667, NULL },
+ { "question", 556, NULL },
+ { "equal", 584, NULL },
+ { "Y", 667, NULL },
+ { "Z", 611, NULL },
+ { "four", 556, NULL },
+ { "a", 556, NULL },
+ { "Gcommaaccent", 778, NULL },
+ { "b", 556, NULL },
+ { "c", 500, NULL },
+ { "d", 556, NULL },
+ { "e", 556, NULL },
+ { "f", 278, NULL },
+ { "g", 556, NULL },
+ { "bullet", 350, NULL },
+ { "h", 556, NULL },
+ { "i", 222, NULL },
+ { "Oslash", 778, NULL },
+ { "dagger", 556, NULL },
+ { "j", 222, NULL },
+ { "k", 500, NULL },
+ { "l", 222, NULL },
+ { "m", 833, NULL },
+ { "n", 556, NULL },
+ { "tcommaaccent", 278, NULL },
+ { "o", 556, NULL },
+ { "ordfeminine", 370, NULL },
+ { "ring", 333, NULL },
+ { "p", 556, NULL },
+ { "q", 556, NULL },
+ { "uhungarumlaut", 556, NULL },
+ { "r", 333, NULL },
+ { "twosuperior", 333, NULL },
+ { "aacute", 556, NULL },
+ { "s", 500, NULL },
+ { "OE", 1000, NULL },
+ { "t", 278, NULL },
+ { "divide", 584, NULL },
+ { "u", 556, NULL },
+ { "Ccaron", 722, NULL },
+ { "v", 500, NULL },
+ { "w", 722, NULL },
+ { "x", 500, NULL },
+ { "y", 500, NULL },
+ { "z", 500, NULL },
+ { "Gbreve", 778, NULL },
+ { "commaaccent", 250, NULL },
+ { "hungarumlaut", 333, NULL },
+ { "Idotaccent", 278, NULL },
+ { "Nacute", 722, NULL },
+ { "quotedbl", 355, NULL },
+ { "gcommaaccent", 556, NULL },
+ { "mu", 556, NULL },
+ { "greaterequal", 549, NULL },
+ { "Scaron", 667, NULL },
+ { "Lslash", 556, NULL },
+ { "semicolon", 278, NULL },
+ { "oslash", 611, NULL },
+ { "lessequal", 549, NULL },
+ { "lozenge", 471, NULL },
+ { "parenright", 333, NULL },
+ { "ccaron", 500, NULL },
+ { "Ecircumflex", 667, NULL },
+ { "gbreve", 556, NULL },
+ { "trademark", 1000, NULL },
+ { "daggerdbl", 556, NULL },
+ { "nacute", 556, NULL },
+ { "macron", 333, NULL },
+ { "Otilde", 778, NULL },
+ { "Emacron", 667, NULL },
+ { "ellipsis", 1000, NULL },
+ { "scaron", 500, NULL },
+ { "AE", 1000, NULL },
+ { "Ucircumflex", 722, NULL },
+ { "lslash", 222, NULL },
+ { "quotedblleft", 333, NULL },
+ { "guilsinglright", 333, NULL },
+ { "hyphen", 333, NULL },
+ { "quotesingle", 191, NULL },
+ { "eight", 556, NULL },
+ { "exclamdown", 333, NULL },
+ { "endash", 556, NULL },
+ { "oe", 944, NULL },
+ { "Abreve", 667, NULL },
+ { "Umacron", 722, NULL },
+ { "ecircumflex", 556, NULL },
+ { "Adieresis", 667, NULL },
+ { "copyright", 737, NULL },
+ { "Egrave", 667, NULL },
+ { "slash", 278, NULL },
+ { "Edieresis", 667, NULL },
+ { "otilde", 556, NULL },
+ { "Idieresis", 278, NULL },
+ { "parenleft", 333, NULL },
+ { "one", 556, NULL },
+ { "emacron", 556, NULL },
+ { "Odieresis", 778, NULL },
+ { "ucircumflex", 556, NULL },
+ { "bracketleft", 278, NULL },
+ { "Ugrave", 722, NULL },
+ { "quoteright", 222, NULL },
+ { "Udieresis", 722, NULL },
+ { "perthousand", 1000, NULL },
+ { "Ydieresis", 667, NULL },
+ { "umacron", 556, NULL },
+ { "abreve", 556, NULL },
+ { "Eacute", 667, NULL },
+ { "adieresis", 556, NULL },
+ { "egrave", 556, NULL },
+ { "edieresis", 556, NULL },
+ { "idieresis", 278, NULL },
+ { "Eth", 722, NULL },
+ { "ae", 889, NULL },
+ { "asterisk", 389, NULL },
+ { "odieresis", 556, NULL },
+ { "Uacute", 722, NULL },
+ { "ugrave", 556, NULL },
+ { "nine", 556, NULL },
+ { "five", 556, NULL },
+ { "udieresis", 556, NULL },
+ { "Zcaron", 611, NULL },
+ { "Scommaaccent", 667, NULL },
+ { "threequarters", 834, NULL },
+ { "guillemotright", 556, NULL },
+ { "Ccedilla", 722, NULL },
+ { "ydieresis", 500, NULL },
+ { "tilde", 333, NULL },
+ { "at", 1015, NULL },
+ { "eacute", 556, NULL },
+ { "underscore", 556, NULL },
+ { "Euro", 556, NULL },
+ { "Dcroat", 722, NULL },
+ { "multiply", 584, NULL },
+ { "zero", 556, NULL },
+ { "eth", 556, NULL },
+ { "Scedilla", 667, NULL },
+ { "Ograve", 778, NULL },
+ { "Racute", 722, NULL },
+ { "partialdiff", 476, NULL },
+ { "uacute", 556, NULL },
+ { "braceleft", 334, NULL },
+ { "Thorn", 667, NULL },
+ { "zcaron", 500, NULL },
+ { "scommaaccent", 500, NULL },
+ { "ccedilla", 500, NULL },
+ { "Dcaron", 722, NULL },
+ { "dcroat", 556, NULL },
+ { "Ocircumflex", 778, NULL },
+ { "Oacute", 778, NULL },
+ { "scedilla", 500, NULL },
+ { "ogonek", 333, NULL },
+ { "ograve", 556, NULL },
+ { "racute", 333, NULL },
+ { "Tcaron", 611, NULL },
+ { "Eogonek", 667, NULL },
+ { "thorn", 556, NULL },
+ { "degree", 400, NULL },
+ { "registered", 737, NULL },
+ { "radical", 453, NULL },
+ { "Aring", 667, NULL },
+ { "percent", 889, NULL },
+ { "six", 556, NULL },
+ { "paragraph", 537, NULL },
+ { "dcaron", 643, NULL },
+ { "Uogonek", 722, NULL },
+ { "two", 556, NULL },
+ { "summation", 600, NULL },
+ { "Igrave", 278, NULL },
+ { "Lacute", 556, NULL },
+ { "ocircumflex", 556, NULL },
+ { "oacute", 556, NULL },
+ { "Uring", 722, NULL },
+ { "Lcommaaccent", 556, NULL },
+ { "tcaron", 317, NULL },
+ { "eogonek", 556, NULL },
+ { "Delta", 612, NULL },
+ { "Ohungarumlaut", 778, NULL },
+ { "asciicircum", 469, NULL },
+ { "aring", 556, NULL },
+ { "grave", 333, NULL },
+ { "uogonek", 556, NULL },
+ { "bracketright", 278, NULL },
+ { "Iacute", 278, NULL },
+ { "ampersand", 667, NULL },
+ { "igrave", 278, NULL },
+ { "lacute", 222, NULL },
+ { "Ncaron", 722, NULL },
+ { "plus", 584, NULL },
+ { "uring", 556, NULL },
+ { "quotesinglbase", 222, NULL },
+ { "lcommaaccent", 222, NULL },
+ { "Yacute", 667, NULL },
+ { "ohungarumlaut", 556, NULL },
+ { "threesuperior", 333, NULL },
+ { "acute", 333, NULL },
+ { "section", 556, NULL },
+ { "dieresis", 333, NULL },
+ { "iacute", 278, NULL },
+ { "quotedblbase", 333, NULL },
+ { "ncaron", 556, NULL },
+ { "florin", 556, NULL },
+ { "yacute", 500, NULL },
+ { "Rcommaaccent", 722, NULL },
+ { "fi", 500, NULL },
+ { "fl", 500, NULL },
+ { "Acircumflex", 667, NULL },
+ { "Cacute", 722, NULL },
+ { "Icircumflex", 278, NULL },
+ { "guillemotleft", 556, NULL },
+ { "germandbls", 611, NULL },
+ { "Amacron", 667, NULL },
+ { "seven", 556, NULL },
+ { "Sacute", 667, NULL },
+ { "ordmasculine", 365, NULL },
+ { "dotlessi", 278, NULL },
+ { "sterling", 556, NULL },
+ { "notequal", 549, NULL },
+ { "Imacron", 278, NULL },
+ { "rcommaaccent", 333, NULL },
+ { "Zdotaccent", 611, NULL },
+ { "acircumflex", 556, NULL },
+ { "cacute", 500, NULL },
+ { "Ecaron", 667, NULL },
+ { "icircumflex", 278, NULL },
+ { "braceright", 334, NULL },
+ { "quotedblright", 333, NULL },
+ { "amacron", 556, NULL },
+ { "sacute", 500, NULL },
+ { "imacron", 278, NULL },
+ { "cent", 556, NULL },
+ { "currency", 556, NULL },
+ { "logicalnot", 584, NULL },
+ { "zdotaccent", 500, NULL },
+ { "Atilde", 667, NULL },
+ { "breve", 333, NULL },
+ { "bar", 260, NULL },
+ { "fraction", 167, NULL },
+ { "less", 584, NULL },
+ { "ecaron", 556, NULL },
+ { "guilsinglleft", 333, NULL },
+ { "exclam", 278, NULL },
+ { "period", 278, NULL },
+ { "Rcaron", 722, NULL },
+ { "Kcommaaccent", 667, NULL },
+ { "greater", 584, NULL },
+ { "atilde", 556, NULL },
+ { "brokenbar", 260, NULL },
+ { "quoteleft", 222, NULL },
+ { "Edotaccent", 667, NULL },
+ { "onesuperior", 333, NULL }
+};
+
+static BuiltinFontWidth helveticaBoldWidthsTab[] = {
+ { "Ntilde", 722, NULL },
+ { "rcaron", 389, NULL },
+ { "kcommaaccent", 556, NULL },
+ { "Ncommaaccent", 722, NULL },
+ { "Zacute", 611, NULL },
+ { "comma", 278, NULL },
+ { "cedilla", 333, NULL },
+ { "plusminus", 584, NULL },
+ { "circumflex", 333, NULL },
+ { "dotaccent", 333, NULL },
+ { "edotaccent", 556, NULL },
+ { "asciitilde", 584, NULL },
+ { "colon", 333, NULL },
+ { "onehalf", 834, NULL },
+ { "dollar", 556, NULL },
+ { "Lcaron", 611, NULL },
+ { "ntilde", 611, NULL },
+ { "Aogonek", 722, NULL },
+ { "ncommaaccent", 611, NULL },
+ { "minus", 584, NULL },
+ { "Iogonek", 278, NULL },
+ { "zacute", 500, NULL },
+ { "yen", 556, NULL },
+ { "space", 278, NULL },
+ { "Omacron", 778, NULL },
+ { "questiondown", 611, NULL },
+ { "emdash", 1000, NULL },
+ { "Agrave", 722, NULL },
+ { "three", 556, NULL },
+ { "numbersign", 556, NULL },
+ { "lcaron", 400, NULL },
+ { "A", 722, NULL },
+ { "B", 722, NULL },
+ { "C", 722, NULL },
+ { "aogonek", 556, NULL },
+ { "D", 722, NULL },
+ { "E", 667, NULL },
+ { "onequarter", 834, NULL },
+ { "F", 611, NULL },
+ { "G", 778, NULL },
+ { "H", 722, NULL },
+ { "I", 278, NULL },
+ { "J", 556, NULL },
+ { "K", 722, NULL },
+ { "iogonek", 278, NULL },
+ { "backslash", 278, NULL },
+ { "L", 611, NULL },
+ { "periodcentered", 278, NULL },
+ { "M", 833, NULL },
+ { "N", 722, NULL },
+ { "omacron", 611, NULL },
+ { "Tcommaaccent", 611, NULL },
+ { "O", 778, NULL },
+ { "P", 667, NULL },
+ { "Q", 778, NULL },
+ { "Uhungarumlaut", 722, NULL },
+ { "R", 722, NULL },
+ { "Aacute", 722, NULL },
+ { "caron", 333, NULL },
+ { "S", 667, NULL },
+ { "T", 611, NULL },
+ { "U", 722, NULL },
+ { "agrave", 556, NULL },
+ { "V", 667, NULL },
+ { "W", 944, NULL },
+ { "X", 667, NULL },
+ { "question", 611, NULL },
+ { "equal", 584, NULL },
+ { "Y", 667, NULL },
+ { "Z", 611, NULL },
+ { "four", 556, NULL },
+ { "a", 556, NULL },
+ { "Gcommaaccent", 778, NULL },
+ { "b", 611, NULL },
+ { "c", 556, NULL },
+ { "d", 611, NULL },
+ { "e", 556, NULL },
+ { "f", 333, NULL },
+ { "g", 611, NULL },
+ { "bullet", 350, NULL },
+ { "h", 611, NULL },
+ { "i", 278, NULL },
+ { "Oslash", 778, NULL },
+ { "dagger", 556, NULL },
+ { "j", 278, NULL },
+ { "k", 556, NULL },
+ { "l", 278, NULL },
+ { "m", 889, NULL },
+ { "n", 611, NULL },
+ { "tcommaaccent", 333, NULL },
+ { "o", 611, NULL },
+ { "ordfeminine", 370, NULL },
+ { "ring", 333, NULL },
+ { "p", 611, NULL },
+ { "q", 611, NULL },
+ { "uhungarumlaut", 611, NULL },
+ { "r", 389, NULL },
+ { "twosuperior", 333, NULL },
+ { "aacute", 556, NULL },
+ { "s", 556, NULL },
+ { "OE", 1000, NULL },
+ { "t", 333, NULL },
+ { "divide", 584, NULL },
+ { "u", 611, NULL },
+ { "Ccaron", 722, NULL },
+ { "v", 556, NULL },
+ { "w", 778, NULL },
+ { "x", 556, NULL },
+ { "y", 556, NULL },
+ { "z", 500, NULL },
+ { "Gbreve", 778, NULL },
+ { "commaaccent", 250, NULL },
+ { "hungarumlaut", 333, NULL },
+ { "Idotaccent", 278, NULL },
+ { "Nacute", 722, NULL },
+ { "quotedbl", 474, NULL },
+ { "gcommaaccent", 611, NULL },
+ { "mu", 611, NULL },
+ { "greaterequal", 549, NULL },
+ { "Scaron", 667, NULL },
+ { "Lslash", 611, NULL },
+ { "semicolon", 333, NULL },
+ { "oslash", 611, NULL },
+ { "lessequal", 549, NULL },
+ { "lozenge", 494, NULL },
+ { "parenright", 333, NULL },
+ { "ccaron", 556, NULL },
+ { "Ecircumflex", 667, NULL },
+ { "gbreve", 611, NULL },
+ { "trademark", 1000, NULL },
+ { "daggerdbl", 556, NULL },
+ { "nacute", 611, NULL },
+ { "macron", 333, NULL },
+ { "Otilde", 778, NULL },
+ { "Emacron", 667, NULL },
+ { "ellipsis", 1000, NULL },
+ { "scaron", 556, NULL },
+ { "AE", 1000, NULL },
+ { "Ucircumflex", 722, NULL },
+ { "lslash", 278, NULL },
+ { "quotedblleft", 500, NULL },
+ { "guilsinglright", 333, NULL },
+ { "hyphen", 333, NULL },
+ { "quotesingle", 238, NULL },
+ { "eight", 556, NULL },
+ { "exclamdown", 333, NULL },
+ { "endash", 556, NULL },
+ { "oe", 944, NULL },
+ { "Abreve", 722, NULL },
+ { "Umacron", 722, NULL },
+ { "ecircumflex", 556, NULL },
+ { "Adieresis", 722, NULL },
+ { "copyright", 737, NULL },
+ { "Egrave", 667, NULL },
+ { "slash", 278, NULL },
+ { "Edieresis", 667, NULL },
+ { "otilde", 611, NULL },
+ { "Idieresis", 278, NULL },
+ { "parenleft", 333, NULL },
+ { "one", 556, NULL },
+ { "emacron", 556, NULL },
+ { "Odieresis", 778, NULL },
+ { "ucircumflex", 611, NULL },
+ { "bracketleft", 333, NULL },
+ { "Ugrave", 722, NULL },
+ { "quoteright", 278, NULL },
+ { "Udieresis", 722, NULL },
+ { "perthousand", 1000, NULL },
+ { "Ydieresis", 667, NULL },
+ { "umacron", 611, NULL },
+ { "abreve", 556, NULL },
+ { "Eacute", 667, NULL },
+ { "adieresis", 556, NULL },
+ { "egrave", 556, NULL },
+ { "edieresis", 556, NULL },
+ { "idieresis", 278, NULL },
+ { "Eth", 722, NULL },
+ { "ae", 889, NULL },
+ { "asterisk", 389, NULL },
+ { "odieresis", 611, NULL },
+ { "Uacute", 722, NULL },
+ { "ugrave", 611, NULL },
+ { "nine", 556, NULL },
+ { "five", 556, NULL },
+ { "udieresis", 611, NULL },
+ { "Zcaron", 611, NULL },
+ { "Scommaaccent", 667, NULL },
+ { "threequarters", 834, NULL },
+ { "guillemotright", 556, NULL },
+ { "Ccedilla", 722, NULL },
+ { "ydieresis", 556, NULL },
+ { "tilde", 333, NULL },
+ { "dbldaggerumlaut", 556, NULL },
+ { "at", 975, NULL },
+ { "eacute", 556, NULL },
+ { "underscore", 556, NULL },
+ { "Euro", 556, NULL },
+ { "Dcroat", 722, NULL },
+ { "multiply", 584, NULL },
+ { "zero", 556, NULL },
+ { "eth", 611, NULL },
+ { "Scedilla", 667, NULL },
+ { "Ograve", 778, NULL },
+ { "Racute", 722, NULL },
+ { "partialdiff", 494, NULL },
+ { "uacute", 611, NULL },
+ { "braceleft", 389, NULL },
+ { "Thorn", 667, NULL },
+ { "zcaron", 500, NULL },
+ { "scommaaccent", 556, NULL },
+ { "ccedilla", 556, NULL },
+ { "Dcaron", 722, NULL },
+ { "dcroat", 611, NULL },
+ { "Ocircumflex", 778, NULL },
+ { "Oacute", 778, NULL },
+ { "scedilla", 556, NULL },
+ { "ogonek", 333, NULL },
+ { "ograve", 611, NULL },
+ { "racute", 389, NULL },
+ { "Tcaron", 611, NULL },
+ { "Eogonek", 667, NULL },
+ { "thorn", 611, NULL },
+ { "degree", 400, NULL },
+ { "registered", 737, NULL },
+ { "radical", 549, NULL },
+ { "Aring", 722, NULL },
+ { "percent", 889, NULL },
+ { "six", 556, NULL },
+ { "paragraph", 556, NULL },
+ { "dcaron", 743, NULL },
+ { "Uogonek", 722, NULL },
+ { "two", 556, NULL },
+ { "summation", 600, NULL },
+ { "Igrave", 278, NULL },
+ { "Lacute", 611, NULL },
+ { "ocircumflex", 611, NULL },
+ { "oacute", 611, NULL },
+ { "Uring", 722, NULL },
+ { "Lcommaaccent", 611, NULL },
+ { "tcaron", 389, NULL },
+ { "eogonek", 556, NULL },
+ { "Delta", 612, NULL },
+ { "Ohungarumlaut", 778, NULL },
+ { "asciicircum", 584, NULL },
+ { "aring", 556, NULL },
+ { "grave", 333, NULL },
+ { "uogonek", 611, NULL },
+ { "bracketright", 333, NULL },
+ { "Iacute", 278, NULL },
+ { "ampersand", 722, NULL },
+ { "igrave", 278, NULL },
+ { "lacute", 278, NULL },
+ { "Ncaron", 722, NULL },
+ { "plus", 584, NULL },
+ { "uring", 611, NULL },
+ { "quotesinglbase", 278, NULL },
+ { "lcommaaccent", 278, NULL },
+ { "Yacute", 667, NULL },
+ { "ohungarumlaut", 611, NULL },
+ { "threesuperior", 333, NULL },
+ { "acute", 333, NULL },
+ { "section", 556, NULL },
+ { "dieresis", 333, NULL },
+ { "iacute", 278, NULL },
+ { "quotedblbase", 500, NULL },
+ { "ncaron", 611, NULL },
+ { "florin", 556, NULL },
+ { "yacute", 556, NULL },
+ { "Rcommaaccent", 722, NULL },
+ { "fi", 611, NULL },
+ { "fl", 611, NULL },
+ { "Acircumflex", 722, NULL },
+ { "Cacute", 722, NULL },
+ { "Icircumflex", 278, NULL },
+ { "guillemotleft", 556, NULL },
+ { "germandbls", 611, NULL },
+ { "Amacron", 722, NULL },
+ { "seven", 556, NULL },
+ { "Sacute", 667, NULL },
+ { "ordmasculine", 365, NULL },
+ { "dotlessi", 278, NULL },
+ { "sterling", 556, NULL },
+ { "notequal", 549, NULL },
+ { "Imacron", 278, NULL },
+ { "rcommaaccent", 389, NULL },
+ { "Zdotaccent", 611, NULL },
+ { "acircumflex", 556, NULL },
+ { "cacute", 556, NULL },
+ { "Ecaron", 667, NULL },
+ { "icircumflex", 278, NULL },
+ { "braceright", 389, NULL },
+ { "quotedblright", 500, NULL },
+ { "amacron", 556, NULL },
+ { "sacute", 556, NULL },
+ { "imacron", 278, NULL },
+ { "cent", 556, NULL },
+ { "currency", 556, NULL },
+ { "logicalnot", 584, NULL },
+ { "zdotaccent", 500, NULL },
+ { "Atilde", 722, NULL },
+ { "breve", 333, NULL },
+ { "bar", 280, NULL },
+ { "fraction", 167, NULL },
+ { "less", 584, NULL },
+ { "ecaron", 556, NULL },
+ { "guilsinglleft", 333, NULL },
+ { "exclam", 333, NULL },
+ { "period", 278, NULL },
+ { "Rcaron", 722, NULL },
+ { "Kcommaaccent", 722, NULL },
+ { "greater", 584, NULL },
+ { "atilde", 556, NULL },
+ { "brokenbar", 280, NULL },
+ { "quoteleft", 278, NULL },
+ { "Edotaccent", 667, NULL },
+ { "onesuperior", 333, NULL }
+};
+
+static BuiltinFontWidth helveticaBoldObliqueWidthsTab[] = {
+ { "Ntilde", 722, NULL },
+ { "rcaron", 389, NULL },
+ { "kcommaaccent", 556, NULL },
+ { "Ncommaaccent", 722, NULL },
+ { "Zacute", 611, NULL },
+ { "comma", 278, NULL },
+ { "cedilla", 333, NULL },
+ { "plusminus", 584, NULL },
+ { "circumflex", 333, NULL },
+ { "dotaccent", 333, NULL },
+ { "edotaccent", 556, NULL },
+ { "asciitilde", 584, NULL },
+ { "colon", 333, NULL },
+ { "onehalf", 834, NULL },
+ { "dollar", 556, NULL },
+ { "Lcaron", 611, NULL },
+ { "ntilde", 611, NULL },
+ { "Aogonek", 722, NULL },
+ { "ncommaaccent", 611, NULL },
+ { "minus", 584, NULL },
+ { "Iogonek", 278, NULL },
+ { "zacute", 500, NULL },
+ { "yen", 556, NULL },
+ { "space", 278, NULL },
+ { "Omacron", 778, NULL },
+ { "questiondown", 611, NULL },
+ { "emdash", 1000, NULL },
+ { "Agrave", 722, NULL },
+ { "three", 556, NULL },
+ { "numbersign", 556, NULL },
+ { "lcaron", 400, NULL },
+ { "A", 722, NULL },
+ { "B", 722, NULL },
+ { "C", 722, NULL },
+ { "aogonek", 556, NULL },
+ { "D", 722, NULL },
+ { "E", 667, NULL },
+ { "onequarter", 834, NULL },
+ { "F", 611, NULL },
+ { "G", 778, NULL },
+ { "H", 722, NULL },
+ { "I", 278, NULL },
+ { "J", 556, NULL },
+ { "K", 722, NULL },
+ { "iogonek", 278, NULL },
+ { "backslash", 278, NULL },
+ { "L", 611, NULL },
+ { "periodcentered", 278, NULL },
+ { "M", 833, NULL },
+ { "N", 722, NULL },
+ { "omacron", 611, NULL },
+ { "Tcommaaccent", 611, NULL },
+ { "O", 778, NULL },
+ { "P", 667, NULL },
+ { "Q", 778, NULL },
+ { "Uhungarumlaut", 722, NULL },
+ { "R", 722, NULL },
+ { "Aacute", 722, NULL },
+ { "caron", 333, NULL },
+ { "S", 667, NULL },
+ { "T", 611, NULL },
+ { "U", 722, NULL },
+ { "agrave", 556, NULL },
+ { "V", 667, NULL },
+ { "W", 944, NULL },
+ { "X", 667, NULL },
+ { "question", 611, NULL },
+ { "equal", 584, NULL },
+ { "Y", 667, NULL },
+ { "Z", 611, NULL },
+ { "four", 556, NULL },
+ { "a", 556, NULL },
+ { "Gcommaaccent", 778, NULL },
+ { "b", 611, NULL },
+ { "c", 556, NULL },
+ { "d", 611, NULL },
+ { "e", 556, NULL },
+ { "f", 333, NULL },
+ { "g", 611, NULL },
+ { "bullet", 350, NULL },
+ { "h", 611, NULL },
+ { "i", 278, NULL },
+ { "Oslash", 778, NULL },
+ { "dagger", 556, NULL },
+ { "j", 278, NULL },
+ { "k", 556, NULL },
+ { "l", 278, NULL },
+ { "m", 889, NULL },
+ { "n", 611, NULL },
+ { "tcommaaccent", 333, NULL },
+ { "o", 611, NULL },
+ { "ordfeminine", 370, NULL },
+ { "ring", 333, NULL },
+ { "p", 611, NULL },
+ { "q", 611, NULL },
+ { "uhungarumlaut", 611, NULL },
+ { "r", 389, NULL },
+ { "twosuperior", 333, NULL },
+ { "aacute", 556, NULL },
+ { "s", 556, NULL },
+ { "OE", 1000, NULL },
+ { "t", 333, NULL },
+ { "divide", 584, NULL },
+ { "u", 611, NULL },
+ { "Ccaron", 722, NULL },
+ { "v", 556, NULL },
+ { "w", 778, NULL },
+ { "x", 556, NULL },
+ { "y", 556, NULL },
+ { "z", 500, NULL },
+ { "Gbreve", 778, NULL },
+ { "commaaccent", 250, NULL },
+ { "hungarumlaut", 333, NULL },
+ { "Idotaccent", 278, NULL },
+ { "Nacute", 722, NULL },
+ { "quotedbl", 474, NULL },
+ { "gcommaaccent", 611, NULL },
+ { "mu", 611, NULL },
+ { "greaterequal", 549, NULL },
+ { "Scaron", 667, NULL },
+ { "Lslash", 611, NULL },
+ { "semicolon", 333, NULL },
+ { "oslash", 611, NULL },
+ { "lessequal", 549, NULL },
+ { "lozenge", 494, NULL },
+ { "parenright", 333, NULL },
+ { "ccaron", 556, NULL },
+ { "Ecircumflex", 667, NULL },
+ { "gbreve", 611, NULL },
+ { "trademark", 1000, NULL },
+ { "daggerdbl", 556, NULL },
+ { "nacute", 611, NULL },
+ { "macron", 333, NULL },
+ { "Otilde", 778, NULL },
+ { "Emacron", 667, NULL },
+ { "ellipsis", 1000, NULL },
+ { "scaron", 556, NULL },
+ { "AE", 1000, NULL },
+ { "Ucircumflex", 722, NULL },
+ { "lslash", 278, NULL },
+ { "quotedblleft", 500, NULL },
+ { "guilsinglright", 333, NULL },
+ { "hyphen", 333, NULL },
+ { "quotesingle", 238, NULL },
+ { "eight", 556, NULL },
+ { "exclamdown", 333, NULL },
+ { "endash", 556, NULL },
+ { "oe", 944, NULL },
+ { "Abreve", 722, NULL },
+ { "Umacron", 722, NULL },
+ { "ecircumflex", 556, NULL },
+ { "Adieresis", 722, NULL },
+ { "copyright", 737, NULL },
+ { "Egrave", 667, NULL },
+ { "slash", 278, NULL },
+ { "Edieresis", 667, NULL },
+ { "otilde", 611, NULL },
+ { "Idieresis", 278, NULL },
+ { "parenleft", 333, NULL },
+ { "one", 556, NULL },
+ { "emacron", 556, NULL },
+ { "Odieresis", 778, NULL },
+ { "ucircumflex", 611, NULL },
+ { "bracketleft", 333, NULL },
+ { "Ugrave", 722, NULL },
+ { "quoteright", 278, NULL },
+ { "Udieresis", 722, NULL },
+ { "perthousand", 1000, NULL },
+ { "Ydieresis", 667, NULL },
+ { "umacron", 611, NULL },
+ { "abreve", 556, NULL },
+ { "Eacute", 667, NULL },
+ { "adieresis", 556, NULL },
+ { "egrave", 556, NULL },
+ { "edieresis", 556, NULL },
+ { "idieresis", 278, NULL },
+ { "Eth", 722, NULL },
+ { "ae", 889, NULL },
+ { "asterisk", 389, NULL },
+ { "odieresis", 611, NULL },
+ { "Uacute", 722, NULL },
+ { "ugrave", 611, NULL },
+ { "nine", 556, NULL },
+ { "five", 556, NULL },
+ { "udieresis", 611, NULL },
+ { "Zcaron", 611, NULL },
+ { "Scommaaccent", 667, NULL },
+ { "threequarters", 834, NULL },
+ { "guillemotright", 556, NULL },
+ { "Ccedilla", 722, NULL },
+ { "ydieresis", 556, NULL },
+ { "tilde", 333, NULL },
+ { "at", 975, NULL },
+ { "eacute", 556, NULL },
+ { "underscore", 556, NULL },
+ { "Euro", 556, NULL },
+ { "Dcroat", 722, NULL },
+ { "multiply", 584, NULL },
+ { "zero", 556, NULL },
+ { "eth", 611, NULL },
+ { "Scedilla", 667, NULL },
+ { "Ograve", 778, NULL },
+ { "Racute", 722, NULL },
+ { "partialdiff", 494, NULL },
+ { "uacute", 611, NULL },
+ { "braceleft", 389, NULL },
+ { "Thorn", 667, NULL },
+ { "zcaron", 500, NULL },
+ { "scommaaccent", 556, NULL },
+ { "ccedilla", 556, NULL },
+ { "Dcaron", 722, NULL },
+ { "dcroat", 611, NULL },
+ { "Ocircumflex", 778, NULL },
+ { "Oacute", 778, NULL },
+ { "scedilla", 556, NULL },
+ { "ogonek", 333, NULL },
+ { "ograve", 611, NULL },
+ { "racute", 389, NULL },
+ { "Tcaron", 611, NULL },
+ { "Eogonek", 667, NULL },
+ { "thorn", 611, NULL },
+ { "degree", 400, NULL },
+ { "registered", 737, NULL },
+ { "radical", 549, NULL },
+ { "Aring", 722, NULL },
+ { "percent", 889, NULL },
+ { "six", 556, NULL },
+ { "paragraph", 556, NULL },
+ { "dcaron", 743, NULL },
+ { "Uogonek", 722, NULL },
+ { "two", 556, NULL },
+ { "summation", 600, NULL },
+ { "Igrave", 278, NULL },
+ { "Lacute", 611, NULL },
+ { "ocircumflex", 611, NULL },
+ { "oacute", 611, NULL },
+ { "Uring", 722, NULL },
+ { "Lcommaaccent", 611, NULL },
+ { "tcaron", 389, NULL },
+ { "eogonek", 556, NULL },
+ { "Delta", 612, NULL },
+ { "Ohungarumlaut", 778, NULL },
+ { "asciicircum", 584, NULL },
+ { "aring", 556, NULL },
+ { "grave", 333, NULL },
+ { "uogonek", 611, NULL },
+ { "bracketright", 333, NULL },
+ { "Iacute", 278, NULL },
+ { "ampersand", 722, NULL },
+ { "igrave", 278, NULL },
+ { "lacute", 278, NULL },
+ { "Ncaron", 722, NULL },
+ { "plus", 584, NULL },
+ { "uring", 611, NULL },
+ { "quotesinglbase", 278, NULL },
+ { "lcommaaccent", 278, NULL },
+ { "Yacute", 667, NULL },
+ { "ohungarumlaut", 611, NULL },
+ { "threesuperior", 333, NULL },
+ { "acute", 333, NULL },
+ { "section", 556, NULL },
+ { "dieresis", 333, NULL },
+ { "iacute", 278, NULL },
+ { "quotedblbase", 500, NULL },
+ { "ncaron", 611, NULL },
+ { "florin", 556, NULL },
+ { "yacute", 556, NULL },
+ { "Rcommaaccent", 722, NULL },
+ { "fi", 611, NULL },
+ { "fl", 611, NULL },
+ { "Acircumflex", 722, NULL },
+ { "Cacute", 722, NULL },
+ { "Icircumflex", 278, NULL },
+ { "guillemotleft", 556, NULL },
+ { "germandbls", 611, NULL },
+ { "Amacron", 722, NULL },
+ { "seven", 556, NULL },
+ { "Sacute", 667, NULL },
+ { "ordmasculine", 365, NULL },
+ { "dotlessi", 278, NULL },
+ { "sterling", 556, NULL },
+ { "notequal", 549, NULL },
+ { "Imacron", 278, NULL },
+ { "rcommaaccent", 389, NULL },
+ { "Zdotaccent", 611, NULL },
+ { "acircumflex", 556, NULL },
+ { "cacute", 556, NULL },
+ { "Ecaron", 667, NULL },
+ { "icircumflex", 278, NULL },
+ { "braceright", 389, NULL },
+ { "quotedblright", 500, NULL },
+ { "amacron", 556, NULL },
+ { "sacute", 556, NULL },
+ { "imacron", 278, NULL },
+ { "cent", 556, NULL },
+ { "currency", 556, NULL },
+ { "logicalnot", 584, NULL },
+ { "zdotaccent", 500, NULL },
+ { "Atilde", 722, NULL },
+ { "breve", 333, NULL },
+ { "bar", 280, NULL },
+ { "fraction", 167, NULL },
+ { "less", 584, NULL },
+ { "ecaron", 556, NULL },
+ { "guilsinglleft", 333, NULL },
+ { "exclam", 333, NULL },
+ { "period", 278, NULL },
+ { "Rcaron", 722, NULL },
+ { "Kcommaaccent", 722, NULL },
+ { "greater", 584, NULL },
+ { "atilde", 556, NULL },
+ { "brokenbar", 280, NULL },
+ { "quoteleft", 278, NULL },
+ { "Edotaccent", 667, NULL },
+ { "onesuperior", 333, NULL }
+};
+
+static BuiltinFontWidth helveticaObliqueWidthsTab[] = {
+ { "Ntilde", 722, NULL },
+ { "rcaron", 333, NULL },
+ { "kcommaaccent", 500, NULL },
+ { "Ncommaaccent", 722, NULL },
+ { "Zacute", 611, NULL },
+ { "comma", 278, NULL },
+ { "cedilla", 333, NULL },
+ { "plusminus", 584, NULL },
+ { "circumflex", 333, NULL },
+ { "dotaccent", 333, NULL },
+ { "edotaccent", 556, NULL },
+ { "asciitilde", 584, NULL },
+ { "colon", 278, NULL },
+ { "onehalf", 834, NULL },
+ { "dollar", 556, NULL },
+ { "Lcaron", 556, NULL },
+ { "ntilde", 556, NULL },
+ { "Aogonek", 667, NULL },
+ { "ncommaaccent", 556, NULL },
+ { "minus", 584, NULL },
+ { "Iogonek", 278, NULL },
+ { "zacute", 500, NULL },
+ { "yen", 556, NULL },
+ { "space", 278, NULL },
+ { "Omacron", 778, NULL },
+ { "questiondown", 611, NULL },
+ { "emdash", 1000, NULL },
+ { "Agrave", 667, NULL },
+ { "three", 556, NULL },
+ { "numbersign", 556, NULL },
+ { "lcaron", 299, NULL },
+ { "A", 667, NULL },
+ { "B", 667, NULL },
+ { "C", 722, NULL },
+ { "aogonek", 556, NULL },
+ { "D", 722, NULL },
+ { "E", 667, NULL },
+ { "onequarter", 834, NULL },
+ { "F", 611, NULL },
+ { "G", 778, NULL },
+ { "H", 722, NULL },
+ { "I", 278, NULL },
+ { "J", 500, NULL },
+ { "K", 667, NULL },
+ { "iogonek", 222, NULL },
+ { "backslash", 278, NULL },
+ { "L", 556, NULL },
+ { "periodcentered", 278, NULL },
+ { "M", 833, NULL },
+ { "N", 722, NULL },
+ { "omacron", 556, NULL },
+ { "Tcommaaccent", 611, NULL },
+ { "O", 778, NULL },
+ { "P", 667, NULL },
+ { "Q", 778, NULL },
+ { "Uhungarumlaut", 722, NULL },
+ { "R", 722, NULL },
+ { "Aacute", 667, NULL },
+ { "caron", 333, NULL },
+ { "S", 667, NULL },
+ { "T", 611, NULL },
+ { "U", 722, NULL },
+ { "agrave", 556, NULL },
+ { "V", 667, NULL },
+ { "W", 944, NULL },
+ { "X", 667, NULL },
+ { "question", 556, NULL },
+ { "equal", 584, NULL },
+ { "Y", 667, NULL },
+ { "Z", 611, NULL },
+ { "four", 556, NULL },
+ { "a", 556, NULL },
+ { "Gcommaaccent", 778, NULL },
+ { "b", 556, NULL },
+ { "c", 500, NULL },
+ { "d", 556, NULL },
+ { "e", 556, NULL },
+ { "f", 278, NULL },
+ { "g", 556, NULL },
+ { "bullet", 350, NULL },
+ { "h", 556, NULL },
+ { "i", 222, NULL },
+ { "Oslash", 778, NULL },
+ { "dagger", 556, NULL },
+ { "j", 222, NULL },
+ { "k", 500, NULL },
+ { "l", 222, NULL },
+ { "m", 833, NULL },
+ { "n", 556, NULL },
+ { "tcommaaccent", 278, NULL },
+ { "o", 556, NULL },
+ { "ordfeminine", 370, NULL },
+ { "ring", 333, NULL },
+ { "p", 556, NULL },
+ { "q", 556, NULL },
+ { "uhungarumlaut", 556, NULL },
+ { "r", 333, NULL },
+ { "twosuperior", 333, NULL },
+ { "aacute", 556, NULL },
+ { "s", 500, NULL },
+ { "OE", 1000, NULL },
+ { "t", 278, NULL },
+ { "divide", 584, NULL },
+ { "u", 556, NULL },
+ { "Ccaron", 722, NULL },
+ { "v", 500, NULL },
+ { "w", 722, NULL },
+ { "x", 500, NULL },
+ { "y", 500, NULL },
+ { "z", 500, NULL },
+ { "Gbreve", 778, NULL },
+ { "commaaccent", 250, NULL },
+ { "hungarumlaut", 333, NULL },
+ { "Idotaccent", 278, NULL },
+ { "Nacute", 722, NULL },
+ { "quotedbl", 355, NULL },
+ { "gcommaaccent", 556, NULL },
+ { "mu", 556, NULL },
+ { "greaterequal", 549, NULL },
+ { "Scaron", 667, NULL },
+ { "Lslash", 556, NULL },
+ { "semicolon", 278, NULL },
+ { "oslash", 611, NULL },
+ { "lessequal", 549, NULL },
+ { "lozenge", 471, NULL },
+ { "parenright", 333, NULL },
+ { "ccaron", 500, NULL },
+ { "Ecircumflex", 667, NULL },
+ { "gbreve", 556, NULL },
+ { "trademark", 1000, NULL },
+ { "daggerdbl", 556, NULL },
+ { "nacute", 556, NULL },
+ { "macron", 333, NULL },
+ { "Otilde", 778, NULL },
+ { "Emacron", 667, NULL },
+ { "ellipsis", 1000, NULL },
+ { "scaron", 500, NULL },
+ { "AE", 1000, NULL },
+ { "Ucircumflex", 722, NULL },
+ { "lslash", 222, NULL },
+ { "quotedblleft", 333, NULL },
+ { "guilsinglright", 333, NULL },
+ { "hyphen", 333, NULL },
+ { "quotesingle", 191, NULL },
+ { "eight", 556, NULL },
+ { "exclamdown", 333, NULL },
+ { "endash", 556, NULL },
+ { "oe", 944, NULL },
+ { "Abreve", 667, NULL },
+ { "Umacron", 722, NULL },
+ { "ecircumflex", 556, NULL },
+ { "Adieresis", 667, NULL },
+ { "copyright", 737, NULL },
+ { "Egrave", 667, NULL },
+ { "slash", 278, NULL },
+ { "Edieresis", 667, NULL },
+ { "otilde", 556, NULL },
+ { "Idieresis", 278, NULL },
+ { "parenleft", 333, NULL },
+ { "one", 556, NULL },
+ { "emacron", 556, NULL },
+ { "Odieresis", 778, NULL },
+ { "ucircumflex", 556, NULL },
+ { "bracketleft", 278, NULL },
+ { "Ugrave", 722, NULL },
+ { "quoteright", 222, NULL },
+ { "Udieresis", 722, NULL },
+ { "perthousand", 1000, NULL },
+ { "Ydieresis", 667, NULL },
+ { "umacron", 556, NULL },
+ { "abreve", 556, NULL },
+ { "Eacute", 667, NULL },
+ { "adieresis", 556, NULL },
+ { "egrave", 556, NULL },
+ { "edieresis", 556, NULL },
+ { "idieresis", 278, NULL },
+ { "Eth", 722, NULL },
+ { "ae", 889, NULL },
+ { "asterisk", 389, NULL },
+ { "odieresis", 556, NULL },
+ { "Uacute", 722, NULL },
+ { "ugrave", 556, NULL },
+ { "nine", 556, NULL },
+ { "five", 556, NULL },
+ { "udieresis", 556, NULL },
+ { "Zcaron", 611, NULL },
+ { "Scommaaccent", 667, NULL },
+ { "threequarters", 834, NULL },
+ { "guillemotright", 556, NULL },
+ { "Ccedilla", 722, NULL },
+ { "ydieresis", 500, NULL },
+ { "tilde", 333, NULL },
+ { "at", 1015, NULL },
+ { "eacute", 556, NULL },
+ { "underscore", 556, NULL },
+ { "Euro", 556, NULL },
+ { "Dcroat", 722, NULL },
+ { "multiply", 584, NULL },
+ { "zero", 556, NULL },
+ { "eth", 556, NULL },
+ { "Scedilla", 667, NULL },
+ { "Ograve", 778, NULL },
+ { "Racute", 722, NULL },
+ { "partialdiff", 476, NULL },
+ { "uacute", 556, NULL },
+ { "braceleft", 334, NULL },
+ { "Thorn", 667, NULL },
+ { "zcaron", 500, NULL },
+ { "scommaaccent", 500, NULL },
+ { "ccedilla", 500, NULL },
+ { "Dcaron", 722, NULL },
+ { "dcroat", 556, NULL },
+ { "Ocircumflex", 778, NULL },
+ { "Oacute", 778, NULL },
+ { "scedilla", 500, NULL },
+ { "ogonek", 333, NULL },
+ { "ograve", 556, NULL },
+ { "racute", 333, NULL },
+ { "Tcaron", 611, NULL },
+ { "Eogonek", 667, NULL },
+ { "thorn", 556, NULL },
+ { "degree", 400, NULL },
+ { "registered", 737, NULL },
+ { "radical", 453, NULL },
+ { "Aring", 667, NULL },
+ { "percent", 889, NULL },
+ { "six", 556, NULL },
+ { "paragraph", 537, NULL },
+ { "dcaron", 643, NULL },
+ { "Uogonek", 722, NULL },
+ { "two", 556, NULL },
+ { "summation", 600, NULL },
+ { "Igrave", 278, NULL },
+ { "Lacute", 556, NULL },
+ { "ocircumflex", 556, NULL },
+ { "oacute", 556, NULL },
+ { "Uring", 722, NULL },
+ { "Lcommaaccent", 556, NULL },
+ { "tcaron", 317, NULL },
+ { "eogonek", 556, NULL },
+ { "Delta", 612, NULL },
+ { "Ohungarumlaut", 778, NULL },
+ { "asciicircum", 469, NULL },
+ { "aring", 556, NULL },
+ { "grave", 333, NULL },
+ { "uogonek", 556, NULL },
+ { "bracketright", 278, NULL },
+ { "Iacute", 278, NULL },
+ { "ampersand", 667, NULL },
+ { "igrave", 278, NULL },
+ { "lacute", 222, NULL },
+ { "Ncaron", 722, NULL },
+ { "plus", 584, NULL },
+ { "uring", 556, NULL },
+ { "quotesinglbase", 222, NULL },
+ { "lcommaaccent", 222, NULL },
+ { "Yacute", 667, NULL },
+ { "ohungarumlaut", 556, NULL },
+ { "threesuperior", 333, NULL },
+ { "acute", 333, NULL },
+ { "section", 556, NULL },
+ { "dieresis", 333, NULL },
+ { "iacute", 278, NULL },
+ { "quotedblbase", 333, NULL },
+ { "ncaron", 556, NULL },
+ { "florin", 556, NULL },
+ { "yacute", 500, NULL },
+ { "Rcommaaccent", 722, NULL },
+ { "fi", 500, NULL },
+ { "fl", 500, NULL },
+ { "Acircumflex", 667, NULL },
+ { "Cacute", 722, NULL },
+ { "Icircumflex", 278, NULL },
+ { "guillemotleft", 556, NULL },
+ { "germandbls", 611, NULL },
+ { "Amacron", 667, NULL },
+ { "seven", 556, NULL },
+ { "Sacute", 667, NULL },
+ { "ordmasculine", 365, NULL },
+ { "dotlessi", 278, NULL },
+ { "sterling", 556, NULL },
+ { "notequal", 549, NULL },
+ { "Imacron", 278, NULL },
+ { "rcommaaccent", 333, NULL },
+ { "Zdotaccent", 611, NULL },
+ { "acircumflex", 556, NULL },
+ { "cacute", 500, NULL },
+ { "Ecaron", 667, NULL },
+ { "icircumflex", 278, NULL },
+ { "braceright", 334, NULL },
+ { "quotedblright", 333, NULL },
+ { "amacron", 556, NULL },
+ { "sacute", 500, NULL },
+ { "imacron", 278, NULL },
+ { "cent", 556, NULL },
+ { "currency", 556, NULL },
+ { "logicalnot", 584, NULL },
+ { "zdotaccent", 500, NULL },
+ { "Atilde", 667, NULL },
+ { "breve", 333, NULL },
+ { "bar", 260, NULL },
+ { "fraction", 167, NULL },
+ { "less", 584, NULL },
+ { "ecaron", 556, NULL },
+ { "guilsinglleft", 333, NULL },
+ { "exclam", 278, NULL },
+ { "period", 278, NULL },
+ { "Rcaron", 722, NULL },
+ { "Kcommaaccent", 667, NULL },
+ { "greater", 584, NULL },
+ { "atilde", 556, NULL },
+ { "brokenbar", 260, NULL },
+ { "quoteleft", 222, NULL },
+ { "Edotaccent", 667, NULL },
+ { "onesuperior", 333, NULL }
+};
+
+static BuiltinFontWidth symbolWidthsTab[] = {
+ { "bracketleftex", 384, NULL },
+ { "alpha", 631, NULL },
+ { "union", 768, NULL },
+ { "infinity", 713, NULL },
+ { "comma", 250, NULL },
+ { "copyrightsans", 790, NULL },
+ { "plusminus", 549, NULL },
+ { "arrowup", 603, NULL },
+ { "apple", 790, NULL },
+ { "parenleftbt", 384, NULL },
+ { "notelement", 713, NULL },
+ { "colon", 278, NULL },
+ { "beta", 549, NULL },
+ { "braceleftbt", 494, NULL },
+ { "Lambda", 686, NULL },
+ { "Phi", 763, NULL },
+ { "minus", 549, NULL },
+ { "space", 250, NULL },
+ { "Sigma", 592, NULL },
+ { "approxequal", 549, NULL },
+ { "minute", 247, NULL },
+ { "circleplus", 768, NULL },
+ { "Omicron", 722, NULL },
+ { "three", 500, NULL },
+ { "numbersign", 500, NULL },
+ { "lambda", 549, NULL },
+ { "phi", 521, NULL },
+ { "aleph", 823, NULL },
+ { "Tau", 611, NULL },
+ { "spade", 753, NULL },
+ { "logicaland", 603, NULL },
+ { "sigma", 603, NULL },
+ { "propersuperset", 713, NULL },
+ { "omicron", 549, NULL },
+ { "question", 444, NULL },
+ { "equal", 549, NULL },
+ { "Epsilon", 611, NULL },
+ { "emptyset", 823, NULL },
+ { "diamond", 753, NULL },
+ { "four", 500, NULL },
+ { "Mu", 889, NULL },
+ { "parenlefttp", 384, NULL },
+ { "club", 753, NULL },
+ { "bullet", 460, NULL },
+ { "Omega", 768, NULL },
+ { "tau", 439, NULL },
+ { "Upsilon", 690, NULL },
+ { "bracelefttp", 494, NULL },
+ { "heart", 753, NULL },
+ { "divide", 549, NULL },
+ { "epsilon", 439, NULL },
+ { "logicalor", 603, NULL },
+ { "parenleftex", 384, NULL },
+ { "greaterequal", 549, NULL },
+ { "mu", 576, NULL },
+ { "Nu", 722, NULL },
+ { "therefore", 863, NULL },
+ { "notsubset", 713, NULL },
+ { "omega", 686, NULL },
+ { "semicolon", 278, NULL },
+ { "element", 713, NULL },
+ { "upsilon", 576, NULL },
+ { "existential", 549, NULL },
+ { "integralbt", 686, NULL },
+ { "lessequal", 549, NULL },
+ { "phi1", 603, NULL },
+ { "lozenge", 494, NULL },
+ { "trademarkserif", 890, NULL },
+ { "parenright", 333, NULL },
+ { "reflexsuperset", 713, NULL },
+ { "sigma1", 439, NULL },
+ { "nu", 521, NULL },
+ { "Gamma", 603, NULL },
+ { "angleright", 329, NULL },
+ { "ellipsis", 1000, NULL },
+ { "Rho", 556, NULL },
+ { "parenrightbt", 384, NULL },
+ { "radicalex", 500, NULL },
+ { "eight", 500, NULL },
+ { "angleleft", 329, NULL },
+ { "arrowdbldown", 603, NULL },
+ { "congruent", 549, NULL },
+ { "Theta", 741, NULL },
+ { "intersection", 768, NULL },
+ { "Pi", 768, NULL },
+ { "slash", 278, NULL },
+ { "registerserif", 790, NULL },
+ { "parenleft", 333, NULL },
+ { "one", 500, NULL },
+ { "gamma", 411, NULL },
+ { "bracketleft", 333, NULL },
+ { "rho", 549, NULL },
+ { "circlemultiply", 768, NULL },
+ { "Chi", 722, NULL },
+ { "theta", 521, NULL },
+ { "pi", 549, NULL },
+ { "integraltp", 686, NULL },
+ { "Eta", 722, NULL },
+ { "product", 823, NULL },
+ { "nine", 500, NULL },
+ { "five", 500, NULL },
+ { "propersubset", 713, NULL },
+ { "bracketrightbt", 384, NULL },
+ { "trademarksans", 786, NULL },
+ { "dotmath", 250, NULL },
+ { "integralex", 686, NULL },
+ { "chi", 549, NULL },
+ { "parenrighttp", 384, NULL },
+ { "eta", 603, NULL },
+ { "underscore", 500, NULL },
+ { "Euro", 750, NULL },
+ { "multiply", 549, NULL },
+ { "zero", 500, NULL },
+ { "partialdiff", 494, NULL },
+ { "angle", 768, NULL },
+ { "arrowdblleft", 987, NULL },
+ { "braceleft", 480, NULL },
+ { "parenrightex", 384, NULL },
+ { "Rfraktur", 795, NULL },
+ { "Zeta", 611, NULL },
+ { "braceex", 494, NULL },
+ { "arrowdblup", 603, NULL },
+ { "arrowdown", 603, NULL },
+ { "Ifraktur", 686, NULL },
+ { "degree", 400, NULL },
+ { "Iota", 333, NULL },
+ { "perpendicular", 658, NULL },
+ { "radical", 549, NULL },
+ { "asteriskmath", 500, NULL },
+ { "percent", 833, NULL },
+ { "zeta", 494, NULL },
+ { "six", 500, NULL },
+ { "two", 500, NULL },
+ { "weierstrass", 987, NULL },
+ { "summation", 713, NULL },
+ { "bracketrighttp", 384, NULL },
+ { "carriagereturn", 658, NULL },
+ { "suchthat", 439, NULL },
+ { "arrowvertex", 603, NULL },
+ { "Delta", 612, NULL },
+ { "iota", 329, NULL },
+ { "arrowhorizex", 1000, NULL },
+ { "bracketrightex", 384, NULL },
+ { "bracketright", 333, NULL },
+ { "ampersand", 778, NULL },
+ { "plus", 549, NULL },
+ { "proportional", 713, NULL },
+ { "delta", 494, NULL },
+ { "copyrightserif", 790, NULL },
+ { "bracerightmid", 494, NULL },
+ { "arrowleft", 987, NULL },
+ { "second", 411, NULL },
+ { "arrowdblboth", 1042, NULL },
+ { "florin", 500, NULL },
+ { "Psi", 795, NULL },
+ { "bracerightbt", 494, NULL },
+ { "bracketleftbt", 384, NULL },
+ { "seven", 500, NULL },
+ { "braceleftmid", 494, NULL },
+ { "notequal", 549, NULL },
+ { "psi", 686, NULL },
+ { "equivalence", 549, NULL },
+ { "universal", 713, NULL },
+ { "arrowdblright", 987, NULL },
+ { "braceright", 480, NULL },
+ { "reflexsubset", 713, NULL },
+ { "Xi", 645, NULL },
+ { "theta1", 631, NULL },
+ { "logicalnot", 713, NULL },
+ { "Kappa", 722, NULL },
+ { "similar", 549, NULL },
+ { "bar", 200, NULL },
+ { "fraction", 167, NULL },
+ { "less", 549, NULL },
+ { "registersans", 790, NULL },
+ { "omega1", 713, NULL },
+ { "exclam", 333, NULL },
+ { "Upsilon1", 620, NULL },
+ { "bracerighttp", 494, NULL },
+ { "xi", 493, NULL },
+ { "period", 250, NULL },
+ { "Alpha", 722, NULL },
+ { "arrowright", 987, NULL },
+ { "greater", 549, NULL },
+ { "bracketlefttp", 384, NULL },
+ { "kappa", 549, NULL },
+ { "gradient", 713, NULL },
+ { "integral", 274, NULL },
+ { "arrowboth", 1042, NULL },
+ { "Beta", 667, NULL }
+};
+
+static BuiltinFontWidth timesBoldWidthsTab[] = {
+ { "Ntilde", 722, NULL },
+ { "rcaron", 444, NULL },
+ { "kcommaaccent", 556, NULL },
+ { "Ncommaaccent", 722, NULL },
+ { "Zacute", 667, NULL },
+ { "comma", 250, NULL },
+ { "cedilla", 333, NULL },
+ { "plusminus", 570, NULL },
+ { "circumflex", 333, NULL },
+ { "dotaccent", 333, NULL },
+ { "edotaccent", 444, NULL },
+ { "asciitilde", 520, NULL },
+ { "colon", 333, NULL },
+ { "onehalf", 750, NULL },
+ { "dollar", 500, NULL },
+ { "Lcaron", 667, NULL },
+ { "ntilde", 556, NULL },
+ { "Aogonek", 722, NULL },
+ { "ncommaaccent", 556, NULL },
+ { "minus", 570, NULL },
+ { "Iogonek", 389, NULL },
+ { "zacute", 444, NULL },
+ { "yen", 500, NULL },
+ { "space", 250, NULL },
+ { "Omacron", 778, NULL },
+ { "questiondown", 500, NULL },
+ { "emdash", 1000, NULL },
+ { "Agrave", 722, NULL },
+ { "three", 500, NULL },
+ { "numbersign", 500, NULL },
+ { "lcaron", 394, NULL },
+ { "A", 722, NULL },
+ { "B", 667, NULL },
+ { "C", 722, NULL },
+ { "aogonek", 500, NULL },
+ { "D", 722, NULL },
+ { "E", 667, NULL },
+ { "onequarter", 750, NULL },
+ { "F", 611, NULL },
+ { "G", 778, NULL },
+ { "H", 778, NULL },
+ { "I", 389, NULL },
+ { "J", 500, NULL },
+ { "K", 778, NULL },
+ { "iogonek", 278, NULL },
+ { "backslash", 278, NULL },
+ { "L", 667, NULL },
+ { "periodcentered", 250, NULL },
+ { "M", 944, NULL },
+ { "N", 722, NULL },
+ { "omacron", 500, NULL },
+ { "Tcommaaccent", 667, NULL },
+ { "O", 778, NULL },
+ { "P", 611, NULL },
+ { "Q", 778, NULL },
+ { "Uhungarumlaut", 722, NULL },
+ { "R", 722, NULL },
+ { "Aacute", 722, NULL },
+ { "caron", 333, NULL },
+ { "S", 556, NULL },
+ { "T", 667, NULL },
+ { "U", 722, NULL },
+ { "agrave", 500, NULL },
+ { "V", 722, NULL },
+ { "W", 1000, NULL },
+ { "X", 722, NULL },
+ { "question", 500, NULL },
+ { "equal", 570, NULL },
+ { "Y", 722, NULL },
+ { "Z", 667, NULL },
+ { "four", 500, NULL },
+ { "a", 500, NULL },
+ { "Gcommaaccent", 778, NULL },
+ { "b", 556, NULL },
+ { "c", 444, NULL },
+ { "d", 556, NULL },
+ { "e", 444, NULL },
+ { "f", 333, NULL },
+ { "g", 500, NULL },
+ { "bullet", 350, NULL },
+ { "h", 556, NULL },
+ { "i", 278, NULL },
+ { "Oslash", 778, NULL },
+ { "dagger", 500, NULL },
+ { "j", 333, NULL },
+ { "k", 556, NULL },
+ { "l", 278, NULL },
+ { "m", 833, NULL },
+ { "n", 556, NULL },
+ { "tcommaaccent", 333, NULL },
+ { "o", 500, NULL },
+ { "ordfeminine", 300, NULL },
+ { "ring", 333, NULL },
+ { "p", 556, NULL },
+ { "q", 556, NULL },
+ { "uhungarumlaut", 556, NULL },
+ { "r", 444, NULL },
+ { "twosuperior", 300, NULL },
+ { "aacute", 500, NULL },
+ { "s", 389, NULL },
+ { "OE", 1000, NULL },
+ { "t", 333, NULL },
+ { "divide", 570, NULL },
+ { "u", 556, NULL },
+ { "Ccaron", 722, NULL },
+ { "v", 500, NULL },
+ { "w", 722, NULL },
+ { "x", 500, NULL },
+ { "y", 500, NULL },
+ { "z", 444, NULL },
+ { "Gbreve", 778, NULL },
+ { "commaaccent", 250, NULL },
+ { "hungarumlaut", 333, NULL },
+ { "Idotaccent", 389, NULL },
+ { "Nacute", 722, NULL },
+ { "quotedbl", 555, NULL },
+ { "gcommaaccent", 500, NULL },
+ { "mu", 556, NULL },
+ { "greaterequal", 549, NULL },
+ { "Scaron", 556, NULL },
+ { "Lslash", 667, NULL },
+ { "semicolon", 333, NULL },
+ { "oslash", 500, NULL },
+ { "lessequal", 549, NULL },
+ { "lozenge", 494, NULL },
+ { "parenright", 333, NULL },
+ { "ccaron", 444, NULL },
+ { "Ecircumflex", 667, NULL },
+ { "gbreve", 500, NULL },
+ { "trademark", 1000, NULL },
+ { "daggerdbl", 500, NULL },
+ { "nacute", 556, NULL },
+ { "macron", 333, NULL },
+ { "Otilde", 778, NULL },
+ { "Emacron", 667, NULL },
+ { "ellipsis", 1000, NULL },
+ { "scaron", 389, NULL },
+ { "AE", 1000, NULL },
+ { "Ucircumflex", 722, NULL },
+ { "lslash", 278, NULL },
+ { "quotedblleft", 500, NULL },
+ { "guilsinglright", 333, NULL },
+ { "hyphen", 333, NULL },
+ { "quotesingle", 278, NULL },
+ { "eight", 500, NULL },
+ { "exclamdown", 333, NULL },
+ { "endash", 500, NULL },
+ { "oe", 722, NULL },
+ { "Abreve", 722, NULL },
+ { "Umacron", 722, NULL },
+ { "ecircumflex", 444, NULL },
+ { "Adieresis", 722, NULL },
+ { "copyright", 747, NULL },
+ { "Egrave", 667, NULL },
+ { "slash", 278, NULL },
+ { "Edieresis", 667, NULL },
+ { "otilde", 500, NULL },
+ { "Idieresis", 389, NULL },
+ { "parenleft", 333, NULL },
+ { "one", 500, NULL },
+ { "emacron", 444, NULL },
+ { "Odieresis", 778, NULL },
+ { "ucircumflex", 556, NULL },
+ { "bracketleft", 333, NULL },
+ { "Ugrave", 722, NULL },
+ { "quoteright", 333, NULL },
+ { "Udieresis", 722, NULL },
+ { "perthousand", 1000, NULL },
+ { "Ydieresis", 722, NULL },
+ { "umacron", 556, NULL },
+ { "abreve", 500, NULL },
+ { "Eacute", 667, NULL },
+ { "adieresis", 500, NULL },
+ { "egrave", 444, NULL },
+ { "edieresis", 444, NULL },
+ { "idieresis", 278, NULL },
+ { "Eth", 722, NULL },
+ { "ae", 722, NULL },
+ { "asterisk", 500, NULL },
+ { "odieresis", 500, NULL },
+ { "Uacute", 722, NULL },
+ { "ugrave", 556, NULL },
+ { "nine", 500, NULL },
+ { "five", 500, NULL },
+ { "udieresis", 556, NULL },
+ { "Zcaron", 667, NULL },
+ { "Scommaaccent", 556, NULL },
+ { "threequarters", 750, NULL },
+ { "guillemotright", 500, NULL },
+ { "Ccedilla", 722, NULL },
+ { "ydieresis", 500, NULL },
+ { "tilde", 333, NULL },
+ { "at", 930, NULL },
+ { "eacute", 444, NULL },
+ { "underscore", 500, NULL },
+ { "Euro", 500, NULL },
+ { "Dcroat", 722, NULL },
+ { "multiply", 570, NULL },
+ { "zero", 500, NULL },
+ { "eth", 500, NULL },
+ { "Scedilla", 556, NULL },
+ { "Ograve", 778, NULL },
+ { "Racute", 722, NULL },
+ { "partialdiff", 494, NULL },
+ { "uacute", 556, NULL },
+ { "braceleft", 394, NULL },
+ { "Thorn", 611, NULL },
+ { "zcaron", 444, NULL },
+ { "scommaaccent", 389, NULL },
+ { "ccedilla", 444, NULL },
+ { "Dcaron", 722, NULL },
+ { "dcroat", 556, NULL },
+ { "Ocircumflex", 778, NULL },
+ { "Oacute", 778, NULL },
+ { "scedilla", 389, NULL },
+ { "ogonek", 333, NULL },
+ { "ograve", 500, NULL },
+ { "racute", 444, NULL },
+ { "Tcaron", 667, NULL },
+ { "Eogonek", 667, NULL },
+ { "thorn", 556, NULL },
+ { "degree", 400, NULL },
+ { "registered", 747, NULL },
+ { "radical", 549, NULL },
+ { "Aring", 722, NULL },
+ { "percent", 1000, NULL },
+ { "six", 500, NULL },
+ { "paragraph", 540, NULL },
+ { "dcaron", 672, NULL },
+ { "Uogonek", 722, NULL },
+ { "two", 500, NULL },
+ { "summation", 600, NULL },
+ { "Igrave", 389, NULL },
+ { "Lacute", 667, NULL },
+ { "ocircumflex", 500, NULL },
+ { "oacute", 500, NULL },
+ { "Uring", 722, NULL },
+ { "Lcommaaccent", 667, NULL },
+ { "tcaron", 416, NULL },
+ { "eogonek", 444, NULL },
+ { "Delta", 612, NULL },
+ { "Ohungarumlaut", 778, NULL },
+ { "asciicircum", 581, NULL },
+ { "aring", 500, NULL },
+ { "grave", 333, NULL },
+ { "uogonek", 556, NULL },
+ { "bracketright", 333, NULL },
+ { "Iacute", 389, NULL },
+ { "ampersand", 833, NULL },
+ { "igrave", 278, NULL },
+ { "lacute", 278, NULL },
+ { "Ncaron", 722, NULL },
+ { "plus", 570, NULL },
+ { "uring", 556, NULL },
+ { "quotesinglbase", 333, NULL },
+ { "lcommaaccent", 278, NULL },
+ { "Yacute", 722, NULL },
+ { "ohungarumlaut", 500, NULL },
+ { "threesuperior", 300, NULL },
+ { "acute", 333, NULL },
+ { "section", 500, NULL },
+ { "dieresis", 333, NULL },
+ { "iacute", 278, NULL },
+ { "quotedblbase", 500, NULL },
+ { "ncaron", 556, NULL },
+ { "florin", 500, NULL },
+ { "yacute", 500, NULL },
+ { "Rcommaaccent", 722, NULL },
+ { "fi", 556, NULL },
+ { "fl", 556, NULL },
+ { "Acircumflex", 722, NULL },
+ { "Cacute", 722, NULL },
+ { "Icircumflex", 389, NULL },
+ { "guillemotleft", 500, NULL },
+ { "germandbls", 556, NULL },
+ { "Amacron", 722, NULL },
+ { "seven", 500, NULL },
+ { "Sacute", 556, NULL },
+ { "ordmasculine", 330, NULL },
+ { "dotlessi", 278, NULL },
+ { "sterling", 500, NULL },
+ { "notequal", 549, NULL },
+ { "Imacron", 389, NULL },
+ { "rcommaaccent", 444, NULL },
+ { "Zdotaccent", 667, NULL },
+ { "acircumflex", 500, NULL },
+ { "cacute", 444, NULL },
+ { "Ecaron", 667, NULL },
+ { "icircumflex", 278, NULL },
+ { "braceright", 394, NULL },
+ { "quotedblright", 500, NULL },
+ { "amacron", 500, NULL },
+ { "sacute", 389, NULL },
+ { "imacron", 278, NULL },
+ { "cent", 500, NULL },
+ { "currency", 500, NULL },
+ { "logicalnot", 570, NULL },
+ { "zdotaccent", 444, NULL },
+ { "Atilde", 722, NULL },
+ { "breve", 333, NULL },
+ { "bar", 220, NULL },
+ { "fraction", 167, NULL },
+ { "less", 570, NULL },
+ { "ecaron", 444, NULL },
+ { "guilsinglleft", 333, NULL },
+ { "exclam", 333, NULL },
+ { "period", 250, NULL },
+ { "Rcaron", 722, NULL },
+ { "Kcommaaccent", 778, NULL },
+ { "greater", 570, NULL },
+ { "atilde", 500, NULL },
+ { "brokenbar", 220, NULL },
+ { "quoteleft", 333, NULL },
+ { "Edotaccent", 667, NULL },
+ { "onesuperior", 300, NULL }
+};
+
+static BuiltinFontWidth timesBoldItalicWidthsTab[] = {
+ { "Ntilde", 722, NULL },
+ { "rcaron", 389, NULL },
+ { "kcommaaccent", 500, NULL },
+ { "Ncommaaccent", 722, NULL },
+ { "Zacute", 611, NULL },
+ { "comma", 250, NULL },
+ { "cedilla", 333, NULL },
+ { "plusminus", 570, NULL },
+ { "circumflex", 333, NULL },
+ { "dotaccent", 333, NULL },
+ { "edotaccent", 444, NULL },
+ { "asciitilde", 570, NULL },
+ { "colon", 333, NULL },
+ { "onehalf", 750, NULL },
+ { "dollar", 500, NULL },
+ { "Lcaron", 611, NULL },
+ { "ntilde", 556, NULL },
+ { "Aogonek", 667, NULL },
+ { "ncommaaccent", 556, NULL },
+ { "minus", 606, NULL },
+ { "Iogonek", 389, NULL },
+ { "zacute", 389, NULL },
+ { "yen", 500, NULL },
+ { "space", 250, NULL },
+ { "Omacron", 722, NULL },
+ { "questiondown", 500, NULL },
+ { "emdash", 1000, NULL },
+ { "Agrave", 667, NULL },
+ { "three", 500, NULL },
+ { "numbersign", 500, NULL },
+ { "lcaron", 382, NULL },
+ { "A", 667, NULL },
+ { "B", 667, NULL },
+ { "C", 667, NULL },
+ { "aogonek", 500, NULL },
+ { "D", 722, NULL },
+ { "E", 667, NULL },
+ { "onequarter", 750, NULL },
+ { "F", 667, NULL },
+ { "G", 722, NULL },
+ { "H", 778, NULL },
+ { "I", 389, NULL },
+ { "J", 500, NULL },
+ { "K", 667, NULL },
+ { "iogonek", 278, NULL },
+ { "backslash", 278, NULL },
+ { "L", 611, NULL },
+ { "periodcentered", 250, NULL },
+ { "M", 889, NULL },
+ { "N", 722, NULL },
+ { "omacron", 500, NULL },
+ { "Tcommaaccent", 611, NULL },
+ { "O", 722, NULL },
+ { "P", 611, NULL },
+ { "Q", 722, NULL },
+ { "Uhungarumlaut", 722, NULL },
+ { "R", 667, NULL },
+ { "Aacute", 667, NULL },
+ { "caron", 333, NULL },
+ { "S", 556, NULL },
+ { "T", 611, NULL },
+ { "U", 722, NULL },
+ { "agrave", 500, NULL },
+ { "V", 667, NULL },
+ { "W", 889, NULL },
+ { "X", 667, NULL },
+ { "question", 500, NULL },
+ { "equal", 570, NULL },
+ { "Y", 611, NULL },
+ { "Z", 611, NULL },
+ { "four", 500, NULL },
+ { "a", 500, NULL },
+ { "Gcommaaccent", 722, NULL },
+ { "b", 500, NULL },
+ { "c", 444, NULL },
+ { "d", 500, NULL },
+ { "e", 444, NULL },
+ { "f", 333, NULL },
+ { "g", 500, NULL },
+ { "bullet", 350, NULL },
+ { "h", 556, NULL },
+ { "i", 278, NULL },
+ { "Oslash", 722, NULL },
+ { "dagger", 500, NULL },
+ { "j", 278, NULL },
+ { "k", 500, NULL },
+ { "l", 278, NULL },
+ { "m", 778, NULL },
+ { "n", 556, NULL },
+ { "tcommaaccent", 278, NULL },
+ { "o", 500, NULL },
+ { "ordfeminine", 266, NULL },
+ { "ring", 333, NULL },
+ { "p", 500, NULL },
+ { "q", 500, NULL },
+ { "uhungarumlaut", 556, NULL },
+ { "r", 389, NULL },
+ { "twosuperior", 300, NULL },
+ { "aacute", 500, NULL },
+ { "s", 389, NULL },
+ { "OE", 944, NULL },
+ { "t", 278, NULL },
+ { "divide", 570, NULL },
+ { "u", 556, NULL },
+ { "Ccaron", 667, NULL },
+ { "v", 444, NULL },
+ { "w", 667, NULL },
+ { "x", 500, NULL },
+ { "y", 444, NULL },
+ { "z", 389, NULL },
+ { "Gbreve", 722, NULL },
+ { "commaaccent", 250, NULL },
+ { "hungarumlaut", 333, NULL },
+ { "Idotaccent", 389, NULL },
+ { "Nacute", 722, NULL },
+ { "quotedbl", 555, NULL },
+ { "gcommaaccent", 500, NULL },
+ { "mu", 576, NULL },
+ { "greaterequal", 549, NULL },
+ { "Scaron", 556, NULL },
+ { "Lslash", 611, NULL },
+ { "semicolon", 333, NULL },
+ { "oslash", 500, NULL },
+ { "lessequal", 549, NULL },
+ { "lozenge", 494, NULL },
+ { "parenright", 333, NULL },
+ { "ccaron", 444, NULL },
+ { "Ecircumflex", 667, NULL },
+ { "gbreve", 500, NULL },
+ { "trademark", 1000, NULL },
+ { "daggerdbl", 500, NULL },
+ { "nacute", 556, NULL },
+ { "macron", 333, NULL },
+ { "Otilde", 722, NULL },
+ { "Emacron", 667, NULL },
+ { "ellipsis", 1000, NULL },
+ { "scaron", 389, NULL },
+ { "AE", 944, NULL },
+ { "Ucircumflex", 722, NULL },
+ { "lslash", 278, NULL },
+ { "quotedblleft", 500, NULL },
+ { "guilsinglright", 333, NULL },
+ { "hyphen", 333, NULL },
+ { "quotesingle", 278, NULL },
+ { "eight", 500, NULL },
+ { "exclamdown", 389, NULL },
+ { "endash", 500, NULL },
+ { "oe", 722, NULL },
+ { "Abreve", 667, NULL },
+ { "Umacron", 722, NULL },
+ { "ecircumflex", 444, NULL },
+ { "Adieresis", 667, NULL },
+ { "copyright", 747, NULL },
+ { "Egrave", 667, NULL },
+ { "slash", 278, NULL },
+ { "Edieresis", 667, NULL },
+ { "otilde", 500, NULL },
+ { "Idieresis", 389, NULL },
+ { "parenleft", 333, NULL },
+ { "one", 500, NULL },
+ { "emacron", 444, NULL },
+ { "Odieresis", 722, NULL },
+ { "ucircumflex", 556, NULL },
+ { "bracketleft", 333, NULL },
+ { "Ugrave", 722, NULL },
+ { "quoteright", 333, NULL },
+ { "Udieresis", 722, NULL },
+ { "perthousand", 1000, NULL },
+ { "Ydieresis", 611, NULL },
+ { "umacron", 556, NULL },
+ { "abreve", 500, NULL },
+ { "Eacute", 667, NULL },
+ { "adieresis", 500, NULL },
+ { "egrave", 444, NULL },
+ { "edieresis", 444, NULL },
+ { "idieresis", 278, NULL },
+ { "Eth", 722, NULL },
+ { "ae", 722, NULL },
+ { "asterisk", 500, NULL },
+ { "odieresis", 500, NULL },
+ { "Uacute", 722, NULL },
+ { "ugrave", 556, NULL },
+ { "nine", 500, NULL },
+ { "five", 500, NULL },
+ { "udieresis", 556, NULL },
+ { "Zcaron", 611, NULL },
+ { "Scommaaccent", 556, NULL },
+ { "threequarters", 750, NULL },
+ { "guillemotright", 500, NULL },
+ { "Ccedilla", 667, NULL },
+ { "ydieresis", 444, NULL },
+ { "tilde", 333, NULL },
+ { "at", 832, NULL },
+ { "eacute", 444, NULL },
+ { "underscore", 500, NULL },
+ { "Euro", 500, NULL },
+ { "Dcroat", 722, NULL },
+ { "multiply", 570, NULL },
+ { "zero", 500, NULL },
+ { "eth", 500, NULL },
+ { "Scedilla", 556, NULL },
+ { "Ograve", 722, NULL },
+ { "Racute", 667, NULL },
+ { "partialdiff", 494, NULL },
+ { "uacute", 556, NULL },
+ { "braceleft", 348, NULL },
+ { "Thorn", 611, NULL },
+ { "zcaron", 389, NULL },
+ { "scommaaccent", 389, NULL },
+ { "ccedilla", 444, NULL },
+ { "Dcaron", 722, NULL },
+ { "dcroat", 500, NULL },
+ { "Ocircumflex", 722, NULL },
+ { "Oacute", 722, NULL },
+ { "scedilla", 389, NULL },
+ { "ogonek", 333, NULL },
+ { "ograve", 500, NULL },
+ { "racute", 389, NULL },
+ { "Tcaron", 611, NULL },
+ { "Eogonek", 667, NULL },
+ { "thorn", 500, NULL },
+ { "degree", 400, NULL },
+ { "registered", 747, NULL },
+ { "radical", 549, NULL },
+ { "Aring", 667, NULL },
+ { "percent", 833, NULL },
+ { "six", 500, NULL },
+ { "paragraph", 500, NULL },
+ { "dcaron", 608, NULL },
+ { "Uogonek", 722, NULL },
+ { "two", 500, NULL },
+ { "summation", 600, NULL },
+ { "Igrave", 389, NULL },
+ { "Lacute", 611, NULL },
+ { "ocircumflex", 500, NULL },
+ { "oacute", 500, NULL },
+ { "Uring", 722, NULL },
+ { "Lcommaaccent", 611, NULL },
+ { "tcaron", 366, NULL },
+ { "eogonek", 444, NULL },
+ { "Delta", 612, NULL },
+ { "Ohungarumlaut", 722, NULL },
+ { "asciicircum", 570, NULL },
+ { "aring", 500, NULL },
+ { "grave", 333, NULL },
+ { "uogonek", 556, NULL },
+ { "bracketright", 333, NULL },
+ { "Iacute", 389, NULL },
+ { "ampersand", 778, NULL },
+ { "igrave", 278, NULL },
+ { "lacute", 278, NULL },
+ { "Ncaron", 722, NULL },
+ { "plus", 570, NULL },
+ { "uring", 556, NULL },
+ { "quotesinglbase", 333, NULL },
+ { "lcommaaccent", 278, NULL },
+ { "Yacute", 611, NULL },
+ { "ohungarumlaut", 500, NULL },
+ { "threesuperior", 300, NULL },
+ { "acute", 333, NULL },
+ { "section", 500, NULL },
+ { "dieresis", 333, NULL },
+ { "iacute", 278, NULL },
+ { "quotedblbase", 500, NULL },
+ { "ncaron", 556, NULL },
+ { "florin", 500, NULL },
+ { "yacute", 444, NULL },
+ { "Rcommaaccent", 667, NULL },
+ { "fi", 556, NULL },
+ { "fl", 556, NULL },
+ { "Acircumflex", 667, NULL },
+ { "Cacute", 667, NULL },
+ { "Icircumflex", 389, NULL },
+ { "guillemotleft", 500, NULL },
+ { "germandbls", 500, NULL },
+ { "Amacron", 667, NULL },
+ { "seven", 500, NULL },
+ { "Sacute", 556, NULL },
+ { "ordmasculine", 300, NULL },
+ { "dotlessi", 278, NULL },
+ { "sterling", 500, NULL },
+ { "notequal", 549, NULL },
+ { "Imacron", 389, NULL },
+ { "rcommaaccent", 389, NULL },
+ { "Zdotaccent", 611, NULL },
+ { "acircumflex", 500, NULL },
+ { "cacute", 444, NULL },
+ { "Ecaron", 667, NULL },
+ { "icircumflex", 278, NULL },
+ { "braceright", 348, NULL },
+ { "quotedblright", 500, NULL },
+ { "amacron", 500, NULL },
+ { "sacute", 389, NULL },
+ { "imacron", 278, NULL },
+ { "cent", 500, NULL },
+ { "currency", 500, NULL },
+ { "logicalnot", 606, NULL },
+ { "zdotaccent", 389, NULL },
+ { "Atilde", 667, NULL },
+ { "breve", 333, NULL },
+ { "bar", 220, NULL },
+ { "fraction", 167, NULL },
+ { "less", 570, NULL },
+ { "ecaron", 444, NULL },
+ { "guilsinglleft", 333, NULL },
+ { "exclam", 389, NULL },
+ { "period", 250, NULL },
+ { "Rcaron", 667, NULL },
+ { "Kcommaaccent", 667, NULL },
+ { "greater", 570, NULL },
+ { "atilde", 500, NULL },
+ { "brokenbar", 220, NULL },
+ { "quoteleft", 333, NULL },
+ { "Edotaccent", 667, NULL },
+ { "onesuperior", 300, NULL }
+};
+
+static BuiltinFontWidth timesItalicWidthsTab[] = {
+ { "Ntilde", 667, NULL },
+ { "rcaron", 389, NULL },
+ { "kcommaaccent", 444, NULL },
+ { "Ncommaaccent", 667, NULL },
+ { "Zacute", 556, NULL },
+ { "comma", 250, NULL },
+ { "cedilla", 333, NULL },
+ { "plusminus", 675, NULL },
+ { "circumflex", 333, NULL },
+ { "dotaccent", 333, NULL },
+ { "edotaccent", 444, NULL },
+ { "asciitilde", 541, NULL },
+ { "colon", 333, NULL },
+ { "onehalf", 750, NULL },
+ { "dollar", 500, NULL },
+ { "Lcaron", 611, NULL },
+ { "ntilde", 500, NULL },
+ { "Aogonek", 611, NULL },
+ { "ncommaaccent", 500, NULL },
+ { "minus", 675, NULL },
+ { "Iogonek", 333, NULL },
+ { "zacute", 389, NULL },
+ { "yen", 500, NULL },
+ { "space", 250, NULL },
+ { "Omacron", 722, NULL },
+ { "questiondown", 500, NULL },
+ { "emdash", 889, NULL },
+ { "Agrave", 611, NULL },
+ { "three", 500, NULL },
+ { "numbersign", 500, NULL },
+ { "lcaron", 300, NULL },
+ { "A", 611, NULL },
+ { "B", 611, NULL },
+ { "C", 667, NULL },
+ { "aogonek", 500, NULL },
+ { "D", 722, NULL },
+ { "E", 611, NULL },
+ { "onequarter", 750, NULL },
+ { "F", 611, NULL },
+ { "G", 722, NULL },
+ { "H", 722, NULL },
+ { "I", 333, NULL },
+ { "J", 444, NULL },
+ { "K", 667, NULL },
+ { "iogonek", 278, NULL },
+ { "backslash", 278, NULL },
+ { "L", 556, NULL },
+ { "periodcentered", 250, NULL },
+ { "M", 833, NULL },
+ { "N", 667, NULL },
+ { "omacron", 500, NULL },
+ { "Tcommaaccent", 556, NULL },
+ { "O", 722, NULL },
+ { "P", 611, NULL },
+ { "Q", 722, NULL },
+ { "Uhungarumlaut", 722, NULL },
+ { "R", 611, NULL },
+ { "Aacute", 611, NULL },
+ { "caron", 333, NULL },
+ { "S", 500, NULL },
+ { "T", 556, NULL },
+ { "U", 722, NULL },
+ { "agrave", 500, NULL },
+ { "V", 611, NULL },
+ { "W", 833, NULL },
+ { "X", 611, NULL },
+ { "question", 500, NULL },
+ { "equal", 675, NULL },
+ { "Y", 556, NULL },
+ { "Z", 556, NULL },
+ { "four", 500, NULL },
+ { "a", 500, NULL },
+ { "Gcommaaccent", 722, NULL },
+ { "b", 500, NULL },
+ { "c", 444, NULL },
+ { "d", 500, NULL },
+ { "e", 444, NULL },
+ { "f", 278, NULL },
+ { "g", 500, NULL },
+ { "bullet", 350, NULL },
+ { "h", 500, NULL },
+ { "i", 278, NULL },
+ { "Oslash", 722, NULL },
+ { "dagger", 500, NULL },
+ { "j", 278, NULL },
+ { "k", 444, NULL },
+ { "l", 278, NULL },
+ { "m", 722, NULL },
+ { "n", 500, NULL },
+ { "tcommaaccent", 278, NULL },
+ { "o", 500, NULL },
+ { "ordfeminine", 276, NULL },
+ { "ring", 333, NULL },
+ { "p", 500, NULL },
+ { "q", 500, NULL },
+ { "uhungarumlaut", 500, NULL },
+ { "r", 389, NULL },
+ { "twosuperior", 300, NULL },
+ { "aacute", 500, NULL },
+ { "s", 389, NULL },
+ { "OE", 944, NULL },
+ { "t", 278, NULL },
+ { "divide", 675, NULL },
+ { "u", 500, NULL },
+ { "Ccaron", 667, NULL },
+ { "v", 444, NULL },
+ { "w", 667, NULL },
+ { "x", 444, NULL },
+ { "y", 444, NULL },
+ { "z", 389, NULL },
+ { "Gbreve", 722, NULL },
+ { "commaaccent", 250, NULL },
+ { "hungarumlaut", 333, NULL },
+ { "Idotaccent", 333, NULL },
+ { "Nacute", 667, NULL },
+ { "quotedbl", 420, NULL },
+ { "gcommaaccent", 500, NULL },
+ { "mu", 500, NULL },
+ { "greaterequal", 549, NULL },
+ { "Scaron", 500, NULL },
+ { "Lslash", 556, NULL },
+ { "semicolon", 333, NULL },
+ { "oslash", 500, NULL },
+ { "lessequal", 549, NULL },
+ { "lozenge", 471, NULL },
+ { "parenright", 333, NULL },
+ { "ccaron", 444, NULL },
+ { "Ecircumflex", 611, NULL },
+ { "gbreve", 500, NULL },
+ { "trademark", 980, NULL },
+ { "daggerdbl", 500, NULL },
+ { "nacute", 500, NULL },
+ { "macron", 333, NULL },
+ { "Otilde", 722, NULL },
+ { "Emacron", 611, NULL },
+ { "ellipsis", 889, NULL },
+ { "scaron", 389, NULL },
+ { "AE", 889, NULL },
+ { "Ucircumflex", 722, NULL },
+ { "lslash", 278, NULL },
+ { "quotedblleft", 556, NULL },
+ { "guilsinglright", 333, NULL },
+ { "hyphen", 333, NULL },
+ { "quotesingle", 214, NULL },
+ { "eight", 500, NULL },
+ { "exclamdown", 389, NULL },
+ { "endash", 500, NULL },
+ { "oe", 667, NULL },
+ { "Abreve", 611, NULL },
+ { "Umacron", 722, NULL },
+ { "ecircumflex", 444, NULL },
+ { "Adieresis", 611, NULL },
+ { "copyright", 760, NULL },
+ { "Egrave", 611, NULL },
+ { "slash", 278, NULL },
+ { "Edieresis", 611, NULL },
+ { "otilde", 500, NULL },
+ { "Idieresis", 333, NULL },
+ { "parenleft", 333, NULL },
+ { "one", 500, NULL },
+ { "emacron", 444, NULL },
+ { "Odieresis", 722, NULL },
+ { "ucircumflex", 500, NULL },
+ { "bracketleft", 389, NULL },
+ { "Ugrave", 722, NULL },
+ { "quoteright", 333, NULL },
+ { "Udieresis", 722, NULL },
+ { "perthousand", 1000, NULL },
+ { "Ydieresis", 556, NULL },
+ { "umacron", 500, NULL },
+ { "abreve", 500, NULL },
+ { "Eacute", 611, NULL },
+ { "adieresis", 500, NULL },
+ { "egrave", 444, NULL },
+ { "edieresis", 444, NULL },
+ { "idieresis", 278, NULL },
+ { "Eth", 722, NULL },
+ { "ae", 667, NULL },
+ { "asterisk", 500, NULL },
+ { "odieresis", 500, NULL },
+ { "Uacute", 722, NULL },
+ { "ugrave", 500, NULL },
+ { "nine", 500, NULL },
+ { "five", 500, NULL },
+ { "udieresis", 500, NULL },
+ { "Zcaron", 556, NULL },
+ { "Scommaaccent", 500, NULL },
+ { "threequarters", 750, NULL },
+ { "guillemotright", 500, NULL },
+ { "Ccedilla", 667, NULL },
+ { "ydieresis", 444, NULL },
+ { "tilde", 333, NULL },
+ { "at", 920, NULL },
+ { "eacute", 444, NULL },
+ { "underscore", 500, NULL },
+ { "Euro", 500, NULL },
+ { "Dcroat", 722, NULL },
+ { "multiply", 675, NULL },
+ { "zero", 500, NULL },
+ { "eth", 500, NULL },
+ { "Scedilla", 500, NULL },
+ { "Ograve", 722, NULL },
+ { "Racute", 611, NULL },
+ { "partialdiff", 476, NULL },
+ { "uacute", 500, NULL },
+ { "braceleft", 400, NULL },
+ { "Thorn", 611, NULL },
+ { "zcaron", 389, NULL },
+ { "scommaaccent", 389, NULL },
+ { "ccedilla", 444, NULL },
+ { "Dcaron", 722, NULL },
+ { "dcroat", 500, NULL },
+ { "Ocircumflex", 722, NULL },
+ { "Oacute", 722, NULL },
+ { "scedilla", 389, NULL },
+ { "ogonek", 333, NULL },
+ { "ograve", 500, NULL },
+ { "racute", 389, NULL },
+ { "Tcaron", 556, NULL },
+ { "Eogonek", 611, NULL },
+ { "thorn", 500, NULL },
+ { "degree", 400, NULL },
+ { "registered", 760, NULL },
+ { "radical", 453, NULL },
+ { "Aring", 611, NULL },
+ { "percent", 833, NULL },
+ { "six", 500, NULL },
+ { "paragraph", 523, NULL },
+ { "dcaron", 544, NULL },
+ { "Uogonek", 722, NULL },
+ { "two", 500, NULL },
+ { "summation", 600, NULL },
+ { "Igrave", 333, NULL },
+ { "Lacute", 556, NULL },
+ { "ocircumflex", 500, NULL },
+ { "oacute", 500, NULL },
+ { "Uring", 722, NULL },
+ { "Lcommaaccent", 556, NULL },
+ { "tcaron", 300, NULL },
+ { "eogonek", 444, NULL },
+ { "Delta", 612, NULL },
+ { "Ohungarumlaut", 722, NULL },
+ { "asciicircum", 422, NULL },
+ { "aring", 500, NULL },
+ { "grave", 333, NULL },
+ { "uogonek", 500, NULL },
+ { "bracketright", 389, NULL },
+ { "Iacute", 333, NULL },
+ { "ampersand", 778, NULL },
+ { "igrave", 278, NULL },
+ { "lacute", 278, NULL },
+ { "Ncaron", 667, NULL },
+ { "plus", 675, NULL },
+ { "uring", 500, NULL },
+ { "quotesinglbase", 333, NULL },
+ { "lcommaaccent", 278, NULL },
+ { "Yacute", 556, NULL },
+ { "ohungarumlaut", 500, NULL },
+ { "threesuperior", 300, NULL },
+ { "acute", 333, NULL },
+ { "section", 500, NULL },
+ { "dieresis", 333, NULL },
+ { "iacute", 278, NULL },
+ { "quotedblbase", 556, NULL },
+ { "ncaron", 500, NULL },
+ { "florin", 500, NULL },
+ { "yacute", 444, NULL },
+ { "Rcommaaccent", 611, NULL },
+ { "fi", 500, NULL },
+ { "fl", 500, NULL },
+ { "Acircumflex", 611, NULL },
+ { "Cacute", 667, NULL },
+ { "Icircumflex", 333, NULL },
+ { "guillemotleft", 500, NULL },
+ { "germandbls", 500, NULL },
+ { "Amacron", 611, NULL },
+ { "seven", 500, NULL },
+ { "Sacute", 500, NULL },
+ { "ordmasculine", 310, NULL },
+ { "dotlessi", 278, NULL },
+ { "sterling", 500, NULL },
+ { "notequal", 549, NULL },
+ { "Imacron", 333, NULL },
+ { "rcommaaccent", 389, NULL },
+ { "Zdotaccent", 556, NULL },
+ { "acircumflex", 500, NULL },
+ { "cacute", 444, NULL },
+ { "Ecaron", 611, NULL },
+ { "icircumflex", 278, NULL },
+ { "braceright", 400, NULL },
+ { "quotedblright", 556, NULL },
+ { "amacron", 500, NULL },
+ { "sacute", 389, NULL },
+ { "imacron", 278, NULL },
+ { "cent", 500, NULL },
+ { "currency", 500, NULL },
+ { "logicalnot", 675, NULL },
+ { "zdotaccent", 389, NULL },
+ { "Atilde", 611, NULL },
+ { "breve", 333, NULL },
+ { "bar", 275, NULL },
+ { "fraction", 167, NULL },
+ { "less", 675, NULL },
+ { "ecaron", 444, NULL },
+ { "guilsinglleft", 333, NULL },
+ { "exclam", 333, NULL },
+ { "period", 250, NULL },
+ { "Rcaron", 611, NULL },
+ { "Kcommaaccent", 667, NULL },
+ { "greater", 675, NULL },
+ { "atilde", 500, NULL },
+ { "brokenbar", 275, NULL },
+ { "quoteleft", 333, NULL },
+ { "Edotaccent", 611, NULL },
+ { "onesuperior", 300, NULL }
+};
+
+static BuiltinFontWidth timesRomanWidthsTab[] = {
+ { "Ntilde", 722, NULL },
+ { "rcaron", 333, NULL },
+ { "kcommaaccent", 500, NULL },
+ { "Ncommaaccent", 722, NULL },
+ { "Zacute", 611, NULL },
+ { "comma", 250, NULL },
+ { "cedilla", 333, NULL },
+ { "plusminus", 564, NULL },
+ { "circumflex", 333, NULL },
+ { "dotaccent", 333, NULL },
+ { "edotaccent", 444, NULL },
+ { "asciitilde", 541, NULL },
+ { "colon", 278, NULL },
+ { "onehalf", 750, NULL },
+ { "dollar", 500, NULL },
+ { "Lcaron", 611, NULL },
+ { "ntilde", 500, NULL },
+ { "Aogonek", 722, NULL },
+ { "ncommaaccent", 500, NULL },
+ { "minus", 564, NULL },
+ { "Iogonek", 333, NULL },
+ { "zacute", 444, NULL },
+ { "yen", 500, NULL },
+ { "space", 250, NULL },
+ { "Omacron", 722, NULL },
+ { "questiondown", 444, NULL },
+ { "emdash", 1000, NULL },
+ { "Agrave", 722, NULL },
+ { "three", 500, NULL },
+ { "numbersign", 500, NULL },
+ { "lcaron", 344, NULL },
+ { "A", 722, NULL },
+ { "B", 667, NULL },
+ { "C", 667, NULL },
+ { "aogonek", 444, NULL },
+ { "D", 722, NULL },
+ { "E", 611, NULL },
+ { "onequarter", 750, NULL },
+ { "F", 556, NULL },
+ { "G", 722, NULL },
+ { "H", 722, NULL },
+ { "I", 333, NULL },
+ { "J", 389, NULL },
+ { "K", 722, NULL },
+ { "iogonek", 278, NULL },
+ { "backslash", 278, NULL },
+ { "L", 611, NULL },
+ { "periodcentered", 250, NULL },
+ { "M", 889, NULL },
+ { "N", 722, NULL },
+ { "omacron", 500, NULL },
+ { "Tcommaaccent", 611, NULL },
+ { "O", 722, NULL },
+ { "P", 556, NULL },
+ { "Q", 722, NULL },
+ { "Uhungarumlaut", 722, NULL },
+ { "R", 667, NULL },
+ { "Aacute", 722, NULL },
+ { "caron", 333, NULL },
+ { "S", 556, NULL },
+ { "T", 611, NULL },
+ { "U", 722, NULL },
+ { "agrave", 444, NULL },
+ { "V", 722, NULL },
+ { "W", 944, NULL },
+ { "X", 722, NULL },
+ { "question", 444, NULL },
+ { "equal", 564, NULL },
+ { "Y", 722, NULL },
+ { "Z", 611, NULL },
+ { "four", 500, NULL },
+ { "a", 444, NULL },
+ { "Gcommaaccent", 722, NULL },
+ { "b", 500, NULL },
+ { "c", 444, NULL },
+ { "d", 500, NULL },
+ { "e", 444, NULL },
+ { "f", 333, NULL },
+ { "g", 500, NULL },
+ { "bullet", 350, NULL },
+ { "h", 500, NULL },
+ { "i", 278, NULL },
+ { "Oslash", 722, NULL },
+ { "dagger", 500, NULL },
+ { "j", 278, NULL },
+ { "k", 500, NULL },
+ { "l", 278, NULL },
+ { "m", 778, NULL },
+ { "n", 500, NULL },
+ { "tcommaaccent", 278, NULL },
+ { "o", 500, NULL },
+ { "ordfeminine", 276, NULL },
+ { "ring", 333, NULL },
+ { "p", 500, NULL },
+ { "q", 500, NULL },
+ { "uhungarumlaut", 500, NULL },
+ { "r", 333, NULL },
+ { "twosuperior", 300, NULL },
+ { "aacute", 444, NULL },
+ { "s", 389, NULL },
+ { "OE", 889, NULL },
+ { "t", 278, NULL },
+ { "divide", 564, NULL },
+ { "u", 500, NULL },
+ { "Ccaron", 667, NULL },
+ { "v", 500, NULL },
+ { "w", 722, NULL },
+ { "x", 500, NULL },
+ { "y", 500, NULL },
+ { "z", 444, NULL },
+ { "Gbreve", 722, NULL },
+ { "commaaccent", 250, NULL },
+ { "hungarumlaut", 333, NULL },
+ { "Idotaccent", 333, NULL },
+ { "Nacute", 722, NULL },
+ { "quotedbl", 408, NULL },
+ { "gcommaaccent", 500, NULL },
+ { "mu", 500, NULL },
+ { "greaterequal", 549, NULL },
+ { "Scaron", 556, NULL },
+ { "Lslash", 611, NULL },
+ { "semicolon", 278, NULL },
+ { "oslash", 500, NULL },
+ { "lessequal", 549, NULL },
+ { "lozenge", 471, NULL },
+ { "parenright", 333, NULL },
+ { "ccaron", 444, NULL },
+ { "Ecircumflex", 611, NULL },
+ { "gbreve", 500, NULL },
+ { "trademark", 980, NULL },
+ { "daggerdbl", 500, NULL },
+ { "nacute", 500, NULL },
+ { "macron", 333, NULL },
+ { "Otilde", 722, NULL },
+ { "Emacron", 611, NULL },
+ { "ellipsis", 1000, NULL },
+ { "scaron", 389, NULL },
+ { "AE", 889, NULL },
+ { "Ucircumflex", 722, NULL },
+ { "lslash", 278, NULL },
+ { "quotedblleft", 444, NULL },
+ { "guilsinglright", 333, NULL },
+ { "hyphen", 333, NULL },
+ { "quotesingle", 180, NULL },
+ { "eight", 500, NULL },
+ { "exclamdown", 333, NULL },
+ { "endash", 500, NULL },
+ { "oe", 722, NULL },
+ { "Abreve", 722, NULL },
+ { "Umacron", 722, NULL },
+ { "ecircumflex", 444, NULL },
+ { "Adieresis", 722, NULL },
+ { "copyright", 760, NULL },
+ { "Egrave", 611, NULL },
+ { "slash", 278, NULL },
+ { "Edieresis", 611, NULL },
+ { "otilde", 500, NULL },
+ { "Idieresis", 333, NULL },
+ { "parenleft", 333, NULL },
+ { "one", 500, NULL },
+ { "emacron", 444, NULL },
+ { "Odieresis", 722, NULL },
+ { "ucircumflex", 500, NULL },
+ { "bracketleft", 333, NULL },
+ { "Ugrave", 722, NULL },
+ { "quoteright", 333, NULL },
+ { "Udieresis", 722, NULL },
+ { "perthousand", 1000, NULL },
+ { "Ydieresis", 722, NULL },
+ { "umacron", 500, NULL },
+ { "abreve", 444, NULL },
+ { "Eacute", 611, NULL },
+ { "adieresis", 444, NULL },
+ { "egrave", 444, NULL },
+ { "edieresis", 444, NULL },
+ { "idieresis", 278, NULL },
+ { "Eth", 722, NULL },
+ { "ae", 667, NULL },
+ { "asterisk", 500, NULL },
+ { "odieresis", 500, NULL },
+ { "Uacute", 722, NULL },
+ { "ugrave", 500, NULL },
+ { "nine", 500, NULL },
+ { "five", 500, NULL },
+ { "udieresis", 500, NULL },
+ { "Zcaron", 611, NULL },
+ { "Scommaaccent", 556, NULL },
+ { "threequarters", 750, NULL },
+ { "guillemotright", 500, NULL },
+ { "Ccedilla", 667, NULL },
+ { "ydieresis", 500, NULL },
+ { "tilde", 333, NULL },
+ { "at", 921, NULL },
+ { "eacute", 444, NULL },
+ { "underscore", 500, NULL },
+ { "Euro", 500, NULL },
+ { "Dcroat", 722, NULL },
+ { "multiply", 564, NULL },
+ { "zero", 500, NULL },
+ { "eth", 500, NULL },
+ { "Scedilla", 556, NULL },
+ { "Ograve", 722, NULL },
+ { "Racute", 667, NULL },
+ { "partialdiff", 476, NULL },
+ { "uacute", 500, NULL },
+ { "braceleft", 480, NULL },
+ { "Thorn", 556, NULL },
+ { "zcaron", 444, NULL },
+ { "scommaaccent", 389, NULL },
+ { "ccedilla", 444, NULL },
+ { "Dcaron", 722, NULL },
+ { "dcroat", 500, NULL },
+ { "Ocircumflex", 722, NULL },
+ { "Oacute", 722, NULL },
+ { "scedilla", 389, NULL },
+ { "ogonek", 333, NULL },
+ { "ograve", 500, NULL },
+ { "racute", 333, NULL },
+ { "Tcaron", 611, NULL },
+ { "Eogonek", 611, NULL },
+ { "thorn", 500, NULL },
+ { "degree", 400, NULL },
+ { "registered", 760, NULL },
+ { "radical", 453, NULL },
+ { "Aring", 722, NULL },
+ { "percent", 833, NULL },
+ { "six", 500, NULL },
+ { "paragraph", 453, NULL },
+ { "dcaron", 588, NULL },
+ { "Uogonek", 722, NULL },
+ { "two", 500, NULL },
+ { "summation", 600, NULL },
+ { "Igrave", 333, NULL },
+ { "Lacute", 611, NULL },
+ { "ocircumflex", 500, NULL },
+ { "oacute", 500, NULL },
+ { "Uring", 722, NULL },
+ { "Lcommaaccent", 611, NULL },
+ { "tcaron", 326, NULL },
+ { "eogonek", 444, NULL },
+ { "Delta", 612, NULL },
+ { "Ohungarumlaut", 722, NULL },
+ { "asciicircum", 469, NULL },
+ { "aring", 444, NULL },
+ { "grave", 333, NULL },
+ { "uogonek", 500, NULL },
+ { "bracketright", 333, NULL },
+ { "Iacute", 333, NULL },
+ { "ampersand", 778, NULL },
+ { "igrave", 278, NULL },
+ { "lacute", 278, NULL },
+ { "Ncaron", 722, NULL },
+ { "plus", 564, NULL },
+ { "uring", 500, NULL },
+ { "quotesinglbase", 333, NULL },
+ { "lcommaaccent", 278, NULL },
+ { "Yacute", 722, NULL },
+ { "ohungarumlaut", 500, NULL },
+ { "threesuperior", 300, NULL },
+ { "acute", 333, NULL },
+ { "section", 500, NULL },
+ { "dieresis", 333, NULL },
+ { "iacute", 278, NULL },
+ { "quotedblbase", 444, NULL },
+ { "ncaron", 500, NULL },
+ { "florin", 500, NULL },
+ { "yacute", 500, NULL },
+ { "Rcommaaccent", 667, NULL },
+ { "fi", 556, NULL },
+ { "fl", 556, NULL },
+ { "Acircumflex", 722, NULL },
+ { "Cacute", 667, NULL },
+ { "Icircumflex", 333, NULL },
+ { "guillemotleft", 500, NULL },
+ { "germandbls", 500, NULL },
+ { "Amacron", 722, NULL },
+ { "seven", 500, NULL },
+ { "Sacute", 556, NULL },
+ { "ordmasculine", 310, NULL },
+ { "dotlessi", 278, NULL },
+ { "sterling", 500, NULL },
+ { "notequal", 549, NULL },
+ { "Imacron", 333, NULL },
+ { "rcommaaccent", 333, NULL },
+ { "Zdotaccent", 611, NULL },
+ { "acircumflex", 444, NULL },
+ { "cacute", 444, NULL },
+ { "Ecaron", 611, NULL },
+ { "icircumflex", 278, NULL },
+ { "braceright", 480, NULL },
+ { "quotedblright", 444, NULL },
+ { "amacron", 444, NULL },
+ { "sacute", 389, NULL },
+ { "imacron", 278, NULL },
+ { "cent", 500, NULL },
+ { "currency", 500, NULL },
+ { "logicalnot", 564, NULL },
+ { "zdotaccent", 444, NULL },
+ { "Atilde", 722, NULL },
+ { "breve", 333, NULL },
+ { "bar", 200, NULL },
+ { "fraction", 167, NULL },
+ { "less", 564, NULL },
+ { "ecaron", 444, NULL },
+ { "guilsinglleft", 333, NULL },
+ { "exclam", 333, NULL },
+ { "period", 250, NULL },
+ { "Rcaron", 667, NULL },
+ { "Kcommaaccent", 722, NULL },
+ { "greater", 564, NULL },
+ { "atilde", 444, NULL },
+ { "brokenbar", 200, NULL },
+ { "quoteleft", 333, NULL },
+ { "Edotaccent", 611, NULL },
+ { "onesuperior", 300, NULL }
+};
+
+static BuiltinFontWidth zapfDingbatsWidthsTab[] = {
+ { "a81", 438, NULL },
+ { "a82", 138, NULL },
+ { "a83", 277, NULL },
+ { "a84", 415, NULL },
+ { "a85", 509, NULL },
+ { "a86", 410, NULL },
+ { "a87", 234, NULL },
+ { "a88", 234, NULL },
+ { "a89", 390, NULL },
+ { "a140", 788, NULL },
+ { "a141", 788, NULL },
+ { "a142", 788, NULL },
+ { "a143", 788, NULL },
+ { "a144", 788, NULL },
+ { "a145", 788, NULL },
+ { "a146", 788, NULL },
+ { "a147", 788, NULL },
+ { "a148", 788, NULL },
+ { "a149", 788, NULL },
+ { "a90", 390, NULL },
+ { "a91", 276, NULL },
+ { "a92", 276, NULL },
+ { "space", 278, NULL },
+ { "a93", 317, NULL },
+ { "a94", 317, NULL },
+ { "a95", 334, NULL },
+ { "a96", 334, NULL },
+ { "a97", 392, NULL },
+ { "a98", 392, NULL },
+ { "a99", 668, NULL },
+ { "a150", 788, NULL },
+ { "a151", 788, NULL },
+ { "a152", 788, NULL },
+ { "a153", 788, NULL },
+ { "a154", 788, NULL },
+ { "a155", 788, NULL },
+ { "a156", 788, NULL },
+ { "a157", 788, NULL },
+ { "a158", 788, NULL },
+ { "a159", 788, NULL },
+ { "a160", 894, NULL },
+ { "a161", 838, NULL },
+ { "a162", 924, NULL },
+ { "a163", 1016, NULL },
+ { "a164", 458, NULL },
+ { "a165", 924, NULL },
+ { "a166", 918, NULL },
+ { "a167", 927, NULL },
+ { "a168", 928, NULL },
+ { "a169", 928, NULL },
+ { "a170", 834, NULL },
+ { "a171", 873, NULL },
+ { "a172", 828, NULL },
+ { "a173", 924, NULL },
+ { "a174", 917, NULL },
+ { "a175", 930, NULL },
+ { "a176", 931, NULL },
+ { "a177", 463, NULL },
+ { "a178", 883, NULL },
+ { "a179", 836, NULL },
+ { "a180", 867, NULL },
+ { "a181", 696, NULL },
+ { "a182", 874, NULL },
+ { "a183", 760, NULL },
+ { "a184", 946, NULL },
+ { "a185", 865, NULL },
+ { "a186", 967, NULL },
+ { "a187", 831, NULL },
+ { "a188", 873, NULL },
+ { "a189", 927, NULL },
+ { "a1", 974, NULL },
+ { "a2", 961, NULL },
+ { "a3", 980, NULL },
+ { "a4", 719, NULL },
+ { "a5", 789, NULL },
+ { "a6", 494, NULL },
+ { "a7", 552, NULL },
+ { "a8", 537, NULL },
+ { "a9", 577, NULL },
+ { "a190", 970, NULL },
+ { "a191", 918, NULL },
+ { "a192", 748, NULL },
+ { "a193", 836, NULL },
+ { "a194", 771, NULL },
+ { "a195", 888, NULL },
+ { "a196", 748, NULL },
+ { "a197", 771, NULL },
+ { "a198", 888, NULL },
+ { "a199", 867, NULL },
+ { "a10", 692, NULL },
+ { "a11", 960, NULL },
+ { "a12", 939, NULL },
+ { "a13", 549, NULL },
+ { "a14", 855, NULL },
+ { "a15", 911, NULL },
+ { "a16", 933, NULL },
+ { "a17", 945, NULL },
+ { "a18", 974, NULL },
+ { "a19", 755, NULL },
+ { "a20", 846, NULL },
+ { "a21", 762, NULL },
+ { "a22", 761, NULL },
+ { "a23", 571, NULL },
+ { "a24", 677, NULL },
+ { "a25", 763, NULL },
+ { "a26", 760, NULL },
+ { "a27", 759, NULL },
+ { "a28", 754, NULL },
+ { "a29", 786, NULL },
+ { "a30", 788, NULL },
+ { "a31", 788, NULL },
+ { "a32", 790, NULL },
+ { "a33", 793, NULL },
+ { "a34", 794, NULL },
+ { "a35", 816, NULL },
+ { "a36", 823, NULL },
+ { "a37", 789, NULL },
+ { "a38", 841, NULL },
+ { "a39", 823, NULL },
+ { "a40", 833, NULL },
+ { "a41", 816, NULL },
+ { "a42", 831, NULL },
+ { "a43", 923, NULL },
+ { "a44", 744, NULL },
+ { "a45", 723, NULL },
+ { "a46", 749, NULL },
+ { "a47", 790, NULL },
+ { "a48", 792, NULL },
+ { "a49", 695, NULL },
+ { "a100", 668, NULL },
+ { "a101", 732, NULL },
+ { "a102", 544, NULL },
+ { "a103", 544, NULL },
+ { "a104", 910, NULL },
+ { "a105", 911, NULL },
+ { "a106", 667, NULL },
+ { "a107", 760, NULL },
+ { "a108", 760, NULL },
+ { "a109", 626, NULL },
+ { "a50", 776, NULL },
+ { "a51", 768, NULL },
+ { "a52", 792, NULL },
+ { "a53", 759, NULL },
+ { "a54", 707, NULL },
+ { "a55", 708, NULL },
+ { "a56", 682, NULL },
+ { "a57", 701, NULL },
+ { "a58", 826, NULL },
+ { "a59", 815, NULL },
+ { "a110", 694, NULL },
+ { "a111", 595, NULL },
+ { "a112", 776, NULL },
+ { "a117", 690, NULL },
+ { "a118", 791, NULL },
+ { "a119", 790, NULL },
+ { "a60", 789, NULL },
+ { "a61", 789, NULL },
+ { "a62", 707, NULL },
+ { "a63", 687, NULL },
+ { "a64", 696, NULL },
+ { "a65", 689, NULL },
+ { "a66", 786, NULL },
+ { "a67", 787, NULL },
+ { "a68", 713, NULL },
+ { "a69", 791, NULL },
+ { "a200", 696, NULL },
+ { "a201", 874, NULL },
+ { "a120", 788, NULL },
+ { "a121", 788, NULL },
+ { "a202", 974, NULL },
+ { "a122", 788, NULL },
+ { "a203", 762, NULL },
+ { "a123", 788, NULL },
+ { "a204", 759, NULL },
+ { "a124", 788, NULL },
+ { "a205", 509, NULL },
+ { "a125", 788, NULL },
+ { "a206", 410, NULL },
+ { "a126", 788, NULL },
+ { "a127", 788, NULL },
+ { "a128", 788, NULL },
+ { "a129", 788, NULL },
+ { "a70", 785, NULL },
+ { "a71", 791, NULL },
+ { "a72", 873, NULL },
+ { "a73", 761, NULL },
+ { "a74", 762, NULL },
+ { "a75", 759, NULL },
+ { "a76", 892, NULL },
+ { "a77", 892, NULL },
+ { "a78", 788, NULL },
+ { "a79", 784, NULL },
+ { "a130", 788, NULL },
+ { "a131", 788, NULL },
+ { "a132", 788, NULL },
+ { "a133", 788, NULL },
+ { "a134", 788, NULL },
+ { "a135", 788, NULL },
+ { "a136", 788, NULL },
+ { "a137", 788, NULL },
+ { "a138", 788, NULL },
+ { "a139", 788, NULL }
+};
+
+BuiltinFont builtinFonts[] = {
+ { "Courier", standardEncoding, 629, -157, { -23, -250, 715, 805}, NULL },
+ { "Courier-Bold", standardEncoding, 629, -157, {-113, -250, 749, 801}, NULL },
+ { "Courier-BoldOblique", standardEncoding, 629, -157, { -57, -250, 869, 801}, NULL },
+ { "Courier-Oblique", standardEncoding, 629, -157, { -27, -250, 849, 805}, NULL },
+ { "Helvetica", standardEncoding, 718, -207, {-166, -225, 1000, 931}, NULL },
+ { "Helvetica-Bold", standardEncoding, 718, -207, {-170, -228, 1003, 962}, NULL },
+ { "Helvetica-BoldOblique", standardEncoding, 718, -207, {-174, -228, 1114, 962}, NULL },
+ { "Helvetica-Oblique", standardEncoding, 718, -207, {-170, -225, 1116, 931}, NULL },
+ { "Symbol", symbolEncoding, 1010, -293, {-180, -293, 1090, 1010}, NULL },
+ { "Times-Bold", standardEncoding, 683, -217, {-168, -218, 1000, 935}, NULL },
+ { "Times-BoldItalic", standardEncoding, 683, -217, {-200, -218, 996, 921}, NULL },
+ { "Times-Italic", standardEncoding, 683, -217, {-169, -217, 1010, 883}, NULL },
+ { "Times-Roman", standardEncoding, 683, -217, {-168, -218, 1000, 898}, NULL },
+ { "ZapfDingbats", zapfDingbatsEncoding, 820, -143, { -1, -143, 981, 820}, NULL }
+};
+
+BuiltinFont *builtinFontSubst[] = {
+ &builtinFonts[0],
+ &builtinFonts[3],
+ &builtinFonts[1],
+ &builtinFonts[2],
+ &builtinFonts[4],
+ &builtinFonts[7],
+ &builtinFonts[5],
+ &builtinFonts[6],
+ &builtinFonts[12],
+ &builtinFonts[11],
+ &builtinFonts[9],
+ &builtinFonts[10]
+};
+
+void initBuiltinFontTables() {
+ builtinFonts[0].widths = new BuiltinFontWidths(courierWidthsTab, 315);
+ builtinFonts[1].widths = new BuiltinFontWidths(courierBoldWidthsTab, 315);
+ builtinFonts[2].widths = new BuiltinFontWidths(courierBoldObliqueWidthsTab, 315);
+ builtinFonts[3].widths = new BuiltinFontWidths(courierObliqueWidthsTab, 315);
+ builtinFonts[4].widths = new BuiltinFontWidths(helveticaWidthsTab, 315);
+ builtinFonts[5].widths = new BuiltinFontWidths(helveticaBoldWidthsTab, 316);
+ builtinFonts[6].widths = new BuiltinFontWidths(helveticaBoldObliqueWidthsTab, 315);
+ builtinFonts[7].widths = new BuiltinFontWidths(helveticaObliqueWidthsTab, 315);
+ builtinFonts[8].widths = new BuiltinFontWidths(symbolWidthsTab, 190);
+ builtinFonts[9].widths = new BuiltinFontWidths(timesBoldWidthsTab, 315);
+ builtinFonts[10].widths = new BuiltinFontWidths(timesBoldItalicWidthsTab, 315);
+ builtinFonts[11].widths = new BuiltinFontWidths(timesItalicWidthsTab, 315);
+ builtinFonts[12].widths = new BuiltinFontWidths(timesRomanWidthsTab, 315);
+ builtinFonts[13].widths = new BuiltinFontWidths(zapfDingbatsWidthsTab, 202);
+}
+
+void freeBuiltinFontTables() {
+ int i;
+
+ for (i = 0; i < 14; ++i) {
+ delete builtinFonts[i].widths;
+ }
+}
--- /dev/null
+//========================================================================
+//
+// BuiltinFontTables.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef BUILTINFONTTABLES_H
+#define BUILTINFONTTABLES_H
+
+#include "BuiltinFont.h"
+
+#define nBuiltinFonts 14
+#define nBuiltinFontSubsts 12
+
+extern BuiltinFont builtinFonts[nBuiltinFonts];
+extern BuiltinFont *builtinFontSubst[nBuiltinFontSubsts];
+
+extern void initBuiltinFontTables();
+extern void freeBuiltinFontTables();
+
+#endif
--- /dev/null
+//========================================================================
+//
+// CMap.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "gmem.h"
+#include "gfile.h"
+#include "GString.h"
+#include "Error.h"
+#include "GlobalParams.h"
+#include "PSTokenizer.h"
+#include "CMap.h"
+
+//------------------------------------------------------------------------
+
+struct CMapVectorEntry {
+ GBool isVector;
+ union {
+ CMapVectorEntry *vector;
+ CID cid;
+ };
+};
+
+//------------------------------------------------------------------------
+
+static int getCharFromFile(void *data) {
+ return fgetc((FILE *)data);
+}
+
+//------------------------------------------------------------------------
+
+CMap *CMap::parse(CMapCache *cache, GString *collectionA,
+ GString *cMapNameA) {
+ FILE *f;
+ CMap *cmap;
+ PSTokenizer *pst;
+ char tok1[256], tok2[256], tok3[256];
+ int n1, n2, n3;
+ Guint start, end, code;
+
+ if (!(f = globalParams->findCMapFile(collectionA, cMapNameA))) {
+
+ // Check for an identity CMap.
+ if (!cMapNameA->cmp("Identity") || !cMapNameA->cmp("Identity-H")) {
+ return new CMap(collectionA->copy(), cMapNameA->copy(), 0);
+ }
+ if (!cMapNameA->cmp("Identity-V")) {
+ return new CMap(collectionA->copy(), cMapNameA->copy(), 1);
+ }
+
+ error(-1, "Couldn't find '%s' CMap file for '%s' collection",
+ cMapNameA->getCString(), collectionA->getCString());
+ return NULL;
+ }
+
+ cmap = new CMap(collectionA->copy(), cMapNameA->copy());
+
+ pst = new PSTokenizer(&getCharFromFile, f);
+ pst->getToken(tok1, sizeof(tok1), &n1);
+ while (pst->getToken(tok2, sizeof(tok2), &n2)) {
+ if (!strcmp(tok2, "usecmap")) {
+ if (tok1[0] == '/') {
+ cmap->useCMap(cache, tok1 + 1);
+ }
+ pst->getToken(tok1, sizeof(tok1), &n1);
+ } else if (!strcmp(tok1, "/WMode")) {
+ cmap->wMode = atoi(tok2);
+ pst->getToken(tok1, sizeof(tok1), &n1);
+ } else if (!strcmp(tok2, "begincodespacerange")) {
+ while (pst->getToken(tok1, sizeof(tok1), &n1)) {
+ if (!strcmp(tok1, "endcodespacerange")) {
+ break;
+ }
+ if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
+ !strcmp(tok2, "endcodespacerange")) {
+ error(-1, "Illegal entry in codespacerange block in CMap");
+ break;
+ }
+ if (tok1[0] == '<' && tok2[0] == '<' &&
+ n1 == n2 && n1 >= 4 && (n1 & 1) == 0) {
+ tok1[n1 - 1] = tok2[n1 - 1] = '\0';
+ sscanf(tok1 + 1, "%x", &start);
+ sscanf(tok2 + 1, "%x", &end);
+ n1 = (n1 - 2) / 2;
+ cmap->addCodeSpace(cmap->vector, start, end, n1);
+ }
+ }
+ pst->getToken(tok1, sizeof(tok1), &n1);
+ } else if (!strcmp(tok2, "begincidchar")) {
+ while (pst->getToken(tok1, sizeof(tok1), &n1)) {
+ if (!strcmp(tok1, "endcidchar")) {
+ break;
+ }
+ if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
+ !strcmp(tok2, "endcidchar")) {
+ error(-1, "Illegal entry in cidchar block in CMap");
+ break;
+ }
+ if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' &&
+ n1 >= 4 && (n1 & 1) == 0)) {
+ error(-1, "Illegal entry in cidchar block in CMap");
+ continue;
+ }
+ tok1[n1 - 1] = '\0';
+ if (sscanf(tok1 + 1, "%x", &code) != 1) {
+ error(-1, "Illegal entry in cidchar block in CMap");
+ continue;
+ }
+ n1 = (n1 - 2) / 2;
+ cmap->addCIDs(code, code, n1, (CID)atoi(tok2));
+ }
+ pst->getToken(tok1, sizeof(tok1), &n1);
+ } else if (!strcmp(tok2, "begincidrange")) {
+ while (pst->getToken(tok1, sizeof(tok1), &n1)) {
+ if (!strcmp(tok1, "endcidrange")) {
+ break;
+ }
+ if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
+ !strcmp(tok2, "endcidrange") ||
+ !pst->getToken(tok3, sizeof(tok3), &n3) ||
+ !strcmp(tok3, "endcidrange")) {
+ error(-1, "Illegal entry in cidrange block in CMap");
+ break;
+ }
+ if (tok1[0] == '<' && tok2[0] == '<' &&
+ n1 == n2 && n1 >= 4 && (n1 & 1) == 0) {
+ tok1[n1 - 1] = tok2[n1 - 1] = '\0';
+ sscanf(tok1 + 1, "%x", &start);
+ sscanf(tok2 + 1, "%x", &end);
+ n1 = (n1 - 2) / 2;
+ cmap->addCIDs(start, end, n1, (CID)atoi(tok3));
+ }
+ }
+ pst->getToken(tok1, sizeof(tok1), &n1);
+ } else {
+ strcpy(tok1, tok2);
+ }
+ }
+ delete pst;
+
+ fclose(f);
+
+ return cmap;
+}
+
+CMap::CMap(GString *collectionA, GString *cMapNameA) {
+ int i;
+
+ collection = collectionA;
+ cMapName = cMapNameA;
+ wMode = 0;
+ vector = (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
+ for (i = 0; i < 256; ++i) {
+ vector[i].isVector = gFalse;
+ vector[i].cid = 0;
+ }
+ refCnt = 1;
+#if MULTITHREADED
+ gInitMutex(&mutex);
+#endif
+}
+
+CMap::CMap(GString *collectionA, GString *cMapNameA, int wModeA) {
+ collection = collectionA;
+ cMapName = cMapNameA;
+ wMode = wModeA;
+ vector = NULL;
+ refCnt = 1;
+#if MULTITHREADED
+ gInitMutex(&mutex);
+#endif
+}
+
+void CMap::useCMap(CMapCache *cache, char *useName) {
+ GString *useNameStr;
+ CMap *subCMap;
+
+ useNameStr = new GString(useName);
+ subCMap = cache->getCMap(collection, useNameStr);
+ delete useNameStr;
+ if (!subCMap) {
+ return;
+ }
+ copyVector(vector, subCMap->vector);
+ subCMap->decRefCnt();
+}
+
+void CMap::copyVector(CMapVectorEntry *dest, CMapVectorEntry *src) {
+ int i, j;
+
+ for (i = 0; i < 256; ++i) {
+ if (src[i].isVector) {
+ if (!dest[i].isVector) {
+ dest[i].isVector = gTrue;
+ dest[i].vector =
+ (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
+ for (j = 0; j < 256; ++j) {
+ dest[i].vector[j].isVector = gFalse;
+ dest[i].vector[j].cid = 0;
+ }
+ }
+ copyVector(dest[i].vector, src[i].vector);
+ } else {
+ if (dest[i].isVector) {
+ error(-1, "Collision in usecmap");
+ } else {
+ dest[i].cid = src[i].cid;
+ }
+ }
+ }
+}
+
+void CMap::addCodeSpace(CMapVectorEntry *vec, Guint start, Guint end,
+ Guint nBytes) {
+ Guint start2, end2;
+ int startByte, endByte, i, j;
+
+ if (nBytes > 1) {
+ startByte = (start >> (8 * (nBytes - 1))) & 0xff;
+ endByte = (end >> (8 * (nBytes - 1))) & 0xff;
+ start2 = start & ((1 << (8 * (nBytes - 1))) - 1);
+ end2 = end & ((1 << (8 * (nBytes - 1))) - 1);
+ for (i = startByte; i <= endByte; ++i) {
+ if (!vec[i].isVector) {
+ vec[i].isVector = gTrue;
+ vec[i].vector =
+ (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
+ for (j = 0; j < 256; ++j) {
+ vec[i].vector[j].isVector = gFalse;
+ vec[i].vector[j].cid = 0;
+ }
+ }
+ addCodeSpace(vec[i].vector, start2, end2, nBytes - 1);
+ }
+ }
+}
+
+void CMap::addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID) {
+ CMapVectorEntry *vec;
+ CID cid;
+ int byte;
+ Guint i;
+
+ vec = vector;
+ for (i = nBytes - 1; i >= 1; --i) {
+ byte = (start >> (8 * i)) & 0xff;
+ if (!vec[byte].isVector) {
+ error(-1, "Invalid CID (%0*x - %0*x) in CMap",
+ 2*nBytes, start, 2*nBytes, end);
+ return;
+ }
+ vec = vec[byte].vector;
+ }
+ cid = firstCID;
+ for (byte = (int)(start & 0xff); byte <= (int)(end & 0xff); ++byte) {
+ if (vec[byte].isVector) {
+ error(-1, "Invalid CID (%0*x - %0*x) in CMap",
+ 2*nBytes, start, 2*nBytes, end);
+ } else {
+ vec[byte].cid = cid;
+ }
+ ++cid;
+ }
+}
+
+CMap::~CMap() {
+ delete collection;
+ delete cMapName;
+ if (vector) {
+ freeCMapVector(vector);
+ }
+#if MULTITHREADED
+ gDestroyMutex(&mutex);
+#endif
+}
+
+void CMap::freeCMapVector(CMapVectorEntry *vec) {
+ int i;
+
+ for (i = 0; i < 256; ++i) {
+ if (vec[i].isVector) {
+ freeCMapVector(vec[i].vector);
+ }
+ }
+ gfree(vec);
+}
+
+void CMap::incRefCnt() {
+#if MULTITHREADED
+ gLockMutex(&mutex);
+#endif
+ ++refCnt;
+#if MULTITHREADED
+ gUnlockMutex(&mutex);
+#endif
+}
+
+void CMap::decRefCnt() {
+ GBool done;
+
+#if MULTITHREADED
+ gLockMutex(&mutex);
+#endif
+ done = --refCnt == 0;
+#if MULTITHREADED
+ gUnlockMutex(&mutex);
+#endif
+ if (done) {
+ delete this;
+ }
+}
+
+GBool CMap::match(GString *collectionA, GString *cMapNameA) {
+ return !collection->cmp(collectionA) && !cMapName->cmp(cMapNameA);
+}
+
+CID CMap::getCID(char *s, int len, int *nUsed) {
+ CMapVectorEntry *vec;
+ int n, i;
+
+ if (!(vec = vector)) {
+ // identity CMap
+ *nUsed = 2;
+ if (len < 2) {
+ return 0;
+ }
+ return ((s[0] & 0xff) << 8) + (s[1] & 0xff);
+ }
+ n = 0;
+ while (1) {
+ if (n >= len) {
+ *nUsed = n;
+ return 0;
+ }
+ i = s[n++] & 0xff;
+ if (!vec[i].isVector) {
+ *nUsed = n;
+ return vec[i].cid;
+ }
+ vec = vec[i].vector;
+ }
+}
+
+//------------------------------------------------------------------------
+
+CMapCache::CMapCache() {
+ int i;
+
+ for (i = 0; i < cMapCacheSize; ++i) {
+ cache[i] = NULL;
+ }
+}
+
+CMapCache::~CMapCache() {
+ int i;
+
+ for (i = 0; i < cMapCacheSize; ++i) {
+ if (cache[i]) {
+ cache[i]->decRefCnt();
+ }
+ }
+}
+
+CMap *CMapCache::getCMap(GString *collection, GString *cMapName) {
+ CMap *cmap;
+ int i, j;
+
+ if (cache[0] && cache[0]->match(collection, cMapName)) {
+ cache[0]->incRefCnt();
+ return cache[0];
+ }
+ for (i = 1; i < cMapCacheSize; ++i) {
+ if (cache[i] && cache[i]->match(collection, cMapName)) {
+ cmap = cache[i];
+ for (j = i; j >= 1; --j) {
+ cache[j] = cache[j - 1];
+ }
+ cache[0] = cmap;
+ cmap->incRefCnt();
+ return cmap;
+ }
+ }
+ if ((cmap = CMap::parse(this, collection, cMapName))) {
+ if (cache[cMapCacheSize - 1]) {
+ cache[cMapCacheSize - 1]->decRefCnt();
+ }
+ for (j = cMapCacheSize - 1; j >= 1; --j) {
+ cache[j] = cache[j - 1];
+ }
+ cache[0] = cmap;
+ cmap->incRefCnt();
+ return cmap;
+ }
+ return NULL;
+}
--- /dev/null
+//========================================================================
+//
+// CMap.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef CMAP_H
+#define CMAP_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "CharTypes.h"
+
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+
+class GString;
+struct CMapVectorEntry;
+class CMapCache;
+
+//------------------------------------------------------------------------
+
+class CMap {
+public:
+
+ // Create the CMap specified by <collection> and <cMapName>. Sets
+ // the initial reference count to 1. Returns NULL on failure.
+ static CMap *parse(CMapCache *cache, GString *collectionA,
+ GString *cMapNameA);
+
+ ~CMap();
+
+ void incRefCnt();
+ void decRefCnt();
+
+ // Return collection name (<registry>-<ordering>).
+ GString *getCollection() { return collection; }
+
+ // Return true if this CMap matches the specified <collectionA>, and
+ // <cMapNameA>.
+ GBool match(GString *collectionA, GString *cMapNameA);
+
+ // Return the CID corresponding to the character code starting at
+ // <s>, which contains <len> bytes. Sets *<nUsed> to the number of
+ // bytes used by the char code.
+ CID getCID(char *s, int len, int *nUsed);
+
+ // Return the writing mode (0=horizontal, 1=vertical).
+ int getWMode() { return wMode; }
+
+private:
+
+ CMap(GString *collectionA, GString *cMapNameA);
+ CMap(GString *collectionA, GString *cMapNameA, int wModeA);
+ void useCMap(CMapCache *cache, char *useName);
+ void copyVector(CMapVectorEntry *dest, CMapVectorEntry *src);
+ void addCodeSpace(CMapVectorEntry *vec, Guint start, Guint end,
+ Guint nBytes);
+ void addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID);
+ void freeCMapVector(CMapVectorEntry *vec);
+
+ GString *collection;
+ GString *cMapName;
+ int wMode; // writing mode (0=horizontal, 1=vertical)
+ CMapVectorEntry *vector; // vector for first byte (NULL for
+ // identity CMap)
+ int refCnt;
+#if MULTITHREADED
+ GMutex mutex;
+#endif
+};
+
+//------------------------------------------------------------------------
+
+#define cMapCacheSize 4
+
+class CMapCache {
+public:
+
+ CMapCache();
+ ~CMapCache();
+
+ // Get the <cMapName> CMap for the specified character collection.
+ // Increments its reference count; there will be one reference for
+ // the cache plus one for the caller of this function. Returns NULL
+ // on failure.
+ CMap *getCMap(GString *collection, GString *cMapName);
+
+private:
+
+ CMap *cache[cMapCacheSize];
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// Catalog.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stddef.h>
+#include "gmem.h"
+#include "Object.h"
+#include "XRef.h"
+#include "Array.h"
+#include "Dict.h"
+#include "Page.h"
+#include "Error.h"
+#include "Link.h"
+#include "Catalog.h"
+
+//------------------------------------------------------------------------
+// Catalog
+//------------------------------------------------------------------------
+
+Catalog::Catalog(XRef *xrefA) {
+ Object catDict, pagesDict;
+ Object obj, obj2;
+ int numPages0;
+ int i;
+
+ ok = gTrue;
+ xref = xrefA;
+ pages = NULL;
+ pageRefs = NULL;
+ numPages = pagesSize = 0;
+ baseURI = NULL;
+
+ xref->getCatalog(&catDict);
+ if (!catDict.isDict()) {
+ error(-1, "Catalog object is wrong type (%s)", catDict.getTypeName());
+ goto err1;
+ }
+
+ // read page tree
+ catDict.dictLookup("Pages", &pagesDict);
+ // This should really be isDict("Pages"), but I've seen at least one
+ // PDF file where the /Type entry is missing.
+ if (!pagesDict.isDict()) {
+ error(-1, "Top-level pages object is wrong type (%s)",
+ pagesDict.getTypeName());
+ goto err2;
+ }
+ pagesDict.dictLookup("Count", &obj);
+ // some PDF files actually use real numbers here ("/Count 9.0")
+ if (!obj.isNum()) {
+ error(-1, "Page count in top-level pages object is wrong type (%s)",
+ obj.getTypeName());
+ goto err3;
+ }
+ pagesSize = numPages0 = (int)obj.getNum();
+ obj.free();
+ pages = (Page **)gmallocn(pagesSize, sizeof(Page *));
+ pageRefs = (Ref *)gmallocn(pagesSize, sizeof(Ref));
+ for (i = 0; i < pagesSize; ++i) {
+ pages[i] = NULL;
+ pageRefs[i].num = -1;
+ pageRefs[i].gen = -1;
+ }
+ numPages = readPageTree(pagesDict.getDict(), NULL, 0);
+ if (numPages != numPages0) {
+ error(-1, "Page count in top-level pages object is incorrect");
+ }
+ pagesDict.free();
+
+ // read named destination dictionary
+ catDict.dictLookup("Dests", &dests);
+
+ // read root of named destination tree
+ if (catDict.dictLookup("Names", &obj)->isDict())
+ obj.dictLookup("Dests", &nameTree);
+ else
+ nameTree.initNull();
+ obj.free();
+
+ // read base URI
+ if (catDict.dictLookup("URI", &obj)->isDict()) {
+ if (obj.dictLookup("Base", &obj2)->isString()) {
+ baseURI = obj2.getString()->copy();
+ }
+ obj2.free();
+ }
+ obj.free();
+
+ // get the metadata stream
+ catDict.dictLookup("Metadata", &metadata);
+
+ // get the structure tree root
+ catDict.dictLookup("StructTreeRoot", &structTreeRoot);
+
+ // get the outline dictionary
+ catDict.dictLookup("Outlines", &outline);
+
+ // get the AcroForm dictionary
+ catDict.dictLookup("AcroForm", &acroForm);
+
+ catDict.free();
+ return;
+
+ err3:
+ obj.free();
+ err2:
+ pagesDict.free();
+ err1:
+ catDict.free();
+ dests.initNull();
+ nameTree.initNull();
+ ok = gFalse;
+}
+
+Catalog::~Catalog() {
+ int i;
+
+ if (pages) {
+ for (i = 0; i < pagesSize; ++i) {
+ if (pages[i]) {
+ delete pages[i];
+ }
+ }
+ gfree(pages);
+ gfree(pageRefs);
+ }
+ dests.free();
+ nameTree.free();
+ if (baseURI) {
+ delete baseURI;
+ }
+ metadata.free();
+ structTreeRoot.free();
+ outline.free();
+ acroForm.free();
+}
+
+GString *Catalog::readMetadata() {
+ GString *s;
+ Dict *dict;
+ Object obj;
+ int c;
+
+ if (!metadata.isStream()) {
+ return NULL;
+ }
+ dict = metadata.streamGetDict();
+ if (!dict->lookup("Subtype", &obj)->isName("XML")) {
+ error(-1, "Unknown Metadata type: '%s'",
+ obj.isName() ? obj.getName() : "???");
+ }
+ obj.free();
+ s = new GString();
+ metadata.streamReset();
+ while ((c = metadata.streamGetChar()) != EOF) {
+ s->append(c);
+ }
+ metadata.streamClose();
+ return s;
+}
+
+int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start) {
+ Object kids;
+ Object kid;
+ Object kidRef;
+ PageAttrs *attrs1, *attrs2;
+ Page *page;
+ int i, j;
+
+ attrs1 = new PageAttrs(attrs, pagesDict);
+ pagesDict->lookup("Kids", &kids);
+ if (!kids.isArray()) {
+ error(-1, "Kids object (page %d) is wrong type (%s)",
+ start+1, kids.getTypeName());
+ goto err1;
+ }
+ for (i = 0; i < kids.arrayGetLength(); ++i) {
+ kids.arrayGet(i, &kid);
+ if (kid.isDict("Page")) {
+ attrs2 = new PageAttrs(attrs1, kid.getDict());
+ page = new Page(xref, start+1, kid.getDict(), attrs2);
+ if (!page->isOk()) {
+ ++start;
+ goto err3;
+ }
+ if (start >= pagesSize) {
+ pagesSize += 32;
+ pages = (Page **)greallocn(pages, pagesSize, sizeof(Page *));
+ pageRefs = (Ref *)greallocn(pageRefs, pagesSize, sizeof(Ref));
+ for (j = pagesSize - 32; j < pagesSize; ++j) {
+ pages[j] = NULL;
+ pageRefs[j].num = -1;
+ pageRefs[j].gen = -1;
+ }
+ }
+ pages[start] = page;
+ kids.arrayGetNF(i, &kidRef);
+ if (kidRef.isRef()) {
+ pageRefs[start].num = kidRef.getRefNum();
+ pageRefs[start].gen = kidRef.getRefGen();
+ }
+ kidRef.free();
+ ++start;
+ // This should really be isDict("Pages"), but I've seen at least one
+ // PDF file where the /Type entry is missing.
+ } else if (kid.isDict()) {
+ if ((start = readPageTree(kid.getDict(), attrs1, start))
+ < 0)
+ goto err2;
+ } else {
+ error(-1, "Kid object (page %d) is wrong type (%s)",
+ start+1, kid.getTypeName());
+ }
+ kid.free();
+ }
+ delete attrs1;
+ kids.free();
+ return start;
+
+ err3:
+ delete page;
+ err2:
+ kid.free();
+ err1:
+ kids.free();
+ delete attrs1;
+ ok = gFalse;
+ return -1;
+}
+
+int Catalog::findPage(int num, int gen) {
+ int i;
+
+ for (i = 0; i < numPages; ++i) {
+ if (pageRefs[i].num == num && pageRefs[i].gen == gen)
+ return i + 1;
+ }
+ return 0;
+}
+
+LinkDest *Catalog::findDest(GString *name) {
+ LinkDest *dest;
+ Object obj1, obj2;
+ GBool found;
+
+ // try named destination dictionary then name tree
+ found = gFalse;
+ if (dests.isDict()) {
+ if (!dests.dictLookup(name->getCString(), &obj1)->isNull())
+ found = gTrue;
+ else
+ obj1.free();
+ }
+ if (!found && nameTree.isDict()) {
+ if (!findDestInTree(&nameTree, name, &obj1)->isNull())
+ found = gTrue;
+ else
+ obj1.free();
+ }
+ if (!found)
+ return NULL;
+
+ // construct LinkDest
+ dest = NULL;
+ if (obj1.isArray()) {
+ dest = new LinkDest(obj1.getArray());
+ } else if (obj1.isDict()) {
+ if (obj1.dictLookup("D", &obj2)->isArray())
+ dest = new LinkDest(obj2.getArray());
+ else
+ error(-1, "Bad named destination value");
+ obj2.free();
+ } else {
+ error(-1, "Bad named destination value");
+ }
+ obj1.free();
+ if (dest && !dest->isOk()) {
+ delete dest;
+ dest = NULL;
+ }
+
+ return dest;
+}
+
+Object *Catalog::findDestInTree(Object *tree, GString *name, Object *obj) {
+ Object names, name1;
+ Object kids, kid, limits, low, high;
+ GBool done, found;
+ int cmp, i;
+
+ // leaf node
+ if (tree->dictLookup("Names", &names)->isArray()) {
+ done = found = gFalse;
+ for (i = 0; !done && i < names.arrayGetLength(); i += 2) {
+ if (names.arrayGet(i, &name1)->isString()) {
+ cmp = name->cmp(name1.getString());
+ if (cmp == 0) {
+ names.arrayGet(i+1, obj);
+ found = gTrue;
+ done = gTrue;
+ } else if (cmp < 0) {
+ done = gTrue;
+ }
+ }
+ name1.free();
+ }
+ names.free();
+ if (!found)
+ obj->initNull();
+ return obj;
+ }
+ names.free();
+
+ // root or intermediate node
+ done = gFalse;
+ if (tree->dictLookup("Kids", &kids)->isArray()) {
+ for (i = 0; !done && i < kids.arrayGetLength(); ++i) {
+ if (kids.arrayGet(i, &kid)->isDict()) {
+ if (kid.dictLookup("Limits", &limits)->isArray()) {
+ if (limits.arrayGet(0, &low)->isString() &&
+ name->cmp(low.getString()) >= 0) {
+ if (limits.arrayGet(1, &high)->isString() &&
+ name->cmp(high.getString()) <= 0) {
+ findDestInTree(&kid, name, obj);
+ done = gTrue;
+ }
+ high.free();
+ }
+ low.free();
+ }
+ limits.free();
+ }
+ kid.free();
+ }
+ }
+ kids.free();
+
+ // name was outside of ranges of all kids
+ if (!done)
+ obj->initNull();
+
+ return obj;
+}
--- /dev/null
+//========================================================================
+//
+// Catalog.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef CATALOG_H
+#define CATALOG_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+class XRef;
+class Object;
+class Page;
+class PageAttrs;
+struct Ref;
+class LinkDest;
+
+//------------------------------------------------------------------------
+// Catalog
+//------------------------------------------------------------------------
+
+class Catalog {
+public:
+
+ // Constructor.
+ Catalog(XRef *xrefA);
+
+ // Destructor.
+ ~Catalog();
+
+ // Is catalog valid?
+ GBool isOk() { return ok; }
+
+ // Get number of pages.
+ int getNumPages() { return numPages; }
+
+ // Get a page.
+ Page *getPage(int i) { return pages[i-1]; }
+
+ // Get the reference for a page object.
+ Ref *getPageRef(int i) { return &pageRefs[i-1]; }
+
+ // Return base URI, or NULL if none.
+ GString *getBaseURI() { return baseURI; }
+
+ // Return the contents of the metadata stream, or NULL if there is
+ // no metadata.
+ GString *readMetadata();
+
+ // Return the structure tree root object.
+ Object *getStructTreeRoot() { return &structTreeRoot; }
+
+ // Find a page, given its object ID. Returns page number, or 0 if
+ // not found.
+ int findPage(int num, int gen);
+
+ // Find a named destination. Returns the link destination, or
+ // NULL if <name> is not a destination.
+ LinkDest *findDest(GString *name);
+
+ Object *getOutline() { return &outline; }
+
+ Object *getAcroForm() { return &acroForm; }
+
+private:
+
+ XRef *xref; // the xref table for this PDF file
+ Page **pages; // array of pages
+ Ref *pageRefs; // object ID for each page
+ int numPages; // number of pages
+ int pagesSize; // size of pages array
+ Object dests; // named destination dictionary
+ Object nameTree; // name tree
+ GString *baseURI; // base URI for URI-type links
+ Object metadata; // metadata stream
+ Object structTreeRoot; // structure tree root dictionary
+ Object outline; // outline dictionary
+ Object acroForm; // AcroForm dictionary
+ GBool ok; // true if catalog is valid
+
+ int readPageTree(Dict *pages, PageAttrs *attrs, int start);
+ Object *findDestInTree(Object *tree, GString *name, Object *obj);
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// CharCodeToUnicode.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include "gmem.h"
+#include "gfile.h"
+#include "GString.h"
+#include "Error.h"
+#include "GlobalParams.h"
+#include "PSTokenizer.h"
+#include "CharCodeToUnicode.h"
+
+//------------------------------------------------------------------------
+
+#define maxUnicodeString 8
+
+struct CharCodeToUnicodeString {
+ CharCode c;
+ Unicode u[maxUnicodeString];
+ int len;
+};
+
+//------------------------------------------------------------------------
+
+static int getCharFromString(void *data) {
+ char *p;
+ int c;
+
+ p = *(char **)data;
+ if (*p) {
+ c = *p++;
+ *(char **)data = p;
+ } else {
+ c = EOF;
+ }
+ return c;
+}
+
+static int getCharFromFile(void *data) {
+ return fgetc((FILE *)data);
+}
+
+//------------------------------------------------------------------------
+
+CharCodeToUnicode *CharCodeToUnicode::parseCIDToUnicode(GString *fileName,
+ GString *collection) {
+ FILE *f;
+ Unicode *mapA;
+ CharCode size, mapLenA;
+ char buf[64];
+ Unicode u;
+ CharCodeToUnicode *ctu;
+
+ if (!(f = fopen(fileName->getCString(), "r"))) {
+ error(-1, "Couldn't open cidToUnicode file '%s'",
+ fileName->getCString());
+ return NULL;
+ }
+
+ size = 32768;
+ mapA = (Unicode *)gmallocn(size, sizeof(Unicode));
+ mapLenA = 0;
+
+ while (getLine(buf, sizeof(buf), f)) {
+ if (mapLenA == size) {
+ size *= 2;
+ mapA = (Unicode *)greallocn(mapA, size, sizeof(Unicode));
+ }
+ if (sscanf(buf, "%x", &u) == 1) {
+ mapA[mapLenA] = u;
+ } else {
+ error(-1, "Bad line (%d) in cidToUnicode file '%s'",
+ (int)(mapLenA + 1), fileName->getCString());
+ mapA[mapLenA] = 0;
+ }
+ ++mapLenA;
+ }
+ fclose(f);
+
+ ctu = new CharCodeToUnicode(collection->copy(), mapA, mapLenA, gTrue,
+ NULL, 0, 0);
+ gfree(mapA);
+ return ctu;
+}
+
+CharCodeToUnicode *CharCodeToUnicode::parseUnicodeToUnicode(
+ GString *fileName) {
+ FILE *f;
+ Unicode *mapA;
+ CharCodeToUnicodeString *sMapA;
+ CharCode size, oldSize, len, sMapSizeA, sMapLenA;
+ char buf[256];
+ char *tok;
+ Unicode u0;
+ Unicode uBuf[maxUnicodeString];
+ CharCodeToUnicode *ctu;
+ int line, n, i;
+
+ if (!(f = fopen(fileName->getCString(), "r"))) {
+ error(-1, "Couldn't open unicodeToUnicode file '%s'",
+ fileName->getCString());
+ return NULL;
+ }
+
+ size = 4096;
+ mapA = (Unicode *)gmallocn(size, sizeof(Unicode));
+ memset(mapA, 0, size * sizeof(Unicode));
+ len = 0;
+ sMapA = NULL;
+ sMapSizeA = sMapLenA = 0;
+
+ line = 0;
+ while (getLine(buf, sizeof(buf), f)) {
+ ++line;
+ if (!(tok = strtok(buf, " \t\r\n")) ||
+ sscanf(tok, "%x", &u0) != 1) {
+ error(-1, "Bad line (%d) in unicodeToUnicode file '%s'",
+ line, fileName->getCString());
+ continue;
+ }
+ n = 0;
+ while (n < maxUnicodeString) {
+ if (!(tok = strtok(NULL, " \t\r\n"))) {
+ break;
+ }
+ if (sscanf(tok, "%x", &uBuf[n]) != 1) {
+ error(-1, "Bad line (%d) in unicodeToUnicode file '%s'",
+ line, fileName->getCString());
+ break;
+ }
+ ++n;
+ }
+ if (n < 1) {
+ error(-1, "Bad line (%d) in unicodeToUnicode file '%s'",
+ line, fileName->getCString());
+ continue;
+ }
+ if (u0 >= size) {
+ oldSize = size;
+ while (u0 >= size) {
+ size *= 2;
+ }
+ mapA = (Unicode *)greallocn(mapA, size, sizeof(Unicode));
+ memset(mapA + oldSize, 0, (size - oldSize) * sizeof(Unicode));
+ }
+ if (n == 1) {
+ mapA[u0] = uBuf[0];
+ } else {
+ mapA[u0] = 0;
+ if (sMapLenA == sMapSizeA) {
+ sMapSizeA += 16;
+ sMapA = (CharCodeToUnicodeString *)
+ greallocn(sMapA, sMapSizeA, sizeof(CharCodeToUnicodeString));
+ }
+ sMapA[sMapLenA].c = u0;
+ for (i = 0; i < n; ++i) {
+ sMapA[sMapLenA].u[i] = uBuf[i];
+ }
+ sMapA[sMapLenA].len = n;
+ ++sMapLenA;
+ }
+ if (u0 >= len) {
+ len = u0 + 1;
+ }
+ }
+ fclose(f);
+
+ ctu = new CharCodeToUnicode(fileName->copy(), mapA, len, gTrue,
+ sMapA, sMapLenA, sMapSizeA);
+ gfree(mapA);
+ return ctu;
+}
+
+CharCodeToUnicode *CharCodeToUnicode::make8BitToUnicode(Unicode *toUnicode) {
+ return new CharCodeToUnicode(NULL, toUnicode, 256, gTrue, NULL, 0, 0);
+}
+
+CharCodeToUnicode *CharCodeToUnicode::parseCMap(GString *buf, int nBits) {
+ CharCodeToUnicode *ctu;
+ char *p;
+
+ ctu = new CharCodeToUnicode(NULL);
+ p = buf->getCString();
+ ctu->parseCMap1(&getCharFromString, &p, nBits);
+ return ctu;
+}
+
+void CharCodeToUnicode::mergeCMap(GString *buf, int nBits) {
+ char *p;
+
+ p = buf->getCString();
+ parseCMap1(&getCharFromString, &p, nBits);
+}
+
+void CharCodeToUnicode::parseCMap1(int (*getCharFunc)(void *), void *data,
+ int nBits) {
+ PSTokenizer *pst;
+ char tok1[256], tok2[256], tok3[256];
+ int nDigits, n1, n2, n3;
+ CharCode i;
+ CharCode code1, code2;
+ GString *name;
+ FILE *f;
+
+ nDigits = nBits / 4;
+ pst = new PSTokenizer(getCharFunc, data);
+ pst->getToken(tok1, sizeof(tok1), &n1);
+ while (pst->getToken(tok2, sizeof(tok2), &n2)) {
+ if (!strcmp(tok2, "usecmap")) {
+ if (tok1[0] == '/') {
+ name = new GString(tok1 + 1);
+ if ((f = globalParams->findToUnicodeFile(name))) {
+ parseCMap1(&getCharFromFile, f, nBits);
+ fclose(f);
+ } else {
+ error(-1, "Couldn't find ToUnicode CMap file for '%s'",
+ name->getCString());
+ }
+ delete name;
+ }
+ pst->getToken(tok1, sizeof(tok1), &n1);
+ } else if (!strcmp(tok2, "beginbfchar")) {
+ while (pst->getToken(tok1, sizeof(tok1), &n1)) {
+ if (!strcmp(tok1, "endbfchar")) {
+ break;
+ }
+ if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
+ !strcmp(tok2, "endbfchar")) {
+ error(-1, "Illegal entry in bfchar block in ToUnicode CMap");
+ break;
+ }
+ if (!(n1 == 2 + nDigits && tok1[0] == '<' && tok1[n1 - 1] == '>' &&
+ tok2[0] == '<' && tok2[n2 - 1] == '>')) {
+ error(-1, "Illegal entry in bfchar block in ToUnicode CMap");
+ continue;
+ }
+ tok1[n1 - 1] = tok2[n2 - 1] = '\0';
+ if (sscanf(tok1 + 1, "%x", &code1) != 1) {
+ error(-1, "Illegal entry in bfchar block in ToUnicode CMap");
+ continue;
+ }
+ addMapping(code1, tok2 + 1, n2 - 2, 0);
+ }
+ pst->getToken(tok1, sizeof(tok1), &n1);
+ } else if (!strcmp(tok2, "beginbfrange")) {
+ while (pst->getToken(tok1, sizeof(tok1), &n1)) {
+ if (!strcmp(tok1, "endbfrange")) {
+ break;
+ }
+ if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
+ !strcmp(tok2, "endbfrange") ||
+ !pst->getToken(tok3, sizeof(tok3), &n3) ||
+ !strcmp(tok3, "endbfrange")) {
+ error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
+ break;
+ }
+ if (!(n1 == 2 + nDigits && tok1[0] == '<' && tok1[n1 - 1] == '>' &&
+ n2 == 2 + nDigits && tok2[0] == '<' && tok2[n2 - 1] == '>')) {
+ error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
+ continue;
+ }
+ tok1[n1 - 1] = tok2[n2 - 1] = '\0';
+ if (sscanf(tok1 + 1, "%x", &code1) != 1 ||
+ sscanf(tok2 + 1, "%x", &code2) != 1) {
+ error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
+ continue;
+ }
+ if (!strcmp(tok3, "[")) {
+ i = 0;
+ while (pst->getToken(tok1, sizeof(tok1), &n1) &&
+ code1 + i <= code2) {
+ if (!strcmp(tok1, "]")) {
+ break;
+ }
+ if (tok1[0] == '<' && tok1[n1 - 1] == '>') {
+ tok1[n1 - 1] = '\0';
+ addMapping(code1 + i, tok1 + 1, n1 - 2, 0);
+ } else {
+ error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
+ }
+ ++i;
+ }
+ } else if (tok3[0] == '<' && tok3[n3 - 1] == '>') {
+ tok3[n3 - 1] = '\0';
+ for (i = 0; code1 <= code2; ++code1, ++i) {
+ addMapping(code1, tok3 + 1, n3 - 2, i);
+ }
+
+ } else {
+ error(-1, "Illegal entry in bfrange block in ToUnicode CMap");
+ }
+ }
+ pst->getToken(tok1, sizeof(tok1), &n1);
+ } else {
+ strcpy(tok1, tok2);
+ }
+ }
+ delete pst;
+}
+
+void CharCodeToUnicode::addMapping(CharCode code, char *uStr, int n,
+ int offset) {
+ CharCode oldLen, i;
+ Unicode u;
+ char uHex[5];
+ int j;
+
+ if (code >= mapLen) {
+ oldLen = mapLen;
+ mapLen = (code + 256) & ~255;
+ map = (Unicode *)greallocn(map, mapLen, sizeof(Unicode));
+ for (i = oldLen; i < mapLen; ++i) {
+ map[i] = 0;
+ }
+ }
+ if (n <= 4) {
+ if (sscanf(uStr, "%x", &u) != 1) {
+ error(-1, "Illegal entry in ToUnicode CMap");
+ return;
+ }
+ map[code] = u + offset;
+ } else {
+ if (sMapLen >= sMapSize) {
+ sMapSize = sMapSize + 16;
+ sMap = (CharCodeToUnicodeString *)
+ greallocn(sMap, sMapSize, sizeof(CharCodeToUnicodeString));
+ }
+ map[code] = 0;
+ sMap[sMapLen].c = code;
+ sMap[sMapLen].len = n / 4;
+ for (j = 0; j < sMap[sMapLen].len && j < maxUnicodeString; ++j) {
+ strncpy(uHex, uStr + j*4, 4);
+ uHex[4] = '\0';
+ if (sscanf(uHex, "%x", &sMap[sMapLen].u[j]) != 1) {
+ error(-1, "Illegal entry in ToUnicode CMap");
+ }
+ }
+ sMap[sMapLen].u[sMap[sMapLen].len - 1] += offset;
+ ++sMapLen;
+ }
+}
+
+CharCodeToUnicode::CharCodeToUnicode(GString *tagA) {
+ CharCode i;
+
+ tag = tagA;
+ mapLen = 256;
+ map = (Unicode *)gmallocn(mapLen, sizeof(Unicode));
+ for (i = 0; i < mapLen; ++i) {
+ map[i] = 0;
+ }
+ sMap = NULL;
+ sMapLen = sMapSize = 0;
+ refCnt = 1;
+#if MULTITHREADED
+ gInitMutex(&mutex);
+#endif
+}
+
+CharCodeToUnicode::CharCodeToUnicode(GString *tagA, Unicode *mapA,
+ CharCode mapLenA, GBool copyMap,
+ CharCodeToUnicodeString *sMapA,
+ int sMapLenA, int sMapSizeA) {
+ tag = tagA;
+ mapLen = mapLenA;
+ if (copyMap) {
+ map = (Unicode *)gmallocn(mapLen, sizeof(Unicode));
+ memcpy(map, mapA, mapLen * sizeof(Unicode));
+ } else {
+ map = mapA;
+ }
+ sMap = sMapA;
+ sMapLen = sMapLenA;
+ sMapSize = sMapSizeA;
+ refCnt = 1;
+#if MULTITHREADED
+ gInitMutex(&mutex);
+#endif
+}
+
+CharCodeToUnicode::~CharCodeToUnicode() {
+ if (tag) {
+ delete tag;
+ }
+ gfree(map);
+ if (sMap) {
+ gfree(sMap);
+ }
+#if MULTITHREADED
+ gDestroyMutex(&mutex);
+#endif
+}
+
+void CharCodeToUnicode::incRefCnt() {
+#if MULTITHREADED
+ gLockMutex(&mutex);
+#endif
+ ++refCnt;
+#if MULTITHREADED
+ gUnlockMutex(&mutex);
+#endif
+}
+
+void CharCodeToUnicode::decRefCnt() {
+ GBool done;
+
+#if MULTITHREADED
+ gLockMutex(&mutex);
+#endif
+ done = --refCnt == 0;
+#if MULTITHREADED
+ gUnlockMutex(&mutex);
+#endif
+ if (done) {
+ delete this;
+ }
+}
+
+GBool CharCodeToUnicode::match(GString *tagA) {
+ return tag && !tag->cmp(tagA);
+}
+
+void CharCodeToUnicode::setMapping(CharCode c, Unicode *u, int len) {
+ int i, j;
+
+ if (len == 1) {
+ map[c] = u[0];
+ } else {
+ for (i = 0; i < sMapLen; ++i) {
+ if (sMap[i].c == c) {
+ break;
+ }
+ }
+ if (i == sMapLen) {
+ if (sMapLen == sMapSize) {
+ sMapSize += 8;
+ sMap = (CharCodeToUnicodeString *)
+ greallocn(sMap, sMapSize, sizeof(CharCodeToUnicodeString));
+ }
+ ++sMapLen;
+ }
+ map[c] = 0;
+ sMap[i].c = c;
+ sMap[i].len = len;
+ for (j = 0; j < len && j < maxUnicodeString; ++j) {
+ sMap[i].u[j] = u[j];
+ }
+ }
+}
+
+int CharCodeToUnicode::mapToUnicode(CharCode c, Unicode *u, int size) {
+ int i, j;
+
+ if (c >= mapLen) {
+ return 0;
+ }
+ if (map[c]) {
+ u[0] = map[c];
+ return 1;
+ }
+ for (i = 0; i < sMapLen; ++i) {
+ if (sMap[i].c == c) {
+ for (j = 0; j < sMap[i].len && j < size; ++j) {
+ u[j] = sMap[i].u[j];
+ }
+ return j;
+ }
+ }
+ return 0;
+}
+
+//------------------------------------------------------------------------
+
+CharCodeToUnicodeCache::CharCodeToUnicodeCache(int sizeA) {
+ int i;
+
+ size = sizeA;
+ cache = (CharCodeToUnicode **)gmallocn(size, sizeof(CharCodeToUnicode *));
+ for (i = 0; i < size; ++i) {
+ cache[i] = NULL;
+ }
+}
+
+CharCodeToUnicodeCache::~CharCodeToUnicodeCache() {
+ int i;
+
+ for (i = 0; i < size; ++i) {
+ if (cache[i]) {
+ cache[i]->decRefCnt();
+ }
+ }
+ gfree(cache);
+}
+
+CharCodeToUnicode *CharCodeToUnicodeCache::getCharCodeToUnicode(GString *tag) {
+ CharCodeToUnicode *ctu;
+ int i, j;
+
+ if (cache[0] && cache[0]->match(tag)) {
+ cache[0]->incRefCnt();
+ return cache[0];
+ }
+ for (i = 1; i < size; ++i) {
+ if (cache[i] && cache[i]->match(tag)) {
+ ctu = cache[i];
+ for (j = i; j >= 1; --j) {
+ cache[j] = cache[j - 1];
+ }
+ cache[0] = ctu;
+ ctu->incRefCnt();
+ return ctu;
+ }
+ }
+ return NULL;
+}
+
+void CharCodeToUnicodeCache::add(CharCodeToUnicode *ctu) {
+ int i;
+
+ if (cache[size - 1]) {
+ cache[size - 1]->decRefCnt();
+ }
+ for (i = size - 1; i >= 1; --i) {
+ cache[i] = cache[i - 1];
+ }
+ cache[0] = ctu;
+ ctu->incRefCnt();
+}
--- /dev/null
+//========================================================================
+//
+// CharCodeToUnicode.h
+//
+// Mapping from character codes to Unicode.
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef CHARCODETOUNICODE_H
+#define CHARCODETOUNICODE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "CharTypes.h"
+
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+
+struct CharCodeToUnicodeString;
+
+//------------------------------------------------------------------------
+
+class CharCodeToUnicode {
+public:
+
+ // Read the CID-to-Unicode mapping for <collection> from the file
+ // specified by <fileName>. Sets the initial reference count to 1.
+ // Returns NULL on failure.
+ static CharCodeToUnicode *parseCIDToUnicode(GString *fileName,
+ GString *collection);
+
+ // Create a Unicode-to-Unicode mapping from the file specified by
+ // <fileName>. Sets the initial reference count to 1. Returns NULL
+ // on failure.
+ static CharCodeToUnicode *parseUnicodeToUnicode(GString *fileName);
+
+ // Create the CharCode-to-Unicode mapping for an 8-bit font.
+ // <toUnicode> is an array of 256 Unicode indexes. Sets the initial
+ // reference count to 1.
+ static CharCodeToUnicode *make8BitToUnicode(Unicode *toUnicode);
+
+ // Parse a ToUnicode CMap for an 8- or 16-bit font.
+ static CharCodeToUnicode *parseCMap(GString *buf, int nBits);
+
+ // Parse a ToUnicode CMap for an 8- or 16-bit font, merging it into
+ // <this>.
+ void mergeCMap(GString *buf, int nBits);
+
+ ~CharCodeToUnicode();
+
+ void incRefCnt();
+ void decRefCnt();
+
+ // Return true if this mapping matches the specified <tagA>.
+ GBool match(GString *tagA);
+
+ // Set the mapping for <c>.
+ void setMapping(CharCode c, Unicode *u, int len);
+
+ // Map a CharCode to Unicode.
+ int mapToUnicode(CharCode c, Unicode *u, int size);
+
+ // Return the mapping's length, i.e., one more than the max char
+ // code supported by the mapping.
+ CharCode getLength() { return mapLen; }
+
+private:
+
+ void parseCMap1(int (*getCharFunc)(void *), void *data, int nBits);
+ void addMapping(CharCode code, char *uStr, int n, int offset);
+ CharCodeToUnicode(GString *tagA);
+ CharCodeToUnicode(GString *tagA, Unicode *mapA,
+ CharCode mapLenA, GBool copyMap,
+ CharCodeToUnicodeString *sMapA,
+ int sMapLenA, int sMapSizeA);
+
+ GString *tag;
+ Unicode *map;
+ CharCode mapLen;
+ CharCodeToUnicodeString *sMap;
+ int sMapLen, sMapSize;
+ int refCnt;
+#if MULTITHREADED
+ GMutex mutex;
+#endif
+};
+
+//------------------------------------------------------------------------
+
+class CharCodeToUnicodeCache {
+public:
+
+ CharCodeToUnicodeCache(int sizeA);
+ ~CharCodeToUnicodeCache();
+
+ // Get the CharCodeToUnicode object for <tag>. Increments its
+ // reference count; there will be one reference for the cache plus
+ // one for the caller of this function. Returns NULL on failure.
+ CharCodeToUnicode *getCharCodeToUnicode(GString *tag);
+
+ // Insert <ctu> into the cache, in the most-recently-used position.
+ void add(CharCodeToUnicode *ctu);
+
+private:
+
+ CharCodeToUnicode **cache;
+ int size;
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// CharTypes.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef CHARTYPES_H
+#define CHARTYPES_H
+
+// Unicode character.
+typedef unsigned int Unicode;
+
+// Character ID for CID character collections.
+typedef unsigned int CID;
+
+// This is large enough to hold any of the following:
+// - 8-bit char code
+// - 16-bit CID
+// - Unicode
+typedef unsigned int CharCode;
+
+#endif
--- /dev/null
+//========================================================================
+//
+// Decrypt.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include "gmem.h"
+#include "Decrypt.h"
+
+static void rc4InitKey(Guchar *key, int keyLen, Guchar *state);
+static Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c);
+static void md5(Guchar *msg, int msgLen, Guchar *digest);
+
+static Guchar passwordPad[32] = {
+ 0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41,
+ 0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08,
+ 0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80,
+ 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a
+};
+
+//------------------------------------------------------------------------
+// Decrypt
+//------------------------------------------------------------------------
+
+Decrypt::Decrypt(Guchar *fileKey, int keyLength, int objNum, int objGen) {
+ int i;
+
+ // construct object key
+ for (i = 0; i < keyLength; ++i) {
+ objKey[i] = fileKey[i];
+ }
+ objKey[keyLength] = objNum & 0xff;
+ objKey[keyLength + 1] = (objNum >> 8) & 0xff;
+ objKey[keyLength + 2] = (objNum >> 16) & 0xff;
+ objKey[keyLength + 3] = objGen & 0xff;
+ objKey[keyLength + 4] = (objGen >> 8) & 0xff;
+ md5(objKey, keyLength + 5, objKey);
+
+ // set up for decryption
+ x = y = 0;
+ if ((objKeyLength = keyLength + 5) > 16) {
+ objKeyLength = 16;
+ }
+ rc4InitKey(objKey, objKeyLength, state);
+}
+
+void Decrypt::reset() {
+ x = y = 0;
+ rc4InitKey(objKey, objKeyLength, state);
+}
+
+Guchar Decrypt::decryptByte(Guchar c) {
+ return rc4DecryptByte(state, &x, &y, c);
+}
+
+GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
+ GString *ownerKey, GString *userKey,
+ int permissions, GString *fileID,
+ GString *ownerPassword, GString *userPassword,
+ Guchar *fileKey, GBool encryptMetadata,
+ GBool *ownerPasswordOk) {
+ Guchar test[32], test2[32];
+ GString *userPassword2;
+ Guchar fState[256];
+ Guchar tmpKey[16];
+ Guchar fx, fy;
+ int len, i, j;
+
+ // try using the supplied owner password to generate the user password
+ *ownerPasswordOk = gFalse;
+ if (ownerPassword) {
+ len = ownerPassword->getLength();
+ if (len < 32) {
+ memcpy(test, ownerPassword->getCString(), len);
+ memcpy(test + len, passwordPad, 32 - len);
+ } else {
+ memcpy(test, ownerPassword->getCString(), 32);
+ }
+ md5(test, 32, test);
+ if (encRevision == 3) {
+ for (i = 0; i < 50; ++i) {
+ md5(test, 16, test);
+ }
+ }
+ if (encRevision == 2) {
+ rc4InitKey(test, keyLength, fState);
+ fx = fy = 0;
+ for (i = 0; i < 32; ++i) {
+ test2[i] = rc4DecryptByte(fState, &fx, &fy, ownerKey->getChar(i));
+ }
+ } else {
+ memcpy(test2, ownerKey->getCString(), 32);
+ for (i = 19; i >= 0; --i) {
+ for (j = 0; j < keyLength; ++j) {
+ tmpKey[j] = test[j] ^ i;
+ }
+ rc4InitKey(tmpKey, keyLength, fState);
+ fx = fy = 0;
+ for (j = 0; j < 32; ++j) {
+ test2[j] = rc4DecryptByte(fState, &fx, &fy, test2[j]);
+ }
+ }
+ }
+ userPassword2 = new GString((char *)test2, 32);
+ if (makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey,
+ permissions, fileID, userPassword2, fileKey,
+ encryptMetadata)) {
+ *ownerPasswordOk = gTrue;
+ delete userPassword2;
+ return gTrue;
+ }
+ delete userPassword2;
+ }
+
+ // try using the supplied user password
+ return makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey,
+ permissions, fileID, userPassword, fileKey,
+ encryptMetadata);
+}
+
+GBool Decrypt::makeFileKey2(int encVersion, int encRevision, int keyLength,
+ GString *ownerKey, GString *userKey,
+ int permissions, GString *fileID,
+ GString *userPassword, Guchar *fileKey,
+ GBool encryptMetadata) {
+ Guchar *buf;
+ Guchar test[32];
+ Guchar fState[256];
+ Guchar tmpKey[16];
+ Guchar fx, fy;
+ int len, i, j;
+ GBool ok;
+
+ // generate file key
+ buf = (Guchar *)gmalloc(72 + fileID->getLength());
+ if (userPassword) {
+ len = userPassword->getLength();
+ if (len < 32) {
+ memcpy(buf, userPassword->getCString(), len);
+ memcpy(buf + len, passwordPad, 32 - len);
+ } else {
+ memcpy(buf, userPassword->getCString(), 32);
+ }
+ } else {
+ memcpy(buf, passwordPad, 32);
+ }
+ memcpy(buf + 32, ownerKey->getCString(), 32);
+ buf[64] = permissions & 0xff;
+ buf[65] = (permissions >> 8) & 0xff;
+ buf[66] = (permissions >> 16) & 0xff;
+ buf[67] = (permissions >> 24) & 0xff;
+ memcpy(buf + 68, fileID->getCString(), fileID->getLength());
+ len = 68 + fileID->getLength();
+ if (!encryptMetadata) {
+ buf[len++] = 0xff;
+ buf[len++] = 0xff;
+ buf[len++] = 0xff;
+ buf[len++] = 0xff;
+ }
+ md5(buf, len, fileKey);
+ if (encRevision == 3) {
+ for (i = 0; i < 50; ++i) {
+ md5(fileKey, keyLength, fileKey);
+ }
+ }
+
+ // test user password
+ if (encRevision == 2) {
+ rc4InitKey(fileKey, keyLength, fState);
+ fx = fy = 0;
+ for (i = 0; i < 32; ++i) {
+ test[i] = rc4DecryptByte(fState, &fx, &fy, userKey->getChar(i));
+ }
+ ok = memcmp(test, passwordPad, 32) == 0;
+ } else if (encRevision == 3) {
+ memcpy(test, userKey->getCString(), 32);
+ for (i = 19; i >= 0; --i) {
+ for (j = 0; j < keyLength; ++j) {
+ tmpKey[j] = fileKey[j] ^ i;
+ }
+ rc4InitKey(tmpKey, keyLength, fState);
+ fx = fy = 0;
+ for (j = 0; j < 32; ++j) {
+ test[j] = rc4DecryptByte(fState, &fx, &fy, test[j]);
+ }
+ }
+ memcpy(buf, passwordPad, 32);
+ memcpy(buf + 32, fileID->getCString(), fileID->getLength());
+ md5(buf, 32 + fileID->getLength(), buf);
+ ok = memcmp(test, buf, 16) == 0;
+ } else {
+ ok = gFalse;
+ }
+
+ gfree(buf);
+ return ok;
+}
+
+//------------------------------------------------------------------------
+// RC4-compatible decryption
+//------------------------------------------------------------------------
+
+static void rc4InitKey(Guchar *key, int keyLen, Guchar *state) {
+ Guchar index1, index2;
+ Guchar t;
+ int i;
+
+ for (i = 0; i < 256; ++i)
+ state[i] = i;
+ index1 = index2 = 0;
+ for (i = 0; i < 256; ++i) {
+ index2 = (key[index1] + state[i] + index2) % 256;
+ t = state[i];
+ state[i] = state[index2];
+ state[index2] = t;
+ index1 = (index1 + 1) % keyLen;
+ }
+}
+
+static Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c) {
+ Guchar x1, y1, tx, ty;
+
+ x1 = *x = (*x + 1) % 256;
+ y1 = *y = (state[*x] + *y) % 256;
+ tx = state[x1];
+ ty = state[y1];
+ state[x1] = ty;
+ state[y1] = tx;
+ return c ^ state[(tx + ty) % 256];
+}
+
+//------------------------------------------------------------------------
+// MD5 message digest
+//------------------------------------------------------------------------
+
+// this works around a bug in older Sun compilers
+static inline Gulong rotateLeft(Gulong x, int r) {
+ x &= 0xffffffff;
+ return ((x << r) | (x >> (32 - r))) & 0xffffffff;
+}
+
+static inline Gulong md5Round1(Gulong a, Gulong b, Gulong c, Gulong d,
+ Gulong Xk, Gulong s, Gulong Ti) {
+ return b + rotateLeft((a + ((b & c) | (~b & d)) + Xk + Ti), s);
+}
+
+static inline Gulong md5Round2(Gulong a, Gulong b, Gulong c, Gulong d,
+ Gulong Xk, Gulong s, Gulong Ti) {
+ return b + rotateLeft((a + ((b & d) | (c & ~d)) + Xk + Ti), s);
+}
+
+static inline Gulong md5Round3(Gulong a, Gulong b, Gulong c, Gulong d,
+ Gulong Xk, Gulong s, Gulong Ti) {
+ return b + rotateLeft((a + (b ^ c ^ d) + Xk + Ti), s);
+}
+
+static inline Gulong md5Round4(Gulong a, Gulong b, Gulong c, Gulong d,
+ Gulong Xk, Gulong s, Gulong Ti) {
+ return b + rotateLeft((a + (c ^ (b | ~d)) + Xk + Ti), s);
+}
+
+static void md5(Guchar *msg, int msgLen, Guchar *digest) {
+ Gulong x[16];
+ Gulong a, b, c, d, aa, bb, cc, dd;
+ int n64;
+ int i, j, k;
+
+ // compute number of 64-byte blocks
+ // (length + pad byte (0x80) + 8 bytes for length)
+ n64 = (msgLen + 1 + 8 + 63) / 64;
+
+ // initialize a, b, c, d
+ a = 0x67452301;
+ b = 0xefcdab89;
+ c = 0x98badcfe;
+ d = 0x10325476;
+
+ // loop through blocks
+ k = 0;
+ for (i = 0; i < n64; ++i) {
+
+ // grab a 64-byte block
+ for (j = 0; j < 16 && k < msgLen - 3; ++j, k += 4)
+ x[j] = (((((msg[k+3] << 8) + msg[k+2]) << 8) + msg[k+1]) << 8) + msg[k];
+ if (i == n64 - 1) {
+ if (k == msgLen - 3)
+ x[j] = 0x80000000 + (((msg[k+2] << 8) + msg[k+1]) << 8) + msg[k];
+ else if (k == msgLen - 2)
+ x[j] = 0x800000 + (msg[k+1] << 8) + msg[k];
+ else if (k == msgLen - 1)
+ x[j] = 0x8000 + msg[k];
+ else
+ x[j] = 0x80;
+ ++j;
+ while (j < 16)
+ x[j++] = 0;
+ x[14] = msgLen << 3;
+ }
+
+ // save a, b, c, d
+ aa = a;
+ bb = b;
+ cc = c;
+ dd = d;
+
+ // round 1
+ a = md5Round1(a, b, c, d, x[0], 7, 0xd76aa478);
+ d = md5Round1(d, a, b, c, x[1], 12, 0xe8c7b756);
+ c = md5Round1(c, d, a, b, x[2], 17, 0x242070db);
+ b = md5Round1(b, c, d, a, x[3], 22, 0xc1bdceee);
+ a = md5Round1(a, b, c, d, x[4], 7, 0xf57c0faf);
+ d = md5Round1(d, a, b, c, x[5], 12, 0x4787c62a);
+ c = md5Round1(c, d, a, b, x[6], 17, 0xa8304613);
+ b = md5Round1(b, c, d, a, x[7], 22, 0xfd469501);
+ a = md5Round1(a, b, c, d, x[8], 7, 0x698098d8);
+ d = md5Round1(d, a, b, c, x[9], 12, 0x8b44f7af);
+ c = md5Round1(c, d, a, b, x[10], 17, 0xffff5bb1);
+ b = md5Round1(b, c, d, a, x[11], 22, 0x895cd7be);
+ a = md5Round1(a, b, c, d, x[12], 7, 0x6b901122);
+ d = md5Round1(d, a, b, c, x[13], 12, 0xfd987193);
+ c = md5Round1(c, d, a, b, x[14], 17, 0xa679438e);
+ b = md5Round1(b, c, d, a, x[15], 22, 0x49b40821);
+
+ // round 2
+ a = md5Round2(a, b, c, d, x[1], 5, 0xf61e2562);
+ d = md5Round2(d, a, b, c, x[6], 9, 0xc040b340);
+ c = md5Round2(c, d, a, b, x[11], 14, 0x265e5a51);
+ b = md5Round2(b, c, d, a, x[0], 20, 0xe9b6c7aa);
+ a = md5Round2(a, b, c, d, x[5], 5, 0xd62f105d);
+ d = md5Round2(d, a, b, c, x[10], 9, 0x02441453);
+ c = md5Round2(c, d, a, b, x[15], 14, 0xd8a1e681);
+ b = md5Round2(b, c, d, a, x[4], 20, 0xe7d3fbc8);
+ a = md5Round2(a, b, c, d, x[9], 5, 0x21e1cde6);
+ d = md5Round2(d, a, b, c, x[14], 9, 0xc33707d6);
+ c = md5Round2(c, d, a, b, x[3], 14, 0xf4d50d87);
+ b = md5Round2(b, c, d, a, x[8], 20, 0x455a14ed);
+ a = md5Round2(a, b, c, d, x[13], 5, 0xa9e3e905);
+ d = md5Round2(d, a, b, c, x[2], 9, 0xfcefa3f8);
+ c = md5Round2(c, d, a, b, x[7], 14, 0x676f02d9);
+ b = md5Round2(b, c, d, a, x[12], 20, 0x8d2a4c8a);
+
+ // round 3
+ a = md5Round3(a, b, c, d, x[5], 4, 0xfffa3942);
+ d = md5Round3(d, a, b, c, x[8], 11, 0x8771f681);
+ c = md5Round3(c, d, a, b, x[11], 16, 0x6d9d6122);
+ b = md5Round3(b, c, d, a, x[14], 23, 0xfde5380c);
+ a = md5Round3(a, b, c, d, x[1], 4, 0xa4beea44);
+ d = md5Round3(d, a, b, c, x[4], 11, 0x4bdecfa9);
+ c = md5Round3(c, d, a, b, x[7], 16, 0xf6bb4b60);
+ b = md5Round3(b, c, d, a, x[10], 23, 0xbebfbc70);
+ a = md5Round3(a, b, c, d, x[13], 4, 0x289b7ec6);
+ d = md5Round3(d, a, b, c, x[0], 11, 0xeaa127fa);
+ c = md5Round3(c, d, a, b, x[3], 16, 0xd4ef3085);
+ b = md5Round3(b, c, d, a, x[6], 23, 0x04881d05);
+ a = md5Round3(a, b, c, d, x[9], 4, 0xd9d4d039);
+ d = md5Round3(d, a, b, c, x[12], 11, 0xe6db99e5);
+ c = md5Round3(c, d, a, b, x[15], 16, 0x1fa27cf8);
+ b = md5Round3(b, c, d, a, x[2], 23, 0xc4ac5665);
+
+ // round 4
+ a = md5Round4(a, b, c, d, x[0], 6, 0xf4292244);
+ d = md5Round4(d, a, b, c, x[7], 10, 0x432aff97);
+ c = md5Round4(c, d, a, b, x[14], 15, 0xab9423a7);
+ b = md5Round4(b, c, d, a, x[5], 21, 0xfc93a039);
+ a = md5Round4(a, b, c, d, x[12], 6, 0x655b59c3);
+ d = md5Round4(d, a, b, c, x[3], 10, 0x8f0ccc92);
+ c = md5Round4(c, d, a, b, x[10], 15, 0xffeff47d);
+ b = md5Round4(b, c, d, a, x[1], 21, 0x85845dd1);
+ a = md5Round4(a, b, c, d, x[8], 6, 0x6fa87e4f);
+ d = md5Round4(d, a, b, c, x[15], 10, 0xfe2ce6e0);
+ c = md5Round4(c, d, a, b, x[6], 15, 0xa3014314);
+ b = md5Round4(b, c, d, a, x[13], 21, 0x4e0811a1);
+ a = md5Round4(a, b, c, d, x[4], 6, 0xf7537e82);
+ d = md5Round4(d, a, b, c, x[11], 10, 0xbd3af235);
+ c = md5Round4(c, d, a, b, x[2], 15, 0x2ad7d2bb);
+ b = md5Round4(b, c, d, a, x[9], 21, 0xeb86d391);
+
+ // increment a, b, c, d
+ a += aa;
+ b += bb;
+ c += cc;
+ d += dd;
+ }
+
+ // break digest into bytes
+ digest[0] = (Guchar)(a & 0xff);
+ digest[1] = (Guchar)((a >>= 8) & 0xff);
+ digest[2] = (Guchar)((a >>= 8) & 0xff);
+ digest[3] = (Guchar)((a >>= 8) & 0xff);
+ digest[4] = (Guchar)(b & 0xff);
+ digest[5] = (Guchar)((b >>= 8) & 0xff);
+ digest[6] = (Guchar)((b >>= 8) & 0xff);
+ digest[7] = (Guchar)((b >>= 8) & 0xff);
+ digest[8] = (Guchar)(c & 0xff);
+ digest[9] = (Guchar)((c >>= 8) & 0xff);
+ digest[10] = (Guchar)((c >>= 8) & 0xff);
+ digest[11] = (Guchar)((c >>= 8) & 0xff);
+ digest[12] = (Guchar)(d & 0xff);
+ digest[13] = (Guchar)((d >>= 8) & 0xff);
+ digest[14] = (Guchar)((d >>= 8) & 0xff);
+ digest[15] = (Guchar)((d >>= 8) & 0xff);
+}
--- /dev/null
+//========================================================================
+//
+// Decrypt.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef DECRYPT_H
+#define DECRYPT_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "GString.h"
+
+//------------------------------------------------------------------------
+// Decrypt
+//------------------------------------------------------------------------
+
+class Decrypt {
+public:
+
+ // Initialize the decryptor object.
+ Decrypt(Guchar *fileKey, int keyLength, int objNum, int objGen);
+
+ // Reset decryption.
+ void reset();
+
+ // Decrypt one byte.
+ Guchar decryptByte(Guchar c);
+
+ // Generate a file key. The <fileKey> buffer must have space for at
+ // least 16 bytes. Checks <ownerPassword> and then <userPassword>
+ // and returns true if either is correct. Sets <ownerPasswordOk> if
+ // the owner password was correct. Either or both of the passwords
+ // may be NULL, which is treated as an empty string.
+ static GBool makeFileKey(int encVersion, int encRevision, int keyLength,
+ GString *ownerKey, GString *userKey,
+ int permissions, GString *fileID,
+ GString *ownerPassword, GString *userPassword,
+ Guchar *fileKey, GBool encryptMetadata,
+ GBool *ownerPasswordOk);
+
+private:
+
+ static GBool makeFileKey2(int encVersion, int encRevision, int keyLength,
+ GString *ownerKey, GString *userKey,
+ int permissions, GString *fileID,
+ GString *userPassword, Guchar *fileKey,
+ GBool encryptMetadata);
+
+ int objKeyLength;
+ Guchar objKey[21];
+ Guchar state[256];
+ Guchar x, y;
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// Dict.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stddef.h>
+#include <string.h>
+#include "gmem.h"
+#include "Object.h"
+#include "XRef.h"
+#include "Dict.h"
+
+//------------------------------------------------------------------------
+// Dict
+//------------------------------------------------------------------------
+
+Dict::Dict(XRef *xrefA) {
+ xref = xrefA;
+ entries = NULL;
+ size = length = 0;
+ ref = 1;
+}
+
+Dict::~Dict() {
+ int i;
+
+ for (i = 0; i < length; ++i) {
+ gfree(entries[i].key);
+ entries[i].val.free();
+ }
+ gfree(entries);
+}
+
+void Dict::add(char *key, Object *val) {
+ if (length == size) {
+ if (length == 0) {
+ size = 8;
+ } else {
+ size *= 2;
+ }
+ entries = (DictEntry *)greallocn(entries, size, sizeof(DictEntry));
+ }
+ entries[length].key = key;
+ entries[length].val = *val;
+ ++length;
+}
+
+inline DictEntry *Dict::find(char *key) {
+ int i;
+
+ for (i = 0; i < length; ++i) {
+ if (!strcmp(key, entries[i].key))
+ return &entries[i];
+ }
+ return NULL;
+}
+
+GBool Dict::is(char *type) {
+ DictEntry *e;
+
+ return (e = find("Type")) && e->val.isName(type);
+}
+
+Object *Dict::lookup(char *key, Object *obj) {
+ DictEntry *e;
+
+ return (e = find(key)) ? e->val.fetch(xref, obj) : obj->initNull();
+}
+
+Object *Dict::lookupNF(char *key, Object *obj) {
+ DictEntry *e;
+
+ return (e = find(key)) ? e->val.copy(obj) : obj->initNull();
+}
+
+char *Dict::getKey(int i) {
+ return entries[i].key;
+}
+
+Object *Dict::getVal(int i, Object *obj) {
+ return entries[i].val.fetch(xref, obj);
+}
+
+Object *Dict::getValNF(int i, Object *obj) {
+ return entries[i].val.copy(obj);
+}
--- /dev/null
+//========================================================================
+//
+// Dict.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef DICT_H
+#define DICT_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "Object.h"
+
+//------------------------------------------------------------------------
+// Dict
+//------------------------------------------------------------------------
+
+struct DictEntry {
+ char *key;
+ Object val;
+};
+
+class Dict {
+public:
+
+ // Constructor.
+ Dict(XRef *xrefA);
+
+ // Destructor.
+ ~Dict();
+
+ // Reference counting.
+ int incRef() { return ++ref; }
+ int decRef() { return --ref; }
+
+ // Get number of entries.
+ int getLength() { return length; }
+
+ // Add an entry. NB: does not copy key.
+ void add(char *key, Object *val);
+
+ // Check if dictionary is of specified type.
+ GBool is(char *type);
+
+ // Look up an entry and return the value. Returns a null object
+ // if <key> is not in the dictionary.
+ Object *lookup(char *key, Object *obj);
+ Object *lookupNF(char *key, Object *obj);
+
+ // Iterative accessors.
+ char *getKey(int i);
+ Object *getVal(int i, Object *obj);
+ Object *getValNF(int i, Object *obj);
+
+ // Set the xref pointer. This is only used in one special case: the
+ // trailer dictionary, which is read before the xref table is
+ // parsed.
+ void setXRef(XRef *xrefA) { xref = xrefA; }
+
+private:
+
+ XRef *xref; // the xref table for this PDF file
+ DictEntry *entries; // array of entries
+ int size; // size of <entries> array
+ int length; // number of entries in dictionary
+ int ref; // reference count
+
+ DictEntry *find(char *key);
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// Error.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include "GlobalParams.h"
+#include "Error.h"
+
+void CDECL error(int pos, char *msg, ...) {
+ va_list args;
+
+ // NB: this can be called before the globalParams object is created
+ if (globalParams && globalParams->getErrQuiet()) {
+ return;
+ }
+ if (pos >= 0) {
+ fprintf(stderr, "Error (%d): ", pos);
+ } else {
+ fprintf(stderr, "Error: ");
+ }
+ va_start(args, msg);
+ vfprintf(stderr, msg, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+}
--- /dev/null
+//========================================================================
+//
+// Error.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef ERROR_H
+#define ERROR_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <stdio.h>
+#include "config.h"
+
+extern void CDECL error(int pos, char *msg, ...);
+
+#endif
--- /dev/null
+//========================================================================
+//
+// ErrorCodes.h
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef ERRORCODES_H
+#define ERRORCODES_H
+
+#define errNone 0 // no error
+
+#define errOpenFile 1 // couldn't open the PDF file
+
+#define errBadCatalog 2 // couldn't read the page catalog
+
+#define errDamaged 3 // PDF file was damaged and couldn't be
+ // repaired
+
+#define errEncrypted 4 // file was encrypted and password was
+ // incorrect or not supplied
+
+#define errHighlightFile 5 // nonexistent or invalid highlight file
+
+#define errBadPrinter 6 // invalid printer
+
+#define errPrinting 7 // error during printing
+
+#define errPermission 8 // PDF file doesn't allow that operation
+
+#define errBadPageNum 9 // invalid page number
+
+#define errFileIO 10 // file I/O error
+
+#endif
--- /dev/null
+//========================================================================
+//
+// 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;
+}
--- /dev/null
+//========================================================================
+//
+// 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
--- /dev/null
+//========================================================================
+//
+// 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
+};
--- /dev/null
+//========================================================================
+//
+// 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
--- /dev/null
+//========================================================================
+//
+// FoFiTrueType.cc
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "gtypes.h"
+#include "gmem.h"
+#include "GString.h"
+#include "GHash.h"
+#include "FoFiTrueType.h"
+
+//
+// Terminology
+// -----------
+//
+// character code = number used as an element of a text string
+//
+// character name = glyph name = name for a particular glyph within a
+// font
+//
+// glyph index = GID = position (within some internal table in the font)
+// where the instructions to draw a particular glyph are
+// stored
+//
+// Type 1 fonts
+// ------------
+//
+// Type 1 fonts contain:
+//
+// Encoding: array of glyph names, maps char codes to glyph names
+//
+// Encoding[charCode] = charName
+//
+// CharStrings: dictionary of instructions, keyed by character names,
+// maps character name to glyph data
+//
+// CharStrings[charName] = glyphData
+//
+// TrueType fonts
+// --------------
+//
+// TrueType fonts contain:
+//
+// 'cmap' table: mapping from character code to glyph index; there may
+// be multiple cmaps in a TrueType font
+//
+// cmap[charCode] = gid
+//
+// 'post' table: mapping from glyph index to glyph name
+//
+// post[gid] = glyphName
+//
+// Type 42 fonts
+// -------------
+//
+// Type 42 fonts contain:
+//
+// Encoding: array of glyph names, maps char codes to glyph names
+//
+// Encoding[charCode] = charName
+//
+// CharStrings: dictionary of glyph indexes, keyed by character names,
+// maps character name to glyph index
+//
+// CharStrings[charName] = gid
+//
+
+//------------------------------------------------------------------------
+
+#define ttcfTag 0x74746366
+
+//------------------------------------------------------------------------
+
+struct TrueTypeTable {
+ Guint tag;
+ Guint checksum;
+ int offset;
+ int origOffset;
+ int len;
+};
+
+struct TrueTypeCmap {
+ int platform;
+ int encoding;
+ int offset;
+ int len;
+ int fmt;
+};
+
+struct TrueTypeLoca {
+ int idx;
+ int origOffset;
+ int newOffset;
+ int len;
+};
+
+#define cmapTag 0x636d6170
+#define glyfTag 0x676c7966
+#define headTag 0x68656164
+#define locaTag 0x6c6f6361
+#define nameTag 0x6e616d65
+#define postTag 0x706f7374
+
+static int cmpTrueTypeLocaOffset(const void *p1, const void *p2) {
+ TrueTypeLoca *loca1 = (TrueTypeLoca *)p1;
+ TrueTypeLoca *loca2 = (TrueTypeLoca *)p2;
+
+ if (loca1->origOffset == loca2->origOffset) {
+ return loca1->idx - loca2->idx;
+ }
+ return loca1->origOffset - loca2->origOffset;
+}
+
+static int cmpTrueTypeLocaIdx(const void *p1, const void *p2) {
+ TrueTypeLoca *loca1 = (TrueTypeLoca *)p1;
+ TrueTypeLoca *loca2 = (TrueTypeLoca *)p2;
+
+ return loca1->idx - loca2->idx;
+}
+
+static int cmpTrueTypeTableTag(const void *p1, const void *p2) {
+ TrueTypeTable *tab1 = (TrueTypeTable *)p1;
+ TrueTypeTable *tab2 = (TrueTypeTable *)p2;
+
+ return (int)tab1->tag - (int)tab2->tag;
+}
+
+//------------------------------------------------------------------------
+
+struct T42Table {
+ char *tag; // 4-byte tag
+ GBool required; // required by the TrueType spec?
+};
+
+// TrueType tables to be embedded in Type 42 fonts.
+// NB: the table names must be in alphabetical order here.
+#define nT42Tables 11
+static T42Table t42Tables[nT42Tables] = {
+ { "cvt ", gTrue },
+ { "fpgm", gTrue },
+ { "glyf", gTrue },
+ { "head", gTrue },
+ { "hhea", gTrue },
+ { "hmtx", gTrue },
+ { "loca", gTrue },
+ { "maxp", gTrue },
+ { "prep", gTrue },
+ { "vhea", gFalse },
+ { "vmtx", gFalse }
+};
+#define t42HeadTable 3
+#define t42LocaTable 6
+#define t42GlyfTable 2
+#define t42VheaTable 9
+#define t42VmtxTable 10
+
+//------------------------------------------------------------------------
+
+// Glyph names in some arbitrary standard order that Apple uses for
+// their TrueType fonts.
+static char *macGlyphNames[258] = {
+ ".notdef", "null", "CR", "space",
+ "exclam", "quotedbl", "numbersign", "dollar",
+ "percent", "ampersand", "quotesingle", "parenleft",
+ "parenright", "asterisk", "plus", "comma",
+ "hyphen", "period", "slash", "zero",
+ "one", "two", "three", "four",
+ "five", "six", "seven", "eight",
+ "nine", "colon", "semicolon", "less",
+ "equal", "greater", "question", "at",
+ "A", "B", "C", "D",
+ "E", "F", "G", "H",
+ "I", "J", "K", "L",
+ "M", "N", "O", "P",
+ "Q", "R", "S", "T",
+ "U", "V", "W", "X",
+ "Y", "Z", "bracketleft", "backslash",
+ "bracketright", "asciicircum", "underscore", "grave",
+ "a", "b", "c", "d",
+ "e", "f", "g", "h",
+ "i", "j", "k", "l",
+ "m", "n", "o", "p",
+ "q", "r", "s", "t",
+ "u", "v", "w", "x",
+ "y", "z", "braceleft", "bar",
+ "braceright", "asciitilde", "Adieresis", "Aring",
+ "Ccedilla", "Eacute", "Ntilde", "Odieresis",
+ "Udieresis", "aacute", "agrave", "acircumflex",
+ "adieresis", "atilde", "aring", "ccedilla",
+ "eacute", "egrave", "ecircumflex", "edieresis",
+ "iacute", "igrave", "icircumflex", "idieresis",
+ "ntilde", "oacute", "ograve", "ocircumflex",
+ "odieresis", "otilde", "uacute", "ugrave",
+ "ucircumflex", "udieresis", "dagger", "degree",
+ "cent", "sterling", "section", "bullet",
+ "paragraph", "germandbls", "registered", "copyright",
+ "trademark", "acute", "dieresis", "notequal",
+ "AE", "Oslash", "infinity", "plusminus",
+ "lessequal", "greaterequal", "yen", "mu1",
+ "partialdiff", "summation", "product", "pi",
+ "integral", "ordfeminine", "ordmasculine", "Ohm",
+ "ae", "oslash", "questiondown", "exclamdown",
+ "logicalnot", "radical", "florin", "approxequal",
+ "increment", "guillemotleft", "guillemotright", "ellipsis",
+ "nbspace", "Agrave", "Atilde", "Otilde",
+ "OE", "oe", "endash", "emdash",
+ "quotedblleft", "quotedblright", "quoteleft", "quoteright",
+ "divide", "lozenge", "ydieresis", "Ydieresis",
+ "fraction", "currency", "guilsinglleft", "guilsinglright",
+ "fi", "fl", "daggerdbl", "periodcentered",
+ "quotesinglbase", "quotedblbase", "perthousand", "Acircumflex",
+ "Ecircumflex", "Aacute", "Edieresis", "Egrave",
+ "Iacute", "Icircumflex", "Idieresis", "Igrave",
+ "Oacute", "Ocircumflex", "applelogo", "Ograve",
+ "Uacute", "Ucircumflex", "Ugrave", "dotlessi",
+ "circumflex", "tilde", "overscore", "breve",
+ "dotaccent", "ring", "cedilla", "hungarumlaut",
+ "ogonek", "caron", "Lslash", "lslash",
+ "Scaron", "scaron", "Zcaron", "zcaron",
+ "brokenbar", "Eth", "eth", "Yacute",
+ "yacute", "Thorn", "thorn", "minus",
+ "multiply", "onesuperior", "twosuperior", "threesuperior",
+ "onehalf", "onequarter", "threequarters", "franc",
+ "Gbreve", "gbreve", "Idot", "Scedilla",
+ "scedilla", "Cacute", "cacute", "Ccaron",
+ "ccaron", "dmacron"
+};
+
+//------------------------------------------------------------------------
+// FoFiTrueType
+//------------------------------------------------------------------------
+
+FoFiTrueType *FoFiTrueType::make(char *fileA, int lenA) {
+ FoFiTrueType *ff;
+
+ ff = new FoFiTrueType(fileA, lenA, gFalse);
+ if (!ff->parsedOk) {
+ delete ff;
+ return NULL;
+ }
+ return ff;
+}
+
+FoFiTrueType *FoFiTrueType::load(char *fileName) {
+ FoFiTrueType *ff;
+ char *fileA;
+ int lenA;
+
+ if (!(fileA = FoFiBase::readFile(fileName, &lenA))) {
+ return NULL;
+ }
+ ff = new FoFiTrueType(fileA, lenA, gTrue);
+ if (!ff->parsedOk) {
+ delete ff;
+ return NULL;
+ }
+ return ff;
+}
+
+FoFiTrueType::FoFiTrueType(char *fileA, int lenA, GBool freeFileDataA):
+ FoFiBase(fileA, lenA, freeFileDataA)
+{
+ tables = NULL;
+ nTables = 0;
+ cmaps = NULL;
+ nCmaps = 0;
+ nameToGID = NULL;
+ parsedOk = gFalse;
+
+ parse();
+}
+
+FoFiTrueType::~FoFiTrueType() {
+ gfree(tables);
+ gfree(cmaps);
+ if (nameToGID) {
+ delete nameToGID;
+ }
+}
+
+int FoFiTrueType::getNumCmaps() {
+ return nCmaps;
+}
+
+int FoFiTrueType::getCmapPlatform(int i) {
+ return cmaps[i].platform;
+}
+
+int FoFiTrueType::getCmapEncoding(int i) {
+ return cmaps[i].encoding;
+}
+
+int FoFiTrueType::findCmap(int platform, int encoding) {
+ int i;
+
+ for (i = 0; i < nCmaps; ++i) {
+ if (cmaps[i].platform == platform && cmaps[i].encoding == encoding) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+Gushort FoFiTrueType::mapCodeToGID(int i, int c) {
+ Gushort gid;
+ int segCnt, segEnd, segStart, segDelta, segOffset;
+ int cmapFirst, cmapLen;
+ int pos, a, b, m;
+ GBool ok;
+
+ if (i < 0 || i >= nCmaps) {
+ return 0;
+ }
+ ok = gTrue;
+ pos = cmaps[i].offset;
+ switch (cmaps[i].fmt) {
+ case 0:
+ if (c < 0 || c >= cmaps[i].len - 6) {
+ return 0;
+ }
+ gid = getU8(cmaps[i].offset + 6 + c, &ok);
+ break;
+ case 4:
+ segCnt = getU16BE(pos + 6, &ok) / 2;
+ a = -1;
+ b = segCnt - 1;
+ segEnd = getU16BE(pos + 14 + 2*b, &ok);
+ if (c > segEnd) {
+ // malformed font -- the TrueType spec requires the last segEnd
+ // to be 0xffff
+ return 0;
+ }
+ // invariant: seg[a].end < code <= seg[b].end
+ while (b - a > 1 && ok) {
+ m = (a + b) / 2;
+ segEnd = getU16BE(pos + 14 + 2*m, &ok);
+ if (segEnd < c) {
+ a = m;
+ } else {
+ b = m;
+ }
+ }
+ segStart = getU16BE(pos + 16 + 2*segCnt + 2*b, &ok);
+ segDelta = getU16BE(pos + 16 + 4*segCnt + 2*b, &ok);
+ segOffset = getU16BE(pos + 16 + 6*segCnt + 2*b, &ok);
+ if (c < segStart) {
+ return 0;
+ }
+ if (segOffset == 0) {
+ gid = (c + segDelta) & 0xffff;
+ } else {
+ gid = getU16BE(pos + 16 + 6*segCnt + 2*b +
+ segOffset + 2 * (c - segStart), &ok);
+ if (gid != 0) {
+ gid = (gid + segDelta) & 0xffff;
+ }
+ }
+ break;
+ case 6:
+ cmapFirst = getU16BE(pos + 6, &ok);
+ cmapLen = getU16BE(pos + 8, &ok);
+ if (c < cmapFirst || c >= cmapFirst + cmapLen) {
+ return 0;
+ }
+ gid = getU16BE(pos + 10 + 2 * (c - cmapFirst), &ok);
+ break;
+ default:
+ return 0;
+ }
+ if (!ok) {
+ return 0;
+ }
+ return gid;
+}
+
+int FoFiTrueType::mapNameToGID(char *name) {
+ if (!nameToGID) {
+ return 0;
+ }
+ return nameToGID->lookupInt(name);
+}
+
+int FoFiTrueType::getEmbeddingRights() {
+ int i, fsType;
+ GBool ok;
+
+ if ((i = seekTable("OS/2")) < 0) {
+ return 4;
+ }
+ ok = gTrue;
+ fsType = getU16BE(tables[i].offset + 8, &ok);
+ if (!ok) {
+ return 4;
+ }
+ if (fsType & 0x0008) {
+ return 2;
+ }
+ if (fsType & 0x0004) {
+ return 1;
+ }
+ if (fsType & 0x0002) {
+ return 0;
+ }
+ return 3;
+}
+
+void FoFiTrueType::convertToType42(char *psName, char **encoding,
+ Gushort *codeToGID,
+ FoFiOutputFunc outputFunc,
+ void *outputStream) {
+ char buf[512];
+ GBool ok;
+
+ // write the header
+ ok = gTrue;
+ sprintf(buf, "%%!PS-TrueTypeFont-%g\n", (double)getS32BE(0, &ok) / 65536.0);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+
+ // begin the font dictionary
+ (*outputFunc)(outputStream, "10 dict begin\n", 14);
+ (*outputFunc)(outputStream, "/FontName /", 11);
+ (*outputFunc)(outputStream, psName, strlen(psName));
+ (*outputFunc)(outputStream, " def\n", 5);
+ (*outputFunc)(outputStream, "/FontType 42 def\n", 17);
+ (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+ sprintf(buf, "/FontBBox [%d %d %d %d] def\n",
+ bbox[0], bbox[1], bbox[2], bbox[3]);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream, "/PaintType 0 def\n", 17);
+
+ // write the guts of the dictionary
+ cvtEncoding(encoding, outputFunc, outputStream);
+ cvtCharStrings(encoding, codeToGID, outputFunc, outputStream);
+ cvtSfnts(outputFunc, outputStream, NULL, gFalse);
+
+ // end the dictionary and define the font
+ (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40);
+}
+
+void FoFiTrueType::convertToCIDType2(char *psName,
+ Gushort *cidMap, int nCIDs,
+ GBool needVerticalMetrics,
+ FoFiOutputFunc outputFunc,
+ void *outputStream) {
+ char buf[512];
+ Gushort cid;
+ GBool ok;
+ int i, j, k;
+
+ // write the header
+ ok = gTrue;
+ sprintf(buf, "%%!PS-TrueTypeFont-%g\n", (double)getS32BE(0, &ok) / 65536.0);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+
+ // begin the font dictionary
+ (*outputFunc)(outputStream, "20 dict begin\n", 14);
+ (*outputFunc)(outputStream, "/CIDFontName /", 14);
+ (*outputFunc)(outputStream, psName, strlen(psName));
+ (*outputFunc)(outputStream, " def\n", 5);
+ (*outputFunc)(outputStream, "/CIDFontType 2 def\n", 19);
+ (*outputFunc)(outputStream, "/FontType 42 def\n", 17);
+ (*outputFunc)(outputStream, "/CIDSystemInfo 3 dict dup begin\n", 32);
+ (*outputFunc)(outputStream, " /Registry (Adobe) def\n", 24);
+ (*outputFunc)(outputStream, " /Ordering (Identity) def\n", 27);
+ (*outputFunc)(outputStream, " /Supplement 0 def\n", 20);
+ (*outputFunc)(outputStream, " end def\n", 10);
+ (*outputFunc)(outputStream, "/GDBytes 2 def\n", 15);
+ if (cidMap) {
+ sprintf(buf, "/CIDCount %d def\n", nCIDs);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ if (nCIDs > 32767) {
+ (*outputFunc)(outputStream, "/CIDMap [", 9);
+ for (i = 0; i < nCIDs; i += 32768 - 16) {
+ (*outputFunc)(outputStream, "<\n", 2);
+ for (j = 0; j < 32768 - 16 && i+j < nCIDs; j += 16) {
+ (*outputFunc)(outputStream, " ", 2);
+ for (k = 0; k < 16 && i+j+k < nCIDs; ++k) {
+ cid = cidMap[i+j+k];
+ sprintf(buf, "%02x%02x", (cid >> 8) & 0xff, cid & 0xff);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ (*outputFunc)(outputStream, "\n", 1);
+ }
+ (*outputFunc)(outputStream, " >", 3);
+ }
+ (*outputFunc)(outputStream, "\n", 1);
+ (*outputFunc)(outputStream, "] def\n", 6);
+ } else {
+ (*outputFunc)(outputStream, "/CIDMap <\n", 10);
+ for (i = 0; i < nCIDs; i += 16) {
+ (*outputFunc)(outputStream, " ", 2);
+ for (j = 0; j < 16 && i+j < nCIDs; ++j) {
+ cid = cidMap[i+j];
+ sprintf(buf, "%02x%02x", (cid >> 8) & 0xff, cid & 0xff);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ (*outputFunc)(outputStream, "\n", 1);
+ }
+ (*outputFunc)(outputStream, "> def\n", 6);
+ }
+ } else {
+ // direct mapping - just fill the string(s) with s[i]=i
+ sprintf(buf, "/CIDCount %d def\n", nGlyphs);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ if (nGlyphs > 32767) {
+ (*outputFunc)(outputStream, "/CIDMap [\n", 10);
+ for (i = 0; i < nGlyphs; i += 32767) {
+ j = nGlyphs - i < 32767 ? nGlyphs - i : 32767;
+ sprintf(buf, " %d string 0 1 %d {\n", 2 * j, j - 1);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ sprintf(buf, " 2 copy dup 2 mul exch %d add -8 bitshift put\n", i);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ sprintf(buf, " 1 index exch dup 2 mul 1 add exch %d add"
+ " 255 and put\n", i);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream, " } for\n", 8);
+ }
+ (*outputFunc)(outputStream, "] def\n", 6);
+ } else {
+ sprintf(buf, "/CIDMap %d string\n", 2 * nGlyphs);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ sprintf(buf, " 0 1 %d {\n", nGlyphs - 1);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream,
+ " 2 copy dup 2 mul exch -8 bitshift put\n", 42);
+ (*outputFunc)(outputStream,
+ " 1 index exch dup 2 mul 1 add exch 255 and put\n", 50);
+ (*outputFunc)(outputStream, " } for\n", 8);
+ (*outputFunc)(outputStream, "def\n", 4);
+ }
+ }
+ (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+ sprintf(buf, "/FontBBox [%d %d %d %d] def\n",
+ bbox[0], bbox[1], bbox[2], bbox[3]);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream, "/PaintType 0 def\n", 17);
+ (*outputFunc)(outputStream, "/Encoding [] readonly def\n", 26);
+ (*outputFunc)(outputStream, "/CharStrings 1 dict dup begin\n", 30);
+ (*outputFunc)(outputStream, " /.notdef 0 def\n", 17);
+ (*outputFunc)(outputStream, " end readonly def\n", 19);
+
+ // write the guts of the dictionary
+ cvtSfnts(outputFunc, outputStream, NULL, needVerticalMetrics);
+
+ // end the dictionary and define the font
+ (*outputFunc)(outputStream,
+ "CIDFontName currentdict end /CIDFont defineresource pop\n",
+ 56);
+}
+
+void FoFiTrueType::convertToType0(char *psName, Gushort *cidMap, int nCIDs,
+ GBool needVerticalMetrics,
+ FoFiOutputFunc outputFunc,
+ void *outputStream) {
+ char buf[512];
+ GString *sfntsName;
+ int n, i, j;
+
+ // write the Type 42 sfnts array
+ sfntsName = (new GString(psName))->append("_sfnts");
+ cvtSfnts(outputFunc, outputStream, sfntsName, needVerticalMetrics);
+ delete sfntsName;
+
+ // write the descendant Type 42 fonts
+ n = cidMap ? nCIDs : nGlyphs;
+ for (i = 0; i < n; i += 256) {
+ (*outputFunc)(outputStream, "10 dict begin\n", 14);
+ (*outputFunc)(outputStream, "/FontName /", 11);
+ (*outputFunc)(outputStream, psName, strlen(psName));
+ sprintf(buf, "_%02x def\n", i >> 8);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream, "/FontType 42 def\n", 17);
+ (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+ sprintf(buf, "/FontBBox [%d %d %d %d] def\n",
+ bbox[0], bbox[1], bbox[2], bbox[3]);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream, "/PaintType 0 def\n", 17);
+ (*outputFunc)(outputStream, "/sfnts ", 7);
+ (*outputFunc)(outputStream, psName, strlen(psName));
+ (*outputFunc)(outputStream, "_sfnts def\n", 11);
+ (*outputFunc)(outputStream, "/Encoding 256 array\n", 20);
+ for (j = 0; j < 256 && i+j < n; ++j) {
+ sprintf(buf, "dup %d /c%02x put\n", j, j);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ (*outputFunc)(outputStream, "readonly def\n", 13);
+ (*outputFunc)(outputStream, "/CharStrings 257 dict dup begin\n", 32);
+ (*outputFunc)(outputStream, "/.notdef 0 def\n", 15);
+ for (j = 0; j < 256 && i+j < n; ++j) {
+ sprintf(buf, "/c%02x %d def\n", j, cidMap ? cidMap[i+j] : i+j);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ (*outputFunc)(outputStream, "end readonly def\n", 17);
+ (*outputFunc)(outputStream,
+ "FontName currentdict end definefont pop\n", 40);
+ }
+
+ // write the Type 0 parent font
+ (*outputFunc)(outputStream, "16 dict begin\n", 14);
+ (*outputFunc)(outputStream, "/FontName /", 11);
+ (*outputFunc)(outputStream, psName, strlen(psName));
+ (*outputFunc)(outputStream, " def\n", 5);
+ (*outputFunc)(outputStream, "/FontType 0 def\n", 16);
+ (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+ (*outputFunc)(outputStream, "/FMapType 2 def\n", 16);
+ (*outputFunc)(outputStream, "/Encoding [\n", 12);
+ for (i = 0; i < n; i += 256) {
+ sprintf(buf, "%d\n", i >> 8);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ (*outputFunc)(outputStream, "] def\n", 6);
+ (*outputFunc)(outputStream, "/FDepVector [\n", 14);
+ for (i = 0; i < n; i += 256) {
+ (*outputFunc)(outputStream, "/", 1);
+ (*outputFunc)(outputStream, psName, strlen(psName));
+ sprintf(buf, "_%02x findfont\n", i >> 8);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ (*outputFunc)(outputStream, "] def\n", 6);
+ (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40);
+}
+
+void FoFiTrueType::writeTTF(FoFiOutputFunc outputFunc,
+ void *outputStream, char *name,
+ Gushort *codeToGID) {
+ // this substitute cmap table maps char codes 0000-ffff directly to
+ // glyphs 0000-ffff
+ static char cmapTab[36] = {
+ 0, 0, // table version number
+ 0, 1, // number of encoding tables
+ 0, 1, // platform ID
+ 0, 0, // encoding ID
+ 0, 0, 0, 12, // offset of subtable
+ 0, 4, // subtable format
+ 0, 24, // subtable length
+ 0, 0, // subtable version
+ 0, 2, // segment count * 2
+ 0, 2, // 2 * 2 ^ floor(log2(segCount))
+ 0, 0, // floor(log2(segCount))
+ 0, 0, // 2*segCount - 2*2^floor(log2(segCount))
+ (char)0xff, (char)0xff, // endCount[0]
+ 0, 0, // reserved
+ 0, 0, // startCount[0]
+ 0, 0, // idDelta[0]
+ 0, 0 // pad to a mulitple of four bytes
+ };
+ static char nameTab[8] = {
+ 0, 0, // format
+ 0, 0, // number of name records
+ 0, 6, // offset to start of string storage
+ 0, 0 // pad to multiple of four bytes
+ };
+ static char postTab[32] = {
+ 0, 1, 0, 0, // format
+ 0, 0, 0, 0, // italic angle
+ 0, 0, // underline position
+ 0, 0, // underline thickness
+ 0, 0, 0, 0, // fixed pitch
+ 0, 0, 0, 0, // min Type 42 memory
+ 0, 0, 0, 0, // max Type 42 memory
+ 0, 0, 0, 0, // min Type 1 memory
+ 0, 0, 0, 0 // max Type 1 memory
+ };
+ GBool missingCmap, missingName, missingPost, unsortedLoca, badCmapLen;
+ int nZeroLengthTables;
+ TrueTypeLoca *locaTable;
+ TrueTypeTable *newTables;
+ char *newNameTab, *newCmapTab;
+ int nNewTables, cmapIdx, cmapLen, glyfLen, newNameLen, newCmapLen, next;
+ Guint locaChecksum, glyfChecksum, fileChecksum;
+ char *tableDir;
+ char locaBuf[4], checksumBuf[4];
+ GBool ok;
+ Guint t;
+ int pos, i, j, k, n;
+
+ // check for missing tables
+ missingCmap = (cmapIdx = seekTable("cmap")) < 0;
+ missingName = seekTable("name") < 0;
+ missingPost = seekTable("post") < 0;
+
+ // read the loca table, check to see if it's sorted
+ locaTable = (TrueTypeLoca *)gmallocn(nGlyphs + 1, sizeof(TrueTypeLoca));
+ unsortedLoca = gFalse;
+ i = seekTable("loca");
+ pos = tables[i].offset;
+ ok = gTrue;
+ for (i = 0; i <= nGlyphs; ++i) {
+ if (locaFmt) {
+ locaTable[i].origOffset = (int)getU32BE(pos + i*4, &ok);
+ } else {
+ locaTable[i].origOffset = 2 * getU16BE(pos + i*2, &ok);
+ }
+ if (i > 0 && locaTable[i].origOffset < locaTable[i-1].origOffset) {
+ unsortedLoca = gTrue;
+ }
+ locaTable[i].idx = i;
+ }
+
+ // check for zero-length tables
+ nZeroLengthTables = 0;
+ for (i = 0; i < nTables; ++i) {
+ if (tables[i].len == 0) {
+ ++nZeroLengthTables;
+ }
+ }
+
+ // check for an incorrect cmap table length
+ badCmapLen = gFalse;
+ cmapLen = 0; // make gcc happy
+ if (!missingCmap) {
+ cmapLen = cmaps[0].offset + cmaps[0].len;
+ for (i = 1; i < nCmaps; ++i) {
+ if (cmaps[i].offset + cmaps[i].len > cmapLen) {
+ cmapLen = cmaps[i].offset + cmaps[i].len;
+ }
+ }
+ cmapLen -= tables[cmapIdx].offset;
+ if (cmapLen > tables[cmapIdx].len) {
+ badCmapLen = gTrue;
+ }
+ }
+
+ // if nothing is broken, just write the TTF file as is
+ if (!missingCmap && !missingName && !missingPost && !unsortedLoca &&
+ !badCmapLen && nZeroLengthTables == 0 && !name && !codeToGID) {
+ (*outputFunc)(outputStream, (char *)file, len);
+ goto done1;
+ }
+
+ // sort the 'loca' table: some (non-compliant) fonts have
+ // out-of-order loca tables; in order to correctly handle the case
+ // where (compliant) fonts have empty entries in the middle of the
+ // table, cmpTrueTypeLocaOffset uses offset as its primary sort key,
+ // and idx as its secondary key (ensuring that adjacent entries with
+ // the same pos value remain in the same order)
+ glyfLen = 0; // make gcc happy
+ if (unsortedLoca) {
+ qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),
+ &cmpTrueTypeLocaOffset);
+ for (i = 0; i < nGlyphs; ++i) {
+ locaTable[i].len = locaTable[i+1].origOffset - locaTable[i].origOffset;
+ }
+ locaTable[nGlyphs].len = 0;
+ qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),
+ &cmpTrueTypeLocaIdx);
+ pos = 0;
+ for (i = 0; i <= nGlyphs; ++i) {
+ locaTable[i].newOffset = pos;
+ pos += locaTable[i].len;
+ if (pos & 3) {
+ pos += 4 - (pos & 3);
+ }
+ }
+ glyfLen = pos;
+ }
+
+ // compute checksums for the loca and glyf tables
+ locaChecksum = glyfChecksum = 0;
+ if (unsortedLoca) {
+ if (locaFmt) {
+ for (j = 0; j <= nGlyphs; ++j) {
+ locaChecksum += locaTable[j].newOffset;
+ }
+ } else {
+ for (j = 0; j <= nGlyphs; j += 2) {
+ locaChecksum += locaTable[j].newOffset << 16;
+ if (j + 1 <= nGlyphs) {
+ locaChecksum += locaTable[j+1].newOffset;
+ }
+ }
+ }
+ pos = tables[seekTable("glyf")].offset;
+ for (j = 0; j < nGlyphs; ++j) {
+ n = locaTable[j].len;
+ if (n > 0) {
+ k = locaTable[j].origOffset;
+ if (checkRegion(pos + k, n)) {
+ glyfChecksum += computeTableChecksum(file + pos + k, n);
+ }
+ }
+ }
+ }
+
+ // construct the new name table
+ if (name) {
+ n = strlen(name);
+ newNameLen = (6 + 4*12 + 2 * (3*n + 7) + 3) & ~3;
+ newNameTab = (char *)gmalloc(newNameLen);
+ memset(newNameTab, 0, newNameLen);
+ newNameTab[0] = 0; // format selector
+ newNameTab[1] = 0;
+ newNameTab[2] = 0; // number of name records
+ newNameTab[3] = 4;
+ newNameTab[4] = 0; // offset to start of string storage
+ newNameTab[5] = 6 + 4*12;
+ next = 0;
+ for (i = 0; i < 4; ++i) {
+ newNameTab[6 + i*12 + 0] = 0; // platform ID = Microsoft
+ newNameTab[6 + i*12 + 1] = 3;
+ newNameTab[6 + i*12 + 2] = 0; // encoding ID = Unicode
+ newNameTab[6 + i*12 + 3] = 1;
+ newNameTab[6 + i*12 + 4] = 0x04; // language ID = American English
+ newNameTab[6 + i*12 + 5] = 0x09;
+ newNameTab[6 + i*12 + 6] = 0; // name ID
+ newNameTab[6 + i*12 + 7] = i + 1;
+ newNameTab[6 + i*12 + 8] = i+1 == 2 ? 0 : ((2*n) >> 8); // string length
+ newNameTab[6 + i*12 + 9] = i+1 == 2 ? 14 : ((2*n) & 0xff);
+ newNameTab[6 + i*12 + 10] = next >> 8; // string offset
+ newNameTab[6 + i*12 + 11] = next & 0xff;
+ if (i+1 == 2) {
+ memcpy(newNameTab + 6 + 4*12 + next, "\0R\0e\0g\0u\0l\0a\0r", 14);
+ next += 14;
+ } else {
+ for (j = 0; j < n; ++j) {
+ newNameTab[6 + 4*12 + next + 2*j] = 0;
+ newNameTab[6 + 4*12 + next + 2*j + 1] = name[j];
+ }
+ next += 2*n;
+ }
+ }
+ } else {
+ newNameLen = 0;
+ newNameTab = NULL;
+ }
+
+ // construct the new cmap table
+ if (codeToGID) {
+ newCmapLen = 44 + 256 * 2;
+ newCmapTab = (char *)gmalloc(newCmapLen);
+ newCmapTab[0] = 0; // table version number = 0
+ newCmapTab[1] = 0;
+ newCmapTab[2] = 0; // number of encoding tables = 1
+ newCmapTab[3] = 1;
+ newCmapTab[4] = 0; // platform ID = Microsoft
+ newCmapTab[5] = 3;
+ newCmapTab[6] = 0; // encoding ID = Unicode
+ newCmapTab[7] = 1;
+ newCmapTab[8] = 0; // offset of subtable
+ newCmapTab[9] = 0;
+ newCmapTab[10] = 0;
+ newCmapTab[11] = 12;
+ newCmapTab[12] = 0; // subtable format = 4
+ newCmapTab[13] = 4;
+ newCmapTab[14] = 0x02; // subtable length
+ newCmapTab[15] = 0x20;
+ newCmapTab[16] = 0; // subtable version = 0
+ newCmapTab[17] = 0;
+ newCmapTab[18] = 0; // segment count * 2
+ newCmapTab[19] = 4;
+ newCmapTab[20] = 0; // 2 * 2 ^ floor(log2(segCount))
+ newCmapTab[21] = 4;
+ newCmapTab[22] = 0; // floor(log2(segCount))
+ newCmapTab[23] = 1;
+ newCmapTab[24] = 0; // 2*segCount - 2*2^floor(log2(segCount))
+ newCmapTab[25] = 0;
+ newCmapTab[26] = 0x00; // endCount[0]
+ newCmapTab[27] = (char)0xff;
+ newCmapTab[28] = (char)0xff; // endCount[1]
+ newCmapTab[29] = (char)0xff;
+ newCmapTab[30] = 0; // reserved
+ newCmapTab[31] = 0;
+ newCmapTab[32] = 0x00; // startCount[0]
+ newCmapTab[33] = 0x00;
+ newCmapTab[34] = (char)0xff; // startCount[1]
+ newCmapTab[35] = (char)0xff;
+ newCmapTab[36] = 0; // idDelta[0]
+ newCmapTab[37] = 0;
+ newCmapTab[38] = 0; // idDelta[1]
+ newCmapTab[39] = 1;
+ newCmapTab[40] = 0; // idRangeOffset[0]
+ newCmapTab[41] = 4;
+ newCmapTab[42] = 0; // idRangeOffset[1]
+ newCmapTab[43] = 0;
+ for (i = 0; i < 256; ++i) {
+ newCmapTab[44 + 2*i] = codeToGID[i] >> 8;
+ newCmapTab[44 + 2*i + 1] = codeToGID[i] & 0xff;
+ }
+ } else {
+ newCmapLen = 0;
+ newCmapTab = NULL;
+ }
+
+ // construct the new table directory:
+ // - keep all original tables with non-zero length
+ // - fix the cmap table's length, if necessary
+ // - add missing tables
+ // - sort the table by tag
+ // - compute new table positions, including 4-byte alignment
+ // - (re)compute table checksums
+ nNewTables = nTables - nZeroLengthTables +
+ (missingCmap ? 1 : 0) + (missingName ? 1 : 0) +
+ (missingPost ? 1 : 0);
+ newTables = (TrueTypeTable *)gmallocn(nNewTables, sizeof(TrueTypeTable));
+ j = 0;
+ for (i = 0; i < nTables; ++i) {
+ if (tables[i].len > 0) {
+ newTables[j] = tables[i];
+ newTables[j].origOffset = tables[i].offset;
+ if (checkRegion(tables[i].offset, newTables[i].len)) {
+ newTables[j].checksum =
+ computeTableChecksum(file + tables[i].offset, tables[i].len);
+ if (tables[i].tag == headTag) {
+ // don't include the file checksum
+ newTables[j].checksum -= getU32BE(tables[i].offset + 8, &ok);
+ }
+ }
+ if (newTables[j].tag == cmapTag && codeToGID) {
+ newTables[j].len = newCmapLen;
+ newTables[j].checksum = computeTableChecksum((Guchar *)newCmapTab,
+ newCmapLen);
+ } else if (newTables[j].tag == cmapTag && badCmapLen) {
+ newTables[j].len = cmapLen;
+ } else if (newTables[j].tag == locaTag && unsortedLoca) {
+ newTables[j].len = (nGlyphs + 1) * (locaFmt ? 4 : 2);
+ newTables[j].checksum = locaChecksum;
+ } else if (newTables[j].tag == glyfTag && unsortedLoca) {
+ newTables[j].len = glyfLen;
+ newTables[j].checksum = glyfChecksum;
+ } else if (newTables[j].tag == nameTag && name) {
+ newTables[j].len = newNameLen;
+ newTables[j].checksum = computeTableChecksum((Guchar *)newNameTab,
+ newNameLen);
+ }
+ ++j;
+ }
+ }
+ if (missingCmap) {
+ newTables[j].tag = cmapTag;
+ if (codeToGID) {
+ newTables[j].checksum = computeTableChecksum((Guchar *)newCmapTab,
+ newCmapLen);
+ newTables[j].len = newCmapLen;
+ } else {
+ newTables[j].checksum = computeTableChecksum((Guchar *)cmapTab,
+ sizeof(cmapTab));
+ newTables[j].len = sizeof(cmapTab);
+ }
+ ++j;
+ }
+ if (missingName) {
+ newTables[j].tag = nameTag;
+ if (name) {
+ newTables[j].checksum = computeTableChecksum((Guchar *)newNameTab,
+ newNameLen);
+ newTables[j].len = newNameLen;
+ } else {
+ newTables[j].checksum = computeTableChecksum((Guchar *)nameTab,
+ sizeof(nameTab));
+ newTables[j].len = sizeof(nameTab);
+ }
+ ++j;
+ }
+ if (missingPost) {
+ newTables[j].tag = postTag;
+ newTables[j].checksum = computeTableChecksum((Guchar *)postTab,
+ sizeof(postTab));
+ newTables[j].len = sizeof(postTab);
+ ++j;
+ }
+ qsort(newTables, nNewTables, sizeof(TrueTypeTable),
+ &cmpTrueTypeTableTag);
+ pos = 12 + nNewTables * 16;
+ for (i = 0; i < nNewTables; ++i) {
+ newTables[i].offset = pos;
+ pos += newTables[i].len;
+ if (pos & 3) {
+ pos += 4 - (pos & 3);
+ }
+ }
+
+ // write the table directory
+ tableDir = (char *)gmalloc(12 + nNewTables * 16);
+ tableDir[0] = 0x00; // sfnt version
+ tableDir[1] = 0x01;
+ tableDir[2] = 0x00;
+ tableDir[3] = 0x00;
+ tableDir[4] = (char)((nNewTables >> 8) & 0xff); // numTables
+ tableDir[5] = (char)(nNewTables & 0xff);
+ for (i = -1, t = (Guint)nNewTables; t; ++i, t >>= 1) ;
+ t = 1 << (4 + i);
+ tableDir[6] = (char)((t >> 8) & 0xff); // searchRange
+ tableDir[7] = (char)(t & 0xff);
+ tableDir[8] = (char)((i >> 8) & 0xff); // entrySelector
+ tableDir[9] = (char)(i & 0xff);
+ t = nNewTables * 16 - t;
+ tableDir[10] = (char)((t >> 8) & 0xff); // rangeShift
+ tableDir[11] = (char)(t & 0xff);
+ pos = 12;
+ for (i = 0; i < nNewTables; ++i) {
+ tableDir[pos ] = (char)(newTables[i].tag >> 24);
+ tableDir[pos+ 1] = (char)(newTables[i].tag >> 16);
+ tableDir[pos+ 2] = (char)(newTables[i].tag >> 8);
+ tableDir[pos+ 3] = (char) newTables[i].tag;
+ tableDir[pos+ 4] = (char)(newTables[i].checksum >> 24);
+ tableDir[pos+ 5] = (char)(newTables[i].checksum >> 16);
+ tableDir[pos+ 6] = (char)(newTables[i].checksum >> 8);
+ tableDir[pos+ 7] = (char) newTables[i].checksum;
+ tableDir[pos+ 8] = (char)(newTables[i].offset >> 24);
+ tableDir[pos+ 9] = (char)(newTables[i].offset >> 16);
+ tableDir[pos+10] = (char)(newTables[i].offset >> 8);
+ tableDir[pos+11] = (char) newTables[i].offset;
+ tableDir[pos+12] = (char)(newTables[i].len >> 24);
+ tableDir[pos+13] = (char)(newTables[i].len >> 16);
+ tableDir[pos+14] = (char)(newTables[i].len >> 8);
+ tableDir[pos+15] = (char) newTables[i].len;
+ pos += 16;
+ }
+ (*outputFunc)(outputStream, tableDir, 12 + nNewTables * 16);
+
+ // compute the file checksum
+ fileChecksum = computeTableChecksum((Guchar *)tableDir,
+ 12 + nNewTables * 16);
+ for (i = 0; i < nNewTables; ++i) {
+ fileChecksum += newTables[i].checksum;
+ }
+ fileChecksum = 0xb1b0afba - fileChecksum;
+
+ // write the tables
+ for (i = 0; i < nNewTables; ++i) {
+ if (newTables[i].tag == headTag) {
+ if (checkRegion(newTables[i].origOffset, newTables[i].len)) {
+ (*outputFunc)(outputStream, (char *)file + newTables[i].origOffset, 8);
+ checksumBuf[0] = fileChecksum >> 24;
+ checksumBuf[1] = fileChecksum >> 16;
+ checksumBuf[2] = fileChecksum >> 8;
+ checksumBuf[3] = fileChecksum;
+ (*outputFunc)(outputStream, checksumBuf, 4);
+ (*outputFunc)(outputStream,
+ (char *)file + newTables[i].origOffset + 12,
+ newTables[i].len - 12);
+ } else {
+ for (j = 0; j < newTables[i].len; ++j) {
+ (*outputFunc)(outputStream, "\0", 1);
+ }
+ }
+ } else if (newTables[i].tag == cmapTag && codeToGID) {
+ (*outputFunc)(outputStream, newCmapTab, newTables[i].len);
+ } else if (newTables[i].tag == cmapTag && missingCmap) {
+ (*outputFunc)(outputStream, cmapTab, newTables[i].len);
+ } else if (newTables[i].tag == nameTag && name) {
+ (*outputFunc)(outputStream, newNameTab, newTables[i].len);
+ } else if (newTables[i].tag == nameTag && missingName) {
+ (*outputFunc)(outputStream, nameTab, newTables[i].len);
+ } else if (newTables[i].tag == postTag && missingPost) {
+ (*outputFunc)(outputStream, postTab, newTables[i].len);
+ } else if (newTables[i].tag == locaTag && unsortedLoca) {
+ for (j = 0; j <= nGlyphs; ++j) {
+ if (locaFmt) {
+ locaBuf[0] = (char)(locaTable[j].newOffset >> 24);
+ locaBuf[1] = (char)(locaTable[j].newOffset >> 16);
+ locaBuf[2] = (char)(locaTable[j].newOffset >> 8);
+ locaBuf[3] = (char) locaTable[j].newOffset;
+ (*outputFunc)(outputStream, locaBuf, 4);
+ } else {
+ locaBuf[0] = (char)(locaTable[j].newOffset >> 9);
+ locaBuf[1] = (char)(locaTable[j].newOffset >> 1);
+ (*outputFunc)(outputStream, locaBuf, 2);
+ }
+ }
+ } else if (newTables[i].tag == glyfTag && unsortedLoca) {
+ pos = tables[seekTable("glyf")].offset;
+ for (j = 0; j < nGlyphs; ++j) {
+ n = locaTable[j].len;
+ if (n > 0) {
+ k = locaTable[j].origOffset;
+ if (checkRegion(pos + k, n)) {
+ (*outputFunc)(outputStream, (char *)file + pos + k, n);
+ } else {
+ for (k = 0; k < n; ++k) {
+ (*outputFunc)(outputStream, "\0", 1);
+ }
+ }
+ if ((k = locaTable[j].len & 3)) {
+ (*outputFunc)(outputStream, "\0\0\0\0", 4 - k);
+ }
+ }
+ }
+ } else {
+ if (checkRegion(newTables[i].origOffset, newTables[i].len)) {
+ (*outputFunc)(outputStream, (char *)file + newTables[i].origOffset,
+ newTables[i].len);
+ } else {
+ for (j = 0; j < newTables[i].len; ++j) {
+ (*outputFunc)(outputStream, "\0", 1);
+ }
+ }
+ }
+ if (newTables[i].len & 3) {
+ (*outputFunc)(outputStream, "\0\0\0", 4 - (newTables[i].len & 3));
+ }
+ }
+
+ gfree(newCmapTab);
+ gfree(newNameTab);
+ gfree(tableDir);
+ gfree(newTables);
+ done1:
+ gfree(locaTable);
+}
+
+void FoFiTrueType::cvtEncoding(char **encoding,
+ FoFiOutputFunc outputFunc,
+ void *outputStream) {
+ char *name;
+ char buf[64];
+ int i;
+
+ (*outputFunc)(outputStream, "/Encoding 256 array\n", 20);
+ if (encoding) {
+ for (i = 0; i < 256; ++i) {
+ if (!(name = encoding[i])) {
+ name = ".notdef";
+ }
+ sprintf(buf, "dup %d /", i);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream, name, strlen(name));
+ (*outputFunc)(outputStream, " put\n", 5);
+ }
+ } else {
+ for (i = 0; i < 256; ++i) {
+ sprintf(buf, "dup %d /c%02x put\n", i, i);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ }
+ (*outputFunc)(outputStream, "readonly def\n", 13);
+}
+
+void FoFiTrueType::cvtCharStrings(char **encoding,
+ Gushort *codeToGID,
+ FoFiOutputFunc outputFunc,
+ void *outputStream) {
+ char *name;
+ char buf[64], buf2[16];
+ int i, k;
+
+ // always define '.notdef'
+ (*outputFunc)(outputStream, "/CharStrings 256 dict dup begin\n", 32);
+ (*outputFunc)(outputStream, "/.notdef 0 def\n", 15);
+
+ // if there's no 'cmap' table, punt
+ if (nCmaps == 0) {
+ goto err;
+ }
+
+ // map char name to glyph index:
+ // 1. use encoding to map name to char code
+ // 2. use codeToGID to map char code to glyph index
+ // N.B. We do this in reverse order because font subsets can have
+ // weird encodings that use the same character name twice, and
+ // the first definition is probably the one we want.
+ k = 0; // make gcc happy
+ for (i = 255; i >= 0; --i) {
+ if (encoding) {
+ name = encoding[i];
+ } else {
+ sprintf(buf2, "c%02x", i);
+ name = buf2;
+ }
+ if (name && strcmp(name, ".notdef")) {
+ k = codeToGID[i];
+ // note: Distiller (maybe Adobe's PS interpreter in general)
+ // doesn't like TrueType fonts that have CharStrings entries
+ // which point to nonexistent glyphs, hence the (k < nGlyphs)
+ // test
+ if (k > 0 && k < nGlyphs) {
+ (*outputFunc)(outputStream, "/", 1);
+ (*outputFunc)(outputStream, name, strlen(name));
+ sprintf(buf, " %d def\n", k);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ }
+ }
+
+ err:
+ (*outputFunc)(outputStream, "end readonly def\n", 17);
+}
+
+void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc,
+ void *outputStream, GString *name,
+ GBool needVerticalMetrics) {
+ Guchar headData[54];
+ TrueTypeLoca *locaTable;
+ Guchar *locaData;
+ TrueTypeTable newTables[nT42Tables];
+ Guchar tableDir[12 + nT42Tables*16];
+ GBool ok;
+ Guint checksum;
+ int nNewTables;
+ int length, pos, glyfPos, i, j, k;
+ Guchar vheaTab[36] = {
+ 0, 1, 0, 0, // table version number
+ 0, 0, // ascent
+ 0, 0, // descent
+ 0, 0, // reserved
+ 0, 0, // max advance height
+ 0, 0, // min top side bearing
+ 0, 0, // min bottom side bearing
+ 0, 0, // y max extent
+ 0, 0, // caret slope rise
+ 0, 1, // caret slope run
+ 0, 0, // caret offset
+ 0, 0, // reserved
+ 0, 0, // reserved
+ 0, 0, // reserved
+ 0, 0, // reserved
+ 0, 0, // metric data format
+ 0, 1 // number of advance heights in vmtx table
+ };
+ Guchar *vmtxTab;
+ GBool needVhea, needVmtx;
+ int advance;
+
+ // construct the 'head' table, zero out the font checksum
+ i = seekTable("head");
+ pos = tables[i].offset;
+ if (!checkRegion(pos, 54)) {
+ return;
+ }
+ memcpy(headData, file + pos, 54);
+ headData[8] = headData[9] = headData[10] = headData[11] = (Guchar)0;
+
+ // read the original 'loca' table, pad entries out to 4 bytes, and
+ // sort it into proper order -- some (non-compliant) fonts have
+ // out-of-order loca tables; in order to correctly handle the case
+ // where (compliant) fonts have empty entries in the middle of the
+ // table, cmpTrueTypeLocaPos uses offset as its primary sort key,
+ // and idx as its secondary key (ensuring that adjacent entries with
+ // the same pos value remain in the same order)
+ locaTable = (TrueTypeLoca *)gmallocn(nGlyphs + 1, sizeof(TrueTypeLoca));
+ i = seekTable("loca");
+ pos = tables[i].offset;
+ ok = gTrue;
+ for (i = 0; i <= nGlyphs; ++i) {
+ locaTable[i].idx = i;
+ if (locaFmt) {
+ locaTable[i].origOffset = (int)getU32BE(pos + i*4, &ok);
+ } else {
+ locaTable[i].origOffset = 2 * getU16BE(pos + i*2, &ok);
+ }
+ }
+ qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),
+ &cmpTrueTypeLocaOffset);
+ for (i = 0; i < nGlyphs; ++i) {
+ locaTable[i].len = locaTable[i+1].origOffset - locaTable[i].origOffset;
+ }
+ locaTable[nGlyphs].len = 0;
+ qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),
+ &cmpTrueTypeLocaIdx);
+ pos = 0;
+ for (i = 0; i <= nGlyphs; ++i) {
+ locaTable[i].newOffset = pos;
+ pos += locaTable[i].len;
+ if (pos & 3) {
+ pos += 4 - (pos & 3);
+ }
+ }
+
+ // construct the new 'loca' table
+ locaData = (Guchar *)gmallocn(nGlyphs + 1, (locaFmt ? 4 : 2));
+ for (i = 0; i <= nGlyphs; ++i) {
+ pos = locaTable[i].newOffset;
+ if (locaFmt) {
+ locaData[4*i ] = (Guchar)(pos >> 24);
+ locaData[4*i+1] = (Guchar)(pos >> 16);
+ locaData[4*i+2] = (Guchar)(pos >> 8);
+ locaData[4*i+3] = (Guchar) pos;
+ } else {
+ locaData[2*i ] = (Guchar)(pos >> 9);
+ locaData[2*i+1] = (Guchar)(pos >> 1);
+ }
+ }
+
+ // count the number of tables
+ nNewTables = 0;
+ for (i = 0; i < nT42Tables; ++i) {
+ if (t42Tables[i].required ||
+ seekTable(t42Tables[i].tag) >= 0) {
+ ++nNewTables;
+ }
+ }
+ vmtxTab = NULL; // make gcc happy
+ advance = 0; // make gcc happy
+ if (needVerticalMetrics) {
+ needVhea = seekTable("vhea") < 0;
+ needVmtx = seekTable("vmtx") < 0;
+ if (needVhea || needVmtx) {
+ i = seekTable("head");
+ advance = getU16BE(tables[i].offset + 18, &ok); // units per em
+ if (needVhea) {
+ ++nNewTables;
+ }
+ if (needVmtx) {
+ ++nNewTables;
+ }
+ }
+ }
+
+ // construct the new table headers, including table checksums
+ // (pad each table out to a multiple of 4 bytes)
+ pos = 12 + nNewTables*16;
+ k = 0;
+ for (i = 0; i < nT42Tables; ++i) {
+ length = -1;
+ checksum = 0; // make gcc happy
+ if (i == t42HeadTable) {
+ length = 54;
+ checksum = computeTableChecksum(headData, 54);
+ } else if (i == t42LocaTable) {
+ length = (nGlyphs + 1) * (locaFmt ? 4 : 2);
+ checksum = computeTableChecksum(locaData, length);
+ } else if (i == t42GlyfTable) {
+ length = 0;
+ checksum = 0;
+ glyfPos = tables[seekTable("glyf")].offset;
+ for (j = 0; j < nGlyphs; ++j) {
+ length += locaTable[j].len;
+ if (length & 3) {
+ length += 4 - (length & 3);
+ }
+ if (checkRegion(glyfPos + locaTable[j].origOffset, locaTable[j].len)) {
+ checksum +=
+ computeTableChecksum(file + glyfPos + locaTable[j].origOffset,
+ locaTable[j].len);
+ }
+ }
+ } else {
+ if ((j = seekTable(t42Tables[i].tag)) >= 0) {
+ length = tables[j].len;
+ if (checkRegion(tables[j].offset, length)) {
+ checksum = computeTableChecksum(file + tables[j].offset, length);
+ }
+ } else if (needVerticalMetrics && i == t42VheaTable) {
+ vheaTab[10] = advance / 256; // max advance height
+ vheaTab[11] = advance % 256;
+ length = sizeof(vheaTab);
+ checksum = computeTableChecksum(vheaTab, length);
+ } else if (needVerticalMetrics && i == t42VmtxTable) {
+ length = 4 + (nGlyphs - 1) * 4;
+ vmtxTab = (Guchar *)gmalloc(length);
+ vmtxTab[0] = advance / 256;
+ vmtxTab[1] = advance % 256;
+ for (j = 2; j < length; j += 2) {
+ vmtxTab[j] = 0;
+ vmtxTab[j+1] = 0;
+ }
+ checksum = computeTableChecksum(vmtxTab, length);
+ } else if (t42Tables[i].required) {
+ //~ error(-1, "Embedded TrueType font is missing a required table ('%s')",
+ //~ t42Tables[i].tag);
+ length = 0;
+ checksum = 0;
+ }
+ }
+ if (length >= 0) {
+ newTables[k].tag = ((t42Tables[i].tag[0] & 0xff) << 24) |
+ ((t42Tables[i].tag[1] & 0xff) << 16) |
+ ((t42Tables[i].tag[2] & 0xff) << 8) |
+ (t42Tables[i].tag[3] & 0xff);
+ newTables[k].checksum = checksum;
+ newTables[k].offset = pos;
+ newTables[k].len = length;
+ pos += length;
+ if (pos & 3) {
+ pos += 4 - (length & 3);
+ }
+ ++k;
+ }
+ }
+
+ // construct the table directory
+ tableDir[0] = 0x00; // sfnt version
+ tableDir[1] = 0x01;
+ tableDir[2] = 0x00;
+ tableDir[3] = 0x00;
+ tableDir[4] = 0; // numTables
+ tableDir[5] = nNewTables;
+ tableDir[6] = 0; // searchRange
+ tableDir[7] = (Guchar)128;
+ tableDir[8] = 0; // entrySelector
+ tableDir[9] = 3;
+ tableDir[10] = 0; // rangeShift
+ tableDir[11] = (Guchar)(16 * nNewTables - 128);
+ pos = 12;
+ for (i = 0; i < nNewTables; ++i) {
+ tableDir[pos ] = (Guchar)(newTables[i].tag >> 24);
+ tableDir[pos+ 1] = (Guchar)(newTables[i].tag >> 16);
+ tableDir[pos+ 2] = (Guchar)(newTables[i].tag >> 8);
+ tableDir[pos+ 3] = (Guchar) newTables[i].tag;
+ tableDir[pos+ 4] = (Guchar)(newTables[i].checksum >> 24);
+ tableDir[pos+ 5] = (Guchar)(newTables[i].checksum >> 16);
+ tableDir[pos+ 6] = (Guchar)(newTables[i].checksum >> 8);
+ tableDir[pos+ 7] = (Guchar) newTables[i].checksum;
+ tableDir[pos+ 8] = (Guchar)(newTables[i].offset >> 24);
+ tableDir[pos+ 9] = (Guchar)(newTables[i].offset >> 16);
+ tableDir[pos+10] = (Guchar)(newTables[i].offset >> 8);
+ tableDir[pos+11] = (Guchar) newTables[i].offset;
+ tableDir[pos+12] = (Guchar)(newTables[i].len >> 24);
+ tableDir[pos+13] = (Guchar)(newTables[i].len >> 16);
+ tableDir[pos+14] = (Guchar)(newTables[i].len >> 8);
+ tableDir[pos+15] = (Guchar) newTables[i].len;
+ pos += 16;
+ }
+
+ // compute the font checksum and store it in the head table
+ checksum = computeTableChecksum(tableDir, 12 + nNewTables*16);
+ for (i = 0; i < nNewTables; ++i) {
+ checksum += newTables[i].checksum;
+ }
+ checksum = 0xb1b0afba - checksum; // because the TrueType spec says so
+ headData[ 8] = (Guchar)(checksum >> 24);
+ headData[ 9] = (Guchar)(checksum >> 16);
+ headData[10] = (Guchar)(checksum >> 8);
+ headData[11] = (Guchar) checksum;
+
+ // start the sfnts array
+ if (name) {
+ (*outputFunc)(outputStream, "/", 1);
+ (*outputFunc)(outputStream, name->getCString(), name->getLength());
+ (*outputFunc)(outputStream, " [\n", 3);
+ } else {
+ (*outputFunc)(outputStream, "/sfnts [\n", 9);
+ }
+
+ // write the table directory
+ dumpString(tableDir, 12 + nNewTables*16, outputFunc, outputStream);
+
+ // write the tables
+ for (i = 0; i < nNewTables; ++i) {
+ if (i == t42HeadTable) {
+ dumpString(headData, 54, outputFunc, outputStream);
+ } else if (i == t42LocaTable) {
+ length = (nGlyphs + 1) * (locaFmt ? 4 : 2);
+ dumpString(locaData, length, outputFunc, outputStream);
+ } else if (i == t42GlyfTable) {
+ glyfPos = tables[seekTable("glyf")].offset;
+ for (j = 0; j < nGlyphs; ++j) {
+ if (locaTable[j].len > 0 &&
+ checkRegion(glyfPos + locaTable[j].origOffset, locaTable[j].len)) {
+ dumpString(file + glyfPos + locaTable[j].origOffset,
+ locaTable[j].len, outputFunc, outputStream);
+ }
+ }
+ } else {
+ // length == 0 means the table is missing and the error was
+ // already reported during the construction of the table
+ // headers
+ if ((length = newTables[i].len) > 0) {
+ if ((j = seekTable(t42Tables[i].tag)) >= 0 &&
+ checkRegion(tables[j].offset, tables[j].len)) {
+ dumpString(file + tables[j].offset, tables[j].len,
+ outputFunc, outputStream);
+ } else if (needVerticalMetrics && i == t42VheaTable) {
+ dumpString(vheaTab, length, outputFunc, outputStream);
+ } else if (needVerticalMetrics && i == t42VmtxTable) {
+ dumpString(vmtxTab, length, outputFunc, outputStream);
+ gfree(vmtxTab);
+ }
+ }
+ }
+ }
+
+ // end the sfnts array
+ (*outputFunc)(outputStream, "] def\n", 6);
+
+ gfree(locaData);
+ gfree(locaTable);
+}
+
+void FoFiTrueType::dumpString(Guchar *s, int length,
+ FoFiOutputFunc outputFunc,
+ void *outputStream) {
+ char buf[64];
+ int pad, i, j;
+
+ (*outputFunc)(outputStream, "<", 1);
+ for (i = 0; i < length; i += 32) {
+ for (j = 0; j < 32 && i+j < length; ++j) {
+ sprintf(buf, "%02X", s[i+j] & 0xff);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ if (i % (65536 - 32) == 65536 - 64) {
+ (*outputFunc)(outputStream, ">\n<", 3);
+ } else if (i+32 < length) {
+ (*outputFunc)(outputStream, "\n", 1);
+ }
+ }
+ if (length & 3) {
+ pad = 4 - (length & 3);
+ for (i = 0; i < pad; ++i) {
+ (*outputFunc)(outputStream, "00", 2);
+ }
+ }
+ // add an extra zero byte because the Adobe Type 42 spec says so
+ (*outputFunc)(outputStream, "00>\n", 4);
+}
+
+Guint FoFiTrueType::computeTableChecksum(Guchar *data, int length) {
+ Guint checksum, word;
+ int i;
+
+ checksum = 0;
+ for (i = 0; i+3 < length; i += 4) {
+ word = ((data[i ] & 0xff) << 24) +
+ ((data[i+1] & 0xff) << 16) +
+ ((data[i+2] & 0xff) << 8) +
+ (data[i+3] & 0xff);
+ checksum += word;
+ }
+ if (length & 3) {
+ word = 0;
+ i = length & ~3;
+ switch (length & 3) {
+ case 3:
+ word |= (data[i+2] & 0xff) << 8;
+ case 2:
+ word |= (data[i+1] & 0xff) << 16;
+ case 1:
+ word |= (data[i ] & 0xff) << 24;
+ break;
+ }
+ checksum += word;
+ }
+ return checksum;
+}
+
+void FoFiTrueType::parse() {
+ Guint topTag;
+ int pos, i, j;
+
+ parsedOk = gTrue;
+
+ // look for a collection (TTC)
+ topTag = getU32BE(0, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ if (topTag == ttcfTag) {
+ pos = getU32BE(12, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ } else {
+ pos = 0;
+ }
+
+ // read the table directory
+ nTables = getU16BE(pos + 4, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ tables = (TrueTypeTable *)gmallocn(nTables, sizeof(TrueTypeTable));
+ pos += 12;
+ for (i = 0; i < nTables; ++i) {
+ tables[i].tag = getU32BE(pos, &parsedOk);
+ tables[i].checksum = getU32BE(pos + 4, &parsedOk);
+ tables[i].offset = (int)getU32BE(pos + 8, &parsedOk);
+ tables[i].len = (int)getU32BE(pos + 12, &parsedOk);
+ if (tables[i].offset + tables[i].len < tables[i].offset ||
+ tables[i].offset + tables[i].len > len) {
+ parsedOk = gFalse;
+ }
+ pos += 16;
+ }
+ if (!parsedOk) {
+ return;
+ }
+
+ // check for tables that are required by both the TrueType spec and
+ // the Type 42 spec
+ if (seekTable("head") < 0 ||
+ seekTable("hhea") < 0 ||
+ seekTable("loca") < 0 ||
+ seekTable("maxp") < 0 ||
+ seekTable("glyf") < 0 ||
+ seekTable("hmtx") < 0) {
+ parsedOk = gFalse;
+ return;
+ }
+
+ // read the cmaps
+ if ((i = seekTable("cmap")) >= 0) {
+ pos = tables[i].offset + 2;
+ nCmaps = getU16BE(pos, &parsedOk);
+ pos += 2;
+ if (!parsedOk) {
+ return;
+ }
+ cmaps = (TrueTypeCmap *)gmallocn(nCmaps, sizeof(TrueTypeCmap));
+ for (j = 0; j < nCmaps; ++j) {
+ cmaps[j].platform = getU16BE(pos, &parsedOk);
+ cmaps[j].encoding = getU16BE(pos + 2, &parsedOk);
+ cmaps[j].offset = tables[i].offset + getU32BE(pos + 4, &parsedOk);
+ pos += 8;
+ cmaps[j].fmt = getU16BE(cmaps[j].offset, &parsedOk);
+ cmaps[j].len = getU16BE(cmaps[j].offset + 2, &parsedOk);
+ }
+ if (!parsedOk) {
+ return;
+ }
+ } else {
+ nCmaps = 0;
+ }
+
+ // get the number of glyphs from the maxp table
+ i = seekTable("maxp");
+ nGlyphs = getU16BE(tables[i].offset + 4, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+
+ // get the bbox and loca table format from the head table
+ i = seekTable("head");
+ bbox[0] = getS16BE(tables[i].offset + 36, &parsedOk);
+ bbox[1] = getS16BE(tables[i].offset + 38, &parsedOk);
+ bbox[2] = getS16BE(tables[i].offset + 40, &parsedOk);
+ bbox[3] = getS16BE(tables[i].offset + 42, &parsedOk);
+ locaFmt = getS16BE(tables[i].offset + 50, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+
+ // make sure the loca table is sane (correct length and entries are
+ // in bounds)
+ i = seekTable("loca");
+ if (tables[i].len < (nGlyphs + 1) * (locaFmt ? 4 : 2)) {
+ parsedOk = gFalse;
+ return;
+ }
+ for (j = 0; j <= nGlyphs; ++j) {
+ if (locaFmt) {
+ pos = (int)getU32BE(tables[i].offset + j*4, &parsedOk);
+ } else {
+ pos = getU16BE(tables[i].offset + j*2, &parsedOk);
+ }
+ if (pos < 0 || pos > len) {
+ parsedOk = gFalse;
+ }
+ }
+ if (!parsedOk) {
+ return;
+ }
+
+ // read the post table
+ readPostTable();
+}
+
+void FoFiTrueType::readPostTable() {
+ GString *name;
+ int tablePos, postFmt, stringIdx, stringPos;
+ GBool ok;
+ int i, j, n, m;
+
+ ok = gTrue;
+ if ((i = seekTable("post")) < 0) {
+ return;
+ }
+ tablePos = tables[i].offset;
+ postFmt = getU32BE(tablePos, &ok);
+ if (!ok) {
+ goto err;
+ }
+ if (postFmt == 0x00010000) {
+ nameToGID = new GHash(gTrue);
+ for (i = 0; i < 258; ++i) {
+ nameToGID->add(new GString(macGlyphNames[i]), i);
+ }
+ } else if (postFmt == 0x00020000) {
+ nameToGID = new GHash(gTrue);
+ n = getU16BE(tablePos + 32, &ok);
+ if (!ok) {
+ goto err;
+ }
+ if (n > nGlyphs) {
+ n = nGlyphs;
+ }
+ stringIdx = 0;
+ stringPos = tablePos + 34 + 2*n;
+ for (i = 0; i < n; ++i) {
+ j = getU16BE(tablePos + 34 + 2*i, &ok);
+ if (j < 258) {
+ nameToGID->removeInt(macGlyphNames[j]);
+ nameToGID->add(new GString(macGlyphNames[j]), i);
+ } else {
+ j -= 258;
+ if (j != stringIdx) {
+ for (stringIdx = 0, stringPos = tablePos + 34 + 2*n;
+ stringIdx < j;
+ ++stringIdx, stringPos += 1 + getU8(stringPos, &ok)) ;
+ if (!ok) {
+ goto err;
+ }
+ }
+ m = getU8(stringPos, &ok);
+ if (!ok || !checkRegion(stringPos + 1, m)) {
+ goto err;
+ }
+ name = new GString((char *)&file[stringPos + 1], m);
+ nameToGID->removeInt(name);
+ nameToGID->add(name, i);
+ ++stringIdx;
+ stringPos += 1 + m;
+ }
+ }
+ } else if (postFmt == 0x00028000) {
+ nameToGID = new GHash(gTrue);
+ for (i = 0; i < nGlyphs; ++i) {
+ j = getU8(tablePos + 32 + i, &ok);
+ if (!ok) {
+ goto err;
+ }
+ if (j < 258) {
+ nameToGID->removeInt(macGlyphNames[j]);
+ nameToGID->add(new GString(macGlyphNames[j]), i);
+ }
+ }
+ }
+
+ return;
+
+ err:
+ if (nameToGID) {
+ delete nameToGID;
+ nameToGID = NULL;
+ }
+}
+
+int FoFiTrueType::seekTable(char *tag) {
+ Guint tagI;
+ int i;
+
+ tagI = ((tag[0] & 0xff) << 24) |
+ ((tag[1] & 0xff) << 16) |
+ ((tag[2] & 0xff) << 8) |
+ (tag[3] & 0xff);
+ for (i = 0; i < nTables; ++i) {
+ if (tables[i].tag == tagI) {
+ return i;
+ }
+ }
+ return -1;
+}
--- /dev/null
+//========================================================================
+//
+// FoFiTrueType.h
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FOFITRUETYPE_H
+#define FOFITRUETYPE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "FoFiBase.h"
+
+class GString;
+class GHash;
+struct TrueTypeTable;
+struct TrueTypeCmap;
+
+//------------------------------------------------------------------------
+// FoFiTrueType
+//------------------------------------------------------------------------
+
+class FoFiTrueType: public FoFiBase {
+public:
+
+ // Create a FoFiTrueType object from a memory buffer.
+ static FoFiTrueType *make(char *fileA, int lenA);
+
+ // Create a FoFiTrueType object from a file on disk.
+ static FoFiTrueType *load(char *fileName);
+
+ virtual ~FoFiTrueType();
+
+ // Return the number of cmaps defined by this font.
+ int getNumCmaps();
+
+ // Return the platform ID of the <i>th cmap.
+ int getCmapPlatform(int i);
+
+ // Return the encoding ID of the <i>th cmap.
+ int getCmapEncoding(int i);
+
+ // Return the index of the cmap for <platform>, <encoding>. Returns
+ // -1 if there is no corresponding cmap.
+ int findCmap(int platform, int encoding);
+
+ // Return the GID corresponding to <c> according to the <i>th cmap.
+ Gushort mapCodeToGID(int i, int c);
+
+ // Returns the GID corresponding to <name> according to the post
+ // table. Returns 0 if there is no mapping for <name> or if the
+ // font does not have a post table.
+ int mapNameToGID(char *name);
+
+ // Returns the least restrictive embedding licensing right (as
+ // defined by the TrueType spec):
+ // * 4: OS/2 table is missing or invalid
+ // * 3: installable embedding
+ // * 2: editable embedding
+ // * 1: preview & print embedding
+ // * 0: restricted license embedding
+ int getEmbeddingRights();
+
+ // Convert to a Type 42 font, suitable for embedding in a PostScript
+ // file. <psName> will be used as the PostScript font name (so we
+ // don't need to depend on the 'name' table in the font). The
+ // <encoding> array specifies the mapping from char codes to names.
+ // If <encoding> is NULL, the encoding is unknown or undefined. The
+ // <codeToGID> array specifies the mapping from char codes to GIDs.
+ void convertToType42(char *psName, char **encoding,
+ Gushort *codeToGID,
+ FoFiOutputFunc outputFunc, void *outputStream);
+
+ // Convert to a Type 2 CIDFont, suitable for embedding in a
+ // PostScript file. <psName> will be used as the PostScript font
+ // name (so we don't need to depend on the 'name' table in the
+ // font). The <cidMap> array maps CIDs to GIDs; it has <nCIDs>
+ // entries.
+ void convertToCIDType2(char *psName, Gushort *cidMap, int nCIDs,
+ GBool needVerticalMetrics,
+ FoFiOutputFunc outputFunc, void *outputStream);
+
+ // Convert to a Type 0 (but non-CID) composite font, suitable for
+ // embedding in a PostScript file. <psName> will be used as the
+ // PostScript font name (so we don't need to depend on the 'name'
+ // table in the font). The <cidMap> array maps CIDs to GIDs; it has
+ // <nCIDs> entries.
+ void convertToType0(char *psName, Gushort *cidMap, int nCIDs,
+ GBool needVerticalMetrics,
+ FoFiOutputFunc outputFunc, void *outputStream);
+
+ // Write a clean TTF file, filling in missing tables and correcting
+ // various other errors. If <name> is non-NULL, the font is renamed
+ // to <name>. If <codeToGID> is non-NULL, the font is re-encoded,
+ // using a Windows Unicode cmap. If <name> is NULL and the font is
+ // complete and correct, it will be written unmodified.
+ void writeTTF(FoFiOutputFunc outputFunc, void *outputStream,
+ char *name = NULL, Gushort *codeToGID = NULL);
+
+private:
+
+ FoFiTrueType(char *fileA, int lenA, GBool freeFileDataA);
+ void cvtEncoding(char **encoding,
+ FoFiOutputFunc outputFunc,
+ void *outputStream);
+ void cvtCharStrings(char **encoding,
+ Gushort *codeToGID,
+ FoFiOutputFunc outputFunc,
+ void *outputStream);
+ void cvtSfnts(FoFiOutputFunc outputFunc,
+ void *outputStream, GString *name,
+ GBool needVerticalMetrics);
+ void dumpString(Guchar *s, int length,
+ FoFiOutputFunc outputFunc,
+ void *outputStream);
+ Guint computeTableChecksum(Guchar *data, int length);
+ void parse();
+ void readPostTable();
+ int seekTable(char *tag);
+
+ TrueTypeTable *tables;
+ int nTables;
+ TrueTypeCmap *cmaps;
+ int nCmaps;
+ int nGlyphs;
+ int locaFmt;
+ int bbox[4];
+ GHash *nameToGID;
+
+ GBool parsedOk;
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// FoFiType1.cc
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "gmem.h"
+#include "FoFiEncodings.h"
+#include "FoFiType1.h"
+
+//------------------------------------------------------------------------
+// FoFiType1
+//------------------------------------------------------------------------
+
+FoFiType1 *FoFiType1::make(char *fileA, int lenA) {
+ return new FoFiType1(fileA, lenA, gFalse);
+}
+
+FoFiType1 *FoFiType1::load(char *fileName) {
+ char *fileA;
+ int lenA;
+
+ if (!(fileA = FoFiBase::readFile(fileName, &lenA))) {
+ return NULL;
+ }
+ return new FoFiType1(fileA, lenA, gTrue);
+}
+
+FoFiType1::FoFiType1(char *fileA, int lenA, GBool freeFileDataA):
+ FoFiBase(fileA, lenA, freeFileDataA)
+{
+ name = NULL;
+ encoding = NULL;
+ parsed = gFalse;
+}
+
+FoFiType1::~FoFiType1() {
+ int i;
+
+ if (name) {
+ gfree(name);
+ }
+ if (encoding && encoding != fofiType1StandardEncoding) {
+ for (i = 0; i < 256; ++i) {
+ gfree(encoding[i]);
+ }
+ gfree(encoding);
+ }
+}
+
+char *FoFiType1::getName() {
+ if (!parsed) {
+ parse();
+ }
+ return name;
+}
+
+char **FoFiType1::getEncoding() {
+ if (!parsed) {
+ parse();
+ }
+ return encoding;
+}
+
+void FoFiType1::writeEncoded(char **newEncoding,
+ FoFiOutputFunc outputFunc, void *outputStream) {
+ char buf[512];
+ char *line;
+ int i;
+
+ // copy everything up to the encoding
+ for (line = (char *)file;
+ line && strncmp(line, "/Encoding", 9);
+ line = getNextLine(line)) ;
+ if (!line) {
+ // no encoding - just copy the whole font file
+ (*outputFunc)(outputStream, (char *)file, len);
+ return;
+ }
+ (*outputFunc)(outputStream, (char *)file, line - (char *)file);
+
+ // write the new encoding
+ (*outputFunc)(outputStream, "/Encoding 256 array\n", 20);
+ (*outputFunc)(outputStream,
+ "0 1 255 {1 index exch /.notdef put} for\n", 40);
+ for (i = 0; i < 256; ++i) {
+ if (newEncoding[i]) {
+ sprintf(buf, "dup %d /%s put\n", i, newEncoding[i]);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ }
+ (*outputFunc)(outputStream, "readonly def\n", 13);
+
+ // copy everything after the encoding
+ if (!strncmp(line, "/Encoding StandardEncoding def", 30)) {
+ line = getNextLine(line);
+ } else {
+ for (line = getNextLine(line);
+ line && strncmp(line, "readonly def", 12);
+ line = getNextLine(line)) ;
+ if (line) {
+ line = getNextLine(line);
+ }
+ }
+ if (line) {
+ (*outputFunc)(outputStream, line, ((char *)file + len) - line);
+ }
+}
+
+char *FoFiType1::getNextLine(char *line) {
+ while (line < (char *)file + len && *line != '\x0a' && *line != '\x0d') {
+ ++line;
+ }
+ if (line < (char *)file + len && *line == '\x0d') {
+ ++line;
+ }
+ if (line < (char *)file + len && *line == '\x0a') {
+ ++line;
+ }
+ if (line >= (char *)file + len) {
+ return NULL;
+ }
+ return line;
+}
+
+void FoFiType1::parse() {
+ char *line, *line1, *p, *p2;
+ char buf[256];
+ char c;
+ int n, code, i, j;
+
+ for (i = 1, line = (char *)file;
+ i <= 100 && line && (!name || !encoding);
+ ++i) {
+
+ // get font name
+ if (!name && !strncmp(line, "/FontName", 9)) {
+ strncpy(buf, line, 255);
+ buf[255] = '\0';
+ if ((p = strchr(buf+9, '/')) &&
+ (p = strtok(p+1, " \t\n\r"))) {
+ name = copyString(p);
+ }
+ line = getNextLine(line);
+
+ // get encoding
+ } else if (!encoding &&
+ !strncmp(line, "/Encoding StandardEncoding def", 30)) {
+ encoding = fofiType1StandardEncoding;
+ } else if (!encoding &&
+ !strncmp(line, "/Encoding 256 array", 19)) {
+ encoding = (char **)gmallocn(256, sizeof(char *));
+ for (j = 0; j < 256; ++j) {
+ encoding[j] = NULL;
+ }
+ for (j = 0, line = getNextLine(line);
+ j < 300 && line && (line1 = getNextLine(line));
+ ++j, line = line1) {
+ if ((n = line1 - line) > 255) {
+ n = 255;
+ }
+ strncpy(buf, line, n);
+ buf[n] = '\0';
+ for (p = buf; *p == ' ' || *p == '\t'; ++p) ;
+ if (!strncmp(p, "dup", 3)) {
+ for (p += 3; *p == ' ' || *p == '\t'; ++p) ;
+ for (p2 = p; *p2 >= '0' && *p2 <= '9'; ++p2) ;
+ if (*p2) {
+ c = *p2;
+ *p2 = '\0';
+ if ((code = atoi(p)) < 256) {
+ *p2 = c;
+ for (p = p2; *p == ' ' || *p == '\t'; ++p) ;
+ if (*p == '/') {
+ ++p;
+ for (p2 = p; *p2 && *p2 != ' ' && *p2 != '\t'; ++p2) ;
+ *p2 = '\0';
+ encoding[code] = copyString(p);
+ }
+ }
+ }
+ } else {
+ p = strtok(buf, " \t\n\r");
+ if (p)
+ {
+ if (!strcmp(p, "def")) break;
+ if (!strcmp(p, "readonly")) break;
+ // the spec does not says this but i'm mantaining old xpdf behaviour that accepts "foo def" as end of the encoding array
+ p = strtok(buf, " \t\n\r");
+ if (p && !strcmp(p, "def")) break;
+ }
+ }
+ }
+ //~ check for getinterval/putinterval junk
+
+ } else {
+ line = getNextLine(line);
+ }
+ }
+
+ parsed = gTrue;
+}
--- /dev/null
+//========================================================================
+//
+// 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
--- /dev/null
+//========================================================================
+//
+// FoFiType1C.cc
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "gmem.h"
+#include "GString.h"
+#include "FoFiEncodings.h"
+#include "FoFiType1C.h"
+
+//------------------------------------------------------------------------
+
+static char hexChars[17] = "0123456789ABCDEF";
+
+//------------------------------------------------------------------------
+// FoFiType1C
+//------------------------------------------------------------------------
+
+FoFiType1C *FoFiType1C::make(char *fileA, int lenA) {
+ FoFiType1C *ff;
+
+ ff = new FoFiType1C(fileA, lenA, gFalse);
+ if (!ff->parse()) {
+ delete ff;
+ return NULL;
+ }
+ return ff;
+}
+
+FoFiType1C *FoFiType1C::load(char *fileName) {
+ FoFiType1C *ff;
+ char *fileA;
+ int lenA;
+
+ if (!(fileA = FoFiBase::readFile(fileName, &lenA))) {
+ return NULL;
+ }
+ ff = new FoFiType1C(fileA, lenA, gTrue);
+ if (!ff->parse()) {
+ delete ff;
+ return NULL;
+ }
+ return ff;
+}
+
+FoFiType1C::FoFiType1C(char *fileA, int lenA, GBool freeFileDataA):
+ FoFiBase(fileA, lenA, freeFileDataA)
+{
+ name = NULL;
+ encoding = NULL;
+ privateDicts = NULL;
+ fdSelect = NULL;
+ charset = NULL;
+}
+
+FoFiType1C::~FoFiType1C() {
+ int i;
+
+ if (name) {
+ delete name;
+ }
+ if (encoding &&
+ encoding != fofiType1StandardEncoding &&
+ encoding != fofiType1ExpertEncoding) {
+ for (i = 0; i < 256; ++i) {
+ gfree(encoding[i]);
+ }
+ gfree(encoding);
+ }
+ if (privateDicts) {
+ gfree(privateDicts);
+ }
+ if (fdSelect) {
+ gfree(fdSelect);
+ }
+ if (charset &&
+ charset != fofiType1CISOAdobeCharset &&
+ charset != fofiType1CExpertCharset &&
+ charset != fofiType1CExpertSubsetCharset) {
+ gfree(charset);
+ }
+}
+
+char *FoFiType1C::getName() {
+ return name ? name->getCString() : (char *)NULL;
+}
+
+char **FoFiType1C::getEncoding() {
+ return encoding;
+}
+
+Gushort *FoFiType1C::getCIDToGIDMap(int *nCIDs) {
+ Gushort *map;
+ int n, i;
+
+ // a CID font's top dict has ROS as the first operator
+ if (topDict.firstOp != 0x0c1e) {
+ *nCIDs = 0;
+ return NULL;
+ }
+
+ // in a CID font, the charset data is the GID-to-CID mapping, so all
+ // we have to do is reverse it
+ n = 0;
+ for (i = 0; i < nGlyphs; ++i) {
+ if (charset[i] > n) {
+ n = charset[i];
+ }
+ }
+ ++n;
+ map = (Gushort *)gmallocn(n, sizeof(Gushort));
+ memset(map, 0, n * sizeof(Gushort));
+ for (i = 0; i < nGlyphs; ++i) {
+ map[charset[i]] = i;
+ }
+ *nCIDs = n;
+ return map;
+}
+
+void FoFiType1C::convertToType1(char **newEncoding, GBool ascii,
+ FoFiOutputFunc outputFunc,
+ void *outputStream) {
+ Type1CEexecBuf eb;
+ Type1CIndex subrIdx;
+ Type1CIndexVal val;
+ char buf[512];
+ char **enc;
+ GBool ok;
+ int i;
+
+ // write header and font dictionary, up to encoding
+ ok = gTrue;
+ (*outputFunc)(outputStream, "%!FontType1-1.0: ", 17);
+ (*outputFunc)(outputStream, name->getCString(), name->getLength());
+ if (topDict.versionSID != 0) {
+ getString(topDict.versionSID, buf, &ok);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ (*outputFunc)(outputStream, "\n", 1);
+ // the dictionary needs room for 12 entries: the following 9, plus
+ // Private and CharStrings (in the eexec section) and FID (which is
+ // added by definefont)
+ (*outputFunc)(outputStream, "12 dict begin\n", 14);
+ (*outputFunc)(outputStream, "/FontInfo 10 dict dup begin\n", 28);
+ if (topDict.versionSID != 0) {
+ (*outputFunc)(outputStream, "/version (", 10);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream, ") readonly def\n", 15);
+ }
+ if (topDict.noticeSID != 0) {
+ getString(topDict.noticeSID, buf, &ok);
+ (*outputFunc)(outputStream, "/Notice (", 9);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream, ") readonly def\n", 15);
+ }
+ if (topDict.copyrightSID != 0) {
+ getString(topDict.copyrightSID, buf, &ok);
+ (*outputFunc)(outputStream, "/Copyright (", 12);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream, ") readonly def\n", 15);
+ }
+ if (topDict.fullNameSID != 0) {
+ getString(topDict.fullNameSID, buf, &ok);
+ (*outputFunc)(outputStream, "/FullName (", 11);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream, ") readonly def\n", 15);
+ }
+ if (topDict.familyNameSID != 0) {
+ getString(topDict.familyNameSID, buf, &ok);
+ (*outputFunc)(outputStream, "/FamilyName (", 13);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream, ") readonly def\n", 15);
+ }
+ if (topDict.weightSID != 0) {
+ getString(topDict.weightSID, buf, &ok);
+ (*outputFunc)(outputStream, "/Weight (", 9);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream, ") readonly def\n", 15);
+ }
+ if (topDict.isFixedPitch) {
+ (*outputFunc)(outputStream, "/isFixedPitch true def\n", 23);
+ } else {
+ (*outputFunc)(outputStream, "/isFixedPitch false def\n", 24);
+ }
+ sprintf(buf, "/ItalicAngle %g def\n", topDict.italicAngle);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ sprintf(buf, "/UnderlinePosition %g def\n", topDict.underlinePosition);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ sprintf(buf, "/UnderlineThickness %g def\n", topDict.underlineThickness);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream, "end readonly def\n", 17);
+ (*outputFunc)(outputStream, "/FontName /", 11);
+ (*outputFunc)(outputStream, name->getCString(), name->getLength());
+ (*outputFunc)(outputStream, " def\n", 5);
+ sprintf(buf, "/PaintType %d def\n", topDict.paintType);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream, "/FontType 1 def\n", 16);
+ sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] readonly def\n",
+ topDict.fontMatrix[0], topDict.fontMatrix[1], topDict.fontMatrix[2],
+ topDict.fontMatrix[3], topDict.fontMatrix[4], topDict.fontMatrix[5]);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ sprintf(buf, "/FontBBox [%g %g %g %g] readonly def\n",
+ topDict.fontBBox[0], topDict.fontBBox[1],
+ topDict.fontBBox[2], topDict.fontBBox[3]);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ sprintf(buf, "/StrokeWidth %g def\n", topDict.strokeWidth);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ if (topDict.uniqueID != 0) {
+ sprintf(buf, "/UniqueID %d def\n", topDict.uniqueID);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+
+ // write the encoding
+ (*outputFunc)(outputStream, "/Encoding ", 10);
+ if (!newEncoding && encoding == fofiType1StandardEncoding) {
+ (*outputFunc)(outputStream, "StandardEncoding def\n", 21);
+ } else {
+ (*outputFunc)(outputStream, "256 array\n", 10);
+ (*outputFunc)(outputStream,
+ "0 1 255 {1 index exch /.notdef put} for\n", 40);
+ enc = newEncoding ? newEncoding : encoding;
+ if(!enc) {
+ fprintf(stderr, "convertToType1: Warning: No Encoding\n");
+ }
+ for (i = 0; i < 256; ++i) {
+ if (enc && enc[i]) {
+ sprintf(buf, "dup %d /%s put\n", i, enc[i]);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ }
+ (*outputFunc)(outputStream, "readonly def\n", 13);
+ }
+ (*outputFunc)(outputStream, "currentdict end\n", 16);
+
+ // start the binary section
+ (*outputFunc)(outputStream, "currentfile eexec\n", 18);
+ eb.outputFunc = outputFunc;
+ eb.outputStream = outputStream;
+ eb.ascii = ascii;
+ eb.r1 = 55665;
+ eb.line = 0;
+
+ // write the private dictionary
+ eexecWrite(&eb, "\x83\xca\x73\xd5");
+ eexecWrite(&eb, "dup /Private 32 dict dup begin\n");
+ eexecWrite(&eb, "/RD {string currentfile exch readstring pop}"
+ " executeonly def\n");
+ eexecWrite(&eb, "/ND {noaccess def} executeonly def\n");
+ eexecWrite(&eb, "/NP {noaccess put} executeonly def\n");
+ eexecWrite(&eb, "/MinFeature {16 16} def\n");
+ eexecWrite(&eb, "/password 5839 def\n");
+ if (privateDicts[0].nBlueValues) {
+ eexecWrite(&eb, "/BlueValues [");
+ for (i = 0; i < privateDicts[0].nBlueValues; ++i) {
+ sprintf(buf, "%s%d", i > 0 ? " " : "", privateDicts[0].blueValues[i]);
+ eexecWrite(&eb, buf);
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ if (privateDicts[0].nOtherBlues) {
+ eexecWrite(&eb, "/OtherBlues [");
+ for (i = 0; i < privateDicts[0].nOtherBlues; ++i) {
+ sprintf(buf, "%s%d", i > 0 ? " " : "", privateDicts[0].otherBlues[i]);
+ eexecWrite(&eb, buf);
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ if (privateDicts[0].nFamilyBlues) {
+ eexecWrite(&eb, "/FamilyBlues [");
+ for (i = 0; i < privateDicts[0].nFamilyBlues; ++i) {
+ sprintf(buf, "%s%d", i > 0 ? " " : "", privateDicts[0].familyBlues[i]);
+ eexecWrite(&eb, buf);
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ if (privateDicts[0].nFamilyOtherBlues) {
+ eexecWrite(&eb, "/FamilyOtherBlues [");
+ for (i = 0; i < privateDicts[0].nFamilyOtherBlues; ++i) {
+ sprintf(buf, "%s%d", i > 0 ? " " : "",
+ privateDicts[0].familyOtherBlues[i]);
+ eexecWrite(&eb, buf);
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ if (privateDicts[0].blueScale != 0.039625) {
+ sprintf(buf, "/BlueScale %g def\n", privateDicts[0].blueScale);
+ eexecWrite(&eb, buf);
+ }
+ if (privateDicts[0].blueShift != 7) {
+ sprintf(buf, "/BlueShift %d def\n", privateDicts[0].blueShift);
+ eexecWrite(&eb, buf);
+ }
+ if (privateDicts[0].blueFuzz != 1) {
+ sprintf(buf, "/BlueFuzz %d def\n", privateDicts[0].blueFuzz);
+ eexecWrite(&eb, buf);
+ }
+ if (privateDicts[0].hasStdHW) {
+ sprintf(buf, "/StdHW [%g] def\n", privateDicts[0].stdHW);
+ eexecWrite(&eb, buf);
+ }
+ if (privateDicts[0].hasStdVW) {
+ sprintf(buf, "/StdVW [%g] def\n", privateDicts[0].stdVW);
+ eexecWrite(&eb, buf);
+ }
+ if (privateDicts[0].nStemSnapH) {
+ eexecWrite(&eb, "/StemSnapH [");
+ for (i = 0; i < privateDicts[0].nStemSnapH; ++i) {
+ sprintf(buf, "%s%g", i > 0 ? " " : "", privateDicts[0].stemSnapH[i]);
+ eexecWrite(&eb, buf);
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ if (privateDicts[0].nStemSnapV) {
+ eexecWrite(&eb, "/StemSnapV [");
+ for (i = 0; i < privateDicts[0].nStemSnapV; ++i) {
+ sprintf(buf, "%s%g", i > 0 ? " " : "", privateDicts[0].stemSnapV[i]);
+ eexecWrite(&eb, buf);
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ if (privateDicts[0].hasForceBold) {
+ sprintf(buf, "/ForceBold %s def\n",
+ privateDicts[0].forceBold ? "true" : "false");
+ eexecWrite(&eb, buf);
+ }
+ if (privateDicts[0].forceBoldThreshold != 0) {
+ sprintf(buf, "/ForceBoldThreshold %g def\n",
+ privateDicts[0].forceBoldThreshold);
+ eexecWrite(&eb, buf);
+ }
+ if (privateDicts[0].languageGroup != 0) {
+ sprintf(buf, "/LanguageGroup %d def\n", privateDicts[0].languageGroup);
+ eexecWrite(&eb, buf);
+ }
+ if (privateDicts[0].expansionFactor != 0.06) {
+ sprintf(buf, "/ExpansionFactor %g def\n", privateDicts[0].expansionFactor);
+ eexecWrite(&eb, buf);
+ }
+
+ // set up subroutines
+ ok = gTrue;
+ getIndex(privateDicts[0].subrsOffset, &subrIdx, &ok);
+ if (!ok) {
+ subrIdx.pos = -1;
+ }
+
+ // write the CharStrings
+ sprintf(buf, "2 index /CharStrings %d dict dup begin\n", nGlyphs);
+ eexecWrite(&eb, buf);
+ for (i = 0; i < nGlyphs; ++i) {
+ ok = gTrue;
+ getIndexVal(&charStringsIdx, i, &val, &ok);
+ if (ok) {
+ getString(charset[i], buf, &ok);
+ if (ok) {
+ eexecCvtGlyph(&eb, buf, val.pos, val.len, &subrIdx, &privateDicts[0]);
+ }
+ }
+ }
+ eexecWrite(&eb, "end\n");
+ eexecWrite(&eb, "end\n");
+ eexecWrite(&eb, "readonly put\n");
+ eexecWrite(&eb, "noaccess put\n");
+ eexecWrite(&eb, "dup /FontName get exch definefont pop\n");
+ eexecWrite(&eb, "mark currentfile closefile\n");
+
+ // trailer
+ if (ascii && eb.line > 0) {
+ (*outputFunc)(outputStream, "\n", 1);
+ }
+ for (i = 0; i < 8; ++i) {
+ (*outputFunc)(outputStream, "0000000000000000000000000000000000000000000000000000000000000000\n", 65);
+ }
+ (*outputFunc)(outputStream, "cleartomark\n", 12);
+}
+
+void FoFiType1C::convertToCIDType0(char *psName,
+ FoFiOutputFunc outputFunc,
+ void *outputStream) {
+ int *cidMap;
+ GString *charStrings;
+ int *charStringOffsets;
+ Type1CIndex subrIdx;
+ Type1CIndexVal val;
+ int nCIDs, gdBytes;
+ char buf[512], buf2[512];
+ GBool ok;
+ int gid, offset, n, i, j, k;
+
+ // compute the CID count and build the CID-to-GID mapping
+ nCIDs = 0;
+ for (i = 0; i < nGlyphs; ++i) {
+ if (charset[i] >= nCIDs) {
+ nCIDs = charset[i] + 1;
+ }
+ }
+ cidMap = (int *)gmallocn(nCIDs, sizeof(int));
+ for (i = 0; i < nCIDs; ++i) {
+ cidMap[i] = -1;
+ }
+ for (i = 0; i < nGlyphs; ++i) {
+ cidMap[charset[i]] = i;
+ }
+
+ // build the charstrings
+ charStrings = new GString();
+ charStringOffsets = (int *)gmallocn(nCIDs + 1, sizeof(int));
+ for (i = 0; i < nCIDs; ++i) {
+ charStringOffsets[i] = charStrings->getLength();
+ if ((gid = cidMap[i]) >= 0) {
+ ok = gTrue;
+ getIndexVal(&charStringsIdx, gid, &val, &ok);
+ if (ok) {
+ getIndex(privateDicts[fdSelect[gid]].subrsOffset, &subrIdx, &ok);
+ if (!ok) {
+ subrIdx.pos = -1;
+ }
+ cvtGlyph(val.pos, val.len, charStrings,
+ &subrIdx, &privateDicts[fdSelect[gid]], gTrue);
+ }
+ }
+ }
+ charStringOffsets[nCIDs] = charStrings->getLength();
+
+ // compute gdBytes = number of bytes needed for charstring offsets
+ // (offset size needs to account for the charstring offset table,
+ // with a worst case of five bytes per entry, plus the charstrings
+ // themselves)
+ i = (nCIDs + 1) * 5 + charStrings->getLength();
+ if (i < 0x100) {
+ gdBytes = 1;
+ } else if (i < 0x10000) {
+ gdBytes = 2;
+ } else if (i < 0x1000000) {
+ gdBytes = 3;
+ } else {
+ gdBytes = 4;
+ }
+
+ // begin the font dictionary
+ (*outputFunc)(outputStream, "/CIDInit /ProcSet findresource begin\n", 37);
+ (*outputFunc)(outputStream, "20 dict begin\n", 14);
+ (*outputFunc)(outputStream, "/CIDFontName /", 14);
+ (*outputFunc)(outputStream, psName, strlen(psName));
+ (*outputFunc)(outputStream, " def\n", 5);
+ (*outputFunc)(outputStream, "/CIDFontType 0 def\n", 19);
+ (*outputFunc)(outputStream, "/CIDSystemInfo 3 dict dup begin\n", 32);
+ if (topDict.registrySID > 0 && topDict.orderingSID > 0) {
+ ok = gTrue;
+ getString(topDict.registrySID, buf, &ok);
+ if (ok) {
+ (*outputFunc)(outputStream, " /Registry (", 13);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream, ") def\n", 6);
+ }
+ ok = gTrue;
+ getString(topDict.orderingSID, buf, &ok);
+ if (ok) {
+ (*outputFunc)(outputStream, " /Ordering (", 13);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream, ") def\n", 6);
+ }
+ } else {
+ (*outputFunc)(outputStream, " /Registry (Adobe) def\n", 24);
+ (*outputFunc)(outputStream, " /Ordering (Identity) def\n", 27);
+ }
+ sprintf(buf, " /Supplement %d def\n", topDict.supplement);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream, "end def\n", 8);
+ if (topDict.hasFontMatrix) {
+ sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] def\n",
+ topDict.fontMatrix[0], topDict.fontMatrix[1],
+ topDict.fontMatrix[2], topDict.fontMatrix[3],
+ topDict.fontMatrix[4], topDict.fontMatrix[5]);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ } else if (privateDicts[0].hasFontMatrix) {
+ (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+ } else {
+ (*outputFunc)(outputStream,
+ "/FontMatrix [0.001 0 0 0.001 0 0] def\n", 38);
+ }
+ sprintf(buf, "/FontBBox [%g %g %g %g] def\n",
+ topDict.fontBBox[0], topDict.fontBBox[1],
+ topDict.fontBBox[2], topDict.fontBBox[3]);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream, "/FontInfo 1 dict dup begin\n", 27);
+ (*outputFunc)(outputStream, " /FSType 8 def\n", 16);
+ (*outputFunc)(outputStream, "end def\n", 8);
+
+ // CIDFont-specific entries
+ sprintf(buf, "/CIDCount %d def\n", nCIDs);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream, "/FDBytes 1 def\n", 15);
+ sprintf(buf, "/GDBytes %d def\n", gdBytes);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream, "/CIDMapOffset 0 def\n", 20);
+ if (topDict.paintType != 0) {
+ sprintf(buf, "/PaintType %d def\n", topDict.paintType);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ sprintf(buf, "/StrokeWidth %g def\n", topDict.strokeWidth);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+
+ // FDArray entry
+ sprintf(buf, "/FDArray %d array\n", nFDs);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ for (i = 0; i < nFDs; ++i) {
+ sprintf(buf, "dup %d 10 dict begin\n", i);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream, "/FontType 1 def\n", 16);
+ if (privateDicts[i].hasFontMatrix) {
+ sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] def\n",
+ privateDicts[i].fontMatrix[0],
+ privateDicts[i].fontMatrix[1],
+ privateDicts[i].fontMatrix[2],
+ privateDicts[i].fontMatrix[3],
+ privateDicts[i].fontMatrix[4],
+ privateDicts[i].fontMatrix[5]);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ } else {
+ (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+ }
+ sprintf(buf, "/PaintType %d def\n", topDict.paintType);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream, "/Private 32 dict begin\n", 23);
+ if (privateDicts[i].nBlueValues) {
+ (*outputFunc)(outputStream, "/BlueValues [", 13);
+ for (j = 0; j < privateDicts[i].nBlueValues; ++j) {
+ sprintf(buf, "%s%d", j > 0 ? " " : "", privateDicts[i].blueValues[j]);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ (*outputFunc)(outputStream, "] def\n", 6);
+ }
+ if (privateDicts[i].nOtherBlues) {
+ (*outputFunc)(outputStream, "/OtherBlues [", 13);
+ for (j = 0; j < privateDicts[i].nOtherBlues; ++j) {
+ sprintf(buf, "%s%d", j > 0 ? " " : "", privateDicts[i].otherBlues[j]);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ (*outputFunc)(outputStream, "] def\n", 6);
+ }
+ if (privateDicts[i].nFamilyBlues) {
+ (*outputFunc)(outputStream, "/FamilyBlues [", 14);
+ for (j = 0; j < privateDicts[i].nFamilyBlues; ++j) {
+ sprintf(buf, "%s%d", j > 0 ? " " : "", privateDicts[i].familyBlues[j]);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ (*outputFunc)(outputStream, "] def\n", 6);
+ }
+ if (privateDicts[i].nFamilyOtherBlues) {
+ (*outputFunc)(outputStream, "/FamilyOtherBlues [", 19);
+ for (j = 0; j < privateDicts[i].nFamilyOtherBlues; ++j) {
+ sprintf(buf, "%s%d", j > 0 ? " " : "",
+ privateDicts[i].familyOtherBlues[j]);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ (*outputFunc)(outputStream, "] def\n", 6);
+ }
+ if (privateDicts[i].blueScale != 0.039625) {
+ sprintf(buf, "/BlueScale %g def\n", privateDicts[i].blueScale);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ if (privateDicts[i].blueShift != 7) {
+ sprintf(buf, "/BlueShift %d def\n", privateDicts[i].blueShift);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ if (privateDicts[i].blueFuzz != 1) {
+ sprintf(buf, "/BlueFuzz %d def\n", privateDicts[i].blueFuzz);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ if (privateDicts[i].hasStdHW) {
+ sprintf(buf, "/StdHW [%g] def\n", privateDicts[i].stdHW);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ if (privateDicts[i].hasStdVW) {
+ sprintf(buf, "/StdVW [%g] def\n", privateDicts[i].stdVW);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ if (privateDicts[i].nStemSnapH) {
+ (*outputFunc)(outputStream, "/StemSnapH [", 12);
+ for (j = 0; j < privateDicts[i].nStemSnapH; ++j) {
+ sprintf(buf, "%s%g", j > 0 ? " " : "", privateDicts[i].stemSnapH[j]);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ (*outputFunc)(outputStream, "] def\n", 6);
+ }
+ if (privateDicts[i].nStemSnapV) {
+ (*outputFunc)(outputStream, "/StemSnapV [", 12);
+ for (j = 0; j < privateDicts[i].nStemSnapV; ++j) {
+ sprintf(buf, "%s%g", j > 0 ? " " : "", privateDicts[i].stemSnapV[j]);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ (*outputFunc)(outputStream, "] def\n", 6);
+ }
+ if (privateDicts[i].hasForceBold) {
+ sprintf(buf, "/ForceBold %s def\n",
+ privateDicts[i].forceBold ? "true" : "false");
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ if (privateDicts[i].forceBoldThreshold != 0) {
+ sprintf(buf, "/ForceBoldThreshold %g def\n",
+ privateDicts[i].forceBoldThreshold);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ if (privateDicts[i].languageGroup != 0) {
+ sprintf(buf, "/LanguageGroup %d def\n", privateDicts[i].languageGroup);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ if (privateDicts[i].expansionFactor != 0.06) {
+ sprintf(buf, "/ExpansionFactor %g def\n",
+ privateDicts[i].expansionFactor);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ (*outputFunc)(outputStream, "currentdict end def\n", 20);
+ (*outputFunc)(outputStream, "currentdict end put\n", 20);
+ }
+ (*outputFunc)(outputStream, "def\n", 4);
+
+ // start the binary section
+ offset = (nCIDs + 1) * (1 + gdBytes);
+ sprintf(buf, "(Hex) %d StartData\n",
+ offset + charStrings->getLength());
+ (*outputFunc)(outputStream, buf, strlen(buf));
+
+ // write the charstring offset (CIDMap) table
+ for (i = 0; i <= nCIDs; i += 6) {
+ for (j = 0; j < 6 && i+j <= nCIDs; ++j) {
+ if (i+j < nCIDs && cidMap[i+j] >= 0) {
+ buf[0] = (char)fdSelect[cidMap[i+j]];
+ } else {
+ buf[0] = (char)0;
+ }
+ n = offset + charStringOffsets[i+j];
+ for (k = gdBytes; k >= 1; --k) {
+ buf[k] = (char)(n & 0xff);
+ n >>= 8;
+ }
+ for (k = 0; k <= gdBytes; ++k) {
+ sprintf(buf2, "%02x", buf[k] & 0xff);
+ (*outputFunc)(outputStream, buf2, 2);
+ }
+ }
+ (*outputFunc)(outputStream, "\n", 1);
+ }
+
+ // write the charstring data
+ n = charStrings->getLength();
+ for (i = 0; i < n; i += 32) {
+ for (j = 0; j < 32 && i+j < n; ++j) {
+ sprintf(buf, "%02x", charStrings->getChar(i+j) & 0xff);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ if (i + 32 >= n) {
+ (*outputFunc)(outputStream, ">", 1);
+ }
+ (*outputFunc)(outputStream, "\n", 1);
+ }
+
+ gfree(charStringOffsets);
+ delete charStrings;
+ gfree(cidMap);
+}
+
+void FoFiType1C::convertToType0(char *psName,
+ FoFiOutputFunc outputFunc,
+ void *outputStream) {
+ int *cidMap;
+ Type1CIndex subrIdx;
+ Type1CIndexVal val;
+ int nCIDs;
+ char buf[512];
+ Type1CEexecBuf eb;
+ GBool ok;
+ int fd, i, j, k;
+
+ // compute the CID count and build the CID-to-GID mapping
+ nCIDs = 0;
+ for (i = 0; i < nGlyphs; ++i) {
+ if (charset[i] >= nCIDs) {
+ nCIDs = charset[i] + 1;
+ }
+ }
+ cidMap = (int *)gmallocn(nCIDs, sizeof(int));
+ for (i = 0; i < nCIDs; ++i) {
+ cidMap[i] = -1;
+ }
+ for (i = 0; i < nGlyphs; ++i) {
+ cidMap[charset[i]] = i;
+ }
+
+ // write the descendant Type 1 fonts
+ for (i = 0; i < nCIDs; i += 256) {
+
+ //~ this assumes that all CIDs in this block have the same FD --
+ //~ to handle multiple FDs correctly, need to somehow divide the
+ //~ font up by FD
+ fd = 0;
+ for (j = 0; j < 256 && i+j < nCIDs; ++j) {
+ if (cidMap[i+j] >= 0) {
+ fd = fdSelect[cidMap[i+j]];
+ break;
+ }
+ }
+
+ // font dictionary (unencrypted section)
+ (*outputFunc)(outputStream, "16 dict begin\n", 14);
+ (*outputFunc)(outputStream, "/FontName /", 11);
+ (*outputFunc)(outputStream, psName, strlen(psName));
+ sprintf(buf, "_%02x def\n", i >> 8);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ (*outputFunc)(outputStream, "/FontType 1 def\n", 16);
+ if (privateDicts[fd].hasFontMatrix) {
+ sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] def\n",
+ privateDicts[fd].fontMatrix[0],
+ privateDicts[fd].fontMatrix[1],
+ privateDicts[fd].fontMatrix[2],
+ privateDicts[fd].fontMatrix[3],
+ privateDicts[fd].fontMatrix[4],
+ privateDicts[fd].fontMatrix[5]);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ } else if (topDict.hasFontMatrix) {
+ (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+ } else {
+ (*outputFunc)(outputStream,
+ "/FontMatrix [0.001 0 0 0.001 0 0] def\n", 38);
+ }
+ sprintf(buf, "/FontBBox [%g %g %g %g] def\n",
+ topDict.fontBBox[0], topDict.fontBBox[1],
+ topDict.fontBBox[2], topDict.fontBBox[3]);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ sprintf(buf, "/PaintType %d def\n", topDict.paintType);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ if (topDict.paintType != 0) {
+ sprintf(buf, "/StrokeWidth %g def\n", topDict.strokeWidth);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ (*outputFunc)(outputStream, "/Encoding 256 array\n", 20);
+ for (j = 0; j < 256 && i+j < nCIDs; ++j) {
+ sprintf(buf, "dup %d /c%02x put\n", j, j);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ if (j < 256) {
+ sprintf(buf, "%d 1 255 { 1 index exch /.notdef put } for\n", j);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ (*outputFunc)(outputStream, "readonly def\n", 13);
+ (*outputFunc)(outputStream, "currentdict end\n", 16);
+
+ // start the binary section
+ (*outputFunc)(outputStream, "currentfile eexec\n", 18);
+ eb.outputFunc = outputFunc;
+ eb.outputStream = outputStream;
+ eb.ascii = gTrue;
+ eb.r1 = 55665;
+ eb.line = 0;
+
+ // start the private dictionary
+ eexecWrite(&eb, "\x83\xca\x73\xd5");
+ eexecWrite(&eb, "dup /Private 32 dict dup begin\n");
+ eexecWrite(&eb, "/RD {string currentfile exch readstring pop}"
+ " executeonly def\n");
+ eexecWrite(&eb, "/ND {noaccess def} executeonly def\n");
+ eexecWrite(&eb, "/NP {noaccess put} executeonly def\n");
+ eexecWrite(&eb, "/MinFeature {16 16} def\n");
+ eexecWrite(&eb, "/password 5839 def\n");
+ if (privateDicts[fd].nBlueValues) {
+ eexecWrite(&eb, "/BlueValues [");
+ for (k = 0; k < privateDicts[fd].nBlueValues; ++k) {
+ sprintf(buf, "%s%d", k > 0 ? " " : "", privateDicts[fd].blueValues[k]);
+ eexecWrite(&eb, buf);
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ if (privateDicts[fd].nOtherBlues) {
+ eexecWrite(&eb, "/OtherBlues [");
+ for (k = 0; k < privateDicts[fd].nOtherBlues; ++k) {
+ sprintf(buf, "%s%d", k > 0 ? " " : "", privateDicts[fd].otherBlues[k]);
+ eexecWrite(&eb, buf);
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ if (privateDicts[fd].nFamilyBlues) {
+ eexecWrite(&eb, "/FamilyBlues [");
+ for (k = 0; k < privateDicts[fd].nFamilyBlues; ++k) {
+ sprintf(buf, "%s%d", k > 0 ? " " : "",
+ privateDicts[fd].familyBlues[k]);
+ eexecWrite(&eb, buf);
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ if (privateDicts[fd].nFamilyOtherBlues) {
+ eexecWrite(&eb, "/FamilyOtherBlues [");
+ for (k = 0; k < privateDicts[fd].nFamilyOtherBlues; ++k) {
+ sprintf(buf, "%s%d", k > 0 ? " " : "",
+ privateDicts[fd].familyOtherBlues[k]);
+ eexecWrite(&eb, buf);
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ if (privateDicts[fd].blueScale != 0.039625) {
+ sprintf(buf, "/BlueScale %g def\n", privateDicts[fd].blueScale);
+ eexecWrite(&eb, buf);
+ }
+ if (privateDicts[fd].blueShift != 7) {
+ sprintf(buf, "/BlueShift %d def\n", privateDicts[fd].blueShift);
+ eexecWrite(&eb, buf);
+ }
+ if (privateDicts[fd].blueFuzz != 1) {
+ sprintf(buf, "/BlueFuzz %d def\n", privateDicts[fd].blueFuzz);
+ eexecWrite(&eb, buf);
+ }
+ if (privateDicts[fd].hasStdHW) {
+ sprintf(buf, "/StdHW [%g] def\n", privateDicts[fd].stdHW);
+ eexecWrite(&eb, buf);
+ }
+ if (privateDicts[fd].hasStdVW) {
+ sprintf(buf, "/StdVW [%g] def\n", privateDicts[fd].stdVW);
+ eexecWrite(&eb, buf);
+ }
+ if (privateDicts[fd].nStemSnapH) {
+ eexecWrite(&eb, "/StemSnapH [");
+ for (k = 0; k < privateDicts[fd].nStemSnapH; ++k) {
+ sprintf(buf, "%s%g", k > 0 ? " " : "", privateDicts[fd].stemSnapH[k]);
+ eexecWrite(&eb, buf);
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ if (privateDicts[fd].nStemSnapV) {
+ eexecWrite(&eb, "/StemSnapV [");
+ for (k = 0; k < privateDicts[fd].nStemSnapV; ++k) {
+ sprintf(buf, "%s%g", k > 0 ? " " : "", privateDicts[fd].stemSnapV[k]);
+ eexecWrite(&eb, buf);
+ }
+ eexecWrite(&eb, "] def\n");
+ }
+ if (privateDicts[fd].hasForceBold) {
+ sprintf(buf, "/ForceBold %s def\n",
+ privateDicts[fd].forceBold ? "true" : "false");
+ eexecWrite(&eb, buf);
+ }
+ if (privateDicts[fd].forceBoldThreshold != 0) {
+ sprintf(buf, "/ForceBoldThreshold %g def\n",
+ privateDicts[fd].forceBoldThreshold);
+ eexecWrite(&eb, buf);
+ }
+ if (privateDicts[fd].languageGroup != 0) {
+ sprintf(buf, "/LanguageGroup %d def\n", privateDicts[fd].languageGroup);
+ eexecWrite(&eb, buf);
+ }
+ if (privateDicts[fd].expansionFactor != 0.06) {
+ sprintf(buf, "/ExpansionFactor %g def\n",
+ privateDicts[fd].expansionFactor);
+ eexecWrite(&eb, buf);
+ }
+
+ // set up the subroutines
+ ok = gTrue;
+ getIndex(privateDicts[fd].subrsOffset, &subrIdx, &ok);
+ if (!ok) {
+ subrIdx.pos = -1;
+ }
+
+ // start the CharStrings
+ sprintf(buf, "2 index /CharStrings 256 dict dup begin\n");
+ eexecWrite(&eb, buf);
+
+ // write the .notdef CharString
+ ok = gTrue;
+ getIndexVal(&charStringsIdx, 0, &val, &ok);
+ if (ok) {
+ eexecCvtGlyph(&eb, ".notdef", val.pos, val.len,
+ &subrIdx, &privateDicts[fd]);
+ }
+
+ // write the CharStrings
+ for (j = 0; j < 256 && i+j < nCIDs; ++j) {
+ if (cidMap[i+j] >= 0) {
+ ok = gTrue;
+ getIndexVal(&charStringsIdx, cidMap[i+j], &val, &ok);
+ if (ok) {
+ sprintf(buf, "c%02x", j);
+ eexecCvtGlyph(&eb, buf, val.pos, val.len,
+ &subrIdx, &privateDicts[fd]);
+ }
+ }
+ }
+ eexecWrite(&eb, "end\n");
+ eexecWrite(&eb, "end\n");
+ eexecWrite(&eb, "readonly put\n");
+ eexecWrite(&eb, "noaccess put\n");
+ eexecWrite(&eb, "dup /FontName get exch definefont pop\n");
+ eexecWrite(&eb, "mark currentfile closefile\n");
+
+ // trailer
+ if (eb.line > 0) {
+ (*outputFunc)(outputStream, "\n", 1);
+ }
+ for (j = 0; j < 8; ++j) {
+ (*outputFunc)(outputStream, "0000000000000000000000000000000000000000000000000000000000000000\n", 65);
+ }
+ (*outputFunc)(outputStream, "cleartomark\n", 12);
+ }
+
+ // write the Type 0 parent font
+ (*outputFunc)(outputStream, "16 dict begin\n", 14);
+ (*outputFunc)(outputStream, "/FontName /", 11);
+ (*outputFunc)(outputStream, psName, strlen(psName));
+ (*outputFunc)(outputStream, " def\n", 5);
+ (*outputFunc)(outputStream, "/FontType 0 def\n", 16);
+ if (topDict.hasFontMatrix) {
+ sprintf(buf, "/FontMatrix [%g %g %g %g %g %g] def\n",
+ topDict.fontMatrix[0], topDict.fontMatrix[1],
+ topDict.fontMatrix[2], topDict.fontMatrix[3],
+ topDict.fontMatrix[4], topDict.fontMatrix[5]);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ } else {
+ (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30);
+ }
+ (*outputFunc)(outputStream, "/FMapType 2 def\n", 16);
+ (*outputFunc)(outputStream, "/Encoding [\n", 12);
+ for (i = 0; i < nCIDs; i += 256) {
+ sprintf(buf, "%d\n", i >> 8);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ (*outputFunc)(outputStream, "] def\n", 6);
+ (*outputFunc)(outputStream, "/FDepVector [\n", 14);
+ for (i = 0; i < nCIDs; i += 256) {
+ (*outputFunc)(outputStream, "/", 1);
+ (*outputFunc)(outputStream, psName, strlen(psName));
+ sprintf(buf, "_%02x findfont\n", i >> 8);
+ (*outputFunc)(outputStream, buf, strlen(buf));
+ }
+ (*outputFunc)(outputStream, "] def\n", 6);
+ (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40);
+
+ gfree(cidMap);
+}
+
+void FoFiType1C::eexecCvtGlyph(Type1CEexecBuf *eb, char *glyphName,
+ int offset, int nBytes,
+ Type1CIndex *subrIdx,
+ Type1CPrivateDict *pDict) {
+ char buf[512];
+ GString *charBuf;
+
+ // generate the charstring
+ charBuf = new GString();
+ cvtGlyph(offset, nBytes, charBuf, subrIdx, pDict, gTrue);
+
+ sprintf(buf, "/%s %d RD ", glyphName, charBuf->getLength());
+ eexecWrite(eb, buf);
+ eexecWriteCharstring(eb, (Guchar *)charBuf->getCString(),
+ charBuf->getLength());
+ eexecWrite(eb, " ND\n");
+
+ delete charBuf;
+}
+
+void FoFiType1C::cvtGlyph(int offset, int nBytes, GString *charBuf,
+ Type1CIndex *subrIdx, Type1CPrivateDict *pDict,
+ GBool top) {
+ Type1CIndexVal val;
+ GBool ok, dFP;
+ double d, dx, dy;
+ Gushort r2;
+ Guchar byte;
+ int pos, subrBias, start, i, k;
+
+ start = charBuf->getLength();
+ if (top) {
+ charBuf->append((char)73);
+ charBuf->append((char)58);
+ charBuf->append((char)147);
+ charBuf->append((char)134);
+ nOps = 0;
+ nHints = 0;
+ firstOp = gTrue;
+ openPath = gFalse;
+ }
+
+ pos = offset;
+ while (pos < offset + nBytes) {
+ ok = gTrue;
+ pos = getOp(pos, gTrue, &ok);
+ if (!ok) {
+ break;
+ }
+ if (!ops[nOps - 1].isNum) {
+ --nOps; // drop the operator
+ switch (ops[nOps].op) {
+ case 0x0001: // hstem
+ if (firstOp) {
+ cvtGlyphWidth(nOps & 1, charBuf, pDict);
+ firstOp = gFalse;
+ }
+ if (nOps & 1) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 hstem", nOps);
+ }
+ d = 0;
+ dFP = gFalse;
+ for (k = 0; k < nOps; k += 2) {
+ // convert Type 2 edge hints (-20 or -21) to Type 1 ghost hints
+ if (ops[k+1].num < 0) {
+ d += ops[k].num + ops[k+1].num;
+ dFP |= ops[k].isFP | ops[k+1].isFP;
+ cvtNum(d, dFP, charBuf);
+ cvtNum(-ops[k+1].num, ops[k+1].isFP, charBuf);
+ } else {
+ d += ops[k].num;
+ dFP |= ops[k].isFP;
+ cvtNum(d, dFP, charBuf);
+ cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+ d += ops[k+1].num;
+ dFP |= ops[k+1].isFP;
+ }
+ charBuf->append((char)1);
+ }
+ nHints += nOps / 2;
+ nOps = 0;
+ break;
+ case 0x0003: // vstem
+ if (firstOp) {
+ cvtGlyphWidth(nOps & 1, charBuf, pDict);
+ firstOp = gFalse;
+ }
+ if (nOps & 1) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 vstem", nOps);
+ }
+ d = 0;
+ dFP = gFalse;
+ for (k = 0; k < nOps; k += 2) {
+ // convert Type 2 edge hints (-20 or -21) to Type 1 ghost hints
+ if (ops[k+1].num < 0) {
+ d += ops[k].num + ops[k+1].num;
+ dFP |= ops[k].isFP | ops[k+1].isFP;
+ cvtNum(d, dFP, charBuf);
+ cvtNum(-ops[k+1].num, ops[k+1].isFP, charBuf);
+ } else {
+ d += ops[k].num;
+ dFP |= ops[k].isFP;
+ cvtNum(d, dFP, charBuf);
+ cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+ d += ops[k+1].num;
+ dFP |= ops[k+1].isFP;
+ }
+ charBuf->append((char)3);
+ }
+ nHints += nOps / 2;
+ nOps = 0;
+ break;
+ case 0x0004: // vmoveto
+ if (firstOp) {
+ cvtGlyphWidth(nOps == 2, charBuf, pDict);
+ firstOp = gFalse;
+ }
+ if (openPath) {
+ charBuf->append((char)9);
+ openPath = gFalse;
+ }
+ if (nOps != 1) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 vmoveto", nOps);
+ }
+ cvtNum(ops[0].num, ops[0].isFP, charBuf);
+ charBuf->append((char)4);
+ nOps = 0;
+ break;
+ case 0x0005: // rlineto
+ if (nOps < 2 || nOps % 2 != 0) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 rlineto", nOps);
+ }
+ for (k = 0; k < nOps; k += 2) {
+ cvtNum(ops[k].num, ops[k].isFP, charBuf);
+ cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+ charBuf->append((char)5);
+ }
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x0006: // hlineto
+ if (nOps < 1) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 hlineto", nOps);
+ }
+ for (k = 0; k < nOps; ++k) {
+ cvtNum(ops[k].num, ops[k].isFP, charBuf);
+ charBuf->append((char)((k & 1) ? 7 : 6));
+ }
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x0007: // vlineto
+ if (nOps < 1) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 vlineto", nOps);
+ }
+ for (k = 0; k < nOps; ++k) {
+ cvtNum(ops[k].num, ops[k].isFP, charBuf);
+ charBuf->append((char)((k & 1) ? 6 : 7));
+ }
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x0008: // rrcurveto
+ if (nOps < 6 || nOps % 6 != 0) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 rrcurveto", nOps);
+ }
+ for (k = 0; k < nOps; k += 6) {
+ cvtNum(ops[k].num, ops[k].isFP, charBuf);
+ cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+ cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+ cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+ cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
+ cvtNum(ops[k+5].num, ops[k+5].isFP, charBuf);
+ charBuf->append((char)8);
+ }
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x000a: // callsubr
+ if (nOps >= 1) {
+ subrBias = (subrIdx->len < 1240)
+ ? 107 : (subrIdx->len < 33900) ? 1131 : 32768;
+ k = subrBias + (int)ops[nOps - 1].num;
+ --nOps;
+ ok = gTrue;
+ getIndexVal(subrIdx, k, &val, &ok);
+ if (ok) {
+ cvtGlyph(val.pos, val.len, charBuf, subrIdx, pDict, gFalse);
+ }
+ } else {
+ //~ error(-1, "Too few args to Type 2 callsubr");
+ }
+ // don't clear the stack
+ break;
+ case 0x000b: // return
+ // don't clear the stack
+ break;
+ case 0x000e: // endchar / seac
+ if (firstOp) {
+ cvtGlyphWidth(nOps == 1 || nOps == 5, charBuf, pDict);
+ firstOp = gFalse;
+ }
+ if (openPath) {
+ charBuf->append((char)9);
+ openPath = gFalse;
+ }
+ if (nOps == 4) {
+ cvtNum(0, gFalse, charBuf);
+ cvtNum(ops[0].num, ops[0].isFP, charBuf);
+ cvtNum(ops[1].num, ops[1].isFP, charBuf);
+ cvtNum(ops[2].num, ops[2].isFP, charBuf);
+ cvtNum(ops[3].num, ops[3].isFP, charBuf);
+ charBuf->append((char)12)->append((char)6);
+ } else if (nOps == 0) {
+ charBuf->append((char)14);
+ } else {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 endchar", nOps);
+ }
+ nOps = 0;
+ break;
+ case 0x000f: // (obsolete)
+ // this op is ignored, but we need the glyph width
+ if (firstOp) {
+ cvtGlyphWidth(nOps > 0, charBuf, pDict);
+ firstOp = gFalse;
+ }
+ nOps = 0;
+ break;
+ case 0x0010: // blend
+ //~ error(-1, "Unimplemented Type 2 charstring op: %d", file[i]);
+ nOps = 0;
+ break;
+ case 0x0012: // hstemhm
+ // ignored
+ if (firstOp) {
+ cvtGlyphWidth(nOps & 1, charBuf, pDict);
+ firstOp = gFalse;
+ }
+ if (nOps & 1) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 hstemhm", nOps);
+ }
+ nHints += nOps / 2;
+ nOps = 0;
+ break;
+ case 0x0013: // hintmask
+ // ignored
+ if (firstOp) {
+ cvtGlyphWidth(nOps & 1, charBuf, pDict);
+ firstOp = gFalse;
+ }
+ if (nOps > 0) {
+ if (nOps & 1) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 hintmask/vstemhm",
+ //~ nOps);
+ }
+ nHints += nOps / 2;
+ }
+ pos += (nHints + 7) >> 3;
+ nOps = 0;
+ break;
+ case 0x0014: // cntrmask
+ // ignored
+ if (firstOp) {
+ cvtGlyphWidth(nOps & 1, charBuf, pDict);
+ firstOp = gFalse;
+ }
+ if (nOps > 0) {
+ if (nOps & 1) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 cntrmask/vstemhm",
+ //~ nOps);
+ }
+ nHints += nOps / 2;
+ }
+ pos += (nHints + 7) >> 3;
+ nOps = 0;
+ break;
+ case 0x0015: // rmoveto
+ if (firstOp) {
+ cvtGlyphWidth(nOps == 3, charBuf, pDict);
+ firstOp = gFalse;
+ }
+ if (openPath) {
+ charBuf->append((char)9);
+ openPath = gFalse;
+ }
+ if (nOps != 2) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 rmoveto", nOps);
+ }
+ cvtNum(ops[0].num, ops[0].isFP, charBuf);
+ cvtNum(ops[1].num, ops[1].isFP, charBuf);
+ charBuf->append((char)21);
+ nOps = 0;
+ break;
+ case 0x0016: // hmoveto
+ if (firstOp) {
+ cvtGlyphWidth(nOps == 2, charBuf, pDict);
+ firstOp = gFalse;
+ }
+ if (openPath) {
+ charBuf->append((char)9);
+ openPath = gFalse;
+ }
+ if (nOps != 1) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 hmoveto", nOps);
+ }
+ cvtNum(ops[0].num, ops[0].isFP, charBuf);
+ charBuf->append((char)22);
+ nOps = 0;
+ break;
+ case 0x0017: // vstemhm
+ // ignored
+ if (firstOp) {
+ cvtGlyphWidth(nOps & 1, charBuf, pDict);
+ firstOp = gFalse;
+ }
+ if (nOps & 1) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 vstemhm", nOps);
+ }
+ nHints += nOps / 2;
+ nOps = 0;
+ break;
+ case 0x0018: // rcurveline
+ if (nOps < 8 || (nOps - 2) % 6 != 0) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 rcurveline", nOps);
+ }
+ for (k = 0; k < nOps - 2; k += 6) {
+ cvtNum(ops[k].num, ops[k].isFP, charBuf);
+ cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+ cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+ cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+ cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
+ cvtNum(ops[k+5].num, ops[k+5].isFP, charBuf);
+ charBuf->append((char)8);
+ }
+ cvtNum(ops[k].num, ops[k].isFP, charBuf);
+ cvtNum(ops[k+1].num, ops[k].isFP, charBuf);
+ charBuf->append((char)5);
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x0019: // rlinecurve
+ if (nOps < 8 || (nOps - 6) % 2 != 0) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 rlinecurve", nOps);
+ }
+ for (k = 0; k < nOps - 6; k += 2) {
+ cvtNum(ops[k].num, ops[k].isFP, charBuf);
+ cvtNum(ops[k+1].num, ops[k].isFP, charBuf);
+ charBuf->append((char)5);
+ }
+ cvtNum(ops[k].num, ops[k].isFP, charBuf);
+ cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+ cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+ cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+ cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
+ cvtNum(ops[k+5].num, ops[k+5].isFP, charBuf);
+ charBuf->append((char)8);
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x001a: // vvcurveto
+ if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 vvcurveto", nOps);
+ }
+ if (nOps % 2 == 1) {
+ cvtNum(ops[0].num, ops[0].isFP, charBuf);
+ cvtNum(ops[1].num, ops[1].isFP, charBuf);
+ cvtNum(ops[2].num, ops[2].isFP, charBuf);
+ cvtNum(ops[3].num, ops[3].isFP, charBuf);
+ cvtNum(0, gFalse, charBuf);
+ cvtNum(ops[4].num, ops[4].isFP, charBuf);
+ charBuf->append((char)8);
+ k = 5;
+ } else {
+ k = 0;
+ }
+ for (; k < nOps; k += 4) {
+ cvtNum(0, gFalse, charBuf);
+ cvtNum(ops[k].num, ops[k].isFP, charBuf);
+ cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+ cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+ cvtNum(0, gFalse, charBuf);
+ cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+ charBuf->append((char)8);
+ }
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x001b: // hhcurveto
+ if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 hhcurveto", nOps);
+ }
+ if (nOps % 2 == 1) {
+ cvtNum(ops[1].num, ops[1].isFP, charBuf);
+ cvtNum(ops[0].num, ops[0].isFP, charBuf);
+ cvtNum(ops[2].num, ops[2].isFP, charBuf);
+ cvtNum(ops[3].num, ops[3].isFP, charBuf);
+ cvtNum(ops[4].num, ops[4].isFP, charBuf);
+ cvtNum(0, gFalse, charBuf);
+ charBuf->append((char)8);
+ k = 5;
+ } else {
+ k = 0;
+ }
+ for (; k < nOps; k += 4) {
+ cvtNum(ops[k].num, ops[k].isFP, charBuf);
+ cvtNum(0, gFalse, charBuf);
+ cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+ cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+ cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+ cvtNum(0, gFalse, charBuf);
+ charBuf->append((char)8);
+ }
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x001d: // callgsubr
+ if (nOps >= 1) {
+ k = gsubrBias + (int)ops[nOps - 1].num;
+ --nOps;
+ ok = gTrue;
+ getIndexVal(&gsubrIdx, k, &val, &ok);
+ if (ok) {
+ cvtGlyph(val.pos, val.len, charBuf, subrIdx, pDict, gFalse);
+ }
+ } else {
+ //~ error(-1, "Too few args to Type 2 callgsubr");
+ }
+ // don't clear the stack
+ break;
+ case 0x001e: // vhcurveto
+ if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 vhcurveto", nOps);
+ }
+ for (k = 0; k < nOps && k != nOps-5; k += 4) {
+ if (k % 8 == 0) {
+ cvtNum(ops[k].num, ops[k].isFP, charBuf);
+ cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+ cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+ cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+ charBuf->append((char)30);
+ } else {
+ cvtNum(ops[k].num, ops[k].isFP, charBuf);
+ cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+ cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+ cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+ charBuf->append((char)31);
+ }
+ }
+ if (k == nOps-5) {
+ if (k % 8 == 0) {
+ cvtNum(0, gFalse, charBuf);
+ cvtNum(ops[k].num, ops[k].isFP, charBuf);
+ cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+ cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+ cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+ cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
+ } else {
+ cvtNum(ops[k].num, ops[k].isFP, charBuf);
+ cvtNum(0, gFalse, charBuf);
+ cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+ cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+ cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
+ cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+ }
+ charBuf->append((char)8);
+ }
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x001f: // hvcurveto
+ if (nOps < 4 || !(nOps % 4 == 0 || (nOps-1) % 4 == 0)) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 hvcurveto", nOps);
+ }
+ for (k = 0; k < nOps && k != nOps-5; k += 4) {
+ if (k % 8 == 0) {
+ cvtNum(ops[k].num, ops[k].isFP, charBuf);
+ cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+ cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+ cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+ charBuf->append((char)31);
+ } else {
+ cvtNum(ops[k].num, ops[k].isFP, charBuf);
+ cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+ cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+ cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+ charBuf->append((char)30);
+ }
+ }
+ if (k == nOps-5) {
+ if (k % 8 == 0) {
+ cvtNum(ops[k].num, ops[k].isFP, charBuf);
+ cvtNum(0, gFalse, charBuf);
+ cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+ cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+ cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
+ cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+ } else {
+ cvtNum(0, gFalse, charBuf);
+ cvtNum(ops[k].num, ops[k].isFP, charBuf);
+ cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);
+ cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);
+ cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);
+ cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);
+ }
+ charBuf->append((char)8);
+ }
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x0c00: // dotsection (should be Type 1 only?)
+ // ignored
+ nOps = 0;
+ break;
+ case 0x0c03: // and
+ case 0x0c04: // or
+ case 0x0c05: // not
+ case 0x0c08: // store
+ case 0x0c09: // abs
+ case 0x0c0a: // add
+ case 0x0c0b: // sub
+ case 0x0c0c: // div
+ case 0x0c0d: // load
+ case 0x0c0e: // neg
+ case 0x0c0f: // eq
+ case 0x0c12: // drop
+ case 0x0c14: // put
+ case 0x0c15: // get
+ case 0x0c16: // ifelse
+ case 0x0c17: // random
+ case 0x0c18: // mul
+ case 0x0c1a: // sqrt
+ case 0x0c1b: // dup
+ case 0x0c1c: // exch
+ case 0x0c1d: // index
+ case 0x0c1e: // roll
+ //~ error(-1, "Unimplemented Type 2 charstring op: 12.%d", file[i+1]);
+ nOps = 0;
+ break;
+ case 0x0c22: // hflex
+ if (nOps != 7) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 hflex", nOps);
+ }
+ cvtNum(ops[0].num, ops[0].isFP, charBuf);
+ cvtNum(0, gFalse, charBuf);
+ cvtNum(ops[1].num, ops[1].isFP, charBuf);
+ cvtNum(ops[2].num, ops[2].isFP, charBuf);
+ cvtNum(ops[3].num, ops[3].isFP, charBuf);
+ cvtNum(0, gFalse, charBuf);
+ charBuf->append((char)8);
+ cvtNum(ops[4].num, ops[4].isFP, charBuf);
+ cvtNum(0, gFalse, charBuf);
+ cvtNum(ops[5].num, ops[5].isFP, charBuf);
+ cvtNum(-ops[2].num, ops[2].isFP, charBuf);
+ cvtNum(ops[6].num, ops[6].isFP, charBuf);
+ cvtNum(0, gFalse, charBuf);
+ charBuf->append((char)8);
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x0c23: // flex
+ if (nOps != 13) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 flex", nOps);
+ }
+ cvtNum(ops[0].num, ops[0].isFP, charBuf);
+ cvtNum(ops[1].num, ops[1].isFP, charBuf);
+ cvtNum(ops[2].num, ops[2].isFP, charBuf);
+ cvtNum(ops[3].num, ops[3].isFP, charBuf);
+ cvtNum(ops[4].num, ops[4].isFP, charBuf);
+ cvtNum(ops[5].num, ops[5].isFP, charBuf);
+ charBuf->append((char)8);
+ cvtNum(ops[6].num, ops[6].isFP, charBuf);
+ cvtNum(ops[7].num, ops[7].isFP, charBuf);
+ cvtNum(ops[8].num, ops[8].isFP, charBuf);
+ cvtNum(ops[9].num, ops[9].isFP, charBuf);
+ cvtNum(ops[10].num, ops[10].isFP, charBuf);
+ cvtNum(ops[11].num, ops[11].isFP, charBuf);
+ charBuf->append((char)8);
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x0c24: // hflex1
+ if (nOps != 9) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 hflex1", nOps);
+ }
+ cvtNum(ops[0].num, ops[0].isFP, charBuf);
+ cvtNum(ops[1].num, ops[1].isFP, charBuf);
+ cvtNum(ops[2].num, ops[2].isFP, charBuf);
+ cvtNum(ops[3].num, ops[3].isFP, charBuf);
+ cvtNum(ops[4].num, ops[4].isFP, charBuf);
+ cvtNum(0, gFalse, charBuf);
+ charBuf->append((char)8);
+ cvtNum(ops[5].num, ops[5].isFP, charBuf);
+ cvtNum(0, gFalse, charBuf);
+ cvtNum(ops[6].num, ops[6].isFP, charBuf);
+ cvtNum(ops[7].num, ops[7].isFP, charBuf);
+ cvtNum(ops[8].num, ops[8].isFP, charBuf);
+ cvtNum(-(ops[1].num + ops[3].num + ops[7].num),
+ ops[1].isFP | ops[3].isFP | ops[7].isFP, charBuf);
+ charBuf->append((char)8);
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ case 0x0c25: // flex1
+ if (nOps != 11) {
+ //~ error(-1, "Wrong number of args (%d) to Type 2 flex1", nOps);
+ }
+ cvtNum(ops[0].num, ops[0].isFP, charBuf);
+ cvtNum(ops[1].num, ops[1].isFP, charBuf);
+ cvtNum(ops[2].num, ops[2].isFP, charBuf);
+ cvtNum(ops[3].num, ops[3].isFP, charBuf);
+ cvtNum(ops[4].num, ops[4].isFP, charBuf);
+ cvtNum(ops[5].num, ops[5].isFP, charBuf);
+ charBuf->append((char)8);
+ cvtNum(ops[6].num, ops[6].isFP, charBuf);
+ cvtNum(ops[7].num, ops[7].isFP, charBuf);
+ cvtNum(ops[8].num, ops[8].isFP, charBuf);
+ cvtNum(ops[9].num, ops[9].isFP, charBuf);
+ dx = ops[0].num + ops[2].num + ops[4].num + ops[6].num + ops[8].num;
+ dy = ops[1].num + ops[3].num + ops[5].num + ops[7].num + ops[9].num;
+ if (fabs(dx) > fabs(dy)) {
+ cvtNum(ops[10].num, ops[10].isFP, charBuf);
+ cvtNum(-dy, ops[1].isFP | ops[3].isFP | ops[5].isFP |
+ ops[7].isFP | ops[9].isFP, charBuf);
+ } else {
+ cvtNum(-dx, ops[0].isFP | ops[2].isFP | ops[4].isFP |
+ ops[6].isFP | ops[8].isFP, charBuf);
+ cvtNum(ops[10].num, ops[10].isFP, charBuf);
+ }
+ charBuf->append((char)8);
+ nOps = 0;
+ openPath = gTrue;
+ break;
+ default:
+ //~ error(-1, "Illegal Type 2 charstring op: %04x",
+ //~ ops[nOps].op);
+ nOps = 0;
+ break;
+ }
+ }
+ }
+
+ // charstring encryption
+ if (top) {
+ r2 = 4330;
+ for (i = start; i < charBuf->getLength(); ++i) {
+ byte = charBuf->getChar(i) ^ (r2 >> 8);
+ charBuf->setChar(i, byte);
+ r2 = (byte + r2) * 52845 + 22719;
+ }
+ }
+}
+
+void FoFiType1C::cvtGlyphWidth(GBool useOp, GString *charBuf,
+ Type1CPrivateDict *pDict) {
+ double w;
+ GBool wFP;
+ int i;
+
+ if (useOp) {
+ w = pDict->nominalWidthX + ops[0].num;
+ wFP = pDict->nominalWidthXFP | ops[0].isFP;
+ for (i = 1; i < nOps; ++i) {
+ ops[i-1] = ops[i];
+ }
+ --nOps;
+ } else {
+ w = pDict->defaultWidthX;
+ wFP = pDict->defaultWidthXFP;
+ }
+ cvtNum(0, gFalse, charBuf);
+ cvtNum(w, wFP, charBuf);
+ charBuf->append((char)13);
+}
+
+void FoFiType1C::cvtNum(double x, GBool isFP, GString *charBuf) {
+ Guchar buf[12];
+ int y, n;
+
+ n = 0;
+ if (isFP) {
+ if (x >= -32768 && x < 32768) {
+ y = (int)(x * 256.0);
+ buf[0] = 255;
+ buf[1] = (Guchar)(y >> 24);
+ buf[2] = (Guchar)(y >> 16);
+ buf[3] = (Guchar)(y >> 8);
+ buf[4] = (Guchar)y;
+ buf[5] = 255;
+ buf[6] = 0;
+ buf[7] = 0;
+ buf[8] = 1;
+ buf[9] = 0;
+ buf[10] = 12;
+ buf[11] = 12;
+ n = 12;
+ } else {
+ //~ error(-1, "Type 2 fixed point constant out of range");
+ }
+ } else {
+ y = (int)x;
+ if (y >= -107 && y <= 107) {
+ buf[0] = (Guchar)(y + 139);
+ n = 1;
+ } else if (y > 107 && y <= 1131) {
+ y -= 108;
+ buf[0] = (Guchar)((y >> 8) + 247);
+ buf[1] = (Guchar)(y & 0xff);
+ n = 2;
+ } else if (y < -107 && y >= -1131) {
+ y = -y - 108;
+ buf[0] = (Guchar)((y >> 8) + 251);
+ buf[1] = (Guchar)(y & 0xff);
+ n = 2;
+ } else {
+ buf[0] = 255;
+ buf[1] = (Guchar)(y >> 24);
+ buf[2] = (Guchar)(y >> 16);
+ buf[3] = (Guchar)(y >> 8);
+ buf[4] = (Guchar)y;
+ n = 5;
+ }
+ }
+ charBuf->append((char *)buf, n);
+}
+
+void FoFiType1C::eexecWrite(Type1CEexecBuf *eb, char *s) {
+ Guchar *p;
+ Guchar x;
+
+ for (p = (Guchar *)s; *p; ++p) {
+ x = *p ^ (eb->r1 >> 8);
+ eb->r1 = (x + eb->r1) * 52845 + 22719;
+ if (eb->ascii) {
+ (*eb->outputFunc)(eb->outputStream, &hexChars[x >> 4], 1);
+ (*eb->outputFunc)(eb->outputStream, &hexChars[x & 0x0f], 1);
+ eb->line += 2;
+ if (eb->line == 64) {
+ (*eb->outputFunc)(eb->outputStream, "\n", 1);
+ eb->line = 0;
+ }
+ } else {
+ (*eb->outputFunc)(eb->outputStream, (char *)&x, 1);
+ }
+ }
+}
+
+void FoFiType1C::eexecWriteCharstring(Type1CEexecBuf *eb,
+ Guchar *s, int n) {
+ Guchar x;
+ int i;
+
+ // eexec encryption
+ for (i = 0; i < n; ++i) {
+ x = s[i] ^ (eb->r1 >> 8);
+ eb->r1 = (x + eb->r1) * 52845 + 22719;
+ if (eb->ascii) {
+ (*eb->outputFunc)(eb->outputStream, &hexChars[x >> 4], 1);
+ (*eb->outputFunc)(eb->outputStream, &hexChars[x & 0x0f], 1);
+ eb->line += 2;
+ if (eb->line == 64) {
+ (*eb->outputFunc)(eb->outputStream, "\n", 1);
+ eb->line = 0;
+ }
+ } else {
+ (*eb->outputFunc)(eb->outputStream, (char *)&x, 1);
+ }
+ }
+}
+
+GBool FoFiType1C::parse() {
+ Type1CIndex fdIdx;
+ Type1CIndexVal val;
+ int i;
+
+ parsedOk = gTrue;
+
+ // some tools embed Type 1C fonts with an extra whitespace char at
+ // the beginning
+ if (len > 0 && file[0] != '\x01') {
+ ++file;
+ --len;
+ }
+
+ // find the indexes
+ getIndex(getU8(2, &parsedOk), &nameIdx, &parsedOk);
+ getIndex(nameIdx.endPos, &topDictIdx, &parsedOk);
+ getIndex(topDictIdx.endPos, &stringIdx, &parsedOk);
+ getIndex(stringIdx.endPos, &gsubrIdx, &parsedOk);
+ if (!parsedOk) {
+ return gFalse;
+ }
+ gsubrBias = (gsubrIdx.len < 1240) ? 107
+ : (gsubrIdx.len < 33900) ? 1131 : 32768;
+
+ // read the first font name
+ getIndexVal(&nameIdx, 0, &val, &parsedOk);
+ if (!parsedOk) {
+ return gFalse;
+ }
+ name = new GString((char *)&file[val.pos], val.len);
+
+ // read the top dict for the first font
+ readTopDict();
+
+ // for CID fonts: read the FDArray dicts and private dicts
+ if (topDict.firstOp == 0x0c1e) {
+ if (topDict.fdArrayOffset == 0) {
+ nFDs = 1;
+ privateDicts = (Type1CPrivateDict *)gmalloc(sizeof(Type1CPrivateDict));
+ readPrivateDict(0, 0, &privateDicts[0]);
+ } else {
+ getIndex(topDict.fdArrayOffset, &fdIdx, &parsedOk);
+ if (!parsedOk) {
+ return gFalse;
+ }
+ nFDs = fdIdx.len;
+ privateDicts = (Type1CPrivateDict *)
+ gmallocn(nFDs, sizeof(Type1CPrivateDict));
+ for (i = 0; i < nFDs; ++i) {
+ getIndexVal(&fdIdx, i, &val, &parsedOk);
+ if (!parsedOk) {
+ return gFalse;
+ }
+ readFD(val.pos, val.len, &privateDicts[i]);
+ }
+ }
+
+ // for 8-bit fonts: read the private dict
+ } else {
+ privateDicts = (Type1CPrivateDict *)gmalloc(sizeof(Type1CPrivateDict));
+ readPrivateDict(topDict.privateOffset, topDict.privateSize,
+ &privateDicts[0]);
+ }
+
+ // check for parse errors in the private dict(s)
+ if (!parsedOk) {
+ return gFalse;
+ }
+
+ // get the charstrings index
+ if (topDict.charStringsOffset <= 0) {
+ parsedOk = gFalse;
+ return gFalse;
+ }
+ getIndex(topDict.charStringsOffset, &charStringsIdx, &parsedOk);
+ if (!parsedOk) {
+ return gFalse;
+ }
+ nGlyphs = charStringsIdx.len;
+
+ // for CID fonts: read the FDSelect table
+ if (topDict.firstOp == 0x0c1e) {
+ readFDSelect();
+ if (!parsedOk) {
+ return gFalse;
+ }
+ }
+
+ // read the charset
+ if (!readCharset()) {
+ parsedOk = gFalse;
+ return gFalse;
+ }
+
+ // for 8-bit fonts: build the encoding
+ if (topDict.firstOp != 0x0c14 && topDict.firstOp != 0x0c1e) {
+ buildEncoding();
+ if (!parsedOk) {
+ return gFalse;
+ }
+ }
+
+ return parsedOk;
+}
+
+void FoFiType1C::readTopDict() {
+ Type1CIndexVal topDictPtr;
+ int pos;
+
+ topDict.firstOp = -1;
+ topDict.versionSID = 0;
+ topDict.noticeSID = 0;
+ topDict.copyrightSID = 0;
+ topDict.fullNameSID = 0;
+ topDict.familyNameSID = 0;
+ topDict.weightSID = 0;
+ topDict.isFixedPitch = 0;
+ topDict.italicAngle = 0;
+ topDict.underlinePosition = -100;
+ topDict.underlineThickness = 50;
+ topDict.paintType = 0;
+ topDict.charstringType = 2;
+ topDict.fontMatrix[0] = 0.001;
+ topDict.fontMatrix[1] = 0;
+ topDict.fontMatrix[2] = 0;
+ topDict.fontMatrix[3] = 0.001;
+ topDict.fontMatrix[4] = 0;
+ topDict.fontMatrix[5] = 0;
+ topDict.hasFontMatrix = gFalse;
+ topDict.uniqueID = 0;
+ topDict.fontBBox[0] = 0;
+ topDict.fontBBox[1] = 0;
+ topDict.fontBBox[2] = 0;
+ topDict.fontBBox[3] = 0;
+ topDict.strokeWidth = 0;
+ topDict.charsetOffset = 0;
+ topDict.encodingOffset = 0;
+ topDict.charStringsOffset = 0;
+ topDict.privateSize = 0;
+ topDict.privateOffset = 0;
+ topDict.registrySID = 0;
+ topDict.orderingSID = 0;
+ topDict.supplement = 0;
+ topDict.fdArrayOffset = 0;
+ topDict.fdSelectOffset = 0;
+
+ getIndexVal(&topDictIdx, 0, &topDictPtr, &parsedOk);
+ pos = topDictPtr.pos;
+ nOps = 0;
+ while (pos < topDictPtr.pos + topDictPtr.len) {
+ pos = getOp(pos, gFalse, &parsedOk);
+ if (!parsedOk) {
+ break;
+ }
+ if (!ops[nOps - 1].isNum) {
+ --nOps; // drop the operator
+ if (topDict.firstOp < 0) {
+ topDict.firstOp = ops[nOps].op;
+ }
+ switch (ops[nOps].op) {
+ case 0x0000: topDict.versionSID = (int)ops[0].num; break;
+ case 0x0001: topDict.noticeSID = (int)ops[0].num; break;
+ case 0x0c00: topDict.copyrightSID = (int)ops[0].num; break;
+ case 0x0002: topDict.fullNameSID = (int)ops[0].num; break;
+ case 0x0003: topDict.familyNameSID = (int)ops[0].num; break;
+ case 0x0004: topDict.weightSID = (int)ops[0].num; break;
+ case 0x0c01: topDict.isFixedPitch = (int)ops[0].num; break;
+ case 0x0c02: topDict.italicAngle = ops[0].num; break;
+ case 0x0c03: topDict.underlinePosition = ops[0].num; break;
+ case 0x0c04: topDict.underlineThickness = ops[0].num; break;
+ case 0x0c05: topDict.paintType = (int)ops[0].num; break;
+ case 0x0c06: topDict.charstringType = (int)ops[0].num; break;
+ case 0x0c07: topDict.fontMatrix[0] = ops[0].num;
+ topDict.fontMatrix[1] = ops[1].num;
+ topDict.fontMatrix[2] = ops[2].num;
+ topDict.fontMatrix[3] = ops[3].num;
+ topDict.fontMatrix[4] = ops[4].num;
+ topDict.fontMatrix[5] = ops[5].num;
+ topDict.hasFontMatrix = gTrue; break;
+ case 0x000d: topDict.uniqueID = (int)ops[0].num; break;
+ case 0x0005: topDict.fontBBox[0] = ops[0].num;
+ topDict.fontBBox[1] = ops[1].num;
+ topDict.fontBBox[2] = ops[2].num;
+ topDict.fontBBox[3] = ops[3].num; break;
+ case 0x0c08: topDict.strokeWidth = ops[0].num; break;
+ case 0x000f: topDict.charsetOffset = (int)ops[0].num; break;
+ case 0x0010: topDict.encodingOffset = (int)ops[0].num; break;
+ case 0x0011: topDict.charStringsOffset = (int)ops[0].num; break;
+ case 0x0012: topDict.privateSize = (int)ops[0].num;
+ topDict.privateOffset = (int)ops[1].num; break;
+ case 0x0c1e: topDict.registrySID = (int)ops[0].num;
+ topDict.orderingSID = (int)ops[1].num;
+ topDict.supplement = (int)ops[2].num; break;
+ case 0x0c24: topDict.fdArrayOffset = (int)ops[0].num; break;
+ case 0x0c25: topDict.fdSelectOffset = (int)ops[0].num; break;
+ }
+ nOps = 0;
+ }
+ }
+}
+
+// Read a CID font dict (FD) - this pulls out the private dict
+// pointer, and reads the private dict. It also pulls the FontMatrix
+// (if any) out of the FD.
+void FoFiType1C::readFD(int offset, int length, Type1CPrivateDict *pDict) {
+ int pos, pSize, pOffset;
+ double fontMatrix[6];
+ GBool hasFontMatrix;
+
+ hasFontMatrix = gFalse;
+ pSize = pOffset = 0;
+ pos = offset;
+ nOps = 0;
+ while (pos < offset + length) {
+ pos = getOp(pos, gFalse, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ if (!ops[nOps - 1].isNum) {
+ if (ops[nOps - 1].op == 0x0012) {
+ if (nOps < 3) {
+ parsedOk = gFalse;
+ return;
+ }
+ pSize = (int)ops[0].num;
+ pOffset = (int)ops[1].num;
+ break;
+ } else if (ops[nOps - 1].op == 0x0c07) {
+ fontMatrix[0] = ops[0].num;
+ fontMatrix[1] = ops[1].num;
+ fontMatrix[2] = ops[2].num;
+ fontMatrix[3] = ops[3].num;
+ fontMatrix[4] = ops[4].num;
+ fontMatrix[5] = ops[5].num;
+ hasFontMatrix = gTrue;
+ }
+ nOps = 0;
+ }
+ }
+ readPrivateDict(pOffset, pSize, pDict);
+ if (hasFontMatrix) {
+ pDict->fontMatrix[0] = fontMatrix[0];
+ pDict->fontMatrix[1] = fontMatrix[1];
+ pDict->fontMatrix[2] = fontMatrix[2];
+ pDict->fontMatrix[3] = fontMatrix[3];
+ pDict->fontMatrix[4] = fontMatrix[4];
+ pDict->fontMatrix[5] = fontMatrix[5];
+ pDict->hasFontMatrix = gTrue;
+ }
+}
+
+void FoFiType1C::readPrivateDict(int offset, int length,
+ Type1CPrivateDict *pDict) {
+ int pos;
+
+ pDict->hasFontMatrix = gFalse;
+ pDict->nBlueValues = 0;
+ pDict->nOtherBlues = 0;
+ pDict->nFamilyBlues = 0;
+ pDict->nFamilyOtherBlues = 0;
+ pDict->blueScale = 0.039625;
+ pDict->blueShift = 7;
+ pDict->blueFuzz = 1;
+ pDict->hasStdHW = gFalse;
+ pDict->hasStdVW = gFalse;
+ pDict->nStemSnapH = 0;
+ pDict->nStemSnapV = 0;
+ pDict->hasForceBold = gFalse;
+ pDict->forceBoldThreshold = 0;
+ pDict->languageGroup = 0;
+ pDict->expansionFactor = 0.06;
+ pDict->initialRandomSeed = 0;
+ pDict->subrsOffset = 0;
+ pDict->defaultWidthX = 0;
+ pDict->defaultWidthXFP = gFalse;
+ pDict->nominalWidthX = 0;
+ pDict->nominalWidthXFP = gFalse;
+
+ // no dictionary
+ if (offset == 0 || length == 0) {
+ return;
+ }
+
+ pos = offset;
+ nOps = 0;
+ while (pos < offset + length) {
+ pos = getOp(pos, gFalse, &parsedOk);
+ if (!parsedOk) {
+ break;
+ }
+ if (!ops[nOps - 1].isNum) {
+ --nOps; // drop the operator
+ switch (ops[nOps].op) {
+ case 0x0006:
+ pDict->nBlueValues = getDeltaIntArray(pDict->blueValues,
+ type1CMaxBlueValues);
+ break;
+ case 0x0007:
+ pDict->nOtherBlues = getDeltaIntArray(pDict->otherBlues,
+ type1CMaxOtherBlues);
+ break;
+ case 0x0008:
+ pDict->nFamilyBlues = getDeltaIntArray(pDict->familyBlues,
+ type1CMaxBlueValues);
+ break;
+ case 0x0009:
+ pDict->nFamilyOtherBlues = getDeltaIntArray(pDict->familyOtherBlues,
+ type1CMaxOtherBlues);
+ break;
+ case 0x0c09:
+ pDict->blueScale = ops[0].num;
+ break;
+ case 0x0c0a:
+ pDict->blueShift = (int)ops[0].num;
+ break;
+ case 0x0c0b:
+ pDict->blueFuzz = (int)ops[0].num;
+ break;
+ case 0x000a:
+ pDict->stdHW = ops[0].num;
+ pDict->hasStdHW = gTrue;
+ break;
+ case 0x000b:
+ pDict->stdVW = ops[0].num;
+ pDict->hasStdVW = gTrue;
+ break;
+ case 0x0c0c:
+ pDict->nStemSnapH = getDeltaFPArray(pDict->stemSnapH,
+ type1CMaxStemSnap);
+ break;
+ case 0x0c0d:
+ pDict->nStemSnapV = getDeltaFPArray(pDict->stemSnapV,
+ type1CMaxStemSnap);
+ break;
+ case 0x0c0e:
+ pDict->forceBold = ops[0].num != 0;
+ pDict->hasForceBold = gTrue;
+ break;
+ case 0x0c0f:
+ pDict->forceBoldThreshold = ops[0].num;
+ break;
+ case 0x0c11:
+ pDict->languageGroup = (int)ops[0].num;
+ break;
+ case 0x0c12:
+ pDict->expansionFactor = ops[0].num;
+ break;
+ case 0x0c13:
+ pDict->initialRandomSeed = (int)ops[0].num;
+ break;
+ case 0x0013:
+ pDict->subrsOffset = offset + (int)ops[0].num;
+ break;
+ case 0x0014:
+ pDict->defaultWidthX = ops[0].num;
+ pDict->defaultWidthXFP = ops[0].isFP;
+ break;
+ case 0x0015:
+ pDict->nominalWidthX = ops[0].num;
+ pDict->nominalWidthXFP = ops[0].isFP;
+ break;
+ }
+ nOps = 0;
+ }
+ }
+}
+
+void FoFiType1C::readFDSelect() {
+ int fdSelectFmt, pos, nRanges, gid0, gid1, fd, i, j;
+
+ fdSelect = (Guchar *)gmalloc(nGlyphs);
+ if (topDict.fdSelectOffset == 0) {
+ for (i = 0; i < nGlyphs; ++i) {
+ fdSelect[i] = 0;
+ }
+ } else {
+ pos = topDict.fdSelectOffset;
+ fdSelectFmt = getU8(pos++, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ if (fdSelectFmt == 0) {
+ if (!checkRegion(pos, nGlyphs)) {
+ parsedOk = gFalse;
+ return;
+ }
+ memcpy(fdSelect, file + pos, nGlyphs);
+ } else if (fdSelectFmt == 3) {
+ nRanges = getU16BE(pos, &parsedOk);
+ pos += 2;
+ gid0 = getU16BE(pos, &parsedOk);
+ pos += 2;
+ for (i = 1; i <= nRanges; ++i) {
+ fd = getU8(pos++, &parsedOk);
+ gid1 = getU16BE(pos, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ pos += 2;
+ if (gid0 > gid1 || gid1 > nGlyphs) {
+ //~ error(-1, "Bad FDSelect table in CID font");
+ parsedOk = gFalse;
+ return;
+ }
+ for (j = gid0; j < gid1; ++j) {
+ fdSelect[j] = fd;
+ }
+ gid0 = gid1;
+ }
+ } else {
+ //~ error(-1, "Unknown FDSelect table format in CID font");
+ for (i = 0; i < nGlyphs; ++i) {
+ fdSelect[i] = 0;
+ }
+ }
+ }
+}
+
+void FoFiType1C::buildEncoding() {
+ char buf[256];
+ int nCodes, nRanges, encFormat;
+ int pos, c, sid, nLeft, nSups, i, j;
+
+ if (topDict.encodingOffset == 0) {
+ encoding = fofiType1StandardEncoding;
+
+ } else if (topDict.encodingOffset == 1) {
+ encoding = fofiType1ExpertEncoding;
+
+ } else {
+ encoding = (char **)gmallocn(256, sizeof(char *));
+ for (i = 0; i < 256; ++i) {
+ encoding[i] = NULL;
+ }
+ pos = topDict.encodingOffset;
+ encFormat = getU8(pos++, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ if ((encFormat & 0x7f) == 0) {
+ nCodes = 1 + getU8(pos++, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ if (nCodes > nGlyphs) {
+ nCodes = nGlyphs;
+ }
+ for (i = 1; i < nCodes; ++i) {
+ c = getU8(pos++, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ if (encoding[c]) {
+ gfree(encoding[c]);
+ }
+ encoding[c] = copyString(getString(charset[i], buf, &parsedOk));
+ }
+ } else if ((encFormat & 0x7f) == 1) {
+ nRanges = getU8(pos++, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ nCodes = 1;
+ for (i = 0; i < nRanges; ++i) {
+ c = getU8(pos++, &parsedOk);
+ nLeft = getU8(pos++, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ for (j = 0; j <= nLeft && nCodes < nGlyphs; ++j) {
+ if (c < 256) {
+ if (encoding[c]) {
+ gfree(encoding[c]);
+ }
+ encoding[c] = copyString(getString(charset[nCodes], buf,
+ &parsedOk));
+ }
+ ++nCodes;
+ ++c;
+ }
+ }
+ }
+ if (encFormat & 0x80) {
+ nSups = getU8(pos++, &parsedOk);
+ if (!parsedOk) {
+ return;
+ }
+ for (i = 0; i < nSups; ++i) {
+ c = getU8(pos++, &parsedOk);;
+ if (!parsedOk) {
+ return;;
+ }
+ sid = getU16BE(pos, &parsedOk);
+ pos += 2;
+ if (!parsedOk) {
+ return;
+ }
+ if (encoding[c]) {
+ gfree(encoding[c]);
+ }
+ encoding[c] = copyString(getString(sid, buf, &parsedOk));
+ }
+ }
+ }
+}
+
+GBool FoFiType1C::readCharset() {
+ int charsetFormat, c, pos;
+ int nLeft, i, j;
+
+ if (topDict.charsetOffset == 0) {
+ charset = fofiType1CISOAdobeCharset;
+ } else if (topDict.charsetOffset == 1) {
+ charset = fofiType1CExpertCharset;
+ } else if (topDict.charsetOffset == 2) {
+ charset = fofiType1CExpertSubsetCharset;
+ } else {
+ charset = (Gushort *)gmallocn(nGlyphs, sizeof(Gushort));
+ for (i = 0; i < nGlyphs; ++i) {
+ charset[i] = 0;
+ }
+ pos = topDict.charsetOffset;
+ charsetFormat = getU8(pos++, &parsedOk);
+ if (charsetFormat == 0) {
+ for (i = 1; i < nGlyphs; ++i) {
+ charset[i] = (Gushort)getU16BE(pos, &parsedOk);
+ pos += 2;
+ if (!parsedOk) {
+ break;
+ }
+ }
+ } else if (charsetFormat == 1) {
+ i = 1;
+ while (i < nGlyphs) {
+ c = getU16BE(pos, &parsedOk);
+ pos += 2;
+ nLeft = getU8(pos++, &parsedOk);
+ if (!parsedOk) {
+ break;
+ }
+ for (j = 0; j <= nLeft && i < nGlyphs; ++j) {
+ charset[i++] = (Gushort)c++;
+ }
+ }
+ } else if (charsetFormat == 2) {
+ i = 1;
+ while (i < nGlyphs) {
+ c = getU16BE(pos, &parsedOk);
+ pos += 2;
+ nLeft = getU16BE(pos, &parsedOk);
+ pos += 2;
+ if (!parsedOk) {
+ break;
+ }
+ for (j = 0; j <= nLeft && i < nGlyphs; ++j) {
+ charset[i++] = (Gushort)c++;
+ }
+ }
+ }
+ if (!parsedOk) {
+ gfree(charset);
+ charset = NULL;
+ return gFalse;
+ }
+ }
+ return gTrue;
+}
+
+int FoFiType1C::getOp(int pos, GBool charstring, GBool *ok) {
+ static char nybChars[16] = "0123456789.ee -";
+ Type1COp op;
+ char buf[65];
+ int b0, b1, nyb0, nyb1, x, i;
+
+ b0 = getU8(pos++, ok);
+ op.isNum = gTrue;
+ op.isFP = gFalse;
+
+ if (b0 == 28) {
+ x = getU8(pos++, ok);
+ x = (x << 8) | getU8(pos++, ok);
+ if (x & 0x8000) {
+ x |= ~0xffff;
+ }
+ op.num = x;
+
+ } else if (!charstring && b0 == 29) {
+ x = getU8(pos++, ok);
+ x = (x << 8) | getU8(pos++, ok);
+ x = (x << 8) | getU8(pos++, ok);
+ x = (x << 8) | getU8(pos++, ok);
+ if (x & 0x80000000) {
+ x |= ~0xffffffff;
+ }
+ op.num = x;
+
+ } else if (!charstring && b0 == 30) {
+ i = 0;
+ do {
+ b1 = getU8(pos++, ok);
+ nyb0 = b1 >> 4;
+ nyb1 = b1 & 0x0f;
+ if (nyb0 == 0xf) {
+ break;
+ }
+ buf[i++] = nybChars[nyb0];
+ if (i == 64) {
+ break;
+ }
+ if (nyb0 == 0xc) {
+ buf[i++] = '-';
+ }
+ if (i == 64) {
+ break;
+ }
+ if (nyb1 == 0xf) {
+ break;
+ }
+ buf[i++] = nybChars[nyb1];
+ if (i == 64) {
+ break;
+ }
+ if (nyb1 == 0xc) {
+ buf[i++] = '-';
+ }
+ } while (i < 64);
+ buf[i] = '\0';
+ op.num = atof(buf);
+ op.isFP = gTrue;
+
+ } else if (b0 >= 32 && b0 <= 246) {
+ op.num = b0 - 139;
+
+ } else if (b0 >= 247 && b0 <= 250) {
+ op.num = ((b0 - 247) << 8) + getU8(pos++, ok) + 108;
+
+ } else if (b0 >= 251 && b0 <= 254) {
+ op.num = -((b0 - 251) << 8) - getU8(pos++, ok) - 108;
+
+ } else if (charstring && b0 == 255) {
+ x = getU8(pos++, ok);
+ x = (x << 8) | getU8(pos++, ok);
+ x = (x << 8) | getU8(pos++, ok);
+ x = (x << 8) | getU8(pos++, ok);
+ if (x & 0x80000000) {
+ x |= ~0xffffffff;
+ }
+ op.num = (double)x / 65536.0;
+ op.isFP = gTrue;
+
+ } else if (b0 == 12) {
+ op.isNum = gFalse;
+ op.op = 0x0c00 + getU8(pos++, ok);
+
+ } else {
+ op.isNum = gFalse;
+ op.op = b0;
+ }
+
+ if (nOps < 49) {
+ ops[nOps++] = op;
+ }
+
+ return pos;
+}
+
+// Convert the delta-encoded ops array to an array of ints.
+int FoFiType1C::getDeltaIntArray(int *arr, int maxLen) {
+ int x;
+ int n, i;
+
+ if ((n = nOps) > maxLen) {
+ n = maxLen;
+ }
+ x = 0;
+ for (i = 0; i < n; ++i) {
+ x += (int)ops[i].num;
+ arr[i] = x;
+ }
+ return n;
+}
+
+// Convert the delta-encoded ops array to an array of doubles.
+int FoFiType1C::getDeltaFPArray(double *arr, int maxLen) {
+ double x;
+ int n, i;
+
+ if ((n = nOps) > maxLen) {
+ n = maxLen;
+ }
+ x = 0;
+ for (i = 0; i < n; ++i) {
+ x += ops[i].num;
+ arr[i] = x;
+ }
+ return n;
+}
+
+void FoFiType1C::getIndex(int pos, Type1CIndex *idx, GBool *ok) {
+ idx->pos = pos;
+ idx->len = getU16BE(pos, ok);
+ if (idx->len == 0) {
+ // empty indexes are legal and contain just the length field
+ idx->offSize = 0;
+ idx->startPos = idx->endPos = pos + 2;
+ } else {
+ idx->offSize = getU8(pos + 2, ok);
+ if (idx->offSize < 1 || idx->offSize > 4) {
+ *ok = gFalse;
+ }
+ idx->startPos = pos + 3 + (idx->len + 1) * idx->offSize - 1;
+ if (idx->startPos < 0 || idx->startPos >= len) {
+ *ok = gFalse;
+ }
+ idx->endPos = idx->startPos + getUVarBE(pos + 3 + idx->len * idx->offSize,
+ idx->offSize, ok);
+ if (idx->endPos < idx->startPos || idx->endPos > len) {
+ *ok = gFalse;
+ }
+ }
+}
+
+void FoFiType1C::getIndexVal(Type1CIndex *idx, int i,
+ Type1CIndexVal *val, GBool *ok) {
+ int pos0, pos1;
+
+ if (i < 0 || i >= idx->len) {
+ *ok = gFalse;
+ return;
+ }
+ pos0 = idx->startPos + getUVarBE(idx->pos + 3 + i * idx->offSize,
+ idx->offSize, ok);
+ pos1 = idx->startPos + getUVarBE(idx->pos + 3 + (i + 1) * idx->offSize,
+ idx->offSize, ok);
+ if (pos0 < idx->startPos || pos0 > idx->endPos ||
+ pos1 <= idx->startPos || pos1 > idx->endPos ||
+ pos1 < pos0) {
+ *ok = gFalse;
+ }
+ val->pos = pos0;
+ val->len = pos1 - pos0;
+}
+
+char *FoFiType1C::getString(int sid, char *buf, GBool *ok) {
+ Type1CIndexVal val;
+ int n;
+
+ if (sid < 391) {
+ strcpy(buf, fofiType1CStdStrings[sid]);
+ } else {
+ sid -= 391;
+ getIndexVal(&stringIdx, sid, &val, ok);
+ if (*ok) {
+ if ((n = val.len) > 255) {
+ n = 255;
+ }
+ strncpy(buf, (char *)&file[val.pos], n);
+ buf[n] = '\0';
+ } else {
+ buf[0] = '\0';
+ }
+ }
+ return buf;
+}
--- /dev/null
+//========================================================================
+//
+// FoFiType1C.h
+//
+// Copyright 1999-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FOFITYPE1C_H
+#define FOFITYPE1C_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "FoFiBase.h"
+
+class GString;
+
+//------------------------------------------------------------------------
+
+struct Type1CIndex {
+ int pos; // absolute position in file
+ int len; // length (number of entries)
+ int offSize; // offset size
+ int startPos; // position of start of index data - 1
+ int endPos; // position one byte past end of the index
+};
+
+struct Type1CIndexVal {
+ int pos; // absolute position in file
+ int len; // length, in bytes
+};
+
+struct Type1CTopDict {
+ int firstOp;
+
+ int versionSID;
+ int noticeSID;
+ int copyrightSID;
+ int fullNameSID;
+ int familyNameSID;
+ int weightSID;
+ int isFixedPitch;
+ double italicAngle;
+ double underlinePosition;
+ double underlineThickness;
+ int paintType;
+ int charstringType;
+ double fontMatrix[6];
+ GBool hasFontMatrix; // CID fonts are allowed to put their
+ // FontMatrix in the FD instead of the
+ // top dict
+ int uniqueID;
+ double fontBBox[4];
+ double strokeWidth;
+ int charsetOffset;
+ int encodingOffset;
+ int charStringsOffset;
+ int privateSize;
+ int privateOffset;
+
+ // CIDFont entries
+ int registrySID;
+ int orderingSID;
+ int supplement;
+ int fdArrayOffset;
+ int fdSelectOffset;
+};
+
+#define type1CMaxBlueValues 14
+#define type1CMaxOtherBlues 10
+#define type1CMaxStemSnap 12
+
+struct Type1CPrivateDict {
+ double fontMatrix[6];
+ GBool hasFontMatrix;
+ int blueValues[type1CMaxBlueValues];
+ int nBlueValues;
+ int otherBlues[type1CMaxOtherBlues];
+ int nOtherBlues;
+ int familyBlues[type1CMaxBlueValues];
+ int nFamilyBlues;
+ int familyOtherBlues[type1CMaxOtherBlues];
+ int nFamilyOtherBlues;
+ double blueScale;
+ int blueShift;
+ int blueFuzz;
+ double stdHW;
+ GBool hasStdHW;
+ double stdVW;
+ GBool hasStdVW;
+ double stemSnapH[type1CMaxStemSnap];
+ int nStemSnapH;
+ double stemSnapV[type1CMaxStemSnap];
+ int nStemSnapV;
+ GBool forceBold;
+ GBool hasForceBold;
+ double forceBoldThreshold;
+ int languageGroup;
+ double expansionFactor;
+ int initialRandomSeed;
+ int subrsOffset;
+ double defaultWidthX;
+ GBool defaultWidthXFP;
+ double nominalWidthX;
+ GBool nominalWidthXFP;
+};
+
+struct Type1COp {
+ GBool isNum; // true -> number, false -> operator
+ GBool isFP; // true -> floating point number, false -> int
+ union {
+ double num; // if num is true
+ int op; // if num is false
+ };
+};
+
+struct Type1CEexecBuf {
+ FoFiOutputFunc outputFunc;
+ void *outputStream;
+ GBool ascii; // ASCII encoding?
+ Gushort r1; // eexec encryption key
+ int line; // number of eexec chars left on current line
+};
+
+//------------------------------------------------------------------------
+// FoFiType1C
+//------------------------------------------------------------------------
+
+class FoFiType1C: public FoFiBase {
+public:
+
+ // Create a FoFiType1C object from a memory buffer.
+ static FoFiType1C *make(char *fileA, int lenA);
+
+ // Create a FoFiType1C object from a file on disk.
+ static FoFiType1C *load(char *fileName);
+
+ virtual ~FoFiType1C();
+
+ // Return the font name.
+ char *getName();
+
+ // Return the encoding, as an array of 256 names (any of which may
+ // be NULL). This is only useful with 8-bit fonts.
+ char **getEncoding();
+
+ // Return the mapping from CIDs to GIDs, and return the number of
+ // CIDs in *<nCIDs>. This is only useful for CID fonts.
+ Gushort *getCIDToGIDMap(int *nCIDs);
+
+ // Convert to a Type 1 font, suitable for embedding in a PostScript
+ // file. This is only useful with 8-bit fonts. If <newEncoding> is
+ // not NULL, it will be used in place of the encoding in the Type 1C
+ // font. If <ascii> is true the eexec section will be hex-encoded,
+ // otherwise it will be left as binary data.
+ void convertToType1(char **newEncoding, GBool ascii,
+ FoFiOutputFunc outputFunc, void *outputStream);
+
+ // Convert to a Type 0 CIDFont, suitable for embedding in a
+ // PostScript file. <psName> will be used as the PostScript font
+ // name.
+ void convertToCIDType0(char *psName,
+ FoFiOutputFunc outputFunc, void *outputStream);
+
+ // Convert to a Type 0 (but non-CID) composite font, suitable for
+ // embedding in a PostScript file. <psName> will be used as the
+ // PostScript font name.
+ void convertToType0(char *psName,
+ FoFiOutputFunc outputFunc, void *outputStream);
+
+private:
+
+ FoFiType1C(char *fileA, int lenA, GBool freeFileDataA);
+ void eexecCvtGlyph(Type1CEexecBuf *eb, char *glyphName,
+ int offset, int nBytes,
+ Type1CIndex *subrIdx,
+ Type1CPrivateDict *pDict);
+ void cvtGlyph(int offset, int nBytes, GString *charBuf,
+ Type1CIndex *subrIdx, Type1CPrivateDict *pDict,
+ GBool top);
+ void cvtGlyphWidth(GBool useOp, GString *charBuf,
+ Type1CPrivateDict *pDict);
+ void cvtNum(double x, GBool isFP, GString *charBuf);
+ void eexecWrite(Type1CEexecBuf *eb, char *s);
+ void eexecWriteCharstring(Type1CEexecBuf *eb, Guchar *s, int n);
+ GBool parse();
+ void readTopDict();
+ void readFD(int offset, int length, Type1CPrivateDict *pDict);
+ void readPrivateDict(int offset, int length, Type1CPrivateDict *pDict);
+ void readFDSelect();
+ void buildEncoding();
+ GBool readCharset();
+ int getOp(int pos, GBool charstring, GBool *ok);
+ int getDeltaIntArray(int *arr, int maxLen);
+ int getDeltaFPArray(double *arr, int maxLen);
+ void getIndex(int pos, Type1CIndex *idx, GBool *ok);
+ void getIndexVal(Type1CIndex *idx, int i, Type1CIndexVal *val, GBool *ok);
+ char *getString(int sid, char *buf, GBool *ok);
+
+ GString *name;
+ char **encoding;
+
+ Type1CIndex nameIdx;
+ Type1CIndex topDictIdx;
+ Type1CIndex stringIdx;
+ Type1CIndex gsubrIdx;
+ Type1CIndex charStringsIdx;
+
+ Type1CTopDict topDict;
+ Type1CPrivateDict *privateDicts;
+
+ int nGlyphs;
+ int nFDs;
+ Guchar *fdSelect;
+ Gushort *charset;
+ int gsubrBias;
+
+ GBool parsedOk;
+
+ Type1COp ops[49]; // operands and operator
+ int nOps; // number of operands
+ int nHints; // number of hints for the current glyph
+ GBool firstOp; // true if we haven't hit the first op yet
+ GBool openPath; // true if there is an unclosed path
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// FontEncodingTables.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+#include <stdlib.h>
+#include "FontEncodingTables.h"
+
+char *macRomanEncoding[256] = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "space",
+ "exclam",
+ "quotedbl",
+ "numbersign",
+ "dollar",
+ "percent",
+ "ampersand",
+ "quotesingle",
+ "parenleft",
+ "parenright",
+ "asterisk",
+ "plus",
+ "comma",
+ "hyphen",
+ "period",
+ "slash",
+ "zero",
+ "one",
+ "two",
+ "three",
+ "four",
+ "five",
+ "six",
+ "seven",
+ "eight",
+ "nine",
+ "colon",
+ "semicolon",
+ "less",
+ "equal",
+ "greater",
+ "question",
+ "at",
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I",
+ "J",
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P",
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W",
+ "X",
+ "Y",
+ "Z",
+ "bracketleft",
+ "backslash",
+ "bracketright",
+ "asciicircum",
+ "underscore",
+ "grave",
+ "a",
+ "b",
+ "c",
+ "d",
+ "e",
+ "f",
+ "g",
+ "h",
+ "i",
+ "j",
+ "k",
+ "l",
+ "m",
+ "n",
+ "o",
+ "p",
+ "q",
+ "r",
+ "s",
+ "t",
+ "u",
+ "v",
+ "w",
+ "x",
+ "y",
+ "z",
+ "braceleft",
+ "bar",
+ "braceright",
+ "asciitilde",
+ NULL,
+ "Adieresis",
+ "Aring",
+ "Ccedilla",
+ "Eacute",
+ "Ntilde",
+ "Odieresis",
+ "Udieresis",
+ "aacute",
+ "agrave",
+ "acircumflex",
+ "adieresis",
+ "atilde",
+ "aring",
+ "ccedilla",
+ "eacute",
+ "egrave",
+ "ecircumflex",
+ "edieresis",
+ "iacute",
+ "igrave",
+ "icircumflex",
+ "idieresis",
+ "ntilde",
+ "oacute",
+ "ograve",
+ "ocircumflex",
+ "odieresis",
+ "otilde",
+ "uacute",
+ "ugrave",
+ "ucircumflex",
+ "udieresis",
+ "dagger",
+ "degree",
+ "cent",
+ "sterling",
+ "section",
+ "bullet",
+ "paragraph",
+ "germandbls",
+ "registered",
+ "copyright",
+ "trademark",
+ "acute",
+ "dieresis",
+ "notequal",
+ "AE",
+ "Oslash",
+ "infinity",
+ "plusminus",
+ "lessequal",
+ "greaterequal",
+ "yen",
+ "mu",
+ "partialdiff",
+ "summation",
+ "product",
+ "pi",
+ "integral",
+ "ordfeminine",
+ "ordmasculine",
+ "Omega",
+ "ae",
+ "oslash",
+ "questiondown",
+ "exclamdown",
+ "logicalnot",
+ "radical",
+ "florin",
+ "approxequal",
+ "Delta",
+ "guillemotleft",
+ "guillemotright",
+ "ellipsis",
+ "space",
+ "Agrave",
+ "Atilde",
+ "Otilde",
+ "OE",
+ "oe",
+ "endash",
+ "emdash",
+ "quotedblleft",
+ "quotedblright",
+ "quoteleft",
+ "quoteright",
+ "divide",
+ "lozenge",
+ "ydieresis",
+ "Ydieresis",
+ "fraction",
+ "currency",
+ "guilsinglleft",
+ "guilsinglright",
+ "fi",
+ "fl",
+ "daggerdbl",
+ "periodcentered",
+ "quotesinglbase",
+ "quotedblbase",
+ "perthousand",
+ "Acircumflex",
+ "Ecircumflex",
+ "Aacute",
+ "Edieresis",
+ "Egrave",
+ "Iacute",
+ "Icircumflex",
+ "Idieresis",
+ "Igrave",
+ "Oacute",
+ "Ocircumflex",
+ "apple",
+ "Ograve",
+ "Uacute",
+ "Ucircumflex",
+ "Ugrave",
+ "dotlessi",
+ "circumflex",
+ "tilde",
+ "macron",
+ "breve",
+ "dotaccent",
+ "ring",
+ "cedilla",
+ "hungarumlaut",
+ "ogonek",
+ "caron"
+};
+
+char *macExpertEncoding[256] = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "space",
+ "exclamsmall",
+ "Hungarumlautsmall",
+ "centoldstyle",
+ "dollaroldstyle",
+ "dollarsuperior",
+ "ampersandsmall",
+ "Acutesmall",
+ "parenleftsuperior",
+ "parenrightsuperior",
+ "twodotenleader",
+ "onedotenleader",
+ "comma",
+ "hyphen",
+ "period",
+ "fraction",
+ "zerooldstyle",
+ "oneoldstyle",
+ "twooldstyle",
+ "threeoldstyle",
+ "fouroldstyle",
+ "fiveoldstyle",
+ "sixoldstyle",
+ "sevenoldstyle",
+ "eightoldstyle",
+ "nineoldstyle",
+ "colon",
+ "semicolon",
+ NULL,
+ "threequartersemdash",
+ NULL,
+ "questionsmall",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Ethsmall",
+ NULL,
+ NULL,
+ "onequarter",
+ "onehalf",
+ "threequarters",
+ "oneeighth",
+ "threeeighths",
+ "fiveeighths",
+ "seveneighths",
+ "onethird",
+ "twothirds",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "ff",
+ "fi",
+ "fl",
+ "ffi",
+ "ffl",
+ "parenleftinferior",
+ NULL,
+ "parenrightinferior",
+ "Circumflexsmall",
+ "hypheninferior",
+ "Gravesmall",
+ "Asmall",
+ "Bsmall",
+ "Csmall",
+ "Dsmall",
+ "Esmall",
+ "Fsmall",
+ "Gsmall",
+ "Hsmall",
+ "Ismall",
+ "Jsmall",
+ "Ksmall",
+ "Lsmall",
+ "Msmall",
+ "Nsmall",
+ "Osmall",
+ "Psmall",
+ "Qsmall",
+ "Rsmall",
+ "Ssmall",
+ "Tsmall",
+ "Usmall",
+ "Vsmall",
+ "Wsmall",
+ "Xsmall",
+ "Ysmall",
+ "Zsmall",
+ "colonmonetary",
+ "onefitted",
+ "rupiah",
+ "Tildesmall",
+ NULL,
+ NULL,
+ "asuperior",
+ "centsuperior",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Aacutesmall",
+ "Agravesmall",
+ "Acircumflexsmall",
+ "Adieresissmall",
+ "Atildesmall",
+ "Aringsmall",
+ "Ccedillasmall",
+ "Eacutesmall",
+ "Egravesmall",
+ "Ecircumflexsmall",
+ "Edieresissmall",
+ "Iacutesmall",
+ "Igravesmall",
+ "Icircumflexsmall",
+ "Idieresissmall",
+ "Ntildesmall",
+ "Oacutesmall",
+ "Ogravesmall",
+ "Ocircumflexsmall",
+ "Odieresissmall",
+ "Otildesmall",
+ "Uacutesmall",
+ "Ugravesmall",
+ "Ucircumflexsmall",
+ "Udieresissmall",
+ NULL,
+ "eightsuperior",
+ "fourinferior",
+ "threeinferior",
+ "sixinferior",
+ "eightinferior",
+ "seveninferior",
+ "Scaronsmall",
+ NULL,
+ "centinferior",
+ "twoinferior",
+ NULL,
+ "Dieresissmall",
+ NULL,
+ "Caronsmall",
+ "osuperior",
+ "fiveinferior",
+ NULL,
+ "commainferior",
+ "periodinferior",
+ "Yacutesmall",
+ NULL,
+ "dollarinferior",
+ NULL,
+ NULL,
+ "Thornsmall",
+ NULL,
+ "nineinferior",
+ "zeroinferior",
+ "Zcaronsmall",
+ "AEsmall",
+ "Oslashsmall",
+ "questiondownsmall",
+ "oneinferior",
+ "Lslashsmall",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Cedillasmall",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "OEsmall",
+ "figuredash",
+ "hyphensuperior",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "exclamdownsmall",
+ NULL,
+ "Ydieresissmall",
+ NULL,
+ "onesuperior",
+ "twosuperior",
+ "threesuperior",
+ "foursuperior",
+ "fivesuperior",
+ "sixsuperior",
+ "sevensuperior",
+ "ninesuperior",
+ "zerosuperior",
+ NULL,
+ "esuperior",
+ "rsuperior",
+ "tsuperior",
+ NULL,
+ NULL,
+ "isuperior",
+ "ssuperior",
+ "dsuperior",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "lsuperior",
+ "Ogoneksmall",
+ "Brevesmall",
+ "Macronsmall",
+ "bsuperior",
+ "nsuperior",
+ "msuperior",
+ "commasuperior",
+ "periodsuperior",
+ "Dotaccentsmall",
+ "Ringsmall",
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+char *winAnsiEncoding[256] = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "space",
+ "exclam",
+ "quotedbl",
+ "numbersign",
+ "dollar",
+ "percent",
+ "ampersand",
+ "quotesingle",
+ "parenleft",
+ "parenright",
+ "asterisk",
+ "plus",
+ "comma",
+ "hyphen",
+ "period",
+ "slash",
+ "zero",
+ "one",
+ "two",
+ "three",
+ "four",
+ "five",
+ "six",
+ "seven",
+ "eight",
+ "nine",
+ "colon",
+ "semicolon",
+ "less",
+ "equal",
+ "greater",
+ "question",
+ "at",
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I",
+ "J",
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P",
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W",
+ "X",
+ "Y",
+ "Z",
+ "bracketleft",
+ "backslash",
+ "bracketright",
+ "asciicircum",
+ "underscore",
+ "grave",
+ "a",
+ "b",
+ "c",
+ "d",
+ "e",
+ "f",
+ "g",
+ "h",
+ "i",
+ "j",
+ "k",
+ "l",
+ "m",
+ "n",
+ "o",
+ "p",
+ "q",
+ "r",
+ "s",
+ "t",
+ "u",
+ "v",
+ "w",
+ "x",
+ "y",
+ "z",
+ "braceleft",
+ "bar",
+ "braceright",
+ "asciitilde",
+ "bullet",
+ "Euro",
+ "bullet",
+ "quotesinglbase",
+ "florin",
+ "quotedblbase",
+ "ellipsis",
+ "dagger",
+ "daggerdbl",
+ "circumflex",
+ "perthousand",
+ "Scaron",
+ "guilsinglleft",
+ "OE",
+ "bullet",
+ "Zcaron",
+ "bullet",
+ "bullet",
+ "quoteleft",
+ "quoteright",
+ "quotedblleft",
+ "quotedblright",
+ "bullet",
+ "endash",
+ "emdash",
+ "tilde",
+ "trademark",
+ "scaron",
+ "guilsinglright",
+ "oe",
+ "bullet",
+ "zcaron",
+ "Ydieresis",
+ "space",
+ "exclamdown",
+ "cent",
+ "sterling",
+ "currency",
+ "yen",
+ "brokenbar",
+ "section",
+ "dieresis",
+ "copyright",
+ "ordfeminine",
+ "guillemotleft",
+ "logicalnot",
+ "hyphen",
+ "registered",
+ "macron",
+ "degree",
+ "plusminus",
+ "twosuperior",
+ "threesuperior",
+ "acute",
+ "mu",
+ "paragraph",
+ "periodcentered",
+ "cedilla",
+ "onesuperior",
+ "ordmasculine",
+ "guillemotright",
+ "onequarter",
+ "onehalf",
+ "threequarters",
+ "questiondown",
+ "Agrave",
+ "Aacute",
+ "Acircumflex",
+ "Atilde",
+ "Adieresis",
+ "Aring",
+ "AE",
+ "Ccedilla",
+ "Egrave",
+ "Eacute",
+ "Ecircumflex",
+ "Edieresis",
+ "Igrave",
+ "Iacute",
+ "Icircumflex",
+ "Idieresis",
+ "Eth",
+ "Ntilde",
+ "Ograve",
+ "Oacute",
+ "Ocircumflex",
+ "Otilde",
+ "Odieresis",
+ "multiply",
+ "Oslash",
+ "Ugrave",
+ "Uacute",
+ "Ucircumflex",
+ "Udieresis",
+ "Yacute",
+ "Thorn",
+ "germandbls",
+ "agrave",
+ "aacute",
+ "acircumflex",
+ "atilde",
+ "adieresis",
+ "aring",
+ "ae",
+ "ccedilla",
+ "egrave",
+ "eacute",
+ "ecircumflex",
+ "edieresis",
+ "igrave",
+ "iacute",
+ "icircumflex",
+ "idieresis",
+ "eth",
+ "ntilde",
+ "ograve",
+ "oacute",
+ "ocircumflex",
+ "otilde",
+ "odieresis",
+ "divide",
+ "oslash",
+ "ugrave",
+ "uacute",
+ "ucircumflex",
+ "udieresis",
+ "yacute",
+ "thorn",
+ "ydieresis"
+};
+
+char *standardEncoding[256] = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "space",
+ "exclam",
+ "quotedbl",
+ "numbersign",
+ "dollar",
+ "percent",
+ "ampersand",
+ "quoteright",
+ "parenleft",
+ "parenright",
+ "asterisk",
+ "plus",
+ "comma",
+ "hyphen",
+ "period",
+ "slash",
+ "zero",
+ "one",
+ "two",
+ "three",
+ "four",
+ "five",
+ "six",
+ "seven",
+ "eight",
+ "nine",
+ "colon",
+ "semicolon",
+ "less",
+ "equal",
+ "greater",
+ "question",
+ "at",
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I",
+ "J",
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P",
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W",
+ "X",
+ "Y",
+ "Z",
+ "bracketleft",
+ "backslash",
+ "bracketright",
+ "asciicircum",
+ "underscore",
+ "quoteleft",
+ "a",
+ "b",
+ "c",
+ "d",
+ "e",
+ "f",
+ "g",
+ "h",
+ "i",
+ "j",
+ "k",
+ "l",
+ "m",
+ "n",
+ "o",
+ "p",
+ "q",
+ "r",
+ "s",
+ "t",
+ "u",
+ "v",
+ "w",
+ "x",
+ "y",
+ "z",
+ "braceleft",
+ "bar",
+ "braceright",
+ "asciitilde",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "exclamdown",
+ "cent",
+ "sterling",
+ "fraction",
+ "yen",
+ "florin",
+ "section",
+ "currency",
+ "quotesingle",
+ "quotedblleft",
+ "guillemotleft",
+ "guilsinglleft",
+ "guilsinglright",
+ "fi",
+ "fl",
+ NULL,
+ "endash",
+ "dagger",
+ "daggerdbl",
+ "periodcentered",
+ NULL,
+ "paragraph",
+ "bullet",
+ "quotesinglbase",
+ "quotedblbase",
+ "quotedblright",
+ "guillemotright",
+ "ellipsis",
+ "perthousand",
+ NULL,
+ "questiondown",
+ NULL,
+ "grave",
+ "acute",
+ "circumflex",
+ "tilde",
+ "macron",
+ "breve",
+ "dotaccent",
+ "dieresis",
+ NULL,
+ "ring",
+ "cedilla",
+ NULL,
+ "hungarumlaut",
+ "ogonek",
+ "caron",
+ "emdash",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "AE",
+ NULL,
+ "ordfeminine",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Lslash",
+ "Oslash",
+ "OE",
+ "ordmasculine",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "ae",
+ NULL,
+ NULL,
+ NULL,
+ "dotlessi",
+ NULL,
+ NULL,
+ "lslash",
+ "oslash",
+ "oe",
+ "germandbls",
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+char *expertEncoding[256] = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "space",
+ "exclamsmall",
+ "Hungarumlautsmall",
+ NULL,
+ "dollaroldstyle",
+ "dollarsuperior",
+ "ampersandsmall",
+ "Acutesmall",
+ "parenleftsuperior",
+ "parenrightsuperior",
+ "twodotenleader",
+ "onedotenleader",
+ "comma",
+ "hyphen",
+ "period",
+ "fraction",
+ "zerooldstyle",
+ "oneoldstyle",
+ "twooldstyle",
+ "threeoldstyle",
+ "fouroldstyle",
+ "fiveoldstyle",
+ "sixoldstyle",
+ "sevenoldstyle",
+ "eightoldstyle",
+ "nineoldstyle",
+ "colon",
+ "semicolon",
+ "commasuperior",
+ "threequartersemdash",
+ "periodsuperior",
+ "questionsmall",
+ NULL,
+ "asuperior",
+ "bsuperior",
+ "centsuperior",
+ "dsuperior",
+ "esuperior",
+ NULL,
+ NULL,
+ NULL,
+ "isuperior",
+ NULL,
+ NULL,
+ "lsuperior",
+ "msuperior",
+ "nsuperior",
+ "osuperior",
+ NULL,
+ NULL,
+ "rsuperior",
+ "ssuperior",
+ "tsuperior",
+ NULL,
+ "ff",
+ "fi",
+ "fl",
+ "ffi",
+ "ffl",
+ "parenleftinferior",
+ NULL,
+ "parenrightinferior",
+ "Circumflexsmall",
+ "hyphensuperior",
+ "Gravesmall",
+ "Asmall",
+ "Bsmall",
+ "Csmall",
+ "Dsmall",
+ "Esmall",
+ "Fsmall",
+ "Gsmall",
+ "Hsmall",
+ "Ismall",
+ "Jsmall",
+ "Ksmall",
+ "Lsmall",
+ "Msmall",
+ "Nsmall",
+ "Osmall",
+ "Psmall",
+ "Qsmall",
+ "Rsmall",
+ "Ssmall",
+ "Tsmall",
+ "Usmall",
+ "Vsmall",
+ "Wsmall",
+ "Xsmall",
+ "Ysmall",
+ "Zsmall",
+ "colonmonetary",
+ "onefitted",
+ "rupiah",
+ "Tildesmall",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "exclamdownsmall",
+ "centoldstyle",
+ "Lslashsmall",
+ NULL,
+ NULL,
+ "Scaronsmall",
+ "Zcaronsmall",
+ "Dieresissmall",
+ "Brevesmall",
+ "Caronsmall",
+ NULL,
+ "Dotaccentsmall",
+ NULL,
+ NULL,
+ "Macronsmall",
+ NULL,
+ NULL,
+ "figuredash",
+ "hypheninferior",
+ NULL,
+ NULL,
+ "Ogoneksmall",
+ "Ringsmall",
+ "Cedillasmall",
+ NULL,
+ NULL,
+ NULL,
+ "onequarter",
+ "onehalf",
+ "threequarters",
+ "questiondownsmall",
+ "oneeighth",
+ "threeeighths",
+ "fiveeighths",
+ "seveneighths",
+ "onethird",
+ "twothirds",
+ NULL,
+ NULL,
+ "zerosuperior",
+ "onesuperior",
+ "twosuperior",
+ "threesuperior",
+ "foursuperior",
+ "fivesuperior",
+ "sixsuperior",
+ "sevensuperior",
+ "eightsuperior",
+ "ninesuperior",
+ "zeroinferior",
+ "oneinferior",
+ "twoinferior",
+ "threeinferior",
+ "fourinferior",
+ "fiveinferior",
+ "sixinferior",
+ "seveninferior",
+ "eightinferior",
+ "nineinferior",
+ "centinferior",
+ "dollarinferior",
+ "periodinferior",
+ "commainferior",
+ "Agravesmall",
+ "Aacutesmall",
+ "Acircumflexsmall",
+ "Atildesmall",
+ "Adieresissmall",
+ "Aringsmall",
+ "AEsmall",
+ "Ccedillasmall",
+ "Egravesmall",
+ "Eacutesmall",
+ "Ecircumflexsmall",
+ "Edieresissmall",
+ "Igravesmall",
+ "Iacutesmall",
+ "Icircumflexsmall",
+ "Idieresissmall",
+ "Ethsmall",
+ "Ntildesmall",
+ "Ogravesmall",
+ "Oacutesmall",
+ "Ocircumflexsmall",
+ "Otildesmall",
+ "Odieresissmall",
+ "OEsmall",
+ "Oslashsmall",
+ "Ugravesmall",
+ "Uacutesmall",
+ "Ucircumflexsmall",
+ "Udieresissmall",
+ "Yacutesmall",
+ "Thornsmall",
+ "Ydieresissmall"
+};
+
+char *symbolEncoding[256] = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "space",
+ "exclam",
+ "universal",
+ "numbersign",
+ "existential",
+ "percent",
+ "ampersand",
+ "suchthat",
+ "parenleft",
+ "parenright",
+ "asteriskmath",
+ "plus",
+ "comma",
+ "minus",
+ "period",
+ "slash",
+ "zero",
+ "one",
+ "two",
+ "three",
+ "four",
+ "five",
+ "six",
+ "seven",
+ "eight",
+ "nine",
+ "colon",
+ "semicolon",
+ "less",
+ "equal",
+ "greater",
+ "question",
+ "congruent",
+ "Alpha",
+ "Beta",
+ "Chi",
+ "Delta",
+ "Epsilon",
+ "Phi",
+ "Gamma",
+ "Eta",
+ "Iota",
+ "theta1",
+ "Kappa",
+ "Lambda",
+ "Mu",
+ "Nu",
+ "Omicron",
+ "Pi",
+ "Theta",
+ "Rho",
+ "Sigma",
+ "Tau",
+ "Upsilon",
+ "sigma1",
+ "Omega",
+ "Xi",
+ "Psi",
+ "Zeta",
+ "bracketleft",
+ "therefore",
+ "bracketright",
+ "perpendicular",
+ "underscore",
+ "radicalex",
+ "alpha",
+ "beta",
+ "chi",
+ "delta",
+ "epsilon",
+ "phi",
+ "gamma",
+ "eta",
+ "iota",
+ "phi1",
+ "kappa",
+ "lambda",
+ "mu",
+ "nu",
+ "omicron",
+ "pi",
+ "theta",
+ "rho",
+ "sigma",
+ "tau",
+ "upsilon",
+ "omega1",
+ "omega",
+ "xi",
+ "psi",
+ "zeta",
+ "braceleft",
+ "bar",
+ "braceright",
+ "similar",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Upsilon1",
+ "minute",
+ "lessequal",
+ "fraction",
+ "infinity",
+ "florin",
+ "club",
+ "diamond",
+ "heart",
+ "spade",
+ "arrowboth",
+ "arrowleft",
+ "arrowup",
+ "arrowright",
+ "arrowdown",
+ "degree",
+ "plusminus",
+ "second",
+ "greaterequal",
+ "multiply",
+ "proportional",
+ "partialdiff",
+ "bullet",
+ "divide",
+ "notequal",
+ "equivalence",
+ "approxequal",
+ "ellipsis",
+ "arrowvertex",
+ "arrowhorizex",
+ "carriagereturn",
+ "aleph",
+ "Ifraktur",
+ "Rfraktur",
+ "weierstrass",
+ "circlemultiply",
+ "circleplus",
+ "emptyset",
+ "intersection",
+ "union",
+ "propersuperset",
+ "reflexsuperset",
+ "notsubset",
+ "propersubset",
+ "reflexsubset",
+ "element",
+ "notelement",
+ "angle",
+ "gradient",
+ "registerserif",
+ "copyrightserif",
+ "trademarkserif",
+ "product",
+ "radical",
+ "dotmath",
+ "logicalnot",
+ "logicaland",
+ "logicalor",
+ "arrowdblboth",
+ "arrowdblleft",
+ "arrowdblup",
+ "arrowdblright",
+ "arrowdbldown",
+ "lozenge",
+ "angleleft",
+ "registersans",
+ "copyrightsans",
+ "trademarksans",
+ "summation",
+ "parenlefttp",
+ "parenleftex",
+ "parenleftbt",
+ "bracketlefttp",
+ "bracketleftex",
+ "bracketleftbt",
+ "bracelefttp",
+ "braceleftmid",
+ "braceleftbt",
+ "braceex",
+ NULL,
+ "angleright",
+ "integral",
+ "integraltp",
+ "integralex",
+ "integralbt",
+ "parenrighttp",
+ "parenrightex",
+ "parenrightbt",
+ "bracketrighttp",
+ "bracketrightex",
+ "bracketrightbt",
+ "bracerighttp",
+ "bracerightmid",
+ "bracerightbt",
+ NULL
+};
+
+char *zapfDingbatsEncoding[256] = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "space",
+ "a1",
+ "a2",
+ "a202",
+ "a3",
+ "a4",
+ "a5",
+ "a119",
+ "a118",
+ "a117",
+ "a11",
+ "a12",
+ "a13",
+ "a14",
+ "a15",
+ "a16",
+ "a105",
+ "a17",
+ "a18",
+ "a19",
+ "a20",
+ "a21",
+ "a22",
+ "a23",
+ "a24",
+ "a25",
+ "a26",
+ "a27",
+ "a28",
+ "a6",
+ "a7",
+ "a8",
+ "a9",
+ "a10",
+ "a29",
+ "a30",
+ "a31",
+ "a32",
+ "a33",
+ "a34",
+ "a35",
+ "a36",
+ "a37",
+ "a38",
+ "a39",
+ "a40",
+ "a41",
+ "a42",
+ "a43",
+ "a44",
+ "a45",
+ "a46",
+ "a47",
+ "a48",
+ "a49",
+ "a50",
+ "a51",
+ "a52",
+ "a53",
+ "a54",
+ "a55",
+ "a56",
+ "a57",
+ "a58",
+ "a59",
+ "a60",
+ "a61",
+ "a62",
+ "a63",
+ "a64",
+ "a65",
+ "a66",
+ "a67",
+ "a68",
+ "a69",
+ "a70",
+ "a71",
+ "a72",
+ "a73",
+ "a74",
+ "a203",
+ "a75",
+ "a204",
+ "a76",
+ "a77",
+ "a78",
+ "a79",
+ "a81",
+ "a82",
+ "a83",
+ "a84",
+ "a97",
+ "a98",
+ "a99",
+ "a100",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "a101",
+ "a102",
+ "a103",
+ "a104",
+ "a106",
+ "a107",
+ "a108",
+ "a112",
+ "a111",
+ "a110",
+ "a109",
+ "a120",
+ "a121",
+ "a122",
+ "a123",
+ "a124",
+ "a125",
+ "a126",
+ "a127",
+ "a128",
+ "a129",
+ "a130",
+ "a131",
+ "a132",
+ "a133",
+ "a134",
+ "a135",
+ "a136",
+ "a137",
+ "a138",
+ "a139",
+ "a140",
+ "a141",
+ "a142",
+ "a143",
+ "a144",
+ "a145",
+ "a146",
+ "a147",
+ "a148",
+ "a149",
+ "a150",
+ "a151",
+ "a152",
+ "a153",
+ "a154",
+ "a155",
+ "a156",
+ "a157",
+ "a158",
+ "a159",
+ "a160",
+ "a161",
+ "a163",
+ "a164",
+ "a196",
+ "a165",
+ "a192",
+ "a166",
+ "a167",
+ "a168",
+ "a169",
+ "a170",
+ "a171",
+ "a172",
+ "a173",
+ "a162",
+ "a174",
+ "a175",
+ "a176",
+ "a177",
+ "a178",
+ "a179",
+ "a193",
+ "a180",
+ "a199",
+ "a181",
+ "a200",
+ "a182",
+ NULL,
+ "a201",
+ "a183",
+ "a184",
+ "a197",
+ "a185",
+ "a194",
+ "a198",
+ "a186",
+ "a195",
+ "a187",
+ "a188",
+ "a189",
+ "a190",
+ "a191",
+ NULL
+};
--- /dev/null
+//========================================================================
+//
+// FontEncodingTables.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FONTENCODINGTABLES_H
+#define FONTENCODINGTABLES_H
+
+extern char *macRomanEncoding[];
+extern char *macExpertEncoding[];
+extern char *winAnsiEncoding[];
+extern char *standardEncoding[];
+extern char *expertEncoding[];
+extern char *symbolEncoding[];
+extern char *zapfDingbatsEncoding[];
+
+#endif
--- /dev/null
+//========================================================================
+//
+// Function.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include "gmem.h"
+#include "Object.h"
+#include "Dict.h"
+#include "Stream.h"
+#include "Error.h"
+#include "Function.h"
+
+//------------------------------------------------------------------------
+// Function
+//------------------------------------------------------------------------
+
+Function::Function() {
+}
+
+Function::~Function() {
+}
+
+Function *Function::parse(Object *funcObj) {
+ Function *func;
+ Dict *dict;
+ int funcType;
+ Object obj1;
+
+ if (funcObj->isStream()) {
+ dict = funcObj->streamGetDict();
+ } else if (funcObj->isDict()) {
+ dict = funcObj->getDict();
+ } else if (funcObj->isName("Identity")) {
+ return new IdentityFunction();
+ } else {
+ error(-1, "Expected function dictionary or stream");
+ return NULL;
+ }
+
+ if (!dict->lookup("FunctionType", &obj1)->isInt()) {
+ error(-1, "Function type is missing or wrong type");
+ obj1.free();
+ return NULL;
+ }
+ funcType = obj1.getInt();
+ obj1.free();
+
+ if (funcType == 0) {
+ func = new SampledFunction(funcObj, dict);
+ } else if (funcType == 2) {
+ func = new ExponentialFunction(funcObj, dict);
+ } else if (funcType == 3) {
+ func = new StitchingFunction(funcObj, dict);
+ } else if (funcType == 4) {
+ func = new PostScriptFunction(funcObj, dict);
+ } else {
+ error(-1, "Unimplemented function type (%d)", funcType);
+ return NULL;
+ }
+ if (!func->isOk()) {
+ delete func;
+ return NULL;
+ }
+
+ return func;
+}
+
+GBool Function::init(Dict *dict) {
+ Object obj1, obj2;
+ int i;
+
+ //----- Domain
+ if (!dict->lookup("Domain", &obj1)->isArray()) {
+ error(-1, "Function is missing domain");
+ goto err2;
+ }
+ m = obj1.arrayGetLength() / 2;
+ if (m > funcMaxInputs) {
+ error(-1, "Functions with more than %d inputs are unsupported",
+ funcMaxInputs);
+ goto err2;
+ }
+ for (i = 0; i < m; ++i) {
+ obj1.arrayGet(2*i, &obj2);
+ if (!obj2.isNum()) {
+ error(-1, "Illegal value in function domain array");
+ goto err1;
+ }
+ domain[i][0] = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(2*i+1, &obj2);
+ if (!obj2.isNum()) {
+ error(-1, "Illegal value in function domain array");
+ goto err1;
+ }
+ domain[i][1] = obj2.getNum();
+ obj2.free();
+ }
+ obj1.free();
+
+ //----- Range
+ hasRange = gFalse;
+ n = 0;
+ if (dict->lookup("Range", &obj1)->isArray()) {
+ hasRange = gTrue;
+ n = obj1.arrayGetLength() / 2;
+ if (n > funcMaxOutputs) {
+ error(-1, "Functions with more than %d outputs are unsupported",
+ funcMaxOutputs);
+ goto err2;
+ }
+ for (i = 0; i < n; ++i) {
+ obj1.arrayGet(2*i, &obj2);
+ if (!obj2.isNum()) {
+ error(-1, "Illegal value in function range array");
+ goto err1;
+ }
+ range[i][0] = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(2*i+1, &obj2);
+ if (!obj2.isNum()) {
+ error(-1, "Illegal value in function range array");
+ goto err1;
+ }
+ range[i][1] = obj2.getNum();
+ obj2.free();
+ }
+ }
+ obj1.free();
+
+ return gTrue;
+
+ err1:
+ obj2.free();
+ err2:
+ obj1.free();
+ return gFalse;
+}
+
+//------------------------------------------------------------------------
+// IdentityFunction
+//------------------------------------------------------------------------
+
+IdentityFunction::IdentityFunction() {
+ int i;
+
+ // fill these in with arbitrary values just in case they get used
+ // somewhere
+ m = funcMaxInputs;
+ n = funcMaxOutputs;
+ for (i = 0; i < funcMaxInputs; ++i) {
+ domain[i][0] = 0;
+ domain[i][1] = 1;
+ }
+ hasRange = gFalse;
+}
+
+IdentityFunction::~IdentityFunction() {
+}
+
+void IdentityFunction::transform(double *in, double *out) {
+ int i;
+
+ for (i = 0; i < funcMaxOutputs; ++i) {
+ out[i] = in[i];
+ }
+}
+
+//------------------------------------------------------------------------
+// SampledFunction
+//------------------------------------------------------------------------
+
+SampledFunction::SampledFunction(Object *funcObj, Dict *dict) {
+ Stream *str;
+ int sampleBits;
+ double sampleMul;
+ Object obj1, obj2;
+ Guint buf, bitMask;
+ int bits;
+ int s;
+ int i;
+
+ samples = NULL;
+ ok = gFalse;
+
+ //----- initialize the generic stuff
+ if (!init(dict)) {
+ goto err1;
+ }
+ if (!hasRange) {
+ error(-1, "Type 0 function is missing range");
+ goto err1;
+ }
+
+ //----- get the stream
+ if (!funcObj->isStream()) {
+ error(-1, "Type 0 function isn't a stream");
+ goto err1;
+ }
+ str = funcObj->getStream();
+
+ //----- Size
+ if (!dict->lookup("Size", &obj1)->isArray() ||
+ obj1.arrayGetLength() != m) {
+ error(-1, "Function has missing or invalid size array");
+ goto err2;
+ }
+ for (i = 0; i < m; ++i) {
+ obj1.arrayGet(i, &obj2);
+ if (!obj2.isInt()) {
+ error(-1, "Illegal value in function size array");
+ goto err3;
+ }
+ sampleSize[i] = obj2.getInt();
+ obj2.free();
+ }
+ obj1.free();
+ idxMul[0] = n;
+ for (i = 1; i < m; ++i) {
+ idxMul[i] = idxMul[i-1] * sampleSize[i-1];
+ }
+
+ //----- BitsPerSample
+ if (!dict->lookup("BitsPerSample", &obj1)->isInt()) {
+ error(-1, "Function has missing or invalid BitsPerSample");
+ goto err2;
+ }
+ sampleBits = obj1.getInt();
+ sampleMul = 1.0 / (double)((1 << sampleBits) - 1);
+ obj1.free();
+
+ //----- Encode
+ if (dict->lookup("Encode", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 2*m) {
+ for (i = 0; i < m; ++i) {
+ obj1.arrayGet(2*i, &obj2);
+ if (!obj2.isNum()) {
+ error(-1, "Illegal value in function encode array");
+ goto err3;
+ }
+ encode[i][0] = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(2*i+1, &obj2);
+ if (!obj2.isNum()) {
+ error(-1, "Illegal value in function encode array");
+ goto err3;
+ }
+ encode[i][1] = obj2.getNum();
+ obj2.free();
+ }
+ } else {
+ for (i = 0; i < m; ++i) {
+ encode[i][0] = 0;
+ encode[i][1] = sampleSize[i] - 1;
+ }
+ }
+ obj1.free();
+ for (i = 0; i < m; ++i) {
+ inputMul[i] = (encode[i][1] - encode[i][0]) /
+ (domain[i][1] - domain[i][0]);
+ }
+
+ //----- Decode
+ if (dict->lookup("Decode", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 2*n) {
+ for (i = 0; i < n; ++i) {
+ obj1.arrayGet(2*i, &obj2);
+ if (!obj2.isNum()) {
+ error(-1, "Illegal value in function decode array");
+ goto err3;
+ }
+ decode[i][0] = obj2.getNum();
+ obj2.free();
+ obj1.arrayGet(2*i+1, &obj2);
+ if (!obj2.isNum()) {
+ error(-1, "Illegal value in function decode array");
+ goto err3;
+ }
+ decode[i][1] = obj2.getNum();
+ obj2.free();
+ }
+ } else {
+ for (i = 0; i < n; ++i) {
+ decode[i][0] = range[i][0];
+ decode[i][1] = range[i][1];
+ }
+ }
+ obj1.free();
+
+ //----- samples
+ nSamples = n;
+ for (i = 0; i < m; ++i)
+ nSamples *= sampleSize[i];
+ samples = (double *)gmallocn(nSamples, sizeof(double));
+ buf = 0;
+ bits = 0;
+ bitMask = (1 << sampleBits) - 1;
+ str->reset();
+ for (i = 0; i < nSamples; ++i) {
+ if (sampleBits == 8) {
+ s = str->getChar();
+ } else if (sampleBits == 16) {
+ s = str->getChar();
+ s = (s << 8) + str->getChar();
+ } else if (sampleBits == 32) {
+ s = str->getChar();
+ s = (s << 8) + str->getChar();
+ s = (s << 8) + str->getChar();
+ s = (s << 8) + str->getChar();
+ } else {
+ while (bits < sampleBits) {
+ buf = (buf << 8) | (str->getChar() & 0xff);
+ bits += 8;
+ }
+ s = (buf >> (bits - sampleBits)) & bitMask;
+ bits -= sampleBits;
+ }
+ samples[i] = (double)s * sampleMul;
+ }
+ str->close();
+
+ ok = gTrue;
+ return;
+
+ err3:
+ obj2.free();
+ err2:
+ obj1.free();
+ err1:
+ return;
+}
+
+SampledFunction::~SampledFunction() {
+ if (samples) {
+ gfree(samples);
+ }
+}
+
+SampledFunction::SampledFunction(SampledFunction *func) {
+ memcpy(this, func, sizeof(SampledFunction));
+ samples = (double *)gmallocn(nSamples, sizeof(double));
+ memcpy(samples, func->samples, nSamples * sizeof(double));
+}
+
+void SampledFunction::transform(double *in, double *out) {
+ double x;
+ int e[funcMaxInputs][2];
+ double efrac0[funcMaxInputs];
+ double efrac1[funcMaxInputs];
+ double s[1 << funcMaxInputs];
+ int i, j, k, idx, t;
+
+ // map input values into sample array
+ for (i = 0; i < m; ++i) {
+ x = (in[i] - domain[i][0]) * inputMul[i] + encode[i][0];
+ if (x < 0) {
+ x = 0;
+ } else if (x > sampleSize[i] - 1) {
+ x = sampleSize[i] - 1;
+ }
+ e[i][0] = (int)x;
+ if ((e[i][1] = e[i][0] + 1) >= sampleSize[i]) {
+ // this happens if in[i] = domain[i][1]
+ e[i][1] = e[i][0];
+ }
+ efrac1[i] = x - e[i][0];
+ efrac0[i] = 1 - efrac1[i];
+ }
+
+ // for each output, do m-linear interpolation
+ for (i = 0; i < n; ++i) {
+
+ // pull 2^m values out of the sample array
+ for (j = 0; j < (1<<m); ++j) {
+ idx = i;
+ for (k = 0, t = j; k < m; ++k, t >>= 1) {
+ idx += idxMul[k] * (e[k][t & 1]);
+ }
+ s[j] = samples[idx];
+ }
+
+ // do m sets of interpolations
+ for (j = 0, t = (1<<m); j < m; ++j, t >>= 1) {
+ for (k = 0; k < t; k += 2) {
+ s[k >> 1] = efrac0[j] * s[k] + efrac1[j] * s[k+1];
+ }
+ }
+
+ // map output value to range
+ out[i] = s[0] * (decode[i][1] - decode[i][0]) + decode[i][0];
+ if (out[i] < range[i][0]) {
+ out[i] = range[i][0];
+ } else if (out[i] > range[i][1]) {
+ out[i] = range[i][1];
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+// ExponentialFunction
+//------------------------------------------------------------------------
+
+ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) {
+ Object obj1, obj2;
+ int i;
+
+ ok = gFalse;
+
+ //----- initialize the generic stuff
+ if (!init(dict)) {
+ goto err1;
+ }
+ if (m != 1) {
+ error(-1, "Exponential function with more than one input");
+ goto err1;
+ }
+
+ //----- C0
+ if (dict->lookup("C0", &obj1)->isArray()) {
+ if (hasRange && obj1.arrayGetLength() != n) {
+ error(-1, "Function's C0 array is wrong length");
+ goto err2;
+ }
+ n = obj1.arrayGetLength();
+ for (i = 0; i < n; ++i) {
+ obj1.arrayGet(i, &obj2);
+ if (!obj2.isNum()) {
+ error(-1, "Illegal value in function C0 array");
+ goto err3;
+ }
+ c0[i] = obj2.getNum();
+ obj2.free();
+ }
+ } else {
+ if (hasRange && n != 1) {
+ error(-1, "Function's C0 array is wrong length");
+ goto err2;
+ }
+ n = 1;
+ c0[0] = 0;
+ }
+ obj1.free();
+
+ //----- C1
+ if (dict->lookup("C1", &obj1)->isArray()) {
+ if (obj1.arrayGetLength() != n) {
+ error(-1, "Function's C1 array is wrong length");
+ goto err2;
+ }
+ for (i = 0; i < n; ++i) {
+ obj1.arrayGet(i, &obj2);
+ if (!obj2.isNum()) {
+ error(-1, "Illegal value in function C1 array");
+ goto err3;
+ }
+ c1[i] = obj2.getNum();
+ obj2.free();
+ }
+ } else {
+ if (n != 1) {
+ error(-1, "Function's C1 array is wrong length");
+ goto err2;
+ }
+ c1[0] = 1;
+ }
+ obj1.free();
+
+ //----- N (exponent)
+ if (!dict->lookup("N", &obj1)->isNum()) {
+ error(-1, "Function has missing or invalid N");
+ goto err2;
+ }
+ e = obj1.getNum();
+ obj1.free();
+
+ ok = gTrue;
+ return;
+
+ err3:
+ obj2.free();
+ err2:
+ obj1.free();
+ err1:
+ return;
+}
+
+ExponentialFunction::~ExponentialFunction() {
+}
+
+ExponentialFunction::ExponentialFunction(ExponentialFunction *func) {
+ memcpy(this, func, sizeof(ExponentialFunction));
+}
+
+void ExponentialFunction::transform(double *in, double *out) {
+ double x;
+ int i;
+
+ if (in[0] < domain[0][0]) {
+ x = domain[0][0];
+ } else if (in[0] > domain[0][1]) {
+ x = domain[0][1];
+ } else {
+ x = in[0];
+ }
+ for (i = 0; i < n; ++i) {
+ out[i] = c0[i] + pow(x, e) * (c1[i] - c0[i]);
+ if (hasRange) {
+ if (out[i] < range[i][0]) {
+ out[i] = range[i][0];
+ } else if (out[i] > range[i][1]) {
+ out[i] = range[i][1];
+ }
+ }
+ }
+ return;
+}
+
+//------------------------------------------------------------------------
+// StitchingFunction
+//------------------------------------------------------------------------
+
+StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict) {
+ Object obj1, obj2;
+ int i;
+
+ ok = gFalse;
+ funcs = NULL;
+ bounds = NULL;
+ encode = NULL;
+
+ //----- initialize the generic stuff
+ if (!init(dict)) {
+ goto err1;
+ }
+ if (m != 1) {
+ error(-1, "Stitching function with more than one input");
+ goto err1;
+ }
+
+ //----- Functions
+ if (!dict->lookup("Functions", &obj1)->isArray()) {
+ error(-1, "Missing 'Functions' entry in stitching function");
+ goto err1;
+ }
+ k = obj1.arrayGetLength();
+ funcs = (Function **)gmallocn(k, sizeof(Function *));
+ bounds = (double *)gmallocn(k + 1, sizeof(double));
+ encode = (double *)gmallocn(2 * k, sizeof(double));
+ for (i = 0; i < k; ++i) {
+ funcs[i] = NULL;
+ }
+ for (i = 0; i < k; ++i) {
+ if (!(funcs[i] = Function::parse(obj1.arrayGet(i, &obj2)))) {
+ goto err2;
+ }
+ if (i > 0 && (funcs[i]->getInputSize() != 1 ||
+ funcs[i]->getOutputSize() != funcs[0]->getOutputSize())) {
+ error(-1, "Incompatible subfunctions in stitching function");
+ goto err2;
+ }
+ obj2.free();
+ }
+ obj1.free();
+
+ //----- Bounds
+ if (!dict->lookup("Bounds", &obj1)->isArray() ||
+ obj1.arrayGetLength() != k - 1) {
+ error(-1, "Missing or invalid 'Bounds' entry in stitching function");
+ goto err1;
+ }
+ bounds[0] = domain[0][0];
+ for (i = 1; i < k; ++i) {
+ if (!obj1.arrayGet(i - 1, &obj2)->isNum()) {
+ error(-1, "Invalid type in 'Bounds' array in stitching function");
+ goto err2;
+ }
+ bounds[i] = obj2.getNum();
+ obj2.free();
+ }
+ bounds[k] = domain[0][1];
+ obj1.free();
+
+ //----- Encode
+ if (!dict->lookup("Encode", &obj1)->isArray() ||
+ obj1.arrayGetLength() != 2 * k) {
+ error(-1, "Missing or invalid 'Encode' entry in stitching function");
+ goto err1;
+ }
+ for (i = 0; i < 2 * k; ++i) {
+ if (!obj1.arrayGet(i, &obj2)->isNum()) {
+ error(-1, "Invalid type in 'Encode' array in stitching function");
+ goto err2;
+ }
+ encode[i] = obj2.getNum();
+ obj2.free();
+ }
+ obj1.free();
+
+ ok = gTrue;
+ return;
+
+ err2:
+ obj2.free();
+ err1:
+ obj1.free();
+}
+
+StitchingFunction::StitchingFunction(StitchingFunction *func) {
+ int i;
+
+ k = func->k;
+ funcs = (Function **)gmallocn(k, sizeof(Function *));
+ for (i = 0; i < k; ++i) {
+ funcs[i] = func->funcs[i]->copy();
+ }
+ bounds = (double *)gmallocn(k + 1, sizeof(double));
+ memcpy(bounds, func->bounds, (k + 1) * sizeof(double));
+ encode = (double *)gmallocn(2 * k, sizeof(double));
+ memcpy(encode, func->encode, 2 * k * sizeof(double));
+ ok = gTrue;
+}
+
+StitchingFunction::~StitchingFunction() {
+ int i;
+
+ if (funcs) {
+ for (i = 0; i < k; ++i) {
+ if (funcs[i]) {
+ delete funcs[i];
+ }
+ }
+ }
+ gfree(funcs);
+ gfree(bounds);
+ gfree(encode);
+}
+
+void StitchingFunction::transform(double *in, double *out) {
+ double x;
+ int i;
+
+ if (in[0] < domain[0][0]) {
+ x = domain[0][0];
+ } else if (in[0] > domain[0][1]) {
+ x = domain[0][1];
+ } else {
+ x = in[0];
+ }
+ for (i = 0; i < k - 1; ++i) {
+ if (x < bounds[i+1]) {
+ break;
+ }
+ }
+ x = encode[2*i] + ((x - bounds[i]) / (bounds[i+1] - bounds[i])) *
+ (encode[2*i+1] - encode[2*i]);
+ funcs[i]->transform(&x, out);
+}
+
+//------------------------------------------------------------------------
+// PostScriptFunction
+//------------------------------------------------------------------------
+
+enum PSOp {
+ psOpAbs,
+ psOpAdd,
+ psOpAnd,
+ psOpAtan,
+ psOpBitshift,
+ psOpCeiling,
+ psOpCopy,
+ psOpCos,
+ psOpCvi,
+ psOpCvr,
+ psOpDiv,
+ psOpDup,
+ psOpEq,
+ psOpExch,
+ psOpExp,
+ psOpFalse,
+ psOpFloor,
+ psOpGe,
+ psOpGt,
+ psOpIdiv,
+ psOpIndex,
+ psOpLe,
+ psOpLn,
+ psOpLog,
+ psOpLt,
+ psOpMod,
+ psOpMul,
+ psOpNe,
+ psOpNeg,
+ psOpNot,
+ psOpOr,
+ psOpPop,
+ psOpRoll,
+ psOpRound,
+ psOpSin,
+ psOpSqrt,
+ psOpSub,
+ psOpTrue,
+ psOpTruncate,
+ psOpXor,
+ psOpIf,
+ psOpIfelse,
+ psOpReturn
+};
+
+// Note: 'if' and 'ifelse' are parsed separately.
+// The rest are listed here in alphabetical order.
+// The index in this table is equivalent to the entry in PSOp.
+char *psOpNames[] = {
+ "abs",
+ "add",
+ "and",
+ "atan",
+ "bitshift",
+ "ceiling",
+ "copy",
+ "cos",
+ "cvi",
+ "cvr",
+ "div",
+ "dup",
+ "eq",
+ "exch",
+ "exp",
+ "false",
+ "floor",
+ "ge",
+ "gt",
+ "idiv",
+ "index",
+ "le",
+ "ln",
+ "log",
+ "lt",
+ "mod",
+ "mul",
+ "ne",
+ "neg",
+ "not",
+ "or",
+ "pop",
+ "roll",
+ "round",
+ "sin",
+ "sqrt",
+ "sub",
+ "true",
+ "truncate",
+ "xor"
+};
+
+#define nPSOps (sizeof(psOpNames) / sizeof(char *))
+
+enum PSObjectType {
+ psBool,
+ psInt,
+ psReal,
+ psOperator,
+ psBlock
+};
+
+// In the code array, 'if'/'ifelse' operators take up three slots
+// plus space for the code in the subclause(s).
+//
+// +---------------------------------+
+// | psOperator: psOpIf / psOpIfelse |
+// +---------------------------------+
+// | psBlock: ptr=<A> |
+// +---------------------------------+
+// | psBlock: ptr=<B> |
+// +---------------------------------+
+// | if clause |
+// | ... |
+// | psOperator: psOpReturn |
+// +---------------------------------+
+// <A> | else clause |
+// | ... |
+// | psOperator: psOpReturn |
+// +---------------------------------+
+// <B> | ... |
+//
+// For 'if', pointer <A> is present in the code stream but unused.
+
+struct PSObject {
+ PSObjectType type;
+ union {
+ GBool booln; // boolean (stack only)
+ int intg; // integer (stack and code)
+ double real; // real (stack and code)
+ PSOp op; // operator (code only)
+ int blk; // if/ifelse block pointer (code only)
+ };
+};
+
+#define psStackSize 100
+
+class PSStack {
+public:
+
+ PSStack() { sp = psStackSize; }
+ void pushBool(GBool booln);
+ void pushInt(int intg);
+ void pushReal(double real);
+ GBool popBool();
+ int popInt();
+ double popNum();
+ GBool empty() { return sp == psStackSize; }
+ GBool topIsInt() { return sp < psStackSize && stack[sp].type == psInt; }
+ GBool topTwoAreInts()
+ { return sp < psStackSize - 1 &&
+ stack[sp].type == psInt &&
+ stack[sp+1].type == psInt; }
+ GBool topIsReal() { return sp < psStackSize && stack[sp].type == psReal; }
+ GBool topTwoAreNums()
+ { return sp < psStackSize - 1 &&
+ (stack[sp].type == psInt || stack[sp].type == psReal) &&
+ (stack[sp+1].type == psInt || stack[sp+1].type == psReal); }
+ void copy(int n);
+ void roll(int n, int j);
+ void index(int i);
+ void pop();
+
+private:
+
+ GBool checkOverflow(int n = 1);
+ GBool checkUnderflow();
+ GBool checkType(PSObjectType t1, PSObjectType t2);
+
+ PSObject stack[psStackSize];
+ int sp;
+};
+
+GBool PSStack::checkOverflow(int n) {
+ if (sp - n < 0) {
+ error(-1, "Stack overflow in PostScript function");
+ return gFalse;
+ }
+ return gTrue;
+}
+
+GBool PSStack::checkUnderflow() {
+ if (sp == psStackSize) {
+ error(-1, "Stack underflow in PostScript function");
+ return gFalse;
+ }
+ return gTrue;
+}
+
+GBool PSStack::checkType(PSObjectType t1, PSObjectType t2) {
+ if (stack[sp].type != t1 && stack[sp].type != t2) {
+ error(-1, "Type mismatch in PostScript function");
+ return gFalse;
+ }
+ return gTrue;
+}
+
+void PSStack::pushBool(GBool booln) {
+ if (checkOverflow()) {
+ stack[--sp].type = psBool;
+ stack[sp].booln = booln;
+ }
+}
+
+void PSStack::pushInt(int intg) {
+ if (checkOverflow()) {
+ stack[--sp].type = psInt;
+ stack[sp].intg = intg;
+ }
+}
+
+void PSStack::pushReal(double real) {
+ if (checkOverflow()) {
+ stack[--sp].type = psReal;
+ stack[sp].real = real;
+ }
+}
+
+GBool PSStack::popBool() {
+ if (checkUnderflow() && checkType(psBool, psBool)) {
+ return stack[sp++].booln;
+ }
+ return gFalse;
+}
+
+int PSStack::popInt() {
+ if (checkUnderflow() && checkType(psInt, psInt)) {
+ return stack[sp++].intg;
+ }
+ return 0;
+}
+
+double PSStack::popNum() {
+ double ret;
+
+ if (checkUnderflow() && checkType(psInt, psReal)) {
+ ret = (stack[sp].type == psInt) ? (double)stack[sp].intg : stack[sp].real;
+ ++sp;
+ return ret;
+ }
+ return 0;
+}
+
+void PSStack::copy(int n) {
+ int i;
+
+ if (sp + n > psStackSize) {
+ error(-1, "Stack underflow in PostScript function");
+ return;
+ }
+ if (!checkOverflow(n)) {
+ return;
+ }
+ for (i = sp + n - 1; i >= sp; --i) {
+ stack[i - n] = stack[i];
+ }
+ sp -= n;
+}
+
+void PSStack::roll(int n, int j) {
+ PSObject obj;
+ int i, k;
+
+ if (j >= 0) {
+ j %= n;
+ } else {
+ j = -j % n;
+ if (j != 0) {
+ j = n - j;
+ }
+ }
+ if (n <= 0 || j == 0) {
+ return;
+ }
+ for (i = 0; i < j; ++i) {
+ obj = stack[sp];
+ for (k = sp; k < sp + n - 1; ++k) {
+ stack[k] = stack[k+1];
+ }
+ stack[sp + n - 1] = obj;
+ }
+}
+
+void PSStack::index(int i) {
+ if (!checkOverflow()) {
+ return;
+ }
+ --sp;
+ stack[sp] = stack[sp + 1 + i];
+}
+
+void PSStack::pop() {
+ if (!checkUnderflow()) {
+ return;
+ }
+ ++sp;
+}
+
+PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) {
+ Stream *str;
+ int codePtr;
+ GString *tok;
+
+ code = NULL;
+ codeSize = 0;
+ ok = gFalse;
+
+ //----- initialize the generic stuff
+ if (!init(dict)) {
+ goto err1;
+ }
+ if (!hasRange) {
+ error(-1, "Type 4 function is missing range");
+ goto err1;
+ }
+
+ //----- get the stream
+ if (!funcObj->isStream()) {
+ error(-1, "Type 4 function isn't a stream");
+ goto err1;
+ }
+ str = funcObj->getStream();
+
+ //----- parse the function
+ codeString = new GString();
+ str->reset();
+ if (!(tok = getToken(str)) || tok->cmp("{")) {
+ error(-1, "Expected '{' at start of PostScript function");
+ if (tok) {
+ delete tok;
+ }
+ goto err1;
+ }
+ delete tok;
+ codePtr = 0;
+ if (!parseCode(str, &codePtr)) {
+ goto err2;
+ }
+ str->close();
+
+ ok = gTrue;
+
+ err2:
+ str->close();
+ err1:
+ return;
+}
+
+PostScriptFunction::PostScriptFunction(PostScriptFunction *func) {
+ memcpy(this, func, sizeof(PostScriptFunction));
+ code = (PSObject *)gmallocn(codeSize, sizeof(PSObject));
+ memcpy(code, func->code, codeSize * sizeof(PSObject));
+ codeString = func->codeString->copy();
+}
+
+PostScriptFunction::~PostScriptFunction() {
+ gfree(code);
+ delete codeString;
+}
+
+void PostScriptFunction::transform(double *in, double *out) {
+ PSStack *stack;
+ int i;
+
+ stack = new PSStack();
+ for (i = 0; i < m; ++i) {
+ //~ may need to check for integers here
+ stack->pushReal(in[i]);
+ }
+ exec(stack, 0);
+ for (i = n - 1; i >= 0; --i) {
+ out[i] = stack->popNum();
+ if (out[i] < range[i][0]) {
+ out[i] = range[i][0];
+ } else if (out[i] > range[i][1]) {
+ out[i] = range[i][1];
+ }
+ }
+ // if (!stack->empty()) {
+ // error(-1, "Extra values on stack at end of PostScript function");
+ // }
+ delete stack;
+}
+
+GBool PostScriptFunction::parseCode(Stream *str, int *codePtr) {
+ GString *tok;
+ char *p;
+ GBool isReal;
+ int opPtr, elsePtr;
+ int a, b, mid, cmp;
+
+ while (1) {
+ if (!(tok = getToken(str))) {
+ error(-1, "Unexpected end of PostScript function stream");
+ return gFalse;
+ }
+ p = tok->getCString();
+ if (isdigit(*p) || *p == '.' || *p == '-') {
+ isReal = gFalse;
+ for (++p; *p; ++p) {
+ if (*p == '.') {
+ isReal = gTrue;
+ break;
+ }
+ }
+ resizeCode(*codePtr);
+ if (isReal) {
+ code[*codePtr].type = psReal;
+ code[*codePtr].real = atof(tok->getCString());
+ } else {
+ code[*codePtr].type = psInt;
+ code[*codePtr].intg = atoi(tok->getCString());
+ }
+ ++*codePtr;
+ delete tok;
+ } else if (!tok->cmp("{")) {
+ delete tok;
+ opPtr = *codePtr;
+ *codePtr += 3;
+ resizeCode(opPtr + 2);
+ if (!parseCode(str, codePtr)) {
+ return gFalse;
+ }
+ if (!(tok = getToken(str))) {
+ error(-1, "Unexpected end of PostScript function stream");
+ return gFalse;
+ }
+ if (!tok->cmp("{")) {
+ elsePtr = *codePtr;
+ if (!parseCode(str, codePtr)) {
+ return gFalse;
+ }
+ delete tok;
+ if (!(tok = getToken(str))) {
+ error(-1, "Unexpected end of PostScript function stream");
+ return gFalse;
+ }
+ } else {
+ elsePtr = -1;
+ }
+ if (!tok->cmp("if")) {
+ if (elsePtr >= 0) {
+ error(-1, "Got 'if' operator with two blocks in PostScript function");
+ return gFalse;
+ }
+ code[opPtr].type = psOperator;
+ code[opPtr].op = psOpIf;
+ code[opPtr+2].type = psBlock;
+ code[opPtr+2].blk = *codePtr;
+ } else if (!tok->cmp("ifelse")) {
+ if (elsePtr < 0) {
+ error(-1, "Got 'ifelse' operator with one blocks in PostScript function");
+ return gFalse;
+ }
+ code[opPtr].type = psOperator;
+ code[opPtr].op = psOpIfelse;
+ code[opPtr+1].type = psBlock;
+ code[opPtr+1].blk = elsePtr;
+ code[opPtr+2].type = psBlock;
+ code[opPtr+2].blk = *codePtr;
+ } else {
+ error(-1, "Expected if/ifelse operator in PostScript function");
+ delete tok;
+ return gFalse;
+ }
+ delete tok;
+ } else if (!tok->cmp("}")) {
+ delete tok;
+ resizeCode(*codePtr);
+ code[*codePtr].type = psOperator;
+ code[*codePtr].op = psOpReturn;
+ ++*codePtr;
+ break;
+ } else {
+ a = -1;
+ b = nPSOps;
+ // invariant: psOpNames[a] < tok < psOpNames[b]
+ while (b - a > 1) {
+ mid = (a + b) / 2;
+ cmp = tok->cmp(psOpNames[mid]);
+ if (cmp > 0) {
+ a = mid;
+ } else if (cmp < 0) {
+ b = mid;
+ } else {
+ a = b = mid;
+ }
+ }
+ if (cmp != 0) {
+ error(-1, "Unknown operator '%s' in PostScript function",
+ tok->getCString());
+ delete tok;
+ return gFalse;
+ }
+ delete tok;
+ resizeCode(*codePtr);
+ code[*codePtr].type = psOperator;
+ code[*codePtr].op = (PSOp)a;
+ ++*codePtr;
+ }
+ }
+ return gTrue;
+}
+
+GString *PostScriptFunction::getToken(Stream *str) {
+ GString *s;
+ int c;
+
+ s = new GString();
+ do {
+ c = str->getChar();
+ if (c != EOF) {
+ codeString->append(c);
+ }
+ } while (c != EOF && isspace(c));
+ if (c == '{' || c == '}') {
+ s->append((char)c);
+ } else if (isdigit(c) || c == '.' || c == '-') {
+ while (1) {
+ s->append((char)c);
+ c = str->lookChar();
+ if (c == EOF || !(isdigit(c) || c == '.' || c == '-')) {
+ break;
+ }
+ str->getChar();
+ codeString->append(c);
+ }
+ } else {
+ while (1) {
+ s->append((char)c);
+ c = str->lookChar();
+ if (c == EOF || !isalnum(c)) {
+ break;
+ }
+ str->getChar();
+ codeString->append(c);
+ }
+ }
+ return s;
+}
+
+void PostScriptFunction::resizeCode(int newSize) {
+ if (newSize >= codeSize) {
+ codeSize += 64;
+ code = (PSObject *)greallocn(code, codeSize, sizeof(PSObject));
+ }
+}
+
+void PostScriptFunction::exec(PSStack *stack, int codePtr) {
+ int i1, i2;
+ double r1, r2;
+ GBool b1, b2;
+
+ while (1) {
+ switch (code[codePtr].type) {
+ case psInt:
+ stack->pushInt(code[codePtr++].intg);
+ break;
+ case psReal:
+ stack->pushReal(code[codePtr++].real);
+ break;
+ case psOperator:
+ switch (code[codePtr++].op) {
+ case psOpAbs:
+ if (stack->topIsInt()) {
+ stack->pushInt(abs(stack->popInt()));
+ } else {
+ stack->pushReal(fabs(stack->popNum()));
+ }
+ break;
+ case psOpAdd:
+ if (stack->topTwoAreInts()) {
+ i2 = stack->popInt();
+ i1 = stack->popInt();
+ stack->pushInt(i1 + i2);
+ } else {
+ r2 = stack->popNum();
+ r1 = stack->popNum();
+ stack->pushReal(r1 + r2);
+ }
+ break;
+ case psOpAnd:
+ if (stack->topTwoAreInts()) {
+ i2 = stack->popInt();
+ i1 = stack->popInt();
+ stack->pushInt(i1 & i2);
+ } else {
+ b2 = stack->popBool();
+ b1 = stack->popBool();
+ stack->pushBool(b1 && b2);
+ }
+ break;
+ case psOpAtan:
+ r2 = stack->popNum();
+ r1 = stack->popNum();
+ stack->pushReal(atan2(r1, r2));
+ break;
+ case psOpBitshift:
+ i2 = stack->popInt();
+ i1 = stack->popInt();
+ if (i2 > 0) {
+ stack->pushInt(i1 << i2);
+ } else if (i2 < 0) {
+ stack->pushInt((int)((Guint)i1 >> i2));
+ } else {
+ stack->pushInt(i1);
+ }
+ break;
+ case psOpCeiling:
+ if (!stack->topIsInt()) {
+ stack->pushReal(ceil(stack->popNum()));
+ }
+ break;
+ case psOpCopy:
+ stack->copy(stack->popInt());
+ break;
+ case psOpCos:
+ stack->pushReal(cos(stack->popNum()));
+ break;
+ case psOpCvi:
+ if (!stack->topIsInt()) {
+ stack->pushInt((int)stack->popNum());
+ }
+ break;
+ case psOpCvr:
+ if (!stack->topIsReal()) {
+ stack->pushReal(stack->popNum());
+ }
+ break;
+ case psOpDiv:
+ r2 = stack->popNum();
+ r1 = stack->popNum();
+ stack->pushReal(r1 / r2);
+ break;
+ case psOpDup:
+ stack->copy(1);
+ break;
+ case psOpEq:
+ if (stack->topTwoAreInts()) {
+ i2 = stack->popInt();
+ i1 = stack->popInt();
+ stack->pushBool(i1 == i2);
+ } else if (stack->topTwoAreNums()) {
+ r2 = stack->popNum();
+ r1 = stack->popNum();
+ stack->pushBool(r1 == r2);
+ } else {
+ b2 = stack->popBool();
+ b1 = stack->popBool();
+ stack->pushBool(b1 == b2);
+ }
+ break;
+ case psOpExch:
+ stack->roll(2, 1);
+ break;
+ case psOpExp:
+ r2 = stack->popNum();
+ r1 = stack->popNum();
+ stack->pushReal(pow(r1, r2));
+ break;
+ case psOpFalse:
+ stack->pushBool(gFalse);
+ break;
+ case psOpFloor:
+ if (!stack->topIsInt()) {
+ stack->pushReal(floor(stack->popNum()));
+ }
+ break;
+ case psOpGe:
+ if (stack->topTwoAreInts()) {
+ i2 = stack->popInt();
+ i1 = stack->popInt();
+ stack->pushBool(i1 >= i2);
+ } else {
+ r2 = stack->popNum();
+ r1 = stack->popNum();
+ stack->pushBool(r1 >= r2);
+ }
+ break;
+ case psOpGt:
+ if (stack->topTwoAreInts()) {
+ i2 = stack->popInt();
+ i1 = stack->popInt();
+ stack->pushBool(i1 > i2);
+ } else {
+ r2 = stack->popNum();
+ r1 = stack->popNum();
+ stack->pushBool(r1 > r2);
+ }
+ break;
+ case psOpIdiv:
+ i2 = stack->popInt();
+ i1 = stack->popInt();
+ stack->pushInt(i1 / i2);
+ break;
+ case psOpIndex:
+ stack->index(stack->popInt());
+ break;
+ case psOpLe:
+ if (stack->topTwoAreInts()) {
+ i2 = stack->popInt();
+ i1 = stack->popInt();
+ stack->pushBool(i1 <= i2);
+ } else {
+ r2 = stack->popNum();
+ r1 = stack->popNum();
+ stack->pushBool(r1 <= r2);
+ }
+ break;
+ case psOpLn:
+ stack->pushReal(log(stack->popNum()));
+ break;
+ case psOpLog:
+ stack->pushReal(log10(stack->popNum()));
+ break;
+ case psOpLt:
+ if (stack->topTwoAreInts()) {
+ i2 = stack->popInt();
+ i1 = stack->popInt();
+ stack->pushBool(i1 < i2);
+ } else {
+ r2 = stack->popNum();
+ r1 = stack->popNum();
+ stack->pushBool(r1 < r2);
+ }
+ break;
+ case psOpMod:
+ i2 = stack->popInt();
+ i1 = stack->popInt();
+ stack->pushInt(i1 % i2);
+ break;
+ case psOpMul:
+ if (stack->topTwoAreInts()) {
+ i2 = stack->popInt();
+ i1 = stack->popInt();
+ //~ should check for out-of-range, and push a real instead
+ stack->pushInt(i1 * i2);
+ } else {
+ r2 = stack->popNum();
+ r1 = stack->popNum();
+ stack->pushReal(r1 * r2);
+ }
+ break;
+ case psOpNe:
+ if (stack->topTwoAreInts()) {
+ i2 = stack->popInt();
+ i1 = stack->popInt();
+ stack->pushBool(i1 != i2);
+ } else if (stack->topTwoAreNums()) {
+ r2 = stack->popNum();
+ r1 = stack->popNum();
+ stack->pushBool(r1 != r2);
+ } else {
+ b2 = stack->popBool();
+ b1 = stack->popBool();
+ stack->pushBool(b1 != b2);
+ }
+ break;
+ case psOpNeg:
+ if (stack->topIsInt()) {
+ stack->pushInt(-stack->popInt());
+ } else {
+ stack->pushReal(-stack->popNum());
+ }
+ break;
+ case psOpNot:
+ if (stack->topIsInt()) {
+ stack->pushInt(~stack->popInt());
+ } else {
+ stack->pushBool(!stack->popBool());
+ }
+ break;
+ case psOpOr:
+ if (stack->topTwoAreInts()) {
+ i2 = stack->popInt();
+ i1 = stack->popInt();
+ stack->pushInt(i1 | i2);
+ } else {
+ b2 = stack->popBool();
+ b1 = stack->popBool();
+ stack->pushBool(b1 || b2);
+ }
+ break;
+ case psOpPop:
+ stack->pop();
+ break;
+ case psOpRoll:
+ i2 = stack->popInt();
+ i1 = stack->popInt();
+ stack->roll(i1, i2);
+ break;
+ case psOpRound:
+ if (!stack->topIsInt()) {
+ r1 = stack->popNum();
+ stack->pushReal((r1 >= 0) ? floor(r1 + 0.5) : ceil(r1 - 0.5));
+ }
+ break;
+ case psOpSin:
+ stack->pushReal(sin(stack->popNum()));
+ break;
+ case psOpSqrt:
+ stack->pushReal(sqrt(stack->popNum()));
+ break;
+ case psOpSub:
+ if (stack->topTwoAreInts()) {
+ i2 = stack->popInt();
+ i1 = stack->popInt();
+ stack->pushInt(i1 - i2);
+ } else {
+ r2 = stack->popNum();
+ r1 = stack->popNum();
+ stack->pushReal(r1 - r2);
+ }
+ break;
+ case psOpTrue:
+ stack->pushBool(gTrue);
+ break;
+ case psOpTruncate:
+ if (!stack->topIsInt()) {
+ r1 = stack->popNum();
+ stack->pushReal((r1 >= 0) ? floor(r1) : ceil(r1));
+ }
+ break;
+ case psOpXor:
+ if (stack->topTwoAreInts()) {
+ i2 = stack->popInt();
+ i1 = stack->popInt();
+ stack->pushInt(i1 ^ i2);
+ } else {
+ b2 = stack->popBool();
+ b1 = stack->popBool();
+ stack->pushBool(b1 ^ b2);
+ }
+ break;
+ case psOpIf:
+ b1 = stack->popBool();
+ if (b1) {
+ exec(stack, codePtr + 2);
+ }
+ codePtr = code[codePtr + 1].blk;
+ break;
+ case psOpIfelse:
+ b1 = stack->popBool();
+ if (b1) {
+ exec(stack, codePtr + 2);
+ } else {
+ exec(stack, code[codePtr].blk);
+ }
+ codePtr = code[codePtr + 1].blk;
+ break;
+ case psOpReturn:
+ return;
+ }
+ break;
+ default:
+ error(-1, "Internal: bad object in PostScript function code");
+ break;
+ }
+ }
+}
--- /dev/null
+//========================================================================
+//
+// Function.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef FUNCTION_H
+#define FUNCTION_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "Object.h"
+
+class Dict;
+class Stream;
+struct PSObject;
+class PSStack;
+
+//------------------------------------------------------------------------
+// Function
+//------------------------------------------------------------------------
+
+#define funcMaxInputs 8
+#define funcMaxOutputs 32
+
+class Function {
+public:
+
+ Function();
+
+ virtual ~Function();
+
+ // Construct a function. Returns NULL if unsuccessful.
+ static Function *parse(Object *funcObj);
+
+ // Initialize the entries common to all function types.
+ GBool init(Dict *dict);
+
+ virtual Function *copy() = 0;
+
+ // Return the function type:
+ // -1 : identity
+ // 0 : sampled
+ // 2 : exponential
+ // 3 : stitching
+ // 4 : PostScript
+ virtual int getType() = 0;
+
+ // Return size of input and output tuples.
+ int getInputSize() { return m; }
+ int getOutputSize() { return n; }
+
+ double getDomainMin(int i) { return domain[i][0]; }
+ double getDomainMax(int i) { return domain[i][1]; }
+ double getRangeMin(int i) { return range[i][0]; }
+ double getRangeMax(int i) { return range[i][1]; }
+ GBool getHasRange() { return hasRange; }
+
+ // Transform an input tuple into an output tuple.
+ virtual void transform(double *in, double *out) = 0;
+
+ virtual GBool isOk() = 0;
+
+protected:
+
+ int m, n; // size of input and output tuples
+ double // min and max values for function domain
+ domain[funcMaxInputs][2];
+ double // min and max values for function range
+ range[funcMaxOutputs][2];
+ GBool hasRange; // set if range is defined
+};
+
+//------------------------------------------------------------------------
+// IdentityFunction
+//------------------------------------------------------------------------
+
+class IdentityFunction: public Function {
+public:
+
+ IdentityFunction();
+ virtual ~IdentityFunction();
+ virtual Function *copy() { return new IdentityFunction(); }
+ virtual int getType() { return -1; }
+ virtual void transform(double *in, double *out);
+ virtual GBool isOk() { return gTrue; }
+
+private:
+};
+
+//------------------------------------------------------------------------
+// SampledFunction
+//------------------------------------------------------------------------
+
+class SampledFunction: public Function {
+public:
+
+ SampledFunction(Object *funcObj, Dict *dict);
+ virtual ~SampledFunction();
+ virtual Function *copy() { return new SampledFunction(this); }
+ virtual int getType() { return 0; }
+ virtual void transform(double *in, double *out);
+ virtual GBool isOk() { return ok; }
+
+ int getSampleSize(int i) { return sampleSize[i]; }
+ double getEncodeMin(int i) { return encode[i][0]; }
+ double getEncodeMax(int i) { return encode[i][1]; }
+ double getDecodeMin(int i) { return decode[i][0]; }
+ double getDecodeMax(int i) { return decode[i][1]; }
+ double *getSamples() { return samples; }
+
+private:
+
+ SampledFunction(SampledFunction *func);
+
+ int // number of samples for each domain element
+ sampleSize[funcMaxInputs];
+ double // min and max values for domain encoder
+ encode[funcMaxInputs][2];
+ double // min and max values for range decoder
+ decode[funcMaxOutputs][2];
+ double // input multipliers
+ inputMul[funcMaxInputs];
+ int idxMul[funcMaxInputs]; // sample array index multipliers
+ double *samples; // the samples
+ int nSamples; // size of the samples array
+ GBool ok;
+};
+
+//------------------------------------------------------------------------
+// ExponentialFunction
+//------------------------------------------------------------------------
+
+class ExponentialFunction: public Function {
+public:
+
+ ExponentialFunction(Object *funcObj, Dict *dict);
+ virtual ~ExponentialFunction();
+ virtual Function *copy() { return new ExponentialFunction(this); }
+ virtual int getType() { return 2; }
+ virtual void transform(double *in, double *out);
+ virtual GBool isOk() { return ok; }
+
+ double *getC0() { return c0; }
+ double *getC1() { return c1; }
+ double getE() { return e; }
+
+private:
+
+ ExponentialFunction(ExponentialFunction *func);
+
+ double c0[funcMaxOutputs];
+ double c1[funcMaxOutputs];
+ double e;
+ GBool ok;
+};
+
+//------------------------------------------------------------------------
+// StitchingFunction
+//------------------------------------------------------------------------
+
+class StitchingFunction: public Function {
+public:
+
+ StitchingFunction(Object *funcObj, Dict *dict);
+ virtual ~StitchingFunction();
+ virtual Function *copy() { return new StitchingFunction(this); }
+ virtual int getType() { return 3; }
+ virtual void transform(double *in, double *out);
+ virtual GBool isOk() { return ok; }
+
+ int getNumFuncs() { return k; }
+ Function *getFunc(int i) { return funcs[i]; }
+ double *getBounds() { return bounds; }
+ double *getEncode() { return encode; }
+
+private:
+
+ StitchingFunction(StitchingFunction *func);
+
+ int k;
+ Function **funcs;
+ double *bounds;
+ double *encode;
+ GBool ok;
+};
+
+//------------------------------------------------------------------------
+// PostScriptFunction
+//------------------------------------------------------------------------
+
+class PostScriptFunction: public Function {
+public:
+
+ PostScriptFunction(Object *funcObj, Dict *dict);
+ virtual ~PostScriptFunction();
+ virtual Function *copy() { return new PostScriptFunction(this); }
+ virtual int getType() { return 4; }
+ virtual void transform(double *in, double *out);
+ virtual GBool isOk() { return ok; }
+
+ GString *getCodeString() { return codeString; }
+
+private:
+
+ PostScriptFunction(PostScriptFunction *func);
+ GBool parseCode(Stream *str, int *codePtr);
+ GString *getToken(Stream *str);
+ void resizeCode(int newSize);
+ void exec(PSStack *stack, int codePtr);
+
+ GString *codeString;
+ PSObject *code;
+ int codeSize;
+ GBool ok;
+};
+
+#endif
--- /dev/null
+/* pdfswf.cc
+ implements a pdf output device (OutputDev).
+
+ This file is part of swftools.
+
+ Swftools is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ Swftools is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with swftools; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include "../../config.h"
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_FONTCONFIG
+#include <fontconfig.h>
+#endif
+//xpdf header files
+#include "config.h"
+#include "gfile.h"
+#include "GString.h"
+#include "gmem.h"
+#include "Object.h"
+#include "Stream.h"
+#include "Array.h"
+#include "Dict.h"
+#include "XRef.h"
+#include "Catalog.h"
+#include "Page.h"
+#include "PDFDoc.h"
+#include "Error.h"
+#include "Link.h"
+#include "OutputDev.h"
+#include "GfxFont.h"
+#include "GfxState.h"
+#include "CharCodeToUnicode.h"
+#include "NameToUnicodeTable.h"
+#include "GlobalParams.h"
+#include "FoFiType1C.h"
+#include "FoFiTrueType.h"
+#include "GHash.h"
+#include "GFXOutputDev.h"
+
+//swftools header files
+#include "../log.h"
+#include "../gfxdevice.h"
+#include "../gfxtools.h"
+#include "../gfxfont.h"
+
+#include <math.h>
+
+typedef struct _fontfile
+{
+ char*filename;
+ int used;
+} fontfile_t;
+
+// for pdfswf_addfont
+static fontfile_t fonts[2048];
+static int fontnum = 0;
+
+/* config */
+
+static char* lastfontdir = 0;
+
+struct mapping {
+ char*pdffont;
+ char*filename;
+} pdf2t1map[] ={
+{"Times-Roman", "n021003l"},
+{"Times-Italic", "n021023l"},
+{"Times-Bold", "n021004l"},
+{"Times-BoldItalic", "n021024l"},
+{"Helvetica", "n019003l"},
+{"Helvetica-Oblique", "n019023l"},
+{"Helvetica-Bold", "n019004l"},
+{"Helvetica-BoldOblique", "n019024l"},
+{"Courier", "n022003l"},
+{"Courier-Oblique", "n022023l"},
+{"Courier-Bold", "n022004l"},
+{"Courier-BoldOblique", "n022024l"},
+{"Symbol", "s050000l"},
+{"ZapfDingbats", "d050000l"}};
+
+GFXOutputState::GFXOutputState() {
+ this->clipping = 0;
+ this->textRender = 0;
+}
+
+GBool GFXOutputDev::interpretType3Chars()
+{
+ return gTrue;
+}
+
+typedef struct _drawnchar
+{
+ gfxcoord_t x,y;
+ int charid;
+ gfxcolor_t color;
+} drawnchar_t;
+
+class CharBuffer
+{
+ drawnchar_t * chars;
+ int buf_size;
+ int num_chars;
+
+public:
+
+ CharBuffer()
+ {
+ buf_size = 32;
+ chars = (drawnchar_t*)malloc(sizeof(drawnchar_t)*buf_size);
+ memset(chars, 0, sizeof(drawnchar_t)*buf_size);
+ num_chars = 0;
+ }
+ ~CharBuffer()
+ {
+ free(chars);chars = 0;
+ }
+
+ void grow(int size)
+ {
+ if(size>=buf_size) {
+ buf_size += 32;
+ chars = (drawnchar_t*)realloc(chars, sizeof(drawnchar_t)*buf_size);
+ }
+ }
+
+ void addChar(int charid, gfxcoord_t x, gfxcoord_t y, gfxcolor_t color)
+ {
+ grow(num_chars);
+ chars[num_chars].x = x;
+ chars[num_chars].y = y;
+ chars[num_chars].color = color;
+ chars[num_chars].charid = charid;
+ }
+};
+
+static char*getFontID(GfxFont*font);
+
+GFXOutputDev::GFXOutputDev(parameter_t*p)
+{
+ this->jpeginfo = 0;
+ this->textmodeinfo = 0;
+ this->ttfinfo = 0;
+ this->linkinfo = 0;
+ this->pbminfo = 0;
+ this->type3active = 0;
+ this->statepos = 0;
+ this->xref = 0;
+ this->substitutepos = 0;
+ this->type3Warning = 0;
+ this->user_movex = 0;
+ this->user_movey = 0;
+ this->user_clipx1 = 0;
+ this->user_clipy1 = 0;
+ this->user_clipx2 = 0;
+ this->user_clipy2 = 0;
+ this->current_text_stroke = 0;
+ this->current_text_clip = 0;
+ this->fontlist = 0;
+ this->outer_clip_box = 0;
+ this->pages = 0;
+ this->pagebuflen = 0;
+ this->pagepos = 0;
+
+ this->forceType0Fonts=1;
+ this->config_use_fontconfig=1;
+
+ /* configure device */
+ while(p) {
+ if(!strcmp(p->name,"forceType0Fonts")) {
+ this->forceType0Fonts = atoi(p->value);
+ } else if(!strcmp(p->name,"fontconfig")) {
+ this->config_use_fontconfig = atoi(p->value);
+ } else {
+ msg("<warning> Ignored Paramter %s=%s", p->name, p->value);
+ }
+ p = p->next;
+ }
+};
+
+void GFXOutputDev::setDevice(gfxdevice_t*dev)
+{
+ this->device = dev;
+}
+
+void GFXOutputDev::setMove(int x,int y)
+{
+ this->user_movex = x;
+ this->user_movey = y;
+}
+
+void GFXOutputDev::setClip(int x1,int y1,int x2,int y2)
+{
+ if(x2<x1) {int x3=x1;x1=x2;x2=x3;}
+ if(y2<y1) {int y3=y1;y1=y2;y2=y3;}
+
+ this->user_clipx1 = x1;
+ this->user_clipy1 = y1;
+ this->user_clipx2 = x2;
+ this->user_clipy2 = y2;
+}
+
+static char*getFontID(GfxFont*font)
+{
+ Ref*ref = font->getID();
+ GString*gstr = font->getName();
+ char* fname = gstr==0?0:gstr->getCString();
+ char buf[128];
+ if(fname==0) {
+ sprintf(buf, "font-%d-%d", ref->num, ref->gen);
+ } else {
+ sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen);
+ }
+ return strdup(buf);
+}
+
+static char*getFontName(GfxFont*font)
+{
+ char*fontid;
+ GString*gstr = font->getName();
+ char* fname = gstr==0?0:gstr->getCString();
+ if(fname==0) {
+ char buf[32];
+ Ref*r=font->getID();
+ sprintf(buf, "UFONT%d", r->num);
+ fontid = strdup(buf);
+ }
+ fontid = strdup(fname);
+
+ char*fontname= 0;
+ char* plus = strchr(fontid, '+');
+ if(plus && plus < &fontid[strlen(fontid)-1]) {
+ fontname = strdup(plus+1);
+ } else {
+ fontname = strdup(fontid);
+ }
+ free(fontid);
+ return fontname;
+}
+
+static char mybuf[1024];
+static char* gfxstate2str(GfxState *state)
+{
+ char*bufpos = mybuf;
+ GfxRGB rgb;
+ bufpos+=sprintf(bufpos,"CTM[%.3f/%.3f/%.3f/%.3f/%.3f/%.3f] ",
+ state->getCTM()[0],
+ state->getCTM()[1],
+ state->getCTM()[2],
+ state->getCTM()[3],
+ state->getCTM()[4],
+ state->getCTM()[5]);
+ if(state->getX1()!=0.0)
+ bufpos+=sprintf(bufpos,"X1-%.1f ",state->getX1());
+ if(state->getY1()!=0.0)
+ bufpos+=sprintf(bufpos,"Y1-%.1f ",state->getY1());
+ bufpos+=sprintf(bufpos,"X2-%.1f ",state->getX2());
+ bufpos+=sprintf(bufpos,"Y2-%.1f ",state->getY2());
+ bufpos+=sprintf(bufpos,"PW%.1f ",state->getPageWidth());
+ bufpos+=sprintf(bufpos,"PH%.1f ",state->getPageHeight());
+ /*bufpos+=sprintf(bufpos,"FC[%.1f/%.1f] ",
+ state->getFillColor()->c[0], state->getFillColor()->c[1]);
+ bufpos+=sprintf(bufpos,"SC[%.1f/%.1f] ",
+ state->getStrokeColor()->c[0], state->getFillColor()->c[1]);*/
+/* bufpos+=sprintf(bufpos,"FC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
+ state->getFillColor()->c[0], state->getFillColor()->c[1],
+ state->getFillColor()->c[2], state->getFillColor()->c[3],
+ state->getFillColor()->c[4], state->getFillColor()->c[5],
+ state->getFillColor()->c[6], state->getFillColor()->c[7]);
+ bufpos+=sprintf(bufpos,"SC[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f/%.1f]",
+ state->getStrokeColor()->c[0], state->getFillColor()->c[1],
+ state->getStrokeColor()->c[2], state->getFillColor()->c[3],
+ state->getStrokeColor()->c[4], state->getFillColor()->c[5],
+ state->getStrokeColor()->c[6], state->getFillColor()->c[7]);*/
+ state->getFillRGB(&rgb);
+ if(rgb.r || rgb.g || rgb.b)
+ bufpos+=sprintf(bufpos,"FR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
+ state->getStrokeRGB(&rgb);
+ if(rgb.r || rgb.g || rgb.b)
+ bufpos+=sprintf(bufpos,"SR[%.1f/%.1f/%.1f] ", rgb.r,rgb.g,rgb.b);
+ if(state->getFillColorSpace()->getNComps()>1)
+ bufpos+=sprintf(bufpos,"CS[[%d]] ",state->getFillColorSpace()->getNComps());
+ if(state->getStrokeColorSpace()->getNComps()>1)
+ bufpos+=sprintf(bufpos,"SS[[%d]] ",state->getStrokeColorSpace()->getNComps());
+ if(state->getFillPattern())
+ bufpos+=sprintf(bufpos,"FP%08x ", state->getFillPattern());
+ if(state->getStrokePattern())
+ bufpos+=sprintf(bufpos,"SP%08x ", state->getStrokePattern());
+
+ if(state->getFillOpacity()!=1.0)
+ bufpos+=sprintf(bufpos,"FO%.1f ", state->getFillOpacity());
+ if(state->getStrokeOpacity()!=1.0)
+ bufpos+=sprintf(bufpos,"SO%.1f ", state->getStrokeOpacity());
+
+ bufpos+=sprintf(bufpos,"LW%.1f ", state->getLineWidth());
+
+ double * dash;
+ int length;
+ double start;
+ state->getLineDash(&dash, &length, &start);
+ int t;
+ if(length)
+ {
+ bufpos+=sprintf(bufpos,"DASH%.1f[",start);
+ for(t=0;t<length;t++) {
+ bufpos+=sprintf(bufpos,"D%.1f",dash[t]);
+ }
+ bufpos+=sprintf(bufpos,"]");
+ }
+
+ if(state->getFlatness()!=1)
+ bufpos+=sprintf(bufpos,"F%d ", state->getFlatness());
+ if(state->getLineJoin()!=0)
+ bufpos+=sprintf(bufpos,"J%d ", state->getLineJoin());
+ if(state->getLineJoin()!=0)
+ bufpos+=sprintf(bufpos,"C%d ", state->getLineCap());
+ if(state->getLineJoin()!=0)
+ bufpos+=sprintf(bufpos,"ML%d ", state->getMiterLimit());
+
+ if(state->getFont() && getFontID(state->getFont()))
+ bufpos+=sprintf(bufpos,"F\"%s\" ",getFontID(state->getFont()));
+ bufpos+=sprintf(bufpos,"FS%.1f ", state->getFontSize());
+ bufpos+=sprintf(bufpos,"MAT[%.1f/%.1f/%.1f/%.1f/%.1f/%.1f] ", state->getTextMat()[0],state->getTextMat()[1],state->getTextMat()[2],
+ state->getTextMat()[3],state->getTextMat()[4],state->getTextMat()[5]);
+ if(state->getCharSpace())
+ bufpos+=sprintf(bufpos,"CS%.5f ", state->getCharSpace());
+ if(state->getWordSpace())
+ bufpos+=sprintf(bufpos,"WS%.5f ", state->getWordSpace());
+ if(state->getHorizScaling()!=1.0)
+ bufpos+=sprintf(bufpos,"SC%.1f ", state->getHorizScaling());
+ if(state->getLeading())
+ bufpos+=sprintf(bufpos,"L%.1f ", state->getLeading());
+ if(state->getRise())
+ bufpos+=sprintf(bufpos,"R%.1f ", state->getRise());
+ if(state->getRender())
+ bufpos+=sprintf(bufpos,"R%d ", state->getRender());
+ bufpos+=sprintf(bufpos,"P%08x ", state->getPath());
+ bufpos+=sprintf(bufpos,"CX%.1f ", state->getCurX());
+ bufpos+=sprintf(bufpos,"CY%.1f ", state->getCurY());
+ if(state->getLineX())
+ bufpos+=sprintf(bufpos,"LX%.1f ", state->getLineX());
+ if(state->getLineY())
+ bufpos+=sprintf(bufpos,"LY%.1f ", state->getLineY());
+ bufpos+=sprintf(bufpos," ");
+ return mybuf;
+}
+
+static void dumpFontInfo(char*loglevel, GfxFont*font);
+static int lastdumps[1024];
+static int lastdumppos = 0;
+/* nr = 0 unknown
+ nr = 1 substituting
+ nr = 2 type 3
+ */
+static void showFontError(GfxFont*font, int nr)
+{
+ Ref*r=font->getID();
+ int t;
+ for(t=0;t<lastdumppos;t++)
+ if(lastdumps[t] == r->num)
+ break;
+ if(t < lastdumppos)
+ return;
+ if(lastdumppos<sizeof(lastdumps)/sizeof(int))
+ lastdumps[lastdumppos++] = r->num;
+ if(nr == 0)
+ msg("<warning> The following font caused problems:");
+ else if(nr == 1)
+ msg("<warning> The following font caused problems (substituting):");
+ else if(nr == 2)
+ msg("<warning> The following Type 3 Font will be rendered as bitmap:");
+ dumpFontInfo("<warning>", font);
+}
+
+static void dumpFontInfo(char*loglevel, GfxFont*font)
+{
+ char* id = getFontID(font);
+ char* name = getFontName(font);
+ Ref* r=font->getID();
+ msg("%s=========== %s (ID:%d,%d) ==========\n", loglevel, name, r->num,r->gen);
+
+ GString*gstr = font->getTag();
+
+ msg("%s| Tag: %s\n", loglevel, id);
+
+ if(font->isCIDFont()) msg("%s| is CID font\n", loglevel);
+
+ GfxFontType type=font->getType();
+ switch(type) {
+ case fontUnknownType:
+ msg("%s| Type: unknown\n",loglevel);
+ break;
+ case fontType1:
+ msg("%s| Type: 1\n",loglevel);
+ break;
+ case fontType1C:
+ msg("%s| Type: 1C\n",loglevel);
+ break;
+ case fontType3:
+ msg("%s| Type: 3\n",loglevel);
+ break;
+ case fontTrueType:
+ msg("%s| Type: TrueType\n",loglevel);
+ break;
+ case fontCIDType0:
+ msg("%s| Type: CIDType0\n",loglevel);
+ break;
+ case fontCIDType0C:
+ msg("%s| Type: CIDType0C\n",loglevel);
+ break;
+ case fontCIDType2:
+ msg("%s| Type: CIDType2\n",loglevel);
+ break;
+ }
+
+ Ref embRef;
+ GBool embedded = font->getEmbeddedFontID(&embRef);
+ char*embeddedName=0;
+ if(font->getEmbeddedFontName()) {
+ embeddedName = font->getEmbeddedFontName()->getCString();
+ }
+ if(embedded)
+ msg("%s| Embedded id: %s id: %d\n",loglevel, FIXNULL(embeddedName), embRef.num);
+
+ gstr = font->getExtFontFile();
+ if(gstr)
+ msg("%s| External Font file: %s\n", loglevel, FIXNULL(gstr->getCString()));
+
+ // Get font descriptor flags.
+ if(font->isFixedWidth()) msg("%s| is fixed width\n", loglevel);
+ if(font->isSerif()) msg("%s| is serif\n", loglevel);
+ if(font->isSymbolic()) msg("%s| is symbolic\n", loglevel);
+ if(font->isItalic()) msg("%s| is italic\n", loglevel);
+ if(font->isBold()) msg("%s| is bold\n", loglevel);
+
+ free(id);
+ free(name);
+}
+
+//void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) {printf("void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg) \n");}
+//void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) {printf("void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, GBool inlineImg) \n");}
+
+
+void dump_outline(gfxline_t*line)
+{
+ while(line) {
+ if(line->type == gfx_moveTo) {
+ msg("<debug> | moveTo %.2f %.2f", line->x,line->y);
+ } else if(line->type == gfx_lineTo) {
+ msg("<debug> | lineTo %.2f %.2f", line->x,line->y);
+ } else if(line->type == gfx_splineTo) {
+ msg("<debug> | splineTo (%.2f %.2f) %.2f %.2f", line->sx,line->sy, line->x, line->y);
+ }
+ line = line->next;
+ }
+}
+
+gfxline_t* gfxPath_to_gfxline(GfxState*state, GfxPath*path, int closed, int user_movex, int user_movey)
+{
+ int num = path->getNumSubpaths();
+ int s,t;
+ int cpos = 0;
+ double lastx=0,lasty=0,posx=0,posy=0;
+ int needsfix=0;
+ if(!num) {
+ msg("<warning> empty path");
+ return 0;
+ }
+ gfxdrawer_t draw;
+ gfxdrawer_target_gfxline(&draw);
+
+ for(t = 0; t < num; t++) {
+ GfxSubpath *subpath = path->getSubpath(t);
+ int subnum = subpath->getNumPoints();
+ double bx=0,by=0,cx=0,cy=0;
+
+ for(s=0;s<subnum;s++) {
+ double x,y;
+
+ state->transform(subpath->getX(s),subpath->getY(s),&x,&y);
+ x += user_movex;
+ y += user_movey;
+
+ if(s==0) {
+ if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
+ draw.lineTo(&draw, lastx, lasty);
+ }
+ draw.moveTo(&draw, x,y);
+ posx = lastx = x;
+ posy = lasty = y;
+ cpos = 0;
+ needsfix = 0;
+ } else if(subpath->getCurve(s) && cpos==0) {
+ bx = x;
+ by = y;
+ cpos = 1;
+ } else if(subpath->getCurve(s) && cpos==1) {
+ cx = x;
+ cy = y;
+ cpos = 2;
+ } else {
+ posx = x;
+ posy = y;
+ if(cpos==0) {
+ draw.lineTo(&draw, x,y);
+ } else {
+ gfxdraw_cubicTo(&draw, bx,by, cx,cy, x,y, 0.05);
+ }
+ needsfix = 1;
+ cpos = 0;
+ }
+ }
+ }
+ /* fix non-closed lines */
+ if(closed && needsfix && (fabs(posx-lastx)+fabs(posy-lasty))>0.001) {
+ draw.lineTo(&draw, lastx, lasty);
+ }
+ gfxline_t*result = (gfxline_t*)draw.result(&draw);
+ return result;
+}
+
+/*----------------------------------------------------------------------------
+ * Primitive Graphic routines
+ *----------------------------------------------------------------------------*/
+
+void GFXOutputDev::stroke(GfxState *state)
+{
+ GfxPath * path = state->getPath();
+ gfxline_t*line= gfxPath_to_gfxline(state, path, 0, user_movex, user_movey);
+ strokeGfxline(state, line);
+ gfxline_free(line);
+}
+
+void GFXOutputDev::strokeGfxline(GfxState *state, gfxline_t*line)
+{
+ int lineCap = state->getLineCap(); // 0=butt, 1=round 2=square
+ int lineJoin = state->getLineJoin(); // 0=miter, 1=round 2=bevel
+ double miterLimit = state->getMiterLimit();
+ double width = state->getTransformedLineWidth();
+
+ GfxRGB rgb;
+ double opaq = state->getStrokeOpacity();
+ if(type3active)
+ state->getFillRGB(&rgb);
+ else
+ state->getStrokeRGB(&rgb);
+ gfxcolor_t col;
+ col.r = colToByte(rgb.r);
+ col.g = colToByte(rgb.g);
+ col.b = colToByte(rgb.b);
+ col.a = (unsigned char)(opaq*255);
+
+ gfx_capType capType = gfx_capRound;
+ if(lineCap == 0) capType = gfx_capButt;
+ else if(lineCap == 1) capType = gfx_capRound;
+ else if(lineCap == 2) capType = gfx_capSquare;
+
+ gfx_joinType joinType = gfx_joinRound;
+ if(lineJoin == 0) joinType = gfx_joinMiter;
+ else if(lineJoin == 1) joinType = gfx_joinRound;
+ else if(lineJoin == 2) joinType = gfx_joinBevel;
+
+ int dashnum = 0;
+ double dashphase = 0;
+ double * ldash = 0;
+ state->getLineDash(&ldash, &dashnum, &dashphase);
+
+ gfxline_t*line2 = 0;
+
+ if(dashnum && ldash) {
+ float * dash = (float*)malloc(sizeof(float)*(dashnum+1));
+ int t;
+ double cut = 0;
+ int fixzero = 0;
+ msg("<trace> %d dashes", dashnum);
+ msg("<trace> | phase: %f", dashphase);
+ for(t=0;t<dashnum;t++) {
+ dash[t] = ldash[t];
+ msg("<trace> | d%-3d: %f", t, ldash[t]);
+ }
+ dash[dashnum] = -1;
+ if(getLogLevel() >= LOGLEVEL_TRACE) {
+ dump_outline(line);
+ }
+
+ line2 = gfxtool_dash_line(line, dash, dashphase);
+ line = line2;
+ free(dash);
+ msg("<trace> After dashing:");
+ }
+
+ if(getLogLevel() >= LOGLEVEL_TRACE) {
+ msg("<trace> stroke width=%f join=%s cap=%s dashes=%d color=%02x%02x%02x%02x\n",
+ width,
+ lineJoin==0?"miter": (lineJoin==1?"round":"bevel"),
+ lineCap==0?"butt": (lineJoin==1?"round":"square"),
+ dashnum,
+ col.r,col.g,col.b,col.a
+ );
+ dump_outline(line);
+ }
+
+ //swfoutput_drawgfxline(output, line, width, &col, capType, joinType, miterLimit);
+ device->stroke(device, line, width, &col, capType, joinType, miterLimit);
+
+ if(line2)
+ gfxline_free(line2);
+}
+
+void convertRGB()
+{
+}
+
+gfxcolor_t getFillColor(GfxState * state)
+{
+ GfxRGB rgb;
+ double opaq = state->getFillOpacity();
+ state->getFillRGB(&rgb);
+ gfxcolor_t col;
+ col.r = colToByte(rgb.r);
+ col.g = colToByte(rgb.g);
+ col.b = colToByte(rgb.b);
+ col.a = (unsigned char)(opaq*255);
+ return col;
+}
+
+void GFXOutputDev::fillGfxLine(GfxState *state, gfxline_t*line)
+{
+ gfxcolor_t col = getFillColor(state);
+
+ if(getLogLevel() >= LOGLEVEL_TRACE) {
+ msg("<trace> fill %02x%02x%02x%02x\n", col.r, col.g, col.b, col.a);
+ dump_outline(line);
+ }
+ device->fill(device, line, &col);
+}
+void GFXOutputDev::fill(GfxState *state)
+{
+ GfxPath * path = state->getPath();
+ gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
+ fillGfxLine(state, line);
+ gfxline_free(line);
+}
+void GFXOutputDev::eoFill(GfxState *state)
+{
+ GfxPath * path = state->getPath();
+ gfxcolor_t col = getFillColor(state);
+
+ gfxline_t*line= gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
+
+ if(getLogLevel() >= LOGLEVEL_TRACE) {
+ msg("<trace> eofill\n");
+ dump_outline(line);
+ }
+
+ device->fill(device, line, &col);
+ gfxline_free(line);
+}
+
+void GFXOutputDev::clip(GfxState *state)
+{
+ GfxPath * path = state->getPath();
+ gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
+ clipToGfxLine(state, line);
+ gfxline_free(line);
+}
+
+void GFXOutputDev::clipToGfxLine(GfxState *state, gfxline_t*line)
+{
+ if(getLogLevel() >= LOGLEVEL_TRACE) {
+ msg("<trace> clip\n");
+ dump_outline(line);
+ }
+
+ device->startclip(device, line);
+ states[statepos].clipping++;
+}
+void GFXOutputDev::eoClip(GfxState *state)
+{
+ GfxPath * path = state->getPath();
+ gfxline_t*line = gfxPath_to_gfxline(state, path, 1, user_movex, user_movey);
+
+ if(getLogLevel() >= LOGLEVEL_TRACE) {
+ msg("<trace> eoclip\n");
+ dump_outline(line);
+ }
+
+ device->startclip(device, line);
+ states[statepos].clipping++;
+ gfxline_free(line);
+}
+
+void GFXOutputDev::endframe()
+{
+ if(outer_clip_box) {
+ device->endclip(device);
+ outer_clip_box = 0;
+ }
+
+ device->endpage(device);
+}
+
+void GFXOutputDev::finish()
+{
+ if(outer_clip_box) {
+ if(device) {
+ device->endclip(device);
+ }
+ outer_clip_box = 0;
+ }
+}
+
+GFXOutputDev::~GFXOutputDev()
+{
+ finish();
+
+ if(this->pages) {
+ free(this->pages); this->pages = 0;
+ }
+
+ fontlist_t*l = this->fontlist;
+ while(l) {
+ fontlist_t*next = l->next;
+ l->next = 0;
+ gfxfont_free(l->font);
+ free(l->id);l->id=0;
+ free(l->filename);l->filename=0;
+ free(l);
+ l = next;
+ }
+ this->fontlist = 0;
+};
+GBool GFXOutputDev::upsideDown()
+{
+ return gTrue;
+};
+GBool GFXOutputDev::useDrawChar()
+{
+ return gTrue;
+}
+
+char*renderModeDesc[]= {"fill", "stroke", "fill+stroke", "invisible",
+ "clip+fill", "stroke+clip", "fill+stroke+clip", "clip"};
+
+#define RENDER_FILL 0
+#define RENDER_STROKE 1
+#define RENDER_FILLSTROKE 2
+#define RENDER_INVISIBLE 3
+#define RENDER_CLIP 4
+
+static char tmp_printstr[4096];
+char* makeStringPrintable(char*str)
+{
+ int len = strlen(str);
+ int dots = 0;
+ if(len>=80) {
+ len = 80;
+ dots = 1;
+ }
+ int t;
+ for(t=0;t<len;t++) {
+ char c = str[t];
+ if(c<32 || c>124) {
+ c = '.';
+ }
+ tmp_printstr[t] = c;
+ }
+ if(dots) {
+ tmp_printstr[len++] = '.';
+ tmp_printstr[len++] = '.';
+ tmp_printstr[len++] = '.';
+ }
+ tmp_printstr[len] = 0;
+ return tmp_printstr;
+}
+
+
+int getGfxCharID(gfxfont_t*font, int charnr, char *charname, int u)
+{
+ char*uniname = 0;
+ if(u>0) {
+ int t;
+ /* find out char name from unicode index
+ TODO: should be precomputed
+ */
+ for(t=0;t<sizeof(nameToUnicodeTab)/sizeof(nameToUnicodeTab[0]);t++) {
+ if(nameToUnicodeTab[t].u == u) {
+ uniname = nameToUnicodeTab[t].name;
+ break;
+ }
+ }
+ }
+
+ if(charname) {
+ int t;
+ for(t=0;t<font->num_glyphs;t++) {
+ if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,charname)) {
+ msg("<debug> Char [%d,>%s<,%d] maps to %d\n", charnr, charname, u, t);
+ return t;
+ }
+ }
+ /* if we didn't find the character, maybe
+ we can find the capitalized version */
+ for(t=0;t<font->num_glyphs;t++) {
+ if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,charname)) {
+ msg("<debug> Char [%d,>>%s<<,%d] maps to %d\n", charnr, charname, u, t);
+ return t;
+ }
+ }
+ }
+
+ if(uniname) {
+ int t;
+ for(t=0;t<font->num_glyphs;t++) {
+ if(font->glyphs[t].name && !strcmp(font->glyphs[t].name,uniname)) {
+ msg("<debug> Char [%d,%s,>%d(%s)<] maps to %d\n", charnr, charname, u, uniname, t);
+ return t;
+ }
+ }
+ /* if we didn't find the character, maybe
+ we can find the capitalized version */
+ for(t=0;t<font->num_glyphs;t++) {
+ if(font->glyphs[t].name && !strcasecmp(font->glyphs[t].name,uniname)) {
+ msg("<debug> Char [%d,%s,>>%d(%s)<<] maps to %d\n", charnr, charname, u, uniname, t);
+ return t;
+ }
+ }
+ }
+
+ /* try to use the unicode id */
+ if(u>=0 && u<font->max_unicode && font->unicode2glyph[u]>=0) {
+ msg("<debug> Char [%d,%s,>%d<] maps to %d\n", charnr, charname, u, font->unicode2glyph[u]);
+ return font->unicode2glyph[u];
+ }
+
+ if(charnr>=0 && charnr<font->num_glyphs) {
+ msg("<debug> Char [>%d<,%s,%d] maps to %d\n", charnr, charname, u, charnr);
+ return charnr;
+ }
+
+ return -1;
+}
+
+
+void GFXOutputDev::beginString(GfxState *state, GString *s)
+{
+ int render = state->getRender();
+ if(current_text_stroke) {
+ msg("<error> Error: Incompatible change of text rendering to %d while inside cliptext", render);
+ }
+
+ msg("<trace> beginString(%s) render=%d", makeStringPrintable(s->getCString()), render);
+ double m11,m21,m12,m22;
+// msg("<debug> %s beginstring \"%s\"\n", gfxstate2str(state), s->getCString());
+ state->getFontTransMat(&m11, &m12, &m21, &m22);
+ m11 *= state->getHorizScaling();
+ m21 *= state->getHorizScaling();
+
+ this->current_font_matrix.m00 = m11 / 1024.0;
+ this->current_font_matrix.m01 = m12 / 1024.0;
+ this->current_font_matrix.m10 = -m21 / 1024.0;
+ this->current_font_matrix.m11 = -m22 / 1024.0;
+ this->current_font_matrix.tx = 0;
+ this->current_font_matrix.ty = 0;
+
+ gfxmatrix_t m = this->current_font_matrix;
+
+ /*if(render != 3 && render != 0)
+ msg("<warning> Text rendering mode %d (%s) not fully supported yet (for text \"%s\")", render, renderModeDesc[render&7], makeStringPrintable(s->getCString()));*/
+ states[statepos].textRender = render;
+}
+
+void GFXOutputDev::drawChar(GfxState *state, double x, double y,
+ double dx, double dy,
+ double originX, double originY,
+ CharCode c, int nBytes, Unicode *_u, int uLen)
+{
+ int render = state->getRender();
+ // check for invisible text -- this is used by Acrobat Capture
+ if (render == 3) {
+ msg("<debug> Ignoring invisible text: char %d at %f,%f", c, x, y);
+ return;
+ }
+
+ if(states[statepos].textRender != render)
+ msg("<error> Internal error: drawChar.render!=beginString.render");
+
+ gfxcolor_t col = getFillColor(state);
+
+ Gushort *CIDToGIDMap = 0;
+ GfxFont*font = state->getFont();
+
+ if(font->getType() == fontType3) {
+ /* type 3 chars are passed as graphics */
+ msg("<debug> type3 char at %f/%f", x, y);
+ return;
+ }
+
+ Unicode u=0;
+ char*name=0;
+
+ if(uLen)
+ u = _u[0];
+
+ if(font->isCIDFont()) {
+ GfxCIDFont*cfont = (GfxCIDFont*)font;
+
+ if(font->getType() == fontCIDType2)
+ CIDToGIDMap = cfont->getCIDToGID();
+ } else {
+ Gfx8BitFont*font8;
+ font8 = (Gfx8BitFont*)font;
+ char**enc=font8->getEncoding();
+ name = enc[c];
+ }
+ if (CIDToGIDMap) {
+ msg("<debug> drawChar(%f, %f, c='%c' (%d), GID=%d, u=%d <%d>) CID=%d name=\"%s\" render=%d\n", x, y, (c&127)>=32?c:'?', c, CIDToGIDMap[c], u, uLen, font->isCIDFont(), FIXNULL(name), render);
+ c = CIDToGIDMap[c];
+ } else {
+ msg("<debug> drawChar(%f,%f,c='%c' (%d), u=%d <%d>) CID=%d name=\"%s\" render=%d\n",x,y,(c&127)>=32?c:'?',c,u, uLen, font->isCIDFont(), FIXNULL(name), render);
+ }
+
+ int charid = -1;
+
+ if(uLen<=1) {
+ charid = getGfxCharID(current_gfxfont, c, name, u);
+ } else {
+ charid = getGfxCharID(current_gfxfont, c, name, -1);
+
+ if(charid < 0) {
+ /* multiple unicodes- should usually map to a ligature.
+ if the ligature doesn't exist, we need to draw
+ the characters one-by-one. */
+ int t;
+ msg("<warning> ligature %d missing in font %s\n", c, current_font_id);
+ for(t=0;t<uLen;t++) {
+ drawChar(state, x, y, dx, dy, originX, originY, c, nBytes, _u+t, 1);
+ }
+ return;
+ }
+ }
+ if(charid<0) {
+ msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
+ FIXNULL(name),c, u, FIXNULL((char*)current_font_id), current_gfxfont->num_glyphs);
+ return;
+ }
+
+ gfxmatrix_t m = this->current_font_matrix;
+ state->transform(x, y, &m.tx, &m.ty);
+ m.tx += user_movex;
+ m.ty += user_movey;
+
+ if(render == RENDER_FILL) {
+ device->drawchar(device, current_font_id, charid, &col, &m);
+ } else {
+ msg("<debug> Drawing glyph %d as shape", charid);
+ if(!textmodeinfo) {
+ msg("<notice> Some texts will be rendered as shape");
+ textmodeinfo = 1;
+ }
+ gfxline_t*glyph = current_gfxfont->glyphs[charid].line;
+ gfxline_t*tglyph = gfxline_clone(glyph);
+ gfxline_transform(tglyph, &m);
+ if((render&3) != RENDER_INVISIBLE) {
+ gfxline_t*add = gfxline_clone(tglyph);
+ current_text_stroke = gfxline_append(current_text_stroke, add);
+ }
+ if(render&RENDER_CLIP) {
+ gfxline_t*add = gfxline_clone(tglyph);
+ current_text_clip = gfxline_append(current_text_clip, add);
+ }
+ gfxline_free(tglyph);
+ }
+}
+
+void GFXOutputDev::endString(GfxState *state)
+{
+ int render = state->getRender();
+ msg("<trace> endString() render=%d textstroke=%08x", render, current_text_stroke);
+ if(states[statepos].textRender != render)
+ msg("<error> Internal error: drawChar.render!=beginString.render");
+
+ if(current_text_stroke) {
+ /* fillstroke and stroke text rendering objects we can process right
+ now (as there may be texts of other rendering modes in this
+ text object)- clipping objects have to wait until endTextObject,
+ however */
+ device->setparameter(device, "mark","TXT");
+ if((render&3) == RENDER_FILL) {
+ fillGfxLine(state, current_text_stroke);
+ gfxline_free(current_text_stroke);
+ current_text_stroke = 0;
+ } else if((render&3) == RENDER_FILLSTROKE) {
+ fillGfxLine(state, current_text_stroke);
+ strokeGfxline(state, current_text_stroke);
+ gfxline_free(current_text_stroke);
+ current_text_stroke = 0;
+ } else if((render&3) == RENDER_STROKE) {
+ strokeGfxline(state, current_text_stroke);
+ gfxline_free(current_text_stroke);
+ current_text_stroke = 0;
+ }
+ device->setparameter(device, "mark","");
+ }
+}
+
+void GFXOutputDev::endTextObject(GfxState *state)
+{
+ int render = state->getRender();
+ msg("<trace> endTextObject() render=%d textstroke=%08x clipstroke=%08x", render, current_text_stroke, current_text_clip);
+ if(states[statepos].textRender != render)
+ msg("<error> Internal error: drawChar.render!=beginString.render");
+
+ if(current_text_clip) {
+ device->setparameter(device, "mark","TXT");
+ clipToGfxLine(state, current_text_clip);
+ device->setparameter(device, "mark","");
+ gfxline_free(current_text_clip);
+ current_text_clip = 0;
+ }
+}
+
+/* the logic seems to be as following:
+ first, beginType3Char is called, with the charcode and the coordinates.
+ if this function returns true, it already knew about the char and has now drawn it.
+ if the function returns false, it's a new char, and type3D1 is called with some parameters-
+ the all draw operations until endType3Char are part of the char (which in this moment is
+ at the position first passed to beginType3Char). the char ends with endType3Char.
+
+ The drawing operations between beginType3Char and endType3Char are somewhat different to
+ the normal ones. For example, the fillcolor equals the stroke color.
+*/
+
+GBool GFXOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen)
+{
+ msg("<debug> beginType3Char %d, %08x, %d", code, *u, uLen);
+ type3active = 1;
+ /* the character itself is going to be passed using the draw functions */
+ return gFalse; /* gTrue= is_in_cache? */
+}
+
+void GFXOutputDev::type3D0(GfxState *state, double wx, double wy) {
+ msg("<debug> type3D0 width=%f height=%f", wx, wy);
+}
+void GFXOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) {
+ msg("<debug> type3D1 width=%f height=%f bbox=(%f,%f,%f,%f)", wx, wy,
+ llx,lly,urx,ury);
+}
+
+void GFXOutputDev::endType3Char(GfxState *state)
+{
+ type3active = 0;
+ msg("<debug> endType3Char");
+}
+
+void GFXOutputDev::startFrame(int width, int height)
+{
+ device->startpage(device, width, height);
+}
+
+void GFXOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
+{
+ this->currentpage = pageNum;
+ double x1,y1,x2,y2;
+ int rot = doc->getPageRotate(1);
+ gfxcolor_t white;
+ laststate = state;
+ gfxline_t clippath[5];
+
+ white.r = white.g = white.b = white.a = 255;
+
+ /* state->transform(state->getX1(),state->getY1(),&x1,&y1);
+ state->transform(state->getX2(),state->getY2(),&x2,&y2);
+ Use CropBox, not MediaBox, as page size
+ */
+
+ /*x1 = crop_x1;
+ y1 = crop_y1;
+ x2 = crop_x2;
+ y2 = crop_y2;*/
+ state->transform(crop_x1,crop_y1,&x1,&y1); //x1 += user_movex; y1 += user_movey;
+ state->transform(crop_x2,crop_y2,&x2,&y2); //x2 += user_movex; y2 += user_movey;
+
+ if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
+ if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
+
+
+ /* apply user clip box */
+ if(user_clipx1|user_clipy1|user_clipx2|user_clipy2) {
+ /*if(user_clipx1 > x1)*/ x1 = user_clipx1;
+ /*if(user_clipx2 < x2)*/ x2 = user_clipx2;
+ /*if(user_clipy1 > y1)*/ y1 = user_clipy1;
+ /*if(user_clipy2 < y2)*/ y2 = user_clipy2;
+ }
+
+ //msg("<verbose> Bounding box is (%f,%f)-(%f,%f) [shifted by %d/%d]", x1,y1,x2,y2, user_movex, user_movey);
+
+ if(outer_clip_box) {
+ device->endclip(device);
+ outer_clip_box = 0;
+ }
+
+ msg("<notice> processing PDF page %d (%dx%d:%d:%d) (move:%d:%d)", pageNum, (int)x2-(int)x1,(int)y2-(int)y1, (int)x1, (int)y1, user_movex, user_movey);
+ if(rot!=0)
+ msg("<verbose> page is rotated %d degrees\n", rot);
+
+ clippath[0].type = gfx_moveTo;clippath[0].x = x1; clippath[0].y = y1; clippath[0].next = &clippath[1];
+ clippath[1].type = gfx_lineTo;clippath[1].x = x2; clippath[1].y = y1; clippath[1].next = &clippath[2];
+ clippath[2].type = gfx_lineTo;clippath[2].x = x2; clippath[2].y = y2; clippath[2].next = &clippath[3];
+ clippath[3].type = gfx_lineTo;clippath[3].x = x1; clippath[3].y = y2; clippath[3].next = &clippath[4];
+ clippath[4].type = gfx_lineTo;clippath[4].x = x1; clippath[4].y = y1; clippath[4].next = 0;
+ device->startclip(device, clippath); outer_clip_box = 1;
+ device->fill(device, clippath, &white);
+}
+
+void GFXOutputDev::drawLink(Link *link, Catalog *catalog)
+{
+ double x1, y1, x2, y2, w;
+ gfxline_t points[5];
+ int x, y;
+
+ msg("<debug> drawlink\n");
+
+ link->getRect(&x1, &y1, &x2, &y2);
+ cvtUserToDev(x1, y1, &x, &y);
+ points[0].type = gfx_moveTo;
+ points[0].x = points[4].x = x + user_movex;
+ points[0].y = points[4].y = y + user_movey;
+ points[0].next = &points[1];
+ cvtUserToDev(x2, y1, &x, &y);
+ points[1].type = gfx_lineTo;
+ points[1].x = x + user_movex;
+ points[1].y = y + user_movey;
+ points[1].next = &points[2];
+ cvtUserToDev(x2, y2, &x, &y);
+ points[2].type = gfx_lineTo;
+ points[2].x = x + user_movex;
+ points[2].y = y + user_movey;
+ points[2].next = &points[3];
+ cvtUserToDev(x1, y2, &x, &y);
+ points[3].type = gfx_lineTo;
+ points[3].x = x + user_movex;
+ points[3].y = y + user_movey;
+ points[3].next = &points[4];
+ cvtUserToDev(x1, y1, &x, &y);
+ points[4].type = gfx_lineTo;
+ points[4].x = x + user_movex;
+ points[4].y = y + user_movey;
+ points[4].next = 0;
+
+ msg("<trace> drawlink %.2f/%.2f %.2f/%.2f %.2f/%.2f %.2f/%.2f\n",
+ points[0].x, points[0].y,
+ points[1].x, points[1].y,
+ points[2].x, points[2].y,
+ points[3].x, points[3].y);
+
+ LinkAction*action=link->getAction();
+ char buf[128];
+ char*s = 0;
+ char*type = "-?-";
+ char*named = 0;
+ int page = -1;
+ msg("<trace> drawlink action=%d\n", action->getKind());
+ switch(action->getKind())
+ {
+ case actionGoTo: {
+ type = "GoTo";
+ LinkGoTo *ha=(LinkGoTo *)link->getAction();
+ LinkDest *dest=NULL;
+ if (ha->getDest()==NULL)
+ dest=catalog->findDest(ha->getNamedDest());
+ else dest=ha->getDest();
+ if (dest){
+ if (dest->isPageRef()){
+ Ref pageref=dest->getPageRef();
+ page=catalog->findPage(pageref.num,pageref.gen);
+ }
+ else page=dest->getPageNum();
+ sprintf(buf, "%d", page);
+ s = strdup(buf);
+ }
+ }
+ break;
+ case actionGoToR: {
+ type = "GoToR";
+ LinkGoToR*l = (LinkGoToR*)action;
+ GString*g = l->getNamedDest();
+ if(g)
+ s = strdup(g->getCString());
+ }
+ break;
+ case actionNamed: {
+ type = "Named";
+ LinkNamed*l = (LinkNamed*)action;
+ GString*name = l->getName();
+ if(name) {
+ s = strdup(name->lowerCase()->getCString());
+ named = name->getCString();
+ if(!strchr(s,':'))
+ {
+ if(strstr(s, "next") || strstr(s, "forward"))
+ {
+ page = currentpage + 1;
+ }
+ else if(strstr(s, "prev") || strstr(s, "back"))
+ {
+ page = currentpage - 1;
+ }
+ else if(strstr(s, "last") || strstr(s, "end"))
+ {
+ if(pages && pagepos>0)
+ page = pages[pagepos-1];
+ }
+ else if(strstr(s, "first") || strstr(s, "top"))
+ {
+ page = 1;
+ }
+ }
+ }
+ }
+ break;
+ case actionLaunch: {
+ type = "Launch";
+ LinkLaunch*l = (LinkLaunch*)action;
+ GString * str = new GString(l->getFileName());
+ GString * params = l->getParams();
+ if(params)
+ str->append(params);
+ s = strdup(str->getCString());
+ delete str;
+ }
+ break;
+ case actionURI: {
+ char*url = 0;
+ type = "URI";
+ LinkURI*l = (LinkURI*)action;
+ GString*g = l->getURI();
+ if(g) {
+ url = g->getCString();
+ s = strdup(url);
+ }
+ }
+ break;
+ case actionUnknown: {
+ type = "Unknown";
+ LinkUnknown*l = (LinkUnknown*)action;
+ s = strdup("");
+ }
+ break;
+ default: {
+ msg("<error> Unknown link type!\n");
+ break;
+ }
+ }
+
+ if(!s) s = strdup("-?-");
+
+ msg("<trace> drawlink s=%s\n", s);
+
+ if(!linkinfo && (page || s))
+ {
+ msg("<notice> File contains links");
+ linkinfo = 1;
+ }
+
+ if(page>0)
+ {
+ int t;
+ int lpage = -1;
+ for(t=1;t<=pagepos;t++) {
+ if(pages[t]==page) {
+ lpage = t;
+ break;
+ }
+ }
+ if(lpage<0) {
+ lpage = page;
+ }
+ char buf[80];
+ sprintf(buf, "page%d", lpage);
+ device->drawlink(device, points, buf);
+ }
+ else if(s)
+ {
+ device->drawlink(device, points, s);
+ }
+
+ msg("<verbose> \"%s\" link to \"%s\" (%d)\n", type, FIXNULL(s), page);
+ free(s);s=0;
+}
+
+void GFXOutputDev::saveState(GfxState *state) {
+ msg("<trace> saveState\n");
+ updateAll(state);
+ if(statepos>=64) {
+ msg("<error> Too many nested states in pdf.");
+ return;
+ }
+ statepos ++;
+ states[statepos].clipping = 0; //? shouldn't this be the current value?
+ states[statepos].textRender = states[statepos-1].textRender;
+};
+
+void GFXOutputDev::restoreState(GfxState *state) {
+ msg("<trace> restoreState\n");
+ updateAll(state);
+ while(states[statepos].clipping) {
+ device->endclip(device);
+ states[statepos].clipping--;
+ }
+ statepos--;
+}
+
+char* GFXOutputDev::searchFont(char*name)
+{
+ int i;
+ char*filename=0;
+ int is_standard_font = 0;
+
+ msg("<verbose> SearchFont(%s)", name);
+
+ /* see if it is a pdf standard font */
+ for(i=0;i<sizeof(pdf2t1map)/sizeof(mapping);i++)
+ {
+ if(!strcmp(name, pdf2t1map[i].pdffont))
+ {
+ name = pdf2t1map[i].filename;
+ is_standard_font = 1;
+ break;
+ }
+ }
+ /* look in all font files */
+ for(i=0;i<fontnum;i++)
+ {
+ if(strstr(fonts[i].filename, name))
+ {
+ if(!fonts[i].used) {
+
+ fonts[i].used = 1;
+ if(!is_standard_font)
+ msg("<notice> Using %s for %s", fonts[i].filename, name);
+ }
+ return strdup(fonts[i].filename);
+ }
+ }
+ return 0;
+}
+
+void GFXOutputDev::updateLineWidth(GfxState *state)
+{
+ double width = state->getTransformedLineWidth();
+ //swfoutput_setlinewidth(&device, width);
+}
+
+void GFXOutputDev::updateLineCap(GfxState *state)
+{
+ int c = state->getLineCap();
+}
+
+void GFXOutputDev::updateLineJoin(GfxState *state)
+{
+ int j = state->getLineJoin();
+}
+
+void GFXOutputDev::updateFillColor(GfxState *state)
+{
+ GfxRGB rgb;
+ double opaq = state->getFillOpacity();
+ state->getFillRGB(&rgb);
+
+ //swfoutput_setfillcolor(&device, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
+}
+
+void GFXOutputDev::updateStrokeColor(GfxState *state)
+{
+ GfxRGB rgb;
+ double opaq = state->getStrokeOpacity();
+ state->getStrokeRGB(&rgb);
+ //swfoutput_setstrokecolor(&device, (char)(rgb.r*255), (char)(rgb.g*255), (char)(rgb.b*255), (char)(opaq*255));
+}
+
+void FoFiWrite(void *stream, char *data, int len)
+{
+ fwrite(data, len, 1, (FILE*)stream);
+}
+
+char*GFXOutputDev::writeEmbeddedFontToFile(XRef*ref, GfxFont*font)
+{
+ char*tmpFileName = NULL;
+ FILE *f;
+ int c;
+ char *fontBuf;
+ int fontLen;
+ Ref embRef;
+ Object refObj, strObj;
+ char namebuf[512];
+ tmpFileName = mktmpname(namebuf);
+ int ret;
+
+ ret = font->getEmbeddedFontID(&embRef);
+ if(!ret) {
+ msg("<verbose> Didn't get embedded font id");
+ /* not embedded- the caller should now search the font
+ directories for this font */
+ return 0;
+ }
+
+ f = fopen(tmpFileName, "wb");
+ if (!f) {
+ msg("<error> Couldn't create temporary Type 1 font file");
+ return 0;
+ }
+
+ /*if(font->isCIDFont()) {
+ GfxCIDFont* cidFont = (GfxCIDFont *)font;
+ GString c = cidFont->getCollection();
+ msg("<notice> Collection: %s", c.getCString());
+ }*/
+
+ //if (font->getType() == fontType1C) {
+ if (0) { //font->getType() == fontType1C) {
+ if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
+ fclose(f);
+ msg("<error> Couldn't read embedded font file");
+ return 0;
+ }
+ FoFiType1C *cvt = FoFiType1C::make(fontBuf, fontLen);
+ if(!cvt) return 0;
+ cvt->convertToType1(NULL, gTrue, FoFiWrite, f);
+ //cvt->convertToCIDType0("test", f);
+ //cvt->convertToType0("test", f);
+ delete cvt;
+ gfree(fontBuf);
+ } else if(font->getType() == fontTrueType) {
+ msg("<verbose> writing font using TrueTypeFontFile::writeTTF");
+ if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) {
+ fclose(f);
+ msg("<error> Couldn't read embedded font file");
+ return 0;
+ }
+ FoFiTrueType *cvt = FoFiTrueType::make(fontBuf, fontLen);
+ cvt->writeTTF(FoFiWrite, f);
+ delete cvt;
+ gfree(fontBuf);
+ } else {
+ font->getEmbeddedFontID(&embRef);
+ refObj.initRef(embRef.num, embRef.gen);
+ refObj.fetch(ref, &strObj);
+ refObj.free();
+ strObj.streamReset();
+ int f4[4];
+ char f4c[4];
+ int t;
+ for(t=0;t<4;t++) {
+ f4[t] = strObj.streamGetChar();
+ f4c[t] = (char)f4[t];
+ if(f4[t] == EOF)
+ break;
+ }
+ if(t==4) {
+ if(!strncmp(f4c, "true", 4)) {
+ /* some weird TTF fonts don't start with 0,1,0,0 but with "true".
+ Change this on the fly */
+ f4[0] = f4[2] = f4[3] = 0;
+ f4[1] = 1;
+ }
+ fputc(f4[0], f);
+ fputc(f4[1], f);
+ fputc(f4[2], f);
+ fputc(f4[3], f);
+
+ while ((c = strObj.streamGetChar()) != EOF) {
+ fputc(c, f);
+ }
+ }
+ strObj.streamClose();
+ strObj.free();
+ }
+ fclose(f);
+
+ return strdup(tmpFileName);
+}
+
+char* GFXOutputDev::searchForSuitableFont(GfxFont*gfxFont)
+{
+ char*name = getFontName(gfxFont);
+ char*fontname = 0;
+ char*filename = 0;
+
+ if(!this->config_use_fontconfig)
+ return 0;
+
+#ifdef HAVE_FONTCONFIG
+ FcPattern *pattern, *match;
+ FcResult result;
+ FcChar8 *v;
+
+ static int fcinitcalled = false;
+
+ msg("<debug> searchForSuitableFont(%s)", name);
+
+ // call init ony once
+ if (!fcinitcalled) {
+ msg("<debug> Initializing FontConfig...");
+ fcinitcalled = true;
+ if(!FcInit()) {
+ msg("<debug> FontConfig Initialization failed. Disabling.");
+ config_use_fontconfig = 0;
+ return 0;
+ }
+ msg("<debug> ...initialized FontConfig");
+ }
+
+ msg("<debug> FontConfig: Create \"%s\" Family Pattern", name);
+ pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, name, NULL);
+ if (gfxFont->isItalic()) // check for italic
+ msg("<debug> FontConfig: Adding Italic Slant");
+ FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
+ if (gfxFont->isBold()) // check for bold
+ msg("<debug> FontConfig: Adding Bold Weight");
+ FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
+
+ msg("<debug> FontConfig: Try to match...");
+ // configure and match using the original font name
+ FcConfigSubstitute(0, pattern, FcMatchPattern);
+ FcDefaultSubstitute(pattern);
+ match = FcFontMatch(0, pattern, &result);
+
+ if (FcPatternGetString(match, "family", 0, &v) == FcResultMatch) {
+ msg("<debug> FontConfig: family=%s", (char*)v);
+ // if we get an exact match
+ if (strcmp((char *)v, name) == 0) {
+ if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
+ filename = strdup((char*)v); // mem leak
+ char *nfn = strrchr(filename, '/');
+ if(nfn) fontname = strdup(nfn+1);
+ else fontname = filename;
+ }
+ msg("<debug> FontConfig: Returning \"%s\"", fontname);
+ } else {
+ // initialize patterns
+ FcPatternDestroy(pattern);
+ FcPatternDestroy(match);
+
+ // now match against serif etc.
+ if (gfxFont->isSerif()) {
+ msg("<debug> FontConfig: Create Serif Family Pattern");
+ pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "serif", NULL);
+ } else if (gfxFont->isFixedWidth()) {
+ msg("<debug> FontConfig: Create Monospace Family Pattern");
+ pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "monospace", NULL);
+ } else {
+ msg("<debug> FontConfig: Create Sans Family Pattern");
+ pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, "sans", NULL);
+ }
+
+ // check for italic
+ if (gfxFont->isItalic()) {
+ msg("<debug> FontConfig: Adding Italic Slant");
+ int bb = FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
+ }
+ // check for bold
+ if (gfxFont->isBold()) {
+ msg("<debug> FontConfig: Adding Bold Weight");
+ int bb = FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
+ }
+
+ msg("<debug> FontConfig: Try to match... (2)");
+ // configure and match using serif etc
+ FcConfigSubstitute (0, pattern, FcMatchPattern);
+ FcDefaultSubstitute (pattern);
+ match = FcFontMatch (0, pattern, &result);
+
+ if (FcPatternGetString(match, "file", 0, &v) == FcResultMatch) {
+ filename = strdup((char*)v); // mem leak
+ char *nfn = strrchr(filename, '/');
+ if(nfn) fontname = strdup(nfn+1);
+ else fontname = filename;
+ }
+ msg("<debug> FontConfig: Returning \"%s\"", fontname);
+ }
+ }
+
+ //printf("FONTCONFIG: pattern");
+ //FcPatternPrint(pattern);
+ //printf("FONTCONFIG: match");
+ //FcPatternPrint(match);
+
+ FcPatternDestroy(pattern);
+ FcPatternDestroy(match);
+
+ pdfswf_addfont(filename);
+ return fontname;
+#else
+ return 0;
+#endif
+}
+
+char* GFXOutputDev::substituteFont(GfxFont*gfxFont, char* oldname)
+{
+ char*fontname = 0, *filename = 0;
+ msg("<notice> substituteFont(%s)", oldname);
+
+ if(!(fontname = searchForSuitableFont(gfxFont))) {
+ fontname = "Times-Roman";
+ }
+ filename = searchFont(fontname);
+ if(!filename) {
+ msg("<error> Couldn't find font %s- did you install the default fonts?", fontname);
+ return 0;
+ }
+
+ if(substitutepos>=sizeof(substitutesource)/sizeof(char*)) {
+ msg("<fatal> Too many fonts in file.");
+ exit(1);
+ }
+ if(oldname) {
+ substitutesource[substitutepos] = strdup(oldname); //mem leak
+ substitutetarget[substitutepos] = fontname;
+ msg("<notice> substituting %s -> %s", FIXNULL(oldname), FIXNULL(fontname));
+ substitutepos ++;
+ }
+ return strdup(filename); //mem leak
+}
+
+void unlinkfont(char* filename)
+{
+ int l;
+ if(!filename)
+ return;
+ l=strlen(filename);
+ unlink(filename);
+ if(!strncmp(&filename[l-4],".afm",4)) {
+ memcpy(&filename[l-4],".pfb",4);
+ unlink(filename);
+ memcpy(&filename[l-4],".pfa",4);
+ unlink(filename);
+ memcpy(&filename[l-4],".afm",4);
+ return;
+ } else
+ if(!strncmp(&filename[l-4],".pfa",4)) {
+ memcpy(&filename[l-4],".afm",4);
+ unlink(filename);
+ memcpy(&filename[l-4],".pfa",4);
+ return;
+ } else
+ if(!strncmp(&filename[l-4],".pfb",4)) {
+ memcpy(&filename[l-4],".afm",4);
+ unlink(filename);
+ memcpy(&filename[l-4],".pfb",4);
+ return;
+ }
+}
+
+void GFXOutputDev::setXRef(PDFDoc*doc, XRef *xref)
+{
+ this->doc = doc;
+ this->xref = xref;
+}
+
+int GFXOutputDev::setGfxFont(char*id, char*name, char*filename, double maxSize)
+{
+ gfxfont_t*font = 0;
+ fontlist_t*last=0,*l = this->fontlist;
+
+ /* TODO: should this be part of the state? */
+ while(l) {
+ last = l;
+ if(!strcmp(l->id, id)) {
+ current_font_id = l->id;
+ current_gfxfont = l->font;
+ font = l->font;
+ device->addfont(device, id, current_gfxfont);
+ return 1;
+ }
+ l = l->next;
+ }
+ if(!filename) return 0;
+
+ /* A font size of e.g. 9 means the font will be scaled down by
+ 1024 and scaled up by 9. So to have a maximum error of 1/20px,
+ we have to divide 0.05 by (fontsize/1024)
+ */
+ double quality = (1024 * 0.05) / maxSize;
+
+ msg("<verbose> Loading %s...", filename);
+ font = gfxfont_load(filename, quality);
+ msg("<verbose> Font %s loaded successfully", filename);
+
+ l = new fontlist_t;
+ l->font = font;
+ l->filename = strdup(filename);
+ l->id = strdup(id);
+ l->next = 0;
+ current_font_id = l->id;
+ current_gfxfont = l->font;
+ if(last) {
+ last->next = l;
+ } else {
+ this->fontlist = l;
+ }
+ device->addfont(device, id, current_gfxfont);
+ return 1;
+}
+
+void GFXOutputDev::updateFont(GfxState *state)
+{
+ GfxFont*gfxFont = state->getFont();
+
+ if (!gfxFont) {
+ return;
+ }
+
+ char * fontid = getFontID(gfxFont);
+ char * fontname = getFontName(gfxFont);
+
+ double maxSize = 1.0;
+
+ if(this->info) {
+ maxSize = this->info->getMaximumFontSize(fontid);
+ }
+
+ int t;
+ /* first, look if we substituted this font before-
+ this way, we don't initialize the T1 Fonts
+ too often */
+ for(t=0;t<substitutepos;t++) {
+ if(!strcmp(fontid, substitutesource[t])) {
+ free(fontid);fontid=0;
+ fontid = strdup(substitutetarget[t]);
+ break;
+ }
+ }
+
+ /* second, see if this is a font which was used before-
+ if so, we are done */
+ if(setGfxFont(fontid, fontname, 0, 0)) {
+ free(fontid);
+ free(fontname);
+ return;
+ }
+/* if(swfoutput_queryfont(&device, fontid))
+ swfoutput_setfont(&device, fontid, 0);
+
+ msg("<debug> updateFont(%s) [cached]", fontid);
+ return;
+ }*/
+
+ // look for Type 3 font
+ if (gfxFont->getType() == fontType3) {
+ if(!type3Warning) {
+ type3Warning = gTrue;
+ showFontError(gfxFont, 2);
+ }
+ free(fontid);
+ free(fontname);
+ return;
+ }
+
+ /* now either load the font, or find a substitution */
+
+ Ref embRef;
+ GBool embedded = gfxFont->getEmbeddedFontID(&embRef);
+
+ char*fileName = 0;
+ int del = 0;
+ if(embedded &&
+ (gfxFont->getType() == fontType1 ||
+ gfxFont->getType() == fontType1C ||
+ (gfxFont->getType() == fontCIDType0C && forceType0Fonts) ||
+ gfxFont->getType() == fontTrueType ||
+ gfxFont->getType() == fontCIDType2
+ ))
+ {
+ fileName = writeEmbeddedFontToFile(xref, gfxFont);
+ if(!fileName) showFontError(gfxFont,0);
+ else del = 1;
+ } else {
+ fileName = searchFont(fontname);
+ if(!fileName) showFontError(gfxFont,0);
+ }
+ if(!fileName) {
+ char * fontname = getFontName(gfxFont);
+ msg("<warning> Font %s %scould not be loaded.", fontname, embedded?"":"(not embedded) ");
+
+ if(lastfontdir)
+ msg("<warning> Try putting a TTF version of that font (named \"%s.ttf\") into %s", fontname, lastfontdir);
+ else
+ msg("<warning> Try specifying one or more font directories");
+
+ fileName = substituteFont(gfxFont, fontid);
+ if(!fileName)
+ exit(1);
+ if(fontid) { free(fontid);fontid = strdup(substitutetarget[substitutepos-1]); /*ugly hack*/};
+ msg("<notice> Font is now %s (%s)", fontid, fileName);
+ }
+
+ if(!fileName) {
+ msg("<error> Couldn't set font %s\n", fontid);
+ free(fontid);
+ free(fontname);
+ return;
+ }
+
+ msg("<verbose> updateFont(%s) -> %s (max size: %f)", fontid, fileName, maxSize);
+ dumpFontInfo("<verbose>", gfxFont);
+
+ //swfoutput_setfont(&device, fontid, fileName);
+
+ if(!setGfxFont(fontid, fontname, 0, 0)) {
+ setGfxFont(fontid, fontname, fileName, maxSize);
+ }
+
+ if(fileName && del)
+ unlinkfont(fileName);
+
+ if(fileName)
+ free(fileName);
+ free(fontid);
+ free(fontname);
+
+ msg("<verbose> |");
+}
+
+#define SQR(x) ((x)*(x))
+
+unsigned char* antialize(unsigned char*data, int width, int height, int newwidth, int newheight, int palettesize)
+{
+ if((newwidth<2 || newheight<2) ||
+ (width<=newwidth || height<=newheight))
+ return 0;
+ unsigned char*newdata;
+ int x,y;
+ newdata= (unsigned char*)malloc(newwidth*newheight);
+ int t;
+ double fx = (double)(width)/newwidth;
+ double fy = (double)(height)/newheight;
+ double px = 0;
+ int blocksize = (int)(8192/(fx*fy));
+ int r = 8192*256/palettesize;
+ for(x=0;x<newwidth;x++) {
+ double ex = px + fx;
+ int fromx = (int)px;
+ int tox = (int)ex;
+ int xweight1 = (int)(((fromx+1)-px)*256);
+ int xweight2 = (int)((ex-tox)*256);
+ double py =0;
+ for(y=0;y<newheight;y++) {
+ double ey = py + fy;
+ int fromy = (int)py;
+ int toy = (int)ey;
+ int yweight1 = (int)(((fromy+1)-py)*256);
+ int yweight2 = (int)((ey-toy)*256);
+ int a = 0;
+ int xx,yy;
+ for(xx=fromx;xx<=tox;xx++)
+ for(yy=fromy;yy<=toy;yy++) {
+ int b = 1-data[width*yy+xx];
+ int weight=256;
+ if(xx==fromx) weight = (weight*xweight1)/256;
+ if(xx==tox) weight = (weight*xweight2)/256;
+ if(yy==fromy) weight = (weight*yweight1)/256;
+ if(yy==toy) weight = (weight*yweight2)/256;
+ a+=b*weight;
+ }
+ //if(a) a=(palettesize-1)*r/blocksize;
+ newdata[y*newwidth+x] = (a*blocksize)/r;
+ py = ey;
+ }
+ px = ex;
+ }
+ return newdata;
+}
+
+#define IMAGE_TYPE_JPEG 0
+#define IMAGE_TYPE_LOSSLESS 1
+
+static void drawimage(gfxdevice_t*dev, gfxcolor_t* data, int sizex,int sizey,
+ double x1,double y1,
+ double x2,double y2,
+ double x3,double y3,
+ double x4,double y4, int type)
+{
+ gfxcolor_t*newpic=0;
+
+ double l1 = sqrt((x4-x1)*(x4-x1) + (y4-y1)*(y4-y1));
+ double l2 = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
+
+ gfxline_t p1,p2,p3,p4,p5;
+ p1.type=gfx_moveTo;p1.x=x1; p1.y=y1;p1.next=&p2;
+ p2.type=gfx_lineTo;p2.x=x2; p2.y=y2;p2.next=&p3;
+ p3.type=gfx_lineTo;p3.x=x3; p3.y=y3;p3.next=&p4;
+ p4.type=gfx_lineTo;p4.x=x4; p4.y=y4;p4.next=&p5;
+ p5.type=gfx_lineTo;p5.x=x1; p5.y=y1;p5.next=0;
+
+ {p1.x = (int)(p1.x*20)/20.0;
+ p1.y = (int)(p1.y*20)/20.0;
+ p2.x = (int)(p2.x*20)/20.0;
+ p2.y = (int)(p2.y*20)/20.0;
+ p3.x = (int)(p3.x*20)/20.0;
+ p3.y = (int)(p3.y*20)/20.0;
+ p4.x = (int)(p4.x*20)/20.0;
+ p4.y = (int)(p4.y*20)/20.0;
+ p5.x = (int)(p5.x*20)/20.0;
+ p5.y = (int)(p5.y*20)/20.0;
+ }
+
+ float m00,m10,tx;
+ float m01,m11,ty;
+
+ gfxmatrix_t m;
+ m.m00 = (p4.x-p1.x)/sizex; m.m10 = (p2.x-p1.x)/sizey;
+ m.m01 = (p4.y-p1.y)/sizex; m.m11 = (p2.y-p1.y)/sizey;
+ m.tx = p1.x - 0.5;
+ m.ty = p1.y - 0.5;
+
+ gfximage_t img;
+ img.data = (gfxcolor_t*)data;
+ img.width = sizex;
+ img.height = sizey;
+
+ if(type == IMAGE_TYPE_JPEG)
+ /* TODO: pass image_dpi to device instead */
+ dev->setparameter(dev, "next_bitmap_is_jpeg", "1");
+
+ dev->fillbitmap(dev, &p1, &img, &m, 0);
+}
+
+void drawimagejpeg(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
+ double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
+{
+ drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_JPEG);
+}
+
+void drawimagelossless(gfxdevice_t*dev, gfxcolor_t*mem, int sizex,int sizey,
+ double x1,double y1, double x2,double y2, double x3,double y3, double x4,double y4)
+{
+ drawimage(dev,mem,sizex,sizey,x1,y1,x2,y2,x3,y3,x4,y4, IMAGE_TYPE_LOSSLESS);
+}
+
+
+void GFXOutputDev::drawGeneralImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GfxImageColorMap*colorMap, GBool invert,
+ GBool inlineImg, int mask, int*maskColors,
+ Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap)
+{
+ double x1,y1,x2,y2,x3,y3,x4,y4;
+ ImageStream *imgStr;
+ Guchar pixBuf[4];
+ GfxRGB rgb;
+ int ncomps = 1;
+ int bits = 1;
+ unsigned char* maskbitmap = 0;
+
+ if(colorMap) {
+ ncomps = colorMap->getNumPixelComps();
+ bits = colorMap->getBits();
+ }
+
+ if(maskStr) {
+ int x,y;
+ unsigned char buf[8];
+ maskbitmap = (unsigned char*)malloc(maskHeight*maskWidth);
+ if(maskColorMap) {
+ ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits());
+ imgMaskStr->reset();
+ unsigned char pal[256];
+ int n = 1 << colorMap->getBits();
+ int t;
+ for(t=0;t<n;t++) {
+ GfxGray gray;
+ pixBuf[0] = t;
+ maskColorMap->getGray(pixBuf, &gray);
+ pal[t] = colToByte(gray);
+ }
+ for (y = 0; y < maskHeight; y++) {
+ for (x = 0; x < maskWidth; x++) {
+ imgMaskStr->getPixel(buf);
+ maskbitmap[y*maskWidth+x] = pal[buf[0]];
+ }
+ }
+ delete imgMaskStr;
+ } else {
+ ImageStream*imgMaskStr = new ImageStream(maskStr, maskWidth, 1, 1);
+ imgMaskStr->reset();
+ for (y = 0; y < maskHeight; y++) {
+ for (x = 0; x < maskWidth; x++) {
+ imgMaskStr->getPixel(buf);
+ buf[0]^=maskInvert;
+ maskbitmap[y*maskWidth+x] = (buf[0]^1)*255;
+ }
+ }
+ delete imgMaskStr;
+ }
+ maskStr->close();
+ }
+
+ imgStr = new ImageStream(str, width, ncomps,bits);
+ imgStr->reset();
+
+ if(!width || !height || (height<=1 && width<=1))
+ {
+ msg("<verbose> Ignoring %d by %d image", width, height);
+ unsigned char buf[8];
+ int x,y;
+ for (y = 0; y < height; ++y)
+ for (x = 0; x < width; ++x) {
+ imgStr->getPixel(buf);
+ }
+ delete imgStr;
+ if(maskbitmap)
+ free(maskbitmap);
+ return;
+ }
+
+ state->transform(0, 1, &x1, &y1); x1 += user_movex; y1 += user_movey;
+ state->transform(0, 0, &x2, &y2); x2 += user_movex; y2 += user_movey;
+ state->transform(1, 0, &x3, &y3); x3 += user_movex; y3 += user_movey;
+ state->transform(1, 1, &x4, &y4); x4 += user_movex; y4 += user_movey;
+
+
+ if(!pbminfo && !(str->getKind()==strDCT)) {
+ if(!type3active) {
+ msg("<notice> file contains pbm pictures %s",mask?"(masked)":"");
+ pbminfo = 1;
+ }
+ if(mask)
+ msg("<verbose> drawing %d by %d masked picture\n", width, height);
+ }
+ if(!jpeginfo && (str->getKind()==strDCT)) {
+ msg("<notice> file contains jpeg pictures");
+ jpeginfo = 1;
+ }
+
+ if(mask) {
+ int i,j;
+ unsigned char buf[8];
+ int x,y;
+ unsigned char*pic = new unsigned char[width*height];
+ gfxcolor_t pal[256];
+ GfxRGB rgb;
+ state->getFillRGB(&rgb);
+
+ memset(pal,255,sizeof(pal));
+ pal[0].r = (int)(colToByte(rgb.r)); pal[1].r = 0;
+ pal[0].g = (int)(colToByte(rgb.g)); pal[1].g = 0;
+ pal[0].b = (int)(colToByte(rgb.b)); pal[1].b = 0;
+ pal[0].a = 255; pal[1].a = 0;
+
+ int numpalette = 2;
+ int realwidth = (int)sqrt(SQR(x2-x3) + SQR(y2-y3));
+ int realheight = (int)sqrt(SQR(x1-x2) + SQR(y1-y2));
+ for (y = 0; y < height; ++y)
+ for (x = 0; x < width; ++x)
+ {
+ imgStr->getPixel(buf);
+ if(invert)
+ buf[0]=1-buf[0];
+ pic[width*y+x] = buf[0];
+ }
+
+ /* the size of the drawn image is added to the identifier
+ as the same image may require different bitmaps if displayed
+ at different sizes (due to antialiasing): */
+ int t,found = -1;
+ if(type3active) {
+ unsigned char*pic2 = 0;
+ numpalette = 16;
+
+ pic2 = antialize(pic,width,height,realwidth,realheight,numpalette);
+
+ if(!pic2) {
+ delete pic;
+ delete imgStr;
+ return;
+ }
+
+ width = realwidth;
+ height = realheight;
+ free(pic);
+ pic = pic2;
+
+ /* make a black/white palette */
+
+ float r = 255/(numpalette-1);
+ int t;
+ for(t=0;t<numpalette;t++) {
+ pal[t].r = colToByte(rgb.r);
+ pal[t].g = colToByte(rgb.g);
+ pal[t].b = colToByte(rgb.b);
+ pal[t].a = (unsigned char)(t*r);
+ }
+ }
+
+ gfxcolor_t*pic2 = new gfxcolor_t[width*height];
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; ++x) {
+ pic2[width*y+x] = pal[pic[y*width+x]];
+ }
+ }
+ drawimagelossless(device, pic2, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
+ free(pic2);
+ free(pic);
+ delete imgStr;
+ if(maskbitmap) free(maskbitmap);
+ return;
+ }
+
+ int x,y;
+
+ if(colorMap->getNumPixelComps()!=1 || str->getKind()==strDCT) {
+ gfxcolor_t*pic=new gfxcolor_t[width*height];
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; ++x) {
+ imgStr->getPixel(pixBuf);
+ colorMap->getRGB(pixBuf, &rgb);
+ pic[width*y+x].r = (unsigned char)(colToByte(rgb.r));
+ pic[width*y+x].g = (unsigned char)(colToByte(rgb.g));
+ pic[width*y+x].b = (unsigned char)(colToByte(rgb.b));
+ pic[width*y+x].a = 255;//(U8)(rgb.a * 255 + 0.5);
+ if(maskbitmap) {
+ pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
+ }
+ }
+ }
+ if(str->getKind()==strDCT)
+ drawimagejpeg(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
+ else
+ drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
+ delete pic;
+ delete imgStr;
+ if(maskbitmap) free(maskbitmap);
+ return;
+ } else {
+ gfxcolor_t*pic=new gfxcolor_t[width*height];
+ gfxcolor_t pal[256];
+ int n = 1 << colorMap->getBits();
+ int t;
+ for(t=0;t<256;t++) {
+ pixBuf[0] = t;
+ colorMap->getRGB(pixBuf, &rgb);
+
+ {/*if(maskColors && *maskColors==t) {
+ msg("<notice> Color %d is transparent", t);
+ if (imgData->maskColors) {
+ *alpha = 0;
+ for (i = 0; i < imgData->colorMap->getNumPixelComps(); ++i) {
+ if (pix[i] < imgData->maskColors[2*i] ||
+ pix[i] > imgData->maskColors[2*i+1]) {
+ *alpha = 1;
+ break;
+ }
+ }
+ } else {
+ *alpha = 1;
+ }
+ if(!*alpha) {
+ pal[t].r = 0;
+ pal[t].g = 0;
+ pal[t].b = 0;
+ pal[t].a = 0;
+ }
+ } else {*/
+ pal[t].r = (unsigned char)(colToByte(rgb.r));
+ pal[t].g = (unsigned char)(colToByte(rgb.g));
+ pal[t].b = (unsigned char)(colToByte(rgb.b));
+ pal[t].a = 255;//(U8)(rgb.b * 255 + 0.5);
+ }
+ }
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; ++x) {
+ imgStr->getPixel(pixBuf);
+ pic[width*y+x] = pal[pixBuf[0]];
+ if(maskbitmap) {
+ pic[width*y+x].a = maskbitmap[(y*maskHeight/height)*maskWidth+(x*maskWidth/width)];
+ }
+ }
+ }
+ drawimagelossless(device, pic, width, height, x1,y1,x2,y2,x3,y3,x4,y4);
+
+ delete pic;
+ delete imgStr;
+ if(maskbitmap) free(maskbitmap);
+ return;
+ }
+}
+
+void GFXOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GBool invert,
+ GBool inlineImg)
+{
+ if(states[statepos].textRender & 4) //clipped
+ return;
+ msg("<verbose> drawImageMask %dx%d, invert=%d inline=%d", width, height, invert, inlineImg);
+ drawGeneralImage(state,ref,str,width,height,0,invert,inlineImg,1, 0, 0,0,0,0, 0);
+}
+
+void GFXOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GfxImageColorMap *colorMap,
+ int *maskColors, GBool inlineImg)
+{
+ if(states[statepos].textRender & 4) //clipped
+ return;
+
+ msg("<verbose> drawImage %dx%d, %s, %s, inline=%d", width, height,
+ colorMap?"colorMap":"no colorMap",
+ maskColors?"maskColors":"no maskColors",
+ inlineImg);
+ if(colorMap)
+ msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
+ colorMap->getBits(),colorMap->getColorSpace()->getMode());
+ drawGeneralImage(state,ref,str,width,height,colorMap,0,inlineImg,0,maskColors, 0,0,0,0, 0);
+}
+
+void GFXOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Stream *maskStr, int maskWidth, int maskHeight,
+ GBool maskInvert)
+{
+ if(states[statepos].textRender & 4) //clipped
+ return;
+
+ msg("<verbose> drawMaskedImage %dx%d, %s, %dx%d mask", width, height,
+ colorMap?"colorMap":"no colorMap",
+ maskWidth, maskHeight);
+ if(colorMap)
+ msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
+ colorMap->getBits(),colorMap->getColorSpace()->getMode());
+ drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, maskInvert, 0);
+}
+
+void GFXOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GfxImageColorMap *maskColorMap)
+{
+ if(states[statepos].textRender & 4) //clipped
+ return;
+
+ msg("<verbose> drawSoftMaskedImage %dx%d, %s, %dx%d mask", width, height,
+ colorMap?"colorMap":"no colorMap",
+ maskWidth, maskHeight);
+ if(colorMap)
+ msg("<verbose> colorMap pixcomps:%d bits:%d mode:%d\n", colorMap->getNumPixelComps(),
+ colorMap->getBits(),colorMap->getColorSpace()->getMode());
+ drawGeneralImage(state,ref,str,width,height,colorMap,0,0,0,0, maskStr, maskWidth, maskHeight, 0, maskColorMap);
+}
+
+static char* dirseparator()
+{
+#ifdef WIN32
+ return "\\";
+#else
+ return "/";
+#endif
+}
+
+void addGlobalFont(char*filename)
+{
+ fontfile_t f;
+ memset(&f, 0, sizeof(fontfile_t));
+ f.filename = filename;
+ if(fontnum < sizeof(fonts)/sizeof(fonts[0])) {
+ msg("<verbose> Adding font \"%s\".", filename);
+ fonts[fontnum++] = f;
+ } else {
+ msg("<error> Too many external fonts. Not adding font file \"%s\".", filename);
+ }
+}
+
+void addGlobalLanguageDir(char*dir)
+{
+ if(!globalParams)
+ globalParams = new GlobalParams("");
+
+ msg("<notice> Adding %s to language pack directories", dir);
+
+ int l;
+ FILE*fi = 0;
+ char* config_file = (char*)malloc(strlen(dir) + 1 + sizeof("add-to-xpdfrc") + 1);
+ strcpy(config_file, dir);
+ strcat(config_file, dirseparator());
+ strcat(config_file, "add-to-xpdfrc");
+
+ fi = fopen(config_file, "rb");
+ if(!fi) {
+ msg("<error> Could not open %s", config_file);
+ return;
+ }
+ globalParams->parseFile(new GString(config_file), fi);
+ fclose(fi);
+}
+
+void addGlobalFontDir(char*dirname)
+{
+#ifdef HAVE_DIRENT_H
+ msg("<notice> Adding %s to font directories", dirname);
+ lastfontdir = strdup(dirname);
+ DIR*dir = opendir(dirname);
+ if(!dir) {
+ msg("<warning> Couldn't open directory %s\n", dirname);
+ return;
+ }
+ struct dirent*ent;
+ while(1) {
+ ent = readdir (dir);
+ if (!ent)
+ break;
+ int l;
+ char*name = ent->d_name;
+ char type = 0;
+ if(!name) continue;
+ l=strlen(name);
+ if(l<4)
+ continue;
+ if(!strncasecmp(&name[l-4], ".pfa", 4))
+ type=1;
+ if(!strncasecmp(&name[l-4], ".pfb", 4))
+ type=3;
+ if(!strncasecmp(&name[l-4], ".ttf", 4))
+ type=2;
+ if(type)
+ {
+ char*fontname = (char*)malloc(strlen(dirname)+strlen(name)+2);
+ strcpy(fontname, dirname);
+ strcat(fontname, dirseparator());
+ strcat(fontname, name);
+ addGlobalFont(fontname);
+ }
+ }
+ closedir(dir);
+#else
+ msg("<warning> No dirent.h- unable to add font dir %s", dir);
+#endif
+}
+
+void GFXOutputDev::preparePage(int pdfpage, int outputpage)
+{
+ if(pdfpage < 0)
+ return;
+
+ if(!this->pages) {
+ this->pagebuflen = 1024;
+ this->pages = (int*)malloc(this->pagebuflen*sizeof(int));
+ memset(this->pages, -1, this->pagebuflen*sizeof(int));
+ } else {
+ while(pdfpage >= this->pagebuflen)
+ {
+ int oldlen = this->pagebuflen;
+ this->pagebuflen+=1024;
+ this->pages = (int*)realloc(this->pages, this->pagebuflen*sizeof(int));
+ memset(&this->pages[oldlen], -1, (this->pagebuflen-oldlen)*sizeof(int));
+ }
+ }
+ this->pages[pdfpage] = outputpage;
+ if(pdfpage>this->pagepos)
+ this->pagepos = pdfpage;
+}
+
+/*class MemCheck
+{
+ public: ~MemCheck()
+ {
+ delete globalParams;globalParams=0;
+ Object::memCheck(stderr);
+ gMemReport(stderr);
+ }
+} myMemCheck;*/
+
--- /dev/null
+#ifndef __gfxoutputdev_h__
+#define __gfxoutputdev_h__
+
+#include "../gfxdevice.h"
+#include "../gfxsource.h"
+
+#include "InfoOutputDev.h"
+#include "PDFDoc.h"
+
+typedef struct _fontlist
+{
+ char*id;
+ char*filename;
+ gfxfont_t*font;
+ _fontlist*next;
+} fontlist_t;
+
+class GFXOutputState {
+ public:
+ int clipping;
+ int textRender;
+ GFXOutputState();
+};
+
+typedef struct _parameter
+{
+ char*name;
+ char*value;
+ struct _parameter*next;
+} parameter_t;
+
+void addGlobalFont(char*filename);
+void addGlobalLanguageDir(char*dir);
+void addGlobalFontDir(char*dirname);
+
+class GFXOutputDev: public OutputDev {
+public:
+ gfxdevice_t* device;
+
+ // Constructor.
+ GFXOutputDev::GFXOutputDev(parameter_t*p);
+ void setDevice(gfxdevice_t*dev);
+
+ // Destructor.
+ virtual ~GFXOutputDev() ;
+
+ void setMove(int x,int y);
+ void setClip(int x1,int y1,int x2,int y2);
+
+ void setInfo(InfoOutputDev*info) {this->info = info;}
+
+ // Start a page.
+ void startFrame(int width, int height);
+
+ virtual void startPage(int pageNum, GfxState *state, double x1, double y1, double x2, double y2) ;
+
+ void endframe();
+
+ //----- get info about output device
+
+ // Does this device use upside-down coordinates?
+ // (Upside-down means (0,0) is the top left corner of the page.)
+ virtual GBool upsideDown();
+
+ // Does this device use drawChar() or drawString()?
+ virtual GBool useDrawChar();
+
+ virtual GBool interpretType3Chars();
+
+ //virtual GBool useShadedFills() { return gTrue; }
+
+ //----- initialization and control
+
+ void setXRef(PDFDoc*doc, XRef *xref);
+
+ //----- link borders
+ virtual void drawLink(Link *link, Catalog *catalog) ;
+
+ //----- save/restore graphics state
+ virtual void saveState(GfxState *state) ;
+ virtual void restoreState(GfxState *state) ;
+
+ //----- update graphics state
+
+ virtual void updateFont(GfxState *state);
+ virtual void updateFillColor(GfxState *state);
+ virtual void updateStrokeColor(GfxState *state);
+ virtual void updateLineWidth(GfxState *state);
+ virtual void updateLineJoin(GfxState *state);
+ virtual void updateLineCap(GfxState *state);
+
+ virtual void updateAll(GfxState *state)
+ {
+ updateFont(state);
+ updateFillColor(state);
+ updateStrokeColor(state);
+ updateLineWidth(state);
+ updateLineJoin(state);
+ updateLineCap(state);
+ };
+
+ //----- path painting
+ virtual void stroke(GfxState *state) ;
+ virtual void fill(GfxState *state) ;
+ virtual void eoFill(GfxState *state) ;
+
+ //----- path clipping
+ virtual void clip(GfxState *state) ;
+ virtual void eoClip(GfxState *state) ;
+
+ //----- text drawing
+ virtual void beginString(GfxState *state, GString *s) ;
+ virtual void endString(GfxState *state) ;
+ virtual void endTextObject(GfxState *state);
+ virtual void drawChar(GfxState *state, double x, double y,
+ double dx, double dy,
+ double originX, double originY,
+ CharCode code, int nBytes, Unicode *u, int uLen);
+
+ //----- image drawing
+ virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GBool invert,
+ GBool inlineImg);
+ virtual void drawImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GfxImageColorMap *colorMap,
+ int *maskColors, GBool inlineImg);
+ virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Stream *maskStr, int maskWidth, int maskHeight,
+ GBool maskInvert);
+ virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GfxImageColorMap *maskColorMap);
+
+ virtual GBool beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen);
+ virtual void endType3Char(GfxState *state);
+
+ virtual void type3D0(GfxState *state, double wx, double wy);
+ virtual void type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury);
+
+ void preparePage(int pdfpage, int outputpage);
+
+ char* searchForSuitableFont(GfxFont*gfxFont);
+
+ void finish();
+
+ private:
+ void drawGeneralImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GfxImageColorMap*colorMap, GBool invert,
+ GBool inlineImg, int mask, int *maskColors,
+ Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GfxImageColorMap*maskColorMap);
+ int setGfxFont(char*id, char*name, char*filename, double quality);
+ void strokeGfxline(GfxState *state, gfxline_t*line);
+ void clipToGfxLine(GfxState *state, gfxline_t*line);
+ void fillGfxLine(GfxState *state, gfxline_t*line);
+
+ char outer_clip_box; //whether the page clip box is still on
+
+ InfoOutputDev*info;
+ GFXOutputState states[64];
+ int statepos;
+
+ int currentpage;
+
+ PDFDoc*doc;
+ XRef*xref;
+
+ char* searchFont(char*name);
+ char* substituteFont(GfxFont*gfxFont, char*oldname);
+ char* writeEmbeddedFontToFile(XRef*ref, GfxFont*font);
+ int t1id;
+ int textmodeinfo; // did we write "Text will be rendered as polygon" yet?
+ int jpeginfo; // did we write "File contains jpegs" yet?
+ int pbminfo; // did we write "File contains jpegs" yet?
+ int linkinfo; // did we write "File contains links" yet?
+ int ttfinfo; // did we write "File contains TrueType Fonts" yet?
+ int gradientinfo; // did we write "File contains Gradients yet?
+
+ int type3active; // are we between beginType3()/endType3()?
+
+ GfxState *laststate;
+
+ char type3Warning;
+
+ char* substitutetarget[256];
+ char* substitutesource[256];
+ int substitutepos;
+
+ int user_movex,user_movey;
+ int user_clipx1,user_clipx2,user_clipy1,user_clipy2;
+
+ gfxline_t* current_text_stroke;
+ gfxline_t* current_text_clip;
+ char* current_font_id;
+ gfxfont_t* current_gfxfont;
+ gfxmatrix_t current_font_matrix;
+
+ fontlist_t* fontlist;
+
+ int*pages;
+ int pagebuflen;
+ int pagepos;
+
+ /* config */
+ int forceType0Fonts;
+ int config_use_fontconfig;
+};
+
+#endif //__gfxoutputdev_h__
--- /dev/null
+//========================================================================
+//
+// GHash.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "gmem.h"
+#include "GString.h"
+#include "GHash.h"
+
+//------------------------------------------------------------------------
+
+struct GHashBucket {
+ GString *key;
+ union {
+ void *p;
+ int i;
+ } val;
+ GHashBucket *next;
+};
+
+struct GHashIter {
+ int h;
+ GHashBucket *p;
+};
+
+//------------------------------------------------------------------------
+
+GHash::GHash(GBool deleteKeysA) {
+ int h;
+
+ deleteKeys = deleteKeysA;
+ size = 7;
+ tab = (GHashBucket **)gmallocn(size, sizeof(GHashBucket *));
+ for (h = 0; h < size; ++h) {
+ tab[h] = NULL;
+ }
+ len = 0;
+}
+
+GHash::~GHash() {
+ GHashBucket *p;
+ int h;
+
+ for (h = 0; h < size; ++h) {
+ while (tab[h]) {
+ p = tab[h];
+ tab[h] = p->next;
+ if (deleteKeys) {
+ delete p->key;
+ }
+ delete p;
+ }
+ }
+ gfree(tab);
+}
+
+void GHash::add(GString *key, void *val) {
+ GHashBucket *p;
+ int h;
+
+ // expand the table if necessary
+ if (len >= size) {
+ expand();
+ }
+
+ // add the new symbol
+ p = new GHashBucket;
+ p->key = key;
+ p->val.p = val;
+ h = hash(key);
+ p->next = tab[h];
+ tab[h] = p;
+ ++len;
+}
+
+void GHash::add(GString *key, int val) {
+ GHashBucket *p;
+ int h;
+
+ // expand the table if necessary
+ if (len >= size) {
+ expand();
+ }
+
+ // add the new symbol
+ p = new GHashBucket;
+ p->key = key;
+ p->val.i = val;
+ h = hash(key);
+ p->next = tab[h];
+ tab[h] = p;
+ ++len;
+}
+
+void GHash::replace(GString *key, void *val) {
+ GHashBucket *p;
+ int h;
+
+ if ((p = find(key, &h))) {
+ p->val.p = val;
+ delete key;
+ } else {
+ add(key, val);
+ }
+}
+
+void GHash::replace(GString *key, int val) {
+ GHashBucket *p;
+ int h;
+
+ if ((p = find(key, &h))) {
+ p->val.i = val;
+ delete key;
+ } else {
+ add(key, val);
+ }
+}
+
+void *GHash::lookup(GString *key) {
+ GHashBucket *p;
+ int h;
+
+ if (!(p = find(key, &h))) {
+ return NULL;
+ }
+ return p->val.p;
+}
+
+int GHash::lookupInt(GString *key) {
+ GHashBucket *p;
+ int h;
+
+ if (!(p = find(key, &h))) {
+ return 0;
+ }
+ return p->val.i;
+}
+
+void *GHash::lookup(char *key) {
+ GHashBucket *p;
+ int h;
+
+ if (!(p = find(key, &h))) {
+ return NULL;
+ }
+ return p->val.p;
+}
+
+int GHash::lookupInt(char *key) {
+ GHashBucket *p;
+ int h;
+
+ if (!(p = find(key, &h))) {
+ return 0;
+ }
+ return p->val.i;
+}
+
+void *GHash::remove(GString *key) {
+ GHashBucket *p;
+ GHashBucket **q;
+ void *val;
+ int h;
+
+ if (!(p = find(key, &h))) {
+ return NULL;
+ }
+ q = &tab[h];
+ while (*q != p) {
+ q = &((*q)->next);
+ }
+ *q = p->next;
+ if (deleteKeys) {
+ delete p->key;
+ }
+ val = p->val.p;
+ delete p;
+ --len;
+ return val;
+}
+
+int GHash::removeInt(GString *key) {
+ GHashBucket *p;
+ GHashBucket **q;
+ int val;
+ int h;
+
+ if (!(p = find(key, &h))) {
+ return 0;
+ }
+ q = &tab[h];
+ while (*q != p) {
+ q = &((*q)->next);
+ }
+ *q = p->next;
+ if (deleteKeys) {
+ delete p->key;
+ }
+ val = p->val.i;
+ delete p;
+ --len;
+ return val;
+}
+
+void *GHash::remove(char *key) {
+ GHashBucket *p;
+ GHashBucket **q;
+ void *val;
+ int h;
+
+ if (!(p = find(key, &h))) {
+ return NULL;
+ }
+ q = &tab[h];
+ while (*q != p) {
+ q = &((*q)->next);
+ }
+ *q = p->next;
+ if (deleteKeys) {
+ delete p->key;
+ }
+ val = p->val.p;
+ delete p;
+ --len;
+ return val;
+}
+
+int GHash::removeInt(char *key) {
+ GHashBucket *p;
+ GHashBucket **q;
+ int val;
+ int h;
+
+ if (!(p = find(key, &h))) {
+ return 0;
+ }
+ q = &tab[h];
+ while (*q != p) {
+ q = &((*q)->next);
+ }
+ *q = p->next;
+ if (deleteKeys) {
+ delete p->key;
+ }
+ val = p->val.i;
+ delete p;
+ --len;
+ return val;
+}
+
+void GHash::startIter(GHashIter **iter) {
+ *iter = new GHashIter;
+ (*iter)->h = -1;
+ (*iter)->p = NULL;
+}
+
+GBool GHash::getNext(GHashIter **iter, GString **key, void **val) {
+ if (!*iter) {
+ return gFalse;
+ }
+ if ((*iter)->p) {
+ (*iter)->p = (*iter)->p->next;
+ }
+ while (!(*iter)->p) {
+ if (++(*iter)->h == size) {
+ delete *iter;
+ *iter = NULL;
+ return gFalse;
+ }
+ (*iter)->p = tab[(*iter)->h];
+ }
+ *key = (*iter)->p->key;
+ *val = (*iter)->p->val.p;
+ return gTrue;
+}
+
+GBool GHash::getNext(GHashIter **iter, GString **key, int *val) {
+ if (!*iter) {
+ return gFalse;
+ }
+ if ((*iter)->p) {
+ (*iter)->p = (*iter)->p->next;
+ }
+ while (!(*iter)->p) {
+ if (++(*iter)->h == size) {
+ delete *iter;
+ *iter = NULL;
+ return gFalse;
+ }
+ (*iter)->p = tab[(*iter)->h];
+ }
+ *key = (*iter)->p->key;
+ *val = (*iter)->p->val.i;
+ return gTrue;
+}
+
+void GHash::killIter(GHashIter **iter) {
+ delete *iter;
+ *iter = NULL;
+}
+
+void GHash::expand() {
+ GHashBucket **oldTab;
+ GHashBucket *p;
+ int oldSize, h, i;
+
+ oldSize = size;
+ oldTab = tab;
+ size = 2*size + 1;
+ tab = (GHashBucket **)gmallocn(size, sizeof(GHashBucket *));
+ for (h = 0; h < size; ++h) {
+ tab[h] = NULL;
+ }
+ for (i = 0; i < oldSize; ++i) {
+ while (oldTab[i]) {
+ p = oldTab[i];
+ oldTab[i] = oldTab[i]->next;
+ h = hash(p->key);
+ p->next = tab[h];
+ tab[h] = p;
+ }
+ }
+ gfree(oldTab);
+}
+
+GHashBucket *GHash::find(GString *key, int *h) {
+ GHashBucket *p;
+
+ *h = hash(key);
+ for (p = tab[*h]; p; p = p->next) {
+ if (!p->key->cmp(key)) {
+ return p;
+ }
+ }
+ return NULL;
+}
+
+GHashBucket *GHash::find(char *key, int *h) {
+ GHashBucket *p;
+
+ *h = hash(key);
+ for (p = tab[*h]; p; p = p->next) {
+ if (!p->key->cmp(key)) {
+ return p;
+ }
+ }
+ return NULL;
+}
+
+int GHash::hash(GString *key) {
+ char *p;
+ unsigned int h;
+ int i;
+
+ h = 0;
+ for (p = key->getCString(), i = 0; i < key->getLength(); ++p, ++i) {
+ h = 17 * h + (int)(*p & 0xff);
+ }
+ return (int)(h % size);
+}
+
+int GHash::hash(char *key) {
+ char *p;
+ unsigned int h;
+
+ h = 0;
+ for (p = key; *p; ++p) {
+ h = 17 * h + (int)(*p & 0xff);
+ }
+ return (int)(h % size);
+}
--- /dev/null
+//========================================================================
+//
+// GHash.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GHASH_H
+#define GHASH_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+class GString;
+struct GHashBucket;
+struct GHashIter;
+
+//------------------------------------------------------------------------
+
+class GHash {
+public:
+
+ GHash(GBool deleteKeysA = gFalse);
+ ~GHash();
+ void add(GString *key, void *val);
+ void add(GString *key, int val);
+ void replace(GString *key, void *val);
+ void replace(GString *key, int val);
+ void *lookup(GString *key);
+ int lookupInt(GString *key);
+ void *lookup(char *key);
+ int lookupInt(char *key);
+ void *remove(GString *key);
+ int removeInt(GString *key);
+ void *remove(char *key);
+ int removeInt(char *key);
+ int getLength() { return len; }
+ void startIter(GHashIter **iter);
+ GBool getNext(GHashIter **iter, GString **key, void **val);
+ GBool getNext(GHashIter **iter, GString **key, int *val);
+ void killIter(GHashIter **iter);
+
+private:
+
+ void expand();
+ GHashBucket *find(GString *key, int *h);
+ GHashBucket *find(char *key, int *h);
+ int hash(GString *key);
+ int hash(char *key);
+
+ GBool deleteKeys; // set if key strings should be deleted
+ int size; // number of buckets
+ int len; // number of entries
+ GHashBucket **tab;
+};
+
+#define deleteGHash(hash, T) \
+ do { \
+ GHash *_hash = (hash); \
+ { \
+ GHashIter *_iter; \
+ GString *_key; \
+ void *_p; \
+ _hash->startIter(&_iter); \
+ while (_hash->getNext(&_iter, &_key, &_p)) { \
+ delete (T*)_p; \
+ } \
+ delete _hash; \
+ } \
+ } while(0)
+
+#endif
--- /dev/null
+//========================================================================
+//
+// GList.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "gmem.h"
+#include "GList.h"
+
+//------------------------------------------------------------------------
+// GList
+//------------------------------------------------------------------------
+
+GList::GList() {
+ size = 8;
+ data = (void **)gmallocn(size, sizeof(void*));
+ length = 0;
+ inc = 0;
+}
+
+GList::GList(int sizeA) {
+ size = sizeA;
+ data = (void **)gmallocn(size, sizeof(void*));
+ length = 0;
+ inc = 0;
+}
+
+GList::~GList() {
+ gfree(data);
+}
+
+void GList::append(void *p) {
+ if (length >= size) {
+ expand();
+ }
+ data[length++] = p;
+}
+
+void GList::append(GList *list) {
+ int i;
+
+ while (length + list->length > size) {
+ expand();
+ }
+ for (i = 0; i < list->length; ++i) {
+ data[length++] = list->data[i];
+ }
+}
+
+void GList::insert(int i, void *p) {
+ if (length >= size) {
+ expand();
+ }
+ if (i < length) {
+ memmove(data+i+1, data+i, (length - i) * sizeof(void *));
+ }
+ data[i] = p;
+ ++length;
+}
+
+void *GList::del(int i) {
+ void *p;
+
+ p = data[i];
+ if (i < length - 1) {
+ memmove(data+i, data+i+1, (length - i - 1) * sizeof(void *));
+ }
+ --length;
+ if (size - length >= ((inc > 0) ? inc : size/2)) {
+ shrink();
+ }
+ return p;
+}
+
+void GList::sort(int (*cmp)(const void *obj1, const void *obj2)) {
+ qsort(data, length, sizeof(void *), cmp);
+}
+
+void GList::expand() {
+ size += (inc > 0) ? inc : size;
+ data = (void **)greallocn(data, size, sizeof(void*));
+}
+
+void GList::shrink() {
+ size -= (inc > 0) ? inc : size/2;
+ data = (void **)greallocn(data, size, sizeof(void*));
+}
--- /dev/null
+//========================================================================
+//
+// GList.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GLIST_H
+#define GLIST_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+//------------------------------------------------------------------------
+// GList
+//------------------------------------------------------------------------
+
+class GList {
+public:
+
+ // Create an empty list.
+ GList();
+
+ // Create an empty list with space for <size1> elements.
+ GList(int sizeA);
+
+ // Destructor - does not free pointed-to objects.
+ ~GList();
+
+ //----- general
+
+ // Get the number of elements.
+ int getLength() { return length; }
+
+ //----- ordered list support
+
+ // Return the <i>th element.
+ // Assumes 0 <= i < length.
+ void *get(int i) { return data[i]; }
+
+ // Append an element to the end of the list.
+ void append(void *p);
+
+ // Append another list to the end of this one.
+ void append(GList *list);
+
+ // Insert an element at index <i>.
+ // Assumes 0 <= i <= length.
+ void insert(int i, void *p);
+
+ // Deletes and returns the element at index <i>.
+ // Assumes 0 <= i < length.
+ void *del(int i);
+
+ // Sort the list accoring to the given comparison function.
+ // NB: this sorts an array of pointers, so the pointer args need to
+ // be double-dereferenced.
+ void sort(int (*cmp)(const void *ptr1, const void *ptr2));
+
+ //----- control
+
+ // Set allocation increment to <inc>. If inc > 0, that many
+ // elements will be allocated every time the list is expanded.
+ // If inc <= 0, the list will be doubled in size.
+ void setAllocIncr(int incA) { inc = incA; }
+
+private:
+
+ void expand();
+ void shrink();
+
+ void **data; // the list elements
+ int size; // size of data array
+ int length; // number of elements on list
+ int inc; // allocation increment
+};
+
+#define deleteGList(list, T) \
+ do { \
+ GList *_list = (list); \
+ { \
+ int _i; \
+ for (_i = 0; _i < _list->getLength(); ++_i) { \
+ delete (T*)_list->get(_i); \
+ } \
+ delete _list; \
+ } \
+ } while (0)
+
+#endif
--- /dev/null
+//========================================================================
+//
+// GMutex.h
+//
+// Portable mutex macros.
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GMUTEX_H
+#define GMUTEX_H
+
+// Usage:
+//
+// GMutex m;
+// gInitMutex(&m);
+// ...
+// gLockMutex(&m);
+// ... critical section ...
+// gUnlockMutex(&m);
+// ...
+// gDestroyMutex(&m);
+
+#ifdef WIN32
+
+#include <windows.h>
+
+typedef CRITICAL_SECTION GMutex;
+
+#define gInitMutex(m) InitializeCriticalSection(m)
+#define gDestroyMutex(m) DeleteCriticalSection(m)
+#define gLockMutex(m) EnterCriticalSection(m)
+#define gUnlockMutex(m) LeaveCriticalSection(m)
+
+#else // assume pthreads
+
+#include <pthread.h>
+
+typedef pthread_mutex_t GMutex;
+
+#define gInitMutex(m) pthread_mutex_init(m, NULL)
+#define gDestroyMutex(m) pthread_mutex_destroy(m)
+#define gLockMutex(m) pthread_mutex_lock(m)
+#define gUnlockMutex(m) pthread_mutex_unlock(m)
+
+#endif
+
+#endif
--- /dev/null
+//========================================================================
+//
+// GString.cc
+//
+// Simple variable-length string type.
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include "gtypes.h"
+#include "GString.h"
+
+static inline int size(int len) {
+ int delta;
+
+ delta = len < 256 ? 7 : 255;
+ return ((len + 1) + delta) & ~delta;
+}
+
+inline void GString::resize(int length1) {
+ char *s1;
+
+ if (!s) {
+ s = new char[size(length1)];
+ } else if (size(length1) != size(length)) {
+ s1 = new char[size(length1)];
+ if (length1 < length) {
+ memcpy(s1, s, length1);
+ s1[length1] = '\0';
+ } else {
+ memcpy(s1, s, length + 1);
+ }
+ delete[] s;
+ s = s1;
+ }
+}
+
+GString::GString() {
+ s = NULL;
+ resize(length = 0);
+ s[0] = '\0';
+}
+
+GString::GString(const char *sA) {
+ int n = strlen(sA);
+
+ s = NULL;
+ resize(length = n);
+ memcpy(s, sA, n + 1);
+}
+
+GString::GString(const char *sA, int lengthA) {
+ s = NULL;
+ resize(length = lengthA);
+ memcpy(s, sA, length * sizeof(char));
+ s[length] = '\0';
+}
+
+GString::GString(GString *str, int idx, int lengthA) {
+ s = NULL;
+ resize(length = lengthA);
+ memcpy(s, str->getCString() + idx, length);
+ s[length] = '\0';
+}
+
+GString::GString(GString *str) {
+ s = NULL;
+ resize(length = str->getLength());
+ memcpy(s, str->getCString(), length + 1);
+}
+
+GString::GString(GString *str1, GString *str2) {
+ int n1 = str1->getLength();
+ int n2 = str2->getLength();
+
+ s = NULL;
+ resize(length = n1 + n2);
+ memcpy(s, str1->getCString(), n1);
+ memcpy(s + n1, str2->getCString(), n2 + 1);
+}
+
+GString *GString::fromInt(int x) {
+ char buf[24]; // enough space for 64-bit ints plus a little extra
+ GBool neg;
+ Guint y;
+ int i;
+
+ i = 24;
+ if (x == 0) {
+ buf[--i] = '0';
+ } else {
+ if ((neg = x < 0)) {
+ y = (Guint)-x;
+ } else {
+ y = (Guint)x;
+ }
+ while (i > 0 && y > 0) {
+ buf[--i] = '0' + y % 10;
+ y /= 10;
+ }
+ if (neg && i > 0) {
+ buf[--i] = '-';
+ }
+ }
+ return new GString(buf + i, 24 - i);
+}
+
+GString::~GString() {
+ delete[] s;
+}
+
+GString *GString::clear() {
+ s[length = 0] = '\0';
+ resize(0);
+ return this;
+}
+
+GString *GString::append(char c) {
+ resize(length + 1);
+ s[length++] = c;
+ s[length] = '\0';
+ return this;
+}
+
+GString *GString::append(GString *str) {
+ int n = str->getLength();
+
+ resize(length + n);
+ memcpy(s + length, str->getCString(), n + 1);
+ length += n;
+ return this;
+}
+
+GString *GString::append(const char *str) {
+ int n = strlen(str);
+
+ resize(length + n);
+ memcpy(s + length, str, n + 1);
+ length += n;
+ return this;
+}
+
+GString *GString::append(const char *str, int lengthA) {
+ resize(length + lengthA);
+ memcpy(s + length, str, lengthA);
+ length += lengthA;
+ s[length] = '\0';
+ return this;
+}
+
+GString *GString::insert(int i, char c) {
+ int j;
+
+ resize(length + 1);
+ for (j = length + 1; j > i; --j)
+ s[j] = s[j-1];
+ s[i] = c;
+ ++length;
+ return this;
+}
+
+GString *GString::insert(int i, GString *str) {
+ int n = str->getLength();
+ int j;
+
+ resize(length + n);
+ for (j = length; j >= i; --j)
+ s[j+n] = s[j];
+ memcpy(s+i, str->getCString(), n);
+ length += n;
+ return this;
+}
+
+GString *GString::insert(int i, const char *str) {
+ int n = strlen(str);
+ int j;
+
+ resize(length + n);
+ for (j = length; j >= i; --j)
+ s[j+n] = s[j];
+ memcpy(s+i, str, n);
+ length += n;
+ return this;
+}
+
+GString *GString::insert(int i, const char *str, int lengthA) {
+ int j;
+
+ resize(length + lengthA);
+ for (j = length; j >= i; --j)
+ s[j+lengthA] = s[j];
+ memcpy(s+i, str, lengthA);
+ length += lengthA;
+ return this;
+}
+
+GString *GString::del(int i, int n) {
+ int j;
+
+ if (n > 0) {
+ if (i + n > length) {
+ n = length - i;
+ }
+ for (j = i; j <= length - n; ++j) {
+ s[j] = s[j + n];
+ }
+ resize(length -= n);
+ }
+ return this;
+}
+
+GString *GString::upperCase() {
+ int i;
+
+ for (i = 0; i < length; ++i) {
+ if (islower(s[i]))
+ s[i] = toupper(s[i]);
+ }
+ return this;
+}
+
+GString *GString::lowerCase() {
+ int i;
+
+ for (i = 0; i < length; ++i) {
+ if (isupper(s[i]))
+ s[i] = tolower(s[i]);
+ }
+ return this;
+}
+
+int GString::cmp(GString *str) {
+ int n1, n2, i, x;
+ char *p1, *p2;
+
+ n1 = length;
+ n2 = str->length;
+ for (i = 0, p1 = s, p2 = str->s; i < n1 && i < n2; ++i, ++p1, ++p2) {
+ x = *p1 - *p2;
+ if (x != 0) {
+ return x;
+ }
+ }
+ return n1 - n2;
+}
+
+int GString::cmpN(GString *str, int n) {
+ int n1, n2, i, x;
+ char *p1, *p2;
+
+ n1 = length;
+ n2 = str->length;
+ for (i = 0, p1 = s, p2 = str->s;
+ i < n1 && i < n2 && i < n;
+ ++i, ++p1, ++p2) {
+ x = *p1 - *p2;
+ if (x != 0) {
+ return x;
+ }
+ }
+ if (i == n) {
+ return 0;
+ }
+ return n1 - n2;
+}
+
+int GString::cmp(const char *sA) {
+ int n1, i, x;
+ const char *p1, *p2;
+
+ n1 = length;
+ for (i = 0, p1 = s, p2 = sA; i < n1 && *p2; ++i, ++p1, ++p2) {
+ x = *p1 - *p2;
+ if (x != 0) {
+ return x;
+ }
+ }
+ if (i < n1) {
+ return 1;
+ }
+ if (*p2) {
+ return -1;
+ }
+ return 0;
+}
+
+int GString::cmpN(const char *sA, int n) {
+ int n1, i, x;
+ const char *p1, *p2;
+
+ n1 = length;
+ for (i = 0, p1 = s, p2 = sA; i < n1 && *p2 && i < n; ++i, ++p1, ++p2) {
+ x = *p1 - *p2;
+ if (x != 0) {
+ return x;
+ }
+ }
+ if (i == n) {
+ return 0;
+ }
+ if (i < n1) {
+ return 1;
+ }
+ if (*p2) {
+ return -1;
+ }
+ return 0;
+}
--- /dev/null
+//========================================================================
+//
+// GString.h
+//
+// Simple variable-length string type.
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GSTRING_H
+#define GSTRING_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+class GString {
+public:
+
+ // Create an empty string.
+ GString();
+
+ // Create a string from a C string.
+ GString(const char *sA);
+
+ // Create a string from <lengthA> chars at <sA>. This string
+ // can contain null characters.
+ GString(const char *sA, int lengthA);
+
+ // Create a string from <lengthA> chars at <idx> in <str>.
+ GString(GString *str, int idx, int lengthA);
+
+ // Copy a string.
+ GString(GString *str);
+ GString *copy() { return new GString(this); }
+
+ // Concatenate two strings.
+ GString(GString *str1, GString *str2);
+
+ // Convert an integer to a string.
+ static GString *fromInt(int x);
+
+ // Destructor.
+ ~GString();
+
+ // Get length.
+ int getLength() { return length; }
+
+ // Get C string.
+ char *getCString() { return s; }
+
+ // Get <i>th character.
+ char getChar(int i) { return s[i]; }
+
+ // Change <i>th character.
+ void setChar(int i, char c) { s[i] = c; }
+
+ // Clear string to zero length.
+ GString *clear();
+
+ // Append a character or string.
+ GString *append(char c);
+ GString *append(GString *str);
+ GString *append(const char *str);
+ GString *append(const char *str, int lengthA);
+
+ // Insert a character or string.
+ GString *insert(int i, char c);
+ GString *insert(int i, GString *str);
+ GString *insert(int i, const char *str);
+ GString *insert(int i, const char *str, int lengthA);
+
+ // Delete a character or range of characters.
+ GString *del(int i, int n = 1);
+
+ // Convert string to all-upper/all-lower case.
+ GString *upperCase();
+ GString *lowerCase();
+
+ // Compare two strings: -1:< 0:= +1:>
+ int cmp(GString *str);
+ int cmpN(GString *str, int n);
+ int cmp(const char *sA);
+ int cmpN(const char *sA, int n);
+
+private:
+
+ int length;
+ char *s;
+
+ void resize(int length1);
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// Gfx.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <string.h>
+#include <math.h>
+#include "gmem.h"
+#include "GlobalParams.h"
+#include "CharTypes.h"
+#include "Object.h"
+#include "Array.h"
+#include "Dict.h"
+#include "Stream.h"
+#include "Lexer.h"
+#include "Parser.h"
+#include "GfxFont.h"
+#include "GfxState.h"
+#include "OutputDev.h"
+#include "Page.h"
+#include "Error.h"
+#include "Gfx.h"
+
+// the MSVC math.h doesn't define this
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+//------------------------------------------------------------------------
+// constants
+//------------------------------------------------------------------------
+
+// Max recursive depth for a function shading fill.
+#define functionMaxDepth 6
+
+// Max delta allowed in any color component for a function shading fill.
+#define functionColorDelta (dblToCol(1 / 256.0))
+
+// Max number of splits along the t axis for an axial shading fill.
+#define axialMaxSplits 256
+
+// Max delta allowed in any color component for an axial shading fill.
+#define axialColorDelta (dblToCol(1 / 256.0))
+
+// Max number of splits along the t axis for a radial shading fill.
+#define radialMaxSplits 256
+
+// Max delta allowed in any color component for a radial shading fill.
+#define radialColorDelta (dblToCol(1 / 256.0))
+
+// Max recursive depth for a Gouraud triangle shading fill.
+#define gouraudMaxDepth 4
+
+// Max delta allowed in any color component for a Gouraud triangle
+// shading fill.
+#define gouraudColorDelta (dblToCol(1 / 256.0))
+
+// Max recursive depth for a patch mesh shading fill.
+#define patchMaxDepth 6
+
+// Max delta allowed in any color component for a patch mesh shading
+// fill.
+#define patchColorDelta (dblToCol(1 / 256.0))
+
+//------------------------------------------------------------------------
+// Operator table
+//------------------------------------------------------------------------
+
+#ifdef WIN32 // this works around a bug in the VC7 compiler
+# pragma optimize("",off)
+#endif
+
+Operator Gfx::opTab[] = {
+ {"\"", 3, {tchkNum, tchkNum, tchkString},
+ &Gfx::opMoveSetShowText},
+ {"'", 1, {tchkString},
+ &Gfx::opMoveShowText},
+ {"B", 0, {tchkNone},
+ &Gfx::opFillStroke},
+ {"B*", 0, {tchkNone},
+ &Gfx::opEOFillStroke},
+ {"BDC", 2, {tchkName, tchkProps},
+ &Gfx::opBeginMarkedContent},
+ {"BI", 0, {tchkNone},
+ &Gfx::opBeginImage},
+ {"BMC", 1, {tchkName},
+ &Gfx::opBeginMarkedContent},
+ {"BT", 0, {tchkNone},
+ &Gfx::opBeginText},
+ {"BX", 0, {tchkNone},
+ &Gfx::opBeginIgnoreUndef},
+ {"CS", 1, {tchkName},
+ &Gfx::opSetStrokeColorSpace},
+ {"DP", 2, {tchkName, tchkProps},
+ &Gfx::opMarkPoint},
+ {"Do", 1, {tchkName},
+ &Gfx::opXObject},
+ {"EI", 0, {tchkNone},
+ &Gfx::opEndImage},
+ {"EMC", 0, {tchkNone},
+ &Gfx::opEndMarkedContent},
+ {"ET", 0, {tchkNone},
+ &Gfx::opEndText},
+ {"EX", 0, {tchkNone},
+ &Gfx::opEndIgnoreUndef},
+ {"F", 0, {tchkNone},
+ &Gfx::opFill},
+ {"G", 1, {tchkNum},
+ &Gfx::opSetStrokeGray},
+ {"ID", 0, {tchkNone},
+ &Gfx::opImageData},
+ {"J", 1, {tchkInt},
+ &Gfx::opSetLineCap},
+ {"K", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
+ &Gfx::opSetStrokeCMYKColor},
+ {"M", 1, {tchkNum},
+ &Gfx::opSetMiterLimit},
+ {"MP", 1, {tchkName},
+ &Gfx::opMarkPoint},
+ {"Q", 0, {tchkNone},
+ &Gfx::opRestore},
+ {"RG", 3, {tchkNum, tchkNum, tchkNum},
+ &Gfx::opSetStrokeRGBColor},
+ {"S", 0, {tchkNone},
+ &Gfx::opStroke},
+ {"SC", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
+ &Gfx::opSetStrokeColor},
+ {"SCN", -5, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
+ tchkSCN},
+ &Gfx::opSetStrokeColorN},
+ {"T*", 0, {tchkNone},
+ &Gfx::opTextNextLine},
+ {"TD", 2, {tchkNum, tchkNum},
+ &Gfx::opTextMoveSet},
+ {"TJ", 1, {tchkArray},
+ &Gfx::opShowSpaceText},
+ {"TL", 1, {tchkNum},
+ &Gfx::opSetTextLeading},
+ {"Tc", 1, {tchkNum},
+ &Gfx::opSetCharSpacing},
+ {"Td", 2, {tchkNum, tchkNum},
+ &Gfx::opTextMove},
+ {"Tf", 2, {tchkName, tchkNum},
+ &Gfx::opSetFont},
+ {"Tj", 1, {tchkString},
+ &Gfx::opShowText},
+ {"Tm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum, tchkNum},
+ &Gfx::opSetTextMatrix},
+ {"Tr", 1, {tchkInt},
+ &Gfx::opSetTextRender},
+ {"Ts", 1, {tchkNum},
+ &Gfx::opSetTextRise},
+ {"Tw", 1, {tchkNum},
+ &Gfx::opSetWordSpacing},
+ {"Tz", 1, {tchkNum},
+ &Gfx::opSetHorizScaling},
+ {"W", 0, {tchkNone},
+ &Gfx::opClip},
+ {"W*", 0, {tchkNone},
+ &Gfx::opEOClip},
+ {"b", 0, {tchkNone},
+ &Gfx::opCloseFillStroke},
+ {"b*", 0, {tchkNone},
+ &Gfx::opCloseEOFillStroke},
+ {"c", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum, tchkNum},
+ &Gfx::opCurveTo},
+ {"cm", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum, tchkNum},
+ &Gfx::opConcat},
+ {"cs", 1, {tchkName},
+ &Gfx::opSetFillColorSpace},
+ {"d", 2, {tchkArray, tchkNum},
+ &Gfx::opSetDash},
+ {"d0", 2, {tchkNum, tchkNum},
+ &Gfx::opSetCharWidth},
+ {"d1", 6, {tchkNum, tchkNum, tchkNum, tchkNum,
+ tchkNum, tchkNum},
+ &Gfx::opSetCacheDevice},
+ {"f", 0, {tchkNone},
+ &Gfx::opFill},
+ {"f*", 0, {tchkNone},
+ &Gfx::opEOFill},
+ {"g", 1, {tchkNum},
+ &Gfx::opSetFillGray},
+ {"gs", 1, {tchkName},
+ &Gfx::opSetExtGState},
+ {"h", 0, {tchkNone},
+ &Gfx::opClosePath},
+ {"i", 1, {tchkNum},
+ &Gfx::opSetFlat},
+ {"j", 1, {tchkInt},
+ &Gfx::opSetLineJoin},
+ {"k", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
+ &Gfx::opSetFillCMYKColor},
+ {"l", 2, {tchkNum, tchkNum},
+ &Gfx::opLineTo},
+ {"m", 2, {tchkNum, tchkNum},
+ &Gfx::opMoveTo},
+ {"n", 0, {tchkNone},
+ &Gfx::opEndPath},
+ {"q", 0, {tchkNone},
+ &Gfx::opSave},
+ {"re", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
+ &Gfx::opRectangle},
+ {"rg", 3, {tchkNum, tchkNum, tchkNum},
+ &Gfx::opSetFillRGBColor},
+ {"ri", 1, {tchkName},
+ &Gfx::opSetRenderingIntent},
+ {"s", 0, {tchkNone},
+ &Gfx::opCloseStroke},
+ {"sc", -4, {tchkNum, tchkNum, tchkNum, tchkNum},
+ &Gfx::opSetFillColor},
+ {"scn", -5, {tchkSCN, tchkSCN, tchkSCN, tchkSCN,
+ tchkSCN},
+ &Gfx::opSetFillColorN},
+ {"sh", 1, {tchkName},
+ &Gfx::opShFill},
+ {"v", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
+ &Gfx::opCurveTo1},
+ {"w", 1, {tchkNum},
+ &Gfx::opSetLineWidth},
+ {"y", 4, {tchkNum, tchkNum, tchkNum, tchkNum},
+ &Gfx::opCurveTo2},
+};
+
+#ifdef WIN32 // this works around a bug in the VC7 compiler
+# pragma optimize("",on)
+#endif
+
+#define numOps (sizeof(opTab) / sizeof(Operator))
+
+//------------------------------------------------------------------------
+// GfxResources
+//------------------------------------------------------------------------
+
+GfxResources::GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA) {
+ Object obj1, obj2;
+ Ref r;
+
+ if (resDict) {
+
+ // build font dictionary
+ fonts = NULL;
+ resDict->lookupNF("Font", &obj1);
+ if (obj1.isRef()) {
+ obj1.fetch(xref, &obj2);
+ if (obj2.isDict()) {
+ r = obj1.getRef();
+ fonts = new GfxFontDict(xref, &r, obj2.getDict());
+ }
+ obj2.free();
+ } else if (obj1.isDict()) {
+ fonts = new GfxFontDict(xref, NULL, obj1.getDict());
+ }
+ obj1.free();
+
+ // get XObject dictionary
+ resDict->lookup("XObject", &xObjDict);
+
+ // get color space dictionary
+ resDict->lookup("ColorSpace", &colorSpaceDict);
+
+ // get pattern dictionary
+ resDict->lookup("Pattern", &patternDict);
+
+ // get shading dictionary
+ resDict->lookup("Shading", &shadingDict);
+
+ // get graphics state parameter dictionary
+ resDict->lookup("ExtGState", &gStateDict);
+
+ } else {
+ fonts = NULL;
+ xObjDict.initNull();
+ colorSpaceDict.initNull();
+ patternDict.initNull();
+ shadingDict.initNull();
+ gStateDict.initNull();
+ }
+
+ next = nextA;
+}
+
+GfxResources::~GfxResources() {
+ if (fonts) {
+ delete fonts;
+ }
+ xObjDict.free();
+ colorSpaceDict.free();
+ patternDict.free();
+ shadingDict.free();
+ gStateDict.free();
+}
+
+GfxFont *GfxResources::lookupFont(char *name) {
+ GfxFont *font;
+ GfxResources *resPtr;
+
+ for (resPtr = this; resPtr; resPtr = resPtr->next) {
+ if (resPtr->fonts) {
+ if ((font = resPtr->fonts->lookup(name)))
+ return font;
+ }
+ }
+ error(-1, "Unknown font tag '%s'", name);
+ return NULL;
+}
+
+GBool GfxResources::lookupXObject(char *name, Object *obj) {
+ GfxResources *resPtr;
+
+ for (resPtr = this; resPtr; resPtr = resPtr->next) {
+ if (resPtr->xObjDict.isDict()) {
+ if (!resPtr->xObjDict.dictLookup(name, obj)->isNull())
+ return gTrue;
+ obj->free();
+ }
+ }
+ error(-1, "XObject '%s' is unknown", name);
+ return gFalse;
+}
+
+GBool GfxResources::lookupXObjectNF(char *name, Object *obj) {
+ GfxResources *resPtr;
+
+ for (resPtr = this; resPtr; resPtr = resPtr->next) {
+ if (resPtr->xObjDict.isDict()) {
+ if (!resPtr->xObjDict.dictLookupNF(name, obj)->isNull())
+ return gTrue;
+ obj->free();
+ }
+ }
+ error(-1, "XObject '%s' is unknown", name);
+ return gFalse;
+}
+
+void GfxResources::lookupColorSpace(char *name, Object *obj) {
+ GfxResources *resPtr;
+
+ for (resPtr = this; resPtr; resPtr = resPtr->next) {
+ if (resPtr->colorSpaceDict.isDict()) {
+ if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {
+ return;
+ }
+ obj->free();
+ }
+ }
+ obj->initNull();
+}
+
+GfxPattern *GfxResources::lookupPattern(char *name) {
+ GfxResources *resPtr;
+ GfxPattern *pattern;
+ Object obj;
+
+ for (resPtr = this; resPtr; resPtr = resPtr->next) {
+ if (resPtr->patternDict.isDict()) {
+ if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) {
+ pattern = GfxPattern::parse(&obj);
+ obj.free();
+ return pattern;
+ }
+ obj.free();
+ }
+ }
+ error(-1, "Unknown pattern '%s'", name);
+ return NULL;
+}
+
+GfxShading *GfxResources::lookupShading(char *name) {
+ GfxResources *resPtr;
+ GfxShading *shading;
+ Object obj;
+
+ for (resPtr = this; resPtr; resPtr = resPtr->next) {
+ if (resPtr->shadingDict.isDict()) {
+ if (!resPtr->shadingDict.dictLookup(name, &obj)->isNull()) {
+ shading = GfxShading::parse(&obj);
+ obj.free();
+ return shading;
+ }
+ obj.free();
+ }
+ }
+ error(-1, "Unknown shading '%s'", name);
+ return NULL;
+}
+
+GBool GfxResources::lookupGState(char *name, Object *obj) {
+ GfxResources *resPtr;
+
+ for (resPtr = this; resPtr; resPtr = resPtr->next) {
+ if (resPtr->gStateDict.isDict()) {
+ if (!resPtr->gStateDict.dictLookup(name, obj)->isNull()) {
+ return gTrue;
+ }
+ obj->free();
+ }
+ }
+ error(-1, "ExtGState '%s' is unknown", name);
+ return gFalse;
+}
+
+//------------------------------------------------------------------------
+// Gfx
+//------------------------------------------------------------------------
+
+Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict,
+ double hDPI, double vDPI, PDFRectangle *box,
+ PDFRectangle *cropBox, int rotate,
+ GBool (*abortCheckCbkA)(void *data),
+ void *abortCheckCbkDataA) {
+ int i;
+
+ xref = xrefA;
+ subPage = gFalse;
+ printCommands = globalParams->getPrintCommands();
+
+ // start the resource stack
+ res = new GfxResources(xref, resDict, NULL);
+
+ // initialize
+ out = outA;
+ state = new GfxState(hDPI, vDPI, box, rotate, out->upsideDown());
+ fontChanged = gFalse;
+ clip = clipNone;
+ ignoreUndef = 0;
+ out->startPage(pageNum, state, cropBox->x1,cropBox->y1,cropBox->x2,cropBox->y2);
+ out->setDefaultCTM(state->getCTM());
+ out->updateAll(state);
+ for (i = 0; i < 6; ++i) {
+ baseMatrix[i] = state->getCTM()[i];
+ }
+ formDepth = 0;
+ abortCheckCbk = abortCheckCbkA;
+ abortCheckCbkData = abortCheckCbkDataA;
+
+ // set crop box
+ /*if (cropBox) {
+ state->moveTo(cropBox->x1, cropBox->y1);
+ state->lineTo(cropBox->x2, cropBox->y1);
+ state->lineTo(cropBox->x2, cropBox->y2);
+ state->lineTo(cropBox->x1, cropBox->y2);
+ state->closePath();
+ state->clip();
+ out->clip(state);
+ state->clearPath();
+ }*/
+}
+
+Gfx::Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
+ PDFRectangle *box, PDFRectangle *cropBox,
+ GBool (*abortCheckCbkA)(void *data),
+ void *abortCheckCbkDataA) {
+ int i;
+
+ xref = xrefA;
+ subPage = gTrue;
+ printCommands = globalParams->getPrintCommands();
+
+ // start the resource stack
+ res = new GfxResources(xref, resDict, NULL);
+
+ // initialize
+ out = outA;
+ state = new GfxState(72, 72, box, 0, gFalse);
+ fontChanged = gFalse;
+ clip = clipNone;
+ ignoreUndef = 0;
+ for (i = 0; i < 6; ++i) {
+ baseMatrix[i] = state->getCTM()[i];
+ }
+ formDepth = 0;
+ abortCheckCbk = abortCheckCbkA;
+ abortCheckCbkData = abortCheckCbkDataA;
+
+ // set crop box
+ if (cropBox) {
+ state->moveTo(cropBox->x1, cropBox->y1);
+ state->lineTo(cropBox->x2, cropBox->y1);
+ state->lineTo(cropBox->x2, cropBox->y2);
+ state->lineTo(cropBox->x1, cropBox->y2);
+ state->closePath();
+ state->clip();
+ out->clip(state);
+ state->clearPath();
+ }
+}
+
+Gfx::~Gfx() {
+ while (state->hasSaves()) {
+ restoreState();
+ }
+ if (!subPage) {
+ out->endPage();
+ }
+ while (res) {
+ popResources();
+ }
+ if (state) {
+ delete state;
+ }
+}
+
+void Gfx::display(Object *obj, GBool topLevel) {
+ Object obj2;
+ int i;
+
+ if (obj->isArray()) {
+ for (i = 0; i < obj->arrayGetLength(); ++i) {
+ obj->arrayGet(i, &obj2);
+ if (!obj2.isStream()) {
+ error(-1, "Weird page contents");
+ obj2.free();
+ return;
+ }
+ obj2.free();
+ }
+ } else if (!obj->isStream()) {
+ error(-1, "Weird page contents");
+ return;
+ }
+ parser = new Parser(xref, new Lexer(xref, obj));
+ go(topLevel);
+ delete parser;
+ parser = NULL;
+}
+
+void Gfx::go(GBool topLevel) {
+ Object obj;
+ Object args[maxArgs];
+ int numArgs, i;
+ int lastAbortCheck;
+
+ // scan a sequence of objects
+ updateLevel = lastAbortCheck = 0;
+ numArgs = 0;
+ parser->getObj(&obj);
+ while (!obj.isEOF()) {
+
+ // got a command - execute it
+ if (obj.isCmd()) {
+ if (printCommands) {
+ obj.print(stdout);
+ for (i = 0; i < numArgs; ++i) {
+ printf(" ");
+ args[i].print(stdout);
+ }
+ printf("\n");
+ fflush(stdout);
+ }
+ execOp(&obj, args, numArgs);
+ obj.free();
+ for (i = 0; i < numArgs; ++i)
+ args[i].free();
+ numArgs = 0;
+
+ // periodically update display
+ if (++updateLevel >= 20000) {
+ out->dump();
+ updateLevel = 0;
+ }
+
+ // check for an abort
+ if (abortCheckCbk) {
+ if (updateLevel - lastAbortCheck > 10) {
+ if ((*abortCheckCbk)(abortCheckCbkData)) {
+ break;
+ }
+ lastAbortCheck = updateLevel;
+ }
+ }
+
+ // got an argument - save it
+ } else if (numArgs < maxArgs) {
+ args[numArgs++] = obj;
+
+ // too many arguments - something is wrong
+ } else {
+ error(getPos(), "Too many args in content stream");
+ if (printCommands) {
+ printf("throwing away arg: ");
+ obj.print(stdout);
+ printf("\n");
+ fflush(stdout);
+ }
+ obj.free();
+ }
+
+ // grab the next object
+ parser->getObj(&obj);
+ }
+ obj.free();
+
+ // args at end with no command
+ if (numArgs > 0) {
+ error(getPos(), "Leftover args in content stream");
+ if (printCommands) {
+ printf("%d leftovers:", numArgs);
+ for (i = 0; i < numArgs; ++i) {
+ printf(" ");
+ args[i].print(stdout);
+ }
+ printf("\n");
+ fflush(stdout);
+ }
+ for (i = 0; i < numArgs; ++i)
+ args[i].free();
+ }
+
+ // update display
+ if (topLevel && updateLevel > 0) {
+ out->dump();
+ }
+}
+
+void Gfx::execOp(Object *cmd, Object args[], int numArgs) {
+ Operator *op;
+ char *name;
+ Object *argPtr;
+ int i;
+
+ // find operator
+ name = cmd->getCmd();
+ if (!(op = findOp(name))) {
+ if (ignoreUndef == 0)
+ error(getPos(), "Unknown operator '%s'", name);
+ return;
+ }
+
+ // type check args
+ argPtr = args;
+ if (op->numArgs >= 0) {
+ if (numArgs < op->numArgs) {
+ error(getPos(), "Too few (%d) args to '%s' operator", numArgs, name);
+ return;
+ }
+ if (numArgs > op->numArgs) {
+#if 0
+ error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);
+#endif
+ argPtr += numArgs - op->numArgs;
+ numArgs = op->numArgs;
+ }
+ } else {
+ if (numArgs > -op->numArgs) {
+ error(getPos(), "Too many (%d) args to '%s' operator",
+ numArgs, name);
+ return;
+ }
+ }
+ for (i = 0; i < numArgs; ++i) {
+ if (!checkArg(&argPtr[i], op->tchk[i])) {
+ error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",
+ i, name, argPtr[i].getTypeName());
+ return;
+ }
+ }
+
+ // do it
+ (this->*op->func)(argPtr, numArgs);
+}
+
+Operator *Gfx::findOp(char *name) {
+ int a, b, m, cmp;
+
+ a = -1;
+ b = numOps;
+ // invariant: opTab[a] < name < opTab[b]
+ while (b - a > 1) {
+ m = (a + b) / 2;
+ cmp = strcmp(opTab[m].name, name);
+ if (cmp < 0)
+ a = m;
+ else if (cmp > 0)
+ b = m;
+ else
+ a = b = m;
+ }
+ if (cmp != 0)
+ return NULL;
+ return &opTab[a];
+}
+
+GBool Gfx::checkArg(Object *arg, TchkType type) {
+ switch (type) {
+ case tchkBool: return arg->isBool();
+ case tchkInt: return arg->isInt();
+ case tchkNum: return arg->isNum();
+ case tchkString: return arg->isString();
+ case tchkName: return arg->isName();
+ case tchkArray: return arg->isArray();
+ case tchkProps: return arg->isDict() || arg->isName();
+ case tchkSCN: return arg->isNum() || arg->isName();
+ case tchkNone: return gFalse;
+ }
+ return gFalse;
+}
+
+int Gfx::getPos() {
+ return parser ? parser->getPos() : -1;
+}
+
+//------------------------------------------------------------------------
+// graphics state operators
+//------------------------------------------------------------------------
+
+void Gfx::opSave(Object args[], int numArgs) {
+ saveState();
+}
+
+void Gfx::opRestore(Object args[], int numArgs) {
+ restoreState();
+}
+
+void Gfx::opConcat(Object args[], int numArgs) {
+ state->concatCTM(args[0].getNum(), args[1].getNum(),
+ args[2].getNum(), args[3].getNum(),
+ args[4].getNum(), args[5].getNum());
+ out->updateCTM(state, args[0].getNum(), args[1].getNum(),
+ args[2].getNum(), args[3].getNum(),
+ args[4].getNum(), args[5].getNum());
+ fontChanged = gTrue;
+}
+
+void Gfx::opSetDash(Object args[], int numArgs) {
+ Array *a;
+ int length;
+ Object obj;
+ double *dash;
+ int i;
+
+ a = args[0].getArray();
+ length = a->getLength();
+ if (length == 0) {
+ dash = NULL;
+ } else {
+ dash = (double *)gmallocn(length, sizeof(double));
+ for (i = 0; i < length; ++i) {
+ dash[i] = a->get(i, &obj)->getNum();
+ obj.free();
+ }
+ }
+ state->setLineDash(dash, length, args[1].getNum());
+ out->updateLineDash(state);
+}
+
+void Gfx::opSetFlat(Object args[], int numArgs) {
+ state->setFlatness((int)args[0].getNum());
+ out->updateFlatness(state);
+}
+
+void Gfx::opSetLineJoin(Object args[], int numArgs) {
+ state->setLineJoin(args[0].getInt());
+ out->updateLineJoin(state);
+}
+
+void Gfx::opSetLineCap(Object args[], int numArgs) {
+ state->setLineCap(args[0].getInt());
+ out->updateLineCap(state);
+}
+
+void Gfx::opSetMiterLimit(Object args[], int numArgs) {
+ state->setMiterLimit(args[0].getNum());
+ out->updateMiterLimit(state);
+}
+
+void Gfx::opSetLineWidth(Object args[], int numArgs) {
+ state->setLineWidth(args[0].getNum());
+ out->updateLineWidth(state);
+}
+
+void Gfx::opSetExtGState(Object args[], int numArgs) {
+ Object obj1, obj2;
+ GfxBlendMode mode;
+ GBool haveFillOP;
+
+ if (!res->lookupGState(args[0].getName(), &obj1)) {
+ return;
+ }
+ if (!obj1.isDict()) {
+ error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());
+ obj1.free();
+ return;
+ }
+
+ // transparency support: blend mode, fill/stroke opacity
+ if (!obj1.dictLookup("BM", &obj2)->isNull()) {
+ if (state->parseBlendMode(&obj2, &mode)) {
+ state->setBlendMode(mode);
+ out->updateBlendMode(state);
+ } else {
+ error(getPos(), "Invalid blend mode in ExtGState");
+ }
+ }
+ obj2.free();
+ if (obj1.dictLookup("ca", &obj2)->isNum()) {
+ state->setFillOpacity(obj2.getNum());
+ out->updateFillOpacity(state);
+ }
+ obj2.free();
+ if (obj1.dictLookup("CA", &obj2)->isNum()) {
+ state->setStrokeOpacity(obj2.getNum());
+ out->updateStrokeOpacity(state);
+ }
+ obj2.free();
+
+ // fill/stroke overprint
+ if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) {
+ state->setFillOverprint(obj2.getBool());
+ out->updateFillOverprint(state);
+ }
+ obj2.free();
+ if (obj1.dictLookup("OP", &obj2)->isBool()) {
+ state->setStrokeOverprint(obj2.getBool());
+ out->updateStrokeOverprint(state);
+ if (!haveFillOP) {
+ state->setFillOverprint(obj2.getBool());
+ out->updateFillOverprint(state);
+ }
+ }
+ obj2.free();
+
+ obj1.free();
+}
+
+void Gfx::opSetRenderingIntent(Object args[], int numArgs) {
+}
+
+//------------------------------------------------------------------------
+// color operators
+//------------------------------------------------------------------------
+
+void Gfx::opSetFillGray(Object args[], int numArgs) {
+ GfxColor color;
+
+ state->setFillPattern(NULL);
+ state->setFillColorSpace(new GfxDeviceGrayColorSpace());
+ out->updateFillColorSpace(state);
+ color.c[0] = dblToCol(args[0].getNum());
+ state->setFillColor(&color);
+ out->updateFillColor(state);
+}
+
+void Gfx::opSetStrokeGray(Object args[], int numArgs) {
+ GfxColor color;
+
+ state->setStrokePattern(NULL);
+ state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
+ out->updateStrokeColorSpace(state);
+ color.c[0] = dblToCol(args[0].getNum());
+ state->setStrokeColor(&color);
+ out->updateStrokeColor(state);
+}
+
+void Gfx::opSetFillCMYKColor(Object args[], int numArgs) {
+ GfxColor color;
+ int i;
+
+ state->setFillPattern(NULL);
+ state->setFillColorSpace(new GfxDeviceCMYKColorSpace());
+ out->updateFillColorSpace(state);
+ for (i = 0; i < 4; ++i) {
+ color.c[i] = dblToCol(args[i].getNum());
+ }
+ state->setFillColor(&color);
+ out->updateFillColor(state);
+}
+
+void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) {
+ GfxColor color;
+ int i;
+
+ state->setStrokePattern(NULL);
+ state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());
+ out->updateStrokeColorSpace(state);
+ for (i = 0; i < 4; ++i) {
+ color.c[i] = dblToCol(args[i].getNum());
+ }
+ state->setStrokeColor(&color);
+ out->updateStrokeColor(state);
+}
+
+void Gfx::opSetFillRGBColor(Object args[], int numArgs) {
+ GfxColor color;
+ int i;
+
+ state->setFillPattern(NULL);
+ state->setFillColorSpace(new GfxDeviceRGBColorSpace());
+ out->updateFillColorSpace(state);
+ for (i = 0; i < 3; ++i) {
+ color.c[i] = dblToCol(args[i].getNum());
+ }
+ state->setFillColor(&color);
+ out->updateFillColor(state);
+}
+
+void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) {
+ GfxColor color;
+ int i;
+
+ state->setStrokePattern(NULL);
+ state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());
+ out->updateStrokeColorSpace(state);
+ for (i = 0; i < 3; ++i) {
+ color.c[i] = dblToCol(args[i].getNum());
+ }
+ state->setStrokeColor(&color);
+ out->updateStrokeColor(state);
+}
+
+void Gfx::opSetFillColorSpace(Object args[], int numArgs) {
+ Object obj;
+ GfxColorSpace *colorSpace;
+ GfxColor color;
+ int i;
+
+ state->setFillPattern(NULL);
+ res->lookupColorSpace(args[0].getName(), &obj);
+ if (obj.isNull()) {
+ colorSpace = GfxColorSpace::parse(&args[0]);
+ } else {
+ colorSpace = GfxColorSpace::parse(&obj);
+ }
+ obj.free();
+ if (colorSpace) {
+ state->setFillColorSpace(colorSpace);
+ out->updateFillColorSpace(state);
+ } else {
+ error(getPos(), "Bad color space (fill)");
+ }
+ for (i = 0; i < gfxColorMaxComps; ++i) {
+ color.c[i] = 0;
+ }
+ state->setFillColor(&color);
+ out->updateFillColor(state);
+}
+
+void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {
+ Object obj;
+ GfxColorSpace *colorSpace;
+ GfxColor color;
+ int i;
+
+ state->setStrokePattern(NULL);
+ res->lookupColorSpace(args[0].getName(), &obj);
+ if (obj.isNull()) {
+ colorSpace = GfxColorSpace::parse(&args[0]);
+ } else {
+ colorSpace = GfxColorSpace::parse(&obj);
+ }
+ obj.free();
+ if (colorSpace) {
+ state->setStrokeColorSpace(colorSpace);
+ out->updateStrokeColorSpace(state);
+ } else {
+ error(getPos(), "Bad color space (stroke)");
+ }
+ for (i = 0; i < gfxColorMaxComps; ++i) {
+ color.c[i] = 0;
+ }
+ state->setStrokeColor(&color);
+ out->updateStrokeColor(state);
+}
+
+void Gfx::opSetFillColor(Object args[], int numArgs) {
+ GfxColor color;
+ int i;
+
+ state->setFillPattern(NULL);
+ for (i = 0; i < numArgs; ++i) {
+ color.c[i] = dblToCol(args[i].getNum());
+ }
+ state->setFillColor(&color);
+ out->updateFillColor(state);
+}
+
+void Gfx::opSetStrokeColor(Object args[], int numArgs) {
+ GfxColor color;
+ int i;
+
+ state->setStrokePattern(NULL);
+ for (i = 0; i < numArgs; ++i) {
+ color.c[i] = dblToCol(args[i].getNum());
+ }
+ state->setStrokeColor(&color);
+ out->updateStrokeColor(state);
+}
+
+void Gfx::opSetFillColorN(Object args[], int numArgs) {
+ GfxColor color;
+ GfxPattern *pattern;
+ int i;
+
+ if (state->getFillColorSpace()->getMode() == csPattern) {
+ if (numArgs > 1) {
+ for (i = 0; i < numArgs && i < 4; ++i) {
+ if (args[i].isNum()) {
+ color.c[i] = dblToCol(args[i].getNum());
+ }
+ }
+ state->setFillColor(&color);
+ out->updateFillColor(state);
+ }
+ if (args[numArgs-1].isName() &&
+ (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
+ state->setFillPattern(pattern);
+ }
+
+ } else {
+ state->setFillPattern(NULL);
+ for (i = 0; i < numArgs && i < 4; ++i) {
+ if (args[i].isNum()) {
+ color.c[i] = dblToCol(args[i].getNum());
+ }
+ }
+ state->setFillColor(&color);
+ out->updateFillColor(state);
+ }
+}
+
+void Gfx::opSetStrokeColorN(Object args[], int numArgs) {
+ GfxColor color;
+ GfxPattern *pattern;
+ int i;
+
+ if (state->getStrokeColorSpace()->getMode() == csPattern) {
+ if (numArgs > 1) {
+ for (i = 0; i < numArgs && i < 4; ++i) {
+ if (args[i].isNum()) {
+ color.c[i] = dblToCol(args[i].getNum());
+ }
+ }
+ state->setStrokeColor(&color);
+ out->updateStrokeColor(state);
+ }
+ if (args[numArgs-1].isName() &&
+ (pattern = res->lookupPattern(args[numArgs-1].getName()))) {
+ state->setStrokePattern(pattern);
+ }
+
+ } else {
+ state->setStrokePattern(NULL);
+ for (i = 0; i < numArgs && i < 4; ++i) {
+ if (args[i].isNum()) {
+ color.c[i] = dblToCol(args[i].getNum());
+ }
+ }
+ state->setStrokeColor(&color);
+ out->updateStrokeColor(state);
+ }
+}
+
+//------------------------------------------------------------------------
+// path segment operators
+//------------------------------------------------------------------------
+
+void Gfx::opMoveTo(Object args[], int numArgs) {
+ state->moveTo(args[0].getNum(), args[1].getNum());
+}
+
+void Gfx::opLineTo(Object args[], int numArgs) {
+ if (!state->isCurPt()) {
+ error(getPos(), "No current point in lineto");
+ return;
+ }
+ state->lineTo(args[0].getNum(), args[1].getNum());
+}
+
+void Gfx::opCurveTo(Object args[], int numArgs) {
+ double x1, y1, x2, y2, x3, y3;
+
+ if (!state->isCurPt()) {
+ error(getPos(), "No current point in curveto");
+ return;
+ }
+ x1 = args[0].getNum();
+ y1 = args[1].getNum();
+ x2 = args[2].getNum();
+ y2 = args[3].getNum();
+ x3 = args[4].getNum();
+ y3 = args[5].getNum();
+ state->curveTo(x1, y1, x2, y2, x3, y3);
+}
+
+void Gfx::opCurveTo1(Object args[], int numArgs) {
+ double x1, y1, x2, y2, x3, y3;
+
+ if (!state->isCurPt()) {
+ error(getPos(), "No current point in curveto1");
+ return;
+ }
+ x1 = state->getCurX();
+ y1 = state->getCurY();
+ x2 = args[0].getNum();
+ y2 = args[1].getNum();
+ x3 = args[2].getNum();
+ y3 = args[3].getNum();
+ state->curveTo(x1, y1, x2, y2, x3, y3);
+}
+
+void Gfx::opCurveTo2(Object args[], int numArgs) {
+ double x1, y1, x2, y2, x3, y3;
+
+ if (!state->isCurPt()) {
+ error(getPos(), "No current point in curveto2");
+ return;
+ }
+ x1 = args[0].getNum();
+ y1 = args[1].getNum();
+ x2 = args[2].getNum();
+ y2 = args[3].getNum();
+ x3 = x2;
+ y3 = y2;
+ state->curveTo(x1, y1, x2, y2, x3, y3);
+}
+
+void Gfx::opRectangle(Object args[], int numArgs) {
+ double x, y, w, h;
+
+ x = args[0].getNum();
+ y = args[1].getNum();
+ w = args[2].getNum();
+ h = args[3].getNum();
+ state->moveTo(x, y);
+ state->lineTo(x + w, y);
+ state->lineTo(x + w, y + h);
+ state->lineTo(x, y + h);
+ state->closePath();
+}
+
+void Gfx::opClosePath(Object args[], int numArgs) {
+ if (!state->isCurPt()) {
+ error(getPos(), "No current point in closepath");
+ return;
+ }
+ state->closePath();
+}
+
+//------------------------------------------------------------------------
+// path painting operators
+//------------------------------------------------------------------------
+
+void Gfx::opEndPath(Object args[], int numArgs) {
+ doEndPath();
+}
+
+void Gfx::opStroke(Object args[], int numArgs) {
+ if (!state->isCurPt()) {
+ //error(getPos(), "No path in stroke");
+ return;
+ }
+ if (state->isPath())
+ out->stroke(state);
+ doEndPath();
+}
+
+void Gfx::opCloseStroke(Object args[], int numArgs) {
+ if (!state->isCurPt()) {
+ //error(getPos(), "No path in closepath/stroke");
+ return;
+ }
+ if (state->isPath()) {
+ state->closePath();
+ out->stroke(state);
+ }
+ doEndPath();
+}
+
+void Gfx::opFill(Object args[], int numArgs) {
+ if (!state->isCurPt()) {
+ //error(getPos(), "No path in fill");
+ return;
+ }
+ if (state->isPath()) {
+ if (state->getFillColorSpace()->getMode() == csPattern) {
+ doPatternFill(gFalse);
+ } else {
+ out->fill(state);
+ }
+ }
+ doEndPath();
+}
+
+void Gfx::opEOFill(Object args[], int numArgs) {
+ if (!state->isCurPt()) {
+ //error(getPos(), "No path in eofill");
+ return;
+ }
+ if (state->isPath()) {
+ if (state->getFillColorSpace()->getMode() == csPattern) {
+ doPatternFill(gTrue);
+ } else {
+ out->eoFill(state);
+ }
+ }
+ doEndPath();
+}
+
+void Gfx::opFillStroke(Object args[], int numArgs) {
+ if (!state->isCurPt()) {
+ //error(getPos(), "No path in fill/stroke");
+ return;
+ }
+ if (state->isPath()) {
+ if (state->getFillColorSpace()->getMode() == csPattern) {
+ doPatternFill(gFalse);
+ } else {
+ out->fill(state);
+ }
+ out->stroke(state);
+ }
+ doEndPath();
+}
+
+void Gfx::opCloseFillStroke(Object args[], int numArgs) {
+ if (!state->isCurPt()) {
+ //error(getPos(), "No path in closepath/fill/stroke");
+ return;
+ }
+ if (state->isPath()) {
+ state->closePath();
+ if (state->getFillColorSpace()->getMode() == csPattern) {
+ doPatternFill(gFalse);
+ } else {
+ out->fill(state);
+ }
+ out->stroke(state);
+ }
+ doEndPath();
+}
+
+void Gfx::opEOFillStroke(Object args[], int numArgs) {
+ if (!state->isCurPt()) {
+ //error(getPos(), "No path in eofill/stroke");
+ return;
+ }
+ if (state->isPath()) {
+ if (state->getFillColorSpace()->getMode() == csPattern) {
+ doPatternFill(gTrue);
+ } else {
+ out->eoFill(state);
+ }
+ out->stroke(state);
+ }
+ doEndPath();
+}
+
+void Gfx::opCloseEOFillStroke(Object args[], int numArgs) {
+ if (!state->isCurPt()) {
+ //error(getPos(), "No path in closepath/eofill/stroke");
+ return;
+ }
+ if (state->isPath()) {
+ state->closePath();
+ if (state->getFillColorSpace()->getMode() == csPattern) {
+ doPatternFill(gTrue);
+ } else {
+ out->eoFill(state);
+ }
+ out->stroke(state);
+ }
+ doEndPath();
+}
+
+void Gfx::doPatternFill(GBool eoFill) {
+ GfxPattern *pattern;
+
+ // this is a bit of a kludge -- patterns can be really slow, so we
+ // skip them if we're only doing text extraction, since they almost
+ // certainly don't contain any text
+ if (!out->needNonText()) {
+ return;
+ }
+
+ if (!(pattern = state->getFillPattern())) {
+ return;
+ }
+ switch (pattern->getType()) {
+ case 1:
+ doTilingPatternFill((GfxTilingPattern *)pattern, eoFill);
+ break;
+ case 2:
+ doShadingPatternFill((GfxShadingPattern *)pattern, eoFill);
+ break;
+ default:
+ error(getPos(), "Unimplemented pattern type (%d) in fill",
+ pattern->getType());
+ break;
+ }
+}
+
+void Gfx::doTilingPatternFill(GfxTilingPattern *tPat, GBool eoFill) {
+ GfxPatternColorSpace *patCS;
+ GfxColorSpace *cs;
+ GfxPath *savedPath;
+ double xMin, yMin, xMax, yMax, x, y, x1, y1;
+ double cxMin, cyMin, cxMax, cyMax;
+ int xi0, yi0, xi1, yi1, xi, yi;
+ double *ctm, *btm, *ptm;
+ double m[6], ictm[6], m1[6], imb[6];
+ double det;
+ double xstep, ystep;
+ int i;
+
+ // get color space
+ patCS = (GfxPatternColorSpace *)state->getFillColorSpace();
+
+ // construct a (pattern space) -> (current space) transform matrix
+ ctm = state->getCTM();
+ btm = baseMatrix;
+ ptm = tPat->getMatrix();
+ // iCTM = invert CTM
+ det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
+ ictm[0] = ctm[3] * det;
+ ictm[1] = -ctm[1] * det;
+ ictm[2] = -ctm[2] * det;
+ ictm[3] = ctm[0] * det;
+ ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
+ ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
+ // m1 = PTM * BTM = PTM * base transform matrix
+ m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
+ m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
+ m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
+ m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
+ m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
+ m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
+ // m = m1 * iCTM = (PTM * BTM) * (iCTM)
+ m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
+ m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
+ m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
+ m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
+ m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
+ m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
+
+ // construct a (device space) -> (pattern space) transform matrix
+ det = 1 / (m1[0] * m1[3] - m1[1] * m1[2]);
+ imb[0] = m1[3] * det;
+ imb[1] = -m1[1] * det;
+ imb[2] = -m1[2] * det;
+ imb[3] = m1[0] * det;
+ imb[4] = (m1[2] * m1[5] - m1[3] * m1[4]) * det;
+ imb[5] = (m1[1] * m1[4] - m1[0] * m1[5]) * det;
+
+ // save current graphics state
+ savedPath = state->getPath()->copy();
+ saveState();
+
+ // set underlying color space (for uncolored tiling patterns); set
+ // various other parameters (stroke color, line width) to match
+ // Adobe's behavior
+ if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) {
+ state->setFillColorSpace(cs->copy());
+ out->updateFillColorSpace(state);
+ state->setStrokeColorSpace(cs->copy());
+ out->updateStrokeColorSpace(state);
+ state->setStrokeColor(state->getFillColor());
+ } else {
+ state->setFillColorSpace(new GfxDeviceGrayColorSpace());
+ out->updateFillColorSpace(state);
+ state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());
+ out->updateStrokeColorSpace(state);
+ }
+ state->setFillPattern(NULL);
+ out->updateFillColor(state);
+ state->setStrokePattern(NULL);
+ out->updateStrokeColor(state);
+ state->setLineWidth(0);
+ out->updateLineWidth(state);
+
+ // clip to current path
+ state->clip();
+ if (eoFill) {
+ out->eoClip(state);
+ } else {
+ out->clip(state);
+ }
+ state->clearPath();
+
+ // get the clip region, check for empty
+ state->getClipBBox(&cxMin, &cyMin, &cxMax, &cyMax);
+ if (cxMin > cxMax || cyMin > cyMax) {
+ goto err;
+ }
+
+ // transform clip region bbox to pattern space
+ xMin = xMax = cxMin * imb[0] + cyMin * imb[2] + imb[4];
+ yMin = yMax = cxMin * imb[1] + cyMin * imb[3] + imb[5];
+ x1 = cxMin * imb[0] + cyMax * imb[2] + imb[4];
+ y1 = cxMin * imb[1] + cyMax * imb[3] + imb[5];
+ if (x1 < xMin) {
+ xMin = x1;
+ } else if (x1 > xMax) {
+ xMax = x1;
+ }
+ if (y1 < yMin) {
+ yMin = y1;
+ } else if (y1 > yMax) {
+ yMax = y1;
+ }
+ x1 = cxMax * imb[0] + cyMin * imb[2] + imb[4];
+ y1 = cxMax * imb[1] + cyMin * imb[3] + imb[5];
+ if (x1 < xMin) {
+ xMin = x1;
+ } else if (x1 > xMax) {
+ xMax = x1;
+ }
+ if (y1 < yMin) {
+ yMin = y1;
+ } else if (y1 > yMax) {
+ yMax = y1;
+ }
+ x1 = cxMax * imb[0] + cyMax * imb[2] + imb[4];
+ y1 = cxMax * imb[1] + cyMax * imb[3] + imb[5];
+ if (x1 < xMin) {
+ xMin = x1;
+ } else if (x1 > xMax) {
+ xMax = x1;
+ }
+ if (y1 < yMin) {
+ yMin = y1;
+ } else if (y1 > yMax) {
+ yMax = y1;
+ }
+
+ // draw the pattern
+ //~ this should treat negative steps differently -- start at right/top
+ //~ edge instead of left/bottom (?)
+ xstep = fabs(tPat->getXStep());
+ ystep = fabs(tPat->getYStep());
+ xi0 = (int)floor((xMin - tPat->getBBox()[0]) / xstep);
+ xi1 = (int)ceil((xMax - tPat->getBBox()[0]) / xstep);
+ yi0 = (int)floor((yMin - tPat->getBBox()[1]) / ystep);
+ yi1 = (int)ceil((yMax - tPat->getBBox()[1]) / ystep);
+ for (i = 0; i < 4; ++i) {
+ m1[i] = m[i];
+ }
+ if (out->useTilingPatternFill()) {
+ m1[4] = m[4];
+ m1[5] = m[5];
+ out->tilingPatternFill(state, tPat->getContentStream(),
+ tPat->getPaintType(), tPat->getResDict(),
+ m1, tPat->getBBox(),
+ xi0, yi0, xi1, yi1, xstep, ystep);
+ } else {
+ for (yi = yi0; yi < yi1; ++yi) {
+ for (xi = xi0; xi < xi1; ++xi) {
+ x = xi * xstep;
+ y = yi * ystep;
+ m1[4] = x * m[0] + y * m[2] + m[4];
+ m1[5] = x * m[1] + y * m[3] + m[5];
+ doForm1(tPat->getContentStream(), tPat->getResDict(),
+ m1, tPat->getBBox());
+ }
+ }
+ }
+
+ // restore graphics state
+ err:
+ restoreState();
+ state->setPath(savedPath);
+}
+
+void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, GBool eoFill) {
+ GfxShading *shading;
+ GfxPath *savedPath;
+ double *ctm, *btm, *ptm;
+ double m[6], ictm[6], m1[6];
+ double xMin, yMin, xMax, yMax;
+ double det;
+
+ shading = sPat->getShading();
+
+ // save current graphics state
+ savedPath = state->getPath()->copy();
+ saveState();
+
+ // clip to bbox
+ if (shading->getHasBBox()) {
+ shading->getBBox(&xMin, &yMin, &xMax, &yMax);
+ state->moveTo(xMin, yMin);
+ state->lineTo(xMax, yMin);
+ state->lineTo(xMax, yMax);
+ state->lineTo(xMin, yMax);
+ state->closePath();
+ state->clip();
+ out->clip(state);
+ state->setPath(savedPath->copy());
+ }
+
+ // clip to current path
+ state->clip();
+ if (eoFill) {
+ out->eoClip(state);
+ } else {
+ out->clip(state);
+ }
+
+ // set the color space
+ state->setFillColorSpace(shading->getColorSpace()->copy());
+ out->updateFillColorSpace(state);
+
+ // background color fill
+ if (shading->getHasBackground()) {
+ state->setFillColor(shading->getBackground());
+ out->updateFillColor(state);
+ out->fill(state);
+ }
+ state->clearPath();
+
+ // construct a (pattern space) -> (current space) transform matrix
+ ctm = state->getCTM();
+ btm = baseMatrix;
+ ptm = sPat->getMatrix();
+ // iCTM = invert CTM
+ det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
+ ictm[0] = ctm[3] * det;
+ ictm[1] = -ctm[1] * det;
+ ictm[2] = -ctm[2] * det;
+ ictm[3] = ctm[0] * det;
+ ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
+ ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
+ // m1 = PTM * BTM = PTM * base transform matrix
+ m1[0] = ptm[0] * btm[0] + ptm[1] * btm[2];
+ m1[1] = ptm[0] * btm[1] + ptm[1] * btm[3];
+ m1[2] = ptm[2] * btm[0] + ptm[3] * btm[2];
+ m1[3] = ptm[2] * btm[1] + ptm[3] * btm[3];
+ m1[4] = ptm[4] * btm[0] + ptm[5] * btm[2] + btm[4];
+ m1[5] = ptm[4] * btm[1] + ptm[5] * btm[3] + btm[5];
+ // m = m1 * iCTM = (PTM * BTM) * (iCTM)
+ m[0] = m1[0] * ictm[0] + m1[1] * ictm[2];
+ m[1] = m1[0] * ictm[1] + m1[1] * ictm[3];
+ m[2] = m1[2] * ictm[0] + m1[3] * ictm[2];
+ m[3] = m1[2] * ictm[1] + m1[3] * ictm[3];
+ m[4] = m1[4] * ictm[0] + m1[5] * ictm[2] + ictm[4];
+ m[5] = m1[4] * ictm[1] + m1[5] * ictm[3] + ictm[5];
+
+ // set the new matrix
+ state->concatCTM(m[0], m[1], m[2], m[3], m[4], m[5]);
+ out->updateCTM(state, m[0], m[1], m[2], m[3], m[4], m[5]);
+
+ // do shading type-specific operations
+ switch (shading->getType()) {
+ case 1:
+ doFunctionShFill((GfxFunctionShading *)shading);
+ break;
+ case 2:
+ doAxialShFill((GfxAxialShading *)shading);
+ break;
+ case 3:
+ doRadialShFill((GfxRadialShading *)shading);
+ break;
+ case 4:
+ case 5:
+ doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
+ break;
+ case 6:
+ case 7:
+ doPatchMeshShFill((GfxPatchMeshShading *)shading);
+ break;
+ }
+
+ // restore graphics state
+ restoreState();
+ state->setPath(savedPath);
+}
+
+void Gfx::opShFill(Object args[], int numArgs) {
+ GfxShading *shading;
+ GfxPath *savedPath;
+ double xMin, yMin, xMax, yMax;
+
+ if (!(shading = res->lookupShading(args[0].getName()))) {
+ return;
+ }
+
+ // save current graphics state
+ savedPath = state->getPath()->copy();
+ saveState();
+
+ // clip to bbox
+ if (shading->getHasBBox()) {
+ shading->getBBox(&xMin, &yMin, &xMax, &yMax);
+ state->moveTo(xMin, yMin);
+ state->lineTo(xMax, yMin);
+ state->lineTo(xMax, yMax);
+ state->lineTo(xMin, yMax);
+ state->closePath();
+ state->clip();
+ out->clip(state);
+ state->clearPath();
+ }
+
+ // set the color space
+ state->setFillColorSpace(shading->getColorSpace()->copy());
+ out->updateFillColorSpace(state);
+
+ // do shading type-specific operations
+ switch (shading->getType()) {
+ case 1:
+ doFunctionShFill((GfxFunctionShading *)shading);
+ break;
+ case 2:
+ doAxialShFill((GfxAxialShading *)shading);
+ break;
+ case 3:
+ doRadialShFill((GfxRadialShading *)shading);
+ break;
+ case 4:
+ case 5:
+ doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);
+ break;
+ case 6:
+ case 7:
+ doPatchMeshShFill((GfxPatchMeshShading *)shading);
+ break;
+ }
+
+ // restore graphics state
+ restoreState();
+ state->setPath(savedPath);
+
+ delete shading;
+}
+
+void Gfx::doFunctionShFill(GfxFunctionShading *shading) {
+ double x0, y0, x1, y1;
+ GfxColor colors[4];
+
+ if (out->useShadedFills()) {
+ out->functionShadedFill(state, shading);
+ } else {
+ shading->getDomain(&x0, &y0, &x1, &y1);
+ shading->getColor(x0, y0, &colors[0]);
+ shading->getColor(x0, y1, &colors[1]);
+ shading->getColor(x1, y0, &colors[2]);
+ shading->getColor(x1, y1, &colors[3]);
+ doFunctionShFill1(shading, x0, y0, x1, y1, colors, 0);
+ }
+}
+
+void Gfx::doFunctionShFill1(GfxFunctionShading *shading,
+ double x0, double y0,
+ double x1, double y1,
+ GfxColor *colors, int depth) {
+ GfxColor fillColor;
+ GfxColor color0M, color1M, colorM0, colorM1, colorMM;
+ GfxColor colors2[4];
+ double *matrix;
+ double xM, yM;
+ int nComps, i, j;
+
+ nComps = shading->getColorSpace()->getNComps();
+ matrix = shading->getMatrix();
+
+ // compare the four corner colors
+ for (i = 0; i < 4; ++i) {
+ for (j = 0; j < nComps; ++j) {
+ if (abs(colors[i].c[j] - colors[(i+1)&3].c[j]) > functionColorDelta) {
+ break;
+ }
+ }
+ if (j < nComps) {
+ break;
+ }
+ }
+
+ // center of the rectangle
+ xM = 0.5 * (x0 + x1);
+ yM = 0.5 * (y0 + y1);
+
+ // the four corner colors are close (or we hit the recursive limit)
+ // -- fill the rectangle; but require at least one subdivision
+ // (depth==0) to avoid problems when the four outer corners of the
+ // shaded region are the same color
+ if ((i == 4 && depth > 0) || depth == functionMaxDepth) {
+
+ // use the center color
+ shading->getColor(xM, yM, &fillColor);
+ state->setFillColor(&fillColor);
+ out->updateFillColor(state);
+
+ // fill the rectangle
+ state->moveTo(x0 * matrix[0] + y0 * matrix[2] + matrix[4],
+ x0 * matrix[1] + y0 * matrix[3] + matrix[5]);
+ state->lineTo(x1 * matrix[0] + y0 * matrix[2] + matrix[4],
+ x1 * matrix[1] + y0 * matrix[3] + matrix[5]);
+ state->lineTo(x1 * matrix[0] + y1 * matrix[2] + matrix[4],
+ x1 * matrix[1] + y1 * matrix[3] + matrix[5]);
+ state->lineTo(x0 * matrix[0] + y1 * matrix[2] + matrix[4],
+ x0 * matrix[1] + y1 * matrix[3] + matrix[5]);
+ state->closePath();
+ out->fill(state);
+ state->clearPath();
+
+ // the four corner colors are not close enough -- subdivide the
+ // rectangle
+ } else {
+
+ // colors[0] colorM0 colors[2]
+ // (x0,y0) (xM,y0) (x1,y0)
+ // +----------+----------+
+ // | | |
+ // | UL | UR |
+ // color0M | colorMM | color1M
+ // (x0,yM) +----------+----------+ (x1,yM)
+ // | (xM,yM) |
+ // | LL | LR |
+ // | | |
+ // +----------+----------+
+ // colors[1] colorM1 colors[3]
+ // (x0,y1) (xM,y1) (x1,y1)
+
+ shading->getColor(x0, yM, &color0M);
+ shading->getColor(x1, yM, &color1M);
+ shading->getColor(xM, y0, &colorM0);
+ shading->getColor(xM, y1, &colorM1);
+ shading->getColor(xM, yM, &colorMM);
+
+ // upper-left sub-rectangle
+ colors2[0] = colors[0];
+ colors2[1] = color0M;
+ colors2[2] = colorM0;
+ colors2[3] = colorMM;
+ doFunctionShFill1(shading, x0, y0, xM, yM, colors2, depth + 1);
+
+ // lower-left sub-rectangle
+ colors2[0] = color0M;
+ colors2[1] = colors[1];
+ colors2[2] = colorMM;
+ colors2[3] = colorM1;
+ doFunctionShFill1(shading, x0, yM, xM, y1, colors2, depth + 1);
+
+ // upper-right sub-rectangle
+ colors2[0] = colorM0;
+ colors2[1] = colorMM;
+ colors2[2] = colors[2];
+ colors2[3] = color1M;
+ doFunctionShFill1(shading, xM, y0, x1, yM, colors2, depth + 1);
+
+ // lower-right sub-rectangle
+ colors2[0] = colorMM;
+ colors2[1] = colorM1;
+ colors2[2] = color1M;
+ colors2[3] = colors[3];
+ doFunctionShFill1(shading, xM, yM, x1, y1, colors2, depth + 1);
+ }
+}
+
+void Gfx::doAxialShFill(GfxAxialShading *shading) {
+ double xMin, yMin, xMax, yMax;
+ double x0, y0, x1, y1;
+ double dx, dy, mul;
+ GBool dxZero, dyZero;
+ double tMin, tMax, t, tx, ty;
+ double s[4], sMin, sMax, tmp;
+ double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;
+ double t0, t1, tt;
+ double ta[axialMaxSplits + 1];
+ int next[axialMaxSplits + 1];
+ GfxColor color0, color1;
+ int nComps;
+ int i, j, k, kk;
+
+ if (out->useShadedFills()) {
+
+ out->axialShadedFill(state, shading);
+
+ } else {
+
+ // get the clip region bbox
+ state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+
+ // compute min and max t values, based on the four corners of the
+ // clip region bbox
+ shading->getCoords(&x0, &y0, &x1, &y1);
+ dx = x1 - x0;
+ dy = y1 - y0;
+ dxZero = fabs(dx) < 0.001;
+ dyZero = fabs(dy) < 0.001;
+ mul = 1 / (dx * dx + dy * dy);
+ tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul;
+ t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul;
+ if (t < tMin) {
+ tMin = t;
+ } else if (t > tMax) {
+ tMax = t;
+ }
+ t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul;
+ if (t < tMin) {
+ tMin = t;
+ } else if (t > tMax) {
+ tMax = t;
+ }
+ t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul;
+ if (t < tMin) {
+ tMin = t;
+ } else if (t > tMax) {
+ tMax = t;
+ }
+ if (tMin < 0 && !shading->getExtend0()) {
+ tMin = 0;
+ }
+ if (tMax > 1 && !shading->getExtend1()) {
+ tMax = 1;
+ }
+
+ // get the function domain
+ t0 = shading->getDomain0();
+ t1 = shading->getDomain1();
+
+ // Traverse the t axis and do the shading.
+ //
+ // For each point (tx, ty) on the t axis, consider a line through
+ // that point perpendicular to the t axis:
+ //
+ // x(s) = tx + s * -dy --> s = (x - tx) / -dy
+ // y(s) = ty + s * dx --> s = (y - ty) / dx
+ //
+ // Then look at the intersection of this line with the bounding box
+ // (xMin, yMin, xMax, yMax). In the general case, there are four
+ // intersection points:
+ //
+ // s0 = (xMin - tx) / -dy
+ // s1 = (xMax - tx) / -dy
+ // s2 = (yMin - ty) / dx
+ // s3 = (yMax - ty) / dx
+ //
+ // and we want the middle two s values.
+ //
+ // In the case where dx = 0, take s0 and s1; in the case where dy =
+ // 0, take s2 and s3.
+ //
+ // Each filled polygon is bounded by two of these line segments
+ // perpdendicular to the t axis.
+ //
+ // The t axis is bisected into smaller regions until the color
+ // difference across a region is small enough, and then the region
+ // is painted with a single color.
+
+ // set up: require at least one split to avoid problems when the two
+ // ends of the t axis have the same color
+ nComps = shading->getColorSpace()->getNComps();
+ ta[0] = tMin;
+ next[0] = axialMaxSplits / 2;
+ ta[axialMaxSplits / 2] = 0.5 * (tMin + tMax);
+ next[axialMaxSplits / 2] = axialMaxSplits;
+ ta[axialMaxSplits] = tMax;
+
+ // compute the color at t = tMin
+ if (tMin < 0) {
+ tt = t0;
+ } else if (tMin > 1) {
+ tt = t1;
+ } else {
+ tt = t0 + (t1 - t0) * tMin;
+ }
+ shading->getColor(tt, &color0);
+
+ // compute the coordinates of the point on the t axis at t = tMin;
+ // then compute the intersection of the perpendicular line with the
+ // bounding box
+ tx = x0 + tMin * dx;
+ ty = y0 + tMin * dy;
+ if (dxZero && dyZero) {
+ sMin = sMax = 0;
+ } if (dxZero) {
+ sMin = (xMin - tx) / -dy;
+ sMax = (xMax - tx) / -dy;
+ if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
+ } else if (dyZero) {
+ sMin = (yMin - ty) / dx;
+ sMax = (yMax - ty) / dx;
+ if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
+ } else {
+ s[0] = (yMin - ty) / dx;
+ s[1] = (yMax - ty) / dx;
+ s[2] = (xMin - tx) / -dy;
+ s[3] = (xMax - tx) / -dy;
+ for (j = 0; j < 3; ++j) {
+ kk = j;
+ for (k = j + 1; k < 4; ++k) {
+ if (s[k] < s[kk]) {
+ kk = k;
+ }
+ }
+ tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
+ }
+ sMin = s[1];
+ sMax = s[2];
+ }
+ ux0 = tx - sMin * dy;
+ uy0 = ty + sMin * dx;
+ vx0 = tx - sMax * dy;
+ vy0 = ty + sMax * dx;
+
+ i = 0;
+ while (i < axialMaxSplits) {
+
+ // bisect until color difference is small enough or we hit the
+ // bisection limit
+ j = next[i];
+ while (j > i + 1) {
+ if (ta[j] < 0) {
+ tt = t0;
+ } else if (ta[j] > 1) {
+ tt = t1;
+ } else {
+ tt = t0 + (t1 - t0) * ta[j];
+ }
+ shading->getColor(tt, &color1);
+ for (k = 0; k < nComps; ++k) {
+ if (abs(color1.c[k] - color0.c[k]) > axialColorDelta) {
+ break;
+ }
+ }
+ if (k == nComps) {
+ break;
+ }
+ k = (i + j) / 2;
+ ta[k] = 0.5 * (ta[i] + ta[j]);
+ next[i] = k;
+ next[k] = j;
+ j = k;
+ }
+
+ // use the average of the colors of the two sides of the region
+ for (k = 0; k < nComps; ++k) {
+ color0.c[k] = (color0.c[k] + color1.c[k]) / 2;
+ }
+
+ // compute the coordinates of the point on the t axis; then
+ // compute the intersection of the perpendicular line with the
+ // bounding box
+ tx = x0 + ta[j] * dx;
+ ty = y0 + ta[j] * dy;
+ if (dxZero && dyZero) {
+ sMin = sMax = 0;
+ } if (dxZero) {
+ sMin = (xMin - tx) / -dy;
+ sMax = (xMax - tx) / -dy;
+ if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
+ } else if (dyZero) {
+ sMin = (yMin - ty) / dx;
+ sMax = (yMax - ty) / dx;
+ if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }
+ } else {
+ s[0] = (yMin - ty) / dx;
+ s[1] = (yMax - ty) / dx;
+ s[2] = (xMin - tx) / -dy;
+ s[3] = (xMax - tx) / -dy;
+ for (j = 0; j < 3; ++j) {
+ kk = j;
+ for (k = j + 1; k < 4; ++k) {
+ if (s[k] < s[kk]) {
+ kk = k;
+ }
+ }
+ tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;
+ }
+ sMin = s[1];
+ sMax = s[2];
+ }
+ ux1 = tx - sMin * dy;
+ uy1 = ty + sMin * dx;
+ vx1 = tx - sMax * dy;
+ vy1 = ty + sMax * dx;
+
+ // set the color
+ state->setFillColor(&color0);
+ out->updateFillColor(state);
+
+ // fill the region
+ state->moveTo(ux0, uy0);
+ state->lineTo(vx0, vy0);
+ state->lineTo(vx1, vy1);
+ state->lineTo(ux1, uy1);
+ state->closePath();
+ out->fill(state);
+ state->clearPath();
+
+ // set up for next region
+ ux0 = ux1;
+ uy0 = uy1;
+ vx0 = vx1;
+ vy0 = vy1;
+ color0 = color1;
+ i = next[i];
+ }
+ }
+}
+
+void Gfx::doRadialShFill(GfxRadialShading *shading) {
+ double sMin, sMax, xMin, yMin, xMax, yMax;
+ double x0, y0, r0, x1, y1, r1, t0, t1;
+ int nComps;
+ GfxColor colorA, colorB;
+ double xa, ya, xb, yb, ra, rb;
+ double ta, tb, sa, sb;
+ int ia, ib, k, n;
+ double *ctm;
+ double angle, t, d0, d1;
+
+ if (out->useShadedFills()) {
+
+ out->radialShadedFill(state, shading);
+
+ } else {
+
+ // get the shading info
+ shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1);
+ t0 = shading->getDomain0();
+ t1 = shading->getDomain1();
+ nComps = shading->getColorSpace()->getNComps();
+
+ // compute the (possibly extended) s range
+ sMin = 0;
+ sMax = 1;
+ if (shading->getExtend0()) {
+ if (r0 < r1) {
+ // extend the smaller end
+ sMin = -r0 / (r1 - r0);
+ } else {
+ // extend the larger end
+ state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+ d0 = (x0 - xMin) * (x0 - xMin);
+ d1 = (x0 - xMax) * (x0 - xMax);
+ sMin = d0 > d1 ? d0 : d1;
+ d0 = (y0 - yMin) * (y0 - yMin);
+ d1 = (y0 - yMax) * (y0 - yMax);
+ sMin += d0 > d1 ? d0 : d1;
+ sMin = (sqrt(sMin) - r0) / (r1 - r0);
+ if (sMin > 0) {
+ sMin = 0;
+ } else if (sMin < -20) {
+ // sanity check
+ sMin = -20;
+ }
+ }
+ }
+ if (shading->getExtend1()) {
+ if (r1 < r0) {
+ // extend the smaller end
+ sMax = -r0 / (r1 - r0);
+ } else if (r1 > r0) {
+ // extend the larger end
+ state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
+ d0 = (x1 - xMin) * (x1 - xMin);
+ d1 = (x1 - xMax) * (x1 - xMax);
+ sMax = d0 > d1 ? d0 : d1;
+ d0 = (y1 - yMin) * (y1 - yMin);
+ d1 = (y1 - yMax) * (y1 - yMax);
+ sMax += d0 > d1 ? d0 : d1;
+ sMax = (sqrt(sMax) - r0) / (r1 - r0);
+ if (sMax < 1) {
+ sMax = 1;
+ } else if (sMax > 20) {
+ // sanity check
+ sMax = 20;
+ }
+ }
+ }
+
+ // compute the number of steps into which circles must be divided to
+ // achieve a curve flatness of 0.1 pixel in device space for the
+ // largest circle (note that "device space" is 72 dpi when generating
+ // PostScript, hence the relatively small 0.1 pixel accuracy)
+ ctm = state->getCTM();
+ t = fabs(ctm[0]);
+ if (fabs(ctm[1]) > t) {
+ t = fabs(ctm[1]);
+ }
+ if (fabs(ctm[2]) > t) {
+ t = fabs(ctm[2]);
+ }
+ if (fabs(ctm[3]) > t) {
+ t = fabs(ctm[3]);
+ }
+ if (r0 > r1) {
+ t *= r0;
+ } else {
+ t *= r1;
+ }
+ if (t < 1) {
+ n = 3;
+ } else {
+ n = (int)(M_PI / acos(1 - 0.1 / t));
+ if (n < 3) {
+ n = 3;
+ } else if (n > 200) {
+ n = 200;
+ }
+ }
+
+ // Traverse the t axis and do the shading.
+ //
+ // This generates and fills a series of rings. Each ring is defined
+ // by two circles:
+ // sa, ta, xa, ya, ra, colorA
+ // sb, tb, xb, yb, rb, colorB
+ //
+ // The s/t axis is divided into radialMaxSplits parts; these parts
+ // are combined as much as possible while respecting the
+ // radialColorDelta parameter.
+
+ // setup for the start circle
+ ia = 0;
+ sa = sMin;
+ ta = t0 + sa * (t1 - t0);
+ xa = x0 + sa * (x1 - x0);
+ ya = y0 + sa * (y1 - y0);
+ ra = r0 + sa * (r1 - r0);
+ if (ta < t0) {
+ shading->getColor(t0, &colorA);
+ } else if (ta > t1) {
+ shading->getColor(t1, &colorA);
+ } else {
+ shading->getColor(ta, &colorA);
+ }
+
+ while (ia < radialMaxSplits) {
+
+ // go as far along the t axis (toward t1) as we can, such that the
+ // color difference is within the tolerance (radialColorDelta) --
+ // this uses bisection (between the current value, t, and t1),
+ // limited to radialMaxSplits points along the t axis; require at
+ // least one split to avoid problems when the innermost and
+ // outermost colors are the same
+ ib = radialMaxSplits;
+ sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
+ tb = t0 + sb * (t1 - t0);
+ if (tb < t0) {
+ shading->getColor(t0, &colorB);
+ } else if (tb > t1) {
+ shading->getColor(t1, &colorB);
+ } else {
+ shading->getColor(tb, &colorB);
+ }
+ while (ib - ia > 1) {
+ for (k = 0; k < nComps; ++k) {
+ if (abs(colorB.c[k] - colorA.c[k]) > radialColorDelta) {
+ break;
+ }
+ }
+ if (k == nComps && ib < radialMaxSplits) {
+ break;
+ }
+ ib = (ia + ib) / 2;
+ sb = sMin + ((double)ib / (double)radialMaxSplits) * (sMax - sMin);
+ tb = t0 + sb * (t1 - t0);
+ if (tb < t0) {
+ shading->getColor(t0, &colorB);
+ } else if (tb > t1) {
+ shading->getColor(t1, &colorB);
+ } else {
+ shading->getColor(tb, &colorB);
+ }
+ }
+
+ // compute center and radius of the circle
+ xb = x0 + sb * (x1 - x0);
+ yb = y0 + sb * (y1 - y0);
+ rb = r0 + sb * (r1 - r0);
+
+ // use the average of the colors at the two circles
+ for (k = 0; k < nComps; ++k) {
+ colorA.c[k] = (colorA.c[k] + colorB.c[k]) / 2;
+ }
+ state->setFillColor(&colorA);
+ out->updateFillColor(state);
+
+ // construct path for first circle
+ state->moveTo(xa + ra, ya);
+ for (k = 1; k < n; ++k) {
+ angle = ((double)k / (double)n) * 2 * M_PI;
+ state->lineTo(xa + ra * cos(angle), ya + ra * sin(angle));
+ }
+ state->closePath();
+
+ // construct and append path for second circle
+ state->moveTo(xb + rb, yb);
+ for (k = 1; k < n; ++k) {
+ angle = ((double)k / (double)n) * 2 * M_PI;
+ state->lineTo(xb + rb * cos(angle), yb + rb * sin(angle));
+ }
+ state->closePath();
+
+ // fill the ring
+ out->eoFill(state);
+ state->clearPath();
+
+ // step to the next value of t
+ ia = ib;
+ sa = sb;
+ ta = tb;
+ xa = xb;
+ ya = yb;
+ ra = rb;
+ colorA = colorB;
+ }
+ }
+}
+
+void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {
+ double x0, y0, x1, y1, x2, y2;
+ GfxColor color0, color1, color2;
+ int i;
+
+ for (i = 0; i < shading->getNTriangles(); ++i) {
+ shading->getTriangle(i, &x0, &y0, &color0,
+ &x1, &y1, &color1,
+ &x2, &y2, &color2);
+ gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2,
+ shading->getColorSpace()->getNComps(), 0);
+ }
+}
+
+void Gfx::gouraudFillTriangle(double x0, double y0, GfxColor *color0,
+ double x1, double y1, GfxColor *color1,
+ double x2, double y2, GfxColor *color2,
+ int nComps, int depth) {
+ double x01, y01, x12, y12, x20, y20;
+ GfxColor color01, color12, color20;
+ int i;
+
+ for (i = 0; i < nComps; ++i) {
+ if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||
+ abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {
+ break;
+ }
+ }
+ if (i == nComps || depth == gouraudMaxDepth) {
+ state->setFillColor(color0);
+ out->updateFillColor(state);
+ state->moveTo(x0, y0);
+ state->lineTo(x1, y1);
+ state->lineTo(x2, y2);
+ state->closePath();
+ out->fill(state);
+ state->clearPath();
+ } else {
+ x01 = 0.5 * (x0 + x1);
+ y01 = 0.5 * (y0 + y1);
+ x12 = 0.5 * (x1 + x2);
+ y12 = 0.5 * (y1 + y2);
+ x20 = 0.5 * (x2 + x0);
+ y20 = 0.5 * (y2 + y0);
+ //~ if the shading has a Function, this should interpolate on the
+ //~ function parameter, not on the color components
+ for (i = 0; i < nComps; ++i) {
+ color01.c[i] = (color0->c[i] + color1->c[i]) / 2;
+ color12.c[i] = (color1->c[i] + color2->c[i]) / 2;
+ color20.c[i] = (color2->c[i] + color0->c[i]) / 2;
+ }
+ gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,
+ x20, y20, &color20, nComps, depth + 1);
+ gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,
+ x12, y12, &color12, nComps, depth + 1);
+ gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,
+ x20, y20, &color20, nComps, depth + 1);
+ gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,
+ x2, y2, color2, nComps, depth + 1);
+ }
+}
+
+void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) {
+ int start, i;
+
+ if (shading->getNPatches() > 128) {
+ start = 3;
+ } else if (shading->getNPatches() > 64) {
+ start = 2;
+ } else if (shading->getNPatches() > 16) {
+ start = 1;
+ } else {
+ start = 0;
+ }
+ for (i = 0; i < shading->getNPatches(); ++i) {
+ fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(),
+ start);
+ }
+}
+
+void Gfx::fillPatch(GfxPatch *patch, int nComps, int depth) {
+ GfxPatch patch00, patch01, patch10, patch11;
+ double xx[4][8], yy[4][8];
+ double xxm, yym;
+ int i;
+
+ for (i = 0; i < nComps; ++i) {
+ if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])
+ > patchColorDelta ||
+ abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])
+ > patchColorDelta ||
+ abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])
+ > patchColorDelta ||
+ abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])
+ > patchColorDelta) {
+ break;
+ }
+ }
+ if (i == nComps || depth == patchMaxDepth) {
+ state->setFillColor(&patch->color[0][0]);
+ out->updateFillColor(state);
+ state->moveTo(patch->x[0][0], patch->y[0][0]);
+ state->curveTo(patch->x[0][1], patch->y[0][1],
+ patch->x[0][2], patch->y[0][2],
+ patch->x[0][3], patch->y[0][3]);
+ state->curveTo(patch->x[1][3], patch->y[1][3],
+ patch->x[2][3], patch->y[2][3],
+ patch->x[3][3], patch->y[3][3]);
+ state->curveTo(patch->x[3][2], patch->y[3][2],
+ patch->x[3][1], patch->y[3][1],
+ patch->x[3][0], patch->y[3][0]);
+ state->curveTo(patch->x[2][0], patch->y[2][0],
+ patch->x[1][0], patch->y[1][0],
+ patch->x[0][0], patch->y[0][0]);
+ state->closePath();
+ out->fill(state);
+ state->clearPath();
+ } else {
+ for (i = 0; i < 4; ++i) {
+ xx[i][0] = patch->x[i][0];
+ yy[i][0] = patch->y[i][0];
+ xx[i][1] = 0.5 * (patch->x[i][0] + patch->x[i][1]);
+ yy[i][1] = 0.5 * (patch->y[i][0] + patch->y[i][1]);
+ xxm = 0.5 * (patch->x[i][1] + patch->x[i][2]);
+ yym = 0.5 * (patch->y[i][1] + patch->y[i][2]);
+ xx[i][6] = 0.5 * (patch->x[i][2] + patch->x[i][3]);
+ yy[i][6] = 0.5 * (patch->y[i][2] + patch->y[i][3]);
+ xx[i][2] = 0.5 * (xx[i][1] + xxm);
+ yy[i][2] = 0.5 * (yy[i][1] + yym);
+ xx[i][5] = 0.5 * (xxm + xx[i][6]);
+ yy[i][5] = 0.5 * (yym + yy[i][6]);
+ xx[i][3] = xx[i][4] = 0.5 * (xx[i][2] + xx[i][5]);
+ yy[i][3] = yy[i][4] = 0.5 * (yy[i][2] + yy[i][5]);
+ xx[i][7] = patch->x[i][3];
+ yy[i][7] = patch->y[i][3];
+ }
+ for (i = 0; i < 4; ++i) {
+ patch00.x[0][i] = xx[0][i];
+ patch00.y[0][i] = yy[0][i];
+ patch00.x[1][i] = 0.5 * (xx[0][i] + xx[1][i]);
+ patch00.y[1][i] = 0.5 * (yy[0][i] + yy[1][i]);
+ xxm = 0.5 * (xx[1][i] + xx[2][i]);
+ yym = 0.5 * (yy[1][i] + yy[2][i]);
+ patch10.x[2][i] = 0.5 * (xx[2][i] + xx[3][i]);
+ patch10.y[2][i] = 0.5 * (yy[2][i] + yy[3][i]);
+ patch00.x[2][i] = 0.5 * (patch00.x[1][i] + xxm);
+ patch00.y[2][i] = 0.5 * (patch00.y[1][i] + yym);
+ patch10.x[1][i] = 0.5 * (xxm + patch10.x[2][i]);
+ patch10.y[1][i] = 0.5 * (yym + patch10.y[2][i]);
+ patch00.x[3][i] = 0.5 * (patch00.x[2][i] + patch10.x[1][i]);
+ patch00.y[3][i] = 0.5 * (patch00.y[2][i] + patch10.y[1][i]);
+ patch10.x[0][i] = patch00.x[3][i];
+ patch10.y[0][i] = patch00.y[3][i];
+ patch10.x[3][i] = xx[3][i];
+ patch10.y[3][i] = yy[3][i];
+ }
+ for (i = 4; i < 8; ++i) {
+ patch01.x[0][i-4] = xx[0][i];
+ patch01.y[0][i-4] = yy[0][i];
+ patch01.x[1][i-4] = 0.5 * (xx[0][i] + xx[1][i]);
+ patch01.y[1][i-4] = 0.5 * (yy[0][i] + yy[1][i]);
+ xxm = 0.5 * (xx[1][i] + xx[2][i]);
+ yym = 0.5 * (yy[1][i] + yy[2][i]);
+ patch11.x[2][i-4] = 0.5 * (xx[2][i] + xx[3][i]);
+ patch11.y[2][i-4] = 0.5 * (yy[2][i] + yy[3][i]);
+ patch01.x[2][i-4] = 0.5 * (patch01.x[1][i-4] + xxm);
+ patch01.y[2][i-4] = 0.5 * (patch01.y[1][i-4] + yym);
+ patch11.x[1][i-4] = 0.5 * (xxm + patch11.x[2][i-4]);
+ patch11.y[1][i-4] = 0.5 * (yym + patch11.y[2][i-4]);
+ patch01.x[3][i-4] = 0.5 * (patch01.x[2][i-4] + patch11.x[1][i-4]);
+ patch01.y[3][i-4] = 0.5 * (patch01.y[2][i-4] + patch11.y[1][i-4]);
+ patch11.x[0][i-4] = patch01.x[3][i-4];
+ patch11.y[0][i-4] = patch01.y[3][i-4];
+ patch11.x[3][i-4] = xx[3][i];
+ patch11.y[3][i-4] = yy[3][i];
+ }
+ //~ if the shading has a Function, this should interpolate on the
+ //~ function parameter, not on the color components
+ for (i = 0; i < nComps; ++i) {
+ patch00.color[0][0].c[i] = patch->color[0][0].c[i];
+ patch00.color[0][1].c[i] = (patch->color[0][0].c[i] +
+ patch->color[0][1].c[i]) / 2;
+ patch01.color[0][0].c[i] = patch00.color[0][1].c[i];
+ patch01.color[0][1].c[i] = patch->color[0][1].c[i];
+ patch01.color[1][1].c[i] = (patch->color[0][1].c[i] +
+ patch->color[1][1].c[i]) / 2;
+ patch11.color[0][1].c[i] = patch01.color[1][1].c[i];
+ patch11.color[1][1].c[i] = patch->color[1][1].c[i];
+ patch11.color[1][0].c[i] = (patch->color[1][1].c[i] +
+ patch->color[1][0].c[i]) / 2;
+ patch10.color[1][1].c[i] = patch11.color[1][0].c[i];
+ patch10.color[1][0].c[i] = patch->color[1][0].c[i];
+ patch10.color[0][0].c[i] = (patch->color[1][0].c[i] +
+ patch->color[0][0].c[i]) / 2;
+ patch00.color[1][0].c[i] = patch10.color[0][0].c[i];
+ patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] +
+ patch01.color[1][1].c[i]) / 2;
+ patch01.color[1][0].c[i] = patch00.color[1][1].c[i];
+ patch11.color[0][0].c[i] = patch00.color[1][1].c[i];
+ patch10.color[0][1].c[i] = patch00.color[1][1].c[i];
+ }
+ fillPatch(&patch00, nComps, depth + 1);
+ fillPatch(&patch10, nComps, depth + 1);
+ fillPatch(&patch01, nComps, depth + 1);
+ fillPatch(&patch11, nComps, depth + 1);
+ }
+}
+
+void Gfx::doEndPath() {
+ if (state->isCurPt() && clip != clipNone) {
+ state->clip();
+ if (clip == clipNormal) {
+ out->clip(state);
+ } else {
+ out->eoClip(state);
+ }
+ }
+ clip = clipNone;
+ state->clearPath();
+}
+
+//------------------------------------------------------------------------
+// path clipping operators
+//------------------------------------------------------------------------
+
+void Gfx::opClip(Object args[], int numArgs) {
+ clip = clipNormal;
+}
+
+void Gfx::opEOClip(Object args[], int numArgs) {
+ clip = clipEO;
+}
+
+//------------------------------------------------------------------------
+// text object operators
+//------------------------------------------------------------------------
+
+void Gfx::opBeginText(Object args[], int numArgs) {
+ state->setTextMat(1, 0, 0, 1, 0, 0);
+ state->textMoveTo(0, 0);
+ out->updateTextMat(state);
+ out->updateTextPos(state);
+ fontChanged = gTrue;
+}
+
+void Gfx::opEndText(Object args[], int numArgs) {
+ out->endTextObject(state);
+}
+
+//------------------------------------------------------------------------
+// text state operators
+//------------------------------------------------------------------------
+
+void Gfx::opSetCharSpacing(Object args[], int numArgs) {
+ state->setCharSpace(args[0].getNum());
+ out->updateCharSpace(state);
+}
+
+void Gfx::opSetFont(Object args[], int numArgs) {
+ GfxFont *font;
+
+ if (!(font = res->lookupFont(args[0].getName()))) {
+ return;
+ }
+ if (printCommands) {
+ printf(" font: tag=%s name='%s' %g\n",
+ font->getTag()->getCString(),
+ font->getName() ? font->getName()->getCString() : "???",
+ args[1].getNum());
+ fflush(stdout);
+ }
+ state->setFont(font, args[1].getNum());
+ fontChanged = gTrue;
+}
+
+void Gfx::opSetTextLeading(Object args[], int numArgs) {
+ state->setLeading(args[0].getNum());
+}
+
+void Gfx::opSetTextRender(Object args[], int numArgs) {
+ state->setRender(args[0].getInt());
+ out->updateRender(state);
+}
+
+void Gfx::opSetTextRise(Object args[], int numArgs) {
+ state->setRise(args[0].getNum());
+ out->updateRise(state);
+}
+
+void Gfx::opSetWordSpacing(Object args[], int numArgs) {
+ state->setWordSpace(args[0].getNum());
+ out->updateWordSpace(state);
+}
+
+void Gfx::opSetHorizScaling(Object args[], int numArgs) {
+ state->setHorizScaling(args[0].getNum());
+ out->updateHorizScaling(state);
+ fontChanged = gTrue;
+}
+
+//------------------------------------------------------------------------
+// text positioning operators
+//------------------------------------------------------------------------
+
+void Gfx::opTextMove(Object args[], int numArgs) {
+ double tx, ty;
+
+ tx = state->getLineX() + args[0].getNum();
+ ty = state->getLineY() + args[1].getNum();
+ state->textMoveTo(tx, ty);
+ out->updateTextPos(state);
+}
+
+void Gfx::opTextMoveSet(Object args[], int numArgs) {
+ double tx, ty;
+
+ tx = state->getLineX() + args[0].getNum();
+ ty = args[1].getNum();
+ state->setLeading(-ty);
+ ty += state->getLineY();
+ state->textMoveTo(tx, ty);
+ out->updateTextPos(state);
+}
+
+void Gfx::opSetTextMatrix(Object args[], int numArgs) {
+ state->setTextMat(args[0].getNum(), args[1].getNum(),
+ args[2].getNum(), args[3].getNum(),
+ args[4].getNum(), args[5].getNum());
+ state->textMoveTo(0, 0);
+ out->updateTextMat(state);
+ out->updateTextPos(state);
+ fontChanged = gTrue;
+}
+
+void Gfx::opTextNextLine(Object args[], int numArgs) {
+ double tx, ty;
+
+ tx = state->getLineX();
+ ty = state->getLineY() - state->getLeading();
+ state->textMoveTo(tx, ty);
+ out->updateTextPos(state);
+}
+
+//------------------------------------------------------------------------
+// text string operators
+//------------------------------------------------------------------------
+
+void Gfx::opShowText(Object args[], int numArgs) {
+ if (!state->getFont()) {
+ error(getPos(), "No font in show");
+ return;
+ }
+ if (fontChanged) {
+ out->updateFont(state);
+ fontChanged = gFalse;
+ }
+ out->beginStringOp(state);
+ doShowText(args[0].getString());
+ out->endStringOp(state);
+}
+
+void Gfx::opMoveShowText(Object args[], int numArgs) {
+ double tx, ty;
+
+ if (!state->getFont()) {
+ error(getPos(), "No font in move/show");
+ return;
+ }
+ if (fontChanged) {
+ out->updateFont(state);
+ fontChanged = gFalse;
+ }
+ tx = state->getLineX();
+ ty = state->getLineY() - state->getLeading();
+ state->textMoveTo(tx, ty);
+ out->updateTextPos(state);
+ out->beginStringOp(state);
+ doShowText(args[0].getString());
+ out->endStringOp(state);
+}
+
+void Gfx::opMoveSetShowText(Object args[], int numArgs) {
+ double tx, ty;
+
+ if (!state->getFont()) {
+ error(getPos(), "No font in move/set/show");
+ return;
+ }
+ if (fontChanged) {
+ out->updateFont(state);
+ fontChanged = gFalse;
+ }
+ state->setWordSpace(args[0].getNum());
+ state->setCharSpace(args[1].getNum());
+ tx = state->getLineX();
+ ty = state->getLineY() - state->getLeading();
+ state->textMoveTo(tx, ty);
+ out->updateWordSpace(state);
+ out->updateCharSpace(state);
+ out->updateTextPos(state);
+ out->beginStringOp(state);
+ doShowText(args[2].getString());
+ out->endStringOp(state);
+}
+
+void Gfx::opShowSpaceText(Object args[], int numArgs) {
+ Array *a;
+ Object obj;
+ int wMode;
+ int i;
+
+ if (!state->getFont()) {
+ error(getPos(), "No font in show/space");
+ return;
+ }
+ if (fontChanged) {
+ out->updateFont(state);
+ fontChanged = gFalse;
+ }
+ out->beginStringOp(state);
+ wMode = state->getFont()->getWMode();
+ a = args[0].getArray();
+ for (i = 0; i < a->getLength(); ++i) {
+ a->get(i, &obj);
+ if (obj.isNum()) {
+ // this uses the absolute value of the font size to match
+ // Acrobat's behavior
+ if (wMode) {
+ state->textShift(0, -obj.getNum() * 0.001 *
+ fabs(state->getFontSize()));
+ } else {
+ state->textShift(-obj.getNum() * 0.001 *
+ fabs(state->getFontSize()), 0);
+ }
+ out->updateTextShift(state, obj.getNum());
+ } else if (obj.isString()) {
+ doShowText(obj.getString());
+ } else {
+ error(getPos(), "Element of show/space array must be number or string");
+ }
+ obj.free();
+ }
+ out->endStringOp(state);
+}
+
+void Gfx::doShowText(GString *s) {
+ GfxFont *font;
+ int wMode;
+ double riseX, riseY;
+ CharCode code;
+ Unicode u[8];
+ double x, y, dx, dy, dx2, dy2, curX, curY, tdx, tdy, lineX, lineY;
+ double originX, originY, tOriginX, tOriginY;
+ double oldCTM[6], newCTM[6];
+ double *mat;
+ Object charProc;
+ Dict *resDict;
+ Parser *oldParser;
+ char *p;
+ int len, n, uLen, nChars, nSpaces, i;
+
+ font = state->getFont();
+ wMode = font->getWMode();
+
+ if (out->useDrawChar()) {
+ out->beginString(state, s);
+ }
+
+ // handle a Type 3 char
+ if (font->getType() == fontType3 && out->interpretType3Chars()) {
+ mat = state->getCTM();
+ for (i = 0; i < 6; ++i) {
+ oldCTM[i] = mat[i];
+ }
+ mat = state->getTextMat();
+ newCTM[0] = mat[0] * oldCTM[0] + mat[1] * oldCTM[2];
+ newCTM[1] = mat[0] * oldCTM[1] + mat[1] * oldCTM[3];
+ newCTM[2] = mat[2] * oldCTM[0] + mat[3] * oldCTM[2];
+ newCTM[3] = mat[2] * oldCTM[1] + mat[3] * oldCTM[3];
+ mat = font->getFontMatrix();
+ newCTM[0] = mat[0] * newCTM[0] + mat[1] * newCTM[2];
+ newCTM[1] = mat[0] * newCTM[1] + mat[1] * newCTM[3];
+ newCTM[2] = mat[2] * newCTM[0] + mat[3] * newCTM[2];
+ newCTM[3] = mat[2] * newCTM[1] + mat[3] * newCTM[3];
+ newCTM[0] *= state->getFontSize();
+ newCTM[1] *= state->getFontSize();
+ newCTM[2] *= state->getFontSize();
+ newCTM[3] *= state->getFontSize();
+ newCTM[0] *= state->getHorizScaling();
+ newCTM[2] *= state->getHorizScaling();
+ state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
+ curX = state->getCurX();
+ curY = state->getCurY();
+ lineX = state->getLineX();
+ lineY = state->getLineY();
+ oldParser = parser;
+ p = s->getCString();
+ len = s->getLength();
+ while (len > 0) {
+ n = font->getNextChar(p, len, &code,
+ u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
+ &dx, &dy, &originX, &originY);
+ dx = dx * state->getFontSize() + state->getCharSpace();
+ if (n == 1 && *p == ' ') {
+ dx += state->getWordSpace();
+ }
+ dx *= state->getHorizScaling();
+ dy *= state->getFontSize();
+ state->textTransformDelta(dx, dy, &tdx, &tdy);
+ state->transform(curX + riseX, curY + riseY, &x, &y);
+ saveState();
+ state->setCTM(newCTM[0], newCTM[1], newCTM[2], newCTM[3], x, y);
+ //~ out->updateCTM(???)
+ if (!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,
+ code, u, uLen)) {
+ ((Gfx8BitFont *)font)->getCharProc(code, &charProc);
+ if ((resDict = ((Gfx8BitFont *)font)->getResources())) {
+ pushResources(resDict);
+ }
+ if (charProc.isStream()) {
+ display(&charProc, gFalse);
+ } else {
+ error(getPos(), "Missing or bad Type3 CharProc entry");
+ }
+ out->endType3Char(state);
+ if (resDict) {
+ popResources();
+ }
+ charProc.free();
+ }
+ restoreState();
+ // GfxState::restore() does *not* restore the current position,
+ // so we deal with it here using (curX, curY) and (lineX, lineY)
+ curX += tdx;
+ curY += tdy;
+ state->moveTo(curX, curY);
+ state->textSetPos(lineX, lineY);
+ p += n;
+ len -= n;
+ }
+ parser = oldParser;
+
+ } else if (out->useDrawChar()) {
+ state->textTransformDelta(0, state->getRise(), &riseX, &riseY);
+ p = s->getCString();
+ len = s->getLength();
+ while (len > 0) {
+ n = font->getNextChar(p, len, &code,
+ u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
+ &dx, &dy, &originX, &originY);
+ if (wMode) {
+ dx *= state->getFontSize();
+ dy = dy * state->getFontSize() + state->getCharSpace();
+ if (n == 1 && *p == ' ') {
+ dy += state->getWordSpace();
+ }
+ } else {
+ dx = dx * state->getFontSize() + state->getCharSpace();
+ if (n == 1 && *p == ' ') {
+ dx += state->getWordSpace();
+ }
+ dx *= state->getHorizScaling();
+ dy *= state->getFontSize();
+ }
+ state->textTransformDelta(dx, dy, &tdx, &tdy);
+ originX *= state->getFontSize();
+ originY *= state->getFontSize();
+ state->textTransformDelta(originX, originY, &tOriginX, &tOriginY);
+ out->drawChar(state, state->getCurX() + riseX, state->getCurY() + riseY,
+ tdx, tdy, tOriginX, tOriginY, code, n, u, uLen);
+ state->shift(tdx, tdy);
+ p += n;
+ len -= n;
+ }
+
+ } else {
+ dx = dy = 0;
+ p = s->getCString();
+ len = s->getLength();
+ nChars = nSpaces = 0;
+ while (len > 0) {
+ n = font->getNextChar(p, len, &code,
+ u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,
+ &dx2, &dy2, &originX, &originY);
+ dx += dx2;
+ dy += dy2;
+ if (n == 1 && *p == ' ') {
+ ++nSpaces;
+ }
+ ++nChars;
+ p += n;
+ len -= n;
+ }
+ if (wMode) {
+ dx *= state->getFontSize();
+ dy = dy * state->getFontSize()
+ + nChars * state->getCharSpace()
+ + nSpaces * state->getWordSpace();
+ } else {
+ dx = dx * state->getFontSize()
+ + nChars * state->getCharSpace()
+ + nSpaces * state->getWordSpace();
+ dx *= state->getHorizScaling();
+ dy *= state->getFontSize();
+ }
+ state->textTransformDelta(dx, dy, &tdx, &tdy);
+ out->drawString(state, s);
+ state->shift(tdx, tdy);
+ }
+
+ if (out->useDrawChar()) {
+ out->endString(state);
+ }
+
+ updateLevel += 10 * s->getLength();
+}
+
+//------------------------------------------------------------------------
+// XObject operators
+//------------------------------------------------------------------------
+
+void Gfx::opXObject(Object args[], int numArgs) {
+ Object obj1, obj2, obj3, refObj;
+#if OPI_SUPPORT
+ Object opiDict;
+#endif
+
+ if (!res->lookupXObject(args[0].getName(), &obj1)) {
+ return;
+ }
+ if (!obj1.isStream()) {
+ error(getPos(), "XObject '%s' is wrong type", args[0].getName());
+ obj1.free();
+ return;
+ }
+#if OPI_SUPPORT
+ obj1.streamGetDict()->lookup("OPI", &opiDict);
+ if (opiDict.isDict()) {
+ out->opiBegin(state, opiDict.getDict());
+ }
+#endif
+ obj1.streamGetDict()->lookup("Subtype", &obj2);
+ if (obj2.isName("Image")) {
+ if (out->needNonText()) {
+ res->lookupXObjectNF(args[0].getName(), &refObj);
+ doImage(&refObj, obj1.getStream(), gFalse);
+ refObj.free();
+ }
+ } else if (obj2.isName("Form")) {
+ doForm(&obj1);
+ } else if (obj2.isName("PS")) {
+ obj1.streamGetDict()->lookup("Level1", &obj3);
+ out->psXObject(obj1.getStream(),
+ obj3.isStream() ? obj3.getStream() : (Stream *)NULL);
+ } else if (obj2.isName()) {
+ error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());
+ } else {
+ error(getPos(), "XObject subtype is missing or wrong type");
+ }
+ obj2.free();
+#if OPI_SUPPORT
+ if (opiDict.isDict()) {
+ out->opiEnd(state, opiDict.getDict());
+ }
+ opiDict.free();
+#endif
+ obj1.free();
+}
+
+void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {
+ Dict *dict, *maskDict;
+ int width, height;
+ int bits, maskBits;
+ StreamColorSpaceMode csMode;
+ GBool mask;
+ GBool invert;
+ GfxColorSpace *colorSpace, *maskColorSpace;
+ GfxImageColorMap *colorMap, *maskColorMap;
+ Object maskObj, smaskObj;
+ GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;
+ int maskColors[2*gfxColorMaxComps];
+ int maskWidth, maskHeight;
+ GBool maskInvert;
+ Stream *maskStr;
+ Object obj1, obj2;
+ int i;
+
+ // get info from the stream
+ bits = 0;
+ csMode = streamCSNone;
+ str->getImageParams(&bits, &csMode);
+
+ // get stream dict
+ dict = str->getDict();
+
+ // get size
+ dict->lookup("Width", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ dict->lookup("W", &obj1);
+ }
+ if (!obj1.isInt())
+ goto err2;
+ width = obj1.getInt();
+ obj1.free();
+ dict->lookup("Height", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ dict->lookup("H", &obj1);
+ }
+ if (!obj1.isInt())
+ goto err2;
+ height = obj1.getInt();
+ obj1.free();
+
+ // image or mask?
+ dict->lookup("ImageMask", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ dict->lookup("IM", &obj1);
+ }
+ mask = gFalse;
+ if (obj1.isBool())
+ mask = obj1.getBool();
+ else if (!obj1.isNull())
+ goto err2;
+ obj1.free();
+
+ // bit depth
+ if (bits == 0) {
+ dict->lookup("BitsPerComponent", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ dict->lookup("BPC", &obj1);
+ }
+ if (obj1.isInt()) {
+ bits = obj1.getInt();
+ } else if (mask) {
+ bits = 1;
+ } else {
+ goto err2;
+ }
+ obj1.free();
+ }
+
+ // display a mask
+ if (mask) {
+
+ // check for inverted mask
+ if (bits != 1)
+ goto err1;
+ invert = gFalse;
+ dict->lookup("Decode", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ dict->lookup("D", &obj1);
+ }
+ if (obj1.isArray()) {
+ obj1.arrayGet(0, &obj2);
+ if (obj2.isInt() && obj2.getInt() == 1)
+ invert = gTrue;
+ obj2.free();
+ } else if (!obj1.isNull()) {
+ goto err2;
+ }
+ obj1.free();
+
+ // draw it
+ out->drawImageMask(state, ref, str, width, height, invert, inlineImg);
+
+ } else {
+
+ // get color space and color map
+ dict->lookup("ColorSpace", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ dict->lookup("CS", &obj1);
+ }
+ if (obj1.isName()) {
+ res->lookupColorSpace(obj1.getName(), &obj2);
+ if (!obj2.isNull()) {
+ obj1.free();
+ obj1 = obj2;
+ } else {
+ obj2.free();
+ }
+ }
+ if (!obj1.isNull()) {
+ colorSpace = GfxColorSpace::parse(&obj1);
+ } else if (csMode == streamCSDeviceGray) {
+ colorSpace = new GfxDeviceGrayColorSpace();
+ } else if (csMode == streamCSDeviceRGB) {
+ colorSpace = new GfxDeviceRGBColorSpace();
+ } else if (csMode == streamCSDeviceCMYK) {
+ colorSpace = new GfxDeviceCMYKColorSpace();
+ } else {
+ colorSpace = NULL;
+ }
+ obj1.free();
+ if (!colorSpace) {
+ goto err1;
+ }
+ dict->lookup("Decode", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ dict->lookup("D", &obj1);
+ }
+ colorMap = new GfxImageColorMap(bits, &obj1, colorSpace);
+ obj1.free();
+ if (!colorMap->isOk()) {
+ delete colorMap;
+ goto err1;
+ }
+
+ // get the mask
+ haveColorKeyMask = haveExplicitMask = haveSoftMask = gFalse;
+ maskStr = NULL; // make gcc happy
+ maskWidth = maskHeight = 0; // make gcc happy
+ maskInvert = gFalse; // make gcc happy
+ maskColorMap = NULL; // make gcc happy
+ dict->lookup("Mask", &maskObj);
+ dict->lookup("SMask", &smaskObj);
+ if (smaskObj.isStream()) {
+ // soft mask
+ if (inlineImg) {
+ goto err1;
+ }
+ maskStr = smaskObj.getStream();
+ maskDict = smaskObj.streamGetDict();
+ maskDict->lookup("Width", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup("W", &obj1);
+ }
+ if (!obj1.isInt()) {
+ goto err2;
+ }
+ maskWidth = obj1.getInt();
+ obj1.free();
+ maskDict->lookup("Height", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup("H", &obj1);
+ }
+ if (!obj1.isInt()) {
+ goto err2;
+ }
+ maskHeight = obj1.getInt();
+ obj1.free();
+ maskDict->lookup("BitsPerComponent", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup("BPC", &obj1);
+ }
+ if (!obj1.isInt()) {
+ goto err2;
+ }
+ maskBits = obj1.getInt();
+ obj1.free();
+ maskDict->lookup("ColorSpace", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup("CS", &obj1);
+ }
+ if (obj1.isName()) {
+ res->lookupColorSpace(obj1.getName(), &obj2);
+ if (!obj2.isNull()) {
+ obj1.free();
+ obj1 = obj2;
+ } else {
+ obj2.free();
+ }
+ }
+ maskColorSpace = GfxColorSpace::parse(&obj1);
+ obj1.free();
+ if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {
+ goto err1;
+ }
+ maskDict->lookup("Decode", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup("D", &obj1);
+ }
+ maskColorMap = new GfxImageColorMap(maskBits, &obj1, maskColorSpace);
+ obj1.free();
+ if (!maskColorMap->isOk()) {
+ delete maskColorMap;
+ goto err1;
+ }
+ //~ handle the Matte entry
+ haveSoftMask = gTrue;
+ } else if (maskObj.isArray()) {
+ // color key mask
+ for (i = 0;
+ i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
+ ++i) {
+ maskObj.arrayGet(i, &obj1);
+ maskColors[i] = obj1.getInt();
+ obj1.free();
+ }
+ haveColorKeyMask = gTrue;
+ } else if (maskObj.isStream()) {
+ // explicit mask
+ if (inlineImg) {
+ goto err1;
+ }
+ maskStr = maskObj.getStream();
+ maskDict = maskObj.streamGetDict();
+ maskDict->lookup("Width", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup("W", &obj1);
+ }
+ if (!obj1.isInt()) {
+ goto err2;
+ }
+ maskWidth = obj1.getInt();
+ obj1.free();
+ maskDict->lookup("Height", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup("H", &obj1);
+ }
+ if (!obj1.isInt()) {
+ goto err2;
+ }
+ maskHeight = obj1.getInt();
+ obj1.free();
+ maskDict->lookup("ImageMask", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup("IM", &obj1);
+ }
+ if (!obj1.isBool() || !obj1.getBool()) {
+ goto err2;
+ }
+ obj1.free();
+ maskInvert = gFalse;
+ maskDict->lookup("Decode", &obj1);
+ if (obj1.isNull()) {
+ obj1.free();
+ maskDict->lookup("D", &obj1);
+ }
+ if (obj1.isArray()) {
+ obj1.arrayGet(0, &obj2);
+ if (obj2.isInt() && obj2.getInt() == 1) {
+ maskInvert = gTrue;
+ }
+ obj2.free();
+ } else if (!obj1.isNull()) {
+ goto err2;
+ }
+ obj1.free();
+ haveExplicitMask = gTrue;
+ }
+
+ // draw it
+ if (haveSoftMask) {
+ out->drawSoftMaskedImage(state, ref, str, width, height, colorMap,
+ maskStr, maskWidth, maskHeight, maskColorMap);
+ delete maskColorMap;
+ } else if (haveExplicitMask) {
+ out->drawMaskedImage(state, ref, str, width, height, colorMap,
+ maskStr, maskWidth, maskHeight, maskInvert);
+ } else {
+ out->drawImage(state, ref, str, width, height, colorMap,
+ haveColorKeyMask ? maskColors : (int *)NULL, inlineImg);
+ }
+ delete colorMap;
+
+ maskObj.free();
+ smaskObj.free();
+ }
+
+ if ((i = width * height) > 1000) {
+ i = 1000;
+ }
+ updateLevel += i;
+
+ return;
+
+ err2:
+ obj1.free();
+ err1:
+ error(getPos(), "Bad image parameters");
+}
+
+void Gfx::doForm(Object *str) {
+ Dict *dict;
+ Object matrixObj, bboxObj;
+ double m[6], bbox[6];
+ Object resObj;
+ Dict *resDict;
+ Object obj1;
+ int i;
+
+ // check for excessive recursion
+ if (formDepth > 20) {
+ return;
+ }
+
+ // get stream dict
+ dict = str->streamGetDict();
+
+ // check form type
+ dict->lookup("FormType", &obj1);
+ if (!(obj1.isNull() || (obj1.isInt() && obj1.getInt() == 1))) {
+ error(getPos(), "Unknown form type");
+ }
+ obj1.free();
+
+ // get bounding box
+ dict->lookup("BBox", &bboxObj);
+ if (!bboxObj.isArray()) {
+ matrixObj.free();
+ bboxObj.free();
+ error(getPos(), "Bad form bounding box");
+ return;
+ }
+ for (i = 0; i < 4; ++i) {
+ bboxObj.arrayGet(i, &obj1);
+ bbox[i] = obj1.getNum();
+ obj1.free();
+ }
+ bboxObj.free();
+
+ // get matrix
+ dict->lookup("Matrix", &matrixObj);
+ if (matrixObj.isArray()) {
+ for (i = 0; i < 6; ++i) {
+ matrixObj.arrayGet(i, &obj1);
+ m[i] = obj1.getNum();
+ obj1.free();
+ }
+ } else {
+ m[0] = 1; m[1] = 0;
+ m[2] = 0; m[3] = 1;
+ m[4] = 0; m[5] = 0;
+ }
+ matrixObj.free();
+
+ // get resources
+ dict->lookup("Resources", &resObj);
+ resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
+
+ // draw it
+ ++formDepth;
+ doForm1(str, resDict, m, bbox);
+ --formDepth;
+
+ resObj.free();
+}
+
+void Gfx::doAnnot(Object *str, double xMin, double yMin,
+ double xMax, double yMax) {
+ Dict *dict, *resDict;
+ Object matrixObj, bboxObj, resObj;
+ Object obj1;
+ double m[6], bbox[6], ictm[6];
+ double *ctm;
+ double formX0, formY0, formX1, formY1;
+ double annotX0, annotY0, annotX1, annotY1;
+ double det, x, y, sx, sy;
+ int i;
+
+ // get stream dict
+ dict = str->streamGetDict();
+
+ // get the form bounding box
+ dict->lookup("BBox", &bboxObj);
+ if (!bboxObj.isArray()) {
+ bboxObj.free();
+ error(getPos(), "Bad form bounding box");
+ return;
+ }
+ for (i = 0; i < 4; ++i) {
+ bboxObj.arrayGet(i, &obj1);
+ bbox[i] = obj1.getNum();
+ obj1.free();
+ }
+ bboxObj.free();
+
+ // get the form matrix
+ dict->lookup("Matrix", &matrixObj);
+ if (matrixObj.isArray()) {
+ for (i = 0; i < 6; ++i) {
+ matrixObj.arrayGet(i, &obj1);
+ m[i] = obj1.getNum();
+ obj1.free();
+ }
+ } else {
+ m[0] = 1; m[1] = 0;
+ m[2] = 0; m[3] = 1;
+ m[4] = 0; m[5] = 0;
+ }
+ matrixObj.free();
+
+ // transform the form bbox from form space to user space
+ formX0 = bbox[0] * m[0] + bbox[1] * m[2] + m[4];
+ formY0 = bbox[0] * m[1] + bbox[1] * m[3] + m[5];
+ formX1 = bbox[2] * m[0] + bbox[3] * m[2] + m[4];
+ formY1 = bbox[2] * m[1] + bbox[3] * m[3] + m[5];
+
+ // transform the annotation bbox from default user space to user
+ // space: (bbox * baseMatrix) * iCTM
+ ctm = state->getCTM();
+ det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
+ ictm[0] = ctm[3] * det;
+ ictm[1] = -ctm[1] * det;
+ ictm[2] = -ctm[2] * det;
+ ictm[3] = ctm[0] * det;
+ ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
+ ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
+ x = baseMatrix[0] * xMin + baseMatrix[2] * yMin + baseMatrix[4];
+ y = baseMatrix[1] * xMin + baseMatrix[3] * yMin + baseMatrix[5];
+ annotX0 = ictm[0] * x + ictm[2] * y + ictm[4];
+ annotY0 = ictm[1] * x + ictm[3] * y + ictm[5];
+ x = baseMatrix[0] * xMax + baseMatrix[2] * yMax + baseMatrix[4];
+ y = baseMatrix[1] * xMax + baseMatrix[3] * yMax + baseMatrix[5];
+ annotX1 = ictm[0] * x + ictm[2] * y + ictm[4];
+ annotY1 = ictm[1] * x + ictm[3] * y + ictm[5];
+
+ // swap min/max coords
+ if (formX0 > formX1) {
+ x = formX0; formX0 = formX1; formX1 = x;
+ }
+ if (formY0 > formY1) {
+ y = formY0; formY0 = formY1; formY1 = y;
+ }
+ if (annotX0 > annotX1) {
+ x = annotX0; annotX0 = annotX1; annotX1 = x;
+ }
+ if (annotY0 > annotY1) {
+ y = annotY0; annotY0 = annotY1; annotY1 = y;
+ }
+
+ // scale the form to fit the annotation bbox
+ if (formX1 == formX0) {
+ // this shouldn't happen
+ sx = 1;
+ } else {
+ sx = (annotX1 - annotX0) / (formX1 - formX0);
+ }
+ if (formY1 == formY0) {
+ // this shouldn't happen
+ sy = 1;
+ } else {
+ sy = (annotY1 - annotY0) / (formY1 - formY0);
+ }
+ m[0] *= sx;
+ m[2] *= sx;
+ m[4] = (m[4] - formX0) * sx + annotX0;
+ m[1] *= sy;
+ m[3] *= sy;
+ m[5] = (m[5] - formY0) * sy + annotY0;
+
+ // get resources
+ dict->lookup("Resources", &resObj);
+ resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;
+
+ // draw it
+ doForm1(str, resDict, m, bbox);
+
+ resObj.free();
+ bboxObj.free();
+}
+
+void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox) {
+ Parser *oldParser;
+ double oldBaseMatrix[6];
+ int i;
+
+ // push new resources on stack
+ pushResources(resDict);
+
+ // save current graphics state
+ saveState();
+
+ // kill any pre-existing path
+ state->clearPath();
+
+ // save current parser
+ oldParser = parser;
+
+ // set form transformation matrix
+ state->concatCTM(matrix[0], matrix[1], matrix[2],
+ matrix[3], matrix[4], matrix[5]);
+ out->updateCTM(state, matrix[0], matrix[1], matrix[2],
+ matrix[3], matrix[4], matrix[5]);
+
+ // set new base matrix
+ for (i = 0; i < 6; ++i) {
+ oldBaseMatrix[i] = baseMatrix[i];
+ baseMatrix[i] = state->getCTM()[i];
+ }
+
+ // set form bounding box
+ state->moveTo(bbox[0], bbox[1]);
+ state->lineTo(bbox[2], bbox[1]);
+ state->lineTo(bbox[2], bbox[3]);
+ state->lineTo(bbox[0], bbox[3]);
+ state->closePath();
+ state->clip();
+ out->clip(state);
+ state->clearPath();
+
+ // draw the form
+ display(str, gFalse);
+
+ // restore base matrix
+ for (i = 0; i < 6; ++i) {
+ baseMatrix[i] = oldBaseMatrix[i];
+ }
+
+ // restore parser
+ parser = oldParser;
+
+ // restore graphics state
+ restoreState();
+
+ // pop resource stack
+ popResources();
+
+ return;
+}
+
+//------------------------------------------------------------------------
+// in-line image operators
+//------------------------------------------------------------------------
+
+void Gfx::opBeginImage(Object args[], int numArgs) {
+ Stream *str;
+ int c1, c2;
+
+ // build dict/stream
+ str = buildImageStream();
+
+ // display the image
+ if (str) {
+ doImage(NULL, str, gTrue);
+
+ // skip 'EI' tag
+ c1 = str->getBaseStream()->getChar();
+ c2 = str->getBaseStream()->getChar();
+ while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {
+ c1 = c2;
+ c2 = str->getBaseStream()->getChar();
+ }
+ delete str;
+ }
+}
+
+Stream *Gfx::buildImageStream() {
+ Object dict;
+ Object obj;
+ char *key;
+ Stream *str;
+
+ // build dictionary
+ dict.initDict(xref);
+ parser->getObj(&obj);
+ while (!obj.isCmd("ID") && !obj.isEOF()) {
+ if (!obj.isName()) {
+ error(getPos(), "Inline image dictionary key must be a name object");
+ obj.free();
+ } else {
+ key = copyString(obj.getName());
+ obj.free();
+ parser->getObj(&obj);
+ if (obj.isEOF() || obj.isError()) {
+ gfree(key);
+ break;
+ }
+ dict.dictAdd(key, &obj);
+ }
+ parser->getObj(&obj);
+ }
+ if (obj.isEOF()) {
+ error(getPos(), "End of file in inline image");
+ obj.free();
+ dict.free();
+ return NULL;
+ }
+ obj.free();
+
+ // make stream
+ str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);
+ str = str->addFilters(&dict);
+
+ return str;
+}
+
+void Gfx::opImageData(Object args[], int numArgs) {
+ error(getPos(), "Internal: got 'ID' operator");
+}
+
+void Gfx::opEndImage(Object args[], int numArgs) {
+ error(getPos(), "Internal: got 'EI' operator");
+}
+
+//------------------------------------------------------------------------
+// type 3 font operators
+//------------------------------------------------------------------------
+
+void Gfx::opSetCharWidth(Object args[], int numArgs) {
+ out->type3D0(state, args[0].getNum(), args[1].getNum());
+}
+
+void Gfx::opSetCacheDevice(Object args[], int numArgs) {
+ out->type3D1(state, args[0].getNum(), args[1].getNum(),
+ args[2].getNum(), args[3].getNum(),
+ args[4].getNum(), args[5].getNum());
+}
+
+//------------------------------------------------------------------------
+// compatibility operators
+//------------------------------------------------------------------------
+
+void Gfx::opBeginIgnoreUndef(Object args[], int numArgs) {
+ ++ignoreUndef;
+}
+
+void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
+ if (ignoreUndef > 0)
+ --ignoreUndef;
+}
+
+//------------------------------------------------------------------------
+// marked content operators
+//------------------------------------------------------------------------
+
+void Gfx::opBeginMarkedContent(Object args[], int numArgs) {
+ if (printCommands) {
+ printf(" marked content: %s ", args[0].getName());
+ if (numArgs == 2)
+ args[2].print(stdout);
+ printf("\n");
+ fflush(stdout);
+ }
+}
+
+void Gfx::opEndMarkedContent(Object args[], int numArgs) {
+}
+
+void Gfx::opMarkPoint(Object args[], int numArgs) {
+ if (printCommands) {
+ printf(" mark point: %s ", args[0].getName());
+ if (numArgs == 2)
+ args[2].print(stdout);
+ printf("\n");
+ fflush(stdout);
+ }
+}
+
+//------------------------------------------------------------------------
+// misc
+//------------------------------------------------------------------------
+
+void Gfx::saveState() {
+ out->saveState(state);
+ state = state->save();
+}
+
+void Gfx::restoreState() {
+ state = state->restore();
+ out->restoreState(state);
+}
+
+void Gfx::pushResources(Dict *resDict) {
+ res = new GfxResources(xref, resDict, res);
+}
+
+void Gfx::popResources() {
+ GfxResources *resPtr;
+
+ resPtr = res->getNext();
+ delete res;
+ res = resPtr;
+}
--- /dev/null
+//========================================================================
+//
+// Gfx.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GFX_H
+#define GFX_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+class GString;
+class XRef;
+class Array;
+class Stream;
+class Parser;
+class Dict;
+class OutputDev;
+class GfxFontDict;
+class GfxFont;
+class GfxPattern;
+class GfxTilingPattern;
+class GfxShadingPattern;
+class GfxShading;
+class GfxFunctionShading;
+class GfxAxialShading;
+class GfxRadialShading;
+class GfxGouraudTriangleShading;
+class GfxPatchMeshShading;
+struct GfxPatch;
+class GfxState;
+struct GfxColor;
+class Gfx;
+class PDFRectangle;
+
+//------------------------------------------------------------------------
+// Gfx
+//------------------------------------------------------------------------
+
+enum GfxClipType {
+ clipNone,
+ clipNormal,
+ clipEO
+};
+
+enum TchkType {
+ tchkBool, // boolean
+ tchkInt, // integer
+ tchkNum, // number (integer or real)
+ tchkString, // string
+ tchkName, // name
+ tchkArray, // array
+ tchkProps, // properties (dictionary or name)
+ tchkSCN, // scn/SCN args (number of name)
+ tchkNone // used to avoid empty initializer lists
+};
+
+#define maxArgs 8
+
+struct Operator {
+ char name[4];
+ int numArgs;
+ TchkType tchk[maxArgs];
+ void (Gfx::*func)(Object args[], int numArgs);
+};
+
+class GfxResources {
+public:
+
+ GfxResources(XRef *xref, Dict *resDict, GfxResources *nextA);
+ ~GfxResources();
+
+ GfxFont *lookupFont(char *name);
+ GBool lookupXObject(char *name, Object *obj);
+ GBool lookupXObjectNF(char *name, Object *obj);
+ void lookupColorSpace(char *name, Object *obj);
+ GfxPattern *lookupPattern(char *name);
+ GfxShading *lookupShading(char *name);
+ GBool lookupGState(char *name, Object *obj);
+
+ GfxResources *getNext() { return next; }
+
+private:
+
+ GfxFontDict *fonts;
+ Object xObjDict;
+ Object colorSpaceDict;
+ Object patternDict;
+ Object shadingDict;
+ Object gStateDict;
+ GfxResources *next;
+};
+
+class Gfx {
+public:
+
+ // Constructor for regular output.
+ Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict,
+ double hDPI, double vDPI, PDFRectangle *box,
+ PDFRectangle *cropBox, int rotate,
+ GBool (*abortCheckCbkA)(void *data) = NULL,
+ void *abortCheckCbkDataA = NULL);
+
+ // Constructor for a sub-page object.
+ Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
+ PDFRectangle *box, PDFRectangle *cropBox,
+ GBool (*abortCheckCbkA)(void *data) = NULL,
+ void *abortCheckCbkDataA = NULL);
+
+ ~Gfx();
+
+ // Interpret a stream or array of streams.
+ void display(Object *obj, GBool topLevel = gTrue);
+
+ // Display an annotation, given its appearance (a Form XObject) and
+ // bounding box (in default user space).
+ void doAnnot(Object *str, double xMin, double yMin,
+ double xMax, double yMax);
+
+ // Save graphics state.
+ void saveState();
+
+ // Restore graphics state.
+ void restoreState();
+
+ // Get the current graphics state object.
+ GfxState *getState() { return state; }
+
+private:
+
+ XRef *xref; // the xref table for this PDF file
+ OutputDev *out; // output device
+ GBool subPage; // is this a sub-page object?
+ GBool printCommands; // print the drawing commands (for debugging)
+ GfxResources *res; // resource stack
+ int updateLevel;
+
+ GfxState *state; // current graphics state
+ GBool fontChanged; // set if font or text matrix has changed
+ GfxClipType clip; // do a clip?
+ int ignoreUndef; // current BX/EX nesting level
+ double baseMatrix[6]; // default matrix for most recent
+ // page/form/pattern
+ int formDepth;
+
+ Parser *parser; // parser for page content stream(s)
+
+ GBool // callback to check for an abort
+ (*abortCheckCbk)(void *data);
+ void *abortCheckCbkData;
+
+ static Operator opTab[]; // table of operators
+
+ void go(GBool topLevel);
+ void execOp(Object *cmd, Object args[], int numArgs);
+ Operator *findOp(char *name);
+ GBool checkArg(Object *arg, TchkType type);
+ int getPos();
+
+ // graphics state operators
+ void opSave(Object args[], int numArgs);
+ void opRestore(Object args[], int numArgs);
+ void opConcat(Object args[], int numArgs);
+ void opSetDash(Object args[], int numArgs);
+ void opSetFlat(Object args[], int numArgs);
+ void opSetLineJoin(Object args[], int numArgs);
+ void opSetLineCap(Object args[], int numArgs);
+ void opSetMiterLimit(Object args[], int numArgs);
+ void opSetLineWidth(Object args[], int numArgs);
+ void opSetExtGState(Object args[], int numArgs);
+ void opSetRenderingIntent(Object args[], int numArgs);
+
+ // color operators
+ void opSetFillGray(Object args[], int numArgs);
+ void opSetStrokeGray(Object args[], int numArgs);
+ void opSetFillCMYKColor(Object args[], int numArgs);
+ void opSetStrokeCMYKColor(Object args[], int numArgs);
+ void opSetFillRGBColor(Object args[], int numArgs);
+ void opSetStrokeRGBColor(Object args[], int numArgs);
+ void opSetFillColorSpace(Object args[], int numArgs);
+ void opSetStrokeColorSpace(Object args[], int numArgs);
+ void opSetFillColor(Object args[], int numArgs);
+ void opSetStrokeColor(Object args[], int numArgs);
+ void opSetFillColorN(Object args[], int numArgs);
+ void opSetStrokeColorN(Object args[], int numArgs);
+
+ // path segment operators
+ void opMoveTo(Object args[], int numArgs);
+ void opLineTo(Object args[], int numArgs);
+ void opCurveTo(Object args[], int numArgs);
+ void opCurveTo1(Object args[], int numArgs);
+ void opCurveTo2(Object args[], int numArgs);
+ void opRectangle(Object args[], int numArgs);
+ void opClosePath(Object args[], int numArgs);
+
+ // path painting operators
+ void opEndPath(Object args[], int numArgs);
+ void opStroke(Object args[], int numArgs);
+ void opCloseStroke(Object args[], int numArgs);
+ void opFill(Object args[], int numArgs);
+ void opEOFill(Object args[], int numArgs);
+ void opFillStroke(Object args[], int numArgs);
+ void opCloseFillStroke(Object args[], int numArgs);
+ void opEOFillStroke(Object args[], int numArgs);
+ void opCloseEOFillStroke(Object args[], int numArgs);
+ void doPatternFill(GBool eoFill);
+ void doTilingPatternFill(GfxTilingPattern *tPat, GBool eoFill);
+ void doShadingPatternFill(GfxShadingPattern *sPat, GBool eoFill);
+ void opShFill(Object args[], int numArgs);
+ void doFunctionShFill(GfxFunctionShading *shading);
+ void doFunctionShFill1(GfxFunctionShading *shading,
+ double x0, double y0,
+ double x1, double y1,
+ GfxColor *colors, int depth);
+ void doAxialShFill(GfxAxialShading *shading);
+ void doRadialShFill(GfxRadialShading *shading);
+ void doGouraudTriangleShFill(GfxGouraudTriangleShading *shading);
+ void gouraudFillTriangle(double x0, double y0, GfxColor *color0,
+ double x1, double y1, GfxColor *color1,
+ double x2, double y2, GfxColor *color2,
+ int nComps, int depth);
+ void doPatchMeshShFill(GfxPatchMeshShading *shading);
+ void fillPatch(GfxPatch *patch, int nComps, int depth);
+ void doEndPath();
+
+ // path clipping operators
+ void opClip(Object args[], int numArgs);
+ void opEOClip(Object args[], int numArgs);
+
+ // text object operators
+ void opBeginText(Object args[], int numArgs);
+ void opEndText(Object args[], int numArgs);
+
+ // text state operators
+ void opSetCharSpacing(Object args[], int numArgs);
+ void opSetFont(Object args[], int numArgs);
+ void opSetTextLeading(Object args[], int numArgs);
+ void opSetTextRender(Object args[], int numArgs);
+ void opSetTextRise(Object args[], int numArgs);
+ void opSetWordSpacing(Object args[], int numArgs);
+ void opSetHorizScaling(Object args[], int numArgs);
+
+ // text positioning operators
+ void opTextMove(Object args[], int numArgs);
+ void opTextMoveSet(Object args[], int numArgs);
+ void opSetTextMatrix(Object args[], int numArgs);
+ void opTextNextLine(Object args[], int numArgs);
+
+ // text string operators
+ void opShowText(Object args[], int numArgs);
+ void opMoveShowText(Object args[], int numArgs);
+ void opMoveSetShowText(Object args[], int numArgs);
+ void opShowSpaceText(Object args[], int numArgs);
+ void doShowText(GString *s);
+
+ // XObject operators
+ void opXObject(Object args[], int numArgs);
+ void doImage(Object *ref, Stream *str, GBool inlineImg);
+ void doForm(Object *str);
+ void doForm1(Object *str, Dict *resDict, double *matrix, double *bbox);
+
+ // in-line image operators
+ void opBeginImage(Object args[], int numArgs);
+ Stream *buildImageStream();
+ void opImageData(Object args[], int numArgs);
+ void opEndImage(Object args[], int numArgs);
+
+ // type 3 font operators
+ void opSetCharWidth(Object args[], int numArgs);
+ void opSetCacheDevice(Object args[], int numArgs);
+
+ // compatibility operators
+ void opBeginIgnoreUndef(Object args[], int numArgs);
+ void opEndIgnoreUndef(Object args[], int numArgs);
+
+ // marked content operators
+ void opBeginMarkedContent(Object args[], int numArgs);
+ void opEndMarkedContent(Object args[], int numArgs);
+ void opMarkPoint(Object args[], int numArgs);
+
+ void pushResources(Dict *resDict);
+ void popResources();
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// GfxFont.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "gmem.h"
+#include "Error.h"
+#include "Object.h"
+#include "Dict.h"
+#include "GlobalParams.h"
+#include "CMap.h"
+#include "CharCodeToUnicode.h"
+#include "FontEncodingTables.h"
+#include "BuiltinFontTables.h"
+#include "FoFiType1.h"
+#include "FoFiType1C.h"
+#include "FoFiTrueType.h"
+#include "GfxFont.h"
+
+//------------------------------------------------------------------------
+
+struct StdFontMapEntry {
+ char *altName;
+ char *properName;
+};
+
+// Acrobat 4.0 and earlier substituted Base14-compatible fonts without
+// providing Widths and a FontDescriptor, so we munge the names into
+// the proper Base14 names. This table is from implementation note 44
+// in the PDF 1.4 spec, with some additions based on empirical
+// evidence.
+static StdFontMapEntry stdFontMap[] = {
+ { "Arial", "Helvetica" },
+ { "Arial,Bold", "Helvetica-Bold" },
+ { "Arial,BoldItalic", "Helvetica-BoldOblique" },
+ { "Arial,Italic", "Helvetica-Oblique" },
+ { "Arial-Bold", "Helvetica-Bold" },
+ { "Arial-BoldItalic", "Helvetica-BoldOblique" },
+ { "Arial-BoldItalicMT", "Helvetica-BoldOblique" },
+ { "Arial-BoldMT", "Helvetica-Bold" },
+ { "Arial-Italic", "Helvetica-Oblique" },
+ { "Arial-ItalicMT", "Helvetica-Oblique" },
+ { "ArialMT", "Helvetica" },
+ { "Courier,Bold", "Courier-Bold" },
+ { "Courier,BoldItalic", "Courier-BoldOblique" },
+ { "Courier,Italic", "Courier-Oblique" },
+ { "CourierNew", "Courier" },
+ { "CourierNew,Bold", "Courier-Bold" },
+ { "CourierNew,BoldItalic", "Courier-BoldOblique" },
+ { "CourierNew,Italic", "Courier-Oblique" },
+ { "CourierNew-Bold", "Courier-Bold" },
+ { "CourierNew-BoldItalic", "Courier-BoldOblique" },
+ { "CourierNew-Italic", "Courier-Oblique" },
+ { "CourierNewPS-BoldItalicMT", "Courier-BoldOblique" },
+ { "CourierNewPS-BoldMT", "Courier-Bold" },
+ { "CourierNewPS-ItalicMT", "Courier-Oblique" },
+ { "CourierNewPSMT", "Courier" },
+ { "Helvetica,Bold", "Helvetica-Bold" },
+ { "Helvetica,BoldItalic", "Helvetica-BoldOblique" },
+ { "Helvetica,Italic", "Helvetica-Oblique" },
+ { "Helvetica-BoldItalic", "Helvetica-BoldOblique" },
+ { "Helvetica-Italic", "Helvetica-Oblique" },
+ { "Symbol,Bold", "Symbol" },
+ { "Symbol,BoldItalic", "Symbol" },
+ { "Symbol,Italic", "Symbol" },
+ { "TimesNewRoman", "Times-Roman" },
+ { "TimesNewRoman,Bold", "Times-Bold" },
+ { "TimesNewRoman,BoldItalic", "Times-BoldItalic" },
+ { "TimesNewRoman,Italic", "Times-Italic" },
+ { "TimesNewRoman-Bold", "Times-Bold" },
+ { "TimesNewRoman-BoldItalic", "Times-BoldItalic" },
+ { "TimesNewRoman-Italic", "Times-Italic" },
+ { "TimesNewRomanPS", "Times-Roman" },
+ { "TimesNewRomanPS-Bold", "Times-Bold" },
+ { "TimesNewRomanPS-BoldItalic", "Times-BoldItalic" },
+ { "TimesNewRomanPS-BoldItalicMT", "Times-BoldItalic" },
+ { "TimesNewRomanPS-BoldMT", "Times-Bold" },
+ { "TimesNewRomanPS-Italic", "Times-Italic" },
+ { "TimesNewRomanPS-ItalicMT", "Times-Italic" },
+ { "TimesNewRomanPSMT", "Times-Roman" },
+ { "TimesNewRomanPSMT,Bold", "Times-Bold" },
+ { "TimesNewRomanPSMT,BoldItalic", "Times-BoldItalic" },
+ { "TimesNewRomanPSMT,Italic", "Times-Italic" }
+};
+
+//------------------------------------------------------------------------
+// GfxFont
+//------------------------------------------------------------------------
+
+GfxFont *GfxFont::makeFont(XRef *xref, char *tagA, Ref idA, Dict *fontDict) {
+ GString *nameA;
+ GfxFont *font;
+ Object obj1;
+
+ // get base font name
+ nameA = NULL;
+ fontDict->lookup("BaseFont", &obj1);
+ if (obj1.isName()) {
+ nameA = new GString(obj1.getName());
+ }
+ obj1.free();
+
+ // get font type
+ font = NULL;
+ fontDict->lookup("Subtype", &obj1);
+ if (obj1.isName("Type1") || obj1.isName("MMType1")) {
+ font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType1, fontDict);
+ } else if (obj1.isName("Type1C")) {
+ font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType1C, fontDict);
+ } else if (obj1.isName("Type3")) {
+ font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType3, fontDict);
+ } else if (obj1.isName("TrueType")) {
+ font = new Gfx8BitFont(xref, tagA, idA, nameA, fontTrueType, fontDict);
+ } else if (obj1.isName("Type0")) {
+ font = new GfxCIDFont(xref, tagA, idA, nameA, fontDict);
+ } else {
+ error(-1, "Unknown font type: '%s'",
+ obj1.isName() ? obj1.getName() : "???");
+ font = new Gfx8BitFont(xref, tagA, idA, nameA, fontUnknownType, fontDict);
+ }
+ obj1.free();
+
+ return font;
+}
+
+GfxFont::GfxFont(char *tagA, Ref idA, GString *nameA) {
+ ok = gFalse;
+ tag = new GString(tagA);
+ id = idA;
+ name = nameA;
+ origName = nameA;
+ embFontName = NULL;
+ extFontFile = NULL;
+}
+
+GfxFont::~GfxFont() {
+ delete tag;
+ if (origName && origName != name) {
+ delete origName;
+ }
+ if (name) {
+ delete name;
+ }
+ if (embFontName) {
+ delete embFontName;
+ }
+ if (extFontFile) {
+ delete extFontFile;
+ }
+}
+
+void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) {
+ Object obj1, obj2, obj3, obj4;
+ double t;
+ int i;
+
+ // assume Times-Roman by default (for substitution purposes)
+ flags = fontSerif;
+
+ embFontID.num = -1;
+ embFontID.gen = -1;
+ missingWidth = 0;
+
+ if (fontDict->lookup("FontDescriptor", &obj1)->isDict()) {
+
+ // get flags
+ if (obj1.dictLookup("Flags", &obj2)->isInt()) {
+ flags = obj2.getInt();
+ }
+ obj2.free();
+
+ // get name
+ obj1.dictLookup("FontName", &obj2);
+ if (obj2.isName()) {
+ embFontName = new GString(obj2.getName());
+ }
+ obj2.free();
+
+ // look for embedded font file
+ if (obj1.dictLookupNF("FontFile", &obj2)->isRef()) {
+ embFontID = obj2.getRef();
+ if (type != fontType1) {
+ error(-1, "Mismatch between font type and embedded font file");
+ type = fontType1;
+ }
+ }
+ obj2.free();
+ if (embFontID.num == -1 &&
+ obj1.dictLookupNF("FontFile2", &obj2)->isRef()) {
+ embFontID = obj2.getRef();
+ if (type != fontTrueType && type != fontCIDType2) {
+ error(-1, "Mismatch between font type and embedded font file");
+ type = type == fontCIDType0 ? fontCIDType2 : fontTrueType;
+ }
+ }
+ obj2.free();
+ if (embFontID.num == -1 &&
+ obj1.dictLookupNF("FontFile3", &obj2)->isRef()) {
+ if (obj2.fetch(xref, &obj3)->isStream()) {
+ obj3.streamGetDict()->lookup("Subtype", &obj4);
+ if (obj4.isName("Type1")) {
+ embFontID = obj2.getRef();
+ if (type != fontType1) {
+ error(-1, "Mismatch between font type and embedded font file");
+ type = fontType1;
+ }
+ } else if (obj4.isName("Type1C")) {
+ embFontID = obj2.getRef();
+ if (type != fontType1 && type != fontType1C) {
+ error(-1, "Mismatch between font type and embedded font file");
+ }
+ type = fontType1C;
+ } else if (obj4.isName("TrueType")) {
+ embFontID = obj2.getRef();
+ if (type != fontTrueType) {
+ error(-1, "Mismatch between font type and embedded font file");
+ type = fontTrueType;
+ }
+ } else if (obj4.isName("CIDFontType0C")) {
+ embFontID = obj2.getRef();
+ if (type != fontCIDType0) {
+ error(-1, "Mismatch between font type and embedded font file");
+ }
+ type = fontCIDType0C;
+ } else {
+ error(-1, "Unknown embedded font type '%s'",
+ obj4.isName() ? obj4.getName() : "???");
+ }
+ obj4.free();
+ }
+ obj3.free();
+ }
+ obj2.free();
+
+ // look for MissingWidth
+ obj1.dictLookup("MissingWidth", &obj2);
+ if (obj2.isNum()) {
+ missingWidth = obj2.getNum();
+ }
+ obj2.free();
+
+ // get Ascent and Descent
+ obj1.dictLookup("Ascent", &obj2);
+ if (obj2.isNum()) {
+ t = 0.001 * obj2.getNum();
+ // some broken font descriptors set ascent and descent to 0
+ if (t != 0) {
+ ascent = t;
+ }
+ }
+ obj2.free();
+ obj1.dictLookup("Descent", &obj2);
+ if (obj2.isNum()) {
+ t = 0.001 * obj2.getNum();
+ // some broken font descriptors set ascent and descent to 0
+ if (t != 0) {
+ descent = t;
+ }
+ // some broken font descriptors specify a positive descent
+ if (descent > 0) {
+ descent = -descent;
+ }
+ }
+ obj2.free();
+
+ // font FontBBox
+ if (obj1.dictLookup("FontBBox", &obj2)->isArray()) {
+ for (i = 0; i < 4 && i < obj2.arrayGetLength(); ++i) {
+ if (obj2.arrayGet(i, &obj3)->isNum()) {
+ fontBBox[i] = 0.001 * obj3.getNum();
+ }
+ obj3.free();
+ }
+ }
+ obj2.free();
+
+ }
+ obj1.free();
+}
+
+CharCodeToUnicode *GfxFont::readToUnicodeCMap(Dict *fontDict, int nBits,
+ CharCodeToUnicode *ctu) {
+ GString *buf;
+ Object obj1;
+ int c;
+
+ if (!fontDict->lookup("ToUnicode", &obj1)->isStream()) {
+ obj1.free();
+ return NULL;
+ }
+ buf = new GString();
+ obj1.streamReset();
+ while ((c = obj1.streamGetChar()) != EOF) {
+ buf->append(c);
+ }
+ obj1.streamClose();
+ obj1.free();
+ if (ctu) {
+ ctu->mergeCMap(buf, nBits);
+ } else {
+ ctu = CharCodeToUnicode::parseCMap(buf, nBits);
+ }
+ delete buf;
+ return ctu;
+}
+
+void GfxFont::findExtFontFile() {
+ static char *type1Exts[] = { ".pfa", ".pfb", ".ps", "", NULL };
+ static char *ttExts[] = { ".ttf", NULL };
+
+ if (name) {
+ if (type == fontType1) {
+ extFontFile = globalParams->findFontFile(name, type1Exts);
+ } else if (type == fontTrueType) {
+ extFontFile = globalParams->findFontFile(name, ttExts);
+ }
+ }
+}
+
+char *GfxFont::readExtFontFile(int *len) {
+ FILE *f;
+ char *buf;
+
+ if (!(f = fopen(extFontFile->getCString(), "rb"))) {
+ error(-1, "External font file '%s' vanished", extFontFile->getCString());
+ return NULL;
+ }
+ fseek(f, 0, SEEK_END);
+ *len = (int)ftell(f);
+ fseek(f, 0, SEEK_SET);
+ buf = (char *)gmalloc(*len);
+ if ((int)fread(buf, 1, *len, f) != *len) {
+ error(-1, "Error reading external font file '%s'",
+ extFontFile->getCString());
+ }
+ fclose(f);
+ return buf;
+}
+
+char *GfxFont::readEmbFontFile(XRef *xref, int *len) {
+ char *buf;
+ Object obj1, obj2;
+ Stream *str;
+ int c;
+ int size, i;
+
+ obj1.initRef(embFontID.num, embFontID.gen);
+ obj1.fetch(xref, &obj2);
+ if (!obj2.isStream()) {
+ error(-1, "Embedded font file is not a stream");
+ obj2.free();
+ obj1.free();
+ embFontID.num = -1;
+ return NULL;
+ }
+ str = obj2.getStream();
+
+ buf = NULL;
+ i = size = 0;
+ str->reset();
+ while ((c = str->getChar()) != EOF) {
+ if (i == size) {
+ size += 4096;
+ buf = (char *)grealloc(buf, size);
+ }
+ buf[i++] = c;
+ }
+ *len = i;
+ str->close();
+
+ obj2.free();
+ obj1.free();
+
+ return buf;
+}
+
+//------------------------------------------------------------------------
+// Gfx8BitFont
+//------------------------------------------------------------------------
+
+Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
+ GfxFontType typeA, Dict *fontDict):
+ GfxFont(tagA, idA, nameA)
+{
+ GString *name2;
+ BuiltinFont *builtinFont;
+ char **baseEnc;
+ GBool baseEncFromFontFile;
+ char *buf;
+ int len;
+ FoFiType1 *ffT1;
+ FoFiType1C *ffT1C;
+ int code, code2;
+ char *charName;
+ GBool missing, hex;
+ Unicode toUnicode[256];
+ CharCodeToUnicode *utu, *ctu2;
+ Unicode uBuf[8];
+ double mul;
+ int firstChar, lastChar;
+ Gushort w;
+ Object obj1, obj2, obj3;
+ int n, i, a, b, m;
+
+ type = typeA;
+ ctu = NULL;
+
+ // do font name substitution for various aliases of the Base 14 font
+ // names
+ if (name) {
+ name2 = name->copy();
+ i = 0;
+ while (i < name2->getLength()) {
+ if (name2->getChar(i) == ' ') {
+ name2->del(i);
+ } else {
+ ++i;
+ }
+ }
+ a = 0;
+ b = sizeof(stdFontMap) / sizeof(StdFontMapEntry);
+ // invariant: stdFontMap[a].altName <= name2 < stdFontMap[b].altName
+ while (b - a > 1) {
+ m = (a + b) / 2;
+ if (name2->cmp(stdFontMap[m].altName) >= 0) {
+ a = m;
+ } else {
+ b = m;
+ }
+ }
+ if (!name2->cmp(stdFontMap[a].altName)) {
+ name = new GString(stdFontMap[a].properName);
+ }
+ delete name2;
+ }
+
+ // is it a built-in font?
+ builtinFont = NULL;
+ if (name) {
+ for (i = 0; i < nBuiltinFonts; ++i) {
+ if (!name->cmp(builtinFonts[i].name)) {
+ builtinFont = &builtinFonts[i];
+ break;
+ }
+ }
+ }
+
+ // default ascent/descent values
+ if (builtinFont) {
+ ascent = 0.001 * builtinFont->ascent;
+ descent = 0.001 * builtinFont->descent;
+ fontBBox[0] = 0.001 * builtinFont->bbox[0];
+ fontBBox[1] = 0.001 * builtinFont->bbox[1];
+ fontBBox[2] = 0.001 * builtinFont->bbox[2];
+ fontBBox[3] = 0.001 * builtinFont->bbox[3];
+ } else {
+ ascent = 0.95;
+ descent = -0.35;
+ fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0;
+ }
+
+ // get info from font descriptor
+ readFontDescriptor(xref, fontDict);
+
+ // for non-embedded fonts, don't trust the ascent/descent/bbox
+ // values from the font descriptor
+ if (builtinFont && embFontID.num < 0) {
+ ascent = 0.001 * builtinFont->ascent;
+ descent = 0.001 * builtinFont->descent;
+ fontBBox[0] = 0.001 * builtinFont->bbox[0];
+ fontBBox[1] = 0.001 * builtinFont->bbox[1];
+ fontBBox[2] = 0.001 * builtinFont->bbox[2];
+ fontBBox[3] = 0.001 * builtinFont->bbox[3];
+ }
+
+ // look for an external font file
+ findExtFontFile();
+
+ // get font matrix
+ fontMat[0] = fontMat[3] = 1;
+ fontMat[1] = fontMat[2] = fontMat[4] = fontMat[5] = 0;
+ if (fontDict->lookup("FontMatrix", &obj1)->isArray()) {
+ for (i = 0; i < 6 && i < obj1.arrayGetLength(); ++i) {
+ if (obj1.arrayGet(i, &obj2)->isNum()) {
+ fontMat[i] = obj2.getNum();
+ }
+ obj2.free();
+ }
+ }
+ obj1.free();
+
+ // get Type 3 bounding box, font definition, and resources
+ if (type == fontType3) {
+ if (fontDict->lookup("FontBBox", &obj1)->isArray()) {
+ for (i = 0; i < 4 && i < obj1.arrayGetLength(); ++i) {
+ if (obj1.arrayGet(i, &obj2)->isNum()) {
+ fontBBox[i] = obj2.getNum();
+ }
+ obj2.free();
+ }
+ }
+ obj1.free();
+ if (!fontDict->lookup("CharProcs", &charProcs)->isDict()) {
+ error(-1, "Missing or invalid CharProcs dictionary in Type 3 font");
+ charProcs.free();
+ }
+ if (!fontDict->lookup("Resources", &resources)->isDict()) {
+ resources.free();
+ }
+ }
+
+ //----- build the font encoding -----
+
+ // Encodings start with a base encoding, which can come from
+ // (in order of priority):
+ // 1. FontDict.Encoding or FontDict.Encoding.BaseEncoding
+ // - MacRoman / MacExpert / WinAnsi / Standard
+ // 2. embedded or external font file
+ // 3. default:
+ // - builtin --> builtin encoding
+ // - TrueType --> WinAnsiEncoding
+ // - others --> StandardEncoding
+ // and then add a list of differences (if any) from
+ // FontDict.Encoding.Differences.
+
+ // check FontDict for base encoding
+ hasEncoding = gFalse;
+ usesMacRomanEnc = gFalse;
+ baseEnc = NULL;
+ baseEncFromFontFile = gFalse;
+ fontDict->lookup("Encoding", &obj1);
+ if (obj1.isDict()) {
+ obj1.dictLookup("BaseEncoding", &obj2);
+ if (obj2.isName("MacRomanEncoding")) {
+ hasEncoding = gTrue;
+ usesMacRomanEnc = gTrue;
+ baseEnc = macRomanEncoding;
+ } else if (obj2.isName("MacExpertEncoding")) {
+ hasEncoding = gTrue;
+ baseEnc = macExpertEncoding;
+ } else if (obj2.isName("WinAnsiEncoding")) {
+ hasEncoding = gTrue;
+ baseEnc = winAnsiEncoding;
+ }
+ obj2.free();
+ } else if (obj1.isName("MacRomanEncoding")) {
+ hasEncoding = gTrue;
+ usesMacRomanEnc = gTrue;
+ baseEnc = macRomanEncoding;
+ } else if (obj1.isName("MacExpertEncoding")) {
+ hasEncoding = gTrue;
+ baseEnc = macExpertEncoding;
+ } else if (obj1.isName("WinAnsiEncoding")) {
+ hasEncoding = gTrue;
+ baseEnc = winAnsiEncoding;
+ }
+
+ // check embedded or external font file for base encoding
+ // (only for Type 1 fonts - trying to get an encoding out of a
+ // TrueType font is a losing proposition)
+ ffT1 = NULL;
+ ffT1C = NULL;
+ buf = NULL;
+ if (type == fontType1 && (extFontFile || embFontID.num >= 0)) {
+ if (extFontFile) {
+ ffT1 = FoFiType1::load(extFontFile->getCString());
+ } else {
+ buf = readEmbFontFile(xref, &len);
+ ffT1 = FoFiType1::make(buf, len);
+ }
+ if (ffT1) {
+ if (ffT1->getName()) {
+ if (embFontName) {
+ delete embFontName;
+ }
+ embFontName = new GString(ffT1->getName());
+ }
+ if (!baseEnc) {
+ baseEnc = ffT1->getEncoding();
+ baseEncFromFontFile = gTrue;
+ }
+ }
+ } else if (type == fontType1C && (extFontFile || embFontID.num >= 0)) {
+ if (extFontFile) {
+ ffT1C = FoFiType1C::load(extFontFile->getCString());
+ } else {
+ buf = readEmbFontFile(xref, &len);
+ ffT1C = FoFiType1C::make(buf, len);
+ }
+ if (ffT1C) {
+ if (ffT1C->getName()) {
+ if (embFontName) {
+ delete embFontName;
+ }
+ embFontName = new GString(ffT1C->getName());
+ }
+ if (!baseEnc) {
+ baseEnc = ffT1C->getEncoding();
+ baseEncFromFontFile = gTrue;
+ }
+ }
+ }
+ if (buf) {
+ gfree(buf);
+ }
+
+ // get default base encoding
+ if (!baseEnc) {
+ if (builtinFont && embFontID.num < 0) {
+ baseEnc = builtinFont->defaultBaseEnc;
+ hasEncoding = gTrue;
+ } else if (type == fontTrueType) {
+ baseEnc = winAnsiEncoding;
+ } else {
+ baseEnc = standardEncoding;
+ }
+ }
+
+ // copy the base encoding
+ for (i = 0; i < 256; ++i) {
+ enc[i] = baseEnc[i];
+ if ((encFree[i] = baseEncFromFontFile) && enc[i]) {
+ enc[i] = copyString(baseEnc[i]);
+ }
+ }
+
+ // some Type 1C font files have empty encodings, which can break the
+ // T1C->T1 conversion (since the 'seac' operator depends on having
+ // the accents in the encoding), so we fill in any gaps from
+ // StandardEncoding
+ if (type == fontType1C && (extFontFile || embFontID.num >= 0) &&
+ baseEncFromFontFile) {
+ for (i = 0; i < 256; ++i) {
+ if (!enc[i] && standardEncoding[i]) {
+ enc[i] = standardEncoding[i];
+ encFree[i] = gFalse;
+ }
+ }
+ }
+
+ // merge differences into encoding
+ if (obj1.isDict()) {
+ obj1.dictLookup("Differences", &obj2);
+ if (obj2.isArray()) {
+ hasEncoding = gTrue;
+ code = 0;
+ for (i = 0; i < obj2.arrayGetLength(); ++i) {
+ obj2.arrayGet(i, &obj3);
+ if (obj3.isInt()) {
+ code = obj3.getInt();
+ } else if (obj3.isName()) {
+ if (code >= 0 && code < 256) {
+ if (encFree[code]) {
+ gfree(enc[code]);
+ }
+ enc[code] = copyString(obj3.getName());
+ encFree[code] = gTrue;
+ }
+ ++code;
+ } else {
+ error(-1, "Wrong type in font encoding resource differences (%s)",
+ obj3.getTypeName());
+ }
+ obj3.free();
+ }
+ }
+ obj2.free();
+ }
+ obj1.free();
+ if (ffT1) {
+ delete ffT1;
+ }
+ if (ffT1C) {
+ delete ffT1C;
+ }
+
+ //----- build the mapping to Unicode -----
+
+ // pass 1: use the name-to-Unicode mapping table
+ missing = hex = gFalse;
+ for (code = 0; code < 256; ++code) {
+ if ((charName = enc[code])) {
+ if (!(toUnicode[code] = globalParams->mapNameToUnicode(charName)) &&
+ strcmp(charName, ".notdef")) {
+ // if it wasn't in the name-to-Unicode table, check for a
+ // name that looks like 'Axx' or 'xx', where 'A' is any letter
+ // and 'xx' is two hex digits
+ if ((strlen(charName) == 3 &&
+ isalpha(charName[0]) &&
+ isxdigit(charName[1]) && isxdigit(charName[2]) &&
+ ((charName[1] >= 'a' && charName[1] <= 'f') ||
+ (charName[1] >= 'A' && charName[1] <= 'F') ||
+ (charName[2] >= 'a' && charName[2] <= 'f') ||
+ (charName[2] >= 'A' && charName[2] <= 'F'))) ||
+ (strlen(charName) == 2 &&
+ isxdigit(charName[0]) && isxdigit(charName[1]) &&
+ ((charName[0] >= 'a' && charName[0] <= 'f') ||
+ (charName[0] >= 'A' && charName[0] <= 'F') ||
+ (charName[1] >= 'a' && charName[1] <= 'f') ||
+ (charName[1] >= 'A' && charName[1] <= 'F')))) {
+ hex = gTrue;
+ }
+ missing = gTrue;
+ }
+ } else {
+ toUnicode[code] = 0;
+ }
+ }
+
+ // pass 2: try to fill in the missing chars, looking for names of
+ // the form 'Axx', 'xx', 'Ann', 'ABnn', or 'nn', where 'A' and 'B'
+ // are any letters, 'xx' is two hex digits, and 'nn' is 2-4
+ // decimal digits
+ if (missing && globalParams->getMapNumericCharNames()) {
+ for (code = 0; code < 256; ++code) {
+ if ((charName = enc[code]) && !toUnicode[code] &&
+ strcmp(charName, ".notdef")) {
+ n = strlen(charName);
+ code2 = -1;
+ if (hex && n == 3 && isalpha(charName[0]) &&
+ isxdigit(charName[1]) && isxdigit(charName[2])) {
+ sscanf(charName+1, "%x", &code2);
+ } else if (hex && n == 2 &&
+ isxdigit(charName[0]) && isxdigit(charName[1])) {
+ sscanf(charName, "%x", &code2);
+ } else if (!hex && n >= 2 && n <= 4 &&
+ isdigit(charName[0]) && isdigit(charName[1])) {
+ code2 = atoi(charName);
+ } else if (n >= 3 && n <= 5 &&
+ isdigit(charName[1]) && isdigit(charName[2])) {
+ code2 = atoi(charName+1);
+ } else if (n >= 4 && n <= 6 &&
+ isdigit(charName[2]) && isdigit(charName[3])) {
+ code2 = atoi(charName+2);
+ }
+ if (code2 >= 0 && code2 <= 0xff) {
+ toUnicode[code] = (Unicode)code2;
+ }
+ }
+ }
+ }
+
+ // construct the char code -> Unicode mapping object
+ ctu = CharCodeToUnicode::make8BitToUnicode(toUnicode);
+
+ // merge in a ToUnicode CMap, if there is one -- this overwrites
+ // existing entries in ctu, i.e., the ToUnicode CMap takes
+ // precedence, but the other encoding info is allowed to fill in any
+ // holes
+ readToUnicodeCMap(fontDict, 8, ctu);
+
+ // look for a Unicode-to-Unicode mapping
+ if (name && (utu = globalParams->getUnicodeToUnicode(name))) {
+ for (i = 0; i < 256; ++i) {
+ toUnicode[i] = 0;
+ }
+ ctu2 = CharCodeToUnicode::make8BitToUnicode(toUnicode);
+ for (i = 0; i < 256; ++i) {
+ n = ctu->mapToUnicode((CharCode)i, uBuf, 8);
+ if (n >= 1) {
+ n = utu->mapToUnicode((CharCode)uBuf[0], uBuf, 8);
+ if (n >= 1) {
+ ctu2->setMapping((CharCode)i, uBuf, n);
+ }
+ }
+ }
+ utu->decRefCnt();
+ delete ctu;
+ ctu = ctu2;
+ }
+
+ //----- get the character widths -----
+
+ // initialize all widths
+ for (code = 0; code < 256; ++code) {
+ widths[code] = missingWidth * 0.001;
+ }
+
+ // use widths from font dict, if present
+ fontDict->lookup("FirstChar", &obj1);
+ firstChar = obj1.isInt() ? obj1.getInt() : 0;
+ obj1.free();
+ if (firstChar < 0 || firstChar > 255) {
+ firstChar = 0;
+ }
+ fontDict->lookup("LastChar", &obj1);
+ lastChar = obj1.isInt() ? obj1.getInt() : 255;
+ obj1.free();
+ if (lastChar < 0 || lastChar > 255) {
+ lastChar = 255;
+ }
+ mul = (type == fontType3) ? fontMat[0] : 0.001;
+ fontDict->lookup("Widths", &obj1);
+ if (obj1.isArray()) {
+ flags |= fontFixedWidth;
+ if (obj1.arrayGetLength() < lastChar - firstChar + 1) {
+ lastChar = firstChar + obj1.arrayGetLength() - 1;
+ }
+ for (code = firstChar; code <= lastChar; ++code) {
+ obj1.arrayGet(code - firstChar, &obj2);
+ if (obj2.isNum()) {
+ widths[code] = obj2.getNum() * mul;
+ if (widths[code] != widths[firstChar]) {
+ flags &= ~fontFixedWidth;
+ }
+ }
+ obj2.free();
+ }
+
+ // use widths from built-in font
+ } else if (builtinFont) {
+ // this is a kludge for broken PDF files that encode char 32
+ // as .notdef
+ if (builtinFont->widths->getWidth("space", &w)) {
+ widths[32] = 0.001 * w;
+ }
+ for (code = 0; code < 256; ++code) {
+ if (enc[code] && builtinFont->widths->getWidth(enc[code], &w)) {
+ widths[code] = 0.001 * w;
+ }
+ }
+
+ // couldn't find widths -- use defaults
+ } else {
+ // this is technically an error -- the Widths entry is required
+ // for all but the Base-14 fonts -- but certain PDF generators
+ // apparently don't include widths for Arial and TimesNewRoman
+ if (isFixedWidth()) {
+ i = 0;
+ } else if (isSerif()) {
+ i = 8;
+ } else {
+ i = 4;
+ }
+ if (isBold()) {
+ i += 2;
+ }
+ if (isItalic()) {
+ i += 1;
+ }
+ builtinFont = builtinFontSubst[i];
+ // this is a kludge for broken PDF files that encode char 32
+ // as .notdef
+ if (builtinFont->widths->getWidth("space", &w)) {
+ widths[32] = 0.001 * w;
+ }
+ for (code = 0; code < 256; ++code) {
+ if (enc[code] && builtinFont->widths->getWidth(enc[code], &w)) {
+ widths[code] = 0.001 * w;
+ }
+ }
+ }
+ obj1.free();
+
+ ok = gTrue;
+}
+
+Gfx8BitFont::~Gfx8BitFont() {
+ int i;
+
+ for (i = 0; i < 256; ++i) {
+ if (encFree[i] && enc[i]) {
+ gfree(enc[i]);
+ }
+ }
+ ctu->decRefCnt();
+ if (charProcs.isDict()) {
+ charProcs.free();
+ }
+ if (resources.isDict()) {
+ resources.free();
+ }
+}
+
+int Gfx8BitFont::getNextChar(char *s, int len, CharCode *code,
+ Unicode *u, int uSize, int *uLen,
+ double *dx, double *dy, double *ox, double *oy) {
+ CharCode c;
+
+ *code = c = (CharCode)(*s & 0xff);
+ *uLen = ctu->mapToUnicode(c, u, uSize);
+ *dx = widths[c];
+ *dy = *ox = *oy = 0;
+ return 1;
+}
+
+CharCodeToUnicode *Gfx8BitFont::getToUnicode() {
+ ctu->incRefCnt();
+ return ctu;
+}
+
+Gushort *Gfx8BitFont::getCodeToGIDMap(FoFiTrueType *ff) {
+ Gushort *map;
+ int cmapPlatform, cmapEncoding;
+ int unicodeCmap, macRomanCmap, msSymbolCmap, cmap;
+ GBool useMacRoman, useUnicode;
+ char *charName;
+ Unicode u;
+ int code, i, n;
+
+ map = (Gushort *)gmallocn(256, sizeof(Gushort));
+ for (i = 0; i < 256; ++i) {
+ map[i] = 0;
+ }
+
+ // To match up with the Adobe-defined behaviour, we choose a cmap
+ // like this:
+ // 1. If the PDF font has an encoding:
+ // 1a. If the PDF font specified MacRomanEncoding and the
+ // TrueType font has a Macintosh Roman cmap, use it, and
+ // reverse map the char names through MacRomanEncoding to
+ // get char codes.
+ // 1b. If the TrueType font has a Microsoft Unicode cmap or a
+ // non-Microsoft Unicode cmap, use it, and use the Unicode
+ // indexes, not the char codes.
+ // 1c. If the PDF font is symbolic and the TrueType font has a
+ // Microsoft Symbol cmap, use it, and use char codes
+ // directly (possibly with an offset of 0xf000).
+ // 1d. If the TrueType font has a Macintosh Roman cmap, use it,
+ // as in case 1a.
+ // 2. If the PDF font does not have an encoding or the PDF font is
+ // symbolic:
+ // 2a. If the TrueType font has a Macintosh Roman cmap, use it,
+ // and use char codes directly (possibly with an offset of
+ // 0xf000).
+ // 2b. If the TrueType font has a Microsoft Symbol cmap, use it,
+ // and use char codes directly (possible with an offset of
+ // 0xf000).
+ // 3. If none of these rules apply, use the first cmap and hope for
+ // the best (this shouldn't happen).
+ unicodeCmap = macRomanCmap = msSymbolCmap = -1;
+ for (i = 0; i < ff->getNumCmaps(); ++i) {
+ cmapPlatform = ff->getCmapPlatform(i);
+ cmapEncoding = ff->getCmapEncoding(i);
+ if ((cmapPlatform == 3 && cmapEncoding == 1) ||
+ cmapPlatform == 0) {
+ unicodeCmap = i;
+ } else if (cmapPlatform == 1 && cmapEncoding == 0) {
+ macRomanCmap = i;
+ } else if (cmapPlatform == 3 && cmapEncoding == 0) {
+ msSymbolCmap = i;
+ }
+ }
+ cmap = 0;
+ useMacRoman = gFalse;
+ useUnicode = gFalse;
+ if (hasEncoding) {
+ if (usesMacRomanEnc && macRomanCmap >= 0) {
+ cmap = macRomanCmap;
+ useMacRoman = gTrue;
+ } else if (unicodeCmap >= 0) {
+ cmap = unicodeCmap;
+ useUnicode = gTrue;
+ } else if ((flags & fontSymbolic) && msSymbolCmap >= 0) {
+ cmap = msSymbolCmap;
+ } else if ((flags & fontSymbolic) && macRomanCmap >= 0) {
+ cmap = macRomanCmap;
+ } else if (macRomanCmap >= 0) {
+ cmap = macRomanCmap;
+ useMacRoman = gTrue;
+ }
+ } else {
+ if (macRomanCmap >= 0) {
+ cmap = macRomanCmap;
+ } else if (msSymbolCmap >= 0) {
+ cmap = msSymbolCmap;
+ }
+ }
+
+ // reverse map the char names through MacRomanEncoding, then map the
+ // char codes through the cmap
+ if (useMacRoman) {
+ for (i = 0; i < 256; ++i) {
+ if ((charName = enc[i])) {
+ if ((code = globalParams->getMacRomanCharCode(charName))) {
+ map[i] = ff->mapCodeToGID(cmap, code);
+ }
+ }
+ }
+
+ // map Unicode through the cmap
+ } else if (useUnicode) {
+ for (i = 0; i < 256; ++i) {
+ if (((charName = enc[i]) &&
+ (u = globalParams->mapNameToUnicode(charName))) ||
+ (n = ctu->mapToUnicode((CharCode)i, &u, 1))) {
+ map[i] = ff->mapCodeToGID(cmap, u);
+ }
+ }
+
+ // map the char codes through the cmap, possibly with an offset of
+ // 0xf000
+ } else {
+ for (i = 0; i < 256; ++i) {
+ if (!(map[i] = ff->mapCodeToGID(cmap, i))) {
+ map[i] = ff->mapCodeToGID(cmap, 0xf000 + i);
+ }
+ }
+ }
+
+ // try the TrueType 'post' table to handle any unmapped characters
+ for (i = 0; i < 256; ++i) {
+ if (!map[i] && (charName = enc[i])) {
+ map[i] = (Gushort)(int)ff->mapNameToGID(charName);
+ }
+ }
+
+ return map;
+}
+
+Dict *Gfx8BitFont::getCharProcs() {
+ return charProcs.isDict() ? charProcs.getDict() : (Dict *)NULL;
+}
+
+Object *Gfx8BitFont::getCharProc(int code, Object *proc) {
+ if (enc[code] && charProcs.isDict()) {
+ charProcs.dictLookup(enc[code], proc);
+ } else {
+ proc->initNull();
+ }
+ return proc;
+}
+
+Dict *Gfx8BitFont::getResources() {
+ return resources.isDict() ? resources.getDict() : (Dict *)NULL;
+}
+
+//------------------------------------------------------------------------
+// GfxCIDFont
+//------------------------------------------------------------------------
+
+static int CDECL cmpWidthExcep(const void *w1, const void *w2) {
+ return ((GfxFontCIDWidthExcep *)w1)->first -
+ ((GfxFontCIDWidthExcep *)w2)->first;
+}
+
+static int CDECL cmpWidthExcepV(const void *w1, const void *w2) {
+ return ((GfxFontCIDWidthExcepV *)w1)->first -
+ ((GfxFontCIDWidthExcepV *)w2)->first;
+}
+
+GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
+ Dict *fontDict):
+ GfxFont(tagA, idA, nameA)
+{
+ Dict *desFontDict;
+ GString *collection, *cMapName;
+ Object desFontDictObj;
+ Object obj1, obj2, obj3, obj4, obj5, obj6;
+ CharCodeToUnicode *utu;
+ CharCode c;
+ Unicode uBuf[8];
+ int c1, c2;
+ int excepsSize, i, j, k, n;
+
+ ascent = 0.95;
+ descent = -0.35;
+ fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0;
+ cMap = NULL;
+ ctu = NULL;
+ widths.defWidth = 1.0;
+ widths.defHeight = -1.0;
+ widths.defVY = 0.880;
+ widths.exceps = NULL;
+ widths.nExceps = 0;
+ widths.excepsV = NULL;
+ widths.nExcepsV = 0;
+ cidToGID = NULL;
+ cidToGIDLen = 0;
+
+ // get the descendant font
+ if (!fontDict->lookup("DescendantFonts", &obj1)->isArray()) {
+ error(-1, "Missing DescendantFonts entry in Type 0 font");
+ obj1.free();
+ goto err1;
+ }
+ if (!obj1.arrayGet(0, &desFontDictObj)->isDict()) {
+ error(-1, "Bad descendant font in Type 0 font");
+ goto err3;
+ }
+ obj1.free();
+ desFontDict = desFontDictObj.getDict();
+
+ // font type
+ if (!desFontDict->lookup("Subtype", &obj1)) {
+ error(-1, "Missing Subtype entry in Type 0 descendant font");
+ goto err3;
+ }
+ if (obj1.isName("CIDFontType0")) {
+ type = fontCIDType0;
+ } else if (obj1.isName("CIDFontType2")) {
+ type = fontCIDType2;
+ } else {
+ error(-1, "Unknown Type 0 descendant font type '%s'",
+ obj1.isName() ? obj1.getName() : "???");
+ goto err3;
+ }
+ obj1.free();
+
+ // get info from font descriptor
+ readFontDescriptor(xref, desFontDict);
+
+ // look for an external font file
+ findExtFontFile();
+
+ //----- encoding info -----
+
+ // char collection
+ if (!desFontDict->lookup("CIDSystemInfo", &obj1)->isDict()) {
+ error(-1, "Missing CIDSystemInfo dictionary in Type 0 descendant font");
+ goto err3;
+ }
+ obj1.dictLookup("Registry", &obj2);
+ obj1.dictLookup("Ordering", &obj3);
+ if (!obj2.isString() || !obj3.isString()) {
+ error(-1, "Invalid CIDSystemInfo dictionary in Type 0 descendant font");
+ goto err4;
+ }
+ collection = obj2.getString()->copy()->append('-')->append(obj3.getString());
+ obj3.free();
+ obj2.free();
+ obj1.free();
+
+ // look for a ToUnicode CMap
+ if (!(ctu = readToUnicodeCMap(fontDict, 16, NULL))) {
+
+ // the "Adobe-Identity" and "Adobe-UCS" collections don't have
+ // cidToUnicode files
+ if (collection->cmp("Adobe-Identity") &&
+ collection->cmp("Adobe-UCS")) {
+
+ // look for a user-supplied .cidToUnicode file
+ if (!(ctu = globalParams->getCIDToUnicode(collection))) {
+ error(-1, "Unknown character collection '%s'",
+ collection->getCString());
+ // fall-through, assuming the Identity mapping -- this appears
+ // to match Adobe's behavior
+ }
+ }
+ }
+
+ // look for a Unicode-to-Unicode mapping
+ if (name && (utu = globalParams->getUnicodeToUnicode(name))) {
+ if (ctu) {
+ for (c = 0; c < ctu->getLength(); ++c) {
+ n = ctu->mapToUnicode(c, uBuf, 8);
+ if (n >= 1) {
+ n = utu->mapToUnicode((CharCode)uBuf[0], uBuf, 8);
+ if (n >= 1) {
+ ctu->setMapping(c, uBuf, n);
+ }
+ }
+ }
+ utu->decRefCnt();
+ } else {
+ ctu = utu;
+ }
+ }
+
+ // encoding (i.e., CMap)
+ //~ need to handle a CMap stream here
+ //~ also need to deal with the UseCMap entry in the stream dict
+ if (!fontDict->lookup("Encoding", &obj1)->isName()) {
+ error(-1, "Missing or invalid Encoding entry in Type 0 font");
+ delete collection;
+ goto err3;
+ }
+ cMapName = new GString(obj1.getName());
+ obj1.free();
+ if (!(cMap = globalParams->getCMap(collection, cMapName))) {
+ error(-1, "Unknown CMap '%s' for character collection '%s'",
+ cMapName->getCString(), collection->getCString());
+ delete collection;
+ delete cMapName;
+ goto err2;
+ }
+ delete collection;
+ delete cMapName;
+
+ // CIDToGIDMap (for embedded TrueType fonts)
+ if (type == fontCIDType2) {
+ desFontDict->lookup("CIDToGIDMap", &obj1);
+ if (obj1.isStream()) {
+ cidToGIDLen = 0;
+ i = 64;
+ cidToGID = (Gushort *)gmallocn(i, sizeof(Gushort));
+ obj1.streamReset();
+ while ((c1 = obj1.streamGetChar()) != EOF &&
+ (c2 = obj1.streamGetChar()) != EOF) {
+ if (cidToGIDLen == i) {
+ i *= 2;
+ cidToGID = (Gushort *)greallocn(cidToGID, i, sizeof(Gushort));
+ }
+ cidToGID[cidToGIDLen++] = (Gushort)((c1 << 8) + c2);
+ }
+ } else if (!obj1.isName("Identity") && !obj1.isNull()) {
+ error(-1, "Invalid CIDToGIDMap entry in CID font");
+ }
+ obj1.free();
+ }
+
+ //----- character metrics -----
+
+ // default char width
+ if (desFontDict->lookup("DW", &obj1)->isInt()) {
+ widths.defWidth = obj1.getInt() * 0.001;
+ }
+ obj1.free();
+
+ // char width exceptions
+ if (desFontDict->lookup("W", &obj1)->isArray()) {
+ excepsSize = 0;
+ i = 0;
+ while (i + 1 < obj1.arrayGetLength()) {
+ obj1.arrayGet(i, &obj2);
+ obj1.arrayGet(i + 1, &obj3);
+ if (obj2.isInt() && obj3.isInt() && i + 2 < obj1.arrayGetLength()) {
+ if (obj1.arrayGet(i + 2, &obj4)->isNum()) {
+ if (widths.nExceps == excepsSize) {
+ excepsSize += 16;
+ widths.exceps = (GfxFontCIDWidthExcep *)
+ greallocn(widths.exceps,
+ excepsSize, sizeof(GfxFontCIDWidthExcep));
+ }
+ widths.exceps[widths.nExceps].first = obj2.getInt();
+ widths.exceps[widths.nExceps].last = obj3.getInt();
+ widths.exceps[widths.nExceps].width = obj4.getNum() * 0.001;
+ ++widths.nExceps;
+ } else {
+ error(-1, "Bad widths array in Type 0 font");
+ }
+ obj4.free();
+ i += 3;
+ } else if (obj2.isInt() && obj3.isArray()) {
+ if (widths.nExceps + obj3.arrayGetLength() > excepsSize) {
+ excepsSize = (widths.nExceps + obj3.arrayGetLength() + 15) & ~15;
+ widths.exceps = (GfxFontCIDWidthExcep *)
+ greallocn(widths.exceps,
+ excepsSize, sizeof(GfxFontCIDWidthExcep));
+ }
+ j = obj2.getInt();
+ for (k = 0; k < obj3.arrayGetLength(); ++k) {
+ if (obj3.arrayGet(k, &obj4)->isNum()) {
+ widths.exceps[widths.nExceps].first = j;
+ widths.exceps[widths.nExceps].last = j;
+ widths.exceps[widths.nExceps].width = obj4.getNum() * 0.001;
+ ++j;
+ ++widths.nExceps;
+ } else {
+ error(-1, "Bad widths array in Type 0 font");
+ }
+ obj4.free();
+ }
+ i += 2;
+ } else {
+ error(-1, "Bad widths array in Type 0 font");
+ ++i;
+ }
+ obj3.free();
+ obj2.free();
+ }
+ qsort(widths.exceps, widths.nExceps, sizeof(GfxFontCIDWidthExcep),
+ &cmpWidthExcep);
+ }
+ obj1.free();
+
+ // default metrics for vertical font
+ if (desFontDict->lookup("DW2", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 2) {
+ if (obj1.arrayGet(0, &obj2)->isNum()) {
+ widths.defVY = obj2.getNum() * 0.001;
+ }
+ obj2.free();
+ if (obj1.arrayGet(1, &obj2)->isNum()) {
+ widths.defHeight = obj2.getNum() * 0.001;
+ }
+ obj2.free();
+ }
+ obj1.free();
+
+ // char metric exceptions for vertical font
+ if (desFontDict->lookup("W2", &obj1)->isArray()) {
+ excepsSize = 0;
+ i = 0;
+ while (i + 1 < obj1.arrayGetLength()) {
+ obj1.arrayGet(i, &obj2);
+ obj1.arrayGet(i+ 1, &obj3);
+ if (obj2.isInt() && obj3.isInt() && i + 4 < obj1.arrayGetLength()) {
+ if (obj1.arrayGet(i + 2, &obj4)->isNum() &&
+ obj1.arrayGet(i + 3, &obj5)->isNum() &&
+ obj1.arrayGet(i + 4, &obj6)->isNum()) {
+ if (widths.nExcepsV == excepsSize) {
+ excepsSize += 16;
+ widths.excepsV = (GfxFontCIDWidthExcepV *)
+ greallocn(widths.excepsV,
+ excepsSize, sizeof(GfxFontCIDWidthExcepV));
+ }
+ widths.excepsV[widths.nExcepsV].first = obj2.getInt();
+ widths.excepsV[widths.nExcepsV].last = obj3.getInt();
+ widths.excepsV[widths.nExcepsV].height = obj4.getNum() * 0.001;
+ widths.excepsV[widths.nExcepsV].vx = obj5.getNum() * 0.001;
+ widths.excepsV[widths.nExcepsV].vy = obj6.getNum() * 0.001;
+ ++widths.nExcepsV;
+ } else {
+ error(-1, "Bad widths (W2) array in Type 0 font");
+ }
+ obj6.free();
+ obj5.free();
+ obj4.free();
+ i += 5;
+ } else if (obj2.isInt() && obj3.isArray()) {
+ if (widths.nExcepsV + obj3.arrayGetLength() / 3 > excepsSize) {
+ excepsSize =
+ (widths.nExcepsV + obj3.arrayGetLength() / 3 + 15) & ~15;
+ widths.excepsV = (GfxFontCIDWidthExcepV *)
+ greallocn(widths.excepsV,
+ excepsSize, sizeof(GfxFontCIDWidthExcepV));
+ }
+ j = obj2.getInt();
+ for (k = 0; k < obj3.arrayGetLength(); k += 3) {
+ if (obj3.arrayGet(k, &obj4)->isNum() &&
+ obj3.arrayGet(k+1, &obj5)->isNum() &&
+ obj3.arrayGet(k+2, &obj6)->isNum()) {
+ widths.excepsV[widths.nExceps].first = j;
+ widths.excepsV[widths.nExceps].last = j;
+ widths.excepsV[widths.nExceps].height = obj4.getNum() * 0.001;
+ widths.excepsV[widths.nExceps].vx = obj5.getNum() * 0.001;
+ widths.excepsV[widths.nExceps].vy = obj6.getNum() * 0.001;
+ ++j;
+ ++widths.nExcepsV;
+ } else {
+ error(-1, "Bad widths (W2) array in Type 0 font");
+ }
+ obj6.free();
+ obj5.free();
+ obj4.free();
+ }
+ i += 2;
+ } else {
+ error(-1, "Bad widths (W2) array in Type 0 font");
+ ++i;
+ }
+ obj3.free();
+ obj2.free();
+ }
+ qsort(widths.excepsV, widths.nExcepsV, sizeof(GfxFontCIDWidthExcepV),
+ &cmpWidthExcepV);
+ }
+ obj1.free();
+
+ desFontDictObj.free();
+ ok = gTrue;
+ return;
+
+ err4:
+ obj3.free();
+ obj2.free();
+ err3:
+ obj1.free();
+ err2:
+ desFontDictObj.free();
+ err1:;
+}
+
+GfxCIDFont::~GfxCIDFont() {
+ if (cMap) {
+ cMap->decRefCnt();
+ }
+ if (ctu) {
+ ctu->decRefCnt();
+ }
+ gfree(widths.exceps);
+ gfree(widths.excepsV);
+ if (cidToGID) {
+ gfree(cidToGID);
+ }
+}
+
+int GfxCIDFont::getNextChar(char *s, int len, CharCode *code,
+ Unicode *u, int uSize, int *uLen,
+ double *dx, double *dy, double *ox, double *oy) {
+ CID cid;
+ double w, h, vx, vy;
+ int n, a, b, m;
+
+ if (!cMap) {
+ *code = 0;
+ *uLen = 0;
+ *dx = *dy = 0;
+ return 1;
+ }
+
+ *code = (CharCode)(cid = cMap->getCID(s, len, &n));
+ if (ctu) {
+ *uLen = ctu->mapToUnicode(cid, u, uSize);
+ } else {
+ *uLen = 0;
+ }
+
+ // horizontal
+ if (cMap->getWMode() == 0) {
+ w = widths.defWidth;
+ h = vx = vy = 0;
+ if (widths.nExceps > 0 && cid >= widths.exceps[0].first) {
+ a = 0;
+ b = widths.nExceps;
+ // invariant: widths.exceps[a].first <= cid < widths.exceps[b].first
+ while (b - a > 1) {
+ m = (a + b) / 2;
+ if (widths.exceps[m].first <= cid) {
+ a = m;
+ } else {
+ b = m;
+ }
+ }
+ if (cid <= widths.exceps[a].last) {
+ w = widths.exceps[a].width;
+ }
+ }
+
+ // vertical
+ } else {
+ w = 0;
+ h = widths.defHeight;
+ vx = widths.defWidth / 2;
+ vy = widths.defVY;
+ if (widths.nExcepsV > 0 && cid >= widths.excepsV[0].first) {
+ a = 0;
+ b = widths.nExcepsV;
+ // invariant: widths.excepsV[a].first <= cid < widths.excepsV[b].first
+ while (b - a > 1) {
+ m = (a + b) / 2;
+ if (widths.excepsV[m].last <= cid) {
+ a = m;
+ } else {
+ b = m;
+ }
+ }
+ if (cid <= widths.excepsV[a].last) {
+ h = widths.excepsV[a].height;
+ vx = widths.excepsV[a].vx;
+ vy = widths.excepsV[a].vy;
+ }
+ }
+ }
+
+ *dx = w;
+ *dy = h;
+ *ox = vx;
+ *oy = vy;
+
+ return n;
+}
+
+int GfxCIDFont::getWMode() {
+ return cMap ? cMap->getWMode() : 0;
+}
+
+CharCodeToUnicode *GfxCIDFont::getToUnicode() {
+ if (ctu) {
+ ctu->incRefCnt();
+ }
+ return ctu;
+}
+
+GString *GfxCIDFont::getCollection() {
+ return cMap ? cMap->getCollection() : (GString *)NULL;
+}
+
+//------------------------------------------------------------------------
+// GfxFontDict
+//------------------------------------------------------------------------
+
+GfxFontDict::GfxFontDict(XRef *xref, Ref *fontDictRef, Dict *fontDict) {
+ int i;
+ Object obj1, obj2;
+ Ref r;
+
+ numFonts = fontDict->getLength();
+ fonts = (GfxFont **)gmallocn(numFonts, sizeof(GfxFont *));
+ for (i = 0; i < numFonts; ++i) {
+ fontDict->getValNF(i, &obj1);
+ obj1.fetch(xref, &obj2);
+ if (obj2.isDict()) {
+ if (obj1.isRef()) {
+ r = obj1.getRef();
+ } else {
+ // no indirect reference for this font, so invent a unique one
+ // (legal generation numbers are five digits, so any 6-digit
+ // number would be safe)
+ r.num = i;
+ if (fontDictRef) {
+ r.gen = 100000 + fontDictRef->num;
+ } else {
+ r.gen = 999999;
+ }
+ }
+ fonts[i] = GfxFont::makeFont(xref, fontDict->getKey(i),
+ r, obj2.getDict());
+ if (fonts[i] && !fonts[i]->isOk()) {
+ delete fonts[i];
+ fonts[i] = NULL;
+ }
+ } else {
+ error(-1, "font resource is not a dictionary");
+ fonts[i] = NULL;
+ }
+ obj1.free();
+ obj2.free();
+ }
+}
+
+GfxFontDict::~GfxFontDict() {
+ int i;
+
+ for (i = 0; i < numFonts; ++i) {
+ if (fonts[i]) {
+ delete fonts[i];
+ }
+ }
+ gfree(fonts);
+}
+
+GfxFont *GfxFontDict::lookup(char *tag) {
+ int i;
+
+ for (i = 0; i < numFonts; ++i) {
+ if (fonts[i] && fonts[i]->matches(tag)) {
+ return fonts[i];
+ }
+ }
+ return NULL;
+}
--- /dev/null
+//========================================================================
+//
+// GfxFont.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GFXFONT_H
+#define GFXFONT_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "GString.h"
+#include "Object.h"
+#include "CharTypes.h"
+
+class Dict;
+class CMap;
+class CharCodeToUnicode;
+class FoFiTrueType;
+struct GfxFontCIDWidths;
+
+//------------------------------------------------------------------------
+// GfxFontType
+//------------------------------------------------------------------------
+
+enum GfxFontType {
+ //----- Gfx8BitFont
+ fontUnknownType,
+ fontType1,
+ fontType1C,
+ fontType3,
+ fontTrueType,
+ //----- GfxCIDFont
+ fontCIDType0,
+ fontCIDType0C,
+ fontCIDType2
+};
+
+//------------------------------------------------------------------------
+// GfxFontCIDWidths
+//------------------------------------------------------------------------
+
+struct GfxFontCIDWidthExcep {
+ CID first; // this record applies to
+ CID last; // CIDs <first>..<last>
+ double width; // char width
+};
+
+struct GfxFontCIDWidthExcepV {
+ CID first; // this record applies to
+ CID last; // CIDs <first>..<last>
+ double height; // char height
+ double vx, vy; // origin position
+};
+
+struct GfxFontCIDWidths {
+ double defWidth; // default char width
+ double defHeight; // default char height
+ double defVY; // default origin position
+ GfxFontCIDWidthExcep *exceps; // exceptions
+ int nExceps; // number of valid entries in exceps
+ GfxFontCIDWidthExcepV * // exceptions for vertical font
+ excepsV;
+ int nExcepsV; // number of valid entries in excepsV
+};
+
+//------------------------------------------------------------------------
+// GfxFont
+//------------------------------------------------------------------------
+
+#define fontFixedWidth (1 << 0)
+#define fontSerif (1 << 1)
+#define fontSymbolic (1 << 2)
+#define fontItalic (1 << 6)
+#define fontBold (1 << 18)
+
+class GfxFont {
+public:
+
+ // Build a GfxFont object.
+ static GfxFont *makeFont(XRef *xref, char *tagA, Ref idA, Dict *fontDict);
+
+ GfxFont(char *tagA, Ref idA, GString *nameA);
+
+ virtual ~GfxFont();
+
+ GBool isOk() { return ok; }
+
+ // Get font tag.
+ GString *getTag() { return tag; }
+
+ // Get font dictionary ID.
+ Ref *getID() { return &id; }
+
+ // Does this font match the tag?
+ GBool matches(char *tagA) { return !tag->cmp(tagA); }
+
+ // Get base font name.
+ GString *getName() { return name; }
+
+ // Get the original font name (ignornig any munging that might have
+ // been done to map to a canonical Base-14 font name).
+ GString *getOrigName() { return origName; }
+
+ // Get font type.
+ GfxFontType getType() { return type; }
+ virtual GBool isCIDFont() { return gFalse; }
+
+ // Get embedded font ID, i.e., a ref for the font file stream.
+ // Returns false if there is no embedded font.
+ GBool getEmbeddedFontID(Ref *embID)
+ { *embID = embFontID; return embFontID.num >= 0; }
+
+ // Get the PostScript font name for the embedded font. Returns
+ // NULL if there is no embedded font.
+ GString *getEmbeddedFontName() { return embFontName; }
+
+ // Get the name of the external font file. Returns NULL if there
+ // is no external font file.
+ GString *getExtFontFile() { return extFontFile; }
+
+ // Get font descriptor flags.
+ GBool isFixedWidth() { return flags & fontFixedWidth; }
+ GBool isSerif() { return flags & fontSerif; }
+ GBool isSymbolic() { return flags & fontSymbolic; }
+ GBool isItalic() { return flags & fontItalic; }
+ GBool isBold() { return flags & fontBold; }
+
+ // Return the font matrix.
+ double *getFontMatrix() { return fontMat; }
+
+ // Return the font bounding box.
+ double *getFontBBox() { return fontBBox; }
+
+ // Return the ascent and descent values.
+ double getAscent() { return ascent; }
+ double getDescent() { return descent; }
+
+ // Return the writing mode (0=horizontal, 1=vertical).
+ virtual int getWMode() { return 0; }
+
+ // Read an external or embedded font file into a buffer.
+ char *readExtFontFile(int *len);
+ char *readEmbFontFile(XRef *xref, int *len);
+
+ // Get the next char from a string <s> of <len> bytes, returning the
+ // char <code>, its Unicode mapping <u>, its displacement vector
+ // (<dx>, <dy>), and its origin offset vector (<ox>, <oy>). <uSize>
+ // is the number of entries available in <u>, and <uLen> is set to
+ // the number actually used. Returns the number of bytes used by
+ // the char code.
+ virtual int getNextChar(char *s, int len, CharCode *code,
+ Unicode *u, int uSize, int *uLen,
+ double *dx, double *dy, double *ox, double *oy) = 0;
+
+protected:
+
+ void readFontDescriptor(XRef *xref, Dict *fontDict);
+ CharCodeToUnicode *readToUnicodeCMap(Dict *fontDict, int nBits,
+ CharCodeToUnicode *ctu);
+ void findExtFontFile();
+
+ GString *tag; // PDF font tag
+ Ref id; // reference (used as unique ID)
+ GString *name; // font name
+ GString *origName; // original font name
+ GfxFontType type; // type of font
+ int flags; // font descriptor flags
+ GString *embFontName; // name of embedded font
+ Ref embFontID; // ref to embedded font file stream
+ GString *extFontFile; // external font file name
+ double fontMat[6]; // font matrix (Type 3 only)
+ double fontBBox[4]; // font bounding box (Type 3 only)
+ double missingWidth; // "default" width
+ double ascent; // max height above baseline
+ double descent; // max depth below baseline
+ GBool ok;
+};
+
+//------------------------------------------------------------------------
+// Gfx8BitFont
+//------------------------------------------------------------------------
+
+class Gfx8BitFont: public GfxFont {
+public:
+
+ Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
+ GfxFontType typeA, Dict *fontDict);
+
+ virtual ~Gfx8BitFont();
+
+ virtual int getNextChar(char *s, int len, CharCode *code,
+ Unicode *u, int uSize, int *uLen,
+ double *dx, double *dy, double *ox, double *oy);
+
+ // Return the encoding.
+ char **getEncoding() { return enc; }
+
+ // Return the Unicode map.
+ CharCodeToUnicode *getToUnicode();
+
+ // Return the character name associated with <code>.
+ char *getCharName(int code) { return code>=256?0:enc[code]; }
+
+ // Returns true if the PDF font specified an encoding.
+ GBool getHasEncoding() { return hasEncoding; }
+
+ // Returns true if the PDF font specified MacRomanEncoding.
+ GBool getUsesMacRomanEnc() { return usesMacRomanEnc; }
+
+ // Get width of a character.
+ double getWidth(Guchar c) { return widths[c]; }
+
+ // Return a char code-to-GID mapping for the provided font file.
+ // (This is only useful for TrueType fonts.)
+ Gushort *getCodeToGIDMap(FoFiTrueType *ff);
+
+ // Return the Type 3 CharProc dictionary, or NULL if none.
+ Dict *getCharProcs();
+
+ // Return the Type 3 CharProc for the character associated with <code>.
+ Object *getCharProc(int code, Object *proc);
+
+ // Return the Type 3 Resources dictionary, or NULL if none.
+ Dict *getResources();
+
+private:
+
+ char *enc[256]; // char code --> char name
+ char encFree[256]; // boolean for each char name: if set,
+ // the string is malloc'ed
+ CharCodeToUnicode *ctu; // char code --> Unicode
+ GBool hasEncoding;
+ GBool usesMacRomanEnc;
+ double widths[256]; // character widths
+ Object charProcs; // Type 3 CharProcs dictionary
+ Object resources; // Type 3 Resources dictionary
+};
+
+//------------------------------------------------------------------------
+// GfxCIDFont
+//------------------------------------------------------------------------
+
+class GfxCIDFont: public GfxFont {
+public:
+
+ GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
+ Dict *fontDict);
+
+ virtual ~GfxCIDFont();
+
+ virtual GBool isCIDFont() { return gTrue; }
+
+ virtual int getNextChar(char *s, int len, CharCode *code,
+ Unicode *u, int uSize, int *uLen,
+ double *dx, double *dy, double *ox, double *oy);
+
+ // Return the writing mode (0=horizontal, 1=vertical).
+ virtual int getWMode();
+
+ // Return the Unicode map.
+ CharCodeToUnicode *getToUnicode();
+
+ // Get the collection name (<registry>-<ordering>).
+ GString *getCollection();
+
+ // Return the CID-to-GID mapping table. These should only be called
+ // if type is fontCIDType2.
+ Gushort *getCIDToGID() { return cidToGID; }
+ int getCIDToGIDLen() { return cidToGIDLen; }
+
+private:
+
+ CMap *cMap; // char code --> CID
+ CharCodeToUnicode *ctu; // CID --> Unicode
+ GfxFontCIDWidths widths; // character widths
+ Gushort *cidToGID; // CID --> GID mapping (for embedded
+ // TrueType fonts)
+ int cidToGIDLen;
+};
+
+//------------------------------------------------------------------------
+// GfxFontDict
+//------------------------------------------------------------------------
+
+class GfxFontDict {
+public:
+
+ // Build the font dictionary, given the PDF font dictionary.
+ GfxFontDict(XRef *xref, Ref *fontDictRef, Dict *fontDict);
+
+ // Destructor.
+ ~GfxFontDict();
+
+ // Get the specified font.
+ GfxFont *lookup(char *tag);
+
+ // Iterative access.
+ int getNumFonts() { return numFonts; }
+ GfxFont *getFont(int i) { return fonts[i]; }
+
+private:
+
+ GfxFont **fonts; // list of fonts
+ int numFonts; // number of fonts
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// GfxState.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stddef.h>
+#include <math.h>
+#include <string.h>
+#include "gmem.h"
+#include "Error.h"
+#include "Object.h"
+#include "Array.h"
+#include "Page.h"
+#include "GfxState.h"
+#include "cmyk.h"
+
+//------------------------------------------------------------------------
+
+static inline GfxColorComp clip01(GfxColorComp x) {
+ return (x < 0) ? 0 : (x > gfxColorComp1) ? gfxColorComp1 : x;
+}
+
+static inline double clip01(double x) {
+ return (x < 0) ? 0 : (x > 1) ? 1 : x;
+}
+
+//------------------------------------------------------------------------
+
+static struct {
+ char *name;
+ GfxBlendMode mode;
+} gfxBlendModeNames[] = {
+ { "Normal", gfxBlendNormal },
+ { "Compatible", gfxBlendNormal },
+ { "Multiply", gfxBlendMultiply },
+ { "Screen", gfxBlendScreen },
+ { "Overlay", gfxBlendOverlay },
+ { "Darken", gfxBlendDarken },
+ { "Lighten", gfxBlendLighten },
+ { "ColorDodge", gfxBlendColorDodge },
+ { "ColorBurn", gfxBlendColorBurn },
+ { "HardLight", gfxBlendHardLight },
+ { "SoftLight", gfxBlendSoftLight },
+ { "Difference", gfxBlendDifference },
+ { "Exclusion", gfxBlendExclusion },
+ { "Hue", gfxBlendHue },
+ { "Saturation", gfxBlendSaturation },
+ { "Color", gfxBlendColor },
+ { "Luminosity", gfxBlendLuminosity }
+};
+
+#define nGfxBlendModeNames \
+ ((int)((sizeof(gfxBlendModeNames) / sizeof(char *))))
+
+//------------------------------------------------------------------------
+
+// NB: This must match the GfxColorSpaceMode enum defined in
+// GfxState.h
+static char *gfxColorSpaceModeNames[] = {
+ "DeviceGray",
+ "CalGray",
+ "DeviceRGB",
+ "CalRGB",
+ "DeviceCMYK",
+ "Lab",
+ "ICCBased",
+ "Indexed",
+ "Separation",
+ "DeviceN",
+ "Pattern"
+};
+
+#define nGfxColorSpaceModes ((sizeof(gfxColorSpaceModeNames) / sizeof(char *)))
+
+//------------------------------------------------------------------------
+// GfxColorSpace
+//------------------------------------------------------------------------
+
+GfxColorSpace::GfxColorSpace() {
+}
+
+GfxColorSpace::~GfxColorSpace() {
+}
+
+GfxColorSpace *GfxColorSpace::parse(Object *csObj) {
+ GfxColorSpace *cs;
+ Object obj1;
+
+ cs = NULL;
+ if (csObj->isName()) {
+ if (csObj->isName("DeviceGray") || csObj->isName("G")) {
+ cs = new GfxDeviceGrayColorSpace();
+ } else if (csObj->isName("DeviceRGB") || csObj->isName("RGB")) {
+ cs = new GfxDeviceRGBColorSpace();
+ } else if (csObj->isName("DeviceCMYK") || csObj->isName("CMYK")) {
+ cs = new GfxDeviceCMYKColorSpace();
+ } else if (csObj->isName("Pattern")) {
+ cs = new GfxPatternColorSpace(NULL);
+ } else {
+ error(-1, "Bad color space '%s'", csObj->getName());
+ }
+ } else if (csObj->isArray()) {
+ csObj->arrayGet(0, &obj1);
+ if (obj1.isName("DeviceGray") || obj1.isName("G")) {
+ cs = new GfxDeviceGrayColorSpace();
+ } else if (obj1.isName("DeviceRGB") || obj1.isName("RGB")) {
+ cs = new GfxDeviceRGBColorSpace();
+ } else if (obj1.isName("DeviceCMYK") || obj1.isName("CMYK")) {
+ cs = new GfxDeviceCMYKColorSpace();
+ } else if (obj1.isName("CalGray")) {
+ cs = GfxCalGrayColorSpace::parse(csObj->getArray());
+ } else if (obj1.isName("CalRGB")) {
+ cs = GfxCalRGBColorSpace::parse(csObj->getArray());
+ } else if (obj1.isName("Lab")) {
+ cs = GfxLabColorSpace::parse(csObj->getArray());
+ } else if (obj1.isName("ICCBased")) {
+ cs = GfxICCBasedColorSpace::parse(csObj->getArray());
+ } else if (obj1.isName("Indexed") || obj1.isName("I")) {
+ cs = GfxIndexedColorSpace::parse(csObj->getArray());
+ } else if (obj1.isName("Separation")) {
+ cs = GfxSeparationColorSpace::parse(csObj->getArray());
+ } else if (obj1.isName("DeviceN")) {
+ cs = GfxDeviceNColorSpace::parse(csObj->getArray());
+ } else if (obj1.isName("Pattern")) {
+ cs = GfxPatternColorSpace::parse(csObj->getArray());
+ } else {
+ error(-1, "Bad color space");
+ }
+ obj1.free();
+ } else {
+ error(-1, "Bad color space - expected name or array");
+ }
+ return cs;
+}
+
+void GfxColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
+ int maxImgPixel) {
+ int i;
+
+ for (i = 0; i < getNComps(); ++i) {
+ decodeLow[i] = 0;
+ decodeRange[i] = 1;
+ }
+}
+
+int GfxColorSpace::getNumColorSpaceModes() {
+ return nGfxColorSpaceModes;
+}
+
+char *GfxColorSpace::getColorSpaceModeName(int idx) {
+ return gfxColorSpaceModeNames[idx];
+}
+
+//------------------------------------------------------------------------
+// GfxDeviceGrayColorSpace
+//------------------------------------------------------------------------
+
+GfxDeviceGrayColorSpace::GfxDeviceGrayColorSpace() {
+}
+
+GfxDeviceGrayColorSpace::~GfxDeviceGrayColorSpace() {
+}
+
+GfxColorSpace *GfxDeviceGrayColorSpace::copy() {
+ return new GfxDeviceGrayColorSpace();
+}
+
+void GfxDeviceGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+ *gray = clip01(color->c[0]);
+}
+
+void GfxDeviceGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+ rgb->r = rgb->g = rgb->b = clip01(color->c[0]);
+}
+
+void GfxDeviceGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+ cmyk->c = cmyk->m = cmyk->y = 0;
+ cmyk->k = clip01(gfxColorComp1 - color->c[0]);
+}
+
+//------------------------------------------------------------------------
+// GfxCalGrayColorSpace
+//------------------------------------------------------------------------
+
+GfxCalGrayColorSpace::GfxCalGrayColorSpace() {
+ whiteX = whiteY = whiteZ = 1;
+ blackX = blackY = blackZ = 0;
+ gamma = 1;
+}
+
+GfxCalGrayColorSpace::~GfxCalGrayColorSpace() {
+}
+
+GfxColorSpace *GfxCalGrayColorSpace::copy() {
+ GfxCalGrayColorSpace *cs;
+
+ cs = new GfxCalGrayColorSpace();
+ cs->whiteX = whiteX;
+ cs->whiteY = whiteY;
+ cs->whiteZ = whiteZ;
+ cs->blackX = blackX;
+ cs->blackY = blackY;
+ cs->blackZ = blackZ;
+ cs->gamma = gamma;
+ return cs;
+}
+
+GfxColorSpace *GfxCalGrayColorSpace::parse(Array *arr) {
+ GfxCalGrayColorSpace *cs;
+ Object obj1, obj2, obj3;
+
+ arr->get(1, &obj1);
+ if (!obj1.isDict()) {
+ error(-1, "Bad CalGray color space");
+ obj1.free();
+ return NULL;
+ }
+ cs = new GfxCalGrayColorSpace();
+ if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
+ obj2.arrayGetLength() == 3) {
+ obj2.arrayGet(0, &obj3);
+ cs->whiteX = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(1, &obj3);
+ cs->whiteY = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(2, &obj3);
+ cs->whiteZ = obj3.getNum();
+ obj3.free();
+ }
+ obj2.free();
+ if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
+ obj2.arrayGetLength() == 3) {
+ obj2.arrayGet(0, &obj3);
+ cs->blackX = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(1, &obj3);
+ cs->blackY = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(2, &obj3);
+ cs->blackZ = obj3.getNum();
+ obj3.free();
+ }
+ obj2.free();
+ if (obj1.dictLookup("Gamma", &obj2)->isNum()) {
+ cs->gamma = obj2.getNum();
+ }
+ obj2.free();
+ obj1.free();
+ return cs;
+}
+
+void GfxCalGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+ *gray = clip01(color->c[0]);
+}
+
+void GfxCalGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+ rgb->r = rgb->g = rgb->b = clip01(color->c[0]);
+}
+
+void GfxCalGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+ cmyk->c = cmyk->m = cmyk->y = 0;
+ cmyk->k = clip01(gfxColorComp1 - color->c[0]);
+}
+
+//------------------------------------------------------------------------
+// GfxDeviceRGBColorSpace
+//------------------------------------------------------------------------
+
+GfxDeviceRGBColorSpace::GfxDeviceRGBColorSpace() {
+}
+
+GfxDeviceRGBColorSpace::~GfxDeviceRGBColorSpace() {
+}
+
+GfxColorSpace *GfxDeviceRGBColorSpace::copy() {
+ return new GfxDeviceRGBColorSpace();
+}
+
+void GfxDeviceRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+ *gray = clip01((GfxColorComp)(0.3 * color->c[0] +
+ 0.59 * color->c[1] +
+ 0.11 * color->c[2] + 0.5));
+}
+
+void GfxDeviceRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+ rgb->r = clip01(color->c[0]);
+ rgb->g = clip01(color->c[1]);
+ rgb->b = clip01(color->c[2]);
+}
+
+void GfxDeviceRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+ GfxColorComp c, m, y, k;
+
+ c = clip01(gfxColorComp1 - color->c[0]);
+ m = clip01(gfxColorComp1 - color->c[1]);
+ y = clip01(gfxColorComp1 - color->c[2]);
+ k = c;
+ if (m < k) {
+ k = m;
+ }
+ if (y < k) {
+ k = y;
+ }
+ cmyk->c = c - k;
+ cmyk->m = m - k;
+ cmyk->y = y - k;
+ cmyk->k = k;
+}
+
+//------------------------------------------------------------------------
+// GfxCalRGBColorSpace
+//------------------------------------------------------------------------
+
+GfxCalRGBColorSpace::GfxCalRGBColorSpace() {
+ whiteX = whiteY = whiteZ = 1;
+ blackX = blackY = blackZ = 0;
+ gammaR = gammaG = gammaB = 1;
+ mat[0] = 1; mat[1] = 0; mat[2] = 0;
+ mat[3] = 0; mat[4] = 1; mat[5] = 0;
+ mat[6] = 0; mat[7] = 0; mat[8] = 1;
+}
+
+GfxCalRGBColorSpace::~GfxCalRGBColorSpace() {
+}
+
+GfxColorSpace *GfxCalRGBColorSpace::copy() {
+ GfxCalRGBColorSpace *cs;
+ int i;
+
+ cs = new GfxCalRGBColorSpace();
+ cs->whiteX = whiteX;
+ cs->whiteY = whiteY;
+ cs->whiteZ = whiteZ;
+ cs->blackX = blackX;
+ cs->blackY = blackY;
+ cs->blackZ = blackZ;
+ cs->gammaR = gammaR;
+ cs->gammaG = gammaG;
+ cs->gammaB = gammaB;
+ for (i = 0; i < 9; ++i) {
+ cs->mat[i] = mat[i];
+ }
+ return cs;
+}
+
+GfxColorSpace *GfxCalRGBColorSpace::parse(Array *arr) {
+ GfxCalRGBColorSpace *cs;
+ Object obj1, obj2, obj3;
+ int i;
+
+ arr->get(1, &obj1);
+ if (!obj1.isDict()) {
+ error(-1, "Bad CalRGB color space");
+ obj1.free();
+ return NULL;
+ }
+ cs = new GfxCalRGBColorSpace();
+ if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
+ obj2.arrayGetLength() == 3) {
+ obj2.arrayGet(0, &obj3);
+ cs->whiteX = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(1, &obj3);
+ cs->whiteY = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(2, &obj3);
+ cs->whiteZ = obj3.getNum();
+ obj3.free();
+ }
+ obj2.free();
+ if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
+ obj2.arrayGetLength() == 3) {
+ obj2.arrayGet(0, &obj3);
+ cs->blackX = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(1, &obj3);
+ cs->blackY = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(2, &obj3);
+ cs->blackZ = obj3.getNum();
+ obj3.free();
+ }
+ obj2.free();
+ if (obj1.dictLookup("Gamma", &obj2)->isArray() &&
+ obj2.arrayGetLength() == 3) {
+ obj2.arrayGet(0, &obj3);
+ cs->gammaR = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(1, &obj3);
+ cs->gammaG = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(2, &obj3);
+ cs->gammaB = obj3.getNum();
+ obj3.free();
+ }
+ obj2.free();
+ if (obj1.dictLookup("Matrix", &obj2)->isArray() &&
+ obj2.arrayGetLength() == 9) {
+ for (i = 0; i < 9; ++i) {
+ obj2.arrayGet(i, &obj3);
+ cs->mat[i] = obj3.getNum();
+ obj3.free();
+ }
+ }
+ obj2.free();
+ obj1.free();
+ return cs;
+}
+
+void GfxCalRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+ *gray = clip01((GfxColorComp)(0.299 * color->c[0] +
+ 0.587 * color->c[1] +
+ 0.114 * color->c[2] + 0.5));
+}
+
+void GfxCalRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+ rgb->r = clip01(color->c[0]);
+ rgb->g = clip01(color->c[1]);
+ rgb->b = clip01(color->c[2]);
+}
+
+void GfxCalRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+ GfxColorComp c, m, y, k;
+
+ c = clip01(gfxColorComp1 - color->c[0]);
+ m = clip01(gfxColorComp1 - color->c[1]);
+ y = clip01(gfxColorComp1 - color->c[2]);
+ k = c;
+ if (m < k) {
+ k = m;
+ }
+ if (y < k) {
+ k = y;
+ }
+ cmyk->c = c - k;
+ cmyk->m = m - k;
+ cmyk->y = y - k;
+ cmyk->k = k;
+}
+
+//------------------------------------------------------------------------
+// GfxDeviceCMYKColorSpace
+//------------------------------------------------------------------------
+
+GfxDeviceCMYKColorSpace::GfxDeviceCMYKColorSpace() {
+}
+
+GfxDeviceCMYKColorSpace::~GfxDeviceCMYKColorSpace() {
+}
+
+GfxColorSpace *GfxDeviceCMYKColorSpace::copy() {
+ return new GfxDeviceCMYKColorSpace();
+}
+
+void GfxDeviceCMYKColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+ *gray = clip01((GfxColorComp)(gfxColorComp1 - color->c[3]
+ - 0.3 * color->c[0]
+ - 0.59 * color->c[1]
+ - 0.11 * color->c[2] + 0.5));
+}
+
+/*void GfxDeviceCMYKColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+ unsigned char r,g,b;
+ float c = color->c[0];
+ float m = color->c[1];
+ float y = color->c[2];
+ float k = color->c[3];
+ convert_cmyk2rgb(c,m,y,k, &r,&g,&b);
+ rgb->r = r/255.0;
+ rgb->g = g/255.0;
+ rgb->b = b/255.0;
+}*/
+
+void GfxDeviceCMYKColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+ double c, m, y, k, c1, m1, y1, k1, r, g, b, x;
+
+ c = colToDbl(color->c[0]);
+ m = colToDbl(color->c[1]);
+ y = colToDbl(color->c[2]);
+ k = colToDbl(color->c[3]);
+ c1 = 1 - c;
+ m1 = 1 - m;
+ y1 = 1 - y;
+ k1 = 1 - k;
+ // this is a matrix multiplication, unrolled for performance
+ // C M Y K
+ x = c1 * m1 * y1 * k1; // 0 0 0 0
+ r = g = b = x;
+ x = c1 * m1 * y1 * k; // 0 0 0 1
+ r += 0.1373 * x;
+ g += 0.1216 * x;
+ b += 0.1255 * x;
+ x = c1 * m1 * y * k1; // 0 0 1 0
+ r += x;
+ g += 0.9490 * x;
+ x = c1 * m1 * y * k; // 0 0 1 1
+ r += 0.1098 * x;
+ g += 0.1020 * x;
+ x = c1 * m * y1 * k1; // 0 1 0 0
+ r += 0.9255 * x;
+ b += 0.5490 * x;
+ x = c1 * m * y1 * k; // 0 1 0 1
+ r += 0.1412 * x;
+ x = c1 * m * y * k1; // 0 1 1 0
+ r += 0.9294 * x;
+ g += 0.1098 * x;
+ b += 0.1412 * x;
+ x = c1 * m * y * k; // 0 1 1 1
+ r += 0.1333 * x;
+ x = c * m1 * y1 * k1; // 1 0 0 0
+ g += 0.6784 * x;
+ b += 0.9373 * x;
+ x = c * m1 * y1 * k; // 1 0 0 1
+ g += 0.0588 * x;
+ b += 0.1412 * x;
+ x = c * m1 * y * k1; // 1 0 1 0
+ g += 0.6510 * x;
+ b += 0.3137 * x;
+ x = c * m1 * y * k; // 1 0 1 1
+ g += 0.0745 * x;
+ x = c * m * y1 * k1; // 1 1 0 0
+ r += 0.1804 * x;
+ g += 0.1922 * x;
+ b += 0.5725 * x;
+ x = c * m * y1 * k; // 1 1 0 1
+ b += 0.0078 * x;
+ x = c * m * y * k1; // 1 1 1 0
+ r += 0.2118 * x;
+ g += 0.2119 * x;
+ b += 0.2235 * x;
+ rgb->r = clip01(dblToCol(r));
+ rgb->g = clip01(dblToCol(g));
+ rgb->b = clip01(dblToCol(b));
+}
+
+void GfxDeviceCMYKColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+ cmyk->c = clip01(color->c[0]);
+ cmyk->m = clip01(color->c[1]);
+ cmyk->y = clip01(color->c[2]);
+ cmyk->k = clip01(color->c[3]);
+}
+
+//------------------------------------------------------------------------
+// GfxLabColorSpace
+//------------------------------------------------------------------------
+
+// This is the inverse of MatrixLMN in Example 4.10 from the PostScript
+// Language Reference, Third Edition.
+static double xyzrgb[3][3] = {
+ { 3.240449, -1.537136, -0.498531 },
+ { -0.969265, 1.876011, 0.041556 },
+ { 0.055643, -0.204026, 1.057229 }
+};
+
+GfxLabColorSpace::GfxLabColorSpace() {
+ whiteX = whiteY = whiteZ = 1;
+ blackX = blackY = blackZ = 0;
+ aMin = bMin = -100;
+ aMax = bMax = 100;
+}
+
+GfxLabColorSpace::~GfxLabColorSpace() {
+}
+
+GfxColorSpace *GfxLabColorSpace::copy() {
+ GfxLabColorSpace *cs;
+
+ cs = new GfxLabColorSpace();
+ cs->whiteX = whiteX;
+ cs->whiteY = whiteY;
+ cs->whiteZ = whiteZ;
+ cs->blackX = blackX;
+ cs->blackY = blackY;
+ cs->blackZ = blackZ;
+ cs->aMin = aMin;
+ cs->aMax = aMax;
+ cs->bMin = bMin;
+ cs->bMax = bMax;
+ cs->kr = kr;
+ cs->kg = kg;
+ cs->kb = kb;
+ return cs;
+}
+
+GfxColorSpace *GfxLabColorSpace::parse(Array *arr) {
+ GfxLabColorSpace *cs;
+ Object obj1, obj2, obj3;
+
+ arr->get(1, &obj1);
+ if (!obj1.isDict()) {
+ error(-1, "Bad Lab color space");
+ obj1.free();
+ return NULL;
+ }
+ cs = new GfxLabColorSpace();
+ if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
+ obj2.arrayGetLength() == 3) {
+ obj2.arrayGet(0, &obj3);
+ cs->whiteX = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(1, &obj3);
+ cs->whiteY = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(2, &obj3);
+ cs->whiteZ = obj3.getNum();
+ obj3.free();
+ }
+ obj2.free();
+ if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
+ obj2.arrayGetLength() == 3) {
+ obj2.arrayGet(0, &obj3);
+ cs->blackX = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(1, &obj3);
+ cs->blackY = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(2, &obj3);
+ cs->blackZ = obj3.getNum();
+ obj3.free();
+ }
+ obj2.free();
+ if (obj1.dictLookup("Range", &obj2)->isArray() &&
+ obj2.arrayGetLength() == 4) {
+ obj2.arrayGet(0, &obj3);
+ cs->aMin = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(1, &obj3);
+ cs->aMax = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(2, &obj3);
+ cs->bMin = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(3, &obj3);
+ cs->bMax = obj3.getNum();
+ obj3.free();
+ }
+ obj2.free();
+ obj1.free();
+
+ cs->kr = 1 / (xyzrgb[0][0] * cs->whiteX +
+ xyzrgb[0][1] * cs->whiteY +
+ xyzrgb[0][2] * cs->whiteZ);
+ cs->kg = 1 / (xyzrgb[1][0] * cs->whiteX +
+ xyzrgb[1][1] * cs->whiteY +
+ xyzrgb[1][2] * cs->whiteZ);
+ cs->kb = 1 / (xyzrgb[2][0] * cs->whiteX +
+ xyzrgb[2][1] * cs->whiteY +
+ xyzrgb[2][2] * cs->whiteZ);
+
+ return cs;
+}
+
+void GfxLabColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+ GfxRGB rgb;
+
+ getRGB(color, &rgb);
+ *gray = clip01((GfxColorComp)(0.299 * rgb.r +
+ 0.587 * rgb.g +
+ 0.114 * rgb.b + 0.5));
+}
+
+void GfxLabColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+ double X, Y, Z;
+ double t1, t2;
+ double r, g, b;
+
+ // convert L*a*b* to CIE 1931 XYZ color space
+ t1 = (colToDbl(color->c[0]) + 16) / 116;
+ t2 = t1 + colToDbl(color->c[1]) / 500;
+ if (t2 >= (6.0 / 29.0)) {
+ X = t2 * t2 * t2;
+ } else {
+ X = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
+ }
+ X *= whiteX;
+ if (t1 >= (6.0 / 29.0)) {
+ Y = t1 * t1 * t1;
+ } else {
+ Y = (108.0 / 841.0) * (t1 - (4.0 / 29.0));
+ }
+ Y *= whiteY;
+ t2 = t1 - colToDbl(color->c[2]) / 200;
+ if (t2 >= (6.0 / 29.0)) {
+ Z = t2 * t2 * t2;
+ } else {
+ Z = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
+ }
+ Z *= whiteZ;
+
+ // convert XYZ to RGB, including gamut mapping and gamma correction
+ r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
+ g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
+ b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z;
+ rgb->r = dblToCol(pow(clip01(r * kr), 0.5));
+ rgb->g = dblToCol(pow(clip01(g * kg), 0.5));
+ rgb->b = dblToCol(pow(clip01(b * kb), 0.5));
+}
+
+void GfxLabColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+ GfxRGB rgb;
+ GfxColorComp c, m, y, k;
+
+ getRGB(color, &rgb);
+ c = clip01(gfxColorComp1 - rgb.r);
+ m = clip01(gfxColorComp1 - rgb.g);
+ y = clip01(gfxColorComp1 - rgb.b);
+ k = c;
+ if (m < k) {
+ k = m;
+ }
+ if (y < k) {
+ k = y;
+ }
+ cmyk->c = c - k;
+ cmyk->m = m - k;
+ cmyk->y = y - k;
+ cmyk->k = k;
+}
+
+void GfxLabColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
+ int maxImgPixel) {
+ decodeLow[0] = 0;
+ decodeRange[0] = 100;
+ decodeLow[1] = aMin;
+ decodeRange[1] = aMax - aMin;
+ decodeLow[2] = bMin;
+ decodeRange[2] = bMax - bMin;
+}
+
+//------------------------------------------------------------------------
+// GfxICCBasedColorSpace
+//------------------------------------------------------------------------
+
+GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA,
+ Ref *iccProfileStreamA) {
+ nComps = nCompsA;
+ alt = altA;
+ iccProfileStream = *iccProfileStreamA;
+ rangeMin[0] = rangeMin[1] = rangeMin[2] = rangeMin[3] = 0;
+ rangeMax[0] = rangeMax[1] = rangeMax[2] = rangeMax[3] = 1;
+}
+
+GfxICCBasedColorSpace::~GfxICCBasedColorSpace() {
+ delete alt;
+}
+
+GfxColorSpace *GfxICCBasedColorSpace::copy() {
+ GfxICCBasedColorSpace *cs;
+ int i;
+
+ cs = new GfxICCBasedColorSpace(nComps, alt->copy(), &iccProfileStream);
+ for (i = 0; i < 4; ++i) {
+ cs->rangeMin[i] = rangeMin[i];
+ cs->rangeMax[i] = rangeMax[i];
+ }
+ return cs;
+}
+
+GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr) {
+ GfxICCBasedColorSpace *cs;
+ Ref iccProfileStreamA;
+ int nCompsA;
+ GfxColorSpace *altA;
+ Dict *dict;
+ Object obj1, obj2, obj3;
+ int i;
+
+ arr->getNF(1, &obj1);
+ if (obj1.isRef()) {
+ iccProfileStreamA = obj1.getRef();
+ } else {
+ iccProfileStreamA.num = 0;
+ iccProfileStreamA.gen = 0;
+ }
+ obj1.free();
+ arr->get(1, &obj1);
+ if (!obj1.isStream()) {
+ error(-1, "Bad ICCBased color space (stream)");
+ obj1.free();
+ return NULL;
+ }
+ dict = obj1.streamGetDict();
+ if (!dict->lookup("N", &obj2)->isInt()) {
+ error(-1, "Bad ICCBased color space (N)");
+ obj2.free();
+ obj1.free();
+ return NULL;
+ }
+ nCompsA = obj2.getInt();
+ obj2.free();
+ if (nCompsA > gfxColorMaxComps) {
+ error(-1, "ICCBased color space with too many (%d > %d) components",
+ nCompsA, gfxColorMaxComps);
+ nCompsA = gfxColorMaxComps;
+ }
+ if (dict->lookup("Alternate", &obj2)->isNull() ||
+ !(altA = GfxColorSpace::parse(&obj2))) {
+ switch (nCompsA) {
+ case 1:
+ altA = new GfxDeviceGrayColorSpace();
+ break;
+ case 3:
+ altA = new GfxDeviceRGBColorSpace();
+ break;
+ case 4:
+ altA = new GfxDeviceCMYKColorSpace();
+ break;
+ default:
+ error(-1, "Bad ICCBased color space - invalid N");
+ obj2.free();
+ obj1.free();
+ return NULL;
+ }
+ }
+ obj2.free();
+ cs = new GfxICCBasedColorSpace(nCompsA, altA, &iccProfileStreamA);
+ if (dict->lookup("Range", &obj2)->isArray() &&
+ obj2.arrayGetLength() == 2 * nCompsA) {
+ for (i = 0; i < nCompsA; ++i) {
+ obj2.arrayGet(2*i, &obj3);
+ cs->rangeMin[i] = obj3.getNum();
+ obj3.free();
+ obj2.arrayGet(2*i+1, &obj3);
+ cs->rangeMax[i] = obj3.getNum();
+ obj3.free();
+ }
+ }
+ obj2.free();
+ obj1.free();
+ return cs;
+}
+
+void GfxICCBasedColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+ alt->getGray(color, gray);
+}
+
+void GfxICCBasedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+ alt->getRGB(color, rgb);
+}
+
+void GfxICCBasedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+ alt->getCMYK(color, cmyk);
+}
+
+void GfxICCBasedColorSpace::getDefaultRanges(double *decodeLow,
+ double *decodeRange,
+ int maxImgPixel) {
+ alt->getDefaultRanges(decodeLow, decodeRange, maxImgPixel);
+
+#if 0
+ // this is nominally correct, but some PDF files don't set the
+ // correct ranges in the ICCBased dict
+ int i;
+
+ for (i = 0; i < nComps; ++i) {
+ decodeLow[i] = rangeMin[i];
+ decodeRange[i] = rangeMax[i] - rangeMin[i];
+ }
+#endif
+}
+
+//------------------------------------------------------------------------
+// GfxIndexedColorSpace
+//------------------------------------------------------------------------
+
+GfxIndexedColorSpace::GfxIndexedColorSpace(GfxColorSpace *baseA,
+ int indexHighA) {
+ base = baseA;
+ indexHigh = indexHighA;
+ lookup = (Guchar *)gmallocn((indexHigh + 1) * base->getNComps(),
+ sizeof(Guchar));
+}
+
+GfxIndexedColorSpace::~GfxIndexedColorSpace() {
+ delete base;
+ gfree(lookup);
+}
+
+GfxColorSpace *GfxIndexedColorSpace::copy() {
+ GfxIndexedColorSpace *cs;
+
+ cs = new GfxIndexedColorSpace(base->copy(), indexHigh);
+ memcpy(cs->lookup, lookup,
+ (indexHigh + 1) * base->getNComps() * sizeof(Guchar));
+ return cs;
+}
+
+GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr) {
+ GfxIndexedColorSpace *cs;
+ GfxColorSpace *baseA;
+ int indexHighA;
+ Object obj1;
+ int x;
+ char *s;
+ int n, i, j;
+
+ if (arr->getLength() != 4) {
+ error(-1, "Bad Indexed color space");
+ goto err1;
+ }
+ arr->get(1, &obj1);
+ if (!(baseA = GfxColorSpace::parse(&obj1))) {
+ error(-1, "Bad Indexed color space (base color space)");
+ goto err2;
+ }
+ obj1.free();
+ if (!arr->get(2, &obj1)->isInt()) {
+ error(-1, "Bad Indexed color space (hival)");
+ delete baseA;
+ goto err2;
+ }
+ indexHighA = obj1.getInt();
+ if (indexHighA < 0 || indexHighA > 255) {
+ // the PDF spec requires indexHigh to be in [0,255] -- allowing
+ // values larger than 255 creates a security hole: if nComps *
+ // indexHigh is greater than 2^31, the loop below may overwrite
+ // past the end of the array
+ error(-1, "Bad Indexed color space (invalid indexHigh value)");
+ delete baseA;
+ goto err2;
+ }
+ obj1.free();
+ cs = new GfxIndexedColorSpace(baseA, indexHighA);
+ arr->get(3, &obj1);
+ n = baseA->getNComps();
+ if (obj1.isStream()) {
+ obj1.streamReset();
+ for (i = 0; i <= indexHighA; ++i) {
+ for (j = 0; j < n; ++j) {
+ if ((x = obj1.streamGetChar()) == EOF) {
+ error(-1, "Bad Indexed color space (lookup table stream too short)");
+ goto err3;
+ }
+ cs->lookup[i*n + j] = (Guchar)x;
+ }
+ }
+ obj1.streamClose();
+ } else if (obj1.isString()) {
+ if (obj1.getString()->getLength() < (indexHighA + 1) * n) {
+ error(-1, "Bad Indexed color space (lookup table string too short)");
+ goto err3;
+ }
+ s = obj1.getString()->getCString();
+ for (i = 0; i <= indexHighA; ++i) {
+ for (j = 0; j < n; ++j) {
+ cs->lookup[i*n + j] = (Guchar)*s++;
+ }
+ }
+ } else {
+ error(-1, "Bad Indexed color space (lookup table)");
+ goto err3;
+ }
+ obj1.free();
+ return cs;
+
+ err3:
+ delete cs;
+ err2:
+ obj1.free();
+ err1:
+ return NULL;
+}
+
+GfxColor *GfxIndexedColorSpace::mapColorToBase(GfxColor *color,
+ GfxColor *baseColor) {
+ Guchar *p;
+ double low[gfxColorMaxComps], range[gfxColorMaxComps];
+ int n, i;
+
+ n = base->getNComps();
+ base->getDefaultRanges(low, range, indexHigh);
+ p = &lookup[(int)(colToDbl(color->c[0]) + 0.5) * n];
+ for (i = 0; i < n; ++i) {
+ baseColor->c[i] = dblToCol(low[i] + (p[i] / 255.0) * range[i]);
+ }
+ return baseColor;
+}
+
+void GfxIndexedColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+ GfxColor color2;
+
+ base->getGray(mapColorToBase(color, &color2), gray);
+}
+
+void GfxIndexedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+ GfxColor color2;
+
+ base->getRGB(mapColorToBase(color, &color2), rgb);
+}
+
+void GfxIndexedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+ GfxColor color2;
+
+ base->getCMYK(mapColorToBase(color, &color2), cmyk);
+}
+
+void GfxIndexedColorSpace::getDefaultRanges(double *decodeLow,
+ double *decodeRange,
+ int maxImgPixel) {
+ decodeLow[0] = 0;
+ decodeRange[0] = maxImgPixel;
+}
+
+//------------------------------------------------------------------------
+// GfxSeparationColorSpace
+//------------------------------------------------------------------------
+
+GfxSeparationColorSpace::GfxSeparationColorSpace(GString *nameA,
+ GfxColorSpace *altA,
+ Function *funcA) {
+ name = nameA;
+ alt = altA;
+ func = funcA;
+}
+
+GfxSeparationColorSpace::~GfxSeparationColorSpace() {
+ delete name;
+ delete alt;
+ delete func;
+}
+
+GfxColorSpace *GfxSeparationColorSpace::copy() {
+ return new GfxSeparationColorSpace(name->copy(), alt->copy(), func->copy());
+}
+
+//~ handle the 'All' and 'None' colorants
+GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr) {
+ GfxSeparationColorSpace *cs;
+ GString *nameA;
+ GfxColorSpace *altA;
+ Function *funcA;
+ Object obj1;
+
+ if (arr->getLength() != 4) {
+ error(-1, "Bad Separation color space");
+ goto err1;
+ }
+ if (!arr->get(1, &obj1)->isName()) {
+ error(-1, "Bad Separation color space (name)");
+ goto err2;
+ }
+ nameA = new GString(obj1.getName());
+ obj1.free();
+ arr->get(2, &obj1);
+ if (!(altA = GfxColorSpace::parse(&obj1))) {
+ error(-1, "Bad Separation color space (alternate color space)");
+ goto err3;
+ }
+ obj1.free();
+ arr->get(3, &obj1);
+ if (!(funcA = Function::parse(&obj1))) {
+ goto err4;
+ }
+ obj1.free();
+ cs = new GfxSeparationColorSpace(nameA, altA, funcA);
+ return cs;
+
+ err4:
+ delete altA;
+ err3:
+ delete nameA;
+ err2:
+ obj1.free();
+ err1:
+ return NULL;
+}
+
+void GfxSeparationColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+ double x;
+ double c[gfxColorMaxComps];
+ GfxColor color2;
+ int i;
+
+ x = colToDbl(color->c[0]);
+ func->transform(&x, c);
+ for (i = 0; i < alt->getNComps(); ++i) {
+ color2.c[i] = dblToCol(c[i]);
+ }
+ alt->getGray(&color2, gray);
+}
+
+void GfxSeparationColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+ double x;
+ double c[gfxColorMaxComps];
+ GfxColor color2;
+ int i;
+
+ x = colToDbl(color->c[0]);
+ func->transform(&x, c);
+ for (i = 0; i < alt->getNComps(); ++i) {
+ color2.c[i] = dblToCol(c[i]);
+ }
+ alt->getRGB(&color2, rgb);
+}
+
+void GfxSeparationColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+ double x;
+ double c[gfxColorMaxComps];
+ GfxColor color2;
+ int i;
+
+ x = colToDbl(color->c[0]);
+ func->transform(&x, c);
+ for (i = 0; i < alt->getNComps(); ++i) {
+ color2.c[i] = dblToCol(c[i]);
+ }
+ alt->getCMYK(&color2, cmyk);
+}
+
+//------------------------------------------------------------------------
+// GfxDeviceNColorSpace
+//------------------------------------------------------------------------
+
+GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA,
+ GfxColorSpace *altA,
+ Function *funcA) {
+ nComps = nCompsA;
+ alt = altA;
+ func = funcA;
+}
+
+GfxDeviceNColorSpace::~GfxDeviceNColorSpace() {
+ int i;
+
+ for (i = 0; i < nComps; ++i) {
+ delete names[i];
+ }
+ delete alt;
+ delete func;
+}
+
+GfxColorSpace *GfxDeviceNColorSpace::copy() {
+ GfxDeviceNColorSpace *cs;
+ int i;
+
+ cs = new GfxDeviceNColorSpace(nComps, alt->copy(), func->copy());
+ for (i = 0; i < nComps; ++i) {
+ cs->names[i] = names[i]->copy();
+ }
+ return cs;
+}
+
+//~ handle the 'None' colorant
+GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr) {
+ GfxDeviceNColorSpace *cs;
+ int nCompsA;
+ GString *namesA[gfxColorMaxComps];
+ GfxColorSpace *altA;
+ Function *funcA;
+ Object obj1, obj2;
+ int i;
+
+ if (arr->getLength() != 4 && arr->getLength() != 5) {
+ error(-1, "Bad DeviceN color space");
+ goto err1;
+ }
+ if (!arr->get(1, &obj1)->isArray()) {
+ error(-1, "Bad DeviceN color space (names)");
+ goto err2;
+ }
+ nCompsA = obj1.arrayGetLength();
+ if (nCompsA > gfxColorMaxComps) {
+ error(-1, "DeviceN color space with too many (%d > %d) components",
+ nCompsA, gfxColorMaxComps);
+ nCompsA = gfxColorMaxComps;
+ }
+ for (i = 0; i < nCompsA; ++i) {
+ if (!obj1.arrayGet(i, &obj2)->isName()) {
+ error(-1, "Bad DeviceN color space (names)");
+ obj2.free();
+ goto err2;
+ }
+ namesA[i] = new GString(obj2.getName());
+ obj2.free();
+ }
+ obj1.free();
+ arr->get(2, &obj1);
+ if (!(altA = GfxColorSpace::parse(&obj1))) {
+ error(-1, "Bad DeviceN color space (alternate color space)");
+ goto err3;
+ }
+ obj1.free();
+ arr->get(3, &obj1);
+ if (!(funcA = Function::parse(&obj1))) {
+ goto err4;
+ }
+ obj1.free();
+ cs = new GfxDeviceNColorSpace(nCompsA, altA, funcA);
+ for (i = 0; i < nCompsA; ++i) {
+ cs->names[i] = namesA[i];
+ }
+ return cs;
+
+ err4:
+ delete altA;
+ err3:
+ for (i = 0; i < nCompsA; ++i) {
+ delete namesA[i];
+ }
+ err2:
+ obj1.free();
+ err1:
+ return NULL;
+}
+
+void GfxDeviceNColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+ double x[gfxColorMaxComps], c[gfxColorMaxComps];
+ GfxColor color2;
+ int i;
+
+ for (i = 0; i < nComps; ++i) {
+ x[i] = colToDbl(color->c[i]);
+ }
+ func->transform(x, c);
+ for (i = 0; i < alt->getNComps(); ++i) {
+ color2.c[i] = dblToCol(c[i]);
+ }
+ alt->getGray(&color2, gray);
+}
+
+void GfxDeviceNColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+ double x[gfxColorMaxComps], c[gfxColorMaxComps];
+ GfxColor color2;
+ int i;
+
+ for (i = 0; i < nComps; ++i) {
+ x[i] = colToDbl(color->c[i]);
+ }
+ func->transform(x, c);
+ for (i = 0; i < alt->getNComps(); ++i) {
+ color2.c[i] = dblToCol(c[i]);
+ }
+ alt->getRGB(&color2, rgb);
+}
+
+void GfxDeviceNColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+ double x[gfxColorMaxComps], c[gfxColorMaxComps];
+ GfxColor color2;
+ int i;
+
+ for (i = 0; i < nComps; ++i) {
+ x[i] = colToDbl(color->c[i]);
+ }
+ func->transform(x, c);
+ for (i = 0; i < alt->getNComps(); ++i) {
+ color2.c[i] = dblToCol(c[i]);
+ }
+ alt->getCMYK(&color2, cmyk);
+}
+
+//------------------------------------------------------------------------
+// GfxPatternColorSpace
+//------------------------------------------------------------------------
+
+GfxPatternColorSpace::GfxPatternColorSpace(GfxColorSpace *underA) {
+ under = underA;
+}
+
+GfxPatternColorSpace::~GfxPatternColorSpace() {
+ if (under) {
+ delete under;
+ }
+}
+
+GfxColorSpace *GfxPatternColorSpace::copy() {
+ return new GfxPatternColorSpace(under ? under->copy() :
+ (GfxColorSpace *)NULL);
+}
+
+GfxColorSpace *GfxPatternColorSpace::parse(Array *arr) {
+ GfxPatternColorSpace *cs;
+ GfxColorSpace *underA;
+ Object obj1;
+
+ if (arr->getLength() != 1 && arr->getLength() != 2) {
+ error(-1, "Bad Pattern color space");
+ return NULL;
+ }
+ underA = NULL;
+ if (arr->getLength() == 2) {
+ arr->get(1, &obj1);
+ if (!(underA = GfxColorSpace::parse(&obj1))) {
+ error(-1, "Bad Pattern color space (underlying color space)");
+ obj1.free();
+ return NULL;
+ }
+ obj1.free();
+ }
+ cs = new GfxPatternColorSpace(underA);
+ return cs;
+}
+
+void GfxPatternColorSpace::getGray(GfxColor *color, GfxGray *gray) {
+ *gray = 0;
+}
+
+void GfxPatternColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
+ rgb->r = rgb->g = rgb->b = 0;
+}
+
+void GfxPatternColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
+ cmyk->c = cmyk->m = cmyk->y = 0;
+ cmyk->k = 1;
+}
+
+//------------------------------------------------------------------------
+// Pattern
+//------------------------------------------------------------------------
+
+GfxPattern::GfxPattern(int typeA) {
+ type = typeA;
+}
+
+GfxPattern::~GfxPattern() {
+}
+
+GfxPattern *GfxPattern::parse(Object *obj) {
+ GfxPattern *pattern;
+ Object obj1;
+
+ if (obj->isDict()) {
+ obj->dictLookup("PatternType", &obj1);
+ } else if (obj->isStream()) {
+ obj->streamGetDict()->lookup("PatternType", &obj1);
+ } else {
+ return NULL;
+ }
+ pattern = NULL;
+ if (obj1.isInt() && obj1.getInt() == 1) {
+ pattern = GfxTilingPattern::parse(obj);
+ } else if (obj1.isInt() && obj1.getInt() == 2) {
+ pattern = GfxShadingPattern::parse(obj);
+ }
+ obj1.free();
+ return pattern;
+}
+
+//------------------------------------------------------------------------
+// GfxTilingPattern
+//------------------------------------------------------------------------
+
+GfxTilingPattern *GfxTilingPattern::parse(Object *patObj) {
+ GfxTilingPattern *pat;
+ Dict *dict;
+ int paintTypeA, tilingTypeA;
+ double bboxA[4], matrixA[6];
+ double xStepA, yStepA;
+ Object resDictA;
+ Object obj1, obj2;
+ int i;
+
+ if (!patObj->isStream()) {
+ return NULL;
+ }
+ dict = patObj->streamGetDict();
+
+ if (dict->lookup("PaintType", &obj1)->isInt()) {
+ paintTypeA = obj1.getInt();
+ } else {
+ paintTypeA = 1;
+ error(-1, "Invalid or missing PaintType in pattern");
+ }
+ obj1.free();
+ if (dict->lookup("TilingType", &obj1)->isInt()) {
+ tilingTypeA = obj1.getInt();
+ } else {
+ tilingTypeA = 1;
+ error(-1, "Invalid or missing TilingType in pattern");
+ }
+ obj1.free();
+ bboxA[0] = bboxA[1] = 0;
+ bboxA[2] = bboxA[3] = 1;
+ if (dict->lookup("BBox", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 4) {
+ for (i = 0; i < 4; ++i) {
+ if (obj1.arrayGet(i, &obj2)->isNum()) {
+ bboxA[i] = obj2.getNum();
+ }
+ obj2.free();
+ }
+ } else {
+ error(-1, "Invalid or missing BBox in pattern");
+ }
+ obj1.free();
+ if (dict->lookup("XStep", &obj1)->isNum()) {
+ xStepA = obj1.getNum();
+ } else {
+ xStepA = 1;
+ error(-1, "Invalid or missing XStep in pattern");
+ }
+ obj1.free();
+ if (dict->lookup("YStep", &obj1)->isNum()) {
+ yStepA = obj1.getNum();
+ } else {
+ yStepA = 1;
+ error(-1, "Invalid or missing YStep in pattern");
+ }
+ obj1.free();
+ if (!dict->lookup("Resources", &resDictA)->isDict()) {
+ resDictA.free();
+ resDictA.initNull();
+ error(-1, "Invalid or missing Resources in pattern");
+ }
+ matrixA[0] = 1; matrixA[1] = 0;
+ matrixA[2] = 0; matrixA[3] = 1;
+ matrixA[4] = 0; matrixA[5] = 0;
+ if (dict->lookup("Matrix", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 6) {
+ for (i = 0; i < 6; ++i) {
+ if (obj1.arrayGet(i, &obj2)->isNum()) {
+ matrixA[i] = obj2.getNum();
+ }
+ obj2.free();
+ }
+ }
+ obj1.free();
+
+ pat = new GfxTilingPattern(paintTypeA, tilingTypeA, bboxA, xStepA, yStepA,
+ &resDictA, matrixA, patObj);
+ resDictA.free();
+ return pat;
+}
+
+GfxTilingPattern::GfxTilingPattern(int paintTypeA, int tilingTypeA,
+ double *bboxA, double xStepA, double yStepA,
+ Object *resDictA, double *matrixA,
+ Object *contentStreamA):
+ GfxPattern(1)
+{
+ int i;
+
+ paintType = paintTypeA;
+ tilingType = tilingTypeA;
+ for (i = 0; i < 4; ++i) {
+ bbox[i] = bboxA[i];
+ }
+ xStep = xStepA;
+ yStep = yStepA;
+ resDictA->copy(&resDict);
+ for (i = 0; i < 6; ++i) {
+ matrix[i] = matrixA[i];
+ }
+ contentStreamA->copy(&contentStream);
+}
+
+GfxTilingPattern::~GfxTilingPattern() {
+ resDict.free();
+ contentStream.free();
+}
+
+GfxPattern *GfxTilingPattern::copy() {
+ return new GfxTilingPattern(paintType, tilingType, bbox, xStep, yStep,
+ &resDict, matrix, &contentStream);
+}
+
+//------------------------------------------------------------------------
+// GfxShadingPattern
+//------------------------------------------------------------------------
+
+GfxShadingPattern *GfxShadingPattern::parse(Object *patObj) {
+ Dict *dict;
+ GfxShading *shadingA;
+ double matrixA[6];
+ Object obj1, obj2;
+ int i;
+
+ if (!patObj->isDict()) {
+ return NULL;
+ }
+ dict = patObj->getDict();
+
+ dict->lookup("Shading", &obj1);
+ shadingA = GfxShading::parse(&obj1);
+ obj1.free();
+ if (!shadingA) {
+ return NULL;
+ }
+
+ matrixA[0] = 1; matrixA[1] = 0;
+ matrixA[2] = 0; matrixA[3] = 1;
+ matrixA[4] = 0; matrixA[5] = 0;
+ if (dict->lookup("Matrix", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 6) {
+ for (i = 0; i < 6; ++i) {
+ if (obj1.arrayGet(i, &obj2)->isNum()) {
+ matrixA[i] = obj2.getNum();
+ }
+ obj2.free();
+ }
+ }
+ obj1.free();
+
+ return new GfxShadingPattern(shadingA, matrixA);
+}
+
+GfxShadingPattern::GfxShadingPattern(GfxShading *shadingA, double *matrixA):
+ GfxPattern(2)
+{
+ int i;
+
+ shading = shadingA;
+ for (i = 0; i < 6; ++i) {
+ matrix[i] = matrixA[i];
+ }
+}
+
+GfxShadingPattern::~GfxShadingPattern() {
+ delete shading;
+}
+
+GfxPattern *GfxShadingPattern::copy() {
+ return new GfxShadingPattern(shading->copy(), matrix);
+}
+
+//------------------------------------------------------------------------
+// GfxShading
+//------------------------------------------------------------------------
+
+GfxShading::GfxShading(int typeA) {
+ type = typeA;
+ colorSpace = NULL;
+}
+
+GfxShading::GfxShading(GfxShading *shading) {
+ int i;
+
+ type = shading->type;
+ colorSpace = shading->colorSpace->copy();
+ for (i = 0; i < gfxColorMaxComps; ++i) {
+ background.c[i] = shading->background.c[i];
+ }
+ hasBackground = shading->hasBackground;
+ xMin = shading->xMin;
+ yMin = shading->yMin;
+ xMax = shading->xMax;
+ yMax = shading->yMax;
+ hasBBox = shading->hasBBox;
+}
+
+GfxShading::~GfxShading() {
+ if (colorSpace) {
+ delete colorSpace;
+ }
+}
+
+GfxShading *GfxShading::parse(Object *obj) {
+ GfxShading *shading;
+ Dict *dict;
+ int typeA;
+ Object obj1;
+
+ if (obj->isDict()) {
+ dict = obj->getDict();
+ } else if (obj->isStream()) {
+ dict = obj->streamGetDict();
+ } else {
+ return NULL;
+ }
+
+ if (!dict->lookup("ShadingType", &obj1)->isInt()) {
+ error(-1, "Invalid ShadingType in shading dictionary");
+ obj1.free();
+ return NULL;
+ }
+ typeA = obj1.getInt();
+ obj1.free();
+
+ switch (typeA) {
+ case 1:
+ shading = GfxFunctionShading::parse(dict);
+ break;
+ case 2:
+ shading = GfxAxialShading::parse(dict);
+ break;
+ case 3:
+ shading = GfxRadialShading::parse(dict);
+ break;
+ case 4:
+ if (obj->isStream()) {
+ shading = GfxGouraudTriangleShading::parse(4, dict, obj->getStream());
+ } else {
+ error(-1, "Invalid Type 4 shading object");
+ goto err1;
+ }
+ break;
+ case 5:
+ if (obj->isStream()) {
+ shading = GfxGouraudTriangleShading::parse(5, dict, obj->getStream());
+ } else {
+ error(-1, "Invalid Type 5 shading object");
+ goto err1;
+ }
+ break;
+ case 6:
+ if (obj->isStream()) {
+ shading = GfxPatchMeshShading::parse(6, dict, obj->getStream());
+ } else {
+ error(-1, "Invalid Type 6 shading object");
+ goto err1;
+ }
+ break;
+ case 7:
+ if (obj->isStream()) {
+ shading = GfxPatchMeshShading::parse(7, dict, obj->getStream());
+ } else {
+ error(-1, "Invalid Type 7 shading object");
+ goto err1;
+ }
+ break;
+ default:
+ error(-1, "Unimplemented shading type %d", typeA);
+ goto err1;
+ }
+
+ return shading;
+
+ err1:
+ return NULL;
+}
+
+GBool GfxShading::init(Dict *dict) {
+ Object obj1, obj2;
+ int i;
+
+ dict->lookup("ColorSpace", &obj1);
+ if (!(colorSpace = GfxColorSpace::parse(&obj1))) {
+ error(-1, "Bad color space in shading dictionary");
+ obj1.free();
+ return gFalse;
+ }
+ obj1.free();
+
+ for (i = 0; i < gfxColorMaxComps; ++i) {
+ background.c[i] = 0;
+ }
+ hasBackground = gFalse;
+ if (dict->lookup("Background", &obj1)->isArray()) {
+ if (obj1.arrayGetLength() == colorSpace->getNComps()) {
+ hasBackground = gTrue;
+ for (i = 0; i < colorSpace->getNComps(); ++i) {
+ background.c[i] = dblToCol(obj1.arrayGet(i, &obj2)->getNum());
+ obj2.free();
+ }
+ } else {
+ error(-1, "Bad Background in shading dictionary");
+ }
+ }
+ obj1.free();
+
+ xMin = yMin = xMax = yMax = 0;
+ hasBBox = gFalse;
+ if (dict->lookup("BBox", &obj1)->isArray()) {
+ if (obj1.arrayGetLength() == 4) {
+ hasBBox = gTrue;
+ xMin = obj1.arrayGet(0, &obj2)->getNum();
+ obj2.free();
+ yMin = obj1.arrayGet(1, &obj2)->getNum();
+ obj2.free();
+ xMax = obj1.arrayGet(2, &obj2)->getNum();
+ obj2.free();
+ yMax = obj1.arrayGet(3, &obj2)->getNum();
+ obj2.free();
+ } else {
+ error(-1, "Bad BBox in shading dictionary");
+ }
+ }
+ obj1.free();
+
+ return gTrue;
+}
+
+//------------------------------------------------------------------------
+// GfxFunctionShading
+//------------------------------------------------------------------------
+
+GfxFunctionShading::GfxFunctionShading(double x0A, double y0A,
+ double x1A, double y1A,
+ double *matrixA,
+ Function **funcsA, int nFuncsA):
+ GfxShading(1)
+{
+ int i;
+
+ x0 = x0A;
+ y0 = y0A;
+ x1 = x1A;
+ y1 = y1A;
+ for (i = 0; i < 6; ++i) {
+ matrix[i] = matrixA[i];
+ }
+ nFuncs = nFuncsA;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i] = funcsA[i];
+ }
+}
+
+GfxFunctionShading::GfxFunctionShading(GfxFunctionShading *shading):
+ GfxShading(shading)
+{
+ int i;
+
+ x0 = shading->x0;
+ y0 = shading->y0;
+ x1 = shading->x1;
+ y1 = shading->y1;
+ for (i = 0; i < 6; ++i) {
+ matrix[i] = shading->matrix[i];
+ }
+ nFuncs = shading->nFuncs;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i] = shading->funcs[i]->copy();
+ }
+}
+
+GfxFunctionShading::~GfxFunctionShading() {
+ int i;
+
+ for (i = 0; i < nFuncs; ++i) {
+ delete funcs[i];
+ }
+}
+
+GfxFunctionShading *GfxFunctionShading::parse(Dict *dict) {
+ GfxFunctionShading *shading;
+ double x0A, y0A, x1A, y1A;
+ double matrixA[6];
+ Function *funcsA[gfxColorMaxComps];
+ int nFuncsA;
+ Object obj1, obj2;
+ int i;
+
+ x0A = y0A = 0;
+ x1A = y1A = 1;
+ if (dict->lookup("Domain", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 4) {
+ x0A = obj1.arrayGet(0, &obj2)->getNum();
+ obj2.free();
+ y0A = obj1.arrayGet(1, &obj2)->getNum();
+ obj2.free();
+ x1A = obj1.arrayGet(2, &obj2)->getNum();
+ obj2.free();
+ y1A = obj1.arrayGet(3, &obj2)->getNum();
+ obj2.free();
+ }
+ obj1.free();
+
+ matrixA[0] = 1; matrixA[1] = 0;
+ matrixA[2] = 0; matrixA[3] = 1;
+ matrixA[4] = 0; matrixA[5] = 0;
+ if (dict->lookup("Matrix", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 6) {
+ matrixA[0] = obj1.arrayGet(0, &obj2)->getNum();
+ obj2.free();
+ matrixA[1] = obj1.arrayGet(1, &obj2)->getNum();
+ obj2.free();
+ matrixA[2] = obj1.arrayGet(2, &obj2)->getNum();
+ obj2.free();
+ matrixA[3] = obj1.arrayGet(3, &obj2)->getNum();
+ obj2.free();
+ matrixA[4] = obj1.arrayGet(4, &obj2)->getNum();
+ obj2.free();
+ matrixA[5] = obj1.arrayGet(5, &obj2)->getNum();
+ obj2.free();
+ }
+ obj1.free();
+
+ dict->lookup("Function", &obj1);
+ if (obj1.isArray()) {
+ nFuncsA = obj1.arrayGetLength();
+ if (nFuncsA > gfxColorMaxComps) {
+ error(-1, "Invalid Function array in shading dictionary");
+ goto err1;
+ }
+ for (i = 0; i < nFuncsA; ++i) {
+ obj1.arrayGet(i, &obj2);
+ if (!(funcsA[i] = Function::parse(&obj2))) {
+ goto err2;
+ }
+ obj2.free();
+ }
+ } else {
+ nFuncsA = 1;
+ if (!(funcsA[0] = Function::parse(&obj1))) {
+ goto err1;
+ }
+ }
+ obj1.free();
+
+ shading = new GfxFunctionShading(x0A, y0A, x1A, y1A, matrixA,
+ funcsA, nFuncsA);
+ if (!shading->init(dict)) {
+ delete shading;
+ return NULL;
+ }
+ return shading;
+
+ err2:
+ obj2.free();
+ err1:
+ obj1.free();
+ return NULL;
+}
+
+GfxShading *GfxFunctionShading::copy() {
+ return new GfxFunctionShading(this);
+}
+
+void GfxFunctionShading::getColor(double x, double y, GfxColor *color) {
+ double in[2], out[gfxColorMaxComps];
+ int i;
+
+ // NB: there can be one function with n outputs or n functions with
+ // one output each (where n = number of color components)
+ for (i = 0; i < gfxColorMaxComps; ++i) {
+ out[i] = 0;
+ }
+ in[0] = x;
+ in[1] = y;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i]->transform(in, &out[i]);
+ }
+ for (i = 0; i < gfxColorMaxComps; ++i) {
+ color->c[i] = dblToCol(out[i]);
+ }
+}
+
+//------------------------------------------------------------------------
+// GfxAxialShading
+//------------------------------------------------------------------------
+
+GfxAxialShading::GfxAxialShading(double x0A, double y0A,
+ double x1A, double y1A,
+ double t0A, double t1A,
+ Function **funcsA, int nFuncsA,
+ GBool extend0A, GBool extend1A):
+ GfxShading(2)
+{
+ int i;
+
+ x0 = x0A;
+ y0 = y0A;
+ x1 = x1A;
+ y1 = y1A;
+ t0 = t0A;
+ t1 = t1A;
+ nFuncs = nFuncsA;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i] = funcsA[i];
+ }
+ extend0 = extend0A;
+ extend1 = extend1A;
+}
+
+GfxAxialShading::GfxAxialShading(GfxAxialShading *shading):
+ GfxShading(shading)
+{
+ int i;
+
+ x0 = shading->x0;
+ y0 = shading->y0;
+ x1 = shading->x1;
+ y1 = shading->y1;
+ t0 = shading->t0;
+ y1 = shading->t1;
+ nFuncs = shading->nFuncs;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i] = shading->funcs[i]->copy();
+ }
+ extend0 = shading->extend0;
+ extend1 = shading->extend1;
+}
+
+GfxAxialShading::~GfxAxialShading() {
+ int i;
+
+ for (i = 0; i < nFuncs; ++i) {
+ delete funcs[i];
+ }
+}
+
+GfxAxialShading *GfxAxialShading::parse(Dict *dict) {
+ GfxAxialShading *shading;
+ double x0A, y0A, x1A, y1A;
+ double t0A, t1A;
+ Function *funcsA[gfxColorMaxComps];
+ int nFuncsA;
+ GBool extend0A, extend1A;
+ Object obj1, obj2;
+ int i;
+
+ x0A = y0A = x1A = y1A = 0;
+ if (dict->lookup("Coords", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 4) {
+ x0A = obj1.arrayGet(0, &obj2)->getNum();
+ obj2.free();
+ y0A = obj1.arrayGet(1, &obj2)->getNum();
+ obj2.free();
+ x1A = obj1.arrayGet(2, &obj2)->getNum();
+ obj2.free();
+ y1A = obj1.arrayGet(3, &obj2)->getNum();
+ obj2.free();
+ } else {
+ error(-1, "Missing or invalid Coords in shading dictionary");
+ goto err1;
+ }
+ obj1.free();
+
+ t0A = 0;
+ t1A = 1;
+ if (dict->lookup("Domain", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 2) {
+ t0A = obj1.arrayGet(0, &obj2)->getNum();
+ obj2.free();
+ t1A = obj1.arrayGet(1, &obj2)->getNum();
+ obj2.free();
+ }
+ obj1.free();
+
+ dict->lookup("Function", &obj1);
+ if (obj1.isArray()) {
+ nFuncsA = obj1.arrayGetLength();
+ if (nFuncsA > gfxColorMaxComps) {
+ error(-1, "Invalid Function array in shading dictionary");
+ goto err1;
+ }
+ for (i = 0; i < nFuncsA; ++i) {
+ obj1.arrayGet(i, &obj2);
+ if (!(funcsA[i] = Function::parse(&obj2))) {
+ obj1.free();
+ obj2.free();
+ goto err1;
+ }
+ obj2.free();
+ }
+ } else {
+ nFuncsA = 1;
+ if (!(funcsA[0] = Function::parse(&obj1))) {
+ obj1.free();
+ goto err1;
+ }
+ }
+ obj1.free();
+
+ extend0A = extend1A = gFalse;
+ if (dict->lookup("Extend", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 2) {
+ extend0A = obj1.arrayGet(0, &obj2)->getBool();
+ obj2.free();
+ extend1A = obj1.arrayGet(1, &obj2)->getBool();
+ obj2.free();
+ }
+ obj1.free();
+
+ shading = new GfxAxialShading(x0A, y0A, x1A, y1A, t0A, t1A,
+ funcsA, nFuncsA, extend0A, extend1A);
+ if (!shading->init(dict)) {
+ delete shading;
+ return NULL;
+ }
+ return shading;
+
+ err1:
+ return NULL;
+}
+
+GfxShading *GfxAxialShading::copy() {
+ return new GfxAxialShading(this);
+}
+
+void GfxAxialShading::getColor(double t, GfxColor *color) {
+ double out[gfxColorMaxComps];
+ int i;
+
+ // NB: there can be one function with n outputs or n functions with
+ // one output each (where n = number of color components)
+ for (i = 0; i < gfxColorMaxComps; ++i) {
+ out[i] = 0;
+ }
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i]->transform(&t, &out[i]);
+ }
+ for (i = 0; i < gfxColorMaxComps; ++i) {
+ color->c[i] = dblToCol(out[i]);
+ }
+}
+
+//------------------------------------------------------------------------
+// GfxRadialShading
+//------------------------------------------------------------------------
+
+GfxRadialShading::GfxRadialShading(double x0A, double y0A, double r0A,
+ double x1A, double y1A, double r1A,
+ double t0A, double t1A,
+ Function **funcsA, int nFuncsA,
+ GBool extend0A, GBool extend1A):
+ GfxShading(3)
+{
+ int i;
+
+ x0 = x0A;
+ y0 = y0A;
+ r0 = r0A;
+ x1 = x1A;
+ y1 = y1A;
+ r1 = r1A;
+ t0 = t0A;
+ t1 = t1A;
+ nFuncs = nFuncsA;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i] = funcsA[i];
+ }
+ extend0 = extend0A;
+ extend1 = extend1A;
+}
+
+GfxRadialShading::GfxRadialShading(GfxRadialShading *shading):
+ GfxShading(shading)
+{
+ int i;
+
+ x0 = shading->x0;
+ y0 = shading->y0;
+ r0 = shading->r0;
+ x1 = shading->x1;
+ y1 = shading->y1;
+ r1 = shading->r1;
+ t0 = shading->t0;
+ y1 = shading->t1;
+ nFuncs = shading->nFuncs;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i] = shading->funcs[i]->copy();
+ }
+ extend0 = shading->extend0;
+ extend1 = shading->extend1;
+}
+
+GfxRadialShading::~GfxRadialShading() {
+ int i;
+
+ for (i = 0; i < nFuncs; ++i) {
+ delete funcs[i];
+ }
+}
+
+GfxRadialShading *GfxRadialShading::parse(Dict *dict) {
+ GfxRadialShading *shading;
+ double x0A, y0A, r0A, x1A, y1A, r1A;
+ double t0A, t1A;
+ Function *funcsA[gfxColorMaxComps];
+ int nFuncsA;
+ GBool extend0A, extend1A;
+ Object obj1, obj2;
+ int i;
+
+ x0A = y0A = r0A = x1A = y1A = r1A = 0;
+ if (dict->lookup("Coords", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 6) {
+ x0A = obj1.arrayGet(0, &obj2)->getNum();
+ obj2.free();
+ y0A = obj1.arrayGet(1, &obj2)->getNum();
+ obj2.free();
+ r0A = obj1.arrayGet(2, &obj2)->getNum();
+ obj2.free();
+ x1A = obj1.arrayGet(3, &obj2)->getNum();
+ obj2.free();
+ y1A = obj1.arrayGet(4, &obj2)->getNum();
+ obj2.free();
+ r1A = obj1.arrayGet(5, &obj2)->getNum();
+ obj2.free();
+ } else {
+ error(-1, "Missing or invalid Coords in shading dictionary");
+ goto err1;
+ }
+ obj1.free();
+
+ t0A = 0;
+ t1A = 1;
+ if (dict->lookup("Domain", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 2) {
+ t0A = obj1.arrayGet(0, &obj2)->getNum();
+ obj2.free();
+ t1A = obj1.arrayGet(1, &obj2)->getNum();
+ obj2.free();
+ }
+ obj1.free();
+
+ dict->lookup("Function", &obj1);
+ if (obj1.isArray()) {
+ nFuncsA = obj1.arrayGetLength();
+ if (nFuncsA > gfxColorMaxComps) {
+ error(-1, "Invalid Function array in shading dictionary");
+ goto err1;
+ }
+ for (i = 0; i < nFuncsA; ++i) {
+ obj1.arrayGet(i, &obj2);
+ if (!(funcsA[i] = Function::parse(&obj2))) {
+ obj1.free();
+ obj2.free();
+ goto err1;
+ }
+ obj2.free();
+ }
+ } else {
+ nFuncsA = 1;
+ if (!(funcsA[0] = Function::parse(&obj1))) {
+ obj1.free();
+ goto err1;
+ }
+ }
+ obj1.free();
+
+ extend0A = extend1A = gFalse;
+ if (dict->lookup("Extend", &obj1)->isArray() &&
+ obj1.arrayGetLength() == 2) {
+ extend0A = obj1.arrayGet(0, &obj2)->getBool();
+ obj2.free();
+ extend1A = obj1.arrayGet(1, &obj2)->getBool();
+ obj2.free();
+ }
+ obj1.free();
+
+ shading = new GfxRadialShading(x0A, y0A, r0A, x1A, y1A, r1A, t0A, t1A,
+ funcsA, nFuncsA, extend0A, extend1A);
+ if (!shading->init(dict)) {
+ delete shading;
+ return NULL;
+ }
+ return shading;
+
+ err1:
+ return NULL;
+}
+
+GfxShading *GfxRadialShading::copy() {
+ return new GfxRadialShading(this);
+}
+
+void GfxRadialShading::getColor(double t, GfxColor *color) {
+ double out[gfxColorMaxComps];
+ int i;
+
+ // NB: there can be one function with n outputs or n functions with
+ // one output each (where n = number of color components)
+ for (i = 0; i < gfxColorMaxComps; ++i) {
+ out[i] = 0;
+ }
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i]->transform(&t, &out[i]);
+ }
+ for (i = 0; i < gfxColorMaxComps; ++i) {
+ color->c[i] = dblToCol(out[i]);
+ }
+}
+
+//------------------------------------------------------------------------
+// GfxShadingBitBuf
+//------------------------------------------------------------------------
+
+class GfxShadingBitBuf {
+public:
+
+ GfxShadingBitBuf(Stream *strA);
+ ~GfxShadingBitBuf();
+ GBool getBits(int n, Guint *val);
+ void flushBits();
+
+private:
+
+ Stream *str;
+ int bitBuf;
+ int nBits;
+};
+
+GfxShadingBitBuf::GfxShadingBitBuf(Stream *strA) {
+ str = strA;
+ str->reset();
+ bitBuf = 0;
+ nBits = 0;
+}
+
+GfxShadingBitBuf::~GfxShadingBitBuf() {
+ str->close();
+}
+
+GBool GfxShadingBitBuf::getBits(int n, Guint *val) {
+ int x;
+
+ if (nBits >= n) {
+ x = (bitBuf >> (nBits - n)) & ((1 << n) - 1);
+ nBits -= n;
+ } else {
+ x = 0;
+ if (nBits > 0) {
+ x = bitBuf & ((1 << nBits) - 1);
+ n -= nBits;
+ nBits = 0;
+ }
+ while (n > 0) {
+ if ((bitBuf = str->getChar()) == EOF) {
+ nBits = 0;
+ return gFalse;
+ }
+ if (n >= 8) {
+ x = (x << 8) | bitBuf;
+ n -= 8;
+ } else {
+ x = (x << n) | (bitBuf >> (8 - n));
+ nBits = 8 - n;
+ n = 0;
+ }
+ }
+ }
+ *val = x;
+ return gTrue;
+}
+
+void GfxShadingBitBuf::flushBits() {
+ bitBuf = 0;
+ nBits = 0;
+}
+
+//------------------------------------------------------------------------
+// GfxGouraudTriangleShading
+//------------------------------------------------------------------------
+
+GfxGouraudTriangleShading::GfxGouraudTriangleShading(
+ int typeA,
+ GfxGouraudVertex *verticesA, int nVerticesA,
+ int (*trianglesA)[3], int nTrianglesA,
+ Function **funcsA, int nFuncsA):
+ GfxShading(typeA)
+{
+ int i;
+
+ vertices = verticesA;
+ nVertices = nVerticesA;
+ triangles = trianglesA;
+ nTriangles = nTrianglesA;
+ nFuncs = nFuncsA;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i] = funcsA[i];
+ }
+}
+
+GfxGouraudTriangleShading::GfxGouraudTriangleShading(
+ GfxGouraudTriangleShading *shading):
+ GfxShading(shading)
+{
+ int i;
+
+ nVertices = shading->nVertices;
+ vertices = (GfxGouraudVertex *)gmallocn(nVertices, sizeof(GfxGouraudVertex));
+ memcpy(vertices, shading->vertices, nVertices * sizeof(GfxGouraudVertex));
+ nTriangles = shading->nTriangles;
+ triangles = (int (*)[3])gmallocn(nTriangles * 3, sizeof(int));
+ memcpy(triangles, shading->triangles, nTriangles * 3 * sizeof(int));
+ nFuncs = shading->nFuncs;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i] = shading->funcs[i]->copy();
+ }
+}
+
+GfxGouraudTriangleShading::~GfxGouraudTriangleShading() {
+ int i;
+
+ gfree(vertices);
+ gfree(triangles);
+ for (i = 0; i < nFuncs; ++i) {
+ delete funcs[i];
+ }
+}
+
+GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(int typeA,
+ Dict *dict,
+ Stream *str) {
+ GfxGouraudTriangleShading *shading;
+ Function *funcsA[gfxColorMaxComps];
+ int nFuncsA;
+ int coordBits, compBits, flagBits, vertsPerRow, nRows;
+ double xMin, xMax, yMin, yMax;
+ double cMin[gfxColorMaxComps], cMax[gfxColorMaxComps];
+ double xMul, yMul;
+ double cMul[gfxColorMaxComps];
+ GfxGouraudVertex *verticesA;
+ int (*trianglesA)[3];
+ int nComps, nVerticesA, nTrianglesA, vertSize, triSize;
+ Guint x, y, flag;
+ Guint c[gfxColorMaxComps];
+ GfxShadingBitBuf *bitBuf;
+ Object obj1, obj2;
+ int i, j, k, state;
+
+ if (dict->lookup("BitsPerCoordinate", &obj1)->isInt()) {
+ coordBits = obj1.getInt();
+ } else {
+ error(-1, "Missing or invalid BitsPerCoordinate in shading dictionary");
+ goto err2;
+ }
+ obj1.free();
+ if (dict->lookup("BitsPerComponent", &obj1)->isInt()) {
+ compBits = obj1.getInt();
+ } else {
+ error(-1, "Missing or invalid BitsPerComponent in shading dictionary");
+ goto err2;
+ }
+ obj1.free();
+ flagBits = vertsPerRow = 0; // make gcc happy
+ if (typeA == 4) {
+ if (dict->lookup("BitsPerFlag", &obj1)->isInt()) {
+ flagBits = obj1.getInt();
+ } else {
+ error(-1, "Missing or invalid BitsPerFlag in shading dictionary");
+ goto err2;
+ }
+ obj1.free();
+ } else {
+ if (dict->lookup("VerticesPerRow", &obj1)->isInt()) {
+ vertsPerRow = obj1.getInt();
+ } else {
+ error(-1, "Missing or invalid VerticesPerRow in shading dictionary");
+ goto err2;
+ }
+ obj1.free();
+ }
+ if (dict->lookup("Decode", &obj1)->isArray() &&
+ obj1.arrayGetLength() >= 6) {
+ xMin = obj1.arrayGet(0, &obj2)->getNum();
+ obj2.free();
+ xMax = obj1.arrayGet(1, &obj2)->getNum();
+ obj2.free();
+ xMul = (xMax - xMin) / (pow(2.0, coordBits) - 1);
+ yMin = obj1.arrayGet(2, &obj2)->getNum();
+ obj2.free();
+ yMax = obj1.arrayGet(3, &obj2)->getNum();
+ obj2.free();
+ yMul = (yMax - yMin) / (pow(2.0, coordBits) - 1);
+ for (i = 0; 5 + 2*i < obj1.arrayGetLength() && i < gfxColorMaxComps; ++i) {
+ cMin[i] = obj1.arrayGet(4 + 2*i, &obj2)->getNum();
+ obj2.free();
+ cMax[i] = obj1.arrayGet(5 + 2*i, &obj2)->getNum();
+ obj2.free();
+ cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1);
+ }
+ nComps = i;
+ } else {
+ error(-1, "Missing or invalid Decode array in shading dictionary");
+ goto err2;
+ }
+ obj1.free();
+
+ if (!dict->lookup("Function", &obj1)->isNull()) {
+ if (obj1.isArray()) {
+ nFuncsA = obj1.arrayGetLength();
+ if (nFuncsA > gfxColorMaxComps) {
+ error(-1, "Invalid Function array in shading dictionary");
+ goto err1;
+ }
+ for (i = 0; i < nFuncsA; ++i) {
+ obj1.arrayGet(i, &obj2);
+ if (!(funcsA[i] = Function::parse(&obj2))) {
+ obj1.free();
+ obj2.free();
+ goto err1;
+ }
+ obj2.free();
+ }
+ } else {
+ nFuncsA = 1;
+ if (!(funcsA[0] = Function::parse(&obj1))) {
+ obj1.free();
+ goto err1;
+ }
+ }
+ } else {
+ nFuncsA = 0;
+ }
+ obj1.free();
+
+ nVerticesA = nTrianglesA = 0;
+ verticesA = NULL;
+ trianglesA = NULL;
+ vertSize = triSize = 0;
+ state = 0;
+ flag = 0; // make gcc happy
+ bitBuf = new GfxShadingBitBuf(str);
+ while (1) {
+ if (typeA == 4) {
+ if (!bitBuf->getBits(flagBits, &flag)) {
+ break;
+ }
+ }
+ if (!bitBuf->getBits(coordBits, &x) ||
+ !bitBuf->getBits(coordBits, &y)) {
+ break;
+ }
+ for (i = 0; i < nComps; ++i) {
+ if (!bitBuf->getBits(compBits, &c[i])) {
+ break;
+ }
+ }
+ if (i < nComps) {
+ break;
+ }
+ if (nVerticesA == vertSize) {
+ vertSize = (vertSize == 0) ? 16 : 2 * vertSize;
+ verticesA = (GfxGouraudVertex *)
+ greallocn(verticesA, vertSize, sizeof(GfxGouraudVertex));
+ }
+ verticesA[nVerticesA].x = xMin + xMul * (double)x;
+ verticesA[nVerticesA].y = yMin + yMul * (double)y;
+ for (i = 0; i < nComps; ++i) {
+ verticesA[nVerticesA].color.c[i] =
+ dblToCol(cMin[i] + cMul[i] * (double)c[i]);
+ }
+ ++nVerticesA;
+ bitBuf->flushBits();
+ if (typeA == 4) {
+ if (state == 0 || state == 1) {
+ ++state;
+ } else if (state == 2 || flag > 0) {
+ if (nTrianglesA == triSize) {
+ triSize = (triSize == 0) ? 16 : 2 * triSize;
+ trianglesA = (int (*)[3])
+ greallocn(trianglesA, triSize * 3, sizeof(int));
+ }
+ if (state == 2) {
+ trianglesA[nTrianglesA][0] = nVerticesA - 3;
+ trianglesA[nTrianglesA][1] = nVerticesA - 2;
+ trianglesA[nTrianglesA][2] = nVerticesA - 1;
+ ++state;
+ } else if (flag == 1) {
+ trianglesA[nTrianglesA][0] = trianglesA[nTrianglesA - 1][1];
+ trianglesA[nTrianglesA][1] = trianglesA[nTrianglesA - 1][2];
+ trianglesA[nTrianglesA][2] = nVerticesA - 1;
+ } else { // flag == 2
+ trianglesA[nTrianglesA][0] = trianglesA[nTrianglesA - 1][0];
+ trianglesA[nTrianglesA][1] = trianglesA[nTrianglesA - 1][2];
+ trianglesA[nTrianglesA][2] = nVerticesA - 1;
+ }
+ ++nTrianglesA;
+ } else { // state == 3 && flag == 0
+ state = 1;
+ }
+ }
+ }
+ delete bitBuf;
+ if (typeA == 5) {
+ nRows = nVerticesA / vertsPerRow;
+ nTrianglesA = (nRows - 1) * 2 * (vertsPerRow - 1);
+ trianglesA = (int (*)[3])gmallocn(nTrianglesA * 3, sizeof(int));
+ k = 0;
+ for (i = 0; i < nRows - 1; ++i) {
+ for (j = 0; j < vertsPerRow - 1; ++j) {
+ trianglesA[k][0] = i * vertsPerRow + j;
+ trianglesA[k][1] = i * vertsPerRow + j+1;
+ trianglesA[k][2] = (i+1) * vertsPerRow + j;
+ ++k;
+ trianglesA[k][0] = i * vertsPerRow + j+1;
+ trianglesA[k][1] = (i+1) * vertsPerRow + j;
+ trianglesA[k][2] = (i+1) * vertsPerRow + j+1;
+ ++k;
+ }
+ }
+ }
+
+ shading = new GfxGouraudTriangleShading(typeA, verticesA, nVerticesA,
+ trianglesA, nTrianglesA,
+ funcsA, nFuncsA);
+ if (!shading->init(dict)) {
+ delete shading;
+ return NULL;
+ }
+ return shading;
+
+ err2:
+ obj1.free();
+ err1:
+ return NULL;
+}
+
+GfxShading *GfxGouraudTriangleShading::copy() {
+ return new GfxGouraudTriangleShading(this);
+}
+
+void GfxGouraudTriangleShading::getTriangle(
+ int i,
+ double *x0, double *y0, GfxColor *color0,
+ double *x1, double *y1, GfxColor *color1,
+ double *x2, double *y2, GfxColor *color2) {
+ double in;
+ double out[gfxColorMaxComps];
+ int v, j;
+
+ v = triangles[i][0];
+ *x0 = vertices[v].x;
+ *y0 = vertices[v].y;
+ if (nFuncs > 0) {
+ in = colToDbl(vertices[v].color.c[0]);
+ for (j = 0; j < nFuncs; ++j) {
+ funcs[j]->transform(&in, &out[j]);
+ }
+ for (j = 0; j < gfxColorMaxComps; ++j) {
+ color0->c[j] = dblToCol(out[j]);
+ }
+ } else {
+ *color0 = vertices[v].color;
+ }
+ v = triangles[i][1];
+ *x1 = vertices[v].x;
+ *y1 = vertices[v].y;
+ if (nFuncs > 0) {
+ in = colToDbl(vertices[v].color.c[0]);
+ for (j = 0; j < nFuncs; ++j) {
+ funcs[j]->transform(&in, &out[j]);
+ }
+ for (j = 0; j < gfxColorMaxComps; ++j) {
+ color1->c[j] = dblToCol(out[j]);
+ }
+ } else {
+ *color1 = vertices[v].color;
+ }
+ v = triangles[i][2];
+ *x2 = vertices[v].x;
+ *y2 = vertices[v].y;
+ if (nFuncs > 0) {
+ in = colToDbl(vertices[v].color.c[0]);
+ for (j = 0; j < nFuncs; ++j) {
+ funcs[j]->transform(&in, &out[j]);
+ }
+ for (j = 0; j < gfxColorMaxComps; ++j) {
+ color2->c[j] = dblToCol(out[j]);
+ }
+ } else {
+ *color2 = vertices[v].color;
+ }
+}
+
+//------------------------------------------------------------------------
+// GfxPatchMeshShading
+//------------------------------------------------------------------------
+
+GfxPatchMeshShading::GfxPatchMeshShading(int typeA,
+ GfxPatch *patchesA, int nPatchesA,
+ Function **funcsA, int nFuncsA):
+ GfxShading(typeA)
+{
+ int i;
+
+ patches = patchesA;
+ nPatches = nPatchesA;
+ nFuncs = nFuncsA;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i] = funcsA[i];
+ }
+}
+
+GfxPatchMeshShading::GfxPatchMeshShading(GfxPatchMeshShading *shading):
+ GfxShading(shading)
+{
+ int i;
+
+ nPatches = shading->nPatches;
+ patches = (GfxPatch *)gmallocn(nPatches, sizeof(GfxPatch));
+ memcpy(patches, shading->patches, nPatches * sizeof(GfxPatch));
+ nFuncs = shading->nFuncs;
+ for (i = 0; i < nFuncs; ++i) {
+ funcs[i] = shading->funcs[i]->copy();
+ }
+}
+
+GfxPatchMeshShading::~GfxPatchMeshShading() {
+ int i;
+
+ gfree(patches);
+ for (i = 0; i < nFuncs; ++i) {
+ delete funcs[i];
+ }
+}
+
+GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
+ Stream *str) {
+ GfxPatchMeshShading *shading;
+ Function *funcsA[gfxColorMaxComps];
+ int nFuncsA;
+ int coordBits, compBits, flagBits;
+ double xMin, xMax, yMin, yMax;
+ double cMin[gfxColorMaxComps], cMax[gfxColorMaxComps];
+ double xMul, yMul;
+ double cMul[gfxColorMaxComps];
+ GfxPatch *patchesA, *p;
+ int nComps, nPatchesA, patchesSize, nPts, nColors;
+ Guint flag;
+ double x[16], y[16];
+ Guint xi, yi;
+ GfxColorComp c[4][gfxColorMaxComps];
+ Guint ci[4];
+ GfxShadingBitBuf *bitBuf;
+ Object obj1, obj2;
+ int i, j;
+
+ if (dict->lookup("BitsPerCoordinate", &obj1)->isInt()) {
+ coordBits = obj1.getInt();
+ } else {
+ error(-1, "Missing or invalid BitsPerCoordinate in shading dictionary");
+ goto err2;
+ }
+ obj1.free();
+ if (dict->lookup("BitsPerComponent", &obj1)->isInt()) {
+ compBits = obj1.getInt();
+ } else {
+ error(-1, "Missing or invalid BitsPerComponent in shading dictionary");
+ goto err2;
+ }
+ obj1.free();
+ if (dict->lookup("BitsPerFlag", &obj1)->isInt()) {
+ flagBits = obj1.getInt();
+ } else {
+ error(-1, "Missing or invalid BitsPerFlag in shading dictionary");
+ goto err2;
+ }
+ obj1.free();
+ if (dict->lookup("Decode", &obj1)->isArray() &&
+ obj1.arrayGetLength() >= 6) {
+ xMin = obj1.arrayGet(0, &obj2)->getNum();
+ obj2.free();
+ xMax = obj1.arrayGet(1, &obj2)->getNum();
+ obj2.free();
+ xMul = (xMax - xMin) / (pow(2.0, coordBits) - 1);
+ yMin = obj1.arrayGet(2, &obj2)->getNum();
+ obj2.free();
+ yMax = obj1.arrayGet(3, &obj2)->getNum();
+ obj2.free();
+ yMul = (yMax - yMin) / (pow(2.0, coordBits) - 1);
+ for (i = 0; 5 + 2*i < obj1.arrayGetLength() && i < gfxColorMaxComps; ++i) {
+ cMin[i] = obj1.arrayGet(4 + 2*i, &obj2)->getNum();
+ obj2.free();
+ cMax[i] = obj1.arrayGet(5 + 2*i, &obj2)->getNum();
+ obj2.free();
+ cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1);
+ }
+ nComps = i;
+ } else {
+ error(-1, "Missing or invalid Decode array in shading dictionary");
+ goto err2;
+ }
+ obj1.free();
+
+ if (!dict->lookup("Function", &obj1)->isNull()) {
+ if (obj1.isArray()) {
+ nFuncsA = obj1.arrayGetLength();
+ if (nFuncsA > gfxColorMaxComps) {
+ error(-1, "Invalid Function array in shading dictionary");
+ goto err1;
+ }
+ for (i = 0; i < nFuncsA; ++i) {
+ obj1.arrayGet(i, &obj2);
+ if (!(funcsA[i] = Function::parse(&obj2))) {
+ obj1.free();
+ obj2.free();
+ goto err1;
+ }
+ obj2.free();
+ }
+ } else {
+ nFuncsA = 1;
+ if (!(funcsA[0] = Function::parse(&obj1))) {
+ obj1.free();
+ goto err1;
+ }
+ }
+ } else {
+ nFuncsA = 0;
+ }
+ obj1.free();
+
+ nPatchesA = 0;
+ patchesA = NULL;
+ patchesSize = 0;
+ bitBuf = new GfxShadingBitBuf(str);
+ while (1) {
+ if (!bitBuf->getBits(flagBits, &flag)) {
+ break;
+ }
+ if (typeA == 6) {
+ switch (flag) {
+ case 0: nPts = 12; nColors = 4; break;
+ case 1:
+ case 2:
+ case 3:
+ default: nPts = 8; nColors = 2; break;
+ }
+ } else {
+ switch (flag) {
+ case 0: nPts = 16; nColors = 4; break;
+ case 1:
+ case 2:
+ case 3:
+ default: nPts = 12; nColors = 2; break;
+ }
+ }
+ for (i = 0; i < nPts; ++i) {
+ if (!bitBuf->getBits(coordBits, &xi) ||
+ !bitBuf->getBits(coordBits, &yi)) {
+ break;
+ }
+ x[i] = xMin + xMul * (double)xi;
+ y[i] = yMin + yMul * (double)yi;
+ }
+ if (i < nPts) {
+ break;
+ }
+ for (i = 0; i < nColors; ++i) {
+ for (j = 0; j < nComps; ++j) {
+ if (!bitBuf->getBits(compBits, &ci[j])) {
+ break;
+ }
+ c[i][j] = dblToCol(cMin[j] + cMul[j] * (double)ci[j]);
+ }
+ if (j < nComps) {
+ break;
+ }
+ }
+ if (i < nColors) {
+ break;
+ }
+ if (nPatchesA == patchesSize) {
+ patchesSize = (patchesSize == 0) ? 16 : 2 * patchesSize;
+ patchesA = (GfxPatch *)greallocn(patchesA,
+ patchesSize, sizeof(GfxPatch));
+ }
+ p = &patchesA[nPatchesA];
+ if (typeA == 6) {
+ switch (flag) {
+ case 0:
+ p->x[0][0] = x[0];
+ p->y[0][0] = y[0];
+ p->x[0][1] = x[1];
+ p->y[0][1] = y[1];
+ p->x[0][2] = x[2];
+ p->y[0][2] = y[2];
+ p->x[0][3] = x[3];
+ p->y[0][3] = y[3];
+ p->x[1][3] = x[4];
+ p->y[1][3] = y[4];
+ p->x[2][3] = x[5];
+ p->y[2][3] = y[5];
+ p->x[3][3] = x[6];
+ p->y[3][3] = y[6];
+ p->x[3][2] = x[7];
+ p->y[3][2] = y[7];
+ p->x[3][1] = x[8];
+ p->y[3][1] = y[8];
+ p->x[3][0] = x[9];
+ p->y[3][0] = y[9];
+ p->x[2][0] = x[10];
+ p->y[2][0] = y[10];
+ p->x[1][0] = x[11];
+ p->y[1][0] = y[11];
+ for (j = 0; j < nComps; ++j) {
+ p->color[0][0].c[j] = c[0][j];
+ p->color[0][1].c[j] = c[1][j];
+ p->color[1][1].c[j] = c[2][j];
+ p->color[1][0].c[j] = c[3][j];
+ }
+ break;
+ case 1:
+ p->x[0][0] = patchesA[nPatchesA-1].x[0][3];
+ p->y[0][0] = patchesA[nPatchesA-1].y[0][3];
+ p->x[0][1] = patchesA[nPatchesA-1].x[1][3];
+ p->y[0][1] = patchesA[nPatchesA-1].y[1][3];
+ p->x[0][2] = patchesA[nPatchesA-1].x[2][3];
+ p->y[0][2] = patchesA[nPatchesA-1].y[2][3];
+ p->x[0][3] = patchesA[nPatchesA-1].x[3][3];
+ p->y[0][3] = patchesA[nPatchesA-1].y[3][3];
+ p->x[1][3] = x[0];
+ p->y[1][3] = y[0];
+ p->x[2][3] = x[1];
+ p->y[2][3] = y[1];
+ p->x[3][3] = x[2];
+ p->y[3][3] = y[2];
+ p->x[3][2] = x[3];
+ p->y[3][2] = y[3];
+ p->x[3][1] = x[4];
+ p->y[3][1] = y[4];
+ p->x[3][0] = x[5];
+ p->y[3][0] = y[5];
+ p->x[2][0] = x[6];
+ p->y[2][0] = y[6];
+ p->x[1][0] = x[7];
+ p->y[1][0] = y[7];
+ for (j = 0; j < nComps; ++j) {
+ p->color[0][0].c[j] = patchesA[nPatchesA-1].color[0][1].c[j];
+ p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
+ p->color[1][1].c[j] = c[0][j];
+ p->color[1][0].c[j] = c[1][j];
+ }
+ break;
+ case 2:
+ p->x[0][0] = patchesA[nPatchesA-1].x[3][3];
+ p->y[0][0] = patchesA[nPatchesA-1].y[3][3];
+ p->x[0][1] = patchesA[nPatchesA-1].x[3][2];
+ p->y[0][1] = patchesA[nPatchesA-1].y[3][2];
+ p->x[0][2] = patchesA[nPatchesA-1].x[3][1];
+ p->y[0][2] = patchesA[nPatchesA-1].y[3][1];
+ p->x[0][3] = patchesA[nPatchesA-1].x[3][0];
+ p->y[0][3] = patchesA[nPatchesA-1].y[3][0];
+ p->x[1][3] = x[0];
+ p->y[1][3] = y[0];
+ p->x[2][3] = x[1];
+ p->y[2][3] = y[1];
+ p->x[3][3] = x[2];
+ p->y[3][3] = y[2];
+ p->x[3][2] = x[3];
+ p->y[3][2] = y[3];
+ p->x[3][1] = x[4];
+ p->y[3][1] = y[4];
+ p->x[3][0] = x[5];
+ p->y[3][0] = y[5];
+ p->x[2][0] = x[6];
+ p->y[2][0] = y[6];
+ p->x[1][0] = x[7];
+ p->y[1][0] = y[7];
+ for (j = 0; j < nComps; ++j) {
+ p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
+ p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
+ p->color[1][1].c[j] = c[0][j];
+ p->color[1][0].c[j] = c[1][j];
+ }
+ break;
+ case 3:
+ p->x[0][0] = patchesA[nPatchesA-1].x[3][0];
+ p->y[0][0] = patchesA[nPatchesA-1].y[3][0];
+ p->x[0][1] = patchesA[nPatchesA-1].x[2][0];
+ p->y[0][1] = patchesA[nPatchesA-1].y[2][0];
+ p->x[0][2] = patchesA[nPatchesA-1].x[1][0];
+ p->y[0][2] = patchesA[nPatchesA-1].y[1][0];
+ p->x[0][3] = patchesA[nPatchesA-1].x[0][0];
+ p->y[0][3] = patchesA[nPatchesA-1].y[0][0];
+ p->x[1][3] = x[0];
+ p->y[1][3] = y[0];
+ p->x[2][3] = x[1];
+ p->y[2][3] = y[1];
+ p->x[3][3] = x[2];
+ p->y[3][3] = y[2];
+ p->x[3][2] = x[3];
+ p->y[3][2] = y[3];
+ p->x[3][1] = x[4];
+ p->y[3][1] = y[4];
+ p->x[3][0] = x[5];
+ p->y[3][0] = y[5];
+ p->x[2][0] = x[6];
+ p->y[2][0] = y[6];
+ p->x[1][0] = x[7];
+ p->y[1][0] = y[7];
+ for (j = 0; j < nComps; ++j) {
+ p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
+ p->color[0][1].c[j] = patchesA[nPatchesA-1].color[0][0].c[j];
+ p->color[1][1].c[j] = c[0][j];
+ p->color[1][0].c[j] = c[1][j];
+ }
+ break;
+ }
+ } else {
+ switch (flag) {
+ case 0:
+ p->x[0][0] = x[0];
+ p->y[0][0] = y[0];
+ p->x[0][1] = x[1];
+ p->y[0][1] = y[1];
+ p->x[0][2] = x[2];
+ p->y[0][2] = y[2];
+ p->x[0][3] = x[3];
+ p->y[0][3] = y[3];
+ p->x[1][3] = x[4];
+ p->y[1][3] = y[4];
+ p->x[2][3] = x[5];
+ p->y[2][3] = y[5];
+ p->x[3][3] = x[6];
+ p->y[3][3] = y[6];
+ p->x[3][2] = x[7];
+ p->y[3][2] = y[7];
+ p->x[3][1] = x[8];
+ p->y[3][1] = y[8];
+ p->x[3][0] = x[9];
+ p->y[3][0] = y[9];
+ p->x[2][0] = x[10];
+ p->y[2][0] = y[10];
+ p->x[1][0] = x[11];
+ p->y[1][0] = y[11];
+ p->x[1][1] = x[12];
+ p->y[1][1] = y[12];
+ p->x[1][2] = x[13];
+ p->y[1][2] = y[13];
+ p->x[2][2] = x[14];
+ p->y[2][2] = y[14];
+ p->x[2][1] = x[15];
+ p->y[2][1] = y[15];
+ for (j = 0; j < nComps; ++j) {
+ p->color[0][0].c[j] = c[0][j];
+ p->color[0][1].c[j] = c[1][j];
+ p->color[1][1].c[j] = c[2][j];
+ p->color[1][0].c[j] = c[3][j];
+ }
+ break;
+ case 1:
+ p->x[0][0] = patchesA[nPatchesA-1].x[0][3];
+ p->y[0][0] = patchesA[nPatchesA-1].y[0][3];
+ p->x[0][1] = patchesA[nPatchesA-1].x[1][3];
+ p->y[0][1] = patchesA[nPatchesA-1].y[1][3];
+ p->x[0][2] = patchesA[nPatchesA-1].x[2][3];
+ p->y[0][2] = patchesA[nPatchesA-1].y[2][3];
+ p->x[0][3] = patchesA[nPatchesA-1].x[3][3];
+ p->y[0][3] = patchesA[nPatchesA-1].y[3][3];
+ p->x[1][3] = x[0];
+ p->y[1][3] = y[0];
+ p->x[2][3] = x[1];
+ p->y[2][3] = y[1];
+ p->x[3][3] = x[2];
+ p->y[3][3] = y[2];
+ p->x[3][2] = x[3];
+ p->y[3][2] = y[3];
+ p->x[3][1] = x[4];
+ p->y[3][1] = y[4];
+ p->x[3][0] = x[5];
+ p->y[3][0] = y[5];
+ p->x[2][0] = x[6];
+ p->y[2][0] = y[6];
+ p->x[1][0] = x[7];
+ p->y[1][0] = y[7];
+ p->x[1][1] = x[8];
+ p->y[1][1] = y[8];
+ p->x[1][2] = x[9];
+ p->y[1][2] = y[9];
+ p->x[2][2] = x[10];
+ p->y[2][2] = y[10];
+ p->x[2][1] = x[11];
+ p->y[2][1] = y[11];
+ for (j = 0; j < nComps; ++j) {
+ p->color[0][0].c[j] = patchesA[nPatchesA-1].color[0][1].c[j];
+ p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
+ p->color[1][1].c[j] = c[0][j];
+ p->color[1][0].c[j] = c[1][j];
+ }
+ break;
+ case 2:
+ p->x[0][0] = patchesA[nPatchesA-1].x[3][3];
+ p->y[0][0] = patchesA[nPatchesA-1].y[3][3];
+ p->x[0][1] = patchesA[nPatchesA-1].x[3][2];
+ p->y[0][1] = patchesA[nPatchesA-1].y[3][2];
+ p->x[0][2] = patchesA[nPatchesA-1].x[3][1];
+ p->y[0][2] = patchesA[nPatchesA-1].y[3][1];
+ p->x[0][3] = patchesA[nPatchesA-1].x[3][0];
+ p->y[0][3] = patchesA[nPatchesA-1].y[3][0];
+ p->x[1][3] = x[0];
+ p->y[1][3] = y[0];
+ p->x[2][3] = x[1];
+ p->y[2][3] = y[1];
+ p->x[3][3] = x[2];
+ p->y[3][3] = y[2];
+ p->x[3][2] = x[3];
+ p->y[3][2] = y[3];
+ p->x[3][1] = x[4];
+ p->y[3][1] = y[4];
+ p->x[3][0] = x[5];
+ p->y[3][0] = y[5];
+ p->x[2][0] = x[6];
+ p->y[2][0] = y[6];
+ p->x[1][0] = x[7];
+ p->y[1][0] = y[7];
+ p->x[1][1] = x[8];
+ p->y[1][1] = y[8];
+ p->x[1][2] = x[9];
+ p->y[1][2] = y[9];
+ p->x[2][2] = x[10];
+ p->y[2][2] = y[10];
+ p->x[2][1] = x[11];
+ p->y[2][1] = y[11];
+ for (j = 0; j < nComps; ++j) {
+ p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
+ p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
+ p->color[1][1].c[j] = c[0][j];
+ p->color[1][0].c[j] = c[1][j];
+ }
+ break;
+ case 3:
+ p->x[0][0] = patchesA[nPatchesA-1].x[3][0];
+ p->y[0][0] = patchesA[nPatchesA-1].y[3][0];
+ p->x[0][1] = patchesA[nPatchesA-1].x[2][0];
+ p->y[0][1] = patchesA[nPatchesA-1].y[2][0];
+ p->x[0][2] = patchesA[nPatchesA-1].x[1][0];
+ p->y[0][2] = patchesA[nPatchesA-1].y[1][0];
+ p->x[0][3] = patchesA[nPatchesA-1].x[0][0];
+ p->y[0][3] = patchesA[nPatchesA-1].y[0][0];
+ p->x[1][3] = x[0];
+ p->y[1][3] = y[0];
+ p->x[2][3] = x[1];
+ p->y[2][3] = y[1];
+ p->x[3][3] = x[2];
+ p->y[3][3] = y[2];
+ p->x[3][2] = x[3];
+ p->y[3][2] = y[3];
+ p->x[3][1] = x[4];
+ p->y[3][1] = y[4];
+ p->x[3][0] = x[5];
+ p->y[3][0] = y[5];
+ p->x[2][0] = x[6];
+ p->y[2][0] = y[6];
+ p->x[1][0] = x[7];
+ p->y[1][0] = y[7];
+ p->x[1][1] = x[8];
+ p->y[1][1] = y[8];
+ p->x[1][2] = x[9];
+ p->y[1][2] = y[9];
+ p->x[2][2] = x[10];
+ p->y[2][2] = y[10];
+ p->x[2][1] = x[11];
+ p->y[2][1] = y[11];
+ for (j = 0; j < nComps; ++j) {
+ p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
+ p->color[0][1].c[j] = patchesA[nPatchesA-1].color[0][0].c[j];
+ p->color[1][1].c[j] = c[0][j];
+ p->color[1][0].c[j] = c[1][j];
+ }
+ break;
+ }
+ }
+ ++nPatchesA;
+ bitBuf->flushBits();
+ }
+ delete bitBuf;
+
+ if (typeA == 6) {
+ for (i = 0; i < nPatchesA; ++i) {
+ p = &patchesA[i];
+ p->x[1][1] = (-4 * p->x[0][0]
+ +6 * (p->x[0][1] + p->x[1][0])
+ -2 * (p->x[0][3] + p->x[3][0])
+ +3 * (p->x[3][1] + p->x[1][3])
+ - p->x[3][3]) / 9;
+ p->y[1][1] = (-4 * p->y[0][0]
+ +6 * (p->y[0][1] + p->y[1][0])
+ -2 * (p->y[0][3] + p->y[3][0])
+ +3 * (p->y[3][1] + p->y[1][3])
+ - p->y[3][3]) / 9;
+ p->x[1][2] = (-4 * p->x[0][3]
+ +6 * (p->x[0][2] + p->x[1][3])
+ -2 * (p->x[0][0] + p->x[3][3])
+ +3 * (p->x[3][2] + p->x[1][0])
+ - p->x[3][0]) / 9;
+ p->y[1][2] = (-4 * p->y[0][3]
+ +6 * (p->y[0][2] + p->y[1][3])
+ -2 * (p->y[0][0] + p->y[3][3])
+ +3 * (p->y[3][2] + p->y[1][0])
+ - p->y[3][0]) / 9;
+ p->x[2][1] = (-4 * p->x[3][0]
+ +6 * (p->x[3][1] + p->x[2][0])
+ -2 * (p->x[3][3] + p->x[0][0])
+ +3 * (p->x[0][1] + p->x[2][3])
+ - p->x[0][3]) / 9;
+ p->y[2][1] = (-4 * p->y[3][0]
+ +6 * (p->y[3][1] + p->y[2][0])
+ -2 * (p->y[3][3] + p->y[0][0])
+ +3 * (p->y[0][1] + p->y[2][3])
+ - p->y[0][3]) / 9;
+ p->x[2][2] = (-4 * p->x[3][3]
+ +6 * (p->x[3][2] + p->x[2][3])
+ -2 * (p->x[3][0] + p->x[0][3])
+ +3 * (p->x[0][2] + p->x[2][0])
+ - p->x[0][0]) / 9;
+ p->y[2][2] = (-4 * p->y[3][3]
+ +6 * (p->y[3][2] + p->y[2][3])
+ -2 * (p->y[3][0] + p->y[0][3])
+ +3 * (p->y[0][2] + p->y[2][0])
+ - p->y[0][0]) / 9;
+ }
+ }
+
+ shading = new GfxPatchMeshShading(typeA, patchesA, nPatchesA,
+ funcsA, nFuncsA);
+ if (!shading->init(dict)) {
+ delete shading;
+ return NULL;
+ }
+ return shading;
+
+ err2:
+ obj1.free();
+ err1:
+ return NULL;
+}
+
+GfxShading *GfxPatchMeshShading::copy() {
+ return new GfxPatchMeshShading(this);
+}
+
+//------------------------------------------------------------------------
+// GfxImageColorMap
+//------------------------------------------------------------------------
+
+GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode,
+ GfxColorSpace *colorSpaceA) {
+ GfxIndexedColorSpace *indexedCS;
+ GfxSeparationColorSpace *sepCS;
+ int maxPixel, indexHigh;
+ int maxPixelForAlloc;
+ Guchar *lookup2;
+ Function *sepFunc;
+ Object obj;
+ double x[gfxColorMaxComps];
+ double y[gfxColorMaxComps];
+ int i, j, k;
+
+ ok = gTrue;
+
+ // bits per component and color space
+ bits = bitsA;
+ maxPixel = (1 << bits) - 1;
+ maxPixelForAlloc = (1 << (bits>8?bits:8));
+ colorSpace = colorSpaceA;
+
+ // get decode map
+ if (decode->isNull()) {
+ nComps = colorSpace->getNComps();
+ colorSpace->getDefaultRanges(decodeLow, decodeRange, maxPixel);
+ } else if (decode->isArray()) {
+ nComps = decode->arrayGetLength() / 2;
+ if (nComps != colorSpace->getNComps()) {
+ goto err1;
+ }
+ for (i = 0; i < nComps; ++i) {
+ decode->arrayGet(2*i, &obj);
+ if (!obj.isNum()) {
+ goto err2;
+ }
+ decodeLow[i] = obj.getNum();
+ obj.free();
+ decode->arrayGet(2*i+1, &obj);
+ if (!obj.isNum()) {
+ goto err2;
+ }
+ decodeRange[i] = obj.getNum() - decodeLow[i];
+ obj.free();
+ }
+ } else {
+ goto err1;
+ }
+
+ // Construct a lookup table -- this stores pre-computed decoded
+ // values for each component, i.e., the result of applying the
+ // decode mapping to each possible image pixel component value.
+ //
+ // Optimization: for Indexed and Separation color spaces (which have
+ // only one component), we store color values in the lookup table
+ // rather than component values.
+ for (k = 0; k < gfxColorMaxComps; ++k) {
+ lookup[k] = NULL;
+ }
+ colorSpace2 = NULL;
+ nComps2 = 0;
+ if (colorSpace->getMode() == csIndexed) {
+ // Note that indexHigh may not be the same as maxPixel --
+ // Distiller will remove unused palette entries, resulting in
+ // indexHigh < maxPixel.
+ indexedCS = (GfxIndexedColorSpace *)colorSpace;
+ colorSpace2 = indexedCS->getBase();
+ indexHigh = indexedCS->getIndexHigh();
+ nComps2 = colorSpace2->getNComps();
+ lookup2 = indexedCS->getLookup();
+ colorSpace2->getDefaultRanges(x, y, indexHigh);
+ for (k = 0; k < nComps2; ++k) {
+ lookup[k] = (GfxColorComp *)gmallocn(maxPixelForAlloc + 1,
+ sizeof(GfxColorComp));
+ for (i = 0; i <= maxPixel; ++i) {
+ j = (int)(decodeLow[0] + (i * decodeRange[0]) / maxPixel + 0.5);
+ if (j < 0) {
+ j = 0;
+ } else if (j > indexHigh) {
+ j = indexHigh;
+ }
+ lookup[k][i] =
+ dblToCol(x[k] + (lookup2[j*nComps2 + k] / 255.0) * y[k]);
+ }
+ }
+ } else if (colorSpace->getMode() == csSeparation) {
+ sepCS = (GfxSeparationColorSpace *)colorSpace;
+ colorSpace2 = sepCS->getAlt();
+ nComps2 = colorSpace2->getNComps();
+ sepFunc = sepCS->getFunc();
+ for (k = 0; k < nComps2; ++k) {
+ lookup[k] = (GfxColorComp *)gmallocn(maxPixelForAlloc + 1,
+ sizeof(GfxColorComp));
+ for (i = 0; i <= maxPixel; ++i) {
+ x[0] = decodeLow[0] + (i * decodeRange[0]) / maxPixel;
+ sepFunc->transform(x, y);
+ lookup[k][i] = dblToCol(y[k]);
+ }
+ }
+ } else {
+ for (k = 0; k < nComps; ++k) {
+ lookup[k] = (GfxColorComp *)gmallocn(maxPixelForAlloc + 1,
+ sizeof(GfxColorComp));
+ for (i = 0; i <= maxPixel; ++i) {
+ lookup[k][i] = dblToCol(decodeLow[k] +
+ (i * decodeRange[k]) / maxPixel);
+ }
+ }
+ }
+
+ return;
+
+ err2:
+ obj.free();
+ err1:
+ ok = gFalse;
+}
+
+GfxImageColorMap::GfxImageColorMap(GfxImageColorMap *colorMap) {
+ int n, i, k;
+
+ colorSpace = colorMap->colorSpace->copy();
+ bits = colorMap->bits;
+ nComps = colorMap->nComps;
+ nComps2 = colorMap->nComps2;
+ colorSpace2 = NULL;
+ for (k = 0; k < gfxColorMaxComps; ++k) {
+ lookup[k] = NULL;
+ }
+ n = 1 << bits;
+ if (colorSpace->getMode() == csIndexed) {
+ colorSpace2 = ((GfxIndexedColorSpace *)colorSpace)->getBase();
+ for (k = 0; k < nComps2; ++k) {
+ lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
+ memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
+ }
+ } else if (colorSpace->getMode() == csSeparation) {
+ colorSpace2 = ((GfxSeparationColorSpace *)colorSpace)->getAlt();
+ for (k = 0; k < nComps2; ++k) {
+ lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
+ memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
+ }
+ } else {
+ for (k = 0; k < nComps; ++k) {
+ lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
+ memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
+ }
+ }
+ for (i = 0; i < nComps; ++i) {
+ decodeLow[i] = colorMap->decodeLow[i];
+ decodeRange[i] = colorMap->decodeRange[i];
+ }
+ ok = gTrue;
+}
+
+GfxImageColorMap::~GfxImageColorMap() {
+ int i;
+
+ delete colorSpace;
+ for (i = 0; i < gfxColorMaxComps; ++i) {
+ gfree(lookup[i]);
+ }
+}
+
+void GfxImageColorMap::getGray(Guchar *x, GfxGray *gray) {
+ GfxColor color;
+ int i;
+
+ if (colorSpace2) {
+ for (i = 0; i < nComps2; ++i) {
+ color.c[i] = lookup[i][x[0]];
+ }
+ colorSpace2->getGray(&color, gray);
+ } else {
+ for (i = 0; i < nComps; ++i) {
+ color.c[i] = lookup[i][x[i]];
+ }
+ colorSpace->getGray(&color, gray);
+ }
+}
+
+void GfxImageColorMap::getRGB(Guchar *x, GfxRGB *rgb) {
+ GfxColor color;
+ int i;
+
+ if (colorSpace2) {
+ for (i = 0; i < nComps2; ++i) {
+ color.c[i] = lookup[i][x[0]];
+ }
+ colorSpace2->getRGB(&color, rgb);
+ } else {
+ for (i = 0; i < nComps; ++i) {
+ color.c[i] = lookup[i][x[i]];
+ }
+ colorSpace->getRGB(&color, rgb);
+ }
+}
+
+void GfxImageColorMap::getCMYK(Guchar *x, GfxCMYK *cmyk) {
+ GfxColor color;
+ int i;
+
+ if (colorSpace2) {
+ for (i = 0; i < nComps2; ++i) {
+ color.c[i] = lookup[i][x[0]];
+ }
+ colorSpace2->getCMYK(&color, cmyk);
+ } else {
+ for (i = 0; i < nComps; ++i) {
+ color.c[i] = lookup[i][x[i]];
+ }
+ colorSpace->getCMYK(&color, cmyk);
+ }
+}
+
+void GfxImageColorMap::getColor(Guchar *x, GfxColor *color) {
+ int maxPixel, i;
+
+ maxPixel = (1 << bits) - 1;
+ for (i = 0; i < nComps; ++i) {
+ color->c[i] = dblToCol(decodeLow[i] + (x[i] * decodeRange[i]) / maxPixel);
+ }
+}
+
+//------------------------------------------------------------------------
+// GfxSubpath and GfxPath
+//------------------------------------------------------------------------
+
+GfxSubpath::GfxSubpath(double x1, double y1) {
+ size = 16;
+ x = (double *)gmallocn(size, sizeof(double));
+ y = (double *)gmallocn(size, sizeof(double));
+ curve = (GBool *)gmallocn(size, sizeof(GBool));
+ n = 1;
+ x[0] = x1;
+ y[0] = y1;
+ curve[0] = gFalse;
+ closed = gFalse;
+}
+
+GfxSubpath::~GfxSubpath() {
+ gfree(x);
+ gfree(y);
+ gfree(curve);
+}
+
+// Used for copy().
+GfxSubpath::GfxSubpath(GfxSubpath *subpath) {
+ size = subpath->size;
+ n = subpath->n;
+ x = (double *)gmallocn(size, sizeof(double));
+ y = (double *)gmallocn(size, sizeof(double));
+ curve = (GBool *)gmallocn(size, sizeof(GBool));
+ memcpy(x, subpath->x, n * sizeof(double));
+ memcpy(y, subpath->y, n * sizeof(double));
+ memcpy(curve, subpath->curve, n * sizeof(GBool));
+ closed = subpath->closed;
+}
+
+void GfxSubpath::lineTo(double x1, double y1) {
+ if (n >= size) {
+ size += 16;
+ x = (double *)greallocn(x, size, sizeof(double));
+ y = (double *)greallocn(y, size, sizeof(double));
+ curve = (GBool *)greallocn(curve, size, sizeof(GBool));
+ }
+ x[n] = x1;
+ y[n] = y1;
+ curve[n] = gFalse;
+ ++n;
+}
+
+void GfxSubpath::curveTo(double x1, double y1, double x2, double y2,
+ double x3, double y3) {
+ if (n+3 > size) {
+ size += 16;
+ x = (double *)greallocn(x, size, sizeof(double));
+ y = (double *)greallocn(y, size, sizeof(double));
+ curve = (GBool *)greallocn(curve, size, sizeof(GBool));
+ }
+ x[n] = x1;
+ y[n] = y1;
+ x[n+1] = x2;
+ y[n+1] = y2;
+ x[n+2] = x3;
+ y[n+2] = y3;
+ curve[n] = curve[n+1] = gTrue;
+ curve[n+2] = gFalse;
+ n += 3;
+}
+
+void GfxSubpath::close() {
+ if (x[n-1] != x[0] || y[n-1] != y[0]) {
+ lineTo(x[0], y[0]);
+ }
+ closed = gTrue;
+}
+
+void GfxSubpath::offset(double dx, double dy) {
+ int i;
+
+ for (i = 0; i < n; ++i) {
+ x[i] += dx;
+ y[i] += dy;
+ }
+}
+
+GfxPath::GfxPath() {
+ justMoved = gFalse;
+ size = 16;
+ n = 0;
+ firstX = firstY = 0;
+ subpaths = (GfxSubpath **)gmallocn(size, sizeof(GfxSubpath *));
+}
+
+GfxPath::~GfxPath() {
+ int i;
+
+ for (i = 0; i < n; ++i)
+ delete subpaths[i];
+ gfree(subpaths);
+}
+
+// Used for copy().
+GfxPath::GfxPath(GBool justMoved1, double firstX1, double firstY1,
+ GfxSubpath **subpaths1, int n1, int size1) {
+ int i;
+
+ justMoved = justMoved1;
+ firstX = firstX1;
+ firstY = firstY1;
+ size = size1;
+ n = n1;
+ subpaths = (GfxSubpath **)gmallocn(size, sizeof(GfxSubpath *));
+ for (i = 0; i < n; ++i)
+ subpaths[i] = subpaths1[i]->copy();
+}
+
+void GfxPath::moveTo(double x, double y) {
+ justMoved = gTrue;
+ firstX = x;
+ firstY = y;
+}
+
+void GfxPath::lineTo(double x, double y) {
+ if (justMoved) {
+ if (n >= size) {
+ size += 16;
+ subpaths = (GfxSubpath **)
+ greallocn(subpaths, size, sizeof(GfxSubpath *));
+ }
+ subpaths[n] = new GfxSubpath(firstX, firstY);
+ ++n;
+ justMoved = gFalse;
+ }
+ subpaths[n-1]->lineTo(x, y);
+}
+
+void GfxPath::curveTo(double x1, double y1, double x2, double y2,
+ double x3, double y3) {
+ if (justMoved) {
+ if (n >= size) {
+ size += 16;
+ subpaths = (GfxSubpath **)
+ greallocn(subpaths, size, sizeof(GfxSubpath *));
+ }
+ subpaths[n] = new GfxSubpath(firstX, firstY);
+ ++n;
+ justMoved = gFalse;
+ }
+ subpaths[n-1]->curveTo(x1, y1, x2, y2, x3, y3);
+}
+
+void GfxPath::close() {
+ // this is necessary to handle the pathological case of
+ // moveto/closepath/clip, which defines an empty clipping region
+ if (justMoved) {
+ if (n >= size) {
+ size += 16;
+ subpaths = (GfxSubpath **)
+ greallocn(subpaths, size, sizeof(GfxSubpath *));
+ }
+ subpaths[n] = new GfxSubpath(firstX, firstY);
+ ++n;
+ justMoved = gFalse;
+ }
+ subpaths[n-1]->close();
+}
+
+void GfxPath::append(GfxPath *path) {
+ int i;
+
+ if (n + path->n > size) {
+ size = n + path->n;
+ subpaths = (GfxSubpath **)
+ greallocn(subpaths, size, sizeof(GfxSubpath *));
+ }
+ for (i = 0; i < path->n; ++i) {
+ subpaths[n++] = path->subpaths[i]->copy();
+ }
+ justMoved = gFalse;
+}
+
+void GfxPath::offset(double dx, double dy) {
+ int i;
+
+ for (i = 0; i < n; ++i) {
+ subpaths[i]->offset(dx, dy);
+ }
+}
+
+//------------------------------------------------------------------------
+// GfxState
+//------------------------------------------------------------------------
+
+GfxState::GfxState(double hDPI, double vDPI, PDFRectangle *pageBox,
+ int rotateA, GBool upsideDown) {
+ double kx, ky;
+
+ rotate = rotateA;
+ px1 = pageBox->x1;
+ py1 = pageBox->y1;
+ px2 = pageBox->x2;
+ py2 = pageBox->y2;
+ kx = hDPI / 72.0;
+ ky = vDPI / 72.0;
+ if (rotate == 90) {
+ ctm[0] = 0;
+ ctm[1] = upsideDown ? ky : -ky;
+ ctm[2] = kx;
+ ctm[3] = 0;
+ ctm[4] = -kx * py1;
+ ctm[5] = ky * (upsideDown ? -px1 : px2);
+ pageWidth = kx * (py2 - py1);
+ pageHeight = ky * (px2 - px1);
+ } else if (rotate == 180) {
+ ctm[0] = -kx;
+ ctm[1] = 0;
+ ctm[2] = 0;
+ ctm[3] = upsideDown ? ky : -ky;
+ ctm[4] = kx * px2;
+ ctm[5] = ky * (upsideDown ? -py1 : py2);
+ pageWidth = kx * (px2 - px1);
+ pageHeight = ky * (py2 - py1);
+ } else if (rotate == 270) {
+ ctm[0] = 0;
+ ctm[1] = upsideDown ? -ky : ky;
+ ctm[2] = -kx;
+ ctm[3] = 0;
+ ctm[4] = kx * py2;
+ ctm[5] = ky * (upsideDown ? px2 : -px1);
+ pageWidth = kx * (py2 - py1);
+ pageHeight = ky * (px2 - px1);
+ } else {
+ ctm[0] = kx;
+ ctm[1] = 0;
+ ctm[2] = 0;
+ ctm[3] = upsideDown ? -ky : ky;
+ ctm[4] = -kx * px1;
+ ctm[5] = ky * (upsideDown ? py2 : -py1);
+ pageWidth = kx * (px2 - px1);
+ pageHeight = ky * (py2 - py1);
+ }
+
+ fillColorSpace = new GfxDeviceGrayColorSpace();
+ strokeColorSpace = new GfxDeviceGrayColorSpace();
+ fillColor.c[0] = 0;
+ strokeColor.c[0] = 0;
+ fillPattern = NULL;
+ strokePattern = NULL;
+ blendMode = gfxBlendNormal;
+ fillOpacity = 1;
+ strokeOpacity = 1;
+ fillOverprint = gFalse;
+ strokeOverprint = gFalse;
+
+ lineWidth = 1;
+ lineDash = NULL;
+ lineDashLength = 0;
+ lineDashStart = 0;
+ flatness = 1;
+ lineJoin = 0;
+ lineCap = 0;
+ miterLimit = 10;
+
+ font = NULL;
+ fontSize = 0;
+ textMat[0] = 1; textMat[1] = 0;
+ textMat[2] = 0; textMat[3] = 1;
+ textMat[4] = 0; textMat[5] = 0;
+ charSpace = 0;
+ wordSpace = 0;
+ horizScaling = 1;
+ leading = 0;
+ rise = 0;
+ render = 0;
+
+ path = new GfxPath();
+ curX = curY = 0;
+ lineX = lineY = 0;
+
+ clipXMin = 0;
+ clipYMin = 0;
+ clipXMax = pageWidth;
+ clipYMax = pageHeight;
+
+ saved = NULL;
+}
+
+GfxState::~GfxState() {
+ if (fillColorSpace) {
+ delete fillColorSpace;
+ }
+ if (strokeColorSpace) {
+ delete strokeColorSpace;
+ }
+ if (fillPattern) {
+ delete fillPattern;
+ }
+ if (strokePattern) {
+ delete strokePattern;
+ }
+ gfree(lineDash);
+ if (path) {
+ // this gets set to NULL by restore()
+ delete path;
+ }
+ if (saved) {
+ delete saved;
+ }
+}
+
+// Used for copy();
+GfxState::GfxState(GfxState *state) {
+ memcpy(this, state, sizeof(GfxState));
+ if (fillColorSpace) {
+ fillColorSpace = state->fillColorSpace->copy();
+ }
+ if (strokeColorSpace) {
+ strokeColorSpace = state->strokeColorSpace->copy();
+ }
+ if (fillPattern) {
+ fillPattern = state->fillPattern->copy();
+ }
+ if (strokePattern) {
+ strokePattern = state->strokePattern->copy();
+ }
+ if (lineDashLength > 0) {
+ lineDash = (double *)gmallocn(lineDashLength, sizeof(double));
+ memcpy(lineDash, state->lineDash, lineDashLength * sizeof(double));
+ }
+ saved = NULL;
+}
+
+void GfxState::setPath(GfxPath *pathA) {
+ delete path;
+ path = pathA;
+}
+
+void GfxState::getUserClipBBox(double *xMin, double *yMin,
+ double *xMax, double *yMax) {
+ double ictm[6];
+ double xMin1, yMin1, xMax1, yMax1, det, tx, ty;
+
+ // invert the CTM
+ det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
+ ictm[0] = ctm[3] * det;
+ ictm[1] = -ctm[1] * det;
+ ictm[2] = -ctm[2] * det;
+ ictm[3] = ctm[0] * det;
+ ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
+ ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
+
+ // transform all four corners of the clip bbox; find the min and max
+ // x and y values
+ xMin1 = xMax1 = clipXMin * ictm[0] + clipYMin * ictm[2] + ictm[4];
+ yMin1 = yMax1 = clipXMin * ictm[1] + clipYMin * ictm[3] + ictm[5];
+ tx = clipXMin * ictm[0] + clipYMax * ictm[2] + ictm[4];
+ ty = clipXMin * ictm[1] + clipYMax * ictm[3] + ictm[5];
+ if (tx < xMin1) {
+ xMin1 = tx;
+ } else if (tx > xMax1) {
+ xMax1 = tx;
+ }
+ if (ty < yMin1) {
+ yMin1 = ty;
+ } else if (ty > yMax1) {
+ yMax1 = ty;
+ }
+ tx = clipXMax * ictm[0] + clipYMin * ictm[2] + ictm[4];
+ ty = clipXMax * ictm[1] + clipYMin * ictm[3] + ictm[5];
+ if (tx < xMin1) {
+ xMin1 = tx;
+ } else if (tx > xMax1) {
+ xMax1 = tx;
+ }
+ if (ty < yMin1) {
+ yMin1 = ty;
+ } else if (ty > yMax1) {
+ yMax1 = ty;
+ }
+ tx = clipXMax * ictm[0] + clipYMax * ictm[2] + ictm[4];
+ ty = clipXMax * ictm[1] + clipYMax * ictm[3] + ictm[5];
+ if (tx < xMin1) {
+ xMin1 = tx;
+ } else if (tx > xMax1) {
+ xMax1 = tx;
+ }
+ if (ty < yMin1) {
+ yMin1 = ty;
+ } else if (ty > yMax1) {
+ yMax1 = ty;
+ }
+
+ *xMin = xMin1;
+ *yMin = yMin1;
+ *xMax = xMax1;
+ *yMax = yMax1;
+}
+
+double GfxState::transformWidth(double w) {
+ double x, y;
+
+ x = ctm[0] + ctm[2];
+ y = ctm[1] + ctm[3];
+ return w * sqrt(0.5 * (x * x + y * y));
+}
+
+double GfxState::getTransformedFontSize() {
+ double x1, y1, x2, y2;
+
+ x1 = textMat[2] * fontSize;
+ y1 = textMat[3] * fontSize;
+ x2 = ctm[0] * x1 + ctm[2] * y1;
+ y2 = ctm[1] * x1 + ctm[3] * y1;
+ return sqrt(x2 * x2 + y2 * y2);
+}
+
+void GfxState::getFontTransMat(double *m11, double *m12,
+ double *m21, double *m22) {
+ *m11 = (textMat[0] * ctm[0] + textMat[1] * ctm[2]) * fontSize;
+ *m12 = (textMat[0] * ctm[1] + textMat[1] * ctm[3]) * fontSize;
+ *m21 = (textMat[2] * ctm[0] + textMat[3] * ctm[2]) * fontSize;
+ *m22 = (textMat[2] * ctm[1] + textMat[3] * ctm[3]) * fontSize;
+}
+
+void GfxState::setCTM(double a, double b, double c,
+ double d, double e, double f) {
+ int i;
+
+ ctm[0] = a;
+ ctm[1] = b;
+ ctm[2] = c;
+ ctm[3] = d;
+ ctm[4] = e;
+ ctm[5] = f;
+
+ // avoid FP exceptions on badly messed up PDF files
+ for (i = 0; i < 6; ++i) {
+ if (ctm[i] > 1e10) {
+ ctm[i] = 1e10;
+ } else if (ctm[i] < -1e10) {
+ ctm[i] = -1e10;
+ }
+ }
+}
+
+void GfxState::concatCTM(double a, double b, double c,
+ double d, double e, double f) {
+ double a1 = ctm[0];
+ double b1 = ctm[1];
+ double c1 = ctm[2];
+ double d1 = ctm[3];
+ int i;
+
+ ctm[0] = a * a1 + b * c1;
+ ctm[1] = a * b1 + b * d1;
+ ctm[2] = c * a1 + d * c1;
+ ctm[3] = c * b1 + d * d1;
+ ctm[4] = e * a1 + f * c1 + ctm[4];
+ ctm[5] = e * b1 + f * d1 + ctm[5];
+
+ // avoid FP exceptions on badly messed up PDF files
+ for (i = 0; i < 6; ++i) {
+ if (ctm[i] > 1e10) {
+ ctm[i] = 1e10;
+ } else if (ctm[i] < -1e10) {
+ ctm[i] = -1e10;
+ }
+ }
+}
+
+void GfxState::setFillColorSpace(GfxColorSpace *colorSpace) {
+ if (fillColorSpace) {
+ delete fillColorSpace;
+ }
+ fillColorSpace = colorSpace;
+}
+
+void GfxState::setStrokeColorSpace(GfxColorSpace *colorSpace) {
+ if (strokeColorSpace) {
+ delete strokeColorSpace;
+ }
+ strokeColorSpace = colorSpace;
+}
+
+void GfxState::setFillPattern(GfxPattern *pattern) {
+ if (fillPattern) {
+ delete fillPattern;
+ }
+ fillPattern = pattern;
+}
+
+void GfxState::setStrokePattern(GfxPattern *pattern) {
+ if (strokePattern) {
+ delete strokePattern;
+ }
+ strokePattern = pattern;
+}
+
+void GfxState::setLineDash(double *dash, int length, double start) {
+ if (lineDash)
+ gfree(lineDash);
+ lineDash = dash;
+ lineDashLength = length;
+ lineDashStart = start;
+}
+
+void GfxState::clearPath() {
+ delete path;
+ path = new GfxPath();
+}
+
+void GfxState::clip() {
+ double xMin, yMin, xMax, yMax, x, y;
+ GfxSubpath *subpath;
+ int i, j;
+
+ xMin = xMax = yMin = yMax = 0; // make gcc happy
+ for (i = 0; i < path->getNumSubpaths(); ++i) {
+ subpath = path->getSubpath(i);
+ for (j = 0; j < subpath->getNumPoints(); ++j) {
+ transform(subpath->getX(j), subpath->getY(j), &x, &y);
+ if (i == 0 && j == 0) {
+ xMin = xMax = x;
+ yMin = yMax = y;
+ } else {
+ if (x < xMin) {
+ xMin = x;
+ } else if (x > xMax) {
+ xMax = x;
+ }
+ if (y < yMin) {
+ yMin = y;
+ } else if (y > yMax) {
+ yMax = y;
+ }
+ }
+ }
+ }
+ if (xMin > clipXMin) {
+ clipXMin = xMin;
+ }
+ if (yMin > clipYMin) {
+ clipYMin = yMin;
+ }
+ if (xMax < clipXMax) {
+ clipXMax = xMax;
+ }
+ if (yMax < clipYMax) {
+ clipYMax = yMax;
+ }
+}
+
+void GfxState::textShift(double tx, double ty) {
+ double dx, dy;
+
+ textTransformDelta(tx, ty, &dx, &dy);
+ curX += dx;
+ curY += dy;
+}
+
+void GfxState::shift(double dx, double dy) {
+ curX += dx;
+ curY += dy;
+}
+
+GfxState *GfxState::save() {
+ GfxState *newState;
+
+ newState = copy();
+ newState->saved = this;
+ return newState;
+}
+
+GfxState *GfxState::restore() {
+ GfxState *oldState;
+
+ if (saved) {
+ oldState = saved;
+
+ // these attributes aren't saved/restored by the q/Q operators
+ oldState->path = path;
+ oldState->curX = curX;
+ oldState->curY = curY;
+ oldState->lineX = lineX;
+ oldState->lineY = lineY;
+
+ path = NULL;
+ saved = NULL;
+ delete this;
+
+ } else {
+ oldState = this;
+ }
+
+ return oldState;
+}
+
+GBool GfxState::parseBlendMode(Object *obj, GfxBlendMode *mode) {
+ Object obj2;
+ int i, j;
+
+ if (obj->isName()) {
+ for (i = 0; i < nGfxBlendModeNames; ++i) {
+ if (!strcmp(obj->getName(), gfxBlendModeNames[i].name)) {
+ *mode = gfxBlendModeNames[i].mode;
+ return gTrue;
+ }
+ }
+ return gFalse;
+ } else if (obj->isArray()) {
+ for (i = 0; i < obj->arrayGetLength(); ++i) {
+ obj->arrayGet(i, &obj2);
+ if (!obj2.isName()) {
+ obj2.free();
+ return gFalse;
+ }
+ for (j = 0; j < nGfxBlendModeNames; ++j) {
+ if (!strcmp(obj2.getName(), gfxBlendModeNames[j].name)) {
+ obj2.free();
+ *mode = gfxBlendModeNames[j].mode;
+ return gTrue;
+ }
+ }
+ obj2.free();
+ }
+ *mode = gfxBlendNormal;
+ return gTrue;
+ } else {
+ return gFalse;
+ }
+}
--- /dev/null
+//========================================================================
+//
+// GfxState.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GFXSTATE_H
+#define GFXSTATE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "Object.h"
+#include "Function.h"
+
+class Array;
+class GfxFont;
+class PDFRectangle;
+class GfxShading;
+
+//------------------------------------------------------------------------
+// GfxBlendMode
+//------------------------------------------------------------------------
+
+enum GfxBlendMode {
+ gfxBlendNormal,
+ gfxBlendMultiply,
+ gfxBlendScreen,
+ gfxBlendOverlay,
+ gfxBlendDarken,
+ gfxBlendLighten,
+ gfxBlendColorDodge,
+ gfxBlendColorBurn,
+ gfxBlendHardLight,
+ gfxBlendSoftLight,
+ gfxBlendDifference,
+ gfxBlendExclusion,
+ gfxBlendHue,
+ gfxBlendSaturation,
+ gfxBlendColor,
+ gfxBlendLuminosity
+};
+
+//------------------------------------------------------------------------
+// GfxColorComp
+//------------------------------------------------------------------------
+
+// 16.16 fixed point color component
+typedef int GfxColorComp;
+
+#define gfxColorComp1 0x10000
+
+static inline GfxColorComp dblToCol(double x) {
+ return (GfxColorComp)(x * gfxColorComp1);
+}
+
+static inline double colToDbl(GfxColorComp x) {
+ return (double)x / (double)gfxColorComp1;
+}
+
+static inline GfxColorComp byteToCol(Guchar x) {
+ // (x / 255) << 16 = (0.0000000100000001... * x) << 16
+ // = ((x << 8) + (x) + (x >> 8) + ...) << 16
+ // = (x << 8) + (x) + (x >> 7)
+ // [for rounding]
+ return (GfxColorComp)((x << 8) + x + (x >> 7));
+}
+
+static inline Guchar colToByte(GfxColorComp x) {
+ // 255 * x + 0.5 = 256 * x - x + 0x8000
+ return (Guchar)(((x << 8) - x + 0x8000) >> 16);
+}
+
+//------------------------------------------------------------------------
+// GfxColor
+//------------------------------------------------------------------------
+
+#define gfxColorMaxComps funcMaxOutputs
+
+struct GfxColor {
+ GfxColorComp c[gfxColorMaxComps];
+};
+
+//------------------------------------------------------------------------
+// GfxGray
+//------------------------------------------------------------------------
+
+typedef GfxColorComp GfxGray;
+
+//------------------------------------------------------------------------
+// GfxRGB
+//------------------------------------------------------------------------
+
+struct GfxRGB {
+ GfxColorComp r, g, b;
+};
+
+//------------------------------------------------------------------------
+// GfxCMYK
+//------------------------------------------------------------------------
+
+struct GfxCMYK {
+ GfxColorComp c, m, y, k;
+};
+
+//------------------------------------------------------------------------
+// GfxColorSpace
+//------------------------------------------------------------------------
+
+// NB: The nGfxColorSpaceModes constant and the gfxColorSpaceModeNames
+// array defined in GfxState.cc must match this enum.
+enum GfxColorSpaceMode {
+ csDeviceGray,
+ csCalGray,
+ csDeviceRGB,
+ csCalRGB,
+ csDeviceCMYK,
+ csLab,
+ csICCBased,
+ csIndexed,
+ csSeparation,
+ csDeviceN,
+ csPattern
+};
+
+class GfxColorSpace {
+public:
+
+ GfxColorSpace();
+ virtual ~GfxColorSpace();
+ virtual GfxColorSpace *copy() = 0;
+ virtual GfxColorSpaceMode getMode() = 0;
+
+ // Construct a color space. Returns NULL if unsuccessful.
+ static GfxColorSpace *parse(Object *csObj);
+
+ // Convert to gray, RGB, or CMYK.
+ virtual void getGray(GfxColor *color, GfxGray *gray) = 0;
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb) = 0;
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk) = 0;
+
+ // Return the number of color components.
+ virtual int getNComps() = 0;
+
+ // Return the default ranges for each component, assuming an image
+ // with a max pixel value of <maxImgPixel>.
+ virtual void getDefaultRanges(double *decodeLow, double *decodeRange,
+ int maxImgPixel);
+
+ // Return the number of color space modes
+ static int getNumColorSpaceModes();
+
+ // Return the name of the <idx>th color space mode.
+ static char *getColorSpaceModeName(int idx);
+
+private:
+};
+
+//------------------------------------------------------------------------
+// GfxDeviceGrayColorSpace
+//------------------------------------------------------------------------
+
+class GfxDeviceGrayColorSpace: public GfxColorSpace {
+public:
+
+ GfxDeviceGrayColorSpace();
+ virtual ~GfxDeviceGrayColorSpace();
+ virtual GfxColorSpace *copy();
+ virtual GfxColorSpaceMode getMode() { return csDeviceGray; }
+
+ virtual void getGray(GfxColor *color, GfxGray *gray);
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb);
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+
+ virtual int getNComps() { return 1; }
+
+private:
+};
+
+//------------------------------------------------------------------------
+// GfxCalGrayColorSpace
+//------------------------------------------------------------------------
+
+class GfxCalGrayColorSpace: public GfxColorSpace {
+public:
+
+ GfxCalGrayColorSpace();
+ virtual ~GfxCalGrayColorSpace();
+ virtual GfxColorSpace *copy();
+ virtual GfxColorSpaceMode getMode() { return csCalGray; }
+
+ // Construct a CalGray color space. Returns NULL if unsuccessful.
+ static GfxColorSpace *parse(Array *arr);
+
+ virtual void getGray(GfxColor *color, GfxGray *gray);
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb);
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+
+ virtual int getNComps() { return 1; }
+
+ // CalGray-specific access.
+ double getWhiteX() { return whiteX; }
+ double getWhiteY() { return whiteY; }
+ double getWhiteZ() { return whiteZ; }
+ double getBlackX() { return blackX; }
+ double getBlackY() { return blackY; }
+ double getBlackZ() { return blackZ; }
+ double getGamma() { return gamma; }
+
+private:
+
+ double whiteX, whiteY, whiteZ; // white point
+ double blackX, blackY, blackZ; // black point
+ double gamma; // gamma value
+};
+
+//------------------------------------------------------------------------
+// GfxDeviceRGBColorSpace
+//------------------------------------------------------------------------
+
+class GfxDeviceRGBColorSpace: public GfxColorSpace {
+public:
+
+ GfxDeviceRGBColorSpace();
+ virtual ~GfxDeviceRGBColorSpace();
+ virtual GfxColorSpace *copy();
+ virtual GfxColorSpaceMode getMode() { return csDeviceRGB; }
+
+ virtual void getGray(GfxColor *color, GfxGray *gray);
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb);
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+
+ virtual int getNComps() { return 3; }
+
+private:
+};
+
+//------------------------------------------------------------------------
+// GfxCalRGBColorSpace
+//------------------------------------------------------------------------
+
+class GfxCalRGBColorSpace: public GfxColorSpace {
+public:
+
+ GfxCalRGBColorSpace();
+ virtual ~GfxCalRGBColorSpace();
+ virtual GfxColorSpace *copy();
+ virtual GfxColorSpaceMode getMode() { return csCalRGB; }
+
+ // Construct a CalRGB color space. Returns NULL if unsuccessful.
+ static GfxColorSpace *parse(Array *arr);
+
+ virtual void getGray(GfxColor *color, GfxGray *gray);
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb);
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+
+ virtual int getNComps() { return 3; }
+
+ // CalRGB-specific access.
+ double getWhiteX() { return whiteX; }
+ double getWhiteY() { return whiteY; }
+ double getWhiteZ() { return whiteZ; }
+ double getBlackX() { return blackX; }
+ double getBlackY() { return blackY; }
+ double getBlackZ() { return blackZ; }
+ double getGammaR() { return gammaR; }
+ double getGammaG() { return gammaG; }
+ double getGammaB() { return gammaB; }
+ double *getMatrix() { return mat; }
+
+private:
+
+ double whiteX, whiteY, whiteZ; // white point
+ double blackX, blackY, blackZ; // black point
+ double gammaR, gammaG, gammaB; // gamma values
+ double mat[9]; // ABC -> XYZ transform matrix
+};
+
+//------------------------------------------------------------------------
+// GfxDeviceCMYKColorSpace
+//------------------------------------------------------------------------
+
+class GfxDeviceCMYKColorSpace: public GfxColorSpace {
+public:
+
+ GfxDeviceCMYKColorSpace();
+ virtual ~GfxDeviceCMYKColorSpace();
+ virtual GfxColorSpace *copy();
+ virtual GfxColorSpaceMode getMode() { return csDeviceCMYK; }
+
+ virtual void getGray(GfxColor *color, GfxGray *gray);
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb);
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+
+ virtual int getNComps() { return 4; }
+
+private:
+};
+
+//------------------------------------------------------------------------
+// GfxLabColorSpace
+//------------------------------------------------------------------------
+
+class GfxLabColorSpace: public GfxColorSpace {
+public:
+
+ GfxLabColorSpace();
+ virtual ~GfxLabColorSpace();
+ virtual GfxColorSpace *copy();
+ virtual GfxColorSpaceMode getMode() { return csLab; }
+
+ // Construct a Lab color space. Returns NULL if unsuccessful.
+ static GfxColorSpace *parse(Array *arr);
+
+ virtual void getGray(GfxColor *color, GfxGray *gray);
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb);
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+
+ virtual int getNComps() { return 3; }
+
+ virtual void getDefaultRanges(double *decodeLow, double *decodeRange,
+ int maxImgPixel);
+
+ // Lab-specific access.
+ double getWhiteX() { return whiteX; }
+ double getWhiteY() { return whiteY; }
+ double getWhiteZ() { return whiteZ; }
+ double getBlackX() { return blackX; }
+ double getBlackY() { return blackY; }
+ double getBlackZ() { return blackZ; }
+ double getAMin() { return aMin; }
+ double getAMax() { return aMax; }
+ double getBMin() { return bMin; }
+ double getBMax() { return bMax; }
+
+private:
+
+ double whiteX, whiteY, whiteZ; // white point
+ double blackX, blackY, blackZ; // black point
+ double aMin, aMax, bMin, bMax; // range for the a and b components
+ double kr, kg, kb; // gamut mapping mulitpliers
+};
+
+//------------------------------------------------------------------------
+// GfxICCBasedColorSpace
+//------------------------------------------------------------------------
+
+class GfxICCBasedColorSpace: public GfxColorSpace {
+public:
+
+ GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA,
+ Ref *iccProfileStreamA);
+ virtual ~GfxICCBasedColorSpace();
+ virtual GfxColorSpace *copy();
+ virtual GfxColorSpaceMode getMode() { return csICCBased; }
+
+ // Construct an ICCBased color space. Returns NULL if unsuccessful.
+ static GfxColorSpace *parse(Array *arr);
+
+ virtual void getGray(GfxColor *color, GfxGray *gray);
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb);
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+
+ virtual int getNComps() { return nComps; }
+
+ virtual void getDefaultRanges(double *decodeLow, double *decodeRange,
+ int maxImgPixel);
+
+ // ICCBased-specific access.
+ GfxColorSpace *getAlt() { return alt; }
+
+private:
+
+ int nComps; // number of color components (1, 3, or 4)
+ GfxColorSpace *alt; // alternate color space
+ double rangeMin[4]; // min values for each component
+ double rangeMax[4]; // max values for each component
+ Ref iccProfileStream; // the ICC profile
+};
+
+//------------------------------------------------------------------------
+// GfxIndexedColorSpace
+//------------------------------------------------------------------------
+
+class GfxIndexedColorSpace: public GfxColorSpace {
+public:
+
+ GfxIndexedColorSpace(GfxColorSpace *baseA, int indexHighA);
+ virtual ~GfxIndexedColorSpace();
+ virtual GfxColorSpace *copy();
+ virtual GfxColorSpaceMode getMode() { return csIndexed; }
+
+ // Construct a Lab color space. Returns NULL if unsuccessful.
+ static GfxColorSpace *parse(Array *arr);
+
+ virtual void getGray(GfxColor *color, GfxGray *gray);
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb);
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+
+ virtual int getNComps() { return 1; }
+
+ virtual void getDefaultRanges(double *decodeLow, double *decodeRange,
+ int maxImgPixel);
+
+ // Indexed-specific access.
+ GfxColorSpace *getBase() { return base; }
+ int getIndexHigh() { return indexHigh; }
+ Guchar *getLookup() { return lookup; }
+ GfxColor *mapColorToBase(GfxColor *color, GfxColor *baseColor);
+
+private:
+
+ GfxColorSpace *base; // base color space
+ int indexHigh; // max pixel value
+ Guchar *lookup; // lookup table
+};
+
+//------------------------------------------------------------------------
+// GfxSeparationColorSpace
+//------------------------------------------------------------------------
+
+class GfxSeparationColorSpace: public GfxColorSpace {
+public:
+
+ GfxSeparationColorSpace(GString *nameA, GfxColorSpace *altA,
+ Function *funcA);
+ virtual ~GfxSeparationColorSpace();
+ virtual GfxColorSpace *copy();
+ virtual GfxColorSpaceMode getMode() { return csSeparation; }
+
+ // Construct a Separation color space. Returns NULL if unsuccessful.
+ static GfxColorSpace *parse(Array *arr);
+
+ virtual void getGray(GfxColor *color, GfxGray *gray);
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb);
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+
+ virtual int getNComps() { return 1; }
+
+ // Separation-specific access.
+ GString *getName() { return name; }
+ GfxColorSpace *getAlt() { return alt; }
+ Function *getFunc() { return func; }
+
+private:
+
+ GString *name; // colorant name
+ GfxColorSpace *alt; // alternate color space
+ Function *func; // tint transform (into alternate color space)
+};
+
+//------------------------------------------------------------------------
+// GfxDeviceNColorSpace
+//------------------------------------------------------------------------
+
+class GfxDeviceNColorSpace: public GfxColorSpace {
+public:
+
+ GfxDeviceNColorSpace(int nCompsA, GfxColorSpace *alt, Function *func);
+ virtual ~GfxDeviceNColorSpace();
+ virtual GfxColorSpace *copy();
+ virtual GfxColorSpaceMode getMode() { return csDeviceN; }
+
+ // Construct a DeviceN color space. Returns NULL if unsuccessful.
+ static GfxColorSpace *parse(Array *arr);
+
+ virtual void getGray(GfxColor *color, GfxGray *gray);
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb);
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+
+ virtual int getNComps() { return nComps; }
+
+ // DeviceN-specific access.
+ GString *getColorantName(int i) { return names[i]; }
+ GfxColorSpace *getAlt() { return alt; }
+ Function *getTintTransformFunc() { return func; }
+
+private:
+
+ int nComps; // number of components
+ GString // colorant names
+ *names[gfxColorMaxComps];
+ GfxColorSpace *alt; // alternate color space
+ Function *func; // tint transform (into alternate color space)
+};
+
+//------------------------------------------------------------------------
+// GfxPatternColorSpace
+//------------------------------------------------------------------------
+
+class GfxPatternColorSpace: public GfxColorSpace {
+public:
+
+ GfxPatternColorSpace(GfxColorSpace *underA);
+ virtual ~GfxPatternColorSpace();
+ virtual GfxColorSpace *copy();
+ virtual GfxColorSpaceMode getMode() { return csPattern; }
+
+ // Construct a Pattern color space. Returns NULL if unsuccessful.
+ static GfxColorSpace *parse(Array *arr);
+
+ virtual void getGray(GfxColor *color, GfxGray *gray);
+ virtual void getRGB(GfxColor *color, GfxRGB *rgb);
+ virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
+
+ virtual int getNComps() { return 0; }
+
+ // Pattern-specific access.
+ GfxColorSpace *getUnder() { return under; }
+
+private:
+
+ GfxColorSpace *under; // underlying color space (for uncolored
+ // patterns)
+};
+
+//------------------------------------------------------------------------
+// GfxPattern
+//------------------------------------------------------------------------
+
+class GfxPattern {
+public:
+
+ GfxPattern(int typeA);
+ virtual ~GfxPattern();
+
+ static GfxPattern *parse(Object *obj);
+
+ virtual GfxPattern *copy() = 0;
+
+ int getType() { return type; }
+
+private:
+
+ int type;
+};
+
+//------------------------------------------------------------------------
+// GfxTilingPattern
+//------------------------------------------------------------------------
+
+class GfxTilingPattern: public GfxPattern {
+public:
+
+ static GfxTilingPattern *parse(Object *patObj);
+ virtual ~GfxTilingPattern();
+
+ virtual GfxPattern *copy();
+
+ int getPaintType() { return paintType; }
+ int getTilingType() { return tilingType; }
+ double *getBBox() { return bbox; }
+ double getXStep() { return xStep; }
+ double getYStep() { return yStep; }
+ Dict *getResDict()
+ { return resDict.isDict() ? resDict.getDict() : (Dict *)NULL; }
+ double *getMatrix() { return matrix; }
+ Object *getContentStream() { return &contentStream; }
+
+private:
+
+ GfxTilingPattern(int paintTypeA, int tilingTypeA,
+ double *bboxA, double xStepA, double yStepA,
+ Object *resDictA, double *matrixA,
+ Object *contentStreamA);
+
+ int paintType;
+ int tilingType;
+ double bbox[4];
+ double xStep, yStep;
+ Object resDict;
+ double matrix[6];
+ Object contentStream;
+};
+
+//------------------------------------------------------------------------
+// GfxShadingPattern
+//------------------------------------------------------------------------
+
+class GfxShadingPattern: public GfxPattern {
+public:
+
+ static GfxShadingPattern *parse(Object *patObj);
+ virtual ~GfxShadingPattern();
+
+ virtual GfxPattern *copy();
+
+ GfxShading *getShading() { return shading; }
+ double *getMatrix() { return matrix; }
+
+private:
+
+ GfxShadingPattern(GfxShading *shadingA, double *matrixA);
+
+ GfxShading *shading;
+ double matrix[6];
+};
+
+//------------------------------------------------------------------------
+// GfxShading
+//------------------------------------------------------------------------
+
+class GfxShading {
+public:
+
+ GfxShading(int typeA);
+ GfxShading(GfxShading *shading);
+ virtual ~GfxShading();
+
+ static GfxShading *parse(Object *obj);
+
+ virtual GfxShading *copy() = 0;
+
+ int getType() { return type; }
+ GfxColorSpace *getColorSpace() { return colorSpace; }
+ GfxColor *getBackground() { return &background; }
+ GBool getHasBackground() { return hasBackground; }
+ void getBBox(double *xMinA, double *yMinA, double *xMaxA, double *yMaxA)
+ { *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; }
+ GBool getHasBBox() { return hasBBox; }
+
+protected:
+
+ GBool init(Dict *dict);
+
+ int type;
+ GfxColorSpace *colorSpace;
+ GfxColor background;
+ GBool hasBackground;
+ double xMin, yMin, xMax, yMax;
+ GBool hasBBox;
+};
+
+//------------------------------------------------------------------------
+// GfxFunctionShading
+//------------------------------------------------------------------------
+
+class GfxFunctionShading: public GfxShading {
+public:
+
+ GfxFunctionShading(double x0A, double y0A,
+ double x1A, double y1A,
+ double *matrixA,
+ Function **funcsA, int nFuncsA);
+ GfxFunctionShading(GfxFunctionShading *shading);
+ virtual ~GfxFunctionShading();
+
+ static GfxFunctionShading *parse(Dict *dict);
+
+ virtual GfxShading *copy();
+
+ void getDomain(double *x0A, double *y0A, double *x1A, double *y1A)
+ { *x0A = x0; *y0A = y0; *x1A = x1; *y1A = y1; }
+ double *getMatrix() { return matrix; }
+ int getNFuncs() { return nFuncs; }
+ Function *getFunc(int i) { return funcs[i]; }
+ void getColor(double x, double y, GfxColor *color);
+
+private:
+
+ double x0, y0, x1, y1;
+ double matrix[6];
+ Function *funcs[gfxColorMaxComps];
+ int nFuncs;
+};
+
+//------------------------------------------------------------------------
+// GfxAxialShading
+//------------------------------------------------------------------------
+
+class GfxAxialShading: public GfxShading {
+public:
+
+ GfxAxialShading(double x0A, double y0A,
+ double x1A, double y1A,
+ double t0A, double t1A,
+ Function **funcsA, int nFuncsA,
+ GBool extend0A, GBool extend1A);
+ GfxAxialShading(GfxAxialShading *shading);
+ virtual ~GfxAxialShading();
+
+ static GfxAxialShading *parse(Dict *dict);
+
+ virtual GfxShading *copy();
+
+ void getCoords(double *x0A, double *y0A, double *x1A, double *y1A)
+ { *x0A = x0; *y0A = y0; *x1A = x1; *y1A = y1; }
+ double getDomain0() { return t0; }
+ double getDomain1() { return t1; }
+ GBool getExtend0() { return extend0; }
+ GBool getExtend1() { return extend1; }
+ int getNFuncs() { return nFuncs; }
+ Function *getFunc(int i) { return funcs[i]; }
+ void getColor(double t, GfxColor *color);
+
+private:
+
+ double x0, y0, x1, y1;
+ double t0, t1;
+ Function *funcs[gfxColorMaxComps];
+ int nFuncs;
+ GBool extend0, extend1;
+};
+
+//------------------------------------------------------------------------
+// GfxRadialShading
+//------------------------------------------------------------------------
+
+class GfxRadialShading: public GfxShading {
+public:
+
+ GfxRadialShading(double x0A, double y0A, double r0A,
+ double x1A, double y1A, double r1A,
+ double t0A, double t1A,
+ Function **funcsA, int nFuncsA,
+ GBool extend0A, GBool extend1A);
+ GfxRadialShading(GfxRadialShading *shading);
+ virtual ~GfxRadialShading();
+
+ static GfxRadialShading *parse(Dict *dict);
+
+ virtual GfxShading *copy();
+
+ void getCoords(double *x0A, double *y0A, double *r0A,
+ double *x1A, double *y1A, double *r1A)
+ { *x0A = x0; *y0A = y0; *r0A = r0; *x1A = x1; *y1A = y1; *r1A = r1; }
+ double getDomain0() { return t0; }
+ double getDomain1() { return t1; }
+ GBool getExtend0() { return extend0; }
+ GBool getExtend1() { return extend1; }
+ int getNFuncs() { return nFuncs; }
+ Function *getFunc(int i) { return funcs[i]; }
+ void getColor(double t, GfxColor *color);
+
+private:
+
+ double x0, y0, r0, x1, y1, r1;
+ double t0, t1;
+ Function *funcs[gfxColorMaxComps];
+ int nFuncs;
+ GBool extend0, extend1;
+};
+
+//------------------------------------------------------------------------
+// GfxGouraudTriangleShading
+//------------------------------------------------------------------------
+
+struct GfxGouraudVertex {
+ double x, y;
+ GfxColor color;
+};
+
+class GfxGouraudTriangleShading: public GfxShading {
+public:
+
+ GfxGouraudTriangleShading(int typeA,
+ GfxGouraudVertex *verticesA, int nVerticesA,
+ int (*trianglesA)[3], int nTrianglesA,
+ Function **funcsA, int nFuncsA);
+ GfxGouraudTriangleShading(GfxGouraudTriangleShading *shading);
+ virtual ~GfxGouraudTriangleShading();
+
+ static GfxGouraudTriangleShading *parse(int typeA, Dict *dict, Stream *str);
+
+ virtual GfxShading *copy();
+
+ int getNTriangles() { return nTriangles; }
+ void getTriangle(int i, double *x0, double *y0, GfxColor *color0,
+ double *x1, double *y1, GfxColor *color1,
+ double *x2, double *y2, GfxColor *color2);
+
+private:
+
+ GfxGouraudVertex *vertices;
+ int nVertices;
+ int (*triangles)[3];
+ int nTriangles;
+ Function *funcs[gfxColorMaxComps];
+ int nFuncs;
+};
+
+//------------------------------------------------------------------------
+// GfxPatchMeshShading
+//------------------------------------------------------------------------
+
+struct GfxPatch {
+ double x[4][4];
+ double y[4][4];
+ GfxColor color[2][2];
+};
+
+class GfxPatchMeshShading: public GfxShading {
+public:
+
+ GfxPatchMeshShading(int typeA, GfxPatch *patchesA, int nPatchesA,
+ Function **funcsA, int nFuncsA);
+ GfxPatchMeshShading(GfxPatchMeshShading *shading);
+ virtual ~GfxPatchMeshShading();
+
+ static GfxPatchMeshShading *parse(int typeA, Dict *dict, Stream *str);
+
+ virtual GfxShading *copy();
+
+ int getNPatches() { return nPatches; }
+ GfxPatch *getPatch(int i) { return &patches[i]; }
+
+private:
+
+ GfxPatch *patches;
+ int nPatches;
+ Function *funcs[gfxColorMaxComps];
+ int nFuncs;
+};
+
+//------------------------------------------------------------------------
+// GfxImageColorMap
+//------------------------------------------------------------------------
+
+class GfxImageColorMap {
+public:
+
+ // Constructor.
+ GfxImageColorMap(int bitsA, Object *decode, GfxColorSpace *colorSpaceA);
+
+ // Destructor.
+ ~GfxImageColorMap();
+
+ // Return a copy of this color map.
+ GfxImageColorMap *copy() { return new GfxImageColorMap(this); }
+
+ // Is color map valid?
+ GBool isOk() { return ok; }
+
+ // Get the color space.
+ GfxColorSpace *getColorSpace() { return colorSpace; }
+
+ // Get stream decoding info.
+ int getNumPixelComps() { return nComps; }
+ int getBits() { return bits; }
+
+ // Get decode table.
+ double getDecodeLow(int i) { return decodeLow[i]; }
+ double getDecodeHigh(int i) { return decodeLow[i] + decodeRange[i]; }
+
+ // Convert an image pixel to a color.
+ void getGray(Guchar *x, GfxGray *gray);
+ void getRGB(Guchar *x, GfxRGB *rgb);
+ void getCMYK(Guchar *x, GfxCMYK *cmyk);
+ void getColor(Guchar *x, GfxColor *color);
+
+private:
+
+ GfxImageColorMap(GfxImageColorMap *colorMap);
+
+ GfxColorSpace *colorSpace; // the image color space
+ int bits; // bits per component
+ int nComps; // number of components in a pixel
+ GfxColorSpace *colorSpace2; // secondary color space
+ int nComps2; // number of components in colorSpace2
+ GfxColorComp * // lookup table
+ lookup[gfxColorMaxComps];
+ double // minimum values for each component
+ decodeLow[gfxColorMaxComps];
+ double // max - min value for each component
+ decodeRange[gfxColorMaxComps];
+ GBool ok;
+};
+
+//------------------------------------------------------------------------
+// GfxSubpath and GfxPath
+//------------------------------------------------------------------------
+
+class GfxSubpath {
+public:
+
+ // Constructor.
+ GfxSubpath(double x1, double y1);
+
+ // Destructor.
+ ~GfxSubpath();
+
+ // Copy.
+ GfxSubpath *copy() { return new GfxSubpath(this); }
+
+ // Get points.
+ int getNumPoints() { return n; }
+ double getX(int i) { return x[i]; }
+ double getY(int i) { return y[i]; }
+ GBool getCurve(int i) { return curve[i]; }
+
+ // Get last point.
+ double getLastX() { return x[n-1]; }
+ double getLastY() { return y[n-1]; }
+
+ // Add a line segment.
+ void lineTo(double x1, double y1);
+
+ // Add a Bezier curve.
+ void curveTo(double x1, double y1, double x2, double y2,
+ double x3, double y3);
+
+ // Close the subpath.
+ void close();
+ GBool isClosed() { return closed; }
+
+ // Add (<dx>, <dy>) to each point in the subpath.
+ void offset(double dx, double dy);
+
+private:
+
+ double *x, *y; // points
+ GBool *curve; // curve[i] => point i is a control point
+ // for a Bezier curve
+ int n; // number of points
+ int size; // size of x/y arrays
+ GBool closed; // set if path is closed
+
+ GfxSubpath(GfxSubpath *subpath);
+};
+
+class GfxPath {
+public:
+
+ // Constructor.
+ GfxPath();
+
+ // Destructor.
+ ~GfxPath();
+
+ // Copy.
+ GfxPath *copy()
+ { return new GfxPath(justMoved, firstX, firstY, subpaths, n, size); }
+
+ // Is there a current point?
+ GBool isCurPt() { return n > 0 || justMoved; }
+
+ // Is the path non-empty, i.e., is there at least one segment?
+ GBool isPath() { return n > 0; }
+
+ // Get subpaths.
+ int getNumSubpaths() { return n; }
+ GfxSubpath *getSubpath(int i) { return subpaths[i]; }
+
+ // Get last point on last subpath.
+ double getLastX() { return subpaths[n-1]->getLastX(); }
+ double getLastY() { return subpaths[n-1]->getLastY(); }
+
+ // Move the current point.
+ void moveTo(double x, double y);
+
+ // Add a segment to the last subpath.
+ void lineTo(double x, double y);
+
+ // Add a Bezier curve to the last subpath
+ void curveTo(double x1, double y1, double x2, double y2,
+ double x3, double y3);
+
+ // Close the last subpath.
+ void close();
+
+ // Append <path> to <this>.
+ void append(GfxPath *path);
+
+ // Add (<dx>, <dy>) to each point in the path.
+ void offset(double dx, double dy);
+
+private:
+
+ GBool justMoved; // set if a new subpath was just started
+ double firstX, firstY; // first point in new subpath
+ GfxSubpath **subpaths; // subpaths
+ int n; // number of subpaths
+ int size; // size of subpaths array
+
+ GfxPath(GBool justMoved1, double firstX1, double firstY1,
+ GfxSubpath **subpaths1, int n1, int size1);
+};
+
+//------------------------------------------------------------------------
+// GfxState
+//------------------------------------------------------------------------
+
+class GfxState {
+public:
+
+ // Construct a default GfxState, for a device with resolution <hDPI>
+ // x <vDPI>, page box <pageBox>, page rotation <rotateA>, and
+ // coordinate system specified by <upsideDown>.
+ GfxState(double hDPI, double vDPI, PDFRectangle *pageBox,
+ int rotateA, GBool upsideDown);
+
+ // Destructor.
+ ~GfxState();
+
+ // Copy.
+ GfxState *copy() { return new GfxState(this); }
+
+ // Accessors.
+ double *getCTM() { return ctm; }
+ double getX1() { return px1; }
+ double getY1() { return py1; }
+ double getX2() { return px2; }
+ double getY2() { return py2; }
+ double getPageWidth() { return pageWidth; }
+ double getPageHeight() { return pageHeight; }
+ int getRotate() { return rotate; }
+ GfxColor *getFillColor() { return &fillColor; }
+ GfxColor *getStrokeColor() { return &strokeColor; }
+ void getFillGray(GfxGray *gray)
+ { fillColorSpace->getGray(&fillColor, gray); }
+ void getStrokeGray(GfxGray *gray)
+ { strokeColorSpace->getGray(&strokeColor, gray); }
+ void getFillRGB(GfxRGB *rgb)
+ { fillColorSpace->getRGB(&fillColor, rgb); }
+ void getStrokeRGB(GfxRGB *rgb)
+ { strokeColorSpace->getRGB(&strokeColor, rgb); }
+ void getFillCMYK(GfxCMYK *cmyk)
+ { fillColorSpace->getCMYK(&fillColor, cmyk); }
+ void getStrokeCMYK(GfxCMYK *cmyk)
+ { strokeColorSpace->getCMYK(&strokeColor, cmyk); }
+ GfxColorSpace *getFillColorSpace() { return fillColorSpace; }
+ GfxColorSpace *getStrokeColorSpace() { return strokeColorSpace; }
+ GfxPattern *getFillPattern() { return fillPattern; }
+ GfxPattern *getStrokePattern() { return strokePattern; }
+ GfxBlendMode getBlendMode() { return blendMode; }
+ double getFillOpacity() { return fillOpacity; }
+ double getStrokeOpacity() { return strokeOpacity; }
+ GBool getFillOverprint() { return fillOverprint; }
+ GBool getStrokeOverprint() { return strokeOverprint; }
+ double getLineWidth() { return lineWidth; }
+ void getLineDash(double **dash, int *length, double *start)
+ { *dash = lineDash; *length = lineDashLength; *start = lineDashStart; }
+ int getFlatness() { return flatness; }
+ int getLineJoin() { return lineJoin; }
+ int getLineCap() { return lineCap; }
+ double getMiterLimit() { return miterLimit; }
+ GfxFont *getFont() { return font; }
+ double getFontSize() { return fontSize; }
+ double *getTextMat() { return textMat; }
+ double getCharSpace() { return charSpace; }
+ double getWordSpace() { return wordSpace; }
+ double getHorizScaling() { return horizScaling; }
+ double getLeading() { return leading; }
+ double getRise() { return rise; }
+ int getRender() { return render; }
+ GfxPath *getPath() { return path; }
+ void setPath(GfxPath *pathA);
+ double getCurX() { return curX; }
+ double getCurY() { return curY; }
+ void getClipBBox(double *xMin, double *yMin, double *xMax, double *yMax)
+ { *xMin = clipXMin; *yMin = clipYMin; *xMax = clipXMax; *yMax = clipYMax; }
+ void getUserClipBBox(double *xMin, double *yMin, double *xMax, double *yMax);
+ double getLineX() { return lineX; }
+ double getLineY() { return lineY; }
+
+ // Is there a current point/path?
+ GBool isCurPt() { return path->isCurPt(); }
+ GBool isPath() { return path->isPath(); }
+
+ // Transforms.
+ void transform(double x1, double y1, double *x2, double *y2)
+ { *x2 = ctm[0] * x1 + ctm[2] * y1 + ctm[4];
+ *y2 = ctm[1] * x1 + ctm[3] * y1 + ctm[5]; }
+ void transformDelta(double x1, double y1, double *x2, double *y2)
+ { *x2 = ctm[0] * x1 + ctm[2] * y1;
+ *y2 = ctm[1] * x1 + ctm[3] * y1; }
+ void textTransform(double x1, double y1, double *x2, double *y2)
+ { *x2 = textMat[0] * x1 + textMat[2] * y1 + textMat[4];
+ *y2 = textMat[1] * x1 + textMat[3] * y1 + textMat[5]; }
+ void textTransformDelta(double x1, double y1, double *x2, double *y2)
+ { *x2 = textMat[0] * x1 + textMat[2] * y1;
+ *y2 = textMat[1] * x1 + textMat[3] * y1; }
+ double transformWidth(double w);
+ double getTransformedLineWidth()
+ { return transformWidth(lineWidth); }
+ double getTransformedFontSize();
+ void getFontTransMat(double *m11, double *m12, double *m21, double *m22);
+
+ // Change state parameters.
+ void setCTM(double a, double b, double c,
+ double d, double e, double f);
+ void concatCTM(double a, double b, double c,
+ double d, double e, double f);
+ void setFillColorSpace(GfxColorSpace *colorSpace);
+ void setStrokeColorSpace(GfxColorSpace *colorSpace);
+ void setFillColor(GfxColor *color) { fillColor = *color; }
+ void setStrokeColor(GfxColor *color) { strokeColor = *color; }
+ void setFillPattern(GfxPattern *pattern);
+ void setStrokePattern(GfxPattern *pattern);
+ void setBlendMode(GfxBlendMode mode) { blendMode = mode; }
+ void setFillOpacity(double opac) { fillOpacity = opac; }
+ void setStrokeOpacity(double opac) { strokeOpacity = opac; }
+ void setFillOverprint(GBool op) { fillOverprint = op; }
+ void setStrokeOverprint(GBool op) { strokeOverprint = op; }
+ void setLineWidth(double width) { lineWidth = width; }
+ void setLineDash(double *dash, int length, double start);
+ void setFlatness(int flatness1) { flatness = flatness1; }
+ void setLineJoin(int lineJoin1) { lineJoin = lineJoin1; }
+ void setLineCap(int lineCap1) { lineCap = lineCap1; }
+ void setMiterLimit(double limit) { miterLimit = limit; }
+ void setFont(GfxFont *fontA, double fontSizeA)
+ { font = fontA; fontSize = fontSizeA; }
+ void setTextMat(double a, double b, double c,
+ double d, double e, double f)
+ { textMat[0] = a; textMat[1] = b; textMat[2] = c;
+ textMat[3] = d; textMat[4] = e; textMat[5] = f; }
+ void setCharSpace(double space)
+ { charSpace = space; }
+ void setWordSpace(double space)
+ { wordSpace = space; }
+ void setHorizScaling(double scale)
+ { horizScaling = 0.01 * scale; }
+ void setLeading(double leadingA)
+ { leading = leadingA; }
+ void setRise(double riseA)
+ { rise = riseA; }
+ void setRender(int renderA)
+ { render = renderA; }
+
+ // Add to path.
+ void moveTo(double x, double y)
+ { path->moveTo(curX = x, curY = y); }
+ void lineTo(double x, double y)
+ { path->lineTo(curX = x, curY = y); }
+ void curveTo(double x1, double y1, double x2, double y2,
+ double x3, double y3)
+ { path->curveTo(x1, y1, x2, y2, curX = x3, curY = y3); }
+ void closePath()
+ { path->close(); curX = path->getLastX(); curY = path->getLastY(); }
+ void clearPath();
+
+ // Update clip region.
+ void clip();
+
+ // Text position.
+ void textSetPos(double tx, double ty) { lineX = tx; lineY = ty; }
+ void textMoveTo(double tx, double ty)
+ { lineX = tx; lineY = ty; textTransform(tx, ty, &curX, &curY); }
+ void textShift(double tx, double ty);
+ void shift(double dx, double dy);
+
+ // Push/pop GfxState on/off stack.
+ GfxState *save();
+ GfxState *restore();
+ GBool hasSaves() { return saved != NULL; }
+
+ // Misc
+ GBool parseBlendMode(Object *obj, GfxBlendMode *mode);
+
+private:
+
+ double ctm[6]; // coord transform matrix
+ double px1, py1, px2, py2; // page corners (user coords)
+ double pageWidth, pageHeight; // page size (pixels)
+ int rotate; // page rotation angle
+
+ GfxColorSpace *fillColorSpace; // fill color space
+ GfxColorSpace *strokeColorSpace; // stroke color space
+ GfxColor fillColor; // fill color
+ GfxColor strokeColor; // stroke color
+ GfxPattern *fillPattern; // fill pattern
+ GfxPattern *strokePattern; // stroke pattern
+ GfxBlendMode blendMode; // transparency blend mode
+ double fillOpacity; // fill opacity
+ double strokeOpacity; // stroke opacity
+ GBool fillOverprint; // fill overprint
+ GBool strokeOverprint; // stroke overprint
+
+ double lineWidth; // line width
+ double *lineDash; // line dash
+ int lineDashLength;
+ double lineDashStart;
+ int flatness; // curve flatness
+ int lineJoin; // line join style
+ int lineCap; // line cap style
+ double miterLimit; // line miter limit
+
+ GfxFont *font; // font
+ double fontSize; // font size
+ double textMat[6]; // text matrix
+ double charSpace; // character spacing
+ double wordSpace; // word spacing
+ double horizScaling; // horizontal scaling
+ double leading; // text leading
+ double rise; // text rise
+ int render; // text rendering mode
+
+ GfxPath *path; // array of path elements
+ double curX, curY; // current point (user coords)
+ double lineX, lineY; // start of current text line (text coords)
+
+ double clipXMin, clipYMin, // bounding box for clip region
+ clipXMax, clipYMax;
+
+ GfxState *saved; // next GfxState on stack
+
+ GfxState(GfxState *state);
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// GlobalParams.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#ifdef ENABLE_PLUGINS
+# ifndef WIN32
+# include <dlfcn.h>
+# endif
+#endif
+#ifdef WIN32
+# include <shlobj.h>
+#endif
+#if HAVE_PAPER_H
+#include <paper.h>
+#endif
+#include "gmem.h"
+#include "GString.h"
+#include "GList.h"
+#include "GHash.h"
+#include "gfile.h"
+#include "Error.h"
+#include "NameToCharCode.h"
+#include "CharCodeToUnicode.h"
+#include "UnicodeMap.h"
+#include "CMap.h"
+#include "BuiltinFontTables.h"
+#include "FontEncodingTables.h"
+#ifdef ENABLE_PLUGINS
+# include "XpdfPluginAPI.h"
+#endif
+#include "GlobalParams.h"
+
+#if MULTITHREADED
+# define lockGlobalParams gLockMutex(&mutex)
+# define lockUnicodeMapCache gLockMutex(&unicodeMapCacheMutex)
+# define lockCMapCache gLockMutex(&cMapCacheMutex)
+# define unlockGlobalParams gUnlockMutex(&mutex)
+# define unlockUnicodeMapCache gUnlockMutex(&unicodeMapCacheMutex)
+# define unlockCMapCache gUnlockMutex(&cMapCacheMutex)
+#else
+# define lockGlobalParams
+# define lockUnicodeMapCache
+# define lockCMapCache
+# define unlockGlobalParams
+# define unlockUnicodeMapCache
+# define unlockCMapCache
+#endif
+
+#include "NameToUnicodeTable.h"
+#include "UnicodeMapTables.h"
+#include "UTF8.h"
+
+#ifdef ENABLE_PLUGINS
+# ifdef WIN32
+extern XpdfPluginVecTable xpdfPluginVecTable;
+# endif
+#endif
+
+//------------------------------------------------------------------------
+
+#define cidToUnicodeCacheSize 4
+#define unicodeToUnicodeCacheSize 4
+
+//------------------------------------------------------------------------
+
+static struct {
+ char *name;
+ char *t1FileName;
+ char *ttFileName;
+} displayFontTab[] = {
+ {"Courier", "n022003l.pfb", "cour.ttf"},
+ {"Courier-Bold", "n022004l.pfb", "courbd.ttf"},
+ {"Courier-BoldOblique", "n022024l.pfb", "courbi.ttf"},
+ {"Courier-Oblique", "n022023l.pfb", "couri.ttf"},
+ {"Helvetica", "n019003l.pfb", "arial.ttf"},
+ {"Helvetica-Bold", "n019004l.pfb", "arialbd.ttf"},
+ {"Helvetica-BoldOblique", "n019024l.pfb", "arialbi.ttf"},
+ {"Helvetica-Oblique", "n019023l.pfb", "ariali.ttf"},
+ {"Symbol", "s050000l.pfb", NULL},
+ {"Times-Bold", "n021004l.pfb", "timesbd.ttf"},
+ {"Times-BoldItalic", "n021024l.pfb", "timesbi.ttf"},
+ {"Times-Italic", "n021023l.pfb", "timesi.ttf"},
+ {"Times-Roman", "n021003l.pfb", "times.ttf"},
+ {"ZapfDingbats", "d050000l.pfb", NULL},
+ {NULL}
+};
+
+#ifdef WIN32
+static char *displayFontDirs[] = {
+ "c:/windows/fonts",
+ "c:/winnt/fonts",
+ NULL
+};
+#else
+static char *displayFontDirs[] = {
+ "/usr/share/ghostscript/fonts",
+ "/usr/local/share/ghostscript/fonts",
+ "/usr/share/fonts/default/Type1",
+ "/usr/share/fonts/default/ghostscript",
+ "/usr/share/fonts/type1/gsfonts",
+ NULL
+};
+#endif
+
+//------------------------------------------------------------------------
+
+GlobalParams *globalParams = NULL;
+
+//------------------------------------------------------------------------
+// DisplayFontParam
+//------------------------------------------------------------------------
+
+DisplayFontParam::DisplayFontParam(GString *nameA,
+ DisplayFontParamKind kindA) {
+ name = nameA;
+ kind = kindA;
+ switch (kind) {
+ case displayFontT1:
+ t1.fileName = NULL;
+ break;
+ case displayFontTT:
+ tt.fileName = NULL;
+ break;
+ }
+}
+
+DisplayFontParam::~DisplayFontParam() {
+ delete name;
+ switch (kind) {
+ case displayFontT1:
+ if (t1.fileName) {
+ delete t1.fileName;
+ }
+ break;
+ case displayFontTT:
+ if (tt.fileName) {
+ delete tt.fileName;
+ }
+ break;
+ }
+}
+
+//------------------------------------------------------------------------
+// PSFontParam
+//------------------------------------------------------------------------
+
+PSFontParam::PSFontParam(GString *pdfFontNameA, int wModeA,
+ GString *psFontNameA, GString *encodingA) {
+ pdfFontName = pdfFontNameA;
+ wMode = wModeA;
+ psFontName = psFontNameA;
+ encoding = encodingA;
+}
+
+PSFontParam::~PSFontParam() {
+ delete pdfFontName;
+ delete psFontName;
+ if (encoding) {
+ delete encoding;
+ }
+}
+
+#ifdef ENABLE_PLUGINS
+//------------------------------------------------------------------------
+// Plugin
+//------------------------------------------------------------------------
+
+class Plugin {
+public:
+
+ static Plugin *load(char *type, char *name);
+ ~Plugin();
+
+private:
+
+#ifdef WIN32
+ Plugin(HMODULE libA);
+ HMODULE lib;
+#else
+ Plugin(void *dlA);
+ void *dl;
+#endif
+};
+
+Plugin *Plugin::load(char *type, char *name) {
+ GString *path;
+ Plugin *plugin;
+ XpdfPluginVecTable *vt;
+ XpdfBool (*xpdfInitPlugin)(void);
+#ifdef WIN32
+ HMODULE libA;
+#else
+ void *dlA;
+#endif
+
+ path = globalParams->getBaseDir();
+ appendToPath(path, "plugins");
+ appendToPath(path, type);
+ appendToPath(path, name);
+
+#ifdef WIN32
+ path->append(".dll");
+ if (!(libA = LoadLibrary(path->getCString()))) {
+ error(-1, "Failed to load plugin '%s'",
+ path->getCString());
+ goto err1;
+ }
+ if (!(vt = (XpdfPluginVecTable *)
+ GetProcAddress(libA, "xpdfPluginVecTable"))) {
+ error(-1, "Failed to find xpdfPluginVecTable in plugin '%s'",
+ path->getCString());
+ goto err2;
+ }
+#else
+ //~ need to deal with other extensions here
+ path->append(".so");
+ if (!(dlA = dlopen(path->getCString(), RTLD_NOW))) {
+ error(-1, "Failed to load plugin '%s': %s",
+ path->getCString(), dlerror());
+ goto err1;
+ }
+ if (!(vt = (XpdfPluginVecTable *)dlsym(dlA, "xpdfPluginVecTable"))) {
+ error(-1, "Failed to find xpdfPluginVecTable in plugin '%s'",
+ path->getCString());
+ goto err2;
+ }
+#endif
+
+ if (vt->version != xpdfPluginVecTable.version) {
+ error(-1, "Plugin '%s' is wrong version", path->getCString());
+ goto err2;
+ }
+ memcpy(vt, &xpdfPluginVecTable, sizeof(xpdfPluginVecTable));
+
+#ifdef WIN32
+ if (!(xpdfInitPlugin = (XpdfBool (*)(void))
+ GetProcAddress(libA, "xpdfInitPlugin"))) {
+ error(-1, "Failed to find xpdfInitPlugin in plugin '%s'",
+ path->getCString());
+ goto err2;
+ }
+#else
+ if (!(xpdfInitPlugin = (XpdfBool (*)(void))dlsym(dlA, "xpdfInitPlugin"))) {
+ error(-1, "Failed to find xpdfInitPlugin in plugin '%s'",
+ path->getCString());
+ goto err2;
+ }
+#endif
+
+ if (!(*xpdfInitPlugin)()) {
+ error(-1, "Initialization of plugin '%s' failed",
+ path->getCString());
+ goto err2;
+ }
+
+#ifdef WIN32
+ plugin = new Plugin(libA);
+#else
+ plugin = new Plugin(dlA);
+#endif
+
+ delete path;
+ return plugin;
+
+ err2:
+#ifdef WIN32
+ FreeLibrary(libA);
+#else
+ dlclose(dlA);
+#endif
+ err1:
+ delete path;
+ return NULL;
+}
+
+#ifdef WIN32
+Plugin::Plugin(HMODULE libA) {
+ lib = libA;
+}
+#else
+Plugin::Plugin(void *dlA) {
+ dl = dlA;
+}
+#endif
+
+Plugin::~Plugin() {
+ void (*xpdfFreePlugin)(void);
+
+#ifdef WIN32
+ if ((xpdfFreePlugin = (void (*)(void))
+ GetProcAddress(lib, "xpdfFreePlugin"))) {
+ (*xpdfFreePlugin)();
+ }
+ FreeLibrary(lib);
+#else
+ if ((xpdfFreePlugin = (void (*)(void))dlsym(dl, "xpdfFreePlugin"))) {
+ (*xpdfFreePlugin)();
+ }
+ dlclose(dl);
+#endif
+}
+
+#endif // ENABLE_PLUGINS
+
+//------------------------------------------------------------------------
+// parsing
+//------------------------------------------------------------------------
+
+GlobalParams::GlobalParams(char *cfgFileName) {
+ UnicodeMap *map;
+ GString *fileName;
+ FILE *f;
+ int i;
+
+#if MULTITHREADED
+ gInitMutex(&mutex);
+ gInitMutex(&unicodeMapCacheMutex);
+ gInitMutex(&cMapCacheMutex);
+#endif
+
+ initBuiltinFontTables();
+
+ // scan the encoding in reverse because we want the lowest-numbered
+ // index for each char name ('space' is encoded twice)
+ macRomanReverseMap = new NameToCharCode();
+ for (i = 255; i >= 0; --i) {
+ if (macRomanEncoding[i]) {
+ macRomanReverseMap->add(macRomanEncoding[i], (CharCode)i);
+ }
+ }
+
+#ifdef WIN32
+ // baseDir will be set by a call to setBaseDir
+ baseDir = new GString();
+#else
+ baseDir = appendToPath(getHomeDir(), ".xpdf");
+#endif
+ nameToUnicode = new NameToCharCode();
+ cidToUnicodes = new GHash(gTrue);
+ unicodeToUnicodes = new GHash(gTrue);
+ residentUnicodeMaps = new GHash();
+ unicodeMaps = new GHash(gTrue);
+ cMapDirs = new GHash(gTrue);
+ toUnicodeDirs = new GList();
+ displayFonts = new GHash();
+ displayCIDFonts = new GHash();
+ displayNamedCIDFonts = new GHash();
+#if HAVE_PAPER_H
+ char *paperName;
+ const struct paper *paperType;
+ paperinit();
+ if ((paperName = systempapername())) {
+ paperType = paperinfo(paperName);
+ psPaperWidth = (int)paperpswidth(paperType);
+ psPaperHeight = (int)paperpsheight(paperType);
+ } else {
+ error(-1, "No paper information available - using defaults");
+ psPaperWidth = defPaperWidth;
+ psPaperHeight = defPaperHeight;
+ }
+ paperdone();
+#else
+ psPaperWidth = defPaperWidth;
+ psPaperHeight = defPaperHeight;
+#endif
+ psImageableLLX = psImageableLLY = 0;
+ psImageableURX = psPaperWidth;
+ psImageableURY = psPaperHeight;
+ psCrop = gTrue;
+ psExpandSmaller = gFalse;
+ psShrinkLarger = gTrue;
+ psCenter = gTrue;
+ psDuplex = gFalse;
+ psLevel = psLevel2;
+ psFile = NULL;
+ psFonts = new GHash();
+ psNamedFonts16 = new GList();
+ psFonts16 = new GList();
+ psEmbedType1 = gTrue;
+ psEmbedTrueType = gTrue;
+ psEmbedCIDPostScript = gTrue;
+ psEmbedCIDTrueType = gTrue;
+ psOPI = gFalse;
+ psASCIIHex = gFalse;
+ textEncoding = new GString("Latin1");
+#if defined(WIN32)
+ textEOL = eolDOS;
+#elif defined(MACOS)
+ textEOL = eolMac;
+#else
+ textEOL = eolUnix;
+#endif
+ textPageBreaks = gTrue;
+ textKeepTinyChars = gFalse;
+ fontDirs = new GList();
+ initialZoom = new GString("125");
+ continuousView = gFalse;
+ enableT1lib = gTrue;
+ enableFreeType = gTrue;
+ antialias = gTrue;
+ urlCommand = NULL;
+ movieCommand = NULL;
+ mapNumericCharNames = gTrue;
+ printCommands = gFalse;
+ errQuiet = gFalse;
+
+ cidToUnicodeCache = new CharCodeToUnicodeCache(cidToUnicodeCacheSize);
+ unicodeToUnicodeCache =
+ new CharCodeToUnicodeCache(unicodeToUnicodeCacheSize);
+ unicodeMapCache = new UnicodeMapCache();
+ cMapCache = new CMapCache();
+
+#ifdef ENABLE_PLUGINS
+ plugins = new GList();
+ securityHandlers = new GList();
+#endif
+
+ // set up the initial nameToUnicode table
+ for (i = 0; nameToUnicodeTab[i].name; ++i) {
+ nameToUnicode->add(nameToUnicodeTab[i].name, nameToUnicodeTab[i].u);
+ }
+
+ // set up the residentUnicodeMaps table
+ map = new UnicodeMap("Latin1", gFalse,
+ latin1UnicodeMapRanges, latin1UnicodeMapLen);
+ residentUnicodeMaps->add(map->getEncodingName(), map);
+ map = new UnicodeMap("ASCII7", gFalse,
+ ascii7UnicodeMapRanges, ascii7UnicodeMapLen);
+ residentUnicodeMaps->add(map->getEncodingName(), map);
+ map = new UnicodeMap("Symbol", gFalse,
+ symbolUnicodeMapRanges, symbolUnicodeMapLen);
+ residentUnicodeMaps->add(map->getEncodingName(), map);
+ map = new UnicodeMap("ZapfDingbats", gFalse, zapfDingbatsUnicodeMapRanges,
+ zapfDingbatsUnicodeMapLen);
+ residentUnicodeMaps->add(map->getEncodingName(), map);
+ map = new UnicodeMap("UTF-8", gTrue, &mapUTF8);
+ residentUnicodeMaps->add(map->getEncodingName(), map);
+ map = new UnicodeMap("UCS-2", gTrue, &mapUCS2);
+ residentUnicodeMaps->add(map->getEncodingName(), map);
+
+ // look for a user config file, then a system-wide config file
+ f = NULL;
+ fileName = NULL;
+ if (cfgFileName && cfgFileName[0]) {
+ fileName = new GString(cfgFileName);
+ if (!(f = fopen(fileName->getCString(), "r"))) {
+ delete fileName;
+ }
+ }
+ if (!f) {
+ fileName = appendToPath(getHomeDir(), xpdfUserConfigFile);
+ if (!(f = fopen(fileName->getCString(), "r"))) {
+ delete fileName;
+ }
+ }
+ if (!f) {
+#if defined(WIN32) && !defined(__CYGWIN32__)
+ char buf[512];
+ i = GetModuleFileName(NULL, buf, sizeof(buf));
+ if (i <= 0 || i >= sizeof(buf)) {
+ // error or path too long for buffer - just use the current dir
+ buf[0] = '\0';
+ }
+ fileName = grabPath(buf);
+ appendToPath(fileName, xpdfSysConfigFile);
+#else
+ fileName = new GString(xpdfSysConfigFile);
+#endif
+ if (!(f = fopen(fileName->getCString(), "r"))) {
+ delete fileName;
+ }
+ }
+ if (f) {
+ parseFile(fileName, f);
+ delete fileName;
+ fclose(f);
+ }
+}
+
+void GlobalParams::parseFile(GString *fileName, FILE *f) {
+ int line;
+ GList *tokens;
+ GString *cmd, *incFile;
+ char *p1, *p2;
+ char buf[512];
+ FILE *f2;
+
+ /* extract path */
+ if(fileName) {
+ char* cfgFileName = fileName->getCString();
+ char* pos1 = strrchr(cfgFileName, '/');
+ char* pos2 = strrchr(cfgFileName, '\\');
+ char* p = pos1>pos2?pos1:pos2;
+ int pos = p ? p-cfgFileName : -1;
+ GString*path = new GString(new GString(cfgFileName), 0, (pos < 0 ? strlen(cfgFileName): pos));
+ if(pos1>=0)
+ path->append('/');
+ else if(pos2>=0)
+ path->append('\\');
+ else
+#ifdef WIN32
+ path->append('\\');
+#else
+ path->append('/');
+#endif
+ this->path = path;
+ } else {
+ this->path = new GString();
+ }
+
+ line = 1;
+ while (getLine(buf, sizeof(buf) - 1, f)) {
+
+ // break the line into tokens
+ tokens = new GList();
+ p1 = buf;
+ while (*p1) {
+ for (; *p1 && isspace(*p1); ++p1) ;
+ if (!*p1) {
+ break;
+ }
+ if (*p1 == '"' || *p1 == '\'') {
+ for (p2 = p1 + 1; *p2 && *p2 != *p1; ++p2) ;
+ ++p1;
+ } else {
+ for (p2 = p1 + 1; *p2 && !isspace(*p2); ++p2) ;
+ }
+ tokens->append(new GString(p1, p2 - p1));
+ p1 = *p2 ? p2 + 1 : p2;
+ }
+
+ if (tokens->getLength() > 0 &&
+ ((GString *)tokens->get(0))->getChar(0) != '#') {
+ cmd = (GString *)tokens->get(0);
+ if (!cmd->cmp("include")) {
+ if (tokens->getLength() == 2) {
+ incFile = (GString *)tokens->get(1);
+ if ((f2 = fopen(incFile->getCString(), "r"))) {
+ parseFile(incFile, f2);
+ fclose(f2);
+ } else {
+ error(-1, "Couldn't find included config file: '%s' (%s:%d)",
+ incFile->getCString(), fileName->getCString(), line);
+ }
+ } else {
+ error(-1, "Bad 'include' config file command (%s:%d)",
+ fileName->getCString(), line);
+ }
+ } else if (!cmd->cmp("nameToUnicode")) {
+ parseNameToUnicode(tokens, fileName, line);
+ } else if (!cmd->cmp("cidToUnicode")) {
+ parseCIDToUnicode(tokens, fileName, line);
+ } else if (!cmd->cmp("unicodeToUnicode")) {
+ parseUnicodeToUnicode(tokens, fileName, line);
+ } else if (!cmd->cmp("unicodeMap")) {
+ parseUnicodeMap(tokens, fileName, line);
+ } else if (!cmd->cmp("cMapDir")) {
+ parseCMapDir(tokens, fileName, line);
+ } else if (!cmd->cmp("toUnicodeDir")) {
+ parseToUnicodeDir(tokens, fileName, line);
+ } else if (!cmd->cmp("displayFontT1")) {
+ parseDisplayFont(tokens, displayFonts, displayFontT1, fileName, line);
+ } else if (!cmd->cmp("displayFontTT")) {
+ parseDisplayFont(tokens, displayFonts, displayFontTT, fileName, line);
+ } else if (!cmd->cmp("displayNamedCIDFontT1")) {
+ parseDisplayFont(tokens, displayNamedCIDFonts,
+ displayFontT1, fileName, line);
+ } else if (!cmd->cmp("displayCIDFontT1")) {
+ parseDisplayFont(tokens, displayCIDFonts,
+ displayFontT1, fileName, line);
+ } else if (!cmd->cmp("displayNamedCIDFontTT")) {
+ parseDisplayFont(tokens, displayNamedCIDFonts,
+ displayFontTT, fileName, line);
+ } else if (!cmd->cmp("displayCIDFontTT")) {
+ parseDisplayFont(tokens, displayCIDFonts,
+ displayFontTT, fileName, line);
+ } else if (!cmd->cmp("psFile")) {
+ parsePSFile(tokens, fileName, line);
+ } else if (!cmd->cmp("psFont")) {
+ parsePSFont(tokens, fileName, line);
+ } else if (!cmd->cmp("psNamedFont16")) {
+ parsePSFont16("psNamedFont16", psNamedFonts16,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("psFont16")) {
+ parsePSFont16("psFont16", psFonts16, tokens, fileName, line);
+ } else if (!cmd->cmp("psPaperSize")) {
+ parsePSPaperSize(tokens, fileName, line);
+ } else if (!cmd->cmp("psImageableArea")) {
+ parsePSImageableArea(tokens, fileName, line);
+ } else if (!cmd->cmp("psCrop")) {
+ parseYesNo("psCrop", &psCrop, tokens, fileName, line);
+ } else if (!cmd->cmp("psExpandSmaller")) {
+ parseYesNo("psExpandSmaller", &psExpandSmaller,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("psShrinkLarger")) {
+ parseYesNo("psShrinkLarger", &psShrinkLarger, tokens, fileName, line);
+ } else if (!cmd->cmp("psCenter")) {
+ parseYesNo("psCenter", &psCenter, tokens, fileName, line);
+ } else if (!cmd->cmp("psDuplex")) {
+ parseYesNo("psDuplex", &psDuplex, tokens, fileName, line);
+ } else if (!cmd->cmp("psLevel")) {
+ parsePSLevel(tokens, fileName, line);
+ } else if (!cmd->cmp("psEmbedType1Fonts")) {
+ parseYesNo("psEmbedType1", &psEmbedType1, tokens, fileName, line);
+ } else if (!cmd->cmp("psEmbedTrueTypeFonts")) {
+ parseYesNo("psEmbedTrueType", &psEmbedTrueType,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("psEmbedCIDPostScriptFonts")) {
+ parseYesNo("psEmbedCIDPostScript", &psEmbedCIDPostScript,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("psEmbedCIDTrueTypeFonts")) {
+ parseYesNo("psEmbedCIDTrueType", &psEmbedCIDTrueType,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("psOPI")) {
+ parseYesNo("psOPI", &psOPI, tokens, fileName, line);
+ } else if (!cmd->cmp("psASCIIHex")) {
+ parseYesNo("psASCIIHex", &psASCIIHex, tokens, fileName, line);
+ } else if (!cmd->cmp("textEncoding")) {
+ parseTextEncoding(tokens, fileName, line);
+ } else if (!cmd->cmp("textEOL")) {
+ parseTextEOL(tokens, fileName, line);
+ } else if (!cmd->cmp("textPageBreaks")) {
+ parseYesNo("textPageBreaks", &textPageBreaks,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("textKeepTinyChars")) {
+ parseYesNo("textKeepTinyChars", &textKeepTinyChars,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("fontDir")) {
+ parseFontDir(tokens, fileName, line);
+ } else if (!cmd->cmp("initialZoom")) {
+ parseInitialZoom(tokens, fileName, line);
+ } else if (!cmd->cmp("continuousView")) {
+ parseYesNo("continuousView", &continuousView, tokens, fileName, line);
+ } else if (!cmd->cmp("enableT1lib")) {
+ parseYesNo("enableT1lib", &enableT1lib, tokens, fileName, line);
+ } else if (!cmd->cmp("enableFreeType")) {
+ parseYesNo("enableFreeType", &enableFreeType, tokens, fileName, line);
+ } else if (!cmd->cmp("antialias")) {
+ parseYesNo("antialias", &antialias, tokens, fileName, line);
+ } else if (!cmd->cmp("urlCommand")) {
+ parseCommand("urlCommand", &urlCommand, tokens, fileName, line);
+ } else if (!cmd->cmp("movieCommand")) {
+ parseCommand("movieCommand", &movieCommand, tokens, fileName, line);
+ } else if (!cmd->cmp("mapNumericCharNames")) {
+ parseYesNo("mapNumericCharNames", &mapNumericCharNames,
+ tokens, fileName, line);
+ } else if (!cmd->cmp("printCommands")) {
+ parseYesNo("printCommands", &printCommands, tokens, fileName, line);
+ } else if (!cmd->cmp("errQuiet")) {
+ parseYesNo("errQuiet", &errQuiet, tokens, fileName, line);
+ } else {
+ error(-1, "Unknown config file command '%s' (%s:%d)",
+ cmd->getCString(), fileName->getCString(), line);
+ if (!cmd->cmp("displayFontX") ||
+ !cmd->cmp("displayNamedCIDFontX") ||
+ !cmd->cmp("displayCIDFontX")) {
+ error(-1, "-- Xpdf no longer supports X fonts");
+ } else if (!cmd->cmp("t1libControl") || !cmd->cmp("freetypeControl")) {
+ error(-1, "-- The t1libControl and freetypeControl options have been replaced");
+ error(-1, " by the enableT1lib, enableFreeType, and antialias options");
+ } else if (!cmd->cmp("fontpath") || !cmd->cmp("fontmap")) {
+ error(-1, "-- the config file format has changed since Xpdf 0.9x");
+ }
+ }
+ }
+
+ deleteGList(tokens, GString);
+ ++line;
+ }
+}
+
+static GString* qualify_filename(GString*path, GString*filename)
+{
+ GString*fullpath = 0;
+ char*prefix = "/usr/local/share/xpdf/";
+
+ if (filename->getChar(0) != '\\' && filename->getChar(0) != '/') {
+ /* relative path */
+ fullpath = path->copy();
+ fullpath->append(filename);
+ } else if (!strncmp(filename->getCString(), prefix, strlen(prefix))) {
+ /* xpdf default path */
+ char*s = strchr(filename->getCString()+strlen(prefix), '/');
+ if(s) {
+ fullpath = path->copy();
+ fullpath->append(s+1);
+ } else {
+ fullpath = filename->copy();
+ }
+ } else {
+ /* absolute path */
+ fullpath = filename->copy();
+ }
+ //printf("%s -%s-> %s\n", filename->getCString(), path->getCString(), fullpath->getCString());
+ return fullpath;
+}
+
+void GlobalParams::parseNameToUnicode(GList *tokens, GString *fileName,
+ int line) {
+ GString *name;
+ char *tok1, *tok2;
+ FILE *f;
+ char buf[256];
+ int line2;
+ Unicode u;
+
+ if (tokens->getLength() != 2) {
+ error(-1, "Bad 'nameToUnicode' config file command (%s:%d)",
+ fileName->getCString(), line);
+ return;
+ }
+ name = qualify_filename(this->path, (GString *)tokens->get(1));
+ if (!(f = fopen(name->getCString(), "r"))) {
+ error(-1, "Couldn't open 'nameToUnicode' file '%s'",
+ name->getCString());
+ return;
+ }
+ line2 = 1;
+ while (getLine(buf, sizeof(buf), f)) {
+ tok1 = strtok(buf, " \t\r\n");
+ tok2 = strtok(NULL, " \t\r\n");
+ if (tok1 && tok2) {
+ sscanf(tok1, "%x", &u);
+ nameToUnicode->add(tok2, u);
+ } else {
+ error(-1, "Bad line in 'nameToUnicode' file (%s:%d)", name, line2);
+ }
+ ++line2;
+ }
+ fclose(f);
+}
+
+void GlobalParams::parseCIDToUnicode(GList *tokens, GString *fileName,
+ int line) {
+ GString *collection, *name, *old;
+
+ if (tokens->getLength() != 3) {
+ error(-1, "Bad 'cidToUnicode' config file command (%s:%d)",
+ fileName->getCString(), line);
+ return;
+ }
+ collection = (GString *)tokens->get(1);
+ name = (GString *)tokens->get(2);
+
+ if ((old = (GString *)cidToUnicodes->remove(collection))) {
+ delete old;
+ }
+
+ cidToUnicodes->add(collection->copy(), qualify_filename(this->path, name));
+}
+
+void GlobalParams::parseUnicodeToUnicode(GList *tokens, GString *fileName,
+ int line) {
+ GString *font, *file, *old;
+
+ if (tokens->getLength() != 3) {
+ error(-1, "Bad 'unicodeToUnicode' config file command (%s:%d)",
+ fileName->getCString(), line);
+ return;
+ }
+ font = (GString *)tokens->get(1);
+ file = (GString *)tokens->get(2);
+ if ((old = (GString *)unicodeToUnicodes->remove(font))) {
+ delete old;
+ }
+
+ unicodeToUnicodes->add(font->copy(), qualify_filename(this->path, file));
+}
+
+void GlobalParams::parseUnicodeMap(GList *tokens, GString *fileName,
+ int line) {
+ GString *encodingName, *name, *old;
+
+ if (tokens->getLength() != 3) {
+ error(-1, "Bad 'unicodeMap' config file command (%s:%d)",
+ fileName->getCString(), line);
+ return;
+ }
+ encodingName = (GString *)tokens->get(1);
+ name = (GString *)tokens->get(2);
+ if ((old = (GString *)unicodeMaps->remove(encodingName))) {
+ delete old;
+ }
+
+ unicodeMaps->add(encodingName->copy(), qualify_filename(this->path, name));
+}
+
+void GlobalParams::parseCMapDir(GList *tokens, GString *fileName, int line) {
+ GString *collection, *dir;
+ GList *list;
+
+ if (tokens->getLength() != 3) {
+ error(-1, "Bad 'cMapDir' config file command (%s:%d)",
+ fileName->getCString(), line);
+ return;
+ }
+ collection = (GString *)tokens->get(1);
+ dir = (GString *)tokens->get(2);
+ if (!(list = (GList *)cMapDirs->lookup(collection))) {
+ list = new GList();
+ cMapDirs->add(collection->copy(), list);
+ }
+
+ list->append(qualify_filename(this->path, dir));
+}
+
+void GlobalParams::parseToUnicodeDir(GList *tokens, GString *fileName,
+ int line) {
+ GString *dir;
+
+ if (tokens->getLength() != 2) {
+ error(-1, "Bad 'toUnicodeDir' config file command (%s:%d)",
+ fileName->getCString(), line);
+ return;
+ }
+
+ dir = (GString *)tokens->get(1);
+
+ toUnicodeDirs->append(qualify_filename(this->path, dir));
+}
+
+void GlobalParams::parseDisplayFont(GList *tokens, GHash *fontHash,
+ DisplayFontParamKind kind,
+ GString *fileName, int line) {
+ DisplayFontParam *param, *old;
+ GString *file;
+
+ if (tokens->getLength() < 2) {
+ goto err1;
+ }
+ param = new DisplayFontParam(((GString *)tokens->get(1))->copy(), kind);
+
+ switch (kind) {
+ case displayFontT1:
+ if (tokens->getLength() != 3) {
+ goto err2;
+ }
+ file = (GString *)tokens->get(2);
+ param->t1.fileName = qualify_filename(this->path, file);
+ break;
+ case displayFontTT:
+ if (tokens->getLength() != 3) {
+ goto err2;
+ }
+ file = (GString *)tokens->get(2);
+ param->tt.fileName = qualify_filename(this->path, file);
+ break;
+ }
+
+ if ((old = (DisplayFontParam *)fontHash->remove(param->name))) {
+ delete old;
+ }
+ fontHash->add(param->name, param);
+ return;
+
+ err2:
+ delete param;
+ err1:
+ error(-1, "Bad 'display*Font*' config file command (%s:%d)",
+ fileName->getCString(), line);
+}
+
+void GlobalParams::parsePSPaperSize(GList *tokens, GString *fileName,
+ int line) {
+ GString *tok;
+
+ if (tokens->getLength() == 2) {
+ tok = (GString *)tokens->get(1);
+ if (!setPSPaperSize(tok->getCString())) {
+ error(-1, "Bad 'psPaperSize' config file command (%s:%d)",
+ fileName->getCString(), line);
+ }
+ } else if (tokens->getLength() == 3) {
+ tok = (GString *)tokens->get(1);
+ psPaperWidth = atoi(tok->getCString());
+ tok = (GString *)tokens->get(2);
+ psPaperHeight = atoi(tok->getCString());
+ psImageableLLX = psImageableLLY = 0;
+ psImageableURX = psPaperWidth;
+ psImageableURY = psPaperHeight;
+ } else {
+ error(-1, "Bad 'psPaperSize' config file command (%s:%d)",
+ fileName->getCString(), line);
+ }
+}
+
+void GlobalParams::parsePSImageableArea(GList *tokens, GString *fileName,
+ int line) {
+ if (tokens->getLength() != 5) {
+ error(-1, "Bad 'psImageableArea' config file command (%s:%d)",
+ fileName->getCString(), line);
+ return;
+ }
+ psImageableLLX = atoi(((GString *)tokens->get(1))->getCString());
+ psImageableLLY = atoi(((GString *)tokens->get(2))->getCString());
+ psImageableURX = atoi(((GString *)tokens->get(3))->getCString());
+ psImageableURY = atoi(((GString *)tokens->get(4))->getCString());
+}
+
+void GlobalParams::parsePSLevel(GList *tokens, GString *fileName, int line) {
+ GString *tok;
+
+ if (tokens->getLength() != 2) {
+ error(-1, "Bad 'psLevel' config file command (%s:%d)",
+ fileName->getCString(), line);
+ return;
+ }
+ tok = (GString *)tokens->get(1);
+ if (!tok->cmp("level1")) {
+ psLevel = psLevel1;
+ } else if (!tok->cmp("level1sep")) {
+ psLevel = psLevel1Sep;
+ } else if (!tok->cmp("level2")) {
+ psLevel = psLevel2;
+ } else if (!tok->cmp("level2sep")) {
+ psLevel = psLevel2Sep;
+ } else if (!tok->cmp("level3")) {
+ psLevel = psLevel3;
+ } else if (!tok->cmp("level3Sep")) {
+ psLevel = psLevel3Sep;
+ } else {
+ error(-1, "Bad 'psLevel' config file command (%s:%d)",
+ fileName->getCString(), line);
+ }
+}
+
+void GlobalParams::parsePSFile(GList *tokens, GString *fileName, int line) {
+ if (tokens->getLength() != 2) {
+ error(-1, "Bad 'psFile' config file command (%s:%d)",
+ fileName->getCString(), line);
+ return;
+ }
+ if (psFile) {
+ delete psFile;
+ }
+ psFile = ((GString *)tokens->get(1))->copy();
+}
+
+void GlobalParams::parsePSFont(GList *tokens, GString *fileName, int line) {
+ PSFontParam *param;
+
+ if (tokens->getLength() != 3) {
+ error(-1, "Bad 'psFont' config file command (%s:%d)",
+ fileName->getCString(), line);
+ return;
+ }
+ param = new PSFontParam(((GString *)tokens->get(1))->copy(), 0,
+ ((GString *)tokens->get(2))->copy(), NULL);
+ psFonts->add(param->pdfFontName, param);
+}
+
+void GlobalParams::parsePSFont16(char *cmdName, GList *fontList,
+ GList *tokens, GString *fileName, int line) {
+ PSFontParam *param;
+ int wMode;
+ GString *tok;
+
+ if (tokens->getLength() != 5) {
+ error(-1, "Bad '%s' config file command (%s:%d)",
+ cmdName, fileName->getCString(), line);
+ return;
+ }
+ tok = (GString *)tokens->get(2);
+ if (!tok->cmp("H")) {
+ wMode = 0;
+ } else if (!tok->cmp("V")) {
+ wMode = 1;
+ } else {
+ error(-1, "Bad '%s' config file command (%s:%d)",
+ cmdName, fileName->getCString(), line);
+ return;
+ }
+ param = new PSFontParam(((GString *)tokens->get(1))->copy(),
+ wMode,
+ ((GString *)tokens->get(3))->copy(),
+ ((GString *)tokens->get(4))->copy());
+ fontList->append(param);
+}
+
+void GlobalParams::parseTextEncoding(GList *tokens, GString *fileName,
+ int line) {
+ if (tokens->getLength() != 2) {
+ error(-1, "Bad 'textEncoding' config file command (%s:%d)",
+ fileName->getCString(), line);
+ return;
+ }
+ delete textEncoding;
+ textEncoding = ((GString *)tokens->get(1))->copy();
+}
+
+void GlobalParams::parseTextEOL(GList *tokens, GString *fileName, int line) {
+ GString *tok;
+
+ if (tokens->getLength() != 2) {
+ error(-1, "Bad 'textEOL' config file command (%s:%d)",
+ fileName->getCString(), line);
+ return;
+ }
+ tok = (GString *)tokens->get(1);
+ if (!tok->cmp("unix")) {
+ textEOL = eolUnix;
+ } else if (!tok->cmp("dos")) {
+ textEOL = eolDOS;
+ } else if (!tok->cmp("mac")) {
+ textEOL = eolMac;
+ } else {
+ error(-1, "Bad 'textEOL' config file command (%s:%d)",
+ fileName->getCString(), line);
+ }
+}
+
+void GlobalParams::parseFontDir(GList *tokens, GString *fileName, int line) {
+ if (tokens->getLength() != 2) {
+ error(-1, "Bad 'fontDir' config file command (%s:%d)",
+ fileName->getCString(), line);
+ return;
+ }
+ fontDirs->append(((GString *)tokens->get(1))->copy());
+}
+
+void GlobalParams::parseInitialZoom(GList *tokens,
+ GString *fileName, int line) {
+ if (tokens->getLength() != 2) {
+ error(-1, "Bad 'initialZoom' config file command (%s:%d)",
+ fileName->getCString(), line);
+ return;
+ }
+ delete initialZoom;
+ initialZoom = ((GString *)tokens->get(1))->copy();
+}
+
+void GlobalParams::parseCommand(char *cmdName, GString **val,
+ GList *tokens, GString *fileName, int line) {
+ if (tokens->getLength() != 2) {
+ error(-1, "Bad '%s' config file command (%s:%d)",
+ cmdName, fileName->getCString(), line);
+ return;
+ }
+ if (*val) {
+ delete *val;
+ }
+ *val = ((GString *)tokens->get(1))->copy();
+}
+
+void GlobalParams::parseYesNo(char *cmdName, GBool *flag,
+ GList *tokens, GString *fileName, int line) {
+ GString *tok;
+
+ if (tokens->getLength() != 2) {
+ error(-1, "Bad '%s' config file command (%s:%d)",
+ cmdName, fileName->getCString(), line);
+ return;
+ }
+ tok = (GString *)tokens->get(1);
+ if (!parseYesNo2(tok->getCString(), flag)) {
+ error(-1, "Bad '%s' config file command (%s:%d)",
+ cmdName, fileName->getCString(), line);
+ }
+}
+
+GBool GlobalParams::parseYesNo2(char *token, GBool *flag) {
+ if (!strcmp(token, "yes")) {
+ *flag = gTrue;
+ } else if (!strcmp(token, "no")) {
+ *flag = gFalse;
+ } else {
+ return gFalse;
+ }
+ return gTrue;
+}
+
+GlobalParams::~GlobalParams() {
+ GHashIter *iter;
+ GString *key;
+ GList *list;
+
+ freeBuiltinFontTables();
+
+ delete macRomanReverseMap;
+
+ delete baseDir;
+ delete nameToUnicode;
+ deleteGHash(cidToUnicodes, GString);
+ deleteGHash(unicodeToUnicodes, GString);
+ deleteGHash(residentUnicodeMaps, UnicodeMap);
+ deleteGHash(unicodeMaps, GString);
+ deleteGList(toUnicodeDirs, GString);
+ deleteGHash(displayFonts, DisplayFontParam);
+ deleteGHash(displayCIDFonts, DisplayFontParam);
+ deleteGHash(displayNamedCIDFonts, DisplayFontParam);
+ if (psFile) {
+ delete psFile;
+ }
+ deleteGHash(psFonts, PSFontParam);
+ deleteGList(psNamedFonts16, PSFontParam);
+ deleteGList(psFonts16, PSFontParam);
+ delete textEncoding;
+ deleteGList(fontDirs, GString);
+ delete initialZoom;
+ if (urlCommand) {
+ delete urlCommand;
+ }
+ if (movieCommand) {
+ delete movieCommand;
+ }
+
+ cMapDirs->startIter(&iter);
+ while (cMapDirs->getNext(&iter, &key, (void **)&list)) {
+ deleteGList(list, GString);
+ }
+ delete cMapDirs;
+
+ delete cidToUnicodeCache;
+ delete unicodeToUnicodeCache;
+ delete unicodeMapCache;
+ delete cMapCache;
+
+#ifdef ENABLE_PLUGINS
+ delete securityHandlers;
+ deleteGList(plugins, Plugin);
+#endif
+
+#if MULTITHREADED
+ gDestroyMutex(&mutex);
+ gDestroyMutex(&unicodeMapCacheMutex);
+ gDestroyMutex(&cMapCacheMutex);
+#endif
+}
+
+//------------------------------------------------------------------------
+
+void GlobalParams::setBaseDir(char *dir) {
+ delete baseDir;
+ baseDir = new GString(dir);
+}
+
+void GlobalParams::setupBaseFonts(char *dir) {
+ GString *fontName;
+ GString *fileName;
+#ifdef WIN32
+ HMODULE shell32Lib;
+ BOOL (__stdcall *SHGetSpecialFolderPathFunc)(HWND hwndOwner,
+ LPTSTR lpszPath,
+ int nFolder,
+ BOOL fCreate);
+ char winFontDir[MAX_PATH];
+#endif
+ FILE *f;
+ DisplayFontParamKind kind;
+ DisplayFontParam *dfp;
+ int i, j;
+
+#ifdef WIN32
+ // SHGetSpecialFolderPath isn't available in older versions of
+ // shell32.dll (Win95 and WinNT4), so do a dynamic load
+ winFontDir[0] = '\0';
+ if ((shell32Lib = LoadLibrary("shell32.dll"))) {
+ if ((SHGetSpecialFolderPathFunc =
+ (BOOL (__stdcall *)(HWND hwndOwner, LPTSTR lpszPath,
+ int nFolder, BOOL fCreate))
+ GetProcAddress(shell32Lib, "SHGetSpecialFolderPath"))) {
+ if (!(*SHGetSpecialFolderPathFunc)(NULL, winFontDir,
+ CSIDL_FONTS, FALSE)) {
+ winFontDir[0] = '\0';
+ }
+ }
+ }
+#endif
+ for (i = 0; displayFontTab[i].name; ++i) {
+ fontName = new GString(displayFontTab[i].name);
+ if (getDisplayFont(fontName)) {
+ delete fontName;
+ continue;
+ }
+ fileName = NULL;
+ kind = displayFontT1; // make gcc happy
+ if (dir) {
+ fileName = appendToPath(new GString(dir), displayFontTab[i].t1FileName);
+ kind = displayFontT1;
+ if ((f = fopen(fileName->getCString(), "rb"))) {
+ fclose(f);
+ } else {
+ delete fileName;
+ fileName = NULL;
+ }
+ }
+#ifdef WIN32
+ if (!fileName && winFontDir[0] && displayFontTab[i].ttFileName) {
+ fileName = appendToPath(new GString(winFontDir),
+ displayFontTab[i].ttFileName);
+ kind = displayFontTT;
+ if ((f = fopen(fileName->getCString(), "rb"))) {
+ fclose(f);
+ } else {
+ delete fileName;
+ fileName = NULL;
+ }
+ }
+ // SHGetSpecialFolderPath(CSIDL_FONTS) doesn't work on Win 2k Server
+ // or Win2003 Server, or with older versions of shell32.dll, so check
+ // the "standard" directories
+ if (displayFontTab[i].ttFileName) {
+ for (j = 0; !fileName && displayFontDirs[j]; ++j) {
+ fileName = appendToPath(new GString(displayFontDirs[j]),
+ displayFontTab[i].ttFileName);
+ kind = displayFontTT;
+ if ((f = fopen(fileName->getCString(), "rb"))) {
+ fclose(f);
+ } else {
+ delete fileName;
+ fileName = NULL;
+ }
+ }
+ }
+#else
+ for (j = 0; !fileName && displayFontDirs[j]; ++j) {
+ fileName = appendToPath(new GString(displayFontDirs[j]),
+ displayFontTab[i].t1FileName);
+ kind = displayFontT1;
+ if ((f = fopen(fileName->getCString(), "rb"))) {
+ fclose(f);
+ } else {
+ delete fileName;
+ fileName = NULL;
+ }
+ }
+#endif
+ if (!fileName) {
+ error(-1, "No display font for '%s'", displayFontTab[i].name);
+ delete fontName;
+ continue;
+ }
+ dfp = new DisplayFontParam(fontName, kind);
+ dfp->t1.fileName = fileName;
+ globalParams->addDisplayFont(dfp);
+ }
+}
+
+//------------------------------------------------------------------------
+// accessors
+//------------------------------------------------------------------------
+
+CharCode GlobalParams::getMacRomanCharCode(char *charName) {
+ // no need to lock - macRomanReverseMap is constant
+ return macRomanReverseMap->lookup(charName);
+}
+
+GString *GlobalParams::getBaseDir() {
+ GString *s;
+
+ lockGlobalParams;
+ s = baseDir->copy();
+ unlockGlobalParams;
+ return s;
+}
+
+Unicode GlobalParams::mapNameToUnicode(char *charName) {
+ // no need to lock - nameToUnicode is constant
+ return nameToUnicode->lookup(charName);
+}
+
+UnicodeMap *GlobalParams::getResidentUnicodeMap(GString *encodingName) {
+ UnicodeMap *map;
+
+ lockGlobalParams;
+ map = (UnicodeMap *)residentUnicodeMaps->lookup(encodingName);
+ unlockGlobalParams;
+ if (map) {
+ map->incRefCnt();
+ }
+ return map;
+}
+
+FILE *GlobalParams::getUnicodeMapFile(GString *encodingName) {
+ GString *fileName;
+ FILE *f;
+
+ lockGlobalParams;
+ if ((fileName = (GString *)unicodeMaps->lookup(encodingName))) {
+ f = fopen(fileName->getCString(), "r");
+ } else {
+ f = NULL;
+ }
+ unlockGlobalParams;
+ return f;
+}
+
+FILE *GlobalParams::findCMapFile(GString *collection, GString *cMapName) {
+ GList *list;
+ GString *dir;
+ GString *fileName;
+ FILE *f;
+ int i;
+
+ lockGlobalParams;
+ if (!(list = (GList *)cMapDirs->lookup(collection))) {
+ unlockGlobalParams;
+ return NULL;
+ }
+ for (i = 0; i < list->getLength(); ++i) {
+ dir = (GString *)list->get(i);
+ fileName = appendToPath(dir->copy(), cMapName->getCString());
+ f = fopen(fileName->getCString(), "r");
+ delete fileName;
+ if (f) {
+ unlockGlobalParams;
+ return f;
+ }
+ }
+ unlockGlobalParams;
+ return NULL;
+}
+
+FILE *GlobalParams::findToUnicodeFile(GString *name) {
+ GString *dir, *fileName;
+ FILE *f;
+ int i;
+
+ lockGlobalParams;
+ for (i = 0; i < toUnicodeDirs->getLength(); ++i) {
+ dir = (GString *)toUnicodeDirs->get(i);
+ fileName = appendToPath(dir->copy(), name->getCString());
+ f = fopen(fileName->getCString(), "r");
+ delete fileName;
+ if (f) {
+ unlockGlobalParams;
+ return f;
+ }
+ }
+ unlockGlobalParams;
+ return NULL;
+}
+
+DisplayFontParam *GlobalParams::getDisplayFont(GString *fontName) {
+ DisplayFontParam *dfp;
+
+ lockGlobalParams;
+ dfp = (DisplayFontParam *)displayFonts->lookup(fontName);
+ unlockGlobalParams;
+ return dfp;
+}
+
+DisplayFontParam *GlobalParams::getDisplayCIDFont(GString *fontName,
+ GString *collection) {
+ DisplayFontParam *dfp;
+
+ lockGlobalParams;
+ if (!fontName ||
+ !(dfp = (DisplayFontParam *)displayNamedCIDFonts->lookup(fontName))) {
+ dfp = (DisplayFontParam *)displayCIDFonts->lookup(collection);
+ }
+ unlockGlobalParams;
+ return dfp;
+}
+
+GString *GlobalParams::getPSFile() {
+ GString *s;
+
+ lockGlobalParams;
+ s = psFile ? psFile->copy() : (GString *)NULL;
+ unlockGlobalParams;
+ return s;
+}
+
+int GlobalParams::getPSPaperWidth() {
+ int w;
+
+ lockGlobalParams;
+ w = psPaperWidth;
+ unlockGlobalParams;
+ return w;
+}
+
+int GlobalParams::getPSPaperHeight() {
+ int h;
+
+ lockGlobalParams;
+ h = psPaperHeight;
+ unlockGlobalParams;
+ return h;
+}
+
+void GlobalParams::getPSImageableArea(int *llx, int *lly, int *urx, int *ury) {
+ lockGlobalParams;
+ *llx = psImageableLLX;
+ *lly = psImageableLLY;
+ *urx = psImageableURX;
+ *ury = psImageableURY;
+ unlockGlobalParams;
+}
+
+GBool GlobalParams::getPSCrop() {
+ GBool f;
+
+ lockGlobalParams;
+ f = psCrop;
+ unlockGlobalParams;
+ return f;
+}
+
+GBool GlobalParams::getPSExpandSmaller() {
+ GBool f;
+
+ lockGlobalParams;
+ f = psExpandSmaller;
+ unlockGlobalParams;
+ return f;
+}
+
+GBool GlobalParams::getPSShrinkLarger() {
+ GBool f;
+
+ lockGlobalParams;
+ f = psShrinkLarger;
+ unlockGlobalParams;
+ return f;
+}
+
+GBool GlobalParams::getPSCenter() {
+ GBool f;
+
+ lockGlobalParams;
+ f = psCenter;
+ unlockGlobalParams;
+ return f;
+}
+
+GBool GlobalParams::getPSDuplex() {
+ GBool d;
+
+ lockGlobalParams;
+ d = psDuplex;
+ unlockGlobalParams;
+ return d;
+}
+
+PSLevel GlobalParams::getPSLevel() {
+ PSLevel level;
+
+ lockGlobalParams;
+ level = psLevel;
+ unlockGlobalParams;
+ return level;
+}
+
+PSFontParam *GlobalParams::getPSFont(GString *fontName) {
+ PSFontParam *p;
+
+ lockGlobalParams;
+ p = (PSFontParam *)psFonts->lookup(fontName);
+ unlockGlobalParams;
+ return p;
+}
+
+PSFontParam *GlobalParams::getPSFont16(GString *fontName,
+ GString *collection, int wMode) {
+ PSFontParam *p;
+ int i;
+
+ lockGlobalParams;
+ p = NULL;
+ if (fontName) {
+ for (i = 0; i < psNamedFonts16->getLength(); ++i) {
+ p = (PSFontParam *)psNamedFonts16->get(i);
+ if (!p->pdfFontName->cmp(fontName) &&
+ p->wMode == wMode) {
+ break;
+ }
+ p = NULL;
+ }
+ }
+ if (!p && collection) {
+ for (i = 0; i < psFonts16->getLength(); ++i) {
+ p = (PSFontParam *)psFonts16->get(i);
+ if (!p->pdfFontName->cmp(collection) &&
+ p->wMode == wMode) {
+ break;
+ }
+ p = NULL;
+ }
+ }
+ unlockGlobalParams;
+ return p;
+}
+
+GBool GlobalParams::getPSEmbedType1() {
+ GBool e;
+
+ lockGlobalParams;
+ e = psEmbedType1;
+ unlockGlobalParams;
+ return e;
+}
+
+GBool GlobalParams::getPSEmbedTrueType() {
+ GBool e;
+
+ lockGlobalParams;
+ e = psEmbedTrueType;
+ unlockGlobalParams;
+ return e;
+}
+
+GBool GlobalParams::getPSEmbedCIDPostScript() {
+ GBool e;
+
+ lockGlobalParams;
+ e = psEmbedCIDPostScript;
+ unlockGlobalParams;
+ return e;
+}
+
+GBool GlobalParams::getPSEmbedCIDTrueType() {
+ GBool e;
+
+ lockGlobalParams;
+ e = psEmbedCIDTrueType;
+ unlockGlobalParams;
+ return e;
+}
+
+GBool GlobalParams::getPSOPI() {
+ GBool opi;
+
+ lockGlobalParams;
+ opi = psOPI;
+ unlockGlobalParams;
+ return opi;
+}
+
+GBool GlobalParams::getPSASCIIHex() {
+ GBool ah;
+
+ lockGlobalParams;
+ ah = psASCIIHex;
+ unlockGlobalParams;
+ return ah;
+}
+
+GString *GlobalParams::getTextEncodingName() {
+ GString *s;
+
+ lockGlobalParams;
+ s = textEncoding->copy();
+ unlockGlobalParams;
+ return s;
+}
+
+EndOfLineKind GlobalParams::getTextEOL() {
+ EndOfLineKind eol;
+
+ lockGlobalParams;
+ eol = textEOL;
+ unlockGlobalParams;
+ return eol;
+}
+
+GBool GlobalParams::getTextPageBreaks() {
+ GBool pageBreaks;
+
+ lockGlobalParams;
+ pageBreaks = textPageBreaks;
+ unlockGlobalParams;
+ return pageBreaks;
+}
+
+GBool GlobalParams::getTextKeepTinyChars() {
+ GBool tiny;
+
+ lockGlobalParams;
+ tiny = textKeepTinyChars;
+ unlockGlobalParams;
+ return tiny;
+}
+
+GString *GlobalParams::findFontFile(GString *fontName, char **exts) {
+ GString *dir, *fileName;
+ char **ext;
+ FILE *f;
+ int i;
+
+ lockGlobalParams;
+ for (i = 0; i < fontDirs->getLength(); ++i) {
+ dir = (GString *)fontDirs->get(i);
+ for (ext = exts; *ext; ++ext) {
+ fileName = appendToPath(dir->copy(), fontName->getCString());
+ fileName->append(*ext);
+ if ((f = fopen(fileName->getCString(), "rb"))) {
+ fclose(f);
+ unlockGlobalParams;
+ return fileName;
+ }
+ delete fileName;
+ }
+ }
+ unlockGlobalParams;
+ return NULL;
+}
+
+GString *GlobalParams::getInitialZoom() {
+ GString *s;
+
+ lockGlobalParams;
+ s = initialZoom->copy();
+ unlockGlobalParams;
+ return s;
+}
+
+GBool GlobalParams::getContinuousView() {
+ GBool f;
+
+ lockGlobalParams;
+ f = continuousView;
+ unlockGlobalParams;
+ return f;
+}
+
+GBool GlobalParams::getEnableT1lib() {
+ GBool f;
+
+ lockGlobalParams;
+ f = enableT1lib;
+ unlockGlobalParams;
+ return f;
+}
+
+GBool GlobalParams::getEnableFreeType() {
+ GBool f;
+
+ lockGlobalParams;
+ f = enableFreeType;
+ unlockGlobalParams;
+ return f;
+}
+
+
+GBool GlobalParams::getAntialias() {
+ GBool f;
+
+ lockGlobalParams;
+ f = antialias;
+ unlockGlobalParams;
+ return f;
+}
+
+GBool GlobalParams::getMapNumericCharNames() {
+ GBool map;
+
+ lockGlobalParams;
+ map = mapNumericCharNames;
+ unlockGlobalParams;
+ return map;
+}
+
+GBool GlobalParams::getPrintCommands() {
+ GBool p;
+
+ lockGlobalParams;
+ p = printCommands;
+ unlockGlobalParams;
+ return p;
+}
+
+GBool GlobalParams::getErrQuiet() {
+ GBool q;
+
+ lockGlobalParams;
+ q = errQuiet;
+ unlockGlobalParams;
+ return q;
+}
+
+CharCodeToUnicode *GlobalParams::getCIDToUnicode(GString *collection) {
+ GString *fileName;
+ CharCodeToUnicode *ctu;
+
+ lockGlobalParams;
+ if (!(ctu = cidToUnicodeCache->getCharCodeToUnicode(collection))) {
+ if ((fileName = (GString *)cidToUnicodes->lookup(collection)) &&
+ (ctu = CharCodeToUnicode::parseCIDToUnicode(fileName, collection))) {
+ cidToUnicodeCache->add(ctu);
+ }
+ }
+ unlockGlobalParams;
+ return ctu;
+}
+
+CharCodeToUnicode *GlobalParams::getUnicodeToUnicode(GString *fontName) {
+ CharCodeToUnicode *ctu;
+ GHashIter *iter;
+ GString *fontPattern, *fileName;
+
+ lockGlobalParams;
+ fileName = NULL;
+ unicodeToUnicodes->startIter(&iter);
+ while (unicodeToUnicodes->getNext(&iter, &fontPattern, (void **)&fileName)) {
+ if (strstr(fontName->getCString(), fontPattern->getCString())) {
+ unicodeToUnicodes->killIter(&iter);
+ break;
+ }
+ fileName = NULL;
+ }
+ if (fileName) {
+ if (!(ctu = unicodeToUnicodeCache->getCharCodeToUnicode(fileName))) {
+ if ((ctu = CharCodeToUnicode::parseUnicodeToUnicode(fileName))) {
+ unicodeToUnicodeCache->add(ctu);
+ }
+ }
+ } else {
+ ctu = NULL;
+ }
+ unlockGlobalParams;
+ return ctu;
+}
+
+UnicodeMap *GlobalParams::getUnicodeMap(GString *encodingName) {
+ return getUnicodeMap2(encodingName);
+}
+
+UnicodeMap *GlobalParams::getUnicodeMap2(GString *encodingName) {
+ UnicodeMap *map;
+
+ if (!(map = getResidentUnicodeMap(encodingName))) {
+ lockUnicodeMapCache;
+ map = unicodeMapCache->getUnicodeMap(encodingName);
+ unlockUnicodeMapCache;
+ }
+ return map;
+}
+
+CMap *GlobalParams::getCMap(GString *collection, GString *cMapName) {
+ CMap *cMap;
+
+ lockCMapCache;
+ cMap = cMapCache->getCMap(collection, cMapName);
+ unlockCMapCache;
+ return cMap;
+}
+
+UnicodeMap *GlobalParams::getTextEncoding() {
+ return getUnicodeMap2(textEncoding);
+}
+
+//------------------------------------------------------------------------
+// functions to set parameters
+//------------------------------------------------------------------------
+
+void GlobalParams::addDisplayFont(DisplayFontParam *param) {
+ DisplayFontParam *old;
+
+ lockGlobalParams;
+ if ((old = (DisplayFontParam *)displayFonts->remove(param->name))) {
+ delete old;
+ }
+ displayFonts->add(param->name, param);
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSFile(char *file) {
+ lockGlobalParams;
+ if (psFile) {
+ delete psFile;
+ }
+ psFile = new GString(file);
+ unlockGlobalParams;
+}
+
+GBool GlobalParams::setPSPaperSize(char *size) {
+ lockGlobalParams;
+ if (!strcmp(size, "match")) {
+ psPaperWidth = psPaperHeight = -1;
+ } else if (!strcmp(size, "letter")) {
+ psPaperWidth = 612;
+ psPaperHeight = 792;
+ } else if (!strcmp(size, "legal")) {
+ psPaperWidth = 612;
+ psPaperHeight = 1008;
+ } else if (!strcmp(size, "A4")) {
+ psPaperWidth = 595;
+ psPaperHeight = 842;
+ } else if (!strcmp(size, "A3")) {
+ psPaperWidth = 842;
+ psPaperHeight = 1190;
+ } else {
+ unlockGlobalParams;
+ return gFalse;
+ }
+ psImageableLLX = psImageableLLY = 0;
+ psImageableURX = psPaperWidth;
+ psImageableURY = psPaperHeight;
+ unlockGlobalParams;
+ return gTrue;
+}
+
+void GlobalParams::setPSPaperWidth(int width) {
+ lockGlobalParams;
+ psPaperWidth = width;
+ psImageableLLX = 0;
+ psImageableURX = psPaperWidth;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSPaperHeight(int height) {
+ lockGlobalParams;
+ psPaperHeight = height;
+ psImageableLLY = 0;
+ psImageableURY = psPaperHeight;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSImageableArea(int llx, int lly, int urx, int ury) {
+ lockGlobalParams;
+ psImageableLLX = llx;
+ psImageableLLY = lly;
+ psImageableURX = urx;
+ psImageableURY = ury;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSCrop(GBool crop) {
+ lockGlobalParams;
+ psCrop = crop;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSExpandSmaller(GBool expand) {
+ lockGlobalParams;
+ psExpandSmaller = expand;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSShrinkLarger(GBool shrink) {
+ lockGlobalParams;
+ psShrinkLarger = shrink;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSCenter(GBool center) {
+ lockGlobalParams;
+ psCenter = center;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSDuplex(GBool duplex) {
+ lockGlobalParams;
+ psDuplex = duplex;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSLevel(PSLevel level) {
+ lockGlobalParams;
+ psLevel = level;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSEmbedType1(GBool embed) {
+ lockGlobalParams;
+ psEmbedType1 = embed;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSEmbedTrueType(GBool embed) {
+ lockGlobalParams;
+ psEmbedTrueType = embed;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSEmbedCIDPostScript(GBool embed) {
+ lockGlobalParams;
+ psEmbedCIDPostScript = embed;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSEmbedCIDTrueType(GBool embed) {
+ lockGlobalParams;
+ psEmbedCIDTrueType = embed;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSOPI(GBool opi) {
+ lockGlobalParams;
+ psOPI = opi;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPSASCIIHex(GBool hex) {
+ lockGlobalParams;
+ psASCIIHex = hex;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setTextEncoding(char *encodingName) {
+ lockGlobalParams;
+ delete textEncoding;
+ textEncoding = new GString(encodingName);
+ unlockGlobalParams;
+}
+
+GBool GlobalParams::setTextEOL(char *s) {
+ lockGlobalParams;
+ if (!strcmp(s, "unix")) {
+ textEOL = eolUnix;
+ } else if (!strcmp(s, "dos")) {
+ textEOL = eolDOS;
+ } else if (!strcmp(s, "mac")) {
+ textEOL = eolMac;
+ } else {
+ unlockGlobalParams;
+ return gFalse;
+ }
+ unlockGlobalParams;
+ return gTrue;
+}
+
+void GlobalParams::setTextPageBreaks(GBool pageBreaks) {
+ lockGlobalParams;
+ textPageBreaks = pageBreaks;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setTextKeepTinyChars(GBool keep) {
+ lockGlobalParams;
+ textKeepTinyChars = keep;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setInitialZoom(char *s) {
+ lockGlobalParams;
+ delete initialZoom;
+ initialZoom = new GString(s);
+ unlockGlobalParams;
+}
+
+void GlobalParams::setContinuousView(GBool cont) {
+ lockGlobalParams;
+ continuousView = cont;
+ unlockGlobalParams;
+}
+
+GBool GlobalParams::setEnableT1lib(char *s) {
+ GBool ok;
+
+ lockGlobalParams;
+ ok = parseYesNo2(s, &enableT1lib);
+ unlockGlobalParams;
+ return ok;
+}
+
+GBool GlobalParams::setEnableFreeType(char *s) {
+ GBool ok;
+
+ lockGlobalParams;
+ ok = parseYesNo2(s, &enableFreeType);
+ unlockGlobalParams;
+ return ok;
+}
+
+
+GBool GlobalParams::setAntialias(char *s) {
+ GBool ok;
+
+ lockGlobalParams;
+ ok = parseYesNo2(s, &antialias);
+ unlockGlobalParams;
+ return ok;
+}
+
+void GlobalParams::setMapNumericCharNames(GBool map) {
+ lockGlobalParams;
+ mapNumericCharNames = map;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setPrintCommands(GBool printCommandsA) {
+ lockGlobalParams;
+ printCommands = printCommandsA;
+ unlockGlobalParams;
+}
+
+void GlobalParams::setErrQuiet(GBool errQuietA) {
+ lockGlobalParams;
+ errQuiet = errQuietA;
+ unlockGlobalParams;
+}
+
+void GlobalParams::addSecurityHandler(XpdfSecurityHandler *handler) {
+#ifdef ENABLE_PLUGINS
+ lockGlobalParams;
+ securityHandlers->append(handler);
+ unlockGlobalParams;
+#endif
+}
+
+XpdfSecurityHandler *GlobalParams::getSecurityHandler(char *name) {
+#ifdef ENABLE_PLUGINS
+ XpdfSecurityHandler *hdlr;
+ int i;
+
+ lockGlobalParams;
+ for (i = 0; i < securityHandlers->getLength(); ++i) {
+ hdlr = (XpdfSecurityHandler *)securityHandlers->get(i);
+ if (!stricmp(hdlr->name, name)) {
+ unlockGlobalParams;
+ return hdlr;
+ }
+ }
+ unlockGlobalParams;
+
+ if (!loadPlugin("security", name)) {
+ return NULL;
+ }
+
+ lockGlobalParams;
+ for (i = 0; i < securityHandlers->getLength(); ++i) {
+ hdlr = (XpdfSecurityHandler *)securityHandlers->get(i);
+ if (!strcmp(hdlr->name, name)) {
+ unlockGlobalParams;
+ return hdlr;
+ }
+ }
+ unlockGlobalParams;
+#endif
+
+ return NULL;
+}
+
+#ifdef ENABLE_PLUGINS
+//------------------------------------------------------------------------
+// plugins
+//------------------------------------------------------------------------
+
+GBool GlobalParams::loadPlugin(char *type, char *name) {
+ Plugin *plugin;
+
+ if (!(plugin = Plugin::load(type, name))) {
+ return gFalse;
+ }
+ lockGlobalParams;
+ plugins->append(plugin);
+ unlockGlobalParams;
+ return gTrue;
+}
+
+#endif // ENABLE_PLUGINS
--- /dev/null
+//========================================================================
+//
+// GlobalParams.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef GLOBALPARAMS_H
+#define GLOBALPARAMS_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <stdio.h>
+#include "gtypes.h"
+#include "CharTypes.h"
+
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+
+class GString;
+class GList;
+class GHash;
+class NameToCharCode;
+class CharCodeToUnicode;
+class CharCodeToUnicodeCache;
+class UnicodeMap;
+class UnicodeMapCache;
+class CMap;
+class CMapCache;
+struct XpdfSecurityHandler;
+class GlobalParams;
+
+//------------------------------------------------------------------------
+
+// The global parameters object.
+extern GlobalParams *globalParams;
+
+//------------------------------------------------------------------------
+
+enum DisplayFontParamKind {
+ displayFontT1,
+ displayFontTT
+};
+
+struct DisplayFontParamT1 {
+ GString *fileName;
+};
+
+struct DisplayFontParamTT {
+ GString *fileName;
+};
+
+class DisplayFontParam {
+public:
+
+ GString *name; // font name for 8-bit fonts and named
+ // CID fonts; collection name for
+ // generic CID fonts
+ DisplayFontParamKind kind;
+ union {
+ DisplayFontParamT1 t1;
+ DisplayFontParamTT tt;
+ };
+
+ DisplayFontParam(GString *nameA, DisplayFontParamKind kindA);
+ ~DisplayFontParam();
+};
+
+//------------------------------------------------------------------------
+
+class PSFontParam {
+public:
+
+ GString *pdfFontName; // PDF font name for 8-bit fonts and
+ // named 16-bit fonts; char collection
+ // name for generic 16-bit fonts
+ int wMode; // writing mode (0=horiz, 1=vert) for
+ // 16-bit fonts
+ GString *psFontName; // PostScript font name
+ GString *encoding; // encoding, for 16-bit fonts only
+
+ PSFontParam(GString *pdfFontNameA, int wModeA,
+ GString *psFontNameA, GString *encodingA);
+ ~PSFontParam();
+};
+
+//------------------------------------------------------------------------
+
+enum PSLevel {
+ psLevel1,
+ psLevel1Sep,
+ psLevel2,
+ psLevel2Sep,
+ psLevel3,
+ psLevel3Sep
+};
+
+//------------------------------------------------------------------------
+
+enum EndOfLineKind {
+ eolUnix, // LF
+ eolDOS, // CR+LF
+ eolMac // CR
+};
+
+//------------------------------------------------------------------------
+
+class GlobalParams {
+public:
+
+ // Initialize the global parameters by attempting to read a config
+ // file.
+ GlobalParams(char *cfgFileName);
+
+ ~GlobalParams();
+
+ void setBaseDir(char *dir);
+ void setupBaseFonts(char *dir);
+
+ //----- accessors
+
+ CharCode getMacRomanCharCode(char *charName);
+
+ GString *getBaseDir();
+ Unicode mapNameToUnicode(char *charName);
+ UnicodeMap *getResidentUnicodeMap(GString *encodingName);
+ FILE *getUnicodeMapFile(GString *encodingName);
+ FILE *findCMapFile(GString *collection, GString *cMapName);
+ FILE *findToUnicodeFile(GString *name);
+ DisplayFontParam *getDisplayFont(GString *fontName);
+ DisplayFontParam *getDisplayCIDFont(GString *fontName, GString *collection);
+ GString *getPSFile();
+ int getPSPaperWidth();
+ int getPSPaperHeight();
+ void getPSImageableArea(int *llx, int *lly, int *urx, int *ury);
+ GBool getPSDuplex();
+ GBool getPSCrop();
+ GBool getPSExpandSmaller();
+ GBool getPSShrinkLarger();
+ GBool getPSCenter();
+ PSLevel getPSLevel();
+ PSFontParam *getPSFont(GString *fontName);
+ PSFontParam *getPSFont16(GString *fontName, GString *collection, int wMode);
+ GBool getPSEmbedType1();
+ GBool getPSEmbedTrueType();
+ GBool getPSEmbedCIDPostScript();
+ GBool getPSEmbedCIDTrueType();
+ GBool getPSOPI();
+ GBool getPSASCIIHex();
+ GString *getTextEncodingName();
+ EndOfLineKind getTextEOL();
+ GBool getTextPageBreaks();
+ GBool getTextKeepTinyChars();
+ GString *findFontFile(GString *fontName, char **exts);
+ GString *getInitialZoom();
+ GBool getContinuousView();
+ GBool getEnableT1lib();
+ GBool getEnableFreeType();
+ GBool getAntialias();
+ GString *getURLCommand() { return urlCommand; }
+ GString *getMovieCommand() { return movieCommand; }
+ GBool getMapNumericCharNames();
+ GBool getPrintCommands();
+ GBool getErrQuiet();
+
+ CharCodeToUnicode *getCIDToUnicode(GString *collection);
+ CharCodeToUnicode *getUnicodeToUnicode(GString *fontName);
+ UnicodeMap *getUnicodeMap(GString *encodingName);
+ CMap *getCMap(GString *collection, GString *cMapName);
+ UnicodeMap *getTextEncoding();
+
+ //----- functions to set parameters
+
+ void addDisplayFont(DisplayFontParam *param);
+ void setPSFile(char *file);
+ GBool setPSPaperSize(char *size);
+ void setPSPaperWidth(int width);
+ void setPSPaperHeight(int height);
+ void setPSImageableArea(int llx, int lly, int urx, int ury);
+ void setPSDuplex(GBool duplex);
+ void setPSCrop(GBool crop);
+ void setPSExpandSmaller(GBool expand);
+ void setPSShrinkLarger(GBool shrink);
+ void setPSCenter(GBool center);
+ void setPSLevel(PSLevel level);
+ void setPSEmbedType1(GBool embed);
+ void setPSEmbedTrueType(GBool embed);
+ void setPSEmbedCIDPostScript(GBool embed);
+ void setPSEmbedCIDTrueType(GBool embed);
+ void setPSOPI(GBool opi);
+ void setPSASCIIHex(GBool hex);
+ void setTextEncoding(char *encodingName);
+ GBool setTextEOL(char *s);
+ void setTextPageBreaks(GBool pageBreaks);
+ void setTextKeepTinyChars(GBool keep);
+ void setInitialZoom(char *s);
+ void setContinuousView(GBool cont);
+ GBool setEnableT1lib(char *s);
+ GBool setEnableFreeType(char *s);
+ GBool setAntialias(char *s);
+ void setMapNumericCharNames(GBool map);
+ void setPrintCommands(GBool printCommandsA);
+ void setErrQuiet(GBool errQuietA);
+
+ //----- security handlers
+
+ void addSecurityHandler(XpdfSecurityHandler *handler);
+ XpdfSecurityHandler *getSecurityHandler(char *name);
+ void parseFile(GString *fileName, FILE *f);
+
+private:
+
+ void parseNameToUnicode(GList *tokens, GString *fileName, int line);
+ void parseCIDToUnicode(GList *tokens, GString *fileName, int line);
+ void parseUnicodeToUnicode(GList *tokens, GString *fileName, int line);
+ void parseUnicodeMap(GList *tokens, GString *fileName, int line);
+ void parseCMapDir(GList *tokens, GString *fileName, int line);
+ void parseToUnicodeDir(GList *tokens, GString *fileName, int line);
+ void parseDisplayFont(GList *tokens, GHash *fontHash,
+ DisplayFontParamKind kind,
+ GString *fileName, int line);
+ void parsePSFile(GList *tokens, GString *fileName, int line);
+ void parsePSPaperSize(GList *tokens, GString *fileName, int line);
+ void parsePSImageableArea(GList *tokens, GString *fileName, int line);
+ void parsePSLevel(GList *tokens, GString *fileName, int line);
+ void parsePSFont(GList *tokens, GString *fileName, int line);
+ void parsePSFont16(char *cmdName, GList *fontList,
+ GList *tokens, GString *fileName, int line);
+ void parseTextEncoding(GList *tokens, GString *fileName, int line);
+ void parseTextEOL(GList *tokens, GString *fileName, int line);
+ void parseFontDir(GList *tokens, GString *fileName, int line);
+ void parseInitialZoom(GList *tokens, GString *fileName, int line);
+ void parseCommand(char *cmdName, GString **val,
+ GList *tokens, GString *fileName, int line);
+ void parseYesNo(char *cmdName, GBool *flag,
+ GList *tokens, GString *fileName, int line);
+ GBool parseYesNo2(char *token, GBool *flag);
+ UnicodeMap *getUnicodeMap2(GString *encodingName);
+#ifdef ENABLE_PLUGINS
+ GBool loadPlugin(char *type, char *name);
+#endif
+
+ //----- config file base path
+
+ GString*path;
+
+ //----- static tables
+
+ NameToCharCode * // mapping from char name to
+ macRomanReverseMap; // MacRomanEncoding index
+
+ //----- user-modifiable settings
+
+ GString *baseDir; // base directory - for plugins, etc.
+ NameToCharCode * // mapping from char name to Unicode
+ nameToUnicode;
+ GHash *cidToUnicodes; // files for mappings from char collections
+ // to Unicode, indexed by collection name
+ // [GString]
+ GHash *unicodeToUnicodes; // files for Unicode-to-Unicode mappings,
+ // indexed by font name pattern [GString]
+ GHash *residentUnicodeMaps; // mappings from Unicode to char codes,
+ // indexed by encoding name [UnicodeMap]
+ GHash *unicodeMaps; // files for mappings from Unicode to char
+ // codes, indexed by encoding name [GString]
+ GHash *cMapDirs; // list of CMap dirs, indexed by collection
+ // name [GList[GString]]
+ GList *toUnicodeDirs; // list of ToUnicode CMap dirs [GString]
+ GHash *displayFonts; // display font info, indexed by font name
+ // [DisplayFontParam]
+ GHash *displayCIDFonts; // display CID font info, indexed by
+ // collection [DisplayFontParam]
+ GHash *displayNamedCIDFonts; // display CID font info, indexed by
+ // font name [DisplayFontParam]
+ GString *psFile; // PostScript file or command (for xpdf)
+ int psPaperWidth; // paper size, in PostScript points, for
+ int psPaperHeight; // PostScript output
+ int psImageableLLX, // imageable area, in PostScript points,
+ psImageableLLY, // for PostScript output
+ psImageableURX,
+ psImageableURY;
+ GBool psCrop; // crop PS output to CropBox
+ GBool psExpandSmaller; // expand smaller pages to fill paper
+ GBool psShrinkLarger; // shrink larger pages to fit paper
+ GBool psCenter; // center pages on the paper
+ GBool psDuplex; // enable duplexing in PostScript?
+ PSLevel psLevel; // PostScript level to generate
+ GHash *psFonts; // PostScript font info, indexed by PDF
+ // font name [PSFontParam]
+ GList *psNamedFonts16; // named 16-bit fonts [PSFontParam]
+ GList *psFonts16; // generic 16-bit fonts [PSFontParam]
+ GBool psEmbedType1; // embed Type 1 fonts?
+ GBool psEmbedTrueType; // embed TrueType fonts?
+ GBool psEmbedCIDPostScript; // embed CID PostScript fonts?
+ GBool psEmbedCIDTrueType; // embed CID TrueType fonts?
+ GBool psOPI; // generate PostScript OPI comments?
+ GBool psASCIIHex; // use ASCIIHex instead of ASCII85?
+ GString *textEncoding; // encoding (unicodeMap) to use for text
+ // output
+ EndOfLineKind textEOL; // type of EOL marker to use for text
+ // output
+ GBool textPageBreaks; // insert end-of-page markers?
+ GBool textKeepTinyChars; // keep all characters in text output
+ GList *fontDirs; // list of font dirs [GString]
+ GString *initialZoom; // initial zoom level
+ GBool continuousView; // continuous view mode
+ GBool enableT1lib; // t1lib enable flag
+ GBool enableFreeType; // FreeType enable flag
+ GBool antialias; // anti-aliasing enable flag
+ GString *urlCommand; // command executed for URL links
+ GString *movieCommand; // command executed for movie annotations
+ GBool mapNumericCharNames; // map numeric char names (from font subsets)?
+ GBool printCommands; // print the drawing commands
+ GBool errQuiet; // suppress error messages?
+
+ CharCodeToUnicodeCache *cidToUnicodeCache;
+ CharCodeToUnicodeCache *unicodeToUnicodeCache;
+ UnicodeMapCache *unicodeMapCache;
+ CMapCache *cMapCache;
+
+#ifdef ENABLE_PLUGINS
+ GList *plugins; // list of plugins [Plugin]
+ GList *securityHandlers; // list of loaded security handlers
+ // [XpdfSecurityHandler]
+#endif
+
+#if MULTITHREADED
+ GMutex mutex;
+ GMutex unicodeMapCacheMutex;
+ GMutex cMapCacheMutex;
+#endif
+};
+
+#endif
--- /dev/null
+#include "InfoOutputDev.h"
+#include "GfxState.h"
+#include "../log.h"
+#include <math.h>
+
+InfoOutputDev::InfoOutputDev()
+{
+ num_links = 0;
+ num_images = 0;
+ num_fonts = 0;
+ id2font = new GHash();
+}
+InfoOutputDev::~InfoOutputDev()
+{
+ delete id2font;
+}
+GBool InfoOutputDev::upsideDown() {return gTrue;}
+GBool InfoOutputDev::useDrawChar() {return gTrue;}
+GBool InfoOutputDev::interpretType3Chars() {return gTrue;}
+void InfoOutputDev::startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2)
+{
+ double x1,y1,x2,y2;
+ state->transform(crop_x1,crop_y1,&x1,&y1);
+ state->transform(crop_x2,crop_y2,&x2,&y2);
+ if(x2<x1) {double x3=x1;x1=x2;x2=x3;}
+ if(y2<y1) {double y3=y1;y1=y2;y2=y3;}
+ this->x1 = (int)x1;
+ this->y1 = (int)y1;
+ this->x2 = (int)x2;
+ this->y2 = (int)y2;
+}
+void InfoOutputDev::drawLink(Link *link, Catalog *catalog)
+{
+ num_links++;
+}
+double InfoOutputDev::getMaximumFontSize(char*id)
+{
+ FontInfo*info = (FontInfo*)id2font->lookup(id);
+ if(!info) {
+ msg("<error> Unknown font id: %s", id);
+ return 0.0;
+ }
+ return info->max_size;
+}
+
+static char*getFontID(GfxFont*font)
+{
+ Ref*ref = font->getID();
+ GString*gstr = font->getName();
+ char* fname = gstr==0?0:gstr->getCString();
+ char buf[128];
+ if(fname==0) {
+ sprintf(buf, "font-%d-%d", ref->num, ref->gen);
+ } else {
+ sprintf(buf, "%s-%d-%d", fname, ref->num, ref->gen);
+ }
+ return strdup(buf);
+}
+
+
+void InfoOutputDev::updateFont(GfxState *state)
+{
+ GfxFont*font = state->getFont();
+ if(!font)
+ return;
+ char*id = getFontID(font);
+
+ FontInfo*info = (FontInfo*)id2font->lookup(id);
+ if(!info) {
+ GString* idStr = new GString(id);
+ info = new FontInfo;
+ info->font = font;
+ info->max_size = 0;
+ id2font->add(idStr, (void*)info);
+ free(id);
+ num_fonts++;
+ }
+ currentfont = info;
+}
+
+void InfoOutputDev::drawChar(GfxState *state, double x, double y,
+ double dx, double dy,
+ double originX, double originY,
+ CharCode code, int nBytes, Unicode *u, int uLen)
+{
+ int render = state->getRender();
+ if (render == 3)
+ return;
+ double m11,m21,m12,m22;
+ state->getFontTransMat(&m11, &m12, &m21, &m22);
+ m11 *= state->getHorizScaling();
+ m21 *= state->getHorizScaling();
+ double lenx = sqrt(m11*m11 + m12*m12);
+ double leny = sqrt(m21*m21 + m22*m22);
+ double len = lenx>leny?lenx:leny;
+ if(currentfont && currentfont->max_size < len) {
+ currentfont->max_size = len;
+ }
+}
+void InfoOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GBool invert,
+ GBool inlineImg)
+{
+ num_images++;
+}
+void InfoOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GfxImageColorMap *colorMap,
+ int *maskColors, GBool inlineImg)
+{
+ num_images++;
+}
+
--- /dev/null
+#ifndef __infooutputdev_h__
+#define __infooutputdev_h__
+
+#include "GfxFont.h"
+#include "OutputDev.h"
+#include "GHash.h"
+
+struct FontInfo
+{
+ GfxFont*font;
+ double max_size;
+};
+
+class InfoOutputDev: public OutputDev
+{
+ GHash* id2font;
+ FontInfo* currentfont;
+ public:
+ int x1,y1,x2,y2;
+ int num_links;
+ int num_images;
+ int num_fonts;
+
+ InfoOutputDev();
+ virtual ~InfoOutputDev();
+ virtual GBool upsideDown();
+ virtual GBool useDrawChar();
+ virtual GBool interpretType3Chars();
+ virtual void startPage(int pageNum, GfxState *state, double crop_x1, double crop_y1, double crop_x2, double crop_y2);
+ virtual void drawLink(Link *link, Catalog *catalog);
+ virtual double getMaximumFontSize(char*id);
+ virtual void updateFont(GfxState *state);
+ virtual void drawChar(GfxState *state, double x, double y,
+ double dx, double dy,
+ double originX, double originY,
+ CharCode code, int nBytes, Unicode *u, int uLen);
+ virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GBool invert,
+ GBool inlineImg);
+ virtual void drawImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GfxImageColorMap *colorMap,
+ int *maskColors, GBool inlineImg);
+};
+
+#endif //__infooutputdev_h__
--- /dev/null
+//========================================================================
+//
+// JArithmeticDecoder.cc
+//
+// Copyright 2002-2004 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "Object.h"
+#include "Stream.h"
+#include "JArithmeticDecoder.h"
+
+//------------------------------------------------------------------------
+// JArithmeticDecoderStates
+//------------------------------------------------------------------------
+
+JArithmeticDecoderStats::JArithmeticDecoderStats(int contextSizeA) {
+ contextSize = contextSizeA;
+ cxTab = (Guchar *)gmallocn(contextSize, sizeof(Guchar));
+ reset();
+}
+
+JArithmeticDecoderStats::~JArithmeticDecoderStats() {
+ gfree(cxTab);
+}
+
+JArithmeticDecoderStats *JArithmeticDecoderStats::copy() {
+ JArithmeticDecoderStats *stats;
+
+ stats = new JArithmeticDecoderStats(contextSize);
+ memcpy(stats->cxTab, cxTab, contextSize);
+ return stats;
+}
+
+void JArithmeticDecoderStats::reset() {
+ memset(cxTab, 0, contextSize);
+}
+
+void JArithmeticDecoderStats::copyFrom(JArithmeticDecoderStats *stats) {
+ memcpy(cxTab, stats->cxTab, contextSize);
+}
+
+void JArithmeticDecoderStats::setEntry(Guint cx, int i, int mps) {
+ cxTab[cx] = (i << 1) + mps;
+}
+
+//------------------------------------------------------------------------
+// JArithmeticDecoder
+//------------------------------------------------------------------------
+
+Guint JArithmeticDecoder::qeTab[47] = {
+ 0x56010000, 0x34010000, 0x18010000, 0x0AC10000,
+ 0x05210000, 0x02210000, 0x56010000, 0x54010000,
+ 0x48010000, 0x38010000, 0x30010000, 0x24010000,
+ 0x1C010000, 0x16010000, 0x56010000, 0x54010000,
+ 0x51010000, 0x48010000, 0x38010000, 0x34010000,
+ 0x30010000, 0x28010000, 0x24010000, 0x22010000,
+ 0x1C010000, 0x18010000, 0x16010000, 0x14010000,
+ 0x12010000, 0x11010000, 0x0AC10000, 0x09C10000,
+ 0x08A10000, 0x05210000, 0x04410000, 0x02A10000,
+ 0x02210000, 0x01410000, 0x01110000, 0x00850000,
+ 0x00490000, 0x00250000, 0x00150000, 0x00090000,
+ 0x00050000, 0x00010000, 0x56010000
+};
+
+int JArithmeticDecoder::nmpsTab[47] = {
+ 1, 2, 3, 4, 5, 38, 7, 8, 9, 10, 11, 12, 13, 29, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 45, 46
+};
+
+int JArithmeticDecoder::nlpsTab[47] = {
+ 1, 6, 9, 12, 29, 33, 6, 14, 14, 14, 17, 18, 20, 21, 14, 14,
+ 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 46
+};
+
+int JArithmeticDecoder::switchTab[47] = {
+ 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+JArithmeticDecoder::JArithmeticDecoder() {
+ str = NULL;
+ dataLen = 0;
+ limitStream = gFalse;
+}
+
+inline Guint JArithmeticDecoder::readByte() {
+ if (limitStream) {
+ --dataLen;
+ if (dataLen < 0) {
+ return 0xff;
+ }
+ }
+ return (Guint)str->getChar() & 0xff;
+}
+
+JArithmeticDecoder::~JArithmeticDecoder() {
+ cleanup();
+}
+
+void JArithmeticDecoder::start() {
+ buf0 = readByte();
+ buf1 = readByte();
+
+ // INITDEC
+ c = (buf0 ^ 0xff) << 16;
+ byteIn();
+ c <<= 7;
+ ct -= 7;
+ a = 0x80000000;
+}
+
+void JArithmeticDecoder::restart(int dataLenA) {
+ int oldDataLen;
+
+ oldDataLen = dataLen;
+ dataLen = dataLenA;
+ if (oldDataLen == -1) {
+ buf1 = readByte();
+ } else if (oldDataLen <= -2) {
+ buf0 = readByte();
+ buf1 = readByte();
+ }
+}
+
+void JArithmeticDecoder::cleanup() {
+ if (limitStream) {
+ while (dataLen > 0) {
+ buf0 = buf1;
+ buf1 = readByte();
+ }
+ }
+}
+
+int JArithmeticDecoder::decodeBit(Guint context,
+ JArithmeticDecoderStats *stats) {
+ int bit;
+ Guint qe;
+ int iCX, mpsCX;
+
+ iCX = stats->cxTab[context] >> 1;
+ mpsCX = stats->cxTab[context] & 1;
+ qe = qeTab[iCX];
+ a -= qe;
+ if (c < a) {
+ if (a & 0x80000000) {
+ bit = mpsCX;
+ } else {
+ // MPS_EXCHANGE
+ if (a < qe) {
+ bit = 1 - mpsCX;
+ if (switchTab[iCX]) {
+ stats->cxTab[context] = (nlpsTab[iCX] << 1) | (1 - mpsCX);
+ } else {
+ stats->cxTab[context] = (nlpsTab[iCX] << 1) | mpsCX;
+ }
+ } else {
+ bit = mpsCX;
+ stats->cxTab[context] = (nmpsTab[iCX] << 1) | mpsCX;
+ }
+ // RENORMD
+ do {
+ if (ct == 0) {
+ byteIn();
+ }
+ a <<= 1;
+ c <<= 1;
+ --ct;
+ } while (!(a & 0x80000000));
+ }
+ } else {
+ c -= a;
+ // LPS_EXCHANGE
+ if (a < qe) {
+ bit = mpsCX;
+ stats->cxTab[context] = (nmpsTab[iCX] << 1) | mpsCX;
+ } else {
+ bit = 1 - mpsCX;
+ if (switchTab[iCX]) {
+ stats->cxTab[context] = (nlpsTab[iCX] << 1) | (1 - mpsCX);
+ } else {
+ stats->cxTab[context] = (nlpsTab[iCX] << 1) | mpsCX;
+ }
+ }
+ a = qe;
+ // RENORMD
+ do {
+ if (ct == 0) {
+ byteIn();
+ }
+ a <<= 1;
+ c <<= 1;
+ --ct;
+ } while (!(a & 0x80000000));
+ }
+ return bit;
+}
+
+int JArithmeticDecoder::decodeByte(Guint context,
+ JArithmeticDecoderStats *stats) {
+ int byte;
+ int i;
+
+ byte = 0;
+ for (i = 0; i < 8; ++i) {
+ byte = (byte << 1) | decodeBit(context, stats);
+ }
+ return byte;
+}
+
+GBool JArithmeticDecoder::decodeInt(int *x, JArithmeticDecoderStats *stats) {
+ int s;
+ Guint v;
+ int i;
+
+ prev = 1;
+ s = decodeIntBit(stats);
+ if (decodeIntBit(stats)) {
+ if (decodeIntBit(stats)) {
+ if (decodeIntBit(stats)) {
+ if (decodeIntBit(stats)) {
+ if (decodeIntBit(stats)) {
+ v = 0;
+ for (i = 0; i < 32; ++i) {
+ v = (v << 1) | decodeIntBit(stats);
+ }
+ v += 4436;
+ } else {
+ v = 0;
+ for (i = 0; i < 12; ++i) {
+ v = (v << 1) | decodeIntBit(stats);
+ }
+ v += 340;
+ }
+ } else {
+ v = 0;
+ for (i = 0; i < 8; ++i) {
+ v = (v << 1) | decodeIntBit(stats);
+ }
+ v += 84;
+ }
+ } else {
+ v = 0;
+ for (i = 0; i < 6; ++i) {
+ v = (v << 1) | decodeIntBit(stats);
+ }
+ v += 20;
+ }
+ } else {
+ v = decodeIntBit(stats);
+ v = (v << 1) | decodeIntBit(stats);
+ v = (v << 1) | decodeIntBit(stats);
+ v = (v << 1) | decodeIntBit(stats);
+ v += 4;
+ }
+ } else {
+ v = decodeIntBit(stats);
+ v = (v << 1) | decodeIntBit(stats);
+ }
+
+ if (s) {
+ if (v == 0) {
+ return gFalse;
+ }
+ *x = -(int)v;
+ } else {
+ *x = (int)v;
+ }
+ return gTrue;
+}
+
+int JArithmeticDecoder::decodeIntBit(JArithmeticDecoderStats *stats) {
+ int bit;
+
+ bit = decodeBit(prev, stats);
+ if (prev < 0x100) {
+ prev = (prev << 1) | bit;
+ } else {
+ prev = (((prev << 1) | bit) & 0x1ff) | 0x100;
+ }
+ return bit;
+}
+
+Guint JArithmeticDecoder::decodeIAID(Guint codeLen,
+ JArithmeticDecoderStats *stats) {
+ Guint i;
+ int bit;
+
+ prev = 1;
+ for (i = 0; i < codeLen; ++i) {
+ bit = decodeBit(prev, stats);
+ prev = (prev << 1) | bit;
+ }
+ return prev - (1 << codeLen);
+}
+
+void JArithmeticDecoder::byteIn() {
+ if (buf0 == 0xff) {
+ if (buf1 > 0x8f) {
+ ct = 8;
+ } else {
+ buf0 = buf1;
+ buf1 = readByte();
+ c = c + 0xfe00 - (buf0 << 9);
+ ct = 7;
+ }
+ } else {
+ buf0 = buf1;
+ buf1 = readByte();
+ c = c + 0xff00 - (buf0 << 8);
+ ct = 8;
+ }
+}
--- /dev/null
+//========================================================================
+//
+// JArithmeticDecoder.h
+//
+// Arithmetic decoder used by the JBIG2 and JPEG2000 decoders.
+//
+// Copyright 2002-2004 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef JARITHMETICDECODER_H
+#define JARITHMETICDECODER_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+class Stream;
+
+//------------------------------------------------------------------------
+// JArithmeticDecoderStats
+//------------------------------------------------------------------------
+
+class JArithmeticDecoderStats {
+public:
+
+ JArithmeticDecoderStats(int contextSizeA);
+ ~JArithmeticDecoderStats();
+ JArithmeticDecoderStats *copy();
+ void reset();
+ int getContextSize() { return contextSize; }
+ void copyFrom(JArithmeticDecoderStats *stats);
+ void setEntry(Guint cx, int i, int mps);
+
+private:
+
+ Guchar *cxTab; // cxTab[cx] = (i[cx] << 1) + mps[cx]
+ int contextSize;
+
+ friend class JArithmeticDecoder;
+};
+
+//------------------------------------------------------------------------
+// JArithmeticDecoder
+//------------------------------------------------------------------------
+
+class JArithmeticDecoder {
+public:
+
+ JArithmeticDecoder();
+ ~JArithmeticDecoder();
+
+ void setStream(Stream *strA)
+ { str = strA; dataLen = 0; limitStream = gFalse; }
+ void setStream(Stream *strA, int dataLenA)
+ { str = strA; dataLen = dataLenA; limitStream = gTrue; }
+
+ // Start decoding on a new stream. This fills the byte buffers and
+ // runs INITDEC.
+ void start();
+
+ // Restart decoding on an interrupted stream. This refills the
+ // buffers if needed, but does not run INITDEC. (This is used in
+ // JPEG 2000 streams when codeblock data is split across multiple
+ // packets/layers.)
+ void restart(int dataLenA);
+
+ // Read any leftover data in the stream.
+ void cleanup();
+
+ // Decode one bit.
+ int decodeBit(Guint context, JArithmeticDecoderStats *stats);
+
+ // Decode eight bits.
+ int decodeByte(Guint context, JArithmeticDecoderStats *stats);
+
+ // Returns false for OOB, otherwise sets *<x> and returns true.
+ GBool decodeInt(int *x, JArithmeticDecoderStats *stats);
+
+ Guint decodeIAID(Guint codeLen,
+ JArithmeticDecoderStats *stats);
+
+private:
+
+ Guint readByte();
+ int decodeIntBit(JArithmeticDecoderStats *stats);
+ void byteIn();
+
+ static Guint qeTab[47];
+ static int nmpsTab[47];
+ static int nlpsTab[47];
+ static int switchTab[47];
+
+ Guint buf0, buf1;
+ Guint c, a;
+ int ct;
+
+ Guint prev; // for the integer decoder
+
+ Stream *str;
+ int dataLen;
+ GBool limitStream;
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// JBIG2Stream.cc
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include "GList.h"
+#include "Error.h"
+#include "JArithmeticDecoder.h"
+#include "JBIG2Stream.h"
+
+//~ share these tables
+#include "Stream-CCITT.h"
+
+//------------------------------------------------------------------------
+
+static int contextSize[4] = { 16, 13, 10, 10 };
+static int refContextSize[2] = { 13, 10 };
+
+//------------------------------------------------------------------------
+// JBIG2HuffmanTable
+//------------------------------------------------------------------------
+
+#define jbig2HuffmanLOW 0xfffffffd
+#define jbig2HuffmanOOB 0xfffffffe
+#define jbig2HuffmanEOT 0xffffffff
+
+struct JBIG2HuffmanTable {
+ int val;
+ Guint prefixLen;
+ Guint rangeLen; // can also be LOW, OOB, or EOT
+ Guint prefix;
+};
+
+JBIG2HuffmanTable huffTableA[] = {
+ { 0, 1, 4, 0x000 },
+ { 16, 2, 8, 0x002 },
+ { 272, 3, 16, 0x006 },
+ { 65808, 3, 32, 0x007 },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableB[] = {
+ { 0, 1, 0, 0x000 },
+ { 1, 2, 0, 0x002 },
+ { 2, 3, 0, 0x006 },
+ { 3, 4, 3, 0x00e },
+ { 11, 5, 6, 0x01e },
+ { 75, 6, 32, 0x03e },
+ { 0, 6, jbig2HuffmanOOB, 0x03f },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableC[] = {
+ { 0, 1, 0, 0x000 },
+ { 1, 2, 0, 0x002 },
+ { 2, 3, 0, 0x006 },
+ { 3, 4, 3, 0x00e },
+ { 11, 5, 6, 0x01e },
+ { 0, 6, jbig2HuffmanOOB, 0x03e },
+ { 75, 7, 32, 0x0fe },
+ { -256, 8, 8, 0x0fe },
+ { -257, 8, jbig2HuffmanLOW, 0x0ff },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableD[] = {
+ { 1, 1, 0, 0x000 },
+ { 2, 2, 0, 0x002 },
+ { 3, 3, 0, 0x006 },
+ { 4, 4, 3, 0x00e },
+ { 12, 5, 6, 0x01e },
+ { 76, 5, 32, 0x01f },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableE[] = {
+ { 1, 1, 0, 0x000 },
+ { 2, 2, 0, 0x002 },
+ { 3, 3, 0, 0x006 },
+ { 4, 4, 3, 0x00e },
+ { 12, 5, 6, 0x01e },
+ { 76, 6, 32, 0x03e },
+ { -255, 7, 8, 0x07e },
+ { -256, 7, jbig2HuffmanLOW, 0x07f },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableF[] = {
+ { 0, 2, 7, 0x000 },
+ { 128, 3, 7, 0x002 },
+ { 256, 3, 8, 0x003 },
+ { -1024, 4, 9, 0x008 },
+ { -512, 4, 8, 0x009 },
+ { -256, 4, 7, 0x00a },
+ { -32, 4, 5, 0x00b },
+ { 512, 4, 9, 0x00c },
+ { 1024, 4, 10, 0x00d },
+ { -2048, 5, 10, 0x01c },
+ { -128, 5, 6, 0x01d },
+ { -64, 5, 5, 0x01e },
+ { -2049, 6, jbig2HuffmanLOW, 0x03e },
+ { 2048, 6, 32, 0x03f },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableG[] = {
+ { -512, 3, 8, 0x000 },
+ { 256, 3, 8, 0x001 },
+ { 512, 3, 9, 0x002 },
+ { 1024, 3, 10, 0x003 },
+ { -1024, 4, 9, 0x008 },
+ { -256, 4, 7, 0x009 },
+ { -32, 4, 5, 0x00a },
+ { 0, 4, 5, 0x00b },
+ { 128, 4, 7, 0x00c },
+ { -128, 5, 6, 0x01a },
+ { -64, 5, 5, 0x01b },
+ { 32, 5, 5, 0x01c },
+ { 64, 5, 6, 0x01d },
+ { -1025, 5, jbig2HuffmanLOW, 0x01e },
+ { 2048, 5, 32, 0x01f },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableH[] = {
+ { 0, 2, 1, 0x000 },
+ { 0, 2, jbig2HuffmanOOB, 0x001 },
+ { 4, 3, 4, 0x004 },
+ { -1, 4, 0, 0x00a },
+ { 22, 4, 4, 0x00b },
+ { 38, 4, 5, 0x00c },
+ { 2, 5, 0, 0x01a },
+ { 70, 5, 6, 0x01b },
+ { 134, 5, 7, 0x01c },
+ { 3, 6, 0, 0x03a },
+ { 20, 6, 1, 0x03b },
+ { 262, 6, 7, 0x03c },
+ { 646, 6, 10, 0x03d },
+ { -2, 7, 0, 0x07c },
+ { 390, 7, 8, 0x07d },
+ { -15, 8, 3, 0x0fc },
+ { -5, 8, 1, 0x0fd },
+ { -7, 9, 1, 0x1fc },
+ { -3, 9, 0, 0x1fd },
+ { -16, 9, jbig2HuffmanLOW, 0x1fe },
+ { 1670, 9, 32, 0x1ff },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableI[] = {
+ { 0, 2, jbig2HuffmanOOB, 0x000 },
+ { -1, 3, 1, 0x002 },
+ { 1, 3, 1, 0x003 },
+ { 7, 3, 5, 0x004 },
+ { -3, 4, 1, 0x00a },
+ { 43, 4, 5, 0x00b },
+ { 75, 4, 6, 0x00c },
+ { 3, 5, 1, 0x01a },
+ { 139, 5, 7, 0x01b },
+ { 267, 5, 8, 0x01c },
+ { 5, 6, 1, 0x03a },
+ { 39, 6, 2, 0x03b },
+ { 523, 6, 8, 0x03c },
+ { 1291, 6, 11, 0x03d },
+ { -5, 7, 1, 0x07c },
+ { 779, 7, 9, 0x07d },
+ { -31, 8, 4, 0x0fc },
+ { -11, 8, 2, 0x0fd },
+ { -15, 9, 2, 0x1fc },
+ { -7, 9, 1, 0x1fd },
+ { -32, 9, jbig2HuffmanLOW, 0x1fe },
+ { 3339, 9, 32, 0x1ff },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableJ[] = {
+ { -2, 2, 2, 0x000 },
+ { 6, 2, 6, 0x001 },
+ { 0, 2, jbig2HuffmanOOB, 0x002 },
+ { -3, 5, 0, 0x018 },
+ { 2, 5, 0, 0x019 },
+ { 70, 5, 5, 0x01a },
+ { 3, 6, 0, 0x036 },
+ { 102, 6, 5, 0x037 },
+ { 134, 6, 6, 0x038 },
+ { 198, 6, 7, 0x039 },
+ { 326, 6, 8, 0x03a },
+ { 582, 6, 9, 0x03b },
+ { 1094, 6, 10, 0x03c },
+ { -21, 7, 4, 0x07a },
+ { -4, 7, 0, 0x07b },
+ { 4, 7, 0, 0x07c },
+ { 2118, 7, 11, 0x07d },
+ { -5, 8, 0, 0x0fc },
+ { 5, 8, 0, 0x0fd },
+ { -22, 8, jbig2HuffmanLOW, 0x0fe },
+ { 4166, 8, 32, 0x0ff },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableK[] = {
+ { 1, 1, 0, 0x000 },
+ { 2, 2, 1, 0x002 },
+ { 4, 4, 0, 0x00c },
+ { 5, 4, 1, 0x00d },
+ { 7, 5, 1, 0x01c },
+ { 9, 5, 2, 0x01d },
+ { 13, 6, 2, 0x03c },
+ { 17, 7, 2, 0x07a },
+ { 21, 7, 3, 0x07b },
+ { 29, 7, 4, 0x07c },
+ { 45, 7, 5, 0x07d },
+ { 77, 7, 6, 0x07e },
+ { 141, 7, 32, 0x07f },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableL[] = {
+ { 1, 1, 0, 0x000 },
+ { 2, 2, 0, 0x002 },
+ { 3, 3, 1, 0x006 },
+ { 5, 5, 0, 0x01c },
+ { 6, 5, 1, 0x01d },
+ { 8, 6, 1, 0x03c },
+ { 10, 7, 0, 0x07a },
+ { 11, 7, 1, 0x07b },
+ { 13, 7, 2, 0x07c },
+ { 17, 7, 3, 0x07d },
+ { 25, 7, 4, 0x07e },
+ { 41, 8, 5, 0x0fe },
+ { 73, 8, 32, 0x0ff },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableM[] = {
+ { 1, 1, 0, 0x000 },
+ { 2, 3, 0, 0x004 },
+ { 7, 3, 3, 0x005 },
+ { 3, 4, 0, 0x00c },
+ { 5, 4, 1, 0x00d },
+ { 4, 5, 0, 0x01c },
+ { 15, 6, 1, 0x03a },
+ { 17, 6, 2, 0x03b },
+ { 21, 6, 3, 0x03c },
+ { 29, 6, 4, 0x03d },
+ { 45, 6, 5, 0x03e },
+ { 77, 7, 6, 0x07e },
+ { 141, 7, 32, 0x07f },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableN[] = {
+ { 0, 1, 0, 0x000 },
+ { -2, 3, 0, 0x004 },
+ { -1, 3, 0, 0x005 },
+ { 1, 3, 0, 0x006 },
+ { 2, 3, 0, 0x007 },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+JBIG2HuffmanTable huffTableO[] = {
+ { 0, 1, 0, 0x000 },
+ { -1, 3, 0, 0x004 },
+ { 1, 3, 0, 0x005 },
+ { -2, 4, 0, 0x00c },
+ { 2, 4, 0, 0x00d },
+ { -4, 5, 1, 0x01c },
+ { 3, 5, 1, 0x01d },
+ { -8, 6, 2, 0x03c },
+ { 5, 6, 2, 0x03d },
+ { -24, 7, 4, 0x07c },
+ { 9, 7, 4, 0x07d },
+ { -25, 7, jbig2HuffmanLOW, 0x07e },
+ { 25, 7, 32, 0x07f },
+ { 0, 0, jbig2HuffmanEOT, 0 }
+};
+
+//------------------------------------------------------------------------
+// JBIG2HuffmanDecoder
+//------------------------------------------------------------------------
+
+class JBIG2HuffmanDecoder {
+public:
+
+ JBIG2HuffmanDecoder();
+ ~JBIG2HuffmanDecoder();
+ void setStream(Stream *strA) { str = strA; }
+
+ void reset();
+
+ // Returns false for OOB, otherwise sets *<x> and returns true.
+ GBool decodeInt(int *x, JBIG2HuffmanTable *table);
+
+ Guint readBits(Guint n);
+ Guint readBit();
+
+ // Sort the table by prefix length and assign prefix values.
+ void buildTable(JBIG2HuffmanTable *table, Guint len);
+
+private:
+
+ Stream *str;
+ Guint buf;
+ Guint bufLen;
+};
+
+JBIG2HuffmanDecoder::JBIG2HuffmanDecoder() {
+ str = NULL;
+ reset();
+}
+
+JBIG2HuffmanDecoder::~JBIG2HuffmanDecoder() {
+}
+
+void JBIG2HuffmanDecoder::reset() {
+ buf = 0;
+ bufLen = 0;
+}
+
+//~ optimize this
+GBool JBIG2HuffmanDecoder::decodeInt(int *x, JBIG2HuffmanTable *table) {
+ Guint i, len, prefix;
+
+ i = 0;
+ len = 0;
+ prefix = 0;
+ while (table[i].rangeLen != jbig2HuffmanEOT) {
+ while (len < table[i].prefixLen) {
+ prefix = (prefix << 1) | readBit();
+ ++len;
+ }
+ if (prefix == table[i].prefix) {
+ if (table[i].rangeLen == jbig2HuffmanOOB) {
+ return gFalse;
+ }
+ if (table[i].rangeLen == jbig2HuffmanLOW) {
+ *x = table[i].val - readBits(32);
+ } else if (table[i].rangeLen > 0) {
+ *x = table[i].val + readBits(table[i].rangeLen);
+ } else {
+ *x = table[i].val;
+ }
+ return gTrue;
+ }
+ ++i;
+ }
+ return gFalse;
+}
+
+Guint JBIG2HuffmanDecoder::readBits(Guint n) {
+ Guint x, mask, nLeft;
+
+ mask = (n == 32) ? 0xffffffff : ((1 << n) - 1);
+ if (bufLen >= n) {
+ x = (buf >> (bufLen - n)) & mask;
+ bufLen -= n;
+ } else {
+ x = buf & ((1 << bufLen) - 1);
+ nLeft = n - bufLen;
+ bufLen = 0;
+ while (nLeft >= 8) {
+ x = (x << 8) | (str->getChar() & 0xff);
+ nLeft -= 8;
+ }
+ if (nLeft > 0) {
+ buf = str->getChar();
+ bufLen = 8 - nLeft;
+ x = (x << nLeft) | ((buf >> bufLen) & ((1 << nLeft) - 1));
+ }
+ }
+ return x;
+}
+
+Guint JBIG2HuffmanDecoder::readBit() {
+ if (bufLen == 0) {
+ buf = str->getChar();
+ bufLen = 8;
+ }
+ --bufLen;
+ return (buf >> bufLen) & 1;
+}
+
+void JBIG2HuffmanDecoder::buildTable(JBIG2HuffmanTable *table, Guint len) {
+ Guint i, j, k, prefix;
+ JBIG2HuffmanTable tab;
+
+ // stable selection sort:
+ // - entries with prefixLen > 0, in ascending prefixLen order
+ // - entry with prefixLen = 0, rangeLen = EOT
+ // - all other entries with prefixLen = 0
+ // (on entry, table[len] has prefixLen = 0, rangeLen = EOT)
+ for (i = 0; i < len; ++i) {
+ for (j = i; j < len && table[j].prefixLen == 0; ++j) ;
+ if (j == len) {
+ break;
+ }
+ for (k = j + 1; k < len; ++k) {
+ if (table[k].prefixLen > 0 &&
+ table[k].prefixLen < table[j].prefixLen) {
+ j = k;
+ }
+ }
+ if (j != i) {
+ tab = table[j];
+ for (k = j; k > i; --k) {
+ table[k] = table[k - 1];
+ }
+ table[i] = tab;
+ }
+ }
+ table[i] = table[len];
+
+ // assign prefixes
+ i = 0;
+ prefix = 0;
+ table[i++].prefix = prefix++;
+ for (; table[i].rangeLen != jbig2HuffmanEOT; ++i) {
+ prefix <<= table[i].prefixLen - table[i-1].prefixLen;
+ table[i].prefix = prefix++;
+ }
+}
+
+//------------------------------------------------------------------------
+// JBIG2MMRDecoder
+//------------------------------------------------------------------------
+
+class JBIG2MMRDecoder {
+public:
+
+ JBIG2MMRDecoder();
+ ~JBIG2MMRDecoder();
+ void setStream(Stream *strA) { str = strA; }
+ void reset();
+ int get2DCode();
+ int getBlackCode();
+ int getWhiteCode();
+ Guint get24Bits();
+ void skipTo(Guint length);
+
+private:
+
+ Stream *str;
+ Guint buf;
+ Guint bufLen;
+ Guint nBytesRead;
+};
+
+JBIG2MMRDecoder::JBIG2MMRDecoder() {
+ str = NULL;
+ reset();
+}
+
+JBIG2MMRDecoder::~JBIG2MMRDecoder() {
+}
+
+void JBIG2MMRDecoder::reset() {
+ buf = 0;
+ bufLen = 0;
+ nBytesRead = 0;
+}
+
+int JBIG2MMRDecoder::get2DCode() {
+ CCITTCode *p;
+
+ if (bufLen == 0) {
+ buf = str->getChar() & 0xff;
+ bufLen = 8;
+ ++nBytesRead;
+ p = &twoDimTab1[(buf >> 1) & 0x7f];
+ } else if (bufLen == 8) {
+ p = &twoDimTab1[(buf >> 1) & 0x7f];
+ } else {
+ p = &twoDimTab1[(buf << (7 - bufLen)) & 0x7f];
+ if (p->bits < 0 || p->bits > (int)bufLen) {
+ buf = (buf << 8) | (str->getChar() & 0xff);
+ bufLen += 8;
+ ++nBytesRead;
+ p = &twoDimTab1[(buf >> (bufLen - 7)) & 0x7f];
+ }
+ }
+ if (p->bits < 0) {
+ error(str->getPos(), "Bad two dim code in JBIG2 MMR stream");
+ return 0;
+ }
+ bufLen -= p->bits;
+ return p->n;
+}
+
+int JBIG2MMRDecoder::getWhiteCode() {
+ CCITTCode *p;
+ Guint code;
+
+ if (bufLen == 0) {
+ buf = str->getChar() & 0xff;
+ bufLen = 8;
+ ++nBytesRead;
+ }
+ while (1) {
+ if (bufLen >= 7 && ((buf >> (bufLen - 7)) & 0x7f) == 0) {
+ if (bufLen <= 12) {
+ code = buf << (12 - bufLen);
+ } else {
+ code = buf >> (bufLen - 12);
+ }
+ p = &whiteTab1[code & 0x1f];
+ } else {
+ if (bufLen <= 9) {
+ code = buf << (9 - bufLen);
+ } else {
+ code = buf >> (bufLen - 9);
+ }
+ p = &whiteTab2[code & 0x1ff];
+ }
+ if (p->bits > 0 && p->bits <= (int)bufLen) {
+ bufLen -= p->bits;
+ return p->n;
+ }
+ if (bufLen >= 12) {
+ break;
+ }
+ buf = (buf << 8) | (str->getChar() & 0xff);
+ bufLen += 8;
+ ++nBytesRead;
+ }
+ error(str->getPos(), "Bad white code in JBIG2 MMR stream");
+ // eat a bit and return a positive number so that the caller doesn't
+ // go into an infinite loop
+ --bufLen;
+ return 1;
+}
+
+int JBIG2MMRDecoder::getBlackCode() {
+ CCITTCode *p;
+ Guint code;
+
+ if (bufLen == 0) {
+ buf = str->getChar() & 0xff;
+ bufLen = 8;
+ ++nBytesRead;
+ }
+ while (1) {
+ if (bufLen >= 6 && ((buf >> (bufLen - 6)) & 0x3f) == 0) {
+ if (bufLen <= 13) {
+ code = buf << (13 - bufLen);
+ } else {
+ code = buf >> (bufLen - 13);
+ }
+ p = &blackTab1[code & 0x7f];
+ } else if (bufLen >= 4 && ((buf >> (bufLen - 4)) & 0x0f) == 0) {
+ if (bufLen <= 12) {
+ code = buf << (12 - bufLen);
+ } else {
+ code = buf >> (bufLen - 12);
+ }
+ p = &blackTab2[(code & 0xff) - 64];
+ } else {
+ if (bufLen <= 6) {
+ code = buf << (6 - bufLen);
+ } else {
+ code = buf >> (bufLen - 6);
+ }
+ p = &blackTab3[code & 0x3f];
+ }
+ if (p->bits > 0 && p->bits <= (int)bufLen) {
+ bufLen -= p->bits;
+ return p->n;
+ }
+ if (bufLen >= 13) {
+ break;
+ }
+ buf = (buf << 8) | (str->getChar() & 0xff);
+ bufLen += 8;
+ ++nBytesRead;
+ }
+ error(str->getPos(), "Bad black code in JBIG2 MMR stream");
+ // eat a bit and return a positive number so that the caller doesn't
+ // go into an infinite loop
+ --bufLen;
+ return 1;
+}
+
+Guint JBIG2MMRDecoder::get24Bits() {
+ while (bufLen < 24) {
+ buf = (buf << 8) | (str->getChar() & 0xff);
+ bufLen += 8;
+ ++nBytesRead;
+ }
+ return (buf >> (bufLen - 24)) & 0xffffff;
+}
+
+void JBIG2MMRDecoder::skipTo(Guint length) {
+ while (nBytesRead < length) {
+ str->getChar();
+ ++nBytesRead;
+ }
+}
+
+//------------------------------------------------------------------------
+// JBIG2Segment
+//------------------------------------------------------------------------
+
+enum JBIG2SegmentType {
+ jbig2SegBitmap,
+ jbig2SegSymbolDict,
+ jbig2SegPatternDict,
+ jbig2SegCodeTable
+};
+
+class JBIG2Segment {
+public:
+
+ JBIG2Segment(Guint segNumA) { segNum = segNumA; }
+ virtual ~JBIG2Segment() {}
+ void setSegNum(Guint segNumA) { segNum = segNumA; }
+ Guint getSegNum() { return segNum; }
+ virtual JBIG2SegmentType getType() = 0;
+
+private:
+
+ Guint segNum;
+};
+
+//------------------------------------------------------------------------
+// JBIG2Bitmap
+//------------------------------------------------------------------------
+
+struct JBIG2BitmapPtr {
+ Guchar *p;
+ int shift;
+ int x;
+};
+
+class JBIG2Bitmap: public JBIG2Segment {
+public:
+
+ JBIG2Bitmap(Guint segNumA, int wA, int hA);
+ virtual ~JBIG2Bitmap();
+ virtual JBIG2SegmentType getType() { return jbig2SegBitmap; }
+ JBIG2Bitmap *copy() { return new JBIG2Bitmap(0, this); }
+ JBIG2Bitmap *getSlice(Guint x, Guint y, Guint wA, Guint hA);
+ void expand(int newH, Guint pixel);
+ void clearToZero();
+ void clearToOne();
+ int getWidth() { return w; }
+ int getHeight() { return h; }
+ int getPixel(int x, int y)
+ { return (x < 0 || x >= w || y < 0 || y >= h) ? 0 :
+ (data[y * line + (x >> 3)] >> (7 - (x & 7))) & 1; }
+ void setPixel(int x, int y)
+ { data[y * line + (x >> 3)] |= 1 << (7 - (x & 7)); }
+ void clearPixel(int x, int y)
+ { data[y * line + (x >> 3)] &= 0x7f7f >> (x & 7); }
+ void getPixelPtr(int x, int y, JBIG2BitmapPtr *ptr);
+ int nextPixel(JBIG2BitmapPtr *ptr);
+ void duplicateRow(int yDest, int ySrc);
+ void combine(JBIG2Bitmap *bitmap, int x, int y, Guint combOp);
+ Guchar *getDataPtr() { return data; }
+ int getDataSize() { return h * line; }
+
+private:
+
+ JBIG2Bitmap(Guint segNumA, JBIG2Bitmap *bitmap);
+
+ int w, h, line;
+ Guchar *data;
+};
+
+JBIG2Bitmap::JBIG2Bitmap(Guint segNumA, int wA, int hA):
+ JBIG2Segment(segNumA)
+{
+ w = wA;
+ h = hA;
+ line = (wA + 7) >> 3;
+ // need to allocate one extra guard byte for use in combine()
+ data = (Guchar *)gmalloc(h * line + 1);
+ data[h * line] = 0;
+}
+
+JBIG2Bitmap::JBIG2Bitmap(Guint segNumA, JBIG2Bitmap *bitmap):
+ JBIG2Segment(segNumA)
+{
+ w = bitmap->w;
+ h = bitmap->h;
+ line = bitmap->line;
+ // need to allocate one extra guard byte for use in combine()
+ data = (Guchar *)gmalloc(h * line + 1);
+ memcpy(data, bitmap->data, h * line);
+ data[h * line] = 0;
+}
+
+JBIG2Bitmap::~JBIG2Bitmap() {
+ gfree(data);
+}
+
+//~ optimize this
+JBIG2Bitmap *JBIG2Bitmap::getSlice(Guint x, Guint y, Guint wA, Guint hA) {
+ JBIG2Bitmap *slice;
+ Guint xx, yy;
+
+ slice = new JBIG2Bitmap(0, wA, hA);
+ slice->clearToZero();
+ for (yy = 0; yy < hA; ++yy) {
+ for (xx = 0; xx < wA; ++xx) {
+ if (getPixel(x + xx, y + yy)) {
+ slice->setPixel(xx, yy);
+ }
+ }
+ }
+ return slice;
+}
+
+void JBIG2Bitmap::expand(int newH, Guint pixel) {
+ if (newH <= h) {
+ return;
+ }
+ // need to allocate one extra guard byte for use in combine()
+ data = (Guchar *)grealloc(data, newH * line + 1);
+ if (pixel) {
+ memset(data + h * line, 0xff, (newH - h) * line);
+ } else {
+ memset(data + h * line, 0x00, (newH - h) * line);
+ }
+ h = newH;
+ data[h * line] = 0;
+}
+
+void JBIG2Bitmap::clearToZero() {
+ memset(data, 0, h * line);
+}
+
+void JBIG2Bitmap::clearToOne() {
+ memset(data, 0xff, h * line);
+}
+
+inline void JBIG2Bitmap::getPixelPtr(int x, int y, JBIG2BitmapPtr *ptr) {
+ if (y < 0 || y >= h || x >= w) {
+ ptr->p = NULL;
+ } else if (x < 0) {
+ ptr->p = &data[y * line];
+ ptr->shift = 7;
+ ptr->x = x;
+ } else {
+ ptr->p = &data[y * line + (x >> 3)];
+ ptr->shift = 7 - (x & 7);
+ ptr->x = x;
+ }
+}
+
+inline int JBIG2Bitmap::nextPixel(JBIG2BitmapPtr *ptr) {
+ int pix;
+
+ if (!ptr->p) {
+ pix = 0;
+ } else if (ptr->x < 0) {
+ ++ptr->x;
+ pix = 0;
+ } else {
+ pix = (*ptr->p >> ptr->shift) & 1;
+ if (++ptr->x == w) {
+ ptr->p = NULL;
+ } else if (ptr->shift == 0) {
+ ++ptr->p;
+ ptr->shift = 7;
+ } else {
+ --ptr->shift;
+ }
+ }
+ return pix;
+}
+
+void JBIG2Bitmap::duplicateRow(int yDest, int ySrc) {
+ memcpy(data + yDest * line, data + ySrc * line, line);
+}
+
+void JBIG2Bitmap::combine(JBIG2Bitmap *bitmap, int x, int y,
+ Guint combOp) {
+ int x0, x1, y0, y1, xx, yy;
+ Guchar *srcPtr, *destPtr;
+ Guint src0, src1, src, dest, s1, s2, m1, m2, m3;
+ GBool oneByte;
+
+ if (y < 0) {
+ y0 = -y;
+ } else {
+ y0 = 0;
+ }
+ if (y + bitmap->h > h) {
+ y1 = h - y;
+ } else {
+ y1 = bitmap->h;
+ }
+ if (y0 >= y1) {
+ return;
+ }
+
+ if (x >= 0) {
+ x0 = x & ~7;
+ } else {
+ x0 = 0;
+ }
+ x1 = x + bitmap->w;
+ if (x1 > w) {
+ x1 = w;
+ }
+ if (x0 >= x1) {
+ return;
+ }
+
+ s1 = x & 7;
+ s2 = 8 - s1;
+ m1 = 0xff >> (x1 & 7);
+ m2 = 0xff << (((x1 & 7) == 0) ? 0 : 8 - (x1 & 7));
+ m3 = (0xff >> s1) & m2;
+
+ oneByte = x0 == ((x1 - 1) & ~7);
+
+ for (yy = y0; yy < y1; ++yy) {
+
+ // one byte per line -- need to mask both left and right side
+ if (oneByte) {
+ if (x >= 0) {
+ destPtr = data + (y + yy) * line + (x >> 3);
+ srcPtr = bitmap->data + yy * bitmap->line;
+ dest = *destPtr;
+ src1 = *srcPtr;
+ switch (combOp) {
+ case 0: // or
+ dest |= (src1 >> s1) & m2;
+ break;
+ case 1: // and
+ dest &= ((0xff00 | src1) >> s1) | m1;
+ break;
+ case 2: // xor
+ dest ^= (src1 >> s1) & m2;
+ break;
+ case 3: // xnor
+ dest ^= ((src1 ^ 0xff) >> s1) & m2;
+ break;
+ case 4: // replace
+ dest = (dest & ~m3) | ((src1 >> s1) & m3);
+ break;
+ }
+ *destPtr = dest;
+ } else {
+ destPtr = data + (y + yy) * line;
+ srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3);
+ dest = *destPtr;
+ src1 = *srcPtr;
+ switch (combOp) {
+ case 0: // or
+ dest |= src1 & m2;
+ break;
+ case 1: // and
+ dest &= src1 | m1;
+ break;
+ case 2: // xor
+ dest ^= src1 & m2;
+ break;
+ case 3: // xnor
+ dest ^= (src1 ^ 0xff) & m2;
+ break;
+ case 4: // replace
+ dest = (src1 & m2) | (dest & m1);
+ break;
+ }
+ *destPtr = dest;
+ }
+
+ // multiple bytes per line -- need to mask left side of left-most
+ // byte and right side of right-most byte
+ } else {
+
+ // left-most byte
+ if (x >= 0) {
+ destPtr = data + (y + yy) * line + (x >> 3);
+ srcPtr = bitmap->data + yy * bitmap->line;
+ src1 = *srcPtr++;
+ dest = *destPtr;
+ switch (combOp) {
+ case 0: // or
+ dest |= src1 >> s1;
+ break;
+ case 1: // and
+ dest &= (0xff00 | src1) >> s1;
+ break;
+ case 2: // xor
+ dest ^= src1 >> s1;
+ break;
+ case 3: // xnor
+ dest ^= (src1 ^ 0xff) >> s1;
+ break;
+ case 4: // replace
+ dest = (dest & (0xff << s2)) | (src1 >> s1);
+ break;
+ }
+ *destPtr++ = dest;
+ xx = x0 + 8;
+ } else {
+ destPtr = data + (y + yy) * line;
+ srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3);
+ src1 = *srcPtr++;
+ xx = x0;
+ }
+
+ // middle bytes
+ for (; xx < x1 - 8; xx += 8) {
+ dest = *destPtr;
+ src0 = src1;
+ src1 = *srcPtr++;
+ src = (((src0 << 8) | src1) >> s1) & 0xff;
+ switch (combOp) {
+ case 0: // or
+ dest |= src;
+ break;
+ case 1: // and
+ dest &= src;
+ break;
+ case 2: // xor
+ dest ^= src;
+ break;
+ case 3: // xnor
+ dest ^= src ^ 0xff;
+ break;
+ case 4: // replace
+ dest = src;
+ break;
+ }
+ *destPtr++ = dest;
+ }
+
+ // right-most byte
+ // note: this last byte (src1) may not actually be used, depending
+ // on the values of s1, m1, and m2 - and in fact, it may be off
+ // the edge of the source bitmap, which means we need to allocate
+ // one extra guard byte at the end of each bitmap
+ dest = *destPtr;
+ src0 = src1;
+ src1 = *srcPtr++;
+ src = (((src0 << 8) | src1) >> s1) & 0xff;
+ switch (combOp) {
+ case 0: // or
+ dest |= src & m2;
+ break;
+ case 1: // and
+ dest &= src | m1;
+ break;
+ case 2: // xor
+ dest ^= src & m2;
+ break;
+ case 3: // xnor
+ dest ^= (src ^ 0xff) & m2;
+ break;
+ case 4: // replace
+ dest = (src & m2) | (dest & m1);
+ break;
+ }
+ *destPtr = dest;
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+// JBIG2SymbolDict
+//------------------------------------------------------------------------
+
+class JBIG2SymbolDict: public JBIG2Segment {
+public:
+
+ JBIG2SymbolDict(Guint segNumA, Guint sizeA);
+ virtual ~JBIG2SymbolDict();
+ virtual JBIG2SegmentType getType() { return jbig2SegSymbolDict; }
+ Guint getSize() { return size; }
+ void setBitmap(Guint idx, JBIG2Bitmap *bitmap) { bitmaps[idx] = bitmap; }
+ JBIG2Bitmap *getBitmap(Guint idx) { return bitmaps[idx]; }
+ void setGenericRegionStats(JArithmeticDecoderStats *stats)
+ { genericRegionStats = stats; }
+ void setRefinementRegionStats(JArithmeticDecoderStats *stats)
+ { refinementRegionStats = stats; }
+ JArithmeticDecoderStats *getGenericRegionStats()
+ { return genericRegionStats; }
+ JArithmeticDecoderStats *getRefinementRegionStats()
+ { return refinementRegionStats; }
+
+private:
+
+ Guint size;
+ JBIG2Bitmap **bitmaps;
+ JArithmeticDecoderStats *genericRegionStats;
+ JArithmeticDecoderStats *refinementRegionStats;
+};
+
+JBIG2SymbolDict::JBIG2SymbolDict(Guint segNumA, Guint sizeA):
+ JBIG2Segment(segNumA)
+{
+ size = sizeA;
+ bitmaps = (JBIG2Bitmap **)gmallocn(size, sizeof(JBIG2Bitmap *));
+ genericRegionStats = NULL;
+ refinementRegionStats = NULL;
+}
+
+JBIG2SymbolDict::~JBIG2SymbolDict() {
+ Guint i;
+
+ for (i = 0; i < size; ++i) {
+ delete bitmaps[i];
+ }
+ gfree(bitmaps);
+ if (genericRegionStats) {
+ delete genericRegionStats;
+ }
+ if (refinementRegionStats) {
+ delete refinementRegionStats;
+ }
+}
+
+//------------------------------------------------------------------------
+// JBIG2PatternDict
+//------------------------------------------------------------------------
+
+class JBIG2PatternDict: public JBIG2Segment {
+public:
+
+ JBIG2PatternDict(Guint segNumA, Guint sizeA);
+ virtual ~JBIG2PatternDict();
+ virtual JBIG2SegmentType getType() { return jbig2SegPatternDict; }
+ Guint getSize() { return size; }
+ void setBitmap(Guint idx, JBIG2Bitmap *bitmap) { bitmaps[idx] = bitmap; }
+ JBIG2Bitmap *getBitmap(Guint idx) { return bitmaps[idx]; }
+
+private:
+
+ Guint size;
+ JBIG2Bitmap **bitmaps;
+};
+
+JBIG2PatternDict::JBIG2PatternDict(Guint segNumA, Guint sizeA):
+ JBIG2Segment(segNumA)
+{
+ size = sizeA;
+ bitmaps = (JBIG2Bitmap **)gmallocn(size, sizeof(JBIG2Bitmap *));
+}
+
+JBIG2PatternDict::~JBIG2PatternDict() {
+ Guint i;
+
+ for (i = 0; i < size; ++i) {
+ delete bitmaps[i];
+ }
+ gfree(bitmaps);
+}
+
+//------------------------------------------------------------------------
+// JBIG2CodeTable
+//------------------------------------------------------------------------
+
+class JBIG2CodeTable: public JBIG2Segment {
+public:
+
+ JBIG2CodeTable(Guint segNumA, JBIG2HuffmanTable *tableA);
+ virtual ~JBIG2CodeTable();
+ virtual JBIG2SegmentType getType() { return jbig2SegCodeTable; }
+ JBIG2HuffmanTable *getHuffTable() { return table; }
+
+private:
+
+ JBIG2HuffmanTable *table;
+};
+
+JBIG2CodeTable::JBIG2CodeTable(Guint segNumA, JBIG2HuffmanTable *tableA):
+ JBIG2Segment(segNumA)
+{
+ table = tableA;
+}
+
+JBIG2CodeTable::~JBIG2CodeTable() {
+ gfree(table);
+}
+
+//------------------------------------------------------------------------
+// JBIG2Stream
+//------------------------------------------------------------------------
+
+JBIG2Stream::JBIG2Stream(Stream *strA, Object *globalsStream):
+ FilterStream(strA)
+{
+ pageBitmap = NULL;
+
+ arithDecoder = new JArithmeticDecoder();
+ genericRegionStats = new JArithmeticDecoderStats(1 << 1);
+ refinementRegionStats = new JArithmeticDecoderStats(1 << 1);
+ iadhStats = new JArithmeticDecoderStats(1 << 9);
+ iadwStats = new JArithmeticDecoderStats(1 << 9);
+ iaexStats = new JArithmeticDecoderStats(1 << 9);
+ iaaiStats = new JArithmeticDecoderStats(1 << 9);
+ iadtStats = new JArithmeticDecoderStats(1 << 9);
+ iaitStats = new JArithmeticDecoderStats(1 << 9);
+ iafsStats = new JArithmeticDecoderStats(1 << 9);
+ iadsStats = new JArithmeticDecoderStats(1 << 9);
+ iardxStats = new JArithmeticDecoderStats(1 << 9);
+ iardyStats = new JArithmeticDecoderStats(1 << 9);
+ iardwStats = new JArithmeticDecoderStats(1 << 9);
+ iardhStats = new JArithmeticDecoderStats(1 << 9);
+ iariStats = new JArithmeticDecoderStats(1 << 9);
+ iaidStats = new JArithmeticDecoderStats(1 << 1);
+ huffDecoder = new JBIG2HuffmanDecoder();
+ mmrDecoder = new JBIG2MMRDecoder();
+
+ segments = globalSegments = new GList();
+ if (globalsStream->isStream()) {
+ curStr = globalsStream->getStream();
+ curStr->reset();
+ arithDecoder->setStream(curStr);
+ huffDecoder->setStream(curStr);
+ mmrDecoder->setStream(curStr);
+ readSegments();
+ }
+
+ segments = NULL;
+ curStr = NULL;
+ dataPtr = dataEnd = NULL;
+}
+
+JBIG2Stream::~JBIG2Stream() {
+ delete arithDecoder;
+ delete genericRegionStats;
+ delete refinementRegionStats;
+ delete iadhStats;
+ delete iadwStats;
+ delete iaexStats;
+ delete iaaiStats;
+ delete iadtStats;
+ delete iaitStats;
+ delete iafsStats;
+ delete iadsStats;
+ delete iardxStats;
+ delete iardyStats;
+ delete iardwStats;
+ delete iardhStats;
+ delete iariStats;
+ delete iaidStats;
+ delete huffDecoder;
+ delete mmrDecoder;
+ if (pageBitmap) {
+ delete pageBitmap;
+ }
+ if (segments) {
+ deleteGList(segments, JBIG2Segment);
+ }
+ if (globalSegments) {
+ deleteGList(globalSegments, JBIG2Segment);
+ }
+ delete str;
+}
+
+void JBIG2Stream::reset() {
+ if (pageBitmap) {
+ delete pageBitmap;
+ pageBitmap = NULL;
+ }
+ if (segments) {
+ deleteGList(segments, JBIG2Segment);
+ }
+ segments = new GList();
+
+ curStr = str;
+ curStr->reset();
+ arithDecoder->setStream(curStr);
+ huffDecoder->setStream(curStr);
+ mmrDecoder->setStream(curStr);
+ readSegments();
+
+ if (pageBitmap) {
+ dataPtr = pageBitmap->getDataPtr();
+ dataEnd = dataPtr + pageBitmap->getDataSize();
+ } else {
+ dataPtr = NULL;
+ }
+}
+
+int JBIG2Stream::getChar() {
+ if (dataPtr && dataPtr < dataEnd) {
+ return (*dataPtr++ ^ 0xff) & 0xff;
+ }
+ return EOF;
+}
+
+int JBIG2Stream::lookChar() {
+ if (dataPtr && dataPtr < dataEnd) {
+ return (*dataPtr ^ 0xff) & 0xff;
+ }
+ return EOF;
+}
+
+GString *JBIG2Stream::getPSFilter(int psLevel, char *indent) {
+ return NULL;
+}
+
+GBool JBIG2Stream::isBinary(GBool last) {
+ return str->isBinary(gTrue);
+}
+
+void JBIG2Stream::readSegments() {
+ Guint segNum, segFlags, segType, page, segLength;
+ Guint refFlags, nRefSegs;
+ Guint *refSegs;
+ int c1, c2, c3;
+ Guint i;
+
+ while (readULong(&segNum)) {
+
+ // segment header flags
+ if (!readUByte(&segFlags)) {
+ goto eofError1;
+ }
+ segType = segFlags & 0x3f;
+
+ // referred-to segment count and retention flags
+ if (!readUByte(&refFlags)) {
+ goto eofError1;
+ }
+ nRefSegs = refFlags >> 5;
+ if (nRefSegs == 7) {
+ if ((c1 = curStr->getChar()) == EOF ||
+ (c2 = curStr->getChar()) == EOF ||
+ (c3 = curStr->getChar()) == EOF) {
+ goto eofError1;
+ }
+ refFlags = (refFlags << 24) | (c1 << 16) | (c2 << 8) | c3;
+ nRefSegs = refFlags & 0x1fffffff;
+ for (i = 0; i < (nRefSegs + 9) >> 3; ++i) {
+ c1 = curStr->getChar();
+ }
+ }
+
+ // referred-to segment numbers
+ refSegs = (Guint *)gmallocn(nRefSegs, sizeof(Guint));
+ if (segNum <= 256) {
+ for (i = 0; i < nRefSegs; ++i) {
+ if (!readUByte(&refSegs[i])) {
+ goto eofError2;
+ }
+ }
+ } else if (segNum <= 65536) {
+ for (i = 0; i < nRefSegs; ++i) {
+ if (!readUWord(&refSegs[i])) {
+ goto eofError2;
+ }
+ }
+ } else {
+ for (i = 0; i < nRefSegs; ++i) {
+ if (!readULong(&refSegs[i])) {
+ goto eofError2;
+ }
+ }
+ }
+
+ // segment page association
+ if (segFlags & 0x40) {
+ if (!readULong(&page)) {
+ goto eofError2;
+ }
+ } else {
+ if (!readUByte(&page)) {
+ goto eofError2;
+ }
+ }
+
+ // segment data length
+ if (!readULong(&segLength)) {
+ goto eofError2;
+ }
+
+ // read the segment data
+ switch (segType) {
+ case 0:
+ if (!readSymbolDictSeg(segNum, segLength, refSegs, nRefSegs)) {
+ goto syntaxError;
+ }
+ break;
+ case 4:
+ readTextRegionSeg(segNum, gFalse, gFalse, segLength, refSegs, nRefSegs);
+ break;
+ case 6:
+ readTextRegionSeg(segNum, gTrue, gFalse, segLength, refSegs, nRefSegs);
+ break;
+ case 7:
+ readTextRegionSeg(segNum, gTrue, gTrue, segLength, refSegs, nRefSegs);
+ break;
+ case 16:
+ readPatternDictSeg(segNum, segLength);
+ break;
+ case 20:
+ readHalftoneRegionSeg(segNum, gFalse, gFalse, segLength,
+ refSegs, nRefSegs);
+ break;
+ case 22:
+ readHalftoneRegionSeg(segNum, gTrue, gFalse, segLength,
+ refSegs, nRefSegs);
+ break;
+ case 23:
+ readHalftoneRegionSeg(segNum, gTrue, gTrue, segLength,
+ refSegs, nRefSegs);
+ break;
+ case 36:
+ readGenericRegionSeg(segNum, gFalse, gFalse, segLength);
+ break;
+ case 38:
+ readGenericRegionSeg(segNum, gTrue, gFalse, segLength);
+ break;
+ case 39:
+ readGenericRegionSeg(segNum, gTrue, gTrue, segLength);
+ break;
+ case 40:
+ readGenericRefinementRegionSeg(segNum, gFalse, gFalse, segLength,
+ refSegs, nRefSegs);
+ break;
+ case 42:
+ readGenericRefinementRegionSeg(segNum, gTrue, gFalse, segLength,
+ refSegs, nRefSegs);
+ break;
+ case 43:
+ readGenericRefinementRegionSeg(segNum, gTrue, gTrue, segLength,
+ refSegs, nRefSegs);
+ break;
+ case 48:
+ readPageInfoSeg(segLength);
+ break;
+ case 50:
+ readEndOfStripeSeg(segLength);
+ break;
+ case 52:
+ readProfilesSeg(segLength);
+ break;
+ case 53:
+ readCodeTableSeg(segNum, segLength);
+ break;
+ case 62:
+ readExtensionSeg(segLength);
+ break;
+ default:
+ error(getPos(), "Unknown segment type in JBIG2 stream");
+ for (i = 0; i < segLength; ++i) {
+ if ((c1 = curStr->getChar()) == EOF) {
+ goto eofError2;
+ }
+ }
+ break;
+ }
+
+ gfree(refSegs);
+ }
+
+ return;
+
+ syntaxError:
+ gfree(refSegs);
+ return;
+
+ eofError2:
+ gfree(refSegs);
+ eofError1:
+ error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+GBool JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length,
+ Guint *refSegs, Guint nRefSegs) {
+ JBIG2SymbolDict *symbolDict;
+ JBIG2HuffmanTable *huffDHTable, *huffDWTable;
+ JBIG2HuffmanTable *huffBMSizeTable, *huffAggInstTable;
+ JBIG2Segment *seg;
+ GList *codeTables;
+ JBIG2SymbolDict *inputSymbolDict;
+ Guint flags, sdTemplate, sdrTemplate, huff, refAgg;
+ Guint huffDH, huffDW, huffBMSize, huffAggInst;
+ Guint contextUsed, contextRetained;
+ int sdATX[4], sdATY[4], sdrATX[2], sdrATY[2];
+ Guint numExSyms, numNewSyms, numInputSyms, symCodeLen;
+ JBIG2Bitmap **bitmaps;
+ JBIG2Bitmap *collBitmap, *refBitmap;
+ Guint *symWidths;
+ Guint symHeight, symWidth, totalWidth, x, symID;
+ int dh, dw, refAggNum, refDX, refDY, bmSize;
+ GBool ex;
+ int run, cnt;
+ Guint i, j, k;
+ Guchar *p;
+
+ // symbol dictionary flags
+ if (!readUWord(&flags)) {
+ goto eofError;
+ }
+ sdTemplate = (flags >> 10) & 3;
+ sdrTemplate = (flags >> 12) & 1;
+ huff = flags & 1;
+ refAgg = (flags >> 1) & 1;
+ huffDH = (flags >> 2) & 3;
+ huffDW = (flags >> 4) & 3;
+ huffBMSize = (flags >> 6) & 1;
+ huffAggInst = (flags >> 7) & 1;
+ contextUsed = (flags >> 8) & 1;
+ contextRetained = (flags >> 9) & 1;
+
+ // symbol dictionary AT flags
+ if (!huff) {
+ if (sdTemplate == 0) {
+ if (!readByte(&sdATX[0]) ||
+ !readByte(&sdATY[0]) ||
+ !readByte(&sdATX[1]) ||
+ !readByte(&sdATY[1]) ||
+ !readByte(&sdATX[2]) ||
+ !readByte(&sdATY[2]) ||
+ !readByte(&sdATX[3]) ||
+ !readByte(&sdATY[3])) {
+ goto eofError;
+ }
+ } else {
+ if (!readByte(&sdATX[0]) ||
+ !readByte(&sdATY[0])) {
+ goto eofError;
+ }
+ }
+ }
+
+ // symbol dictionary refinement AT flags
+ if (refAgg && !sdrTemplate) {
+ if (!readByte(&sdrATX[0]) ||
+ !readByte(&sdrATY[0]) ||
+ !readByte(&sdrATX[1]) ||
+ !readByte(&sdrATY[1])) {
+ goto eofError;
+ }
+ }
+
+ // SDNUMEXSYMS and SDNUMNEWSYMS
+ if (!readULong(&numExSyms) || !readULong(&numNewSyms)) {
+ goto eofError;
+ }
+
+ // get referenced segments: input symbol dictionaries and code tables
+ codeTables = new GList();
+ numInputSyms = 0;
+ for (i = 0; i < nRefSegs; ++i) {
+ seg = findSegment(refSegs[i]);
+ if (seg->getType() == jbig2SegSymbolDict) {
+ numInputSyms += ((JBIG2SymbolDict *)seg)->getSize();
+ } else if (seg->getType() == jbig2SegCodeTable) {
+ codeTables->append(seg);
+ }
+ }
+
+ // compute symbol code length
+ symCodeLen = 0;
+ i = 1;
+ while (i < numInputSyms + numNewSyms) {
+ ++symCodeLen;
+ i <<= 1;
+ }
+
+ // get the input symbol bitmaps
+ bitmaps = (JBIG2Bitmap **)gmallocn(numInputSyms + numNewSyms,
+ sizeof(JBIG2Bitmap *));
+ for (i = 0; i < numInputSyms + numNewSyms; ++i) {
+ bitmaps[i] = NULL;
+ }
+ k = 0;
+ inputSymbolDict = NULL;
+ for (i = 0; i < nRefSegs; ++i) {
+ seg = findSegment(refSegs[i]);
+ if (seg->getType() == jbig2SegSymbolDict) {
+ inputSymbolDict = (JBIG2SymbolDict *)seg;
+ for (j = 0; j < inputSymbolDict->getSize(); ++j) {
+ bitmaps[k++] = inputSymbolDict->getBitmap(j);
+ }
+ }
+ }
+
+ // get the Huffman tables
+ huffDHTable = huffDWTable = NULL; // make gcc happy
+ huffBMSizeTable = huffAggInstTable = NULL; // make gcc happy
+ i = 0;
+ if (huff) {
+ if (huffDH == 0) {
+ huffDHTable = huffTableD;
+ } else if (huffDH == 1) {
+ huffDHTable = huffTableE;
+ } else {
+ huffDHTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ if (huffDW == 0) {
+ huffDWTable = huffTableB;
+ } else if (huffDW == 1) {
+ huffDWTable = huffTableC;
+ } else {
+ huffDWTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ if (huffBMSize == 0) {
+ huffBMSizeTable = huffTableA;
+ } else {
+ huffBMSizeTable =
+ ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ if (huffAggInst == 0) {
+ huffAggInstTable = huffTableA;
+ } else {
+ huffAggInstTable =
+ ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ }
+ delete codeTables;
+
+ // set up the Huffman decoder
+ if (huff) {
+ huffDecoder->reset();
+
+ // set up the arithmetic decoder
+ } else {
+ if (contextUsed && inputSymbolDict) {
+ resetGenericStats(sdTemplate, inputSymbolDict->getGenericRegionStats());
+ } else {
+ resetGenericStats(sdTemplate, NULL);
+ }
+ resetIntStats(symCodeLen);
+ arithDecoder->start();
+ }
+
+ // set up the arithmetic decoder for refinement/aggregation
+ if (refAgg) {
+ if (contextUsed && inputSymbolDict) {
+ resetRefinementStats(sdrTemplate,
+ inputSymbolDict->getRefinementRegionStats());
+ } else {
+ resetRefinementStats(sdrTemplate, NULL);
+ }
+ }
+
+ // allocate symbol widths storage
+ symWidths = NULL;
+ if (huff && !refAgg) {
+ symWidths = (Guint *)gmallocn(numNewSyms, sizeof(Guint));
+ }
+
+ symHeight = 0;
+ i = 0;
+ while (i < numNewSyms) {
+
+ // read the height class delta height
+ if (huff) {
+ huffDecoder->decodeInt(&dh, huffDHTable);
+ } else {
+ arithDecoder->decodeInt(&dh, iadhStats);
+ }
+ if (dh < 0 && (Guint)-dh >= symHeight) {
+ error(getPos(), "Bad delta-height value in JBIG2 symbol dictionary");
+ goto syntaxError;
+ }
+ symHeight += dh;
+ symWidth = 0;
+ totalWidth = 0;
+ j = i;
+
+ // read the symbols in this height class
+ while (1) {
+
+ // read the delta width
+ if (huff) {
+ if (!huffDecoder->decodeInt(&dw, huffDWTable)) {
+ break;
+ }
+ } else {
+ if (!arithDecoder->decodeInt(&dw, iadwStats)) {
+ break;
+ }
+ }
+ if (dw < 0 && (Guint)-dw >= symWidth) {
+ error(getPos(), "Bad delta-height value in JBIG2 symbol dictionary");
+ goto syntaxError;
+ }
+ symWidth += dw;
+
+ // using a collective bitmap, so don't read a bitmap here
+ if (huff && !refAgg) {
+ symWidths[i] = symWidth;
+ totalWidth += symWidth;
+
+ // refinement/aggregate coding
+ } else if (refAgg) {
+ if (huff) {
+ if (!huffDecoder->decodeInt(&refAggNum, huffAggInstTable)) {
+ break;
+ }
+ } else {
+ if (!arithDecoder->decodeInt(&refAggNum, iaaiStats)) {
+ break;
+ }
+ }
+#if 0 //~ This special case was added about a year before the final draft
+ //~ of the JBIG2 spec was released. I have encountered some old
+ //~ JBIG2 images that predate it.
+ if (0) {
+#else
+ if (refAggNum == 1) {
+#endif
+ if (huff) {
+ symID = huffDecoder->readBits(symCodeLen);
+ huffDecoder->decodeInt(&refDX, huffTableO);
+ huffDecoder->decodeInt(&refDY, huffTableO);
+ huffDecoder->decodeInt(&bmSize, huffTableA);
+ huffDecoder->reset();
+ arithDecoder->start();
+ } else {
+ symID = arithDecoder->decodeIAID(symCodeLen, iaidStats);
+ arithDecoder->decodeInt(&refDX, iardxStats);
+ arithDecoder->decodeInt(&refDY, iardyStats);
+ }
+ refBitmap = bitmaps[symID];
+ bitmaps[numInputSyms + i] =
+ readGenericRefinementRegion(symWidth, symHeight,
+ sdrTemplate, gFalse,
+ refBitmap, refDX, refDY,
+ sdrATX, sdrATY);
+ //~ do we need to use the bmSize value here (in Huffman mode)?
+ } else {
+ bitmaps[numInputSyms + i] =
+ readTextRegion(huff, gTrue, symWidth, symHeight,
+ refAggNum, 0, numInputSyms + i, NULL,
+ symCodeLen, bitmaps, 0, 0, 0, 1, 0,
+ huffTableF, huffTableH, huffTableK, huffTableO,
+ huffTableO, huffTableO, huffTableO, huffTableA,
+ sdrTemplate, sdrATX, sdrATY);
+ }
+
+ // non-ref/agg coding
+ } else {
+ bitmaps[numInputSyms + i] =
+ readGenericBitmap(gFalse, symWidth, symHeight,
+ sdTemplate, gFalse, gFalse, NULL,
+ sdATX, sdATY, 0);
+ }
+
+ ++i;
+ }
+
+ // read the collective bitmap
+ if (huff && !refAgg) {
+ huffDecoder->decodeInt(&bmSize, huffBMSizeTable);
+ huffDecoder->reset();
+ if (bmSize == 0) {
+ collBitmap = new JBIG2Bitmap(0, totalWidth, symHeight);
+ bmSize = symHeight * ((totalWidth + 7) >> 3);
+ p = collBitmap->getDataPtr();
+ for (k = 0; k < (Guint)bmSize; ++k) {
+ *p++ = curStr->getChar();
+ }
+ } else {
+ collBitmap = readGenericBitmap(gTrue, totalWidth, symHeight,
+ 0, gFalse, gFalse, NULL, NULL, NULL,
+ bmSize);
+ }
+ x = 0;
+ for (; j < i; ++j) {
+ bitmaps[numInputSyms + j] =
+ collBitmap->getSlice(x, 0, symWidths[j], symHeight);
+ x += symWidths[j];
+ }
+ delete collBitmap;
+ }
+ }
+
+ // create the symbol dict object
+ symbolDict = new JBIG2SymbolDict(segNum, numExSyms);
+
+ // exported symbol list
+ i = j = 0;
+ ex = gFalse;
+ while (i < numInputSyms + numNewSyms) {
+ if (huff) {
+ huffDecoder->decodeInt(&run, huffTableA);
+ } else {
+ arithDecoder->decodeInt(&run, iaexStats);
+ }
+ if (ex) {
+ for (cnt = 0; cnt < run; ++cnt) {
+ symbolDict->setBitmap(j++, bitmaps[i++]->copy());
+ }
+ } else {
+ i += run;
+ }
+ ex = !ex;
+ }
+
+ for (i = 0; i < numNewSyms; ++i) {
+ delete bitmaps[numInputSyms + i];
+ }
+ gfree(bitmaps);
+ if (symWidths) {
+ gfree(symWidths);
+ }
+
+ // save the arithmetic decoder stats
+ if (!huff && contextRetained) {
+ symbolDict->setGenericRegionStats(genericRegionStats->copy());
+ if (refAgg) {
+ symbolDict->setRefinementRegionStats(refinementRegionStats->copy());
+ }
+ }
+
+ // store the new symbol dict
+ segments->append(symbolDict);
+
+ return gTrue;
+
+ syntaxError:
+ for (i = 0; i < numNewSyms; ++i) {
+ if (bitmaps[numInputSyms + i]) {
+ delete bitmaps[numInputSyms + i];
+ }
+ }
+ gfree(bitmaps);
+ if (symWidths) {
+ gfree(symWidths);
+ }
+ return gFalse;
+
+ eofError:
+ error(getPos(), "Unexpected EOF in JBIG2 stream");
+ return gFalse;
+}
+
+void JBIG2Stream::readTextRegionSeg(Guint segNum, GBool imm,
+ GBool lossless, Guint length,
+ Guint *refSegs, Guint nRefSegs) {
+ JBIG2Bitmap *bitmap;
+ JBIG2HuffmanTable runLengthTab[36];
+ JBIG2HuffmanTable *symCodeTab;
+ JBIG2HuffmanTable *huffFSTable, *huffDSTable, *huffDTTable;
+ JBIG2HuffmanTable *huffRDWTable, *huffRDHTable;
+ JBIG2HuffmanTable *huffRDXTable, *huffRDYTable, *huffRSizeTable;
+ JBIG2Segment *seg;
+ GList *codeTables;
+ JBIG2SymbolDict *symbolDict;
+ JBIG2Bitmap **syms;
+ Guint w, h, x, y, segInfoFlags, extCombOp;
+ Guint flags, huff, refine, logStrips, refCorner, transposed;
+ Guint combOp, defPixel, templ;
+ int sOffset;
+ Guint huffFlags, huffFS, huffDS, huffDT;
+ Guint huffRDW, huffRDH, huffRDX, huffRDY, huffRSize;
+ Guint numInstances, numSyms, symCodeLen;
+ int atx[2], aty[2];
+ Guint i, k, kk;
+ int j;
+
+ // region segment info field
+ if (!readULong(&w) || !readULong(&h) ||
+ !readULong(&x) || !readULong(&y) ||
+ !readUByte(&segInfoFlags)) {
+ goto eofError;
+ }
+ extCombOp = segInfoFlags & 7;
+
+ // rest of the text region header
+ if (!readUWord(&flags)) {
+ goto eofError;
+ }
+ huff = flags & 1;
+ refine = (flags >> 1) & 1;
+ logStrips = (flags >> 2) & 3;
+ refCorner = (flags >> 4) & 3;
+ transposed = (flags >> 6) & 1;
+ combOp = (flags >> 7) & 3;
+ defPixel = (flags >> 9) & 1;
+ sOffset = (flags >> 10) & 0x1f;
+ if (sOffset & 0x10) {
+ sOffset |= -1 - 0x0f;
+ }
+ templ = (flags >> 15) & 1;
+ huffFS = huffDS = huffDT = 0; // make gcc happy
+ huffRDW = huffRDH = huffRDX = huffRDY = huffRSize = 0; // make gcc happy
+ if (huff) {
+ if (!readUWord(&huffFlags)) {
+ goto eofError;
+ }
+ huffFS = huffFlags & 3;
+ huffDS = (huffFlags >> 2) & 3;
+ huffDT = (huffFlags >> 4) & 3;
+ huffRDW = (huffFlags >> 6) & 3;
+ huffRDH = (huffFlags >> 8) & 3;
+ huffRDX = (huffFlags >> 10) & 3;
+ huffRDY = (huffFlags >> 12) & 3;
+ huffRSize = (huffFlags >> 14) & 1;
+ }
+ if (refine && templ == 0) {
+ if (!readByte(&atx[0]) || !readByte(&aty[0]) ||
+ !readByte(&atx[1]) || !readByte(&aty[1])) {
+ goto eofError;
+ }
+ }
+ if (!readULong(&numInstances)) {
+ goto eofError;
+ }
+
+ // get symbol dictionaries and tables
+ codeTables = new GList();
+ numSyms = 0;
+ for (i = 0; i < nRefSegs; ++i) {
+ if ((seg = findSegment(refSegs[i]))) {
+ if (seg->getType() == jbig2SegSymbolDict) {
+ numSyms += ((JBIG2SymbolDict *)seg)->getSize();
+ } else if (seg->getType() == jbig2SegCodeTable) {
+ codeTables->append(seg);
+ }
+ } else {
+ error(getPos(), "Invalid segment reference in JBIG2 text region");
+ }
+ }
+ symCodeLen = 0;
+ i = 1;
+ while (i < numSyms) {
+ ++symCodeLen;
+ i <<= 1;
+ }
+
+ // get the symbol bitmaps
+ syms = (JBIG2Bitmap **)gmallocn(numSyms, sizeof(JBIG2Bitmap *));
+ kk = 0;
+ for (i = 0; i < nRefSegs; ++i) {
+ if ((seg = findSegment(refSegs[i]))) {
+ if (seg->getType() == jbig2SegSymbolDict) {
+ symbolDict = (JBIG2SymbolDict *)seg;
+ for (k = 0; k < symbolDict->getSize(); ++k) {
+ syms[kk++] = symbolDict->getBitmap(k);
+ }
+ }
+ }
+ }
+
+ // get the Huffman tables
+ huffFSTable = huffDSTable = huffDTTable = NULL; // make gcc happy
+ huffRDWTable = huffRDHTable = NULL; // make gcc happy
+ huffRDXTable = huffRDYTable = huffRSizeTable = NULL; // make gcc happy
+ i = 0;
+ if (huff) {
+ if (huffFS == 0) {
+ huffFSTable = huffTableF;
+ } else if (huffFS == 1) {
+ huffFSTable = huffTableG;
+ } else {
+ huffFSTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ if (huffDS == 0) {
+ huffDSTable = huffTableH;
+ } else if (huffDS == 1) {
+ huffDSTable = huffTableI;
+ } else if (huffDS == 2) {
+ huffDSTable = huffTableJ;
+ } else {
+ huffDSTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ if (huffDT == 0) {
+ huffDTTable = huffTableK;
+ } else if (huffDT == 1) {
+ huffDTTable = huffTableL;
+ } else if (huffDT == 2) {
+ huffDTTable = huffTableM;
+ } else {
+ huffDTTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ if (huffRDW == 0) {
+ huffRDWTable = huffTableN;
+ } else if (huffRDW == 1) {
+ huffRDWTable = huffTableO;
+ } else {
+ huffRDWTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ if (huffRDH == 0) {
+ huffRDHTable = huffTableN;
+ } else if (huffRDH == 1) {
+ huffRDHTable = huffTableO;
+ } else {
+ huffRDHTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ if (huffRDX == 0) {
+ huffRDXTable = huffTableN;
+ } else if (huffRDX == 1) {
+ huffRDXTable = huffTableO;
+ } else {
+ huffRDXTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ if (huffRDY == 0) {
+ huffRDYTable = huffTableN;
+ } else if (huffRDY == 1) {
+ huffRDYTable = huffTableO;
+ } else {
+ huffRDYTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ if (huffRSize == 0) {
+ huffRSizeTable = huffTableA;
+ } else {
+ huffRSizeTable =
+ ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();
+ }
+ }
+ delete codeTables;
+
+ // symbol ID Huffman decoding table
+ if (huff) {
+ huffDecoder->reset();
+ for (i = 0; i < 32; ++i) {
+ runLengthTab[i].val = i;
+ runLengthTab[i].prefixLen = huffDecoder->readBits(4);
+ runLengthTab[i].rangeLen = 0;
+ }
+ runLengthTab[32].val = 0x103;
+ runLengthTab[32].prefixLen = huffDecoder->readBits(4);
+ runLengthTab[32].rangeLen = 2;
+ runLengthTab[33].val = 0x203;
+ runLengthTab[33].prefixLen = huffDecoder->readBits(4);
+ runLengthTab[33].rangeLen = 3;
+ runLengthTab[34].val = 0x20b;
+ runLengthTab[34].prefixLen = huffDecoder->readBits(4);
+ runLengthTab[34].rangeLen = 7;
+ runLengthTab[35].prefixLen = 0;
+ runLengthTab[35].rangeLen = jbig2HuffmanEOT;
+ huffDecoder->buildTable(runLengthTab, 35);
+ symCodeTab = (JBIG2HuffmanTable *)gmallocn(numSyms + 1,
+ sizeof(JBIG2HuffmanTable));
+ for (i = 0; i < numSyms; ++i) {
+ symCodeTab[i].val = i;
+ symCodeTab[i].rangeLen = 0;
+ }
+ i = 0;
+ while (i < numSyms) {
+ huffDecoder->decodeInt(&j, runLengthTab);
+ if (j > 0x200) {
+ for (j -= 0x200; j && i < numSyms; --j) {
+ symCodeTab[i++].prefixLen = 0;
+ }
+ } else if (j > 0x100) {
+ for (j -= 0x100; j && i < numSyms; --j) {
+ symCodeTab[i].prefixLen = symCodeTab[i-1].prefixLen;
+ ++i;
+ }
+ } else {
+ symCodeTab[i++].prefixLen = j;
+ }
+ }
+ symCodeTab[numSyms].prefixLen = 0;
+ symCodeTab[numSyms].rangeLen = jbig2HuffmanEOT;
+ huffDecoder->buildTable(symCodeTab, numSyms);
+ huffDecoder->reset();
+
+ // set up the arithmetic decoder
+ } else {
+ symCodeTab = NULL;
+ resetIntStats(symCodeLen);
+ arithDecoder->start();
+ }
+ if (refine) {
+ resetRefinementStats(templ, NULL);
+ }
+
+ bitmap = readTextRegion(huff, refine, w, h, numInstances,
+ logStrips, numSyms, symCodeTab, symCodeLen, syms,
+ defPixel, combOp, transposed, refCorner, sOffset,
+ huffFSTable, huffDSTable, huffDTTable,
+ huffRDWTable, huffRDHTable,
+ huffRDXTable, huffRDYTable, huffRSizeTable,
+ templ, atx, aty);
+
+ gfree(syms);
+
+ // combine the region bitmap into the page bitmap
+ if (imm) {
+ if (pageH == 0xffffffff && y + h > curPageH) {
+ pageBitmap->expand(y + h, pageDefPixel);
+ }
+ pageBitmap->combine(bitmap, x, y, extCombOp);
+ delete bitmap;
+
+ // store the region bitmap
+ } else {
+ bitmap->setSegNum(segNum);
+ segments->append(bitmap);
+ }
+
+ // clean up the Huffman decoder
+ if (huff) {
+ gfree(symCodeTab);
+ }
+
+ return;
+
+ eofError:
+ error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+JBIG2Bitmap *JBIG2Stream::readTextRegion(GBool huff, GBool refine,
+ int w, int h,
+ Guint numInstances,
+ Guint logStrips,
+ int numSyms,
+ JBIG2HuffmanTable *symCodeTab,
+ Guint symCodeLen,
+ JBIG2Bitmap **syms,
+ Guint defPixel, Guint combOp,
+ Guint transposed, Guint refCorner,
+ int sOffset,
+ JBIG2HuffmanTable *huffFSTable,
+ JBIG2HuffmanTable *huffDSTable,
+ JBIG2HuffmanTable *huffDTTable,
+ JBIG2HuffmanTable *huffRDWTable,
+ JBIG2HuffmanTable *huffRDHTable,
+ JBIG2HuffmanTable *huffRDXTable,
+ JBIG2HuffmanTable *huffRDYTable,
+ JBIG2HuffmanTable *huffRSizeTable,
+ Guint templ,
+ int *atx, int *aty) {
+ JBIG2Bitmap *bitmap;
+ JBIG2Bitmap *symbolBitmap;
+ Guint strips;
+ int t, dt, tt, s, ds, sFirst, j;
+ int rdw, rdh, rdx, rdy, ri, refDX, refDY, bmSize;
+ Guint symID, inst, bw, bh;
+
+ strips = 1 << logStrips;
+
+ // allocate the bitmap
+ bitmap = new JBIG2Bitmap(0, w, h);
+ if (defPixel) {
+ bitmap->clearToOne();
+ } else {
+ bitmap->clearToZero();
+ }
+
+ // decode initial T value
+ if (huff) {
+ huffDecoder->decodeInt(&t, huffDTTable);
+ } else {
+ arithDecoder->decodeInt(&t, iadtStats);
+ }
+ t *= -(int)strips;
+
+ inst = 0;
+ sFirst = 0;
+ while (inst < numInstances) {
+
+ // decode delta-T
+ if (huff) {
+ huffDecoder->decodeInt(&dt, huffDTTable);
+ } else {
+ arithDecoder->decodeInt(&dt, iadtStats);
+ }
+ t += dt * strips;
+
+ // first S value
+ if (huff) {
+ huffDecoder->decodeInt(&ds, huffFSTable);
+ } else {
+ arithDecoder->decodeInt(&ds, iafsStats);
+ }
+ sFirst += ds;
+ s = sFirst;
+
+ // read the instances
+ while (1) {
+
+ // T value
+ if (strips == 1) {
+ dt = 0;
+ } else if (huff) {
+ dt = huffDecoder->readBits(logStrips);
+ } else {
+ arithDecoder->decodeInt(&dt, iaitStats);
+ }
+ tt = t + dt;
+
+ // symbol ID
+ if (huff) {
+ if (symCodeTab) {
+ huffDecoder->decodeInt(&j, symCodeTab);
+ symID = (Guint)j;
+ } else {
+ symID = huffDecoder->readBits(symCodeLen);
+ }
+ } else {
+ symID = arithDecoder->decodeIAID(symCodeLen, iaidStats);
+ }
+
+ if (symID >= (Guint)numSyms) {
+ error(getPos(), "Invalid symbol number in JBIG2 text region");
+ } else {
+
+ // get the symbol bitmap
+ symbolBitmap = NULL;
+ if (refine) {
+ if (huff) {
+ ri = (int)huffDecoder->readBit();
+ } else {
+ arithDecoder->decodeInt(&ri, iariStats);
+ }
+ } else {
+ ri = 0;
+ }
+ if (ri) {
+ if (huff) {
+ huffDecoder->decodeInt(&rdw, huffRDWTable);
+ huffDecoder->decodeInt(&rdh, huffRDHTable);
+ huffDecoder->decodeInt(&rdx, huffRDXTable);
+ huffDecoder->decodeInt(&rdy, huffRDYTable);
+ huffDecoder->decodeInt(&bmSize, huffRSizeTable);
+ huffDecoder->reset();
+ arithDecoder->start();
+ } else {
+ arithDecoder->decodeInt(&rdw, iardwStats);
+ arithDecoder->decodeInt(&rdh, iardhStats);
+ arithDecoder->decodeInt(&rdx, iardxStats);
+ arithDecoder->decodeInt(&rdy, iardyStats);
+ }
+ refDX = ((rdw >= 0) ? rdw : rdw - 1) / 2 + rdx;
+ refDY = ((rdh >= 0) ? rdh : rdh - 1) / 2 + rdy;
+
+ symbolBitmap =
+ readGenericRefinementRegion(rdw + syms[symID]->getWidth(),
+ rdh + syms[symID]->getHeight(),
+ templ, gFalse, syms[symID],
+ refDX, refDY, atx, aty);
+ //~ do we need to use the bmSize value here (in Huffman mode)?
+ } else {
+ symbolBitmap = syms[symID];
+ }
+
+ // combine the symbol bitmap into the region bitmap
+ //~ something is wrong here - refCorner shouldn't degenerate into
+ //~ two cases
+ bw = symbolBitmap->getWidth() - 1;
+ bh = symbolBitmap->getHeight() - 1;
+ if (transposed) {
+ switch (refCorner) {
+ case 0: // bottom left
+ bitmap->combine(symbolBitmap, tt, s, combOp);
+ break;
+ case 1: // top left
+ bitmap->combine(symbolBitmap, tt, s, combOp);
+ break;
+ case 2: // bottom right
+ bitmap->combine(symbolBitmap, tt - bw, s, combOp);
+ break;
+ case 3: // top right
+ bitmap->combine(symbolBitmap, tt - bw, s, combOp);
+ break;
+ }
+ s += bh;
+ } else {
+ switch (refCorner) {
+ case 0: // bottom left
+ bitmap->combine(symbolBitmap, s, tt - bh, combOp);
+ break;
+ case 1: // top left
+ bitmap->combine(symbolBitmap, s, tt, combOp);
+ break;
+ case 2: // bottom right
+ bitmap->combine(symbolBitmap, s, tt - bh, combOp);
+ break;
+ case 3: // top right
+ bitmap->combine(symbolBitmap, s, tt, combOp);
+ break;
+ }
+ s += bw;
+ }
+ if (ri) {
+ delete symbolBitmap;
+ }
+ }
+
+ // next instance
+ ++inst;
+
+ // next S value
+ if (huff) {
+ if (!huffDecoder->decodeInt(&ds, huffDSTable)) {
+ break;
+ }
+ } else {
+ if (!arithDecoder->decodeInt(&ds, iadsStats)) {
+ break;
+ }
+ }
+ s += sOffset + ds;
+ }
+ }
+
+ return bitmap;
+}
+
+void JBIG2Stream::readPatternDictSeg(Guint segNum, Guint length) {
+ JBIG2PatternDict *patternDict;
+ JBIG2Bitmap *bitmap;
+ Guint flags, patternW, patternH, grayMax, templ, mmr;
+ int atx[4], aty[4];
+ Guint i, x;
+
+ // halftone dictionary flags, pattern width and height, max gray value
+ if (!readUByte(&flags) ||
+ !readUByte(&patternW) ||
+ !readUByte(&patternH) ||
+ !readULong(&grayMax)) {
+ goto eofError;
+ }
+ templ = (flags >> 1) & 3;
+ mmr = flags & 1;
+
+ // set up the arithmetic decoder
+ if (!mmr) {
+ resetGenericStats(templ, NULL);
+ arithDecoder->start();
+ }
+
+ // read the bitmap
+ atx[0] = -(int)patternW; aty[0] = 0;
+ atx[1] = -3; aty[1] = -1;
+ atx[2] = 2; aty[2] = -2;
+ atx[3] = -2; aty[3] = -2;
+ bitmap = readGenericBitmap(mmr, (grayMax + 1) * patternW, patternH,
+ templ, gFalse, gFalse, NULL,
+ atx, aty, length - 7);
+
+ // create the pattern dict object
+ patternDict = new JBIG2PatternDict(segNum, grayMax + 1);
+
+ // split up the bitmap
+ x = 0;
+ for (i = 0; i <= grayMax; ++i) {
+ patternDict->setBitmap(i, bitmap->getSlice(x, 0, patternW, patternH));
+ x += patternW;
+ }
+
+ // free memory
+ delete bitmap;
+
+ // store the new pattern dict
+ segments->append(patternDict);
+
+ return;
+
+ eofError:
+ error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+void JBIG2Stream::readHalftoneRegionSeg(Guint segNum, GBool imm,
+ GBool lossless, Guint length,
+ Guint *refSegs, Guint nRefSegs) {
+ JBIG2Bitmap *bitmap;
+ JBIG2Segment *seg;
+ JBIG2PatternDict *patternDict;
+ JBIG2Bitmap *skipBitmap;
+ Guint *grayImg;
+ JBIG2Bitmap *grayBitmap;
+ JBIG2Bitmap *patternBitmap;
+ Guint w, h, x, y, segInfoFlags, extCombOp;
+ Guint flags, mmr, templ, enableSkip, combOp;
+ Guint gridW, gridH, stepX, stepY, patW, patH;
+ int atx[4], aty[4];
+ int gridX, gridY, xx, yy, bit, j;
+ Guint bpp, m, n, i;
+
+ // region segment info field
+ if (!readULong(&w) || !readULong(&h) ||
+ !readULong(&x) || !readULong(&y) ||
+ !readUByte(&segInfoFlags)) {
+ goto eofError;
+ }
+ extCombOp = segInfoFlags & 7;
+
+ // rest of the halftone region header
+ if (!readUByte(&flags)) {
+ goto eofError;
+ }
+ mmr = flags & 1;
+ templ = (flags >> 1) & 3;
+ enableSkip = (flags >> 3) & 1;
+ combOp = (flags >> 4) & 7;
+ if (!readULong(&gridW) || !readULong(&gridH) ||
+ !readLong(&gridX) || !readLong(&gridY) ||
+ !readUWord(&stepX) || !readUWord(&stepY)) {
+ goto eofError;
+ }
+
+ // get pattern dictionary
+ if (nRefSegs != 1) {
+ error(getPos(), "Bad symbol dictionary reference in JBIG2 halftone segment");
+ return;
+ }
+ seg = findSegment(refSegs[0]);
+ if (seg->getType() != jbig2SegPatternDict) {
+ error(getPos(), "Bad symbol dictionary reference in JBIG2 halftone segment");
+ return;
+ }
+ patternDict = (JBIG2PatternDict *)seg;
+ bpp = 0;
+ i = 1;
+ while (i < patternDict->getSize()) {
+ ++bpp;
+ i <<= 1;
+ }
+ patW = patternDict->getBitmap(0)->getWidth();
+ patH = patternDict->getBitmap(0)->getHeight();
+
+ // set up the arithmetic decoder
+ if (!mmr) {
+ resetGenericStats(templ, NULL);
+ arithDecoder->start();
+ }
+
+ // allocate the bitmap
+ bitmap = new JBIG2Bitmap(segNum, w, h);
+ if (flags & 0x80) { // HDEFPIXEL
+ bitmap->clearToOne();
+ } else {
+ bitmap->clearToZero();
+ }
+
+ // compute the skip bitmap
+ skipBitmap = NULL;
+ if (enableSkip) {
+ skipBitmap = new JBIG2Bitmap(0, gridW, gridH);
+ skipBitmap->clearToZero();
+ for (m = 0; m < gridH; ++m) {
+ xx = gridX + m * stepY;
+ yy = gridY + m * stepX;
+ for (n = 0; n < gridW; ++n) {
+ if (((xx + (int)patW) >> 8) <= 0 || (xx >> 8) >= (int)w ||
+ ((yy + (int)patH) >> 8) <= 0 || (yy >> 8) >= (int)h) {
+ skipBitmap->setPixel(n, m);
+ }
+ }
+ }
+ }
+
+ // read the gray-scale image
+ grayImg = (Guint *)gmallocn(gridW * gridH, sizeof(Guint));
+ memset(grayImg, 0, gridW * gridH * sizeof(Guint));
+ atx[0] = templ <= 1 ? 3 : 2; aty[0] = -1;
+ atx[1] = -3; aty[1] = -1;
+ atx[2] = 2; aty[2] = -2;
+ atx[3] = -2; aty[3] = -2;
+ for (j = bpp - 1; j >= 0; --j) {
+ grayBitmap = readGenericBitmap(mmr, gridW, gridH, templ, gFalse,
+ enableSkip, skipBitmap, atx, aty, -1);
+ i = 0;
+ for (m = 0; m < gridH; ++m) {
+ for (n = 0; n < gridW; ++n) {
+ bit = grayBitmap->getPixel(n, m) ^ (grayImg[i] & 1);
+ grayImg[i] = (grayImg[i] << 1) | bit;
+ ++i;
+ }
+ }
+ delete grayBitmap;
+ }
+
+ // decode the image
+ i = 0;
+ for (m = 0; m < gridH; ++m) {
+ xx = gridX + m * stepY;
+ yy = gridY + m * stepX;
+ for (n = 0; n < gridW; ++n) {
+ if (!(enableSkip && skipBitmap->getPixel(n, m))) {
+ patternBitmap = patternDict->getBitmap(grayImg[i]);
+ bitmap->combine(patternBitmap, xx >> 8, yy >> 8, combOp);
+ }
+ xx += stepX;
+ yy -= stepY;
+ ++i;
+ }
+ }
+
+ gfree(grayImg);
+
+ // combine the region bitmap into the page bitmap
+ if (imm) {
+ if (pageH == 0xffffffff && y + h > curPageH) {
+ pageBitmap->expand(y + h, pageDefPixel);
+ }
+ pageBitmap->combine(bitmap, x, y, extCombOp);
+ delete bitmap;
+
+ // store the region bitmap
+ } else {
+ segments->append(bitmap);
+ }
+
+ return;
+
+ eofError:
+ error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+void JBIG2Stream::readGenericRegionSeg(Guint segNum, GBool imm,
+ GBool lossless, Guint length) {
+ JBIG2Bitmap *bitmap;
+ Guint w, h, x, y, segInfoFlags, extCombOp;
+ Guint flags, mmr, templ, tpgdOn;
+ int atx[4], aty[4];
+
+ // region segment info field
+ if (!readULong(&w) || !readULong(&h) ||
+ !readULong(&x) || !readULong(&y) ||
+ !readUByte(&segInfoFlags)) {
+ goto eofError;
+ }
+ extCombOp = segInfoFlags & 7;
+
+ // rest of the generic region segment header
+ if (!readUByte(&flags)) {
+ goto eofError;
+ }
+ mmr = flags & 1;
+ templ = (flags >> 1) & 3;
+ tpgdOn = (flags >> 3) & 1;
+
+ // AT flags
+ if (!mmr) {
+ if (templ == 0) {
+ if (!readByte(&atx[0]) ||
+ !readByte(&aty[0]) ||
+ !readByte(&atx[1]) ||
+ !readByte(&aty[1]) ||
+ !readByte(&atx[2]) ||
+ !readByte(&aty[2]) ||
+ !readByte(&atx[3]) ||
+ !readByte(&aty[3])) {
+ goto eofError;
+ }
+ } else {
+ if (!readByte(&atx[0]) ||
+ !readByte(&aty[0])) {
+ goto eofError;
+ }
+ }
+ }
+
+ // set up the arithmetic decoder
+ if (!mmr) {
+ resetGenericStats(templ, NULL);
+ arithDecoder->start();
+ }
+
+ // read the bitmap
+ bitmap = readGenericBitmap(mmr, w, h, templ, tpgdOn, gFalse,
+ NULL, atx, aty, mmr ? 0 : length - 18);
+
+ // combine the region bitmap into the page bitmap
+ if (imm) {
+ if (pageH == 0xffffffff && y + h > curPageH) {
+ pageBitmap->expand(y + h, pageDefPixel);
+ }
+ pageBitmap->combine(bitmap, x, y, extCombOp);
+ delete bitmap;
+
+ // store the region bitmap
+ } else {
+ bitmap->setSegNum(segNum);
+ segments->append(bitmap);
+ }
+
+ return;
+
+ eofError:
+ error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+JBIG2Bitmap *JBIG2Stream::readGenericBitmap(GBool mmr, int w, int h,
+ int templ, GBool tpgdOn,
+ GBool useSkip, JBIG2Bitmap *skip,
+ int *atx, int *aty,
+ int mmrDataLength) {
+ JBIG2Bitmap *bitmap;
+ GBool ltp;
+ Guint ltpCX, cx, cx0, cx1, cx2;
+ JBIG2BitmapPtr cxPtr0, cxPtr1;
+ JBIG2BitmapPtr atPtr0, atPtr1, atPtr2, atPtr3;
+ int *refLine, *codingLine;
+ int code1, code2, code3;
+ int x, y, a0, pix, i, refI, codingI;
+
+ bitmap = new JBIG2Bitmap(0, w, h);
+ bitmap->clearToZero();
+
+ //----- MMR decode
+
+ if (mmr) {
+
+ mmrDecoder->reset();
+ refLine = (int *)gmallocn(w + 2, sizeof(int));
+ codingLine = (int *)gmallocn(w + 2, sizeof(int));
+ codingLine[0] = codingLine[1] = w;
+
+ for (y = 0; y < h; ++y) {
+
+ // copy coding line to ref line
+ for (i = 0; codingLine[i] < w; ++i) {
+ refLine[i] = codingLine[i];
+ }
+ refLine[i] = refLine[i + 1] = w;
+
+ // decode a line
+ refI = 0; // b1 = refLine[refI]
+ codingI = 0; // a1 = codingLine[codingI]
+ a0 = 0;
+ do {
+ code1 = mmrDecoder->get2DCode();
+ switch (code1) {
+ case twoDimPass:
+ if (refLine[refI] < w) {
+ a0 = refLine[refI + 1];
+ refI += 2;
+ }
+ break;
+ case twoDimHoriz:
+ if (codingI & 1) {
+ code1 = 0;
+ do {
+ code1 += code3 = mmrDecoder->getBlackCode();
+ } while (code3 >= 64);
+ code2 = 0;
+ do {
+ code2 += code3 = mmrDecoder->getWhiteCode();
+ } while (code3 >= 64);
+ } else {
+ code1 = 0;
+ do {
+ code1 += code3 = mmrDecoder->getWhiteCode();
+ } while (code3 >= 64);
+ code2 = 0;
+ do {
+ code2 += code3 = mmrDecoder->getBlackCode();
+ } while (code3 >= 64);
+ }
+ if (code1 > 0 || code2 > 0) {
+ a0 = codingLine[codingI++] = a0 + code1;
+ a0 = codingLine[codingI++] = a0 + code2;
+ while (refLine[refI] <= a0 && refLine[refI] < w) {
+ refI += 2;
+ }
+ }
+ break;
+ case twoDimVert0:
+ a0 = codingLine[codingI++] = refLine[refI];
+ if (refLine[refI] < w) {
+ ++refI;
+ }
+ break;
+ case twoDimVertR1:
+ a0 = codingLine[codingI++] = refLine[refI] + 1;
+ if (refLine[refI] < w) {
+ ++refI;
+ while (refLine[refI] <= a0 && refLine[refI] < w) {
+ refI += 2;
+ }
+ }
+ break;
+ case twoDimVertR2:
+ a0 = codingLine[codingI++] = refLine[refI] + 2;
+ if (refLine[refI] < w) {
+ ++refI;
+ while (refLine[refI] <= a0 && refLine[refI] < w) {
+ refI += 2;
+ }
+ }
+ break;
+ case twoDimVertR3:
+ a0 = codingLine[codingI++] = refLine[refI] + 3;
+ if (refLine[refI] < w) {
+ ++refI;
+ while (refLine[refI] <= a0 && refLine[refI] < w) {
+ refI += 2;
+ }
+ }
+ break;
+ case twoDimVertL1:
+ a0 = codingLine[codingI++] = refLine[refI] - 1;
+ if (refI > 0) {
+ --refI;
+ } else {
+ ++refI;
+ }
+ while (refLine[refI] <= a0 && refLine[refI] < w) {
+ refI += 2;
+ }
+ break;
+ case twoDimVertL2:
+ a0 = codingLine[codingI++] = refLine[refI] - 2;
+ if (refI > 0) {
+ --refI;
+ } else {
+ ++refI;
+ }
+ while (refLine[refI] <= a0 && refLine[refI] < w) {
+ refI += 2;
+ }
+ break;
+ case twoDimVertL3:
+ a0 = codingLine[codingI++] = refLine[refI] - 3;
+ if (refI > 0) {
+ --refI;
+ } else {
+ ++refI;
+ }
+ while (refLine[refI] <= a0 && refLine[refI] < w) {
+ refI += 2;
+ }
+ break;
+ default:
+ error(getPos(), "Illegal code in JBIG2 MMR bitmap data");
+ break;
+ }
+ } while (a0 < w);
+ codingLine[codingI++] = w;
+
+ // convert the run lengths to a bitmap line
+ i = 0;
+ while (codingLine[i] < w) {
+ for (x = codingLine[i]; x < codingLine[i+1]; ++x) {
+ bitmap->setPixel(x, y);
+ }
+ i += 2;
+ }
+ }
+
+ if (mmrDataLength >= 0) {
+ mmrDecoder->skipTo(mmrDataLength);
+ } else {
+ if (mmrDecoder->get24Bits() != 0x001001) {
+ error(getPos(), "Missing EOFB in JBIG2 MMR bitmap data");
+ }
+ }
+
+ gfree(refLine);
+ gfree(codingLine);
+
+ //----- arithmetic decode
+
+ } else {
+ // set up the typical row context
+ ltpCX = 0; // make gcc happy
+ if (tpgdOn) {
+ switch (templ) {
+ case 0:
+ ltpCX = 0x3953; // 001 11001 0101 0011
+ break;
+ case 1:
+ ltpCX = 0x079a; // 0011 11001 101 0
+ break;
+ case 2:
+ ltpCX = 0x0e3; // 001 1100 01 1
+ break;
+ case 3:
+ ltpCX = 0x18a; // 01100 0101 1
+ break;
+ }
+ }
+
+ ltp = 0;
+ cx = cx0 = cx1 = cx2 = 0; // make gcc happy
+ for (y = 0; y < h; ++y) {
+
+ // check for a "typical" (duplicate) row
+ if (tpgdOn) {
+ if (arithDecoder->decodeBit(ltpCX, genericRegionStats)) {
+ ltp = !ltp;
+ }
+ if (ltp) {
+ bitmap->duplicateRow(y, y-1);
+ continue;
+ }
+ }
+
+ switch (templ) {
+ case 0:
+
+ // set up the context
+ bitmap->getPixelPtr(0, y-2, &cxPtr0);
+ cx0 = bitmap->nextPixel(&cxPtr0);
+ cx0 = (cx0 << 1) | bitmap->nextPixel(&cxPtr0);
+ bitmap->getPixelPtr(0, y-1, &cxPtr1);
+ cx1 = bitmap->nextPixel(&cxPtr1);
+ cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
+ cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
+ cx2 = 0;
+ bitmap->getPixelPtr(atx[0], y + aty[0], &atPtr0);
+ bitmap->getPixelPtr(atx[1], y + aty[1], &atPtr1);
+ bitmap->getPixelPtr(atx[2], y + aty[2], &atPtr2);
+ bitmap->getPixelPtr(atx[3], y + aty[3], &atPtr3);
+
+ // decode the row
+ for (x = 0; x < w; ++x) {
+
+ // build the context
+ cx = (cx0 << 13) | (cx1 << 8) | (cx2 << 4) |
+ (bitmap->nextPixel(&atPtr0) << 3) |
+ (bitmap->nextPixel(&atPtr1) << 2) |
+ (bitmap->nextPixel(&atPtr2) << 1) |
+ bitmap->nextPixel(&atPtr3);
+
+ // check for a skipped pixel
+ if (useSkip && skip->getPixel(x, y)) {
+ pix = 0;
+
+ // decode the pixel
+ } else if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
+ bitmap->setPixel(x, y);
+ }
+
+ // update the context
+ cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 0x07;
+ cx1 = ((cx1 << 1) | bitmap->nextPixel(&cxPtr1)) & 0x1f;
+ cx2 = ((cx2 << 1) | pix) & 0x0f;
+ }
+ break;
+
+ case 1:
+
+ // set up the context
+ bitmap->getPixelPtr(0, y-2, &cxPtr0);
+ cx0 = bitmap->nextPixel(&cxPtr0);
+ cx0 = (cx0 << 1) | bitmap->nextPixel(&cxPtr0);
+ cx0 = (cx0 << 1) | bitmap->nextPixel(&cxPtr0);
+ bitmap->getPixelPtr(0, y-1, &cxPtr1);
+ cx1 = bitmap->nextPixel(&cxPtr1);
+ cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
+ cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
+ cx2 = 0;
+ bitmap->getPixelPtr(atx[0], y + aty[0], &atPtr0);
+
+ // decode the row
+ for (x = 0; x < w; ++x) {
+
+ // build the context
+ cx = (cx0 << 9) | (cx1 << 4) | (cx2 << 1) |
+ bitmap->nextPixel(&atPtr0);
+
+ // check for a skipped pixel
+ if (useSkip && skip->getPixel(x, y)) {
+ pix = 0;
+
+ // decode the pixel
+ } else if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
+ bitmap->setPixel(x, y);
+ }
+
+ // update the context
+ cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 0x0f;
+ cx1 = ((cx1 << 1) | bitmap->nextPixel(&cxPtr1)) & 0x1f;
+ cx2 = ((cx2 << 1) | pix) & 0x07;
+ }
+ break;
+
+ case 2:
+
+ // set up the context
+ bitmap->getPixelPtr(0, y-2, &cxPtr0);
+ cx0 = bitmap->nextPixel(&cxPtr0);
+ cx0 = (cx0 << 1) | bitmap->nextPixel(&cxPtr0);
+ bitmap->getPixelPtr(0, y-1, &cxPtr1);
+ cx1 = bitmap->nextPixel(&cxPtr1);
+ cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
+ cx2 = 0;
+ bitmap->getPixelPtr(atx[0], y + aty[0], &atPtr0);
+
+ // decode the row
+ for (x = 0; x < w; ++x) {
+
+ // build the context
+ cx = (cx0 << 7) | (cx1 << 3) | (cx2 << 1) |
+ bitmap->nextPixel(&atPtr0);
+
+ // check for a skipped pixel
+ if (useSkip && skip->getPixel(x, y)) {
+ pix = 0;
+
+ // decode the pixel
+ } else if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
+ bitmap->setPixel(x, y);
+ }
+
+ // update the context
+ cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 0x07;
+ cx1 = ((cx1 << 1) | bitmap->nextPixel(&cxPtr1)) & 0x0f;
+ cx2 = ((cx2 << 1) | pix) & 0x03;
+ }
+ break;
+
+ case 3:
+
+ // set up the context
+ bitmap->getPixelPtr(0, y-1, &cxPtr1);
+ cx1 = bitmap->nextPixel(&cxPtr1);
+ cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);
+ cx2 = 0;
+ bitmap->getPixelPtr(atx[0], y + aty[0], &atPtr0);
+
+ // decode the row
+ for (x = 0; x < w; ++x) {
+
+ // build the context
+ cx = (cx1 << 5) | (cx2 << 1) |
+ bitmap->nextPixel(&atPtr0);
+
+ // check for a skipped pixel
+ if (useSkip && skip->getPixel(x, y)) {
+ pix = 0;
+
+ // decode the pixel
+ } else if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {
+ bitmap->setPixel(x, y);
+ }
+
+ // update the context
+ cx1 = ((cx1 << 1) | bitmap->nextPixel(&cxPtr1)) & 0x1f;
+ cx2 = ((cx2 << 1) | pix) & 0x0f;
+ }
+ break;
+ }
+ }
+ }
+
+ return bitmap;
+}
+
+void JBIG2Stream::readGenericRefinementRegionSeg(Guint segNum, GBool imm,
+ GBool lossless, Guint length,
+ Guint *refSegs,
+ Guint nRefSegs) {
+ JBIG2Bitmap *bitmap, *refBitmap;
+ Guint w, h, x, y, segInfoFlags, extCombOp;
+ Guint flags, templ, tpgrOn;
+ int atx[2], aty[2];
+ JBIG2Segment *seg;
+
+ // region segment info field
+ if (!readULong(&w) || !readULong(&h) ||
+ !readULong(&x) || !readULong(&y) ||
+ !readUByte(&segInfoFlags)) {
+ goto eofError;
+ }
+ extCombOp = segInfoFlags & 7;
+
+ // rest of the generic refinement region segment header
+ if (!readUByte(&flags)) {
+ goto eofError;
+ }
+ templ = flags & 1;
+ tpgrOn = (flags >> 1) & 1;
+
+ // AT flags
+ if (!templ) {
+ if (!readByte(&atx[0]) || !readByte(&aty[0]) ||
+ !readByte(&atx[1]) || !readByte(&aty[1])) {
+ goto eofError;
+ }
+ }
+
+ // resize the page bitmap if needed
+ if (nRefSegs == 0 || imm) {
+ if (pageH == 0xffffffff && y + h > curPageH) {
+ pageBitmap->expand(y + h, pageDefPixel);
+ }
+ }
+
+ // get referenced bitmap
+ if (nRefSegs > 1) {
+ error(getPos(), "Bad reference in JBIG2 generic refinement segment");
+ return;
+ }
+ if (nRefSegs == 1) {
+ seg = findSegment(refSegs[0]);
+ if (seg->getType() != jbig2SegBitmap) {
+ error(getPos(), "Bad bitmap reference in JBIG2 generic refinement segment");
+ return;
+ }
+ refBitmap = (JBIG2Bitmap *)seg;
+ } else {
+ refBitmap = pageBitmap->getSlice(x, y, w, h);
+ }
+
+ // set up the arithmetic decoder
+ resetRefinementStats(templ, NULL);
+ arithDecoder->start();
+
+ // read
+ bitmap = readGenericRefinementRegion(w, h, templ, tpgrOn,
+ refBitmap, 0, 0, atx, aty);
+
+ // combine the region bitmap into the page bitmap
+ if (imm) {
+ pageBitmap->combine(bitmap, x, y, extCombOp);
+ delete bitmap;
+
+ // store the region bitmap
+ } else {
+ bitmap->setSegNum(segNum);
+ segments->append(bitmap);
+ }
+
+ // delete the referenced bitmap
+ if (nRefSegs == 1) {
+ discardSegment(refSegs[0]);
+ } else {
+ delete refBitmap;
+ }
+
+ return;
+
+ eofError:
+ error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+JBIG2Bitmap *JBIG2Stream::readGenericRefinementRegion(int w, int h,
+ int templ, GBool tpgrOn,
+ JBIG2Bitmap *refBitmap,
+ int refDX, int refDY,
+ int *atx, int *aty) {
+ JBIG2Bitmap *bitmap;
+ GBool ltp;
+ Guint ltpCX, cx, cx0, cx2, cx3, cx4, tpgrCX0, tpgrCX1, tpgrCX2;
+ JBIG2BitmapPtr cxPtr0, cxPtr1, cxPtr2, cxPtr3, cxPtr4, cxPtr5, cxPtr6;
+ JBIG2BitmapPtr tpgrCXPtr0, tpgrCXPtr1, tpgrCXPtr2;
+ int x, y, pix;
+
+ bitmap = new JBIG2Bitmap(0, w, h);
+ bitmap->clearToZero();
+
+ // set up the typical row context
+ if (templ) {
+ ltpCX = 0x008;
+ } else {
+ ltpCX = 0x0010;
+ }
+
+ ltp = 0;
+ for (y = 0; y < h; ++y) {
+
+ if (templ) {
+
+ // set up the context
+ bitmap->getPixelPtr(0, y-1, &cxPtr0);
+ cx0 = bitmap->nextPixel(&cxPtr0);
+ bitmap->getPixelPtr(-1, y, &cxPtr1);
+ refBitmap->getPixelPtr(-refDX, y-1-refDY, &cxPtr2);
+ refBitmap->getPixelPtr(-1-refDX, y-refDY, &cxPtr3);
+ cx3 = refBitmap->nextPixel(&cxPtr3);
+ cx3 = (cx3 << 1) | refBitmap->nextPixel(&cxPtr3);
+ refBitmap->getPixelPtr(-refDX, y+1-refDY, &cxPtr4);
+ cx4 = refBitmap->nextPixel(&cxPtr4);
+
+ // set up the typical prediction context
+ tpgrCX0 = tpgrCX1 = tpgrCX2 = 0; // make gcc happy
+ if (tpgrOn) {
+ refBitmap->getPixelPtr(-1-refDX, y-1-refDY, &tpgrCXPtr0);
+ tpgrCX0 = refBitmap->nextPixel(&tpgrCXPtr0);
+ tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0);
+ tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0);
+ refBitmap->getPixelPtr(-1-refDX, y-refDY, &tpgrCXPtr1);
+ tpgrCX1 = refBitmap->nextPixel(&tpgrCXPtr1);
+ tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1);
+ tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1);
+ refBitmap->getPixelPtr(-1-refDX, y+1-refDY, &tpgrCXPtr2);
+ tpgrCX2 = refBitmap->nextPixel(&tpgrCXPtr2);
+ tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
+ tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
+ }
+
+ for (x = 0; x < w; ++x) {
+
+ // update the context
+ cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 7;
+ cx3 = ((cx3 << 1) | refBitmap->nextPixel(&cxPtr3)) & 7;
+ cx4 = ((cx4 << 1) | refBitmap->nextPixel(&cxPtr4)) & 3;
+
+ if (tpgrOn) {
+ // update the typical predictor context
+ tpgrCX0 = ((tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0)) & 7;
+ tpgrCX1 = ((tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1)) & 7;
+ tpgrCX2 = ((tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2)) & 7;
+
+ // check for a "typical" pixel
+ if (arithDecoder->decodeBit(ltpCX, refinementRegionStats)) {
+ ltp = !ltp;
+ }
+ if (tpgrCX0 == 0 && tpgrCX1 == 0 && tpgrCX2 == 0) {
+ bitmap->clearPixel(x, y);
+ continue;
+ } else if (tpgrCX0 == 7 && tpgrCX1 == 7 && tpgrCX2 == 7) {
+ bitmap->setPixel(x, y);
+ continue;
+ }
+ }
+
+ // build the context
+ cx = (cx0 << 7) | (bitmap->nextPixel(&cxPtr1) << 6) |
+ (refBitmap->nextPixel(&cxPtr2) << 5) |
+ (cx3 << 2) | cx4;
+
+ // decode the pixel
+ if ((pix = arithDecoder->decodeBit(cx, refinementRegionStats))) {
+ bitmap->setPixel(x, y);
+ }
+ }
+
+ } else {
+
+ // set up the context
+ bitmap->getPixelPtr(0, y-1, &cxPtr0);
+ cx0 = bitmap->nextPixel(&cxPtr0);
+ bitmap->getPixelPtr(-1, y, &cxPtr1);
+ refBitmap->getPixelPtr(-refDX, y-1-refDY, &cxPtr2);
+ cx2 = refBitmap->nextPixel(&cxPtr2);
+ refBitmap->getPixelPtr(-1-refDX, y-refDY, &cxPtr3);
+ cx3 = refBitmap->nextPixel(&cxPtr3);
+ cx3 = (cx3 << 1) | refBitmap->nextPixel(&cxPtr3);
+ refBitmap->getPixelPtr(-1-refDX, y+1-refDY, &cxPtr4);
+ cx4 = refBitmap->nextPixel(&cxPtr4);
+ cx4 = (cx4 << 1) | refBitmap->nextPixel(&cxPtr4);
+ bitmap->getPixelPtr(atx[0], y+aty[0], &cxPtr5);
+ refBitmap->getPixelPtr(atx[1]-refDX, y+aty[1]-refDY, &cxPtr6);
+
+ // set up the typical prediction context
+ tpgrCX0 = tpgrCX1 = tpgrCX2 = 0; // make gcc happy
+ if (tpgrOn) {
+ refBitmap->getPixelPtr(-1-refDX, y-1-refDY, &tpgrCXPtr0);
+ tpgrCX0 = refBitmap->nextPixel(&tpgrCXPtr0);
+ tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0);
+ tpgrCX0 = (tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0);
+ refBitmap->getPixelPtr(-1-refDX, y-refDY, &tpgrCXPtr1);
+ tpgrCX1 = refBitmap->nextPixel(&tpgrCXPtr1);
+ tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1);
+ tpgrCX1 = (tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1);
+ refBitmap->getPixelPtr(-1-refDX, y+1-refDY, &tpgrCXPtr2);
+ tpgrCX2 = refBitmap->nextPixel(&tpgrCXPtr2);
+ tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
+ tpgrCX2 = (tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2);
+ }
+
+ for (x = 0; x < w; ++x) {
+
+ // update the context
+ cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 3;
+ cx2 = ((cx2 << 1) | refBitmap->nextPixel(&cxPtr2)) & 3;
+ cx3 = ((cx3 << 1) | refBitmap->nextPixel(&cxPtr3)) & 7;
+ cx4 = ((cx4 << 1) | refBitmap->nextPixel(&cxPtr4)) & 7;
+
+ if (tpgrOn) {
+ // update the typical predictor context
+ tpgrCX0 = ((tpgrCX0 << 1) | refBitmap->nextPixel(&tpgrCXPtr0)) & 7;
+ tpgrCX1 = ((tpgrCX1 << 1) | refBitmap->nextPixel(&tpgrCXPtr1)) & 7;
+ tpgrCX2 = ((tpgrCX2 << 1) | refBitmap->nextPixel(&tpgrCXPtr2)) & 7;
+
+ // check for a "typical" pixel
+ if (arithDecoder->decodeBit(ltpCX, refinementRegionStats)) {
+ ltp = !ltp;
+ }
+ if (tpgrCX0 == 0 && tpgrCX1 == 0 && tpgrCX2 == 0) {
+ bitmap->clearPixel(x, y);
+ continue;
+ } else if (tpgrCX0 == 7 && tpgrCX1 == 7 && tpgrCX2 == 7) {
+ bitmap->setPixel(x, y);
+ continue;
+ }
+ }
+
+ // build the context
+ cx = (cx0 << 11) | (bitmap->nextPixel(&cxPtr1) << 10) |
+ (cx2 << 8) | (cx3 << 5) | (cx4 << 2) |
+ (bitmap->nextPixel(&cxPtr5) << 1) |
+ refBitmap->nextPixel(&cxPtr6);
+
+ // decode the pixel
+ if ((pix = arithDecoder->decodeBit(cx, refinementRegionStats))) {
+ bitmap->setPixel(x, y);
+ }
+ }
+ }
+ }
+
+ return bitmap;
+}
+
+void JBIG2Stream::readPageInfoSeg(Guint length) {
+ Guint xRes, yRes, flags, striping;
+
+ if (!readULong(&pageW) || !readULong(&pageH) ||
+ !readULong(&xRes) || !readULong(&yRes) ||
+ !readUByte(&flags) || !readUWord(&striping)) {
+ goto eofError;
+ }
+ pageDefPixel = (flags >> 2) & 1;
+ defCombOp = (flags >> 3) & 3;
+
+ // allocate the page bitmap
+ if (pageH == 0xffffffff) {
+ curPageH = striping & 0x7fff;
+ } else {
+ curPageH = pageH;
+ }
+ pageBitmap = new JBIG2Bitmap(0, pageW, curPageH);
+
+ // default pixel value
+ if (pageDefPixel) {
+ pageBitmap->clearToOne();
+ } else {
+ pageBitmap->clearToZero();
+ }
+
+ return;
+
+ eofError:
+ error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+void JBIG2Stream::readEndOfStripeSeg(Guint length) {
+ Guint i;
+
+ // skip the segment
+ for (i = 0; i < length; ++i) {
+ curStr->getChar();
+ }
+}
+
+void JBIG2Stream::readProfilesSeg(Guint length) {
+ Guint i;
+
+ // skip the segment
+ for (i = 0; i < length; ++i) {
+ curStr->getChar();
+ }
+}
+
+void JBIG2Stream::readCodeTableSeg(Guint segNum, Guint length) {
+ JBIG2HuffmanTable *huffTab;
+ Guint flags, oob, prefixBits, rangeBits;
+ int lowVal, highVal, val;
+ Guint huffTabSize, i;
+
+ if (!readUByte(&flags) || !readLong(&lowVal) || !readLong(&highVal)) {
+ goto eofError;
+ }
+ oob = flags & 1;
+ prefixBits = ((flags >> 1) & 7) + 1;
+ rangeBits = ((flags >> 4) & 7) + 1;
+
+ huffDecoder->reset();
+ huffTabSize = 8;
+ huffTab = (JBIG2HuffmanTable *)
+ gmallocn(huffTabSize, sizeof(JBIG2HuffmanTable));
+ i = 0;
+ val = lowVal;
+ while (val < highVal) {
+ if (i == huffTabSize) {
+ huffTabSize *= 2;
+ huffTab = (JBIG2HuffmanTable *)
+ greallocn(huffTab, huffTabSize, sizeof(JBIG2HuffmanTable));
+ }
+ huffTab[i].val = val;
+ huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
+ huffTab[i].rangeLen = huffDecoder->readBits(rangeBits);
+ val += 1 << huffTab[i].rangeLen;
+ ++i;
+ }
+ if (i + oob + 3 > huffTabSize) {
+ huffTabSize = i + oob + 3;
+ huffTab = (JBIG2HuffmanTable *)
+ greallocn(huffTab, huffTabSize, sizeof(JBIG2HuffmanTable));
+ }
+ huffTab[i].val = lowVal - 1;
+ huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
+ huffTab[i].rangeLen = jbig2HuffmanLOW;
+ ++i;
+ huffTab[i].val = highVal;
+ huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
+ huffTab[i].rangeLen = 32;
+ ++i;
+ if (oob) {
+ huffTab[i].val = 0;
+ huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
+ huffTab[i].rangeLen = jbig2HuffmanOOB;
+ ++i;
+ }
+ huffTab[i].val = 0;
+ huffTab[i].prefixLen = 0;
+ huffTab[i].rangeLen = jbig2HuffmanEOT;
+ huffDecoder->buildTable(huffTab, i);
+
+ // create and store the new table segment
+ segments->append(new JBIG2CodeTable(segNum, huffTab));
+
+ return;
+
+ eofError:
+ error(getPos(), "Unexpected EOF in JBIG2 stream");
+}
+
+void JBIG2Stream::readExtensionSeg(Guint length) {
+ Guint i;
+
+ // skip the segment
+ for (i = 0; i < length; ++i) {
+ curStr->getChar();
+ }
+}
+
+JBIG2Segment *JBIG2Stream::findSegment(Guint segNum) {
+ JBIG2Segment *seg;
+ int i;
+
+ for (i = 0; i < globalSegments->getLength(); ++i) {
+ seg = (JBIG2Segment *)globalSegments->get(i);
+ if (seg->getSegNum() == segNum) {
+ return seg;
+ }
+ }
+ for (i = 0; i < segments->getLength(); ++i) {
+ seg = (JBIG2Segment *)segments->get(i);
+ if (seg->getSegNum() == segNum) {
+ return seg;
+ }
+ }
+ return NULL;
+}
+
+void JBIG2Stream::discardSegment(Guint segNum) {
+ JBIG2Segment *seg;
+ int i;
+
+ for (i = 0; i < globalSegments->getLength(); ++i) {
+ seg = (JBIG2Segment *)globalSegments->get(i);
+ if (seg->getSegNum() == segNum) {
+ globalSegments->del(i);
+ return;
+ }
+ }
+ for (i = 0; i < segments->getLength(); ++i) {
+ seg = (JBIG2Segment *)segments->get(i);
+ if (seg->getSegNum() == segNum) {
+ segments->del(i);
+ return;
+ }
+ }
+}
+
+void JBIG2Stream::resetGenericStats(Guint templ,
+ JArithmeticDecoderStats *prevStats) {
+ int size;
+
+ size = contextSize[templ];
+ if (prevStats && prevStats->getContextSize() == size) {
+ if (genericRegionStats->getContextSize() == size) {
+ genericRegionStats->copyFrom(prevStats);
+ } else {
+ delete genericRegionStats;
+ genericRegionStats = prevStats->copy();
+ }
+ } else {
+ if (genericRegionStats->getContextSize() == size) {
+ genericRegionStats->reset();
+ } else {
+ delete genericRegionStats;
+ genericRegionStats = new JArithmeticDecoderStats(1 << size);
+ }
+ }
+}
+
+void JBIG2Stream::resetRefinementStats(Guint templ,
+ JArithmeticDecoderStats *prevStats) {
+ int size;
+
+ size = refContextSize[templ];
+ if (prevStats && prevStats->getContextSize() == size) {
+ if (refinementRegionStats->getContextSize() == size) {
+ refinementRegionStats->copyFrom(prevStats);
+ } else {
+ delete refinementRegionStats;
+ refinementRegionStats = prevStats->copy();
+ }
+ } else {
+ if (refinementRegionStats->getContextSize() == size) {
+ refinementRegionStats->reset();
+ } else {
+ delete refinementRegionStats;
+ refinementRegionStats = new JArithmeticDecoderStats(1 << size);
+ }
+ }
+}
+
+void JBIG2Stream::resetIntStats(int symCodeLen) {
+ iadhStats->reset();
+ iadwStats->reset();
+ iaexStats->reset();
+ iaaiStats->reset();
+ iadtStats->reset();
+ iaitStats->reset();
+ iafsStats->reset();
+ iadsStats->reset();
+ iardxStats->reset();
+ iardyStats->reset();
+ iardwStats->reset();
+ iardhStats->reset();
+ iariStats->reset();
+ if (iaidStats->getContextSize() == symCodeLen + 1) {
+ iaidStats->reset();
+ } else {
+ delete iaidStats;
+ iaidStats = new JArithmeticDecoderStats(1 << (symCodeLen + 1));
+ }
+}
+
+GBool JBIG2Stream::readUByte(Guint *x) {
+ int c0;
+
+ if ((c0 = curStr->getChar()) == EOF) {
+ return gFalse;
+ }
+ *x = (Guint)c0;
+ return gTrue;
+}
+
+GBool JBIG2Stream::readByte(int *x) {
+ int c0;
+
+ if ((c0 = curStr->getChar()) == EOF) {
+ return gFalse;
+ }
+ *x = c0;
+ if (c0 & 0x80) {
+ *x |= -1 - 0xff;
+ }
+ return gTrue;
+}
+
+GBool JBIG2Stream::readUWord(Guint *x) {
+ int c0, c1;
+
+ if ((c0 = curStr->getChar()) == EOF ||
+ (c1 = curStr->getChar()) == EOF) {
+ return gFalse;
+ }
+ *x = (Guint)((c0 << 8) | c1);
+ return gTrue;
+}
+
+GBool JBIG2Stream::readULong(Guint *x) {
+ int c0, c1, c2, c3;
+
+ if ((c0 = curStr->getChar()) == EOF ||
+ (c1 = curStr->getChar()) == EOF ||
+ (c2 = curStr->getChar()) == EOF ||
+ (c3 = curStr->getChar()) == EOF) {
+ return gFalse;
+ }
+ *x = (Guint)((c0 << 24) | (c1 << 16) | (c2 << 8) | c3);
+ return gTrue;
+}
+
+GBool JBIG2Stream::readLong(int *x) {
+ int c0, c1, c2, c3;
+
+ if ((c0 = curStr->getChar()) == EOF ||
+ (c1 = curStr->getChar()) == EOF ||
+ (c2 = curStr->getChar()) == EOF ||
+ (c3 = curStr->getChar()) == EOF) {
+ return gFalse;
+ }
+ *x = ((c0 << 24) | (c1 << 16) | (c2 << 8) | c3);
+ if (c0 & 0x80) {
+ *x |= -1 - (int)0xffffffff;
+ }
+ return gTrue;
+}
--- /dev/null
+//========================================================================
+//
+// JBIG2Stream.h
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef JBIG2STREAM_H
+#define JBIG2STREAM_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "Object.h"
+#include "Stream.h"
+
+class GList;
+class JBIG2Segment;
+class JBIG2Bitmap;
+class JArithmeticDecoder;
+class JArithmeticDecoderStats;
+class JBIG2HuffmanDecoder;
+struct JBIG2HuffmanTable;
+class JBIG2MMRDecoder;
+
+//------------------------------------------------------------------------
+
+class JBIG2Stream: public FilterStream {
+public:
+
+ JBIG2Stream(Stream *strA, Object *globalsStream);
+ virtual ~JBIG2Stream();
+ virtual StreamKind getKind() { return strJBIG2; }
+ virtual void reset();
+ virtual int getChar();
+ virtual int lookChar();
+ virtual GString *getPSFilter(int psLevel, char *indent);
+ virtual GBool isBinary(GBool last = gTrue);
+
+private:
+
+ void readSegments();
+ GBool readSymbolDictSeg(Guint segNum, Guint length,
+ Guint *refSegs, Guint nRefSegs);
+ void readTextRegionSeg(Guint segNum, GBool imm,
+ GBool lossless, Guint length,
+ Guint *refSegs, Guint nRefSegs);
+ JBIG2Bitmap *readTextRegion(GBool huff, GBool refine,
+ int w, int h,
+ Guint numInstances,
+ Guint logStrips,
+ int numSyms,
+ JBIG2HuffmanTable *symCodeTab,
+ Guint symCodeLen,
+ JBIG2Bitmap **syms,
+ Guint defPixel, Guint combOp,
+ Guint transposed, Guint refCorner,
+ int sOffset,
+ JBIG2HuffmanTable *huffFSTable,
+ JBIG2HuffmanTable *huffDSTable,
+ JBIG2HuffmanTable *huffDTTable,
+ JBIG2HuffmanTable *huffRDWTable,
+ JBIG2HuffmanTable *huffRDHTable,
+ JBIG2HuffmanTable *huffRDXTable,
+ JBIG2HuffmanTable *huffRDYTable,
+ JBIG2HuffmanTable *huffRSizeTable,
+ Guint templ,
+ int *atx, int *aty);
+ void readPatternDictSeg(Guint segNum, Guint length);
+ void readHalftoneRegionSeg(Guint segNum, GBool imm,
+ GBool lossless, Guint length,
+ Guint *refSegs, Guint nRefSegs);
+ void readGenericRegionSeg(Guint segNum, GBool imm,
+ GBool lossless, Guint length);
+ JBIG2Bitmap *readGenericBitmap(GBool mmr, int w, int h,
+ int templ, GBool tpgdOn,
+ GBool useSkip, JBIG2Bitmap *skip,
+ int *atx, int *aty,
+ int mmrDataLength);
+ void readGenericRefinementRegionSeg(Guint segNum, GBool imm,
+ GBool lossless, Guint length,
+ Guint *refSegs,
+ Guint nRefSegs);
+ JBIG2Bitmap *readGenericRefinementRegion(int w, int h,
+ int templ, GBool tpgrOn,
+ JBIG2Bitmap *refBitmap,
+ int refDX, int refDY,
+ int *atx, int *aty);
+ void readPageInfoSeg(Guint length);
+ void readEndOfStripeSeg(Guint length);
+ void readProfilesSeg(Guint length);
+ void readCodeTableSeg(Guint segNum, Guint length);
+ void readExtensionSeg(Guint length);
+ JBIG2Segment *findSegment(Guint segNum);
+ void discardSegment(Guint segNum);
+ void resetGenericStats(Guint templ,
+ JArithmeticDecoderStats *prevStats);
+ void resetRefinementStats(Guint templ,
+ JArithmeticDecoderStats *prevStats);
+ void resetIntStats(int symCodeLen);
+ GBool readUByte(Guint *x);
+ GBool readByte(int *x);
+ GBool readUWord(Guint *x);
+ GBool readULong(Guint *x);
+ GBool readLong(int *x);
+
+ Guint pageW, pageH, curPageH;
+ Guint pageDefPixel;
+ JBIG2Bitmap *pageBitmap;
+ Guint defCombOp;
+ GList *segments; // [JBIG2Segment]
+ GList *globalSegments; // [JBIG2Segment]
+ Stream *curStr;
+ Guchar *dataPtr;
+ Guchar *dataEnd;
+
+ JArithmeticDecoder *arithDecoder;
+ JArithmeticDecoderStats *genericRegionStats;
+ JArithmeticDecoderStats *refinementRegionStats;
+ JArithmeticDecoderStats *iadhStats;
+ JArithmeticDecoderStats *iadwStats;
+ JArithmeticDecoderStats *iaexStats;
+ JArithmeticDecoderStats *iaaiStats;
+ JArithmeticDecoderStats *iadtStats;
+ JArithmeticDecoderStats *iaitStats;
+ JArithmeticDecoderStats *iafsStats;
+ JArithmeticDecoderStats *iadsStats;
+ JArithmeticDecoderStats *iardxStats;
+ JArithmeticDecoderStats *iardyStats;
+ JArithmeticDecoderStats *iardwStats;
+ JArithmeticDecoderStats *iardhStats;
+ JArithmeticDecoderStats *iariStats;
+ JArithmeticDecoderStats *iaidStats;
+ JBIG2HuffmanDecoder *huffDecoder;
+ JBIG2MMRDecoder *mmrDecoder;
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// JPXStream.cc
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "gmem.h"
+#include "Error.h"
+#include "JArithmeticDecoder.h"
+#include "JPXStream.h"
+
+//~ to do:
+// - precincts
+// - ROI
+// - progression order changes
+// - packed packet headers
+// - support for palettes, channel maps, etc.
+// - make sure all needed JP2/JPX subboxes are parsed (readBoxes)
+// - can we assume that QCC segments must come after the QCD segment?
+// - skip EPH markers (readTilePartData)
+// - handle tilePartToEOC in readTilePartData
+// - deal with multiple codeword segments (readTilePartData,
+// readCodeBlockData)
+// - progression orders 2, 3, and 4
+// - in coefficient decoding (readCodeBlockData):
+// - termination pattern: terminate after every coding pass
+// - error resilience segmentation symbol
+// - selective arithmetic coding bypass
+// - vertically causal context formation
+// - coeffs longer than 31 bits (should just ignore the extra bits?)
+// - handle boxes larger than 2^32 bytes
+// - the fixed-point arithmetic won't handle 16-bit pixels
+
+//------------------------------------------------------------------------
+
+// number of contexts for the arithmetic decoder
+#define jpxNContexts 19
+
+#define jpxContextSigProp 0 // 0 - 8: significance prop and cleanup
+#define jpxContextSign 9 // 9 - 13: sign
+#define jpxContextMagRef 14 // 14 -16: magnitude refinement
+#define jpxContextRunLength 17 // cleanup: run length
+#define jpxContextUniform 18 // cleanup: first signif coeff
+
+//------------------------------------------------------------------------
+
+#define jpxPassSigProp 0
+#define jpxPassMagRef 1
+#define jpxPassCleanup 2
+
+//------------------------------------------------------------------------
+
+// arithmetic decoder context for the significance propagation and
+// cleanup passes:
+// [horiz][vert][diag][subband]
+// where subband = 0 for HL
+// = 1 for LH and LL
+// = 2 for HH
+static Guint sigPropContext[3][3][5][3] = {
+ {{{ 0, 0, 0 }, // horiz=0, vert=0, diag=0
+ { 1, 1, 3 }, // horiz=0, vert=0, diag=1
+ { 2, 2, 6 }, // horiz=0, vert=0, diag=2
+ { 2, 2, 8 }, // horiz=0, vert=0, diag=3
+ { 2, 2, 8 }}, // horiz=0, vert=0, diag=4
+ {{ 5, 3, 1 }, // horiz=0, vert=1, diag=0
+ { 6, 3, 4 }, // horiz=0, vert=1, diag=1
+ { 6, 3, 7 }, // horiz=0, vert=1, diag=2
+ { 6, 3, 8 }, // horiz=0, vert=1, diag=3
+ { 6, 3, 8 }}, // horiz=0, vert=1, diag=4
+ {{ 8, 4, 2 }, // horiz=0, vert=2, diag=0
+ { 8, 4, 5 }, // horiz=0, vert=2, diag=1
+ { 8, 4, 7 }, // horiz=0, vert=2, diag=2
+ { 8, 4, 8 }, // horiz=0, vert=2, diag=3
+ { 8, 4, 8 }}}, // horiz=0, vert=2, diag=4
+ {{{ 3, 5, 1 }, // horiz=1, vert=0, diag=0
+ { 3, 6, 4 }, // horiz=1, vert=0, diag=1
+ { 3, 6, 7 }, // horiz=1, vert=0, diag=2
+ { 3, 6, 8 }, // horiz=1, vert=0, diag=3
+ { 3, 6, 8 }}, // horiz=1, vert=0, diag=4
+ {{ 7, 7, 2 }, // horiz=1, vert=1, diag=0
+ { 7, 7, 5 }, // horiz=1, vert=1, diag=1
+ { 7, 7, 7 }, // horiz=1, vert=1, diag=2
+ { 7, 7, 8 }, // horiz=1, vert=1, diag=3
+ { 7, 7, 8 }}, // horiz=1, vert=1, diag=4
+ {{ 8, 7, 2 }, // horiz=1, vert=2, diag=0
+ { 8, 7, 5 }, // horiz=1, vert=2, diag=1
+ { 8, 7, 7 }, // horiz=1, vert=2, diag=2
+ { 8, 7, 8 }, // horiz=1, vert=2, diag=3
+ { 8, 7, 8 }}}, // horiz=1, vert=2, diag=4
+ {{{ 4, 8, 2 }, // horiz=2, vert=0, diag=0
+ { 4, 8, 5 }, // horiz=2, vert=0, diag=1
+ { 4, 8, 7 }, // horiz=2, vert=0, diag=2
+ { 4, 8, 8 }, // horiz=2, vert=0, diag=3
+ { 4, 8, 8 }}, // horiz=2, vert=0, diag=4
+ {{ 7, 8, 2 }, // horiz=2, vert=1, diag=0
+ { 7, 8, 5 }, // horiz=2, vert=1, diag=1
+ { 7, 8, 7 }, // horiz=2, vert=1, diag=2
+ { 7, 8, 8 }, // horiz=2, vert=1, diag=3
+ { 7, 8, 8 }}, // horiz=2, vert=1, diag=4
+ {{ 8, 8, 2 }, // horiz=2, vert=2, diag=0
+ { 8, 8, 5 }, // horiz=2, vert=2, diag=1
+ { 8, 8, 7 }, // horiz=2, vert=2, diag=2
+ { 8, 8, 8 }, // horiz=2, vert=2, diag=3
+ { 8, 8, 8 }}} // horiz=2, vert=2, diag=4
+};
+
+// arithmetic decoder context and xor bit for the sign bit in the
+// significance propagation pass:
+// [horiz][vert][k]
+// where horiz/vert are offset by 2 (i.e., range is -2 .. 2)
+// and k = 0 for the context
+// = 1 for the xor bit
+static Guint signContext[5][5][2] = {
+ {{ 13, 1 }, // horiz=-2, vert=-2
+ { 13, 1 }, // horiz=-2, vert=-1
+ { 12, 1 }, // horiz=-2, vert= 0
+ { 11, 1 }, // horiz=-2, vert=+1
+ { 11, 1 }}, // horiz=-2, vert=+2
+ {{ 13, 1 }, // horiz=-1, vert=-2
+ { 13, 1 }, // horiz=-1, vert=-1
+ { 12, 1 }, // horiz=-1, vert= 0
+ { 11, 1 }, // horiz=-1, vert=+1
+ { 11, 1 }}, // horiz=-1, vert=+2
+ {{ 10, 1 }, // horiz= 0, vert=-2
+ { 10, 1 }, // horiz= 0, vert=-1
+ { 9, 0 }, // horiz= 0, vert= 0
+ { 10, 0 }, // horiz= 0, vert=+1
+ { 10, 0 }}, // horiz= 0, vert=+2
+ {{ 11, 0 }, // horiz=+1, vert=-2
+ { 11, 0 }, // horiz=+1, vert=-1
+ { 12, 0 }, // horiz=+1, vert= 0
+ { 13, 0 }, // horiz=+1, vert=+1
+ { 13, 0 }}, // horiz=+1, vert=+2
+ {{ 11, 0 }, // horiz=+2, vert=-2
+ { 11, 0 }, // horiz=+2, vert=-1
+ { 12, 0 }, // horiz=+2, vert= 0
+ { 13, 0 }, // horiz=+2, vert=+1
+ { 13, 0 }}, // horiz=+2, vert=+2
+};
+
+//------------------------------------------------------------------------
+
+// constants used in the IDWT
+#define idwtAlpha -1.586134342059924
+#define idwtBeta -0.052980118572961
+#define idwtGamma 0.882911075530934
+#define idwtDelta 0.443506852043971
+#define idwtKappa 1.230174104914001
+#define idwtIKappa (1.0 / idwtKappa)
+
+// number of bits to the right of the decimal point for the fixed
+// point arithmetic used in the IDWT
+#define fracBits 16
+
+//------------------------------------------------------------------------
+
+// floor(x / y)
+#define jpxFloorDiv(x, y) ((x) / (y))
+
+// floor(x / 2^y)
+#define jpxFloorDivPow2(x, y) ((x) >> (y))
+
+// ceil(x / y)
+#define jpxCeilDiv(x, y) (((x) + (y) - 1) / (y))
+
+// ceil(x / 2^y)
+#define jpxCeilDivPow2(x, y) (((x) + (1 << (y)) - 1) >> (y))
+
+//------------------------------------------------------------------------
+
+JPXStream::JPXStream(Stream *strA):
+ FilterStream(strA)
+{
+ nComps = 0;
+ bpc = NULL;
+ width = height = 0;
+ haveCS = gFalse;
+ havePalette = gFalse;
+ haveCompMap = gFalse;
+ haveChannelDefn = gFalse;
+
+ img.tiles = NULL;
+ bitBuf = 0;
+ bitBufLen = 0;
+ bitBufSkip = gFalse;
+ byteCount = 0;
+}
+
+JPXStream::~JPXStream() {
+ JPXTile *tile;
+ JPXTileComp *tileComp;
+ JPXResLevel *resLevel;
+ JPXPrecinct *precinct;
+ JPXSubband *subband;
+ JPXCodeBlock *cb;
+ Guint comp, i, k, r, pre, sb;
+
+ gfree(bpc);
+ if (havePalette) {
+ gfree(palette.bpc);
+ gfree(palette.c);
+ }
+ if (haveCompMap) {
+ gfree(compMap.comp);
+ gfree(compMap.type);
+ gfree(compMap.pComp);
+ }
+ if (haveChannelDefn) {
+ gfree(channelDefn.idx);
+ gfree(channelDefn.type);
+ gfree(channelDefn.assoc);
+ }
+
+ if (img.tiles) {
+ for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+ tile = &img.tiles[i];
+ if (tile->tileComps) {
+ for (comp = 0; comp < img.nComps; ++comp) {
+ tileComp = &tile->tileComps[comp];
+ gfree(tileComp->quantSteps);
+ gfree(tileComp->data);
+ gfree(tileComp->buf);
+ if (tileComp->resLevels) {
+ for (r = 0; r <= tileComp->nDecompLevels; ++r) {
+ resLevel = &tileComp->resLevels[r];
+ if (resLevel->precincts) {
+ for (pre = 0; pre < 1; ++pre) {
+ precinct = &resLevel->precincts[pre];
+ if (precinct->subbands) {
+ for (sb = 0; sb < (r == 0 ? 1 : 3); ++sb) {
+ subband = &precinct->subbands[sb];
+ gfree(subband->inclusion);
+ gfree(subband->zeroBitPlane);
+ if (subband->cbs) {
+ for (k = 0; k < subband->nXCBs * subband->nYCBs; ++k) {
+ cb = &subband->cbs[k];
+ gfree(cb->coeffs);
+ if (cb->arithDecoder) {
+ delete cb->arithDecoder;
+ }
+ if (cb->stats) {
+ delete cb->stats;
+ }
+ }
+ gfree(subband->cbs);
+ }
+ }
+ gfree(precinct->subbands);
+ }
+ }
+ gfree(img.tiles[i].tileComps[comp].resLevels[r].precincts);
+ }
+ }
+ gfree(img.tiles[i].tileComps[comp].resLevels);
+ }
+ }
+ gfree(img.tiles[i].tileComps);
+ }
+ }
+ gfree(img.tiles);
+ }
+ delete str;
+}
+
+void JPXStream::reset() {
+ str->reset();
+ if (readBoxes()) {
+ curY = img.yOffset;
+ } else {
+ // readBoxes reported an error, so we go immediately to EOF
+ curY = img.ySize;
+ }
+ curX = img.xOffset;
+ curComp = 0;
+ readBufLen = 0;
+}
+
+int JPXStream::getChar() {
+ int c;
+
+ if (readBufLen < 8) {
+ fillReadBuf();
+ }
+ if (readBufLen == 8) {
+ c = readBuf & 0xff;
+ readBufLen = 0;
+ } else if (readBufLen > 8) {
+ c = (readBuf >> (readBufLen - 8)) & 0xff;
+ readBufLen -= 8;
+ } else if (readBufLen == 0) {
+ c = EOF;
+ } else {
+ c = (readBuf << (8 - readBufLen)) & 0xff;
+ readBufLen = 0;
+ }
+ return c;
+}
+
+int JPXStream::lookChar() {
+ int c;
+
+ if (readBufLen < 8) {
+ fillReadBuf();
+ }
+ if (readBufLen == 8) {
+ c = readBuf & 0xff;
+ } else if (readBufLen > 8) {
+ c = (readBuf >> (readBufLen - 8)) & 0xff;
+ } else if (readBufLen == 0) {
+ c = EOF;
+ } else {
+ c = (readBuf << (8 - readBufLen)) & 0xff;
+ }
+ return c;
+}
+
+void JPXStream::fillReadBuf() {
+ JPXTileComp *tileComp;
+ Guint tileIdx, tx, ty;
+ int pix, pixBits;
+
+ do {
+ if (curY >= img.ySize) {
+ return;
+ }
+ tileIdx = ((curY - img.yTileOffset) / img.yTileSize) * img.nXTiles
+ + (curX - img.xTileOffset) / img.xTileSize;
+#if 1 //~ ignore the palette, assume the PDF ColorSpace object is valid
+ tileComp = &img.tiles[tileIdx].tileComps[curComp];
+#else
+ tileComp = &img.tiles[tileIdx].tileComps[havePalette ? 0 : curComp];
+#endif
+ tx = jpxCeilDiv((curX - img.xTileOffset) % img.xTileSize, tileComp->hSep);
+ ty = jpxCeilDiv((curY - img.yTileOffset) % img.yTileSize, tileComp->vSep);
+ pix = (int)tileComp->data[ty * (tileComp->x1 - tileComp->x0) + tx];
+ pixBits = tileComp->prec;
+#if 1 //~ ignore the palette, assume the PDF ColorSpace object is valid
+ if (++curComp == img.nComps) {
+#else
+ if (havePalette) {
+ if (pix >= 0 && pix < palette.nEntries) {
+ pix = palette.c[pix * palette.nComps + curComp];
+ } else {
+ pix =
+ pixBits = palette.bpc[curComp];
+ }
+ if (++curComp == (Guint)(havePalette ? palette.nComps : img.nComps)) {
+#endif
+ curComp = 0;
+ if (++curX == img.xSize) {
+ curX = img.xOffset;
+ ++curY;
+ }
+ }
+ if (pixBits == 8) {
+ readBuf = (readBuf << 8) | (pix & 0xff);
+ } else {
+ readBuf = (readBuf << pixBits) | (pix & ((1 << pixBits) - 1));
+ }
+ readBufLen += pixBits;
+ } while (readBufLen < 8);
+}
+
+GString *JPXStream::getPSFilter(int psLevel, char *indent) {
+ return NULL;
+}
+
+GBool JPXStream::isBinary(GBool last) {
+ return str->isBinary(gTrue);
+}
+
+void JPXStream::getImageParams(int *bitsPerComponent,
+ StreamColorSpaceMode *csMode) {
+ Guint boxType, boxLen, dataLen, csEnum;
+ Guint bpc1, dummy, i;
+ int csMeth, csPrec, csPrec1, dummy2;
+ StreamColorSpaceMode csMode1;
+ GBool haveBPC, haveCSMode;
+
+ csPrec = 0; // make gcc happy
+ haveBPC = haveCSMode = gFalse;
+ str->reset();
+ if (str->lookChar() == 0xff) {
+ getImageParams2(bitsPerComponent, csMode);
+ } else {
+ while (readBoxHdr(&boxType, &boxLen, &dataLen)) {
+ if (boxType == 0x6a703268) { // JP2 header
+ // skip the superbox
+ } else if (boxType == 0x69686472) { // image header
+ if (readULong(&dummy) &&
+ readULong(&dummy) &&
+ readUWord(&dummy) &&
+ readUByte(&bpc1) &&
+ readUByte(&dummy) &&
+ readUByte(&dummy) &&
+ readUByte(&dummy)) {
+ *bitsPerComponent = bpc1 + 1;
+ haveBPC = gTrue;
+ }
+ } else if (boxType == 0x636F6C72) { // color specification
+ if (readByte(&csMeth) &&
+ readByte(&csPrec1) &&
+ readByte(&dummy2)) {
+ if (csMeth == 1) {
+ if (readULong(&csEnum)) {
+ csMode1 = streamCSNone;
+ if (csEnum == jpxCSBiLevel ||
+ csEnum == jpxCSGrayscale) {
+ csMode1 = streamCSDeviceGray;
+ } else if (csEnum == jpxCSCMYK) {
+ csMode1 = streamCSDeviceCMYK;
+ } else if (csEnum == jpxCSsRGB ||
+ csEnum == jpxCSCISesRGB ||
+ csEnum == jpxCSROMMRGB) {
+ csMode1 = streamCSDeviceRGB;
+ }
+ if (csMode1 != streamCSNone &&
+ (!haveCSMode || csPrec1 > csPrec)) {
+ *csMode = csMode1;
+ csPrec = csPrec1;
+ haveCSMode = gTrue;
+ }
+ for (i = 0; i < dataLen - 7; ++i) {
+ str->getChar();
+ }
+ }
+ } else {
+ for (i = 0; i < dataLen - 3; ++i) {
+ str->getChar();
+ }
+ }
+ }
+ } else if (boxType == 0x6A703263) { // codestream
+ if (!(haveBPC && haveCSMode)) {
+ getImageParams2(bitsPerComponent, csMode);
+ }
+ break;
+ } else {
+ for (i = 0; i < dataLen; ++i) {
+ str->getChar();
+ }
+ }
+ }
+ }
+ str->close();
+}
+
+// Get image parameters from the codestream.
+void JPXStream::getImageParams2(int *bitsPerComponent,
+ StreamColorSpaceMode *csMode) {
+ int segType;
+ Guint segLen, nComps1, bpc1, dummy, i;
+
+ while (readMarkerHdr(&segType, &segLen)) {
+ if (segType == 0x51) { // SIZ - image and tile size
+ if (readUWord(&dummy) &&
+ readULong(&dummy) &&
+ readULong(&dummy) &&
+ readULong(&dummy) &&
+ readULong(&dummy) &&
+ readULong(&dummy) &&
+ readULong(&dummy) &&
+ readULong(&dummy) &&
+ readULong(&dummy) &&
+ readUWord(&nComps1) &&
+ readUByte(&bpc1)) {
+ *bitsPerComponent = (bpc1 & 0x7f) + 1;
+ // if there's no color space info, take a guess
+ if (nComps1 == 1) {
+ *csMode = streamCSDeviceGray;
+ } else if (nComps1 == 3) {
+ *csMode = streamCSDeviceRGB;
+ } else if (nComps1 == 4) {
+ *csMode = streamCSDeviceCMYK;
+ }
+ }
+ break;
+ } else {
+ if (segLen > 2) {
+ for (i = 0; i < segLen - 2; ++i) {
+ str->getChar();
+ }
+ }
+ }
+ }
+}
+
+GBool JPXStream::readBoxes() {
+ Guint boxType, boxLen, dataLen;
+ Guint bpc1, compression, unknownColorspace, ipr;
+ Guint i, j;
+
+ haveImgHdr = gFalse;
+
+ // check for a naked JPEG 2000 codestream (without the JP2/JPX
+ // wrapper) -- this appears to be a violation of the PDF spec, but
+ // Acrobat allows it
+ if (str->lookChar() == 0xff) {
+ error(getPos(), "Naked JPEG 2000 codestream, missing JP2/JPX wrapper");
+ readCodestream(0);
+ nComps = img.nComps;
+ bpc = (Guint *)gmallocn(nComps, sizeof(Guint));
+ for (i = 0; i < nComps; ++i) {
+ bpc[i] = img.tiles[0].tileComps[i].prec;
+ }
+ width = img.xSize - img.xOffset;
+ height = img.ySize - img.yOffset;
+ return gTrue;
+ }
+
+ while (readBoxHdr(&boxType, &boxLen, &dataLen)) {
+ switch (boxType) {
+ case 0x6a703268: // JP2 header
+ // this is a grouping box ('superbox') which has no real
+ // contents and doesn't appear to be used consistently, i.e.,
+ // some things which should be subboxes of the JP2 header box
+ // show up outside of it - so we simply ignore the JP2 header
+ // box
+ break;
+ case 0x69686472: // image header
+ if (!readULong(&height) ||
+ !readULong(&width) ||
+ !readUWord(&nComps) ||
+ !readUByte(&bpc1) ||
+ !readUByte(&compression) ||
+ !readUByte(&unknownColorspace) ||
+ !readUByte(&ipr)) {
+ error(getPos(), "Unexpected EOF in JPX stream");
+ return gFalse;
+ }
+ if (compression != 7) {
+ error(getPos(), "Unknown compression type in JPX stream");
+ return gFalse;
+ }
+ bpc = (Guint *)gmallocn(nComps, sizeof(Guint));
+ for (i = 0; i < nComps; ++i) {
+ bpc[i] = bpc1;
+ }
+ haveImgHdr = gTrue;
+ break;
+ case 0x62706363: // bits per component
+ if (!haveImgHdr) {
+ error(getPos(), "Found bits per component box before image header box in JPX stream");
+ return gFalse;
+ }
+ if (dataLen != nComps) {
+ error(getPos(), "Invalid bits per component box in JPX stream");
+ return gFalse;
+ }
+ for (i = 0; i < nComps; ++i) {
+ if (!readUByte(&bpc[i])) {
+ error(getPos(), "Unexpected EOF in JPX stream");
+ return gFalse;
+ }
+ }
+ break;
+ case 0x636F6C72: // color specification
+ if (!readColorSpecBox(dataLen)) {
+ return gFalse;
+ }
+ break;
+ case 0x70636c72: // palette
+ if (!readUWord(&palette.nEntries) ||
+ !readUByte(&palette.nComps)) {
+ error(getPos(), "Unexpected EOF in JPX stream");
+ return gFalse;
+ }
+ palette.bpc = (Guint *)gmallocn(palette.nComps, sizeof(Guint));
+ palette.c =
+ (int *)gmallocn(palette.nEntries * palette.nComps, sizeof(int));
+ for (i = 0; i < palette.nComps; ++i) {
+ if (!readUByte(&palette.bpc[i])) {
+ error(getPos(), "Unexpected EOF in JPX stream");
+ return gFalse;
+ }
+ ++palette.bpc[i];
+ }
+ for (i = 0; i < palette.nEntries; ++i) {
+ for (j = 0; j < palette.nComps; ++j) {
+ if (!readNBytes(((palette.bpc[j] & 0x7f) + 7) >> 3,
+ (palette.bpc[j] & 0x80) ? gTrue : gFalse,
+ &palette.c[i * palette.nComps + j])) {
+ error(getPos(), "Unexpected EOF in JPX stream");
+ return gFalse;
+ }
+ }
+ }
+ havePalette = gTrue;
+ break;
+ case 0x636d6170: // component mapping
+ compMap.nChannels = dataLen / 4;
+ compMap.comp = (Guint *)gmallocn(compMap.nChannels, sizeof(Guint));
+ compMap.type = (Guint *)gmallocn(compMap.nChannels, sizeof(Guint));
+ compMap.pComp = (Guint *)gmallocn(compMap.nChannels, sizeof(Guint));
+ for (i = 0; i < compMap.nChannels; ++i) {
+ if (!readUWord(&compMap.comp[i]) ||
+ !readUByte(&compMap.type[i]) ||
+ !readUByte(&compMap.pComp[i])) {
+ error(getPos(), "Unexpected EOF in JPX stream");
+ return gFalse;
+ }
+ }
+ haveCompMap = gTrue;
+ break;
+ case 0x63646566: // channel definition
+ if (!readUWord(&channelDefn.nChannels)) {
+ error(getPos(), "Unexpected EOF in JPX stream");
+ return gFalse;
+ }
+ channelDefn.idx =
+ (Guint *)gmallocn(channelDefn.nChannels, sizeof(Guint));
+ channelDefn.type =
+ (Guint *)gmallocn(channelDefn.nChannels, sizeof(Guint));
+ channelDefn.assoc =
+ (Guint *)gmallocn(channelDefn.nChannels, sizeof(Guint));
+ for (i = 0; i < channelDefn.nChannels; ++i) {
+ if (!readUWord(&channelDefn.idx[i]) ||
+ !readUWord(&channelDefn.type[i]) ||
+ !readUWord(&channelDefn.assoc[i])) {
+ error(getPos(), "Unexpected EOF in JPX stream");
+ return gFalse;
+ }
+ }
+ haveChannelDefn = gTrue;
+ break;
+ case 0x6A703263: // contiguous codestream
+ if (!bpc) {
+ error(getPos(), "JPX stream is missing the image header box");
+ }
+ if (!haveCS) {
+ error(getPos(), "JPX stream has no supported color spec");
+ }
+ if (!readCodestream(dataLen)) {
+ return gFalse;
+ }
+ break;
+ default:
+ for (i = 0; i < dataLen; ++i) {
+ if (str->getChar() == EOF) {
+ error(getPos(), "Unexpected EOF in JPX stream");
+ return gFalse;
+ }
+ }
+ break;
+ }
+ }
+ return gTrue;
+}
+
+GBool JPXStream::readColorSpecBox(Guint dataLen) {
+ JPXColorSpec newCS;
+ Guint csApprox, csEnum;
+ Guint i;
+ GBool ok;
+
+ ok = gFalse;
+ if (!readUByte(&newCS.meth) ||
+ !readByte(&newCS.prec) ||
+ !readUByte(&csApprox)) {
+ goto err;
+ }
+ switch (newCS.meth) {
+ case 1: // enumerated colorspace
+ if (!readULong(&csEnum)) {
+ goto err;
+ }
+ newCS.enumerated.type = (JPXColorSpaceType)csEnum;
+ switch (newCS.enumerated.type) {
+ case jpxCSBiLevel:
+ ok = gTrue;
+ break;
+ case jpxCSYCbCr1:
+ ok = gTrue;
+ break;
+ case jpxCSYCbCr2:
+ ok = gTrue;
+ break;
+ case jpxCSYCBCr3:
+ ok = gTrue;
+ break;
+ case jpxCSPhotoYCC:
+ ok = gTrue;
+ break;
+ case jpxCSCMY:
+ ok = gTrue;
+ break;
+ case jpxCSCMYK:
+ ok = gTrue;
+ break;
+ case jpxCSYCCK:
+ ok = gTrue;
+ break;
+ case jpxCSCIELab:
+ if (dataLen == 7 + 7*4) {
+ if (!readULong(&newCS.enumerated.cieLab.rl) ||
+ !readULong(&newCS.enumerated.cieLab.ol) ||
+ !readULong(&newCS.enumerated.cieLab.ra) ||
+ !readULong(&newCS.enumerated.cieLab.oa) ||
+ !readULong(&newCS.enumerated.cieLab.rb) ||
+ !readULong(&newCS.enumerated.cieLab.ob) ||
+ !readULong(&newCS.enumerated.cieLab.il)) {
+ goto err;
+ }
+ } else if (dataLen == 7) {
+ //~ this assumes the 8-bit case
+ newCS.enumerated.cieLab.rl = 100;
+ newCS.enumerated.cieLab.ol = 0;
+ newCS.enumerated.cieLab.ra = 255;
+ newCS.enumerated.cieLab.oa = 128;
+ newCS.enumerated.cieLab.rb = 255;
+ newCS.enumerated.cieLab.ob = 96;
+ newCS.enumerated.cieLab.il = 0x00443530;
+ } else {
+ goto err;
+ }
+ ok = gTrue;
+ break;
+ case jpxCSsRGB:
+ ok = gTrue;
+ break;
+ case jpxCSGrayscale:
+ ok = gTrue;
+ break;
+ case jpxCSBiLevel2:
+ ok = gTrue;
+ break;
+ case jpxCSCIEJab:
+ // not allowed in PDF
+ goto err;
+ case jpxCSCISesRGB:
+ ok = gTrue;
+ break;
+ case jpxCSROMMRGB:
+ ok = gTrue;
+ break;
+ case jpxCSsRGBYCbCr:
+ ok = gTrue;
+ break;
+ case jpxCSYPbPr1125:
+ ok = gTrue;
+ break;
+ case jpxCSYPbPr1250:
+ ok = gTrue;
+ break;
+ default:
+ goto err;
+ }
+ break;
+ case 2: // restricted ICC profile
+ case 3: // any ICC profile (JPX)
+ case 4: // vendor color (JPX)
+ for (i = 0; i < dataLen - 3; ++i) {
+ if (str->getChar() == EOF) {
+ goto err;
+ }
+ }
+ break;
+ }
+
+ if (ok && (!haveCS || newCS.prec > cs.prec)) {
+ cs = newCS;
+ haveCS = gTrue;
+ }
+
+ return gTrue;
+
+ err:
+ error(getPos(), "Error in JPX color spec");
+ return gFalse;
+}
+
+GBool JPXStream::readCodestream(Guint len) {
+ JPXTile *tile;
+ JPXTileComp *tileComp;
+ int segType;
+ GBool haveSIZ, haveCOD, haveQCD, haveSOT;
+ Guint precinctSize, style;
+ Guint segLen, capabilities, nTiles, comp, i, j, r;
+
+ //----- main header
+ haveSIZ = haveCOD = haveQCD = haveSOT = gFalse;
+ do {
+ if (!readMarkerHdr(&segType, &segLen)) {
+ error(getPos(), "Error in JPX codestream");
+ return gFalse;
+ }
+ switch (segType) {
+ case 0x4f: // SOC - start of codestream
+ // marker only
+ break;
+ case 0x51: // SIZ - image and tile size
+ if (!readUWord(&capabilities) ||
+ !readULong(&img.xSize) ||
+ !readULong(&img.ySize) ||
+ !readULong(&img.xOffset) ||
+ !readULong(&img.yOffset) ||
+ !readULong(&img.xTileSize) ||
+ !readULong(&img.yTileSize) ||
+ !readULong(&img.xTileOffset) ||
+ !readULong(&img.yTileOffset) ||
+ !readUWord(&img.nComps)) {
+ error(getPos(), "Error in JPX SIZ marker segment");
+ return gFalse;
+ }
+ if (haveImgHdr && img.nComps != nComps) {
+ error(getPos(), "Different number of components in JPX SIZ marker segment");
+ return gFalse;
+ }
+ img.nXTiles = (img.xSize - img.xTileOffset + img.xTileSize - 1)
+ / img.xTileSize;
+ img.nYTiles = (img.ySize - img.yTileOffset + img.yTileSize - 1)
+ / img.yTileSize;
+ nTiles = img.nXTiles * img.nYTiles;
+ // check for overflow before allocating memory
+ if (nTiles == 0 || nTiles / img.nXTiles != img.nYTiles) {
+ error(getPos(), "Bad tile count in JPX SIZ marker segment");
+ return gFalse;
+ }
+ img.tiles = (JPXTile *)gmallocn(nTiles, sizeof(JPXTile));
+ for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+ img.tiles[i].tileComps = (JPXTileComp *)gmallocn(img.nComps,
+ sizeof(JPXTileComp));
+ for (comp = 0; comp < img.nComps; ++comp) {
+ img.tiles[i].tileComps[comp].quantSteps = NULL;
+ img.tiles[i].tileComps[comp].data = NULL;
+ img.tiles[i].tileComps[comp].buf = NULL;
+ img.tiles[i].tileComps[comp].resLevels = NULL;
+ }
+ }
+ for (comp = 0; comp < img.nComps; ++comp) {
+ if (!readUByte(&img.tiles[0].tileComps[comp].prec) ||
+ !readUByte(&img.tiles[0].tileComps[comp].hSep) ||
+ !readUByte(&img.tiles[0].tileComps[comp].vSep)) {
+ error(getPos(), "Error in JPX SIZ marker segment");
+ return gFalse;
+ }
+ img.tiles[0].tileComps[comp].sgned =
+ (img.tiles[0].tileComps[comp].prec & 0x80) ? gTrue : gFalse;
+ img.tiles[0].tileComps[comp].prec =
+ (img.tiles[0].tileComps[comp].prec & 0x7f) + 1;
+ for (i = 1; i < img.nXTiles * img.nYTiles; ++i) {
+ img.tiles[i].tileComps[comp] = img.tiles[0].tileComps[comp];
+ }
+ }
+ haveSIZ = gTrue;
+ break;
+ case 0x52: // COD - coding style default
+ if (!readUByte(&img.tiles[0].tileComps[0].style) ||
+ !readUByte(&img.tiles[0].progOrder) ||
+ !readUWord(&img.tiles[0].nLayers) ||
+ !readUByte(&img.tiles[0].multiComp) ||
+ !readUByte(&img.tiles[0].tileComps[0].nDecompLevels) ||
+ !readUByte(&img.tiles[0].tileComps[0].codeBlockW) ||
+ !readUByte(&img.tiles[0].tileComps[0].codeBlockH) ||
+ !readUByte(&img.tiles[0].tileComps[0].codeBlockStyle) ||
+ !readUByte(&img.tiles[0].tileComps[0].transform)) {
+ error(getPos(), "Error in JPX COD marker segment");
+ return gFalse;
+ }
+ img.tiles[0].tileComps[0].codeBlockW += 2;
+ img.tiles[0].tileComps[0].codeBlockH += 2;
+ for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+ if (i != 0) {
+ img.tiles[i].progOrder = img.tiles[0].progOrder;
+ img.tiles[i].nLayers = img.tiles[0].nLayers;
+ img.tiles[i].multiComp = img.tiles[0].multiComp;
+ }
+ for (comp = 0; comp < img.nComps; ++comp) {
+ if (!(i == 0 && comp == 0)) {
+ img.tiles[i].tileComps[comp].style =
+ img.tiles[0].tileComps[0].style;
+ img.tiles[i].tileComps[comp].nDecompLevels =
+ img.tiles[0].tileComps[0].nDecompLevels;
+ img.tiles[i].tileComps[comp].codeBlockW =
+ img.tiles[0].tileComps[0].codeBlockW;
+ img.tiles[i].tileComps[comp].codeBlockH =
+ img.tiles[0].tileComps[0].codeBlockH;
+ img.tiles[i].tileComps[comp].codeBlockStyle =
+ img.tiles[0].tileComps[0].codeBlockStyle;
+ img.tiles[i].tileComps[comp].transform =
+ img.tiles[0].tileComps[0].transform;
+ }
+ img.tiles[i].tileComps[comp].resLevels =
+ (JPXResLevel *)gmallocn(
+ (img.tiles[i].tileComps[comp].nDecompLevels + 1),
+ sizeof(JPXResLevel));
+ for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) {
+ img.tiles[i].tileComps[comp].resLevels[r].precincts = NULL;
+ }
+ }
+ }
+ for (r = 0; r <= img.tiles[0].tileComps[0].nDecompLevels; ++r) {
+ if (img.tiles[0].tileComps[0].style & 0x01) {
+ if (!readUByte(&precinctSize)) {
+ error(getPos(), "Error in JPX COD marker segment");
+ return gFalse;
+ }
+ img.tiles[0].tileComps[0].resLevels[r].precinctWidth =
+ precinctSize & 0x0f;
+ img.tiles[0].tileComps[0].resLevels[r].precinctHeight =
+ (precinctSize >> 4) & 0x0f;
+ } else {
+ img.tiles[0].tileComps[0].resLevels[r].precinctWidth = 15;
+ img.tiles[0].tileComps[0].resLevels[r].precinctHeight = 15;
+ }
+ }
+ for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+ for (comp = 0; comp < img.nComps; ++comp) {
+ if (!(i == 0 && comp == 0)) {
+ for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) {
+ img.tiles[i].tileComps[comp].resLevels[r].precinctWidth =
+ img.tiles[0].tileComps[0].resLevels[r].precinctWidth;
+ img.tiles[i].tileComps[comp].resLevels[r].precinctHeight =
+ img.tiles[0].tileComps[0].resLevels[r].precinctHeight;
+ }
+ }
+ }
+ }
+ haveCOD = gTrue;
+ break;
+ case 0x53: // COC - coding style component
+ if (!haveCOD) {
+ error(getPos(), "JPX COC marker segment before COD segment");
+ return gFalse;
+ }
+ if ((img.nComps > 256 && !readUWord(&comp)) ||
+ (img.nComps <= 256 && !readUByte(&comp)) ||
+ comp >= img.nComps ||
+ !readUByte(&style) ||
+ !readUByte(&img.tiles[0].tileComps[comp].nDecompLevels) ||
+ !readUByte(&img.tiles[0].tileComps[comp].codeBlockW) ||
+ !readUByte(&img.tiles[0].tileComps[comp].codeBlockH) ||
+ !readUByte(&img.tiles[0].tileComps[comp].codeBlockStyle) ||
+ !readUByte(&img.tiles[0].tileComps[comp].transform)) {
+ error(getPos(), "Error in JPX COC marker segment");
+ return gFalse;
+ }
+ img.tiles[0].tileComps[comp].style =
+ (img.tiles[0].tileComps[comp].style & ~1) | (style & 1);
+ img.tiles[0].tileComps[comp].codeBlockW += 2;
+ img.tiles[0].tileComps[comp].codeBlockH += 2;
+ for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+ if (i != 0) {
+ img.tiles[i].tileComps[comp].style =
+ img.tiles[0].tileComps[comp].style;
+ img.tiles[i].tileComps[comp].nDecompLevels =
+ img.tiles[0].tileComps[comp].nDecompLevels;
+ img.tiles[i].tileComps[comp].codeBlockW =
+ img.tiles[0].tileComps[comp].codeBlockW;
+ img.tiles[i].tileComps[comp].codeBlockH =
+ img.tiles[0].tileComps[comp].codeBlockH;
+ img.tiles[i].tileComps[comp].codeBlockStyle =
+ img.tiles[0].tileComps[comp].codeBlockStyle;
+ img.tiles[i].tileComps[comp].transform =
+ img.tiles[0].tileComps[comp].transform;
+ }
+ img.tiles[i].tileComps[comp].resLevels =
+ (JPXResLevel *)greallocn(
+ img.tiles[i].tileComps[comp].resLevels,
+ (img.tiles[i].tileComps[comp].nDecompLevels + 1),
+ sizeof(JPXResLevel));
+ for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) {
+ img.tiles[i].tileComps[comp].resLevels[r].precincts = NULL;
+ }
+ }
+ for (r = 0; r <= img.tiles[0].tileComps[comp].nDecompLevels; ++r) {
+ if (img.tiles[0].tileComps[comp].style & 0x01) {
+ if (!readUByte(&precinctSize)) {
+ error(getPos(), "Error in JPX COD marker segment");
+ return gFalse;
+ }
+ img.tiles[0].tileComps[comp].resLevels[r].precinctWidth =
+ precinctSize & 0x0f;
+ img.tiles[0].tileComps[comp].resLevels[r].precinctHeight =
+ (precinctSize >> 4) & 0x0f;
+ } else {
+ img.tiles[0].tileComps[comp].resLevels[r].precinctWidth = 15;
+ img.tiles[0].tileComps[comp].resLevels[r].precinctHeight = 15;
+ }
+ }
+ for (i = 1; i < img.nXTiles * img.nYTiles; ++i) {
+ for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) {
+ img.tiles[i].tileComps[comp].resLevels[r].precinctWidth =
+ img.tiles[0].tileComps[comp].resLevels[r].precinctWidth;
+ img.tiles[i].tileComps[comp].resLevels[r].precinctHeight =
+ img.tiles[0].tileComps[comp].resLevels[r].precinctHeight;
+ }
+ }
+ break;
+ case 0x5c: // QCD - quantization default
+ if (!readUByte(&img.tiles[0].tileComps[0].quantStyle)) {
+ error(getPos(), "Error in JPX QCD marker segment");
+ return gFalse;
+ }
+ if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x00) {
+ img.tiles[0].tileComps[0].nQuantSteps = segLen - 3;
+ img.tiles[0].tileComps[0].quantSteps =
+ (Guint *)greallocn(img.tiles[0].tileComps[0].quantSteps,
+ img.tiles[0].tileComps[0].nQuantSteps,
+ sizeof(Guint));
+ for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) {
+ if (!readUByte(&img.tiles[0].tileComps[0].quantSteps[i])) {
+ error(getPos(), "Error in JPX QCD marker segment");
+ return gFalse;
+ }
+ }
+ } else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x01) {
+ img.tiles[0].tileComps[0].nQuantSteps = 1;
+ img.tiles[0].tileComps[0].quantSteps =
+ (Guint *)greallocn(img.tiles[0].tileComps[0].quantSteps,
+ img.tiles[0].tileComps[0].nQuantSteps,
+ sizeof(Guint));
+ if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[0])) {
+ error(getPos(), "Error in JPX QCD marker segment");
+ return gFalse;
+ }
+ } else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x02) {
+ img.tiles[0].tileComps[0].nQuantSteps = (segLen - 3) / 2;
+ img.tiles[0].tileComps[0].quantSteps =
+ (Guint *)greallocn(img.tiles[0].tileComps[0].quantSteps,
+ img.tiles[0].tileComps[0].nQuantSteps,
+ sizeof(Guint));
+ for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) {
+ if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[i])) {
+ error(getPos(), "Error in JPX QCD marker segment");
+ return gFalse;
+ }
+ }
+ } else {
+ error(getPos(), "Error in JPX QCD marker segment");
+ return gFalse;
+ }
+ for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+ for (comp = 0; comp < img.nComps; ++comp) {
+ if (!(i == 0 && comp == 0)) {
+ img.tiles[i].tileComps[comp].quantStyle =
+ img.tiles[0].tileComps[0].quantStyle;
+ img.tiles[i].tileComps[comp].nQuantSteps =
+ img.tiles[0].tileComps[0].nQuantSteps;
+ img.tiles[i].tileComps[comp].quantSteps =
+ (Guint *)greallocn(img.tiles[i].tileComps[comp].quantSteps,
+ img.tiles[0].tileComps[0].nQuantSteps,
+ sizeof(Guint));
+ for (j = 0; j < img.tiles[0].tileComps[0].nQuantSteps; ++j) {
+ img.tiles[i].tileComps[comp].quantSteps[j] =
+ img.tiles[0].tileComps[0].quantSteps[j];
+ }
+ }
+ }
+ }
+ haveQCD = gTrue;
+ break;
+ case 0x5d: // QCC - quantization component
+ if (!haveQCD) {
+ error(getPos(), "JPX QCC marker segment before QCD segment");
+ return gFalse;
+ }
+ if ((img.nComps > 256 && !readUWord(&comp)) ||
+ (img.nComps <= 256 && !readUByte(&comp)) ||
+ comp >= img.nComps ||
+ !readUByte(&img.tiles[0].tileComps[comp].quantStyle)) {
+ error(getPos(), "Error in JPX QCC marker segment");
+ return gFalse;
+ }
+ if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x00) {
+ img.tiles[0].tileComps[comp].nQuantSteps =
+ segLen - (img.nComps > 256 ? 5 : 4);
+ img.tiles[0].tileComps[comp].quantSteps =
+ (Guint *)greallocn(img.tiles[0].tileComps[comp].quantSteps,
+ img.tiles[0].tileComps[comp].nQuantSteps,
+ sizeof(Guint));
+ for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) {
+ if (!readUByte(&img.tiles[0].tileComps[comp].quantSteps[i])) {
+ error(getPos(), "Error in JPX QCC marker segment");
+ return gFalse;
+ }
+ }
+ } else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x01) {
+ img.tiles[0].tileComps[comp].nQuantSteps = 1;
+ img.tiles[0].tileComps[comp].quantSteps =
+ (Guint *)greallocn(img.tiles[0].tileComps[comp].quantSteps,
+ img.tiles[0].tileComps[comp].nQuantSteps,
+ sizeof(Guint));
+ if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[0])) {
+ error(getPos(), "Error in JPX QCC marker segment");
+ return gFalse;
+ }
+ } else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x02) {
+ img.tiles[0].tileComps[comp].nQuantSteps =
+ (segLen - (img.nComps > 256 ? 5 : 4)) / 2;
+ img.tiles[0].tileComps[comp].quantSteps =
+ (Guint *)greallocn(img.tiles[0].tileComps[comp].quantSteps,
+ img.tiles[0].tileComps[comp].nQuantSteps,
+ sizeof(Guint));
+ for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) {
+ if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[i])) {
+ error(getPos(), "Error in JPX QCD marker segment");
+ return gFalse;
+ }
+ }
+ } else {
+ error(getPos(), "Error in JPX QCC marker segment");
+ return gFalse;
+ }
+ for (i = 1; i < img.nXTiles * img.nYTiles; ++i) {
+ img.tiles[i].tileComps[comp].quantStyle =
+ img.tiles[0].tileComps[comp].quantStyle;
+ img.tiles[i].tileComps[comp].nQuantSteps =
+ img.tiles[0].tileComps[comp].nQuantSteps;
+ img.tiles[i].tileComps[comp].quantSteps =
+ (Guint *)greallocn(img.tiles[i].tileComps[comp].quantSteps,
+ img.tiles[0].tileComps[comp].nQuantSteps,
+ sizeof(Guint));
+ for (j = 0; j < img.tiles[0].tileComps[comp].nQuantSteps; ++j) {
+ img.tiles[i].tileComps[comp].quantSteps[j] =
+ img.tiles[0].tileComps[comp].quantSteps[j];
+ }
+ }
+ break;
+ case 0x5e: // RGN - region of interest
+#if 1 //~ ROI is unimplemented
+ fprintf(stderr, "RGN\n");
+ for (i = 0; i < segLen - 2; ++i) {
+ if (str->getChar() == EOF) {
+ error(getPos(), "Error in JPX PPM marker segment");
+ return gFalse;
+ }
+ }
+#else
+ if ((img.nComps > 256 && !readUWord(&comp)) ||
+ (img.nComps <= 256 && !readUByte(&comp)) ||
+ comp >= img.nComps ||
+ !readUByte(&compInfo[comp].defROI.style) ||
+ !readUByte(&compInfo[comp].defROI.shift)) {
+ error(getPos(), "Error in JPX RGN marker segment");
+ return gFalse;
+ }
+#endif
+ break;
+ case 0x5f: // POC - progression order change
+#if 1 //~ progression order changes are unimplemented
+ fprintf(stderr, "POC\n");
+ for (i = 0; i < segLen - 2; ++i) {
+ if (str->getChar() == EOF) {
+ error(getPos(), "Error in JPX PPM marker segment");
+ return gFalse;
+ }
+ }
+#else
+ nProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7);
+ progs = (JPXProgOrder *)gmallocn(nProgs, sizeof(JPXProgOrder));
+ for (i = 0; i < nProgs; ++i) {
+ if (!readUByte(&progs[i].startRes) ||
+ !(img.nComps > 256 && readUWord(&progs[i].startComp)) ||
+ !(img.nComps <= 256 && readUByte(&progs[i].startComp)) ||
+ !readUWord(&progs[i].endLayer) ||
+ !readUByte(&progs[i].endRes) ||
+ !(img.nComps > 256 && readUWord(&progs[i].endComp)) ||
+ !(img.nComps <= 256 && readUByte(&progs[i].endComp)) ||
+ !readUByte(&progs[i].progOrder)) {
+ error(getPos(), "Error in JPX POC marker segment");
+ return gFalse;
+ }
+ }
+#endif
+ break;
+ case 0x60: // PPM - packed packet headers, main header
+#if 1 //~ packed packet headers are unimplemented
+ fprintf(stderr, "PPM\n");
+ for (i = 0; i < segLen - 2; ++i) {
+ if (str->getChar() == EOF) {
+ error(getPos(), "Error in JPX PPM marker segment");
+ return gFalse;
+ }
+ }
+#endif
+ break;
+ case 0x55: // TLM - tile-part lengths
+ // skipped
+ for (i = 0; i < segLen - 2; ++i) {
+ if (str->getChar() == EOF) {
+ error(getPos(), "Error in JPX TLM marker segment");
+ return gFalse;
+ }
+ }
+ break;
+ case 0x57: // PLM - packet length, main header
+ // skipped
+ for (i = 0; i < segLen - 2; ++i) {
+ if (str->getChar() == EOF) {
+ error(getPos(), "Error in JPX PLM marker segment");
+ return gFalse;
+ }
+ }
+ break;
+ case 0x63: // CRG - component registration
+ // skipped
+ for (i = 0; i < segLen - 2; ++i) {
+ if (str->getChar() == EOF) {
+ error(getPos(), "Error in JPX CRG marker segment");
+ return gFalse;
+ }
+ }
+ break;
+ case 0x64: // COM - comment
+ // skipped
+ for (i = 0; i < segLen - 2; ++i) {
+ if (str->getChar() == EOF) {
+ error(getPos(), "Error in JPX COM marker segment");
+ return gFalse;
+ }
+ }
+ break;
+ case 0x90: // SOT - start of tile
+ haveSOT = gTrue;
+ break;
+ default:
+ error(getPos(), "Unknown marker segment %02x in JPX stream", segType);
+ for (i = 0; i < segLen - 2; ++i) {
+ if (str->getChar() == EOF) {
+ break;
+ }
+ }
+ break;
+ }
+ } while (!haveSOT);
+
+ if (!haveSIZ) {
+ error(getPos(), "Missing SIZ marker segment in JPX stream");
+ return gFalse;
+ }
+ if (!haveCOD) {
+ error(getPos(), "Missing COD marker segment in JPX stream");
+ return gFalse;
+ }
+ if (!haveQCD) {
+ error(getPos(), "Missing QCD marker segment in JPX stream");
+ return gFalse;
+ }
+
+ //----- read the tile-parts
+ while (1) {
+ if (!readTilePart()) {
+ return gFalse;
+ }
+ if (!readMarkerHdr(&segType, &segLen)) {
+ error(getPos(), "Error in JPX codestream");
+ return gFalse;
+ }
+ if (segType != 0x90) { // SOT - start of tile
+ break;
+ }
+ }
+
+ if (segType != 0xd9) { // EOC - end of codestream
+ error(getPos(), "Missing EOC marker in JPX codestream");
+ return gFalse;
+ }
+
+ //----- finish decoding the image
+ for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
+ tile = &img.tiles[i];
+ for (comp = 0; comp < img.nComps; ++comp) {
+ tileComp = &tile->tileComps[comp];
+ inverseTransform(tileComp);
+ }
+ if (!inverseMultiCompAndDC(tile)) {
+ return gFalse;
+ }
+ }
+
+ //~ can free memory below tileComps here, and also tileComp.buf
+
+ return gTrue;
+}
+
+GBool JPXStream::readTilePart() {
+ JPXTile *tile;
+ JPXTileComp *tileComp;
+ JPXResLevel *resLevel;
+ JPXPrecinct *precinct;
+ JPXSubband *subband;
+ JPXCodeBlock *cb;
+ GBool haveSOD;
+ Guint tileIdx, tilePartLen, tilePartIdx, nTileParts;
+ GBool tilePartToEOC;
+ Guint precinctSize, style;
+ Guint n, nSBs, nx, ny, sbx0, sby0, comp, segLen;
+ Guint i, j, k, cbX, cbY, r, pre, sb, cbi;
+ int segType, level;
+
+ // process the SOT marker segment
+ if (!readUWord(&tileIdx) ||
+ !readULong(&tilePartLen) ||
+ !readUByte(&tilePartIdx) ||
+ !readUByte(&nTileParts)) {
+ error(getPos(), "Error in JPX SOT marker segment");
+ return gFalse;
+ }
+
+ if (tileIdx >= img.nXTiles * img.nYTiles) {
+ error(getPos(), "Weird tile index in JPX stream");
+ return gFalse;
+ }
+
+ tilePartToEOC = tilePartLen == 0;
+ tilePartLen -= 12; // subtract size of SOT segment
+
+ haveSOD = gFalse;
+ do {
+ if (!readMarkerHdr(&segType, &segLen)) {
+ error(getPos(), "Error in JPX tile-part codestream");
+ return gFalse;
+ }
+ tilePartLen -= 2 + segLen;
+ switch (segType) {
+ case 0x52: // COD - coding style default
+ if (!readUByte(&img.tiles[tileIdx].tileComps[0].style) ||
+ !readUByte(&img.tiles[tileIdx].progOrder) ||
+ !readUWord(&img.tiles[tileIdx].nLayers) ||
+ !readUByte(&img.tiles[tileIdx].multiComp) ||
+ !readUByte(&img.tiles[tileIdx].tileComps[0].nDecompLevels) ||
+ !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockW) ||
+ !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockH) ||
+ !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockStyle) ||
+ !readUByte(&img.tiles[tileIdx].tileComps[0].transform)) {
+ error(getPos(), "Error in JPX COD marker segment");
+ return gFalse;
+ }
+ img.tiles[tileIdx].tileComps[0].codeBlockW += 2;
+ img.tiles[tileIdx].tileComps[0].codeBlockH += 2;
+ for (comp = 0; comp < img.nComps; ++comp) {
+ if (comp != 0) {
+ img.tiles[tileIdx].tileComps[comp].style =
+ img.tiles[tileIdx].tileComps[0].style;
+ img.tiles[tileIdx].tileComps[comp].nDecompLevels =
+ img.tiles[tileIdx].tileComps[0].nDecompLevels;
+ img.tiles[tileIdx].tileComps[comp].codeBlockW =
+ img.tiles[tileIdx].tileComps[0].codeBlockW;
+ img.tiles[tileIdx].tileComps[comp].codeBlockH =
+ img.tiles[tileIdx].tileComps[0].codeBlockH;
+ img.tiles[tileIdx].tileComps[comp].codeBlockStyle =
+ img.tiles[tileIdx].tileComps[0].codeBlockStyle;
+ img.tiles[tileIdx].tileComps[comp].transform =
+ img.tiles[tileIdx].tileComps[0].transform;
+ }
+ img.tiles[tileIdx].tileComps[comp].resLevels =
+ (JPXResLevel *)greallocn(
+ img.tiles[tileIdx].tileComps[comp].resLevels,
+ (img.tiles[tileIdx].tileComps[comp].nDecompLevels + 1),
+ sizeof(JPXResLevel));
+ for (r = 0;
+ r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels;
+ ++r) {
+ img.tiles[tileIdx].tileComps[comp].resLevels[r].precincts = NULL;
+ }
+ }
+ for (r = 0; r <= img.tiles[tileIdx].tileComps[0].nDecompLevels; ++r) {
+ if (img.tiles[tileIdx].tileComps[0].style & 0x01) {
+ if (!readUByte(&precinctSize)) {
+ error(getPos(), "Error in JPX COD marker segment");
+ return gFalse;
+ }
+ img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth =
+ precinctSize & 0x0f;
+ img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight =
+ (precinctSize >> 4) & 0x0f;
+ } else {
+ img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth = 15;
+ img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight = 15;
+ }
+ }
+ for (comp = 1; comp < img.nComps; ++comp) {
+ for (r = 0;
+ r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels;
+ ++r) {
+ img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth =
+ img.tiles[tileIdx].tileComps[0].resLevels[r].precinctWidth;
+ img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight =
+ img.tiles[tileIdx].tileComps[0].resLevels[r].precinctHeight;
+ }
+ }
+ break;
+ case 0x53: // COC - coding style component
+ if ((img.nComps > 256 && !readUWord(&comp)) ||
+ (img.nComps <= 256 && !readUByte(&comp)) ||
+ comp >= img.nComps ||
+ !readUByte(&style) ||
+ !readUByte(&img.tiles[tileIdx].tileComps[comp].nDecompLevels) ||
+ !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockW) ||
+ !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockH) ||
+ !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockStyle) ||
+ !readUByte(&img.tiles[tileIdx].tileComps[comp].transform)) {
+ error(getPos(), "Error in JPX COC marker segment");
+ return gFalse;
+ }
+ img.tiles[tileIdx].tileComps[comp].style =
+ (img.tiles[tileIdx].tileComps[comp].style & ~1) | (style & 1);
+ img.tiles[tileIdx].tileComps[comp].codeBlockW += 2;
+ img.tiles[tileIdx].tileComps[comp].codeBlockH += 2;
+ img.tiles[tileIdx].tileComps[comp].resLevels =
+ (JPXResLevel *)greallocn(
+ img.tiles[tileIdx].tileComps[comp].resLevels,
+ (img.tiles[tileIdx].tileComps[comp].nDecompLevels + 1),
+ sizeof(JPXResLevel));
+ for (r = 0; r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; ++r) {
+ img.tiles[tileIdx].tileComps[comp].resLevels[r].precincts = NULL;
+ }
+ for (r = 0; r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; ++r) {
+ if (img.tiles[tileIdx].tileComps[comp].style & 0x01) {
+ if (!readUByte(&precinctSize)) {
+ error(getPos(), "Error in JPX COD marker segment");
+ return gFalse;
+ }
+ img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth =
+ precinctSize & 0x0f;
+ img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight =
+ (precinctSize >> 4) & 0x0f;
+ } else {
+ img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctWidth = 15;
+ img.tiles[tileIdx].tileComps[comp].resLevels[r].precinctHeight = 15;
+ }
+ }
+ break;
+ case 0x5c: // QCD - quantization default
+ if (!readUByte(&img.tiles[tileIdx].tileComps[0].quantStyle)) {
+ error(getPos(), "Error in JPX QCD marker segment");
+ return gFalse;
+ }
+ if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x00) {
+ img.tiles[tileIdx].tileComps[0].nQuantSteps =
+ segLen - 3;
+ img.tiles[tileIdx].tileComps[0].quantSteps =
+ (Guint *)greallocn(img.tiles[tileIdx].tileComps[0].quantSteps,
+ img.tiles[tileIdx].tileComps[0].nQuantSteps,
+ sizeof(Guint));
+ for (i = 0; i < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++i) {
+ if (!readUByte(&img.tiles[tileIdx].tileComps[0].quantSteps[i])) {
+ error(getPos(), "Error in JPX QCD marker segment");
+ return gFalse;
+ }
+ }
+ } else if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x01) {
+ img.tiles[tileIdx].tileComps[0].nQuantSteps = 1;
+ img.tiles[tileIdx].tileComps[0].quantSteps =
+ (Guint *)greallocn(img.tiles[tileIdx].tileComps[0].quantSteps,
+ img.tiles[tileIdx].tileComps[0].nQuantSteps,
+ sizeof(Guint));
+ if (!readUWord(&img.tiles[tileIdx].tileComps[0].quantSteps[0])) {
+ error(getPos(), "Error in JPX QCD marker segment");
+ return gFalse;
+ }
+ } else if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x02) {
+ img.tiles[tileIdx].tileComps[0].nQuantSteps = (segLen - 3) / 2;
+ img.tiles[tileIdx].tileComps[0].quantSteps =
+ (Guint *)greallocn(img.tiles[tileIdx].tileComps[0].quantSteps,
+ img.tiles[tileIdx].tileComps[0].nQuantSteps,
+ sizeof(Guint));
+ for (i = 0; i < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++i) {
+ if (!readUWord(&img.tiles[tileIdx].tileComps[0].quantSteps[i])) {
+ error(getPos(), "Error in JPX QCD marker segment");
+ return gFalse;
+ }
+ }
+ } else {
+ error(getPos(), "Error in JPX QCD marker segment");
+ return gFalse;
+ }
+ for (comp = 1; comp < img.nComps; ++comp) {
+ img.tiles[tileIdx].tileComps[comp].quantStyle =
+ img.tiles[tileIdx].tileComps[0].quantStyle;
+ img.tiles[tileIdx].tileComps[comp].nQuantSteps =
+ img.tiles[tileIdx].tileComps[0].nQuantSteps;
+ img.tiles[tileIdx].tileComps[comp].quantSteps =
+ (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps,
+ img.tiles[tileIdx].tileComps[0].nQuantSteps,
+ sizeof(Guint));
+ for (j = 0; j < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++j) {
+ img.tiles[tileIdx].tileComps[comp].quantSteps[j] =
+ img.tiles[tileIdx].tileComps[0].quantSteps[j];
+ }
+ }
+ break;
+ case 0x5d: // QCC - quantization component
+ if ((img.nComps > 256 && !readUWord(&comp)) ||
+ (img.nComps <= 256 && !readUByte(&comp)) ||
+ comp >= img.nComps ||
+ !readUByte(&img.tiles[tileIdx].tileComps[comp].quantStyle)) {
+ error(getPos(), "Error in JPX QCC marker segment");
+ return gFalse;
+ }
+ if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f) == 0x00) {
+ img.tiles[tileIdx].tileComps[comp].nQuantSteps =
+ segLen - (img.nComps > 256 ? 5 : 4);
+ img.tiles[tileIdx].tileComps[comp].quantSteps =
+ (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps,
+ img.tiles[tileIdx].tileComps[comp].nQuantSteps,
+ sizeof(Guint));
+ for (i = 0; i < img.tiles[tileIdx].tileComps[comp].nQuantSteps; ++i) {
+ if (!readUByte(&img.tiles[tileIdx].tileComps[comp].quantSteps[i])) {
+ error(getPos(), "Error in JPX QCC marker segment");
+ return gFalse;
+ }
+ }
+ } else if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f)
+ == 0x01) {
+ img.tiles[tileIdx].tileComps[comp].nQuantSteps = 1;
+ img.tiles[tileIdx].tileComps[comp].quantSteps =
+ (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps,
+ img.tiles[tileIdx].tileComps[comp].nQuantSteps,
+ sizeof(Guint));
+ if (!readUWord(&img.tiles[tileIdx].tileComps[comp].quantSteps[0])) {
+ error(getPos(), "Error in JPX QCC marker segment");
+ return gFalse;
+ }
+ } else if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f)
+ == 0x02) {
+ img.tiles[tileIdx].tileComps[comp].nQuantSteps =
+ (segLen - (img.nComps > 256 ? 5 : 4)) / 2;
+ img.tiles[tileIdx].tileComps[comp].quantSteps =
+ (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps,
+ img.tiles[tileIdx].tileComps[comp].nQuantSteps,
+ sizeof(Guint));
+ for (i = 0; i < img.tiles[tileIdx].tileComps[comp].nQuantSteps; ++i) {
+ if (!readUWord(&img.tiles[tileIdx].tileComps[comp].quantSteps[i])) {
+ error(getPos(), "Error in JPX QCD marker segment");
+ return gFalse;
+ }
+ }
+ } else {
+ error(getPos(), "Error in JPX QCC marker segment");
+ return gFalse;
+ }
+ break;
+ case 0x5e: // RGN - region of interest
+#if 1 //~ ROI is unimplemented
+ fprintf(stderr, "RGN\n");
+ for (i = 0; i < segLen - 2; ++i) {
+ if (str->getChar() == EOF) {
+ error(getPos(), "Error in JPX PPM marker segment");
+ return gFalse;
+ }
+ }
+#else
+ if ((img.nComps > 256 && !readUWord(&comp)) ||
+ (img.nComps <= 256 && !readUByte(&comp)) ||
+ comp >= img.nComps ||
+ !readUByte(&compInfo[comp].roi.style) ||
+ !readUByte(&compInfo[comp].roi.shift)) {
+ error(getPos(), "Error in JPX RGN marker segment");
+ return gFalse;
+ }
+#endif
+ break;
+ case 0x5f: // POC - progression order change
+#if 1 //~ progression order changes are unimplemented
+ fprintf(stderr, "POC\n");
+ for (i = 0; i < segLen - 2; ++i) {
+ if (str->getChar() == EOF) {
+ error(getPos(), "Error in JPX PPM marker segment");
+ return gFalse;
+ }
+ }
+#else
+ nTileProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7);
+ tileProgs = (JPXProgOrder *)gmallocn(nTileProgs, sizeof(JPXProgOrder));
+ for (i = 0; i < nTileProgs; ++i) {
+ if (!readUByte(&tileProgs[i].startRes) ||
+ !(img.nComps > 256 && readUWord(&tileProgs[i].startComp)) ||
+ !(img.nComps <= 256 && readUByte(&tileProgs[i].startComp)) ||
+ !readUWord(&tileProgs[i].endLayer) ||
+ !readUByte(&tileProgs[i].endRes) ||
+ !(img.nComps > 256 && readUWord(&tileProgs[i].endComp)) ||
+ !(img.nComps <= 256 && readUByte(&tileProgs[i].endComp)) ||
+ !readUByte(&tileProgs[i].progOrder)) {
+ error(getPos(), "Error in JPX POC marker segment");
+ return gFalse;
+ }
+ }
+#endif
+ break;
+ case 0x61: // PPT - packed packet headers, tile-part hdr
+#if 1 //~ packed packet headers are unimplemented
+ fprintf(stderr, "PPT\n");
+ for (i = 0; i < segLen - 2; ++i) {
+ if (str->getChar() == EOF) {
+ error(getPos(), "Error in JPX PPT marker segment");
+ return gFalse;
+ }
+ }
+#endif
+ case 0x58: // PLT - packet length, tile-part header
+ // skipped
+ for (i = 0; i < segLen - 2; ++i) {
+ if (str->getChar() == EOF) {
+ error(getPos(), "Error in JPX PLT marker segment");
+ return gFalse;
+ }
+ }
+ break;
+ case 0x64: // COM - comment
+ // skipped
+ for (i = 0; i < segLen - 2; ++i) {
+ if (str->getChar() == EOF) {
+ error(getPos(), "Error in JPX COM marker segment");
+ return gFalse;
+ }
+ }
+ break;
+ case 0x93: // SOD - start of data
+ haveSOD = gTrue;
+ break;
+ default:
+ error(getPos(), "Unknown marker segment %02x in JPX tile-part stream",
+ segType);
+ for (i = 0; i < segLen - 2; ++i) {
+ if (str->getChar() == EOF) {
+ break;
+ }
+ }
+ break;
+ }
+ } while (!haveSOD);
+
+ //----- initialize the tile, precincts, and code-blocks
+ if (tilePartIdx == 0) {
+ tile = &img.tiles[tileIdx];
+ i = tileIdx / img.nXTiles;
+ j = tileIdx % img.nXTiles;
+ if ((tile->x0 = img.xTileOffset + j * img.xTileSize) < img.xOffset) {
+ tile->x0 = img.xOffset;
+ }
+ if ((tile->y0 = img.yTileOffset + i * img.yTileSize) < img.yOffset) {
+ tile->y0 = img.yOffset;
+ }
+ if ((tile->x1 = img.xTileOffset + (j + 1) * img.xTileSize) > img.xSize) {
+ tile->x1 = img.xSize;
+ }
+ if ((tile->y1 = img.yTileOffset + (i + 1) * img.yTileSize) > img.ySize) {
+ tile->y1 = img.ySize;
+ }
+ tile->comp = 0;
+ tile->res = 0;
+ tile->precinct = 0;
+ tile->layer = 0;
+ tile->maxNDecompLevels = 0;
+ for (comp = 0; comp < img.nComps; ++comp) {
+ tileComp = &tile->tileComps[comp];
+ if (tileComp->nDecompLevels > tile->maxNDecompLevels) {
+ tile->maxNDecompLevels = tileComp->nDecompLevels;
+ }
+ tileComp->x0 = jpxCeilDiv(tile->x0, tileComp->hSep);
+ tileComp->y0 = jpxCeilDiv(tile->y0, tileComp->hSep);
+ tileComp->x1 = jpxCeilDiv(tile->x1, tileComp->hSep);
+ tileComp->y1 = jpxCeilDiv(tile->y1, tileComp->hSep);
+ tileComp->cbW = 1 << tileComp->codeBlockW;
+ tileComp->cbH = 1 << tileComp->codeBlockH;
+ tileComp->data = (int *)gmallocn((tileComp->x1 - tileComp->x0) *
+ (tileComp->y1 - tileComp->y0),
+ sizeof(int));
+ if (tileComp->x1 - tileComp->x0 > tileComp->y1 - tileComp->y0) {
+ n = tileComp->x1 - tileComp->x0;
+ } else {
+ n = tileComp->y1 - tileComp->y0;
+ }
+ tileComp->buf = (int *)gmallocn(n + 8, sizeof(int));
+ for (r = 0; r <= tileComp->nDecompLevels; ++r) {
+ resLevel = &tileComp->resLevels[r];
+ k = r == 0 ? tileComp->nDecompLevels
+ : tileComp->nDecompLevels - r + 1;
+ resLevel->x0 = jpxCeilDivPow2(tileComp->x0, k);
+ resLevel->y0 = jpxCeilDivPow2(tileComp->y0, k);
+ resLevel->x1 = jpxCeilDivPow2(tileComp->x1, k);
+ resLevel->y1 = jpxCeilDivPow2(tileComp->y1, k);
+ if (r == 0) {
+ resLevel->bx0[0] = resLevel->x0;
+ resLevel->by0[0] = resLevel->y0;
+ resLevel->bx1[0] = resLevel->x1;
+ resLevel->by1[0] = resLevel->y1;
+ } else {
+ resLevel->bx0[0] = jpxCeilDivPow2(tileComp->x0 - (1 << (k-1)), k);
+ resLevel->by0[0] = resLevel->y0;
+ resLevel->bx1[0] = jpxCeilDivPow2(tileComp->x1 - (1 << (k-1)), k);
+ resLevel->by1[0] = resLevel->y1;
+ resLevel->bx0[1] = resLevel->x0;
+ resLevel->by0[1] = jpxCeilDivPow2(tileComp->y0 - (1 << (k-1)), k);
+ resLevel->bx1[1] = resLevel->x1;
+ resLevel->by1[1] = jpxCeilDivPow2(tileComp->y1 - (1 << (k-1)), k);
+ resLevel->bx0[2] = jpxCeilDivPow2(tileComp->x0 - (1 << (k-1)), k);
+ resLevel->by0[2] = jpxCeilDivPow2(tileComp->y0 - (1 << (k-1)), k);
+ resLevel->bx1[2] = jpxCeilDivPow2(tileComp->x1 - (1 << (k-1)), k);
+ resLevel->by1[2] = jpxCeilDivPow2(tileComp->y1 - (1 << (k-1)), k);
+ }
+ resLevel->precincts = (JPXPrecinct *)gmallocn(1, sizeof(JPXPrecinct));
+ for (pre = 0; pre < 1; ++pre) {
+ precinct = &resLevel->precincts[pre];
+ precinct->x0 = resLevel->x0;
+ precinct->y0 = resLevel->y0;
+ precinct->x1 = resLevel->x1;
+ precinct->y1 = resLevel->y1;
+ nSBs = r == 0 ? 1 : 3;
+ precinct->subbands =
+ (JPXSubband *)gmallocn(nSBs, sizeof(JPXSubband));
+ for (sb = 0; sb < nSBs; ++sb) {
+ subband = &precinct->subbands[sb];
+ subband->x0 = resLevel->bx0[sb];
+ subband->y0 = resLevel->by0[sb];
+ subband->x1 = resLevel->bx1[sb];
+ subband->y1 = resLevel->by1[sb];
+ subband->nXCBs = jpxCeilDivPow2(subband->x1,
+ tileComp->codeBlockW)
+ - jpxFloorDivPow2(subband->x0,
+ tileComp->codeBlockW);
+ subband->nYCBs = jpxCeilDivPow2(subband->y1,
+ tileComp->codeBlockH)
+ - jpxFloorDivPow2(subband->y0,
+ tileComp->codeBlockH);
+ n = subband->nXCBs > subband->nYCBs ? subband->nXCBs
+ : subband->nYCBs;
+ for (subband->maxTTLevel = 0, --n;
+ n;
+ ++subband->maxTTLevel, n >>= 1) ;
+ n = 0;
+ for (level = subband->maxTTLevel; level >= 0; --level) {
+ nx = jpxCeilDivPow2(subband->nXCBs, level);
+ ny = jpxCeilDivPow2(subband->nYCBs, level);
+ n += nx * ny;
+ }
+ subband->inclusion =
+ (JPXTagTreeNode *)gmallocn(n, sizeof(JPXTagTreeNode));
+ subband->zeroBitPlane =
+ (JPXTagTreeNode *)gmallocn(n, sizeof(JPXTagTreeNode));
+ for (k = 0; k < n; ++k) {
+ subband->inclusion[k].finished = gFalse;
+ subband->inclusion[k].val = 0;
+ subband->zeroBitPlane[k].finished = gFalse;
+ subband->zeroBitPlane[k].val = 0;
+ }
+ subband->cbs = (JPXCodeBlock *)gmallocn(subband->nXCBs *
+ subband->nYCBs,
+ sizeof(JPXCodeBlock));
+ sbx0 = jpxFloorDivPow2(subband->x0, tileComp->codeBlockW);
+ sby0 = jpxFloorDivPow2(subband->y0, tileComp->codeBlockH);
+ cb = subband->cbs;
+ for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
+ for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
+ cb->x0 = (sbx0 + cbX) << tileComp->codeBlockW;
+ cb->x1 = cb->x0 + tileComp->cbW;
+ if (subband->x0 > cb->x0) {
+ cb->x0 = subband->x0;
+ }
+ if (subband->x1 < cb->x1) {
+ cb->x1 = subband->x1;
+ }
+ cb->y0 = (sby0 + cbY) << tileComp->codeBlockH;
+ cb->y1 = cb->y0 + tileComp->cbH;
+ if (subband->y0 > cb->y0) {
+ cb->y0 = subband->y0;
+ }
+ if (subband->y1 < cb->y1) {
+ cb->y1 = subband->y1;
+ }
+ cb->seen = gFalse;
+ cb->lBlock = 3;
+ cb->nextPass = jpxPassCleanup;
+ cb->nZeroBitPlanes = 0;
+ cb->coeffs =
+ (JPXCoeff *)gmallocn((1 << (tileComp->codeBlockW
+ + tileComp->codeBlockH)),
+ sizeof(JPXCoeff));
+ for (cbi = 0;
+ cbi < (Guint)(1 << (tileComp->codeBlockW
+ + tileComp->codeBlockH));
+ ++cbi) {
+ cb->coeffs[cbi].flags = 0;
+ cb->coeffs[cbi].len = 0;
+ cb->coeffs[cbi].mag = 0;
+ }
+ cb->arithDecoder = NULL;
+ cb->stats = NULL;
+ ++cb;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return readTilePartData(tileIdx, tilePartLen, tilePartToEOC);
+}
+
+GBool JPXStream::readTilePartData(Guint tileIdx,
+ Guint tilePartLen, GBool tilePartToEOC) {
+ JPXTile *tile;
+ JPXTileComp *tileComp;
+ JPXResLevel *resLevel;
+ JPXPrecinct *precinct;
+ JPXSubband *subband;
+ JPXCodeBlock *cb;
+ Guint ttVal;
+ Guint bits, cbX, cbY, nx, ny, i, j, n, sb;
+ int level;
+
+ tile = &img.tiles[tileIdx];
+
+ // read all packets from this tile-part
+ while (1) {
+ if (tilePartToEOC) {
+ //~ peek for an EOC marker
+ } else if (tilePartLen == 0) {
+ break;
+ }
+
+ tileComp = &tile->tileComps[tile->comp];
+ resLevel = &tileComp->resLevels[tile->res];
+ precinct = &resLevel->precincts[tile->precinct];
+
+ //----- packet header
+
+ // zero-length flag
+ if (!readBits(1, &bits)) {
+ goto err;
+ }
+ if (!bits) {
+ // packet is empty -- clear all code-block inclusion flags
+ for (sb = 0; sb < (tile->res == 0 ? 1 : 3); ++sb) {
+ subband = &precinct->subbands[sb];
+ for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
+ for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
+ cb = &subband->cbs[cbY * subband->nXCBs + cbX];
+ cb->included = gFalse;
+ }
+ }
+ }
+ } else {
+
+ for (sb = 0; sb < (tile->res == 0 ? 1 : 3); ++sb) {
+ subband = &precinct->subbands[sb];
+ for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
+ for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
+ cb = &subband->cbs[cbY * subband->nXCBs + cbX];
+
+ // skip code-blocks with no coefficients
+ if (cb->x0 >= cb->x1 || cb->y0 >= cb->y1) {
+ cb->included = gFalse;
+ continue;
+ }
+
+ // code-block inclusion
+ if (cb->seen) {
+ if (!readBits(1, &cb->included)) {
+ goto err;
+ }
+ } else {
+ ttVal = 0;
+ i = 0;
+ for (level = subband->maxTTLevel; level >= 0; --level) {
+ nx = jpxCeilDivPow2(subband->nXCBs, level);
+ ny = jpxCeilDivPow2(subband->nYCBs, level);
+ j = i + (cbY >> level) * nx + (cbX >> level);
+ if (!subband->inclusion[j].finished &&
+ !subband->inclusion[j].val) {
+ subband->inclusion[j].val = ttVal;
+ } else {
+ ttVal = subband->inclusion[j].val;
+ }
+ while (!subband->inclusion[j].finished &&
+ ttVal <= tile->layer) {
+ if (!readBits(1, &bits)) {
+ goto err;
+ }
+ if (bits == 1) {
+ subband->inclusion[j].finished = gTrue;
+ } else {
+ ++ttVal;
+ }
+ }
+ subband->inclusion[j].val = ttVal;
+ if (ttVal > tile->layer) {
+ break;
+ }
+ i += nx * ny;
+ }
+ cb->included = level < 0;
+ }
+
+ if (cb->included) {
+
+ // zero bit-plane count
+ if (!cb->seen) {
+ ttVal = 0;
+ i = 0;
+ for (level = subband->maxTTLevel; level >= 0; --level) {
+ nx = jpxCeilDivPow2(subband->nXCBs, level);
+ ny = jpxCeilDivPow2(subband->nYCBs, level);
+ j = i + (cbY >> level) * nx + (cbX >> level);
+ if (!subband->zeroBitPlane[j].finished &&
+ !subband->zeroBitPlane[j].val) {
+ subband->zeroBitPlane[j].val = ttVal;
+ } else {
+ ttVal = subband->zeroBitPlane[j].val;
+ }
+ while (!subband->zeroBitPlane[j].finished) {
+ if (!readBits(1, &bits)) {
+ goto err;
+ }
+ if (bits == 1) {
+ subband->zeroBitPlane[j].finished = gTrue;
+ } else {
+ ++ttVal;
+ }
+ }
+ subband->zeroBitPlane[j].val = ttVal;
+ i += nx * ny;
+ }
+ cb->nZeroBitPlanes = ttVal;
+ }
+
+ // number of coding passes
+ if (!readBits(1, &bits)) {
+ goto err;
+ }
+ if (bits == 0) {
+ cb->nCodingPasses = 1;
+ } else {
+ if (!readBits(1, &bits)) {
+ goto err;
+ }
+ if (bits == 0) {
+ cb->nCodingPasses = 2;
+ } else {
+ if (!readBits(2, &bits)) {
+ goto err;
+ }
+ if (bits < 3) {
+ cb->nCodingPasses = 3 + bits;
+ } else {
+ if (!readBits(5, &bits)) {
+ goto err;
+ }
+ if (bits < 31) {
+ cb->nCodingPasses = 6 + bits;
+ } else {
+ if (!readBits(7, &bits)) {
+ goto err;
+ }
+ cb->nCodingPasses = 37 + bits;
+ }
+ }
+ }
+ }
+
+ // update Lblock
+ while (1) {
+ if (!readBits(1, &bits)) {
+ goto err;
+ }
+ if (!bits) {
+ break;
+ }
+ ++cb->lBlock;
+ }
+
+ // length of compressed data
+ //~ deal with multiple codeword segments
+ for (n = cb->lBlock, i = cb->nCodingPasses >> 1;
+ i;
+ ++n, i >>= 1) ;
+ if (!readBits(n, &cb->dataLen)) {
+ goto err;
+ }
+ }
+ }
+ }
+ }
+ }
+ tilePartLen -= byteCount;
+ clearBitBuf();
+
+ //----- packet data
+
+ for (sb = 0; sb < (tile->res == 0 ? 1 : 3); ++sb) {
+ subband = &precinct->subbands[sb];
+ for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
+ for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
+ cb = &subband->cbs[cbY * subband->nXCBs + cbX];
+ if (cb->included) {
+ if (!readCodeBlockData(tileComp, resLevel, precinct, subband,
+ tile->res, sb, cb)) {
+ return gFalse;
+ }
+ tilePartLen -= cb->dataLen;
+ cb->seen = gTrue;
+ }
+ }
+ }
+ }
+
+ //----- next packet
+
+ switch (tile->progOrder) {
+ case 0: // layer, resolution level, component, precinct
+ if (++tile->comp == img.nComps) {
+ tile->comp = 0;
+ if (++tile->res == tile->maxNDecompLevels + 1) {
+ tile->res = 0;
+ if (++tile->layer == tile->nLayers) {
+ tile->layer = 0;
+ }
+ }
+ }
+ break;
+ case 1: // resolution level, layer, component, precinct
+ if (++tile->comp == img.nComps) {
+ tile->comp = 0;
+ if (++tile->layer == tile->nLayers) {
+ tile->layer = 0;
+ if (++tile->res == tile->maxNDecompLevels + 1) {
+ tile->res = 0;
+ }
+ }
+ }
+ break;
+ case 2: // resolution level, precinct, component, layer
+ //~ this isn't correct -- see B.12.1.3
+ if (++tile->layer == tile->nLayers) {
+ tile->layer = 0;
+ if (++tile->comp == img.nComps) {
+ tile->comp = 0;
+ if (++tile->res == tile->maxNDecompLevels + 1) {
+ tile->res = 0;
+ }
+ }
+ }
+ break;
+ case 3: // precinct, component, resolution level, layer
+ //~ this isn't correct -- see B.12.1.4
+ if (++tile->layer == tile->nLayers) {
+ tile->layer = 0;
+ if (++tile->res == tile->maxNDecompLevels + 1) {
+ tile->res = 0;
+ if (++tile->comp == img.nComps) {
+ tile->comp = 0;
+ }
+ }
+ }
+ break;
+ case 4: // component, precinct, resolution level, layer
+ //~ this isn't correct -- see B.12.1.5
+ if (++tile->layer == tile->nLayers) {
+ tile->layer = 0;
+ if (++tile->res == tile->maxNDecompLevels + 1) {
+ tile->res = 0;
+ if (++tile->comp == img.nComps) {
+ tile->comp = 0;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ return gTrue;
+
+ err:
+ error(getPos(), "Error in JPX stream");
+ return gFalse;
+}
+
+GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
+ JPXResLevel *resLevel,
+ JPXPrecinct *precinct,
+ JPXSubband *subband,
+ Guint res, Guint sb,
+ JPXCodeBlock *cb) {
+ JPXCoeff *coeff0, *coeff1, *coeff;
+ Guint horiz, vert, diag, all, cx, xorBit;
+ int horizSign, vertSign;
+ Guint i, x, y0, y1, y2;
+
+ if (cb->arithDecoder) {
+ cb->arithDecoder->restart(cb->dataLen);
+ } else {
+ cb->arithDecoder = new JArithmeticDecoder();
+ cb->arithDecoder->setStream(str, cb->dataLen);
+ cb->arithDecoder->start();
+ cb->stats = new JArithmeticDecoderStats(jpxNContexts);
+ cb->stats->setEntry(jpxContextSigProp, 4, 0);
+ cb->stats->setEntry(jpxContextRunLength, 3, 0);
+ cb->stats->setEntry(jpxContextUniform, 46, 0);
+ }
+
+ for (i = 0; i < cb->nCodingPasses; ++i) {
+ switch (cb->nextPass) {
+
+ //----- significance propagation pass
+ case jpxPassSigProp:
+ for (y0 = cb->y0, coeff0 = cb->coeffs;
+ y0 < cb->y1;
+ y0 += 4, coeff0 += 4 << tileComp->codeBlockW) {
+ for (x = cb->x0, coeff1 = coeff0;
+ x < cb->x1;
+ ++x, ++coeff1) {
+ for (y1 = 0, coeff = coeff1;
+ y1 < 4 && y0+y1 < cb->y1;
+ ++y1, coeff += tileComp->cbW) {
+ if (!(coeff->flags & jpxCoeffSignificant)) {
+ horiz = vert = diag = 0;
+ horizSign = vertSign = 2;
+ if (x > cb->x0) {
+ if (coeff[-1].flags & jpxCoeffSignificant) {
+ ++horiz;
+ horizSign += (coeff[-1].flags & jpxCoeffSign) ? -1 : 1;
+ }
+ if (y0+y1 > cb->y0) {
+ diag += (coeff[-(int)tileComp->cbW - 1].flags
+ >> jpxCoeffSignificantB) & 1;
+ }
+ if (y0+y1 < cb->y1 - 1) {
+ diag += (coeff[tileComp->cbW - 1].flags
+ >> jpxCoeffSignificantB) & 1;
+ }
+ }
+ if (x < cb->x1 - 1) {
+ if (coeff[1].flags & jpxCoeffSignificant) {
+ ++horiz;
+ horizSign += (coeff[1].flags & jpxCoeffSign) ? -1 : 1;
+ }
+ if (y0+y1 > cb->y0) {
+ diag += (coeff[-(int)tileComp->cbW + 1].flags
+ >> jpxCoeffSignificantB) & 1;
+ }
+ if (y0+y1 < cb->y1 - 1) {
+ diag += (coeff[tileComp->cbW + 1].flags
+ >> jpxCoeffSignificantB) & 1;
+ }
+ }
+ if (y0+y1 > cb->y0) {
+ if (coeff[-(int)tileComp->cbW].flags & jpxCoeffSignificant) {
+ ++vert;
+ vertSign += (coeff[-(int)tileComp->cbW].flags & jpxCoeffSign)
+ ? -1 : 1;
+ }
+ }
+ if (y0+y1 < cb->y1 - 1) {
+ if (coeff[tileComp->cbW].flags & jpxCoeffSignificant) {
+ ++vert;
+ vertSign += (coeff[tileComp->cbW].flags & jpxCoeffSign)
+ ? -1 : 1;
+ }
+ }
+ cx = sigPropContext[horiz][vert][diag][res == 0 ? 1 : sb];
+ if (cx != 0) {
+ if (cb->arithDecoder->decodeBit(cx, cb->stats)) {
+ coeff->flags |= jpxCoeffSignificant | jpxCoeffFirstMagRef;
+ coeff->mag = (coeff->mag << 1) | 1;
+ cx = signContext[horizSign][vertSign][0];
+ xorBit = signContext[horizSign][vertSign][1];
+ if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {
+ coeff->flags |= jpxCoeffSign;
+ }
+ }
+ ++coeff->len;
+ coeff->flags |= jpxCoeffTouched;
+ }
+ }
+ }
+ }
+ }
+ ++cb->nextPass;
+ break;
+
+ //----- magnitude refinement pass
+ case jpxPassMagRef:
+ for (y0 = cb->y0, coeff0 = cb->coeffs;
+ y0 < cb->y1;
+ y0 += 4, coeff0 += 4 << tileComp->codeBlockW) {
+ for (x = cb->x0, coeff1 = coeff0;
+ x < cb->x1;
+ ++x, ++coeff1) {
+ for (y1 = 0, coeff = coeff1;
+ y1 < 4 && y0+y1 < cb->y1;
+ ++y1, coeff += tileComp->cbW) {
+ if ((coeff->flags & jpxCoeffSignificant) &&
+ !(coeff->flags & jpxCoeffTouched)) {
+ if (coeff->flags & jpxCoeffFirstMagRef) {
+ all = 0;
+ if (x > cb->x0) {
+ all += (coeff[-1].flags >> jpxCoeffSignificantB) & 1;
+ if (y0+y1 > cb->y0) {
+ all += (coeff[-(int)tileComp->cbW - 1].flags
+ >> jpxCoeffSignificantB) & 1;
+ }
+ if (y0+y1 < cb->y1 - 1) {
+ all += (coeff[tileComp->cbW - 1].flags
+ >> jpxCoeffSignificantB) & 1;
+ }
+ }
+ if (x < cb->x1 - 1) {
+ all += (coeff[1].flags >> jpxCoeffSignificantB) & 1;
+ if (y0+y1 > cb->y0) {
+ all += (coeff[-(int)tileComp->cbW + 1].flags
+ >> jpxCoeffSignificantB) & 1;
+ }
+ if (y0+y1 < cb->y1 - 1) {
+ all += (coeff[tileComp->cbW + 1].flags
+ >> jpxCoeffSignificantB) & 1;
+ }
+ }
+ if (y0+y1 > cb->y0) {
+ all += (coeff[-(int)tileComp->cbW].flags
+ >> jpxCoeffSignificantB) & 1;
+ }
+ if (y0+y1 < cb->y1 - 1) {
+ all += (coeff[tileComp->cbW].flags
+ >> jpxCoeffSignificantB) & 1;
+ }
+ cx = all ? 15 : 14;
+ } else {
+ cx = 16;
+ }
+ coeff->mag = (coeff->mag << 1) |
+ cb->arithDecoder->decodeBit(cx, cb->stats);
+ ++coeff->len;
+ coeff->flags |= jpxCoeffTouched;
+ coeff->flags &= ~jpxCoeffFirstMagRef;
+ }
+ }
+ }
+ }
+ ++cb->nextPass;
+ break;
+
+ //----- cleanup pass
+ case jpxPassCleanup:
+ for (y0 = cb->y0, coeff0 = cb->coeffs;
+ y0 < cb->y1;
+ y0 += 4, coeff0 += 4 << tileComp->codeBlockW) {
+ for (x = cb->x0, coeff1 = coeff0;
+ x < cb->x1;
+ ++x, ++coeff1) {
+ y1 = 0;
+ if (y0 + 3 < cb->y1 &&
+ !(coeff1->flags & jpxCoeffTouched) &&
+ !(coeff1[tileComp->cbW].flags & jpxCoeffTouched) &&
+ !(coeff1[2 * tileComp->cbW].flags & jpxCoeffTouched) &&
+ !(coeff1[3 * tileComp->cbW].flags & jpxCoeffTouched) &&
+ (x == cb->x0 || y0 == cb->y0 ||
+ !(coeff1[-(int)tileComp->cbW - 1].flags
+ & jpxCoeffSignificant)) &&
+ (y0 == cb->y0 ||
+ !(coeff1[-(int)tileComp->cbW].flags
+ & jpxCoeffSignificant)) &&
+ (x == cb->x1 - 1 || y0 == cb->y0 ||
+ !(coeff1[-(int)tileComp->cbW + 1].flags
+ & jpxCoeffSignificant)) &&
+ (x == cb->x0 ||
+ (!(coeff1[-1].flags & jpxCoeffSignificant) &&
+ !(coeff1[tileComp->cbW - 1].flags
+ & jpxCoeffSignificant) &&
+ !(coeff1[2 * tileComp->cbW - 1].flags
+ & jpxCoeffSignificant) &&
+ !(coeff1[3 * tileComp->cbW - 1].flags
+ & jpxCoeffSignificant))) &&
+ (x == cb->x1 - 1 ||
+ (!(coeff1[1].flags & jpxCoeffSignificant) &&
+ !(coeff1[tileComp->cbW + 1].flags
+ & jpxCoeffSignificant) &&
+ !(coeff1[2 * tileComp->cbW + 1].flags
+ & jpxCoeffSignificant) &&
+ !(coeff1[3 * tileComp->cbW + 1].flags
+ & jpxCoeffSignificant))) &&
+ (x == cb->x0 || y0+4 == cb->y1 ||
+ !(coeff1[4 * tileComp->cbW - 1].flags & jpxCoeffSignificant)) &&
+ (y0+4 == cb->y1 ||
+ !(coeff1[4 * tileComp->cbW].flags & jpxCoeffSignificant)) &&
+ (x == cb->x1 - 1 || y0+4 == cb->y1 ||
+ !(coeff1[4 * tileComp->cbW + 1].flags
+ & jpxCoeffSignificant))) {
+ if (cb->arithDecoder->decodeBit(jpxContextRunLength, cb->stats)) {
+ y1 = cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats);
+ y1 = (y1 << 1) |
+ cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats);
+ for (y2 = 0, coeff = coeff1;
+ y2 < y1;
+ ++y2, coeff += tileComp->cbW) {
+ ++coeff->len;
+ }
+ coeff->flags |= jpxCoeffSignificant | jpxCoeffFirstMagRef;
+ coeff->mag = (coeff->mag << 1) | 1;
+ ++coeff->len;
+ cx = signContext[2][2][0];
+ xorBit = signContext[2][2][1];
+ if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {
+ coeff->flags |= jpxCoeffSign;
+ }
+ ++y1;
+ } else {
+ for (y1 = 0, coeff = coeff1;
+ y1 < 4;
+ ++y1, coeff += tileComp->cbW) {
+ ++coeff->len;
+ }
+ y1 = 4;
+ }
+ }
+ for (coeff = &coeff1[y1 << tileComp->codeBlockW];
+ y1 < 4 && y0 + y1 < cb->y1;
+ ++y1, coeff += tileComp->cbW) {
+ if (!(coeff->flags & jpxCoeffTouched)) {
+ horiz = vert = diag = 0;
+ horizSign = vertSign = 2;
+ if (x > cb->x0) {
+ if (coeff[-1].flags & jpxCoeffSignificant) {
+ ++horiz;
+ horizSign += (coeff[-1].flags & jpxCoeffSign) ? -1 : 1;
+ }
+ if (y0+y1 > cb->y0) {
+ diag += (coeff[-(int)tileComp->cbW - 1].flags
+ >> jpxCoeffSignificantB) & 1;
+ }
+ if (y0+y1 < cb->y1 - 1) {
+ diag += (coeff[tileComp->cbW - 1].flags
+ >> jpxCoeffSignificantB) & 1;
+ }
+ }
+ if (x < cb->x1 - 1) {
+ if (coeff[1].flags & jpxCoeffSignificant) {
+ ++horiz;
+ horizSign += (coeff[1].flags & jpxCoeffSign) ? -1 : 1;
+ }
+ if (y0+y1 > cb->y0) {
+ diag += (coeff[-(int)tileComp->cbW + 1].flags
+ >> jpxCoeffSignificantB) & 1;
+ }
+ if (y0+y1 < cb->y1 - 1) {
+ diag += (coeff[tileComp->cbW + 1].flags
+ >> jpxCoeffSignificantB) & 1;
+ }
+ }
+ if (y0+y1 > cb->y0) {
+ if (coeff[-(int)tileComp->cbW].flags & jpxCoeffSignificant) {
+ ++vert;
+ vertSign += (coeff[-(int)tileComp->cbW].flags & jpxCoeffSign)
+ ? -1 : 1;
+ }
+ }
+ if (y0+y1 < cb->y1 - 1) {
+ if (coeff[tileComp->cbW].flags & jpxCoeffSignificant) {
+ ++vert;
+ vertSign += (coeff[tileComp->cbW].flags & jpxCoeffSign)
+ ? -1 : 1;
+ }
+ }
+ cx = sigPropContext[horiz][vert][diag][res == 0 ? 1 : sb];
+ if (cb->arithDecoder->decodeBit(cx, cb->stats)) {
+ coeff->flags |= jpxCoeffSignificant | jpxCoeffFirstMagRef;
+ coeff->mag = (coeff->mag << 1) | 1;
+ cx = signContext[horizSign][vertSign][0];
+ xorBit = signContext[horizSign][vertSign][1];
+ if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {
+ coeff->flags |= jpxCoeffSign;
+ }
+ }
+ ++coeff->len;
+ } else {
+ coeff->flags &= ~jpxCoeffTouched;
+ }
+ }
+ }
+ }
+ cb->nextPass = jpxPassSigProp;
+ break;
+ }
+ }
+
+ cb->arithDecoder->cleanup();
+ return gTrue;
+}
+
+// Inverse quantization, and wavelet transform (IDWT). This also does
+// the initial shift to convert to fixed point format.
+void JPXStream::inverseTransform(JPXTileComp *tileComp) {
+ JPXResLevel *resLevel;
+ JPXPrecinct *precinct;
+ JPXSubband *subband;
+ JPXCodeBlock *cb;
+ JPXCoeff *coeff0, *coeff;
+ Guint qStyle, guard, eps, shift;
+ int shift2;
+ double mu;
+ int val;
+ int *dataPtr;
+ Guint nx0, ny0, nx1, ny1;
+ Guint r, cbX, cbY, x, y;
+
+ //----- (NL)LL subband (resolution level 0)
+
+ resLevel = &tileComp->resLevels[0];
+ precinct = &resLevel->precincts[0];
+ subband = &precinct->subbands[0];
+
+ // i-quant parameters
+ qStyle = tileComp->quantStyle & 0x1f;
+ guard = (tileComp->quantStyle >> 5) & 7;
+ if (qStyle == 0) {
+ eps = (tileComp->quantSteps[0] >> 3) & 0x1f;
+ shift = guard + eps - 1;
+ mu = 0; // make gcc happy
+ } else {
+ shift = guard - 1 + tileComp->prec;
+ mu = (double)(0x800 + (tileComp->quantSteps[0] & 0x7ff)) / 2048.0;
+ }
+ if (tileComp->transform == 0) {
+ shift += fracBits;
+ }
+
+ // copy (NL)LL into the upper-left corner of the data array, doing
+ // the fixed point adjustment and dequantization along the way
+ cb = subband->cbs;
+ for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
+ for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
+ for (y = cb->y0, coeff0 = cb->coeffs;
+ y < cb->y1;
+ ++y, coeff0 += tileComp->cbW) {
+ dataPtr = &tileComp->data[(y - subband->y0)
+ * (tileComp->x1 - tileComp->x0)
+ + (cb->x0 - subband->x0)];
+ for (x = cb->x0, coeff = coeff0; x < cb->x1; ++x, ++coeff) {
+ val = (int)coeff->mag;
+ if (val != 0) {
+ shift2 = shift - (cb->nZeroBitPlanes + coeff->len);
+ if (shift2 > 0) {
+ val = (val << shift2) + (1 << (shift2 - 1));
+ } else {
+ val >>= -shift2;
+ }
+ if (qStyle == 0) {
+ if (tileComp->transform == 0) {
+ val &= -1 << fracBits;
+ }
+ } else {
+ val = (int)((double)val * mu);
+ }
+ if (coeff->flags & jpxCoeffSign) {
+ val = -val;
+ }
+ }
+ *dataPtr++ = val;
+ }
+ }
+ ++cb;
+ }
+ }
+
+ //----- IDWT for each level
+
+ for (r = 1; r <= tileComp->nDecompLevels; ++r) {
+ resLevel = &tileComp->resLevels[r];
+
+ // (n)LL is already in the upper-left corner of the
+ // tile-component data array -- interleave with (n)HL/LH/HH
+ // and inverse transform to get (n-1)LL, which will be stored
+ // in the upper-left corner of the tile-component data array
+ if (r == tileComp->nDecompLevels) {
+ nx0 = tileComp->x0;
+ ny0 = tileComp->y0;
+ nx1 = tileComp->x1;
+ ny1 = tileComp->y1;
+ } else {
+ nx0 = tileComp->resLevels[r+1].x0;
+ ny0 = tileComp->resLevels[r+1].y0;
+ nx1 = tileComp->resLevels[r+1].x1;
+ ny1 = tileComp->resLevels[r+1].y1;
+ }
+ inverseTransformLevel(tileComp, r, resLevel, nx0, ny0, nx1, ny1);
+ }
+}
+
+// Do one level of the inverse transform:
+// - take (n)LL from the tile-component data array
+// - take (n)HL/LH/HH from <resLevel>
+// - leave the resulting (n-1)LL in the tile-component data array
+void JPXStream::inverseTransformLevel(JPXTileComp *tileComp,
+ Guint r, JPXResLevel *resLevel,
+ Guint nx0, Guint ny0,
+ Guint nx1, Guint ny1) {
+ JPXPrecinct *precinct;
+ JPXSubband *subband;
+ JPXCodeBlock *cb;
+ JPXCoeff *coeff0, *coeff;
+ Guint qStyle, guard, eps, shift, t;
+ int shift2;
+ double mu;
+ int val;
+ int *dataPtr;
+ Guint xo, yo;
+ Guint x, y, sb, cbX, cbY;
+ int xx, yy;
+
+ //----- interleave
+
+ // spread out LL
+ for (yy = resLevel->y1 - 1; yy >= (int)resLevel->y0; --yy) {
+ for (xx = resLevel->x1 - 1; xx >= (int)resLevel->x0; --xx) {
+ tileComp->data[(2 * yy - ny0) * (tileComp->x1 - tileComp->x0)
+ + (2 * xx - nx0)] =
+ tileComp->data[(yy - resLevel->y0) * (tileComp->x1 - tileComp->x0)
+ + (xx - resLevel->x0)];
+ }
+ }
+
+ // i-quant parameters
+ qStyle = tileComp->quantStyle & 0x1f;
+ guard = (tileComp->quantStyle >> 5) & 7;
+
+ // interleave HL/LH/HH
+ precinct = &resLevel->precincts[0];
+ for (sb = 0; sb < 3; ++sb) {
+
+ // i-quant parameters
+ if (qStyle == 0) {
+ eps = (tileComp->quantSteps[3*r - 2 + sb] >> 3) & 0x1f;
+ shift = guard + eps - 1;
+ mu = 0; // make gcc happy
+ } else {
+ shift = guard + tileComp->prec;
+ if (sb == 2) {
+ ++shift;
+ }
+ t = tileComp->quantSteps[qStyle == 1 ? 0 : (3*r - 2 + sb)];
+ mu = (double)(0x800 + (t & 0x7ff)) / 2048.0;
+ }
+ if (tileComp->transform == 0) {
+ shift += fracBits;
+ }
+
+ // copy the subband coefficients into the data array, doing the
+ // fixed point adjustment and dequantization along the way
+ xo = (sb & 1) ? 0 : 1;
+ yo = (sb > 0) ? 1 : 0;
+ subband = &precinct->subbands[sb];
+ cb = subband->cbs;
+ for (cbY = 0; cbY < subband->nYCBs; ++cbY) {
+ for (cbX = 0; cbX < subband->nXCBs; ++cbX) {
+ for (y = cb->y0, coeff0 = cb->coeffs;
+ y < cb->y1;
+ ++y, coeff0 += tileComp->cbW) {
+ dataPtr = &tileComp->data[(2 * y + yo - ny0)
+ * (tileComp->x1 - tileComp->x0)
+ + (2 * cb->x0 + xo - nx0)];
+ for (x = cb->x0, coeff = coeff0; x < cb->x1; ++x, ++coeff) {
+ val = (int)coeff->mag;
+ if (val != 0) {
+ shift2 = shift - (cb->nZeroBitPlanes + coeff->len);
+ if (shift2 > 0) {
+ val = (val << shift2) + (1 << (shift2 - 1));
+ } else {
+ val >>= -shift2;
+ }
+ if (qStyle == 0) {
+ if (tileComp->transform == 0) {
+ val &= -1 << fracBits;
+ }
+ } else {
+ val = (int)((double)val * mu);
+ }
+ if (coeff->flags & jpxCoeffSign) {
+ val = -val;
+ }
+ }
+ *dataPtr = val;
+ dataPtr += 2;
+ }
+ }
+ ++cb;
+ }
+ }
+ }
+
+ //----- horizontal (row) transforms
+ dataPtr = tileComp->data;
+ for (y = 0; y < ny1 - ny0; ++y) {
+ inverseTransform1D(tileComp, dataPtr, 1, nx0, nx1);
+ dataPtr += tileComp->x1 - tileComp->x0;
+ }
+
+ //----- vertical (column) transforms
+ dataPtr = tileComp->data;
+ for (x = 0; x < nx1 - nx0; ++x) {
+ inverseTransform1D(tileComp, dataPtr,
+ tileComp->x1 - tileComp->x0, ny0, ny1);
+ ++dataPtr;
+ }
+}
+
+void JPXStream::inverseTransform1D(JPXTileComp *tileComp,
+ int *data, Guint stride,
+ Guint i0, Guint i1) {
+ int *buf;
+ Guint offset, end, i;
+
+ //----- special case for length = 1
+ if (i1 - i0 == 1) {
+ if (i0 & 1) {
+ *data >>= 1;
+ }
+
+ } else {
+
+ // choose an offset: this makes even buf[] indexes correspond to
+ // odd values of i, and vice versa
+ offset = 3 + (i0 & 1);
+ end = offset + i1 - i0;
+
+ //----- gather
+ buf = tileComp->buf;
+ for (i = 0; i < i1 - i0; ++i) {
+ buf[offset + i] = data[i * stride];
+ }
+
+ //----- extend right
+ buf[end] = buf[end - 2];
+ if (i1 - i0 == 2) {
+ buf[end+1] = buf[offset + 1];
+ buf[end+2] = buf[offset];
+ buf[end+3] = buf[offset + 1];
+ } else {
+ buf[end+1] = buf[end - 3];
+ if (i1 - i0 == 3) {
+ buf[end+2] = buf[offset + 1];
+ buf[end+3] = buf[offset + 2];
+ } else {
+ buf[end+2] = buf[end - 4];
+ if (i1 - i0 == 4) {
+ buf[end+3] = buf[offset + 1];
+ } else {
+ buf[end+3] = buf[end - 5];
+ }
+ }
+ }
+
+ //----- extend left
+ buf[offset - 1] = buf[offset + 1];
+ buf[offset - 2] = buf[offset + 2];
+ buf[offset - 3] = buf[offset + 3];
+ if (offset == 4) {
+ buf[0] = buf[offset + 4];
+ }
+
+ //----- 9-7 irreversible filter
+
+ if (tileComp->transform == 0) {
+ // step 1 (even)
+ for (i = 1; i <= end + 2; i += 2) {
+ buf[i] = (int)(idwtKappa * buf[i]);
+ }
+ // step 2 (odd)
+ for (i = 0; i <= end + 3; i += 2) {
+ buf[i] = (int)(idwtIKappa * buf[i]);
+ }
+ // step 3 (even)
+ for (i = 1; i <= end + 2; i += 2) {
+ buf[i] = (int)(buf[i] - idwtDelta * (buf[i-1] + buf[i+1]));
+ }
+ // step 4 (odd)
+ for (i = 2; i <= end + 1; i += 2) {
+ buf[i] = (int)(buf[i] - idwtGamma * (buf[i-1] + buf[i+1]));
+ }
+ // step 5 (even)
+ for (i = 3; i <= end; i += 2) {
+ buf[i] = (int)(buf[i] - idwtBeta * (buf[i-1] + buf[i+1]));
+ }
+ // step 6 (odd)
+ for (i = 4; i <= end - 1; i += 2) {
+ buf[i] = (int)(buf[i] - idwtAlpha * (buf[i-1] + buf[i+1]));
+ }
+
+ //----- 5-3 reversible filter
+
+ } else {
+ // step 1 (even)
+ for (i = 3; i <= end; i += 2) {
+ buf[i] -= (buf[i-1] + buf[i+1] + 2) >> 2;
+ }
+ // step 2 (odd)
+ for (i = 4; i < end; i += 2) {
+ buf[i] += (buf[i-1] + buf[i+1]) >> 1;
+ }
+ }
+
+ //----- scatter
+ for (i = 0; i < i1 - i0; ++i) {
+ data[i * stride] = buf[offset + i];
+ }
+ }
+}
+
+// Inverse multi-component transform and DC level shift. This also
+// converts fixed point samples back to integers.
+GBool JPXStream::inverseMultiCompAndDC(JPXTile *tile) {
+ JPXTileComp *tileComp;
+ int coeff, d0, d1, d2, t, minVal, maxVal, zeroVal;
+ int *dataPtr;
+ Guint j, comp, x, y;
+
+ //----- inverse multi-component transform
+
+ if (tile->multiComp == 1) {
+ if (img.nComps < 3 ||
+ tile->tileComps[0].hSep != tile->tileComps[1].hSep ||
+ tile->tileComps[0].vSep != tile->tileComps[1].vSep ||
+ tile->tileComps[1].hSep != tile->tileComps[2].hSep ||
+ tile->tileComps[1].vSep != tile->tileComps[2].vSep) {
+ return gFalse;
+ }
+
+ // inverse irreversible multiple component transform
+ if (tile->tileComps[0].transform == 0) {
+ j = 0;
+ for (y = 0; y < tile->tileComps[0].y1 - tile->tileComps[0].y0; ++y) {
+ for (x = 0; x < tile->tileComps[0].x1 - tile->tileComps[0].x0; ++x) {
+ d0 = tile->tileComps[0].data[j];
+ d1 = tile->tileComps[1].data[j];
+ d2 = tile->tileComps[2].data[j];
+ tile->tileComps[0].data[j] = (int)(d0 + 1.402 * d2 + 0.5);
+ tile->tileComps[1].data[j] =
+ (int)(d0 - 0.34413 * d1 - 0.71414 * d2 + 0.5);
+ tile->tileComps[2].data[j] = (int)(d0 + 1.772 * d1 + 0.5);
+ ++j;
+ }
+ }
+
+ // inverse reversible multiple component transform
+ } else {
+ j = 0;
+ for (y = 0; y < tile->tileComps[0].y1 - tile->tileComps[0].y0; ++y) {
+ for (x = 0; x < tile->tileComps[0].x1 - tile->tileComps[0].x0; ++x) {
+ d0 = tile->tileComps[0].data[j];
+ d1 = tile->tileComps[1].data[j];
+ d2 = tile->tileComps[2].data[j];
+ tile->tileComps[1].data[j] = t = d0 - ((d2 + d1) >> 2);
+ tile->tileComps[0].data[j] = d2 + t;
+ tile->tileComps[2].data[j] = d1 + t;
+ ++j;
+ }
+ }
+ }
+ }
+
+ //----- DC level shift
+ for (comp = 0; comp < img.nComps; ++comp) {
+ tileComp = &tile->tileComps[comp];
+
+ // signed: clip
+ if (tileComp->sgned) {
+ minVal = -(1 << (tileComp->prec - 1));
+ maxVal = (1 << (tileComp->prec - 1)) - 1;
+ dataPtr = tileComp->data;
+ for (y = 0; y < tileComp->y1 - tileComp->y0; ++y) {
+ for (x = 0; x < tileComp->x1 - tileComp->x0; ++x) {
+ coeff = *dataPtr;
+ if (tileComp->transform == 0) {
+ coeff >>= fracBits;
+ }
+ if (coeff < minVal) {
+ coeff = minVal;
+ } else if (coeff > maxVal) {
+ coeff = maxVal;
+ }
+ *dataPtr++ = coeff;
+ }
+ }
+
+ // unsigned: inverse DC level shift and clip
+ } else {
+ maxVal = (1 << tileComp->prec) - 1;
+ zeroVal = 1 << (tileComp->prec - 1);
+ dataPtr = tileComp->data;
+ for (y = 0; y < tileComp->y1 - tileComp->y0; ++y) {
+ for (x = 0; x < tileComp->x1 - tileComp->x0; ++x) {
+ coeff = *dataPtr;
+ if (tileComp->transform == 0) {
+ coeff >>= fracBits;
+ }
+ coeff += zeroVal;
+ if (coeff < 0) {
+ coeff = 0;
+ } else if (coeff > maxVal) {
+ coeff = maxVal;
+ }
+ *dataPtr++ = coeff;
+ }
+ }
+ }
+ }
+
+ return gTrue;
+}
+
+GBool JPXStream::readBoxHdr(Guint *boxType, Guint *boxLen, Guint *dataLen) {
+ Guint len, lenH;
+
+ if (!readULong(&len) ||
+ !readULong(boxType)) {
+ return gFalse;
+ }
+ if (len == 1) {
+ if (!readULong(&lenH) || !readULong(&len)) {
+ return gFalse;
+ }
+ if (lenH) {
+ error(getPos(), "JPX stream contains a box larger than 2^32 bytes");
+ return gFalse;
+ }
+ *boxLen = len;
+ *dataLen = len - 16;
+ } else if (len == 0) {
+ *boxLen = 0;
+ *dataLen = 0;
+ } else {
+ *boxLen = len;
+ *dataLen = len - 8;
+ }
+ return gTrue;
+}
+
+int JPXStream::readMarkerHdr(int *segType, Guint *segLen) {
+ int c;
+
+ do {
+ do {
+ if ((c = str->getChar()) == EOF) {
+ return gFalse;
+ }
+ } while (c != 0xff);
+ do {
+ if ((c = str->getChar()) == EOF) {
+ return gFalse;
+ }
+ } while (c == 0xff);
+ } while (c == 0x00);
+ *segType = c;
+ if ((c >= 0x30 && c <= 0x3f) ||
+ c == 0x4f || c == 0x92 || c == 0x93 || c == 0xd9) {
+ *segLen = 0;
+ return gTrue;
+ }
+ return readUWord(segLen);
+}
+
+GBool JPXStream::readUByte(Guint *x) {
+ int c0;
+
+ if ((c0 = str->getChar()) == EOF) {
+ return gFalse;
+ }
+ *x = (Guint)c0;
+ return gTrue;
+}
+
+GBool JPXStream::readByte(int *x) {
+ int c0;
+
+ if ((c0 = str->getChar()) == EOF) {
+ return gFalse;
+ }
+ *x = c0;
+ if (c0 & 0x80) {
+ *x |= -1 - 0xff;
+ }
+ return gTrue;
+}
+
+GBool JPXStream::readUWord(Guint *x) {
+ int c0, c1;
+
+ if ((c0 = str->getChar()) == EOF ||
+ (c1 = str->getChar()) == EOF) {
+ return gFalse;
+ }
+ *x = (Guint)((c0 << 8) | c1);
+ return gTrue;
+}
+
+GBool JPXStream::readULong(Guint *x) {
+ int c0, c1, c2, c3;
+
+ if ((c0 = str->getChar()) == EOF ||
+ (c1 = str->getChar()) == EOF ||
+ (c2 = str->getChar()) == EOF ||
+ (c3 = str->getChar()) == EOF) {
+ return gFalse;
+ }
+ *x = (Guint)((c0 << 24) | (c1 << 16) | (c2 << 8) | c3);
+ return gTrue;
+}
+
+GBool JPXStream::readNBytes(int nBytes, GBool signd, int *x) {
+ int y, c, i;
+
+ y = 0;
+ for (i = 0; i < nBytes; ++i) {
+ if ((c = str->getChar()) == EOF) {
+ return gFalse;
+ }
+ y = (y << 8) + c;
+ }
+ if (signd) {
+ if (y & (1 << (8 * nBytes - 1))) {
+ y |= -1 << (8 * nBytes);
+ }
+ }
+ *x = y;
+ return gTrue;
+}
+
+GBool JPXStream::readBits(int nBits, Guint *x) {
+ int c;
+
+ while (bitBufLen < nBits) {
+ if ((c = str->getChar()) == EOF) {
+ return gFalse;
+ }
+ ++byteCount;
+ if (bitBufSkip) {
+ bitBuf = (bitBuf << 7) | (c & 0x7f);
+ bitBufLen += 7;
+ } else {
+ bitBuf = (bitBuf << 8) | (c & 0xff);
+ bitBufLen += 8;
+ }
+ bitBufSkip = c == 0xff;
+ }
+ *x = (bitBuf >> (bitBufLen - nBits)) & ((1 << nBits) - 1);
+ bitBufLen -= nBits;
+ return gTrue;
+}
+
+void JPXStream::clearBitBuf() {
+ bitBufLen = 0;
+ bitBufSkip = gFalse;
+ byteCount = 0;
+}
--- /dev/null
+//========================================================================
+//
+// JPXStream.h
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef JPXSTREAM_H
+#define JPXSTREAM_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "Object.h"
+#include "Stream.h"
+
+class JArithmeticDecoderStats;
+
+//------------------------------------------------------------------------
+
+enum JPXColorSpaceType {
+ jpxCSBiLevel = 0,
+ jpxCSYCbCr1 = 1,
+ jpxCSYCbCr2 = 3,
+ jpxCSYCBCr3 = 4,
+ jpxCSPhotoYCC = 9,
+ jpxCSCMY = 11,
+ jpxCSCMYK = 12,
+ jpxCSYCCK = 13,
+ jpxCSCIELab = 14,
+ jpxCSsRGB = 16,
+ jpxCSGrayscale = 17,
+ jpxCSBiLevel2 = 18,
+ jpxCSCIEJab = 19,
+ jpxCSCISesRGB = 20,
+ jpxCSROMMRGB = 21,
+ jpxCSsRGBYCbCr = 22,
+ jpxCSYPbPr1125 = 23,
+ jpxCSYPbPr1250 = 24
+};
+
+struct JPXColorSpecCIELab {
+ Guint rl, ol, ra, oa, rb, ob, il;
+};
+
+struct JPXColorSpecEnumerated {
+ JPXColorSpaceType type; // color space type
+ union {
+ JPXColorSpecCIELab cieLab;
+ };
+};
+
+struct JPXColorSpec {
+ Guint meth; // method
+ int prec; // precedence
+ union {
+ JPXColorSpecEnumerated enumerated;
+ };
+};
+
+//------------------------------------------------------------------------
+
+struct JPXPalette {
+ Guint nEntries; // number of entries in the palette
+ Guint nComps; // number of components in each entry
+ Guint *bpc; // bits per component, for each component
+ int *c; // color data:
+ // c[i*nComps+j] = entry i, component j
+};
+
+//------------------------------------------------------------------------
+
+struct JPXCompMap {
+ Guint nChannels; // number of channels
+ Guint *comp; // codestream components mapped to each channel
+ Guint *type; // 0 for direct use, 1 for palette mapping
+ Guint *pComp; // palette components to use
+};
+
+//------------------------------------------------------------------------
+
+struct JPXChannelDefn {
+ Guint nChannels; // number of channels
+ Guint *idx; // channel indexes
+ Guint *type; // channel types
+ Guint *assoc; // channel associations
+};
+
+//------------------------------------------------------------------------
+
+struct JPXTagTreeNode {
+ GBool finished; // true if this node is finished
+ Guint val; // current value
+};
+
+//------------------------------------------------------------------------
+
+struct JPXCoeff {
+ Gushort flags; // flag bits
+ Gushort len; // number of significant bits in mag
+ Guint mag; // magnitude value
+};
+
+// coefficient flags
+#define jpxCoeffSignificantB 0
+#define jpxCoeffTouchedB 1
+#define jpxCoeffFirstMagRefB 2
+#define jpxCoeffSignB 7
+#define jpxCoeffSignificant (1 << jpxCoeffSignificantB)
+#define jpxCoeffTouched (1 << jpxCoeffTouchedB)
+#define jpxCoeffFirstMagRef (1 << jpxCoeffFirstMagRefB)
+#define jpxCoeffSign (1 << jpxCoeffSignB)
+
+//------------------------------------------------------------------------
+
+struct JPXCodeBlock {
+ //----- size
+ Guint x0, y0, x1, y1; // bounds
+
+ //----- persistent state
+ GBool seen; // true if this code-block has already
+ // been seen
+ Guint lBlock; // base number of bits used for pkt data length
+ Guint nextPass; // next coding pass
+
+ //---- info from first packet
+ Guint nZeroBitPlanes; // number of zero bit planes
+
+ //----- info for the current packet
+ Guint included; // code-block inclusion in this packet:
+ // 0=not included, 1=included
+ Guint nCodingPasses; // number of coding passes in this pkt
+ Guint dataLen; // pkt data length
+
+ //----- coefficient data
+ JPXCoeff *coeffs; // the coefficients
+ JArithmeticDecoder // arithmetic decoder
+ *arithDecoder;
+ JArithmeticDecoderStats // arithmetic decoder stats
+ *stats;
+};
+
+//------------------------------------------------------------------------
+
+struct JPXSubband {
+ //----- computed
+ Guint x0, y0, x1, y1; // bounds
+ Guint nXCBs, nYCBs; // number of code-blocks in the x and y
+ // directions
+
+ //----- tag trees
+ Guint maxTTLevel; // max tag tree level
+ JPXTagTreeNode *inclusion; // inclusion tag tree for each subband
+ JPXTagTreeNode *zeroBitPlane; // zero-bit plane tag tree for each
+ // subband
+
+ //----- children
+ JPXCodeBlock *cbs; // the code-blocks (len = nXCBs * nYCBs)
+};
+
+//------------------------------------------------------------------------
+
+struct JPXPrecinct {
+ //----- computed
+ Guint x0, y0, x1, y1; // bounds of the precinct
+
+ //----- children
+ JPXSubband *subbands; // the subbands
+};
+
+//------------------------------------------------------------------------
+
+struct JPXResLevel {
+ //----- from the COD and COC segments (main and tile)
+ Guint precinctWidth; // log2(precinct width)
+ Guint precinctHeight; // log2(precinct height)
+
+ //----- computed
+ Guint x0, y0, x1, y1; // bounds of the tile-comp (for this res level)
+ Guint bx0[3], by0[3], // subband bounds
+ bx1[3], by1[3];
+
+ //---- children
+ JPXPrecinct *precincts; // the precincts
+};
+
+//------------------------------------------------------------------------
+
+struct JPXTileComp {
+ //----- from the SIZ segment
+ GBool sgned; // 1 for signed, 0 for unsigned
+ Guint prec; // precision, in bits
+ Guint hSep; // horizontal separation of samples
+ Guint vSep; // vertical separation of samples
+
+ //----- from the COD and COC segments (main and tile)
+ Guint style; // coding style parameter (Scod / Scoc)
+ Guint nDecompLevels; // number of decomposition levels
+ Guint codeBlockW; // log2(code-block width)
+ Guint codeBlockH; // log2(code-block height)
+ Guint codeBlockStyle; // code-block style
+ Guint transform; // wavelet transformation
+
+ //----- from the QCD and QCC segments (main and tile)
+ Guint quantStyle; // quantization style
+ Guint *quantSteps; // quantization step size for each subband
+ Guint nQuantSteps; // number of entries in quantSteps
+
+ //----- computed
+ Guint x0, y0, x1, y1; // bounds of the tile-comp, in ref coords
+ Guint cbW; // code-block width
+ Guint cbH; // code-block height
+
+ //----- image data
+ int *data; // the decoded image data
+ int *buf; // intermediate buffer for the inverse
+ // transform
+
+ //----- children
+ JPXResLevel *resLevels; // the resolution levels
+ // (len = nDecompLevels + 1)
+};
+
+//------------------------------------------------------------------------
+
+struct JPXTile {
+ //----- from the COD segments (main and tile)
+ Guint progOrder; // progression order
+ Guint nLayers; // number of layers
+ Guint multiComp; // multiple component transformation
+
+ //----- computed
+ Guint x0, y0, x1, y1; // bounds of the tile, in ref coords
+ Guint maxNDecompLevels; // max number of decomposition levels used
+ // in any component in this tile
+
+ //----- progression order loop counters
+ Guint comp; // component
+ Guint res; // resolution level
+ Guint precinct; // precinct
+ Guint layer; // layer
+
+ //----- children
+ JPXTileComp *tileComps; // the tile-components (len = JPXImage.nComps)
+};
+
+//------------------------------------------------------------------------
+
+struct JPXImage {
+ //----- from the SIZ segment
+ Guint xSize, ySize; // size of reference grid
+ Guint xOffset, yOffset; // image offset
+ Guint xTileSize, yTileSize; // size of tiles
+ Guint xTileOffset, // offset of first tile
+ yTileOffset;
+ Guint nComps; // number of components
+
+ //----- computed
+ Guint nXTiles; // number of tiles in x direction
+ Guint nYTiles; // number of tiles in y direction
+
+ //----- children
+ JPXTile *tiles; // the tiles (len = nXTiles * nYTiles)
+};
+
+//------------------------------------------------------------------------
+
+class JPXStream: public FilterStream {
+public:
+
+ JPXStream(Stream *strA);
+ virtual ~JPXStream();
+ virtual StreamKind getKind() { return strJPX; }
+ virtual void reset();
+ virtual int getChar();
+ virtual int lookChar();
+ virtual GString *getPSFilter(int psLevel, char *indent);
+ virtual GBool isBinary(GBool last = gTrue);
+ virtual void getImageParams(int *bitsPerComponent,
+ StreamColorSpaceMode *csMode);
+
+private:
+
+ void fillReadBuf();
+ void getImageParams2(int *bitsPerComponent, StreamColorSpaceMode *csMode);
+ GBool readBoxes();
+ GBool readColorSpecBox(Guint dataLen);
+ GBool readCodestream(Guint len);
+ GBool readTilePart();
+ GBool readTilePartData(Guint tileIdx,
+ Guint tilePartLen, GBool tilePartToEOC);
+ GBool readCodeBlockData(JPXTileComp *tileComp,
+ JPXResLevel *resLevel,
+ JPXPrecinct *precinct,
+ JPXSubband *subband,
+ Guint res, Guint sb,
+ JPXCodeBlock *cb);
+ void inverseTransform(JPXTileComp *tileComp);
+ void inverseTransformLevel(JPXTileComp *tileComp,
+ Guint r, JPXResLevel *resLevel,
+ Guint nx0, Guint ny0,
+ Guint nx1, Guint ny1);
+ void inverseTransform1D(JPXTileComp *tileComp,
+ int *data, Guint stride,
+ Guint i0, Guint i1);
+ GBool inverseMultiCompAndDC(JPXTile *tile);
+ GBool readBoxHdr(Guint *boxType, Guint *boxLen, Guint *dataLen);
+ int readMarkerHdr(int *segType, Guint *segLen);
+ GBool readUByte(Guint *x);
+ GBool readByte(int *x);
+ GBool readUWord(Guint *x);
+ GBool readULong(Guint *x);
+ GBool readNBytes(int nBytes, GBool signd, int *x);
+ GBool readBits(int nBits, Guint *x);
+ void clearBitBuf();
+
+ Guint nComps; // number of components
+ Guint *bpc; // bits per component, for each component
+ Guint width, height; // image size
+ GBool haveImgHdr; // set if a JP2/JPX image header has been
+ // found
+ JPXColorSpec cs; // color specification
+ GBool haveCS; // set if a color spec has been found
+ JPXPalette palette; // the palette
+ GBool havePalette; // set if a palette has been found
+ JPXCompMap compMap; // the component mapping
+ GBool haveCompMap; // set if a component mapping has been found
+ JPXChannelDefn channelDefn; // channel definition
+ GBool haveChannelDefn; // set if a channel defn has been found
+
+ JPXImage img; // JPEG2000 decoder data
+ Guint bitBuf; // buffer for bit reads
+ int bitBufLen; // number of bits in bitBuf
+ GBool bitBufSkip; // true if next bit should be skipped
+ // (for bit stuffing)
+ Guint byteCount; // number of bytes read since last call
+ // to clearBitBuf
+
+ Guint curX, curY, curComp; // current position for lookChar/getChar
+ Guint readBuf; // read buffer
+ Guint readBufLen; // number of valid bits in readBuf
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// Lexer.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include "Lexer.h"
+#include "Error.h"
+
+//------------------------------------------------------------------------
+
+// A '1' in this array means the character is white space. A '1' or
+// '2' means the character ends a name or command.
+static char specialChars[256] = {
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 1, 0, 0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, // 2x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, // 3x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 5x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // fx
+};
+
+//------------------------------------------------------------------------
+// Lexer
+//------------------------------------------------------------------------
+
+Lexer::Lexer(XRef *xref, Stream *str) {
+ Object obj;
+
+ curStr.initStream(str);
+ streams = new Array(xref);
+ streams->add(curStr.copy(&obj));
+ strPtr = 0;
+ freeArray = gTrue;
+ curStr.streamReset();
+}
+
+Lexer::Lexer(XRef *xref, Object *obj) {
+ Object obj2;
+
+ if (obj->isStream()) {
+ streams = new Array(xref);
+ freeArray = gTrue;
+ streams->add(obj->copy(&obj2));
+ } else {
+ streams = obj->getArray();
+ freeArray = gFalse;
+ }
+ strPtr = 0;
+ if (streams->getLength() > 0) {
+ streams->get(strPtr, &curStr);
+ curStr.streamReset();
+ }
+}
+static int illegalChars = 0;
+
+Lexer::~Lexer() {
+ if (!curStr.isNone()) {
+ curStr.streamClose();
+ curStr.free();
+ }
+ if (freeArray) {
+ delete streams;
+ }
+ if(illegalChars)
+ error(0, "Illegal characters in hex string (%d)", illegalChars);
+ illegalChars = 0;
+}
+
+int Lexer::getChar() {
+ int c;
+
+ c = EOF;
+ while (!curStr.isNone() && (c = curStr.streamGetChar()) == EOF) {
+ curStr.streamClose();
+ curStr.free();
+ ++strPtr;
+ if (strPtr < streams->getLength()) {
+ streams->get(strPtr, &curStr);
+ curStr.streamReset();
+ }
+ }
+ return c;
+}
+
+int Lexer::lookChar() {
+ if (curStr.isNone()) {
+ return EOF;
+ }
+ return curStr.streamLookChar();
+}
+
+Object *Lexer::getObj(Object *obj) {
+ char *p;
+ int c, c2;
+ GBool comment, neg, done;
+ int numParen;
+ int xi;
+ double xf, scale;
+ GString *s;
+ int n, m;
+
+ // skip whitespace and comments
+ comment = gFalse;
+ while (1) {
+ if ((c = getChar()) == EOF) {
+ return obj->initEOF();
+ }
+ if (comment) {
+ if (c == '\r' || c == '\n')
+ comment = gFalse;
+ } else if (c == '%') {
+ comment = gTrue;
+ } else if (specialChars[c] != 1) {
+ break;
+ }
+ }
+
+ // start reading token
+ switch (c) {
+
+ // number
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case '-': case '.':
+ neg = gFalse;
+ xi = 0;
+ if (c == '-') {
+ neg = gTrue;
+ } else if (c == '.') {
+ goto doReal;
+ } else {
+ xi = c - '0';
+ }
+ while (1) {
+ c = lookChar();
+ if (isdigit(c)) {
+ getChar();
+ xi = xi * 10 + (c - '0');
+ } else if (c == '.') {
+ getChar();
+ goto doReal;
+ } else {
+ break;
+ }
+ }
+ if (neg)
+ xi = -xi;
+ obj->initInt(xi);
+ break;
+ doReal:
+ xf = xi;
+ scale = 0.1;
+ while (1) {
+ c = lookChar();
+ if (c == '-') {
+ // ignore minus signs in the middle of numbers to match
+ // Adobe's behavior
+ error(getPos(), "Badly formatted number");
+ getChar();
+ continue;
+ }
+ if (!isdigit(c)) {
+ break;
+ }
+ getChar();
+ xf = xf + scale * (c - '0');
+ scale *= 0.1;
+ }
+ if (neg)
+ xf = -xf;
+ obj->initReal(xf);
+ break;
+
+ // string
+ case '(':
+ p = tokBuf;
+ n = 0;
+ numParen = 1;
+ done = gFalse;
+ s = NULL;
+ do {
+ c2 = EOF;
+ switch (c = getChar()) {
+
+ case EOF:
+#if 0
+ // This breaks some PDF files, e.g., ones from Photoshop.
+ case '\r':
+ case '\n':
+#endif
+ error(getPos(), "Unterminated string");
+ done = gTrue;
+ break;
+
+ case '(':
+ ++numParen;
+ c2 = c;
+ break;
+
+ case ')':
+ if (--numParen == 0) {
+ done = gTrue;
+ } else {
+ c2 = c;
+ }
+ break;
+
+ case '\\':
+ switch (c = getChar()) {
+ case 'n':
+ c2 = '\n';
+ break;
+ case 'r':
+ c2 = '\r';
+ break;
+ case 't':
+ c2 = '\t';
+ break;
+ case 'b':
+ c2 = '\b';
+ break;
+ case 'f':
+ c2 = '\f';
+ break;
+ case '\\':
+ case '(':
+ case ')':
+ c2 = c;
+ break;
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ c2 = c - '0';
+ c = lookChar();
+ if (c >= '0' && c <= '7') {
+ getChar();
+ c2 = (c2 << 3) + (c - '0');
+ c = lookChar();
+ if (c >= '0' && c <= '7') {
+ getChar();
+ c2 = (c2 << 3) + (c - '0');
+ }
+ }
+ break;
+ case '\r':
+ c = lookChar();
+ if (c == '\n') {
+ getChar();
+ }
+ break;
+ case '\n':
+ break;
+ case EOF:
+ error(getPos(), "Unterminated string");
+ done = gTrue;
+ break;
+ default:
+ c2 = c;
+ break;
+ }
+ break;
+
+ default:
+ c2 = c;
+ break;
+ }
+
+ if (c2 != EOF) {
+ if (n == tokBufSize) {
+ if (!s)
+ s = new GString(tokBuf, tokBufSize);
+ else
+ s->append(tokBuf, tokBufSize);
+ p = tokBuf;
+ n = 0;
+ }
+ *p++ = (char)c2;
+ ++n;
+ }
+ } while (!done);
+ if (!s)
+ s = new GString(tokBuf, n);
+ else
+ s->append(tokBuf, n);
+ obj->initString(s);
+ break;
+
+ // name
+ case '/':
+ p = tokBuf;
+ n = 0;
+ while ((c = lookChar()) != EOF && !specialChars[c]) {
+ getChar();
+ if (c == '#') {
+ c2 = lookChar();
+ if (c2 >= '0' && c2 <= '9') {
+ c = c2 - '0';
+ } else if (c2 >= 'A' && c2 <= 'F') {
+ c = c2 - 'A' + 10;
+ } else if (c2 >= 'a' && c2 <= 'f') {
+ c = c2 - 'a' + 10;
+ } else {
+ goto notEscChar;
+ }
+ getChar();
+ c <<= 4;
+ c2 = getChar();
+ if (c2 >= '0' && c2 <= '9') {
+ c += c2 - '0';
+ } else if (c2 >= 'A' && c2 <= 'F') {
+ c += c2 - 'A' + 10;
+ } else if (c2 >= 'a' && c2 <= 'f') {
+ c += c2 - 'a' + 10;
+ } else {
+ illegalChars++;
+ //error(getPos(), "Illegal digit in hex char in name");
+ }
+ }
+ notEscChar:
+ if (++n == tokBufSize) {
+ error(getPos(), "Name token too long");
+ break;
+ }
+ *p++ = c;
+ }
+ *p = '\0';
+ obj->initName(tokBuf);
+ break;
+
+ // array punctuation
+ case '[':
+ case ']':
+ tokBuf[0] = c;
+ tokBuf[1] = '\0';
+ obj->initCmd(tokBuf);
+ break;
+
+ // hex string or dict punctuation
+ case '<':
+ c = lookChar();
+
+ // dict punctuation
+ if (c == '<') {
+ getChar();
+ tokBuf[0] = tokBuf[1] = '<';
+ tokBuf[2] = '\0';
+ obj->initCmd(tokBuf);
+
+ // hex string
+ } else {
+ p = tokBuf;
+ m = n = 0;
+ c2 = 0;
+ s = NULL;
+ while (1) {
+ c = getChar();
+ if (c == '>') {
+ break;
+ } else if (c == EOF) {
+ error(getPos(), "Unterminated hex string");
+ break;
+ } else if (specialChars[c] != 1) {
+ c2 = c2 << 4;
+ if (c >= '0' && c <= '9')
+ c2 += c - '0';
+ else if (c >= 'A' && c <= 'F')
+ c2 += c - 'A' + 10;
+ else if (c >= 'a' && c <= 'f')
+ c2 += c - 'a' + 10;
+ else {
+ illegalChars++;
+ //error(getPos(), "Illegal character <%02x> in hex string", c);
+ }
+ if (++m == 2) {
+ if (n == tokBufSize) {
+ if (!s)
+ s = new GString(tokBuf, tokBufSize);
+ else
+ s->append(tokBuf, tokBufSize);
+ p = tokBuf;
+ n = 0;
+ }
+ *p++ = (char)c2;
+ ++n;
+ c2 = 0;
+ m = 0;
+ }
+ }
+ }
+ if (!s)
+ s = new GString(tokBuf, n);
+ else
+ s->append(tokBuf, n);
+ if (m == 1)
+ s->append((char)(c2 << 4));
+ obj->initString(s);
+ }
+ break;
+
+ // dict punctuation
+ case '>':
+ c = lookChar();
+ if (c == '>') {
+ getChar();
+ tokBuf[0] = tokBuf[1] = '>';
+ tokBuf[2] = '\0';
+ obj->initCmd(tokBuf);
+ } else {
+ illegalChars++;
+ //error(getPos(), "Illegal character '>'");
+ obj->initError();
+ }
+ break;
+
+ // error
+ case ')':
+ case '{':
+ case '}':
+ //error(getPos(), "Illegal character '%c'", c);
+ illegalChars++;
+ obj->initError();
+ break;
+
+ // command
+ default:
+ p = tokBuf;
+ *p++ = c;
+ n = 1;
+ while ((c = lookChar()) != EOF && !specialChars[c]) {
+ getChar();
+ if (++n == tokBufSize) {
+ error(getPos(), "Command token too long");
+ break;
+ }
+ *p++ = c;
+ }
+ *p = '\0';
+ if (tokBuf[0] == 't' && !strcmp(tokBuf, "true")) {
+ obj->initBool(gTrue);
+ } else if (tokBuf[0] == 'f' && !strcmp(tokBuf, "false")) {
+ obj->initBool(gFalse);
+ } else if (tokBuf[0] == 'n' && !strcmp(tokBuf, "null")) {
+ obj->initNull();
+ } else {
+ obj->initCmd(tokBuf);
+ }
+ break;
+ }
+ return obj;
+}
+
+void Lexer::skipToNextLine() {
+ int c;
+
+ while (1) {
+ c = getChar();
+ if (c == EOF || c == '\n') {
+ return;
+ }
+ if (c == '\r') {
+ if ((c = lookChar()) == '\n') {
+ getChar();
+ }
+ return;
+ }
+ }
+}
+
+GBool Lexer::isSpace(int c) {
+ return c >= 0 && c <= 0xff && specialChars[c] == 1;
+}
--- /dev/null
+//========================================================================
+//
+// Lexer.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef LEXER_H
+#define LEXER_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "Object.h"
+#include "Stream.h"
+
+class XRef;
+
+#define tokBufSize 128 // size of token buffer
+
+//------------------------------------------------------------------------
+// Lexer
+//------------------------------------------------------------------------
+
+class Lexer {
+public:
+
+ // Construct a lexer for a single stream. Deletes the stream when
+ // lexer is deleted.
+ Lexer(XRef *xref, Stream *str);
+
+ // Construct a lexer for a stream or array of streams (assumes obj
+ // is either a stream or array of streams).
+ Lexer(XRef *xref, Object *obj);
+
+ // Destructor.
+ ~Lexer();
+
+ // Get the next object from the input stream.
+ Object *getObj(Object *obj);
+
+ // Skip to the beginning of the next line in the input stream.
+ void skipToNextLine();
+
+ // Skip over one character.
+ void skipChar() { getChar(); }
+
+ // Get stream.
+ Stream *getStream()
+ { return curStr.isNone() ? (Stream *)NULL : curStr.getStream(); }
+
+ // Get current position in file. This is only used for error
+ // messages, so it returns an int instead of a Guint.
+ int getPos()
+ { return curStr.isNone() ? -1 : (int)curStr.streamGetPos(); }
+
+ // Set position in file.
+ void setPos(Guint pos, int dir = 0)
+ { if (!curStr.isNone()) curStr.streamSetPos(pos, dir); }
+
+ // Returns true if <c> is a whitespace character.
+ static GBool isSpace(int c);
+
+private:
+
+ int getChar();
+ int lookChar();
+
+ Array *streams; // array of input streams
+ int strPtr; // index of current stream
+ Object curStr; // current stream
+ GBool freeArray; // should lexer free the streams array?
+ char tokBuf[tokBufSize]; // temporary token buffer
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// Link.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stddef.h>
+#include <string.h>
+#include "gmem.h"
+#include "GString.h"
+#include "Error.h"
+#include "Object.h"
+#include "Array.h"
+#include "Dict.h"
+#include "Link.h"
+
+//------------------------------------------------------------------------
+// LinkAction
+//------------------------------------------------------------------------
+
+LinkAction *LinkAction::parseDest(Object *obj) {
+ LinkAction *action;
+
+ action = new LinkGoTo(obj);
+ if (!action->isOk()) {
+ delete action;
+ return NULL;
+ }
+ return action;
+}
+
+LinkAction *LinkAction::parseAction(Object *obj, GString *baseURI) {
+ LinkAction *action;
+ Object obj2, obj3, obj4;
+
+ if (!obj->isDict()) {
+ error(-1, "Bad annotation action");
+ return NULL;
+ }
+
+ obj->dictLookup("S", &obj2);
+
+ // GoTo action
+ if (obj2.isName("GoTo")) {
+ obj->dictLookup("D", &obj3);
+ action = new LinkGoTo(&obj3);
+ obj3.free();
+
+ // GoToR action
+ } else if (obj2.isName("GoToR")) {
+ obj->dictLookup("F", &obj3);
+ obj->dictLookup("D", &obj4);
+ action = new LinkGoToR(&obj3, &obj4);
+ obj3.free();
+ obj4.free();
+
+ // Launch action
+ } else if (obj2.isName("Launch")) {
+ action = new LinkLaunch(obj);
+
+ // URI action
+ } else if (obj2.isName("URI")) {
+ obj->dictLookup("URI", &obj3);
+ action = new LinkURI(&obj3, baseURI);
+ obj3.free();
+
+ // Named action
+ } else if (obj2.isName("Named")) {
+ obj->dictLookup("N", &obj3);
+ action = new LinkNamed(&obj3);
+ obj3.free();
+
+ // Movie action
+ } else if (obj2.isName("Movie")) {
+ obj->dictLookupNF("Annot", &obj3);
+ obj->dictLookup("T", &obj4);
+ action = new LinkMovie(&obj3, &obj4);
+ obj3.free();
+ obj4.free();
+
+ // unknown action
+ } else if (obj2.isName()) {
+ action = new LinkUnknown(obj2.getName());
+
+ // action is missing or wrong type
+ } else {
+ error(-1, "Bad annotation action");
+ action = NULL;
+ }
+
+ obj2.free();
+
+ if (action && !action->isOk()) {
+ delete action;
+ return NULL;
+ }
+ return action;
+}
+
+GString *LinkAction::getFileSpecName(Object *fileSpecObj) {
+ GString *name;
+ Object obj1;
+
+ name = NULL;
+
+ // string
+ if (fileSpecObj->isString()) {
+ name = fileSpecObj->getString()->copy();
+
+ // dictionary
+ } else if (fileSpecObj->isDict()) {
+#ifdef WIN32
+ if (!fileSpecObj->dictLookup("DOS", &obj1)->isString()) {
+#else
+ if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) {
+#endif
+ obj1.free();
+ fileSpecObj->dictLookup("F", &obj1);
+ }
+ if (obj1.isString()) {
+ name = obj1.getString()->copy();
+ } else {
+ error(-1, "Illegal file spec in link");
+ }
+ obj1.free();
+
+ // error
+ } else {
+ error(-1, "Illegal file spec in link");
+ }
+
+ // system-dependent path manipulation
+ if (name) {
+#ifdef WIN32
+ int i, j;
+
+ // "//...." --> "\...."
+ // "/x/...." --> "x:\...."
+ // "/server/share/...." --> "\\server\share\...."
+ // convert escaped slashes to slashes and unescaped slashes to backslashes
+ i = 0;
+ if (name->getChar(0) == '/') {
+ if (name->getLength() >= 2 && name->getChar(1) == '/') {
+ name->del(0);
+ i = 0;
+ } else if (name->getLength() >= 2 &&
+ ((name->getChar(1) >= 'a' && name->getChar(1) <= 'z') ||
+ (name->getChar(1) >= 'A' && name->getChar(1) <= 'Z')) &&
+ (name->getLength() == 2 || name->getChar(2) == '/')) {
+ name->setChar(0, name->getChar(1));
+ name->setChar(1, ':');
+ i = 2;
+ } else {
+ for (j = 2; j < name->getLength(); ++j) {
+ if (name->getChar(j-1) != '\\' &&
+ name->getChar(j) == '/') {
+ break;
+ }
+ }
+ if (j < name->getLength()) {
+ name->setChar(0, '\\');
+ name->insert(0, '\\');
+ i = 2;
+ }
+ }
+ }
+ for (; i < name->getLength(); ++i) {
+ if (name->getChar(i) == '/') {
+ name->setChar(i, '\\');
+ } else if (name->getChar(i) == '\\' &&
+ i+1 < name->getLength() &&
+ name->getChar(i+1) == '/') {
+ name->del(i);
+ }
+ }
+#else
+ // no manipulation needed for Unix
+#endif
+ }
+
+ return name;
+}
+
+//------------------------------------------------------------------------
+// LinkDest
+//------------------------------------------------------------------------
+
+LinkDest::LinkDest(Array *a) {
+ Object obj1, obj2;
+
+ // initialize fields
+ left = bottom = right = top = zoom = 0;
+ ok = gFalse;
+
+ // get page
+ if (a->getLength() < 2) {
+ error(-1, "Annotation destination array is too short");
+ return;
+ }
+ a->getNF(0, &obj1);
+ if (obj1.isInt()) {
+ pageNum = obj1.getInt() + 1;
+ pageIsRef = gFalse;
+ } else if (obj1.isRef()) {
+ pageRef.num = obj1.getRefNum();
+ pageRef.gen = obj1.getRefGen();
+ pageIsRef = gTrue;
+ } else {
+ error(-1, "Bad annotation destination");
+ goto err2;
+ }
+ obj1.free();
+
+ // get destination type
+ a->get(1, &obj1);
+
+ // XYZ link
+ if (obj1.isName("XYZ")) {
+ kind = destXYZ;
+ if (a->getLength() < 3) {
+ changeLeft = gFalse;
+ } else {
+ a->get(2, &obj2);
+ if (obj2.isNull()) {
+ changeLeft = gFalse;
+ } else if (obj2.isNum()) {
+ changeLeft = gTrue;
+ left = obj2.getNum();
+ } else {
+ error(-1, "Bad annotation destination position");
+ goto err1;
+ }
+ obj2.free();
+ }
+ if (a->getLength() < 4) {
+ changeTop = gFalse;
+ } else {
+ a->get(3, &obj2);
+ if (obj2.isNull()) {
+ changeTop = gFalse;
+ } else if (obj2.isNum()) {
+ changeTop = gTrue;
+ top = obj2.getNum();
+ } else {
+ error(-1, "Bad annotation destination position");
+ goto err1;
+ }
+ obj2.free();
+ }
+ if (a->getLength() < 5) {
+ changeZoom = gFalse;
+ } else {
+ a->get(4, &obj2);
+ if (obj2.isNull()) {
+ changeZoom = gFalse;
+ } else if (obj2.isNum()) {
+ changeZoom = gTrue;
+ zoom = obj2.getNum();
+ } else {
+ error(-1, "Bad annotation destination position");
+ goto err1;
+ }
+ obj2.free();
+ }
+
+ // Fit link
+ } else if (obj1.isName("Fit")) {
+ if (a->getLength() < 2) {
+ error(-1, "Annotation destination array is too short");
+ goto err2;
+ }
+ kind = destFit;
+
+ // FitH link
+ } else if (obj1.isName("FitH")) {
+ if (a->getLength() < 3) {
+ error(-1, "Annotation destination array is too short");
+ goto err2;
+ }
+ kind = destFitH;
+ if (!a->get(2, &obj2)->isNum()) {
+ error(-1, "Bad annotation destination position");
+ goto err1;
+ }
+ top = obj2.getNum();
+ obj2.free();
+
+ // FitV link
+ } else if (obj1.isName("FitV")) {
+ if (a->getLength() < 3) {
+ error(-1, "Annotation destination array is too short");
+ goto err2;
+ }
+ kind = destFitV;
+ if (!a->get(2, &obj2)->isNum()) {
+ error(-1, "Bad annotation destination position");
+ goto err1;
+ }
+ left = obj2.getNum();
+ obj2.free();
+
+ // FitR link
+ } else if (obj1.isName("FitR")) {
+ if (a->getLength() < 6) {
+ error(-1, "Annotation destination array is too short");
+ goto err2;
+ }
+ kind = destFitR;
+ if (!a->get(2, &obj2)->isNum()) {
+ error(-1, "Bad annotation destination position");
+ goto err1;
+ }
+ left = obj2.getNum();
+ obj2.free();
+ if (!a->get(3, &obj2)->isNum()) {
+ error(-1, "Bad annotation destination position");
+ goto err1;
+ }
+ bottom = obj2.getNum();
+ obj2.free();
+ if (!a->get(4, &obj2)->isNum()) {
+ error(-1, "Bad annotation destination position");
+ goto err1;
+ }
+ right = obj2.getNum();
+ obj2.free();
+ if (!a->get(5, &obj2)->isNum()) {
+ error(-1, "Bad annotation destination position");
+ goto err1;
+ }
+ top = obj2.getNum();
+ obj2.free();
+
+ // FitB link
+ } else if (obj1.isName("FitB")) {
+ if (a->getLength() < 2) {
+ error(-1, "Annotation destination array is too short");
+ goto err2;
+ }
+ kind = destFitB;
+
+ // FitBH link
+ } else if (obj1.isName("FitBH")) {
+ if (a->getLength() < 3) {
+ error(-1, "Annotation destination array is too short");
+ goto err2;
+ }
+ kind = destFitBH;
+ if (!a->get(2, &obj2)->isNum()) {
+ error(-1, "Bad annotation destination position");
+ goto err1;
+ }
+ top = obj2.getNum();
+ obj2.free();
+
+ // FitBV link
+ } else if (obj1.isName("FitBV")) {
+ if (a->getLength() < 3) {
+ error(-1, "Annotation destination array is too short");
+ goto err2;
+ }
+ kind = destFitBV;
+ if (!a->get(2, &obj2)->isNum()) {
+ error(-1, "Bad annotation destination position");
+ goto err1;
+ }
+ left = obj2.getNum();
+ obj2.free();
+
+ // unknown link kind
+ } else {
+ error(-1, "Unknown annotation destination type");
+ goto err2;
+ }
+
+ obj1.free();
+ ok = gTrue;
+ return;
+
+ err1:
+ obj2.free();
+ err2:
+ obj1.free();
+}
+
+LinkDest::LinkDest(LinkDest *dest) {
+ kind = dest->kind;
+ pageIsRef = dest->pageIsRef;
+ if (pageIsRef)
+ pageRef = dest->pageRef;
+ else
+ pageNum = dest->pageNum;
+ left = dest->left;
+ bottom = dest->bottom;
+ right = dest->right;
+ top = dest->top;
+ zoom = dest->zoom;
+ changeLeft = dest->changeLeft;
+ changeTop = dest->changeTop;
+ changeZoom = dest->changeZoom;
+ ok = gTrue;
+}
+
+//------------------------------------------------------------------------
+// LinkGoTo
+//------------------------------------------------------------------------
+
+LinkGoTo::LinkGoTo(Object *destObj) {
+ dest = NULL;
+ namedDest = NULL;
+
+ // named destination
+ if (destObj->isName()) {
+ namedDest = new GString(destObj->getName());
+ } else if (destObj->isString()) {
+ namedDest = destObj->getString()->copy();
+
+ // destination dictionary
+ } else if (destObj->isArray()) {
+ dest = new LinkDest(destObj->getArray());
+ if (!dest->isOk()) {
+ delete dest;
+ dest = NULL;
+ }
+ // error
+ } else {
+ error(-1, "Illegal annotation destination %d", destObj->getType());
+ }
+}
+
+LinkGoTo::~LinkGoTo() {
+ if (dest)
+ delete dest;
+ if (namedDest)
+ delete namedDest;
+}
+
+//------------------------------------------------------------------------
+// LinkGoToR
+//------------------------------------------------------------------------
+
+LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj) {
+ dest = NULL;
+ namedDest = NULL;
+
+ // get file name
+ fileName = getFileSpecName(fileSpecObj);
+
+ // named destination
+ if (destObj->isName()) {
+ namedDest = new GString(destObj->getName());
+ } else if (destObj->isString()) {
+ namedDest = destObj->getString()->copy();
+
+ // destination dictionary
+ } else if (destObj->isArray()) {
+ dest = new LinkDest(destObj->getArray());
+ if (!dest->isOk()) {
+ delete dest;
+ dest = NULL;
+ }
+ // error
+ } else {
+ error(-1, "Illegal annotation destination %d", destObj->getType());
+ }
+}
+
+LinkGoToR::~LinkGoToR() {
+ if (fileName)
+ delete fileName;
+ if (dest)
+ delete dest;
+ if (namedDest)
+ delete namedDest;
+}
+
+
+//------------------------------------------------------------------------
+// LinkLaunch
+//------------------------------------------------------------------------
+
+LinkLaunch::LinkLaunch(Object *actionObj) {
+ Object obj1, obj2;
+
+ fileName = NULL;
+ params = NULL;
+
+ if (actionObj->isDict()) {
+ if (!actionObj->dictLookup("F", &obj1)->isNull()) {
+ fileName = getFileSpecName(&obj1);
+ } else {
+ obj1.free();
+#ifdef WIN32
+ if (actionObj->dictLookup("Win", &obj1)->isDict()) {
+ obj1.dictLookup("F", &obj2);
+ fileName = getFileSpecName(&obj2);
+ obj2.free();
+ if (obj1.dictLookup("P", &obj2)->isString()) {
+ params = obj2.getString()->copy();
+ }
+ obj2.free();
+ } else {
+ error(-1, "Bad launch-type link action");
+ }
+#else
+ //~ This hasn't been defined by Adobe yet, so assume it looks
+ //~ just like the Win dictionary until they say otherwise.
+ if (actionObj->dictLookup("Unix", &obj1)->isDict()) {
+ obj1.dictLookup("F", &obj2);
+ fileName = getFileSpecName(&obj2);
+ obj2.free();
+ if (obj1.dictLookup("P", &obj2)->isString()) {
+ params = obj2.getString()->copy();
+ }
+ obj2.free();
+ } else {
+ error(-1, "Bad launch-type link action");
+ }
+#endif
+ }
+ obj1.free();
+ }
+}
+
+LinkLaunch::~LinkLaunch() {
+ if (fileName)
+ delete fileName;
+ if (params)
+ delete params;
+}
+
+//------------------------------------------------------------------------
+// LinkURI
+//------------------------------------------------------------------------
+
+LinkURI::LinkURI(Object *uriObj, GString *baseURI) {
+ GString *uri2;
+ int n;
+ char c;
+
+ uri = NULL;
+ if (uriObj->isString()) {
+ uri2 = uriObj->getString()->copy();
+ if (baseURI && baseURI->getLength() > 0) {
+ n = strcspn(uri2->getCString(), "/:");
+ if (n == uri2->getLength() || uri2->getChar(n) == '/') {
+ uri = baseURI->copy();
+ c = uri->getChar(uri->getLength() - 1);
+ if (c == '/' || c == '?') {
+ if (uri2->getChar(0) == '/') {
+ uri2->del(0);
+ }
+ } else {
+ if (uri2->getChar(0) != '/') {
+ uri->append('/');
+ }
+ }
+ uri->append(uri2);
+ delete uri2;
+ } else {
+ uri = uri2;
+ }
+ } else {
+ uri = uri2;
+ }
+ } else {
+ error(-1, "Illegal URI-type link");
+ }
+}
+
+LinkURI::~LinkURI() {
+ if (uri)
+ delete uri;
+}
+
+//------------------------------------------------------------------------
+// LinkNamed
+//------------------------------------------------------------------------
+
+LinkNamed::LinkNamed(Object *nameObj) {
+ name = NULL;
+ if (nameObj->isName()) {
+ name = new GString(nameObj->getName());
+ }
+}
+
+LinkNamed::~LinkNamed() {
+ if (name) {
+ delete name;
+ }
+}
+
+//------------------------------------------------------------------------
+// LinkMovie
+//------------------------------------------------------------------------
+
+LinkMovie::LinkMovie(Object *annotObj, Object *titleObj) {
+ annotRef.num = -1;
+ title = NULL;
+ if (annotObj->isRef()) {
+ annotRef = annotObj->getRef();
+ } else if (titleObj->isString()) {
+ title = titleObj->getString()->copy();
+ } else {
+ error(-1, "Movie action is missing both the Annot and T keys");
+ }
+}
+
+LinkMovie::~LinkMovie() {
+ if (title) {
+ delete title;
+ }
+}
+
+//------------------------------------------------------------------------
+// LinkUnknown
+//------------------------------------------------------------------------
+
+LinkUnknown::LinkUnknown(char *actionA) {
+ action = new GString(actionA);
+}
+
+LinkUnknown::~LinkUnknown() {
+ delete action;
+}
+
+//------------------------------------------------------------------------
+// LinkBorderStyle
+//------------------------------------------------------------------------
+
+LinkBorderStyle::LinkBorderStyle(LinkBorderType typeA, double widthA,
+ double *dashA, int dashLengthA,
+ double rA, double gA, double bA) {
+ type = typeA;
+ width = widthA;
+ dash = dashA;
+ dashLength = dashLengthA;
+ r = rA;
+ g = gA;
+ b = bA;
+}
+
+LinkBorderStyle::~LinkBorderStyle() {
+ if (dash) {
+ gfree(dash);
+ }
+}
+
+//------------------------------------------------------------------------
+// Link
+//------------------------------------------------------------------------
+
+Link::Link(Dict *dict, GString *baseURI) {
+ Object obj1, obj2, obj3;
+ LinkBorderType borderType;
+ double borderWidth;
+ double *borderDash;
+ int borderDashLength;
+ double borderR, borderG, borderB;
+ double t;
+ int i;
+
+ borderStyle = NULL;
+ action = NULL;
+ ok = gFalse;
+
+ // get rectangle
+ if (!dict->lookup("Rect", &obj1)->isArray()) {
+ error(-1, "Annotation rectangle is wrong type");
+ goto err2;
+ }
+ if (!obj1.arrayGet(0, &obj2)->isNum()) {
+ error(-1, "Bad annotation rectangle");
+ goto err1;
+ }
+ x1 = obj2.getNum();
+ obj2.free();
+ if (!obj1.arrayGet(1, &obj2)->isNum()) {
+ error(-1, "Bad annotation rectangle");
+ goto err1;
+ }
+ y1 = obj2.getNum();
+ obj2.free();
+ if (!obj1.arrayGet(2, &obj2)->isNum()) {
+ error(-1, "Bad annotation rectangle");
+ goto err1;
+ }
+ x2 = obj2.getNum();
+ obj2.free();
+ if (!obj1.arrayGet(3, &obj2)->isNum()) {
+ error(-1, "Bad annotation rectangle");
+ goto err1;
+ }
+ y2 = obj2.getNum();
+ obj2.free();
+ obj1.free();
+ if (x1 > x2) {
+ t = x1;
+ x1 = x2;
+ x2 = t;
+ }
+ if (y1 > y2) {
+ t = y1;
+ y1 = y2;
+ y2 = t;
+ }
+
+ // get the border style info
+ borderType = linkBorderSolid;
+ borderWidth = 1;
+ borderDash = NULL;
+ borderDashLength = 0;
+ borderR = 0;
+ borderG = 0;
+ borderB = 1;
+ if (dict->lookup("BS", &obj1)->isDict()) {
+ if (obj1.dictLookup("S", &obj2)->isName()) {
+ if (obj2.isName("S")) {
+ borderType = linkBorderSolid;
+ } else if (obj2.isName("D")) {
+ borderType = linkBorderDashed;
+ } else if (obj2.isName("B")) {
+ borderType = linkBorderEmbossed;
+ } else if (obj2.isName("I")) {
+ borderType = linkBorderEngraved;
+ } else if (obj2.isName("U")) {
+ borderType = linkBorderUnderlined;
+ }
+ }
+ obj2.free();
+ if (obj1.dictLookup("W", &obj2)->isNum()) {
+ borderWidth = obj2.getNum();
+ }
+ obj2.free();
+ if (obj1.dictLookup("D", &obj2)->isArray()) {
+ borderDashLength = obj2.arrayGetLength();
+ borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
+ for (i = 0; i < borderDashLength; ++i) {
+ if (obj2.arrayGet(i, &obj3)->isNum()) {
+ borderDash[i] = obj3.getNum();
+ } else {
+ borderDash[i] = 1;
+ }
+ obj3.free();
+ }
+ }
+ obj2.free();
+ } else {
+ obj1.free();
+ if (dict->lookup("Border", &obj1)->isArray()) {
+ if (obj1.arrayGetLength() >= 3) {
+ if (obj1.arrayGet(2, &obj2)->isNum()) {
+ borderWidth = obj2.getNum();
+ }
+ obj2.free();
+ if (obj1.arrayGetLength() >= 4) {
+ if (obj1.arrayGet(3, &obj2)->isArray()) {
+ borderType = linkBorderDashed;
+ borderDashLength = obj2.arrayGetLength();
+ borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
+ for (i = 0; i < borderDashLength; ++i) {
+ if (obj2.arrayGet(i, &obj3)->isNum()) {
+ borderDash[i] = obj3.getNum();
+ } else {
+ borderDash[i] = 1;
+ }
+ obj3.free();
+ }
+ } else {
+ // Adobe draws no border at all if the last element is of
+ // the wrong type.
+ borderWidth = 0;
+ }
+ obj2.free();
+ }
+ }
+ }
+ }
+ obj1.free();
+ if (dict->lookup("C", &obj1)->isArray() && obj1.arrayGetLength() == 3) {
+ if (obj1.arrayGet(0, &obj2)->isNum()) {
+ borderR = obj2.getNum();
+ }
+ obj1.free();
+ if (obj1.arrayGet(1, &obj2)->isNum()) {
+ borderG = obj2.getNum();
+ }
+ obj1.free();
+ if (obj1.arrayGet(2, &obj2)->isNum()) {
+ borderB = obj2.getNum();
+ }
+ obj1.free();
+ }
+ obj1.free();
+ borderStyle = new LinkBorderStyle(borderType, borderWidth,
+ borderDash, borderDashLength,
+ borderR, borderG, borderB);
+
+ // look for destination
+ if (!dict->lookup("Dest", &obj1)->isNull()) {
+ action = LinkAction::parseDest(&obj1);
+
+ // look for action
+ } else {
+ obj1.free();
+ if (dict->lookup("A", &obj1)->isDict()) {
+ action = LinkAction::parseAction(&obj1, baseURI);
+ }
+ }
+ obj1.free();
+
+ // check for bad action
+ if (action) {
+ ok = gTrue;
+ }
+
+ return;
+
+ err1:
+ obj2.free();
+ err2:
+ obj1.free();
+}
+
+Link::~Link() {
+ if (borderStyle) {
+ delete borderStyle;
+ }
+ if (action) {
+ delete action;
+ }
+}
+
+//------------------------------------------------------------------------
+// Links
+//------------------------------------------------------------------------
+
+Links::Links(Object *annots, GString *baseURI) {
+ Link *link;
+ Object obj1, obj2;
+ int size;
+ int i;
+
+ links = NULL;
+ size = 0;
+ numLinks = 0;
+
+ if (annots->isArray()) {
+ for (i = 0; i < annots->arrayGetLength(); ++i) {
+ if (annots->arrayGet(i, &obj1)->isDict()) {
+ if (obj1.dictLookup("Subtype", &obj2)->isName("Link")) {
+ link = new Link(obj1.getDict(), baseURI);
+ if (link->isOk()) {
+ if (numLinks >= size) {
+ size += 16;
+ links = (Link **)greallocn(links, size, sizeof(Link *));
+ }
+ links[numLinks++] = link;
+ } else {
+ delete link;
+ }
+ }
+ obj2.free();
+ }
+ obj1.free();
+ }
+ }
+}
+
+Links::~Links() {
+ int i;
+
+ for (i = 0; i < numLinks; ++i)
+ delete links[i];
+ gfree(links);
+}
+
+LinkAction *Links::find(double x, double y) {
+ int i;
+
+ for (i = numLinks - 1; i >= 0; --i) {
+ if (links[i]->inRect(x, y)) {
+ return links[i]->getAction();
+ }
+ }
+ return NULL;
+}
+
+GBool Links::onLink(double x, double y) {
+ int i;
+
+ for (i = 0; i < numLinks; ++i) {
+ if (links[i]->inRect(x, y))
+ return gTrue;
+ }
+ return gFalse;
+}
--- /dev/null
+//========================================================================
+//
+// Link.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef LINK_H
+#define LINK_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "Object.h"
+
+class GString;
+class Array;
+class Dict;
+
+//------------------------------------------------------------------------
+// LinkAction
+//------------------------------------------------------------------------
+
+enum LinkActionKind {
+ actionGoTo, // go to destination
+ actionGoToR, // go to destination in new file
+ actionLaunch, // launch app (or open document)
+ actionURI, // URI
+ actionNamed, // named action
+ actionMovie, // movie action
+ actionUnknown // anything else
+};
+
+class LinkAction {
+public:
+
+ // Destructor.
+ virtual ~LinkAction() {}
+
+ // Was the LinkAction created successfully?
+ virtual GBool isOk() = 0;
+
+ // Check link action type.
+ virtual LinkActionKind getKind() = 0;
+
+ // Parse a destination (old-style action) name, string, or array.
+ static LinkAction *parseDest(Object *obj);
+
+ // Parse an action dictionary.
+ static LinkAction *parseAction(Object *obj, GString *baseURI = NULL);
+
+ // Extract a file name from a file specification (string or
+ // dictionary).
+ static GString *getFileSpecName(Object *fileSpecObj);
+};
+
+//------------------------------------------------------------------------
+// LinkDest
+//------------------------------------------------------------------------
+
+enum LinkDestKind {
+ destXYZ,
+ destFit,
+ destFitH,
+ destFitV,
+ destFitR,
+ destFitB,
+ destFitBH,
+ destFitBV
+};
+
+class LinkDest {
+public:
+
+ // Build a LinkDest from the array.
+ LinkDest(Array *a);
+
+ // Copy a LinkDest.
+ LinkDest *copy() { return new LinkDest(this); }
+
+ // Was the LinkDest created successfully?
+ GBool isOk() { return ok; }
+
+ // Accessors.
+ LinkDestKind getKind() { return kind; }
+ GBool isPageRef() { return pageIsRef; }
+ int getPageNum() { return pageNum; }
+ Ref getPageRef() { return pageRef; }
+ double getLeft() { return left; }
+ double getBottom() { return bottom; }
+ double getRight() { return right; }
+ double getTop() { return top; }
+ double getZoom() { return zoom; }
+ GBool getChangeLeft() { return changeLeft; }
+ GBool getChangeTop() { return changeTop; }
+ GBool getChangeZoom() { return changeZoom; }
+
+private:
+
+ LinkDestKind kind; // destination type
+ GBool pageIsRef; // is the page a reference or number?
+ union {
+ Ref pageRef; // reference to page
+ int pageNum; // one-relative page number
+ };
+ double left, bottom; // position
+ double right, top;
+ double zoom; // zoom factor
+ GBool changeLeft, changeTop; // for destXYZ links, which position
+ GBool changeZoom; // components to change
+ GBool ok; // set if created successfully
+
+ LinkDest(LinkDest *dest);
+};
+
+//------------------------------------------------------------------------
+// LinkGoTo
+//------------------------------------------------------------------------
+
+class LinkGoTo: public LinkAction {
+public:
+
+ // Build a LinkGoTo from a destination (dictionary, name, or string).
+ LinkGoTo(Object *destObj);
+
+ // Destructor.
+ virtual ~LinkGoTo();
+
+ // Was the LinkGoTo created successfully?
+ virtual GBool isOk() { return dest || namedDest; }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionGoTo; }
+ LinkDest *getDest() { return dest; }
+ GString *getNamedDest() { return namedDest; }
+
+private:
+
+ LinkDest *dest; // regular destination (NULL for remote
+ // link with bad destination)
+ GString *namedDest; // named destination (only one of dest and
+ // and namedDest may be non-NULL)
+};
+
+//------------------------------------------------------------------------
+// LinkGoToR
+//------------------------------------------------------------------------
+
+class LinkGoToR: public LinkAction {
+public:
+
+ // Build a LinkGoToR from a file spec (dictionary) and destination
+ // (dictionary, name, or string).
+ LinkGoToR(Object *fileSpecObj, Object *destObj);
+
+ // Destructor.
+ virtual ~LinkGoToR();
+
+ // Was the LinkGoToR created successfully?
+ virtual GBool isOk() { return fileName && (dest || namedDest); }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionGoToR; }
+ GString *getFileName() { return fileName; }
+ LinkDest *getDest() { return dest; }
+ GString *getNamedDest() { return namedDest; }
+
+private:
+
+ GString *fileName; // file name
+ LinkDest *dest; // regular destination (NULL for remote
+ // link with bad destination)
+ GString *namedDest; // named destination (only one of dest and
+ // and namedDest may be non-NULL)
+};
+
+//------------------------------------------------------------------------
+// LinkLaunch
+//------------------------------------------------------------------------
+
+class LinkLaunch: public LinkAction {
+public:
+
+ // Build a LinkLaunch from an action dictionary.
+ LinkLaunch(Object *actionObj);
+
+ // Destructor.
+ virtual ~LinkLaunch();
+
+ // Was the LinkLaunch created successfully?
+ virtual GBool isOk() { return fileName != NULL; }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionLaunch; }
+ GString *getFileName() { return fileName; }
+ GString *getParams() { return params; }
+
+private:
+
+ GString *fileName; // file name
+ GString *params; // parameters
+};
+
+//------------------------------------------------------------------------
+// LinkURI
+//------------------------------------------------------------------------
+
+class LinkURI: public LinkAction {
+public:
+
+ // Build a LinkURI given the URI (string) and base URI.
+ LinkURI(Object *uriObj, GString *baseURI);
+
+ // Destructor.
+ virtual ~LinkURI();
+
+ // Was the LinkURI created successfully?
+ virtual GBool isOk() { return uri != NULL; }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionURI; }
+ GString *getURI() { return uri; }
+
+private:
+
+ GString *uri; // the URI
+};
+
+//------------------------------------------------------------------------
+// LinkNamed
+//------------------------------------------------------------------------
+
+class LinkNamed: public LinkAction {
+public:
+
+ // Build a LinkNamed given the action name.
+ LinkNamed(Object *nameObj);
+
+ virtual ~LinkNamed();
+
+ virtual GBool isOk() { return name != NULL; }
+
+ virtual LinkActionKind getKind() { return actionNamed; }
+ GString *getName() { return name; }
+
+private:
+
+ GString *name;
+};
+
+//------------------------------------------------------------------------
+// LinkMovie
+//------------------------------------------------------------------------
+
+class LinkMovie: public LinkAction {
+public:
+
+ LinkMovie(Object *annotObj, Object *titleObj);
+
+ virtual ~LinkMovie();
+
+ virtual GBool isOk() { return annotRef.num >= 0 || title != NULL; }
+
+ virtual LinkActionKind getKind() { return actionMovie; }
+ GBool hasAnnotRef() { return annotRef.num >= 0; }
+ Ref *getAnnotRef() { return &annotRef; }
+ GString *getTitle() { return title; }
+
+private:
+
+ Ref annotRef;
+ GString *title;
+};
+
+//------------------------------------------------------------------------
+// LinkUnknown
+//------------------------------------------------------------------------
+
+class LinkUnknown: public LinkAction {
+public:
+
+ // Build a LinkUnknown with the specified action type.
+ LinkUnknown(char *actionA);
+
+ // Destructor.
+ virtual ~LinkUnknown();
+
+ // Was the LinkUnknown create successfully?
+ virtual GBool isOk() { return action != NULL; }
+
+ // Accessors.
+ virtual LinkActionKind getKind() { return actionUnknown; }
+ GString *getAction() { return action; }
+
+private:
+
+ GString *action; // action subtype
+};
+
+//------------------------------------------------------------------------
+// LinkBorderStyle
+//------------------------------------------------------------------------
+
+enum LinkBorderType {
+ linkBorderSolid,
+ linkBorderDashed,
+ linkBorderEmbossed,
+ linkBorderEngraved,
+ linkBorderUnderlined
+};
+
+class LinkBorderStyle {
+public:
+
+ LinkBorderStyle(LinkBorderType typeA, double widthA,
+ double *dashA, int dashLengthA,
+ double rA, double gA, double bA);
+ ~LinkBorderStyle();
+
+ LinkBorderType getType() { return type; }
+ double getWidth() { return width; }
+ void getDash(double **dashA, int *dashLengthA)
+ { *dashA = dash; *dashLengthA = dashLength; }
+ void getColor(double *rA, double *gA, double *bA)
+ { *rA = r; *gA = g; *bA = b; }
+
+private:
+
+ LinkBorderType type;
+ double width;
+ double *dash;
+ int dashLength;
+ double r, g, b;
+};
+
+//------------------------------------------------------------------------
+// Link
+//------------------------------------------------------------------------
+
+class Link {
+public:
+
+ // Construct a link, given its dictionary.
+ Link(Dict *dict, GString *baseURI);
+
+ // Destructor.
+ ~Link();
+
+ // Was the link created successfully?
+ GBool isOk() { return ok; }
+
+ // Check if point is inside the link rectangle.
+ GBool inRect(double x, double y)
+ { return x1 <= x && x <= x2 && y1 <= y && y <= y2; }
+
+ // Get action.
+ LinkAction *getAction() { return action; }
+
+ // Get the link rectangle.
+ void getRect(double *xa1, double *ya1, double *xa2, double *ya2)
+ { *xa1 = x1; *ya1 = y1; *xa2 = x2; *ya2 = y2; }
+
+ // Get the border style info.
+ LinkBorderStyle *getBorderStyle() { return borderStyle; }
+
+private:
+
+ double x1, y1; // lower left corner
+ double x2, y2; // upper right corner
+ LinkBorderStyle *borderStyle; // border style
+ LinkAction *action; // action
+ GBool ok; // is link valid?
+};
+
+//------------------------------------------------------------------------
+// Links
+//------------------------------------------------------------------------
+
+class Links {
+public:
+
+ // Extract links from array of annotations.
+ Links(Object *annots, GString *baseURI);
+
+ // Destructor.
+ ~Links();
+
+ // Iterate through list of links.
+ int getNumLinks() { return numLinks; }
+ Link *getLink(int i) { return links[i]; }
+
+ // If point <x>,<y> is in a link, return the associated action;
+ // else return NULL.
+ LinkAction *find(double x, double y);
+
+ // Return true if <x>,<y> is in a link.
+ GBool onLink(double x, double y);
+
+private:
+
+ Link **links;
+ int numLinks;
+};
+
+#endif
--- /dev/null
+# Generated automatically from Makefile.in by configure.
+top_builddir = ../..
+srcdir = .
+top_srcdir = ../..
+include ../../Makefile.common
+
+all: ../libpdf$(A)
+
+libpdf_objects = GHash.$(O) GList.$(O) GString.$(O) gmem.$(O) gfile.$(O) \
+ FoFiTrueType.$(O) FoFiType1.$(O) FoFiType1C.$(O) FoFiBase.$(O) FoFiEncodings.$(O) \
+ OutputDev.$(O) PDFDoc.$(O) Error.$(O) Stream.$(O) Object.$(O) \
+ Decrypt.$(O) Array.$(O) XRef.$(O) Dict.$(O) Parser.$(O) \
+ Lexer.$(O) Outline.$(O) PDFDocEncoding.$(O) Catalog.$(O) \
+ Link.$(O) GlobalParams.$(O) JBIG2Stream.$(O) Page.$(O) JPXStream.$(O) \
+ JArithmeticDecoder.$(O) Gfx.$(O) GfxFont.$(O) CMap.$(O) CharCodeToUnicode.$(O) \
+ PSTokenizer.$(O) FontEncodingTables.$(O) BuiltinFont.$(O) BuiltinFontTables.$(O) \
+ GfxState.$(O) Function.$(O) Annot.$(O) NameToCharCode.$(O) UnicodeMap.$(O) cmyk.$(O) \
+ SecurityHandler.$(O) GFXOutputDev.$(O) InfoOutputDev.$(O) pdf.$(O)
+
+pdf.$(O): pdf.cc
+ $(CC) -I./ pdf.cc -o $@
+cmyk.$(O): cmyk.cc
+ $(CC) -I./ cmyk.cc -o $@
+UnicodeMap.$(O): UnicodeMap.cc
+ $(CC) -I./ UnicodeMap.cc -o $@
+InfoOutputDev.$(O): InfoOutputDev.cc
+ $(CC) -I./ InfoOutputDev.cc -o $@
+GFXOutputDev.$(O): GFXOutputDev.cc
+ $(CC) -I./ GFXOutputDev.cc -o $@
+NameToCharCode.$(O): NameToCharCode.cc
+ $(CC) -I./ NameToCharCode.cc -o $@
+Annot.$(O): Annot.cc
+ $(CC) -I./ Annot.cc -o $@
+Function.$(O): Function.cc
+ $(CC) -I./ Function.cc -o $@
+BuiltinFontTables.$(O): BuiltinFontTables.cc
+ $(CC) -I./ BuiltinFontTables.cc -o $@
+BuiltinFont.$(O): BuiltinFont.cc
+ $(CC) -I./ BuiltinFont.cc -o $@
+FontEncodingTables.$(O): FontEncodingTables.cc
+ $(CC) -I./ FontEncodingTables.cc -o $@
+PSTokenizer.$(O): PSTokenizer.cc
+ $(CC) -I./ PSTokenizer.cc -o $@
+CharCodeToUnicode.$(O): CharCodeToUnicode.cc
+ $(CC) -I./ CharCodeToUnicode.cc -o $@
+CMap.$(O): CMap.cc
+ $(CC) -I./ CMap.cc -o $@
+GfxFont.$(O): GfxFont.cc
+ $(CC) -I./ GfxFont.cc -o $@
+Gfx.$(O): Gfx.cc
+ $(CC) -I./ Gfx.cc -o $@
+GfxState.$(O): GfxState.cc
+ $(CC) -I./ GfxState.cc -o $@
+JArithmeticDecoder.$(O): JArithmeticDecoder.cc
+ $(CC) -I./ JArithmeticDecoder.cc -o $@
+JPXStream.$(O): JPXStream.cc
+ $(CC) -I./ JPXStream.cc -o $@
+GlobalParams.$(O): GlobalParams.cc GlobalParams.h
+ $(CC) -I./ GlobalParams.cc -o $@
+JBIG2Stream.$(O): JBIG2Stream.cc
+ $(CC) -I./ JBIG2Stream.cc -o $@
+Page.$(O): Page.cc
+ $(CC) -I./ Page.cc -o $@
+Link.$(O): Link.cc
+ $(CC) -I./ Link.cc -o $@
+Catalog.$(O): Catalog.cc
+ $(CC) -I./ Catalog.cc -o $@
+PDFDocEncoding.$(O): PDFDocEncoding.cc
+ $(CC) -I./ PDFDocEncoding.cc -o $@
+Outline.$(O): Outline.cc
+ $(CC) -I./ Outline.cc -o $@
+Lexer.$(O): Lexer.cc
+ $(CC) -I./ Lexer.cc -o $@
+Parser.$(O): Parser.cc
+ $(CC) -I./ Parser.cc -o $@
+XRef.$(O): XRef.cc
+ $(CC) -I./ XRef.cc -o $@
+Array.$(O): Array.cc
+ $(CC) -I./ Array.cc -o $@
+Dict.$(O): Dict.cc
+ $(CC) -I./ Dict.cc -o $@
+Decrypt.$(O): Decrypt.cc
+ $(CC) -I./ Decrypt.cc -o $@
+Object.$(O): Object.cc
+ $(CC) -I./ Object.cc -o $@
+Error.$(O): Error.cc aconf.h
+ $(CC) -I./ Error.cc -o $@
+Stream.$(O): Stream.cc
+ $(CC) -I./ Stream.cc -o $@
+PDFDoc.$(O): PDFDoc.cc
+ $(CC) -I./ PDFDoc.cc -o $@
+SecurityHandler.$(O): SecurityHandler.cc SecurityHandler.h
+ $(CC) -I./ SecurityHandler.cc -o $@
+OutputDev.$(O): OutputDev.cc GfxState.h Stream.h Object.h OutputDev.h gtypes.h CharTypes.h
+ $(CC) -I./ OutputDev.cc -o $@
+FoFiBase.$(O): FoFiBase.cc
+ $(CC) -I./ FoFiBase.cc -o $@
+FoFiTrueType.$(O): FoFiTrueType.cc FoFiBase.h
+ $(CC) -I./ FoFiTrueType.cc -o $@
+FoFiEncodings.$(O): FoFiEncodings.cc FoFiEncodings.h
+ $(CC) -I./ FoFiEncodings.cc -o $@
+FoFiType1C.$(O): FoFiType1C.cc FoFiBase.h
+ $(CC) -I./ FoFiType1C.cc -o $@
+FoFiType1.$(O): FoFiType1.cc FoFiBase.h
+ $(CC) -I./ FoFiType1.cc -o $@
+GList.$(O): GList.cc
+ $(CC) -I./ GList.cc -o $@
+GString.$(O): GString.cc
+ $(CC) -I./ GString.cc -o $@
+GHash.$(O): GHash.cc
+ $(CC) -I./ GHash.cc -o $@
+gfile.$(O): gfile.cc
+ $(CC) -I./ gfile.cc -o $@
+gmem.$(O): gmem.c
+ $(C) -I./ gmem.c -o $@
+
+../libpdf$(A): $(libpdf_objects)
+ $(AR) r ../libpdf$(A) $(libpdf_objects)
+ $(RANLIB) ../libpdf$(A)
+
+install:
+uninstall:
+
+clean:
+ rm -f *.o *.obj *.lo *.a *.lib *.la gmon.out
+
--- /dev/null
+//========================================================================
+//
+// NameToCharCode.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <string.h>
+#include "gmem.h"
+#include "NameToCharCode.h"
+
+//------------------------------------------------------------------------
+
+struct NameToCharCodeEntry {
+ char *name;
+ CharCode c;
+};
+
+//------------------------------------------------------------------------
+
+NameToCharCode::NameToCharCode() {
+ int i;
+
+ size = 31;
+ len = 0;
+ tab = (NameToCharCodeEntry *)gmallocn(size, sizeof(NameToCharCodeEntry));
+ for (i = 0; i < size; ++i) {
+ tab[i].name = NULL;
+ }
+}
+
+NameToCharCode::~NameToCharCode() {
+ int i;
+
+ for (i = 0; i < size; ++i) {
+ if (tab[i].name) {
+ gfree(tab[i].name);
+ }
+ }
+ gfree(tab);
+}
+
+void NameToCharCode::add(char *name, CharCode c) {
+ NameToCharCodeEntry *oldTab;
+ int h, i, oldSize;
+
+ // expand the table if necessary
+ if (len >= size / 2) {
+ oldSize = size;
+ oldTab = tab;
+ size = 2*size + 1;
+ tab = (NameToCharCodeEntry *)gmallocn(size, sizeof(NameToCharCodeEntry));
+ for (h = 0; h < size; ++h) {
+ tab[h].name = NULL;
+ }
+ for (i = 0; i < oldSize; ++i) {
+ if (oldTab[i].name) {
+ h = hash(oldTab[i].name);
+ while (tab[h].name) {
+ if (++h == size) {
+ h = 0;
+ }
+ }
+ tab[h] = oldTab[i];
+ }
+ }
+ gfree(oldTab);
+ }
+
+ // add the new name
+ h = hash(name);
+ while (tab[h].name && strcmp(tab[h].name, name)) {
+ if (++h == size) {
+ h = 0;
+ }
+ }
+ if (!tab[h].name) {
+ tab[h].name = copyString(name);
+ }
+ tab[h].c = c;
+
+ ++len;
+}
+
+CharCode NameToCharCode::lookup(char *name) {
+ int h;
+
+ h = hash(name);
+ while (tab[h].name) {
+ if (!strcmp(tab[h].name, name)) {
+ return tab[h].c;
+ }
+ if (++h == size) {
+ h = 0;
+ }
+ }
+ return 0;
+}
+
+int NameToCharCode::hash(char *name) {
+ char *p;
+ unsigned int h;
+
+ h = 0;
+ for (p = name; *p; ++p) {
+ h = 17 * h + (int)(*p & 0xff);
+ }
+ return (int)(h % size);
+}
--- /dev/null
+//========================================================================
+//
+// NameToCharCode.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef NAMETOCHARCODE_H
+#define NAMETOCHARCODE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "CharTypes.h"
+
+struct NameToCharCodeEntry;
+
+//------------------------------------------------------------------------
+
+class NameToCharCode {
+public:
+
+ NameToCharCode();
+ ~NameToCharCode();
+
+ void add(char *name, CharCode c);
+ CharCode lookup(char *name);
+
+private:
+
+ int hash(char *name);
+
+ NameToCharCodeEntry *tab;
+ int size;
+ int len;
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// NameToUnicodeTable.h
+//
+// Copyright 2001-2004 Glyph & Cog, LLC
+//
+//========================================================================
+
+static struct {
+ Unicode u;
+ char *name;
+} nameToUnicodeTab[] = {
+ {0x0021, "!"},
+ {0x0023, "#"},
+ {0x0024, "$"},
+ {0x0025, "%"},
+ {0x0026, "&"},
+ {0x0027, "'"},
+ {0x0028, "("},
+ {0x0029, ")"},
+ {0x002a, "*"},
+ {0x002b, "+"},
+ {0x002c, ","},
+ {0x002d, "-"},
+ {0x002e, "."},
+ {0x002f, "/"},
+ {0x0030, "0"},
+ {0x0031, "1"},
+ {0x0032, "2"},
+ {0x0033, "3"},
+ {0x0034, "4"},
+ {0x0035, "5"},
+ {0x0036, "6"},
+ {0x0037, "7"},
+ {0x0038, "8"},
+ {0x0039, "9"},
+ {0x003a, ":"},
+ {0x003b, ";"},
+ {0x003c, "<"},
+ {0x003d, "="},
+ {0x003e, ">"},
+ {0x003f, "?"},
+ {0x0040, "@"},
+ {0x0041, "A"},
+ {0x00c6, "AE"},
+ {0x01fc, "AEacute"},
+ {0xf7e6, "AEsmall"},
+ {0x00c1, "Aacute"},
+ {0xf7e1, "Aacutesmall"},
+ {0x0102, "Abreve"},
+ {0x00c2, "Acircumflex"},
+ {0xf7e2, "Acircumflexsmall"},
+ {0xf6c9, "Acute"},
+ {0xf7b4, "Acutesmall"},
+ {0x00c4, "Adieresis"},
+ {0xf7e4, "Adieresissmall"},
+ {0x00c0, "Agrave"},
+ {0xf7e0, "Agravesmall"},
+ {0x0391, "Alpha"},
+ {0x0386, "Alphatonos"},
+ {0x0100, "Amacron"},
+ {0x0104, "Aogonek"},
+ {0x00c5, "Aring"},
+ {0x01fa, "Aringacute"},
+ {0xf7e5, "Aringsmall"},
+ {0xf761, "Asmall"},
+ {0x00c3, "Atilde"},
+ {0xf7e3, "Atildesmall"},
+ {0x0042, "B"},
+ {0x0392, "Beta"},
+ {0xf6f4, "Brevesmall"},
+ {0xf762, "Bsmall"},
+ {0x0043, "C"},
+ {0x0106, "Cacute"},
+ {0xf6ca, "Caron"},
+ {0xf6f5, "Caronsmall"},
+ {0x010c, "Ccaron"},
+ {0x00c7, "Ccedilla"},
+ {0xf7e7, "Ccedillasmall"},
+ {0x0108, "Ccircumflex"},
+ {0x010a, "Cdotaccent"},
+ {0xf7b8, "Cedillasmall"},
+ {0x03a7, "Chi"},
+ {0xf6f6, "Circumflexsmall"},
+ {0xf763, "Csmall"},
+ {0x0044, "D"},
+ {0x010e, "Dcaron"},
+ {0x0110, "Dcroat"},
+ {0x2206, "Delta"},
+ {0xf6cb, "Dieresis"},
+ {0xf6cc, "DieresisAcute"},
+ {0xf6cd, "DieresisGrave"},
+ {0xf7a8, "Dieresissmall"},
+ {0xf6f7, "Dotaccentsmall"},
+ {0xf764, "Dsmall"},
+ {0x0045, "E"},
+ {0x00c9, "Eacute"},
+ {0xf7e9, "Eacutesmall"},
+ {0x0114, "Ebreve"},
+ {0x011a, "Ecaron"},
+ {0x00ca, "Ecircumflex"},
+ {0xf7ea, "Ecircumflexsmall"},
+ {0x00cb, "Edieresis"},
+ {0xf7eb, "Edieresissmall"},
+ {0x0116, "Edotaccent"},
+ {0x00c8, "Egrave"},
+ {0xf7e8, "Egravesmall"},
+ {0x0112, "Emacron"},
+ {0x014a, "Eng"},
+ {0x0118, "Eogonek"},
+ {0x0395, "Epsilon"},
+ {0x0388, "Epsilontonos"},
+ {0xf765, "Esmall"},
+ {0x0397, "Eta"},
+ {0x0389, "Etatonos"},
+ {0x00d0, "Eth"},
+ {0xf7f0, "Ethsmall"},
+ {0x20ac, "Euro"},
+ {0x0046, "F"},
+ {0xf766, "Fsmall"},
+ {0x0047, "G"},
+ {0x0393, "Gamma"},
+ {0x011e, "Gbreve"},
+ {0x01e6, "Gcaron"},
+ {0x011c, "Gcircumflex"},
+ {0x0122, "Gcommaaccent"},
+ {0x0120, "Gdotaccent"},
+ {0xf6ce, "Grave"},
+ {0xf760, "Gravesmall"},
+ {0xf767, "Gsmall"},
+ {0x0048, "H"},
+ {0x25cf, "H18533"},
+ {0x25aa, "H18543"},
+ {0x25ab, "H18551"},
+ {0x25a1, "H22073"},
+ {0x0126, "Hbar"},
+ {0x0124, "Hcircumflex"},
+ {0xf768, "Hsmall"},
+ {0xf6cf, "Hungarumlaut"},
+ {0xf6f8, "Hungarumlautsmall"},
+ {0x0049, "I"},
+ {0x0132, "IJ"},
+ {0x00cd, "Iacute"},
+ {0xf7ed, "Iacutesmall"},
+ {0x012c, "Ibreve"},
+ {0x00ce, "Icircumflex"},
+ {0xf7ee, "Icircumflexsmall"},
+ {0x00cf, "Idieresis"},
+ {0xf7ef, "Idieresissmall"},
+ {0x0130, "Idotaccent"},
+ {0x2111, "Ifraktur"},
+ {0x00cc, "Igrave"},
+ {0xf7ec, "Igravesmall"},
+ {0x012a, "Imacron"},
+ {0x012e, "Iogonek"},
+ {0x0399, "Iota"},
+ {0x03aa, "Iotadieresis"},
+ {0x038a, "Iotatonos"},
+ {0xf769, "Ismall"},
+ {0x0128, "Itilde"},
+ {0x004a, "J"},
+ {0x0134, "Jcircumflex"},
+ {0xf76a, "Jsmall"},
+ {0x004b, "K"},
+ {0x039a, "Kappa"},
+ {0x0136, "Kcommaaccent"},
+ {0xf76b, "Ksmall"},
+ {0x004c, "L"},
+ {0xf6bf, "LL"},
+ {0x0139, "Lacute"},
+ {0x039b, "Lambda"},
+ {0x013d, "Lcaron"},
+ {0x013b, "Lcommaaccent"},
+ {0x013f, "Ldot"},
+ {0x0141, "Lslash"},
+ {0xf6f9, "Lslashsmall"},
+ {0xf76c, "Lsmall"},
+ {0x004d, "M"},
+ {0xf6d0, "Macron"},
+ {0xf7af, "Macronsmall"},
+ {0xf76d, "Msmall"},
+ {0x039c, "Mu"},
+ {0x004e, "N"},
+ {0x0143, "Nacute"},
+ {0x0147, "Ncaron"},
+ {0x0145, "Ncommaaccent"},
+ {0xf76e, "Nsmall"},
+ {0x00d1, "Ntilde"},
+ {0xf7f1, "Ntildesmall"},
+ {0x039d, "Nu"},
+ {0x004f, "O"},
+ {0x0152, "OE"},
+ {0xf6fa, "OEsmall"},
+ {0x00d3, "Oacute"},
+ {0xf7f3, "Oacutesmall"},
+ {0x014e, "Obreve"},
+ {0x00d4, "Ocircumflex"},
+ {0xf7f4, "Ocircumflexsmall"},
+ {0x00d6, "Odieresis"},
+ {0xf7f6, "Odieresissmall"},
+ {0xf6fb, "Ogoneksmall"},
+ {0x00d2, "Ograve"},
+ {0xf7f2, "Ogravesmall"},
+ {0x01a0, "Ohorn"},
+ {0x0150, "Ohungarumlaut"},
+ {0x014c, "Omacron"},
+ {0x2126, "Omega"},
+ {0x038f, "Omegatonos"},
+ {0x039f, "Omicron"},
+ {0x038c, "Omicrontonos"},
+ {0x00d8, "Oslash"},
+ {0x01fe, "Oslashacute"},
+ {0xf7f8, "Oslashsmall"},
+ {0xf76f, "Osmall"},
+ {0x00d5, "Otilde"},
+ {0xf7f5, "Otildesmall"},
+ {0x0050, "P"},
+ {0x03a6, "Phi"},
+ {0x03a0, "Pi"},
+ {0x03a8, "Psi"},
+ {0xf770, "Psmall"},
+ {0x0051, "Q"},
+ {0xf771, "Qsmall"},
+ {0x0052, "R"},
+ {0x0154, "Racute"},
+ {0x0158, "Rcaron"},
+ {0x0156, "Rcommaaccent"},
+ {0x211c, "Rfraktur"},
+ {0x03a1, "Rho"},
+ {0xf6fc, "Ringsmall"},
+ {0xf772, "Rsmall"},
+ {0x0053, "S"},
+ {0x250c, "SF010000"},
+ {0x2514, "SF020000"},
+ {0x2510, "SF030000"},
+ {0x2518, "SF040000"},
+ {0x253c, "SF050000"},
+ {0x252c, "SF060000"},
+ {0x2534, "SF070000"},
+ {0x251c, "SF080000"},
+ {0x2524, "SF090000"},
+ {0x2500, "SF100000"},
+ {0x2502, "SF110000"},
+ {0x2561, "SF190000"},
+ {0x2562, "SF200000"},
+ {0x2556, "SF210000"},
+ {0x2555, "SF220000"},
+ {0x2563, "SF230000"},
+ {0x2551, "SF240000"},
+ {0x2557, "SF250000"},
+ {0x255d, "SF260000"},
+ {0x255c, "SF270000"},
+ {0x255b, "SF280000"},
+ {0x255e, "SF360000"},
+ {0x255f, "SF370000"},
+ {0x255a, "SF380000"},
+ {0x2554, "SF390000"},
+ {0x2569, "SF400000"},
+ {0x2566, "SF410000"},
+ {0x2560, "SF420000"},
+ {0x2550, "SF430000"},
+ {0x256c, "SF440000"},
+ {0x2567, "SF450000"},
+ {0x2568, "SF460000"},
+ {0x2564, "SF470000"},
+ {0x2565, "SF480000"},
+ {0x2559, "SF490000"},
+ {0x2558, "SF500000"},
+ {0x2552, "SF510000"},
+ {0x2553, "SF520000"},
+ {0x256b, "SF530000"},
+ {0x256a, "SF540000"},
+ {0x015a, "Sacute"},
+ {0x0160, "Scaron"},
+ {0xf6fd, "Scaronsmall"},
+ {0x015e, "Scedilla"},
+ {0x015c, "Scircumflex"},
+ {0x0218, "Scommaaccent"},
+ {0x03a3, "Sigma"},
+ {0xf773, "Ssmall"},
+ {0x0054, "T"},
+ {0x03a4, "Tau"},
+ {0x0166, "Tbar"},
+ {0x0164, "Tcaron"},
+ {0x0162, "Tcommaaccent"},
+ {0x0398, "Theta"},
+ {0x00de, "Thorn"},
+ {0xf7fe, "Thornsmall"},
+ {0xf6fe, "Tildesmall"},
+ {0xf774, "Tsmall"},
+ {0x0055, "U"},
+ {0x00da, "Uacute"},
+ {0xf7fa, "Uacutesmall"},
+ {0x016c, "Ubreve"},
+ {0x00db, "Ucircumflex"},
+ {0xf7fb, "Ucircumflexsmall"},
+ {0x00dc, "Udieresis"},
+ {0xf7fc, "Udieresissmall"},
+ {0x00d9, "Ugrave"},
+ {0xf7f9, "Ugravesmall"},
+ {0x01af, "Uhorn"},
+ {0x0170, "Uhungarumlaut"},
+ {0x016a, "Umacron"},
+ {0x0172, "Uogonek"},
+ {0x03a5, "Upsilon"},
+ {0x03d2, "Upsilon1"},
+ {0x03ab, "Upsilondieresis"},
+ {0x038e, "Upsilontonos"},
+ {0x016e, "Uring"},
+ {0xf775, "Usmall"},
+ {0x0168, "Utilde"},
+ {0x0056, "V"},
+ {0xf776, "Vsmall"},
+ {0x0057, "W"},
+ {0x1e82, "Wacute"},
+ {0x0174, "Wcircumflex"},
+ {0x1e84, "Wdieresis"},
+ {0x1e80, "Wgrave"},
+ {0xf777, "Wsmall"},
+ {0x0058, "X"},
+ {0x039e, "Xi"},
+ {0xf778, "Xsmall"},
+ {0x0059, "Y"},
+ {0x00dd, "Yacute"},
+ {0xf7fd, "Yacutesmall"},
+ {0x0176, "Ycircumflex"},
+ {0x0178, "Ydieresis"},
+ {0xf7ff, "Ydieresissmall"},
+ {0x1ef2, "Ygrave"},
+ {0xf779, "Ysmall"},
+ {0x005a, "Z"},
+ {0x0179, "Zacute"},
+ {0x017d, "Zcaron"},
+ {0xf6ff, "Zcaronsmall"},
+ {0x017b, "Zdotaccent"},
+ {0x0396, "Zeta"},
+ {0xf77a, "Zsmall"},
+ {0x0022, "\""},
+ {0x005c, "\\"},
+ {0x005d, "]"},
+ {0x005e, "^"},
+ {0x005f, "_"},
+ {0x0060, "`"},
+ {0x0061, "a"},
+ {0x00e1, "aacute"},
+ {0x0103, "abreve"},
+ {0x00e2, "acircumflex"},
+ {0x00b4, "acute"},
+ {0x0301, "acutecomb"},
+ {0x00e4, "adieresis"},
+ {0x00e6, "ae"},
+ {0x01fd, "aeacute"},
+ {0x2015, "afii00208"},
+ {0x0410, "afii10017"},
+ {0x0411, "afii10018"},
+ {0x0412, "afii10019"},
+ {0x0413, "afii10020"},
+ {0x0414, "afii10021"},
+ {0x0415, "afii10022"},
+ {0x0401, "afii10023"},
+ {0x0416, "afii10024"},
+ {0x0417, "afii10025"},
+ {0x0418, "afii10026"},
+ {0x0419, "afii10027"},
+ {0x041a, "afii10028"},
+ {0x041b, "afii10029"},
+ {0x041c, "afii10030"},
+ {0x041d, "afii10031"},
+ {0x041e, "afii10032"},
+ {0x041f, "afii10033"},
+ {0x0420, "afii10034"},
+ {0x0421, "afii10035"},
+ {0x0422, "afii10036"},
+ {0x0423, "afii10037"},
+ {0x0424, "afii10038"},
+ {0x0425, "afii10039"},
+ {0x0426, "afii10040"},
+ {0x0427, "afii10041"},
+ {0x0428, "afii10042"},
+ {0x0429, "afii10043"},
+ {0x042a, "afii10044"},
+ {0x042b, "afii10045"},
+ {0x042c, "afii10046"},
+ {0x042d, "afii10047"},
+ {0x042e, "afii10048"},
+ {0x042f, "afii10049"},
+ {0x0490, "afii10050"},
+ {0x0402, "afii10051"},
+ {0x0403, "afii10052"},
+ {0x0404, "afii10053"},
+ {0x0405, "afii10054"},
+ {0x0406, "afii10055"},
+ {0x0407, "afii10056"},
+ {0x0408, "afii10057"},
+ {0x0409, "afii10058"},
+ {0x040a, "afii10059"},
+ {0x040b, "afii10060"},
+ {0x040c, "afii10061"},
+ {0x040e, "afii10062"},
+ {0xf6c4, "afii10063"},
+ {0xf6c5, "afii10064"},
+ {0x0430, "afii10065"},
+ {0x0431, "afii10066"},
+ {0x0432, "afii10067"},
+ {0x0433, "afii10068"},
+ {0x0434, "afii10069"},
+ {0x0435, "afii10070"},
+ {0x0451, "afii10071"},
+ {0x0436, "afii10072"},
+ {0x0437, "afii10073"},
+ {0x0438, "afii10074"},
+ {0x0439, "afii10075"},
+ {0x043a, "afii10076"},
+ {0x043b, "afii10077"},
+ {0x043c, "afii10078"},
+ {0x043d, "afii10079"},
+ {0x043e, "afii10080"},
+ {0x043f, "afii10081"},
+ {0x0440, "afii10082"},
+ {0x0441, "afii10083"},
+ {0x0442, "afii10084"},
+ {0x0443, "afii10085"},
+ {0x0444, "afii10086"},
+ {0x0445, "afii10087"},
+ {0x0446, "afii10088"},
+ {0x0447, "afii10089"},
+ {0x0448, "afii10090"},
+ {0x0449, "afii10091"},
+ {0x044a, "afii10092"},
+ {0x044b, "afii10093"},
+ {0x044c, "afii10094"},
+ {0x044d, "afii10095"},
+ {0x044e, "afii10096"},
+ {0x044f, "afii10097"},
+ {0x0491, "afii10098"},
+ {0x0452, "afii10099"},
+ {0x0453, "afii10100"},
+ {0x0454, "afii10101"},
+ {0x0455, "afii10102"},
+ {0x0456, "afii10103"},
+ {0x0457, "afii10104"},
+ {0x0458, "afii10105"},
+ {0x0459, "afii10106"},
+ {0x045a, "afii10107"},
+ {0x045b, "afii10108"},
+ {0x045c, "afii10109"},
+ {0x045e, "afii10110"},
+ {0x040f, "afii10145"},
+ {0x0462, "afii10146"},
+ {0x0472, "afii10147"},
+ {0x0474, "afii10148"},
+ {0xf6c6, "afii10192"},
+ {0x045f, "afii10193"},
+ {0x0463, "afii10194"},
+ {0x0473, "afii10195"},
+ {0x0475, "afii10196"},
+ {0xf6c7, "afii10831"},
+ {0xf6c8, "afii10832"},
+ {0x04d9, "afii10846"},
+ {0x200e, "afii299"},
+ {0x200f, "afii300"},
+ {0x200d, "afii301"},
+ {0x066a, "afii57381"},
+ {0x060c, "afii57388"},
+ {0x0660, "afii57392"},
+ {0x0661, "afii57393"},
+ {0x0662, "afii57394"},
+ {0x0663, "afii57395"},
+ {0x0664, "afii57396"},
+ {0x0665, "afii57397"},
+ {0x0666, "afii57398"},
+ {0x0667, "afii57399"},
+ {0x0668, "afii57400"},
+ {0x0669, "afii57401"},
+ {0x061b, "afii57403"},
+ {0x061f, "afii57407"},
+ {0x0621, "afii57409"},
+ {0x0622, "afii57410"},
+ {0x0623, "afii57411"},
+ {0x0624, "afii57412"},
+ {0x0625, "afii57413"},
+ {0x0626, "afii57414"},
+ {0x0627, "afii57415"},
+ {0x0628, "afii57416"},
+ {0x0629, "afii57417"},
+ {0x062a, "afii57418"},
+ {0x062b, "afii57419"},
+ {0x062c, "afii57420"},
+ {0x062d, "afii57421"},
+ {0x062e, "afii57422"},
+ {0x062f, "afii57423"},
+ {0x0630, "afii57424"},
+ {0x0631, "afii57425"},
+ {0x0632, "afii57426"},
+ {0x0633, "afii57427"},
+ {0x0634, "afii57428"},
+ {0x0635, "afii57429"},
+ {0x0636, "afii57430"},
+ {0x0637, "afii57431"},
+ {0x0638, "afii57432"},
+ {0x0639, "afii57433"},
+ {0x063a, "afii57434"},
+ {0x0640, "afii57440"},
+ {0x0641, "afii57441"},
+ {0x0642, "afii57442"},
+ {0x0643, "afii57443"},
+ {0x0644, "afii57444"},
+ {0x0645, "afii57445"},
+ {0x0646, "afii57446"},
+ {0x0648, "afii57448"},
+ {0x0649, "afii57449"},
+ {0x064a, "afii57450"},
+ {0x064b, "afii57451"},
+ {0x064c, "afii57452"},
+ {0x064d, "afii57453"},
+ {0x064e, "afii57454"},
+ {0x064f, "afii57455"},
+ {0x0650, "afii57456"},
+ {0x0651, "afii57457"},
+ {0x0652, "afii57458"},
+ {0x0647, "afii57470"},
+ {0x06a4, "afii57505"},
+ {0x067e, "afii57506"},
+ {0x0686, "afii57507"},
+ {0x0698, "afii57508"},
+ {0x06af, "afii57509"},
+ {0x0679, "afii57511"},
+ {0x0688, "afii57512"},
+ {0x0691, "afii57513"},
+ {0x06ba, "afii57514"},
+ {0x06d2, "afii57519"},
+ {0x06d5, "afii57534"},
+ {0x20aa, "afii57636"},
+ {0x05be, "afii57645"},
+ {0x05c3, "afii57658"},
+ {0x05d0, "afii57664"},
+ {0x05d1, "afii57665"},
+ {0x05d2, "afii57666"},
+ {0x05d3, "afii57667"},
+ {0x05d4, "afii57668"},
+ {0x05d5, "afii57669"},
+ {0x05d6, "afii57670"},
+ {0x05d7, "afii57671"},
+ {0x05d8, "afii57672"},
+ {0x05d9, "afii57673"},
+ {0x05da, "afii57674"},
+ {0x05db, "afii57675"},
+ {0x05dc, "afii57676"},
+ {0x05dd, "afii57677"},
+ {0x05de, "afii57678"},
+ {0x05df, "afii57679"},
+ {0x05e0, "afii57680"},
+ {0x05e1, "afii57681"},
+ {0x05e2, "afii57682"},
+ {0x05e3, "afii57683"},
+ {0x05e4, "afii57684"},
+ {0x05e5, "afii57685"},
+ {0x05e6, "afii57686"},
+ {0x05e7, "afii57687"},
+ {0x05e8, "afii57688"},
+ {0x05e9, "afii57689"},
+ {0x05ea, "afii57690"},
+ {0xfb2a, "afii57694"},
+ {0xfb2b, "afii57695"},
+ {0xfb4b, "afii57700"},
+ {0xfb1f, "afii57705"},
+ {0x05f0, "afii57716"},
+ {0x05f1, "afii57717"},
+ {0x05f2, "afii57718"},
+ {0xfb35, "afii57723"},
+ {0x05b4, "afii57793"},
+ {0x05b5, "afii57794"},
+ {0x05b6, "afii57795"},
+ {0x05bb, "afii57796"},
+ {0x05b8, "afii57797"},
+ {0x05b7, "afii57798"},
+ {0x05b0, "afii57799"},
+ {0x05b2, "afii57800"},
+ {0x05b1, "afii57801"},
+ {0x05b3, "afii57802"},
+ {0x05c2, "afii57803"},
+ {0x05c1, "afii57804"},
+ {0x05b9, "afii57806"},
+ {0x05bc, "afii57807"},
+ {0x05bd, "afii57839"},
+ {0x05bf, "afii57841"},
+ {0x05c0, "afii57842"},
+ {0x02bc, "afii57929"},
+ {0x2105, "afii61248"},
+ {0x2113, "afii61289"},
+ {0x2116, "afii61352"},
+ {0x202c, "afii61573"},
+ {0x202d, "afii61574"},
+ {0x202e, "afii61575"},
+ {0x200c, "afii61664"},
+ {0x066d, "afii63167"},
+ {0x02bd, "afii64937"},
+ {0x00e0, "agrave"},
+ {0x2135, "aleph"},
+ {0x03b1, "alpha"},
+ {0x03ac, "alphatonos"},
+ {0x0101, "amacron"},
+ {0x0026, "ampersand"},
+ {0xf726, "ampersandsmall"},
+ {0x2220, "angle"},
+ {0x2329, "angleleft"},
+ {0x232a, "angleright"},
+ {0x0387, "anoteleia"},
+ {0x0105, "aogonek"},
+ {0x2248, "approxequal"},
+ {0x00e5, "aring"},
+ {0x01fb, "aringacute"},
+ {0x2194, "arrowboth"},
+ {0x21d4, "arrowdblboth"},
+ {0x21d3, "arrowdbldown"},
+ {0x21d0, "arrowdblleft"},
+ {0x21d2, "arrowdblright"},
+ {0x21d1, "arrowdblup"},
+ {0x2193, "arrowdown"},
+ {0xf8e7, "arrowhorizex"},
+ {0x2190, "arrowleft"},
+ {0x2192, "arrowright"},
+ {0x2191, "arrowup"},
+ {0x2195, "arrowupdn"},
+ {0x21a8, "arrowupdnbse"},
+ {0xf8e6, "arrowvertex"},
+ {0x005e, "asciicircum"},
+ {0x007e, "asciitilde"},
+ {0x002a, "asterisk"},
+ {0x2217, "asteriskmath"},
+ {0xf6e9, "asuperior"},
+ {0x0040, "at"},
+ {0x00e3, "atilde"},
+ {0x0062, "b"},
+ {0x005c, "backslash"},
+ {0x007c, "bar"},
+ {0x03b2, "beta"},
+ {0x2588, "block"},
+ {0xf8f4, "braceex"},
+ {0x007b, "braceleft"},
+ {0xf8f3, "braceleftbt"},
+ {0xf8f2, "braceleftmid"},
+ {0xf8f1, "bracelefttp"},
+ {0x007d, "braceright"},
+ {0xf8fe, "bracerightbt"},
+ {0xf8fd, "bracerightmid"},
+ {0xf8fc, "bracerighttp"},
+ {0x005b, "bracketleft"},
+ {0xf8f0, "bracketleftbt"},
+ {0xf8ef, "bracketleftex"},
+ {0xf8ee, "bracketlefttp"},
+ {0x005d, "bracketright"},
+ {0xf8fb, "bracketrightbt"},
+ {0xf8fa, "bracketrightex"},
+ {0xf8f9, "bracketrighttp"},
+ {0x02d8, "breve"},
+ {0x00a6, "brokenbar"},
+ {0xf6ea, "bsuperior"},
+ {0x2022, "bullet"},
+ {0x0063, "c"},
+ {0x0107, "cacute"},
+ {0x02c7, "caron"},
+ {0x21b5, "carriagereturn"},
+ {0x010d, "ccaron"},
+ {0x00e7, "ccedilla"},
+ {0x0109, "ccircumflex"},
+ {0x010b, "cdotaccent"},
+ {0x00b8, "cedilla"},
+ {0x00a2, "cent"},
+ {0xf6df, "centinferior"},
+ {0xf7a2, "centoldstyle"},
+ {0xf6e0, "centsuperior"},
+ {0x03c7, "chi"},
+ {0x25cb, "circle"},
+ {0x2297, "circlemultiply"},
+ {0x2295, "circleplus"},
+ {0x02c6, "circumflex"},
+ {0x2663, "club"},
+ {0x003a, "colon"},
+ {0x20a1, "colonmonetary"},
+ {0x002c, "comma"},
+ {0xf6c3, "commaaccent"},
+ {0xf6e1, "commainferior"},
+ {0xf6e2, "commasuperior"},
+ {0x2245, "congruent"},
+ {0x00a9, "copyright"},
+ {0x00a9, "copyrightsans"},
+ {0x00a9, "copyrightserif"},
+ {0x00a4, "currency"},
+ {0xf6d1, "cyrBreve"},
+ {0xf6d2, "cyrFlex"},
+ {0xf6d4, "cyrbreve"},
+ {0xf6d5, "cyrflex"},
+ {0x0064, "d"},
+ {0x2020, "dagger"},
+ {0x2021, "daggerdbl"},
+ {0xf6d3, "dblGrave"},
+ {0xf6d6, "dblgrave"},
+ {0x010f, "dcaron"},
+ {0x0111, "dcroat"},
+ {0x00b0, "degree"},
+ {0x03b4, "delta"},
+ {0x2666, "diamond"},
+ {0x00a8, "dieresis"},
+ {0xf6d7, "dieresisacute"},
+ {0xf6d8, "dieresisgrave"},
+ {0x0385, "dieresistonos"},
+ {0x00f7, "divide"},
+ {0x2593, "dkshade"},
+ {0x2584, "dnblock"},
+ {0x0024, "dollar"},
+ {0xf6e3, "dollarinferior"},
+ {0xf724, "dollaroldstyle"},
+ {0xf6e4, "dollarsuperior"},
+ {0x20ab, "dong"},
+ {0x02d9, "dotaccent"},
+ {0x0323, "dotbelowcomb"},
+ {0x0131, "dotlessi"},
+ {0xf6be, "dotlessj"},
+ {0x22c5, "dotmath"},
+ {0xf6eb, "dsuperior"},
+ {0x0065, "e"},
+ {0x00e9, "eacute"},
+ {0x0115, "ebreve"},
+ {0x011b, "ecaron"},
+ {0x00ea, "ecircumflex"},
+ {0x00eb, "edieresis"},
+ {0x0117, "edotaccent"},
+ {0x00e8, "egrave"},
+ {0x0038, "eight"},
+ {0x2088, "eightinferior"},
+ {0xf738, "eightoldstyle"},
+ {0x2078, "eightsuperior"},
+ {0x2208, "element"},
+ {0x2026, "ellipsis"},
+ {0x0113, "emacron"},
+ {0x2014, "emdash"},
+ {0x2205, "emptyset"},
+ {0x2013, "endash"},
+ {0x014b, "eng"},
+ {0x0119, "eogonek"},
+ {0x03b5, "epsilon"},
+ {0x03ad, "epsilontonos"},
+ {0x003d, "equal"},
+ {0x2261, "equivalence"},
+ {0x212e, "estimated"},
+ {0xf6ec, "esuperior"},
+ {0x03b7, "eta"},
+ {0x03ae, "etatonos"},
+ {0x00f0, "eth"},
+ {0x0021, "exclam"},
+ {0x203c, "exclamdbl"},
+ {0x00a1, "exclamdown"},
+ {0xf7a1, "exclamdownsmall"},
+ {0x0021, "exclamleft"},
+ {0xf721, "exclamsmall"},
+ {0x2203, "existential"},
+ {0x0066, "f"},
+ {0x2640, "female"},
+ {0xfb00, "ff"},
+ {0xfb03, "ffi"},
+ {0xfb04, "ffl"},
+ {0xfb01, "fi"},
+ {0x2012, "figuredash"},
+ {0x25a0, "filledbox"},
+ {0x25ac, "filledrect"},
+ {0x0035, "five"},
+ {0x215d, "fiveeighths"},
+ {0x2085, "fiveinferior"},
+ {0xf735, "fiveoldstyle"},
+ {0x2075, "fivesuperior"},
+ {0xfb02, "fl"},
+ {0x0192, "florin"},
+ {0x0034, "four"},
+ {0x2084, "fourinferior"},
+ {0xf734, "fouroldstyle"},
+ {0x2074, "foursuperior"},
+ {0x2044, "fraction"},
+ {0x20a3, "franc"},
+ {0x0067, "g"},
+ {0x03b3, "gamma"},
+ {0x011f, "gbreve"},
+ {0x01e7, "gcaron"},
+ {0x011d, "gcircumflex"},
+ {0x0123, "gcommaaccent"},
+ {0x0121, "gdotaccent"},
+ {0x00df, "germandbls"},
+ {0x2207, "gradient"},
+ {0x0060, "grave"},
+ {0x0300, "gravecomb"},
+ {0x003e, "greater"},
+ {0x2265, "greaterequal"},
+ {0x00ab, "guillemotleft"},
+ {0x00bb, "guillemotright"},
+ {0x2039, "guilsinglleft"},
+ {0x203a, "guilsinglright"},
+ {0x0068, "h"},
+ {0x0127, "hbar"},
+ {0x0125, "hcircumflex"},
+ {0x2665, "heart"},
+ {0x0309, "hookabovecomb"},
+ {0x2302, "house"},
+ {0x02dd, "hungarumlaut"},
+ {0x002d, "hyphen"},
+ {0xf6e5, "hypheninferior"},
+ {0xf6e6, "hyphensuperior"},
+ {0x0069, "i"},
+ {0x00ed, "iacute"},
+ {0x012d, "ibreve"},
+ {0x00ee, "icircumflex"},
+ {0x00ef, "idieresis"},
+ {0x00ec, "igrave"},
+ {0x0133, "ij"},
+ {0x012b, "imacron"},
+ {0x221e, "infinity"},
+ {0x222b, "integral"},
+ {0x2321, "integralbt"},
+ {0xf8f5, "integralex"},
+ {0x2320, "integraltp"},
+ {0x2229, "intersection"},
+ {0x25d8, "invbullet"},
+ {0x25d9, "invcircle"},
+ {0x263b, "invsmileface"},
+ {0x012f, "iogonek"},
+ {0x03b9, "iota"},
+ {0x03ca, "iotadieresis"},
+ {0x0390, "iotadieresistonos"},
+ {0x03af, "iotatonos"},
+ {0xf6ed, "isuperior"},
+ {0x0129, "itilde"},
+ {0x006a, "j"},
+ {0x0135, "jcircumflex"},
+ {0x006b, "k"},
+ {0x03ba, "kappa"},
+ {0x0137, "kcommaaccent"},
+ {0x0138, "kgreenlandic"},
+ {0x006c, "l"},
+ {0x013a, "lacute"},
+ {0x03bb, "lambda"},
+ {0x013e, "lcaron"},
+ {0x013c, "lcommaaccent"},
+ {0x0140, "ldot"},
+ {0x003c, "less"},
+ {0x2264, "lessequal"},
+ {0x258c, "lfblock"},
+ {0x20a4, "lira"},
+ {0xf6c0, "ll"},
+ {0x2227, "logicaland"},
+ {0x00ac, "logicalnot"},
+ {0x2228, "logicalor"},
+ {0x017f, "longs"},
+ {0x25ca, "lozenge"},
+ {0x0142, "lslash"},
+ {0xf6ee, "lsuperior"},
+ {0x2591, "ltshade"},
+ {0x006d, "m"},
+ {0x00af, "macron"},
+ {0x2642, "male"},
+ {0x2212, "minus"},
+ {0x2032, "minute"},
+ {0xf6ef, "msuperior"},
+ {0x00b5, "mu"},
+ {0x00d7, "multiply"},
+ {0x266a, "musicalnote"},
+ {0x266b, "musicalnotedbl"},
+ {0x006e, "n"},
+ {0x0144, "nacute"},
+ {0x0149, "napostrophe"},
+ {0x00a0, "nbspace"},
+ {0x0148, "ncaron"},
+ {0x0146, "ncommaaccent"},
+ {0x0039, "nine"},
+ {0x2089, "nineinferior"},
+ {0xf739, "nineoldstyle"},
+ {0x2079, "ninesuperior"},
+ {0x00a0, "nonbreakingspace"},
+ {0x2209, "notelement"},
+ {0x2260, "notequal"},
+ {0x2284, "notsubset"},
+ {0x207f, "nsuperior"},
+ {0x00f1, "ntilde"},
+ {0x03bd, "nu"},
+ {0x0023, "numbersign"},
+ {0x006f, "o"},
+ {0x00f3, "oacute"},
+ {0x014f, "obreve"},
+ {0x00f4, "ocircumflex"},
+ {0x00f6, "odieresis"},
+ {0x0153, "oe"},
+ {0x02db, "ogonek"},
+ {0x00f2, "ograve"},
+ {0x01a1, "ohorn"},
+ {0x0151, "ohungarumlaut"},
+ {0x014d, "omacron"},
+ {0x03c9, "omega"},
+ {0x03d6, "omega1"},
+ {0x03ce, "omegatonos"},
+ {0x03bf, "omicron"},
+ {0x03cc, "omicrontonos"},
+ {0x0031, "one"},
+ {0x2024, "onedotenleader"},
+ {0x215b, "oneeighth"},
+ {0xf6dc, "onefitted"},
+ {0x00bd, "onehalf"},
+ {0x2081, "oneinferior"},
+ {0xf731, "oneoldstyle"},
+ {0x00bc, "onequarter"},
+ {0x00b9, "onesuperior"},
+ {0x2153, "onethird"},
+ {0x25e6, "openbullet"},
+ {0x00aa, "ordfeminine"},
+ {0x00ba, "ordmasculine"},
+ {0x221f, "orthogonal"},
+ {0x00f8, "oslash"},
+ {0x01ff, "oslashacute"},
+ {0xf6f0, "osuperior"},
+ {0x00f5, "otilde"},
+ {0x0070, "p"},
+ {0x00b6, "paragraph"},
+ {0x0028, "parenleft"},
+ {0xf8ed, "parenleftbt"},
+ {0xf8ec, "parenleftex"},
+ {0x208d, "parenleftinferior"},
+ {0x207d, "parenleftsuperior"},
+ {0xf8eb, "parenlefttp"},
+ {0x0029, "parenright"},
+ {0xf8f8, "parenrightbt"},
+ {0xf8f7, "parenrightex"},
+ {0x208e, "parenrightinferior"},
+ {0x207e, "parenrightsuperior"},
+ {0xf8f6, "parenrighttp"},
+ {0x2202, "partialdiff"},
+ {0x0025, "percent"},
+ {0x002e, "period"},
+ {0x00b7, "periodcentered"},
+ {0xf6e7, "periodinferior"},
+ {0xf6e8, "periodsuperior"},
+ {0x22a5, "perpendicular"},
+ {0x2030, "perthousand"},
+ {0x20a7, "peseta"},
+ {0x03c6, "phi"},
+ {0x03d5, "phi1"},
+ {0x03c0, "pi"},
+ {0x002b, "plus"},
+ {0x00b1, "plusminus"},
+ {0x211e, "prescription"},
+ {0x220f, "product"},
+ {0x2282, "propersubset"},
+ {0x2283, "propersuperset"},
+ {0x221d, "proportional"},
+ {0x03c8, "psi"},
+ {0x0071, "q"},
+ {0x003f, "question"},
+ {0x00bf, "questiondown"},
+ {0xf7bf, "questiondownsmall"},
+ {0xf73f, "questionsmall"},
+ {0x0022, "quotedbl"},
+ {0x201e, "quotedblbase"},
+ {0x201c, "quotedblleft"},
+ {0x201d, "quotedblright"},
+ {0x2018, "quoteleft"},
+ {0x201b, "quotereversed"},
+ {0x2019, "quoteright"},
+ {0x201a, "quotesinglbase"},
+ {0x0027, "quotesingle"},
+ {0x0072, "r"},
+ {0x0155, "racute"},
+ {0x221a, "radical"},
+ {0xf8e5, "radicalex"},
+ {0x0159, "rcaron"},
+ {0x0157, "rcommaaccent"},
+ {0x2286, "reflexsubset"},
+ {0x2287, "reflexsuperset"},
+ {0x00ae, "registered"},
+ {0x00ae, "registersans"},
+ {0x00ae, "registerserif"},
+ {0x2310, "revlogicalnot"},
+ {0x03c1, "rho"},
+ {0x02da, "ring"},
+ {0xf6f1, "rsuperior"},
+ {0x2590, "rtblock"},
+ {0xf6dd, "rupiah"},
+ {0x0073, "s"},
+ {0x015b, "sacute"},
+ {0x0161, "scaron"},
+ {0x015f, "scedilla"},
+ {0x015d, "scircumflex"},
+ {0x0219, "scommaaccent"},
+ {0x2033, "second"},
+ {0x00a7, "section"},
+ {0x003b, "semicolon"},
+ {0x0037, "seven"},
+ {0x215e, "seveneighths"},
+ {0x2087, "seveninferior"},
+ {0xf737, "sevenoldstyle"},
+ {0x2077, "sevensuperior"},
+ {0x2592, "shade"},
+ {0x03c3, "sigma"},
+ {0x03c2, "sigma1"},
+ {0x223c, "similar"},
+ {0x0036, "six"},
+ {0x2086, "sixinferior"},
+ {0xf736, "sixoldstyle"},
+ {0x2076, "sixsuperior"},
+ {0x002f, "slash"},
+ {0x263a, "smileface"},
+ {0x0020, "space"},
+ {0x2660, "spade"},
+ {0xf6f2, "ssuperior"},
+ {0x00a3, "sterling"},
+ {0x220b, "suchthat"},
+ {0x2211, "summation"},
+ {0x263c, "sun"},
+ {0x0074, "t"},
+ {0x03c4, "tau"},
+ {0x0167, "tbar"},
+ {0x0165, "tcaron"},
+ {0x0163, "tcommaaccent"},
+ {0x2234, "therefore"},
+ {0x03b8, "theta"},
+ {0x03d1, "theta1"},
+ {0x00fe, "thorn"},
+ {0x0033, "three"},
+ {0x215c, "threeeighths"},
+ {0x2083, "threeinferior"},
+ {0xf733, "threeoldstyle"},
+ {0x00be, "threequarters"},
+ {0xf6de, "threequartersemdash"},
+ {0x00b3, "threesuperior"},
+ {0x02dc, "tilde"},
+ {0x0303, "tildecomb"},
+ {0x0384, "tonos"},
+ {0x2122, "trademark"},
+ {0x2122, "trademarksans"},
+ {0x2122, "trademarkserif"},
+ {0x25bc, "triagdn"},
+ {0x25c4, "triaglf"},
+ {0x25ba, "triagrt"},
+ {0x25b2, "triagup"},
+ {0xf6f3, "tsuperior"},
+ {0x0032, "two"},
+ {0x2025, "twodotenleader"},
+ {0x2082, "twoinferior"},
+ {0xf732, "twooldstyle"},
+ {0x00b2, "twosuperior"},
+ {0x2154, "twothirds"},
+ {0x0075, "u"},
+ {0x00fa, "uacute"},
+ {0x016d, "ubreve"},
+ {0x00fb, "ucircumflex"},
+ {0x00fc, "udieresis"},
+ {0x00f9, "ugrave"},
+ {0x01b0, "uhorn"},
+ {0x0171, "uhungarumlaut"},
+ {0x016b, "umacron"},
+ {0x005f, "underscore"},
+ {0x2017, "underscoredbl"},
+ {0x222a, "union"},
+ {0x2200, "universal"},
+ {0x0173, "uogonek"},
+ {0x2580, "upblock"},
+ {0x03c5, "upsilon"},
+ {0x03cb, "upsilondieresis"},
+ {0x03b0, "upsilondieresistonos"},
+ {0x03cd, "upsilontonos"},
+ {0x016f, "uring"},
+ {0x0169, "utilde"},
+ {0x0076, "v"},
+ {0x0077, "w"},
+ {0x1e83, "wacute"},
+ {0x0175, "wcircumflex"},
+ {0x1e85, "wdieresis"},
+ {0x2118, "weierstrass"},
+ {0x1e81, "wgrave"},
+ {0x0078, "x"},
+ {0x03be, "xi"},
+ {0x0079, "y"},
+ {0x00fd, "yacute"},
+ {0x0177, "ycircumflex"},
+ {0x00ff, "ydieresis"},
+ {0x00a5, "yen"},
+ {0x1ef3, "ygrave"},
+ {0x007a, "z"},
+ {0x017a, "zacute"},
+ {0x017e, "zcaron"},
+ {0x017c, "zdotaccent"},
+ {0x0030, "zero"},
+ {0x2080, "zeroinferior"},
+ {0xf730, "zerooldstyle"},
+ {0x2070, "zerosuperior"},
+ {0x03b6, "zeta"},
+ {0x007b, "{"},
+ {0x007c, "|"},
+ {0x007d, "}"},
+ {0x007e, "~"},
+ { 0, NULL }
+};
--- /dev/null
+//========================================================================
+//
+// Object.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stddef.h>
+#include "Object.h"
+#include "Array.h"
+#include "Dict.h"
+#include "Error.h"
+#include "Stream.h"
+#include "XRef.h"
+
+//------------------------------------------------------------------------
+// Object
+//------------------------------------------------------------------------
+
+char *objTypeNames[numObjTypes] = {
+ "boolean",
+ "integer",
+ "real",
+ "string",
+ "name",
+ "null",
+ "array",
+ "dictionary",
+ "stream",
+ "ref",
+ "cmd",
+ "error",
+ "eof",
+ "none"
+};
+
+#ifdef DEBUG_MEM
+int Object::numAlloc[numObjTypes] =
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+#endif
+
+Object *Object::initArray(XRef *xref) {
+ initObj(objArray);
+ array = new Array(xref);
+ return this;
+}
+
+Object *Object::initDict(XRef *xref) {
+ initObj(objDict);
+ dict = new Dict(xref);
+ return this;
+}
+
+Object *Object::initDict(Dict *dictA) {
+ initObj(objDict);
+ dict = dictA;
+ dict->incRef();
+ return this;
+}
+
+Object *Object::initStream(Stream *streamA) {
+ initObj(objStream);
+ stream = streamA;
+ return this;
+}
+
+Object *Object::copy(Object *obj) {
+ *obj = *this;
+ switch (type) {
+ case objString:
+ obj->string = string->copy();
+ break;
+ case objName:
+ obj->name = copyString(name);
+ break;
+ case objArray:
+ array->incRef();
+ break;
+ case objDict:
+ dict->incRef();
+ break;
+ case objStream:
+ stream->incRef();
+ break;
+ case objCmd:
+ obj->cmd = copyString(cmd);
+ break;
+ default:
+ break;
+ }
+#ifdef DEBUG_MEM
+ ++numAlloc[type];
+#endif
+ return obj;
+}
+
+Object *Object::fetch(XRef *xref, Object *obj) {
+ return (type == objRef && xref) ?
+ xref->fetch(ref.num, ref.gen, obj) : copy(obj);
+}
+
+void Object::free() {
+ switch (type) {
+ case objString:
+ delete string;
+ break;
+ case objName:
+ gfree(name);
+ break;
+ case objArray:
+ if (!array->decRef()) {
+ delete array;
+ }
+ break;
+ case objDict:
+ if (!dict->decRef()) {
+ delete dict;
+ }
+ break;
+ case objStream:
+ if (!stream->decRef()) {
+ delete stream;
+ }
+ break;
+ case objCmd:
+ gfree(cmd);
+ break;
+ default:
+ break;
+ }
+#ifdef DEBUG_MEM
+ --numAlloc[type];
+#endif
+ type = objNone;
+}
+
+char *Object::getTypeName() {
+ return objTypeNames[type];
+}
+
+void Object::print(FILE *f) {
+ Object obj;
+ int i;
+
+ switch (type) {
+ case objBool:
+ fprintf(f, "%s", booln ? "true" : "false");
+ break;
+ case objInt:
+ fprintf(f, "%d", intg);
+ break;
+ case objReal:
+ fprintf(f, "%g", real);
+ break;
+ case objString:
+ fprintf(f, "(");
+ fwrite(string->getCString(), 1, string->getLength(), f);
+ fprintf(f, ")");
+ break;
+ case objName:
+ fprintf(f, "/%s", name);
+ break;
+ case objNull:
+ fprintf(f, "null");
+ break;
+ case objArray:
+ fprintf(f, "[");
+ for (i = 0; i < arrayGetLength(); ++i) {
+ if (i > 0)
+ fprintf(f, " ");
+ arrayGetNF(i, &obj);
+ obj.print(f);
+ obj.free();
+ }
+ fprintf(f, "]");
+ break;
+ case objDict:
+ fprintf(f, "<<");
+ for (i = 0; i < dictGetLength(); ++i) {
+ fprintf(f, " /%s ", dictGetKey(i));
+ dictGetValNF(i, &obj);
+ obj.print(f);
+ obj.free();
+ }
+ fprintf(f, " >>");
+ break;
+ case objStream:
+ fprintf(f, "<stream>");
+ break;
+ case objRef:
+ fprintf(f, "%d %d R", ref.num, ref.gen);
+ break;
+ case objCmd:
+ fprintf(f, "%s", cmd);
+ break;
+ case objError:
+ fprintf(f, "<error>");
+ break;
+ case objEOF:
+ fprintf(f, "<EOF>");
+ break;
+ case objNone:
+ fprintf(f, "<none>");
+ break;
+ }
+}
+
+void Object::memCheck(FILE *f) {
+#ifdef DEBUG_MEM
+ int i;
+ int t;
+
+ t = 0;
+ for (i = 0; i < numObjTypes; ++i)
+ t += numAlloc[i];
+ if (t > 0) {
+ fprintf(f, "Allocated objects:\n");
+ for (i = 0; i < numObjTypes; ++i) {
+ if (numAlloc[i] > 0)
+ fprintf(f, " %-20s: %6d\n", objTypeNames[i], numAlloc[i]);
+ }
+ }
+#endif
+}
--- /dev/null
+//========================================================================
+//
+// Object.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef OBJECT_H
+#define OBJECT_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include "gtypes.h"
+#include "gmem.h"
+#include "GString.h"
+
+class XRef;
+class Array;
+class Dict;
+class Stream;
+
+//------------------------------------------------------------------------
+// Ref
+//------------------------------------------------------------------------
+
+struct Ref {
+ int num; // object number
+ int gen; // generation number
+};
+
+//------------------------------------------------------------------------
+// object types
+//------------------------------------------------------------------------
+
+enum ObjType {
+ // simple objects
+ objBool, // boolean
+ objInt, // integer
+ objReal, // real
+ objString, // string
+ objName, // name
+ objNull, // null
+
+ // complex objects
+ objArray, // array
+ objDict, // dictionary
+ objStream, // stream
+ objRef, // indirect reference
+
+ // special objects
+ objCmd, // command name
+ objError, // error return from Lexer
+ objEOF, // end of file return from Lexer
+ objNone // uninitialized object
+};
+
+#define numObjTypes 14 // total number of object types
+
+//------------------------------------------------------------------------
+// Object
+//------------------------------------------------------------------------
+
+#ifdef DEBUG_MEM
+#define initObj(t) ++numAlloc[type = t]
+#else
+#define initObj(t) type = t
+#endif
+
+class Object {
+public:
+
+ // Default constructor.
+ Object():
+ type(objNone) {}
+
+ // Initialize an object.
+ Object *initBool(GBool boolnA)
+ { initObj(objBool); booln = boolnA; return this; }
+ Object *initInt(int intgA)
+ { initObj(objInt); intg = intgA; return this; }
+ Object *initReal(double realA)
+ { initObj(objReal); real = realA; return this; }
+ Object *initString(GString *stringA)
+ { initObj(objString); string = stringA; return this; }
+ Object *initName(char *nameA)
+ { initObj(objName); name = copyString(nameA); return this; }
+ Object *initNull()
+ { initObj(objNull); return this; }
+ Object *initArray(XRef *xref);
+ Object *initDict(XRef *xref);
+ Object *initDict(Dict *dictA);
+ Object *initStream(Stream *streamA);
+ Object *initRef(int numA, int genA)
+ { initObj(objRef); ref.num = numA; ref.gen = genA; return this; }
+ Object *initCmd(char *cmdA)
+ { initObj(objCmd); cmd = copyString(cmdA); return this; }
+ Object *initError()
+ { initObj(objError); return this; }
+ Object *initEOF()
+ { initObj(objEOF); return this; }
+
+ // Copy an object.
+ Object *copy(Object *obj);
+
+ // If object is a Ref, fetch and return the referenced object.
+ // Otherwise, return a copy of the object.
+ Object *fetch(XRef *xref, Object *obj);
+
+ // Free object contents.
+ void free();
+
+ // Type checking.
+ ObjType getType() { return type; }
+ GBool isBool() { return type == objBool; }
+ GBool isInt() { return type == objInt; }
+ GBool isReal() { return type == objReal; }
+ GBool isNum() { return type == objInt || type == objReal; }
+ GBool isString() { return type == objString; }
+ GBool isName() { return type == objName; }
+ GBool isNull() { return type == objNull; }
+ GBool isArray() { return type == objArray; }
+ GBool isDict() { return type == objDict; }
+ GBool isStream() { return type == objStream; }
+ GBool isRef() { return type == objRef; }
+ GBool isCmd() { return type == objCmd; }
+ GBool isError() { return type == objError; }
+ GBool isEOF() { return type == objEOF; }
+ GBool isNone() { return type == objNone; }
+
+ // Special type checking.
+ GBool isName(char *nameA)
+ { return type == objName && !strcmp(name, nameA); }
+ GBool isDict(char *dictType);
+ GBool isStream(char *dictType);
+ GBool isCmd(char *cmdA)
+ { return type == objCmd && !strcmp(cmd, cmdA); }
+
+ // Accessors. NB: these assume object is of correct type.
+ GBool getBool() { return booln; }
+ int getInt() { return intg; }
+ double getReal() { return real; }
+ double getNum() { return type == objInt ? (double)intg : real; }
+ GString *getString() { return string; }
+ char *getName() { return name; }
+ Array *getArray() { return array; }
+ Dict *getDict() { return dict; }
+ Stream *getStream() { return stream; }
+ Ref getRef() { return ref; }
+ int getRefNum() { return ref.num; }
+ int getRefGen() { return ref.gen; }
+ char *getCmd() { return cmd; }
+
+ // Array accessors.
+ int arrayGetLength();
+ void arrayAdd(Object *elem);
+ Object *arrayGet(int i, Object *obj);
+ Object *arrayGetNF(int i, Object *obj);
+
+ // Dict accessors.
+ int dictGetLength();
+ void dictAdd(char *key, Object *val);
+ GBool dictIs(char *dictType);
+ Object *dictLookup(char *key, Object *obj);
+ Object *dictLookupNF(char *key, Object *obj);
+ char *dictGetKey(int i);
+ Object *dictGetVal(int i, Object *obj);
+ Object *dictGetValNF(int i, Object *obj);
+
+ // Stream accessors.
+ GBool streamIs(char *dictType);
+ void streamReset();
+ void streamClose();
+ int streamGetChar();
+ int streamLookChar();
+ char *streamGetLine(char *buf, int size);
+ Guint streamGetPos();
+ void streamSetPos(Guint pos, int dir = 0);
+ Dict *streamGetDict();
+
+ // Output.
+ char *getTypeName();
+ void print(FILE *f = stdout);
+
+ // Memory testing.
+ static void memCheck(FILE *f);
+
+private:
+
+ ObjType type; // object type
+ union { // value for each type:
+ GBool booln; // boolean
+ int intg; // integer
+ double real; // real
+ GString *string; // string
+ char *name; // name
+ Array *array; // array
+ Dict *dict; // dictionary
+ Stream *stream; // stream
+ Ref ref; // indirect reference
+ char *cmd; // command
+ };
+
+#ifdef DEBUG_MEM
+ static int // number of each type of object
+ numAlloc[numObjTypes]; // currently allocated
+#endif
+};
+
+//------------------------------------------------------------------------
+// Array accessors.
+//------------------------------------------------------------------------
+
+#include "Array.h"
+
+inline int Object::arrayGetLength()
+ { return array->getLength(); }
+
+inline void Object::arrayAdd(Object *elem)
+ { array->add(elem); }
+
+inline Object *Object::arrayGet(int i, Object *obj)
+ { return array->get(i, obj); }
+
+inline Object *Object::arrayGetNF(int i, Object *obj)
+ { return array->getNF(i, obj); }
+
+//------------------------------------------------------------------------
+// Dict accessors.
+//------------------------------------------------------------------------
+
+#include "Dict.h"
+
+inline int Object::dictGetLength()
+ { return dict->getLength(); }
+
+inline void Object::dictAdd(char *key, Object *val)
+ { dict->add(key, val); }
+
+inline GBool Object::dictIs(char *dictType)
+ { return dict->is(dictType); }
+
+inline GBool Object::isDict(char *dictType)
+ { return type == objDict && dictIs(dictType); }
+
+inline Object *Object::dictLookup(char *key, Object *obj)
+ { return dict->lookup(key, obj); }
+
+inline Object *Object::dictLookupNF(char *key, Object *obj)
+ { return dict->lookupNF(key, obj); }
+
+inline char *Object::dictGetKey(int i)
+ { return dict->getKey(i); }
+
+inline Object *Object::dictGetVal(int i, Object *obj)
+ { return dict->getVal(i, obj); }
+
+inline Object *Object::dictGetValNF(int i, Object *obj)
+ { return dict->getValNF(i, obj); }
+
+//------------------------------------------------------------------------
+// Stream accessors.
+//------------------------------------------------------------------------
+
+#include "Stream.h"
+
+inline GBool Object::streamIs(char *dictType)
+ { return stream->getDict()->is(dictType); }
+
+inline GBool Object::isStream(char *dictType)
+ { return type == objStream && streamIs(dictType); }
+
+inline void Object::streamReset()
+ { stream->reset(); }
+
+inline void Object::streamClose()
+ { stream->close(); }
+
+inline int Object::streamGetChar()
+ { return stream->getChar(); }
+
+inline int Object::streamLookChar()
+ { return stream->lookChar(); }
+
+inline char *Object::streamGetLine(char *buf, int size)
+ { return stream->getLine(buf, size); }
+
+inline Guint Object::streamGetPos()
+ { return stream->getPos(); }
+
+inline void Object::streamSetPos(Guint pos, int dir)
+ { stream->setPos(pos, dir); }
+
+inline Dict *Object::streamGetDict()
+ { return stream->getDict(); }
+
+#endif
--- /dev/null
+//========================================================================
+//
+// Outline.cc
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "gmem.h"
+#include "GString.h"
+#include "GList.h"
+#include "Link.h"
+#include "PDFDocEncoding.h"
+#include "Outline.h"
+
+//------------------------------------------------------------------------
+
+Outline::Outline(Object *outlineObj, XRef *xref) {
+ Object first, last;
+
+ items = NULL;
+ if (!outlineObj->isDict()) {
+ return;
+ }
+ items = OutlineItem::readItemList(outlineObj->dictLookupNF("First", &first),
+ outlineObj->dictLookupNF("Last", &last),
+ xref);
+ first.free();
+ last.free();
+}
+
+Outline::~Outline() {
+ if (items) {
+ deleteGList(items, OutlineItem);
+ }
+}
+
+//------------------------------------------------------------------------
+
+OutlineItem::OutlineItem(Dict *dict, XRef *xrefA) {
+ Object obj1;
+ GString *s;
+ int i;
+
+ xref = xrefA;
+ title = NULL;
+ action = NULL;
+ kids = NULL;
+
+ if (dict->lookup("Title", &obj1)->isString()) {
+ s = obj1.getString();
+ if ((s->getChar(0) & 0xff) == 0xfe &&
+ (s->getChar(1) & 0xff) == 0xff) {
+ titleLen = (s->getLength() - 2) / 2;
+ title = (Unicode *)gmallocn(titleLen, sizeof(Unicode));
+ for (i = 0; i < titleLen; ++i) {
+ title[i] = ((s->getChar(2 + 2*i) & 0xff) << 8) |
+ (s->getChar(3 + 2*i) & 0xff);
+ }
+ } else {
+ titleLen = s->getLength();
+ title = (Unicode *)gmallocn(titleLen, sizeof(Unicode));
+ for (i = 0; i < titleLen; ++i) {
+ title[i] = pdfDocEncoding[s->getChar(i) & 0xff];
+ }
+ }
+ } else {
+ titleLen = 0;
+ }
+ obj1.free();
+
+ if (!dict->lookup("Dest", &obj1)->isNull()) {
+ action = LinkAction::parseDest(&obj1);
+ } else {
+ obj1.free();
+ if (!dict->lookup("A", &obj1)->isNull()) {
+ action = LinkAction::parseAction(&obj1);
+ }
+ }
+ obj1.free();
+
+ dict->lookupNF("First", &firstRef);
+ dict->lookupNF("Last", &lastRef);
+ dict->lookupNF("Next", &nextRef);
+
+ startsOpen = gFalse;
+ if (dict->lookup("Count", &obj1)->isInt()) {
+ if (obj1.getInt() > 0) {
+ startsOpen = gTrue;
+ }
+ }
+ obj1.free();
+}
+
+OutlineItem::~OutlineItem() {
+ close();
+ if (title) {
+ gfree(title);
+ }
+ if (action) {
+ delete action;
+ }
+ firstRef.free();
+ lastRef.free();
+ nextRef.free();
+}
+
+GList *OutlineItem::readItemList(Object *firstItemRef, Object *lastItemRef,
+ XRef *xrefA) {
+ GList *items;
+ OutlineItem *item;
+ Object obj;
+ Object *p;
+
+ items = new GList();
+ p = firstItemRef;
+ while (p->isRef()) {
+ if (!p->fetch(xrefA, &obj)->isDict()) {
+ obj.free();
+ break;
+ }
+ item = new OutlineItem(obj.getDict(), xrefA);
+ obj.free();
+ items->append(item);
+ if (p->getRef().num == lastItemRef->getRef().num &&
+ p->getRef().gen == lastItemRef->getRef().gen) {
+ break;
+ }
+ p = &item->nextRef;
+ }
+ return items;
+}
+
+void OutlineItem::open() {
+ if (!kids) {
+ kids = readItemList(&firstRef, &lastRef, xref);
+ }
+}
+
+void OutlineItem::close() {
+ if (kids) {
+ deleteGList(kids, OutlineItem);
+ kids = NULL;
+ }
+}
--- /dev/null
+//========================================================================
+//
+// Outline.h
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef OUTLINE_H
+#define OUTLINE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "Object.h"
+#include "CharTypes.h"
+
+class GString;
+class GList;
+class XRef;
+class LinkAction;
+
+//------------------------------------------------------------------------
+
+class Outline {
+public:
+
+ Outline(Object *outlineObj, XRef *xref);
+ ~Outline();
+
+ GList *getItems() { return items; }
+
+private:
+
+ GList *items; // NULL if document has no outline
+ // [OutlineItem]
+};
+
+//------------------------------------------------------------------------
+
+class OutlineItem {
+public:
+
+ OutlineItem(Dict *dict, XRef *xrefA);
+ ~OutlineItem();
+
+ static GList *readItemList(Object *firstItemRef, Object *lastItemRef,
+ XRef *xrefA);
+
+ void open();
+ void close();
+
+ Unicode *getTitle() { return title; }
+ int getTitleLength() { return titleLen; }
+ LinkAction *getAction() { return action; }
+ GBool isOpen() { return startsOpen; }
+ GBool hasKids() { return firstRef.isRef(); }
+ GList *getKids() { return kids; }
+
+private:
+
+ XRef *xref;
+ Unicode *title;
+ int titleLen;
+ LinkAction *action;
+ Object firstRef;
+ Object lastRef;
+ Object nextRef;
+ GBool startsOpen;
+ GList *kids; // NULL unless this item is open [OutlineItem]
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// OutputDev.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stddef.h>
+#include "Object.h"
+#include "Stream.h"
+#include "GfxState.h"
+#include "OutputDev.h"
+
+//------------------------------------------------------------------------
+// OutputDev
+//------------------------------------------------------------------------
+
+void OutputDev::setDefaultCTM(double *ctm) {
+ int i;
+ double det;
+
+ for (i = 0; i < 6; ++i) {
+ defCTM[i] = ctm[i];
+ }
+ det = 1 / (defCTM[0] * defCTM[3] - defCTM[1] * defCTM[2]);
+ defICTM[0] = defCTM[3] * det;
+ defICTM[1] = -defCTM[1] * det;
+ defICTM[2] = -defCTM[2] * det;
+ defICTM[3] = defCTM[0] * det;
+ defICTM[4] = (defCTM[2] * defCTM[5] - defCTM[3] * defCTM[4]) * det;
+ defICTM[5] = (defCTM[1] * defCTM[4] - defCTM[0] * defCTM[5]) * det;
+}
+
+void OutputDev::cvtDevToUser(double dx, double dy, double *ux, double *uy) {
+ *ux = defICTM[0] * dx + defICTM[2] * dy + defICTM[4];
+ *uy = defICTM[1] * dx + defICTM[3] * dy + defICTM[5];
+}
+
+void OutputDev::cvtUserToDev(double ux, double uy, int *dx, int *dy) {
+ *dx = (int)(defCTM[0] * ux + defCTM[2] * uy + defCTM[4] + 0.5);
+ *dy = (int)(defCTM[1] * ux + defCTM[3] * uy + defCTM[5] + 0.5);
+}
+
+void OutputDev::updateAll(GfxState *state) {
+ updateLineDash(state);
+ updateFlatness(state);
+ updateLineJoin(state);
+ updateLineCap(state);
+ updateMiterLimit(state);
+ updateLineWidth(state);
+ updateFillColorSpace(state);
+ updateFillColor(state);
+ updateStrokeColorSpace(state);
+ updateStrokeColor(state);
+ updateBlendMode(state);
+ updateFillOpacity(state);
+ updateStrokeOpacity(state);
+ updateFillOverprint(state);
+ updateStrokeOverprint(state);
+ updateFont(state);
+}
+
+GBool OutputDev::beginType3Char(GfxState *state, double x, double y,
+ double dx, double dy,
+ CharCode code, Unicode *u, int uLen) {
+ return gFalse;
+}
+
+void OutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GBool invert,
+ GBool inlineImg) {
+ int i, j;
+
+ if (inlineImg) {
+ str->reset();
+ j = height * ((width + 7) / 8);
+ for (i = 0; i < j; ++i)
+ str->getChar();
+ str->close();
+ }
+}
+
+void OutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GfxImageColorMap *colorMap,
+ int *maskColors, GBool inlineImg) {
+ int i, j;
+
+ if (inlineImg) {
+ str->reset();
+ j = height * ((width * colorMap->getNumPixelComps() *
+ colorMap->getBits() + 7) / 8);
+ for (i = 0; i < j; ++i)
+ str->getChar();
+ str->close();
+ }
+}
+
+void OutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GBool maskInvert) {
+ drawImage(state, ref, str, width, height, colorMap, NULL, gFalse);
+}
+
+void OutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GfxImageColorMap *maskColorMap) {
+ drawImage(state, ref, str, width, height, colorMap, NULL, gFalse);
+}
+
+#if OPI_SUPPORT
+void OutputDev::opiBegin(GfxState *state, Dict *opiDict) {
+}
+
+void OutputDev::opiEnd(GfxState *state, Dict *opiDict) {
+}
+#endif
--- /dev/null
+//========================================================================
+//
+// OutputDev.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef OUTPUTDEV_H
+#define OUTPUTDEV_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "CharTypes.h"
+
+class GString;
+class GfxState;
+class GfxColorSpace;
+class GfxImageColorMap;
+class GfxFunctionShading;
+class GfxAxialShading;
+class GfxRadialShading;
+class Stream;
+class Link;
+class Catalog;
+
+//------------------------------------------------------------------------
+// OutputDev
+//------------------------------------------------------------------------
+
+class OutputDev {
+public:
+
+ // Constructor.
+ OutputDev() {}
+
+ // Destructor.
+ virtual ~OutputDev() {}
+
+ //----- get info about output device
+
+ // Does this device use upside-down coordinates?
+ // (Upside-down means (0,0) is the top left corner of the page.)
+ virtual GBool upsideDown() = 0;
+
+ // Does this device use drawChar() or drawString()?
+ virtual GBool useDrawChar() = 0;
+
+ // Does this device use tilingPatternFill()? If this returns false,
+ // tiling pattern fills will be reduced to a series of other drawing
+ // operations.
+ virtual GBool useTilingPatternFill() { return gFalse; }
+
+ // Does this device use functionShadedFill(), axialShadedFill(), and
+ // radialShadedFill()? If this returns false, these shaded fills
+ // will be reduced to a series of other drawing operations.
+ virtual GBool useShadedFills() { return gFalse; }
+
+ // Does this device use beginType3Char/endType3Char? Otherwise,
+ // text in Type 3 fonts will be drawn with drawChar/drawString.
+ virtual GBool interpretType3Chars() = 0;
+
+ // Does this device need non-text content?
+ virtual GBool needNonText() { return gTrue; }
+
+ //----- initialization and control
+
+ // Set default transform matrix.
+ virtual void setDefaultCTM(double *ctm);
+
+ // Start a page.
+ virtual void startPage(int pageNum, GfxState *state, double x1,double y1,double x2,double y2) {}
+
+ // End a page.
+ virtual void endPage() {}
+
+ // Dump page contents to display.
+ virtual void dump() {}
+
+ //----- coordinate conversion
+
+ // Convert between device and user coordinates.
+ virtual void cvtDevToUser(double dx, double dy, double *ux, double *uy);
+ virtual void cvtUserToDev(double ux, double uy, int *dx, int *dy);
+
+ double *getDefCTM() { return defCTM; }
+ double *getDefICTM() { return defICTM; }
+
+ //----- link borders
+ virtual void drawLink(Link *link, Catalog *catalog) {}
+
+ //----- save/restore graphics state
+ virtual void saveState(GfxState *state) {}
+ virtual void restoreState(GfxState *state) {}
+
+ //----- update graphics state
+ virtual void updateAll(GfxState *state);
+ virtual void updateCTM(GfxState *state, double m11, double m12,
+ double m21, double m22, double m31, double m32) {}
+ virtual void updateLineDash(GfxState *state) {}
+ virtual void updateFlatness(GfxState *state) {}
+ virtual void updateLineJoin(GfxState *state) {}
+ virtual void updateLineCap(GfxState *state) {}
+ virtual void updateMiterLimit(GfxState *state) {}
+ virtual void updateLineWidth(GfxState *state) {}
+ virtual void updateFillColorSpace(GfxState *state) {}
+ virtual void updateStrokeColorSpace(GfxState *state) {}
+ virtual void updateFillColor(GfxState *state) {}
+ virtual void updateStrokeColor(GfxState *state) {}
+ virtual void updateBlendMode(GfxState *state) {}
+ virtual void updateFillOpacity(GfxState *state) {}
+ virtual void updateStrokeOpacity(GfxState *state) {}
+ virtual void updateFillOverprint(GfxState *state) {}
+ virtual void updateStrokeOverprint(GfxState *state) {}
+
+ //----- update text state
+ virtual void updateFont(GfxState *state) {}
+ virtual void updateTextMat(GfxState *state) {}
+ virtual void updateCharSpace(GfxState *state) {}
+ virtual void updateRender(GfxState *state) {}
+ virtual void updateRise(GfxState *state) {}
+ virtual void updateWordSpace(GfxState *state) {}
+ virtual void updateHorizScaling(GfxState *state) {}
+ virtual void updateTextPos(GfxState *state) {}
+ virtual void updateTextShift(GfxState *state, double shift) {}
+
+ //----- path painting
+ virtual void stroke(GfxState *state) {}
+ virtual void fill(GfxState *state) {}
+ virtual void eoFill(GfxState *state) {}
+ virtual void tilingPatternFill(GfxState *state, Object *str,
+ int paintType, Dict *resDict,
+ double *mat, double *bbox,
+ int x0, int y0, int x1, int y1,
+ double xStep, double yStep) {}
+ virtual void functionShadedFill(GfxState *state,
+ GfxFunctionShading *shading) {}
+ virtual void axialShadedFill(GfxState *state, GfxAxialShading *shading) {}
+ virtual void radialShadedFill(GfxState *state, GfxRadialShading *shading) {}
+
+ //----- path clipping
+ virtual void clip(GfxState *state) {}
+ virtual void eoClip(GfxState *state) {}
+
+ //----- text drawing
+ virtual void beginStringOp(GfxState *state) {}
+ virtual void endStringOp(GfxState *state) {}
+ virtual void beginString(GfxState *state, GString *s) {}
+ virtual void endString(GfxState *state) {}
+ virtual void drawChar(GfxState *state, double x, double y,
+ double dx, double dy,
+ double originX, double originY,
+ CharCode code, int nBytes, Unicode *u, int uLen) {}
+ virtual void drawString(GfxState *state, GString *s) {}
+ virtual GBool beginType3Char(GfxState *state, double x, double y,
+ double dx, double dy,
+ CharCode code, Unicode *u, int uLen);
+ virtual void endType3Char(GfxState *state) {}
+ virtual void endTextObject(GfxState *state) {}
+
+ //----- image drawing
+ virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GBool invert,
+ GBool inlineImg);
+ virtual void drawImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height, GfxImageColorMap *colorMap,
+ int *maskColors, GBool inlineImg);
+ virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Stream *maskStr, int maskWidth, int maskHeight,
+ GBool maskInvert);
+ virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
+ int width, int height,
+ GfxImageColorMap *colorMap,
+ Stream *maskStr,
+ int maskWidth, int maskHeight,
+ GfxImageColorMap *maskColorMap);
+
+#if OPI_SUPPORT
+ //----- OPI functions
+ virtual void opiBegin(GfxState *state, Dict *opiDict);
+ virtual void opiEnd(GfxState *state, Dict *opiDict);
+#endif
+
+ //----- Type 3 font operators
+ virtual void type3D0(GfxState *state, double wx, double wy) {}
+ virtual void type3D1(GfxState *state, double wx, double wy,
+ double llx, double lly, double urx, double ury) {}
+
+ //----- PostScript XObjects
+ virtual void psXObject(Stream *psStream, Stream *level1Stream) {}
+
+private:
+
+ double defCTM[6]; // default coordinate transform matrix
+ double defICTM[6]; // inverse of default CTM
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// PDFDoc.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#ifdef WIN32
+# include <windows.h>
+#endif
+#include "GString.h"
+#include "config.h"
+#include "GlobalParams.h"
+#include "Page.h"
+#include "Catalog.h"
+#include "Stream.h"
+#include "XRef.h"
+#include "Link.h"
+#include "OutputDev.h"
+#include "Error.h"
+#include "ErrorCodes.h"
+#include "Lexer.h"
+#include "Parser.h"
+#include "SecurityHandler.h"
+#ifndef DISABLE_OUTLINE
+#include "Outline.h"
+#endif
+#include "PDFDoc.h"
+
+//------------------------------------------------------------------------
+
+#define headerSearchSize 1024 // read this many bytes at beginning of
+ // file to look for '%PDF'
+
+//------------------------------------------------------------------------
+// PDFDoc
+//------------------------------------------------------------------------
+
+PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword,
+ GString *userPassword, void *guiDataA) {
+ Object obj;
+ GString *fileName1, *fileName2;
+
+ ok = gFalse;
+ errCode = errNone;
+
+ guiData = guiDataA;
+
+ file = NULL;
+ str = NULL;
+ xref = NULL;
+ catalog = NULL;
+ links = NULL;
+#ifndef DISABLE_OUTLINE
+ outline = NULL;
+#endif
+
+ fileName = fileNameA;
+ fileName1 = fileName;
+
+
+ // try to open file
+ fileName2 = NULL;
+#ifdef VMS
+ if (!(file = fopen(fileName1->getCString(), "rb", "ctx=stm"))) {
+ error(-1, "Couldn't open file '%s'", fileName1->getCString());
+ errCode = errOpenFile;
+ return;
+ }
+#else
+ if (!(file = fopen(fileName1->getCString(), "rb"))) {
+ fileName2 = fileName->copy();
+ fileName2->lowerCase();
+ if (!(file = fopen(fileName2->getCString(), "rb"))) {
+ fileName2->upperCase();
+ if (!(file = fopen(fileName2->getCString(), "rb"))) {
+ error(-1, "Couldn't open file '%s'", fileName->getCString());
+ delete fileName2;
+ errCode = errOpenFile;
+ return;
+ }
+ }
+ delete fileName2;
+ }
+#endif
+
+ // create stream
+ obj.initNull();
+ str = new FileStream(file, 0, gFalse, 0, &obj);
+
+ ok = setup(ownerPassword, userPassword);
+}
+
+#ifdef WIN32
+PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword,
+ GString *userPassword, void *guiDataA) {
+ OSVERSIONINFO version;
+ wchar_t fileName2[_MAX_PATH + 1];
+ Object obj;
+ int i;
+
+ ok = gFalse;
+ errCode = errNone;
+
+ guiData = guiDataA;
+
+ file = NULL;
+ str = NULL;
+ xref = NULL;
+ catalog = NULL;
+ links = NULL;
+#ifndef DISABLE_OUTLINE
+ outline = NULL;
+#endif
+
+ //~ file name should be stored in Unicode (?)
+ fileName = new GString();
+ for (i = 0; i < fileNameLen; ++i) {
+ fileName->append((char)fileNameA[i]);
+ }
+
+ // zero-terminate the file name string
+ for (i = 0; i < fileNameLen && i < _MAX_PATH; ++i) {
+ fileName2[i] = fileNameA[i];
+ }
+ fileName2[i] = 0;
+
+ // try to open file
+ // NB: _wfopen is only available in NT
+ version.dwOSVersionInfoSize = sizeof(version);
+ GetVersionEx(&version);
+ if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+ file = _wfopen(fileName2, L"rb");
+ } else {
+ file = fopen(fileName->getCString(), "rb");
+ }
+ if (!file) {
+ error(-1, "Couldn't open file '%s'", fileName->getCString());
+ errCode = errOpenFile;
+ return;
+ }
+
+ // create stream
+ obj.initNull();
+ str = new FileStream(file, 0, gFalse, 0, &obj);
+
+ ok = setup(ownerPassword, userPassword);
+}
+#endif
+
+PDFDoc::PDFDoc(BaseStream *strA, GString *ownerPassword,
+ GString *userPassword, void *guiDataA) {
+ ok = gFalse;
+ errCode = errNone;
+ guiData = guiDataA;
+ fileName = NULL;
+ file = NULL;
+ str = strA;
+ xref = NULL;
+ catalog = NULL;
+ links = NULL;
+#ifndef DISABLE_OUTLINE
+ outline = NULL;
+#endif
+ ok = setup(ownerPassword, userPassword);
+}
+
+GBool PDFDoc::setup(GString *ownerPassword, GString *userPassword) {
+ str->reset();
+
+ // check header
+ checkHeader();
+
+ // read xref table
+ xref = new XRef(str);
+ if (!xref->isOk()) {
+ error(-1, "Couldn't read xref table");
+ errCode = xref->getErrorCode();
+ return gFalse;
+ }
+
+ // check for encryption
+ if (!checkEncryption(ownerPassword, userPassword)) {
+ errCode = errEncrypted;
+ return gFalse;
+ }
+
+ // read catalog
+ catalog = new Catalog(xref);
+ if (!catalog->isOk()) {
+ error(-1, "Couldn't read page catalog");
+ errCode = errBadCatalog;
+ return gFalse;
+ }
+
+#ifndef DISABLE_OUTLINE
+ // read outline
+ outline = new Outline(catalog->getOutline(), xref);
+#endif
+
+ // done
+ return gTrue;
+}
+
+PDFDoc::~PDFDoc() {
+#ifndef DISABLE_OUTLINE
+ if (outline) {
+ delete outline;
+ }
+#endif
+ if (catalog) {
+ delete catalog;
+ }
+ if (xref) {
+ delete xref;
+ }
+ if (str) {
+ delete str;
+ }
+ if (file) {
+ fclose(file);
+ }
+ if (fileName) {
+ delete fileName;
+ }
+ if (links) {
+ delete links;
+ }
+}
+
+// Check for a PDF header on this stream. Skip past some garbage
+// if necessary.
+void PDFDoc::checkHeader() {
+ char hdrBuf[headerSearchSize+1];
+ char *p;
+ int i;
+
+ pdfVersion = 0;
+ for (i = 0; i < headerSearchSize; ++i) {
+ hdrBuf[i] = str->getChar();
+ }
+ hdrBuf[headerSearchSize] = '\0';
+ for (i = 0; i < headerSearchSize - 5; ++i) {
+ if (!strncmp(&hdrBuf[i], "%PDF-", 5)) {
+ break;
+ }
+ }
+ if (i >= headerSearchSize - 5) {
+ error(-1, "May not be a PDF file (continuing anyway)");
+ return;
+ }
+ str->moveStart(i);
+ if (!(p = strtok(&hdrBuf[i+5], " \t\n\r"))) {
+ error(-1, "May not be a PDF file (continuing anyway)");
+ return;
+ }
+ pdfVersion = atof(p);
+ if (!(hdrBuf[i+5] >= '0' && hdrBuf[i+5] <= '9') ||
+ pdfVersion > supportedPDFVersionNum + 0.0001) {
+ error(-1, "PDF version %s -- xpdf supports version %s"
+ " (continuing anyway)", p, supportedPDFVersionStr);
+ }
+}
+
+GBool PDFDoc::checkEncryption(GString *ownerPassword, GString *userPassword) {
+ Object encrypt;
+ GBool encrypted;
+ SecurityHandler *secHdlr;
+ GBool ret;
+
+ xref->getTrailerDict()->dictLookup("Encrypt", &encrypt);
+ if ((encrypted = encrypt.isDict())) {
+ if ((secHdlr = SecurityHandler::make(this, &encrypt))) {
+ if (secHdlr->checkEncryption(ownerPassword, userPassword)) {
+ // authorization succeeded
+ xref->setEncryption(secHdlr->getPermissionFlags(),
+ secHdlr->getOwnerPasswordOk(),
+ secHdlr->getFileKey(),
+ secHdlr->getFileKeyLength(),
+ secHdlr->getEncVersion());
+ ret = gTrue;
+ } else {
+ // authorization failed
+ ret = gFalse;
+ }
+ delete secHdlr;
+ } else {
+ // couldn't find the matching security handler
+ ret = gFalse;
+ }
+ } else {
+ // document is not encrypted
+ ret = gTrue;
+ }
+ encrypt.free();
+ return ret;
+}
+
+void PDFDoc::displayPage(OutputDev *out, int page, double hDPI, double vDPI,
+ int rotate, GBool useMediaBox, GBool crop,
+ GBool doLinks,
+ GBool (*abortCheckCbk)(void *data),
+ void *abortCheckCbkData) {
+ Page *p;
+
+ if (globalParams->getPrintCommands()) {
+ printf("***** page %d *****\n", page);
+ }
+ p = catalog->getPage(page);
+ if (doLinks) {
+ if (links) {
+ delete links;
+ }
+ getLinks(p);
+ p->display(out, hDPI, vDPI, rotate, useMediaBox, crop, links, catalog,
+ abortCheckCbk, abortCheckCbkData);
+ } else {
+ p->display(out, hDPI, vDPI, rotate, useMediaBox, crop, NULL, catalog,
+ abortCheckCbk, abortCheckCbkData);
+ }
+}
+
+void PDFDoc::displayPages(OutputDev *out, int firstPage, int lastPage,
+ double hDPI, double vDPI, int rotate,
+ GBool useMediaBox, GBool crop, GBool doLinks,
+ GBool (*abortCheckCbk)(void *data),
+ void *abortCheckCbkData) {
+ int page;
+
+ for (page = firstPage; page <= lastPage; ++page) {
+ displayPage(out, page, hDPI, vDPI, rotate, useMediaBox, crop, doLinks,
+ abortCheckCbk, abortCheckCbkData);
+ }
+}
+
+void PDFDoc::displayPageSlice(OutputDev *out, int page,
+ double hDPI, double vDPI, int rotate,
+ GBool useMediaBox, GBool crop, GBool doLinks,
+ int sliceX, int sliceY, int sliceW, int sliceH,
+ GBool (*abortCheckCbk)(void *data),
+ void *abortCheckCbkData) {
+ Page *p;
+
+ p = catalog->getPage(page);
+ if (doLinks) {
+ if (links) {
+ delete links;
+ }
+ getLinks(p);
+ p->displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop,
+ sliceX, sliceY, sliceW, sliceH,
+ links, catalog, abortCheckCbk, abortCheckCbkData);
+ } else {
+ p->displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop,
+ sliceX, sliceY, sliceW, sliceH,
+ NULL, catalog, abortCheckCbk, abortCheckCbkData);
+ }
+}
+
+Links *PDFDoc::takeLinks() {
+ Links *ret;
+
+ ret = links;
+ links = NULL;
+ return ret;
+}
+
+GBool PDFDoc::isLinearized() {
+ Parser *parser;
+ Object obj1, obj2, obj3, obj4, obj5;
+ GBool lin;
+
+ lin = gFalse;
+ obj1.initNull();
+ parser = new Parser(xref,
+ new Lexer(xref,
+ str->makeSubStream(str->getStart(), gFalse, 0, &obj1)));
+ parser->getObj(&obj1);
+ parser->getObj(&obj2);
+ parser->getObj(&obj3);
+ parser->getObj(&obj4);
+ if (obj1.isInt() && obj2.isInt() && obj3.isCmd("obj") &&
+ obj4.isDict()) {
+ obj4.dictLookup("Linearized", &obj5);
+ if (obj5.isNum() && obj5.getNum() > 0) {
+ lin = gTrue;
+ }
+ obj5.free();
+ }
+ obj4.free();
+ obj3.free();
+ obj2.free();
+ obj1.free();
+ delete parser;
+ return lin;
+}
+
+GBool PDFDoc::saveAs(GString *name) {
+ FILE *f;
+ int c;
+
+ if (!(f = fopen(name->getCString(), "wb"))) {
+ error(-1, "Couldn't open file '%s'", name->getCString());
+ return gFalse;
+ }
+ str->reset();
+ while ((c = str->getChar()) != EOF) {
+ fputc(c, f);
+ }
+ str->close();
+ fclose(f);
+ return gTrue;
+}
+
+void PDFDoc::getLinks(Page *page) {
+ Object obj;
+
+ links = new Links(page->getAnnots(&obj), catalog->getBaseURI());
+ obj.free();
+}
--- /dev/null
+//========================================================================
+//
+// PDFDoc.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef PDFDOC_H
+#define PDFDOC_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <stdio.h>
+#include "XRef.h"
+#include "Catalog.h"
+#include "Page.h"
+
+class GString;
+class BaseStream;
+class OutputDev;
+class Links;
+class LinkAction;
+class LinkDest;
+class Outline;
+
+//------------------------------------------------------------------------
+// PDFDoc
+//------------------------------------------------------------------------
+
+class PDFDoc {
+public:
+
+ PDFDoc(GString *fileNameA, GString *ownerPassword = NULL,
+ GString *userPassword = NULL, void *guiDataA = NULL);
+#ifdef WIN32
+ PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword = NULL,
+ GString *userPassword = NULL, void *guiDataA = NULL);
+#endif
+ PDFDoc(BaseStream *strA, GString *ownerPassword = NULL,
+ GString *userPassword = NULL, void *guiDataA = NULL);
+ ~PDFDoc();
+
+ // Was PDF document successfully opened?
+ GBool isOk() { return ok; }
+
+ // Get the error code (if isOk() returns false).
+ int getErrorCode() { return errCode; }
+
+ // Get file name.
+ GString *getFileName() { return fileName; }
+
+ // Get the xref table.
+ XRef *getXRef() { return xref; }
+
+ // Get catalog.
+ Catalog *getCatalog() { return catalog; }
+
+ // Get base stream.
+ BaseStream *getBaseStream() { return str; }
+
+ // Get page parameters.
+ double getPageMediaWidth(int page)
+ { return catalog->getPage(page)->getMediaWidth(); }
+ double getPageMediaHeight(int page)
+ { return catalog->getPage(page)->getMediaHeight(); }
+ double getPageCropWidth(int page)
+ { return catalog->getPage(page)->getCropWidth(); }
+ double getPageCropHeight(int page)
+ { return catalog->getPage(page)->getCropHeight(); }
+ int getPageRotate(int page)
+ { return catalog->getPage(page)->getRotate(); }
+
+ // Get number of pages.
+ int getNumPages() { return catalog->getNumPages(); }
+
+ // Return the contents of the metadata stream, or NULL if there is
+ // no metadata.
+ GString *readMetadata() { return catalog->readMetadata(); }
+
+ // Return the structure tree root object.
+ Object *getStructTreeRoot() { return catalog->getStructTreeRoot(); }
+
+ // Display a page.
+ void displayPage(OutputDev *out, int page, double hDPI, double vDPI,
+ int rotate, GBool useMediaBox, GBool crop,
+ GBool doLinks,
+ GBool (*abortCheckCbk)(void *data) = NULL,
+ void *abortCheckCbkData = NULL);
+
+ // Display a range of pages.
+ void displayPages(OutputDev *out, int firstPage, int lastPage,
+ double hDPI, double vDPI, int rotate,
+ GBool useMediaBox, GBool crop, GBool doLinks,
+ GBool (*abortCheckCbk)(void *data) = NULL,
+ void *abortCheckCbkData = NULL);
+
+ // Display part of a page.
+ void displayPageSlice(OutputDev *out, int page,
+ double hDPI, double vDPI, int rotate,
+ GBool useMediaBox, GBool crop, GBool doLinks,
+ int sliceX, int sliceY, int sliceW, int sliceH,
+ GBool (*abortCheckCbk)(void *data) = NULL,
+ void *abortCheckCbkData = NULL);
+
+ // Find a page, given its object ID. Returns page number, or 0 if
+ // not found.
+ int findPage(int num, int gen) { return catalog->findPage(num, gen); }
+
+ // Returns the links for the current page, transferring ownership to
+ // the caller.
+ Links *takeLinks();
+
+ // Find a named destination. Returns the link destination, or
+ // NULL if <name> is not a destination.
+ LinkDest *findDest(GString *name)
+ { return catalog->findDest(name); }
+
+#ifndef DISABLE_OUTLINE
+ // Return the outline object.
+ Outline *getOutline() { return outline; }
+#endif
+
+ // Is the file encrypted?
+ GBool isEncrypted() { return xref->isEncrypted(); }
+
+ // Check various permissions.
+ GBool okToPrint(GBool ignoreOwnerPW = gFalse)
+ { return xref->okToPrint(ignoreOwnerPW); }
+ GBool okToChange(GBool ignoreOwnerPW = gFalse)
+ { return xref->okToChange(ignoreOwnerPW); }
+ GBool okToCopy(GBool ignoreOwnerPW = gFalse)
+ { return xref->okToCopy(ignoreOwnerPW); }
+ GBool okToAddNotes(GBool ignoreOwnerPW = gFalse)
+ { return xref->okToAddNotes(ignoreOwnerPW); }
+
+ // Is this document linearized?
+ GBool isLinearized();
+
+ // Return the document's Info dictionary (if any).
+ Object *getDocInfo(Object *obj) { return xref->getDocInfo(obj); }
+ Object *getDocInfoNF(Object *obj) { return xref->getDocInfoNF(obj); }
+
+ // Return the PDF version specified by the file.
+ double getPDFVersion() { return pdfVersion; }
+
+ // Save this file with another name.
+ GBool saveAs(GString *name);
+
+ // Return a pointer to the GUI (XPDFCore or WinPDFCore object).
+ void *getGUIData() { return guiData; }
+
+
+private:
+
+ GBool setup(GString *ownerPassword, GString *userPassword);
+ void checkHeader();
+ GBool checkEncryption(GString *ownerPassword, GString *userPassword);
+ void getLinks(Page *page);
+
+ GString *fileName;
+ FILE *file;
+ BaseStream *str;
+ void *guiData;
+ double pdfVersion;
+ XRef *xref;
+ Catalog *catalog;
+ Links *links;
+#ifndef DISABLE_OUTLINE
+ Outline *outline;
+#endif
+
+
+ GBool ok;
+ int errCode;
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// PDFDocEncoding.h
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include "PDFDocEncoding.h"
+
+Unicode pdfDocEncoding[256] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 00
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 10
+ 0x02d8, 0x02c7, 0x02c6, 0x02d9, 0x02dd, 0x02db, 0x02da, 0x02dc,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, // 20
+ 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, // 30
+ 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, // 40
+ 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, // 50
+ 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, // 60
+ 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, // 70
+ 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x0000,
+ 0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x0192, 0x2044, // 80
+ 0x2039, 0x203a, 0x2212, 0x2030, 0x201e, 0x201c, 0x201d, 0x2018,
+ 0x2019, 0x201a, 0x2122, 0xfb01, 0xfb02, 0x0141, 0x0152, 0x0160, // 90
+ 0x0178, 0x017d, 0x0131, 0x0142, 0x0153, 0x0161, 0x017e, 0x0000,
+ 0x20ac, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, // a0
+ 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x0000, 0x00ae, 0x00af,
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, // b0
+ 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, // c0
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+ 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, // d0
+ 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, // e0
+ 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+ 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, // f0
+ 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff
+};
--- /dev/null
+//========================================================================
+//
+// PDFDocEncoding.h
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef PDFDOCENCODING_H
+#define PDFDOCENCODING_H
+
+#include "CharTypes.h"
+
+extern Unicode pdfDocEncoding[256];
+
+#endif
--- /dev/null
+//========================================================================
+//
+// PSTokenizer.cc
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "PSTokenizer.h"
+
+//------------------------------------------------------------------------
+
+// A '1' in this array means the character is white space. A '1' or
+// '2' means the character ends a name or command.
+static char specialChars[256] = {
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, // 0x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
+ 1, 0, 0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, // 2x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, // 3x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 5x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 7x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ax
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // bx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // cx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // dx
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ex
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // fx
+};
+
+//------------------------------------------------------------------------
+
+PSTokenizer::PSTokenizer(int (*getCharFuncA)(void *), void *dataA) {
+ getCharFunc = getCharFuncA;
+ data = dataA;
+ charBuf = -1;
+}
+
+PSTokenizer::~PSTokenizer() {
+}
+
+GBool PSTokenizer::getToken(char *buf, int size, int *length) {
+ GBool comment, backslash;
+ int c;
+ int i;
+
+ // skip whitespace and comments
+ comment = gFalse;
+ while (1) {
+ if ((c = getChar()) == EOF) {
+ buf[0] = '\0';
+ *length = 0;
+ return gFalse;
+ }
+ if (comment) {
+ if (c == '\x0a' || c == '\x0d') {
+ comment = gFalse;
+ }
+ } else if (c == '%') {
+ comment = gTrue;
+ } else if (specialChars[c] != 1) {
+ break;
+ }
+ }
+
+ // read a token
+ i = 0;
+ buf[i++] = c;
+ if (c == '(') {
+ backslash = gFalse;
+ while ((c = lookChar()) != EOF) {
+ if (i < size - 1) {
+ buf[i++] = c;
+ }
+ getChar();
+ if (c == '\\') {
+ backslash = gTrue;
+ } else if (!backslash && c == ')') {
+ break;
+ } else {
+ backslash = gFalse;
+ }
+ }
+ } else if (c == '<') {
+ while ((c = lookChar()) != EOF) {
+ getChar();
+ if (i < size - 1) {
+ buf[i++] = c;
+ }
+ if (c == '>') {
+ break;
+ }
+ }
+ } else if (c != '[' && c != ']') {
+ while ((c = lookChar()) != EOF && !specialChars[c]) {
+ getChar();
+ if (i < size - 1) {
+ buf[i++] = c;
+ }
+ }
+ }
+ buf[i] = '\0';
+ *length = i;
+
+ return gTrue;
+}
+
+int PSTokenizer::lookChar() {
+ if (charBuf < 0) {
+ charBuf = (*getCharFunc)(data);
+ }
+ return charBuf;
+}
+
+int PSTokenizer::getChar() {
+ int c;
+
+ if (charBuf < 0) {
+ charBuf = (*getCharFunc)(data);
+ }
+ c = charBuf;
+ charBuf = -1;
+ return c;
+}
--- /dev/null
+//========================================================================
+//
+// PSTokenizer.h
+//
+// Copyright 2002-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef PSTOKENIZER_H
+#define PSTOKENIZER_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+
+//------------------------------------------------------------------------
+
+class PSTokenizer {
+public:
+
+ PSTokenizer(int (*getCharFuncA)(void *), void *dataA);
+ ~PSTokenizer();
+
+ // Get the next PostScript token. Returns false at end-of-stream.
+ GBool getToken(char *buf, int size, int *length);
+
+private:
+
+ int lookChar();
+ int getChar();
+
+ int (*getCharFunc)(void *);
+ void *data;
+ int charBuf;
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// Page.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stddef.h>
+#include "GlobalParams.h"
+#include "Object.h"
+#include "Array.h"
+#include "Dict.h"
+#include "XRef.h"
+#include "Link.h"
+#include "OutputDev.h"
+#ifndef PDF_PARSER_ONLY
+#include "Gfx.h"
+#include "GfxState.h"
+#include "Annot.h"
+#endif
+#include "Error.h"
+#include "Page.h"
+
+//------------------------------------------------------------------------
+// PageAttrs
+//------------------------------------------------------------------------
+
+PageAttrs::PageAttrs(PageAttrs *attrs, Dict *dict) {
+ Object obj1;
+
+ // get old/default values
+ if (attrs) {
+ mediaBox = attrs->mediaBox;
+ cropBox = attrs->cropBox;
+ haveCropBox = attrs->haveCropBox;
+ rotate = attrs->rotate;
+ attrs->resources.copy(&resources);
+ } else {
+ // set default MediaBox to 8.5" x 11" -- this shouldn't be necessary
+ // but some (non-compliant) PDF files don't specify a MediaBox
+ mediaBox.x1 = 0;
+ mediaBox.y1 = 0;
+ mediaBox.x2 = 612;
+ mediaBox.y2 = 792;
+ cropBox.x1 = cropBox.y1 = cropBox.x2 = cropBox.y2 = 0;
+ haveCropBox = gFalse;
+ rotate = 0;
+ resources.initNull();
+ }
+
+ // media box
+ readBox(dict, "MediaBox", &mediaBox);
+
+ // crop box
+ if (readBox(dict, "CropBox", &cropBox)) {
+ haveCropBox = gTrue;
+ }
+ if (!haveCropBox) {
+ cropBox = mediaBox;
+ }
+
+ /* if the crop box is larger than the media box, cut it down to
+ media box size */
+ if(haveCropBox &&
+ mediaBox.x1 <= cropBox.x2 &&
+ mediaBox.y1 <= cropBox.y2 &&
+ cropBox.x1 <= mediaBox.x2 &&
+ cropBox.y1 <= mediaBox.y2) {
+ if(mediaBox.x1 >= cropBox.x1) cropBox.x1 = mediaBox.x1;
+ if(mediaBox.y1 >= cropBox.y1) cropBox.y1 = mediaBox.y1;
+ if(mediaBox.x2 <= cropBox.x2) cropBox.x2 = mediaBox.x2;
+ if(mediaBox.y2 <= cropBox.y2) cropBox.y2 = mediaBox.y2;
+ }
+
+ // other boxes
+ bleedBox = cropBox;
+ readBox(dict, "BleedBox", &bleedBox);
+ trimBox = cropBox;
+ readBox(dict, "TrimBox", &trimBox);
+ artBox = cropBox;
+ readBox(dict, "ArtBox", &artBox);
+
+ // rotate
+ dict->lookup("Rotate", &obj1);
+ if (obj1.isInt()) {
+ rotate = obj1.getInt();
+ }
+ obj1.free();
+ while (rotate < 0) {
+ rotate += 360;
+ }
+ while (rotate >= 360) {
+ rotate -= 360;
+ }
+
+ // misc attributes
+ dict->lookup("LastModified", &lastModified);
+ dict->lookup("BoxColorInfo", &boxColorInfo);
+ dict->lookup("Group", &group);
+ dict->lookup("Metadata", &metadata);
+ dict->lookup("PieceInfo", &pieceInfo);
+ dict->lookup("SeparationInfo", &separationInfo);
+
+ // resource dictionary
+ dict->lookup("Resources", &obj1);
+ if (obj1.isDict()) {
+ resources.free();
+ obj1.copy(&resources);
+ }
+ obj1.free();
+}
+
+PageAttrs::~PageAttrs() {
+ lastModified.free();
+ boxColorInfo.free();
+ group.free();
+ metadata.free();
+ pieceInfo.free();
+ separationInfo.free();
+ resources.free();
+}
+
+GBool PageAttrs::readBox(Dict *dict, char *key, PDFRectangle *box) {
+ PDFRectangle tmp;
+ double t;
+ Object obj1, obj2;
+ GBool ok;
+
+ dict->lookup(key, &obj1);
+ if (obj1.isArray() && obj1.arrayGetLength() == 4) {
+ ok = gTrue;
+ obj1.arrayGet(0, &obj2);
+ if (obj2.isNum()) {
+ tmp.x1 = obj2.getNum();
+ } else {
+ ok = gFalse;
+ }
+ obj2.free();
+ obj1.arrayGet(1, &obj2);
+ if (obj2.isNum()) {
+ tmp.y1 = obj2.getNum();
+ } else {
+ ok = gFalse;
+ }
+ obj2.free();
+ obj1.arrayGet(2, &obj2);
+ if (obj2.isNum()) {
+ tmp.x2 = obj2.getNum();
+ } else {
+ ok = gFalse;
+ }
+ obj2.free();
+ obj1.arrayGet(3, &obj2);
+ if (obj2.isNum()) {
+ tmp.y2 = obj2.getNum();
+ } else {
+ ok = gFalse;
+ }
+ obj2.free();
+ if (ok) {
+ if (tmp.x1 > tmp.x2) {
+ t = tmp.x1; tmp.x1 = tmp.x2; tmp.x2 = t;
+ }
+ if (tmp.y1 > tmp.y2) {
+ t = tmp.y1; tmp.y1 = tmp.y2; tmp.y2 = t;
+ }
+ *box = tmp;
+ }
+ } else {
+ ok = gFalse;
+ }
+ obj1.free();
+ return ok;
+}
+
+//------------------------------------------------------------------------
+// Page
+//------------------------------------------------------------------------
+
+Page::Page(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA) {
+ ok = gTrue;
+ xref = xrefA;
+ num = numA;
+
+ // get attributes
+ attrs = attrsA;
+
+ // annotations
+ pageDict->lookupNF("Annots", &annots);
+ if (!(annots.isRef() || annots.isArray() || annots.isNull())) {
+ error(-1, "Page annotations object (page %d) is wrong type (%s)",
+ num, annots.getTypeName());
+ annots.free();
+ goto err2;
+ }
+
+ // contents
+ pageDict->lookupNF("Contents", &contents);
+ if (!(contents.isRef() || contents.isArray() ||
+ contents.isNull())) {
+ error(-1, "Page contents object (page %d) is wrong type (%s)",
+ num, contents.getTypeName());
+ contents.free();
+ goto err1;
+ }
+
+ return;
+
+ err2:
+ annots.initNull();
+ err1:
+ contents.initNull();
+ ok = gFalse;
+}
+
+Page::~Page() {
+ delete attrs;
+ annots.free();
+ contents.free();
+}
+
+void Page::display(OutputDev *out, double hDPI, double vDPI,
+ int rotate, GBool useMediaBox, GBool crop,
+ Links *links, Catalog *catalog,
+ GBool (*abortCheckCbk)(void *data),
+ void *abortCheckCbkData) {
+ displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop,
+ -1, -1, -1, -1, links, catalog,
+ abortCheckCbk, abortCheckCbkData);
+}
+
+void Page::displaySlice(OutputDev *out, double hDPI, double vDPI,
+ int rotate, GBool useMediaBox, GBool crop,
+ int sliceX, int sliceY, int sliceW, int sliceH,
+ Links *links, Catalog *catalog,
+ GBool (*abortCheckCbk)(void *data),
+ void *abortCheckCbkData) {
+#ifndef PDF_PARSER_ONLY
+ PDFRectangle *mediaBox, *cropBox, *baseBox;
+ PDFRectangle box;
+ Gfx *gfx;
+ Object obj;
+ Link *link;
+ Annots *annotList;
+ double kx, ky;
+ int i;
+
+ rotate += getRotate();
+ if (rotate >= 360) {
+ rotate -= 360;
+ } else if (rotate < 0) {
+ rotate += 360;
+ }
+
+ mediaBox = getMediaBox();
+ cropBox = getCropBox();
+ if (sliceW >= 0 && sliceH >= 0) {
+ baseBox = useMediaBox ? mediaBox : cropBox;
+ kx = 72.0 / hDPI;
+ ky = 72.0 / vDPI;
+ if (rotate == 90) {
+ if (out->upsideDown()) {
+ box.x1 = baseBox->x1 + ky * sliceY;
+ box.x2 = baseBox->x1 + ky * (sliceY + sliceH);
+ } else {
+ box.x1 = baseBox->x2 - ky * (sliceY + sliceH);
+ box.x2 = baseBox->x2 - ky * sliceY;
+ }
+ box.y1 = baseBox->y1 + kx * sliceX;
+ box.y2 = baseBox->y1 + kx * (sliceX + sliceW);
+ } else if (rotate == 180) {
+ box.x1 = baseBox->x2 - kx * (sliceX + sliceW);
+ box.x2 = baseBox->x2 - kx * sliceX;
+ if (out->upsideDown()) {
+ box.y1 = baseBox->y1 + ky * sliceY;
+ box.y2 = baseBox->y1 + ky * (sliceY + sliceH);
+ } else {
+ box.y1 = baseBox->y2 - ky * (sliceY + sliceH);
+ box.y2 = baseBox->y2 - ky * sliceY;
+ }
+ } else if (rotate == 270) {
+ if (out->upsideDown()) {
+ box.x1 = baseBox->x2 - ky * (sliceY + sliceH);
+ box.x2 = baseBox->x2 - ky * sliceY;
+ } else {
+ box.x1 = baseBox->x1 + ky * sliceY;
+ box.x2 = baseBox->x1 + ky * (sliceY + sliceH);
+ }
+ box.y1 = baseBox->y2 - kx * (sliceX + sliceW);
+ box.y2 = baseBox->y2 - kx * sliceX;
+ } else {
+ box.x1 = baseBox->x1 + kx * sliceX;
+ box.x2 = baseBox->x1 + kx * (sliceX + sliceW);
+ if (out->upsideDown()) {
+ box.y1 = baseBox->y2 - ky * (sliceY + sliceH);
+ box.y2 = baseBox->y2 - ky * sliceY;
+ } else {
+ box.y1 = baseBox->y1 + ky * sliceY;
+ box.y2 = baseBox->y1 + ky * (sliceY + sliceH);
+ }
+ }
+ } else if (useMediaBox) {
+ box = *mediaBox;
+ } else {
+ box = *cropBox;
+ crop = gFalse;
+ }
+
+ if (globalParams->getPrintCommands()) {
+ printf("***** MediaBox = ll:%g,%g ur:%g,%g\n",
+ mediaBox->x1, mediaBox->y1, mediaBox->x2, mediaBox->y2);
+ printf("***** CropBox = ll:%g,%g ur:%g,%g\n",
+ cropBox->x1, cropBox->y1, cropBox->x2, cropBox->y2);
+ printf("***** Rotate = %d\n", attrs->getRotate());
+ }
+
+ gfx = new Gfx(xref, out, num, attrs->getResourceDict(),
+ hDPI, vDPI, &box, crop ? cropBox : (PDFRectangle *)NULL,
+ rotate, abortCheckCbk, abortCheckCbkData);
+ contents.fetch(xref, &obj);
+ if (!obj.isNull()) {
+ gfx->saveState();
+ gfx->display(&obj);
+ gfx->restoreState();
+ }
+ obj.free();
+
+ // draw links
+ if (links) {
+ gfx->saveState();
+ for (i = 0; i < links->getNumLinks(); ++i) {
+ link = links->getLink(i);
+ out->drawLink(link, catalog);
+ }
+ gfx->restoreState();
+ out->dump();
+ }
+
+ // draw non-link annotations
+ annotList = new Annots(xref, catalog, annots.fetch(xref, &obj));
+ obj.free();
+ if (annotList->getNumAnnots() > 0) {
+ if (globalParams->getPrintCommands()) {
+ printf("***** Annotations\n");
+ }
+ for (i = 0; i < annotList->getNumAnnots(); ++i) {
+ annotList->getAnnot(i)->draw(gfx);
+ }
+ out->dump();
+ }
+ delete annotList;
+
+ delete gfx;
+#endif
+}
+
+void Page::getDefaultCTM(double *ctm, double hDPI, double vDPI,
+ int rotate, GBool upsideDown) {
+ GfxState *state;
+ int i;
+
+ rotate += getRotate();
+ if (rotate >= 360) {
+ rotate -= 360;
+ } else if (rotate < 0) {
+ rotate += 360;
+ }
+ state = new GfxState(hDPI, vDPI, getMediaBox(), rotate, upsideDown);
+ for (i = 0; i < 6; ++i) {
+ ctm[i] = state->getCTM()[i];
+ }
+ delete state;
+}
--- /dev/null
+//========================================================================
+//
+// Page.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef PAGE_H
+#define PAGE_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "Object.h"
+
+class Dict;
+class XRef;
+class OutputDev;
+class Links;
+class Catalog;
+
+//------------------------------------------------------------------------
+
+class PDFRectangle {
+public:
+ double x1, y1, x2, y2;
+
+ PDFRectangle() { x1 = y1 = x2 = y2 = 0; }
+ PDFRectangle(double x1A, double y1A, double x2A, double y2A)
+ { x1 = x1A; y1 = y1A; x2 = x2A; y2 = y2A; }
+ GBool isValid() { return x1 != 0 || y1 != 0 || x2 != 0 || y2 != 0; }
+};
+
+//------------------------------------------------------------------------
+// PageAttrs
+//------------------------------------------------------------------------
+
+class PageAttrs {
+public:
+
+ // Construct a new PageAttrs object by merging a dictionary
+ // (of type Pages or Page) into another PageAttrs object. If
+ // <attrs> is NULL, uses defaults.
+ PageAttrs(PageAttrs *attrs, Dict *dict);
+
+ // Destructor.
+ ~PageAttrs();
+
+ // Accessors.
+ PDFRectangle *getMediaBox() { return &mediaBox; }
+ PDFRectangle *getCropBox() { return &cropBox; }
+ GBool isCropped() { return haveCropBox; }
+ PDFRectangle *getBleedBox() { return &bleedBox; }
+ PDFRectangle *getTrimBox() { return &trimBox; }
+ PDFRectangle *getArtBox() { return &artBox; }
+ int getRotate() { return rotate; }
+ GString *getLastModified()
+ { return lastModified.isString()
+ ? lastModified.getString() : (GString *)NULL; }
+ Dict *getBoxColorInfo()
+ { return boxColorInfo.isDict() ? boxColorInfo.getDict() : (Dict *)NULL; }
+ Dict *getGroup()
+ { return group.isDict() ? group.getDict() : (Dict *)NULL; }
+ Stream *getMetadata()
+ { return metadata.isStream() ? metadata.getStream() : (Stream *)NULL; }
+ Dict *getPieceInfo()
+ { return pieceInfo.isDict() ? pieceInfo.getDict() : (Dict *)NULL; }
+ Dict *getSeparationInfo()
+ { return separationInfo.isDict()
+ ? separationInfo.getDict() : (Dict *)NULL; }
+ Dict *getResourceDict()
+ { return resources.isDict() ? resources.getDict() : (Dict *)NULL; }
+
+private:
+
+ GBool readBox(Dict *dict, char *key, PDFRectangle *box);
+
+ PDFRectangle mediaBox;
+ PDFRectangle cropBox;
+ GBool haveCropBox;
+ PDFRectangle bleedBox;
+ PDFRectangle trimBox;
+ PDFRectangle artBox;
+ int rotate;
+ Object lastModified;
+ Object boxColorInfo;
+ Object group;
+ Object metadata;
+ Object pieceInfo;
+ Object separationInfo;
+ Object resources;
+};
+
+//------------------------------------------------------------------------
+// Page
+//------------------------------------------------------------------------
+
+class Page {
+public:
+
+ // Constructor.
+ Page(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA);
+
+ // Destructor.
+ ~Page();
+
+ // Is page valid?
+ GBool isOk() { return ok; }
+
+ // Get page parameters.
+ PDFRectangle *getMediaBox() { return attrs->getMediaBox(); }
+ PDFRectangle *getCropBox() { return attrs->getCropBox(); }
+ GBool isCropped() { return attrs->isCropped(); }
+ double getMediaWidth()
+ { return attrs->getMediaBox()->x2 - attrs->getMediaBox()->x1; }
+ double getMediaHeight()
+ { return attrs->getMediaBox()->y2 - attrs->getMediaBox()->y1; }
+ double getCropWidth()
+ { return attrs->getCropBox()->x2 - attrs->getCropBox()->x1; }
+ double getCropHeight()
+ { return attrs->getCropBox()->y2 - attrs->getCropBox()->y1; }
+ PDFRectangle *getBleedBox() { return attrs->getBleedBox(); }
+ PDFRectangle *getTrimBox() { return attrs->getTrimBox(); }
+ PDFRectangle *getArtBox() { return attrs->getArtBox(); }
+ int getRotate() { return attrs->getRotate(); }
+ GString *getLastModified() { return attrs->getLastModified(); }
+ Dict *getBoxColorInfo() { return attrs->getBoxColorInfo(); }
+ Dict *getGroup() { return attrs->getGroup(); }
+ Stream *getMetadata() { return attrs->getMetadata(); }
+ Dict *getPieceInfo() { return attrs->getPieceInfo(); }
+ Dict *getSeparationInfo() { return attrs->getSeparationInfo(); }
+
+ // Get resource dictionary.
+ Dict *getResourceDict() { return attrs->getResourceDict(); }
+
+ // Get annotations array.
+ Object *getAnnots(Object *obj) { return annots.fetch(xref, obj); }
+
+ // Get contents.
+ Object *getContents(Object *obj) { return contents.fetch(xref, obj); }
+
+ // Display a page.
+ void display(OutputDev *out, double hDPI, double vDPI,
+ int rotate, GBool useMediaBox, GBool crop,
+ Links *links, Catalog *catalog,
+ GBool (*abortCheckCbk)(void *data) = NULL,
+ void *abortCheckCbkData = NULL);
+
+ // Display part of a page.
+ void displaySlice(OutputDev *out, double hDPI, double vDPI,
+ int rotate, GBool useMediaBox, GBool crop,
+ int sliceX, int sliceY, int sliceW, int sliceH,
+ Links *links, Catalog *catalog,
+ GBool (*abortCheckCbk)(void *data) = NULL,
+ void *abortCheckCbkData = NULL);
+
+ // Get the page's default CTM.
+ void getDefaultCTM(double *ctm, double hDPI, double vDPI,
+ int rotate, GBool upsideDown);
+
+private:
+
+ XRef *xref; // the xref table for this PDF file
+ int num; // page number
+ PageAttrs *attrs; // page attributes
+ Object annots; // annotations array
+ Object contents; // page contents
+ GBool ok; // true if page is valid
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// Parser.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stddef.h>
+#include "Object.h"
+#include "Array.h"
+#include "Dict.h"
+#include "Parser.h"
+#include "XRef.h"
+#include "Error.h"
+#include "Decrypt.h"
+
+Parser::Parser(XRef *xrefA, Lexer *lexerA) {
+ xref = xrefA;
+ lexer = lexerA;
+ inlineImg = 0;
+ lexer->getObj(&buf1);
+ lexer->getObj(&buf2);
+}
+
+Parser::~Parser() {
+ buf1.free();
+ buf2.free();
+ delete lexer;
+}
+
+Object *Parser::getObj(Object *obj,
+ Guchar *fileKey, int keyLength,
+ int objNum, int objGen) {
+ char *key;
+ Stream *str;
+ Object obj2;
+ int num;
+ Decrypt *decrypt;
+ GString *s;
+ char *p;
+ int i;
+
+ // refill buffer after inline image data
+ if (inlineImg == 2) {
+ buf1.free();
+ buf2.free();
+ lexer->getObj(&buf1);
+ lexer->getObj(&buf2);
+ inlineImg = 0;
+ }
+
+ // array
+ if (buf1.isCmd("[")) {
+ shift();
+ obj->initArray(xref);
+ while (!buf1.isCmd("]") && !buf1.isEOF())
+ obj->arrayAdd(getObj(&obj2, fileKey, keyLength, objNum, objGen));
+ if (buf1.isEOF())
+ error(getPos(), "End of file inside array");
+ shift();
+
+ // dictionary or stream
+ } else if (buf1.isCmd("<<")) {
+ shift();
+ obj->initDict(xref);
+ while (!buf1.isCmd(">>") && !buf1.isEOF()) {
+ if (!buf1.isName()) {
+ error(getPos(), "Dictionary key must be a name object");
+ shift();
+ } else {
+ key = copyString(buf1.getName());
+ shift();
+ if (buf1.isEOF() || buf1.isError()) {
+ gfree(key);
+ break;
+ }
+ obj->dictAdd(key, getObj(&obj2, fileKey, keyLength, objNum, objGen));
+ }
+ }
+ if (buf1.isEOF())
+ error(getPos(), "End of file inside dictionary");
+ if (buf2.isCmd("stream")) {
+ if ((str = makeStream(obj))) {
+ obj->initStream(str);
+ if (fileKey) {
+ str->getBaseStream()->doDecryption(fileKey, keyLength,
+ objNum, objGen);
+ }
+ } else {
+ obj->free();
+ obj->initError();
+ }
+ } else {
+ shift();
+ }
+
+ // indirect reference or integer
+ } else if (buf1.isInt()) {
+ num = buf1.getInt();
+ shift();
+ if (buf1.isInt() && buf2.isCmd("R")) {
+ obj->initRef(num, buf1.getInt());
+ shift();
+ shift();
+ } else {
+ obj->initInt(num);
+ }
+
+ // string
+ } else if (buf1.isString() && fileKey) {
+ buf1.copy(obj);
+ s = obj->getString();
+ decrypt = new Decrypt(fileKey, keyLength, objNum, objGen);
+ for (i = 0, p = obj->getString()->getCString();
+ i < s->getLength();
+ ++i, ++p) {
+ *p = decrypt->decryptByte(*p);
+ }
+ delete decrypt;
+ shift();
+
+ // simple object
+ } else {
+ buf1.copy(obj);
+ shift();
+ }
+
+ return obj;
+}
+
+Stream *Parser::makeStream(Object *dict) {
+ Object obj;
+ BaseStream *baseStr;
+ Stream *str;
+ Guint pos, endPos, length;
+
+ // get stream start position
+ lexer->skipToNextLine();
+ pos = lexer->getPos();
+
+ // get length
+ dict->dictLookup("Length", &obj);
+ if (obj.isInt()) {
+ length = (Guint)obj.getInt();
+ obj.free();
+ } else {
+ error(getPos(), "Bad 'Length' attribute in stream");
+ obj.free();
+ return NULL;
+ }
+
+ // check for length in damaged file
+ if (xref && xref->getStreamEnd(pos, &endPos)) {
+ length = endPos - pos;
+ }
+
+ // in badly damaged PDF files, we can run off the end of the input
+ // stream immediately after the "stream" token
+ if (!lexer->getStream()) {
+ return NULL;
+ }
+ baseStr = lexer->getStream()->getBaseStream();
+
+ // skip over stream data
+ lexer->setPos(pos + length);
+
+ // refill token buffers and check for 'endstream'
+ shift(); // kill '>>'
+ shift(); // kill 'stream'
+ if (buf1.isCmd("endstream")) {
+ shift();
+ } else {
+ error(getPos(), "Missing 'endstream'");
+ // kludge for broken PDF files: just add 5k to the length, and
+ // hope its enough
+ length += 5000;
+ }
+
+ // make base stream
+ str = baseStr->makeSubStream(pos, gTrue, length, dict);
+
+ // get filters
+ str = str->addFilters(dict);
+
+ return str;
+}
+
+void Parser::shift() {
+ if (inlineImg > 0) {
+ if (inlineImg < 2) {
+ ++inlineImg;
+ } else {
+ // in a damaged content stream, if 'ID' shows up in the middle
+ // of a dictionary, we need to reset
+ inlineImg = 0;
+ }
+ } else if (buf2.isCmd("ID")) {
+ lexer->skipChar(); // skip char after 'ID' command
+ inlineImg = 1;
+ }
+ buf1.free();
+ buf1 = buf2;
+ if (inlineImg > 0) // don't buffer inline image data
+ buf2.initNull();
+ else
+ lexer->getObj(&buf2);
+}
--- /dev/null
+//========================================================================
+//
+// Parser.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef PARSER_H
+#define PARSER_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "Lexer.h"
+
+//------------------------------------------------------------------------
+// Parser
+//------------------------------------------------------------------------
+
+class Parser {
+public:
+
+ // Constructor.
+ Parser(XRef *xrefA, Lexer *lexerA);
+
+ // Destructor.
+ ~Parser();
+
+ // Get the next object from the input stream.
+ Object *getObj(Object *obj,
+ Guchar *fileKey = NULL, int keyLength = 0,
+ int objNum = 0, int objGen = 0);
+
+ // Get stream.
+ Stream *getStream() { return lexer->getStream(); }
+
+ // Get current position in file.
+ int getPos() { return lexer->getPos(); }
+
+private:
+
+ XRef *xref; // the xref table for this PDF file
+ Lexer *lexer; // input stream
+ Object buf1, buf2; // next two tokens
+ int inlineImg; // set when inline image data is encountered
+
+ Stream *makeStream(Object *dict);
+ void shift();
+};
+
+#endif
+
--- /dev/null
+/* pdfswf.h
+ Header file for pdfswf.cc.
+
+ Part of the swftools package.
+
+ Copyright (c) 2001 Matthias Kramm <kramm@quiss.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef __pdf_h__
+#define __pdf_h__
+
+#include "../lib/gfxdevice.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void pdfswf_setparameter(char*name, char*value);
+
+typedef struct _pdf_doc
+{
+ char*info;
+ char*title;
+ int num_pages;
+ void*internal;
+} pdf_doc_t;
+
+pdf_doc_t* pdf_init(char*filename, char*userPassword);
+void pdf_destroy(pdf_doc_t*doc);
+
+typedef struct _dev_output
+{
+ int num_pages;
+ void*internal;
+} dev_output_t;
+
+dev_output_t* dev_output_init(gfxdevice_t*dev);
+void dev_output_startframe(dev_output_t*, int width, int height);
+void dev_output_endframe(dev_output_t*);
+void dev_output_setparameter(dev_output_t*, char*name, char*value);
+void dev_output_finish(dev_output_t*);
+void dev_output_preparepage(dev_output_t*, int pdfpage, int outputpage);
+void dev_output_destroy(dev_output_t*);
+
+typedef struct _pdf_page
+{
+ pdf_doc_t*parent;
+ int nr;
+ void*internal;
+} pdf_page_t;
+
+pdf_page_t* pdf_getpage(pdf_doc_t*doc, int page);
+void pdf_page_render(pdf_page_t*page, dev_output_t*output);
+void pdf_page_rendersection(pdf_page_t*page, dev_output_t*output, int x, int y, int x1, int y1, int x2, int y2);
+void pdf_page_destroy(pdf_page_t*page);
+
+typedef struct _pdf_page_info
+{
+ int xMin, yMin, xMax, yMax;
+ int number_of_images;
+ int number_of_links;
+ int number_of_fonts;
+} pdf_page_info_t;
+
+pdf_page_info_t* pdf_page_getinfo(pdf_page_t*page);
+void pdf_page_info_destroy(pdf_page_info_t*info);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //__pdf_h__
--- /dev/null
+//========================================================================
+//
+// SecurityHandler.cc
+//
+// Copyright 2004 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "GString.h"
+#include "PDFDoc.h"
+#include "Decrypt.h"
+#include "Error.h"
+#include "GlobalParams.h"
+#if HAVE_XPDFCORE
+# include "XPDFCore.h"
+#elif HAVE_WINPDFCORE
+# include "WinPDFCore.h"
+#endif
+#ifdef ENABLE_PLUGINS
+# include "XpdfPluginAPI.h"
+#endif
+#include "SecurityHandler.h"
+
+//------------------------------------------------------------------------
+// SecurityHandler
+//------------------------------------------------------------------------
+
+SecurityHandler *SecurityHandler::make(PDFDoc *docA, Object *encryptDictA) {
+ Object filterObj;
+ SecurityHandler *secHdlr;
+ XpdfSecurityHandler *xsh;
+
+ encryptDictA->dictLookup("Filter", &filterObj);
+ if (filterObj.isName("Standard")) {
+ secHdlr = new StandardSecurityHandler(docA, encryptDictA);
+ } else if (filterObj.isName()) {
+#ifdef ENABLE_PLUGINS
+ if ((xsh = globalParams->getSecurityHandler(filterObj.getName()))) {
+ secHdlr = new ExternalSecurityHandler(docA, encryptDictA, xsh);
+ } else {
+#endif
+ error(-1, "Couldn't find the '%s' security handler",
+ filterObj.getName());
+ secHdlr = NULL;
+#ifdef ENABLE_PLUGINS
+ }
+#endif
+ } else {
+ error(-1, "Missing or invalid 'Filter' entry in encryption dictionary");
+ secHdlr = NULL;
+ }
+ filterObj.free();
+ return secHdlr;
+}
+
+SecurityHandler::SecurityHandler(PDFDoc *docA) {
+ doc = docA;
+}
+
+SecurityHandler::~SecurityHandler() {
+}
+
+GBool SecurityHandler::checkEncryption(GString *ownerPassword,
+ GString *userPassword) {
+ void *authData;
+ GBool ok;
+ int i;
+
+ if (ownerPassword || userPassword) {
+ authData = makeAuthData(ownerPassword, userPassword);
+ } else {
+ authData = NULL;
+ }
+ ok = authorize(authData);
+ if (authData) {
+ freeAuthData(authData);
+ }
+ for (i = 0; !ok && i < 3; ++i) {
+ if (!(authData = getAuthData())) {
+ break;
+ }
+ ok = authorize(authData);
+ if (authData) {
+ freeAuthData(authData);
+ }
+ }
+ if (!ok) {
+ error(-1, "Incorrect password");
+ }
+ return ok;
+}
+
+//------------------------------------------------------------------------
+// StandardSecurityHandler
+//------------------------------------------------------------------------
+
+class StandardAuthData {
+public:
+
+ StandardAuthData(GString *ownerPasswordA, GString *userPasswordA) {
+ ownerPassword = ownerPasswordA;
+ userPassword = userPasswordA;
+ }
+
+ ~StandardAuthData() {
+ if (ownerPassword) {
+ delete ownerPassword;
+ }
+ if (userPassword) {
+ delete userPassword;
+ }
+ }
+
+ GString *ownerPassword;
+ GString *userPassword;
+};
+
+StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA,
+ Object *encryptDictA):
+ SecurityHandler(docA)
+{
+ Object versionObj, revisionObj, lengthObj;
+ Object ownerKeyObj, userKeyObj, permObj, fileIDObj;
+ Object fileIDObj1;
+ Object cryptFiltersObj, streamFilterObj, stringFilterObj;
+ Object cryptFilterObj, cfmObj, cfLengthObj;
+ Object encryptMetadataObj;
+
+ ok = gFalse;
+ fileID = NULL;
+ ownerKey = NULL;
+ userKey = NULL;
+
+ encryptDictA->dictLookup("V", &versionObj);
+ encryptDictA->dictLookup("R", &revisionObj);
+ encryptDictA->dictLookup("Length", &lengthObj);
+ encryptDictA->dictLookup("O", &ownerKeyObj);
+ encryptDictA->dictLookup("U", &userKeyObj);
+ encryptDictA->dictLookup("P", &permObj);
+ doc->getXRef()->getTrailerDict()->dictLookup("ID", &fileIDObj);
+ if (versionObj.isInt() &&
+ revisionObj.isInt() &&
+ ownerKeyObj.isString() && ownerKeyObj.getString()->getLength() == 32 &&
+ userKeyObj.isString() && userKeyObj.getString()->getLength() == 32 &&
+ permObj.isInt()) {
+ encVersion = versionObj.getInt();
+ encRevision = revisionObj.getInt();
+ // revision 2 forces a 40-bit key - some buggy PDF generators
+ // set the Length value incorrectly
+ if (encRevision == 2 || !lengthObj.isInt()) {
+ fileKeyLength = 5;
+ } else {
+ fileKeyLength = lengthObj.getInt() / 8;
+ }
+ encryptMetadata = gTrue;
+ //~ this currently only handles a subset of crypt filter functionality
+ if (encVersion == 4 && encRevision == 4) {
+ encryptDictA->dictLookup("CF", &cryptFiltersObj);
+ encryptDictA->dictLookup("StmF", &streamFilterObj);
+ encryptDictA->dictLookup("StrF", &stringFilterObj);
+ if (cryptFiltersObj.isDict() &&
+ streamFilterObj.isName() &&
+ stringFilterObj.isName() &&
+ !strcmp(streamFilterObj.getName(), stringFilterObj.getName())) {
+ if (cryptFiltersObj.dictLookup(streamFilterObj.getName(),
+ &cryptFilterObj)->isDict()) {
+ if (cryptFilterObj.dictLookup("CFM", &cfmObj)->isName("V2")) {
+ encVersion = 2;
+ encRevision = 3;
+ if (cryptFilterObj.dictLookup("Length", &cfLengthObj)->isInt()) {
+ //~ according to the spec, this should be cfLengthObj / 8
+ fileKeyLength = cfLengthObj.getInt();
+ }
+ cfLengthObj.free();
+ }
+ cfmObj.free();
+ }
+ cryptFilterObj.free();
+ }
+ stringFilterObj.free();
+ streamFilterObj.free();
+ cryptFiltersObj.free();
+ if (encryptDictA->dictLookup("EncryptMetadata",
+ &encryptMetadataObj)->isBool()) {
+ encryptMetadata = encryptMetadataObj.getBool();
+ }
+ encryptMetadataObj.free();
+ }
+ permFlags = permObj.getInt();
+ ownerKey = ownerKeyObj.getString()->copy();
+ userKey = userKeyObj.getString()->copy();
+ if (encVersion >= 1 && encVersion <= 2 &&
+ encRevision >= 2 && encRevision <= 3) {
+ if (fileIDObj.isArray()) {
+ if (fileIDObj.arrayGet(0, &fileIDObj1)->isString()) {
+ fileID = fileIDObj1.getString()->copy();
+ } else {
+ fileID = new GString();
+ }
+ fileIDObj1.free();
+ } else {
+ fileID = new GString();
+ }
+ ok = gTrue;
+ } else {
+ error(-1, "Unsupported version/revision (%d/%d) of Standard security handler",
+ encVersion, encRevision);
+ }
+ } else {
+ error(-1, "Weird encryption info");
+ }
+ if (fileKeyLength > 16) {
+ fileKeyLength = 16;
+ }
+ fileIDObj.free();
+ permObj.free();
+ userKeyObj.free();
+ ownerKeyObj.free();
+ lengthObj.free();
+ revisionObj.free();
+ versionObj.free();
+}
+
+StandardSecurityHandler::~StandardSecurityHandler() {
+ if (fileID) {
+ delete fileID;
+ }
+ if (ownerKey) {
+ delete ownerKey;
+ }
+ if (userKey) {
+ delete userKey;
+ }
+}
+
+void *StandardSecurityHandler::makeAuthData(GString *ownerPassword,
+ GString *userPassword) {
+ return new StandardAuthData(ownerPassword ? ownerPassword->copy()
+ : (GString *)NULL,
+ userPassword ? userPassword->copy()
+ : (GString *)NULL);
+}
+
+void *StandardSecurityHandler::getAuthData() {
+#if HAVE_XPDFCORE
+ XPDFCore *core;
+ GString *password;
+
+ if (!(core = (XPDFCore *)doc->getGUIData()) ||
+ !(password = core->getPassword())) {
+ return NULL;
+ }
+ return new StandardAuthData(password, password->copy());
+#elif HAVE_WINPDFCORE
+ WinPDFCore *core;
+ GString *password;
+
+ if (!(core = (WinPDFCore *)doc->getGUIData()) ||
+ !(password = core->getPassword())) {
+ return NULL;
+ }
+ return new StandardAuthData(password, password->copy());
+#else
+ return NULL;
+#endif
+}
+
+void StandardSecurityHandler::freeAuthData(void *authData) {
+ delete (StandardAuthData *)authData;
+}
+
+GBool StandardSecurityHandler::authorize(void *authData) {
+ GString *ownerPassword, *userPassword;
+
+ if (!ok) {
+ return gFalse;
+ }
+ if (authData) {
+ ownerPassword = ((StandardAuthData *)authData)->ownerPassword;
+ userPassword = ((StandardAuthData *)authData)->userPassword;
+ } else {
+ ownerPassword = NULL;
+ userPassword = NULL;
+ }
+ if (!Decrypt::makeFileKey(encVersion, encRevision, fileKeyLength,
+ ownerKey, userKey, permFlags, fileID,
+ ownerPassword, userPassword, fileKey,
+ encryptMetadata, &ownerPasswordOk)) {
+ return gFalse;
+ }
+ return gTrue;
+}
+
+#ifdef ENABLE_PLUGINS
+
+//------------------------------------------------------------------------
+// ExternalSecurityHandler
+//------------------------------------------------------------------------
+
+ExternalSecurityHandler::ExternalSecurityHandler(PDFDoc *docA,
+ Object *encryptDictA,
+ XpdfSecurityHandler *xshA):
+ SecurityHandler(docA)
+{
+ encryptDictA->copy(&encryptDict);
+ xsh = xshA;
+ ok = gFalse;
+
+ if (!(*xsh->newDoc)(xsh->handlerData, (XpdfDoc)docA,
+ (XpdfObject)encryptDictA, &docData)) {
+ return;
+ }
+
+ ok = gTrue;
+}
+
+ExternalSecurityHandler::~ExternalSecurityHandler() {
+ (*xsh->freeDoc)(xsh->handlerData, docData);
+ encryptDict.free();
+}
+
+void *ExternalSecurityHandler::makeAuthData(GString *ownerPassword,
+ GString *userPassword) {
+ char *opw, *upw;
+ void *authData;
+
+ opw = ownerPassword ? ownerPassword->getCString() : (char *)NULL;
+ upw = userPassword ? userPassword->getCString() : (char *)NULL;
+ if (!(*xsh->makeAuthData)(xsh->handlerData, docData, opw, upw, &authData)) {
+ return NULL;
+ }
+ return authData;
+}
+
+void *ExternalSecurityHandler::getAuthData() {
+ void *authData;
+
+ if (!(*xsh->getAuthData)(xsh->handlerData, docData, &authData)) {
+ return NULL;
+ }
+ return authData;
+}
+
+void ExternalSecurityHandler::freeAuthData(void *authData) {
+ (*xsh->freeAuthData)(xsh->handlerData, docData, authData);
+}
+
+GBool ExternalSecurityHandler::authorize(void *authData) {
+ char *key;
+ int length;
+
+ if (!ok) {
+ return gFalse;
+ }
+ permFlags = (*xsh->authorize)(xsh->handlerData, docData, authData);
+ if (!(permFlags & xpdfPermissionOpen)) {
+ return gFalse;
+ }
+ if (!(*xsh->getKey)(xsh->handlerData, docData, &key, &length, &encVersion)) {
+ return gFalse;
+ }
+ if ((fileKeyLength = length) > 16) {
+ fileKeyLength = 16;
+ }
+ memcpy(fileKey, key, fileKeyLength);
+ (*xsh->freeKey)(xsh->handlerData, docData, key, length);
+ return gTrue;
+}
+
+#endif // ENABLE_PLUGINS
--- /dev/null
+//========================================================================
+//
+// SecurityHandler.h
+//
+// Copyright 2004 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SECURITYHANDLER_H
+#define SECURITYHANDLER_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "Object.h"
+
+class GString;
+class PDFDoc;
+struct XpdfSecurityHandler;
+
+//------------------------------------------------------------------------
+// SecurityHandler
+//------------------------------------------------------------------------
+
+class SecurityHandler {
+public:
+
+ static SecurityHandler *make(PDFDoc *docA, Object *encryptDictA);
+
+ SecurityHandler(PDFDoc *docA);
+ virtual ~SecurityHandler();
+
+ // Check the document's encryption. If the document is encrypted,
+ // this will first try <ownerPassword> and <userPassword> (in
+ // "batch" mode), and if those fail, it will attempt to request a
+ // password from the user. This is the high-level function that
+ // calls the lower level functions for the specific security handler
+ // (requesting a password three times, etc.). Returns true if the
+ // document can be opened (if it's unencrypted, or if a correct
+ // password is obtained); false otherwise (encrypted and no correct
+ // password).
+ GBool checkEncryption(GString *ownerPassword,
+ GString *userPassword);
+
+ // Create authorization data for the specified owner and user
+ // passwords. If the security handler doesn't support "batch" mode,
+ // this function should return NULL.
+ virtual void *makeAuthData(GString *ownerPassword,
+ GString *userPassword) = 0;
+
+ // Construct authorization data, typically by prompting the user for
+ // a password. Returns an authorization data object, or NULL to
+ // cancel.
+ virtual void *getAuthData() = 0;
+
+ // Free the authorization data returned by makeAuthData or
+ // getAuthData.
+ virtual void freeAuthData(void *authData) = 0;
+
+ // Attempt to authorize the document, using the supplied
+ // authorization data (which may be NULL). Returns true if
+ // successful (i.e., if at least the right to open the document was
+ // granted).
+ virtual GBool authorize(void *authData) = 0;
+
+ // Return the various authorization parameters. These are only
+ // valid after authorize has returned true.
+ virtual int getPermissionFlags() = 0;
+ virtual GBool getOwnerPasswordOk() = 0;
+ virtual Guchar *getFileKey() = 0;
+ virtual int getFileKeyLength() = 0;
+ virtual int getEncVersion() = 0;
+
+protected:
+
+ PDFDoc *doc;
+};
+
+//------------------------------------------------------------------------
+// StandardSecurityHandler
+//------------------------------------------------------------------------
+
+class StandardSecurityHandler: public SecurityHandler {
+public:
+
+ StandardSecurityHandler(PDFDoc *docA, Object *encryptDictA);
+ virtual ~StandardSecurityHandler();
+
+ virtual void *makeAuthData(GString *ownerPassword,
+ GString *userPassword);
+ virtual void *getAuthData();
+ virtual void freeAuthData(void *authData);
+ virtual GBool authorize(void *authData);
+ virtual int getPermissionFlags() { return permFlags; }
+ virtual GBool getOwnerPasswordOk() { return ownerPasswordOk; }
+ virtual Guchar *getFileKey() { return fileKey; }
+ virtual int getFileKeyLength() { return fileKeyLength; }
+ virtual int getEncVersion() { return encVersion; }
+
+private:
+
+ int permFlags;
+ GBool ownerPasswordOk;
+ Guchar fileKey[16];
+ int fileKeyLength;
+ int encVersion;
+ int encRevision;
+ GBool encryptMetadata;
+
+ GString *ownerKey, *userKey;
+ GString *fileID;
+ GBool ok;
+};
+
+#ifdef ENABLE_PLUGINS
+//------------------------------------------------------------------------
+// ExternalSecurityHandler
+//------------------------------------------------------------------------
+
+class ExternalSecurityHandler: public SecurityHandler {
+public:
+
+ ExternalSecurityHandler(PDFDoc *docA, Object *encryptDictA,
+ XpdfSecurityHandler *xshA);
+ virtual ~ExternalSecurityHandler();
+
+ virtual void *makeAuthData(GString *ownerPassword,
+ GString *userPassword);
+ virtual void *getAuthData();
+ virtual void freeAuthData(void *authData);
+ virtual GBool authorize(void *authData);
+ virtual int getPermissionFlags() { return permFlags; }
+ virtual GBool getOwnerPasswordOk() { return gFalse; }
+ virtual Guchar *getFileKey() { return fileKey; }
+ virtual int getFileKeyLength() { return fileKeyLength; }
+ virtual int getEncVersion() { return encVersion; }
+
+private:
+
+ Object encryptDict;
+ XpdfSecurityHandler *xsh;
+ void *docData;
+ int permFlags;
+ Guchar fileKey[16];
+ int fileKeyLength;
+ int encVersion;
+ GBool ok;
+};
+#endif // ENABLE_PLUGINS
+
+#endif
--- /dev/null
+//========================================================================
+//
+// Stream-CCITT.h
+//
+// Tables for CCITT Fax decoding.
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+struct CCITTCode {
+ short bits;
+ short n;
+};
+
+#define ccittEOL -2
+
+//------------------------------------------------------------------------
+// 2D codes
+//------------------------------------------------------------------------
+
+#define twoDimPass 0
+#define twoDimHoriz 1
+#define twoDimVert0 2
+#define twoDimVertR1 3
+#define twoDimVertL1 4
+#define twoDimVertR2 5
+#define twoDimVertL2 6
+#define twoDimVertR3 7
+#define twoDimVertL3 8
+
+// 1-7 bit codes
+static CCITTCode twoDimTab1[128] = {
+ {-1, -1}, {-1, -1}, // 000000x
+ {7, twoDimVertL3}, // 0000010
+ {7, twoDimVertR3}, // 0000011
+ {6, twoDimVertL2}, {6, twoDimVertL2}, // 000010x
+ {6, twoDimVertR2}, {6, twoDimVertR2}, // 000011x
+ {4, twoDimPass}, {4, twoDimPass}, // 0001xxx
+ {4, twoDimPass}, {4, twoDimPass},
+ {4, twoDimPass}, {4, twoDimPass},
+ {4, twoDimPass}, {4, twoDimPass},
+ {3, twoDimHoriz}, {3, twoDimHoriz}, // 001xxxx
+ {3, twoDimHoriz}, {3, twoDimHoriz},
+ {3, twoDimHoriz}, {3, twoDimHoriz},
+ {3, twoDimHoriz}, {3, twoDimHoriz},
+ {3, twoDimHoriz}, {3, twoDimHoriz},
+ {3, twoDimHoriz}, {3, twoDimHoriz},
+ {3, twoDimHoriz}, {3, twoDimHoriz},
+ {3, twoDimHoriz}, {3, twoDimHoriz},
+ {3, twoDimVertL1}, {3, twoDimVertL1}, // 010xxxx
+ {3, twoDimVertL1}, {3, twoDimVertL1},
+ {3, twoDimVertL1}, {3, twoDimVertL1},
+ {3, twoDimVertL1}, {3, twoDimVertL1},
+ {3, twoDimVertL1}, {3, twoDimVertL1},
+ {3, twoDimVertL1}, {3, twoDimVertL1},
+ {3, twoDimVertL1}, {3, twoDimVertL1},
+ {3, twoDimVertL1}, {3, twoDimVertL1},
+ {3, twoDimVertR1}, {3, twoDimVertR1}, // 011xxxx
+ {3, twoDimVertR1}, {3, twoDimVertR1},
+ {3, twoDimVertR1}, {3, twoDimVertR1},
+ {3, twoDimVertR1}, {3, twoDimVertR1},
+ {3, twoDimVertR1}, {3, twoDimVertR1},
+ {3, twoDimVertR1}, {3, twoDimVertR1},
+ {3, twoDimVertR1}, {3, twoDimVertR1},
+ {3, twoDimVertR1}, {3, twoDimVertR1},
+ {1, twoDimVert0}, {1, twoDimVert0}, // 1xxxxxx
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0},
+ {1, twoDimVert0}, {1, twoDimVert0}
+};
+
+//------------------------------------------------------------------------
+// white run lengths
+//------------------------------------------------------------------------
+
+// 11-12 bit codes (upper 7 bits are 0)
+static CCITTCode whiteTab1[32] = {
+ {-1, -1}, // 00000
+ {12, ccittEOL}, // 00001
+ {-1, -1}, {-1, -1}, // 0001x
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 001xx
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 010xx
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 011xx
+ {11, 1792}, {11, 1792}, // 1000x
+ {12, 1984}, // 10010
+ {12, 2048}, // 10011
+ {12, 2112}, // 10100
+ {12, 2176}, // 10101
+ {12, 2240}, // 10110
+ {12, 2304}, // 10111
+ {11, 1856}, {11, 1856}, // 1100x
+ {11, 1920}, {11, 1920}, // 1101x
+ {12, 2368}, // 11100
+ {12, 2432}, // 11101
+ {12, 2496}, // 11110
+ {12, 2560} // 11111
+};
+
+// 1-9 bit codes
+static CCITTCode whiteTab2[512] = {
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 0000000xx
+ {8, 29}, {8, 29}, // 00000010x
+ {8, 30}, {8, 30}, // 00000011x
+ {8, 45}, {8, 45}, // 00000100x
+ {8, 46}, {8, 46}, // 00000101x
+ {7, 22}, {7, 22}, {7, 22}, {7, 22}, // 0000011xx
+ {7, 23}, {7, 23}, {7, 23}, {7, 23}, // 0000100xx
+ {8, 47}, {8, 47}, // 00001010x
+ {8, 48}, {8, 48}, // 00001011x
+ {6, 13}, {6, 13}, {6, 13}, {6, 13}, // 000011xxx
+ {6, 13}, {6, 13}, {6, 13}, {6, 13},
+ {7, 20}, {7, 20}, {7, 20}, {7, 20}, // 0001000xx
+ {8, 33}, {8, 33}, // 00010010x
+ {8, 34}, {8, 34}, // 00010011x
+ {8, 35}, {8, 35}, // 00010100x
+ {8, 36}, {8, 36}, // 00010101x
+ {8, 37}, {8, 37}, // 00010110x
+ {8, 38}, {8, 38}, // 00010111x
+ {7, 19}, {7, 19}, {7, 19}, {7, 19}, // 0001100xx
+ {8, 31}, {8, 31}, // 00011010x
+ {8, 32}, {8, 32}, // 00011011x
+ {6, 1}, {6, 1}, {6, 1}, {6, 1}, // 000111xxx
+ {6, 1}, {6, 1}, {6, 1}, {6, 1},
+ {6, 12}, {6, 12}, {6, 12}, {6, 12}, // 001000xxx
+ {6, 12}, {6, 12}, {6, 12}, {6, 12},
+ {8, 53}, {8, 53}, // 00100100x
+ {8, 54}, {8, 54}, // 00100101x
+ {7, 26}, {7, 26}, {7, 26}, {7, 26}, // 0010011xx
+ {8, 39}, {8, 39}, // 00101000x
+ {8, 40}, {8, 40}, // 00101001x
+ {8, 41}, {8, 41}, // 00101010x
+ {8, 42}, {8, 42}, // 00101011x
+ {8, 43}, {8, 43}, // 00101100x
+ {8, 44}, {8, 44}, // 00101101x
+ {7, 21}, {7, 21}, {7, 21}, {7, 21}, // 0010111xx
+ {7, 28}, {7, 28}, {7, 28}, {7, 28}, // 0011000xx
+ {8, 61}, {8, 61}, // 00110010x
+ {8, 62}, {8, 62}, // 00110011x
+ {8, 63}, {8, 63}, // 00110100x
+ {8, 0}, {8, 0}, // 00110101x
+ {8, 320}, {8, 320}, // 00110110x
+ {8, 384}, {8, 384}, // 00110111x
+ {5, 10}, {5, 10}, {5, 10}, {5, 10}, // 00111xxxx
+ {5, 10}, {5, 10}, {5, 10}, {5, 10},
+ {5, 10}, {5, 10}, {5, 10}, {5, 10},
+ {5, 10}, {5, 10}, {5, 10}, {5, 10},
+ {5, 11}, {5, 11}, {5, 11}, {5, 11}, // 01000xxxx
+ {5, 11}, {5, 11}, {5, 11}, {5, 11},
+ {5, 11}, {5, 11}, {5, 11}, {5, 11},
+ {5, 11}, {5, 11}, {5, 11}, {5, 11},
+ {7, 27}, {7, 27}, {7, 27}, {7, 27}, // 0100100xx
+ {8, 59}, {8, 59}, // 01001010x
+ {8, 60}, {8, 60}, // 01001011x
+ {9, 1472}, // 010011000
+ {9, 1536}, // 010011001
+ {9, 1600}, // 010011010
+ {9, 1728}, // 010011011
+ {7, 18}, {7, 18}, {7, 18}, {7, 18}, // 0100111xx
+ {7, 24}, {7, 24}, {7, 24}, {7, 24}, // 0101000xx
+ {8, 49}, {8, 49}, // 01010010x
+ {8, 50}, {8, 50}, // 01010011x
+ {8, 51}, {8, 51}, // 01010100x
+ {8, 52}, {8, 52}, // 01010101x
+ {7, 25}, {7, 25}, {7, 25}, {7, 25}, // 0101011xx
+ {8, 55}, {8, 55}, // 01011000x
+ {8, 56}, {8, 56}, // 01011001x
+ {8, 57}, {8, 57}, // 01011010x
+ {8, 58}, {8, 58}, // 01011011x
+ {6, 192}, {6, 192}, {6, 192}, {6, 192}, // 010111xxx
+ {6, 192}, {6, 192}, {6, 192}, {6, 192},
+ {6, 1664}, {6, 1664}, {6, 1664}, {6, 1664}, // 011000xxx
+ {6, 1664}, {6, 1664}, {6, 1664}, {6, 1664},
+ {8, 448}, {8, 448}, // 01100100x
+ {8, 512}, {8, 512}, // 01100101x
+ {9, 704}, // 011001100
+ {9, 768}, // 011001101
+ {8, 640}, {8, 640}, // 01100111x
+ {8, 576}, {8, 576}, // 01101000x
+ {9, 832}, // 011010010
+ {9, 896}, // 011010011
+ {9, 960}, // 011010100
+ {9, 1024}, // 011010101
+ {9, 1088}, // 011010110
+ {9, 1152}, // 011010111
+ {9, 1216}, // 011011000
+ {9, 1280}, // 011011001
+ {9, 1344}, // 011011010
+ {9, 1408}, // 011011011
+ {7, 256}, {7, 256}, {7, 256}, {7, 256}, // 0110111xx
+ {4, 2}, {4, 2}, {4, 2}, {4, 2}, // 0111xxxxx
+ {4, 2}, {4, 2}, {4, 2}, {4, 2},
+ {4, 2}, {4, 2}, {4, 2}, {4, 2},
+ {4, 2}, {4, 2}, {4, 2}, {4, 2},
+ {4, 2}, {4, 2}, {4, 2}, {4, 2},
+ {4, 2}, {4, 2}, {4, 2}, {4, 2},
+ {4, 2}, {4, 2}, {4, 2}, {4, 2},
+ {4, 2}, {4, 2}, {4, 2}, {4, 2},
+ {4, 3}, {4, 3}, {4, 3}, {4, 3}, // 1000xxxxx
+ {4, 3}, {4, 3}, {4, 3}, {4, 3},
+ {4, 3}, {4, 3}, {4, 3}, {4, 3},
+ {4, 3}, {4, 3}, {4, 3}, {4, 3},
+ {4, 3}, {4, 3}, {4, 3}, {4, 3},
+ {4, 3}, {4, 3}, {4, 3}, {4, 3},
+ {4, 3}, {4, 3}, {4, 3}, {4, 3},
+ {4, 3}, {4, 3}, {4, 3}, {4, 3},
+ {5, 128}, {5, 128}, {5, 128}, {5, 128}, // 10010xxxx
+ {5, 128}, {5, 128}, {5, 128}, {5, 128},
+ {5, 128}, {5, 128}, {5, 128}, {5, 128},
+ {5, 128}, {5, 128}, {5, 128}, {5, 128},
+ {5, 8}, {5, 8}, {5, 8}, {5, 8}, // 10011xxxx
+ {5, 8}, {5, 8}, {5, 8}, {5, 8},
+ {5, 8}, {5, 8}, {5, 8}, {5, 8},
+ {5, 8}, {5, 8}, {5, 8}, {5, 8},
+ {5, 9}, {5, 9}, {5, 9}, {5, 9}, // 10100xxxx
+ {5, 9}, {5, 9}, {5, 9}, {5, 9},
+ {5, 9}, {5, 9}, {5, 9}, {5, 9},
+ {5, 9}, {5, 9}, {5, 9}, {5, 9},
+ {6, 16}, {6, 16}, {6, 16}, {6, 16}, // 101010xxx
+ {6, 16}, {6, 16}, {6, 16}, {6, 16},
+ {6, 17}, {6, 17}, {6, 17}, {6, 17}, // 101011xxx
+ {6, 17}, {6, 17}, {6, 17}, {6, 17},
+ {4, 4}, {4, 4}, {4, 4}, {4, 4}, // 1011xxxxx
+ {4, 4}, {4, 4}, {4, 4}, {4, 4},
+ {4, 4}, {4, 4}, {4, 4}, {4, 4},
+ {4, 4}, {4, 4}, {4, 4}, {4, 4},
+ {4, 4}, {4, 4}, {4, 4}, {4, 4},
+ {4, 4}, {4, 4}, {4, 4}, {4, 4},
+ {4, 4}, {4, 4}, {4, 4}, {4, 4},
+ {4, 4}, {4, 4}, {4, 4}, {4, 4},
+ {4, 5}, {4, 5}, {4, 5}, {4, 5}, // 1100xxxxx
+ {4, 5}, {4, 5}, {4, 5}, {4, 5},
+ {4, 5}, {4, 5}, {4, 5}, {4, 5},
+ {4, 5}, {4, 5}, {4, 5}, {4, 5},
+ {4, 5}, {4, 5}, {4, 5}, {4, 5},
+ {4, 5}, {4, 5}, {4, 5}, {4, 5},
+ {4, 5}, {4, 5}, {4, 5}, {4, 5},
+ {4, 5}, {4, 5}, {4, 5}, {4, 5},
+ {6, 14}, {6, 14}, {6, 14}, {6, 14}, // 110100xxx
+ {6, 14}, {6, 14}, {6, 14}, {6, 14},
+ {6, 15}, {6, 15}, {6, 15}, {6, 15}, // 110101xxx
+ {6, 15}, {6, 15}, {6, 15}, {6, 15},
+ {5, 64}, {5, 64}, {5, 64}, {5, 64}, // 11011xxxx
+ {5, 64}, {5, 64}, {5, 64}, {5, 64},
+ {5, 64}, {5, 64}, {5, 64}, {5, 64},
+ {5, 64}, {5, 64}, {5, 64}, {5, 64},
+ {4, 6}, {4, 6}, {4, 6}, {4, 6}, // 1110xxxxx
+ {4, 6}, {4, 6}, {4, 6}, {4, 6},
+ {4, 6}, {4, 6}, {4, 6}, {4, 6},
+ {4, 6}, {4, 6}, {4, 6}, {4, 6},
+ {4, 6}, {4, 6}, {4, 6}, {4, 6},
+ {4, 6}, {4, 6}, {4, 6}, {4, 6},
+ {4, 6}, {4, 6}, {4, 6}, {4, 6},
+ {4, 6}, {4, 6}, {4, 6}, {4, 6},
+ {4, 7}, {4, 7}, {4, 7}, {4, 7}, // 1111xxxxx
+ {4, 7}, {4, 7}, {4, 7}, {4, 7},
+ {4, 7}, {4, 7}, {4, 7}, {4, 7},
+ {4, 7}, {4, 7}, {4, 7}, {4, 7},
+ {4, 7}, {4, 7}, {4, 7}, {4, 7},
+ {4, 7}, {4, 7}, {4, 7}, {4, 7},
+ {4, 7}, {4, 7}, {4, 7}, {4, 7},
+ {4, 7}, {4, 7}, {4, 7}, {4, 7}
+};
+
+//------------------------------------------------------------------------
+// black run lengths
+//------------------------------------------------------------------------
+
+// 10-13 bit codes (upper 6 bits are 0)
+static CCITTCode blackTab1[128] = {
+ {-1, -1}, {-1, -1}, // 000000000000x
+ {12, ccittEOL}, {12, ccittEOL}, // 000000000001x
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 00000000001xx
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 00000000010xx
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 00000000011xx
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 00000000100xx
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 00000000101xx
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 00000000110xx
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 00000000111xx
+ {11, 1792}, {11, 1792}, {11, 1792}, {11, 1792}, // 00000001000xx
+ {12, 1984}, {12, 1984}, // 000000010010x
+ {12, 2048}, {12, 2048}, // 000000010011x
+ {12, 2112}, {12, 2112}, // 000000010100x
+ {12, 2176}, {12, 2176}, // 000000010101x
+ {12, 2240}, {12, 2240}, // 000000010110x
+ {12, 2304}, {12, 2304}, // 000000010111x
+ {11, 1856}, {11, 1856}, {11, 1856}, {11, 1856}, // 00000001100xx
+ {11, 1920}, {11, 1920}, {11, 1920}, {11, 1920}, // 00000001101xx
+ {12, 2368}, {12, 2368}, // 000000011100x
+ {12, 2432}, {12, 2432}, // 000000011101x
+ {12, 2496}, {12, 2496}, // 000000011110x
+ {12, 2560}, {12, 2560}, // 000000011111x
+ {10, 18}, {10, 18}, {10, 18}, {10, 18}, // 0000001000xxx
+ {10, 18}, {10, 18}, {10, 18}, {10, 18},
+ {12, 52}, {12, 52}, // 000000100100x
+ {13, 640}, // 0000001001010
+ {13, 704}, // 0000001001011
+ {13, 768}, // 0000001001100
+ {13, 832}, // 0000001001101
+ {12, 55}, {12, 55}, // 000000100111x
+ {12, 56}, {12, 56}, // 000000101000x
+ {13, 1280}, // 0000001010010
+ {13, 1344}, // 0000001010011
+ {13, 1408}, // 0000001010100
+ {13, 1472}, // 0000001010101
+ {12, 59}, {12, 59}, // 000000101011x
+ {12, 60}, {12, 60}, // 000000101100x
+ {13, 1536}, // 0000001011010
+ {13, 1600}, // 0000001011011
+ {11, 24}, {11, 24}, {11, 24}, {11, 24}, // 00000010111xx
+ {11, 25}, {11, 25}, {11, 25}, {11, 25}, // 00000011000xx
+ {13, 1664}, // 0000001100100
+ {13, 1728}, // 0000001100101
+ {12, 320}, {12, 320}, // 000000110011x
+ {12, 384}, {12, 384}, // 000000110100x
+ {12, 448}, {12, 448}, // 000000110101x
+ {13, 512}, // 0000001101100
+ {13, 576}, // 0000001101101
+ {12, 53}, {12, 53}, // 000000110111x
+ {12, 54}, {12, 54}, // 000000111000x
+ {13, 896}, // 0000001110010
+ {13, 960}, // 0000001110011
+ {13, 1024}, // 0000001110100
+ {13, 1088}, // 0000001110101
+ {13, 1152}, // 0000001110110
+ {13, 1216}, // 0000001110111
+ {10, 64}, {10, 64}, {10, 64}, {10, 64}, // 0000001111xxx
+ {10, 64}, {10, 64}, {10, 64}, {10, 64}
+};
+
+// 7-12 bit codes (upper 4 bits are 0)
+static CCITTCode blackTab2[192] = {
+ {8, 13}, {8, 13}, {8, 13}, {8, 13}, // 00000100xxxx
+ {8, 13}, {8, 13}, {8, 13}, {8, 13},
+ {8, 13}, {8, 13}, {8, 13}, {8, 13},
+ {8, 13}, {8, 13}, {8, 13}, {8, 13},
+ {11, 23}, {11, 23}, // 00000101000x
+ {12, 50}, // 000001010010
+ {12, 51}, // 000001010011
+ {12, 44}, // 000001010100
+ {12, 45}, // 000001010101
+ {12, 46}, // 000001010110
+ {12, 47}, // 000001010111
+ {12, 57}, // 000001011000
+ {12, 58}, // 000001011001
+ {12, 61}, // 000001011010
+ {12, 256}, // 000001011011
+ {10, 16}, {10, 16}, {10, 16}, {10, 16}, // 0000010111xx
+ {10, 17}, {10, 17}, {10, 17}, {10, 17}, // 0000011000xx
+ {12, 48}, // 000001100100
+ {12, 49}, // 000001100101
+ {12, 62}, // 000001100110
+ {12, 63}, // 000001100111
+ {12, 30}, // 000001101000
+ {12, 31}, // 000001101001
+ {12, 32}, // 000001101010
+ {12, 33}, // 000001101011
+ {12, 40}, // 000001101100
+ {12, 41}, // 000001101101
+ {11, 22}, {11, 22}, // 00000110111x
+ {8, 14}, {8, 14}, {8, 14}, {8, 14}, // 00000111xxxx
+ {8, 14}, {8, 14}, {8, 14}, {8, 14},
+ {8, 14}, {8, 14}, {8, 14}, {8, 14},
+ {8, 14}, {8, 14}, {8, 14}, {8, 14},
+ {7, 10}, {7, 10}, {7, 10}, {7, 10}, // 0000100xxxxx
+ {7, 10}, {7, 10}, {7, 10}, {7, 10},
+ {7, 10}, {7, 10}, {7, 10}, {7, 10},
+ {7, 10}, {7, 10}, {7, 10}, {7, 10},
+ {7, 10}, {7, 10}, {7, 10}, {7, 10},
+ {7, 10}, {7, 10}, {7, 10}, {7, 10},
+ {7, 10}, {7, 10}, {7, 10}, {7, 10},
+ {7, 10}, {7, 10}, {7, 10}, {7, 10},
+ {7, 11}, {7, 11}, {7, 11}, {7, 11}, // 0000101xxxxx
+ {7, 11}, {7, 11}, {7, 11}, {7, 11},
+ {7, 11}, {7, 11}, {7, 11}, {7, 11},
+ {7, 11}, {7, 11}, {7, 11}, {7, 11},
+ {7, 11}, {7, 11}, {7, 11}, {7, 11},
+ {7, 11}, {7, 11}, {7, 11}, {7, 11},
+ {7, 11}, {7, 11}, {7, 11}, {7, 11},
+ {7, 11}, {7, 11}, {7, 11}, {7, 11},
+ {9, 15}, {9, 15}, {9, 15}, {9, 15}, // 000011000xxx
+ {9, 15}, {9, 15}, {9, 15}, {9, 15},
+ {12, 128}, // 000011001000
+ {12, 192}, // 000011001001
+ {12, 26}, // 000011001010
+ {12, 27}, // 000011001011
+ {12, 28}, // 000011001100
+ {12, 29}, // 000011001101
+ {11, 19}, {11, 19}, // 00001100111x
+ {11, 20}, {11, 20}, // 00001101000x
+ {12, 34}, // 000011010010
+ {12, 35}, // 000011010011
+ {12, 36}, // 000011010100
+ {12, 37}, // 000011010101
+ {12, 38}, // 000011010110
+ {12, 39}, // 000011010111
+ {11, 21}, {11, 21}, // 00001101100x
+ {12, 42}, // 000011011010
+ {12, 43}, // 000011011011
+ {10, 0}, {10, 0}, {10, 0}, {10, 0}, // 0000110111xx
+ {7, 12}, {7, 12}, {7, 12}, {7, 12}, // 0000111xxxxx
+ {7, 12}, {7, 12}, {7, 12}, {7, 12},
+ {7, 12}, {7, 12}, {7, 12}, {7, 12},
+ {7, 12}, {7, 12}, {7, 12}, {7, 12},
+ {7, 12}, {7, 12}, {7, 12}, {7, 12},
+ {7, 12}, {7, 12}, {7, 12}, {7, 12},
+ {7, 12}, {7, 12}, {7, 12}, {7, 12},
+ {7, 12}, {7, 12}, {7, 12}, {7, 12}
+};
+
+// 2-6 bit codes
+static CCITTCode blackTab3[64] = {
+ {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, // 0000xx
+ {6, 9}, // 000100
+ {6, 8}, // 000101
+ {5, 7}, {5, 7}, // 00011x
+ {4, 6}, {4, 6}, {4, 6}, {4, 6}, // 0010xx
+ {4, 5}, {4, 5}, {4, 5}, {4, 5}, // 0011xx
+ {3, 1}, {3, 1}, {3, 1}, {3, 1}, // 010xxx
+ {3, 1}, {3, 1}, {3, 1}, {3, 1},
+ {3, 4}, {3, 4}, {3, 4}, {3, 4}, // 011xxx
+ {3, 4}, {3, 4}, {3, 4}, {3, 4},
+ {2, 3}, {2, 3}, {2, 3}, {2, 3}, // 10xxxx
+ {2, 3}, {2, 3}, {2, 3}, {2, 3},
+ {2, 3}, {2, 3}, {2, 3}, {2, 3},
+ {2, 3}, {2, 3}, {2, 3}, {2, 3},
+ {2, 2}, {2, 2}, {2, 2}, {2, 2}, // 11xxxx
+ {2, 2}, {2, 2}, {2, 2}, {2, 2},
+ {2, 2}, {2, 2}, {2, 2}, {2, 2},
+ {2, 2}, {2, 2}, {2, 2}, {2, 2}
+};
--- /dev/null
+//========================================================================
+//
+// Stream.h
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef STREAM_H
+#define STREAM_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include <stdio.h>
+#include "gtypes.h"
+#include "Object.h"
+
+class Decrypt;
+class BaseStream;
+
+//------------------------------------------------------------------------
+
+enum StreamKind {
+ strFile,
+ strASCIIHex,
+ strASCII85,
+ strLZW,
+ strRunLength,
+ strCCITTFax,
+ strDCT,
+ strFlate,
+ strJBIG2,
+ strJPX,
+ strWeird // internal-use stream types
+};
+
+enum StreamColorSpaceMode {
+ streamCSNone,
+ streamCSDeviceGray,
+ streamCSDeviceRGB,
+ streamCSDeviceCMYK
+};
+
+//------------------------------------------------------------------------
+// Stream (base class)
+//------------------------------------------------------------------------
+
+class Stream {
+public:
+
+ // Constructor.
+ Stream();
+
+ // Destructor.
+ virtual ~Stream();
+
+ // Reference counting.
+ int incRef() { return ++ref; }
+ int decRef() { return --ref; }
+
+ // Get kind of stream.
+ virtual StreamKind getKind() = 0;
+
+ // Reset stream to beginning.
+ virtual void reset() = 0;
+
+ // Close down the stream.
+ virtual void close();
+
+ // Get next char from stream.
+ virtual int getChar() = 0;
+
+ // Peek at next char in stream.
+ virtual int lookChar() = 0;
+
+ // Get next char from stream without using the predictor.
+ // This is only used by StreamPredictor.
+ virtual int getRawChar();
+
+ // Get next line from stream.
+ virtual char *getLine(char *buf, int size);
+
+ // Get current position in file.
+ virtual int getPos() = 0;
+
+ // Go to a position in the stream. If <dir> is negative, the
+ // position is from the end of the file; otherwise the position is
+ // from the start of the file.
+ virtual void setPos(Guint pos, int dir = 0) = 0;
+
+ // Get PostScript command for the filter(s).
+ virtual GString *getPSFilter(int psLevel, char *indent);
+
+ // Does this stream type potentially contain non-printable chars?
+ virtual GBool isBinary(GBool last = gTrue) = 0;
+
+ // Get the BaseStream of this stream.
+ virtual BaseStream *getBaseStream() = 0;
+
+ // Get the dictionary associated with this stream.
+ virtual Dict *getDict() = 0;
+
+ // Is this an encoding filter?
+ virtual GBool isEncoder() { return gFalse; }
+
+ // Get image parameters which are defined by the stream contents.
+ virtual void getImageParams(int *bitsPerComponent,
+ StreamColorSpaceMode *csMode) {}
+
+ // Add filters to this stream according to the parameters in <dict>.
+ // Returns the new stream.
+ Stream *addFilters(Object *dict);
+
+private:
+
+ Stream *makeFilter(char *name, Stream *str, Object *params);
+
+ int ref; // reference count
+};
+
+//------------------------------------------------------------------------
+// BaseStream
+//
+// This is the base class for all streams that read directly from a file.
+//------------------------------------------------------------------------
+
+class BaseStream: public Stream {
+public:
+
+ BaseStream(Object *dictA);
+ virtual ~BaseStream();
+ virtual Stream *makeSubStream(Guint start, GBool limited,
+ Guint length, Object *dict) = 0;
+ virtual void setPos(Guint pos, int dir = 0) = 0;
+ virtual GBool isBinary(GBool last = gTrue) { return last; }
+ virtual BaseStream *getBaseStream() { return this; }
+ virtual Dict *getDict() { return dict.getDict(); }
+
+ // Get/set position of first byte of stream within the file.
+ virtual Guint getStart() = 0;
+ virtual void moveStart(int delta) = 0;
+
+ // Set decryption for this stream.
+ virtual void doDecryption(Guchar *fileKey, int keyLength,
+ int objNum, int objGen);
+
+protected:
+
+ Decrypt *decrypt;
+
+private:
+
+ Object dict;
+};
+
+//------------------------------------------------------------------------
+// FilterStream
+//
+// This is the base class for all streams that filter another stream.
+//------------------------------------------------------------------------
+
+class FilterStream: public Stream {
+public:
+
+ FilterStream(Stream *strA);
+ virtual ~FilterStream();
+ virtual void close();
+ virtual int getPos() { return str->getPos(); }
+ virtual void setPos(Guint pos, int dir = 0);
+ virtual BaseStream *getBaseStream() { return str->getBaseStream(); }
+ virtual Dict *getDict() { return str->getDict(); }
+
+protected:
+
+ Stream *str;
+};
+
+//------------------------------------------------------------------------
+// ImageStream
+//------------------------------------------------------------------------
+
+class ImageStream {
+public:
+
+ // Create an image stream object for an image with the specified
+ // parameters. Note that these are the actual image parameters,
+ // which may be different from the predictor parameters.
+ ImageStream(Stream *strA, int widthA, int nCompsA, int nBitsA);
+
+ ~ImageStream();
+
+ // Reset the stream.
+ void reset();
+
+ // Gets the next pixel from the stream. <pix> should be able to hold
+ // at least nComps elements. Returns false at end of file.
+ GBool getPixel(Guchar *pix);
+
+ // Returns a pointer to the next line of pixels. Returns NULL at
+ // end of file.
+ Guchar *getLine();
+
+ // Skip an entire line from the image.
+ void skipLine();
+
+private:
+
+ Stream *str; // base stream
+ int width; // pixels per line
+ int nComps; // components per pixel
+ int nBits; // bits per component
+ int nVals; // components per line
+ Guchar *imgLine; // line buffer
+ int imgIdx; // current index in imgLine
+};
+
+//------------------------------------------------------------------------
+// StreamPredictor
+//------------------------------------------------------------------------
+
+class StreamPredictor {
+public:
+
+ // Create a predictor object. Note that the parameters are for the
+ // predictor, and may not match the actual image parameters.
+ StreamPredictor(Stream *strA, int predictorA,
+ int widthA, int nCompsA, int nBitsA);
+
+ ~StreamPredictor();
+
+ GBool isOk() { return ok; }
+
+ int lookChar();
+ int getChar();
+
+private:
+
+ GBool getNextLine();
+
+ Stream *str; // base stream
+ int predictor; // predictor
+ int width; // pixels per line
+ int nComps; // components per pixel
+ int nBits; // bits per component
+ int nVals; // components per line
+ int pixBytes; // bytes per pixel
+ int rowBytes; // bytes per line
+ Guchar *predLine; // line buffer
+ int predIdx; // current index in predLine
+ GBool ok;
+};
+
+//------------------------------------------------------------------------
+// FileStream
+//------------------------------------------------------------------------
+
+#define fileStreamBufSize 256
+
+class FileStream: public BaseStream {
+public:
+
+ FileStream(FILE *fA, Guint startA, GBool limitedA,
+ Guint lengthA, Object *dictA);
+ virtual ~FileStream();
+ virtual Stream *makeSubStream(Guint startA, GBool limitedA,
+ Guint lengthA, Object *dictA);
+ virtual StreamKind getKind() { return strFile; }
+ virtual void reset();
+ virtual void close();
+ virtual int getChar()
+ { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
+ virtual int lookChar()
+ { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
+ virtual int getPos() { return bufPos + (bufPtr - buf); }
+ virtual void setPos(Guint pos, int dir = 0);
+ virtual Guint getStart() { return start; }
+ virtual void moveStart(int delta);
+
+private:
+
+ GBool fillBuf();
+
+ FILE *f;
+ Guint start;
+ GBool limited;
+ Guint length;
+ char buf[fileStreamBufSize];
+ char *bufPtr;
+ char *bufEnd;
+ Guint bufPos;
+ int savePos;
+ GBool saved;
+};
+
+//------------------------------------------------------------------------
+// MemStream
+//------------------------------------------------------------------------
+
+class MemStream: public BaseStream {
+public:
+
+ MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA);
+ virtual ~MemStream();
+ virtual Stream *makeSubStream(Guint start, GBool limited,
+ Guint lengthA, Object *dictA);
+ virtual StreamKind getKind() { return strWeird; }
+ virtual void reset();
+ virtual void close();
+ virtual int getChar()
+ { return (bufPtr < bufEnd) ? (*bufPtr++ & 0xff) : EOF; }
+ virtual int lookChar()
+ { return (bufPtr < bufEnd) ? (*bufPtr & 0xff) : EOF; }
+ virtual int getPos() { return (int)(bufPtr - buf); }
+ virtual void setPos(Guint pos, int dir = 0);
+ virtual Guint getStart() { return start; }
+ virtual void moveStart(int delta);
+ virtual void doDecryption(Guchar *fileKey, int keyLength,
+ int objNum, int objGen);
+
+private:
+
+ char *buf;
+ Guint start;
+ Guint length;
+ char *bufEnd;
+ char *bufPtr;
+ GBool needFree;
+};
+
+//------------------------------------------------------------------------
+// EmbedStream
+//
+// This is a special stream type used for embedded streams (inline
+// images). It reads directly from the base stream -- after the
+// EmbedStream is deleted, reads from the base stream will proceed where
+// the BaseStream left off. Note that this is very different behavior
+// that creating a new FileStream (using makeSubStream).
+//------------------------------------------------------------------------
+
+class EmbedStream: public BaseStream {
+public:
+
+ EmbedStream(Stream *strA, Object *dictA, GBool limitedA, Guint lengthA);
+ virtual ~EmbedStream();
+ virtual Stream *makeSubStream(Guint start, GBool limitedA,
+ Guint lengthA, Object *dictA);
+ virtual StreamKind getKind() { return str->getKind(); }
+ virtual void reset() {}
+ virtual int getChar();
+ virtual int lookChar();
+ virtual int getPos() { return str->getPos(); }
+ virtual void setPos(Guint pos, int dir = 0);
+ virtual Guint getStart();
+ virtual void moveStart(int delta);
+
+private:
+
+ Stream *str;
+ GBool limited;
+ Guint length;
+};
+
+//------------------------------------------------------------------------
+// ASCIIHexStream
+//------------------------------------------------------------------------
+
+class ASCIIHexStream: public FilterStream {
+public:
+
+ ASCIIHexStream(Stream *strA);
+ virtual ~ASCIIHexStream();
+ virtual StreamKind getKind() { return strASCIIHex; }
+ virtual void reset();
+ virtual int getChar()
+ { int c = lookChar(); buf = EOF; return c; }
+ virtual int lookChar();
+ virtual GString *getPSFilter(int psLevel, char *indent);
+ virtual GBool isBinary(GBool last = gTrue);
+
+private:
+
+ int buf;
+ GBool eof;
+};
+
+//------------------------------------------------------------------------
+// ASCII85Stream
+//------------------------------------------------------------------------
+
+class ASCII85Stream: public FilterStream {
+public:
+
+ ASCII85Stream(Stream *strA);
+ virtual ~ASCII85Stream();
+ virtual StreamKind getKind() { return strASCII85; }
+ virtual void reset();
+ virtual int getChar()
+ { int ch = lookChar(); ++index; return ch; }
+ virtual int lookChar();
+ virtual GString *getPSFilter(int psLevel, char *indent);
+ virtual GBool isBinary(GBool last = gTrue);
+
+private:
+
+ int c[5];
+ int b[4];
+ int index, n;
+ GBool eof;
+};
+
+//------------------------------------------------------------------------
+// LZWStream
+//------------------------------------------------------------------------
+
+class LZWStream: public FilterStream {
+public:
+
+ LZWStream(Stream *strA, int predictor, int columns, int colors,
+ int bits, int earlyA);
+ virtual ~LZWStream();
+ virtual StreamKind getKind() { return strLZW; }
+ virtual void reset();
+ virtual int getChar();
+ virtual int lookChar();
+ virtual int getRawChar();
+ virtual GString *getPSFilter(int psLevel, char *indent);
+ virtual GBool isBinary(GBool last = gTrue);
+
+private:
+
+ StreamPredictor *pred; // predictor
+ int early; // early parameter
+ GBool eof; // true if at eof
+ int inputBuf; // input buffer
+ int inputBits; // number of bits in input buffer
+ struct { // decoding table
+ int length;
+ int head;
+ Guchar tail;
+ } table[4097];
+ int nextCode; // next code to be used
+ int nextBits; // number of bits in next code word
+ int prevCode; // previous code used in stream
+ int newChar; // next char to be added to table
+ Guchar seqBuf[4097]; // buffer for current sequence
+ int seqLength; // length of current sequence
+ int seqIndex; // index into current sequence
+ GBool first; // first code after a table clear
+
+ GBool processNextCode();
+ void clearTable();
+ int getCode();
+};
+
+//------------------------------------------------------------------------
+// RunLengthStream
+//------------------------------------------------------------------------
+
+class RunLengthStream: public FilterStream {
+public:
+
+ RunLengthStream(Stream *strA);
+ virtual ~RunLengthStream();
+ virtual StreamKind getKind() { return strRunLength; }
+ virtual void reset();
+ virtual int getChar()
+ { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
+ virtual int lookChar()
+ { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
+ virtual GString *getPSFilter(int psLevel, char *indent);
+ virtual GBool isBinary(GBool last = gTrue);
+
+private:
+
+ char buf[128]; // buffer
+ char *bufPtr; // next char to read
+ char *bufEnd; // end of buffer
+ GBool eof;
+
+ GBool fillBuf();
+};
+
+//------------------------------------------------------------------------
+// CCITTFaxStream
+//------------------------------------------------------------------------
+
+struct CCITTCodeTable;
+
+class CCITTFaxStream: public FilterStream {
+public:
+
+ CCITTFaxStream(Stream *strA, int encodingA, GBool endOfLineA,
+ GBool byteAlignA, int columnsA, int rowsA,
+ GBool endOfBlockA, GBool blackA);
+ virtual ~CCITTFaxStream();
+ virtual StreamKind getKind() { return strCCITTFax; }
+ virtual void reset();
+ virtual int getChar()
+ { int c = lookChar(); buf = EOF; return c; }
+ virtual int lookChar();
+ virtual GString *getPSFilter(int psLevel, char *indent);
+ virtual GBool isBinary(GBool last = gTrue);
+
+private:
+
+ int encoding; // 'K' parameter
+ GBool endOfLine; // 'EndOfLine' parameter
+ GBool byteAlign; // 'EncodedByteAlign' parameter
+ int columns; // 'Columns' parameter
+ int rows; // 'Rows' parameter
+ GBool endOfBlock; // 'EndOfBlock' parameter
+ GBool black; // 'BlackIs1' parameter
+ GBool eof; // true if at eof
+ GBool nextLine2D; // true if next line uses 2D encoding
+ int row; // current row
+ int inputBuf; // input buffer
+ int inputBits; // number of bits in input buffer
+ short *refLine; // reference line changing elements
+ int b1; // index into refLine
+ short *codingLine; // coding line changing elements
+ int a0; // index into codingLine
+ int outputBits; // remaining ouput bits
+ int buf; // character buffer
+
+ short getTwoDimCode();
+ short getWhiteCode();
+ short getBlackCode();
+ short lookBits(int n);
+ void eatBits(int n) { inputBits -= n; }
+};
+
+//------------------------------------------------------------------------
+// DCTStream
+//------------------------------------------------------------------------
+
+// DCT component info
+struct DCTCompInfo {
+ int id; // component ID
+ int hSample, vSample; // horiz/vert sampling resolutions
+ int quantTable; // quantization table number
+ int prevDC; // DC coefficient accumulator
+};
+
+struct DCTScanInfo {
+ GBool comp[4]; // comp[i] is set if component i is
+ // included in this scan
+ int numComps; // number of components in the scan
+ int dcHuffTable[4]; // DC Huffman table numbers
+ int acHuffTable[4]; // AC Huffman table numbers
+ int firstCoeff, lastCoeff; // first and last DCT coefficient
+ int ah, al; // successive approximation parameters
+};
+
+// DCT Huffman decoding table
+struct DCTHuffTable {
+ Guchar firstSym[17]; // first symbol for this bit length
+ Gushort firstCode[17]; // first code for this bit length
+ Gushort numCodes[17]; // number of codes of this bit length
+ Guchar sym[256]; // symbols
+};
+
+class DCTStream: public FilterStream {
+public:
+
+ DCTStream(Stream *strA);
+ virtual ~DCTStream();
+ virtual StreamKind getKind() { return strDCT; }
+ virtual void reset();
+ virtual int getChar();
+ virtual int lookChar();
+ virtual GString *getPSFilter(int psLevel, char *indent);
+ virtual GBool isBinary(GBool last = gTrue);
+ Stream *getRawStream() { return str; }
+
+private:
+
+ GBool progressive; // set if in progressive mode
+ GBool interleaved; // set if in interleaved mode
+ int width, height; // image size
+ int mcuWidth, mcuHeight; // size of min coding unit, in data units
+ int bufWidth, bufHeight; // frameBuf size
+ DCTCompInfo compInfo[4]; // info for each component
+ DCTScanInfo scanInfo; // info for the current scan
+ int numComps; // number of components in image
+ int colorXform; // need YCbCr-to-RGB transform?
+ GBool gotJFIFMarker; // set if APP0 JFIF marker was present
+ GBool gotAdobeMarker; // set if APP14 Adobe marker was present
+ int restartInterval; // restart interval, in MCUs
+ Gushort quantTables[4][64]; // quantization tables
+ int numQuantTables; // number of quantization tables
+ DCTHuffTable dcHuffTables[4]; // DC Huffman tables
+ DCTHuffTable acHuffTables[4]; // AC Huffman tables
+ int numDCHuffTables; // number of DC Huffman tables
+ int numACHuffTables; // number of AC Huffman tables
+ Guchar *rowBuf[4][32]; // buffer for one MCU (non-progressive mode)
+ int *frameBuf[4]; // buffer for frame (progressive mode)
+ int comp, x, y, dy; // current position within image/MCU
+ int restartCtr; // MCUs left until restart
+ int restartMarker; // next restart marker
+ int eobRun; // number of EOBs left in the current run
+ int inputBuf; // input buffer for variable length codes
+ int inputBits; // number of valid bits in input buffer
+
+ void restart();
+ GBool readMCURow();
+ void readScan();
+ GBool readDataUnit(DCTHuffTable *dcHuffTable,
+ DCTHuffTable *acHuffTable,
+ int *prevDC, int data[64]);
+ GBool readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
+ DCTHuffTable *acHuffTable,
+ int *prevDC, int data[64]);
+ void decodeImage();
+ void transformDataUnit(Gushort *quantTable,
+ int dataIn[64], Guchar dataOut[64]);
+ int readHuffSym(DCTHuffTable *table);
+ int readAmp(int size);
+ int readBit();
+ GBool readHeader();
+ GBool readBaselineSOF();
+ GBool readProgressiveSOF();
+ GBool readScanInfo();
+ GBool readQuantTables();
+ GBool readHuffmanTables();
+ GBool readRestartInterval();
+ GBool readJFIFMarker();
+ GBool readAdobeMarker();
+ GBool readTrailer();
+ int readMarker();
+ int read16();
+};
+
+//------------------------------------------------------------------------
+// FlateStream
+//------------------------------------------------------------------------
+
+#define flateWindow 32768 // buffer size
+#define flateMask (flateWindow-1)
+#define flateMaxHuffman 15 // max Huffman code length
+#define flateMaxCodeLenCodes 19 // max # code length codes
+#define flateMaxLitCodes 288 // max # literal codes
+#define flateMaxDistCodes 30 // max # distance codes
+
+// Huffman code table entry
+struct FlateCode {
+ Gushort len; // code length, in bits
+ Gushort val; // value represented by this code
+};
+
+struct FlateHuffmanTab {
+ FlateCode *codes;
+ int maxLen;
+};
+
+// Decoding info for length and distance code words
+struct FlateDecode {
+ int bits; // # extra bits
+ int first; // first length/distance
+};
+
+class FlateStream: public FilterStream {
+public:
+
+ FlateStream(Stream *strA, int predictor, int columns,
+ int colors, int bits);
+ virtual ~FlateStream();
+ virtual StreamKind getKind() { return strFlate; }
+ virtual void reset();
+ virtual int getChar();
+ virtual int lookChar();
+ virtual int getRawChar();
+ virtual GString *getPSFilter(int psLevel, char *indent);
+ virtual GBool isBinary(GBool last = gTrue);
+
+private:
+
+ StreamPredictor *pred; // predictor
+ Guchar buf[flateWindow]; // output data buffer
+ int index; // current index into output buffer
+ int remain; // number valid bytes in output buffer
+ int codeBuf; // input buffer
+ int codeSize; // number of bits in input buffer
+ int // literal and distance code lengths
+ codeLengths[flateMaxLitCodes + flateMaxDistCodes];
+ FlateHuffmanTab litCodeTab; // literal code table
+ FlateHuffmanTab distCodeTab; // distance code table
+ GBool compressedBlock; // set if reading a compressed block
+ int blockLen; // remaining length of uncompressed block
+ GBool endOfBlock; // set when end of block is reached
+ GBool eof; // set when end of stream is reached
+
+ static int // code length code reordering
+ codeLenCodeMap[flateMaxCodeLenCodes];
+ static FlateDecode // length decoding info
+ lengthDecode[flateMaxLitCodes-257];
+ static FlateDecode // distance decoding info
+ distDecode[flateMaxDistCodes];
+ static FlateHuffmanTab // fixed literal code table
+ fixedLitCodeTab;
+ static FlateHuffmanTab // fixed distance code table
+ fixedDistCodeTab;
+
+ void readSome();
+ GBool startBlock();
+ void loadFixedCodes();
+ GBool readDynamicCodes();
+ void compHuffmanCodes(int *lengths, int n, FlateHuffmanTab *tab);
+ int getHuffmanCodeWord(FlateHuffmanTab *tab);
+ int getCodeWord(int bits);
+};
+
+//------------------------------------------------------------------------
+// EOFStream
+//------------------------------------------------------------------------
+
+class EOFStream: public FilterStream {
+public:
+
+ EOFStream(Stream *strA);
+ virtual ~EOFStream();
+ virtual StreamKind getKind() { return strWeird; }
+ virtual void reset() {}
+ virtual int getChar() { return EOF; }
+ virtual int lookChar() { return EOF; }
+ virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; }
+ virtual GBool isBinary(GBool last = gTrue) { return gFalse; }
+};
+
+//------------------------------------------------------------------------
+// FixedLengthEncoder
+//------------------------------------------------------------------------
+
+class FixedLengthEncoder: public FilterStream {
+public:
+
+ FixedLengthEncoder(Stream *strA, int lengthA);
+ ~FixedLengthEncoder();
+ virtual StreamKind getKind() { return strWeird; }
+ virtual void reset();
+ virtual int getChar();
+ virtual int lookChar();
+ virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; }
+ virtual GBool isBinary(GBool last = gTrue);
+ virtual GBool isEncoder() { return gTrue; }
+
+private:
+
+ int length;
+ int count;
+};
+
+//------------------------------------------------------------------------
+// ASCIIHexEncoder
+//------------------------------------------------------------------------
+
+class ASCIIHexEncoder: public FilterStream {
+public:
+
+ ASCIIHexEncoder(Stream *strA);
+ virtual ~ASCIIHexEncoder();
+ virtual StreamKind getKind() { return strWeird; }
+ virtual void reset();
+ virtual int getChar()
+ { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
+ virtual int lookChar()
+ { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
+ virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; }
+ virtual GBool isBinary(GBool last = gTrue) { return gFalse; }
+ virtual GBool isEncoder() { return gTrue; }
+
+private:
+
+ char buf[4];
+ char *bufPtr;
+ char *bufEnd;
+ int lineLen;
+ GBool eof;
+
+ GBool fillBuf();
+};
+
+//------------------------------------------------------------------------
+// ASCII85Encoder
+//------------------------------------------------------------------------
+
+class ASCII85Encoder: public FilterStream {
+public:
+
+ ASCII85Encoder(Stream *strA);
+ virtual ~ASCII85Encoder();
+ virtual StreamKind getKind() { return strWeird; }
+ virtual void reset();
+ virtual int getChar()
+ { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
+ virtual int lookChar()
+ { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
+ virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; }
+ virtual GBool isBinary(GBool last = gTrue) { return gFalse; }
+ virtual GBool isEncoder() { return gTrue; }
+
+private:
+
+ char buf[8];
+ char *bufPtr;
+ char *bufEnd;
+ int lineLen;
+ GBool eof;
+
+ GBool fillBuf();
+};
+
+//------------------------------------------------------------------------
+// RunLengthEncoder
+//------------------------------------------------------------------------
+
+class RunLengthEncoder: public FilterStream {
+public:
+
+ RunLengthEncoder(Stream *strA);
+ virtual ~RunLengthEncoder();
+ virtual StreamKind getKind() { return strWeird; }
+ virtual void reset();
+ virtual int getChar()
+ { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }
+ virtual int lookChar()
+ { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
+ virtual GString *getPSFilter(int psLevel, char *indent) { return NULL; }
+ virtual GBool isBinary(GBool last = gTrue) { return gTrue; }
+ virtual GBool isEncoder() { return gTrue; }
+
+private:
+
+ char buf[131];
+ char *bufPtr;
+ char *bufEnd;
+ char *nextEnd;
+ GBool eof;
+
+ GBool fillBuf();
+};
+
+#endif
--- /dev/null
+//========================================================================
+//
+// UTF8.h
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+static int mapUTF8(Unicode u, char *buf, int bufSize) {
+ if (u <= 0x0000007f) {
+ if (bufSize < 1) {
+ return 0;
+ }
+ buf[0] = (char)u;
+ return 1;
+ } else if (u <= 0x000007ff) {
+ if (bufSize < 2) {
+ return 0;
+ }
+ buf[0] = (char)(0xc0 + (u >> 6));
+ buf[1] = (char)(0x80 + (u & 0x3f));
+ return 2;
+ } else if (u <= 0x0000ffff) {
+ if (bufSize < 3) {
+ return 0;
+ }
+ buf[0] = (char)(0xe0 + (u >> 12));
+ buf[1] = (char)(0x80 + ((u >> 6) & 0x3f));
+ buf[2] = (char)(0x80 + (u & 0x3f));
+ return 3;
+ } else if (u <= 0x0010ffff) {
+ if (bufSize < 4) {
+ return 0;
+ }
+ buf[0] = (char)(0xf0 + (u >> 18));
+ buf[1] = (char)(0x80 + ((u >> 12) & 0x3f));
+ buf[2] = (char)(0x80 + ((u >> 6) & 0x3f));
+ buf[3] = (char)(0x80 + (u & 0x3f));
+ return 4;
+ } else {
+ return 0;
+ }
+}
+
+static int mapUCS2(Unicode u, char *buf, int bufSize) {
+ if (u <= 0xffff) {
+ if (bufSize < 2) {
+ return 0;
+ }
+ buf[0] = (char)((u >> 8) & 0xff);
+ buf[1] = (char)(u & 0xff);
+ return 2;
+ } else {
+ return 0;
+ }
+}
--- /dev/null
+//========================================================================
+//
+// UnicodeMap.cc
+//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include "gmem.h"
+#include "gfile.h"
+#include "GString.h"
+#include "GList.h"
+#include "Error.h"
+#include "GlobalParams.h"
+#include "UnicodeMap.h"
+
+//------------------------------------------------------------------------
+
+#define maxExtCode 16
+
+struct UnicodeMapExt {
+ Unicode u; // Unicode char
+ char code[maxExtCode];
+ Guint nBytes;
+};
+
+//------------------------------------------------------------------------
+
+UnicodeMap *UnicodeMap::parse(GString *encodingNameA) {
+ FILE *f;
+ UnicodeMap *map;
+ UnicodeMapRange *range;
+ UnicodeMapExt *eMap;
+ int size, eMapsSize;
+ char buf[256];
+ int line, nBytes, i, x;
+ char *tok1, *tok2, *tok3;
+
+ if (!(f = globalParams->getUnicodeMapFile(encodingNameA))) {
+ error(-1, "Couldn't find unicodeMap file for the '%s' encoding",
+ encodingNameA->getCString());
+ return NULL;
+ }
+
+ map = new UnicodeMap(encodingNameA->copy());
+
+ size = 8;
+ map->ranges = (UnicodeMapRange *)gmallocn(size, sizeof(UnicodeMapRange));
+ eMapsSize = 0;
+
+ line = 1;
+ while (getLine(buf, sizeof(buf), f)) {
+ if ((tok1 = strtok(buf, " \t\r\n")) &&
+ (tok2 = strtok(NULL, " \t\r\n"))) {
+ if (!(tok3 = strtok(NULL, " \t\r\n"))) {
+ tok3 = tok2;
+ tok2 = tok1;
+ }
+ nBytes = strlen(tok3) / 2;
+ if (nBytes <= 4) {
+ if (map->len == size) {
+ size *= 2;
+ map->ranges = (UnicodeMapRange *)
+ greallocn(map->ranges, size, sizeof(UnicodeMapRange));
+ }
+ range = &map->ranges[map->len];
+ sscanf(tok1, "%x", &range->start);
+ sscanf(tok2, "%x", &range->end);
+ sscanf(tok3, "%x", &range->code);
+ range->nBytes = nBytes;
+ ++map->len;
+ } else if (tok2 == tok1) {
+ if (map->eMapsLen == eMapsSize) {
+ eMapsSize += 16;
+ map->eMaps = (UnicodeMapExt *)
+ greallocn(map->eMaps, eMapsSize, sizeof(UnicodeMapExt));
+ }
+ eMap = &map->eMaps[map->eMapsLen];
+ sscanf(tok1, "%x", &eMap->u);
+ for (i = 0; i < nBytes; ++i) {
+ sscanf(tok3 + i*2, "%2x", &x);
+ eMap->code[i] = (char)x;
+ }
+ eMap->nBytes = nBytes;
+ ++map->eMapsLen;
+ } else {
+ error(-1, "Bad line (%d) in unicodeMap file for the '%s' encoding",
+ line, encodingNameA->getCString());
+ }
+ } else {
+ error(-1, "Bad line (%d) in unicodeMap file for the '%s' encoding",
+ line, encodingNameA->getCString());
+ }
+ ++line;
+ }
+
+ fclose(f);
+
+ return map;
+}
+
+UnicodeMap::UnicodeMap(GString *encodingNameA) {
+ encodingName = encodingNameA;
+ unicodeOut = gFalse;
+ kind = unicodeMapUser;
+ ranges = NULL;
+ len = 0;
+ eMaps = NULL;
+ eMapsLen = 0;
+ refCnt = 1;
+#if MULTITHREADED
+ gInitMutex(&mutex);
+#endif
+}
+
+UnicodeMap::UnicodeMap(char *encodingNameA, GBool unicodeOutA,
+ UnicodeMapRange *rangesA, int lenA) {
+ encodingName = new GString(encodingNameA);
+ unicodeOut = unicodeOutA;
+ kind = unicodeMapResident;
+ ranges = rangesA;
+ len = lenA;
+ eMaps = NULL;
+ eMapsLen = 0;
+ refCnt = 1;
+#if MULTITHREADED
+ gInitMutex(&mutex);
+#endif
+}
+
+UnicodeMap::UnicodeMap(char *encodingNameA, GBool unicodeOutA,
+ UnicodeMapFunc funcA) {
+ encodingName = new GString(encodingNameA);
+ unicodeOut = unicodeOutA;
+ kind = unicodeMapFunc;
+ func = funcA;
+ eMaps = NULL;
+ eMapsLen = 0;
+ refCnt = 1;
+#if MULTITHREADED
+ gInitMutex(&mutex);
+#endif
+}
+
+UnicodeMap::~UnicodeMap() {
+ delete encodingName;
+ if (kind == unicodeMapUser && ranges) {
+ gfree(ranges);
+ }
+ if (eMaps) {
+ gfree(eMaps);
+ }
+#if MULTITHREADED
+ gDestroyMutex(&mutex);
+#endif
+}
+
+void UnicodeMap::incRefCnt() {
+#if MULTITHREADED
+ gLockMutex(&mutex);
+#endif
+ ++refCnt;
+#if MULTITHREADED
+ gUnlockMutex(&mutex);
+#endif
+}
+
+void UnicodeMap::decRefCnt() {
+ GBool done;
+
+#if MULTITHREADED
+ gLockMutex(&mutex);
+#endif
+ done = --refCnt == 0;
+#if MULTITHREADED
+ gUnlockMutex(&mutex);
+#endif
+ if (done) {
+ delete this;
+ }
+}
+
+GBool UnicodeMap::match(GString *encodingNameA) {
+ return !encodingName->cmp(encodingNameA);
+}
+
+int UnicodeMap::mapUnicode(Unicode u, char *buf, int bufSize) {
+ int a, b, m, n, i, j;
+ Guint code;
+
+ if (kind == unicodeMapFunc) {
+ return (*func)(u, buf, bufSize);
+ }
+
+ a = 0;
+ b = len;
+ if (u >= ranges[a].start) {
+ // invariant: ranges[a].start <= u < ranges[b].start
+ while (b - a > 1) {
+ m = (a + b) / 2;
+ if (u >= ranges[m].start) {
+ a = m;
+ } else if (u < ranges[m].start) {
+ b = m;
+ }
+ }
+ if (u <= ranges[a].end) {
+ n = ranges[a].nBytes;
+ if (n > bufSize) {
+ return 0;
+ }
+ code = ranges[a].code + (u - ranges[a].start);
+ for (i = n - 1; i >= 0; --i) {
+ buf[i] = (char)(code & 0xff);
+ code >>= 8;
+ }
+ return n;
+ }
+ }
+
+ for (i = 0; i < eMapsLen; ++i) {
+ if (eMaps[i].u == u) {
+ n = eMaps[i].nBytes;
+ for (j = 0; j < n; ++j) {
+ buf[j] = eMaps[i].code[j];
+ }
+ return n;
+ }
+ }
+
+ return 0;
+}
+
+//------------------------------------------------------------------------
+
+UnicodeMapCache::UnicodeMapCache() {
+ int i;
+
+ for (i = 0; i < unicodeMapCacheSize; ++i) {
+ cache[i] = NULL;
+ }
+}
+
+UnicodeMapCache::~UnicodeMapCache() {
+ int i;
+
+ for (i = 0; i < unicodeMapCacheSize; ++i) {
+ if (cache[i]) {
+ cache[i]->decRefCnt();
+ }
+ }
+}
+
+UnicodeMap *UnicodeMapCache::getUnicodeMap(GString *encodingName) {
+ UnicodeMap *map;
+ int i, j;
+
+ if (cache[0] && cache[0]->match(encodingName)) {
+ cache[0]->incRefCnt();
+ return cache[0];
+ }
+ for (i = 1; i < unicodeMapCacheSize; ++i) {
+ if (cache[i] && cache[i]->match(encodingName)) {
+ map = cache[i];
+ for (j = i; j >= 1; --j) {
+ cache[j] = cache[j - 1];
+ }
+ cache[0] = map;
+ map->incRefCnt();
+ return map;
+ }
+ }
+ if ((map = UnicodeMap::parse(encodingName))) {
+ if (cache[unicodeMapCacheSize - 1]) {
+ cache[unicodeMapCacheSize - 1]->decRefCnt();
+ }
+ for (j = unicodeMapCacheSize - 1; j >= 1; --j) {
+ cache[j] = cache[j - 1];
+ }
+ cache[0] = map;
+ map->incRefCnt();
+ return map;
+ }
+ return NULL;
+}