summaryrefslogtreecommitdiff
path: root/fitz
diff options
context:
space:
mode:
authorTor Andersson <tor@ghostscript.com>2009-02-28 13:00:34 +0100
committerTor Andersson <tor@ghostscript.com>2009-02-28 13:00:34 +0100
commitf744dace3f0f91b8505979bf244453c9ec713b4b (patch)
tree86fed467944886a17d4d22038990a8fd8d2e3be8 /fitz
parentd0631f32c95f656d30c90d28c15a56b09fcc86ee (diff)
downloadmupdf-f744dace3f0f91b8505979bf244453c9ec713b4b.tar.xz
Moved Fitz files into one directory.
Diffstat (limited to 'fitz')
-rw-r--r--fitz/Jamfile102
-rw-r--r--fitz/base_cleanname.c52
-rw-r--r--fitz/base_cpudep.c326
-rw-r--r--fitz/base_error.c71
-rw-r--r--fitz/base_hash.c276
-rw-r--r--fitz/base_matrix.c183
-rw-r--r--fitz/base_memory.c79
-rw-r--r--fitz/base_rect.c75
-rw-r--r--fitz/base_rune.c174
-rw-r--r--fitz/crypt_arc4.c100
-rw-r--r--fitz/crypt_crc32.c86
-rw-r--r--fitz/crypt_md5.c274
-rw-r--r--fitz/filt_a85d.c128
-rw-r--r--fitz/filt_a85e.c126
-rw-r--r--fitz/filt_ahxd.c112
-rw-r--r--fitz/filt_ahxe.c64
-rw-r--r--fitz/filt_arc4.c47
-rw-r--r--fitz/filt_dctc.h39
-rw-r--r--fitz/filt_dctd.c232
-rw-r--r--fitz/filt_dcte.c254
-rw-r--r--fitz/filt_faxc.h125
-rw-r--r--fitz/filt_faxd.c470
-rw-r--r--fitz/filt_faxd.h61
-rw-r--r--fitz/filt_faxdtab.c931
-rw-r--r--fitz/filt_faxe.c407
-rw-r--r--fitz/filt_faxe.h37
-rw-r--r--fitz/filt_faxetab.c131
-rw-r--r--fitz/filt_flate.c208
-rw-r--r--fitz/filt_jbig2d.c119
-rw-r--r--fitz/filt_jpxd.c146
-rw-r--r--fitz/filt_lzwd.c254
-rw-r--r--fitz/filt_lzwe.c261
-rw-r--r--fitz/filt_null.c54
-rw-r--r--fitz/filt_pipeline.c139
-rw-r--r--fitz/filt_predict.c254
-rw-r--r--fitz/filt_rld.c73
-rw-r--r--fitz/filt_rle.c238
-rw-r--r--fitz/node_misc1.c155
-rw-r--r--fitz/node_misc2.c296
-rw-r--r--fitz/node_optimize.c235
-rw-r--r--fitz/node_path.c347
-rw-r--r--fitz/node_text.c143
-rw-r--r--fitz/node_toxml.c185
-rw-r--r--fitz/node_tree.c108
-rw-r--r--fitz/obj_array.c230
-rw-r--r--fitz/obj_dict.c399
-rw-r--r--fitz/obj_parse.c492
-rw-r--r--fitz/obj_print.c339
-rw-r--r--fitz/obj_simple.c323
-rw-r--r--fitz/res_colorspace.c88
-rw-r--r--fitz/res_font.c408
-rw-r--r--fitz/res_image.c23
-rw-r--r--fitz/res_shade.c29
-rw-r--r--fitz/stm_buffer.c105
-rw-r--r--fitz/stm_filter.c61
-rw-r--r--fitz/stm_misc.c97
-rw-r--r--fitz/stm_open.c242
-rw-r--r--fitz/stm_read.c313
-rw-r--r--fitz/stm_write.c313
-rw-r--r--fitz/util_getopt.c117
-rw-r--r--fitz/util_gettimeofday.c50
-rw-r--r--fitz/util_strlcpy.c65
-rw-r--r--fitz/util_strsep.c11
63 files changed, 11882 insertions, 0 deletions
diff --git a/fitz/Jamfile b/fitz/Jamfile
new file mode 100644
index 00000000..1cfbd3a5
--- /dev/null
+++ b/fitz/Jamfile
@@ -0,0 +1,102 @@
+#
+#
+#
+
+SubDir TOP fitz ;
+
+#
+# The base runtime support, and patching over
+# platform specific wonkyness.
+#
+
+Library libfitz :
+ base_cpudep.c
+ base_error.c
+ base_hash.c
+ base_matrix.c
+ base_memory.c
+ base_rect.c
+ base_rune.c
+ base_cleanname.c
+ ;
+
+if $(NEED_STRLCPY) { Library libfitz : util_strlcpy.c ; }
+if $(NEED_STRSEP) { Library libfitz : util_strsep.c ; }
+if $(NEED_GETOPT) { Library libfitz : util_getopt.c ; }
+
+# MSVC does not have gettimeofday()
+if $(OS) = NT
+{
+ Library libfitz : util_gettimeofday.c ;
+}
+
+#
+# Encryption, carousel-object-system, filters, buffers and streams.
+#
+
+Library libfitz :
+
+ crypt_arc4.c
+ crypt_crc32.c
+ crypt_md5.c
+
+ obj_array.c
+ obj_dict.c
+ obj_parse.c
+ obj_print.c
+ obj_simple.c
+
+ stm_buffer.c
+ stm_filter.c
+ stm_open.c
+ stm_read.c
+ stm_write.c
+ stm_misc.c
+
+ filt_pipeline.c
+ filt_arc4.c
+ filt_null.c
+
+ filt_a85d.c
+ filt_a85e.c
+ filt_ahxd.c
+ filt_ahxe.c
+ filt_dctd.c
+ filt_dcte.c
+ filt_faxd.c
+ filt_faxdtab.c
+ filt_faxe.c
+ filt_faxetab.c
+ filt_flate.c
+ filt_lzwd.c
+ filt_lzwe.c
+ filt_predict.c
+ filt_rld.c
+ filt_rle.c
+
+ ;
+
+if $(HAVE_JASPER) { Library libstream : filt_jpxd.c ; }
+if $(HAVE_JBIG2DEC) { Library libstream : filt_jbig2d.c ; }
+
+#
+# Resources and display tree.
+#
+
+Library libfitz :
+
+ node_toxml.c
+ node_misc1.c
+ node_misc2.c
+ node_optimize.c
+ node_path.c
+ node_text.c
+ node_tree.c
+
+ res_colorspace.c
+ res_font.c
+ res_image.c
+ res_shade.c
+
+ ;
+
diff --git a/fitz/base_cleanname.c b/fitz/base_cleanname.c
new file mode 100644
index 00000000..54764630
--- /dev/null
+++ b/fitz/base_cleanname.c
@@ -0,0 +1,52 @@
+/*
+ * 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/fitz/base_cpudep.c b/fitz/base_cpudep.c
new file mode 100644
index 00000000..51273a59
--- /dev/null
+++ b/fitz/base_cpudep.c
@@ -0,0 +1,326 @@
+/*
+run-time cpu feature detection code
+mm, alphabet soup...
+
+Glenn Kennard <d98gk@efd.lth.se>
+*/
+
+#include "fitz-base.h"
+
+/* global run-time constant */
+unsigned fz_cpuflags = 0;
+
+#ifndef HAVE_CPUDEP
+
+void fz_accelerate(void)
+{
+}
+
+void fz_cpudetect(void)
+{
+}
+
+#else
+
+#ifndef WIN32
+#include <signal.h> /* signal/sigaction */
+#include <setjmp.h> /* sigsetjmp/siglongjmp */
+#endif
+
+/*
+#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)
+
+#ifdef __GNUC__
+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"); }
+*/
+#else
+static void mmx(void)
+{ __asm pand mm0, mm0; }
+
+static void m3dnow(void)
+{ __asm pavgusb mm0, mm0; }
+
+static void mmxext(void) /* aka Extended 3DNow! */
+{ __asm pmaxsw mm0, mm0; }
+
+static void sse(void)
+{ __asm andps xmm0, xmm0; }
+
+static void sse2(void)
+{ __asm andpd xmm0, xmm0; }
+#endif
+
+
+#ifdef ARCH_X86_64
+static void amd64(void)
+#ifdef __GNUC__
+{ __asm__ ("and %rax, %rax\n\t"); }
+#else
+{ __asm and rax, rax; }
+#endif
+#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)
+static void vis(void)
+/*
+Stupidly Sun assembler decides to mark anything using VIS instructions in the
+ELF header, which causes link errors if using the following (which also
+requires passing -xarch=v8plusa|v9a passed to the assembler so it accepts the
+instruction in the first place, v9a for 64 bit binaries):
+
+{ __asm__ ("fand %f8, %f8, %f8\n\t"); }
+
+so instead we just emit the opcode directly, bypassing that check.
+*/
+{ __asm__ (".word 0x91B20E08"); }
+
+/* static void vis2(void)
+{ __asm__ ("edge8n %%l0, %%l0, %%l0\n\t" : : : "%l0"); } */
+
+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 int
+enabled(char *env, const char *ext)
+{
+ int len;
+ char *s;
+ if (!env)
+ return 1;
+ len = strlen(ext);
+ s = env;
+ while ((s = strstr(s, ext)))
+ {
+ int atstart = s == env || *(s-1) == ',' || *(s-1) == ' ';
+ s += len;
+ if (atstart && (*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);
+}
+
+#ifndef WIN32
+
+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);
+}
+
+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 defined(ARCH_X86) || defined(ARCH_X86_64)
+ /* reset mmx/x87 pipeline state */
+ if (features[i].flag & (HAVE_MMX | HAVE_3DNOW | HAVE_MMXEXT)) {
+ __asm__ __volatile__ ("emms\n\t");
+ }
+#endif
+
+ /* 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();
+}
+*/
+
+
+#else /* WIN32 */
+
+void fz_cpudetect(void)
+{
+ static int hasrun = 0;
+
+ unsigned flags = 0;
+ int i;
+ char *env;
+
+ if (hasrun)
+ return;
+ hasrun = 1;
+
+ env = getenv("CPUACCEL");
+
+ for (i = 0; i < sizeof(features) / sizeof(featuretest); i++)
+ {
+ __try
+ {
+ features[i].test();
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* test failed - disable feature */
+ flags &= ~features[i].flag;
+ continue;
+ }
+
+#if defined(ARCH_X86) || defined(ARCH_X86_64)
+ if (features[i].flag & (HAVE_MMX | HAVE_3DNOW | HAVE_MMXEXT)) {
+ /* reset mmx/x87 pipeline state */
+ __asm emms;
+ }
+#endif
+
+ /* if we got here the test succeeded */
+ if (enabled(env, features[i].name))
+ flags |= features[i].flag;
+ else
+ flags &= ~features[i].flag;
+ }
+
+ fz_cpuflags = flags;
+
+ dumpflags();
+}
+
+
+#endif
+
+#endif
+
diff --git a/fitz/base_error.c b/fitz/base_error.c
new file mode 100644
index 00000000..461ffc60
--- /dev/null
+++ b/fitz/base_error.c
@@ -0,0 +1,71 @@
+#include "fitz-base.h"
+
+void
+fz_droperror(fz_error *eo)
+{
+ if (eo->cause)
+ fz_droperror(eo->cause);
+ fz_free(eo);
+}
+
+void
+fz_printerror(fz_error *eo)
+{
+#if 1
+ if (eo->cause)
+ {
+ fz_printerror(eo->cause);
+ fprintf(stderr, "| %s:%d: %s(): %s\n", eo->file, eo->line, eo->func, eo->msg);
+ }
+ else
+ {
+ fprintf(stderr, "+ %s:%d: %s(): %s\n", eo->file, eo->line, eo->func, eo->msg);
+ }
+#else
+ fprintf(stderr, "+ %s:%d: %s(): %s\n", eo->file, eo->line, eo->func, eo->msg);
+ eo = eo->cause;
+
+ while (eo)
+ {
+ fprintf(stderr, "| %s:%d: %s(): %s\n", eo->file, eo->line, eo->func, eo->msg);
+ eo = eo->cause;
+ }
+#endif
+}
+
+
+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_throwimp(fz_error *cause, 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; /* oops. we're *really* out of memory here. */
+
+ va_start(ap, fmt);
+ vsnprintf(eo->msg, sizeof eo->msg, fmt, ap);
+ eo->msg[sizeof(eo->msg) - 1] = '\0';
+ va_end(ap);
+
+ strlcpy(eo->func, func, sizeof eo->func);
+ strlcpy(eo->file, file, sizeof eo->file);
+ eo->line = line;
+
+ eo->cause = cause;
+
+ return eo;
+}
+
diff --git a/fitz/base_hash.c b/fitz/base_hash.c
new file mode 100644
index 00000000..c7086ca9
--- /dev/null
+++ b/fitz/base_hash.c
@@ -0,0 +1,276 @@
+/* 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-base.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_throw("outofmem: hash table struct");
+
+ 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_throw("outofmem: hash table entries (size=%d)", initialsize);
+ }
+
+ memset(table->ents, 0, sizeof(fz_hashentry) * table->size);
+
+ return fz_okay;
+}
+
+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("assert: resize hash too small");
+
+ newents = fz_malloc(sizeof(fz_hashentry) * newsize);
+ if (!newents)
+ return fz_throw("outofmem: hash table (size=%d)", newsize);
+
+ 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 fz_rethrow(error, "cannot re-insert old entries");
+ }
+ }
+ }
+
+ fz_free(oldents);
+
+ return fz_okay;
+}
+
+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 fz_rethrow(error, "cannot resize hash table");
+ }
+
+ 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 fz_okay;
+ }
+
+ if (memcmp(key, &ents[pos].key, table->keylen) == 0)
+ return fz_throw("assert: overwrite hash slot");
+
+ pos = (pos + 1) % size;
+ }
+
+ return fz_okay;
+}
+
+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("assert: 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 fz_okay;
+ }
+
+ 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..364b287c
--- /dev/null
+++ b/fitz/base_matrix.c
@@ -0,0 +1,183 @@
+#include "fitz-base.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;
+ float c;
+
+ while (theta < 0.0f)
+ theta += 360.0f;
+ while (theta >= 360.0f)
+ theta -= 360.0f;
+
+ if (fabs(0.0f - theta) < FLT_EPSILON)
+ {
+ s = 0.0f;
+ c = 1.0f;
+ }
+ else if (fabs(90.0f - theta) < FLT_EPSILON)
+ {
+ s = 1.0f;
+ c = 0.0f;
+ }
+ else if (fabs(180.0f - theta) < FLT_EPSILON)
+ {
+ s = 0.0f;
+ c = -1.0f;
+ }
+ else if (fabs(270.0f - theta) < FLT_EPSILON)
+ {
+ s = -1.0f;
+ c = 0.0f;
+ }
+ else
+ {
+ s = sin(theta * M_PI / 180.0);
+ 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.x0; s.y = r.y0;
+ t.x = r.x0; t.y = r.y1;
+ u.x = r.x1; u.y = r.y1;
+ v.x = r.x1; v.y = r.y0;
+ s = fz_transformpoint(m, s);
+ t = fz_transformpoint(m, t);
+ u = fz_transformpoint(m, u);
+ v = fz_transformpoint(m, v);
+ r.x0 = MIN4(s.x, t.x, u.x, v.x);
+ r.y0 = MIN4(s.y, t.y, u.y, v.y);
+ r.x1 = MAX4(s.x, t.x, u.x, v.x);
+ r.y1 = 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..8c2c0249
--- /dev/null
+++ b/fitz/base_memory.c
@@ -0,0 +1,79 @@
+#include "fitz-base.h"
+
+/* Make this thread local storage if you wish. */
+
+static void *stdmalloc(fz_memorycontext *mem, int n)
+{
+ return malloc(n);
+}
+
+static void *stdrealloc(fz_memorycontext *mem, void *p, int n)
+{
+ return realloc(p, n);
+}
+
+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 = {
+ {"out of memory"},
+ {"<internal>"},
+ {"<internal>"},
+ 0,
+ nil
+};
+
+fz_memorycontext *
+fz_currentmemorycontext()
+{
+ return curmem;
+}
+
+void
+fz_setmemorycontext(fz_memorycontext *mem)
+{
+ curmem = mem;
+}
+
+void *
+fz_malloc(int n)
+{
+ fz_memorycontext *mem = fz_currentmemorycontext();
+ void *p = mem->malloc(mem, n);
+ if (!p)
+ fz_warn("cannot malloc %d bytes", n);
+ return p;
+}
+
+void *
+fz_realloc(void *p, int n)
+{
+ fz_memorycontext *mem = fz_currentmemorycontext();
+ void *np = mem->realloc(mem, p, n);
+ if (np == nil)
+ fz_warn("cannot realloc %d bytes", n);
+ return np;
+}
+
+void
+fz_free(void *p)
+{
+ fz_memorycontext *mem = fz_currentmemorycontext();
+ mem->free(mem, p);
+}
+
+char *
+fz_strdup(char *s)
+{
+ int len = strlen(s);
+ char *ns = fz_malloc(len + 1);
+ if (ns)
+ strcpy(ns, s);
+ return ns;
+}
+
diff --git a/fitz/base_rect.c b/fitz/base_rect.c
new file mode 100644
index 00000000..3c00cb94
--- /dev/null
+++ b/fitz/base_rect.c
@@ -0,0 +1,75 @@
+#include "fitz-base.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.x0 = fz_floor(f.x0);
+ i.y0 = fz_floor(f.y0);
+ i.x1 = fz_ceil(f.x1);
+ i.y1 = fz_ceil(f.y1);
+ 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.x0 = MAX(a.x0, b.x0);
+ r.y0 = MAX(a.y0, b.y0);
+ r.x1 = MIN(a.x1, b.x1);
+ r.y1 = MIN(a.y1, b.y1);
+ return (r.x1 < r.x0 || r.y1 < r.y0) ? 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.x0 = MIN(a.x0, b.x0);
+ r.y0 = MIN(a.y0, b.y0);
+ r.x1 = MAX(a.x1, b.x1);
+ r.y1 = MAX(a.y1, b.y1);
+ 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.x0 = MAX(a.x0, b.x0);
+ r.y0 = MAX(a.y0, b.y0);
+ r.x1 = MIN(a.x1, b.x1);
+ r.y1 = MIN(a.y1, b.y1);
+ return (r.x1 < r.x0 || r.y1 < r.y0) ? 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.x0 = MIN(a.x0, b.x0);
+ r.y0 = MIN(a.y0, b.y0);
+ r.x1 = MAX(a.x1, b.x1);
+ r.y1 = MAX(a.y1, b.y1);
+ return r;
+}
+
diff --git a/fitz/base_rune.c b/fitz/base_rune.c
new file mode 100644
index 00000000..32168792
--- /dev/null
+++ b/fitz/base_rune.c
@@ -0,0 +1,174 @@
+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/crypt_arc4.c b/fitz/crypt_arc4.c
new file mode 100644
index 00000000..fc4d487c
--- /dev/null
+++ b/fitz/crypt_arc4.c
@@ -0,0 +1,100 @@
+/* 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-base.h"
+#include "fitz-stream.h"
+
+void
+fz_arc4init(fz_arc4 *arc4, const unsigned char *key, const 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, const unsigned char *src, const 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/fitz/crypt_crc32.c b/fitz/crypt_crc32.c
new file mode 100644
index 00000000..dded733e
--- /dev/null
+++ b/fitz/crypt_crc32.c
@@ -0,0 +1,86 @@
+/*
+ * Compute the CRC-32 of a data buffer
+ */
+
+#include "fitz-base.h"
+#include "fitz-stream.h"
+
+static const unsigned long crctab[256] =
+{
+ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+ 0x2d02ef8dL
+};
+
+#define DO1(buf) crc = crctab[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8);
+#define DO2(buf) DO1(buf); DO1(buf);
+#define DO4(buf) DO2(buf); DO2(buf);
+#define DO8(buf) DO4(buf); DO4(buf);
+
+unsigned long
+fz_crc32(unsigned long crc, unsigned char *buf, int len)
+{
+ if (buf == nil)
+ return 0L;
+ crc = crc ^ 0xffffffffL;
+ while (len >= 8)
+ {
+ DO8(buf);
+ len -= 8;
+ }
+ if (len)
+ {
+ do { DO1(buf); } while (--len);
+ }
+ return crc ^ 0xffffffffL;
+}
+
diff --git a/fitz/crypt_md5.c b/fitz/crypt_md5.c
new file mode 100644
index 00000000..8f665329
--- /dev/null
+++ b/fitz/crypt_md5.c
@@ -0,0 +1,274 @@
+/*
+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-base.h"
+#include "fitz-stream.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 *, const unsigned long *, const unsigned);
+static void decode(unsigned long *, const unsigned char *, const unsigned);
+static void transform(unsigned long state[4], const 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, const unsigned long *input, const 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, const unsigned char *input, const 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], const 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, const unsigned char *input, const 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/fitz/filt_a85d.c b/fitz/filt_a85d.c
new file mode 100644
index 00000000..19ba9e9a
--- /dev/null
+++ b/fitz/filt_a85d.c
@@ -0,0 +1,128 @@
+#include "fitz-base.h"
+#include "fitz-stream.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 fz_okay;
+}
+
+void
+fz_dropa85d(fz_filter *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("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("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;
+ }
+ return fz_iodone;
+ }
+
+ else if (!iswhite(c)) {
+ return fz_throw("bad data in a85d: '%c'", c);
+ }
+ }
+}
+
diff --git a/fitz/filt_a85e.c b/fitz/filt_a85e.c
new file mode 100644
index 00000000..386d8bb3
--- /dev/null
+++ b/fitz/filt_a85e.c
@@ -0,0 +1,126 @@
+#include "fitz-base.h"
+#include "fitz-stream.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 fz_okay;
+}
+
+void
+fz_dropa85e(fz_filter *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++ = '>';
+ return fz_iodone;
+ }
+
+ else {
+ goto needinput;
+ }
+ }
+
+needinput:
+ if (in->eof) {
+ if (out->wp + 2 > out->ep)
+ return fz_ioneedout;
+ *out->wp++ = '~';
+ *out->wp++ = '>';
+ return fz_iodone;
+ }
+ return fz_ioneedin;
+}
+
diff --git a/fitz/filt_ahxd.c b/fitz/filt_ahxd.c
new file mode 100644
index 00000000..0feb10c2
--- /dev/null
+++ b/fitz/filt_ahxd.c
@@ -0,0 +1,112 @@
+#include "fitz-base.h"
+#include "fitz-stream.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 fz_okay;
+}
+
+void
+fz_dropahxd(fz_filter *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);
+ return fz_iodone;
+ }
+
+ else if (!iswhite(c)) {
+ return fz_throw("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/fitz/filt_ahxe.c b/fitz/filt_ahxe.c
new file mode 100644
index 00000000..0fc727c3
--- /dev/null
+++ b/fitz/filt_ahxe.c
@@ -0,0 +1,64 @@
+#include "fitz-base.h"
+#include "fitz-stream.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 fz_okay;
+}
+
+void
+fz_dropahxe(fz_filter *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++ = '>';
+ return fz_iodone;
+ }
+ return fz_ioneedin;
+}
+
diff --git a/fitz/filt_arc4.c b/fitz/filt_arc4.c
new file mode 100644
index 00000000..af038599
--- /dev/null
+++ b/fitz/filt_arc4.c
@@ -0,0 +1,47 @@
+#include "fitz-base.h"
+#include "fitz-stream.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 fz_okay;
+}
+
+void
+fz_droparc4filter(fz_filter *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/fitz/filt_dctc.h b/fitz/filt_dctc.h
new file mode 100644
index 00000000..72f61ef2
--- /dev/null
+++ b/fitz/filt_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);
+ fz_warn("jpeg error: %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/fitz/filt_dctd.c b/fitz/filt_dctd.c
new file mode 100644
index 00000000..3425b8e5
--- /dev/null
+++ b/fitz/filt_dctd.c
@@ -0,0 +1,232 @@
+#include "fitz-base.h"
+#include "fitz-stream.h"
+
+#include "filt_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; /* "unset" */
+
+ 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("cannot decode jpeg: %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;
+
+ /* speed up jpeg decoding a bit */
+ d->cinfo.dct_method = JDCT_FASTEST;
+ d->cinfo.do_fancy_upsampling = FALSE;
+
+ return fz_okay;
+}
+
+void
+fz_dropdctd(fz_filter *filter)
+{
+ fz_dctd *d = (fz_dctd*)filter;
+ if (setjmp(d->err.jb)) {
+ fz_warn("jpeg error: 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("cannot decode jpeg: %s", d->err.msg);
+ }
+
+ switch (d->stage)
+ {
+ case 0:
+ i = jpeg_read_header(&d->cinfo, TRUE);
+ if (i == JPEG_SUSPENDED)
+ goto needinput;
+
+ /* default value if ColorTransform is not set */
+ if (d->colortransform == -1)
+ {
+ if (d->cinfo.num_components == 3)
+ d->colortransform = 1;
+ else
+ d->colortransform = 0;
+ }
+
+ if (d->cinfo.saw_Adobe_marker)
+ d->colortransform = d->cinfo.Adobe_transform;
+
+ /* Guess the input colorspace, and set output colorspace accordingly */
+ 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;
+ 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/fitz/filt_dcte.c b/fitz/filt_dcte.c
new file mode 100644
index 00000000..5380c6b1
--- /dev/null
+++ b/fitz/filt_dcte.c
@@ -0,0 +1,254 @@
+#include "fitz-base.h"
+#include "fitz-stream.h"
+
+#include "filt_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("missing Columns parameter"); }
+ e->columns = fz_toint(obj);
+
+ obj = fz_dictgets(params, "Rows");
+ if (!obj) { fz_free(e); return fz_throw("missing Rows parameter"); }
+ e->rows = fz_toint(obj);
+
+ obj = fz_dictgets(params, "Colors");
+ if (!obj) { fz_free(e); return fz_throw("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("cannot encode jpeg: %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 fz_okay;
+}
+
+void
+fz_dropdcte(fz_filter *filter)
+{
+ fz_dcte *e = (fz_dcte*)filter;
+
+ if (setjmp(e->err.jb)) {
+ fz_warn("jpeg error: jpeg_destroy_compress: %s", e->err.msg);
+ return;
+ }
+
+ jpeg_destroy_compress(&e->cinfo);
+}
+
+/* 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("jpeg error: jpeg_add_quant_table: %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 fz_okay;
+}
+
+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("cannot encode jpeg: %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->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/fitz/filt_faxc.h b/fitz/filt_faxc.h
new file mode 100644
index 00000000..f11334be
--- /dev/null
+++ b/fitz/filt_faxc.h
@@ -0,0 +1,125 @@
+/* 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)
+ {
+ if (b1)
+ line[a0] |= lm[b0] & rm[b1];
+ }
+ else
+ {
+ line[a0] |= lm[b0];
+ for (a = a0 + 1; a < a1; a++)
+ line[a] = 0xFF;
+ if (b1)
+ line[a1] |= rm[b1];
+ }
+}
+
diff --git a/fitz/filt_faxd.c b/fitz/filt_faxd.c
new file mode 100644
index 00000000..3eaf5f3b
--- /dev/null
+++ b/fitz/filt_faxd.c
@@ -0,0 +1,470 @@
+#include "fitz-base.h"
+#include "fitz-stream.h"
+
+#include "filt_faxd.h"
+#include "filt_faxc.h"
+
+typedef enum fax_stage_e
+{
+ 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) */
+} fax_stage_e;
+
+/* 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;
+
+ fax_stage_e stage;
+
+ int 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_throw("outofmem: scanline buffer one");
+ }
+
+ fax->dst = fz_malloc(fax->stride);
+ if (!fax->dst)
+ {
+ fz_free(fax);
+ fz_free(fax->ref);
+ return fz_throw("outofmem: scanline buffer two");
+ }
+
+ memset(fax->ref, 0, fax->stride);
+ memset(fax->dst, 0, fax->stride);
+
+ return fz_okay;
+}
+
+void
+fz_dropfaxd(fz_filter *p)
+{
+ fz_faxd *fax = (fz_faxd*) p;
+ fz_free(fax->ref);
+ fz_free(fax->dst);
+}
+
+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 fz_okay;
+}
+
+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);
+
+ if (code == UNCOMPRESSED)
+ return fz_throw("uncompressed data in faxd");
+
+ if (code < 0)
+ return fz_throw("negative code in 1d faxd");
+
+ if (fax->a + code > fax->columns)
+ return fz_throw("overflow in 1d 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 fz_okay;
+}
+
+/* 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);
+
+ if (code == UNCOMPRESSED)
+ return fz_throw("uncompressed data in faxd");
+
+ if (code < 0)
+ return fz_throw("negative code in 2d faxd");
+
+ if (fax->a + code > fax->columns)
+ return fz_throw("overflow in 2d 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 fz_okay;
+ }
+
+ code = getcode(fax, cf_2d_decode, cfd_2d_initial_bits);
+
+ switch (code)
+ {
+ case H:
+ fax->stage = SH1;
+ break;
+
+ case P:
+ b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c);
+ b2 = findchanging(fax->ref, b1, fax->columns);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ if (fax->c) setbits(fax->dst, fax->a, b1 - 3);
+ fax->a = b1 - 3;
+ fax->c = !fax->c;
+ break;
+
+ case UNCOMPRESSED:
+ return fz_throw("uncompressed data in faxd");
+
+ case ERROR:
+ return fz_throw("invalid code in 2d faxd");
+
+ default:
+ return fz_throw("invalid code in 2d 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 *error;
+ int i;
+ unsigned char *tmp;
+
+ 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)
+ {
+ eatbits(fax, 12);
+ fax->eolc ++;
+
+ if (fax->k > 0)
+ {
+ if (fax->a == -1)
+ fax->a = 0;
+ if ((fax->word >> (32 - 1)) == 1)
+ fax->dim = 1;
+ else
+ fax->dim = 2;
+ eatbits(fax, 1);
+ }
+ }
+ else if (fax->k > 0 && fax->a == -1)
+ {
+ fax->a = 0;
+ if ((fax->word >> (32 - 1)) == 1)
+ fax->dim = 1;
+ else
+ fax->dim = 2;
+ eatbits(fax, 1);
+ }
+ else if (fax->dim == 1)
+ {
+ fax->eolc = 0;
+ error = dec1d(fax);
+ if (error) return error; /* can be fz_io* or real error */
+
+ }
+ else if (fax->dim == 2)
+ {
+ fax->eolc = 0;
+ error = dec2d(fax);
+ if (error) return error; /* can be fz_io* or real error */
+ }
+
+ /* 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 {
+ unsigned char * restrict d = out->wp;
+ unsigned char * restrict s = fax->dst;
+ unsigned w = fax->stride;
+ for (i = 0; i < w; i++)
+ *d++ = *s++ ^ 0xff;
+ }
+
+ tmp = fax->ref;
+ fax->ref = fax->dst;
+ fax->dst = tmp;
+ 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 && fax->a == -1)
+ {
+ if (fax->ridx % fax->k == 0)
+ fax->dim = 1;
+ else
+ fax->dim = 2;
+ }
+
+ /* 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:
+ i = (32 - fax->bidx) / 8;
+ while (i-- && in->rp > in->bp)
+ in->rp --;
+
+ return fz_iodone;
+}
+
diff --git a/fitz/filt_faxd.h b/fitz/filt_faxd.h
new file mode 100644
index 00000000..9f3fb470
--- /dev/null
+++ b/fitz/filt_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/fitz/filt_faxdtab.c b/fitz/filt_faxdtab.c
new file mode 100644
index 00000000..6efcf053
--- /dev/null
+++ b/fitz/filt_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 "filt_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/fitz/filt_faxe.c b/fitz/filt_faxe.c
new file mode 100644
index 00000000..7551fb80
--- /dev/null
+++ b/fitz/filt_faxe.c
@@ -0,0 +1,407 @@
+#include "fitz-base.h"
+#include "fitz-stream.h"
+
+#include "filt_faxe.h"
+#include "filt_faxc.h"
+
+/* TODO: honor Rows param */
+
+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_throw("outofmemory: scanline buffer one");
+ }
+
+ fax->src = fz_malloc(fax->stride);
+ if (!fax->src)
+ {
+ fz_free(fax);
+ fz_free(fax->ref);
+ return fz_throw("outofmemory: scanline buffer two");
+ }
+
+ memset(fax->ref, 0, fax->stride);
+ memset(fax->src, 0, fax->stride);
+
+ return fz_okay;
+}
+
+void
+fz_dropfaxe(fz_filter *p)
+{
+ fz_faxe *fax = (fz_faxe*) p;
+ fz_free(fax->src);
+ fz_free(fax->ref);
+}
+
+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)
+{
+ 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)
+ {
+ putcode(fax, out, &codetable->makeup[40]);
+ m -= 40;
+ }
+ if (m > 0)
+ {
+ putcode(fax, out, &codetable->makeup[m]);
+ }
+ putcode(fax, out, &codetable->termination[run % 64]);
+ }
+ else
+ {
+ putcode(fax, out, &codetable->termination[run]);
+ }
+}
+
+static fz_error *
+enc1d(fz_faxe *fax, unsigned char *line, fz_buffer *out)
+{
+ int run;
+
+ 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 fz_okay;
+}
+
+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;
+
+ 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);
+
+ /* pass */
+ if (b2 < a1)
+ {
+ if (out->wp + 1 + codebytes > out->ep)
+ return fz_ioneedout;
+
+ 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;
+
+ 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;
+
+ putcode(fax, out, &cf2_run_horizontal);
+ putrun(fax, out, run1, fax->c);
+ putrun(fax, out, run2, !fax->c);
+
+ fax->a0 = a2;
+ }
+ }
+
+ return fz_okay;
+}
+
+static fz_error *
+process(fz_faxe *fax, fz_buffer *in, fz_buffer *out)
+{
+ fz_error *error;
+ 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;
+
+ 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;
+
+ fax->stage ++;
+
+ case 3:
+ error = 0; /* to silence compiler */
+
+ if (fax->k < 0)
+ error = enc2d(fax, fax->ref, fax->src, out);
+
+ else if (fax->k == 0)
+ error = enc1d(fax, fax->src, out);
+
+ else if (fax->k > 0)
+ {
+ if (fax->ridx % fax->k == 0)
+ error = enc1d(fax, fax->src, out);
+ else
+ error = enc2d(fax, fax->ref, fax->src, out);
+ }
+
+ if (error)
+ return error; /* one of fz_io* */
+
+ 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);
+ }
+ }
+
+ if (fax->bidx)
+ out->wp ++;
+
+ return fz_iodone;
+}
+
+fz_error *
+fz_processfaxe(fz_filter *p, fz_buffer *in, fz_buffer *out)
+{
+ fz_faxe *fax = (fz_faxe*) p;
+ fz_error *error;
+
+ /* restore partial bits */
+ *out->wp = fax->bsave;
+
+ error = process(fax, in, out);
+
+ /* save partial bits */
+ fax->bsave = *out->wp;
+
+ return error;
+}
+
diff --git a/fitz/filt_faxe.h b/fitz/filt_faxe.h
new file mode 100644
index 00000000..dd3fc121
--- /dev/null
+++ b/fitz/filt_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/fitz/filt_faxetab.c b/fitz/filt_faxetab.c
new file mode 100644
index 00000000..34914c2d
--- /dev/null
+++ b/fitz/filt_faxetab.c
@@ -0,0 +1,131 @@
+/* Tables for CCITTFaxEncode filter */
+
+#include "filt_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/fitz/filt_flate.c b/fitz/filt_flate.c
new file mode 100644
index 00000000..6a86da3a
--- /dev/null
+++ b/fitz/filt_flate.c
@@ -0,0 +1,208 @@
+#include "fitz-base.h"
+#include "fitz-stream.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)
+{
+ return fz_malloc(items * size);
+}
+
+fz_error *
+fz_newflated(fz_filter **fp, fz_obj *params)
+{
+ fz_error *eo;
+ fz_obj *obj;
+ int zipfmt;
+ 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;
+
+ zipfmt = 0;
+
+ if (params)
+ {
+ obj = fz_dictgets(params, "ZIP");
+ if (obj) zipfmt = fz_tobool(obj);
+ }
+
+ if (zipfmt)
+ {
+ /* if windowbits is negative the zlib header is skipped */
+ ei = inflateInit2(&f->z, -15);
+ }
+ else
+ ei = inflateInit(&f->z);
+
+ if (ei != Z_OK)
+ {
+ eo = fz_throw("zlib error: inflateInit: %s", f->z.msg);
+ fz_free(f);
+ return eo;
+ }
+
+ return fz_okay;
+}
+
+void
+fz_dropflated(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_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 || err == Z_BUF_ERROR)
+ {
+ 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("zlib error: inflate: %s", zp->msg);
+ }
+}
+
+fz_error *
+fz_newflatee(fz_filter **fp, fz_obj *params)
+{
+ fz_obj *obj;
+ fz_error *eo;
+ int effort;
+ int zipfmt;
+ int ei;
+
+ FZ_NEWFILTER(fz_flate, f, flatee);
+
+ effort = -1;
+ zipfmt = 0;
+
+ if (params)
+ {
+ obj = fz_dictgets(params, "Effort");
+ if (obj) effort = fz_toint(obj);
+ obj = fz_dictgets(params, "ZIP");
+ if (obj) zipfmt = fz_tobool(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;
+
+ if (zipfmt)
+ ei = deflateInit2(&f->z, effort, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
+ else
+ ei = deflateInit(&f->z, effort);
+
+ if (ei != Z_OK)
+ {
+ eo = fz_throw("zlib error: deflateInit: %s", f->z.msg);
+ fz_free(f);
+ return eo;
+ }
+
+ return fz_okay;
+}
+
+void
+fz_dropflatee(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)
+ {
+ 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("zlib error: deflate: %s", zp->msg);
+ }
+}
+
diff --git a/fitz/filt_jbig2d.c b/fitz/filt_jbig2d.c
new file mode 100644
index 00000000..3569d3eb
--- /dev/null
+++ b/fitz/filt_jbig2d.c
@@ -0,0 +1,119 @@
+#include "fitz-base.h"
+#include "fitz-stream.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
+*/
+
+#ifdef WIN32 /* Microsoft Visual C++ */
+
+typedef signed char int8_t;
+typedef short int int16_t;
+typedef int int32_t;
+typedef __int64 int64_t;
+
+typedef unsigned char uint8_t;
+typedef unsigned short int uint16_t;
+typedef unsigned int uint32_t;
+
+#else
+#include <inttypes.h>
+#endif
+
+#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 fz_okay;
+}
+
+void
+fz_dropjbig2d(fz_filter *filter)
+{
+ fz_jbig2d *d = (fz_jbig2d*)filter;
+ jbig2_ctx_free(d->ctx);
+}
+
+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 fz_okay;
+}
+
+fz_error *
+fz_processjbig2d(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_jbig2d *d = (fz_jbig2d*)filter;
+ int len;
+ int i;
+
+ 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;
+
+ /* XXX memcpy(out->wp, d->page->data + d->idx, len); */
+ {
+ unsigned char * restrict in = &d->page->data[d->idx];
+ unsigned char * restrict o = out->wp;
+ for (i = 0; i < len; i++)
+ *o++ = 0xff ^ *in++;
+ }
+
+ out->wp += len;
+ d->idx += len;
+
+ if (d->idx == d->page->height * d->page->stride) {
+ jbig2_release_page(d->ctx, d->page);
+ return fz_iodone;
+ }
+ }
+ else {
+ len = in->wp - in->rp;
+ jbig2_data_in(d->ctx, in->rp, len);
+ in->rp += len;
+ }
+ }
+}
+
diff --git a/fitz/filt_jpxd.c b/fitz/filt_jpxd.c
new file mode 100644
index 00000000..28dc87de
--- /dev/null
+++ b/fitz/filt_jpxd.c
@@ -0,0 +1,146 @@
+#include "fitz-base.h"
+#include "fitz-stream.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("jasper error: jas_init()");
+ }
+
+ d->stream = jas_stream_memopen(nil, 0);
+ if (!d->stream)
+ {
+ fz_free(d);
+ return fz_throw("jasper error: jas_stream_memopen()");
+ }
+
+ d->image = nil;
+ d->offset = 0;
+ d->stage = 0;
+
+ return fz_okay;
+}
+
+void
+fz_dropjpxd(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_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("jasper error: jas_image_decode()");
+
+ 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 ++;
+ }
+
+ return fz_iodone;
+}
+
diff --git a/fitz/filt_lzwd.c b/fitz/filt_lzwd.c
new file mode 100644
index 00000000..c4aed45b
--- /dev/null
+++ b/fitz/filt_lzwd.c
@@ -0,0 +1,254 @@
+#include "fitz-base.h"
+#include "fitz-stream.h"
+
+/* TODO: error checking */
+
+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 = 256; 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 fz_okay;
+}
+
+void
+fz_droplzwd(fz_filter *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 fz_okay;
+}
+
+static inline void unstuff(fz_lzwd *lzw, fz_buffer *in)
+{
+ int i = (32 - lzw->bidx) / 8;
+ while (i-- && in->rp > in->bp)
+ in->rp --;
+}
+
+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))
+ {
+ if (in->eof)
+ {
+ if (lzw->bidx > 32 - lzw->codebits)
+ {
+ unstuff(lzw, in);
+ return fz_iodone;
+ }
+ }
+ else
+ {
+ return fz_ioneedin;
+ }
+ }
+
+ lzw->code = lzw->word >> (32 - lzw->codebits);
+
+ if (lzw->code == LZW_EOD)
+ {
+ eatbits(lzw, lzw->codebits);
+ unstuff(lzw, in);
+ return fz_iodone;
+ }
+
+ if (lzw->code == LZW_CLEAR)
+ {
+ int oldcodebits = lzw->codebits;
+
+ lzw->codebits = MINBITS;
+ lzw->nextcode = LZW_FIRST;
+
+ lzw->code = lzw->word >> (32 - oldcodebits - MINBITS) & ((1 << MINBITS) - 1);
+
+ if (lzw->code == LZW_EOD)
+ {
+ eatbits(lzw, oldcodebits + MINBITS);
+ unstuff(lzw, in);
+ return fz_iodone;
+ }
+
+ eatbits(lzw, oldcodebits + MINBITS);
+
+ lzw->oldcode = lzw->code;
+
+ if (out->wp + 1 > out->ep)
+ {
+ lzw->resume = 1;
+ return fz_ioneedout;
+ }
+
+ *out->wp++ = lzw->code;
+
+ 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;
+
+ 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;
+
+ do
+ {
+ *(--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;
+ }
+
+ *out->wp++ = lzw->code;
+ }
+ }
+}
+
diff --git a/fitz/filt_lzwe.c b/fitz/filt_lzwe.c
new file mode 100644
index 00000000..5a221f9f
--- /dev/null
+++ b/fitz/filt_lzwe.c
@@ -0,0 +1,261 @@
+#include "fitz-base.h"
+#include "fitz-stream.h"
+
+#define noDEBUG 1
+
+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 fz_okay;
+}
+
+void
+fz_droplzwe(fz_filter *filter)
+{
+}
+
+static void
+putcode(fz_lzwe *lzw, fz_buffer *out, int code)
+{
+ int nbits = lzw->codebits;
+
+ 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++;
+
+ /* 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);
+
+ return fz_iodone;
+}
+
+fz_error *
+fz_processlzwe(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_lzwe *lzw = (fz_lzwe*)filter;
+ fz_error *error;
+
+ /* restore partial bits */
+ *out->wp = lzw->bsave;
+
+ error = compress(lzw, in, out);
+
+ /* save partial bits */
+ lzw->bsave = *out->wp;
+
+ return error;
+}
+
diff --git a/fitz/filt_null.c b/fitz/filt_null.c
new file mode 100644
index 00000000..601ed815
--- /dev/null
+++ b/fitz/filt_null.c
@@ -0,0 +1,54 @@
+#include "fitz-base.h"
+#include "fitz-stream.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 fz_okay;
+}
+
+void
+fz_dropnullfilter(fz_filter *f)
+{
+}
+
+fz_error *
+fz_processnullfilter(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_nullfilter *f = (fz_nullfilter*)filter;
+ int n;
+
+ n = MIN(in->wp - in->rp, out->ep - out->wp);
+ if (f->len >= 0)
+ n = MIN(n, 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 trapped in nullfilter");
+}
+
diff --git a/fitz/filt_pipeline.c b/fitz/filt_pipeline.c
new file mode 100644
index 00000000..ea0b9885
--- /dev/null
+++ b/fitz/filt_pipeline.c
@@ -0,0 +1,139 @@
+#include "fitz-base.h"
+#include "fitz-stream.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 = fz_keepfilter(head);
+ p->tail = fz_keepfilter(tail);
+ p->tailneedsin = 1;
+ p->buffer = fz_keepbuffer(buf);
+ return fz_okay;
+}
+
+void
+fz_unchainpipeline(fz_filter *filter, fz_filter **oldfp, fz_buffer **oldbp)
+{
+ fz_pipeline *p = (fz_pipeline*)filter;
+
+ *oldfp = fz_keepfilter(p->head);
+ *oldbp = fz_keepbuffer(p->buffer);
+
+ fz_dropfilter(filter);
+}
+
+fz_error *
+fz_newpipeline(fz_filter **fp, fz_filter *head, fz_filter *tail)
+{
+ fz_error *error;
+
+ FZ_NEWFILTER(fz_pipeline, p, pipeline);
+ p->head = fz_keepfilter(head);
+ p->tail = fz_keepfilter(tail);
+ p->tailneedsin = 1;
+
+ error = fz_newbuffer(&p->buffer, FZ_BUFSIZE);
+ if (error)
+ {
+ fz_free(p);
+ return fz_rethrow(error, "cannot create buffer");
+ }
+
+ return fz_okay;
+}
+
+void
+fz_droppipeline(fz_filter *filter)
+{
+ fz_pipeline *p = (fz_pipeline*)filter;
+ fz_dropfilter(p->head);
+ fz_dropfilter(p->tail);
+ fz_dropbuffer(p->buffer);
+}
+
+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 if (e)
+ return fz_rethrow(e, "cannot process head filter");
+
+ else
+ return fz_okay;
+
+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 if (e)
+ return fz_rethrow(e, "cannot process tail filter");
+
+ else
+ return fz_okay;
+}
+
diff --git a/fitz/filt_predict.c b/fitz/filt_predict.c
new file mode 100644
index 00000000..4654e792
--- /dev/null
+++ b/fitz/filt_predict.c
@@ -0,0 +1,254 @@
+#include "fitz-base.h"
+#include "fitz-stream.h"
+
+/* TODO: check if this works with 16bpp images */
+
+enum { MAXC = 32 };
+
+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_throw("outofmem: scanline buffer");
+ }
+ memset(p->ref, 0, p->stride);
+ }
+ else
+ {
+ p->ref = nil;
+ }
+
+ return fz_okay;
+}
+
+void
+fz_droppredict(fz_filter *filter)
+{
+ fz_predict *p = (fz_predict*)filter;
+ fz_free(p->ref);
+}
+
+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/fitz/filt_rld.c b/fitz/filt_rld.c
new file mode 100644
index 00000000..9b0ed75f
--- /dev/null
+++ b/fitz/filt_rld.c
@@ -0,0 +1,73 @@
+#include "fitz-base.h"
+#include "fitz-stream.h"
+
+fz_error *
+fz_newrld(fz_filter **fp, fz_obj *params)
+{
+ FZ_NEWFILTER(fz_filter, f, rld);
+ return fz_okay;
+}
+
+void
+fz_droprld(fz_filter *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)
+ {
+ if (in->eof)
+ {
+ return fz_iodone;
+ }
+ return fz_ioneedin;
+ }
+
+ if (out->wp == out->ep)
+ return fz_ioneedout;
+
+ run = *in->rp++;
+
+ if (run == 128)
+ {
+ 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/fitz/filt_rle.c b/fitz/filt_rle.c
new file mode 100644
index 00000000..65c2a7a4
--- /dev/null
+++ b/fitz/filt_rle.c
@@ -0,0 +1,238 @@
+#include "fitz-base.h"
+#include "fitz-stream.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 fz_okay;
+}
+
+void
+fz_droprle(fz_filter *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 fz_okay;
+}
+
+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 fz_okay;
+}
+
+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 fz_okay;
+}
+
+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 fz_okay;
+}
+
+static fz_error *
+savebuf(fz_rle *enc, fz_buffer *in, fz_buffer *out)
+{
+ switch (enc->state)
+ {
+ case ZERO: return fz_okay;
+ 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 fz_okay;
+ }
+}
+
+fz_error *
+fz_processrle(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_rle *enc = (fz_rle*)filter;
+ fz_error *error;
+ unsigned char c;
+
+ while (1)
+ {
+
+ if (enc->reclen && enc->curlen == enc->reclen) {
+ error = savebuf(enc, in, out);
+ if (error) return error;
+#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) {
+ error = savebuf(enc, in, out);
+ if (error) return error;
+ }
+ 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) {
+ error = putdiff(enc, in, out);
+ if (error) return error;
+
+ 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 */
+ error = putdiff(enc, in, out);
+ if (error) return error;
+ }
+
+ 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]) {
+ error = putsame(enc, in, out);
+ if (error) return error;
+
+ enc->state = ONE;
+ enc->run = 1;
+ enc->buf[0] = c;
+ }
+ else {
+ enc->run ++;
+ }
+ break;
+
+ case END:
+ error = puteod(enc, in, out);
+ if (error)
+ return error;
+
+ return fz_iodone;
+ }
+
+ in->rp ++;
+
+ enc->curlen ++;
+
+ }
+}
+
diff --git a/fitz/node_misc1.c b/fitz/node_misc1.c
new file mode 100644
index 00000000..fc8407d5
--- /dev/null
+++ b/fitz/node_misc1.c
@@ -0,0 +1,155 @@
+#include "fitz-base.h"
+#include "fitz-world.h"
+
+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_boundsolidnode(fz_solidnode* 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);
+
+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;
+ }
+
+ 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_boundsolidnode((fz_solidnode *) 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);
+ }
+ 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_issolidnode(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;
+}
+
diff --git a/fitz/node_misc2.c b/fitz/node_misc2.c
new file mode 100644
index 00000000..b83678d1
--- /dev/null
+++ b/fitz/node_misc2.c
@@ -0,0 +1,296 @@
+#include "fitz-base.h"
+#include "fitz-world.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 fz_okay;
+}
+
+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 fz_okay;
+}
+
+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_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->mode = b;
+ node->knockout = k;
+ node->isolated = i;
+
+ return fz_okay;
+}
+
+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 fz_okay;
+}
+
+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));
+}
+
+/*
+ * 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 fz_okay;
+}
+
+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_newsolidnode(fz_node **nodep, float a, fz_colorspace *cs, int n, float *v)
+{
+ fz_solidnode *node;
+ int i;
+
+ node = fz_malloc(sizeof(fz_solidnode) + sizeof(float) * n);
+ if (!node)
+ return fz_outofmem;
+ *nodep = (fz_node*)node;
+
+ fz_initnode((fz_node*)node, FZ_NCOLOR);
+ node->a = a;
+ node->cs = fz_keepcolorspace(cs);
+ node->n = n;
+ for (i = 0; i < n; i++)
+ node->samples[i] = v[i];
+
+ return fz_okay;
+}
+
+fz_rect
+fz_boundsolidnode(fz_solidnode *node, fz_matrix ctm)
+{
+ return fz_infiniterect;
+}
+
+void
+fz_dropsolidnode(fz_solidnode *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 fz_okay;
+}
+
+void
+fz_dropimagenode(fz_imagenode *node)
+{
+ fz_dropimage(node->image);
+}
+
+fz_rect
+fz_boundimagenode(fz_imagenode *node, fz_matrix ctm)
+{
+ fz_rect bbox;
+ bbox.x0 = 0;
+ bbox.y0 = 0;
+ bbox.x1 = 1;
+ bbox.y1 = 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 fz_okay;
+}
+
+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..cb3918e1
--- /dev/null
+++ b/fitz/node_optimize.c
@@ -0,0 +1,235 @@
+#include "fitz-base.h"
+#include "fitz-world.h"
+
+/*
+ * Remove (mask ... white) until we get something not white
+ */
+
+static int iswhitenode(fz_solidnode *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_issolidnode(current))
+ {
+ if (!iswhitenode((fz_solidnode*)current))
+ return 1;
+ }
+
+ else if (fz_ismasknode(current))
+ {
+ shape = current->first;
+ color = shape->next;
+ if (fz_issolidnode(color))
+ {
+ if (iswhitenode((fz_solidnode*)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->x0 = MIN(x, x + w);
+ bboxp->y0 = MIN(y, y + h);
+ bboxp->x1 = MAX(x, x + w);
+ bboxp->y1 = 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.x0 < clip.x0) return 0;
+ if (bbox.x1 > clip.x1) return 0;
+ if (bbox.y0 < clip.y0) return 0;
+ if (bbox.y1 > clip.y1) 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 (!current)
+ break;
+
+ 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;
+ }
+}
+
+/*
+ *
+ */
+
+fz_error *
+fz_optimizetree(fz_tree *tree)
+{
+ if (getenv("DONTOPT"))
+ return fz_okay;
+ cleanwhite(tree->root);
+ cleanovers(tree->root);
+ cleanmasks(tree->root);
+ return fz_okay;
+}
+
diff --git a/fitz/node_path.c b/fitz/node_path.c
new file mode 100644
index 00000000..b5aea542
--- /dev/null
+++ b/fitz/node_path.c
@@ -0,0 +1,347 @@
+#include "fitz-base.h"
+#include "fitz-world.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 fz_okay;
+}
+
+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 fz_okay;
+}
+
+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 fz_okay;
+}
+
+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 fz_okay;
+}
+
+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 fz_okay;
+}
+
+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 fz_okay;
+}
+
+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 fz_okay;
+}
+
+fz_error *
+fz_endpath(fz_pathnode *path, fz_pathkind paint, fz_stroke *stroke, fz_dash *dash)
+{
+ if (path->len == 0)
+ fz_warn("creating an empty path");
+
+ 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 fz_okay;
+}
+
+static inline fz_rect boundexpand(fz_rect r, fz_point p)
+{
+ if (p.x < r.x0) r.x0 = p.x;
+ if (p.y < r.y0) r.y0 = p.y;
+ if (p.x > r.x1) r.x1 = p.x;
+ if (p.y > r.y1) r.y1 = 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.x0 = r.x1 = p.x;
+ r.y0 = r.y1 = 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.x0 -= expand;
+ r.y0 -= expand;
+ r.x1 += expand;
+ r.y1 += expand;
+ }
+
+ return r;
+}
+
+void
+fz_printpathnode(fz_pathnode *path, int indent)
+{
+ float x, y;
+ int i = 0;
+ int n;
+ while (i < path->len)
+ {
+ for (n = 0; n < indent; n++)
+ putchar(' ');
+ 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");
+ }
+ }
+
+ for (n = 0; n < indent; n++)
+ putchar(' ');
+
+ switch (path->paint)
+ {
+ case FZ_STROKE:
+ printf("S\n");
+ break;
+ case FZ_FILL:
+ printf("f\n");
+ break;
+ case FZ_EOFILL:
+ printf("f*\n");
+ break;
+ }
+}
+
+void
+fz_debugpathnode(fz_pathnode *path, int indent)
+{
+ float x, y;
+ int i = 0;
+ int n;
+ while (i < path->len)
+ {
+ for (n = 0; n < indent; n++)
+ putchar(' ');
+ switch (path->els[i++].k)
+ {
+ case FZ_MOVETO:
+ x = path->els[i++].v;
+ y = path->els[i++].v;
+ printf("<moveto x=\"%g\" y=\"%g\" />\n", x, y);
+ break;
+ case FZ_LINETO:
+ x = path->els[i++].v;
+ y = path->els[i++].v;
+ printf("<lineto x=\"%g\" y=\"%g\" />\n", x, y);
+ break;
+ case FZ_CURVETO:
+ x = path->els[i++].v;
+ y = path->els[i++].v;
+ printf("<curveto x1=\"%g\" y1=\"%g\" ", x, y);
+ x = path->els[i++].v;
+ y = path->els[i++].v;
+ printf("x2=\"%g\" y2=\"%g\" ", x, y);
+ x = path->els[i++].v;
+ y = path->els[i++].v;
+ printf("x3=\"%g\" y3=\"%g\" />\n", x, y);
+ break;
+ case FZ_CLOSEPATH:
+ printf("<closepath />\n");
+ }
+ }
+}
+
+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 fz_okay;
+}
+
+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..86a523df
--- /dev/null
+++ b/fitz/node_text.c
@@ -0,0 +1,143 @@
+#include "fitz-base.h"
+#include "fitz-world.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 fz_okay;
+}
+
+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 fz_okay;
+}
+
+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.x0 = bbox.x1 = text->els[0].x;
+ bbox.y0 = bbox.y1 = text->els[0].y;
+
+ for (i = 1; i < text->len; i++)
+ {
+ bbox.x0 = MIN(bbox.x0, text->els[i].x);
+ bbox.y0 = MIN(bbox.y0, text->els[i].y);
+ bbox.x1 = MAX(bbox.x1, text->els[i].x);
+ bbox.y1 = MAX(bbox.y1, 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.x0 = text->font->bbox.x0 * 0.001;
+ fbox.y0 = text->font->bbox.y0 * 0.001;
+ fbox.x1 = text->font->bbox.x1 * 0.001;
+ fbox.y1 = text->font->bbox.y1 * 0.001;
+
+ fbox = fz_transformaabb(trm, fbox);
+
+ /* expand glyph origin bbox by font bbox */
+
+ bbox.x0 += fbox.x0;
+ bbox.y0 += fbox.y0;
+ bbox.x1 += fbox.x1;
+ bbox.y1 += fbox.y1;
+
+ 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 fz_okay;
+}
+
+fz_error *
+fz_addtext(fz_textnode *text, int gid, int ucs, float x, float y)
+{
+ if (growtext(text, 1) != nil)
+ return fz_outofmem;
+ text->els[text->len].ucs = ucs;
+ text->els[text->len].gid = gid;
+ text->els[text->len].x = x;
+ text->els[text->len].y = y;
+ text->len++;
+ return fz_okay;
+}
+
diff --git a/fitz/node_toxml.c b/fitz/node_toxml.c
new file mode 100644
index 00000000..cc87c997
--- /dev/null
+++ b/fitz/node_toxml.c
@@ -0,0 +1,185 @@
+#include "fitz-base.h"
+#include "fitz-world.h"
+
+static void indent(int level)
+{
+ while (level--)
+ putchar(' ');
+}
+
+static void xmlnode(fz_node *node, int level);
+
+static void xmlover(fz_overnode *node, int level)
+{
+ fz_node *child;
+ indent(level);
+ printf("<over>\n");
+ for (child = node->super.first; child; child = child->next)
+ xmlnode(child, level + 1);
+ indent(level);
+ printf("</over>\n");
+}
+
+static void xmlmask(fz_masknode *node, int level)
+{
+ fz_node *child;
+ indent(level);
+ printf("<mask>\n");
+ for (child = node->super.first; child; child = child->next)
+ xmlnode(child, level + 1);
+ indent(level);
+ printf("</mask>\n");
+}
+
+static void xmlblend(fz_blendnode *node, int level)
+{
+ fz_node *child;
+ indent(level);
+ printf("<blend mode=\"%d\">\n", node->mode);
+ for (child = node->super.first; child; child = child->next)
+ xmlnode(child, level + 1);
+ indent(level);
+ printf("</blend>\n");
+}
+
+static void xmltransform(fz_transformnode *node, int level)
+{
+ indent(level);
+ printf("<transform matrix=\"%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);
+ xmlnode(node->super.first, level + 1);
+ indent(level);
+ printf("</transform>\n");
+}
+
+static void xmlsolid(fz_solidnode *node, int level)
+{
+ int i;
+ indent(level);
+ printf("<solid colorspace=\"%s\" alpha=\"%g\" v=\"", node->cs->name, node->a);
+ for (i = 0; i < node->n; i++)
+ {
+ printf("%g", node->samples[i]);
+ if (i < node->n - 1)
+ putchar(' ');
+ }
+ printf("\" />\n");
+}
+
+static void xmllink(fz_linknode *node, int level)
+{
+ indent(level);
+ printf("<link name=\"%p\" />\n", node->tree);
+}
+
+static void xmlpath(fz_pathnode *node, int level)
+{
+ int i;
+
+ indent(level);
+
+ if (node->paint == FZ_STROKE)
+ {
+ printf("<path fill=\"stroke\" cap=\"%d\" join=\"%d\" width=\"%g\" miter=\"%g\"",
+ node->linecap,
+ node->linejoin,
+ node->linewidth,
+ node->miterlimit);
+ if (node->dash)
+ {
+ printf(" phase=\"%g\" array=\"", node->dash->phase);
+ for (i = 0; i < node->dash->len; i++)
+ printf("%g ", node->dash->array[i]);
+ printf("\"");
+ }
+ printf(">\n");
+ }
+ else
+ {
+ printf("<path fill=\"%s\">\n",
+ node->paint == FZ_FILL ? "nonzero" : "evenodd");
+ }
+
+ fz_debugpathnode(node, level + 2);
+
+ indent(level);
+ printf("</path>\n");
+}
+
+static void xmltext(fz_textnode *node, int level)
+{
+ int i;
+
+ indent(level);
+ printf("<text font=\"%s\" matrix=\"%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].ucs >= 32 && node->els[i].ucs < 128)
+ printf("<g ucs=\"%c\" gid=%d x=\"%g\" y=\"%g\" />\n",
+ node->els[i].ucs, node->els[i].gid, node->els[i].x, node->els[i].y);
+ else
+ printf("<g ucs=\"U+%04X\" gid=%d x=\"%g\" y=\"%g\" />\n",
+ node->els[i].ucs, node->els[i].gid, node->els[i].x, node->els[i].y);
+ }
+
+ indent(level);
+ printf("</text>\n");
+}
+
+static void xmlimage(fz_imagenode *node, int level)
+{
+ fz_image *image = node->image;
+ indent(level);
+ printf("<image w=\"%d\" h=\"%d\" n=\"%d\" a=\"%d\" />\n",
+ image->w, image->h, image->n, image->a);
+}
+
+static void xmlshade(fz_shadenode *node, int level)
+{
+ indent(level);
+ printf("<shade />\n");
+}
+
+static void xmlnode(fz_node *node, int level)
+{
+ if (!node)
+ {
+ indent(level);
+ printf("<nil />\n");
+ return;
+ }
+
+ switch (node->kind)
+ {
+ case FZ_NOVER: xmlover((fz_overnode*)node, level); break;
+ case FZ_NMASK: xmlmask((fz_masknode*)node, level); break;
+ case FZ_NBLEND: xmlblend((fz_blendnode*)node, level); break;
+ case FZ_NTRANSFORM: xmltransform((fz_transformnode*)node, level); break;
+ case FZ_NCOLOR: xmlsolid((fz_solidnode*)node, level); break;
+ case FZ_NPATH: xmlpath((fz_pathnode*)node, level); break;
+ case FZ_NTEXT: xmltext((fz_textnode*)node, level); break;
+ case FZ_NIMAGE: xmlimage((fz_imagenode*)node, level); break;
+ case FZ_NSHADE: xmlshade((fz_shadenode*)node, level); break;
+ case FZ_NLINK: xmllink((fz_linknode*)node, level); break;
+ }
+}
+
+void
+fz_debugnode(fz_node *node)
+{
+ xmlnode(node, 0);
+}
+
+void
+fz_debugtree(fz_tree *tree)
+{
+ printf("<tree>\n");
+ xmlnode(tree->root, 1);
+ printf("</tree>\n");
+}
+
diff --git a/fitz/node_tree.c b/fitz/node_tree.c
new file mode 100644
index 00000000..f5698bc1
--- /dev/null
+++ b/fitz/node_tree.c
@@ -0,0 +1,108 @@
+#include "fitz-base.h"
+#include "fitz-world.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 fz_okay;
+}
+
+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/obj_array.c b/fitz/obj_array.c
new file mode 100644
index 00000000..3953470a
--- /dev/null
+++ b/fitz/obj_array.c
@@ -0,0 +1,230 @@
+#include "fitz-base.h"
+#include "fitz-stream.h"
+
+void fz_droparray(fz_obj *obj);
+
+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_throw("outofmem: array struct");
+
+ obj->refs = 1;
+ obj->kind = FZ_ARRAY;
+
+ 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_throw("outofmem: array item buffer");
+ }
+
+ for (i = 0; i < obj->u.a.cap; i++)
+ obj->u.a.items[i] = nil;
+
+ return fz_okay;
+}
+
+fz_error *
+fz_copyarray(fz_obj **op, fz_obj *obj)
+{
+ fz_error *error;
+ fz_obj *new;
+ int i;
+
+ if (!fz_isarray(obj))
+ return fz_throw("assert: not an array (%s)", fz_objkindstr(obj));
+
+ error = fz_newarray(&new, fz_arraylen(obj));
+ if (error)
+ return fz_rethrow(error, "cannot create new array");
+
+ for (i = 0; i < fz_arraylen(obj); i++)
+ {
+ error = fz_arraypush(new, fz_arrayget(obj, i));
+ if (error)
+ {
+ fz_droparray(new);
+ return fz_rethrow(error, "cannot add item to array");
+ }
+ }
+
+ *op = new;
+
+ return fz_okay;
+}
+
+fz_error *
+fz_deepcopyarray(fz_obj **op, fz_obj *obj)
+{
+ fz_error *error;
+ fz_obj *new;
+ fz_obj *val;
+ int i;
+
+ if (!fz_isarray(obj))
+ return fz_throw("assert: not an array (%s)", fz_objkindstr(obj));
+
+ error = fz_newarray(&new, fz_arraylen(obj));
+ if (error)
+ return fz_rethrow(error, "cannot create new array");
+
+ for (i = 0; i < fz_arraylen(obj); i++)
+ {
+ val = fz_arrayget(obj, i);
+
+ if (fz_isarray(val))
+ {
+ error = fz_deepcopyarray(&val, val);
+ if (error)
+ {
+ fz_droparray(new);
+ return fz_rethrow(error, "cannot deep copy item");
+ }
+
+ error = fz_arraypush(new, val);
+ if (error)
+ {
+ fz_dropobj(val);
+ fz_droparray(new);
+ return fz_rethrow(error, "cannot add copied item to array");
+ }
+
+ fz_dropobj(val);
+ }
+
+ else if (fz_isdict(val))
+ {
+ error = fz_deepcopydict(&val, val);
+ if (error)
+ {
+ fz_droparray(new);
+ return fz_rethrow(error, "cannot deep copy item");
+ }
+
+ error = fz_arraypush(new, val);
+ if (error)
+ {
+ fz_dropobj(val);
+ fz_droparray(new);
+ return fz_rethrow(error, "cannot add copied item to array");
+ }
+ fz_dropobj(val);
+ }
+
+ else
+ {
+ error = fz_arraypush(new, val);
+ if (error)
+ {
+ fz_droparray(new);
+ return fz_rethrow(error, "cannot add copied item to array");
+ }
+ }
+ }
+
+ *op = new;
+
+ return fz_okay;
+}
+
+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("assert: not an array (%s)", fz_objkindstr(obj));
+ if (i < 0)
+ return fz_throw("assert: index %d < 0", i);
+ if (i >= obj->u.a.len)
+ return fz_throw("assert: index %d > length %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 fz_okay;
+}
+
+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_throw("outofmem: resize item buffer");
+
+ 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 fz_okay;
+}
+
+fz_error *
+fz_arraypush(fz_obj *obj, fz_obj *item)
+{
+ fz_error *error;
+
+ if (!fz_isarray(obj))
+ return fz_throw("assert: not an array (%s)", fz_objkindstr(obj));
+
+ if (obj->u.a.len + 1 > obj->u.a.cap)
+ {
+ error = growarray(obj);
+ if (error)
+ return fz_rethrow(error, "cannot grow item buffer");
+ }
+
+ obj->u.a.items[obj->u.a.len] = fz_keepobj(item);
+ obj->u.a.len++;
+
+ return fz_okay;
+}
+
+void
+fz_droparray(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/fitz/obj_dict.c b/fitz/obj_dict.c
new file mode 100644
index 00000000..236c8a1a
--- /dev/null
+++ b/fitz/obj_dict.c
@@ -0,0 +1,399 @@
+#include "fitz-base.h"
+#include "fitz-stream.h"
+
+/* keep either names or strings in the dict. don't mix & match. */
+
+static int keyvalcmp(const void *ap, const void *bp)
+{
+ const fz_keyval *a = ap;
+ const fz_keyval *b = bp;
+ if (fz_isname(a->k))
+ return strcmp(fz_toname(a->k), fz_toname(b->k));
+ if (fz_isstring(a->k))
+ return strcmp(fz_tostrbuf(a->k), fz_tostrbuf(b->k));
+ return -1;
+}
+
+static inline int keystrcmp(fz_obj *key, char *s)
+{
+ if (fz_isname(key))
+ return strcmp(fz_toname(key), s);
+ if (fz_isstring(key))
+ return strcmp(fz_tostrbuf(key), s);
+ return -1;
+}
+
+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_throw("outofmem: dict struct");
+
+ obj->refs = 1;
+ obj->kind = FZ_DICT;
+
+ obj->u.d.sorted = 1;
+ obj->u.d.len = 0;
+ obj->u.d.cap = initialcap > 0 ? initialcap : 10;
+
+ obj->u.d.items = fz_malloc(sizeof(fz_keyval) * obj->u.d.cap);
+ if (!obj->u.d.items)
+ {
+ fz_free(obj);
+ return fz_throw("outofmem: dict item buffer");
+ }
+
+ for (i = 0; i < obj->u.d.cap; i++)
+ {
+ obj->u.d.items[i].k = nil;
+ obj->u.d.items[i].v = nil;
+ }
+
+ return fz_okay;
+}
+
+fz_error *
+fz_copydict(fz_obj **op, fz_obj *obj)
+{
+ fz_error *error;
+ fz_obj *new;
+ int i;
+
+ if (!fz_isdict(obj))
+ return fz_throw("assert: not a dict (%s)", fz_objkindstr(obj));
+
+ error = fz_newdict(&new, obj->u.d.cap);
+ if (error)
+ return fz_rethrow(error, "cannot create new dict");
+
+ for (i = 0; i < fz_dictlen(obj); i++)
+ {
+ error = fz_dictput(new, fz_dictgetkey(obj, i), fz_dictgetval(obj, i));
+ if (error)
+ {
+ fz_dropobj(new);
+ return fz_rethrow(error, "cannot copy dict entry");
+ }
+ }
+
+ *op = new;
+ return fz_okay;
+}
+
+fz_error *
+fz_deepcopydict(fz_obj **op, fz_obj *obj)
+{
+ fz_error *error;
+ fz_obj *new;
+ fz_obj *val;
+ int i;
+
+ if (!fz_isdict(obj))
+ return fz_throw("assert: not a dict (%s)", fz_objkindstr(obj));
+
+ error = fz_newdict(&new, obj->u.d.cap);
+ if (error)
+ return fz_rethrow(error, "cannot create new dict");
+
+ for (i = 0; i < fz_dictlen(obj); i++)
+ {
+ val = fz_dictgetval(obj, i);
+
+ if (fz_isarray(val))
+ {
+ error = fz_deepcopyarray(&val, val);
+ if (error)
+ {
+ fz_dropobj(new);
+ return fz_rethrow(error, "cannot deep copy item");
+ }
+ error = fz_dictput(new, fz_dictgetkey(obj, i), val);
+ if (error)
+ {
+ fz_dropobj(val);
+ fz_dropobj(new);
+ return fz_rethrow(error, "cannot add copied dict entry");
+ }
+ fz_dropobj(val);
+ }
+
+ else if (fz_isdict(val))
+ {
+ error = fz_deepcopydict(&val, val);
+ if (error)
+ {
+ fz_dropobj(new);
+ return fz_rethrow(error, "cannot deep copy item");
+ }
+ error = fz_dictput(new, fz_dictgetkey(obj, i), val);
+ if (error)
+ {
+ fz_dropobj(val);
+ fz_dropobj(new);
+ return fz_rethrow(error, "cannot add copied dict entry");
+ }
+ fz_dropobj(val);
+ }
+
+ else
+ {
+ error = fz_dictput(new, fz_dictgetkey(obj, i), val);
+ if (error)
+ {
+ fz_dropobj(new);
+ return fz_rethrow(error, "cannot copy dict entry");
+ }
+ }
+ }
+
+ *op = new;
+ return fz_okay;
+}
+
+static fz_error *
+growdict(fz_obj *obj)
+{
+ fz_keyval *newitems;
+ int newcap;
+ int i;
+
+ newcap = obj->u.d.cap * 2;
+
+ newitems = fz_realloc(obj->u.d.items, sizeof(fz_keyval) * newcap);
+ if (!newitems)
+ return fz_throw("outofmem: resize item buffer");
+
+ 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 fz_okay;
+}
+
+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;
+}
+
+static inline int dictfinds(fz_obj *obj, char *key)
+{
+ if (obj->u.d.sorted)
+ {
+ int l = 0;
+ int r = obj->u.d.len - 1;
+ while (l <= r)
+ {
+ int m = (l + r) >> 1;
+ int c = -keystrcmp(obj->u.d.items[m].k, key);
+ if (c < 0)
+ r = m - 1;
+ else if (c > 0)
+ l = m + 1;
+ else
+ return m;
+ }
+ }
+
+ else
+ {
+ int i;
+ for (i = 0; i < obj->u.d.len; i++)
+ if (keystrcmp(obj->u.d.items[i].k, key) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+fz_obj *
+fz_dictgets(fz_obj *obj, char *key)
+{
+ int i;
+
+ if (!fz_isdict(obj))
+ return nil;
+
+ i = dictfinds(obj, key);
+ if (i >= 0)
+ return obj->u.d.items[i].v;
+
+ return nil;
+}
+
+fz_obj *
+fz_dictget(fz_obj *obj, fz_obj *key)
+{
+ if (fz_isname(key))
+ return fz_dictgets(obj, fz_toname(key));
+ if (fz_isstring(key))
+ return fz_dictgets(obj, fz_tostrbuf(key));
+ return nil;
+}
+
+fz_obj *
+fz_dictgetsa(fz_obj *obj, char *key, char *abbrev)
+{
+ fz_obj *v;
+ v = fz_dictgets(obj, key);
+ if (v)
+ return v;
+ return fz_dictgets(obj, abbrev);
+}
+
+fz_error *
+fz_dictput(fz_obj *obj, fz_obj *key, fz_obj *val)
+{
+ fz_error *error;
+ char *s;
+ int i;
+
+ if (!fz_isdict(obj))
+ return fz_throw("assert: not a dict (%s)", fz_objkindstr(obj));
+
+ if (fz_isname(key))
+ s = fz_toname(key);
+ else if (fz_isstring(key))
+ s = fz_tostrbuf(key);
+ else
+ return fz_throw("assert: key is not string or name (%s)", fz_objkindstr(obj));
+
+ i = dictfinds(obj, s);
+ if (i >= 0)
+ {
+ fz_dropobj(obj->u.d.items[i].v);
+ obj->u.d.items[i].v = fz_keepobj(val);
+ return fz_okay;
+ }
+
+ if (obj->u.d.len + 1 > obj->u.d.cap)
+ {
+ error = growdict(obj);
+ if (error)
+ return fz_rethrow(error, "cannot grow dict item buffer");
+ }
+
+ /* borked! */
+ if (obj->u.d.len)
+ if (keystrcmp(obj->u.d.items[obj->u.d.len - 1].k, s) > 0)
+ obj->u.d.sorted = 0;
+
+ 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 fz_okay;
+}
+
+fz_error *
+fz_dictputs(fz_obj *obj, char *key, fz_obj *val)
+{
+ fz_error *error;
+ fz_obj *keyobj;
+ error = fz_newname(&keyobj, key);
+ if (error)
+ return fz_rethrow(error, "cannot create dict key");
+ error = fz_dictput(obj, keyobj, val);
+ fz_dropobj(keyobj);
+ if (error)
+ return fz_rethrow(error, "cannot insert dict entry");
+ return fz_okay;
+}
+
+fz_error *
+fz_dictdels(fz_obj *obj, char *key)
+{
+ int i;
+
+ if (!fz_isdict(obj))
+ return fz_throw("assert: not a dict (%s)", fz_objkindstr(obj));
+
+ i = dictfinds(obj, key);
+ if (i >= 0)
+ {
+ fz_dropobj(obj->u.d.items[i].k);
+ fz_dropobj(obj->u.d.items[i].v);
+ obj->u.d.sorted = 0;
+ obj->u.d.items[i] = obj->u.d.items[obj->u.d.len-1];
+ obj->u.d.len --;
+ }
+
+ return fz_okay;
+}
+
+fz_error *
+fz_dictdel(fz_obj *obj, fz_obj *key)
+{
+ if (fz_isname(key))
+ return fz_dictdels(obj, fz_toname(key));
+ else if (fz_isstring(key))
+ return fz_dictdels(obj, fz_tostrbuf(key));
+ else
+ return fz_throw("assert: key is not string or name (%s)", fz_objkindstr(obj));
+}
+
+void
+fz_dropdict(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);
+}
+
+void
+fz_sortdict(fz_obj *obj)
+{
+ if (!fz_isdict(obj))
+ return;
+ if (!obj->u.d.sorted)
+ {
+ qsort(obj->u.d.items, obj->u.d.len, sizeof(fz_keyval), keyvalcmp);
+ obj->u.d.sorted = 1;
+ }
+}
+
diff --git a/fitz/obj_parse.c b/fitz/obj_parse.c
new file mode 100644
index 00000000..48d59538
--- /dev/null
+++ b/fitz/obj_parse.c
@@ -0,0 +1,492 @@
+#include "fitz-base.h"
+#include "fitz-stream.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)
+{
+ fz_error *error;
+ 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;
+
+ error = fz_newname(obj, buf);
+ if (error)
+ return fz_rethrow(error, "cannot create name");
+ return fz_okay;
+}
+
+static fz_error *parsenumber(fz_obj **obj, char **sp)
+{
+ fz_error *error;
+ 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, '.'))
+ error = fz_newreal(obj, atof(buf));
+ else
+ error = fz_newint(obj, atoi(buf));
+
+ if (error)
+ return fz_rethrow(error, "cannot parse number");
+ return fz_okay;
+}
+
+static fz_error *parsedict(fz_obj **obj, char **sp, struct vap *v)
+{
+ fz_error *error = fz_okay;
+ fz_obj *dict = nil;
+ fz_obj *key = nil;
+ fz_obj *val = nil;
+ char *s = *sp;
+
+ error = fz_newdict(&dict, 8);
+ if (error)
+ return fz_rethrow(error, "cannot create dict");
+
+ s += 2; /* skip "<<" */
+
+ while (*s)
+ {
+ skipwhite(&s);
+
+ /* end-of-dict marker >> */
+ if (*s == '>')
+ {
+ s ++;
+ if (*s == '>')
+ {
+ s ++;
+ break;
+ }
+ error = fz_throw("malformed >> marker");
+ goto cleanup;
+ }
+
+ /* non-name as key, bail */
+ if (*s != '/')
+ {
+ error = fz_throw("key is not a name");
+ goto cleanup;
+ }
+
+ error = parsename(&key, &s);
+ if (error)
+ {
+ error = fz_rethrow(error, "cannot parse key");
+ goto cleanup;
+ }
+
+ skipwhite(&s);
+
+ error = parseobj(&val, &s, v);
+ if (error)
+ {
+ error = fz_rethrow(error, "cannot parse value");
+ goto cleanup;
+ }
+
+ error = fz_dictput(dict, key, val);
+ if (error)
+ {
+ error = fz_rethrow(error, "cannot insert dict entry");
+ goto cleanup;
+ }
+
+ fz_dropobj(val); val = nil;
+ fz_dropobj(key); key = nil;
+ }
+
+ *obj = dict;
+ *sp = s;
+ return fz_okay;
+
+cleanup:
+ if (val) fz_dropobj(val);
+ if (key) fz_dropobj(key);
+ if (dict) fz_dropobj(dict);
+ *obj = nil;
+ *sp = s;
+ return error; /* already rethrown */
+}
+
+static fz_error *parsearray(fz_obj **obj, char **sp, struct vap *v)
+{
+ fz_error *error;
+ fz_obj *a;
+ fz_obj *o;
+ char *s = *sp;
+
+ error = fz_newarray(&a, 8);
+ if (error)
+ return fz_rethrow(error, "cannot create array");
+
+ s ++; /* skip '[' */
+
+ while (*s)
+ {
+ skipwhite(&s);
+
+ if (*s == ']')
+ {
+ s ++;
+ break;
+ }
+
+ error = parseobj(&o, &s, v);
+ if (error)
+ {
+ fz_dropobj(a);
+ return fz_rethrow(error, "cannot parse item");
+ }
+
+ error = fz_arraypush(a, o);
+ if (error)
+ {
+ fz_dropobj(o);
+ fz_dropobj(a);
+ return fz_rethrow(error, "cannot add item to array");
+ }
+
+ fz_dropobj(o);
+ }
+
+ *obj = a;
+ *sp = s;
+ return fz_okay;
+}
+
+static fz_error *parsestring(fz_obj **obj, char **sp)
+{
+ fz_error *error;
+ 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;
+
+ error = fz_newstring(obj, buf, p - buf - 1);
+ if (error)
+ return fz_rethrow(error, "cannot create string");
+ return fz_okay;
+}
+
+static fz_error *parsehexstring(fz_obj **obj, char **sp)
+{
+ fz_error *error;
+ 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;
+ error = fz_newstring(obj, buf, p - buf);
+ if (error)
+ return fz_rethrow(error, "cannot create string");
+ return fz_okay;
+}
+
+static fz_error *parseobj(fz_obj **obj, char **sp, struct vap *v)
+{
+ fz_error *error;
+ char buf[32];
+ int oid, gid, len;
+ char *tmp;
+ char *s = *sp;
+
+ if (*s == '\0')
+ return fz_throw("end of data");
+
+ skipwhite(&s);
+
+ error = fz_okay;
+
+ if (v != nil && *s == '%')
+ {
+ s ++;
+
+ switch (*s)
+ {
+ case 'p': error = fz_newpointer(obj, va_arg(v->ap, void*)); break;
+ case 'o': *obj = fz_keepobj(va_arg(v->ap, fz_obj*)); break;
+ case 'b': error = fz_newbool(obj, va_arg(v->ap, int)); break;
+ case 'i': error = fz_newint(obj, va_arg(v->ap, int)); break;
+ case 'f': error = fz_newreal(obj, (float)va_arg(v->ap, double)); break;
+ case 'n': error = fz_newname(obj, va_arg(v->ap, char*)); break;
+ case 'r':
+ oid = va_arg(v->ap, int);
+ gid = va_arg(v->ap, int);
+ error = fz_newindirect(obj, oid, gid);
+ break;
+ case 's':
+ tmp = va_arg(v->ap, char*);
+ error = fz_newstring(obj, tmp, strlen(tmp));
+ break;
+ case '#':
+ tmp = va_arg(v->ap, char*);
+ len = va_arg(v->ap, int);
+ error = fz_newstring(obj, tmp, len);
+ break;
+ default:
+ error = fz_throw("unknown format specifier in packobj: '%c'", *s);
+ break;
+ }
+
+ if (error)
+ error = fz_rethrow(error, "cannot create object for %% format");
+
+ s ++;
+ }
+
+ else if (*s == '/')
+ {
+ error = parsename(obj, &s);
+ if (error)
+ error = fz_rethrow(error, "cannot parse name");
+ }
+
+ else if (*s == '(')
+ {
+ error = parsestring(obj, &s);
+ if (error)
+ error = fz_rethrow(error, "cannot parse string");
+ }
+
+ else if (*s == '<')
+ {
+ if (s[1] == '<')
+ {
+ error = parsedict(obj, &s, v);
+ if (error)
+ error = fz_rethrow(error, "cannot parse dict");
+ }
+ else
+ {
+ error = parsehexstring(obj, &s);
+ if (error)
+ error = fz_rethrow(error, "cannot parse hex string");
+ }
+ }
+
+ else if (*s == '[')
+ {
+ error = parsearray(obj, &s, v);
+ if (error)
+ error = fz_rethrow(error, "cannot parse array");
+ }
+
+ else if (*s == '-' || *s == '.' || (*s >= '0' && *s <= '9'))
+ {
+ error = parsenumber(obj, &s);
+ if (error)
+ error = fz_rethrow(error, "cannot parse number");
+ }
+
+ else if (isregular(*s))
+ {
+ parsekeyword(&s, buf, buf + sizeof buf);
+
+ if (strcmp("true", buf) == 0)
+ {
+ error = fz_newbool(obj, 1);
+ if (error)
+ error = fz_rethrow(error, "cannot create bool (true)");
+ }
+ else if (strcmp("false", buf) == 0)
+ {
+ error = fz_newbool(obj, 0);
+ if (error)
+ error = fz_rethrow(error, "cannot create bool (false)");
+ }
+ else if (strcmp("null", buf) == 0)
+ {
+ error = fz_newnull(obj);
+ if (error)
+ error = fz_rethrow(error, "cannot create null object");
+ }
+ else
+ error = fz_throw("undefined keyword %s", buf);
+ }
+
+ else
+ error = fz_throw("syntax error: unknown byte 0x%d", *s);
+
+ *sp = s;
+ return error; /* already rethrown */
+}
+
+fz_error *
+fz_packobj(fz_obj **op, char *fmt, ...)
+{
+ fz_error *error;
+ struct vap v;
+ va_list ap;
+
+ va_start(ap, fmt);
+ va_copy(v.ap, ap);
+
+ error = parseobj(op, &fmt, &v);
+
+ va_end(ap);
+
+ if (error)
+ return fz_rethrow(error, "cannot parse object");
+ return fz_okay;
+}
+
+fz_error *
+fz_parseobj(fz_obj **op, char *str)
+{
+ return parseobj(op, &str, nil);
+}
+
diff --git a/fitz/obj_print.c b/fitz/obj_print.c
new file mode 100644
index 00000000..4e321127
--- /dev/null
+++ b/fitz/obj_print.c
@@ -0,0 +1,339 @@
+#include "fitz-base.h"
+#include "fitz-stream.h"
+
+struct fmt
+{
+ char *buf;
+ int cap;
+ int len;
+ int indent;
+ int tight;
+ int col;
+ int sep;
+ int last;
+};
+
+static void fmtobj(struct fmt *fmt, fz_obj *obj);
+
+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 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->buf && fmt->len < fmt->cap)
+ 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 = (unsigned char) 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);
+ }
+ else
+ fmtputc(fmt, c);
+ }
+ fmtputc(fmt, ')');
+}
+
+static void fmthex(struct fmt *fmt, fz_obj *obj)
+{
+ int i;
+ int b;
+ int c;
+
+ fmtputc(fmt, '<');
+ for (i = 0; i < obj->u.s.len; i++) {
+ b = (unsigned char) obj->u.s.buf[i];
+ c = (b >> 4) & 0x0f;
+ fmtputc(fmt, c < 0xA ? c + '0' : c + 'A' - 0xA);
+ c = (b) & 0x0f;
+ fmtputc(fmt, c < 0xA ? c + '0' : c + 'A' - 0xA);
+ }
+ fmtputc(fmt, '>');
+}
+
+static void fmtname(struct fmt *fmt, fz_obj *obj)
+{
+ char *s = fz_toname(obj);
+ int i, c;
+
+ fmtputc(fmt, '/');
+
+ for (i = 0; s[i]; i++)
+ {
+ if (isdelim(s[i]) || iswhite(s[i]))
+ {
+ fmtputc(fmt, '#');
+ c = (s[i] >> 4) & 0xf;
+ fmtputc(fmt, c < 0xA ? c + '0' : c + 'A' - 0xA);
+ c = s[i] & 0xf;
+ fmtputc(fmt, c < 0xA ? c + '0' : c + 'A' - 0xA);
+ }
+ else
+ {
+ fmtputc(fmt, s[i]);
+ }
+ }
+}
+
+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:
+ fmtname(fmt, obj);
+ 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, int n, fz_obj *obj, int tight)
+{
+ struct fmt fmt;
+
+ fmt.indent = 0;
+ fmt.col = 0;
+ fmt.sep = 0;
+ fmt.last = 0;
+
+ fmt.tight = tight;
+ fmt.buf = s;
+ fmt.cap = n;
+ fmt.len = 0;
+ fmtobj(&fmt, obj);
+
+ if (fmt.buf && fmt.len < fmt.cap)
+ fmt.buf[fmt.len] = '\0';
+
+ return fmt.len;
+}
+
+void
+fz_debugobj(fz_obj *obj)
+{
+ char buf[1024];
+ char *ptr;
+ int n;
+
+ n = fz_sprintobj(nil, 0, obj, 0);
+ if (n < sizeof buf)
+ {
+ fz_sprintobj(buf, sizeof buf, obj, 0);
+ fputs(buf, stdout);
+ fputc('\n', stdout);
+ }
+ else
+ {
+ ptr = fz_malloc(n);
+ if (!ptr)
+ return;
+ fz_sprintobj(ptr, n, obj, 0);
+ fputs(ptr, stdout);
+ fputc('\n', stdout);
+ fz_free(ptr);
+ }
+}
+
diff --git a/fitz/obj_simple.c b/fitz/obj_simple.c
new file mode 100644
index 00000000..d8eb77b7
--- /dev/null
+++ b/fitz/obj_simple.c
@@ -0,0 +1,323 @@
+#include "fitz-base.h"
+#include "fitz-stream.h"
+
+extern void fz_droparray(fz_obj *array);
+extern void fz_dropdict(fz_obj *dict);
+
+#define NEWOBJ(KIND,SIZE) \
+ fz_obj *o; \
+ o = *op = fz_malloc(SIZE); \
+ if (!o) return fz_throw("outofmem: dynamic object"); \
+ o->refs = 1; \
+ o->kind = KIND;
+
+fz_error *
+fz_newnull(fz_obj **op)
+{
+ NEWOBJ(FZ_NULL, sizeof (fz_obj));
+ return fz_okay;
+}
+
+fz_error *
+fz_newbool(fz_obj **op, int b)
+{
+ NEWOBJ(FZ_BOOL, sizeof (fz_obj));
+ o->u.b = b;
+ return fz_okay;
+}
+
+fz_error *
+fz_newint(fz_obj **op, int i)
+{
+ NEWOBJ(FZ_INT, sizeof (fz_obj));
+ o->u.i = i;
+ return fz_okay;
+}
+
+fz_error *
+fz_newreal(fz_obj **op, float f)
+{
+ NEWOBJ(FZ_REAL, sizeof (fz_obj));
+ o->u.f = f;
+ return fz_okay;
+}
+
+fz_error *
+fz_newstring(fz_obj **op, char *str, int len)
+{
+ NEWOBJ(FZ_STRING, offsetof(fz_obj, u.s.buf) + len + 1);
+ o->u.s.len = len;
+ memcpy(o->u.s.buf, str, len);
+ o->u.s.buf[len] = '\0';
+ return fz_okay;
+}
+
+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 fz_okay;
+}
+
+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 fz_okay;
+}
+
+fz_error *
+fz_newpointer(fz_obj **op, void *p)
+{
+ NEWOBJ(FZ_POINTER, sizeof (fz_obj));
+ o->u.p = p;
+ return fz_okay;
+}
+
+fz_obj *
+fz_keepobj(fz_obj *o)
+{
+ assert(o != nil);
+ o->refs ++;
+ return o;
+}
+
+void
+fz_dropobj(fz_obj *o)
+{
+ assert(o != nil);
+ if (--o->refs == 0)
+ {
+ if (o->kind == FZ_ARRAY)
+ fz_droparray(o);
+ else if (o->kind == FZ_DICT)
+ fz_dropdict(o);
+ else
+ fz_free(o);
+ }
+}
+
+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;
+ if (fz_isreal(obj))
+ return obj->u.f;
+ 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_tostrbuf(fz_obj *obj)
+{
+ if (fz_isstring(obj))
+ return obj->u.s.buf;
+ return "";
+}
+
+int
+fz_tostrlen(fz_obj *obj)
+{
+ if (fz_isstring(obj))
+ return obj->u.s.len;
+ return 0;
+}
+
+int
+fz_tonum(fz_obj *obj)
+{
+ if (fz_isindirect(obj))
+ return obj->u.r.oid;
+ return 0;
+}
+
+int
+fz_togen(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 fz_okay;
+}
+
+fz_error *
+fz_newnamefromstring(fz_obj **op, fz_obj *str)
+{
+ NEWOBJ(FZ_NAME, offsetof(fz_obj, u.n) + fz_tostrlen(str) + 1);
+ memcpy(o->u.n, fz_tostrbuf(str), fz_tostrlen(str));
+ o->u.n[fz_tostrlen(str)] = '\0';
+ return fz_okay;
+}
+
+int
+fz_objcmp(fz_obj *a, fz_obj *b)
+{
+ int i;
+
+ if (a == b)
+ return 0;
+ if (a->kind != b->kind)
+ return 1;
+
+ switch (a->kind)
+ {
+ case FZ_NULL: return 0;
+ case FZ_BOOL: return a->u.b - b->u.b;
+ case FZ_INT: return a->u.i - b->u.i;
+ case FZ_REAL: return a->u.f - b->u.f;
+
+ case FZ_STRING:
+ if (a->u.s.len != b->u.s.len)
+ return a->u.s.len - b->u.s.len;
+ return memcmp(a->u.s.buf, b->u.s.buf, a->u.s.len);
+
+ case FZ_NAME:
+ return strcmp(a->u.n, b->u.n);
+
+ case FZ_INDIRECT:
+ if (a->u.r.oid == b->u.r.oid)
+ return a->u.r.gid - b->u.r.gid;
+ return a->u.r.oid - b->u.r.oid;
+
+ case FZ_ARRAY:
+ if (a->u.a.len != b->u.a.len)
+ return a->u.a.len - b->u.a.len;
+ for (i = 0; i < a->u.a.len; i++)
+ if (fz_objcmp(a->u.a.items[i], b->u.a.items[i]))
+ return 1;
+ return 0;
+
+ case FZ_DICT:
+ if (a->u.d.len != b->u.d.len)
+ return a->u.d.len - b->u.d.len;
+ for (i = 0; i < a->u.d.len; i++)
+ {
+ if (fz_objcmp(a->u.d.items[i].k, b->u.d.items[i].k))
+ return 1;
+ if (fz_objcmp(a->u.d.items[i].v, b->u.d.items[i].v))
+ return 1;
+ }
+ return 0;
+
+ case FZ_POINTER:
+ return (char *) a->u.p - (char *) b->u.p;
+ }
+ return 1;
+}
+
+char *fz_objkindstr(fz_obj *obj)
+{
+ if (obj == nil)
+ return "<nil>";
+ switch (obj->kind)
+ {
+ case FZ_NULL: return "null";
+ case FZ_BOOL: return "boolean";
+ case FZ_INT: return "integer";
+ case FZ_REAL: return "real";
+ case FZ_STRING: return "string";
+ case FZ_NAME: return "name";
+ case FZ_ARRAY: return "array";
+ case FZ_DICT: return "dictionary";
+ case FZ_INDIRECT: return "reference";
+ case FZ_POINTER: return "pointer";
+ }
+ return "<unknown>";
+}
+
diff --git a/fitz/res_colorspace.c b/fitz/res_colorspace.c
new file mode 100644
index 00000000..04fc532e
--- /dev/null
+++ b/fitz/res_colorspace.c
@@ -0,0 +1,88 @@
+#include "fitz-base.h"
+#include "fitz-world.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 && cs->refs < 0)
+ return;
+ if (cs && --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;
+
+ 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..7238cc3c
--- /dev/null
+++ b/fitz/res_font.c
@@ -0,0 +1,408 @@
+#include "fitz-base.h"
+#include "fitz-world.h"
+#include "fitz-draw.h" /* for type3 font rendering */
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+static fz_font *
+fz_newfont(void)
+{
+ fz_font *font;
+
+ font = fz_malloc(sizeof(fz_font));
+ if (!font)
+ return nil;
+
+ font->refs = 1;
+ strcpy(font->name, "<unknown>");
+
+ font->ftface = nil;
+ font->ftsubstitute = 0;
+ font->fthint = 0;
+
+ font->t3matrix = fz_identity();
+ font->t3procs = nil;
+
+ font->bbox.x0 = 0;
+ font->bbox.y0 = 0;
+ font->bbox.x1 = 1000;
+ font->bbox.y1 = 1000;
+
+ return font;
+}
+
+fz_font *
+fz_keepfont(fz_font *font)
+{
+ font->refs ++;
+ return font;
+}
+
+void
+fz_dropfont(fz_font *font)
+{
+ int i;
+
+ if (font && --font->refs == 0)
+ {
+ if (font->t3procs)
+ {
+ for (i = 0; i < 256; i++)
+ if (font->t3procs[i])
+ fz_droptree(font->t3procs[i]);
+ fz_free(font->t3procs);
+ }
+
+ if (font->ftface)
+ {
+ FT_Done_Face((FT_Face)font->ftface);
+ }
+
+ fz_free(font);
+ }
+}
+
+void
+fz_setfontbbox(fz_font *font, int xmin, int ymin, int xmax, int ymax)
+{
+ font->bbox.x0 = xmin;
+ font->bbox.y0 = ymin;
+ font->bbox.x1 = xmax;
+ font->bbox.y1 = ymax;
+}
+
+/*
+ * Freetype hooks
+ */
+
+static FT_Library fz_ftlib = nil;
+
+#undef __FTERRORS_H__
+#define FT_ERRORDEF(e, v, s) { (e), (s) },
+#define FT_ERROR_START_LIST
+#define FT_ERROR_END_LIST { 0, NULL }
+
+struct ft_error
+{
+ int err;
+ char *str;
+};
+
+const struct ft_error ft_errors[] =
+{
+#include FT_ERRORS_H
+};
+
+char *ft_errorstring(int err)
+{
+ const struct ft_error *e;
+
+ for (e = ft_errors; e->str != NULL; e++)
+ if (e->err == err)
+ return e->str;
+
+ return "Unknown error";
+}
+
+static fz_error *
+fz_initfreetype(void)
+{
+ int code;
+ int maj, min, pat;
+
+ if (fz_ftlib)
+ return fz_okay;
+
+ code = FT_Init_FreeType(&fz_ftlib);
+ if (code)
+ return fz_throw("cannot init freetype: %s", ft_errorstring(code));
+
+ FT_Library_Version(fz_ftlib, &maj, &min, &pat);
+ if (maj == 2 && min == 1 && pat < 7)
+ return fz_throw("freetype version too old: %d.%d.%d", maj, min, pat);
+
+ return fz_okay;
+}
+
+fz_error *
+fz_newfontfromfile(fz_font **fontp, char *path, int index)
+{
+ fz_error *error;
+ fz_font *font;
+ int code;
+
+ error = fz_initfreetype();
+ if (error)
+ return fz_rethrow(error, "cannot init freetype library");
+
+ font = fz_newfont();
+ if (!font)
+ return fz_throw("outofmem: font struct");
+
+ code = FT_New_Face(fz_ftlib, path, index, (FT_Face*)&font->ftface);
+ if (code)
+ {
+ fz_free(font);
+ return fz_throw("freetype: cannot load font: %s", ft_errorstring(code));
+ }
+
+ *fontp = font;
+ return fz_okay;
+}
+
+fz_error *
+fz_newfontfrombuffer(fz_font **fontp, unsigned char *data, int len, int index)
+{
+ fz_error *error;
+ fz_font *font;
+ int code;
+
+ error = fz_initfreetype();
+ if (error)
+ return fz_rethrow(error, "cannot init freetype library");
+
+ font = fz_newfont();
+ if (!font)
+ return fz_throw("outofmem: font struct");
+
+ code = FT_New_Memory_Face(fz_ftlib, data, len, index, (FT_Face*)&font->ftface);
+ if (code)
+ {
+ fz_free(font);
+ return fz_throw("freetype: cannot load font: %s", ft_errorstring(code));
+ }
+
+ *fontp = font;
+ return fz_okay;
+}
+
+fz_error *
+fz_renderftglyph(fz_glyph *glyph, fz_font *font, int gid, fz_matrix trm)
+{
+ FT_Face face = font->ftface;
+ FT_Matrix m;
+ FT_Vector v;
+ FT_Error fterr;
+ int x, y;
+
+#if 0
+ /* We lost this feature in refactoring.
+ * We can't access pdf_fontdesc metrics from fz_font.
+ * The pdf_fontdesc metrics are character based (cid),
+ * where the glyph being rendered is given by glyph (gid).
+ */
+ if (font->ftsubstitute && font->wmode == 0)
+ {
+ fz_hmtx subw;
+ int realw;
+ float scale;
+
+ FT_Set_Char_Size(face, 1000, 1000, 72, 72);
+
+ fterr = FT_Load_Glyph(font->ftface, gid,
+ FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM);
+ if (fterr)
+ return fz_throw("freetype failed to load glyph: %s", ft_errorstring(fterr));
+
+ realw = ((FT_Face)font->ftface)->glyph->advance.x;
+ subw = fz_gethmtx(font, cid); // <-- this is the offender
+ if (realw)
+ scale = (float) subw.w / realw;
+ else
+ scale = 1.0;
+
+ trm = fz_concat(fz_scale(scale, 1.0), trm);
+ }
+#endif
+
+ glyph->w = 0;
+ glyph->h = 0;
+ glyph->x = 0;
+ glyph->y = 0;
+ glyph->samples = nil;
+
+ /* freetype mutilates complex glyphs if they are loaded
+ * with FT_Set_Char_Size 1.0. it rounds the coordinates
+ * before applying transformation. to get more precision in
+ * freetype, we shift part of the scale in the matrix
+ * into FT_Set_Char_Size instead
+ */
+
+ m.xx = trm.a * 64; /* should be 65536 */
+ m.yx = trm.b * 64;
+ m.xy = trm.c * 64;
+ m.yy = trm.d * 64;
+ v.x = trm.e * 64;
+ v.y = trm.f * 64;
+
+ FT_Set_Char_Size(face, 65536, 65536, 72, 72); /* should be 64, 64 */
+ FT_Set_Transform(face, &m, &v);
+
+ if (font->fthint)
+ {
+ /* Enable hinting, but keep the huge char size so that
+ * it is hinted for a character. This will in effect nullify
+ * the effect of grid fitting. This form of hinting should
+ * only be used for DynaLab and similar tricky TrueType fonts,
+ * so that we get the correct outline shape.
+ */
+#ifdef USE_HINTING
+ /* If you really want grid fitting, enable this code. */
+ float scale = fz_matrixexpansion(trm);
+ m.xx = trm.a * 65536 / scale;
+ m.xy = trm.b * 65536 / scale;
+ m.yx = trm.c * 65536 / scale;
+ m.yy = trm.d * 65536 / scale;
+ v.x = 0;
+ v.y = 0;
+ FT_Set_Char_Size(face, 64 * scale, 64 * scale, 72, 72);
+ FT_Set_Transform(face, &m, &v);
+#endif
+ fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP);
+ if (fterr)
+ fz_warn("freetype load glyph: %s", ft_errorstring(fterr));
+ }
+ else
+ {
+ fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING);
+ if (fterr)
+ fz_warn("freetype load glyph: %s", ft_errorstring(fterr));
+ }
+
+ fterr = FT_Render_Glyph(face->glyph, ft_render_mode_normal);
+ if (fterr)
+ fz_warn("freetype render glyph: %s", ft_errorstring(fterr));
+
+ glyph->w = face->glyph->bitmap.width;
+ glyph->h = face->glyph->bitmap.rows;
+ glyph->x = face->glyph->bitmap_left;
+ glyph->y = face->glyph->bitmap_top - glyph->h;
+ glyph->samples = face->glyph->bitmap.buffer;
+
+ for (y = 0; y < glyph->h / 2; y++)
+ {
+ for (x = 0; x < glyph->w; x++)
+ {
+ unsigned char a = glyph->samples[y * glyph->w + x ];
+ unsigned char b = glyph->samples[(glyph->h - y - 1) * glyph->w + x];
+ glyph->samples[y * glyph->w + x ] = b;
+ glyph->samples[(glyph->h - y - 1) * glyph->w + x] = a;
+ }
+ }
+
+ return fz_okay;
+}
+
+
+/*
+ * Type 3 fonts...
+ */
+
+fz_error *
+fz_newtype3font(fz_font **fontp, char *name, fz_matrix matrix, void **procs0)
+{
+ fz_font *font;
+ fz_tree **procs = (fz_tree**)procs0;
+ int i;
+
+ font = fz_newfont();
+ if (!font)
+ return fz_throw("outofmem: font struct");
+
+ font->t3procs = fz_malloc(sizeof(fz_tree*) * 256);
+ if (!font->t3procs)
+ {
+ fz_free(font);
+ return fz_throw("outofmem: type3 font charproc array");
+ }
+
+ font->t3matrix = matrix;
+ for (i = 0; i < 256; i++)
+ font->t3procs[i] = procs[i];
+
+ strlcpy(font->name, name, sizeof(font->name));
+
+ *fontp = font;
+ return fz_okay;
+}
+
+fz_error *
+fz_rendert3glyph(fz_glyph *glyph, fz_font *font, int gid, fz_matrix trm)
+{
+ fz_error *error;
+ fz_renderer *gc;
+ fz_tree *tree;
+ fz_matrix ctm;
+ fz_irect bbox;
+
+ /* TODO: make it reentrant */
+ static fz_pixmap *pixmap = nil;
+ if (pixmap)
+ {
+ fz_droppixmap(pixmap);
+ pixmap = nil;
+ }
+
+ if (gid < 0 || gid > 255)
+ return fz_throw("assert: glyph out of range");
+
+ tree = font->t3procs[gid];
+ if (!tree)
+ {
+ glyph->w = 0;
+ glyph->h = 0;
+ return fz_okay;
+ }
+
+ /* XXX UGLY HACK XXX */
+ extern fz_colorspace *pdf_devicegray;
+
+ ctm = fz_concat(font->t3matrix, trm);
+ bbox = fz_roundrect(fz_boundtree(tree, ctm));
+
+ error = fz_newrenderer(&gc, pdf_devicegray, 1, 4096);
+ if (error)
+ return fz_rethrow(error, "cannot create renderer");
+ error = fz_rendertree(&pixmap, gc, tree, ctm, bbox, 0);
+ fz_droprenderer(gc);
+ if (error)
+ return fz_rethrow(error, "cannot render glyph");
+
+ assert(pixmap->n == 1);
+
+ glyph->x = pixmap->x;
+ glyph->y = pixmap->y;
+ glyph->w = pixmap->w;
+ glyph->h = pixmap->h;
+ glyph->samples = pixmap->samples;
+
+ return fz_okay;
+}
+
+void
+fz_debugfont(fz_font *font)
+{
+ printf("font '%s' {\n", font->name);
+
+ if (font->ftface)
+ {
+ printf(" freetype face %p\n", font->ftface);
+ if (font->ftsubstitute)
+ printf(" substitute font\n");
+ }
+
+ if (font->t3procs)
+ {
+ printf(" type3 matrix [%g %g %g %g]\n",
+ font->t3matrix.a, font->t3matrix.b,
+ font->t3matrix.c, font->t3matrix.d);
+ }
+
+ printf(" bbox [%d %d %d %d]\n",
+ font->bbox.x0, font->bbox.y0,
+ font->bbox.x1, font->bbox.y1);
+
+ printf("}\n");
+}
+
diff --git a/fitz/res_image.c b/fitz/res_image.c
new file mode 100644
index 00000000..4379cf17
--- /dev/null
+++ b/fitz/res_image.c
@@ -0,0 +1,23 @@
+#include "fitz-base.h"
+#include "fitz-world.h"
+
+fz_image *
+fz_keepimage(fz_image *image)
+{
+ image->refs ++;
+ return image;
+}
+
+void
+fz_dropimage(fz_image *image)
+{
+ if (image && --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..d1bd991e
--- /dev/null
+++ b/fitz/res_shade.c
@@ -0,0 +1,29 @@
+#include "fitz-base.h"
+#include "fitz-world.h"
+
+fz_shade *
+fz_keepshade(fz_shade *shade)
+{
+ shade->refs ++;
+ return shade;
+}
+
+void
+fz_dropshade(fz_shade *shade)
+{
+ if (shade && --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/stm_buffer.c b/fitz/stm_buffer.c
new file mode 100644
index 00000000..8a1e0850
--- /dev/null
+++ b/fitz/stm_buffer.c
@@ -0,0 +1,105 @@
+#include "fitz-base.h"
+#include "fitz-stream.h"
+
+fz_error *
+fz_newbuffer(fz_buffer **bp, int size)
+{
+ fz_buffer *b;
+
+ b = *bp = fz_malloc(sizeof(fz_buffer));
+ if (!b)
+ return fz_throw("outofmem: buffer struct");
+
+ b->refs = 1;
+ b->ownsdata = 1;
+ b->bp = fz_malloc(size);
+ if (!b->bp)
+ {
+ fz_free(b);
+ return fz_throw("outofmem: buffer memory");
+ }
+
+ b->rp = b->bp;
+ b->wp = b->bp;
+ b->ep = b->bp + size;
+ b->eof = 0;
+
+ return fz_okay;
+}
+
+fz_error *
+fz_newbufferwithmemory(fz_buffer **bp, unsigned char *data, int size)
+{
+ fz_buffer *b;
+
+ b = *bp = fz_malloc(sizeof(fz_buffer));
+ if (!b)
+ return fz_throw("outofmem: buffer struct");
+
+ b->refs = 1;
+ b->ownsdata = 0;
+ b->bp = data;
+
+ b->rp = b->bp;
+ b->wp = b->bp + size;
+ b->ep = b->bp + size;
+ b->eof = 0;
+
+ return fz_okay;
+}
+
+fz_buffer *
+fz_keepbuffer(fz_buffer *buf)
+{
+ buf->refs ++;
+ return buf;
+}
+
+void
+fz_dropbuffer(fz_buffer *buf)
+{
+ if (--buf->refs == 0)
+ {
+ if (buf->ownsdata)
+ 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;
+
+ if (!buf->ownsdata)
+ return fz_throw("assert: grow borrowed memory");
+
+ newbp = fz_realloc(buf->bp, ep * 2);
+ if (!newbp)
+ return fz_throw("outofmem: resize buffer memory");
+
+ buf->bp = newbp;
+ buf->rp = buf->bp + rp;
+ buf->wp = buf->bp + wp;
+ buf->ep = buf->bp + ep * 2;
+
+ return fz_okay;
+}
+
+fz_error *
+fz_rewindbuffer(fz_buffer *buf)
+{
+ if (!buf->ownsdata)
+ return fz_throw("assert: rewind borrowed memory");
+
+ memmove(buf->bp, buf->rp, buf->wp - buf->rp);
+ buf->wp = buf->bp + (buf->wp - buf->rp);
+ buf->rp = buf->bp;
+
+ return fz_okay;
+}
+
diff --git a/fitz/stm_filter.c b/fitz/stm_filter.c
new file mode 100644
index 00000000..450433d4
--- /dev/null
+++ b/fitz/stm_filter.c
@@ -0,0 +1,61 @@
+#include "fitz-base.h"
+#include "fitz-stream.h"
+
+fz_error fz_kioneedin = { "<ioneedin>", "<internal>", "<internal>", 0, nil };
+fz_error fz_kioneedout = { "<ioneedout>", "<internal>", "<internal>", 0, nil };
+fz_error fz_kiodone = { "<iodone>", "<internal>", "<internal>", 0, nil };
+
+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;
+
+ if (f->done)
+ return fz_iodone;
+
+ 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;
+
+ /* iodone or error */
+ if (reason != fz_ioneedin && reason != fz_ioneedout)
+ {
+ if (reason != fz_iodone)
+ reason = fz_rethrow(reason, "cannot process filter");
+ out->eof = 1;
+ f->done = 1;
+ }
+
+ return reason;
+}
+
+fz_filter *
+fz_keepfilter(fz_filter *f)
+{
+ f->refs ++;
+ return f;
+}
+
+void
+fz_dropfilter(fz_filter *f)
+{
+ if (--f->refs == 0)
+ {
+ if (f->drop)
+ f->drop(f);
+ fz_free(f);
+ }
+}
+
diff --git a/fitz/stm_misc.c b/fitz/stm_misc.c
new file mode 100644
index 00000000..70c1c315
--- /dev/null
+++ b/fitz/stm_misc.c
@@ -0,0 +1,97 @@
+/*
+ * Miscellaneous I/O functions
+ */
+
+#include "fitz-base.h"
+#include "fitz-stream.h"
+
+int
+fz_tell(fz_stream *stm)
+{
+ if (stm->mode == FZ_SREAD)
+ return fz_rtell(stm);
+ return fz_wtell(stm);
+}
+
+fz_error *
+fz_seek(fz_stream *stm, int offset, int whence)
+{
+ if (stm->mode == FZ_SREAD)
+ return fz_rseek(stm, offset, whence);
+ return fz_wseek(stm, offset, whence);
+}
+
+/*
+ * Read a line terminated by LF or CR or CRLF.
+ */
+
+fz_error *
+fz_readline(fz_stream *stm, char *mem, int n)
+{
+ fz_error *error;
+
+ char *s = mem;
+ int c = EOF;
+ while (n > 1)
+ {
+ c = fz_readbyte(stm);
+ if (c == EOF)
+ break;
+ if (c == '\r') {
+ c = fz_peekbyte(stm);
+ if (c == '\n')
+ c = fz_readbyte(stm);
+ break;
+ }
+ if (c == '\n')
+ break;
+ *s++ = c;
+ n--;
+ }
+ if (n)
+ *s = '\0';
+
+ error = fz_readerror(stm);
+ if (error)
+ return fz_rethrow(error, "cannot read line");
+ return fz_okay;
+}
+
+/*
+ * Utility function to consume all the contents of an input stream into
+ * a freshly allocated buffer.
+ */
+
+fz_error *
+fz_readall(fz_buffer **bufp, fz_stream *stm, int sizehint)
+{
+ fz_error *error;
+ fz_buffer *buf;
+ int c;
+
+ if (sizehint == 0)
+ sizehint = 4 * 1024;
+
+ error = fz_newbuffer(&buf, sizehint);
+ if (error)
+ return fz_rethrow(error, "cannot create scratch buffer");
+
+ for (c = fz_readbyte(stm); c != EOF; c = fz_readbyte(stm))
+ {
+ if (buf->wp == buf->ep)
+ {
+ error = fz_growbuffer(buf);
+ if (error)
+ {
+ fz_dropbuffer(buf);
+ return fz_rethrow(error, "cannot resize scratch buffer");
+ }
+ }
+
+ *buf->wp++ = c;
+ }
+
+ *bufp = buf;
+ return fz_okay;
+}
+
diff --git a/fitz/stm_open.c b/fitz/stm_open.c
new file mode 100644
index 00000000..14e52642
--- /dev/null
+++ b/fitz/stm_open.c
@@ -0,0 +1,242 @@
+/*
+ * Creation and destruction.
+ */
+
+#include "fitz-base.h"
+#include "fitz-stream.h"
+
+static fz_stream *
+newstm(int kind, int mode)
+{
+ fz_stream *stm;
+
+ stm = fz_malloc(sizeof(fz_stream));
+ if (!stm)
+ return nil;
+
+ stm->refs = 1;
+ stm->kind = kind;
+ stm->mode = mode;
+ stm->dead = 0;
+ stm->error = fz_okay;
+ stm->buffer = nil;
+
+ stm->chain = nil;
+ stm->filter = nil;
+ stm->file = -1;
+
+ return stm;
+}
+
+fz_stream *
+fz_keepstream(fz_stream *stm)
+{
+ stm->refs ++;
+ return stm;
+}
+
+void
+fz_dropstream(fz_stream *stm)
+{
+ stm->refs --;
+ if (stm->refs == 0)
+ {
+ if (stm->error)
+ {
+ fflush(stdout);
+ fz_printerror(stm->error);
+ fz_droperror(stm->error);
+ fflush(stderr);
+ fz_warn("dropped unhandled ioerror");
+ }
+
+ if (stm->mode == FZ_SWRITE)
+ {
+ stm->buffer->eof = 1;
+ fz_flush(stm);
+ }
+
+ switch (stm->kind)
+ {
+ case FZ_SFILE:
+ close(stm->file);
+ break;
+ case FZ_SFILTER:
+ fz_dropfilter(stm->filter);
+ fz_dropstream(stm->chain);
+ break;
+ case FZ_SBUFFER:
+ break;
+ }
+
+ fz_dropbuffer(stm->buffer);
+ fz_free(stm);
+ }
+}
+
+static fz_error *
+openfile(fz_stream **stmp, char *path, int mode, int realmode)
+{
+ fz_error *error;
+ fz_stream *stm;
+
+ stm = newstm(FZ_SFILE, mode);
+ if (!stm)
+ return fz_throw("outofmem: stream struct");
+
+ error = fz_newbuffer(&stm->buffer, FZ_BUFSIZE);
+ if (error)
+ {
+ fz_free(stm);
+ return fz_rethrow(error, "cannot create buffer");
+ }
+
+ stm->file = open(path, realmode, 0666);
+ if (stm->file < 0)
+ {
+ fz_dropbuffer(stm->buffer);
+ fz_free(stm);
+ return fz_throw("syserr: open '%s': %s", path, strerror(errno));
+ }
+
+ *stmp = stm;
+ return fz_okay;
+}
+
+static fz_error *
+openfilter(fz_stream **stmp, fz_filter *flt, fz_stream *src, int mode)
+{
+ fz_error *error;
+ fz_stream *stm;
+
+ stm = newstm(FZ_SFILTER, mode);
+ if (!stm)
+ return fz_throw("outofmem: stream struct");
+
+ error = fz_newbuffer(&stm->buffer, FZ_BUFSIZE);
+ if (error)
+ {
+ fz_free(stm);
+ return fz_rethrow(error, "cannot create buffer");
+ }
+
+ stm->chain = fz_keepstream(src);
+ stm->filter = fz_keepfilter(flt);
+
+ *stmp = stm;
+ return fz_okay;
+}
+
+static fz_error *
+openbuffer(fz_stream **stmp, fz_buffer *buf, int mode)
+{
+ fz_stream *stm;
+
+ stm = newstm(FZ_SBUFFER, mode);
+ if (!stm)
+ return fz_throw("outofmem: stream struct");
+
+ stm->buffer = fz_keepbuffer(buf);
+
+ if (mode == FZ_SREAD)
+ stm->buffer->eof = 1;
+
+ *stmp = stm;
+ return fz_okay;
+}
+
+fz_error * fz_openrfile(fz_stream **stmp, char *path)
+{
+ fz_error *error;
+ error = openfile(stmp, path, FZ_SREAD, O_BINARY | O_RDONLY);
+ if (error)
+ return fz_rethrow(error, "cannot open file for reading: '%s'", path);
+ return fz_okay;
+}
+
+fz_error * fz_openwfile(fz_stream **stmp, char *path)
+{
+ fz_error *error;
+ error = openfile(stmp, path, FZ_SWRITE,
+ O_BINARY | O_WRONLY | O_CREAT | O_TRUNC);
+ if (error)
+ return fz_rethrow(error, "cannot open file for writing: '%s'", path);
+ return fz_okay;
+}
+
+fz_error * fz_openafile(fz_stream **stmp, char *path)
+{
+ fz_error *error;
+ int t;
+
+ error = openfile(stmp, path, FZ_SWRITE, O_BINARY | O_WRONLY);
+ if (error)
+ return fz_rethrow(error, "cannot open file for writing: '%s'", path);
+
+ t = lseek((*stmp)->file, 0, 2);
+ if (t < 0)
+ {
+ (*stmp)->dead = 1;
+ return fz_throw("syserr: lseek '%s': %s", path, strerror(errno));
+ }
+
+ return fz_okay;
+}
+
+fz_error * fz_openrfilter(fz_stream **stmp, fz_filter *flt, fz_stream *src)
+{
+ fz_error *error;
+ error = openfilter(stmp, flt, src, FZ_SREAD);
+ if (error)
+ return fz_rethrow(error, "cannot create reading filter stream");
+ return fz_okay;
+}
+
+fz_error * fz_openwfilter(fz_stream **stmp, fz_filter *flt, fz_stream *src)
+{
+ fz_error *error;
+ error = openfilter(stmp, flt, src, FZ_SWRITE);
+ if (error)
+ return fz_rethrow(error, "cannot create writing filter stream");
+ return fz_okay;
+}
+
+fz_error * fz_openrbuffer(fz_stream **stmp, fz_buffer *buf)
+{
+ fz_error *error;
+ error = openbuffer(stmp, buf, FZ_SREAD);
+ if (error)
+ return fz_rethrow(error, "cannot create reading buffer stream");
+ return fz_okay;
+}
+
+fz_error * fz_openwbuffer(fz_stream **stmp, fz_buffer *buf)
+{
+ fz_error *error;
+ error = openbuffer(stmp, buf, FZ_SWRITE);
+ if (error)
+ return fz_rethrow(error, "cannot create writing buffer stream");
+ return fz_okay;
+}
+
+fz_error * fz_openrmemory(fz_stream **stmp, unsigned char *mem, int len)
+{
+ fz_error *error;
+ fz_buffer *buf;
+
+ error = fz_newbufferwithmemory(&buf, mem, len);
+ if (error)
+ return fz_rethrow(error, "cannot create memory buffer");
+
+ error = fz_openrbuffer(stmp, buf);
+ if (error)
+ {
+ fz_dropbuffer(buf);
+ return fz_rethrow(error, "cannot open memory buffer stream");
+ }
+
+ fz_dropbuffer(buf);
+
+ return fz_okay;
+}
+
diff --git a/fitz/stm_read.c b/fitz/stm_read.c
new file mode 100644
index 00000000..e0a6e8a1
--- /dev/null
+++ b/fitz/stm_read.c
@@ -0,0 +1,313 @@
+/*
+ * Input streams.
+ */
+
+#include "fitz-base.h"
+#include "fitz-stream.h"
+
+fz_error *
+fz_readimp(fz_stream *stm)
+{
+ fz_buffer *buf = stm->buffer;
+ fz_error *error;
+ fz_error *reason;
+ int produced;
+ int n;
+
+ if (stm->dead)
+ return fz_throw("assert: read from dead stream");
+
+ if (stm->mode != FZ_SREAD)
+ return fz_throw("assert: read from writing stream");
+
+ if (buf->eof)
+ return fz_okay;
+
+ error = fz_rewindbuffer(buf);
+ if (error)
+ {
+ stm->dead = 1;
+ return fz_rethrow(error, "cannot rewind output buffer");
+ }
+
+ if (buf->ep - buf->wp == 0)
+ {
+ error = fz_growbuffer(buf);
+ if (error)
+ {
+ stm->dead = 1;
+ return fz_rethrow(error, "cannot grow output buffer");
+ }
+ }
+
+ switch (stm->kind)
+ {
+
+ case FZ_SFILE:
+ n = read(stm->file, buf->wp, buf->ep - buf->wp);
+ if (n == -1)
+ {
+ stm->dead = 1;
+ return fz_throw("syserr: read: %s", strerror(errno));
+ }
+
+ if (n == 0)
+ buf->eof = 1;
+ buf->wp += n;
+
+ return fz_okay;
+
+ case FZ_SFILTER:
+ produced = 0;
+
+ while (1)
+ {
+ reason = fz_process(stm->filter, stm->chain->buffer, buf);
+
+ if (stm->filter->produced)
+ produced = 1;
+
+ if (reason == fz_ioneedin)
+ {
+ error = fz_readimp(stm->chain);
+ if (error)
+ {
+ stm->dead = 1;
+ return fz_rethrow(error, "cannot read from input stream");
+ }
+ }
+
+ else if (reason == fz_ioneedout)
+ {
+ if (produced)
+ return 0;
+
+ if (buf->rp > buf->bp)
+ {
+ error = fz_rewindbuffer(buf);
+ if (error)
+ {
+ stm->dead = 1;
+ return fz_rethrow(error, "cannot rewind buffer");
+ }
+ }
+ else
+ {
+ error = fz_growbuffer(buf);
+ if (error)
+ {
+ stm->dead = 1;
+ return fz_rethrow(error, "cannot grow buffer");
+ }
+ }
+ }
+
+ else if (reason == fz_iodone)
+ {
+ return fz_okay;
+ }
+
+ else
+ {
+ stm->dead = 1;
+ return fz_rethrow(reason, "cannot process filter");
+ }
+ }
+
+ case FZ_SBUFFER:
+ return fz_okay;
+
+ default:
+ return fz_throw("assert: unknown stream type");
+ }
+}
+
+int
+fz_rtell(fz_stream *stm)
+{
+ fz_buffer *buf = stm->buffer;
+ int t;
+
+ if (stm->dead)
+ return EOF;
+ if (stm->mode != FZ_SREAD)
+ return EOF;
+
+ switch (stm->kind)
+ {
+ case FZ_SFILE:
+ t = lseek(stm->file, 0, 1);
+ if (t < 0)
+ {
+ fz_warn("syserr: lseek: %s", strerror(errno));
+ stm->dead = 1;
+ return EOF;
+ }
+ return t - (buf->wp - buf->rp);
+
+ case FZ_SFILTER:
+ return stm->filter->count - (buf->wp - buf->rp);
+
+ case FZ_SBUFFER:
+ return buf->rp - buf->bp;
+
+ default:
+ return EOF;
+ }
+}
+
+fz_error *
+fz_rseek(fz_stream *stm, int offset, int whence)
+{
+ fz_error *error;
+ fz_buffer *buf = stm->buffer;
+ int t, c;
+
+ if (stm->dead)
+ return fz_throw("assert: seek in dead stream");
+
+ if (stm->mode != FZ_SREAD)
+ return fz_throw("assert: read operation on writing stream");
+
+ if (whence == 1)
+ {
+ int cur = fz_rtell(stm);
+ if (cur < 0)
+ return fz_throw("cannot tell current position");
+ offset = cur + offset;
+ whence = 0;
+ }
+
+ buf->eof = 0;
+
+ switch (stm->kind)
+ {
+ case FZ_SFILE:
+ t = lseek(stm->file, offset, whence);
+ if (t < 0)
+ {
+ stm->dead = 1;
+ return fz_throw("syserr: lseek: %s", strerror(errno));
+ }
+
+ buf->rp = buf->bp;
+ buf->wp = buf->bp;
+
+ return fz_okay;
+
+ case FZ_SFILTER:
+ if (whence == 0)
+ {
+ if (offset < fz_rtell(stm))
+ {
+ stm->dead = 1;
+ return fz_throw("assert: seek backwards in filter");
+ }
+ while (fz_rtell(stm) < offset)
+ {
+ c = fz_readbyte(stm);
+ if (c == EOF)
+ {
+ error = fz_readerror(stm);
+ if (error)
+ return fz_rethrow(error, "cannot seek forward in filter");
+ break;
+ }
+ }
+ return fz_okay;
+ }
+
+ stm->dead = 1;
+ return fz_throw("assert: relative seek in filter");
+
+ case FZ_SBUFFER:
+ if (whence == 0)
+ buf->rp = CLAMP(buf->bp + offset, buf->bp, buf->ep);
+ else
+ buf->rp = CLAMP(buf->ep + offset, buf->bp, buf->ep);
+ return fz_okay;
+
+ default:
+ return fz_throw("unknown stream type");
+ }
+}
+
+fz_error *
+fz_read(int *np, fz_stream *stm, unsigned char *mem, int n)
+{
+ fz_error *error;
+ fz_buffer *buf = stm->buffer;
+ int i = 0;
+
+ while (i < n)
+ {
+ while (buf->rp < buf->wp && i < n)
+ mem[i++] = *buf->rp++;
+
+ if (buf->rp == buf->wp)
+ {
+ if (buf->eof)
+ {
+ *np = i;
+ return fz_okay;
+ }
+
+ error = fz_readimp(stm);
+ if (error)
+ return fz_rethrow(error, "cannot produce data");
+ }
+ }
+
+ *np = i;
+ return fz_okay;
+}
+
+fz_error *
+fz_readerror(fz_stream *stm)
+{
+ fz_error *error;
+ if (stm->error)
+ {
+ error = stm->error;
+ stm->error = fz_okay;
+ return fz_rethrow(error, "delayed read error");
+ }
+ return fz_okay;
+}
+
+int
+fz_readbytex(fz_stream *stm)
+{
+ fz_buffer *buf = stm->buffer;
+
+ if (buf->rp == buf->wp)
+ {
+ if (!buf->eof && !stm->error)
+ {
+ fz_error *error = fz_readimp(stm);
+ if (error)
+ stm->error = fz_rethrow(error, "cannot read data");
+ }
+ }
+
+ return buf->rp < buf->wp ? *buf->rp++ : EOF ;
+}
+
+int
+fz_peekbytex(fz_stream *stm)
+{
+ fz_buffer *buf = stm->buffer;
+
+ if (buf->rp == buf->wp)
+ {
+ if (!buf->eof && !stm->error)
+ {
+ fz_error *error = fz_readimp(stm);
+ if (error)
+ stm->error = fz_rethrow(error, "cannot read data");
+ }
+ }
+
+ return buf->rp < buf->wp ? *buf->rp : EOF ;
+}
+
diff --git a/fitz/stm_write.c b/fitz/stm_write.c
new file mode 100644
index 00000000..ed928b50
--- /dev/null
+++ b/fitz/stm_write.c
@@ -0,0 +1,313 @@
+/*
+ * Output streams.
+ */
+
+#include "fitz-base.h"
+#include "fitz-stream.h"
+
+int
+fz_wtell(fz_stream *stm)
+{
+ fz_buffer *buf = stm->buffer;
+ int t;
+
+ if (stm->dead)
+ return EOF;
+
+ if (stm->mode != FZ_SWRITE)
+ return EOF;
+
+ switch (stm->kind)
+ {
+ case FZ_SFILE:
+ t = lseek(stm->file, 0, 1);
+ if (t < 0)
+ {
+ fz_warn("syserr: lseek: %s", strerror(errno));
+ stm->dead = 1;
+ return EOF;
+ }
+ return t + (buf->wp - buf->rp);
+
+ case FZ_SFILTER:
+ return stm->filter->count + (buf->wp - buf->rp);
+
+ case FZ_SBUFFER:
+ return buf->wp - buf->bp;
+
+ default:
+ return EOF;
+ }
+}
+
+fz_error *
+fz_wseek(fz_stream *stm, int offset, int whence)
+{
+ fz_buffer *buf = stm->buffer;
+ int t;
+
+ if (stm->dead)
+ return fz_throw("assert: seek in dead stream");
+
+ if (stm->mode != FZ_SWRITE)
+ return fz_throw("assert: write operation on reading stream");
+
+ if (stm->kind != FZ_SFILE)
+ return fz_throw("assert: write seek on non-file stream");
+
+ t = lseek(stm->file, offset, whence);
+ if (t < 0)
+ {
+ stm->dead = 1;
+ return fz_throw("syserr: lseek: %s", strerror(errno));
+ }
+
+ buf->rp = buf->bp;
+ buf->wp = buf->bp;
+ buf->eof = 0;
+
+ return fz_okay;
+}
+
+static fz_error *
+fz_flushfilterimp(fz_stream *stm)
+{
+ fz_buffer *buf = stm->buffer;
+ fz_error *error;
+ fz_error *reason;
+
+loop:
+
+ reason = fz_process(stm->filter, stm->buffer, stm->chain->buffer);
+
+ if (reason == fz_ioneedin)
+ {
+ if (buf->rp > buf->bp)
+ {
+ error = fz_rewindbuffer(buf);
+ if (error)
+ {
+ stm->dead = 1;
+ return fz_rethrow(error, "cannot rewind buffer");
+ }
+ }
+ else
+ {
+ error = fz_growbuffer(buf);
+ if (error)
+ {
+ stm->dead = 1;
+ return fz_rethrow(error, "cannot grow buffer");
+ }
+ }
+ }
+
+ else if (reason == fz_ioneedout)
+ {
+ error = fz_flush(stm->chain);
+ if (error)
+ return fz_rethrow(error, "cannot flush chain buffer");
+ }
+
+ else if (reason == fz_iodone)
+ {
+ stm->dead = 2; /* special flag that we are dead because of eod */
+ }
+
+ else
+ {
+ stm->dead = 1;
+ return fz_rethrow(reason, "cannot process filter");
+ }
+
+ /* if we are at eof, repeat until other filter sets otherside to eof */
+ if (buf->eof && !stm->chain->buffer->eof)
+ goto loop;
+
+ return fz_okay;
+}
+
+/*
+ * Empty the buffer into the sink.
+ * Promise to make more space available.
+ * Called by fz_write and fz_dropstream.
+ * If buffer is eof, then all data must be flushed.
+ */
+fz_error *
+fz_flush(fz_stream *stm)
+{
+ fz_buffer *buf = stm->buffer;
+ fz_error *error;
+ int t;
+
+ if (stm->dead == 2) /* eod flag */
+ return fz_okay;
+
+ if (stm->dead)
+ return fz_throw("assert: flush on dead stream");
+
+ if (stm->mode != FZ_SWRITE)
+ return fz_throw("assert: write operation on reading stream");
+
+ switch (stm->kind)
+ {
+ case FZ_SFILE:
+ while (buf->rp < buf->wp)
+ {
+ t = write(stm->file, buf->rp, buf->wp - buf->rp);
+ if (t < 0)
+ {
+ stm->dead = 1;
+ return fz_throw("syserr: write: %s", strerror(errno));
+ }
+
+ buf->rp += t;
+ }
+
+ if (buf->rp > buf->bp)
+ {
+ error = fz_rewindbuffer(buf);
+ if (error)
+ {
+ stm->dead = 1;
+ return fz_rethrow(error, "cannot rewind buffer");
+ }
+ }
+
+ return fz_okay;
+
+ case FZ_SFILTER:
+ error = fz_flushfilterimp(stm);
+ if (error)
+ return fz_rethrow(error, "cannot flush through filter");
+ return fz_okay;
+
+ case FZ_SBUFFER:
+ if (!buf->eof && buf->wp == buf->ep)
+ {
+ error = fz_growbuffer(buf);
+ if (error)
+ {
+ stm->dead = 1;
+ return fz_rethrow(error, "cannot grow buffer");
+ }
+ }
+ return fz_okay;
+
+ default:
+ return fz_throw("unknown stream type");
+ }
+}
+
+/*
+ * Write data to stream.
+ * Buffer until internal buffer is full.
+ * When full, call fz_flush to make more space available.
+ * Return error if all the data could not be written.
+ */
+fz_error *
+fz_write(fz_stream *stm, unsigned char *mem, int n)
+{
+ fz_buffer *buf = stm->buffer;
+ fz_error *error;
+ int i = 0;
+
+ if (stm->dead)
+ return fz_throw("assert: write on dead stream");
+
+ if (stm->mode != FZ_SWRITE)
+ return fz_throw("assert: write on reading stream");
+
+ while (i < n)
+ {
+ while (buf->wp < buf->ep && i < n)
+ *buf->wp++ = mem[i++];
+
+ if (buf->wp == buf->ep && i < n)
+ {
+ error = fz_flush(stm);
+ if (error)
+ return fz_rethrow(error, "cannot flush buffer");
+ if (stm->dead)
+ return fz_throw("assert: write on dead stream");
+ }
+ }
+
+ return fz_okay;
+}
+
+fz_error *
+fz_printstr(fz_stream *stm, char *s)
+{
+ return fz_write(stm, (unsigned char *) s, strlen(s));
+}
+
+fz_error *
+fz_printobj(fz_stream *file, fz_obj *obj, int tight)
+{
+ fz_error *error;
+ unsigned char buf[1024];
+ char *ptr;
+ int n;
+
+ n = fz_sprintobj(nil, 0, obj, tight);
+ if (n < sizeof buf)
+ {
+ fz_sprintobj(buf, sizeof buf, obj, tight);
+ error = fz_write(file, (unsigned char *) buf, n);
+ if (error)
+ return fz_rethrow(error, "cannot write buffer");
+ return fz_okay;
+ }
+ else
+ {
+ ptr = fz_malloc(n);
+ if (!ptr)
+ return fz_throw("outofmem: scratch buffer");
+ fz_sprintobj(ptr, n, obj, tight);
+ error = fz_write(file, (unsigned char *) ptr, n);
+ fz_free(ptr);
+ if (error)
+ return fz_rethrow(error, "cannot write buffer");
+ return fz_okay;
+ }
+}
+
+fz_error *
+fz_print(fz_stream *stm, char *fmt, ...)
+{
+ fz_error *error;
+ va_list ap;
+ char buf[1024];
+ char *p;
+ int n;
+
+ va_start(ap, fmt);
+ n = vsnprintf(buf, sizeof buf, fmt, ap);
+ va_end(ap);
+
+ if (n < sizeof buf)
+ {
+ error = fz_write(stm, (unsigned char *) buf, n);
+ if (error)
+ return fz_rethrow(error, "cannot write buffer");
+ return fz_okay;
+ }
+
+ p = fz_malloc(n);
+ if (!p)
+ return fz_throw("outofmem: scratch buffer");
+
+ va_start(ap, fmt);
+ vsnprintf(p, n, fmt, ap);
+ va_end(ap);
+
+ error = fz_write(stm, (unsigned char *) p, n);
+
+ fz_free(p);
+
+ if (error)
+ return fz_rethrow(error, "cannot write buffer");
+ return fz_okay;
+}
+
diff --git a/fitz/util_getopt.c b/fitz/util_getopt.c
new file mode 100644
index 00000000..8025334e
--- /dev/null
+++ b/fitz/util_getopt.c
@@ -0,0 +1,117 @@
+/*
+ * 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_gettimeofday.c b/fitz/util_gettimeofday.c
new file mode 100644
index 00000000..fee36673
--- /dev/null
+++ b/fitz/util_gettimeofday.c
@@ -0,0 +1,50 @@
+#include <time.h>
+#include <winsock2.h>
+#include <windows.h>
+
+#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
+ #define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
+#else
+ #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
+#endif
+
+struct timezone
+{
+ int tz_minuteswest; /* minutes W of Greenwich */
+ int tz_dsttime; /* type of dst correction */
+};
+
+int gettimeofday(struct timeval *tv, struct timezone *tz)
+{
+ FILETIME ft;
+ unsigned __int64 tmpres = 0;
+ static int tzflag = 0;
+
+ if (NULL != tv)
+ {
+ GetSystemTimeAsFileTime(&ft);
+
+ tmpres |= ft.dwHighDateTime;
+ tmpres <<= 32;
+ tmpres |= ft.dwLowDateTime;
+
+ tmpres /= 10; /*convert into microseconds*/
+ /*converting file time to unix epoch*/
+ tmpres -= DELTA_EPOCH_IN_MICROSECS;
+ tv->tv_sec = (long)(tmpres / 1000000UL);
+ tv->tv_usec = (long)(tmpres % 1000000UL);
+ }
+
+ if (NULL != tz)
+ {
+ if (!tzflag)
+ {
+ _tzset();
+ tzflag++;
+ }
+ tz->tz_minuteswest = _timezone / 60;
+ tz->tz_dsttime = _daylight;
+ }
+
+ return 0;
+}
diff --git a/fitz/util_strlcpy.c b/fitz/util_strlcpy.c
new file mode 100644
index 00000000..15b378b2
--- /dev/null
+++ b/fitz/util_strlcpy.c
@@ -0,0 +1,65 @@
+#include <string.h>
+
+/* 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.
+ */
+
+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 */
+}
+
+/* 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.
+ */
+
+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_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;
+}
+