summaryrefslogtreecommitdiff
path: root/filter
diff options
context:
space:
mode:
Diffstat (limited to 'filter')
-rw-r--r--filter/TODO8
-rw-r--r--filter/a85d.c129
-rw-r--r--filter/a85e.c128
-rw-r--r--filter/ahxd.c113
-rw-r--r--filter/ahxe.c65
-rw-r--r--filter/arc4filter.c47
-rw-r--r--filter/buffer.c57
-rw-r--r--filter/dctc.h39
-rw-r--r--filter/dctd.c215
-rw-r--r--filter/dcte.c254
-rw-r--r--filter/faxc.h116
-rw-r--r--filter/faxd.c463
-rw-r--r--filter/faxd.h61
-rw-r--r--filter/faxdtab.c931
-rw-r--r--filter/faxe.c448
-rw-r--r--filter/faxe.h37
-rw-r--r--filter/faxetab.c131
-rw-r--r--filter/file.c589
-rw-r--r--filter/filter.c59
-rw-r--r--filter/flate.c178
-rw-r--r--filter/jbig2d.c97
-rw-r--r--filter/jpxd.c144
-rw-r--r--filter/lzwd.c263
-rw-r--r--filter/lzwe.c257
-rw-r--r--filter/null.c51
-rw-r--r--filter/pipeline.c128
-rw-r--r--filter/predict.c239
-rw-r--r--filter/rld.c67
-rw-r--r--filter/rle.c238
29 files changed, 5552 insertions, 0 deletions
diff --git a/filter/TODO b/filter/TODO
new file mode 100644
index 00000000..404d76c2
--- /dev/null
+++ b/filter/TODO
@@ -0,0 +1,8 @@
+validate ahxd pushback
+go through eof responsibility
+be more defensive of api user errors
+flate needin/needout
+
+jbig2 rewrite
+dctencode params
+
diff --git a/filter/a85d.c b/filter/a85d.c
new file mode 100644
index 00000000..08c96a1b
--- /dev/null
+++ b/filter/a85d.c
@@ -0,0 +1,129 @@
+#include <fitz.h>
+
+typedef struct fz_a85d_s fz_a85d;
+
+struct fz_a85d_s
+{
+ fz_filter super;
+ unsigned long word;
+ int count;
+};
+
+static inline int iswhite(int a)
+{
+ switch (a) {
+ case '\n': case '\r': case '\t': case ' ':
+ case '\0': case '\f': case '\b': case 0177:
+ return 1;
+ }
+ return 0;
+}
+
+fz_error *
+fz_newa85d(fz_filter **fp, fz_obj *params)
+{
+ FZ_NEWFILTER(fz_a85d, f, a85d);
+ f->word = 0;
+ f->count = 0;
+ return nil;
+}
+
+void
+fz_freea85d(fz_filter *f)
+{
+ fz_free(f);
+}
+
+fz_error *
+fz_processa85d(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_a85d *f = (fz_a85d*)filter;
+ int c;
+
+ while (1)
+ {
+ if (in->rp == in->wp)
+ return fz_ioneedin;
+
+ c = *in->rp++;
+
+ if (c >= '!' && c <= 'u') {
+ if (f->count == 4) {
+ if (out->wp + 4 > out->ep) {
+ in->rp --;
+ return fz_ioneedout;
+ }
+
+ f->word = f->word * 85 + (c - '!');
+
+ *out->wp++ = (f->word >> 24) & 0xff;
+ *out->wp++ = (f->word >> 16) & 0xff;
+ *out->wp++ = (f->word >> 8) & 0xff;
+ *out->wp++ = (f->word) & 0xff;
+
+ f->word = 0;
+ f->count = 0;
+ }
+ else {
+ f->word = f->word * 85 + (c - '!');
+ f->count ++;
+ }
+ }
+
+ else if (c == 'z' && f->count == 0) {
+ if (out->wp + 4 > out->ep) {
+ in->rp --;
+ return fz_ioneedout;
+ }
+ *out->wp++ = 0;
+ *out->wp++ = 0;
+ *out->wp++ = 0;
+ *out->wp++ = 0;
+ }
+
+ else if (c == '~') {
+ if (in->rp == in->wp) {
+ in->rp --;
+ return fz_ioneedin;
+ }
+
+ c = *in->rp++;
+
+ if (c != '>') {
+ return fz_throw("ioerror: bad eod marker in a85d");
+ }
+
+ if (out->wp + f->count - 1 > out->ep) {
+ in->rp -= 2;
+ return fz_ioneedout;
+ }
+
+ switch (f->count) {
+ case 0:
+ break;
+ case 1:
+ return fz_throw("ioerror: partial final byte in a85d");
+ case 2:
+ f->word = f->word * (85L * 85 * 85) + 0xffffffL;
+ goto o1;
+ case 3:
+ f->word = f->word * (85L * 85) + 0xffffL;
+ goto o2;
+ case 4:
+ f->word = f->word * 85 + 0xffL;
+ *(out->wp+2) = f->word >> 8;
+o2: *(out->wp+1) = f->word >> 16;
+o1: *(out->wp+0) = f->word >> 24;
+ out->wp += f->count - 1;
+ break;
+ }
+ out->eof = 1;
+ return fz_iodone;
+ }
+
+ else if (!iswhite(c)) {
+ return fz_throw("ioerror: bad data in a85d: '%c'", c);
+ }
+ }
+}
+
diff --git a/filter/a85e.c b/filter/a85e.c
new file mode 100644
index 00000000..c57aac4f
--- /dev/null
+++ b/filter/a85e.c
@@ -0,0 +1,128 @@
+#include <fitz.h>
+
+typedef struct fz_a85e_s fz_a85e;
+
+struct fz_a85e_s
+{
+ fz_filter super;
+ int c;
+};
+
+fz_error *
+fz_newa85e(fz_filter **fp, fz_obj *params)
+{
+ FZ_NEWFILTER(fz_a85e, f, a85e);
+ f->c = 0;
+ return nil;
+}
+
+void
+fz_freea85e(fz_filter *f)
+{
+ fz_free(f);
+}
+
+fz_error *
+fz_processa85e(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_a85e *f = (fz_a85e*)filter;
+ unsigned long word;
+ int count;
+ int n;
+
+ n = 0;
+
+ while (1)
+ {
+ if (f->c >= 70) {
+ if (out->wp + 1 > out->ep)
+ return fz_ioneedout;
+ *out->wp++ = '\n';
+ f->c = 0;
+ n ++;
+ }
+
+ if (in->rp + 4 <= in->wp)
+ {
+ word = (in->rp[0] << 24) |
+ (in->rp[1] << 16) |
+ (in->rp[2] << 8) |
+ (in->rp[3]);
+ if (word == 0) {
+ if (out->wp + 1 > out->ep)
+ return fz_ioneedout;
+ *out->wp++ = 'z';
+ f->c ++;
+ n ++;
+ }
+ else {
+ unsigned long v1, v2, v3, v4;
+
+ if (out->wp + 5 > out->ep)
+ return fz_ioneedout;
+
+ v4 = word / 85;
+ v3 = v4 / 85;
+ v2 = v3 / 85;
+ v1 = v2 / 85;
+
+ *out->wp++ = (v1 % 85) + '!';
+ *out->wp++ = (v2 % 85) + '!';
+ *out->wp++ = (v3 % 85) + '!';
+ *out->wp++ = (v4 % 85) + '!';
+ *out->wp++ = (word % 85) + '!';
+ f->c += 5;
+ n += 5;
+ }
+ in->rp += 4;
+ }
+
+ else if (in->eof)
+ {
+ unsigned long divisor;
+
+ if (in->rp == in->wp)
+ goto needinput; /* handle clean eof here */
+
+ count = in->wp - in->rp;
+
+ if (out->wp + count + 3 > out->ep)
+ return fz_ioneedout;
+
+ word = 0;
+ switch (count) {
+ case 3: word |= in->rp[2] << 8;
+ case 2: word |= in->rp[1] << 16;
+ case 1: word |= in->rp[0] << 24;
+ }
+ in->rp += count;
+
+ divisor = 85L * 85 * 85 * 85;
+ while (count-- >= 0) {
+ *out->wp++ = ((word / divisor) % 85) + '!';
+ divisor /= 85;
+ }
+
+ *out->wp++ = '~';
+ *out->wp++ = '>';
+ out->eof = 1;
+ return fz_iodone;
+ }
+
+ else {
+ goto needinput;
+ }
+ }
+
+needinput:
+ if (in->eof) {
+ if (out->wp + 2 > out->ep)
+ return fz_ioneedout;
+ *out->wp++ = '~';
+ *out->wp++ = '>';
+ out->eof = 1;
+ return fz_iodone;
+ }
+ return fz_ioneedin;
+}
+
diff --git a/filter/ahxd.c b/filter/ahxd.c
new file mode 100644
index 00000000..b557399c
--- /dev/null
+++ b/filter/ahxd.c
@@ -0,0 +1,113 @@
+#include <fitz.h>
+
+typedef struct fz_ahxd_s fz_ahxd;
+
+struct fz_ahxd_s
+{
+ fz_filter super;
+ int odd;
+ int a;
+};
+
+static inline int iswhite(int a)
+{
+ switch (a) {
+ case '\n': case '\r': case '\t': case ' ':
+ case '\0': case '\f': case '\b': case 0177:
+ return 1;
+ }
+ return 0;
+}
+
+static inline int ishex(int a)
+{
+ return (a >= 'A' && a <= 'F') ||
+ (a >= 'a' && a <= 'f') ||
+ (a >= '0' && a <= '9');
+}
+
+static inline int fromhex(int a)
+{
+ if (a >= 'A' && a <= 'F')
+ return a - 'A' + 0xA;
+ if (a >= 'a' && a <= 'f')
+ return a - 'a' + 0xA;
+ if (a >= '0' && a <= '9')
+ return a - '0';
+ return 0;
+}
+
+fz_error *
+fz_newahxd(fz_filter **fp, fz_obj *params)
+{
+ FZ_NEWFILTER(fz_ahxd, f, ahxd);
+ f->odd = 0;
+ f->a = 0;
+ return nil;
+}
+
+void
+fz_freeahxd(fz_filter *f)
+{
+ fz_free(f);
+}
+
+fz_error *
+fz_processahxd(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_ahxd *f = (fz_ahxd*)filter;
+ int b, c;
+
+ while (1)
+ {
+ if (in->rp == in->wp)
+ return fz_ioneedin;
+
+ if (out->wp == out->ep)
+ return fz_ioneedout;
+
+ c = *in->rp++;
+
+ if (ishex(c)) {
+ if (!f->odd) {
+ f->a = fromhex(c);
+ f->odd = 1;
+ }
+ else {
+ b = fromhex(c);
+ *out->wp++ = (f->a << 4) | b;
+ f->odd = 0;
+ }
+ }
+
+ else if (c == '>') {
+ if (f->odd)
+ *out->wp++ = (f->a << 4);
+ out->eof = 1;
+ return fz_iodone;
+ }
+
+ else if (!iswhite(c)) {
+ return fz_throw("ioerror: bad data in ahxd: '%c'", c);
+ }
+ }
+}
+
+void
+fz_pushbackahxd(fz_filter *filter, fz_buffer *in, fz_buffer *out, int n)
+{
+ int k;
+
+ assert(filter->process == fz_processahxd);
+ assert(out->wp - n >= out->rp);
+
+ k = 0;
+ while (k < n * 2) {
+ in->rp --;
+ if (ishex(*in->rp))
+ k ++;
+ }
+
+ out->wp -= n;
+}
+
diff --git a/filter/ahxe.c b/filter/ahxe.c
new file mode 100644
index 00000000..e092653b
--- /dev/null
+++ b/filter/ahxe.c
@@ -0,0 +1,65 @@
+#include <fitz.h>
+
+typedef struct fz_ahxe_s fz_ahxe;
+
+struct fz_ahxe_s
+{
+ fz_filter super;
+ int c;
+};
+
+static const char tohex[16] = "0123456789ABCDEF";
+
+fz_error *
+fz_newahxe(fz_filter **fp, fz_obj *params)
+{
+ FZ_NEWFILTER(fz_ahxe, f, ahxe);
+ f->c = 0;
+ return nil;
+}
+
+void
+fz_freeahxe(fz_filter *f)
+{
+ fz_free(f);
+}
+
+fz_error *
+fz_processahxe(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_ahxe *f = (fz_ahxe*)filter;
+ int a, b, c;
+
+ while (1)
+ {
+ if (in->rp == in->wp)
+ goto needinput;
+
+ if (out->wp + 2 >= out->ep) /* can write 3 bytes from 1 */
+ return fz_ioneedout;
+
+ c = *in->rp++;
+ a = tohex[(c >> 4) & 0x0f];
+ b = tohex[c & 0x0f];
+
+ *out->wp++ = a;
+ *out->wp++ = b;
+
+ f->c += 2;
+ if (f->c == 60) {
+ *out->wp++ = '\n';
+ f->c = 0;
+ }
+ }
+
+needinput:
+ if (in->eof) {
+ if (out->wp == out->ep)
+ return fz_ioneedout;
+ *out->wp++ = '>';
+ out->eof = 1;
+ return fz_iodone;
+ }
+ return fz_ioneedin;
+}
+
diff --git a/filter/arc4filter.c b/filter/arc4filter.c
new file mode 100644
index 00000000..257462d8
--- /dev/null
+++ b/filter/arc4filter.c
@@ -0,0 +1,47 @@
+#include <fitz.h>
+
+typedef struct fz_arc4c_s fz_arc4c;
+
+struct fz_arc4c_s
+{
+ fz_filter super;
+ fz_arc4 arc4;
+};
+
+fz_error *
+fz_newarc4filter(fz_filter **fp, unsigned char *key, unsigned keylen)
+{
+ FZ_NEWFILTER(fz_arc4c, f, arc4filter);
+ fz_arc4init(&f->arc4, key, keylen);
+ return nil;
+}
+
+void
+fz_freearc4filter(fz_filter *f)
+{
+ fz_free(f);
+}
+
+fz_error *
+fz_processarc4filter(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_arc4c *f = (fz_arc4c*)filter;
+ int n;
+
+ while (1)
+ {
+ if (in->rp + 1 > in->wp) {
+ if (in->eof)
+ return fz_iodone;
+ return fz_ioneedin;
+ }
+ if (out->wp + 1 > out->ep)
+ return fz_ioneedout;
+
+ n = MIN(in->wp - in->rp, out->ep - out->wp);
+ fz_arc4encrypt(&f->arc4, out->wp, in->rp, n);
+ in->rp += n;
+ out->wp += n;
+ }
+}
+
diff --git a/filter/buffer.c b/filter/buffer.c
new file mode 100644
index 00000000..7b728b9b
--- /dev/null
+++ b/filter/buffer.c
@@ -0,0 +1,57 @@
+#include <fitz.h>
+
+fz_error *
+fz_newbuffer(fz_buffer **bp, int size)
+{
+ fz_buffer *b;
+
+ b = *bp = fz_malloc(sizeof(fz_buffer));
+ if (!b) return fz_outofmem;
+
+ b->bp = fz_malloc(size);
+ if (!b->bp) { fz_free(b); return fz_outofmem; }
+
+ b->rp = b->bp;
+ b->wp = b->bp;
+ b->ep = b->bp + size;
+ b->eof = 0;
+
+ return nil;
+}
+
+void
+fz_freebuffer(fz_buffer *buf)
+{
+ fz_free(buf->bp);
+ fz_free(buf);
+}
+
+fz_error *
+fz_growbuffer(fz_buffer *buf)
+{
+ unsigned char *newbp;
+
+ int rp = buf->rp - buf->bp;
+ int wp = buf->wp - buf->bp;
+ int ep = buf->ep - buf->bp;
+
+ newbp = fz_realloc(buf->bp, ep * 2);
+ if (!newbp) return fz_outofmem;
+
+ buf->bp = newbp;
+ buf->rp = buf->bp + rp;
+ buf->wp = buf->bp + wp;
+ buf->ep = buf->bp + ep * 2;
+
+ return nil;
+}
+
+fz_error *
+fz_rewindbuffer(fz_buffer *buf)
+{
+ memmove(buf->bp, buf->rp, buf->wp - buf->rp);
+ buf->wp = buf->bp + (buf->wp - buf->rp);
+ buf->rp = buf->bp;
+ return nil;
+}
+
diff --git a/filter/dctc.h b/filter/dctc.h
new file mode 100644
index 00000000..8aa6aeb7
--- /dev/null
+++ b/filter/dctc.h
@@ -0,0 +1,39 @@
+/*
+ * Extend libjpegs error handler to use setjmp/longjmp
+ */
+
+#include <jpeglib.h>
+
+#include <setjmp.h>
+
+struct myerrmgr
+{
+ struct jpeg_error_mgr super;
+ jmp_buf jb;
+ char msg[JMSG_LENGTH_MAX];
+};
+
+static void myerrexit(j_common_ptr cinfo)
+{
+ struct myerrmgr *err = (struct myerrmgr *)cinfo->err;
+ char msgbuf[JMSG_LENGTH_MAX];
+ err->super.format_message(cinfo, msgbuf);
+ strlcpy(err->msg, msgbuf, sizeof err->msg);
+ longjmp(err->jb, 1);
+}
+
+static void myoutmess(j_common_ptr cinfo)
+{
+ struct myerrmgr *err = (struct myerrmgr *)cinfo->err;
+ char msgbuf[JMSG_LENGTH_MAX];
+ err->super.format_message(cinfo, msgbuf);
+ fprintf(stderr, "ioerror: dct: %s", msgbuf);
+}
+
+static void myiniterr(struct myerrmgr *err)
+{
+ jpeg_std_error(&err->super);
+ err->super.error_exit = myerrexit;
+ err->super.output_message = myoutmess;
+}
+
diff --git a/filter/dctd.c b/filter/dctd.c
new file mode 100644
index 00000000..dd978ac7
--- /dev/null
+++ b/filter/dctd.c
@@ -0,0 +1,215 @@
+#include <fitz.h>
+
+#include "dctc.h"
+
+typedef struct fz_dctd_s fz_dctd;
+
+struct mysrcmgr
+{
+ struct jpeg_source_mgr super;
+ fz_buffer *buf;
+ int skip;
+};
+
+struct fz_dctd_s
+{
+ fz_filter super;
+ struct jpeg_decompress_struct cinfo;
+ struct mysrcmgr src;
+ struct myerrmgr err;
+ int colortransform;
+ int stage;
+};
+
+static void myinitsource(j_decompress_ptr cinfo) { /* empty */ }
+static boolean myfillinput(j_decompress_ptr cinfo) { return FALSE; }
+static void mytermsource(j_decompress_ptr cinfo) { /* empty */ }
+
+static void myskipinput(j_decompress_ptr cinfo, long n)
+{
+ struct mysrcmgr *src = (struct mysrcmgr *)cinfo->src;
+ fz_buffer *in = src->buf;
+
+ assert(src->skip == 0);
+
+ in->rp = in->wp - src->super.bytes_in_buffer;
+
+ if (in->rp + n > in->wp) {
+ src->skip = (in->rp + n) - in->wp;
+ in->rp = in->wp;
+ }
+ else {
+ src->skip = 0;
+ in->rp += n;
+ }
+
+ src->super.bytes_in_buffer = in->wp - in->rp;
+ src->super.next_input_byte = in->rp;
+}
+
+fz_error *
+fz_newdctd(fz_filter **fp, fz_obj *params)
+{
+ fz_error *err;
+ fz_obj *obj;
+ int colortransform;
+
+ FZ_NEWFILTER(fz_dctd, d, dctd);
+
+ colortransform = 1;
+
+ if (params) {
+ obj = fz_dictgets(params, "ColorTransform");
+ if (obj) colortransform = fz_toint(obj);
+ }
+
+ d->colortransform = colortransform;
+ d->stage = 0;
+
+ /* setup error callback first thing */
+ myiniterr(&d->err);
+ d->cinfo.err = (struct jpeg_error_mgr*) &d->err;
+
+ if (setjmp(d->err.jb)) {
+ err = fz_throw("ioerror in dctd: %s", d->err.msg);
+ fz_free(d);
+ return err;
+ }
+
+ /* create decompression object. this zeroes cinfo except for err. */
+ jpeg_create_decompress(&d->cinfo);
+
+ /* prepare source manager */
+ d->cinfo.src = (struct jpeg_source_mgr *)&d->src;
+ d->src.super.init_source = myinitsource;
+ d->src.super.fill_input_buffer = myfillinput;
+ d->src.super.skip_input_data = myskipinput;
+ d->src.super.resync_to_restart = jpeg_resync_to_restart;
+ d->src.super.term_source = mytermsource;
+
+ d->src.super.bytes_in_buffer = 0;
+ d->src.super.next_input_byte = nil;
+ d->src.skip = 0;
+
+ return nil;
+}
+
+void
+fz_freedctd(fz_filter *filter)
+{
+ fz_dctd *d = (fz_dctd*)filter;
+ if (setjmp(d->err.jb)) {
+ fprintf(stderr, "ioerror in dct: jpeg_destroy_decompress: %s", d->err.msg);
+ return;
+ }
+ jpeg_destroy_decompress(&d->cinfo);
+}
+
+fz_error *
+fz_processdctd(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_dctd *d = (fz_dctd*)filter;
+ boolean b;
+ int i;
+ int stride;
+ JSAMPROW scanlines[1];
+
+ d->src.buf = in;
+
+ /* skip any bytes left over from myskipinput() */
+ if (d->src.skip > 0) {
+ if (in->rp + d->src.skip > in->wp) {
+ d->src.skip = (in->rp + d->src.skip) - in->wp;
+ in->rp = in->wp;
+ goto needinput;
+ }
+ else {
+ in->rp += d->src.skip;
+ d->src.skip = 0;
+ }
+ }
+
+ d->src.super.bytes_in_buffer = in->wp - in->rp;
+ d->src.super.next_input_byte = in->rp;
+
+ if (setjmp(d->err.jb)) {
+ return fz_throw("ioerror in dctd: %s", d->err.msg);
+ }
+
+ switch (d->stage)
+ {
+ case 0:
+ i = jpeg_read_header(&d->cinfo, TRUE);
+ if (i == JPEG_SUSPENDED)
+ goto needinput;
+
+ /* FIXME: default value if ColorTransform is not set */
+
+ if (!d->cinfo.saw_Adobe_marker) {
+ switch (d->cinfo.num_components) {
+ case 3:
+ if (d->colortransform)
+ d->cinfo.jpeg_color_space = JCS_YCbCr;
+ else
+ d->cinfo.jpeg_color_space = JCS_RGB;
+ break;
+ case 4:
+ if (d->colortransform)
+ d->cinfo.jpeg_color_space = JCS_YCCK;
+ else
+ d->cinfo.jpeg_color_space = JCS_CMYK;
+ break;
+ }
+ }
+
+ /* fall through */
+ d->stage = 1;
+
+ case 1:
+ b = jpeg_start_decompress(&d->cinfo);
+ if (b == FALSE)
+ goto needinput;
+
+ /* fall through */
+ d->stage = 2;
+
+ case 2:
+ stride = d->cinfo.output_width * d->cinfo.output_components;
+
+ while (d->cinfo.output_scanline < d->cinfo.output_height)
+ {
+ if (out->wp + stride > out->ep)
+ goto needoutput;
+
+ scanlines[0] = out->wp;
+
+ i = jpeg_read_scanlines(&d->cinfo, scanlines, 1);
+
+ if (i == 0)
+ goto needinput;
+
+ out->wp += stride;
+ }
+
+ /* fall through */
+ d->stage = 3;
+
+ case 3:
+ b = jpeg_finish_decompress(&d->cinfo);
+ if (b == FALSE)
+ goto needinput;
+ d->stage = 4;
+ out->eof = 1;
+ in->rp = in->wp - d->src.super.bytes_in_buffer;
+ return fz_iodone;
+ }
+
+needinput:
+ in->rp = in->wp - d->src.super.bytes_in_buffer;
+ return fz_ioneedin;
+
+needoutput:
+ in->rp = in->wp - d->src.super.bytes_in_buffer;
+ return fz_ioneedout;
+}
+
diff --git a/filter/dcte.c b/filter/dcte.c
new file mode 100644
index 00000000..52933789
--- /dev/null
+++ b/filter/dcte.c
@@ -0,0 +1,254 @@
+#include <fitz.h>
+
+#include "dctc.h"
+
+typedef struct fz_dcte_s fz_dcte;
+
+struct mydstmgr
+{
+ struct jpeg_destination_mgr super;
+ fz_buffer *buf;
+};
+
+struct fz_dcte_s
+{
+ fz_filter super;
+
+ struct jpeg_compress_struct cinfo;
+ struct mydstmgr dst;
+ struct myerrmgr err;
+ int stage;
+
+ int columns;
+ int rows;
+ int colors;
+};
+
+static void myinitdest(j_compress_ptr cinfo) { /* empty */ }
+static boolean myemptybuf(j_compress_ptr cinfo) { return FALSE; }
+static void mytermdest(j_compress_ptr cinfo) { /* empty */ }
+
+fz_error *
+fz_newdcte(fz_filter **fp, fz_obj *params)
+{
+ fz_error *err;
+ fz_obj *obj;
+ int i;
+
+ FZ_NEWFILTER(fz_dcte, e, dcte);
+
+ e->stage = 0;
+
+ obj = fz_dictgets(params, "Columns");
+ if (!obj) { fz_free(e); return fz_throw("ioerror in dcte: missing Columns parameter"); }
+ e->columns = fz_toint(obj);
+
+ obj = fz_dictgets(params, "Rows");
+ if (!obj) { fz_free(e); return fz_throw("ioerror in dcte: missing Rows parameter"); }
+ e->rows = fz_toint(obj);
+
+ obj = fz_dictgets(params, "Colors");
+ if (!obj) { fz_free(e); return fz_throw("ioerror in dcte: missing Colors parameter"); }
+ e->colors = fz_toint(obj);
+
+ /* setup error callback first thing */
+ myiniterr(&e->err);
+ e->cinfo.err = (struct jpeg_error_mgr*) &e->err;
+
+ if (setjmp(e->err.jb)) {
+ err = fz_throw("ioerror in dcte: %s", e->err.msg);
+ fz_free(e);
+ return err;
+ }
+
+ jpeg_create_compress(&e->cinfo);
+
+ /* prepare destination manager */
+ e->cinfo.dest = (struct jpeg_destination_mgr *) &e->dst;
+ e->dst.super.init_destination = myinitdest;
+ e->dst.super.empty_output_buffer = myemptybuf;
+ e->dst.super.term_destination = mytermdest;
+
+ e->dst.super.next_output_byte = nil;
+ e->dst.super.free_in_buffer = 0;
+
+ /* prepare required encoding options */
+ e->cinfo.image_width = e->columns;
+ e->cinfo.image_height = e->rows;
+ e->cinfo.input_components = e->colors;
+
+ switch (e->colors) {
+ case 1: e->cinfo.in_color_space = JCS_GRAYSCALE; break;
+ case 3: e->cinfo.in_color_space = JCS_RGB; break;
+ case 4: e->cinfo.in_color_space = JCS_CMYK; break;
+ default: e->cinfo.in_color_space = JCS_UNKNOWN; break;
+ }
+
+ jpeg_set_defaults(&e->cinfo);
+
+ /* FIXME check this */
+ obj = fz_dictgets(params, "ColorTransform");
+ if (obj) {
+ int colortransform = fz_toint(obj);
+ if (e->colors == 3 && !colortransform)
+ jpeg_set_colorspace(&e->cinfo, JCS_RGB);
+ if (e->colors == 4 && colortransform)
+ jpeg_set_colorspace(&e->cinfo, JCS_YCCK);
+ if (e->colors == 4 && !colortransform)
+ jpeg_set_colorspace(&e->cinfo, JCS_CMYK);
+ }
+
+ obj = fz_dictgets(params, "HSamples");
+ if (obj && fz_isarray(obj)) {
+ fz_obj *o;
+ for (i = 0; i < e->colors; i++) {
+ o = fz_arrayget(obj, i);
+ e->cinfo.comp_info[i].h_samp_factor = fz_toint(o);
+ }
+ }
+
+ obj = fz_dictgets(params, "VSamples");
+ if (obj && fz_isarray(obj)) {
+ fz_obj *o;
+ for (i = 0; i < e->colors; i++) {
+ o = fz_arrayget(obj, i);
+ e->cinfo.comp_info[i].v_samp_factor = fz_toint(o);
+ }
+ }
+
+ /* TODO: quant-tables and huffman-tables */
+
+ return nil;
+}
+
+void
+fz_freedcte(fz_filter *filter)
+{
+ fz_dcte *e = (fz_dcte*)filter;
+
+ if (setjmp(e->err.jb)) {
+ fprintf(stderr, "ioerror in dcte: jpeg_destroy_compress: %s", e->err.msg);
+ return;
+ }
+
+ jpeg_destroy_compress(&e->cinfo);
+
+ fz_free(e);
+}
+
+/* Adobe says zigzag order. IJG > v6a says natural order. */
+#if JPEG_LIB_VERSION >= 61
+#define unzigzag(x) unzigzagorder[x]
+/* zigzag array position of n'th element of natural array order */
+static const unsigned char unzigzagorder[] =
+{
+ 0, 1, 5, 6, 14, 15, 27, 28,
+ 2, 4, 7, 13, 16, 26, 29, 42,
+ 3, 8, 12, 17, 25, 30, 41, 43,
+ 9, 11, 18, 24, 31, 40, 44, 53,
+ 10, 19, 23, 32, 39, 45, 52, 54,
+ 20, 22, 33, 38, 46, 51, 55, 60,
+ 21, 34, 37, 47, 50, 56, 59, 61,
+ 35, 36, 48, 49, 57, 58, 62, 63
+};
+#else
+#define unzigzag(x) (x)
+#endif
+
+fz_error *
+fz_setquanttables(fz_dcte *e, unsigned int **qtables, int qfactor)
+{
+ int i, j;
+ unsigned int table[64];
+
+ if (setjmp(e->err.jb)) {
+ return fz_throw("ioerror in dcte: %s", e->err.msg);
+ }
+
+ /* TODO: check for duplicate tables */
+
+ for (i = 0; i < e->colors; i++) {
+ for (j = 0; j < 64; j++) {
+ table[j] = unzigzag(qtables[i][j]);
+ }
+ jpeg_add_quant_table(&e->cinfo, i, table, qfactor, TRUE);
+ e->cinfo.comp_info[i].quant_tbl_no = i;
+ }
+
+ return nil;
+}
+
+fz_error *
+fz_processdcte(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_dcte *e = (fz_dcte*)filter;
+ JSAMPROW scanline[1];
+ int stride;
+ int i;
+
+ e->dst.buf = out;
+ e->dst.super.free_in_buffer = out->ep - out->wp;
+ e->dst.super.next_output_byte = out->wp;
+
+ if (setjmp(e->err.jb)) {
+ return fz_throw("ioerror in dcte: %s", e->err.msg);
+ }
+
+ switch (e->stage)
+ {
+ case 0:
+ /* must have enough space for markers, typically 600 bytes or so */
+ if (out->wp + 1024 > out->ep)
+ goto needoutput;
+
+ jpeg_start_compress(&e->cinfo, TRUE);
+
+ /* TODO: write Adobe ColorTransform marker */
+
+ /* fall through */
+ e->stage = 1;
+
+ case 1:
+ stride = e->columns * e->colors;
+
+ while (e->cinfo.next_scanline < e->cinfo.image_height)
+ {
+ if (in->rp + stride > in->wp)
+ goto needinput;
+
+ scanline[0] = in->rp;
+
+ i = jpeg_write_scanlines(&e->cinfo, scanline, 1);
+
+ if (i == 0)
+ goto needoutput;
+
+ in->rp += stride;
+ }
+
+ /* fall through */
+ e->stage = 2;
+
+ case 2:
+ /* must have enough space for end markers */
+ if (out->wp + 100 > out->ep)
+ goto needoutput;
+
+ /* finish compress cannot suspend! */
+ jpeg_finish_compress(&e->cinfo);
+
+ e->stage = 3;
+ out->eof = 1;
+ out->wp = out->ep - e->dst.super.free_in_buffer;
+ return fz_iodone;
+ }
+
+needinput:
+ out->wp = out->ep - e->dst.super.free_in_buffer;
+ return fz_ioneedin;
+
+needoutput:
+ out->wp = out->ep - e->dst.super.free_in_buffer;
+ return fz_ioneedout;
+}
+
diff --git a/filter/faxc.h b/filter/faxc.h
new file mode 100644
index 00000000..caf29df0
--- /dev/null
+++ b/filter/faxc.h
@@ -0,0 +1,116 @@
+/* common bit magic */
+
+static inline void
+printbits(FILE *f, int code, int nbits)
+{
+ int n, b;
+ for (n = nbits - 1; n >= 0; n--) {
+ b = (code >> n) & 1;
+ fprintf(f, "%c", b ? '1' : '0');
+ }
+}
+
+static inline int
+getbit(const unsigned char *buf, int x)
+{
+ return ( buf[x >> 3] >> ( 7 - (x & 7) ) ) & 1;
+}
+
+static inline void
+printline(FILE *f, unsigned char *line, int w)
+{
+ int i;
+ for (i = 0; i < w; i++)
+ fprintf(f, "%c", getbit(line, i) ? '#' : '.');
+ fprintf(f, "\n");
+}
+
+static inline int
+getrun(const unsigned char *line, int x, int w, int c)
+{
+ int z;
+ int b;
+
+ if (x < 0)
+ x = 0;
+
+ z = x;
+ while (z < w) {
+ b = getbit(line, z);
+ if (c != b)
+ break;
+ z ++;
+ }
+
+ return z - x;
+}
+
+static inline int
+findchanging(const unsigned char *line, int x, int w)
+{
+ int a, b;
+
+ if (line == 0)
+ return w;
+
+ if (x == -1) {
+ a = 0;
+ x = 0;
+ }
+ else {
+ a = getbit(line, x);
+ x++;
+ }
+
+ while (x < w) {
+ b = getbit(line, x);
+ if (a != b)
+ break;
+ x++;
+ }
+
+ return x;
+}
+
+static inline int
+findchangingcolor(const unsigned char *line, int x, int w, int color)
+{
+ if (line == 0)
+ return w;
+
+ x = findchanging(line, x, w);
+
+ if (x < w && getbit(line, x) != color)
+ x = findchanging(line, x, w);
+
+ return x;
+}
+
+static const unsigned char lm[8] =
+ { 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01 };
+
+static const unsigned char rm[8] =
+ { 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE };
+
+static inline void
+setbits(unsigned char *line, int x0, int x1)
+{
+ int a0, a1, b0, b1, a;
+
+ a0 = x0 >> 3;
+ a1 = x1 >> 3;
+
+ b0 = x0 & 7;
+ b1 = x1 & 7;
+
+ if (a0 == a1) {
+ line[a0] |= lm[b0] & rm[b1];
+ }
+ else {
+ line[a0] |= lm[b0];
+ for (a = a0 + 1; a < a1; a++)
+ line[a] = 0xFF;
+ line[a1] |= rm[b1];
+ }
+}
+
diff --git a/filter/faxd.c b/filter/faxd.c
new file mode 100644
index 00000000..1b86c17b
--- /dev/null
+++ b/filter/faxd.c
@@ -0,0 +1,463 @@
+#include <fitz.h>
+
+#include "faxd.h"
+#include "faxc.h"
+
+enum
+{
+ SNORMAL, /* neutral state, waiting for any code */
+ SMAKEUP, /* got a 1d makeup code, waiting for terminating code */
+ SEOL, /* at eol, needs output buffer space */
+ SH1, SH2 /* in H part 1 and 2 (both makeup and terminating codes) */
+};
+
+#define DEBUG 1
+
+#ifdef noDEBUG
+#define DPRINT(...) fprintf(stderr, __VA_ARGS__)
+#else
+#define DPRINT(...)
+#endif
+
+/* TODO: uncompressed */
+
+typedef struct fz_faxd_s fz_faxd;
+
+struct fz_faxd_s
+{
+ fz_filter super;
+
+ int k;
+ int endofline;
+ int encodedbytealign;
+ int columns;
+ int rows;
+ int endofblock;
+ int blackis1;
+
+ int stride;
+ int ridx;
+
+ int bidx;
+ unsigned int word;
+
+ int stage, a, c, dim, eolc;
+ unsigned char *ref;
+ unsigned char *dst;
+};
+
+fz_error *
+fz_newfaxd(fz_filter **fp, fz_obj *params)
+{
+ fz_obj *obj;
+
+ FZ_NEWFILTER(fz_faxd, fax, faxd);
+
+ fax->ref = nil;
+ fax->dst = nil;
+
+ fax->k = 0;
+ fax->endofline = 0;
+ fax->encodedbytealign = 0;
+ fax->columns = 1728;
+ fax->rows = 0;
+ fax->endofblock = 1;
+ fax->blackis1 = 0;
+
+ obj = fz_dictgets(params, "K");
+ if (obj) fax->k = fz_toint(obj);
+
+ obj = fz_dictgets(params, "EndOfLine");
+ if (obj) fax->endofline = fz_tobool(obj);
+
+ obj = fz_dictgets(params, "EncodedByteAlign");
+ if (obj) fax->encodedbytealign = fz_tobool(obj);
+
+ obj = fz_dictgets(params, "Columns");
+ if (obj) fax->columns = fz_toint(obj);
+
+ obj = fz_dictgets(params, "Rows");
+ if (obj) fax->rows = fz_toint(obj);
+
+ obj = fz_dictgets(params, "EndOfBlock");
+ if (obj) fax->endofblock = fz_tobool(obj);
+
+ obj = fz_dictgets(params, "BlackIs1");
+ if (obj) fax->blackis1 = fz_tobool(obj);
+
+ fax->stride = ((fax->columns - 1) >> 3) + 1;
+ fax->ridx = 0;
+ fax->bidx = 32;
+ fax->word = 0;
+
+ fax->stage = SNORMAL;
+ fax->a = -1;
+ fax->c = 0;
+ fax->dim = fax->k < 0 ? 2 : 1;
+ fax->eolc = 0;
+
+ fax->ref = fz_malloc(fax->stride);
+ if (!fax->ref) { fz_free(fax); return fz_outofmem; }
+
+ fax->dst = fz_malloc(fax->stride);
+ if (!fax->dst) { fz_free(fax); fz_free(fax->ref); return fz_outofmem; }
+
+ memset(fax->ref, 0, fax->stride);
+ memset(fax->dst, 0, fax->stride);
+
+ DPRINT("FAXD k=%d eol=%d eba=%d cols=%d rows=%d eob=%d black1=%d stride=%d\n",
+ fax->k, fax->endofline, fax->encodedbytealign,
+ fax->columns, fax->rows, fax->endofblock, fax->blackis1,
+ fax->stride);
+
+ return nil;
+}
+
+void
+fz_freefaxd(fz_filter *p)
+{
+ fz_faxd *fax = (fz_faxd*) p;
+ fz_free(fax->ref);
+ fz_free(fax->dst);
+ fz_free(fax);
+}
+
+static inline void eatbits(fz_faxd *fax, int nbits)
+{
+ fax->word <<= nbits;
+ fax->bidx += nbits;
+}
+
+static inline fz_error * fillbits(fz_faxd *fax, fz_buffer *in)
+{
+ while (fax->bidx >= 8)
+ {
+ if (in->rp + 1 > in->wp)
+ return fz_ioneedin;
+ fax->bidx -= 8;
+ fax->word |= *in->rp << fax->bidx;
+ in->rp ++;
+ }
+ return nil;
+}
+
+static int
+getcode(fz_faxd *fax, const cfd_node *table, int initialbits)
+{
+ unsigned int word = fax->word;
+ int tidx = word >> (32 - initialbits);
+ int val = table[tidx].val;
+ int nbits = table[tidx].nbits;
+
+ if (nbits > initialbits)
+ {
+ int mask = (1 << (32 - initialbits)) - 1;
+ tidx = val + ((word & mask) >> (32 - nbits));
+ val = table[tidx].val;
+ nbits = initialbits + table[tidx].nbits;
+ }
+
+ eatbits(fax, nbits);
+
+ return val;
+}
+
+/* decode one 1d code */
+static fz_error *
+dec1d(fz_faxd *fax)
+{
+ int code;
+
+ if (fax->a == -1)
+ fax->a = 0;
+
+ if (fax->c)
+ code = getcode(fax, cf_black_decode, cfd_black_initial_bits);
+ else
+ code = getcode(fax, cf_white_decode, cfd_white_initial_bits);
+
+ DPRINT("%c %d\n", fax->c?'b':'w', code);
+
+ if (code == UNCOMPRESSED)
+ return fz_throw("ioerror: uncompressed data in faxd");
+
+ if (code < 0)
+ return fz_throw("ioerror: invalid 1d code in faxd");
+
+ if (fax->a + code > fax->columns)
+ return fz_throw("ioerror: invalid 1d data in faxd");
+
+ if (fax->c)
+ setbits(fax->dst, fax->a, fax->a + code);
+
+ fax->a += code;
+
+ if (code < 64)
+ {
+ fax->c = !fax->c;
+ fax->stage = SNORMAL;
+ }
+ else
+ fax->stage = SMAKEUP;
+
+ return nil;
+}
+
+/* decode one 2d code */
+static fz_error *
+dec2d(fz_faxd *fax)
+{
+ int code, b1, b2;
+
+ if (fax->stage == SH1 || fax->stage == SH2)
+ {
+ if (fax->a == -1)
+ fax->a = 0;
+
+ if (fax->c)
+ code = getcode(fax, cf_black_decode, cfd_black_initial_bits);
+ else
+ code = getcode(fax, cf_white_decode, cfd_white_initial_bits);
+
+ DPRINT("%c %d\n", fax->c ? 'b' : 'w', code);
+
+ if (code == UNCOMPRESSED)
+ return fz_throw("ioerror: uncompressed data in faxd");
+
+ if (code < 0)
+ return fz_throw("ioerror: invalid 2d code in faxd");
+
+ if (fax->a + code > fax->columns)
+ return fz_throw("ioerror: invalid 2d data in faxd");
+
+ if (fax->c)
+ setbits(fax->dst, fax->a, fax->a + code);
+
+ fax->a += code;
+
+ if (code < 64)
+ {
+ fax->c = !fax->c;
+ if (fax->stage == SH1)
+ fax->stage = SH2;
+ else if (fax->stage == SH2)
+ fax->stage = SNORMAL;
+ }
+
+ return nil;
+ }
+
+ code = getcode(fax, cf_2d_decode, cfd_2d_initial_bits);
+
+ switch (code)
+ {
+ case H:
+ fax->stage = SH1;
+ DPRINT("H\n");
+ break;
+
+ case P:
+ b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c);
+ b2 = findchanging(fax->ref, b1, fax->columns);
+ DPRINT("P\n");
+ if (fax->c) setbits(fax->dst, fax->a, b2);
+ fax->a = b2;
+ break;
+
+ case V0:
+ b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c);
+ DPRINT("V0\n");
+ if (fax->c) setbits(fax->dst, fax->a, b1);
+ fax->a = b1;
+ fax->c = !fax->c;
+ break;
+
+ case VR1:
+ b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c);
+ DPRINT("VR1\n");
+ if (fax->c) setbits(fax->dst, fax->a, b1 + 1);
+ fax->a = b1 + 1;
+ fax->c = !fax->c;
+ break;
+
+ case VR2:
+ b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c);
+ DPRINT("VR2\n");
+ if (fax->c) setbits(fax->dst, fax->a, b1 + 2);
+ fax->a = b1 + 2;
+ fax->c = !fax->c;
+ break;
+
+ case VR3:
+ b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c);
+ DPRINT("VR3\n");
+ if (fax->c) setbits(fax->dst, fax->a, b1 + 3);
+ fax->a = b1 + 3;
+ fax->c = !fax->c;
+ break;
+
+ case VL1:
+ b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c);
+ DPRINT("VL1\n");
+ if (fax->c) setbits(fax->dst, fax->a, b1 - 1);
+ fax->a = b1 - 1;
+ fax->c = !fax->c;
+ break;
+
+ case VL2:
+ b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c);
+ DPRINT("VL2\n");
+ if (fax->c) setbits(fax->dst, fax->a, b1 - 2);
+ fax->a = b1 - 2;
+ fax->c = !fax->c;
+ break;
+
+ case VL3:
+ b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c);
+ DPRINT("VL3\n");
+ if (fax->c) setbits(fax->dst, fax->a, b1 - 3);
+ fax->a = b1 - 3;
+ fax->c = !fax->c;
+ break;
+
+ case UNCOMPRESSED:
+ return fz_throw("ioerror: uncompressed data in faxd");
+
+ case ERROR:
+ return fz_throw("ioerror: invalid 2d code in faxd");
+
+ default:
+ return fz_throw("ioerror: invalid 2d code in faxd (%d)", code);
+ }
+
+ return 0;
+}
+
+fz_error *
+fz_processfaxd(fz_filter *f, fz_buffer *in, fz_buffer *out)
+{
+ fz_faxd *fax = (fz_faxd*)f;
+ fz_error * err;
+ int i;
+
+ if (fax->stage == SEOL)
+ goto eol;
+
+loop:
+
+ if (fillbits(fax, in))
+ {
+ if (in->eof) {
+ if (fax->bidx > 31) {
+ if (fax->a > 0)
+ goto eol;
+ goto rtc;
+ }
+ }
+ else {
+ return fz_ioneedin;
+ }
+ }
+
+ if ((fax->word >> (32 - 12)) == 0)
+ {
+ eatbits(fax, 1);
+ goto loop;
+ }
+
+ if ((fax->word >> (32 - 12)) == 1)
+ {
+ DPRINT("EOL\n");
+ eatbits(fax, 12);
+ fax->eolc ++;
+
+ if (fax->k > 0) {
+ if ((fax->word >> (32 - 1)) == 1)
+ fax->dim = 1;
+ else
+ fax->dim = 2;
+ eatbits(fax, 1);
+ DPRINT("DIM %d\n", fax->dim);
+ }
+ }
+ else if (fax->dim == 1)
+ {
+ fax->eolc = 0;
+ err = dec1d(fax);
+ if (err) return err;
+
+ }
+ else if (fax->dim == 2)
+ {
+ fax->eolc = 0;
+ err = dec2d(fax);
+ if (err) return err;
+ }
+
+ /* no eol check after makeup codes nor in the middle of an H code */
+ if (fax->stage == SMAKEUP || fax->stage == SH1 || fax->stage == SH2)
+ goto loop;
+
+ /* check for eol conditions */
+ if (fax->eolc || fax->a >= fax->columns)
+ {
+ if (fax->a > 0)
+ goto eol;
+ if (fax->eolc == (fax->k < 0 ? 2 : 6))
+ goto rtc;
+ }
+
+ goto loop;
+
+eol:
+ fax->stage = SEOL;
+ if (out->wp + fax->stride > out->ep)
+ return fz_ioneedout;
+
+ if (fax->blackis1)
+ memcpy(out->wp, fax->dst, fax->stride);
+ else
+ for (i = 0; i < fax->stride; i++)
+ out->wp[i] = ~fax->dst[i];
+
+ memcpy(fax->ref, fax->dst, fax->stride);
+ memset(fax->dst, 0, fax->stride);
+ out->wp += fax->stride;
+
+ fax->stage = SNORMAL;
+ fax->c = 0;
+ fax->a = -1;
+ fax->ridx ++;
+
+ if (!fax->endofblock && fax->rows) {
+ if (fax->ridx >= fax->rows)
+ goto rtc;
+ }
+
+ /* we have not read dim from eol, make a guess */
+ if (fax->k > 0 && !fax->eolc)
+ {
+ if (fax->ridx % fax->k == 0)
+ fax->dim = 1;
+ else
+ fax->dim = 2;
+ }
+
+ DPRINT("%dd scanline %d\n", fax->dim, fax->ridx + 1);
+
+ /* if endofline & encodedbytealign, EOLs are *not* optional */
+ if (fax->encodedbytealign) {
+ if (fax->endofline)
+ eatbits(fax, (12 - fax->bidx) & 7);
+ else
+ eatbits(fax, (8 - fax->bidx) & 7);
+ }
+
+ goto loop;
+
+rtc:
+ DPRINT("RTC\n");
+ out->eof = 1;
+ return fz_iodone;
+}
+
diff --git a/filter/faxd.h b/filter/faxd.h
new file mode 100644
index 00000000..886514f5
--- /dev/null
+++ b/filter/faxd.h
@@ -0,0 +1,61 @@
+/* Fax G3/G4 tables */
+
+/*
+<raph> the first 2^(initialbits) entries map bit patterns to decodes
+<raph> let's say initial_bits is 8 for the sake of example
+<raph> and that the code is 1001
+<raph> that means that entries 0x90 .. 0x9f have the entry { val, 4 }
+<raph> because those are all the bytes that start with the code
+<raph> and the 4 is the length of the code
+... if (n_bits > initial_bits) ...
+<raph> anyway, in that case, it basically points to a mini table
+<raph> the n_bits is the maximum length of all codes beginning with that byte
+<raph> so 2^(n_bits - initial_bits) is the size of the mini-table
+<raph> peter came up with this, and it makes sense
+*/
+
+typedef struct cfd_node_s cfd_node;
+
+struct cfd_node_s
+{
+ short val;
+ short nbits;
+};
+
+enum
+{
+ cfd_white_initial_bits = 8,
+ cfd_black_initial_bits = 7,
+ cfd_2d_initial_bits = 7,
+ cfd_uncompressed_initial_bits = 6 /* must be 6 */
+};
+
+/* non-run codes in tables */
+enum
+{
+ ERROR = -1,
+ ZEROS = -2, /* EOL follows, possibly with more padding first */
+ UNCOMPRESSED = -3,
+};
+
+/* semantic codes for cf_2d_decode */
+enum
+{
+ P = -4,
+ H = -5,
+ VR3 = 0,
+ VR2 = 1,
+ VR1 = 2,
+ V0 = 3,
+ VL1 = 4,
+ VL2 = 5,
+ VL3 = 6,
+};
+
+/* Decoding tables */
+
+extern const cfd_node cf_white_decode[];
+extern const cfd_node cf_black_decode[];
+extern const cfd_node cf_2d_decode[];
+extern const cfd_node cf_uncompressed_decode[];
+
diff --git a/filter/faxdtab.c b/filter/faxdtab.c
new file mode 100644
index 00000000..8e387d03
--- /dev/null
+++ b/filter/faxdtab.c
@@ -0,0 +1,931 @@
+/* Tables for CCITTFaxDecode filter. */
+
+/* This file was generated automatically. It is governed by the same terms */
+/* as the files scfetab.c and scfdgen.c from which it was derived. */
+/* Consult those files for the licensing terms and conditions. */
+
+#include "faxd.h"
+
+/* White decoding table. */
+const cfd_node cf_white_decode[] = {
+ { 256, 12 },
+ { 272, 12 },
+ { 29, 8 },
+ { 30, 8 },
+ { 45, 8 },
+ { 46, 8 },
+ { 22, 7 },
+ { 22, 7 },
+ { 23, 7 },
+ { 23, 7 },
+ { 47, 8 },
+ { 48, 8 },
+ { 13, 6 },
+ { 13, 6 },
+ { 13, 6 },
+ { 13, 6 },
+ { 20, 7 },
+ { 20, 7 },
+ { 33, 8 },
+ { 34, 8 },
+ { 35, 8 },
+ { 36, 8 },
+ { 37, 8 },
+ { 38, 8 },
+ { 19, 7 },
+ { 19, 7 },
+ { 31, 8 },
+ { 32, 8 },
+ { 1, 6 },
+ { 1, 6 },
+ { 1, 6 },
+ { 1, 6 },
+ { 12, 6 },
+ { 12, 6 },
+ { 12, 6 },
+ { 12, 6 },
+ { 53, 8 },
+ { 54, 8 },
+ { 26, 7 },
+ { 26, 7 },
+ { 39, 8 },
+ { 40, 8 },
+ { 41, 8 },
+ { 42, 8 },
+ { 43, 8 },
+ { 44, 8 },
+ { 21, 7 },
+ { 21, 7 },
+ { 28, 7 },
+ { 28, 7 },
+ { 61, 8 },
+ { 62, 8 },
+ { 63, 8 },
+ { 0, 8 },
+ { 320, 8 },
+ { 384, 8 },
+ { 10, 5 },
+ { 10, 5 },
+ { 10, 5 },
+ { 10, 5 },
+ { 10, 5 },
+ { 10, 5 },
+ { 10, 5 },
+ { 10, 5 },
+ { 11, 5 },
+ { 11, 5 },
+ { 11, 5 },
+ { 11, 5 },
+ { 11, 5 },
+ { 11, 5 },
+ { 11, 5 },
+ { 11, 5 },
+ { 27, 7 },
+ { 27, 7 },
+ { 59, 8 },
+ { 60, 8 },
+ { 288, 9 },
+ { 290, 9 },
+ { 18, 7 },
+ { 18, 7 },
+ { 24, 7 },
+ { 24, 7 },
+ { 49, 8 },
+ { 50, 8 },
+ { 51, 8 },
+ { 52, 8 },
+ { 25, 7 },
+ { 25, 7 },
+ { 55, 8 },
+ { 56, 8 },
+ { 57, 8 },
+ { 58, 8 },
+ { 192, 6 },
+ { 192, 6 },
+ { 192, 6 },
+ { 192, 6 },
+ { 1664, 6 },
+ { 1664, 6 },
+ { 1664, 6 },
+ { 1664, 6 },
+ { 448, 8 },
+ { 512, 8 },
+ { 292, 9 },
+ { 640, 8 },
+ { 576, 8 },
+ { 294, 9 },
+ { 296, 9 },
+ { 298, 9 },
+ { 300, 9 },
+ { 302, 9 },
+ { 256, 7 },
+ { 256, 7 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 2, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 128, 5 },
+ { 128, 5 },
+ { 128, 5 },
+ { 128, 5 },
+ { 128, 5 },
+ { 128, 5 },
+ { 128, 5 },
+ { 128, 5 },
+ { 8, 5 },
+ { 8, 5 },
+ { 8, 5 },
+ { 8, 5 },
+ { 8, 5 },
+ { 8, 5 },
+ { 8, 5 },
+ { 8, 5 },
+ { 9, 5 },
+ { 9, 5 },
+ { 9, 5 },
+ { 9, 5 },
+ { 9, 5 },
+ { 9, 5 },
+ { 9, 5 },
+ { 9, 5 },
+ { 16, 6 },
+ { 16, 6 },
+ { 16, 6 },
+ { 16, 6 },
+ { 17, 6 },
+ { 17, 6 },
+ { 17, 6 },
+ { 17, 6 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 14, 6 },
+ { 14, 6 },
+ { 14, 6 },
+ { 14, 6 },
+ { 15, 6 },
+ { 15, 6 },
+ { 15, 6 },
+ { 15, 6 },
+ { 64, 5 },
+ { 64, 5 },
+ { 64, 5 },
+ { 64, 5 },
+ { 64, 5 },
+ { 64, 5 },
+ { 64, 5 },
+ { 64, 5 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { 7, 4 },
+ { -2, 3 },
+ { -2, 3 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -3, 4 },
+ { 1792, 3 },
+ { 1792, 3 },
+ { 1984, 4 },
+ { 2048, 4 },
+ { 2112, 4 },
+ { 2176, 4 },
+ { 2240, 4 },
+ { 2304, 4 },
+ { 1856, 3 },
+ { 1856, 3 },
+ { 1920, 3 },
+ { 1920, 3 },
+ { 2368, 4 },
+ { 2432, 4 },
+ { 2496, 4 },
+ { 2560, 4 },
+ { 1472, 1 },
+ { 1536, 1 },
+ { 1600, 1 },
+ { 1728, 1 },
+ { 704, 1 },
+ { 768, 1 },
+ { 832, 1 },
+ { 896, 1 },
+ { 960, 1 },
+ { 1024, 1 },
+ { 1088, 1 },
+ { 1152, 1 },
+ { 1216, 1 },
+ { 1280, 1 },
+ { 1344, 1 },
+ { 1408, 1 }
+};
+
+/* Black decoding table. */
+const cfd_node cf_black_decode[] = {
+ { 128, 12 },
+ { 160, 13 },
+ { 224, 12 },
+ { 256, 12 },
+ { 10, 7 },
+ { 11, 7 },
+ { 288, 12 },
+ { 12, 7 },
+ { 9, 6 },
+ { 9, 6 },
+ { 8, 6 },
+ { 8, 6 },
+ { 7, 5 },
+ { 7, 5 },
+ { 7, 5 },
+ { 7, 5 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 6, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 1, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 3, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { 2, 2 },
+ { -2, 4 },
+ { -2, 4 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -3, 5 },
+ { 1792, 4 },
+ { 1792, 4 },
+ { 1984, 5 },
+ { 2048, 5 },
+ { 2112, 5 },
+ { 2176, 5 },
+ { 2240, 5 },
+ { 2304, 5 },
+ { 1856, 4 },
+ { 1856, 4 },
+ { 1920, 4 },
+ { 1920, 4 },
+ { 2368, 5 },
+ { 2432, 5 },
+ { 2496, 5 },
+ { 2560, 5 },
+ { 18, 3 },
+ { 18, 3 },
+ { 18, 3 },
+ { 18, 3 },
+ { 18, 3 },
+ { 18, 3 },
+ { 18, 3 },
+ { 18, 3 },
+ { 52, 5 },
+ { 52, 5 },
+ { 640, 6 },
+ { 704, 6 },
+ { 768, 6 },
+ { 832, 6 },
+ { 55, 5 },
+ { 55, 5 },
+ { 56, 5 },
+ { 56, 5 },
+ { 1280, 6 },
+ { 1344, 6 },
+ { 1408, 6 },
+ { 1472, 6 },
+ { 59, 5 },
+ { 59, 5 },
+ { 60, 5 },
+ { 60, 5 },
+ { 1536, 6 },
+ { 1600, 6 },
+ { 24, 4 },
+ { 24, 4 },
+ { 24, 4 },
+ { 24, 4 },
+ { 25, 4 },
+ { 25, 4 },
+ { 25, 4 },
+ { 25, 4 },
+ { 1664, 6 },
+ { 1728, 6 },
+ { 320, 5 },
+ { 320, 5 },
+ { 384, 5 },
+ { 384, 5 },
+ { 448, 5 },
+ { 448, 5 },
+ { 512, 6 },
+ { 576, 6 },
+ { 53, 5 },
+ { 53, 5 },
+ { 54, 5 },
+ { 54, 5 },
+ { 896, 6 },
+ { 960, 6 },
+ { 1024, 6 },
+ { 1088, 6 },
+ { 1152, 6 },
+ { 1216, 6 },
+ { 64, 3 },
+ { 64, 3 },
+ { 64, 3 },
+ { 64, 3 },
+ { 64, 3 },
+ { 64, 3 },
+ { 64, 3 },
+ { 64, 3 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 13, 1 },
+ { 23, 4 },
+ { 23, 4 },
+ { 50, 5 },
+ { 51, 5 },
+ { 44, 5 },
+ { 45, 5 },
+ { 46, 5 },
+ { 47, 5 },
+ { 57, 5 },
+ { 58, 5 },
+ { 61, 5 },
+ { 256, 5 },
+ { 16, 3 },
+ { 16, 3 },
+ { 16, 3 },
+ { 16, 3 },
+ { 17, 3 },
+ { 17, 3 },
+ { 17, 3 },
+ { 17, 3 },
+ { 48, 5 },
+ { 49, 5 },
+ { 62, 5 },
+ { 63, 5 },
+ { 30, 5 },
+ { 31, 5 },
+ { 32, 5 },
+ { 33, 5 },
+ { 40, 5 },
+ { 41, 5 },
+ { 22, 4 },
+ { 22, 4 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 14, 1 },
+ { 15, 2 },
+ { 15, 2 },
+ { 15, 2 },
+ { 15, 2 },
+ { 15, 2 },
+ { 15, 2 },
+ { 15, 2 },
+ { 15, 2 },
+ { 128, 5 },
+ { 192, 5 },
+ { 26, 5 },
+ { 27, 5 },
+ { 28, 5 },
+ { 29, 5 },
+ { 19, 4 },
+ { 19, 4 },
+ { 20, 4 },
+ { 20, 4 },
+ { 34, 5 },
+ { 35, 5 },
+ { 36, 5 },
+ { 37, 5 },
+ { 38, 5 },
+ { 39, 5 },
+ { 21, 4 },
+ { 21, 4 },
+ { 42, 5 },
+ { 43, 5 },
+ { 0, 3 },
+ { 0, 3 },
+ { 0, 3 },
+ { 0, 3 }
+};
+
+/* 2-D decoding table. */
+const cfd_node cf_2d_decode[] = {
+ { 128, 11 },
+ { 144, 10 },
+ { 6, 7 },
+ { 0, 7 },
+ { 5, 6 },
+ { 5, 6 },
+ { 1, 6 },
+ { 1, 6 },
+ { -4, 4 },
+ { -4, 4 },
+ { -4, 4 },
+ { -4, 4 },
+ { -4, 4 },
+ { -4, 4 },
+ { -4, 4 },
+ { -4, 4 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { -5, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 4, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { 3, 1 },
+ { -2, 4 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -1, 0 },
+ { -3, 3 }
+};
+
+/* Uncompresssed decoding table. */
+const cfd_node cf_uncompressed_decode[] = {
+ { 64, 12 },
+ { 5, 6 },
+ { 4, 5 },
+ { 4, 5 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 3, 4 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { -1, 0 },
+ { -1, 0 },
+ { 8, 6 },
+ { 9, 6 },
+ { 6, 5 },
+ { 6, 5 },
+ { 7, 5 },
+ { 7, 5 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 4, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 5, 4 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ { 3, 3 },
+ { 3, 3 },
+ { 3, 3 },
+ { 3, 3 },
+ { 3, 3 },
+ { 3, 3 },
+ { 3, 3 },
+ { 3, 3 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 0, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 },
+ { 1, 2 }
+};
+
+/* Dummy executable code to pacify compilers. */
+void scfdtab_dummy(void) { }
+
diff --git a/filter/faxe.c b/filter/faxe.c
new file mode 100644
index 00000000..20e776dd
--- /dev/null
+++ b/filter/faxe.c
@@ -0,0 +1,448 @@
+#include <fitz.h>
+
+#include "faxe.h"
+#include "faxc.h"
+
+/* TODO: honor Rows param */
+
+#define noDEBUGBITS 1
+#define noDEBUG 1
+
+#ifdef DEBUG
+#define DPRINT(...) fprintf(stderr, __VA_ARGS__)
+#else
+#define DPRINT(...)
+#endif
+
+typedef struct fz_faxe_s fz_faxe;
+
+struct fz_faxe_s
+{
+ fz_filter super;
+
+ int k;
+ int endofline;
+ int encodedbytealign;
+ int columns;
+ int endofblock;
+ int blackis1;
+
+ int stride;
+ int ridx; /* how many rows in total */
+ int bidx; /* how many bits are already used in out->wp */
+ unsigned char bsave; /* partial byte saved between process() calls */
+
+ int stage;
+ int a0, c; /* mid-line coding state */
+
+ unsigned char *ref;
+ unsigned char *src;
+};
+
+fz_error *
+fz_newfaxe(fz_filter **fp, fz_obj *params)
+{
+ fz_obj *obj;
+
+ FZ_NEWFILTER(fz_faxe, fax, faxe);
+
+ fax->ref = nil;
+ fax->src = nil;
+
+ fax->k = 0;
+ fax->endofline = 0;
+ fax->encodedbytealign = 0;
+ fax->columns = 1728;
+ fax->endofblock = 1;
+ fax->blackis1 = 0;
+
+ obj = fz_dictgets(params, "K");
+ if (obj) fax->k = fz_toint(obj);
+
+ obj = fz_dictgets(params, "EndOfLine");
+ if (obj) fax->endofline = fz_tobool(obj);
+
+ obj = fz_dictgets(params, "EncodedByteAlign");
+ if (obj) fax->encodedbytealign = fz_tobool(obj);
+
+ obj = fz_dictgets(params, "Columns");
+ if (obj) fax->columns = fz_toint(obj);
+
+ obj = fz_dictgets(params, "EndOfBlock");
+ if (obj) fax->endofblock = fz_tobool(obj);
+
+ obj = fz_dictgets(params, "BlackIs1");
+ if (obj) fax->blackis1 = fz_tobool(obj);
+
+ fax->stride = ((fax->columns - 1) >> 3) + 1;
+ fax->bidx = 0;
+ fax->ridx = 0;
+
+ fax->stage = 0;
+ fax->a0 = -1;
+ fax->c = 0;
+
+ fax->ref = fz_malloc(fax->stride);
+ if (!fax->ref) { fz_free(fax); return fz_outofmem; }
+
+ fax->src = fz_malloc(fax->stride);
+ if (!fax->src) { fz_free(fax); fz_free(fax->ref); return fz_outofmem; }
+
+ memset(fax->ref, 0, fax->stride);
+ memset(fax->src, 0, fax->stride);
+
+ return nil;
+}
+
+void
+fz_freefaxe(fz_filter *p)
+{
+ fz_faxe *fax = (fz_faxe*) p;
+ fz_free(fax->src);
+ fz_free(fax->ref);
+ fz_free(fax);
+}
+
+enum { codebytes = 2 };
+
+static inline int
+runbytes(int run)
+{
+ int m = (run / 64) / 40 + 1; /* number of makeup codes */
+ return codebytes * (m + 1); /* bytes for makeup + term codes */
+}
+
+static void
+putbits(fz_faxe *fax, fz_buffer *out, int code, int nbits)
+{
+#ifdef DEBUGBITS
+ fprintf(stderr, "BITS ");
+ printbits(stderr, code, nbits);
+ fprintf(stderr, "\n");
+#endif
+
+ while (nbits > 0)
+ {
+ if (fax->bidx == 0) {
+ *out->wp = 0;
+ }
+
+ /* code does not fit: shift right */
+ if (nbits > (8 - fax->bidx)) {
+ *out->wp |= code >> (nbits - (8 - fax->bidx));
+ nbits = nbits - (8 - fax->bidx);
+ fax->bidx = 0;
+ out->wp ++;
+ }
+
+ /* shift left */
+ else {
+ *out->wp |= code << ((8 - fax->bidx) - nbits);
+ fax->bidx += nbits;
+ if (fax->bidx == 8) {
+ fax->bidx = 0;
+ out->wp ++;
+ }
+ nbits = 0;
+ }
+ }
+}
+
+static inline void
+putcode(fz_faxe *fax, fz_buffer *out, const cfe_code *run)
+{
+ putbits(fax, out, run->code, run->nbits);
+}
+
+static void
+putrun(fz_faxe *fax, fz_buffer *out, int run, int c)
+{
+ int m;
+
+ const cf_runs *codetable = c ? &cf_black_runs : &cf_white_runs;
+
+ if (run > 63) {
+ m = run / 64;
+ while (m > 40) {
+ DPRINT("%c %d\n", c?'b':'w', 40 * 64);
+ putcode(fax, out, &codetable->makeup[40]);
+ m -= 40;
+ }
+ if (m > 0) {
+ DPRINT("%c %d\n", c?'b':'w', m * 64);
+ putcode(fax, out, &codetable->makeup[m]);
+ }
+ DPRINT("%c %d\n", c?'b':'w', run % 64);
+ putcode(fax, out, &codetable->termination[run % 64]);
+ }
+ else {
+ DPRINT("%c %d\n", c?'b':'w', run);
+ putcode(fax, out, &codetable->termination[run]);
+ }
+}
+
+static fz_error *
+enc1d(fz_faxe *fax, unsigned char *line, fz_buffer *out)
+{
+ int run;
+
+#ifdef DEBUG
+if (fax->a0 < 0) {
+ DPRINT("1d scanline %d\n", fax->ridx + 1);
+ DPRINT("color = %d\n", fax->c);
+}
+#endif
+
+ if (fax->a0 < 0)
+ fax->a0 = 0;
+
+ while (fax->a0 < fax->columns)
+ {
+ run = getrun(line, fax->a0, fax->columns, fax->c);
+
+ if (out->wp + 1 + runbytes(run) > out->ep)
+ return fz_ioneedout;
+
+ putrun(fax, out, run, fax->c);
+
+ fax->a0 += run;
+ fax->c = !fax->c;
+ }
+
+ return 0;
+}
+
+static fz_error *
+enc2d(fz_faxe *fax, unsigned char *ref, unsigned char *src, fz_buffer *out)
+{
+ int a1, a2, b1, b2;
+ int run1, run2, n;
+
+
+#ifdef DEBUG
+if (fax->a0 < 0)
+{
+ DPRINT("2d scanline %d\n", fax->ridx + 1);
+}
+#endif
+
+ DPRINT("color = %d\n", fax->c);
+
+ while (fax->a0 < fax->columns)
+ {
+ a1 = findchanging(src, fax->a0, fax->columns);
+ b1 = findchangingcolor(ref, fax->a0, fax->columns, !fax->c);
+ b2 = findchanging(ref, b1, fax->columns);
+
+#ifdef DEBUGBITS
+ DPRINT("twod a0=%d a1=%d b1=%d b2=%d\n", fax->a0, a1, b1, b2);
+#endif
+
+ /* pass */
+ if (b2 < a1)
+ {
+ if (out->wp + 1 + codebytes > out->ep)
+ return fz_ioneedout;
+
+ DPRINT("P\n");
+
+ putcode(fax, out, &cf2_run_pass);
+
+ fax->a0 = b2;
+ }
+
+ /* vertical */
+ else if (ABS(b1 - a1) <= 3)
+ {
+ if (out->wp + 1 + codebytes > out->ep)
+ return fz_ioneedout;
+
+#ifdef DEBUG
+ if (b1 - a1 == 0)
+ DPRINT("V0\n");
+ else if (b1 - a1 < 0)
+ DPRINT("VR%d\n", a1 - b1);
+ else
+ DPRINT("VL%d\n", b1 - a1);
+#endif
+
+ putcode(fax, out, &cf2_run_vertical[b1 - a1 + 3]);
+
+ fax->a0 = a1;
+ fax->c = !fax->c;
+ }
+
+ /* horizontal */
+ else
+ {
+ a2 = findchanging(src, a1, fax->columns);
+ run1 = a1 - fax->a0;
+ run2 = a2 - a1;
+ n = codebytes + runbytes(run1) + runbytes(run2);
+
+ if (out->wp + 1 + n > out->ep)
+ return fz_ioneedout;
+
+ DPRINT("H\n");
+
+ putcode(fax, out, &cf2_run_horizontal);
+ putrun(fax, out, run1, fax->c);
+ putrun(fax, out, run2, !fax->c);
+
+ fax->a0 = a2;
+ }
+ }
+
+ return 0;
+}
+
+static fz_error *
+process(fz_faxe *fax, fz_buffer *in, fz_buffer *out)
+{
+ fz_error *err;
+ int i, n;
+
+ while (1)
+ {
+ if (in->rp == in->wp && in->eof)
+ goto rtc;
+
+ switch (fax->stage)
+ {
+ case 0:
+ if (fax->encodedbytealign)
+ {
+ if (fax->endofline) {
+ if (out->wp + 1 + 1 > out->ep)
+ return fz_ioneedout;
+
+ /* make sure that EOL ends on a byte border */
+ putbits(fax, out, 0, (12 - fax->bidx) & 7);
+ }
+ else {
+ if (fax->bidx) {
+ if (out->wp + 1 > out->ep)
+ return fz_ioneedout;
+ fax->bidx = 0;
+ out->wp ++;
+ }
+ }
+ }
+
+ fax->stage ++;
+
+ case 1:
+ if (fax->endofline)
+ {
+ if (out->wp + 1 + codebytes + 1 > out->ep)
+ return fz_ioneedout;
+
+ DPRINT("EOL\n");
+ if (fax->k > 0) {
+ if (fax->ridx % fax->k == 0)
+ putcode(fax, out, &cf2_run_eol_1d);
+ else
+ putcode(fax, out, &cf2_run_eol_2d);
+ }
+ else {
+ putcode(fax, out, &cf_run_eol);
+ }
+ }
+
+ fax->stage ++;
+
+ case 2:
+ if (in->rp + fax->stride > in->wp) {
+ if (in->eof) /* XXX barf here? */
+ goto rtc;
+ return fz_ioneedin;
+ }
+
+ memcpy(fax->ref, fax->src, fax->stride);
+ memcpy(fax->src, in->rp, fax->stride);
+
+ if (!fax->blackis1)
+ {
+ for (i = 0; i < fax->stride; i++)
+ fax->src[i] = ~fax->src[i];
+ }
+
+ in->rp += fax->stride;
+
+ fax->c = 0;
+ fax->a0 = -1;
+
+#ifdef DEBUGBITS
+ DPRINT("LINE %d\n", fax->ridx);
+ printline(stderr, fax->ref, fax->columns);
+ printline(stderr, fax->src, fax->columns);
+#endif
+
+ fax->stage ++;
+
+ case 3:
+ err = 0; /* to silence compiler */
+
+ if (fax->k < 0) {
+ err = enc2d(fax, fax->ref, fax->src, out);
+ }
+ else if (fax->k == 0) {
+ err = enc1d(fax, fax->src, out);
+ }
+ else if (fax->k > 0) {
+ if (fax->ridx % fax->k == 0) {
+ err = enc1d(fax, fax->src, out);
+ }
+ else {
+ err = enc2d(fax, fax->ref, fax->src, out);
+ }
+ }
+
+ if (err) return err;
+
+ fax->ridx ++;
+
+ fax->stage = 0;
+ }
+ }
+
+rtc:
+ if (fax->endofblock)
+ {
+ n = fax->k < 0 ? 2 : 6;
+
+ if (out->wp + 1 + codebytes * n > out->ep)
+ return fz_ioneedout;
+
+ for (i = 0; i < n; i++) {
+ putcode(fax, out, &cf_run_eol);
+ if (fax->k > 0)
+ putbits(fax, out, 1, 1);
+ }
+ DPRINT("RTC\n");
+ }
+
+ if (fax->bidx)
+ out->wp ++;
+ out->eof = 1;
+
+ return fz_iodone;
+}
+
+fz_error *
+fz_processfaxe(fz_filter *p, fz_buffer *in, fz_buffer *out)
+{
+ fz_faxe *fax = (fz_faxe*) p;
+ fz_error *err;
+
+ /* restore partial bits */
+ *out->wp = fax->bsave;
+
+ err = process(fax, in, out);
+
+ /* save partial bits */
+ fax->bsave = *out->wp;
+
+ return err;
+}
+
diff --git a/filter/faxe.h b/filter/faxe.h
new file mode 100644
index 00000000..dd3fc121
--- /dev/null
+++ b/filter/faxe.h
@@ -0,0 +1,37 @@
+/* Fax G3/G4 tables */
+
+typedef struct cfe_code_s cfe_code;
+
+struct cfe_code_s
+{
+ unsigned short code;
+ unsigned short nbits;
+};
+
+typedef struct cf_runs_s {
+ cfe_code termination[64];
+ cfe_code makeup[41];
+} cf_runs;
+
+/* Encoding tables */
+
+/* Codes common to 1-D and 2-D encoding. */
+extern const cfe_code cf_run_eol;
+extern const cf_runs cf_white_runs, cf_black_runs;
+extern const cfe_code cf_uncompressed[6];
+extern const cfe_code cf_uncompressed_exit[10]; /* indexed by 2 x length of */
+
+/* 1-D encoding. */
+extern const cfe_code cf1_run_uncompressed;
+
+/* 2-D encoding. */
+enum { cf2_run_vertical_offset = 3 };
+extern const cfe_code cf2_run_pass;
+extern const cfe_code cf2_run_vertical[7]; /* indexed by b1 - a1 + offset */
+extern const cfe_code cf2_run_horizontal;
+extern const cfe_code cf2_run_uncompressed;
+
+/* 2-D Group 3 encoding. */
+extern const cfe_code cf2_run_eol_1d;
+extern const cfe_code cf2_run_eol_2d;
+
diff --git a/filter/faxetab.c b/filter/faxetab.c
new file mode 100644
index 00000000..409039f4
--- /dev/null
+++ b/filter/faxetab.c
@@ -0,0 +1,131 @@
+/* Tables for CCITTFaxEncode filter */
+
+#include "faxe.h"
+
+/* Define the end-of-line code. */
+const cfe_code cf_run_eol = {1, 12};
+
+/* Define the 1-D code that signals uncompressed data. */
+const cfe_code cf1_run_uncompressed = {0xf, 12};
+
+/* Define the 2-D run codes. */
+const cfe_code cf2_run_pass = {0x1, 4};
+const cfe_code cf2_run_vertical[7] =
+{
+ {0x3, 7},
+ {0x3, 6},
+ {0x3, 3},
+ {0x1, 1},
+ {0x2, 3},
+ {0x2, 6},
+ {0x2, 7}
+};
+const cfe_code cf2_run_horizontal = {1, 3};
+const cfe_code cf2_run_uncompressed = {0xf, 10};
+
+/* EOL codes for Group 3 2-D. */
+const cfe_code cf2_run_eol_1d = { (1 << 1) + 1, 12 + 1};
+const cfe_code cf2_run_eol_2d = { (1 << 1) + 0, 12 + 1};
+
+/* White run codes. */
+const cf_runs cf_white_runs =
+{
+ /* Termination codes */
+ {
+ {0x35, 8}, {0x7, 6}, {0x7, 4}, {0x8, 4},
+ {0xb, 4}, {0xc, 4}, {0xe, 4}, {0xf, 4},
+ {0x13, 5}, {0x14, 5}, {0x7, 5}, {0x8, 5},
+ {0x8, 6}, {0x3, 6}, {0x34, 6}, {0x35, 6},
+ {0x2a, 6}, {0x2b, 6}, {0x27, 7}, {0xc, 7},
+ {0x8, 7}, {0x17, 7}, {0x3, 7}, {0x4, 7},
+ {0x28, 7}, {0x2b, 7}, {0x13, 7}, {0x24, 7},
+ {0x18, 7}, {0x2, 8}, {0x3, 8}, {0x1a, 8},
+ {0x1b, 8}, {0x12, 8}, {0x13, 8}, {0x14, 8},
+ {0x15, 8}, {0x16, 8}, {0x17, 8}, {0x28, 8},
+ {0x29, 8}, {0x2a, 8}, {0x2b, 8}, {0x2c, 8},
+ {0x2d, 8}, {0x4, 8}, {0x5, 8}, {0xa, 8},
+ {0xb, 8}, {0x52, 8}, {0x53, 8}, {0x54, 8},
+ {0x55, 8}, {0x24, 8}, {0x25, 8}, {0x58, 8},
+ {0x59, 8}, {0x5a, 8}, {0x5b, 8}, {0x4a, 8},
+ {0x4b, 8}, {0x32, 8}, {0x33, 8}, {0x34, 8}
+ },
+
+ /* Make-up codes */
+ {
+ {0, 0} /* dummy */ , {0x1b, 5}, {0x12, 5}, {0x17, 6},
+ {0x37, 7}, {0x36, 8}, {0x37, 8}, {0x64, 8},
+ {0x65, 8}, {0x68, 8}, {0x67, 8}, {0xcc, 9},
+ {0xcd, 9}, {0xd2, 9}, {0xd3, 9}, {0xd4, 9},
+ {0xd5, 9}, {0xd6, 9}, {0xd7, 9}, {0xd8, 9},
+ {0xd9, 9}, {0xda, 9}, {0xdb, 9}, {0x98, 9},
+ {0x99, 9}, {0x9a, 9}, {0x18, 6}, {0x9b, 9},
+ {0x8, 11}, {0xc, 11}, {0xd, 11}, {0x12, 12},
+ {0x13, 12}, {0x14, 12}, {0x15, 12}, {0x16, 12},
+ {0x17, 12}, {0x1c, 12}, {0x1d, 12}, {0x1e, 12},
+ {0x1f, 12}
+ }
+};
+
+/* Black run codes. */
+const cf_runs cf_black_runs =
+{
+ /* Termination codes */
+ {
+ {0x37, 10}, {0x2, 3}, {0x3, 2}, {0x2, 2},
+ {0x3, 3}, {0x3, 4}, {0x2, 4}, {0x3, 5},
+ {0x5, 6}, {0x4, 6}, {0x4, 7}, {0x5, 7},
+ {0x7, 7}, {0x4, 8}, {0x7, 8}, {0x18, 9},
+ {0x17, 10}, {0x18, 10}, {0x8, 10}, {0x67, 11},
+ {0x68, 11}, {0x6c, 11}, {0x37, 11}, {0x28, 11},
+ {0x17, 11}, {0x18, 11}, {0xca, 12}, {0xcb, 12},
+ {0xcc, 12}, {0xcd, 12}, {0x68, 12}, {0x69, 12},
+ {0x6a, 12}, {0x6b, 12}, {0xd2, 12}, {0xd3, 12},
+ {0xd4, 12}, {0xd5, 12}, {0xd6, 12}, {0xd7, 12},
+ {0x6c, 12}, {0x6d, 12}, {0xda, 12}, {0xdb, 12},
+ {0x54, 12}, {0x55, 12}, {0x56, 12}, {0x57, 12},
+ {0x64, 12}, {0x65, 12}, {0x52, 12}, {0x53, 12},
+ {0x24, 12}, {0x37, 12}, {0x38, 12}, {0x27, 12},
+ {0x28, 12}, {0x58, 12}, {0x59, 12}, {0x2b, 12},
+ {0x2c, 12}, {0x5a, 12}, {0x66, 12}, {0x67, 12}
+ },
+
+ /* Make-up codes. */
+ {
+ {0, 0} /* dummy */ , {0xf, 10}, {0xc8, 12}, {0xc9, 12},
+ {0x5b, 12}, {0x33, 12}, {0x34, 12}, {0x35, 12},
+ {0x6c, 13}, {0x6d, 13}, {0x4a, 13}, {0x4b, 13},
+ {0x4c, 13}, {0x4d, 13}, {0x72, 13}, {0x73, 13},
+ {0x74, 13}, {0x75, 13}, {0x76, 13}, {0x77, 13},
+ {0x52, 13}, {0x53, 13}, {0x54, 13}, {0x55, 13},
+ {0x5a, 13}, {0x5b, 13}, {0x64, 13}, {0x65, 13},
+ {0x8, 11}, {0xc, 11}, {0xd, 11}, {0x12, 12},
+ {0x13, 12}, {0x14, 12}, {0x15, 12}, {0x16, 12},
+ {0x17, 12}, {0x1c, 12}, {0x1d, 12}, {0x1e, 12},
+ {0x1f, 12}
+ }
+};
+
+/* Uncompressed codes. */
+const cfe_code cf_uncompressed[6] =
+{
+ {1, 1},
+ {1, 2},
+ {1, 3},
+ {1, 4},
+ {1, 5},
+ {1, 6}
+};
+
+/* Uncompressed exit codes. */
+const cfe_code cf_uncompressed_exit[10] =
+{
+ {2, 8}, {3, 8},
+ {2, 9}, {3, 9},
+ {2, 10}, {3, 10},
+ {2, 11}, {3, 11},
+ {2, 12}, {3, 12}
+};
+
+/* Some C compilers insist on having executable code in every file.... */
+void scfetab_dummy(void) { }
+
diff --git a/filter/file.c b/filter/file.c
new file mode 100644
index 00000000..c3ab63a4
--- /dev/null
+++ b/filter/file.c
@@ -0,0 +1,589 @@
+#include <fitz.h>
+
+/* TODO: nil filter on write */
+
+fz_error *
+fz_ferror(fz_file *f)
+{
+ fz_error *e = f->error;
+ f->error = nil;
+ return e;
+}
+
+fz_error *
+fz_openfile(fz_file **filep, char *path, int mode)
+{
+ fz_error *error;
+ fz_file *file;
+ int fd;
+
+ assert(mode == O_RDONLY || mode == O_WRONLY);
+
+ file = *filep = fz_malloc(sizeof(fz_file));
+ if (!file)
+ return fz_outofmem;
+
+ fd = open(path, mode, 0);
+ if (fd == -1)
+ return fz_throw("ioerror: open '%s': %s", path, strerror(errno));
+
+ file->mode = mode;
+ file->fd = fd;
+ file->depth = 0;
+ file->error = nil;
+ file->filter = nil;
+ file->in = nil;
+ file->out = nil;
+
+ error = fz_newbuffer(&file->in, FZ_BUFSIZE);
+ if (error)
+ goto cleanup;
+
+ error = fz_newbuffer(&file->out, FZ_BUFSIZE);
+ if (error)
+ goto cleanup;
+
+ return nil;
+
+cleanup:
+ close(fd);
+ fz_free(file->out);
+ fz_free(file->in);
+ fz_free(file);
+ *filep = nil;
+ return error;
+}
+
+void
+fz_closefile(fz_file *file)
+{
+ assert(file->depth == 0);
+
+ if (file->mode == O_WRONLY)
+ fz_flush(file);
+
+ if (file->error)
+ {
+ fz_warn("%s", file->error->msg);
+ fz_freeerror(file->error);
+ file->error = nil;
+ }
+
+ close(file->fd);
+
+ if (file->filter) fz_freefilter(file->filter);
+ fz_freebuffer(file->in);
+ fz_freebuffer(file->out);
+ fz_free(file);
+}
+
+fz_error *
+fz_pushfilter(fz_file *file, fz_filter *filter)
+{
+ fz_error *error;
+ fz_buffer *buf;
+
+ if (file->depth == 0)
+ {
+ buf = file->out;
+ file->out = file->in;
+ file->in = buf;
+
+ file->out->rp = file->out->bp;
+ file->out->wp = file->out->bp;
+ file->out->eof = 0;
+
+ file->filter = filter;
+ }
+ else
+ {
+ error = fz_chainpipeline(&file->filter, file->filter, filter, file->out);
+ if (error)
+ return error;
+
+ error = fz_newbuffer(&file->out, FZ_BUFSIZE);
+ if (error)
+ {
+ fz_unchainpipeline(file->filter, &file->filter, &file->out);
+ return error;
+ }
+ }
+
+ file->depth ++;
+
+ return nil;
+}
+
+void
+fz_popfilter(fz_file *file)
+{
+ fz_buffer *buf;
+
+ assert(file->depth > 0);
+
+ if (file->mode == O_WRONLY)
+ fz_flush(file);
+
+ if (file->error)
+ {
+ fz_warn("%s", file->error->msg);
+ fz_freeerror(file->error);
+ file->error = nil;
+ }
+
+ if (file->depth == 1)
+ {
+ fz_freefilter(file->filter);
+ file->filter = nil;
+
+ buf = file->out;
+ file->out = file->in;
+ file->in = buf;
+
+ file->in->rp = file->in->bp;
+ file->in->wp = file->in->bp;
+ file->in->eof = 0;
+ }
+ else
+ {
+ fz_freebuffer(file->out);
+ fz_unchainpipeline(file->filter, &file->filter, &file->out);
+ }
+
+ file->depth --;
+}
+
+int
+fz_seek(fz_file *f, int ofs)
+{
+ int t;
+ int c;
+
+ assert(f->mode == O_RDONLY);
+
+ if (f->filter)
+ {
+ if (ofs < fz_tell(f))
+ {
+ f->error = fz_throw("ioerror: cannot seek backwards in filter");
+ return -1;
+ }
+ while (fz_tell(f) < ofs)
+ {
+ c = fz_readbyte(f);
+ if (c == EOF)
+ return -1;
+ }
+ return 0;
+ }
+
+ t = lseek(f->fd, ofs, 0);
+ if (t == -1)
+ {
+ f->error = fz_throw("ioerror: lseek: %s", strerror(errno));
+ return -1;
+ }
+
+ f->out->rp = f->out->bp;
+ f->out->wp = f->out->bp;
+ f->out->eof = 0;
+
+ return 0;
+}
+
+int
+fz_tell(fz_file *f)
+{
+ int t;
+
+ if (f->filter)
+ {
+ return f->filter->count - (f->out->wp - f->out->rp);
+ }
+
+ t = lseek(f->fd, 0, 1);
+ if (t == -1)
+ {
+ f->error = fz_throw("ioerror: lseek: %s", strerror(errno));
+ return -1;
+ }
+
+ return t - (f->out->wp - f->out->rp);
+}
+
+/*
+ * Read mode
+ */
+
+static int doread(fz_buffer *b, int fd)
+{
+ int n = read(fd, b->wp, b->ep - b->wp);
+ if (n == -1)
+ return -1;
+ if (n == 0)
+ b->eof = 1;
+ b->wp += n;
+ return n;
+}
+
+static int producedata(fz_file *f)
+{
+ fz_error *reason;
+ int produced;
+ int n;
+
+ assert(f->mode == O_RDONLY);
+ assert(f->error == nil);
+
+ if (!f->filter)
+ {
+ fz_rewindbuffer(f->out);
+ n = doread(f->out, f->fd);
+ if (n < 0) {
+ f->error = fz_throw("ioerror in read: %s", strerror(errno));
+ return -1;
+ }
+ return 0;
+ }
+
+ produced = 0;
+
+ while (1)
+ {
+ reason = fz_process(f->filter, f->in, f->out);
+
+ if (f->filter->produced)
+ produced = 1;
+
+ if (reason == fz_ioneedin)
+ {
+ if (f->in->eof) {
+ f->error = fz_throw("ioerror: premature eof in filter");
+ return -1;
+ }
+
+ /* no space to produce, rewind or grow */
+ if (f->in->wp == f->in->ep)
+ {
+ if (f->in->rp > f->in->bp)
+ f->error = fz_rewindbuffer(f->in);
+ else
+ f->error = fz_growbuffer(f->in);
+ if (f->error)
+ return -1;
+ }
+
+ /* then fill with more input */
+ n = doread(f->in, f->fd);
+ if (n < 0) {
+ f->error = fz_throw("ioerror in read: %s", strerror(errno));
+ return -1;
+ }
+
+ if (produced)
+ return 0;
+ }
+
+ else if (reason == fz_ioneedout)
+ {
+ if (produced)
+ return 0;
+
+ /* need more outspace, and produced no data */
+ if (f->out->rp > f->out->bp)
+ f->error = fz_rewindbuffer(f->out);
+ else
+ f->error = fz_growbuffer(f->out);
+ if (f->error)
+ return -1;
+ }
+
+ else if (reason == fz_iodone)
+ return 0;
+
+ else {
+ f->error = reason;
+ return -1;
+ }
+ }
+}
+
+int
+fz_peekbyte(fz_file *f)
+{
+ if (f->out->rp == f->out->wp)
+ {
+ if (f->out->eof) return EOF;
+ if (producedata(f)) return EOF;
+ }
+
+ if (f->out->rp < f->out->wp)
+ return *f->out->rp;
+
+ return EOF;
+}
+
+int
+fz_readbyte(fz_file *f)
+{
+ if (f->out->rp == f->out->wp)
+ {
+ if (f->out->eof) return EOF;
+ if (producedata(f)) return EOF;
+ }
+
+ if (f->out->rp < f->out->wp)
+ return *f->out->rp++;
+
+ return EOF;
+}
+
+int
+fz_read(fz_file *f, char *buf, int n)
+{
+ int i = 0;
+
+ while (i < n)
+ {
+ while (f->out->rp < f->out->wp && i < n)
+ buf[i++] = *f->out->rp ++;
+
+ if (f->out->rp == f->out->wp)
+ {
+ if (f->out->eof) return i;
+ if (producedata(f) < 0) return -1;
+ }
+ }
+
+ return i;
+}
+
+int
+fz_readline(fz_file *f, char *buf, int n)
+{
+ int c = EOF;
+ char *s = buf;
+
+ while (n > 1)
+ {
+ c = fz_readbyte(f);
+ if (c == EOF)
+ break;
+ if (c == '\r') {
+ c = fz_peekbyte(f);
+ if (c == '\n')
+ c = fz_readbyte(f);
+ break;
+ }
+ if (c == '\n')
+ break;
+ *s++ = c;
+ n--;
+ }
+ if (n)
+ *s = '\0';
+ return s - buf;
+}
+
+/*
+ * Write mode
+ */
+
+static int dowrite(fz_buffer *b, int fd)
+{
+ int n = write(fd, b->rp, b->wp - b->rp);
+ if (n == -1)
+ return -1;
+ b->rp += n;
+ return n;
+}
+
+int
+fz_write(fz_file *f, char *buf, int n)
+{
+ fz_error *reason;
+ int i = 0;
+ int x;
+
+ assert(f->mode == O_WRONLY);
+ assert(f->error == nil);
+
+ while (i < n)
+ {
+ while (f->in->rp < f->in->wp && i < n)
+ {
+ *f->in->rp++ = buf[i++];
+ }
+
+ if (f->in->rp == f->in->wp)
+ {
+ reason = fz_process(f->filter, f->in, f->out);
+
+ if (reason == fz_ioneedin)
+ {
+ if (f->in->wp == f->in->ep) {
+ if (f->in->rp > f->in->bp)
+ f->error = fz_rewindbuffer(f->in);
+ else
+ f->error = fz_growbuffer(f->in);
+ if (f->error)
+ return -1;
+ }
+ }
+
+ else if (reason == fz_ioneedout)
+ {
+ x = dowrite(f->out, f->fd);
+ if (x < 0) {
+ f->error = fz_throw("ioerror in write: %s", strerror(errno));
+ return -1;
+ }
+
+ if (f->out->rp > f->out->bp)
+ f->error = fz_rewindbuffer(f->out);
+ else
+ f->error = fz_growbuffer(f->out);
+ if (f->error)
+ return -1;
+ }
+
+ else if (reason == fz_iodone)
+ {
+ x = dowrite(f->out, f->fd);
+ if (x < 0) {
+ f->error = fz_throw("ioerror in write: %s", strerror(errno));
+ return -1;
+ }
+ break;
+ }
+
+ else {
+ f->error = reason;
+ return -1;
+ }
+ }
+ }
+
+ return i;
+}
+
+int
+fz_flush(fz_file *f)
+{
+ fz_error *reason;
+ int n;
+
+ assert(f->mode == O_WRONLY);
+ assert(f->error == nil);
+
+ f->in->eof = 1;
+
+ while (!f->out->eof)
+ {
+ reason = fz_process(f->filter, f->in, f->out);
+
+ if (reason == fz_ioneedin) {
+ f->error = fz_throw("ioerror: premature eof in filter");
+ return -1;
+ }
+
+ else if (reason == fz_ioneedout)
+ {
+ n = dowrite(f->out, f->fd);
+ if (n < 0) {
+ f->error = fz_throw("ioerror in write: %s", strerror(errno));
+ return -1;
+ }
+
+ if (f->out->rp > f->out->bp)
+ f->error = fz_rewindbuffer(f->out);
+ else
+ f->error = fz_growbuffer(f->out);
+ if (f->error)
+ return -1;
+ }
+
+ else if (reason == fz_iodone)
+ {
+ n = dowrite(f->out, f->fd);
+ if (n < 0) {
+ f->error = fz_throw("ioerror in write: %s", strerror(errno));
+ return -1;
+ }
+ break;
+ }
+
+ else {
+ f->error = reason;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Utility function to consume contents of file stream into
+ * a freshly allocated buffer; realloced and trimmed to size.
+ */
+
+enum { CHUNKSIZE = 4096 };
+
+fz_error *
+fz_readfile(unsigned char **bufp, int *lenp, fz_file *file)
+{
+ unsigned char *newbuf;
+ unsigned char *buf;
+ int len;
+ int pos;
+ int n;
+
+ *bufp = nil;
+ *lenp = 0;
+
+ len = 0;
+ pos = 0;
+ buf = nil;
+
+ while (1)
+ {
+ if (len - pos == 0)
+ {
+ len += CHUNKSIZE;
+ newbuf = fz_realloc(buf, len);
+ if (!newbuf)
+ {
+ fz_free(buf);
+ return fz_outofmem;
+ }
+ buf = newbuf;
+ }
+
+ n = fz_read(file, buf + pos, len - pos);
+
+printf("fz_read %d bytes\n", n);
+
+ if (n < 0)
+ {
+ fz_free(buf);
+ return fz_ferror(file);
+ }
+
+ pos += n;
+
+ if (n < CHUNKSIZE)
+ {
+ newbuf = fz_realloc(buf, pos);
+ if (!newbuf)
+ {
+ fz_free(buf);
+ return fz_outofmem;
+ }
+
+ *bufp = newbuf;
+ *lenp = pos;
+ return nil;
+ }
+ }
+}
+
diff --git a/filter/filter.c b/filter/filter.c
new file mode 100644
index 00000000..8e28a532
--- /dev/null
+++ b/filter/filter.c
@@ -0,0 +1,59 @@
+#include <fitz.h>
+
+fz_error fz_kioneedin = {
+ .msg = {"<ioneedin>"},
+ .func = {"<process>"},
+ .file = {"filter.c"},
+ .line = 0,
+ .frozen = 1
+};
+
+fz_error fz_kioneedout = {
+ .msg = {"<ioneedout>"},
+ .func = {"<process>"},
+ .file = {"filter.c"},
+ .line = 0,
+ .frozen = 1
+};
+
+fz_error fz_kiodone = {
+ .msg = {"<iodone>"},
+ .func = {"<process>"},
+ .file = {"filter.c"},
+ .line = 0,
+ .frozen = 1
+};
+
+fz_error *
+fz_process(fz_filter *f, fz_buffer *in, fz_buffer *out)
+{
+ fz_error *reason;
+ unsigned char *oldrp;
+ unsigned char *oldwp;
+
+ assert(!out->eof);
+
+ oldrp = in->rp;
+ oldwp = out->wp;
+
+ reason = f->process(f, in, out);
+
+ assert(in->rp <= in->wp);
+ assert(out->wp <= out->ep);
+
+ f->consumed = in->rp > oldrp;
+ f->produced = out->wp > oldwp;
+ f->count += out->wp - oldwp;
+
+ if (reason != fz_ioneedin && reason != fz_ioneedout)
+ out->eof = 1;
+
+ return reason;
+}
+
+void
+fz_freefilter(fz_filter *f)
+{
+ f->free(f);
+}
+
diff --git a/filter/flate.c b/filter/flate.c
new file mode 100644
index 00000000..f7046474
--- /dev/null
+++ b/filter/flate.c
@@ -0,0 +1,178 @@
+#include <fitz.h>
+
+#include <zlib.h>
+
+typedef struct fz_flate_s fz_flate;
+
+struct fz_flate_s
+{
+ fz_filter super;
+ z_stream z;
+};
+
+static void *
+zmalloc(void *opaque, unsigned int items, unsigned int size)
+{
+ fz_memorycontext *mctx = (fz_memorycontext*)opaque;
+ return mctx->malloc(mctx, items * size);
+}
+
+fz_error *
+fz_newflated(fz_filter **fp, fz_obj *params)
+{
+ fz_error *eo;
+ int ei;
+
+ FZ_NEWFILTER(fz_flate, f, flated);
+
+ f->z.zalloc = zmalloc;
+ f->z.zfree = (void(*)(void*,void*))fz_currentmemorycontext()->free;
+ f->z.opaque = fz_currentmemorycontext();
+ f->z.next_in = nil;
+ f->z.avail_in = 0;
+
+ ei = inflateInit(&f->z);
+ if (ei != Z_OK) {
+ eo = fz_throw("ioerror: inflateInit: %s", f->z.msg);
+ fz_free(f);
+ return eo;
+ }
+
+ return nil;
+}
+
+void
+fz_freeflated(fz_filter *f)
+{
+ z_streamp zp = &((fz_flate*)f)->z;
+ int err;
+
+ err = inflateEnd(zp);
+ if (err != Z_OK)
+ fprintf(stderr, "inflateEnd: %s", zp->msg);
+
+ fz_free(f);
+}
+
+fz_error *
+fz_processflated(fz_filter *f, fz_buffer *in, fz_buffer *out)
+{
+ z_streamp zp = &((fz_flate*)f)->z;
+ int err;
+
+ if (in->rp == in->wp && !in->eof)
+ return fz_ioneedin;
+ if (out->wp == out->ep)
+ return fz_ioneedout;
+
+ zp->next_in = in->rp;
+ zp->avail_in = in->wp - in->rp;
+
+ zp->next_out = out->wp;
+ zp->avail_out = out->ep - out->wp;
+
+ err = inflate(zp, Z_NO_FLUSH);
+
+ in->rp = in->wp - zp->avail_in;
+ out->wp = out->ep - zp->avail_out;
+
+ if (err == Z_STREAM_END) {
+ out->eof = 1;
+ return fz_iodone;
+ }
+ else if (err == Z_OK) {
+ if (in->rp == in->wp && !in->eof)
+ return fz_ioneedin;
+ if (out->wp == out->ep)
+ return fz_ioneedout;
+ return fz_ioneedin; /* hmm, what's going on here? */
+ }
+ else {
+ return fz_throw("ioerror: inflate: %s", zp->msg);
+ }
+}
+
+fz_error *
+fz_newflatee(fz_filter **fp, fz_obj *params)
+{
+ fz_obj *obj;
+ fz_error *eo;
+ int effort;
+ int ei;
+
+ FZ_NEWFILTER(fz_flate, f, flatee);
+
+ effort = -1;
+
+ if (params) {
+ obj = fz_dictgets(params, "Effort");
+ if (obj) effort = fz_toint(obj);
+ }
+
+ f->z.zalloc = zmalloc;
+ f->z.zfree = (void(*)(void*,void*))fz_currentmemorycontext()->free;
+ f->z.opaque = fz_currentmemorycontext();
+ f->z.next_in = nil;
+ f->z.avail_in = 0;
+
+ ei = deflateInit(&f->z, effort);
+ if (ei != Z_OK) {
+ eo = fz_throw("ioerror: deflateInit: %s", f->z.msg);
+ fz_free(f);
+ return eo;
+ }
+
+ return nil;
+}
+
+void
+fz_freeflatee(fz_filter *f)
+{
+ z_streamp zp = &((fz_flate*)f)->z;
+ int err;
+
+ err = deflateEnd(zp);
+ if (err != Z_OK)
+ fprintf(stderr, "deflateEnd: %s", zp->msg);
+
+ fz_free(f);
+}
+
+fz_error *
+fz_processflatee(fz_filter *f, fz_buffer *in, fz_buffer *out)
+{
+ z_streamp zp = &((fz_flate*)f)->z;
+ int err;
+
+ if (in->rp == in->wp && !in->eof)
+ return fz_ioneedin;
+ if (out->wp == out->ep)
+ return fz_ioneedout;
+
+ zp->next_in = in->rp;
+ zp->avail_in = in->wp - in->rp;
+
+ zp->next_out = out->wp;
+ zp->avail_out = out->ep - out->wp;
+
+ err = deflate(zp, in->eof ? Z_FINISH : Z_NO_FLUSH);
+
+ in->rp = in->wp - zp->avail_in;
+ out->wp = out->ep - zp->avail_out;
+
+ if (err == Z_STREAM_END) {
+ out->eof = 1;
+ return fz_iodone;
+ }
+ else if (err == Z_OK) {
+ if (in->rp == in->wp && !in->eof)
+ return fz_ioneedin;
+ if (out->wp == out->ep)
+ return fz_ioneedout;
+ return fz_ioneedin; /* hmm? */
+ }
+ else {
+ return fz_throw("ioerror: deflate: %s", zp->msg);
+ }
+}
+
diff --git a/filter/jbig2d.c b/filter/jbig2d.c
new file mode 100644
index 00000000..e58a5e49
--- /dev/null
+++ b/filter/jbig2d.c
@@ -0,0 +1,97 @@
+#include <fitz.h>
+
+/* TODO: complete rewrite with error checking and use fitz memctx */
+
+/*
+<rillian> so to use a global_ctx, you run your global data through a normal ctx
+<rillian> then call jbig2_make_global_ctx with the normal context
+<rillian> that does the (currently null) conversion
+<maskros> make_global_ctx after i fed it all the global stream data?
+<rillian> maskros: yes
+<rillian> and you pass the new global ctx object to jbig2_ctx_new() when you
++create the per-page ctx
+*/
+
+#include <inttypes.h>
+#include <jbig2.h>
+
+typedef struct fz_jbig2d_s fz_jbig2d;
+
+struct fz_jbig2d_s
+{
+ fz_filter super;
+ Jbig2Ctx *ctx;
+ Jbig2GlobalCtx *gctx;
+ Jbig2Image *page;
+ int idx;
+};
+
+fz_error *
+fz_newjbig2d(fz_filter **fp, fz_obj *params)
+{
+ FZ_NEWFILTER(fz_jbig2d, d, jbig2d);
+ d->ctx = jbig2_ctx_new(nil, JBIG2_OPTIONS_EMBEDDED, nil, nil, nil);
+ d->page = nil;
+ d->idx = 0;
+ return nil;
+}
+
+void
+fz_freejbig2d(fz_filter *filter)
+{
+ fz_jbig2d *d = (fz_jbig2d*)filter;
+ jbig2_ctx_free(d->ctx);
+ fz_free(d);
+}
+
+fz_error *
+fz_setjbig2dglobalstream(fz_filter *filter, unsigned char *buf, int len)
+{
+ fz_jbig2d *d = (fz_jbig2d*)filter;
+ jbig2_data_in(d->ctx, buf, len);
+ d->gctx = jbig2_make_global_ctx(d->ctx);
+ d->ctx = jbig2_ctx_new(nil, JBIG2_OPTIONS_EMBEDDED, d->gctx, nil, nil);
+ return nil;
+}
+
+fz_error *
+fz_processjbig2d(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_jbig2d *d = (fz_jbig2d*)filter;
+ int len;
+
+ while (1)
+ {
+ if (in->rp == in->wp) {
+ if (!in->eof)
+ return fz_ioneedin;
+
+ if (!d->page) {
+ jbig2_complete_page(d->ctx);
+ d->page = jbig2_page_out(d->ctx);
+ }
+
+ if (out->wp == out->ep)
+ return fz_ioneedout;
+
+ len = out->ep - out->wp;
+ if (d->idx + len > d->page->height * d->page->stride)
+ len = d->page->height * d->page->stride - d->idx;
+ memcpy(out->wp, d->page->data + d->idx, len);
+ out->wp += len;
+ d->idx += len;
+
+ if (d->idx == d->page->height * d->page->stride) {
+ jbig2_release_page(d->ctx, d->page);
+ out->eof = 1;
+ return fz_iodone;
+ }
+ }
+ else {
+ len = in->wp - in->rp;
+ jbig2_data_in(d->ctx, in->rp, len);
+ in->rp += len;
+ }
+ }
+}
+
diff --git a/filter/jpxd.c b/filter/jpxd.c
new file mode 100644
index 00000000..26ec743b
--- /dev/null
+++ b/filter/jpxd.c
@@ -0,0 +1,144 @@
+#include <fitz.h>
+
+/* TODO: bpc */
+
+/*
+ * We use the Jasper JPEG2000 coder for now.
+ */
+
+#include <jasper/jasper.h>
+
+static char *colorspacename(jas_clrspc_t clrspc)
+{
+ switch (jas_clrspc_fam(clrspc))
+ {
+ case JAS_CLRSPC_FAM_UNKNOWN: return "Unknown";
+ case JAS_CLRSPC_FAM_XYZ: return "XYZ";
+ case JAS_CLRSPC_FAM_LAB: return "Lab";
+ case JAS_CLRSPC_FAM_GRAY: return "Gray";
+ case JAS_CLRSPC_FAM_RGB: return "RGB";
+ case JAS_CLRSPC_FAM_YCBCR: return "YCbCr";
+ }
+ return "Unknown";
+}
+
+typedef struct fz_jpxd_s fz_jpxd;
+
+struct fz_jpxd_s
+{
+ fz_filter super;
+ jas_stream_t *stream;
+ jas_image_t *image;
+ int offset;
+ int stage;
+};
+
+fz_error *
+fz_newjpxd(fz_filter **fp, fz_obj *params)
+{
+ int err;
+
+ FZ_NEWFILTER(fz_jpxd, d, jpxd);
+
+ err = jas_init();
+ if (err) {
+ fz_free(d);
+ return fz_throw("ioerror in jpxd: jas_init()");
+ }
+
+ d->stream = jas_stream_memopen(nil, 0);
+ if (!d->stream) {
+ fz_free(d);
+ return fz_throw("ioerror in jpxd: jas_stream_memopen()");
+ }
+
+ d->image = nil;
+ d->offset = 0;
+ d->stage = 0;
+
+ return nil;
+}
+
+void
+fz_freejpxd(fz_filter *filter)
+{
+ fz_jpxd *d = (fz_jpxd*)filter;
+ if (d->stream) jas_stream_close(d->stream);
+ if (d->image) jas_image_destroy(d->image);
+ fz_free(d);
+}
+
+fz_error *
+fz_processjpxd(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_jpxd *d = (fz_jpxd*)filter;
+ int n, bpc, w, h;
+ int i, x, y;
+
+ switch (d->stage)
+ {
+ case 0: goto input;
+ case 1: goto decode;
+ case 2: goto output;
+ }
+
+input:
+ while (in->rp < in->wp) {
+ n = jas_stream_write(d->stream, in->rp, in->wp - in->rp);
+ in->rp += n;
+ }
+
+ if (!in->eof)
+ return fz_ioneedin;
+
+ d->stage = 1;
+
+decode:
+ jas_stream_seek(d->stream, 0, 0);
+
+ d->image = jas_image_decode(d->stream, -1, 0);
+ if (!d->image)
+ return fz_throw("ioerror in jpxd: unable to decode image data");
+
+ fprintf(stderr, "P%c\n# JPX %d x %d n=%d bpc=%d colorspace=%04x %s\n%d %d\n%d\n",
+ jas_image_numcmpts(d->image) == 1 ? '5' : '6',
+
+ jas_image_width(d->image),
+ jas_image_height(d->image),
+ jas_image_numcmpts(d->image),
+ jas_image_cmptprec(d->image, 0),
+ jas_image_clrspc(d->image),
+ colorspacename(jas_image_clrspc(d->image)),
+
+ jas_image_width(d->image),
+ jas_image_height(d->image),
+ (1 << jas_image_cmptprec(d->image, 0)) - 1);
+
+ d->stage = 2;
+
+output:
+ w = jas_image_width(d->image);
+ h = jas_image_height(d->image);
+ n = jas_image_numcmpts(d->image);
+ bpc = jas_image_cmptprec(d->image, 0); /* use precision of first component for all... */
+
+ while (d->offset < w * h)
+ {
+ y = d->offset / w;
+ x = d->offset - y * w;
+
+ /* FIXME bpc != 8 */
+
+ if (out->wp + n >= out->ep)
+ return fz_ioneedout;
+
+ for (i = 0; i < n; i++)
+ *out->wp++ = jas_image_readcmptsample(d->image, i, x, y);
+
+ d->offset ++;
+ }
+
+ out->eof = 1;
+ return fz_iodone;
+}
+
diff --git a/filter/lzwd.c b/filter/lzwd.c
new file mode 100644
index 00000000..1ab47357
--- /dev/null
+++ b/filter/lzwd.c
@@ -0,0 +1,263 @@
+#include <fitz.h>
+
+/* TODO: error checking */
+
+#define noDEBUG 1
+
+#ifdef DEBUG
+#define DPRINT(...) fprintf(stderr, __VA_ARGS__)
+#else
+#define DPRINT(...)
+#endif
+
+enum
+{
+ MINBITS = 9,
+ MAXBITS = 12,
+ NUMCODES = (1 << MAXBITS),
+ LZW_CLEAR = 256,
+ LZW_EOD = 257,
+ LZW_FIRST = 258,
+};
+
+typedef struct lzw_code_s lzw_code;
+
+struct lzw_code_s
+{
+ int prev; /* prev code (in string) */
+ unsigned short length; /* string len, including this token */
+ unsigned char value; /* data value */
+ unsigned char firstchar; /* first token of string */
+};
+
+typedef struct fz_lzwd_s fz_lzwd;
+
+struct fz_lzwd_s
+{
+ fz_filter super;
+
+ int earlychange;
+
+ unsigned int word; /* bits loaded from data */
+ int bidx;
+
+ int resume; /* resume output of code from needout */
+ int codebits; /* num bits/code */
+ int code; /* current code */
+ int oldcode; /* previously recognized code */
+ int nextcode; /* next free entry */
+ lzw_code table[NUMCODES];
+};
+
+fz_error *
+fz_newlzwd(fz_filter **fp, fz_obj *params)
+{
+ int i;
+
+ FZ_NEWFILTER(fz_lzwd, lzw, lzwd);
+
+ lzw->earlychange = 0;
+
+ if (params) {
+ fz_obj *obj;
+ obj = fz_dictgets(params, "EarlyChange");
+ if (obj) lzw->earlychange = fz_toint(obj) != 0;
+ }
+
+ lzw->bidx = 32;
+ lzw->word = 0;
+
+ for (i = 0; i < 256; i++) {
+ lzw->table[i].value = i;
+ lzw->table[i].firstchar = i;
+ lzw->table[i].length = 1;
+ lzw->table[i].prev = -1;
+ }
+
+ for (i = LZW_FIRST; i < NUMCODES; i++) {
+ lzw->table[i].value = 0;
+ lzw->table[i].firstchar = 0;
+ lzw->table[i].length = 0;
+ lzw->table[i].prev = -1;
+ }
+
+ lzw->codebits = MINBITS;
+ lzw->code = -1;
+ lzw->nextcode = LZW_FIRST;
+ lzw->oldcode = -1;
+ lzw->resume = 0;
+
+ return nil;
+}
+
+void
+fz_freelzwd(fz_filter *filter)
+{
+ fz_free(filter);
+}
+
+static inline void eatbits(fz_lzwd *lzw, int nbits)
+{
+ lzw->word <<= nbits;
+ lzw->bidx += nbits;
+}
+
+static inline fz_error * fillbits(fz_lzwd *lzw, fz_buffer *in)
+{
+ while (lzw->bidx >= 8)
+ {
+ if (in->rp + 1 > in->wp)
+ return fz_ioneedin;
+ lzw->bidx -= 8;
+ lzw->word |= *in->rp << lzw->bidx;
+ in->rp ++;
+ }
+ return nil;
+}
+
+fz_error *
+fz_processlzwd(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_lzwd *lzw = (fz_lzwd*)filter;
+ unsigned char *s;
+ int len;
+
+ if (lzw->resume) {
+ lzw->resume = 0;
+ goto output;
+ }
+
+ while (1)
+ {
+ if (fillbits(lzw, in)) {
+ DPRINT("FILL[eof=%d] bidx=%d word=%08x\n", in->eof, lzw->bidx, lzw->word);
+ if (in->eof) {
+ if (lzw->bidx > 32 - lzw->codebits) {
+ out->eof = 1;
+ return fz_iodone;
+ }
+ }
+ else {
+ return fz_ioneedin;
+ }
+ }
+
+ lzw->code = lzw->word >> (32 - lzw->codebits);
+
+ DPRINT("CODE[%d]: %03x (%d)\n", lzw->codebits, lzw->code, lzw->code);
+
+ if (lzw->code == LZW_EOD)
+ {
+ DPRINT("-> LZW_EOD\n");
+
+ eatbits(lzw, lzw->codebits);
+ out->eof = 1;
+ return fz_iodone;
+ }
+
+ if (lzw->code == LZW_CLEAR)
+ {
+ DPRINT("-> LZW_CLEAR\n");
+
+ int oldcodebits = lzw->codebits;
+
+ lzw->codebits = MINBITS;
+ lzw->nextcode = LZW_FIRST;
+
+ lzw->code = lzw->word >> (32 - oldcodebits - MINBITS) & ((1 << MINBITS) - 1);
+
+ DPRINT("CODE[%d]: %03x (%d) [after CLEAR]\n", lzw->codebits, lzw->code, lzw->code);
+
+ if (lzw->code == LZW_EOD) {
+ DPRINT("-> LZW_EOD\n");
+
+ eatbits(lzw, oldcodebits + MINBITS);
+ out->eof = 1;
+ return fz_iodone;
+ }
+
+ DPRINT("-> %d\n", lzw->code);
+
+ if (out->wp + 1 > out->ep)
+ return fz_ioneedout;
+
+ *out->wp++ = lzw->code;
+
+ lzw->oldcode = lzw->code;
+
+ eatbits(lzw, oldcodebits + MINBITS);
+
+ continue;
+ }
+
+ eatbits(lzw, lzw->codebits);
+
+ /* if stream starts without a clear code, oldcode is undefined... */
+ if (lzw->oldcode == -1) {
+ lzw->oldcode = lzw->code;
+ goto output;
+ }
+
+ /* add new entry to the code table */
+ lzw->table[lzw->nextcode].prev = lzw->oldcode;
+ lzw->table[lzw->nextcode].firstchar = lzw->table[lzw->oldcode].firstchar;
+ lzw->table[lzw->nextcode].length = lzw->table[lzw->oldcode].length + 1;
+ if (lzw->code < lzw->nextcode)
+ lzw->table[lzw->nextcode].value = lzw->table[lzw->code].firstchar;
+ else
+ lzw->table[lzw->nextcode].value = lzw->table[lzw->nextcode].firstchar;
+
+ DPRINT("NEW[%d] prev=%d, f=%d, l=%d, v=%d\n",
+ lzw->nextcode,
+ lzw->table[lzw->nextcode].prev,
+ lzw->table[lzw->nextcode].firstchar,
+ lzw->table[lzw->nextcode].length,
+ lzw->table[lzw->nextcode].value);
+
+ lzw->nextcode ++;
+
+ if (lzw->nextcode >= (1 << lzw->codebits) - lzw->earlychange - 1)
+ {
+ lzw->codebits ++;
+ if (lzw->codebits > MAXBITS)
+ lzw->codebits = MAXBITS; /* FIXME */
+ }
+
+ lzw->oldcode = lzw->code;
+
+output:
+ /* code maps to a string, copy to output (in reverse...) */
+ if (lzw->code > 255)
+ {
+ if (out->wp + lzw->table[lzw->code].length > out->ep) {
+ lzw->resume = 1;
+ return fz_ioneedout;
+ }
+
+ len = lzw->table[lzw->code].length;
+ s = out->wp + len;
+
+ DPRINT("OUT code=%d len=%d\n", lzw->code, len);
+ do {
+ DPRINT("-> %d\n", lzw->table[lzw->code].value);
+ *(--s) = lzw->table[lzw->code].value;
+ lzw->code = lzw->table[lzw->code].prev;
+ } while (lzw->code >= 0 && s > out->wp);
+ out->wp += len;
+ }
+
+ /* ... or just a single character */
+ else
+ {
+ if (out->wp + 1 > out->ep) {
+ lzw->resume = 1;
+ return fz_ioneedout;
+ }
+
+ DPRINT("OUT code=%d\n-> %d\n", lzw->code, lzw->code);
+
+ *out->wp++ = lzw->code;
+ }
+ }
+}
+
diff --git a/filter/lzwe.c b/filter/lzwe.c
new file mode 100644
index 00000000..dbb6967b
--- /dev/null
+++ b/filter/lzwe.c
@@ -0,0 +1,257 @@
+#include <fitz.h>
+
+#define noDEBUG 1
+
+#ifdef DEBUG
+#define DPRINT(...) fprintf(stderr, __VA_ARGS__)
+#else
+#define DPRINT(...)
+#endif
+
+enum
+{
+ MINBITS = 9,
+ MAXBITS = 12,
+ MAXBYTES = 2,
+ NUMCODES = (1 << MAXBITS),
+ LZW_CLEAR = 256,
+ LZW_EOD = 257,
+ LZW_FIRST = 258,
+ HSIZE = 9001, /* 91% occupancy (???) */
+ HSHIFT = (13 - 8),
+};
+
+typedef struct lzw_hash_s lzw_hash;
+
+struct lzw_hash_s
+{
+ int hash;
+ int code;
+};
+
+typedef struct fz_lzwe_s fz_lzwe;
+
+struct fz_lzwe_s
+{
+ fz_filter super;
+
+ int earlychange;
+
+ int bidx; /* partial bits used in out->wp */
+ unsigned char bsave; /* partial byte saved between process() calls */
+
+ int resume;
+ int code;
+ int fcode;
+ int hcode;
+
+ int codebits;
+ int oldcode;
+ int nextcode;
+
+ lzw_hash table[HSIZE];
+};
+
+static void
+clearhash(fz_lzwe *lzw)
+{
+ int i;
+ for (i = 0; i < HSIZE; i++)
+ lzw->table[i].hash = -1;
+}
+
+fz_error *
+fz_newlzwe(fz_filter **fp, fz_obj *params)
+{
+ FZ_NEWFILTER(fz_lzwe, lzw, lzwe);
+
+ lzw->earlychange = 0;
+
+ if (params) {
+ fz_obj *obj;
+ obj = fz_dictgets(params, "EarlyChange");
+ if (obj) lzw->earlychange = fz_toint(obj) != 0;
+ }
+
+ lzw->bidx = 0;
+ lzw->bsave = 0;
+
+ lzw->resume = 0;
+ lzw->code = -1;
+ lzw->hcode = -1;
+ lzw->fcode = -1;
+
+ lzw->codebits = MINBITS;
+ lzw->nextcode = LZW_FIRST;
+ lzw->oldcode = -1; /* generates LZW_CLEAR */
+
+ clearhash(lzw);
+
+ return nil;
+}
+
+void
+fz_freelzwe(fz_filter *filter)
+{
+ fz_free(filter);
+}
+
+static void
+putcode(fz_lzwe *lzw, fz_buffer *out, int code)
+{
+ int nbits = lzw->codebits;
+
+ DPRINT("CODE[%d] %d\n", nbits, code);
+
+ while (nbits > 0)
+ {
+ if (lzw->bidx == 0) {
+ *out->wp = 0;
+ }
+
+ /* code does not fit: shift right */
+ if (nbits > (8 - lzw->bidx)) {
+ *out->wp |= code >> (nbits - (8 - lzw->bidx));
+ nbits = nbits - (8 - lzw->bidx);
+ lzw->bidx = 0;
+ out->wp ++;
+ }
+
+ /* shift left */
+ else {
+ *out->wp |= code << ((8 - lzw->bidx) - nbits);
+ lzw->bidx += nbits;
+ if (lzw->bidx == 8) {
+ lzw->bidx = 0;
+ out->wp ++;
+ }
+ nbits = 0;
+ }
+ }
+}
+
+
+static fz_error *
+compress(fz_lzwe *lzw, fz_buffer *in, fz_buffer *out)
+{
+ if (lzw->resume) {
+ lzw->resume = 0;
+ goto resume;
+ }
+
+ /* at start of data, output a clear code */
+ if (lzw->oldcode == -1)
+ {
+ if (out->wp + 3 > out->ep)
+ return fz_ioneedout;
+
+ if (in->rp + 1 > in->wp) {
+ if (in->eof)
+ goto eof;
+ return fz_ioneedin;
+ }
+
+ putcode(lzw, out, LZW_CLEAR);
+
+ lzw->oldcode = *in->rp++;
+ }
+
+begin:
+ while (1)
+ {
+ if (in->rp + 1 > in->wp) {
+ if (in->eof)
+ goto eof;
+ return fz_ioneedin;
+ }
+
+ /* read character */
+ lzw->code = *in->rp++;
+
+ DPRINT("READ %d\n", lzw->code);
+
+ /* hash string + character */
+ lzw->fcode = (lzw->code << MAXBITS) + lzw->oldcode;
+ lzw->hcode = (lzw->code << HSHIFT) ^ lzw->oldcode;
+
+ /* primary hash */
+ if (lzw->table[lzw->hcode].hash == lzw->fcode) {
+ lzw->oldcode = lzw->table[lzw->hcode].code;
+ continue;
+ }
+
+ /* secondary hash */
+ if (lzw->table[lzw->hcode].hash != -1) {
+ int disp = HSIZE - lzw->hcode;
+ if (lzw->hcode == 0)
+ disp = 1;
+ do {
+ lzw->hcode = lzw->hcode - disp;
+ if (lzw->hcode < 0)
+ lzw->hcode += HSIZE;
+ if (lzw->table[lzw->hcode].hash == lzw->fcode) {
+ lzw->oldcode = lzw->table[lzw->hcode].code;
+ goto begin;
+ }
+ } while (lzw->table[lzw->hcode].hash != -1);
+ }
+
+resume:
+ /* new entry: emit code and add to table */
+
+ /* reserve space for this code and an eventual CLEAR code */
+ if (out->wp + 5 > out->ep) {
+ lzw->resume = 1;
+ return fz_ioneedout;
+ }
+
+ putcode(lzw, out, lzw->oldcode);
+
+ lzw->oldcode = lzw->code;
+ lzw->table[lzw->hcode].code = lzw->nextcode;
+ lzw->table[lzw->hcode].hash = lzw->fcode;
+
+ lzw->nextcode ++;
+
+ /* table is full: emit clear code and reset */
+ if (lzw->nextcode == NUMCODES - 1) {
+ putcode(lzw, out, LZW_CLEAR);
+ clearhash(lzw);
+ lzw->nextcode = LZW_FIRST;
+ lzw->codebits = MINBITS;
+ }
+
+ /* check if next entry will be too big for the code size */
+ else if (lzw->nextcode >= (1 << lzw->codebits) - lzw->earlychange) {
+ lzw->codebits ++;
+ }
+ }
+
+eof:
+ if (out->wp + 5 > out->ep)
+ return fz_ioneedout;
+
+ putcode(lzw, out, lzw->oldcode);
+ putcode(lzw, out, LZW_EOD);
+
+ out->eof = 1;
+ return fz_iodone;
+}
+
+fz_error *
+fz_processlzwe(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_lzwe *lzw = (fz_lzwe*)filter;
+ fz_error *err;
+
+ /* restore partial bits */
+ *out->wp = lzw->bsave;
+
+ err = compress(lzw, in, out);
+
+ /* save partial bits */
+ lzw->bsave = *out->wp;
+
+ return err;
+}
+
diff --git a/filter/null.c b/filter/null.c
new file mode 100644
index 00000000..a4af03b1
--- /dev/null
+++ b/filter/null.c
@@ -0,0 +1,51 @@
+#include <fitz.h>
+
+typedef struct fz_nullfilter_s fz_nullfilter;
+
+struct fz_nullfilter_s
+{
+ fz_filter super;
+ int len;
+ int cur;
+};
+
+fz_error *
+fz_newnullfilter(fz_filter **fp, int len)
+{
+ FZ_NEWFILTER(fz_nullfilter, f, nullfilter);
+ f->len = len;
+ f->cur = 0;
+ return nil;
+}
+
+void
+fz_freenullfilter(fz_filter *f)
+{
+ fz_free(f);
+}
+
+fz_error *
+fz_processnullfilter(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_nullfilter *f = (fz_nullfilter*)filter;
+ int n;
+
+ n = MIN(MIN(in->wp - in->rp, out->ep - out->wp), f->len - f->cur);
+
+ if (n) {
+ memcpy(out->wp, in->rp, n);
+ in->rp += n;
+ out->wp += n;
+ f->cur += n;
+ }
+
+ if (f->cur == f->len)
+ return fz_iodone;
+ if (in->rp == in->wp)
+ return fz_ioneedin;
+ if (out->wp == out->ep)
+ return fz_ioneedout;
+
+ return fz_throw("braindead programmer in nullfilter");
+}
+
diff --git a/filter/pipeline.c b/filter/pipeline.c
new file mode 100644
index 00000000..ef0412c0
--- /dev/null
+++ b/filter/pipeline.c
@@ -0,0 +1,128 @@
+#include <fitz.h>
+
+#define noDEBUG 1
+
+typedef struct fz_pipeline_s fz_pipeline;
+
+fz_error * fz_processpipeline(fz_filter *filter, fz_buffer *in, fz_buffer *out);
+
+struct fz_pipeline_s
+{
+ fz_filter super;
+ fz_filter *head;
+ fz_buffer *buffer;
+ fz_filter *tail;
+ int tailneedsin;
+};
+
+fz_error *
+fz_chainpipeline(fz_filter **fp, fz_filter *head, fz_filter *tail, fz_buffer *buf)
+{
+ FZ_NEWFILTER(fz_pipeline, p, pipeline);
+ p->head = head;
+ p->tail = tail;
+ p->tailneedsin = 1;
+ p->buffer = buf;
+ return nil;
+}
+
+void
+fz_unchainpipeline(fz_filter *filter, fz_filter **oldfp, fz_buffer **oldbp)
+{
+ fz_pipeline *p = (fz_pipeline*)filter;
+ *oldfp = p->head;
+ *oldbp = p->buffer;
+ fz_freefilter(p->tail);
+ fz_free(p);
+}
+
+fz_error *
+fz_newpipeline(fz_filter **fp, fz_filter *head, fz_filter *tail)
+{
+ fz_error *err;
+
+ FZ_NEWFILTER(fz_pipeline, p, pipeline);
+ p->head = head;
+ p->tail = tail;
+ p->tailneedsin = 1;
+
+ err = fz_newbuffer(&p->buffer, FZ_BUFSIZE);
+ if (err) { fz_free(p); return err; }
+
+ return nil;
+}
+
+void
+fz_freepipeline(fz_filter *filter)
+{
+ fz_pipeline *p = (fz_pipeline*)filter;
+ fz_freefilter(p->head);
+ fz_freefilter(p->tail);
+ fz_freebuffer(p->buffer);
+ fz_free(p);
+}
+
+fz_error *
+fz_processpipeline(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_pipeline *p = (fz_pipeline*)filter;
+ fz_error *e;
+
+ if (p->buffer->eof)
+ goto tail;
+
+ if (p->tailneedsin && p->head->produced)
+ goto tail;
+
+head:
+ e = fz_process(p->head, in, p->buffer);
+
+ if (e == fz_ioneedin)
+ return fz_ioneedin;
+
+ else if (e == fz_ioneedout)
+ {
+ if (p->tailneedsin && !p->head->produced)
+ {
+ fz_error *be = nil;
+ if (p->buffer->rp > p->buffer->bp)
+ be = fz_rewindbuffer(p->buffer);
+ else
+ be = fz_growbuffer(p->buffer);
+ if (be)
+ return be;
+ goto head;
+ }
+ goto tail;
+ }
+
+ else if (e == fz_iodone)
+ goto tail;
+
+ else
+ return e;
+
+tail:
+ e = fz_process(p->tail, p->buffer, out);
+
+ if (e == fz_ioneedin)
+ {
+ if (p->buffer->eof)
+ return fz_throw("ioerror: premature eof in pipeline");
+ p->tailneedsin = 1;
+ goto head;
+ }
+
+ else if (e == fz_ioneedout)
+ {
+ p->tailneedsin = 0;
+ return fz_ioneedout;
+ }
+
+ else if (e == fz_iodone)
+ return fz_iodone;
+
+ else
+ return e;
+}
+
diff --git a/filter/predict.c b/filter/predict.c
new file mode 100644
index 00000000..d5f1f54d
--- /dev/null
+++ b/filter/predict.c
@@ -0,0 +1,239 @@
+#include <fitz.h>
+
+/* TODO: check if this works with 16bpp images */
+
+enum { MAXC = 16 };
+
+typedef struct fz_predict_s fz_predict;
+
+struct fz_predict_s
+{
+ fz_filter super;
+
+ int predictor;
+ int columns;
+ int colors;
+ int bpc;
+
+ int stride;
+ int bpp;
+ unsigned char *ref;
+
+ int encode;
+};
+
+fz_error *
+fz_newpredict(fz_filter **fp, fz_obj *params, int encode)
+{
+ fz_obj *obj;
+
+ FZ_NEWFILTER(fz_predict, p, predict);
+
+ p->encode = encode;
+
+ p->predictor = 1;
+ p->columns = 1;
+ p->colors = 1;
+ p->bpc = 8;
+
+ obj = fz_dictgets(params, "Predictor");
+ if (obj) p->predictor = fz_toint(obj);
+
+ obj = fz_dictgets(params, "Columns");
+ if (obj) p->columns = fz_toint(obj);
+
+ obj = fz_dictgets(params, "Colors");
+ if (obj) p->colors = fz_toint(obj);
+
+ obj = fz_dictgets(params, "BitsPerComponent");
+ if (obj) p->bpc = fz_toint(obj);
+
+ p->stride = (p->bpc * p->colors * p->columns + 7) / 8;
+ p->bpp = (p->bpc * p->colors + 7) / 8;
+
+ if (p->predictor >= 10) {
+ p->ref = fz_malloc(p->stride);
+ if (!p->ref) { fz_free(p); return fz_outofmem; }
+ memset(p->ref, 0, p->stride);
+ }
+ else {
+ p->ref = nil;
+ }
+
+ return nil;
+}
+
+void
+fz_freepredict(fz_filter *filter)
+{
+ fz_predict *p = (fz_predict*)filter;
+ fz_free(p->ref);
+ fz_free(p);
+}
+
+static inline int
+getcomponent(unsigned char *buf, int x, int bpc)
+{
+ switch (bpc)
+ {
+ case 1: return buf[x / 8] >> (7 - (x % 8)) & 0x01;
+ case 2: return buf[x / 4] >> ((3 - (x % 4)) * 2) & 0x03;
+ case 4: return buf[x / 2] >> ((1 - (x % 2)) * 4) & 0x0f;
+ case 8: return buf[x];
+ }
+ return 0;
+}
+
+static inline void
+putcomponent(unsigned char *buf, int x, int bpc, int value)
+{
+ switch (bpc)
+ {
+ case 1: buf[x / 8] |= value << (7 - (x % 8)); break;
+ case 2: buf[x / 4] |= value << ((3 - (x % 4)) * 2); break;
+ case 4: buf[x / 2] |= value << ((1 - (x % 2)) * 4); break;
+ case 8: buf[x] = value; break;
+ }
+}
+
+static inline int
+paeth(int a, int b, int c)
+{
+ /* The definitions of ac and bc are correct, not a typo. */
+ int ac = b - c, bc = a - c, abcc = ac + bc;
+ int pa = (ac < 0 ? -ac : ac);
+ int pb = (bc < 0 ? -bc : bc);
+ int pc = (abcc < 0 ? -abcc : abcc);
+ return pa <= pb && pa <= pc ? a : pb <= pc ? b : c;
+}
+
+static inline void
+none(fz_predict *p, unsigned char *in, unsigned char *out)
+{
+ memcpy(out, in, p->stride);
+}
+
+static void
+tiff(fz_predict *p, unsigned char *in, unsigned char *out)
+{
+ int left[MAXC];
+ int i, k;
+
+ for (k = 0; k < p->colors; k++)
+ left[k] = 0;
+
+ for (i = 0; i < p->columns; i++) {
+ for (k = 0; k < p->colors; k++) {
+ int a = getcomponent(in, i * p->colors + k, p->bpc);
+ int b = p->encode ? a - left[k] : a + left[k];
+ int c = b % (1 << p->bpc);
+ putcomponent(out, i * p->colors + k, p->bpc, c);
+ left[k] = p->encode ? a : c;
+ }
+ }
+}
+
+static void
+png(fz_predict *p, unsigned char *in, unsigned char *out, int predictor)
+{
+ int upleft[MAXC], left[MAXC], i, k;
+
+ for (k = 0; k < p->bpp; k++) {
+ left[k] = 0;
+ upleft[k] = 0;
+ }
+
+ if (p->encode)
+ {
+ for (k = 0, i = 0; i < p->stride; k = (k + 1) % p->bpp, i ++)
+ {
+ switch (predictor)
+ {
+ case 0: out[i] = in[i]; break;
+ case 1: out[i] = in[i] - left[k]; break;
+ case 2: out[i] = in[i] - p->ref[i]; break;
+ case 3: out[i] = in[i] - (left[k] + p->ref[i]) / 2; break;
+ case 4: out[i] = in[i] - paeth(left[k], p->ref[i], upleft[k]); break;
+ }
+ left[k] = in[i];
+ upleft[k] = p->ref[i];
+ }
+ }
+
+ else
+ {
+ for (k = 0, i = 0; i < p->stride; k = (k + 1) % p->bpp, i ++)
+ {
+ switch (predictor)
+ {
+ case 0: out[i] = in[i]; break;
+ case 1: out[i] = in[i] + left[k]; break;
+ case 2: out[i] = in[i] + p->ref[i]; break;
+ case 3: out[i] = in[i] + (left[k] + p->ref[i]) / 2; break;
+ case 4: out[i] = in[i] + paeth(left[k], p->ref[i], upleft[k]); break;
+ }
+ left[k] = out[i];
+ upleft[k] = p->ref[i];
+ }
+ }
+}
+
+fz_error *
+fz_processpredict(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_predict *dec = (fz_predict*)filter;
+ int ispng = dec->predictor >= 10;
+ int predictor;
+
+ while (1)
+ {
+ if (in->rp + dec->stride + (!dec->encode && ispng) > in->wp) {
+ if (in->eof)
+ return fz_iodone;
+ return fz_ioneedin;
+ }
+
+ if (out->wp + dec->stride + (dec->encode && ispng) > out->ep)
+ return fz_ioneedout;
+
+ if (dec->predictor == 1) {
+ none(dec, in->rp, out->wp);
+ }
+ else if (dec->predictor == 2) {
+ if (dec->bpc != 8)
+ memset(out->wp, 0, dec->stride);
+ tiff(dec, in->rp, out->wp);
+ }
+ else {
+ if (dec->encode) {
+ predictor = dec->predictor - 10;
+ if (predictor < 0 || predictor > 4)
+ predictor = 1;
+ *out->wp ++ = predictor;
+ }
+ else {
+ predictor = *in->rp++;
+ }
+ png(dec, in->rp, out->wp, predictor);
+ }
+
+ if (dec->ref)
+ memcpy(dec->ref, out->wp, dec->stride);
+
+ in->rp += dec->stride;
+ out->wp += dec->stride;
+ }
+}
+
+fz_error *
+fz_newpredictd(fz_filter **fp, fz_obj *params)
+{
+ return fz_newpredict(fp, params, 0);
+}
+
+fz_error *
+fz_newpredicte(fz_filter **fp, fz_obj *params)
+{
+ return fz_newpredict(fp, params, 1);
+}
+
diff --git a/filter/rld.c b/filter/rld.c
new file mode 100644
index 00000000..a66e0f94
--- /dev/null
+++ b/filter/rld.c
@@ -0,0 +1,67 @@
+#include <fitz.h>
+
+fz_error *
+fz_newrld(fz_filter **fp, fz_obj *params)
+{
+ FZ_NEWFILTER(fz_filter, f, rld);
+ return nil;
+}
+
+void
+fz_freerld(fz_filter *rld)
+{
+ fz_free(rld);
+}
+
+fz_error *
+fz_processrld(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ int run, i;
+ unsigned char c;
+
+ while (1)
+ {
+ if (in->rp == in->wp)
+ return fz_ioneedin;
+
+ if (out->wp == out->ep)
+ return fz_ioneedout;
+
+ run = *in->rp++;
+
+ if (run == 128) {
+ out->eof = 1;
+ return fz_iodone;
+ }
+
+ else if (run < 128) {
+ run = run + 1;
+ if (in->rp + run > in->wp) {
+ in->rp --;
+ return fz_ioneedin;
+ }
+ if (out->wp + run > out->ep) {
+ in->rp --;
+ return fz_ioneedout;
+ }
+ for (i = 0; i < run; i++)
+ *out->wp++ = *in->rp++;
+ }
+
+ else if (run > 128) {
+ run = 257 - run;
+ if (in->rp + 1 > in->wp) {
+ in->rp --;
+ return fz_ioneedin;
+ }
+ if (out->wp + run > out->ep) {
+ in->rp --;
+ return fz_ioneedout;
+ }
+ c = *in->rp++;
+ for (i = 0; i < run; i++)
+ *out->wp++ = c;
+ }
+ }
+}
+
diff --git a/filter/rle.c b/filter/rle.c
new file mode 100644
index 00000000..0e611918
--- /dev/null
+++ b/filter/rle.c
@@ -0,0 +1,238 @@
+#include <fitz.h>
+
+/* TODO: rewrite!
+ * make it non-optimal or something,
+ * just not this horrid mess...
+ */
+
+#define noDEBUG
+
+typedef struct fz_rle_s fz_rle;
+
+struct fz_rle_s
+{
+ fz_filter super;
+ int reclen;
+ int curlen;
+ int state;
+ int run;
+ unsigned char buf[128];
+};
+
+enum {
+ ZERO,
+ ONE,
+ DIFF,
+ SAME,
+ END
+};
+
+fz_error *
+fz_newrle(fz_filter **fp, fz_obj *params)
+{
+ FZ_NEWFILTER(fz_rle, enc, rle);
+
+ if (params)
+ enc->reclen = fz_toint(params);
+ else
+ enc->reclen = 0;
+
+ enc->curlen = 0;
+ enc->state = ZERO;
+ enc->run = 0;
+
+ return nil;
+}
+
+void
+fz_freerle(fz_filter *enc)
+{
+ fz_free(enc);
+}
+
+static fz_error *
+putone(fz_rle *enc, fz_buffer *in, fz_buffer *out)
+{
+ if (out->wp + 2 >= out->ep)
+ return fz_ioneedout;
+
+#ifdef DEBUG
+fprintf(stderr, "one '%c'\n", enc->buf[0]);
+#endif
+
+ *out->wp++ = 0;
+ *out->wp++ = enc->buf[0];
+
+ return nil;
+}
+
+static fz_error *
+putsame(fz_rle *enc, fz_buffer *in, fz_buffer *out)
+{
+ if (out->wp + enc->run >= out->ep)
+ return fz_ioneedout;
+
+#ifdef DEBUG
+fprintf(stderr, "same %d x '%c'\n", enc->run, enc->buf[0]);
+#endif
+
+ *out->wp++ = 257 - enc->run;
+ *out->wp++ = enc->buf[0];
+ return nil;
+}
+
+static fz_error *
+putdiff(fz_rle *enc, fz_buffer *in, fz_buffer *out)
+{
+ int i;
+ if (out->wp + enc->run >= out->ep)
+ return fz_ioneedout;
+
+#ifdef DEBUG
+fprintf(stderr, "diff %d\n", enc->run);
+#endif
+
+ *out->wp++ = enc->run - 1;
+ for (i = 0; i < enc->run; i++)
+ *out->wp++ = enc->buf[i];
+ return nil;
+}
+
+static fz_error *
+puteod(fz_rle *enc, fz_buffer *in, fz_buffer *out)
+{
+ if (out->wp + 1 >= out->ep)
+ return fz_ioneedout;
+
+#ifdef DEBUG
+fprintf(stderr, "eod\n");
+#endif
+
+ *out->wp++ = 128;
+ return nil;
+}
+
+static fz_error *
+savebuf(fz_rle *enc, fz_buffer *in, fz_buffer *out)
+{
+ switch (enc->state)
+ {
+ case ZERO: return nil;
+ case ONE: return putone(enc, in, out);
+ case SAME: return putsame(enc, in, out);
+ case DIFF: return putdiff(enc, in, out);
+ case END: return puteod(enc, in, out);
+ default: assert(!"invalid state in rle"); return nil;
+ }
+}
+
+fz_error *
+fz_processrle(fz_filter *filter, fz_buffer *in, fz_buffer *out)
+{
+ fz_rle *enc = (fz_rle*)filter;
+ fz_error *err;
+ unsigned char c;
+
+ while (1)
+ {
+
+ if (enc->reclen && enc->curlen == enc->reclen) {
+ err = savebuf(enc, in, out);
+ if (err) return err;
+#ifdef DEBUG
+fprintf(stderr, "--record--\n");
+#endif
+ enc->state = ZERO;
+ enc->curlen = 0;
+ }
+
+ if (in->rp == in->wp) {
+ if (in->eof) {
+ if (enc->state != END) {
+ err = savebuf(enc, in, out);
+ if (err) return err;
+ }
+ enc->state = END;
+ }
+ else
+ return fz_ioneedin;
+ }
+
+ c = *in->rp;
+
+ switch (enc->state)
+ {
+ case ZERO:
+ enc->state = ONE;
+ enc->run = 1;
+ enc->buf[0] = c;
+ break;
+
+ case ONE:
+ enc->state = DIFF;
+ enc->run = 2;
+ enc->buf[1] = c;
+ break;
+
+ case DIFF:
+ /* out of space */
+ if (enc->run == 128) {
+ err = putdiff(enc, in, out);
+ if (err) return err;
+
+ enc->state = ONE;
+ enc->run = 1;
+ enc->buf[0] = c;
+ }
+
+ /* run of three that are the same */
+ else if ((enc->run > 1) &&
+ (c == enc->buf[enc->run - 1]) &&
+ (c == enc->buf[enc->run - 2]))
+ {
+ if (enc->run >= 3) {
+ enc->run -= 2; /* skip prev two for diff run */
+ err = putdiff(enc, in, out);
+ if (err) return err;
+ }
+
+ enc->state = SAME;
+ enc->run = 3;
+ enc->buf[0] = c;
+ }
+
+ /* keep on collecting */
+ else {
+ enc->buf[enc->run++] = c;
+ }
+ break;
+
+ case SAME:
+ if (enc->run == 128 || c != enc->buf[0]) {
+ err = putsame(enc, in, out);
+ if (err) return err;
+
+ enc->state = ONE;
+ enc->run = 1;
+ enc->buf[0] = c;
+ }
+ else {
+ enc->run ++;
+ }
+ break;
+
+ case END:
+ err = puteod(enc, in, out);
+ if (err) return err;
+
+ out->eof = 1;
+ return fz_iodone;
+ }
+
+ in->rp ++;
+
+ enc->curlen ++;
+
+ }
+}
+