summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Jamfile129
-rw-r--r--base/arc4.c95
-rw-r--r--base/error.c50
-rw-r--r--base/hash.c263
-rw-r--r--base/matrix.c70
-rw-r--r--base/md5.c269
-rw-r--r--base/memory.c81
-rw-r--r--base/rect.c46
-rw-r--r--filter/TODO8
-rw-r--r--filter/a85d.c129
-rw-r--r--filter/a85e.c128
-rw-r--r--filter/ahxd.c113
-rw-r--r--filter/ahxe.c65
-rw-r--r--filter/arc4filter.c47
-rw-r--r--filter/buffer.c57
-rw-r--r--filter/dctc.h39
-rw-r--r--filter/dctd.c215
-rw-r--r--filter/dcte.c254
-rw-r--r--filter/faxc.h116
-rw-r--r--filter/faxd.c463
-rw-r--r--filter/faxd.h61
-rw-r--r--filter/faxdtab.c931
-rw-r--r--filter/faxe.c448
-rw-r--r--filter/faxe.h37
-rw-r--r--filter/faxetab.c131
-rw-r--r--filter/file.c589
-rw-r--r--filter/filter.c59
-rw-r--r--filter/flate.c178
-rw-r--r--filter/jbig2d.c97
-rw-r--r--filter/jpxd.c144
-rw-r--r--filter/lzwd.c263
-rw-r--r--filter/lzwe.c257
-rw-r--r--filter/null.c51
-rw-r--r--filter/pipeline.c128
-rw-r--r--filter/predict.c239
-rw-r--r--filter/rld.c67
-rw-r--r--filter/rle.c238
-rw-r--r--include/fitz.h30
-rw-r--r--include/fitz/base.h59
-rw-r--r--include/fitz/cmap.h25
-rw-r--r--include/fitz/crypt.h30
-rw-r--r--include/fitz/file.h33
-rw-r--r--include/fitz/filter.h80
-rw-r--r--include/fitz/font.h63
-rw-r--r--include/fitz/geometry.h56
-rw-r--r--include/fitz/hash.h15
-rw-r--r--include/fitz/image.h17
-rw-r--r--include/fitz/math.h30
-rw-r--r--include/fitz/object.h124
-rw-r--r--include/fitz/path.h57
-rw-r--r--include/fitz/pixmap.h22
-rw-r--r--include/fitz/render.h11
-rw-r--r--include/fitz/scanconv.h46
-rw-r--r--include/fitz/sysdep.h48
-rw-r--r--include/fitz/text.h23
-rw-r--r--include/fitz/tree.h156
-rw-r--r--include/mupdf.h253
-rw-r--r--mupdf/build.c376
-rw-r--r--mupdf/cmap.c521
-rw-r--r--mupdf/crypt.c356
-rw-r--r--mupdf/font.c790
-rw-r--r--mupdf/fontagl.h4305
-rw-r--r--mupdf/fontenc.h349
-rw-r--r--mupdf/fontfile.c263
-rw-r--r--mupdf/interpret.c786
-rw-r--r--mupdf/lex.c342
-rw-r--r--mupdf/open.c492
-rw-r--r--mupdf/pagetree.c174
-rw-r--r--mupdf/parse.c305
-rw-r--r--mupdf/repair.c279
-rw-r--r--mupdf/resources.c184
-rw-r--r--mupdf/save.c314
-rw-r--r--mupdf/stream.c262
-rw-r--r--mupdf/xref.c410
-rw-r--r--object/array.c182
-rw-r--r--object/dict.c260
-rw-r--r--object/parse.c400
-rw-r--r--object/print.c332
-rw-r--r--object/simple.c230
-rw-r--r--render/edgelist.c288
-rw-r--r--render/fill.c123
-rw-r--r--render/glyphcache.c370
-rw-r--r--render/pixmap.c60
-rw-r--r--render/porterduff.c74
-rw-r--r--render/render.c183
-rw-r--r--render/scanconv.c176
-rw-r--r--render/stroke.c716
-rw-r--r--tests/gsrfilt.rc10
-rw-r--r--tests/gswfilt.rc11
-rw-r--r--tests/oscar.rawbin0 -> 14401 bytes
-rw-r--r--tests/other.h17
-rw-r--r--tests/pred.rc23
-rw-r--r--tests/ptt3.pbmbin0 -> 513249 bytes
-rw-r--r--tests/t-filter.c111
-rw-r--r--tests/t-parse.c44
-rw-r--r--tests/t-scanconv.c204
-rwxr-xr-xtests/testfax.rc40
-rw-r--r--tests/tiger.c213
-rw-r--r--tests/tiger.h3162
-rw-r--r--tests/zapf.rawbin0 -> 1131 bytes
-rw-r--r--tree/blend.c53
-rw-r--r--tree/cache.c98
-rw-r--r--tree/cmap.c452
-rw-r--r--tree/debug.c177
-rw-r--r--tree/font.c275
-rw-r--r--tree/form.c30
-rw-r--r--tree/image.c49
-rw-r--r--tree/mask.c50
-rw-r--r--tree/meta.c37
-rw-r--r--tree/node.c148
-rw-r--r--tree/over.c50
-rw-r--r--tree/path.c291
-rw-r--r--tree/solid.c33
-rw-r--r--tree/text.c67
-rw-r--r--tree/transform.c35
-rw-r--r--tree/tree.c63
-rw-r--r--util/cleanname.c63
-rw-r--r--util/getopt.c116
-rw-r--r--util/strlcat.c34
-rw-r--r--util/strlcpy.c32
-rw-r--r--util/strsep.c11
121 files changed, 27634 insertions, 0 deletions
diff --git a/Jamfile b/Jamfile
new file mode 100644
index 00000000..6e335c98
--- /dev/null
+++ b/Jamfile
@@ -0,0 +1,129 @@
+#
+# Jamfile for Fitz
+#
+
+include $(HOME)/.jamrules ;
+
+HDRS = include ;
+
+Library libfitz :
+ # util stuff
+ #util/cleanname.c
+ #util/getopt.c
+ #util/strsep.c
+ #util/strlcpy.c
+ #util/strlcat.c
+
+ # base runtime
+ base/error.c
+ base/memory.c
+ base/md5.c
+ base/arc4.c
+ base/rect.c
+ base/matrix.c
+ base/hash.c
+
+ # object
+ object/simple.c
+ object/array.c
+ object/dict.c
+ object/print.c
+ object/parse.c
+
+ # filter
+ filter/buffer.c
+ filter/filter.c
+ filter/file.c # unistd.h
+
+ filter/null.c
+ filter/arc4filter.c
+ filter/pipeline.c
+
+ filter/ahxd.c
+ filter/ahxe.c
+ filter/a85d.c
+ filter/a85e.c
+ filter/rld.c
+ filter/rle.c
+ filter/predict.c
+ filter/lzwd.c
+ filter/lzwe.c
+
+ filter/faxd.c
+ filter/faxdtab.c
+ filter/faxe.c
+ filter/faxetab.c
+
+ filter/flate.c # libz
+ filter/dctd.c # libjpeg
+ filter/dcte.c # libjpeg
+ filter/jbig2d.c # jbig2dec
+ filter/jpxd.c # jasper
+
+ # tree
+ tree/tree.c
+ tree/debug.c
+ tree/cmap.c
+ tree/font.c
+
+ tree/node.c
+ tree/meta.c
+ tree/over.c
+ tree/mask.c
+ tree/blend.c
+ tree/form.c
+ tree/transform.c
+ tree/solid.c
+ tree/image.c
+ tree/text.c
+ tree/path.c
+
+ # render
+ render/glyphcache.c
+ render/pixmap.c
+ render/edgelist.c
+ render/scanconv.c
+ render/fill.c
+ render/stroke.c
+ render/render.c
+;
+
+Library libmupdf :
+ mupdf/lex.c
+ mupdf/parse.c
+ mupdf/crypt.c
+ mupdf/stream.c
+
+ mupdf/open.c
+ mupdf/repair.c
+ mupdf/save.c
+ mupdf/xref.c
+
+ mupdf/cmap.c
+ mupdf/font.c
+ mupdf/fontfile.c
+ mupdf/resources.c
+ mupdf/pagetree.c
+ mupdf/build.c
+ mupdf/interpret.c
+ ;
+
+LINKLIBS =
+ -lfontconfig
+ -lfreetype
+ -ljbig2dec
+ -ljasper
+ -ljpeg
+ -lz
+ -lm
+ ;
+
+Main t-mupdf : test/mupdf.c ;
+LinkLibraries t-mupdf : libmupdf libfitz ;
+
+#Main t-filter : tests/t-filter.c ;
+#Main t-parse : tests/t-parse.c ;
+#Main t-scanconv : tests/t-scanconv.c ;
+#Main tiger : tests/tiger.c ;
+#LinkLibraries t-filter t-parse t-scanconv t-fontrast tiger : libfitz ;
+
diff --git a/base/arc4.c b/base/arc4.c
new file mode 100644
index 00000000..86c9afd8
--- /dev/null
+++ b/base/arc4.c
@@ -0,0 +1,95 @@
+/* This code illustrates a sample implementation
+ * of the Arcfour algorithm
+ * Copyright (c) April 29, 1997 Kalle Kaukonen.
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that this copyright
+ * notice and disclaimer are retained.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KALLE KAUKONEN AND CONTRIBUTORS ``AS
+ * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KALLE
+ * KAUKONEN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <fitz.h>
+
+void
+fz_arc4init(fz_arc4 *arc4, unsigned char *key, unsigned keylen)
+{
+ unsigned int t, u;
+ unsigned int keyindex;
+ unsigned int stateindex;
+ unsigned char *state;
+ unsigned int counter;
+
+ state = arc4->state;
+
+ arc4->x = 0;
+ arc4->y = 0;
+
+ for (counter = 0; counter < 256; counter++) {
+ state[counter] = counter;
+ }
+
+ keyindex = 0;
+ stateindex = 0;
+
+ for (counter = 0; counter < 256; counter++) {
+ t = state[counter];
+ stateindex = (stateindex + key[keyindex] + t) & 0xff;
+ u = state[stateindex];
+
+ state[stateindex] = t;
+ state[counter] = u;
+
+ if (++keyindex >= keylen) {
+ keyindex = 0;
+ }
+ }
+}
+
+unsigned char
+fz_arc4next(fz_arc4 *arc4)
+{
+ unsigned int x;
+ unsigned int y;
+ unsigned int sx, sy;
+ unsigned char *state;
+
+ state = arc4->state;
+
+ x = (arc4->x + 1) & 0xff;
+ sx = state[x];
+ y = (sx + arc4->y) & 0xff;
+ sy = state[y];
+
+ arc4->x = x;
+ arc4->y = y;
+
+ state[y] = sx;
+ state[x] = sy;
+
+ return state[(sx + sy) & 0xff];
+}
+
+void
+fz_arc4encrypt(fz_arc4 *arc4, unsigned char *dest, unsigned char *src, unsigned len)
+{
+ unsigned int i;
+ for (i = 0; i < len; i++) {
+ unsigned char x;
+ x = fz_arc4next(arc4);
+ dest[i] = src[i] ^ x;
+ }
+}
+
diff --git a/base/error.c b/base/error.c
new file mode 100644
index 00000000..bbba9ac3
--- /dev/null
+++ b/base/error.c
@@ -0,0 +1,50 @@
+#include <fitz.h>
+
+void
+fz_warn(char *fmt, ...)
+{
+ va_list ap;
+ fprintf(stderr, "warning: ");
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+
+fz_error *
+fz_throw0(const char *func, const char *file, int line, char *fmt, ...)
+{
+ va_list ap;
+ fz_error *eo;
+
+ eo = fz_malloc(sizeof(fz_error));
+ if (!eo) return fz_outofmem;
+
+ strlcpy(eo->func, func, sizeof eo->func);
+ strlcpy(eo->file, file, sizeof eo->file);
+ eo->line = line;
+
+ va_start(ap, fmt);
+ vsnprintf(eo->msg, sizeof eo->msg, fmt, ap);
+ eo->msg[sizeof(eo->msg) - 1] = '\0';
+ va_end(ap);
+
+ return eo;
+}
+
+void
+fz_freeerror(fz_error *eo)
+{
+ if (!eo->frozen)
+ fz_free(eo);
+}
+
+void
+fz_abort(fz_error *eo)
+{
+ fflush(stdout);
+ fprintf(stderr, "%s:%d: %s(): %s\n", eo->file, eo->line, eo->func, eo->msg);
+ fflush(stderr);
+ abort();
+}
+
diff --git a/base/hash.c b/base/hash.c
new file mode 100644
index 00000000..4b85b6b3
--- /dev/null
+++ b/base/hash.c
@@ -0,0 +1,263 @@
+/* Linear probe hash table.
+ * 2004 (C) Tor Andersson.
+ * BSD license.
+ *
+ * Simple hashtable with open adressing linear probe.
+ * Unlike text book examples, removing entries works
+ * correctly in this implementation so it wont start
+ * exhibiting bad behaviour if entries are inserted
+ * and removed frequently.
+ */
+
+#include <fitz.h>
+
+enum { MAXKEYLEN = 16 };
+
+typedef struct fz_hashentry_s fz_hashentry;
+
+struct fz_hashentry_s
+{
+ unsigned char key[MAXKEYLEN];
+ void *val;
+};
+
+struct fz_hashtable_s
+{
+ unsigned keylen;
+ unsigned size;
+ unsigned load;
+ fz_hashentry *ents;
+};
+
+static unsigned hash(unsigned char *s, int len)
+{
+ unsigned hash = 0;
+ unsigned i;
+ for (i = 0; i < len; i++)
+ {
+ hash += s[i];
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+ return hash;
+}
+
+fz_error *
+fz_newhash(fz_hashtable **tablep, int initialsize, int keylen)
+{
+ fz_hashtable *table;
+
+ assert(keylen <= MAXKEYLEN);
+
+ table = *tablep = fz_malloc(sizeof(fz_hashtable));
+ if (!table)
+ return fz_outofmem;
+
+ table->keylen = keylen;
+ table->size = initialsize;
+ table->load = 0;
+
+ table->ents = fz_malloc(sizeof(fz_hashentry) * table->size);
+ if (!table->ents)
+ {
+ fz_free(table);
+ *tablep = nil;
+ return fz_outofmem;
+ }
+
+ memset(table->ents, 0, sizeof(fz_hashentry) * table->size);
+
+ return nil;
+}
+
+int
+fz_hashlen(fz_hashtable *table)
+{
+ return table->size;
+}
+
+void *
+fz_gethashkey(fz_hashtable *table, int idx)
+{
+ return table->ents[idx].key;
+}
+
+void *
+fz_gethashval(fz_hashtable *table, int idx)
+{
+ return table->ents[idx].val;
+}
+
+void
+fz_freehash(fz_hashtable *table)
+{
+ fz_free(table->ents);
+ fz_free(table);
+}
+
+fz_error *
+fz_resizehash(fz_hashtable *table, int newsize)
+{
+ fz_error *error;
+ fz_hashentry *newents;
+ fz_hashentry *oldents;
+ unsigned oldload;
+ unsigned oldsize;
+ unsigned i;
+
+ oldsize = table->size;
+ oldload = table->load;
+ oldents = table->ents;
+
+ if (newsize < oldload * 8 / 10)
+ return fz_throw("rangecheck: resize hash too small");
+
+ newents = fz_realloc(table->ents, sizeof(fz_hashentry) * newsize);
+ if (!newents)
+ return fz_outofmem;
+
+ table->size = newsize;
+ table->load = 0;
+ table->ents = newents;
+ memset(table->ents, 0, sizeof(fz_hashentry) * table->size);
+
+ for (i = 0; i < oldsize; i++)
+ {
+ if (oldents[i].val)
+ {
+ error = fz_hashinsert(table, oldents[i].key, oldents[i].val);
+ if (error)
+ {
+ table->size = oldsize;
+ table->load = oldload;
+ table->ents = oldents;
+ fz_free(newents);
+ return error;
+ }
+ }
+ }
+
+ fz_free(oldents);
+ return nil;
+}
+
+void *
+fz_hashfind(fz_hashtable *table, void *key)
+{
+ fz_hashentry *ents = table->ents;
+ unsigned size = table->size;
+ unsigned pos = hash(key, table->keylen) % size;
+
+ while (1)
+ {
+ if (!ents[pos].val)
+ return nil;
+
+ if (memcmp(key, &ents[pos].key, table->keylen) == 0)
+ return ents[pos].val;
+
+ pos = (pos + 1) % size;
+ }
+}
+
+fz_error *
+fz_hashinsert(fz_hashtable *table, void *key, void *val)
+{
+ fz_error *error;
+ fz_hashentry *ents = table->ents;
+ unsigned size = table->size;
+ unsigned pos = hash(key, table->keylen) % size;
+
+ if (table->load > table->size * 8 / 10)
+ {
+ error = fz_resizehash(table, table->size * 2);
+ if (error)
+ return error;
+ }
+
+ while (1)
+ {
+ if (!ents[pos].val)
+ {
+ memcpy(ents[pos].key, key, table->keylen);
+ ents[pos].val = val;
+ table->load ++;
+ return nil;
+ }
+
+ if (memcmp(key, &ents[pos].key, table->keylen) == 0)
+ return fz_throw("rangecheck: overwrite hash slot");
+
+ pos = (pos + 1) % size;
+ }
+
+ return nil;
+}
+
+fz_error *
+fz_hashremove(fz_hashtable *table, void *key)
+{
+ fz_hashentry *ents = table->ents;
+ unsigned size = table->size;
+ unsigned pos = hash(key, table->keylen) % size;
+ unsigned hole, look, code;
+
+ while (1)
+ {
+ if (!ents[pos].val)
+ return fz_throw("rangecheck: remove inexistant hash entry");
+
+ if (memcmp(key, &ents[pos].key, table->keylen) == 0)
+ {
+ ents[pos].val = nil;
+
+ hole = pos;
+ look = (hole + 1) % size;
+
+ while (ents[look].val)
+ {
+ code = hash(ents[look].key, table->keylen) % size;
+ if ((code <= hole && hole < look) ||
+ (look < code && code <= hole) ||
+ (hole < look && look < code))
+ {
+ ents[hole] = ents[look];
+ ents[look].val = nil;
+ hole = look;
+ }
+
+ look = (look + 1) % size;
+ }
+
+ table->load --;
+ return nil;
+ }
+
+ pos = (pos + 1) % size;
+ }
+}
+
+void
+fz_debughash(fz_hashtable *table)
+{
+ int i, k;
+
+ printf("cache load %d / %d\n", table->load, table->size);
+
+ for (i = 0; i < table->size; i++)
+ {
+ if (!table->ents[i].val)
+ printf("table % 4d: empty\n", i);
+ else
+ {
+ printf("table % 4d: key=", i);
+ for (k = 0; k < MAXKEYLEN; k++)
+ printf("%02x", ((char*)table->ents[i].key)[k]);
+ printf(" val=$%p\n", table->ents[i].val);
+ }
+ }
+}
+
diff --git a/base/matrix.c b/base/matrix.c
new file mode 100644
index 00000000..70193427
--- /dev/null
+++ b/base/matrix.c
@@ -0,0 +1,70 @@
+#include <fitz.h>
+
+fz_matrix
+fz_concat(fz_matrix one, fz_matrix two)
+{
+ fz_matrix dst;
+ dst.a = one.a * two.a + one.b * two.c;
+ dst.b = one.a * two.b + one.b * two.d;
+ dst.c = one.c * two.a + one.d * two.c;
+ dst.d = one.c * two.b + one.d * two.d;
+ dst.e = one.e * two.a + one.f * two.c + two.e;
+ dst.f = one.e * two.b + one.f * two.d + two.f;
+ return dst;
+}
+
+fz_matrix
+fz_identity(void)
+{
+ return (fz_matrix) { 1, 0, 0, 1, 0, 0 };
+}
+
+fz_matrix
+fz_scale(float sx, float sy)
+{
+ return (fz_matrix) { sx, 0, 0, sy, 0, 0 };
+}
+
+fz_matrix
+fz_rotate(float theta)
+{
+ float s = sin(theta * M_PI / 180.0);
+ float c = cos(theta * M_PI / 180.0);
+ return (fz_matrix) { c, s, -s, c, 0 ,0 };
+}
+
+fz_matrix
+fz_translate(float tx, float ty)
+{
+ return (fz_matrix) { 1, 0, 0, 1, tx, ty };
+}
+
+fz_matrix
+fz_invertmatrix(fz_matrix src)
+{
+ fz_matrix dst;
+ float rdet = 1.0 / (src.a * src.d - src.b * src.c);
+ dst.a = src.d * rdet;
+ dst.b = -src.b * rdet;
+ dst.c = -src.c * rdet;
+ dst.d = src.a * rdet;
+ dst.e = -src.e * dst.a - src.f * dst.c;
+ dst.f = -src.e * dst.b - src.f * dst.d;
+ return dst;
+}
+
+int
+fz_isrectilinear(fz_matrix m)
+{
+ return (fabs(m.b) < FLT_EPSILON && fabs(m.c) < FLT_EPSILON) ||
+ (fabs(m.a) < FLT_EPSILON && fabs(m.d) < FLT_EPSILON);
+}
+
+fz_point
+fz_transformpoint(fz_matrix m, fz_point p)
+{
+ float x = p.x * m.a + p.y * m.c + m.e;
+ float y = p.x * m.b + p.y * m.d + m.f;
+ return (fz_point) { x, y };
+}
+
diff --git a/base/md5.c b/base/md5.c
new file mode 100644
index 00000000..648532a5
--- /dev/null
+++ b/base/md5.c
@@ -0,0 +1,269 @@
+/*
+MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
+
+Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991.
+All rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+*/
+
+#include <fitz.h>
+
+/* Constants for MD5Transform routine */
+enum
+{
+ S11 = 7, S12 = 12, S13 = 17, S14 = 22,
+ S21 = 5, S22 = 9, S23 = 14, S24 = 20,
+ S31 = 4, S32 = 11, S33 = 16, S34 = 23,
+ S41 = 6, S42 = 10, S43 = 15, S44 = 21,
+};
+
+static void encode(unsigned char *, unsigned long *, unsigned);
+static void decode(unsigned long *, unsigned char *, unsigned);
+static void transform(unsigned long state[4], unsigned char block[64]);
+
+static unsigned char padding[64] =
+{
+ 0x80, 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, 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
+};
+
+/* F, G, H and I are basic MD5 functions */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE rotates x left n bits */
+#define ROTATE(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+ * Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+ (a) += F ((b), (c), (d)) + (x) + (unsigned long)(ac); \
+ (a) = ROTATE ((a), (s)); \
+ (a) += (b); \
+ }
+#define GG(a, b, c, d, x, s, ac) { \
+ (a) += G ((b), (c), (d)) + (x) + (unsigned long)(ac); \
+ (a) = ROTATE ((a), (s)); \
+ (a) += (b); \
+ }
+#define HH(a, b, c, d, x, s, ac) { \
+ (a) += H ((b), (c), (d)) + (x) + (unsigned long)(ac); \
+ (a) = ROTATE ((a), (s)); \
+ (a) += (b); \
+ }
+#define II(a, b, c, d, x, s, ac) { \
+ (a) += I ((b), (c), (d)) + (x) + (unsigned long)(ac); \
+ (a) = ROTATE ((a), (s)); \
+ (a) += (b); \
+ }
+
+static void encode(unsigned char *output, unsigned long *input, unsigned len)
+{
+ unsigned i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4) {
+ output[j] = (unsigned char)(input[i] & 0xff);
+ output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+ output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+ output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+ }
+}
+
+static void decode(unsigned long *output, unsigned char *input, unsigned len)
+{
+ unsigned i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4) {
+ output[i] = ((unsigned long)input[j]) |
+ (((unsigned long)input[j+1]) << 8) |
+ (((unsigned long)input[j+2]) << 16) |
+ (((unsigned long)input[j+3]) << 24);
+ }
+}
+
+static void transform(unsigned long state[4], unsigned char block[64])
+{
+ unsigned long a = state[0];
+ unsigned long b = state[1];
+ unsigned long c = state[2];
+ unsigned long d = state[3];
+ unsigned long x[16];
+
+ decode(x, block, 64);
+
+ /* Round 1 */
+ FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
+ FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
+ FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
+ FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
+ FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
+ FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
+ FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
+ FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
+ FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
+ FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
+ FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+ FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+ FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+ FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+ FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+ FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+ GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
+ GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
+ GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+ GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
+ GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
+ GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
+ GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+ GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
+ GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
+ GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+ GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
+ GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
+ GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+ GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
+ GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
+ GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+ /* Round 3 */
+ HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
+ HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
+ HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+ HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+ HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
+ HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
+ HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
+ HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+ HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+ HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
+ HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
+ HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
+ HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
+ HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+ HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+ HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
+
+ /* Round 4 */
+ II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
+ II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
+ II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+ II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
+ II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+ II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
+ II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+ II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
+ II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
+ II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+ II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
+ II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+ II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
+ II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+ II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
+ II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+
+ /* Zeroize sensitive information */
+ memset(x, 0, sizeof (x));
+}
+
+/* MD5 initialization. Begins an MD5 operation, writing a new context. */
+void fz_md5init(fz_md5 *context)
+{
+ context->count[0] = context->count[1] = 0;
+
+ /* Load magic initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xefcdab89;
+ context->state[2] = 0x98badcfe;
+ context->state[3] = 0x10325476;
+}
+
+/* MD5 block update operation. Continues an MD5 message-digest operation,
+ * processing another message block, and updating the context.
+ */
+void fz_md5update(fz_md5 *context, unsigned char *input, unsigned inlen)
+{
+ unsigned i, index, partlen;
+
+ /* Compute number of bytes mod 64 */
+ index = (unsigned)((context->count[0] >> 3) & 0x3F);
+
+ /* Update number of bits */
+ context->count[0] += (unsigned long) inlen << 3;
+ if (context->count[0] < (unsigned long) inlen << 3)
+ context->count[1] ++;
+ context->count[1] += (unsigned long) inlen >> 29;
+
+ partlen = 64 - index;
+
+ /* Transform as many times as possible. */
+ if (inlen >= partlen) {
+ memcpy(context->buffer + index, input, partlen);
+ transform(context->state, context->buffer);
+
+ for (i = partlen; i + 63 < inlen; i += 64)
+ transform(context->state, input + i);
+
+ index = 0;
+ }
+ else {
+ i = 0;
+ }
+
+ /* Buffer remaining input */
+ memcpy(context->buffer + index, input + i, inlen - i);
+}
+
+/* MD5 finalization. Ends an MD5 message-digest operation, writing the
+ * the message digest and zeroizing the context.
+ */
+void fz_md5final(fz_md5 *context, unsigned char digest[16])
+{
+ unsigned char bits[8];
+ unsigned index, padlen;
+
+ /* Save number of bits */
+ encode(bits, context->count, 8);
+
+ /* Pad out to 56 mod 64 */
+ index = (unsigned)((context->count[0] >> 3) & 0x3f);
+ padlen = index < 56 ? 56 - index : 120 - index;
+ fz_md5update(context, padding, padlen);
+
+ /* Append length (before padding) */
+ fz_md5update(context, bits, 8);
+
+ /* Store state in digest */
+ encode(digest, context->state, 16);
+
+ /* Zeroize sensitive information */
+ memset(context, 0, sizeof(fz_md5));
+}
+
diff --git a/base/memory.c b/base/memory.c
new file mode 100644
index 00000000..88102d00
--- /dev/null
+++ b/base/memory.c
@@ -0,0 +1,81 @@
+#include <fitz.h>
+
+/* Make this thread local storage if you wish. */
+
+static void *stdmalloc(fz_memorycontext *mem, int n)
+{
+#if 0
+ void *p = malloc(n);
+ if (!p)
+ fprintf(stderr, "failed to malloc %d bytes\n", n);
+ return p;
+#else
+ return malloc(n);
+#endif
+}
+
+static void *stdrealloc(fz_memorycontext *mem, void *p, int n)
+{
+#if 0
+ void *np = realloc(p, n);
+ if (np == nil)
+ fprintf(stderr, "realloc failed %d nytes", n);
+ else if (np == p)
+ fprintf(stderr, "realloc kept %d\n", n);
+ else
+ fprintf(stderr, "realloc moved %d\n", n);
+ return np;
+#else
+ return realloc(p, n);
+#endif
+}
+
+static void stdfree(fz_memorycontext *mem, void *p)
+{
+ free(p);
+}
+
+static fz_memorycontext defmem = { stdmalloc, stdrealloc, stdfree };
+static fz_memorycontext *curmem = &defmem;
+
+fz_error fz_koutofmem = {
+ .msg = {"out of memory"},
+ .func = {"<malloc>"},
+ .file = {"memory.c"},
+ .line = 0,
+ .frozen = 1
+};
+
+fz_memorycontext *
+fz_currentmemorycontext()
+{
+ return curmem;
+}
+
+void
+fz_setmemorycontext(fz_memorycontext *mem)
+{
+ curmem = mem;
+}
+
+void *
+fz_malloc(int n)
+{
+ fz_memorycontext *mem = fz_currentmemorycontext();
+ return mem->malloc(mem, n);
+}
+
+void *
+fz_realloc(void *p, int n)
+{
+ fz_memorycontext *mem = fz_currentmemorycontext();
+ return mem->realloc(mem, p, n);
+}
+
+void
+fz_free(void *p)
+{
+ fz_memorycontext *mem = fz_currentmemorycontext();
+ mem->free(mem, p);
+}
+
diff --git a/base/rect.c b/base/rect.c
new file mode 100644
index 00000000..cb15d434
--- /dev/null
+++ b/base/rect.c
@@ -0,0 +1,46 @@
+#include <fitz.h>
+
+fz_rect
+fz_intersectrects(fz_rect a, fz_rect b)
+{
+ fz_rect r;
+ r.min.x = MAX(a.min.x, b.min.x);
+ r.min.y = MAX(a.min.y, b.min.y);
+ r.max.x = MIN(a.max.x, b.max.x);
+ r.max.y = MIN(a.max.y, b.max.y);
+ return r;
+}
+
+fz_rect
+fz_mergerects(fz_rect a, fz_rect b)
+{
+ fz_rect r;
+ r.min.x = MIN(a.min.x, b.min.x);
+ r.min.y = MIN(a.min.y, b.min.y);
+ r.max.x = MAX(a.max.x, b.max.x);
+ r.max.y = MAX(a.max.y, b.max.y);
+ return r;
+}
+
+fz_irect
+fz_intersectirects(fz_irect a, fz_irect b)
+{
+ fz_irect r;
+ r.min.x = MAX(a.min.x, b.min.x);
+ r.min.y = MAX(a.min.y, b.min.y);
+ r.max.x = MIN(a.max.x, b.max.x);
+ r.max.y = MIN(a.max.y, b.max.y);
+ return r;
+}
+
+fz_irect
+fz_mergeirects(fz_irect a, fz_irect b)
+{
+ fz_irect r;
+ r.min.x = MIN(a.min.x, b.min.x);
+ r.min.y = MIN(a.min.y, b.min.y);
+ r.max.x = MAX(a.max.x, b.max.x);
+ r.max.y = MAX(a.max.y, b.max.y);
+ return r;
+}
+
diff --git a/filter/TODO b/filter/TODO
new file mode 100644
index 00000000..404d76c2
--- /dev/null
+++ b/filter/TODO
@@ -0,0 +1,8 @@
+validate ahxd pushback
+go through eof responsibility
+be more defensive of api user errors
+flate needin/needout
+
+jbig2 rewrite
+dctencode params
+
diff --git a/filter/a85d.c b/filter/a85d.c
new file mode 100644
index 00000000..08c96a1b
--- /dev/null
+++ b/filter/a85d.c
@@ -0,0 +1,129 @@
+#include <fitz.h>
+
+typedef struct fz_a85d_s fz_a85d;
+
+struct fz_a85d_s
+{
+ fz_filter super;
+ unsigned long word;
+ int count;
+};
+
+static inline int iswhite(int a)
+{
+ switch (a) {
+ case '\n': case '\r': case '\t': case ' ':
+ case '\0': case '\f': case '\b': case 0177:
+ return 1;
+ }
+ return 0;
+}
+
+fz_error *
+fz_newa85d(fz_filter **fp, fz_obj *params)
+{
+ FZ_NEWFILTER(fz_a85d, f, a85d);
+ f->word = 0;
+ f->count = 0;
+ return nil;
+}
+
+void
+fz_freea85d(fz_filter *f)
+{
+ fz_free(f);
+}
+
+fz_error *
+fz_processa85d(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_a85d *f = (fz_a85d*)filter;
+ int c;
+
+ while (1)
+ {
+ if (in->rp == in->wp)
+ return fz_ioneedin;
+
+ c = *in->rp++;
+
+ if (c >= '!' && c <= 'u') {
+ if (f->count == 4) {
+ if (out->wp + 4 > out->ep) {
+ in->rp --;
+ return fz_ioneedout;
+ }
+
+ f->word = f->word * 85 + (c - '!');
+
+ *out->wp++ = (f->word >> 24) & 0xff;
+ *out->wp++ = (f->word >> 16) & 0xff;
+ *out->wp++ = (f->word >> 8) & 0xff;
+ *out->wp++ = (f->word) & 0xff;
+
+ f->word = 0;
+ f->count = 0;
+ }
+ else {
+ f->word = f->word * 85 + (c - '!');
+ f->count ++;
+ }
+ }
+
+ else if (c == 'z' && f->count == 0) {
+ if (out->wp + 4 > out->ep) {
+ in->rp --;
+ return fz_ioneedout;
+ }
+ *out->wp++ = 0;
+ *out->wp++ = 0;
+ *out->wp++ = 0;
+ *out->wp++ = 0;
+ }
+
+ else if (c == '~') {
+ if (in->rp == in->wp) {
+ in->rp --;
+ return fz_ioneedin;
+ }
+
+ c = *in->rp++;
+
+ if (c != '>') {
+ return fz_throw("ioerror: bad eod marker in a85d");
+ }
+
+ if (out->wp + f->count - 1 > out->ep) {
+ in->rp -= 2;
+ return fz_ioneedout;
+ }
+
+ switch (f->count) {
+ case 0:
+ break;
+ case 1:
+ return fz_throw("ioerror: partial final byte in a85d");
+ case 2:
+ f->word = f->word * (85L * 85 * 85) + 0xffffffL;
+ goto o1;
+ case 3:
+ f->word = f->word * (85L * 85) + 0xffffL;
+ goto o2;
+ case 4:
+ f->word = f->word * 85 + 0xffL;
+ *(out->wp+2) = f->word >> 8;
+o2: *(out->wp+1) = f->word >> 16;
+o1: *(out->wp+0) = f->word >> 24;
+ out->wp += f->count - 1;
+ break;
+ }
+ out->eof = 1;
+ return fz_iodone;
+ }
+
+ else if (!iswhite(c)) {
+ return fz_throw("ioerror: bad data in a85d: '%c'", c);
+ }
+ }
+}
+
diff --git a/filter/a85e.c b/filter/a85e.c
new file mode 100644
index 00000000..c57aac4f
--- /dev/null
+++ b/filter/a85e.c
@@ -0,0 +1,128 @@
+#include <fitz.h>
+
+typedef struct fz_a85e_s fz_a85e;
+
+struct fz_a85e_s
+{
+ fz_filter super;
+ int c;
+};
+
+fz_error *
+fz_newa85e(fz_filter **fp, fz_obj *params)
+{
+ FZ_NEWFILTER(fz_a85e, f, a85e);
+ f->c = 0;
+ return nil;
+}
+
+void
+fz_freea85e(fz_filter *f)
+{
+ fz_free(f);
+}
+
+fz_error *
+fz_processa85e(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_a85e *f = (fz_a85e*)filter;
+ unsigned long word;
+ int count;
+ int n;
+
+ n = 0;
+
+ while (1)
+ {
+ if (f->c >= 70) {
+ if (out->wp + 1 > out->ep)
+ return fz_ioneedout;
+ *out->wp++ = '\n';
+ f->c = 0;
+ n ++;
+ }
+
+ if (in->rp + 4 <= in->wp)
+ {
+ word = (in->rp[0] << 24) |
+ (in->rp[1] << 16) |
+ (in->rp[2] << 8) |
+ (in->rp[3]);
+ if (word == 0) {
+ if (out->wp + 1 > out->ep)
+ return fz_ioneedout;
+ *out->wp++ = 'z';
+ f->c ++;
+ n ++;
+ }
+ else {
+ unsigned long v1, v2, v3, v4;
+
+ if (out->wp + 5 > out->ep)
+ return fz_ioneedout;
+
+ v4 = word / 85;
+ v3 = v4 / 85;
+ v2 = v3 / 85;
+ v1 = v2 / 85;
+
+ *out->wp++ = (v1 % 85) + '!';
+ *out->wp++ = (v2 % 85) + '!';
+ *out->wp++ = (v3 % 85) + '!';
+ *out->wp++ = (v4 % 85) + '!';
+ *out->wp++ = (word % 85) + '!';
+ f->c += 5;
+ n += 5;
+ }
+ in->rp += 4;
+ }
+
+ else if (in->eof)
+ {
+ unsigned long divisor;
+
+ if (in->rp == in->wp)
+ goto needinput; /* handle clean eof here */
+
+ count = in->wp - in->rp;
+
+ if (out->wp + count + 3 > out->ep)
+ return fz_ioneedout;
+
+ word = 0;
+ switch (count) {
+ case 3: word |= in->rp[2] << 8;
+ case 2: word |= in->rp[1] << 16;
+ case 1: word |= in->rp[0] << 24;
+ }
+ in->rp += count;
+
+ divisor = 85L * 85 * 85 * 85;
+ while (count-- >= 0) {
+ *out->wp++ = ((word / divisor) % 85) + '!';
+ divisor /= 85;
+ }
+
+ *out->wp++ = '~';
+ *out->wp++ = '>';
+ out->eof = 1;
+ return fz_iodone;
+ }
+
+ else {
+ goto needinput;
+ }
+ }
+
+needinput:
+ if (in->eof) {
+ if (out->wp + 2 > out->ep)
+ return fz_ioneedout;
+ *out->wp++ = '~';
+ *out->wp++ = '>';
+ out->eof = 1;
+ return fz_iodone;
+ }
+ return fz_ioneedin;
+}
+
diff --git a/filter/ahxd.c b/filter/ahxd.c
new file mode 100644
index 00000000..b557399c
--- /dev/null
+++ b/filter/ahxd.c
@@ -0,0 +1,113 @@
+#include <fitz.h>
+
+typedef struct fz_ahxd_s fz_ahxd;
+
+struct fz_ahxd_s
+{
+ fz_filter super;
+ int odd;
+ int a;
+};
+
+static inline int iswhite(int a)
+{
+ switch (a) {
+ case '\n': case '\r': case '\t': case ' ':
+ case '\0': case '\f': case '\b': case 0177:
+ return 1;
+ }
+ return 0;
+}
+
+static inline int ishex(int a)
+{
+ return (a >= 'A' && a <= 'F') ||
+ (a >= 'a' && a <= 'f') ||
+ (a >= '0' && a <= '9');
+}
+
+static inline int fromhex(int a)
+{
+ if (a >= 'A' && a <= 'F')
+ return a - 'A' + 0xA;
+ if (a >= 'a' && a <= 'f')
+ return a - 'a' + 0xA;
+ if (a >= '0' && a <= '9')
+ return a - '0';
+ return 0;
+}
+
+fz_error *
+fz_newahxd(fz_filter **fp, fz_obj *params)
+{
+ FZ_NEWFILTER(fz_ahxd, f, ahxd);
+ f->odd = 0;
+ f->a = 0;
+ return nil;
+}
+
+void
+fz_freeahxd(fz_filter *f)
+{
+ fz_free(f);
+}
+
+fz_error *
+fz_processahxd(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_ahxd *f = (fz_ahxd*)filter;
+ int b, c;
+
+ while (1)
+ {
+ if (in->rp == in->wp)
+ return fz_ioneedin;
+
+ if (out->wp == out->ep)
+ return fz_ioneedout;
+
+ c = *in->rp++;
+
+ if (ishex(c)) {
+ if (!f->odd) {
+ f->a = fromhex(c);
+ f->odd = 1;
+ }
+ else {
+ b = fromhex(c);
+ *out->wp++ = (f->a << 4) | b;
+ f->odd = 0;
+ }
+ }
+
+ else if (c == '>') {
+ if (f->odd)
+ *out->wp++ = (f->a << 4);
+ out->eof = 1;
+ return fz_iodone;
+ }
+
+ else if (!iswhite(c)) {
+ return fz_throw("ioerror: bad data in ahxd: '%c'", c);
+ }
+ }
+}
+
+void
+fz_pushbackahxd(fz_filter *filter, fz_buffer *in, fz_buffer *out, int n)
+{
+ int k;
+
+ assert(filter->process == fz_processahxd);
+ assert(out->wp - n >= out->rp);
+
+ k = 0;
+ while (k < n * 2) {
+ in->rp --;
+ if (ishex(*in->rp))
+ k ++;
+ }
+
+ out->wp -= n;
+}
+
diff --git a/filter/ahxe.c b/filter/ahxe.c
new file mode 100644
index 00000000..e092653b
--- /dev/null
+++ b/filter/ahxe.c
@@ -0,0 +1,65 @@
+#include <fitz.h>
+
+typedef struct fz_ahxe_s fz_ahxe;
+
+struct fz_ahxe_s
+{
+ fz_filter super;
+ int c;
+};
+
+static const char tohex[16] = "0123456789ABCDEF";
+
+fz_error *
+fz_newahxe(fz_filter **fp, fz_obj *params)
+{
+ FZ_NEWFILTER(fz_ahxe, f, ahxe);
+ f->c = 0;
+ return nil;
+}
+
+void
+fz_freeahxe(fz_filter *f)
+{
+ fz_free(f);
+}
+
+fz_error *
+fz_processahxe(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_ahxe *f = (fz_ahxe*)filter;
+ int a, b, c;
+
+ while (1)
+ {
+ if (in->rp == in->wp)
+ goto needinput;
+
+ if (out->wp + 2 >= out->ep) /* can write 3 bytes from 1 */
+ return fz_ioneedout;
+
+ c = *in->rp++;
+ a = tohex[(c >> 4) & 0x0f];
+ b = tohex[c & 0x0f];
+
+ *out->wp++ = a;
+ *out->wp++ = b;
+
+ f->c += 2;
+ if (f->c == 60) {
+ *out->wp++ = '\n';
+ f->c = 0;
+ }
+ }
+
+needinput:
+ if (in->eof) {
+ if (out->wp == out->ep)
+ return fz_ioneedout;
+ *out->wp++ = '>';
+ out->eof = 1;
+ return fz_iodone;
+ }
+ return fz_ioneedin;
+}
+
diff --git a/filter/arc4filter.c b/filter/arc4filter.c
new file mode 100644
index 00000000..257462d8
--- /dev/null
+++ b/filter/arc4filter.c
@@ -0,0 +1,47 @@
+#include <fitz.h>
+
+typedef struct fz_arc4c_s fz_arc4c;
+
+struct fz_arc4c_s
+{
+ fz_filter super;
+ fz_arc4 arc4;
+};
+
+fz_error *
+fz_newarc4filter(fz_filter **fp, unsigned char *key, unsigned keylen)
+{
+ FZ_NEWFILTER(fz_arc4c, f, arc4filter);
+ fz_arc4init(&f->arc4, key, keylen);
+ return nil;
+}
+
+void
+fz_freearc4filter(fz_filter *f)
+{
+ fz_free(f);
+}
+
+fz_error *
+fz_processarc4filter(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_arc4c *f = (fz_arc4c*)filter;
+ int n;
+
+ while (1)
+ {
+ if (in->rp + 1 > in->wp) {
+ if (in->eof)
+ return fz_iodone;
+ return fz_ioneedin;
+ }
+ if (out->wp + 1 > out->ep)
+ return fz_ioneedout;
+
+ n = MIN(in->wp - in->rp, out->ep - out->wp);
+ fz_arc4encrypt(&f->arc4, out->wp, in->rp, n);
+ in->rp += n;
+ out->wp += n;
+ }
+}
+
diff --git a/filter/buffer.c b/filter/buffer.c
new file mode 100644
index 00000000..7b728b9b
--- /dev/null
+++ b/filter/buffer.c
@@ -0,0 +1,57 @@
+#include <fitz.h>
+
+fz_error *
+fz_newbuffer(fz_buffer **bp, int size)
+{
+ fz_buffer *b;
+
+ b = *bp = fz_malloc(sizeof(fz_buffer));
+ if (!b) return fz_outofmem;
+
+ b->bp = fz_malloc(size);
+ if (!b->bp) { fz_free(b); return fz_outofmem; }
+
+ b->rp = b->bp;
+ b->wp = b->bp;
+ b->ep = b->bp + size;
+ b->eof = 0;
+
+ return nil;
+}
+
+void
+fz_freebuffer(fz_buffer *buf)
+{
+ fz_free(buf->bp);
+ fz_free(buf);
+}
+
+fz_error *
+fz_growbuffer(fz_buffer *buf)
+{
+ unsigned char *newbp;
+
+ int rp = buf->rp - buf->bp;
+ int wp = buf->wp - buf->bp;
+ int ep = buf->ep - buf->bp;
+
+ newbp = fz_realloc(buf->bp, ep * 2);
+ if (!newbp) return fz_outofmem;
+
+ buf->bp = newbp;
+ buf->rp = buf->bp + rp;
+ buf->wp = buf->bp + wp;
+ buf->ep = buf->bp + ep * 2;
+
+ return nil;
+}
+
+fz_error *
+fz_rewindbuffer(fz_buffer *buf)
+{
+ memmove(buf->bp, buf->rp, buf->wp - buf->rp);
+ buf->wp = buf->bp + (buf->wp - buf->rp);
+ buf->rp = buf->bp;
+ return nil;
+}
+
diff --git a/filter/dctc.h b/filter/dctc.h
new file mode 100644
index 00000000..8aa6aeb7
--- /dev/null
+++ b/filter/dctc.h
@@ -0,0 +1,39 @@
+/*
+ * Extend libjpegs error handler to use setjmp/longjmp
+ */
+
+#include <jpeglib.h>
+
+#include <setjmp.h>
+
+struct myerrmgr
+{
+ struct jpeg_error_mgr super;
+ jmp_buf jb;
+ char msg[JMSG_LENGTH_MAX];
+};
+
+static void myerrexit(j_common_ptr cinfo)
+{
+ struct myerrmgr *err = (struct myerrmgr *)cinfo->err;
+ char msgbuf[JMSG_LENGTH_MAX];
+ err->super.format_message(cinfo, msgbuf);
+ strlcpy(err->msg, msgbuf, sizeof err->msg);
+ longjmp(err->jb, 1);
+}
+
+static void myoutmess(j_common_ptr cinfo)
+{
+ struct myerrmgr *err = (struct myerrmgr *)cinfo->err;
+ char msgbuf[JMSG_LENGTH_MAX];
+ err->super.format_message(cinfo, msgbuf);
+ fprintf(stderr, "ioerror: dct: %s", msgbuf);
+}
+
+static void myiniterr(struct myerrmgr *err)
+{
+ jpeg_std_error(&err->super);
+ err->super.error_exit = myerrexit;
+ err->super.output_message = myoutmess;
+}
+
diff --git a/filter/dctd.c b/filter/dctd.c
new file mode 100644
index 00000000..dd978ac7
--- /dev/null
+++ b/filter/dctd.c
@@ -0,0 +1,215 @@
+#include <fitz.h>
+
+#include "dctc.h"
+
+typedef struct fz_dctd_s fz_dctd;
+
+struct mysrcmgr
+{
+ struct jpeg_source_mgr super;
+ fz_buffer *buf;
+ int skip;
+};
+
+struct fz_dctd_s
+{
+ fz_filter super;
+ struct jpeg_decompress_struct cinfo;
+ struct mysrcmgr src;
+ struct myerrmgr err;
+ int colortransform;
+ int stage;
+};
+
+static void myinitsource(j_decompress_ptr cinfo) { /* empty */ }
+static boolean myfillinput(j_decompress_ptr cinfo) { return FALSE; }
+static void mytermsource(j_decompress_ptr cinfo) { /* empty */ }
+
+static void myskipinput(j_decompress_ptr cinfo, long n)
+{
+ struct mysrcmgr *src = (struct mysrcmgr *)cinfo->src;
+ fz_buffer *in = src->buf;
+
+ assert(src->skip == 0);
+
+ in->rp = in->wp - src->super.bytes_in_buffer;
+
+ if (in->rp + n > in->wp) {
+ src->skip = (in->rp + n) - in->wp;
+ in->rp = in->wp;
+ }
+ else {
+ src->skip = 0;
+ in->rp += n;
+ }
+
+ src->super.bytes_in_buffer = in->wp - in->rp;
+ src->super.next_input_byte = in->rp;
+}
+
+fz_error *
+fz_newdctd(fz_filter **fp, fz_obj *params)
+{
+ fz_error *err;
+ fz_obj *obj;
+ int colortransform;
+
+ FZ_NEWFILTER(fz_dctd, d, dctd);
+
+ colortransform = 1;
+
+ if (params) {
+ obj = fz_dictgets(params, "ColorTransform");
+ if (obj) colortransform = fz_toint(obj);
+ }
+
+ d->colortransform = colortransform;
+ d->stage = 0;
+
+ /* setup error callback first thing */
+ myiniterr(&d->err);
+ d->cinfo.err = (struct jpeg_error_mgr*) &d->err;
+
+ if (setjmp(d->err.jb)) {
+ err = fz_throw("ioerror in dctd: %s", d->err.msg);
+ fz_free(d);
+ return err;
+ }
+
+ /* create decompression object. this zeroes cinfo except for err. */
+ jpeg_create_decompress(&d->cinfo);
+
+ /* prepare source manager */
+ d->cinfo.src = (struct jpeg_source_mgr *)&d->src;
+ d->src.super.init_source = myinitsource;
+ d->src.super.fill_input_buffer = myfillinput;
+ d->src.super.skip_input_data = myskipinput;
+ d->src.super.resync_to_restart = jpeg_resync_to_restart;
+ d->src.super.term_source = mytermsource;
+
+ d->src.super.bytes_in_buffer = 0;
+ d->src.super.next_input_byte = nil;
+ d->src.skip = 0;
+
+ return nil;
+}
+
+void
+fz_freedctd(fz_filter *filter)
+{
+ fz_dctd *d = (fz_dctd*)filter;
+ if (setjmp(d->err.jb)) {
+ fprintf(stderr, "ioerror in dct: jpeg_destroy_decompress: %s", d->err.msg);
+ return;
+ }
+ jpeg_destroy_decompress(&d->cinfo);
+}
+
+fz_error *
+fz_processdctd(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_dctd *d = (fz_dctd*)filter;
+ boolean b;
+ int i;
+ int stride;
+ JSAMPROW scanlines[1];
+
+ d->src.buf = in;
+
+ /* skip any bytes left over from myskipinput() */
+ if (d->src.skip > 0) {
+ if (in->rp + d->src.skip > in->wp) {
+ d->src.skip = (in->rp + d->src.skip) - in->wp;
+ in->rp = in->wp;
+ goto needinput;
+ }
+ else {
+ in->rp += d->src.skip;
+ d->src.skip = 0;
+ }
+ }
+
+ d->src.super.bytes_in_buffer = in->wp - in->rp;
+ d->src.super.next_input_byte = in->rp;
+
+ if (setjmp(d->err.jb)) {
+ return fz_throw("ioerror in dctd: %s", d->err.msg);
+ }
+
+ switch (d->stage)
+ {
+ case 0:
+ i = jpeg_read_header(&d->cinfo, TRUE);
+ if (i == JPEG_SUSPENDED)
+ goto needinput;
+
+ /* FIXME: default value if ColorTransform is not set */
+
+ if (!d->cinfo.saw_Adobe_marker) {
+ switch (d->cinfo.num_components) {
+ case 3:
+ if (d->colortransform)
+ d->cinfo.jpeg_color_space = JCS_YCbCr;
+ else
+ d->cinfo.jpeg_color_space = JCS_RGB;
+ break;
+ case 4:
+ if (d->colortransform)
+ d->cinfo.jpeg_color_space = JCS_YCCK;
+ else
+ d->cinfo.jpeg_color_space = JCS_CMYK;
+ break;
+ }
+ }
+
+ /* fall through */
+ d->stage = 1;
+
+ case 1:
+ b = jpeg_start_decompress(&d->cinfo);
+ if (b == FALSE)
+ goto needinput;
+
+ /* fall through */
+ d->stage = 2;
+
+ case 2:
+ stride = d->cinfo.output_width * d->cinfo.output_components;
+
+ while (d->cinfo.output_scanline < d->cinfo.output_height)
+ {
+ if (out->wp + stride > out->ep)
+ goto needoutput;
+
+ scanlines[0] = out->wp;
+
+ i = jpeg_read_scanlines(&d->cinfo, scanlines, 1);
+
+ if (i == 0)
+ goto needinput;
+
+ out->wp += stride;
+ }
+
+ /* fall through */
+ d->stage = 3;
+
+ case 3:
+ b = jpeg_finish_decompress(&d->cinfo);
+ if (b == FALSE)
+ goto needinput;
+ d->stage = 4;
+ out->eof = 1;
+ in->rp = in->wp - d->src.super.bytes_in_buffer;
+ return fz_iodone;
+ }
+
+needinput:
+ in->rp = in->wp - d->src.super.bytes_in_buffer;
+ return fz_ioneedin;
+
+needoutput:
+ in->rp = in->wp - d->src.super.bytes_in_buffer;
+ return fz_ioneedout;
+}
+
diff --git a/filter/dcte.c b/filter/dcte.c
new file mode 100644
index 00000000..52933789
--- /dev/null
+++ b/filter/dcte.c
@@ -0,0 +1,254 @@
+#include <fitz.h>
+
+#include "dctc.h"
+
+typedef struct fz_dcte_s fz_dcte;
+
+struct mydstmgr
+{
+ struct jpeg_destination_mgr super;
+ fz_buffer *buf;
+};
+
+struct fz_dcte_s
+{
+ fz_filter super;
+
+ struct jpeg_compress_struct cinfo;
+ struct mydstmgr dst;
+ struct myerrmgr err;
+ int stage;
+
+ int columns;
+ int rows;
+ int colors;
+};
+
+static void myinitdest(j_compress_ptr cinfo) { /* empty */ }
+static boolean myemptybuf(j_compress_ptr cinfo) { return FALSE; }
+static void mytermdest(j_compress_ptr cinfo) { /* empty */ }
+
+fz_error *
+fz_newdcte(fz_filter **fp, fz_obj *params)
+{
+ fz_error *err;
+ fz_obj *obj;
+ int i;
+
+ FZ_NEWFILTER(fz_dcte, e, dcte);
+
+ e->stage = 0;
+
+ obj = fz_dictgets(params, "Columns");
+ if (!obj) { fz_free(e); return fz_throw("ioerror in dcte: missing Columns parameter"); }
+ e->columns = fz_toint(obj);
+
+ obj = fz_dictgets(params, "Rows");
+ if (!obj) { fz_free(e); return fz_throw("ioerror in dcte: missing Rows parameter"); }
+ e->rows = fz_toint(obj);
+
+ obj = fz_dictgets(params, "Colors");
+ if (!obj) { fz_free(e); return fz_throw("ioerror in dcte: missing Colors parameter"); }
+ e->colors = fz_toint(obj);
+
+ /* setup error callback first thing */
+ myiniterr(&e->err);
+ e->cinfo.err = (struct jpeg_error_mgr*) &e->err;
+
+ if (setjmp(e->err.jb)) {
+ err = fz_throw("ioerror in dcte: %s", e->err.msg);
+ fz_free(e);
+ return err;
+ }
+
+ jpeg_create_compress(&e->cinfo);
+
+ /* prepare destination manager */
+ e->cinfo.dest = (struct jpeg_destination_mgr *) &e->dst;
+ e->dst.super.init_destination = myinitdest;
+ e->dst.super.empty_output_buffer = myemptybuf;
+ e->dst.super.term_destination = mytermdest;
+
+ e->dst.super.next_output_byte = nil;
+ e->dst.super.free_in_buffer = 0;
+
+ /* prepare required encoding options */
+ e->cinfo.image_width = e->columns;
+ e->cinfo.image_height = e->rows;
+ e->cinfo.input_components = e->colors;
+
+ switch (e->colors) {
+ case 1: e->cinfo.in_color_space = JCS_GRAYSCALE; break;
+ case 3: e->cinfo.in_color_space = JCS_RGB; break;
+ case 4: e->cinfo.in_color_space = JCS_CMYK; break;
+ default: e->cinfo.in_color_space = JCS_UNKNOWN; break;
+ }
+
+ jpeg_set_defaults(&e->cinfo);
+
+ /* FIXME check this */
+ obj = fz_dictgets(params, "ColorTransform");
+ if (obj) {
+ int colortransform = fz_toint(obj);
+ if (e->colors == 3 && !colortransform)
+ jpeg_set_colorspace(&e->cinfo, JCS_RGB);
+ if (e->colors == 4 && colortransform)
+ jpeg_set_colorspace(&e->cinfo, JCS_YCCK);
+ if (e->colors == 4 && !colortransform)
+ jpeg_set_colorspace(&e->cinfo, JCS_CMYK);
+ }
+
+ obj = fz_dictgets(params, "HSamples");
+ if (obj && fz_isarray(obj)) {
+ fz_obj *o;
+ for (i = 0; i < e->colors; i++) {
+ o = fz_arrayget(obj, i);
+ e->cinfo.comp_info[i].h_samp_factor = fz_toint(o);
+ }
+ }
+
+ obj = fz_dictgets(params, "VSamples");
+ if (obj && fz_isarray(obj)) {
+ fz_obj *o;
+ for (i = 0; i < e->colors; i++) {
+ o = fz_arrayget(obj, i);
+ e->cinfo.comp_info[i].v_samp_factor = fz_toint(o);
+ }
+ }
+
+ /* TODO: quant-tables and huffman-tables */
+
+ return nil;
+}
+
+void
+fz_freedcte(fz_filter *filter)
+{
+ fz_dcte *e = (fz_dcte*)filter;
+
+ if (setjmp(e->err.jb)) {
+ fprintf(stderr, "ioerror in dcte: jpeg_destroy_compress: %s", e->err.msg);
+ return;
+ }
+
+ jpeg_destroy_compress(&e->cinfo);
+
+ fz_free(e);
+}
+
+/* Adobe says zigzag order. IJG > v6a says natural order. */
+#if JPEG_LIB_VERSION >= 61
+#define unzigzag(x) unzigzagorder[x]
+/* zigzag array position of n'th element of natural array order */
+static const unsigned char unzigzagorder[] =
+{
+ 0, 1, 5, 6, 14, 15, 27, 28,
+ 2, 4, 7, 13, 16, 26, 29, 42,
+ 3, 8, 12, 17, 25, 30, 41, 43,
+ 9, 11, 18, 24, 31, 40, 44, 53,
+ 10, 19, 23, 32, 39, 45, 52, 54,
+ 20, 22, 33, 38, 46, 51, 55, 60,
+ 21, 34, 37, 47, 50, 56, 59, 61,
+ 35, 36, 48, 49, 57, 58, 62, 63
+};
+#else
+#define unzigzag(x) (x)
+#endif
+
+fz_error *
+fz_setquanttables(fz_dcte *e, unsigned int **qtables, int qfactor)
+{
+ int i, j;
+ unsigned int table[64];
+
+ if (setjmp(e->err.jb)) {
+ return fz_throw("ioerror in dcte: %s", e->err.msg);
+ }
+
+ /* TODO: check for duplicate tables */
+
+ for (i = 0; i < e->colors; i++) {
+ for (j = 0; j < 64; j++) {
+ table[j] = unzigzag(qtables[i][j]);
+ }
+ jpeg_add_quant_table(&e->cinfo, i, table, qfactor, TRUE);
+ e->cinfo.comp_info[i].quant_tbl_no = i;
+ }
+
+ return nil;
+}
+
+fz_error *
+fz_processdcte(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_dcte *e = (fz_dcte*)filter;
+ JSAMPROW scanline[1];
+ int stride;
+ int i;
+
+ e->dst.buf = out;
+ e->dst.super.free_in_buffer = out->ep - out->wp;
+ e->dst.super.next_output_byte = out->wp;
+
+ if (setjmp(e->err.jb)) {
+ return fz_throw("ioerror in dcte: %s", e->err.msg);
+ }
+
+ switch (e->stage)
+ {
+ case 0:
+ /* must have enough space for markers, typically 600 bytes or so */
+ if (out->wp + 1024 > out->ep)
+ goto needoutput;
+
+ jpeg_start_compress(&e->cinfo, TRUE);
+
+ /* TODO: write Adobe ColorTransform marker */
+
+ /* fall through */
+ e->stage = 1;
+
+ case 1:
+ stride = e->columns * e->colors;
+
+ while (e->cinfo.next_scanline < e->cinfo.image_height)
+ {
+ if (in->rp + stride > in->wp)
+ goto needinput;
+
+ scanline[0] = in->rp;
+
+ i = jpeg_write_scanlines(&e->cinfo, scanline, 1);
+
+ if (i == 0)
+ goto needoutput;
+
+ in->rp += stride;
+ }
+
+ /* fall through */
+ e->stage = 2;
+
+ case 2:
+ /* must have enough space for end markers */
+ if (out->wp + 100 > out->ep)
+ goto needoutput;
+
+ /* finish compress cannot suspend! */
+ jpeg_finish_compress(&e->cinfo);
+
+ e->stage = 3;
+ out->eof = 1;
+ out->wp = out->ep - e->dst.super.free_in_buffer;
+ return fz_iodone;
+ }
+
+needinput:
+ out->wp = out->ep - e->dst.super.free_in_buffer;
+ return fz_ioneedin;
+
+needoutput:
+ out->wp = out->ep - e->dst.super.free_in_buffer;
+ return fz_ioneedout;
+}
+
diff --git a/filter/faxc.h b/filter/faxc.h
new file mode 100644
index 00000000..caf29df0
--- /dev/null
+++ b/filter/faxc.h
@@ -0,0 +1,116 @@
+/* common bit magic */
+
+static inline void
+printbits(FILE *f, int code, int nbits)
+{
+ int n, b;
+ for (n = nbits - 1; n >= 0; n--) {
+ b = (code >> n) & 1;
+ fprintf(f, "%c", b ? '1' : '0');
+ }
+}
+
+static inline int
+getbit(const unsigned char *buf, int x)
+{
+ return ( buf[x >> 3] >> ( 7 - (x & 7) ) ) & 1;
+}
+
+static inline void
+printline(FILE *f, unsigned char *line, int w)
+{
+ int i;
+ for (i = 0; i < w; i++)
+ fprintf(f, "%c", getbit(line, i) ? '#' : '.');
+ fprintf(f, "\n");
+}
+
+static inline int
+getrun(const unsigned char *line, int x, int w, int c)
+{
+ int z;
+ int b;
+
+ if (x < 0)
+ x = 0;
+
+ z = x;
+ while (z < w) {
+ b = getbit(line, z);
+ if (c != b)
+ break;
+ z ++;
+ }
+
+ return z - x;
+}
+
+static inline int
+findchanging(const unsigned char *line, int x, int w)
+{
+ int a, b;
+
+ if (line == 0)
+ return w;
+
+ if (x == -1) {
+ a = 0;
+ x = 0;
+ }
+ else {
+ a = getbit(line, x);
+ x++;
+ }
+
+ while (x < w) {
+ b = getbit(line, x);
+ if (a != b)
+ break;
+ x++;
+ }
+
+ return x;
+}
+
+static inline int
+findchangingcolor(const unsigned char *line, int x, int w, int color)
+{
+ if (line == 0)
+ return w;
+
+ x = findchanging(line, x, w);
+
+ if (x < w && getbit(line, x) != color)
+ x = findchanging(line, x, w);
+
+ return x;
+}
+
+static const unsigned char lm[8] =
+ { 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01 };
+
+static const unsigned char rm[8] =
+ { 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE };
+
+static inline void
+setbits(unsigned char *line, int x0, int x1)
+{
+ int a0, a1, b0, b1, a;
+
+ a0 = x0 >> 3;
+ a1 = x1 >> 3;
+
+ b0 = x0 & 7;
+ b1 = x1 & 7;
+
+ if (a0 == a1) {
+ line[a0] |= lm[b0] & rm[b1];
+ }
+ else {
+ line[a0] |= lm[b0];
+ for (a = a0 + 1; a < a1; a++)
+ line[a] = 0xFF;
+ line[a1] |= rm[b1];
+ }
+}
+
diff --git a/filter/faxd.c b/filter/faxd.c
new file mode 100644
index 00000000..1b86c17b
--- /dev/null
+++ b/filter/faxd.c
@@ -0,0 +1,463 @@
+#include <fitz.h>
+
+#include "faxd.h"
+#include "faxc.h"
+
+enum
+{
+ SNORMAL, /* neutral state, waiting for any code */
+ SMAKEUP, /* got a 1d makeup code, waiting for terminating code */
+ SEOL, /* at eol, needs output buffer space */
+ SH1, SH2 /* in H part 1 and 2 (both makeup and terminating codes) */
+};
+
+#define DEBUG 1
+
+#ifdef noDEBUG
+#define DPRINT(...) fprintf(stderr, __VA_ARGS__)
+#else
+#define DPRINT(...)
+#endif
+
+/* TODO: uncompressed */
+
+typedef struct fz_faxd_s fz_faxd;
+
+struct fz_faxd_s
+{
+ fz_filter super;
+
+ int k;
+ int endofline;
+ int encodedbytealign;
+ int columns;
+ int rows;
+ int endofblock;
+ int blackis1;
+
+ int stride;
+ int ridx;
+
+ int bidx;
+ unsigned int word;
+
+ int stage, a, c, dim, eolc;
+ unsigned char *ref;
+ unsigned char *dst;
+};
+
+fz_error *
+fz_newfaxd(fz_filter **fp, fz_obj *params)
+{
+ fz_obj *obj;
+
+ FZ_NEWFILTER(fz_faxd, fax, faxd);
+
+ fax->ref = nil;
+ fax->dst = nil;
+
+ fax->k = 0;
+ fax->endofline = 0;
+ fax->encodedbytealign = 0;
+ fax->columns = 1728;
+ fax->rows = 0;
+ fax->endofblock = 1;
+ fax->blackis1 = 0;
+
+ obj = fz_dictgets(params, "K");
+ if (obj) fax->k = fz_toint(obj);
+
+ obj = fz_dictgets(params, "EndOfLine");
+ if (obj) fax->endofline = fz_tobool(obj);
+
+ obj = fz_dictgets(params, "EncodedByteAlign");
+ if (obj) fax->encodedbytealign = fz_tobool(obj);
+
+ obj = fz_dictgets(params, "Columns");
+ if (obj) fax->columns = fz_toint(obj);
+
+ obj = fz_dictgets(params, "Rows");
+ if (obj) fax->rows = fz_toint(obj);
+
+ obj = fz_dictgets(params, "EndOfBlock");
+ if (obj) fax->endofblock = fz_tobool(obj);
+
+ obj = fz_dictgets(params, "BlackIs1");
+ if (obj) fax->blackis1 = fz_tobool(obj);
+
+ fax->stride = ((fax->columns - 1) >> 3) + 1;
+ fax->ridx = 0;
+ fax->bidx = 32;
+ fax->word = 0;
+
+ fax->stage = SNORMAL;
+ fax->a = -1;
+ fax->c = 0;
+ fax->dim = fax->k < 0 ? 2 : 1;
+ fax->eolc = 0;
+
+ fax->ref = fz_malloc(fax->stride);
+ if (!fax->ref) { fz_free(fax); return fz_outofmem; }
+
+ fax->dst = fz_malloc(fax->stride);
+ if (!fax->dst) { fz_free(fax); fz_free(fax->ref); return fz_outofmem; }
+
+ memset(fax->ref, 0, fax->stride);
+ memset(fax->dst, 0, fax->stride);
+
+ DPRINT("FAXD k=%d eol=%d eba=%d cols=%d rows=%d eob=%d black1=%d stride=%d\n",
+ fax->k, fax->endofline, fax->encodedbytealign,
+ fax->columns, fax->rows, fax->endofblock, fax->blackis1,
+ fax->stride);
+
+ return nil;
+}
+
+void
+fz_freefaxd(fz_filter *p)
+{
+ fz_faxd *fax = (fz_faxd*) p;
+ fz_free(fax->ref);
+ fz_free(fax->dst);
+ fz_free(fax);
+}
+
+static inline void eatbits(fz_faxd *fax, int nbits)
+{
+ fax->word <<= nbits;
+ fax->bidx += nbits;
+}
+
+static inline fz_error * fillbits(fz_faxd *fax, fz_buffer *in)
+{
+ while (fax->bidx >= 8)
+ {
+ if (in->rp + 1 > in->wp)
+ return fz_ioneedin;
+ fax->bidx -= 8;
+ fax->word |= *in->rp << fax->bidx;
+ in->rp ++;
+ }
+ return nil;
+}
+
+static int
+getcode(fz_faxd *fax, const cfd_node *table, int initialbits)
+{
+ unsigned int word = fax->word;
+ int tidx = word >> (32 - initialbits);
+ int val = table[tidx].val;
+ int nbits = table[tidx].nbits;
+
+ if (nbits > initialbits)
+ {
+ int mask = (1 << (32 - initialbits)) - 1;
+ tidx = val + ((word & mask) >> (32 - nbits));
+ val = table[tidx].val;
+ nbits = initialbits + table[tidx].nbits;
+ }
+
+ eatbits(fax, nbits);
+
+ return val;
+}
+
+/* decode one 1d code */
+static fz_error *
+dec1d(fz_faxd *fax)
+{
+ int code;
+
+ if (fax->a == -1)
+ fax->a = 0;
+
+ if (fax->c)
+ code = getcode(fax, cf_black_decode, cfd_black_initial_bits);
+ else
+ code = getcode(fax, cf_white_decode, cfd_white_initial_bits);
+
+ DPRINT("%c %d\n", fax->c?'b':'w', code);
+
+ if (code == UNCOMPRESSED)
+ return fz_throw("ioerror: uncompressed data in faxd");
+
+ if (code < 0)
+ return fz_throw("ioerror: invalid 1d code in faxd");
+
+ if (fax->a + code > fax->columns)
+ return fz_throw("ioerror: invalid 1d data in faxd");
+
+ if (fax->c)
+ setbits(fax->dst, fax->a, fax->a + code);
+
+ fax->a += code;
+
+ if (code < 64)
+ {
+ fax->c = !fax->c;
+ fax->stage = SNORMAL;
+ }
+ else
+ fax->stage = SMAKEUP;
+
+ return nil;
+}
+
+/* decode one 2d code */
+static fz_error *
+dec2d(fz_faxd *fax)
+{
+ int code, b1, b2;
+
+ if (fax->stage == SH1 || fax->stage == SH2)
+ {
+ if (fax->a == -1)
+ fax->a = 0;
+
+ if (fax->c)
+ code = getcode(fax, cf_black_decode, cfd_black_initial_bits);
+ else
+ code = getcode(fax, cf_white_decode, cfd_white_initial_bits);
+
+ DPRINT("%c %d\n", fax->c ? 'b' : 'w', code);
+
+ if (code == UNCOMPRESSED)
+ return fz_throw("ioerror: uncompressed data in faxd");
+
+ if (code < 0)
+ return fz_throw("ioerror: invalid 2d code in faxd");
+
+ if (fax->a + code > fax->columns)
+ return fz_throw("ioerror: invalid 2d data in faxd");
+
+ if (fax->c)
+ setbits(fax->dst, fax->a, fax->a + code);
+
+ fax->a += code;
+
+ if (code < 64)
+ {
+ fax->c = !fax->c;
+ if (fax->stage == SH1)
+ fax->stage = SH2;
+ else if (fax->stage == SH2)
+ fax->stage = SNORMAL;
+ }
+
+ return nil;
+ }
+
+ code = getcode(fax, cf_2d_decode, cfd_2d_initial_bits);
+
+ switch (code)
+ {
+ case H:
+ fax->stage = SH1;
+ DPRINT("H\n");
+ break;
+
+ case P:
+ b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c);
+ b2 = findchanging(fax->ref, b1, fax->columns);
+ DPRINT("P\n");
+ if (fax->c) setbits(fax->dst, fax->a, b2);
+ fax->a = b2;
+ break;
+
+ case V0:
+ b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c);
+ DPRINT("V0\n");
+ if (fax->c) setbits(fax->dst, fax->a, b1);
+ fax->a = b1;
+ fax->c = !fax->c;
+ break;
+
+ case VR1:
+ b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c);
+ DPRINT("VR1\n");
+ if (fax->c) setbits(fax->dst, fax->a, b1 + 1);
+ fax->a = b1 + 1;
+ fax->c = !fax->c;
+ break;
+
+ case VR2:
+ b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c);
+ DPRINT("VR2\n");
+ if (fax->c) setbits(fax->dst, fax->a, b1 + 2);
+ fax->a = b1 + 2;
+ fax->c = !fax->c;
+ break;
+
+ case VR3:
+ b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c);
+ DPRINT("VR3\n");
+ if (fax->c) setbits(fax->dst, fax->a, b1 + 3);
+ fax->a = b1 + 3;
+ fax->c = !fax->c;
+ break;
+
+ case VL1:
+ b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c);
+ DPRINT("VL1\n");
+ if (fax->c) setbits(fax->dst, fax->a, b1 - 1);
+ fax->a = b1 - 1;
+ fax->c = !fax->c;
+ break;
+
+ case VL2:
+ b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c);
+ DPRINT("VL2\n");
+ if (fax->c) setbits(fax->dst, fax->a, b1 - 2);
+ fax->a = b1 - 2;
+ fax->c = !fax->c;
+ break;
+
+ case VL3:
+ b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c);
+ DPRINT("VL3\n");
+ if (fax->c) setbits(fax->dst, fax->a, b1 - 3);
+ fax->a = b1 - 3;
+ fax->c = !fax->c;
+ break;
+
+ case UNCOMPRESSED:
+ return fz_throw("ioerror: uncompressed data in faxd");
+
+ case ERROR:
+ return fz_throw("ioerror: invalid 2d code in faxd");
+
+ default:
+ return fz_throw("ioerror: invalid 2d code in faxd (%d)", code);
+ }
+
+ return 0;
+}
+
+fz_error *
+fz_processfaxd(fz_filter *f, fz_buffer *in, fz_buffer *out)
+{
+ fz_faxd *fax = (fz_faxd*)f;
+ fz_error * err;
+ int i;
+
+ if (fax->stage == SEOL)
+ goto eol;
+
+loop:
+
+ if (fillbits(fax, in))
+ {
+ if (in->eof) {
+ if (fax->bidx > 31) {
+ if (fax->a > 0)
+ goto eol;
+ goto rtc;
+ }
+ }
+ else {
+ return fz_ioneedin;
+ }
+ }
+
+ if ((fax->word >> (32 - 12)) == 0)
+ {
+ eatbits(fax, 1);
+ goto loop;
+ }
+
+ if ((fax->word >> (32 - 12)) == 1)
+ {
+ DPRINT("EOL\n");
+ eatbits(fax, 12);
+ fax->eolc ++;
+
+ if (fax->k > 0) {
+ if ((fax->word >> (32 - 1)) == 1)
+ fax->dim = 1;
+ else
+ fax->dim = 2;
+ eatbits(fax, 1);
+ DPRINT("DIM %d\n", fax->dim);
+ }
+ }
+ else if (fax->dim == 1)
+ {
+ fax->eolc = 0;
+ err = dec1d(fax);
+ if (err) return err;
+
+ }
+ else if (fax->dim == 2)
+ {
+ fax->eolc = 0;
+ err = dec2d(fax);
+ if (err) return err;
+ }
+
+ /* no eol check after makeup codes nor in the middle of an H code */
+ if (fax->stage == SMAKEUP || fax->stage == SH1 || fax->stage == SH2)
+ goto loop;
+
+ /* check for eol conditions */
+ if (fax->eolc || fax->a >= fax->columns)
+ {
+ if (fax->a > 0)
+ goto eol;
+ if (fax->eolc == (fax->k < 0 ? 2 : 6))
+ goto rtc;
+ }
+
+ goto loop;
+
+eol:
+ fax->stage = SEOL;
+ if (out->wp + fax->stride > out->ep)
+ return fz_ioneedout;
+
+ if (fax->blackis1)
+ memcpy(out->wp, fax->dst, fax->stride);
+ else
+ for (i = 0; i < fax->stride; i++)
+ out->wp[i] = ~fax->dst[i];
+
+ memcpy(fax->ref, fax->dst, fax->stride);
+ memset(fax->dst, 0, fax->stride);
+ out->wp += fax->stride;
+
+ fax->stage = SNORMAL;
+ fax->c = 0;
+ fax->a = -1;
+ fax->ridx ++;
+
+ if (!fax->endofblock && fax->rows) {
+ if (fax->ridx >= fax->rows)
+ goto rtc;
+ }
+
+ /* we have not read dim from eol, make a guess */
+ if (fax->k > 0 && !fax->eolc)
+ {
+ if (fax->ridx % fax->k == 0)
+ fax->dim = 1;
+ else
+ fax->dim = 2;
+ }
+
+ DPRINT("%dd scanline %d\n", fax->dim, fax->ridx + 1);
+
+ /* if endofline & encodedbytealign, EOLs are *not* optional */
+ if (fax->encodedbytealign) {
+ if (fax->endofline)
+ eatbits(fax, (12 - fax->bidx) & 7);
+ else
+ eatbits(fax, (8 - fax->bidx) & 7);
+ }
+
+ goto loop;
+
+rtc:
+ DPRINT("RTC\n");
+ out->eof = 1;
+ return fz_iodone;
+}
+
diff --git a/filter/faxd.h b/filter/faxd.h
new file mode 100644
index 00000000..886514f5
--- /dev/null
+++ b/filter/faxd.h
@@ -0,0 +1,61 @@
+/* Fax G3/G4 tables */
+
+/*
+<raph> the first 2^(initialbits) entries map bit patterns to decodes
+<raph> let's say initial_bits is 8 for the sake of example
+<raph> and that the code is 1001
+<raph> that means that entries 0x90 .. 0x9f have the entry { val, 4 }
+<raph> because those are all the bytes that start with the code
+<raph> and the 4 is the length of the code
+... if (n_bits > initial_bits) ...
+<raph> anyway, in that case, it basically points to a mini table
+<raph> the n_bits is the maximum length of all codes beginning with that byte
+<raph> so 2^(n_bits - initial_bits) is the size of the mini-table
+<raph> peter came up with this, and it makes sense
+*/
+
+typedef struct cfd_node_s cfd_node;
+
+struct cfd_node_s
+{
+ short val;
+ short nbits;
+};
+
+enum
+{
+ cfd_white_initial_bits = 8,
+ cfd_black_initial_bits = 7,
+ cfd_2d_initial_bits = 7,
+ cfd_uncompressed_initial_bits = 6 /* must be 6 */
+};
+
+/* non-run codes in tables */
+enum
+{
+ ERROR = -1,
+ ZEROS = -2, /* EOL follows, possibly with more padding first */
+ UNCOMPRESSED = -3,
+};
+
+/* semantic codes for cf_2d_decode */
+enum
+{
+ P = -4,
+ H = -5,
+ VR3 = 0,
+ VR2 = 1,
+ VR1 = 2,
+ V0 = 3,
+ VL1 = 4,
+ VL2 = 5,
+ VL3 = 6,
+};
+
+/* Decoding tables */
+
+extern const cfd_node cf_white_decode[];
+extern const cfd_node cf_black_decode[];
+extern const cfd_node cf_2d_decode[];
+extern const cfd_node cf_uncompressed_decode[];
+
diff --git a/filter/faxdtab.c b/filter/faxdtab.c
new file mode 100644
index 00000000..8e387d03
--- /dev/null
+++ b/filter/faxdtab.c
@@ -0,0 +1,931 @@
+/* Tables for CCITTFaxDecode filter. */
+
+/* This file was generated automatically. It is governed by the same terms */
+/* as the files scfetab.c and scfdgen.c from which it was derived. */
+/* Consult those files for the licensing terms and conditions. */
+
+#include "faxd.h"
+
+/* White decoding table. */
+const cfd_node cf_white_decode[] = {
+ { 256, 12 },
+ { 272, 12 },
+ { 29, 8 },
+ { 30, 8 },
+ { 45, 8 },
+ { 46, 8 },
+ { 22, 7 },
+ { 22, 7 },
+ { 23, 7 },
+ { 23, 7 },
+ { 47, 8 },
+ { 48, 8 },
+ { 13, 6 },
+ { 13, 6 },
+ { 13, 6 },
+ { 13, 6 },
+ { 20, 7 },
+ { 20, 7 },
+ { 33, 8 },
+ { 34, 8 },
+ { 35, 8 },
+ { 36, 8 },
+ { 37, 8 },
+ { 38, 8 },
+ { 19, 7 },
+ { 19, 7 },
+ { 31, 8 },
+ { 32, 8 },
+ { 1, 6 },
+ { 1, 6 },
+ { 1, 6 },
+ { 1, 6 },
+ { 12, 6 },
+ { 12, 6 },
+ { 12, 6 },
+ { 12, 6 },
+ { 53, 8 },
+ { 54, 8 },
+ { 26, 7 },
+ { 26, 7 },
+ { 39, 8 },
+ { 40, 8 },
+ { 41, 8 },
+ { 42, 8 },
+ { 43, 8 },
+ { 44, 8 },
+ { 21, 7 },
+ { 21, 7 },
+ { 28, 7 },
+ { 28, 7 },
+ { 61, 8 },
+ { 62, 8 },
+ { 63, 8 },
+ { 0, 8 },
+ { 320, 8 },
+ { 384, 8 },
+ { 10, 5 },
+ { 10, 5 },
+ { 10, 5 },
+ { 10, 5 },
+ { 10, 5 },
+ { 10, 5 },
+ { 10, 5 },
+ { 10, 5 },
+ { 11, 5 },
+ { 11, 5 },
+ { 11, 5 },
+ { 11, 5 },
+ { 11, 5 },
+ { 11, 5 },
+ { 11, 5 },
+ { 11, 5 },
+ { 27, 7 },
+ { 27, 7 },
+ { 59, 8 },
+ { 60, 8 },
+ { 288, 9 },
+ { 290, 9 },
+ { 18, 7 },
+ { 18, 7 },
+ { 24, 7 },
+ { 24, 7 },
+ { 49, 8 },
+ { 50, 8 },
+ { 51, 8 },
+ { 52, 8 },
+ { 25, 7 },
+ { 25, 7 },
+ { 55, 8 },
+ { 56, 8 },
+ { 57, 8 },
+ { 58, 8 },
+ { 192, 6 },
+ { 192, 6 },
+ { 192, 6 },
+ { 192, 6 },
+ { 1664, 6 },
+ { 1664, 6 },
+ { 1664, 6 },
+ { 1664, 6 },
+ { 448, 8 },
+ { 512, 8 },
+ { 292, 9 },
+ { 640, 8 },
+ { 576, 8 },
+ { 294, 9 },
+ { 296, 9 },
+ { 298, 9 },
+ { 300, 9 },
+ { 302, 9 },
+ { 256, 7 },
+ { 256, 7 },
+ { 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, 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 },
+ { 128, 5 },
+ { 128, 5 },
+ { 128, 5 },
+ { 128, 5 },
+ { 128, 5 },
+ { 128, 5 },
+ { 128, 5 },
+ { 128, 5 },
+ { 8, 5 },
+ { 8, 5 },
+ { 8, 5 },
+ { 8, 5 },
+ { 8, 5 },
+ { 8, 5 },
+ { 8, 5 },
+ { 8, 5 },
+ { 9, 5 },
+ { 9, 5 },
+ { 9, 5 },
+ { 9, 5 },
+ { 9, 5 },
+ { 9, 5 },
+ { 9, 5 },
+ { 9, 5 },
+ { 16, 6 },
+ { 16, 6 },
+ { 16, 6 },
+ { 16, 6 },
+ { 17, 6 },
+ { 17, 6 },
+ { 17, 6 },
+ { 17, 6 },
+ { 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, 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 },
+ { 14, 6 },
+ { 14, 6 },
+ { 14, 6 },
+ { 14, 6 },
+ { 15, 6 },
+ { 15, 6 },
+ { 15, 6 },
+ { 15, 6 },
+ { 64, 5 },
+ { 64, 5 },
+ { 64, 5 },
+ { 64, 5 },
+ { 64, 5 },
+ { 64, 5 },
+ { 64, 5 },
+ { 64, 5 },
+ { 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, 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 },
+ { -2, 3 },
+ { -2, 3 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -3, 4 },
+ { 1792, 3 },
+ { 1792, 3 },
+ { 1984, 4 },
+ { 2048, 4 },
+ { 2112, 4 },
+ { 2176, 4 },
+ { 2240, 4 },
+ { 2304, 4 },
+ { 1856, 3 },
+ { 1856, 3 },
+ { 1920, 3 },
+ { 1920, 3 },
+ { 2368, 4 },
+ { 2432, 4 },
+ { 2496, 4 },
+ { 2560, 4 },
+ { 1472, 1 },
+ { 1536, 1 },
+ { 1600, 1 },
+ { 1728, 1 },
+ { 704, 1 },
+ { 768, 1 },
+ { 832, 1 },
+ { 896, 1 },
+ { 960, 1 },
+ { 1024, 1 },
+ { 1088, 1 },
+ { 1152, 1 },
+ { 1216, 1 },
+ { 1280, 1 },
+ { 1344, 1 },
+ { 1408, 1 }
+};
+
+/* Black decoding table. */
+const cfd_node cf_black_decode[] = {
+ { 128, 12 },
+ { 160, 13 },
+ { 224, 12 },
+ { 256, 12 },
+ { 10, 7 },
+ { 11, 7 },
+ { 288, 12 },
+ { 12, 7 },
+ { 9, 6 },
+ { 9, 6 },
+ { 8, 6 },
+ { 8, 6 },
+ { 7, 5 },
+ { 7, 5 },
+ { 7, 5 },
+ { 7, 5 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 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 },
+ { 3, 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 },
+ { 3, 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 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { -2, 4 },
+ { -2, 4 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -3, 5 },
+ { 1792, 4 },
+ { 1792, 4 },
+ { 1984, 5 },
+ { 2048, 5 },
+ { 2112, 5 },
+ { 2176, 5 },
+ { 2240, 5 },
+ { 2304, 5 },
+ { 1856, 4 },
+ { 1856, 4 },
+ { 1920, 4 },
+ { 1920, 4 },
+ { 2368, 5 },
+ { 2432, 5 },
+ { 2496, 5 },
+ { 2560, 5 },
+ { 18, 3 },
+ { 18, 3 },
+ { 18, 3 },
+ { 18, 3 },
+ { 18, 3 },
+ { 18, 3 },
+ { 18, 3 },
+ { 18, 3 },
+ { 52, 5 },
+ { 52, 5 },
+ { 640, 6 },
+ { 704, 6 },
+ { 768, 6 },
+ { 832, 6 },
+ { 55, 5 },
+ { 55, 5 },
+ { 56, 5 },
+ { 56, 5 },
+ { 1280, 6 },
+ { 1344, 6 },
+ { 1408, 6 },
+ { 1472, 6 },
+ { 59, 5 },
+ { 59, 5 },
+ { 60, 5 },
+ { 60, 5 },
+ { 1536, 6 },
+ { 1600, 6 },
+ { 24, 4 },
+ { 24, 4 },
+ { 24, 4 },
+ { 24, 4 },
+ { 25, 4 },
+ { 25, 4 },
+ { 25, 4 },
+ { 25, 4 },
+ { 1664, 6 },
+ { 1728, 6 },
+ { 320, 5 },
+ { 320, 5 },
+ { 384, 5 },
+ { 384, 5 },
+ { 448, 5 },
+ { 448, 5 },
+ { 512, 6 },
+ { 576, 6 },
+ { 53, 5 },
+ { 53, 5 },
+ { 54, 5 },
+ { 54, 5 },
+ { 896, 6 },
+ { 960, 6 },
+ { 1024, 6 },
+ { 1088, 6 },
+ { 1152, 6 },
+ { 1216, 6 },
+ { 64, 3 },
+ { 64, 3 },
+ { 64, 3 },
+ { 64, 3 },
+ { 64, 3 },
+ { 64, 3 },
+ { 64, 3 },
+ { 64, 3 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 23, 4 },
+ { 23, 4 },
+ { 50, 5 },
+ { 51, 5 },
+ { 44, 5 },
+ { 45, 5 },
+ { 46, 5 },
+ { 47, 5 },
+ { 57, 5 },
+ { 58, 5 },
+ { 61, 5 },
+ { 256, 5 },
+ { 16, 3 },
+ { 16, 3 },
+ { 16, 3 },
+ { 16, 3 },
+ { 17, 3 },
+ { 17, 3 },
+ { 17, 3 },
+ { 17, 3 },
+ { 48, 5 },
+ { 49, 5 },
+ { 62, 5 },
+ { 63, 5 },
+ { 30, 5 },
+ { 31, 5 },
+ { 32, 5 },
+ { 33, 5 },
+ { 40, 5 },
+ { 41, 5 },
+ { 22, 4 },
+ { 22, 4 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 15, 2 },
+ { 15, 2 },
+ { 15, 2 },
+ { 15, 2 },
+ { 15, 2 },
+ { 15, 2 },
+ { 15, 2 },
+ { 15, 2 },
+ { 128, 5 },
+ { 192, 5 },
+ { 26, 5 },
+ { 27, 5 },
+ { 28, 5 },
+ { 29, 5 },
+ { 19, 4 },
+ { 19, 4 },
+ { 20, 4 },
+ { 20, 4 },
+ { 34, 5 },
+ { 35, 5 },
+ { 36, 5 },
+ { 37, 5 },
+ { 38, 5 },
+ { 39, 5 },
+ { 21, 4 },
+ { 21, 4 },
+ { 42, 5 },
+ { 43, 5 },
+ { 0, 3 },
+ { 0, 3 },
+ { 0, 3 },
+ { 0, 3 }
+};
+
+/* 2-D decoding table. */
+const cfd_node cf_2d_decode[] = {
+ { 128, 11 },
+ { 144, 10 },
+ { 6, 7 },
+ { 0, 7 },
+ { 5, 6 },
+ { 5, 6 },
+ { 1, 6 },
+ { 1, 6 },
+ { -4, 4 },
+ { -4, 4 },
+ { -4, 4 },
+ { -4, 4 },
+ { -4, 4 },
+ { -4, 4 },
+ { -4, 4 },
+ { -4, 4 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 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 },
+ { 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, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { -2, 4 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -3, 3 }
+};
+
+/* Uncompresssed decoding table. */
+const cfd_node cf_uncompressed_decode[] = {
+ { 64, 12 },
+ { 5, 6 },
+ { 4, 5 },
+ { 4, 5 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { -1, 0 },
+ { -1, 0 },
+ { 8, 6 },
+ { 9, 6 },
+ { 6, 5 },
+ { 6, 5 },
+ { 7, 5 },
+ { 7, 5 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 3, 3 },
+ { 3, 3 },
+ { 3, 3 },
+ { 3, 3 },
+ { 3, 3 },
+ { 3, 3 },
+ { 3, 3 },
+ { 3, 3 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 }
+};
+
+/* Dummy executable code to pacify compilers. */
+void scfdtab_dummy(void) { }
+
diff --git a/filter/faxe.c b/filter/faxe.c
new file mode 100644
index 00000000..20e776dd
--- /dev/null
+++ b/filter/faxe.c
@@ -0,0 +1,448 @@
+#include <fitz.h>
+
+#include "faxe.h"
+#include "faxc.h"
+
+/* TODO: honor Rows param */
+
+#define noDEBUGBITS 1
+#define noDEBUG 1
+
+#ifdef DEBUG
+#define DPRINT(...) fprintf(stderr, __VA_ARGS__)
+#else
+#define DPRINT(...)
+#endif
+
+typedef struct fz_faxe_s fz_faxe;
+
+struct fz_faxe_s
+{
+ fz_filter super;
+
+ int k;
+ int endofline;
+ int encodedbytealign;
+ int columns;
+ int endofblock;
+ int blackis1;
+
+ int stride;
+ int ridx; /* how many rows in total */
+ int bidx; /* how many bits are already used in out->wp */
+ unsigned char bsave; /* partial byte saved between process() calls */
+
+ int stage;
+ int a0, c; /* mid-line coding state */
+
+ unsigned char *ref;
+ unsigned char *src;
+};
+
+fz_error *
+fz_newfaxe(fz_filter **fp, fz_obj *params)
+{
+ fz_obj *obj;
+
+ FZ_NEWFILTER(fz_faxe, fax, faxe);
+
+ fax->ref = nil;
+ fax->src = nil;
+
+ fax->k = 0;
+ fax->endofline = 0;
+ fax->encodedbytealign = 0;
+ fax->columns = 1728;
+ fax->endofblock = 1;
+ fax->blackis1 = 0;
+
+ obj = fz_dictgets(params, "K");
+ if (obj) fax->k = fz_toint(obj);
+
+ obj = fz_dictgets(params, "EndOfLine");
+ if (obj) fax->endofline = fz_tobool(obj);
+
+ obj = fz_dictgets(params, "EncodedByteAlign");
+ if (obj) fax->encodedbytealign = fz_tobool(obj);
+
+ obj = fz_dictgets(params, "Columns");
+ if (obj) fax->columns = fz_toint(obj);
+
+ obj = fz_dictgets(params, "EndOfBlock");
+ if (obj) fax->endofblock = fz_tobool(obj);
+
+ obj = fz_dictgets(params, "BlackIs1");
+ if (obj) fax->blackis1 = fz_tobool(obj);
+
+ fax->stride = ((fax->columns - 1) >> 3) + 1;
+ fax->bidx = 0;
+ fax->ridx = 0;
+
+ fax->stage = 0;
+ fax->a0 = -1;
+ fax->c = 0;
+
+ fax->ref = fz_malloc(fax->stride);
+ if (!fax->ref) { fz_free(fax); return fz_outofmem; }
+
+ fax->src = fz_malloc(fax->stride);
+ if (!fax->src) { fz_free(fax); fz_free(fax->ref); return fz_outofmem; }
+
+ memset(fax->ref, 0, fax->stride);
+ memset(fax->src, 0, fax->stride);
+
+ return nil;
+}
+
+void
+fz_freefaxe(fz_filter *p)
+{
+ fz_faxe *fax = (fz_faxe*) p;
+ fz_free(fax->src);
+ fz_free(fax->ref);
+ fz_free(fax);
+}
+
+enum { codebytes = 2 };
+
+static inline int
+runbytes(int run)
+{
+ int m = (run / 64) / 40 + 1; /* number of makeup codes */
+ return codebytes * (m + 1); /* bytes for makeup + term codes */
+}
+
+static void
+putbits(fz_faxe *fax, fz_buffer *out, int code, int nbits)
+{
+#ifdef DEBUGBITS
+ fprintf(stderr, "BITS ");
+ printbits(stderr, code, nbits);
+ fprintf(stderr, "\n");
+#endif
+
+ while (nbits > 0)
+ {
+ if (fax->bidx == 0) {
+ *out->wp = 0;
+ }
+
+ /* code does not fit: shift right */
+ if (nbits > (8 - fax->bidx)) {
+ *out->wp |= code >> (nbits - (8 - fax->bidx));
+ nbits = nbits - (8 - fax->bidx);
+ fax->bidx = 0;
+ out->wp ++;
+ }
+
+ /* shift left */
+ else {
+ *out->wp |= code << ((8 - fax->bidx) - nbits);
+ fax->bidx += nbits;
+ if (fax->bidx == 8) {
+ fax->bidx = 0;
+ out->wp ++;
+ }
+ nbits = 0;
+ }
+ }
+}
+
+static inline void
+putcode(fz_faxe *fax, fz_buffer *out, const cfe_code *run)
+{
+ putbits(fax, out, run->code, run->nbits);
+}
+
+static void
+putrun(fz_faxe *fax, fz_buffer *out, int run, int c)
+{
+ int m;
+
+ const cf_runs *codetable = c ? &cf_black_runs : &cf_white_runs;
+
+ if (run > 63) {
+ m = run / 64;
+ while (m > 40) {
+ DPRINT("%c %d\n", c?'b':'w', 40 * 64);
+ putcode(fax, out, &codetable->makeup[40]);
+ m -= 40;
+ }
+ if (m > 0) {
+ DPRINT("%c %d\n", c?'b':'w', m * 64);
+ putcode(fax, out, &codetable->makeup[m]);
+ }
+ DPRINT("%c %d\n", c?'b':'w', run % 64);
+ putcode(fax, out, &codetable->termination[run % 64]);
+ }
+ else {
+ DPRINT("%c %d\n", c?'b':'w', run);
+ putcode(fax, out, &codetable->termination[run]);
+ }
+}
+
+static fz_error *
+enc1d(fz_faxe *fax, unsigned char *line, fz_buffer *out)
+{
+ int run;
+
+#ifdef DEBUG
+if (fax->a0 < 0) {
+ DPRINT("1d scanline %d\n", fax->ridx + 1);
+ DPRINT("color = %d\n", fax->c);
+}
+#endif
+
+ if (fax->a0 < 0)
+ fax->a0 = 0;
+
+ while (fax->a0 < fax->columns)
+ {
+ run = getrun(line, fax->a0, fax->columns, fax->c);
+
+ if (out->wp + 1 + runbytes(run) > out->ep)
+ return fz_ioneedout;
+
+ putrun(fax, out, run, fax->c);
+
+ fax->a0 += run;
+ fax->c = !fax->c;
+ }
+
+ return 0;
+}
+
+static fz_error *
+enc2d(fz_faxe *fax, unsigned char *ref, unsigned char *src, fz_buffer *out)
+{
+ int a1, a2, b1, b2;
+ int run1, run2, n;
+
+
+#ifdef DEBUG
+if (fax->a0 < 0)
+{
+ DPRINT("2d scanline %d\n", fax->ridx + 1);
+}
+#endif
+
+ DPRINT("color = %d\n", fax->c);
+
+ while (fax->a0 < fax->columns)
+ {
+ a1 = findchanging(src, fax->a0, fax->columns);
+ b1 = findchangingcolor(ref, fax->a0, fax->columns, !fax->c);
+ b2 = findchanging(ref, b1, fax->columns);
+
+#ifdef DEBUGBITS
+ DPRINT("twod a0=%d a1=%d b1=%d b2=%d\n", fax->a0, a1, b1, b2);
+#endif
+
+ /* pass */
+ if (b2 < a1)
+ {
+ if (out->wp + 1 + codebytes > out->ep)
+ return fz_ioneedout;
+
+ DPRINT("P\n");
+
+ putcode(fax, out, &cf2_run_pass);
+
+ fax->a0 = b2;
+ }
+
+ /* vertical */
+ else if (ABS(b1 - a1) <= 3)
+ {
+ if (out->wp + 1 + codebytes > out->ep)
+ return fz_ioneedout;
+
+#ifdef DEBUG
+ if (b1 - a1 == 0)
+ DPRINT("V0\n");
+ else if (b1 - a1 < 0)
+ DPRINT("VR%d\n", a1 - b1);
+ else
+ DPRINT("VL%d\n", b1 - a1);
+#endif
+
+ putcode(fax, out, &cf2_run_vertical[b1 - a1 + 3]);
+
+ fax->a0 = a1;
+ fax->c = !fax->c;
+ }
+
+ /* horizontal */
+ else
+ {
+ a2 = findchanging(src, a1, fax->columns);
+ run1 = a1 - fax->a0;
+ run2 = a2 - a1;
+ n = codebytes + runbytes(run1) + runbytes(run2);
+
+ if (out->wp + 1 + n > out->ep)
+ return fz_ioneedout;
+
+ DPRINT("H\n");
+
+ putcode(fax, out, &cf2_run_horizontal);
+ putrun(fax, out, run1, fax->c);
+ putrun(fax, out, run2, !fax->c);
+
+ fax->a0 = a2;
+ }
+ }
+
+ return 0;
+}
+
+static fz_error *
+process(fz_faxe *fax, fz_buffer *in, fz_buffer *out)
+{
+ fz_error *err;
+ int i, n;
+
+ while (1)
+ {
+ if (in->rp == in->wp && in->eof)
+ goto rtc;
+
+ switch (fax->stage)
+ {
+ case 0:
+ if (fax->encodedbytealign)
+ {
+ if (fax->endofline) {
+ if (out->wp + 1 + 1 > out->ep)
+ return fz_ioneedout;
+
+ /* make sure that EOL ends on a byte border */
+ putbits(fax, out, 0, (12 - fax->bidx) & 7);
+ }
+ else {
+ if (fax->bidx) {
+ if (out->wp + 1 > out->ep)
+ return fz_ioneedout;
+ fax->bidx = 0;
+ out->wp ++;
+ }
+ }
+ }
+
+ fax->stage ++;
+
+ case 1:
+ if (fax->endofline)
+ {
+ if (out->wp + 1 + codebytes + 1 > out->ep)
+ return fz_ioneedout;
+
+ DPRINT("EOL\n");
+ if (fax->k > 0) {
+ if (fax->ridx % fax->k == 0)
+ putcode(fax, out, &cf2_run_eol_1d);
+ else
+ putcode(fax, out, &cf2_run_eol_2d);
+ }
+ else {
+ putcode(fax, out, &cf_run_eol);
+ }
+ }
+
+ fax->stage ++;
+
+ case 2:
+ if (in->rp + fax->stride > in->wp) {
+ if (in->eof) /* XXX barf here? */
+ goto rtc;
+ return fz_ioneedin;
+ }
+
+ memcpy(fax->ref, fax->src, fax->stride);
+ memcpy(fax->src, in->rp, fax->stride);
+
+ if (!fax->blackis1)
+ {
+ for (i = 0; i < fax->stride; i++)
+ fax->src[i] = ~fax->src[i];
+ }
+
+ in->rp += fax->stride;
+
+ fax->c = 0;
+ fax->a0 = -1;
+
+#ifdef DEBUGBITS
+ DPRINT("LINE %d\n", fax->ridx);
+ printline(stderr, fax->ref, fax->columns);
+ printline(stderr, fax->src, fax->columns);
+#endif
+
+ fax->stage ++;
+
+ case 3:
+ err = 0; /* to silence compiler */
+
+ if (fax->k < 0) {
+ err = enc2d(fax, fax->ref, fax->src, out);
+ }
+ else if (fax->k == 0) {
+ err = enc1d(fax, fax->src, out);
+ }
+ else if (fax->k > 0) {
+ if (fax->ridx % fax->k == 0) {
+ err = enc1d(fax, fax->src, out);
+ }
+ else {
+ err = enc2d(fax, fax->ref, fax->src, out);
+ }
+ }
+
+ if (err) return err;
+
+ fax->ridx ++;
+
+ fax->stage = 0;
+ }
+ }
+
+rtc:
+ if (fax->endofblock)
+ {
+ n = fax->k < 0 ? 2 : 6;
+
+ if (out->wp + 1 + codebytes * n > out->ep)
+ return fz_ioneedout;
+
+ for (i = 0; i < n; i++) {
+ putcode(fax, out, &cf_run_eol);
+ if (fax->k > 0)
+ putbits(fax, out, 1, 1);
+ }
+ DPRINT("RTC\n");
+ }
+
+ if (fax->bidx)
+ out->wp ++;
+ out->eof = 1;
+
+ return fz_iodone;
+}
+
+fz_error *
+fz_processfaxe(fz_filter *p, fz_buffer *in, fz_buffer *out)
+{
+ fz_faxe *fax = (fz_faxe*) p;
+ fz_error *err;
+
+ /* restore partial bits */
+ *out->wp = fax->bsave;
+
+ err = process(fax, in, out);
+
+ /* save partial bits */
+ fax->bsave = *out->wp;
+
+ return err;
+}
+
diff --git a/filter/faxe.h b/filter/faxe.h
new file mode 100644
index 00000000..dd3fc121
--- /dev/null
+++ b/filter/faxe.h
@@ -0,0 +1,37 @@
+/* Fax G3/G4 tables */
+
+typedef struct cfe_code_s cfe_code;
+
+struct cfe_code_s
+{
+ unsigned short code;
+ unsigned short nbits;
+};
+
+typedef struct cf_runs_s {
+ cfe_code termination[64];
+ cfe_code makeup[41];
+} cf_runs;
+
+/* Encoding tables */
+
+/* Codes common to 1-D and 2-D encoding. */
+extern const cfe_code cf_run_eol;
+extern const cf_runs cf_white_runs, cf_black_runs;
+extern const cfe_code cf_uncompressed[6];
+extern const cfe_code cf_uncompressed_exit[10]; /* indexed by 2 x length of */
+
+/* 1-D encoding. */
+extern const cfe_code cf1_run_uncompressed;
+
+/* 2-D encoding. */
+enum { cf2_run_vertical_offset = 3 };
+extern const cfe_code cf2_run_pass;
+extern const cfe_code cf2_run_vertical[7]; /* indexed by b1 - a1 + offset */
+extern const cfe_code cf2_run_horizontal;
+extern const cfe_code cf2_run_uncompressed;
+
+/* 2-D Group 3 encoding. */
+extern const cfe_code cf2_run_eol_1d;
+extern const cfe_code cf2_run_eol_2d;
+
diff --git a/filter/faxetab.c b/filter/faxetab.c
new file mode 100644
index 00000000..409039f4
--- /dev/null
+++ b/filter/faxetab.c
@@ -0,0 +1,131 @@
+/* Tables for CCITTFaxEncode filter */
+
+#include "faxe.h"
+
+/* Define the end-of-line code. */
+const cfe_code cf_run_eol = {1, 12};
+
+/* Define the 1-D code that signals uncompressed data. */
+const cfe_code cf1_run_uncompressed = {0xf, 12};
+
+/* Define the 2-D run codes. */
+const cfe_code cf2_run_pass = {0x1, 4};
+const cfe_code cf2_run_vertical[7] =
+{
+ {0x3, 7},
+ {0x3, 6},
+ {0x3, 3},
+ {0x1, 1},
+ {0x2, 3},
+ {0x2, 6},
+ {0x2, 7}
+};
+const cfe_code cf2_run_horizontal = {1, 3};
+const cfe_code cf2_run_uncompressed = {0xf, 10};
+
+/* EOL codes for Group 3 2-D. */
+const cfe_code cf2_run_eol_1d = { (1 << 1) + 1, 12 + 1};
+const cfe_code cf2_run_eol_2d = { (1 << 1) + 0, 12 + 1};
+
+/* White run codes. */
+const cf_runs cf_white_runs =
+{
+ /* Termination codes */
+ {
+ {0x35, 8}, {0x7, 6}, {0x7, 4}, {0x8, 4},
+ {0xb, 4}, {0xc, 4}, {0xe, 4}, {0xf, 4},
+ {0x13, 5}, {0x14, 5}, {0x7, 5}, {0x8, 5},
+ {0x8, 6}, {0x3, 6}, {0x34, 6}, {0x35, 6},
+ {0x2a, 6}, {0x2b, 6}, {0x27, 7}, {0xc, 7},
+ {0x8, 7}, {0x17, 7}, {0x3, 7}, {0x4, 7},
+ {0x28, 7}, {0x2b, 7}, {0x13, 7}, {0x24, 7},
+ {0x18, 7}, {0x2, 8}, {0x3, 8}, {0x1a, 8},
+ {0x1b, 8}, {0x12, 8}, {0x13, 8}, {0x14, 8},
+ {0x15, 8}, {0x16, 8}, {0x17, 8}, {0x28, 8},
+ {0x29, 8}, {0x2a, 8}, {0x2b, 8}, {0x2c, 8},
+ {0x2d, 8}, {0x4, 8}, {0x5, 8}, {0xa, 8},
+ {0xb, 8}, {0x52, 8}, {0x53, 8}, {0x54, 8},
+ {0x55, 8}, {0x24, 8}, {0x25, 8}, {0x58, 8},
+ {0x59, 8}, {0x5a, 8}, {0x5b, 8}, {0x4a, 8},
+ {0x4b, 8}, {0x32, 8}, {0x33, 8}, {0x34, 8}
+ },
+
+ /* Make-up codes */
+ {
+ {0, 0} /* dummy */ , {0x1b, 5}, {0x12, 5}, {0x17, 6},
+ {0x37, 7}, {0x36, 8}, {0x37, 8}, {0x64, 8},
+ {0x65, 8}, {0x68, 8}, {0x67, 8}, {0xcc, 9},
+ {0xcd, 9}, {0xd2, 9}, {0xd3, 9}, {0xd4, 9},
+ {0xd5, 9}, {0xd6, 9}, {0xd7, 9}, {0xd8, 9},
+ {0xd9, 9}, {0xda, 9}, {0xdb, 9}, {0x98, 9},
+ {0x99, 9}, {0x9a, 9}, {0x18, 6}, {0x9b, 9},
+ {0x8, 11}, {0xc, 11}, {0xd, 11}, {0x12, 12},
+ {0x13, 12}, {0x14, 12}, {0x15, 12}, {0x16, 12},
+ {0x17, 12}, {0x1c, 12}, {0x1d, 12}, {0x1e, 12},
+ {0x1f, 12}
+ }
+};
+
+/* Black run codes. */
+const cf_runs cf_black_runs =
+{
+ /* Termination codes */
+ {
+ {0x37, 10}, {0x2, 3}, {0x3, 2}, {0x2, 2},
+ {0x3, 3}, {0x3, 4}, {0x2, 4}, {0x3, 5},
+ {0x5, 6}, {0x4, 6}, {0x4, 7}, {0x5, 7},
+ {0x7, 7}, {0x4, 8}, {0x7, 8}, {0x18, 9},
+ {0x17, 10}, {0x18, 10}, {0x8, 10}, {0x67, 11},
+ {0x68, 11}, {0x6c, 11}, {0x37, 11}, {0x28, 11},
+ {0x17, 11}, {0x18, 11}, {0xca, 12}, {0xcb, 12},
+ {0xcc, 12}, {0xcd, 12}, {0x68, 12}, {0x69, 12},
+ {0x6a, 12}, {0x6b, 12}, {0xd2, 12}, {0xd3, 12},
+ {0xd4, 12}, {0xd5, 12}, {0xd6, 12}, {0xd7, 12},
+ {0x6c, 12}, {0x6d, 12}, {0xda, 12}, {0xdb, 12},
+ {0x54, 12}, {0x55, 12}, {0x56, 12}, {0x57, 12},
+ {0x64, 12}, {0x65, 12}, {0x52, 12}, {0x53, 12},
+ {0x24, 12}, {0x37, 12}, {0x38, 12}, {0x27, 12},
+ {0x28, 12}, {0x58, 12}, {0x59, 12}, {0x2b, 12},
+ {0x2c, 12}, {0x5a, 12}, {0x66, 12}, {0x67, 12}
+ },
+
+ /* Make-up codes. */
+ {
+ {0, 0} /* dummy */ , {0xf, 10}, {0xc8, 12}, {0xc9, 12},
+ {0x5b, 12}, {0x33, 12}, {0x34, 12}, {0x35, 12},
+ {0x6c, 13}, {0x6d, 13}, {0x4a, 13}, {0x4b, 13},
+ {0x4c, 13}, {0x4d, 13}, {0x72, 13}, {0x73, 13},
+ {0x74, 13}, {0x75, 13}, {0x76, 13}, {0x77, 13},
+ {0x52, 13}, {0x53, 13}, {0x54, 13}, {0x55, 13},
+ {0x5a, 13}, {0x5b, 13}, {0x64, 13}, {0x65, 13},
+ {0x8, 11}, {0xc, 11}, {0xd, 11}, {0x12, 12},
+ {0x13, 12}, {0x14, 12}, {0x15, 12}, {0x16, 12},
+ {0x17, 12}, {0x1c, 12}, {0x1d, 12}, {0x1e, 12},
+ {0x1f, 12}
+ }
+};
+
+/* Uncompressed codes. */
+const cfe_code cf_uncompressed[6] =
+{
+ {1, 1},
+ {1, 2},
+ {1, 3},
+ {1, 4},
+ {1, 5},
+ {1, 6}
+};
+
+/* Uncompressed exit codes. */
+const cfe_code cf_uncompressed_exit[10] =
+{
+ {2, 8}, {3, 8},
+ {2, 9}, {3, 9},
+ {2, 10}, {3, 10},
+ {2, 11}, {3, 11},
+ {2, 12}, {3, 12}
+};
+
+/* Some C compilers insist on having executable code in every file.... */
+void scfetab_dummy(void) { }
+
diff --git a/filter/file.c b/filter/file.c
new file mode 100644
index 00000000..c3ab63a4
--- /dev/null
+++ b/filter/file.c
@@ -0,0 +1,589 @@
+#include <fitz.h>
+
+/* TODO: nil filter on write */
+
+fz_error *
+fz_ferror(fz_file *f)
+{
+ fz_error *e = f->error;
+ f->error = nil;
+ return e;
+}
+
+fz_error *
+fz_openfile(fz_file **filep, char *path, int mode)
+{
+ fz_error *error;
+ fz_file *file;
+ int fd;
+
+ assert(mode == O_RDONLY || mode == O_WRONLY);
+
+ file = *filep = fz_malloc(sizeof(fz_file));
+ if (!file)
+ return fz_outofmem;
+
+ fd = open(path, mode, 0);
+ if (fd == -1)
+ return fz_throw("ioerror: open '%s': %s", path, strerror(errno));
+
+ file->mode = mode;
+ file->fd = fd;
+ file->depth = 0;
+ file->error = nil;
+ file->filter = nil;
+ file->in = nil;
+ file->out = nil;
+
+ error = fz_newbuffer(&file->in, FZ_BUFSIZE);
+ if (error)
+ goto cleanup;
+
+ error = fz_newbuffer(&file->out, FZ_BUFSIZE);
+ if (error)
+ goto cleanup;
+
+ return nil;
+
+cleanup:
+ close(fd);
+ fz_free(file->out);
+ fz_free(file->in);
+ fz_free(file);
+ *filep = nil;
+ return error;
+}
+
+void
+fz_closefile(fz_file *file)
+{
+ assert(file->depth == 0);
+
+ if (file->mode == O_WRONLY)
+ fz_flush(file);
+
+ if (file->error)
+ {
+ fz_warn("%s", file->error->msg);
+ fz_freeerror(file->error);
+ file->error = nil;
+ }
+
+ close(file->fd);
+
+ if (file->filter) fz_freefilter(file->filter);
+ fz_freebuffer(file->in);
+ fz_freebuffer(file->out);
+ fz_free(file);
+}
+
+fz_error *
+fz_pushfilter(fz_file *file, fz_filter *filter)
+{
+ fz_error *error;
+ fz_buffer *buf;
+
+ if (file->depth == 0)
+ {
+ buf = file->out;
+ file->out = file->in;
+ file->in = buf;
+
+ file->out->rp = file->out->bp;
+ file->out->wp = file->out->bp;
+ file->out->eof = 0;
+
+ file->filter = filter;
+ }
+ else
+ {
+ error = fz_chainpipeline(&file->filter, file->filter, filter, file->out);
+ if (error)
+ return error;
+
+ error = fz_newbuffer(&file->out, FZ_BUFSIZE);
+ if (error)
+ {
+ fz_unchainpipeline(file->filter, &file->filter, &file->out);
+ return error;
+ }
+ }
+
+ file->depth ++;
+
+ return nil;
+}
+
+void
+fz_popfilter(fz_file *file)
+{
+ fz_buffer *buf;
+
+ assert(file->depth > 0);
+
+ if (file->mode == O_WRONLY)
+ fz_flush(file);
+
+ if (file->error)
+ {
+ fz_warn("%s", file->error->msg);
+ fz_freeerror(file->error);
+ file->error = nil;
+ }
+
+ if (file->depth == 1)
+ {
+ fz_freefilter(file->filter);
+ file->filter = nil;
+
+ buf = file->out;
+ file->out = file->in;
+ file->in = buf;
+
+ file->in->rp = file->in->bp;
+ file->in->wp = file->in->bp;
+ file->in->eof = 0;
+ }
+ else
+ {
+ fz_freebuffer(file->out);
+ fz_unchainpipeline(file->filter, &file->filter, &file->out);
+ }
+
+ file->depth --;
+}
+
+int
+fz_seek(fz_file *f, int ofs)
+{
+ int t;
+ int c;
+
+ assert(f->mode == O_RDONLY);
+
+ if (f->filter)
+ {
+ if (ofs < fz_tell(f))
+ {
+ f->error = fz_throw("ioerror: cannot seek backwards in filter");
+ return -1;
+ }
+ while (fz_tell(f) < ofs)
+ {
+ c = fz_readbyte(f);
+ if (c == EOF)
+ return -1;
+ }
+ return 0;
+ }
+
+ t = lseek(f->fd, ofs, 0);
+ if (t == -1)
+ {
+ f->error = fz_throw("ioerror: lseek: %s", strerror(errno));
+ return -1;
+ }
+
+ f->out->rp = f->out->bp;
+ f->out->wp = f->out->bp;
+ f->out->eof = 0;
+
+ return 0;
+}
+
+int
+fz_tell(fz_file *f)
+{
+ int t;
+
+ if (f->filter)
+ {
+ return f->filter->count - (f->out->wp - f->out->rp);
+ }
+
+ t = lseek(f->fd, 0, 1);
+ if (t == -1)
+ {
+ f->error = fz_throw("ioerror: lseek: %s", strerror(errno));
+ return -1;
+ }
+
+ return t - (f->out->wp - f->out->rp);
+}
+
+/*
+ * Read mode
+ */
+
+static int doread(fz_buffer *b, int fd)
+{
+ int n = read(fd, b->wp, b->ep - b->wp);
+ if (n == -1)
+ return -1;
+ if (n == 0)
+ b->eof = 1;
+ b->wp += n;
+ return n;
+}
+
+static int producedata(fz_file *f)
+{
+ fz_error *reason;
+ int produced;
+ int n;
+
+ assert(f->mode == O_RDONLY);
+ assert(f->error == nil);
+
+ if (!f->filter)
+ {
+ fz_rewindbuffer(f->out);
+ n = doread(f->out, f->fd);
+ if (n < 0) {
+ f->error = fz_throw("ioerror in read: %s", strerror(errno));
+ return -1;
+ }
+ return 0;
+ }
+
+ produced = 0;
+
+ while (1)
+ {
+ reason = fz_process(f->filter, f->in, f->out);
+
+ if (f->filter->produced)
+ produced = 1;
+
+ if (reason == fz_ioneedin)
+ {
+ if (f->in->eof) {
+ f->error = fz_throw("ioerror: premature eof in filter");
+ return -1;
+ }
+
+ /* no space to produce, rewind or grow */
+ if (f->in->wp == f->in->ep)
+ {
+ if (f->in->rp > f->in->bp)
+ f->error = fz_rewindbuffer(f->in);
+ else
+ f->error = fz_growbuffer(f->in);
+ if (f->error)
+ return -1;
+ }
+
+ /* then fill with more input */
+ n = doread(f->in, f->fd);
+ if (n < 0) {
+ f->error = fz_throw("ioerror in read: %s", strerror(errno));
+ return -1;
+ }
+
+ if (produced)
+ return 0;
+ }
+
+ else if (reason == fz_ioneedout)
+ {
+ if (produced)
+ return 0;
+
+ /* need more outspace, and produced no data */
+ if (f->out->rp > f->out->bp)
+ f->error = fz_rewindbuffer(f->out);
+ else
+ f->error = fz_growbuffer(f->out);
+ if (f->error)
+ return -1;
+ }
+
+ else if (reason == fz_iodone)
+ return 0;
+
+ else {
+ f->error = reason;
+ return -1;
+ }
+ }
+}
+
+int
+fz_peekbyte(fz_file *f)
+{
+ if (f->out->rp == f->out->wp)
+ {
+ if (f->out->eof) return EOF;
+ if (producedata(f)) return EOF;
+ }
+
+ if (f->out->rp < f->out->wp)
+ return *f->out->rp;
+
+ return EOF;
+}
+
+int
+fz_readbyte(fz_file *f)
+{
+ if (f->out->rp == f->out->wp)
+ {
+ if (f->out->eof) return EOF;
+ if (producedata(f)) return EOF;
+ }
+
+ if (f->out->rp < f->out->wp)
+ return *f->out->rp++;
+
+ return EOF;
+}
+
+int
+fz_read(fz_file *f, char *buf, int n)
+{
+ int i = 0;
+
+ while (i < n)
+ {
+ while (f->out->rp < f->out->wp && i < n)
+ buf[i++] = *f->out->rp ++;
+
+ if (f->out->rp == f->out->wp)
+ {
+ if (f->out->eof) return i;
+ if (producedata(f) < 0) return -1;
+ }
+ }
+
+ return i;
+}
+
+int
+fz_readline(fz_file *f, char *buf, int n)
+{
+ int c = EOF;
+ char *s = buf;
+
+ while (n > 1)
+ {
+ c = fz_readbyte(f);
+ if (c == EOF)
+ break;
+ if (c == '\r') {
+ c = fz_peekbyte(f);
+ if (c == '\n')
+ c = fz_readbyte(f);
+ break;
+ }
+ if (c == '\n')
+ break;
+ *s++ = c;
+ n--;
+ }
+ if (n)
+ *s = '\0';
+ return s - buf;
+}
+
+/*
+ * Write mode
+ */
+
+static int dowrite(fz_buffer *b, int fd)
+{
+ int n = write(fd, b->rp, b->wp - b->rp);
+ if (n == -1)
+ return -1;
+ b->rp += n;
+ return n;
+}
+
+int
+fz_write(fz_file *f, char *buf, int n)
+{
+ fz_error *reason;
+ int i = 0;
+ int x;
+
+ assert(f->mode == O_WRONLY);
+ assert(f->error == nil);
+
+ while (i < n)
+ {
+ while (f->in->rp < f->in->wp && i < n)
+ {
+ *f->in->rp++ = buf[i++];
+ }
+
+ if (f->in->rp == f->in->wp)
+ {
+ reason = fz_process(f->filter, f->in, f->out);
+
+ if (reason == fz_ioneedin)
+ {
+ if (f->in->wp == f->in->ep) {
+ if (f->in->rp > f->in->bp)
+ f->error = fz_rewindbuffer(f->in);
+ else
+ f->error = fz_growbuffer(f->in);
+ if (f->error)
+ return -1;
+ }
+ }
+
+ else if (reason == fz_ioneedout)
+ {
+ x = dowrite(f->out, f->fd);
+ if (x < 0) {
+ f->error = fz_throw("ioerror in write: %s", strerror(errno));
+ return -1;
+ }
+
+ if (f->out->rp > f->out->bp)
+ f->error = fz_rewindbuffer(f->out);
+ else
+ f->error = fz_growbuffer(f->out);
+ if (f->error)
+ return -1;
+ }
+
+ else if (reason == fz_iodone)
+ {
+ x = dowrite(f->out, f->fd);
+ if (x < 0) {
+ f->error = fz_throw("ioerror in write: %s", strerror(errno));
+ return -1;
+ }
+ break;
+ }
+
+ else {
+ f->error = reason;
+ return -1;
+ }
+ }
+ }
+
+ return i;
+}
+
+int
+fz_flush(fz_file *f)
+{
+ fz_error *reason;
+ int n;
+
+ assert(f->mode == O_WRONLY);
+ assert(f->error == nil);
+
+ f->in->eof = 1;
+
+ while (!f->out->eof)
+ {
+ reason = fz_process(f->filter, f->in, f->out);
+
+ if (reason == fz_ioneedin) {
+ f->error = fz_throw("ioerror: premature eof in filter");
+ return -1;
+ }
+
+ else if (reason == fz_ioneedout)
+ {
+ n = dowrite(f->out, f->fd);
+ if (n < 0) {
+ f->error = fz_throw("ioerror in write: %s", strerror(errno));
+ return -1;
+ }
+
+ if (f->out->rp > f->out->bp)
+ f->error = fz_rewindbuffer(f->out);
+ else
+ f->error = fz_growbuffer(f->out);
+ if (f->error)
+ return -1;
+ }
+
+ else if (reason == fz_iodone)
+ {
+ n = dowrite(f->out, f->fd);
+ if (n < 0) {
+ f->error = fz_throw("ioerror in write: %s", strerror(errno));
+ return -1;
+ }
+ break;
+ }
+
+ else {
+ f->error = reason;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Utility function to consume contents of file stream into
+ * a freshly allocated buffer; realloced and trimmed to size.
+ */
+
+enum { CHUNKSIZE = 4096 };
+
+fz_error *
+fz_readfile(unsigned char **bufp, int *lenp, fz_file *file)
+{
+ unsigned char *newbuf;
+ unsigned char *buf;
+ int len;
+ int pos;
+ int n;
+
+ *bufp = nil;
+ *lenp = 0;
+
+ len = 0;
+ pos = 0;
+ buf = nil;
+
+ while (1)
+ {
+ if (len - pos == 0)
+ {
+ len += CHUNKSIZE;
+ newbuf = fz_realloc(buf, len);
+ if (!newbuf)
+ {
+ fz_free(buf);
+ return fz_outofmem;
+ }
+ buf = newbuf;
+ }
+
+ n = fz_read(file, buf + pos, len - pos);
+
+printf("fz_read %d bytes\n", n);
+
+ if (n < 0)
+ {
+ fz_free(buf);
+ return fz_ferror(file);
+ }
+
+ pos += n;
+
+ if (n < CHUNKSIZE)
+ {
+ newbuf = fz_realloc(buf, pos);
+ if (!newbuf)
+ {
+ fz_free(buf);
+ return fz_outofmem;
+ }
+
+ *bufp = newbuf;
+ *lenp = pos;
+ return nil;
+ }
+ }
+}
+
diff --git a/filter/filter.c b/filter/filter.c
new file mode 100644
index 00000000..8e28a532
--- /dev/null
+++ b/filter/filter.c
@@ -0,0 +1,59 @@
+#include <fitz.h>
+
+fz_error fz_kioneedin = {
+ .msg = {"<ioneedin>"},
+ .func = {"<process>"},
+ .file = {"filter.c"},
+ .line = 0,
+ .frozen = 1
+};
+
+fz_error fz_kioneedout = {
+ .msg = {"<ioneedout>"},
+ .func = {"<process>"},
+ .file = {"filter.c"},
+ .line = 0,
+ .frozen = 1
+};
+
+fz_error fz_kiodone = {
+ .msg = {"<iodone>"},
+ .func = {"<process>"},
+ .file = {"filter.c"},
+ .line = 0,
+ .frozen = 1
+};
+
+fz_error *
+fz_process(fz_filter *f, fz_buffer *in, fz_buffer *out)
+{
+ fz_error *reason;
+ unsigned char *oldrp;
+ unsigned char *oldwp;
+
+ assert(!out->eof);
+
+ oldrp = in->rp;
+ oldwp = out->wp;
+
+ reason = f->process(f, in, out);
+
+ assert(in->rp <= in->wp);
+ assert(out->wp <= out->ep);
+
+ f->consumed = in->rp > oldrp;
+ f->produced = out->wp > oldwp;
+ f->count += out->wp - oldwp;
+
+ if (reason != fz_ioneedin && reason != fz_ioneedout)
+ out->eof = 1;
+
+ return reason;
+}
+
+void
+fz_freefilter(fz_filter *f)
+{
+ f->free(f);
+}
+
diff --git a/filter/flate.c b/filter/flate.c
new file mode 100644
index 00000000..f7046474
--- /dev/null
+++ b/filter/flate.c
@@ -0,0 +1,178 @@
+#include <fitz.h>
+
+#include <zlib.h>
+
+typedef struct fz_flate_s fz_flate;
+
+struct fz_flate_s
+{
+ fz_filter super;
+ z_stream z;
+};
+
+static void *
+zmalloc(void *opaque, unsigned int items, unsigned int size)
+{
+ fz_memorycontext *mctx = (fz_memorycontext*)opaque;
+ return mctx->malloc(mctx, items * size);
+}
+
+fz_error *
+fz_newflated(fz_filter **fp, fz_obj *params)
+{
+ fz_error *eo;
+ int ei;
+
+ FZ_NEWFILTER(fz_flate, f, flated);
+
+ f->z.zalloc = zmalloc;
+ f->z.zfree = (void(*)(void*,void*))fz_currentmemorycontext()->free;
+ f->z.opaque = fz_currentmemorycontext();
+ f->z.next_in = nil;
+ f->z.avail_in = 0;
+
+ ei = inflateInit(&f->z);
+ if (ei != Z_OK) {
+ eo = fz_throw("ioerror: inflateInit: %s", f->z.msg);
+ fz_free(f);
+ return eo;
+ }
+
+ return nil;
+}
+
+void
+fz_freeflated(fz_filter *f)
+{
+ z_streamp zp = &((fz_flate*)f)->z;
+ int err;
+
+ err = inflateEnd(zp);
+ if (err != Z_OK)
+ fprintf(stderr, "inflateEnd: %s", zp->msg);
+
+ fz_free(f);
+}
+
+fz_error *
+fz_processflated(fz_filter *f, fz_buffer *in, fz_buffer *out)
+{
+ z_streamp zp = &((fz_flate*)f)->z;
+ int err;
+
+ if (in->rp == in->wp && !in->eof)
+ return fz_ioneedin;
+ if (out->wp == out->ep)
+ return fz_ioneedout;
+
+ zp->next_in = in->rp;
+ zp->avail_in = in->wp - in->rp;
+
+ zp->next_out = out->wp;
+ zp->avail_out = out->ep - out->wp;
+
+ err = inflate(zp, Z_NO_FLUSH);
+
+ in->rp = in->wp - zp->avail_in;
+ out->wp = out->ep - zp->avail_out;
+
+ if (err == Z_STREAM_END) {
+ out->eof = 1;
+ return fz_iodone;
+ }
+ else if (err == Z_OK) {
+ if (in->rp == in->wp && !in->eof)
+ return fz_ioneedin;
+ if (out->wp == out->ep)
+ return fz_ioneedout;
+ return fz_ioneedin; /* hmm, what's going on here? */
+ }
+ else {
+ return fz_throw("ioerror: inflate: %s", zp->msg);
+ }
+}
+
+fz_error *
+fz_newflatee(fz_filter **fp, fz_obj *params)
+{
+ fz_obj *obj;
+ fz_error *eo;
+ int effort;
+ int ei;
+
+ FZ_NEWFILTER(fz_flate, f, flatee);
+
+ effort = -1;
+
+ if (params) {
+ obj = fz_dictgets(params, "Effort");
+ if (obj) effort = fz_toint(obj);
+ }
+
+ f->z.zalloc = zmalloc;
+ f->z.zfree = (void(*)(void*,void*))fz_currentmemorycontext()->free;
+ f->z.opaque = fz_currentmemorycontext();
+ f->z.next_in = nil;
+ f->z.avail_in = 0;
+
+ ei = deflateInit(&f->z, effort);
+ if (ei != Z_OK) {
+ eo = fz_throw("ioerror: deflateInit: %s", f->z.msg);
+ fz_free(f);
+ return eo;
+ }
+
+ return nil;
+}
+
+void
+fz_freeflatee(fz_filter *f)
+{
+ z_streamp zp = &((fz_flate*)f)->z;
+ int err;
+
+ err = deflateEnd(zp);
+ if (err != Z_OK)
+ fprintf(stderr, "deflateEnd: %s", zp->msg);
+
+ fz_free(f);
+}
+
+fz_error *
+fz_processflatee(fz_filter *f, fz_buffer *in, fz_buffer *out)
+{
+ z_streamp zp = &((fz_flate*)f)->z;
+ int err;
+
+ if (in->rp == in->wp && !in->eof)
+ return fz_ioneedin;
+ if (out->wp == out->ep)
+ return fz_ioneedout;
+
+ zp->next_in = in->rp;
+ zp->avail_in = in->wp - in->rp;
+
+ zp->next_out = out->wp;
+ zp->avail_out = out->ep - out->wp;
+
+ err = deflate(zp, in->eof ? Z_FINISH : Z_NO_FLUSH);
+
+ in->rp = in->wp - zp->avail_in;
+ out->wp = out->ep - zp->avail_out;
+
+ if (err == Z_STREAM_END) {
+ out->eof = 1;
+ return fz_iodone;
+ }
+ else if (err == Z_OK) {
+ if (in->rp == in->wp && !in->eof)
+ return fz_ioneedin;
+ if (out->wp == out->ep)
+ return fz_ioneedout;
+ return fz_ioneedin; /* hmm? */
+ }
+ else {
+ return fz_throw("ioerror: deflate: %s", zp->msg);
+ }
+}
+
diff --git a/filter/jbig2d.c b/filter/jbig2d.c
new file mode 100644
index 00000000..e58a5e49
--- /dev/null
+++ b/filter/jbig2d.c
@@ -0,0 +1,97 @@
+#include <fitz.h>
+
+/* TODO: complete rewrite with error checking and use fitz memctx */
+
+/*
+<rillian> so to use a global_ctx, you run your global data through a normal ctx
+<rillian> then call jbig2_make_global_ctx with the normal context
+<rillian> that does the (currently null) conversion
+<maskros> make_global_ctx after i fed it all the global stream data?
+<rillian> maskros: yes
+<rillian> and you pass the new global ctx object to jbig2_ctx_new() when you
++create the per-page ctx
+*/
+
+#include <inttypes.h>
+#include <jbig2.h>
+
+typedef struct fz_jbig2d_s fz_jbig2d;
+
+struct fz_jbig2d_s
+{
+ fz_filter super;
+ Jbig2Ctx *ctx;
+ Jbig2GlobalCtx *gctx;
+ Jbig2Image *page;
+ int idx;
+};
+
+fz_error *
+fz_newjbig2d(fz_filter **fp, fz_obj *params)
+{
+ FZ_NEWFILTER(fz_jbig2d, d, jbig2d);
+ d->ctx = jbig2_ctx_new(nil, JBIG2_OPTIONS_EMBEDDED, nil, nil, nil);
+ d->page = nil;
+ d->idx = 0;
+ return nil;
+}
+
+void
+fz_freejbig2d(fz_filter *filter)
+{
+ fz_jbig2d *d = (fz_jbig2d*)filter;
+ jbig2_ctx_free(d->ctx);
+ fz_free(d);
+}
+
+fz_error *
+fz_setjbig2dglobalstream(fz_filter *filter, unsigned char *buf, int len)
+{
+ fz_jbig2d *d = (fz_jbig2d*)filter;
+ jbig2_data_in(d->ctx, buf, len);
+ d->gctx = jbig2_make_global_ctx(d->ctx);
+ d->ctx = jbig2_ctx_new(nil, JBIG2_OPTIONS_EMBEDDED, d->gctx, nil, nil);
+ return nil;
+}
+
+fz_error *
+fz_processjbig2d(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_jbig2d *d = (fz_jbig2d*)filter;
+ int len;
+
+ while (1)
+ {
+ if (in->rp == in->wp) {
+ if (!in->eof)
+ return fz_ioneedin;
+
+ if (!d->page) {
+ jbig2_complete_page(d->ctx);
+ d->page = jbig2_page_out(d->ctx);
+ }
+
+ if (out->wp == out->ep)
+ return fz_ioneedout;
+
+ len = out->ep - out->wp;
+ if (d->idx + len > d->page->height * d->page->stride)
+ len = d->page->height * d->page->stride - d->idx;
+ memcpy(out->wp, d->page->data + d->idx, len);
+ out->wp += len;
+ d->idx += len;
+
+ if (d->idx == d->page->height * d->page->stride) {
+ jbig2_release_page(d->ctx, d->page);
+ out->eof = 1;
+ return fz_iodone;
+ }
+ }
+ else {
+ len = in->wp - in->rp;
+ jbig2_data_in(d->ctx, in->rp, len);
+ in->rp += len;
+ }
+ }
+}
+
diff --git a/filter/jpxd.c b/filter/jpxd.c
new file mode 100644
index 00000000..26ec743b
--- /dev/null
+++ b/filter/jpxd.c
@@ -0,0 +1,144 @@
+#include <fitz.h>
+
+/* TODO: bpc */
+
+/*
+ * We use the Jasper JPEG2000 coder for now.
+ */
+
+#include <jasper/jasper.h>
+
+static char *colorspacename(jas_clrspc_t clrspc)
+{
+ switch (jas_clrspc_fam(clrspc))
+ {
+ case JAS_CLRSPC_FAM_UNKNOWN: return "Unknown";
+ case JAS_CLRSPC_FAM_XYZ: return "XYZ";
+ case JAS_CLRSPC_FAM_LAB: return "Lab";
+ case JAS_CLRSPC_FAM_GRAY: return "Gray";
+ case JAS_CLRSPC_FAM_RGB: return "RGB";
+ case JAS_CLRSPC_FAM_YCBCR: return "YCbCr";
+ }
+ return "Unknown";
+}
+
+typedef struct fz_jpxd_s fz_jpxd;
+
+struct fz_jpxd_s
+{
+ fz_filter super;
+ jas_stream_t *stream;
+ jas_image_t *image;
+ int offset;
+ int stage;
+};
+
+fz_error *
+fz_newjpxd(fz_filter **fp, fz_obj *params)
+{
+ int err;
+
+ FZ_NEWFILTER(fz_jpxd, d, jpxd);
+
+ err = jas_init();
+ if (err) {
+ fz_free(d);
+ return fz_throw("ioerror in jpxd: jas_init()");
+ }
+
+ d->stream = jas_stream_memopen(nil, 0);
+ if (!d->stream) {
+ fz_free(d);
+ return fz_throw("ioerror in jpxd: jas_stream_memopen()");
+ }
+
+ d->image = nil;
+ d->offset = 0;
+ d->stage = 0;
+
+ return nil;
+}
+
+void
+fz_freejpxd(fz_filter *filter)
+{
+ fz_jpxd *d = (fz_jpxd*)filter;
+ if (d->stream) jas_stream_close(d->stream);
+ if (d->image) jas_image_destroy(d->image);
+ fz_free(d);
+}
+
+fz_error *
+fz_processjpxd(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_jpxd *d = (fz_jpxd*)filter;
+ int n, bpc, w, h;
+ int i, x, y;
+
+ switch (d->stage)
+ {
+ case 0: goto input;
+ case 1: goto decode;
+ case 2: goto output;
+ }
+
+input:
+ while (in->rp < in->wp) {
+ n = jas_stream_write(d->stream, in->rp, in->wp - in->rp);
+ in->rp += n;
+ }
+
+ if (!in->eof)
+ return fz_ioneedin;
+
+ d->stage = 1;
+
+decode:
+ jas_stream_seek(d->stream, 0, 0);
+
+ d->image = jas_image_decode(d->stream, -1, 0);
+ if (!d->image)
+ return fz_throw("ioerror in jpxd: unable to decode image data");
+
+ fprintf(stderr, "P%c\n# JPX %d x %d n=%d bpc=%d colorspace=%04x %s\n%d %d\n%d\n",
+ jas_image_numcmpts(d->image) == 1 ? '5' : '6',
+
+ jas_image_width(d->image),
+ jas_image_height(d->image),
+ jas_image_numcmpts(d->image),
+ jas_image_cmptprec(d->image, 0),
+ jas_image_clrspc(d->image),
+ colorspacename(jas_image_clrspc(d->image)),
+
+ jas_image_width(d->image),
+ jas_image_height(d->image),
+ (1 << jas_image_cmptprec(d->image, 0)) - 1);
+
+ d->stage = 2;
+
+output:
+ w = jas_image_width(d->image);
+ h = jas_image_height(d->image);
+ n = jas_image_numcmpts(d->image);
+ bpc = jas_image_cmptprec(d->image, 0); /* use precision of first component for all... */
+
+ while (d->offset < w * h)
+ {
+ y = d->offset / w;
+ x = d->offset - y * w;
+
+ /* FIXME bpc != 8 */
+
+ if (out->wp + n >= out->ep)
+ return fz_ioneedout;
+
+ for (i = 0; i < n; i++)
+ *out->wp++ = jas_image_readcmptsample(d->image, i, x, y);
+
+ d->offset ++;
+ }
+
+ out->eof = 1;
+ return fz_iodone;
+}
+
diff --git a/filter/lzwd.c b/filter/lzwd.c
new file mode 100644
index 00000000..1ab47357
--- /dev/null
+++ b/filter/lzwd.c
@@ -0,0 +1,263 @@
+#include <fitz.h>
+
+/* TODO: error checking */
+
+#define noDEBUG 1
+
+#ifdef DEBUG
+#define DPRINT(...) fprintf(stderr, __VA_ARGS__)
+#else
+#define DPRINT(...)
+#endif
+
+enum
+{
+ MINBITS = 9,
+ MAXBITS = 12,
+ NUMCODES = (1 << MAXBITS),
+ LZW_CLEAR = 256,
+ LZW_EOD = 257,
+ LZW_FIRST = 258,
+};
+
+typedef struct lzw_code_s lzw_code;
+
+struct lzw_code_s
+{
+ int prev; /* prev code (in string) */
+ unsigned short length; /* string len, including this token */
+ unsigned char value; /* data value */
+ unsigned char firstchar; /* first token of string */
+};
+
+typedef struct fz_lzwd_s fz_lzwd;
+
+struct fz_lzwd_s
+{
+ fz_filter super;
+
+ int earlychange;
+
+ unsigned int word; /* bits loaded from data */
+ int bidx;
+
+ int resume; /* resume output of code from needout */
+ int codebits; /* num bits/code */
+ int code; /* current code */
+ int oldcode; /* previously recognized code */
+ int nextcode; /* next free entry */
+ lzw_code table[NUMCODES];
+};
+
+fz_error *
+fz_newlzwd(fz_filter **fp, fz_obj *params)
+{
+ int i;
+
+ FZ_NEWFILTER(fz_lzwd, lzw, lzwd);
+
+ lzw->earlychange = 0;
+
+ if (params) {
+ fz_obj *obj;
+ obj = fz_dictgets(params, "EarlyChange");
+ if (obj) lzw->earlychange = fz_toint(obj) != 0;
+ }
+
+ lzw->bidx = 32;
+ lzw->word = 0;
+
+ for (i = 0; i < 256; i++) {
+ lzw->table[i].value = i;
+ lzw->table[i].firstchar = i;
+ lzw->table[i].length = 1;
+ lzw->table[i].prev = -1;
+ }
+
+ for (i = LZW_FIRST; i < NUMCODES; i++) {
+ lzw->table[i].value = 0;
+ lzw->table[i].firstchar = 0;
+ lzw->table[i].length = 0;
+ lzw->table[i].prev = -1;
+ }
+
+ lzw->codebits = MINBITS;
+ lzw->code = -1;
+ lzw->nextcode = LZW_FIRST;
+ lzw->oldcode = -1;
+ lzw->resume = 0;
+
+ return nil;
+}
+
+void
+fz_freelzwd(fz_filter *filter)
+{
+ fz_free(filter);
+}
+
+static inline void eatbits(fz_lzwd *lzw, int nbits)
+{
+ lzw->word <<= nbits;
+ lzw->bidx += nbits;
+}
+
+static inline fz_error * fillbits(fz_lzwd *lzw, fz_buffer *in)
+{
+ while (lzw->bidx >= 8)
+ {
+ if (in->rp + 1 > in->wp)
+ return fz_ioneedin;
+ lzw->bidx -= 8;
+ lzw->word |= *in->rp << lzw->bidx;
+ in->rp ++;
+ }
+ return nil;
+}
+
+fz_error *
+fz_processlzwd(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_lzwd *lzw = (fz_lzwd*)filter;
+ unsigned char *s;
+ int len;
+
+ if (lzw->resume) {
+ lzw->resume = 0;
+ goto output;
+ }
+
+ while (1)
+ {
+ if (fillbits(lzw, in)) {
+ DPRINT("FILL[eof=%d] bidx=%d word=%08x\n", in->eof, lzw->bidx, lzw->word);
+ if (in->eof) {
+ if (lzw->bidx > 32 - lzw->codebits) {
+ out->eof = 1;
+ return fz_iodone;
+ }
+ }
+ else {
+ return fz_ioneedin;
+ }
+ }
+
+ lzw->code = lzw->word >> (32 - lzw->codebits);
+
+ DPRINT("CODE[%d]: %03x (%d)\n", lzw->codebits, lzw->code, lzw->code);
+
+ if (lzw->code == LZW_EOD)
+ {
+ DPRINT("-> LZW_EOD\n");
+
+ eatbits(lzw, lzw->codebits);
+ out->eof = 1;
+ return fz_iodone;
+ }
+
+ if (lzw->code == LZW_CLEAR)
+ {
+ DPRINT("-> LZW_CLEAR\n");
+
+ int oldcodebits = lzw->codebits;
+
+ lzw->codebits = MINBITS;
+ lzw->nextcode = LZW_FIRST;
+
+ lzw->code = lzw->word >> (32 - oldcodebits - MINBITS) & ((1 << MINBITS) - 1);
+
+ DPRINT("CODE[%d]: %03x (%d) [after CLEAR]\n", lzw->codebits, lzw->code, lzw->code);
+
+ if (lzw->code == LZW_EOD) {
+ DPRINT("-> LZW_EOD\n");
+
+ eatbits(lzw, oldcodebits + MINBITS);
+ out->eof = 1;
+ return fz_iodone;
+ }
+
+ DPRINT("-> %d\n", lzw->code);
+
+ if (out->wp + 1 > out->ep)
+ return fz_ioneedout;
+
+ *out->wp++ = lzw->code;
+
+ lzw->oldcode = lzw->code;
+
+ eatbits(lzw, oldcodebits + MINBITS);
+
+ continue;
+ }
+
+ eatbits(lzw, lzw->codebits);
+
+ /* if stream starts without a clear code, oldcode is undefined... */
+ if (lzw->oldcode == -1) {
+ lzw->oldcode = lzw->code;
+ goto output;
+ }
+
+ /* add new entry to the code table */
+ lzw->table[lzw->nextcode].prev = lzw->oldcode;
+ lzw->table[lzw->nextcode].firstchar = lzw->table[lzw->oldcode].firstchar;
+ lzw->table[lzw->nextcode].length = lzw->table[lzw->oldcode].length + 1;
+ if (lzw->code < lzw->nextcode)
+ lzw->table[lzw->nextcode].value = lzw->table[lzw->code].firstchar;
+ else
+ lzw->table[lzw->nextcode].value = lzw->table[lzw->nextcode].firstchar;
+
+ DPRINT("NEW[%d] prev=%d, f=%d, l=%d, v=%d\n",
+ lzw->nextcode,
+ lzw->table[lzw->nextcode].prev,
+ lzw->table[lzw->nextcode].firstchar,
+ lzw->table[lzw->nextcode].length,
+ lzw->table[lzw->nextcode].value);
+
+ lzw->nextcode ++;
+
+ if (lzw->nextcode >= (1 << lzw->codebits) - lzw->earlychange - 1)
+ {
+ lzw->codebits ++;
+ if (lzw->codebits > MAXBITS)
+ lzw->codebits = MAXBITS; /* FIXME */
+ }
+
+ lzw->oldcode = lzw->code;
+
+output:
+ /* code maps to a string, copy to output (in reverse...) */
+ if (lzw->code > 255)
+ {
+ if (out->wp + lzw->table[lzw->code].length > out->ep) {
+ lzw->resume = 1;
+ return fz_ioneedout;
+ }
+
+ len = lzw->table[lzw->code].length;
+ s = out->wp + len;
+
+ DPRINT("OUT code=%d len=%d\n", lzw->code, len);
+ do {
+ DPRINT("-> %d\n", lzw->table[lzw->code].value);
+ *(--s) = lzw->table[lzw->code].value;
+ lzw->code = lzw->table[lzw->code].prev;
+ } while (lzw->code >= 0 && s > out->wp);
+ out->wp += len;
+ }
+
+ /* ... or just a single character */
+ else
+ {
+ if (out->wp + 1 > out->ep) {
+ lzw->resume = 1;
+ return fz_ioneedout;
+ }
+
+ DPRINT("OUT code=%d\n-> %d\n", lzw->code, lzw->code);
+
+ *out->wp++ = lzw->code;
+ }
+ }
+}
+
diff --git a/filter/lzwe.c b/filter/lzwe.c
new file mode 100644
index 00000000..dbb6967b
--- /dev/null
+++ b/filter/lzwe.c
@@ -0,0 +1,257 @@
+#include <fitz.h>
+
+#define noDEBUG 1
+
+#ifdef DEBUG
+#define DPRINT(...) fprintf(stderr, __VA_ARGS__)
+#else
+#define DPRINT(...)
+#endif
+
+enum
+{
+ MINBITS = 9,
+ MAXBITS = 12,
+ MAXBYTES = 2,
+ NUMCODES = (1 << MAXBITS),
+ LZW_CLEAR = 256,
+ LZW_EOD = 257,
+ LZW_FIRST = 258,
+ HSIZE = 9001, /* 91% occupancy (???) */
+ HSHIFT = (13 - 8),
+};
+
+typedef struct lzw_hash_s lzw_hash;
+
+struct lzw_hash_s
+{
+ int hash;
+ int code;
+};
+
+typedef struct fz_lzwe_s fz_lzwe;
+
+struct fz_lzwe_s
+{
+ fz_filter super;
+
+ int earlychange;
+
+ int bidx; /* partial bits used in out->wp */
+ unsigned char bsave; /* partial byte saved between process() calls */
+
+ int resume;
+ int code;
+ int fcode;
+ int hcode;
+
+ int codebits;
+ int oldcode;
+ int nextcode;
+
+ lzw_hash table[HSIZE];
+};
+
+static void
+clearhash(fz_lzwe *lzw)
+{
+ int i;
+ for (i = 0; i < HSIZE; i++)
+ lzw->table[i].hash = -1;
+}
+
+fz_error *
+fz_newlzwe(fz_filter **fp, fz_obj *params)
+{
+ FZ_NEWFILTER(fz_lzwe, lzw, lzwe);
+
+ lzw->earlychange = 0;
+
+ if (params) {
+ fz_obj *obj;
+ obj = fz_dictgets(params, "EarlyChange");
+ if (obj) lzw->earlychange = fz_toint(obj) != 0;
+ }
+
+ lzw->bidx = 0;
+ lzw->bsave = 0;
+
+ lzw->resume = 0;
+ lzw->code = -1;
+ lzw->hcode = -1;
+ lzw->fcode = -1;
+
+ lzw->codebits = MINBITS;
+ lzw->nextcode = LZW_FIRST;
+ lzw->oldcode = -1; /* generates LZW_CLEAR */
+
+ clearhash(lzw);
+
+ return nil;
+}
+
+void
+fz_freelzwe(fz_filter *filter)
+{
+ fz_free(filter);
+}
+
+static void
+putcode(fz_lzwe *lzw, fz_buffer *out, int code)
+{
+ int nbits = lzw->codebits;
+
+ DPRINT("CODE[%d] %d\n", nbits, code);
+
+ while (nbits > 0)
+ {
+ if (lzw->bidx == 0) {
+ *out->wp = 0;
+ }
+
+ /* code does not fit: shift right */
+ if (nbits > (8 - lzw->bidx)) {
+ *out->wp |= code >> (nbits - (8 - lzw->bidx));
+ nbits = nbits - (8 - lzw->bidx);
+ lzw->bidx = 0;
+ out->wp ++;
+ }
+
+ /* shift left */
+ else {
+ *out->wp |= code << ((8 - lzw->bidx) - nbits);
+ lzw->bidx += nbits;
+ if (lzw->bidx == 8) {
+ lzw->bidx = 0;
+ out->wp ++;
+ }
+ nbits = 0;
+ }
+ }
+}
+
+
+static fz_error *
+compress(fz_lzwe *lzw, fz_buffer *in, fz_buffer *out)
+{
+ if (lzw->resume) {
+ lzw->resume = 0;
+ goto resume;
+ }
+
+ /* at start of data, output a clear code */
+ if (lzw->oldcode == -1)
+ {
+ if (out->wp + 3 > out->ep)
+ return fz_ioneedout;
+
+ if (in->rp + 1 > in->wp) {
+ if (in->eof)
+ goto eof;
+ return fz_ioneedin;
+ }
+
+ putcode(lzw, out, LZW_CLEAR);
+
+ lzw->oldcode = *in->rp++;
+ }
+
+begin:
+ while (1)
+ {
+ if (in->rp + 1 > in->wp) {
+ if (in->eof)
+ goto eof;
+ return fz_ioneedin;
+ }
+
+ /* read character */
+ lzw->code = *in->rp++;
+
+ DPRINT("READ %d\n", lzw->code);
+
+ /* hash string + character */
+ lzw->fcode = (lzw->code << MAXBITS) + lzw->oldcode;
+ lzw->hcode = (lzw->code << HSHIFT) ^ lzw->oldcode;
+
+ /* primary hash */
+ if (lzw->table[lzw->hcode].hash == lzw->fcode) {
+ lzw->oldcode = lzw->table[lzw->hcode].code;
+ continue;
+ }
+
+ /* secondary hash */
+ if (lzw->table[lzw->hcode].hash != -1) {
+ int disp = HSIZE - lzw->hcode;
+ if (lzw->hcode == 0)
+ disp = 1;
+ do {
+ lzw->hcode = lzw->hcode - disp;
+ if (lzw->hcode < 0)
+ lzw->hcode += HSIZE;
+ if (lzw->table[lzw->hcode].hash == lzw->fcode) {
+ lzw->oldcode = lzw->table[lzw->hcode].code;
+ goto begin;
+ }
+ } while (lzw->table[lzw->hcode].hash != -1);
+ }
+
+resume:
+ /* new entry: emit code and add to table */
+
+ /* reserve space for this code and an eventual CLEAR code */
+ if (out->wp + 5 > out->ep) {
+ lzw->resume = 1;
+ return fz_ioneedout;
+ }
+
+ putcode(lzw, out, lzw->oldcode);
+
+ lzw->oldcode = lzw->code;
+ lzw->table[lzw->hcode].code = lzw->nextcode;
+ lzw->table[lzw->hcode].hash = lzw->fcode;
+
+ lzw->nextcode ++;
+
+ /* table is full: emit clear code and reset */
+ if (lzw->nextcode == NUMCODES - 1) {
+ putcode(lzw, out, LZW_CLEAR);
+ clearhash(lzw);
+ lzw->nextcode = LZW_FIRST;
+ lzw->codebits = MINBITS;
+ }
+
+ /* check if next entry will be too big for the code size */
+ else if (lzw->nextcode >= (1 << lzw->codebits) - lzw->earlychange) {
+ lzw->codebits ++;
+ }
+ }
+
+eof:
+ if (out->wp + 5 > out->ep)
+ return fz_ioneedout;
+
+ putcode(lzw, out, lzw->oldcode);
+ putcode(lzw, out, LZW_EOD);
+
+ out->eof = 1;
+ return fz_iodone;
+}
+
+fz_error *
+fz_processlzwe(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_lzwe *lzw = (fz_lzwe*)filter;
+ fz_error *err;
+
+ /* restore partial bits */
+ *out->wp = lzw->bsave;
+
+ err = compress(lzw, in, out);
+
+ /* save partial bits */
+ lzw->bsave = *out->wp;
+
+ return err;
+}
+
diff --git a/filter/null.c b/filter/null.c
new file mode 100644
index 00000000..a4af03b1
--- /dev/null
+++ b/filter/null.c
@@ -0,0 +1,51 @@
+#include <fitz.h>
+
+typedef struct fz_nullfilter_s fz_nullfilter;
+
+struct fz_nullfilter_s
+{
+ fz_filter super;
+ int len;
+ int cur;
+};
+
+fz_error *
+fz_newnullfilter(fz_filter **fp, int len)
+{
+ FZ_NEWFILTER(fz_nullfilter, f, nullfilter);
+ f->len = len;
+ f->cur = 0;
+ return nil;
+}
+
+void
+fz_freenullfilter(fz_filter *f)
+{
+ fz_free(f);
+}
+
+fz_error *
+fz_processnullfilter(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_nullfilter *f = (fz_nullfilter*)filter;
+ int n;
+
+ n = MIN(MIN(in->wp - in->rp, out->ep - out->wp), f->len - f->cur);
+
+ if (n) {
+ memcpy(out->wp, in->rp, n);
+ in->rp += n;
+ out->wp += n;
+ f->cur += n;
+ }
+
+ if (f->cur == f->len)
+ return fz_iodone;
+ if (in->rp == in->wp)
+ return fz_ioneedin;
+ if (out->wp == out->ep)
+ return fz_ioneedout;
+
+ return fz_throw("braindead programmer in nullfilter");
+}
+
diff --git a/filter/pipeline.c b/filter/pipeline.c
new file mode 100644
index 00000000..ef0412c0
--- /dev/null
+++ b/filter/pipeline.c
@@ -0,0 +1,128 @@
+#include <fitz.h>
+
+#define noDEBUG 1
+
+typedef struct fz_pipeline_s fz_pipeline;
+
+fz_error * fz_processpipeline(fz_filter *filter, fz_buffer *in, fz_buffer *out);
+
+struct fz_pipeline_s
+{
+ fz_filter super;
+ fz_filter *head;
+ fz_buffer *buffer;
+ fz_filter *tail;
+ int tailneedsin;
+};
+
+fz_error *
+fz_chainpipeline(fz_filter **fp, fz_filter *head, fz_filter *tail, fz_buffer *buf)
+{
+ FZ_NEWFILTER(fz_pipeline, p, pipeline);
+ p->head = head;
+ p->tail = tail;
+ p->tailneedsin = 1;
+ p->buffer = buf;
+ return nil;
+}
+
+void
+fz_unchainpipeline(fz_filter *filter, fz_filter **oldfp, fz_buffer **oldbp)
+{
+ fz_pipeline *p = (fz_pipeline*)filter;
+ *oldfp = p->head;
+ *oldbp = p->buffer;
+ fz_freefilter(p->tail);
+ fz_free(p);
+}
+
+fz_error *
+fz_newpipeline(fz_filter **fp, fz_filter *head, fz_filter *tail)
+{
+ fz_error *err;
+
+ FZ_NEWFILTER(fz_pipeline, p, pipeline);
+ p->head = head;
+ p->tail = tail;
+ p->tailneedsin = 1;
+
+ err = fz_newbuffer(&p->buffer, FZ_BUFSIZE);
+ if (err) { fz_free(p); return err; }
+
+ return nil;
+}
+
+void
+fz_freepipeline(fz_filter *filter)
+{
+ fz_pipeline *p = (fz_pipeline*)filter;
+ fz_freefilter(p->head);
+ fz_freefilter(p->tail);
+ fz_freebuffer(p->buffer);
+ fz_free(p);
+}
+
+fz_error *
+fz_processpipeline(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_pipeline *p = (fz_pipeline*)filter;
+ fz_error *e;
+
+ if (p->buffer->eof)
+ goto tail;
+
+ if (p->tailneedsin && p->head->produced)
+ goto tail;
+
+head:
+ e = fz_process(p->head, in, p->buffer);
+
+ if (e == fz_ioneedin)
+ return fz_ioneedin;
+
+ else if (e == fz_ioneedout)
+ {
+ if (p->tailneedsin && !p->head->produced)
+ {
+ fz_error *be = nil;
+ if (p->buffer->rp > p->buffer->bp)
+ be = fz_rewindbuffer(p->buffer);
+ else
+ be = fz_growbuffer(p->buffer);
+ if (be)
+ return be;
+ goto head;
+ }
+ goto tail;
+ }
+
+ else if (e == fz_iodone)
+ goto tail;
+
+ else
+ return e;
+
+tail:
+ e = fz_process(p->tail, p->buffer, out);
+
+ if (e == fz_ioneedin)
+ {
+ if (p->buffer->eof)
+ return fz_throw("ioerror: premature eof in pipeline");
+ p->tailneedsin = 1;
+ goto head;
+ }
+
+ else if (e == fz_ioneedout)
+ {
+ p->tailneedsin = 0;
+ return fz_ioneedout;
+ }
+
+ else if (e == fz_iodone)
+ return fz_iodone;
+
+ else
+ return e;
+}
+
diff --git a/filter/predict.c b/filter/predict.c
new file mode 100644
index 00000000..d5f1f54d
--- /dev/null
+++ b/filter/predict.c
@@ -0,0 +1,239 @@
+#include <fitz.h>
+
+/* TODO: check if this works with 16bpp images */
+
+enum { MAXC = 16 };
+
+typedef struct fz_predict_s fz_predict;
+
+struct fz_predict_s
+{
+ fz_filter super;
+
+ int predictor;
+ int columns;
+ int colors;
+ int bpc;
+
+ int stride;
+ int bpp;
+ unsigned char *ref;
+
+ int encode;
+};
+
+fz_error *
+fz_newpredict(fz_filter **fp, fz_obj *params, int encode)
+{
+ fz_obj *obj;
+
+ FZ_NEWFILTER(fz_predict, p, predict);
+
+ p->encode = encode;
+
+ p->predictor = 1;
+ p->columns = 1;
+ p->colors = 1;
+ p->bpc = 8;
+
+ obj = fz_dictgets(params, "Predictor");
+ if (obj) p->predictor = fz_toint(obj);
+
+ obj = fz_dictgets(params, "Columns");
+ if (obj) p->columns = fz_toint(obj);
+
+ obj = fz_dictgets(params, "Colors");
+ if (obj) p->colors = fz_toint(obj);
+
+ obj = fz_dictgets(params, "BitsPerComponent");
+ if (obj) p->bpc = fz_toint(obj);
+
+ p->stride = (p->bpc * p->colors * p->columns + 7) / 8;
+ p->bpp = (p->bpc * p->colors + 7) / 8;
+
+ if (p->predictor >= 10) {
+ p->ref = fz_malloc(p->stride);
+ if (!p->ref) { fz_free(p); return fz_outofmem; }
+ memset(p->ref, 0, p->stride);
+ }
+ else {
+ p->ref = nil;
+ }
+
+ return nil;
+}
+
+void
+fz_freepredict(fz_filter *filter)
+{
+ fz_predict *p = (fz_predict*)filter;
+ fz_free(p->ref);
+ fz_free(p);
+}
+
+static inline int
+getcomponent(unsigned char *buf, int x, int bpc)
+{
+ switch (bpc)
+ {
+ case 1: return buf[x / 8] >> (7 - (x % 8)) & 0x01;
+ case 2: return buf[x / 4] >> ((3 - (x % 4)) * 2) & 0x03;
+ case 4: return buf[x / 2] >> ((1 - (x % 2)) * 4) & 0x0f;
+ case 8: return buf[x];
+ }
+ return 0;
+}
+
+static inline void
+putcomponent(unsigned char *buf, int x, int bpc, int value)
+{
+ switch (bpc)
+ {
+ case 1: buf[x / 8] |= value << (7 - (x % 8)); break;
+ case 2: buf[x / 4] |= value << ((3 - (x % 4)) * 2); break;
+ case 4: buf[x / 2] |= value << ((1 - (x % 2)) * 4); break;
+ case 8: buf[x] = value; break;
+ }
+}
+
+static inline int
+paeth(int a, int b, int c)
+{
+ /* The definitions of ac and bc are correct, not a typo. */
+ int ac = b - c, bc = a - c, abcc = ac + bc;
+ int pa = (ac < 0 ? -ac : ac);
+ int pb = (bc < 0 ? -bc : bc);
+ int pc = (abcc < 0 ? -abcc : abcc);
+ return pa <= pb && pa <= pc ? a : pb <= pc ? b : c;
+}
+
+static inline void
+none(fz_predict *p, unsigned char *in, unsigned char *out)
+{
+ memcpy(out, in, p->stride);
+}
+
+static void
+tiff(fz_predict *p, unsigned char *in, unsigned char *out)
+{
+ int left[MAXC];
+ int i, k;
+
+ for (k = 0; k < p->colors; k++)
+ left[k] = 0;
+
+ for (i = 0; i < p->columns; i++) {
+ for (k = 0; k < p->colors; k++) {
+ int a = getcomponent(in, i * p->colors + k, p->bpc);
+ int b = p->encode ? a - left[k] : a + left[k];
+ int c = b % (1 << p->bpc);
+ putcomponent(out, i * p->colors + k, p->bpc, c);
+ left[k] = p->encode ? a : c;
+ }
+ }
+}
+
+static void
+png(fz_predict *p, unsigned char *in, unsigned char *out, int predictor)
+{
+ int upleft[MAXC], left[MAXC], i, k;
+
+ for (k = 0; k < p->bpp; k++) {
+ left[k] = 0;
+ upleft[k] = 0;
+ }
+
+ if (p->encode)
+ {
+ for (k = 0, i = 0; i < p->stride; k = (k + 1) % p->bpp, i ++)
+ {
+ switch (predictor)
+ {
+ case 0: out[i] = in[i]; break;
+ case 1: out[i] = in[i] - left[k]; break;
+ case 2: out[i] = in[i] - p->ref[i]; break;
+ case 3: out[i] = in[i] - (left[k] + p->ref[i]) / 2; break;
+ case 4: out[i] = in[i] - paeth(left[k], p->ref[i], upleft[k]); break;
+ }
+ left[k] = in[i];
+ upleft[k] = p->ref[i];
+ }
+ }
+
+ else
+ {
+ for (k = 0, i = 0; i < p->stride; k = (k + 1) % p->bpp, i ++)
+ {
+ switch (predictor)
+ {
+ case 0: out[i] = in[i]; break;
+ case 1: out[i] = in[i] + left[k]; break;
+ case 2: out[i] = in[i] + p->ref[i]; break;
+ case 3: out[i] = in[i] + (left[k] + p->ref[i]) / 2; break;
+ case 4: out[i] = in[i] + paeth(left[k], p->ref[i], upleft[k]); break;
+ }
+ left[k] = out[i];
+ upleft[k] = p->ref[i];
+ }
+ }
+}
+
+fz_error *
+fz_processpredict(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_predict *dec = (fz_predict*)filter;
+ int ispng = dec->predictor >= 10;
+ int predictor;
+
+ while (1)
+ {
+ if (in->rp + dec->stride + (!dec->encode && ispng) > in->wp) {
+ if (in->eof)
+ return fz_iodone;
+ return fz_ioneedin;
+ }
+
+ if (out->wp + dec->stride + (dec->encode && ispng) > out->ep)
+ return fz_ioneedout;
+
+ if (dec->predictor == 1) {
+ none(dec, in->rp, out->wp);
+ }
+ else if (dec->predictor == 2) {
+ if (dec->bpc != 8)
+ memset(out->wp, 0, dec->stride);
+ tiff(dec, in->rp, out->wp);
+ }
+ else {
+ if (dec->encode) {
+ predictor = dec->predictor - 10;
+ if (predictor < 0 || predictor > 4)
+ predictor = 1;
+ *out->wp ++ = predictor;
+ }
+ else {
+ predictor = *in->rp++;
+ }
+ png(dec, in->rp, out->wp, predictor);
+ }
+
+ if (dec->ref)
+ memcpy(dec->ref, out->wp, dec->stride);
+
+ in->rp += dec->stride;
+ out->wp += dec->stride;
+ }
+}
+
+fz_error *
+fz_newpredictd(fz_filter **fp, fz_obj *params)
+{
+ return fz_newpredict(fp, params, 0);
+}
+
+fz_error *
+fz_newpredicte(fz_filter **fp, fz_obj *params)
+{
+ return fz_newpredict(fp, params, 1);
+}
+
diff --git a/filter/rld.c b/filter/rld.c
new file mode 100644
index 00000000..a66e0f94
--- /dev/null
+++ b/filter/rld.c
@@ -0,0 +1,67 @@
+#include <fitz.h>
+
+fz_error *
+fz_newrld(fz_filter **fp, fz_obj *params)
+{
+ FZ_NEWFILTER(fz_filter, f, rld);
+ return nil;
+}
+
+void
+fz_freerld(fz_filter *rld)
+{
+ fz_free(rld);
+}
+
+fz_error *
+fz_processrld(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ int run, i;
+ unsigned char c;
+
+ while (1)
+ {
+ if (in->rp == in->wp)
+ return fz_ioneedin;
+
+ if (out->wp == out->ep)
+ return fz_ioneedout;
+
+ run = *in->rp++;
+
+ if (run == 128) {
+ out->eof = 1;
+ return fz_iodone;
+ }
+
+ else if (run < 128) {
+ run = run + 1;
+ if (in->rp + run > in->wp) {
+ in->rp --;
+ return fz_ioneedin;
+ }
+ if (out->wp + run > out->ep) {
+ in->rp --;
+ return fz_ioneedout;
+ }
+ for (i = 0; i < run; i++)
+ *out->wp++ = *in->rp++;
+ }
+
+ else if (run > 128) {
+ run = 257 - run;
+ if (in->rp + 1 > in->wp) {
+ in->rp --;
+ return fz_ioneedin;
+ }
+ if (out->wp + run > out->ep) {
+ in->rp --;
+ return fz_ioneedout;
+ }
+ c = *in->rp++;
+ for (i = 0; i < run; i++)
+ *out->wp++ = c;
+ }
+ }
+}
+
diff --git a/filter/rle.c b/filter/rle.c
new file mode 100644
index 00000000..0e611918
--- /dev/null
+++ b/filter/rle.c
@@ -0,0 +1,238 @@
+#include <fitz.h>
+
+/* TODO: rewrite!
+ * make it non-optimal or something,
+ * just not this horrid mess...
+ */
+
+#define noDEBUG
+
+typedef struct fz_rle_s fz_rle;
+
+struct fz_rle_s
+{
+ fz_filter super;
+ int reclen;
+ int curlen;
+ int state;
+ int run;
+ unsigned char buf[128];
+};
+
+enum {
+ ZERO,
+ ONE,
+ DIFF,
+ SAME,
+ END
+};
+
+fz_error *
+fz_newrle(fz_filter **fp, fz_obj *params)
+{
+ FZ_NEWFILTER(fz_rle, enc, rle);
+
+ if (params)
+ enc->reclen = fz_toint(params);
+ else
+ enc->reclen = 0;
+
+ enc->curlen = 0;
+ enc->state = ZERO;
+ enc->run = 0;
+
+ return nil;
+}
+
+void
+fz_freerle(fz_filter *enc)
+{
+ fz_free(enc);
+}
+
+static fz_error *
+putone(fz_rle *enc, fz_buffer *in, fz_buffer *out)
+{
+ if (out->wp + 2 >= out->ep)
+ return fz_ioneedout;
+
+#ifdef DEBUG
+fprintf(stderr, "one '%c'\n", enc->buf[0]);
+#endif
+
+ *out->wp++ = 0;
+ *out->wp++ = enc->buf[0];
+
+ return nil;
+}
+
+static fz_error *
+putsame(fz_rle *enc, fz_buffer *in, fz_buffer *out)
+{
+ if (out->wp + enc->run >= out->ep)
+ return fz_ioneedout;
+
+#ifdef DEBUG
+fprintf(stderr, "same %d x '%c'\n", enc->run, enc->buf[0]);
+#endif
+
+ *out->wp++ = 257 - enc->run;
+ *out->wp++ = enc->buf[0];
+ return nil;
+}
+
+static fz_error *
+putdiff(fz_rle *enc, fz_buffer *in, fz_buffer *out)
+{
+ int i;
+ if (out->wp + enc->run >= out->ep)
+ return fz_ioneedout;
+
+#ifdef DEBUG
+fprintf(stderr, "diff %d\n", enc->run);
+#endif
+
+ *out->wp++ = enc->run - 1;
+ for (i = 0; i < enc->run; i++)
+ *out->wp++ = enc->buf[i];
+ return nil;
+}
+
+static fz_error *
+puteod(fz_rle *enc, fz_buffer *in, fz_buffer *out)
+{
+ if (out->wp + 1 >= out->ep)
+ return fz_ioneedout;
+
+#ifdef DEBUG
+fprintf(stderr, "eod\n");
+#endif
+
+ *out->wp++ = 128;
+ return nil;
+}
+
+static fz_error *
+savebuf(fz_rle *enc, fz_buffer *in, fz_buffer *out)
+{
+ switch (enc->state)
+ {
+ case ZERO: return nil;
+ case ONE: return putone(enc, in, out);
+ case SAME: return putsame(enc, in, out);
+ case DIFF: return putdiff(enc, in, out);
+ case END: return puteod(enc, in, out);
+ default: assert(!"invalid state in rle"); return nil;
+ }
+}
+
+fz_error *
+fz_processrle(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_rle *enc = (fz_rle*)filter;
+ fz_error *err;
+ unsigned char c;
+
+ while (1)
+ {
+
+ if (enc->reclen && enc->curlen == enc->reclen) {
+ err = savebuf(enc, in, out);
+ if (err) return err;
+#ifdef DEBUG
+fprintf(stderr, "--record--\n");
+#endif
+ enc->state = ZERO;
+ enc->curlen = 0;
+ }
+
+ if (in->rp == in->wp) {
+ if (in->eof) {
+ if (enc->state != END) {
+ err = savebuf(enc, in, out);
+ if (err) return err;
+ }
+ enc->state = END;
+ }
+ else
+ return fz_ioneedin;
+ }
+
+ c = *in->rp;
+
+ switch (enc->state)
+ {
+ case ZERO:
+ enc->state = ONE;
+ enc->run = 1;
+ enc->buf[0] = c;
+ break;
+
+ case ONE:
+ enc->state = DIFF;
+ enc->run = 2;
+ enc->buf[1] = c;
+ break;
+
+ case DIFF:
+ /* out of space */
+ if (enc->run == 128) {
+ err = putdiff(enc, in, out);
+ if (err) return err;
+
+ enc->state = ONE;
+ enc->run = 1;
+ enc->buf[0] = c;
+ }
+
+ /* run of three that are the same */
+ else if ((enc->run > 1) &&
+ (c == enc->buf[enc->run - 1]) &&
+ (c == enc->buf[enc->run - 2]))
+ {
+ if (enc->run >= 3) {
+ enc->run -= 2; /* skip prev two for diff run */
+ err = putdiff(enc, in, out);
+ if (err) return err;
+ }
+
+ enc->state = SAME;
+ enc->run = 3;
+ enc->buf[0] = c;
+ }
+
+ /* keep on collecting */
+ else {
+ enc->buf[enc->run++] = c;
+ }
+ break;
+
+ case SAME:
+ if (enc->run == 128 || c != enc->buf[0]) {
+ err = putsame(enc, in, out);
+ if (err) return err;
+
+ enc->state = ONE;
+ enc->run = 1;
+ enc->buf[0] = c;
+ }
+ else {
+ enc->run ++;
+ }
+ break;
+
+ case END:
+ err = puteod(enc, in, out);
+ if (err) return err;
+
+ out->eof = 1;
+ return fz_iodone;
+ }
+
+ in->rp ++;
+
+ enc->curlen ++;
+
+ }
+}
+
diff --git a/include/fitz.h b/include/fitz.h
new file mode 100644
index 00000000..20c105db
--- /dev/null
+++ b/include/fitz.h
@@ -0,0 +1,30 @@
+#ifdef _FITZ_H_
+#error "fitz.h must only be included once"
+#endif
+#define _FITZ_H_
+
+#include "fitz/sysdep.h"
+
+#include "fitz/base.h"
+#include "fitz/math.h"
+#include "fitz/geometry.h"
+#include "fitz/hash.h"
+#include "fitz/crypt.h"
+
+#include "fitz/object.h"
+
+#include "fitz/filter.h"
+#include "fitz/file.h"
+
+#include "fitz/cmap.h"
+#include "fitz/font.h"
+
+#include "fitz/tree.h"
+#include "fitz/path.h"
+#include "fitz/text.h"
+#include "fitz/image.h"
+
+#include "fitz/pixmap.h"
+#include "fitz/scanconv.h"
+#include "fitz/render.h"
+
diff --git a/include/fitz/base.h b/include/fitz/base.h
new file mode 100644
index 00000000..36fe0aeb
--- /dev/null
+++ b/include/fitz/base.h
@@ -0,0 +1,59 @@
+#undef nil
+#define nil ((void*)0)
+
+#undef offsetof
+#define offsetof(s, m) (unsigned long)(&(((s*)0)->m))
+
+#undef ABS
+#define ABS(x) ( (x) < 0 ? -(x) : (x) )
+
+#undef MAX
+#define MAX(a,b) ( (a) > (b) ? (a) : (b) )
+
+#undef MIN
+#define MIN(a,b) ( (a) < (b) ? (a) : (b) )
+
+#undef CLAMP
+#define CLAMP(x,a,b) ( (x) > (b) ? (b) : ( (x) < (a) ? (a) : (x) ) )
+
+#define MAX4(a,b,c,d) MAX(MAX(a,b), MAX(c,d))
+#define MIN4(a,b,c,d) MIN(MIN(a,b), MIN(c,d))
+
+#define STRIDE(n, bcp) (((bpc) * (n) + 7) / 8)
+
+typedef struct fz_error_s fz_error;
+
+struct fz_error_s
+{
+ char msg[184];
+ char file[32];
+ char func[32];
+ int line;
+ int frozen;
+};
+
+#define fz_outofmem (&fz_koutofmem)
+extern fz_error fz_koutofmem;
+
+#define fz_throw(fmt, ...) fz_throw0(__func__, __FILE__, __LINE__, fmt, ## __VA_ARGS__)
+fz_error *fz_throw0(const char *func, const char *file, int line, char *fmt, ...);
+void fz_warn(char *fmt, ...);
+void fz_abort(fz_error *eo);
+void fz_freeerror(fz_error *eo);
+
+typedef struct fz_memorycontext_s fz_memorycontext;
+
+struct fz_memorycontext_s
+{
+ void * (*malloc)(fz_memorycontext *, int);
+ void * (*realloc)(fz_memorycontext *, void *, int);
+ void (*free)(fz_memorycontext *, void *);
+};
+
+fz_memorycontext *fz_currentmemorycontext(void);
+void fz_setmemorycontext(fz_memorycontext *memorycontext);
+
+void *fz_malloc(int n);
+void *fz_realloc(void *p, int n);
+void fz_free(void *p);
+
diff --git a/include/fitz/cmap.h b/include/fitz/cmap.h
new file mode 100644
index 00000000..ddd37149
--- /dev/null
+++ b/include/fitz/cmap.h
@@ -0,0 +1,25 @@
+typedef struct fz_cmap_s fz_cmap;
+
+fz_error *fz_newcmap(fz_cmap **cmapp);
+void fz_debugcmap(fz_cmap *cmap);
+void fz_freecmap(fz_cmap *cmap);
+
+char *fz_getcmapname(fz_cmap *cmap);
+void fz_setcmapname(fz_cmap *cmap, char *name);
+char *fz_getusecmapname(fz_cmap *cmap);
+void fz_setusecmapname(fz_cmap *cmap, char *usecmap);
+void fz_setusecmap(fz_cmap *cmap, fz_cmap *usecmap);
+fz_cmap *fz_getusecmap(fz_cmap *cmap);
+void fz_setwmode(fz_cmap *cmap, int wmode);
+int fz_getwmode(fz_cmap *cmap);
+
+fz_error *fz_addcodespacerange(fz_cmap *cmap, unsigned lo, unsigned hi, int n);
+
+fz_error *fz_setcidlookup(fz_cmap *cmap, int map[256]);
+
+fz_error *fz_addcidrange(fz_cmap *cmap, int srclo, int srchi, int dstlo);
+fz_error *fz_endcidrange(fz_cmap *cmap);
+
+int fz_lookupcid(fz_cmap *cmap, int cpt);
+char *fz_decodecpt(fz_cmap *cmap, unsigned char *s, int *cpt);
+
diff --git a/include/fitz/crypt.h b/include/fitz/crypt.h
new file mode 100644
index 00000000..f6365c4e
--- /dev/null
+++ b/include/fitz/crypt.h
@@ -0,0 +1,30 @@
+/* md5 digests */
+
+typedef struct fz_md5_s fz_md5;
+
+struct fz_md5_s
+{
+ unsigned long state[4];
+ unsigned long count[2];
+ unsigned char buffer[64];
+};
+
+void fz_md5init(fz_md5 *state);
+void fz_md5update(fz_md5 *state, unsigned char *input, unsigned inlen);
+void fz_md5final(fz_md5 *state, unsigned char digest[16]);
+
+/* arc4 crypto */
+
+typedef struct fz_arc4_s fz_arc4;
+
+struct fz_arc4_s
+{
+ unsigned x;
+ unsigned y;
+ unsigned char state[256];
+};
+
+void fz_arc4init(fz_arc4 *state, unsigned char *key, unsigned len);
+unsigned char fz_arc4next(fz_arc4 *state);
+void fz_arc4encrypt(fz_arc4 *state, unsigned char *dest, unsigned char *src, unsigned len);
+
diff --git a/include/fitz/file.h b/include/fitz/file.h
new file mode 100644
index 00000000..e687e572
--- /dev/null
+++ b/include/fitz/file.h
@@ -0,0 +1,33 @@
+typedef struct fz_file_s fz_file;
+
+struct fz_file_s
+{
+ int mode; /* O_RDONLY or O_WRONLY */
+ int fd;
+ int depth;
+ fz_filter *filter;
+ fz_buffer *in;
+ fz_buffer *out;
+ fz_error *error;
+};
+
+fz_error *fz_openfile(fz_file **filep, char *path, int mode);
+fz_error *fz_pushfilter(fz_file *file, fz_filter *filter);
+void fz_popfilter(fz_file *file);
+void fz_closefile(fz_file *file);
+
+int fz_seek(fz_file *f, int ofs);
+int fz_tell(fz_file *f);
+
+int fz_readbyte(fz_file *f);
+int fz_peekbyte(fz_file *f);
+int fz_readline(fz_file *f, char *buf, int n);
+int fz_read(fz_file *f, char *buf, int n);
+
+int fz_write(fz_file *f, char *buf, int n);
+int fz_flush(fz_file *f);
+
+fz_error *fz_readfile(unsigned char **bufp, int *lenp, fz_file *file);
+
+fz_error *fz_ferror(fz_file *f);
+
diff --git a/include/fitz/filter.h b/include/fitz/filter.h
new file mode 100644
index 00000000..6f234143
--- /dev/null
+++ b/include/fitz/filter.h
@@ -0,0 +1,80 @@
+typedef struct fz_filter_s fz_filter;
+typedef struct fz_buffer_s fz_buffer;
+
+#define FZ_BUFSIZE (32 * 1024)
+
+#define fz_ioneedin (&fz_kioneedin)
+#define fz_ioneedout (&fz_kioneedout)
+#define fz_iodone (&fz_kiodone)
+
+extern fz_error fz_kioneedin;
+extern fz_error fz_kioneedout;
+extern fz_error fz_kiodone;
+
+#define FZ_NEWFILTER(TYPE,VAR,NAME) \
+ fz_error * fz_process ## NAME (fz_filter*,fz_buffer*,fz_buffer*); \
+ void fz_free ## NAME (fz_filter*); \
+ TYPE *VAR; \
+ *fp = fz_malloc(sizeof(TYPE)); \
+ if (!*fp) return fz_outofmem; \
+ (*fp)->process = fz_process ## NAME ; \
+ (*fp)->free = fz_free ## NAME ; \
+ (*fp)->consumed = 0; \
+ (*fp)->produced = 0; \
+ (*fp)->count = 0; \
+ VAR = (TYPE*) *fp
+
+struct fz_filter_s
+{
+ fz_error* (*process)(fz_filter *filter, fz_buffer *in, fz_buffer *out);
+ void (*free)(fz_filter *filter);
+ int consumed;
+ int produced;
+ int count;
+};
+
+struct fz_buffer_s
+{
+ unsigned char *bp;
+ unsigned char *rp;
+ unsigned char *wp;
+ unsigned char *ep;
+ int eof;
+};
+
+fz_error *fz_process(fz_filter *f, fz_buffer *in, fz_buffer *out);
+void fz_freefilter(fz_filter *f);
+
+fz_error *fz_newnullfilter(fz_filter **fp, int len);
+fz_error *fz_newarc4filter(fz_filter **fp, unsigned char *key, unsigned keylen);
+fz_error *fz_newpipeline(fz_filter **fp, fz_filter *head, fz_filter *tail);
+
+fz_error *fz_chainpipeline(fz_filter **fp, fz_filter *head, fz_filter *tail, fz_buffer *buf);
+void fz_unchainpipeline(fz_filter *pipe, fz_filter **oldfp, fz_buffer **oldbp);
+
+fz_error *fz_newbuffer(fz_buffer **bufp, int size);
+fz_error *fz_rewindbuffer(fz_buffer *buf);
+fz_error *fz_growbuffer(fz_buffer *buf);
+void fz_freebuffer(fz_buffer *buf);
+
+fz_error *fz_newa85d(fz_filter **filterp, fz_obj *param);
+fz_error *fz_newa85e(fz_filter **filterp, fz_obj *param);
+fz_error *fz_newahxd(fz_filter **filterp, fz_obj *param);
+fz_error *fz_newahxe(fz_filter **filterp, fz_obj *param);
+fz_error *fz_newrld(fz_filter **filterp, fz_obj *param);
+fz_error *fz_newrle(fz_filter **filterp, fz_obj *param);
+fz_error *fz_newdctd(fz_filter **filterp, fz_obj *param);
+fz_error *fz_newdcte(fz_filter **filterp, fz_obj *param);
+fz_error *fz_newfaxd(fz_filter **filterp, fz_obj *param);
+fz_error *fz_newfaxe(fz_filter **filterp, fz_obj *param);
+fz_error *fz_newflated(fz_filter **filterp, fz_obj *param);
+fz_error *fz_newflatee(fz_filter **filterp, fz_obj *param);
+fz_error *fz_newlzwd(fz_filter **filterp, fz_obj *param);
+fz_error *fz_newlzwe(fz_filter **filterp, fz_obj *param);
+fz_error *fz_newpredictd(fz_filter **filterp, fz_obj *param);
+fz_error *fz_newpredicte(fz_filter **filterp, fz_obj *param);
+fz_error *fz_newjbig2d(fz_filter **filterp, fz_obj *param);
+fz_error *fz_newjpxd(fz_filter **filterp, fz_obj *param);
+
+void fz_pushbackahxd(fz_filter *filter, fz_buffer *in, fz_buffer *out, int n);
+
diff --git a/include/fitz/font.h b/include/fitz/font.h
new file mode 100644
index 00000000..743d87fa
--- /dev/null
+++ b/include/fitz/font.h
@@ -0,0 +1,63 @@
+typedef struct fz_font_s fz_font;
+typedef struct fz_hmtx_s fz_hmtx;
+typedef struct fz_vmtx_s fz_vmtx;
+typedef struct fz_glyph_s fz_glyph;
+typedef struct fz_glyphcache_s fz_glyphcache;
+
+struct fz_hmtx_s
+{
+ unsigned short c;
+ short w;
+};
+
+struct fz_vmtx_s
+{
+ unsigned short c;
+ short x;
+ short y;
+ short w;
+};
+
+struct fz_font_s
+{
+ char name[32];
+
+ fz_error* (*render)(fz_glyph*, fz_font*, int, fz_matrix);
+ void (*free)(fz_font *);
+
+ int wmode;
+ fz_irect bbox;
+
+ int nhmtx, hmtxcap;
+ fz_hmtx dhmtx;
+ fz_hmtx *hmtx;
+
+ int nvmtx, vmtxcap;
+ fz_vmtx dvmtx;
+ fz_vmtx *vmtx;
+};
+
+struct fz_glyph_s
+{
+ int w, h, lsb, top;
+ unsigned char *bitmap;
+};
+
+void fz_initfont(fz_font *font, char *name);
+void fz_freefont(fz_font *font);
+void fz_debugfont(fz_font *font);
+void fz_setfontwmode(fz_font *font, int wmode);
+void fz_setfontbbox(fz_font *font, int xmin, int ymin, int xmax, int ymax);
+void fz_setdefaulthmtx(fz_font *font, int w);
+void fz_setdefaultvmtx(fz_font *font, int y, int w);
+fz_error *fz_addhmtx(fz_font *font, int gid, int w);
+fz_error *fz_addvmtx(fz_font *font, int gid, int x, int y, int w);
+fz_error *fz_endhmtx(fz_font *font);
+fz_error *fz_endvmtx(fz_font *font);
+fz_hmtx fz_gethmtx(fz_font *font, int gid);
+fz_vmtx fz_getvmtx(fz_font *font, int gid);
+
+fz_error *fz_newglyphcache(fz_glyphcache **arenap, int slots, int size);
+fz_error *fz_renderglyph(fz_glyphcache*, fz_glyph*, fz_font*, int, fz_matrix);
+void fz_freeglyphcache(fz_glyphcache *);
+
diff --git a/include/fitz/geometry.h b/include/fitz/geometry.h
new file mode 100644
index 00000000..588aa562
--- /dev/null
+++ b/include/fitz/geometry.h
@@ -0,0 +1,56 @@
+typedef struct fz_matrix_s fz_matrix;
+typedef struct fz_point_s fz_point;
+typedef struct fz_rect_s fz_rect;
+typedef struct fz_ipoint_s fz_ipoint;
+typedef struct fz_irect_s fz_irect;
+
+/*
+ / a b 0 \
+ | c d 0 |
+ \ e f 1 /
+*/
+struct fz_matrix_s
+{
+ float a, b, c, d, e, f;
+};
+
+struct fz_point_s
+{
+ float x, y;
+};
+
+struct fz_rect_s
+{
+ fz_point min;
+ fz_point max;
+};
+
+struct fz_ipoint_s
+{
+ int x, y;
+};
+
+struct fz_irect_s
+{
+ fz_ipoint min;
+ fz_ipoint max;
+};
+
+#define FZ_INFRECT (fz_rect){{1,1},{-1,-1}}
+
+fz_matrix fz_concat(fz_matrix one, fz_matrix two);
+fz_matrix fz_identity(void);
+fz_matrix fz_scale(float sx, float sy);
+fz_matrix fz_rotate(float theta);
+fz_matrix fz_translate(float tx, float ty);
+fz_matrix fz_invertmatrix(fz_matrix m);
+int fz_isrectilinear(fz_matrix m);
+
+fz_rect fz_intersectrects(fz_rect a, fz_rect b);
+fz_rect fz_mergerects(fz_rect a, fz_rect b);
+
+fz_irect fz_intersectirects(fz_irect a, fz_irect b);
+fz_irect fz_mergeirects(fz_irect a, fz_irect b);
+
+fz_point fz_transformpoint(fz_matrix m, fz_point p);
+
diff --git a/include/fitz/hash.h b/include/fitz/hash.h
new file mode 100644
index 00000000..4229a02c
--- /dev/null
+++ b/include/fitz/hash.h
@@ -0,0 +1,15 @@
+typedef struct fz_hashtable_s fz_hashtable;
+
+fz_error *fz_newhash(fz_hashtable **tablep, int initialsize, int keylen);
+fz_error *fz_resizehash(fz_hashtable *table, int newsize);
+void fz_debughash(fz_hashtable *table);
+void fz_freehash(fz_hashtable *table);
+
+void *fz_hashfind(fz_hashtable *table, void *key);
+fz_error *fz_hashinsert(fz_hashtable *table, void *key, void *val);
+fz_error *fz_hashremove(fz_hashtable *table, void *key);
+
+int fz_hashlen(fz_hashtable *table);
+void *fz_gethashkey(fz_hashtable *table, int idx);
+void *fz_gethashval(fz_hashtable *table, int idx);
+
diff --git a/include/fitz/image.h b/include/fitz/image.h
new file mode 100644
index 00000000..7f6038ce
--- /dev/null
+++ b/include/fitz/image.h
@@ -0,0 +1,17 @@
+enum
+{
+ FZ_CSGRAY,
+ FZ_CSRGB,
+ FZ_CSCMYK
+};
+
+struct fz_image_s
+{
+ fz_node super;
+ int w, h, n, bpc;
+ int cs;
+ unsigned char *data;
+};
+
+fz_error *fz_newimage(fz_node **nodep, int w, int h, int n, int bpc, int cs);
+
diff --git a/include/fitz/math.h b/include/fitz/math.h
new file mode 100644
index 00000000..acefeb50
--- /dev/null
+++ b/include/fitz/math.h
@@ -0,0 +1,30 @@
+/* multiply 8-bit fixpoint (0..1) so that 0*0==0 and 255*255==255 */
+static inline unsigned char fz_mul255(unsigned char a, unsigned char b)
+{
+ return ((a + 1) * b) >> 8;
+}
+
+/* divide and floor towards -inf */
+static inline int fz_idiv(int a, int b)
+{
+ return a < 0 ? (a - b + 1) / b : a / b;
+}
+
+/* from python */
+static inline void fz_idivmod(int x, int y, int *d, int *m)
+{
+ int xdivy = x / y;
+ int xmody = x - xdivy * y;
+ /* If the signs of x and y differ, and the remainder is non-0,
+ * C89 doesn't define whether xdivy is now the floor or the
+ * ceiling of the infinitely precise quotient. We want the floor,
+ * and we have it iff the remainder's sign matches y's.
+ */
+ if (xmody && ((y ^ xmody) < 0)) {
+ xmody += y;
+ xdivy --;
+ }
+ *d = xdivy;
+ *m = xmody;
+}
+
diff --git a/include/fitz/object.h b/include/fitz/object.h
new file mode 100644
index 00000000..8ccdd153
--- /dev/null
+++ b/include/fitz/object.h
@@ -0,0 +1,124 @@
+typedef struct fz_obj_s fz_obj;
+
+typedef enum fz_objkind_e fz_objkind;
+
+enum fz_objkind_e
+{
+ FZ_NULL,
+ FZ_BOOL,
+ FZ_INT,
+ FZ_REAL,
+ FZ_STRING,
+ FZ_NAME,
+ FZ_ARRAY,
+ FZ_DICT,
+ FZ_INDIRECT,
+ FZ_POINTER,
+};
+
+struct fz_keyval_s
+{
+ fz_obj *k;
+ fz_obj *v;
+};
+
+struct fz_obj_s
+{
+ unsigned short kind; /* fz_objkind takes 4 bytes :( */
+ unsigned short refcount;
+ union
+ {
+ int b;
+ int i;
+ float f;
+ struct {
+ unsigned short len;
+ unsigned char buf[];
+ } s;
+ unsigned char n[1];
+ struct {
+ int len;
+ int cap;
+ fz_obj **items;
+ } a;
+ struct {
+ int len;
+ int cap;
+ struct fz_keyval_s *items;
+ } d;
+ struct {
+ int oid;
+ int gid;
+ } r;
+ void *p;
+ } u;
+};
+
+fz_error *fz_newnull(fz_obj **op);
+fz_error *fz_newbool(fz_obj **op, int b);
+fz_error *fz_newint(fz_obj **op, int i);
+fz_error *fz_newreal(fz_obj **op, float f);
+fz_error *fz_newname(fz_obj **op, char *str);
+fz_error *fz_newstring(fz_obj **op, char *str, int len);
+fz_error *fz_newindirect(fz_obj **op, int oid, int gid);
+fz_error *fz_newpointer(fz_obj **op, void *p);
+
+fz_error *fz_newarray(fz_obj **op, int initialcap);
+fz_error *fz_newdict(fz_obj **op, int initialcap);
+fz_error *fz_copyarray(fz_obj **op, fz_obj *array);
+fz_error *fz_copydict(fz_obj **op, fz_obj *dict);
+fz_error *fz_deepcopyarray(fz_obj **op, fz_obj *array);
+fz_error *fz_deepcopydict(fz_obj **op, fz_obj *dict);
+
+fz_obj *fz_keepobj(fz_obj *obj);
+fz_obj *fz_dropobj(fz_obj *obj);
+
+/* type queries */
+int fz_isnull(fz_obj *obj);
+int fz_isbool(fz_obj *obj);
+int fz_isint(fz_obj *obj);
+int fz_isreal(fz_obj *obj);
+int fz_isname(fz_obj *obj);
+int fz_isstring(fz_obj *obj);
+int fz_isarray(fz_obj *obj);
+int fz_isdict(fz_obj *obj);
+int fz_isindirect(fz_obj *obj);
+int fz_ispointer(fz_obj *obj);
+
+/* silent failure, no error reporting */
+int fz_tobool(fz_obj *obj);
+int fz_toint(fz_obj *obj);
+float fz_toreal(fz_obj *obj);
+char *fz_toname(fz_obj *obj);
+char *fz_tostringbuf(fz_obj *obj);
+int fz_tostringlen(fz_obj *obj);
+int fz_toobjid(fz_obj *obj);
+int fz_togenid(fz_obj *obj);
+void *fz_topointer(fz_obj *obj);
+
+int fz_arraylen(fz_obj *array);
+fz_obj *fz_arrayget(fz_obj *array, int i);
+fz_error *fz_arrayput(fz_obj *array, int i, fz_obj *obj);
+fz_error *fz_arraypush(fz_obj *array, fz_obj *obj);
+void fz_freearray(fz_obj *array);
+
+int fz_dictlen(fz_obj *dict);
+fz_obj *fz_dictgetkey(fz_obj *dict, int idx);
+fz_obj *fz_dictgetval(fz_obj *dict, int idx);
+fz_obj *fz_dictget(fz_obj *dict, fz_obj *key);
+fz_obj *fz_dictgets(fz_obj *dict, char *key);
+fz_error *fz_dictput(fz_obj *dict, fz_obj *key, fz_obj *val);
+fz_error *fz_dictputs(fz_obj *dict, char *key, fz_obj *val);
+fz_error *fz_dictdel(fz_obj *dict, fz_obj *key);
+fz_error *fz_dictdels(fz_obj *dict, char *key);
+void fz_freedict(fz_obj *dict);
+
+int fz_sprintobj(char *s, fz_obj *obj);
+int fz_sprintcobj(char *s, fz_obj *obj);
+int fz_fprintobj(FILE *f, fz_obj *obj);
+int fz_fprintcobj(FILE *f, fz_obj *obj);
+
+fz_error *fz_parseobj(fz_obj **objp, char *s);
+fz_error *fz_packobj(fz_obj **objp, char *fmt, ...);
+fz_error *fz_unpackobj(fz_obj *obj, char *fmt, ...);
+
diff --git a/include/fitz/path.h b/include/fitz/path.h
new file mode 100644
index 00000000..536ca281
--- /dev/null
+++ b/include/fitz/path.h
@@ -0,0 +1,57 @@
+typedef enum fz_pathkind_e fz_pathkind;
+typedef enum fz_pathelkind_e fz_pathelkind;
+typedef struct fz_stroke_s fz_stroke;
+typedef struct fz_dash_s fz_dash;
+typedef union fz_pathel_s fz_pathel;
+
+enum fz_pathkind_e { FZ_STROKE, FZ_FILL, FZ_EOFILL };
+enum fz_pathelkind_e { FZ_MOVETO, FZ_LINETO, FZ_CURVETO, FZ_CLOSEPATH };
+
+struct fz_stroke_s
+{
+ int linecap;
+ int linejoin;
+ float linewidth;
+ float miterlimit;
+};
+
+struct fz_dash_s
+{
+ int len;
+ float phase;
+ float array[];
+};
+
+union fz_pathel_s
+{
+ fz_pathelkind k;
+ float v;
+};
+
+struct fz_path_s
+{
+ fz_node super;
+ fz_pathkind paint;
+ fz_stroke *stroke;
+ fz_dash *dash;
+ int len, cap;
+ fz_pathel *els;
+};
+
+fz_error *fz_newpath(fz_path **pathp);
+fz_error *fz_clonepath(fz_path **pathp, fz_path *oldpath);
+fz_error *fz_moveto(fz_path*, float x, float y);
+fz_error *fz_lineto(fz_path*, float x, float y);
+fz_error *fz_curveto(fz_path*, float, float, float, float, float, float);
+fz_error *fz_curvetov(fz_path*, float, float, float, float);
+fz_error *fz_curvetoy(fz_path*, float, float, float, float);
+fz_error *fz_closepath(fz_path*);
+fz_error *fz_endpath(fz_path*, fz_pathkind paint, fz_stroke *stroke, fz_dash *dash);
+void fz_freepath(fz_path *path);
+
+fz_rect fz_boundpath(fz_path *node, fz_matrix ctm);
+void fz_debugpath(fz_path *node);
+
+fz_error *fz_newdash(fz_dash **dashp, float phase, int len, float *array);
+void fz_freedash(fz_dash *dash);
+
diff --git a/include/fitz/pixmap.h b/include/fitz/pixmap.h
new file mode 100644
index 00000000..90071a30
--- /dev/null
+++ b/include/fitz/pixmap.h
@@ -0,0 +1,22 @@
+typedef struct fz_pixmap_s fz_pixmap;
+typedef struct fz_colorspace_s fz_colorspace;
+
+struct fz_pixmap_s
+{
+ int x, y, w, h;
+ int n, a;
+ int stride;
+ fz_colorspace *cs;
+ short *samples;
+};
+
+fz_error *fz_newpixmap(fz_pixmap **mapp, int x, int y, int w, int h, int n, int a);
+void fz_clearpixmap(fz_pixmap *map);
+void fz_freepixmap(fz_pixmap *map);
+
+void fz_blendover(short *C, short *A, short *B, int n);
+void fz_blendin(short *C, short *A, short *B, int n);
+void fz_blendout(short *C, short *A, short *B, int n);
+void fz_blendatop(short *C, short *A, short *B, int n);
+void fz_blendxor(short *C, short *A, short *B, int n);
+
diff --git a/include/fitz/render.h b/include/fitz/render.h
new file mode 100644
index 00000000..d237e872
--- /dev/null
+++ b/include/fitz/render.h
@@ -0,0 +1,11 @@
+typedef struct fz_renderer_s fz_renderer;
+
+fz_error *fz_newrenderer(fz_renderer **gcp);
+void fz_freerenderer(fz_renderer *gc);
+
+fz_error *fz_renderover(fz_renderer *gc, fz_over *over, fz_matrix ctm, fz_pixmap *out);
+fz_error *fz_rendermask(fz_renderer *gc, fz_mask *mask, fz_matrix ctm, fz_pixmap *out);
+fz_error *fz_rendertransform(fz_renderer *gc, fz_transform *xform, fz_matrix ctm, fz_pixmap *out);
+fz_error *fz_rendertext(fz_renderer *gc, fz_text *text, fz_matrix ctm, fz_pixmap *out);
+fz_error *fz_rendernode(fz_renderer *gc, fz_node *node, fz_matrix ctm, fz_pixmap *out);
+
diff --git a/include/fitz/scanconv.h b/include/fitz/scanconv.h
new file mode 100644
index 00000000..f391a387
--- /dev/null
+++ b/include/fitz/scanconv.h
@@ -0,0 +1,46 @@
+typedef struct fz_edge_s fz_edge;
+typedef struct fz_gel_s fz_gel;
+typedef struct fz_ael_s fz_ael;
+
+struct fz_edge_s
+{
+ int x, e, h, y;
+ int adjup, adjdown;
+ int xmove;
+ int xdir, ydir; /* -1 or +1 */
+};
+
+struct fz_gel_s
+{
+ int hs, vs;
+ int xmin, xmax;
+ int ymin, ymax;
+ int cap;
+ int len;
+ fz_edge *edges;
+};
+
+struct fz_ael_s
+{
+ int cap;
+ int len;
+ fz_edge **edges;
+};
+
+fz_error *fz_newgel(fz_gel **gelp);
+fz_error *fz_insertgel(fz_gel *gel, float x0, float y0, float x1, float y1);
+void fz_resetgel(fz_gel *gel, int hs, int vs);
+void fz_sortgel(fz_gel *gel);
+void fz_freegel(fz_gel *gel);
+
+fz_error *fz_newael(fz_ael **aelp);
+fz_error *fz_insertael(fz_ael *ael, fz_gel *gel, int y, int *e);
+void fz_advanceael(fz_ael *ael);
+void fz_freeael(fz_ael *ael);
+
+fz_error *fz_scanconvert(fz_gel *gel, fz_ael *ael, int eofill);
+
+fz_error *fz_fillpath(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness);
+fz_error *fz_strokepath(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness);
+fz_error *fz_dashpath(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness);
+
diff --git a/include/fitz/sysdep.h b/include/fitz/sysdep.h
new file mode 100644
index 00000000..bb78521f
--- /dev/null
+++ b/include/fitz/sysdep.h
@@ -0,0 +1,48 @@
+/*
+ * Include the basic standard libc headers.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdarg.h>
+
+#include <limits.h> /* INT_MIN, MAX ... */
+#include <float.h> /* DBL_EPSILON */
+#include <math.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h> /* O_RDONLY & co */
+
+/* not supposed to be here, but printf debugging sorta needs it */
+#include <stdio.h>
+
+typedef unsigned char fz_u8;
+typedef signed char fz_s8;
+typedef unsigned short fz_u16;
+typedef signed short fz_s16;
+typedef unsigned long fz_u32;
+typedef signed long fz_s32;
+typedef unsigned long long fz_u64;
+typedef signed long long fz_s64;
+
+/*
+ * Extras! Extras! Get them while they're hot!
+ */
+
+#ifdef NEED_STRLCPY
+extern int strlcpy(char *dst, const char *src, int n);
+extern int strlcat(char *dst, const char *src, int n);
+#endif
+
+#ifdef NEED_STRSEP
+extern char *strsep(char **stringp, const char *delim);
+#endif
+
+#ifdef NEED_GETOPT
+extern int getopt(int nargc, char * const * nargv, const char *ostr);
+extern int opterr, optind, optopt;
+extern char *optarg;
+#endif
+
diff --git a/include/fitz/text.h b/include/fitz/text.h
new file mode 100644
index 00000000..41a08c45
--- /dev/null
+++ b/include/fitz/text.h
@@ -0,0 +1,23 @@
+typedef struct fz_textbuilder_s fz_textbuilder;
+typedef struct fz_textel_s fz_textel;
+
+struct fz_textel_s
+{
+ float x, y;
+ int g;
+};
+
+struct fz_text_s
+{
+ fz_node super;
+ fz_font *font;
+ fz_matrix trm;
+ int len, cap;
+ fz_textel *els;
+};
+
+fz_error *fz_newtext(fz_text **textp, fz_font *face);
+fz_error *fz_addtext(fz_text *text, int g, float x, float y);
+fz_error *fz_endtext(fz_text *text);
+void fz_freetext(fz_text *text);
+
diff --git a/include/fitz/tree.h b/include/fitz/tree.h
new file mode 100644
index 00000000..080e55bf
--- /dev/null
+++ b/include/fitz/tree.h
@@ -0,0 +1,156 @@
+typedef struct fz_tree_s fz_tree;
+typedef struct fz_node_s fz_node;
+
+typedef enum fz_nodekind_e fz_nodekind;
+typedef enum fz_blendkind_e fz_blendkind;
+
+typedef struct fz_transform_s fz_transform;
+typedef struct fz_over_s fz_over;
+typedef struct fz_mask_s fz_mask;
+typedef struct fz_blend_s fz_blend;
+typedef struct fz_path_s fz_path;
+typedef struct fz_text_s fz_text;
+typedef struct fz_solid_s fz_solid;
+typedef struct fz_image_s fz_image;
+typedef struct fz_shade_s fz_shade;
+typedef struct fz_form_s fz_form;
+typedef struct fz_meta_s fz_meta;
+typedef struct fz_halftone_s fz_halftone;
+
+enum fz_nodekind_e
+{
+ FZ_NTRANSFORM,
+ FZ_NOVER,
+ FZ_NMASK,
+ FZ_NBLEND,
+ FZ_NPATH,
+ FZ_NTEXT,
+ FZ_NSOLID,
+ FZ_NIMAGE,
+ FZ_NSHADE,
+ FZ_NFORM,
+ FZ_NMETA,
+ FZ_NHALFTONE
+};
+
+enum fz_blendkind_e
+{
+ /* PDF 1.4 -- standard separable */
+ FZ_BNORMAL,
+ FZ_BMULTIPLY,
+ FZ_BSCREEN,
+ FZ_BOVERLAY,
+ FZ_BDARKEN,
+ FZ_BLIGHTEN,
+ FZ_BCOLORDODGE,
+ FZ_BCOLORBURN,
+ FZ_BHARDLIGHT,
+ FZ_BSOFTLIGHT,
+ FZ_BDIFFERENCE,
+ FZ_BEXCLUSION,
+
+ /* PDF 1.4 -- standard non-separable */
+ FZ_BHUE,
+ FZ_BSATURATION,
+ FZ_BCOLOR,
+ FZ_BLUMINOSITY,
+
+ FZ_BOVERPRINT,
+};
+
+struct fz_tree_s
+{
+ fz_node *root;
+ fz_node *head;
+};
+
+struct fz_node_s
+{
+ fz_nodekind kind;
+ fz_node *parent;
+ fz_node *next;
+};
+
+struct fz_meta_s
+{
+ fz_node super;
+ fz_node *child;
+ fz_obj *info;
+};
+
+struct fz_over_s
+{
+ fz_node super;
+ fz_node *child;
+};
+
+struct fz_mask_s
+{
+ fz_node super;
+ fz_node *child;
+};
+
+struct fz_blend_s
+{
+ fz_node super;
+ fz_node *child;
+ fz_blendkind mode;
+ int isolated;
+ int knockout;
+};
+
+struct fz_transform_s
+{
+ fz_node super;
+ fz_node *child;
+ fz_matrix m;
+};
+
+struct fz_form_s
+{
+ fz_node super;
+ fz_tree *tree;
+};
+
+struct fz_solid_s
+{
+ fz_node super;
+ float r, g, b;
+};
+
+/* tree operations */
+fz_error *fz_newtree(fz_tree **treep);
+void fz_freetree(fz_tree *tree);
+fz_rect fz_boundtree(fz_tree *tree, fz_matrix ctm);
+
+void fz_debugtree(fz_tree *tree);
+void fz_insertnode(fz_node *node, fz_node *child);
+
+/* common to all nodes */
+void fz_initnode(fz_node *node, fz_nodekind kind);
+fz_rect fz_boundnode(fz_node *node, fz_matrix ctm);
+void fz_freenode(fz_node *node);
+
+/* branch nodes */
+fz_error *fz_newmeta(fz_node **nodep, fz_obj *info);
+fz_error *fz_newover(fz_node **nodep);
+fz_error *fz_newmask(fz_node **nodep);
+fz_error *fz_newblend(fz_node **nodep, fz_blendkind b, int k, int i);
+fz_error *fz_newtransform(fz_node **nodep, fz_matrix m);
+
+int fz_ismeta(fz_node *node);
+int fz_isover(fz_node *node);
+int fz_ismask(fz_node *node);
+int fz_isblend(fz_node *node);
+int fz_istransform(fz_node *node);
+
+/* leaf nodes */
+fz_error *fz_newform(fz_node **nodep, fz_tree *subtree);
+fz_error *fz_newsolid(fz_node **nodep, float r, float g, float b);
+
+int fz_isform(fz_node *node);
+int fz_issolid(fz_node *node);
+int fz_ispath(fz_node *node);
+int fz_istext(fz_node *node);
+int fz_isimage(fz_node *node);
+
diff --git a/include/mupdf.h b/include/mupdf.h
new file mode 100644
index 00000000..ca4a267b
--- /dev/null
+++ b/include/mupdf.h
@@ -0,0 +1,253 @@
+#ifdef _MUPDF_H_
+#error "mupdf.h must only be included once"
+#endif
+#define _MUPDF_H_
+
+#ifndef _FITZ_H_
+#error "fitz.h must be included before mupdf.h"
+#endif
+
+/*
+ * tokenizer and low-level object parser
+ */
+
+enum
+{
+ PDF_TERROR, PDF_TEOF,
+ PDF_TOARRAY, PDF_TCARRAY,
+ PDF_TODICT, PDF_TCDICT,
+ PDF_TNAME, PDF_TINT, PDF_TREAL, PDF_TSTRING, PDF_TKEYWORD,
+ PDF_TR, PDF_TTRUE, PDF_TFALSE, PDF_TNULL,
+ PDF_TOBJ, PDF_TENDOBJ,
+ PDF_TSTREAM, PDF_TENDSTREAM,
+ PDF_TXREF, PDF_TTRAILER, PDF_TSTARTXREF,
+ PDF_NTOKENS
+};
+
+/* lex.c */
+int pdf_lex(fz_file *f, unsigned char *buf, int n, int *len);
+
+/* parse.c */
+fz_error *pdf_parsearray(fz_obj **op, fz_file *f, unsigned char *buf, int cap);
+fz_error *pdf_parsedict(fz_obj **op, fz_file *f, unsigned char *buf, int cap);
+fz_error *pdf_parsestmobj(fz_obj **op, fz_file *f, unsigned char *buf, int cap);
+fz_error *pdf_parseindobj(fz_obj **op, fz_file *f, unsigned char *buf, int cap, int *oid, int *gid, int *stmofsj);
+
+/*
+ * xref and syntax object api
+ */
+
+typedef struct pdf_xref_s pdf_xref;
+typedef struct pdf_xrefentry_s pdf_xrefentry;
+typedef struct pdf_crypt_s pdf_crypt;
+
+struct pdf_xref_s
+{
+ float version;
+ pdf_crypt *crypt;
+ fz_file *file;
+ int size;
+ int capacity;
+ pdf_xrefentry *table;
+ fz_obj *trailer;
+ int startxref;
+ fz_hashtable *store;
+};
+
+struct pdf_xrefentry_s
+{
+ unsigned int ofs; /* file offset / objstm object number */
+ unsigned short gen; /* generation / objstm index */
+ char type; /* 0=unset (f)ree i(n)use (o)bjstm (d)elete (a)dd */
+ char mark; /* for garbage collection etc */
+};
+
+struct pdf_crypt_s
+{
+ unsigned char o[32];
+ unsigned char u[32];
+ unsigned int p;
+ int r;
+ int n;
+
+ fz_obj *id;
+
+ unsigned char key[16];
+ int keylen;
+};
+
+/* stream.c */
+fz_error *pdf_buildfilter(fz_filter**, pdf_xref*, fz_obj *stm, int oid, int gid);
+fz_error *pdf_openstream0(pdf_xref*, fz_obj *stmobj, int oid, int gid, int ofs);
+fz_error *pdf_openstream(pdf_xref*, fz_obj *stmref);
+void pdf_closestream(pdf_xref*);
+fz_error *pdf_readstream(unsigned char **bufp, int *lenp, pdf_xref*, fz_obj *stmref);
+
+/* crypt.c */
+fz_error *pdf_newdecrypt(pdf_crypt **cp, fz_obj *enc, fz_obj *id);
+fz_error *pdf_newencrypt(pdf_crypt **cp, fz_obj **edict, char *userpw, char *ownerpw, int p, int n, fz_obj *id);
+fz_error *pdf_setpassword(pdf_crypt *crypt, char *pw);
+fz_error *pdf_cryptstm(fz_filter **fp, pdf_crypt *crypt, int oid, int gid);
+void pdf_cryptobj(pdf_crypt *crypt, fz_obj *obj, int oid, int gid);
+void pdf_freecrypt(pdf_crypt *crypt);
+
+/* repair.c */
+fz_error *pdf_repairxref(pdf_xref*, char *filename);
+
+/* open.c */
+fz_error *pdf_openxref(pdf_xref*, char *filename);
+fz_error *pdf_readobjstm(pdf_xref *xref, int oid, int gid, unsigned char *buf, int cap);
+
+/* xref.c */
+fz_error *pdf_newxref(pdf_xref **xrefp);
+fz_error *pdf_decryptxref(pdf_xref *xref);
+void pdf_closexref(pdf_xref*);
+void pdf_debugxref(pdf_xref*);
+
+fz_obj *pdf_findstoredobject(fz_hashtable *store, int oid, int gid);
+fz_buffer *pdf_findstoredstream(fz_hashtable *store, int oid, int gid);
+fz_error *pdf_deletestoredobject(fz_hashtable *store, int oid, int gid);
+fz_error *pdf_deletestoredstream(fz_hashtable *store, int oid, int gid);
+fz_error *pdf_storeobject(fz_hashtable *store, int oid, int gid, fz_obj *obj);
+fz_error *pdf_storestream(fz_hashtable *store, int oid, int gid, fz_buffer *buf);
+
+fz_error *pdf_createobject(pdf_xref *xref, int *oidp, int *gidp);
+fz_error *pdf_deleteobject(pdf_xref *xref, int oid, int gid);
+fz_error *pdf_saveobject(pdf_xref *xref, int oid, int gid, fz_obj *obj);
+fz_error *pdf_loadobject0(fz_obj **, pdf_xref*, int oid, int gid, int *stmofs);
+fz_error *pdf_loadobject(fz_obj **, pdf_xref*, fz_obj *ref, int *stmofs);
+fz_error *pdf_resolve(fz_obj **, pdf_xref*);
+
+/* save.c */
+fz_error *pdf_saveincrementalpdf(pdf_xref *xref, char *path);
+fz_error *pdf_savepdf(pdf_xref *xref, char *path);
+
+/*
+ * high-level semantic objects for resources and pages
+ */
+
+typedef struct pdf_pagetree_s pdf_pagetree;
+typedef struct pdf_font_s pdf_font;
+typedef struct pdf_resources_s pdf_resources;
+typedef struct pdf_gstate_s pdf_gstate;
+typedef struct pdf_csi_s pdf_csi;
+
+struct pdf_pagetree_s
+{
+ int count;
+ int cursor;
+ fz_obj **pref;
+ fz_obj **pobj;
+};
+
+struct pdf_font_s
+{
+ fz_font super;
+
+ void *ftface;
+
+ fz_cmap *encoding;
+ int cidtogidlen;
+ int *cidtogidmap;
+
+ char *filename;
+ char *fontdata;
+ int fontlen;
+};
+
+struct pdf_type3_s
+{
+ fz_rect bbox;
+ fz_matrix matrix;
+ int widths[256];
+ fz_tree *charprocs[256];
+ int tounicode[256];
+};
+
+struct pdf_resources_s
+{
+ fz_obj *extgstate;
+ fz_obj *colorspace;
+ fz_obj *font;
+ fz_obj *ximage;
+ fz_obj *xform;
+};
+
+struct pdf_gstate_s
+{
+ /* path stroking */
+ float linewidth;
+ int linecap;
+ int linejoin;
+ float miterlimit;
+ float dashphase;
+ int dashlen;
+ float dashlist[32];
+
+ /* colors and colorspaces */
+ struct { float r, g, b; } stroke, fill;
+
+ /* text state */
+ float charspace;
+ float wordspace;
+ float scale;
+ float leading;
+ pdf_font *font;
+ float size;
+ int render;
+ float rise;
+
+ /* tree construction state */
+ fz_node *head;
+};
+
+struct pdf_csi_s
+{
+ pdf_gstate gstate[32];
+ int gtop;
+ fz_obj *stack[32];
+ int top;
+ int xbalance;
+
+ /* path object state */
+ fz_path *path;
+ fz_path *clip;
+
+ /* text object state */
+ fz_text *text;
+ fz_matrix tlm;
+ fz_matrix tm;
+
+ fz_tree *tree;
+};
+
+/* pagetree.c */
+fz_error *pdf_loadpagetree(pdf_pagetree **pp, pdf_xref *xref);
+void pdf_debugpagetree(pdf_pagetree *pages);
+void pdf_freepagetree(pdf_pagetree *pages);
+
+/* cmap.c */
+fz_error *pdf_parsecmap(fz_cmap **cmapp, fz_file *file);
+fz_error *pdf_loadembeddedcmap(fz_cmap **cmapp, pdf_xref *xref, fz_obj *stmref);
+fz_error *pdf_loadsystemcmap(fz_cmap **cmapp, char *name);
+fz_error *pdf_makeidentitycmap(fz_cmap **cmapp, int wmode);
+
+/* fontfile.c */
+fz_error *pdf_loadbuiltinfont(void **fontp, char *pattern);
+fz_error *pdf_loadsystemfont(void **fontp, char *basefont, char *collection);
+fz_error *pdf_loadembeddedfont(void **fontp, pdf_xref *xref, fz_obj *stmref);
+fz_error *pdf_loadfontdescriptor(void **fontp, pdf_xref *xref, fz_obj *desc, char *collection);
+
+/* font.c */
+fz_error *pdf_loadfont(pdf_font **fontp, pdf_xref *xref, fz_obj *font);
+void pdf_freefont(pdf_font *font);
+
+/* resources.c */
+fz_error *pdf_loadresources(pdf_resources **rdbp, pdf_xref *xref, fz_obj *resdict);
+void pdf_freeresources(pdf_resources *rdb);
+
+/* interpret.c */
+fz_error *pdf_newcsi(pdf_csi **csip);
+fz_error *pdf_runcsi(pdf_csi *, pdf_resources *, fz_file *);
+void pdf_freecsi(pdf_csi *csi);
+
diff --git a/mupdf/build.c b/mupdf/build.c
new file mode 100644
index 00000000..f759e500
--- /dev/null
+++ b/mupdf/build.c
@@ -0,0 +1,376 @@
+#include <fitz.h>
+#include <mupdf.h>
+
+extern int FT_Get_Char_Index(void*, int);
+
+void
+pdf_initgstate(pdf_gstate *gs)
+{
+ gs->linewidth = 1.0;
+ gs->linecap = 0;
+ gs->linejoin = 0;
+ gs->miterlimit = 10;
+ gs->dashphase = 0;
+ gs->dashlen = 0;
+ memset(gs->dashlist, 0, sizeof(gs->dashlist));
+
+ gs->stroke.r = 0;
+ gs->stroke.g = 0;
+ gs->stroke.b = 0;
+
+ gs->fill.r = 0;
+ gs->fill.g = 0;
+ gs->fill.b = 0;
+
+ gs->charspace = 0;
+ gs->wordspace = 0;
+ gs->scale = 1;
+ gs->leading = 0;
+ gs->font = nil;
+ gs->size = -1;
+ gs->render = 0;
+ gs->rise = 0;
+
+ gs->head = nil;
+}
+
+fz_error *
+pdf_buildstrokepath(pdf_gstate *gs, fz_path *path)
+{
+ fz_error *error;
+ fz_stroke *stroke;
+ fz_dash *dash;
+
+ stroke = fz_malloc(sizeof(fz_stroke));
+ if (!stroke)
+ return fz_outofmem;
+ stroke->linecap = gs->linecap;
+ stroke->linejoin = gs->linejoin;
+ stroke->linewidth = gs->linewidth;
+ stroke->miterlimit = gs->miterlimit;
+
+ if (gs->dashlen)
+ {
+ error = fz_newdash(&dash, gs->dashphase, gs->dashlen, gs->dashlist);
+ if (error) {
+ fz_free(stroke);
+ return error;
+ }
+ }
+ else
+ {
+ dash = nil;
+ }
+
+ error = fz_endpath(path, FZ_STROKE, stroke, dash);
+ if (error) {
+ fz_freedash(dash);
+ fz_free(stroke);
+ return error;
+ }
+
+ return nil;
+}
+
+fz_error *
+pdf_buildfillpath(pdf_gstate *gs, fz_path *path, int eofill)
+{
+ return fz_endpath(path, eofill ? FZ_EOFILL : FZ_FILL, nil, nil);
+}
+
+static fz_error *
+addcolorshape(pdf_gstate *gs, fz_node *shape, float r, float g, float b)
+{
+ fz_error *error;
+ fz_node *mask;
+ fz_node *solid;
+
+ error = fz_newmask(&mask);
+ if (error) return error;
+
+ error = fz_newsolid(&solid, r, g, b);
+ if (error) return error;
+
+ fz_insertnode(mask, shape);
+ fz_insertnode(mask, solid);
+ fz_insertnode(gs->head, mask);
+
+ return nil;
+}
+
+fz_error *
+pdf_addfillshape(pdf_gstate *gs, fz_node *shape)
+{
+ return addcolorshape(gs, shape, gs->fill.r, gs->fill.g, gs->fill.b);
+}
+
+fz_error *
+pdf_addstrokeshape(pdf_gstate *gs, fz_node *shape)
+{
+ return addcolorshape(gs, shape, gs->stroke.r, gs->stroke.g, gs->stroke.b);
+}
+
+fz_error *
+pdf_addclipmask(pdf_gstate *gs, fz_node *shape)
+{
+ fz_error *error;
+ fz_node *mask;
+ fz_node *over;
+
+ error = fz_newmask(&mask);
+ if (error) return error;
+
+ error = fz_newover(&over);
+ if (error) return error;
+
+ fz_insertnode(mask, shape);
+ fz_insertnode(mask, over);
+ fz_insertnode(gs->head, mask);
+ gs->head = over;
+
+ return nil;
+}
+
+fz_error *
+pdf_addtransform(pdf_gstate *gs, fz_node *affine)
+{
+ fz_error *error;
+ fz_node *over;
+
+ error = fz_newover(&over);
+ if (error) return error;
+
+ fz_insertnode(gs->head, affine);
+ fz_insertnode(affine, over);
+ gs->head = over;
+
+ return nil;
+}
+
+fz_error *
+pdf_showpath(pdf_csi *csi,
+ int doclose, int dofill, int dostroke, int evenodd)
+{
+ pdf_gstate *gstate = csi->gstate + csi->gtop;
+ fz_error *error;
+ fz_path *spath;
+ fz_path *fpath;
+
+ if (doclose)
+ {
+ error = fz_closepath(csi->path);
+ if (error) return error;
+ }
+
+ if (dofill && dostroke)
+ {
+ fpath = csi->path;
+ error = fz_clonepath(&spath, fpath);
+ if (error) return error;
+ }
+ else
+ {
+ spath = fpath = csi->path;
+ }
+
+ if (dofill)
+ {
+ error = pdf_buildfillpath(gstate, fpath, evenodd);
+ if (error) return error;
+ error = pdf_addfillshape(gstate, (fz_node*)fpath);
+ if (error) return error;
+ }
+
+ if (dostroke)
+ {
+ error = pdf_buildstrokepath(gstate, spath);
+ if (error) return error;
+ error = pdf_addstrokeshape(gstate, (fz_node*)spath);
+ if (error) return error;
+ }
+
+ if (!dofill && !dostroke)
+ {
+ fz_free(csi->path);
+ }
+
+ if (csi->clip)
+ {
+ error = pdf_addclipmask(gstate, (fz_node*)csi->clip);
+ if (error) return error;
+ csi->clip = nil;
+ }
+
+ csi->path = nil;
+
+ error = fz_newpath(&csi->path);
+ if (error) return error;
+
+ return nil;
+}
+
+fz_error *
+pdf_flushtext(pdf_csi *csi)
+{
+ pdf_gstate *gstate = csi->gstate + csi->gtop;
+ fz_error *error;
+
+ if (csi->text)
+ {
+ error = pdf_addfillshape(gstate, (fz_node*)csi->text);
+ if (error)
+ return error;
+ csi->text = nil;
+ }
+
+ return nil;
+}
+
+fz_error *
+showglyph(pdf_csi *csi, int g)
+{
+ pdf_gstate *gstate = csi->gstate + csi->gtop;
+ pdf_font *font = gstate->font;
+ fz_error *error;
+ fz_matrix tsm, trm, tm;
+ float w0, w1, tx, ty;
+ fz_hmtx h;
+ fz_vmtx v;
+
+ tsm.a = gstate->size * gstate->scale;
+ tsm.b = 0;
+ tsm.c = 0;
+ tsm.d = gstate->size;
+ tsm.e = 0;
+ tsm.f = gstate->rise;
+
+ tm = csi->tm;
+
+ if (font->super.wmode == 1)
+ {
+ v = fz_getvmtx((fz_font*)font, g);
+ tm.e -= v.x * gstate->size / 1000.0;
+ tm.f += v.y * gstate->size / 1000.0;
+ }
+
+ trm = fz_concat(tsm, tm);
+
+ /* flush buffered text if face or matrix has changed */
+ if (!csi->text ||
+ ((fz_font*)font) != csi->text->font ||
+ fabs(trm.a - csi->text->trm.a) > FLT_EPSILON ||
+ fabs(trm.b - csi->text->trm.b) > FLT_EPSILON ||
+ fabs(trm.c - csi->text->trm.c) > FLT_EPSILON ||
+ fabs(trm.d - csi->text->trm.d) > FLT_EPSILON)
+ {
+ error = pdf_flushtext(csi);
+ if (error) return error;
+
+ error = fz_newtext(&csi->text, (fz_font*)font);
+ if (error) return error;
+
+ csi->text->trm = trm;
+ csi->text->trm.e = 0;
+ csi->text->trm.f = 0;
+ }
+
+ /* add glyph to textobject */
+ error = fz_addtext(csi->text, g, trm.e, trm.f);
+ if (error)
+ return error;
+
+ if (font->super.wmode == 0)
+ {
+ h = fz_gethmtx((fz_font*)font, g);
+ w0 = h.w / 1000.0;
+ tx = (w0 * gstate->size + gstate->charspace) * gstate->scale;
+ csi->tm = fz_concat(fz_translate(tx, 0), csi->tm);
+ }
+ else
+ {
+ w1 = v.w / 1000.0;
+ ty = w1 * gstate->size + gstate->charspace;
+ csi->tm = fz_concat(fz_translate(0, ty), csi->tm);
+ }
+
+ return nil;
+}
+
+void
+showspace(pdf_csi *csi, float tadj)
+{
+ pdf_gstate *gstate = csi->gstate + csi->gtop;
+ pdf_font *font = gstate->font;
+ if (font->super.wmode == 0)
+ csi->tm = fz_concat(fz_translate(tadj * gstate->scale, 0), csi->tm);
+ else
+ csi->tm = fz_concat(fz_translate(0, tadj), csi->tm);
+}
+
+fz_error *
+pdf_showtext(pdf_csi *csi, fz_obj *text)
+{
+ pdf_gstate *gstate = csi->gstate + csi->gtop;
+ pdf_font *font = gstate->font;
+ fz_error *error;
+ unsigned char *buf;
+ unsigned char *end;
+ int i, len;
+ int cpt, cid, gid;
+
+ if (fz_isarray(text))
+ {
+ for (i = 0; i < fz_arraylen(text); i++)
+ {
+ fz_obj *item = fz_arrayget(text, i);
+ if (fz_isstring(item))
+ {
+ error = pdf_showtext(csi, item);
+ if (error) return error;
+ }
+ else
+ {
+ showspace(csi, - fz_toreal(item) * gstate->size / 1000.0);
+ }
+ }
+ return nil;
+ }
+
+ buf = fz_tostringbuf(text);
+ len = fz_tostringlen(text);
+ end = buf + len;
+
+ while (buf < end)
+ {
+ buf = fz_decodecpt(font->encoding, buf, &cpt);
+
+ cid = fz_lookupcid(font->encoding, cpt);
+
+ if (font->cidtogidmap)
+ {
+ if (cid >= 0 && cid < font->cidtogidlen)
+ gid = font->cidtogidmap[cid];
+ else
+ gid = 0;
+ }
+ else
+ {
+ gid = cid;
+ }
+
+//printf("gl %s %g [%g %g %g %g %g %g] cpt<%02x> cid %d gid %d h %d\n",
+// font->super.name, size,
+// csi->tm.a, csi->tm.b, csi->tm.c, csi->tm.d, csi->tm.e, csi->tm.f,
+// cpt, cid, gid, font->super.hadv[gid]);
+
+ error = showglyph(csi, gid);
+ if (error)
+ return error;
+
+ if (cpt == 32)
+ showspace(csi, gstate->wordspace);
+ }
+
+ return nil;
+}
+
diff --git a/mupdf/cmap.c b/mupdf/cmap.c
new file mode 100644
index 00000000..cec14e19
--- /dev/null
+++ b/mupdf/cmap.c
@@ -0,0 +1,521 @@
+#include <fitz.h>
+#include <mupdf.h>
+
+enum
+{
+ TUSECMAP = PDF_NTOKENS,
+ TBEGINCODESPACERANGE,
+ TENDCODESPACERANGE,
+ TBEGINBFCHAR,
+ TENDBFCHAR,
+ TBEGINBFRANGE,
+ TENDBFRANGE,
+ TBEGINCIDCHAR,
+ TENDCIDCHAR,
+ TBEGINCIDRANGE,
+ TENDCIDRANGE,
+};
+
+static int tokenfromkeyword(unsigned char *key)
+{
+ if (!strcmp(key, "usecmap")) return TUSECMAP;
+ if (!strcmp(key, "begincodespacerange")) return TBEGINCODESPACERANGE;
+ if (!strcmp(key, "endcodespacerange")) return TENDCODESPACERANGE;
+ if (!strcmp(key, "beginbfchar")) return TBEGINBFCHAR;
+ if (!strcmp(key, "endbfchar")) return TENDBFCHAR;
+ if (!strcmp(key, "beginbfrange")) return TBEGINBFRANGE;
+ if (!strcmp(key, "endbfrange")) return TENDBFRANGE;
+ if (!strcmp(key, "begincidchar")) return TBEGINCIDCHAR;
+ if (!strcmp(key, "endcidchar")) return TENDCIDCHAR;
+ if (!strcmp(key, "begincidrange")) return TBEGINCIDRANGE;
+ if (!strcmp(key, "endcidrange")) return TENDCIDRANGE;
+ return PDF_TKEYWORD;
+}
+
+static int codefromstring(unsigned char *buf, int len)
+{
+ int a = 0;
+ while (len--)
+ a = (a << 8) | *buf++;
+ return a;
+}
+
+static int mylex(fz_file *file, unsigned char *buf, int n, int *sl)
+{
+ int token = pdf_lex(file, buf, n, sl);
+ if (token == PDF_TKEYWORD)
+ token = tokenfromkeyword(buf);
+ return token;
+}
+
+static fz_error *parsecmapname(fz_cmap *cmap, fz_file *file)
+{
+ unsigned char buf[256];
+ int token;
+ int len;
+
+ token = mylex(file, buf, sizeof buf, &len);
+ if (token == PDF_TNAME) {
+ fz_setcmapname(cmap, buf);
+ return nil;
+ }
+
+ return fz_throw("syntaxerror in CMap after /CMapName");
+}
+
+static fz_error *parsewmode(fz_cmap *cmap, fz_file *file)
+{
+ unsigned char buf[256];
+ int token;
+ int len;
+
+ token = mylex(file, buf, sizeof buf, &len);
+ if (token == PDF_TINT) {
+ fz_setwmode(cmap, atoi(buf));
+ return nil;
+ }
+
+ return fz_throw("syntaxerror in CMap after /WMode");
+}
+
+static fz_error *parsecodespacerange(fz_cmap *cmap, fz_file *file)
+{
+ unsigned char buf[256];
+ int token;
+ int len;
+ fz_error *error;
+ int lo, hi;
+
+ while (1)
+ {
+ token = mylex(file, buf, sizeof buf, &len);
+
+ if (token == TENDCODESPACERANGE)
+ return nil;
+
+ else if (token == PDF_TSTRING)
+ {
+ lo = codefromstring(buf, len);
+ token = mylex(file, buf, sizeof buf, &len);
+ if (token == PDF_TSTRING)
+ {
+ hi = codefromstring(buf, len);
+ error = fz_addcodespacerange(cmap, lo, hi, len);
+ if (error)
+ return error;
+ }
+ else break;
+ }
+
+ else break;
+ }
+
+ return fz_throw("syntaxerror in CMap codespacerange section");
+}
+
+static fz_error *parsecidrange(fz_cmap *cmap, fz_file *file)
+{
+ unsigned char buf[256];
+ int token;
+ int len;
+ fz_error *error;
+ int lo, hi, dst;
+
+ while (1)
+ {
+ token = mylex(file, buf, sizeof buf, &len);
+
+ if (token == TENDCIDRANGE)
+ return nil;
+
+ else if (token != PDF_TSTRING)
+ goto cleanup;
+
+ lo = codefromstring(buf, len);
+
+ token = mylex(file, buf, sizeof buf, &len);
+ if (token != PDF_TSTRING)
+ goto cleanup;
+
+ hi = codefromstring(buf, len);
+
+ token = mylex(file, buf, sizeof buf, &len);
+ if (token != PDF_TINT)
+ goto cleanup;
+
+ dst = atoi(buf);
+
+ error = fz_addcidrange(cmap, lo, hi, dst);
+ if (error)
+ return error;
+ }
+
+cleanup:
+ return fz_throw("syntaxerror in CMap cidrange section");
+}
+
+static fz_error *parsecidchar(fz_cmap *cmap, fz_file *file)
+{
+ unsigned char buf[256];
+ int token;
+ int len;
+ fz_error *error;
+ int src, dst;
+
+ while (1)
+ {
+ token = mylex(file, buf, sizeof buf, &len);
+
+ if (token == TENDCIDCHAR)
+ return nil;
+
+ else if (token != PDF_TSTRING)
+ goto cleanup;
+
+ src = codefromstring(buf, len);
+
+ token = mylex(file, buf, sizeof buf, &len);
+ if (token != PDF_TINT)
+ goto cleanup;
+
+ dst = atoi(buf);
+
+ error = fz_addcidrange(cmap, src, src, dst);
+ if (error)
+ return error;
+ }
+
+cleanup:
+ return fz_throw("syntaxerror in CMap cidchar section");
+}
+
+static fz_error *parsebfrange(fz_cmap *cmap, fz_file *file)
+{
+ unsigned char buf[256];
+ int token;
+ int len;
+ fz_error *error;
+ int lo, hi, dst;
+
+ while (1)
+ {
+ token = mylex(file, buf, sizeof buf, &len);
+
+ if (token == TENDBFRANGE)
+ return nil;
+
+ else if (token != PDF_TSTRING)
+ goto cleanup;
+
+ lo = codefromstring(buf, len);
+
+ token = mylex(file, buf, sizeof buf, &len);
+ if (token != PDF_TSTRING)
+ goto cleanup;
+
+ hi = codefromstring(buf, len);
+
+ token = mylex(file, buf, sizeof buf, &len);
+ /* Note: does not handle [ /Name /Name /Name ... ] */
+ if (token != PDF_TSTRING)
+ goto cleanup;
+
+ dst = codefromstring(buf, len);
+
+ error = fz_addcidrange(cmap, lo, hi, dst);
+ if (error)
+ return error;
+ }
+
+cleanup:
+ return fz_throw("syntaxerror in CMap bfrange section");
+}
+
+static fz_error *parsebfchar(fz_cmap *cmap, fz_file *file)
+{
+ unsigned char buf[256];
+ int token;
+ int len;
+ fz_error *error;
+ int src, dst;
+
+ while (1)
+ {
+ token = mylex(file, buf, sizeof buf, &len);
+
+ if (token == TENDBFCHAR)
+ return nil;
+
+ else if (token != PDF_TSTRING)
+ goto cleanup;
+
+ src = codefromstring(buf, len);
+
+ token = mylex(file, buf, sizeof buf, &len);
+ /* Note: does not handle /dstName */
+ if (token != PDF_TSTRING)
+ goto cleanup;
+
+ dst = codefromstring(buf, len);
+
+ error = fz_addcidrange(cmap, src, src, dst);
+ if (error)
+ return error;
+ }
+
+cleanup:
+ return fz_throw("syntaxerror in CMap bfchar section");
+}
+
+fz_error *
+pdf_parsecmap(fz_cmap **cmapp, fz_file *file)
+{
+ fz_error *error;
+ fz_cmap *cmap;
+ unsigned char key[64];
+ unsigned char buf[256];
+ int token;
+ int len;
+
+ error = fz_newcmap(&cmap);
+ if (error)
+ return error;
+ *cmapp = cmap;
+
+ strcpy(key, ".notdef");
+
+ while (1)
+ {
+ token = mylex(file, buf, sizeof buf, &len);
+
+ if (token == PDF_TEOF)
+ break;
+
+ else if (token == PDF_TERROR)
+ {
+ error = fz_throw("syntaxerror in CMap");
+ goto cleanup;
+ }
+
+ else if (token == PDF_TNAME)
+ {
+ if (!strcmp(buf, "CMapName"))
+ {
+ error = parsecmapname(cmap, file);
+ if (error)
+ goto cleanup;
+ }
+ else if (!strcmp(buf, "WMode"))
+ {
+ error = parsewmode(cmap, file);
+ if (error)
+ goto cleanup;
+ }
+ else
+ strlcpy(key, buf, sizeof key);
+ }
+
+ else if (token == TUSECMAP)
+ {
+ fz_setusecmapname(cmap, key);
+ }
+
+ else if (token == TBEGINCODESPACERANGE)
+ {
+ error = parsecodespacerange(cmap, file);
+ if (error)
+ goto cleanup;
+ }
+
+ else if (token == TBEGINBFCHAR)
+ {
+ error = parsebfchar(cmap, file);
+ if (error)
+ goto cleanup;
+ }
+
+ else if (token == TBEGINCIDCHAR)
+ {
+ error = parsecidchar(cmap, file);
+ if (error)
+ goto cleanup;
+ }
+
+ else if (token == TBEGINBFRANGE)
+ {
+ error = parsebfrange(cmap, file);
+ if (error)
+ goto cleanup;
+ }
+
+ else if (token == TBEGINCIDRANGE)
+ {
+ error = parsecidrange(cmap, file);
+ if (error)
+ goto cleanup;
+ }
+
+ /* ignore everything else */
+ }
+
+ error = fz_endcidrange(cmap);
+ if (error)
+ goto cleanup;
+
+ return nil;
+
+cleanup:
+ fz_freecmap(cmap);
+ *cmapp = nil;
+ return error;
+}
+
+fz_error *
+pdf_loadembeddedcmap(fz_cmap **cmapp, pdf_xref *xref, fz_obj *stmref)
+{
+ fz_obj *stmobj = stmref;
+ fz_error *error = nil;
+ fz_cmap *cmap = nil;
+ fz_cmap *usecmap;
+ fz_obj *wmode;
+ fz_obj *obj;
+
+ error = pdf_resolve(&stmobj, xref);
+ if (error)
+ return error;
+
+printf(" embedded cmap: ");
+fz_fprintcobj(stdout, stmobj);
+printf("\n");
+fflush(stdout);
+
+ error = pdf_openstream(xref, stmref);
+ if (error)
+ goto cleanup;
+
+ error = pdf_parsecmap(&cmap, xref->file);
+ if (error)
+ goto cleanup;
+
+ pdf_closestream(xref);
+
+ wmode = fz_dictgets(stmobj, "WMode");
+ if (fz_isint(wmode))
+ fz_setwmode(cmap, fz_toint(wmode));
+
+ obj = fz_dictgets(stmobj, "UseCMap");
+ if (fz_isname(obj))
+ {
+ printf(" usecmap predefined: %s\n", fz_toname(obj));
+ error = pdf_loadsystemcmap(&usecmap, fz_toname(obj));
+ if (error)
+ goto cleanup;
+ fz_setusecmap(cmap, usecmap);
+ }
+ else if (fz_isindirect(obj))
+ {
+ printf(" usecmap recursive pdf obj\n");
+ error = pdf_loadembeddedcmap(&usecmap, xref, obj);
+ if (error)
+ goto cleanup;
+ fz_setusecmap(cmap, usecmap);
+ }
+
+ *cmapp = cmap;
+
+ fz_dropobj(stmobj);
+ return nil;
+
+cleanup:
+ if (cmap)
+ fz_freecmap(cmap);
+ fz_dropobj(stmobj);
+ *cmapp = nil;
+ return error;
+}
+
+fz_error *
+pdf_loadsystemcmap(fz_cmap **cmapp, char *name)
+{
+ fz_error *error = nil;
+ fz_file *file;
+ char *cmapdir;
+ char *usecmapname;
+ fz_cmap *usecmap;
+ char path[1024];
+
+ *cmapp = nil;
+
+ cmapdir = getenv("CMAPDIR");
+ if (!cmapdir)
+ return fz_throw("ioerror: CMAPDIR environment not set");
+
+ strlcpy(path, cmapdir, sizeof path);
+ strlcat(path, "/", sizeof path);
+ strlcat(path, name, sizeof path);
+
+printf(" system cmap loading %s\n", path);
+
+ error = fz_openfile(&file, path, O_RDONLY);
+ if (error)
+ goto cleanup;
+
+ error = pdf_parsecmap(cmapp, file);
+ if (error)
+ goto cleanup;
+
+ fz_closefile(file);
+
+ usecmapname = fz_getusecmapname(*cmapp);
+ if (usecmapname)
+ {
+printf(" system cmap: usecmap %s\n", usecmapname);
+ error = pdf_loadsystemcmap(&usecmap, usecmapname);
+ if (error)
+ goto cleanup;
+ fz_setusecmap(*cmapp, usecmap);
+ }
+
+ return nil;
+
+cleanup:
+ if (*cmapp)
+ fz_freecmap(*cmapp);
+ if (file)
+ fz_closefile(file);
+ *cmapp = nil;
+ return error;
+}
+
+fz_error *
+pdf_makeidentitycmap(fz_cmap **cmapp, int wmode)
+{
+ fz_error *error = nil;
+
+ error = fz_newcmap(cmapp);
+ if (error)
+ return error;
+
+ error = fz_addcodespacerange(*cmapp, 0x0000, 0xffff, 2);
+ if (error) {
+ fz_freecmap(*cmapp);
+ return error;
+ }
+
+ error = fz_addcidrange(*cmapp, 0x0000, 0xffff, 0);
+ if (error) {
+ fz_freecmap(*cmapp);
+ return error;
+ }
+
+ error = fz_endcidrange(*cmapp);
+ if (error) {
+ fz_freecmap(*cmapp);
+ return error;
+ }
+
+ fz_setwmode(*cmapp, wmode);
+
+ return nil;
+}
+
diff --git a/mupdf/crypt.c b/mupdf/crypt.c
new file mode 100644
index 00000000..730c0bd5
--- /dev/null
+++ b/mupdf/crypt.c
@@ -0,0 +1,356 @@
+#include <fitz.h>
+#include <mupdf.h>
+
+static unsigned char padding[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,
+};
+
+static void voodoo50(unsigned char *buf, int n)
+{
+ fz_md5 md5;
+ int i;
+
+ for (i = 0; i < 50; i++) {
+ fz_md5init(&md5);
+ fz_md5update(&md5, buf, n);
+ fz_md5final(&md5, buf);
+ }
+}
+
+static void voodoo19(unsigned char *data, int ndata, unsigned char *key, int nkey)
+{
+ fz_arc4 arc4;
+ unsigned char keybuf[16];
+ int i, k;
+
+ for (i = 1; i <= 19; i++) {
+ for (k = 0; k < nkey; k++)
+ keybuf[k] = key[k] ^ (unsigned char)i;
+ fz_arc4init(&arc4, keybuf, nkey);
+ fz_arc4encrypt(&arc4, data, data, ndata);
+ }
+}
+
+static void padpassword(unsigned char *buf, char *pw)
+{
+ int len = strlen(pw);
+ if (len > 32)
+ len = 32;
+ memcpy(buf, pw, len);
+ memcpy(buf + len, padding, 32 - len);
+}
+
+fz_error *
+pdf_newdecrypt(pdf_crypt **cp, fz_obj *enc, fz_obj *id)
+{
+ pdf_crypt *crypt = nil;
+ fz_obj *obj;
+ int m;
+
+ obj = fz_dictgets(enc, "V");
+ m = 0;
+ if (fz_isint(obj))
+ m = fz_toint(obj);
+ if (m != 1 && m != 2)
+ return fz_throw("unsupported encryption: %d", m);
+
+ crypt = *cp = fz_malloc(sizeof(pdf_crypt));
+ if (!crypt) return fz_outofmem;
+
+ obj = fz_dictgets(enc, "O");
+ if (!fz_isstring(obj) || fz_tostringlen(obj) != 32)
+ goto cleanup;
+ memcpy(crypt->o, fz_tostringbuf(obj), 32);
+
+ obj = fz_dictgets(enc, "U");
+ if (!fz_isstring(obj) || fz_tostringlen(obj) != 32)
+ goto cleanup;
+ memcpy(crypt->u, fz_tostringbuf(obj), 32);
+
+ obj = fz_dictgets(enc, "P");
+ if (!fz_isint(obj))
+ goto cleanup;
+ crypt->p = fz_toint(obj);
+
+ obj = fz_dictgets(enc, "Length");
+ if (fz_isint(obj))
+ crypt->n = fz_toint(obj) / 8;
+ else
+ crypt->n = 40 / 8;
+ if (crypt->n < 5) goto cleanup;
+ if (crypt->n > 16) goto cleanup;
+
+ obj = fz_dictgets(enc, "R");
+ if (!fz_isint(obj))
+ goto cleanup;
+ crypt->r = fz_toint(obj);
+ if (crypt->r != 2 && crypt->r != 3)
+ goto cleanup;
+ if (crypt->r == 2 && crypt->n != 5)
+ goto cleanup;
+
+ if (!fz_isarray(id) || fz_arraylen(id) != 2)
+ goto cleanup;
+ obj = fz_arrayget(id, 0);
+ if (!fz_isstring(obj))
+ goto cleanup;
+ crypt->id = fz_keepobj(obj);
+
+ crypt->keylen = crypt->n + 5;
+ if (crypt->keylen > 16)
+ crypt->keylen = 16;
+
+ memset(crypt->key, 0, 16);
+
+ return nil;
+
+cleanup:
+ if (crypt) fz_free(crypt);
+ return fz_throw("corrupt encryption dictionary");
+}
+
+void
+pdf_freecrypt(pdf_crypt *crypt)
+{
+ fz_dropobj(crypt->id);
+ fz_free(crypt);
+}
+
+static void
+createobjkey(pdf_crypt *crypt, unsigned oid, unsigned gid, unsigned char *key)
+{
+ unsigned char message[5];
+ fz_md5 md5;
+
+ /* Algorithm 3.1 Encryption of data using an encryption key */
+
+ fz_md5init(&md5);
+
+ fz_md5update(&md5, crypt->key, crypt->n);
+
+ message[0] = oid & 0xFF;
+ message[1] = (oid >> 8) & 0xFF;
+ message[2] = (oid >> 16) & 0xFF;
+
+ message[3] = gid & 0xFF;
+ message[4] = (gid >> 8) & 0xFF;
+
+ fz_md5update(&md5, message, 5);
+
+ fz_md5final(&md5, key);
+}
+
+/*
+ * Algorithm 3.2 Computing an encryption key
+ */
+static void
+createkey(pdf_crypt *crypt, unsigned char *userpw)
+{
+ unsigned char buf[32];
+ fz_md5 md5;
+
+ /* Step 1 + 2 */
+ fz_md5init(&md5);
+ padpassword(buf, userpw);
+ fz_md5update(&md5, buf, 32);
+
+ /* Step 3 */
+ fz_md5update(&md5, crypt->o, 32);
+
+ /* Step 4 */
+ buf[0] = crypt->p & 0xFF;
+ buf[1] = (crypt->p >> 8) & 0xFF;
+ buf[2] = (crypt->p >> 16) & 0xFF;
+ buf[3] = (crypt->p >> 24) & 0xFF;
+ fz_md5update(&md5, buf, 4);
+
+ /* Step 5 */
+ fz_md5update(&md5, fz_tostringbuf(crypt->id), fz_tostringlen(crypt->id));
+ fz_md5final(&md5, crypt->key);
+
+ /* Step 6 (rev 3 only) */
+ if (crypt->r == 3)
+ voodoo50(crypt->key, crypt->n);
+
+ /* Step 7: key is in crypt->key */
+}
+
+/*
+ * Algorithm 3.3 Computing the O value
+ */
+static void
+createowner(pdf_crypt *crypt, char *userpw, char *ownerpw)
+{
+ unsigned char buf[32];
+ unsigned char key[16];
+ fz_arc4 arc4;
+ fz_md5 md5;
+
+ /* Step 1 + 2 */
+ if (strlen(ownerpw) == 0)
+ ownerpw = userpw;
+ padpassword(buf, ownerpw);
+ fz_md5init(&md5);
+ fz_md5update(&md5, buf, 32);
+ fz_md5final(&md5, key);
+
+ /* Step 3 (rev 3 only) */
+ if (crypt->r == 3)
+ voodoo50(key, crypt->n);
+
+ /* Step 4 */
+ fz_arc4init(&arc4, key, crypt->n);
+
+ /* Step 5 */
+ padpassword(buf, userpw);
+
+ /* Step 6 */
+ fz_arc4encrypt(&arc4, buf, buf, 32);
+
+ /* Step 7 (rev 3 only) */
+ if (crypt->r == 3)
+ voodoo19(buf, 32, key, crypt->n);
+
+ /* Step 8 */
+ memcpy(crypt->o, buf, 32);
+}
+
+/*
+ * Algorithm 3.4 Computing the U value (rev 2)
+ * Algorithm 3.5 Computing the U value (rev 3)
+ */
+static void
+createuser(pdf_crypt *crypt, char *userpw)
+{
+ unsigned char key[16];
+ fz_arc4 arc4;
+ fz_md5 md5;
+
+ if (crypt->r == 2) {
+ createkey(crypt, userpw);
+ fz_arc4init(&arc4, crypt->key, crypt->n);
+ fz_arc4encrypt(&arc4, crypt->u, padding, 32);
+ }
+
+ if (crypt->r == 3) {
+ /* Step 1 */
+ createkey(crypt, userpw);
+
+ /* Step 2 */
+ fz_md5init(&md5);
+ fz_md5update(&md5, padding, 32);
+
+ /* Step 3 */
+ fz_md5update(&md5, fz_tostringbuf(crypt->id), fz_tostringlen(crypt->id));
+ fz_md5final(&md5, key);
+
+ /* Step 4 */
+ fz_arc4init(&arc4, crypt->key, crypt->n);
+ fz_arc4encrypt(&arc4, key, key, 16);
+
+ /* Step 5 */
+ voodoo19(key, 16, crypt->key, crypt->n);
+
+ /* Step 6 */
+ memcpy(crypt->u, key, 16);
+ memset(crypt->u + 16, 0, 16);
+ }
+}
+
+fz_error *
+pdf_newencrypt(pdf_crypt **cp, fz_obj **dict,
+ char *userpw, char *ownerpw, int p, int n, fz_obj *id)
+{
+ fz_error *error;
+ pdf_crypt *crypt;
+
+ crypt = *cp = fz_malloc(sizeof(pdf_crypt));
+ if (!crypt) return fz_outofmem;
+
+ crypt->id = fz_keepobj(fz_arrayget(id, 0));
+ crypt->p = p;
+ crypt->n = MIN(MAX(n / 8, 5), 16);
+ crypt->keylen = MIN(crypt->n + 5, 16);
+ crypt->r = crypt->n == 5 ? 2 : 3;
+
+ createowner(crypt, userpw, ownerpw);
+ createuser(crypt, userpw);
+
+ error = fz_packobj(dict,
+ "<< /Filter /Standard "
+ "/V %i /R %i "
+ "/O %# /U %# "
+ "/P %i "
+ "/Length %i >>",
+ crypt->r == 2 ? 1 : 2,
+ crypt->r,
+ crypt->o, 32,
+ crypt->u, 32,
+ crypt->p,
+ crypt->n * 8);
+ return error;
+}
+
+/*
+ * Algorithm 3.6 Checking the user password
+ */
+fz_error *
+pdf_setpassword(pdf_crypt *crypt, char *userpw)
+{
+ unsigned char saved[32];
+ unsigned char test[32];
+
+ memcpy(saved, crypt->u, 32);
+ createuser(crypt, userpw);
+ memcpy(test, crypt->u, 32);
+ memcpy(crypt->u, saved, 32);
+
+ if (memcmp(test, saved, crypt->r == 3 ? 16 : 32) != 0)
+ return fz_throw("invalid password");
+
+ return nil;
+}
+
+void
+pdf_cryptobj(pdf_crypt *crypt, fz_obj *obj, int oid, int gid)
+{
+ fz_arc4 arc4;
+ unsigned char key[16];
+ unsigned char *s;
+ int i, n;
+
+ if (fz_isstring(obj)) {
+ s = fz_tostringbuf(obj);
+ n = fz_tostringlen(obj);
+ createobjkey(crypt, oid, gid, key);
+ fz_arc4init(&arc4, key, crypt->keylen);
+ fz_arc4encrypt(&arc4, s, s, n);
+ }
+
+ else if (fz_isarray(obj)) {
+ n = fz_arraylen(obj);
+ for (i = 0; i < n; i++) {
+ pdf_cryptobj(crypt, fz_arrayget(obj, i), oid, gid);
+ }
+ }
+
+ else if (fz_isdict(obj)) {
+ n = fz_dictlen(obj);
+ for (i = 0; i < n; i++) {
+ pdf_cryptobj(crypt, fz_dictgetval(obj, i), oid, gid);
+ }
+ }
+}
+
+fz_error *
+pdf_cryptstm(fz_filter **fp, pdf_crypt *crypt, int oid, int gid)
+{
+ unsigned char key[16];
+ createobjkey(crypt, oid, gid, key);
+ return fz_newarc4filter(fp, key, crypt->keylen);
+}
+
diff --git a/mupdf/font.c b/mupdf/font.c
new file mode 100644
index 00000000..1fa5fa93
--- /dev/null
+++ b/mupdf/font.c
@@ -0,0 +1,790 @@
+#include <fitz.h>
+#include <mupdf.h>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include <freetype/internal/ftobjs.h>
+
+#include "fontenc.h"
+#include "fontagl.h"
+
+enum { UNKNOWN, TYPE1, CFF, TRUETYPE, CID };
+
+static int ftkind(FT_Face face)
+{
+ const char *kind = face->driver->clazz->root.module_name;
+printf(" type %s\n", kind);
+ if (!strcmp(kind, "type1"))
+ return TYPE1;
+ if (!strcmp(kind, "cff"))
+ return CFF;
+ if (!strcmp(kind, "truetype"))
+ return TRUETYPE;
+ if (!strcmp(kind, "t1cid"))
+ return CID;
+ return UNKNOWN;
+}
+
+static int ftwidth(FT_Face face, int gid)
+{
+ int e;
+ e = FT_Load_Glyph(face, gid,
+ FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM);
+ if (e)
+ return 0;
+ return face->glyph->advance.x;
+}
+
+static fz_error *
+ftrender(fz_glyph *glyph, fz_font *font, int gid, fz_matrix trm)
+{
+ FT_Face face = ((pdf_font*)font)->ftface;
+ FT_Matrix m;
+ FT_Vector v;
+ FT_Error fterr;
+
+ glyph->w = 0;
+ glyph->h = 0;
+ glyph->lsb = 0;
+ glyph->top = 0;
+ glyph->bitmap = nil;
+
+ m.xx = trm.a * 65536;
+ m.yx = trm.b * 65536;
+ m.xy = trm.c * 65536;
+ m.yy = trm.d * 65536;
+ v.x = trm.e * 64;
+ v.y = trm.f * 64;
+
+ FT_Set_Char_Size(face, 64, 64, 72, 72);
+ FT_Set_Transform(face, &m, &v);
+
+ fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING);
+ if (fterr)
+ return fz_throw("freetype failed to load glyph: 0x%x", fterr);
+
+ fterr = FT_Render_Glyph(face->glyph, ft_render_mode_normal);
+ if (fterr)
+ return fz_throw("freetype failed to render glyph: 0x%x", fterr);
+
+ glyph->w = face->glyph->bitmap.width;
+ glyph->h = face->glyph->bitmap.rows;
+ glyph->lsb = face->glyph->bitmap_left;
+ glyph->top = face->glyph->bitmap_top;
+ glyph->bitmap = face->glyph->bitmap.buffer;
+
+ return nil;
+}
+
+static char *cleanfontname(char *fontname)
+{
+ int i, k;
+ for (i = 0; i < 14; i++)
+ for (k = 0; basefontnames[i][k]; k++)
+ if (!strcmp(basefontnames[i][k], fontname))
+ return basefontnames[i][0];
+ return fontname;
+}
+
+static void loadencoding(char **estrings, char *encoding)
+{
+ char **bstrings = nil;
+ int i;
+
+ if (!strcmp(encoding, "MacRomanEncoding"))
+ bstrings = macroman;
+ if (!strcmp(encoding, "MacExpertEncoding"))
+ bstrings = macexpert;
+ if (!strcmp(encoding, "WinAnsiEncoding"))
+ bstrings = winansi;
+
+ if (bstrings)
+ for (i = 0; i < 256; i++)
+ estrings[i] = bstrings[i];
+}
+
+static int aglcode(char *name)
+{
+ int l = 0;
+ int r = adobeglyphlen;
+
+ while (l <= r)
+ {
+ int m = (l + r) >> 1;
+ int c = strcmp(name, adobeglyphlist[m].name);
+ if (c < 0)
+ r = m - 1;
+ else if (c > 0)
+ l = m + 1;
+ else
+ return adobeglyphlist[m].code;
+ }
+
+ if (strstr(name, "uni") == name)
+ return strtol(name + 3, 0, 16);
+
+ if (strstr(name, "u") == name)
+ return strtol(name + 1, 0, 16);
+
+ return -1;
+}
+
+static int mrecode(char *name)
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (macroman[i] && !strcmp(name, macroman[i]))
+ return i;
+ return -1;
+}
+
+static int cidtogid(pdf_font *font, int cid)
+{
+ if (font->cidtogidmap)
+ {
+ if (cid >= 0 && cid < font->cidtogidlen)
+ return font->cidtogidmap[cid];
+ return 0;
+ }
+ return cid;
+}
+
+static void ftfreefont(fz_font *font)
+{
+ pdf_font *pfont = (pdf_font*)font;
+ if (pfont->encoding)
+ fz_freecmap(pfont->encoding);
+}
+
+static pdf_font *
+newfont(char *name)
+{
+ pdf_font *font;
+
+ font = fz_malloc(sizeof (pdf_font));
+ if (!font)
+ return nil;
+
+ fz_initfont((fz_font*)font, name);
+ font->super.render = ftrender;
+ font->super.free = (void(*)(fz_font*)) ftfreefont;
+
+ font->ftface = nil;
+ font->encoding = nil;
+ font->cidtogidlen = 0;
+ font->cidtogidmap = nil;
+
+ return font;
+}
+
+static fz_error *
+loadsimplefont(pdf_font **fontp, pdf_xref *xref, fz_obj *dict)
+{
+ fz_error *error;
+ fz_obj *descriptor = nil;
+ fz_obj *encoding = nil;
+ fz_obj *widths = nil;
+ pdf_font *font;
+ FT_Face face;
+ FT_CharMap cmap;
+ int kind;
+
+ char *basefont;
+ char *estrings[256];
+ int etable[256];
+ int i, k, n, e;
+
+ basefont = fz_toname(fz_dictgets(dict, "BaseFont"));
+ basefont = cleanfontname(basefont);
+
+ /*
+ * Load font file
+ */
+
+printf("loading simple font %s\n", basefont);
+
+ font = *fontp = newfont(basefont);
+ if (!font)
+ return fz_outofmem;
+
+ descriptor = fz_dictgets(dict, "FontDescriptor");
+ if (descriptor)
+ error = pdf_loadfontdescriptor(&font->ftface, xref, descriptor, nil);
+ else
+ error = pdf_loadsystemfont(&font->ftface, basefont, nil);
+ if (error)
+ goto cleanup;
+
+ face = font->ftface;
+ kind = ftkind(face);
+
+ fz_setfontbbox((fz_font*)font,
+ face->bbox.xMin, face->bbox.yMin,
+ face->bbox.xMax, face->bbox.yMax);
+
+ /*
+ * Encoding
+ */
+
+ if (face->num_charmaps > 0)
+ cmap = face->charmaps[0];
+ else
+ cmap = nil;
+
+ for (i = 0; i < face->num_charmaps; i++)
+ {
+ FT_CharMap test = face->charmaps[i];
+
+ if (kind == CFF || kind == TYPE1)
+ {
+ if (test->platform_id == 7)
+ cmap = test;
+ }
+
+ if (kind == TRUETYPE)
+ {
+ if (test->platform_id == 1 && test->encoding_id == 0)
+ cmap = test;
+ if (test->platform_id == 3 && test->encoding_id == 1)
+ cmap = test;
+ }
+ }
+
+ if (!cmap)
+ {
+ error = fz_throw("freetype could not find any cmaps");
+ goto cleanup;
+ }
+
+ e = FT_Set_Charmap(face, cmap);
+ if (e)
+ {
+ error = fz_throw("freetype could not set cmap: 0x%x", e);
+ goto cleanup;
+ }
+
+ for (i = 0; i < 256; i++)
+ {
+ estrings[i] = _notdef;
+ etable[i] = 0;
+ }
+
+ encoding = fz_dictgets(dict, "Encoding");
+ if (encoding)
+ {
+ error = pdf_resolve(&encoding, xref);
+ if (error)
+ goto cleanup;
+
+
+ if (fz_isname(encoding))
+ loadencoding(estrings, fz_toname(encoding));
+
+ if (fz_isdict(encoding))
+ {
+ fz_obj *base, *diff, *item;
+
+ base = fz_dictgets(encoding, "BaseEncoding");
+ if (fz_isname(base))
+ loadencoding(estrings, fz_toname(base));
+
+
+ diff = fz_dictgets(encoding, "Differences");
+ if (fz_isarray(diff))
+ {
+ n = fz_arraylen(diff);
+ k = 0;
+ for (i = 0; i < n; i++)
+ {
+ item = fz_arrayget(diff, i);
+ if (fz_isint(item))
+ k = fz_toint(item);
+ if (fz_isname(item))
+ estrings[k++] = fz_toname(item);
+ if (k < 0) k = 0;
+ if (k > 255) k = 255;
+ }
+ }
+ }
+
+ if (kind == TYPE1 || kind == CFF)
+ {
+ for (i = 0; i < 256; i++)
+ if (estrings[i])
+ etable[i] = FT_Get_Name_Index(face, estrings[i]);
+ else
+ etable[i] = FT_Get_Char_Index(face, i);
+ }
+
+ if (kind == TRUETYPE)
+ {
+ /* Unicode cmap */
+ if (face->charmap->platform_id == 3)
+ {
+ for (i = 0; i < 256; i++)
+ if (estrings[i])
+ {
+ k = aglcode(estrings[i]);
+ if (k == -1)
+ etable[i] = FT_Get_Name_Index(face, estrings[i]);
+ else
+ etable[i] = FT_Get_Char_Index(face, k);
+ }
+ else
+ etable[i] = FT_Get_Char_Index(face, i);
+ }
+
+ /* MacRoman cmap */
+ else if (face->charmap->platform_id == 1)
+ {
+ for (i = 0; i < 256; i++)
+ if (estrings[i])
+ {
+ k = mrecode(estrings[i]);
+ if (k <= 0)
+ etable[i] = FT_Get_Name_Index(face, estrings[i]);
+ else
+ etable[i] = FT_Get_Char_Index(face, k);
+ }
+ else
+ etable[i] = FT_Get_Char_Index(face, i);
+ }
+
+ /* Symbolic cmap */
+ else
+ {
+ for (i = 0; i < 256; i++)
+ etable[i] = FT_Get_Char_Index(face, i);
+ }
+ }
+
+ encoding = fz_dropobj(encoding);
+ }
+
+ else
+ {
+ for (i = 0; i < 256; i++)
+ etable[i] = FT_Get_Char_Index(face, i);
+ }
+
+ error = fz_newcmap(&font->encoding);
+ if (error)
+ goto cleanup;
+
+ error = fz_addcodespacerange(font->encoding, 0x00, 0xff, 1);
+ if (error)
+ goto cleanup;
+
+ error = fz_setcidlookup(font->encoding, etable);
+ if (error)
+ goto cleanup;
+
+ /*
+ * Widths
+ */
+
+ /* FIXME should set defaulthmtx to MissingWidth in FontDescriptor */
+
+ widths = fz_dictgets(dict, "Widths");
+ if (widths)
+ {
+ int first, last;
+
+ error = pdf_resolve(&widths, xref);
+ if (error)
+ goto cleanup;
+
+ first = fz_toint(fz_dictgets(dict, "FirstChar"));
+ last = fz_toint(fz_dictgets(dict, "LastChar"));
+
+printf(" widths vector %d to %d\n", first, last);
+
+ if (first < 0 || last > 255 || first > last)
+ first = last = 0;
+
+ for (i = 0; i < last - first + 1; i++)
+ {
+ int gid = etable[i + first];
+ int wid = fz_toint(fz_arrayget(widths, i));
+ if (gid >= 0)
+ {
+ error = fz_addhmtx((fz_font*)font, gid, wid);
+ if (error)
+ goto cleanup;
+ }
+ }
+
+ widths = fz_dropobj(widths);
+ }
+ else
+ {
+printf(" builtin widths\n");
+ FT_Set_Char_Size(face, 1000, 1000, 72, 72);
+ for (i = 0; i < 256; i++)
+ {
+ int gid = etable[i];
+ if (gid >= 0)
+ {
+ error = fz_addhmtx((fz_font*)font, gid, ftwidth(face, gid));
+ if (error)
+ goto cleanup;
+ }
+ }
+ }
+
+ error = fz_endhmtx((fz_font*)font);
+ if (error)
+ goto cleanup;
+
+ FT_Set_Char_Size(face, 64, 64, 72, 72);
+
+printf("\n");
+
+fz_debugfont((fz_font*)font);
+
+ return nil;
+
+cleanup:
+ if (widths)
+ fz_dropobj(widths);
+ fz_freefont((fz_font*)font);
+ *fontp = nil;
+ return error;
+}
+
+static fz_error *
+loadcidfont(pdf_font **fontp, pdf_xref *xref, fz_obj *dict, fz_obj *encoding)
+{
+ fz_error *error;
+ fz_obj *widths = nil;
+ fz_obj *descriptor;
+ pdf_font *font;
+ FT_Face face;
+ int kind;
+ char collection[256];
+ char *basefont;
+ int i, k;
+
+ /*
+ * Get font name and CID collection
+ */
+
+ basefont = fz_toname(fz_dictgets(dict, "BaseFont"));
+
+printf("loading cid font %s\n", basefont);
+
+ {
+ fz_obj *cidinfo;
+ fz_obj *obj;
+ char tmpstr[64];
+ int tmplen;
+
+ cidinfo = fz_dictgets(dict, "CIDSystemInfo");
+
+ error = pdf_resolve(&cidinfo, xref);
+ if (error)
+ return error;
+
+ obj = fz_dictgets(cidinfo, "Registry");
+ tmplen = MIN(sizeof tmpstr - 1, fz_tostringlen(obj));
+ memcpy(tmpstr, fz_tostringbuf(obj), tmplen);
+ tmpstr[tmplen] = '\0';
+ strlcpy(collection, tmpstr, sizeof collection);
+
+ strlcat(collection, "-", sizeof collection);
+
+ obj = fz_dictgets(cidinfo, "Ordering");
+ tmplen = MIN(sizeof tmpstr - 1, fz_tostringlen(obj));
+ memcpy(tmpstr, fz_tostringbuf(obj), tmplen);
+ tmpstr[tmplen] = '\0';
+ strlcat(collection, tmpstr, sizeof collection);
+
+ fz_dropobj(cidinfo);
+ }
+
+printf(" collection %s\n", collection);
+
+ /*
+ * Load font file
+ */
+
+ font = *fontp = newfont(basefont);
+ if (!font)
+ return fz_outofmem;
+
+ descriptor = fz_dictgets(dict, "FontDescriptor");
+ if (descriptor)
+ error = pdf_loadfontdescriptor(&font->ftface, xref, descriptor, collection);
+ else
+ error = fz_throw("syntaxerror: missing font descriptor");
+ if (error)
+ goto cleanup;
+
+ face = font->ftface;
+ kind = ftkind(face);
+
+ fz_setfontbbox((fz_font*)font,
+ face->bbox.xMin, face->bbox.yMin,
+ face->bbox.xMax, face->bbox.yMax);
+
+ /*
+ * Encoding
+ */
+
+ if (fz_isname(encoding))
+ {
+printf(" external CMap %s\n", fz_toname(encoding));
+ if (!strcmp(fz_toname(encoding), "Identity-H"))
+ error = pdf_makeidentitycmap(&font->encoding, 0);
+ else if (!strcmp(fz_toname(encoding), "Identity-V"))
+ error = pdf_makeidentitycmap(&font->encoding, 1);
+ else
+ error = pdf_loadsystemcmap(&font->encoding, fz_toname(encoding));
+ }
+ else if (fz_isindirect(encoding))
+ {
+printf(" embedded CMap\n");
+ error = pdf_loadembeddedcmap(&font->encoding, xref, encoding);
+ }
+ else
+ {
+ error = fz_throw("syntaxerror: font missing encoding");
+ }
+ if (error)
+ goto cleanup;
+
+ fz_setfontwmode((fz_font*)font, fz_getwmode(font->encoding));
+
+ if (kind == TRUETYPE)
+ {
+ fz_obj *cidtogidmap;
+
+ cidtogidmap = fz_dictgets(dict, "CIDToGIDMap");
+ if (fz_isindirect(cidtogidmap))
+ {
+ unsigned char *buf;
+ int len;
+
+ error = pdf_readstream(&buf, &len, xref, cidtogidmap);
+ if (error)
+ goto cleanup;
+
+ font->cidtogidlen = len / 2;
+ font->cidtogidmap = fz_malloc((len / 2) * sizeof(int));
+ if (!font->cidtogidmap) {
+ fz_free(buf);
+ error = fz_outofmem;
+ goto cleanup;
+ }
+
+printf(" cidtogidmap %d\n", len / 2);
+
+ for (i = 0; i < len / 2; i++)
+ font->cidtogidmap[i] = (buf[i * 2] << 8) + buf[i * 2 + 1];
+ }
+
+ /* TODO: if truetype font is external, cidtogidmap should not be identity */
+ /* we should map the cid to another encoding represented by a 'cmap' table */
+ /* and then through that to a gid */
+ /* cids: Adobe-CNS1 Adobe-GB1 Adobe-Japan1 Adobe-Japan2 Adobe-Korea1 */
+ /* cmap: Big5 Johab PRC ShiftJIS Unicode Wansung */
+ /* win: 3,4 3,6 3,3 3,2 3,1 3,5 */
+ }
+
+ /*
+ * Horizontal
+ */
+
+ fz_setdefaulthmtx((fz_font*)font, fz_toint(fz_dictgets(dict, "DW")));
+
+ widths = fz_dictgets(dict, "W");
+ if (widths)
+ {
+ int c0, c1, w, gid;
+ fz_obj *obj;
+
+ error = pdf_resolve(&widths, xref);
+ if (error)
+ goto cleanup;
+
+ for (i = 0; i < fz_arraylen(widths); )
+ {
+ c0 = fz_toint(fz_arrayget(widths, i));
+ obj = fz_arrayget(widths, i + 1);
+ if (fz_isarray(obj))
+ {
+ for (k = 0; k < fz_arraylen(obj); k++)
+ {
+ w = fz_toint(fz_arrayget(obj, k));
+ gid = cidtogid(font, c0 + k);
+ error = fz_addhmtx((fz_font*)font, gid, w);
+ if (error)
+ goto cleanup;
+ }
+ i += 2;
+ }
+ else
+ {
+ c1 = fz_toint(obj);
+ w = fz_toint(fz_arrayget(widths, i + 2));
+ for (k = c0; k <= c1; k++)
+ {
+ gid = cidtogid(font, k);
+ error = fz_addhmtx((fz_font*)font, gid, w);
+ if (error)
+ goto cleanup;
+ }
+ i += 3;
+ }
+ }
+
+ widths = fz_dropobj(widths);
+ }
+
+ error = fz_endhmtx((fz_font*)font);
+ if (error)
+ goto cleanup;
+
+ /*
+ * Vertical
+ */
+
+ if (fz_getwmode(font->encoding) == 1)
+ {
+ fz_obj *obj;
+ int dw2y = 880;
+ int dw2w = -1000;
+
+ obj = fz_dictgets(dict, "DW2");
+ if (obj)
+ {
+ dw2y = fz_toint(fz_arrayget(obj, 0));
+ dw2w = fz_toint(fz_arrayget(obj, 1));
+ }
+
+ fz_setdefaultvmtx((fz_font*)font, dw2y, dw2w);
+
+ widths = fz_dictgets(dict, "W2");
+ if (widths)
+ {
+ int c0, c1, w, x, y, k, gid;
+
+ error = pdf_resolve(&widths, xref);
+ if (error)
+ goto cleanup;
+
+printf(" W2 ");
+fz_fprintobj(stdout, widths);
+printf("\n");
+
+ for (i = 0; i < fz_arraylen(widths); )
+ {
+ c0 = fz_toint(fz_arrayget(widths, i));
+ obj = fz_arrayget(widths, i + 1);
+ if (fz_isarray(obj))
+ {
+ for (k = 0; k < fz_arraylen(obj); k += 3)
+ {
+ w = fz_toint(fz_arrayget(obj, k + 0));
+ x = fz_toint(fz_arrayget(obj, k + 1));
+ y = fz_toint(fz_arrayget(obj, k + 2));
+ gid = cidtogid(font, c0 + k);
+ error = fz_addvmtx((fz_font*)font, gid, x, y, w);
+ if (error)
+ goto cleanup;
+ }
+ i += 2;
+ }
+ else
+ {
+ c1 = fz_toint(obj);
+ w = fz_toint(fz_arrayget(widths, i + 2));
+ x = fz_toint(fz_arrayget(widths, i + 3));
+ y = fz_toint(fz_arrayget(widths, i + 4));
+ for (k = c0; k <= c1; k++)
+ {
+ gid = cidtogid(font, c0);
+ error = fz_addvmtx((fz_font*)font, gid, x, y, w);
+ if (error)
+ goto cleanup;
+ }
+ i += 5;
+ }
+ }
+
+ widths = fz_dropobj(widths);
+ }
+
+ error = fz_endvmtx((fz_font*)font);
+ if (error)
+ goto cleanup;
+ }
+
+ FT_Set_Char_Size(face, 64, 64, 72, 72);
+
+printf("\n");
+
+fz_debugfont((fz_font*)font);
+
+ return nil;
+
+cleanup:
+ if (widths)
+ fz_dropobj(widths);
+ fz_freefont((fz_font*)font);
+ *fontp = nil;
+ return error;
+}
+
+static fz_error *
+loadtype0(pdf_font **fontp, pdf_xref *xref, fz_obj *dict)
+{
+ fz_error *error;
+ fz_obj *dfonts;
+ fz_obj *dfont;
+ fz_obj *subtype;
+ fz_obj *encoding;
+
+ dfonts = fz_dictgets(dict, "DescendantFonts");
+ error = pdf_resolve(&dfonts, xref);
+ if (error)
+ return error;
+
+ dfont = fz_arrayget(dfonts, 0);
+ error = pdf_resolve(&dfont, xref);
+ if (error)
+ return fz_dropobj(dfonts), error;
+
+ encoding = fz_dictgets(dict, "Encoding");
+ subtype = fz_dictgets(dfont, "Subtype");
+
+ if (!strcmp(fz_toname(subtype), "CIDFontType0"))
+ error = loadcidfont(fontp, xref, dfont, encoding);
+ else if (!strcmp(fz_toname(subtype), "CIDFontType2"))
+ error = loadcidfont(fontp, xref, dfont, encoding);
+ else
+ error = fz_throw("syntaxerror: unknown cid font type");
+
+ fz_dropobj(dfont);
+ fz_dropobj(dfonts);
+
+ if (error)
+ return error;
+
+ return nil;
+}
+
+fz_error *
+pdf_loadfont(pdf_font **fontp, pdf_xref *xref, fz_obj *dict)
+{
+ char *subtype = fz_toname(fz_dictgets(dict, "Subtype"));
+ if (!strcmp(subtype, "Type0"))
+ return loadtype0(fontp, xref, dict);
+ if (!strcmp(subtype, "Type1") || !strcmp(subtype, "MMType1"))
+ return loadsimplefont(fontp, xref, dict);
+ else if (!strcmp(subtype, "TrueType"))
+ return loadsimplefont(fontp, xref, dict);
+ else
+ return fz_throw("unimplemented: %s fonts", subtype);
+}
+
diff --git a/mupdf/fontagl.h b/mupdf/fontagl.h
new file mode 100644
index 00000000..847c0a21
--- /dev/null
+++ b/mupdf/fontagl.h
@@ -0,0 +1,4305 @@
+/* Name: Adobe Glyph List
+ # Table version: 2.0
+ # Date: September 20, 2002
+ #
+ # See http:partners.adobe.com/asn/developer/typeforum/unicodegn.html
+ #
+ # Format: Semicolon-delimited fields:
+ # (1) glyph name
+ # (2) Unicode scalar value
+ */
+
+struct aglpair { char *name; int code; };
+
+#define adobeglyphlen (sizeof(adobeglyphlist) / sizeof(struct aglpair))
+
+static struct aglpair adobeglyphlist[] =
+{
+
+{"A",0x0041},
+{"AE",0x00C6},
+{"AEacute",0x01FC},
+{"AEmacron",0x01E2},
+{"AEsmall",0xF7E6},
+{"Aacute",0x00C1},
+{"Aacutesmall",0xF7E1},
+{"Abreve",0x0102},
+{"Abreveacute",0x1EAE},
+{"Abrevecyrillic",0x04D0},
+{"Abrevedotbelow",0x1EB6},
+{"Abrevegrave",0x1EB0},
+{"Abrevehookabove",0x1EB2},
+{"Abrevetilde",0x1EB4},
+{"Acaron",0x01CD},
+{"Acircle",0x24B6},
+{"Acircumflex",0x00C2},
+{"Acircumflexacute",0x1EA4},
+{"Acircumflexdotbelow",0x1EAC},
+{"Acircumflexgrave",0x1EA6},
+{"Acircumflexhookabove",0x1EA8},
+{"Acircumflexsmall",0xF7E2},
+{"Acircumflextilde",0x1EAA},
+{"Acute",0xF6C9},
+{"Acutesmall",0xF7B4},
+{"Acyrillic",0x0410},
+{"Adblgrave",0x0200},
+{"Adieresis",0x00C4},
+{"Adieresiscyrillic",0x04D2},
+{"Adieresismacron",0x01DE},
+{"Adieresissmall",0xF7E4},
+{"Adotbelow",0x1EA0},
+{"Adotmacron",0x01E0},
+{"Agrave",0x00C0},
+{"Agravesmall",0xF7E0},
+{"Ahookabove",0x1EA2},
+{"Aiecyrillic",0x04D4},
+{"Ainvertedbreve",0x0202},
+{"Alpha",0x0391},
+{"Alphatonos",0x0386},
+{"Amacron",0x0100},
+{"Amonospace",0xFF21},
+{"Aogonek",0x0104},
+{"Aring",0x00C5},
+{"Aringacute",0x01FA},
+{"Aringbelow",0x1E00},
+{"Aringsmall",0xF7E5},
+{"Asmall",0xF761},
+{"Atilde",0x00C3},
+{"Atildesmall",0xF7E3},
+{"Aybarmenian",0x0531},
+{"B",0x0042},
+{"Bcircle",0x24B7},
+{"Bdotaccent",0x1E02},
+{"Bdotbelow",0x1E04},
+{"Becyrillic",0x0411},
+{"Benarmenian",0x0532},
+{"Beta",0x0392},
+{"Bhook",0x0181},
+{"Blinebelow",0x1E06},
+{"Bmonospace",0xFF22},
+{"Brevesmall",0xF6F4},
+{"Bsmall",0xF762},
+{"Btopbar",0x0182},
+{"C",0x0043},
+{"Caarmenian",0x053E},
+{"Cacute",0x0106},
+{"Caron",0xF6CA},
+{"Caronsmall",0xF6F5},
+{"Ccaron",0x010C},
+{"Ccedilla",0x00C7},
+{"Ccedillaacute",0x1E08},
+{"Ccedillasmall",0xF7E7},
+{"Ccircle",0x24B8},
+{"Ccircumflex",0x0108},
+{"Cdot",0x010A},
+{"Cdotaccent",0x010A},
+{"Cedillasmall",0xF7B8},
+{"Chaarmenian",0x0549},
+{"Cheabkhasiancyrillic",0x04BC},
+{"Checyrillic",0x0427},
+{"Chedescenderabkhasiancyrillic",0x04BE},
+{"Chedescendercyrillic",0x04B6},
+{"Chedieresiscyrillic",0x04F4},
+{"Cheharmenian",0x0543},
+{"Chekhakassiancyrillic",0x04CB},
+{"Cheverticalstrokecyrillic",0x04B8},
+{"Chi",0x03A7},
+{"Chook",0x0187},
+{"Circumflexsmall",0xF6F6},
+{"Cmonospace",0xFF23},
+{"Coarmenian",0x0551},
+{"Csmall",0xF763},
+{"D",0x0044},
+{"DZ",0x01F1},
+{"DZcaron",0x01C4},
+{"Daarmenian",0x0534},
+{"Dafrican",0x0189},
+{"Dcaron",0x010E},
+{"Dcedilla",0x1E10},
+{"Dcircle",0x24B9},
+{"Dcircumflexbelow",0x1E12},
+{"Dcroat",0x0110},
+{"Ddotaccent",0x1E0A},
+{"Ddotbelow",0x1E0C},
+{"Decyrillic",0x0414},
+{"Deicoptic",0x03EE},
+{"Delta",0x2206},
+{"Deltagreek",0x0394},
+{"Dhook",0x018A},
+{"Dieresis",0xF6CB},
+{"DieresisAcute",0xF6CC},
+{"DieresisGrave",0xF6CD},
+{"Dieresissmall",0xF7A8},
+{"Digammagreek",0x03DC},
+{"Djecyrillic",0x0402},
+{"Dlinebelow",0x1E0E},
+{"Dmonospace",0xFF24},
+{"Dotaccentsmall",0xF6F7},
+{"Dslash",0x0110},
+{"Dsmall",0xF764},
+{"Dtopbar",0x018B},
+{"Dz",0x01F2},
+{"Dzcaron",0x01C5},
+{"Dzeabkhasiancyrillic",0x04E0},
+{"Dzecyrillic",0x0405},
+{"Dzhecyrillic",0x040F},
+{"E",0x0045},
+{"Eacute",0x00C9},
+{"Eacutesmall",0xF7E9},
+{"Ebreve",0x0114},
+{"Ecaron",0x011A},
+{"Ecedillabreve",0x1E1C},
+{"Echarmenian",0x0535},
+{"Ecircle",0x24BA},
+{"Ecircumflex",0x00CA},
+{"Ecircumflexacute",0x1EBE},
+{"Ecircumflexbelow",0x1E18},
+{"Ecircumflexdotbelow",0x1EC6},
+{"Ecircumflexgrave",0x1EC0},
+{"Ecircumflexhookabove",0x1EC2},
+{"Ecircumflexsmall",0xF7EA},
+{"Ecircumflextilde",0x1EC4},
+{"Ecyrillic",0x0404},
+{"Edblgrave",0x0204},
+{"Edieresis",0x00CB},
+{"Edieresissmall",0xF7EB},
+{"Edot",0x0116},
+{"Edotaccent",0x0116},
+{"Edotbelow",0x1EB8},
+{"Efcyrillic",0x0424},
+{"Egrave",0x00C8},
+{"Egravesmall",0xF7E8},
+{"Eharmenian",0x0537},
+{"Ehookabove",0x1EBA},
+{"Eightroman",0x2167},
+{"Einvertedbreve",0x0206},
+{"Eiotifiedcyrillic",0x0464},
+{"Elcyrillic",0x041B},
+{"Elevenroman",0x216A},
+{"Emacron",0x0112},
+{"Emacronacute",0x1E16},
+{"Emacrongrave",0x1E14},
+{"Emcyrillic",0x041C},
+{"Emonospace",0xFF25},
+{"Encyrillic",0x041D},
+{"Endescendercyrillic",0x04A2},
+{"Eng",0x014A},
+{"Enghecyrillic",0x04A4},
+{"Enhookcyrillic",0x04C7},
+{"Eogonek",0x0118},
+{"Eopen",0x0190},
+{"Epsilon",0x0395},
+{"Epsilontonos",0x0388},
+{"Ercyrillic",0x0420},
+{"Ereversed",0x018E},
+{"Ereversedcyrillic",0x042D},
+{"Escyrillic",0x0421},
+{"Esdescendercyrillic",0x04AA},
+{"Esh",0x01A9},
+{"Esmall",0xF765},
+{"Eta",0x0397},
+{"Etarmenian",0x0538},
+{"Etatonos",0x0389},
+{"Eth",0x00D0},
+{"Ethsmall",0xF7F0},
+{"Etilde",0x1EBC},
+{"Etildebelow",0x1E1A},
+{"Euro",0x20AC},
+{"Ezh",0x01B7},
+{"Ezhcaron",0x01EE},
+{"Ezhreversed",0x01B8},
+{"F",0x0046},
+{"Fcircle",0x24BB},
+{"Fdotaccent",0x1E1E},
+{"Feharmenian",0x0556},
+{"Feicoptic",0x03E4},
+{"Fhook",0x0191},
+{"Fitacyrillic",0x0472},
+{"Fiveroman",0x2164},
+{"Fmonospace",0xFF26},
+{"Fourroman",0x2163},
+{"Fsmall",0xF766},
+{"G",0x0047},
+{"GBsquare",0x3387},
+{"Gacute",0x01F4},
+{"Gamma",0x0393},
+{"Gammaafrican",0x0194},
+{"Gangiacoptic",0x03EA},
+{"Gbreve",0x011E},
+{"Gcaron",0x01E6},
+{"Gcedilla",0x0122},
+{"Gcircle",0x24BC},
+{"Gcircumflex",0x011C},
+{"Gcommaaccent",0x0122},
+{"Gdot",0x0120},
+{"Gdotaccent",0x0120},
+{"Gecyrillic",0x0413},
+{"Ghadarmenian",0x0542},
+{"Ghemiddlehookcyrillic",0x0494},
+{"Ghestrokecyrillic",0x0492},
+{"Gheupturncyrillic",0x0490},
+{"Ghook",0x0193},
+{"Gimarmenian",0x0533},
+{"Gjecyrillic",0x0403},
+{"Gmacron",0x1E20},
+{"Gmonospace",0xFF27},
+{"Grave",0xF6CE},
+{"Gravesmall",0xF760},
+{"Gsmall",0xF767},
+{"Gsmallhook",0x029B},
+{"Gstroke",0x01E4},
+{"H",0x0048},
+{"H18533",0x25CF},
+{"H18543",0x25AA},
+{"H18551",0x25AB},
+{"H22073",0x25A1},
+{"HPsquare",0x33CB},
+{"Haabkhasiancyrillic",0x04A8},
+{"Hadescendercyrillic",0x04B2},
+{"Hardsigncyrillic",0x042A},
+{"Hbar",0x0126},
+{"Hbrevebelow",0x1E2A},
+{"Hcedilla",0x1E28},
+{"Hcircle",0x24BD},
+{"Hcircumflex",0x0124},
+{"Hdieresis",0x1E26},
+{"Hdotaccent",0x1E22},
+{"Hdotbelow",0x1E24},
+{"Hmonospace",0xFF28},
+{"Hoarmenian",0x0540},
+{"Horicoptic",0x03E8},
+{"Hsmall",0xF768},
+{"Hungarumlaut",0xF6CF},
+{"Hungarumlautsmall",0xF6F8},
+{"Hzsquare",0x3390},
+{"I",0x0049},
+{"IAcyrillic",0x042F},
+{"IJ",0x0132},
+{"IUcyrillic",0x042E},
+{"Iacute",0x00CD},
+{"Iacutesmall",0xF7ED},
+{"Ibreve",0x012C},
+{"Icaron",0x01CF},
+{"Icircle",0x24BE},
+{"Icircumflex",0x00CE},
+{"Icircumflexsmall",0xF7EE},
+{"Icyrillic",0x0406},
+{"Idblgrave",0x0208},
+{"Idieresis",0x00CF},
+{"Idieresisacute",0x1E2E},
+{"Idieresiscyrillic",0x04E4},
+{"Idieresissmall",0xF7EF},
+{"Idot",0x0130},
+{"Idotaccent",0x0130},
+{"Idotbelow",0x1ECA},
+{"Iebrevecyrillic",0x04D6},
+{"Iecyrillic",0x0415},
+{"Ifraktur",0x2111},
+{"Igrave",0x00CC},
+{"Igravesmall",0xF7EC},
+{"Ihookabove",0x1EC8},
+{"Iicyrillic",0x0418},
+{"Iinvertedbreve",0x020A},
+{"Iishortcyrillic",0x0419},
+{"Imacron",0x012A},
+{"Imacroncyrillic",0x04E2},
+{"Imonospace",0xFF29},
+{"Iniarmenian",0x053B},
+{"Iocyrillic",0x0401},
+{"Iogonek",0x012E},
+{"Iota",0x0399},
+{"Iotaafrican",0x0196},
+{"Iotadieresis",0x03AA},
+{"Iotatonos",0x038A},
+{"Ismall",0xF769},
+{"Istroke",0x0197},
+{"Itilde",0x0128},
+{"Itildebelow",0x1E2C},
+{"Izhitsacyrillic",0x0474},
+{"Izhitsadblgravecyrillic",0x0476},
+{"J",0x004A},
+{"Jaarmenian",0x0541},
+{"Jcircle",0x24BF},
+{"Jcircumflex",0x0134},
+{"Jecyrillic",0x0408},
+{"Jheharmenian",0x054B},
+{"Jmonospace",0xFF2A},
+{"Jsmall",0xF76A},
+{"K",0x004B},
+{"KBsquare",0x3385},
+{"KKsquare",0x33CD},
+{"Kabashkircyrillic",0x04A0},
+{"Kacute",0x1E30},
+{"Kacyrillic",0x041A},
+{"Kadescendercyrillic",0x049A},
+{"Kahookcyrillic",0x04C3},
+{"Kappa",0x039A},
+{"Kastrokecyrillic",0x049E},
+{"Kaverticalstrokecyrillic",0x049C},
+{"Kcaron",0x01E8},
+{"Kcedilla",0x0136},
+{"Kcircle",0x24C0},
+{"Kcommaaccent",0x0136},
+{"Kdotbelow",0x1E32},
+{"Keharmenian",0x0554},
+{"Kenarmenian",0x053F},
+{"Khacyrillic",0x0425},
+{"Kheicoptic",0x03E6},
+{"Khook",0x0198},
+{"Kjecyrillic",0x040C},
+{"Klinebelow",0x1E34},
+{"Kmonospace",0xFF2B},
+{"Koppacyrillic",0x0480},
+{"Koppagreek",0x03DE},
+{"Ksicyrillic",0x046E},
+{"Ksmall",0xF76B},
+{"L",0x004C},
+{"LJ",0x01C7},
+{"LL",0xF6BF},
+{"Lacute",0x0139},
+{"Lambda",0x039B},
+{"Lcaron",0x013D},
+{"Lcedilla",0x013B},
+{"Lcircle",0x24C1},
+{"Lcircumflexbelow",0x1E3C},
+{"Lcommaaccent",0x013B},
+{"Ldot",0x013F},
+{"Ldotaccent",0x013F},
+{"Ldotbelow",0x1E36},
+{"Ldotbelowmacron",0x1E38},
+{"Liwnarmenian",0x053C},
+{"Lj",0x01C8},
+{"Ljecyrillic",0x0409},
+{"Llinebelow",0x1E3A},
+{"Lmonospace",0xFF2C},
+{"Lslash",0x0141},
+{"Lslashsmall",0xF6F9},
+{"Lsmall",0xF76C},
+{"M",0x004D},
+{"MBsquare",0x3386},
+{"Macron",0xF6D0},
+{"Macronsmall",0xF7AF},
+{"Macute",0x1E3E},
+{"Mcircle",0x24C2},
+{"Mdotaccent",0x1E40},
+{"Mdotbelow",0x1E42},
+{"Menarmenian",0x0544},
+{"Mmonospace",0xFF2D},
+{"Msmall",0xF76D},
+{"Mturned",0x019C},
+{"Mu",0x039C},
+{"N",0x004E},
+{"NJ",0x01CA},
+{"Nacute",0x0143},
+{"Ncaron",0x0147},
+{"Ncedilla",0x0145},
+{"Ncircle",0x24C3},
+{"Ncircumflexbelow",0x1E4A},
+{"Ncommaaccent",0x0145},
+{"Ndotaccent",0x1E44},
+{"Ndotbelow",0x1E46},
+{"Nhookleft",0x019D},
+{"Nineroman",0x2168},
+{"Nj",0x01CB},
+{"Njecyrillic",0x040A},
+{"Nlinebelow",0x1E48},
+{"Nmonospace",0xFF2E},
+{"Nowarmenian",0x0546},
+{"Nsmall",0xF76E},
+{"Ntilde",0x00D1},
+{"Ntildesmall",0xF7F1},
+{"Nu",0x039D},
+{"O",0x004F},
+{"OE",0x0152},
+{"OEsmall",0xF6FA},
+{"Oacute",0x00D3},
+{"Oacutesmall",0xF7F3},
+{"Obarredcyrillic",0x04E8},
+{"Obarreddieresiscyrillic",0x04EA},
+{"Obreve",0x014E},
+{"Ocaron",0x01D1},
+{"Ocenteredtilde",0x019F},
+{"Ocircle",0x24C4},
+{"Ocircumflex",0x00D4},
+{"Ocircumflexacute",0x1ED0},
+{"Ocircumflexdotbelow",0x1ED8},
+{"Ocircumflexgrave",0x1ED2},
+{"Ocircumflexhookabove",0x1ED4},
+{"Ocircumflexsmall",0xF7F4},
+{"Ocircumflextilde",0x1ED6},
+{"Ocyrillic",0x041E},
+{"Odblacute",0x0150},
+{"Odblgrave",0x020C},
+{"Odieresis",0x00D6},
+{"Odieresiscyrillic",0x04E6},
+{"Odieresissmall",0xF7F6},
+{"Odotbelow",0x1ECC},
+{"Ogoneksmall",0xF6FB},
+{"Ograve",0x00D2},
+{"Ogravesmall",0xF7F2},
+{"Oharmenian",0x0555},
+{"Ohm",0x2126},
+{"Ohookabove",0x1ECE},
+{"Ohorn",0x01A0},
+{"Ohornacute",0x1EDA},
+{"Ohorndotbelow",0x1EE2},
+{"Ohorngrave",0x1EDC},
+{"Ohornhookabove",0x1EDE},
+{"Ohorntilde",0x1EE0},
+{"Ohungarumlaut",0x0150},
+{"Oi",0x01A2},
+{"Oinvertedbreve",0x020E},
+{"Omacron",0x014C},
+{"Omacronacute",0x1E52},
+{"Omacrongrave",0x1E50},
+{"Omega",0x2126},
+{"Omegacyrillic",0x0460},
+{"Omegagreek",0x03A9},
+{"Omegaroundcyrillic",0x047A},
+{"Omegatitlocyrillic",0x047C},
+{"Omegatonos",0x038F},
+{"Omicron",0x039F},
+{"Omicrontonos",0x038C},
+{"Omonospace",0xFF2F},
+{"Oneroman",0x2160},
+{"Oogonek",0x01EA},
+{"Oogonekmacron",0x01EC},
+{"Oopen",0x0186},
+{"Oslash",0x00D8},
+{"Oslashacute",0x01FE},
+{"Oslashsmall",0xF7F8},
+{"Osmall",0xF76F},
+{"Ostrokeacute",0x01FE},
+{"Otcyrillic",0x047E},
+{"Otilde",0x00D5},
+{"Otildeacute",0x1E4C},
+{"Otildedieresis",0x1E4E},
+{"Otildesmall",0xF7F5},
+{"P",0x0050},
+{"Pacute",0x1E54},
+{"Pcircle",0x24C5},
+{"Pdotaccent",0x1E56},
+{"Pecyrillic",0x041F},
+{"Peharmenian",0x054A},
+{"Pemiddlehookcyrillic",0x04A6},
+{"Phi",0x03A6},
+{"Phook",0x01A4},
+{"Pi",0x03A0},
+{"Piwrarmenian",0x0553},
+{"Pmonospace",0xFF30},
+{"Psi",0x03A8},
+{"Psicyrillic",0x0470},
+{"Psmall",0xF770},
+{"Q",0x0051},
+{"Qcircle",0x24C6},
+{"Qmonospace",0xFF31},
+{"Qsmall",0xF771},
+{"R",0x0052},
+{"Raarmenian",0x054C},
+{"Racute",0x0154},
+{"Rcaron",0x0158},
+{"Rcedilla",0x0156},
+{"Rcircle",0x24C7},
+{"Rcommaaccent",0x0156},
+{"Rdblgrave",0x0210},
+{"Rdotaccent",0x1E58},
+{"Rdotbelow",0x1E5A},
+{"Rdotbelowmacron",0x1E5C},
+{"Reharmenian",0x0550},
+{"Rfraktur",0x211C},
+{"Rho",0x03A1},
+{"Ringsmall",0xF6FC},
+{"Rinvertedbreve",0x0212},
+{"Rlinebelow",0x1E5E},
+{"Rmonospace",0xFF32},
+{"Rsmall",0xF772},
+{"Rsmallinverted",0x0281},
+{"Rsmallinvertedsuperior",0x02B6},
+{"S",0x0053},
+{"SF010000",0x250C},
+{"SF020000",0x2514},
+{"SF030000",0x2510},
+{"SF040000",0x2518},
+{"SF050000",0x253C},
+{"SF060000",0x252C},
+{"SF070000",0x2534},
+{"SF080000",0x251C},
+{"SF090000",0x2524},
+{"SF100000",0x2500},
+{"SF110000",0x2502},
+{"SF190000",0x2561},
+{"SF200000",0x2562},
+{"SF210000",0x2556},
+{"SF220000",0x2555},
+{"SF230000",0x2563},
+{"SF240000",0x2551},
+{"SF250000",0x2557},
+{"SF260000",0x255D},
+{"SF270000",0x255C},
+{"SF280000",0x255B},
+{"SF360000",0x255E},
+{"SF370000",0x255F},
+{"SF380000",0x255A},
+{"SF390000",0x2554},
+{"SF400000",0x2569},
+{"SF410000",0x2566},
+{"SF420000",0x2560},
+{"SF430000",0x2550},
+{"SF440000",0x256C},
+{"SF450000",0x2567},
+{"SF460000",0x2568},
+{"SF470000",0x2564},
+{"SF480000",0x2565},
+{"SF490000",0x2559},
+{"SF500000",0x2558},
+{"SF510000",0x2552},
+{"SF520000",0x2553},
+{"SF530000",0x256B},
+{"SF540000",0x256A},
+{"Sacute",0x015A},
+{"Sacutedotaccent",0x1E64},
+{"Sampigreek",0x03E0},
+{"Scaron",0x0160},
+{"Scarondotaccent",0x1E66},
+{"Scaronsmall",0xF6FD},
+{"Scedilla",0x015E},
+{"Schwa",0x018F},
+{"Schwacyrillic",0x04D8},
+{"Schwadieresiscyrillic",0x04DA},
+{"Scircle",0x24C8},
+{"Scircumflex",0x015C},
+{"Scommaaccent",0x0218},
+{"Sdotaccent",0x1E60},
+{"Sdotbelow",0x1E62},
+{"Sdotbelowdotaccent",0x1E68},
+{"Seharmenian",0x054D},
+{"Sevenroman",0x2166},
+{"Shaarmenian",0x0547},
+{"Shacyrillic",0x0428},
+{"Shchacyrillic",0x0429},
+{"Sheicoptic",0x03E2},
+{"Shhacyrillic",0x04BA},
+{"Shimacoptic",0x03EC},
+{"Sigma",0x03A3},
+{"Sixroman",0x2165},
+{"Smonospace",0xFF33},
+{"Softsigncyrillic",0x042C},
+{"Ssmall",0xF773},
+{"Stigmagreek",0x03DA},
+{"T",0x0054},
+{"Tau",0x03A4},
+{"Tbar",0x0166},
+{"Tcaron",0x0164},
+{"Tcedilla",0x0162},
+{"Tcircle",0x24C9},
+{"Tcircumflexbelow",0x1E70},
+{"Tcommaaccent",0x0162},
+{"Tdotaccent",0x1E6A},
+{"Tdotbelow",0x1E6C},
+{"Tecyrillic",0x0422},
+{"Tedescendercyrillic",0x04AC},
+{"Tenroman",0x2169},
+{"Tetsecyrillic",0x04B4},
+{"Theta",0x0398},
+{"Thook",0x01AC},
+{"Thorn",0x00DE},
+{"Thornsmall",0xF7FE},
+{"Threeroman",0x2162},
+{"Tildesmall",0xF6FE},
+{"Tiwnarmenian",0x054F},
+{"Tlinebelow",0x1E6E},
+{"Tmonospace",0xFF34},
+{"Toarmenian",0x0539},
+{"Tonefive",0x01BC},
+{"Tonesix",0x0184},
+{"Tonetwo",0x01A7},
+{"Tretroflexhook",0x01AE},
+{"Tsecyrillic",0x0426},
+{"Tshecyrillic",0x040B},
+{"Tsmall",0xF774},
+{"Twelveroman",0x216B},
+{"Tworoman",0x2161},
+{"U",0x0055},
+{"Uacute",0x00DA},
+{"Uacutesmall",0xF7FA},
+{"Ubreve",0x016C},
+{"Ucaron",0x01D3},
+{"Ucircle",0x24CA},
+{"Ucircumflex",0x00DB},
+{"Ucircumflexbelow",0x1E76},
+{"Ucircumflexsmall",0xF7FB},
+{"Ucyrillic",0x0423},
+{"Udblacute",0x0170},
+{"Udblgrave",0x0214},
+{"Udieresis",0x00DC},
+{"Udieresisacute",0x01D7},
+{"Udieresisbelow",0x1E72},
+{"Udieresiscaron",0x01D9},
+{"Udieresiscyrillic",0x04F0},
+{"Udieresisgrave",0x01DB},
+{"Udieresismacron",0x01D5},
+{"Udieresissmall",0xF7FC},
+{"Udotbelow",0x1EE4},
+{"Ugrave",0x00D9},
+{"Ugravesmall",0xF7F9},
+{"Uhookabove",0x1EE6},
+{"Uhorn",0x01AF},
+{"Uhornacute",0x1EE8},
+{"Uhorndotbelow",0x1EF0},
+{"Uhorngrave",0x1EEA},
+{"Uhornhookabove",0x1EEC},
+{"Uhorntilde",0x1EEE},
+{"Uhungarumlaut",0x0170},
+{"Uhungarumlautcyrillic",0x04F2},
+{"Uinvertedbreve",0x0216},
+{"Ukcyrillic",0x0478},
+{"Umacron",0x016A},
+{"Umacroncyrillic",0x04EE},
+{"Umacrondieresis",0x1E7A},
+{"Umonospace",0xFF35},
+{"Uogonek",0x0172},
+{"Upsilon",0x03A5},
+{"Upsilon1",0x03D2},
+{"Upsilonacutehooksymbolgreek",0x03D3},
+{"Upsilonafrican",0x01B1},
+{"Upsilondieresis",0x03AB},
+{"Upsilondieresishooksymbolgreek",0x03D4},
+{"Upsilonhooksymbol",0x03D2},
+{"Upsilontonos",0x038E},
+{"Uring",0x016E},
+{"Ushortcyrillic",0x040E},
+{"Usmall",0xF775},
+{"Ustraightcyrillic",0x04AE},
+{"Ustraightstrokecyrillic",0x04B0},
+{"Utilde",0x0168},
+{"Utildeacute",0x1E78},
+{"Utildebelow",0x1E74},
+{"V",0x0056},
+{"Vcircle",0x24CB},
+{"Vdotbelow",0x1E7E},
+{"Vecyrillic",0x0412},
+{"Vewarmenian",0x054E},
+{"Vhook",0x01B2},
+{"Vmonospace",0xFF36},
+{"Voarmenian",0x0548},
+{"Vsmall",0xF776},
+{"Vtilde",0x1E7C},
+{"W",0x0057},
+{"Wacute",0x1E82},
+{"Wcircle",0x24CC},
+{"Wcircumflex",0x0174},
+{"Wdieresis",0x1E84},
+{"Wdotaccent",0x1E86},
+{"Wdotbelow",0x1E88},
+{"Wgrave",0x1E80},
+{"Wmonospace",0xFF37},
+{"Wsmall",0xF777},
+{"X",0x0058},
+{"Xcircle",0x24CD},
+{"Xdieresis",0x1E8C},
+{"Xdotaccent",0x1E8A},
+{"Xeharmenian",0x053D},
+{"Xi",0x039E},
+{"Xmonospace",0xFF38},
+{"Xsmall",0xF778},
+{"Y",0x0059},
+{"Yacute",0x00DD},
+{"Yacutesmall",0xF7FD},
+{"Yatcyrillic",0x0462},
+{"Ycircle",0x24CE},
+{"Ycircumflex",0x0176},
+{"Ydieresis",0x0178},
+{"Ydieresissmall",0xF7FF},
+{"Ydotaccent",0x1E8E},
+{"Ydotbelow",0x1EF4},
+{"Yericyrillic",0x042B},
+{"Yerudieresiscyrillic",0x04F8},
+{"Ygrave",0x1EF2},
+{"Yhook",0x01B3},
+{"Yhookabove",0x1EF6},
+{"Yiarmenian",0x0545},
+{"Yicyrillic",0x0407},
+{"Yiwnarmenian",0x0552},
+{"Ymonospace",0xFF39},
+{"Ysmall",0xF779},
+{"Ytilde",0x1EF8},
+{"Yusbigcyrillic",0x046A},
+{"Yusbigiotifiedcyrillic",0x046C},
+{"Yuslittlecyrillic",0x0466},
+{"Yuslittleiotifiedcyrillic",0x0468},
+{"Z",0x005A},
+{"Zaarmenian",0x0536},
+{"Zacute",0x0179},
+{"Zcaron",0x017D},
+{"Zcaronsmall",0xF6FF},
+{"Zcircle",0x24CF},
+{"Zcircumflex",0x1E90},
+{"Zdot",0x017B},
+{"Zdotaccent",0x017B},
+{"Zdotbelow",0x1E92},
+{"Zecyrillic",0x0417},
+{"Zedescendercyrillic",0x0498},
+{"Zedieresiscyrillic",0x04DE},
+{"Zeta",0x0396},
+{"Zhearmenian",0x053A},
+{"Zhebrevecyrillic",0x04C1},
+{"Zhecyrillic",0x0416},
+{"Zhedescendercyrillic",0x0496},
+{"Zhedieresiscyrillic",0x04DC},
+{"Zlinebelow",0x1E94},
+{"Zmonospace",0xFF3A},
+{"Zsmall",0xF77A},
+{"Zstroke",0x01B5},
+{"a",0x0061},
+{"aabengali",0x0986},
+{"aacute",0x00E1},
+{"aadeva",0x0906},
+{"aagujarati",0x0A86},
+{"aagurmukhi",0x0A06},
+{"aamatragurmukhi",0x0A3E},
+{"aarusquare",0x3303},
+{"aavowelsignbengali",0x09BE},
+{"aavowelsigndeva",0x093E},
+{"aavowelsigngujarati",0x0ABE},
+{"abbreviationmarkarmenian",0x055F},
+{"abbreviationsigndeva",0x0970},
+{"abengali",0x0985},
+{"abopomofo",0x311A},
+{"abreve",0x0103},
+{"abreveacute",0x1EAF},
+{"abrevecyrillic",0x04D1},
+{"abrevedotbelow",0x1EB7},
+{"abrevegrave",0x1EB1},
+{"abrevehookabove",0x1EB3},
+{"abrevetilde",0x1EB5},
+{"acaron",0x01CE},
+{"acircle",0x24D0},
+{"acircumflex",0x00E2},
+{"acircumflexacute",0x1EA5},
+{"acircumflexdotbelow",0x1EAD},
+{"acircumflexgrave",0x1EA7},
+{"acircumflexhookabove",0x1EA9},
+{"acircumflextilde",0x1EAB},
+{"acute",0x00B4},
+{"acutebelowcmb",0x0317},
+{"acutecmb",0x0301},
+{"acutecomb",0x0301},
+{"acutedeva",0x0954},
+{"acutelowmod",0x02CF},
+{"acutetonecmb",0x0341},
+{"acyrillic",0x0430},
+{"adblgrave",0x0201},
+{"addakgurmukhi",0x0A71},
+{"adeva",0x0905},
+{"adieresis",0x00E4},
+{"adieresiscyrillic",0x04D3},
+{"adieresismacron",0x01DF},
+{"adotbelow",0x1EA1},
+{"adotmacron",0x01E1},
+{"ae",0x00E6},
+{"aeacute",0x01FD},
+{"aekorean",0x3150},
+{"aemacron",0x01E3},
+{"afii00208",0x2015},
+{"afii08941",0x20A4},
+{"afii10017",0x0410},
+{"afii10018",0x0411},
+{"afii10019",0x0412},
+{"afii10020",0x0413},
+{"afii10021",0x0414},
+{"afii10022",0x0415},
+{"afii10023",0x0401},
+{"afii10024",0x0416},
+{"afii10025",0x0417},
+{"afii10026",0x0418},
+{"afii10027",0x0419},
+{"afii10028",0x041A},
+{"afii10029",0x041B},
+{"afii10030",0x041C},
+{"afii10031",0x041D},
+{"afii10032",0x041E},
+{"afii10033",0x041F},
+{"afii10034",0x0420},
+{"afii10035",0x0421},
+{"afii10036",0x0422},
+{"afii10037",0x0423},
+{"afii10038",0x0424},
+{"afii10039",0x0425},
+{"afii10040",0x0426},
+{"afii10041",0x0427},
+{"afii10042",0x0428},
+{"afii10043",0x0429},
+{"afii10044",0x042A},
+{"afii10045",0x042B},
+{"afii10046",0x042C},
+{"afii10047",0x042D},
+{"afii10048",0x042E},
+{"afii10049",0x042F},
+{"afii10050",0x0490},
+{"afii10051",0x0402},
+{"afii10052",0x0403},
+{"afii10053",0x0404},
+{"afii10054",0x0405},
+{"afii10055",0x0406},
+{"afii10056",0x0407},
+{"afii10057",0x0408},
+{"afii10058",0x0409},
+{"afii10059",0x040A},
+{"afii10060",0x040B},
+{"afii10061",0x040C},
+{"afii10062",0x040E},
+{"afii10063",0xF6C4},
+{"afii10064",0xF6C5},
+{"afii10065",0x0430},
+{"afii10066",0x0431},
+{"afii10067",0x0432},
+{"afii10068",0x0433},
+{"afii10069",0x0434},
+{"afii10070",0x0435},
+{"afii10071",0x0451},
+{"afii10072",0x0436},
+{"afii10073",0x0437},
+{"afii10074",0x0438},
+{"afii10075",0x0439},
+{"afii10076",0x043A},
+{"afii10077",0x043B},
+{"afii10078",0x043C},
+{"afii10079",0x043D},
+{"afii10080",0x043E},
+{"afii10081",0x043F},
+{"afii10082",0x0440},
+{"afii10083",0x0441},
+{"afii10084",0x0442},
+{"afii10085",0x0443},
+{"afii10086",0x0444},
+{"afii10087",0x0445},
+{"afii10088",0x0446},
+{"afii10089",0x0447},
+{"afii10090",0x0448},
+{"afii10091",0x0449},
+{"afii10092",0x044A},
+{"afii10093",0x044B},
+{"afii10094",0x044C},
+{"afii10095",0x044D},
+{"afii10096",0x044E},
+{"afii10097",0x044F},
+{"afii10098",0x0491},
+{"afii10099",0x0452},
+{"afii10100",0x0453},
+{"afii10101",0x0454},
+{"afii10102",0x0455},
+{"afii10103",0x0456},
+{"afii10104",0x0457},
+{"afii10105",0x0458},
+{"afii10106",0x0459},
+{"afii10107",0x045A},
+{"afii10108",0x045B},
+{"afii10109",0x045C},
+{"afii10110",0x045E},
+{"afii10145",0x040F},
+{"afii10146",0x0462},
+{"afii10147",0x0472},
+{"afii10148",0x0474},
+{"afii10192",0xF6C6},
+{"afii10193",0x045F},
+{"afii10194",0x0463},
+{"afii10195",0x0473},
+{"afii10196",0x0475},
+{"afii10831",0xF6C7},
+{"afii10832",0xF6C8},
+{"afii10846",0x04D9},
+{"afii299",0x200E},
+{"afii300",0x200F},
+{"afii301",0x200D},
+{"afii57381",0x066A},
+{"afii57388",0x060C},
+{"afii57392",0x0660},
+{"afii57393",0x0661},
+{"afii57394",0x0662},
+{"afii57395",0x0663},
+{"afii57396",0x0664},
+{"afii57397",0x0665},
+{"afii57398",0x0666},
+{"afii57399",0x0667},
+{"afii57400",0x0668},
+{"afii57401",0x0669},
+{"afii57403",0x061B},
+{"afii57407",0x061F},
+{"afii57409",0x0621},
+{"afii57410",0x0622},
+{"afii57411",0x0623},
+{"afii57412",0x0624},
+{"afii57413",0x0625},
+{"afii57414",0x0626},
+{"afii57415",0x0627},
+{"afii57416",0x0628},
+{"afii57417",0x0629},
+{"afii57418",0x062A},
+{"afii57419",0x062B},
+{"afii57420",0x062C},
+{"afii57421",0x062D},
+{"afii57422",0x062E},
+{"afii57423",0x062F},
+{"afii57424",0x0630},
+{"afii57425",0x0631},
+{"afii57426",0x0632},
+{"afii57427",0x0633},
+{"afii57428",0x0634},
+{"afii57429",0x0635},
+{"afii57430",0x0636},
+{"afii57431",0x0637},
+{"afii57432",0x0638},
+{"afii57433",0x0639},
+{"afii57434",0x063A},
+{"afii57440",0x0640},
+{"afii57441",0x0641},
+{"afii57442",0x0642},
+{"afii57443",0x0643},
+{"afii57444",0x0644},
+{"afii57445",0x0645},
+{"afii57446",0x0646},
+{"afii57448",0x0648},
+{"afii57449",0x0649},
+{"afii57450",0x064A},
+{"afii57451",0x064B},
+{"afii57452",0x064C},
+{"afii57453",0x064D},
+{"afii57454",0x064E},
+{"afii57455",0x064F},
+{"afii57456",0x0650},
+{"afii57457",0x0651},
+{"afii57458",0x0652},
+{"afii57470",0x0647},
+{"afii57505",0x06A4},
+{"afii57506",0x067E},
+{"afii57507",0x0686},
+{"afii57508",0x0698},
+{"afii57509",0x06AF},
+{"afii57511",0x0679},
+{"afii57512",0x0688},
+{"afii57513",0x0691},
+{"afii57514",0x06BA},
+{"afii57519",0x06D2},
+{"afii57534",0x06D5},
+{"afii57636",0x20AA},
+{"afii57645",0x05BE},
+{"afii57658",0x05C3},
+{"afii57664",0x05D0},
+{"afii57665",0x05D1},
+{"afii57666",0x05D2},
+{"afii57667",0x05D3},
+{"afii57668",0x05D4},
+{"afii57669",0x05D5},
+{"afii57670",0x05D6},
+{"afii57671",0x05D7},
+{"afii57672",0x05D8},
+{"afii57673",0x05D9},
+{"afii57674",0x05DA},
+{"afii57675",0x05DB},
+{"afii57676",0x05DC},
+{"afii57677",0x05DD},
+{"afii57678",0x05DE},
+{"afii57679",0x05DF},
+{"afii57680",0x05E0},
+{"afii57681",0x05E1},
+{"afii57682",0x05E2},
+{"afii57683",0x05E3},
+{"afii57684",0x05E4},
+{"afii57685",0x05E5},
+{"afii57686",0x05E6},
+{"afii57687",0x05E7},
+{"afii57688",0x05E8},
+{"afii57689",0x05E9},
+{"afii57690",0x05EA},
+{"afii57694",0xFB2A},
+{"afii57695",0xFB2B},
+{"afii57700",0xFB4B},
+{"afii57705",0xFB1F},
+{"afii57716",0x05F0},
+{"afii57717",0x05F1},
+{"afii57718",0x05F2},
+{"afii57723",0xFB35},
+{"afii57793",0x05B4},
+{"afii57794",0x05B5},
+{"afii57795",0x05B6},
+{"afii57796",0x05BB},
+{"afii57797",0x05B8},
+{"afii57798",0x05B7},
+{"afii57799",0x05B0},
+{"afii57800",0x05B2},
+{"afii57801",0x05B1},
+{"afii57802",0x05B3},
+{"afii57803",0x05C2},
+{"afii57804",0x05C1},
+{"afii57806",0x05B9},
+{"afii57807",0x05BC},
+{"afii57839",0x05BD},
+{"afii57841",0x05BF},
+{"afii57842",0x05C0},
+{"afii57929",0x02BC},
+{"afii61248",0x2105},
+{"afii61289",0x2113},
+{"afii61352",0x2116},
+{"afii61573",0x202C},
+{"afii61574",0x202D},
+{"afii61575",0x202E},
+{"afii61664",0x200C},
+{"afii63167",0x066D},
+{"afii64937",0x02BD},
+{"agrave",0x00E0},
+{"agujarati",0x0A85},
+{"agurmukhi",0x0A05},
+{"ahiragana",0x3042},
+{"ahookabove",0x1EA3},
+{"aibengali",0x0990},
+{"aibopomofo",0x311E},
+{"aideva",0x0910},
+{"aiecyrillic",0x04D5},
+{"aigujarati",0x0A90},
+{"aigurmukhi",0x0A10},
+{"aimatragurmukhi",0x0A48},
+{"ainarabic",0x0639},
+{"ainfinalarabic",0xFECA},
+{"aininitialarabic",0xFECB},
+{"ainmedialarabic",0xFECC},
+{"ainvertedbreve",0x0203},
+{"aivowelsignbengali",0x09C8},
+{"aivowelsigndeva",0x0948},
+{"aivowelsigngujarati",0x0AC8},
+{"akatakana",0x30A2},
+{"akatakanahalfwidth",0xFF71},
+{"akorean",0x314F},
+{"alef",0x05D0},
+{"alefarabic",0x0627},
+{"alefdageshhebrew",0xFB30},
+{"aleffinalarabic",0xFE8E},
+{"alefhamzaabovearabic",0x0623},
+{"alefhamzaabovefinalarabic",0xFE84},
+{"alefhamzabelowarabic",0x0625},
+{"alefhamzabelowfinalarabic",0xFE88},
+{"alefhebrew",0x05D0},
+{"aleflamedhebrew",0xFB4F},
+{"alefmaddaabovearabic",0x0622},
+{"alefmaddaabovefinalarabic",0xFE82},
+{"alefmaksuraarabic",0x0649},
+{"alefmaksurafinalarabic",0xFEF0},
+{"alefmaksurainitialarabic",0xFEF3},
+{"alefmaksuramedialarabic",0xFEF4},
+{"alefpatahhebrew",0xFB2E},
+{"alefqamatshebrew",0xFB2F},
+{"aleph",0x2135},
+{"allequal",0x224C},
+{"alpha",0x03B1},
+{"alphatonos",0x03AC},
+{"amacron",0x0101},
+{"amonospace",0xFF41},
+{"ampersand",0x0026},
+{"ampersandmonospace",0xFF06},
+{"ampersandsmall",0xF726},
+{"amsquare",0x33C2},
+{"anbopomofo",0x3122},
+{"angbopomofo",0x3124},
+{"angkhankhuthai",0x0E5A},
+{"angle",0x2220},
+{"anglebracketleft",0x3008},
+{"anglebracketleftvertical",0xFE3F},
+{"anglebracketright",0x3009},
+{"anglebracketrightvertical",0xFE40},
+{"angleleft",0x2329},
+{"angleright",0x232A},
+{"angstrom",0x212B},
+{"anoteleia",0x0387},
+{"anudattadeva",0x0952},
+{"anusvarabengali",0x0982},
+{"anusvaradeva",0x0902},
+{"anusvaragujarati",0x0A82},
+{"aogonek",0x0105},
+{"apaatosquare",0x3300},
+{"aparen",0x249C},
+{"apostrophearmenian",0x055A},
+{"apostrophemod",0x02BC},
+{"apple",0xF8FF},
+{"approaches",0x2250},
+{"approxequal",0x2248},
+{"approxequalorimage",0x2252},
+{"approximatelyequal",0x2245},
+{"araeaekorean",0x318E},
+{"araeakorean",0x318D},
+{"arc",0x2312},
+{"arighthalfring",0x1E9A},
+{"aring",0x00E5},
+{"aringacute",0x01FB},
+{"aringbelow",0x1E01},
+{"arrowboth",0x2194},
+{"arrowdashdown",0x21E3},
+{"arrowdashleft",0x21E0},
+{"arrowdashright",0x21E2},
+{"arrowdashup",0x21E1},
+{"arrowdblboth",0x21D4},
+{"arrowdbldown",0x21D3},
+{"arrowdblleft",0x21D0},
+{"arrowdblright",0x21D2},
+{"arrowdblup",0x21D1},
+{"arrowdown",0x2193},
+{"arrowdownleft",0x2199},
+{"arrowdownright",0x2198},
+{"arrowdownwhite",0x21E9},
+{"arrowheaddownmod",0x02C5},
+{"arrowheadleftmod",0x02C2},
+{"arrowheadrightmod",0x02C3},
+{"arrowheadupmod",0x02C4},
+{"arrowhorizex",0xF8E7},
+{"arrowleft",0x2190},
+{"arrowleftdbl",0x21D0},
+{"arrowleftdblstroke",0x21CD},
+{"arrowleftoverright",0x21C6},
+{"arrowleftwhite",0x21E6},
+{"arrowright",0x2192},
+{"arrowrightdblstroke",0x21CF},
+{"arrowrightheavy",0x279E},
+{"arrowrightoverleft",0x21C4},
+{"arrowrightwhite",0x21E8},
+{"arrowtableft",0x21E4},
+{"arrowtabright",0x21E5},
+{"arrowup",0x2191},
+{"arrowupdn",0x2195},
+{"arrowupdnbse",0x21A8},
+{"arrowupdownbase",0x21A8},
+{"arrowupleft",0x2196},
+{"arrowupleftofdown",0x21C5},
+{"arrowupright",0x2197},
+{"arrowupwhite",0x21E7},
+{"arrowvertex",0xF8E6},
+{"asciicircum",0x005E},
+{"asciicircummonospace",0xFF3E},
+{"asciitilde",0x007E},
+{"asciitildemonospace",0xFF5E},
+{"ascript",0x0251},
+{"ascriptturned",0x0252},
+{"asmallhiragana",0x3041},
+{"asmallkatakana",0x30A1},
+{"asmallkatakanahalfwidth",0xFF67},
+{"asterisk",0x002A},
+{"asteriskaltonearabic",0x066D},
+{"asteriskarabic",0x066D},
+{"asteriskmath",0x2217},
+{"asteriskmonospace",0xFF0A},
+{"asterisksmall",0xFE61},
+{"asterism",0x2042},
+{"asuperior",0xF6E9},
+{"asymptoticallyequal",0x2243},
+{"at",0x0040},
+{"atilde",0x00E3},
+{"atmonospace",0xFF20},
+{"atsmall",0xFE6B},
+{"aturned",0x0250},
+{"aubengali",0x0994},
+{"aubopomofo",0x3120},
+{"audeva",0x0914},
+{"augujarati",0x0A94},
+{"augurmukhi",0x0A14},
+{"aulengthmarkbengali",0x09D7},
+{"aumatragurmukhi",0x0A4C},
+{"auvowelsignbengali",0x09CC},
+{"auvowelsigndeva",0x094C},
+{"auvowelsigngujarati",0x0ACC},
+{"avagrahadeva",0x093D},
+{"aybarmenian",0x0561},
+{"ayin",0x05E2},
+{"ayinaltonehebrew",0xFB20},
+{"ayinhebrew",0x05E2},
+{"b",0x0062},
+{"babengali",0x09AC},
+{"backslash",0x005C},
+{"backslashmonospace",0xFF3C},
+{"badeva",0x092C},
+{"bagujarati",0x0AAC},
+{"bagurmukhi",0x0A2C},
+{"bahiragana",0x3070},
+{"bahtthai",0x0E3F},
+{"bakatakana",0x30D0},
+{"bar",0x007C},
+{"barmonospace",0xFF5C},
+{"bbopomofo",0x3105},
+{"bcircle",0x24D1},
+{"bdotaccent",0x1E03},
+{"bdotbelow",0x1E05},
+{"beamedsixteenthnotes",0x266C},
+{"because",0x2235},
+{"becyrillic",0x0431},
+{"beharabic",0x0628},
+{"behfinalarabic",0xFE90},
+{"behinitialarabic",0xFE91},
+{"behiragana",0x3079},
+{"behmedialarabic",0xFE92},
+{"behmeeminitialarabic",0xFC9F},
+{"behmeemisolatedarabic",0xFC08},
+{"behnoonfinalarabic",0xFC6D},
+{"bekatakana",0x30D9},
+{"benarmenian",0x0562},
+{"bet",0x05D1},
+{"beta",0x03B2},
+{"betasymbolgreek",0x03D0},
+{"betdagesh",0xFB31},
+{"betdageshhebrew",0xFB31},
+{"bethebrew",0x05D1},
+{"betrafehebrew",0xFB4C},
+{"bhabengali",0x09AD},
+{"bhadeva",0x092D},
+{"bhagujarati",0x0AAD},
+{"bhagurmukhi",0x0A2D},
+{"bhook",0x0253},
+{"bihiragana",0x3073},
+{"bikatakana",0x30D3},
+{"bilabialclick",0x0298},
+{"bindigurmukhi",0x0A02},
+{"birusquare",0x3331},
+{"blackcircle",0x25CF},
+{"blackdiamond",0x25C6},
+{"blackdownpointingtriangle",0x25BC},
+{"blackleftpointingpointer",0x25C4},
+{"blackleftpointingtriangle",0x25C0},
+{"blacklenticularbracketleft",0x3010},
+{"blacklenticularbracketleftvertical",0xFE3B},
+{"blacklenticularbracketright",0x3011},
+{"blacklenticularbracketrightvertical",0xFE3C},
+{"blacklowerlefttriangle",0x25E3},
+{"blacklowerrighttriangle",0x25E2},
+{"blackrectangle",0x25AC},
+{"blackrightpointingpointer",0x25BA},
+{"blackrightpointingtriangle",0x25B6},
+{"blacksmallsquare",0x25AA},
+{"blacksmilingface",0x263B},
+{"blacksquare",0x25A0},
+{"blackstar",0x2605},
+{"blackupperlefttriangle",0x25E4},
+{"blackupperrighttriangle",0x25E5},
+{"blackuppointingsmalltriangle",0x25B4},
+{"blackuppointingtriangle",0x25B2},
+{"blank",0x2423},
+{"blinebelow",0x1E07},
+{"block",0x2588},
+{"bmonospace",0xFF42},
+{"bobaimaithai",0x0E1A},
+{"bohiragana",0x307C},
+{"bokatakana",0x30DC},
+{"bparen",0x249D},
+{"bqsquare",0x33C3},
+{"braceex",0xF8F4},
+{"braceleft",0x007B},
+{"braceleftbt",0xF8F3},
+{"braceleftmid",0xF8F2},
+{"braceleftmonospace",0xFF5B},
+{"braceleftsmall",0xFE5B},
+{"bracelefttp",0xF8F1},
+{"braceleftvertical",0xFE37},
+{"braceright",0x007D},
+{"bracerightbt",0xF8FE},
+{"bracerightmid",0xF8FD},
+{"bracerightmonospace",0xFF5D},
+{"bracerightsmall",0xFE5C},
+{"bracerighttp",0xF8FC},
+{"bracerightvertical",0xFE38},
+{"bracketleft",0x005B},
+{"bracketleftbt",0xF8F0},
+{"bracketleftex",0xF8EF},
+{"bracketleftmonospace",0xFF3B},
+{"bracketlefttp",0xF8EE},
+{"bracketright",0x005D},
+{"bracketrightbt",0xF8FB},
+{"bracketrightex",0xF8FA},
+{"bracketrightmonospace",0xFF3D},
+{"bracketrighttp",0xF8F9},
+{"breve",0x02D8},
+{"brevebelowcmb",0x032E},
+{"brevecmb",0x0306},
+{"breveinvertedbelowcmb",0x032F},
+{"breveinvertedcmb",0x0311},
+{"breveinverteddoublecmb",0x0361},
+{"bridgebelowcmb",0x032A},
+{"bridgeinvertedbelowcmb",0x033A},
+{"brokenbar",0x00A6},
+{"bstroke",0x0180},
+{"bsuperior",0xF6EA},
+{"btopbar",0x0183},
+{"buhiragana",0x3076},
+{"bukatakana",0x30D6},
+{"bullet",0x2022},
+{"bulletinverse",0x25D8},
+{"bulletoperator",0x2219},
+{"bullseye",0x25CE},
+{"c",0x0063},
+{"caarmenian",0x056E},
+{"cabengali",0x099A},
+{"cacute",0x0107},
+{"cadeva",0x091A},
+{"cagujarati",0x0A9A},
+{"cagurmukhi",0x0A1A},
+{"calsquare",0x3388},
+{"candrabindubengali",0x0981},
+{"candrabinducmb",0x0310},
+{"candrabindudeva",0x0901},
+{"candrabindugujarati",0x0A81},
+{"capslock",0x21EA},
+{"careof",0x2105},
+{"caron",0x02C7},
+{"caronbelowcmb",0x032C},
+{"caroncmb",0x030C},
+{"carriagereturn",0x21B5},
+{"cbopomofo",0x3118},
+{"ccaron",0x010D},
+{"ccedilla",0x00E7},
+{"ccedillaacute",0x1E09},
+{"ccircle",0x24D2},
+{"ccircumflex",0x0109},
+{"ccurl",0x0255},
+{"cdot",0x010B},
+{"cdotaccent",0x010B},
+{"cdsquare",0x33C5},
+{"cedilla",0x00B8},
+{"cedillacmb",0x0327},
+{"cent",0x00A2},
+{"centigrade",0x2103},
+{"centinferior",0xF6DF},
+{"centmonospace",0xFFE0},
+{"centoldstyle",0xF7A2},
+{"centsuperior",0xF6E0},
+{"chaarmenian",0x0579},
+{"chabengali",0x099B},
+{"chadeva",0x091B},
+{"chagujarati",0x0A9B},
+{"chagurmukhi",0x0A1B},
+{"chbopomofo",0x3114},
+{"cheabkhasiancyrillic",0x04BD},
+{"checkmark",0x2713},
+{"checyrillic",0x0447},
+{"chedescenderabkhasiancyrillic",0x04BF},
+{"chedescendercyrillic",0x04B7},
+{"chedieresiscyrillic",0x04F5},
+{"cheharmenian",0x0573},
+{"chekhakassiancyrillic",0x04CC},
+{"cheverticalstrokecyrillic",0x04B9},
+{"chi",0x03C7},
+{"chieuchacirclekorean",0x3277},
+{"chieuchaparenkorean",0x3217},
+{"chieuchcirclekorean",0x3269},
+{"chieuchkorean",0x314A},
+{"chieuchparenkorean",0x3209},
+{"chochangthai",0x0E0A},
+{"chochanthai",0x0E08},
+{"chochingthai",0x0E09},
+{"chochoethai",0x0E0C},
+{"chook",0x0188},
+{"cieucacirclekorean",0x3276},
+{"cieucaparenkorean",0x3216},
+{"cieuccirclekorean",0x3268},
+{"cieuckorean",0x3148},
+{"cieucparenkorean",0x3208},
+{"cieucuparenkorean",0x321C},
+{"circle",0x25CB},
+{"circlemultiply",0x2297},
+{"circleot",0x2299},
+{"circleplus",0x2295},
+{"circlepostalmark",0x3036},
+{"circlewithlefthalfblack",0x25D0},
+{"circlewithrighthalfblack",0x25D1},
+{"circumflex",0x02C6},
+{"circumflexbelowcmb",0x032D},
+{"circumflexcmb",0x0302},
+{"clear",0x2327},
+{"clickalveolar",0x01C2},
+{"clickdental",0x01C0},
+{"clicklateral",0x01C1},
+{"clickretroflex",0x01C3},
+{"club",0x2663},
+{"clubsuitblack",0x2663},
+{"clubsuitwhite",0x2667},
+{"cmcubedsquare",0x33A4},
+{"cmonospace",0xFF43},
+{"cmsquaredsquare",0x33A0},
+{"coarmenian",0x0581},
+{"colon",0x003A},
+{"colonmonetary",0x20A1},
+{"colonmonospace",0xFF1A},
+{"colonsign",0x20A1},
+{"colonsmall",0xFE55},
+{"colontriangularhalfmod",0x02D1},
+{"colontriangularmod",0x02D0},
+{"comma",0x002C},
+{"commaabovecmb",0x0313},
+{"commaaboverightcmb",0x0315},
+{"commaaccent",0xF6C3},
+{"commaarabic",0x060C},
+{"commaarmenian",0x055D},
+{"commainferior",0xF6E1},
+{"commamonospace",0xFF0C},
+{"commareversedabovecmb",0x0314},
+{"commareversedmod",0x02BD},
+{"commasmall",0xFE50},
+{"commasuperior",0xF6E2},
+{"commaturnedabovecmb",0x0312},
+{"commaturnedmod",0x02BB},
+{"compass",0x263C},
+{"congruent",0x2245},
+{"contourintegral",0x222E},
+{"control",0x2303},
+{"controlACK",0x0006},
+{"controlBEL",0x0007},
+{"controlBS",0x0008},
+{"controlCAN",0x0018},
+{"controlCR",0x000D},
+{"controlDC1",0x0011},
+{"controlDC2",0x0012},
+{"controlDC3",0x0013},
+{"controlDC4",0x0014},
+{"controlDEL",0x007F},
+{"controlDLE",0x0010},
+{"controlEM",0x0019},
+{"controlENQ",0x0005},
+{"controlEOT",0x0004},
+{"controlESC",0x001B},
+{"controlETB",0x0017},
+{"controlETX",0x0003},
+{"controlFF",0x000C},
+{"controlFS",0x001C},
+{"controlGS",0x001D},
+{"controlHT",0x0009},
+{"controlLF",0x000A},
+{"controlNAK",0x0015},
+{"controlRS",0x001E},
+{"controlSI",0x000F},
+{"controlSO",0x000E},
+{"controlSOT",0x0002},
+{"controlSTX",0x0001},
+{"controlSUB",0x001A},
+{"controlSYN",0x0016},
+{"controlUS",0x001F},
+{"controlVT",0x000B},
+{"copyright",0x00A9},
+{"copyrightsans",0xF8E9},
+{"copyrightserif",0xF6D9},
+{"cornerbracketleft",0x300C},
+{"cornerbracketlefthalfwidth",0xFF62},
+{"cornerbracketleftvertical",0xFE41},
+{"cornerbracketright",0x300D},
+{"cornerbracketrighthalfwidth",0xFF63},
+{"cornerbracketrightvertical",0xFE42},
+{"corporationsquare",0x337F},
+{"cosquare",0x33C7},
+{"coverkgsquare",0x33C6},
+{"cparen",0x249E},
+{"cruzeiro",0x20A2},
+{"cstretched",0x0297},
+{"curlyand",0x22CF},
+{"curlyor",0x22CE},
+{"currency",0x00A4},
+{"cyrBreve",0xF6D1},
+{"cyrFlex",0xF6D2},
+{"cyrbreve",0xF6D4},
+{"cyrflex",0xF6D5},
+{"d",0x0064},
+{"daarmenian",0x0564},
+{"dabengali",0x09A6},
+{"dadarabic",0x0636},
+{"dadeva",0x0926},
+{"dadfinalarabic",0xFEBE},
+{"dadinitialarabic",0xFEBF},
+{"dadmedialarabic",0xFEC0},
+{"dagesh",0x05BC},
+{"dageshhebrew",0x05BC},
+{"dagger",0x2020},
+{"daggerdbl",0x2021},
+{"dagujarati",0x0AA6},
+{"dagurmukhi",0x0A26},
+{"dahiragana",0x3060},
+{"dakatakana",0x30C0},
+{"dalarabic",0x062F},
+{"dalet",0x05D3},
+{"daletdagesh",0xFB33},
+{"daletdageshhebrew",0xFB33},
+{"dalethebrew",0x05D3},
+{"dalfinalarabic",0xFEAA},
+{"dammaarabic",0x064F},
+{"dammalowarabic",0x064F},
+{"dammatanaltonearabic",0x064C},
+{"dammatanarabic",0x064C},
+{"danda",0x0964},
+{"dargahebrew",0x05A7},
+{"dargalefthebrew",0x05A7},
+{"dasiapneumatacyrilliccmb",0x0485},
+{"dblGrave",0xF6D3},
+{"dblanglebracketleft",0x300A},
+{"dblanglebracketleftvertical",0xFE3D},
+{"dblanglebracketright",0x300B},
+{"dblanglebracketrightvertical",0xFE3E},
+{"dblarchinvertedbelowcmb",0x032B},
+{"dblarrowleft",0x21D4},
+{"dblarrowright",0x21D2},
+{"dbldanda",0x0965},
+{"dblgrave",0xF6D6},
+{"dblgravecmb",0x030F},
+{"dblintegral",0x222C},
+{"dbllowline",0x2017},
+{"dbllowlinecmb",0x0333},
+{"dbloverlinecmb",0x033F},
+{"dblprimemod",0x02BA},
+{"dblverticalbar",0x2016},
+{"dblverticallineabovecmb",0x030E},
+{"dbopomofo",0x3109},
+{"dbsquare",0x33C8},
+{"dcaron",0x010F},
+{"dcedilla",0x1E11},
+{"dcircle",0x24D3},
+{"dcircumflexbelow",0x1E13},
+{"dcroat",0x0111},
+{"ddabengali",0x09A1},
+{"ddadeva",0x0921},
+{"ddagujarati",0x0AA1},
+{"ddagurmukhi",0x0A21},
+{"ddalarabic",0x0688},
+{"ddalfinalarabic",0xFB89},
+{"dddhadeva",0x095C},
+{"ddhabengali",0x09A2},
+{"ddhadeva",0x0922},
+{"ddhagujarati",0x0AA2},
+{"ddhagurmukhi",0x0A22},
+{"ddotaccent",0x1E0B},
+{"ddotbelow",0x1E0D},
+{"decimalseparatorarabic",0x066B},
+{"decimalseparatorpersian",0x066B},
+{"decyrillic",0x0434},
+{"degree",0x00B0},
+{"dehihebrew",0x05AD},
+{"dehiragana",0x3067},
+{"deicoptic",0x03EF},
+{"dekatakana",0x30C7},
+{"deleteleft",0x232B},
+{"deleteright",0x2326},
+{"delta",0x03B4},
+{"deltaturned",0x018D},
+{"denominatorminusonenumeratorbengali",0x09F8},
+{"dezh",0x02A4},
+{"dhabengali",0x09A7},
+{"dhadeva",0x0927},
+{"dhagujarati",0x0AA7},
+{"dhagurmukhi",0x0A27},
+{"dhook",0x0257},
+{"dialytikatonos",0x0385},
+{"dialytikatonoscmb",0x0344},
+{"diamond",0x2666},
+{"diamondsuitwhite",0x2662},
+{"dieresis",0x00A8},
+{"dieresisacute",0xF6D7},
+{"dieresisbelowcmb",0x0324},
+{"dieresiscmb",0x0308},
+{"dieresisgrave",0xF6D8},
+{"dieresistonos",0x0385},
+{"dihiragana",0x3062},
+{"dikatakana",0x30C2},
+{"dittomark",0x3003},
+{"divide",0x00F7},
+{"divides",0x2223},
+{"divisionslash",0x2215},
+{"djecyrillic",0x0452},
+{"dkshade",0x2593},
+{"dlinebelow",0x1E0F},
+{"dlsquare",0x3397},
+{"dmacron",0x0111},
+{"dmonospace",0xFF44},
+{"dnblock",0x2584},
+{"dochadathai",0x0E0E},
+{"dodekthai",0x0E14},
+{"dohiragana",0x3069},
+{"dokatakana",0x30C9},
+{"dollar",0x0024},
+{"dollarinferior",0xF6E3},
+{"dollarmonospace",0xFF04},
+{"dollaroldstyle",0xF724},
+{"dollarsmall",0xFE69},
+{"dollarsuperior",0xF6E4},
+{"dong",0x20AB},
+{"dorusquare",0x3326},
+{"dotaccent",0x02D9},
+{"dotaccentcmb",0x0307},
+{"dotbelowcmb",0x0323},
+{"dotbelowcomb",0x0323},
+{"dotkatakana",0x30FB},
+{"dotlessi",0x0131},
+{"dotlessj",0xF6BE},
+{"dotlessjstrokehook",0x0284},
+{"dotmath",0x22C5},
+{"dottedcircle",0x25CC},
+{"doubleyodpatah",0xFB1F},
+{"doubleyodpatahhebrew",0xFB1F},
+{"downtackbelowcmb",0x031E},
+{"downtackmod",0x02D5},
+{"dparen",0x249F},
+{"dsuperior",0xF6EB},
+{"dtail",0x0256},
+{"dtopbar",0x018C},
+{"duhiragana",0x3065},
+{"dukatakana",0x30C5},
+{"dz",0x01F3},
+{"dzaltone",0x02A3},
+{"dzcaron",0x01C6},
+{"dzcurl",0x02A5},
+{"dzeabkhasiancyrillic",0x04E1},
+{"dzecyrillic",0x0455},
+{"dzhecyrillic",0x045F},
+{"e",0x0065},
+{"eacute",0x00E9},
+{"earth",0x2641},
+{"ebengali",0x098F},
+{"ebopomofo",0x311C},
+{"ebreve",0x0115},
+{"ecandradeva",0x090D},
+{"ecandragujarati",0x0A8D},
+{"ecandravowelsigndeva",0x0945},
+{"ecandravowelsigngujarati",0x0AC5},
+{"ecaron",0x011B},
+{"ecedillabreve",0x1E1D},
+{"echarmenian",0x0565},
+{"echyiwnarmenian",0x0587},
+{"ecircle",0x24D4},
+{"ecircumflex",0x00EA},
+{"ecircumflexacute",0x1EBF},
+{"ecircumflexbelow",0x1E19},
+{"ecircumflexdotbelow",0x1EC7},
+{"ecircumflexgrave",0x1EC1},
+{"ecircumflexhookabove",0x1EC3},
+{"ecircumflextilde",0x1EC5},
+{"ecyrillic",0x0454},
+{"edblgrave",0x0205},
+{"edeva",0x090F},
+{"edieresis",0x00EB},
+{"edot",0x0117},
+{"edotaccent",0x0117},
+{"edotbelow",0x1EB9},
+{"eegurmukhi",0x0A0F},
+{"eematragurmukhi",0x0A47},
+{"efcyrillic",0x0444},
+{"egrave",0x00E8},
+{"egujarati",0x0A8F},
+{"eharmenian",0x0567},
+{"ehbopomofo",0x311D},
+{"ehiragana",0x3048},
+{"ehookabove",0x1EBB},
+{"eibopomofo",0x311F},
+{"eight",0x0038},
+{"eightarabic",0x0668},
+{"eightbengali",0x09EE},
+{"eightcircle",0x2467},
+{"eightcircleinversesansserif",0x2791},
+{"eightdeva",0x096E},
+{"eighteencircle",0x2471},
+{"eighteenparen",0x2485},
+{"eighteenperiod",0x2499},
+{"eightgujarati",0x0AEE},
+{"eightgurmukhi",0x0A6E},
+{"eighthackarabic",0x0668},
+{"eighthangzhou",0x3028},
+{"eighthnotebeamed",0x266B},
+{"eightideographicparen",0x3227},
+{"eightinferior",0x2088},
+{"eightmonospace",0xFF18},
+{"eightoldstyle",0xF738},
+{"eightparen",0x247B},
+{"eightperiod",0x248F},
+{"eightpersian",0x06F8},
+{"eightroman",0x2177},
+{"eightsuperior",0x2078},
+{"eightthai",0x0E58},
+{"einvertedbreve",0x0207},
+{"eiotifiedcyrillic",0x0465},
+{"ekatakana",0x30A8},
+{"ekatakanahalfwidth",0xFF74},
+{"ekonkargurmukhi",0x0A74},
+{"ekorean",0x3154},
+{"elcyrillic",0x043B},
+{"element",0x2208},
+{"elevencircle",0x246A},
+{"elevenparen",0x247E},
+{"elevenperiod",0x2492},
+{"elevenroman",0x217A},
+{"ellipsis",0x2026},
+{"ellipsisvertical",0x22EE},
+{"emacron",0x0113},
+{"emacronacute",0x1E17},
+{"emacrongrave",0x1E15},
+{"emcyrillic",0x043C},
+{"emdash",0x2014},
+{"emdashvertical",0xFE31},
+{"emonospace",0xFF45},
+{"emphasismarkarmenian",0x055B},
+{"emptyset",0x2205},
+{"enbopomofo",0x3123},
+{"encyrillic",0x043D},
+{"endash",0x2013},
+{"endashvertical",0xFE32},
+{"endescendercyrillic",0x04A3},
+{"eng",0x014B},
+{"engbopomofo",0x3125},
+{"enghecyrillic",0x04A5},
+{"enhookcyrillic",0x04C8},
+{"enspace",0x2002},
+{"eogonek",0x0119},
+{"eokorean",0x3153},
+{"eopen",0x025B},
+{"eopenclosed",0x029A},
+{"eopenreversed",0x025C},
+{"eopenreversedclosed",0x025E},
+{"eopenreversedhook",0x025D},
+{"eparen",0x24A0},
+{"epsilon",0x03B5},
+{"epsilontonos",0x03AD},
+{"equal",0x003D},
+{"equalmonospace",0xFF1D},
+{"equalsmall",0xFE66},
+{"equalsuperior",0x207C},
+{"equivalence",0x2261},
+{"erbopomofo",0x3126},
+{"ercyrillic",0x0440},
+{"ereversed",0x0258},
+{"ereversedcyrillic",0x044D},
+{"escyrillic",0x0441},
+{"esdescendercyrillic",0x04AB},
+{"esh",0x0283},
+{"eshcurl",0x0286},
+{"eshortdeva",0x090E},
+{"eshortvowelsigndeva",0x0946},
+{"eshreversedloop",0x01AA},
+{"eshsquatreversed",0x0285},
+{"esmallhiragana",0x3047},
+{"esmallkatakana",0x30A7},
+{"esmallkatakanahalfwidth",0xFF6A},
+{"estimated",0x212E},
+{"esuperior",0xF6EC},
+{"eta",0x03B7},
+{"etarmenian",0x0568},
+{"etatonos",0x03AE},
+{"eth",0x00F0},
+{"etilde",0x1EBD},
+{"etildebelow",0x1E1B},
+{"etnahtafoukhhebrew",0x0591},
+{"etnahtafoukhlefthebrew",0x0591},
+{"etnahtahebrew",0x0591},
+{"etnahtalefthebrew",0x0591},
+{"eturned",0x01DD},
+{"eukorean",0x3161},
+{"euro",0x20AC},
+{"evowelsignbengali",0x09C7},
+{"evowelsigndeva",0x0947},
+{"evowelsigngujarati",0x0AC7},
+{"exclam",0x0021},
+{"exclamarmenian",0x055C},
+{"exclamdbl",0x203C},
+{"exclamdown",0x00A1},
+{"exclamdownsmall",0xF7A1},
+{"exclammonospace",0xFF01},
+{"exclamsmall",0xF721},
+{"existential",0x2203},
+{"ezh",0x0292},
+{"ezhcaron",0x01EF},
+{"ezhcurl",0x0293},
+{"ezhreversed",0x01B9},
+{"ezhtail",0x01BA},
+{"f",0x0066},
+{"fadeva",0x095E},
+{"fagurmukhi",0x0A5E},
+{"fahrenheit",0x2109},
+{"fathaarabic",0x064E},
+{"fathalowarabic",0x064E},
+{"fathatanarabic",0x064B},
+{"fbopomofo",0x3108},
+{"fcircle",0x24D5},
+{"fdotaccent",0x1E1F},
+{"feharabic",0x0641},
+{"feharmenian",0x0586},
+{"fehfinalarabic",0xFED2},
+{"fehinitialarabic",0xFED3},
+{"fehmedialarabic",0xFED4},
+{"feicoptic",0x03E5},
+{"female",0x2640},
+{"ff",0xFB00},
+{"ffi",0xFB03},
+{"ffl",0xFB04},
+{"fi",0xFB01},
+{"fifteencircle",0x246E},
+{"fifteenparen",0x2482},
+{"fifteenperiod",0x2496},
+{"figuredash",0x2012},
+{"filledbox",0x25A0},
+{"filledrect",0x25AC},
+{"finalkaf",0x05DA},
+{"finalkafdagesh",0xFB3A},
+{"finalkafdageshhebrew",0xFB3A},
+{"finalkafhebrew",0x05DA},
+{"finalmem",0x05DD},
+{"finalmemhebrew",0x05DD},
+{"finalnun",0x05DF},
+{"finalnunhebrew",0x05DF},
+{"finalpe",0x05E3},
+{"finalpehebrew",0x05E3},
+{"finaltsadi",0x05E5},
+{"finaltsadihebrew",0x05E5},
+{"firsttonechinese",0x02C9},
+{"fisheye",0x25C9},
+{"fitacyrillic",0x0473},
+{"five",0x0035},
+{"fivearabic",0x0665},
+{"fivebengali",0x09EB},
+{"fivecircle",0x2464},
+{"fivecircleinversesansserif",0x278E},
+{"fivedeva",0x096B},
+{"fiveeighths",0x215D},
+{"fivegujarati",0x0AEB},
+{"fivegurmukhi",0x0A6B},
+{"fivehackarabic",0x0665},
+{"fivehangzhou",0x3025},
+{"fiveideographicparen",0x3224},
+{"fiveinferior",0x2085},
+{"fivemonospace",0xFF15},
+{"fiveoldstyle",0xF735},
+{"fiveparen",0x2478},
+{"fiveperiod",0x248C},
+{"fivepersian",0x06F5},
+{"fiveroman",0x2174},
+{"fivesuperior",0x2075},
+{"fivethai",0x0E55},
+{"fl",0xFB02},
+{"florin",0x0192},
+{"fmonospace",0xFF46},
+{"fmsquare",0x3399},
+{"fofanthai",0x0E1F},
+{"fofathai",0x0E1D},
+{"fongmanthai",0x0E4F},
+{"forall",0x2200},
+{"four",0x0034},
+{"fourarabic",0x0664},
+{"fourbengali",0x09EA},
+{"fourcircle",0x2463},
+{"fourcircleinversesansserif",0x278D},
+{"fourdeva",0x096A},
+{"fourgujarati",0x0AEA},
+{"fourgurmukhi",0x0A6A},
+{"fourhackarabic",0x0664},
+{"fourhangzhou",0x3024},
+{"fourideographicparen",0x3223},
+{"fourinferior",0x2084},
+{"fourmonospace",0xFF14},
+{"fournumeratorbengali",0x09F7},
+{"fouroldstyle",0xF734},
+{"fourparen",0x2477},
+{"fourperiod",0x248B},
+{"fourpersian",0x06F4},
+{"fourroman",0x2173},
+{"foursuperior",0x2074},
+{"fourteencircle",0x246D},
+{"fourteenparen",0x2481},
+{"fourteenperiod",0x2495},
+{"fourthai",0x0E54},
+{"fourthtonechinese",0x02CB},
+{"fparen",0x24A1},
+{"fraction",0x2044},
+{"franc",0x20A3},
+{"g",0x0067},
+{"gabengali",0x0997},
+{"gacute",0x01F5},
+{"gadeva",0x0917},
+{"gafarabic",0x06AF},
+{"gaffinalarabic",0xFB93},
+{"gafinitialarabic",0xFB94},
+{"gafmedialarabic",0xFB95},
+{"gagujarati",0x0A97},
+{"gagurmukhi",0x0A17},
+{"gahiragana",0x304C},
+{"gakatakana",0x30AC},
+{"gamma",0x03B3},
+{"gammalatinsmall",0x0263},
+{"gammasuperior",0x02E0},
+{"gangiacoptic",0x03EB},
+{"gbopomofo",0x310D},
+{"gbreve",0x011F},
+{"gcaron",0x01E7},
+{"gcedilla",0x0123},
+{"gcircle",0x24D6},
+{"gcircumflex",0x011D},
+{"gcommaaccent",0x0123},
+{"gdot",0x0121},
+{"gdotaccent",0x0121},
+{"gecyrillic",0x0433},
+{"gehiragana",0x3052},
+{"gekatakana",0x30B2},
+{"geometricallyequal",0x2251},
+{"gereshaccenthebrew",0x059C},
+{"gereshhebrew",0x05F3},
+{"gereshmuqdamhebrew",0x059D},
+{"germandbls",0x00DF},
+{"gershayimaccenthebrew",0x059E},
+{"gershayimhebrew",0x05F4},
+{"getamark",0x3013},
+{"ghabengali",0x0998},
+{"ghadarmenian",0x0572},
+{"ghadeva",0x0918},
+{"ghagujarati",0x0A98},
+{"ghagurmukhi",0x0A18},
+{"ghainarabic",0x063A},
+{"ghainfinalarabic",0xFECE},
+{"ghaininitialarabic",0xFECF},
+{"ghainmedialarabic",0xFED0},
+{"ghemiddlehookcyrillic",0x0495},
+{"ghestrokecyrillic",0x0493},
+{"gheupturncyrillic",0x0491},
+{"ghhadeva",0x095A},
+{"ghhagurmukhi",0x0A5A},
+{"ghook",0x0260},
+{"ghzsquare",0x3393},
+{"gihiragana",0x304E},
+{"gikatakana",0x30AE},
+{"gimarmenian",0x0563},
+{"gimel",0x05D2},
+{"gimeldagesh",0xFB32},
+{"gimeldageshhebrew",0xFB32},
+{"gimelhebrew",0x05D2},
+{"gjecyrillic",0x0453},
+{"glottalinvertedstroke",0x01BE},
+{"glottalstop",0x0294},
+{"glottalstopinverted",0x0296},
+{"glottalstopmod",0x02C0},
+{"glottalstopreversed",0x0295},
+{"glottalstopreversedmod",0x02C1},
+{"glottalstopreversedsuperior",0x02E4},
+{"glottalstopstroke",0x02A1},
+{"glottalstopstrokereversed",0x02A2},
+{"gmacron",0x1E21},
+{"gmonospace",0xFF47},
+{"gohiragana",0x3054},
+{"gokatakana",0x30B4},
+{"gparen",0x24A2},
+{"gpasquare",0x33AC},
+{"gradient",0x2207},
+{"grave",0x0060},
+{"gravebelowcmb",0x0316},
+{"gravecmb",0x0300},
+{"gravecomb",0x0300},
+{"gravedeva",0x0953},
+{"gravelowmod",0x02CE},
+{"gravemonospace",0xFF40},
+{"gravetonecmb",0x0340},
+{"greater",0x003E},
+{"greaterequal",0x2265},
+{"greaterequalorless",0x22DB},
+{"greatermonospace",0xFF1E},
+{"greaterorequivalent",0x2273},
+{"greaterorless",0x2277},
+{"greateroverequal",0x2267},
+{"greatersmall",0xFE65},
+{"gscript",0x0261},
+{"gstroke",0x01E5},
+{"guhiragana",0x3050},
+{"guillemotleft",0x00AB},
+{"guillemotright",0x00BB},
+{"guilsinglleft",0x2039},
+{"guilsinglright",0x203A},
+{"gukatakana",0x30B0},
+{"guramusquare",0x3318},
+{"gysquare",0x33C9},
+{"h",0x0068},
+{"haabkhasiancyrillic",0x04A9},
+{"haaltonearabic",0x06C1},
+{"habengali",0x09B9},
+{"hadescendercyrillic",0x04B3},
+{"hadeva",0x0939},
+{"hagujarati",0x0AB9},
+{"hagurmukhi",0x0A39},
+{"haharabic",0x062D},
+{"hahfinalarabic",0xFEA2},
+{"hahinitialarabic",0xFEA3},
+{"hahiragana",0x306F},
+{"hahmedialarabic",0xFEA4},
+{"haitusquare",0x332A},
+{"hakatakana",0x30CF},
+{"hakatakanahalfwidth",0xFF8A},
+{"halantgurmukhi",0x0A4D},
+{"hamzaarabic",0x0621},
+{"hamzalowarabic",0x0621},
+{"hangulfiller",0x3164},
+{"hardsigncyrillic",0x044A},
+{"harpoonleftbarbup",0x21BC},
+{"harpoonrightbarbup",0x21C0},
+{"hasquare",0x33CA},
+{"hatafpatah",0x05B2},
+{"hatafpatah16",0x05B2},
+{"hatafpatah23",0x05B2},
+{"hatafpatah2f",0x05B2},
+{"hatafpatahhebrew",0x05B2},
+{"hatafpatahnarrowhebrew",0x05B2},
+{"hatafpatahquarterhebrew",0x05B2},
+{"hatafpatahwidehebrew",0x05B2},
+{"hatafqamats",0x05B3},
+{"hatafqamats1b",0x05B3},
+{"hatafqamats28",0x05B3},
+{"hatafqamats34",0x05B3},
+{"hatafqamatshebrew",0x05B3},
+{"hatafqamatsnarrowhebrew",0x05B3},
+{"hatafqamatsquarterhebrew",0x05B3},
+{"hatafqamatswidehebrew",0x05B3},
+{"hatafsegol",0x05B1},
+{"hatafsegol17",0x05B1},
+{"hatafsegol24",0x05B1},
+{"hatafsegol30",0x05B1},
+{"hatafsegolhebrew",0x05B1},
+{"hatafsegolnarrowhebrew",0x05B1},
+{"hatafsegolquarterhebrew",0x05B1},
+{"hatafsegolwidehebrew",0x05B1},
+{"hbar",0x0127},
+{"hbopomofo",0x310F},
+{"hbrevebelow",0x1E2B},
+{"hcedilla",0x1E29},
+{"hcircle",0x24D7},
+{"hcircumflex",0x0125},
+{"hdieresis",0x1E27},
+{"hdotaccent",0x1E23},
+{"hdotbelow",0x1E25},
+{"he",0x05D4},
+{"heart",0x2665},
+{"heartsuitblack",0x2665},
+{"heartsuitwhite",0x2661},
+{"hedagesh",0xFB34},
+{"hedageshhebrew",0xFB34},
+{"hehaltonearabic",0x06C1},
+{"heharabic",0x0647},
+{"hehebrew",0x05D4},
+{"hehfinalaltonearabic",0xFBA7},
+{"hehfinalalttwoarabic",0xFEEA},
+{"hehfinalarabic",0xFEEA},
+{"hehhamzaabovefinalarabic",0xFBA5},
+{"hehhamzaaboveisolatedarabic",0xFBA4},
+{"hehinitialaltonearabic",0xFBA8},
+{"hehinitialarabic",0xFEEB},
+{"hehiragana",0x3078},
+{"hehmedialaltonearabic",0xFBA9},
+{"hehmedialarabic",0xFEEC},
+{"heiseierasquare",0x337B},
+{"hekatakana",0x30D8},
+{"hekatakanahalfwidth",0xFF8D},
+{"hekutaarusquare",0x3336},
+{"henghook",0x0267},
+{"herutusquare",0x3339},
+{"het",0x05D7},
+{"hethebrew",0x05D7},
+{"hhook",0x0266},
+{"hhooksuperior",0x02B1},
+{"hieuhacirclekorean",0x327B},
+{"hieuhaparenkorean",0x321B},
+{"hieuhcirclekorean",0x326D},
+{"hieuhkorean",0x314E},
+{"hieuhparenkorean",0x320D},
+{"hihiragana",0x3072},
+{"hikatakana",0x30D2},
+{"hikatakanahalfwidth",0xFF8B},
+{"hiriq",0x05B4},
+{"hiriq14",0x05B4},
+{"hiriq21",0x05B4},
+{"hiriq2d",0x05B4},
+{"hiriqhebrew",0x05B4},
+{"hiriqnarrowhebrew",0x05B4},
+{"hiriqquarterhebrew",0x05B4},
+{"hiriqwidehebrew",0x05B4},
+{"hlinebelow",0x1E96},
+{"hmonospace",0xFF48},
+{"hoarmenian",0x0570},
+{"hohipthai",0x0E2B},
+{"hohiragana",0x307B},
+{"hokatakana",0x30DB},
+{"hokatakanahalfwidth",0xFF8E},
+{"holam",0x05B9},
+{"holam19",0x05B9},
+{"holam26",0x05B9},
+{"holam32",0x05B9},
+{"holamhebrew",0x05B9},
+{"holamnarrowhebrew",0x05B9},
+{"holamquarterhebrew",0x05B9},
+{"holamwidehebrew",0x05B9},
+{"honokhukthai",0x0E2E},
+{"hookabovecomb",0x0309},
+{"hookcmb",0x0309},
+{"hookpalatalizedbelowcmb",0x0321},
+{"hookretroflexbelowcmb",0x0322},
+{"hoonsquare",0x3342},
+{"horicoptic",0x03E9},
+{"horizontalbar",0x2015},
+{"horncmb",0x031B},
+{"hotsprings",0x2668},
+{"house",0x2302},
+{"hparen",0x24A3},
+{"hsuperior",0x02B0},
+{"hturned",0x0265},
+{"huhiragana",0x3075},
+{"huiitosquare",0x3333},
+{"hukatakana",0x30D5},
+{"hukatakanahalfwidth",0xFF8C},
+{"hungarumlaut",0x02DD},
+{"hungarumlautcmb",0x030B},
+{"hv",0x0195},
+{"hyphen",0x002D},
+{"hypheninferior",0xF6E5},
+{"hyphenmonospace",0xFF0D},
+{"hyphensmall",0xFE63},
+{"hyphensuperior",0xF6E6},
+{"hyphentwo",0x2010},
+{"i",0x0069},
+{"iacute",0x00ED},
+{"iacyrillic",0x044F},
+{"ibengali",0x0987},
+{"ibopomofo",0x3127},
+{"ibreve",0x012D},
+{"icaron",0x01D0},
+{"icircle",0x24D8},
+{"icircumflex",0x00EE},
+{"icyrillic",0x0456},
+{"idblgrave",0x0209},
+{"ideographearthcircle",0x328F},
+{"ideographfirecircle",0x328B},
+{"ideographicallianceparen",0x323F},
+{"ideographiccallparen",0x323A},
+{"ideographiccentrecircle",0x32A5},
+{"ideographicclose",0x3006},
+{"ideographiccomma",0x3001},
+{"ideographiccommaleft",0xFF64},
+{"ideographiccongratulationparen",0x3237},
+{"ideographiccorrectcircle",0x32A3},
+{"ideographicearthparen",0x322F},
+{"ideographicenterpriseparen",0x323D},
+{"ideographicexcellentcircle",0x329D},
+{"ideographicfestivalparen",0x3240},
+{"ideographicfinancialcircle",0x3296},
+{"ideographicfinancialparen",0x3236},
+{"ideographicfireparen",0x322B},
+{"ideographichaveparen",0x3232},
+{"ideographichighcircle",0x32A4},
+{"ideographiciterationmark",0x3005},
+{"ideographiclaborcircle",0x3298},
+{"ideographiclaborparen",0x3238},
+{"ideographicleftcircle",0x32A7},
+{"ideographiclowcircle",0x32A6},
+{"ideographicmedicinecircle",0x32A9},
+{"ideographicmetalparen",0x322E},
+{"ideographicmoonparen",0x322A},
+{"ideographicnameparen",0x3234},
+{"ideographicperiod",0x3002},
+{"ideographicprintcircle",0x329E},
+{"ideographicreachparen",0x3243},
+{"ideographicrepresentparen",0x3239},
+{"ideographicresourceparen",0x323E},
+{"ideographicrightcircle",0x32A8},
+{"ideographicsecretcircle",0x3299},
+{"ideographicselfparen",0x3242},
+{"ideographicsocietyparen",0x3233},
+{"ideographicspace",0x3000},
+{"ideographicspecialparen",0x3235},
+{"ideographicstockparen",0x3231},
+{"ideographicstudyparen",0x323B},
+{"ideographicsunparen",0x3230},
+{"ideographicsuperviseparen",0x323C},
+{"ideographicwaterparen",0x322C},
+{"ideographicwoodparen",0x322D},
+{"ideographiczero",0x3007},
+{"ideographmetalcircle",0x328E},
+{"ideographmooncircle",0x328A},
+{"ideographnamecircle",0x3294},
+{"ideographsuncircle",0x3290},
+{"ideographwatercircle",0x328C},
+{"ideographwoodcircle",0x328D},
+{"ideva",0x0907},
+{"idieresis",0x00EF},
+{"idieresisacute",0x1E2F},
+{"idieresiscyrillic",0x04E5},
+{"idotbelow",0x1ECB},
+{"iebrevecyrillic",0x04D7},
+{"iecyrillic",0x0435},
+{"ieungacirclekorean",0x3275},
+{"ieungaparenkorean",0x3215},
+{"ieungcirclekorean",0x3267},
+{"ieungkorean",0x3147},
+{"ieungparenkorean",0x3207},
+{"igrave",0x00EC},
+{"igujarati",0x0A87},
+{"igurmukhi",0x0A07},
+{"ihiragana",0x3044},
+{"ihookabove",0x1EC9},
+{"iibengali",0x0988},
+{"iicyrillic",0x0438},
+{"iideva",0x0908},
+{"iigujarati",0x0A88},
+{"iigurmukhi",0x0A08},
+{"iimatragurmukhi",0x0A40},
+{"iinvertedbreve",0x020B},
+{"iishortcyrillic",0x0439},
+{"iivowelsignbengali",0x09C0},
+{"iivowelsigndeva",0x0940},
+{"iivowelsigngujarati",0x0AC0},
+{"ij",0x0133},
+{"ikatakana",0x30A4},
+{"ikatakanahalfwidth",0xFF72},
+{"ikorean",0x3163},
+{"ilde",0x02DC},
+{"iluyhebrew",0x05AC},
+{"imacron",0x012B},
+{"imacroncyrillic",0x04E3},
+{"imageorapproximatelyequal",0x2253},
+{"imatragurmukhi",0x0A3F},
+{"imonospace",0xFF49},
+{"increment",0x2206},
+{"infinity",0x221E},
+{"iniarmenian",0x056B},
+{"integral",0x222B},
+{"integralbottom",0x2321},
+{"integralbt",0x2321},
+{"integralex",0xF8F5},
+{"integraltop",0x2320},
+{"integraltp",0x2320},
+{"intersection",0x2229},
+{"intisquare",0x3305},
+{"invbullet",0x25D8},
+{"invcircle",0x25D9},
+{"invsmileface",0x263B},
+{"iocyrillic",0x0451},
+{"iogonek",0x012F},
+{"iota",0x03B9},
+{"iotadieresis",0x03CA},
+{"iotadieresistonos",0x0390},
+{"iotalatin",0x0269},
+{"iotatonos",0x03AF},
+{"iparen",0x24A4},
+{"irigurmukhi",0x0A72},
+{"ismallhiragana",0x3043},
+{"ismallkatakana",0x30A3},
+{"ismallkatakanahalfwidth",0xFF68},
+{"issharbengali",0x09FA},
+{"istroke",0x0268},
+{"isuperior",0xF6ED},
+{"iterationhiragana",0x309D},
+{"iterationkatakana",0x30FD},
+{"itilde",0x0129},
+{"itildebelow",0x1E2D},
+{"iubopomofo",0x3129},
+{"iucyrillic",0x044E},
+{"ivowelsignbengali",0x09BF},
+{"ivowelsigndeva",0x093F},
+{"ivowelsigngujarati",0x0ABF},
+{"izhitsacyrillic",0x0475},
+{"izhitsadblgravecyrillic",0x0477},
+{"j",0x006A},
+{"jaarmenian",0x0571},
+{"jabengali",0x099C},
+{"jadeva",0x091C},
+{"jagujarati",0x0A9C},
+{"jagurmukhi",0x0A1C},
+{"jbopomofo",0x3110},
+{"jcaron",0x01F0},
+{"jcircle",0x24D9},
+{"jcircumflex",0x0135},
+{"jcrossedtail",0x029D},
+{"jdotlessstroke",0x025F},
+{"jecyrillic",0x0458},
+{"jeemarabic",0x062C},
+{"jeemfinalarabic",0xFE9E},
+{"jeeminitialarabic",0xFE9F},
+{"jeemmedialarabic",0xFEA0},
+{"jeharabic",0x0698},
+{"jehfinalarabic",0xFB8B},
+{"jhabengali",0x099D},
+{"jhadeva",0x091D},
+{"jhagujarati",0x0A9D},
+{"jhagurmukhi",0x0A1D},
+{"jheharmenian",0x057B},
+{"jis",0x3004},
+{"jmonospace",0xFF4A},
+{"jparen",0x24A5},
+{"jsuperior",0x02B2},
+{"k",0x006B},
+{"kabashkircyrillic",0x04A1},
+{"kabengali",0x0995},
+{"kacute",0x1E31},
+{"kacyrillic",0x043A},
+{"kadescendercyrillic",0x049B},
+{"kadeva",0x0915},
+{"kaf",0x05DB},
+{"kafarabic",0x0643},
+{"kafdagesh",0xFB3B},
+{"kafdageshhebrew",0xFB3B},
+{"kaffinalarabic",0xFEDA},
+{"kafhebrew",0x05DB},
+{"kafinitialarabic",0xFEDB},
+{"kafmedialarabic",0xFEDC},
+{"kafrafehebrew",0xFB4D},
+{"kagujarati",0x0A95},
+{"kagurmukhi",0x0A15},
+{"kahiragana",0x304B},
+{"kahookcyrillic",0x04C4},
+{"kakatakana",0x30AB},
+{"kakatakanahalfwidth",0xFF76},
+{"kappa",0x03BA},
+{"kappasymbolgreek",0x03F0},
+{"kapyeounmieumkorean",0x3171},
+{"kapyeounphieuphkorean",0x3184},
+{"kapyeounpieupkorean",0x3178},
+{"kapyeounssangpieupkorean",0x3179},
+{"karoriisquare",0x330D},
+{"kashidaautoarabic",0x0640},
+{"kashidaautonosidebearingarabic",0x0640},
+{"kasmallkatakana",0x30F5},
+{"kasquare",0x3384},
+{"kasraarabic",0x0650},
+{"kasratanarabic",0x064D},
+{"kastrokecyrillic",0x049F},
+{"katahiraprolongmarkhalfwidth",0xFF70},
+{"kaverticalstrokecyrillic",0x049D},
+{"kbopomofo",0x310E},
+{"kcalsquare",0x3389},
+{"kcaron",0x01E9},
+{"kcedilla",0x0137},
+{"kcircle",0x24DA},
+{"kcommaaccent",0x0137},
+{"kdotbelow",0x1E33},
+{"keharmenian",0x0584},
+{"kehiragana",0x3051},
+{"kekatakana",0x30B1},
+{"kekatakanahalfwidth",0xFF79},
+{"kenarmenian",0x056F},
+{"kesmallkatakana",0x30F6},
+{"kgreenlandic",0x0138},
+{"khabengali",0x0996},
+{"khacyrillic",0x0445},
+{"khadeva",0x0916},
+{"khagujarati",0x0A96},
+{"khagurmukhi",0x0A16},
+{"khaharabic",0x062E},
+{"khahfinalarabic",0xFEA6},
+{"khahinitialarabic",0xFEA7},
+{"khahmedialarabic",0xFEA8},
+{"kheicoptic",0x03E7},
+{"khhadeva",0x0959},
+{"khhagurmukhi",0x0A59},
+{"khieukhacirclekorean",0x3278},
+{"khieukhaparenkorean",0x3218},
+{"khieukhcirclekorean",0x326A},
+{"khieukhkorean",0x314B},
+{"khieukhparenkorean",0x320A},
+{"khokhaithai",0x0E02},
+{"khokhonthai",0x0E05},
+{"khokhuatthai",0x0E03},
+{"khokhwaithai",0x0E04},
+{"khomutthai",0x0E5B},
+{"khook",0x0199},
+{"khorakhangthai",0x0E06},
+{"khzsquare",0x3391},
+{"kihiragana",0x304D},
+{"kikatakana",0x30AD},
+{"kikatakanahalfwidth",0xFF77},
+{"kiroguramusquare",0x3315},
+{"kiromeetorusquare",0x3316},
+{"kirosquare",0x3314},
+{"kiyeokacirclekorean",0x326E},
+{"kiyeokaparenkorean",0x320E},
+{"kiyeokcirclekorean",0x3260},
+{"kiyeokkorean",0x3131},
+{"kiyeokparenkorean",0x3200},
+{"kiyeoksioskorean",0x3133},
+{"kjecyrillic",0x045C},
+{"klinebelow",0x1E35},
+{"klsquare",0x3398},
+{"kmcubedsquare",0x33A6},
+{"kmonospace",0xFF4B},
+{"kmsquaredsquare",0x33A2},
+{"kohiragana",0x3053},
+{"kohmsquare",0x33C0},
+{"kokaithai",0x0E01},
+{"kokatakana",0x30B3},
+{"kokatakanahalfwidth",0xFF7A},
+{"kooposquare",0x331E},
+{"koppacyrillic",0x0481},
+{"koreanstandardsymbol",0x327F},
+{"koroniscmb",0x0343},
+{"kparen",0x24A6},
+{"kpasquare",0x33AA},
+{"ksicyrillic",0x046F},
+{"ktsquare",0x33CF},
+{"kturned",0x029E},
+{"kuhiragana",0x304F},
+{"kukatakana",0x30AF},
+{"kukatakanahalfwidth",0xFF78},
+{"kvsquare",0x33B8},
+{"kwsquare",0x33BE},
+{"l",0x006C},
+{"labengali",0x09B2},
+{"lacute",0x013A},
+{"ladeva",0x0932},
+{"lagujarati",0x0AB2},
+{"lagurmukhi",0x0A32},
+{"lakkhangyaothai",0x0E45},
+{"lamaleffinalarabic",0xFEFC},
+{"lamalefhamzaabovefinalarabic",0xFEF8},
+{"lamalefhamzaaboveisolatedarabic",0xFEF7},
+{"lamalefhamzabelowfinalarabic",0xFEFA},
+{"lamalefhamzabelowisolatedarabic",0xFEF9},
+{"lamalefisolatedarabic",0xFEFB},
+{"lamalefmaddaabovefinalarabic",0xFEF6},
+{"lamalefmaddaaboveisolatedarabic",0xFEF5},
+{"lamarabic",0x0644},
+{"lambda",0x03BB},
+{"lambdastroke",0x019B},
+{"lamed",0x05DC},
+{"lameddagesh",0xFB3C},
+{"lameddageshhebrew",0xFB3C},
+{"lamedhebrew",0x05DC},
+{"lamfinalarabic",0xFEDE},
+{"lamhahinitialarabic",0xFCCA},
+{"laminitialarabic",0xFEDF},
+{"lamjeeminitialarabic",0xFCC9},
+{"lamkhahinitialarabic",0xFCCB},
+{"lamlamhehisolatedarabic",0xFDF2},
+{"lammedialarabic",0xFEE0},
+{"lammeemhahinitialarabic",0xFD88},
+{"lammeeminitialarabic",0xFCCC},
+{"largecircle",0x25EF},
+{"lbar",0x019A},
+{"lbelt",0x026C},
+{"lbopomofo",0x310C},
+{"lcaron",0x013E},
+{"lcedilla",0x013C},
+{"lcircle",0x24DB},
+{"lcircumflexbelow",0x1E3D},
+{"lcommaaccent",0x013C},
+{"ldot",0x0140},
+{"ldotaccent",0x0140},
+{"ldotbelow",0x1E37},
+{"ldotbelowmacron",0x1E39},
+{"leftangleabovecmb",0x031A},
+{"lefttackbelowcmb",0x0318},
+{"less",0x003C},
+{"lessequal",0x2264},
+{"lessequalorgreater",0x22DA},
+{"lessmonospace",0xFF1C},
+{"lessorequivalent",0x2272},
+{"lessorgreater",0x2276},
+{"lessoverequal",0x2266},
+{"lesssmall",0xFE64},
+{"lezh",0x026E},
+{"lfblock",0x258C},
+{"lhookretroflex",0x026D},
+{"lira",0x20A4},
+{"liwnarmenian",0x056C},
+{"lj",0x01C9},
+{"ljecyrillic",0x0459},
+{"ll",0xF6C0},
+{"lladeva",0x0933},
+{"llagujarati",0x0AB3},
+{"llinebelow",0x1E3B},
+{"llladeva",0x0934},
+{"llvocalicbengali",0x09E1},
+{"llvocalicdeva",0x0961},
+{"llvocalicvowelsignbengali",0x09E3},
+{"llvocalicvowelsigndeva",0x0963},
+{"lmiddletilde",0x026B},
+{"lmonospace",0xFF4C},
+{"lmsquare",0x33D0},
+{"lochulathai",0x0E2C},
+{"logicaland",0x2227},
+{"logicalnot",0x00AC},
+{"logicalnotreversed",0x2310},
+{"logicalor",0x2228},
+{"lolingthai",0x0E25},
+{"longs",0x017F},
+{"lowlinecenterline",0xFE4E},
+{"lowlinecmb",0x0332},
+{"lowlinedashed",0xFE4D},
+{"lozenge",0x25CA},
+{"lparen",0x24A7},
+{"lslash",0x0142},
+{"lsquare",0x2113},
+{"lsuperior",0xF6EE},
+{"ltshade",0x2591},
+{"luthai",0x0E26},
+{"lvocalicbengali",0x098C},
+{"lvocalicdeva",0x090C},
+{"lvocalicvowelsignbengali",0x09E2},
+{"lvocalicvowelsigndeva",0x0962},
+{"lxsquare",0x33D3},
+{"m",0x006D},
+{"mabengali",0x09AE},
+{"macron",0x00AF},
+{"macronbelowcmb",0x0331},
+{"macroncmb",0x0304},
+{"macronlowmod",0x02CD},
+{"macronmonospace",0xFFE3},
+{"macute",0x1E3F},
+{"madeva",0x092E},
+{"magujarati",0x0AAE},
+{"magurmukhi",0x0A2E},
+{"mahapakhhebrew",0x05A4},
+{"mahapakhlefthebrew",0x05A4},
+{"mahiragana",0x307E},
+{"maichattawalowleftthai",0xF895},
+{"maichattawalowrightthai",0xF894},
+{"maichattawathai",0x0E4B},
+{"maichattawaupperleftthai",0xF893},
+{"maieklowleftthai",0xF88C},
+{"maieklowrightthai",0xF88B},
+{"maiekthai",0x0E48},
+{"maiekupperleftthai",0xF88A},
+{"maihanakatleftthai",0xF884},
+{"maihanakatthai",0x0E31},
+{"maitaikhuleftthai",0xF889},
+{"maitaikhuthai",0x0E47},
+{"maitholowleftthai",0xF88F},
+{"maitholowrightthai",0xF88E},
+{"maithothai",0x0E49},
+{"maithoupperleftthai",0xF88D},
+{"maitrilowleftthai",0xF892},
+{"maitrilowrightthai",0xF891},
+{"maitrithai",0x0E4A},
+{"maitriupperleftthai",0xF890},
+{"maiyamokthai",0x0E46},
+{"makatakana",0x30DE},
+{"makatakanahalfwidth",0xFF8F},
+{"male",0x2642},
+{"mansyonsquare",0x3347},
+{"maqafhebrew",0x05BE},
+{"mars",0x2642},
+{"masoracirclehebrew",0x05AF},
+{"masquare",0x3383},
+{"mbopomofo",0x3107},
+{"mbsquare",0x33D4},
+{"mcircle",0x24DC},
+{"mcubedsquare",0x33A5},
+{"mdotaccent",0x1E41},
+{"mdotbelow",0x1E43},
+{"meemarabic",0x0645},
+{"meemfinalarabic",0xFEE2},
+{"meeminitialarabic",0xFEE3},
+{"meemmedialarabic",0xFEE4},
+{"meemmeeminitialarabic",0xFCD1},
+{"meemmeemisolatedarabic",0xFC48},
+{"meetorusquare",0x334D},
+{"mehiragana",0x3081},
+{"meizierasquare",0x337E},
+{"mekatakana",0x30E1},
+{"mekatakanahalfwidth",0xFF92},
+{"mem",0x05DE},
+{"memdagesh",0xFB3E},
+{"memdageshhebrew",0xFB3E},
+{"memhebrew",0x05DE},
+{"menarmenian",0x0574},
+{"merkhahebrew",0x05A5},
+{"merkhakefulahebrew",0x05A6},
+{"merkhakefulalefthebrew",0x05A6},
+{"merkhalefthebrew",0x05A5},
+{"mhook",0x0271},
+{"mhzsquare",0x3392},
+{"middledotkatakanahalfwidth",0xFF65},
+{"middot",0x00B7},
+{"mieumacirclekorean",0x3272},
+{"mieumaparenkorean",0x3212},
+{"mieumcirclekorean",0x3264},
+{"mieumkorean",0x3141},
+{"mieumpansioskorean",0x3170},
+{"mieumparenkorean",0x3204},
+{"mieumpieupkorean",0x316E},
+{"mieumsioskorean",0x316F},
+{"mihiragana",0x307F},
+{"mikatakana",0x30DF},
+{"mikatakanahalfwidth",0xFF90},
+{"minus",0x2212},
+{"minusbelowcmb",0x0320},
+{"minuscircle",0x2296},
+{"minusmod",0x02D7},
+{"minusplus",0x2213},
+{"minute",0x2032},
+{"miribaarusquare",0x334A},
+{"mirisquare",0x3349},
+{"mlonglegturned",0x0270},
+{"mlsquare",0x3396},
+{"mmcubedsquare",0x33A3},
+{"mmonospace",0xFF4D},
+{"mmsquaredsquare",0x339F},
+{"mohiragana",0x3082},
+{"mohmsquare",0x33C1},
+{"mokatakana",0x30E2},
+{"mokatakanahalfwidth",0xFF93},
+{"molsquare",0x33D6},
+{"momathai",0x0E21},
+{"moverssquare",0x33A7},
+{"moverssquaredsquare",0x33A8},
+{"mparen",0x24A8},
+{"mpasquare",0x33AB},
+{"mssquare",0x33B3},
+{"msuperior",0xF6EF},
+{"mturned",0x026F},
+{"mu",0x00B5},
+{"mu1",0x00B5},
+{"muasquare",0x3382},
+{"muchgreater",0x226B},
+{"muchless",0x226A},
+{"mufsquare",0x338C},
+{"mugreek",0x03BC},
+{"mugsquare",0x338D},
+{"muhiragana",0x3080},
+{"mukatakana",0x30E0},
+{"mukatakanahalfwidth",0xFF91},
+{"mulsquare",0x3395},
+{"multiply",0x00D7},
+{"mumsquare",0x339B},
+{"munahhebrew",0x05A3},
+{"munahlefthebrew",0x05A3},
+{"musicalnote",0x266A},
+{"musicalnotedbl",0x266B},
+{"musicflatsign",0x266D},
+{"musicsharpsign",0x266F},
+{"mussquare",0x33B2},
+{"muvsquare",0x33B6},
+{"muwsquare",0x33BC},
+{"mvmegasquare",0x33B9},
+{"mvsquare",0x33B7},
+{"mwmegasquare",0x33BF},
+{"mwsquare",0x33BD},
+{"n",0x006E},
+{"nabengali",0x09A8},
+{"nabla",0x2207},
+{"nacute",0x0144},
+{"nadeva",0x0928},
+{"nagujarati",0x0AA8},
+{"nagurmukhi",0x0A28},
+{"nahiragana",0x306A},
+{"nakatakana",0x30CA},
+{"nakatakanahalfwidth",0xFF85},
+{"napostrophe",0x0149},
+{"nasquare",0x3381},
+{"nbopomofo",0x310B},
+{"nbspace",0x00A0},
+{"ncaron",0x0148},
+{"ncedilla",0x0146},
+{"ncircle",0x24DD},
+{"ncircumflexbelow",0x1E4B},
+{"ncommaaccent",0x0146},
+{"ndotaccent",0x1E45},
+{"ndotbelow",0x1E47},
+{"nehiragana",0x306D},
+{"nekatakana",0x30CD},
+{"nekatakanahalfwidth",0xFF88},
+{"newsheqelsign",0x20AA},
+{"nfsquare",0x338B},
+{"ngabengali",0x0999},
+{"ngadeva",0x0919},
+{"ngagujarati",0x0A99},
+{"ngagurmukhi",0x0A19},
+{"ngonguthai",0x0E07},
+{"nhiragana",0x3093},
+{"nhookleft",0x0272},
+{"nhookretroflex",0x0273},
+{"nieunacirclekorean",0x326F},
+{"nieunaparenkorean",0x320F},
+{"nieuncieuckorean",0x3135},
+{"nieuncirclekorean",0x3261},
+{"nieunhieuhkorean",0x3136},
+{"nieunkorean",0x3134},
+{"nieunpansioskorean",0x3168},
+{"nieunparenkorean",0x3201},
+{"nieunsioskorean",0x3167},
+{"nieuntikeutkorean",0x3166},
+{"nihiragana",0x306B},
+{"nikatakana",0x30CB},
+{"nikatakanahalfwidth",0xFF86},
+{"nikhahitleftthai",0xF899},
+{"nikhahitthai",0x0E4D},
+{"nine",0x0039},
+{"ninearabic",0x0669},
+{"ninebengali",0x09EF},
+{"ninecircle",0x2468},
+{"ninecircleinversesansserif",0x2792},
+{"ninedeva",0x096F},
+{"ninegujarati",0x0AEF},
+{"ninegurmukhi",0x0A6F},
+{"ninehackarabic",0x0669},
+{"ninehangzhou",0x3029},
+{"nineideographicparen",0x3228},
+{"nineinferior",0x2089},
+{"ninemonospace",0xFF19},
+{"nineoldstyle",0xF739},
+{"nineparen",0x247C},
+{"nineperiod",0x2490},
+{"ninepersian",0x06F9},
+{"nineroman",0x2178},
+{"ninesuperior",0x2079},
+{"nineteencircle",0x2472},
+{"nineteenparen",0x2486},
+{"nineteenperiod",0x249A},
+{"ninethai",0x0E59},
+{"nj",0x01CC},
+{"njecyrillic",0x045A},
+{"nkatakana",0x30F3},
+{"nkatakanahalfwidth",0xFF9D},
+{"nlegrightlong",0x019E},
+{"nlinebelow",0x1E49},
+{"nmonospace",0xFF4E},
+{"nmsquare",0x339A},
+{"nnabengali",0x09A3},
+{"nnadeva",0x0923},
+{"nnagujarati",0x0AA3},
+{"nnagurmukhi",0x0A23},
+{"nnnadeva",0x0929},
+{"nohiragana",0x306E},
+{"nokatakana",0x30CE},
+{"nokatakanahalfwidth",0xFF89},
+{"nonbreakingspace",0x00A0},
+{"nonenthai",0x0E13},
+{"nonuthai",0x0E19},
+{"noonarabic",0x0646},
+{"noonfinalarabic",0xFEE6},
+{"noonghunnaarabic",0x06BA},
+{"noonghunnafinalarabic",0xFB9F},
+{"nooninitialarabic",0xFEE7},
+{"noonjeeminitialarabic",0xFCD2},
+{"noonjeemisolatedarabic",0xFC4B},
+{"noonmedialarabic",0xFEE8},
+{"noonmeeminitialarabic",0xFCD5},
+{"noonmeemisolatedarabic",0xFC4E},
+{"noonnoonfinalarabic",0xFC8D},
+{"notcontains",0x220C},
+{"notelement",0x2209},
+{"notelementof",0x2209},
+{"notequal",0x2260},
+{"notgreater",0x226F},
+{"notgreaternorequal",0x2271},
+{"notgreaternorless",0x2279},
+{"notidentical",0x2262},
+{"notless",0x226E},
+{"notlessnorequal",0x2270},
+{"notparallel",0x2226},
+{"notprecedes",0x2280},
+{"notsubset",0x2284},
+{"notsucceeds",0x2281},
+{"notsuperset",0x2285},
+{"nowarmenian",0x0576},
+{"nparen",0x24A9},
+{"nssquare",0x33B1},
+{"nsuperior",0x207F},
+{"ntilde",0x00F1},
+{"nu",0x03BD},
+{"nuhiragana",0x306C},
+{"nukatakana",0x30CC},
+{"nukatakanahalfwidth",0xFF87},
+{"nuktabengali",0x09BC},
+{"nuktadeva",0x093C},
+{"nuktagujarati",0x0ABC},
+{"nuktagurmukhi",0x0A3C},
+{"numbersign",0x0023},
+{"numbersignmonospace",0xFF03},
+{"numbersignsmall",0xFE5F},
+{"numeralsigngreek",0x0374},
+{"numeralsignlowergreek",0x0375},
+{"numero",0x2116},
+{"nun",0x05E0},
+{"nundagesh",0xFB40},
+{"nundageshhebrew",0xFB40},
+{"nunhebrew",0x05E0},
+{"nvsquare",0x33B5},
+{"nwsquare",0x33BB},
+{"nyabengali",0x099E},
+{"nyadeva",0x091E},
+{"nyagujarati",0x0A9E},
+{"nyagurmukhi",0x0A1E},
+{"o",0x006F},
+{"oacute",0x00F3},
+{"oangthai",0x0E2D},
+{"obarred",0x0275},
+{"obarredcyrillic",0x04E9},
+{"obarreddieresiscyrillic",0x04EB},
+{"obengali",0x0993},
+{"obopomofo",0x311B},
+{"obreve",0x014F},
+{"ocandradeva",0x0911},
+{"ocandragujarati",0x0A91},
+{"ocandravowelsigndeva",0x0949},
+{"ocandravowelsigngujarati",0x0AC9},
+{"ocaron",0x01D2},
+{"ocircle",0x24DE},
+{"ocircumflex",0x00F4},
+{"ocircumflexacute",0x1ED1},
+{"ocircumflexdotbelow",0x1ED9},
+{"ocircumflexgrave",0x1ED3},
+{"ocircumflexhookabove",0x1ED5},
+{"ocircumflextilde",0x1ED7},
+{"ocyrillic",0x043E},
+{"odblacute",0x0151},
+{"odblgrave",0x020D},
+{"odeva",0x0913},
+{"odieresis",0x00F6},
+{"odieresiscyrillic",0x04E7},
+{"odotbelow",0x1ECD},
+{"oe",0x0153},
+{"oekorean",0x315A},
+{"ogonek",0x02DB},
+{"ogonekcmb",0x0328},
+{"ograve",0x00F2},
+{"ogujarati",0x0A93},
+{"oharmenian",0x0585},
+{"ohiragana",0x304A},
+{"ohookabove",0x1ECF},
+{"ohorn",0x01A1},
+{"ohornacute",0x1EDB},
+{"ohorndotbelow",0x1EE3},
+{"ohorngrave",0x1EDD},
+{"ohornhookabove",0x1EDF},
+{"ohorntilde",0x1EE1},
+{"ohungarumlaut",0x0151},
+{"oi",0x01A3},
+{"oinvertedbreve",0x020F},
+{"okatakana",0x30AA},
+{"okatakanahalfwidth",0xFF75},
+{"okorean",0x3157},
+{"olehebrew",0x05AB},
+{"omacron",0x014D},
+{"omacronacute",0x1E53},
+{"omacrongrave",0x1E51},
+{"omdeva",0x0950},
+{"omega",0x03C9},
+{"omega1",0x03D6},
+{"omegacyrillic",0x0461},
+{"omegalatinclosed",0x0277},
+{"omegaroundcyrillic",0x047B},
+{"omegatitlocyrillic",0x047D},
+{"omegatonos",0x03CE},
+{"omgujarati",0x0AD0},
+{"omicron",0x03BF},
+{"omicrontonos",0x03CC},
+{"omonospace",0xFF4F},
+{"one",0x0031},
+{"onearabic",0x0661},
+{"onebengali",0x09E7},
+{"onecircle",0x2460},
+{"onecircleinversesansserif",0x278A},
+{"onedeva",0x0967},
+{"onedotenleader",0x2024},
+{"oneeighth",0x215B},
+{"onefitted",0xF6DC},
+{"onegujarati",0x0AE7},
+{"onegurmukhi",0x0A67},
+{"onehackarabic",0x0661},
+{"onehalf",0x00BD},
+{"onehangzhou",0x3021},
+{"oneideographicparen",0x3220},
+{"oneinferior",0x2081},
+{"onemonospace",0xFF11},
+{"onenumeratorbengali",0x09F4},
+{"oneoldstyle",0xF731},
+{"oneparen",0x2474},
+{"oneperiod",0x2488},
+{"onepersian",0x06F1},
+{"onequarter",0x00BC},
+{"oneroman",0x2170},
+{"onesuperior",0x00B9},
+{"onethai",0x0E51},
+{"onethird",0x2153},
+{"oogonek",0x01EB},
+{"oogonekmacron",0x01ED},
+{"oogurmukhi",0x0A13},
+{"oomatragurmukhi",0x0A4B},
+{"oopen",0x0254},
+{"oparen",0x24AA},
+{"openbullet",0x25E6},
+{"option",0x2325},
+{"ordfeminine",0x00AA},
+{"ordmasculine",0x00BA},
+{"orthogonal",0x221F},
+{"oshortdeva",0x0912},
+{"oshortvowelsigndeva",0x094A},
+{"oslash",0x00F8},
+{"oslashacute",0x01FF},
+{"osmallhiragana",0x3049},
+{"osmallkatakana",0x30A9},
+{"osmallkatakanahalfwidth",0xFF6B},
+{"ostrokeacute",0x01FF},
+{"osuperior",0xF6F0},
+{"otcyrillic",0x047F},
+{"otilde",0x00F5},
+{"otildeacute",0x1E4D},
+{"otildedieresis",0x1E4F},
+{"oubopomofo",0x3121},
+{"overline",0x203E},
+{"overlinecenterline",0xFE4A},
+{"overlinecmb",0x0305},
+{"overlinedashed",0xFE49},
+{"overlinedblwavy",0xFE4C},
+{"overlinewavy",0xFE4B},
+{"overscore",0x00AF},
+{"ovowelsignbengali",0x09CB},
+{"ovowelsigndeva",0x094B},
+{"ovowelsigngujarati",0x0ACB},
+{"p",0x0070},
+{"paampssquare",0x3380},
+{"paasentosquare",0x332B},
+{"pabengali",0x09AA},
+{"pacute",0x1E55},
+{"padeva",0x092A},
+{"pagedown",0x21DF},
+{"pageup",0x21DE},
+{"pagujarati",0x0AAA},
+{"pagurmukhi",0x0A2A},
+{"pahiragana",0x3071},
+{"paiyannoithai",0x0E2F},
+{"pakatakana",0x30D1},
+{"palatalizationcyrilliccmb",0x0484},
+{"palochkacyrillic",0x04C0},
+{"pansioskorean",0x317F},
+{"paragraph",0x00B6},
+{"parallel",0x2225},
+{"parenleft",0x0028},
+{"parenleftaltonearabic",0xFD3E},
+{"parenleftbt",0xF8ED},
+{"parenleftex",0xF8EC},
+{"parenleftinferior",0x208D},
+{"parenleftmonospace",0xFF08},
+{"parenleftsmall",0xFE59},
+{"parenleftsuperior",0x207D},
+{"parenlefttp",0xF8EB},
+{"parenleftvertical",0xFE35},
+{"parenright",0x0029},
+{"parenrightaltonearabic",0xFD3F},
+{"parenrightbt",0xF8F8},
+{"parenrightex",0xF8F7},
+{"parenrightinferior",0x208E},
+{"parenrightmonospace",0xFF09},
+{"parenrightsmall",0xFE5A},
+{"parenrightsuperior",0x207E},
+{"parenrighttp",0xF8F6},
+{"parenrightvertical",0xFE36},
+{"partialdiff",0x2202},
+{"paseqhebrew",0x05C0},
+{"pashtahebrew",0x0599},
+{"pasquare",0x33A9},
+{"patah",0x05B7},
+{"patah11",0x05B7},
+{"patah1d",0x05B7},
+{"patah2a",0x05B7},
+{"patahhebrew",0x05B7},
+{"patahnarrowhebrew",0x05B7},
+{"patahquarterhebrew",0x05B7},
+{"patahwidehebrew",0x05B7},
+{"pazerhebrew",0x05A1},
+{"pbopomofo",0x3106},
+{"pcircle",0x24DF},
+{"pdotaccent",0x1E57},
+{"pe",0x05E4},
+{"pecyrillic",0x043F},
+{"pedagesh",0xFB44},
+{"pedageshhebrew",0xFB44},
+{"peezisquare",0x333B},
+{"pefinaldageshhebrew",0xFB43},
+{"peharabic",0x067E},
+{"peharmenian",0x057A},
+{"pehebrew",0x05E4},
+{"pehfinalarabic",0xFB57},
+{"pehinitialarabic",0xFB58},
+{"pehiragana",0x307A},
+{"pehmedialarabic",0xFB59},
+{"pekatakana",0x30DA},
+{"pemiddlehookcyrillic",0x04A7},
+{"perafehebrew",0xFB4E},
+{"percent",0x0025},
+{"percentarabic",0x066A},
+{"percentmonospace",0xFF05},
+{"percentsmall",0xFE6A},
+{"period",0x002E},
+{"periodarmenian",0x0589},
+{"periodcentered",0x00B7},
+{"periodhalfwidth",0xFF61},
+{"periodinferior",0xF6E7},
+{"periodmonospace",0xFF0E},
+{"periodsmall",0xFE52},
+{"periodsuperior",0xF6E8},
+{"perispomenigreekcmb",0x0342},
+{"perpendicular",0x22A5},
+{"perthousand",0x2030},
+{"peseta",0x20A7},
+{"pfsquare",0x338A},
+{"phabengali",0x09AB},
+{"phadeva",0x092B},
+{"phagujarati",0x0AAB},
+{"phagurmukhi",0x0A2B},
+{"phi",0x03C6},
+{"phi1",0x03D5},
+{"phieuphacirclekorean",0x327A},
+{"phieuphaparenkorean",0x321A},
+{"phieuphcirclekorean",0x326C},
+{"phieuphkorean",0x314D},
+{"phieuphparenkorean",0x320C},
+{"philatin",0x0278},
+{"phinthuthai",0x0E3A},
+{"phisymbolgreek",0x03D5},
+{"phook",0x01A5},
+{"phophanthai",0x0E1E},
+{"phophungthai",0x0E1C},
+{"phosamphaothai",0x0E20},
+{"pi",0x03C0},
+{"pieupacirclekorean",0x3273},
+{"pieupaparenkorean",0x3213},
+{"pieupcieuckorean",0x3176},
+{"pieupcirclekorean",0x3265},
+{"pieupkiyeokkorean",0x3172},
+{"pieupkorean",0x3142},
+{"pieupparenkorean",0x3205},
+{"pieupsioskiyeokkorean",0x3174},
+{"pieupsioskorean",0x3144},
+{"pieupsiostikeutkorean",0x3175},
+{"pieupthieuthkorean",0x3177},
+{"pieuptikeutkorean",0x3173},
+{"pihiragana",0x3074},
+{"pikatakana",0x30D4},
+{"pisymbolgreek",0x03D6},
+{"piwrarmenian",0x0583},
+{"plus",0x002B},
+{"plusbelowcmb",0x031F},
+{"pluscircle",0x2295},
+{"plusminus",0x00B1},
+{"plusmod",0x02D6},
+{"plusmonospace",0xFF0B},
+{"plussmall",0xFE62},
+{"plussuperior",0x207A},
+{"pmonospace",0xFF50},
+{"pmsquare",0x33D8},
+{"pohiragana",0x307D},
+{"pointingindexdownwhite",0x261F},
+{"pointingindexleftwhite",0x261C},
+{"pointingindexrightwhite",0x261E},
+{"pointingindexupwhite",0x261D},
+{"pokatakana",0x30DD},
+{"poplathai",0x0E1B},
+{"postalmark",0x3012},
+{"postalmarkface",0x3020},
+{"pparen",0x24AB},
+{"precedes",0x227A},
+{"prescription",0x211E},
+{"primemod",0x02B9},
+{"primereversed",0x2035},
+{"product",0x220F},
+{"projective",0x2305},
+{"prolongedkana",0x30FC},
+{"propellor",0x2318},
+{"propersubset",0x2282},
+{"propersuperset",0x2283},
+{"proportion",0x2237},
+{"proportional",0x221D},
+{"psi",0x03C8},
+{"psicyrillic",0x0471},
+{"psilipneumatacyrilliccmb",0x0486},
+{"pssquare",0x33B0},
+{"puhiragana",0x3077},
+{"pukatakana",0x30D7},
+{"pvsquare",0x33B4},
+{"pwsquare",0x33BA},
+{"q",0x0071},
+{"qadeva",0x0958},
+{"qadmahebrew",0x05A8},
+{"qafarabic",0x0642},
+{"qaffinalarabic",0xFED6},
+{"qafinitialarabic",0xFED7},
+{"qafmedialarabic",0xFED8},
+{"qamats",0x05B8},
+{"qamats10",0x05B8},
+{"qamats1a",0x05B8},
+{"qamats1c",0x05B8},
+{"qamats27",0x05B8},
+{"qamats29",0x05B8},
+{"qamats33",0x05B8},
+{"qamatsde",0x05B8},
+{"qamatshebrew",0x05B8},
+{"qamatsnarrowhebrew",0x05B8},
+{"qamatsqatanhebrew",0x05B8},
+{"qamatsqatannarrowhebrew",0x05B8},
+{"qamatsqatanquarterhebrew",0x05B8},
+{"qamatsqatanwidehebrew",0x05B8},
+{"qamatsquarterhebrew",0x05B8},
+{"qamatswidehebrew",0x05B8},
+{"qarneyparahebrew",0x059F},
+{"qbopomofo",0x3111},
+{"qcircle",0x24E0},
+{"qhook",0x02A0},
+{"qmonospace",0xFF51},
+{"qof",0x05E7},
+{"qofdagesh",0xFB47},
+{"qofdageshhebrew",0xFB47},
+{"qofhebrew",0x05E7},
+{"qparen",0x24AC},
+{"quarternote",0x2669},
+{"qubuts",0x05BB},
+{"qubuts18",0x05BB},
+{"qubuts25",0x05BB},
+{"qubuts31",0x05BB},
+{"qubutshebrew",0x05BB},
+{"qubutsnarrowhebrew",0x05BB},
+{"qubutsquarterhebrew",0x05BB},
+{"qubutswidehebrew",0x05BB},
+{"question",0x003F},
+{"questionarabic",0x061F},
+{"questionarmenian",0x055E},
+{"questiondown",0x00BF},
+{"questiondownsmall",0xF7BF},
+{"questiongreek",0x037E},
+{"questionmonospace",0xFF1F},
+{"questionsmall",0xF73F},
+{"quotedbl",0x0022},
+{"quotedblbase",0x201E},
+{"quotedblleft",0x201C},
+{"quotedblmonospace",0xFF02},
+{"quotedblprime",0x301E},
+{"quotedblprimereversed",0x301D},
+{"quotedblright",0x201D},
+{"quoteleft",0x2018},
+{"quoteleftreversed",0x201B},
+{"quotereversed",0x201B},
+{"quoteright",0x2019},
+{"quoterightn",0x0149},
+{"quotesinglbase",0x201A},
+{"quotesingle",0x0027},
+{"quotesinglemonospace",0xFF07},
+{"r",0x0072},
+{"raarmenian",0x057C},
+{"rabengali",0x09B0},
+{"racute",0x0155},
+{"radeva",0x0930},
+{"radical",0x221A},
+{"radicalex",0xF8E5},
+{"radoverssquare",0x33AE},
+{"radoverssquaredsquare",0x33AF},
+{"radsquare",0x33AD},
+{"rafe",0x05BF},
+{"rafehebrew",0x05BF},
+{"ragujarati",0x0AB0},
+{"ragurmukhi",0x0A30},
+{"rahiragana",0x3089},
+{"rakatakana",0x30E9},
+{"rakatakanahalfwidth",0xFF97},
+{"ralowerdiagonalbengali",0x09F1},
+{"ramiddlediagonalbengali",0x09F0},
+{"ramshorn",0x0264},
+{"ratio",0x2236},
+{"rbopomofo",0x3116},
+{"rcaron",0x0159},
+{"rcedilla",0x0157},
+{"rcircle",0x24E1},
+{"rcommaaccent",0x0157},
+{"rdblgrave",0x0211},
+{"rdotaccent",0x1E59},
+{"rdotbelow",0x1E5B},
+{"rdotbelowmacron",0x1E5D},
+{"referencemark",0x203B},
+{"reflexsubset",0x2286},
+{"reflexsuperset",0x2287},
+{"registered",0x00AE},
+{"registersans",0xF8E8},
+{"registerserif",0xF6DA},
+{"reharabic",0x0631},
+{"reharmenian",0x0580},
+{"rehfinalarabic",0xFEAE},
+{"rehiragana",0x308C},
+{"rekatakana",0x30EC},
+{"rekatakanahalfwidth",0xFF9A},
+{"resh",0x05E8},
+{"reshdageshhebrew",0xFB48},
+{"reshhebrew",0x05E8},
+{"reversedtilde",0x223D},
+{"reviahebrew",0x0597},
+{"reviamugrashhebrew",0x0597},
+{"revlogicalnot",0x2310},
+{"rfishhook",0x027E},
+{"rfishhookreversed",0x027F},
+{"rhabengali",0x09DD},
+{"rhadeva",0x095D},
+{"rho",0x03C1},
+{"rhook",0x027D},
+{"rhookturned",0x027B},
+{"rhookturnedsuperior",0x02B5},
+{"rhosymbolgreek",0x03F1},
+{"rhotichookmod",0x02DE},
+{"rieulacirclekorean",0x3271},
+{"rieulaparenkorean",0x3211},
+{"rieulcirclekorean",0x3263},
+{"rieulhieuhkorean",0x3140},
+{"rieulkiyeokkorean",0x313A},
+{"rieulkiyeoksioskorean",0x3169},
+{"rieulkorean",0x3139},
+{"rieulmieumkorean",0x313B},
+{"rieulpansioskorean",0x316C},
+{"rieulparenkorean",0x3203},
+{"rieulphieuphkorean",0x313F},
+{"rieulpieupkorean",0x313C},
+{"rieulpieupsioskorean",0x316B},
+{"rieulsioskorean",0x313D},
+{"rieulthieuthkorean",0x313E},
+{"rieultikeutkorean",0x316A},
+{"rieulyeorinhieuhkorean",0x316D},
+{"rightangle",0x221F},
+{"righttackbelowcmb",0x0319},
+{"righttriangle",0x22BF},
+{"rihiragana",0x308A},
+{"rikatakana",0x30EA},
+{"rikatakanahalfwidth",0xFF98},
+{"ring",0x02DA},
+{"ringbelowcmb",0x0325},
+{"ringcmb",0x030A},
+{"ringhalfleft",0x02BF},
+{"ringhalfleftarmenian",0x0559},
+{"ringhalfleftbelowcmb",0x031C},
+{"ringhalfleftcentered",0x02D3},
+{"ringhalfright",0x02BE},
+{"ringhalfrightbelowcmb",0x0339},
+{"ringhalfrightcentered",0x02D2},
+{"rinvertedbreve",0x0213},
+{"rittorusquare",0x3351},
+{"rlinebelow",0x1E5F},
+{"rlongleg",0x027C},
+{"rlonglegturned",0x027A},
+{"rmonospace",0xFF52},
+{"rohiragana",0x308D},
+{"rokatakana",0x30ED},
+{"rokatakanahalfwidth",0xFF9B},
+{"roruathai",0x0E23},
+{"rparen",0x24AD},
+{"rrabengali",0x09DC},
+{"rradeva",0x0931},
+{"rragurmukhi",0x0A5C},
+{"rreharabic",0x0691},
+{"rrehfinalarabic",0xFB8D},
+{"rrvocalicbengali",0x09E0},
+{"rrvocalicdeva",0x0960},
+{"rrvocalicgujarati",0x0AE0},
+{"rrvocalicvowelsignbengali",0x09C4},
+{"rrvocalicvowelsigndeva",0x0944},
+{"rrvocalicvowelsigngujarati",0x0AC4},
+{"rsuperior",0xF6F1},
+{"rtblock",0x2590},
+{"rturned",0x0279},
+{"rturnedsuperior",0x02B4},
+{"ruhiragana",0x308B},
+{"rukatakana",0x30EB},
+{"rukatakanahalfwidth",0xFF99},
+{"rupeemarkbengali",0x09F2},
+{"rupeesignbengali",0x09F3},
+{"rupiah",0xF6DD},
+{"ruthai",0x0E24},
+{"rvocalicbengali",0x098B},
+{"rvocalicdeva",0x090B},
+{"rvocalicgujarati",0x0A8B},
+{"rvocalicvowelsignbengali",0x09C3},
+{"rvocalicvowelsigndeva",0x0943},
+{"rvocalicvowelsigngujarati",0x0AC3},
+{"s",0x0073},
+{"sabengali",0x09B8},
+{"sacute",0x015B},
+{"sacutedotaccent",0x1E65},
+{"sadarabic",0x0635},
+{"sadeva",0x0938},
+{"sadfinalarabic",0xFEBA},
+{"sadinitialarabic",0xFEBB},
+{"sadmedialarabic",0xFEBC},
+{"sagujarati",0x0AB8},
+{"sagurmukhi",0x0A38},
+{"sahiragana",0x3055},
+{"sakatakana",0x30B5},
+{"sakatakanahalfwidth",0xFF7B},
+{"sallallahoualayhewasallamarabic",0xFDFA},
+{"samekh",0x05E1},
+{"samekhdagesh",0xFB41},
+{"samekhdageshhebrew",0xFB41},
+{"samekhhebrew",0x05E1},
+{"saraaathai",0x0E32},
+{"saraaethai",0x0E41},
+{"saraaimaimalaithai",0x0E44},
+{"saraaimaimuanthai",0x0E43},
+{"saraamthai",0x0E33},
+{"saraathai",0x0E30},
+{"saraethai",0x0E40},
+{"saraiileftthai",0xF886},
+{"saraiithai",0x0E35},
+{"saraileftthai",0xF885},
+{"saraithai",0x0E34},
+{"saraothai",0x0E42},
+{"saraueeleftthai",0xF888},
+{"saraueethai",0x0E37},
+{"saraueleftthai",0xF887},
+{"sarauethai",0x0E36},
+{"sarauthai",0x0E38},
+{"sarauuthai",0x0E39},
+{"sbopomofo",0x3119},
+{"scaron",0x0161},
+{"scarondotaccent",0x1E67},
+{"scedilla",0x015F},
+{"schwa",0x0259},
+{"schwacyrillic",0x04D9},
+{"schwadieresiscyrillic",0x04DB},
+{"schwahook",0x025A},
+{"scircle",0x24E2},
+{"scircumflex",0x015D},
+{"scommaaccent",0x0219},
+{"sdotaccent",0x1E61},
+{"sdotbelow",0x1E63},
+{"sdotbelowdotaccent",0x1E69},
+{"seagullbelowcmb",0x033C},
+{"second",0x2033},
+{"secondtonechinese",0x02CA},
+{"section",0x00A7},
+{"seenarabic",0x0633},
+{"seenfinalarabic",0xFEB2},
+{"seeninitialarabic",0xFEB3},
+{"seenmedialarabic",0xFEB4},
+{"segol",0x05B6},
+{"segol13",0x05B6},
+{"segol1f",0x05B6},
+{"segol2c",0x05B6},
+{"segolhebrew",0x05B6},
+{"segolnarrowhebrew",0x05B6},
+{"segolquarterhebrew",0x05B6},
+{"segoltahebrew",0x0592},
+{"segolwidehebrew",0x05B6},
+{"seharmenian",0x057D},
+{"sehiragana",0x305B},
+{"sekatakana",0x30BB},
+{"sekatakanahalfwidth",0xFF7E},
+{"semicolon",0x003B},
+{"semicolonarabic",0x061B},
+{"semicolonmonospace",0xFF1B},
+{"semicolonsmall",0xFE54},
+{"semivoicedmarkkana",0x309C},
+{"semivoicedmarkkanahalfwidth",0xFF9F},
+{"sentisquare",0x3322},
+{"sentosquare",0x3323},
+{"seven",0x0037},
+{"sevenarabic",0x0667},
+{"sevenbengali",0x09ED},
+{"sevencircle",0x2466},
+{"sevencircleinversesansserif",0x2790},
+{"sevendeva",0x096D},
+{"seveneighths",0x215E},
+{"sevengujarati",0x0AED},
+{"sevengurmukhi",0x0A6D},
+{"sevenhackarabic",0x0667},
+{"sevenhangzhou",0x3027},
+{"sevenideographicparen",0x3226},
+{"seveninferior",0x2087},
+{"sevenmonospace",0xFF17},
+{"sevenoldstyle",0xF737},
+{"sevenparen",0x247A},
+{"sevenperiod",0x248E},
+{"sevenpersian",0x06F7},
+{"sevenroman",0x2176},
+{"sevensuperior",0x2077},
+{"seventeencircle",0x2470},
+{"seventeenparen",0x2484},
+{"seventeenperiod",0x2498},
+{"seventhai",0x0E57},
+{"sfthyphen",0x00AD},
+{"shaarmenian",0x0577},
+{"shabengali",0x09B6},
+{"shacyrillic",0x0448},
+{"shaddaarabic",0x0651},
+{"shaddadammaarabic",0xFC61},
+{"shaddadammatanarabic",0xFC5E},
+{"shaddafathaarabic",0xFC60},
+{"shaddakasraarabic",0xFC62},
+{"shaddakasratanarabic",0xFC5F},
+{"shade",0x2592},
+{"shadedark",0x2593},
+{"shadelight",0x2591},
+{"shademedium",0x2592},
+{"shadeva",0x0936},
+{"shagujarati",0x0AB6},
+{"shagurmukhi",0x0A36},
+{"shalshelethebrew",0x0593},
+{"shbopomofo",0x3115},
+{"shchacyrillic",0x0449},
+{"sheenarabic",0x0634},
+{"sheenfinalarabic",0xFEB6},
+{"sheeninitialarabic",0xFEB7},
+{"sheenmedialarabic",0xFEB8},
+{"sheicoptic",0x03E3},
+{"sheqel",0x20AA},
+{"sheqelhebrew",0x20AA},
+{"sheva",0x05B0},
+{"sheva115",0x05B0},
+{"sheva15",0x05B0},
+{"sheva22",0x05B0},
+{"sheva2e",0x05B0},
+{"shevahebrew",0x05B0},
+{"shevanarrowhebrew",0x05B0},
+{"shevaquarterhebrew",0x05B0},
+{"shevawidehebrew",0x05B0},
+{"shhacyrillic",0x04BB},
+{"shimacoptic",0x03ED},
+{"shin",0x05E9},
+{"shindagesh",0xFB49},
+{"shindageshhebrew",0xFB49},
+{"shindageshshindot",0xFB2C},
+{"shindageshshindothebrew",0xFB2C},
+{"shindageshsindot",0xFB2D},
+{"shindageshsindothebrew",0xFB2D},
+{"shindothebrew",0x05C1},
+{"shinhebrew",0x05E9},
+{"shinshindot",0xFB2A},
+{"shinshindothebrew",0xFB2A},
+{"shinsindot",0xFB2B},
+{"shinsindothebrew",0xFB2B},
+{"shook",0x0282},
+{"sigma",0x03C3},
+{"sigma1",0x03C2},
+{"sigmafinal",0x03C2},
+{"sigmalunatesymbolgreek",0x03F2},
+{"sihiragana",0x3057},
+{"sikatakana",0x30B7},
+{"sikatakanahalfwidth",0xFF7C},
+{"siluqhebrew",0x05BD},
+{"siluqlefthebrew",0x05BD},
+{"similar",0x223C},
+{"sindothebrew",0x05C2},
+{"siosacirclekorean",0x3274},
+{"siosaparenkorean",0x3214},
+{"sioscieuckorean",0x317E},
+{"sioscirclekorean",0x3266},
+{"sioskiyeokkorean",0x317A},
+{"sioskorean",0x3145},
+{"siosnieunkorean",0x317B},
+{"siosparenkorean",0x3206},
+{"siospieupkorean",0x317D},
+{"siostikeutkorean",0x317C},
+{"six",0x0036},
+{"sixarabic",0x0666},
+{"sixbengali",0x09EC},
+{"sixcircle",0x2465},
+{"sixcircleinversesansserif",0x278F},
+{"sixdeva",0x096C},
+{"sixgujarati",0x0AEC},
+{"sixgurmukhi",0x0A6C},
+{"sixhackarabic",0x0666},
+{"sixhangzhou",0x3026},
+{"sixideographicparen",0x3225},
+{"sixinferior",0x2086},
+{"sixmonospace",0xFF16},
+{"sixoldstyle",0xF736},
+{"sixparen",0x2479},
+{"sixperiod",0x248D},
+{"sixpersian",0x06F6},
+{"sixroman",0x2175},
+{"sixsuperior",0x2076},
+{"sixteencircle",0x246F},
+{"sixteencurrencydenominatorbengali",0x09F9},
+{"sixteenparen",0x2483},
+{"sixteenperiod",0x2497},
+{"sixthai",0x0E56},
+{"slash",0x002F},
+{"slashmonospace",0xFF0F},
+{"slong",0x017F},
+{"slongdotaccent",0x1E9B},
+{"smileface",0x263A},
+{"smonospace",0xFF53},
+{"sofpasuqhebrew",0x05C3},
+{"softhyphen",0x00AD},
+{"softsigncyrillic",0x044C},
+{"sohiragana",0x305D},
+{"sokatakana",0x30BD},
+{"sokatakanahalfwidth",0xFF7F},
+{"soliduslongoverlaycmb",0x0338},
+{"solidusshortoverlaycmb",0x0337},
+{"sorusithai",0x0E29},
+{"sosalathai",0x0E28},
+{"sosothai",0x0E0B},
+{"sosuathai",0x0E2A},
+{"space",0x0020},
+{"spacehackarabic",0x0020},
+{"spade",0x2660},
+{"spadesuitblack",0x2660},
+{"spadesuitwhite",0x2664},
+{"sparen",0x24AE},
+{"squarebelowcmb",0x033B},
+{"squarecc",0x33C4},
+{"squarecm",0x339D},
+{"squarediagonalcrosshatchfill",0x25A9},
+{"squarehorizontalfill",0x25A4},
+{"squarekg",0x338F},
+{"squarekm",0x339E},
+{"squarekmcapital",0x33CE},
+{"squareln",0x33D1},
+{"squarelog",0x33D2},
+{"squaremg",0x338E},
+{"squaremil",0x33D5},
+{"squaremm",0x339C},
+{"squaremsquared",0x33A1},
+{"squareorthogonalcrosshatchfill",0x25A6},
+{"squareupperlefttolowerrightfill",0x25A7},
+{"squareupperrighttolowerleftfill",0x25A8},
+{"squareverticalfill",0x25A5},
+{"squarewhitewithsmallblack",0x25A3},
+{"srsquare",0x33DB},
+{"ssabengali",0x09B7},
+{"ssadeva",0x0937},
+{"ssagujarati",0x0AB7},
+{"ssangcieuckorean",0x3149},
+{"ssanghieuhkorean",0x3185},
+{"ssangieungkorean",0x3180},
+{"ssangkiyeokkorean",0x3132},
+{"ssangnieunkorean",0x3165},
+{"ssangpieupkorean",0x3143},
+{"ssangsioskorean",0x3146},
+{"ssangtikeutkorean",0x3138},
+{"ssuperior",0xF6F2},
+{"sterling",0x00A3},
+{"sterlingmonospace",0xFFE1},
+{"strokelongoverlaycmb",0x0336},
+{"strokeshortoverlaycmb",0x0335},
+{"subset",0x2282},
+{"subsetnotequal",0x228A},
+{"subsetorequal",0x2286},
+{"succeeds",0x227B},
+{"suchthat",0x220B},
+{"suhiragana",0x3059},
+{"sukatakana",0x30B9},
+{"sukatakanahalfwidth",0xFF7D},
+{"sukunarabic",0x0652},
+{"summation",0x2211},
+{"sun",0x263C},
+{"superset",0x2283},
+{"supersetnotequal",0x228B},
+{"supersetorequal",0x2287},
+{"svsquare",0x33DC},
+{"syouwaerasquare",0x337C},
+{"t",0x0074},
+{"tabengali",0x09A4},
+{"tackdown",0x22A4},
+{"tackleft",0x22A3},
+{"tadeva",0x0924},
+{"tagujarati",0x0AA4},
+{"tagurmukhi",0x0A24},
+{"taharabic",0x0637},
+{"tahfinalarabic",0xFEC2},
+{"tahinitialarabic",0xFEC3},
+{"tahiragana",0x305F},
+{"tahmedialarabic",0xFEC4},
+{"taisyouerasquare",0x337D},
+{"takatakana",0x30BF},
+{"takatakanahalfwidth",0xFF80},
+{"tatweelarabic",0x0640},
+{"tau",0x03C4},
+{"tav",0x05EA},
+{"tavdages",0xFB4A},
+{"tavdagesh",0xFB4A},
+{"tavdageshhebrew",0xFB4A},
+{"tavhebrew",0x05EA},
+{"tbar",0x0167},
+{"tbopomofo",0x310A},
+{"tcaron",0x0165},
+{"tccurl",0x02A8},
+{"tcedilla",0x0163},
+{"tcheharabic",0x0686},
+{"tchehfinalarabic",0xFB7B},
+{"tchehinitialarabic",0xFB7C},
+{"tchehmedialarabic",0xFB7D},
+{"tcircle",0x24E3},
+{"tcircumflexbelow",0x1E71},
+{"tcommaaccent",0x0163},
+{"tdieresis",0x1E97},
+{"tdotaccent",0x1E6B},
+{"tdotbelow",0x1E6D},
+{"tecyrillic",0x0442},
+{"tedescendercyrillic",0x04AD},
+{"teharabic",0x062A},
+{"tehfinalarabic",0xFE96},
+{"tehhahinitialarabic",0xFCA2},
+{"tehhahisolatedarabic",0xFC0C},
+{"tehinitialarabic",0xFE97},
+{"tehiragana",0x3066},
+{"tehjeeminitialarabic",0xFCA1},
+{"tehjeemisolatedarabic",0xFC0B},
+{"tehmarbutaarabic",0x0629},
+{"tehmarbutafinalarabic",0xFE94},
+{"tehmedialarabic",0xFE98},
+{"tehmeeminitialarabic",0xFCA4},
+{"tehmeemisolatedarabic",0xFC0E},
+{"tehnoonfinalarabic",0xFC73},
+{"tekatakana",0x30C6},
+{"tekatakanahalfwidth",0xFF83},
+{"telephone",0x2121},
+{"telephoneblack",0x260E},
+{"telishagedolahebrew",0x05A0},
+{"telishaqetanahebrew",0x05A9},
+{"tencircle",0x2469},
+{"tenideographicparen",0x3229},
+{"tenparen",0x247D},
+{"tenperiod",0x2491},
+{"tenroman",0x2179},
+{"tesh",0x02A7},
+{"tet",0x05D8},
+{"tetdagesh",0xFB38},
+{"tetdageshhebrew",0xFB38},
+{"tethebrew",0x05D8},
+{"tetsecyrillic",0x04B5},
+{"tevirhebrew",0x059B},
+{"tevirlefthebrew",0x059B},
+{"thabengali",0x09A5},
+{"thadeva",0x0925},
+{"thagujarati",0x0AA5},
+{"thagurmukhi",0x0A25},
+{"thalarabic",0x0630},
+{"thalfinalarabic",0xFEAC},
+{"thanthakhatlowleftthai",0xF898},
+{"thanthakhatlowrightthai",0xF897},
+{"thanthakhatthai",0x0E4C},
+{"thanthakhatupperleftthai",0xF896},
+{"theharabic",0x062B},
+{"thehfinalarabic",0xFE9A},
+{"thehinitialarabic",0xFE9B},
+{"thehmedialarabic",0xFE9C},
+{"thereexists",0x2203},
+{"therefore",0x2234},
+{"theta",0x03B8},
+{"theta1",0x03D1},
+{"thetasymbolgreek",0x03D1},
+{"thieuthacirclekorean",0x3279},
+{"thieuthaparenkorean",0x3219},
+{"thieuthcirclekorean",0x326B},
+{"thieuthkorean",0x314C},
+{"thieuthparenkorean",0x320B},
+{"thirteencircle",0x246C},
+{"thirteenparen",0x2480},
+{"thirteenperiod",0x2494},
+{"thonangmonthothai",0x0E11},
+{"thook",0x01AD},
+{"thophuthaothai",0x0E12},
+{"thorn",0x00FE},
+{"thothahanthai",0x0E17},
+{"thothanthai",0x0E10},
+{"thothongthai",0x0E18},
+{"thothungthai",0x0E16},
+{"thousandcyrillic",0x0482},
+{"thousandsseparatorarabic",0x066C},
+{"thousandsseparatorpersian",0x066C},
+{"three",0x0033},
+{"threearabic",0x0663},
+{"threebengali",0x09E9},
+{"threecircle",0x2462},
+{"threecircleinversesansserif",0x278C},
+{"threedeva",0x0969},
+{"threeeighths",0x215C},
+{"threegujarati",0x0AE9},
+{"threegurmukhi",0x0A69},
+{"threehackarabic",0x0663},
+{"threehangzhou",0x3023},
+{"threeideographicparen",0x3222},
+{"threeinferior",0x2083},
+{"threemonospace",0xFF13},
+{"threenumeratorbengali",0x09F6},
+{"threeoldstyle",0xF733},
+{"threeparen",0x2476},
+{"threeperiod",0x248A},
+{"threepersian",0x06F3},
+{"threequarters",0x00BE},
+{"threequartersemdash",0xF6DE},
+{"threeroman",0x2172},
+{"threesuperior",0x00B3},
+{"threethai",0x0E53},
+{"thzsquare",0x3394},
+{"tihiragana",0x3061},
+{"tikatakana",0x30C1},
+{"tikatakanahalfwidth",0xFF81},
+{"tikeutacirclekorean",0x3270},
+{"tikeutaparenkorean",0x3210},
+{"tikeutcirclekorean",0x3262},
+{"tikeutkorean",0x3137},
+{"tikeutparenkorean",0x3202},
+{"tilde",0x02DC},
+{"tildebelowcmb",0x0330},
+{"tildecmb",0x0303},
+{"tildecomb",0x0303},
+{"tildedoublecmb",0x0360},
+{"tildeoperator",0x223C},
+{"tildeoverlaycmb",0x0334},
+{"tildeverticalcmb",0x033E},
+{"timescircle",0x2297},
+{"tipehahebrew",0x0596},
+{"tipehalefthebrew",0x0596},
+{"tippigurmukhi",0x0A70},
+{"titlocyrilliccmb",0x0483},
+{"tiwnarmenian",0x057F},
+{"tlinebelow",0x1E6F},
+{"tmonospace",0xFF54},
+{"toarmenian",0x0569},
+{"tohiragana",0x3068},
+{"tokatakana",0x30C8},
+{"tokatakanahalfwidth",0xFF84},
+{"tonebarextrahighmod",0x02E5},
+{"tonebarextralowmod",0x02E9},
+{"tonebarhighmod",0x02E6},
+{"tonebarlowmod",0x02E8},
+{"tonebarmidmod",0x02E7},
+{"tonefive",0x01BD},
+{"tonesix",0x0185},
+{"tonetwo",0x01A8},
+{"tonos",0x0384},
+{"tonsquare",0x3327},
+{"topatakthai",0x0E0F},
+{"tortoiseshellbracketleft",0x3014},
+{"tortoiseshellbracketleftsmall",0xFE5D},
+{"tortoiseshellbracketleftvertical",0xFE39},
+{"tortoiseshellbracketright",0x3015},
+{"tortoiseshellbracketrightsmall",0xFE5E},
+{"tortoiseshellbracketrightvertical",0xFE3A},
+{"totaothai",0x0E15},
+{"tpalatalhook",0x01AB},
+{"tparen",0x24AF},
+{"trademark",0x2122},
+{"trademarksans",0xF8EA},
+{"trademarkserif",0xF6DB},
+{"tretroflexhook",0x0288},
+{"triagdn",0x25BC},
+{"triaglf",0x25C4},
+{"triagrt",0x25BA},
+{"triagup",0x25B2},
+{"ts",0x02A6},
+{"tsadi",0x05E6},
+{"tsadidagesh",0xFB46},
+{"tsadidageshhebrew",0xFB46},
+{"tsadihebrew",0x05E6},
+{"tsecyrillic",0x0446},
+{"tsere",0x05B5},
+{"tsere12",0x05B5},
+{"tsere1e",0x05B5},
+{"tsere2b",0x05B5},
+{"tserehebrew",0x05B5},
+{"tserenarrowhebrew",0x05B5},
+{"tserequarterhebrew",0x05B5},
+{"tserewidehebrew",0x05B5},
+{"tshecyrillic",0x045B},
+{"tsuperior",0xF6F3},
+{"ttabengali",0x099F},
+{"ttadeva",0x091F},
+{"ttagujarati",0x0A9F},
+{"ttagurmukhi",0x0A1F},
+{"tteharabic",0x0679},
+{"ttehfinalarabic",0xFB67},
+{"ttehinitialarabic",0xFB68},
+{"ttehmedialarabic",0xFB69},
+{"tthabengali",0x09A0},
+{"tthadeva",0x0920},
+{"tthagujarati",0x0AA0},
+{"tthagurmukhi",0x0A20},
+{"tturned",0x0287},
+{"tuhiragana",0x3064},
+{"tukatakana",0x30C4},
+{"tukatakanahalfwidth",0xFF82},
+{"tusmallhiragana",0x3063},
+{"tusmallkatakana",0x30C3},
+{"tusmallkatakanahalfwidth",0xFF6F},
+{"twelvecircle",0x246B},
+{"twelveparen",0x247F},
+{"twelveperiod",0x2493},
+{"twelveroman",0x217B},
+{"twentycircle",0x2473},
+{"twentyhangzhou",0x5344},
+{"twentyparen",0x2487},
+{"twentyperiod",0x249B},
+{"two",0x0032},
+{"twoarabic",0x0662},
+{"twobengali",0x09E8},
+{"twocircle",0x2461},
+{"twocircleinversesansserif",0x278B},
+{"twodeva",0x0968},
+{"twodotenleader",0x2025},
+{"twodotleader",0x2025},
+{"twodotleadervertical",0xFE30},
+{"twogujarati",0x0AE8},
+{"twogurmukhi",0x0A68},
+{"twohackarabic",0x0662},
+{"twohangzhou",0x3022},
+{"twoideographicparen",0x3221},
+{"twoinferior",0x2082},
+{"twomonospace",0xFF12},
+{"twonumeratorbengali",0x09F5},
+{"twooldstyle",0xF732},
+{"twoparen",0x2475},
+{"twoperiod",0x2489},
+{"twopersian",0x06F2},
+{"tworoman",0x2171},
+{"twostroke",0x01BB},
+{"twosuperior",0x00B2},
+{"twothai",0x0E52},
+{"twothirds",0x2154},
+{"u",0x0075},
+{"uacute",0x00FA},
+{"ubar",0x0289},
+{"ubengali",0x0989},
+{"ubopomofo",0x3128},
+{"ubreve",0x016D},
+{"ucaron",0x01D4},
+{"ucircle",0x24E4},
+{"ucircumflex",0x00FB},
+{"ucircumflexbelow",0x1E77},
+{"ucyrillic",0x0443},
+{"udattadeva",0x0951},
+{"udblacute",0x0171},
+{"udblgrave",0x0215},
+{"udeva",0x0909},
+{"udieresis",0x00FC},
+{"udieresisacute",0x01D8},
+{"udieresisbelow",0x1E73},
+{"udieresiscaron",0x01DA},
+{"udieresiscyrillic",0x04F1},
+{"udieresisgrave",0x01DC},
+{"udieresismacron",0x01D6},
+{"udotbelow",0x1EE5},
+{"ugrave",0x00F9},
+{"ugujarati",0x0A89},
+{"ugurmukhi",0x0A09},
+{"uhiragana",0x3046},
+{"uhookabove",0x1EE7},
+{"uhorn",0x01B0},
+{"uhornacute",0x1EE9},
+{"uhorndotbelow",0x1EF1},
+{"uhorngrave",0x1EEB},
+{"uhornhookabove",0x1EED},
+{"uhorntilde",0x1EEF},
+{"uhungarumlaut",0x0171},
+{"uhungarumlautcyrillic",0x04F3},
+{"uinvertedbreve",0x0217},
+{"ukatakana",0x30A6},
+{"ukatakanahalfwidth",0xFF73},
+{"ukcyrillic",0x0479},
+{"ukorean",0x315C},
+{"umacron",0x016B},
+{"umacroncyrillic",0x04EF},
+{"umacrondieresis",0x1E7B},
+{"umatragurmukhi",0x0A41},
+{"umonospace",0xFF55},
+{"underscore",0x005F},
+{"underscoredbl",0x2017},
+{"underscoremonospace",0xFF3F},
+{"underscorevertical",0xFE33},
+{"underscorewavy",0xFE4F},
+{"union",0x222A},
+{"universal",0x2200},
+{"uogonek",0x0173},
+{"uparen",0x24B0},
+{"upblock",0x2580},
+{"upperdothebrew",0x05C4},
+{"upsilon",0x03C5},
+{"upsilondieresis",0x03CB},
+{"upsilondieresistonos",0x03B0},
+{"upsilonlatin",0x028A},
+{"upsilontonos",0x03CD},
+{"uptackbelowcmb",0x031D},
+{"uptackmod",0x02D4},
+{"uragurmukhi",0x0A73},
+{"uring",0x016F},
+{"ushortcyrillic",0x045E},
+{"usmallhiragana",0x3045},
+{"usmallkatakana",0x30A5},
+{"usmallkatakanahalfwidth",0xFF69},
+{"ustraightcyrillic",0x04AF},
+{"ustraightstrokecyrillic",0x04B1},
+{"utilde",0x0169},
+{"utildeacute",0x1E79},
+{"utildebelow",0x1E75},
+{"uubengali",0x098A},
+{"uudeva",0x090A},
+{"uugujarati",0x0A8A},
+{"uugurmukhi",0x0A0A},
+{"uumatragurmukhi",0x0A42},
+{"uuvowelsignbengali",0x09C2},
+{"uuvowelsigndeva",0x0942},
+{"uuvowelsigngujarati",0x0AC2},
+{"uvowelsignbengali",0x09C1},
+{"uvowelsigndeva",0x0941},
+{"uvowelsigngujarati",0x0AC1},
+{"v",0x0076},
+{"vadeva",0x0935},
+{"vagujarati",0x0AB5},
+{"vagurmukhi",0x0A35},
+{"vakatakana",0x30F7},
+{"vav",0x05D5},
+{"vavdagesh",0xFB35},
+{"vavdagesh65",0xFB35},
+{"vavdageshhebrew",0xFB35},
+{"vavhebrew",0x05D5},
+{"vavholam",0xFB4B},
+{"vavholamhebrew",0xFB4B},
+{"vavvavhebrew",0x05F0},
+{"vavyodhebrew",0x05F1},
+{"vcircle",0x24E5},
+{"vdotbelow",0x1E7F},
+{"vecyrillic",0x0432},
+{"veharabic",0x06A4},
+{"vehfinalarabic",0xFB6B},
+{"vehinitialarabic",0xFB6C},
+{"vehmedialarabic",0xFB6D},
+{"vekatakana",0x30F9},
+{"venus",0x2640},
+{"verticalbar",0x007C},
+{"verticallineabovecmb",0x030D},
+{"verticallinebelowcmb",0x0329},
+{"verticallinelowmod",0x02CC},
+{"verticallinemod",0x02C8},
+{"vewarmenian",0x057E},
+{"vhook",0x028B},
+{"vikatakana",0x30F8},
+{"viramabengali",0x09CD},
+{"viramadeva",0x094D},
+{"viramagujarati",0x0ACD},
+{"visargabengali",0x0983},
+{"visargadeva",0x0903},
+{"visargagujarati",0x0A83},
+{"vmonospace",0xFF56},
+{"voarmenian",0x0578},
+{"voicediterationhiragana",0x309E},
+{"voicediterationkatakana",0x30FE},
+{"voicedmarkkana",0x309B},
+{"voicedmarkkanahalfwidth",0xFF9E},
+{"vokatakana",0x30FA},
+{"vparen",0x24B1},
+{"vtilde",0x1E7D},
+{"vturned",0x028C},
+{"vuhiragana",0x3094},
+{"vukatakana",0x30F4},
+{"w",0x0077},
+{"wacute",0x1E83},
+{"waekorean",0x3159},
+{"wahiragana",0x308F},
+{"wakatakana",0x30EF},
+{"wakatakanahalfwidth",0xFF9C},
+{"wakorean",0x3158},
+{"wasmallhiragana",0x308E},
+{"wasmallkatakana",0x30EE},
+{"wattosquare",0x3357},
+{"wavedash",0x301C},
+{"wavyunderscorevertical",0xFE34},
+{"wawarabic",0x0648},
+{"wawfinalarabic",0xFEEE},
+{"wawhamzaabovearabic",0x0624},
+{"wawhamzaabovefinalarabic",0xFE86},
+{"wbsquare",0x33DD},
+{"wcircle",0x24E6},
+{"wcircumflex",0x0175},
+{"wdieresis",0x1E85},
+{"wdotaccent",0x1E87},
+{"wdotbelow",0x1E89},
+{"wehiragana",0x3091},
+{"weierstrass",0x2118},
+{"wekatakana",0x30F1},
+{"wekorean",0x315E},
+{"weokorean",0x315D},
+{"wgrave",0x1E81},
+{"whitebullet",0x25E6},
+{"whitecircle",0x25CB},
+{"whitecircleinverse",0x25D9},
+{"whitecornerbracketleft",0x300E},
+{"whitecornerbracketleftvertical",0xFE43},
+{"whitecornerbracketright",0x300F},
+{"whitecornerbracketrightvertical",0xFE44},
+{"whitediamond",0x25C7},
+{"whitediamondcontainingblacksmalldiamond",0x25C8},
+{"whitedownpointingsmalltriangle",0x25BF},
+{"whitedownpointingtriangle",0x25BD},
+{"whiteleftpointingsmalltriangle",0x25C3},
+{"whiteleftpointingtriangle",0x25C1},
+{"whitelenticularbracketleft",0x3016},
+{"whitelenticularbracketright",0x3017},
+{"whiterightpointingsmalltriangle",0x25B9},
+{"whiterightpointingtriangle",0x25B7},
+{"whitesmallsquare",0x25AB},
+{"whitesmilingface",0x263A},
+{"whitesquare",0x25A1},
+{"whitestar",0x2606},
+{"whitetelephone",0x260F},
+{"whitetortoiseshellbracketleft",0x3018},
+{"whitetortoiseshellbracketright",0x3019},
+{"whiteuppointingsmalltriangle",0x25B5},
+{"whiteuppointingtriangle",0x25B3},
+{"wihiragana",0x3090},
+{"wikatakana",0x30F0},
+{"wikorean",0x315F},
+{"wmonospace",0xFF57},
+{"wohiragana",0x3092},
+{"wokatakana",0x30F2},
+{"wokatakanahalfwidth",0xFF66},
+{"won",0x20A9},
+{"wonmonospace",0xFFE6},
+{"wowaenthai",0x0E27},
+{"wparen",0x24B2},
+{"wring",0x1E98},
+{"wsuperior",0x02B7},
+{"wturned",0x028D},
+{"wynn",0x01BF},
+{"x",0x0078},
+{"xabovecmb",0x033D},
+{"xbopomofo",0x3112},
+{"xcircle",0x24E7},
+{"xdieresis",0x1E8D},
+{"xdotaccent",0x1E8B},
+{"xeharmenian",0x056D},
+{"xi",0x03BE},
+{"xmonospace",0xFF58},
+{"xparen",0x24B3},
+{"xsuperior",0x02E3},
+{"y",0x0079},
+{"yaadosquare",0x334E},
+{"yabengali",0x09AF},
+{"yacute",0x00FD},
+{"yadeva",0x092F},
+{"yaekorean",0x3152},
+{"yagujarati",0x0AAF},
+{"yagurmukhi",0x0A2F},
+{"yahiragana",0x3084},
+{"yakatakana",0x30E4},
+{"yakatakanahalfwidth",0xFF94},
+{"yakorean",0x3151},
+{"yamakkanthai",0x0E4E},
+{"yasmallhiragana",0x3083},
+{"yasmallkatakana",0x30E3},
+{"yasmallkatakanahalfwidth",0xFF6C},
+{"yatcyrillic",0x0463},
+{"ycircle",0x24E8},
+{"ycircumflex",0x0177},
+{"ydieresis",0x00FF},
+{"ydotaccent",0x1E8F},
+{"ydotbelow",0x1EF5},
+{"yeharabic",0x064A},
+{"yehbarreearabic",0x06D2},
+{"yehbarreefinalarabic",0xFBAF},
+{"yehfinalarabic",0xFEF2},
+{"yehhamzaabovearabic",0x0626},
+{"yehhamzaabovefinalarabic",0xFE8A},
+{"yehhamzaaboveinitialarabic",0xFE8B},
+{"yehhamzaabovemedialarabic",0xFE8C},
+{"yehinitialarabic",0xFEF3},
+{"yehmedialarabic",0xFEF4},
+{"yehmeeminitialarabic",0xFCDD},
+{"yehmeemisolatedarabic",0xFC58},
+{"yehnoonfinalarabic",0xFC94},
+{"yehthreedotsbelowarabic",0x06D1},
+{"yekorean",0x3156},
+{"yen",0x00A5},
+{"yenmonospace",0xFFE5},
+{"yeokorean",0x3155},
+{"yeorinhieuhkorean",0x3186},
+{"yerahbenyomohebrew",0x05AA},
+{"yerahbenyomolefthebrew",0x05AA},
+{"yericyrillic",0x044B},
+{"yerudieresiscyrillic",0x04F9},
+{"yesieungkorean",0x3181},
+{"yesieungpansioskorean",0x3183},
+{"yesieungsioskorean",0x3182},
+{"yetivhebrew",0x059A},
+{"ygrave",0x1EF3},
+{"yhook",0x01B4},
+{"yhookabove",0x1EF7},
+{"yiarmenian",0x0575},
+{"yicyrillic",0x0457},
+{"yikorean",0x3162},
+{"yinyang",0x262F},
+{"yiwnarmenian",0x0582},
+{"ymonospace",0xFF59},
+{"yod",0x05D9},
+{"yoddagesh",0xFB39},
+{"yoddageshhebrew",0xFB39},
+{"yodhebrew",0x05D9},
+{"yodyodhebrew",0x05F2},
+{"yodyodpatahhebrew",0xFB1F},
+{"yohiragana",0x3088},
+{"yoikorean",0x3189},
+{"yokatakana",0x30E8},
+{"yokatakanahalfwidth",0xFF96},
+{"yokorean",0x315B},
+{"yosmallhiragana",0x3087},
+{"yosmallkatakana",0x30E7},
+{"yosmallkatakanahalfwidth",0xFF6E},
+{"yotgreek",0x03F3},
+{"yoyaekorean",0x3188},
+{"yoyakorean",0x3187},
+{"yoyakthai",0x0E22},
+{"yoyingthai",0x0E0D},
+{"yparen",0x24B4},
+{"ypogegrammeni",0x037A},
+{"ypogegrammenigreekcmb",0x0345},
+{"yr",0x01A6},
+{"yring",0x1E99},
+{"ysuperior",0x02B8},
+{"ytilde",0x1EF9},
+{"yturned",0x028E},
+{"yuhiragana",0x3086},
+{"yuikorean",0x318C},
+{"yukatakana",0x30E6},
+{"yukatakanahalfwidth",0xFF95},
+{"yukorean",0x3160},
+{"yusbigcyrillic",0x046B},
+{"yusbigiotifiedcyrillic",0x046D},
+{"yuslittlecyrillic",0x0467},
+{"yuslittleiotifiedcyrillic",0x0469},
+{"yusmallhiragana",0x3085},
+{"yusmallkatakana",0x30E5},
+{"yusmallkatakanahalfwidth",0xFF6D},
+{"yuyekorean",0x318B},
+{"yuyeokorean",0x318A},
+{"yyabengali",0x09DF},
+{"yyadeva",0x095F},
+{"z",0x007A},
+{"zaarmenian",0x0566},
+{"zacute",0x017A},
+{"zadeva",0x095B},
+{"zagurmukhi",0x0A5B},
+{"zaharabic",0x0638},
+{"zahfinalarabic",0xFEC6},
+{"zahinitialarabic",0xFEC7},
+{"zahiragana",0x3056},
+{"zahmedialarabic",0xFEC8},
+{"zainarabic",0x0632},
+{"zainfinalarabic",0xFEB0},
+{"zakatakana",0x30B6},
+{"zaqefgadolhebrew",0x0595},
+{"zaqefqatanhebrew",0x0594},
+{"zarqahebrew",0x0598},
+{"zayin",0x05D6},
+{"zayindagesh",0xFB36},
+{"zayindageshhebrew",0xFB36},
+{"zayinhebrew",0x05D6},
+{"zbopomofo",0x3117},
+{"zcaron",0x017E},
+{"zcircle",0x24E9},
+{"zcircumflex",0x1E91},
+{"zcurl",0x0291},
+{"zdot",0x017C},
+{"zdotaccent",0x017C},
+{"zdotbelow",0x1E93},
+{"zecyrillic",0x0437},
+{"zedescendercyrillic",0x0499},
+{"zedieresiscyrillic",0x04DF},
+{"zehiragana",0x305C},
+{"zekatakana",0x30BC},
+{"zero",0x0030},
+{"zeroarabic",0x0660},
+{"zerobengali",0x09E6},
+{"zerodeva",0x0966},
+{"zerogujarati",0x0AE6},
+{"zerogurmukhi",0x0A66},
+{"zerohackarabic",0x0660},
+{"zeroinferior",0x2080},
+{"zeromonospace",0xFF10},
+{"zerooldstyle",0xF730},
+{"zeropersian",0x06F0},
+{"zerosuperior",0x2070},
+{"zerothai",0x0E50},
+{"zerowidthjoiner",0xFEFF},
+{"zerowidthnonjoiner",0x200C},
+{"zerowidthspace",0x200B},
+{"zeta",0x03B6},
+{"zhbopomofo",0x3113},
+{"zhearmenian",0x056A},
+{"zhebrevecyrillic",0x04C2},
+{"zhecyrillic",0x0436},
+{"zhedescendercyrillic",0x0497},
+{"zhedieresiscyrillic",0x04DD},
+{"zihiragana",0x3058},
+{"zikatakana",0x30B8},
+{"zinorhebrew",0x05AE},
+{"zlinebelow",0x1E95},
+{"zmonospace",0xFF5A},
+{"zohiragana",0x305E},
+{"zokatakana",0x30BE},
+{"zparen",0x24B5},
+{"zretroflexhook",0x0290},
+{"zstroke",0x01B6},
+{"zuhiragana",0x305A},
+{"zukatakana",0x30BA},
+
+/* deleted because they decompose to more than one character */
+/*
+{"dalethatafpatah",0x05D3 05B2},
+{"dalethatafpatahhebrew",0x05D3 05B2},
+{"dalethatafsegol",0x05D3 05B1},
+{"dalethatafsegolhebrew",0x05D3 05B1},
+{"dalethiriq",0x05D3 05B4},
+{"dalethiriqhebrew",0x05D3 05B4},
+{"daletholam",0x05D3 05B9},
+{"daletholamhebrew",0x05D3 05B9},
+{"daletpatah",0x05D3 05B7},
+{"daletpatahhebrew",0x05D3 05B7},
+{"daletqamats",0x05D3 05B8},
+{"daletqamatshebrew",0x05D3 05B8},
+{"daletqubuts",0x05D3 05BB},
+{"daletqubutshebrew",0x05D3 05BB},
+{"daletsegol",0x05D3 05B6},
+{"daletsegolhebrew",0x05D3 05B6},
+{"daletsheva",0x05D3 05B0},
+{"daletshevahebrew",0x05D3 05B0},
+{"dalettsere",0x05D3 05B5},
+{"dalettserehebrew",0x05D3 05B5},
+{"finalkafqamats",0x05DA 05B8},
+{"finalkafqamatshebrew",0x05DA 05B8},
+{"finalkafsheva",0x05DA 05B0},
+{"finalkafshevahebrew",0x05DA 05B0},
+{"hamzadammaarabic",0x0621 064F},
+{"hamzadammatanarabic",0x0621 064C},
+{"hamzafathaarabic",0x0621 064E},
+{"hamzafathatanarabic",0x0621 064B},
+{"hamzalowkasraarabic",0x0621 0650},
+{"hamzalowkasratanarabic",0x0621 064D},
+{"hamzasukunarabic",0x0621 0652},
+{"lamedholam",0x05DC 05B9},
+{"lamedholamdagesh",0x05DC 05B9 05BC},
+{"lamedholamdageshhebrew",0x05DC 05B9 05BC},
+{"lamedholamhebrew",0x05DC 05B9},
+{"lammeemjeeminitialarabic",0xFEDF FEE4 FEA0},
+{"lammeemkhahinitialarabic",0xFEDF FEE4 FEA8},
+{"noonhehinitialarabic",0xFEE7 FEEC},
+{"qofhatafpatah",0x05E7 05B2},
+{"qofhatafpatahhebrew",0x05E7 05B2},
+{"qofhatafsegol",0x05E7 05B1},
+{"qofhatafsegolhebrew",0x05E7 05B1},
+{"qofhiriq",0x05E7 05B4},
+{"qofhiriqhebrew",0x05E7 05B4},
+{"qofholam",0x05E7 05B9},
+{"qofholamhebrew",0x05E7 05B9},
+{"qofpatah",0x05E7 05B7},
+{"qofpatahhebrew",0x05E7 05B7},
+{"qofqamats",0x05E7 05B8},
+{"qofqamatshebrew",0x05E7 05B8},
+{"qofqubuts",0x05E7 05BB},
+{"qofqubutshebrew",0x05E7 05BB},
+{"qofsegol",0x05E7 05B6},
+{"qofsegolhebrew",0x05E7 05B6},
+{"qofsheva",0x05E7 05B0},
+{"qofshevahebrew",0x05E7 05B0},
+{"qoftsere",0x05E7 05B5},
+{"qoftserehebrew",0x05E7 05B5},
+{"rehyehaleflamarabic",0x0631 FEF3 FE8E 0644},
+{"reshhatafpatah",0x05E8 05B2},
+{"reshhatafpatahhebrew",0x05E8 05B2},
+{"reshhatafsegol",0x05E8 05B1},
+{"reshhatafsegolhebrew",0x05E8 05B1},
+{"reshhiriq",0x05E8 05B4},
+{"reshhiriqhebrew",0x05E8 05B4},
+{"reshholam",0x05E8 05B9},
+{"reshholamhebrew",0x05E8 05B9},
+{"reshpatah",0x05E8 05B7},
+{"reshpatahhebrew",0x05E8 05B7},
+{"reshqamats",0x05E8 05B8},
+{"reshqamatshebrew",0x05E8 05B8},
+{"reshqubuts",0x05E8 05BB},
+{"reshqubutshebrew",0x05E8 05BB},
+{"reshsegol",0x05E8 05B6},
+{"reshsegolhebrew",0x05E8 05B6},
+{"reshsheva",0x05E8 05B0},
+{"reshshevahebrew",0x05E8 05B0},
+{"reshtsere",0x05E8 05B5},
+{"reshtserehebrew",0x05E8 05B5},
+{"shaddafathatanarabic",0x0651 064B},
+{"tchehmeeminitialarabic",0xFB7C FEE4},
+*/
+};
+
diff --git a/mupdf/fontenc.h b/mupdf/fontenc.h
new file mode 100644
index 00000000..376a3f2d
--- /dev/null
+++ b/mupdf/fontenc.h
@@ -0,0 +1,349 @@
+/*
+ * Built-in font tables
+ */
+
+#define _notdef 0
+
+static char *basefontnames[14][7] =
+{
+ { "Courier", "CourierNew", "CourierNewPSMT", 0 },
+ { "Courier-Bold", "CourierNew,Bold", "Courier,Bold",
+ "CourierNewPS-BoldMT", "CourierNew-Bold", 0 },
+ { "Courier-Oblique", "CourierNew,Italic", "Courier,Italic",
+ "CourierNewPS-ItalicMT", "CourierNew-Italic", 0 },
+ { "Courier-BoldOblique", "CourierNew,BoldItalic", "Courier,BoldItalic",
+ "CourierNewPS-BoldItalicMT", "CourierNew-BoldItalic", 0 },
+ { "Helvetica", "ArialMT", "Arial", 0 },
+ { "Helvetica-Bold", "Arial-BoldMT", "Arial,Bold", "Arial-Bold",
+ "Helvetica,Bold", 0 },
+ { "Helvetica-Oblique", "Arial-ItalicMT", "Arial,Italic", "Arial-Italic",
+ "Helvetica,Italic", "Helvetica-Italic", 0 },
+ { "Helvetica-BoldOblique", "Arial-BoldItalicMT",
+ "Arial,BoldItalic", "Arial-BoldItalic",
+ "Helvetica,BoldItalic", "Helvetica-BoldItalic", 0 },
+ { "Times-Roman", "TimesNewRomanPSMT", "TimesNewRoman",
+ "TimesNewRomanPS", 0 },
+ { "Times-Bold", "TimesNewRomanPS-BoldMT", "TimesNewRoman,Bold",
+ "TimesNewRomanPS-Bold", "TimesNewRoman-Bold", 0 },
+ { "Times-Italic", "TimesNewRomanPS-ItalicMT", "TimesNewRoman,Italic",
+ "TimesNewRomanPS-Italic", "TimesNewRoman-Italic", 0 },
+ { "Times-BoldItalic", "TimesNewRomanPS-BoldItalicMT",
+ "TimesNewRoman,BoldItalic", "TimesNewRomanPS-BoldItalic",
+ "TimesNewRoman-BoldItalic", 0 },
+ { "Symbol", 0 },
+ { "ZapfDingbats", 0 }
+};
+
+static char *macroman[256] = { _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ "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", _notdef,
+ "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", _notdef, "AE",
+ "Oslash", _notdef, "plusminus", _notdef, _notdef, "yen", "mu",
+ _notdef, _notdef, _notdef, _notdef, _notdef, "ordfeminine",
+ "ordmasculine", _notdef, "ae", "oslash", "questiondown", "exclamdown",
+ "logicalnot", _notdef, "florin", _notdef, _notdef, "guillemotleft",
+ "guillemotright", "ellipsis", "space", "Agrave", "Atilde", "Otilde",
+ "OE", "oe", "endash", "emdash", "quotedblleft", "quotedblright",
+ "quoteleft", "quoteright", "divide", _notdef, "ydieresis",
+ "Ydieresis", "fraction", "currency", "guilsinglleft", "guilsinglright",
+ "fi", "fl", "daggerdbl", "periodcentered", "quotesinglbase",
+ "quotedblbase", "perthousand", "Acircumflex", "Ecircumflex", "Aacute",
+ "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave",
+ "Oacute", "Ocircumflex", _notdef, "Ograve", "Uacute", "Ucircumflex",
+ "Ugrave", "dotlessi", "circumflex", "tilde", "macron", "breve",
+ "dotaccent", "ring", "cedilla", "hungarumlaut", "ogonek", "caron" };
+
+static char *macexpert[256] = { _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ "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", _notdef,
+ "threequartersemdash", _notdef, "questionsmall", _notdef,
+ _notdef, _notdef, _notdef, "Ethsmall", _notdef, _notdef,
+ "onequarter", "onehalf", "threequarters", "oneeighth", "threeeighths",
+ "fiveeighths", "seveneighths", "onethird", "twothirds", _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, "ff", "fi",
+ "fl", "ffi", "ffl", "parenleftinferior", _notdef, "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", _notdef, _notdef, "asuperior", "centsuperior",
+ _notdef, _notdef, _notdef, _notdef, "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",
+ _notdef, "eightsuperior", "fourinferior", "threeinferior",
+ "sixinferior", "eightinferior", "seveninferior", "Scaronsmall",
+ _notdef, "centinferior", "twoinferior", _notdef, "Dieresissmall",
+ _notdef, "Caronsmall", "osuperior", "fiveinferior", _notdef,
+ "commainferior", "periodinferior", "Yacutesmall", _notdef,
+ "dollarinferior", _notdef, _notdef, "Thornsmall", _notdef,
+ "nineinferior", "zeroinferior", "Zcaronsmall", "AEsmall", "Oslashsmall",
+ "questiondownsmall", "oneinferior", "Lslashsmall", _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, "Cedillasmall",
+ _notdef, _notdef, _notdef, _notdef, _notdef, "OEsmall",
+ "figuredash", "hyphensuperior", _notdef, _notdef, _notdef,
+ _notdef, "exclamdownsmall", _notdef, "Ydieresissmall", _notdef,
+ "onesuperior", "twosuperior", "threesuperior", "foursuperior",
+ "fivesuperior", "sixsuperior", "sevensuperior", "ninesuperior",
+ "zerosuperior", _notdef, "esuperior", "rsuperior", "tsuperior",
+ _notdef, _notdef, "isuperior", "ssuperior", "dsuperior",
+ _notdef, _notdef, _notdef, _notdef, _notdef, "lsuperior",
+ "Ogoneksmall", "Brevesmall", "Macronsmall", "bsuperior", "nsuperior",
+ "msuperior", "commasuperior", "periodsuperior", "Dotaccentsmall",
+ "Ringsmall", _notdef, _notdef, _notdef, _notdef };
+
+static char *winansi[256] = { _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, "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" };
+
+#if 0
+
+static char *standard[256] = { _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _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", _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, "exclamdown", "cent", "sterling",
+ "fraction", "yen", "florin", "section", "currency", "quotesingle",
+ "quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright",
+ "fi", "fl", _notdef, "endash", "dagger", "daggerdbl", "periodcentered",
+ _notdef, "paragraph", "bullet", "quotesinglbase", "quotedblbase",
+ "quotedblright", "guillemotright", "ellipsis", "perthousand",
+ _notdef, "questiondown", _notdef, "grave", "acute", "circumflex",
+ "tilde", "macron", "breve", "dotaccent", "dieresis", _notdef,
+ "ring", "cedilla", _notdef, "hungarumlaut", "ogonek", "caron",
+ "emdash", _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, "AE",
+ _notdef, "ordfeminine", _notdef, _notdef, _notdef, _notdef,
+ "Lslash", "Oslash", "OE", "ordmasculine", _notdef, _notdef,
+ _notdef, _notdef, _notdef, "ae", _notdef, _notdef,
+ _notdef, "dotlessi", _notdef, _notdef, "lslash", "oslash",
+ "oe", "germandbls", _notdef, _notdef, _notdef, _notdef };
+
+static char *expert[256] = { _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, "space",
+ "exclamsmall", "Hungarumlautsmall", _notdef, "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", _notdef, "asuperior", "bsuperior",
+ "centsuperior", "dsuperior", "esuperior", _notdef, _notdef,
+ _notdef, "isuperior", _notdef, _notdef, "lsuperior", "msuperior",
+ "nsuperior", "osuperior", _notdef, _notdef, "rsuperior",
+ "ssuperior", "tsuperior", _notdef, "ff", "fi", "fl", "ffi", "ffl",
+ "parenleftinferior", _notdef, "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", _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, "exclamdownsmall",
+ "centoldstyle", "Lslashsmall", _notdef, _notdef, "Scaronsmall",
+ "Zcaronsmall", "Dieresissmall", "Brevesmall", "Caronsmall", _notdef,
+ "Dotaccentsmall", _notdef, _notdef, "Macronsmall", _notdef,
+ _notdef, "figuredash", "hypheninferior", _notdef, _notdef,
+ "Ogoneksmall", "Ringsmall", "Cedillasmall", _notdef, _notdef,
+ _notdef, "onequarter", "onehalf", "threequarters", "questiondownsmall",
+ "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird",
+ "twothirds", _notdef, _notdef, "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" };
+
+static char *symbol[256] = { _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, "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", _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, "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", _notdef,
+ "angleright", "integral", "integraltp", "integralex", "integralbt",
+ "parenrighttp", "parenrightex", "parenrightbt", "bracketrighttp",
+ "bracketrightex", "bracketrightbt", "bracerighttp", "bracerightmid",
+ "bracerightbt", _notdef };
+
+static char *zapfdingbats[256] = { _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ "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", _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
+ _notdef, "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",
+ _notdef, "a201", "a183", "a184", "a197", "a185", "a194", "a198",
+ "a186", "a195", "a187", "a188", "a189", "a190", "a191", _notdef };
+
+#endif
+
diff --git a/mupdf/fontfile.c b/mupdf/fontfile.c
new file mode 100644
index 00000000..3c0470f7
--- /dev/null
+++ b/mupdf/fontfile.c
@@ -0,0 +1,263 @@
+#include <fitz.h>
+#include <mupdf.h>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include <fontconfig/fontconfig.h>
+
+static FT_Library ftlib = nil;
+static FcConfig *fclib = nil;
+
+static char *basenames[14] =
+{
+ "Courier",
+ "Courier-Bold",
+ "Courier-Oblique",
+ "Courier-BoldOblique",
+ "Helvetica",
+ "Helvetica-Bold",
+ "Helvetica-Oblique",
+ "Helvetica-BoldOblique",
+ "Times-Roman",
+ "Times-Bold",
+ "Times-Italic",
+ "Times-BoldItalic",
+ "Symbol",
+ "ZapfDingbats"
+};
+
+static char *basepatterns[14] =
+{
+ "Courier,Nimbus Mono L,Courier New:style=Regular,Roman",
+ "Courier,Nimbus Mono L,Courier New:style=Bold",
+ "Courier,Nimbus Mono L,Courier New:style=Oblique,Italic",
+ "Courier,Nimbus Mono L,Courier New:style=BoldOblique,BoldItalic",
+ "Helvetica,Nimbus Sans L,Arial:style=Regular,Roman",
+ "Helvetica,Nimbus Sans L,Arial:style=Bold",
+ "Helvetica,Nimbus Sans L,Arial:style=Oblique,Italic",
+ "Helvetica,Nimbus Sans L,Arial:style=BoldOblique,BoldItalic",
+ "Times,Nimbus Roman No9 L,Times New Roman:style=Regular,Roman",
+ "Times,Nimbus Roman No9 L,Times New Roman:style=Bold,Medium",
+ "Times,Nimbus Roman No9 L,Times New Roman:style=Italic,Regular Italic",
+ "Times,Nimbus Roman No9 L,Times New Roman:style=BoldItalic,Medium Italic",
+ "Standard Symbols L,Symbol",
+ "Zapf Dingbats,Dingbats"
+};
+
+static fz_error *initfontlibs(void)
+{
+ int fterr;
+
+ if (ftlib)
+ return nil;
+
+ fterr = FT_Init_FreeType(&ftlib);
+ if (fterr)
+ return fz_throw("freetype failed initialisation: 0x%x", fterr);
+
+ fclib = FcInitLoadConfigAndFonts();
+ if (!fclib)
+ return fz_throw("fontconfig failed initialisation");
+
+ return nil;
+}
+
+fz_error *
+pdf_loadbuiltinfont(void **fontp, char *pattern)
+{
+ fz_error *error;
+ FcResult fcerr;
+ int fterr;
+
+ FcPattern *searchpat;
+ FcPattern *matchpat;
+ FT_Face face;
+ char *file;
+
+ error = initfontlibs();
+ if (error)
+ return error;
+
+ fcerr = FcResultMatch;
+ searchpat = FcNameParse(pattern);
+ FcDefaultSubstitute(searchpat);
+ FcConfigSubstitute(fclib, searchpat, FcMatchPattern);
+
+ matchpat = FcFontMatch(fclib, searchpat, &fcerr);
+ if (fcerr != FcResultMatch)
+ return fz_throw("fontconfig could not find font %s", pattern);
+
+ fcerr = FcPatternGetString(matchpat, "file", 0, (FcChar8**)&file);
+ if (fcerr != FcResultMatch)
+ return fz_throw("fontconfig could not find font %s", pattern);
+
+ fterr = FT_New_Face(ftlib, file, 0, &face);
+ if (fterr)
+ return fz_throw("freetype could not load font file '%s': 0x%x", file, fterr);
+
+ FcPatternDestroy(matchpat);
+ FcPatternDestroy(searchpat);
+
+ *fontp = face;
+
+ return nil;
+}
+
+fz_error *
+pdf_loadsystemfont(void **fontp, char *basefont, char *collection)
+{
+ fz_error *error;
+ FcResult fcerr;
+ int fterr;
+
+ char fontname[200];
+ FcPattern *searchpat;
+ FcPattern *matchpat;
+ FT_Face face;
+ char *style;
+ char *file;
+
+ error = initfontlibs();
+ if (error)
+ return error;
+
+ /* parse windows-style font name descriptors Font,Style or Font-Style */
+ strlcpy(fontname, basefont, sizeof fontname);
+
+ style = strchr(fontname, ',');
+ if (style) {
+ *style++ = 0;
+ }
+ else {
+ style = strchr(fontname, '-');
+ if (style)
+ *style++ = 0;
+ }
+
+ searchpat = FcPatternCreate();
+ if (!searchpat)
+ return fz_outofmem;
+
+ error = fz_outofmem;
+
+ if (!FcPatternAddString(searchpat, FC_FAMILY, fontname))
+ goto cleanup;
+ if (collection)
+ if (!FcPatternAddString(searchpat, FC_FAMILY, collection))
+ goto cleanup;
+ if (style)
+ if (!FcPatternAddString(searchpat, FC_STYLE, style))
+ goto cleanup;
+ if (!FcPatternAddBool(searchpat, FC_OUTLINE, 1))
+ goto cleanup;
+
+file = FcNameUnparse(searchpat);
+printf(" system font pattern %s\n", file);
+free(file);
+
+ fcerr = FcResultMatch;
+ FcDefaultSubstitute(searchpat);
+ FcConfigSubstitute(fclib, searchpat, FcMatchPattern);
+
+ matchpat = FcFontMatch(fclib, searchpat, &fcerr);
+ if (fcerr != FcResultMatch)
+ return fz_throw("fontconfig could not find font %s", basefont);
+
+ fcerr = FcPatternGetString(matchpat, "file", 0, (FcChar8**)&file);
+ if (fcerr != FcResultMatch)
+ return fz_throw("fontconfig could not find font %s", basefont);
+
+printf(" system font file %s\n", file);
+
+ fterr = FT_New_Face(ftlib, file, 0, &face);
+ if (fterr) {
+ FcPatternDestroy(matchpat);
+ FcPatternDestroy(searchpat);
+ return fz_throw("freetype could not load font file '%s': 0x%x", file, fterr);
+ }
+
+ FcPatternDestroy(matchpat);
+ FcPatternDestroy(searchpat);
+
+ *fontp = face;
+
+ return nil;
+
+cleanup:
+ FcPatternDestroy(searchpat);
+ return error;
+}
+
+fz_error *
+pdf_loadembeddedfont(void **fontp, pdf_xref *xref, fz_obj *stmref)
+{
+ fz_error *error;
+ int fterr;
+ FT_Face face;
+ unsigned char *buf;
+ int len;
+
+ error = initfontlibs();
+ if (error)
+ return error;
+
+ error = pdf_readstream(&buf, &len, xref, stmref);
+ if (error)
+ return error;
+
+printf("readstream $%p %d\n", buf, len);
+
+ fterr = FT_New_Memory_Face(ftlib, buf, len, 0, &face);
+
+ if (fterr) {
+ fz_free(buf);
+ return fz_throw("freetype could not load embedded font: 0x%x", fterr);
+ }
+
+ *fontp = face;
+
+ /* TODO: figure out how to free 'buf' when the FT_Face is freed */
+
+ return nil;
+}
+
+fz_error *
+pdf_loadfontdescriptor(void **facep, pdf_xref *xref, fz_obj *desc, char *collection)
+{
+ fz_error *error;
+ fz_obj *obj1, *obj2, *obj3, *obj;
+ char *fontname;
+
+ error = pdf_resolve(&desc, xref);
+ if (error)
+ return error;
+
+ fontname = fz_toname(fz_dictgets(desc, "FontName"));
+
+ obj1 = fz_dictgets(desc, "FontFile");
+ obj2 = fz_dictgets(desc, "FontFile2");
+ obj3 = fz_dictgets(desc, "FontFile3");
+ obj = obj1 ? obj1 : obj2 ? obj2 : obj3;
+
+ if (fz_isindirect(obj))
+ {
+ error = pdf_loadembeddedfont(facep, xref, obj);
+ if (error)
+ goto cleanup;
+ }
+ else
+ {
+ error = pdf_loadsystemfont(facep, fontname, collection);
+ if (error)
+ goto cleanup;
+ }
+
+ fz_dropobj(desc);
+
+ return nil;
+
+cleanup:
+ fz_dropobj(desc);
+ return error;
+}
+
diff --git a/mupdf/interpret.c b/mupdf/interpret.c
new file mode 100644
index 00000000..6ad24d11
--- /dev/null
+++ b/mupdf/interpret.c
@@ -0,0 +1,786 @@
+#include <fitz.h>
+#include <mupdf.h>
+
+void pdf_initgstate(pdf_gstate *gs);
+
+fz_error *pdf_buildstrokepath(pdf_gstate *gs, fz_path *path);
+fz_error *pdf_buildfillpath(pdf_gstate *gs, fz_path *path, int evenodd);
+
+fz_error *pdf_addfillshape(pdf_gstate *gs, fz_node *shape);
+fz_error *pdf_addstrokeshape(pdf_gstate *gs, fz_node *shape);
+fz_error *pdf_addclipmask(pdf_gstate *gs, fz_node *shape);
+fz_error *pdf_addtransform(pdf_gstate *gs, fz_node *affine);
+
+fz_error *pdf_showpath(pdf_csi *, int doclose, int dofill, int dostroke, int evenodd);
+fz_error *pdf_showtext(pdf_csi *, fz_obj *text);
+fz_error *pdf_flushtext(pdf_csi *);
+
+fz_error *
+pdf_newcsi(pdf_csi **csip)
+{
+ fz_error *error;
+ pdf_csi *csi;
+ fz_node *node;
+
+ csi = *csip = fz_malloc(sizeof(pdf_csi));
+ if (!csi)
+ return fz_outofmem;
+
+ pdf_initgstate(&csi->gstate[0]);
+
+ csi->gtop = 0;
+ csi->top = 0;
+
+ csi->xbalance = 0;
+
+ error = fz_newpath(&csi->path);
+ if (error) {
+ fz_free(csi);
+ return error;
+ }
+
+ error = fz_newtree(&csi->tree);
+ if (error) {
+ fz_freepath(csi->path);
+ fz_free(csi);
+ return error;
+ }
+
+ error = fz_newover(&node);
+ csi->tree->root = node;
+ csi->gstate[0].head = node;
+
+ csi->clip = nil;
+
+ csi->text = nil;
+ csi->tm = fz_identity();
+ csi->tlm = fz_identity();
+
+ return nil;
+}
+
+static void
+clearstack(pdf_csi *csi)
+{
+ int i;
+ for (i = 0; i < csi->top; i++)
+ fz_dropobj(csi->stack[i]);
+ csi->top = 0;
+}
+
+void
+pdf_freecsi(pdf_csi *csi)
+{
+ if (csi->path) fz_freepath(csi->path);
+ if (csi->clip) fz_freepath(csi->clip);
+ if (csi->text) fz_freetext(csi->text);
+ clearstack(csi);
+ fz_free(csi);
+}
+
+static fz_error *
+runextgstate(pdf_gstate *gstate, pdf_resources *rdb, fz_obj *extgstate)
+{
+ char name[64];
+ int i, k;
+
+ for (i = 0; i < fz_dictlen(extgstate); i++)
+ {
+ fz_obj *key = fz_dictgetkey(extgstate, i);
+ fz_obj *val = fz_dictgetval(extgstate, i);
+ char *s = fz_toname(key);
+
+ if (!strcmp(s, "Font"))
+ {
+ if (fz_isarray(val) && fz_arraylen(val) == 2)
+ {
+ fz_obj *ref, *obj;
+ ref = fz_arrayget(val, 0);
+ sprintf(name, "$f.%d.%d", fz_toobjid(ref), fz_togenid(ref));
+ obj = fz_dictgets(rdb->font, name);
+ if (!obj)
+ return fz_throw("syntaxerror: missing resource");
+ gstate->font = fz_topointer(obj);
+ gstate->size = fz_toreal(fz_arrayget(val, 1));
+ }
+ else
+ return fz_throw("syntaxerror in ExtGState/Font");
+ }
+
+ else if (!strcmp(s, "LW"))
+ gstate->linewidth = fz_toreal(val);
+ else if (!strcmp(s, "LC"))
+ gstate->linecap = fz_toint(val);
+ else if (!strcmp(s, "LJ"))
+ gstate->linejoin = fz_toint(val);
+ else if (!strcmp(s, "ML"))
+ gstate->miterlimit = fz_toreal(val);
+
+ else if (!strcmp(s, "D"))
+ {
+ if (fz_isarray(val) && fz_arraylen(val) == 2)
+ {
+ fz_obj *dashes = fz_arrayget(val, 0);
+ gstate->dashlen = MAX(fz_arraylen(dashes), 32);
+ for (k = 0; k < gstate->dashlen; k++)
+ gstate->dashlist[k] = fz_toreal(fz_arrayget(dashes, k));
+ gstate->dashphase = fz_toreal(fz_arrayget(val, 1));
+ }
+ else
+ return fz_throw("syntaxerror in ExtGState/D");
+ }
+ }
+
+ return nil;
+}
+
+static fz_error *
+runkeyword(pdf_csi *csi, pdf_resources *rdb, char *buf)
+{
+ pdf_gstate *gstate = csi->gstate + csi->gtop;
+ fz_error *error;
+ float a, b, c, d, e, f;
+ float x, y, w, h;
+ fz_matrix m;
+
+ if (strlen(buf) > 1)
+ {
+ if (!strcmp(buf, "BX"))
+ {
+ if (csi->top != 0)
+ goto syntaxerror;
+ csi->xbalance ++;
+ }
+
+ else if (!strcmp(buf, "EX"))
+ {
+ if (csi->top != 0)
+ goto syntaxerror;
+ csi->xbalance --;
+ }
+
+ else if (!strcmp(buf, "MP"))
+ {
+ fz_node *meta;
+ if (csi->top != 1)
+ goto syntaxerror;
+ error = fz_newmeta(&meta, csi->stack[0]);
+ if (error) return error;
+ fz_insertnode(gstate->head, meta);
+ }
+
+ else if (!strcmp(buf, "DP"))
+ {
+ fz_node *meta;
+ fz_obj *info;
+ if (csi->top != 2)
+ goto syntaxerror;
+ error = fz_packobj(&info, "<< %o %o >>",
+ csi->stack[0], csi->stack[1]);
+ if (error) return error;
+ error = fz_newmeta(&meta, info);
+ fz_dropobj(info);
+ if (error) return error;
+ fz_insertnode(gstate->head, meta);
+ }
+
+ else if (!strcmp(buf, "cm"))
+ {
+ fz_matrix m;
+ fz_node *affine;
+
+ if (csi->top != 6)
+ goto syntaxerror;
+
+ m.a = fz_toreal(csi->stack[0]);
+ m.b = fz_toreal(csi->stack[1]);
+ m.c = fz_toreal(csi->stack[2]);
+ m.d = fz_toreal(csi->stack[3]);
+ m.e = fz_toreal(csi->stack[4]);
+ m.f = fz_toreal(csi->stack[5]);
+
+ error = fz_newtransform(&affine, m);
+ if (error) return error;
+ error = pdf_addtransform(gstate, affine);
+ if (error) return error;
+ }
+
+ else if (!strcmp(buf, "ri"))
+ {
+ if (csi->top != 1)
+ goto syntaxerror;
+ }
+
+ else if (!strcmp(buf, "gs"))
+ {
+ fz_obj *obj;
+
+ if (csi->top != 1)
+ goto syntaxerror;
+
+ obj = fz_dictget(rdb->extgstate, csi->stack[0]);
+ if (!obj)
+ return fz_throw("syntaxerror: missing resource");
+
+ runextgstate(gstate, rdb, obj);
+ }
+
+ else if (!strcmp(buf, "re"))
+ {
+ if (csi->top != 4)
+ goto syntaxerror;
+ x = fz_toreal(csi->stack[0]);
+ y = fz_toreal(csi->stack[1]);
+ w = fz_toreal(csi->stack[2]);
+ h = fz_toreal(csi->stack[3]);
+ error = fz_moveto(csi->path, x, y);
+ if (error) return error;
+ error = fz_lineto(csi->path, x + w, y);
+ if (error) return error;
+ error = fz_lineto(csi->path, x + w, y + h);
+ if (error) return error;
+ error = fz_lineto(csi->path, x, y + h);
+ if (error) return error;
+ error = fz_closepath(csi->path);
+ if (error) return error;
+ }
+
+ else if (!strcmp(buf, "f*"))
+ {
+ if (csi->top != 0)
+ goto syntaxerror;
+ error = pdf_showpath(csi, 0, 1, 0, 1);
+ if (error) return error;
+ }
+
+ else if (!strcmp(buf, "B*"))
+ {
+ if (csi->top != 0)
+ goto syntaxerror;
+ error = pdf_showpath(csi, 0, 1, 1, 1);
+ if (error) return error;
+ }
+
+ else if (!strcmp(buf, "b*"))
+ {
+ if (csi->top != 0)
+ goto syntaxerror;
+ error = pdf_showpath(csi, 1, 1, 1, 1);
+ if (error) return error;
+ }
+
+ else if (!strcmp(buf, "W*"))
+ {
+ if (csi->top != 0)
+ goto syntaxerror;
+ error = fz_clonepath(&csi->clip, csi->path);
+ if (error) return error;
+ error = fz_endpath(csi->clip, FZ_EOFILL, nil, nil);
+ if (error) return error;
+ }
+
+ else if (!strcmp(buf, "rg"))
+ {
+ if (csi->top != 3)
+ goto syntaxerror;
+ gstate->fill.r = fz_toreal(csi->stack[0]);
+ gstate->fill.g = fz_toreal(csi->stack[1]);
+ gstate->fill.b = fz_toreal(csi->stack[2]);
+ }
+
+ else if (!strcmp(buf, "RG"))
+ {
+ if (csi->top != 3)
+ goto syntaxerror;
+ gstate->stroke.r = fz_toreal(csi->stack[0]);
+ gstate->stroke.g = fz_toreal(csi->stack[1]);
+ gstate->stroke.b = fz_toreal(csi->stack[2]);
+ }
+
+ else if (!strcmp(buf, "BT"))
+ {
+ if (csi->top != 0)
+ goto syntaxerror;
+ csi->tm = fz_identity();
+ csi->tlm = fz_identity();
+ }
+
+ else if (!strcmp(buf, "ET"))
+ {
+ if (csi->top != 0)
+ goto syntaxerror;
+ error = pdf_flushtext(csi);
+ if (error)
+ return error;
+ }
+
+ else if (!strcmp(buf, "Tc"))
+ {
+ if (csi->top != 1)
+ goto syntaxerror;
+ gstate->charspace = fz_toreal(csi->stack[0]);
+ }
+
+ else if (!strcmp(buf, "Tw"))
+ {
+ if (csi->top != 1)
+ goto syntaxerror;
+ gstate->wordspace = fz_toreal(csi->stack[0]);
+ }
+
+ else if (!strcmp(buf, "Tz"))
+ {
+ if (csi->top != 1)
+ goto syntaxerror;
+
+ error = pdf_flushtext(csi);
+ if (error) return error;
+
+ gstate->scale = fz_toreal(csi->stack[0]) / 100.0;
+ }
+
+ else if (!strcmp(buf, "TL"))
+ {
+ if (csi->top != 1)
+ goto syntaxerror;
+ gstate->leading = fz_toreal(csi->stack[0]);
+ }
+
+ else if (!strcmp(buf, "Tf"))
+ {
+ fz_obj *obj;
+
+ if (csi->top != 2)
+ goto syntaxerror;
+
+ obj = fz_dictget(rdb->font, csi->stack[0]);
+ if (!obj)
+ return fz_throw("syntaxerror: missing resource");
+
+ gstate->font = fz_topointer(obj);
+ gstate->size = fz_toreal(csi->stack[1]);
+ }
+
+ else if (!strcmp(buf, "Tr"))
+ {
+ if (csi->top != 1)
+ goto syntaxerror;
+ gstate->render = fz_toint(csi->stack[0]);
+ }
+
+ else if (!strcmp(buf, "Ts"))
+ {
+ if (csi->top != 1)
+ goto syntaxerror;
+ gstate->rise = fz_toreal(csi->stack[0]);
+ }
+
+ else if (!strcmp(buf, "Td"))
+ {
+ if (csi->top != 2)
+ goto syntaxerror;
+ m = fz_translate(fz_toreal(csi->stack[0]), fz_toreal(csi->stack[1]));
+ csi->tlm = fz_concat(m, csi->tlm);
+ csi->tm = csi->tlm;
+ }
+
+ else if (!strcmp(buf, "TD"))
+ {
+ if (csi->top != 2)
+ goto syntaxerror;
+ gstate->leading = -fz_toreal(csi->stack[1]);
+ m = fz_translate(fz_toreal(csi->stack[0]), fz_toreal(csi->stack[1]));
+ csi->tlm = fz_concat(m, csi->tlm);
+ csi->tm = csi->tlm;
+ }
+
+ else if (!strcmp(buf, "Tm"))
+ {
+ if (csi->top != 6)
+ goto syntaxerror;
+
+ error = pdf_flushtext(csi);
+ if (error) return error;
+
+ csi->tm.a = fz_toreal(csi->stack[0]);
+ csi->tm.b = fz_toreal(csi->stack[1]);
+ csi->tm.c = fz_toreal(csi->stack[2]);
+ csi->tm.d = fz_toreal(csi->stack[3]);
+ csi->tm.e = fz_toreal(csi->stack[4]);
+ csi->tm.f = fz_toreal(csi->stack[5]);
+ csi->tlm = csi->tm;
+ }
+
+ else if (!strcmp(buf, "T*"))
+ {
+ if (csi->top != 0)
+ goto syntaxerror;
+ m = fz_translate(0, -gstate->leading);
+ csi->tlm = fz_concat(m, csi->tlm);
+ csi->tm = csi->tlm;
+ }
+
+ else if (!strcmp(buf, "Tj"))
+ {
+ if (csi->top != 1)
+ goto syntaxerror;
+ error = pdf_showtext(csi, csi->stack[0]);
+ if (error) return error;
+ }
+
+ else if (!strcmp(buf, "TJ"))
+ {
+ if (csi->top != 1)
+ goto syntaxerror;
+ error = pdf_showtext(csi, csi->stack[0]);
+ if (error) return error;
+ }
+
+ else
+fprintf(stderr, "syntaxerror: unknown keyword '%s'\n", buf);
+ //return fz_throw("syntaxerror: unknown keyword '%s'", buf);
+ //if (!csi->xbalance) goto syntaxerror;
+ }
+
+ else switch (buf[0])
+ {
+
+ case 'q':
+ if (csi->top != 0)
+ goto syntaxerror;
+ if (csi->gtop == 31)
+ return fz_throw("gstate overflow in content stream");
+ memcpy(&csi->gstate[csi->gtop + 1],
+ &csi->gstate[csi->gtop],
+ sizeof (pdf_gstate));
+ csi->gtop ++;
+ break;
+
+ case 'Q':
+ if (csi->top != 0)
+ goto syntaxerror;
+ if (csi->gtop == 0)
+ return fz_throw("gstate underflow in content stream");
+ csi->gtop --;
+ break;
+
+ case 'w':
+ if (csi->top != 1)
+ goto syntaxerror;
+ gstate->linewidth = fz_toreal(csi->stack[0]);
+ break;
+
+ case 'J':
+ if (csi->top != 1)
+ goto syntaxerror;
+ gstate->linecap = fz_toint(csi->stack[0]);
+ break;
+
+ case 'j':
+ if (csi->top != 1)
+ goto syntaxerror;
+ gstate->linejoin = fz_toint(csi->stack[0]);
+ break;
+
+ case 'M':
+ if (csi->top != 1)
+ goto syntaxerror;
+ gstate->miterlimit = fz_toreal(csi->stack[0]);
+ break;
+
+ case 'd':
+ if (csi->top != 2)
+ goto syntaxerror;
+ {
+ int i;
+ fz_obj *array = csi->stack[0];
+ gstate->dashlen = fz_arraylen(array);
+ if (gstate->dashlen > 32)
+ return fz_throw("rangecheck: too large dash pattern");
+ for (i = 0; i < gstate->dashlen; i++)
+ gstate->dashlist[i] = fz_toreal(fz_arrayget(array, i));
+ gstate->dashphase = fz_toreal(csi->stack[1]);
+ }
+ break;
+
+ case 'i':
+ if (csi->top != 1)
+ goto syntaxerror;
+ /* flatness */
+ break;
+
+ case 'm':
+ if (csi->top != 2)
+ goto syntaxerror;
+ a = fz_toreal(csi->stack[0]);
+ b = fz_toreal(csi->stack[1]);
+ return fz_moveto(csi->path, a, b);
+
+ case 'l':
+ if (csi->top != 2)
+ goto syntaxerror;
+ a = fz_toreal(csi->stack[0]);
+ b = fz_toreal(csi->stack[1]);
+ return fz_lineto(csi->path, a, b);
+
+ case 'c':
+ if (csi->top != 6)
+ goto syntaxerror;
+ a = fz_toreal(csi->stack[0]);
+ b = fz_toreal(csi->stack[1]);
+ c = fz_toreal(csi->stack[2]);
+ d = fz_toreal(csi->stack[3]);
+ e = fz_toreal(csi->stack[4]);
+ f = fz_toreal(csi->stack[5]);
+ return fz_curveto(csi->path, a, b, c, d, e, f);
+
+ case 'v':
+ if (csi->top != 4)
+ goto syntaxerror;
+ a = fz_toreal(csi->stack[0]);
+ b = fz_toreal(csi->stack[1]);
+ c = fz_toreal(csi->stack[2]);
+ d = fz_toreal(csi->stack[3]);
+ return fz_curvetov(csi->path, a, b, c, d);
+
+ case 'y':
+ if (csi->top != 4)
+ goto syntaxerror;
+ a = fz_toreal(csi->stack[0]);
+ b = fz_toreal(csi->stack[1]);
+ c = fz_toreal(csi->stack[2]);
+ d = fz_toreal(csi->stack[3]);
+ return fz_curvetoy(csi->path, a, b, c, d);
+
+ case 'h':
+ if (csi->top != 0)
+ goto syntaxerror;
+ return fz_closepath(csi->path);
+
+ case 'S':
+ if (csi->top != 0)
+ goto syntaxerror;
+ error = pdf_showpath(csi, 0, 0, 1, 0);
+ if (error) return error;
+ break;
+
+ case 's':
+ if (csi->top != 0)
+ goto syntaxerror;
+ error = pdf_showpath(csi, 1, 0, 1, 0);
+ if (error) return error;
+ break;
+
+ case 'F':
+ case 'f':
+ if (csi->top != 0)
+ goto syntaxerror;
+ error = pdf_showpath(csi, 0, 1, 0, 0);
+ if (error) return error;
+ break;
+
+ case 'B':
+ if (csi->top != 0)
+ goto syntaxerror;
+ error = pdf_showpath(csi, 0, 1, 1, 0);
+ if (error) return error;
+ break;
+
+ case 'b':
+ if (csi->top != 0)
+ goto syntaxerror;
+ error = pdf_showpath(csi, 1, 1, 1, 0);
+ if (error) return error;
+ break;
+
+ case 'n':
+ if (csi->top != 0)
+ goto syntaxerror;
+ error = pdf_showpath(csi, 0, 0, 0, 0);
+ if (error) return error;
+ break;
+
+ case 'W':
+ if (csi->top != 0)
+ goto syntaxerror;
+ error = fz_clonepath(&csi->clip, csi->path);
+ if (error) return error;
+ error = fz_endpath(csi->clip, FZ_FILL, nil, nil);
+ if (error) return error;
+ break;
+
+ case 'g':
+ if (csi->top != 1)
+ goto syntaxerror;
+ a = fz_toreal(csi->stack[0]);
+ gstate->fill.r = a;
+ gstate->fill.g = a;
+ gstate->fill.b = a;
+ break;
+
+ case 'G':
+ if (csi->top != 1)
+ goto syntaxerror;
+ a = fz_toreal(csi->stack[0]);
+ gstate->stroke.r = a;
+ gstate->stroke.g = a;
+ gstate->stroke.b = a;
+ break;
+
+ case 'k':
+ if (csi->top != 4)
+ goto syntaxerror;
+ a = fz_toreal(csi->stack[0]);
+ b = fz_toreal(csi->stack[1]);
+ c = fz_toreal(csi->stack[2]);
+ d = fz_toreal(csi->stack[3]);
+ gstate->fill.r = 1.0 - MIN(1.0, a + d);
+ gstate->fill.g = 1.0 - MIN(1.0, b + d);
+ gstate->fill.b = 1.0 - MIN(1.0, c + d);
+ break;
+
+ case 'K':
+ if (csi->top != 4)
+ goto syntaxerror;
+ a = fz_toreal(csi->stack[0]);
+ b = fz_toreal(csi->stack[1]);
+ c = fz_toreal(csi->stack[2]);
+ d = fz_toreal(csi->stack[3]);
+ gstate->stroke.r = 1.0 - MIN(1.0, a + d);
+ gstate->stroke.g = 1.0 - MIN(1.0, b + d);
+ gstate->stroke.b = 1.0 - MIN(1.0, c + d);
+ break;
+
+ case '\'':
+ if (csi->top != 1)
+ goto syntaxerror;
+
+ m = fz_translate(0, -gstate->leading);
+ csi->tlm = fz_concat(m, csi->tlm);
+ csi->tm = csi->tlm;
+
+ error = pdf_showtext(csi, csi->stack[0]);
+ if (error) return error;
+ break;
+
+ case '"':
+ if (csi->top != 3)
+ goto syntaxerror;
+
+ gstate->wordspace = fz_toreal(csi->stack[0]);
+ gstate->charspace = fz_toreal(csi->stack[1]);
+
+ m = fz_translate(0, -gstate->leading);
+ csi->tlm = fz_concat(m, csi->tlm);
+ csi->tm = csi->tlm;
+
+ error = pdf_showtext(csi, csi->stack[2]);
+ if (error) return error;
+ break;
+
+ default:
+fprintf(stderr, "syntaxerror: unknown keyword '%s'\n", buf);
+ //return fz_throw("syntaxerror: unknown keyword '%s'", buf);
+ //if (!csi->xbalance) goto syntaxerror;
+ }
+
+ return nil;
+
+syntaxerror:
+ return fz_throw("syntaxerror in content stream: '%s'", buf);
+}
+
+fz_error *
+pdf_runcsi(pdf_csi *csi, pdf_resources *rdb, fz_file *file)
+{
+ fz_error *error;
+ unsigned char buf[65536];
+ int token, len;
+
+ while (1)
+ {
+ if (csi->top == 31)
+ return fz_throw("stack overflow in content stream");
+
+ token = pdf_lex(file, buf, sizeof buf, &len);
+
+ switch (token)
+ {
+ case PDF_TEOF:
+ return nil;
+
+ /* FIXME: need to make array parsing be able to span files for
+ those stupid pdf files that split TJ arrays across content
+ streams...
+ */
+ case PDF_TOARRAY:
+ error = pdf_parsearray(&csi->stack[csi->top], file, buf, sizeof buf);
+ if (error) return error;
+ csi->top ++;
+ break;
+
+ /* drop down to normal pdf object parsing for dictionaries,
+ and pray that they are not split in the middle with the beginning
+ and end in different streams
+ */
+ case PDF_TODICT:
+ error = pdf_parsedict(&csi->stack[csi->top], file, buf, sizeof buf);
+ if (error) return error;
+ csi->top ++;
+ break;
+
+ case PDF_TNAME:
+ error = fz_newname(&csi->stack[csi->top], buf);
+ if (error) return error;
+ csi->top ++;
+ break;
+
+ case PDF_TINT:
+ error = fz_newint(&csi->stack[csi->top], atoi(buf));
+ if (error) return error;
+ csi->top ++;
+ break;
+
+ case PDF_TREAL:
+ error = fz_newreal(&csi->stack[csi->top], atof(buf));
+ if (error) return error;
+ csi->top ++;
+ break;
+
+ case PDF_TSTRING:
+ error = fz_newstring(&csi->stack[csi->top], buf, len);
+ if (error) return error;
+ csi->top ++;
+ break;
+
+ case PDF_TTRUE:
+ error = fz_newbool(&csi->stack[csi->top], 1);
+ if (error) return error;
+ csi->top ++;
+ break;
+
+ case PDF_TFALSE:
+ error = fz_newbool(&csi->stack[csi->top], 0);
+ if (error) return error;
+ csi->top ++;
+ break;
+
+ case PDF_TNULL:
+ error = fz_newnull(&csi->stack[csi->top]);
+ if (error) return error;
+ csi->top ++;
+ break;
+
+ case PDF_TKEYWORD:
+ error = runkeyword(csi, rdb, buf);
+ if (error) return error;
+ clearstack(csi);
+ break;
+
+ default:
+ clearstack(csi);
+ return fz_throw("syntaxerror in content stream");
+ }
+ }
+}
+
diff --git a/mupdf/lex.c b/mupdf/lex.c
new file mode 100644
index 00000000..d8ec7d75
--- /dev/null
+++ b/mupdf/lex.c
@@ -0,0 +1,342 @@
+#include <fitz.h>
+#include <mupdf.h>
+
+static inline int iswhite(int ch)
+{
+ return ch == '\000' ||
+ ch == '\011' ||
+ ch == '\012' ||
+ ch == '\014' ||
+ ch == '\015' ||
+ ch == '\040';
+}
+
+static inline int isdelim(int ch)
+{
+ return ch == '(' || ch == ')' ||
+ ch == '<' || ch == '>' ||
+ ch == '[' || ch == ']' ||
+ ch == '{' || ch == '}' ||
+ ch == '/' ||
+ ch == '%';
+}
+
+static inline int isregular(int ch)
+{
+ return !isdelim(ch) && !iswhite(ch) && ch != EOF;
+}
+
+static inline int isnumber(int ch)
+{
+ return ch == '+' || ch == '-' || ch == '.' || (ch >= '0' && ch <= '9');
+}
+
+static inline int ishex(int ch)
+{
+ return (ch >= '0' && ch <= '9') ||
+ (ch >= 'A' && ch <= 'F') ||
+ (ch >= 'a' && ch <= 'f');
+}
+
+static inline int fromhex(int ch)
+{
+ if (ch >= '0' && ch <= '9')
+ return ch - '0';
+ else if (ch >= 'A' && ch <= 'F')
+ return ch - 'A' + 0xA;
+ else if (ch >= 'a' && ch <= 'f')
+ return ch - 'a' + 0xA;
+ return 0;
+}
+
+static void
+lexwhite(fz_file *f)
+{
+ int c;
+ while (1)
+ {
+ c = fz_peekbyte(f);
+ if (!iswhite(c))
+ break;
+ fz_readbyte(f);
+ }
+}
+
+static void
+lexcomment(fz_file *f)
+{
+ int c;
+ while (1)
+ {
+ c = fz_readbyte(f);
+ if (c == '\012') break;
+ if (c == '\015') break;
+ if (c == EOF) break;
+ }
+}
+
+static void
+lexnumber(fz_file *f, unsigned char *s, int n)
+{
+ while (n > 1)
+ {
+ if (!isnumber(fz_peekbyte(f)))
+ break;
+ *s++ = fz_readbyte(f);
+ n--;
+ }
+ *s = '\0';
+}
+
+static void
+lexname(fz_file *f, unsigned char *s, int n)
+{
+#if 0
+ unsigned char *p = s;
+ unsigned char *q = s;
+#endif
+
+ while (n > 1)
+ {
+ if (!isregular(fz_peekbyte(f)))
+ break;
+ *s++ = fz_readbyte(f);
+ n--;
+ }
+ *s = '\0';
+
+#if 0
+ while (*p)
+ {
+ if (p[0] == '#' && p[1] != 0 && p[2] != 0)
+ {
+ *q++ = fromhex(p[1]) * 16 + fromhex(p[2]);
+ p += 3;
+ }
+ else
+ *q++ = *p++;
+ }
+ *q = '\0';
+#endif
+}
+
+static int
+lexstring(fz_file *f, unsigned char *buf, int n)
+{
+ unsigned char *s = buf;
+ unsigned char *e = buf + n;
+ int bal = 1;
+ int oct;
+ int c;
+
+ while (s < e)
+ {
+ c = fz_readbyte(f);
+ if (c == '(')
+ {
+ bal++;
+ *s++ = c;
+ }
+ else if (c == ')')
+ {
+ bal --;
+ if (bal == 0)
+ break;
+ *s++ = c;
+ }
+ else if (c == '\\')
+ {
+ c = fz_readbyte(f);
+ if (c == 'n') *s++ = '\n';
+ else if (c == 'r') *s++ = '\r';
+ else if (c == 't') *s++ = '\t';
+ else if (c == 'b') *s++ = '\b';
+ else if (c == 'f') *s++ = '\f';
+ else if (c == '(') *s++ = '(';
+ else if (c == ')') *s++ = ')';
+ else if (c == '\\') *s++ = '\\';
+
+ else if (c >= '0' && c <= '9')
+ {
+ oct = c - '0';
+ c = fz_peekbyte(f);
+ if (c >= '0' && c <= '9')
+ {
+ fz_readbyte(f);
+ oct = oct * 8 + (c - '0');
+ c = fz_peekbyte(f);
+ if (c >= '0' && c <= '9')
+ {
+ fz_readbyte(f);
+ oct = oct * 8 + (c - '0');
+ }
+ }
+ *s++ = oct;
+ }
+
+ else if (c == '\n')
+ ;
+ else if (c == '\r')
+ {
+ c = fz_peekbyte(f);
+ if (c == '\n')
+ fz_readbyte(f);
+ }
+ else *s++ = c;
+ }
+ else
+ {
+ *s++ = c;
+ }
+ }
+
+ return s - buf;
+}
+
+static int
+lexhexstring(fz_file *f, unsigned char *buf, int n)
+{
+ unsigned char *s = buf;
+ unsigned char *e = buf + n;
+ int a = 0, x = 0;
+ int c;
+
+ while (s < e)
+ {
+ c = fz_readbyte(f);
+ if (c == '>')
+ break;
+ else if (iswhite(c))
+ continue;
+ else if (ishex(c))
+ {
+ if (x)
+ {
+ *s++ = a * 16 + fromhex(c);
+ x = !x;
+ }
+ else
+ {
+ a = fromhex(c);
+ x = !x;
+ }
+ }
+ else
+ break;
+ }
+
+ return s - buf;
+}
+
+static int
+tokenfromkeyword(unsigned char *key)
+{
+ if (!strcmp(key, "R")) return PDF_TR;
+ if (!strcmp(key, "true")) return PDF_TTRUE;
+ if (!strcmp(key, "false")) return PDF_TFALSE;
+ if (!strcmp(key, "null")) return PDF_TNULL;
+
+ if (!strcmp(key, "obj")) return PDF_TOBJ;
+ if (!strcmp(key, "endobj")) return PDF_TENDOBJ;
+ if (!strcmp(key, "stream")) return PDF_TSTREAM;
+ if (!strcmp(key, "endstream")) return PDF_TENDSTREAM;
+
+ if (!strcmp(key, "xref")) return PDF_TXREF;
+ if (!strcmp(key, "trailer")) return PDF_TTRAILER;
+ if (!strcmp(key, "startxref")) return PDF_TSTARTXREF;
+
+ return PDF_TKEYWORD;
+}
+
+int
+pdf_lex(fz_file *f, unsigned char *buf, int n, int *sl)
+{
+ int c;
+
+ while (1)
+ {
+ c = fz_peekbyte(f);
+
+ if (c == EOF)
+ return PDF_TEOF;
+
+ else if (iswhite(c))
+ lexwhite(f);
+
+ else if (c == '%')
+ lexcomment(f);
+
+
+ else if (c == '/')
+ {
+ fz_readbyte(f);
+ lexname(f, buf, n);
+ *sl = strlen(buf);
+ return PDF_TNAME;
+ }
+
+ else if (c == '(')
+ {
+ fz_readbyte(f);
+ *sl = lexstring(f, buf, n);
+ return PDF_TSTRING;
+ }
+
+ else if (c == '<')
+ {
+ fz_readbyte(f);
+ c = fz_peekbyte(f);
+ if (c == '<')
+ {
+ fz_readbyte(f);
+ return PDF_TODICT;
+ }
+ else
+ {
+ *sl = lexhexstring(f, buf, n);
+ return PDF_TSTRING;
+ }
+ }
+
+ else if (c == '>')
+ {
+ fz_readbyte(f);
+ c = fz_readbyte(f);
+ if (c == '>')
+ return PDF_TCDICT;
+ return PDF_TERROR;
+ }
+
+ else if (c == '[')
+ {
+ fz_readbyte(f);
+ return PDF_TOARRAY;
+ }
+
+ else if (c == ']')
+ {
+ fz_readbyte(f);
+ return PDF_TCARRAY;
+ }
+
+ else if (isnumber(c))
+ {
+ lexnumber(f, buf, n);
+ *sl = strlen(buf);
+ if (strchr(buf, '.'))
+ return PDF_TREAL;
+ return PDF_TINT;
+ }
+
+ else if (isregular(c))
+ {
+ lexname(f, buf, n);
+ *sl = strlen(buf);
+ return tokenfromkeyword(buf);
+ }
+
+ else
+ return PDF_TERROR;
+ }
+}
+
diff --git a/mupdf/open.c b/mupdf/open.c
new file mode 100644
index 00000000..016cebf4
--- /dev/null
+++ b/mupdf/open.c
@@ -0,0 +1,492 @@
+#include <fitz.h>
+#include <mupdf.h>
+
+static inline int iswhite(int ch)
+{
+ return ch == '\000' || ch == '\011' || ch == '\012' ||
+ ch == '\014' || ch == '\015' || ch == '\040';
+}
+
+/*
+ * magic version tag and startxref
+ */
+
+static fz_error *
+loadversion(float *version, fz_file *file)
+{
+ char buf[20];
+ int n;
+
+ n = fz_seek(file, 0);
+ if (n < 0)
+ return fz_ferror(file);
+
+ fz_readline(file, buf, sizeof buf);
+ if (memcmp(buf, "%PDF-", 5) != 0)
+ return fz_throw("syntaxerror: missing magic pdf marker");
+
+ *version = atof(buf + 5);
+
+ return nil;
+}
+
+static fz_error *
+readstartxref(int *ofs, int fd)
+{
+ unsigned char buf[1024];
+ int t, n;
+ int i;
+
+ t = lseek(fd, 0, 2);
+ if (t == -1)
+ return fz_throw("ioerror in startxref: lseek: %s", strerror(errno));
+
+ t = lseek(fd, MAX(0, t - ((int)sizeof buf)), 0);
+ if (t == -1)
+ return fz_throw("ioerror in startxref: lseek: %s", strerror(errno));
+
+ n = read(fd, buf, sizeof buf);
+ if (n == -1)
+ return fz_throw("ioerror in startxref: read: %s", strerror(errno));
+
+ for (i = n - 9; i >= 0; i--) {
+ if (memcmp(buf + i, "startxref", 9) == 0) {
+ i += 9;
+ while (iswhite(buf[i]) && i < n)
+ i ++;
+ *ofs = atoi(buf + i);
+ return nil;
+ }
+ }
+
+ return fz_throw("syntaxerror: missing startxref");
+}
+
+/*
+ * trailer dictionary
+ */
+
+static fz_error *
+readoldtrailer(fz_obj **objp, fz_file *file, unsigned char *buf, int cap)
+{
+ int ofs, len;
+ char *s;
+ int n;
+ int t;
+ int c;
+
+ fz_readline(file, buf, cap);
+ if (strcmp(buf, "xref") != 0)
+ return fz_throw("syntaxerror: missing xref");
+
+ while (1)
+ {
+ c = fz_peekbyte(file);
+ if (!(c >= '0' && c <= '9'))
+ break;
+
+ n = fz_readline(file, buf, cap);
+ if (n < 0) return fz_ferror(file);
+
+ s = buf;
+ ofs = atoi(strsep(&s, " "));
+ len = atoi(strsep(&s, " "));
+
+ t = fz_tell(file);
+ if (t < 0) return fz_ferror(file);
+
+ n = fz_seek(file, t + 20 * len);
+ if (n < 0) return fz_ferror(file);
+ }
+
+ t = pdf_lex(file, buf, cap, &n);
+ if (t != PDF_TTRAILER)
+ return fz_throw("syntaxerror: missing trailer");
+
+ t = pdf_lex(file, buf, cap, &n);
+ if (t != PDF_TODICT)
+ return fz_throw("syntaxerror: trailer must be dictionary");
+
+ return pdf_parsedict(objp, file, buf, cap);
+}
+
+static fz_error *
+readnewtrailer(fz_obj **objp, fz_file *file, unsigned char *buf, int cap)
+{
+ return pdf_parseindobj(objp, file, buf, cap, nil, nil, nil);
+}
+
+static fz_error *
+readtrailer(fz_obj **objp, fz_file *file, int ofs, unsigned char *buf, int cap)
+{
+ int n;
+ int c;
+
+ n = fz_seek(file, ofs);
+ if (n < 0)
+ return fz_ferror(file);
+
+ c = fz_peekbyte(file);
+ if (c == 'x')
+ return readoldtrailer(objp, file, buf, cap);
+ else if (c >= '0' && c <= '9')
+ return readnewtrailer(objp, file, buf, cap);
+
+ return fz_throw("syntaxerror: missing xref");
+}
+
+/*
+ * xref tables
+ */
+
+static fz_error *
+readoldxref(fz_obj **trailerp, pdf_xref *xref, unsigned char *buf, int cap)
+{
+ int ofs, len;
+ char *s;
+ int n;
+ int t;
+ int i;
+ int c;
+
+ fz_readline(xref->file, buf, cap);
+ if (strcmp(buf, "xref") != 0)
+ return fz_throw("syntaxerror: missing xref");
+
+ while (1)
+ {
+ c = fz_peekbyte(xref->file);
+ if (!(c >= '0' && c <= '9'))
+ break;
+
+ n = fz_readline(xref->file, buf, cap);
+ if (n < 0) return fz_ferror(xref->file);
+
+ s = buf;
+ ofs = atoi(strsep(&s, " "));
+ len = atoi(strsep(&s, " "));
+
+ for (i = 0; i < len; i++)
+ {
+ n = fz_read(xref->file, buf, 20);
+ if (n < 0) return fz_ferror(xref->file);
+ if (n != 20) return fz_throw("syntaxerror: truncated xref table");
+ if (!xref->table[ofs + i].type)
+ {
+ s = buf;
+ xref->table[ofs + i].ofs = atoi(strsep(&s, " "));
+ xref->table[ofs + i].gen = atoi(strsep(&s, " "));
+ xref->table[ofs + i].type = strsep(&s, " ")[0];
+ }
+ }
+ }
+
+ t = pdf_lex(xref->file, buf, cap, &n);
+ if (t != PDF_TTRAILER)
+ return fz_throw("syntaxerror: missing trailer");
+ t = pdf_lex(xref->file, buf, cap, &n);
+ if (t != PDF_TODICT)
+ return fz_throw("syntaxerror: trailer must be dictionary");
+
+ return pdf_parsedict(trailerp, xref->file, buf, cap);
+}
+
+static fz_error *
+readnewxref(fz_obj **trailerp, pdf_xref *xref, unsigned char *buf, int cap)
+{
+ fz_error *error;
+ fz_obj *trailer;
+ fz_obj *obj;
+ int oid, gid, stmofs;
+ int size, w0, w1, w2, i0, i1;
+ int i, n;
+
+ error = pdf_parseindobj(&trailer, xref->file, buf, cap, &oid, &gid, &stmofs);
+ if (error)
+ return error;
+
+ obj = fz_dictgets(trailer, "Size");
+ if (!obj) {
+ error = fz_throw("syntaxerror: xref stream missing Size entry");
+ goto cleanup;
+ }
+ size = fz_toint(obj);
+
+ obj = fz_dictgets(trailer, "W");
+ if (!obj) {
+ error = fz_throw("syntaxerror: xref stream missing W entry");
+ goto cleanup;
+ }
+ w0 = fz_toint(fz_arrayget(obj, 0));
+ w1 = fz_toint(fz_arrayget(obj, 1));
+ w2 = fz_toint(fz_arrayget(obj, 2));
+
+ obj = fz_dictgets(trailer, "Index");
+ if (obj) {
+ i0 = fz_toint(fz_arrayget(obj, 0));
+ i1 = fz_toint(fz_arrayget(obj, 1));
+ }
+ else {
+ i0 = 0;
+ i1 = size;
+ }
+
+ if (i0 < 0 || i1 > xref->size) {
+ error = fz_throw("syntaxerror: xref stream has too many entries");
+ goto cleanup;
+ }
+
+ error = pdf_openstream0(xref, trailer, oid, gid, stmofs);
+ if (error)
+ goto cleanup;
+
+ for (i = i0; i < i0 + i1; i++)
+ {
+ int a = 0;
+ int b = 0;
+ int c = 0;
+
+ if (fz_peekbyte(xref->file) == EOF)
+ {
+ error = fz_ferror(xref->file);
+ if (!error)
+ error = fz_throw("syntaxerror: truncated xref stream");
+ pdf_closestream(xref);
+ goto cleanup;
+ }
+
+ for (n = 0; n < w0; n++)
+ a = (a << 8) + fz_readbyte(xref->file);
+ for (n = 0; n < w1; n++)
+ b = (b << 8) + fz_readbyte(xref->file);
+ for (n = 0; n < w2; n++)
+ c = (c << 8) + fz_readbyte(xref->file);
+
+ if (!xref->table[i].type)
+ {
+ int t = w0 ? a : 1;
+ xref->table[i].type = t == 0 ? 'f' : t == 1 ? 'n' : t == 2 ? 'o' : 0;
+ xref->table[i].ofs = w2 ? b : 0;
+ xref->table[i].gen = w1 ? c : 0;
+ }
+ }
+
+ pdf_closestream(xref);
+
+ *trailerp = trailer;
+
+ return nil;
+
+cleanup:
+ fz_dropobj(trailer);
+ return error;
+}
+
+static fz_error *
+readxref(fz_obj **trailerp, pdf_xref *xref, int ofs, unsigned char *buf, int cap)
+{
+ int n;
+ int c;
+
+ n = fz_seek(xref->file, ofs);
+ if (n < 0)
+ return fz_ferror(xref->file);
+
+ c = fz_peekbyte(xref->file);
+ if (c == 'x')
+ return readoldxref(trailerp, xref, buf, cap);
+ else if (c >= '0' && c <= '9')
+ return readnewxref(trailerp, xref, buf, cap);
+
+ return fz_throw("syntaxerror: missing xref");
+}
+
+static fz_error *
+readxrefsections(pdf_xref *xref, int ofs, unsigned char *buf, int cap)
+{
+ fz_error *error;
+ fz_obj *trailer;
+ fz_obj *prev;
+ fz_obj *xrefstm;
+
+ error = readxref(&trailer, xref, ofs, buf, cap);
+ if (error)
+ return error;
+
+ /* FIXME: do we overwrite free entries properly? */
+ xrefstm = fz_dictgets(trailer, "XrefStm");
+ if (xrefstm)
+ {
+ error = readxrefsections(xref, fz_toint(xrefstm), buf, cap);
+ if (error)
+ goto cleanup;
+ }
+
+ prev = fz_dictgets(trailer, "Prev");
+ if (prev)
+ {
+ error = readxrefsections(xref, fz_toint(prev), buf, cap);
+ if (error)
+ goto cleanup;
+ }
+
+ fz_dropobj(trailer);
+ return nil;
+
+cleanup:
+ fz_dropobj(trailer);
+ return error;
+}
+
+/*
+ * compressed object streams
+ */
+
+fz_error *
+pdf_readobjstm(pdf_xref *xref, int oid, int gid, unsigned char *buf, int cap)
+{
+ fz_error *error;
+ fz_obj *objstm;
+ int *oidbuf;
+ int *ofsbuf;
+
+ fz_obj *obj;
+ int stmofs;
+ int first;
+ int count;
+ int i, n, t;
+
+ error = pdf_loadobject0(&objstm, xref, oid, gid, &stmofs);
+ if (error)
+ return error;
+
+ count = fz_toint(fz_dictgets(objstm, "N"));
+ first = fz_toint(fz_dictgets(objstm, "First"));
+
+ oidbuf = fz_malloc(count * sizeof(int));
+ if (!oidbuf) { error = fz_outofmem; goto cleanup1; }
+
+ ofsbuf = fz_malloc(count * sizeof(int));
+ if (!ofsbuf) { error = fz_outofmem; goto cleanup2; }
+
+ error = pdf_openstream0(xref, objstm, oid, gid, stmofs);
+ if (error)
+ goto cleanup3;
+
+ for (i = 0; i < count; i++)
+ {
+ t = pdf_lex(xref->file, buf, cap, &n);
+ if (t != PDF_TINT)
+ {
+ error = fz_throw("syntaxerror: corrupt object stream");
+ goto cleanup4;
+ }
+ oidbuf[i] = atoi(buf);
+
+ t = pdf_lex(xref->file, buf, cap, &n);
+ if (t != PDF_TINT)
+ {
+ error = fz_throw("syntaxerror: corrupt object stream");
+ goto cleanup4;
+ }
+ ofsbuf[i] = atoi(buf);
+ }
+
+ n = fz_seek(xref->file, first);
+ if (n < 0)
+ {
+ error = fz_ferror(xref->file);
+ goto cleanup4;
+ }
+
+ for (i = 0; i < count; i++)
+ {
+ /* FIXME: seek to first + ofsbuf[i] */
+
+ error = pdf_parsestmobj(&obj, xref->file, buf, cap);
+ if (error)
+ goto cleanup4;
+
+ if (oidbuf[i] < 1 || oidbuf[i] >= xref->size)
+ {
+ error = fz_throw("rangecheck: object number out of range");
+ goto cleanup4;
+ }
+
+ error = pdf_storeobject(xref->store, oidbuf[i], 0, obj);
+ if (error)
+ goto cleanup4;
+ }
+
+ pdf_closestream(xref);
+ fz_free(ofsbuf);
+ fz_free(oidbuf);
+ fz_dropobj(objstm);
+ return nil;
+
+cleanup4:
+ pdf_closestream(xref);
+cleanup3:
+ fz_free(ofsbuf);
+cleanup2:
+ fz_free(oidbuf);
+cleanup1:
+ fz_dropobj(objstm);
+ return error;
+}
+
+/*
+ * open xref in normal mode (as opposed to repair mode)
+ */
+
+fz_error *
+pdf_openxref(pdf_xref *xref, char *filename)
+{
+ fz_error *error;
+ fz_obj *size;
+ int i;
+
+ unsigned char buf[65536]; /* yeowch! */
+
+ error = fz_openfile(&xref->file, filename, O_RDONLY);
+ if (error)
+ return error;
+
+ error = loadversion(&xref->version, xref->file);
+ if (error)
+ return error;
+
+ error = readstartxref(&xref->startxref, xref->file->fd);
+ if (error)
+ return error;
+
+ error = readtrailer(&xref->trailer, xref->file, xref->startxref, buf, sizeof buf);
+ if (error)
+ return error;
+
+ size = fz_dictgets(xref->trailer, "Size");
+ if (!size)
+ return fz_throw("syntaxerror: trailer missing Size entry");
+
+ xref->capacity = fz_toint(size);
+ xref->size = fz_toint(size);
+
+ xref->table = fz_malloc(xref->capacity * sizeof(pdf_xrefentry));
+ if (!xref->table)
+ return fz_outofmem;
+
+ for (i = 0; i < xref->size; i++)
+ {
+ xref->table[i].ofs = 0;
+ xref->table[i].gen = 0;
+ xref->table[i].type = 0;
+ xref->table[i].mark = 0;
+ }
+
+ error = readxrefsections(xref, xref->startxref, buf, sizeof buf);
+ if (error)
+ return error;
+
+ return nil;
+}
+
diff --git a/mupdf/pagetree.c b/mupdf/pagetree.c
new file mode 100644
index 00000000..de500f18
--- /dev/null
+++ b/mupdf/pagetree.c
@@ -0,0 +1,174 @@
+#include <fitz.h>
+#include <mupdf.h>
+
+struct stuff
+{
+ fz_obj *resources;
+ fz_obj *mediabox;
+ fz_obj *cropbox;
+ fz_obj *rotate;
+};
+
+static fz_error *
+loadpagetree(pdf_xref *xref, pdf_pagetree *pages,
+ struct stuff inherit, fz_obj *obj, fz_obj *ref)
+{
+ fz_error *err;
+ fz_obj *type;
+ fz_obj *kids;
+ fz_obj *kref, *kobj;
+ fz_obj *inh;
+ int i;
+
+ type = fz_dictgets(obj, "Type");
+
+ if (strcmp(fz_toname(type), "Page") == 0)
+ {
+ if (inherit.resources && !fz_dictgets(obj, "Resources")) {
+ err = fz_dictputs(obj, "Resources", inherit.resources);
+ if (err) return err;
+ }
+
+ if (inherit.mediabox && !fz_dictgets(obj, "MediaBox")) {
+ err = fz_dictputs(obj, "MediaBox", inherit.mediabox);
+ if (err) return err;
+ }
+
+ if (inherit.cropbox && !fz_dictgets(obj, "CropBox")) {
+ err = fz_dictputs(obj, "CropBox", inherit.cropbox);
+ if (err) return err;
+ }
+
+ if (inherit.rotate && !fz_dictgets(obj, "Rotate")) {
+ err = fz_dictputs(obj, "Rotate", inherit.rotate);
+ if (err) return err;
+ }
+
+ pages->pref[pages->cursor] = fz_keepobj(ref);
+ pages->pobj[pages->cursor] = fz_keepobj(obj);
+ pages->cursor ++;
+ }
+
+ else if (strcmp(fz_toname(type), "Pages") == 0)
+ {
+ inh = fz_dictgets(obj, "Resources");
+ if (inh) inherit.resources = inh;
+
+ inh = fz_dictgets(obj, "MediaBox");
+ if (inh) inherit.mediabox = inh;
+
+ inh = fz_dictgets(obj, "CropBox");
+ if (inh) inherit.cropbox = inh;
+
+ inh = fz_dictgets(obj, "Rotate");
+ if (inh) inherit.rotate = inh;
+
+ kids = fz_dictgets(obj, "Kids");
+ for (i = 0; i < fz_arraylen(kids); i++)
+ {
+ kref = fz_arrayget(kids, i);
+
+ err = pdf_loadobject(&kobj, xref, kref, nil);
+ if (err) return err;
+
+ err = loadpagetree(xref, pages, inherit, kobj, kref);
+ fz_dropobj(kobj);
+ if (err) return err;
+ }
+ }
+
+ return nil;
+}
+
+void
+pdf_debugpagetree(pdf_pagetree *pages)
+{
+ int i;
+ printf("<<\n /Type /Pages\n /Count %d\n /Kids [\n", pages->count);
+ for (i = 0; i < pages->count; i++) {
+ printf(" ");
+ fz_fprintobj(stdout, pages->pref[i]);
+ printf("\t%% page %d\n", i + 1);
+ //fz_fprintobj(stdout, pages->pobj[i]);
+ //printf("\n");
+ }
+ printf(" ]\n>>\n");
+}
+
+fz_error *
+pdf_loadpagetree(pdf_pagetree **pp, pdf_xref *xref)
+{
+ fz_error *err;
+ struct stuff inherit;
+ pdf_pagetree *p = nil;
+ fz_obj *catalog = nil;
+ fz_obj *pages = nil;
+ fz_obj *trailer;
+ fz_obj *ref;
+ int count;
+
+ inherit.resources = nil;
+ inherit.mediabox = nil;
+ inherit.cropbox = nil;
+ inherit.rotate = nil;
+
+ trailer = xref->trailer;
+
+ ref = fz_dictgets(trailer, "Root");
+ err = pdf_loadobject(&catalog, xref, ref, nil);
+ if (err) goto error;
+
+ ref = fz_dictgets(catalog, "Pages");
+ err = pdf_loadobject(&pages, xref, ref, nil);
+ if (err) goto error;
+
+ ref = fz_dictgets(pages, "Count");
+ count = fz_toint(ref);
+
+ p = *pp = fz_malloc(sizeof(pdf_pagetree));
+ if (!p) { err = fz_outofmem; goto error; }
+
+ p->pref = nil;
+ p->pobj = nil;
+ p->count = count;
+ p->cursor = 0;
+
+ p->pref = fz_malloc(sizeof(fz_obj*) * count);
+ if (!p->pref) { err = fz_outofmem; goto error; }
+
+ p->pobj = fz_malloc(sizeof(fz_obj*) * count);
+ if (!p->pobj) { err = fz_outofmem; goto error; }
+
+ err = loadpagetree(xref, p, inherit, pages, ref);
+ if (err) goto error;
+
+ fz_dropobj(pages);
+ fz_dropobj(catalog);
+ return nil;
+
+error:
+ if (pages) fz_dropobj(pages);
+ if (catalog) fz_dropobj(catalog);
+ if (p) {
+ fz_free(p->pref);
+ fz_free(p->pobj);
+ fz_free(p);
+ }
+ return nil;
+}
+
+void
+pdf_freepagetree(pdf_pagetree *pages)
+{
+ int i;
+ for (i = 0; i < pages->count; i++) {
+ if (pages->pref[i])
+ fz_dropobj(pages->pref[i]);
+ if (pages->pobj[i])
+ fz_dropobj(pages->pobj[i]);
+ }
+ fz_free(pages->pref);
+ fz_free(pages->pobj);
+ fz_free(pages);
+}
+
diff --git a/mupdf/parse.c b/mupdf/parse.c
new file mode 100644
index 00000000..90f7c453
--- /dev/null
+++ b/mupdf/parse.c
@@ -0,0 +1,305 @@
+#include <fitz.h>
+#include <mupdf.h>
+
+fz_error *
+pdf_parsearray(fz_obj **op, fz_file *file, unsigned char *buf, int cap)
+{
+ fz_error *error = nil;
+ fz_obj *ary = nil;
+ fz_obj *obj = nil;
+ int a = 0, b = 0, n = 0;
+ int tok, len;
+
+ error = fz_newarray(op, 4);
+ if (error) return error;
+ ary = *op;
+
+ while (1)
+ {
+ tok = pdf_lex(file, buf, cap, &len);
+
+ if (tok != PDF_TINT && tok != PDF_TR)
+ {
+ if (n > 0)
+ {
+ error = fz_newint(&obj, a);
+ if (error) goto cleanup;
+ error = fz_arraypush(ary, obj);
+ if (error) goto cleanup;
+ fz_dropobj(obj);
+ obj = nil;
+ }
+ if (n > 1)
+ {
+ error = fz_newint(&obj, b);
+ if (error) goto cleanup;
+ error = fz_arraypush(ary, obj);
+ if (error) goto cleanup;
+ fz_dropobj(obj);
+ obj = nil;
+ }
+ n = 0;
+ }
+
+ if (tok == PDF_TINT && n == 2)
+ {
+ error = fz_newint(&obj, a);
+ if (error) goto cleanup;
+ error = fz_arraypush(ary, obj);
+ if (error) goto cleanup;
+ fz_dropobj(obj);
+ obj = nil;
+ a = b;
+ n --;
+ }
+
+ switch (tok)
+ {
+ case PDF_TCARRAY:
+ return nil;
+ case PDF_TINT:
+ if (n == 0)
+ a = atoi(buf);
+ if (n == 1)
+ b = atoi(buf);
+ n ++;
+ break;
+ case PDF_TR:
+ if (n != 2)
+ goto cleanup;
+ error = fz_newindirect(&obj, a, b);
+ if (error) goto cleanup;
+ n = 0;
+ break;
+ case PDF_TOARRAY: error = pdf_parsearray(&obj, file, buf, cap); break;
+ case PDF_TODICT: error = pdf_parsedict(&obj, file, buf, cap); break;
+ case PDF_TNAME: error = fz_newname(&obj, buf); break;
+ case PDF_TREAL: error = fz_newreal(&obj, atof(buf)); break;
+ case PDF_TSTRING: error = fz_newstring(&obj, buf, len); break;
+ case PDF_TTRUE: error = fz_newbool(&obj, 1); break;
+ case PDF_TFALSE: error = fz_newbool(&obj, 0); break;
+ case PDF_TNULL: error = fz_newnull(&obj); break;
+ default: goto cleanup;
+ }
+ if (error) goto cleanup;
+
+ if (obj)
+ {
+ error = fz_arraypush(ary, obj);
+ if (error) goto cleanup;
+ fz_dropobj(obj);
+ }
+
+ obj = nil;
+ }
+
+cleanup:
+ if (obj) fz_dropobj(obj);
+ if (ary) fz_dropobj(ary);
+ if (error) return error;
+ return fz_throw("syntaxerror in array");
+}
+
+fz_error *
+pdf_parsedict(fz_obj **op, fz_file *file, unsigned char *buf, int cap)
+{
+ fz_error *error = nil;
+ fz_obj *dict = nil;
+ fz_obj *key = nil;
+ fz_obj *val = nil;
+ int tok, len;
+ int a, b;
+
+ error = fz_newdict(op, 8);
+ if (error) return error;
+ dict = *op;
+
+ while (1)
+ {
+ tok = pdf_lex(file, buf, cap, &len);
+
+skip:
+ if (tok == PDF_TCDICT)
+ return nil;
+
+ if (tok != PDF_TNAME)
+ goto cleanup;
+
+ error = fz_newname(&key, buf);
+ if (error) goto cleanup;
+
+ tok = pdf_lex(file, buf, cap, &len);
+
+ switch (tok)
+ {
+ case PDF_TOARRAY: error = pdf_parsearray(&val, file, buf, cap); break;
+ case PDF_TODICT: error = pdf_parsedict(&val, file, buf, cap); break;
+ case PDF_TNAME: error = fz_newname(&val, buf); break;
+ case PDF_TREAL: error = fz_newreal(&val, atof(buf)); break;
+ case PDF_TSTRING: error = fz_newstring(&val, buf, len); break;
+ case PDF_TTRUE: error = fz_newbool(&val, 1); break;
+ case PDF_TFALSE: error = fz_newbool(&val, 0); break;
+ case PDF_TNULL: error = fz_newnull(&val); break;
+ case PDF_TINT:
+ a = atoi(buf);
+ tok = pdf_lex(file, buf, cap, &len);
+ if (tok == PDF_TCDICT || tok == PDF_TNAME)
+ {
+ error = fz_newint(&val, a);
+ if (error) goto cleanup;
+ error = fz_dictput(dict, key, val);
+ if (error) goto cleanup;
+ fz_dropobj(val);
+ fz_dropobj(key);
+ key = val = nil;
+ goto skip;
+ }
+ if (tok == PDF_TINT)
+ {
+ b = atoi(buf);
+ tok = pdf_lex(file, buf, cap, &len);
+ if (tok == PDF_TR)
+ {
+ error = fz_newindirect(&val, a, b);
+ break;
+ }
+ }
+ goto cleanup;
+ default:
+ goto cleanup;
+ }
+
+ if (error) goto cleanup;
+
+ error = fz_dictput(dict, key, val);
+ if (error) goto cleanup;
+
+ fz_dropobj(val);
+ fz_dropobj(key);
+ key = val = nil;
+ }
+
+cleanup:
+ if (key) fz_dropobj(key);
+ if (val) fz_dropobj(val);
+ if (dict) fz_dropobj(dict);
+ if (error) return error;
+ return fz_throw("syntaxerror in dictionary");
+}
+
+fz_error *
+pdf_parsestmobj(fz_obj **op, fz_file *file, unsigned char *buf, int cap)
+{
+ int tok, len;
+
+ tok = pdf_lex(file, buf, cap, &len);
+
+ switch (tok)
+ {
+ case PDF_TOARRAY: return pdf_parsearray(op, file, buf, cap);
+ case PDF_TODICT: return pdf_parsedict(op, file, buf, cap);
+ case PDF_TNAME: return fz_newname(op, buf);
+ case PDF_TREAL: return fz_newreal(op, atof(buf));
+ case PDF_TSTRING: return fz_newstring(op, buf, len);
+ case PDF_TTRUE: return fz_newbool(op, 1);
+ case PDF_TFALSE: return fz_newbool(op, 0);
+ case PDF_TNULL: return fz_newnull(op);
+ case PDF_TINT: return fz_newint(op, atoi(buf));
+ }
+
+ return fz_throw("syntaxerror in object stream");
+}
+
+fz_error *
+pdf_parseindobj(fz_obj **op, fz_file *file, unsigned char *buf, int cap,
+ int *ooid, int *ogid, int *ostmofs)
+{
+ fz_error *error = nil;
+ fz_obj *obj = nil;
+ int oid, gid, stmofs;
+ int tok, len;
+ int a, b;
+
+ tok = pdf_lex(file, buf, cap, &len);
+ if (tok != PDF_TINT)
+ goto cleanup;
+ oid = atoi(buf);
+
+ tok = pdf_lex(file, buf, cap, &len);
+ if (tok != PDF_TINT)
+ goto cleanup;
+ gid = atoi(buf);
+
+ tok = pdf_lex(file, buf, cap, &len);
+ if (tok != PDF_TOBJ)
+ goto cleanup;
+
+ tok = pdf_lex(file, buf, cap, &len);
+ switch (tok)
+ {
+ case PDF_TOARRAY: error = pdf_parsearray(&obj, file, buf, cap); break;
+ case PDF_TODICT: error = pdf_parsedict(&obj, file, buf, cap); break;
+ case PDF_TNAME: error = fz_newname(&obj, buf); break;
+ case PDF_TREAL: error = fz_newreal(&obj, atof(buf)); break;
+ case PDF_TSTRING: error = fz_newstring(&obj, buf, len); break;
+ case PDF_TTRUE: error = fz_newbool(&obj, 1); break;
+ case PDF_TFALSE: error = fz_newbool(&obj, 0); break;
+ case PDF_TNULL: error = fz_newnull(&obj); break;
+ case PDF_TINT:
+ a = atoi(buf);
+ tok = pdf_lex(file, buf, cap, &len);
+ if (tok == PDF_TSTREAM || tok == PDF_TENDOBJ)
+ {
+ error = fz_newint(&obj, a);
+ if (error) goto cleanup;
+ goto skip;
+ }
+ if (tok == PDF_TINT)
+ {
+ b = atoi(buf);
+ tok = pdf_lex(file, buf, cap, &len);
+ if (tok == PDF_TR)
+ {
+ error = fz_newindirect(&obj, a, b);
+ break;
+ }
+ }
+ goto cleanup;
+ default:
+ goto cleanup;
+ }
+ if (error) goto cleanup;
+
+ tok = pdf_lex(file, buf, cap, &len);
+
+skip:
+ if (tok == PDF_TSTREAM)
+ {
+ int c = fz_readbyte(file);
+ if (c == '\r')
+ {
+ c = fz_peekbyte(file);
+ if (c != '\n')
+ fz_warn("corrupt pdf stream (%d %d)\n", oid, gid);
+ else
+ c = fz_readbyte(file);
+ }
+ stmofs = fz_tell(file);
+ }
+ else if (tok == PDF_TENDOBJ)
+ stmofs = -1;
+ else
+ goto cleanup;
+
+ if (ooid) *ooid = oid;
+ if (ogid) *ogid = gid;
+ if (ostmofs) *ostmofs = stmofs;
+ *op = obj;
+ return nil;
+
+cleanup:
+ if (obj) fz_dropobj(obj);
+ if (error) return error;
+ return fz_throw("syntaxerror in indirect object");
+}
+
diff --git a/mupdf/repair.c b/mupdf/repair.c
new file mode 100644
index 00000000..abc69cbc
--- /dev/null
+++ b/mupdf/repair.c
@@ -0,0 +1,279 @@
+#include <fitz.h>
+#include <mupdf.h>
+
+struct entry
+{
+ int oid;
+ int gid;
+ int ofs;
+ int stmlen;
+};
+
+static fz_error *
+parseobj(fz_file *file, unsigned char *buf, int cap, int *stmlen,
+ int *isroot, int *isinfo)
+{
+ fz_error *error;
+ fz_obj *dict = nil;
+ fz_obj *length;
+ fz_obj *filter;
+ fz_obj *type;
+ int tok, len;
+ int stmofs;
+
+ *stmlen = -1;
+ *isroot = 0;
+ *isinfo = 0;
+
+ tok = pdf_lex(file, buf, cap, &len);
+ if (tok == PDF_TODICT)
+ {
+ error = pdf_parsedict(&dict, file, buf, cap);
+ if (error)
+ return error;
+ }
+
+ if (fz_isdict(dict))
+ {
+ type = fz_dictgets(dict, "Type");
+ if (fz_isname(type) && !strcmp(fz_toname(type), "Catalog"))
+ *isroot = 1;
+
+ filter = fz_dictgets(dict, "Filter");
+ if (fz_isname(filter) && !strcmp(fz_toname(filter), "Standard"))
+ return fz_throw("cannot repair encrypted files");
+
+ if (fz_dictgets(dict, "Producer"))
+ if (fz_dictgets(dict, "Creator"))
+ if (fz_dictgets(dict, "Title"))
+ *isinfo = 1;
+ }
+
+ while ( tok != PDF_TSTREAM &&
+ tok != PDF_TENDOBJ &&
+ tok != PDF_TERROR &&
+ tok != PDF_TEOF )
+ tok = pdf_lex(file, buf, cap, &len);
+
+ if (tok == PDF_TSTREAM)
+ {
+ int c = fz_readbyte(file);
+ if (c == '\r') {
+ c = fz_peekbyte(file);
+ if (c == '\n')
+ fz_readbyte(file);
+ }
+
+ stmofs = fz_tell(file);
+
+ length = fz_dictgets(dict, "Length");
+ if (fz_isint(length))
+ {
+ fz_seek(file, stmofs + fz_toint(length));
+ tok = pdf_lex(file, buf, cap, &len);
+ if (tok == PDF_TENDSTREAM)
+ goto atobjend;
+ fz_seek(file, stmofs);
+ }
+
+ fz_read(file, buf, 8);
+ while (memcmp(buf, "endstream", 8) != 0)
+ {
+ c = fz_readbyte(file);
+ if (c == EOF)
+ break;
+ memmove(buf, buf + 1, 7);
+ buf[7] = c;
+ }
+
+ *stmlen = fz_tell(file) - stmofs - 8;
+
+atobjend:
+ tok = pdf_lex(file, buf, cap, &len);
+ if (tok == PDF_TENDOBJ)
+ ;
+ }
+
+ if (dict)
+ fz_dropobj(dict);
+
+ return nil;
+}
+
+fz_error *
+pdf_repairxref(pdf_xref *xref, char *filename)
+{
+ fz_error *error;
+ fz_file *file;
+
+ struct entry *list = nil;
+ int listlen;
+ int listcap;
+ int maxoid;
+
+ unsigned char buf[65536];
+
+ int oid, gid;
+ int tmpofs, oidofs, gidofs;
+ int stmlen;
+ int isroot, rootoid = 0, rootgid = 0;
+ int isinfo, infooid = 0, infogid = 0;
+ int tok, len;
+ int next;
+ int n;
+ int i;
+
+ listlen = 0;
+ listcap = 1024;
+ list = fz_malloc(listcap * sizeof(struct entry));
+ if (!list)
+ return fz_outofmem;
+
+ error = fz_openfile(&xref->file, filename, O_RDONLY);
+ if (error)
+ goto cleanup;
+
+ file = xref->file;
+
+ n = fz_seek(file, 0);
+ if (n < 0) {
+ error = fz_ferror(file);
+ goto cleanup;
+ }
+
+ maxoid = 0;
+ oid = 0;
+ gid = 0;
+ oidofs = 0;
+ gidofs = 0;
+
+ while (1)
+ {
+ tmpofs = fz_tell(file);
+
+ tok = pdf_lex(file, buf, sizeof buf, &len);
+ if (tok == PDF_TINT)
+ {
+ oidofs = gidofs;
+ oid = gid;
+ gidofs = tmpofs;
+ gid = atoi(buf);
+ }
+
+ if (tok == PDF_TOBJ)
+ {
+
+ error = parseobj(file, buf, sizeof buf, &stmlen, &isroot, &isinfo);
+ if (error)
+ goto cleanup;
+
+ if (isroot) {
+ rootoid = oid;
+ rootgid = gid;
+ }
+
+ if (isinfo) {
+ infooid = oid;
+ infogid = gid;
+ }
+
+ if (listlen + 1 == listcap)
+ {
+ struct entry *newlist;
+ listcap = listcap * 2;
+ newlist = fz_realloc(list, listcap * sizeof(struct entry));
+ if (!newlist) {
+ error = fz_outofmem;
+ goto cleanup;
+ }
+ list = newlist;
+ }
+
+ list[listlen].oid = oid;
+ list[listlen].gid = gid;
+ list[listlen].ofs = oidofs;
+ list[listlen].stmlen = stmlen;
+ listlen ++;
+
+ if (oid > maxoid)
+ maxoid = oid;
+ }
+
+ if (tok == PDF_TEOF)
+ break;
+ }
+
+ error = fz_packobj(&xref->trailer,
+ "<< /Size %i /Root %r >>",
+ maxoid + 1, rootoid, rootgid);
+ if (error)
+ goto cleanup;
+
+ xref->version = 1.3; /* FIXME */
+ xref->size = maxoid + 1;
+ xref->capacity = xref->size;
+ xref->table = fz_malloc(xref->capacity * sizeof(pdf_xrefentry));
+ if (!xref->table) {
+ error = fz_outofmem;
+ goto cleanup;
+ }
+
+ xref->table[0].type = 'f';
+ xref->table[0].mark = 0;
+ xref->table[0].ofs = 0;
+ xref->table[0].gen = 65535;
+
+ for (i = 1; i < xref->size; i++)
+ {
+ xref->table[i].type = 'f';
+ xref->table[i].mark = 0;
+ xref->table[i].ofs = 0;
+ xref->table[i].gen = 0;
+ }
+
+ for (i = 0; i < listlen; i++)
+ {
+ xref->table[list[i].oid].type = 'n';
+ xref->table[list[i].oid].ofs = list[i].ofs;
+ xref->table[list[i].oid].gen = list[i].gid;
+ xref->table[list[i].oid].mark = 0;
+
+ /* corrected stream length */
+ if (list[i].stmlen >= 0)
+ {
+ fz_obj *dict, *length;
+ error = pdf_loadobject0(&dict, xref, list[i].oid, list[i].gid, nil);
+ if (error)
+ goto cleanup;
+ error = fz_newint(&length, list[i].stmlen);
+ if (error)
+ goto cleanup;
+ error = fz_dictputs(dict, "Length", length);
+ if (error)
+ goto cleanup;
+ error = pdf_saveobject(xref, list[i].oid, list[i].gid, dict);
+ if (error)
+ goto cleanup;
+ }
+ }
+
+ next = 0;
+ for (i = xref->size - 1; i >= 0; i--)
+ {
+ if (xref->table[i].type == 'f')
+ {
+ xref->table[i].ofs = next;
+ if (xref->table[i].gen < 65535)
+ xref->table[i].gen ++;
+ next = i;
+ }
+ }
+
+ fz_free(list);
+ return nil;
+
+cleanup:
+ fz_free(list);
+ return error;
+}
+
diff --git a/mupdf/resources.c b/mupdf/resources.c
new file mode 100644
index 00000000..050b003d
--- /dev/null
+++ b/mupdf/resources.c
@@ -0,0 +1,184 @@
+#include <fitz.h>
+#include <mupdf.h>
+
+static fz_error *
+loadextgstatefonts(pdf_resources *rdb, pdf_xref *xref)
+{
+ fz_error *err;
+ char name[64];
+ pdf_font *font;
+ fz_obj *extgstate;
+ fz_obj *obj;
+ fz_obj *ptr;
+ fz_obj *ref;
+ int i;
+
+ for (i = 0; i < fz_dictlen(rdb->extgstate); i++)
+ {
+ extgstate = fz_dictgetval(rdb->extgstate, i);
+
+ obj = fz_dictgets(extgstate, "Font");
+ if (obj)
+ {
+ font = nil;
+ ptr = nil;
+
+ if (!fz_isarray(obj) || fz_arraylen(obj) != 2)
+ return fz_throw("syntaxerror in ExtGState/Font");
+
+ ref = fz_arrayget(obj, 0);
+ sprintf(name, "$f.%d.%d", fz_toobjid(ref), fz_togenid(ref));
+
+ err = pdf_resolve(&ref, xref);
+ if (err) return err;
+
+ err = pdf_loadfont(&font, xref, ref);
+ if (err) goto cleanup;
+
+ err = fz_newpointer(&ptr, font);
+ if (err) goto cleanup;
+
+ err = fz_dictputs(rdb->font, name, ptr);
+ if (err) goto cleanup;
+
+ fz_dropobj(ptr);
+ fz_dropobj(ref);
+ }
+ }
+
+ return nil;
+
+cleanup:
+ if (font) fz_freefont((fz_font*)font);
+ if (ptr) fz_dropobj(ptr);
+ fz_dropobj(ref);
+ return err;
+}
+
+static fz_error *
+loadextgstates(pdf_resources *rdb, pdf_xref *xref, fz_obj *dict)
+{
+ fz_error *err;
+ fz_obj *key, *val;
+ int i;
+
+ for (i = 0; i < fz_dictlen(dict); i++)
+ {
+ key = fz_dictgetkey(dict, i);
+ val = fz_dictgetval(dict, i);
+
+ err = pdf_resolve(&val, xref);
+ if (err) return err;
+
+ err = fz_dictput(rdb->extgstate, key, val);
+ if (err) { fz_dropobj(val); return err; }
+
+ fz_dropobj(val);
+ }
+
+ return nil;
+}
+
+static fz_error *
+loadfonts(pdf_resources *rdb, pdf_xref *xref, fz_obj *dict)
+{
+ fz_error *err;
+ pdf_font *font;
+ fz_obj *key, *val;
+ fz_obj *ptr;
+ int i;
+
+ for (i = 0; i < fz_dictlen(dict); i++)
+ {
+ font = nil;
+ ptr = nil;
+
+ key = fz_dictgetkey(dict, i);
+ val = fz_dictgetval(dict, i);
+
+ err = pdf_resolve(&val, xref);
+ if (err) return err;
+
+ err = pdf_loadfont(&font, xref, val);
+ if (err) goto cleanup;
+
+ err = fz_newpointer(&ptr, font);
+ if (err) goto cleanup;
+
+ err = fz_dictput(rdb->font, key, ptr);
+ if (err) goto cleanup;
+
+ fz_dropobj(ptr);
+ fz_dropobj(val);
+ }
+
+ return nil;
+
+cleanup:
+ if (font) fz_freefont((fz_font*)font);
+ if (ptr) fz_dropobj(ptr);
+ fz_dropobj(val);
+ return err;
+}
+
+fz_error *
+pdf_loadresources(pdf_resources **rdbp, pdf_xref *xref, fz_obj *topdict)
+{
+ fz_error *err;
+ pdf_resources *rdb;
+ fz_obj *subdict;
+
+ rdb = *rdbp = fz_malloc(sizeof (pdf_resources));
+ if (!rdb)
+ return fz_outofmem;
+
+ rdb->extgstate = nil;
+ rdb->font = nil;
+ rdb->colorspace = nil;
+ rdb->ximage = nil;
+ rdb->xform = nil;
+
+ err = fz_newdict(&rdb->extgstate, 5);
+ if (err) { pdf_freeresources(rdb); return err; }
+
+ subdict = fz_dictgets(topdict, "ExtGState");
+ if (subdict)
+ {
+ err = pdf_resolve(&subdict, xref);
+ if (err) { pdf_freeresources(rdb); return err; }
+ err = loadextgstates(rdb, xref, subdict);
+ fz_dropobj(subdict);
+ if (err) { pdf_freeresources(rdb); return err; }
+ }
+
+ err = fz_newdict(&rdb->font, 15);
+ if (err) { pdf_freeresources(rdb); return err; }
+
+ err = loadextgstatefonts(rdb, xref);
+ if (err) { pdf_freeresources(rdb); return err; }
+
+ subdict = fz_dictgets(topdict, "Font");
+ if (subdict)
+ {
+ err = pdf_resolve(&subdict, xref);
+ if (err) { pdf_freeresources(rdb); return err; }
+ err = loadfonts(rdb, xref, subdict);
+ fz_dropobj(subdict);
+ if (err) { pdf_freeresources(rdb); return err; }
+ }
+
+ return nil;
+}
+
+void
+pdf_freeresources(pdf_resources *rdb)
+{
+ /* TODO freefont */
+ if (rdb->extgstate) fz_dropobj(rdb->extgstate);
+ if (rdb->colorspace) fz_dropobj(rdb->colorspace);
+ if (rdb->font) fz_dropobj(rdb->font);
+ if (rdb->ximage) fz_dropobj(rdb->ximage);
+ if (rdb->xform) fz_dropobj(rdb->xform);
+ fz_free(rdb);
+}
+
diff --git a/mupdf/save.c b/mupdf/save.c
new file mode 100644
index 00000000..c86c9e72
--- /dev/null
+++ b/mupdf/save.c
@@ -0,0 +1,314 @@
+#include <fitz.h>
+#include <mupdf.h>
+
+static fz_error *writestored(FILE *out, pdf_xref *xref, int oid)
+{
+ pdf_xrefentry *x = xref->table + oid;
+ fz_error *error;
+ fz_obj *obj;
+ fz_buffer *stm;
+
+ obj = pdf_findstoredobject(xref->store, oid, x->gen);
+ stm = pdf_findstoredstream(xref->store, oid, x->gen);
+
+ if (!obj)
+ return fz_throw("could not find stored object");
+
+ if (xref->crypt)
+ pdf_cryptobj(xref->crypt, obj, oid, x->gen);
+
+ fprintf(out, "%d %d obj\n", oid, x->gen);
+ fz_fprintobj(out, obj);
+ fprintf(out, "\n");
+
+ if (stm)
+ {
+ fprintf(out, "stream\n");
+ fwrite(stm->rp, 1, stm->wp - stm->rp, out);
+ fprintf(out, "endstream\n");
+ }
+
+ fprintf(out, "endobj\n\n");
+
+ if (ferror(out))
+ return fz_throw("ioerror: write failed");
+
+ return nil;
+}
+
+static fz_error *writecopy(FILE *out, pdf_xref *xref, int oid)
+{
+ pdf_xrefentry *x = xref->table + oid;
+ fz_error *error;
+ fz_obj *length;
+ fz_obj *obj;
+ int stmofs;
+ fz_filter *cf;
+ fz_filter *nf;
+ fz_filter *pipe;
+ int n;
+ unsigned char buf[4096];
+
+ error = pdf_loadobject0(&obj, xref, oid, x->gen, &stmofs);
+ if (error)
+ return error;
+
+ fprintf(out, "%d %d obj\n", oid, x->gen);
+ fz_fprintobj(out, obj);
+ fprintf(out, "\n");
+
+ if (stmofs != -1)
+ {
+ fprintf(out, "stream\n");
+
+ length = fz_dictgets(obj, "Length");
+ error = pdf_resolve(&length, xref);
+ if (error)
+ goto cleanup;
+
+ if (xref->crypt)
+ {
+ error = fz_newnullfilter(&nf, fz_toint(length));
+ if (error)
+ goto cleanup;
+ error = pdf_cryptstm(&cf, xref->crypt, oid, x->gen);
+ if (error)
+ goto cleanup;
+ error = fz_newpipeline(&pipe, nf, cf);
+ if (error)
+ goto cleanup;
+ }
+ else
+ {
+ error = fz_newnullfilter(&pipe, fz_toint(length));
+ if (error)
+ goto cleanup;
+ }
+
+ fz_seek(xref->file, stmofs);
+ fz_pushfilter(xref->file, pipe);
+
+ while (1)
+ {
+ n = fz_read(xref->file, buf, sizeof buf);
+ if (n == 0)
+ break;
+ if (n < 0)
+ {
+ error = fz_ferror(xref->file);
+ fz_popfilter(xref->file);
+ goto cleanup;
+ }
+ fwrite(buf, 1, n, out);
+ }
+
+ fz_popfilter(xref->file);
+
+ fprintf(out, "endstream\n");
+ }
+
+ fprintf(out, "endobj\n\n");
+
+ fz_dropobj(obj);
+
+ if (ferror(out))
+ return fz_throw("ioerror: write failed");
+
+ return nil;
+
+cleanup:
+ fz_dropobj(obj);
+ return error;
+}
+
+static int countmodified(pdf_xref *xref, int oid)
+{
+ int i;
+ for (i = oid; i < xref->size; i++)
+ if (xref->table[i].type != 'a' && xref->table[i].type != 'd')
+ return i - oid;
+ return i - oid;
+}
+
+fz_error *
+pdf_saveincrementalpdf(pdf_xref *xref, char *path)
+{
+ fz_error *error;
+ FILE *out;
+ int oid;
+ int i, n;
+ int startxref;
+ fz_obj *obj;
+
+ out = fopen(path, "ab");
+ if (!out)
+ return fz_throw("ioerror: could not open '%s': %s", path, strerror(errno));
+ fprintf(out, "\n");
+
+ for (oid = 0; oid < xref->size; oid++)
+ {
+ if (xref->table[oid].type == 'a')
+ {
+ xref->table[oid].ofs = ftell(out);
+ error = writestored(out, xref, oid);
+ if (error)
+ goto cleanup;
+ }
+ }
+
+ /* always write out entry 0 in appended xref sections */
+ xref->table[0].type = 'd';
+
+ startxref = ftell(out);
+ fprintf(out, "xref\n");
+
+ oid = 0;
+ while (oid < xref->size)
+ {
+ n = countmodified(xref, oid);
+
+ fprintf(out, "%d %d\n", oid, n);
+
+ for (i = 0; i < n; i++)
+ {
+ if (xref->table[oid + i].type == 'd')
+ xref->table[oid + i].type = 'f';
+ if (xref->table[oid + i].type == 'a')
+ xref->table[oid + i].type = 'n';
+
+ fprintf(out, "%010d %05d %c \n",
+ xref->table[oid + i].ofs,
+ xref->table[oid + i].gen,
+ xref->table[oid + i].type);
+ }
+
+ oid += n;
+ while (oid < xref->size &&
+ xref->table[oid].type != 'a' &&
+ xref->table[oid].type != 'd')
+ oid ++;
+ }
+
+ fprintf(out, "\n");
+
+ fprintf(out, "trailer\n<<\n /Size %d\n /Prev %d", xref->size, xref->startxref);
+
+ obj = fz_dictgets(xref->trailer, "Root");
+ fprintf(out,"\n /Root %d %d R", fz_toobjid(obj), fz_togenid(obj));
+
+ obj = fz_dictgets(xref->trailer, "Info");
+ if (obj)
+ fprintf(out,"\n /Info %d %d R", fz_toobjid(obj), fz_togenid(obj));
+
+ obj = fz_dictgets(xref->trailer, "Encrypt");
+ if (obj) {
+ fprintf(out,"\n /Encrypt ");
+ fz_fprintobj(out, obj);
+ }
+
+ obj = fz_dictgets(xref->trailer, "ID");
+ if (obj) {
+ fprintf(out,"\n /ID ");
+ fz_fprintobj(out, obj);
+ }
+
+ fprintf(out, "\n>>\n\n");
+
+ fprintf(out, "startxref\n");
+ fprintf(out, "%d\n", startxref);
+ fprintf(out, "%%%%EOF\n");
+
+ xref->startxref = startxref;
+
+ fclose(out);
+ return nil;
+
+cleanup:
+ fclose(out);
+ return error;
+}
+
+fz_error *
+pdf_savepdf(pdf_xref *xref, char *path)
+{
+ fz_error *error;
+ FILE *out;
+ int oid;
+ int startxref;
+ int *ofsbuf;
+ fz_obj *obj;
+
+ ofsbuf = fz_malloc(sizeof(int) * xref->size);
+ if (!ofsbuf)
+ return fz_outofmem;
+
+ out = fopen(path, "wb");
+ if (!out)
+ {
+ fz_free(ofsbuf);
+ return fz_throw("ioerror: could not open '%s': %s", path, strerror(errno));
+ }
+
+ fprintf(out, "%%PDF-%1.1f\n", xref->version);
+ fprintf(out, "%%\342\343\317\323\n\n");
+
+ for (oid = 0; oid < xref->size; oid++)
+ {
+ if (xref->table[oid].type == 'n' || xref->table[oid].type == 'o')
+ {
+ ofsbuf[oid] = ftell(out);
+ error = writecopy(out, xref, oid);
+ if (error)
+ goto cleanup;
+ }
+ else if (xref->table[oid].type == 'a')
+ {
+ ofsbuf[oid] = ftell(out);
+ error = writestored(out, xref, oid);
+ if (error)
+ goto cleanup;
+ }
+ else
+ {
+ ofsbuf[oid] = xref->table[oid].ofs;
+ }
+ }
+
+ startxref = ftell(out);
+ fprintf(out, "xref\n");
+ fprintf(out, "0 %d\n", xref->size);
+
+ for (oid = 0; oid < xref->size; oid++)
+ {
+ int type = xref->table[oid].type;
+ if (type == 'a' || type == 'o')
+ type = 'n';
+ if (type == 'd')
+ type = 'f';
+ fprintf(out, "%010d %05d %c \n", ofsbuf[oid], xref->table[oid].gen, type);
+ }
+
+ fprintf(out, "\n");
+
+ fprintf(out, "trailer\n<<\n /Size %d", xref->size);
+ obj = fz_dictgets(xref->trailer, "Root");
+ fprintf(out,"\n /Root %d %d R", fz_toobjid(obj), fz_togenid(obj));
+ obj = fz_dictgets(xref->trailer, "Info");
+ if (obj)
+ fprintf(out,"\n /Info %d %d R", fz_toobjid(obj), fz_togenid(obj));
+ fprintf(out, "\n>>\n\n");
+
+ fprintf(out, "startxref\n");
+ fprintf(out, "%d\n", startxref);
+ fprintf(out, "%%%%EOF\n");
+
+ xref->startxref = startxref;
+
+ fclose(out);
+ return nil;
+
+cleanup:
+ fclose(out);
+ return error;
+}
+
diff --git a/mupdf/stream.c b/mupdf/stream.c
new file mode 100644
index 00000000..f3f40e53
--- /dev/null
+++ b/mupdf/stream.c
@@ -0,0 +1,262 @@
+#include <fitz.h>
+#include <mupdf.h>
+
+static fz_error *
+makefilter(fz_filter **fp, fz_obj *f, fz_obj *p)
+{
+ fz_filter *predf = nil;
+ fz_filter *realf = nil;
+ fz_error *error;
+ char *s;
+
+ s = fz_toname(f);
+
+ if (!strcmp(s, "ASCIIHexDecode") || !strcmp(s, "AHx"))
+ return fz_newahxd(fp, p);
+
+ if (!strcmp(s, "ASCII85Decode") || !strcmp(s, "A85"))
+ return fz_newa85d(fp, p);
+
+ if (!strcmp(s, "CCITTFaxDecode") || !strcmp(s, "CCF"))
+ return fz_newfaxd(fp, p);
+
+ if (!strcmp(s, "DCTDecode") || !strcmp(s, "DCT"))
+ return fz_newdctd(fp, p);
+
+ if (!strcmp(s, "RunLengthDecode") || !strcmp(s, "RL"))
+ return fz_newrld(fp, p);
+
+ if (!strcmp(s, "JPXDecode"))
+ return fz_newjpxd(fp, p);
+
+ if (!strcmp(s, "FlateDecode") || !strcmp(s, "Fl"))
+ {
+ if (fz_isdict(p)) {
+ fz_obj *obj = fz_dictgets(p, "Predictor");
+ if (obj) {
+ error = fz_newflated(&realf, p);
+ if (error) goto cleanup;
+ error = fz_newpredictd(&predf, p);
+ if (error) goto cleanup;
+ error = fz_newpipeline(fp, realf, predf);
+ if (error) goto cleanup;
+ return nil;
+ }
+ }
+ return fz_newflated(fp, p);
+ }
+
+ if (!strcmp(s, "LZWDecode") || !strcmp(s, "LZW"))
+ {
+ if (fz_isdict(p)) {
+ fz_obj *obj = fz_dictgets(p, "Predictor");
+ if (obj) {
+ error = fz_newlzwd(&realf, p);
+ if (error) goto cleanup;
+ error = fz_newpredictd(&predf, p);
+ if (error) goto cleanup;
+ error = fz_newpipeline(fp, realf, predf);
+ if (error) goto cleanup;
+ return nil;
+ }
+ }
+ return fz_newlzwd(fp, p);
+ }
+
+ if (!strcmp(s, "JBIG2Decode")) {
+ /* TODO: extract and feed JBIG2Global */
+ return fz_newjbig2d(fp, p);
+ }
+
+ return fz_throw("syntaxerror: unknown filter: %s", s);
+
+cleanup:
+ if (realf) fz_freefilter(realf);
+ if (predf) fz_freefilter(predf);
+ return error;
+}
+
+static fz_error *
+makepipeline(fz_filter **fp, fz_obj *fs, fz_obj *ps)
+{
+ fz_error *error;
+ fz_filter *filter = nil;
+ fz_filter *pipe = nil;
+ fz_obj *f;
+ fz_obj *p;
+ int i;
+
+ for (i = 0; i < fz_arraylen(fs); i++)
+ {
+ f = fz_arrayget(fs, i);
+
+ if (fz_isarray(ps))
+ p = fz_arrayget(ps, i);
+ else
+ p = nil;
+
+ error = makefilter(&filter, f, p);
+ if (error) { if (pipe) fz_freefilter(pipe); return error; }
+
+ if (pipe) {
+ fz_filter *np;
+ error = fz_newpipeline(&np, pipe, filter);
+ if (error) { fz_freefilter(pipe); return error; }
+ pipe = np;
+ }
+ else pipe = filter;
+ }
+
+ *fp = pipe;
+ return nil;
+}
+
+fz_error *
+pdf_buildfilter(fz_filter **fp, pdf_xref *xref, fz_obj *stmobj, int oid, int gid)
+{
+ fz_error *error;
+ fz_filter *filter;
+ fz_obj *filters;
+ fz_obj *params;
+ fz_obj *obj;
+ int len;
+
+ obj = fz_dictgets(stmobj, "Length");
+ error = pdf_resolve(&obj, xref);
+ if (error)
+ return error;
+ len = fz_toint(obj);
+ fz_dropobj(obj);
+
+ filters = fz_dictgets(stmobj, "Filter");
+ params = fz_dictgets(stmobj, "DecodeParms");
+
+ if (!filters)
+ {
+ error = fz_newnullfilter(&filter, len);
+ if (error)
+ return error;
+ }
+
+ else if (fz_isname(filters))
+ {
+ error = makefilter(&filter, filters, params);
+ if (error)
+ return error;
+ }
+
+ else if (fz_isarray(filters))
+ {
+ if (fz_arraylen(filters) == 0)
+ error = fz_newnullfilter(&filter, len);
+ else
+ error = makepipeline(&filter, filters, params);
+ if (error)
+ return error;
+ }
+
+ else
+ {
+ return fz_throw("typecheck in buildstream");
+ }
+
+ if (xref->crypt)
+ {
+ fz_filter *cryptfilter;
+ fz_filter *pipeline;
+
+ error = pdf_cryptstm(&cryptfilter, xref->crypt, oid, gid);
+ if (error)
+ {
+ fz_freefilter(filter);
+ return error;
+ }
+
+ error = fz_newpipeline(&pipeline, cryptfilter, filter);
+ if (error)
+ {
+ fz_freefilter(cryptfilter);
+ fz_freefilter(filter);
+ return error;
+ }
+
+ filter = pipeline;
+ }
+
+ *fp = filter;
+
+ return nil;
+}
+
+fz_error *
+pdf_openstream0(pdf_xref *xref, fz_obj *stmobj, int oid, int gid, int ofs)
+{
+ fz_error *error;
+ fz_filter *filter;
+
+ error = pdf_buildfilter(&filter, xref, stmobj, oid, gid);
+ if (error)
+ return error;
+
+ ofs = fz_seek(xref->file, ofs);
+ if (ofs < 0) {
+ fz_freefilter(filter);
+ return fz_ferror(xref->file);
+ }
+
+ error = fz_pushfilter(xref->file, filter);
+ if (error) {
+ fz_freefilter(filter);
+ return error;
+ }
+
+ return nil;
+}
+
+fz_error *
+pdf_openstream(pdf_xref *xref, fz_obj *stmref)
+{
+ fz_error *error;
+ fz_obj *stmobj;
+ int oid, gid, ofs;
+
+ oid = fz_toobjid(stmref);
+ gid = fz_togenid(stmref);
+
+ error = pdf_loadobject0(&stmobj, xref, oid, gid, &ofs);
+ if (error)
+ return error;
+
+ error = pdf_openstream0(xref, stmobj, oid, gid, ofs);
+ if (error) {
+ fz_dropobj(stmobj);
+ return error;
+ }
+
+ fz_dropobj(stmobj);
+
+ return nil;
+}
+
+void
+pdf_closestream(pdf_xref *xref)
+{
+ fz_popfilter(xref->file);
+}
+
+fz_error *
+pdf_readstream(unsigned char **bufp, int *lenp, pdf_xref *xref, fz_obj *stmref)
+{
+ fz_error *error;
+
+ error = pdf_openstream(xref, stmref);
+ if (error)
+ return error;
+
+ error = fz_readfile(bufp, lenp, xref->file);
+
+ pdf_closestream(xref);
+
+ return error;
+}
+
diff --git a/mupdf/xref.c b/mupdf/xref.c
new file mode 100644
index 00000000..37cc16c2
--- /dev/null
+++ b/mupdf/xref.c
@@ -0,0 +1,410 @@
+#include <fitz.h>
+#include <mupdf.h>
+
+/*
+ * initialize xref
+ */
+
+fz_error *
+pdf_newxref(pdf_xref **xrefp)
+{
+ fz_error *error;
+ pdf_xref *xref;
+
+ xref = *xrefp = fz_malloc(sizeof(pdf_xref));
+ if (!xref)
+ return fz_outofmem;
+
+ xref->version = 1.3;
+ xref->crypt = nil;
+ xref->file = nil;
+ xref->size = 0;
+ xref->capacity = 0;
+ xref->table = nil;
+ xref->trailer = nil;
+ xref->startxref = -1;
+
+ error = fz_newhash(&xref->store, 256, sizeof(int) * 3);
+ if (error)
+ {
+ fz_free(xref);
+ return error;
+ }
+
+ return nil;
+}
+
+fz_error *
+pdf_decryptxref(pdf_xref *xref)
+{
+ fz_error *error;
+ fz_obj *encrypt;
+ fz_obj *id;
+
+ if (xref->size < 0)
+ return fz_throw("rangecheck: xref missing first slot");
+
+ encrypt = fz_dictgets(xref->trailer, "Encrypt");
+ id = fz_dictgets(xref->trailer, "ID");
+
+ if (encrypt && id)
+ {
+ error = pdf_resolve(&encrypt, xref);
+ if (error)
+ goto cleanup;
+
+ error = pdf_resolve(&id, xref);
+ if (error)
+ goto cleanup;
+
+ error = pdf_newdecrypt(&xref->crypt, encrypt, id);
+ if (error)
+ goto cleanup;
+
+ fz_dropobj(encrypt);
+ fz_dropobj(id);
+ }
+
+ return nil;
+
+cleanup:
+ if (encrypt) fz_dropobj(encrypt);
+ if (id) fz_dropobj(id);
+ return error;
+}
+
+void
+pdf_closexref(pdf_xref *xref)
+{
+ int *key;
+ void *val;
+ int i;
+
+ if (xref->store)
+ {
+ for (i = 0; i < fz_hashlen(xref->store); i++)
+ {
+ key = fz_gethashkey(xref->store, i);
+ val = fz_gethashval(xref->store, i);
+ if (val && key[2] == 0)
+ fz_dropobj((fz_obj*)val);
+ if (val && key[2] == 1)
+ fz_freebuffer((fz_buffer*)val);
+ }
+ fz_freehash(xref->store);
+ }
+
+ if (xref->table)
+ fz_free(xref->table);
+ if (xref->trailer)
+ fz_dropobj(xref->trailer);
+ if (xref->file)
+ fz_closefile(xref->file);
+ fz_free(xref);
+}
+
+void
+pdf_debugxref(pdf_xref *xref)
+{
+ int i;
+ printf("%%!PDF-%g\n", xref->version);
+ printf("xref\n0 %d\n", xref->size);
+ for (i = 0; i < xref->size; i++)
+ {
+ printf("%010d %05d %c \n",
+ xref->table[i].ofs,
+ xref->table[i].gen,
+ xref->table[i].type);
+ }
+ printf("trailer\n");
+ fz_fprintobj(stdout, xref->trailer);
+ printf("\n");
+}
+
+/*
+ * object and stream store (cached from objstm and saved for mutation)
+ */
+
+fz_obj *
+pdf_findstoredobject(fz_hashtable *store, int oid, int gid)
+{
+ int key[3];
+ key[0] = oid;
+ key[1] = gid;
+ key[2] = 0;
+ return fz_hashfind(store, key);
+}
+
+fz_buffer *
+pdf_findstoredstream(fz_hashtable *store, int oid, int gid)
+{
+ int key[3];
+ key[0] = oid;
+ key[1] = gid;
+ key[2] = 1;
+ return fz_hashfind(store, key);
+}
+
+fz_error *
+pdf_deletestoredobject(fz_hashtable *store, int oid, int gid)
+{
+ int key[3];
+ fz_obj *obj;
+ key[0] = oid;
+ key[1] = gid;
+ key[2] = 0;
+ obj = fz_hashfind(store, key);
+ if (obj)
+ fz_dropobj(obj);
+ return fz_hashremove(store, key);
+}
+
+fz_error *
+pdf_deletestoredstream(fz_hashtable *store, int oid, int gid)
+{
+ int key[3];
+ fz_buffer *stm;
+ key[0] = oid;
+ key[1] = gid;
+ key[2] = 1;
+ stm = fz_hashfind(store, key);
+ if (stm)
+ fz_freebuffer(stm);
+ return fz_hashremove(store, key);
+}
+
+fz_error *
+pdf_storeobject(fz_hashtable *store, int oid, int gid, fz_obj *obj)
+{
+ int key[3];
+ key[0] = oid;
+ key[1] = gid;
+ key[2] = 0;
+ return fz_hashinsert(store, key, obj);
+}
+
+fz_error *
+pdf_storestream(fz_hashtable *store, int oid, int gid, fz_buffer *buf)
+{
+ int key[3];
+ key[0] = oid;
+ key[1] = gid;
+ key[2] = 1;
+ return fz_hashinsert(store, key, buf);
+}
+
+/*
+ * mutate objects
+ */
+
+static int findprev(pdf_xref *xref, int oid)
+{
+ int prev;
+ for (prev = oid - 1; prev >= 0; prev--)
+ if (xref->table[prev].type == 'f' || xref->table[prev].type == 'd')
+ return prev;
+ return 0;
+}
+
+static int findnext(pdf_xref *xref, int oid)
+{
+ int next;
+ for (next = oid + 1; next < xref->size; next++)
+ if (xref->table[next].type == 'f' || xref->table[next].type == 'd')
+ return next;
+ return 0;
+}
+
+fz_error *
+pdf_createobject(pdf_xref *xref, int *oidp, int *gidp)
+{
+ pdf_xrefentry *x;
+ int prev;
+ int oid = 0;
+
+ while (1)
+ {
+ x = xref->table + oid;
+
+ if (x->type == 'f' || x->type == 'd')
+ {
+ if (x->gen < 65535)
+ {
+ *oidp = oid;
+ *gidp = x->gen;
+ return nil;
+ }
+ }
+
+ oid = x->ofs;
+
+ if (oid == 0)
+ break;
+ }
+
+ if (xref->size + 1 >= xref->capacity)
+ {
+ int newcap = xref->capacity + 256;
+ pdf_xrefentry *newtable;
+
+ newtable = fz_realloc(xref->table, sizeof(pdf_xrefentry) * newcap);
+ if (!newtable)
+ return fz_outofmem;
+
+ xref->table = newtable;
+ xref->capacity = newcap;
+ }
+
+ oid = xref->size ++;
+
+ xref->table[oid].type = 'd';
+ xref->table[oid].mark = 0;
+ xref->table[oid].ofs = 0;
+ xref->table[oid].gen = 0;
+
+ prev = findprev(xref, oid);
+ xref->table[prev].type = 'd';
+ xref->table[prev].ofs = oid;
+
+ *oidp = oid;
+ *gidp = 0;
+
+ return nil;
+}
+
+fz_error *
+pdf_deleteobject(pdf_xref *xref, int oid, int gid)
+{
+ pdf_xrefentry *x;
+ int prev;
+
+ if (oid < 0 || oid >= xref->size)
+ return fz_throw("rangecheck: invalid object number");
+
+ x = xref->table + oid;
+
+ if (x->type != 'n' && x->type != 'o' && x->type == 'a')
+ return fz_throw("rangecheck: delete nonexistant object");
+
+ x->type = 'd';
+ x->ofs = findnext(xref, oid);
+ x->gen ++;
+
+ prev = findprev(xref, oid);
+ xref->table[prev].type = 'd';
+ xref->table[prev].ofs = oid;
+
+ return nil;
+}
+
+fz_error *
+pdf_saveobject(pdf_xref *xref, int oid, int gid, fz_obj *obj)
+{
+ fz_error *error;
+ pdf_xrefentry *x;
+
+ if (oid < 0 || oid >= xref->size)
+ return fz_throw("rangecheck: invalid object number");
+
+ error = pdf_storeobject(xref->store, oid, gid, obj);
+ if (error)
+ return error;
+
+ x = xref->table + oid;
+
+ if (x->type == 'f' || x->type == 'd')
+ {
+ int prev = findprev(xref, oid);
+ int next = findnext(xref, oid);
+ xref->table[prev].type = 'd';
+ xref->table[prev].ofs = next;
+ }
+
+ x->type = 'a';
+
+ return nil;
+}
+
+/*
+ * object loading
+ */
+
+fz_error *
+pdf_loadobject0(fz_obj **objp, pdf_xref *xref, int oid, int gid, int *stmofs)
+{
+ unsigned char buf[65536]; /* yeowch! */
+
+ fz_error *error;
+ pdf_xrefentry *x;
+ int roid, rgid;
+ int n;
+
+ if (oid < 0 || oid >= xref->size)
+ return fz_throw("rangecheck: object number out of range: %d", oid);
+
+ if (stmofs)
+ *stmofs = -1;
+
+ x = &xref->table[oid];
+
+ if (x->type == 'f' || x->type == 'd')
+ return fz_throw("rangecheck: tried to load free object");
+
+ else if (x->type == 'n')
+ {
+ n = fz_seek(xref->file, x->ofs);
+ if (n < 0)
+ return fz_ferror(xref->file);
+
+ error = pdf_parseindobj(objp, xref->file, buf, sizeof buf,
+ &roid, &rgid, stmofs);
+ if (error)
+ return error;
+
+ if (xref->crypt)
+ pdf_cryptobj(xref->crypt, *objp, oid, gid);
+ }
+
+ else if (x->type == 'o')
+ {
+ *objp = pdf_findstoredobject(xref->store, oid, gid);
+ if (*objp)
+ return nil;
+
+ error = pdf_readobjstm(xref, x->ofs, 0, buf, sizeof buf);
+ if (error)
+ return error;
+
+ *objp = pdf_findstoredobject(xref->store, oid, gid);
+ if (!*objp)
+ return fz_throw("rangecheck: could not find object");
+ }
+
+ else if (x->type == 'a')
+ {
+ *objp = pdf_findstoredobject(xref->store, oid, gid);
+ if (!*objp)
+ return fz_throw("rangecheck: could not find object");
+ }
+
+ else
+ return fz_throw("rangecheck: unknown object type");
+
+ return nil;
+}
+
+fz_error *
+pdf_loadobject(fz_obj **objp, pdf_xref *xref, fz_obj *ref, int *stmofs)
+{
+ return pdf_loadobject0(objp, xref, fz_toobjid(ref), fz_togenid(ref), stmofs);
+}
+
+fz_error *
+pdf_resolve(fz_obj **objp, pdf_xref *xref)
+{
+ if (fz_isindirect(*objp))
+ return pdf_loadobject(objp, xref, *objp, nil);
+ fz_keepobj(*objp);
+ return nil;
+}
+
diff --git a/object/array.c b/object/array.c
new file mode 100644
index 00000000..d7beeb38
--- /dev/null
+++ b/object/array.c
@@ -0,0 +1,182 @@
+#include <fitz.h>
+
+fz_error *
+fz_newarray(fz_obj **op, int initialcap)
+{
+ fz_obj *obj;
+ int i;
+
+ obj = *op = fz_malloc(sizeof (fz_obj));
+ if (!obj) return fz_outofmem;
+
+ obj->kind = FZ_ARRAY;
+ obj->refcount = 1;
+
+ obj->u.a.len = 0;
+ obj->u.a.cap = initialcap > 0 ? initialcap : 6;
+
+ obj->u.a.items = fz_malloc(sizeof (fz_obj*) * obj->u.a.cap);
+ if (!obj->u.a.items) { fz_free(obj); return fz_outofmem; }
+
+ for (i = 0; i < obj->u.a.cap; i++)
+ obj->u.a.items[i] = nil;
+
+ return nil;
+}
+
+fz_error *
+fz_copyarray(fz_obj **op, fz_obj *obj)
+{
+ fz_error *err;
+ fz_obj *new;
+ int i;
+
+ if (!fz_isarray(obj))
+ return fz_throw("typecheck in copyarray");
+
+ err = fz_newarray(&new, fz_arraylen(obj));
+ if (err) return err;
+ *op = new;
+
+ for (i = 0; i < fz_arraylen(obj); i++) {
+ err = fz_arraypush(new, fz_arrayget(obj, i));
+ if (err) { fz_freearray(new); return err; }
+ }
+
+ return nil;
+}
+
+fz_error *
+fz_deepcopyarray(fz_obj **op, fz_obj *obj)
+{
+ fz_error *err;
+ fz_obj *new;
+ fz_obj *val;
+ int i;
+
+ if (!fz_isarray(obj))
+ return fz_throw("typecheck in deepcopyarray");
+
+ err = fz_newarray(&new, fz_arraylen(obj));
+ if (err) return err;
+ *op = new;
+
+ for (i = 0; i < fz_arraylen(obj); i++)
+ {
+ val = fz_arrayget(obj, i);
+
+ if (fz_isarray(val)) {
+ err = fz_deepcopyarray(&val, val);
+ if (err) { fz_freearray(new); return err; }
+ err = fz_arraypush(new, val);
+ if (err) { fz_dropobj(val); fz_freearray(new); return err; }
+ fz_dropobj(val);
+ }
+
+ else if (fz_isdict(val)) {
+ err = fz_deepcopydict(&val, val);
+ if (err) { fz_freearray(new); return err; }
+ err = fz_arraypush(new, val);
+ if (err) { fz_dropobj(val); fz_freearray(new); return err; }
+ fz_dropobj(val);
+ }
+
+ else {
+ err = fz_arraypush(new, val);
+ if (err) { fz_freearray(new); return err; }
+ }
+ }
+
+ return nil;
+}
+
+int
+fz_arraylen(fz_obj *obj)
+{
+ if (!fz_isarray(obj))
+ return 0;
+ return obj->u.a.len;
+}
+
+fz_obj *
+fz_arrayget(fz_obj *obj, int i)
+{
+ if (!fz_isarray(obj))
+ return nil;
+
+ if (i < 0 || i >= obj->u.a.len)
+ return nil;
+
+ return obj->u.a.items[i];
+}
+
+fz_error *
+fz_arrayput(fz_obj *obj, int i, fz_obj *item)
+{
+ if (!fz_isarray(obj))
+ return fz_throw("typecheck in arrayput");
+ if (i < 0)
+ return fz_throw("rangecheck in arrayput: %d < 0", i);
+ if (i >= obj->u.a.len)
+ return fz_throw("rangecheck in arrayput: %d > %d", i, obj->u.a.len);
+
+ if (obj->u.a.items[i])
+ fz_dropobj(obj->u.a.items[i]);
+ obj->u.a.items[i] = fz_keepobj(item);
+
+ return nil;
+}
+
+static fz_error *
+growarray(fz_obj *obj)
+{
+ fz_obj **newitems;
+ int newcap;
+ int i;
+
+ newcap = obj->u.a.cap * 2;
+ newitems = fz_realloc(obj->u.a.items, sizeof (fz_obj*) * newcap);
+ if (!newitems) return fz_outofmem;
+
+ obj->u.a.items = newitems;
+ for (i = obj->u.a.cap ; i < newcap; i++)
+ obj->u.a.items[i] = nil;
+ obj->u.a.cap = newcap;
+
+ return nil;
+}
+
+fz_error *
+fz_arraypush(fz_obj *obj, fz_obj *item)
+{
+ fz_error *err;
+
+ if (!fz_isarray(obj))
+ return fz_throw("typecheck in arraypush");
+
+ if (obj->u.a.len + 1 > obj->u.a.cap) {
+ err = growarray(obj);
+ if (err) return err;
+ }
+
+ obj->u.a.items[obj->u.a.len] = fz_keepobj(item);
+ obj->u.a.len++;
+
+ return nil;
+}
+
+void
+fz_freearray(fz_obj *obj)
+{
+ int i;
+
+ assert(obj->kind == FZ_ARRAY);
+
+ for (i = 0; i < obj->u.a.len; i++)
+ if (obj->u.a.items[i])
+ fz_dropobj(obj->u.a.items[i]);
+
+ fz_free(obj->u.a.items);
+ fz_free(obj);
+}
+
diff --git a/object/dict.c b/object/dict.c
new file mode 100644
index 00000000..fd2f3382
--- /dev/null
+++ b/object/dict.c
@@ -0,0 +1,260 @@
+#include <fitz.h>
+
+fz_error *
+fz_newdict(fz_obj **op, int initialcap)
+{
+ fz_obj *obj;
+ int i;
+
+ obj = *op = fz_malloc(sizeof (fz_obj));
+ if (!obj) return fz_outofmem;
+
+ obj->kind = FZ_DICT;
+ obj->refcount = 1;
+
+ obj->u.d.len = 0;
+ obj->u.d.cap = initialcap > 0 ? initialcap : 10;
+
+ obj->u.d.items = fz_malloc(sizeof(struct fz_keyval_s) * obj->u.d.cap);
+ if (!obj->u.d.items) { fz_free(obj); return fz_outofmem; }
+
+ for (i = 0; i < obj->u.d.cap; i++) {
+ obj->u.d.items[i].k = nil;
+ obj->u.d.items[i].v = nil;
+ }
+
+ return nil;
+}
+
+fz_error *
+fz_copydict(fz_obj **op, fz_obj *obj)
+{
+ fz_error *err;
+ fz_obj *new;
+ int i;
+
+ if (!fz_isdict(obj))
+ return fz_throw("typecheck in copydict");
+
+ err = fz_newdict(&new, obj->u.d.cap);
+ if (err) return err;
+ *op = new;
+
+ for (i = 0; i < fz_dictlen(obj); i++) {
+ err = fz_dictput(new, fz_dictgetkey(obj, i), fz_dictgetval(obj, i));
+ if (err) { fz_freedict(new); return err; }
+ }
+
+ return nil;
+}
+
+fz_error *
+fz_deepcopydict(fz_obj **op, fz_obj *obj)
+{
+ fz_error *err;
+ fz_obj *new;
+ fz_obj *val;
+ int i;
+
+ if (!fz_isdict(obj))
+ return fz_throw("typecheck in deepcopydict");
+
+ err = fz_newdict(&new, obj->u.d.cap);
+ if (err) return err;
+ *op = new;
+
+ for (i = 0; i < fz_dictlen(obj); i++)
+ {
+ val = fz_dictgetval(obj, i);
+
+ if (fz_isarray(val)) {
+ err = fz_deepcopyarray(&val, val);
+ if (err) { fz_freedict(new); return err; }
+ err = fz_dictput(new, fz_dictgetkey(obj, i), val);
+ if (err) { fz_dropobj(val); fz_freedict(new); return err; }
+ fz_dropobj(val);
+ }
+
+ else if (fz_isdict(val)) {
+ err = fz_deepcopydict(&val, val);
+ if (err) { fz_freedict(new); return err; }
+ err = fz_dictput(new, fz_dictgetkey(obj, i), val);
+ if (err) { fz_dropobj(val); fz_freedict(new); return err; }
+ fz_dropobj(val);
+ }
+
+ else {
+ err = fz_dictput(new, fz_dictgetkey(obj, i), val);
+ if (err) { fz_freedict(new); return err; }
+ }
+ }
+
+ return nil;
+}
+
+static fz_error *
+growdict(fz_obj *obj)
+{
+ struct fz_keyval_s *newitems;
+ int newcap;
+ int i;
+
+ newcap = obj->u.d.cap * 2;
+
+ newitems = fz_realloc(obj->u.d.items, sizeof(struct fz_keyval_s) * newcap);
+ if (!newitems) return fz_outofmem;
+
+ obj->u.d.items = newitems;
+ for (i = obj->u.d.cap; i < newcap; i++) {
+ obj->u.d.items[i].k = nil;
+ obj->u.d.items[i].v = nil;
+ }
+ obj->u.d.cap = newcap;
+
+ return nil;
+}
+
+int
+fz_dictlen(fz_obj *obj)
+{
+ if (!fz_isdict(obj))
+ return 0;
+ return obj->u.d.len;
+}
+
+fz_obj *
+fz_dictgetkey(fz_obj *obj, int i)
+{
+ if (!fz_isdict(obj))
+ return nil;
+
+ if (i < 0 || i >= obj->u.d.len)
+ return nil;
+
+ return obj->u.d.items[i].k;
+}
+
+fz_obj *
+fz_dictgetval(fz_obj *obj, int i)
+{
+ if (!fz_isdict(obj))
+ return nil;
+
+ if (i < 0 || i >= obj->u.d.len)
+ return nil;
+
+ return obj->u.d.items[i].v;
+}
+
+fz_obj *
+fz_dictgets(fz_obj *obj, char *key)
+{
+ int i;
+
+ if (!fz_isdict(obj))
+ return nil;
+
+ for (i = 0; i < obj->u.d.len; i++)
+ if (strcmp(fz_toname(obj->u.d.items[i].k), key) == 0)
+ return obj->u.d.items[i].v;
+
+ return nil;
+}
+
+fz_obj *
+fz_dictget(fz_obj *obj, fz_obj *key)
+{
+ return fz_dictgets(obj, fz_toname(key));
+}
+
+fz_error *
+fz_dictput(fz_obj *obj, fz_obj *key, fz_obj *val)
+{
+ fz_error *err;
+ int i;
+ char *s;
+
+ if (!fz_isdict(obj))
+ return fz_throw("typecheck in dictput");
+ if (!fz_isname(key))
+ return fz_throw("typecheck in dictput");
+
+ s = fz_toname(key);
+
+ for (i = 0; i < obj->u.d.len; i++) {
+ if (strcmp(fz_toname(obj->u.d.items[i].k), s) == 0) {
+ fz_dropobj(obj->u.d.items[i].v);
+ obj->u.d.items[i].v = fz_keepobj(val);
+ return nil;
+ }
+ }
+
+ if (obj->u.d.len + 1 > obj->u.d.cap) {
+ err = growdict(obj);
+ if (err) return err;
+ }
+
+ obj->u.d.items[obj->u.d.len].k = fz_keepobj(key);
+ obj->u.d.items[obj->u.d.len].v = fz_keepobj(val);
+ obj->u.d.len ++;
+
+ return nil;
+}
+
+fz_error *
+fz_dictputs(fz_obj *obj, char *key, fz_obj *val)
+{
+ fz_error *err;
+ fz_obj *keyobj;
+ err = fz_newname(&keyobj, key);
+ if (err) return err;
+ err = fz_dictput(obj, keyobj, val);
+ fz_dropobj(keyobj);
+ return err;
+}
+
+fz_error *
+fz_dictdels(fz_obj *obj, char *key)
+{
+ int i;
+
+ if (!fz_isdict(obj))
+ return fz_throw("typecheck in dictdel");
+
+ for (i = 0; i < obj->u.d.len; i++) {
+ if (strcmp(fz_toname(obj->u.d.items[i].k), key) == 0) {
+ fz_dropobj(obj->u.d.items[i].k);
+ fz_dropobj(obj->u.d.items[i].v);
+ obj->u.d.items[i] = obj->u.d.items[obj->u.d.len-1];
+ obj->u.d.len --;
+ }
+ }
+
+ return nil;
+}
+
+fz_error *
+fz_dictdel(fz_obj *obj, fz_obj *key)
+{
+ return fz_dictdels(obj, fz_toname(key));
+}
+
+void
+fz_freedict(fz_obj *obj)
+{
+ int i;
+
+ if (!fz_isdict(obj))
+ return;
+
+ for (i = 0; i < obj->u.d.len; i++) {
+ if (obj->u.d.items[i].k)
+ fz_dropobj(obj->u.d.items[i].k);
+ if (obj->u.d.items[i].v)
+ fz_dropobj(obj->u.d.items[i].v);
+ }
+
+ fz_free(obj->u.d.items);
+ fz_free(obj);
+}
+
diff --git a/object/parse.c b/object/parse.c
new file mode 100644
index 00000000..c065a132
--- /dev/null
+++ b/object/parse.c
@@ -0,0 +1,400 @@
+#include <fitz.h>
+
+struct vap { va_list ap; };
+
+static inline int iswhite(int ch)
+{
+ return
+ ch == '\000' ||
+ ch == '\011' ||
+ ch == '\012' ||
+ ch == '\014' ||
+ ch == '\015' ||
+ ch == '\040';
+}
+
+static inline int isdelim(int ch)
+{
+ return
+ ch == '(' || ch == ')' ||
+ ch == '<' || ch == '>' ||
+ ch == '[' || ch == ']' ||
+ ch == '{' || ch == '}' ||
+ ch == '/' ||
+ ch == '%';
+}
+
+static inline int isregular(int ch)
+{
+ return !isdelim(ch) && !iswhite(ch) && ch != EOF;
+}
+
+static fz_error *parseobj(fz_obj **obj, char **sp, struct vap *v);
+
+static inline int fromhex(char ch)
+{
+ if (ch >= '0' && ch <= '9')
+ return ch - '0';
+ else if (ch >= 'A' && ch <= 'F')
+ return ch - 'A' + 0xA;
+ else if (ch >= 'a' && ch <= 'f')
+ return ch - 'a' + 0xA;
+ return 0;
+}
+
+static inline void skipwhite(char **sp)
+{
+ char *s = *sp;
+ while (iswhite(*s))
+ s ++;
+ *sp = s;
+}
+
+static void parsekeyword(char **sp, char *b, char *eb)
+{
+ char *s = *sp;
+ while (b < eb && isregular(*s))
+ *b++ = *s++;
+ *b++ = 0;
+ *sp = s;
+}
+
+static fz_error *parsename(fz_obj **obj, char **sp)
+{
+ char buf[64];
+ char *s = *sp;
+ char *p = buf;
+
+ s ++; /* skip '/' */
+ while (p < buf + sizeof buf - 1 && isregular(*s))
+ *p++ = *s++;
+ *p++ = 0;
+ *sp = s;
+
+ return fz_newname(obj, buf);
+}
+
+static fz_error *parsenumber(fz_obj **obj, char **sp)
+{
+ char buf[32];
+ char *s = *sp;
+ char *p = buf;
+
+ while (p < buf + sizeof buf - 1)
+ {
+ if (s[0] == '-' || s[0] == '.' || (s[0] >= '0' && s[0] <= '9'))
+ *p++ = *s++;
+ else
+ break;
+ }
+ *p++ = 0;
+ *sp = s;
+
+ if (strchr(buf, '.'))
+ return fz_newreal(obj, atof(buf));
+ return fz_newint(obj, atoi(buf));
+}
+
+static fz_error *parsedict(fz_obj **obj, char **sp, struct vap *v)
+{
+ fz_error *err = nil;
+ fz_obj *dict = nil;
+ fz_obj *key = nil;
+ fz_obj *val = nil;
+ char *s = *sp;
+
+ err = fz_newdict(&dict, 8);
+ if (err) return err;
+ *obj = dict;
+
+ s += 2; /* skip "<<" */
+
+ while (*s)
+ {
+ skipwhite(&s);
+
+ /* end-of-dict marker >> */
+ if (*s == '>') {
+ s ++;
+ if (*s == '>') {
+ s ++;
+ break;
+ }
+ err = fz_throw("syntaxerror in parsedict");
+ goto error;
+ }
+
+ /* non-name as key, bail */
+ if (*s != '/') {
+ err = fz_throw("syntaxerror in parsedict");
+ goto error;
+ }
+
+ err = parsename(&key, &s);
+ if (err) goto error;
+
+ skipwhite(&s);
+
+ err = parseobj(&val, &s, v);
+ if (err) goto error;
+
+ err = fz_dictput(dict, key, val);
+ if (err) goto error;
+
+ fz_dropobj(val); val = nil;
+ fz_dropobj(key); key = nil;
+ }
+
+ *sp = s;
+ return nil;
+
+error:
+ if (val) fz_dropobj(val);
+ if (key) fz_dropobj(key);
+ if (dict) fz_freedict(dict);
+ *obj = nil;
+ *sp = s;
+ return err;
+}
+
+static fz_error *parsearray(fz_obj **obj, char **sp, struct vap *v)
+{
+ fz_error *err;
+ fz_obj *a;
+ fz_obj *o;
+ char *s = *sp;
+
+ err = fz_newarray(&a, 8);
+ if (err) return err;
+ *obj = a;
+
+ s ++; /* skip '[' */
+
+ while (*s)
+ {
+ skipwhite(&s);
+
+ if (*s == ']') {
+ s ++;
+ break;
+ }
+
+ err = parseobj(&o, &s, v);
+ if (err) { *obj = nil; fz_freearray(a); return err; }
+
+ err = fz_arraypush(a, o);
+ if (err) { fz_dropobj(o); *obj = nil; fz_freearray(a); return err; }
+
+ fz_dropobj(o);
+ }
+
+ *sp = s;
+ return nil;
+}
+
+static fz_error *parsestring(fz_obj **obj, char **sp)
+{
+ char buf[512];
+ char *s = *sp;
+ char *p = buf;
+ int balance = 1;
+ int oct;
+
+ s ++; /* skip '(' */
+
+ while (*s && p < buf + sizeof buf)
+ {
+ if (*s == '(')
+ {
+ balance ++;
+ *p++ = *s++;
+ }
+ else if (*s == ')')
+ {
+ balance --;
+ *p++ = *s++;
+ }
+ else if (*s == '\\')
+ {
+ s ++;
+ if (*s >= '0' && *s <= '9')
+ {
+ oct = *s - '0';
+ s ++;
+ if (*s >= '0' && *s <= '9')
+ {
+ oct = oct * 8 + (*s - '0');
+ s ++;
+ if (*s >= '0' && *s <= '9')
+ {
+ oct = oct * 8 + (*s - '0');
+ s ++;
+ }
+ }
+ *p++ = oct;
+ }
+ else switch (*s)
+ {
+ case 'n': *p++ = '\n'; s++; break;
+ case 'r': *p++ = '\r'; s++; break;
+ case 't': *p++ = '\t'; s++; break;
+ case 'b': *p++ = '\b'; s++; break;
+ case 'f': *p++ = '\f'; s++; break;
+ default: *p++ = *s++; break;
+ }
+ }
+ else
+ *p++ = *s++;
+
+ if (balance == 0)
+ break;
+ }
+
+ *sp = s;
+ return fz_newstring(obj, buf, p - buf - 1);
+}
+
+static fz_error *parsehexstring(fz_obj **obj, char **sp)
+{
+ char buf[512];
+ char *s = *sp;
+ char *p = buf;
+ int a, b;
+
+ s ++; /* skip '<' */
+
+ while (*s && p < buf + sizeof buf)
+ {
+ skipwhite(&s);
+ if (*s == '>') {
+ s ++;
+ break;
+ }
+ a = *s++;
+
+ if (*s == '\0')
+ break;
+
+ skipwhite(&s);
+ if (*s == '>') {
+ s ++;
+ break;
+ }
+ b = *s++;
+
+ *p++ = fromhex(a) * 16 + fromhex(b);
+ }
+
+ *sp = s;
+ return fz_newstring(obj, buf, p - buf);
+}
+
+static fz_error *parseobj(fz_obj **obj, char **sp, struct vap *v)
+{
+ fz_error *err;
+ char buf[32];
+ int oid, gid, len;
+ char *tmp;
+ char *s = *sp;
+
+ if (*s == '\0')
+ return fz_throw("syntaxerror in parseobj: end-of-string");
+
+ skipwhite(&s);
+
+ err = nil;
+
+ if (v != nil && *s == '%')
+ {
+ s ++;
+ switch (*s)
+ {
+ case 'o': *obj = fz_keepobj(va_arg(v->ap, fz_obj*)); break;
+ case 'b': err = fz_newbool(obj, va_arg(v->ap, int)); break;
+ case 'i': err = fz_newint(obj, va_arg(v->ap, int)); break;
+ case 'f': err = fz_newreal(obj, (float)va_arg(v->ap, double)); break;
+ case 'n': err = fz_newname(obj, va_arg(v->ap, char*)); break;
+ case 'r':
+ oid = va_arg(v->ap, int);
+ gid = va_arg(v->ap, int);
+ err = fz_newindirect(obj, oid, gid);
+ break;
+ case 's':
+ tmp = va_arg(v->ap, char*);
+ err = fz_newstring(obj, tmp, strlen(tmp));
+ break;
+ case '#':
+ tmp = va_arg(v->ap, char*);
+ len = va_arg(v->ap, int);
+ err = fz_newstring(obj, tmp, len);
+ break;
+ default:
+ err = fz_throw("unknown format specifier in packobj: '%c'", *s);
+ break;
+ }
+ s ++;
+ }
+
+ else if (*s == '/')
+ err = parsename(obj, &s);
+
+ else if (*s == '(')
+ err = parsestring(obj, &s);
+
+ else if (*s == '<') {
+ if (s[1] == '<')
+ err = parsedict(obj, &s, v);
+ else
+ err = parsehexstring(obj, &s);
+ }
+
+ else if (*s == '[')
+ err = parsearray(obj, &s, v);
+
+ else if (*s == '-' || *s == '.' || (*s >= '0' && *s <= '9'))
+ err = parsenumber(obj, &s);
+
+ else if (isregular(*s))
+ {
+ parsekeyword(&s, buf, buf + sizeof buf);
+
+ if (strcmp("true", buf) == 0)
+ err = fz_newbool(obj, 1);
+ else if (strcmp("false", buf) == 0)
+ err = fz_newbool(obj, 0);
+ else if (strcmp("null", buf) == 0)
+ err = fz_newnull(obj);
+ else
+ err = fz_throw("syntaxerror in parseobj: undefined keyword %s", buf);
+ }
+
+ else
+ err = fz_throw("syntaxerror in parseobj");
+
+ *sp = s;
+ return err;
+}
+
+fz_error *
+fz_packobj(fz_obj **op, char *fmt, ...)
+{
+ fz_error *err;
+ struct vap v;
+ va_list ap;
+
+ va_start(ap, fmt);
+ va_copy(v.ap, ap);
+
+ err = parseobj(op, &fmt, &v);
+
+ va_end(ap);
+
+ return err;
+}
+
+fz_error *
+fz_parseobj(fz_obj **op, char *str)
+{
+ return parseobj(op, &str, nil);
+}
+
diff --git a/object/print.c b/object/print.c
new file mode 100644
index 00000000..af6941f9
--- /dev/null
+++ b/object/print.c
@@ -0,0 +1,332 @@
+#include <fitz.h>
+
+struct fmt
+{
+ FILE *file;
+ char *buf;
+ int len;
+
+ int indent;
+ int tight;
+ int col;
+ int sep;
+ int last;
+};
+
+static void fmtobj(struct fmt *fmt, fz_obj *obj);
+
+static inline int isdelim(int ch)
+{
+ return ch == '(' || ch == ')' ||
+ ch == '<' || ch == '>' ||
+ ch == '[' || ch == ']' ||
+ ch == '{' || ch == '}' ||
+ ch == '/' ||
+ ch == '%';
+}
+
+static inline void fmtputc(struct fmt *fmt, int c)
+{
+ if (fmt->sep && !isdelim(fmt->last) && !isdelim(c)) {
+ fmt->sep = 0;
+ fmtputc(fmt, ' ');
+ }
+ fmt->sep = 0;
+
+ if (fmt->file)
+ putc(c, fmt->file);
+ if (fmt->buf)
+ fmt->buf[fmt->len] = c;
+
+ if (c == '\n')
+ fmt->col = 0;
+ else
+ fmt->col ++;
+
+ fmt->len ++;
+
+ fmt->last = c;
+}
+
+static void fmtindent(struct fmt *fmt)
+{
+ int i = fmt->indent;
+ while (i--) {
+ fmtputc(fmt, ' ');
+ fmtputc(fmt, ' ');
+ }
+}
+
+static inline void fmtputs(struct fmt *fmt, char *s)
+{
+ while (*s)
+ fmtputc(fmt, *s++);
+}
+
+static inline void fmtsep(struct fmt *fmt)
+{
+ fmt->sep = 1;
+}
+
+static void fmtstr(struct fmt *fmt, fz_obj *obj)
+{
+ int i;
+ int c;
+
+ fmtputc(fmt, '(');
+ for (i = 0; i < obj->u.s.len; i++)
+ {
+ c = obj->u.s.buf[i];
+ if (c == '\n')
+ fmtputs(fmt, "\\n");
+ else if (c == '\r')
+ fmtputs(fmt, "\\r");
+ else if (c == '\t')
+ fmtputs(fmt, "\\t");
+ else if (c == '\b')
+ fmtputs(fmt, "\\b");
+ else if (c == '\f')
+ fmtputs(fmt, "\\f");
+ else if (c == '(')
+ fmtputs(fmt, "\\(");
+ else if (c == ')')
+ fmtputs(fmt, "\\)");
+ else if (c < 32 || c > 126) {
+ char buf[16];
+ fmtputc(fmt, '\\');
+ sprintf(buf, "%o", c);
+ fmtputs(fmt, buf);
+ //fmtputc(fmt, ((c >> 6) & 7) + '0');
+ //fmtputc(fmt, ((c >> 3) & 7) + '0');
+ //fmtputc(fmt, ((c) & 7) + '0');
+ }
+ else
+ fmtputc(fmt, c);
+ }
+ fmtputc(fmt, ')');
+}
+
+static void fmthex(struct fmt *fmt, fz_obj *obj)
+{
+ int i;
+ int c;
+
+ fmtputc(fmt, '<');
+ for (i = 0; i < obj->u.s.len; i++) {
+ c = (obj->u.s.buf[i] >> 4) & 0x0f;
+ fmtputc(fmt, c < 0xA ? c + '0' : c + 'A' - 0xA);
+ c = (obj->u.s.buf[i]) & 0x0f;
+ fmtputc(fmt, c < 0xA ? c + '0' : c + 'A' - 0xA);
+ }
+ fmtputc(fmt, '>');
+}
+
+static void fmtarray(struct fmt *fmt, fz_obj *obj)
+{
+ int i;
+
+ if (fmt->tight) {
+ fmtputc(fmt, '[');
+ for (i = 0; i < fz_arraylen(obj); i++) {
+ fmtobj(fmt, fz_arrayget(obj, i));
+ fmtsep(fmt);
+ }
+ fmtputc(fmt, ']');
+ }
+ else {
+ fmtputs(fmt, "[ ");
+ for (i = 0; i < fz_arraylen(obj); i++) {
+ if (fmt->col > 60) {
+ fmtputc(fmt, '\n');
+ fmtindent(fmt);
+ }
+ fmtobj(fmt, fz_arrayget(obj, i));
+ fmtputc(fmt, ' ');
+ }
+ fmtputc(fmt, ']');
+ fmtsep(fmt);
+ }
+}
+
+static void fmtdict(struct fmt *fmt, fz_obj *obj)
+{
+ int i;
+ fz_obj *key, *val;
+
+ if (fmt->tight) {
+ fmtputs(fmt, "<<");
+ for (i = 0; i < fz_dictlen(obj); i++) {
+ fmtobj(fmt, fz_dictgetkey(obj, i));
+ fmtsep(fmt);
+ fmtobj(fmt, fz_dictgetval(obj, i));
+ fmtsep(fmt);
+ }
+ fmtputs(fmt, ">>");
+ }
+ else {
+ fmtputs(fmt, "<<\n");
+ fmt->indent ++;
+ for (i = 0; i < fz_dictlen(obj); i++) {
+ key = fz_dictgetkey(obj, i);
+ val = fz_dictgetval(obj, i);
+ fmtindent(fmt);
+ fmtobj(fmt, key);
+ fmtputc(fmt, ' ');
+ if (fz_isarray(val))
+ fmt->indent ++;
+ fmtobj(fmt, val);
+ fmtputc(fmt, '\n');
+ if (fz_isarray(val))
+ fmt->indent --;
+ }
+ fmt->indent --;
+ fmtindent(fmt);
+ fmtputs(fmt, ">>");
+ }
+}
+
+static void fmtobj(struct fmt *fmt, fz_obj *obj)
+{
+ char buf[256];
+
+ if (!obj) {
+ fmtputs(fmt, "<nil>");
+ return;
+ }
+
+ switch (obj->kind)
+ {
+ case FZ_NULL:
+ fmtputs(fmt, "null");
+ break;
+ case FZ_BOOL:
+ fmtputs(fmt, fz_tobool(obj) ? "true" : "false");
+ break;
+ case FZ_INT:
+ sprintf(buf, "%d", fz_toint(obj));
+ fmtputs(fmt, buf);
+ break;
+ case FZ_REAL:
+ sprintf(buf, "%g", fz_toreal(obj));
+ if (strchr(buf, 'e')) /* bad news! */
+ sprintf(buf, fabs(fz_toreal(obj)) > 1 ? "%1.1f" : "%1.8f", fz_toreal(obj));
+ fmtputs(fmt, buf);
+ break;
+ case FZ_STRING:
+ {
+ int added = 0;
+ int i, c;
+ for (i = 0; i < obj->u.s.len; i++) {
+ c = (unsigned char)obj->u.s.buf[i];
+ if (strchr("()\\\n\r\t\b\f", c) != 0)
+ added ++;
+ else if (c < 8)
+ added ++;
+ else if (c < 32)
+ added += 2;
+ else if (c >= 127)
+ added += 3;
+ }
+ if (added < obj->u.s.len)
+ fmtstr(fmt, obj);
+ else
+ fmthex(fmt, obj);
+ }
+ break;
+ case FZ_NAME:
+ sprintf(buf, "/%s", fz_toname(obj));
+ fmtputs(fmt, buf);
+ break;
+ case FZ_ARRAY:
+ fmtarray(fmt, obj);
+ break;
+ case FZ_DICT:
+ fmtdict(fmt, obj);
+ break;
+ case FZ_INDIRECT:
+ sprintf(buf, "%d %d R", obj->u.r.oid, obj->u.r.gid);
+ fmtputs(fmt, buf);
+ break;
+ case FZ_POINTER:
+ sprintf(buf, "$%p", obj->u.p);
+ fmtputs(fmt, buf);
+ break;
+ default:
+ sprintf(buf, "<unknown object type %d>", obj->kind);
+ fmtputs(fmt, buf);
+ break;
+ }
+}
+
+int
+fz_sprintobj(char *s, fz_obj *obj)
+{
+ struct fmt fmt;
+
+ fmt.indent = 0;
+ fmt.tight = 0;
+ fmt.col = 0;
+ fmt.sep = 0;
+ fmt.last = 0;
+
+ fmt.file = nil;
+ fmt.buf = s;
+ fmt.len = 0;
+ fmtobj(&fmt, obj);
+ return fmt.len;
+}
+
+int
+fz_sprintcobj(char *s, fz_obj *obj)
+{
+ struct fmt fmt;
+
+ fmt.indent = 0;
+ fmt.tight = 1;
+ fmt.col = 0;
+ fmt.sep = 0;
+ fmt.last = 0;
+
+ fmt.file = nil;
+ fmt.buf = s;
+ fmt.len = 0;
+ fmtobj(&fmt, obj);
+ return fmt.len;
+}
+
+int
+fz_fprintobj(FILE *file, fz_obj *obj)
+{
+ struct fmt fmt;
+
+ fmt.indent = 0;
+ fmt.tight = 0;
+ fmt.col = 0;
+ fmt.sep = 0;
+ fmt.last = 0;
+
+ fmt.file = file;
+ fmt.buf = nil;
+ fmt.len = 0;
+ fmtobj(&fmt, obj);
+ return fmt.len;
+}
+
+int
+fz_fprintcobj(FILE *file, fz_obj *obj)
+{
+ struct fmt fmt;
+
+ fmt.indent = 0;
+ fmt.tight = 1;
+ fmt.col = 0;
+ fmt.sep = 0;
+ fmt.last = 0;
+
+ fmt.file = file;
+ fmt.buf = nil;
+ fmt.len = 0;
+ fmtobj(&fmt, obj);
+ return fmt.len;
+}
+
diff --git a/object/simple.c b/object/simple.c
new file mode 100644
index 00000000..02da3cec
--- /dev/null
+++ b/object/simple.c
@@ -0,0 +1,230 @@
+#include <fitz.h>
+
+#define NEWOBJ(KIND,SIZE) \
+ fz_obj *o; \
+ o = *op = fz_malloc(SIZE); \
+ if (!o) return fz_outofmem; \
+ o->kind = KIND; \
+ o->refcount = 1
+
+fz_error *
+fz_newnull(fz_obj **op)
+{
+ NEWOBJ(FZ_NULL, sizeof (fz_obj));
+ return nil;
+}
+
+fz_error *
+fz_newbool(fz_obj **op, int b)
+{
+ NEWOBJ(FZ_BOOL, sizeof (fz_obj));
+ o->u.b = b;
+ return nil;
+}
+
+fz_error *
+fz_newint(fz_obj **op, int i)
+{
+ NEWOBJ(FZ_INT, sizeof (fz_obj));
+ o->u.i = i;
+ return nil;
+}
+
+fz_error *
+fz_newreal(fz_obj **op, float f)
+{
+ NEWOBJ(FZ_REAL, sizeof (fz_obj));
+ o->u.f = f;
+ return nil;
+}
+
+fz_error *
+fz_newstring(fz_obj **op, char *str, int len)
+{
+ NEWOBJ(FZ_STRING, offsetof(fz_obj, u.s.buf) + len);
+ o->u.s.len = len;
+ memcpy(o->u.s.buf, str, len);
+ return nil;
+}
+
+fz_error *
+fz_newname(fz_obj **op, char *str)
+{
+ NEWOBJ(FZ_NAME, offsetof(fz_obj, u.n) + strlen(str) + 1);
+ strcpy(o->u.n, str);
+ return nil;
+}
+
+fz_error *
+fz_newindirect(fz_obj **op, int objid, int genid)
+{
+ NEWOBJ(FZ_INDIRECT, sizeof (fz_obj));
+ o->u.r.oid = objid;
+ o->u.r.gid = genid;
+ return nil;
+}
+
+fz_error *
+fz_newpointer(fz_obj **op, void *p)
+{
+ NEWOBJ(FZ_POINTER, sizeof (fz_obj));
+ o->u.p = p;
+ return nil;
+}
+
+fz_obj *
+fz_keepobj(fz_obj *o)
+{
+ o->refcount ++;
+ return o;
+}
+
+fz_obj *
+fz_dropobj(fz_obj *o)
+{
+ o->refcount --;
+ if (o->refcount == 0) {
+ if (o->kind == FZ_ARRAY)
+ fz_freearray(o);
+ else if (o->kind == FZ_DICT)
+ fz_freedict(o);
+ else
+ fz_free(o);
+ }
+ return nil;
+}
+
+int
+fz_isnull(fz_obj *obj)
+{
+ return obj ? obj->kind == FZ_NULL : 0;
+}
+
+int
+fz_isbool(fz_obj *obj)
+{
+ return obj ? obj->kind == FZ_BOOL : 0;
+}
+
+int
+fz_isint(fz_obj *obj)
+{
+ return obj ? obj->kind == FZ_INT : 0;
+}
+
+int
+fz_isreal(fz_obj *obj)
+{
+ return obj ? obj->kind == FZ_REAL : 0;
+}
+
+int
+fz_isstring(fz_obj *obj)
+{
+ return obj ? obj->kind == FZ_STRING : 0;
+}
+
+int
+fz_isname(fz_obj *obj)
+{
+ return obj ? obj->kind == FZ_NAME : 0;
+}
+
+int
+fz_isarray(fz_obj *obj)
+{
+ return obj ? obj->kind == FZ_ARRAY : 0;
+}
+
+int
+fz_isdict(fz_obj *obj)
+{
+ return obj ? obj->kind == FZ_DICT : 0;
+}
+
+int
+fz_isindirect(fz_obj *obj)
+{
+ return obj ? obj->kind == FZ_INDIRECT : 0;
+}
+
+int
+fz_ispointer(fz_obj *obj)
+{
+ return obj ? obj->kind == FZ_POINTER : 0;
+}
+
+int
+fz_tobool(fz_obj *obj)
+{
+ if (fz_isbool(obj))
+ return obj->u.b;
+ return 0;
+}
+
+int
+fz_toint(fz_obj *obj)
+{
+ if (fz_isint(obj))
+ return obj->u.i;
+ return 0;
+}
+
+float
+fz_toreal(fz_obj *obj)
+{
+ if (fz_isreal(obj))
+ return obj->u.f;
+ if (fz_isint(obj))
+ return obj->u.i;
+ return 0;
+}
+
+char *
+fz_toname(fz_obj *obj)
+{
+ if (fz_isname(obj))
+ return obj->u.n;
+ return "";
+}
+
+char *
+fz_tostringbuf(fz_obj *obj)
+{
+ if (fz_isstring(obj))
+ return obj->u.s.buf;
+ return "";
+}
+
+int
+fz_tostringlen(fz_obj *obj)
+{
+ if (fz_isstring(obj))
+ return obj->u.s.len;
+ return 0;
+}
+
+int
+fz_toobjid(fz_obj *obj)
+{
+ if (fz_isindirect(obj))
+ return obj->u.r.oid;
+ return 0;
+}
+
+int
+fz_togenid(fz_obj *obj)
+{
+ if (fz_isindirect(obj))
+ return obj->u.r.gid;
+ return 0;
+}
+
+void *
+fz_topointer(fz_obj *obj)
+{
+ if (fz_ispointer(obj))
+ return obj->u.p;
+ return nil;
+}
+
diff --git a/render/edgelist.c b/render/edgelist.c
new file mode 100644
index 00000000..d62ffad4
--- /dev/null
+++ b/render/edgelist.c
@@ -0,0 +1,288 @@
+#include <fitz.h>
+
+/*
+ * Global Edge List -- list of straight path segments for scan conversion
+ *
+ * Stepping along the edges is with bresenham's line algorithm.
+ *
+ * See Mike Abrash -- Graphics Programming Black Book (notably chapter 40)
+ */
+
+fz_error *
+fz_newgel(fz_gel **gelp)
+{
+ fz_gel *gel;
+
+ gel = *gelp = fz_malloc(sizeof(fz_gel));
+ if (!gel)
+ return fz_outofmem;
+
+ gel->edges = nil;
+
+ gel->cap = 512;
+ gel->len = 0;
+ gel->edges = fz_malloc(sizeof(fz_edge) * gel->cap);
+ if (!gel->edges) {
+ fz_free(gel);
+ return fz_outofmem;
+ }
+
+ gel->xmin = gel->ymin = INT_MAX;
+ gel->xmax = gel->ymax = INT_MIN;
+ gel->hs = 1;
+ gel->vs = 1;
+
+ return nil;
+}
+
+void
+fz_resetgel(fz_gel *gel, int hs, int vs)
+{
+ gel->xmin = gel->ymin = INT_MAX;
+ gel->xmax = gel->ymax = INT_MIN;
+ gel->hs = hs;
+ gel->vs = vs;
+ gel->len = 0;
+}
+
+void
+fz_freegel(fz_gel *gel)
+{
+ fz_free(gel->edges);
+ fz_free(gel);
+}
+
+fz_error *
+fz_insertgel(fz_gel *gel, float fx0, float fy0, float fx1, float fy1)
+{
+ fz_edge *edge;
+ int dx, dy;
+ int winding;
+ int width;
+ int tmp;
+
+ fx0 *= gel->hs;
+ fy0 *= gel->vs;
+ fx1 *= gel->hs;
+ fy1 *= gel->vs;
+
+ /* TODO: should we round or truncate? */
+ int x0 = fx0 < 0 ? fx0 - 0.5 : fx0 + 0.5;
+ int y0 = fy0 < 0 ? fy0 - 0.5 : fy0 + 0.5;
+ int x1 = fx1 < 0 ? fx1 - 0.5 : fx1 + 0.5;
+ int y1 = fy1 < 0 ? fy1 - 0.5 : fy1 + 0.5;
+
+ if (y0 == y1)
+ return nil;
+
+ if (y0 > y1) {
+ winding = -1;
+ tmp = x0; x0 = x1; x1 = tmp;
+ tmp = y0; y0 = y1; y1 = tmp;
+ }
+ else
+ winding = 1;
+
+ if (x0 < gel->xmin) gel->xmin = x0;
+ if (x0 > gel->xmax) gel->xmax = x0;
+ if (x1 < gel->xmin) gel->xmin = x1;
+ if (x1 > gel->xmax) gel->xmax = x1;
+
+ if (y0 < gel->ymin) gel->ymin = y0;
+ if (y1 > gel->ymax) gel->ymax = y1;
+
+ if (gel->len + 1 == gel->cap) {
+ int newcap = gel->cap + 512;
+ fz_edge *newedges = fz_realloc(gel->edges, sizeof(fz_edge) * newcap);
+ if (!newedges)
+ return fz_outofmem;
+ gel->cap = newcap;
+ gel->edges = newedges;
+ }
+
+ edge = &gel->edges[gel->len++];
+
+ dy = y1 - y0;
+ dx = x1 - x0;
+ width = dx < 0 ? -dx : dx;
+
+ edge->xdir = dx > 0 ? 1 : -1;
+ edge->ydir = winding;
+ edge->x = x0;
+ edge->y = y0;
+ edge->h = dy;
+ edge->adjdown = dy;
+
+ /* initial error term going l->r and r->l */
+ if (dx >= 0)
+ edge->e = 0;
+ else
+ edge->e = -dy + 1;
+
+ /* y-major edge */
+ if (dy >= width) {
+ edge->xmove = 0;
+ edge->adjup = width;
+ }
+
+ /* x-major edge */
+ else {
+ edge->xmove = (width / dy) * edge->xdir;
+ edge->adjup = width % dy;
+ }
+
+ return nil;
+}
+
+void
+fz_sortgel(fz_gel *gel)
+{
+ fz_edge *a = gel->edges;
+ int n = gel->len;
+
+ int h, i, k;
+ fz_edge t;
+
+ h = 1;
+ if (n < 14) {
+ h = 1;
+ }
+ else {
+ while (h < n)
+ h = 3 * h + 1;
+ h /= 3;
+ h /= 3;
+ }
+
+ while (h > 0)
+ {
+ for (i = 0; i < n; i++) {
+ t = a[i];
+ k = i - h;
+ /* TODO: sort on y major, x minor */
+ while (k >= 0 && a[k].y > t.y) {
+ a[k + h] = a[k];
+ k -= h;
+ }
+ a[k + h] = t;
+ }
+
+ h /= 3;
+ }
+}
+
+/*
+ * Active Edge List -- keep track of active edges while sweeping
+ */
+
+fz_error *
+fz_newael(fz_ael **aelp)
+{
+ fz_ael *ael;
+
+ ael = *aelp = fz_malloc(sizeof(fz_ael));
+ if (!ael)
+ return fz_outofmem;
+
+ ael->cap = 64;
+ ael->len = 0;
+ ael->edges = fz_malloc(sizeof(fz_edge*) * ael->cap);
+ if (!ael->edges) {
+ fz_free(ael);
+ return fz_outofmem;
+ }
+
+ return nil;
+}
+
+void
+fz_freeael(fz_ael *ael)
+{
+ fz_free(ael->edges);
+ fz_free(ael);
+}
+
+static inline void
+sortael(fz_edge **a, int n)
+{
+ int h, i, k;
+ fz_edge *t;
+
+ h = 1;
+ if (n < 14) {
+ h = 1;
+ }
+ else {
+ while (h < n)
+ h = 3 * h + 1;
+ h /= 3;
+ h /= 3;
+ }
+
+ while (h > 0)
+ {
+ for (i = 0; i < n; i++) {
+ t = a[i];
+ k = i - h;
+ while (k >= 0 && a[k]->x > t->x) {
+ a[k + h] = a[k];
+ k -= h;
+ }
+ a[k + h] = t;
+ }
+
+ h /= 3;
+ }
+}
+
+fz_error *
+fz_insertael(fz_ael *ael, fz_gel *gel, int y, int *e)
+{
+ /* insert edges that start here */
+ while (*e < gel->len && gel->edges[*e].y == y) {
+ if (ael->len + 1 == ael->cap) {
+ int newcap = ael->cap + 64;
+ fz_edge **newedges = fz_realloc(ael->edges, sizeof(fz_edge*) * newcap);
+ if (!newedges)
+ return fz_outofmem;
+ ael->edges = newedges;
+ ael->cap = newcap;
+ }
+ ael->edges[ael->len++] = &gel->edges[(*e)++];
+ }
+
+ /* shell-sort the edges by increasing x */
+ sortael(ael->edges, ael->len);
+
+ return nil;
+}
+
+void
+fz_advanceael(fz_ael *ael)
+{
+ fz_edge *edge;
+ int i = 0;
+
+ while (i < ael->len)
+ {
+ edge = ael->edges[i];
+
+ edge->h --;
+
+ /* terminator! */
+ if (edge->h == 0) {
+ ael->edges[i] = ael->edges[--ael->len];
+ }
+
+ else {
+ edge->x += edge->xmove;
+ edge->e += edge->adjup;
+ if (edge->e > 0) {
+ edge->x += edge->xdir;
+ edge->e -= edge->adjdown;
+ }
+ i ++;
+ }
+ }
+}
+
diff --git a/render/fill.c b/render/fill.c
new file mode 100644
index 00000000..4605726d
--- /dev/null
+++ b/render/fill.c
@@ -0,0 +1,123 @@
+#include <fitz.h>
+
+static fz_error *
+line(fz_gel *gel, fz_matrix *ctm, float x0, float y0, float x1, float y1)
+{
+ float tx0 = ctm->a * x0 + ctm->c * y0 + ctm->e;
+ float ty0 = ctm->b * x0 + ctm->d * y0 + ctm->f;
+ float tx1 = ctm->a * x1 + ctm->c * y1 + ctm->e;
+ float ty1 = ctm->b * x1 + ctm->d * y1 + ctm->f;
+ return fz_insertgel(gel, tx0, ty0, tx1, ty1);
+}
+
+static fz_error *
+bezier(fz_gel *gel, fz_matrix *ctm, float flatness,
+ float xa, float ya,
+ float xb, float yb,
+ float xc, float yc,
+ float xd, float yd)
+{
+ fz_error *error;
+ float dmax;
+ float xab, yab;
+ float xbc, ybc;
+ float xcd, ycd;
+ float xabc, yabc;
+ float xbcd, ybcd;
+ float xabcd, yabcd;
+
+ /* termination check */
+ dmax = ABS(xa - xb);
+ dmax = MAX(dmax, ABS(ya - yb));
+ dmax = MAX(dmax, ABS(xd - xc));
+ dmax = MAX(dmax, ABS(yd - yc));
+ if (dmax < flatness)
+ return line(gel, ctm, xa, ya, xd, yd);
+
+ xab = xa + xb;
+ yab = ya + yb;
+ xbc = xb + xc;
+ ybc = yb + yc;
+ xcd = xc + xd;
+ ycd = yc + yd;
+
+ xabc = xab + xbc;
+ yabc = yab + ybc;
+ xbcd = xbc + xcd;
+ ybcd = ybc + ycd;
+
+ xabcd = xabc + xbcd;
+ yabcd = yabc + ybcd;
+
+ xab *= 0.5f; yab *= 0.5f;
+ xbc *= 0.5f; ybc *= 0.5f;
+ xcd *= 0.5f; ycd *= 0.5f;
+
+ xabc *= 0.25f; yabc *= 0.25f;
+ xbcd *= 0.25f; ybcd *= 0.25f;
+
+ xabcd *= 0.125f; yabcd *= 0.125f;
+
+ error = bezier(gel, ctm, flatness, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd);
+ if (error)
+ return error;
+ return bezier(gel, ctm, flatness, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd);
+}
+
+fz_error *
+fz_fillpath(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness)
+{
+ fz_error *error;
+ float x1, y1, x2, y2, x3, y3;
+ float cx = 0;
+ float cy = 0;
+ float bx = 0;
+ float by = 0;
+ int i = 0;
+
+ while (i < path->len)
+ {
+ switch (path->els[i++].k)
+ {
+ case FZ_MOVETO:
+ x1 = path->els[i++].v;
+ y1 = path->els[i++].v;
+ cx = bx = x1;
+ cy = by = y1;
+ break;
+
+ case FZ_LINETO:
+ x1 = path->els[i++].v;
+ y1 = path->els[i++].v;
+ error = line(gel, &ctm, cx, cy, x1, y1);
+ if (error)
+ return error;
+ cx = x1;
+ cy = y1;
+ break;
+
+ case FZ_CURVETO:
+ x1 = path->els[i++].v;
+ y1 = path->els[i++].v;
+ x2 = path->els[i++].v;
+ y2 = path->els[i++].v;
+ x3 = path->els[i++].v;
+ y3 = path->els[i++].v;
+ error = bezier(gel, &ctm, flatness, cx, cy, x1, y1, x2, y2, x3, y3);
+ if (error)
+ return error;
+ cx = x3;
+ cy = y3;
+ break;
+
+ case FZ_CLOSEPATH:
+ error = line(gel, &ctm, cx, cy, bx, by);
+ if (error)
+ return error;
+ break;
+ }
+ }
+
+ return nil;
+}
+
diff --git a/render/glyphcache.c b/render/glyphcache.c
new file mode 100644
index 00000000..81b1f5d5
--- /dev/null
+++ b/render/glyphcache.c
@@ -0,0 +1,370 @@
+#include <fitz.h>
+
+#define HSUBPIX 5.0
+#define VSUBPIX 5.0
+
+typedef struct fz_hash_s fz_hash;
+typedef struct fz_key_s fz_key;
+typedef struct fz_val_s fz_val;
+
+struct fz_glyphcache_s
+{
+ int slots;
+ int size;
+ fz_hash *hash;
+ fz_val *lru;
+ unsigned char *buffer;
+ int load;
+ int used;
+};
+
+struct fz_key_s
+{
+ void *fid;
+ int a, b;
+ int c, d;
+ unsigned short gid;
+ unsigned char e, f;
+};
+
+struct fz_hash_s
+{
+ fz_key key;
+ fz_val *val;
+};
+
+struct fz_val_s
+{
+ fz_hash *ent;
+ unsigned char *data;
+ short w, h, lsb, top;
+ int uses;
+};
+
+static unsigned int hashkey(fz_key *key)
+{
+ unsigned char *s = (char*)key;
+ unsigned int hash = 0;
+ unsigned int i;
+ for (i = 0; i < sizeof(fz_key); i++)
+ {
+ hash += s[i];
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+ return hash;
+}
+
+fz_error *
+fz_newglyphcache(fz_glyphcache **arenap, int slots, int size)
+{
+ fz_glyphcache *arena;
+
+ arena = *arenap = fz_malloc(sizeof(fz_glyphcache));
+ if (!arena)
+ return fz_outofmem;
+
+ arena->slots = slots;
+ arena->size = size;
+
+ arena->hash = nil;
+ arena->lru = nil;
+ arena->buffer = nil;
+
+ arena->hash = fz_malloc(sizeof(fz_hash) * slots);
+ if (!arena->hash)
+ goto cleanup;
+
+ arena->lru = fz_malloc(sizeof(fz_val) * slots);
+ if (!arena->lru)
+ goto cleanup;
+
+ arena->buffer = fz_malloc(size);
+ if (!arena->buffer)
+ goto cleanup;
+
+ memset(arena->hash, 0, sizeof(fz_hash) * slots);
+ memset(arena->lru, 0, sizeof(fz_val) * slots);
+ memset(arena->buffer, 0, size);
+ arena->load = 0;
+ arena->used = 0;
+
+ return nil;
+
+cleanup:
+ fz_free(arena->hash);
+ fz_free(arena->lru);
+ fz_free(arena->buffer);
+ fz_free(arena);
+ return fz_outofmem;
+}
+
+void
+fz_freeglyphcache(fz_glyphcache *arena)
+{
+ fz_free(arena->hash);
+ fz_free(arena->lru);
+ fz_free(arena->buffer);
+ fz_free(arena);
+}
+
+static int hokay = 0;
+static int hcoll = 0;
+static int hdist = 0;
+static int coos = 0;
+static int covf = 0;
+
+static fz_val *
+hashfind(fz_glyphcache *arena, fz_key *key)
+{
+ fz_hash *tab = arena->hash;
+ int pos = hashkey(key) % arena->slots;
+
+ while (1)
+ {
+ if (!tab[pos].val)
+ return nil;
+
+ if (memcmp(key, &tab[pos].key, sizeof (fz_key)) == 0)
+ return tab[pos].val;
+
+ pos = pos + 1;
+ if (pos == arena->slots)
+ pos = 0;
+ }
+}
+
+static void
+hashinsert(fz_glyphcache *arena, fz_key *key, fz_val *val)
+{
+ fz_hash *tab = arena->hash;
+ int pos = hashkey(key) % arena->slots;
+int didcoll = 0;
+
+ while (1)
+ {
+ if (!tab[pos].val)
+ {
+ tab[pos].key = *key;
+ tab[pos].val = val;
+ tab[pos].val->ent = &tab[pos];
+if (didcoll) hcoll ++; else hokay ++; hdist += didcoll;
+ return;
+ }
+
+ pos = pos + 1;
+ if (pos == arena->slots)
+ pos = 0;
+didcoll ++;
+ }
+}
+
+static void
+hashremove(fz_glyphcache *arena, fz_key *key)
+{
+ fz_hash *tab = arena->hash;
+ unsigned int pos = hashkey(key) % arena->slots;
+ unsigned int hole;
+ unsigned int look;
+ unsigned int code;
+
+ while (1)
+ {
+ if (!tab[pos].val)
+ return; /* boo hoo! tried to remove non-existant key */
+
+ if (memcmp(key, &tab[pos].key, sizeof (fz_key)) == 0)
+ {
+ tab[pos].val = nil;
+
+ hole = pos;
+ look = hole + 1;
+ if (look == arena->slots)
+ look = 0;
+
+ while (tab[look].val)
+ {
+ code = (hashkey(&tab[look].key) % arena->slots);
+ if ((code <= hole && hole < look) ||
+ (look < code && code <= hole) ||
+ (hole < look && look < code))
+ {
+ tab[hole] = tab[look];
+ tab[hole].val->ent = &tab[hole];
+ tab[look].val = nil;
+ hole = look;
+ }
+
+ look = look + 1;
+ if (look == arena->slots)
+ look = 0;
+ }
+
+ return;
+ }
+
+ pos = pos + 1;
+ if (pos == arena->slots)
+ pos = 0;
+ }
+}
+
+void
+fz_debugglyphcache(fz_glyphcache *arena)
+{
+ int i;
+
+ printf("cache load %d / %d (%d / %d bytes)\n",
+ arena->load, arena->slots, arena->used, arena->size);
+ printf("no-colliders: %d colliders: %d\n", hokay, hcoll);
+ printf("avg dist: %d / %d: %g\n", hdist, hcoll, (double)hdist / hcoll);
+ printf("out-of-space evicts: %d\n", coos);
+ printf("out-of-hash evicts: %d\n", covf);
+
+ for (i = 0; i < arena->slots; i++)
+ {
+ if (!arena->hash[i].val)
+ printf("glyph % 4d: empty\n", i);
+ else {
+ fz_key *k = &arena->hash[i].key;
+ fz_val *b = arena->hash[i].val;
+ printf("glyph % 4d: %p %d [%g %g %g %g + %d %d] "
+ "-> [%dx%d %d,%d]\n", i,
+ k->fid, k->gid,
+ k->a / 65536.0,
+ k->b / 65536.0,
+ k->c / 65536.0,
+ k->d / 65536.0,
+ k->e, k->f,
+ b->w, b->h, b->lsb, b->top);
+ }
+ }
+
+ for (i = 0; i < arena->load; i++)
+ printf("lru %04d: glyph %d (%d)\n", i,
+ arena->lru[i].ent - arena->hash, arena->lru[i].uses);
+}
+
+static void
+bubble(fz_glyphcache *arena, int i)
+{
+ fz_val tmp;
+
+ if (i == 0 || arena->load < 2)
+ return;
+
+ tmp = arena->lru[i - 1];
+ arena->lru[i - 1] = arena->lru[i];
+ arena->lru[i] = tmp;
+
+ arena->lru[i - 1].ent->val = &arena->lru[i - 1];
+ arena->lru[i].ent->val = &arena->lru[i];
+}
+
+static void
+evictlast(fz_glyphcache *arena)
+{
+ fz_val *lru = arena->lru;
+ unsigned char *s, *e;
+ int i, k;
+ fz_key key;
+
+ if (arena->load == 0)
+ return;
+
+ k = arena->load - 1;
+ s = lru[k].data;
+ e = s + lru[k].w * lru[k].h;
+
+ /* pack buffer to fill hole */
+ memmove(s, e, arena->buffer + arena->used - e);
+ memset(arena->buffer + arena->used - (e - s), 0, e - s);
+ arena->used -= e - s;
+
+ /* update lru pointers */
+ for (i = 0; i < k; i++)
+ if (lru[i].data >= e)
+ lru[i].data -= e - s;
+
+ /* remove hash entry */
+ key = lru[k].ent->key;
+ hashremove(arena, &key);
+
+ arena->load --;
+}
+
+fz_error *
+fz_renderglyph(fz_glyphcache *arena, fz_glyph *glyph, fz_font *font, int gid, fz_matrix ctm)
+{
+ fz_error *error;
+ fz_key key;
+ fz_val *val;
+ int size;
+
+ key.fid = font;
+ key.gid = gid;
+ key.a = ctm.a * 65536;
+ key.b = ctm.b * 65536;
+ key.c = ctm.c * 65536;
+ key.d = ctm.d * 65536;
+ key.e = (ctm.e - floor(ctm.e)) * HSUBPIX;
+ key.f = (ctm.f - floor(ctm.f)) * VSUBPIX;
+
+ val = hashfind(arena, &key);
+ if (val)
+ {
+ val->uses ++;
+ glyph->w = val->w;
+ glyph->h = val->h;
+ glyph->lsb = val->lsb;
+ glyph->top = val->top;
+ glyph->bitmap = val->data;
+
+ bubble(arena, val - arena->lru);
+
+ return nil;
+ }
+
+ ctm.e = floor(ctm.e) + key.e / HSUBPIX;
+ ctm.f = floor(ctm.f) + key.f / HSUBPIX;
+
+ error = font->render(glyph, font, gid, ctm);
+ if (error)
+ return error;
+
+ size = glyph->w * glyph->h;
+
+ if (size > arena->size / 6)
+ return nil;
+
+ while (arena->load > arena->slots * 75 / 100) {
+ covf ++;
+ evictlast(arena);
+ }
+
+ while (arena->used + size >= arena->size) {
+ coos ++;
+ evictlast(arena);
+ }
+
+ val = &arena->lru[arena->load++];
+ val->uses = 0;
+ val->w = glyph->w;
+ val->h = glyph->h;
+ val->lsb = glyph->lsb;
+ val->top = glyph->top;
+ val->data = arena->buffer + arena->used;
+
+ arena->used += size;
+
+ memcpy(val->data, glyph->bitmap, glyph->w * glyph->h);
+ glyph->bitmap = val->data;
+
+ hashinsert(arena, &key, val);
+
+ return nil;
+}
+
diff --git a/render/pixmap.c b/render/pixmap.c
new file mode 100644
index 00000000..57402d81
--- /dev/null
+++ b/render/pixmap.c
@@ -0,0 +1,60 @@
+#include <fitz.h>
+
+fz_error *
+fz_newpixmap(fz_pixmap **pixp, int x, int y, int w, int h, int n, int a)
+{
+ fz_pixmap *pix;
+
+ pix = *pixp = fz_malloc(sizeof (fz_pixmap));
+ if (!pix)
+ return fz_outofmem;
+
+ pix->x = x;
+ pix->y = y;
+ pix->w = w;
+ pix->h = h;
+ pix->n = n;
+ pix->a = a;
+ pix->cs = nil;
+ pix->stride = (pix->n + pix->a) * pix->w;
+
+ pix->samples = fz_malloc(sizeof(short) * pix->stride * pix->h);
+ if (!pix->samples) {
+ fz_free(pix);
+ return fz_outofmem;
+ }
+
+ memset(pix->samples, 0, sizeof(short) * pix->stride * pix->h);
+
+ return nil;
+}
+
+void
+fz_freepixmap(fz_pixmap *pix)
+{
+ fz_free(pix->samples);
+ fz_free(pix);
+}
+
+void
+fz_clearpixmap(fz_pixmap *pix)
+{
+ memset(pix->samples, 0, sizeof(short) * pix->stride * pix->h);
+}
+
+void
+fz_debugpixmap(fz_pixmap *pix)
+{
+ int x, y;
+ FILE *f = fopen("out.ppm", "w");
+ fprintf(f, "P6\n%d %d\n255\n", pix->w, pix->h);
+ for (y = 0; y < pix->h; y++)
+ for (x = 0; x < pix->w; x++)
+ {
+ putc(255 - pix->samples[x + y * pix->stride], f);
+ putc(255 - pix->samples[x + y * pix->stride], f);
+ putc(255 - pix->samples[x + y * pix->stride], f);
+ }
+ fclose(f);
+}
+
diff --git a/render/porterduff.c b/render/porterduff.c
new file mode 100644
index 00000000..1cedbc38
--- /dev/null
+++ b/render/porterduff.c
@@ -0,0 +1,74 @@
+#include <fitz.h>
+
+/* Porter-Duff compositing arithmetic on premultiplied ARGB buffers */
+
+void
+fz_blendover(unsigned char *C, unsigned char *A, unsigned char *B, int n)
+{
+ while (n--)
+ {
+ unsigned char Fa = 255;
+ unsigned char Fb = 255 - A[0];
+ *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb);
+ *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb);
+ *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb);
+ *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb);
+ }
+}
+
+void
+fz_blendin(unsigned char *C, unsigned char *A, unsigned char *B, int n)
+{
+ while (n--)
+ {
+ unsigned char Fa = B[0];
+ unsigned char Fb = 0;
+ *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb);
+ *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb);
+ *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb);
+ *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb);
+ }
+}
+
+void
+fz_blendout(unsigned char *C, unsigned char *A, unsigned char *B, int n)
+{
+ while (n--)
+ {
+ unsigned char Fa = 255 - B[0];
+ unsigned char Fb = 0;
+ *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb);
+ *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb);
+ *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb);
+ *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb);
+ }
+}
+
+void
+fz_blendatop(unsigned char *C, unsigned char *A, unsigned char *B, int n)
+{
+ while (n--)
+ {
+ unsigned char Fa = B[0];
+ unsigned char Fb = 255 - A[0];
+ *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb);
+ *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb);
+ *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb);
+ *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb);
+ }
+}
+
+void
+fz_blendxor(unsigned char *C, unsigned char *A, unsigned char *B, int n)
+{
+ while (n--)
+ {
+ unsigned char Fa = 255 - B[0];
+ unsigned char Fb = 255 - A[0];
+ *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb);
+ *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb);
+ *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb);
+ *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb);
+ }
+}
+
diff --git a/render/render.c b/render/render.c
new file mode 100644
index 00000000..64856dd2
--- /dev/null
+++ b/render/render.c
@@ -0,0 +1,183 @@
+#include <fitz.h>
+
+struct fz_renderer_s
+{
+ fz_glyphcache *cache;
+ fz_gel *gel;
+ fz_ael *ael;
+};
+
+fz_error *
+fz_newrenderer(fz_renderer **gcp)
+{
+ fz_error *error;
+ fz_renderer *gc;
+
+ gc = *gcp = fz_malloc(sizeof(fz_renderer));
+ if (!gc)
+ return fz_outofmem;
+
+ gc->cache = nil;
+ gc->gel = nil;
+ gc->ael = nil;
+
+ error = fz_newglyphcache(&gc->cache, 1024, 65536);
+ if (error)
+ goto cleanup;
+
+ error = fz_newgel(&gc->gel);
+ if (error)
+ goto cleanup;
+
+ error = fz_newael(&gc->ael);
+ if (error)
+ goto cleanup;
+
+ return nil;
+
+cleanup:
+ if (gc->cache)
+ fz_freeglyphcache(gc->cache);
+ if (gc->gel)
+ fz_freegel(gc->gel);
+ if (gc->ael)
+ fz_freeael(gc->ael);
+ fz_free(gc);
+
+ return error;
+}
+
+void
+fz_freerenderer(fz_renderer *gc)
+{
+ if (gc->cache)
+ fz_freeglyphcache(gc->cache);
+ if (gc->gel)
+ fz_freegel(gc->gel);
+ if (gc->ael)
+ fz_freeael(gc->ael);
+ fz_free(gc);
+}
+
+fz_error *
+fz_renderover(fz_renderer *gc, fz_over *over, fz_matrix ctm, fz_pixmap *out)
+{
+ fz_error *error;
+ fz_node *node;
+
+ for (node = over->child; node; node = node->next)
+ {
+ error = fz_rendernode(gc, node, ctm, out);
+ if (error)
+ return error;
+ }
+
+ return nil;
+}
+
+fz_error *
+fz_rendermask(fz_renderer *gc, fz_mask *mask, fz_matrix ctm, fz_pixmap *out)
+{
+ fz_error *error;
+ fz_node *node;
+
+ for (node = mask->child; node; node = node->next)
+ {
+ error = fz_rendernode(gc, node, ctm, out);
+ if (error)
+ return error;
+ }
+
+ return nil;
+}
+
+fz_error *
+fz_rendertransform(fz_renderer *gc, fz_transform *xform, fz_matrix ctm, fz_pixmap *out)
+{
+ ctm = fz_concat(ctm, xform->m);
+ return fz_rendernode(gc, xform->child, ctm, out);
+}
+
+
+void composite(fz_pixmap *out, fz_glyph *gl, int xo, int yo)
+{
+ int sx, sy, dx, dy, a, b, c;
+
+ for (sy = 0; sy < gl->h; sy++)
+ {
+ for (sx = 0; sx < gl->w; sx++)
+ {
+ dx = xo + sx + gl->lsb;
+ dy = yo - sy + gl->top;
+
+ if (dx < 0) continue;
+ if (dy < 0) continue;
+ if (dx >= out->w) continue;
+ if (dy >= out->h) continue;
+
+ a = gl->bitmap[sx + sy * gl->w];
+ b = out->samples[dx + dy * out->stride];
+ c = MAX(a, b);
+ out->samples[dx + dy * out->stride] = c;
+ }
+ }
+}
+
+fz_error *
+fz_rendertext(fz_renderer *gc, fz_text *text, fz_matrix ctm, fz_pixmap *out)
+{
+ fz_error *error;
+ fz_glyph gl;
+ float x, y;
+ int g, i, ix, iy;
+ fz_matrix tm, trm;
+
+ tm = text->trm;
+
+ for (i = 0; i < text->len; i++)
+ {
+ g = text->els[i].g;
+ x = text->els[i].x;
+ y = text->els[i].y;
+
+ tm.e = x;
+ tm.f = y;
+ trm = fz_concat(tm, ctm);
+
+ ix = floor(trm.e);
+ iy = floor(trm.f);
+
+ trm.e = (trm.e - floor(trm.e));
+ trm.f = (trm.f - floor(trm.f));
+
+ error = fz_renderglyph(gc->cache, &gl, text->font, g, trm);
+ if (error)
+ return error;
+
+ composite(out, &gl, ix, iy);
+ }
+
+ return nil;
+}
+
+fz_error *
+fz_rendernode(fz_renderer *gc, fz_node *node, fz_matrix ctm, fz_pixmap *out)
+{
+ if (!node)
+ return nil;
+
+ switch (node->kind)
+ {
+ case FZ_NOVER:
+ return fz_renderover(gc, (fz_over*)node, ctm, out);
+ case FZ_NMASK:
+ return fz_rendermask(gc, (fz_mask*)node, ctm, out);
+ case FZ_NTRANSFORM:
+ return fz_rendertransform(gc, (fz_transform*)node, ctm, out);
+ case FZ_NTEXT:
+ return fz_rendertext(gc, (fz_text*)node, ctm, out);
+ default:
+ return nil;
+ }
+}
+
diff --git a/render/scanconv.c b/render/scanconv.c
new file mode 100644
index 00000000..b2d32363
--- /dev/null
+++ b/render/scanconv.c
@@ -0,0 +1,176 @@
+#include <fitz.h>
+
+static inline void
+addspan(short *list, int x0, int x1, int xofs, int hs)
+{
+ int x0pix, x0sub;
+ int x1pix, x1sub;
+
+ if (x0 == x1)
+ return;
+
+ /* x between 0 and width of bbox */
+ x0 -= xofs;
+ x1 -= xofs;
+
+ x0pix = x0 / hs;
+ x0sub = x0 % hs;
+ x1pix = x1 / hs;
+ x1sub = x1 % hs;
+
+ if (x0pix == x1pix)
+ {
+ list[x0pix] += x1sub - x0sub;
+ list[x0pix+1] += x0sub - x1sub;
+ }
+
+ else
+ {
+ list[x0pix] += hs - x0sub;
+ list[x0pix+1] += x0sub;
+ list[x1pix] += x1sub - hs;
+ list[x1pix+1] += -x1sub;
+ }
+}
+
+static inline void
+nonzerowinding(fz_ael *ael, short *list, int xofs, int hs)
+{
+ int winding = 0;
+ int x = 0;
+ int i;
+ for (i = 0; i < ael->len; i++)
+ {
+ if (!winding && (winding + ael->edges[i]->ydir))
+ x = ael->edges[i]->x;
+ if (winding && !(winding + ael->edges[i]->ydir))
+ addspan(list, x, ael->edges[i]->x, xofs, hs);
+ winding += ael->edges[i]->ydir;
+ }
+}
+
+static inline void
+evenodd(fz_ael *ael, short *list, int xofs, int hs)
+{
+ int even = 0;
+ int x = 0;
+ int i;
+ for (i = 0; i < ael->len; i++)
+ {
+ if (!even)
+ x = ael->edges[i]->x;
+ else
+ addspan(list, x, ael->edges[i]->x, xofs, hs);
+ even = !even;
+ }
+}
+
+/* XXX */
+
+unsigned char pixmap[1000 * 1000];
+
+void
+fz_emitdeltas(short *list, int y, int x, int n)
+{
+ short a = 0;
+ short d = 0;
+ while (n--) {
+ d = *list++;
+ a += d;
+ pixmap[y * 1000 + x] = 0xff - a;
+ x ++;
+ }
+}
+
+void
+savestuff(void)
+{
+ FILE *f = fopen("out.pgm", "w");
+ fprintf(f, "P5\n1000 1000\n255\n");
+ fwrite(pixmap, 1, 1000 * 1000, f);
+ fclose(f);
+}
+
+/* XXX */
+
+void
+fz_emitdeltas0(short *list, int y, int xofs, int n)
+{
+ int d = 0;
+ while (n--)
+ d += *list++;
+}
+
+fz_error *
+fz_scanconvert(fz_gel *gel, fz_ael *ael, int eofill)
+{
+ fz_error *error;
+ short *deltas;
+ int y, e;
+ int yd, yc;
+
+ int xmin = fz_idiv(gel->xmin, gel->hs);
+ int xmax = fz_idiv(gel->xmax, gel->hs) + 1;
+ int ymin = fz_idiv(gel->ymin, gel->vs);
+ int ymax = fz_idiv(gel->ymax, gel->vs) + 1;
+
+ int xofs = xmin * gel->hs;
+ int hs = gel->hs;
+ int vs = gel->vs;
+
+ if (gel->len == 0)
+ return nil;
+
+memset(pixmap, 0xff, sizeof pixmap);
+
+printf("bbox [%d %d %d %d] samp %d/%d edges %d\n",
+ xmin, ymin, xmax, ymax, hs, vs, gel->len);
+
+ deltas = fz_malloc(sizeof(short) * (xmax - xmin));
+ if (!deltas)
+ return fz_outofmem;
+ memset(deltas, 0, sizeof(short) * (xmax - xmin));
+
+ e = 0;
+ y = gel->edges[0].y;
+ yc = fz_idiv(y, vs);
+ yd = yc;
+
+ while (ael->len > 0 || e < gel->len)
+ {
+ yc = fz_idiv(y, vs);
+ if (yc != yd) {
+ fz_emitdeltas(deltas, yd, xmin, xmax - xmin);
+ memset(deltas, 0, sizeof(short) * (xmax - xmin));
+ }
+ yd = yc;
+
+ error = fz_insertael(ael, gel, y, &e);
+ if (error) {
+ fz_free(deltas);
+ return error;
+ }
+
+// { int i; for (i = 0; i < ael->len; i++)
+// pixmap[yd * 1000 + (ael->edges[i]->x / hs)] = 0; }
+
+ if (eofill)
+ evenodd(ael, deltas, xofs, hs);
+ else
+ nonzerowinding(ael, deltas, xofs, hs);
+
+ fz_advanceael(ael);
+
+ if (ael->len > 0)
+ y ++;
+ else if (e < gel->len)
+ y = gel->edges[e].y;
+ }
+
+ fz_emitdeltas(deltas, yd, xmin, xmax - xmin);
+
+ savestuff();
+
+ return nil;
+}
+
diff --git a/render/stroke.c b/render/stroke.c
new file mode 100644
index 00000000..1203f71b
--- /dev/null
+++ b/render/stroke.c
@@ -0,0 +1,716 @@
+#include <fitz.h>
+
+enum { BUTT = 0, ROUND = 1, SQUARE = 2, MITER = 0, BEVEL = 2 };
+
+struct sctx
+{
+ fz_gel *gel;
+ fz_matrix *ctm;
+ float flatness;
+
+ fz_stroke *stroke;
+ fz_point beg[2];
+ fz_point seg[2];
+ int sn, bn;
+ int dot;
+
+ fz_dash *dash;
+ int toggle;
+ int offset;
+ float phase;
+ fz_point cur;
+};
+
+static fz_error *
+line(struct sctx *s, float x0, float y0, float x1, float y1)
+{
+ float tx0 = s->ctm->a * x0 + s->ctm->c * y0 + s->ctm->e;
+ float ty0 = s->ctm->b * x0 + s->ctm->d * y0 + s->ctm->f;
+ float tx1 = s->ctm->a * x1 + s->ctm->c * y1 + s->ctm->e;
+ float ty1 = s->ctm->b * x1 + s->ctm->d * y1 + s->ctm->f;
+ return fz_insertgel(s->gel, tx0, ty0, tx1, ty1);
+}
+
+static fz_error *
+arc(struct sctx *s,
+ float xc, float yc,
+ float x0, float y0,
+ float x1, float y1)
+{
+ fz_error *error;
+ float th0, th1, r;
+ float theta;
+ float ox, oy, nx, ny;
+ int n, i;
+
+ r = fabs(s->stroke->linewidth);
+ theta = 2 * M_SQRT2 * sqrt(s->flatness / r);
+ th0 = atan2(y0, x0);
+ th1 = atan2(y1, x1);
+
+ if (r > 0)
+ {
+ if (th0 < th1)
+ th0 += M_PI * 2;
+ n = ceil((th0 - th1) / theta);
+ }
+ else
+ {
+ if (th1 < th0)
+ th1 += M_PI * 2;
+ n = ceil((th1 - th0) / theta);
+ }
+
+ ox = x0;
+ oy = y0;
+ for (i = 1; i < n; i++)
+ {
+ theta = th0 + (th1 - th0) * i / n;
+ nx = cos(theta) * r;
+ ny = sin(theta) * r;
+ error = line(s, xc + ox, yc + oy, xc + nx, yc + ny);
+ if (error) return error;
+ ox = nx;
+ oy = ny;
+ }
+
+ error = line(s, xc + ox, yc + oy, xc + x1, yc + y1);
+ if (error) return error;
+
+ return nil;
+}
+
+static fz_error *
+linestroke(struct sctx *s, fz_point a, fz_point b)
+{
+ fz_error *error;
+
+ float dx = b.x - a.x;
+ float dy = b.y - a.y;
+ float scale = s->stroke->linewidth / sqrt(dx * dx + dy * dy);
+ float dlx = dy * scale;
+ float dly = -dx * scale;
+
+ error = line(s, a.x - dlx, a.y - dly, b.x - dlx, b.y - dly);
+ if (error) return error;
+
+ error = line(s, b.x + dlx, b.y + dly, a.x + dlx, a.y + dly);
+ if (error) return error;
+
+ return nil;
+}
+
+static fz_error *
+linejoin(struct sctx *s, fz_point a, fz_point b, fz_point c)
+{
+ fz_error *error;
+ float miterlimit = s->stroke->miterlimit;
+ float linewidth = s->stroke->linewidth;
+ int linejoin = s->stroke->linejoin;
+ float dx0, dy0;
+ float dx1, dy1;
+ float dlx0, dly0;
+ float dlx1, dly1;
+ float dmx, dmy;
+ float dmr2;
+ float scale;
+ float cross;
+
+ dx0 = b.x - a.x;
+ dy0 = b.y - a.y;
+
+ dx1 = c.x - b.x;
+ dy1 = c.y - b.y;
+
+ if (dx0 * dx0 + dy0 * dy0 < FLT_EPSILON)
+ return nil;
+ if (dx1 * dx1 + dy1 * dy1 < FLT_EPSILON)
+ return nil;
+
+ scale = linewidth / sqrt(dx0 * dx0 + dy0 * dy0);
+ dlx0 = dy0 * scale;
+ dly0 = -dx0 * scale;
+
+ scale = linewidth / sqrt(dx1 * dx1 + dy1 * dy1);
+ dlx1 = dy1 * scale;
+ dly1 = -dx1 * scale;
+
+ cross = dx1 * dy0 - dx0 * dy1;
+
+ dmx = (dlx0 + dlx1) * 0.5;
+ dmy = (dly0 + dly1) * 0.5;
+ dmr2 = dmx * dmx + dmy * dmy;
+
+ if (cross * cross < FLT_EPSILON && dx0 * dx1 + dy0 * dy1 >= 0)
+ linejoin = BEVEL;
+
+ if (linejoin == MITER)
+ if (dmr2 * miterlimit * miterlimit < linewidth * linewidth)
+ linejoin = BEVEL;
+
+ if (linejoin == BEVEL)
+ {
+ error = line(s, b.x - dlx0, b.y - dly0, b.x - dlx1, b.y - dly1);
+ if (error) return error;
+ error = line(s, b.x + dlx1, b.y + dly1, b.x + dlx0, b.y + dly0);
+ if (error) return error;
+ }
+
+ if (linejoin == MITER)
+ {
+ scale = linewidth * linewidth / dmr2;
+ dmx *= scale;
+ dmy *= scale;
+
+ if (cross < 0)
+ {
+ error = line(s, b.x - dlx0, b.y - dly0, b.x - dlx1, b.y - dly1);
+ if (error) return error;
+ error = line(s, b.x + dlx1, b.y + dly1, b.x + dmx, b.y + dmy);
+ if (error) return error;
+ error = line(s, b.x + dmx, b.y + dmy, b.x + dlx0, b.y + dly0);
+ if (error) return error;
+ }
+ else
+ {
+ error = line(s, b.x + dlx1, b.y + dly1, b.x + dlx0, b.y + dly0);
+ if (error) return error;
+ error = line(s, b.x - dlx0, b.y - dly0, b.x - dmx, b.y - dmy);
+ if (error) return error;
+ error = line(s, b.x - dmx, b.y - dmy, b.x - dlx1, b.y - dly1);
+ if (error) return error;
+ }
+ }
+
+ if (linejoin == ROUND)
+ {
+ if (cross < 0)
+ {
+ error = line(s, b.x - dlx0, b.y - dly0, b.x - dlx1, b.y - dly1);
+ if (error) return error;
+ error = arc(s, b.x, b.y, dlx1, dly1, dlx0, dly0);
+ if (error) return error;
+ }
+ else
+ {
+ error = line(s, b.x + dlx1, b.y + dly1, b.x + dlx0, b.y + dly0);
+ if (error) return error;
+ error = arc(s, b.x, b.y, -dlx0, -dly0, -dlx1, -dly1);
+ if (error) return error;
+ }
+ }
+
+ return nil;
+}
+
+static fz_error *
+linecap(struct sctx *s, fz_point a, fz_point b)
+{
+ fz_error *error;
+ float flatness = s->flatness;
+ float linewidth = s->stroke->linewidth;
+ int linecap = s->stroke->linecap;
+
+ float dx = b.x - a.x;
+ float dy = b.y - a.y;
+
+ float scale = linewidth / sqrt(dx * dx + dy * dy);
+ float dlx = dy * scale;
+ float dly = -dx * scale;
+
+ if (linecap == BUTT)
+ return line(s, b.x - dlx, b.y - dly, b.x + dlx, b.y + dly);
+
+ if (linecap == ROUND)
+ {
+ int i;
+ int n = ceil(M_PI / (2.0 * M_SQRT2 * sqrt(flatness / linewidth)));
+ float ox = b.x - dlx;
+ float oy = b.y - dly;
+ for (i = 1; i < n; i++)
+ {
+ float theta = M_PI * i / n;
+ float cth = cos(theta);
+ float sth = sin(theta);
+ float nx = b.x - dlx * cth - dly * sth;
+ float ny = b.y - dly * cth + dlx * sth;
+ error = line(s, ox, oy, nx, ny);
+ if (error) return error;
+ ox = nx;
+ oy = ny;
+ }
+ error = line(s, ox, oy, b.x + dlx, b.y + dly);
+ if (error) return error;
+ }
+
+ if (linecap == SQUARE)
+ {
+ error = line(s, b.x - dlx, b.y - dly,
+ b.x - dlx - dly,
+ b.y - dly + dlx);
+ if (error) return error;
+ error = line(s, b.x - dlx - dly,
+ b.y - dly + dlx,
+ b.x + dlx - dly,
+ b.y + dly + dlx);
+ if (error) return error;
+ error = line(s, b.x + dlx - dly,
+ b.y + dly + dlx,
+ b.x + dlx, b.y + dly);
+ if (error) return error;
+ }
+
+ return nil;
+}
+
+static fz_error *
+linedot(struct sctx *s, fz_point a)
+{
+ fz_error *error;
+ float flatness = s->flatness;
+ float linewidth = s->stroke->linewidth;
+ int n = ceil(M_PI / (M_SQRT2 * sqrt(flatness / linewidth)));
+ float ox = a.x - linewidth;
+ float oy = a.y;
+ int i;
+ for (i = 1; i < n; i++)
+ {
+ float theta = M_PI * 2 * i / n;
+ float cth = cos(theta);
+ float sth = sin(theta);
+ float nx = a.x - cth * linewidth;
+ float ny = a.y + sth * linewidth;
+ error = line(s, ox, oy, nx, ny);
+ if (error) return error;
+ ox = nx;
+ oy = ny;
+ }
+ error = line(s, ox, oy, a.x - linewidth, a.y);
+ if (error) return error;
+ return nil;
+}
+
+static fz_error *
+strokeflush(struct sctx *s)
+{
+ fz_error *error;
+
+ if (s->sn == 2)
+ {
+ error = linecap(s, s->beg[1], s->beg[0]);
+ if (error) return error;
+ error = linecap(s, s->seg[0], s->seg[1]);
+ if (error) return error;
+ }
+ else if (s->dot)
+ {
+ error = linedot(s, s->beg[0]);
+ if (error) return error;
+ }
+
+ s->dot = 0;
+
+ return nil;
+}
+
+static fz_error *
+strokemoveto(struct sctx *s, fz_point cur)
+{
+ fz_error *error;
+
+ error = strokeflush(s);
+ if (error) return error;
+
+ s->seg[0] = cur;
+ s->beg[0] = cur;
+ s->sn = 1;
+ s->bn = 1;
+
+ return nil;
+}
+
+static fz_error *
+strokelineto(struct sctx *s, fz_point cur)
+{
+ fz_error *error;
+
+ float dx = cur.x - s->seg[s->sn-1].x;
+ float dy = cur.y - s->seg[s->sn-1].y;
+
+ if (dx * dx + dy * dy < s->flatness * s->flatness * 0.25)
+ {
+ s->dot = 1;
+ return nil;
+ }
+
+ error = linestroke(s, s->seg[s->sn-1], cur);
+ if (error) return error;
+
+ if (s->sn == 2)
+ {
+ error = linejoin(s, s->seg[0], s->seg[1], cur);
+ if (error) return error;
+
+ s->seg[0] = s->seg[1];
+ s->seg[1] = cur;
+ }
+
+ if (s->sn == 1)
+ s->seg[s->sn++] = cur;
+ if (s->bn == 1)
+ s->beg[s->bn++] = cur;
+
+ return nil;
+}
+
+static fz_error *
+strokeclosepath(struct sctx *s)
+{
+ fz_error *error;
+
+ if (s->sn == 2)
+ {
+ error = strokelineto(s, s->beg[0]);
+ if (error) return error;
+
+ if (s->seg[1].x == s->beg[0].x && s->seg[1].y == s->beg[0].y)
+ error = linejoin(s, s->seg[0], s->beg[0], s->beg[1]);
+ else
+ error = linejoin(s, s->seg[1], s->beg[0], s->beg[1]);
+ if (error) return error;
+ }
+
+ else if (s->dot)
+ {
+ error = linedot(s, s->beg[0]);
+ if (error) return error;
+ }
+
+ s->bn = 0;
+ s->sn = 0;
+ s->dot = 0;
+
+ return nil;
+}
+
+static fz_error *
+strokebezier(struct sctx *s,
+ float xa, float ya,
+ float xb, float yb,
+ float xc, float yc,
+ float xd, float yd)
+{
+ fz_error *error;
+ float dmax;
+ float xab, yab;
+ float xbc, ybc;
+ float xcd, ycd;
+ float xabc, yabc;
+ float xbcd, ybcd;
+ float xabcd, yabcd;
+
+ /* termination check */
+ dmax = ABS(xa - xb);
+ dmax = MAX(dmax, ABS(ya - yb));
+ dmax = MAX(dmax, ABS(xd - xc));
+ dmax = MAX(dmax, ABS(yd - yc));
+ if (dmax < s->flatness) {
+ fz_point p;
+ p.x = xd;
+ p.y = yd;
+ return strokelineto(s, p);
+ }
+
+ xab = xa + xb;
+ yab = ya + yb;
+ xbc = xb + xc;
+ ybc = yb + yc;
+ xcd = xc + xd;
+ ycd = yc + yd;
+
+ xabc = xab + xbc;
+ yabc = yab + ybc;
+ xbcd = xbc + xcd;
+ ybcd = ybc + ycd;
+
+ xabcd = xabc + xbcd;
+ yabcd = yabc + ybcd;
+
+ xab *= 0.5f; yab *= 0.5f;
+ xbc *= 0.5f; ybc *= 0.5f;
+ xcd *= 0.5f; ycd *= 0.5f;
+
+ xabc *= 0.25f; yabc *= 0.25f;
+ xbcd *= 0.25f; ybcd *= 0.25f;
+
+ xabcd *= 0.125f; yabcd *= 0.125f;
+
+ error = strokebezier(s, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd);
+ if (error)
+ return error;
+
+ return strokebezier(s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd);
+}
+
+fz_error *
+fz_strokepath(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness)
+{
+ fz_error *error;
+ struct sctx s;
+ fz_point p0, p1, p2, p3;
+ int i;
+
+ s.gel = gel;
+ s.ctm = &ctm;
+ s.flatness = flatness;
+
+ s.stroke = path->stroke;
+ s.sn = 0;
+ s.bn = 0;
+ s.dot = 0;
+
+ i = 0;
+
+ while (i < path->len)
+ {
+ switch (path->els[i++].k)
+ {
+ case FZ_MOVETO:
+ p1.x = path->els[i++].v;
+ p1.y = path->els[i++].v;
+ error = strokemoveto(&s, p1);
+ if (error)
+ return error;
+ p0 = p1;
+ break;
+
+ case FZ_LINETO:
+ p1.x = path->els[i++].v;
+ p1.y = path->els[i++].v;
+ error = strokelineto(&s, p1);
+ if (error)
+ return error;
+ p0 = p1;
+ break;
+
+ case FZ_CURVETO:
+ p1.x = path->els[i++].v;
+ p1.y = path->els[i++].v;
+ p2.x = path->els[i++].v;
+ p2.y = path->els[i++].v;
+ p3.x = path->els[i++].v;
+ p3.y = path->els[i++].v;
+ error = strokebezier(&s, p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
+ if (error)
+ return error;
+ p0 = p3;
+ break;
+
+ case FZ_CLOSEPATH:
+ error = strokeclosepath(&s);
+ if (error)
+ return error;
+ break;
+ }
+ }
+
+ return strokeflush(&s);
+}
+
+static fz_error *
+dashmoveto(struct sctx *s, fz_point a)
+{
+ s->toggle = 1;
+ s->offset = 0;
+ s->phase = s->dash->phase;
+
+ while (s->phase >= s->dash->array[s->offset])
+ {
+ s->toggle = !s->toggle;
+ s->phase -= s->dash->array[s->offset];
+ s->offset ++;
+ if (s->offset == s->dash->len)
+ s->offset = 0;
+ }
+
+ s->cur = a;
+
+ if (s->toggle)
+ return strokemoveto(s, a);
+
+ return nil;
+}
+
+static fz_error *
+dashlineto(struct sctx *s, fz_point b)
+{
+ fz_error *error;
+ float dx, dy;
+ float total, used, ratio;
+ fz_point a;
+ fz_point m;
+
+ a = s->cur;
+ dx = b.x - a.x;
+ dy = b.y - a.y;
+ total = sqrt(dx * dx + dy * dy);
+ used = 0;
+
+ while (total - used > s->dash->array[s->offset] - s->phase)
+ {
+ used += s->dash->array[s->offset] - s->phase;
+ ratio = used / total;
+ m.x = a.x + ratio * dx;
+ m.y = a.y + ratio * dy;
+
+ if (s->toggle)
+ error = strokelineto(s, m);
+ else
+ error = strokemoveto(s, m);
+ if (error)
+ return error;
+
+ s->toggle = !s->toggle;
+ s->phase = 0;
+ s->offset ++;
+ if (s->offset == s->dash->len)
+ s->offset = 0;
+ }
+
+ s->phase += total - used;
+
+ s->cur = b;
+
+ if (s->toggle)
+ return strokelineto(s, b);
+
+ return nil;
+}
+
+static fz_error *
+dashbezier(struct sctx *s,
+ float xa, float ya,
+ float xb, float yb,
+ float xc, float yc,
+ float xd, float yd)
+{
+ fz_error *error;
+ float dmax;
+ float xab, yab;
+ float xbc, ybc;
+ float xcd, ycd;
+ float xabc, yabc;
+ float xbcd, ybcd;
+ float xabcd, yabcd;
+
+ /* termination check */
+ dmax = ABS(xa - xb);
+ dmax = MAX(dmax, ABS(ya - yb));
+ dmax = MAX(dmax, ABS(xd - xc));
+ dmax = MAX(dmax, ABS(yd - yc));
+ if (dmax < s->flatness) {
+ fz_point p;
+ p.x = xd;
+ p.y = yd;
+ return dashlineto(s, p);
+ }
+
+ xab = xa + xb;
+ yab = ya + yb;
+ xbc = xb + xc;
+ ybc = yb + yc;
+ xcd = xc + xd;
+ ycd = yc + yd;
+
+ xabc = xab + xbc;
+ yabc = yab + ybc;
+ xbcd = xbc + xcd;
+ ybcd = ybc + ycd;
+
+ xabcd = xabc + xbcd;
+ yabcd = yabc + ybcd;
+
+ xab *= 0.5f; yab *= 0.5f;
+ xbc *= 0.5f; ybc *= 0.5f;
+ xcd *= 0.5f; ycd *= 0.5f;
+
+ xabc *= 0.25f; yabc *= 0.25f;
+ xbcd *= 0.25f; ybcd *= 0.25f;
+
+ xabcd *= 0.125f; yabcd *= 0.125f;
+
+ error = dashbezier(s, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd);
+ if (error) return error;
+ return dashbezier(s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd);
+}
+
+fz_error *
+fz_dashpath(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness)
+{
+ fz_error *error;
+ struct sctx s;
+ fz_point p0, p1, p2, p3, beg;
+ int i;
+
+ s.gel = gel;
+ s.ctm = &ctm;
+ s.flatness = flatness;
+
+ s.stroke = path->stroke;
+ s.sn = 0;
+ s.bn = 0;
+ s.dot = 0;
+
+ s.dash = path->dash;
+ s.toggle = 0;
+ s.offset = 0;
+ s.phase = 0;
+
+ i = 0;
+
+ while (i < path->len)
+ {
+ switch (path->els[i++].k)
+ {
+ case FZ_MOVETO:
+ p1.x = path->els[i++].v;
+ p1.y = path->els[i++].v;
+ error = dashmoveto(&s, p1);
+ if (error)
+ return error;
+ beg = p0 = p1;
+ break;
+
+ case FZ_LINETO:
+ p1.x = path->els[i++].v;
+ p1.y = path->els[i++].v;
+ error = dashlineto(&s, p1);
+ if (error)
+ return error;
+ p0 = p1;
+ break;
+
+ case FZ_CURVETO:
+ p1.x = path->els[i++].v;
+ p1.y = path->els[i++].v;
+ p2.x = path->els[i++].v;
+ p2.y = path->els[i++].v;
+ p3.x = path->els[i++].v;
+ p3.y = path->els[i++].v;
+ error = dashbezier(&s, p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
+ if (error)
+ return error;
+ p0 = p3;
+ break;
+
+ case FZ_CLOSEPATH:
+ error = dashlineto(&s, beg);
+ if (error)
+ return error;
+ break;
+ }
+ }
+
+ return strokeflush(&s);
+}
+
diff --git a/tests/gsrfilt.rc b/tests/gsrfilt.rc
new file mode 100644
index 00000000..72d4e4d3
--- /dev/null
+++ b/tests/gsrfilt.rc
@@ -0,0 +1,10 @@
+# test ghostscript filters
+#
+# usage: rc gsfilter.rc CCITTFaxDecode '<< ... >>' < in > out
+#
+
+builtin gs -q -dBATCH '-sDEVICE=nullpage' -c \
+ '/myin (%stdin) (r) file ' ^ $2 ^ ' /' ^ $1 ^ ' filter def
+ /s 1024 string def
+ { myin s readstring { print } { print exit } ifelse } loop'
+
diff --git a/tests/gswfilt.rc b/tests/gswfilt.rc
new file mode 100644
index 00000000..7ce65af3
--- /dev/null
+++ b/tests/gswfilt.rc
@@ -0,0 +1,11 @@
+# test ghostscript filters
+#
+# usage: rc gsfilter.rc CCITTFaxDecode '<< ... >>' < in > out
+#
+
+builtin gs -q -dBATCH '-sDEVICE=nullpage' -c \
+ '/myout (%stdout) (w) file ' ^ $2 ^ ' /' ^ $1 ^ ' filter def
+ /myin (%stdin) (r) file def
+ /s 1024 string def
+ { myin s readstring { myout s writestring } { myout s writestring myout closefile exit } ifelse } loop'
+
diff --git a/tests/oscar.raw b/tests/oscar.raw
new file mode 100644
index 00000000..fcbf201c
--- /dev/null
+++ b/tests/oscar.raw
Binary files differ
diff --git a/tests/other.h b/tests/other.h
new file mode 100644
index 00000000..f934b519
--- /dev/null
+++ b/tests/other.h
@@ -0,0 +1,17 @@
+{'g', {0}},
+{'m', {15, 15}}, {'l', {15, 25}}, {'l', {25, 25}}, {'l', {25, 15}}, {'h', {}}, {'f', {}},
+{'m', {10, 10}}, {'l', {10, 30}}, {'l', {30, 30}}, {'l', {30, 10}}, {'h', {}}, {'S', {}},
+{'r', {1, 1, 0}},
+{'q', {}},
+{'T', {1,0,0,1, 15, 15}},
+{'m', {15, 15}}, {'l', {15, 25}}, {'l', {25, 25}}, {'l', {25, 15}}, {'h', {}}, {'f', {}},
+{'m', {10, 10}}, {'l', {10, 30}}, {'l', {30, 30}}, {'l', {30, 10}}, {'h', {}}, {'S', {}},
+{'T', {1,0,0,1, 15, 15}},
+{'r', {1, 0, 1}},
+{'m', {15, 15}}, {'l', {15, 25}}, {'l', {25, 25}}, {'l', {25, 15}}, {'h', {}}, {'f', {}},
+{'m', {10, 10}}, {'l', {10, 30}}, {'l', {30, 30}}, {'l', {30, 10}}, {'h', {}}, {'S', {}},
+{'Q', {}},
+{'r', {0, 1, 1}},
+{'a', {0.5}},
+{'m', {10, 20}}, {'l', {20, 30}}, {'l', {30, 20}}, {'l', {20, 10}}, {'h', {}}, {'f', {}},
+{'!'}
diff --git a/tests/pred.rc b/tests/pred.rc
new file mode 100644
index 00000000..ba019566
--- /dev/null
+++ b/tests/pred.rc
@@ -0,0 +1,23 @@
+# test fax codec
+
+FILE=oscar
+
+ARGS='<< /Predictor 15 /Columns 80 /Colors 3 /BitsPerComponent 8 >>'
+
+gs -q -dBATCH -sDEVICE'='nullpage -c '
+/runfilt { /fo exch def /fi exch def
+ { fi read not { exit } if fo exch write } loop
+ fo closefile
+} def
+(' ^ $FILE ^ '.raw) (r) file
+(' ^ $FILE ^ '.pz-gs) (w) file ' ^ $ARGS ^ ' /FlateEncode filter runfilt'
+
+./t-filter.exe FlateDecode '<< >>' < $FILE.pz-gs > $FILE.p-gs
+./t-filter.exe PredictDecode $ARGS < $FILE.p-gs > $FILE.d-gs
+
+./t-filter.exe PredictEncode $ARGS < $FILE.raw > $FILE.p-fz
+./t-filter.exe FlateEncode '<< >>' < $FILE.p-fz > $FILE.pz-fz
+
+./t-filter.exe FlateDecode '<< >>' < $FILE.pz-fz > $FILE.p-fz2
+./t-filter.exe PredictDecode $ARGS < $FILE.p-fz2 > $FILE.d-fz2
+
diff --git a/tests/ptt3.pbm b/tests/ptt3.pbm
new file mode 100644
index 00000000..a91f7193
--- /dev/null
+++ b/tests/ptt3.pbm
Binary files differ
diff --git a/tests/t-filter.c b/tests/t-filter.c
new file mode 100644
index 00000000..c7bc2c7f
--- /dev/null
+++ b/tests/t-filter.c
@@ -0,0 +1,111 @@
+#include <fitz.h>
+
+void
+usage(void)
+{
+ printf("usage: filter filt0 param0 filt1 param1 filt2 param2 ...\n");
+ exit(1);
+}
+
+struct {
+ char *name;
+ fz_error *(*newf)(fz_filter **, fz_obj*);
+} table[] = {
+ { "ASCIIHexDecode", fz_newahxd },
+ { "ASCII85Decode", fz_newa85d },
+ { "CCITTFaxDecode", fz_newfaxd },
+ { "DCTDecode", fz_newdctd },
+ { "RunLengthDecode", fz_newrld },
+ { "LZWDecode", fz_newlzwd },
+ { "FlateDecode", fz_newflated },
+
+ { "ASCIIHexEncode", fz_newahxe },
+ { "ASCII85Encode", fz_newa85e },
+ { "CCITTFaxEncode", fz_newfaxe },
+ { "DCTEncode", fz_newdcte },
+ { "RunLengthEncode", fz_newrle },
+ { "LZWEncode", fz_newlzwe },
+ { "FlateEncode", fz_newflatee },
+
+ { "PredictDecode", fz_newpredictd },
+ { "PredictEncode", fz_newpredicte },
+
+ { "JBIG2Decode", fz_newjbig2d },
+ { "JPXDecode", fz_newjpxd },
+
+ { nil, nil, }
+};
+
+fz_error *
+makefilter(fz_filter **fp, char *f, fz_obj *p)
+{
+ int i;
+
+ for (i = 0; table[i].name; i++)
+ if (strcmp(f, table[i].name) == 0)
+ return table[i].newf(fp, p);
+
+ return fz_throw("unknown filter type '%s'", f);
+}
+
+int
+main(int argc, char **argv)
+{
+ unsigned char buf[512];
+ int i;
+
+ fz_error *err;
+ fz_filter *filter;
+ fz_filter *pipe;
+ fz_file *file;
+ fz_obj *param;
+
+ if (argc == 1)
+ usage();
+
+ filter = nil;
+ pipe = nil;
+
+ for (i = 1; i < argc - 1; i += 2)
+ {
+ err = fz_parseobj(&param, argv[i+1]);
+ if (err) fz_abort(err);
+
+ err = makefilter(&filter, argv[i], param);
+ if (err) fz_abort(err);
+
+ if (pipe) {
+ err = fz_newpipeline(&pipe, pipe, filter);
+ if (err) fz_abort(err);
+ }
+ else
+ pipe = filter;
+ }
+
+ err = fz_openfile(&file, "/dev/stdin", O_RDONLY);
+ if (err) fz_abort(err);
+
+ if (pipe)
+ {
+ err = fz_pushfilter(file, pipe);
+ if (err) fz_abort(err);
+ }
+
+ while (1)
+ {
+ i = fz_read(file, buf, sizeof buf);
+ if (i < 0)
+ fz_abort(fz_ferror(file));
+ if (i == 0)
+ break;
+ write(1, buf, i);
+ }
+
+ if (pipe)
+ fz_popfilter(file);
+
+ fz_closefile(file);
+
+ return 0;
+}
+
diff --git a/tests/t-parse.c b/tests/t-parse.c
new file mode 100644
index 00000000..fabd2060
--- /dev/null
+++ b/tests/t-parse.c
@@ -0,0 +1,44 @@
+#include <fitz.h>
+
+int
+main(int argc, char **argv)
+{
+ fz_error *err;
+ fz_obj *obj;
+ int i;
+
+ if (argc == 1)
+ {
+ err = fz_packobj(&obj,
+ "[ %s %r [ %i ] "
+ "<< /Float %f /BinString %# /Name %n /Int %i >> "
+ "(foo) /bar 3223 [ [ 1 2 3 %i ] %i [ 23 ] %i ]",
+ "Hello, world",
+ 3, 0,
+ 42,
+ 23.5,
+ "f\0obar", 4,
+ "Foo",
+ 666,
+ -1, -2 , -3
+ );
+ if (err) fz_abort(err);
+
+ printf("pretty: "); fz_fprintobj(stdout, obj); printf("\n");
+ printf("comapct: "); fz_fprintcobj(stdout, obj); printf("\n");
+ fz_dropobj(obj);
+ printf("\n");
+ }
+
+ for (i = 1; i < argc; i++) {
+ err = fz_parseobj(&obj, argv[i]);
+ if (err) fz_abort(err);
+ printf("pretty: "); fz_fprintobj(stdout, obj); printf("\n");
+ printf("compact: "); fz_fprintcobj(stdout, obj); printf("\n");
+ fz_dropobj(obj);
+ printf("\n");
+ }
+
+ return 0;
+}
+
diff --git a/tests/t-scanconv.c b/tests/t-scanconv.c
new file mode 100644
index 00000000..84e09867
--- /dev/null
+++ b/tests/t-scanconv.c
@@ -0,0 +1,204 @@
+#include <fitz.h>
+
+fz_error *fz_fillpath(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness);
+fz_error *fz_strokepath(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness);
+fz_error *fz_dashpath(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness);
+
+int main(int argc, char **argv)
+{
+ fz_path *path;
+ fz_gel *gel;
+ fz_ael *ael;
+ fz_matrix ctm;
+ fz_dash *dash;
+ fz_stroke stroke = { 1, 1, 4.0, 15.0 };
+ float dashes[] = { 20, 20, 5, 10 };
+ int i;
+
+ fz_newdash(&dash, 0.0, 2, dashes);
+
+ fz_newpath(&path);
+
+ /* AGaramond-Regular: 'X' */
+ if (getenv("X")) {
+ fz_moveto(path, 631, 25);
+ fz_lineto(path, 666, 22);
+ fz_curveto(path, 673, 16, 672, 2, 665, -3);
+ fz_curveto(path, 619, -1, 580, 0, 539, 0);
+ fz_curveto(path, 494, 0, 446, -1, 410, -3);
+ fz_curveto(path, 404, 3, 403, 16, 408, 22);
+ fz_lineto(path, 437, 25);
+ fz_curveto(path, 460, 27, 471, 31, 471, 38);
+ fz_curveto(path, 471, 44, 468, 54, 449, 82);
+ fz_curveto(path, 405, 143, 350, 226, 306, 283);
+ fz_curveto(path, 279, 249, 205, 140, 165, 70);
+ fz_curveto(path, 157, 56, 152, 45, 152, 38);
+ fz_curveto(path, 152, 32, 161, 27, 181, 25);
+ fz_lineto(path, 208, 22);
+ fz_curveto(path, 215, 16, 213, 2, 207, -3);
+ fz_curveto(path, 171, -1, 133, 0, 99, 0);
+ fz_curveto(path, 65, 0, 28, -1, -4, -3);
+ fz_curveto(path, -13, 1, -14, 16, -7, 22);
+ fz_lineto(path, 18, 25);
+ fz_curveto(path, 72, 31, 107, 74, 137, 114);
+ fz_curveto(path, 149, 130, 218, 218, 273, 298);
+ fz_curveto(path, 280, 308, 282, 315, 282, 318);
+ fz_curveto(path, 282, 321, 278, 330, 270, 341);
+ fz_lineto(path, 120, 556);
+ fz_curveto(path, 87, 603, 68, 625, 33, 631);
+ fz_lineto(path, 1, 638);
+ fz_curveto(path, -4, 644, -3, 660, 3, 663);
+ fz_curveto(path, 50, 661, 83, 660, 119, 660);
+ fz_curveto(path, 158, 660, 203, 661, 232, 663);
+ fz_curveto(path, 239, 660, 240, 645, 234, 638);
+ fz_lineto(path, 206, 636);
+ fz_curveto(path, 188, 635, 176, 629, 176, 623);
+ fz_curveto(path, 176, 614, 187, 597, 206, 568);
+ fz_curveto(path, 240, 520, 298, 430, 334, 385);
+ fz_curveto(path, 360, 417, 450, 559, 468, 589);
+ fz_curveto(path, 476, 602, 482, 615, 482, 622);
+ fz_curveto(path, 482, 627, 467, 633, 449, 635);
+ fz_lineto(path, 423, 638);
+ fz_curveto(path, 417, 645, 417, 659, 425, 663);
+ fz_curveto(path, 460, 661, 492, 660, 531, 660);
+ fz_curveto(path, 568, 660, 596, 661, 627, 663);
+ fz_curveto(path, 634, 658, 635, 644, 629, 638);
+ fz_lineto(path, 605, 636);
+ fz_curveto(path, 575, 633, 539, 603, 498, 551);
+ fz_curveto(path, 459, 501, 416, 441, 368, 374);
+ fz_curveto(path, 362, 365, 358, 358, 358, 355);
+ fz_curveto(path, 358, 352, 359, 345, 371, 329);
+ fz_lineto(path, 540, 91);
+ fz_curveto(path, 574, 44, 600, 28, 631, 25);
+ fz_closepath(path);
+ }
+
+ /* AGaramond-Regular: 'g' */
+ if (getenv("g")) {
+ fz_moveto(path, 365, 372);
+ fz_lineto(path, 446, 372);
+ fz_curveto(path, 457, 367, 455, 333, 440, 331);
+ fz_lineto(path, 368, 331);
+ fz_curveto(path, 370, 314, 370, 297, 370, 280);
+ fz_curveto(path, 370, 211, 329, 121, 202, 121);
+ fz_curveto(path, 184, 121, 170, 123, 158, 124);
+ fz_curveto(path, 146, 117, 114, 98, 114, 69);
+ fz_curveto(path, 114, 46, 137, 27, 184, 27);
+ fz_curveto(path, 218, 27, 259, 30, 303, 30);
+ fz_curveto(path, 359, 30, 443, 20, 443, -80);
+ fz_curveto(path, 443, -189, 324, -269, 194, -269);
+ fz_curveto(path, 71, -269, 28, -203, 28, -153);
+ fz_curveto(path, 28, -137, 32, -124, 39, -116);
+ fz_curveto(path, 56, -98, 84, -72, 107, -49);
+ fz_curveto(path, 116, -40, 124, -31, 115, -25);
+ fz_curveto(path, 76, -15, 42, 19, 42, 54);
+ fz_curveto(path, 42, 59, 46, 63, 57, 71);
+ fz_curveto(path, 74, 82, 93, 99, 110, 117);
+ fz_curveto(path, 115, 123, 120, 131, 120, 136);
+ fz_curveto(path, 86, 154, 45, 193, 45, 257);
+ fz_curveto(path, 45, 343, 119, 408, 208, 408);
+ fz_curveto(path, 246, 408, 281, 398, 304, 388);
+ fz_curveto(path, 336, 374, 343, 372, 365, 372);
+ fz_closepath(path);
+ fz_moveto(path, 271, -32);
+ fz_lineto(path, 237, -32);
+ fz_curveto(path, 205, -32, 165, -34, 151, -43);
+ fz_curveto(path, 127, -58, 103, -89, 103, -127);
+ fz_curveto(path, 103, -181, 146, -226, 237, -226);
+ fz_curveto(path, 326, -226, 385, -176, 385, -119);
+ fz_curveto(path, 385, -58, 343, -32, 271, -32);
+ fz_closepath(path);
+ fz_moveto(path, 217, 150);
+ fz_curveto(path, 271, 150, 299, 193, 299, 254);
+ fz_curveto(path, 299, 322, 271, 379, 210, 379);
+ fz_curveto(path, 162, 379, 126, 335, 126, 267);
+ fz_curveto(path, 126, 196, 169, 150, 217, 150);
+ fz_closepath(path);
+ }
+
+ /* AGaramond-Regular: 'i' */
+ if (getenv("i"))
+ {
+ fz_moveto(path, 98, 112);
+ fz_lineto(path, 98, 287);
+ fz_curveto(path, 98, 326, 98, 331, 71, 349);
+ fz_lineto(path, 62, 355);
+ fz_curveto(path, 58, 359, 58, 370, 63, 373);
+ fz_curveto(path, 86, 381, 143, 407, 166, 422);
+ fz_curveto(path, 171, 422, 175, 420, 176, 416);
+ fz_curveto(path, 174, 381, 172, 333, 172, 292);
+ fz_lineto(path, 172, 112);
+ fz_curveto(path, 172, 40, 174, 30, 210, 25);
+ fz_lineto(path, 231, 22);
+ fz_curveto(path, 238, 17, 236, 0, 229, -3);
+ fz_curveto(path, 199, -1, 170, 0, 135, 0);
+ fz_curveto(path, 99, 0, 69, -1, 41, -3);
+ fz_curveto(path, 34, 0, 32, 17, 39, 22);
+ fz_lineto(path, 60, 25);
+ fz_curveto(path, 97, 30, 98, 40, 98, 112);
+ fz_closepath(path);
+ fz_moveto(path, 131, 662);
+ fz_curveto(path, 161, 662, 180, 639, 180, 611);
+ fz_curveto(path, 180, 576, 155, 560, 128, 560);
+ fz_curveto(path, 97, 560, 77, 582, 77, 609);
+ fz_curveto(path, 77, 642, 102, 662, 131, 662);
+ fz_closepath(path);
+ }
+
+ if (getenv("rect"))
+ {
+ //fz_moveto(path, 103, 100);
+ //fz_lineto(path, 432, 400);
+ //fz_lineto(path, 400, 100);
+ //fz_closepath(path);
+
+// fz_moveto(path, 103, 100);
+// fz_lineto(path, 432, 400);
+// fz_lineto(path, 100, 400);
+// fz_closepath(path);
+
+ fz_moveto(path, 199, 100);
+ fz_lineto(path, 100, 200);
+ fz_lineto(path, 300, 200);
+ fz_lineto(path, 201, 100);
+
+ fz_moveto(path, 150, 100);
+ fz_lineto(path, 150, 100);
+ fz_closepath(path);
+ }
+
+ fz_moveto(path, 100, 100);
+ for (i = 0; i < 30; i++)
+ fz_lineto(path, 100 + i * 10, 100 + i * 5);
+
+ fz_endpath(path, FZ_STROKE, &stroke, dash);
+
+ ctm = fz_identity();
+ ctm = fz_concat(ctm, fz_rotate(0));
+ ctm = fz_concat(ctm, fz_translate(100, -700));
+ ctm = fz_concat(ctm, fz_scale(1.0, -1.0));
+
+ fz_newgel(&gel);
+ fz_newael(&ael);
+
+ fz_resetgel(gel, 17, 15);
+ // fz_fillpath(gel, path, ctm, 0.25);
+ // fz_strokepath(gel, path, ctm, 0.25);
+ fz_dashpath(gel, path, ctm, 0.25);
+ fz_sortgel(gel);
+
+/*
+ for (i = 0; i < gel->len; i++)
+ {
+ printf("edge %d,%d to ?,%d\n",
+ gel->edges[i].x,
+ gel->edges[i].y,
+ gel->edges[i].y + gel->edges[i].h);
+ }
+*/
+
+ fz_scanconvert(gel, ael, 0);
+
+ return 0;
+}
+
diff --git a/tests/testfax.rc b/tests/testfax.rc
new file mode 100755
index 00000000..516d7dfd
--- /dev/null
+++ b/tests/testfax.rc
@@ -0,0 +1,40 @@
+#!/usr/local/bin/rc
+
+head = 'P4
+1728 2376'
+
+params = '<< /XRows 2376 /Columns 1728 /K 0 /EndOfLine true >>'
+
+echo Encoding with GS
+rc gswfilt.rc CCITTFaxEncode $params < ptt3.raw > /tmp/e-gs
+
+echo Encoding with Fz
+../t-filter CCITTFaxEncode $params < ptt3.raw > /tmp/e-fz >[2] /tmp/e-fz.trace
+
+od -t x1 /tmp/e-gs > /tmp/e-gs.hx
+od -t x1 /tmp/e-fz > /tmp/e-fz.hx
+
+echo HEXDUMP
+diff -u /tmp/e-gs.hx /tmp/e-fz.hx | head -20
+echo
+
+echo $head > /tmp/e-gs,d-gs.pbm
+echo $head > /tmp/e-fz,d-gs.pbm
+echo $head > /tmp/e-gs,d-fz.pbm
+echo $head > /tmp/e-fz,d-fz.pbm
+
+echo Decoding GS output with GS
+rc gsrfilt.rc CCITTFaxDecode $params < /tmp/e-gs >> /tmp/e-gs,d-gs.pbm
+
+echo Decoding Fz output with GS
+rc gsrfilt.rc CCITTFaxDecode $params < /tmp/e-fz >> /tmp/e-fz,d-gs.pbm
+
+echo Decoding GS output with Fz
+../t-filter CCITTFaxDecode $params </tmp/e-gs >>/tmp/e-gs,d-fz.pbm >[2]/tmp/d-gs.tr
+
+echo Decoding Fz output with Fz
+../t-filter CCITTFaxDecode $params </tmp/e-fz >>/tmp/e-fz,d-fz.pbm >[2]/tmp/d-fz.tr
+
+echo TRACE
+diff -uw /tmp/d-gs.tr /tmp/d-fz.tr | head -200
+
diff --git a/tests/tiger.c b/tests/tiger.c
new file mode 100644
index 00000000..11d84013
--- /dev/null
+++ b/tests/tiger.c
@@ -0,0 +1,213 @@
+#include <fitz.h>
+
+static struct { char c; double f[6]; } cmd[] = {
+#include "tiger.h"
+};
+
+static fz_pathbuilder *pbuild;
+static fz_textbuilder *tbuild;
+static fz_tree *tree;
+
+static fz_stroke stroke = { 0, 0, 1.0, 10.0 };
+static float frgb[3] = { 0, 0, 0 };
+static float srgb[3] = { 0, 0, 0 };
+static fz_node *gstack[32];
+static int gtop = 0;
+
+void gsave(void)
+{
+ gstack[gtop++] = tree->head;
+}
+
+void grestore(void)
+{
+ tree->head = gstack[--gtop];
+}
+
+void dostroke(void)
+{
+ fz_node *path;
+ fz_node *solid;
+ fz_node *mask;
+
+ fz_makepath(&path, pbuild, FZ_PSTROKE, &stroke, nil);
+ fz_newsolid(&solid, 1.0f, srgb[0], srgb[1], srgb[2]);
+ fz_newblend(&mask, FZ_BMASK);
+
+ fz_insertnode(mask, path);
+ fz_insertnode(mask, solid);
+ fz_insertnode(tree->head, mask);
+}
+
+void dofill(void)
+{
+ fz_node *path;
+ fz_node *solid;
+ fz_node *mask;
+
+ fz_makepath(&path, pbuild, FZ_PFILL, nil, nil);
+ fz_newsolid(&solid, 1.0f, frgb[0], frgb[1], frgb[2]);
+ fz_newblend(&mask, FZ_BMASK);
+
+ fz_insertnode(mask, path);
+ fz_insertnode(mask, solid);
+ fz_insertnode(tree->head, mask);
+}
+
+void doxform(fz_matrix ctm)
+{
+ fz_node *xform;
+ fz_node *over;
+
+ fz_newtransform(&xform, ctm);
+ fz_newblend(&over, FZ_BOVER);
+
+ fz_insertnode(xform, over);
+ fz_insertnode(tree->head, xform);
+
+ tree->head = over;
+}
+
+int main(int argc, char **argv)
+{
+ fz_node *node;
+ fz_matrix ctm;
+ fz_rect r;
+ int i;
+
+ fz_newpathbuilder(&pbuild);
+ fz_newtextbuilder(&tbuild);
+ fz_newtree(&tree);
+
+ fz_newblend(&node, FZ_BOVER);
+ tree->root = tree->head = node;
+
+ fz_newsolid(&node, 1, .8, .8, .8);
+ fz_insertnode(tree->head, node);
+
+ for (i = 0; cmd[i].c != '!'; i++)
+ {
+ switch (cmd[i].c)
+ {
+ case 'q':
+ gsave();
+ break;
+ case 'Q':
+ grestore();
+ break;
+
+ /* 'cm' -> 'T' ... insert xform node */
+ case 'T':
+ ctm.xx = cmd[i].f[0];
+ ctm.xy = cmd[i].f[1];
+ ctm.yx = cmd[i].f[2];
+ ctm.yy = cmd[i].f[3];
+ ctm.tx = cmd[i].f[4];
+ ctm.ty = cmd[i].f[5];
+ doxform(ctm);
+ break;
+
+ /* current color */
+ case 'g':
+ frgb[1] = cmd[i].f[0];
+ frgb[2] = cmd[i].f[0];
+ frgb[3] = cmd[i].f[0];
+ break;
+ case 'G':
+ srgb[1] = cmd[i].f[0];
+ srgb[2] = cmd[i].f[0];
+ srgb[3] = cmd[i].f[0];
+ break;
+ case 'r':
+ frgb[1] = cmd[i].f[0];
+ frgb[2] = cmd[i].f[1];
+ frgb[3] = cmd[i].f[2];
+ break;
+ case 'R':
+ srgb[1] = cmd[i].f[0];
+ srgb[2] = cmd[i].f[1];
+ srgb[3] = cmd[i].f[2];
+ break;
+ case 'a':
+ frgb[0] = cmd[i].f[0];
+ break;
+ case 'A':
+ srgb[0] = cmd[i].f[0];
+ break;
+
+ /* line attrs */
+ case 'w': stroke.linewidth = cmd[i].f[0]; break;
+ case 'J': stroke.linecap = cmd[i].f[0]; break;
+ case 'j': stroke.linejoin = cmd[i].f[0]; break;
+ case 'M': stroke.miterlimit = cmd[i].f[0]; break;
+ case 'i': break;
+
+ /* path construction */
+ case 'm':
+ fz_moveto(pbuild, cmd[i].f[0], cmd[i].f[1]);
+ break;
+ case 'l':
+ fz_lineto(pbuild, cmd[i].f[0], cmd[i].f[1]);
+ break;
+ case 'c':
+ fz_curveto(pbuild,
+ cmd[i].f[0], cmd[i].f[1],
+ cmd[i].f[2], cmd[i].f[3],
+ cmd[i].f[4], cmd[i].f[5]);
+ break;
+ case 'v':
+ fz_curvetov(pbuild,
+ cmd[i].f[0], cmd[i].f[1],
+ cmd[i].f[2], cmd[i].f[3]);
+ break;
+ case 'y':
+ fz_curvetoy(pbuild,
+ cmd[i].f[0], cmd[i].f[1],
+ cmd[i].f[2], cmd[i].f[3]);
+ break;
+ case 'h':
+ fz_closepath(pbuild);
+ break;
+
+ /* insert path nodes */
+ case 's':
+ fz_closepath(pbuild);
+ case 'S':
+ dostroke();
+ break;
+
+ case 'f':
+ dofill();
+ break;
+ }
+ }
+
+ r = fz_boundtree(tree, fz_scale(1, -1));
+ printf("/* [%g %g %g %g] */\n", r.min.x, r.min.y, r.max.x, r.max.y);
+ fz_debugtree(tree);
+
+ r.min.x -= 10;
+ r.min.y -= 10;
+ r.max.x += 10;
+ r.max.y += 10;
+
+#if 0
+ {
+ fz_pixmap *img;
+ img = fz_newpixmap(r.min.x, r.min.y,
+ r.max.x - r.min.x, r.max.y - r.min.y);
+ fz_clearpixmap(img);
+
+ fz_rendernode(tree, tree->root, fz_scale(1, -1), img);
+
+ f = fopen("o.ppm", "w");
+ fz_savepixmap(img, f);
+ fclose(f);
+
+ fz_freepixmap(img);
+ }
+#endif
+
+ return 0;
+}
+
diff --git a/tests/tiger.h b/tests/tiger.h
new file mode 100644
index 00000000..28f39a41
--- /dev/null
+++ b/tests/tiger.h
@@ -0,0 +1,3162 @@
+{'T', {0.1, 0, 0, 0.1, 0, 0}},
+{'g', {1}},
+{'m', {854.246, 4873.25}},
+{'v', {855.766, 4852.03, 846.25, 4852.25}},
+{'c', {837.637, 4852.45, 656.645, 5381.27, 431.246, 5356.25}},
+{'v', {626.449, 5437.48, 854.246, 4873.25}},
+{'f', {}},
+{'w', {1.892}},
+{'m', {854.246, 4873.25}},
+{'v', {855.766, 4852.03, 846.25, 4852.25}},
+{'c', {837.637, 4852.45, 656.645, 5381.27, 431.246, 5356.25}},
+{'v', {626.449, 5437.48, 854.246, 4873.25}},
+{'h', {}},
+{'S', {}},
+{'m', {893.246, 4906.25}},
+{'v', {887.445, 4886.14, 879.25, 4889.25}},
+{'c', {870.539, 4892.71, 880.25, 5451.57, 659.25, 5505.25}},
+{'c', {659.25, 5505.24, 870.977, 5514.69, 893.246, 4906.25}},
+{'f', {}},
+{'m', {893.246, 4906.25}},
+{'v', {887.445, 4886.14, 879.25, 4889.25}},
+{'c', {870.539, 4892.71, 880.25, 5451.57, 659.25, 5505.25}},
+{'c', {659.25, 5505.24, 870.977, 5514.69, 893.246, 4906.25}},
+{'h', {}},
+{'S', {}},
+{'m', {1196.25, 4440.25}},
+{'c', {1196.25, 4440.24, 1213.87, 4429.95, 1208.25, 4422.25}},
+{'c', {1203.52, 4415.05, 662.605, 4555.88, 558.25, 4354.25}},
+{'c', {558.25, 4354.24, 599.07, 4561.71, 1196.25, 4440.25}},
+{'f', {}},
+{'m', {1196.25, 4440.25}},
+{'c', {1196.25, 4440.24, 1213.87, 4429.95, 1208.25, 4422.25}},
+{'c', {1203.52, 4415.05, 662.605, 4555.88, 558.25, 4354.25}},
+{'c', {558.25, 4354.24, 599.07, 4561.71, 1196.25, 4440.25}},
+{'h', {}},
+{'S', {}},
+{'m', {1165.25, 4328.25}},
+{'c', {1165.25, 4328.24, 1185.39, 4323.83, 1183.25, 4315.25}},
+{'c', {1180.25, 4306.43, 622.52, 4269.67, 587.25, 4045.25}},
+{'v', {560.395, 4255.19, 1165.25, 4328.25}},
+{'f', {}},
+{'m', {1165.25, 4328.25}},
+{'c', {1165.25, 4328.24, 1185.39, 4323.83, 1183.25, 4315.25}},
+{'c', {1180.25, 4306.43, 622.52, 4269.67, 587.25, 4045.25}},
+{'v', {560.395, 4255.19, 1165.25, 4328.25}},
+{'h', {}},
+{'S', {}},
+{'m', {1118.25, 4389.25}},
+{'v', {1138.21, 4381.67, 1134.25, 4373.25}},
+{'c', {1130.6, 4365.21, 573.473, 4410.19, 506.246, 4193.25}},
+{'c', {506.242, 4193.25, 509.891, 4404.92, 1118.25, 4389.25}},
+{'f', {}},
+{'m', {1118.25, 4389.25}},
+{'v', {1138.21, 4381.67, 1134.25, 4373.25}},
+{'c', {1130.6, 4365.21, 573.473, 4410.19, 506.246, 4193.25}},
+{'c', {506.242, 4193.25, 509.891, 4404.92, 1118.25, 4389.25}},
+{'h', {}},
+{'S', {}},
+{'m', {1001.25, 4589.25}},
+{'c', {1001.24, 4589.25, 1015.29, 4574.09, 1008.25, 4568.25}},
+{'c', {1001.23, 4562.63, 520.055, 4847.02, 364.246, 4681.25}},
+{'v', {460.59, 4870.14, 1001.25, 4589.25}},
+{'f', {}},
+{'m', {1001.25, 4589.25}},
+{'c', {1001.24, 4589.25, 1015.29, 4574.09, 1008.25, 4568.25}},
+{'c', {1001.23, 4562.63, 520.055, 4847.02, 364.246, 4681.25}},
+{'v', {460.59, 4870.14, 1001.25, 4589.25}},
+{'h', {}},
+{'S', {}},
+{'m', {918.25, 4543.25}},
+{'v', {933.922, 4529.72, 927.25, 4523.25}},
+{'c', {921.25, 4516.73, 410.98, 4744.86, 275.25, 4563.25}},
+{'c', {275.25, 4563.24, 349.281, 4761.08, 918.25, 4543.25}},
+{'f', {}},
+{'m', {918.25, 4543.25}},
+{'v', {933.922, 4529.72, 927.25, 4523.25}},
+{'c', {921.25, 4516.73, 410.98, 4744.86, 275.25, 4563.25}},
+{'c', {275.25, 4563.24, 349.281, 4761.08, 918.25, 4543.25}},
+{'h', {}},
+{'S', {}},
+{'m', {889.25, 4498.25}},
+{'v', {906.992, 4487.23, 901.25, 4480.25}},
+{'c', {896.211, 4472.64, 359.609, 4629.09, 250.25, 4430.25}},
+{'v', {296.273, 4636.76, 889.25, 4498.25}},
+{'f', {}},
+{'m', {889.25, 4498.25}},
+{'v', {906.992, 4487.23, 901.25, 4480.25}},
+{'c', {896.211, 4472.64, 359.609, 4629.09, 250.25, 4430.25}},
+{'v', {296.273, 4636.76, 889.25, 4498.25}},
+{'h', {}},
+{'S', {}},
+{'m', {1007.25, 4491.25}},
+{'c', {1007.24, 4491.25, 1019.72, 4474.72, 1012.25, 4470.25}},
+{'c', {1004.3, 4465.15, 563.539, 4808.88, 388.25, 4664.25}},
+{'v', {507.516, 4839.41, 1007.25, 4491.25}},
+{'f', {}},
+{'m', {1007.25, 4491.25}},
+{'c', {1007.24, 4491.25, 1019.72, 4474.72, 1012.25, 4470.25}},
+{'c', {1004.3, 4465.15, 563.539, 4808.88, 388.25, 4664.25}},
+{'v', {507.516, 4839.41, 1007.25, 4491.25}},
+{'h', {}},
+{'S', {}},
+{'m', {790.25, 4810.25}},
+{'v', {796.398, 4790.2, 787.246, 4788.25}},
+{'c', {778.797, 4785.8, 464.199, 5247.8, 253.25, 5164.25}},
+{'c', {253.25, 5164.24, 420.199, 5294, 790.25, 4810.25}},
+{'f', {}},
+{'m', {790.25, 4810.25}},
+{'v', {796.398, 4790.2, 787.246, 4788.25}},
+{'c', {778.797, 4785.8, 464.199, 5247.8, 253.25, 5164.25}},
+{'c', {253.25, 5164.24, 420.199, 5294, 790.25, 4810.25}},
+{'h', {}},
+{'S', {}},
+{'m', {797.25, 4733.25}},
+{'v', {808.168, 4715.31, 800.25, 4711.25}},
+{'c', {791.922, 4707.23, 385.086, 5090.51, 197.246, 4963.25}},
+{'v', {332.152, 5126.13, 797.25, 4733.25}},
+{'f', {}},
+{'m', {797.25, 4733.25}},
+{'v', {808.168, 4715.31, 800.25, 4711.25}},
+{'c', {791.922, 4707.23, 385.086, 5090.51, 197.246, 4963.25}},
+{'v', {332.152, 5126.13, 797.25, 4733.25}},
+{'h', {}},
+{'S', {}},
+{'m', {796.25, 4685.25}},
+{'v', {808.52, 4668.32, 801.25, 4663.25}},
+{'c', {793.098, 4658.76, 352.34, 5002.48, 177.25, 4858.25}},
+{'c', {177.25, 4858.24, 296.316, 5033.01, 796.25, 4685.25}},
+{'f', {}},
+{'m', {796.25, 4685.25}},
+{'v', {808.52, 4668.32, 801.25, 4663.25}},
+{'c', {793.098, 4658.76, 352.34, 5002.48, 177.25, 4858.25}},
+{'c', {177.25, 4858.24, 296.316, 5033.01, 796.25, 4685.25}},
+{'h', {}},
+{'S', {}},
+{'m', {772.246, 4666.25}},
+{'c', {777.402, 4599.75, 788.27, 4527.5, 807.25, 4493.25}},
+{'c', {807.25, 4493.24, 767.797, 4356.79, 864.246, 4211.25}},
+{'c', {864.242, 4211.24, 860.199, 4132.39, 878.25, 4097.25}},
+{'c', {878.25, 4097.24, 921.797, 4004.79, 974.246, 3996.25}},
+{'c', {1017.32, 3988.87, 1113.27, 3955.11, 1221.25, 3939.25}},
+{'c', {1221.24, 3939.25, 1410.2, 3784.79, 1375.25, 3644.25}},
+{'c', {1375.24, 3644.25, 1370.6, 3463.59, 1331.25, 3446.25}},
+{'c', {1331.24, 3446.25, 1458.6, 3569.19, 1353.25, 3384.25}},
+{'l', {1304.25, 3177.25}},
+{'c', {1304.24, 3177.24, 1586.2, 3415.19, 1414.25, 3213.25}},
+{'l', {1304.25, 2927.25}},
+{'c', {1304.24, 2927.25, 1520.2, 3129.19, 1441.25, 3037.25}},
+{'l', {1406.25, 2940.25}},
+{'v', {1881, 3239.19, 1542.25, 2913.25}},
+{'c', {1542.25, 2913.24, 1630.2, 2953.19, 1678.25, 2922.25}},
+{'c', {1678.24, 2922.24, 1753.4, 2935.59, 1744.25, 2918.25}},
+{'v', {1515.8, 2803.59, 1476.25, 2601.25}},
+{'v', {1568.6, 2711.19, 1533.25, 2592.25}},
+{'l', {1538.25, 2465.25}},
+{'v', {1581.8, 2702.39, 1577.25, 2289.25}},
+{'v', {1788.6, 2486.79, 1661.25, 2258.25}},
+{'l', {1661.25, 2073.25}},
+{'c', {1661.24, 2073.25, 1828.2, 2253.59, 1758.25, 2113.25}},
+{'v', {1867.8, 2209.59, 1824.25, 2042.25}},
+{'v', {1815, 1927.99, 1863.25, 2051.25}},
+{'v', {2039.4, 2387.79, 1973.25, 2099.25}},
+{'v', {1964.6, 1888.39, 2017.25, 2051.25}},
+{'v', {2021.8, 1936.79, 2123.25, 1857.25}},
+{'v', {2109.8, 2416.39, 2250.25, 2020.25}},
+{'l', {2294.25, 1840.25}},
+{'v', {2325.4, 1941.19, 2321.25, 1998.25}},
+{'v', {2483.8, 2178.79, 2409.25, 1910.25}},
+{'v', {2576.2, 2161.19, 2541.25, 2016.25}},
+{'v', {2457.4, 1839.99, 2475.25, 1787.25}},
+{'v', {2659.8, 2169.99, 2673.25, 2187.25}},
+{'v', {2651, 1721.18, 2770.25, 2117.25}},
+{'v', {2831.4, 1985.19, 2800.25, 1937.25}},
+{'v', {2888.6, 2024.79, 2880.25, 2060.25}},
+{'v', {2930.4, 2150.19, 2961.25, 2000.25}},
+{'v', {2981, 1897.19, 2998.25, 1932.25}},
+{'v', {3042.6, 1668.38, 3056.25, 1919.25}},
+{'v', {3073.4, 2068.79, 2994.25, 2196.25}},
+{'v', {3003, 2231.59, 2972.25, 2275.25}},
+{'v', {3121.8, 2037.99, 3042.25, 2355.25}},
+{'v', {3165.81, 2266.79, 3179.25, 2267.25}},
+{'v', {3029.4, 2521.99, 3126.25, 2469.25}},
+{'v', {3069, 2583.59, 3267.25, 2451.25}},
+{'v', {3091, 2627.59, 3284.25, 2522.25}},
+{'v', {3372.61, 2451.59, 3289.25, 2561.25}},
+{'v', {3130.6, 2737.59, 3372.25, 2539.25}},
+{'v', {3500.21, 2359.19, 3509.25, 2328.25}},
+{'v', {3399.01, 2649.59, 3350.25, 2680.25}},
+{'c', {3350.25, 2680.24, 3443.01, 3080.79, 3896.25, 2909.25}},
+{'c', {3896.25, 2909.24, 3971.01, 2719.99, 4019.25, 2922.25}},
+{'c', {4019.25, 2922.24, 4160.21, 2992.79, 4283.25, 2689.25}},
+{'c', {4283.25, 2689.24, 4327.41, 2838.79, 4318.25, 2869.25}},
+{'c', {4318.25, 2869.24, 4393.41, 2856.39, 4384.25, 2869.25}},
+{'c', {4384.25, 2869.24, 4529.81, 2821.19, 4543.25, 2830.25}},
+{'v', {4617.81, 2755.19, 4622.25, 2795.25}},
+{'c', {4622.24, 2795.25, 4723.41, 2763.99, 4701.25, 2803.25}},
+{'c', {4701.24, 2803.24, 4798.21, 2631.99, 4802.25, 2592.25}},
+{'l', {4829.25, 2746.25}},
+{'l', {4851.25, 2715.25}},
+{'c', {4851.25, 2715.24, 4868.61, 2799.19, 4860.25, 2812.25}},
+{'c', {4851.01, 2825.59, 5079.81, 2737.59, 5132.25, 2509.25}},
+{'l', {5154.25, 2416.25}},
+{'c', {5154.24, 2416.25, 5220.61, 2579.19, 5203.25, 2623.25}},
+{'v', {5260.21, 2614.39, 5264.25, 2566.25}},
+{'c', {5264.24, 2566.25, 5308.61, 2821.19, 5256.25, 2887.25}},
+{'c', {5256.25, 2887.24, 5304.21, 2895.99, 5317.25, 2856.25}},
+{'l', {5317.25, 2935.25}},
+{'c', {5317.24, 2935.24, 5396.61, 2926.79, 5396.25, 2953.25}},
+{'c', {5396.24, 2953.24, 5445.01, 2997.19, 5467.25, 2944.25}},
+{'c', {5467.25, 2944.24, 5330.61, 3331.59, 5533.25, 3120.25}},
+{'c', {5533.25, 3120.24, 5612.21, 3001.59, 5572.25, 3208.25}},
+{'c', {5533.01, 3415.19, 5489.01, 3432.79, 5542.25, 3437.25}},
+{'c', {5542.25, 3437.24, 5550.61, 3476.79, 5528.25, 3494.25}},
+{'y', {5506.61, 3511.99, 5542.25, 3494.25}},
+{'c', {5542.25, 3494.24, 5594.61, 3450.39, 5537.25, 3692.25}},
+{'c', {5537.24, 3692.24, 5607.81, 3674.79, 5476.25, 3996.25}},
+{'v', {5506.61, 4022.39, 5462.25, 4115.25}},
+{'c', {5462.24, 4115.25, 5550.61, 4066.39, 5581.25, 4084.25}},
+{'c', {5581.24, 4084.25, 5577.01, 4101.59, 5542.25, 4145.25}},
+{'c', {5542.25, 4145.24, 5304.21, 4748.4, 5528.25, 4506.25}},
+{'c', {5528.24, 4506.24, 5658.96, 4357.34, 5588.25, 4608.25}},
+{'v', {5488.34, 4872.25, 5497.25, 4919.25}},
+{'l', {772.246, 4666.25}},
+{'f', {}},
+{'w', {11}},
+{'m', {772.246, 4666.25}},
+{'c', {777.402, 4599.75, 788.27, 4527.5, 807.25, 4493.25}},
+{'c', {807.25, 4493.24, 767.797, 4356.79, 864.246, 4211.25}},
+{'c', {864.242, 4211.24, 860.199, 4132.39, 878.25, 4097.25}},
+{'c', {878.25, 4097.24, 921.797, 4004.79, 974.246, 3996.25}},
+{'c', {1017.32, 3988.87, 1113.27, 3955.11, 1221.25, 3939.25}},
+{'c', {1221.24, 3939.25, 1410.2, 3784.79, 1375.25, 3644.25}},
+{'c', {1375.24, 3644.25, 1370.6, 3463.59, 1331.25, 3446.25}},
+{'c', {1331.24, 3446.25, 1458.6, 3569.19, 1353.25, 3384.25}},
+{'l', {1304.25, 3177.25}},
+{'c', {1304.24, 3177.24, 1586.2, 3415.19, 1414.25, 3213.25}},
+{'l', {1304.25, 2927.25}},
+{'c', {1304.24, 2927.25, 1520.2, 3129.19, 1441.25, 3037.25}},
+{'l', {1406.25, 2940.25}},
+{'v', {1881, 3239.19, 1542.25, 2913.25}},
+{'c', {1542.25, 2913.24, 1630.2, 2953.19, 1678.25, 2922.25}},
+{'c', {1678.24, 2922.24, 1753.4, 2935.59, 1744.25, 2918.25}},
+{'v', {1515.8, 2803.59, 1476.25, 2601.25}},
+{'v', {1568.6, 2711.19, 1533.25, 2592.25}},
+{'l', {1538.25, 2465.25}},
+{'v', {1581.8, 2702.39, 1577.25, 2289.25}},
+{'v', {1788.6, 2486.79, 1661.25, 2258.25}},
+{'l', {1661.25, 2073.25}},
+{'c', {1661.24, 2073.25, 1828.2, 2253.59, 1758.25, 2113.25}},
+{'v', {1867.8, 2209.59, 1824.25, 2042.25}},
+{'v', {1815, 1927.99, 1863.25, 2051.25}},
+{'v', {2039.4, 2387.79, 1973.25, 2099.25}},
+{'v', {1964.6, 1888.39, 2017.25, 2051.25}},
+{'v', {2021.8, 1936.79, 2123.25, 1857.25}},
+{'v', {2109.8, 2416.39, 2250.25, 2020.25}},
+{'l', {2294.25, 1840.25}},
+{'v', {2325.4, 1941.19, 2321.25, 1998.25}},
+{'v', {2483.8, 2178.79, 2409.25, 1910.25}},
+{'v', {2576.2, 2161.19, 2541.25, 2016.25}},
+{'v', {2457.4, 1839.99, 2475.25, 1787.25}},
+{'v', {2659.8, 2169.99, 2673.25, 2187.25}},
+{'v', {2651, 1721.18, 2770.25, 2117.25}},
+{'v', {2831.4, 1985.19, 2800.25, 1937.25}},
+{'v', {2888.6, 2024.79, 2880.25, 2060.25}},
+{'v', {2930.4, 2150.19, 2961.25, 2000.25}},
+{'v', {2981, 1897.19, 2998.25, 1932.25}},
+{'v', {3042.6, 1668.38, 3056.25, 1919.25}},
+{'v', {3073.4, 2068.79, 2994.25, 2196.25}},
+{'v', {3003, 2231.59, 2972.25, 2275.25}},
+{'v', {3121.8, 2037.99, 3042.25, 2355.25}},
+{'v', {3165.81, 2266.79, 3179.25, 2267.25}},
+{'v', {3029.4, 2521.99, 3126.25, 2469.25}},
+{'v', {3069, 2583.59, 3267.25, 2451.25}},
+{'v', {3091, 2627.59, 3284.25, 2522.25}},
+{'v', {3372.61, 2451.59, 3289.25, 2561.25}},
+{'v', {3130.6, 2737.59, 3372.25, 2539.25}},
+{'v', {3500.21, 2359.19, 3509.25, 2328.25}},
+{'v', {3399.01, 2649.59, 3350.25, 2680.25}},
+{'c', {3350.25, 2680.24, 3443.01, 3080.79, 3896.25, 2909.25}},
+{'c', {3896.25, 2909.24, 3971.01, 2719.99, 4019.25, 2922.25}},
+{'c', {4019.25, 2922.24, 4160.21, 2992.79, 4283.25, 2689.25}},
+{'c', {4283.25, 2689.24, 4327.41, 2838.79, 4318.25, 2869.25}},
+{'c', {4318.25, 2869.24, 4393.41, 2856.39, 4384.25, 2869.25}},
+{'c', {4384.25, 2869.24, 4529.81, 2821.19, 4543.25, 2830.25}},
+{'v', {4617.81, 2755.19, 4622.25, 2795.25}},
+{'c', {4622.24, 2795.25, 4723.41, 2763.99, 4701.25, 2803.25}},
+{'c', {4701.24, 2803.24, 4798.21, 2631.99, 4802.25, 2592.25}},
+{'l', {4829.25, 2746.25}},
+{'l', {4851.25, 2715.25}},
+{'c', {4851.25, 2715.24, 4868.61, 2799.19, 4860.25, 2812.25}},
+{'c', {4851.01, 2825.59, 5079.81, 2737.59, 5132.25, 2509.25}},
+{'l', {5154.25, 2416.25}},
+{'c', {5154.24, 2416.25, 5220.61, 2579.19, 5203.25, 2623.25}},
+{'v', {5260.21, 2614.39, 5264.25, 2566.25}},
+{'c', {5264.24, 2566.25, 5308.61, 2821.19, 5256.25, 2887.25}},
+{'c', {5256.25, 2887.24, 5304.21, 2895.99, 5317.25, 2856.25}},
+{'l', {5317.25, 2935.25}},
+{'c', {5317.24, 2935.24, 5396.61, 2926.79, 5396.25, 2953.25}},
+{'c', {5396.24, 2953.24, 5445.01, 2997.19, 5467.25, 2944.25}},
+{'c', {5467.25, 2944.24, 5330.61, 3331.59, 5533.25, 3120.25}},
+{'c', {5533.25, 3120.24, 5612.21, 3001.59, 5572.25, 3208.25}},
+{'c', {5533.01, 3415.19, 5489.01, 3432.79, 5542.25, 3437.25}},
+{'c', {5542.25, 3437.24, 5550.61, 3476.79, 5528.25, 3494.25}},
+{'y', {5506.61, 3511.99, 5542.25, 3494.25}},
+{'c', {5542.25, 3494.24, 5594.61, 3450.39, 5537.25, 3692.25}},
+{'c', {5537.24, 3692.24, 5607.81, 3674.79, 5476.25, 3996.25}},
+{'v', {5506.61, 4022.39, 5462.25, 4115.25}},
+{'c', {5462.24, 4115.25, 5550.61, 4066.39, 5581.25, 4084.25}},
+{'c', {5581.24, 4084.25, 5577.01, 4101.59, 5542.25, 4145.25}},
+{'c', {5542.25, 4145.24, 5304.21, 4748.4, 5528.25, 4506.25}},
+{'c', {5528.24, 4506.24, 5658.96, 4357.34, 5588.25, 4608.25}},
+{'v', {5488.34, 4872.25, 5497.25, 4919.25}},
+{'l', {772.246, 4666.25}},
+{'h', {}},
+{'S', {}},
+{'r', {0.8, 0.451, 0.149}},
+{'m', {5497.25, 4917.25}},
+{'c', {5503.79, 4915.31, 5528.06, 4902.95, 5542.25, 4885.25}},
+{'v', {5616.61, 4766, 5559.25, 4968.25}},
+{'c', {5559.24, 4968.24, 5458.21, 5285.2, 5555.25, 5162.25}},
+{'v', {5621.01, 5082.8, 5586.25, 5232.25}},
+{'y', {5543.29, 5413.09, 5515.25, 5483.25}},
+{'c', {5515.24, 5483.24, 5643.01, 5430.4, 5348.25, 5866.25}},
+{'l', {5445.25, 5826.25}},
+{'c', {5445.25, 5826.24, 5229.41, 6262, 4992.25, 6319.25}},
+{'l', {4904.25, 6385.25}},
+{'c', {4904.25, 6385.24, 5326.21, 6803.2, 5185.25, 7208.25}},
+{'c', {5185.24, 7208.25, 5110.61, 7265.2, 5005.25, 7164.25}},
+{'v', {4934.61, 7111.2, 4868.25, 7129.25}},
+{'c', {4868.24, 7129.25, 4529.81, 7115.6, 4508.25, 7115.25}},
+{'c', {4485.81, 7115.6, 4103.01, 7524.8, 3381.25, 7331.25}},
+{'c', {3381.25, 7331.24, 3324.21, 7309.2, 3276.25, 7322.25}},
+{'c', {3276.25, 7322.24, 3073.4, 7498.4, 2536.25, 7247.25}},
+{'c', {2536.25, 7247.24, 2426.6, 7225.6, 2409.25, 7225.25}},
+{'c', {2391.4, 7225.6, 2360.6, 7225.6, 2272.25, 7155.25}},
+{'c', {2184.6, 7084.8, 2180.2, 7076, 2158.25, 7058.25}},
+{'c', {2158.25, 7058.24, 1977.8, 6935.2, 1925.25, 6926.25}},
+{'c', {1925.25, 6926.24, 1797.4, 6856, 1749.25, 6746.25}},
+{'l', {1709.25, 6733.25}},
+{'v', {1691.8, 6653.6, 1687.25, 6640.25}},
+{'c', {1687.25, 6640.24, 1634.6, 6600.8, 1626.25, 6539.25}},
+{'c', {1626.25, 6539.24, 1529, 6473.2, 1533.25, 6425.25}},
+{'v', {1515.8, 6367.6, 1507.25, 6315.25}},
+{'c', {1507.24, 6315.25, 1427.8, 6262, 1436.25, 6231.25}},
+{'c', {1436.24, 6231.24, 1353, 6077.2, 1366.25, 6002.25}},
+{'c', {1366.25, 6002.24, 1295.8, 6006.8, 1265.25, 5980.25}},
+{'c', {1265.24, 5980.24, 1256.2, 5927.6, 1238.25, 5923.25}},
+{'c', {1238.24, 5923.24, 1207.8, 5910, 1234.25, 5866.25}},
+{'v', {1216.6, 5835.2, 1212.25, 5817.25}},
+{'c', {1212.25, 5817.24, 1221, 5786.8, 1172.25, 5725.25}},
+{'c', {1172.24, 5725.24, 1102.2, 5518.4, 1124.25, 5461.25}},
+{'c', {1124.25, 5461.24, 1128.6, 5408.4, 1098.25, 5391.25}},
+{'v', {1058.2, 5395.2, 1150.25, 5263.25}},
+{'c', {1150.24, 5263.24, 1159.4, 5250, 1124.25, 5223.25}},
+{'c', {1124.25, 5223.24, 935, 5184, 908.246, 5003.25}},
+{'c', {908.242, 5003.24, 759, 4840.8, 759.246, 4783.25}},
+{'c', {759, 4758.24, 761.98, 4723.62, 769.25, 4673.25}},
+{'c', {769.25, 4673.24, 763.398, 4581.19, 1067.25, 4572.25}},
+{'y', {1370.6, 4563.59, 5497.25, 4917.25}},
+{'f', {}},
+{'m', {5497.25, 4917.25}},
+{'c', {5503.79, 4915.31, 5528.06, 4902.95, 5542.25, 4885.25}},
+{'v', {5616.61, 4766, 5559.25, 4968.25}},
+{'c', {5559.24, 4968.24, 5458.21, 5285.2, 5555.25, 5162.25}},
+{'v', {5621.01, 5082.8, 5586.25, 5232.25}},
+{'y', {5543.29, 5413.09, 5515.25, 5483.25}},
+{'c', {5515.24, 5483.24, 5643.01, 5430.4, 5348.25, 5866.25}},
+{'l', {5445.25, 5826.25}},
+{'c', {5445.25, 5826.24, 5229.41, 6262, 4992.25, 6319.25}},
+{'l', {4904.25, 6385.25}},
+{'c', {4904.25, 6385.24, 5326.21, 6803.2, 5185.25, 7208.25}},
+{'c', {5185.24, 7208.25, 5110.61, 7265.2, 5005.25, 7164.25}},
+{'v', {4934.61, 7111.2, 4868.25, 7129.25}},
+{'c', {4868.24, 7129.25, 4529.81, 7115.6, 4508.25, 7115.25}},
+{'c', {4485.81, 7115.6, 4103.01, 7524.8, 3381.25, 7331.25}},
+{'c', {3381.25, 7331.24, 3324.21, 7309.2, 3276.25, 7322.25}},
+{'c', {3276.25, 7322.24, 3073.4, 7498.4, 2536.25, 7247.25}},
+{'c', {2536.25, 7247.24, 2426.6, 7225.6, 2409.25, 7225.25}},
+{'c', {2391.4, 7225.6, 2360.6, 7225.6, 2272.25, 7155.25}},
+{'c', {2184.6, 7084.8, 2180.2, 7076, 2158.25, 7058.25}},
+{'c', {2158.25, 7058.24, 1977.8, 6935.2, 1925.25, 6926.25}},
+{'c', {1925.25, 6926.24, 1797.4, 6856, 1749.25, 6746.25}},
+{'l', {1709.25, 6733.25}},
+{'v', {1691.8, 6653.6, 1687.25, 6640.25}},
+{'c', {1687.25, 6640.24, 1634.6, 6600.8, 1626.25, 6539.25}},
+{'c', {1626.25, 6539.24, 1529, 6473.2, 1533.25, 6425.25}},
+{'v', {1515.8, 6367.6, 1507.25, 6315.25}},
+{'c', {1507.24, 6315.25, 1427.8, 6262, 1436.25, 6231.25}},
+{'c', {1436.24, 6231.24, 1353, 6077.2, 1366.25, 6002.25}},
+{'c', {1366.25, 6002.24, 1295.8, 6006.8, 1265.25, 5980.25}},
+{'c', {1265.24, 5980.24, 1256.2, 5927.6, 1238.25, 5923.25}},
+{'c', {1238.24, 5923.24, 1207.8, 5910, 1234.25, 5866.25}},
+{'v', {1216.6, 5835.2, 1212.25, 5817.25}},
+{'c', {1212.25, 5817.24, 1221, 5786.8, 1172.25, 5725.25}},
+{'c', {1172.24, 5725.24, 1102.2, 5518.4, 1124.25, 5461.25}},
+{'c', {1124.25, 5461.24, 1128.6, 5408.4, 1098.25, 5391.25}},
+{'v', {1058.2, 5395.2, 1150.25, 5263.25}},
+{'c', {1150.24, 5263.24, 1159.4, 5250, 1124.25, 5223.25}},
+{'c', {1124.25, 5223.24, 935, 5184, 908.246, 5003.25}},
+{'c', {908.242, 5003.24, 759, 4840.8, 759.246, 4783.25}},
+{'c', {759, 4758.24, 761.98, 4723.62, 769.25, 4673.25}},
+{'c', {769.25, 4673.24, 763.398, 4581.19, 1067.25, 4572.25}},
+{'y', {1370.6, 4563.59, 5497.25, 4917.25}},
+{'h', {}},
+{'S', {}},
+{'m', {928.25, 4671.25}},
+{'y', {653.398, 5104.8, 812.25, 4484.25}},
+{'y', {908.598, 4105.99, 2334.25, 4519.25}},
+{'c', {2334.25, 4519.24, 4191.01, 4854, 4314.25, 4898.25}},
+{'y', {4437.41, 4942, 5484.25, 4871.25}},
+{'l', {5423.25, 5056.25}},
+{'c', {4710.21, 5566.8, 4499.01, 5311.6, 4349.25, 5355.25}},
+{'c', {4199.81, 5399.6, 4226.21, 5294, 4191.25, 5285.25}},
+{'c', {4155.81, 5276.4, 3724.61, 5549.2, 3654.25, 5540.25}},
+{'c', {3583.81, 5531.6, 3305.05, 5792.86, 3469.25, 5443.25}},
+{'c', {3645.41, 5069.6, 2827, 5012.4, 2642.25, 5135.25}},
+{'y', {2457.4, 5258.8, 2721.25, 4933.25}},
+{'y', {2923.8, 4713.2, 2545.25, 4898.25}},
+{'c', {2167, 5038.8, 1903, 4757.2, 1868.25, 4748.25}},
+{'c', {1832.6, 4739.6, 1779.8, 4704.4, 1771.25, 4775.25}},
+{'c', {1762.2, 4845.2, 1679.59, 5028.91, 1331.25, 4739.25}},
+{'y', {1111, 4556.99, 959.246, 4799.25}},
+{'l', {928.25, 4671.25}},
+{'f', {}},
+{'r', {0.91, 0.498, 0.227}},
+{'m', {3668.25, 5521.25}},
+{'c', {3598.21, 5512.39, 3319.17, 5773.52, 3484.25, 5424.25}},
+{'c', {3666.41, 5037.19, 2841.41, 4993.19, 2656.25, 5116.25}},
+{'y', {2471.8, 5239.59, 2736.25, 4914.25}},
+{'y', {2938.21, 4693.99, 2560.25, 4879.25}},
+{'c', {2181.4, 5019.59, 1917.4, 4737.99, 1882.25, 4729.25}},
+{'c', {1847, 4720.39, 1794.2, 4685.19, 1785.25, 4755.25}},
+{'c', {1776.6, 4825.99, 1695.43, 5007.87, 1345.25, 4720.25}},
+{'y', {1111.8, 4529.59, 963.246, 4765.25}},
+{'l', {928.25, 4654.25}},
+{'y', {653.199, 5092.19, 819.25, 4451.25}},
+{'y', {915.805, 4072.38, 2348.25, 4500.25}},
+{'v', {4205.41, 4834.79, 4328.25, 4879.25}},
+{'y', {4451.81, 4922.79, 5489.25, 4853.25}},
+{'l', {5428.25, 5043.25}},
+{'c', {4715.81, 5553.99, 4513.41, 5292.39, 4364.25, 5336.25}},
+{'c', {4214.21, 5380.39, 4240.61, 5274.79, 4205.25, 5266.25}},
+{'c', {4170.21, 5257.19, 3739.01, 5529.99, 3668.25, 5521.25}},
+{'f', {}},
+{'r', {0.922, 0.549, 0.306}},
+{'m', {3683.25, 5502.25}},
+{'c', {3612.61, 5493.2, 3340.53, 5757.52, 3498.25, 5405.25}},
+{'c', {3676.41, 5006.98, 2855.81, 4973.98, 2671.25, 5097.25}},
+{'y', {2486.21, 5220.39, 2750.25, 4895.25}},
+{'y', {2952.61, 4674.79, 2574.25, 4859.25}},
+{'c', {2195.81, 5000.39, 1931.8, 4718.79, 1896.25, 4710.25}},
+{'c', {1861.39, 4701.19, 1808.6, 4665.98, 1800.25, 4736.25}},
+{'c', {1791, 4806.79, 1711.27, 4986.82, 1360.25, 4701.25}},
+{'y', {1112.59, 4502.18, 967.25, 4732.25}},
+{'l', {928.25, 4637.25}},
+{'y', {666.203, 5066.39, 826.246, 4417.25}},
+{'y', {922.996, 4038.79, 2363.25, 4481.25}},
+{'v', {4219.81, 4815.59, 4343.25, 4859.25}},
+{'y', {4466.21, 4903.59, 5494.25, 4835.25}},
+{'l', {5434.25, 5031.25}},
+{'c', {4721.41, 5541.19, 4527.81, 5273.2, 4378.25, 5317.25}},
+{'c', {4228.61, 5361.2, 4255.01, 5255.6, 4220.25, 5247.25}},
+{'c', {4184.61, 5237.98, 3753.41, 5510.8, 3683.25, 5502.25}},
+{'f', {}},
+{'r', {0.929, 0.6, 0.38}},
+{'m', {3697.25, 5483.25}},
+{'c', {3627.01, 5473.99, 3354.99, 5738.34, 3512.25, 5386.25}},
+{'c', {3697.41, 4972.88, 2864.33, 4958.7, 2685.25, 5078.25}},
+{'y', {2500.61, 5201.19, 2764.25, 4875.25}},
+{'y', {2967.01, 4655.59, 2588.25, 4840.25}},
+{'c', {2210.21, 4981.19, 1946.21, 4699.59, 1911.25, 4691.25}},
+{'c', {1875.8, 4681.99, 1823.01, 4646.79, 1814.25, 4717.25}},
+{'c', {1805.41, 4787.59, 1727.12, 4965.77, 1374.25, 4682.25}},
+{'y', {1113.39, 4474.79, 972.25, 4699.25}},
+{'l', {928.25, 4620.25}},
+{'y', {687.992, 5025.19, 833.25, 4383.25}},
+{'y', {930.203, 4005.18, 2377.25, 4462.25}},
+{'c', {2377.25, 4462.24, 4234.21, 4796.39, 4357.25, 4840.25}},
+{'y', {4480.62, 4884.39, 5499.25, 4816.25}},
+{'l', {5440.25, 5018.25}},
+{'c', {4727.02, 5528.4, 4542.22, 5253.99, 4392.25, 5298.25}},
+{'c', {4243.01, 5341.99, 4269.41, 5236.39, 4234.25, 5227.25}},
+{'c', {4199.01, 5218.79, 3767.8, 5491.59, 3697.25, 5483.25}},
+{'f', {}},
+{'r', {0.937, 0.651, 0.459}},
+{'m', {3712.25, 5463.25}},
+{'c', {3641.41, 5454.79, 3374.95, 5721.58, 3527.25, 5367.25}},
+{'c', {3711.82, 4935.59, 2884.61, 4935.59, 2700.25, 5059.25}},
+{'y', {2515, 5181.98, 2779.25, 4856.25}},
+{'y', {2981.41, 4636.39, 2603.25, 4821.25}},
+{'c', {2224.61, 4961.98, 1960.61, 4680.39, 1925.25, 4671.25}},
+{'c', {1890.21, 4662.79, 1837.41, 4627.59, 1828.25, 4698.25}},
+{'c', {1819.8, 4768.39, 1742.97, 4944.73, 1388.25, 4663.25}},
+{'y', {1114.2, 4447.38, 976.25, 4665.25}},
+{'l', {927.25, 4603.25}},
+{'y', {709.797, 4986.2, 840.25, 4350.25}},
+{'y', {937.395, 3971.58, 2392.25, 4443.25}},
+{'v', {4248.62, 4777.18, 4372.25, 4821.25}},
+{'y', {4495.02, 4865.18, 5504.25, 4798.25}},
+{'l', {5445.25, 5005.25}},
+{'c', {4732.62, 5515.59, 4556.62, 5234.79, 4407.25, 5279.25}},
+{'c', {4257.41, 5322.79, 4283.82, 5217.18, 4248.25, 5208.25}},
+{'c', {4213.41, 5199.59, 3782.21, 5472.39, 3712.25, 5463.25}},
+{'f', {}},
+{'r', {0.949, 0.702, 0.537}},
+{'m', {3726.25, 5444.25}},
+{'c', {3655.82, 5435.59, 3370.21, 5693.55, 3541.25, 5347.25}},
+{'c', {3752.61, 4920.79, 2899, 4916.39, 2714.25, 5039.25}},
+{'y', {2529.4, 5162.79, 2793.25, 4837.25}},
+{'y', {2995.8, 4617.19, 2617.25, 4802.25}},
+{'c', {2239, 4942.79, 1975, 4661.19, 1940.25, 4652.25}},
+{'c', {1904.61, 4643.59, 1851.8, 4608.39, 1843.25, 4679.25}},
+{'c', {1834.2, 4749.19, 1758.81, 4923.68, 1403.25, 4643.25}},
+{'y', {1115, 4419.98, 980.246, 4632.25}},
+{'l', {927.25, 4586.25}},
+{'y', {729.398, 4951.59, 848.246, 4316.25}},
+{'y', {944.602, 3937.98, 2406.25, 4423.25}},
+{'v', {4263.02, 4757.99, 4386.25, 4802.25}},
+{'y', {4509.41, 4845.99, 5508.25, 4779.25}},
+{'l', {5451.25, 4992.25}},
+{'c', {4738.21, 5502.79, 4571.02, 5215.59, 4421.25, 5259.25}},
+{'c', {4271.82, 5303.59, 4298.21, 5197.99, 4263.25, 5189.25}},
+{'c', {4227.82, 5180.39, 3796.61, 5453.19, 3726.25, 5444.25}},
+{'f', {}},
+{'r', {0.957, 0.749, 0.616}},
+{'m', {3740.25, 5425.25}},
+{'c', {3670.2, 5416.39, 3380.44, 5672.25, 3556.25, 5328.25}},
+{'c', {3780.21, 4888.38, 2913.4, 4897.18, 2728.25, 5020.25}},
+{'y', {2543.8, 5143.58, 2808.25, 4818.25}},
+{'y', {3010.2, 4597.98, 2632.25, 4783.25}},
+{'c', {2253.4, 4923.58, 1989.4, 4641.98, 1954.25, 4633.25}},
+{'c', {1919, 4624.38, 1866.2, 4589.18, 1857.25, 4659.25}},
+{'c', {1848.6, 4729.98, 1774.65, 4902.64, 1417.25, 4624.25}},
+{'y', {1115.79, 4392.58, 984.25, 4598.25}},
+{'l', {927.25, 4569.25}},
+{'y', {751.199, 4901.58, 855.25, 4283.25}},
+{'y', {951.797, 3904.38, 2420.25, 4404.25}},
+{'v', {4277.41, 4738.79, 4400.25, 4783.25}},
+{'y', {4523.81, 4826.79, 5513.25, 4761.25}},
+{'l', {5456.25, 4979.25}},
+{'c', {4743.81, 5489.98, 4585.41, 5196.38, 4436.25, 5240.25}},
+{'c', {4286.21, 5284.39, 4312.61, 5178.79, 4277.25, 5170.25}},
+{'c', {4242.21, 5161.18, 3811.02, 5434, 3740.25, 5425.25}},
+{'f', {}},
+{'r', {0.965, 0.8, 0.69}},
+{'m', {3755.25, 5406.25}},
+{'c', {3684.61, 5397.19, 3382.75, 5646.61, 3570.25, 5309.25}},
+{'c', {3812.21, 4873.59, 2927.8, 4877.99, 2743.25, 5001.25}},
+{'y', {2558.2, 5124.39, 2822.25, 4799.25}},
+{'y', {3024.6, 4578.79, 2646.25, 4763.25}},
+{'c', {2267.8, 4904.39, 2003.8, 4622.79, 1968.25, 4614.25}},
+{'c', {1933.4, 4605.19, 1880.6, 4569.99, 1872.25, 4640.25}},
+{'c', {1863, 4710.79, 1790.5, 4881.59, 1432.25, 4605.25}},
+{'y', {1116.6, 4365.18, 988.25, 4565.25}},
+{'l', {927.25, 4552.25}},
+{'y', {768.59, 4864.79, 862.25, 4249.25}},
+{'y', {959, 3870.77, 2435.25, 4385.25}},
+{'c', {2435.25, 4385.24, 4291.81, 4719.59, 4415.25, 4763.25}},
+{'y', {4538.21, 4807.59, 5518.25, 4743.25}},
+{'l', {5462.25, 4967.25}},
+{'c', {4749.41, 5477.19, 4599.81, 5177.19, 4450.25, 5221.25}},
+{'c', {4300.61, 5265.19, 4327.01, 5159.59, 4292.25, 5151.25}},
+{'c', {4256.61, 5141.99, 3825.41, 5414.79, 3755.25, 5406.25}},
+{'f', {}},
+{'r', {0.976, 0.851, 0.769}},
+{'m', {3769.25, 5387.25}},
+{'c', {3699.01, 5377.98, 3397.15, 5627.42, 3584.25, 5290.25}},
+{'c', {3826.61, 4854.38, 2942.21, 4858.78, 2757.25, 4982.25}},
+{'y', {2572.61, 5105.18, 2836.25, 4779.25}},
+{'y', {3039.01, 4559.58, 2660.25, 4744.25}},
+{'c', {2282.2, 4885.18, 2018.2, 4603.58, 1983.25, 4595.25}},
+{'c', {1947.8, 4585.98, 1895, 4550.78, 1886.25, 4621.25}},
+{'c', {1877.4, 4691.58, 1806.35, 4860.54, 1446.25, 4586.25}},
+{'y', {1117.4, 4337.78, 993.25, 4532.25}},
+{'l', {927.25, 4535.25}},
+{'y', {777.191, 4825.79, 869.246, 4215.25}},
+{'y', {966.207, 3837.18, 2449.25, 4366.25}},
+{'c', {2449.24, 4366.25, 4306.21, 4700.38, 4429.25, 4744.25}},
+{'y', {4552.61, 4788.38, 5523.25, 4724.25}},
+{'l', {5468.25, 4954.25}},
+{'c', {4755.01, 5464.39, 4614.21, 5157.98, 4464.25, 5202.25}},
+{'c', {4315.01, 5245.98, 4341.41, 5140.38, 4306.25, 5131.25}},
+{'c', {4271.01, 5122.78, 3839.81, 5395.58, 3769.25, 5387.25}},
+{'f', {}},
+{'r', {0.984, 0.902, 0.847}},
+{'m', {3784.25, 5367.25}},
+{'c', {3713.41, 5358.79, 3415.82, 5610.54, 3599.25, 5271.25}},
+{'c', {3841.01, 4821.99, 2956.61, 4839.59, 2772.25, 4963.25}},
+{'y', {2587.01, 5085.99, 2851.25, 4760.25}},
+{'y', {3053.41, 4540.39, 2675.25, 4725.25}},
+{'c', {2296.61, 4865.99, 2032.61, 4584.39, 1997.25, 4575.25}},
+{'c', {1962.2, 4566.79, 1909.4, 4531.59, 1900.25, 4602.25}},
+{'c', {1891.8, 4672.39, 1822.19, 4839.5, 1460.25, 4567.25}},
+{'y', {1118.2, 4310.38, 997.246, 4498.25}},
+{'l', {926.246, 4518.25}},
+{'y', {783.594, 4782.39, 876.246, 4182.25}},
+{'y', {973.398, 3803.57, 2464.25, 4347.25}},
+{'v', {4320.61, 4681.19, 4444.25, 4725.25}},
+{'y', {4567.01, 4769.19, 5528.25, 4706.25}},
+{'l', {5473.25, 4941.25}},
+{'c', {4760.61, 5451.59, 4628.61, 5138.79, 4479.25, 5183.25}},
+{'c', {4329.41, 5226.79, 4355.81, 5121.19, 4320.25, 5112.25}},
+{'c', {4285.41, 5103.59, 3854.21, 5376.39, 3784.25, 5367.25}},
+{'f', {}},
+{'r', {0.992, 0.953, 0.925}},
+{'m', {3798.25, 5348.25}},
+{'c', {3727.81, 5339.59, 3442.09, 5597.48, 3613.25, 5251.25}},
+{'c', {3846.61, 4780.78, 2971.01, 4820.38, 2786.25, 4943.25}},
+{'y', {2601.41, 5066.78, 2865.25, 4741.25}},
+{'y', {3067.81, 4521.18, 2689.25, 4706.25}},
+{'c', {2311.01, 4846.78, 2047.01, 4565.18, 2012.25, 4556.25}},
+{'c', {1976.61, 4547.58, 1923.81, 4512.38, 1915.25, 4583.25}},
+{'c', {1906.21, 4653.18, 1838.04, 4818.45, 1475.25, 4547.25}},
+{'y', {1119, 4282.98, 1001.25, 4465.25}},
+{'l', {926.246, 4501.25}},
+{'y', {785.598, 4756.58, 884.25, 4148.25}},
+{'y', {980.605, 3769.98, 2478.25, 4327.25}},
+{'v', {4335.02, 4661.98, 4458.25, 4706.25}},
+{'y', {4581.42, 4749.98, 5532.25, 4687.25}},
+{'l', {5479.25, 4928.25}},
+{'c', {4766.21, 5438.79, 4643.02, 5119.58, 4493.25, 5163.25}},
+{'c', {4343.82, 5207.59, 4370.22, 5101.98, 4335.25, 5093.25}},
+{'c', {4299.81, 5084.38, 3868.61, 5357.19, 3798.25, 5348.25}},
+{'f', {}},
+{'g', {1}},
+{'m', {926.246, 4484.25}},
+{'y', {785.398, 4726.4, 891.246, 4115.25}},
+{'y', {987.797, 3736.39, 2492.25, 4308.25}},
+{'c', {2492.25, 4308.24, 4349.41, 4642.8, 4472.25, 4687.25}},
+{'y', {4595.81, 4730.8, 5537.25, 4669.25}},
+{'l', {5484.25, 4915.25}},
+{'c', {4771.81, 5426, 4657.41, 5100.4, 4508.25, 5144.25}},
+{'c', {4358.21, 5188.4, 4384.61, 5082.8, 4349.25, 5074.25}},
+{'c', {4314.21, 5065.2, 3883.01, 5338, 3812.25, 5329.25}},
+{'c', {3742.21, 5320.4, 3464.79, 5582.27, 3628.25, 5232.25}},
+{'c', {3872.3, 4707.62, 2959.45, 4818.5, 2800.25, 4924.25}},
+{'y', {2615.8, 5047.6, 2880.25, 4722.25}},
+{'y', {3082.2, 4501.99, 2704.25, 4687.25}},
+{'c', {2325.4, 4827.6, 2061.4, 4545.99, 2026.25, 4537.25}},
+{'c', {1991, 4528.39, 1938.2, 4493.19, 1929.25, 4563.25}},
+{'c', {1920.6, 4634, 1853.88, 4797.41, 1489.25, 4528.25}},
+{'y', {1119.8, 4255.59, 1005.25, 4431.25}},
+{'l', {926.246, 4484.25}},
+{'f', {}},
+{'g', {0}},
+{'m', {1384.25, 4154.25}},
+{'c', {1384.25, 4154.24, 1304.6, 4026.79, 1533.25, 3881.25}},
+{'c', {1533.25, 3881.24, 1548.8, 3866.19, 1351.25, 3912.25}},
+{'c', {1351.25, 3912.24, 1282.6, 3934.39, 1265.25, 4049.25}},
+{'c', {1265.24, 4049.25, 1212.2, 4097.19, 1159.25, 4159.25}},
+{'y', {1106.6, 4220.39, 1384.25, 4154.25}},
+{'f', {}},
+{'g', {0.8}},
+{'m', {2924.25, 4678.25}},
+{'v', {3118.48, 4382.97, 3112.25, 4330.25}},
+{'c', {3097.6, 4215.99, 3095.4, 4110.39, 3130.25, 4066.25}},
+{'y', {3165.81, 4022.39, 3262.25, 3657.25}},
+{'c', {3262.25, 3657.24, 3258.21, 3643.99, 3394.25, 4062.25}},
+{'v', {3522.21, 4237.99, 3302.25, 4440.25}},
+{'c', {3302.25, 4440.24, 2915, 4757.2, 2924.25, 4678.25}},
+{'f', {}},
+{'g', {0}},
+{'m', {1604.25, 3859.25}},
+{'c', {1604.25, 3859.24, 1727, 3780.39, 1568.25, 3437.25}},
+{'l', {1639.25, 3463.25}},
+{'c', {1639.24, 3463.24, 1630.2, 3340.39, 1595.25, 3314.25}},
+{'l', {1674.25, 3349.25}},
+{'c', {1674.25, 3349.24, 1727, 3261.19, 1683.25, 3208.25}},
+{'c', {1683.24, 3208.24, 1867.8, 3120.39, 1859.25, 3050.25}},
+{'v', {1929.4, 3137.99, 1885.25, 3208.25}},
+{'c', {1841.4, 3278.79, 1762.2, 3234.79, 1771.25, 3437.25}},
+{'l', {1674.25, 3402.25}},
+{'v', {1735.8, 3498.79, 1736.25, 3569.25}},
+{'l', {1648.25, 3543.25}},
+{'v', {1817.96, 3835.14, 1700.25, 3851.25}},
+{'y', {1634.6, 3859.59, 1604.25, 3859.25}},
+{'f', {}},
+{'g', {0.8}},
+{'m', {1960.25, 3675.25}},
+{'v', {1991, 3723.19, 1960.25, 3714.25}},
+{'c', {1929.4, 3705.59, 1586.2, 3542.79, 1520.25, 3437.25}},
+{'c', {1520.25, 3437.24, 1898.6, 3705.59, 1960.25, 3675.25}},
+{'f', {}},
+{'m', {2074.25, 3587.25}},
+{'v', {2105.4, 3635.19, 2074.25, 3626.25}},
+{'c', {2043.8, 3617.59, 1700.6, 3454.79, 1634.25, 3349.25}},
+{'c', {1634.24, 3349.24, 2013, 3617.59, 2074.25, 3587.25}},
+{'f', {}},
+{'m', {2220.25, 3754.25}},
+{'v', {2250.6, 3802.39, 2220.25, 3793.25}},
+{'c', {2189, 3784.79, 1845.8, 3621.99, 1780.25, 3516.25}},
+{'c', {1780.25, 3516.24, 2158.2, 3784.79, 2220.25, 3754.25}},
+{'f', {}},
+{'m', {1964.25, 3274.25}},
+{'c', {1964.25, 3274.24, 1964.6, 3340.39, 1934.25, 3331.25}},
+{'c', {1903, 3322.79, 1507, 3129.19, 1441.25, 3023.25}},
+{'c', {1441.24, 3023.24, 1903, 3305.19, 1964.25, 3274.25}},
+{'f', {}},
+{'m', {1978.25, 3393.25}},
+{'c', {1978.25, 3393.24, 1991, 3445.99, 1960.25, 3437.25}},
+{'c', {1938.2, 3437.19, 1647.8, 3309.59, 1582.25, 3204.25}},
+{'v', {1907.4, 3441.59, 1978.25, 3393.25}},
+{'f', {}},
+{'m', {1819.25, 2869.25}},
+{'l', {1709.25, 2786.25}},
+{'v', {1823.8, 2869.59, 1863.25, 2856.25}},
+{'c', {1863.25, 2856.24, 1788.6, 2733.19, 1780.25, 2676.25}},
+{'v', {1894.2, 2816.79, 1956.25, 2812.25}},
+{'c', {1956.25, 2812.24, 2039.4, 2807.99, 2039.25, 2689.25}},
+{'c', {2039.25, 2689.24, 2101, 2803.59, 2136.25, 2799.25}},
+{'c', {2136.25, 2799.24, 2149.4, 2728.79, 2136.25, 2654.25}},
+{'v', {2180.2, 2737.59, 2224.25, 2720.25}},
+{'v', {2294.6, 2741.99, 2286.25, 2614.25}},
+{'v', {2285.8, 2499.99, 2277.25, 2469.25}},
+{'v', {2338.6, 2759.59, 2365.25, 2764.25}},
+{'v', {2453, 2777.19, 2506.25, 2680.25}},
+{'c', {2506.25, 2680.24, 2461.8, 2763.99, 2514.25, 2742.25}},
+{'v', {2633.4, 2724.39, 2668.25, 2649.25}},
+{'c', {2668.25, 2649.24, 2593.8, 2781.59, 2655.25, 2746.25}},
+{'c', {2655.25, 2746.24, 2730.2, 2746.39, 2743.25, 2676.25}},
+{'v', {2835.8, 2442.79, 2858.25, 2425.25}},
+{'v', {2774.2, 2662.79, 2792.25, 2663.25}},
+{'v', {2769.8, 2794.79, 2827.25, 2632.25}},
+{'v', {2791.8, 2785.99, 2853.25, 2777.25}},
+{'c', {2915, 2768.39, 2963.4, 2658.39, 3056.25, 2685.25}},
+{'v', {3161.41, 2623.19, 3183.25, 3384.25}},
+{'l', {1819.25, 2869.25}},
+{'f', {}},
+{'g', {0}},
+{'m', {1872.25, 3890.25}},
+{'c', {1872.25, 3890.24, 2035, 3956.39, 2475.25, 3890.25}},
+{'c', {2475.25, 3890.24, 2554.2, 3885.99, 2629.25, 3983.25}},
+{'c', {2703.8, 4079.59, 2998.6, 4158.79, 3069.25, 4137.25}},
+{'l', {3174.25, 4066.25}},
+{'l', {3183.25, 4053.25}},
+{'c', {3183.25, 4053.24, 3319.81, 3938.79, 3324.25, 3855.25}},
+{'c', {3328.61, 3771.59, 3165.81, 3243.59, 3060.25, 3067.25}},
+{'c', {2954.6, 2891.59, 2849, 2755.19, 2638.25, 2781.25}},
+{'c', {2638.25, 2781.24, 2409, 2825.59, 2127.25, 2781.25}},
+{'c', {2127.25, 2781.24, 1806.2, 2799.19, 1775.25, 2887.25}},
+{'y', {1744.6, 2975.19, 1898.25, 3142.25}},
+{'c', {1898.25, 3142.24, 1947, 3234.79, 1934.25, 3393.25}},
+{'c', {1920.6, 3551.59, 1925, 3859.59, 1872.25, 3890.25}},
+{'f', {}},
+{'r', {0.902, 0.4, 0.549}},
+{'m', {2114.25, 3868.25}},
+{'y', {2206.6, 3665.99, 1881.25, 2949.25}},
+{'c', {1859, 2931.19, 2020.26, 2864.69, 2132.25, 2891.25}},
+{'y', {2252.2, 2920.61, 2695.25, 2874.25}},
+{'y', {2954.6, 3045.59, 3095.25, 3534.25}},
+{'v', {3209.81, 3797.99, 3016.25, 3833.25}},
+{'y', {2822.6, 3868.39, 2114.25, 3868.25}},
+{'f', {}},
+{'r', {0.702, 0.2, 0.349}},
+{'m', {2092.25, 3528.25}},
+{'c', {2128.44, 3669.22, 2145.87, 3799.03, 2114.25, 3868.25}},
+{'c', {2114.25, 3868.24, 2800.6, 3797.99, 2924.25, 4027.25}},
+{'c', {2970.45, 4113.41, 3132.81, 3775.99, 3128.25, 3670.25}},
+{'c', {3128.25, 3670.24, 2435.4, 3511.99, 2272.25, 3635.25}},
+{'l', {2092.25, 3528.25}},
+{'f', {}},
+{'r', {0.651, 0.149, 0.298}},
+{'m', {2140.25, 3349.25}},
+{'c', {2140.25, 3349.24, 2162.6, 3269.99, 2136.25, 3226.25}},
+{'v', {2118.6, 3217.19, 2105.25, 3213.25}},
+{'v', {2118.6, 3173.19, 2184.25, 3155.25}},
+{'c', {2184.25, 3155.24, 2206.6, 3107.19, 2233.25, 3103.25}},
+{'c', {2259.4, 3098.39, 2312.2, 3036.79, 2356.25, 3050.25}},
+{'y', {2400.2, 3063.19, 2523.25, 3107.25}},
+{'c', {2523.25, 3107.24, 2585, 3142.39, 2682.25, 3103.25}},
+{'v', {2707.92, 3111.61, 2712.25, 3155.25}},
+{'c', {2718.1, 3207.29, 2752.2, 3247.99, 2774.25, 3270.25}},
+{'c', {2796.2, 3291.99, 2901.8, 3432.79, 2888.25, 3437.25}},
+{'y', {2875.4, 3441.59, 2140.25, 3349.25}},
+{'f', {}},
+{'r', {1, 0.451, 0.498}},
+{'m', {2092.25, 3881.25}},
+{'c', {2092.25, 3881.24, 2061.4, 3635.19, 2096.25, 3543.25}},
+{'c', {2131.8, 3450.39, 2123, 3428.39, 2114.25, 3384.25}},
+{'c', {2105.4, 3340.39, 2153.8, 3230.39, 2215.25, 3164.25}},
+{'l', {2347.25, 3147.25}},
+{'v', {2514.6, 3186.39, 2616.25, 3155.25}},
+{'c', {2616.25, 3155.24, 2714.73, 3140.81, 2752.25, 3305.25}},
+{'c', {2752.25, 3305.24, 2805, 3375.59, 2884.25, 3406.25}},
+{'c', {2963.4, 3437.19, 3042.6, 3894.79, 2998.25, 3983.25}},
+{'c', {2954.6, 4070.79, 2796.2, 4119.19, 2620.25, 3947.25}},
+{'c', {2444.2, 3775.99, 2422.2, 3960.79, 2092.25, 3881.25}},
+{'f', {}},
+{'m', {2092.25, 3881.25}},
+{'c', {2092.25, 3881.24, 2061.4, 3635.19, 2096.25, 3543.25}},
+{'c', {2131.8, 3450.39, 2123, 3428.39, 2114.25, 3384.25}},
+{'c', {2105.4, 3340.39, 2153.8, 3230.39, 2215.25, 3164.25}},
+{'l', {2347.25, 3147.25}},
+{'v', {2514.6, 3186.39, 2616.25, 3155.25}},
+{'c', {2616.25, 3155.24, 2714.73, 3140.81, 2752.25, 3305.25}},
+{'c', {2752.25, 3305.24, 2805, 3375.59, 2884.25, 3406.25}},
+{'c', {2963.4, 3437.19, 3042.6, 3894.79, 2998.25, 3983.25}},
+{'c', {2954.6, 4070.79, 2796.2, 4119.19, 2620.25, 3947.25}},
+{'c', {2444.2, 3775.99, 2422.2, 3960.79, 2092.25, 3881.25}},
+{'h', {}},
+{'S', {}},
+{'r', {1, 1, 0.8}},
+{'m', {2110.25, 3059.25}},
+{'v', {2101, 3080.79, 2052.25, 3085.25}},
+{'c', {2052.25, 3085.24, 1806.2, 3124.79, 1714.25, 3261.25}},
+{'c', {1714.25, 3261.24, 1639, 3322.79, 1687.25, 3195.25}},
+{'c', {1687.25, 3195.24, 1801.8, 2970.79, 1876.25, 2940.25}},
+{'v', {2057, 2895.99, 2110.25, 3059.25}},
+{'f', {}},
+{'w', {5.5}},
+{'m', {2110.25, 3059.25}},
+{'v', {2101, 3080.79, 2052.25, 3085.25}},
+{'c', {2052.25, 3085.24, 1806.2, 3124.79, 1714.25, 3261.25}},
+{'c', {1714.25, 3261.24, 1639, 3322.79, 1687.25, 3195.25}},
+{'c', {1687.25, 3195.24, 1801.8, 2970.79, 1876.25, 2940.25}},
+{'v', {2057, 2895.99, 2110.25, 3059.25}},
+{'h', {}},
+{'S', {}},
+{'r', {0.8, 0.247, 0.298}},
+{'m', {2989.25, 3762.25}},
+{'c', {2996.41, 3849.45, 3017.89, 3944.2, 2998.25, 3983.25}},
+{'c', {2927.69, 4124.62, 2740.99, 4065.35, 2620.25, 3947.25}},
+{'c', {2444.2, 3775.99, 2422.2, 3960.79, 2092.25, 3881.25}},
+{'c', {2092.25, 3881.24, 2073, 3727.99, 2082.25, 3618.25}},
+{'v', {2492.6, 3745.19, 2501.25, 3683.25}},
+{'c', {2501.25, 3683.24, 2519, 3718.79, 2620.25, 3719.25}},
+{'c', {2721.4, 3718.79, 2971.56, 3731.68, 2989.25, 3762.25}},
+{'f', {}},
+{'w', {22}},
+{'R', {0.651, 0.098, 0.149}},
+{'m', {2514.25, 3873.25}},
+{'v', {2567.4, 3819.99, 2528.25, 3714.25}},
+{'c', {2528.25, 3714.24, 2369.4, 3538.39, 2391.25, 3384.25}},
+{'S', {}},
+{'r', {1, 1, 0.8}},
+{'m', {1986.25, 2940.25}},
+{'v', {1938.2, 3080.79, 2035.25, 3006.25}},
+{'v', {2087.8, 2983.99, 2074.25, 2966.25}},
+{'c', {2061.4, 2948.79, 1999.8, 2904.79, 1986.25, 2940.25}},
+{'f', {}},
+{'w', {5.5}},
+{'G', {0}},
+{'m', {1986.25, 2940.25}},
+{'v', {1938.2, 3080.79, 2035.25, 3006.25}},
+{'v', {2087.8, 2983.99, 2074.25, 2966.25}},
+{'c', {2061.4, 2948.79, 1999.8, 2904.79, 1986.25, 2940.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2042.25, 2927.25}},
+{'c', {2042.24, 2927.25, 2003.32, 3039.43, 2081.25, 2979.25}},
+{'c', {2081.25, 2979.24, 2129.39, 2952.66, 2112.25, 2948.25}},
+{'c', {2062.28, 2933.83, 2112.44, 2905.67, 2042.25, 2927.25}},
+{'f', {}},
+{'m', {2042.25, 2927.25}},
+{'c', {2042.24, 2927.25, 2003.32, 3039.43, 2081.25, 2979.25}},
+{'c', {2081.25, 2979.24, 2129.39, 2952.66, 2112.25, 2948.25}},
+{'c', {2062.28, 2933.83, 2112.44, 2905.67, 2042.25, 2927.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2095.25, 2927.25}},
+{'v', {2056.12, 3039.43, 2133.25, 2979.25}},
+{'c', {2133.25, 2979.24, 2181.68, 2954.18, 2165.25, 2948.25}},
+{'c', {2128.28, 2933.83, 2165.24, 2905.67, 2095.25, 2927.25}},
+{'f', {}},
+{'m', {2095.25, 2927.25}},
+{'v', {2056.12, 3039.43, 2133.25, 2979.25}},
+{'c', {2133.25, 2979.24, 2181.68, 2954.18, 2165.25, 2948.25}},
+{'c', {2128.28, 2933.83, 2165.24, 2905.67, 2095.25, 2927.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2167.25, 2924.25}},
+{'c', {2167.25, 2924.24, 2128.72, 3037.23, 2206.25, 2977.25}},
+{'c', {2206.25, 2977.24, 2254.37, 2951.73, 2238.25, 2945.25}},
+{'c', {2205.28, 2933.83, 2237.85, 2903.47, 2167.25, 2924.25}},
+{'f', {}},
+{'m', {2167.25, 2924.25}},
+{'c', {2167.25, 2924.24, 2128.72, 3037.23, 2206.25, 2977.25}},
+{'c', {2206.25, 2977.24, 2254.37, 2951.73, 2238.25, 2945.25}},
+{'c', {2205.28, 2933.83, 2237.85, 2903.47, 2167.25, 2924.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2238.25, 2925.25}},
+{'v', {2200, 3038.11, 2277.25, 2978.25}},
+{'v', {2319.69, 2960.67, 2309.25, 2946.25}},
+{'c', {2298.57, 2932.51, 2309.13, 2904.35, 2238.25, 2925.25}},
+{'f', {}},
+{'m', {2238.25, 2925.25}},
+{'v', {2200, 3038.11, 2277.25, 2978.25}},
+{'v', {2319.69, 2960.67, 2309.25, 2946.25}},
+{'c', {2298.57, 2932.51, 2309.13, 2904.35, 2238.25, 2925.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2312.25, 2918.25}},
+{'v', {2259.4, 3054.39, 2360.25, 2984.25}},
+{'v', {2413.4, 2961.99, 2400.25, 2944.25}},
+{'c', {2387, 2926.79, 2400.2, 2891.59, 2312.25, 2918.25}},
+{'f', {}},
+{'m', {2312.25, 2918.25}},
+{'v', {2259.4, 3054.39, 2360.25, 2984.25}},
+{'v', {2413.4, 2961.99, 2400.25, 2944.25}},
+{'c', {2387, 2926.79, 2400.2, 2891.59, 2312.25, 2918.25}},
+{'h', {}},
+{'S', {}},
+{'w', {22}},
+{'R', {0.651, 0.149, 0.298}},
+{'m', {2000.25, 3107.25}},
+{'c', {2000.25, 3107.24, 2145, 3137.99, 2211.25, 3103.25}},
+{'v', {2277, 3089.59, 2290.25, 3094.25}},
+{'y', {2303.4, 3098.39, 2338.25, 3103.25}},
+{'S', {}},
+{'m', {2374.25, 3010.25}},
+{'c', {2374.25, 3010.24, 2505.8, 3159.99, 2638.25, 3111.25}},
+{'c', {2714.97, 3083.29, 2703.8, 3120.39, 2712.25, 3151.25}},
+{'c', {2721.4, 3181.99, 2723.6, 3228.19, 2778.25, 3261.25}},
+{'S', {}},
+{'m', {2563.25, 3186.25}},
+{'c', {2563.25, 3186.24, 2519, 3305.19, 2488.25, 3164.25}},
+{'c', {2457.4, 3023.59, 2422.2, 2983.99, 2404.25, 2953.25}},
+{'c', {2404.25, 2953.24, 2404.6, 2895.99, 2497.25, 2900.25}},
+{'c', {2497.25, 2900.24, 2615.8, 2904.79, 2620.25, 2935.25}},
+{'c', {2624.6, 2966.39, 2607, 3093.99, 2563.25, 3186.25}},
+{'f', {}},
+{'w', {5.5}},
+{'G', {0}},
+{'m', {2563.25, 3186.25}},
+{'c', {2563.25, 3186.24, 2519, 3305.19, 2488.25, 3164.25}},
+{'c', {2457.4, 3023.59, 2422.2, 2983.99, 2404.25, 2953.25}},
+{'c', {2404.25, 2953.24, 2404.6, 2895.99, 2497.25, 2900.25}},
+{'c', {2497.25, 2900.24, 2615.8, 2904.79, 2620.25, 2935.25}},
+{'c', {2624.6, 2966.39, 2607, 3093.99, 2563.25, 3186.25}},
+{'h', {}},
+{'S', {}},
+{'w', {22}},
+{'R', {0.651, 0.149, 0.298}},
+{'m', {2717.25, 3107.25}},
+{'c', {2717.25, 3107.24, 2756.6, 3133.59, 2783.25, 3120.25}},
+{'S', {}},
+{'m', {2788.25, 3287.25}},
+{'c', {2788.25, 3287.24, 2820.4, 3341.49, 2873.25, 3350.25}},
+{'S', {}},
+{'g', {0.702}},
+{'m', {1916.25, 2883.25}},
+{'v', {2114.2, 2847.59, 2162.25, 2865.25}},
+{'c', {2162.25, 2865.24, 2259.4, 2865.19, 2167.25, 2843.25}},
+{'c', {2167.25, 2843.24, 2026.2, 2843.19, 1938.25, 2856.25}},
+{'c', {1938.25, 2856.24, 1810.6, 2917.99, 1916.25, 2883.25}},
+{'f', {}},
+{'r', {1, 1, 0.8}},
+{'m', {2070.25, 3908.25}},
+{'v', {2263.8, 3907.99, 2286.25, 3899.25}},
+{'c', {2286.25, 3899.24, 2365, 3560.39, 2325.25, 3477.25}},
+{'v', {2312.2, 3445.99, 2281.25, 3507.25}},
+{'c', {2281.25, 3507.24, 2079, 3868.39, 2044.25, 3890.25}},
+{'c', {2008.6, 3912.39, 2057, 3907.99, 2070.25, 3908.25}},
+{'f', {}},
+{'w', {5.5}},
+{'G', {0}},
+{'m', {2070.25, 3908.25}},
+{'v', {2263.8, 3907.99, 2286.25, 3899.25}},
+{'c', {2286.25, 3899.24, 2365, 3560.39, 2325.25, 3477.25}},
+{'v', {2312.2, 3445.99, 2281.25, 3507.25}},
+{'c', {2281.25, 3507.24, 2079, 3868.39, 2044.25, 3890.25}},
+{'c', {2008.6, 3912.39, 2057, 3907.99, 2070.25, 3908.25}},
+{'h', {}},
+{'S', {}},
+{'m', {1222.25, 3937.25}},
+{'v', {1320, 3918.99, 1458.25, 3890.25}},
+{'c', {1458.24, 3890.24, 1511.4, 3643.99, 1546.25, 3591.25}},
+{'c', {1581.8, 3538.39, 1542.2, 3538.39, 1502.25, 3569.25}},
+{'c', {1463, 3599.99, 1300.2, 3753.99, 1278.25, 3802.25}},
+{'y', {1256.2, 3850.79, 1222.25, 3937.25}},
+{'f', {}},
+{'m', {1222.25, 3937.25}},
+{'v', {1320, 3918.99, 1458.25, 3890.25}},
+{'c', {1458.24, 3890.24, 1511.4, 3643.99, 1546.25, 3591.25}},
+{'c', {1581.8, 3538.39, 1542.2, 3538.39, 1502.25, 3569.25}},
+{'c', {1463, 3599.99, 1300.2, 3753.99, 1278.25, 3802.25}},
+{'y', {1256.2, 3850.79, 1222.25, 3937.25}},
+{'h', {}},
+{'S', {}},
+{'m', {1462.25, 3888.25}},
+{'c', {1462.25, 3888.24, 1526.37, 3870.97, 1537.25, 3846.25}},
+{'y', {1548.55, 3822.25, 1524.25, 3786.25}},
+{'c', {1524.24, 3786.25, 1513.25, 3749.63, 1500.25, 3773.25}},
+{'c', {1486.8, 3797.21, 1455.21, 3881.46, 1462.25, 3888.25}},
+{'f', {}},
+{'m', {1462.25, 3888.25}},
+{'c', {1462.25, 3888.24, 1526.37, 3870.97, 1537.25, 3846.25}},
+{'y', {1548.55, 3822.25, 1524.25, 3786.25}},
+{'c', {1524.24, 3786.25, 1513.25, 3749.63, 1500.25, 3773.25}},
+{'c', {1486.8, 3797.21, 1455.21, 3881.46, 1462.25, 3888.25}},
+{'h', {}},
+{'S', {}},
+{'g', {0}},
+{'m', {1463.25, 3890.25}},
+{'c', {1463.24, 3890.24, 1502.6, 3833.19, 1542.25, 3833.25}},
+{'c', {1581.8, 3833.19, 1586, 3837.73, 1617.25, 3831.25}},
+{'c', {1667.6, 3819.99, 1663.2, 3841.99, 1736.25, 3829.25}},
+{'c', {1764.84, 3823.51, 1793, 3833.19, 1824.25, 3820.25}},
+{'c', {1854.6, 3806.79, 1889.8, 3815.59, 1903.25, 3837.25}},
+{'y', {1916.2, 3859.59, 1969.25, 3906.25}},
+{'v', {1828.2, 3885.99, 1797.25, 3877.25}},
+{'c', {1797.25, 3877.24, 1551, 3863.99, 1463.25, 3890.25}},
+{'f', {}},
+{'r', {1, 1, 0.8}},
+{'m', {1953.25, 3888.25}},
+{'c', {1953.25, 3888.24, 1882.65, 3849.69, 1878.25, 3823.25}},
+{'y', {1873.85, 3796.89, 1936.25, 3756.25}},
+{'c', {1936.25, 3756.24, 1966.25, 3705.59, 1973.25, 3732.25}},
+{'c', {1979.45, 3758.39, 1962.4, 3883.79, 1953.25, 3888.25}},
+{'f', {}},
+{'m', {1953.25, 3888.25}},
+{'c', {1953.25, 3888.24, 1882.65, 3849.69, 1878.25, 3823.25}},
+{'y', {1873.85, 3796.89, 1936.25, 3756.25}},
+{'c', {1936.25, 3756.24, 1966.25, 3705.59, 1973.25, 3732.25}},
+{'c', {1979.45, 3758.39, 1962.4, 3883.79, 1953.25, 3888.25}},
+{'h', {}},
+{'S', {}},
+{'m', {1541.25, 3828.25}},
+{'v', {1618.34, 3705.02, 1620.25, 3828.25}},
+{'v', {1626.86, 3842.18, 1607.25, 3842.25}},
+{'c', {1540.67, 3843.25, 1557.4, 3888.38, 1541.25, 3828.25}},
+{'f', {}},
+{'m', {1541.25, 3828.25}},
+{'v', {1618.34, 3705.02, 1620.25, 3828.25}},
+{'v', {1626.86, 3842.18, 1607.25, 3842.25}},
+{'c', {1540.67, 3843.25, 1557.4, 3888.38, 1541.25, 3828.25}},
+{'h', {}},
+{'S', {}},
+{'m', {1620.25, 3825.25}},
+{'v', {1707.35, 3702.29, 1700.25, 3826.25}},
+{'c', {1700.24, 3826.24, 1700.43, 3830.02, 1681.25, 3831.25}},
+{'c', {1628.93, 3836.26, 1632.73, 3885.95, 1620.25, 3825.25}},
+{'f', {}},
+{'m', {1620.25, 3825.25}},
+{'v', {1707.35, 3702.29, 1700.25, 3826.25}},
+{'c', {1700.24, 3826.24, 1700.43, 3830.02, 1681.25, 3831.25}},
+{'c', {1628.93, 3836.26, 1632.73, 3885.95, 1620.25, 3825.25}},
+{'h', {}},
+{'S', {}},
+{'m', {1699.25, 3825.25}},
+{'v', {1787.12, 3708.35, 1780.25, 3814.25}},
+{'v', {1781.07, 3828.24, 1763.25, 3831.25}},
+{'c', {1720.07, 3839.31, 1717.75, 3875.05, 1699.25, 3825.25}},
+{'f', {}},
+{'m', {1699.25, 3825.25}},
+{'v', {1787.12, 3708.35, 1780.25, 3814.25}},
+{'v', {1781.07, 3828.24, 1763.25, 3831.25}},
+{'c', {1720.07, 3839.31, 1717.75, 3875.05, 1699.25, 3825.25}},
+{'h', {}},
+{'S', {}},
+{'m', {1775.25, 3824.25}},
+{'c', {1775.25, 3824.24, 1862.1, 3697.2, 1866.25, 3805.25}},
+{'c', {1866.24, 3805.25, 1884.01, 3819.95, 1865.25, 3822.25}},
+{'c', {1800.81, 3830.54, 1809.59, 3879.53, 1775.25, 3824.25}},
+{'f', {}},
+{'m', {1775.25, 3824.25}},
+{'c', {1775.25, 3824.24, 1862.1, 3697.2, 1866.25, 3805.25}},
+{'c', {1866.24, 3805.25, 1884.01, 3819.95, 1865.25, 3822.25}},
+{'c', {1800.81, 3830.54, 1809.59, 3879.53, 1775.25, 3824.25}},
+{'h', {}},
+{'S', {}},
+{'r', {0.902, 0.902, 0.702}},
+{'m', {1377.25, 3785.25}},
+{'l', {1293.25, 3802.25}},
+{'y', {1264.45, 3857.39, 1241.25, 3925.25}},
+{'c', {1241.25, 3925.24, 1311.2, 3914.04, 1448.25, 3883.25}},
+{'v', {1458.34, 3846.74, 1474.25, 3783.25}},
+{'l', {1377.25, 3785.25}},
+{'f', {}},
+{'m', {2093.25, 3837.25}},
+{'c', {2074.71, 3864.39, 2060.22, 3883.73, 2053.25, 3888.25}},
+{'c', {2019.93, 3908.91, 2065.5, 3904.77, 2078.25, 3905.25}},
+{'v', {2260.2, 3904.77, 2281.25, 3896.25}},
+{'v', {2286.68, 3871.82, 2294.25, 3834.25}},
+{'v', {2183.23, 3856.63, 2093.25, 3837.25}},
+{'f', {}},
+{'r', {0.8, 0.451, 0.149}},
+{'m', {2682.25, 5356.25}},
+{'c', {2987.61, 5312.91, 3268.33, 5704.95, 3287.25, 5811.25}},
+{'y', {3307.05, 5917.92, 3195.25, 6048.25}},
+{'c', {3210.25, 6082.48, 3157.01, 6237.36, 3099.25, 6339.25}},
+{'c', {3040.85, 6440.64, 2865.94, 6429.93, 2673.25, 6440.25}},
+{'c', {2498.76, 6450.32, 2295.48, 6193.8, 2281.25, 6174.25}},
+{'c', {2266.44, 6155.08, 2334.2, 5733.99, 2348.25, 5671.25}},
+{'y', {2363.24, 5608.15, 2334.25, 5317.25}},
+{'c', {2710.84, 5417.85, 2377.76, 5400.03, 2682.25, 5356.25}},
+{'f', {}},
+{'r', {0.922, 0.561, 0.318}},
+{'m', {2289.25, 6167.25}},
+{'c', {2274.71, 6148.3, 2341.24, 5734.87, 2355.25, 5673.25}},
+{'y', {2369.75, 5611.32, 2341.25, 5326.25}},
+{'c', {2700.63, 5423.88, 2384.01, 5406.98, 2683.25, 5364.25}},
+{'c', {2982.77, 5321.44, 3258.39, 5706.36, 3277.25, 5811.25}},
+{'y', {3296.4, 5915.45, 3187.25, 6044.25}},
+{'c', {3201.36, 6077.02, 3149.09, 6229.09, 3092.25, 6329.25}},
+{'c', {3035.04, 6428.67, 2863.32, 6418.14, 2674.25, 6428.25}},
+{'c', {2502.8, 6438.18, 2303.22, 6186.32, 2289.25, 6167.25}},
+{'f', {}},
+{'r', {0.941, 0.671, 0.49}},
+{'m', {2297.25, 6160.25}},
+{'c', {2282.98, 6141.53, 2348.28, 5735.75, 2362.25, 5675.25}},
+{'y', {2376.26, 5614.48, 2348.25, 5334.25}},
+{'c', {2693.72, 5435.4, 2390.25, 5413.93, 2684.25, 5372.25}},
+{'c', {2977.93, 5329.98, 3248.44, 5707.76, 3267.25, 5810.25}},
+{'y', {3285.75, 5912.99, 3178.25, 6039.25}},
+{'c', {3192.47, 6071.57, 3141.17, 6220.81, 3085.25, 6319.25}},
+{'c', {3029.23, 6416.7, 2860.69, 6406.38, 2675.25, 6416.25}},
+{'c', {2506.86, 6426.03, 2310.96, 6178.84, 2297.25, 6160.25}},
+{'f', {}},
+{'r', {0.961, 0.78, 0.659}},
+{'m', {2305.25, 6153.25}},
+{'c', {2291.25, 6134.75, 2355.32, 5736.63, 2369.25, 5677.25}},
+{'y', {2382.77, 5617.65, 2355.25, 5343.25}},
+{'c', {2679.11, 5442.53, 2396.5, 5420.88, 2685.25, 5379.25}},
+{'c', {2973.09, 5338.52, 3238.5, 5709.17, 3257.25, 5810.25}},
+{'y', {3275.11, 5910.53, 3170.25, 6034.25}},
+{'c', {3183.59, 6066.11, 3133.25, 6212.54, 3078.25, 6308.25}},
+{'c', {3023.43, 6404.73, 2858.06, 6394.6, 2675.25, 6404.25}},
+{'c', {2510.9, 6413.89, 2318.71, 6171.36, 2305.25, 6153.25}},
+{'f', {}},
+{'r', {0.98, 0.89, 0.831}},
+{'m', {2313.25, 6146.25}},
+{'c', {2299.53, 6127.97, 2362.36, 5737.51, 2376.25, 5679.25}},
+{'y', {2389.29, 5620.82, 2362.25, 5351.25}},
+{'c', {2668.91, 5449.66, 2402.75, 5427.84, 2685.25, 5387.25}},
+{'c', {2968.25, 5347.05, 3228.55, 5710.58, 3246.25, 5809.25}},
+{'y', {3264.46, 5908.06, 3161.25, 6029.25}},
+{'c', {3174.7, 6060.65, 3125.33, 6204.27, 3071.25, 6298.25}},
+{'c', {3017.62, 6392.77, 2855.43, 6382.83, 2676.25, 6393.25}},
+{'c', {2514.95, 6401.74, 2326.45, 6163.88, 2313.25, 6146.25}},
+{'f', {}},
+{'g', {1}},
+{'m', {2686.25, 5395.25}},
+{'c', {2963.4, 5355.6, 3218.61, 5712, 3236.25, 5809.25}},
+{'y', {3253.81, 5905.6, 3152.25, 6024.25}},
+{'c', {3165.81, 6055.2, 3117.4, 6196, 3064.25, 6288.25}},
+{'c', {3011.8, 6380.8, 2852.8, 6371.05, 2677.25, 6381.25}},
+{'c', {2519, 6389.6, 2334.2, 6156.4, 2321.25, 6139.25}},
+{'c', {2307.8, 6121.2, 2369.4, 5738.4, 2382.25, 5681.25}},
+{'y', {2395.8, 5624, 2369.25, 5360.25}},
+{'c', {2649.9, 5454.6, 2409, 5434.8, 2686.25, 5395.25}},
+{'f', {}},
+{'g', {0.8}},
+{'m', {3196.25, 5769.25}},
+{'c', {3196.25, 5769.24, 2890.8, 5685.6, 2763.25, 5703.25}},
+{'c', {2763.25, 5703.24, 2589.4, 5775.8, 2492.25, 5536.25}},
+{'v', {2453, 5456.8, 2431.25, 5435.25}},
+{'y', {2409, 5412.8, 3196.25, 5769.25}},
+{'f', {}},
+{'g', {0}},
+{'m', {3238.25, 5793.25}},
+{'c', {3238.25, 5793.24, 2919.4, 5659.2, 2809.25, 5663.25}},
+{'c', {2809.25, 5663.24, 2629, 5714.2, 2536.25, 5553.25}},
+{'c', {2536.25, 5553.24, 2444.2, 5452.4, 2409.25, 5435.25}},
+{'v', {2404.6, 5417.2, 2475.25, 5461.25}},
+{'l', {2589.25, 5404.25}},
+{'v', {2752.2, 5298.4, 2858.25, 5474.25}},
+{'c', {2858.25, 5474.24, 2901.8, 5597.6, 2902.25, 5619.25}},
+{'c', {2901.8, 5641.6, 3135, 5703.2, 3152.25, 5707.25}},
+{'c', {3170.21, 5712, 3242.81, 5758.2, 3238.25, 5793.25}},
+{'f', {}},
+{'r', {0.6, 0.8, 0.2}},
+{'m', {2717.25, 5398.25}},
+{'c', {2641.41, 5398.34, 2549.3, 5440.86, 2549.25, 5509.25}},
+{'c', {2549.3, 5578.33, 2641.41, 5647.24, 2717.25, 5647.25}},
+{'c', {2792.61, 5647.24, 2853.9, 5591.53, 2854.25, 5523.25}},
+{'c', {2853.9, 5454.06, 2792.61, 5398.34, 2717.25, 5398.25}},
+{'f', {}},
+{'r', {0.4, 0.6, 0}},
+{'m', {2677.25, 5582.25}},
+{'c', {2623.84, 5573.93, 2567.86, 5557.39, 2568.25, 5560.25}},
+{'c', {2585.59, 5610.58, 2655.56, 5647.24, 2717.25, 5647.25}},
+{'c', {2764.25, 5647.24, 2805.92, 5625.48, 2830.25, 5592.25}},
+{'v', {2772.04, 5596, 2677.25, 5582.25}},
+{'f', {}},
+{'g', {1}},
+{'m', {2809.25, 5584.25}},
+{'c', {2809.25, 5584.24, 2761, 5619.6, 2761.25, 5595.25}},
+{'c', {2761.25, 5595.24, 2800.6, 5547, 2809.25, 5584.25}},
+{'f', {}},
+{'g', {0}},
+{'m', {2699.25, 5495.25}},
+{'c', {2671.91, 5495.01, 2649.62, 5517.3, 2649.25, 5545.25}},
+{'c', {2649.62, 5572.29, 2671.91, 5594.57, 2699.25, 5594.25}},
+{'c', {2726.9, 5594.57, 2749.18, 5572.29, 2749.25, 5545.25}},
+{'c', {2749.18, 5517.3, 2726.9, 5495.01, 2699.25, 5495.25}},
+{'f', {}},
+{'r', {0.8, 0.451, 0.149}},
+{'m', {1555.25, 5641.25}},
+{'c', {1555.25, 5641.24, 1520.2, 5874.8, 1546.25, 5923.25}},
+{'c', {1546.24, 5923.24, 1665.4, 6033.2, 1661.25, 6073.25}},
+{'c', {1661.24, 6073.25, 1656.6, 6270.8, 1643.25, 6279.25}},
+{'c', {1630.2, 6288.4, 1546.6, 6354.4, 1480.25, 6284.25}},
+{'c', {1480.24, 6284.25, 1366.2, 6086, 1375.25, 6015.25}},
+{'l', {1375.25, 5993.25}},
+{'c', {1375.24, 5993.24, 1291.4, 5998, 1274.25, 5976.25}},
+{'v', {1260.6, 5918.8, 1247.25, 5914.25}},
+{'c', {1247.25, 5914.24, 1216.6, 5888, 1238.25, 5857.25}},
+{'c', {1238.24, 5857.24, 1216.6, 5830.8, 1221.25, 5787.25}},
+{'l', {1304.25, 5743.25}},
+{'c', {1304.24, 5743.25, 1326.6, 5584.4, 1445.25, 5527.25}},
+{'c', {1498.59, 5501.58, 1533.4, 5575.6, 1555.25, 5641.25}},
+{'f', {}},
+{'g', {1}},
+{'m', {1544.25, 5662.25}},
+{'v', {1512.72, 5871.72, 1536.25, 5915.25}},
+{'v', {1643.4, 6014.28, 1639.25, 6050.25}},
+{'c', {1639.24, 6050.25, 1635.48, 6228.12, 1623.25, 6236.25}},
+{'c', {1611.72, 6243.96, 1536.48, 6303.36, 1477.25, 6240.25}},
+{'v', {1374.12, 6061.8, 1382.25, 5998.25}},
+{'l', {1382.25, 5978.25}},
+{'c', {1382.25, 5978.24, 1306.8, 5982.6, 1291.25, 5963.25}},
+{'v', {1279.08, 5911.32, 1267.25, 5907.25}},
+{'v', {1239.48, 5883.6, 1259.25, 5856.25}},
+{'v', {1239.48, 5832.12, 1243.25, 5792.25}},
+{'l', {1318.25, 5753.25}},
+{'v', {1338.48, 5610.36, 1445.25, 5559.25}},
+{'c', {1493.27, 5535.83, 1524.6, 5602.44, 1544.25, 5662.25}},
+{'f', {}},
+{'r', {0.925, 0.588, 0.361}},
+{'m', {1638.25, 6268.25}},
+{'c', {1626.46, 6278.17, 1544.07, 6341.64, 1479.25, 6273.25}},
+{'v', {1368.18, 6079.95, 1377.25, 6011.25}},
+{'l', {1377.25, 5990.25}},
+{'v', {1295.25, 5994.15, 1278.25, 5972.25}},
+{'v', {1265.22, 5916.93, 1252.25, 5912.25}},
+{'c', {1252.25, 5912.24, 1222.32, 5886.9, 1244.25, 5857.25}},
+{'c', {1244.25, 5857.24, 1222.32, 5831.13, 1226.25, 5788.25}},
+{'l', {1308.25, 5745.25}},
+{'v', {1329.57, 5590.89, 1445.25, 5535.25}},
+{'c', {1497.25, 5510.15, 1531.2, 5582.31, 1552.25, 5646.25}},
+{'v', {1518.33, 5874.03, 1544.25, 5921.25}},
+{'v', {1659.9, 6028.47, 1655.25, 6067.25}},
+{'v', {1651.32, 6260.13, 1638.25, 6268.25}},
+{'f', {}},
+{'r', {0.953, 0.725, 0.576}},
+{'m', {1633.25, 6258.25}},
+{'c', {1622.72, 6267.94, 1541.54, 6328.88, 1479.25, 6262.25}},
+{'v', {1370.16, 6073.9, 1378.25, 6007.25}},
+{'l', {1378.25, 5986.25}},
+{'v', {1299.1, 5990.3, 1282.25, 5969.25}},
+{'c', {1282.24, 5969.24, 1269.84, 5915.06, 1257.25, 5911.25}},
+{'v', {1228.04, 5885.8, 1249.25, 5856.25}},
+{'c', {1249.24, 5856.25, 1228.04, 5831.46, 1232.25, 5789.25}},
+{'l', {1311.25, 5748.25}},
+{'v', {1332.54, 5597.38, 1445.25, 5543.25}},
+{'c', {1495.93, 5518.71, 1529, 5589.02, 1550.25, 5651.25}},
+{'v', {1516.46, 5873.26, 1541.25, 5919.25}},
+{'v', {1654.4, 6023.74, 1650.25, 6061.25}},
+{'c', {1650.24, 6061.25, 1646.04, 6249.46, 1633.25, 6258.25}},
+{'f', {}},
+{'r', {0.976, 0.863, 0.788}},
+{'m', {1628.25, 6247.25}},
+{'c', {1618.98, 6257.71, 1539.01, 6316.12, 1478.25, 6251.25}},
+{'v', {1372.14, 6067.85, 1380.25, 6002.25}},
+{'l', {1380.25, 5982.25}},
+{'c', {1380.25, 5982.24, 1302.95, 5986.45, 1286.25, 5966.25}},
+{'v', {1274.46, 5913.19, 1262.25, 5909.25}},
+{'v', {1233.76, 5884.7, 1254.25, 5856.25}},
+{'c', {1254.24, 5856.25, 1233.76, 5831.79, 1238.25, 5791.25}},
+{'l', {1315.25, 5750.25}},
+{'c', {1315.24, 5750.25, 1335.51, 5603.87, 1445.25, 5551.25}},
+{'c', {1494.6, 5527.27, 1526.8, 5595.73, 1547.25, 5657.25}},
+{'v', {1514.59, 5872.49, 1539.25, 5917.25}},
+{'v', {1648.9, 6019.01, 1645.25, 6055.25}},
+{'c', {1645.24, 6055.24, 1640.76, 6238.79, 1628.25, 6247.25}},
+{'f', {}},
+{'g', {1}},
+{'m', {1544.25, 5663.25}},
+{'c', {1544.25, 5663.24, 1512.72, 5871.72, 1536.25, 5915.25}},
+{'v', {1643.4, 6014.28, 1639.25, 6050.25}},
+{'c', {1639.24, 6050.25, 1635.48, 6228.12, 1623.25, 6236.25}},
+{'c', {1615.24, 6247.48, 1536.48, 6303.36, 1477.25, 6240.25}},
+{'v', {1374.12, 6061.8, 1382.25, 5998.25}},
+{'l', {1382.25, 5978.25}},
+{'c', {1382.25, 5978.24, 1306.8, 5982.6, 1291.25, 5963.25}},
+{'v', {1279.08, 5911.32, 1267.25, 5907.25}},
+{'v', {1239.48, 5883.6, 1259.25, 5856.25}},
+{'v', {1239.48, 5832.12, 1243.25, 5792.25}},
+{'l', {1318.25, 5753.25}},
+{'v', {1338.48, 5610.36, 1445.25, 5559.25}},
+{'c', {1493.27, 5535.83, 1524.6, 5603.54, 1544.25, 5663.25}},
+{'f', {}},
+{'g', {0.8}},
+{'m', {1510.25, 5732.25}},
+{'v', {1272.7, 5844, 1263.25, 5853.25}},
+{'v', {1362.9, 5762.6, 1371.25, 5762.25}},
+{'y', {1380.5, 5762.6, 1510.25, 5732.25}},
+{'f', {}},
+{'g', {0}},
+{'m', {1322.25, 5800.25}},
+{'v', {1524.6, 5760.4, 1524.25, 5712.25}},
+{'c', {1524.6, 5679.96, 1521.93, 5532.36, 1463.25, 5549.25}},
+{'c', {1370.6, 5575.6, 1410.2, 5734, 1322.25, 5800.25}},
+{'f', {}},
+{'r', {0.6, 0.8, 0.2}},
+{'m', {1414.25, 5758.25}},
+{'c', {1414.24, 5758.24, 1513.36, 5741.98, 1524.25, 5712.25}},
+{'c', {1531.2, 5694.4, 1538.49, 5603.01, 1478.25, 5591.25}},
+{'c', {1428.32, 5580.98, 1403.8, 5693.41, 1414.25, 5758.25}},
+{'f', {}},
+{'g', {0}},
+{'m', {2360.25, 5290.25}},
+{'c', {2355.07, 5309.32, 2369.5, 5307.88, 2389.25, 5314.25}},
+{'c', {2411.2, 5320.4, 2545.4, 5362.2, 2554.25, 5391.25}},
+{'y', {2563, 5419.4, 2708.25, 5371.25}},
+{'y', {2728, 5362.2, 2776.25, 5333.25}},
+{'y', {2829.2, 5320.4, 2902.25, 5316.25}},
+{'y', {2928.2, 5305, 2965.25, 5274.25}},
+{'y', {3126.2, 5162, 3262.25, 5241.25}},
+{'y', {3482.61, 5313.8, 3416.25, 5503.25}},
+{'y', {3383.61, 5602, 3419.25, 5639.25}},
+{'y', {3421.01, 5681.2, 3500.25, 5611.25}},
+{'y', {3528.81, 5564.6, 3537.25, 5509.25}},
+{'y', {3625.61, 5386.4, 3588.25, 5582.25}},
+{'c', {3590.41, 5593.2, 3559.61, 5632.8, 3559.25, 5646.25}},
+{'y', {3559.61, 5659.2, 3540.25, 5696.25}},
+{'y', {3506.81, 5734, 3533.25, 5811.25}},
+{'y', {3553.01, 5962.8, 3529.25, 5943.25}},
+{'y', {3515.61, 5962.8, 3414.25, 5853.25}},
+{'y', {3390.21, 5815.4, 3324.25, 5798.25}},
+{'y', {3293.41, 5778, 3256.25, 5793.25}},
+{'y', {3227.41, 5797.8, 3166.25, 5721.25}},
+{'c', {3196.61, 5723, 3223.01, 5674.6, 3249.25, 5672.25}},
+{'c', {3275.81, 5670.2, 3295.61, 5698.8, 3313.25, 5705.25}},
+{'y', {3330.81, 5712, 3361.25, 5648.25}},
+{'y', {3366.01, 5619.6, 3304.25, 5567.25}},
+{'y', {3300.01, 5516.2, 3282.25, 5534.25}},
+{'c', {3249.41, 5540.4, 3236.21, 5498.6, 3225.25, 5448.25}},
+{'y', {3214.21, 5397.4, 3168.25, 5393.25}},
+{'y', {3150.41, 5311.6, 3137.25, 5344.25}},
+{'y', {3135, 5406.2, 3069.25, 5342.25}},
+{'y', {3055.8, 5320.4, 3005.25, 5344.25}},
+{'y', {2930.4, 5366.6, 2957.25, 5388.25}},
+{'y', {2976.6, 5412.8, 3100.25, 5388.25}},
+{'y', {3124, 5406.2, 3036.25, 5450.25}},
+{'y', {3029.4, 5470, 3040.25, 5518.25}},
+{'y', {3053.6, 5553.6, 3128.25, 5615.25}},
+{'y', {3231.81, 5628.4, 3201.25, 5646.25}},
+{'y', {3132.81, 5703.2, 3069.25, 5619.25}},
+{'y', {3044.8, 5551.4, 2853.25, 5386.25}},
+{'c', {2800.6, 5349, 2829.2, 5423.8, 2785.25, 5386.25}},
+{'y', {2741.2, 5349, 2514.25, 5448.25}},
+{'c', {2387.41, 5461.12, 2357.36, 5287.96, 2318.25, 5322.25}},
+{'c', {2318.25, 5322.24, 2378.14, 5228.56, 2360.25, 5290.25}},
+{'f', {}},
+{'m', {4503.25, 7120.25}},
+{'v', {4221.81, 7032, 4191.25, 6825.25}},
+{'c', {4191.25, 6825.24, 4164.61, 6574.4, 4389.25, 6381.25}},
+{'v', {4393.41, 6310.4, 4415.25, 6275.25}},
+{'c', {4415.25, 6275.24, 4397.81, 6222.4, 4604.25, 6306.25}},
+{'l', {4904.25, 6398.25}},
+{'c', {4904.25, 6398.24, 4974.21, 6424.8, 5031.25, 6521.25}},
+{'c', {5088.61, 6618.4, 5255.81, 6825.2, 5216.25, 7102.25}},
+{'c', {5216.24, 7102.24, 5229.41, 7225.6, 5163.25, 7230.25}},
+{'c', {5163.24, 7230.25, 5071.01, 7247.6, 4992.25, 7164.25}},
+{'v', {4917.01, 7128.8, 4890.25, 7133.25}},
+{'l', {4503.25, 7120.25}},
+{'f', {}},
+{'m', {5104.25, 7131.25}},
+{'v', {5127.34, 7229.12, 5074.25, 7176.25}},
+{'v', {4996.66, 7112.96, 4914.25, 7113.25}},
+{'c', {4914.25, 7113.24, 4754.65, 7088.76, 4706.25, 6943.25}},
+{'v', {4662.69, 6648.32, 4750.25, 6585.25}},
+{'c', {4750.25, 6585.24, 4803.05, 6503.12, 4880.25, 6575.25}},
+{'c', {4957.94, 6648.32, 5128.44, 6980.85, 5104.25, 7131.25}},
+{'f', {}},
+{'g', {0.2}},
+{'m', {5100.25, 7127.25}},
+{'v', {5123.12, 7223.13, 5071.25, 7171.25}},
+{'v', {4994.81, 7109.09, 4914.25, 7109.25}},
+{'v', {4757.2, 7085.33, 4709.25, 6943.25}},
+{'v', {4666.91, 6652.89, 4752.25, 6591.25}},
+{'v', {4804.72, 6510.33, 4881.25, 6581.25}},
+{'c', {4956.8, 6652.89, 5123.89, 6979.64, 5100.25, 7127.25}},
+{'f', {}},
+{'g', {0.4}},
+{'m', {5096.25, 7123.25}},
+{'v', {5118.89, 7217.15, 5067.25, 7166.25}},
+{'c', {5067.25, 7166.24, 4992.96, 7105.21, 4913.25, 7105.25}},
+{'v', {4759.75, 7081.89, 4713.25, 6942.25}},
+{'v', {4671.14, 6657.47, 4755.25, 6597.25}},
+{'v', {4806.39, 6517.55, 4881.25, 6587.25}},
+{'c', {4955.65, 6657.47, 5119.33, 6978.43, 5096.25, 7123.25}},
+{'f', {}},
+{'g', {0.6}},
+{'m', {5092.25, 7119.25}},
+{'v', {5114.67, 7211.17, 5064.25, 7161.25}},
+{'c', {5064.24, 7161.25, 4991.12, 7101.34, 4913.25, 7101.25}},
+{'v', {4762.3, 7078.46, 4716.25, 6941.25}},
+{'v', {4675.36, 6662.05, 4757.25, 6602.25}},
+{'v', {4808.07, 6524.77, 4881.25, 6593.25}},
+{'c', {4954.51, 6662.05, 5114.78, 6977.22, 5092.25, 7119.25}},
+{'f', {}},
+{'g', {0.8}},
+{'m', {5088.25, 7115.25}},
+{'c', {5088.24, 7115.24, 5110.45, 7205.18, 5061.25, 7156.25}},
+{'v', {4989.26, 7097.47, 4913.25, 7097.25}},
+{'v', {4764.86, 7075.03, 4720.25, 6940.25}},
+{'v', {4679.59, 6666.62, 4760.25, 6608.25}},
+{'v', {4809.74, 6531.98, 4881.25, 6599.25}},
+{'c', {4953.35, 6666.62, 5110.22, 6976.01, 5088.25, 7115.25}},
+{'f', {}},
+{'g', {1}},
+{'m', {5083.25, 7111.25}},
+{'c', {5083.25, 7111.24, 5106.21, 7199.2, 5058.25, 7151.25}},
+{'v', {4987.41, 7093.6, 4912.25, 7093.25}},
+{'c', {4912.24, 7093.24, 4767.41, 7071.6, 4723.25, 6939.25}},
+{'c', {4723.24, 6939.24, 4683.81, 6671.2, 4763.25, 6614.25}},
+{'v', {4811.41, 6539.2, 4882.25, 6605.25}},
+{'c', {4952.21, 6671.2, 5105.66, 6974.8, 5083.25, 7111.25}},
+{'f', {}},
+{'r', {0.6, 0.149, 0}},
+{'m', {2756.25, 4876.25}},
+{'v', {2532.2, 5087.2, 2444.25, 5096.25}},
+{'v', {2065.8, 5140, 1903.25, 4942.25}},
+{'v', {2096.6, 5166.4, 2400.25, 5105.25}},
+{'v', {2162.6, 5153.2, 2026.25, 5118.25}},
+{'v', {1841.4, 5118, 1736.25, 4964.25}},
+{'l', {1705.25, 4911.25}},
+{'c', {1705.24, 4911.24, 1749, 5074, 1951.25, 5140.25}},
+{'v', {2202.2, 5192.8, 2321.25, 5140.25}},
+{'v', {2083.4, 5214.8, 1973.25, 5193.25}},
+{'v', {1639, 5219.2, 1498.25, 4929.25}},
+{'v', {1542.2, 5087.2, 1705.25, 5166.25}},
+{'c', {1705.24, 5166.24, 1854.6, 5263.2, 2079.25, 5232.25}},
+{'c', {2079.25, 5232.24, 2237.4, 5197.2, 2294.25, 5171.25}},
+{'c', {2351.8, 5144.4, 2338.6, 5175.2, 2246.25, 5228.25}},
+{'v', {2184.6, 5338, 2030.25, 5333.25}},
+{'c', {2030.25, 5333.24, 1559.8, 5294, 1445.25, 5162.25}},
+{'v', {1595, 5285.2, 1709.25, 5316.25}},
+{'v', {1955.8, 5404, 2048.25, 5395.25}},
+{'c', {2048.25, 5395.24, 2321, 5384.2, 2404.25, 5428.25}},
+{'c', {2404.25, 5428.24, 2281.4, 5373.2, 2316.25, 5338.25}},
+{'c', {2351.8, 5302.8, 2426.6, 5219.2, 2426.25, 5206.25}},
+{'c', {2426.6, 5192.8, 2692.8, 4949.7, 2732.25, 4901.25}},
+{'l', {2756.25, 4876.25}},
+{'f', {}},
+{'g', {0.8}},
+{'m', {4279.25, 2742.25}},
+{'v', {4108.5, 3143.5, 3971.25, 3248.25}},
+{'v', {4257, 3072, 4295.25, 2874.25}},
+{'v', {4295.5, 2764, 4279.25, 2742.25}},
+{'f', {}},
+{'m', {4796.25, 2659.25}},
+{'v', {4504.5, 3264.5, 4301.25, 3528.25}},
+{'v', {4779.5, 3116, 4834.25, 2824.25}},
+{'l', {4840.25, 2764.25}},
+{'l', {4807.25, 2791.25}},
+{'v', {4801.5, 2692.5, 4796.25, 2659.25}},
+{'f', {}},
+{'m', {5417.25, 3193.25}},
+{'v', {4730, 3847.5, 4713.25, 3875.25}},
+{'v', {5379, 3149, 5412.25, 3066.25}},
+{'v', {5390, 3165.5, 5417.25, 3193.25}},
+{'f', {}},
+{'m', {3344.25, 2714.25}},
+{'v', {3558.5, 3286.5, 3767.25, 3039.25}},
+{'v', {3932.5, 2929, 3927.25, 2896.25}},
+{'v', {3883, 2967.5, 3685.25, 2962.25}},
+{'v', {3476, 2995, 3344.25, 2714.25}},
+{'f', {}},
+{'m', {5439.25, 4117.25}},
+{'v', {4944.5, 4430.5, 4862.25, 4447.25}},
+{'c', {4732.12, 4472.97, 5406.5, 4128, 5461.25, 4012.25}},
+{'v', {5483.5, 4040, 5439.25, 4117.25}},
+{'f', {}},
+{'g', {0}},
+{'m', {3782.25, 2944.25}},
+{'c', {3782.25, 2944.24, 4006.21, 2966.39, 4081.25, 3041.25}},
+{'l', {4129.25, 3001.25}},
+{'l', {4323.25, 3424.25}},
+{'l', {4362.25, 3367.25}},
+{'v', {4521.01, 3529.59, 4512.25, 3617.25}},
+{'y', {4503.41, 3705.59, 4653.25, 3551.25}},
+{'c', {4653.25, 3551.24, 4644.21, 3679.19, 4723.25, 3604.25}},
+{'c', {4723.24, 3604.24, 4697.01, 3775.99, 4789.25, 3688.25}},
+{'c', {4789.24, 3688.25, 4673.5, 4019.71, 4921.25, 3736.25}},
+{'y', {4983.01, 3665.99, 4934.25, 3741.25}},
+{'c', {4934.24, 3741.25, 4648.61, 4268.79, 4886.25, 4110.25}},
+{'c', {4886.24, 4110.24, 4908.21, 4361.19, 4895.25, 4409.25}},
+{'c', {4881.81, 4457.99, 4859.81, 4704.4, 4807.25, 4761.25}},
+{'c', {4754.21, 4818.8, 4811.41, 4836.4, 4873.25, 4779.25}},
+{'c', {4873.25, 4779.24, 4749.81, 5043.2, 4895.25, 4911.25}},
+{'c', {4895.25, 4911.24, 4855.41, 5078.4, 4807.25, 5109.25}},
+{'c', {4807.25, 5109.24, 4745.41, 5298.4, 4912.25, 5179.25}},
+{'c', {4912.24, 5179.24, 4864.21, 5316, 4829.25, 5351.25}},
+{'c', {4829.25, 5351.24, 4701.41, 5654.8, 4780.25, 5602.25}},
+{'l', {4829.25, 5562.25}},
+{'c', {4829.25, 5562.24, 4754.21, 5716.4, 4824.25, 5668.25}},
+{'y', {4895.01, 5619.6, 4895.25, 5624.25}},
+{'v', {4661.81, 5989.2, 4886.25, 5795.25}},
+{'c', {4886.24, 5795.24, 4796.46, 5948.7, 4758.25, 6024.25}},
+{'c', {4758.24, 6024.24, 4551.81, 6248.8, 4710.25, 6178.25}},
+{'l', {4763.25, 6161.25}},
+{'v', {4666.21, 6270.8, 4578.25, 6288.25}},
+{'c', {4490.21, 6306, 4604.61, 6376.4, 4675.25, 6354.25}},
+{'y', {4745.41, 6332.4, 4917.25, 6249.25}},
+{'v', {5057.81, 6042, 5102.25, 6037.25}},
+{'c', {5102.25, 6037.24, 4881.81, 6121.2, 4948.25, 6033.25}},
+{'c', {4948.25, 6033.24, 5106.21, 5879.2, 5027.25, 5883.25}},
+{'c', {5027.25, 5883.24, 4961.01, 5804.4, 5014.25, 5707.25}},
+{'c', {5014.25, 5707.24, 4810.76, 5909.9, 4974.25, 5628.25}},
+{'l', {5049.25, 5448.25}},
+{'v', {4780.61, 5720.8, 4904.25, 5479.25}},
+{'v', {5093.01, 5219.2, 5115.25, 5215.25}},
+{'y', {5137.01, 5210.4, 5185.25, 5113.25}},
+{'l', {5137.25, 5135.25}},
+{'l', {5194.25, 5039.25}},
+{'c', {5194.24, 5039.25, 5071.01, 5170.8, 5137.25, 5025.25}},
+{'l', {5198.25, 4867.25}},
+{'c', {5198.24, 4867.24, 4974.21, 5109.2, 5124.25, 4783.25}},
+{'c', {5124.25, 4783.24, 4943.41, 4840.8, 5040.25, 4651.25}},
+{'c', {5040.24, 4651.24, 5022.61, 4475.59, 5027.25, 4418.25}},
+{'c', {5031.41, 4361.19, 5044.61, 4048.79, 4996.25, 3961.25}},
+{'c', {4947.81, 3872.79, 5062.21, 3661.59, 5084.25, 3617.25}},
+{'c', {5106.21, 3573.59, 5145.81, 3454.79, 5049.25, 3556.25}},
+{'c', {4952.21, 3657.19, 5000.61, 3595.59, 5022.25, 3499.25}},
+{'c', {5044.61, 3401.99, 5110.61, 3230.39, 5102.25, 3169.25}},
+{'v', {5088.61, 3155.59, 5053.25, 3195.25}},
+{'c', {5053.24, 3195.24, 4890.61, 3445.99, 4908.25, 3287.25}},
+{'c', {4908.24, 3287.24, 4895.01, 3199.59, 4860.25, 3103.25}},
+{'v', {4824.61, 2983.99, 4824.25, 3081.25}},
+{'c', {4824.24, 3081.25, 4789.41, 3265.59, 4758.25, 3182.25}},
+{'c', {4727.81, 3098.39, 4688.21, 3032.39, 4657.25, 3006.25}},
+{'c', {4626.61, 2979.59, 4569.41, 3230.39, 4556.25, 3116.25}},
+{'c', {4556.24, 3116.25, 4424.21, 3252.39, 4371.25, 3072.25}},
+{'l', {4244.25, 2891.25}},
+{'c', {4244.25, 2891.24, 4239.41, 3027.99, 4226.25, 2962.25}},
+{'v', {3896.21, 2895.99, 3782.25, 2944.25}},
+{'f', {}},
+{'m', {3403.25, 6869.25}},
+{'c', {3403.25, 6869.24, 3275.81, 6957.2, 3232.25, 6953.25}},
+{'c', {3187.81, 6948.4, 3535.41, 7049.6, 3988.25, 6746.25}},
+{'v', {4041.41, 6715.2, 4081.25, 6719.25}},
+{'c', {4081.25, 6719.24, 4116.21, 6693.2, 4085.25, 6653.25}},
+{'c', {4085.25, 6653.24, 3988.61, 6548, 4112.25, 6425.25}},
+{'v', {4314.21, 6350, 4252.25, 6447.25}},
+{'v', {4371.41, 6402.8, 4398.25, 6359.25}},
+{'y', {4424.21, 6314.8, 4411.25, 6359.25}},
+{'v', {4340.61, 6438, 4274.25, 6495.25}},
+{'c', {4274.25, 6495.24, 4217.41, 6517.2, 4186.25, 6609.25}},
+{'c', {4155.81, 6702, 4129.41, 6812, 4178.25, 6847.25}},
+{'c', {4178.25, 6847.24, 4133.81, 6798.8, 4142.25, 6843.25}},
+{'c', {4151.41, 6886.8, 4191.01, 6926.4, 4208.25, 6931.25}},
+{'c', {4226.21, 6935.2, 4406.61, 7109, 4481.25, 7113.25}},
+{'c', {4481.25, 7113.24, 4380.21, 7098, 4347.25, 7109.25}},
+{'c', {4314.21, 7120, 4021.61, 7245.4, 3955.25, 7258.25}},
+{'c', {3955.25, 7258.24, 3770.81, 7331.2, 3903.25, 7309.25}},
+{'c', {3903.25, 7309.24, 4296.61, 7267.4, 4497.25, 7122.25}},
+{'c', {4497.25, 7122.24, 4417.61, 7214.6, 4215.25, 7291.25}},
+{'c', {4215.25, 7291.24, 3971.01, 7430.2, 3584.25, 7375.25}},
+{'c', {3584.25, 7375.24, 3388.01, 7340, 3302.25, 7320.25}},
+{'c', {3302.25, 7320.24, 3273.61, 7326.8, 3267.25, 7331.25}},
+{'c', {3260.41, 7335.6, 3130.6, 7434.6, 2827.25, 7357.25}},
+{'c', {2827.25, 7357.24, 2640, 7307, 2545.25, 7254.25}},
+{'c', {2545.25, 7254.24, 2378.2, 7241, 2338.25, 7206.25}},
+{'v', {2134, 7045.2, 2112.25, 7036.25}},
+{'c', {2090, 7027.6, 1964.6, 6944, 1956.25, 6939.25}},
+{'c', {1956.25, 6939.24, 2226.4, 7012.2, 2253.25, 7038.25}},
+{'c', {2279.2, 7065, 2470.6, 7093.6, 2497.25, 7078.25}},
+{'c', {2523.4, 7062.8, 2615.8, 7069.4, 2510.25, 7063.25}},
+{'v', {3341.81, 6900, 3350.25, 6878.25}},
+{'y', {3359.41, 6856, 3403.25, 6869.25}},
+{'f', {}},
+{'r', {0.8, 0.451, 0.149}},
+{'m', {4189.25, 6970.25}},
+{'c', {4189.25, 6970.24, 4076.61, 7051.8, 4054.25, 7052.25}},
+{'c', {4032.61, 7051.8, 3896.21, 7164, 3850.25, 7159.25}},
+{'c', {3803.81, 7155.2, 3669.61, 7265.2, 3368.25, 7175.25}},
+{'v', {3361.61, 7197, 3401.25, 7206.25}},
+{'v', {3471.61, 7230, 3476.25, 7236.25}},
+{'c', {3476.25, 7236.24, 3698.21, 7282.8, 3777.25, 7243.25}},
+{'c', {3777.25, 7243.24, 3878.61, 7214.6, 3947.25, 7146.25}},
+{'c', {3947.25, 7146.24, 4070.01, 7111.2, 4105.25, 7122.25}},
+{'c', {4105.25, 7122.24, 4202.01, 7098, 4206.25, 7078.25}},
+{'c', {4206.25, 7078.24, 4270.21, 7045.2, 4250.25, 7016.25}},
+{'c', {4250.25, 7016.24, 4254.81, 6999, 4189.25, 6970.25}},
+{'f', {}},
+{'m', {4051.25, 6993.25}},
+{'c', {4060.5, 6986.64, 4071.71, 6985.57, 4078.25, 6976.25}},
+{'c', {4080.94, 6973.21, 4077.77, 6969.63, 4074.25, 6968.25}},
+{'c', {4062.82, 6965.07, 4051.21, 6971.34, 4038.25, 6965.25}},
+{'c', {4034.23, 6962.6, 4027.16, 6964.6, 4021.25, 6966.25}},
+{'c', {4003.13, 6971.09, 3983.01, 6971.37, 3964.25, 6964.25}},
+{'c', {3942.57, 6976.22, 3916.61, 6969.79, 3893.25, 6981.25}},
+{'c', {3893.09, 6981.1, 3890.64, 6977.29, 3890.25, 6977.25}},
+{'c', {3856.32, 6990.19, 3815.15, 6987.07, 3788.25, 7012.25}},
+{'c', {3761.7, 7016.73, 3735.77, 7021.81, 3709.25, 7029.25}},
+{'c', {3689.07, 7034.89, 3673.57, 7045.77, 3656.25, 7055.25}},
+{'c', {3640.6, 7062.71, 3624.57, 7068.45, 3607.25, 7072.25}},
+{'c', {3587.21, 7077.52, 3567.07, 7076.29, 3546.25, 7082.25}},
+{'c', {3545.22, 7082.28, 3543.11, 7078.5, 3542.25, 7079.25}},
+{'c', {3538.64, 7079.98, 3535.45, 7086.39, 3533.25, 7086.25}},
+{'c', {3515.08, 7080.07, 3498.71, 7090.76, 3480.25, 7087.25}},
+{'c', {3467.45, 7100.46, 3448.44, 7097.77, 3431.25, 7102.25}},
+{'c', {3398.83, 7111.96, 3364.14, 7097.99, 3331.25, 7109.25}},
+{'c', {3376.05, 7129.26, 3427.61, 7115.71, 3472.25, 7138.25}},
+{'c', {3497.9, 7151.51, 3527.18, 7139.39, 3555.25, 7148.25}},
+{'c', {3560.81, 7149.34, 3568.41, 7151.56, 3573.25, 7144.25}},
+{'c', {3574.28, 7145.68, 3576.29, 7148.31, 3577.25, 7148.25}},
+{'c', {3603.88, 7135.28, 3629.34, 7121.26, 3656.25, 7109.25}},
+{'c', {3660.39, 7107.93, 3666.24, 7110.48, 3669.25, 7108.25}},
+{'c', {3685.73, 7094.89, 3707.11, 7096.02, 3722.25, 7082.25}},
+{'c', {3741.09, 7088.12, 3760.81, 7083.94, 3780.25, 7091.25}},
+{'c', {3780.68, 7091.1, 3783.25, 7087.35, 3783.25, 7087.25}},
+{'c', {3796.04, 7095.82, 3808.75, 7092.84, 3818.25, 7089.25}},
+{'c', {3822.28, 7088.23, 3829.57, 7085.51, 3833.25, 7084.25}},
+{'c', {3845.31, 7081.66, 3854.73, 7076.38, 3867.25, 7074.25}},
+{'c', {3868.95, 7074.17, 3870.98, 7077.91, 3872.25, 7077.25}},
+{'c', {3884.13, 7072.79, 3895.59, 7073.4, 3903.25, 7060.25}},
+{'c', {3904.29, 7062.08, 3906.11, 7064.7, 3907.25, 7064.25}},
+{'c', {3918.31, 7060.69, 3925.45, 7052.38, 3937.25, 7049.25}},
+{'c', {3942.89, 7048.56, 3949.62, 7041.68, 3956.25, 7039.25}},
+{'c', {3982.27, 7031.7, 4002.55, 7014.88, 4026.25, 7005.25}},
+{'c', {4035.03, 7001.63, 4044.66, 6999.05, 4051.25, 6993.25}},
+{'f', {}},
+{'m', {3208.25, 7150.25}},
+{'c', {3180.96, 7169.1, 3154.91, 7181.27, 3128.25, 7201.25}},
+{'c', {3126.05, 7202.38, 3122.09, 7200.51, 3120.25, 7202.25}},
+{'c', {3108.8, 7208.55, 3099.03, 7214.94, 3088.25, 7222.25}},
+{'c', {3082.3, 7226.8, 3073.27, 7226.68, 3067.25, 7229.25}},
+{'c', {3039.7, 7243.73, 3010.77, 7249.86, 2983.25, 7263.25}},
+{'c', {2990.7, 7270.02, 3003.04, 7267.33, 3009.25, 7276.25}},
+{'c', {3011.74, 7273.12, 3014.36, 7269.99, 3018.25, 7272.25}},
+{'c', {3036.7, 7282.04, 3057.05, 7283.72, 3075.25, 7282.25}},
+{'c', {3094.23, 7281.72, 3113.15, 7278.36, 3132.25, 7275.25}},
+{'c', {3136.05, 7274.86, 3138.24, 7269.1, 3142.25, 7268.25}},
+{'c', {3166.3, 7260.74, 3192.58, 7266.52, 3216.25, 7257.25}},
+{'c', {3233.39, 7250.38, 3250.66, 7241.33, 3264.25, 7227.25}},
+{'c', {3267.06, 7224.44, 3263.49, 7221.13, 3260.25, 7219.25}},
+{'c', {3264.68, 7220.2, 3267.68, 7217.93, 3269.25, 7214.25}},
+{'c', {3269.82, 7211.86, 3269.82, 7208.54, 3269.25, 7206.25}},
+{'c', {3267.67, 7202.48, 3264.6, 7201.36, 3260.25, 7201.25}},
+{'c', {3245.01, 7198.71, 3264.5, 7213.89, 3257.25, 7209.25}},
+{'c', {3242.8, 7200.01, 3251, 7184.55, 3243.25, 7170.25}},
+{'c', {3239.73, 7172.73, 3237.2, 7175.13, 3238.25, 7179.25}},
+{'c', {3240.98, 7173.66, 3234.36, 7170.46, 3232.25, 7167.25}},
+{'c', {3228.28, 7159.02, 3218.39, 7143.45, 3208.25, 7150.25}},
+{'f', {}},
+{'m', {2851.25, 7069.25}},
+{'c', {2816.48, 7078.03, 2782.93, 7076.77, 2750.25, 7091.25}},
+{'c', {2749.02, 7091.11, 2746.57, 7087.29, 2746.25, 7087.25}},
+{'c', {2730.84, 7094.12, 2720.74, 7104.75, 2709.25, 7116.25}},
+{'c', {2698.86, 7126.03, 2680.62, 7121.77, 2666.25, 7127.25}},
+{'c', {2663.04, 7128.49, 2660.81, 7134.12, 2657.25, 7134.25}},
+{'c', {2644.09, 7136.28, 2633.89, 7146.72, 2622.25, 7153.25}},
+{'c', {2648.09, 7161.74, 2674.61, 7161.4, 2701.25, 7165.25}},
+{'c', {2702.94, 7165.82, 2704.54, 7162.29, 2706.25, 7162.25}},
+{'c', {2707.49, 7162.29, 2708.93, 7164.72, 2710.25, 7166.25}},
+{'c', {2712.54, 7163.12, 2715.86, 7159.53, 2718.25, 7162.25}},
+{'c', {2724.72, 7168.27, 2730.84, 7166.18, 2737.25, 7165.25}},
+{'c', {2738.36, 7165.62, 2739.73, 7162.29, 2741.25, 7162.25}},
+{'c', {2742.7, 7162.29, 2744.14, 7165.71, 2745.25, 7165.25}},
+{'c', {2747.09, 7165.7, 2748.54, 7162.29, 2750.25, 7162.25}},
+{'c', {2751.49, 7162.29, 2752.93, 7164.72, 2754.25, 7166.25}},
+{'c', {2762.01, 7157.6, 2771.75, 7163.69, 2781.25, 7161.25}},
+{'c', {2792.21, 7159.22, 2795.14, 7146.6, 2807.25, 7143.25}},
+{'c', {2858.82, 7128.97, 2904.33, 7105.38, 2952.25, 7082.25}},
+{'c', {2955.7, 7080.8, 2958.01, 7078.08, 2957.25, 7074.25}},
+{'c', {2959.73, 7073.8, 2963.18, 7074.77, 2965.25, 7073.25}},
+{'c', {2976.91, 7065.26, 2988.36, 7059.23, 2996.25, 7047.25}},
+{'c', {2998.4, 7043.43, 2994.78, 7038.95, 2992.25, 7039.25}},
+{'c', {2943.5, 7050.47, 2899.43, 7057.16, 2851.25, 7069.25}},
+{'f', {}},
+{'m', {2698.25, 6583.25}},
+{'c', {2681.2, 6596.38, 2674.78, 6618.72, 2662.25, 6638.25}},
+{'c', {2660.02, 6642.17, 2663.03, 6645.62, 2666.25, 6646.25}},
+{'c', {2672.68, 6648.35, 2678.65, 6643.09, 2683.25, 6641.25}},
+{'c', {2702.35, 6631.39, 2719.23, 6617.73, 2741.25, 6616.25}},
+{'c', {2763.09, 6591.62, 2809.95, 6587.39, 2810.25, 6550.25}},
+{'c', {2810.04, 6540.75, 2794.33, 6550.85, 2789.25, 6541.25}},
+{'c', {2762.64, 6552.43, 2736.36, 6551.3, 2710.25, 6567.25}},
+{'c', {2703.39, 6571.62, 2707.02, 6576.61, 2698.25, 6583.25}},
+{'f', {}},
+{'m', {2396.25, 7161.25}},
+{'c', {2397.28, 7161.3, 2474.62, 7158.74, 2474.25, 7157.25}},
+{'c', {2473.94, 7154.09, 2388.91, 7142.55, 2385.25, 7144.25}},
+{'c', {2383.11, 7145.25, 2300.47, 7118.52, 2299.25, 7120.25}},
+{'c', {2301.95, 7121.48, 2392.87, 7161.31, 2396.25, 7161.25}},
+{'f', {}},
+{'g', {0}},
+{'m', {2565.25, 7054.25}},
+{'v', {2402.4, 7034.2, 2354.25, 7021.25}},
+{'c', {2305.6, 7007.8, 2101, 6924.2, 2068.25, 6902.25}},
+{'c', {2068.25, 6902.24, 1920.6, 6842.8, 1733.25, 6623.25}},
+{'v', {1817.2, 6660.2, 1841.25, 6691.25}},
+{'v', {1991, 6829.6, 1989.25, 6801.25}},
+{'v', {2123, 6895.6, 2116.25, 6871.25}},
+{'c', {2116.25, 6871.24, 2384.8, 6994.6, 2363.25, 6959.25}},
+{'c', {2363.25, 6959.24, 2600.4, 7010, 2589.25, 6988.25}},
+{'v', {2796.2, 6939.6, 2765.25, 6937.25}},
+{'c', {2765.25, 6937.24, 2701.6, 6924.2, 2772.25, 6884.25}},
+{'c', {2772.25, 6884.24, 2734.6, 6836.2, 2675.25, 6880.25}},
+{'c', {2615.8, 6924.2, 2648.8, 6900, 2594.25, 6889.25}},
+{'v', {2565.2, 6880.2, 2514.25, 6924.25}},
+{'c', {2514.25, 6924.24, 2453, 6974.8, 2356.25, 6935.25}},
+{'c', {2356.25, 6935.24, 2019.6, 6796.6, 1997.25, 6790.25}},
+{'v', {1958, 6759.2, 1931.25, 6719.25}},
+{'c', {1931.25, 6719.24, 1867.8, 6671.2, 1835.25, 6656.25}},
+{'v', {1694, 6528.2, 1681.25, 6513.25}},
+{'v', {1643.4, 6455.6, 1634.25, 6451.25}},
+{'c', {1634.24, 6451.24, 1705, 6493, 1727.25, 6515.25}},
+{'v', {1881, 6625, 1940.25, 6634.25}},
+{'v', {1988.8, 6666.8, 1997.25, 6682.25}},
+{'c', {1997.25, 6682.24, 2156, 6783.4, 2202.25, 6783.25}},
+{'c', {2202.25, 6783.24, 2303.4, 6726.2, 2330.25, 6803.25}},
+{'c', {2330.25, 6803.24, 2393.6, 6823, 2455.25, 6810.25}},
+{'v', {2490.4, 6838.4, 2481.25, 6862.25}},
+{'c', {2481.25, 6862.24, 2499.2, 6882.4, 2510.25, 6840.25}},
+{'c', {2510.25, 6840.24, 2547.6, 6801, 2600.25, 6823.25}},
+{'v', {2644.4, 6825.2, 2622.25, 6799.25}},
+{'v', {2574, 6757, 2444.25, 6755.25}},
+{'v', {2307.8, 6748.2, 2127.25, 6664.25}},
+{'c', {2127.25, 6664.24, 1799.6, 6550.2, 1698.25, 6436.25}},
+{'v', {1628, 6339, 1568.25, 6326.25}},
+{'c', {1568.24, 6326.25, 1504.8, 6317, 1439.25, 6235.25}},
+{'c', {1439.25, 6235.24, 1546.6, 6299.4, 1645.25, 6299.25}},
+{'c', {1645.24, 6299.24, 1689.6, 6325.8, 1648.25, 6286.25}},
+{'c', {1648.25, 6286.24, 1608.2, 6202.6, 1626.25, 6143.25}},
+{'c', {1626.25, 6143.24, 1619.2, 6086, 1610.25, 6068.25}},
+{'c', {1610.25, 6068.24, 1524.6, 5927.6, 1524.25, 5901.25}},
+{'c', {1524.6, 5874.8, 1537.8, 5767, 1542.25, 5760.25}},
+{'c', {1546.6, 5753.8, 1531.2, 5778, 1573.25, 5751.25}},
+{'c', {1614.8, 5725.2, 1645.6, 5707.6, 1654.25, 5677.25}},
+{'c', {1663.2, 5646, 1632.4, 5736.2, 1630.25, 5756.25}},
+{'c', {1628, 5775.8, 1581.8, 5855, 1590.25, 5881.25}},
+{'c', {1590.24, 5881.24, 1601.6, 5870.4, 1610.25, 5855.25}},
+{'v', {1603.8, 5861.6, 1610.25, 5901.25}},
+{'c', {1610.25, 5901.24, 1619.2, 5958.4, 1634.25, 5993.25}},
+{'c', {1650, 6028.8, 1672, 6070.6, 1676.25, 6079.25}},
+{'c', {1680.8, 6088.2, 1680.8, 6152, 1696.25, 6123.25}},
+{'l', {1733.25, 6095.25}},
+{'v', {1702.8, 6123.4, 1727.25, 6147.25}},
+{'c', {1727.25, 6147.24, 1716, 6209.2, 1736.25, 6238.25}},
+{'v', {1812.8, 6330.2, 1830.25, 6341.25}},
+{'y', {1848, 6352.2, 1832.25, 6348.25}},
+{'v', {1898.6, 6394, 1835.25, 6376.25}},
+{'c', {1835.25, 6376.24, 1790.8, 6358.8, 1758.25, 6359.25}},
+{'v', {1674.2, 6336.8, 1718.25, 6383.25}},
+{'c', {1762.2, 6429.2, 1872.2, 6488.6, 1914.25, 6486.25}},
+{'l', {1923.25, 6469.25}},
+{'l', {2046.25, 6495.25}},
+{'l', {2033.25, 6486.25}},
+{'c', {2033.25, 6486.24, 2030.6, 6488.6, 2077.25, 6493.25}},
+{'c', {2123, 6497.4, 2186.8, 6482, 2202.25, 6502.25}},
+{'c', {2217.6, 6521.6, 2255, 6532.6, 2250.25, 6517.25}},
+{'y', {2246.2, 6501.8, 2244.25, 6480.25}},
+{'v', {2299, 6543.6, 2292.25, 6519.25}},
+{'c', {2285.8, 6495.2, 2195.6, 6438, 2180.25, 6370.25}},
+{'l', {2294.25, 6460.25}},
+{'l', {2334.25, 6493.25}},
+{'v', {2373.8, 6468.8, 2376.25, 6486.25}},
+{'c', {2378.2, 6504, 2428.8, 6567.8, 2442.25, 6565.25}},
+{'c', {2455.2, 6563.4, 2477.2, 6594.2, 2475.25, 6565.25}},
+{'y', {2472.8, 6537, 2556.25, 6477.25}},
+{'c', {2556.25, 6477.24, 2591.6, 6497.4, 2607.25, 6482.25}},
+{'y', {2622.4, 6466.6, 2668.25, 6700.25}},
+{'l', {2943.25, 6816.25}},
+{'l', {3423.25, 6854.25}},
+{'l', {3236.25, 6928.25}},
+{'l', {2565.25, 7054.25}},
+{'f', {}},
+{'w', {22}},
+{'J', {1}},
+{'R', {0.298, 0, 0}},
+{'m', {2765.25, 4865.25}},
+{'v', {2600.4, 5049.8, 2508.25, 5078.25}},
+{'c', {2508.25, 5078.24, 2360.6, 5153.2, 2090.25, 5067.25}},
+{'S', {}},
+{'m', {2473.25, 5094.25}},
+{'v', {2195.6, 5181.8, 2026.25, 5135.25}},
+{'c', {2026.25, 5135.24, 1823.8, 5113.6, 1731.25, 4962.25}},
+{'S', {}},
+{'m', {2433.25, 5107.25}},
+{'v', {2246.2, 5186.2, 2083.25, 5210.25}},
+{'c', {2083.25, 5210.24, 1900.8, 5239, 1718.25, 5160.25}},
+{'v', {1584, 5093.8, 1524.25, 4981.25}},
+{'S', {}},
+{'m', {2444.25, 5102.25}},
+{'c', {2444.25, 5102.24, 2274.8, 5223.6, 2264.25, 5239.25}},
+{'v', {2186.8, 5360, 2044.25, 5364.25}},
+{'c', {2044.25, 5364.24, 1808.4, 5355.6, 1619.25, 5267.25}},
+{'S', {}},
+{'m', {2430.25, 5201.25}},
+{'y', {2446.8, 5185.43, 2743.25, 4867.25}},
+{'y', {3130.6, 4464.59, 2822.25, 4841.25}},
+{'y', {2739, 4893.6, 2638.25, 5100.25}},
+{'y', {2624.6, 5131.2, 2792.25, 5021.25}},
+{'y', {2835.8, 5012.4, 2985.25, 4801.25}},
+{'y', {2910.6, 4827.6, 2963.25, 4748.25}},
+{'y', {2994.2, 4726.4, 3218.25, 4555.25}},
+{'y', {3258.21, 4510.79, 3302.25, 4493.25}},
+{'y', {3456.21, 4550.39, 3386.25, 4405.25}},
+{'y', {3412.21, 4330.39, 3474.25, 4458.25}},
+{'y', {3597.01, 4642.8, 3416.25, 4616.25}},
+{'y', {3086.6, 4585.59, 3012.25, 4761.25}},
+{'y', {2985.4, 4788, 3082.25, 4761.25}},
+{'y', {3174.61, 4739.6, 3003.25, 4898.25}},
+{'y', {3029.4, 4898, 3130.25, 4823.25}},
+{'y', {3245.01, 4722, 3267.25, 4744.25}},
+{'y', {3465.01, 4840.8, 3579.25, 4757.25}},
+{'c', {3601.41, 4739.6, 3539.81, 4664.8, 3557.25, 4607.25}},
+{'y', {3575.01, 4550.39, 3628.25, 4414.25}},
+{'y', {3601.41, 4396.39, 3606.25, 4277.25}},
+{'y', {3790.61, 4022.39, 3685.25, 4044.25}},
+{'y', {3513.41, 4048.79, 3676.25, 3965.25}},
+{'y', {3711.41, 3943.19, 3808.25, 3864.25}},
+{'y', {3777.41, 3877.19, 3760.25, 3820.25}},
+{'y', {3812.61, 3775.99, 3782.25, 3723.25}},
+{'y', {3715.81, 3709.99, 3702.25, 3666.25}},
+{'y', {3777.41, 3577.99, 3667.25, 3573.25}},
+{'y', {3707.01, 3525.19, 3654.25, 3393.25}},
+{'y', {3601.41, 3393.19, 3531.25, 3331.25}},
+{'y', {3557.41, 3278.79, 3443.25, 3217.25}},
+{'y', {3350.61, 3199.59, 3381.25, 3125.25}},
+{'y', {3293.41, 3058.79, 3267.25, 2883.25}},
+{'c', {3258.21, 2768.39, 3231.81, 2733.19, 3289.25, 2755.25}},
+{'y', {3346.21, 2777.19, 3337.25, 2913.25}},
+{'y', {3284.61, 3085.19, 3755.25, 3261.25}},
+{'y', {3799.41, 3278.79, 3808.25, 3336.25}},
+{'y', {3830.21, 3331.59, 3927.25, 3248.25}},
+{'y', {4010.61, 3124.79, 4015.25, 3226.25}},
+{'y', {4028.21, 3265.59, 4010.25, 3331.25}},
+{'y', {4076.61, 3569.19, 3922.25, 3639.25}},
+{'y', {3812.61, 4009.19, 3966.25, 3917.25}},
+{'y', {3997.41, 3855.19, 4116.25, 3798.25}},
+{'l', {4156.25, 3824.25}},
+{'y', {4138.21, 3877.19, 4230.25, 3943.25}},
+{'y', {4261.41, 3872.79, 4327.25, 3961.25}},
+{'y', {4367.01, 4229.19, 4503.25, 4071.25}},
+{'y', {4547.41, 4057.59, 4560.25, 4132.25}},
+{'y', {4600.21, 4246.79, 4560.25, 4396.25}},
+{'y', {4600.21, 4400.79, 4706.25, 4335.25}},
+{'c', {4736.61, 4374.39, 4635.41, 4559.19, 4679.25, 4533.25}},
+{'y', {4723.41, 4506.39, 4772.25, 4489.25}},
+{'y', {4780.61, 4510.79, 4670.25, 4647.25}},
+{'y', {4622.21, 4678, 4565.25, 4902.25}},
+{'y', {4644.21, 4862.8, 4534.25, 5030.25}},
+{'y', {4534.21, 5065.2, 4600.25, 5188.25}},
+{'y', {4591.41, 5263.2, 4600.25, 5259.25}},
+{'c', {4631.01, 5245.6, 4719.01, 5228, 4644.25, 5298.25}},
+{'y', {4569.41, 5368.8, 4653.25, 5421.25}},
+{'y', {4701.41, 5452.4, 4552.25, 5448.25}},
+{'y', {4494.61, 5496.4, 4499.25, 5540.25}},
+{'c', {4587.01, 5518.4, 4428.61, 5676.8, 4402.25, 5716.25}},
+{'y', {4375.81, 5756, 4481.25, 5813.25}},
+{'y', {4626.61, 5852.8, 4499.25, 5888.25}},
+{'y', {4283.41, 5883.6, 4402.25, 6002.25}},
+{'y', {4468.21, 5998, 4450.25, 6024.25}},
+{'y', {4393.41, 6037.6, 4288.25, 6108.25}},
+{'y', {4243.81, 6147.6, 4283.25, 6139.25}},
+{'y', {4468.21, 6125.6, 4151.25, 6249.25}},
+{'y', {4239.41, 6248.8, 4041.25, 6363.25}},
+{'y', {4019.41, 6380.8, 3984.25, 6464.25}},
+{'y', {3918.21, 6521.6, 3865.25, 6596.25}},
+{'y', {3861.01, 6644.8, 3808.25, 6697.25}},
+{'y', {3680.61, 6847.2, 3619.25, 6843.25}},
+{'y', {3456.21, 6882.4, 3399.25, 6873.25}},
+{'l', {2818.25, 6825.25}},
+{'y', {2527.8, 6684.4, 2613.25, 6453.25}},
+{'y', {2684, 6361, 2785.25, 6403.25}},
+{'y', {2835.8, 6471, 2963.25, 6447.25}},
+{'y', {3187.81, 6411.6, 3159.25, 6451.25}},
+{'c', {3132.81, 6501.8, 2954.6, 6570, 2952.25, 6576.25}},
+{'y', {2950.2, 6583.2, 2853.25, 6620.25}},
+{'y', {2820.4, 6633.8, 2772.25, 6735.25}},
+{'y', {2736.8, 6772.4, 2910.25, 6708.25}},
+{'y', {2897.4, 6697.6, 2979.25, 6653.25}},
+{'y', {3170.21, 6664.6, 3287.25, 6546.25}},
+{'y', {3405.61, 6363.2, 3408.25, 6453.25}},
+{'y', {3438.61, 6556.8, 3309.25, 6790.25}},
+{'y', {3313.21, 6812, 3403.25, 6739.25}},
+{'y', {3418.81, 6761.4, 3427.25, 6697.25}},
+{'y', {3429.81, 6671.2, 3471.25, 6583.25}},
+{'y', {3502.41, 6440.2, 3542.25, 6521.25}},
+{'l', {3592.25, 6418.25}},
+{'y', {3608.01, 6389.6, 3542.25, 6306.25}},
+{'c', {3539.81, 6275.2, 3548.61, 6277.4, 3487.25, 6194.25}},
+{'y', {3425.41, 6110.2, 3463.25, 6062.25}},
+{'y', {3447.41, 5989.2, 3544.25, 5993.25}},
+{'y', {3572.81, 5969.4, 3610.25, 5969.25}},
+{'y', {3630.01, 5947.4, 3656.25, 5954.25}},
+{'y', {3674.01, 5995.8, 3742.25, 5974.25}},
+{'y', {3757.61, 6000.2, 3848.25, 6004.25}},
+{'c', {3858.81, 6033.2, 3863.21, 6050.8, 3900.25, 6057.25}},
+{'y', {3938.01, 6064, 3667.25, 6537.25}},
+{'y', {3737.81, 6545.8, 3647.25, 6682.25}},
+{'c', {3623.41, 6754.8, 3748.81, 6594.2, 3773.25, 6579.25}},
+{'c', {3797.21, 6563.4, 3808.21, 6539.2, 3790.25, 6541.25}},
+{'c', {3773.01, 6543.6, 3753.21, 6519.4, 3768.25, 6517.25}},
+{'c', {3784.01, 6515, 3927.01, 6350, 3964.25, 6238.25}},
+{'c', {4001.81, 6125.6, 4067.81, 6081.6, 4136.25, 6015.25}},
+{'y', {4204.21, 5949.6, 4195.25, 5683.25}},
+{'y', {4191.01, 5586.6, 4257.25, 5470.25}},
+{'y', {4279.01, 5428.2, 4233.25, 5228.25}},
+{'y', {4210.81, 5203.8, 4226.25, 5195.25}},
+{'y', {4237.21, 5181.8, 4312.25, 5036.25}},
+{'y', {4292.21, 5038.8, 4332.25, 4999.25}},
+{'y', {4389.01, 4933.2, 4318.25, 4966.25}},
+{'y', {4252.61, 4983.8, 4329.25, 4876.25}},
+{'y', {4342.81, 4856.2, 4244.25, 4907.25}},
+{'y', {4142.61, 4913.4, 4270.25, 4834.25}},
+{'y', {4364.81, 4755, 4239.25, 4803.25}},
+{'y', {4188.81, 4823.2, 4224.25, 4748.25}},
+{'y', {4259.21, 4730.8, 4448.25, 4654.25}},
+{'y', {4452.81, 4611.99, 4420.25, 4557.25}},
+{'y', {4424.21, 4512.99, 4400.25, 4475.25}},
+{'y', {4386.81, 4385.39, 4380.25, 4376.25}},
+{'y', {4334.01, 4374.39, 4252.25, 4222.25}},
+{'y', {4232.81, 4193.99, 4120.25, 4062.25}},
+{'y', {4098.61, 3984.99, 3900.25, 4064.25}},
+{'y', {3828.01, 4026.79, 3850.25, 4064.25}},
+{'y', {3845.61, 4088.39, 3898.25, 4154.25}},
+{'y', {3975.41, 4182.99, 3947.25, 4302.25}},
+{'c', {3990.81, 4317.19, 3867.61, 4347.99, 3870.25, 4361.25}},
+{'y', {3872.01, 4374.39, 3936.25, 4390.25}},
+{'y', {4023.81, 4411.79, 3975.25, 4438.25}},
+{'y', {3968.81, 4482.19, 4002.25, 4544.25}},
+{'y', {4129.41, 4552.59, 4002.25, 4731.25}},
+{'y', {3883.01, 4814.4, 3872.25, 4878.25}},
+{'c', {4010.61, 4968.4, 3920.41, 5104.8, 3922.25, 5144.25}},
+{'y', {3924.81, 5184, 3938.25, 5421.25}},
+{'y', {3916.01, 5489.8, 3883.25, 5639.25}},
+{'y', {3907.21, 5696.6, 3988.25, 5835.25}},
+{'c', {4019.41, 5881.4, 4116.21, 5934.2, 4092.25, 5967.25}},
+{'y', {4067.81, 6000.2, 3982.25, 5980.25}},
+{'y', {3896.21, 5995.8, 3903.25, 5938.25}},
+{'y', {3885.21, 5927.6, 3876.25, 5872.25}},
+{'y', {3868.48, 5785.34, 3771.25, 5716.25}},
+{'y', {3647.61, 5648.2, 3749.25, 5604.25}},
+{'y', {3814.81, 5531.6, 3707.25, 5529.25}},
+{'y', {3586.01, 5549.2, 3676.25, 5437.25}},
+{'y', {3795.01, 5296.2, 3762.25, 5265.25}},
+{'y', {3649.81, 5254.4, 3788.25, 5153.25}},
+{'c', {3788.25, 5153.24, 3779.61, 5175.2, 3782.25, 5155.25}},
+{'c', {3784.01, 5135.6, 3817.01, 5089.4, 3826.25, 5067.25}},
+{'y', {3834.61, 5045.4, 3790.25, 5043.25}},
+{'y', {3797.21, 4937.6, 3628.25, 4984.25}},
+{'v', {3627.81, 4983.8, 3610.25, 4981.25}},
+{'c', {3592.61, 4979.4, 3469.41, 4988.2, 3405.25, 5012.25}},
+{'y', {3341.81, 5036.6, 3267.25, 5036.25}},
+{'c', {3267.25, 5036.24, 3223.01, 5016.8, 3139.25, 5019.25}},
+{'y', {3055.8, 5021.2, 2968.25, 4990.25}},
+{'c', {2919.4, 4994.8, 3014, 5043.2, 3016.25, 5041.25}},
+{'c', {3018.4, 5038.8, 3080, 5100.4, 2992.25, 5094.25}},
+{'y', {2752.23, 5075.81, 2633.25, 5188.25}},
+{'y', {2611.4, 5203.8, 2583.25, 5234.25}},
+{'y', {2472.8, 5256.6, 2598.25, 5098.25}},
+{'y', {2611.4, 5082.8, 2596.25, 5072.25}},
+{'y', {2587.2, 5089.4, 2501.25, 5149.25}},
+{'c', {2470.48, 5159.5, 2455.48, 5174.18, 2430.25, 5201.25}},
+{'f', {}},
+{'r', {0.298, 0, 0}},
+{'m', {2167.25, 5329.25}},
+{'c', {2167.25, 5329.24, 2294.6, 5267.6, 2323.25, 5237.25}},
+{'y', {2351.8, 5206, 2506.25, 5080.25}},
+{'c', {2506.25, 5080.24, 2446.4, 5102.6, 2418.25, 5122.25}},
+{'y', {2389.2, 5142.2, 2270.25, 5232.25}},
+{'c', {2270.25, 5232.24, 2228.6, 5298.4, 2167.25, 5329.25}},
+{'f', {}},
+{'r', {0.6, 0.8, 0.2}},
+{'m', {1529.25, 5672.25}},
+{'c', {1532.61, 5673.99, 1526.84, 5703.82, 1524.25, 5710.25}},
+{'y', {1513.36, 5739.79, 1414.25, 5756.25}},
+{'c', {1412.1, 5740.98, 1411.5, 5723.43, 1412.25, 5705.25}},
+{'c', {1412.25, 5705.24, 1466.21, 5644.7, 1529.25, 5672.25}},
+{'f', {}},
+{'r', {0.4, 0.6, 0}},
+{'m', {1529.25, 5674.25}},
+{'c', {1523.96, 5672.83, 1528.73, 5704.64, 1527.25, 5710.25}},
+{'y', {1515.55, 5739.79, 1414.25, 5757.25}},
+{'c', {1412.1, 5742.08, 1411.5, 5724.53, 1412.25, 5706.25}},
+{'v', {1459.61, 5650.2, 1529.25, 5674.25}},
+{'f', {}},
+{'g', {0}},
+{'m', {1480.25, 5673.25}},
+{'c', {1473.72, 5672.99, 1468.16, 5685.53, 1468.25, 5701.25}},
+{'c', {1468.16, 5716.45, 1473.72, 5728.99, 1480.25, 5729.25}},
+{'c', {1487.47, 5728.99, 1493.05, 5716.45, 1493.25, 5701.25}},
+{'c', {1493.05, 5685.53, 1487.47, 5672.99, 1480.25, 5673.25}},
+{'f', {}},
+{'m', {979.246, 4594.25}},
+{'c', {979.242, 4594.24, 917.398, 4484.39, 1190.25, 4550.25}},
+{'c', {1190.25, 4550.24, 1344.2, 4563.59, 1370.25, 4590.25}},
+{'c', {1383.8, 4581.19, 1475.82, 4548.93, 1507.25, 4541.25}},
+{'y', {1581.8, 4523.99, 1674.25, 4634.25}},
+{'v', {1724.8, 4749.5, 1755.25, 4749.25}},
+{'y', {1786.4, 4749.5, 1751.25, 4732.25}},
+{'v', {1678.6, 4620.79, 1683.25, 4603.25}},
+{'c', {1683.24, 4603.24, 1625.8, 4383.19, 1450.25, 4374.25}},
+{'c', {1450.25, 4374.24, 1272.15, 4363.94, 1287.25, 4299.25}},
+{'c', {1287.24, 4299.24, 1383.8, 4325.99, 1410.25, 4299.25}},
+{'c', {1410.25, 4299.24, 1529, 4303.99, 1441.25, 4233.25}},
+{'l', {1366.25, 4106.25}},
+{'v', {1367.74, 4062.89, 1256.25, 4101.25}},
+{'y', {1148.4, 4138.99, 1035.25, 4281.25}},
+{'v', {859.648, 4440.94, 979.246, 4594.25}},
+{'f', {}},
+{'r', {0.902, 0.6, 0.6}},
+{'m', {966.25, 4550.25}},
+{'c', {966.25, 4550.24, 943.797, 4444.79, 1348.25, 4559.25}},
+{'c', {1348.24, 4559.24, 1397, 4559.19, 1423.25, 4550.25}},
+{'c', {1449.8, 4541.59, 1581.8, 4510.79, 1604.25, 4524.25}},
+{'v', {1524.6, 4374.39, 1397.25, 4392.25}},
+{'c', {1397.24, 4392.25, 1251.8, 4374.39, 1256.25, 4321.25}},
+{'c', {1256.25, 4321.24, 1300.2, 4242.39, 1353.25, 4216.25}},
+{'c', {1353.24, 4216.25, 1383.8, 4189.59, 1379.25, 4154.25}},
+{'c', {1375, 4119.19, 1344.2, 4101.59, 1322.25, 4093.25}},
+{'c', {1300.2, 4083.99, 1265, 4119.19, 1247.25, 4119.25}},
+{'c', {1229.8, 4119.19, 1137.4, 4189.59, 1089.25, 4242.25}},
+{'c', {1040.6, 4295.19, 948.199, 4427.19, 952.246, 4458.25}},
+{'y', {957, 4488.79, 966.25, 4550.25}},
+{'f', {}},
+{'r', {0.702, 0.4, 0.4}},
+{'m', {1001.25, 4358.25}},
+{'c', {1029.6, 4314.99, 1064.8, 4268.79, 1089.25, 4242.25}},
+{'c', {1137.4, 4189.59, 1229.8, 4119.19, 1247.25, 4119.25}},
+{'c', {1265, 4119.19, 1300.2, 4083.99, 1322.25, 4093.25}},
+{'c', {1344.2, 4101.59, 1375, 4119.19, 1379.25, 4154.25}},
+{'y', {1383.8, 4189.59, 1353.25, 4216.25}},
+{'c', {1319.27, 4232.85, 1289.13, 4271.26, 1272.25, 4297.25}},
+{'c', {1272.25, 4297.24, 1273.8, 4268.79, 1216.25, 4277.25}},
+{'c', {1159.4, 4286.39, 1102.2, 4317.19, 1084.25, 4352.25}},
+{'c', {1067, 4387.59, 1040.6, 4413.99, 1058.25, 4374.25}},
+{'c', {1075.8, 4334.79, 1102.2, 4295.19, 1120.25, 4291.25}},
+{'c', {1137.4, 4286.39, 1133, 4273.19, 1106.25, 4277.25}},
+{'c', {1080.2, 4281.99, 1049.4, 4286.39, 1001.25, 4343.25}},
+{'f', {}},
+{'r', {0.6, 0.149, 0}},
+{'m', {972.25, 4590.25}},
+{'v', {992.199, 4739.6, 1005.25, 4783.25}},
+{'c', {1005.25, 4783.24, 996.598, 4858.4, 1023.25, 4904.25}},
+{'c', {1049.4, 4950.8, 1071.4, 5019, 1104.25, 5078.25}},
+{'c', {1137.4, 5137.8, 1139.6, 5181.8, 1183.25, 5199.25}},
+{'c', {1227.6, 5217, 1293.6, 5311.6, 1324.25, 5322.25}},
+{'y', {1355.2, 5333.6, 1353.25, 5325.25}},
+{'c', {1353.24, 5325.25, 1427.8, 5487.6, 1577.25, 5443.25}},
+{'c', {1577.25, 5443.24, 1399.2, 5474.4, 1573.25, 5578.25}},
+{'c', {1573.24, 5578.25, 1520.2, 5565.7, 1556.25, 5642.25}},
+{'c', {1580.71, 5694.05, 1575.2, 5619.6, 1454.25, 5490.25}},
+{'v', {1399.2, 5395.2, 1342.25, 5362.25}},
+{'c', {1284.8, 5329.2, 1152.8, 5252.2, 1139.25, 5210.25}},
+{'c', {1126.4, 5168.6, 1091.2, 5104.8, 1069.25, 5087.25}},
+{'c', {1047.2, 5069.6, 1016.4, 5023.4, 1012.25, 4986.25}},
+{'c', {1012.24, 4986.25, 998.797, 4942, 983.25, 4929.25}},
+{'c', {968, 4915.6, 965.797, 4880.4, 966.25, 4858.25}},
+{'c', {965.797, 4836.4, 943.797, 4805.6, 946.246, 4779.25}},
+{'c', {946.242, 4779.24, 954.797, 4570.19, 950.25, 4548.25}},
+{'l', {972.25, 4590.25}},
+{'f', {}},
+{'g', {1}},
+{'m', {878.25, 4539.25}},
+{'c', {878.25, 4539.24, 855.797, 4554.79, 807.25, 4489.25}},
+{'v', {887.699, 4125.79, 887.246, 4110.25}},
+{'c', {887.246, 4110.24, 899.797, 4133.49, 885.25, 4212.25}},
+{'y', {871.199, 4291.89, 861.25, 4431.25}},
+{'l', {878.25, 4539.25}},
+{'f', {}},
+{'r', {0.6, 0.149, 0}},
+{'m', {1115.25, 5206.25}},
+{'v', {921.797, 5170.8, 926.246, 4849.25}},
+{'l', {917.25, 4577.25}},
+{'v', {904.199, 4858.4, 891.246, 4876.25}},
+{'c', {877.797, 4893.6, 921.797, 5016.8, 886.246, 4951.25}},
+{'c', {886.242, 4951.25, 732.598, 4796.8, 820.246, 4563.25}},
+{'c', {820.242, 4563.24, 837.098, 4527.29, 804.246, 4578.25}},
+{'v', {753.5, 4716.5, 765.246, 4787.25}},
+{'c', {765.242, 4787.25, 767.797, 4811.1, 788.246, 4842.25}},
+{'v', {883.297, 4970.6, 913.246, 4996.25}},
+{'c', {913.242, 4996.25, 932.797, 5154.3, 1102.25, 5211.25}},
+{'v', {1164.9, 5236.8, 1115.25, 5206.25}},
+{'f', {}},
+{'g', {0}},
+{'m', {2649.25, 5934.25}},
+{'c', {2656.06, 5938.09, 2655.96, 5948.76, 2662.25, 5950.25}},
+{'c', {2674.98, 5954.45, 2676.78, 5966.19, 2682.25, 5975.25}},
+{'c', {2692.08, 5989.32, 2694.21, 6006.18, 2700.25, 6022.25}},
+{'c', {2703.6, 6030.28, 2703.9, 6040.79, 2700.25, 6048.25}},
+{'c', {2687.54, 6075.34, 2679.82, 6102.28, 2664.25, 6129.25}},
+{'c', {2661.01, 6133.91, 2658.12, 6142.65, 2656.25, 6149.25}},
+{'c', {2650.82, 6164.45, 2637, 6175.35, 2627.25, 6190.25}},
+{'c', {2624.37, 6194.72, 2630.14, 6204.9, 2622.25, 6206.25}},
+{'c', {2612.4, 6206.98, 2596.46, 6213.36, 2594.25, 6202.25}},
+{'c', {2587.43, 6173.61, 2598.55, 6145.86, 2609.25, 6119.25}},
+{'c', {2600.6, 6111.39, 2604.27, 6101.25, 2606.25, 6093.25}},
+{'c', {2613.75, 6053.15, 2600.57, 6016.28, 2592.25, 5978.25}},
+{'c', {2591.95, 5976.92, 2595.71, 5974.74, 2595.25, 5974.25}},
+{'c', {2581.96, 5944.52, 2565.99, 5917.62, 2546.25, 5891.25}},
+{'c', {2537.64, 5880.52, 2528.05, 5870.44, 2522.25, 5859.25}},
+{'c', {2518.54, 5850.09, 2514.05, 5839.46, 2517.25, 5828.25}},
+{'c', {2479.01, 5798.02, 2454.26, 5755.72, 2425.25, 5713.25}},
+{'c', {2420.46, 5705.94, 2423.66, 5692.64, 2429.25, 5689.25}},
+{'c', {2438.66, 5685.3, 2449.18, 5696.57, 2454.25, 5706.25}},
+{'c', {2458.63, 5713.55, 2462.51, 5720.7, 2468.25, 5728.25}},
+{'c', {2469.38, 5729.7, 2467.41, 5734.3, 2469.25, 5735.25}},
+{'c', {2497.86, 5761.45, 2516.21, 5793.75, 2543.25, 5820.25}},
+{'c', {2564.65, 5823.42, 2581.55, 5834.39, 2601.25, 5845.25}},
+{'c', {2604.17, 5847.51, 2609.93, 5844.77, 2613.25, 5847.25}},
+{'c', {2632.41, 5859.86, 2632.39, 5882.26, 2633.25, 5903.25}},
+{'c', {2634.1, 5912.99, 2636.26, 5927.48, 2649.25, 5934.25}},
+{'f', {}},
+{'m', {2551.25, 5983.25}},
+{'c', {2552.91, 5984.17, 2551.21, 5988.82, 2552.25, 5991.25}},
+{'c', {2554.19, 5994.77, 2558.62, 5996.83, 2560.25, 6000.25}},
+{'c', {2561.6, 6002.77, 2559.81, 6006.95, 2561.25, 6008.25}},
+{'c', {2586.97, 6035.43, 2589.79, 6068.7, 2578.25, 6101.25}},
+{'c', {2589.66, 6108.22, 2590.33, 6122.1, 2585.25, 6131.25}},
+{'c', {2576.27, 6150.12, 2574.15, 6171.3, 2563.25, 6188.25}},
+{'c', {2554.61, 6202.23, 2537.25, 6215.92, 2522.25, 6201.25}},
+{'c', {2517.54, 6197.19, 2514, 6188.53, 2517.25, 6180.25}},
+{'c', {2517.77, 6178.9, 2520.9, 6177.3, 2520.25, 6176.25}},
+{'c', {2519.43, 6172.83, 2513.42, 6170.37, 2513.25, 6167.25}},
+{'c', {2513.02, 6150.92, 2502.53, 6134.3, 2509.25, 6119.25}},
+{'c', {2517.95, 6101.76, 2526.96, 6081.33, 2534.25, 6062.25}},
+{'c', {2520.82, 6038.52, 2532.19, 6011.58, 2513.25, 5991.25}},
+{'c', {2511.41, 5989.33, 2511.51, 5985.08, 2513.25, 5983.25}},
+{'c', {2516.06, 5977.52, 2520.53, 5973.05, 2526.25, 5970.25}},
+{'c', {2528.09, 5968.51, 2531.92, 5968.48, 2534.25, 5970.25}},
+{'c', {2540.48, 5973.83, 2544.86, 5979.19, 2551.25, 5983.25}},
+{'f', {}},
+{'m', {3242.25, 6096.25}},
+{'c', {3257.76, 6077.03, 3260.95, 6046.29, 3238.25, 6031.25}},
+{'c', {3244.46, 5994.6, 3281.32, 6016.37, 3304.25, 6022.25}},
+{'c', {3303.21, 6026.46, 3305.71, 6030.25, 3309.25, 6030.25}},
+{'c', {3320.45, 6030.47, 3327.93, 6041.82, 3339.25, 6040.25}},
+{'c', {3344.38, 6056.93, 3362.4, 6064.65, 3369.25, 6080.25}},
+{'c', {3388.47, 6120.47, 3381.97, 6167.48, 3353.25, 6203.25}},
+{'c', {3351.32, 6205.98, 3353.71, 6211.66, 3352.25, 6215.25}},
+{'c', {3344.03, 6239.96, 3321.28, 6243.43, 3300.25, 6251.25}},
+{'c', {3287.06, 6293.62, 3279.79, 6337.96, 3260.25, 6378.25}},
+{'c', {3242.66, 6381.35, 3234.98, 6400.48, 3220.25, 6408.25}},
+{'c', {3205.61, 6416.3, 3199.36, 6398.94, 3200.25, 6387.25}},
+{'c', {3199.89, 6385.18, 3204.86, 6382.71, 3203.25, 6378.25}},
+{'c', {3202.15, 6376.89, 3199.29, 6375.67, 3199.25, 6374.25}},
+{'c', {3199.3, 6372.71, 3201.73, 6371.27, 3203.25, 6370.25}},
+{'c', {3193.11, 6360.78, 3177.46, 6355.53, 3174.25, 6343.25}},
+{'c', {3163.72, 6302.12, 3192.02, 6267.75, 3210.25, 6232.25}},
+{'c', {3216.87, 6220.05, 3208.77, 6206.07, 3200.25, 6193.25}},
+{'c', {3195.6, 6185.14, 3196.79, 6172.74, 3200.25, 6163.25}},
+{'c', {3208.09, 6137.69, 3225.59, 6118.56, 3242.25, 6096.25}},
+{'f', {}},
+{'m', {2833.25, 5894.25}},
+{'c', {2817.36, 5874.14, 2779.83, 5845.88, 2812.25, 5824.25}},
+{'c', {2814.05, 5823.25, 2818.12, 5823.21, 2820.25, 5824.25}},
+{'c', {2842.25, 5841.9, 2864.29, 5851.83, 2891.25, 5858.25}},
+{'c', {2892.45, 5858.55, 2895.46, 5853.41, 2899.25, 5855.25}},
+{'c', {2917.27, 5862.92, 2939.38, 5862.28, 2952.25, 5877.25}},
+{'c', {2993.84, 5874.54, 3033.05, 5886.81, 3070.25, 5901.25}},
+{'c', {3083.12, 5906.12, 3097.09, 5912.27, 3110.25, 5918.25}},
+{'c', {3125.44, 5924.13, 3138.68, 5934.34, 3151.25, 5947.25}},
+{'c', {3152.38, 5948.39, 3156.27, 5947.4, 3159.25, 5947.25}},
+{'c', {3158.77, 5956.89, 3169.35, 5958.29, 3172.25, 5965.25}},
+{'c', {3173.08, 5967.68, 3171.43, 5972.04, 3173.25, 5973.25}},
+{'c', {3196.41, 5991.09, 3206.53, 6013.29, 3193.25, 6039.25}},
+{'c', {3190.54, 6045.79, 3187.8, 6052.59, 3181.25, 6058.25}},
+{'c', {3169.64, 6067.85, 3157.52, 6058.45, 3146.25, 6062.25}},
+{'c', {3144.25, 6054.89, 3136.23, 6056.78, 3132.25, 6054.25}},
+{'c', {3122.54, 6049.77, 3107.87, 6056.21, 3098.25, 6051.25}},
+{'c', {3083.44, 6043.75, 3069.79, 6041.89, 3054.25, 6037.25}},
+{'c', {3050.71, 6036.84, 3042.16, 6037.89, 3040.25, 6031.25}},
+{'c', {3038.93, 6032.47, 3037.3, 6035.05, 3036.25, 6035.25}},
+{'c', {3015.14, 6030.45, 3001.28, 6028.09, 2987.25, 6009.25}},
+{'c', {2985.84, 6008.02, 2980.98, 6009.99, 2979.25, 6008.25}},
+{'c', {2968.7, 5999.61, 2964.25, 5986.04, 2952.25, 5978.25}},
+{'c', {2949.92, 5977.3, 2946.05, 5979.14, 2944.25, 5977.25}},
+{'c', {2936.81, 5972.98, 2932.77, 5965.93, 2925.25, 5961.25}},
+{'c', {2922.09, 5958.5, 2917.69, 5962.07, 2918.25, 5965.25}},
+{'c', {2920.74, 5986.3, 2927.92, 6005.96, 2921.25, 6026.25}},
+{'c', {2944.45, 6054.32, 2972.13, 6075.76, 2992.25, 6106.25}},
+{'c', {2992.16, 6129.62, 2999.77, 6153.23, 2998.25, 6176.25}},
+{'c', {2998.42, 6178.2, 2995.25, 6186.26, 2994.25, 6190.25}},
+{'c', {2990.44, 6199.47, 3000.4, 6211.2, 2991.25, 6219.25}},
+{'c', {2975.68, 6232.4, 2961.46, 6222.76, 2952.25, 6207.25}},
+{'c', {2932.18, 6202.71, 2909.45, 6195.09, 2892.25, 6208.25}},
+{'c', {2881.02, 6217.32, 2874.66, 6227.28, 2866.25, 6239.25}},
+{'c', {2855.04, 6253.22, 2858.72, 6269.02, 2859.25, 6286.25}},
+{'c', {2859.56, 6287.63, 2856.09, 6289.12, 2856.25, 6290.25}},
+{'c', {2856.11, 6292.08, 2858.52, 6293.52, 2860.25, 6295.25}},
+{'c', {2852.23, 6301.88, 2849.21, 6313.52, 2838.25, 6317.25}},
+{'c', {2841.35, 6329.01, 2833.92, 6338.73, 2824.25, 6342.25}},
+{'c', {2802.39, 6349.66, 2783.95, 6328.62, 2763.25, 6328.25}},
+{'c', {2757.48, 6327.61, 2752.34, 6339.03, 2745.25, 6342.25}},
+{'c', {2740.45, 6344.98, 2732.71, 6345.34, 2728.25, 6342.25}},
+{'c', {2721.16, 6336.88, 2714.68, 6335.62, 2706.25, 6333.25}},
+{'c', {2688.55, 6328.57, 2674.49, 6316.54, 2658.25, 6307.25}},
+{'c', {2641.88, 6297.7, 2631.38, 6281.9, 2619.25, 6267.25}},
+{'c', {2609.14, 6254.7, 2607.65, 6228.15, 2623.25, 6222.25}},
+{'c', {2643.54, 6215.5, 2657.88, 6245.23, 2679.25, 6241.25}},
+{'c', {2682.94, 6241.09, 2685.21, 6237.67, 2684.25, 6233.25}},
+{'c', {2688.28, 6232.2, 2690.68, 6234.72, 2693.25, 6238.25}},
+{'c', {2702.23, 6226.59, 2715.04, 6222.88, 2725.25, 6213.25}},
+{'c', {2736.03, 6203.34, 2755.23, 6207.93, 2766.25, 6197.25}},
+{'c', {2783.22, 6180.45, 2777.07, 6150.95, 2798.25, 6136.25}},
+{'c', {2791.95, 6122.16, 2785.27, 6108.11, 2781.25, 6092.25}},
+{'c', {2778.49, 6079.14, 2789.42, 6065.98, 2802.25, 6067.25}},
+{'c', {2816.58, 6068.01, 2819.46, 6076.36, 2825.25, 6088.25}},
+{'c', {2827.73, 6085.26, 2832.89, 6081.99, 2832.25, 6079.25}},
+{'c', {2827.41, 6053.36, 2816.12, 6031.44, 2811.25, 6004.25}},
+{'c', {2810.43, 6001.21, 2807.08, 5998.99, 2803.25, 6000.25}},
+{'c', {2797.65, 5954.56, 2753.08, 5928.23, 2725.25, 5893.25}},
+{'c', {2720.65, 5887.89, 2720.61, 5874.15, 2725.25, 5869.25}},
+{'c', {2740.54, 5854.46, 2762.1, 5871.46, 2781.25, 5877.25}},
+{'c', {2783.14, 5890.27, 2792.59, 5900.62, 2807.25, 5900.25}},
+{'c', {2810.06, 5899.9, 2812.64, 5905.86, 2816.25, 5907.25}},
+{'c', {2820.04, 5908.81, 2825.56, 5906.35, 2828.25, 5908.25}},
+{'c', {2847.72, 5921.67, 2863.21, 5933.84, 2882.25, 5947.25}},
+{'c', {2884.44, 5948.36, 2888.22, 5946.61, 2890.25, 5948.25}},
+{'c', {2894.18, 5949.58, 2896.23, 5953.83, 2900.25, 5956.25}},
+{'c', {2903.61, 5958.07, 2906.27, 5954.88, 2908.25, 5952.25}},
+{'c', {2901.29, 5947.96, 2901.25, 5937.43, 2895.25, 5935.25}},
+{'c', {2886.32, 5932.26, 2880.15, 5926.62, 2872.25, 5921.25}},
+{'c', {2869.52, 5919.58, 2862.27, 5922.31, 2861.25, 5920.25}},
+{'c', {2854.55, 5909.03, 2841.53, 5904.21, 2833.25, 5894.25}},
+{'f', {}},
+{'m', {2224.25, 6438.25}},
+{'v', {2122.58, 6469.59, 2000.25, 6187.25}},
+{'c', {2000.25, 6187.24, 1973.4, 6130, 1947.25, 6108.25}},
+{'c', {1920.6, 6086, 1797.4, 6046.4, 1775.25, 6002.25}},
+{'l', {1661.25, 5826.25}},
+{'c', {1661.24, 5826.24, 1823.8, 6002.4, 1859.25, 6029.25}},
+{'v', {1947, 6121.2, 1912.25, 6046.25}},
+{'c', {1912.25, 6046.24, 1757.8, 5927.6, 1771.25, 5826.25}},
+{'c', {1771.25, 5826.24, 1709.4, 5668, 1700.25, 5646.25}},
+{'c', {1700.24, 5646.25, 1876.6, 5998, 1903.25, 6011.25}},
+{'c', {1929.4, 6024.4, 1942.6, 6024.4, 1929.25, 5985.25}},
+{'c', {1916.2, 5945.2, 1911.8, 5764.8, 1881.25, 5743.25}},
+{'v', {1969, 5967.2, 1960.25, 6002.25}},
+{'c', {1960.25, 6002.24, 1995.4, 6042, 2022.25, 5985.25}},
+{'l', {2008.25, 5809.25}},
+{'l', {2057.25, 5677.25}},
+{'v', {2030.6, 5800, 2048.25, 5971.25}},
+{'c', {2048.25, 5971.24, 2026.2, 6086, 2070.25, 6024.25}},
+{'c', {2114.2, 5962.8, 2219.8, 5896.8, 2220.25, 5844.25}},
+{'v', {2162.6, 6037.6, 2061.25, 6090.25}},
+{'l', {2017.25, 6024.25}},
+{'l', {2004.25, 6046.25}},
+{'c', {2004.25, 6046.24, 1964.6, 6055.2, 2013.25, 6130.25}},
+{'y', {2061.4, 6204.8, 2057.25, 6213.25}},
+{'c', {2057.25, 6213.24, 2127.4, 6134.4, 2145.25, 6134.25}},
+{'c', {2145.25, 6134.24, 2290.2, 6218, 2303.25, 5949.25}},
+{'c', {2303.25, 5949.24, 2378.2, 6108, 2277.25, 6183.25}},
+{'v', {2114.2, 6204.8, 2127.25, 6262.25}},
+{'l', {2206.25, 6398.25}},
+{'y', {2246.2, 6455.6, 2228.25, 6425.25}},
+{'f', {}},
+{'m', {2004.25, 6257.25}},
+{'c', {2004.25, 6257.24, 1863.4, 6257.6, 1828.25, 6200.25}},
+{'l', {1749.25, 6095.25}},
+{'v', {1938.2, 6204.8, 1982.25, 6218.25}},
+{'y', {2026.2, 6231.2, 2004.25, 6257.25}},
+{'f', {}},
+{'m', {1564.25, 6187.25}},
+{'c', {1564.25, 6187.24, 1542.2, 6174, 1538.25, 6143.25}},
+{'c', {1533.4, 6112.4, 1507, 6108, 1516.25, 6077.25}},
+{'c', {1524.6, 6046.4, 1546.6, 6020, 1546.25, 6064.25}},
+{'c', {1546.6, 6108, 1564.2, 6130, 1573.25, 6143.25}},
+{'c', {1581.8, 6156.4, 1599.4, 6204.8, 1564.25, 6187.25}},
+{'f', {}},
+{'m', {1467.25, 5514.25}},
+{'v', {1375, 5558, 1340.25, 5597.25}},
+{'c', {1304.6, 5637.2, 1309.57, 5580.37, 1256.25, 5584.25}},
+{'y', {1191.88, 5589.25, 1203.25, 5765.25}},
+{'l', {1159.25, 5681.25}},
+{'c', {1159.25, 5681.24, 1146.2, 5522.8, 1234.25, 5549.25}},
+{'c', {1277.18, 5562.09, 1291.4, 5544.8, 1274.25, 5536.25}},
+{'c', {1256.2, 5527.2, 1335.4, 5522.8, 1304.25, 5505.25}},
+{'c', {1273.8, 5487.6, 1432.2, 5544.8, 1406.25, 5430.25}},
+{'l', {1467.25, 5514.25}},
+{'f', {}},
+{'m', {1329.25, 5355.25}},
+{'c', {1329.25, 5355.24, 1159.4, 5307.2, 1120.25, 5413.25}},
+{'v', {1067, 5386.4, 1091.25, 5353.25}},
+{'y', {1115.4, 5320.4, 1128.25, 5316.25}},
+{'c', {1128.24, 5316.25, 1188, 5302.8, 1181.25, 5294.25}},
+{'y', {1174.8, 5285.2, 1148.25, 5248.25}},
+{'v', {1260.6, 5313.8, 1329.25, 5355.25}},
+{'f', {}},
+{'g', {1}},
+{'m', {3841.25, 4495.25}},
+{'c', {3836.51, 4471.91, 3818.13, 4463.1, 3797.25, 4456.25}},
+{'c', {3776.12, 4466.32, 3747.54, 4500.49, 3727.25, 4478.25}},
+{'c', {3721.59, 4483.07, 3713.03, 4483.73, 3709.25, 4491.25}},
+{'c', {3704.13, 4500.89, 3707.12, 4512.39, 3703.25, 4521.25}},
+{'c', {3697.36, 4535.16, 3689.56, 4550.19, 3691.25, 4566.25}},
+{'c', {3711.48, 4573.62, 3718.02, 4594.59, 3713.25, 4614.25}},
+{'c', {3712.05, 4616.8, 3707.33, 4618.97, 3709.25, 4623.25}},
+{'c', {3711.57, 4626.36, 3715.07, 4628.86, 3718.25, 4632.25}},
+{'c', {3716.52, 4630.31, 3714.93, 4627.75, 3713.25, 4628.25}},
+{'c', {3707, 4629.14, 3708.36, 4636.14, 3710.25, 4640.25}},
+{'c', {3716.23, 4659.61, 3738.38, 4662.51, 3753.25, 4649.25}},
+{'c', {3756.02, 4655.61, 3761.62, 4653.48, 3766.25, 4654.25}},
+{'c', {3765.86, 4660.17, 3770.35, 4665.96, 3772.25, 4670.25}},
+{'c', {3778.22, 4682.66, 3796.14, 4670.5, 3805.25, 4677.25}},
+{'c', {3816.75, 4686.3, 3828.44, 4693.99, 3840.25, 4687.25}},
+{'c', {3860.2, 4676.21, 3878.99, 4662.88, 3892.25, 4643.25}},
+{'c', {3898.51, 4634.56, 3901.15, 4620.47, 3900.25, 4610.25}},
+{'c', {3900.09, 4602.81, 3884.9, 4606.68, 3881.25, 4596.25}},
+{'c', {3874.03, 4577.24, 3894.18, 4571.53, 3902.25, 4557.25}},
+{'c', {3904.67, 4552.97, 3901.77, 4549.63, 3898.25, 4548.25}},
+{'c', {3893.78, 4547.14, 3885.15, 4549.22, 3886.25, 4544.25}},
+{'c', {3897.36, 4509.11, 3867.05, 4501.66, 3841.25, 4495.25}},
+{'f', {}},
+{'m', {3735.25, 4280.25}},
+{'c', {3735.52, 4298.91, 3717.91, 4318.22, 3731.25, 4337.25}},
+{'c', {3732.7, 4335.51, 3734.14, 4333.08, 3735.25, 4333.25}},
+{'c', {3737.09, 4333.08, 3738.54, 4335.51, 3740.25, 4337.25}},
+{'c', {3756.45, 4312.61, 3796.62, 4302.4, 3795.25, 4271.25}},
+{'c', {3794.82, 4266.18, 3782.86, 4256.08, 3793.25, 4249.25}},
+{'c', {3772.87, 4234.16, 3772.26, 4208.09, 3762.25, 4187.25}},
+{'c', {3748.39, 4190.53, 3735.06, 4194.53, 3722.25, 4200.25}},
+{'c', {3726.28, 4216.93, 3724.99, 4235.46, 3734.25, 4250.25}},
+{'c', {3738.79, 4257.66, 3735.63, 4269.52, 3735.25, 4280.25}},
+{'f', {}},
+{'g', {0.8}},
+{'m', {1907.25, 4379.25}},
+{'v', {1721.96, 4267.29, 1876.25, 4436.25}},
+{'y', {1973.4, 4541.59, 2083.25, 4603.25}},
+{'c', {2083.25, 4603.24, 2197.8, 4651.6, 2237.25, 4665.25}},
+{'c', {2277, 4678, 2444.2, 4735.2, 2479.25, 4739.25}},
+{'c', {2514.6, 4744, 2620.2, 4788, 2695.25, 4744.25}},
+{'y', {2769.8, 4700, 2858.25, 4651.25}},
+{'c', {2858.25, 4651.24, 2677.4, 4744, 2638.25, 4717.25}},
+{'c', {2598.2, 4691.2, 2519, 4695.6, 2453.25, 4660.25}},
+{'c', {2453.25, 4660.24, 2290.2, 4611.99, 2255.25, 4590.25}},
+{'c', {2219.8, 4567.99, 2105.4, 4440.39, 2088.25, 4449.25}},
+{'c', {2070.2, 4457.99, 2092.2, 4462.39, 2105.25, 4493.25}},
+{'c', {2118.6, 4523.99, 2096.6, 4541.59, 2008.25, 4471.25}},
+{'y', {1920.6, 4400.79, 1907.25, 4379.25}},
+{'f', {}},
+{'g', {0}},
+{'m', {1989.25, 4444.25}},
+{'v', {2004.36, 4587.86, 2097.25, 4569.25}},
+{'c', {2097.24, 4569.25, 2188.11, 4615.42, 2218.25, 4637.25}},
+{'c', {2218.24, 4637.25, 2307.58, 4655.79, 2322.25, 4662.25}},
+{'c', {2525.26, 4758.28, 2687.16, 4708.57, 2693.25, 4721.25}},
+{'c', {2699.19, 4733.2, 2915.06, 4654.91, 2955.25, 4610.25}},
+{'c', {2959.06, 4605.09, 2842.22, 4671.53, 2735.25, 4692.25}},
+{'c', {2644.54, 4710.09, 2406.92, 4689.61, 2287.25, 4628.25}},
+{'c', {2254.25, 4612.12, 2155.86, 4548.5, 2128.25, 4549.25}},
+{'y', {2100.17, 4550.95, 1989.25, 4444.25}},
+{'f', {}},
+{'g', {0.8}},
+{'m', {1947.25, 4163.25}},
+{'c', {1947.25, 4163.24, 1779.8, 4189.59, 1964.25, 4207.25}},
+{'c', {1964.25, 4207.24, 2162.6, 4229.19, 2206.25, 4286.25}},
+{'c', {2206.25, 4286.24, 2356.2, 4387.59, 2387.25, 4392.25}},
+{'c', {2417.8, 4396.39, 2747.8, 4475.59, 2752.25, 4502.25}},
+{'c', {2756.6, 4528.39, 2818.2, 4528.39, 2836.25, 4519.25}},
+{'c', {2853.4, 4510.79, 2844.6, 4497.59, 2814.25, 4489.25}},
+{'c', {2783, 4479.99, 2439.8, 4299.59, 2369.25, 4286.25}},
+{'c', {2299, 4273.19, 2171.4, 4189.59, 2118.25, 4176.25}},
+{'y', {2065.8, 4163.19, 1947.25, 4163.25}},
+{'f', {}},
+{'g', {0}},
+{'m', {2161.25, 4244.25}},
+{'c', {2161.25, 4244.24, 2067.32, 4253.68, 2162.25, 4262.25}},
+{'c', {2162.25, 4262.24, 2258.9, 4300.36, 2281.25, 4329.25}},
+{'v', {2358.04, 4381.47, 2374.25, 4383.25}},
+{'c', {2389.57, 4385.97, 2543.13, 4426.53, 2545.25, 4440.25}},
+{'c', {2547.64, 4453.55, 2922.37, 4591.95, 2971.25, 4557.25}},
+{'c', {3003.01, 4533.74, 2894.1, 4552.19, 2788.25, 4503.25}},
+{'c', {2773.22, 4496.86, 2400.84, 4336.4, 2365.25, 4329.25}},
+{'c', {2328.74, 4322.89, 2263.41, 4280.08, 2236.25, 4273.25}},
+{'y', {2209.35, 4266.57, 2161.25, 4244.25}},
+{'f', {}},
+{'m', {2074.25, 4220.25}},
+{'c', {2074.25, 4220.24, 2131.8, 4224.79, 2118.25, 4207.25}},
+{'y', {2105.4, 4189.59, 2079.25, 4198.25}},
+{'l', {2074.25, 4220.25}},
+{'f', {}},
+{'m', {1995.25, 4203.25}},
+{'v', {2052.6, 4207.19, 2039.25, 4189.25}},
+{'y', {2026.2, 4171.99, 2000.25, 4181.25}},
+{'l', {1995.25, 4203.25}},
+{'f', {}},
+{'m', {1881.25, 4185.25}},
+{'c', {1881.25, 4185.24, 1938.2, 4189.59, 1925.25, 4172.25}},
+{'y', {1911.8, 4154.39, 1885.25, 4163.25}},
+{'l', {1881.25, 4185.25}},
+{'f', {}},
+{'m', {1797.25, 4176.25}},
+{'c', {1797.25, 4176.24, 1854.6, 4180.79, 1841.25, 4163.25}},
+{'y', {1828.2, 4145.59, 1802.25, 4154.25}},
+{'l', {1797.25, 4176.25}},
+{'f', {}},
+{'m', {2220.25, 4612.25}},
+{'v', {2268.2, 4611.99, 2255.25, 4594.25}},
+{'y', {2241.8, 4576.79, 2206.25, 4581.25}},
+{'l', {2220.25, 4612.25}},
+{'f', {}},
+{'m', {2110.25, 4550.25}},
+{'c', {2110.25, 4550.24, 2181.36, 4573.94, 2154.25, 4537.25}},
+{'y', {2140.6, 4519.59, 2114.25, 4528.25}},
+{'l', {2110.25, 4550.25}},
+{'f', {}},
+{'m', {1986.25, 4497.25}},
+{'c', {1986.25, 4497.24, 2043.8, 4501.99, 2030.25, 4484.25}},
+{'y', {2017.4, 4466.79, 1991.25, 4475.25}},
+{'l', {1986.25, 4497.25}},
+{'f', {}},
+{'m', {1903.25, 4431.25}},
+{'c', {1903.25, 4431.24, 1960.2, 4435.99, 1947.25, 4418.25}},
+{'y', {1933.8, 4400.79, 1907.25, 4409.25}},
+{'l', {1903.25, 4431.25}},
+{'f', {}},
+{'m', {1828.25, 4379.25}},
+{'v', {1885.4, 4383.19, 1872.25, 4365.25}},
+{'y', {1859, 4347.99, 1832.25, 4357.25}},
+{'l', {1828.25, 4379.25}},
+{'f', {}},
+{'m', {2258.25, 4308.25}},
+{'c', {2258.25, 4308.24, 2334.23, 4314.27, 2316.25, 4291.25}},
+{'y', {2299.1, 4267.42, 2264.25, 4279.25}},
+{'l', {2258.25, 4308.25}},
+{'f', {}},
+{'m', {2372.25, 4361.25}},
+{'c', {2372.24, 4361.24, 2448.63, 4367.07, 2431.25, 4343.25}},
+{'y', {2413.5, 4320.22, 2378.25, 4332.25}},
+{'l', {2372.25, 4361.25}},
+{'f', {}},
+{'m', {2491.25, 4409.25}},
+{'c', {2491.25, 4409.24, 2567.43, 4415.47, 2550.25, 4392.25}},
+{'y', {2532.3, 4368.62, 2497.25, 4380.25}},
+{'l', {2491.25, 4409.25}},
+{'f', {}},
+{'m', {2605.25, 4462.25}},
+{'c', {2605.25, 4462.24, 2681.83, 4468.27, 2664.25, 4445.25}},
+{'y', {2646.7, 4421.42, 2611.25, 4433.25}},
+{'l', {2605.25, 4462.25}},
+{'f', {}},
+{'m', {2302.25, 4660.25}},
+{'c', {2302.25, 4660.24, 2378.23, 4666.27, 2360.25, 4643.25}},
+{'y', {2343.1, 4619.42, 2299.25, 4622.25}},
+{'l', {2302.25, 4660.25}},
+{'f', {}},
+{'m', {2412.25, 4695.25}},
+{'c', {2412.25, 4695.24, 2488.23, 4701.47, 2470.25, 4678.25}},
+{'y', {2453.1, 4654.62, 2405.25, 4657.25}},
+{'l', {2412.25, 4695.25}},
+{'f', {}},
+{'m', {2162.25, 4255.25}},
+{'c', {2162.25, 4255.24, 2219.8, 4259.99, 2206.25, 4242.25}},
+{'y', {2193.4, 4224.79, 2167.25, 4233.25}},
+{'l', {2162.25, 4255.25}},
+{'f', {}},
+{'r', {0.6, 0.149, 0}},
+{'m', {1357.25, 5347.25}},
+{'v', {1309, 5250, 1304.25, 5215.25}},
+{'c', {1304.24, 5215.25, 1313.4, 5311.6, 1326.25, 5333.25}},
+{'y', {1339.8, 5355.6, 1357.25, 5347.25}},
+{'f', {}},
+{'m', {1155.25, 5193.25}},
+{'c', {1155.24, 5193.25, 1119.8, 5034.4, 1124.25, 5003.25}},
+{'c', {1124.25, 5003.24, 1111, 5131.2, 1115.25, 5144.25}},
+{'y', {1119.8, 5157.6, 1155.25, 5193.25}},
+{'f', {}},
+{'g', {0.8}},
+{'m', {1384.25, 6013.25}},
+{'l', {1381.25, 5978.25}},
+{'l', {1357.25, 5976.25}},
+{'v', {1513.6, 5837.4, 1520.25, 5754.25}},
+{'v', {1529, 5844, 1384.25, 6013.25}},
+{'f', {}},
+{'g', {0}},
+{'m', {1427.25, 5999.25}},
+{'c', {1422.88, 6004.06, 1425.29, 6012.25, 1421.25, 6015.25}},
+{'c', {1411.9, 6020.79, 1435.08, 6021.42, 1432.25, 6029.25}},
+{'c', {1428.36, 6042.56, 1430.41, 6042.79, 1429.25, 6057.25}},
+{'c', {1428.45, 6063.4, 1434.91, 6080.34, 1439.25, 6085.25}},
+{'c', {1455.05, 6103.36, 1440.49, 6135.61, 1458.25, 6152.25}},
+{'c', {1461.88, 6155.85, 1466.01, 6161.92, 1469.25, 6166.25}},
+{'c', {1476.39, 6177.11, 1489.42, 6182.48, 1500.25, 6191.25}},
+{'c', {1503.37, 6194.62, 1501.16, 6203.72, 1507.25, 6202.25}},
+{'c', {1515.27, 6201.44, 1528.92, 6202.88, 1528.25, 6193.25}},
+{'c', {1527.18, 6170.3, 1512.56, 6151.38, 1498.25, 6133.25}},
+{'c', {1503.45, 6125.29, 1498.41, 6118.3, 1495.25, 6112.25}},
+{'c', {1480.6, 6083.36, 1482.68, 6052.11, 1481.25, 6021.25}},
+{'c', {1480.71, 6019.93, 1477.33, 6019.07, 1477.25, 6018.25}},
+{'c', {1481.3, 5992.8, 1487.73, 5968.62, 1497.25, 5944.25}},
+{'c', {1501.42, 5933.87, 1506.6, 5924.02, 1508.25, 5914.25}},
+{'c', {1509.47, 5906.28, 1510.61, 5897.12, 1506.25, 5889.25}},
+{'c', {1528.5, 5857.57, 1514.11, 5828.41, 1527.25, 5789.25}},
+{'c', {1529.24, 5782.7, 1547.85, 5761.64, 1543.25, 5763.25}},
+{'c', {1515.12, 5772.99, 1513.88, 5777.37, 1512.25, 5785.25}},
+{'c', {1510.33, 5792.51, 1506.7, 5807.65, 1504.25, 5814.25}},
+{'c', {1503.39, 5816.08, 1501.51, 5836.8, 1500.25, 5838.25}},
+{'c', {1484.01, 5864.35, 1498.95, 5862.41, 1484.25, 5888.25}},
+{'c', {1468.17, 5895.83, 1457.54, 5907.97, 1445.25, 5921.25}},
+{'c', {1443.01, 5923, 1455.59, 5930.96, 1453.25, 5933.25}},
+{'c', {1441.58, 5947.48, 1428.9, 5955.92, 1432.25, 5972.25}},
+{'c', {1434.3, 5979.94, 1435.8, 5991.56, 1427.25, 5999.25}},
+{'f', {}},
+{'m', {1388.25, 5980.25}},
+{'c', {1388.25, 5980.24, 1392.6, 5905.6, 1419.25, 5888.25}},
+{'c', {1445.4, 5870.4, 1432.2, 5879.2, 1397.25, 5892.25}},
+{'y', {1361.8, 5905.6, 1375.25, 5914.25}},
+{'c', {1375.24, 5914.24, 1344.2, 5910, 1370.25, 5888.25}},
+{'c', {1397, 5866, 1436.6, 5839.6, 1419.25, 5839.25}},
+{'c', {1401.4, 5839.6, 1317.8, 5883.6, 1318.25, 5914.25}},
+{'y', {1317.8, 5945.2, 1307.25, 5990.25}},
+{'v', {1318.9, 5999.1, 1371.25, 5998.25}},
+{'v', {1387.1, 5990.3, 1388.25, 5980.25}},
+{'f', {}},
+{'g', {1}},
+{'m', {1379.25, 5776.25}},
+{'v', {1285.68, 5806.5, 1082.25, 5769.25}},
+{'c', {1082.25, 5769.24, 1181.74, 5792.05, 1388.25, 5767.25}},
+{'y', {1501.5, 5753.25, 1379.25, 5776.25}},
+{'f', {}},
+{'w', {1.1}},
+{'G', {0}},
+{'m', {1379.25, 5776.25}},
+{'v', {1285.68, 5806.5, 1082.25, 5769.25}},
+{'c', {1082.25, 5769.24, 1181.74, 5792.05, 1388.25, 5767.25}},
+{'y', {1501.5, 5753.25, 1379.25, 5776.25}},
+{'h', {}},
+{'S', {}},
+{'m', {1402.25, 5776.25}},
+{'v', {1311.77, 5815.28, 1106.25, 5795.25}},
+{'c', {1106.24, 5795.24, 1206.97, 5809.86, 1410.25, 5767.25}},
+{'y', {1522.18, 5743.56, 1402.25, 5776.25}},
+{'f', {}},
+{'m', {1402.25, 5776.25}},
+{'v', {1311.77, 5815.28, 1106.25, 5795.25}},
+{'c', {1106.24, 5795.24, 1206.97, 5809.86, 1410.25, 5767.25}},
+{'y', {1522.18, 5743.56, 1402.25, 5776.25}},
+{'h', {}},
+{'S', {}},
+{'m', {1422.25, 5775.25}},
+{'v', {1334.56, 5820.89, 1128.25, 5816.25}},
+{'c', {1128.24, 5816.25, 1229.66, 5823.3, 1429.25, 5765.25}},
+{'y', {1539.05, 5733.72, 1422.25, 5775.25}},
+{'f', {}},
+{'m', {1422.25, 5775.25}},
+{'v', {1334.56, 5820.89, 1128.25, 5816.25}},
+{'c', {1128.24, 5816.25, 1229.66, 5823.3, 1429.25, 5765.25}},
+{'y', {1539.05, 5733.72, 1422.25, 5775.25}},
+{'h', {}},
+{'S', {}},
+{'m', {1436.25, 5773.25}},
+{'c', {1436.24, 5773.24, 1361.05, 5820.48, 1175.25, 5832.25}},
+{'v', {1267.16, 5830.69, 1442.25, 5763.25}},
+{'y', {1537.73, 5726.61, 1436.25, 5773.25}},
+{'f', {}},
+{'m', {1436.25, 5773.25}},
+{'c', {1436.24, 5773.24, 1361.05, 5820.48, 1175.25, 5832.25}},
+{'v', {1267.16, 5830.69, 1442.25, 5763.25}},
+{'y', {1537.73, 5726.61, 1436.25, 5773.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2704.25, 5657.25}},
+{'v', {2694.01, 5650.34, 2696.25, 5663.25}},
+{'c', {2698.73, 5676.32, 3009.02, 5821.2, 3049.25, 5818.25}},
+{'v', {2716.04, 5673.18, 2704.25, 5657.25}},
+{'f', {}},
+{'m', {2704.25, 5657.25}},
+{'v', {2694.01, 5650.34, 2696.25, 5663.25}},
+{'c', {2698.73, 5676.32, 3009.02, 5821.2, 3049.25, 5818.25}},
+{'v', {2716.04, 5673.18, 2704.25, 5657.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2667.25, 5650.25}},
+{'c', {2667.25, 5650.24, 2657.27, 5642.53, 2658.25, 5655.25}},
+{'c', {2659.93, 5668.8, 2957.89, 5837.6, 2997.25, 5838.25}},
+{'v', {2677.44, 5667.02, 2667.25, 5650.25}},
+{'f', {}},
+{'m', {2667.25, 5650.25}},
+{'c', {2667.25, 5650.24, 2657.27, 5642.53, 2658.25, 5655.25}},
+{'c', {2659.93, 5668.8, 2957.89, 5837.6, 2997.25, 5838.25}},
+{'v', {2677.44, 5667.02, 2667.25, 5650.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2631.25, 5635.25}},
+{'v', {2621.65, 5626.78, 2622.25, 5640.25}},
+{'c', {2622.57, 5653.17, 2840.56, 5823.64, 2948.25, 5844.25}},
+{'v', {2750.16, 5754.86, 2631.25, 5635.25}},
+{'f', {}},
+{'m', {2631.25, 5635.25}},
+{'v', {2621.65, 5626.78, 2622.25, 5640.25}},
+{'c', {2622.57, 5653.17, 2840.56, 5823.64, 2948.25, 5844.25}},
+{'v', {2750.16, 5754.86, 2631.25, 5635.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2599.25, 5615.25}},
+{'c', {2599.25, 5615.24, 2590.93, 5607.14, 2591.25, 5619.25}},
+{'c', {2591.76, 5630.89, 2787.94, 5784.31, 2885.25, 5803.25}},
+{'v', {2706.59, 5722.4, 2599.25, 5615.25}},
+{'f', {}},
+{'m', {2599.25, 5615.25}},
+{'c', {2599.25, 5615.24, 2590.93, 5607.14, 2591.25, 5619.25}},
+{'c', {2591.76, 5630.89, 2787.94, 5784.31, 2885.25, 5803.25}},
+{'v', {2706.59, 5722.4, 2599.25, 5615.25}},
+{'h', {}},
+{'S', {}},
+{'g', {0.8}},
+{'m', {2250.25, 3987.25}},
+{'c', {2250.25, 3987.24, 2083.4, 4013.59, 2268.25, 4031.25}},
+{'c', {2268.25, 4031.24, 2466.2, 4053.19, 2510.25, 4110.25}},
+{'c', {2510.25, 4110.24, 2659.8, 4211.59, 2690.25, 4216.25}},
+{'c', {2721.4, 4220.39, 2901.8, 4259.99, 2906.25, 4286.25}},
+{'c', {2910.6, 4312.79, 2976.6, 4339.19, 2994.25, 4330.25}},
+{'c', {3011.8, 4321.59, 3011.8, 4220.39, 2981.25, 4211.25}},
+{'c', {2950.2, 4202.79, 2743.4, 4123.59, 2673.25, 4110.25}},
+{'c', {2602.6, 4097.19, 2475, 4013.59, 2422.25, 4000.25}},
+{'y', {2369.4, 3987.19, 2250.25, 3987.25}},
+{'f', {}},
+{'g', {0}},
+{'m', {3053.25, 4398.25}},
+{'c', {3053.25, 4398.24, 3020.6, 4380.99, 3007.25, 4352.25}},
+{'c', {3007.25, 4352.24, 2937, 4235.79, 2781.25, 4200.25}},
+{'c', {2781.25, 4200.24, 2527.8, 4101.59, 2442.25, 4079.25}},
+{'c', {2442.25, 4079.24, 2294.6, 4024.59, 2213.25, 4033.25}},
+{'c', {2213.25, 4033.24, 2136.2, 4031.19, 2204.25, 4013.25}},
+{'c', {2204.25, 4013.24, 2426.6, 4035.59, 2464.25, 4055.25}},
+{'c', {2464.25, 4055.24, 2635.6, 4112.59, 2668.25, 4141.25}},
+{'c', {2701.6, 4169.79, 2901.8, 4224.79, 2926.25, 4247.25}},
+{'c', {2950.2, 4268.79, 3058, 4361.19, 3053.25, 4398.25}},
+{'f', {}},
+{'m', {2407.25, 4052.25}},
+{'v', {2465.22, 4054.46, 2452.25, 4037.25}},
+{'y', {2439.85, 4020.38, 2413.25, 4030.25}},
+{'l', {2407.25, 4052.25}},
+{'f', {}},
+{'m', {2328.25, 4037.25}},
+{'c', {2328.24, 4037.25, 2385.98, 4039.59, 2373.25, 4022.25}},
+{'y', {2360.63, 4005.5, 2333.25, 4015.25}},
+{'l', {2328.25, 4037.25}},
+{'f', {}},
+{'m', {2214.25, 4023.25}},
+{'v', {2271.28, 4025.88, 2258.25, 4009.25}},
+{'y', {2245.91, 3991.79, 2219.25, 4001.25}},
+{'l', {2214.25, 4023.25}},
+{'f', {}},
+{'m', {2130.25, 4017.25}},
+{'c', {2130.24, 4017.25, 2187.3, 4019.88, 2174.25, 4003.25}},
+{'y', {2161.94, 3985.79, 2135.25, 3995.25}},
+{'l', {2130.25, 4017.25}},
+{'f', {}},
+{'m', {2589.25, 4133.25}},
+{'v', {2666.12, 4136.67, 2649.25, 4114.25}},
+{'y', {2632.36, 4091.3, 2596.25, 4104.25}},
+{'l', {2589.25, 4133.25}},
+{'f', {}},
+{'m', {2703.25, 4182.25}},
+{'v', {2768.58, 4218.34, 2762.25, 4162.25}},
+{'y', {2759.73, 4134.54, 2710.25, 4153.25}},
+{'l', {2703.25, 4182.25}},
+{'f', {}},
+{'m', {2803.25, 4213.25}},
+{'v', {2882.23, 4253.7, 2863.25, 4193.25}},
+{'y', {2854.6, 4166.66, 2810.25, 4183.25}},
+{'l', {2803.25, 4213.25}},
+{'f', {}},
+{'m', {2908.25, 4266.25}},
+{'c', {2908.25, 4266.24, 2956.08, 4319.97, 2968.25, 4246.25}},
+{'y', {2972.27, 4218.77, 2915.25, 4237.25}},
+{'l', {2908.25, 4266.25}},
+{'f', {}},
+{'m', {2495.25, 4084.25}},
+{'v', {2552.68, 4086.53, 2540.25, 4069.25}},
+{'y', {2527.32, 4052.44, 2500.25, 4062.25}},
+{'l', {2495.25, 4084.25}},
+{'f', {}},
+{'g', {1}},
+{'m', {2887.25, 5419.25}},
+{'c', {2887.25, 5419.24, 2878.79, 5412.05, 2879.25, 5424.25}},
+{'c', {2878.77, 5435.6, 3071.1, 5581.5, 3168.25, 5596.25}},
+{'v', {2991.28, 5522.71, 2887.25, 5419.25}},
+{'f', {}},
+{'m', {2887.25, 5419.25}},
+{'c', {2887.25, 5419.24, 2878.79, 5412.05, 2879.25, 5424.25}},
+{'c', {2878.77, 5435.6, 3071.1, 5581.5, 3168.25, 5596.25}},
+{'v', {2991.28, 5522.71, 2887.25, 5419.25}},
+{'h', {}},
+{'S', {}},
+{'g', {0}},
+{'m', {2919.25, 4717.25}},
+{'c', {2919.25, 4717.24, 3161.41, 4471.19, 3262.25, 4431.25}},
+{'c', {3262.25, 4431.24, 3363.81, 4308.39, 3320.25, 4022.25}},
+{'c', {3320.25, 4022.24, 3284.61, 3938.79, 3249.25, 4167.25}},
+{'c', {3249.25, 4167.24, 3284.61, 4444.79, 3161.25, 4269.25}},
+{'v', {3069, 4377.69, 3139.25, 4374.25}},
+{'c', {3139.25, 4374.24, 3174.61, 4352.39, 3179.25, 4370.25}},
+{'c', {3183.41, 4387.59, 3095.4, 4537.19, 2906.25, 4695.25}},
+{'y', {2717, 4854, 2919.25, 4717.25}},
+{'f', {}},
+{'g', {1}},
+{'m', {2277.25, 4291.25}},
+{'v', {2274.8, 4310.59, 2294.25, 4302.25}},
+{'c', {2314.4, 4292.99, 3350.61, 4224.79, 3698.25, 3961.25}},
+{'v', {3201.01, 4215.99, 2277.25, 4291.25}},
+{'f', {}},
+{'m', {2277.25, 4291.25}},
+{'v', {2274.8, 4310.59, 2294.25, 4302.25}},
+{'c', {2314.4, 4292.99, 3350.61, 4224.79, 3698.25, 3961.25}},
+{'v', {3201.01, 4215.99, 2277.25, 4291.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2391.25, 4339.25}},
+{'c', {2391.25, 4339.24, 2389.2, 4358.99, 2409.25, 4350.25}},
+{'c', {2428.8, 4341.39, 3931.41, 4352.39, 4191.25, 3996.25}},
+{'v', {3949.01, 4273.19, 2391.25, 4339.25}},
+{'f', {}},
+{'m', {2391.25, 4339.25}},
+{'c', {2391.25, 4339.24, 2389.2, 4358.99, 2409.25, 4350.25}},
+{'c', {2428.8, 4341.39, 3931.41, 4352.39, 4191.25, 3996.25}},
+{'v', {3949.01, 4273.19, 2391.25, 4339.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2519.25, 4383.25}},
+{'c', {2519.25, 4383.24, 2516.8, 4402.99, 2536.25, 4394.25}},
+{'c', {2556.4, 4385.39, 4463.81, 4528.39, 4723.25, 4172.25}},
+{'c', {4723.24, 4172.25, 4617.81, 4453.59, 2519.25, 4383.25}},
+{'f', {}},
+{'m', {2519.25, 4383.25}},
+{'c', {2519.25, 4383.24, 2516.8, 4402.99, 2536.25, 4394.25}},
+{'c', {2556.4, 4385.39, 4463.81, 4528.39, 4723.25, 4172.25}},
+{'c', {4723.24, 4172.25, 4617.81, 4453.59, 2519.25, 4383.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2629.25, 4436.25}},
+{'v', {2626.8, 4455.79, 2646.25, 4447.25}},
+{'c', {2666.4, 4438.19, 4010.61, 4862.8, 4270.25, 4506.25}},
+{'c', {4270.25, 4506.24, 4122.81, 4777, 2629.25, 4436.25}},
+{'f', {}},
+{'m', {2629.25, 4436.25}},
+{'v', {2626.8, 4455.79, 2646.25, 4447.25}},
+{'c', {2666.4, 4438.19, 4010.61, 4862.8, 4270.25, 4506.25}},
+{'c', {4270.25, 4506.24, 4122.81, 4777, 2629.25, 4436.25}},
+{'h', {}},
+{'S', {}},
+{'m', {1991.25, 4185.25}},
+{'c', {1991.25, 4185.24, 1988.8, 4204.99, 2008.25, 4196.25}},
+{'c', {2028.4, 4187.39, 2224.2, 4163.19, 2246.25, 3736.25}},
+{'c', {2246.25, 3736.24, 2167, 4198.39, 1991.25, 4185.25}},
+{'f', {}},
+{'m', {1991.25, 4185.25}},
+{'c', {1991.25, 4185.24, 1988.8, 4204.99, 2008.25, 4196.25}},
+{'c', {2028.4, 4187.39, 2224.2, 4163.19, 2246.25, 3736.25}},
+{'c', {2246.25, 3736.24, 2167, 4198.39, 1991.25, 4185.25}},
+{'h', {}},
+{'S', {}},
+{'m', {1894.25, 4167.25}},
+{'c', {1894.25, 4167.24, 1892, 4187.39, 1912.25, 4178.25}},
+{'c', {1931.6, 4169.79, 2087.8, 4220.39, 2057.25, 3793.25}},
+{'c', {2057.25, 3793.24, 2070.2, 4180.79, 1894.25, 4167.25}},
+{'f', {}},
+{'m', {1894.25, 4167.25}},
+{'c', {1894.25, 4167.24, 1892, 4187.39, 1912.25, 4178.25}},
+{'c', {1931.6, 4169.79, 2087.8, 4220.39, 2057.25, 3793.25}},
+{'c', {2057.25, 3793.24, 2070.2, 4180.79, 1894.25, 4167.25}},
+{'h', {}},
+{'S', {}},
+{'m', {1806.25, 4163.25}},
+{'c', {1806.25, 4163.24, 1804, 4182.99, 1824.25, 4174.25}},
+{'c', {1843.6, 4165.39, 2013, 4158.79, 1876.25, 3912.25}},
+{'c', {1876.25, 3912.24, 1982.2, 4176.39, 1806.25, 4163.25}},
+{'f', {}},
+{'m', {1806.25, 4163.25}},
+{'c', {1806.25, 4163.24, 1804, 4182.99, 1824.25, 4174.25}},
+{'c', {1843.6, 4165.39, 2013, 4158.79, 1876.25, 3912.25}},
+{'c', {1876.25, 3912.24, 1982.2, 4176.39, 1806.25, 4163.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2327.25, 4651.25}},
+{'c', {2327.25, 4651.24, 2321.9, 4628.89, 2339.25, 4642.25}},
+{'c', {2515.69, 4781.16, 2872.55, 5429.08, 3485.25, 5491.25}},
+{'v', {3063.7, 5624.26, 2327.25, 4651.25}},
+{'f', {}},
+{'m', {2327.25, 4651.25}},
+{'c', {2327.25, 4651.24, 2321.9, 4628.89, 2339.25, 4642.25}},
+{'c', {2515.69, 4781.16, 2872.55, 5429.08, 3485.25, 5491.25}},
+{'v', {3063.7, 5624.26, 2327.25, 4651.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2450.25, 4670.25}},
+{'v', {2434.99, 4683.8, 2455.25, 4690.25}},
+{'c', {2476.09, 4697.55, 3712.95, 5495.08, 4145.25, 5407.25}},
+{'v', {3846.91, 5490.06, 2450.25, 4670.25}},
+{'f', {}},
+{'m', {2450.25, 4670.25}},
+{'v', {2434.99, 4683.8, 2455.25, 4690.25}},
+{'c', {2476.09, 4697.55, 3712.95, 5495.08, 4145.25, 5407.25}},
+{'v', {3846.91, 5490.06, 2450.25, 4670.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2220.25, 4603.25}},
+{'c', {2220.25, 4603.24, 2215.13, 4585.97, 2234.25, 4596.25}},
+{'c', {2332.68, 4653.03, 2372.45, 5282.35, 2852.25, 5296.25}},
+{'c', {2852.25, 5296.24, 2495.27, 5458.01, 2220.25, 4603.25}},
+{'f', {}},
+{'m', {2220.25, 4603.25}},
+{'c', {2220.25, 4603.24, 2215.13, 4585.97, 2234.25, 4596.25}},
+{'c', {2332.68, 4653.03, 2372.45, 5282.35, 2852.25, 5296.25}},
+{'c', {2852.25, 5296.24, 2495.27, 5458.01, 2220.25, 4603.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2001.25, 4482.25}},
+{'c', {2001.25, 4482.24, 1989.73, 4468.13, 2011.25, 4471.25}},
+{'c', {2123.92, 4485.57, 2359.38, 4939.55, 2848.25, 4883.25}},
+{'v', {2496.58, 5041.49, 2001.25, 4482.25}},
+{'f', {}},
+{'m', {2001.25, 4482.25}},
+{'c', {2001.25, 4482.24, 1989.73, 4468.13, 2011.25, 4471.25}},
+{'c', {2123.92, 4485.57, 2359.38, 4939.55, 2848.25, 4883.25}},
+{'v', {2496.58, 5041.49, 2001.25, 4482.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2125.25, 4549.25}},
+{'v', {2116.28, 4533.5, 2137.25, 4540.25}},
+{'c', {2244.62, 4576.39, 2388.55, 5067.12, 2879.25, 5106.25}},
+{'v', {2503.63, 5193.51, 2125.25, 4549.25}},
+{'f', {}},
+{'m', {2125.25, 4549.25}},
+{'v', {2116.28, 4533.5, 2137.25, 4540.25}},
+{'c', {2244.62, 4576.39, 2388.55, 5067.12, 2879.25, 5106.25}},
+{'v', {2503.63, 5193.51, 2125.25, 4549.25}},
+{'h', {}},
+{'S', {}},
+{'m', {1924.25, 4426.25}},
+{'v', {1914.54, 4414.5, 1932.25, 4417.25}},
+{'c', {2023.23, 4428.64, 2213.95, 4796.36, 2610.25, 4751.25}},
+{'v', {2325.08, 4878.93, 1924.25, 4426.25}},
+{'f', {}},
+{'m', {1924.25, 4426.25}},
+{'v', {1914.54, 4414.5, 1932.25, 4417.25}},
+{'c', {2023.23, 4428.64, 2213.95, 4796.36, 2610.25, 4751.25}},
+{'v', {2325.08, 4878.93, 1924.25, 4426.25}},
+{'h', {}},
+{'S', {}},
+{'m', {1840.25, 4361.25}},
+{'c', {1840.25, 4361.24, 1829.5, 4349.47, 1847.25, 4351.25}},
+{'c', {1892.84, 4354.17, 2229.86, 4718.04, 2433.25, 4547.25}},
+{'v', {2300.85, 4753.47, 1840.25, 4361.25}},
+{'f', {}},
+{'m', {1840.25, 4361.25}},
+{'c', {1840.25, 4361.24, 1829.5, 4349.47, 1847.25, 4351.25}},
+{'c', {1892.84, 4354.17, 2229.86, 4718.04, 2433.25, 4547.25}},
+{'v', {2300.85, 4753.47, 1840.25, 4361.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2605.25, 4712.25}},
+{'v', {2592.19, 4727.02, 2613.25, 4731.25}},
+{'c', {2634.7, 4735.48, 3962.43, 5370.33, 4380.25, 5229.25}},
+{'v', {4094.67, 5348.41, 2605.25, 4712.25}},
+{'f', {}},
+{'m', {2605.25, 4712.25}},
+{'v', {2592.19, 4727.02, 2613.25, 4731.25}},
+{'c', {2634.7, 4735.48, 3962.43, 5370.33, 4380.25, 5229.25}},
+{'v', {4094.67, 5348.41, 2605.25, 4712.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2237.25, 4005.25}},
+{'v', {2235.2, 4024.59, 2255.25, 4016.25}},
+{'c', {2274.8, 4006.99, 2444.2, 4000.39, 2308.25, 3754.25}},
+{'v', {2413.4, 4017.99, 2237.25, 4005.25}},
+{'f', {}},
+{'m', {2237.25, 4005.25}},
+{'v', {2235.2, 4024.59, 2255.25, 4016.25}},
+{'c', {2274.8, 4006.99, 2444.2, 4000.39, 2308.25, 3754.25}},
+{'v', {2413.4, 4017.99, 2237.25, 4005.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2352.25, 4022.25}},
+{'c', {2352.25, 4022.24, 2349.6, 4042.19, 2369.25, 4033.25}},
+{'c', {2389.2, 4024.59, 2585, 4000.39, 2607.25, 3573.25}},
+{'c', {2607.25, 3573.24, 2527.8, 4035.59, 2352.25, 4022.25}},
+{'f', {}},
+{'m', {2352.25, 4022.25}},
+{'c', {2352.25, 4022.24, 2349.6, 4042.19, 2369.25, 4033.25}},
+{'c', {2389.2, 4024.59, 2585, 4000.39, 2607.25, 3573.25}},
+{'c', {2607.25, 3573.24, 2527.8, 4035.59, 2352.25, 4022.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2426.25, 4040.25}},
+{'v', {2424.4, 4059.79, 2444.25, 4051.25}},
+{'c', {2464, 4042.19, 2734.6, 4004.79, 2994.25, 3648.25}},
+{'c', {2994.25, 3648.24, 2602.6, 4053.19, 2426.25, 4040.25}},
+{'f', {}},
+{'m', {2426.25, 4040.25}},
+{'v', {2424.4, 4059.79, 2444.25, 4051.25}},
+{'c', {2464, 4042.19, 2734.6, 4004.79, 2994.25, 3648.25}},
+{'c', {2994.25, 3648.24, 2602.6, 4053.19, 2426.25, 4040.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2510.25, 4062.25}},
+{'v', {2505.66, 4081.64, 2526.25, 4075.25}},
+{'c', {2547.25, 4069.46, 2772.02, 4090.34, 3191.25, 3714.25}},
+{'c', {3191.25, 3714.24, 2683.16, 4098.78, 2510.25, 4062.25}},
+{'f', {}},
+{'m', {2510.25, 4062.25}},
+{'v', {2505.66, 4081.64, 2526.25, 4075.25}},
+{'c', {2547.25, 4069.46, 2772.02, 4090.34, 3191.25, 3714.25}},
+{'c', {3191.25, 3714.24, 2683.16, 4098.78, 2510.25, 4062.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2625.25, 4110.25}},
+{'c', {2625.24, 4110.24, 2620.07, 4130.04, 2641.25, 4124.25}},
+{'c', {2661.64, 4117.86, 3044.82, 4068.34, 3613.25, 3683.25}},
+{'c', {3613.25, 3683.24, 2797.56, 4147.18, 2625.25, 4110.25}},
+{'f', {}},
+{'m', {2625.25, 4110.25}},
+{'c', {2625.24, 4110.24, 2620.07, 4130.04, 2641.25, 4124.25}},
+{'c', {2661.64, 4117.86, 3044.82, 4068.34, 3613.25, 3683.25}},
+{'c', {3613.25, 3683.24, 2797.56, 4147.18, 2625.25, 4110.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2180.25, 4238.25}},
+{'v', {2178, 4257.79, 2198.25, 4249.25}},
+{'c', {2217.6, 4240.19, 2805, 4211.59, 3139.25, 3917.25}},
+{'v', {2755.49, 4189.31, 2180.25, 4238.25}},
+{'f', {}},
+{'m', {2180.25, 4238.25}},
+{'v', {2178, 4257.79, 2198.25, 4249.25}},
+{'c', {2217.6, 4240.19, 2805, 4211.59, 3139.25, 3917.25}},
+{'v', {2755.49, 4189.31, 2180.25, 4238.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2070.25, 4194.25}},
+{'v', {2068, 4213.79, 2088.25, 4205.25}},
+{'c', {2107.6, 4196.19, 2378.2, 4158.79, 2638.25, 3802.25}},
+{'c', {2638.25, 3802.24, 2246.2, 4207.19, 2070.25, 4194.25}},
+{'f', {}},
+{'m', {2070.25, 4194.25}},
+{'v', {2068, 4213.79, 2088.25, 4205.25}},
+{'c', {2107.6, 4196.19, 2378.2, 4158.79, 2638.25, 3802.25}},
+{'c', {2638.25, 3802.24, 2246.2, 4207.19, 2070.25, 4194.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2744.25, 4161.25}},
+{'v', {2738.32, 4180.35, 2759.25, 4176.25}},
+{'c', {2780.69, 4171.25, 3166.46, 4149.95, 3762.25, 3808.25}},
+{'v', {2907.48, 4186.25, 2744.25, 4161.25}},
+{'f', {}},
+{'m', {2744.25, 4161.25}},
+{'v', {2738.32, 4180.35, 2759.25, 4176.25}},
+{'c', {2780.69, 4171.25, 3166.46, 4149.95, 3762.25, 3808.25}},
+{'v', {2907.48, 4186.25, 2744.25, 4161.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2837.25, 4188.25}},
+{'v', {2830.71, 4206.75, 2852.25, 4202.25}},
+{'c', {2873.09, 4197.65, 3258.86, 4176.36, 3854.25, 3834.25}},
+{'v', {3006.48, 4217.05, 2837.25, 4188.25}},
+{'f', {}},
+{'m', {2837.25, 4188.25}},
+{'v', {2830.71, 4206.75, 2852.25, 4202.25}},
+{'c', {2873.09, 4197.65, 3258.86, 4176.36, 3854.25, 3834.25}},
+{'v', {3006.48, 4217.05, 2837.25, 4188.25}},
+{'h', {}},
+{'S', {}},
+{'m', {2942.25, 4243.25}},
+{'v', {2936.32, 4261.75, 2957.25, 4257.25}},
+{'c', {2978.69, 4252.65, 3452.46, 4204.95, 4439.25, 3827.25}},
+{'v', {3112.09, 4272.05, 2942.25, 4243.25}},
+{'f', {}},
+{'m', {2942.25, 4243.25}},
+{'v', {2936.32, 4261.75, 2957.25, 4257.25}},
+{'c', {2978.69, 4252.65, 3452.46, 4204.95, 4439.25, 3827.25}},
+{'v', {3112.09, 4272.05, 2942.25, 4243.25}},
+{'h', {}},
+{'S', {}},
+{'g', {0}},
+{'m', {1718.25, 4167.25}},
+{'c', {1718.25, 4167.24, 1775.4, 4171.99, 1762.25, 4154.25}},
+{'y', {1749, 4136.79, 1722.25, 4145.25}},
+{'l', {1718.25, 4167.25}},
+{'f', {}},
+{'m', {2057.25, 4013.25}},
+{'c', {2057.25, 4013.24, 2114.2, 4017.99, 2101.25, 4000.25}},
+{'y', {2087.8, 3982.79, 2061.25, 3991.25}},
+{'l', {2057.25, 4013.25}},
+{'f', {}},
+{'m', {1960.25, 4018.25}},
+{'v', {2017.4, 4022.39, 2004.25, 4005.25}},
+{'y', {1991, 3987.19, 1964.25, 3996.25}},
+{'l', {1960.25, 4018.25}},
+{'f', {}},
+{'m', {911.25, 4148.25}},
+{'v', {966.633, 4133.44, 948.246, 4121.25}},
+{'y', {930.168, 4108.81, 908.246, 4126.25}},
+{'l', {911.25, 4148.25}},
+{'f', {}},
+{'m', {933.25, 4253.25}},
+{'c', {933.25, 4253.24, 988.633, 4239.04, 970.246, 4226.25}},
+{'y', {952.168, 4214.41, 930.246, 4231.25}},
+{'l', {933.25, 4253.25}},
+{'f', {}},
+{'m', {854.246, 4302.25}},
+{'v', {909.434, 4287.44, 891.246, 4275.25}},
+{'y', {872.969, 4262.81, 851.25, 4280.25}},
+{'l', {854.246, 4302.25}},
+{'f', {}},
+{'g', {0.8}},
+{'m', {1731.25, 3477.25}},
+{'v', {1713.8, 3476.79, 1670.25, 3455.25}},
+{'c', {1647.8, 3454.79, 1524.6, 3415.19, 1463.25, 3305.25}},
+{'c', {1463.24, 3305.24, 1599.4, 3410.79, 1731.25, 3477.25}},
+{'f', {}},
+{'m', {2696.25, 2457.25}},
+{'c', {2697.82, 2454.84, 2698.43, 2450.22, 2701.25, 2450.25}},
+{'c', {2708.88, 2449.6, 2722.44, 2446.29, 2721.25, 2453.25}},
+{'c', {2711.74, 2500.65, 2702.12, 2554.95, 2656.25, 2575.25}},
+{'c', {2649.63, 2577.9, 2633.77, 2573.36, 2633.25, 2563.25}},
+{'c', {2631.56, 2546.87, 2630.27, 2532.03, 2633.25, 2516.25}},
+{'c', {2636.97, 2500.27, 2660.71, 2500.16, 2671.25, 2515.25}},
+{'c', {2681.09, 2497, 2685.85, 2476.72, 2696.25, 2457.25}},
+{'f', {}},
+{'m', {2574.25, 2405.25}},
+{'c', {2582.64, 2390.06, 2581.25, 2369.61, 2597.25, 2363.25}},
+{'c', {2604.95, 2360.19, 2625.3, 2370.91, 2620.25, 2382.25}},
+{'c', {2611.88, 2405.4, 2607.61, 2430.03, 2592.25, 2450.25}},
+{'c', {2590.14, 2452.83, 2592.77, 2458.69, 2591.25, 2462.25}},
+{'c', {2584.48, 2475.72, 2571.89, 2483.8, 2556.25, 2480.25}},
+{'c', {2544.13, 2455.95, 2556.76, 2432.54, 2573.25, 2413.25}},
+{'c', {2575, 2412.06, 2573.17, 2407.93, 2574.25, 2405.25}},
+{'f', {}},
+{'m', {2139.25, 2462.25}},
+{'c', {2137.61, 2466.84, 2137.19, 2471.94, 2139.25, 2475.25}},
+{'c', {2144.84, 2486.83, 2153.57, 2499.17, 2150.25, 2510.25}},
+{'c', {2146.03, 2522.03, 2133.76, 2520.03, 2126.25, 2514.25}},
+{'c', {2114.12, 2502.84, 2113.58, 2482.75, 2106.25, 2468.25}},
+{'c', {2103.82, 2463.55, 2104.39, 2457.25, 2099.25, 2453.25}},
+{'c', {2094.32, 2448.32, 2089.48, 2431.55, 2090.25, 2425.25}},
+{'c', {2091.12, 2421.83, 2089.11, 2312.84, 2091.25, 2315.25}},
+{'c', {2098.36, 2323.8, 2131.91, 2430.88, 2132.25, 2440.25}},
+{'c', {2133.29, 2448.73, 2141.35, 2453.84, 2139.25, 2462.25}},
+{'f', {}},
+{'m', {1857.25, 2537.25}},
+{'c', {1885.75, 2564.9, 1916.44, 2597.47, 1911.25, 2638.25}},
+{'c', {1910.3, 2649.04, 1890.78, 2643.24, 1888.25, 2634.25}},
+{'c', {1879.9, 2594.06, 1857.47, 2564.79, 1829.25, 2538.25}},
+{'c', {1805.07, 2515.96, 1784.58, 2446.18, 1782.25, 2440.25}},
+{'c', {1822.66, 2498.4, 1847.49, 2528.61, 1857.25, 2537.25}},
+{'f', {}},
+{'m', {1707.25, 2603.25}},
+{'c', {1713.21, 2607.08, 1709.89, 2612.48, 1712.25, 2616.25}},
+{'c', {1722.76, 2632.18, 1737.07, 2645.88, 1737.25, 2665.25}},
+{'c', {1737.3, 2668.04, 1733.15, 2671.45, 1729.25, 2669.25}},
+{'c', {1726.41, 2666.96, 1722.65, 2665.53, 1721.25, 2664.25}},
+{'c', {1699.14, 2637.16, 1683.73, 2607.78, 1668.25, 2577.25}},
+{'c', {1665.85, 2572.92, 1653.27, 2523.52, 1656.25, 2522.25}},
+{'c', {1659.34, 2521.36, 1678.26, 2567.35, 1680.25, 2568.25}},
+{'c', {1694.46, 2576.15, 1694.59, 2593.91, 1707.25, 2603.25}},
+{'f', {}},
+{'m', {1891.25, 2388.25}},
+{'c', {1896.41, 2397.59, 1913.75, 2410.81, 1912.25, 2420.25}},
+{'c', {1911.2, 2430.8, 1916.35, 2446.67, 1906.25, 2439.25}},
+{'c', {1892.49, 2428.68, 1854.55, 2413.84, 1851.25, 2350.25}},
+{'c', {1851.3, 2343.66, 1884.01, 2373.29, 1891.25, 2388.25}},
+{'f', {}},
+{'m', {2050.25, 2577.25}},
+{'c', {2054.8, 2584.34, 2062.59, 2579.13, 2067.25, 2582.25}},
+{'c', {2074.76, 2586.12, 2081.48, 2592.25, 2084.25, 2599.25}},
+{'c', {2095.37, 2623.03, 2114.82, 2643.05, 2116.25, 2669.25}},
+{'c', {2100, 2684.84, 2092.53, 2662.4, 2085.25, 2652.25}},
+{'c', {2071.05, 2669.9, 2060.05, 2649.29, 2046.25, 2643.25}},
+{'c', {2044.98, 2643.27, 2042.64, 2647.09, 2042.25, 2647.25}},
+{'c', {2028.87, 2641.95, 2021.39, 2630.06, 2010.25, 2621.25}},
+{'c', {2008.53, 2620, 2004.08, 2621.98, 2002.25, 2620.25}},
+{'c', {1995.29, 2613.85, 1984.59, 2610.27, 1981.25, 2603.25}},
+{'c', {1969.3, 2572.99, 1934.75, 2549.95, 1914.25, 2467.25}},
+{'c', {1918.19, 2456.99, 1963.63, 2540.05, 1969.25, 2547.25}},
+{'c', {1978.18, 2560.88, 1979.48, 2529.46, 1993.25, 2537.25}},
+{'c', {1994.08, 2537.08, 1996.12, 2534.46, 1997.25, 2533.25}},
+{'c', {1999.73, 2536.07, 2002.13, 2538.59, 2006.25, 2537.25}},
+{'c', {2006.4, 2541.79, 2004.93, 2547.9, 2007.25, 2549.25}},
+{'c', {2020.74, 2560.4, 2019.82, 2572.15, 2028.25, 2586.25}},
+{'c', {2033.44, 2577.11, 2045.11, 2585.02, 2050.25, 2577.25}},
+{'f', {}},
+{'m', {2708.25, 1978.25}},
+{'v', {2789.6, 2202.99, 2741.25, 2326.25}},
+{'v', {2866.6, 2088.59, 2816.25, 1965.25}},
+{'v', {2811.6, 2079.79, 2767.25, 2135.25}},
+{'v', {2723.6, 1993.99, 2708.25, 1978.25}},
+{'f', {}},
+{'m', {2545.25, 2007.25}},
+{'v', {2604.8, 2103.99, 2517.25, 2306.25}},
+{'v', {2508, 2081.99, 2433.25, 1961.25}},
+{'v', {2589.4, 2183.19, 2545.25, 2007.25}},
+{'f', {}},
+{'m', {2435.25, 2029.25}},
+{'v', {2433.2, 2249.19, 2437.25, 2282.25}},
+{'v', {2395.8, 2099.59, 2283.25, 1994.25}},
+{'v', {2442, 2125.99, 2435.25, 2029.25}},
+{'f', {}},
+{'m', {2330.25, 2381.25}},
+{'v', {2395.8, 2231.59, 2286.25, 2029.25}},
+{'v', {2356.2, 2163.39, 2303.25, 2240.25}},
+{'v', {2332, 2277.79, 2330.25, 2381.25}},
+{'f', {}},
+{'m', {2118.25, 2033.25}},
+{'v', {2107.6, 2205.19, 2127.25, 2229.25}},
+{'v', {2129.6, 2299.79, 2125.25, 2311.25}},
+{'v', {2169.2, 2378.99, 2171.25, 2297.25}},
+{'v', {2186.8, 2211.79, 2217.25, 2161.25}},
+{'v', {2257.2, 2101.79, 2255.25, 2031.25}},
+{'v', {2145, 2363.59, 2118.25, 2033.25}},
+{'f', {}},
+{'m', {2079.25, 2337.25}},
+{'v', {2006.4, 2218.39, 1986.25, 2009.25}},
+{'v', {1971.2, 2077.59, 2013.25, 2236.25}},
+{'v', {2059.2, 2405.39, 2079.25, 2337.25}},
+{'f', {}},
+{'m', {1839.25, 2119.25}},
+{'v', {1894.2, 2178.79, 1909.25, 2234.25}},
+{'v', {1949.2, 2407.59, 1879.25, 2313.25}},
+{'v', {1881, 2224.99, 1791.25, 2143.25}},
+{'v', {1843.6, 2169.99, 1839.25, 2119.25}},
+{'f', {}},
+{'m', {1775.25, 2174.25}},
+{'v', {1812.8, 2365.79, 1821.25, 2374.25}},
+{'v', {1841.4, 2411.99, 1810.25, 2377.25}},
+{'v', {1713.8, 2165.59, 1670.25, 2093.25}},
+{'v', {1757.8, 2194.19, 1775.25, 2174.25}},
+{'f', {}},
+{'m', {1711.25, 2357.25}},
+{'c', {1711.24, 2357.25, 1839.2, 2603.39, 1599.25, 2319.25}},
+{'v', {1720.4, 2427.39, 1711.25, 2357.25}},
+{'f', {}},
+{'m', {1542.25, 2517.25}},
+{'v', {1595, 2724.39, 1623.25, 2722.25}},
+{'c', {1623.24, 2722.24, 1713.8, 2821.19, 1641.25, 2704.25}},
+{'c', {1641.25, 2704.24, 1575.2, 2598.99, 1582.25, 2491.25}},
+{'v', {1575.2, 2596.79, 1542.25, 2517.25}},
+{'f', {}},
+{'m', {5175.25, 2643.25}},
+{'v', {5043.5, 2753, 5016.25, 2791.25}},
+{'v', {5164.5, 2588, 5164.25, 2511.25}},
+{'v', {5192, 2593.5, 5175.25, 2643.25}},
+{'f', {}},
+{'m', {5236.25, 2885.25}},
+{'v', {5005, 3050, 4966.25, 3132.25}},
+{'v', {5258, 2808, 5258.25, 2758.25}},
+{'v', {5263.5, 2857.5, 5236.25, 2885.25}},
+{'f', {}},
+{'m', {5423.25, 4579.25}},
+{'v', {5291, 4667, 5274.25, 4645.25}},
+{'v', {5390, 4573.5, 5417.25, 4480.25}},
+{'v', {5401, 4579, 5423.25, 4579.25}},
+{'f', {}},
+{'m', {5516.25, 3693.25}},
+{'l', {5324.25, 3825.25}},
+{'v', {5533, 3638.5, 5538.25, 3594.25}},
+{'l', {5516.25, 3693.25}},
+{'f', {}},
+{'w', {11}},
+{'m', {1218.25, 3941.25}},
+{'l', {1460.25, 3889.25}},
+{'S', {}},
+{'m', {1771.25, 2159.25}},
+{'v', {1765.5, 2197.5, 1666.25, 2082.25}},
+{'S', {}},
+{'m', {1831.25, 2104.25}},
+{'v', {1853.5, 2175.5, 1782.25, 2126.25}},
+{'S', {}},
+{'m', {2425.25, 2010.25}},
+{'v', {2442, 2131.5, 2315.25, 1988.25}},
+{'S', {}},
+{'!', {}}
diff --git a/tests/zapf.raw b/tests/zapf.raw
new file mode 100644
index 00000000..76987ada
--- /dev/null
+++ b/tests/zapf.raw
Binary files differ
diff --git a/tree/blend.c b/tree/blend.c
new file mode 100644
index 00000000..d2154240
--- /dev/null
+++ b/tree/blend.c
@@ -0,0 +1,53 @@
+#include <fitz.h>
+
+fz_error *
+fz_newblend(fz_node **nodep, fz_blendkind b, int k, int i)
+{
+ fz_blend *node;
+
+ node = fz_malloc(sizeof (fz_blend));
+ if (!node)
+ return fz_outofmem;
+ *nodep = (fz_node*)node;
+
+ fz_initnode((fz_node*)node, FZ_NBLEND);
+ node->child = nil;
+ node->mode = b;
+ node->knockout = k;
+ node->isolated = i;
+
+ return nil;
+}
+
+void
+fz_freeblend(fz_blend *node)
+{
+ if (node->child)
+ fz_freenode(node->child);
+ fz_free(node);
+}
+
+fz_rect
+fz_boundblend(fz_blend *node, fz_matrix ctm)
+{
+ fz_node *child;
+ fz_rect bbox;
+ fz_rect r;
+
+ bbox = FZ_INFRECT;
+
+ for (child = node->child; child; child = child->next)
+ {
+ r = fz_boundnode(child, ctm);
+ if (r.max.x >= r.min.x)
+ {
+ if (bbox.max.x >= bbox.min.x)
+ bbox = fz_mergerects(r, bbox);
+ else
+ bbox = r;
+ }
+ }
+
+ return bbox;
+}
+
diff --git a/tree/cache.c b/tree/cache.c
new file mode 100644
index 00000000..f4544844
--- /dev/null
+++ b/tree/cache.c
@@ -0,0 +1,98 @@
+#include <fitz.h>
+
+static int
+hash(fz_cachekey *k)
+{
+ int h;
+ int i;
+
+ h = (int)k->tree ^ (int)k->node ^ k->tag;
+
+ /* http://www.cs.yorku.ca/~oz/hash.html -- sdbm */
+ for (i = 0; i < k->len; i++)
+ h = k->key[i] + (h << 6) + (h << 16) - h;
+
+ return h;
+}
+
+static int
+equal(fz_cachekey *a, fz_cachekey *b)
+{
+ if (a->tree != b->tree) return 0;
+ if (a->node != b->node) return 0;
+ if (a->tag != b->tag) return 0;
+ if (a->len != b->len) return 0;
+ return memcmp(a->key, b->key, a->len) == 0;
+}
+
+fz_cache *
+fz_newcache(int maxentries, int maxdatasize)
+{
+ fz_cache *cache;
+ int i;
+
+ cache = fz_malloc(sizeof(fz_cache));
+ if (!cache) return nil;
+
+ cache->maxsize = maxdatasize;
+ cache->cursize = 0;
+ cache->len = maxentries;
+ cache->table = fz_malloc(sizeof(fz_cachebucket) * maxentries);
+ if (!cache->table) {
+ fz_free(cache);
+ return nil;
+ }
+
+ for (i = 0; i < cache->len; i++) {
+ cache->table[i].val = nil;
+ }
+
+ return cache;
+}
+
+void *
+fz_findincache(fz_cache *cache, fz_cachekey *key)
+{
+ int h;
+ int i;
+
+ h = hash(key);
+
+ i = h % cache->len;
+ if (equal(key, &cache->table[i].key))
+ return cache->table[i].val;
+ return nil;
+}
+
+int
+fz_insertincache(fz_cache *cache, fz_cachekey *key, void *data, int size)
+{
+ int h;
+ int i;
+
+ h = hash(key);
+ i = h % cache->len;
+
+ if (cache->table[i].val)
+ fz_free(cache->table[i].val);
+ cache->table[i].key = *key;
+
+ return FZ_OKAY;
+}
+
+int
+fz_evictfromcache(fz_cache *cache, fz_cachekey *key)
+{
+ /* TODO */
+ return FZ_OKAY;
+}
+
+void
+fz_freecache(fz_cache *cache)
+{
+ /* FIXME evict everything from cache first? */
+ if (cache->table)
+ fz_free(cache->table);
+ fz_free(cache);
+}
+
diff --git a/tree/cmap.c b/tree/cmap.c
new file mode 100644
index 00000000..21b08274
--- /dev/null
+++ b/tree/cmap.c
@@ -0,0 +1,452 @@
+#include <fitz.h>
+
+typedef struct fz_range_s fz_range;
+
+enum { MAXCODESPACE = 10 };
+enum { SINGLE, RANGE, LOOKUP };
+
+struct fz_range_s
+{
+ int low;
+ int high;
+ int flag;
+ int offset;
+};
+
+struct fz_cmap_s
+{
+ char cmapname[32];
+
+ char usecmapname[32];
+ fz_cmap *usecmap;
+
+ int wmode;
+
+ int ncspace;
+ struct {
+ int n;
+ unsigned char lo[4];
+ unsigned char hi[4];
+ } cspace[MAXCODESPACE];
+
+ int rlen, rcap;
+ fz_range *ranges;
+
+ int tlen, tcap;
+ int *lookup;
+};
+
+fz_error *
+fz_newcmap(fz_cmap **cmapp)
+{
+ fz_cmap *cmap;
+
+ cmap = *cmapp = fz_malloc(sizeof(fz_cmap));
+ if (!cmap)
+ return fz_outofmem;
+
+ strcpy(cmap->cmapname, "");
+
+ strcpy(cmap->usecmapname, "");
+ cmap->usecmap = nil;
+
+ cmap->wmode = 0;
+
+ cmap->ncspace = 0;
+
+ cmap->rlen = 0;
+ cmap->rcap = 0;
+ cmap->ranges = nil;
+
+ cmap->tlen = 0;
+ cmap->tcap = 0;
+ cmap->lookup = nil;
+
+ return nil;
+}
+
+void
+fz_freecmap(fz_cmap *cmap)
+{
+ if (cmap->usecmap)
+ fz_freecmap(cmap->usecmap);
+ fz_free(cmap->ranges);
+ fz_free(cmap->lookup);
+ fz_free(cmap);
+}
+
+char *
+fz_getcmapname(fz_cmap *cmap)
+{
+ if (cmap->cmapname[0])
+ return cmap->cmapname;
+ return nil;
+}
+
+void
+fz_setcmapname(fz_cmap *cmap, char *cmapname)
+{
+ strlcpy(cmap->cmapname, cmapname, sizeof cmap->cmapname);
+}
+
+char *
+fz_getusecmapname(fz_cmap *cmap)
+{
+ if (cmap->usecmapname[0])
+ return cmap->usecmapname;
+ return nil;
+}
+
+void
+fz_setusecmapname(fz_cmap *cmap, char *usecmap)
+{
+ strlcpy(cmap->usecmapname, usecmap, sizeof cmap->usecmapname);
+}
+
+fz_cmap *
+fz_getusecmap(fz_cmap *cmap)
+{
+ return cmap->usecmap;
+}
+
+void
+fz_setusecmap(fz_cmap *cmap, fz_cmap *usecmap)
+{
+ int i;
+
+ cmap->usecmap = usecmap;
+
+ if (cmap->ncspace == 0)
+ {
+ cmap->ncspace = usecmap->ncspace;
+ for (i = 0; i < usecmap->ncspace; i++)
+ cmap->cspace[i] = usecmap->cspace[i];
+ }
+}
+
+void
+fz_setwmode(fz_cmap *cmap, int wmode)
+{
+ cmap->wmode = wmode;
+}
+
+int
+fz_getwmode(fz_cmap *cmap)
+{
+ return cmap->wmode;
+}
+
+fz_error *
+fz_addcodespacerange(fz_cmap *cmap, unsigned lo, unsigned hi, int n)
+{
+ int i;
+
+ if (cmap->ncspace + 1 == MAXCODESPACE)
+ return fz_throw("rangelimit: too many code space ranges");
+
+ cmap->cspace[cmap->ncspace].n = n;
+
+ for (i = 0; i < n; i++)
+ {
+ int o = (n - i - 1) * 8;
+ cmap->cspace[cmap->ncspace].lo[i] = (lo >> o) & 0xFF;
+ cmap->cspace[cmap->ncspace].hi[i] = (hi >> o) & 0xFF;
+ }
+
+ cmap->ncspace ++;
+
+ return nil;
+}
+
+fz_error *
+fz_addcidrange(fz_cmap *cmap, int low, int high, int offset)
+{
+ if (cmap->rlen + 1 > cmap->rcap)
+ {
+ fz_range *newranges;
+ int newcap = cmap->rcap == 0 ? 256 : cmap->rcap * 2;
+ newranges = fz_realloc(cmap->ranges, newcap * sizeof(fz_range));
+ if (!newranges)
+ return fz_outofmem;
+ cmap->rcap = newcap;
+ cmap->ranges = newranges;
+ }
+
+ cmap->ranges[cmap->rlen].low = low;
+ cmap->ranges[cmap->rlen].high = high;
+ cmap->ranges[cmap->rlen].flag = high - low == 0 ? SINGLE : RANGE;
+ cmap->ranges[cmap->rlen].offset = offset;
+ cmap->rlen ++;
+
+ return nil;
+}
+
+static fz_error *
+addlookup(fz_cmap *cmap, int value)
+{
+ if (cmap->tlen + 1 > cmap->tcap)
+ {
+ int newcap = cmap->tcap == 0 ? 256 : cmap->tcap * 2;
+ int *newlookup = fz_realloc(cmap->lookup, newcap * sizeof(int));
+ if (!newlookup)
+ return fz_outofmem;
+ cmap->tcap = newcap;
+ cmap->lookup = newlookup;
+ }
+
+ cmap->lookup[cmap->tlen++] = value;
+
+ return nil;
+}
+
+static int compare(const void *va, const void *vb)
+{
+ return ((const fz_range*)va)->low - ((const fz_range*)vb)->low;
+}
+
+fz_error *
+fz_endcidrange(fz_cmap *cmap)
+{
+ fz_error *err;
+ fz_range *newranges;
+ int *newlookup;
+ fz_range *a; /* last written range on output */
+ fz_range *b; /* current range examined on input */
+
+ qsort(cmap->ranges, cmap->rlen, sizeof(fz_range), compare);
+
+ a = cmap->ranges;
+ b = cmap->ranges + 1;
+
+ while (b < cmap->ranges + cmap->rlen)
+ {
+ /* input contiguous */
+ if (a->high + 1 == b->low)
+ {
+ /* output contiguous */
+ if (a->high - a->low + a->offset + 1 == b->offset)
+ {
+ /* SR -> R and SS -> R and RR -> R and RS -> R */
+ if (a->flag == SINGLE || a->flag == RANGE)
+ {
+ a->flag = RANGE;
+ a->high = b->high;
+ }
+
+ /* LS -> L */
+ else if (a->flag == LOOKUP && b->flag == SINGLE)
+ {
+ a->high = b->high;
+ err = addlookup(cmap, b->offset);
+ if (err)
+ return err;
+ }
+
+ /* LR -> LR */
+ else if (a->flag == LOOKUP && b->flag == RANGE)
+ {
+ *(++a) = *b;
+ }
+ }
+
+ /* output separated */
+ else
+ {
+ /* SS -> L */
+ if (a->flag == SINGLE && b->flag == SINGLE)
+ {
+ a->flag = LOOKUP;
+ a->high = b->high;
+
+ err = addlookup(cmap, a->offset);
+ if (err)
+ return err;
+
+ err = addlookup(cmap, b->offset);
+ if (err)
+ return err;
+
+ a->offset = cmap->tlen - 2;
+ }
+
+ /* LS -> L */
+ else if (a->flag == LOOKUP && b->flag == SINGLE)
+ {
+ a->high = b->high;
+ err = addlookup(cmap, b->offset);
+ if (err)
+ return err;
+ }
+
+ /* XX -> XX */
+ else
+ {
+ *(++a) = *b;
+ }
+ }
+ }
+
+ /* input separated: XX -> XX */
+ else
+ {
+ *(++a) = *b;
+ }
+
+ b ++;
+ }
+
+ cmap->rlen = a - cmap->ranges + 1;
+
+ assert(cmap->rlen > 0);
+
+ newranges = fz_realloc(cmap->ranges, cmap->rlen * sizeof(fz_range));
+ if (!newranges)
+ return fz_outofmem;
+ cmap->rcap = cmap->rlen;
+ cmap->ranges = newranges;
+
+ if (cmap->tlen)
+ {
+ newlookup = fz_realloc(cmap->lookup, cmap->tlen * sizeof(int));
+ if (!newlookup)
+ return fz_outofmem;
+ cmap->tcap = cmap->tlen;
+ cmap->lookup = newlookup;
+ }
+
+ return nil;
+}
+
+fz_error *
+fz_setcidlookup(fz_cmap *cmap, int map[256])
+{
+ int i;
+
+ cmap->rlen = cmap->rcap = 1;
+ cmap->ranges = fz_malloc(sizeof (fz_range));
+ if (!cmap->ranges) {
+ return fz_outofmem;
+ }
+
+ cmap->tlen = cmap->tcap = 256;
+ cmap->lookup = fz_malloc(sizeof (int) * 256);
+ if (!cmap->lookup) {
+ fz_free(cmap->ranges);
+ return fz_outofmem;
+ }
+
+ cmap->ranges[0].low = 0;
+ cmap->ranges[0].high = 255;
+ cmap->ranges[0].flag = LOOKUP;
+ cmap->ranges[0].offset = 0;
+
+ for (i = 0; i < 256; i++)
+ cmap->lookup[i] = map[i];
+
+ return nil;
+}
+
+int
+fz_lookupcid(fz_cmap *cmap, int cpt)
+{
+ int l = 0;
+ int r = cmap->rlen - 1;
+ int m;
+
+ while (l <= r)
+ {
+ m = (l + r) >> 1;
+ if (cpt < cmap->ranges[m].low)
+ r = m - 1;
+ else if (cpt > cmap->ranges[m].high)
+ l = m + 1;
+ else
+ {
+ int i = cpt - cmap->ranges[m].low + cmap->ranges[m].offset;
+ if (cmap->ranges[m].flag == LOOKUP)
+ return cmap->lookup[i];
+ return i;
+ }
+ }
+
+ if (cmap->usecmap)
+ return fz_lookupcid(cmap->usecmap, cpt);
+
+ return -1;
+}
+
+char *
+fz_decodecpt(fz_cmap *cmap, unsigned char *buf, int *cpt)
+{
+ int i, k;
+
+ for (k = 0; k < cmap->ncspace; k++)
+ {
+ unsigned char *lo = cmap->cspace[k].lo;
+ unsigned char *hi = cmap->cspace[k].hi;
+ int n = cmap->cspace[k].n;
+ int c = 0;
+
+ for (i = 0; i < n; i++)
+ {
+ if (lo[i] <= buf[i] && buf[i] <= hi[i])
+ c = (c << 8) | buf[i];
+ else
+ break;
+ }
+
+ if (i == n) {
+ *cpt = c;
+ return buf + n;
+ }
+ }
+
+ *cpt = 0;
+ return buf + 1;
+}
+
+void
+fz_debugcmap(fz_cmap *cmap)
+{
+ int i, k;
+
+ printf("cmap $%p /%s {\n", cmap, cmap->cmapname);
+
+ if (cmap->usecmapname[0])
+ printf(" usecmap /%s\n", cmap->usecmapname);
+ if (cmap->usecmap)
+ printf(" usecmap $%p\n", cmap->usecmap);
+
+ printf(" wmode %d\n", cmap->wmode);
+
+ printf(" codespaces {\n");
+ for (i = 0; i < cmap->ncspace; i++)
+ {
+ printf(" <");
+ for (k = 0; k < cmap->cspace[i].n; k++)
+ printf("%02x", cmap->cspace[i].lo[k]);
+ printf("> <");
+ for (k = 0; k < cmap->cspace[i].n; k++)
+ printf("%02x", cmap->cspace[i].hi[k]);
+ printf(">\n");
+ }
+ printf(" }\n");
+
+ printf(" ranges (%d,%d) {\n", cmap->rlen, cmap->tlen);
+ for (i = 0; i < cmap->rlen; i++)
+ {
+ fz_range *r = &cmap->ranges[i];
+ printf(" <%04x> <%04x> ", r->low, r->high);
+ if (r->flag == LOOKUP)
+ {
+ printf("[ ");
+ for (k = 0; k < r->high - r->low + 1; k++)
+ printf("%d ", cmap->lookup[r->offset + k]);
+ printf("]\n");
+ }
+ else
+ printf("%d\n", r->offset);
+ }
+ printf(" }\n}\n");
+}
+
diff --git a/tree/debug.c b/tree/debug.c
new file mode 100644
index 00000000..f5012c60
--- /dev/null
+++ b/tree/debug.c
@@ -0,0 +1,177 @@
+#include <fitz.h>
+
+static void indent(int level)
+{
+ while (level--)
+ putchar(' ');
+}
+
+static void lispnode(fz_node *node, int level);
+
+static void lispmeta(fz_meta *node, int level)
+{
+ fz_node *child;
+ indent(level);
+ printf("(meta ");
+ fz_fprintcobj(stdout, node->info);
+ printf("\n");
+ for (child = node->child; child; child = child->next)
+ lispnode(child, level + 1);
+ indent(level);
+ printf(")\n");
+}
+
+static void lispover(fz_over *node, int level)
+{
+ fz_node *child;
+ indent(level);
+ printf("(over\n");
+ for (child = node->child; child; child = child->next)
+ lispnode(child, level + 1);
+ indent(level);
+ printf(")\n");
+}
+
+static void lispmask(fz_mask *node, int level)
+{
+ fz_node *child;
+ indent(level);
+ printf("(mask\n");
+ for (child = node->child; child; child = child->next)
+ lispnode(child, level + 1);
+ indent(level);
+ printf(")\n");
+}
+
+static void lispblend(fz_blend *node, int level)
+{
+ fz_node *child;
+ indent(level);
+ printf("(blend-%d\n", node->mode);
+ for (child = node->child; child; child = child->next)
+ lispnode(child, level + 1);
+ indent(level);
+ printf(")\n");
+}
+
+static void lisptransform(fz_transform *node, int level)
+{
+ indent(level);
+ printf("(transform %g %g %g %g %g %g\n",
+ node->m.a, node->m.b,
+ node->m.c, node->m.d,
+ node->m.e, node->m.f);
+ lispnode(node->child, level + 1);
+ indent(level);
+ printf(")\n");
+}
+
+static void lispsolid(fz_solid *node, int level)
+{
+ indent(level);
+ printf("(solid %g %g %g)\n", node->r, node->g, node->b);
+}
+
+static void lispform(fz_form *node, int level)
+{
+ indent(level);
+ printf("(form %p)\n", node->tree);
+}
+
+static void lisppath(fz_path *node, int level)
+{
+ int i;
+
+ indent(level);
+
+ if (node->paint == FZ_STROKE)
+ {
+ printf("(path 'stroke %d %d %g %g ",
+ node->stroke->linecap,
+ node->stroke->linejoin,
+ node->stroke->linewidth,
+ node->stroke->miterlimit);
+ if (node->dash)
+ {
+ printf("%g '( ", node->dash->phase);
+ for (i = 0; i < node->dash->len; i++)
+ printf("%g ", node->dash->array[i]);
+ printf(")");
+ }
+ else
+ printf("0 '()");
+ }
+ else
+ {
+ printf("(path '%s", node->paint == FZ_FILL ? "fill" : "eofill");
+ }
+
+ printf("\n");
+ fz_debugpath(node);
+
+ indent(level);
+ printf(")\n");
+}
+
+static void lisptext(fz_text *node, int level)
+{
+ int i;
+
+ indent(level);
+ printf("(text %s [%g %g %g %g]\n", node->font->name,
+ node->trm.a, node->trm.b, node->trm.c, node->trm.d);
+
+ for (i = 0; i < node->len; i++)
+ {
+ indent(level + 1);
+ printf("(g %d %g %g)\n", node->els[i].g, node->els[i].x, node->els[i].y);
+ }
+
+ indent(level);
+ printf(")\n");
+}
+
+static void lispimage(fz_image *node, int level)
+{
+ indent(level);
+ printf("(image %d %d %d %d '", node->w, node->h, node->n, node->bpc);
+ switch (node->cs)
+ {
+ case FZ_CSGRAY: printf("gray"); break;
+ case FZ_CSRGB: printf("rgb"); break;
+ case FZ_CSCMYK: printf("cmyk"); break;
+ default: printf("unknown"); break;
+ }
+ printf(")\n");
+}
+
+static void lispnode(fz_node *node, int level)
+{
+ if (!node)
+ {
+ indent(level);
+ printf("(nil)\n");
+ return;
+ }
+
+ switch (node->kind)
+ {
+ case FZ_NMETA: lispmeta((fz_meta*)node, level); break;
+ case FZ_NOVER: lispover((fz_over*)node, level); break;
+ case FZ_NMASK: lispmask((fz_mask*)node, level); break;
+ case FZ_NBLEND: lispblend((fz_blend*)node, level); break;
+ case FZ_NTRANSFORM: lisptransform((fz_transform*)node, level); break;
+ case FZ_NSOLID: lispsolid((fz_solid*)node, level); break;
+ case FZ_NPATH: lisppath((fz_path*)node, level); break;
+ case FZ_NTEXT: lisptext((fz_text*)node, level); break;
+ case FZ_NIMAGE: lispimage((fz_image*)node, level); break;
+ case FZ_NFORM: lispform((fz_form*)node, level); break;
+ }
+}
+
+void
+fz_debugtree(fz_tree *tree)
+{
+ lispnode(tree->root, 0);
+}
+
diff --git a/tree/font.c b/tree/font.c
new file mode 100644
index 00000000..9ddbad04
--- /dev/null
+++ b/tree/font.c
@@ -0,0 +1,275 @@
+#include <fitz.h>
+
+void
+fz_initfont(fz_font *font, char *name)
+{
+ strlcpy(font->name, name, sizeof font->name);
+
+ font->wmode = 0;
+
+ font->bbox.min.x = 0;
+ font->bbox.min.y = 0;
+ font->bbox.max.x = 1000;
+ font->bbox.max.y = 1000;
+
+ font->hmtxcap = 0;
+ font->vmtxcap = 0;
+ font->nhmtx = 0;
+ font->nvmtx = 0;
+ font->hmtx = nil;
+ font->vmtx = nil;
+
+ font->dhmtx.c = 0x0000;
+ font->dhmtx.w = 0;
+
+ font->dvmtx.c = 0x0000;
+ font->dvmtx.x = 0;
+ font->dvmtx.y = 880;
+ font->dvmtx.w = -1000;
+}
+
+void
+fz_setfontwmode(fz_font *font, int wmode)
+{
+ font->wmode = wmode;
+}
+
+void
+fz_setfontbbox(fz_font *font, int xmin, int ymin, int xmax, int ymax)
+{
+ font->bbox.min.x = xmin;
+ font->bbox.min.y = ymin;
+ font->bbox.max.x = xmax;
+ font->bbox.max.y = ymax;
+}
+
+void
+fz_setdefaulthmtx(fz_font *font, int w)
+{
+ font->dhmtx.w = w;
+}
+
+void
+fz_setdefaultvmtx(fz_font *font, int y, int w)
+{
+ font->dvmtx.y = y;
+ font->dvmtx.w = w;
+}
+
+fz_error *
+fz_addhmtx(fz_font *font, int c, int w)
+{
+ int newcap;
+ fz_hmtx *newmtx;
+
+ if (font->nhmtx + 1 >= font->hmtxcap)
+ {
+ newcap = font->hmtxcap + 16;
+ newmtx = fz_realloc(font->hmtx, sizeof(fz_hmtx) * newcap);
+ if (!newmtx)
+ return fz_outofmem;
+ font->hmtxcap = newcap;
+ font->hmtx = newmtx;
+ }
+
+ font->hmtx[font->nhmtx].c = c;
+ font->hmtx[font->nhmtx].w = w;
+ font->nhmtx++;
+
+ return nil;
+}
+
+fz_error *
+fz_addvmtx(fz_font *font, int c, int x, int y, int w)
+{
+ int newcap;
+ fz_vmtx *newmtx;
+
+ if (font->nvmtx + 1 >= font->vmtxcap)
+ {
+ newcap = font->vmtxcap + 16;
+ newmtx = fz_realloc(font->vmtx, sizeof(fz_vmtx) * newcap);
+ if (!newmtx)
+ return fz_outofmem;
+ font->vmtxcap = newcap;
+ font->vmtx = newmtx;
+ }
+
+ font->vmtx[font->nvmtx].c = c;
+ font->vmtx[font->nvmtx].x = x;
+ font->vmtx[font->nvmtx].y = y;
+ font->vmtx[font->nvmtx].w = w;
+ font->nvmtx++;
+
+ return nil;
+}
+
+static int cmph(const void *a0, const void *b0)
+{
+ fz_hmtx *a = (fz_hmtx*)a0;
+ fz_hmtx *b = (fz_hmtx*)b0;
+ return a->c - b->c;
+}
+
+static int cmpv(const void *a0, const void *b0)
+{
+ fz_vmtx *a = (fz_vmtx*)a0;
+ fz_vmtx *b = (fz_vmtx*)b0;
+ return a->c - b->c;
+}
+
+static int uniq(void *ptr, int count, int size)
+{
+ char *a = ptr;
+ char *b = ((char*)ptr) + size;
+
+ while (count--)
+ {
+ if (memcmp(a, b, sizeof(short)) != 0)
+ {
+ a += size;
+ if (a != b)
+ memcpy(a, b, size);
+ }
+ b += size;
+ }
+
+ return (a - ((char*)ptr)) / size;
+}
+
+fz_error *
+fz_endhmtx(fz_font *font)
+{
+ fz_hmtx *newmtx;
+
+ if (!font->hmtx)
+ return nil;
+
+ qsort(font->hmtx, font->nhmtx, sizeof(fz_hmtx), cmph);
+ font->nhmtx = uniq(font->hmtx, font->nhmtx, sizeof(fz_hmtx));
+
+ newmtx = fz_realloc(font->hmtx, sizeof(fz_hmtx) * font->nhmtx);
+ if (!newmtx)
+ return fz_outofmem;
+ font->hmtxcap = font->nhmtx;
+ font->hmtx = newmtx;
+
+ return nil;
+}
+
+fz_error *
+fz_endvmtx(fz_font *font)
+{
+ fz_vmtx *newmtx;
+
+ if (!font->vmtx)
+ return nil;
+
+ qsort(font->vmtx, font->nvmtx, sizeof(fz_vmtx), cmpv);
+ font->nvmtx = uniq(font->vmtx, font->nvmtx, sizeof(fz_vmtx));
+
+ newmtx = fz_realloc(font->vmtx, sizeof(fz_vmtx) * font->nvmtx);
+ if (!newmtx)
+ return fz_outofmem;
+ font->vmtxcap = font->nvmtx;
+ font->vmtx = newmtx;
+
+ return nil;
+}
+
+fz_hmtx
+fz_gethmtx(fz_font *font, int gid)
+{
+ int l = 0;
+ int r = font->nhmtx;
+ int m;
+
+ if (!font->hmtx)
+ goto notfound;
+
+ while (l <= r)
+ {
+ m = (l + r) >> 1;
+ if (gid < font->hmtx[m].c)
+ r = m - 1;
+ else if (gid > font->hmtx[m].c)
+ l = m + 1;
+ else
+ return font->hmtx[m];
+ }
+
+notfound:
+ return font->dhmtx;
+}
+
+fz_vmtx
+fz_getvmtx(fz_font *font, int gid)
+{
+ fz_hmtx h;
+ fz_vmtx v;
+ int l = 0;
+ int r = font->nvmtx;
+ int m;
+
+ if (!font->vmtx)
+ goto notfound;
+
+ while (l <= r)
+ {
+ m = (l + r) >> 1;
+ if (gid < font->vmtx[m].c)
+ r = m - 1;
+ else if (gid > font->vmtx[m].c)
+ l = m + 1;
+ else
+ return font->vmtx[m];
+ }
+
+notfound:
+ h = fz_gethmtx(font, gid);
+ v = font->dvmtx;
+ v.x = h.w / 2;
+ return v;
+}
+
+void
+fz_freefont(fz_font *font)
+{
+ if (font->free)
+ font->free(font);
+ fz_free(font->hmtx);
+ fz_free(font->vmtx);
+ fz_free(font);
+}
+
+void
+fz_debugfont(fz_font *font)
+{
+ int i;
+
+ printf("font '%s' {\n", font->name);
+ printf(" wmode %d\n", font->wmode);
+ printf(" bbox [%d %d %d %d]\n",
+ font->bbox.min.x, font->bbox.min.y,
+ font->bbox.max.x, font->bbox.max.y);
+ printf(" DW %d\n", font->dhmtx.w);
+
+ printf(" W {\n");
+ for (i = 0; i < font->nhmtx; i++)
+ printf(" <%04x> %d\n",
+ font->hmtx[i].c, font->hmtx[i].w);
+ printf(" }\n");
+
+ if (font->wmode)
+ {
+ printf(" DW2 [%d %d]\n", font->dvmtx.y, font->dvmtx.w);
+ printf(" W2 {\n");
+ for (i = 0; i < font->nvmtx; i++)
+ printf(" <%04x> %d %d %d\n", font->vmtx[i].c,
+ font->vmtx[i].x, font->vmtx[i].y, font->vmtx[i].w);
+ printf(" }\n");
+ }
+
+ printf("}\n");
+}
+
diff --git a/tree/form.c b/tree/form.c
new file mode 100644
index 00000000..dd45aad9
--- /dev/null
+++ b/tree/form.c
@@ -0,0 +1,30 @@
+#include <fitz.h>
+
+fz_error *
+fz_newform(fz_node **nodep, fz_tree *child)
+{
+ fz_form *node;
+
+ node = fz_malloc(sizeof (fz_form));
+ if (!node)
+ return fz_outofmem;
+ *nodep = (fz_node*)node;
+
+ fz_initnode((fz_node*)node, FZ_NFORM);
+ node->tree = child;
+
+ return nil;
+}
+
+void
+fz_freeform(fz_form *node)
+{
+ fz_free(node);
+}
+
+fz_rect
+fz_boundform(fz_form *node, fz_matrix ctm)
+{
+ return fz_boundtree(node->tree, ctm);
+}
+
diff --git a/tree/image.c b/tree/image.c
new file mode 100644
index 00000000..7991ded3
--- /dev/null
+++ b/tree/image.c
@@ -0,0 +1,49 @@
+#include <fitz.h>
+
+fz_error *
+fz_newimage(fz_node **nodep, int w, int h, int n, int bpc, int cs)
+{
+ fz_image *node;
+
+ node = fz_malloc(sizeof (fz_image));
+ if (!node)
+ return fz_outofmem;
+ *nodep = (fz_node*)node;
+
+ fz_initnode((fz_node*)node, FZ_NIMAGE);
+ node->w = w;
+ node->h = h;
+ node->n = n;
+ node->bpc = bpc;
+ node->cs = cs;
+ node->data = nil;
+
+ return nil;
+}
+
+void
+fz_freeimage(fz_image *node)
+{
+ fz_free(node->data);
+ fz_free(node);
+}
+
+fz_rect
+fz_boundimage(fz_image *node, fz_matrix ctm)
+{
+ fz_point ll, lr, ul, ur;
+ fz_rect r;
+
+ ll = fz_transformpoint(ctm, (fz_point){0,0});
+ lr = fz_transformpoint(ctm, (fz_point){1,0});
+ ul = fz_transformpoint(ctm, (fz_point){0,1});
+ ur = fz_transformpoint(ctm, (fz_point){1,1});
+
+ r.min.x = MIN4(ll.x, lr.x, ul.x, ur.x);
+ r.min.y = MIN4(ll.y, lr.y, ul.y, ur.y);
+ r.max.x = MAX4(ll.x, lr.x, ul.x, ur.x);
+ r.max.y = MAX4(ll.y, lr.y, ul.y, ur.y);
+
+ return r;
+}
+
diff --git a/tree/mask.c b/tree/mask.c
new file mode 100644
index 00000000..62a469ff
--- /dev/null
+++ b/tree/mask.c
@@ -0,0 +1,50 @@
+#include <fitz.h>
+
+fz_error *
+fz_newmask(fz_node **nodep)
+{
+ fz_mask *node;
+
+ node = fz_malloc(sizeof (fz_mask));
+ if (!node)
+ return fz_outofmem;
+ *nodep = (fz_node*)node;
+
+ fz_initnode((fz_node*)node, FZ_NMASK);
+ node->child = nil;
+
+ return nil;
+}
+
+void
+fz_freemask(fz_mask *node)
+{
+ if (node->child)
+ fz_freenode(node->child);
+ fz_free(node);
+}
+
+fz_rect
+fz_boundmask(fz_mask* node, fz_matrix ctm)
+{
+ fz_node *child;
+ fz_rect bbox;
+ fz_rect r;
+
+ bbox = FZ_INFRECT;
+
+ for (child = node->child; child; child = child->next)
+ {
+ r = fz_boundnode(child, ctm);
+ if (r.max.x >= r.min.x)
+ {
+ if (bbox.max.x >= bbox.min.x)
+ bbox = fz_intersectrects(r, bbox);
+ else
+ bbox = r;
+ }
+ }
+
+ return bbox;
+}
+
diff --git a/tree/meta.c b/tree/meta.c
new file mode 100644
index 00000000..4d3a2a74
--- /dev/null
+++ b/tree/meta.c
@@ -0,0 +1,37 @@
+#include <fitz.h>
+
+fz_error *
+fz_newmeta(fz_node **nodep, fz_obj *info)
+{
+ fz_meta *node;
+
+ node = fz_malloc(sizeof (fz_meta));
+ if (!node)
+ return fz_outofmem;
+ *nodep = (fz_node*)node;
+
+ fz_initnode((fz_node*)node, FZ_NMETA);
+ node->info = fz_keepobj(info);
+ node->child = nil;
+
+ return nil;
+}
+
+void
+fz_freemeta(fz_meta *node)
+{
+ if (node->child)
+ fz_freenode(node->child);
+ if (node->info)
+ fz_dropobj(node->info);
+ fz_free(node);
+}
+
+fz_rect
+fz_boundmeta(fz_meta *node, fz_matrix ctm)
+{
+ if (!node->child)
+ return FZ_INFRECT;
+ return fz_boundnode(node->child, ctm);
+}
+
diff --git a/tree/node.c b/tree/node.c
new file mode 100644
index 00000000..8b9d48cc
--- /dev/null
+++ b/tree/node.c
@@ -0,0 +1,148 @@
+#include <fitz.h>
+
+void fz_freeover(fz_over* node);
+void fz_freemask(fz_mask* node);
+void fz_freeblend(fz_blend* node);
+void fz_freetransform(fz_transform* node);
+void fz_freeform(fz_form* node);
+void fz_freesolid(fz_solid* node);
+void fz_freepath(fz_path* node);
+void fz_freetext(fz_text* node);
+void fz_freeimage(fz_image* node);
+
+fz_rect fz_boundover(fz_over* node, fz_matrix ctm);
+fz_rect fz_boundmask(fz_mask* node, fz_matrix ctm);
+fz_rect fz_boundblend(fz_blend* node, fz_matrix ctm);
+fz_rect fz_boundtransform(fz_transform* node, fz_matrix ctm);
+fz_rect fz_boundform(fz_form* node, fz_matrix ctm);
+fz_rect fz_boundsolid(fz_solid* node, fz_matrix ctm);
+fz_rect fz_boundpath(fz_path* node, fz_matrix ctm);
+fz_rect fz_boundtext(fz_text* node, fz_matrix ctm);
+fz_rect fz_boundimage(fz_image* node, fz_matrix ctm);
+
+void
+fz_initnode(fz_node *node, fz_nodekind kind)
+{
+ node->kind = kind;
+ node->parent = nil;
+}
+
+void
+fz_freenode(fz_node *node)
+{
+ if (node->next)
+ fz_freenode(node->next);
+
+ switch (node->kind)
+ {
+ case FZ_NOVER:
+ fz_freeover((fz_over *) node);
+ break;
+ case FZ_NMASK:
+ fz_freemask((fz_mask *) node);
+ break;
+ case FZ_NBLEND:
+ fz_freeblend((fz_blend *) node);
+ break;
+ case FZ_NTRANSFORM:
+ fz_freetransform((fz_transform *) node);
+ break;
+ case FZ_NFORM:
+ fz_freeform((fz_form *) node);
+ break;
+ case FZ_NSOLID:
+ fz_freesolid((fz_solid *) node);
+ break;
+ case FZ_NPATH:
+ fz_freepath((fz_path *) node);
+ break;
+ case FZ_NTEXT:
+ fz_freetext((fz_text *) node);
+ break;
+ case FZ_NIMAGE:
+ fz_freeimage((fz_image *) node);
+ break;
+ }
+}
+
+fz_rect
+fz_boundnode(fz_node *node, fz_matrix ctm)
+{
+ switch (node->kind)
+ {
+ case FZ_NOVER:
+ return fz_boundover((fz_over *) node, ctm);
+ case FZ_NMASK:
+ return fz_boundmask((fz_mask *) node, ctm);
+ case FZ_NBLEND:
+ return fz_boundblend((fz_blend *) node, ctm);
+ case FZ_NTRANSFORM:
+ return fz_boundtransform((fz_transform *) node, ctm);
+ case FZ_NFORM:
+ return fz_boundform((fz_form *) node, ctm);
+ case FZ_NSOLID:
+ return fz_boundsolid((fz_solid *) node, ctm);
+ case FZ_NPATH:
+ return fz_boundpath((fz_path *) node, ctm);
+ case FZ_NTEXT:
+ return fz_boundtext((fz_text *) node, ctm);
+ case FZ_NIMAGE:
+ return fz_boundimage((fz_image *) node, ctm);
+ }
+ return FZ_INFRECT;
+}
+
+int
+fz_isover(fz_node *node)
+{
+ return node ? node->kind == FZ_NOVER : 0;
+}
+
+int
+fz_ismask(fz_node *node)
+{
+ return node ? node->kind == FZ_NMASK : 0;
+}
+
+int
+fz_isblend(fz_node *node)
+{
+ return node ? node->kind == FZ_NBLEND : 0;
+}
+
+int
+fz_istransform(fz_node *node)
+{
+ return node ? node->kind == FZ_NTRANSFORM : 0;
+}
+
+int
+fz_isform(fz_node *node)
+{
+ return node ? node->kind == FZ_NFORM : 0;
+}
+
+int
+fz_issolid(fz_node *node)
+{
+ return node ? node->kind == FZ_NSOLID : 0;
+}
+
+int
+fz_ispath(fz_node *node)
+{
+ return node ? node->kind == FZ_NPATH : 0;
+}
+
+int
+fz_istext(fz_node *node)
+{
+ return node ? node->kind == FZ_NTEXT : 0;
+}
+
+int
+fz_isimage(fz_node *node)
+{
+ return node ? node->kind == FZ_NIMAGE : 0;
+}
+
diff --git a/tree/over.c b/tree/over.c
new file mode 100644
index 00000000..d55bb297
--- /dev/null
+++ b/tree/over.c
@@ -0,0 +1,50 @@
+#include <fitz.h>
+
+fz_error *
+fz_newover(fz_node **nodep)
+{
+ fz_over *node;
+
+ node = fz_malloc(sizeof (fz_over));
+ if (!node)
+ return fz_outofmem;
+ *nodep = (fz_node*)node;
+
+ fz_initnode((fz_node*)node, FZ_NOVER);
+ node->child = nil;
+
+ return nil;
+}
+
+void
+fz_freeover(fz_over *node)
+{
+ if (node->child)
+ fz_freenode(node->child);
+ fz_free(node);
+}
+
+fz_rect
+fz_boundover(fz_over* node, fz_matrix ctm)
+{
+ fz_node *child;
+ fz_rect bbox;
+ fz_rect r;
+
+ bbox = FZ_INFRECT;
+
+ for (child = node->child; child; child = child->next)
+ {
+ r = fz_boundnode(child, ctm);
+ if (r.max.x >= r.min.x)
+ {
+ if (bbox.max.x >= bbox.min.x)
+ bbox = fz_mergerects(r, bbox);
+ else
+ bbox = r;
+ }
+ }
+
+ return bbox;
+}
+
diff --git a/tree/path.c b/tree/path.c
new file mode 100644
index 00000000..d21fda56
--- /dev/null
+++ b/tree/path.c
@@ -0,0 +1,291 @@
+#include <fitz.h>
+
+fz_error *
+fz_newpath(fz_path **pathp)
+{
+ fz_path *path;
+
+ path = *pathp = fz_malloc(sizeof(fz_path));
+ if (!path)
+ return fz_outofmem;
+
+ fz_initnode((fz_node*)path, FZ_NPATH);
+
+ path->paint = FZ_FILL;
+ path->stroke = nil;
+ path->dash = nil;
+ path->len = 0;
+ path->cap = 0;
+ path->els = nil;
+
+ return nil;
+}
+
+fz_error *
+fz_clonepath(fz_path **pathp, fz_path *oldpath)
+{
+ fz_path *path;
+
+ path = *pathp = fz_malloc(sizeof(fz_path));
+ if (!path)
+ return fz_outofmem;
+
+ fz_initnode((fz_node*)path, FZ_NPATH);
+
+ path->paint = FZ_FILL;
+ path->stroke = nil;
+ path->dash = nil;
+ path->len = oldpath->len;
+ path->cap = oldpath->len;
+
+ path->els = fz_malloc(sizeof (fz_pathel) * path->len);
+ if (!path->els) {
+ fz_free(path);
+ return fz_outofmem;
+ }
+ memcpy(path->els, oldpath->els, sizeof(fz_pathel) * path->len);
+
+ return nil;
+}
+
+void
+fz_freepath(fz_path *node)
+{
+ fz_free(node->stroke);
+ fz_free(node->dash);
+ fz_free(node->els);
+ fz_free(node);
+}
+
+static fz_error *
+growpath(fz_path *path, int n)
+{
+ int newcap;
+ fz_pathel *newels;
+
+ while (path->len + n > path->cap)
+ {
+ newcap = path->cap + 36;
+ newels = fz_realloc(path->els, sizeof (fz_pathel) * newcap);
+ if (!newels)
+ return fz_outofmem;
+ path->cap = newcap;
+ path->els = newels;
+ }
+
+ return nil;
+}
+
+fz_error *
+fz_moveto(fz_path *path, float x, float y)
+{
+ if (growpath(path, 3) != nil)
+ return fz_outofmem;
+ path->els[path->len++].k = FZ_MOVETO;
+ path->els[path->len++].v = x;
+ path->els[path->len++].v = y;
+ return nil;
+}
+
+fz_error *
+fz_lineto(fz_path *path, float x, float y)
+{
+ if (growpath(path, 3) != nil)
+ return fz_outofmem;
+ path->els[path->len++].k = FZ_LINETO;
+ path->els[path->len++].v = x;
+ path->els[path->len++].v = y;
+ return nil;
+}
+
+fz_error *
+fz_curveto(fz_path *path,
+ float x1, float y1,
+ float x2, float y2,
+ float x3, float y3)
+{
+ if (growpath(path, 7) != nil)
+ return fz_outofmem;
+ path->els[path->len++].k = FZ_CURVETO;
+ path->els[path->len++].v = x1;
+ path->els[path->len++].v = y1;
+ path->els[path->len++].v = x2;
+ path->els[path->len++].v = y2;
+ path->els[path->len++].v = x3;
+ path->els[path->len++].v = y3;
+ return nil;
+}
+
+fz_error *
+fz_curvetov(fz_path *path, float x2, float y2, float x3, float y3)
+{
+ float x1 = path->els[path->len-2].v;
+ float y1 = path->els[path->len-1].v;
+ return fz_curveto(path, x1, y1, x2, y2, x3, y3);
+}
+
+fz_error *
+fz_curvetoy(fz_path *path, float x1, float y1, float x3, float y3)
+{
+ return fz_curveto(path, x1, y1, x3, y3, x3, y3);
+}
+
+fz_error *
+fz_closepath(fz_path *path)
+{
+ if (growpath(path, 1) != nil)
+ return fz_outofmem;
+ path->els[path->len++].k = FZ_CLOSEPATH;
+ return nil;
+}
+
+fz_error *
+fz_endpath(fz_path *path, fz_pathkind paint, fz_stroke *stroke, fz_dash *dash)
+{
+ fz_pathel *newels;
+
+ newels = fz_realloc(path->els, path->len * sizeof(fz_pathel));
+ if (!newels)
+ return fz_outofmem;
+ path->els = newels;
+
+ path->paint = paint;
+ path->stroke = stroke;
+ path->dash = dash;
+
+ return nil;
+}
+
+static inline fz_rect boundexpand(fz_rect r, fz_point p)
+{
+ if (r.min.x > r.max.x)
+ {
+ r.min.x = r.max.x = p.x;
+ r.min.y = r.max.y = p.y;
+ }
+ else
+ {
+ if (p.x < r.min.x) r.min.x = p.x;
+ if (p.y < r.min.y) r.min.y = p.y;
+ if (p.x > r.max.x) r.max.x = p.x;
+ if (p.y > r.max.y) r.max.y = p.y;
+ }
+ return r;
+}
+
+fz_rect
+fz_boundpath(fz_path *path, fz_matrix ctm)
+{
+ fz_point p;
+ fz_rect r = FZ_INFRECT;
+ int i = 0;
+
+ while (i < path->len)
+ {
+ switch (path->els[i++].k)
+ {
+ case FZ_CURVETO:
+ p.x = path->els[i++].v;
+ p.y = path->els[i++].v;
+ r = boundexpand(r, fz_transformpoint(ctm, p));
+ p.x = path->els[i++].v;
+ p.y = path->els[i++].v;
+ r = boundexpand(r, fz_transformpoint(ctm, p));
+ case FZ_MOVETO:
+ case FZ_LINETO:
+ p.x = path->els[i++].v;
+ p.y = path->els[i++].v;
+ r = boundexpand(r, fz_transformpoint(ctm, p));
+ break;
+ case FZ_CLOSEPATH:
+ break;
+ }
+ }
+
+ if (path->paint == FZ_STROKE)
+ {
+ float miterlength = sin(path->stroke->miterlimit / 2.0);
+ float linewidth = path->stroke->linewidth;
+ float expand = MAX(miterlength, linewidth) / 2.0;
+ r.min.x -= expand;
+ r.min.y -= expand;
+ r.max.x += expand;
+ r.max.y += expand;
+ }
+
+ return r;
+}
+
+void
+fz_debugpath(fz_path *path)
+{
+ float x, y;
+ int i = 0;
+ while (i < path->len)
+ {
+ switch (path->els[i++].k)
+ {
+ case FZ_MOVETO:
+ x = path->els[i++].v;
+ y = path->els[i++].v;
+ printf("%g %g m\n", x, y);
+ break;
+ case FZ_LINETO:
+ x = path->els[i++].v;
+ y = path->els[i++].v;
+ printf("%g %g l\n", x, y);
+ break;
+ case FZ_CURVETO:
+ x = path->els[i++].v;
+ y = path->els[i++].v;
+ printf("%g %g ", x, y);
+ x = path->els[i++].v;
+ y = path->els[i++].v;
+ printf("%g %g ", x, y);
+ x = path->els[i++].v;
+ y = path->els[i++].v;
+ printf("%g %g c\n", x, y);
+ break;
+ case FZ_CLOSEPATH:
+ printf("h\n");
+ }
+ }
+
+ switch (path->paint)
+ {
+ case FZ_STROKE:
+ printf("S\n");
+ break;
+ case FZ_FILL:
+ printf("f\n");
+ break;
+ case FZ_EOFILL:
+ printf("f*\n");
+ break;
+ }
+}
+
+fz_error *
+fz_newdash(fz_dash **dashp, float phase, int len, float *array)
+{
+ fz_dash *dash;
+ int i;
+
+ dash = *dashp = fz_malloc(sizeof(fz_dash) + sizeof(float) * len);
+ if (!dash)
+ return fz_outofmem;
+
+ dash->len = len;
+ dash->phase = phase;
+ for (i = 0; i < len; i++)
+ dash->array[i] = array[i];
+
+ return nil;
+}
+
+void
+fz_freedash(fz_dash *dash)
+{
+ fz_free(dash);
+}
+
diff --git a/tree/solid.c b/tree/solid.c
new file mode 100644
index 00000000..37ff55eb
--- /dev/null
+++ b/tree/solid.c
@@ -0,0 +1,33 @@
+#include <fitz.h>
+
+fz_error *
+fz_newsolid(fz_node **nodep, float r, float g, float b)
+{
+ fz_solid *node;
+
+ node = fz_malloc(sizeof (fz_solid));
+ if (!node)
+ return fz_outofmem;
+ *nodep = (fz_node*)node;
+
+ fz_initnode((fz_node*)node, FZ_NSOLID);
+ node->r = r;
+ node->g = g;
+ node->b = b;
+
+ return nil;
+}
+
+void
+fz_freesolid(fz_solid *node)
+{
+ fz_free(node);
+}
+
+fz_rect
+fz_boundsolid(fz_solid *node, fz_matrix ctm)
+{
+ /* min > max => no bounds */
+ return (fz_rect) { {1,1}, {-1,-1} };
+}
+
diff --git a/tree/text.c b/tree/text.c
new file mode 100644
index 00000000..312e3bde
--- /dev/null
+++ b/tree/text.c
@@ -0,0 +1,67 @@
+#include <fitz.h>
+
+fz_error *
+fz_newtext(fz_text **textp, fz_font *font)
+{
+ fz_text *text;
+
+ text = *textp = fz_malloc(sizeof(fz_text));
+ if (!text)
+ return fz_outofmem;
+
+ fz_initnode((fz_node*)text, FZ_NTEXT);
+
+ text->font = font;
+ text->trm = fz_identity();
+ text->len = 0;
+ text->cap = 0;
+ text->els = nil;
+
+ return nil;
+}
+
+void
+fz_freetext(fz_text *text)
+{
+ fz_free(text->els);
+ fz_free(text);
+};
+
+fz_rect
+fz_boundtext(fz_text *text, fz_matrix ctm)
+{
+ /* fz_rect bounds = fz_boundglyph(text->font, text->els[0], ctm); */
+ return FZ_INFRECT;
+}
+
+static fz_error *
+growtext(fz_text *text, int n)
+{
+ int newcap;
+ fz_textel *newels;
+
+ while (text->len + n > text->cap)
+ {
+ newcap = text->cap + 36;
+ newels = fz_realloc(text->els, sizeof (fz_textel) * newcap);
+ if (!newels)
+ return fz_outofmem;
+ text->cap = newcap;
+ text->els = newels;
+ }
+
+ return nil;
+}
+
+fz_error *
+fz_addtext(fz_text *text, int g, float x, float y)
+{
+ if (growtext(text, 1) != nil)
+ return fz_outofmem;
+ text->els[text->len].g = g;
+ text->els[text->len].x = x;
+ text->els[text->len].y = y;
+ text->len++;
+ return nil;
+}
+
diff --git a/tree/transform.c b/tree/transform.c
new file mode 100644
index 00000000..18910d6b
--- /dev/null
+++ b/tree/transform.c
@@ -0,0 +1,35 @@
+#include <fitz.h>
+
+fz_error *
+fz_newtransform(fz_node **nodep, fz_matrix m)
+{
+ fz_transform *node;
+
+ node = fz_malloc(sizeof (fz_transform));
+ if (!node)
+ return fz_outofmem;
+ *nodep = (fz_node*)node;
+
+ fz_initnode((fz_node*)node, FZ_NTRANSFORM);
+ node->child = nil;
+ node->m = m;
+
+ return nil;
+}
+
+void
+fz_freetransform(fz_transform *node)
+{
+ if (node->child)
+ fz_freenode(node->child);
+ fz_free(node);
+}
+
+fz_rect
+fz_boundtransform(fz_transform *node, fz_matrix ctm)
+{
+ if (!node->child)
+ return FZ_INFRECT;
+ return fz_boundnode(node->child, fz_concat(node->m, ctm));
+}
+
diff --git a/tree/tree.c b/tree/tree.c
new file mode 100644
index 00000000..1a96ec38
--- /dev/null
+++ b/tree/tree.c
@@ -0,0 +1,63 @@
+#include <fitz.h>
+
+fz_error *
+fz_newtree(fz_tree **treep)
+{
+ fz_tree *tree;
+
+ tree = *treep = fz_malloc(sizeof (fz_tree));
+ if (!tree)
+ return fz_outofmem;
+
+ tree->root = nil;
+ tree->head = nil;
+
+ return nil;
+}
+
+void
+fz_freetree(fz_tree *tree)
+{
+ if (tree->root)
+ fz_freenode(tree->root);
+ fz_free(tree);
+}
+
+fz_rect
+fz_boundtree(fz_tree *tree, fz_matrix ctm)
+{
+ if (tree->root)
+ return fz_boundnode(tree->root, ctm);
+ return FZ_INFRECT;
+}
+
+void
+fz_insertnode(fz_node *node, fz_node *child)
+{
+ child->parent = node;
+
+ if (fz_isover(node))
+ {
+ child->next = ((fz_over*)node)->child;
+ ((fz_over*)node)->child = child;
+ }
+
+ if (fz_ismask(node))
+ {
+ child->next = ((fz_mask*)node)->child;
+ ((fz_mask*)node)->child = child;
+ }
+
+ if (fz_isblend(node))
+ {
+ child->next = ((fz_blend*)node)->child;
+ ((fz_blend*)node)->child = child;
+ }
+
+ if (fz_istransform(node))
+ {
+ child->next = ((fz_transform*)node)->child;
+ ((fz_transform*)node)->child = child;
+ }
+}
+
diff --git a/util/cleanname.c b/util/cleanname.c
new file mode 100644
index 00000000..e8a8ed86
--- /dev/null
+++ b/util/cleanname.c
@@ -0,0 +1,63 @@
+/*
+ * In place, rewrite name to compress multiple /, eliminate ., and process ..
+ */
+
+#define SEP(x) ((x)=='/' || (x) == 0)
+
+char *
+cleanname(char *name)
+{
+ char *p, *q, *dotdot;
+ int rooted;
+
+ rooted = name[0] == '/';
+
+ /*
+ * invariants:
+ * p points at beginning of path element we're considering.
+ * q points just past the last path element we wrote (no slash).
+ * dotdot points just past the point where .. cannot backtrack
+ * any further (no slash).
+ */
+
+ p = q = dotdot = name + rooted;
+
+ while (*p)
+ {
+ if (p[0] == '/') /* null element */
+ p++;
+
+ else if (p[0] == '.' && SEP(p[1]))
+ p += 1; /* don't count the separator in case it is nul */
+
+ else if (p[0] == '.' && p[1] == '.' && SEP(p[2]))
+ {
+ p += 2;
+ if (q > dotdot) { /* can backtrack */
+ while (--q > dotdot && *q != '/')
+ ;
+ } else if (!rooted) { /* /.. is / but ./../ is .. */
+ if (q != name)
+ *q++ = '/';
+ *q++ = '.';
+ *q++ = '.';
+ dotdot = q;
+ }
+ }
+
+ else { /* real path element */
+ if (q != name + rooted)
+ *q++ = '/';
+ while ((*q = *p) != '/' && *q != 0)
+ p++, q++;
+ }
+ }
+
+ if (q == name) /* empty string is really ``.'' */
+ *q++ = '.';
+
+ *q = '\0';
+
+ return name;
+}
+
diff --git a/util/getopt.c b/util/getopt.c
new file mode 100644
index 00000000..c870e206
--- /dev/null
+++ b/util/getopt.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)getopt.c 4.13 (Berkeley) 2/23/91";
+#endif /* LIBC_SCCS and not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * get option letter from argument vector
+ */
+int opterr = 1, /* if error message should be printed */
+ optind = 1, /* index into parent argv vector */
+ optopt; /* character checked for validity */
+char *optarg; /* argument associated with option */
+
+#define BADCH (int)'?'
+#define EMSG ""
+
+int getopt(int nargc, char * const * nargv, const char *ostr)
+{
+ static char *place = EMSG; /* option letter processing */
+ register char *oli; /* option letter list index */
+ char *p;
+
+ if (!*place) { /* update scanning pointer */
+ if (optind >= nargc || *(place = nargv[optind]) != '-') {
+ place = EMSG;
+ return(EOF);
+ }
+ if (place[1] && *++place == '-') { /* found "--" */
+ ++optind;
+ place = EMSG;
+ return(EOF);
+ }
+ } /* option letter okay? */
+ if ((optopt = (int)*place++) == (int)':' ||
+ !(oli = strchr(ostr, optopt))) {
+ /*
+ * if the user didn't specify '-' as an option,
+ * assume it means EOF.
+ */
+ if (optopt == (int)'-')
+ return(EOF);
+ if (!*place)
+ ++optind;
+ if (opterr) {
+ if (!(p = strrchr(*nargv, '/')))
+ p = *nargv;
+ else
+ ++p;
+ (void)fprintf(stderr, "%s: illegal option -- %c\n",
+ p, optopt);
+ }
+ return(BADCH);
+ }
+ if (*++oli != ':') { /* don't need argument */
+ optarg = NULL;
+ if (!*place)
+ ++optind;
+ }
+ else { /* need an argument */
+ if (*place) /* no white space */
+ optarg = place;
+ else if (nargc <= ++optind) { /* no arg */
+ place = EMSG;
+ if (!(p = strrchr(*nargv, '/')))
+ p = *nargv;
+ else
+ ++p;
+ if (opterr)
+ (void)fprintf(stderr,
+ "%s: option requires an argument -- %c\n",
+ p, optopt);
+ return(BADCH);
+ }
+ else /* white space */
+ optarg = nargv[optind];
+ place = EMSG;
+ ++optind;
+ }
+ return(optopt); /* dump back option letter */
+}
diff --git a/util/strlcat.c b/util/strlcat.c
new file mode 100644
index 00000000..5f260abb
--- /dev/null
+++ b/util/strlcat.c
@@ -0,0 +1,34 @@
+/* Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left). At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+
+#include <string.h>
+
+int strlcat(char *dst, const char *src, int siz)
+{
+ register char *d = dst;
+ register const char *s = src;
+ register int n = siz;
+ int dlen;
+
+ /* Find the end of dst and adjust bytes left but don't go past end */
+ while (*d != '\0' && n-- != 0)
+ d++;
+ dlen = d - dst;
+ n = siz - dlen;
+
+ if (n == 0)
+ return dlen + strlen(s);
+ while (*s != '\0') {
+ if (n != 1) {
+ *d++ = *s;
+ n--;
+ }
+ s++;
+ }
+ *d = '\0';
+
+ return dlen + (s - src); /* count does not include NUL */
+}
diff --git a/util/strlcpy.c b/util/strlcpy.c
new file mode 100644
index 00000000..73416ae1
--- /dev/null
+++ b/util/strlcpy.c
@@ -0,0 +1,32 @@
+/* Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+
+#include <string.h>
+
+int strlcpy(char *dst, const char *src, int siz)
+{
+ register char *d = dst;
+ register const char *s = src;
+ register int n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0 && --n != 0) {
+ do {
+ if ((*d++ = *s++) == 0)
+ break;
+ } while (--n != 0);
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}
+
diff --git a/util/strsep.c b/util/strsep.c
new file mode 100644
index 00000000..e54903ce
--- /dev/null
+++ b/util/strsep.c
@@ -0,0 +1,11 @@
+#include <string.h>
+
+char *strsep(char **stringp, const char *delim)
+{
+ char *ret = *stringp;
+ if (ret == NULL) return NULL;
+ if ((*stringp = strpbrk(*stringp, delim)) != NULL)
+ *((*stringp)++) = '\0';
+ return ret;
+}
+