diff options
author | Tor Andersson <tor@ghostscript.com> | 2005-03-30 08:30:22 +0200 |
---|---|---|
committer | Tor Andersson <tor@ghostscript.com> | 2005-03-30 08:30:22 +0200 |
commit | ee154f16bd09a43359967f7e7b86c3677c09461d (patch) | |
tree | 08896cfa9ff55e05bfe7855965c620d45115d4d5 /stream | |
parent | 460ad7040d67a4a93a153f98095ff952a2b15d37 (diff) | |
download | mupdf-ee154f16bd09a43359967f7e7b86c3677c09461d.tar.xz |
rename part 1 -- files
Diffstat (limited to 'stream')
37 files changed, 7686 insertions, 0 deletions
diff --git a/stream/crypt_arc4.c b/stream/crypt_arc4.c new file mode 100644 index 00000000..86c9afd8 --- /dev/null +++ b/stream/crypt_arc4.c @@ -0,0 +1,95 @@ +/* This code illustrates a sample implementation + * of the Arcfour algorithm + * Copyright (c) April 29, 1997 Kalle Kaukonen. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that this copyright + * notice and disclaimer are retained. + * + * THIS SOFTWARE IS PROVIDED BY KALLE KAUKONEN AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KALLE + * KAUKONEN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <fitz.h> + +void +fz_arc4init(fz_arc4 *arc4, unsigned char *key, unsigned keylen) +{ + unsigned int t, u; + unsigned int keyindex; + unsigned int stateindex; + unsigned char *state; + unsigned int counter; + + state = arc4->state; + + arc4->x = 0; + arc4->y = 0; + + for (counter = 0; counter < 256; counter++) { + state[counter] = counter; + } + + keyindex = 0; + stateindex = 0; + + for (counter = 0; counter < 256; counter++) { + t = state[counter]; + stateindex = (stateindex + key[keyindex] + t) & 0xff; + u = state[stateindex]; + + state[stateindex] = t; + state[counter] = u; + + if (++keyindex >= keylen) { + keyindex = 0; + } + } +} + +unsigned char +fz_arc4next(fz_arc4 *arc4) +{ + unsigned int x; + unsigned int y; + unsigned int sx, sy; + unsigned char *state; + + state = arc4->state; + + x = (arc4->x + 1) & 0xff; + sx = state[x]; + y = (sx + arc4->y) & 0xff; + sy = state[y]; + + arc4->x = x; + arc4->y = y; + + state[y] = sx; + state[x] = sy; + + return state[(sx + sy) & 0xff]; +} + +void +fz_arc4encrypt(fz_arc4 *arc4, unsigned char *dest, unsigned char *src, unsigned len) +{ + unsigned int i; + for (i = 0; i < len; i++) { + unsigned char x; + x = fz_arc4next(arc4); + dest[i] = src[i] ^ x; + } +} + diff --git a/stream/crypt_md5.c b/stream/crypt_md5.c new file mode 100644 index 00000000..03601e5b --- /dev/null +++ b/stream/crypt_md5.c @@ -0,0 +1,269 @@ +/* +MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + +Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. +All rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. +*/ + +#include <fitz.h> + +/* Constants for MD5Transform routine */ +enum +{ + S11 = 7, S12 = 12, S13 = 17, S14 = 22, + S21 = 5, S22 = 9, S23 = 14, S24 = 20, + S31 = 4, S32 = 11, S33 = 16, S34 = 23, + S41 = 6, S42 = 10, S43 = 15, S44 = 21 +}; + +static void encode(unsigned char *, unsigned long *, unsigned); +static void decode(unsigned long *, unsigned char *, unsigned); +static void transform(unsigned long state[4], unsigned char block[64]); + +static unsigned char padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE rotates x left n bits */ +#define ROTATE(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. + * Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (unsigned long)(ac); \ + (a) = ROTATE ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (unsigned long)(ac); \ + (a) = ROTATE ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (unsigned long)(ac); \ + (a) = ROTATE ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (unsigned long)(ac); \ + (a) = ROTATE ((a), (s)); \ + (a) += (b); \ + } + +static void encode(unsigned char *output, unsigned long *input, unsigned len) +{ + unsigned i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +static void decode(unsigned long *output, unsigned char *input, unsigned len) +{ + unsigned i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[i] = ((unsigned long)input[j]) | + (((unsigned long)input[j+1]) << 8) | + (((unsigned long)input[j+2]) << 16) | + (((unsigned long)input[j+3]) << 24); + } +} + +static void transform(unsigned long state[4], unsigned char block[64]) +{ + unsigned long a = state[0]; + unsigned long b = state[1]; + unsigned long c = state[2]; + unsigned long d = state[3]; + unsigned long x[16]; + + decode(x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information */ + memset(x, 0, sizeof (x)); +} + +/* MD5 initialization. Begins an MD5 operation, writing a new context. */ +void fz_md5init(fz_md5 *context) +{ + context->count[0] = context->count[1] = 0; + + /* Load magic initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest operation, + * processing another message block, and updating the context. + */ +void fz_md5update(fz_md5 *context, unsigned char *input, unsigned inlen) +{ + unsigned i, index, partlen; + + /* Compute number of bytes mod 64 */ + index = (unsigned)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + context->count[0] += (unsigned long) inlen << 3; + if (context->count[0] < (unsigned long) inlen << 3) + context->count[1] ++; + context->count[1] += (unsigned long) inlen >> 29; + + partlen = 64 - index; + + /* Transform as many times as possible. */ + if (inlen >= partlen) { + memcpy(context->buffer + index, input, partlen); + transform(context->state, context->buffer); + + for (i = partlen; i + 63 < inlen; i += 64) + transform(context->state, input + i); + + index = 0; + } + else { + i = 0; + } + + /* Buffer remaining input */ + memcpy(context->buffer + index, input + i, inlen - i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + * the message digest and zeroizing the context. + */ +void fz_md5final(fz_md5 *context, unsigned char digest[16]) +{ + unsigned char bits[8]; + unsigned index, padlen; + + /* Save number of bits */ + encode(bits, context->count, 8); + + /* Pad out to 56 mod 64 */ + index = (unsigned)((context->count[0] >> 3) & 0x3f); + padlen = index < 56 ? 56 - index : 120 - index; + fz_md5update(context, padding, padlen); + + /* Append length (before padding) */ + fz_md5update(context, bits, 8); + + /* Store state in digest */ + encode(digest, context->state, 16); + + /* Zeroize sensitive information */ + memset(context, 0, sizeof(fz_md5)); +} + diff --git a/stream/filt_a85d.c b/stream/filt_a85d.c new file mode 100644 index 00000000..5b2e105e --- /dev/null +++ b/stream/filt_a85d.c @@ -0,0 +1,128 @@ +#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_dropa85d(fz_filter *f) +{ +} + +fz_error * +fz_processa85d(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_a85d *f = (fz_a85d*)filter; + int c; + + while (1) + { + if (in->rp == in->wp) + return fz_ioneedin; + + c = *in->rp++; + + if (c >= '!' && c <= 'u') { + if (f->count == 4) { + if (out->wp + 4 > out->ep) { + in->rp --; + return fz_ioneedout; + } + + f->word = f->word * 85 + (c - '!'); + + *out->wp++ = (f->word >> 24) & 0xff; + *out->wp++ = (f->word >> 16) & 0xff; + *out->wp++ = (f->word >> 8) & 0xff; + *out->wp++ = (f->word) & 0xff; + + f->word = 0; + f->count = 0; + } + else { + f->word = f->word * 85 + (c - '!'); + f->count ++; + } + } + + else if (c == 'z' && f->count == 0) { + if (out->wp + 4 > out->ep) { + in->rp --; + return fz_ioneedout; + } + *out->wp++ = 0; + *out->wp++ = 0; + *out->wp++ = 0; + *out->wp++ = 0; + } + + else if (c == '~') { + if (in->rp == in->wp) { + in->rp --; + return fz_ioneedin; + } + + c = *in->rp++; + + if (c != '>') { + return fz_throw("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/stream/filt_a85e.c b/stream/filt_a85e.c new file mode 100644 index 00000000..d9cd22c6 --- /dev/null +++ b/stream/filt_a85e.c @@ -0,0 +1,127 @@ +#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_dropa85e(fz_filter *f) +{ +} + +fz_error * +fz_processa85e(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_a85e *f = (fz_a85e*)filter; + unsigned long word; + int count; + int n; + + n = 0; + + while (1) + { + if (f->c >= 70) { + if (out->wp + 1 > out->ep) + return fz_ioneedout; + *out->wp++ = '\n'; + f->c = 0; + n ++; + } + + if (in->rp + 4 <= in->wp) + { + word = (in->rp[0] << 24) | + (in->rp[1] << 16) | + (in->rp[2] << 8) | + (in->rp[3]); + if (word == 0) { + if (out->wp + 1 > out->ep) + return fz_ioneedout; + *out->wp++ = 'z'; + f->c ++; + n ++; + } + else { + unsigned long v1, v2, v3, v4; + + if (out->wp + 5 > out->ep) + return fz_ioneedout; + + v4 = word / 85; + v3 = v4 / 85; + v2 = v3 / 85; + v1 = v2 / 85; + + *out->wp++ = (v1 % 85) + '!'; + *out->wp++ = (v2 % 85) + '!'; + *out->wp++ = (v3 % 85) + '!'; + *out->wp++ = (v4 % 85) + '!'; + *out->wp++ = (word % 85) + '!'; + f->c += 5; + n += 5; + } + in->rp += 4; + } + + else if (in->eof) + { + unsigned long divisor; + + if (in->rp == in->wp) + goto needinput; /* handle clean eof here */ + + count = in->wp - in->rp; + + if (out->wp + count + 3 > out->ep) + return fz_ioneedout; + + word = 0; + switch (count) { + case 3: word |= in->rp[2] << 8; + case 2: word |= in->rp[1] << 16; + case 1: word |= in->rp[0] << 24; + } + in->rp += count; + + divisor = 85L * 85 * 85 * 85; + while (count-- >= 0) { + *out->wp++ = ((word / divisor) % 85) + '!'; + divisor /= 85; + } + + *out->wp++ = '~'; + *out->wp++ = '>'; + 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/stream/filt_ahxd.c b/stream/filt_ahxd.c new file mode 100644 index 00000000..d0e6d7f5 --- /dev/null +++ b/stream/filt_ahxd.c @@ -0,0 +1,112 @@ +#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_dropahxd(fz_filter *f) +{ +} + +fz_error * +fz_processahxd(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_ahxd *f = (fz_ahxd*)filter; + int b, c; + + while (1) + { + if (in->rp == in->wp) + return fz_ioneedin; + + if (out->wp == out->ep) + return fz_ioneedout; + + c = *in->rp++; + + if (ishex(c)) { + if (!f->odd) { + f->a = fromhex(c); + f->odd = 1; + } + else { + b = fromhex(c); + *out->wp++ = (f->a << 4) | b; + f->odd = 0; + } + } + + else if (c == '>') { + if (f->odd) + *out->wp++ = (f->a << 4); + 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/stream/filt_ahxe.c b/stream/filt_ahxe.c new file mode 100644 index 00000000..c2b882a6 --- /dev/null +++ b/stream/filt_ahxe.c @@ -0,0 +1,64 @@ +#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_dropahxe(fz_filter *f) +{ +} + +fz_error * +fz_processahxe(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_ahxe *f = (fz_ahxe*)filter; + int a, b, c; + + while (1) + { + if (in->rp == in->wp) + goto needinput; + + if (out->wp + 2 >= out->ep) /* can write 3 bytes from 1 */ + return fz_ioneedout; + + c = *in->rp++; + a = tohex[(c >> 4) & 0x0f]; + b = tohex[c & 0x0f]; + + *out->wp++ = a; + *out->wp++ = b; + + f->c += 2; + if (f->c == 60) { + *out->wp++ = '\n'; + f->c = 0; + } + } + +needinput: + if (in->eof) { + if (out->wp == out->ep) + return fz_ioneedout; + *out->wp++ = '>'; + out->eof = 1; + return fz_iodone; + } + return fz_ioneedin; +} + diff --git a/stream/filt_arc4.c b/stream/filt_arc4.c new file mode 100644 index 00000000..b025368c --- /dev/null +++ b/stream/filt_arc4.c @@ -0,0 +1,46 @@ +#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_droparc4filter(fz_filter *f) +{ +} + +fz_error * +fz_processarc4filter(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_arc4c *f = (fz_arc4c*)filter; + int n; + + while (1) + { + if (in->rp + 1 > in->wp) { + if (in->eof) + return fz_iodone; + return fz_ioneedin; + } + if (out->wp + 1 > out->ep) + return fz_ioneedout; + + n = MIN(in->wp - in->rp, out->ep - out->wp); + fz_arc4encrypt(&f->arc4, out->wp, in->rp, n); + in->rp += n; + out->wp += n; + } +} + diff --git a/stream/filt_buffer.c b/stream/filt_buffer.c new file mode 100644 index 00000000..206137a6 --- /dev/null +++ b/stream/filt_buffer.c @@ -0,0 +1,93 @@ +#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->refs = 1; + b->ownsdata = 1; + 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; +} + +fz_error * +fz_newbufferwithdata(fz_buffer **bp, unsigned char *data, int size) +{ + fz_buffer *b; + + b = *bp = fz_malloc(sizeof(fz_buffer)); + if (!b) return fz_outofmem; + + b->refs = 1; + b->ownsdata = 0; + b->bp = data; + + b->rp = b->bp; + b->wp = b->bp + size; + b->ep = b->bp + size; + b->eof = 0; + + return nil; +} + +fz_buffer * +fz_keepbuffer(fz_buffer *buf) +{ + buf->refs ++; + return buf; +} + +void +fz_dropbuffer(fz_buffer *buf) +{ + if (--buf->refs == 0) + { + if (buf->ownsdata) + fz_free(buf->bp); + fz_free(buf); + } +} + +fz_error * +fz_growbuffer(fz_buffer *buf) +{ + unsigned char *newbp; + + int rp = buf->rp - buf->bp; + int wp = buf->wp - buf->bp; + int ep = buf->ep - buf->bp; + + assert(buf->ownsdata); + + 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) +{ + assert(buf->ownsdata); + 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/stream/filt_dctc.h b/stream/filt_dctc.h new file mode 100644 index 00000000..8aa6aeb7 --- /dev/null +++ b/stream/filt_dctc.h @@ -0,0 +1,39 @@ +/* + * Extend libjpegs error handler to use setjmp/longjmp + */ + +#include <jpeglib.h> + +#include <setjmp.h> + +struct myerrmgr +{ + struct jpeg_error_mgr super; + jmp_buf jb; + char msg[JMSG_LENGTH_MAX]; +}; + +static void myerrexit(j_common_ptr cinfo) +{ + struct myerrmgr *err = (struct myerrmgr *)cinfo->err; + char msgbuf[JMSG_LENGTH_MAX]; + err->super.format_message(cinfo, msgbuf); + strlcpy(err->msg, msgbuf, sizeof err->msg); + longjmp(err->jb, 1); +} + +static void myoutmess(j_common_ptr cinfo) +{ + struct myerrmgr *err = (struct myerrmgr *)cinfo->err; + char msgbuf[JMSG_LENGTH_MAX]; + err->super.format_message(cinfo, msgbuf); + 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/stream/filt_dctd.c b/stream/filt_dctd.c new file mode 100644 index 00000000..750f7893 --- /dev/null +++ b/stream/filt_dctd.c @@ -0,0 +1,219 @@ +#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; + + /* speed up jpeg decoding a bit */ + d->cinfo.dct_method = JDCT_FASTEST; + d->cinfo.do_fancy_upsampling = FALSE; + + return nil; +} + +void +fz_dropdctd(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/stream/filt_dcte.c b/stream/filt_dcte.c new file mode 100644 index 00000000..547721b4 --- /dev/null +++ b/stream/filt_dcte.c @@ -0,0 +1,252 @@ +#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_dropdcte(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); +} + +/* 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/stream/filt_faxc.h b/stream/filt_faxc.h new file mode 100644 index 00000000..14cae13f --- /dev/null +++ b/stream/filt_faxc.h @@ -0,0 +1,125 @@ +/* common bit magic */ + +static inline void +printbits(FILE *f, int code, int nbits) +{ + int n, b; + for (n = nbits - 1; n >= 0; n--) + { + b = (code >> n) & 1; + fprintf(f, "%c", b ? '1' : '0'); + } +} + +static inline int +getbit(const unsigned char *buf, int x) +{ + return ( buf[x >> 3] >> ( 7 - (x & 7) ) ) & 1; +} + +static inline void +printline(FILE *f, unsigned char *line, int w) +{ + int i; + for (i = 0; i < w; i++) + fprintf(f, "%c", getbit(line, i) ? '#' : '.'); + fprintf(f, "\n"); +} + +static inline int +getrun(const unsigned char *line, int x, int w, int c) +{ + int z; + int b; + + if (x < 0) + x = 0; + + z = x; + while (z < w) + { + b = getbit(line, z); + if (c != b) + break; + z ++; + } + + return z - x; +} + +static inline int +findchanging(const unsigned char *line, int x, int w) +{ + int a, b; + + if (line == 0) + return w; + + if (x == -1) + { + a = 0; + x = 0; + } + else + { + a = getbit(line, x); + x++; + } + + while (x < w) + { + b = getbit(line, x); + if (a != b) + break; + x++; + } + + return x; +} + +static inline int +findchangingcolor(const unsigned char *line, int x, int w, int color) +{ + if (line == 0) + return w; + + x = findchanging(line, x, w); + + if (x < w && getbit(line, x) != color) + x = findchanging(line, x, w); + + return x; +} + +static const unsigned char lm[8] = + { 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01 }; + +static const unsigned char rm[8] = + { 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE }; + +static inline void +setbits(unsigned char *line, int x0, int x1) +{ + int a0, a1, b0, b1, a; + + a0 = x0 >> 3; + a1 = x1 >> 3; + + b0 = x0 & 7; + b1 = x1 & 7; + + if (a0 == a1) + { + if (b1) + line[a0] |= lm[b0] & rm[b1]; + } + else + { + line[a0] |= lm[b0]; + for (a = a0 + 1; a < a1; a++) + line[a] = 0xFF; + if (b1) + line[a1] |= rm[b1]; + } +} + diff --git a/stream/filt_faxd.c b/stream/filt_faxd.c new file mode 100644 index 00000000..d8686a08 --- /dev/null +++ b/stream/filt_faxd.c @@ -0,0 +1,441 @@ +#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) */ +}; + +/* 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); + + return nil; +} + +void +fz_dropfaxd(fz_filter *p) +{ + fz_faxd *fax = (fz_faxd*) p; + fz_free(fax->ref); + fz_free(fax->dst); +} + +static inline void eatbits(fz_faxd *fax, int nbits) +{ + fax->word <<= nbits; + fax->bidx += nbits; +} + +static inline fz_error * fillbits(fz_faxd *fax, fz_buffer *in) +{ + while (fax->bidx >= 8) + { + if (in->rp + 1 > in->wp) + return fz_ioneedin; + fax->bidx -= 8; + fax->word |= *in->rp << fax->bidx; + in->rp ++; + } + return 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); + + 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); + + 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; + break; + + case P: + b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c); + b2 = findchanging(fax->ref, b1, fax->columns); + if (fax->c) setbits(fax->dst, fax->a, b2); + fax->a = b2; + break; + + case V0: + b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c); + if (fax->c) setbits(fax->dst, fax->a, b1); + fax->a = b1; + fax->c = !fax->c; + break; + + case VR1: + b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c); + if (fax->c) setbits(fax->dst, fax->a, b1 + 1); + fax->a = b1 + 1; + fax->c = !fax->c; + break; + + case VR2: + b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c); + if (fax->c) setbits(fax->dst, fax->a, b1 + 2); + fax->a = b1 + 2; + fax->c = !fax->c; + break; + + case VR3: + b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c); + if (fax->c) setbits(fax->dst, fax->a, b1 + 3); + fax->a = b1 + 3; + fax->c = !fax->c; + break; + + case VL1: + b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c); + if (fax->c) setbits(fax->dst, fax->a, b1 - 1); + fax->a = b1 - 1; + fax->c = !fax->c; + break; + + case VL2: + b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c); + if (fax->c) setbits(fax->dst, fax->a, b1 - 2); + fax->a = b1 - 2; + fax->c = !fax->c; + break; + + case VL3: + b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c); + if (fax->c) setbits(fax->dst, fax->a, b1 - 3); + fax->a = b1 - 3; + fax->c = !fax->c; + break; + + case UNCOMPRESSED: + return fz_throw("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 *error; + 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) + { + 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); + } + } + else if (fax->dim == 1) + { + fax->eolc = 0; + error = dec1d(fax); + if (error) return error; + + } + else if (fax->dim == 2) + { + fax->eolc = 0; + error = dec2d(fax); + if (error) return error; + } + + /* no eol check after makeup codes nor in the middle of an H code */ + if (fax->stage == SMAKEUP || fax->stage == SH1 || fax->stage == SH2) + goto loop; + + /* check for eol conditions */ + if (fax->eolc || fax->a >= fax->columns) + { + if (fax->a > 0) + goto eol; + if (fax->eolc == (fax->k < 0 ? 2 : 6)) + goto rtc; + } + + goto loop; + +eol: + fax->stage = SEOL; + if (out->wp + fax->stride > out->ep) + return fz_ioneedout; + + if (fax->blackis1) + memcpy(out->wp, fax->dst, fax->stride); + else + 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; + } + + /* if endofline & encodedbytealign, EOLs are *not* optional */ + if (fax->encodedbytealign) + { + if (fax->endofline) + eatbits(fax, (12 - fax->bidx) & 7); + else + eatbits(fax, (8 - fax->bidx) & 7); + } + + goto loop; + +rtc: + i = (32 - fax->bidx) / 8; + while (i-- && in->rp > in->bp) + in->rp --; + + out->eof = 1; + return fz_iodone; +} + diff --git a/stream/filt_faxd.h b/stream/filt_faxd.h new file mode 100644 index 00000000..9f3fb470 --- /dev/null +++ b/stream/filt_faxd.h @@ -0,0 +1,61 @@ +/* Fax G3/G4 tables */ + +/* +<raph> the first 2^(initialbits) entries map bit patterns to decodes +<raph> let's say initial_bits is 8 for the sake of example +<raph> and that the code is 1001 +<raph> that means that entries 0x90 .. 0x9f have the entry { val, 4 } +<raph> because those are all the bytes that start with the code +<raph> and the 4 is the length of the code +... if (n_bits > initial_bits) ... +<raph> anyway, in that case, it basically points to a mini table +<raph> the n_bits is the maximum length of all codes beginning with that byte +<raph> so 2^(n_bits - initial_bits) is the size of the mini-table +<raph> peter came up with this, and it makes sense +*/ + +typedef struct cfd_node_s cfd_node; + +struct cfd_node_s +{ + short val; + short nbits; +}; + +enum +{ + cfd_white_initial_bits = 8, + cfd_black_initial_bits = 7, + cfd_2d_initial_bits = 7, + cfd_uncompressed_initial_bits = 6 /* must be 6 */ +}; + +/* non-run codes in tables */ +enum +{ + ERROR = -1, + ZEROS = -2, /* EOL follows, possibly with more padding first */ + UNCOMPRESSED = -3 +}; + +/* semantic codes for cf_2d_decode */ +enum +{ + P = -4, + H = -5, + VR3 = 0, + VR2 = 1, + VR1 = 2, + V0 = 3, + VL1 = 4, + VL2 = 5, + VL3 = 6 +}; + +/* Decoding tables */ + +extern const cfd_node cf_white_decode[]; +extern const cfd_node cf_black_decode[]; +extern const cfd_node cf_2d_decode[]; +extern const cfd_node cf_uncompressed_decode[]; + diff --git a/stream/filt_faxdtab.c b/stream/filt_faxdtab.c new file mode 100644 index 00000000..8e387d03 --- /dev/null +++ b/stream/filt_faxdtab.c @@ -0,0 +1,931 @@ +/* Tables for CCITTFaxDecode filter. */ + +/* This file was generated automatically. It is governed by the same terms */ +/* as the files scfetab.c and scfdgen.c from which it was derived. */ +/* Consult those files for the licensing terms and conditions. */ + +#include "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/stream/filt_faxe.c b/stream/filt_faxe.c new file mode 100644 index 00000000..e96c5ace --- /dev/null +++ b/stream/filt_faxe.c @@ -0,0 +1,398 @@ +#include <fitz.h> + +#include "faxe.h" +#include "faxc.h" + +/* TODO: honor Rows param */ + +typedef struct fz_faxe_s fz_faxe; + +struct fz_faxe_s +{ + fz_filter super; + + int k; + int endofline; + int encodedbytealign; + int columns; + int endofblock; + int blackis1; + + int stride; + int ridx; /* how many rows in total */ + int bidx; /* how many bits are already used in out->wp */ + unsigned char bsave; /* partial byte saved between process() calls */ + + int stage; + int a0, c; /* mid-line coding state */ + + unsigned char *ref; + unsigned char *src; +}; + +fz_error * +fz_newfaxe(fz_filter **fp, fz_obj *params) +{ + fz_obj *obj; + + FZ_NEWFILTER(fz_faxe, fax, faxe); + + fax->ref = nil; + fax->src = nil; + + fax->k = 0; + fax->endofline = 0; + fax->encodedbytealign = 0; + fax->columns = 1728; + fax->endofblock = 1; + fax->blackis1 = 0; + + obj = fz_dictgets(params, "K"); + if (obj) fax->k = fz_toint(obj); + + obj = fz_dictgets(params, "EndOfLine"); + if (obj) fax->endofline = fz_tobool(obj); + + obj = fz_dictgets(params, "EncodedByteAlign"); + if (obj) fax->encodedbytealign = fz_tobool(obj); + + obj = fz_dictgets(params, "Columns"); + if (obj) fax->columns = fz_toint(obj); + + obj = fz_dictgets(params, "EndOfBlock"); + if (obj) fax->endofblock = fz_tobool(obj); + + obj = fz_dictgets(params, "BlackIs1"); + if (obj) fax->blackis1 = fz_tobool(obj); + + fax->stride = ((fax->columns - 1) >> 3) + 1; + fax->bidx = 0; + fax->ridx = 0; + + fax->stage = 0; + fax->a0 = -1; + fax->c = 0; + + fax->ref = fz_malloc(fax->stride); + if (!fax->ref) { fz_free(fax); return fz_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_dropfaxe(fz_filter *p) +{ + fz_faxe *fax = (fz_faxe*) p; + fz_free(fax->src); + fz_free(fax->ref); +} + +enum { codebytes = 2 }; + +static inline int runbytes(int run) +{ + int m = (run / 64) / 40 + 1; /* number of makeup codes */ + return codebytes * (m + 1); /* bytes for makeup + term codes */ +} + +static void +putbits(fz_faxe *fax, fz_buffer *out, int code, int nbits) +{ + while (nbits > 0) + { + if (fax->bidx == 0) + *out->wp = 0; + + /* code does not fit: shift right */ + if (nbits > (8 - fax->bidx)) + { + *out->wp |= code >> (nbits - (8 - fax->bidx)); + nbits = nbits - (8 - fax->bidx); + fax->bidx = 0; + out->wp ++; + } + + /* shift left */ + else + { + *out->wp |= code << ((8 - fax->bidx) - nbits); + fax->bidx += nbits; + if (fax->bidx == 8) + { + fax->bidx = 0; + out->wp ++; + } + nbits = 0; + } + } +} + +static inline void +putcode(fz_faxe *fax, fz_buffer *out, const cfe_code *run) +{ + putbits(fax, out, run->code, run->nbits); +} + +static void +putrun(fz_faxe *fax, fz_buffer *out, int run, int c) +{ + int m; + + const cf_runs *codetable = c ? &cf_black_runs : &cf_white_runs; + + if (run > 63) + { + m = run / 64; + while (m > 40) + { + putcode(fax, out, &codetable->makeup[40]); + m -= 40; + } + if (m > 0) + { + putcode(fax, out, &codetable->makeup[m]); + } + putcode(fax, out, &codetable->termination[run % 64]); + } + else + { + putcode(fax, out, &codetable->termination[run]); + } +} + +static fz_error * +enc1d(fz_faxe *fax, unsigned char *line, fz_buffer *out) +{ + int run; + + if (fax->a0 < 0) + fax->a0 = 0; + + while (fax->a0 < fax->columns) + { + run = getrun(line, fax->a0, fax->columns, fax->c); + + if (out->wp + 1 + runbytes(run) > out->ep) + return fz_ioneedout; + + putrun(fax, out, run, fax->c); + + fax->a0 += run; + fax->c = !fax->c; + } + + return 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; + + while (fax->a0 < fax->columns) + { + a1 = findchanging(src, fax->a0, fax->columns); + b1 = findchangingcolor(ref, fax->a0, fax->columns, !fax->c); + b2 = findchanging(ref, b1, fax->columns); + + /* pass */ + if (b2 < a1) + { + if (out->wp + 1 + codebytes > out->ep) + return fz_ioneedout; + + putcode(fax, out, &cf2_run_pass); + + fax->a0 = b2; + } + + /* vertical */ + else if (ABS(b1 - a1) <= 3) + { + if (out->wp + 1 + codebytes > out->ep) + return fz_ioneedout; + + putcode(fax, out, &cf2_run_vertical[b1 - a1 + 3]); + + fax->a0 = a1; + fax->c = !fax->c; + } + + /* horizontal */ + else + { + a2 = findchanging(src, a1, fax->columns); + run1 = a1 - fax->a0; + run2 = a2 - a1; + n = codebytes + runbytes(run1) + runbytes(run2); + + if (out->wp + 1 + n > out->ep) + return fz_ioneedout; + + putcode(fax, out, &cf2_run_horizontal); + putrun(fax, out, run1, fax->c); + putrun(fax, out, run2, !fax->c); + + fax->a0 = a2; + } + } + + return 0; +} + +static fz_error * +process(fz_faxe *fax, fz_buffer *in, fz_buffer *out) +{ + fz_error *error; + int i, n; + + while (1) + { + if (in->rp == in->wp && in->eof) + goto rtc; + + switch (fax->stage) + { + case 0: + if (fax->encodedbytealign) + { + if (fax->endofline) + { + if (out->wp + 1 + 1 > out->ep) + return fz_ioneedout; + + /* make sure that EOL ends on a byte border */ + putbits(fax, out, 0, (12 - fax->bidx) & 7); + } + else + { + if (fax->bidx) + { + if (out->wp + 1 > out->ep) + return fz_ioneedout; + fax->bidx = 0; + out->wp ++; + } + } + } + + fax->stage ++; + + case 1: + if (fax->endofline) + { + if (out->wp + 1 + codebytes + 1 > out->ep) + return fz_ioneedout; + + if (fax->k > 0) + { + if (fax->ridx % fax->k == 0) + putcode(fax, out, &cf2_run_eol_1d); + else + putcode(fax, out, &cf2_run_eol_2d); + } + else + { + putcode(fax, out, &cf_run_eol); + } + } + + fax->stage ++; + + case 2: + if (in->rp + fax->stride > in->wp) + { + if (in->eof) /* XXX barf here? */ + goto rtc; + return fz_ioneedin; + } + + memcpy(fax->ref, fax->src, fax->stride); + memcpy(fax->src, in->rp, fax->stride); + + if (!fax->blackis1) + { + for (i = 0; i < fax->stride; i++) + fax->src[i] = ~fax->src[i]; + } + + in->rp += fax->stride; + + fax->c = 0; + fax->a0 = -1; + + fax->stage ++; + + case 3: + error = 0; /* to silence compiler */ + + if (fax->k < 0) + error = enc2d(fax, fax->ref, fax->src, out); + + else if (fax->k == 0) + error = enc1d(fax, fax->src, out); + + else if (fax->k > 0) + { + if (fax->ridx % fax->k == 0) + error = enc1d(fax, fax->src, out); + else + error = enc2d(fax, fax->ref, fax->src, out); + } + + if (error) + return error; + + fax->ridx ++; + + fax->stage = 0; + } + } + +rtc: + if (fax->endofblock) + { + n = fax->k < 0 ? 2 : 6; + + if (out->wp + 1 + codebytes * n > out->ep) + return fz_ioneedout; + + for (i = 0; i < n; i++) + { + putcode(fax, out, &cf_run_eol); + if (fax->k > 0) + putbits(fax, out, 1, 1); + } + } + + if (fax->bidx) + out->wp ++; + 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 *error; + + /* restore partial bits */ + *out->wp = fax->bsave; + + error = process(fax, in, out); + + /* save partial bits */ + fax->bsave = *out->wp; + + return error; +} + diff --git a/stream/filt_faxe.h b/stream/filt_faxe.h new file mode 100644 index 00000000..dd3fc121 --- /dev/null +++ b/stream/filt_faxe.h @@ -0,0 +1,37 @@ +/* Fax G3/G4 tables */ + +typedef struct cfe_code_s cfe_code; + +struct cfe_code_s +{ + unsigned short code; + unsigned short nbits; +}; + +typedef struct cf_runs_s { + cfe_code termination[64]; + cfe_code makeup[41]; +} cf_runs; + +/* Encoding tables */ + +/* Codes common to 1-D and 2-D encoding. */ +extern const cfe_code cf_run_eol; +extern const cf_runs cf_white_runs, cf_black_runs; +extern const cfe_code cf_uncompressed[6]; +extern const cfe_code cf_uncompressed_exit[10]; /* indexed by 2 x length of */ + +/* 1-D encoding. */ +extern const cfe_code cf1_run_uncompressed; + +/* 2-D encoding. */ +enum { cf2_run_vertical_offset = 3 }; +extern const cfe_code cf2_run_pass; +extern const cfe_code cf2_run_vertical[7]; /* indexed by b1 - a1 + offset */ +extern const cfe_code cf2_run_horizontal; +extern const cfe_code cf2_run_uncompressed; + +/* 2-D Group 3 encoding. */ +extern const cfe_code cf2_run_eol_1d; +extern const cfe_code cf2_run_eol_2d; + diff --git a/stream/filt_faxetab.c b/stream/filt_faxetab.c new file mode 100644 index 00000000..409039f4 --- /dev/null +++ b/stream/filt_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/stream/filt_filec.c b/stream/filt_filec.c new file mode 100644 index 00000000..05813185 --- /dev/null +++ b/stream/filt_filec.c @@ -0,0 +1,349 @@ +#include <fitz.h> + +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 realmode; + int fd; + int n; + + assert(mode == FZ_READ || mode == FZ_WRITE || mode == FZ_APPEND); + + realmode = 0; + if (mode == FZ_READ) + realmode = O_BINARY | O_RDONLY; + if (mode == FZ_WRITE) + realmode = O_BINARY | O_WRONLY | O_CREAT | O_TRUNC; + if (mode == FZ_APPEND) + realmode = O_BINARY | O_WRONLY; + + fd = open(path, realmode, 0644); + if (fd == -1) + return fz_throw("ioerror: open '%s': %s", path, strerror(errno)); + + if (mode == FZ_APPEND) + { + mode = FZ_WRITE; + n = lseek(fd, 0, 2); + if (n == -1) { + error = fz_throw("ioerror: lseek: %s", strerror(errno)); + close(fd); + return error; + } + } + + file = *filep = fz_malloc(sizeof(fz_file)); + if (!file) + return fz_outofmem; + + 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: + *filep = nil; + close(fd); + fz_dropbuffer(file->out); + fz_dropbuffer(file->in); + fz_free(file); + return error; +} + +fz_error * +fz_openbuffer(fz_file **filep, fz_buffer *buf, int mode) +{ + fz_error *error; + fz_file *file; + + assert(mode == FZ_READ || mode == FZ_WRITE); + + file = *filep = fz_malloc(sizeof(fz_file)); + if (!file) + return fz_outofmem; + + file->mode = mode; + file->fd = -1; + file->depth = 0; + file->error = nil; + file->filter = nil; + + if (mode == FZ_READ) + { + file->out = fz_keepbuffer(buf); + error = fz_newbuffer(&file->in, FZ_BUFSIZE); + if (error) + goto cleanup; + } + + else + { + error = fz_newbuffer(&file->out, FZ_BUFSIZE); + if (error) + goto cleanup; + file->in = fz_keepbuffer(buf); + } + + return nil; + +cleanup: + *filep = nil; + fz_free(file); + return error; +} + +void +fz_closefile(fz_file *file) +{ + assert(file->depth == 0); + + if (file->mode == FZ_WRITE) + fz_flush(file); + + if (file->error) + { + fz_warn("%s", file->error->msg); + fz_droperror(file->error); + file->error = nil; + } + + if (file->fd != -1) /* open to real file */ + close(file->fd); + + fz_dropbuffer(file->in); + fz_dropbuffer(file->out); + + if (file->filter) + fz_dropfilter(file->filter); + + fz_free(file); +} + +fz_error * +fz_pushfilter(fz_file *file, fz_filter *filter) +{ + fz_error *error; + + /* without a filter, one buffer is ignored: unignore. */ + if (file->depth == 0) + { + fz_buffer *buf; + + buf = file->out; + file->out = file->in; + file->in = buf; + + if (file->mode == FZ_READ) + { + file->out->rp = file->out->bp; + file->out->wp = file->out->bp; + file->out->eof = 0; + } + else + { + file->out->eof = 0; + file->in->rp = file->in->bp; + file->in->wp = file->in->bp; + file->in->eof = 0; + } + + file->filter = fz_keepfilter(filter); + } + + else + { + if (file->mode == FZ_READ) + { + 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; + } + } + + else + { + error = fz_chainpipeline(&file->filter, filter, file->filter, file->in); + if (error) + return error; + + error = fz_newbuffer(&file->in, FZ_BUFSIZE); + if (error) + { + fz_unchainpipeline(file->filter, &file->filter, &file->in); + return error; + } + } + } + + file->depth ++; + + return nil; +} + +void +fz_popfilter(fz_file *file) +{ + fz_buffer *buf; + + assert(file->depth > 0); + + if (file->mode == FZ_WRITE) + fz_flush(file); + + if (file->error) + { + fz_warn("%s", file->error->msg); + fz_droperror(file->error); + file->error = nil; + } + + if (file->depth == 1) + { + fz_dropfilter(file->filter); + file->filter = nil; + + buf = file->out; + file->out = file->in; + file->in = buf; + } + else + { + if (file->mode == FZ_READ) + { + fz_dropbuffer(file->out); + fz_unchainpipeline(file->filter, &file->filter, &file->out); + } + else + { + fz_dropbuffer(file->in); + fz_unchainpipeline(file->filter, &file->filter, &file->in); + } + } + + file->depth --; +} + +int +fz_seek(fz_file *f, int ofs, int whence) +{ + int t; + int c; + + if (f->filter) + { + assert(f->mode == FZ_READ && whence == 0); + + 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 ofs; + } + + if (whence == 1) + { + ofs = fz_tell(f) + ofs; + whence = 0; + } + + if (f->fd == -1) + { + if (whence == 0) + { + if (f->mode == FZ_READ) + f->out->rp = CLAMP(f->out->bp + ofs, f->out->bp, f->in->ep); + else + f->in->wp = CLAMP(f->in->bp + ofs, f->in->bp, f->in->ep); + } + else + { + if (f->mode == FZ_READ) + f->out->rp = CLAMP(f->out->ep + ofs, f->out->bp, f->in->ep); + else + f->in->wp = CLAMP(f->in->ep + ofs, f->in->bp, f->in->ep); + } + return fz_tell(f); + } + + t = lseek(f->fd, ofs, whence); + 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 t; +} + +int +fz_tell(fz_file *f) +{ + int t; + + if (f->filter) + { + assert(f->mode == FZ_READ); + return f->filter->count - (f->out->wp - f->out->rp); + } + + if (f->fd == -1) + { + if (f->mode == FZ_READ) + return f->out->rp - f->out->bp; + else + return f->in->wp - f->in->bp; + } + + t = lseek(f->fd, 0, 1); + if (t == -1) + { + f->error = fz_throw("ioerror: lseek: %s", strerror(errno)); + return -1; + } + + if (f->mode == FZ_READ) + return t - (f->out->wp - f->out->rp); + else + return t + (f->in->wp - f->in->rp); +} + diff --git a/stream/filt_filer.c b/stream/filt_filer.c new file mode 100644 index 00000000..a63a7dd2 --- /dev/null +++ b/stream/filt_filer.c @@ -0,0 +1,252 @@ +#include <fitz.h> + +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; +} + +int fz_producedata(fz_file *f) +{ + fz_error *reason; + int produced; + int n; + + assert(f->mode == FZ_READ); + 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 (fz_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 (fz_producedata(f)) return EOF; + } + + if (f->out->rp < f->out->wp) + return *f->out->rp++; + + return EOF; +} + +int +fz_read(fz_file *f, unsigned 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 (fz_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; +} + +/* + * Utility function to consume contents of file stream into + * a freshly allocated buffer; realloced and trimmed to size. + */ + +enum { CHUNKSIZE = 1024 * 32 }; + +fz_error * +fz_readfile(fz_buffer **bufp, fz_file *file) +{ + fz_buffer *real; + unsigned char *newbuf; + unsigned char *buf; + int len; + int pos; + int n; + + *bufp = nil; + + 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); + + if (n < 0) + { + fz_free(buf); + return fz_ferror(file); + } + + pos += n; + + if (n < CHUNKSIZE) + { + if (pos > 0) + { + newbuf = fz_realloc(buf, pos); + if (!newbuf) + { + fz_free(buf); + return fz_outofmem; + } + } + else newbuf = buf; + + real = *bufp = fz_malloc(sizeof(fz_buffer)); + if (!real) + { + fz_free(newbuf); + return fz_outofmem; + } + + real->refs = 1; + real->ownsdata = 1; + real->bp = buf; + real->rp = buf; + real->wp = buf + pos; + real->ep = buf + pos; + real->eof = 1; + + return nil; + } + } +} + diff --git a/stream/filt_filew.c b/stream/filt_filew.c new file mode 100644 index 00000000..f83399a2 --- /dev/null +++ b/stream/filt_filew.c @@ -0,0 +1,260 @@ +#include <fitz.h> + +int +fz_printstring(fz_file *f, char *s) +{ + return fz_write(f, s, strlen(s)); +} + +int +fz_printobj(fz_file *file, fz_obj *obj, int tight) +{ + char buf[1024]; + char *ptr; + int n; + + n = fz_sprintobj(nil, 0, obj, tight); + if (n < sizeof buf) + { + fz_sprintobj(buf, sizeof buf, obj, tight); + return fz_write(file, buf, n); + } + else + { + ptr = fz_malloc(n); + if (!ptr) { + file->error = fz_outofmem; + return -1; + } + fz_sprintobj(ptr, n, obj, tight); + n = fz_write(file, ptr, n); + fz_free(ptr); + return n; + } +} + +int +fz_print(fz_file *f, char *fmt, ...) +{ + va_list ap; + char buf[1024]; + char *p; + int n; + + va_start(ap, fmt); + n = vsnprintf(buf, sizeof buf, fmt, ap); + va_end(ap); + + if (n < sizeof buf) + return fz_write(f, buf, n); + + p = fz_malloc(n); + if (!p) { + f->error = fz_outofmem; + return -1; + } + + va_start(ap, fmt); + vsnprintf(p, n, fmt, ap); + va_end(ap); + + n = fz_write(f, p, n); + + fz_free(p); + + return n; +} + +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, unsigned char *buf, int n) +{ + fz_error *reason; + int i = 0; + int x; + + assert(f->mode == FZ_WRITE); + assert(f->error == nil); + + if (!f->filter) + { + while (i < n) + { + while (f->in->wp < f->in->ep && i < n) + *f->in->wp++ = buf[i++]; + + if (f->in->wp == f->in->ep) + { + if (f->fd != -1) + { + x = dowrite(f->in, f->fd); + if (x < 0) { + f->error = fz_throw("ioerror in write: %s", strerror(errno)); + return -1; + } + } + + 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; + } + } + + return 0; + } + + while (i < n) + { + while (f->in->wp < f->in->ep && i < n) + *f->in->wp++ = buf[i++]; + + if (f->in->wp == f->in->ep) + { + 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) + { + if (f->fd != -1) + { + 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) + { + if (f->fd != -1) + { + while (f->out->rp < f->out->wp) + { + 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 == FZ_WRITE); + assert(f->error == nil); + + f->in->eof = 1; + + if (!f->filter) + { + if (f->fd != -1) + { + while (f->in->rp < f->in->wp) + { + n = dowrite(f->in, f->fd); + if (n < 0) { + f->error = fz_throw("ioerror in write: %s", strerror(errno)); + return -1; + } + } + } + return 0; + } + + 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) + { + if (f->fd != -1) + { + 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) + { + if (f->fd != -1) + { + 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; +} + diff --git a/stream/filt_flate.c b/stream/filt_flate.c new file mode 100644 index 00000000..c34a49b7 --- /dev/null +++ b/stream/filt_flate.c @@ -0,0 +1,176 @@ +#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_dropflated(fz_filter *f) +{ + z_streamp zp = &((fz_flate*)f)->z; + int err; + + err = inflateEnd(zp); + if (err != Z_OK) + fprintf(stderr, "inflateEnd: %s", zp->msg); +} + +fz_error * +fz_processflated(fz_filter *f, fz_buffer *in, fz_buffer *out) +{ + z_streamp zp = &((fz_flate*)f)->z; + int err; + + if (in->rp == in->wp && !in->eof) + return fz_ioneedin; + if (out->wp == out->ep) + return fz_ioneedout; + + zp->next_in = in->rp; + zp->avail_in = in->wp - in->rp; + + zp->next_out = out->wp; + zp->avail_out = out->ep - out->wp; + + err = inflate(zp, Z_NO_FLUSH); + + in->rp = in->wp - zp->avail_in; + out->wp = out->ep - zp->avail_out; + + if (err == Z_STREAM_END) { + 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_dropflatee(fz_filter *f) +{ + z_streamp zp = &((fz_flate*)f)->z; + int err; + + err = deflateEnd(zp); + if (err != Z_OK) + fprintf(stderr, "deflateEnd: %s", zp->msg); + + fz_free(f); +} + +fz_error * +fz_processflatee(fz_filter *f, fz_buffer *in, fz_buffer *out) +{ + z_streamp zp = &((fz_flate*)f)->z; + int err; + + if (in->rp == in->wp && !in->eof) + return fz_ioneedin; + if (out->wp == out->ep) + return fz_ioneedout; + + zp->next_in = in->rp; + zp->avail_in = in->wp - in->rp; + + zp->next_out = out->wp; + zp->avail_out = out->ep - out->wp; + + err = deflate(zp, in->eof ? Z_FINISH : Z_NO_FLUSH); + + in->rp = in->wp - zp->avail_in; + out->wp = out->ep - zp->avail_out; + + if (err == Z_STREAM_END) { + 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/stream/filt_jbig2d.c b/stream/filt_jbig2d.c new file mode 100644 index 00000000..ed54eed3 --- /dev/null +++ b/stream/filt_jbig2d.c @@ -0,0 +1,115 @@ +#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 +*/ + +#ifdef WIN32 /* Microsoft Visual C++ */ + +typedef signed char int8_t; +typedef short int int16_t; +typedef int int32_t; +typedef __int64 int64_t; + +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; + +#else +#include <inttypes.h> +#endif + +#include <jbig2.h> + +typedef struct fz_jbig2d_s fz_jbig2d; + +struct fz_jbig2d_s +{ + fz_filter super; + Jbig2Ctx *ctx; + Jbig2GlobalCtx *gctx; + Jbig2Image *page; + int idx; +}; + +fz_error * +fz_newjbig2d(fz_filter **fp, fz_obj *params) +{ + FZ_NEWFILTER(fz_jbig2d, d, jbig2d); + d->ctx = jbig2_ctx_new(nil, JBIG2_OPTIONS_EMBEDDED, nil, nil, nil); + d->page = nil; + d->idx = 0; + return nil; +} + +void +fz_dropjbig2d(fz_filter *filter) +{ + fz_jbig2d *d = (fz_jbig2d*)filter; + jbig2_ctx_free(d->ctx); +} + +fz_error * +fz_setjbig2dglobalstream(fz_filter *filter, unsigned char *buf, int len) +{ + fz_jbig2d *d = (fz_jbig2d*)filter; + jbig2_data_in(d->ctx, buf, len); + d->gctx = jbig2_make_global_ctx(d->ctx); + d->ctx = jbig2_ctx_new(nil, JBIG2_OPTIONS_EMBEDDED, d->gctx, nil, nil); + return nil; +} + +fz_error * +fz_processjbig2d(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_jbig2d *d = (fz_jbig2d*)filter; + int len; + int i; + + while (1) + { + if (in->rp == in->wp) { + if (!in->eof) + return fz_ioneedin; + + if (!d->page) { + jbig2_complete_page(d->ctx); + d->page = jbig2_page_out(d->ctx); + } + + if (out->wp == out->ep) + return fz_ioneedout; + + len = out->ep - out->wp; + if (d->idx + len > d->page->height * d->page->stride) + len = d->page->height * d->page->stride - d->idx; + + /* XXX memcpy(out->wp, d->page->data + d->idx, len); */ + for (i = 0; i < len; i++) + out->wp[i] = ~ d->page->data[d->idx + i]; + + 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/stream/filt_jpxd.c b/stream/filt_jpxd.c new file mode 100644 index 00000000..2aa93448 --- /dev/null +++ b/stream/filt_jpxd.c @@ -0,0 +1,143 @@ +#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_dropjpxd(fz_filter *filter) +{ + fz_jpxd *d = (fz_jpxd*)filter; + if (d->stream) jas_stream_close(d->stream); + if (d->image) jas_image_destroy(d->image); +} + +fz_error * +fz_processjpxd(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_jpxd *d = (fz_jpxd*)filter; + int n, bpc, w, h; + int i, x, y; + + switch (d->stage) + { + case 0: goto input; + case 1: goto decode; + case 2: goto output; + } + +input: + while (in->rp < in->wp) { + n = jas_stream_write(d->stream, in->rp, in->wp - in->rp); + in->rp += n; + } + + if (!in->eof) + return fz_ioneedin; + + d->stage = 1; + +decode: + jas_stream_seek(d->stream, 0, 0); + + d->image = jas_image_decode(d->stream, -1, 0); + if (!d->image) + return fz_throw("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/stream/filt_lzwd.c b/stream/filt_lzwd.c new file mode 100644 index 00000000..95fc462a --- /dev/null +++ b/stream/filt_lzwd.c @@ -0,0 +1,253 @@ +#include <fitz.h> + +/* TODO: error checking */ + +enum +{ + MINBITS = 9, + MAXBITS = 12, + NUMCODES = (1 << MAXBITS), + LZW_CLEAR = 256, + LZW_EOD = 257, + LZW_FIRST = 258 +}; + +typedef struct lzw_code_s lzw_code; + +struct lzw_code_s +{ + int prev; /* prev code (in string) */ + unsigned short length; /* string len, including this token */ + unsigned char value; /* data value */ + unsigned char firstchar; /* first token of string */ +}; + +typedef struct fz_lzwd_s fz_lzwd; + +struct fz_lzwd_s +{ + fz_filter super; + + int earlychange; + + unsigned int word; /* bits loaded from data */ + int bidx; + + int resume; /* resume output of code from needout */ + int codebits; /* num bits/code */ + int code; /* current code */ + int oldcode; /* previously recognized code */ + int nextcode; /* next free entry */ + lzw_code table[NUMCODES]; +}; + +fz_error * +fz_newlzwd(fz_filter **fp, fz_obj *params) +{ + int i; + + FZ_NEWFILTER(fz_lzwd, lzw, lzwd); + + lzw->earlychange = 0; + + if (params) + { + fz_obj *obj; + obj = fz_dictgets(params, "EarlyChange"); + if (obj) lzw->earlychange = fz_toint(obj) != 0; + } + + lzw->bidx = 32; + lzw->word = 0; + + for (i = 0; i < 256; i++) + { + lzw->table[i].value = i; + lzw->table[i].firstchar = i; + lzw->table[i].length = 1; + lzw->table[i].prev = -1; + } + + for (i = 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_droplzwd(fz_filter *filter) +{ +} + +static inline void eatbits(fz_lzwd *lzw, int nbits) +{ + lzw->word <<= nbits; + lzw->bidx += nbits; +} + +static inline fz_error * fillbits(fz_lzwd *lzw, fz_buffer *in) +{ + while (lzw->bidx >= 8) + { + if (in->rp + 1 > in->wp) + return fz_ioneedin; + lzw->bidx -= 8; + lzw->word |= *in->rp << lzw->bidx; + in->rp ++; + } + return nil; +} + +static inline void unstuff(fz_lzwd *lzw, fz_buffer *in) +{ + int i = (32 - lzw->bidx) / 8; + while (i-- && in->rp > in->bp) + in->rp --; +} + +fz_error * +fz_processlzwd(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_lzwd *lzw = (fz_lzwd*)filter; + unsigned char *s; + int len; + + if (lzw->resume) + { + lzw->resume = 0; + goto output; + } + + while (1) + { + if (fillbits(lzw, in)) + { + if (in->eof) + { + if (lzw->bidx > 32 - lzw->codebits) + { + out->eof = 1; + unstuff(lzw, in); + return fz_iodone; + } + } + else + { + return fz_ioneedin; + } + } + + lzw->code = lzw->word >> (32 - lzw->codebits); + + if (lzw->code == LZW_EOD) + { + eatbits(lzw, lzw->codebits); + out->eof = 1; + unstuff(lzw, in); + return fz_iodone; + } + + if (lzw->code == LZW_CLEAR) + { + int oldcodebits = lzw->codebits; + + lzw->codebits = MINBITS; + lzw->nextcode = LZW_FIRST; + + lzw->code = lzw->word >> (32 - oldcodebits - MINBITS) & ((1 << MINBITS) - 1); + + if (lzw->code == LZW_EOD) + { + eatbits(lzw, oldcodebits + MINBITS); + out->eof = 1; + unstuff(lzw, in); + return fz_iodone; + } + + 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; + + lzw->nextcode ++; + + if (lzw->nextcode >= (1 << lzw->codebits) - lzw->earlychange - 1) + { + lzw->codebits ++; + if (lzw->codebits > MAXBITS) + lzw->codebits = MAXBITS; /* FIXME */ + } + + lzw->oldcode = lzw->code; + +output: + /* code maps to a string, copy to output (in reverse...) */ + if (lzw->code > 255) + { + if (out->wp + lzw->table[lzw->code].length > out->ep) + { + lzw->resume = 1; + return fz_ioneedout; + } + + len = lzw->table[lzw->code].length; + s = out->wp + len; + + do + { + *(--s) = lzw->table[lzw->code].value; + lzw->code = lzw->table[lzw->code].prev; + } while (lzw->code >= 0 && s > out->wp); + out->wp += len; + } + + /* ... or just a single character */ + else + { + if (out->wp + 1 > out->ep) + { + lzw->resume = 1; + return fz_ioneedout; + } + + *out->wp++ = lzw->code; + } + } +} + diff --git a/stream/filt_lzwe.c b/stream/filt_lzwe.c new file mode 100644 index 00000000..f2c3c7a4 --- /dev/null +++ b/stream/filt_lzwe.c @@ -0,0 +1,261 @@ +#include <fitz.h> + +#define noDEBUG 1 + +enum +{ + MINBITS = 9, + MAXBITS = 12, + MAXBYTES = 2, + NUMCODES = (1 << MAXBITS), + LZW_CLEAR = 256, + LZW_EOD = 257, + LZW_FIRST = 258, + HSIZE = 9001, /* 91% occupancy (???) */ + HSHIFT = (13 - 8) +}; + +typedef struct lzw_hash_s lzw_hash; + +struct lzw_hash_s +{ + int hash; + int code; +}; + +typedef struct fz_lzwe_s fz_lzwe; + +struct fz_lzwe_s +{ + fz_filter super; + + int earlychange; + + int bidx; /* partial bits used in out->wp */ + unsigned char bsave; /* partial byte saved between process() calls */ + + int resume; + int code; + int fcode; + int hcode; + + int codebits; + int oldcode; + int nextcode; + + lzw_hash table[HSIZE]; +}; + +static void +clearhash(fz_lzwe *lzw) +{ + int i; + for (i = 0; i < HSIZE; i++) + lzw->table[i].hash = -1; +} + +fz_error * +fz_newlzwe(fz_filter **fp, fz_obj *params) +{ + FZ_NEWFILTER(fz_lzwe, lzw, lzwe); + + lzw->earlychange = 0; + + if (params) + { + fz_obj *obj; + obj = fz_dictgets(params, "EarlyChange"); + if (obj) lzw->earlychange = fz_toint(obj) != 0; + } + + lzw->bidx = 0; + lzw->bsave = 0; + + lzw->resume = 0; + lzw->code = -1; + lzw->hcode = -1; + lzw->fcode = -1; + + lzw->codebits = MINBITS; + lzw->nextcode = LZW_FIRST; + lzw->oldcode = -1; /* generates LZW_CLEAR */ + + clearhash(lzw); + + return nil; +} + +void +fz_droplzwe(fz_filter *filter) +{ +} + +static void +putcode(fz_lzwe *lzw, fz_buffer *out, int code) +{ + int nbits = lzw->codebits; + + while (nbits > 0) + { + if (lzw->bidx == 0) + { + *out->wp = 0; + } + + /* code does not fit: shift right */ + if (nbits > (8 - lzw->bidx)) + { + *out->wp |= code >> (nbits - (8 - lzw->bidx)); + nbits = nbits - (8 - lzw->bidx); + lzw->bidx = 0; + out->wp ++; + } + + /* shift left */ + else + { + *out->wp |= code << ((8 - lzw->bidx) - nbits); + lzw->bidx += nbits; + if (lzw->bidx == 8) + { + lzw->bidx = 0; + out->wp ++; + } + nbits = 0; + } + } +} + + +static fz_error * +compress(fz_lzwe *lzw, fz_buffer *in, fz_buffer *out) +{ + if (lzw->resume) + { + lzw->resume = 0; + goto resume; + } + + /* at start of data, output a clear code */ + if (lzw->oldcode == -1) + { + if (out->wp + 3 > out->ep) + return fz_ioneedout; + + if (in->rp + 1 > in->wp) + { + if (in->eof) + goto eof; + return fz_ioneedin; + } + + putcode(lzw, out, LZW_CLEAR); + + lzw->oldcode = *in->rp++; + } + +begin: + while (1) + { + if (in->rp + 1 > in->wp) + { + if (in->eof) + goto eof; + return fz_ioneedin; + } + + /* read character */ + lzw->code = *in->rp++; + + /* hash string + character */ + lzw->fcode = (lzw->code << MAXBITS) + lzw->oldcode; + lzw->hcode = (lzw->code << HSHIFT) ^ lzw->oldcode; + + /* primary hash */ + if (lzw->table[lzw->hcode].hash == lzw->fcode) + { + lzw->oldcode = lzw->table[lzw->hcode].code; + continue; + } + + /* secondary hash */ + if (lzw->table[lzw->hcode].hash != -1) + { + int disp = HSIZE - lzw->hcode; + if (lzw->hcode == 0) + disp = 1; + do + { + lzw->hcode = lzw->hcode - disp; + if (lzw->hcode < 0) + lzw->hcode += HSIZE; + if (lzw->table[lzw->hcode].hash == lzw->fcode) + { + lzw->oldcode = lzw->table[lzw->hcode].code; + goto begin; + } + } while (lzw->table[lzw->hcode].hash != -1); + } + +resume: + /* new entry: emit code and add to table */ + + /* reserve space for this code and an eventual CLEAR code */ + if (out->wp + 5 > out->ep) + { + lzw->resume = 1; + return fz_ioneedout; + } + + putcode(lzw, out, lzw->oldcode); + + lzw->oldcode = lzw->code; + lzw->table[lzw->hcode].code = lzw->nextcode; + lzw->table[lzw->hcode].hash = lzw->fcode; + + lzw->nextcode ++; + + /* table is full: emit clear code and reset */ + if (lzw->nextcode == NUMCODES - 1) + { + putcode(lzw, out, LZW_CLEAR); + clearhash(lzw); + lzw->nextcode = LZW_FIRST; + lzw->codebits = MINBITS; + } + + /* check if next entry will be too big for the code size */ + else if (lzw->nextcode >= (1 << lzw->codebits) - lzw->earlychange) + { + lzw->codebits ++; + } + } + +eof: + if (out->wp + 5 > out->ep) + return fz_ioneedout; + + putcode(lzw, out, lzw->oldcode); + putcode(lzw, out, LZW_EOD); + + 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 *error; + + /* restore partial bits */ + *out->wp = lzw->bsave; + + error = compress(lzw, in, out); + + /* save partial bits */ + lzw->bsave = *out->wp; + + return error; +} + diff --git a/stream/filt_null.c b/stream/filt_null.c new file mode 100644 index 00000000..e83b6d5a --- /dev/null +++ b/stream/filt_null.c @@ -0,0 +1,50 @@ +#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_dropnullfilter(fz_filter *f) +{ +} + +fz_error * +fz_processnullfilter(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_nullfilter *f = (fz_nullfilter*)filter; + int n; + + n = MIN(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/stream/filt_pipeline.c b/stream/filt_pipeline.c new file mode 100644 index 00000000..7b7f4252 --- /dev/null +++ b/stream/filt_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 = fz_keepfilter(head); + p->tail = fz_keepfilter(tail); + p->tailneedsin = 1; + p->buffer = fz_keepbuffer(buf); + return nil; +} + +void +fz_unchainpipeline(fz_filter *filter, fz_filter **oldfp, fz_buffer **oldbp) +{ + fz_pipeline *p = (fz_pipeline*)filter; + + *oldfp = fz_keepfilter(p->head); + *oldbp = fz_keepbuffer(p->buffer); + + fz_dropfilter(filter); +} + +fz_error * +fz_newpipeline(fz_filter **fp, fz_filter *head, fz_filter *tail) +{ + fz_error *error; + + FZ_NEWFILTER(fz_pipeline, p, pipeline); + p->head = fz_keepfilter(head); + p->tail = fz_keepfilter(tail); + p->tailneedsin = 1; + + error = fz_newbuffer(&p->buffer, FZ_BUFSIZE); + if (error) { fz_free(p); return error; } + + return nil; +} + +void +fz_droppipeline(fz_filter *filter) +{ + fz_pipeline *p = (fz_pipeline*)filter; + fz_dropfilter(p->head); + fz_dropfilter(p->tail); + fz_dropbuffer(p->buffer); +} + +fz_error * +fz_processpipeline(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_pipeline *p = (fz_pipeline*)filter; + fz_error *e; + + if (p->buffer->eof) + goto tail; + + if (p->tailneedsin && p->head->produced) + goto tail; + +head: + e = fz_process(p->head, in, p->buffer); + + if (e == fz_ioneedin) + return fz_ioneedin; + + else if (e == fz_ioneedout) + { + if (p->tailneedsin && !p->head->produced) + { + fz_error *be = nil; + if (p->buffer->rp > p->buffer->bp) + be = fz_rewindbuffer(p->buffer); + else + be = fz_growbuffer(p->buffer); + if (be) + return be; + goto head; + } + goto tail; + } + + else if (e == fz_iodone) + goto tail; + + else + 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/stream/filt_predict.c b/stream/filt_predict.c new file mode 100644 index 00000000..d7b594ac --- /dev/null +++ b/stream/filt_predict.c @@ -0,0 +1,238 @@ +#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_droppredict(fz_filter *filter) +{ + fz_predict *p = (fz_predict*)filter; + fz_free(p->ref); +} + +static inline int +getcomponent(unsigned char *buf, int x, int bpc) +{ + switch (bpc) + { + case 1: return buf[x / 8] >> (7 - (x % 8)) & 0x01; + case 2: return buf[x / 4] >> ((3 - (x % 4)) * 2) & 0x03; + case 4: return buf[x / 2] >> ((1 - (x % 2)) * 4) & 0x0f; + case 8: return buf[x]; + } + return 0; +} + +static inline void +putcomponent(unsigned char *buf, int x, int bpc, int value) +{ + switch (bpc) + { + case 1: buf[x / 8] |= value << (7 - (x % 8)); break; + case 2: buf[x / 4] |= value << ((3 - (x % 4)) * 2); break; + case 4: buf[x / 2] |= value << ((1 - (x % 2)) * 4); break; + case 8: buf[x] = value; break; + } +} + +static inline int +paeth(int a, int b, int c) +{ + /* The definitions of ac and bc are correct, not a typo. */ + int ac = b - c, bc = a - c, abcc = ac + bc; + int pa = (ac < 0 ? -ac : ac); + int pb = (bc < 0 ? -bc : bc); + int pc = (abcc < 0 ? -abcc : abcc); + return pa <= pb && pa <= pc ? a : pb <= pc ? b : c; +} + +static inline void +none(fz_predict *p, unsigned char *in, unsigned char *out) +{ + memcpy(out, in, p->stride); +} + +static void +tiff(fz_predict *p, unsigned char *in, unsigned char *out) +{ + int left[MAXC]; + int i, k; + + for (k = 0; k < p->colors; k++) + left[k] = 0; + + for (i = 0; i < p->columns; i++) { + for (k = 0; k < p->colors; k++) { + int a = getcomponent(in, i * p->colors + k, p->bpc); + int b = p->encode ? a - left[k] : a + left[k]; + int c = b % (1 << p->bpc); + putcomponent(out, i * p->colors + k, p->bpc, c); + left[k] = p->encode ? a : c; + } + } +} + +static void +png(fz_predict *p, unsigned char *in, unsigned char *out, int predictor) +{ + int upleft[MAXC], left[MAXC], i, k; + + for (k = 0; k < p->bpp; k++) { + left[k] = 0; + upleft[k] = 0; + } + + if (p->encode) + { + for (k = 0, i = 0; i < p->stride; k = (k + 1) % p->bpp, i ++) + { + switch (predictor) + { + case 0: out[i] = in[i]; break; + case 1: out[i] = in[i] - left[k]; break; + case 2: out[i] = in[i] - p->ref[i]; break; + case 3: out[i] = in[i] - (left[k] + p->ref[i]) / 2; break; + case 4: out[i] = in[i] - paeth(left[k], p->ref[i], upleft[k]); break; + } + left[k] = in[i]; + upleft[k] = p->ref[i]; + } + } + + else + { + for (k = 0, i = 0; i < p->stride; k = (k + 1) % p->bpp, i ++) + { + switch (predictor) + { + case 0: out[i] = in[i]; break; + case 1: out[i] = in[i] + left[k]; break; + case 2: out[i] = in[i] + p->ref[i]; break; + case 3: out[i] = in[i] + (left[k] + p->ref[i]) / 2; break; + case 4: out[i] = in[i] + paeth(left[k], p->ref[i], upleft[k]); break; + } + left[k] = out[i]; + upleft[k] = p->ref[i]; + } + } +} + +fz_error * +fz_processpredict(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_predict *dec = (fz_predict*)filter; + int ispng = dec->predictor >= 10; + int predictor; + + while (1) + { + if (in->rp + dec->stride + (!dec->encode && ispng) > in->wp) { + if (in->eof) + return fz_iodone; + return fz_ioneedin; + } + + if (out->wp + dec->stride + (dec->encode && ispng) > out->ep) + return fz_ioneedout; + + if (dec->predictor == 1) { + none(dec, in->rp, out->wp); + } + else if (dec->predictor == 2) { + if (dec->bpc != 8) + memset(out->wp, 0, dec->stride); + tiff(dec, in->rp, out->wp); + } + else { + if (dec->encode) { + predictor = dec->predictor - 10; + if (predictor < 0 || predictor > 4) + predictor = 1; + *out->wp ++ = predictor; + } + else { + predictor = *in->rp++; + } + png(dec, in->rp, out->wp, predictor); + } + + if (dec->ref) + memcpy(dec->ref, out->wp, dec->stride); + + in->rp += dec->stride; + out->wp += dec->stride; + } +} + +fz_error * +fz_newpredictd(fz_filter **fp, fz_obj *params) +{ + return fz_newpredict(fp, params, 0); +} + +fz_error * +fz_newpredicte(fz_filter **fp, fz_obj *params) +{ + return fz_newpredict(fp, params, 1); +} + diff --git a/stream/filt_process.c b/stream/filt_process.c new file mode 100644 index 00000000..afe4feed --- /dev/null +++ b/stream/filt_process.c @@ -0,0 +1,51 @@ +#include <fitz.h> + +fz_error fz_kioneedin = { -1, "<ioneedin>", "<process>", "filter.c", 0 }; +fz_error fz_kioneedout = { -1, "<ioneedout>", "<process>", "filter.c", 0 }; +fz_error fz_kiodone = { -1, "<iodone>", "<process>", "filter.c", 0 }; + +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; +} + +fz_filter * +fz_keepfilter(fz_filter *f) +{ + f->refs ++; + return f; +} + +void +fz_dropfilter(fz_filter *f) +{ + if (--f->refs == 0) + { + if (f->drop) + f->drop(f); + fz_free(f); + } +} + diff --git a/stream/filt_rld.c b/stream/filt_rld.c new file mode 100644 index 00000000..bcad4323 --- /dev/null +++ b/stream/filt_rld.c @@ -0,0 +1,66 @@ +#include <fitz.h> + +fz_error * +fz_newrld(fz_filter **fp, fz_obj *params) +{ + FZ_NEWFILTER(fz_filter, f, rld); + return nil; +} + +void +fz_droprld(fz_filter *rld) +{ +} + +fz_error * +fz_processrld(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + int run, i; + unsigned char c; + + while (1) + { + if (in->rp == in->wp) + 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/stream/filt_rle.c b/stream/filt_rle.c new file mode 100644 index 00000000..921e470a --- /dev/null +++ b/stream/filt_rle.c @@ -0,0 +1,237 @@ +#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_droprle(fz_filter *enc) +{ +} + +static fz_error * +putone(fz_rle *enc, fz_buffer *in, fz_buffer *out) +{ + if (out->wp + 2 >= out->ep) + return fz_ioneedout; + +#ifdef DEBUG +fprintf(stderr, "one '%c'\n", enc->buf[0]); +#endif + + *out->wp++ = 0; + *out->wp++ = enc->buf[0]; + + return 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 *error; + unsigned char c; + + while (1) + { + + if (enc->reclen && enc->curlen == enc->reclen) { + error = savebuf(enc, in, out); + if (error) return error; +#ifdef DEBUG +fprintf(stderr, "--record--\n"); +#endif + enc->state = ZERO; + enc->curlen = 0; + } + + if (in->rp == in->wp) { + if (in->eof) { + if (enc->state != END) { + error = savebuf(enc, in, out); + if (error) return error; + } + enc->state = END; + } + else + return fz_ioneedin; + } + + c = *in->rp; + + switch (enc->state) + { + case ZERO: + enc->state = ONE; + enc->run = 1; + enc->buf[0] = c; + break; + + case ONE: + enc->state = DIFF; + enc->run = 2; + enc->buf[1] = c; + break; + + case DIFF: + /* out of space */ + if (enc->run == 128) { + error = putdiff(enc, in, out); + if (error) return error; + + enc->state = ONE; + enc->run = 1; + enc->buf[0] = c; + } + + /* run of three that are the same */ + else if ((enc->run > 1) && + (c == enc->buf[enc->run - 1]) && + (c == enc->buf[enc->run - 2])) + { + if (enc->run >= 3) { + enc->run -= 2; /* skip prev two for diff run */ + error = putdiff(enc, in, out); + if (error) return error; + } + + enc->state = SAME; + enc->run = 3; + enc->buf[0] = c; + } + + /* keep on collecting */ + else { + enc->buf[enc->run++] = c; + } + break; + + case SAME: + if (enc->run == 128 || c != enc->buf[0]) { + error = putsame(enc, in, out); + if (error) return error; + + enc->state = ONE; + enc->run = 1; + enc->buf[0] = c; + } + else { + enc->run ++; + } + break; + + case END: + error = puteod(enc, in, out); + if (error) return error; + + out->eof = 1; + return fz_iodone; + } + + in->rp ++; + + enc->curlen ++; + + } +} + diff --git a/stream/obj_array.c b/stream/obj_array.c new file mode 100644 index 00000000..c493bdaa --- /dev/null +++ b/stream/obj_array.c @@ -0,0 +1,184 @@ +#include <fitz.h> + +void fz_droparray(fz_obj *obj); + +fz_error * +fz_newarray(fz_obj **op, int initialcap) +{ + fz_obj *obj; + int i; + + obj = *op = fz_malloc(sizeof (fz_obj)); + if (!obj) return fz_outofmem; + + obj->refs = 1; + obj->kind = FZ_ARRAY; + + obj->u.a.len = 0; + obj->u.a.cap = initialcap > 0 ? initialcap : 6; + + obj->u.a.items = fz_malloc(sizeof (fz_obj*) * obj->u.a.cap); + if (!obj->u.a.items) { fz_free(obj); return fz_outofmem; } + + for (i = 0; i < obj->u.a.cap; i++) + obj->u.a.items[i] = nil; + + return nil; +} + +fz_error * +fz_copyarray(fz_obj **op, fz_obj *obj) +{ + fz_error *error; + fz_obj *new; + int i; + + if (!fz_isarray(obj)) + return fz_throw("typecheck in copyarray"); + + error = fz_newarray(&new, fz_arraylen(obj)); + if (error) return error; + *op = new; + + for (i = 0; i < fz_arraylen(obj); i++) { + error = fz_arraypush(new, fz_arrayget(obj, i)); + if (error) { fz_droparray(new); return error; } + } + + return nil; +} + +fz_error * +fz_deepcopyarray(fz_obj **op, fz_obj *obj) +{ + fz_error *error; + fz_obj *new; + fz_obj *val; + int i; + + if (!fz_isarray(obj)) + return fz_throw("typecheck in deepcopyarray"); + + error = fz_newarray(&new, fz_arraylen(obj)); + if (error) return error; + *op = new; + + for (i = 0; i < fz_arraylen(obj); i++) + { + val = fz_arrayget(obj, i); + + if (fz_isarray(val)) { + error = fz_deepcopyarray(&val, val); + if (error) { fz_droparray(new); return error; } + error = fz_arraypush(new, val); + if (error) { fz_dropobj(val); fz_droparray(new); return error; } + fz_dropobj(val); + } + + else if (fz_isdict(val)) { + error = fz_deepcopydict(&val, val); + if (error) { fz_droparray(new); return error; } + error = fz_arraypush(new, val); + if (error) { fz_dropobj(val); fz_droparray(new); return error; } + fz_dropobj(val); + } + + else { + error = fz_arraypush(new, val); + if (error) { fz_droparray(new); return error; } + } + } + + return nil; +} + +int +fz_arraylen(fz_obj *obj) +{ + if (!fz_isarray(obj)) + return 0; + return obj->u.a.len; +} + +fz_obj * +fz_arrayget(fz_obj *obj, int i) +{ + if (!fz_isarray(obj)) + return nil; + + if (i < 0 || i >= obj->u.a.len) + return nil; + + return obj->u.a.items[i]; +} + +fz_error * +fz_arrayput(fz_obj *obj, int i, fz_obj *item) +{ + if (!fz_isarray(obj)) + return fz_throw("typecheck in arrayput"); + if (i < 0) + return fz_throw("rangecheck in arrayput: %d < 0", i); + if (i >= obj->u.a.len) + return fz_throw("rangecheck in arrayput: %d > %d", i, obj->u.a.len); + + if (obj->u.a.items[i]) + fz_dropobj(obj->u.a.items[i]); + obj->u.a.items[i] = fz_keepobj(item); + + return nil; +} + +static fz_error * +growarray(fz_obj *obj) +{ + fz_obj **newitems; + int newcap; + int i; + + newcap = obj->u.a.cap * 2; + newitems = fz_realloc(obj->u.a.items, sizeof (fz_obj*) * newcap); + if (!newitems) return fz_outofmem; + + obj->u.a.items = newitems; + for (i = obj->u.a.cap ; i < newcap; i++) + obj->u.a.items[i] = nil; + obj->u.a.cap = newcap; + + return nil; +} + +fz_error * +fz_arraypush(fz_obj *obj, fz_obj *item) +{ + fz_error *error; + + if (!fz_isarray(obj)) + return fz_throw("typecheck in arraypush"); + + if (obj->u.a.len + 1 > obj->u.a.cap) { + error = growarray(obj); + if (error) return error; + } + + obj->u.a.items[obj->u.a.len] = fz_keepobj(item); + obj->u.a.len++; + + return nil; +} + +void +fz_droparray(fz_obj *obj) +{ + int i; + + assert(obj->kind == FZ_ARRAY); + + for (i = 0; i < obj->u.a.len; i++) + if (obj->u.a.items[i]) + fz_dropobj(obj->u.a.items[i]); + + fz_free(obj->u.a.items); + fz_free(obj); +} + diff --git a/stream/obj_dict.c b/stream/obj_dict.c new file mode 100644 index 00000000..a0fb6d25 --- /dev/null +++ b/stream/obj_dict.c @@ -0,0 +1,355 @@ +#include <fitz.h> + +/* keep either names or strings in the dict. don't mix & match. */ + +static int keyvalcmp(const void *ap, const void *bp) +{ + const fz_keyval *a = ap; + const fz_keyval *b = bp; + if (fz_isname(a->k)) + return strcmp(fz_toname(a->k), fz_toname(b->k)); + if (fz_isstring(a->k)) + return strcmp(fz_tostrbuf(a->k), fz_tostrbuf(b->k)); + return -1; +} + +static inline int keystrcmp(fz_obj *key, char *s) +{ + if (fz_isname(key)) + return strcmp(fz_toname(key), s); + if (fz_isstring(key)) + return strcmp(fz_tostrbuf(key), s); + return -1; +} + +fz_error * +fz_newdict(fz_obj **op, int initialcap) +{ + fz_obj *obj; + int i; + + obj = *op = fz_malloc(sizeof (fz_obj)); + if (!obj) return fz_outofmem; + + obj->refs = 1; + obj->kind = FZ_DICT; + + obj->u.d.sorted = 1; + obj->u.d.len = 0; + obj->u.d.cap = initialcap > 0 ? initialcap : 10; + + obj->u.d.items = fz_malloc(sizeof(fz_keyval) * obj->u.d.cap); + if (!obj->u.d.items) { fz_free(obj); return fz_outofmem; } + + for (i = 0; i < obj->u.d.cap; i++) { + obj->u.d.items[i].k = nil; + obj->u.d.items[i].v = nil; + } + + return nil; +} + +fz_error * +fz_copydict(fz_obj **op, fz_obj *obj) +{ + fz_error *error; + fz_obj *new; + int i; + + if (!fz_isdict(obj)) + return fz_throw("typecheck in copydict"); + + error = fz_newdict(&new, obj->u.d.cap); + if (error) return error; + *op = new; + + for (i = 0; i < fz_dictlen(obj); i++) { + error = fz_dictput(new, fz_dictgetkey(obj, i), fz_dictgetval(obj, i)); + if (error) { fz_dropobj(new); return error; } + } + + return nil; +} + +fz_error * +fz_deepcopydict(fz_obj **op, fz_obj *obj) +{ + fz_error *error; + fz_obj *new; + fz_obj *val; + int i; + + if (!fz_isdict(obj)) + return fz_throw("typecheck in deepcopydict"); + + error = fz_newdict(&new, obj->u.d.cap); + if (error) return error; + *op = new; + + for (i = 0; i < fz_dictlen(obj); i++) + { + val = fz_dictgetval(obj, i); + + if (fz_isarray(val)) { + error = fz_deepcopyarray(&val, val); + if (error) { fz_dropobj(new); return error; } + error = fz_dictput(new, fz_dictgetkey(obj, i), val); + if (error) { fz_dropobj(val); fz_dropobj(new); return error; } + fz_dropobj(val); + } + + else if (fz_isdict(val)) { + error = fz_deepcopydict(&val, val); + if (error) { fz_dropobj(new); return error; } + error = fz_dictput(new, fz_dictgetkey(obj, i), val); + if (error) { fz_dropobj(val); fz_dropobj(new); return error; } + fz_dropobj(val); + } + + else { + error = fz_dictput(new, fz_dictgetkey(obj, i), val); + if (error) { fz_dropobj(new); return error; } + } + } + + return nil; +} + +static fz_error * +growdict(fz_obj *obj) +{ + fz_keyval *newitems; + int newcap; + int i; + + newcap = obj->u.d.cap * 2; + + newitems = fz_realloc(obj->u.d.items, sizeof(fz_keyval) * newcap); + if (!newitems) return fz_outofmem; + + obj->u.d.items = newitems; + for (i = obj->u.d.cap; i < newcap; i++) { + obj->u.d.items[i].k = nil; + obj->u.d.items[i].v = nil; + } + obj->u.d.cap = newcap; + + return nil; +} + +int +fz_dictlen(fz_obj *obj) +{ + if (!fz_isdict(obj)) + return 0; + return obj->u.d.len; +} + +fz_obj * +fz_dictgetkey(fz_obj *obj, int i) +{ + if (!fz_isdict(obj)) + return nil; + + if (i < 0 || i >= obj->u.d.len) + return nil; + + return obj->u.d.items[i].k; +} + +fz_obj * +fz_dictgetval(fz_obj *obj, int i) +{ + if (!fz_isdict(obj)) + return nil; + + if (i < 0 || i >= obj->u.d.len) + return nil; + + return obj->u.d.items[i].v; +} + +static inline int dictfinds(fz_obj *obj, char *key) +{ + if (obj->u.d.sorted) + { + int l = 0; + int r = obj->u.d.len - 1; + while (l <= r) + { + int m = (l + r) >> 1; + int c = -keystrcmp(obj->u.d.items[m].k, key); + if (c < 0) + r = m - 1; + else if (c > 0) + l = m + 1; + else + return m; + } + } + + else + { + int i; + for (i = 0; i < obj->u.d.len; i++) + if (keystrcmp(obj->u.d.items[i].k, key) == 0) + return i; + } + + return -1; +} + +fz_obj * +fz_dictgets(fz_obj *obj, char *key) +{ + int i; + + if (!fz_isdict(obj)) + return nil; + + i = dictfinds(obj, key); + if (i >= 0) + return obj->u.d.items[i].v; + + return nil; +} + +fz_obj * +fz_dictget(fz_obj *obj, fz_obj *key) +{ + if (fz_isname(key)) + return fz_dictgets(obj, fz_toname(key)); + if (fz_isstring(key)) + return fz_dictgets(obj, fz_tostrbuf(key)); + return nil; +} + +fz_obj * +fz_dictgetsa(fz_obj *obj, char *key, char *abbrev) +{ + fz_obj *v; + v = fz_dictgets(obj, key); + if (v) + return v; + return fz_dictgets(obj, abbrev); +} + +fz_error * +fz_dictput(fz_obj *obj, fz_obj *key, fz_obj *val) +{ + fz_error *error; + char *s; + int i; + + if (!fz_isdict(obj)) + return fz_throw("typecheck in dictput"); + + if (fz_isname(key)) + s = fz_toname(key); + else if (fz_isstring(key)) + s = fz_tostrbuf(key); + else + return fz_throw("typecheck in dictput"); + + i = dictfinds(obj, s); + if (i >= 0) + { + fz_dropobj(obj->u.d.items[i].v); + obj->u.d.items[i].v = fz_keepobj(val); + return nil; + } + + if (obj->u.d.len + 1 > obj->u.d.cap) + { + error = growdict(obj); + if (error) + return error; + } + + /* borked! */ + if (obj->u.d.len) + if (keystrcmp(obj->u.d.items[obj->u.d.len - 1].k, s) > 0) + obj->u.d.sorted = 0; + + obj->u.d.items[obj->u.d.len].k = fz_keepobj(key); + obj->u.d.items[obj->u.d.len].v = fz_keepobj(val); + obj->u.d.len ++; + + return nil; +} + +fz_error * +fz_dictputs(fz_obj *obj, char *key, fz_obj *val) +{ + fz_error *error; + fz_obj *keyobj; + error = fz_newname(&keyobj, key); + if (error) return error; + error = fz_dictput(obj, keyobj, val); + fz_dropobj(keyobj); + return error; +} + +fz_error * +fz_dictdels(fz_obj *obj, char *key) +{ + int i; + + if (!fz_isdict(obj)) + return fz_throw("typecheck in dictdel"); + + i = dictfinds(obj, key); + if (i >= 0) + { + fz_dropobj(obj->u.d.items[i].k); + fz_dropobj(obj->u.d.items[i].v); + obj->u.d.sorted = 0; + obj->u.d.items[i] = obj->u.d.items[obj->u.d.len-1]; + obj->u.d.len --; + } + + return nil; +} + +fz_error * +fz_dictdel(fz_obj *obj, fz_obj *key) +{ + if (fz_isname(key)) + return fz_dictdels(obj, fz_toname(key)); + else if (fz_isstring(key)) + return fz_dictdels(obj, fz_tostrbuf(key)); + else + return fz_throw("typecheck in dictdel"); +} + +void +fz_dropdict(fz_obj *obj) +{ + int i; + + if (!fz_isdict(obj)) + return; + + for (i = 0; i < obj->u.d.len; i++) { + if (obj->u.d.items[i].k) + fz_dropobj(obj->u.d.items[i].k); + if (obj->u.d.items[i].v) + fz_dropobj(obj->u.d.items[i].v); + } + + fz_free(obj->u.d.items); + fz_free(obj); +} + +void +fz_sortdict(fz_obj *obj) +{ + if (!fz_isdict(obj)) + return; + if (!obj->u.d.sorted) + { + qsort(obj->u.d.items, obj->u.d.len, sizeof(fz_keyval), keyvalcmp); + obj->u.d.sorted = 1; + } +} + diff --git a/stream/obj_parse.c b/stream/obj_parse.c new file mode 100644 index 00000000..c3f3d3dc --- /dev/null +++ b/stream/obj_parse.c @@ -0,0 +1,401 @@ +#include <fitz.h> + +struct vap { va_list ap; }; + +static inline int iswhite(int ch) +{ + return + ch == '\000' || + ch == '\011' || + ch == '\012' || + ch == '\014' || + ch == '\015' || + ch == '\040'; +} + +static inline int isdelim(int ch) +{ + return + ch == '(' || ch == ')' || + ch == '<' || ch == '>' || + ch == '[' || ch == ']' || + ch == '{' || ch == '}' || + ch == '/' || + ch == '%'; +} + +static inline int isregular(int ch) +{ + return !isdelim(ch) && !iswhite(ch) && ch != EOF; +} + +static fz_error *parseobj(fz_obj **obj, char **sp, struct vap *v); + +static inline int fromhex(char ch) +{ + if (ch >= '0' && ch <= '9') + return ch - '0'; + else if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 0xA; + else if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 0xA; + return 0; +} + +static inline void skipwhite(char **sp) +{ + char *s = *sp; + while (iswhite(*s)) + s ++; + *sp = s; +} + +static void parsekeyword(char **sp, char *b, char *eb) +{ + char *s = *sp; + while (b < eb && isregular(*s)) + *b++ = *s++; + *b++ = 0; + *sp = s; +} + +static fz_error *parsename(fz_obj **obj, char **sp) +{ + char buf[64]; + char *s = *sp; + char *p = buf; + + s ++; /* skip '/' */ + while (p < buf + sizeof buf - 1 && isregular(*s)) + *p++ = *s++; + *p++ = 0; + *sp = s; + + return fz_newname(obj, buf); +} + +static fz_error *parsenumber(fz_obj **obj, char **sp) +{ + char buf[32]; + char *s = *sp; + char *p = buf; + + while (p < buf + sizeof buf - 1) + { + if (s[0] == '-' || s[0] == '.' || (s[0] >= '0' && s[0] <= '9')) + *p++ = *s++; + else + break; + } + *p++ = 0; + *sp = s; + + if (strchr(buf, '.')) + return fz_newreal(obj, atof(buf)); + return fz_newint(obj, atoi(buf)); +} + +static fz_error *parsedict(fz_obj **obj, char **sp, struct vap *v) +{ + fz_error *error = nil; + fz_obj *dict = nil; + fz_obj *key = nil; + fz_obj *val = nil; + char *s = *sp; + + error = fz_newdict(&dict, 8); + if (error) return error; + *obj = dict; + + s += 2; /* skip "<<" */ + + while (*s) + { + skipwhite(&s); + + /* end-of-dict marker >> */ + if (*s == '>') { + s ++; + if (*s == '>') { + s ++; + break; + } + error = fz_throw("syntaxerror in parsedict"); + goto cleanup; + } + + /* non-name as key, bail */ + if (*s != '/') { + error = fz_throw("syntaxerror in parsedict"); + goto cleanup; + } + + error = parsename(&key, &s); + if (error) goto cleanup; + + skipwhite(&s); + + error = parseobj(&val, &s, v); + if (error) goto cleanup; + + error = fz_dictput(dict, key, val); + if (error) goto cleanup; + + fz_dropobj(val); val = nil; + fz_dropobj(key); key = nil; + } + + *sp = s; + return nil; + +cleanup: + if (val) fz_dropobj(val); + if (key) fz_dropobj(key); + if (dict) fz_dropobj(dict); + *obj = nil; + *sp = s; + return error; +} + +static fz_error *parsearray(fz_obj **obj, char **sp, struct vap *v) +{ + fz_error *error; + fz_obj *a; + fz_obj *o; + char *s = *sp; + + error = fz_newarray(&a, 8); + if (error) return error; + *obj = a; + + s ++; /* skip '[' */ + + while (*s) + { + skipwhite(&s); + + if (*s == ']') { + s ++; + break; + } + + error = parseobj(&o, &s, v); + if (error) { *obj = nil; fz_dropobj(a); return error; } + + error = fz_arraypush(a, o); + if (error) { fz_dropobj(o); *obj = nil; fz_dropobj(a); return error; } + + fz_dropobj(o); + } + + *sp = s; + return nil; +} + +static fz_error *parsestring(fz_obj **obj, char **sp) +{ + char buf[512]; + char *s = *sp; + char *p = buf; + int balance = 1; + int oct; + + s ++; /* skip '(' */ + + while (*s && p < buf + sizeof buf) + { + if (*s == '(') + { + balance ++; + *p++ = *s++; + } + else if (*s == ')') + { + balance --; + *p++ = *s++; + } + else if (*s == '\\') + { + s ++; + if (*s >= '0' && *s <= '9') + { + oct = *s - '0'; + s ++; + if (*s >= '0' && *s <= '9') + { + oct = oct * 8 + (*s - '0'); + s ++; + if (*s >= '0' && *s <= '9') + { + oct = oct * 8 + (*s - '0'); + s ++; + } + } + *p++ = oct; + } + else switch (*s) + { + case 'n': *p++ = '\n'; s++; break; + case 'r': *p++ = '\r'; s++; break; + case 't': *p++ = '\t'; s++; break; + case 'b': *p++ = '\b'; s++; break; + case 'f': *p++ = '\f'; s++; break; + default: *p++ = *s++; break; + } + } + else + *p++ = *s++; + + if (balance == 0) + break; + } + + *sp = s; + return fz_newstring(obj, buf, p - buf - 1); +} + +static fz_error *parsehexstring(fz_obj **obj, char **sp) +{ + char buf[512]; + char *s = *sp; + char *p = buf; + int a, b; + + s ++; /* skip '<' */ + + while (*s && p < buf + sizeof buf) + { + skipwhite(&s); + if (*s == '>') { + s ++; + break; + } + a = *s++; + + if (*s == '\0') + break; + + skipwhite(&s); + if (*s == '>') { + s ++; + break; + } + b = *s++; + + *p++ = fromhex(a) * 16 + fromhex(b); + } + + *sp = s; + return fz_newstring(obj, buf, p - buf); +} + +static fz_error *parseobj(fz_obj **obj, char **sp, struct vap *v) +{ + fz_error *error; + char buf[32]; + int oid, gid, len; + char *tmp; + char *s = *sp; + + if (*s == '\0') + return fz_throw("syntaxerror in parseobj: end-of-string"); + + skipwhite(&s); + + error = nil; + + if (v != nil && *s == '%') + { + s ++; + switch (*s) + { + case 'p': error = fz_newpointer(obj, va_arg(v->ap, void*)); break; + case 'o': *obj = fz_keepobj(va_arg(v->ap, fz_obj*)); break; + case 'b': error = fz_newbool(obj, va_arg(v->ap, int)); break; + case 'i': error = fz_newint(obj, va_arg(v->ap, int)); break; + case 'f': error = fz_newreal(obj, (float)va_arg(v->ap, double)); break; + case 'n': error = fz_newname(obj, va_arg(v->ap, char*)); break; + case 'r': + oid = va_arg(v->ap, int); + gid = va_arg(v->ap, int); + error = fz_newindirect(obj, oid, gid); + break; + case 's': + tmp = va_arg(v->ap, char*); + error = fz_newstring(obj, tmp, strlen(tmp)); + break; + case '#': + tmp = va_arg(v->ap, char*); + len = va_arg(v->ap, int); + error = fz_newstring(obj, tmp, len); + break; + default: + error = fz_throw("unknown format specifier in packobj: '%c'", *s); + break; + } + s ++; + } + + else if (*s == '/') + error = parsename(obj, &s); + + else if (*s == '(') + error = parsestring(obj, &s); + + else if (*s == '<') { + if (s[1] == '<') + error = parsedict(obj, &s, v); + else + error = parsehexstring(obj, &s); + } + + else if (*s == '[') + error = parsearray(obj, &s, v); + + else if (*s == '-' || *s == '.' || (*s >= '0' && *s <= '9')) + error = parsenumber(obj, &s); + + else if (isregular(*s)) + { + parsekeyword(&s, buf, buf + sizeof buf); + + if (strcmp("true", buf) == 0) + error = fz_newbool(obj, 1); + else if (strcmp("false", buf) == 0) + error = fz_newbool(obj, 0); + else if (strcmp("null", buf) == 0) + error = fz_newnull(obj); + else + error = fz_throw("syntaxerror in parseobj: undefined keyword %s", buf); + } + + else + error = fz_throw("syntaxerror in parseobj"); + + *sp = s; + return error; +} + +fz_error * +fz_packobj(fz_obj **op, char *fmt, ...) +{ + fz_error *error; + struct vap v; + va_list ap; + + va_start(ap, fmt); + va_copy(v.ap, ap); + + error = parseobj(op, &fmt, &v); + + va_end(ap); + + return error; +} + +fz_error * +fz_parseobj(fz_obj **op, char *str) +{ + return parseobj(op, &str, nil); +} + diff --git a/stream/obj_print.c b/stream/obj_print.c new file mode 100644 index 00000000..7ada9845 --- /dev/null +++ b/stream/obj_print.c @@ -0,0 +1,302 @@ +#include <fitz.h> + +struct fmt +{ + char *buf; + int cap; + int len; + int indent; + int tight; + int col; + int sep; + int last; +}; + +static void fmtobj(struct fmt *fmt, fz_obj *obj); + +static inline int isdelim(int ch) +{ + return ch == '(' || ch == ')' || + ch == '<' || ch == '>' || + ch == '[' || ch == ']' || + ch == '{' || ch == '}' || + ch == '/' || + ch == '%'; +} + +static inline void fmtputc(struct fmt *fmt, int c) +{ + if (fmt->sep && !isdelim(fmt->last) && !isdelim(c)) { + fmt->sep = 0; + fmtputc(fmt, ' '); + } + fmt->sep = 0; + + if (fmt->buf && fmt->len < fmt->cap) + fmt->buf[fmt->len] = c; + + if (c == '\n') + fmt->col = 0; + else + fmt->col ++; + + fmt->len ++; + + fmt->last = c; +} + +static void fmtindent(struct fmt *fmt) +{ + int i = fmt->indent; + while (i--) { + fmtputc(fmt, ' '); + fmtputc(fmt, ' '); + } +} + +static inline void fmtputs(struct fmt *fmt, char *s) +{ + while (*s) + fmtputc(fmt, *s++); +} + +static inline void fmtsep(struct fmt *fmt) +{ + fmt->sep = 1; +} + +static void fmtstr(struct fmt *fmt, fz_obj *obj) +{ + int i; + int c; + + fmtputc(fmt, '('); + for (i = 0; i < obj->u.s.len; i++) + { + c = (unsigned char) obj->u.s.buf[i]; + if (c == '\n') + fmtputs(fmt, "\\n"); + else if (c == '\r') + fmtputs(fmt, "\\r"); + else if (c == '\t') + fmtputs(fmt, "\\t"); + else if (c == '\b') + fmtputs(fmt, "\\b"); + else if (c == '\f') + fmtputs(fmt, "\\f"); + else if (c == '(') + fmtputs(fmt, "\\("); + else if (c == ')') + fmtputs(fmt, "\\)"); + else if (c < 32 || c > 126) { + char buf[16]; + fmtputc(fmt, '\\'); + sprintf(buf, "%o", c); + fmtputs(fmt, buf); + } + else + fmtputc(fmt, c); + } + fmtputc(fmt, ')'); +} + +static void fmthex(struct fmt *fmt, fz_obj *obj) +{ + int i; + int b; + int c; + + fmtputc(fmt, '<'); + for (i = 0; i < obj->u.s.len; i++) { + b = (unsigned char) obj->u.s.buf[i]; + c = (b >> 4) & 0x0f; + fmtputc(fmt, c < 0xA ? c + '0' : c + 'A' - 0xA); + c = (b) & 0x0f; + fmtputc(fmt, c < 0xA ? c + '0' : c + 'A' - 0xA); + } + fmtputc(fmt, '>'); +} + +static void fmtarray(struct fmt *fmt, fz_obj *obj) +{ + int i; + + if (fmt->tight) { + fmtputc(fmt, '['); + for (i = 0; i < fz_arraylen(obj); i++) { + fmtobj(fmt, fz_arrayget(obj, i)); + fmtsep(fmt); + } + fmtputc(fmt, ']'); + } + else { + fmtputs(fmt, "[ "); + for (i = 0; i < fz_arraylen(obj); i++) { + if (fmt->col > 60) { + fmtputc(fmt, '\n'); + fmtindent(fmt); + } + fmtobj(fmt, fz_arrayget(obj, i)); + fmtputc(fmt, ' '); + } + fmtputc(fmt, ']'); + fmtsep(fmt); + } +} + +static void fmtdict(struct fmt *fmt, fz_obj *obj) +{ + int i; + fz_obj *key, *val; + + if (fmt->tight) { + fmtputs(fmt, "<<"); + for (i = 0; i < fz_dictlen(obj); i++) { + fmtobj(fmt, fz_dictgetkey(obj, i)); + fmtsep(fmt); + fmtobj(fmt, fz_dictgetval(obj, i)); + fmtsep(fmt); + } + fmtputs(fmt, ">>"); + } + else { + fmtputs(fmt, "<<\n"); + fmt->indent ++; + for (i = 0; i < fz_dictlen(obj); i++) { + key = fz_dictgetkey(obj, i); + val = fz_dictgetval(obj, i); + fmtindent(fmt); + fmtobj(fmt, key); + fmtputc(fmt, ' '); + if (fz_isarray(val)) + fmt->indent ++; + fmtobj(fmt, val); + fmtputc(fmt, '\n'); + if (fz_isarray(val)) + fmt->indent --; + } + fmt->indent --; + fmtindent(fmt); + fmtputs(fmt, ">>"); + } +} + +static void fmtobj(struct fmt *fmt, fz_obj *obj) +{ + char buf[256]; + + if (!obj) { + fmtputs(fmt, "<nil>"); + return; + } + + switch (obj->kind) + { + case FZ_NULL: + fmtputs(fmt, "null"); + break; + case FZ_BOOL: + fmtputs(fmt, fz_tobool(obj) ? "true" : "false"); + break; + case FZ_INT: + sprintf(buf, "%d", fz_toint(obj)); + fmtputs(fmt, buf); + break; + case FZ_REAL: + sprintf(buf, "%g", fz_toreal(obj)); + if (strchr(buf, 'e')) /* bad news! */ + sprintf(buf, fabs(fz_toreal(obj)) > 1 ? "%1.1f" : "%1.8f", fz_toreal(obj)); + fmtputs(fmt, buf); + break; + case FZ_STRING: + { + int added = 0; + int i, c; + for (i = 0; i < obj->u.s.len; i++) { + c = (unsigned char)obj->u.s.buf[i]; + if (strchr("()\\\n\r\t\b\f", c) != 0) + added ++; + else if (c < 8) + added ++; + else if (c < 32) + added += 2; + else if (c >= 127) + added += 3; + } + if (added < obj->u.s.len) + fmtstr(fmt, obj); + else + fmthex(fmt, obj); + } + break; + case FZ_NAME: + sprintf(buf, "/%s", fz_toname(obj)); + fmtputs(fmt, buf); + break; + case FZ_ARRAY: + fmtarray(fmt, obj); + break; + case FZ_DICT: + fmtdict(fmt, obj); + break; + case FZ_INDIRECT: + sprintf(buf, "%d %d R", obj->u.r.oid, obj->u.r.gid); + fmtputs(fmt, buf); + break; + case FZ_POINTER: + sprintf(buf, "$%p", obj->u.p); + fmtputs(fmt, buf); + break; + default: + sprintf(buf, "<unknown object type %d>", obj->kind); + fmtputs(fmt, buf); + break; + } +} + +int +fz_sprintobj(char *s, int n, fz_obj *obj, int tight) +{ + struct fmt fmt; + + fmt.indent = 0; + fmt.col = 0; + fmt.sep = 0; + fmt.last = 0; + + fmt.tight = tight; + fmt.buf = s; + fmt.cap = n; + fmt.len = 0; + fmtobj(&fmt, obj); + + if (fmt.buf && fmt.len < fmt.cap) + fmt.buf[fmt.len] = '\0'; + + return fmt.len; +} + +void +fz_debugobj(fz_obj *obj) +{ + char buf[1024]; + char *ptr; + int n; + + n = fz_sprintobj(nil, 0, obj, 0); + if (n < sizeof buf) + { + fz_sprintobj(buf, sizeof buf, obj, 0); + fwrite(buf, 1, n, stdout); + } + else + { + ptr = fz_malloc(n); + if (!ptr) + return; + fz_sprintobj(ptr, n, obj, 0); + fwrite(ptr, 1, n, stdout); + fz_free(ptr); + } +} + diff --git a/stream/obj_simple.c b/stream/obj_simple.c new file mode 100644 index 00000000..131e11b4 --- /dev/null +++ b/stream/obj_simple.c @@ -0,0 +1,297 @@ +#include <fitz.h> + +extern void fz_droparray(fz_obj *array); +extern void fz_dropdict(fz_obj *dict); + +#define NEWOBJ(KIND,SIZE) \ + fz_obj *o; \ + o = *op = fz_malloc(SIZE); \ + if (!o) return fz_outofmem; \ + o->refs = 1; \ + o->kind = KIND; \ + +fz_error * +fz_newnull(fz_obj **op) +{ + NEWOBJ(FZ_NULL, sizeof (fz_obj)); + return nil; +} + +fz_error * +fz_newbool(fz_obj **op, int b) +{ + NEWOBJ(FZ_BOOL, sizeof (fz_obj)); + o->u.b = b; + return nil; +} + +fz_error * +fz_newint(fz_obj **op, int i) +{ + NEWOBJ(FZ_INT, sizeof (fz_obj)); + o->u.i = i; + return nil; +} + +fz_error * +fz_newreal(fz_obj **op, float f) +{ + NEWOBJ(FZ_REAL, sizeof (fz_obj)); + o->u.f = f; + return nil; +} + +fz_error * +fz_newstring(fz_obj **op, char *str, int len) +{ + NEWOBJ(FZ_STRING, offsetof(fz_obj, u.s.buf) + len + 1); + o->u.s.len = len; + memcpy(o->u.s.buf, str, len); + o->u.s.buf[len] = '\0'; + return nil; +} + +fz_error * +fz_newname(fz_obj **op, char *str) +{ + NEWOBJ(FZ_NAME, offsetof(fz_obj, u.n) + strlen(str) + 1); + strcpy(o->u.n, str); + return nil; +} + +fz_error * +fz_newindirect(fz_obj **op, int objid, int genid) +{ + NEWOBJ(FZ_INDIRECT, sizeof (fz_obj)); + o->u.r.oid = objid; + o->u.r.gid = genid; + return nil; +} + +fz_error * +fz_newpointer(fz_obj **op, void *p) +{ + NEWOBJ(FZ_POINTER, sizeof (fz_obj)); + o->u.p = p; + return nil; +} + +fz_obj * +fz_keepobj(fz_obj *o) +{ + assert(o != nil); + o->refs ++; + return o; +} + +void +fz_dropobj(fz_obj *o) +{ + assert(o != nil); + if (--o->refs == 0) + { + if (o->kind == FZ_ARRAY) + fz_droparray(o); + else if (o->kind == FZ_DICT) + fz_dropdict(o); + else + fz_free(o); + } +} + +int +fz_isnull(fz_obj *obj) +{ + return obj ? obj->kind == FZ_NULL : 0; +} + +int +fz_isbool(fz_obj *obj) +{ + return obj ? obj->kind == FZ_BOOL : 0; +} + +int +fz_isint(fz_obj *obj) +{ + return obj ? obj->kind == FZ_INT : 0; +} + +int +fz_isreal(fz_obj *obj) +{ + return obj ? obj->kind == FZ_REAL : 0; +} + +int +fz_isstring(fz_obj *obj) +{ + return obj ? obj->kind == FZ_STRING : 0; +} + +int +fz_isname(fz_obj *obj) +{ + return obj ? obj->kind == FZ_NAME : 0; +} + +int +fz_isarray(fz_obj *obj) +{ + return obj ? obj->kind == FZ_ARRAY : 0; +} + +int +fz_isdict(fz_obj *obj) +{ + return obj ? obj->kind == FZ_DICT : 0; +} + +int +fz_isindirect(fz_obj *obj) +{ + return obj ? obj->kind == FZ_INDIRECT : 0; +} + +int +fz_ispointer(fz_obj *obj) +{ + return obj ? obj->kind == FZ_POINTER : 0; +} + +int +fz_tobool(fz_obj *obj) +{ + if (fz_isbool(obj)) + return obj->u.b; + return 0; +} + +int +fz_toint(fz_obj *obj) +{ + if (fz_isint(obj)) + return obj->u.i; + if (fz_isreal(obj)) + return obj->u.f; + return 0; +} + +float +fz_toreal(fz_obj *obj) +{ + if (fz_isreal(obj)) + return obj->u.f; + if (fz_isint(obj)) + return obj->u.i; + return 0; +} + +char * +fz_toname(fz_obj *obj) +{ + if (fz_isname(obj)) + return obj->u.n; + return ""; +} + +char * +fz_tostrbuf(fz_obj *obj) +{ + if (fz_isstring(obj)) + return obj->u.s.buf; + return ""; +} + +int +fz_tostrlen(fz_obj *obj) +{ + if (fz_isstring(obj)) + return obj->u.s.len; + return 0; +} + +int +fz_tonum(fz_obj *obj) +{ + if (fz_isindirect(obj)) + return obj->u.r.oid; + return 0; +} + +int +fz_togen(fz_obj *obj) +{ + if (fz_isindirect(obj)) + return obj->u.r.gid; + return 0; +} + +void * +fz_topointer(fz_obj *obj) +{ + if (fz_ispointer(obj)) + return obj->u.p; + return nil; +} + +fz_error * +fz_newnamefromstring(fz_obj **op, fz_obj *str) +{ + NEWOBJ(FZ_NAME, offsetof(fz_obj, u.n) + fz_tostrlen(str) + 1); + memcpy(o->u.n, fz_tostrbuf(str), fz_tostrlen(str)); + o->u.n[fz_tostrlen(str)] = '\0'; + return nil; +} + +int +fz_objcmp(fz_obj *a, fz_obj *b) +{ + int i; + + if (a == b) + return 0; + if (a->kind != b->kind) + return 1; + + switch (a->kind) + { + case FZ_NULL: return 0; + case FZ_BOOL: return a->u.b - b->u.b; + case FZ_INT: return a->u.i - b->u.i; + case FZ_REAL: return a->u.f - b->u.f; + case FZ_STRING: + if (a->u.s.len != b->u.s.len) + return a->u.s.len - b->u.s.len; + return memcmp(a->u.s.buf, b->u.s.buf, a->u.s.len); + case FZ_NAME: + return strcmp(a->u.n, b->u.n); + + case FZ_INDIRECT: + if (a->u.r.oid == b->u.r.oid) + return a->u.r.gid - b->u.r.gid; + return a->u.r.oid - b->u.r.oid; + + case FZ_ARRAY: + if (a->u.a.len != b->u.a.len) + return a->u.a.len - b->u.a.len; + for (i = 0; i < a->u.a.len; i++) + if (fz_objcmp(a->u.a.items[i], b->u.a.items[i])) + return 1; + return 0; + + case FZ_DICT: + if (a->u.d.len != b->u.d.len) + return a->u.d.len - b->u.d.len; + for (i = 0; i < a->u.d.len; i++) + { + if (fz_objcmp(a->u.d.items[i].k, b->u.d.items[i].k)) + return 1; + if (fz_objcmp(a->u.d.items[i].v, b->u.d.items[i].v)) + return 1; + } + return 0; + } + return 1; +} + |