summaryrefslogtreecommitdiff
path: root/fitz
diff options
context:
space:
mode:
Diffstat (limited to 'fitz')
-rw-r--r--fitz/base_cpudep.c221
-rw-r--r--fitz/base_error.c78
-rw-r--r--fitz/base_hash.c274
-rw-r--r--fitz/base_matrix.c151
-rw-r--r--fitz/base_memory.c81
-rw-r--r--fitz/base_rect.c75
-rw-r--r--fitz/base_rune.c168
-rw-r--r--fitz/node_debug.c191
-rw-r--r--fitz/node_misc1.c167
-rw-r--r--fitz/node_misc2.c338
-rw-r--r--fitz/node_optimize.c310
-rw-r--r--fitz/node_path.c305
-rw-r--r--fitz/node_text.c141
-rw-r--r--fitz/node_tree.c107
-rw-r--r--fitz/res_cmap.c466
-rw-r--r--fitz/res_colorspace.c89
-rw-r--r--fitz/res_font.c269
-rw-r--r--fitz/res_image.c22
-rw-r--r--fitz/res_shade.c28
-rw-r--r--fitz/util_getopt.c116
-rw-r--r--fitz/util_strlcat.c35
-rw-r--r--fitz/util_strlcpy.c32
-rw-r--r--fitz/util_strsep.c11
23 files changed, 3675 insertions, 0 deletions
diff --git a/fitz/base_cpudep.c b/fitz/base_cpudep.c
new file mode 100644
index 00000000..0db2e331
--- /dev/null
+++ b/fitz/base_cpudep.c
@@ -0,0 +1,221 @@
+/*
+run-time cpu feature detection code
+mm, alphabet soup...
+
+Glenn Kennard <d98gk@efd.lth.se>
+*/
+
+#include <fitz.h>
+
+/* global run-time constant */
+unsigned fz_cpuflags = 0;
+
+#ifndef HAVE_CPUDEP
+
+void fz_accelerate(void)
+{
+}
+
+void fz_cpudetect(void)
+{
+}
+
+#else
+
+#include <signal.h> /* signal/sigaction */
+#include <setjmp.h> /* sigsetjmp/siglongjmp */
+
+#ifdef WIN32
+#define sigjmp_buf jmp_buf
+#define sigsetjmp(a,b) setjmp(a)
+#define siglongjmp longjmp
+#endif
+
+typedef struct {
+ void (*test)(void);
+ const unsigned flag;
+ const char *name;
+} featuretest;
+
+
+#if defined(ARCH_X86) || defined(ARCH_X86_64)
+
+/* need emms?? */
+static void mmx(void)
+{ __asm__ ("pand %mm0, %mm0\n\t"); }
+
+static void m3dnow(void)
+{ __asm__ ("pavgusb %mm0, %mm0\n\t"); }
+
+static void mmxext(void) /* aka Extended 3DNow! */
+{ __asm__ ("pmaxsw %mm0, %mm0\n\t"); }
+
+static void sse(void)
+{ __asm__ ("andps %xmm0, %xmm0\n\t"); }
+
+static void sse2(void)
+{ __asm__ ("andpd %xmm0, %xmm0\n\t"); }
+
+/* static void sse3(void) */
+/* { __asm__ ("haddps %%xmm0, %%xmm0\n\t" : : : "%xmm0"); } */
+
+#ifdef ARCH_X86_64
+static void amd64(void)
+{ __asm__ ("and %rax, %rax\n\t"); }
+#endif
+
+
+static const featuretest features[] = {
+ { mmx, HAVE_MMX, "mmx" },
+ { m3dnow, HAVE_3DNOW, "3dnow" },
+ { mmxext, HAVE_MMXEXT, "mmxext" },
+ { sse, HAVE_SSE, "sse" },
+ { sse2, HAVE_SSE2, "sse2" },
+/* { sse3, HAVE_SSE3, "sse3" }, */
+#ifdef ARCH_X86_64
+ { amd64, HAVE_AMD64, "amd64" }
+#endif
+};
+
+#endif
+
+
+#if defined(ARCH_SPARC)
+/* assembler must have -xarch=v8plusa passed to it (v9a for 64 bit binaries) */
+static void vis(void)
+{ __asm__ ("fand %f8, %f8, %f8\n\t"); }
+
+static const featuretest features[] = {
+ { vis, HAVE_VIS, "vis" }
+};
+
+#endif
+
+
+#if defined(ARCH_PPC)
+
+static void altivec(void)
+{ __asm__ ("vand v0, v0, v0\n\t"); }
+
+
+static const featuretest features[] = {
+ { altivec, HAVE_ALTIVEC, "altivec" },
+};
+
+#endif
+
+static sigjmp_buf jmpbuf;
+static volatile sig_atomic_t canjump;
+
+static void
+sigillhandler(int sig)
+{
+ if (!canjump) {
+ signal(sig, SIG_DFL);
+ raise(sig);
+ }
+
+ canjump = 0;
+ siglongjmp(jmpbuf, 1);
+}
+
+static int
+enabled(char *env, const char *ext)
+{
+ int len;
+ char *s;
+ if (!env)
+ return 1;
+ len = strlen(ext);
+ while ((s = strstr(env, ext)))
+ {
+ s += len;
+ if (*s == ' ' || *s == ',' || *s == '\0')
+ return 1;
+ }
+ return 0;
+}
+
+static void
+dumpflags(void)
+{
+ unsigned f = fz_cpuflags;
+ int i, n;
+
+ fputs("detected cpu features:", stdout);
+ n = 0;
+ for (i = 0; i < sizeof(features) / sizeof(featuretest); i++)
+ {
+ if (f & features[i].flag)
+ {
+ fputc(' ', stdout);
+ fputs(features[i].name, stdout);
+ n ++;
+ }
+ }
+ if (!n)
+ fputs(" none", stdout);
+ fputc('\n', stdout);
+}
+
+void fz_cpudetect(void)
+{
+ static int hasrun = 0;
+
+ unsigned flags = 0;
+ int i;
+ void (*oldhandler)(int) = NULL;
+ void (*tmphandler)(int);
+ char *env;
+
+ if (hasrun)
+ return;
+ hasrun = 1;
+
+ env = getenv("CPUACCEL");
+
+ for (i = 0; i < sizeof(features) / sizeof(featuretest); i++)
+ {
+ canjump = 0;
+
+ tmphandler = signal(SIGILL, sigillhandler);
+ if (!oldhandler)
+ oldhandler = tmphandler;
+
+ if (sigsetjmp(jmpbuf, 1))
+ {
+ /* test failed - disable feature */
+ flags &= ~features[i].flag;
+ continue;
+ }
+
+ canjump = 1;
+
+ features[i].test();
+
+ /* if we got here the test succeeded */
+ if (enabled(env, features[i].name))
+ flags |= features[i].flag;
+ else
+ flags &= ~features[i].flag;
+ }
+
+ /* restore previous signal handler */
+ signal(SIGILL, oldhandler);
+
+ fz_cpuflags = flags;
+
+#if defined(ARCH_X86) || defined(ARCH_X86_64)
+ __asm__ __volatile__ ("emms\n\t");
+#endif
+
+ dumpflags();
+}
+
+static __attribute__((constructor, used)) void fzcpudetect(void)
+{
+ fz_cpudetect();
+}
+
+#endif
+
diff --git a/fitz/base_error.c b/fitz/base_error.c
new file mode 100644
index 00000000..926c48dc
--- /dev/null
+++ b/fitz/base_error.c
@@ -0,0 +1,78 @@
+#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_throw1(char *fmt, ...)
+{
+ va_list ap;
+ fz_error *eo;
+
+ eo = fz_malloc(sizeof(fz_error));
+ if (!eo) return fz_outofmem;
+
+ eo->refs = 1;
+ strlcpy(eo->func, "unknown", sizeof eo->func);
+ strlcpy(eo->file, "unknown", sizeof eo->file);
+ eo->line = 0;
+
+ va_start(ap, fmt);
+ vsnprintf(eo->msg, sizeof eo->msg, fmt, ap);
+ eo->msg[sizeof(eo->msg) - 1] = '\0';
+ va_end(ap);
+
+ return eo;
+}
+
+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;
+
+ eo->refs = 1;
+ 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);
+
+ if (getenv("BOMB"))
+ fz_abort(eo);
+
+ return eo;
+}
+
+void
+fz_droperror(fz_error *eo)
+{
+ if (eo->refs > 0)
+ eo->refs--;
+ if (eo->refs == 0)
+ 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/fitz/base_hash.c b/fitz/base_hash.c
new file mode 100644
index 00000000..65bc7130
--- /dev/null
+++ b/fitz/base_hash.c
@@ -0,0 +1,274 @@
+/* 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
+{
+ int keylen;
+ int size;
+ int load;
+ fz_hashentry *ents;
+};
+
+static unsigned hash(unsigned char *s, int len)
+{
+ unsigned hash = 0;
+ int 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;
+}
+
+void
+fz_emptyhash(fz_hashtable *table)
+{
+ table->load = 0;
+ memset(table->ents, 0, sizeof(fz_hashentry) * table->size);
+}
+
+int
+fz_hashlen(fz_hashtable *table)
+{
+ return table->size;
+}
+
+void *
+fz_hashgetkey(fz_hashtable *table, int idx)
+{
+ return table->ents[idx].key;
+}
+
+void *
+fz_hashgetval(fz_hashtable *table, int idx)
+{
+ return table->ents[idx].val;
+}
+
+void
+fz_drophash(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;
+ int oldload;
+ int oldsize;
+ int 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_malloc(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;
+ unsigned size;
+ unsigned pos;
+
+ if (table->load > table->size * 8 / 10)
+ {
+ error = fz_resizehash(table, table->size * 2);
+ if (error)
+ return error;
+ }
+
+ ents = table->ents;
+ size = table->size;
+ pos = hash(key, table->keylen) % size;
+
+ 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/fitz/base_matrix.c b/fitz/base_matrix.c
new file mode 100644
index 00000000..7d7097c4
--- /dev/null
+++ b/fitz/base_matrix.c
@@ -0,0 +1,151 @@
+#include <fitz.h>
+
+void fz_invert3x3(float *dst, float *m)
+{
+ float det;
+ int i;
+
+#define M3(m,i,j) (m)[3*i+j]
+#define D2(a,b,c,d) (a * d - b * c)
+#define D3(a1,a2,a3,b1,b2,b3,c1,c2,c3) \
+ (a1 * D2(b2,b3,c2,c3)) - \
+ (b1 * D2(a2,a3,c2,c3)) + \
+ (c1 * D2(a2,a3,b2,b3))
+
+ det = D3(M3(m,0,0), M3(m,1,0), M3(m,2,0),
+ M3(m,0,1), M3(m,1,1), M3(m,2,1),
+ M3(m,0,2), M3(m,1,2), M3(m,2,2));
+ if (det == 0)
+ det = 1.0;
+ det = 1.0 / det;
+
+ M3(dst,0,0) = M3(m,1,1) * M3(m,2,2) - M3(m,1,2) * M3(m,2,1);
+ M3(dst,0,1) = -M3(m,0,1) * M3(m,2,2) + M3(m,0,2) * M3(m,2,1);
+ M3(dst,0,2) = M3(m,0,1) * M3(m,1,2) - M3(m,0,2) * M3(m,1,1);
+
+ M3(dst,1,0) = -M3(m,1,0) * M3(m,2,2) + M3(m,1,2) * M3(m,2,0);
+ M3(dst,1,1) = M3(m,0,0) * M3(m,2,2) - M3(m,0,2) * M3(m,2,0);
+ M3(dst,1,2) = -M3(m,0,0) * M3(m,1,2) + M3(m,0,2) * M3(m,1,0);
+
+ M3(dst,2,0) = M3(m,1,0) * M3(m,2,1) - M3(m,1,1) * M3(m,2,0);
+ M3(dst,2,1) = -M3(m,0,0) * M3(m,2,1) + M3(m,0,1) * M3(m,2,0);
+ M3(dst,2,2) = M3(m,0,0) * M3(m,1,1) - M3(m,0,1) * M3(m,1,0);
+
+ for (i = 0; i < 9; i++)
+ dst[i] *= det;
+}
+
+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)
+{
+ fz_matrix m;
+ m.a = 1; m.b = 0;
+ m.c = 0; m.d = 1;
+ m.e = 0; m.f = 0;
+ return m;
+}
+
+fz_matrix
+fz_scale(float sx, float sy)
+{
+ fz_matrix m;
+ m.a = sx; m.b = 0;
+ m.c = 0; m.d = sy;
+ m.e = 0; m.f = 0;
+ return m;
+}
+
+fz_matrix
+fz_rotate(float theta)
+{
+ fz_matrix m;
+ float s = sin(theta * M_PI / 180.0);
+ float c = cos(theta * M_PI / 180.0);
+ m.a = c; m.b = s;
+ m.c = -s; m.d = c;
+ m.e = 0; m.f = 0;
+ return m;
+}
+
+fz_matrix
+fz_translate(float tx, float ty)
+{
+ fz_matrix m;
+ m.a = 1; m.b = 0;
+ m.c = 0; m.d = 1;
+ m.e = tx; m.f = ty;
+ return m;
+}
+
+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);
+}
+
+float
+fz_matrixexpansion(fz_matrix m)
+{
+ return sqrt(fabs(m.a * m.d - m.b * m.c));
+}
+
+fz_point
+fz_transformpoint(fz_matrix m, fz_point p)
+{
+ fz_point t;
+ t.x = p.x * m.a + p.y * m.c + m.e;
+ t.y = p.x * m.b + p.y * m.d + m.f;
+ return t;
+}
+
+fz_rect
+fz_transformaabb(fz_matrix m, fz_rect r)
+{
+ fz_point s, t, u, v;
+
+ if (fz_isinfiniterect(r))
+ return r;
+
+ s.x = r.min.x; s.y = r.min.y;
+ t.x = r.min.x; t.y = r.max.y;
+ u.x = r.max.x; u.y = r.max.y;
+ v.x = r.max.x; v.y = r.min.y;
+ s = fz_transformpoint(m, s);
+ t = fz_transformpoint(m, t);
+ u = fz_transformpoint(m, u);
+ v = fz_transformpoint(m, v);
+ r.min.x = MIN4(s.x, t.x, u.x, v.x);
+ r.min.y = MIN4(s.y, t.y, u.y, v.y);
+ r.max.x = MAX4(s.x, t.x, u.x, v.x);
+ r.max.y = MAX4(s.y, t.y, u.y, v.y);
+ return r;
+}
+
diff --git a/fitz/base_memory.c b/fitz/base_memory.c
new file mode 100644
index 00000000..2c2f8e0d
--- /dev/null
+++ b/fitz/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 = {
+ -1,
+ {"out of memory"},
+ {"<malloc>"},
+ {"memory.c"},
+ 0
+};
+
+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/fitz/base_rect.c b/fitz/base_rect.c
new file mode 100644
index 00000000..2efb11f7
--- /dev/null
+++ b/fitz/base_rect.c
@@ -0,0 +1,75 @@
+#include <fitz.h>
+
+fz_rect fz_infiniterect = { { 1, 1}, {-1, -1} };
+fz_rect fz_emptyrect = { { 0, 0}, {0, 0} };
+
+static fz_irect infinite = { { 1, 1}, {-1, -1} };
+static fz_irect empty = { { 0, 0}, {0, 0} };
+
+fz_irect
+fz_roundrect(fz_rect f)
+{
+ fz_irect i;
+ i.min.x = fz_floor(f.min.x);
+ i.min.y = fz_floor(f.min.y);
+ i.max.x = fz_ceil(f.max.x);
+ i.max.y = fz_ceil(f.max.y);
+ return i;
+}
+
+fz_rect
+fz_intersectrects(fz_rect a, fz_rect b)
+{
+ fz_rect r;
+ if (fz_isinfiniterect(a)) return b;
+ if (fz_isinfiniterect(b)) return a;
+ 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.max.x < r.min.x || r.max.y < r.min.y) ? fz_emptyrect : r;
+}
+
+fz_rect
+fz_mergerects(fz_rect a, fz_rect b)
+{
+ fz_rect r;
+ if (fz_isinfiniterect(a) || fz_isinfiniterect(b))
+ return fz_infiniterect;
+ if (fz_isemptyrect(a)) return b;
+ if (fz_isemptyrect(b)) return a;
+ 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;
+ if (fz_isinfiniterect(a)) return b;
+ if (fz_isinfiniterect(b)) return a;
+ 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.max.x < r.min.x || r.max.y < r.min.y) ? empty : r;
+}
+
+fz_irect
+fz_mergeirects(fz_irect a, fz_irect b)
+{
+ fz_irect r;
+ if (fz_isinfiniterect(a) || fz_isinfiniterect(b))
+ return infinite;
+ if (fz_isemptyrect(a)) return b;
+ if (fz_isemptyrect(b)) return a;
+ 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/fitz/base_rune.c b/fitz/base_rune.c
new file mode 100644
index 00000000..4aa81df3
--- /dev/null
+++ b/fitz/base_rune.c
@@ -0,0 +1,168 @@
+enum
+{
+ UTFmax = 3, /* maximum bytes per rune */
+ Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */
+ Runeself = 0x80, /* rune and UTF sequences are the same (<) */
+ Runeerror = 0x80 /* decoding error in UTF */
+};
+
+enum
+{
+ Bit1 = 7,
+ Bitx = 6,
+ Bit2 = 5,
+ Bit3 = 4,
+ Bit4 = 3,
+
+ T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */
+ Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */
+ T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */
+ T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */
+ T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */
+
+ Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0111 1111 */
+ Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0111 1111 1111 */
+ Rune3 = (1<<(Bit3+2*Bitx))-1, /* 1111 1111 1111 1111 */
+
+ Maskx = (1<<Bitx)-1, /* 0011 1111 */
+ Testx = Maskx ^ 0xFF, /* 1100 0000 */
+
+ Bad = Runeerror
+};
+
+int
+chartorune(int *rune, char *str)
+{
+ int c, c1, c2;
+ int l;
+
+ /*
+ * one character sequence
+ * 00000-0007F => T1
+ */
+ c = *(unsigned char*)str;
+ if(c < Tx) {
+ *rune = c;
+ return 1;
+ }
+
+ /*
+ * two character sequence
+ * 0080-07FF => T2 Tx
+ */
+ c1 = *(unsigned char*)(str+1) ^ Tx;
+ if(c1 & Testx)
+ goto bad;
+ if(c < T3) {
+ if(c < T2)
+ goto bad;
+ l = ((c << Bitx) | c1) & Rune2;
+ if(l <= Rune1)
+ goto bad;
+ *rune = l;
+ return 2;
+ }
+
+ /*
+ * three character sequence
+ * 0800-FFFF => T3 Tx Tx
+ */
+ c2 = *(unsigned char*)(str+2) ^ Tx;
+ if(c2 & Testx)
+ goto bad;
+ if(c < T4) {
+ l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3;
+ if(l <= Rune2)
+ goto bad;
+ *rune = l;
+ return 3;
+ }
+
+ /*
+ * bad decoding
+ */
+bad:
+ *rune = Bad;
+ return 1;
+}
+
+int
+runetochar(char *str, int *rune)
+{
+ int c;
+
+ /*
+ * one character sequence
+ * 00000-0007F => 00-7F
+ */
+ c = *rune;
+ if(c <= Rune1) {
+ str[0] = c;
+ return 1;
+ }
+
+ /*
+ * two character sequence
+ * 0080-07FF => T2 Tx
+ */
+ if(c <= Rune2) {
+ str[0] = T2 | (c >> 1*Bitx);
+ str[1] = Tx | (c & Maskx);
+ return 2;
+ }
+
+ /*
+ * three character sequence
+ * 0800-FFFF => T3 Tx Tx
+ */
+ str[0] = T3 | (c >> 2*Bitx);
+ str[1] = Tx | ((c >> 1*Bitx) & Maskx);
+ str[2] = Tx | (c & Maskx);
+ return 3;
+}
+
+int
+runelen(int c)
+{
+ int rune;
+ char str[10];
+
+ rune = c;
+ return runetochar(str, &rune);
+}
+
+int
+runenlen(int *r, int nrune)
+{
+ int nb, c;
+
+ nb = 0;
+ while(nrune--) {
+ c = *r++;
+ if(c <= Rune1)
+ nb++;
+ else
+ if(c <= Rune2)
+ nb += 2;
+ else
+ nb += 3;
+ }
+ return nb;
+}
+
+int
+fullrune(char *str, int n)
+{
+ int c;
+
+ if(n > 0) {
+ c = *(unsigned char*)str;
+ if(c < Tx)
+ return 1;
+ if(n > 1)
+ if(c < T3 || n > 2)
+ return 1;
+ }
+ return 0;
+}
+
diff --git a/fitz/node_debug.c b/fitz/node_debug.c
new file mode 100644
index 00000000..b03d4e28
--- /dev/null
+++ b/fitz/node_debug.c
@@ -0,0 +1,191 @@
+#include <fitz.h>
+
+static void indent(int level)
+{
+ while (level--)
+ putchar(' ');
+}
+
+static void lispnode(fz_node *node, int level);
+
+static void lispmeta(fz_metanode *node, int level)
+{
+ fz_node *child;
+ indent(level);
+ printf("(meta ");
+ if (node->name) { fz_debugobj(node->name); }
+ if (node->dict) { printf("\n"); fz_debugobj(node->dict); }
+ printf("\n");
+ for (child = node->super.first; child; child = child->next)
+ lispnode(child, level + 1);
+ indent(level);
+ printf(")\n");
+}
+
+static void lispover(fz_overnode *node, int level)
+{
+ fz_node *child;
+ indent(level);
+ printf("(over\n");
+ for (child = node->super.first; child; child = child->next)
+ lispnode(child, level + 1);
+ indent(level);
+ printf(")\n");
+}
+
+static void lispmask(fz_masknode *node, int level)
+{
+ fz_node *child;
+ indent(level);
+ printf("(mask\n");
+ for (child = node->super.first; child; child = child->next)
+ lispnode(child, level + 1);
+ indent(level);
+ printf(")\n");
+}
+
+static void lispblend(fz_blendnode *node, int level)
+{
+ fz_node *child;
+ indent(level);
+ printf("(blend-%d\n", node->mode);
+ for (child = node->super.first; child; child = child->next)
+ lispnode(child, level + 1);
+ indent(level);
+ printf(")\n");
+}
+
+static void lisptransform(fz_transformnode *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->super.first, level + 1);
+ indent(level);
+ printf(")\n");
+}
+
+static void lispcolor(fz_colornode *node, int level)
+{
+ int i;
+ indent(level);
+ printf("(color %s ", node->cs->name);
+ for (i = 0; i < node->n; i++)
+ printf("%g ", node->samples[i]);
+ printf(")\n");
+}
+
+static void lisplink(fz_linknode *node, int level)
+{
+ indent(level);
+ printf("(link %p)\n", node->tree);
+}
+
+static void lisppath(fz_pathnode *node, int level)
+{
+ int i;
+
+ indent(level);
+
+ if (node->paint == FZ_STROKE)
+ {
+ printf("(path 'stroke %d %d %g %g ",
+ node->linecap,
+ node->linejoin,
+ node->linewidth,
+ node->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_debugpathnode(node);
+
+ indent(level);
+ printf(")\n");
+}
+
+static void lisptext(fz_textnode *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);
+ if (node->els[i].cid >= 32 && node->els[i].cid < 128)
+ printf("(cid '%c' %g %g)\n", node->els[i].cid, node->els[i].x, node->els[i].y);
+ else
+ printf("(cid <%04x> %g %g)\n", node->els[i].cid, node->els[i].x, node->els[i].y);
+ }
+
+ indent(level);
+ printf(")\n");
+}
+
+static void lispimage(fz_imagenode *node, int level)
+{
+ fz_image *image = node->image;
+ indent(level);
+ printf("(image %dx%d %d+%d)\n", image->w, image->h, image->n, image->a);
+}
+
+static void lispshade(fz_shadenode *node, int level)
+{
+ indent(level);
+ printf("(shade)\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_metanode*)node, level); break;
+ case FZ_NOVER: lispover((fz_overnode*)node, level); break;
+ case FZ_NMASK: lispmask((fz_masknode*)node, level); break;
+ case FZ_NBLEND: lispblend((fz_blendnode*)node, level); break;
+ case FZ_NTRANSFORM: lisptransform((fz_transformnode*)node, level); break;
+ case FZ_NCOLOR: lispcolor((fz_colornode*)node, level); break;
+ case FZ_NPATH: lisppath((fz_pathnode*)node, level); break;
+ case FZ_NTEXT: lisptext((fz_textnode*)node, level); break;
+ case FZ_NIMAGE: lispimage((fz_imagenode*)node, level); break;
+ case FZ_NSHADE: lispshade((fz_shadenode*)node, level); break;
+ case FZ_NLINK: lisplink((fz_linknode*)node, level); break;
+ }
+}
+
+void
+fz_debugnode(fz_node *node)
+{
+ lispnode(node, 0);
+}
+
+void
+fz_debugtree(fz_tree *tree)
+{
+ lispnode(tree->root, 0);
+}
+
diff --git a/fitz/node_misc1.c b/fitz/node_misc1.c
new file mode 100644
index 00000000..c2d6e876
--- /dev/null
+++ b/fitz/node_misc1.c
@@ -0,0 +1,167 @@
+#include <fitz.h>
+
+void fz_dropmetanode(fz_metanode* node);
+void fz_droplinknode(fz_linknode* node);
+void fz_droppathnode(fz_pathnode* node);
+void fz_droptextnode(fz_textnode* node);
+void fz_dropimagenode(fz_imagenode* node);
+void fz_dropshadenode(fz_shadenode* node);
+
+fz_rect fz_boundtransformnode(fz_transformnode* node, fz_matrix ctm);
+fz_rect fz_boundovernode(fz_overnode* node, fz_matrix ctm);
+fz_rect fz_boundmasknode(fz_masknode* node, fz_matrix ctm);
+fz_rect fz_boundblendnode(fz_blendnode* node, fz_matrix ctm);
+fz_rect fz_boundcolornode(fz_colornode* node, fz_matrix ctm);
+fz_rect fz_boundpathnode(fz_pathnode* node, fz_matrix ctm);
+fz_rect fz_boundtextnode(fz_textnode* node, fz_matrix ctm);
+fz_rect fz_boundimagenode(fz_imagenode* node, fz_matrix ctm);
+fz_rect fz_boundshadenode(fz_shadenode* node, fz_matrix ctm);
+fz_rect fz_boundlinknode(fz_linknode* node, fz_matrix ctm);
+fz_rect fz_boundmetanode(fz_metanode* node, fz_matrix ctm);
+
+void
+fz_initnode(fz_node *node, fz_nodekind kind)
+{
+ node->kind = kind;
+ node->parent = nil;
+ node->first = nil;
+ node->last = nil;
+ node->next = nil;
+}
+
+void
+fz_dropnode(fz_node *node)
+{
+ if (node->first)
+ fz_dropnode(node->first);
+ if (node->next)
+ fz_dropnode(node->next);
+
+ switch (node->kind)
+ {
+ case FZ_NTRANSFORM:
+ case FZ_NOVER:
+ case FZ_NMASK:
+ case FZ_NBLEND:
+ case FZ_NCOLOR:
+ break;
+ case FZ_NPATH:
+ fz_droppathnode((fz_pathnode *) node);
+ break;
+ case FZ_NTEXT:
+ fz_droptextnode((fz_textnode *) node);
+ break;
+ case FZ_NIMAGE:
+ fz_dropimagenode((fz_imagenode *) node);
+ break;
+ case FZ_NSHADE:
+ fz_dropshadenode((fz_shadenode *) node);
+ break;
+ case FZ_NLINK:
+ fz_droplinknode((fz_linknode *) node);
+ break;
+ case FZ_NMETA:
+ fz_dropmetanode((fz_metanode *) node);
+ break;
+ }
+
+ fz_free(node);
+}
+
+fz_rect
+fz_boundnode(fz_node *node, fz_matrix ctm)
+{
+ switch (node->kind)
+ {
+ case FZ_NTRANSFORM:
+ return fz_boundtransformnode((fz_transformnode *) node, ctm);
+ case FZ_NOVER:
+ return fz_boundovernode((fz_overnode *) node, ctm);
+ case FZ_NMASK:
+ return fz_boundmasknode((fz_masknode *) node, ctm);
+ case FZ_NBLEND:
+ return fz_boundblendnode((fz_blendnode *) node, ctm);
+ case FZ_NCOLOR:
+ return fz_boundcolornode((fz_colornode *) node, ctm);
+ case FZ_NPATH:
+ return fz_boundpathnode((fz_pathnode *) node, ctm);
+ case FZ_NTEXT:
+ return fz_boundtextnode((fz_textnode *) node, ctm);
+ case FZ_NIMAGE:
+ return fz_boundimagenode((fz_imagenode *) node, ctm);
+ case FZ_NSHADE:
+ return fz_boundshadenode((fz_shadenode *) node, ctm);
+ case FZ_NLINK:
+ return fz_boundlinknode((fz_linknode *) node, ctm);
+ case FZ_NMETA:
+ return fz_boundmetanode((fz_metanode *) node, ctm);
+ }
+ return fz_emptyrect;
+}
+
+int
+fz_istransformnode(fz_node *node)
+{
+ return node ? node->kind == FZ_NTRANSFORM : 0;
+}
+
+int
+fz_isovernode(fz_node *node)
+{
+ return node ? node->kind == FZ_NOVER : 0;
+}
+
+int
+fz_ismasknode(fz_node *node)
+{
+ return node ? node->kind == FZ_NMASK : 0;
+}
+
+int
+fz_isblendnode(fz_node *node)
+{
+ return node ? node->kind == FZ_NBLEND : 0;
+}
+
+int
+fz_iscolornode(fz_node *node)
+{
+ return node ? node->kind == FZ_NCOLOR : 0;
+}
+
+int
+fz_ispathnode(fz_node *node)
+{
+ return node ? node->kind == FZ_NPATH : 0;
+}
+
+int
+fz_istextnode(fz_node *node)
+{
+ return node ? node->kind == FZ_NTEXT : 0;
+}
+
+int
+fz_isimagenode(fz_node *node)
+{
+ return node ? node->kind == FZ_NIMAGE : 0;
+}
+
+int
+fz_isshadenode(fz_node *node)
+{
+ return node ? node->kind == FZ_NSHADE : 0;
+}
+
+int
+fz_islinknode(fz_node *node)
+{
+ return node ? node->kind == FZ_NLINK : 0;
+}
+
+int
+fz_ismetanode(fz_node *node)
+{
+ return node ? node->kind == FZ_NMETA : 0;
+}
+
diff --git a/fitz/node_misc2.c b/fitz/node_misc2.c
new file mode 100644
index 00000000..cd4595f4
--- /dev/null
+++ b/fitz/node_misc2.c
@@ -0,0 +1,338 @@
+#include <fitz.h>
+
+/*
+ * Over
+ */
+
+fz_error *
+fz_newovernode(fz_node **nodep)
+{
+ fz_node *node;
+
+ node = *nodep = fz_malloc(sizeof (fz_overnode));
+ if (!node)
+ return fz_outofmem;
+
+ fz_initnode(node, FZ_NOVER);
+
+ return nil;
+}
+
+fz_rect
+fz_boundovernode(fz_overnode *node, fz_matrix ctm)
+{
+ fz_node *child;
+ fz_rect bbox;
+ fz_rect temp;
+
+ child = node->super.first;
+ if (!child)
+ return fz_emptyrect;
+
+ bbox = fz_boundnode(child, ctm);
+
+ child = child->next;
+ while (child)
+ {
+ temp = fz_boundnode(child, ctm);
+ bbox = fz_mergerects(temp, bbox);
+ child = child->next;
+ }
+
+ return bbox;
+}
+
+/*
+ * Mask
+ */
+
+fz_error *
+fz_newmasknode(fz_node **nodep)
+{
+ fz_node *node;
+
+ node = *nodep = fz_malloc(sizeof (fz_masknode));
+ if (!node)
+ return fz_outofmem;
+
+ fz_initnode(node, FZ_NMASK);
+
+ return nil;
+}
+
+fz_rect
+fz_boundmasknode(fz_masknode *node, fz_matrix ctm)
+{
+ fz_node *shape;
+ fz_node *color;
+ fz_rect one, two;
+
+ shape = node->super.first;
+ color = shape->next;
+
+ one = fz_boundnode(shape, ctm);
+ two = fz_boundnode(color, ctm);
+ return fz_intersectrects(one, two);
+}
+
+/*
+ * Blend
+ */
+
+fz_error *
+fz_newblendnode(fz_node **nodep, fz_colorspace *cs, fz_blendkind b, int k, int i)
+{
+ fz_blendnode *node;
+
+ node = fz_malloc(sizeof (fz_blendnode));
+ if (!node)
+ return fz_outofmem;
+ *nodep = (fz_node*)node;
+
+ fz_initnode((fz_node*)node, FZ_NBLEND);
+ node->cs = fz_keepcolorspace(cs);
+ node->mode = b;
+ node->knockout = k;
+ node->isolated = i;
+
+ return nil;
+}
+
+fz_rect
+fz_boundblendnode(fz_blendnode *node, fz_matrix ctm)
+{
+ fz_node *child;
+ fz_rect bbox;
+ fz_rect temp;
+
+ child = node->super.first;
+ if (!child)
+ return fz_emptyrect;
+
+ bbox = fz_boundnode(child, ctm);
+
+ child = child->next;
+ while (child)
+ {
+ temp = fz_boundnode(child, ctm);
+ bbox = fz_mergerects(temp, bbox);
+ child = child->next;
+ }
+
+ return bbox;
+}
+
+void
+fz_dropblendnode(fz_blendnode *node)
+{
+ fz_dropcolorspace(node->cs);
+}
+
+/*
+ * Transform
+ */
+
+fz_error *
+fz_newtransformnode(fz_node **nodep, fz_matrix m)
+{
+ fz_transformnode *node;
+
+ node = fz_malloc(sizeof (fz_transformnode));
+ if (!node)
+ return fz_outofmem;
+ *nodep = (fz_node*)node;
+
+ fz_initnode((fz_node*)node, FZ_NTRANSFORM);
+ node->m = m;
+
+ return nil;
+}
+
+fz_rect
+fz_boundtransformnode(fz_transformnode *node, fz_matrix ctm)
+{
+ if (!node->super.first)
+ return fz_emptyrect;
+ return fz_boundnode(node->super.first, fz_concat(node->m, ctm));
+}
+
+/*
+ * Meta info
+ */
+
+fz_error *
+fz_newmetanode(fz_node **nodep, void *name, void *dict)
+{
+ fz_metanode *node;
+
+ node = fz_malloc(sizeof (fz_metanode));
+ if (!node)
+ return fz_outofmem;
+ *nodep = (fz_node*)node;
+
+ fz_initnode((fz_node*)node, FZ_NMETA);
+ node->name = nil;
+ node->dict = nil;
+
+ if (name)
+ node->name = fz_keepobj(name);
+ if (dict)
+ node->dict = fz_keepobj(dict);
+
+ return nil;
+}
+
+void
+fz_dropmetanode(fz_metanode *node)
+{
+ if (node->name)
+ fz_dropobj(node->name);
+ if (node->dict)
+ fz_dropobj(node->dict);
+}
+
+fz_rect
+fz_boundmetanode(fz_metanode *node, fz_matrix ctm)
+{
+ if (!node->super.first)
+ return fz_emptyrect;
+ return fz_boundnode(node->super.first, ctm);
+}
+
+/*
+ * Link to tree
+ */
+
+fz_error *
+fz_newlinknode(fz_node **nodep, fz_tree *subtree)
+{
+ fz_linknode *node;
+
+ node = fz_malloc(sizeof (fz_linknode));
+ if (!node)
+ return fz_outofmem;
+ *nodep = (fz_node*)node;
+
+ fz_initnode((fz_node*)node, FZ_NLINK);
+ node->tree = fz_keeptree(subtree);
+
+ return nil;
+}
+
+void
+fz_droplinknode(fz_linknode *node)
+{
+ fz_droptree(node->tree);
+}
+
+fz_rect
+fz_boundlinknode(fz_linknode *node, fz_matrix ctm)
+{
+ return fz_boundtree(node->tree, ctm);
+}
+
+/*
+ * Solid color
+ */
+
+fz_error *
+fz_newcolornode(fz_node **nodep, fz_colorspace *cs, int n, float *v)
+{
+ fz_colornode *node;
+ int i;
+
+ node = fz_malloc(sizeof(fz_colornode) + sizeof(float) * n);
+ if (!node)
+ return fz_outofmem;
+ *nodep = (fz_node*)node;
+
+ fz_initnode((fz_node*)node, FZ_NCOLOR);
+ node->cs = fz_keepcolorspace(cs);
+ node->n = n;
+ for (i = 0; i < n; i++)
+ node->samples[i] = v[i];
+
+ return nil;
+}
+
+fz_rect
+fz_boundcolornode(fz_colornode *node, fz_matrix ctm)
+{
+ return fz_infiniterect;
+}
+
+void
+fz_dropcolornode(fz_colornode *node)
+{
+ fz_dropcolorspace(node->cs);
+}
+
+/*
+ * Image node
+ */
+
+fz_error *
+fz_newimagenode(fz_node **nodep, fz_image *image)
+{
+ fz_imagenode *node;
+
+ node = fz_malloc(sizeof (fz_imagenode));
+ if (!node)
+ return fz_outofmem;
+ *nodep = (fz_node*)node;
+
+ fz_initnode((fz_node*)node, FZ_NIMAGE);
+ node->image = fz_keepimage(image);
+
+ return nil;
+}
+
+void
+fz_dropimagenode(fz_imagenode *node)
+{
+ fz_dropimage(node->image);
+}
+
+fz_rect
+fz_boundimagenode(fz_imagenode *node, fz_matrix ctm)
+{
+ fz_rect bbox;
+ bbox.min.x = 0;
+ bbox.min.y = 0;
+ bbox.max.x = 1;
+ bbox.max.y = 1;
+ return fz_transformaabb(ctm, bbox);
+}
+
+/*
+ * Shade node
+ */
+
+fz_error *
+fz_newshadenode(fz_node **nodep, fz_shade *shade)
+{
+ fz_shadenode *node;
+
+ node = fz_malloc(sizeof (fz_shadenode));
+ if (!node)
+ return fz_outofmem;
+ *nodep = (fz_node*)node;
+
+ fz_initnode((fz_node*)node, FZ_NSHADE);
+ node->shade = fz_keepshade(shade);
+
+ return nil;
+}
+
+void
+fz_dropshadenode(fz_shadenode *node)
+{
+ fz_dropshade(node->shade);
+}
+
+fz_rect
+fz_boundshadenode(fz_shadenode *node, fz_matrix ctm)
+{
+ return fz_boundshade(node->shade, ctm);
+}
+
diff --git a/fitz/node_optimize.c b/fitz/node_optimize.c
new file mode 100644
index 00000000..d92aa812
--- /dev/null
+++ b/fitz/node_optimize.c
@@ -0,0 +1,310 @@
+#include <fitz.h>
+
+/*
+ * Remove (mask ... white) until we get something not white
+ */
+
+static int iswhitenode(fz_colornode *node)
+{
+ if (!strcmp(node->cs->name, "DeviceGray"))
+ return fabs(node->samples[0] - 1.0) < FLT_EPSILON;
+ if (!strcmp(node->cs->name, "DeviceRGB"))
+ return fabs(node->samples[0] - 1.0) < FLT_EPSILON &&
+ fabs(node->samples[1] - 1.0) < FLT_EPSILON &&
+ fabs(node->samples[2] - 1.0) < FLT_EPSILON;
+ if (!strcmp(node->cs->name, "DeviceCMYK"))
+ return fabs(node->samples[0]) < FLT_EPSILON &&
+ fabs(node->samples[1]) < FLT_EPSILON &&
+ fabs(node->samples[2]) < FLT_EPSILON &&
+ fabs(node->samples[3]) < FLT_EPSILON;
+ return 0;
+}
+
+static int cleanwhite(fz_node *node)
+{
+ fz_node *current;
+ fz_node *next;
+ fz_node *shape;
+ fz_node *color;
+
+ for (current = node->first; current; current = next)
+ {
+ next = current->next;
+
+ if (fz_islinknode(current))
+ return 1;
+ else if (fz_isimagenode(current))
+ return 1;
+ else if (fz_isshadenode(current))
+ return 1;
+ else if (fz_iscolornode(current))
+ {
+ if (!iswhitenode((fz_colornode*)current))
+ return 1;
+ }
+
+ else if (fz_ismasknode(current))
+ {
+ shape = current->first;
+ color = shape->next;
+ if (fz_iscolornode(color))
+ {
+ if (iswhitenode((fz_colornode*)color))
+ fz_removenode(current);
+ else
+ return 1;
+ }
+ else
+ {
+ if (cleanwhite(current))
+ return 1;
+ }
+ }
+
+ else
+ {
+ if (cleanwhite(current))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Remove useless overs that only have one child.
+ */
+
+static void cleanovers(fz_node *node)
+{
+ fz_node *prev;
+ fz_node *next;
+ fz_node *current;
+ fz_node *child;
+
+ prev = nil;
+ for (current = node->first; current; current = next)
+ {
+ next = current->next;
+
+ if (fz_isovernode(current))
+ {
+ if (current->first == current->last)
+ {
+ child = current->first;
+ fz_removenode(current);
+ if (child)
+ {
+ if (prev)
+ fz_insertnodeafter(prev, child);
+ else
+ fz_insertnodefirst(node, child);
+ }
+ current = child;
+ }
+ }
+
+ if (current)
+ prev = current;
+ }
+
+ for (current = node->first; current; current = current->next)
+ cleanovers(current);
+}
+
+/*
+ * Remove rectangular clip-masks whose contents fit...
+ */
+
+static int getrect(fz_pathnode *path, fz_rect *bboxp)
+{
+ float x, y, w, h;
+
+ /* move x y, line x+w y, line x+w y+h, line x y+h, close */
+
+ if (path->len != 13)
+ return 0;
+
+ if (path->els[0].k != FZ_MOVETO) return 0;
+ x = path->els[1].v;
+ y = path->els[2].v;
+
+ if (path->els[3].k != FZ_LINETO) return 0;
+ w = path->els[4].v - x;
+ if (path->els[5].v != y) return 0;
+
+ if (path->els[6].k != FZ_LINETO) return 0;
+ if (path->els[7].v != x + w) return 0;
+ h = path->els[8].v - y;
+
+ if (path->els[9].k != FZ_LINETO) return 0;
+ if (path->els[10].v != x) return 0;
+ if (path->els[11].v != y + h) return 0;
+
+ if (path->els[12].k != FZ_CLOSEPATH) return 0;
+
+ bboxp->min.x = MIN(x, x + w);
+ bboxp->min.y = MIN(y, y + h);
+ bboxp->max.x = MAX(x, x + w);
+ bboxp->max.y = MAX(y, y + h);
+
+ return 1;
+}
+
+static int fitsinside(fz_node *node, fz_rect clip)
+{
+ fz_rect bbox;
+ bbox = fz_boundnode(node, fz_identity());
+ if (fz_isinfiniterect(bbox)) return 0;
+ if (fz_isemptyrect(bbox)) return 1;
+ if (bbox.min.x < clip.min.x) return 0;
+ if (bbox.max.x > clip.max.x) return 0;
+ if (bbox.min.y < clip.min.y) return 0;
+ if (bbox.max.y > clip.max.y) return 0;
+ return 1;
+}
+
+static void cleanmasks(fz_node *node)
+{
+ fz_node *prev;
+ fz_node *current;
+ fz_node *shape;
+ fz_node *color;
+ fz_rect bbox;
+
+ for (current = node->first; current; current = current->next)
+ cleanmasks(current);
+
+ prev = nil;
+ for (current = node->first; current; current = current->next)
+ {
+retry:
+ if (fz_ismasknode(current))
+ {
+ shape = current->first;
+ color = shape->next;
+
+ if (color == nil)
+ {
+ fz_removenode(current);
+ prev = nil;
+ current = node->first;
+ goto retry;
+ }
+
+ if (fz_ispathnode(shape))
+ {
+ if (getrect((fz_pathnode*)shape, &bbox))
+ {
+ if (fitsinside(color, bbox))
+ {
+ fz_removenode(current);
+ if (prev)
+ fz_insertnodeafter(prev, color);
+ else
+ fz_insertnodefirst(node, color);
+ current = color;
+ goto retry;
+ }
+ }
+ }
+ }
+
+ prev = current;
+ }
+}
+
+/*
+ * Turn 1x1 images into rectangle fills
+ */
+
+static fz_error *clean1x1(fz_node *node)
+{
+ fz_error *error;
+ fz_node *current;
+ fz_node *color;
+ fz_pathnode *rect;
+ fz_node *mask;
+ fz_image *image;
+ fz_pixmap *pix;
+ float v[FZ_MAXCOLORS];
+ int i;
+
+ for (current = node->first; current; current = current->next)
+ {
+ if (fz_isimagenode(current))
+ {
+ image = ((fz_imagenode*)current)->image;
+ if (image->w == 1 && image->h == 1)
+ {
+ error = fz_newpathnode(&rect);
+ fz_moveto(rect, 0, 0);
+ fz_lineto(rect, 1, 0);
+ fz_lineto(rect, 1, 1);
+ fz_lineto(rect, 0, 1);
+ fz_closepath(rect);
+ fz_endpath(rect, FZ_FILL, nil, nil);
+
+ if (image->cs)
+ {
+ error = fz_newpixmap(&pix, 0, 0, 1, 1, image->n + 1);
+ if (error)
+ return error;
+
+ error = image->loadtile(image, pix);
+ if (error)
+ return error;
+
+ for (i = 0; i < image->n; i++)
+ v[i] = pix->samples[i + 1] / 255.0;
+
+ fz_droppixmap(pix);
+
+ error = fz_newcolornode(&color, image->cs, image->n, v);
+ if (error)
+ return error;
+ error = fz_newmasknode(&mask);
+ if (error)
+ return error;
+
+ fz_insertnodeafter(current, mask);
+ fz_insertnodelast(mask, (fz_node*)rect);
+ fz_insertnodelast(mask, color);
+ fz_removenode(current);
+ current = mask;
+ }
+
+ else
+ {
+ /* pray that the 1x1 image mask is all opaque */
+ fz_insertnodeafter(current, (fz_node*)rect);
+ fz_removenode(current);
+ current = (fz_node*)rect;
+ }
+ }
+ }
+
+ error = clean1x1(current);
+ if (error)
+ return error;
+ }
+
+ return nil;
+}
+
+/*
+ *
+ */
+
+fz_error *
+fz_optimizetree(fz_tree *tree)
+{
+ if (getenv("DONTOPT"))
+ return nil;
+ cleanwhite(tree->root);
+ cleanovers(tree->root);
+ cleanmasks(tree->root);
+ clean1x1(tree->root);
+ return nil;
+}
+
diff --git a/fitz/node_path.c b/fitz/node_path.c
new file mode 100644
index 00000000..24b4dfc8
--- /dev/null
+++ b/fitz/node_path.c
@@ -0,0 +1,305 @@
+#include <fitz.h>
+
+fz_error *
+fz_newpathnode(fz_pathnode **pathp)
+{
+ fz_pathnode *path;
+
+ path = *pathp = fz_malloc(sizeof(fz_pathnode));
+ if (!path)
+ return fz_outofmem;
+
+ fz_initnode((fz_node*)path, FZ_NPATH);
+
+ path->paint = FZ_FILL;
+ path->linecap = 0;
+ path->linejoin = 0;
+ path->linewidth = 1.0;
+ path->miterlimit = 10.0;
+ path->dash = nil;
+ path->len = 0;
+ path->cap = 0;
+ path->els = nil;
+
+ return nil;
+}
+
+fz_error *
+fz_clonepathnode(fz_pathnode **pathp, fz_pathnode *oldpath)
+{
+ fz_pathnode *path;
+
+ path = *pathp = fz_malloc(sizeof(fz_pathnode));
+ if (!path)
+ return fz_outofmem;
+
+ fz_initnode((fz_node*)path, FZ_NPATH);
+
+ path->paint = FZ_FILL;
+ path->linecap = 0;
+ path->linejoin = 0;
+ path->linewidth = 1.0;
+ path->miterlimit = 10.0;
+ 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_droppathnode(fz_pathnode *node)
+{
+ fz_free(node->dash);
+ fz_free(node->els);
+}
+
+static fz_error *
+growpath(fz_pathnode *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_pathnode *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_pathnode *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_pathnode *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_pathnode *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_pathnode *path, float x1, float y1, float x3, float y3)
+{
+ return fz_curveto(path, x1, y1, x3, y3, x3, y3);
+}
+
+fz_error *
+fz_closepath(fz_pathnode *path)
+{
+ if (growpath(path, 1) != nil)
+ return fz_outofmem;
+ path->els[path->len++].k = FZ_CLOSEPATH;
+ return nil;
+}
+
+fz_error *
+fz_endpath(fz_pathnode *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->dash = dash;
+ if (stroke)
+ {
+ path->linecap = stroke->linecap;
+ path->linejoin = stroke->linejoin;
+ path->linewidth = stroke->linewidth;
+ path->miterlimit = stroke->miterlimit;
+ }
+
+ if (path->linewidth < 0.01)
+ path->linewidth = 0.01;
+
+ return nil;
+}
+
+static inline fz_rect boundexpand(fz_rect r, fz_point p)
+{
+ 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_boundpathnode(fz_pathnode *path, fz_matrix ctm)
+{
+ fz_point p;
+ fz_rect r = fz_emptyrect;
+ int i = 0;
+
+ if (path->len)
+ {
+ p.x = path->els[1].v;
+ p.y = path->els[2].v;
+ p = fz_transformpoint(ctm, p);
+ r.min.x = r.max.x = p.x;
+ r.min.y = r.max.y = p.y;
+ }
+
+ 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->miterlimit / 2.0);
+ float linewidth = path->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_debugpathnode(fz_pathnode *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_dropdash(fz_dash *dash)
+{
+ fz_free(dash);
+}
+
diff --git a/fitz/node_text.c b/fitz/node_text.c
new file mode 100644
index 00000000..d88e04ec
--- /dev/null
+++ b/fitz/node_text.c
@@ -0,0 +1,141 @@
+#include <fitz.h>
+
+fz_error *
+fz_newtextnode(fz_textnode **textp, fz_font *font)
+{
+ fz_textnode *text;
+
+ text = fz_malloc(sizeof(fz_textnode));
+ if (!text)
+ return fz_outofmem;
+
+ fz_initnode((fz_node*)text, FZ_NTEXT);
+
+ text->font = fz_keepfont(font);
+ text->trm = fz_identity();
+ text->len = 0;
+ text->cap = 0;
+ text->els = nil;
+
+ *textp = text;
+ return nil;
+}
+
+fz_error *
+fz_clonetextnode(fz_textnode **textp, fz_textnode *oldtext)
+{
+ fz_textnode *text;
+
+ text = *textp = fz_malloc(sizeof(fz_textnode));
+ if (!text)
+ return fz_outofmem;
+
+ fz_initnode((fz_node*)text, FZ_NTEXT);
+
+ text->font = fz_keepfont(oldtext->font);
+ text->trm = oldtext->trm;
+ text->len = oldtext->len;
+ text->cap = oldtext->len;
+ text->els = nil;
+
+ text->els = fz_malloc(sizeof(fz_textel) * text->len);
+ if (!text->els)
+ {
+ fz_dropfont(text->font);
+ fz_free(text);
+ return fz_outofmem;
+ }
+
+ memcpy(text->els, oldtext->els, sizeof(fz_textel) * text->len);
+
+ *textp = text;
+ return nil;
+}
+
+void
+fz_droptextnode(fz_textnode *text)
+{
+ fz_dropfont(text->font);
+ fz_free(text->els);
+}
+
+fz_rect
+fz_boundtextnode(fz_textnode *text, fz_matrix ctm)
+{
+ fz_matrix trm;
+ fz_rect bbox;
+ fz_rect fbox;
+ int i;
+
+ if (text->len == 0)
+ return fz_emptyrect;
+
+ /* find bbox of glyph origins in ctm space */
+
+ bbox.min.x = bbox.max.x = text->els[0].x;
+ bbox.min.y = bbox.max.y = text->els[0].y;
+
+ for (i = 1; i < text->len; i++)
+ {
+ bbox.min.x = MIN(bbox.min.x, text->els[i].x);
+ bbox.min.y = MIN(bbox.min.y, text->els[i].y);
+ bbox.max.x = MAX(bbox.max.x, text->els[i].x);
+ bbox.max.y = MAX(bbox.max.y, text->els[i].y);
+ }
+
+ bbox = fz_transformaabb(ctm, bbox);
+
+ /* find bbox of font in trm * ctm space */
+
+ trm = fz_concat(text->trm, ctm);
+ trm.e = 0;
+ trm.f = 0;
+
+ fbox.min.x = text->font->bbox.min.x * 0.001;
+ fbox.min.y = text->font->bbox.min.y * 0.001;
+ fbox.max.x = text->font->bbox.max.x * 0.001;
+ fbox.max.y = text->font->bbox.max.y * 0.001;
+
+ fbox = fz_transformaabb(trm, fbox);
+
+ /* expand glyph origin bbox by font bbox */
+
+ bbox.min.x += fbox.min.x;
+ bbox.min.y += fbox.min.y;
+ bbox.max.x += fbox.max.x;
+ bbox.max.y += fbox.max.y;
+
+ return bbox;
+}
+
+static fz_error *
+growtext(fz_textnode *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_textnode *text, int cid, float x, float y)
+{
+ if (growtext(text, 1) != nil)
+ return fz_outofmem;
+ text->els[text->len].cid = cid;
+ text->els[text->len].x = x;
+ text->els[text->len].y = y;
+ text->len++;
+ return nil;
+}
+
diff --git a/fitz/node_tree.c b/fitz/node_tree.c
new file mode 100644
index 00000000..f6874711
--- /dev/null
+++ b/fitz/node_tree.c
@@ -0,0 +1,107 @@
+#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->refs = 1;
+ tree->root = nil;
+ tree->head = nil;
+
+ return nil;
+}
+
+fz_tree *
+fz_keeptree(fz_tree *tree)
+{
+ tree->refs ++;
+ return tree;
+}
+
+void
+fz_droptree(fz_tree *tree)
+{
+ if (--tree->refs == 0)
+ {
+ if (tree->root)
+ fz_dropnode(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_emptyrect;
+}
+
+void
+fz_insertnodefirst(fz_node *parent, fz_node *child)
+{
+ child->parent = parent;
+ child->next = parent->first;
+ parent->first = child;
+ if (!parent->last)
+ parent->last = child;
+}
+
+void
+fz_insertnodelast(fz_node *parent, fz_node *child)
+{
+ child->parent = parent;
+ if (!parent->first)
+ parent->first = child;
+ else
+ parent->last->next = child;
+ parent->last = child;
+}
+
+void
+fz_insertnodeafter(fz_node *prev, fz_node *child)
+{
+ fz_node *parent = prev->parent;
+ child->parent = parent;
+ if (parent->last == prev)
+ parent->last = child;
+ child->next = prev->next;
+ prev->next = child;
+}
+
+void
+fz_removenode(fz_node *child)
+{
+ fz_node *parent = child->parent;
+ fz_node *prev;
+ fz_node *node;
+
+ if (parent->first == child)
+ {
+ parent->first = child->next;
+ if (parent->last == child)
+ parent->last = nil;
+ return;
+ }
+
+ prev = parent->first;
+ node = prev->next;
+
+ while (node)
+ {
+ if (node == child)
+ {
+ prev->next = child->next;
+ }
+ prev = node;
+ node = node->next;
+ }
+
+ parent->last = prev;
+}
+
diff --git a/fitz/res_cmap.c b/fitz/res_cmap.c
new file mode 100644
index 00000000..577ba7c0
--- /dev/null
+++ b/fitz/res_cmap.c
@@ -0,0 +1,466 @@
+#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
+{
+ int refs;
+ 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;
+
+ cmap->refs = 1;
+ 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;
+}
+
+fz_cmap *
+fz_keepcmap(fz_cmap *cmap)
+{
+ cmap->refs ++;
+ return cmap;
+}
+
+void
+fz_dropcmap(fz_cmap *cmap)
+{
+ if (--cmap->refs == 0)
+ {
+ if (cmap->usecmap)
+ fz_dropcmap(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;
+
+ if (cmap->usecmap)
+ fz_dropcmap(cmap->usecmap);
+ cmap->usecmap = fz_keepcmap(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 *error;
+ 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;
+ error = addlookup(cmap, b->offset);
+ if (error)
+ return error;
+ }
+
+ /* 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;
+
+ error = addlookup(cmap, a->offset);
+ if (error)
+ return error;
+
+ error = addlookup(cmap, b->offset);
+ if (error)
+ return error;
+
+ a->offset = cmap->tlen - 2;
+ }
+
+ /* LS -> L */
+ else if (a->flag == LOOKUP && b->flag == SINGLE)
+ {
+ a->high = b->high;
+ error = addlookup(cmap, b->offset);
+ if (error)
+ return error;
+ }
+
+ /* 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;
+}
+
+unsigned 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/fitz/res_colorspace.c b/fitz/res_colorspace.c
new file mode 100644
index 00000000..65909bfe
--- /dev/null
+++ b/fitz/res_colorspace.c
@@ -0,0 +1,89 @@
+#include <fitz.h>
+
+void
+fz_convertpixmap(fz_colorspace *srcs, fz_pixmap *src, fz_colorspace *dsts, fz_pixmap *dst)
+{
+ srcs->convpixmap(srcs, src, dsts, dst);
+}
+
+void
+fz_convertcolor(fz_colorspace *srcs, float *srcv, fz_colorspace *dsts, float *dstv)
+{
+ srcs->convcolor(srcs, srcv, dsts, dstv);
+}
+
+fz_colorspace *
+fz_keepcolorspace(fz_colorspace *cs)
+{
+ if (cs->refs < 0)
+ return cs;
+ cs->refs ++;
+ return cs;
+}
+
+void
+fz_dropcolorspace(fz_colorspace *cs)
+{
+ if (cs->refs < 0)
+ return;
+ if (--cs->refs == 0)
+ {
+ if (cs->drop)
+ cs->drop(cs);
+ fz_free(cs);
+ }
+}
+
+void
+fz_stdconvcolor(fz_colorspace *srcs, float *srcv, fz_colorspace *dsts, float *dstv)
+{
+ float xyz[3];
+ int i;
+
+ if (srcs != dsts)
+ {
+ srcs->toxyz(srcs, srcv, xyz);
+ dsts->fromxyz(dsts, xyz, dstv);
+ for (i = 0; i < dsts->n; i++)
+ dstv[i] = CLAMP(dstv[i], 0.0, 1.0);
+ }
+ else
+ {
+ for (i = 0; i < srcs->n; i++)
+ dstv[i] = srcv[i];
+ }
+}
+
+void
+fz_stdconvpixmap(fz_colorspace *srcs, fz_pixmap *src, fz_colorspace *dsts, fz_pixmap *dst)
+{
+ float srcv[FZ_MAXCOLORS];
+ float dstv[FZ_MAXCOLORS];
+ int y, x, k;
+
+ unsigned char *s = src->samples;
+ unsigned char *d = dst->samples;
+
+ printf("convert pixmap from %s to %s\n", srcs->name, dsts->name);
+
+ assert(src->w == dst->w && src->h == dst->h);
+ assert(src->n == srcs->n + 1);
+ assert(dst->n == dsts->n + 1);
+
+ for (y = 0; y < src->h; y++)
+ {
+ for (x = 0; x < src->w; x++)
+ {
+ *d++ = *s++;
+
+ for (k = 0; k < src->n - 1; k++)
+ srcv[k] = *s++ / 255.0;
+
+ fz_convertcolor(srcs, srcv, dsts, dstv);
+
+ for (k = 0; k < dst->n - 1; k++)
+ *d++ = dstv[k] * 255;
+ }
+ }
+}
+
diff --git a/fitz/res_font.c b/fitz/res_font.c
new file mode 100644
index 00000000..b7367ce1
--- /dev/null
+++ b/fitz/res_font.c
@@ -0,0 +1,269 @@
+#include <fitz.h>
+
+void
+fz_initfont(fz_font *font, char *name)
+{
+ font->refs = 1;
+ 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.lo = 0x0000;
+ font->dhmtx.hi = 0xFFFF;
+ font->dhmtx.w = 0;
+
+ font->dvmtx.lo = 0x0000;
+ font->dvmtx.hi = 0xFFFF;
+ font->dvmtx.x = 0;
+ font->dvmtx.y = 880;
+ font->dvmtx.w = -1000;
+}
+
+fz_font *
+fz_keepfont(fz_font *font)
+{
+ font->refs ++;
+ return font;
+}
+
+void
+fz_dropfont(fz_font *font)
+{
+ if (--font->refs == 0)
+ {
+ if (font->drop)
+ font->drop(font);
+ fz_free(font->hmtx);
+ fz_free(font->vmtx);
+ fz_free(font);
+ }
+}
+
+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 lo, int hi, 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].lo = lo;
+ font->hmtx[font->nhmtx].hi = hi;
+ font->hmtx[font->nhmtx].w = w;
+ font->nhmtx++;
+
+ return nil;
+}
+
+fz_error *
+fz_addvmtx(fz_font *font, int lo, int hi, 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].lo = lo;
+ font->vmtx[font->nvmtx].hi = hi;
+ 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->lo - b->lo;
+}
+
+static int cmpv(const void *a0, const void *b0)
+{
+ fz_vmtx *a = (fz_vmtx*)a0;
+ fz_vmtx *b = (fz_vmtx*)b0;
+ return a->lo - b->lo;
+}
+
+fz_error *
+fz_endhmtx(fz_font *font)
+{
+ fz_hmtx *newmtx;
+
+ if (!font->hmtx)
+ return nil;
+
+ qsort(font->hmtx, font->nhmtx, sizeof(fz_hmtx), cmph);
+
+ 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);
+
+ 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 cid)
+{
+ int l = 0;
+ int r = font->nhmtx - 1;
+ int m;
+
+ if (!font->hmtx)
+ goto notfound;
+
+ while (l <= r)
+ {
+ m = (l + r) >> 1;
+ if (cid < font->hmtx[m].lo)
+ r = m - 1;
+ else if (cid > font->hmtx[m].hi)
+ l = m + 1;
+ else
+ return font->hmtx[m];
+ }
+
+notfound:
+ return font->dhmtx;
+}
+
+fz_vmtx
+fz_getvmtx(fz_font *font, int cid)
+{
+ fz_hmtx h;
+ fz_vmtx v;
+ int l = 0;
+ int r = font->nvmtx - 1;
+ int m;
+
+ if (!font->vmtx)
+ goto notfound;
+
+ while (l <= r)
+ {
+ m = (l + r) >> 1;
+ if (cid < font->vmtx[m].lo)
+ r = m - 1;
+ else if (cid > font->vmtx[m].hi)
+ l = m + 1;
+ else
+ return font->vmtx[m];
+ }
+
+notfound:
+ h = fz_gethmtx(font, cid);
+ v = font->dvmtx;
+ v.x = h.w / 2;
+ return v;
+}
+
+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> <%04x> %d\n",
+ font->hmtx[i].lo, font->hmtx[i].hi, 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> <%04x> %d %d %d\n", font->vmtx[i].lo, font->vmtx[i].hi,
+ font->vmtx[i].x, font->vmtx[i].y, font->vmtx[i].w);
+ printf(" }\n");
+ }
+
+ printf("}\n");
+}
+
diff --git a/fitz/res_image.c b/fitz/res_image.c
new file mode 100644
index 00000000..dbeb1fd8
--- /dev/null
+++ b/fitz/res_image.c
@@ -0,0 +1,22 @@
+#include <fitz.h>
+
+fz_image *
+fz_keepimage(fz_image *image)
+{
+ image->refs ++;
+ return image;
+}
+
+void
+fz_dropimage(fz_image *image)
+{
+ if (--image->refs == 0)
+ {
+ if (image->drop)
+ image->drop(image);
+ if (image->cs)
+ fz_dropcolorspace(image->cs);
+ fz_free(image);
+ }
+}
+
diff --git a/fitz/res_shade.c b/fitz/res_shade.c
new file mode 100644
index 00000000..67a64e4e
--- /dev/null
+++ b/fitz/res_shade.c
@@ -0,0 +1,28 @@
+#include <fitz.h>
+
+fz_shade *
+fz_keepshade(fz_shade *shade)
+{
+ shade->refs ++;
+ return shade;
+}
+
+void
+fz_dropshade(fz_shade *shade)
+{
+ if (--shade->refs == 0)
+ {
+ if (shade->cs)
+ fz_dropcolorspace(shade->cs);
+ fz_free(shade->mesh);
+ fz_free(shade);
+ }
+}
+
+fz_rect
+fz_boundshade(fz_shade *shade, fz_matrix ctm)
+{
+ ctm = fz_concat(shade->matrix, ctm);
+ return fz_transformaabb(ctm, shade->bbox);
+}
+
diff --git a/fitz/util_getopt.c b/fitz/util_getopt.c
new file mode 100644
index 00000000..c870e206
--- /dev/null
+++ b/fitz/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/fitz/util_strlcat.c b/fitz/util_strlcat.c
new file mode 100644
index 00000000..c659b73a
--- /dev/null
+++ b/fitz/util_strlcat.c
@@ -0,0 +1,35 @@
+/* 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/fitz/util_strlcpy.c b/fitz/util_strlcpy.c
new file mode 100644
index 00000000..73416ae1
--- /dev/null
+++ b/fitz/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/fitz/util_strsep.c b/fitz/util_strsep.c
new file mode 100644
index 00000000..e54903ce
--- /dev/null
+++ b/fitz/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;
+}
+