diff options
121 files changed, 27634 insertions, 0 deletions
diff --git a/Jamfile b/Jamfile new file mode 100644 index 00000000..6e335c98 --- /dev/null +++ b/Jamfile @@ -0,0 +1,129 @@ +# +# Jamfile for Fitz +# + +include $(HOME)/.jamrules ; + +HDRS = include ; + +Library libfitz : + # util stuff + #util/cleanname.c + #util/getopt.c + #util/strsep.c + #util/strlcpy.c + #util/strlcat.c + + # base runtime + base/error.c + base/memory.c + base/md5.c + base/arc4.c + base/rect.c + base/matrix.c + base/hash.c + + # object + object/simple.c + object/array.c + object/dict.c + object/print.c + object/parse.c + + # filter + filter/buffer.c + filter/filter.c + filter/file.c # unistd.h + + filter/null.c + filter/arc4filter.c + filter/pipeline.c + + filter/ahxd.c + filter/ahxe.c + filter/a85d.c + filter/a85e.c + filter/rld.c + filter/rle.c + filter/predict.c + filter/lzwd.c + filter/lzwe.c + + filter/faxd.c + filter/faxdtab.c + filter/faxe.c + filter/faxetab.c + + filter/flate.c # libz + filter/dctd.c # libjpeg + filter/dcte.c # libjpeg + filter/jbig2d.c # jbig2dec + filter/jpxd.c # jasper + + # tree + tree/tree.c + tree/debug.c + tree/cmap.c + tree/font.c + + tree/node.c + tree/meta.c + tree/over.c + tree/mask.c + tree/blend.c + tree/form.c + tree/transform.c + tree/solid.c + tree/image.c + tree/text.c + tree/path.c + + # render + render/glyphcache.c + render/pixmap.c + render/edgelist.c + render/scanconv.c + render/fill.c + render/stroke.c + render/render.c +; + +Library libmupdf : + mupdf/lex.c + mupdf/parse.c + mupdf/crypt.c + mupdf/stream.c + + mupdf/open.c + mupdf/repair.c + mupdf/save.c + mupdf/xref.c + + mupdf/cmap.c + mupdf/font.c + mupdf/fontfile.c + mupdf/resources.c + mupdf/pagetree.c + mupdf/build.c + mupdf/interpret.c + ; + +LINKLIBS = + -lfontconfig + -lfreetype + -ljbig2dec + -ljasper + -ljpeg + -lz + -lm + ; + +Main t-mupdf : test/mupdf.c ; +LinkLibraries t-mupdf : libmupdf libfitz ; + +#Main t-filter : tests/t-filter.c ; +#Main t-parse : tests/t-parse.c ; +#Main t-scanconv : tests/t-scanconv.c ; +#Main tiger : tests/tiger.c ; +#LinkLibraries t-filter t-parse t-scanconv t-fontrast tiger : libfitz ; + diff --git a/base/arc4.c b/base/arc4.c new file mode 100644 index 00000000..86c9afd8 --- /dev/null +++ b/base/arc4.c @@ -0,0 +1,95 @@ +/* This code illustrates a sample implementation + * of the Arcfour algorithm + * Copyright (c) April 29, 1997 Kalle Kaukonen. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that this copyright + * notice and disclaimer are retained. + * + * THIS SOFTWARE IS PROVIDED BY KALLE KAUKONEN AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KALLE + * KAUKONEN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <fitz.h> + +void +fz_arc4init(fz_arc4 *arc4, unsigned char *key, unsigned keylen) +{ + unsigned int t, u; + unsigned int keyindex; + unsigned int stateindex; + unsigned char *state; + unsigned int counter; + + state = arc4->state; + + arc4->x = 0; + arc4->y = 0; + + for (counter = 0; counter < 256; counter++) { + state[counter] = counter; + } + + keyindex = 0; + stateindex = 0; + + for (counter = 0; counter < 256; counter++) { + t = state[counter]; + stateindex = (stateindex + key[keyindex] + t) & 0xff; + u = state[stateindex]; + + state[stateindex] = t; + state[counter] = u; + + if (++keyindex >= keylen) { + keyindex = 0; + } + } +} + +unsigned char +fz_arc4next(fz_arc4 *arc4) +{ + unsigned int x; + unsigned int y; + unsigned int sx, sy; + unsigned char *state; + + state = arc4->state; + + x = (arc4->x + 1) & 0xff; + sx = state[x]; + y = (sx + arc4->y) & 0xff; + sy = state[y]; + + arc4->x = x; + arc4->y = y; + + state[y] = sx; + state[x] = sy; + + return state[(sx + sy) & 0xff]; +} + +void +fz_arc4encrypt(fz_arc4 *arc4, unsigned char *dest, unsigned char *src, unsigned len) +{ + unsigned int i; + for (i = 0; i < len; i++) { + unsigned char x; + x = fz_arc4next(arc4); + dest[i] = src[i] ^ x; + } +} + diff --git a/base/error.c b/base/error.c new file mode 100644 index 00000000..bbba9ac3 --- /dev/null +++ b/base/error.c @@ -0,0 +1,50 @@ +#include <fitz.h> + +void +fz_warn(char *fmt, ...) +{ + va_list ap; + fprintf(stderr, "warning: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +fz_error * +fz_throw0(const char *func, const char *file, int line, char *fmt, ...) +{ + va_list ap; + fz_error *eo; + + eo = fz_malloc(sizeof(fz_error)); + if (!eo) return fz_outofmem; + + strlcpy(eo->func, func, sizeof eo->func); + strlcpy(eo->file, file, sizeof eo->file); + eo->line = line; + + va_start(ap, fmt); + vsnprintf(eo->msg, sizeof eo->msg, fmt, ap); + eo->msg[sizeof(eo->msg) - 1] = '\0'; + va_end(ap); + + return eo; +} + +void +fz_freeerror(fz_error *eo) +{ + if (!eo->frozen) + fz_free(eo); +} + +void +fz_abort(fz_error *eo) +{ + fflush(stdout); + fprintf(stderr, "%s:%d: %s(): %s\n", eo->file, eo->line, eo->func, eo->msg); + fflush(stderr); + abort(); +} + diff --git a/base/hash.c b/base/hash.c new file mode 100644 index 00000000..4b85b6b3 --- /dev/null +++ b/base/hash.c @@ -0,0 +1,263 @@ +/* Linear probe hash table. + * 2004 (C) Tor Andersson. + * BSD license. + * + * Simple hashtable with open adressing linear probe. + * Unlike text book examples, removing entries works + * correctly in this implementation so it wont start + * exhibiting bad behaviour if entries are inserted + * and removed frequently. + */ + +#include <fitz.h> + +enum { MAXKEYLEN = 16 }; + +typedef struct fz_hashentry_s fz_hashentry; + +struct fz_hashentry_s +{ + unsigned char key[MAXKEYLEN]; + void *val; +}; + +struct fz_hashtable_s +{ + unsigned keylen; + unsigned size; + unsigned load; + fz_hashentry *ents; +}; + +static unsigned hash(unsigned char *s, int len) +{ + unsigned hash = 0; + unsigned i; + for (i = 0; i < len; i++) + { + hash += s[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; +} + +fz_error * +fz_newhash(fz_hashtable **tablep, int initialsize, int keylen) +{ + fz_hashtable *table; + + assert(keylen <= MAXKEYLEN); + + table = *tablep = fz_malloc(sizeof(fz_hashtable)); + if (!table) + return fz_outofmem; + + table->keylen = keylen; + table->size = initialsize; + table->load = 0; + + table->ents = fz_malloc(sizeof(fz_hashentry) * table->size); + if (!table->ents) + { + fz_free(table); + *tablep = nil; + return fz_outofmem; + } + + memset(table->ents, 0, sizeof(fz_hashentry) * table->size); + + return nil; +} + +int +fz_hashlen(fz_hashtable *table) +{ + return table->size; +} + +void * +fz_gethashkey(fz_hashtable *table, int idx) +{ + return table->ents[idx].key; +} + +void * +fz_gethashval(fz_hashtable *table, int idx) +{ + return table->ents[idx].val; +} + +void +fz_freehash(fz_hashtable *table) +{ + fz_free(table->ents); + fz_free(table); +} + +fz_error * +fz_resizehash(fz_hashtable *table, int newsize) +{ + fz_error *error; + fz_hashentry *newents; + fz_hashentry *oldents; + unsigned oldload; + unsigned oldsize; + unsigned i; + + oldsize = table->size; + oldload = table->load; + oldents = table->ents; + + if (newsize < oldload * 8 / 10) + return fz_throw("rangecheck: resize hash too small"); + + newents = fz_realloc(table->ents, sizeof(fz_hashentry) * newsize); + if (!newents) + return fz_outofmem; + + table->size = newsize; + table->load = 0; + table->ents = newents; + memset(table->ents, 0, sizeof(fz_hashentry) * table->size); + + for (i = 0; i < oldsize; i++) + { + if (oldents[i].val) + { + error = fz_hashinsert(table, oldents[i].key, oldents[i].val); + if (error) + { + table->size = oldsize; + table->load = oldload; + table->ents = oldents; + fz_free(newents); + return error; + } + } + } + + fz_free(oldents); + return nil; +} + +void * +fz_hashfind(fz_hashtable *table, void *key) +{ + fz_hashentry *ents = table->ents; + unsigned size = table->size; + unsigned pos = hash(key, table->keylen) % size; + + while (1) + { + if (!ents[pos].val) + return nil; + + if (memcmp(key, &ents[pos].key, table->keylen) == 0) + return ents[pos].val; + + pos = (pos + 1) % size; + } +} + +fz_error * +fz_hashinsert(fz_hashtable *table, void *key, void *val) +{ + fz_error *error; + fz_hashentry *ents = table->ents; + unsigned size = table->size; + unsigned pos = hash(key, table->keylen) % size; + + if (table->load > table->size * 8 / 10) + { + error = fz_resizehash(table, table->size * 2); + if (error) + return error; + } + + while (1) + { + if (!ents[pos].val) + { + memcpy(ents[pos].key, key, table->keylen); + ents[pos].val = val; + table->load ++; + return nil; + } + + if (memcmp(key, &ents[pos].key, table->keylen) == 0) + return fz_throw("rangecheck: overwrite hash slot"); + + pos = (pos + 1) % size; + } + + return nil; +} + +fz_error * +fz_hashremove(fz_hashtable *table, void *key) +{ + fz_hashentry *ents = table->ents; + unsigned size = table->size; + unsigned pos = hash(key, table->keylen) % size; + unsigned hole, look, code; + + while (1) + { + if (!ents[pos].val) + return fz_throw("rangecheck: remove inexistant hash entry"); + + if (memcmp(key, &ents[pos].key, table->keylen) == 0) + { + ents[pos].val = nil; + + hole = pos; + look = (hole + 1) % size; + + while (ents[look].val) + { + code = hash(ents[look].key, table->keylen) % size; + if ((code <= hole && hole < look) || + (look < code && code <= hole) || + (hole < look && look < code)) + { + ents[hole] = ents[look]; + ents[look].val = nil; + hole = look; + } + + look = (look + 1) % size; + } + + table->load --; + return nil; + } + + pos = (pos + 1) % size; + } +} + +void +fz_debughash(fz_hashtable *table) +{ + int i, k; + + printf("cache load %d / %d\n", table->load, table->size); + + for (i = 0; i < table->size; i++) + { + if (!table->ents[i].val) + printf("table % 4d: empty\n", i); + else + { + printf("table % 4d: key=", i); + for (k = 0; k < MAXKEYLEN; k++) + printf("%02x", ((char*)table->ents[i].key)[k]); + printf(" val=$%p\n", table->ents[i].val); + } + } +} + diff --git a/base/matrix.c b/base/matrix.c new file mode 100644 index 00000000..70193427 --- /dev/null +++ b/base/matrix.c @@ -0,0 +1,70 @@ +#include <fitz.h> + +fz_matrix +fz_concat(fz_matrix one, fz_matrix two) +{ + fz_matrix dst; + dst.a = one.a * two.a + one.b * two.c; + dst.b = one.a * two.b + one.b * two.d; + dst.c = one.c * two.a + one.d * two.c; + dst.d = one.c * two.b + one.d * two.d; + dst.e = one.e * two.a + one.f * two.c + two.e; + dst.f = one.e * two.b + one.f * two.d + two.f; + return dst; +} + +fz_matrix +fz_identity(void) +{ + return (fz_matrix) { 1, 0, 0, 1, 0, 0 }; +} + +fz_matrix +fz_scale(float sx, float sy) +{ + return (fz_matrix) { sx, 0, 0, sy, 0, 0 }; +} + +fz_matrix +fz_rotate(float theta) +{ + float s = sin(theta * M_PI / 180.0); + float c = cos(theta * M_PI / 180.0); + return (fz_matrix) { c, s, -s, c, 0 ,0 }; +} + +fz_matrix +fz_translate(float tx, float ty) +{ + return (fz_matrix) { 1, 0, 0, 1, tx, ty }; +} + +fz_matrix +fz_invertmatrix(fz_matrix src) +{ + fz_matrix dst; + float rdet = 1.0 / (src.a * src.d - src.b * src.c); + dst.a = src.d * rdet; + dst.b = -src.b * rdet; + dst.c = -src.c * rdet; + dst.d = src.a * rdet; + dst.e = -src.e * dst.a - src.f * dst.c; + dst.f = -src.e * dst.b - src.f * dst.d; + return dst; +} + +int +fz_isrectilinear(fz_matrix m) +{ + return (fabs(m.b) < FLT_EPSILON && fabs(m.c) < FLT_EPSILON) || + (fabs(m.a) < FLT_EPSILON && fabs(m.d) < FLT_EPSILON); +} + +fz_point +fz_transformpoint(fz_matrix m, fz_point p) +{ + float x = p.x * m.a + p.y * m.c + m.e; + float y = p.x * m.b + p.y * m.d + m.f; + return (fz_point) { x, y }; +} + diff --git a/base/md5.c b/base/md5.c new file mode 100644 index 00000000..648532a5 --- /dev/null +++ b/base/md5.c @@ -0,0 +1,269 @@ +/* +MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + +Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. +All rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. +*/ + +#include <fitz.h> + +/* Constants for MD5Transform routine */ +enum +{ + S11 = 7, S12 = 12, S13 = 17, S14 = 22, + S21 = 5, S22 = 9, S23 = 14, S24 = 20, + S31 = 4, S32 = 11, S33 = 16, S34 = 23, + S41 = 6, S42 = 10, S43 = 15, S44 = 21, +}; + +static void encode(unsigned char *, unsigned long *, unsigned); +static void decode(unsigned long *, unsigned char *, unsigned); +static void transform(unsigned long state[4], unsigned char block[64]); + +static unsigned char padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE rotates x left n bits */ +#define ROTATE(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. + * Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (unsigned long)(ac); \ + (a) = ROTATE ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (unsigned long)(ac); \ + (a) = ROTATE ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (unsigned long)(ac); \ + (a) = ROTATE ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (unsigned long)(ac); \ + (a) = ROTATE ((a), (s)); \ + (a) += (b); \ + } + +static void encode(unsigned char *output, unsigned long *input, unsigned len) +{ + unsigned i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +static void decode(unsigned long *output, unsigned char *input, unsigned len) +{ + unsigned i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[i] = ((unsigned long)input[j]) | + (((unsigned long)input[j+1]) << 8) | + (((unsigned long)input[j+2]) << 16) | + (((unsigned long)input[j+3]) << 24); + } +} + +static void transform(unsigned long state[4], unsigned char block[64]) +{ + unsigned long a = state[0]; + unsigned long b = state[1]; + unsigned long c = state[2]; + unsigned long d = state[3]; + unsigned long x[16]; + + decode(x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information */ + memset(x, 0, sizeof (x)); +} + +/* MD5 initialization. Begins an MD5 operation, writing a new context. */ +void fz_md5init(fz_md5 *context) +{ + context->count[0] = context->count[1] = 0; + + /* Load magic initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest operation, + * processing another message block, and updating the context. + */ +void fz_md5update(fz_md5 *context, unsigned char *input, unsigned inlen) +{ + unsigned i, index, partlen; + + /* Compute number of bytes mod 64 */ + index = (unsigned)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + context->count[0] += (unsigned long) inlen << 3; + if (context->count[0] < (unsigned long) inlen << 3) + context->count[1] ++; + context->count[1] += (unsigned long) inlen >> 29; + + partlen = 64 - index; + + /* Transform as many times as possible. */ + if (inlen >= partlen) { + memcpy(context->buffer + index, input, partlen); + transform(context->state, context->buffer); + + for (i = partlen; i + 63 < inlen; i += 64) + transform(context->state, input + i); + + index = 0; + } + else { + i = 0; + } + + /* Buffer remaining input */ + memcpy(context->buffer + index, input + i, inlen - i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + * the message digest and zeroizing the context. + */ +void fz_md5final(fz_md5 *context, unsigned char digest[16]) +{ + unsigned char bits[8]; + unsigned index, padlen; + + /* Save number of bits */ + encode(bits, context->count, 8); + + /* Pad out to 56 mod 64 */ + index = (unsigned)((context->count[0] >> 3) & 0x3f); + padlen = index < 56 ? 56 - index : 120 - index; + fz_md5update(context, padding, padlen); + + /* Append length (before padding) */ + fz_md5update(context, bits, 8); + + /* Store state in digest */ + encode(digest, context->state, 16); + + /* Zeroize sensitive information */ + memset(context, 0, sizeof(fz_md5)); +} + diff --git a/base/memory.c b/base/memory.c new file mode 100644 index 00000000..88102d00 --- /dev/null +++ b/base/memory.c @@ -0,0 +1,81 @@ +#include <fitz.h> + +/* Make this thread local storage if you wish. */ + +static void *stdmalloc(fz_memorycontext *mem, int n) +{ +#if 0 + void *p = malloc(n); + if (!p) + fprintf(stderr, "failed to malloc %d bytes\n", n); + return p; +#else + return malloc(n); +#endif +} + +static void *stdrealloc(fz_memorycontext *mem, void *p, int n) +{ +#if 0 + void *np = realloc(p, n); + if (np == nil) + fprintf(stderr, "realloc failed %d nytes", n); + else if (np == p) + fprintf(stderr, "realloc kept %d\n", n); + else + fprintf(stderr, "realloc moved %d\n", n); + return np; +#else + return realloc(p, n); +#endif +} + +static void stdfree(fz_memorycontext *mem, void *p) +{ + free(p); +} + +static fz_memorycontext defmem = { stdmalloc, stdrealloc, stdfree }; +static fz_memorycontext *curmem = &defmem; + +fz_error fz_koutofmem = { + .msg = {"out of memory"}, + .func = {"<malloc>"}, + .file = {"memory.c"}, + .line = 0, + .frozen = 1 +}; + +fz_memorycontext * +fz_currentmemorycontext() +{ + return curmem; +} + +void +fz_setmemorycontext(fz_memorycontext *mem) +{ + curmem = mem; +} + +void * +fz_malloc(int n) +{ + fz_memorycontext *mem = fz_currentmemorycontext(); + return mem->malloc(mem, n); +} + +void * +fz_realloc(void *p, int n) +{ + fz_memorycontext *mem = fz_currentmemorycontext(); + return mem->realloc(mem, p, n); +} + +void +fz_free(void *p) +{ + fz_memorycontext *mem = fz_currentmemorycontext(); + mem->free(mem, p); +} + diff --git a/base/rect.c b/base/rect.c new file mode 100644 index 00000000..cb15d434 --- /dev/null +++ b/base/rect.c @@ -0,0 +1,46 @@ +#include <fitz.h> + +fz_rect +fz_intersectrects(fz_rect a, fz_rect b) +{ + fz_rect r; + r.min.x = MAX(a.min.x, b.min.x); + r.min.y = MAX(a.min.y, b.min.y); + r.max.x = MIN(a.max.x, b.max.x); + r.max.y = MIN(a.max.y, b.max.y); + return r; +} + +fz_rect +fz_mergerects(fz_rect a, fz_rect b) +{ + fz_rect r; + r.min.x = MIN(a.min.x, b.min.x); + r.min.y = MIN(a.min.y, b.min.y); + r.max.x = MAX(a.max.x, b.max.x); + r.max.y = MAX(a.max.y, b.max.y); + return r; +} + +fz_irect +fz_intersectirects(fz_irect a, fz_irect b) +{ + fz_irect r; + r.min.x = MAX(a.min.x, b.min.x); + r.min.y = MAX(a.min.y, b.min.y); + r.max.x = MIN(a.max.x, b.max.x); + r.max.y = MIN(a.max.y, b.max.y); + return r; +} + +fz_irect +fz_mergeirects(fz_irect a, fz_irect b) +{ + fz_irect r; + r.min.x = MIN(a.min.x, b.min.x); + r.min.y = MIN(a.min.y, b.min.y); + r.max.x = MAX(a.max.x, b.max.x); + r.max.y = MAX(a.max.y, b.max.y); + return r; +} + diff --git a/filter/TODO b/filter/TODO new file mode 100644 index 00000000..404d76c2 --- /dev/null +++ b/filter/TODO @@ -0,0 +1,8 @@ +validate ahxd pushback +go through eof responsibility +be more defensive of api user errors +flate needin/needout + +jbig2 rewrite +dctencode params + diff --git a/filter/a85d.c b/filter/a85d.c new file mode 100644 index 00000000..08c96a1b --- /dev/null +++ b/filter/a85d.c @@ -0,0 +1,129 @@ +#include <fitz.h> + +typedef struct fz_a85d_s fz_a85d; + +struct fz_a85d_s +{ + fz_filter super; + unsigned long word; + int count; +}; + +static inline int iswhite(int a) +{ + switch (a) { + case '\n': case '\r': case '\t': case ' ': + case '\0': case '\f': case '\b': case 0177: + return 1; + } + return 0; +} + +fz_error * +fz_newa85d(fz_filter **fp, fz_obj *params) +{ + FZ_NEWFILTER(fz_a85d, f, a85d); + f->word = 0; + f->count = 0; + return nil; +} + +void +fz_freea85d(fz_filter *f) +{ + fz_free(f); +} + +fz_error * +fz_processa85d(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_a85d *f = (fz_a85d*)filter; + int c; + + while (1) + { + if (in->rp == in->wp) + return fz_ioneedin; + + c = *in->rp++; + + if (c >= '!' && c <= 'u') { + if (f->count == 4) { + if (out->wp + 4 > out->ep) { + in->rp --; + return fz_ioneedout; + } + + f->word = f->word * 85 + (c - '!'); + + *out->wp++ = (f->word >> 24) & 0xff; + *out->wp++ = (f->word >> 16) & 0xff; + *out->wp++ = (f->word >> 8) & 0xff; + *out->wp++ = (f->word) & 0xff; + + f->word = 0; + f->count = 0; + } + else { + f->word = f->word * 85 + (c - '!'); + f->count ++; + } + } + + else if (c == 'z' && f->count == 0) { + if (out->wp + 4 > out->ep) { + in->rp --; + return fz_ioneedout; + } + *out->wp++ = 0; + *out->wp++ = 0; + *out->wp++ = 0; + *out->wp++ = 0; + } + + else if (c == '~') { + if (in->rp == in->wp) { + in->rp --; + return fz_ioneedin; + } + + c = *in->rp++; + + if (c != '>') { + return fz_throw("ioerror: bad eod marker in a85d"); + } + + if (out->wp + f->count - 1 > out->ep) { + in->rp -= 2; + return fz_ioneedout; + } + + switch (f->count) { + case 0: + break; + case 1: + return fz_throw("ioerror: partial final byte in a85d"); + case 2: + f->word = f->word * (85L * 85 * 85) + 0xffffffL; + goto o1; + case 3: + f->word = f->word * (85L * 85) + 0xffffL; + goto o2; + case 4: + f->word = f->word * 85 + 0xffL; + *(out->wp+2) = f->word >> 8; +o2: *(out->wp+1) = f->word >> 16; +o1: *(out->wp+0) = f->word >> 24; + out->wp += f->count - 1; + break; + } + out->eof = 1; + return fz_iodone; + } + + else if (!iswhite(c)) { + return fz_throw("ioerror: bad data in a85d: '%c'", c); + } + } +} + diff --git a/filter/a85e.c b/filter/a85e.c new file mode 100644 index 00000000..c57aac4f --- /dev/null +++ b/filter/a85e.c @@ -0,0 +1,128 @@ +#include <fitz.h> + +typedef struct fz_a85e_s fz_a85e; + +struct fz_a85e_s +{ + fz_filter super; + int c; +}; + +fz_error * +fz_newa85e(fz_filter **fp, fz_obj *params) +{ + FZ_NEWFILTER(fz_a85e, f, a85e); + f->c = 0; + return nil; +} + +void +fz_freea85e(fz_filter *f) +{ + fz_free(f); +} + +fz_error * +fz_processa85e(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_a85e *f = (fz_a85e*)filter; + unsigned long word; + int count; + int n; + + n = 0; + + while (1) + { + if (f->c >= 70) { + if (out->wp + 1 > out->ep) + return fz_ioneedout; + *out->wp++ = '\n'; + f->c = 0; + n ++; + } + + if (in->rp + 4 <= in->wp) + { + word = (in->rp[0] << 24) | + (in->rp[1] << 16) | + (in->rp[2] << 8) | + (in->rp[3]); + if (word == 0) { + if (out->wp + 1 > out->ep) + return fz_ioneedout; + *out->wp++ = 'z'; + f->c ++; + n ++; + } + else { + unsigned long v1, v2, v3, v4; + + if (out->wp + 5 > out->ep) + return fz_ioneedout; + + v4 = word / 85; + v3 = v4 / 85; + v2 = v3 / 85; + v1 = v2 / 85; + + *out->wp++ = (v1 % 85) + '!'; + *out->wp++ = (v2 % 85) + '!'; + *out->wp++ = (v3 % 85) + '!'; + *out->wp++ = (v4 % 85) + '!'; + *out->wp++ = (word % 85) + '!'; + f->c += 5; + n += 5; + } + in->rp += 4; + } + + else if (in->eof) + { + unsigned long divisor; + + if (in->rp == in->wp) + goto needinput; /* handle clean eof here */ + + count = in->wp - in->rp; + + if (out->wp + count + 3 > out->ep) + return fz_ioneedout; + + word = 0; + switch (count) { + case 3: word |= in->rp[2] << 8; + case 2: word |= in->rp[1] << 16; + case 1: word |= in->rp[0] << 24; + } + in->rp += count; + + divisor = 85L * 85 * 85 * 85; + while (count-- >= 0) { + *out->wp++ = ((word / divisor) % 85) + '!'; + divisor /= 85; + } + + *out->wp++ = '~'; + *out->wp++ = '>'; + out->eof = 1; + return fz_iodone; + } + + else { + goto needinput; + } + } + +needinput: + if (in->eof) { + if (out->wp + 2 > out->ep) + return fz_ioneedout; + *out->wp++ = '~'; + *out->wp++ = '>'; + out->eof = 1; + return fz_iodone; + } + return fz_ioneedin; +} + diff --git a/filter/ahxd.c b/filter/ahxd.c new file mode 100644 index 00000000..b557399c --- /dev/null +++ b/filter/ahxd.c @@ -0,0 +1,113 @@ +#include <fitz.h> + +typedef struct fz_ahxd_s fz_ahxd; + +struct fz_ahxd_s +{ + fz_filter super; + int odd; + int a; +}; + +static inline int iswhite(int a) +{ + switch (a) { + case '\n': case '\r': case '\t': case ' ': + case '\0': case '\f': case '\b': case 0177: + return 1; + } + return 0; +} + +static inline int ishex(int a) +{ + return (a >= 'A' && a <= 'F') || + (a >= 'a' && a <= 'f') || + (a >= '0' && a <= '9'); +} + +static inline int fromhex(int a) +{ + if (a >= 'A' && a <= 'F') + return a - 'A' + 0xA; + if (a >= 'a' && a <= 'f') + return a - 'a' + 0xA; + if (a >= '0' && a <= '9') + return a - '0'; + return 0; +} + +fz_error * +fz_newahxd(fz_filter **fp, fz_obj *params) +{ + FZ_NEWFILTER(fz_ahxd, f, ahxd); + f->odd = 0; + f->a = 0; + return nil; +} + +void +fz_freeahxd(fz_filter *f) +{ + fz_free(f); +} + +fz_error * +fz_processahxd(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_ahxd *f = (fz_ahxd*)filter; + int b, c; + + while (1) + { + if (in->rp == in->wp) + return fz_ioneedin; + + if (out->wp == out->ep) + return fz_ioneedout; + + c = *in->rp++; + + if (ishex(c)) { + if (!f->odd) { + f->a = fromhex(c); + f->odd = 1; + } + else { + b = fromhex(c); + *out->wp++ = (f->a << 4) | b; + f->odd = 0; + } + } + + else if (c == '>') { + if (f->odd) + *out->wp++ = (f->a << 4); + out->eof = 1; + return fz_iodone; + } + + else if (!iswhite(c)) { + return fz_throw("ioerror: bad data in ahxd: '%c'", c); + } + } +} + +void +fz_pushbackahxd(fz_filter *filter, fz_buffer *in, fz_buffer *out, int n) +{ + int k; + + assert(filter->process == fz_processahxd); + assert(out->wp - n >= out->rp); + + k = 0; + while (k < n * 2) { + in->rp --; + if (ishex(*in->rp)) + k ++; + } + + out->wp -= n; +} + diff --git a/filter/ahxe.c b/filter/ahxe.c new file mode 100644 index 00000000..e092653b --- /dev/null +++ b/filter/ahxe.c @@ -0,0 +1,65 @@ +#include <fitz.h> + +typedef struct fz_ahxe_s fz_ahxe; + +struct fz_ahxe_s +{ + fz_filter super; + int c; +}; + +static const char tohex[16] = "0123456789ABCDEF"; + +fz_error * +fz_newahxe(fz_filter **fp, fz_obj *params) +{ + FZ_NEWFILTER(fz_ahxe, f, ahxe); + f->c = 0; + return nil; +} + +void +fz_freeahxe(fz_filter *f) +{ + fz_free(f); +} + +fz_error * +fz_processahxe(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_ahxe *f = (fz_ahxe*)filter; + int a, b, c; + + while (1) + { + if (in->rp == in->wp) + goto needinput; + + if (out->wp + 2 >= out->ep) /* can write 3 bytes from 1 */ + return fz_ioneedout; + + c = *in->rp++; + a = tohex[(c >> 4) & 0x0f]; + b = tohex[c & 0x0f]; + + *out->wp++ = a; + *out->wp++ = b; + + f->c += 2; + if (f->c == 60) { + *out->wp++ = '\n'; + f->c = 0; + } + } + +needinput: + if (in->eof) { + if (out->wp == out->ep) + return fz_ioneedout; + *out->wp++ = '>'; + out->eof = 1; + return fz_iodone; + } + return fz_ioneedin; +} + diff --git a/filter/arc4filter.c b/filter/arc4filter.c new file mode 100644 index 00000000..257462d8 --- /dev/null +++ b/filter/arc4filter.c @@ -0,0 +1,47 @@ +#include <fitz.h> + +typedef struct fz_arc4c_s fz_arc4c; + +struct fz_arc4c_s +{ + fz_filter super; + fz_arc4 arc4; +}; + +fz_error * +fz_newarc4filter(fz_filter **fp, unsigned char *key, unsigned keylen) +{ + FZ_NEWFILTER(fz_arc4c, f, arc4filter); + fz_arc4init(&f->arc4, key, keylen); + return nil; +} + +void +fz_freearc4filter(fz_filter *f) +{ + fz_free(f); +} + +fz_error * +fz_processarc4filter(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_arc4c *f = (fz_arc4c*)filter; + int n; + + while (1) + { + if (in->rp + 1 > in->wp) { + if (in->eof) + return fz_iodone; + return fz_ioneedin; + } + if (out->wp + 1 > out->ep) + return fz_ioneedout; + + n = MIN(in->wp - in->rp, out->ep - out->wp); + fz_arc4encrypt(&f->arc4, out->wp, in->rp, n); + in->rp += n; + out->wp += n; + } +} + diff --git a/filter/buffer.c b/filter/buffer.c new file mode 100644 index 00000000..7b728b9b --- /dev/null +++ b/filter/buffer.c @@ -0,0 +1,57 @@ +#include <fitz.h> + +fz_error * +fz_newbuffer(fz_buffer **bp, int size) +{ + fz_buffer *b; + + b = *bp = fz_malloc(sizeof(fz_buffer)); + if (!b) return fz_outofmem; + + b->bp = fz_malloc(size); + if (!b->bp) { fz_free(b); return fz_outofmem; } + + b->rp = b->bp; + b->wp = b->bp; + b->ep = b->bp + size; + b->eof = 0; + + return nil; +} + +void +fz_freebuffer(fz_buffer *buf) +{ + fz_free(buf->bp); + fz_free(buf); +} + +fz_error * +fz_growbuffer(fz_buffer *buf) +{ + unsigned char *newbp; + + int rp = buf->rp - buf->bp; + int wp = buf->wp - buf->bp; + int ep = buf->ep - buf->bp; + + newbp = fz_realloc(buf->bp, ep * 2); + if (!newbp) return fz_outofmem; + + buf->bp = newbp; + buf->rp = buf->bp + rp; + buf->wp = buf->bp + wp; + buf->ep = buf->bp + ep * 2; + + return nil; +} + +fz_error * +fz_rewindbuffer(fz_buffer *buf) +{ + memmove(buf->bp, buf->rp, buf->wp - buf->rp); + buf->wp = buf->bp + (buf->wp - buf->rp); + buf->rp = buf->bp; + return nil; +} + diff --git a/filter/dctc.h b/filter/dctc.h new file mode 100644 index 00000000..8aa6aeb7 --- /dev/null +++ b/filter/dctc.h @@ -0,0 +1,39 @@ +/* + * Extend libjpegs error handler to use setjmp/longjmp + */ + +#include <jpeglib.h> + +#include <setjmp.h> + +struct myerrmgr +{ + struct jpeg_error_mgr super; + jmp_buf jb; + char msg[JMSG_LENGTH_MAX]; +}; + +static void myerrexit(j_common_ptr cinfo) +{ + struct myerrmgr *err = (struct myerrmgr *)cinfo->err; + char msgbuf[JMSG_LENGTH_MAX]; + err->super.format_message(cinfo, msgbuf); + strlcpy(err->msg, msgbuf, sizeof err->msg); + longjmp(err->jb, 1); +} + +static void myoutmess(j_common_ptr cinfo) +{ + struct myerrmgr *err = (struct myerrmgr *)cinfo->err; + char msgbuf[JMSG_LENGTH_MAX]; + err->super.format_message(cinfo, msgbuf); + fprintf(stderr, "ioerror: dct: %s", msgbuf); +} + +static void myiniterr(struct myerrmgr *err) +{ + jpeg_std_error(&err->super); + err->super.error_exit = myerrexit; + err->super.output_message = myoutmess; +} + diff --git a/filter/dctd.c b/filter/dctd.c new file mode 100644 index 00000000..dd978ac7 --- /dev/null +++ b/filter/dctd.c @@ -0,0 +1,215 @@ +#include <fitz.h> + +#include "dctc.h" + +typedef struct fz_dctd_s fz_dctd; + +struct mysrcmgr +{ + struct jpeg_source_mgr super; + fz_buffer *buf; + int skip; +}; + +struct fz_dctd_s +{ + fz_filter super; + struct jpeg_decompress_struct cinfo; + struct mysrcmgr src; + struct myerrmgr err; + int colortransform; + int stage; +}; + +static void myinitsource(j_decompress_ptr cinfo) { /* empty */ } +static boolean myfillinput(j_decompress_ptr cinfo) { return FALSE; } +static void mytermsource(j_decompress_ptr cinfo) { /* empty */ } + +static void myskipinput(j_decompress_ptr cinfo, long n) +{ + struct mysrcmgr *src = (struct mysrcmgr *)cinfo->src; + fz_buffer *in = src->buf; + + assert(src->skip == 0); + + in->rp = in->wp - src->super.bytes_in_buffer; + + if (in->rp + n > in->wp) { + src->skip = (in->rp + n) - in->wp; + in->rp = in->wp; + } + else { + src->skip = 0; + in->rp += n; + } + + src->super.bytes_in_buffer = in->wp - in->rp; + src->super.next_input_byte = in->rp; +} + +fz_error * +fz_newdctd(fz_filter **fp, fz_obj *params) +{ + fz_error *err; + fz_obj *obj; + int colortransform; + + FZ_NEWFILTER(fz_dctd, d, dctd); + + colortransform = 1; + + if (params) { + obj = fz_dictgets(params, "ColorTransform"); + if (obj) colortransform = fz_toint(obj); + } + + d->colortransform = colortransform; + d->stage = 0; + + /* setup error callback first thing */ + myiniterr(&d->err); + d->cinfo.err = (struct jpeg_error_mgr*) &d->err; + + if (setjmp(d->err.jb)) { + err = fz_throw("ioerror in dctd: %s", d->err.msg); + fz_free(d); + return err; + } + + /* create decompression object. this zeroes cinfo except for err. */ + jpeg_create_decompress(&d->cinfo); + + /* prepare source manager */ + d->cinfo.src = (struct jpeg_source_mgr *)&d->src; + d->src.super.init_source = myinitsource; + d->src.super.fill_input_buffer = myfillinput; + d->src.super.skip_input_data = myskipinput; + d->src.super.resync_to_restart = jpeg_resync_to_restart; + d->src.super.term_source = mytermsource; + + d->src.super.bytes_in_buffer = 0; + d->src.super.next_input_byte = nil; + d->src.skip = 0; + + return nil; +} + +void +fz_freedctd(fz_filter *filter) +{ + fz_dctd *d = (fz_dctd*)filter; + if (setjmp(d->err.jb)) { + fprintf(stderr, "ioerror in dct: jpeg_destroy_decompress: %s", d->err.msg); + return; + } + jpeg_destroy_decompress(&d->cinfo); +} + +fz_error * +fz_processdctd(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_dctd *d = (fz_dctd*)filter; + boolean b; + int i; + int stride; + JSAMPROW scanlines[1]; + + d->src.buf = in; + + /* skip any bytes left over from myskipinput() */ + if (d->src.skip > 0) { + if (in->rp + d->src.skip > in->wp) { + d->src.skip = (in->rp + d->src.skip) - in->wp; + in->rp = in->wp; + goto needinput; + } + else { + in->rp += d->src.skip; + d->src.skip = 0; + } + } + + d->src.super.bytes_in_buffer = in->wp - in->rp; + d->src.super.next_input_byte = in->rp; + + if (setjmp(d->err.jb)) { + return fz_throw("ioerror in dctd: %s", d->err.msg); + } + + switch (d->stage) + { + case 0: + i = jpeg_read_header(&d->cinfo, TRUE); + if (i == JPEG_SUSPENDED) + goto needinput; + + /* FIXME: default value if ColorTransform is not set */ + + if (!d->cinfo.saw_Adobe_marker) { + switch (d->cinfo.num_components) { + case 3: + if (d->colortransform) + d->cinfo.jpeg_color_space = JCS_YCbCr; + else + d->cinfo.jpeg_color_space = JCS_RGB; + break; + case 4: + if (d->colortransform) + d->cinfo.jpeg_color_space = JCS_YCCK; + else + d->cinfo.jpeg_color_space = JCS_CMYK; + break; + } + } + + /* fall through */ + d->stage = 1; + + case 1: + b = jpeg_start_decompress(&d->cinfo); + if (b == FALSE) + goto needinput; + + /* fall through */ + d->stage = 2; + + case 2: + stride = d->cinfo.output_width * d->cinfo.output_components; + + while (d->cinfo.output_scanline < d->cinfo.output_height) + { + if (out->wp + stride > out->ep) + goto needoutput; + + scanlines[0] = out->wp; + + i = jpeg_read_scanlines(&d->cinfo, scanlines, 1); + + if (i == 0) + goto needinput; + + out->wp += stride; + } + + /* fall through */ + d->stage = 3; + + case 3: + b = jpeg_finish_decompress(&d->cinfo); + if (b == FALSE) + goto needinput; + d->stage = 4; + out->eof = 1; + in->rp = in->wp - d->src.super.bytes_in_buffer; + return fz_iodone; + } + +needinput: + in->rp = in->wp - d->src.super.bytes_in_buffer; + return fz_ioneedin; + +needoutput: + in->rp = in->wp - d->src.super.bytes_in_buffer; + return fz_ioneedout; +} + diff --git a/filter/dcte.c b/filter/dcte.c new file mode 100644 index 00000000..52933789 --- /dev/null +++ b/filter/dcte.c @@ -0,0 +1,254 @@ +#include <fitz.h> + +#include "dctc.h" + +typedef struct fz_dcte_s fz_dcte; + +struct mydstmgr +{ + struct jpeg_destination_mgr super; + fz_buffer *buf; +}; + +struct fz_dcte_s +{ + fz_filter super; + + struct jpeg_compress_struct cinfo; + struct mydstmgr dst; + struct myerrmgr err; + int stage; + + int columns; + int rows; + int colors; +}; + +static void myinitdest(j_compress_ptr cinfo) { /* empty */ } +static boolean myemptybuf(j_compress_ptr cinfo) { return FALSE; } +static void mytermdest(j_compress_ptr cinfo) { /* empty */ } + +fz_error * +fz_newdcte(fz_filter **fp, fz_obj *params) +{ + fz_error *err; + fz_obj *obj; + int i; + + FZ_NEWFILTER(fz_dcte, e, dcte); + + e->stage = 0; + + obj = fz_dictgets(params, "Columns"); + if (!obj) { fz_free(e); return fz_throw("ioerror in dcte: missing Columns parameter"); } + e->columns = fz_toint(obj); + + obj = fz_dictgets(params, "Rows"); + if (!obj) { fz_free(e); return fz_throw("ioerror in dcte: missing Rows parameter"); } + e->rows = fz_toint(obj); + + obj = fz_dictgets(params, "Colors"); + if (!obj) { fz_free(e); return fz_throw("ioerror in dcte: missing Colors parameter"); } + e->colors = fz_toint(obj); + + /* setup error callback first thing */ + myiniterr(&e->err); + e->cinfo.err = (struct jpeg_error_mgr*) &e->err; + + if (setjmp(e->err.jb)) { + err = fz_throw("ioerror in dcte: %s", e->err.msg); + fz_free(e); + return err; + } + + jpeg_create_compress(&e->cinfo); + + /* prepare destination manager */ + e->cinfo.dest = (struct jpeg_destination_mgr *) &e->dst; + e->dst.super.init_destination = myinitdest; + e->dst.super.empty_output_buffer = myemptybuf; + e->dst.super.term_destination = mytermdest; + + e->dst.super.next_output_byte = nil; + e->dst.super.free_in_buffer = 0; + + /* prepare required encoding options */ + e->cinfo.image_width = e->columns; + e->cinfo.image_height = e->rows; + e->cinfo.input_components = e->colors; + + switch (e->colors) { + case 1: e->cinfo.in_color_space = JCS_GRAYSCALE; break; + case 3: e->cinfo.in_color_space = JCS_RGB; break; + case 4: e->cinfo.in_color_space = JCS_CMYK; break; + default: e->cinfo.in_color_space = JCS_UNKNOWN; break; + } + + jpeg_set_defaults(&e->cinfo); + + /* FIXME check this */ + obj = fz_dictgets(params, "ColorTransform"); + if (obj) { + int colortransform = fz_toint(obj); + if (e->colors == 3 && !colortransform) + jpeg_set_colorspace(&e->cinfo, JCS_RGB); + if (e->colors == 4 && colortransform) + jpeg_set_colorspace(&e->cinfo, JCS_YCCK); + if (e->colors == 4 && !colortransform) + jpeg_set_colorspace(&e->cinfo, JCS_CMYK); + } + + obj = fz_dictgets(params, "HSamples"); + if (obj && fz_isarray(obj)) { + fz_obj *o; + for (i = 0; i < e->colors; i++) { + o = fz_arrayget(obj, i); + e->cinfo.comp_info[i].h_samp_factor = fz_toint(o); + } + } + + obj = fz_dictgets(params, "VSamples"); + if (obj && fz_isarray(obj)) { + fz_obj *o; + for (i = 0; i < e->colors; i++) { + o = fz_arrayget(obj, i); + e->cinfo.comp_info[i].v_samp_factor = fz_toint(o); + } + } + + /* TODO: quant-tables and huffman-tables */ + + return nil; +} + +void +fz_freedcte(fz_filter *filter) +{ + fz_dcte *e = (fz_dcte*)filter; + + if (setjmp(e->err.jb)) { + fprintf(stderr, "ioerror in dcte: jpeg_destroy_compress: %s", e->err.msg); + return; + } + + jpeg_destroy_compress(&e->cinfo); + + fz_free(e); +} + +/* Adobe says zigzag order. IJG > v6a says natural order. */ +#if JPEG_LIB_VERSION >= 61 +#define unzigzag(x) unzigzagorder[x] +/* zigzag array position of n'th element of natural array order */ +static const unsigned char unzigzagorder[] = +{ + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63 +}; +#else +#define unzigzag(x) (x) +#endif + +fz_error * +fz_setquanttables(fz_dcte *e, unsigned int **qtables, int qfactor) +{ + int i, j; + unsigned int table[64]; + + if (setjmp(e->err.jb)) { + return fz_throw("ioerror in dcte: %s", e->err.msg); + } + + /* TODO: check for duplicate tables */ + + for (i = 0; i < e->colors; i++) { + for (j = 0; j < 64; j++) { + table[j] = unzigzag(qtables[i][j]); + } + jpeg_add_quant_table(&e->cinfo, i, table, qfactor, TRUE); + e->cinfo.comp_info[i].quant_tbl_no = i; + } + + return nil; +} + +fz_error * +fz_processdcte(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_dcte *e = (fz_dcte*)filter; + JSAMPROW scanline[1]; + int stride; + int i; + + e->dst.buf = out; + e->dst.super.free_in_buffer = out->ep - out->wp; + e->dst.super.next_output_byte = out->wp; + + if (setjmp(e->err.jb)) { + return fz_throw("ioerror in dcte: %s", e->err.msg); + } + + switch (e->stage) + { + case 0: + /* must have enough space for markers, typically 600 bytes or so */ + if (out->wp + 1024 > out->ep) + goto needoutput; + + jpeg_start_compress(&e->cinfo, TRUE); + + /* TODO: write Adobe ColorTransform marker */ + + /* fall through */ + e->stage = 1; + + case 1: + stride = e->columns * e->colors; + + while (e->cinfo.next_scanline < e->cinfo.image_height) + { + if (in->rp + stride > in->wp) + goto needinput; + + scanline[0] = in->rp; + + i = jpeg_write_scanlines(&e->cinfo, scanline, 1); + + if (i == 0) + goto needoutput; + + in->rp += stride; + } + + /* fall through */ + e->stage = 2; + + case 2: + /* must have enough space for end markers */ + if (out->wp + 100 > out->ep) + goto needoutput; + + /* finish compress cannot suspend! */ + jpeg_finish_compress(&e->cinfo); + + e->stage = 3; + out->eof = 1; + out->wp = out->ep - e->dst.super.free_in_buffer; + return fz_iodone; + } + +needinput: + out->wp = out->ep - e->dst.super.free_in_buffer; + return fz_ioneedin; + +needoutput: + out->wp = out->ep - e->dst.super.free_in_buffer; + return fz_ioneedout; +} + diff --git a/filter/faxc.h b/filter/faxc.h new file mode 100644 index 00000000..caf29df0 --- /dev/null +++ b/filter/faxc.h @@ -0,0 +1,116 @@ +/* common bit magic */ + +static inline void +printbits(FILE *f, int code, int nbits) +{ + int n, b; + for (n = nbits - 1; n >= 0; n--) { + b = (code >> n) & 1; + fprintf(f, "%c", b ? '1' : '0'); + } +} + +static inline int +getbit(const unsigned char *buf, int x) +{ + return ( buf[x >> 3] >> ( 7 - (x & 7) ) ) & 1; +} + +static inline void +printline(FILE *f, unsigned char *line, int w) +{ + int i; + for (i = 0; i < w; i++) + fprintf(f, "%c", getbit(line, i) ? '#' : '.'); + fprintf(f, "\n"); +} + +static inline int +getrun(const unsigned char *line, int x, int w, int c) +{ + int z; + int b; + + if (x < 0) + x = 0; + + z = x; + while (z < w) { + b = getbit(line, z); + if (c != b) + break; + z ++; + } + + return z - x; +} + +static inline int +findchanging(const unsigned char *line, int x, int w) +{ + int a, b; + + if (line == 0) + return w; + + if (x == -1) { + a = 0; + x = 0; + } + else { + a = getbit(line, x); + x++; + } + + while (x < w) { + b = getbit(line, x); + if (a != b) + break; + x++; + } + + return x; +} + +static inline int +findchangingcolor(const unsigned char *line, int x, int w, int color) +{ + if (line == 0) + return w; + + x = findchanging(line, x, w); + + if (x < w && getbit(line, x) != color) + x = findchanging(line, x, w); + + return x; +} + +static const unsigned char lm[8] = + { 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01 }; + +static const unsigned char rm[8] = + { 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE }; + +static inline void +setbits(unsigned char *line, int x0, int x1) +{ + int a0, a1, b0, b1, a; + + a0 = x0 >> 3; + a1 = x1 >> 3; + + b0 = x0 & 7; + b1 = x1 & 7; + + if (a0 == a1) { + line[a0] |= lm[b0] & rm[b1]; + } + else { + line[a0] |= lm[b0]; + for (a = a0 + 1; a < a1; a++) + line[a] = 0xFF; + line[a1] |= rm[b1]; + } +} + diff --git a/filter/faxd.c b/filter/faxd.c new file mode 100644 index 00000000..1b86c17b --- /dev/null +++ b/filter/faxd.c @@ -0,0 +1,463 @@ +#include <fitz.h> + +#include "faxd.h" +#include "faxc.h" + +enum +{ + SNORMAL, /* neutral state, waiting for any code */ + SMAKEUP, /* got a 1d makeup code, waiting for terminating code */ + SEOL, /* at eol, needs output buffer space */ + SH1, SH2 /* in H part 1 and 2 (both makeup and terminating codes) */ +}; + +#define DEBUG 1 + +#ifdef noDEBUG +#define DPRINT(...) fprintf(stderr, __VA_ARGS__) +#else +#define DPRINT(...) +#endif + +/* TODO: uncompressed */ + +typedef struct fz_faxd_s fz_faxd; + +struct fz_faxd_s +{ + fz_filter super; + + int k; + int endofline; + int encodedbytealign; + int columns; + int rows; + int endofblock; + int blackis1; + + int stride; + int ridx; + + int bidx; + unsigned int word; + + int stage, a, c, dim, eolc; + unsigned char *ref; + unsigned char *dst; +}; + +fz_error * +fz_newfaxd(fz_filter **fp, fz_obj *params) +{ + fz_obj *obj; + + FZ_NEWFILTER(fz_faxd, fax, faxd); + + fax->ref = nil; + fax->dst = nil; + + fax->k = 0; + fax->endofline = 0; + fax->encodedbytealign = 0; + fax->columns = 1728; + fax->rows = 0; + fax->endofblock = 1; + fax->blackis1 = 0; + + obj = fz_dictgets(params, "K"); + if (obj) fax->k = fz_toint(obj); + + obj = fz_dictgets(params, "EndOfLine"); + if (obj) fax->endofline = fz_tobool(obj); + + obj = fz_dictgets(params, "EncodedByteAlign"); + if (obj) fax->encodedbytealign = fz_tobool(obj); + + obj = fz_dictgets(params, "Columns"); + if (obj) fax->columns = fz_toint(obj); + + obj = fz_dictgets(params, "Rows"); + if (obj) fax->rows = fz_toint(obj); + + obj = fz_dictgets(params, "EndOfBlock"); + if (obj) fax->endofblock = fz_tobool(obj); + + obj = fz_dictgets(params, "BlackIs1"); + if (obj) fax->blackis1 = fz_tobool(obj); + + fax->stride = ((fax->columns - 1) >> 3) + 1; + fax->ridx = 0; + fax->bidx = 32; + fax->word = 0; + + fax->stage = SNORMAL; + fax->a = -1; + fax->c = 0; + fax->dim = fax->k < 0 ? 2 : 1; + fax->eolc = 0; + + fax->ref = fz_malloc(fax->stride); + if (!fax->ref) { fz_free(fax); return fz_outofmem; } + + fax->dst = fz_malloc(fax->stride); + if (!fax->dst) { fz_free(fax); fz_free(fax->ref); return fz_outofmem; } + + memset(fax->ref, 0, fax->stride); + memset(fax->dst, 0, fax->stride); + + DPRINT("FAXD k=%d eol=%d eba=%d cols=%d rows=%d eob=%d black1=%d stride=%d\n", + fax->k, fax->endofline, fax->encodedbytealign, + fax->columns, fax->rows, fax->endofblock, fax->blackis1, + fax->stride); + + return nil; +} + +void +fz_freefaxd(fz_filter *p) +{ + fz_faxd *fax = (fz_faxd*) p; + fz_free(fax->ref); + fz_free(fax->dst); + fz_free(fax); +} + +static inline void eatbits(fz_faxd *fax, int nbits) +{ + fax->word <<= nbits; + fax->bidx += nbits; +} + +static inline fz_error * fillbits(fz_faxd *fax, fz_buffer *in) +{ + while (fax->bidx >= 8) + { + if (in->rp + 1 > in->wp) + return fz_ioneedin; + fax->bidx -= 8; + fax->word |= *in->rp << fax->bidx; + in->rp ++; + } + return nil; +} + +static int +getcode(fz_faxd *fax, const cfd_node *table, int initialbits) +{ + unsigned int word = fax->word; + int tidx = word >> (32 - initialbits); + int val = table[tidx].val; + int nbits = table[tidx].nbits; + + if (nbits > initialbits) + { + int mask = (1 << (32 - initialbits)) - 1; + tidx = val + ((word & mask) >> (32 - nbits)); + val = table[tidx].val; + nbits = initialbits + table[tidx].nbits; + } + + eatbits(fax, nbits); + + return val; +} + +/* decode one 1d code */ +static fz_error * +dec1d(fz_faxd *fax) +{ + int code; + + if (fax->a == -1) + fax->a = 0; + + if (fax->c) + code = getcode(fax, cf_black_decode, cfd_black_initial_bits); + else + code = getcode(fax, cf_white_decode, cfd_white_initial_bits); + + DPRINT("%c %d\n", fax->c?'b':'w', code); + + if (code == UNCOMPRESSED) + return fz_throw("ioerror: uncompressed data in faxd"); + + if (code < 0) + return fz_throw("ioerror: invalid 1d code in faxd"); + + if (fax->a + code > fax->columns) + return fz_throw("ioerror: invalid 1d data in faxd"); + + if (fax->c) + setbits(fax->dst, fax->a, fax->a + code); + + fax->a += code; + + if (code < 64) + { + fax->c = !fax->c; + fax->stage = SNORMAL; + } + else + fax->stage = SMAKEUP; + + return nil; +} + +/* decode one 2d code */ +static fz_error * +dec2d(fz_faxd *fax) +{ + int code, b1, b2; + + if (fax->stage == SH1 || fax->stage == SH2) + { + if (fax->a == -1) + fax->a = 0; + + if (fax->c) + code = getcode(fax, cf_black_decode, cfd_black_initial_bits); + else + code = getcode(fax, cf_white_decode, cfd_white_initial_bits); + + DPRINT("%c %d\n", fax->c ? 'b' : 'w', code); + + if (code == UNCOMPRESSED) + return fz_throw("ioerror: uncompressed data in faxd"); + + if (code < 0) + return fz_throw("ioerror: invalid 2d code in faxd"); + + if (fax->a + code > fax->columns) + return fz_throw("ioerror: invalid 2d data in faxd"); + + if (fax->c) + setbits(fax->dst, fax->a, fax->a + code); + + fax->a += code; + + if (code < 64) + { + fax->c = !fax->c; + if (fax->stage == SH1) + fax->stage = SH2; + else if (fax->stage == SH2) + fax->stage = SNORMAL; + } + + return nil; + } + + code = getcode(fax, cf_2d_decode, cfd_2d_initial_bits); + + switch (code) + { + case H: + fax->stage = SH1; + DPRINT("H\n"); + break; + + case P: + b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c); + b2 = findchanging(fax->ref, b1, fax->columns); + DPRINT("P\n"); + if (fax->c) setbits(fax->dst, fax->a, b2); + fax->a = b2; + break; + + case V0: + b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c); + DPRINT("V0\n"); + if (fax->c) setbits(fax->dst, fax->a, b1); + fax->a = b1; + fax->c = !fax->c; + break; + + case VR1: + b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c); + DPRINT("VR1\n"); + if (fax->c) setbits(fax->dst, fax->a, b1 + 1); + fax->a = b1 + 1; + fax->c = !fax->c; + break; + + case VR2: + b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c); + DPRINT("VR2\n"); + if (fax->c) setbits(fax->dst, fax->a, b1 + 2); + fax->a = b1 + 2; + fax->c = !fax->c; + break; + + case VR3: + b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c); + DPRINT("VR3\n"); + if (fax->c) setbits(fax->dst, fax->a, b1 + 3); + fax->a = b1 + 3; + fax->c = !fax->c; + break; + + case VL1: + b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c); + DPRINT("VL1\n"); + if (fax->c) setbits(fax->dst, fax->a, b1 - 1); + fax->a = b1 - 1; + fax->c = !fax->c; + break; + + case VL2: + b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c); + DPRINT("VL2\n"); + if (fax->c) setbits(fax->dst, fax->a, b1 - 2); + fax->a = b1 - 2; + fax->c = !fax->c; + break; + + case VL3: + b1 = findchangingcolor(fax->ref, fax->a, fax->columns, !fax->c); + DPRINT("VL3\n"); + if (fax->c) setbits(fax->dst, fax->a, b1 - 3); + fax->a = b1 - 3; + fax->c = !fax->c; + break; + + case UNCOMPRESSED: + return fz_throw("ioerror: uncompressed data in faxd"); + + case ERROR: + return fz_throw("ioerror: invalid 2d code in faxd"); + + default: + return fz_throw("ioerror: invalid 2d code in faxd (%d)", code); + } + + return 0; +} + +fz_error * +fz_processfaxd(fz_filter *f, fz_buffer *in, fz_buffer *out) +{ + fz_faxd *fax = (fz_faxd*)f; + fz_error * err; + int i; + + if (fax->stage == SEOL) + goto eol; + +loop: + + if (fillbits(fax, in)) + { + if (in->eof) { + if (fax->bidx > 31) { + if (fax->a > 0) + goto eol; + goto rtc; + } + } + else { + return fz_ioneedin; + } + } + + if ((fax->word >> (32 - 12)) == 0) + { + eatbits(fax, 1); + goto loop; + } + + if ((fax->word >> (32 - 12)) == 1) + { + DPRINT("EOL\n"); + eatbits(fax, 12); + fax->eolc ++; + + if (fax->k > 0) { + if ((fax->word >> (32 - 1)) == 1) + fax->dim = 1; + else + fax->dim = 2; + eatbits(fax, 1); + DPRINT("DIM %d\n", fax->dim); + } + } + else if (fax->dim == 1) + { + fax->eolc = 0; + err = dec1d(fax); + if (err) return err; + + } + else if (fax->dim == 2) + { + fax->eolc = 0; + err = dec2d(fax); + if (err) return err; + } + + /* no eol check after makeup codes nor in the middle of an H code */ + if (fax->stage == SMAKEUP || fax->stage == SH1 || fax->stage == SH2) + goto loop; + + /* check for eol conditions */ + if (fax->eolc || fax->a >= fax->columns) + { + if (fax->a > 0) + goto eol; + if (fax->eolc == (fax->k < 0 ? 2 : 6)) + goto rtc; + } + + goto loop; + +eol: + fax->stage = SEOL; + if (out->wp + fax->stride > out->ep) + return fz_ioneedout; + + if (fax->blackis1) + memcpy(out->wp, fax->dst, fax->stride); + else + for (i = 0; i < fax->stride; i++) + out->wp[i] = ~fax->dst[i]; + + memcpy(fax->ref, fax->dst, fax->stride); + memset(fax->dst, 0, fax->stride); + out->wp += fax->stride; + + fax->stage = SNORMAL; + fax->c = 0; + fax->a = -1; + fax->ridx ++; + + if (!fax->endofblock && fax->rows) { + if (fax->ridx >= fax->rows) + goto rtc; + } + + /* we have not read dim from eol, make a guess */ + if (fax->k > 0 && !fax->eolc) + { + if (fax->ridx % fax->k == 0) + fax->dim = 1; + else + fax->dim = 2; + } + + DPRINT("%dd scanline %d\n", fax->dim, fax->ridx + 1); + + /* if endofline & encodedbytealign, EOLs are *not* optional */ + if (fax->encodedbytealign) { + if (fax->endofline) + eatbits(fax, (12 - fax->bidx) & 7); + else + eatbits(fax, (8 - fax->bidx) & 7); + } + + goto loop; + +rtc: + DPRINT("RTC\n"); + out->eof = 1; + return fz_iodone; +} + diff --git a/filter/faxd.h b/filter/faxd.h new file mode 100644 index 00000000..886514f5 --- /dev/null +++ b/filter/faxd.h @@ -0,0 +1,61 @@ +/* Fax G3/G4 tables */ + +/* +<raph> the first 2^(initialbits) entries map bit patterns to decodes +<raph> let's say initial_bits is 8 for the sake of example +<raph> and that the code is 1001 +<raph> that means that entries 0x90 .. 0x9f have the entry { val, 4 } +<raph> because those are all the bytes that start with the code +<raph> and the 4 is the length of the code +... if (n_bits > initial_bits) ... +<raph> anyway, in that case, it basically points to a mini table +<raph> the n_bits is the maximum length of all codes beginning with that byte +<raph> so 2^(n_bits - initial_bits) is the size of the mini-table +<raph> peter came up with this, and it makes sense +*/ + +typedef struct cfd_node_s cfd_node; + +struct cfd_node_s +{ + short val; + short nbits; +}; + +enum +{ + cfd_white_initial_bits = 8, + cfd_black_initial_bits = 7, + cfd_2d_initial_bits = 7, + cfd_uncompressed_initial_bits = 6 /* must be 6 */ +}; + +/* non-run codes in tables */ +enum +{ + ERROR = -1, + ZEROS = -2, /* EOL follows, possibly with more padding first */ + UNCOMPRESSED = -3, +}; + +/* semantic codes for cf_2d_decode */ +enum +{ + P = -4, + H = -5, + VR3 = 0, + VR2 = 1, + VR1 = 2, + V0 = 3, + VL1 = 4, + VL2 = 5, + VL3 = 6, +}; + +/* Decoding tables */ + +extern const cfd_node cf_white_decode[]; +extern const cfd_node cf_black_decode[]; +extern const cfd_node cf_2d_decode[]; +extern const cfd_node cf_uncompressed_decode[]; + diff --git a/filter/faxdtab.c b/filter/faxdtab.c new file mode 100644 index 00000000..8e387d03 --- /dev/null +++ b/filter/faxdtab.c @@ -0,0 +1,931 @@ +/* Tables for CCITTFaxDecode filter. */ + +/* This file was generated automatically. It is governed by the same terms */ +/* as the files scfetab.c and scfdgen.c from which it was derived. */ +/* Consult those files for the licensing terms and conditions. */ + +#include "faxd.h" + +/* White decoding table. */ +const cfd_node cf_white_decode[] = { + { 256, 12 }, + { 272, 12 }, + { 29, 8 }, + { 30, 8 }, + { 45, 8 }, + { 46, 8 }, + { 22, 7 }, + { 22, 7 }, + { 23, 7 }, + { 23, 7 }, + { 47, 8 }, + { 48, 8 }, + { 13, 6 }, + { 13, 6 }, + { 13, 6 }, + { 13, 6 }, + { 20, 7 }, + { 20, 7 }, + { 33, 8 }, + { 34, 8 }, + { 35, 8 }, + { 36, 8 }, + { 37, 8 }, + { 38, 8 }, + { 19, 7 }, + { 19, 7 }, + { 31, 8 }, + { 32, 8 }, + { 1, 6 }, + { 1, 6 }, + { 1, 6 }, + { 1, 6 }, + { 12, 6 }, + { 12, 6 }, + { 12, 6 }, + { 12, 6 }, + { 53, 8 }, + { 54, 8 }, + { 26, 7 }, + { 26, 7 }, + { 39, 8 }, + { 40, 8 }, + { 41, 8 }, + { 42, 8 }, + { 43, 8 }, + { 44, 8 }, + { 21, 7 }, + { 21, 7 }, + { 28, 7 }, + { 28, 7 }, + { 61, 8 }, + { 62, 8 }, + { 63, 8 }, + { 0, 8 }, + { 320, 8 }, + { 384, 8 }, + { 10, 5 }, + { 10, 5 }, + { 10, 5 }, + { 10, 5 }, + { 10, 5 }, + { 10, 5 }, + { 10, 5 }, + { 10, 5 }, + { 11, 5 }, + { 11, 5 }, + { 11, 5 }, + { 11, 5 }, + { 11, 5 }, + { 11, 5 }, + { 11, 5 }, + { 11, 5 }, + { 27, 7 }, + { 27, 7 }, + { 59, 8 }, + { 60, 8 }, + { 288, 9 }, + { 290, 9 }, + { 18, 7 }, + { 18, 7 }, + { 24, 7 }, + { 24, 7 }, + { 49, 8 }, + { 50, 8 }, + { 51, 8 }, + { 52, 8 }, + { 25, 7 }, + { 25, 7 }, + { 55, 8 }, + { 56, 8 }, + { 57, 8 }, + { 58, 8 }, + { 192, 6 }, + { 192, 6 }, + { 192, 6 }, + { 192, 6 }, + { 1664, 6 }, + { 1664, 6 }, + { 1664, 6 }, + { 1664, 6 }, + { 448, 8 }, + { 512, 8 }, + { 292, 9 }, + { 640, 8 }, + { 576, 8 }, + { 294, 9 }, + { 296, 9 }, + { 298, 9 }, + { 300, 9 }, + { 302, 9 }, + { 256, 7 }, + { 256, 7 }, + { 2, 4 }, + { 2, 4 }, + { 2, 4 }, + { 2, 4 }, + { 2, 4 }, + { 2, 4 }, + { 2, 4 }, + { 2, 4 }, + { 2, 4 }, + { 2, 4 }, + { 2, 4 }, + { 2, 4 }, + { 2, 4 }, + { 2, 4 }, + { 2, 4 }, + { 2, 4 }, + { 3, 4 }, + { 3, 4 }, + { 3, 4 }, + { 3, 4 }, + { 3, 4 }, + { 3, 4 }, + { 3, 4 }, + { 3, 4 }, + { 3, 4 }, + { 3, 4 }, + { 3, 4 }, + { 3, 4 }, + { 3, 4 }, + { 3, 4 }, + { 3, 4 }, + { 3, 4 }, + { 128, 5 }, + { 128, 5 }, + { 128, 5 }, + { 128, 5 }, + { 128, 5 }, + { 128, 5 }, + { 128, 5 }, + { 128, 5 }, + { 8, 5 }, + { 8, 5 }, + { 8, 5 }, + { 8, 5 }, + { 8, 5 }, + { 8, 5 }, + { 8, 5 }, + { 8, 5 }, + { 9, 5 }, + { 9, 5 }, + { 9, 5 }, + { 9, 5 }, + { 9, 5 }, + { 9, 5 }, + { 9, 5 }, + { 9, 5 }, + { 16, 6 }, + { 16, 6 }, + { 16, 6 }, + { 16, 6 }, + { 17, 6 }, + { 17, 6 }, + { 17, 6 }, + { 17, 6 }, + { 4, 4 }, + { 4, 4 }, + { 4, 4 }, + { 4, 4 }, + { 4, 4 }, + { 4, 4 }, + { 4, 4 }, + { 4, 4 }, + { 4, 4 }, + { 4, 4 }, + { 4, 4 }, + { 4, 4 }, + { 4, 4 }, + { 4, 4 }, + { 4, 4 }, + { 4, 4 }, + { 5, 4 }, + { 5, 4 }, + { 5, 4 }, + { 5, 4 }, + { 5, 4 }, + { 5, 4 }, + { 5, 4 }, + { 5, 4 }, + { 5, 4 }, + { 5, 4 }, + { 5, 4 }, + { 5, 4 }, + { 5, 4 }, + { 5, 4 }, + { 5, 4 }, + { 5, 4 }, + { 14, 6 }, + { 14, 6 }, + { 14, 6 }, + { 14, 6 }, + { 15, 6 }, + { 15, 6 }, + { 15, 6 }, + { 15, 6 }, + { 64, 5 }, + { 64, 5 }, + { 64, 5 }, + { 64, 5 }, + { 64, 5 }, + { 64, 5 }, + { 64, 5 }, + { 64, 5 }, + { 6, 4 }, + { 6, 4 }, + { 6, 4 }, + { 6, 4 }, + { 6, 4 }, + { 6, 4 }, + { 6, 4 }, + { 6, 4 }, + { 6, 4 }, + { 6, 4 }, + { 6, 4 }, + { 6, 4 }, + { 6, 4 }, + { 6, 4 }, + { 6, 4 }, + { 6, 4 }, + { 7, 4 }, + { 7, 4 }, + { 7, 4 }, + { 7, 4 }, + { 7, 4 }, + { 7, 4 }, + { 7, 4 }, + { 7, 4 }, + { 7, 4 }, + { 7, 4 }, + { 7, 4 }, + { 7, 4 }, + { 7, 4 }, + { 7, 4 }, + { 7, 4 }, + { 7, 4 }, + { -2, 3 }, + { -2, 3 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -3, 4 }, + { 1792, 3 }, + { 1792, 3 }, + { 1984, 4 }, + { 2048, 4 }, + { 2112, 4 }, + { 2176, 4 }, + { 2240, 4 }, + { 2304, 4 }, + { 1856, 3 }, + { 1856, 3 }, + { 1920, 3 }, + { 1920, 3 }, + { 2368, 4 }, + { 2432, 4 }, + { 2496, 4 }, + { 2560, 4 }, + { 1472, 1 }, + { 1536, 1 }, + { 1600, 1 }, + { 1728, 1 }, + { 704, 1 }, + { 768, 1 }, + { 832, 1 }, + { 896, 1 }, + { 960, 1 }, + { 1024, 1 }, + { 1088, 1 }, + { 1152, 1 }, + { 1216, 1 }, + { 1280, 1 }, + { 1344, 1 }, + { 1408, 1 } +}; + +/* Black decoding table. */ +const cfd_node cf_black_decode[] = { + { 128, 12 }, + { 160, 13 }, + { 224, 12 }, + { 256, 12 }, + { 10, 7 }, + { 11, 7 }, + { 288, 12 }, + { 12, 7 }, + { 9, 6 }, + { 9, 6 }, + { 8, 6 }, + { 8, 6 }, + { 7, 5 }, + { 7, 5 }, + { 7, 5 }, + { 7, 5 }, + { 6, 4 }, + { 6, 4 }, + { 6, 4 }, + { 6, 4 }, + { 6, 4 }, + { 6, 4 }, + { 6, 4 }, + { 6, 4 }, + { 5, 4 }, + { 5, 4 }, + { 5, 4 }, + { 5, 4 }, + { 5, 4 }, + { 5, 4 }, + { 5, 4 }, + { 5, 4 }, + { 1, 3 }, + { 1, 3 }, + { 1, 3 }, + { 1, 3 }, + { 1, 3 }, + { 1, 3 }, + { 1, 3 }, + { 1, 3 }, + { 1, 3 }, + { 1, 3 }, + { 1, 3 }, + { 1, 3 }, + { 1, 3 }, + { 1, 3 }, + { 1, 3 }, + { 1, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 3, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { 2, 2 }, + { -2, 4 }, + { -2, 4 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -3, 5 }, + { 1792, 4 }, + { 1792, 4 }, + { 1984, 5 }, + { 2048, 5 }, + { 2112, 5 }, + { 2176, 5 }, + { 2240, 5 }, + { 2304, 5 }, + { 1856, 4 }, + { 1856, 4 }, + { 1920, 4 }, + { 1920, 4 }, + { 2368, 5 }, + { 2432, 5 }, + { 2496, 5 }, + { 2560, 5 }, + { 18, 3 }, + { 18, 3 }, + { 18, 3 }, + { 18, 3 }, + { 18, 3 }, + { 18, 3 }, + { 18, 3 }, + { 18, 3 }, + { 52, 5 }, + { 52, 5 }, + { 640, 6 }, + { 704, 6 }, + { 768, 6 }, + { 832, 6 }, + { 55, 5 }, + { 55, 5 }, + { 56, 5 }, + { 56, 5 }, + { 1280, 6 }, + { 1344, 6 }, + { 1408, 6 }, + { 1472, 6 }, + { 59, 5 }, + { 59, 5 }, + { 60, 5 }, + { 60, 5 }, + { 1536, 6 }, + { 1600, 6 }, + { 24, 4 }, + { 24, 4 }, + { 24, 4 }, + { 24, 4 }, + { 25, 4 }, + { 25, 4 }, + { 25, 4 }, + { 25, 4 }, + { 1664, 6 }, + { 1728, 6 }, + { 320, 5 }, + { 320, 5 }, + { 384, 5 }, + { 384, 5 }, + { 448, 5 }, + { 448, 5 }, + { 512, 6 }, + { 576, 6 }, + { 53, 5 }, + { 53, 5 }, + { 54, 5 }, + { 54, 5 }, + { 896, 6 }, + { 960, 6 }, + { 1024, 6 }, + { 1088, 6 }, + { 1152, 6 }, + { 1216, 6 }, + { 64, 3 }, + { 64, 3 }, + { 64, 3 }, + { 64, 3 }, + { 64, 3 }, + { 64, 3 }, + { 64, 3 }, + { 64, 3 }, + { 13, 1 }, + { 13, 1 }, + { 13, 1 }, + { 13, 1 }, + { 13, 1 }, + { 13, 1 }, + { 13, 1 }, + { 13, 1 }, + { 13, 1 }, + { 13, 1 }, + { 13, 1 }, + { 13, 1 }, + { 13, 1 }, + { 13, 1 }, + { 13, 1 }, + { 13, 1 }, + { 23, 4 }, + { 23, 4 }, + { 50, 5 }, + { 51, 5 }, + { 44, 5 }, + { 45, 5 }, + { 46, 5 }, + { 47, 5 }, + { 57, 5 }, + { 58, 5 }, + { 61, 5 }, + { 256, 5 }, + { 16, 3 }, + { 16, 3 }, + { 16, 3 }, + { 16, 3 }, + { 17, 3 }, + { 17, 3 }, + { 17, 3 }, + { 17, 3 }, + { 48, 5 }, + { 49, 5 }, + { 62, 5 }, + { 63, 5 }, + { 30, 5 }, + { 31, 5 }, + { 32, 5 }, + { 33, 5 }, + { 40, 5 }, + { 41, 5 }, + { 22, 4 }, + { 22, 4 }, + { 14, 1 }, + { 14, 1 }, + { 14, 1 }, + { 14, 1 }, + { 14, 1 }, + { 14, 1 }, + { 14, 1 }, + { 14, 1 }, + { 14, 1 }, + { 14, 1 }, + { 14, 1 }, + { 14, 1 }, + { 14, 1 }, + { 14, 1 }, + { 14, 1 }, + { 14, 1 }, + { 15, 2 }, + { 15, 2 }, + { 15, 2 }, + { 15, 2 }, + { 15, 2 }, + { 15, 2 }, + { 15, 2 }, + { 15, 2 }, + { 128, 5 }, + { 192, 5 }, + { 26, 5 }, + { 27, 5 }, + { 28, 5 }, + { 29, 5 }, + { 19, 4 }, + { 19, 4 }, + { 20, 4 }, + { 20, 4 }, + { 34, 5 }, + { 35, 5 }, + { 36, 5 }, + { 37, 5 }, + { 38, 5 }, + { 39, 5 }, + { 21, 4 }, + { 21, 4 }, + { 42, 5 }, + { 43, 5 }, + { 0, 3 }, + { 0, 3 }, + { 0, 3 }, + { 0, 3 } +}; + +/* 2-D decoding table. */ +const cfd_node cf_2d_decode[] = { + { 128, 11 }, + { 144, 10 }, + { 6, 7 }, + { 0, 7 }, + { 5, 6 }, + { 5, 6 }, + { 1, 6 }, + { 1, 6 }, + { -4, 4 }, + { -4, 4 }, + { -4, 4 }, + { -4, 4 }, + { -4, 4 }, + { -4, 4 }, + { -4, 4 }, + { -4, 4 }, + { -5, 3 }, + { -5, 3 }, + { -5, 3 }, + { -5, 3 }, + { -5, 3 }, + { -5, 3 }, + { -5, 3 }, + { -5, 3 }, + { -5, 3 }, + { -5, 3 }, + { -5, 3 }, + { -5, 3 }, + { -5, 3 }, + { -5, 3 }, + { -5, 3 }, + { -5, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 4, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { 3, 1 }, + { -2, 4 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -1, 0 }, + { -3, 3 } +}; + +/* Uncompresssed decoding table. */ +const cfd_node cf_uncompressed_decode[] = { + { 64, 12 }, + { 5, 6 }, + { 4, 5 }, + { 4, 5 }, + { 3, 4 }, + { 3, 4 }, + { 3, 4 }, + { 3, 4 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { 0, 1 }, + { -1, 0 }, + { -1, 0 }, + { 8, 6 }, + { 9, 6 }, + { 6, 5 }, + { 6, 5 }, + { 7, 5 }, + { 7, 5 }, + { 4, 4 }, + { 4, 4 }, + { 4, 4 }, + { 4, 4 }, + { 5, 4 }, + { 5, 4 }, + { 5, 4 }, + { 5, 4 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 2, 3 }, + { 3, 3 }, + { 3, 3 }, + { 3, 3 }, + { 3, 3 }, + { 3, 3 }, + { 3, 3 }, + { 3, 3 }, + { 3, 3 }, + { 0, 2 }, + { 0, 2 }, + { 0, 2 }, + { 0, 2 }, + { 0, 2 }, + { 0, 2 }, + { 0, 2 }, + { 0, 2 }, + { 0, 2 }, + { 0, 2 }, + { 0, 2 }, + { 0, 2 }, + { 0, 2 }, + { 0, 2 }, + { 0, 2 }, + { 0, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 }, + { 1, 2 } +}; + +/* Dummy executable code to pacify compilers. */ +void scfdtab_dummy(void) { } + diff --git a/filter/faxe.c b/filter/faxe.c new file mode 100644 index 00000000..20e776dd --- /dev/null +++ b/filter/faxe.c @@ -0,0 +1,448 @@ +#include <fitz.h> + +#include "faxe.h" +#include "faxc.h" + +/* TODO: honor Rows param */ + +#define noDEBUGBITS 1 +#define noDEBUG 1 + +#ifdef DEBUG +#define DPRINT(...) fprintf(stderr, __VA_ARGS__) +#else +#define DPRINT(...) +#endif + +typedef struct fz_faxe_s fz_faxe; + +struct fz_faxe_s +{ + fz_filter super; + + int k; + int endofline; + int encodedbytealign; + int columns; + int endofblock; + int blackis1; + + int stride; + int ridx; /* how many rows in total */ + int bidx; /* how many bits are already used in out->wp */ + unsigned char bsave; /* partial byte saved between process() calls */ + + int stage; + int a0, c; /* mid-line coding state */ + + unsigned char *ref; + unsigned char *src; +}; + +fz_error * +fz_newfaxe(fz_filter **fp, fz_obj *params) +{ + fz_obj *obj; + + FZ_NEWFILTER(fz_faxe, fax, faxe); + + fax->ref = nil; + fax->src = nil; + + fax->k = 0; + fax->endofline = 0; + fax->encodedbytealign = 0; + fax->columns = 1728; + fax->endofblock = 1; + fax->blackis1 = 0; + + obj = fz_dictgets(params, "K"); + if (obj) fax->k = fz_toint(obj); + + obj = fz_dictgets(params, "EndOfLine"); + if (obj) fax->endofline = fz_tobool(obj); + + obj = fz_dictgets(params, "EncodedByteAlign"); + if (obj) fax->encodedbytealign = fz_tobool(obj); + + obj = fz_dictgets(params, "Columns"); + if (obj) fax->columns = fz_toint(obj); + + obj = fz_dictgets(params, "EndOfBlock"); + if (obj) fax->endofblock = fz_tobool(obj); + + obj = fz_dictgets(params, "BlackIs1"); + if (obj) fax->blackis1 = fz_tobool(obj); + + fax->stride = ((fax->columns - 1) >> 3) + 1; + fax->bidx = 0; + fax->ridx = 0; + + fax->stage = 0; + fax->a0 = -1; + fax->c = 0; + + fax->ref = fz_malloc(fax->stride); + if (!fax->ref) { fz_free(fax); return fz_outofmem; } + + fax->src = fz_malloc(fax->stride); + if (!fax->src) { fz_free(fax); fz_free(fax->ref); return fz_outofmem; } + + memset(fax->ref, 0, fax->stride); + memset(fax->src, 0, fax->stride); + + return nil; +} + +void +fz_freefaxe(fz_filter *p) +{ + fz_faxe *fax = (fz_faxe*) p; + fz_free(fax->src); + fz_free(fax->ref); + fz_free(fax); +} + +enum { codebytes = 2 }; + +static inline int +runbytes(int run) +{ + int m = (run / 64) / 40 + 1; /* number of makeup codes */ + return codebytes * (m + 1); /* bytes for makeup + term codes */ +} + +static void +putbits(fz_faxe *fax, fz_buffer *out, int code, int nbits) +{ +#ifdef DEBUGBITS + fprintf(stderr, "BITS "); + printbits(stderr, code, nbits); + fprintf(stderr, "\n"); +#endif + + while (nbits > 0) + { + if (fax->bidx == 0) { + *out->wp = 0; + } + + /* code does not fit: shift right */ + if (nbits > (8 - fax->bidx)) { + *out->wp |= code >> (nbits - (8 - fax->bidx)); + nbits = nbits - (8 - fax->bidx); + fax->bidx = 0; + out->wp ++; + } + + /* shift left */ + else { + *out->wp |= code << ((8 - fax->bidx) - nbits); + fax->bidx += nbits; + if (fax->bidx == 8) { + fax->bidx = 0; + out->wp ++; + } + nbits = 0; + } + } +} + +static inline void +putcode(fz_faxe *fax, fz_buffer *out, const cfe_code *run) +{ + putbits(fax, out, run->code, run->nbits); +} + +static void +putrun(fz_faxe *fax, fz_buffer *out, int run, int c) +{ + int m; + + const cf_runs *codetable = c ? &cf_black_runs : &cf_white_runs; + + if (run > 63) { + m = run / 64; + while (m > 40) { + DPRINT("%c %d\n", c?'b':'w', 40 * 64); + putcode(fax, out, &codetable->makeup[40]); + m -= 40; + } + if (m > 0) { + DPRINT("%c %d\n", c?'b':'w', m * 64); + putcode(fax, out, &codetable->makeup[m]); + } + DPRINT("%c %d\n", c?'b':'w', run % 64); + putcode(fax, out, &codetable->termination[run % 64]); + } + else { + DPRINT("%c %d\n", c?'b':'w', run); + putcode(fax, out, &codetable->termination[run]); + } +} + +static fz_error * +enc1d(fz_faxe *fax, unsigned char *line, fz_buffer *out) +{ + int run; + +#ifdef DEBUG +if (fax->a0 < 0) { + DPRINT("1d scanline %d\n", fax->ridx + 1); + DPRINT("color = %d\n", fax->c); +} +#endif + + if (fax->a0 < 0) + fax->a0 = 0; + + while (fax->a0 < fax->columns) + { + run = getrun(line, fax->a0, fax->columns, fax->c); + + if (out->wp + 1 + runbytes(run) > out->ep) + return fz_ioneedout; + + putrun(fax, out, run, fax->c); + + fax->a0 += run; + fax->c = !fax->c; + } + + return 0; +} + +static fz_error * +enc2d(fz_faxe *fax, unsigned char *ref, unsigned char *src, fz_buffer *out) +{ + int a1, a2, b1, b2; + int run1, run2, n; + + +#ifdef DEBUG +if (fax->a0 < 0) +{ + DPRINT("2d scanline %d\n", fax->ridx + 1); +} +#endif + + DPRINT("color = %d\n", fax->c); + + while (fax->a0 < fax->columns) + { + a1 = findchanging(src, fax->a0, fax->columns); + b1 = findchangingcolor(ref, fax->a0, fax->columns, !fax->c); + b2 = findchanging(ref, b1, fax->columns); + +#ifdef DEBUGBITS + DPRINT("twod a0=%d a1=%d b1=%d b2=%d\n", fax->a0, a1, b1, b2); +#endif + + /* pass */ + if (b2 < a1) + { + if (out->wp + 1 + codebytes > out->ep) + return fz_ioneedout; + + DPRINT("P\n"); + + putcode(fax, out, &cf2_run_pass); + + fax->a0 = b2; + } + + /* vertical */ + else if (ABS(b1 - a1) <= 3) + { + if (out->wp + 1 + codebytes > out->ep) + return fz_ioneedout; + +#ifdef DEBUG + if (b1 - a1 == 0) + DPRINT("V0\n"); + else if (b1 - a1 < 0) + DPRINT("VR%d\n", a1 - b1); + else + DPRINT("VL%d\n", b1 - a1); +#endif + + putcode(fax, out, &cf2_run_vertical[b1 - a1 + 3]); + + fax->a0 = a1; + fax->c = !fax->c; + } + + /* horizontal */ + else + { + a2 = findchanging(src, a1, fax->columns); + run1 = a1 - fax->a0; + run2 = a2 - a1; + n = codebytes + runbytes(run1) + runbytes(run2); + + if (out->wp + 1 + n > out->ep) + return fz_ioneedout; + + DPRINT("H\n"); + + putcode(fax, out, &cf2_run_horizontal); + putrun(fax, out, run1, fax->c); + putrun(fax, out, run2, !fax->c); + + fax->a0 = a2; + } + } + + return 0; +} + +static fz_error * +process(fz_faxe *fax, fz_buffer *in, fz_buffer *out) +{ + fz_error *err; + int i, n; + + while (1) + { + if (in->rp == in->wp && in->eof) + goto rtc; + + switch (fax->stage) + { + case 0: + if (fax->encodedbytealign) + { + if (fax->endofline) { + if (out->wp + 1 + 1 > out->ep) + return fz_ioneedout; + + /* make sure that EOL ends on a byte border */ + putbits(fax, out, 0, (12 - fax->bidx) & 7); + } + else { + if (fax->bidx) { + if (out->wp + 1 > out->ep) + return fz_ioneedout; + fax->bidx = 0; + out->wp ++; + } + } + } + + fax->stage ++; + + case 1: + if (fax->endofline) + { + if (out->wp + 1 + codebytes + 1 > out->ep) + return fz_ioneedout; + + DPRINT("EOL\n"); + if (fax->k > 0) { + if (fax->ridx % fax->k == 0) + putcode(fax, out, &cf2_run_eol_1d); + else + putcode(fax, out, &cf2_run_eol_2d); + } + else { + putcode(fax, out, &cf_run_eol); + } + } + + fax->stage ++; + + case 2: + if (in->rp + fax->stride > in->wp) { + if (in->eof) /* XXX barf here? */ + goto rtc; + return fz_ioneedin; + } + + memcpy(fax->ref, fax->src, fax->stride); + memcpy(fax->src, in->rp, fax->stride); + + if (!fax->blackis1) + { + for (i = 0; i < fax->stride; i++) + fax->src[i] = ~fax->src[i]; + } + + in->rp += fax->stride; + + fax->c = 0; + fax->a0 = -1; + +#ifdef DEBUGBITS + DPRINT("LINE %d\n", fax->ridx); + printline(stderr, fax->ref, fax->columns); + printline(stderr, fax->src, fax->columns); +#endif + + fax->stage ++; + + case 3: + err = 0; /* to silence compiler */ + + if (fax->k < 0) { + err = enc2d(fax, fax->ref, fax->src, out); + } + else if (fax->k == 0) { + err = enc1d(fax, fax->src, out); + } + else if (fax->k > 0) { + if (fax->ridx % fax->k == 0) { + err = enc1d(fax, fax->src, out); + } + else { + err = enc2d(fax, fax->ref, fax->src, out); + } + } + + if (err) return err; + + fax->ridx ++; + + fax->stage = 0; + } + } + +rtc: + if (fax->endofblock) + { + n = fax->k < 0 ? 2 : 6; + + if (out->wp + 1 + codebytes * n > out->ep) + return fz_ioneedout; + + for (i = 0; i < n; i++) { + putcode(fax, out, &cf_run_eol); + if (fax->k > 0) + putbits(fax, out, 1, 1); + } + DPRINT("RTC\n"); + } + + if (fax->bidx) + out->wp ++; + out->eof = 1; + + return fz_iodone; +} + +fz_error * +fz_processfaxe(fz_filter *p, fz_buffer *in, fz_buffer *out) +{ + fz_faxe *fax = (fz_faxe*) p; + fz_error *err; + + /* restore partial bits */ + *out->wp = fax->bsave; + + err = process(fax, in, out); + + /* save partial bits */ + fax->bsave = *out->wp; + + return err; +} + diff --git a/filter/faxe.h b/filter/faxe.h new file mode 100644 index 00000000..dd3fc121 --- /dev/null +++ b/filter/faxe.h @@ -0,0 +1,37 @@ +/* Fax G3/G4 tables */ + +typedef struct cfe_code_s cfe_code; + +struct cfe_code_s +{ + unsigned short code; + unsigned short nbits; +}; + +typedef struct cf_runs_s { + cfe_code termination[64]; + cfe_code makeup[41]; +} cf_runs; + +/* Encoding tables */ + +/* Codes common to 1-D and 2-D encoding. */ +extern const cfe_code cf_run_eol; +extern const cf_runs cf_white_runs, cf_black_runs; +extern const cfe_code cf_uncompressed[6]; +extern const cfe_code cf_uncompressed_exit[10]; /* indexed by 2 x length of */ + +/* 1-D encoding. */ +extern const cfe_code cf1_run_uncompressed; + +/* 2-D encoding. */ +enum { cf2_run_vertical_offset = 3 }; +extern const cfe_code cf2_run_pass; +extern const cfe_code cf2_run_vertical[7]; /* indexed by b1 - a1 + offset */ +extern const cfe_code cf2_run_horizontal; +extern const cfe_code cf2_run_uncompressed; + +/* 2-D Group 3 encoding. */ +extern const cfe_code cf2_run_eol_1d; +extern const cfe_code cf2_run_eol_2d; + diff --git a/filter/faxetab.c b/filter/faxetab.c new file mode 100644 index 00000000..409039f4 --- /dev/null +++ b/filter/faxetab.c @@ -0,0 +1,131 @@ +/* Tables for CCITTFaxEncode filter */ + +#include "faxe.h" + +/* Define the end-of-line code. */ +const cfe_code cf_run_eol = {1, 12}; + +/* Define the 1-D code that signals uncompressed data. */ +const cfe_code cf1_run_uncompressed = {0xf, 12}; + +/* Define the 2-D run codes. */ +const cfe_code cf2_run_pass = {0x1, 4}; +const cfe_code cf2_run_vertical[7] = +{ + {0x3, 7}, + {0x3, 6}, + {0x3, 3}, + {0x1, 1}, + {0x2, 3}, + {0x2, 6}, + {0x2, 7} +}; +const cfe_code cf2_run_horizontal = {1, 3}; +const cfe_code cf2_run_uncompressed = {0xf, 10}; + +/* EOL codes for Group 3 2-D. */ +const cfe_code cf2_run_eol_1d = { (1 << 1) + 1, 12 + 1}; +const cfe_code cf2_run_eol_2d = { (1 << 1) + 0, 12 + 1}; + +/* White run codes. */ +const cf_runs cf_white_runs = +{ + /* Termination codes */ + { + {0x35, 8}, {0x7, 6}, {0x7, 4}, {0x8, 4}, + {0xb, 4}, {0xc, 4}, {0xe, 4}, {0xf, 4}, + {0x13, 5}, {0x14, 5}, {0x7, 5}, {0x8, 5}, + {0x8, 6}, {0x3, 6}, {0x34, 6}, {0x35, 6}, + {0x2a, 6}, {0x2b, 6}, {0x27, 7}, {0xc, 7}, + {0x8, 7}, {0x17, 7}, {0x3, 7}, {0x4, 7}, + {0x28, 7}, {0x2b, 7}, {0x13, 7}, {0x24, 7}, + {0x18, 7}, {0x2, 8}, {0x3, 8}, {0x1a, 8}, + {0x1b, 8}, {0x12, 8}, {0x13, 8}, {0x14, 8}, + {0x15, 8}, {0x16, 8}, {0x17, 8}, {0x28, 8}, + {0x29, 8}, {0x2a, 8}, {0x2b, 8}, {0x2c, 8}, + {0x2d, 8}, {0x4, 8}, {0x5, 8}, {0xa, 8}, + {0xb, 8}, {0x52, 8}, {0x53, 8}, {0x54, 8}, + {0x55, 8}, {0x24, 8}, {0x25, 8}, {0x58, 8}, + {0x59, 8}, {0x5a, 8}, {0x5b, 8}, {0x4a, 8}, + {0x4b, 8}, {0x32, 8}, {0x33, 8}, {0x34, 8} + }, + + /* Make-up codes */ + { + {0, 0} /* dummy */ , {0x1b, 5}, {0x12, 5}, {0x17, 6}, + {0x37, 7}, {0x36, 8}, {0x37, 8}, {0x64, 8}, + {0x65, 8}, {0x68, 8}, {0x67, 8}, {0xcc, 9}, + {0xcd, 9}, {0xd2, 9}, {0xd3, 9}, {0xd4, 9}, + {0xd5, 9}, {0xd6, 9}, {0xd7, 9}, {0xd8, 9}, + {0xd9, 9}, {0xda, 9}, {0xdb, 9}, {0x98, 9}, + {0x99, 9}, {0x9a, 9}, {0x18, 6}, {0x9b, 9}, + {0x8, 11}, {0xc, 11}, {0xd, 11}, {0x12, 12}, + {0x13, 12}, {0x14, 12}, {0x15, 12}, {0x16, 12}, + {0x17, 12}, {0x1c, 12}, {0x1d, 12}, {0x1e, 12}, + {0x1f, 12} + } +}; + +/* Black run codes. */ +const cf_runs cf_black_runs = +{ + /* Termination codes */ + { + {0x37, 10}, {0x2, 3}, {0x3, 2}, {0x2, 2}, + {0x3, 3}, {0x3, 4}, {0x2, 4}, {0x3, 5}, + {0x5, 6}, {0x4, 6}, {0x4, 7}, {0x5, 7}, + {0x7, 7}, {0x4, 8}, {0x7, 8}, {0x18, 9}, + {0x17, 10}, {0x18, 10}, {0x8, 10}, {0x67, 11}, + {0x68, 11}, {0x6c, 11}, {0x37, 11}, {0x28, 11}, + {0x17, 11}, {0x18, 11}, {0xca, 12}, {0xcb, 12}, + {0xcc, 12}, {0xcd, 12}, {0x68, 12}, {0x69, 12}, + {0x6a, 12}, {0x6b, 12}, {0xd2, 12}, {0xd3, 12}, + {0xd4, 12}, {0xd5, 12}, {0xd6, 12}, {0xd7, 12}, + {0x6c, 12}, {0x6d, 12}, {0xda, 12}, {0xdb, 12}, + {0x54, 12}, {0x55, 12}, {0x56, 12}, {0x57, 12}, + {0x64, 12}, {0x65, 12}, {0x52, 12}, {0x53, 12}, + {0x24, 12}, {0x37, 12}, {0x38, 12}, {0x27, 12}, + {0x28, 12}, {0x58, 12}, {0x59, 12}, {0x2b, 12}, + {0x2c, 12}, {0x5a, 12}, {0x66, 12}, {0x67, 12} + }, + + /* Make-up codes. */ + { + {0, 0} /* dummy */ , {0xf, 10}, {0xc8, 12}, {0xc9, 12}, + {0x5b, 12}, {0x33, 12}, {0x34, 12}, {0x35, 12}, + {0x6c, 13}, {0x6d, 13}, {0x4a, 13}, {0x4b, 13}, + {0x4c, 13}, {0x4d, 13}, {0x72, 13}, {0x73, 13}, + {0x74, 13}, {0x75, 13}, {0x76, 13}, {0x77, 13}, + {0x52, 13}, {0x53, 13}, {0x54, 13}, {0x55, 13}, + {0x5a, 13}, {0x5b, 13}, {0x64, 13}, {0x65, 13}, + {0x8, 11}, {0xc, 11}, {0xd, 11}, {0x12, 12}, + {0x13, 12}, {0x14, 12}, {0x15, 12}, {0x16, 12}, + {0x17, 12}, {0x1c, 12}, {0x1d, 12}, {0x1e, 12}, + {0x1f, 12} + } +}; + +/* Uncompressed codes. */ +const cfe_code cf_uncompressed[6] = +{ + {1, 1}, + {1, 2}, + {1, 3}, + {1, 4}, + {1, 5}, + {1, 6} +}; + +/* Uncompressed exit codes. */ +const cfe_code cf_uncompressed_exit[10] = +{ + {2, 8}, {3, 8}, + {2, 9}, {3, 9}, + {2, 10}, {3, 10}, + {2, 11}, {3, 11}, + {2, 12}, {3, 12} +}; + +/* Some C compilers insist on having executable code in every file.... */ +void scfetab_dummy(void) { } + diff --git a/filter/file.c b/filter/file.c new file mode 100644 index 00000000..c3ab63a4 --- /dev/null +++ b/filter/file.c @@ -0,0 +1,589 @@ +#include <fitz.h> + +/* TODO: nil filter on write */ + +fz_error * +fz_ferror(fz_file *f) +{ + fz_error *e = f->error; + f->error = nil; + return e; +} + +fz_error * +fz_openfile(fz_file **filep, char *path, int mode) +{ + fz_error *error; + fz_file *file; + int fd; + + assert(mode == O_RDONLY || mode == O_WRONLY); + + file = *filep = fz_malloc(sizeof(fz_file)); + if (!file) + return fz_outofmem; + + fd = open(path, mode, 0); + if (fd == -1) + return fz_throw("ioerror: open '%s': %s", path, strerror(errno)); + + file->mode = mode; + file->fd = fd; + file->depth = 0; + file->error = nil; + file->filter = nil; + file->in = nil; + file->out = nil; + + error = fz_newbuffer(&file->in, FZ_BUFSIZE); + if (error) + goto cleanup; + + error = fz_newbuffer(&file->out, FZ_BUFSIZE); + if (error) + goto cleanup; + + return nil; + +cleanup: + close(fd); + fz_free(file->out); + fz_free(file->in); + fz_free(file); + *filep = nil; + return error; +} + +void +fz_closefile(fz_file *file) +{ + assert(file->depth == 0); + + if (file->mode == O_WRONLY) + fz_flush(file); + + if (file->error) + { + fz_warn("%s", file->error->msg); + fz_freeerror(file->error); + file->error = nil; + } + + close(file->fd); + + if (file->filter) fz_freefilter(file->filter); + fz_freebuffer(file->in); + fz_freebuffer(file->out); + fz_free(file); +} + +fz_error * +fz_pushfilter(fz_file *file, fz_filter *filter) +{ + fz_error *error; + fz_buffer *buf; + + if (file->depth == 0) + { + buf = file->out; + file->out = file->in; + file->in = buf; + + file->out->rp = file->out->bp; + file->out->wp = file->out->bp; + file->out->eof = 0; + + file->filter = filter; + } + else + { + error = fz_chainpipeline(&file->filter, file->filter, filter, file->out); + if (error) + return error; + + error = fz_newbuffer(&file->out, FZ_BUFSIZE); + if (error) + { + fz_unchainpipeline(file->filter, &file->filter, &file->out); + return error; + } + } + + file->depth ++; + + return nil; +} + +void +fz_popfilter(fz_file *file) +{ + fz_buffer *buf; + + assert(file->depth > 0); + + if (file->mode == O_WRONLY) + fz_flush(file); + + if (file->error) + { + fz_warn("%s", file->error->msg); + fz_freeerror(file->error); + file->error = nil; + } + + if (file->depth == 1) + { + fz_freefilter(file->filter); + file->filter = nil; + + buf = file->out; + file->out = file->in; + file->in = buf; + + file->in->rp = file->in->bp; + file->in->wp = file->in->bp; + file->in->eof = 0; + } + else + { + fz_freebuffer(file->out); + fz_unchainpipeline(file->filter, &file->filter, &file->out); + } + + file->depth --; +} + +int +fz_seek(fz_file *f, int ofs) +{ + int t; + int c; + + assert(f->mode == O_RDONLY); + + if (f->filter) + { + if (ofs < fz_tell(f)) + { + f->error = fz_throw("ioerror: cannot seek backwards in filter"); + return -1; + } + while (fz_tell(f) < ofs) + { + c = fz_readbyte(f); + if (c == EOF) + return -1; + } + return 0; + } + + t = lseek(f->fd, ofs, 0); + if (t == -1) + { + f->error = fz_throw("ioerror: lseek: %s", strerror(errno)); + return -1; + } + + f->out->rp = f->out->bp; + f->out->wp = f->out->bp; + f->out->eof = 0; + + return 0; +} + +int +fz_tell(fz_file *f) +{ + int t; + + if (f->filter) + { + return f->filter->count - (f->out->wp - f->out->rp); + } + + t = lseek(f->fd, 0, 1); + if (t == -1) + { + f->error = fz_throw("ioerror: lseek: %s", strerror(errno)); + return -1; + } + + return t - (f->out->wp - f->out->rp); +} + +/* + * Read mode + */ + +static int doread(fz_buffer *b, int fd) +{ + int n = read(fd, b->wp, b->ep - b->wp); + if (n == -1) + return -1; + if (n == 0) + b->eof = 1; + b->wp += n; + return n; +} + +static int producedata(fz_file *f) +{ + fz_error *reason; + int produced; + int n; + + assert(f->mode == O_RDONLY); + assert(f->error == nil); + + if (!f->filter) + { + fz_rewindbuffer(f->out); + n = doread(f->out, f->fd); + if (n < 0) { + f->error = fz_throw("ioerror in read: %s", strerror(errno)); + return -1; + } + return 0; + } + + produced = 0; + + while (1) + { + reason = fz_process(f->filter, f->in, f->out); + + if (f->filter->produced) + produced = 1; + + if (reason == fz_ioneedin) + { + if (f->in->eof) { + f->error = fz_throw("ioerror: premature eof in filter"); + return -1; + } + + /* no space to produce, rewind or grow */ + if (f->in->wp == f->in->ep) + { + if (f->in->rp > f->in->bp) + f->error = fz_rewindbuffer(f->in); + else + f->error = fz_growbuffer(f->in); + if (f->error) + return -1; + } + + /* then fill with more input */ + n = doread(f->in, f->fd); + if (n < 0) { + f->error = fz_throw("ioerror in read: %s", strerror(errno)); + return -1; + } + + if (produced) + return 0; + } + + else if (reason == fz_ioneedout) + { + if (produced) + return 0; + + /* need more outspace, and produced no data */ + if (f->out->rp > f->out->bp) + f->error = fz_rewindbuffer(f->out); + else + f->error = fz_growbuffer(f->out); + if (f->error) + return -1; + } + + else if (reason == fz_iodone) + return 0; + + else { + f->error = reason; + return -1; + } + } +} + +int +fz_peekbyte(fz_file *f) +{ + if (f->out->rp == f->out->wp) + { + if (f->out->eof) return EOF; + if (producedata(f)) return EOF; + } + + if (f->out->rp < f->out->wp) + return *f->out->rp; + + return EOF; +} + +int +fz_readbyte(fz_file *f) +{ + if (f->out->rp == f->out->wp) + { + if (f->out->eof) return EOF; + if (producedata(f)) return EOF; + } + + if (f->out->rp < f->out->wp) + return *f->out->rp++; + + return EOF; +} + +int +fz_read(fz_file *f, char *buf, int n) +{ + int i = 0; + + while (i < n) + { + while (f->out->rp < f->out->wp && i < n) + buf[i++] = *f->out->rp ++; + + if (f->out->rp == f->out->wp) + { + if (f->out->eof) return i; + if (producedata(f) < 0) return -1; + } + } + + return i; +} + +int +fz_readline(fz_file *f, char *buf, int n) +{ + int c = EOF; + char *s = buf; + + while (n > 1) + { + c = fz_readbyte(f); + if (c == EOF) + break; + if (c == '\r') { + c = fz_peekbyte(f); + if (c == '\n') + c = fz_readbyte(f); + break; + } + if (c == '\n') + break; + *s++ = c; + n--; + } + if (n) + *s = '\0'; + return s - buf; +} + +/* + * Write mode + */ + +static int dowrite(fz_buffer *b, int fd) +{ + int n = write(fd, b->rp, b->wp - b->rp); + if (n == -1) + return -1; + b->rp += n; + return n; +} + +int +fz_write(fz_file *f, char *buf, int n) +{ + fz_error *reason; + int i = 0; + int x; + + assert(f->mode == O_WRONLY); + assert(f->error == nil); + + while (i < n) + { + while (f->in->rp < f->in->wp && i < n) + { + *f->in->rp++ = buf[i++]; + } + + if (f->in->rp == f->in->wp) + { + reason = fz_process(f->filter, f->in, f->out); + + if (reason == fz_ioneedin) + { + if (f->in->wp == f->in->ep) { + if (f->in->rp > f->in->bp) + f->error = fz_rewindbuffer(f->in); + else + f->error = fz_growbuffer(f->in); + if (f->error) + return -1; + } + } + + else if (reason == fz_ioneedout) + { + x = dowrite(f->out, f->fd); + if (x < 0) { + f->error = fz_throw("ioerror in write: %s", strerror(errno)); + return -1; + } + + if (f->out->rp > f->out->bp) + f->error = fz_rewindbuffer(f->out); + else + f->error = fz_growbuffer(f->out); + if (f->error) + return -1; + } + + else if (reason == fz_iodone) + { + x = dowrite(f->out, f->fd); + if (x < 0) { + f->error = fz_throw("ioerror in write: %s", strerror(errno)); + return -1; + } + break; + } + + else { + f->error = reason; + return -1; + } + } + } + + return i; +} + +int +fz_flush(fz_file *f) +{ + fz_error *reason; + int n; + + assert(f->mode == O_WRONLY); + assert(f->error == nil); + + f->in->eof = 1; + + while (!f->out->eof) + { + reason = fz_process(f->filter, f->in, f->out); + + if (reason == fz_ioneedin) { + f->error = fz_throw("ioerror: premature eof in filter"); + return -1; + } + + else if (reason == fz_ioneedout) + { + n = dowrite(f->out, f->fd); + if (n < 0) { + f->error = fz_throw("ioerror in write: %s", strerror(errno)); + return -1; + } + + if (f->out->rp > f->out->bp) + f->error = fz_rewindbuffer(f->out); + else + f->error = fz_growbuffer(f->out); + if (f->error) + return -1; + } + + else if (reason == fz_iodone) + { + n = dowrite(f->out, f->fd); + if (n < 0) { + f->error = fz_throw("ioerror in write: %s", strerror(errno)); + return -1; + } + break; + } + + else { + f->error = reason; + return -1; + } + } + + return 0; +} + +/* + * Utility function to consume contents of file stream into + * a freshly allocated buffer; realloced and trimmed to size. + */ + +enum { CHUNKSIZE = 4096 }; + +fz_error * +fz_readfile(unsigned char **bufp, int *lenp, fz_file *file) +{ + unsigned char *newbuf; + unsigned char *buf; + int len; + int pos; + int n; + + *bufp = nil; + *lenp = 0; + + len = 0; + pos = 0; + buf = nil; + + while (1) + { + if (len - pos == 0) + { + len += CHUNKSIZE; + newbuf = fz_realloc(buf, len); + if (!newbuf) + { + fz_free(buf); + return fz_outofmem; + } + buf = newbuf; + } + + n = fz_read(file, buf + pos, len - pos); + +printf("fz_read %d bytes\n", n); + + if (n < 0) + { + fz_free(buf); + return fz_ferror(file); + } + + pos += n; + + if (n < CHUNKSIZE) + { + newbuf = fz_realloc(buf, pos); + if (!newbuf) + { + fz_free(buf); + return fz_outofmem; + } + + *bufp = newbuf; + *lenp = pos; + return nil; + } + } +} + diff --git a/filter/filter.c b/filter/filter.c new file mode 100644 index 00000000..8e28a532 --- /dev/null +++ b/filter/filter.c @@ -0,0 +1,59 @@ +#include <fitz.h> + +fz_error fz_kioneedin = { + .msg = {"<ioneedin>"}, + .func = {"<process>"}, + .file = {"filter.c"}, + .line = 0, + .frozen = 1 +}; + +fz_error fz_kioneedout = { + .msg = {"<ioneedout>"}, + .func = {"<process>"}, + .file = {"filter.c"}, + .line = 0, + .frozen = 1 +}; + +fz_error fz_kiodone = { + .msg = {"<iodone>"}, + .func = {"<process>"}, + .file = {"filter.c"}, + .line = 0, + .frozen = 1 +}; + +fz_error * +fz_process(fz_filter *f, fz_buffer *in, fz_buffer *out) +{ + fz_error *reason; + unsigned char *oldrp; + unsigned char *oldwp; + + assert(!out->eof); + + oldrp = in->rp; + oldwp = out->wp; + + reason = f->process(f, in, out); + + assert(in->rp <= in->wp); + assert(out->wp <= out->ep); + + f->consumed = in->rp > oldrp; + f->produced = out->wp > oldwp; + f->count += out->wp - oldwp; + + if (reason != fz_ioneedin && reason != fz_ioneedout) + out->eof = 1; + + return reason; +} + +void +fz_freefilter(fz_filter *f) +{ + f->free(f); +} + diff --git a/filter/flate.c b/filter/flate.c new file mode 100644 index 00000000..f7046474 --- /dev/null +++ b/filter/flate.c @@ -0,0 +1,178 @@ +#include <fitz.h> + +#include <zlib.h> + +typedef struct fz_flate_s fz_flate; + +struct fz_flate_s +{ + fz_filter super; + z_stream z; +}; + +static void * +zmalloc(void *opaque, unsigned int items, unsigned int size) +{ + fz_memorycontext *mctx = (fz_memorycontext*)opaque; + return mctx->malloc(mctx, items * size); +} + +fz_error * +fz_newflated(fz_filter **fp, fz_obj *params) +{ + fz_error *eo; + int ei; + + FZ_NEWFILTER(fz_flate, f, flated); + + f->z.zalloc = zmalloc; + f->z.zfree = (void(*)(void*,void*))fz_currentmemorycontext()->free; + f->z.opaque = fz_currentmemorycontext(); + f->z.next_in = nil; + f->z.avail_in = 0; + + ei = inflateInit(&f->z); + if (ei != Z_OK) { + eo = fz_throw("ioerror: inflateInit: %s", f->z.msg); + fz_free(f); + return eo; + } + + return nil; +} + +void +fz_freeflated(fz_filter *f) +{ + z_streamp zp = &((fz_flate*)f)->z; + int err; + + err = inflateEnd(zp); + if (err != Z_OK) + fprintf(stderr, "inflateEnd: %s", zp->msg); + + fz_free(f); +} + +fz_error * +fz_processflated(fz_filter *f, fz_buffer *in, fz_buffer *out) +{ + z_streamp zp = &((fz_flate*)f)->z; + int err; + + if (in->rp == in->wp && !in->eof) + return fz_ioneedin; + if (out->wp == out->ep) + return fz_ioneedout; + + zp->next_in = in->rp; + zp->avail_in = in->wp - in->rp; + + zp->next_out = out->wp; + zp->avail_out = out->ep - out->wp; + + err = inflate(zp, Z_NO_FLUSH); + + in->rp = in->wp - zp->avail_in; + out->wp = out->ep - zp->avail_out; + + if (err == Z_STREAM_END) { + out->eof = 1; + return fz_iodone; + } + else if (err == Z_OK) { + if (in->rp == in->wp && !in->eof) + return fz_ioneedin; + if (out->wp == out->ep) + return fz_ioneedout; + return fz_ioneedin; /* hmm, what's going on here? */ + } + else { + return fz_throw("ioerror: inflate: %s", zp->msg); + } +} + +fz_error * +fz_newflatee(fz_filter **fp, fz_obj *params) +{ + fz_obj *obj; + fz_error *eo; + int effort; + int ei; + + FZ_NEWFILTER(fz_flate, f, flatee); + + effort = -1; + + if (params) { + obj = fz_dictgets(params, "Effort"); + if (obj) effort = fz_toint(obj); + } + + f->z.zalloc = zmalloc; + f->z.zfree = (void(*)(void*,void*))fz_currentmemorycontext()->free; + f->z.opaque = fz_currentmemorycontext(); + f->z.next_in = nil; + f->z.avail_in = 0; + + ei = deflateInit(&f->z, effort); + if (ei != Z_OK) { + eo = fz_throw("ioerror: deflateInit: %s", f->z.msg); + fz_free(f); + return eo; + } + + return nil; +} + +void +fz_freeflatee(fz_filter *f) +{ + z_streamp zp = &((fz_flate*)f)->z; + int err; + + err = deflateEnd(zp); + if (err != Z_OK) + fprintf(stderr, "deflateEnd: %s", zp->msg); + + fz_free(f); +} + +fz_error * +fz_processflatee(fz_filter *f, fz_buffer *in, fz_buffer *out) +{ + z_streamp zp = &((fz_flate*)f)->z; + int err; + + if (in->rp == in->wp && !in->eof) + return fz_ioneedin; + if (out->wp == out->ep) + return fz_ioneedout; + + zp->next_in = in->rp; + zp->avail_in = in->wp - in->rp; + + zp->next_out = out->wp; + zp->avail_out = out->ep - out->wp; + + err = deflate(zp, in->eof ? Z_FINISH : Z_NO_FLUSH); + + in->rp = in->wp - zp->avail_in; + out->wp = out->ep - zp->avail_out; + + if (err == Z_STREAM_END) { + out->eof = 1; + return fz_iodone; + } + else if (err == Z_OK) { + if (in->rp == in->wp && !in->eof) + return fz_ioneedin; + if (out->wp == out->ep) + return fz_ioneedout; + return fz_ioneedin; /* hmm? */ + } + else { + return fz_throw("ioerror: deflate: %s", zp->msg); + } +} + diff --git a/filter/jbig2d.c b/filter/jbig2d.c new file mode 100644 index 00000000..e58a5e49 --- /dev/null +++ b/filter/jbig2d.c @@ -0,0 +1,97 @@ +#include <fitz.h> + +/* TODO: complete rewrite with error checking and use fitz memctx */ + +/* +<rillian> so to use a global_ctx, you run your global data through a normal ctx +<rillian> then call jbig2_make_global_ctx with the normal context +<rillian> that does the (currently null) conversion +<maskros> make_global_ctx after i fed it all the global stream data? +<rillian> maskros: yes +<rillian> and you pass the new global ctx object to jbig2_ctx_new() when you ++create the per-page ctx +*/ + +#include <inttypes.h> +#include <jbig2.h> + +typedef struct fz_jbig2d_s fz_jbig2d; + +struct fz_jbig2d_s +{ + fz_filter super; + Jbig2Ctx *ctx; + Jbig2GlobalCtx *gctx; + Jbig2Image *page; + int idx; +}; + +fz_error * +fz_newjbig2d(fz_filter **fp, fz_obj *params) +{ + FZ_NEWFILTER(fz_jbig2d, d, jbig2d); + d->ctx = jbig2_ctx_new(nil, JBIG2_OPTIONS_EMBEDDED, nil, nil, nil); + d->page = nil; + d->idx = 0; + return nil; +} + +void +fz_freejbig2d(fz_filter *filter) +{ + fz_jbig2d *d = (fz_jbig2d*)filter; + jbig2_ctx_free(d->ctx); + fz_free(d); +} + +fz_error * +fz_setjbig2dglobalstream(fz_filter *filter, unsigned char *buf, int len) +{ + fz_jbig2d *d = (fz_jbig2d*)filter; + jbig2_data_in(d->ctx, buf, len); + d->gctx = jbig2_make_global_ctx(d->ctx); + d->ctx = jbig2_ctx_new(nil, JBIG2_OPTIONS_EMBEDDED, d->gctx, nil, nil); + return nil; +} + +fz_error * +fz_processjbig2d(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_jbig2d *d = (fz_jbig2d*)filter; + int len; + + while (1) + { + if (in->rp == in->wp) { + if (!in->eof) + return fz_ioneedin; + + if (!d->page) { + jbig2_complete_page(d->ctx); + d->page = jbig2_page_out(d->ctx); + } + + if (out->wp == out->ep) + return fz_ioneedout; + + len = out->ep - out->wp; + if (d->idx + len > d->page->height * d->page->stride) + len = d->page->height * d->page->stride - d->idx; + memcpy(out->wp, d->page->data + d->idx, len); + out->wp += len; + d->idx += len; + + if (d->idx == d->page->height * d->page->stride) { + jbig2_release_page(d->ctx, d->page); + out->eof = 1; + return fz_iodone; + } + } + else { + len = in->wp - in->rp; + jbig2_data_in(d->ctx, in->rp, len); + in->rp += len; + } + } +} + diff --git a/filter/jpxd.c b/filter/jpxd.c new file mode 100644 index 00000000..26ec743b --- /dev/null +++ b/filter/jpxd.c @@ -0,0 +1,144 @@ +#include <fitz.h> + +/* TODO: bpc */ + +/* + * We use the Jasper JPEG2000 coder for now. + */ + +#include <jasper/jasper.h> + +static char *colorspacename(jas_clrspc_t clrspc) +{ + switch (jas_clrspc_fam(clrspc)) + { + case JAS_CLRSPC_FAM_UNKNOWN: return "Unknown"; + case JAS_CLRSPC_FAM_XYZ: return "XYZ"; + case JAS_CLRSPC_FAM_LAB: return "Lab"; + case JAS_CLRSPC_FAM_GRAY: return "Gray"; + case JAS_CLRSPC_FAM_RGB: return "RGB"; + case JAS_CLRSPC_FAM_YCBCR: return "YCbCr"; + } + return "Unknown"; +} + +typedef struct fz_jpxd_s fz_jpxd; + +struct fz_jpxd_s +{ + fz_filter super; + jas_stream_t *stream; + jas_image_t *image; + int offset; + int stage; +}; + +fz_error * +fz_newjpxd(fz_filter **fp, fz_obj *params) +{ + int err; + + FZ_NEWFILTER(fz_jpxd, d, jpxd); + + err = jas_init(); + if (err) { + fz_free(d); + return fz_throw("ioerror in jpxd: jas_init()"); + } + + d->stream = jas_stream_memopen(nil, 0); + if (!d->stream) { + fz_free(d); + return fz_throw("ioerror in jpxd: jas_stream_memopen()"); + } + + d->image = nil; + d->offset = 0; + d->stage = 0; + + return nil; +} + +void +fz_freejpxd(fz_filter *filter) +{ + fz_jpxd *d = (fz_jpxd*)filter; + if (d->stream) jas_stream_close(d->stream); + if (d->image) jas_image_destroy(d->image); + fz_free(d); +} + +fz_error * +fz_processjpxd(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_jpxd *d = (fz_jpxd*)filter; + int n, bpc, w, h; + int i, x, y; + + switch (d->stage) + { + case 0: goto input; + case 1: goto decode; + case 2: goto output; + } + +input: + while (in->rp < in->wp) { + n = jas_stream_write(d->stream, in->rp, in->wp - in->rp); + in->rp += n; + } + + if (!in->eof) + return fz_ioneedin; + + d->stage = 1; + +decode: + jas_stream_seek(d->stream, 0, 0); + + d->image = jas_image_decode(d->stream, -1, 0); + if (!d->image) + return fz_throw("ioerror in jpxd: unable to decode image data"); + + fprintf(stderr, "P%c\n# JPX %d x %d n=%d bpc=%d colorspace=%04x %s\n%d %d\n%d\n", + jas_image_numcmpts(d->image) == 1 ? '5' : '6', + + jas_image_width(d->image), + jas_image_height(d->image), + jas_image_numcmpts(d->image), + jas_image_cmptprec(d->image, 0), + jas_image_clrspc(d->image), + colorspacename(jas_image_clrspc(d->image)), + + jas_image_width(d->image), + jas_image_height(d->image), + (1 << jas_image_cmptprec(d->image, 0)) - 1); + + d->stage = 2; + +output: + w = jas_image_width(d->image); + h = jas_image_height(d->image); + n = jas_image_numcmpts(d->image); + bpc = jas_image_cmptprec(d->image, 0); /* use precision of first component for all... */ + + while (d->offset < w * h) + { + y = d->offset / w; + x = d->offset - y * w; + + /* FIXME bpc != 8 */ + + if (out->wp + n >= out->ep) + return fz_ioneedout; + + for (i = 0; i < n; i++) + *out->wp++ = jas_image_readcmptsample(d->image, i, x, y); + + d->offset ++; + } + + out->eof = 1; + return fz_iodone; +} + diff --git a/filter/lzwd.c b/filter/lzwd.c new file mode 100644 index 00000000..1ab47357 --- /dev/null +++ b/filter/lzwd.c @@ -0,0 +1,263 @@ +#include <fitz.h> + +/* TODO: error checking */ + +#define noDEBUG 1 + +#ifdef DEBUG +#define DPRINT(...) fprintf(stderr, __VA_ARGS__) +#else +#define DPRINT(...) +#endif + +enum +{ + MINBITS = 9, + MAXBITS = 12, + NUMCODES = (1 << MAXBITS), + LZW_CLEAR = 256, + LZW_EOD = 257, + LZW_FIRST = 258, +}; + +typedef struct lzw_code_s lzw_code; + +struct lzw_code_s +{ + int prev; /* prev code (in string) */ + unsigned short length; /* string len, including this token */ + unsigned char value; /* data value */ + unsigned char firstchar; /* first token of string */ +}; + +typedef struct fz_lzwd_s fz_lzwd; + +struct fz_lzwd_s +{ + fz_filter super; + + int earlychange; + + unsigned int word; /* bits loaded from data */ + int bidx; + + int resume; /* resume output of code from needout */ + int codebits; /* num bits/code */ + int code; /* current code */ + int oldcode; /* previously recognized code */ + int nextcode; /* next free entry */ + lzw_code table[NUMCODES]; +}; + +fz_error * +fz_newlzwd(fz_filter **fp, fz_obj *params) +{ + int i; + + FZ_NEWFILTER(fz_lzwd, lzw, lzwd); + + lzw->earlychange = 0; + + if (params) { + fz_obj *obj; + obj = fz_dictgets(params, "EarlyChange"); + if (obj) lzw->earlychange = fz_toint(obj) != 0; + } + + lzw->bidx = 32; + lzw->word = 0; + + for (i = 0; i < 256; i++) { + lzw->table[i].value = i; + lzw->table[i].firstchar = i; + lzw->table[i].length = 1; + lzw->table[i].prev = -1; + } + + for (i = LZW_FIRST; i < NUMCODES; i++) { + lzw->table[i].value = 0; + lzw->table[i].firstchar = 0; + lzw->table[i].length = 0; + lzw->table[i].prev = -1; + } + + lzw->codebits = MINBITS; + lzw->code = -1; + lzw->nextcode = LZW_FIRST; + lzw->oldcode = -1; + lzw->resume = 0; + + return nil; +} + +void +fz_freelzwd(fz_filter *filter) +{ + fz_free(filter); +} + +static inline void eatbits(fz_lzwd *lzw, int nbits) +{ + lzw->word <<= nbits; + lzw->bidx += nbits; +} + +static inline fz_error * fillbits(fz_lzwd *lzw, fz_buffer *in) +{ + while (lzw->bidx >= 8) + { + if (in->rp + 1 > in->wp) + return fz_ioneedin; + lzw->bidx -= 8; + lzw->word |= *in->rp << lzw->bidx; + in->rp ++; + } + return nil; +} + +fz_error * +fz_processlzwd(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_lzwd *lzw = (fz_lzwd*)filter; + unsigned char *s; + int len; + + if (lzw->resume) { + lzw->resume = 0; + goto output; + } + + while (1) + { + if (fillbits(lzw, in)) { + DPRINT("FILL[eof=%d] bidx=%d word=%08x\n", in->eof, lzw->bidx, lzw->word); + if (in->eof) { + if (lzw->bidx > 32 - lzw->codebits) { + out->eof = 1; + return fz_iodone; + } + } + else { + return fz_ioneedin; + } + } + + lzw->code = lzw->word >> (32 - lzw->codebits); + + DPRINT("CODE[%d]: %03x (%d)\n", lzw->codebits, lzw->code, lzw->code); + + if (lzw->code == LZW_EOD) + { + DPRINT("-> LZW_EOD\n"); + + eatbits(lzw, lzw->codebits); + out->eof = 1; + return fz_iodone; + } + + if (lzw->code == LZW_CLEAR) + { + DPRINT("-> LZW_CLEAR\n"); + + int oldcodebits = lzw->codebits; + + lzw->codebits = MINBITS; + lzw->nextcode = LZW_FIRST; + + lzw->code = lzw->word >> (32 - oldcodebits - MINBITS) & ((1 << MINBITS) - 1); + + DPRINT("CODE[%d]: %03x (%d) [after CLEAR]\n", lzw->codebits, lzw->code, lzw->code); + + if (lzw->code == LZW_EOD) { + DPRINT("-> LZW_EOD\n"); + + eatbits(lzw, oldcodebits + MINBITS); + out->eof = 1; + return fz_iodone; + } + + DPRINT("-> %d\n", lzw->code); + + if (out->wp + 1 > out->ep) + return fz_ioneedout; + + *out->wp++ = lzw->code; + + lzw->oldcode = lzw->code; + + eatbits(lzw, oldcodebits + MINBITS); + + continue; + } + + eatbits(lzw, lzw->codebits); + + /* if stream starts without a clear code, oldcode is undefined... */ + if (lzw->oldcode == -1) { + lzw->oldcode = lzw->code; + goto output; + } + + /* add new entry to the code table */ + lzw->table[lzw->nextcode].prev = lzw->oldcode; + lzw->table[lzw->nextcode].firstchar = lzw->table[lzw->oldcode].firstchar; + lzw->table[lzw->nextcode].length = lzw->table[lzw->oldcode].length + 1; + if (lzw->code < lzw->nextcode) + lzw->table[lzw->nextcode].value = lzw->table[lzw->code].firstchar; + else + lzw->table[lzw->nextcode].value = lzw->table[lzw->nextcode].firstchar; + + DPRINT("NEW[%d] prev=%d, f=%d, l=%d, v=%d\n", + lzw->nextcode, + lzw->table[lzw->nextcode].prev, + lzw->table[lzw->nextcode].firstchar, + lzw->table[lzw->nextcode].length, + lzw->table[lzw->nextcode].value); + + lzw->nextcode ++; + + if (lzw->nextcode >= (1 << lzw->codebits) - lzw->earlychange - 1) + { + lzw->codebits ++; + if (lzw->codebits > MAXBITS) + lzw->codebits = MAXBITS; /* FIXME */ + } + + lzw->oldcode = lzw->code; + +output: + /* code maps to a string, copy to output (in reverse...) */ + if (lzw->code > 255) + { + if (out->wp + lzw->table[lzw->code].length > out->ep) { + lzw->resume = 1; + return fz_ioneedout; + } + + len = lzw->table[lzw->code].length; + s = out->wp + len; + + DPRINT("OUT code=%d len=%d\n", lzw->code, len); + do { + DPRINT("-> %d\n", lzw->table[lzw->code].value); + *(--s) = lzw->table[lzw->code].value; + lzw->code = lzw->table[lzw->code].prev; + } while (lzw->code >= 0 && s > out->wp); + out->wp += len; + } + + /* ... or just a single character */ + else + { + if (out->wp + 1 > out->ep) { + lzw->resume = 1; + return fz_ioneedout; + } + + DPRINT("OUT code=%d\n-> %d\n", lzw->code, lzw->code); + + *out->wp++ = lzw->code; + } + } +} + diff --git a/filter/lzwe.c b/filter/lzwe.c new file mode 100644 index 00000000..dbb6967b --- /dev/null +++ b/filter/lzwe.c @@ -0,0 +1,257 @@ +#include <fitz.h> + +#define noDEBUG 1 + +#ifdef DEBUG +#define DPRINT(...) fprintf(stderr, __VA_ARGS__) +#else +#define DPRINT(...) +#endif + +enum +{ + MINBITS = 9, + MAXBITS = 12, + MAXBYTES = 2, + NUMCODES = (1 << MAXBITS), + LZW_CLEAR = 256, + LZW_EOD = 257, + LZW_FIRST = 258, + HSIZE = 9001, /* 91% occupancy (???) */ + HSHIFT = (13 - 8), +}; + +typedef struct lzw_hash_s lzw_hash; + +struct lzw_hash_s +{ + int hash; + int code; +}; + +typedef struct fz_lzwe_s fz_lzwe; + +struct fz_lzwe_s +{ + fz_filter super; + + int earlychange; + + int bidx; /* partial bits used in out->wp */ + unsigned char bsave; /* partial byte saved between process() calls */ + + int resume; + int code; + int fcode; + int hcode; + + int codebits; + int oldcode; + int nextcode; + + lzw_hash table[HSIZE]; +}; + +static void +clearhash(fz_lzwe *lzw) +{ + int i; + for (i = 0; i < HSIZE; i++) + lzw->table[i].hash = -1; +} + +fz_error * +fz_newlzwe(fz_filter **fp, fz_obj *params) +{ + FZ_NEWFILTER(fz_lzwe, lzw, lzwe); + + lzw->earlychange = 0; + + if (params) { + fz_obj *obj; + obj = fz_dictgets(params, "EarlyChange"); + if (obj) lzw->earlychange = fz_toint(obj) != 0; + } + + lzw->bidx = 0; + lzw->bsave = 0; + + lzw->resume = 0; + lzw->code = -1; + lzw->hcode = -1; + lzw->fcode = -1; + + lzw->codebits = MINBITS; + lzw->nextcode = LZW_FIRST; + lzw->oldcode = -1; /* generates LZW_CLEAR */ + + clearhash(lzw); + + return nil; +} + +void +fz_freelzwe(fz_filter *filter) +{ + fz_free(filter); +} + +static void +putcode(fz_lzwe *lzw, fz_buffer *out, int code) +{ + int nbits = lzw->codebits; + + DPRINT("CODE[%d] %d\n", nbits, code); + + while (nbits > 0) + { + if (lzw->bidx == 0) { + *out->wp = 0; + } + + /* code does not fit: shift right */ + if (nbits > (8 - lzw->bidx)) { + *out->wp |= code >> (nbits - (8 - lzw->bidx)); + nbits = nbits - (8 - lzw->bidx); + lzw->bidx = 0; + out->wp ++; + } + + /* shift left */ + else { + *out->wp |= code << ((8 - lzw->bidx) - nbits); + lzw->bidx += nbits; + if (lzw->bidx == 8) { + lzw->bidx = 0; + out->wp ++; + } + nbits = 0; + } + } +} + + +static fz_error * +compress(fz_lzwe *lzw, fz_buffer *in, fz_buffer *out) +{ + if (lzw->resume) { + lzw->resume = 0; + goto resume; + } + + /* at start of data, output a clear code */ + if (lzw->oldcode == -1) + { + if (out->wp + 3 > out->ep) + return fz_ioneedout; + + if (in->rp + 1 > in->wp) { + if (in->eof) + goto eof; + return fz_ioneedin; + } + + putcode(lzw, out, LZW_CLEAR); + + lzw->oldcode = *in->rp++; + } + +begin: + while (1) + { + if (in->rp + 1 > in->wp) { + if (in->eof) + goto eof; + return fz_ioneedin; + } + + /* read character */ + lzw->code = *in->rp++; + + DPRINT("READ %d\n", lzw->code); + + /* hash string + character */ + lzw->fcode = (lzw->code << MAXBITS) + lzw->oldcode; + lzw->hcode = (lzw->code << HSHIFT) ^ lzw->oldcode; + + /* primary hash */ + if (lzw->table[lzw->hcode].hash == lzw->fcode) { + lzw->oldcode = lzw->table[lzw->hcode].code; + continue; + } + + /* secondary hash */ + if (lzw->table[lzw->hcode].hash != -1) { + int disp = HSIZE - lzw->hcode; + if (lzw->hcode == 0) + disp = 1; + do { + lzw->hcode = lzw->hcode - disp; + if (lzw->hcode < 0) + lzw->hcode += HSIZE; + if (lzw->table[lzw->hcode].hash == lzw->fcode) { + lzw->oldcode = lzw->table[lzw->hcode].code; + goto begin; + } + } while (lzw->table[lzw->hcode].hash != -1); + } + +resume: + /* new entry: emit code and add to table */ + + /* reserve space for this code and an eventual CLEAR code */ + if (out->wp + 5 > out->ep) { + lzw->resume = 1; + return fz_ioneedout; + } + + putcode(lzw, out, lzw->oldcode); + + lzw->oldcode = lzw->code; + lzw->table[lzw->hcode].code = lzw->nextcode; + lzw->table[lzw->hcode].hash = lzw->fcode; + + lzw->nextcode ++; + + /* table is full: emit clear code and reset */ + if (lzw->nextcode == NUMCODES - 1) { + putcode(lzw, out, LZW_CLEAR); + clearhash(lzw); + lzw->nextcode = LZW_FIRST; + lzw->codebits = MINBITS; + } + + /* check if next entry will be too big for the code size */ + else if (lzw->nextcode >= (1 << lzw->codebits) - lzw->earlychange) { + lzw->codebits ++; + } + } + +eof: + if (out->wp + 5 > out->ep) + return fz_ioneedout; + + putcode(lzw, out, lzw->oldcode); + putcode(lzw, out, LZW_EOD); + + out->eof = 1; + return fz_iodone; +} + +fz_error * +fz_processlzwe(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_lzwe *lzw = (fz_lzwe*)filter; + fz_error *err; + + /* restore partial bits */ + *out->wp = lzw->bsave; + + err = compress(lzw, in, out); + + /* save partial bits */ + lzw->bsave = *out->wp; + + return err; +} + diff --git a/filter/null.c b/filter/null.c new file mode 100644 index 00000000..a4af03b1 --- /dev/null +++ b/filter/null.c @@ -0,0 +1,51 @@ +#include <fitz.h> + +typedef struct fz_nullfilter_s fz_nullfilter; + +struct fz_nullfilter_s +{ + fz_filter super; + int len; + int cur; +}; + +fz_error * +fz_newnullfilter(fz_filter **fp, int len) +{ + FZ_NEWFILTER(fz_nullfilter, f, nullfilter); + f->len = len; + f->cur = 0; + return nil; +} + +void +fz_freenullfilter(fz_filter *f) +{ + fz_free(f); +} + +fz_error * +fz_processnullfilter(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_nullfilter *f = (fz_nullfilter*)filter; + int n; + + n = MIN(MIN(in->wp - in->rp, out->ep - out->wp), f->len - f->cur); + + if (n) { + memcpy(out->wp, in->rp, n); + in->rp += n; + out->wp += n; + f->cur += n; + } + + if (f->cur == f->len) + return fz_iodone; + if (in->rp == in->wp) + return fz_ioneedin; + if (out->wp == out->ep) + return fz_ioneedout; + + return fz_throw("braindead programmer in nullfilter"); +} + diff --git a/filter/pipeline.c b/filter/pipeline.c new file mode 100644 index 00000000..ef0412c0 --- /dev/null +++ b/filter/pipeline.c @@ -0,0 +1,128 @@ +#include <fitz.h> + +#define noDEBUG 1 + +typedef struct fz_pipeline_s fz_pipeline; + +fz_error * fz_processpipeline(fz_filter *filter, fz_buffer *in, fz_buffer *out); + +struct fz_pipeline_s +{ + fz_filter super; + fz_filter *head; + fz_buffer *buffer; + fz_filter *tail; + int tailneedsin; +}; + +fz_error * +fz_chainpipeline(fz_filter **fp, fz_filter *head, fz_filter *tail, fz_buffer *buf) +{ + FZ_NEWFILTER(fz_pipeline, p, pipeline); + p->head = head; + p->tail = tail; + p->tailneedsin = 1; + p->buffer = buf; + return nil; +} + +void +fz_unchainpipeline(fz_filter *filter, fz_filter **oldfp, fz_buffer **oldbp) +{ + fz_pipeline *p = (fz_pipeline*)filter; + *oldfp = p->head; + *oldbp = p->buffer; + fz_freefilter(p->tail); + fz_free(p); +} + +fz_error * +fz_newpipeline(fz_filter **fp, fz_filter *head, fz_filter *tail) +{ + fz_error *err; + + FZ_NEWFILTER(fz_pipeline, p, pipeline); + p->head = head; + p->tail = tail; + p->tailneedsin = 1; + + err = fz_newbuffer(&p->buffer, FZ_BUFSIZE); + if (err) { fz_free(p); return err; } + + return nil; +} + +void +fz_freepipeline(fz_filter *filter) +{ + fz_pipeline *p = (fz_pipeline*)filter; + fz_freefilter(p->head); + fz_freefilter(p->tail); + fz_freebuffer(p->buffer); + fz_free(p); +} + +fz_error * +fz_processpipeline(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_pipeline *p = (fz_pipeline*)filter; + fz_error *e; + + if (p->buffer->eof) + goto tail; + + if (p->tailneedsin && p->head->produced) + goto tail; + +head: + e = fz_process(p->head, in, p->buffer); + + if (e == fz_ioneedin) + return fz_ioneedin; + + else if (e == fz_ioneedout) + { + if (p->tailneedsin && !p->head->produced) + { + fz_error *be = nil; + if (p->buffer->rp > p->buffer->bp) + be = fz_rewindbuffer(p->buffer); + else + be = fz_growbuffer(p->buffer); + if (be) + return be; + goto head; + } + goto tail; + } + + else if (e == fz_iodone) + goto tail; + + else + return e; + +tail: + e = fz_process(p->tail, p->buffer, out); + + if (e == fz_ioneedin) + { + if (p->buffer->eof) + return fz_throw("ioerror: premature eof in pipeline"); + p->tailneedsin = 1; + goto head; + } + + else if (e == fz_ioneedout) + { + p->tailneedsin = 0; + return fz_ioneedout; + } + + else if (e == fz_iodone) + return fz_iodone; + + else + return e; +} + diff --git a/filter/predict.c b/filter/predict.c new file mode 100644 index 00000000..d5f1f54d --- /dev/null +++ b/filter/predict.c @@ -0,0 +1,239 @@ +#include <fitz.h> + +/* TODO: check if this works with 16bpp images */ + +enum { MAXC = 16 }; + +typedef struct fz_predict_s fz_predict; + +struct fz_predict_s +{ + fz_filter super; + + int predictor; + int columns; + int colors; + int bpc; + + int stride; + int bpp; + unsigned char *ref; + + int encode; +}; + +fz_error * +fz_newpredict(fz_filter **fp, fz_obj *params, int encode) +{ + fz_obj *obj; + + FZ_NEWFILTER(fz_predict, p, predict); + + p->encode = encode; + + p->predictor = 1; + p->columns = 1; + p->colors = 1; + p->bpc = 8; + + obj = fz_dictgets(params, "Predictor"); + if (obj) p->predictor = fz_toint(obj); + + obj = fz_dictgets(params, "Columns"); + if (obj) p->columns = fz_toint(obj); + + obj = fz_dictgets(params, "Colors"); + if (obj) p->colors = fz_toint(obj); + + obj = fz_dictgets(params, "BitsPerComponent"); + if (obj) p->bpc = fz_toint(obj); + + p->stride = (p->bpc * p->colors * p->columns + 7) / 8; + p->bpp = (p->bpc * p->colors + 7) / 8; + + if (p->predictor >= 10) { + p->ref = fz_malloc(p->stride); + if (!p->ref) { fz_free(p); return fz_outofmem; } + memset(p->ref, 0, p->stride); + } + else { + p->ref = nil; + } + + return nil; +} + +void +fz_freepredict(fz_filter *filter) +{ + fz_predict *p = (fz_predict*)filter; + fz_free(p->ref); + fz_free(p); +} + +static inline int +getcomponent(unsigned char *buf, int x, int bpc) +{ + switch (bpc) + { + case 1: return buf[x / 8] >> (7 - (x % 8)) & 0x01; + case 2: return buf[x / 4] >> ((3 - (x % 4)) * 2) & 0x03; + case 4: return buf[x / 2] >> ((1 - (x % 2)) * 4) & 0x0f; + case 8: return buf[x]; + } + return 0; +} + +static inline void +putcomponent(unsigned char *buf, int x, int bpc, int value) +{ + switch (bpc) + { + case 1: buf[x / 8] |= value << (7 - (x % 8)); break; + case 2: buf[x / 4] |= value << ((3 - (x % 4)) * 2); break; + case 4: buf[x / 2] |= value << ((1 - (x % 2)) * 4); break; + case 8: buf[x] = value; break; + } +} + +static inline int +paeth(int a, int b, int c) +{ + /* The definitions of ac and bc are correct, not a typo. */ + int ac = b - c, bc = a - c, abcc = ac + bc; + int pa = (ac < 0 ? -ac : ac); + int pb = (bc < 0 ? -bc : bc); + int pc = (abcc < 0 ? -abcc : abcc); + return pa <= pb && pa <= pc ? a : pb <= pc ? b : c; +} + +static inline void +none(fz_predict *p, unsigned char *in, unsigned char *out) +{ + memcpy(out, in, p->stride); +} + +static void +tiff(fz_predict *p, unsigned char *in, unsigned char *out) +{ + int left[MAXC]; + int i, k; + + for (k = 0; k < p->colors; k++) + left[k] = 0; + + for (i = 0; i < p->columns; i++) { + for (k = 0; k < p->colors; k++) { + int a = getcomponent(in, i * p->colors + k, p->bpc); + int b = p->encode ? a - left[k] : a + left[k]; + int c = b % (1 << p->bpc); + putcomponent(out, i * p->colors + k, p->bpc, c); + left[k] = p->encode ? a : c; + } + } +} + +static void +png(fz_predict *p, unsigned char *in, unsigned char *out, int predictor) +{ + int upleft[MAXC], left[MAXC], i, k; + + for (k = 0; k < p->bpp; k++) { + left[k] = 0; + upleft[k] = 0; + } + + if (p->encode) + { + for (k = 0, i = 0; i < p->stride; k = (k + 1) % p->bpp, i ++) + { + switch (predictor) + { + case 0: out[i] = in[i]; break; + case 1: out[i] = in[i] - left[k]; break; + case 2: out[i] = in[i] - p->ref[i]; break; + case 3: out[i] = in[i] - (left[k] + p->ref[i]) / 2; break; + case 4: out[i] = in[i] - paeth(left[k], p->ref[i], upleft[k]); break; + } + left[k] = in[i]; + upleft[k] = p->ref[i]; + } + } + + else + { + for (k = 0, i = 0; i < p->stride; k = (k + 1) % p->bpp, i ++) + { + switch (predictor) + { + case 0: out[i] = in[i]; break; + case 1: out[i] = in[i] + left[k]; break; + case 2: out[i] = in[i] + p->ref[i]; break; + case 3: out[i] = in[i] + (left[k] + p->ref[i]) / 2; break; + case 4: out[i] = in[i] + paeth(left[k], p->ref[i], upleft[k]); break; + } + left[k] = out[i]; + upleft[k] = p->ref[i]; + } + } +} + +fz_error * +fz_processpredict(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_predict *dec = (fz_predict*)filter; + int ispng = dec->predictor >= 10; + int predictor; + + while (1) + { + if (in->rp + dec->stride + (!dec->encode && ispng) > in->wp) { + if (in->eof) + return fz_iodone; + return fz_ioneedin; + } + + if (out->wp + dec->stride + (dec->encode && ispng) > out->ep) + return fz_ioneedout; + + if (dec->predictor == 1) { + none(dec, in->rp, out->wp); + } + else if (dec->predictor == 2) { + if (dec->bpc != 8) + memset(out->wp, 0, dec->stride); + tiff(dec, in->rp, out->wp); + } + else { + if (dec->encode) { + predictor = dec->predictor - 10; + if (predictor < 0 || predictor > 4) + predictor = 1; + *out->wp ++ = predictor; + } + else { + predictor = *in->rp++; + } + png(dec, in->rp, out->wp, predictor); + } + + if (dec->ref) + memcpy(dec->ref, out->wp, dec->stride); + + in->rp += dec->stride; + out->wp += dec->stride; + } +} + +fz_error * +fz_newpredictd(fz_filter **fp, fz_obj *params) +{ + return fz_newpredict(fp, params, 0); +} + +fz_error * +fz_newpredicte(fz_filter **fp, fz_obj *params) +{ + return fz_newpredict(fp, params, 1); +} + diff --git a/filter/rld.c b/filter/rld.c new file mode 100644 index 00000000..a66e0f94 --- /dev/null +++ b/filter/rld.c @@ -0,0 +1,67 @@ +#include <fitz.h> + +fz_error * +fz_newrld(fz_filter **fp, fz_obj *params) +{ + FZ_NEWFILTER(fz_filter, f, rld); + return nil; +} + +void +fz_freerld(fz_filter *rld) +{ + fz_free(rld); +} + +fz_error * +fz_processrld(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + int run, i; + unsigned char c; + + while (1) + { + if (in->rp == in->wp) + return fz_ioneedin; + + if (out->wp == out->ep) + return fz_ioneedout; + + run = *in->rp++; + + if (run == 128) { + out->eof = 1; + return fz_iodone; + } + + else if (run < 128) { + run = run + 1; + if (in->rp + run > in->wp) { + in->rp --; + return fz_ioneedin; + } + if (out->wp + run > out->ep) { + in->rp --; + return fz_ioneedout; + } + for (i = 0; i < run; i++) + *out->wp++ = *in->rp++; + } + + else if (run > 128) { + run = 257 - run; + if (in->rp + 1 > in->wp) { + in->rp --; + return fz_ioneedin; + } + if (out->wp + run > out->ep) { + in->rp --; + return fz_ioneedout; + } + c = *in->rp++; + for (i = 0; i < run; i++) + *out->wp++ = c; + } + } +} + diff --git a/filter/rle.c b/filter/rle.c new file mode 100644 index 00000000..0e611918 --- /dev/null +++ b/filter/rle.c @@ -0,0 +1,238 @@ +#include <fitz.h> + +/* TODO: rewrite! + * make it non-optimal or something, + * just not this horrid mess... + */ + +#define noDEBUG + +typedef struct fz_rle_s fz_rle; + +struct fz_rle_s +{ + fz_filter super; + int reclen; + int curlen; + int state; + int run; + unsigned char buf[128]; +}; + +enum { + ZERO, + ONE, + DIFF, + SAME, + END +}; + +fz_error * +fz_newrle(fz_filter **fp, fz_obj *params) +{ + FZ_NEWFILTER(fz_rle, enc, rle); + + if (params) + enc->reclen = fz_toint(params); + else + enc->reclen = 0; + + enc->curlen = 0; + enc->state = ZERO; + enc->run = 0; + + return nil; +} + +void +fz_freerle(fz_filter *enc) +{ + fz_free(enc); +} + +static fz_error * +putone(fz_rle *enc, fz_buffer *in, fz_buffer *out) +{ + if (out->wp + 2 >= out->ep) + return fz_ioneedout; + +#ifdef DEBUG +fprintf(stderr, "one '%c'\n", enc->buf[0]); +#endif + + *out->wp++ = 0; + *out->wp++ = enc->buf[0]; + + return nil; +} + +static fz_error * +putsame(fz_rle *enc, fz_buffer *in, fz_buffer *out) +{ + if (out->wp + enc->run >= out->ep) + return fz_ioneedout; + +#ifdef DEBUG +fprintf(stderr, "same %d x '%c'\n", enc->run, enc->buf[0]); +#endif + + *out->wp++ = 257 - enc->run; + *out->wp++ = enc->buf[0]; + return nil; +} + +static fz_error * +putdiff(fz_rle *enc, fz_buffer *in, fz_buffer *out) +{ + int i; + if (out->wp + enc->run >= out->ep) + return fz_ioneedout; + +#ifdef DEBUG +fprintf(stderr, "diff %d\n", enc->run); +#endif + + *out->wp++ = enc->run - 1; + for (i = 0; i < enc->run; i++) + *out->wp++ = enc->buf[i]; + return nil; +} + +static fz_error * +puteod(fz_rle *enc, fz_buffer *in, fz_buffer *out) +{ + if (out->wp + 1 >= out->ep) + return fz_ioneedout; + +#ifdef DEBUG +fprintf(stderr, "eod\n"); +#endif + + *out->wp++ = 128; + return nil; +} + +static fz_error * +savebuf(fz_rle *enc, fz_buffer *in, fz_buffer *out) +{ + switch (enc->state) + { + case ZERO: return nil; + case ONE: return putone(enc, in, out); + case SAME: return putsame(enc, in, out); + case DIFF: return putdiff(enc, in, out); + case END: return puteod(enc, in, out); + default: assert(!"invalid state in rle"); return nil; + } +} + +fz_error * +fz_processrle(fz_filter *filter, fz_buffer *in, fz_buffer *out) +{ + fz_rle *enc = (fz_rle*)filter; + fz_error *err; + unsigned char c; + + while (1) + { + + if (enc->reclen && enc->curlen == enc->reclen) { + err = savebuf(enc, in, out); + if (err) return err; +#ifdef DEBUG +fprintf(stderr, "--record--\n"); +#endif + enc->state = ZERO; + enc->curlen = 0; + } + + if (in->rp == in->wp) { + if (in->eof) { + if (enc->state != END) { + err = savebuf(enc, in, out); + if (err) return err; + } + enc->state = END; + } + else + return fz_ioneedin; + } + + c = *in->rp; + + switch (enc->state) + { + case ZERO: + enc->state = ONE; + enc->run = 1; + enc->buf[0] = c; + break; + + case ONE: + enc->state = DIFF; + enc->run = 2; + enc->buf[1] = c; + break; + + case DIFF: + /* out of space */ + if (enc->run == 128) { + err = putdiff(enc, in, out); + if (err) return err; + + enc->state = ONE; + enc->run = 1; + enc->buf[0] = c; + } + + /* run of three that are the same */ + else if ((enc->run > 1) && + (c == enc->buf[enc->run - 1]) && + (c == enc->buf[enc->run - 2])) + { + if (enc->run >= 3) { + enc->run -= 2; /* skip prev two for diff run */ + err = putdiff(enc, in, out); + if (err) return err; + } + + enc->state = SAME; + enc->run = 3; + enc->buf[0] = c; + } + + /* keep on collecting */ + else { + enc->buf[enc->run++] = c; + } + break; + + case SAME: + if (enc->run == 128 || c != enc->buf[0]) { + err = putsame(enc, in, out); + if (err) return err; + + enc->state = ONE; + enc->run = 1; + enc->buf[0] = c; + } + else { + enc->run ++; + } + break; + + case END: + err = puteod(enc, in, out); + if (err) return err; + + out->eof = 1; + return fz_iodone; + } + + in->rp ++; + + enc->curlen ++; + + } +} + diff --git a/include/fitz.h b/include/fitz.h new file mode 100644 index 00000000..20c105db --- /dev/null +++ b/include/fitz.h @@ -0,0 +1,30 @@ +#ifdef _FITZ_H_ +#error "fitz.h must only be included once" +#endif +#define _FITZ_H_ + +#include "fitz/sysdep.h" + +#include "fitz/base.h" +#include "fitz/math.h" +#include "fitz/geometry.h" +#include "fitz/hash.h" +#include "fitz/crypt.h" + +#include "fitz/object.h" + +#include "fitz/filter.h" +#include "fitz/file.h" + +#include "fitz/cmap.h" +#include "fitz/font.h" + +#include "fitz/tree.h" +#include "fitz/path.h" +#include "fitz/text.h" +#include "fitz/image.h" + +#include "fitz/pixmap.h" +#include "fitz/scanconv.h" +#include "fitz/render.h" + diff --git a/include/fitz/base.h b/include/fitz/base.h new file mode 100644 index 00000000..36fe0aeb --- /dev/null +++ b/include/fitz/base.h @@ -0,0 +1,59 @@ +#undef nil +#define nil ((void*)0) + +#undef offsetof +#define offsetof(s, m) (unsigned long)(&(((s*)0)->m)) + +#undef ABS +#define ABS(x) ( (x) < 0 ? -(x) : (x) ) + +#undef MAX +#define MAX(a,b) ( (a) > (b) ? (a) : (b) ) + +#undef MIN +#define MIN(a,b) ( (a) < (b) ? (a) : (b) ) + +#undef CLAMP +#define CLAMP(x,a,b) ( (x) > (b) ? (b) : ( (x) < (a) ? (a) : (x) ) ) + +#define MAX4(a,b,c,d) MAX(MAX(a,b), MAX(c,d)) +#define MIN4(a,b,c,d) MIN(MIN(a,b), MIN(c,d)) + +#define STRIDE(n, bcp) (((bpc) * (n) + 7) / 8) + +typedef struct fz_error_s fz_error; + +struct fz_error_s +{ + char msg[184]; + char file[32]; + char func[32]; + int line; + int frozen; +}; + +#define fz_outofmem (&fz_koutofmem) +extern fz_error fz_koutofmem; + +#define fz_throw(fmt, ...) fz_throw0(__func__, __FILE__, __LINE__, fmt, ## __VA_ARGS__) +fz_error *fz_throw0(const char *func, const char *file, int line, char *fmt, ...); +void fz_warn(char *fmt, ...); +void fz_abort(fz_error *eo); +void fz_freeerror(fz_error *eo); + +typedef struct fz_memorycontext_s fz_memorycontext; + +struct fz_memorycontext_s +{ + void * (*malloc)(fz_memorycontext *, int); + void * (*realloc)(fz_memorycontext *, void *, int); + void (*free)(fz_memorycontext *, void *); +}; + +fz_memorycontext *fz_currentmemorycontext(void); +void fz_setmemorycontext(fz_memorycontext *memorycontext); + +void *fz_malloc(int n); +void *fz_realloc(void *p, int n); +void fz_free(void *p); + diff --git a/include/fitz/cmap.h b/include/fitz/cmap.h new file mode 100644 index 00000000..ddd37149 --- /dev/null +++ b/include/fitz/cmap.h @@ -0,0 +1,25 @@ +typedef struct fz_cmap_s fz_cmap; + +fz_error *fz_newcmap(fz_cmap **cmapp); +void fz_debugcmap(fz_cmap *cmap); +void fz_freecmap(fz_cmap *cmap); + +char *fz_getcmapname(fz_cmap *cmap); +void fz_setcmapname(fz_cmap *cmap, char *name); +char *fz_getusecmapname(fz_cmap *cmap); +void fz_setusecmapname(fz_cmap *cmap, char *usecmap); +void fz_setusecmap(fz_cmap *cmap, fz_cmap *usecmap); +fz_cmap *fz_getusecmap(fz_cmap *cmap); +void fz_setwmode(fz_cmap *cmap, int wmode); +int fz_getwmode(fz_cmap *cmap); + +fz_error *fz_addcodespacerange(fz_cmap *cmap, unsigned lo, unsigned hi, int n); + +fz_error *fz_setcidlookup(fz_cmap *cmap, int map[256]); + +fz_error *fz_addcidrange(fz_cmap *cmap, int srclo, int srchi, int dstlo); +fz_error *fz_endcidrange(fz_cmap *cmap); + +int fz_lookupcid(fz_cmap *cmap, int cpt); +char *fz_decodecpt(fz_cmap *cmap, unsigned char *s, int *cpt); + diff --git a/include/fitz/crypt.h b/include/fitz/crypt.h new file mode 100644 index 00000000..f6365c4e --- /dev/null +++ b/include/fitz/crypt.h @@ -0,0 +1,30 @@ +/* md5 digests */ + +typedef struct fz_md5_s fz_md5; + +struct fz_md5_s +{ + unsigned long state[4]; + unsigned long count[2]; + unsigned char buffer[64]; +}; + +void fz_md5init(fz_md5 *state); +void fz_md5update(fz_md5 *state, unsigned char *input, unsigned inlen); +void fz_md5final(fz_md5 *state, unsigned char digest[16]); + +/* arc4 crypto */ + +typedef struct fz_arc4_s fz_arc4; + +struct fz_arc4_s +{ + unsigned x; + unsigned y; + unsigned char state[256]; +}; + +void fz_arc4init(fz_arc4 *state, unsigned char *key, unsigned len); +unsigned char fz_arc4next(fz_arc4 *state); +void fz_arc4encrypt(fz_arc4 *state, unsigned char *dest, unsigned char *src, unsigned len); + diff --git a/include/fitz/file.h b/include/fitz/file.h new file mode 100644 index 00000000..e687e572 --- /dev/null +++ b/include/fitz/file.h @@ -0,0 +1,33 @@ +typedef struct fz_file_s fz_file; + +struct fz_file_s +{ + int mode; /* O_RDONLY or O_WRONLY */ + int fd; + int depth; + fz_filter *filter; + fz_buffer *in; + fz_buffer *out; + fz_error *error; +}; + +fz_error *fz_openfile(fz_file **filep, char *path, int mode); +fz_error *fz_pushfilter(fz_file *file, fz_filter *filter); +void fz_popfilter(fz_file *file); +void fz_closefile(fz_file *file); + +int fz_seek(fz_file *f, int ofs); +int fz_tell(fz_file *f); + +int fz_readbyte(fz_file *f); +int fz_peekbyte(fz_file *f); +int fz_readline(fz_file *f, char *buf, int n); +int fz_read(fz_file *f, char *buf, int n); + +int fz_write(fz_file *f, char *buf, int n); +int fz_flush(fz_file *f); + +fz_error *fz_readfile(unsigned char **bufp, int *lenp, fz_file *file); + +fz_error *fz_ferror(fz_file *f); + diff --git a/include/fitz/filter.h b/include/fitz/filter.h new file mode 100644 index 00000000..6f234143 --- /dev/null +++ b/include/fitz/filter.h @@ -0,0 +1,80 @@ +typedef struct fz_filter_s fz_filter; +typedef struct fz_buffer_s fz_buffer; + +#define FZ_BUFSIZE (32 * 1024) + +#define fz_ioneedin (&fz_kioneedin) +#define fz_ioneedout (&fz_kioneedout) +#define fz_iodone (&fz_kiodone) + +extern fz_error fz_kioneedin; +extern fz_error fz_kioneedout; +extern fz_error fz_kiodone; + +#define FZ_NEWFILTER(TYPE,VAR,NAME) \ + fz_error * fz_process ## NAME (fz_filter*,fz_buffer*,fz_buffer*); \ + void fz_free ## NAME (fz_filter*); \ + TYPE *VAR; \ + *fp = fz_malloc(sizeof(TYPE)); \ + if (!*fp) return fz_outofmem; \ + (*fp)->process = fz_process ## NAME ; \ + (*fp)->free = fz_free ## NAME ; \ + (*fp)->consumed = 0; \ + (*fp)->produced = 0; \ + (*fp)->count = 0; \ + VAR = (TYPE*) *fp + +struct fz_filter_s +{ + fz_error* (*process)(fz_filter *filter, fz_buffer *in, fz_buffer *out); + void (*free)(fz_filter *filter); + int consumed; + int produced; + int count; +}; + +struct fz_buffer_s +{ + unsigned char *bp; + unsigned char *rp; + unsigned char *wp; + unsigned char *ep; + int eof; +}; + +fz_error *fz_process(fz_filter *f, fz_buffer *in, fz_buffer *out); +void fz_freefilter(fz_filter *f); + +fz_error *fz_newnullfilter(fz_filter **fp, int len); +fz_error *fz_newarc4filter(fz_filter **fp, unsigned char *key, unsigned keylen); +fz_error *fz_newpipeline(fz_filter **fp, fz_filter *head, fz_filter *tail); + +fz_error *fz_chainpipeline(fz_filter **fp, fz_filter *head, fz_filter *tail, fz_buffer *buf); +void fz_unchainpipeline(fz_filter *pipe, fz_filter **oldfp, fz_buffer **oldbp); + +fz_error *fz_newbuffer(fz_buffer **bufp, int size); +fz_error *fz_rewindbuffer(fz_buffer *buf); +fz_error *fz_growbuffer(fz_buffer *buf); +void fz_freebuffer(fz_buffer *buf); + +fz_error *fz_newa85d(fz_filter **filterp, fz_obj *param); +fz_error *fz_newa85e(fz_filter **filterp, fz_obj *param); +fz_error *fz_newahxd(fz_filter **filterp, fz_obj *param); +fz_error *fz_newahxe(fz_filter **filterp, fz_obj *param); +fz_error *fz_newrld(fz_filter **filterp, fz_obj *param); +fz_error *fz_newrle(fz_filter **filterp, fz_obj *param); +fz_error *fz_newdctd(fz_filter **filterp, fz_obj *param); +fz_error *fz_newdcte(fz_filter **filterp, fz_obj *param); +fz_error *fz_newfaxd(fz_filter **filterp, fz_obj *param); +fz_error *fz_newfaxe(fz_filter **filterp, fz_obj *param); +fz_error *fz_newflated(fz_filter **filterp, fz_obj *param); +fz_error *fz_newflatee(fz_filter **filterp, fz_obj *param); +fz_error *fz_newlzwd(fz_filter **filterp, fz_obj *param); +fz_error *fz_newlzwe(fz_filter **filterp, fz_obj *param); +fz_error *fz_newpredictd(fz_filter **filterp, fz_obj *param); +fz_error *fz_newpredicte(fz_filter **filterp, fz_obj *param); +fz_error *fz_newjbig2d(fz_filter **filterp, fz_obj *param); +fz_error *fz_newjpxd(fz_filter **filterp, fz_obj *param); + +void fz_pushbackahxd(fz_filter *filter, fz_buffer *in, fz_buffer *out, int n); + diff --git a/include/fitz/font.h b/include/fitz/font.h new file mode 100644 index 00000000..743d87fa --- /dev/null +++ b/include/fitz/font.h @@ -0,0 +1,63 @@ +typedef struct fz_font_s fz_font; +typedef struct fz_hmtx_s fz_hmtx; +typedef struct fz_vmtx_s fz_vmtx; +typedef struct fz_glyph_s fz_glyph; +typedef struct fz_glyphcache_s fz_glyphcache; + +struct fz_hmtx_s +{ + unsigned short c; + short w; +}; + +struct fz_vmtx_s +{ + unsigned short c; + short x; + short y; + short w; +}; + +struct fz_font_s +{ + char name[32]; + + fz_error* (*render)(fz_glyph*, fz_font*, int, fz_matrix); + void (*free)(fz_font *); + + int wmode; + fz_irect bbox; + + int nhmtx, hmtxcap; + fz_hmtx dhmtx; + fz_hmtx *hmtx; + + int nvmtx, vmtxcap; + fz_vmtx dvmtx; + fz_vmtx *vmtx; +}; + +struct fz_glyph_s +{ + int w, h, lsb, top; + unsigned char *bitmap; +}; + +void fz_initfont(fz_font *font, char *name); +void fz_freefont(fz_font *font); +void fz_debugfont(fz_font *font); +void fz_setfontwmode(fz_font *font, int wmode); +void fz_setfontbbox(fz_font *font, int xmin, int ymin, int xmax, int ymax); +void fz_setdefaulthmtx(fz_font *font, int w); +void fz_setdefaultvmtx(fz_font *font, int y, int w); +fz_error *fz_addhmtx(fz_font *font, int gid, int w); +fz_error *fz_addvmtx(fz_font *font, int gid, int x, int y, int w); +fz_error *fz_endhmtx(fz_font *font); +fz_error *fz_endvmtx(fz_font *font); +fz_hmtx fz_gethmtx(fz_font *font, int gid); +fz_vmtx fz_getvmtx(fz_font *font, int gid); + +fz_error *fz_newglyphcache(fz_glyphcache **arenap, int slots, int size); +fz_error *fz_renderglyph(fz_glyphcache*, fz_glyph*, fz_font*, int, fz_matrix); +void fz_freeglyphcache(fz_glyphcache *); + diff --git a/include/fitz/geometry.h b/include/fitz/geometry.h new file mode 100644 index 00000000..588aa562 --- /dev/null +++ b/include/fitz/geometry.h @@ -0,0 +1,56 @@ +typedef struct fz_matrix_s fz_matrix; +typedef struct fz_point_s fz_point; +typedef struct fz_rect_s fz_rect; +typedef struct fz_ipoint_s fz_ipoint; +typedef struct fz_irect_s fz_irect; + +/* + / a b 0 \ + | c d 0 | + \ e f 1 / +*/ +struct fz_matrix_s +{ + float a, b, c, d, e, f; +}; + +struct fz_point_s +{ + float x, y; +}; + +struct fz_rect_s +{ + fz_point min; + fz_point max; +}; + +struct fz_ipoint_s +{ + int x, y; +}; + +struct fz_irect_s +{ + fz_ipoint min; + fz_ipoint max; +}; + +#define FZ_INFRECT (fz_rect){{1,1},{-1,-1}} + +fz_matrix fz_concat(fz_matrix one, fz_matrix two); +fz_matrix fz_identity(void); +fz_matrix fz_scale(float sx, float sy); +fz_matrix fz_rotate(float theta); +fz_matrix fz_translate(float tx, float ty); +fz_matrix fz_invertmatrix(fz_matrix m); +int fz_isrectilinear(fz_matrix m); + +fz_rect fz_intersectrects(fz_rect a, fz_rect b); +fz_rect fz_mergerects(fz_rect a, fz_rect b); + +fz_irect fz_intersectirects(fz_irect a, fz_irect b); +fz_irect fz_mergeirects(fz_irect a, fz_irect b); + +fz_point fz_transformpoint(fz_matrix m, fz_point p); + diff --git a/include/fitz/hash.h b/include/fitz/hash.h new file mode 100644 index 00000000..4229a02c --- /dev/null +++ b/include/fitz/hash.h @@ -0,0 +1,15 @@ +typedef struct fz_hashtable_s fz_hashtable; + +fz_error *fz_newhash(fz_hashtable **tablep, int initialsize, int keylen); +fz_error *fz_resizehash(fz_hashtable *table, int newsize); +void fz_debughash(fz_hashtable *table); +void fz_freehash(fz_hashtable *table); + +void *fz_hashfind(fz_hashtable *table, void *key); +fz_error *fz_hashinsert(fz_hashtable *table, void *key, void *val); +fz_error *fz_hashremove(fz_hashtable *table, void *key); + +int fz_hashlen(fz_hashtable *table); +void *fz_gethashkey(fz_hashtable *table, int idx); +void *fz_gethashval(fz_hashtable *table, int idx); + diff --git a/include/fitz/image.h b/include/fitz/image.h new file mode 100644 index 00000000..7f6038ce --- /dev/null +++ b/include/fitz/image.h @@ -0,0 +1,17 @@ +enum +{ + FZ_CSGRAY, + FZ_CSRGB, + FZ_CSCMYK +}; + +struct fz_image_s +{ + fz_node super; + int w, h, n, bpc; + int cs; + unsigned char *data; +}; + +fz_error *fz_newimage(fz_node **nodep, int w, int h, int n, int bpc, int cs); + diff --git a/include/fitz/math.h b/include/fitz/math.h new file mode 100644 index 00000000..acefeb50 --- /dev/null +++ b/include/fitz/math.h @@ -0,0 +1,30 @@ +/* multiply 8-bit fixpoint (0..1) so that 0*0==0 and 255*255==255 */ +static inline unsigned char fz_mul255(unsigned char a, unsigned char b) +{ + return ((a + 1) * b) >> 8; +} + +/* divide and floor towards -inf */ +static inline int fz_idiv(int a, int b) +{ + return a < 0 ? (a - b + 1) / b : a / b; +} + +/* from python */ +static inline void fz_idivmod(int x, int y, int *d, int *m) +{ + int xdivy = x / y; + int xmody = x - xdivy * y; + /* If the signs of x and y differ, and the remainder is non-0, + * C89 doesn't define whether xdivy is now the floor or the + * ceiling of the infinitely precise quotient. We want the floor, + * and we have it iff the remainder's sign matches y's. + */ + if (xmody && ((y ^ xmody) < 0)) { + xmody += y; + xdivy --; + } + *d = xdivy; + *m = xmody; +} + diff --git a/include/fitz/object.h b/include/fitz/object.h new file mode 100644 index 00000000..8ccdd153 --- /dev/null +++ b/include/fitz/object.h @@ -0,0 +1,124 @@ +typedef struct fz_obj_s fz_obj; + +typedef enum fz_objkind_e fz_objkind; + +enum fz_objkind_e +{ + FZ_NULL, + FZ_BOOL, + FZ_INT, + FZ_REAL, + FZ_STRING, + FZ_NAME, + FZ_ARRAY, + FZ_DICT, + FZ_INDIRECT, + FZ_POINTER, +}; + +struct fz_keyval_s +{ + fz_obj *k; + fz_obj *v; +}; + +struct fz_obj_s +{ + unsigned short kind; /* fz_objkind takes 4 bytes :( */ + unsigned short refcount; + union + { + int b; + int i; + float f; + struct { + unsigned short len; + unsigned char buf[]; + } s; + unsigned char n[1]; + struct { + int len; + int cap; + fz_obj **items; + } a; + struct { + int len; + int cap; + struct fz_keyval_s *items; + } d; + struct { + int oid; + int gid; + } r; + void *p; + } u; +}; + +fz_error *fz_newnull(fz_obj **op); +fz_error *fz_newbool(fz_obj **op, int b); +fz_error *fz_newint(fz_obj **op, int i); +fz_error *fz_newreal(fz_obj **op, float f); +fz_error *fz_newname(fz_obj **op, char *str); +fz_error *fz_newstring(fz_obj **op, char *str, int len); +fz_error *fz_newindirect(fz_obj **op, int oid, int gid); +fz_error *fz_newpointer(fz_obj **op, void *p); + +fz_error *fz_newarray(fz_obj **op, int initialcap); +fz_error *fz_newdict(fz_obj **op, int initialcap); +fz_error *fz_copyarray(fz_obj **op, fz_obj *array); +fz_error *fz_copydict(fz_obj **op, fz_obj *dict); +fz_error *fz_deepcopyarray(fz_obj **op, fz_obj *array); +fz_error *fz_deepcopydict(fz_obj **op, fz_obj *dict); + +fz_obj *fz_keepobj(fz_obj *obj); +fz_obj *fz_dropobj(fz_obj *obj); + +/* type queries */ +int fz_isnull(fz_obj *obj); +int fz_isbool(fz_obj *obj); +int fz_isint(fz_obj *obj); +int fz_isreal(fz_obj *obj); +int fz_isname(fz_obj *obj); +int fz_isstring(fz_obj *obj); +int fz_isarray(fz_obj *obj); +int fz_isdict(fz_obj *obj); +int fz_isindirect(fz_obj *obj); +int fz_ispointer(fz_obj *obj); + +/* silent failure, no error reporting */ +int fz_tobool(fz_obj *obj); +int fz_toint(fz_obj *obj); +float fz_toreal(fz_obj *obj); +char *fz_toname(fz_obj *obj); +char *fz_tostringbuf(fz_obj *obj); +int fz_tostringlen(fz_obj *obj); +int fz_toobjid(fz_obj *obj); +int fz_togenid(fz_obj *obj); +void *fz_topointer(fz_obj *obj); + +int fz_arraylen(fz_obj *array); +fz_obj *fz_arrayget(fz_obj *array, int i); +fz_error *fz_arrayput(fz_obj *array, int i, fz_obj *obj); +fz_error *fz_arraypush(fz_obj *array, fz_obj *obj); +void fz_freearray(fz_obj *array); + +int fz_dictlen(fz_obj *dict); +fz_obj *fz_dictgetkey(fz_obj *dict, int idx); +fz_obj *fz_dictgetval(fz_obj *dict, int idx); +fz_obj *fz_dictget(fz_obj *dict, fz_obj *key); +fz_obj *fz_dictgets(fz_obj *dict, char *key); +fz_error *fz_dictput(fz_obj *dict, fz_obj *key, fz_obj *val); +fz_error *fz_dictputs(fz_obj *dict, char *key, fz_obj *val); +fz_error *fz_dictdel(fz_obj *dict, fz_obj *key); +fz_error *fz_dictdels(fz_obj *dict, char *key); +void fz_freedict(fz_obj *dict); + +int fz_sprintobj(char *s, fz_obj *obj); +int fz_sprintcobj(char *s, fz_obj *obj); +int fz_fprintobj(FILE *f, fz_obj *obj); +int fz_fprintcobj(FILE *f, fz_obj *obj); + +fz_error *fz_parseobj(fz_obj **objp, char *s); +fz_error *fz_packobj(fz_obj **objp, char *fmt, ...); +fz_error *fz_unpackobj(fz_obj *obj, char *fmt, ...); + diff --git a/include/fitz/path.h b/include/fitz/path.h new file mode 100644 index 00000000..536ca281 --- /dev/null +++ b/include/fitz/path.h @@ -0,0 +1,57 @@ +typedef enum fz_pathkind_e fz_pathkind; +typedef enum fz_pathelkind_e fz_pathelkind; +typedef struct fz_stroke_s fz_stroke; +typedef struct fz_dash_s fz_dash; +typedef union fz_pathel_s fz_pathel; + +enum fz_pathkind_e { FZ_STROKE, FZ_FILL, FZ_EOFILL }; +enum fz_pathelkind_e { FZ_MOVETO, FZ_LINETO, FZ_CURVETO, FZ_CLOSEPATH }; + +struct fz_stroke_s +{ + int linecap; + int linejoin; + float linewidth; + float miterlimit; +}; + +struct fz_dash_s +{ + int len; + float phase; + float array[]; +}; + +union fz_pathel_s +{ + fz_pathelkind k; + float v; +}; + +struct fz_path_s +{ + fz_node super; + fz_pathkind paint; + fz_stroke *stroke; + fz_dash *dash; + int len, cap; + fz_pathel *els; +}; + +fz_error *fz_newpath(fz_path **pathp); +fz_error *fz_clonepath(fz_path **pathp, fz_path *oldpath); +fz_error *fz_moveto(fz_path*, float x, float y); +fz_error *fz_lineto(fz_path*, float x, float y); +fz_error *fz_curveto(fz_path*, float, float, float, float, float, float); +fz_error *fz_curvetov(fz_path*, float, float, float, float); +fz_error *fz_curvetoy(fz_path*, float, float, float, float); +fz_error *fz_closepath(fz_path*); +fz_error *fz_endpath(fz_path*, fz_pathkind paint, fz_stroke *stroke, fz_dash *dash); +void fz_freepath(fz_path *path); + +fz_rect fz_boundpath(fz_path *node, fz_matrix ctm); +void fz_debugpath(fz_path *node); + +fz_error *fz_newdash(fz_dash **dashp, float phase, int len, float *array); +void fz_freedash(fz_dash *dash); + diff --git a/include/fitz/pixmap.h b/include/fitz/pixmap.h new file mode 100644 index 00000000..90071a30 --- /dev/null +++ b/include/fitz/pixmap.h @@ -0,0 +1,22 @@ +typedef struct fz_pixmap_s fz_pixmap; +typedef struct fz_colorspace_s fz_colorspace; + +struct fz_pixmap_s +{ + int x, y, w, h; + int n, a; + int stride; + fz_colorspace *cs; + short *samples; +}; + +fz_error *fz_newpixmap(fz_pixmap **mapp, int x, int y, int w, int h, int n, int a); +void fz_clearpixmap(fz_pixmap *map); +void fz_freepixmap(fz_pixmap *map); + +void fz_blendover(short *C, short *A, short *B, int n); +void fz_blendin(short *C, short *A, short *B, int n); +void fz_blendout(short *C, short *A, short *B, int n); +void fz_blendatop(short *C, short *A, short *B, int n); +void fz_blendxor(short *C, short *A, short *B, int n); + diff --git a/include/fitz/render.h b/include/fitz/render.h new file mode 100644 index 00000000..d237e872 --- /dev/null +++ b/include/fitz/render.h @@ -0,0 +1,11 @@ +typedef struct fz_renderer_s fz_renderer; + +fz_error *fz_newrenderer(fz_renderer **gcp); +void fz_freerenderer(fz_renderer *gc); + +fz_error *fz_renderover(fz_renderer *gc, fz_over *over, fz_matrix ctm, fz_pixmap *out); +fz_error *fz_rendermask(fz_renderer *gc, fz_mask *mask, fz_matrix ctm, fz_pixmap *out); +fz_error *fz_rendertransform(fz_renderer *gc, fz_transform *xform, fz_matrix ctm, fz_pixmap *out); +fz_error *fz_rendertext(fz_renderer *gc, fz_text *text, fz_matrix ctm, fz_pixmap *out); +fz_error *fz_rendernode(fz_renderer *gc, fz_node *node, fz_matrix ctm, fz_pixmap *out); + diff --git a/include/fitz/scanconv.h b/include/fitz/scanconv.h new file mode 100644 index 00000000..f391a387 --- /dev/null +++ b/include/fitz/scanconv.h @@ -0,0 +1,46 @@ +typedef struct fz_edge_s fz_edge; +typedef struct fz_gel_s fz_gel; +typedef struct fz_ael_s fz_ael; + +struct fz_edge_s +{ + int x, e, h, y; + int adjup, adjdown; + int xmove; + int xdir, ydir; /* -1 or +1 */ +}; + +struct fz_gel_s +{ + int hs, vs; + int xmin, xmax; + int ymin, ymax; + int cap; + int len; + fz_edge *edges; +}; + +struct fz_ael_s +{ + int cap; + int len; + fz_edge **edges; +}; + +fz_error *fz_newgel(fz_gel **gelp); +fz_error *fz_insertgel(fz_gel *gel, float x0, float y0, float x1, float y1); +void fz_resetgel(fz_gel *gel, int hs, int vs); +void fz_sortgel(fz_gel *gel); +void fz_freegel(fz_gel *gel); + +fz_error *fz_newael(fz_ael **aelp); +fz_error *fz_insertael(fz_ael *ael, fz_gel *gel, int y, int *e); +void fz_advanceael(fz_ael *ael); +void fz_freeael(fz_ael *ael); + +fz_error *fz_scanconvert(fz_gel *gel, fz_ael *ael, int eofill); + +fz_error *fz_fillpath(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness); +fz_error *fz_strokepath(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness); +fz_error *fz_dashpath(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness); + diff --git a/include/fitz/sysdep.h b/include/fitz/sysdep.h new file mode 100644 index 00000000..bb78521f --- /dev/null +++ b/include/fitz/sysdep.h @@ -0,0 +1,48 @@ +/* + * Include the basic standard libc headers. + */ + +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <stdarg.h> + +#include <limits.h> /* INT_MIN, MAX ... */ +#include <float.h> /* DBL_EPSILON */ +#include <math.h> + +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> /* O_RDONLY & co */ + +/* not supposed to be here, but printf debugging sorta needs it */ +#include <stdio.h> + +typedef unsigned char fz_u8; +typedef signed char fz_s8; +typedef unsigned short fz_u16; +typedef signed short fz_s16; +typedef unsigned long fz_u32; +typedef signed long fz_s32; +typedef unsigned long long fz_u64; +typedef signed long long fz_s64; + +/* + * Extras! Extras! Get them while they're hot! + */ + +#ifdef NEED_STRLCPY +extern int strlcpy(char *dst, const char *src, int n); +extern int strlcat(char *dst, const char *src, int n); +#endif + +#ifdef NEED_STRSEP +extern char *strsep(char **stringp, const char *delim); +#endif + +#ifdef NEED_GETOPT +extern int getopt(int nargc, char * const * nargv, const char *ostr); +extern int opterr, optind, optopt; +extern char *optarg; +#endif + diff --git a/include/fitz/text.h b/include/fitz/text.h new file mode 100644 index 00000000..41a08c45 --- /dev/null +++ b/include/fitz/text.h @@ -0,0 +1,23 @@ +typedef struct fz_textbuilder_s fz_textbuilder; +typedef struct fz_textel_s fz_textel; + +struct fz_textel_s +{ + float x, y; + int g; +}; + +struct fz_text_s +{ + fz_node super; + fz_font *font; + fz_matrix trm; + int len, cap; + fz_textel *els; +}; + +fz_error *fz_newtext(fz_text **textp, fz_font *face); +fz_error *fz_addtext(fz_text *text, int g, float x, float y); +fz_error *fz_endtext(fz_text *text); +void fz_freetext(fz_text *text); + diff --git a/include/fitz/tree.h b/include/fitz/tree.h new file mode 100644 index 00000000..080e55bf --- /dev/null +++ b/include/fitz/tree.h @@ -0,0 +1,156 @@ +typedef struct fz_tree_s fz_tree; +typedef struct fz_node_s fz_node; + +typedef enum fz_nodekind_e fz_nodekind; +typedef enum fz_blendkind_e fz_blendkind; + +typedef struct fz_transform_s fz_transform; +typedef struct fz_over_s fz_over; +typedef struct fz_mask_s fz_mask; +typedef struct fz_blend_s fz_blend; +typedef struct fz_path_s fz_path; +typedef struct fz_text_s fz_text; +typedef struct fz_solid_s fz_solid; +typedef struct fz_image_s fz_image; +typedef struct fz_shade_s fz_shade; +typedef struct fz_form_s fz_form; +typedef struct fz_meta_s fz_meta; +typedef struct fz_halftone_s fz_halftone; + +enum fz_nodekind_e +{ + FZ_NTRANSFORM, + FZ_NOVER, + FZ_NMASK, + FZ_NBLEND, + FZ_NPATH, + FZ_NTEXT, + FZ_NSOLID, + FZ_NIMAGE, + FZ_NSHADE, + FZ_NFORM, + FZ_NMETA, + FZ_NHALFTONE +}; + +enum fz_blendkind_e +{ + /* PDF 1.4 -- standard separable */ + FZ_BNORMAL, + FZ_BMULTIPLY, + FZ_BSCREEN, + FZ_BOVERLAY, + FZ_BDARKEN, + FZ_BLIGHTEN, + FZ_BCOLORDODGE, + FZ_BCOLORBURN, + FZ_BHARDLIGHT, + FZ_BSOFTLIGHT, + FZ_BDIFFERENCE, + FZ_BEXCLUSION, + + /* PDF 1.4 -- standard non-separable */ + FZ_BHUE, + FZ_BSATURATION, + FZ_BCOLOR, + FZ_BLUMINOSITY, + + FZ_BOVERPRINT, +}; + +struct fz_tree_s +{ + fz_node *root; + fz_node *head; +}; + +struct fz_node_s +{ + fz_nodekind kind; + fz_node *parent; + fz_node *next; +}; + +struct fz_meta_s +{ + fz_node super; + fz_node *child; + fz_obj *info; +}; + +struct fz_over_s +{ + fz_node super; + fz_node *child; +}; + +struct fz_mask_s +{ + fz_node super; + fz_node *child; +}; + +struct fz_blend_s +{ + fz_node super; + fz_node *child; + fz_blendkind mode; + int isolated; + int knockout; +}; + +struct fz_transform_s +{ + fz_node super; + fz_node *child; + fz_matrix m; +}; + +struct fz_form_s +{ + fz_node super; + fz_tree *tree; +}; + +struct fz_solid_s +{ + fz_node super; + float r, g, b; +}; + +/* tree operations */ +fz_error *fz_newtree(fz_tree **treep); +void fz_freetree(fz_tree *tree); +fz_rect fz_boundtree(fz_tree *tree, fz_matrix ctm); + +void fz_debugtree(fz_tree *tree); +void fz_insertnode(fz_node *node, fz_node *child); + +/* common to all nodes */ +void fz_initnode(fz_node *node, fz_nodekind kind); +fz_rect fz_boundnode(fz_node *node, fz_matrix ctm); +void fz_freenode(fz_node *node); + +/* branch nodes */ +fz_error *fz_newmeta(fz_node **nodep, fz_obj *info); +fz_error *fz_newover(fz_node **nodep); +fz_error *fz_newmask(fz_node **nodep); +fz_error *fz_newblend(fz_node **nodep, fz_blendkind b, int k, int i); +fz_error *fz_newtransform(fz_node **nodep, fz_matrix m); + +int fz_ismeta(fz_node *node); +int fz_isover(fz_node *node); +int fz_ismask(fz_node *node); +int fz_isblend(fz_node *node); +int fz_istransform(fz_node *node); + +/* leaf nodes */ +fz_error *fz_newform(fz_node **nodep, fz_tree *subtree); +fz_error *fz_newsolid(fz_node **nodep, float r, float g, float b); + +int fz_isform(fz_node *node); +int fz_issolid(fz_node *node); +int fz_ispath(fz_node *node); +int fz_istext(fz_node *node); +int fz_isimage(fz_node *node); + diff --git a/include/mupdf.h b/include/mupdf.h new file mode 100644 index 00000000..ca4a267b --- /dev/null +++ b/include/mupdf.h @@ -0,0 +1,253 @@ +#ifdef _MUPDF_H_ +#error "mupdf.h must only be included once" +#endif +#define _MUPDF_H_ + +#ifndef _FITZ_H_ +#error "fitz.h must be included before mupdf.h" +#endif + +/* + * tokenizer and low-level object parser + */ + +enum +{ + PDF_TERROR, PDF_TEOF, + PDF_TOARRAY, PDF_TCARRAY, + PDF_TODICT, PDF_TCDICT, + PDF_TNAME, PDF_TINT, PDF_TREAL, PDF_TSTRING, PDF_TKEYWORD, + PDF_TR, PDF_TTRUE, PDF_TFALSE, PDF_TNULL, + PDF_TOBJ, PDF_TENDOBJ, + PDF_TSTREAM, PDF_TENDSTREAM, + PDF_TXREF, PDF_TTRAILER, PDF_TSTARTXREF, + PDF_NTOKENS +}; + +/* lex.c */ +int pdf_lex(fz_file *f, unsigned char *buf, int n, int *len); + +/* parse.c */ +fz_error *pdf_parsearray(fz_obj **op, fz_file *f, unsigned char *buf, int cap); +fz_error *pdf_parsedict(fz_obj **op, fz_file *f, unsigned char *buf, int cap); +fz_error *pdf_parsestmobj(fz_obj **op, fz_file *f, unsigned char *buf, int cap); +fz_error *pdf_parseindobj(fz_obj **op, fz_file *f, unsigned char *buf, int cap, int *oid, int *gid, int *stmofsj); + +/* + * xref and syntax object api + */ + +typedef struct pdf_xref_s pdf_xref; +typedef struct pdf_xrefentry_s pdf_xrefentry; +typedef struct pdf_crypt_s pdf_crypt; + +struct pdf_xref_s +{ + float version; + pdf_crypt *crypt; + fz_file *file; + int size; + int capacity; + pdf_xrefentry *table; + fz_obj *trailer; + int startxref; + fz_hashtable *store; +}; + +struct pdf_xrefentry_s +{ + unsigned int ofs; /* file offset / objstm object number */ + unsigned short gen; /* generation / objstm index */ + char type; /* 0=unset (f)ree i(n)use (o)bjstm (d)elete (a)dd */ + char mark; /* for garbage collection etc */ +}; + +struct pdf_crypt_s +{ + unsigned char o[32]; + unsigned char u[32]; + unsigned int p; + int r; + int n; + + fz_obj *id; + + unsigned char key[16]; + int keylen; +}; + +/* stream.c */ +fz_error *pdf_buildfilter(fz_filter**, pdf_xref*, fz_obj *stm, int oid, int gid); +fz_error *pdf_openstream0(pdf_xref*, fz_obj *stmobj, int oid, int gid, int ofs); +fz_error *pdf_openstream(pdf_xref*, fz_obj *stmref); +void pdf_closestream(pdf_xref*); +fz_error *pdf_readstream(unsigned char **bufp, int *lenp, pdf_xref*, fz_obj *stmref); + +/* crypt.c */ +fz_error *pdf_newdecrypt(pdf_crypt **cp, fz_obj *enc, fz_obj *id); +fz_error *pdf_newencrypt(pdf_crypt **cp, fz_obj **edict, char *userpw, char *ownerpw, int p, int n, fz_obj *id); +fz_error *pdf_setpassword(pdf_crypt *crypt, char *pw); +fz_error *pdf_cryptstm(fz_filter **fp, pdf_crypt *crypt, int oid, int gid); +void pdf_cryptobj(pdf_crypt *crypt, fz_obj *obj, int oid, int gid); +void pdf_freecrypt(pdf_crypt *crypt); + +/* repair.c */ +fz_error *pdf_repairxref(pdf_xref*, char *filename); + +/* open.c */ +fz_error *pdf_openxref(pdf_xref*, char *filename); +fz_error *pdf_readobjstm(pdf_xref *xref, int oid, int gid, unsigned char *buf, int cap); + +/* xref.c */ +fz_error *pdf_newxref(pdf_xref **xrefp); +fz_error *pdf_decryptxref(pdf_xref *xref); +void pdf_closexref(pdf_xref*); +void pdf_debugxref(pdf_xref*); + +fz_obj *pdf_findstoredobject(fz_hashtable *store, int oid, int gid); +fz_buffer *pdf_findstoredstream(fz_hashtable *store, int oid, int gid); +fz_error *pdf_deletestoredobject(fz_hashtable *store, int oid, int gid); +fz_error *pdf_deletestoredstream(fz_hashtable *store, int oid, int gid); +fz_error *pdf_storeobject(fz_hashtable *store, int oid, int gid, fz_obj *obj); +fz_error *pdf_storestream(fz_hashtable *store, int oid, int gid, fz_buffer *buf); + +fz_error *pdf_createobject(pdf_xref *xref, int *oidp, int *gidp); +fz_error *pdf_deleteobject(pdf_xref *xref, int oid, int gid); +fz_error *pdf_saveobject(pdf_xref *xref, int oid, int gid, fz_obj *obj); +fz_error *pdf_loadobject0(fz_obj **, pdf_xref*, int oid, int gid, int *stmofs); +fz_error *pdf_loadobject(fz_obj **, pdf_xref*, fz_obj *ref, int *stmofs); +fz_error *pdf_resolve(fz_obj **, pdf_xref*); + +/* save.c */ +fz_error *pdf_saveincrementalpdf(pdf_xref *xref, char *path); +fz_error *pdf_savepdf(pdf_xref *xref, char *path); + +/* + * high-level semantic objects for resources and pages + */ + +typedef struct pdf_pagetree_s pdf_pagetree; +typedef struct pdf_font_s pdf_font; +typedef struct pdf_resources_s pdf_resources; +typedef struct pdf_gstate_s pdf_gstate; +typedef struct pdf_csi_s pdf_csi; + +struct pdf_pagetree_s +{ + int count; + int cursor; + fz_obj **pref; + fz_obj **pobj; +}; + +struct pdf_font_s +{ + fz_font super; + + void *ftface; + + fz_cmap *encoding; + int cidtogidlen; + int *cidtogidmap; + + char *filename; + char *fontdata; + int fontlen; +}; + +struct pdf_type3_s +{ + fz_rect bbox; + fz_matrix matrix; + int widths[256]; + fz_tree *charprocs[256]; + int tounicode[256]; +}; + +struct pdf_resources_s +{ + fz_obj *extgstate; + fz_obj *colorspace; + fz_obj *font; + fz_obj *ximage; + fz_obj *xform; +}; + +struct pdf_gstate_s +{ + /* path stroking */ + float linewidth; + int linecap; + int linejoin; + float miterlimit; + float dashphase; + int dashlen; + float dashlist[32]; + + /* colors and colorspaces */ + struct { float r, g, b; } stroke, fill; + + /* text state */ + float charspace; + float wordspace; + float scale; + float leading; + pdf_font *font; + float size; + int render; + float rise; + + /* tree construction state */ + fz_node *head; +}; + +struct pdf_csi_s +{ + pdf_gstate gstate[32]; + int gtop; + fz_obj *stack[32]; + int top; + int xbalance; + + /* path object state */ + fz_path *path; + fz_path *clip; + + /* text object state */ + fz_text *text; + fz_matrix tlm; + fz_matrix tm; + + fz_tree *tree; +}; + +/* pagetree.c */ +fz_error *pdf_loadpagetree(pdf_pagetree **pp, pdf_xref *xref); +void pdf_debugpagetree(pdf_pagetree *pages); +void pdf_freepagetree(pdf_pagetree *pages); + +/* cmap.c */ +fz_error *pdf_parsecmap(fz_cmap **cmapp, fz_file *file); +fz_error *pdf_loadembeddedcmap(fz_cmap **cmapp, pdf_xref *xref, fz_obj *stmref); +fz_error *pdf_loadsystemcmap(fz_cmap **cmapp, char *name); +fz_error *pdf_makeidentitycmap(fz_cmap **cmapp, int wmode); + +/* fontfile.c */ +fz_error *pdf_loadbuiltinfont(void **fontp, char *pattern); +fz_error *pdf_loadsystemfont(void **fontp, char *basefont, char *collection); +fz_error *pdf_loadembeddedfont(void **fontp, pdf_xref *xref, fz_obj *stmref); +fz_error *pdf_loadfontdescriptor(void **fontp, pdf_xref *xref, fz_obj *desc, char *collection); + +/* font.c */ +fz_error *pdf_loadfont(pdf_font **fontp, pdf_xref *xref, fz_obj *font); +void pdf_freefont(pdf_font *font); + +/* resources.c */ +fz_error *pdf_loadresources(pdf_resources **rdbp, pdf_xref *xref, fz_obj *resdict); +void pdf_freeresources(pdf_resources *rdb); + +/* interpret.c */ +fz_error *pdf_newcsi(pdf_csi **csip); +fz_error *pdf_runcsi(pdf_csi *, pdf_resources *, fz_file *); +void pdf_freecsi(pdf_csi *csi); + diff --git a/mupdf/build.c b/mupdf/build.c new file mode 100644 index 00000000..f759e500 --- /dev/null +++ b/mupdf/build.c @@ -0,0 +1,376 @@ +#include <fitz.h> +#include <mupdf.h> + +extern int FT_Get_Char_Index(void*, int); + +void +pdf_initgstate(pdf_gstate *gs) +{ + gs->linewidth = 1.0; + gs->linecap = 0; + gs->linejoin = 0; + gs->miterlimit = 10; + gs->dashphase = 0; + gs->dashlen = 0; + memset(gs->dashlist, 0, sizeof(gs->dashlist)); + + gs->stroke.r = 0; + gs->stroke.g = 0; + gs->stroke.b = 0; + + gs->fill.r = 0; + gs->fill.g = 0; + gs->fill.b = 0; + + gs->charspace = 0; + gs->wordspace = 0; + gs->scale = 1; + gs->leading = 0; + gs->font = nil; + gs->size = -1; + gs->render = 0; + gs->rise = 0; + + gs->head = nil; +} + +fz_error * +pdf_buildstrokepath(pdf_gstate *gs, fz_path *path) +{ + fz_error *error; + fz_stroke *stroke; + fz_dash *dash; + + stroke = fz_malloc(sizeof(fz_stroke)); + if (!stroke) + return fz_outofmem; + stroke->linecap = gs->linecap; + stroke->linejoin = gs->linejoin; + stroke->linewidth = gs->linewidth; + stroke->miterlimit = gs->miterlimit; + + if (gs->dashlen) + { + error = fz_newdash(&dash, gs->dashphase, gs->dashlen, gs->dashlist); + if (error) { + fz_free(stroke); + return error; + } + } + else + { + dash = nil; + } + + error = fz_endpath(path, FZ_STROKE, stroke, dash); + if (error) { + fz_freedash(dash); + fz_free(stroke); + return error; + } + + return nil; +} + +fz_error * +pdf_buildfillpath(pdf_gstate *gs, fz_path *path, int eofill) +{ + return fz_endpath(path, eofill ? FZ_EOFILL : FZ_FILL, nil, nil); +} + +static fz_error * +addcolorshape(pdf_gstate *gs, fz_node *shape, float r, float g, float b) +{ + fz_error *error; + fz_node *mask; + fz_node *solid; + + error = fz_newmask(&mask); + if (error) return error; + + error = fz_newsolid(&solid, r, g, b); + if (error) return error; + + fz_insertnode(mask, shape); + fz_insertnode(mask, solid); + fz_insertnode(gs->head, mask); + + return nil; +} + +fz_error * +pdf_addfillshape(pdf_gstate *gs, fz_node *shape) +{ + return addcolorshape(gs, shape, gs->fill.r, gs->fill.g, gs->fill.b); +} + +fz_error * +pdf_addstrokeshape(pdf_gstate *gs, fz_node *shape) +{ + return addcolorshape(gs, shape, gs->stroke.r, gs->stroke.g, gs->stroke.b); +} + +fz_error * +pdf_addclipmask(pdf_gstate *gs, fz_node *shape) +{ + fz_error *error; + fz_node *mask; + fz_node *over; + + error = fz_newmask(&mask); + if (error) return error; + + error = fz_newover(&over); + if (error) return error; + + fz_insertnode(mask, shape); + fz_insertnode(mask, over); + fz_insertnode(gs->head, mask); + gs->head = over; + + return nil; +} + +fz_error * +pdf_addtransform(pdf_gstate *gs, fz_node *affine) +{ + fz_error *error; + fz_node *over; + + error = fz_newover(&over); + if (error) return error; + + fz_insertnode(gs->head, affine); + fz_insertnode(affine, over); + gs->head = over; + + return nil; +} + +fz_error * +pdf_showpath(pdf_csi *csi, + int doclose, int dofill, int dostroke, int evenodd) +{ + pdf_gstate *gstate = csi->gstate + csi->gtop; + fz_error *error; + fz_path *spath; + fz_path *fpath; + + if (doclose) + { + error = fz_closepath(csi->path); + if (error) return error; + } + + if (dofill && dostroke) + { + fpath = csi->path; + error = fz_clonepath(&spath, fpath); + if (error) return error; + } + else + { + spath = fpath = csi->path; + } + + if (dofill) + { + error = pdf_buildfillpath(gstate, fpath, evenodd); + if (error) return error; + error = pdf_addfillshape(gstate, (fz_node*)fpath); + if (error) return error; + } + + if (dostroke) + { + error = pdf_buildstrokepath(gstate, spath); + if (error) return error; + error = pdf_addstrokeshape(gstate, (fz_node*)spath); + if (error) return error; + } + + if (!dofill && !dostroke) + { + fz_free(csi->path); + } + + if (csi->clip) + { + error = pdf_addclipmask(gstate, (fz_node*)csi->clip); + if (error) return error; + csi->clip = nil; + } + + csi->path = nil; + + error = fz_newpath(&csi->path); + if (error) return error; + + return nil; +} + +fz_error * +pdf_flushtext(pdf_csi *csi) +{ + pdf_gstate *gstate = csi->gstate + csi->gtop; + fz_error *error; + + if (csi->text) + { + error = pdf_addfillshape(gstate, (fz_node*)csi->text); + if (error) + return error; + csi->text = nil; + } + + return nil; +} + +fz_error * +showglyph(pdf_csi *csi, int g) +{ + pdf_gstate *gstate = csi->gstate + csi->gtop; + pdf_font *font = gstate->font; + fz_error *error; + fz_matrix tsm, trm, tm; + float w0, w1, tx, ty; + fz_hmtx h; + fz_vmtx v; + + tsm.a = gstate->size * gstate->scale; + tsm.b = 0; + tsm.c = 0; + tsm.d = gstate->size; + tsm.e = 0; + tsm.f = gstate->rise; + + tm = csi->tm; + + if (font->super.wmode == 1) + { + v = fz_getvmtx((fz_font*)font, g); + tm.e -= v.x * gstate->size / 1000.0; + tm.f += v.y * gstate->size / 1000.0; + } + + trm = fz_concat(tsm, tm); + + /* flush buffered text if face or matrix has changed */ + if (!csi->text || + ((fz_font*)font) != csi->text->font || + fabs(trm.a - csi->text->trm.a) > FLT_EPSILON || + fabs(trm.b - csi->text->trm.b) > FLT_EPSILON || + fabs(trm.c - csi->text->trm.c) > FLT_EPSILON || + fabs(trm.d - csi->text->trm.d) > FLT_EPSILON) + { + error = pdf_flushtext(csi); + if (error) return error; + + error = fz_newtext(&csi->text, (fz_font*)font); + if (error) return error; + + csi->text->trm = trm; + csi->text->trm.e = 0; + csi->text->trm.f = 0; + } + + /* add glyph to textobject */ + error = fz_addtext(csi->text, g, trm.e, trm.f); + if (error) + return error; + + if (font->super.wmode == 0) + { + h = fz_gethmtx((fz_font*)font, g); + w0 = h.w / 1000.0; + tx = (w0 * gstate->size + gstate->charspace) * gstate->scale; + csi->tm = fz_concat(fz_translate(tx, 0), csi->tm); + } + else + { + w1 = v.w / 1000.0; + ty = w1 * gstate->size + gstate->charspace; + csi->tm = fz_concat(fz_translate(0, ty), csi->tm); + } + + return nil; +} + +void +showspace(pdf_csi *csi, float tadj) +{ + pdf_gstate *gstate = csi->gstate + csi->gtop; + pdf_font *font = gstate->font; + if (font->super.wmode == 0) + csi->tm = fz_concat(fz_translate(tadj * gstate->scale, 0), csi->tm); + else + csi->tm = fz_concat(fz_translate(0, tadj), csi->tm); +} + +fz_error * +pdf_showtext(pdf_csi *csi, fz_obj *text) +{ + pdf_gstate *gstate = csi->gstate + csi->gtop; + pdf_font *font = gstate->font; + fz_error *error; + unsigned char *buf; + unsigned char *end; + int i, len; + int cpt, cid, gid; + + if (fz_isarray(text)) + { + for (i = 0; i < fz_arraylen(text); i++) + { + fz_obj *item = fz_arrayget(text, i); + if (fz_isstring(item)) + { + error = pdf_showtext(csi, item); + if (error) return error; + } + else + { + showspace(csi, - fz_toreal(item) * gstate->size / 1000.0); + } + } + return nil; + } + + buf = fz_tostringbuf(text); + len = fz_tostringlen(text); + end = buf + len; + + while (buf < end) + { + buf = fz_decodecpt(font->encoding, buf, &cpt); + + cid = fz_lookupcid(font->encoding, cpt); + + if (font->cidtogidmap) + { + if (cid >= 0 && cid < font->cidtogidlen) + gid = font->cidtogidmap[cid]; + else + gid = 0; + } + else + { + gid = cid; + } + +//printf("gl %s %g [%g %g %g %g %g %g] cpt<%02x> cid %d gid %d h %d\n", +// font->super.name, size, +// csi->tm.a, csi->tm.b, csi->tm.c, csi->tm.d, csi->tm.e, csi->tm.f, +// cpt, cid, gid, font->super.hadv[gid]); + + error = showglyph(csi, gid); + if (error) + return error; + + if (cpt == 32) + showspace(csi, gstate->wordspace); + } + + return nil; +} + diff --git a/mupdf/cmap.c b/mupdf/cmap.c new file mode 100644 index 00000000..cec14e19 --- /dev/null +++ b/mupdf/cmap.c @@ -0,0 +1,521 @@ +#include <fitz.h> +#include <mupdf.h> + +enum +{ + TUSECMAP = PDF_NTOKENS, + TBEGINCODESPACERANGE, + TENDCODESPACERANGE, + TBEGINBFCHAR, + TENDBFCHAR, + TBEGINBFRANGE, + TENDBFRANGE, + TBEGINCIDCHAR, + TENDCIDCHAR, + TBEGINCIDRANGE, + TENDCIDRANGE, +}; + +static int tokenfromkeyword(unsigned char *key) +{ + if (!strcmp(key, "usecmap")) return TUSECMAP; + if (!strcmp(key, "begincodespacerange")) return TBEGINCODESPACERANGE; + if (!strcmp(key, "endcodespacerange")) return TENDCODESPACERANGE; + if (!strcmp(key, "beginbfchar")) return TBEGINBFCHAR; + if (!strcmp(key, "endbfchar")) return TENDBFCHAR; + if (!strcmp(key, "beginbfrange")) return TBEGINBFRANGE; + if (!strcmp(key, "endbfrange")) return TENDBFRANGE; + if (!strcmp(key, "begincidchar")) return TBEGINCIDCHAR; + if (!strcmp(key, "endcidchar")) return TENDCIDCHAR; + if (!strcmp(key, "begincidrange")) return TBEGINCIDRANGE; + if (!strcmp(key, "endcidrange")) return TENDCIDRANGE; + return PDF_TKEYWORD; +} + +static int codefromstring(unsigned char *buf, int len) +{ + int a = 0; + while (len--) + a = (a << 8) | *buf++; + return a; +} + +static int mylex(fz_file *file, unsigned char *buf, int n, int *sl) +{ + int token = pdf_lex(file, buf, n, sl); + if (token == PDF_TKEYWORD) + token = tokenfromkeyword(buf); + return token; +} + +static fz_error *parsecmapname(fz_cmap *cmap, fz_file *file) +{ + unsigned char buf[256]; + int token; + int len; + + token = mylex(file, buf, sizeof buf, &len); + if (token == PDF_TNAME) { + fz_setcmapname(cmap, buf); + return nil; + } + + return fz_throw("syntaxerror in CMap after /CMapName"); +} + +static fz_error *parsewmode(fz_cmap *cmap, fz_file *file) +{ + unsigned char buf[256]; + int token; + int len; + + token = mylex(file, buf, sizeof buf, &len); + if (token == PDF_TINT) { + fz_setwmode(cmap, atoi(buf)); + return nil; + } + + return fz_throw("syntaxerror in CMap after /WMode"); +} + +static fz_error *parsecodespacerange(fz_cmap *cmap, fz_file *file) +{ + unsigned char buf[256]; + int token; + int len; + fz_error *error; + int lo, hi; + + while (1) + { + token = mylex(file, buf, sizeof buf, &len); + + if (token == TENDCODESPACERANGE) + return nil; + + else if (token == PDF_TSTRING) + { + lo = codefromstring(buf, len); + token = mylex(file, buf, sizeof buf, &len); + if (token == PDF_TSTRING) + { + hi = codefromstring(buf, len); + error = fz_addcodespacerange(cmap, lo, hi, len); + if (error) + return error; + } + else break; + } + + else break; + } + + return fz_throw("syntaxerror in CMap codespacerange section"); +} + +static fz_error *parsecidrange(fz_cmap *cmap, fz_file *file) +{ + unsigned char buf[256]; + int token; + int len; + fz_error *error; + int lo, hi, dst; + + while (1) + { + token = mylex(file, buf, sizeof buf, &len); + + if (token == TENDCIDRANGE) + return nil; + + else if (token != PDF_TSTRING) + goto cleanup; + + lo = codefromstring(buf, len); + + token = mylex(file, buf, sizeof buf, &len); + if (token != PDF_TSTRING) + goto cleanup; + + hi = codefromstring(buf, len); + + token = mylex(file, buf, sizeof buf, &len); + if (token != PDF_TINT) + goto cleanup; + + dst = atoi(buf); + + error = fz_addcidrange(cmap, lo, hi, dst); + if (error) + return error; + } + +cleanup: + return fz_throw("syntaxerror in CMap cidrange section"); +} + +static fz_error *parsecidchar(fz_cmap *cmap, fz_file *file) +{ + unsigned char buf[256]; + int token; + int len; + fz_error *error; + int src, dst; + + while (1) + { + token = mylex(file, buf, sizeof buf, &len); + + if (token == TENDCIDCHAR) + return nil; + + else if (token != PDF_TSTRING) + goto cleanup; + + src = codefromstring(buf, len); + + token = mylex(file, buf, sizeof buf, &len); + if (token != PDF_TINT) + goto cleanup; + + dst = atoi(buf); + + error = fz_addcidrange(cmap, src, src, dst); + if (error) + return error; + } + +cleanup: + return fz_throw("syntaxerror in CMap cidchar section"); +} + +static fz_error *parsebfrange(fz_cmap *cmap, fz_file *file) +{ + unsigned char buf[256]; + int token; + int len; + fz_error *error; + int lo, hi, dst; + + while (1) + { + token = mylex(file, buf, sizeof buf, &len); + + if (token == TENDBFRANGE) + return nil; + + else if (token != PDF_TSTRING) + goto cleanup; + + lo = codefromstring(buf, len); + + token = mylex(file, buf, sizeof buf, &len); + if (token != PDF_TSTRING) + goto cleanup; + + hi = codefromstring(buf, len); + + token = mylex(file, buf, sizeof buf, &len); + /* Note: does not handle [ /Name /Name /Name ... ] */ + if (token != PDF_TSTRING) + goto cleanup; + + dst = codefromstring(buf, len); + + error = fz_addcidrange(cmap, lo, hi, dst); + if (error) + return error; + } + +cleanup: + return fz_throw("syntaxerror in CMap bfrange section"); +} + +static fz_error *parsebfchar(fz_cmap *cmap, fz_file *file) +{ + unsigned char buf[256]; + int token; + int len; + fz_error *error; + int src, dst; + + while (1) + { + token = mylex(file, buf, sizeof buf, &len); + + if (token == TENDBFCHAR) + return nil; + + else if (token != PDF_TSTRING) + goto cleanup; + + src = codefromstring(buf, len); + + token = mylex(file, buf, sizeof buf, &len); + /* Note: does not handle /dstName */ + if (token != PDF_TSTRING) + goto cleanup; + + dst = codefromstring(buf, len); + + error = fz_addcidrange(cmap, src, src, dst); + if (error) + return error; + } + +cleanup: + return fz_throw("syntaxerror in CMap bfchar section"); +} + +fz_error * +pdf_parsecmap(fz_cmap **cmapp, fz_file *file) +{ + fz_error *error; + fz_cmap *cmap; + unsigned char key[64]; + unsigned char buf[256]; + int token; + int len; + + error = fz_newcmap(&cmap); + if (error) + return error; + *cmapp = cmap; + + strcpy(key, ".notdef"); + + while (1) + { + token = mylex(file, buf, sizeof buf, &len); + + if (token == PDF_TEOF) + break; + + else if (token == PDF_TERROR) + { + error = fz_throw("syntaxerror in CMap"); + goto cleanup; + } + + else if (token == PDF_TNAME) + { + if (!strcmp(buf, "CMapName")) + { + error = parsecmapname(cmap, file); + if (error) + goto cleanup; + } + else if (!strcmp(buf, "WMode")) + { + error = parsewmode(cmap, file); + if (error) + goto cleanup; + } + else + strlcpy(key, buf, sizeof key); + } + + else if (token == TUSECMAP) + { + fz_setusecmapname(cmap, key); + } + + else if (token == TBEGINCODESPACERANGE) + { + error = parsecodespacerange(cmap, file); + if (error) + goto cleanup; + } + + else if (token == TBEGINBFCHAR) + { + error = parsebfchar(cmap, file); + if (error) + goto cleanup; + } + + else if (token == TBEGINCIDCHAR) + { + error = parsecidchar(cmap, file); + if (error) + goto cleanup; + } + + else if (token == TBEGINBFRANGE) + { + error = parsebfrange(cmap, file); + if (error) + goto cleanup; + } + + else if (token == TBEGINCIDRANGE) + { + error = parsecidrange(cmap, file); + if (error) + goto cleanup; + } + + /* ignore everything else */ + } + + error = fz_endcidrange(cmap); + if (error) + goto cleanup; + + return nil; + +cleanup: + fz_freecmap(cmap); + *cmapp = nil; + return error; +} + +fz_error * +pdf_loadembeddedcmap(fz_cmap **cmapp, pdf_xref *xref, fz_obj *stmref) +{ + fz_obj *stmobj = stmref; + fz_error *error = nil; + fz_cmap *cmap = nil; + fz_cmap *usecmap; + fz_obj *wmode; + fz_obj *obj; + + error = pdf_resolve(&stmobj, xref); + if (error) + return error; + +printf(" embedded cmap: "); +fz_fprintcobj(stdout, stmobj); +printf("\n"); +fflush(stdout); + + error = pdf_openstream(xref, stmref); + if (error) + goto cleanup; + + error = pdf_parsecmap(&cmap, xref->file); + if (error) + goto cleanup; + + pdf_closestream(xref); + + wmode = fz_dictgets(stmobj, "WMode"); + if (fz_isint(wmode)) + fz_setwmode(cmap, fz_toint(wmode)); + + obj = fz_dictgets(stmobj, "UseCMap"); + if (fz_isname(obj)) + { + printf(" usecmap predefined: %s\n", fz_toname(obj)); + error = pdf_loadsystemcmap(&usecmap, fz_toname(obj)); + if (error) + goto cleanup; + fz_setusecmap(cmap, usecmap); + } + else if (fz_isindirect(obj)) + { + printf(" usecmap recursive pdf obj\n"); + error = pdf_loadembeddedcmap(&usecmap, xref, obj); + if (error) + goto cleanup; + fz_setusecmap(cmap, usecmap); + } + + *cmapp = cmap; + + fz_dropobj(stmobj); + return nil; + +cleanup: + if (cmap) + fz_freecmap(cmap); + fz_dropobj(stmobj); + *cmapp = nil; + return error; +} + +fz_error * +pdf_loadsystemcmap(fz_cmap **cmapp, char *name) +{ + fz_error *error = nil; + fz_file *file; + char *cmapdir; + char *usecmapname; + fz_cmap *usecmap; + char path[1024]; + + *cmapp = nil; + + cmapdir = getenv("CMAPDIR"); + if (!cmapdir) + return fz_throw("ioerror: CMAPDIR environment not set"); + + strlcpy(path, cmapdir, sizeof path); + strlcat(path, "/", sizeof path); + strlcat(path, name, sizeof path); + +printf(" system cmap loading %s\n", path); + + error = fz_openfile(&file, path, O_RDONLY); + if (error) + goto cleanup; + + error = pdf_parsecmap(cmapp, file); + if (error) + goto cleanup; + + fz_closefile(file); + + usecmapname = fz_getusecmapname(*cmapp); + if (usecmapname) + { +printf(" system cmap: usecmap %s\n", usecmapname); + error = pdf_loadsystemcmap(&usecmap, usecmapname); + if (error) + goto cleanup; + fz_setusecmap(*cmapp, usecmap); + } + + return nil; + +cleanup: + if (*cmapp) + fz_freecmap(*cmapp); + if (file) + fz_closefile(file); + *cmapp = nil; + return error; +} + +fz_error * +pdf_makeidentitycmap(fz_cmap **cmapp, int wmode) +{ + fz_error *error = nil; + + error = fz_newcmap(cmapp); + if (error) + return error; + + error = fz_addcodespacerange(*cmapp, 0x0000, 0xffff, 2); + if (error) { + fz_freecmap(*cmapp); + return error; + } + + error = fz_addcidrange(*cmapp, 0x0000, 0xffff, 0); + if (error) { + fz_freecmap(*cmapp); + return error; + } + + error = fz_endcidrange(*cmapp); + if (error) { + fz_freecmap(*cmapp); + return error; + } + + fz_setwmode(*cmapp, wmode); + + return nil; +} + diff --git a/mupdf/crypt.c b/mupdf/crypt.c new file mode 100644 index 00000000..730c0bd5 --- /dev/null +++ b/mupdf/crypt.c @@ -0,0 +1,356 @@ +#include <fitz.h> +#include <mupdf.h> + +static unsigned char padding[32] = +{ + 0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, + 0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08, + 0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, + 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a, +}; + +static void voodoo50(unsigned char *buf, int n) +{ + fz_md5 md5; + int i; + + for (i = 0; i < 50; i++) { + fz_md5init(&md5); + fz_md5update(&md5, buf, n); + fz_md5final(&md5, buf); + } +} + +static void voodoo19(unsigned char *data, int ndata, unsigned char *key, int nkey) +{ + fz_arc4 arc4; + unsigned char keybuf[16]; + int i, k; + + for (i = 1; i <= 19; i++) { + for (k = 0; k < nkey; k++) + keybuf[k] = key[k] ^ (unsigned char)i; + fz_arc4init(&arc4, keybuf, nkey); + fz_arc4encrypt(&arc4, data, data, ndata); + } +} + +static void padpassword(unsigned char *buf, char *pw) +{ + int len = strlen(pw); + if (len > 32) + len = 32; + memcpy(buf, pw, len); + memcpy(buf + len, padding, 32 - len); +} + +fz_error * +pdf_newdecrypt(pdf_crypt **cp, fz_obj *enc, fz_obj *id) +{ + pdf_crypt *crypt = nil; + fz_obj *obj; + int m; + + obj = fz_dictgets(enc, "V"); + m = 0; + if (fz_isint(obj)) + m = fz_toint(obj); + if (m != 1 && m != 2) + return fz_throw("unsupported encryption: %d", m); + + crypt = *cp = fz_malloc(sizeof(pdf_crypt)); + if (!crypt) return fz_outofmem; + + obj = fz_dictgets(enc, "O"); + if (!fz_isstring(obj) || fz_tostringlen(obj) != 32) + goto cleanup; + memcpy(crypt->o, fz_tostringbuf(obj), 32); + + obj = fz_dictgets(enc, "U"); + if (!fz_isstring(obj) || fz_tostringlen(obj) != 32) + goto cleanup; + memcpy(crypt->u, fz_tostringbuf(obj), 32); + + obj = fz_dictgets(enc, "P"); + if (!fz_isint(obj)) + goto cleanup; + crypt->p = fz_toint(obj); + + obj = fz_dictgets(enc, "Length"); + if (fz_isint(obj)) + crypt->n = fz_toint(obj) / 8; + else + crypt->n = 40 / 8; + if (crypt->n < 5) goto cleanup; + if (crypt->n > 16) goto cleanup; + + obj = fz_dictgets(enc, "R"); + if (!fz_isint(obj)) + goto cleanup; + crypt->r = fz_toint(obj); + if (crypt->r != 2 && crypt->r != 3) + goto cleanup; + if (crypt->r == 2 && crypt->n != 5) + goto cleanup; + + if (!fz_isarray(id) || fz_arraylen(id) != 2) + goto cleanup; + obj = fz_arrayget(id, 0); + if (!fz_isstring(obj)) + goto cleanup; + crypt->id = fz_keepobj(obj); + + crypt->keylen = crypt->n + 5; + if (crypt->keylen > 16) + crypt->keylen = 16; + + memset(crypt->key, 0, 16); + + return nil; + +cleanup: + if (crypt) fz_free(crypt); + return fz_throw("corrupt encryption dictionary"); +} + +void +pdf_freecrypt(pdf_crypt *crypt) +{ + fz_dropobj(crypt->id); + fz_free(crypt); +} + +static void +createobjkey(pdf_crypt *crypt, unsigned oid, unsigned gid, unsigned char *key) +{ + unsigned char message[5]; + fz_md5 md5; + + /* Algorithm 3.1 Encryption of data using an encryption key */ + + fz_md5init(&md5); + + fz_md5update(&md5, crypt->key, crypt->n); + + message[0] = oid & 0xFF; + message[1] = (oid >> 8) & 0xFF; + message[2] = (oid >> 16) & 0xFF; + + message[3] = gid & 0xFF; + message[4] = (gid >> 8) & 0xFF; + + fz_md5update(&md5, message, 5); + + fz_md5final(&md5, key); +} + +/* + * Algorithm 3.2 Computing an encryption key + */ +static void +createkey(pdf_crypt *crypt, unsigned char *userpw) +{ + unsigned char buf[32]; + fz_md5 md5; + + /* Step 1 + 2 */ + fz_md5init(&md5); + padpassword(buf, userpw); + fz_md5update(&md5, buf, 32); + + /* Step 3 */ + fz_md5update(&md5, crypt->o, 32); + + /* Step 4 */ + buf[0] = crypt->p & 0xFF; + buf[1] = (crypt->p >> 8) & 0xFF; + buf[2] = (crypt->p >> 16) & 0xFF; + buf[3] = (crypt->p >> 24) & 0xFF; + fz_md5update(&md5, buf, 4); + + /* Step 5 */ + fz_md5update(&md5, fz_tostringbuf(crypt->id), fz_tostringlen(crypt->id)); + fz_md5final(&md5, crypt->key); + + /* Step 6 (rev 3 only) */ + if (crypt->r == 3) + voodoo50(crypt->key, crypt->n); + + /* Step 7: key is in crypt->key */ +} + +/* + * Algorithm 3.3 Computing the O value + */ +static void +createowner(pdf_crypt *crypt, char *userpw, char *ownerpw) +{ + unsigned char buf[32]; + unsigned char key[16]; + fz_arc4 arc4; + fz_md5 md5; + + /* Step 1 + 2 */ + if (strlen(ownerpw) == 0) + ownerpw = userpw; + padpassword(buf, ownerpw); + fz_md5init(&md5); + fz_md5update(&md5, buf, 32); + fz_md5final(&md5, key); + + /* Step 3 (rev 3 only) */ + if (crypt->r == 3) + voodoo50(key, crypt->n); + + /* Step 4 */ + fz_arc4init(&arc4, key, crypt->n); + + /* Step 5 */ + padpassword(buf, userpw); + + /* Step 6 */ + fz_arc4encrypt(&arc4, buf, buf, 32); + + /* Step 7 (rev 3 only) */ + if (crypt->r == 3) + voodoo19(buf, 32, key, crypt->n); + + /* Step 8 */ + memcpy(crypt->o, buf, 32); +} + +/* + * Algorithm 3.4 Computing the U value (rev 2) + * Algorithm 3.5 Computing the U value (rev 3) + */ +static void +createuser(pdf_crypt *crypt, char *userpw) +{ + unsigned char key[16]; + fz_arc4 arc4; + fz_md5 md5; + + if (crypt->r == 2) { + createkey(crypt, userpw); + fz_arc4init(&arc4, crypt->key, crypt->n); + fz_arc4encrypt(&arc4, crypt->u, padding, 32); + } + + if (crypt->r == 3) { + /* Step 1 */ + createkey(crypt, userpw); + + /* Step 2 */ + fz_md5init(&md5); + fz_md5update(&md5, padding, 32); + + /* Step 3 */ + fz_md5update(&md5, fz_tostringbuf(crypt->id), fz_tostringlen(crypt->id)); + fz_md5final(&md5, key); + + /* Step 4 */ + fz_arc4init(&arc4, crypt->key, crypt->n); + fz_arc4encrypt(&arc4, key, key, 16); + + /* Step 5 */ + voodoo19(key, 16, crypt->key, crypt->n); + + /* Step 6 */ + memcpy(crypt->u, key, 16); + memset(crypt->u + 16, 0, 16); + } +} + +fz_error * +pdf_newencrypt(pdf_crypt **cp, fz_obj **dict, + char *userpw, char *ownerpw, int p, int n, fz_obj *id) +{ + fz_error *error; + pdf_crypt *crypt; + + crypt = *cp = fz_malloc(sizeof(pdf_crypt)); + if (!crypt) return fz_outofmem; + + crypt->id = fz_keepobj(fz_arrayget(id, 0)); + crypt->p = p; + crypt->n = MIN(MAX(n / 8, 5), 16); + crypt->keylen = MIN(crypt->n + 5, 16); + crypt->r = crypt->n == 5 ? 2 : 3; + + createowner(crypt, userpw, ownerpw); + createuser(crypt, userpw); + + error = fz_packobj(dict, + "<< /Filter /Standard " + "/V %i /R %i " + "/O %# /U %# " + "/P %i " + "/Length %i >>", + crypt->r == 2 ? 1 : 2, + crypt->r, + crypt->o, 32, + crypt->u, 32, + crypt->p, + crypt->n * 8); + return error; +} + +/* + * Algorithm 3.6 Checking the user password + */ +fz_error * +pdf_setpassword(pdf_crypt *crypt, char *userpw) +{ + unsigned char saved[32]; + unsigned char test[32]; + + memcpy(saved, crypt->u, 32); + createuser(crypt, userpw); + memcpy(test, crypt->u, 32); + memcpy(crypt->u, saved, 32); + + if (memcmp(test, saved, crypt->r == 3 ? 16 : 32) != 0) + return fz_throw("invalid password"); + + return nil; +} + +void +pdf_cryptobj(pdf_crypt *crypt, fz_obj *obj, int oid, int gid) +{ + fz_arc4 arc4; + unsigned char key[16]; + unsigned char *s; + int i, n; + + if (fz_isstring(obj)) { + s = fz_tostringbuf(obj); + n = fz_tostringlen(obj); + createobjkey(crypt, oid, gid, key); + fz_arc4init(&arc4, key, crypt->keylen); + fz_arc4encrypt(&arc4, s, s, n); + } + + else if (fz_isarray(obj)) { + n = fz_arraylen(obj); + for (i = 0; i < n; i++) { + pdf_cryptobj(crypt, fz_arrayget(obj, i), oid, gid); + } + } + + else if (fz_isdict(obj)) { + n = fz_dictlen(obj); + for (i = 0; i < n; i++) { + pdf_cryptobj(crypt, fz_dictgetval(obj, i), oid, gid); + } + } +} + +fz_error * +pdf_cryptstm(fz_filter **fp, pdf_crypt *crypt, int oid, int gid) +{ + unsigned char key[16]; + createobjkey(crypt, oid, gid, key); + return fz_newarc4filter(fp, key, crypt->keylen); +} + diff --git a/mupdf/font.c b/mupdf/font.c new file mode 100644 index 00000000..1fa5fa93 --- /dev/null +++ b/mupdf/font.c @@ -0,0 +1,790 @@ +#include <fitz.h> +#include <mupdf.h> + +#include <ft2build.h> +#include FT_FREETYPE_H +#include <freetype/internal/ftobjs.h> + +#include "fontenc.h" +#include "fontagl.h" + +enum { UNKNOWN, TYPE1, CFF, TRUETYPE, CID }; + +static int ftkind(FT_Face face) +{ + const char *kind = face->driver->clazz->root.module_name; +printf(" type %s\n", kind); + if (!strcmp(kind, "type1")) + return TYPE1; + if (!strcmp(kind, "cff")) + return CFF; + if (!strcmp(kind, "truetype")) + return TRUETYPE; + if (!strcmp(kind, "t1cid")) + return CID; + return UNKNOWN; +} + +static int ftwidth(FT_Face face, int gid) +{ + int e; + e = FT_Load_Glyph(face, gid, + FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM); + if (e) + return 0; + return face->glyph->advance.x; +} + +static fz_error * +ftrender(fz_glyph *glyph, fz_font *font, int gid, fz_matrix trm) +{ + FT_Face face = ((pdf_font*)font)->ftface; + FT_Matrix m; + FT_Vector v; + FT_Error fterr; + + glyph->w = 0; + glyph->h = 0; + glyph->lsb = 0; + glyph->top = 0; + glyph->bitmap = nil; + + m.xx = trm.a * 65536; + m.yx = trm.b * 65536; + m.xy = trm.c * 65536; + m.yy = trm.d * 65536; + v.x = trm.e * 64; + v.y = trm.f * 64; + + FT_Set_Char_Size(face, 64, 64, 72, 72); + FT_Set_Transform(face, &m, &v); + + fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING); + if (fterr) + return fz_throw("freetype failed to load glyph: 0x%x", fterr); + + fterr = FT_Render_Glyph(face->glyph, ft_render_mode_normal); + if (fterr) + return fz_throw("freetype failed to render glyph: 0x%x", fterr); + + glyph->w = face->glyph->bitmap.width; + glyph->h = face->glyph->bitmap.rows; + glyph->lsb = face->glyph->bitmap_left; + glyph->top = face->glyph->bitmap_top; + glyph->bitmap = face->glyph->bitmap.buffer; + + return nil; +} + +static char *cleanfontname(char *fontname) +{ + int i, k; + for (i = 0; i < 14; i++) + for (k = 0; basefontnames[i][k]; k++) + if (!strcmp(basefontnames[i][k], fontname)) + return basefontnames[i][0]; + return fontname; +} + +static void loadencoding(char **estrings, char *encoding) +{ + char **bstrings = nil; + int i; + + if (!strcmp(encoding, "MacRomanEncoding")) + bstrings = macroman; + if (!strcmp(encoding, "MacExpertEncoding")) + bstrings = macexpert; + if (!strcmp(encoding, "WinAnsiEncoding")) + bstrings = winansi; + + if (bstrings) + for (i = 0; i < 256; i++) + estrings[i] = bstrings[i]; +} + +static int aglcode(char *name) +{ + int l = 0; + int r = adobeglyphlen; + + while (l <= r) + { + int m = (l + r) >> 1; + int c = strcmp(name, adobeglyphlist[m].name); + if (c < 0) + r = m - 1; + else if (c > 0) + l = m + 1; + else + return adobeglyphlist[m].code; + } + + if (strstr(name, "uni") == name) + return strtol(name + 3, 0, 16); + + if (strstr(name, "u") == name) + return strtol(name + 1, 0, 16); + + return -1; +} + +static int mrecode(char *name) +{ + int i; + for (i = 0; i < 256; i++) + if (macroman[i] && !strcmp(name, macroman[i])) + return i; + return -1; +} + +static int cidtogid(pdf_font *font, int cid) +{ + if (font->cidtogidmap) + { + if (cid >= 0 && cid < font->cidtogidlen) + return font->cidtogidmap[cid]; + return 0; + } + return cid; +} + +static void ftfreefont(fz_font *font) +{ + pdf_font *pfont = (pdf_font*)font; + if (pfont->encoding) + fz_freecmap(pfont->encoding); +} + +static pdf_font * +newfont(char *name) +{ + pdf_font *font; + + font = fz_malloc(sizeof (pdf_font)); + if (!font) + return nil; + + fz_initfont((fz_font*)font, name); + font->super.render = ftrender; + font->super.free = (void(*)(fz_font*)) ftfreefont; + + font->ftface = nil; + font->encoding = nil; + font->cidtogidlen = 0; + font->cidtogidmap = nil; + + return font; +} + +static fz_error * +loadsimplefont(pdf_font **fontp, pdf_xref *xref, fz_obj *dict) +{ + fz_error *error; + fz_obj *descriptor = nil; + fz_obj *encoding = nil; + fz_obj *widths = nil; + pdf_font *font; + FT_Face face; + FT_CharMap cmap; + int kind; + + char *basefont; + char *estrings[256]; + int etable[256]; + int i, k, n, e; + + basefont = fz_toname(fz_dictgets(dict, "BaseFont")); + basefont = cleanfontname(basefont); + + /* + * Load font file + */ + +printf("loading simple font %s\n", basefont); + + font = *fontp = newfont(basefont); + if (!font) + return fz_outofmem; + + descriptor = fz_dictgets(dict, "FontDescriptor"); + if (descriptor) + error = pdf_loadfontdescriptor(&font->ftface, xref, descriptor, nil); + else + error = pdf_loadsystemfont(&font->ftface, basefont, nil); + if (error) + goto cleanup; + + face = font->ftface; + kind = ftkind(face); + + fz_setfontbbox((fz_font*)font, + face->bbox.xMin, face->bbox.yMin, + face->bbox.xMax, face->bbox.yMax); + + /* + * Encoding + */ + + if (face->num_charmaps > 0) + cmap = face->charmaps[0]; + else + cmap = nil; + + for (i = 0; i < face->num_charmaps; i++) + { + FT_CharMap test = face->charmaps[i]; + + if (kind == CFF || kind == TYPE1) + { + if (test->platform_id == 7) + cmap = test; + } + + if (kind == TRUETYPE) + { + if (test->platform_id == 1 && test->encoding_id == 0) + cmap = test; + if (test->platform_id == 3 && test->encoding_id == 1) + cmap = test; + } + } + + if (!cmap) + { + error = fz_throw("freetype could not find any cmaps"); + goto cleanup; + } + + e = FT_Set_Charmap(face, cmap); + if (e) + { + error = fz_throw("freetype could not set cmap: 0x%x", e); + goto cleanup; + } + + for (i = 0; i < 256; i++) + { + estrings[i] = _notdef; + etable[i] = 0; + } + + encoding = fz_dictgets(dict, "Encoding"); + if (encoding) + { + error = pdf_resolve(&encoding, xref); + if (error) + goto cleanup; + + + if (fz_isname(encoding)) + loadencoding(estrings, fz_toname(encoding)); + + if (fz_isdict(encoding)) + { + fz_obj *base, *diff, *item; + + base = fz_dictgets(encoding, "BaseEncoding"); + if (fz_isname(base)) + loadencoding(estrings, fz_toname(base)); + + + diff = fz_dictgets(encoding, "Differences"); + if (fz_isarray(diff)) + { + n = fz_arraylen(diff); + k = 0; + for (i = 0; i < n; i++) + { + item = fz_arrayget(diff, i); + if (fz_isint(item)) + k = fz_toint(item); + if (fz_isname(item)) + estrings[k++] = fz_toname(item); + if (k < 0) k = 0; + if (k > 255) k = 255; + } + } + } + + if (kind == TYPE1 || kind == CFF) + { + for (i = 0; i < 256; i++) + if (estrings[i]) + etable[i] = FT_Get_Name_Index(face, estrings[i]); + else + etable[i] = FT_Get_Char_Index(face, i); + } + + if (kind == TRUETYPE) + { + /* Unicode cmap */ + if (face->charmap->platform_id == 3) + { + for (i = 0; i < 256; i++) + if (estrings[i]) + { + k = aglcode(estrings[i]); + if (k == -1) + etable[i] = FT_Get_Name_Index(face, estrings[i]); + else + etable[i] = FT_Get_Char_Index(face, k); + } + else + etable[i] = FT_Get_Char_Index(face, i); + } + + /* MacRoman cmap */ + else if (face->charmap->platform_id == 1) + { + for (i = 0; i < 256; i++) + if (estrings[i]) + { + k = mrecode(estrings[i]); + if (k <= 0) + etable[i] = FT_Get_Name_Index(face, estrings[i]); + else + etable[i] = FT_Get_Char_Index(face, k); + } + else + etable[i] = FT_Get_Char_Index(face, i); + } + + /* Symbolic cmap */ + else + { + for (i = 0; i < 256; i++) + etable[i] = FT_Get_Char_Index(face, i); + } + } + + encoding = fz_dropobj(encoding); + } + + else + { + for (i = 0; i < 256; i++) + etable[i] = FT_Get_Char_Index(face, i); + } + + error = fz_newcmap(&font->encoding); + if (error) + goto cleanup; + + error = fz_addcodespacerange(font->encoding, 0x00, 0xff, 1); + if (error) + goto cleanup; + + error = fz_setcidlookup(font->encoding, etable); + if (error) + goto cleanup; + + /* + * Widths + */ + + /* FIXME should set defaulthmtx to MissingWidth in FontDescriptor */ + + widths = fz_dictgets(dict, "Widths"); + if (widths) + { + int first, last; + + error = pdf_resolve(&widths, xref); + if (error) + goto cleanup; + + first = fz_toint(fz_dictgets(dict, "FirstChar")); + last = fz_toint(fz_dictgets(dict, "LastChar")); + +printf(" widths vector %d to %d\n", first, last); + + if (first < 0 || last > 255 || first > last) + first = last = 0; + + for (i = 0; i < last - first + 1; i++) + { + int gid = etable[i + first]; + int wid = fz_toint(fz_arrayget(widths, i)); + if (gid >= 0) + { + error = fz_addhmtx((fz_font*)font, gid, wid); + if (error) + goto cleanup; + } + } + + widths = fz_dropobj(widths); + } + else + { +printf(" builtin widths\n"); + FT_Set_Char_Size(face, 1000, 1000, 72, 72); + for (i = 0; i < 256; i++) + { + int gid = etable[i]; + if (gid >= 0) + { + error = fz_addhmtx((fz_font*)font, gid, ftwidth(face, gid)); + if (error) + goto cleanup; + } + } + } + + error = fz_endhmtx((fz_font*)font); + if (error) + goto cleanup; + + FT_Set_Char_Size(face, 64, 64, 72, 72); + +printf("\n"); + +fz_debugfont((fz_font*)font); + + return nil; + +cleanup: + if (widths) + fz_dropobj(widths); + fz_freefont((fz_font*)font); + *fontp = nil; + return error; +} + +static fz_error * +loadcidfont(pdf_font **fontp, pdf_xref *xref, fz_obj *dict, fz_obj *encoding) +{ + fz_error *error; + fz_obj *widths = nil; + fz_obj *descriptor; + pdf_font *font; + FT_Face face; + int kind; + char collection[256]; + char *basefont; + int i, k; + + /* + * Get font name and CID collection + */ + + basefont = fz_toname(fz_dictgets(dict, "BaseFont")); + +printf("loading cid font %s\n", basefont); + + { + fz_obj *cidinfo; + fz_obj *obj; + char tmpstr[64]; + int tmplen; + + cidinfo = fz_dictgets(dict, "CIDSystemInfo"); + + error = pdf_resolve(&cidinfo, xref); + if (error) + return error; + + obj = fz_dictgets(cidinfo, "Registry"); + tmplen = MIN(sizeof tmpstr - 1, fz_tostringlen(obj)); + memcpy(tmpstr, fz_tostringbuf(obj), tmplen); + tmpstr[tmplen] = '\0'; + strlcpy(collection, tmpstr, sizeof collection); + + strlcat(collection, "-", sizeof collection); + + obj = fz_dictgets(cidinfo, "Ordering"); + tmplen = MIN(sizeof tmpstr - 1, fz_tostringlen(obj)); + memcpy(tmpstr, fz_tostringbuf(obj), tmplen); + tmpstr[tmplen] = '\0'; + strlcat(collection, tmpstr, sizeof collection); + + fz_dropobj(cidinfo); + } + +printf(" collection %s\n", collection); + + /* + * Load font file + */ + + font = *fontp = newfont(basefont); + if (!font) + return fz_outofmem; + + descriptor = fz_dictgets(dict, "FontDescriptor"); + if (descriptor) + error = pdf_loadfontdescriptor(&font->ftface, xref, descriptor, collection); + else + error = fz_throw("syntaxerror: missing font descriptor"); + if (error) + goto cleanup; + + face = font->ftface; + kind = ftkind(face); + + fz_setfontbbox((fz_font*)font, + face->bbox.xMin, face->bbox.yMin, + face->bbox.xMax, face->bbox.yMax); + + /* + * Encoding + */ + + if (fz_isname(encoding)) + { +printf(" external CMap %s\n", fz_toname(encoding)); + if (!strcmp(fz_toname(encoding), "Identity-H")) + error = pdf_makeidentitycmap(&font->encoding, 0); + else if (!strcmp(fz_toname(encoding), "Identity-V")) + error = pdf_makeidentitycmap(&font->encoding, 1); + else + error = pdf_loadsystemcmap(&font->encoding, fz_toname(encoding)); + } + else if (fz_isindirect(encoding)) + { +printf(" embedded CMap\n"); + error = pdf_loadembeddedcmap(&font->encoding, xref, encoding); + } + else + { + error = fz_throw("syntaxerror: font missing encoding"); + } + if (error) + goto cleanup; + + fz_setfontwmode((fz_font*)font, fz_getwmode(font->encoding)); + + if (kind == TRUETYPE) + { + fz_obj *cidtogidmap; + + cidtogidmap = fz_dictgets(dict, "CIDToGIDMap"); + if (fz_isindirect(cidtogidmap)) + { + unsigned char *buf; + int len; + + error = pdf_readstream(&buf, &len, xref, cidtogidmap); + if (error) + goto cleanup; + + font->cidtogidlen = len / 2; + font->cidtogidmap = fz_malloc((len / 2) * sizeof(int)); + if (!font->cidtogidmap) { + fz_free(buf); + error = fz_outofmem; + goto cleanup; + } + +printf(" cidtogidmap %d\n", len / 2); + + for (i = 0; i < len / 2; i++) + font->cidtogidmap[i] = (buf[i * 2] << 8) + buf[i * 2 + 1]; + } + + /* TODO: if truetype font is external, cidtogidmap should not be identity */ + /* we should map the cid to another encoding represented by a 'cmap' table */ + /* and then through that to a gid */ + /* cids: Adobe-CNS1 Adobe-GB1 Adobe-Japan1 Adobe-Japan2 Adobe-Korea1 */ + /* cmap: Big5 Johab PRC ShiftJIS Unicode Wansung */ + /* win: 3,4 3,6 3,3 3,2 3,1 3,5 */ + } + + /* + * Horizontal + */ + + fz_setdefaulthmtx((fz_font*)font, fz_toint(fz_dictgets(dict, "DW"))); + + widths = fz_dictgets(dict, "W"); + if (widths) + { + int c0, c1, w, gid; + fz_obj *obj; + + error = pdf_resolve(&widths, xref); + if (error) + goto cleanup; + + for (i = 0; i < fz_arraylen(widths); ) + { + c0 = fz_toint(fz_arrayget(widths, i)); + obj = fz_arrayget(widths, i + 1); + if (fz_isarray(obj)) + { + for (k = 0; k < fz_arraylen(obj); k++) + { + w = fz_toint(fz_arrayget(obj, k)); + gid = cidtogid(font, c0 + k); + error = fz_addhmtx((fz_font*)font, gid, w); + if (error) + goto cleanup; + } + i += 2; + } + else + { + c1 = fz_toint(obj); + w = fz_toint(fz_arrayget(widths, i + 2)); + for (k = c0; k <= c1; k++) + { + gid = cidtogid(font, k); + error = fz_addhmtx((fz_font*)font, gid, w); + if (error) + goto cleanup; + } + i += 3; + } + } + + widths = fz_dropobj(widths); + } + + error = fz_endhmtx((fz_font*)font); + if (error) + goto cleanup; + + /* + * Vertical + */ + + if (fz_getwmode(font->encoding) == 1) + { + fz_obj *obj; + int dw2y = 880; + int dw2w = -1000; + + obj = fz_dictgets(dict, "DW2"); + if (obj) + { + dw2y = fz_toint(fz_arrayget(obj, 0)); + dw2w = fz_toint(fz_arrayget(obj, 1)); + } + + fz_setdefaultvmtx((fz_font*)font, dw2y, dw2w); + + widths = fz_dictgets(dict, "W2"); + if (widths) + { + int c0, c1, w, x, y, k, gid; + + error = pdf_resolve(&widths, xref); + if (error) + goto cleanup; + +printf(" W2 "); +fz_fprintobj(stdout, widths); +printf("\n"); + + for (i = 0; i < fz_arraylen(widths); ) + { + c0 = fz_toint(fz_arrayget(widths, i)); + obj = fz_arrayget(widths, i + 1); + if (fz_isarray(obj)) + { + for (k = 0; k < fz_arraylen(obj); k += 3) + { + w = fz_toint(fz_arrayget(obj, k + 0)); + x = fz_toint(fz_arrayget(obj, k + 1)); + y = fz_toint(fz_arrayget(obj, k + 2)); + gid = cidtogid(font, c0 + k); + error = fz_addvmtx((fz_font*)font, gid, x, y, w); + if (error) + goto cleanup; + } + i += 2; + } + else + { + c1 = fz_toint(obj); + w = fz_toint(fz_arrayget(widths, i + 2)); + x = fz_toint(fz_arrayget(widths, i + 3)); + y = fz_toint(fz_arrayget(widths, i + 4)); + for (k = c0; k <= c1; k++) + { + gid = cidtogid(font, c0); + error = fz_addvmtx((fz_font*)font, gid, x, y, w); + if (error) + goto cleanup; + } + i += 5; + } + } + + widths = fz_dropobj(widths); + } + + error = fz_endvmtx((fz_font*)font); + if (error) + goto cleanup; + } + + FT_Set_Char_Size(face, 64, 64, 72, 72); + +printf("\n"); + +fz_debugfont((fz_font*)font); + + return nil; + +cleanup: + if (widths) + fz_dropobj(widths); + fz_freefont((fz_font*)font); + *fontp = nil; + return error; +} + +static fz_error * +loadtype0(pdf_font **fontp, pdf_xref *xref, fz_obj *dict) +{ + fz_error *error; + fz_obj *dfonts; + fz_obj *dfont; + fz_obj *subtype; + fz_obj *encoding; + + dfonts = fz_dictgets(dict, "DescendantFonts"); + error = pdf_resolve(&dfonts, xref); + if (error) + return error; + + dfont = fz_arrayget(dfonts, 0); + error = pdf_resolve(&dfont, xref); + if (error) + return fz_dropobj(dfonts), error; + + encoding = fz_dictgets(dict, "Encoding"); + subtype = fz_dictgets(dfont, "Subtype"); + + if (!strcmp(fz_toname(subtype), "CIDFontType0")) + error = loadcidfont(fontp, xref, dfont, encoding); + else if (!strcmp(fz_toname(subtype), "CIDFontType2")) + error = loadcidfont(fontp, xref, dfont, encoding); + else + error = fz_throw("syntaxerror: unknown cid font type"); + + fz_dropobj(dfont); + fz_dropobj(dfonts); + + if (error) + return error; + + return nil; +} + +fz_error * +pdf_loadfont(pdf_font **fontp, pdf_xref *xref, fz_obj *dict) +{ + char *subtype = fz_toname(fz_dictgets(dict, "Subtype")); + if (!strcmp(subtype, "Type0")) + return loadtype0(fontp, xref, dict); + if (!strcmp(subtype, "Type1") || !strcmp(subtype, "MMType1")) + return loadsimplefont(fontp, xref, dict); + else if (!strcmp(subtype, "TrueType")) + return loadsimplefont(fontp, xref, dict); + else + return fz_throw("unimplemented: %s fonts", subtype); +} + diff --git a/mupdf/fontagl.h b/mupdf/fontagl.h new file mode 100644 index 00000000..847c0a21 --- /dev/null +++ b/mupdf/fontagl.h @@ -0,0 +1,4305 @@ +/* Name: Adobe Glyph List + # Table version: 2.0 + # Date: September 20, 2002 + # + # See http:partners.adobe.com/asn/developer/typeforum/unicodegn.html + # + # Format: Semicolon-delimited fields: + # (1) glyph name + # (2) Unicode scalar value + */ + +struct aglpair { char *name; int code; }; + +#define adobeglyphlen (sizeof(adobeglyphlist) / sizeof(struct aglpair)) + +static struct aglpair adobeglyphlist[] = +{ + +{"A",0x0041}, +{"AE",0x00C6}, +{"AEacute",0x01FC}, +{"AEmacron",0x01E2}, +{"AEsmall",0xF7E6}, +{"Aacute",0x00C1}, +{"Aacutesmall",0xF7E1}, +{"Abreve",0x0102}, +{"Abreveacute",0x1EAE}, +{"Abrevecyrillic",0x04D0}, +{"Abrevedotbelow",0x1EB6}, +{"Abrevegrave",0x1EB0}, +{"Abrevehookabove",0x1EB2}, +{"Abrevetilde",0x1EB4}, +{"Acaron",0x01CD}, +{"Acircle",0x24B6}, +{"Acircumflex",0x00C2}, +{"Acircumflexacute",0x1EA4}, +{"Acircumflexdotbelow",0x1EAC}, +{"Acircumflexgrave",0x1EA6}, +{"Acircumflexhookabove",0x1EA8}, +{"Acircumflexsmall",0xF7E2}, +{"Acircumflextilde",0x1EAA}, +{"Acute",0xF6C9}, +{"Acutesmall",0xF7B4}, +{"Acyrillic",0x0410}, +{"Adblgrave",0x0200}, +{"Adieresis",0x00C4}, +{"Adieresiscyrillic",0x04D2}, +{"Adieresismacron",0x01DE}, +{"Adieresissmall",0xF7E4}, +{"Adotbelow",0x1EA0}, +{"Adotmacron",0x01E0}, +{"Agrave",0x00C0}, +{"Agravesmall",0xF7E0}, +{"Ahookabove",0x1EA2}, +{"Aiecyrillic",0x04D4}, +{"Ainvertedbreve",0x0202}, +{"Alpha",0x0391}, +{"Alphatonos",0x0386}, +{"Amacron",0x0100}, +{"Amonospace",0xFF21}, +{"Aogonek",0x0104}, +{"Aring",0x00C5}, +{"Aringacute",0x01FA}, +{"Aringbelow",0x1E00}, +{"Aringsmall",0xF7E5}, +{"Asmall",0xF761}, +{"Atilde",0x00C3}, +{"Atildesmall",0xF7E3}, +{"Aybarmenian",0x0531}, +{"B",0x0042}, +{"Bcircle",0x24B7}, +{"Bdotaccent",0x1E02}, +{"Bdotbelow",0x1E04}, +{"Becyrillic",0x0411}, +{"Benarmenian",0x0532}, +{"Beta",0x0392}, +{"Bhook",0x0181}, +{"Blinebelow",0x1E06}, +{"Bmonospace",0xFF22}, +{"Brevesmall",0xF6F4}, +{"Bsmall",0xF762}, +{"Btopbar",0x0182}, +{"C",0x0043}, +{"Caarmenian",0x053E}, +{"Cacute",0x0106}, +{"Caron",0xF6CA}, +{"Caronsmall",0xF6F5}, +{"Ccaron",0x010C}, +{"Ccedilla",0x00C7}, +{"Ccedillaacute",0x1E08}, +{"Ccedillasmall",0xF7E7}, +{"Ccircle",0x24B8}, +{"Ccircumflex",0x0108}, +{"Cdot",0x010A}, +{"Cdotaccent",0x010A}, +{"Cedillasmall",0xF7B8}, +{"Chaarmenian",0x0549}, +{"Cheabkhasiancyrillic",0x04BC}, +{"Checyrillic",0x0427}, +{"Chedescenderabkhasiancyrillic",0x04BE}, +{"Chedescendercyrillic",0x04B6}, +{"Chedieresiscyrillic",0x04F4}, +{"Cheharmenian",0x0543}, +{"Chekhakassiancyrillic",0x04CB}, +{"Cheverticalstrokecyrillic",0x04B8}, +{"Chi",0x03A7}, +{"Chook",0x0187}, +{"Circumflexsmall",0xF6F6}, +{"Cmonospace",0xFF23}, +{"Coarmenian",0x0551}, +{"Csmall",0xF763}, +{"D",0x0044}, +{"DZ",0x01F1}, +{"DZcaron",0x01C4}, +{"Daarmenian",0x0534}, +{"Dafrican",0x0189}, +{"Dcaron",0x010E}, +{"Dcedilla",0x1E10}, +{"Dcircle",0x24B9}, +{"Dcircumflexbelow",0x1E12}, +{"Dcroat",0x0110}, +{"Ddotaccent",0x1E0A}, +{"Ddotbelow",0x1E0C}, +{"Decyrillic",0x0414}, +{"Deicoptic",0x03EE}, +{"Delta",0x2206}, +{"Deltagreek",0x0394}, +{"Dhook",0x018A}, +{"Dieresis",0xF6CB}, +{"DieresisAcute",0xF6CC}, +{"DieresisGrave",0xF6CD}, +{"Dieresissmall",0xF7A8}, +{"Digammagreek",0x03DC}, +{"Djecyrillic",0x0402}, +{"Dlinebelow",0x1E0E}, +{"Dmonospace",0xFF24}, +{"Dotaccentsmall",0xF6F7}, +{"Dslash",0x0110}, +{"Dsmall",0xF764}, +{"Dtopbar",0x018B}, +{"Dz",0x01F2}, +{"Dzcaron",0x01C5}, +{"Dzeabkhasiancyrillic",0x04E0}, +{"Dzecyrillic",0x0405}, +{"Dzhecyrillic",0x040F}, +{"E",0x0045}, +{"Eacute",0x00C9}, +{"Eacutesmall",0xF7E9}, +{"Ebreve",0x0114}, +{"Ecaron",0x011A}, +{"Ecedillabreve",0x1E1C}, +{"Echarmenian",0x0535}, +{"Ecircle",0x24BA}, +{"Ecircumflex",0x00CA}, +{"Ecircumflexacute",0x1EBE}, +{"Ecircumflexbelow",0x1E18}, +{"Ecircumflexdotbelow",0x1EC6}, +{"Ecircumflexgrave",0x1EC0}, +{"Ecircumflexhookabove",0x1EC2}, +{"Ecircumflexsmall",0xF7EA}, +{"Ecircumflextilde",0x1EC4}, +{"Ecyrillic",0x0404}, +{"Edblgrave",0x0204}, +{"Edieresis",0x00CB}, +{"Edieresissmall",0xF7EB}, +{"Edot",0x0116}, +{"Edotaccent",0x0116}, +{"Edotbelow",0x1EB8}, +{"Efcyrillic",0x0424}, +{"Egrave",0x00C8}, +{"Egravesmall",0xF7E8}, +{"Eharmenian",0x0537}, +{"Ehookabove",0x1EBA}, +{"Eightroman",0x2167}, +{"Einvertedbreve",0x0206}, +{"Eiotifiedcyrillic",0x0464}, +{"Elcyrillic",0x041B}, +{"Elevenroman",0x216A}, +{"Emacron",0x0112}, +{"Emacronacute",0x1E16}, +{"Emacrongrave",0x1E14}, +{"Emcyrillic",0x041C}, +{"Emonospace",0xFF25}, +{"Encyrillic",0x041D}, +{"Endescendercyrillic",0x04A2}, +{"Eng",0x014A}, +{"Enghecyrillic",0x04A4}, +{"Enhookcyrillic",0x04C7}, +{"Eogonek",0x0118}, +{"Eopen",0x0190}, +{"Epsilon",0x0395}, +{"Epsilontonos",0x0388}, +{"Ercyrillic",0x0420}, +{"Ereversed",0x018E}, +{"Ereversedcyrillic",0x042D}, +{"Escyrillic",0x0421}, +{"Esdescendercyrillic",0x04AA}, +{"Esh",0x01A9}, +{"Esmall",0xF765}, +{"Eta",0x0397}, +{"Etarmenian",0x0538}, +{"Etatonos",0x0389}, +{"Eth",0x00D0}, +{"Ethsmall",0xF7F0}, +{"Etilde",0x1EBC}, +{"Etildebelow",0x1E1A}, +{"Euro",0x20AC}, +{"Ezh",0x01B7}, +{"Ezhcaron",0x01EE}, +{"Ezhreversed",0x01B8}, +{"F",0x0046}, +{"Fcircle",0x24BB}, +{"Fdotaccent",0x1E1E}, +{"Feharmenian",0x0556}, +{"Feicoptic",0x03E4}, +{"Fhook",0x0191}, +{"Fitacyrillic",0x0472}, +{"Fiveroman",0x2164}, +{"Fmonospace",0xFF26}, +{"Fourroman",0x2163}, +{"Fsmall",0xF766}, +{"G",0x0047}, +{"GBsquare",0x3387}, +{"Gacute",0x01F4}, +{"Gamma",0x0393}, +{"Gammaafrican",0x0194}, +{"Gangiacoptic",0x03EA}, +{"Gbreve",0x011E}, +{"Gcaron",0x01E6}, +{"Gcedilla",0x0122}, +{"Gcircle",0x24BC}, +{"Gcircumflex",0x011C}, +{"Gcommaaccent",0x0122}, +{"Gdot",0x0120}, +{"Gdotaccent",0x0120}, +{"Gecyrillic",0x0413}, +{"Ghadarmenian",0x0542}, +{"Ghemiddlehookcyrillic",0x0494}, +{"Ghestrokecyrillic",0x0492}, +{"Gheupturncyrillic",0x0490}, +{"Ghook",0x0193}, +{"Gimarmenian",0x0533}, +{"Gjecyrillic",0x0403}, +{"Gmacron",0x1E20}, +{"Gmonospace",0xFF27}, +{"Grave",0xF6CE}, +{"Gravesmall",0xF760}, +{"Gsmall",0xF767}, +{"Gsmallhook",0x029B}, +{"Gstroke",0x01E4}, +{"H",0x0048}, +{"H18533",0x25CF}, +{"H18543",0x25AA}, +{"H18551",0x25AB}, +{"H22073",0x25A1}, +{"HPsquare",0x33CB}, +{"Haabkhasiancyrillic",0x04A8}, +{"Hadescendercyrillic",0x04B2}, +{"Hardsigncyrillic",0x042A}, +{"Hbar",0x0126}, +{"Hbrevebelow",0x1E2A}, +{"Hcedilla",0x1E28}, +{"Hcircle",0x24BD}, +{"Hcircumflex",0x0124}, +{"Hdieresis",0x1E26}, +{"Hdotaccent",0x1E22}, +{"Hdotbelow",0x1E24}, +{"Hmonospace",0xFF28}, +{"Hoarmenian",0x0540}, +{"Horicoptic",0x03E8}, +{"Hsmall",0xF768}, +{"Hungarumlaut",0xF6CF}, +{"Hungarumlautsmall",0xF6F8}, +{"Hzsquare",0x3390}, +{"I",0x0049}, +{"IAcyrillic",0x042F}, +{"IJ",0x0132}, +{"IUcyrillic",0x042E}, +{"Iacute",0x00CD}, +{"Iacutesmall",0xF7ED}, +{"Ibreve",0x012C}, +{"Icaron",0x01CF}, +{"Icircle",0x24BE}, +{"Icircumflex",0x00CE}, +{"Icircumflexsmall",0xF7EE}, +{"Icyrillic",0x0406}, +{"Idblgrave",0x0208}, +{"Idieresis",0x00CF}, +{"Idieresisacute",0x1E2E}, +{"Idieresiscyrillic",0x04E4}, +{"Idieresissmall",0xF7EF}, +{"Idot",0x0130}, +{"Idotaccent",0x0130}, +{"Idotbelow",0x1ECA}, +{"Iebrevecyrillic",0x04D6}, +{"Iecyrillic",0x0415}, +{"Ifraktur",0x2111}, +{"Igrave",0x00CC}, +{"Igravesmall",0xF7EC}, +{"Ihookabove",0x1EC8}, +{"Iicyrillic",0x0418}, +{"Iinvertedbreve",0x020A}, +{"Iishortcyrillic",0x0419}, +{"Imacron",0x012A}, +{"Imacroncyrillic",0x04E2}, +{"Imonospace",0xFF29}, +{"Iniarmenian",0x053B}, +{"Iocyrillic",0x0401}, +{"Iogonek",0x012E}, +{"Iota",0x0399}, +{"Iotaafrican",0x0196}, +{"Iotadieresis",0x03AA}, +{"Iotatonos",0x038A}, +{"Ismall",0xF769}, +{"Istroke",0x0197}, +{"Itilde",0x0128}, +{"Itildebelow",0x1E2C}, +{"Izhitsacyrillic",0x0474}, +{"Izhitsadblgravecyrillic",0x0476}, +{"J",0x004A}, +{"Jaarmenian",0x0541}, +{"Jcircle",0x24BF}, +{"Jcircumflex",0x0134}, +{"Jecyrillic",0x0408}, +{"Jheharmenian",0x054B}, +{"Jmonospace",0xFF2A}, +{"Jsmall",0xF76A}, +{"K",0x004B}, +{"KBsquare",0x3385}, +{"KKsquare",0x33CD}, +{"Kabashkircyrillic",0x04A0}, +{"Kacute",0x1E30}, +{"Kacyrillic",0x041A}, +{"Kadescendercyrillic",0x049A}, +{"Kahookcyrillic",0x04C3}, +{"Kappa",0x039A}, +{"Kastrokecyrillic",0x049E}, +{"Kaverticalstrokecyrillic",0x049C}, +{"Kcaron",0x01E8}, +{"Kcedilla",0x0136}, +{"Kcircle",0x24C0}, +{"Kcommaaccent",0x0136}, +{"Kdotbelow",0x1E32}, +{"Keharmenian",0x0554}, +{"Kenarmenian",0x053F}, +{"Khacyrillic",0x0425}, +{"Kheicoptic",0x03E6}, +{"Khook",0x0198}, +{"Kjecyrillic",0x040C}, +{"Klinebelow",0x1E34}, +{"Kmonospace",0xFF2B}, +{"Koppacyrillic",0x0480}, +{"Koppagreek",0x03DE}, +{"Ksicyrillic",0x046E}, +{"Ksmall",0xF76B}, +{"L",0x004C}, +{"LJ",0x01C7}, +{"LL",0xF6BF}, +{"Lacute",0x0139}, +{"Lambda",0x039B}, +{"Lcaron",0x013D}, +{"Lcedilla",0x013B}, +{"Lcircle",0x24C1}, +{"Lcircumflexbelow",0x1E3C}, +{"Lcommaaccent",0x013B}, +{"Ldot",0x013F}, +{"Ldotaccent",0x013F}, +{"Ldotbelow",0x1E36}, +{"Ldotbelowmacron",0x1E38}, +{"Liwnarmenian",0x053C}, +{"Lj",0x01C8}, +{"Ljecyrillic",0x0409}, +{"Llinebelow",0x1E3A}, +{"Lmonospace",0xFF2C}, +{"Lslash",0x0141}, +{"Lslashsmall",0xF6F9}, +{"Lsmall",0xF76C}, +{"M",0x004D}, +{"MBsquare",0x3386}, +{"Macron",0xF6D0}, +{"Macronsmall",0xF7AF}, +{"Macute",0x1E3E}, +{"Mcircle",0x24C2}, +{"Mdotaccent",0x1E40}, +{"Mdotbelow",0x1E42}, +{"Menarmenian",0x0544}, +{"Mmonospace",0xFF2D}, +{"Msmall",0xF76D}, +{"Mturned",0x019C}, +{"Mu",0x039C}, +{"N",0x004E}, +{"NJ",0x01CA}, +{"Nacute",0x0143}, +{"Ncaron",0x0147}, +{"Ncedilla",0x0145}, +{"Ncircle",0x24C3}, +{"Ncircumflexbelow",0x1E4A}, +{"Ncommaaccent",0x0145}, +{"Ndotaccent",0x1E44}, +{"Ndotbelow",0x1E46}, +{"Nhookleft",0x019D}, +{"Nineroman",0x2168}, +{"Nj",0x01CB}, +{"Njecyrillic",0x040A}, +{"Nlinebelow",0x1E48}, +{"Nmonospace",0xFF2E}, +{"Nowarmenian",0x0546}, +{"Nsmall",0xF76E}, +{"Ntilde",0x00D1}, +{"Ntildesmall",0xF7F1}, +{"Nu",0x039D}, +{"O",0x004F}, +{"OE",0x0152}, +{"OEsmall",0xF6FA}, +{"Oacute",0x00D3}, +{"Oacutesmall",0xF7F3}, +{"Obarredcyrillic",0x04E8}, +{"Obarreddieresiscyrillic",0x04EA}, +{"Obreve",0x014E}, +{"Ocaron",0x01D1}, +{"Ocenteredtilde",0x019F}, +{"Ocircle",0x24C4}, +{"Ocircumflex",0x00D4}, +{"Ocircumflexacute",0x1ED0}, +{"Ocircumflexdotbelow",0x1ED8}, +{"Ocircumflexgrave",0x1ED2}, +{"Ocircumflexhookabove",0x1ED4}, +{"Ocircumflexsmall",0xF7F4}, +{"Ocircumflextilde",0x1ED6}, +{"Ocyrillic",0x041E}, +{"Odblacute",0x0150}, +{"Odblgrave",0x020C}, +{"Odieresis",0x00D6}, +{"Odieresiscyrillic",0x04E6}, +{"Odieresissmall",0xF7F6}, +{"Odotbelow",0x1ECC}, +{"Ogoneksmall",0xF6FB}, +{"Ograve",0x00D2}, +{"Ogravesmall",0xF7F2}, +{"Oharmenian",0x0555}, +{"Ohm",0x2126}, +{"Ohookabove",0x1ECE}, +{"Ohorn",0x01A0}, +{"Ohornacute",0x1EDA}, +{"Ohorndotbelow",0x1EE2}, +{"Ohorngrave",0x1EDC}, +{"Ohornhookabove",0x1EDE}, +{"Ohorntilde",0x1EE0}, +{"Ohungarumlaut",0x0150}, +{"Oi",0x01A2}, +{"Oinvertedbreve",0x020E}, +{"Omacron",0x014C}, +{"Omacronacute",0x1E52}, +{"Omacrongrave",0x1E50}, +{"Omega",0x2126}, +{"Omegacyrillic",0x0460}, +{"Omegagreek",0x03A9}, +{"Omegaroundcyrillic",0x047A}, +{"Omegatitlocyrillic",0x047C}, +{"Omegatonos",0x038F}, +{"Omicron",0x039F}, +{"Omicrontonos",0x038C}, +{"Omonospace",0xFF2F}, +{"Oneroman",0x2160}, +{"Oogonek",0x01EA}, +{"Oogonekmacron",0x01EC}, +{"Oopen",0x0186}, +{"Oslash",0x00D8}, +{"Oslashacute",0x01FE}, +{"Oslashsmall",0xF7F8}, +{"Osmall",0xF76F}, +{"Ostrokeacute",0x01FE}, +{"Otcyrillic",0x047E}, +{"Otilde",0x00D5}, +{"Otildeacute",0x1E4C}, +{"Otildedieresis",0x1E4E}, +{"Otildesmall",0xF7F5}, +{"P",0x0050}, +{"Pacute",0x1E54}, +{"Pcircle",0x24C5}, +{"Pdotaccent",0x1E56}, +{"Pecyrillic",0x041F}, +{"Peharmenian",0x054A}, +{"Pemiddlehookcyrillic",0x04A6}, +{"Phi",0x03A6}, +{"Phook",0x01A4}, +{"Pi",0x03A0}, +{"Piwrarmenian",0x0553}, +{"Pmonospace",0xFF30}, +{"Psi",0x03A8}, +{"Psicyrillic",0x0470}, +{"Psmall",0xF770}, +{"Q",0x0051}, +{"Qcircle",0x24C6}, +{"Qmonospace",0xFF31}, +{"Qsmall",0xF771}, +{"R",0x0052}, +{"Raarmenian",0x054C}, +{"Racute",0x0154}, +{"Rcaron",0x0158}, +{"Rcedilla",0x0156}, +{"Rcircle",0x24C7}, +{"Rcommaaccent",0x0156}, +{"Rdblgrave",0x0210}, +{"Rdotaccent",0x1E58}, +{"Rdotbelow",0x1E5A}, +{"Rdotbelowmacron",0x1E5C}, +{"Reharmenian",0x0550}, +{"Rfraktur",0x211C}, +{"Rho",0x03A1}, +{"Ringsmall",0xF6FC}, +{"Rinvertedbreve",0x0212}, +{"Rlinebelow",0x1E5E}, +{"Rmonospace",0xFF32}, +{"Rsmall",0xF772}, +{"Rsmallinverted",0x0281}, +{"Rsmallinvertedsuperior",0x02B6}, +{"S",0x0053}, +{"SF010000",0x250C}, +{"SF020000",0x2514}, +{"SF030000",0x2510}, +{"SF040000",0x2518}, +{"SF050000",0x253C}, +{"SF060000",0x252C}, +{"SF070000",0x2534}, +{"SF080000",0x251C}, +{"SF090000",0x2524}, +{"SF100000",0x2500}, +{"SF110000",0x2502}, +{"SF190000",0x2561}, +{"SF200000",0x2562}, +{"SF210000",0x2556}, +{"SF220000",0x2555}, +{"SF230000",0x2563}, +{"SF240000",0x2551}, +{"SF250000",0x2557}, +{"SF260000",0x255D}, +{"SF270000",0x255C}, +{"SF280000",0x255B}, +{"SF360000",0x255E}, +{"SF370000",0x255F}, +{"SF380000",0x255A}, +{"SF390000",0x2554}, +{"SF400000",0x2569}, +{"SF410000",0x2566}, +{"SF420000",0x2560}, +{"SF430000",0x2550}, +{"SF440000",0x256C}, +{"SF450000",0x2567}, +{"SF460000",0x2568}, +{"SF470000",0x2564}, +{"SF480000",0x2565}, +{"SF490000",0x2559}, +{"SF500000",0x2558}, +{"SF510000",0x2552}, +{"SF520000",0x2553}, +{"SF530000",0x256B}, +{"SF540000",0x256A}, +{"Sacute",0x015A}, +{"Sacutedotaccent",0x1E64}, +{"Sampigreek",0x03E0}, +{"Scaron",0x0160}, +{"Scarondotaccent",0x1E66}, +{"Scaronsmall",0xF6FD}, +{"Scedilla",0x015E}, +{"Schwa",0x018F}, +{"Schwacyrillic",0x04D8}, +{"Schwadieresiscyrillic",0x04DA}, +{"Scircle",0x24C8}, +{"Scircumflex",0x015C}, +{"Scommaaccent",0x0218}, +{"Sdotaccent",0x1E60}, +{"Sdotbelow",0x1E62}, +{"Sdotbelowdotaccent",0x1E68}, +{"Seharmenian",0x054D}, +{"Sevenroman",0x2166}, +{"Shaarmenian",0x0547}, +{"Shacyrillic",0x0428}, +{"Shchacyrillic",0x0429}, +{"Sheicoptic",0x03E2}, +{"Shhacyrillic",0x04BA}, +{"Shimacoptic",0x03EC}, +{"Sigma",0x03A3}, +{"Sixroman",0x2165}, +{"Smonospace",0xFF33}, +{"Softsigncyrillic",0x042C}, +{"Ssmall",0xF773}, +{"Stigmagreek",0x03DA}, +{"T",0x0054}, +{"Tau",0x03A4}, +{"Tbar",0x0166}, +{"Tcaron",0x0164}, +{"Tcedilla",0x0162}, +{"Tcircle",0x24C9}, +{"Tcircumflexbelow",0x1E70}, +{"Tcommaaccent",0x0162}, +{"Tdotaccent",0x1E6A}, +{"Tdotbelow",0x1E6C}, +{"Tecyrillic",0x0422}, +{"Tedescendercyrillic",0x04AC}, +{"Tenroman",0x2169}, +{"Tetsecyrillic",0x04B4}, +{"Theta",0x0398}, +{"Thook",0x01AC}, +{"Thorn",0x00DE}, +{"Thornsmall",0xF7FE}, +{"Threeroman",0x2162}, +{"Tildesmall",0xF6FE}, +{"Tiwnarmenian",0x054F}, +{"Tlinebelow",0x1E6E}, +{"Tmonospace",0xFF34}, +{"Toarmenian",0x0539}, +{"Tonefive",0x01BC}, +{"Tonesix",0x0184}, +{"Tonetwo",0x01A7}, +{"Tretroflexhook",0x01AE}, +{"Tsecyrillic",0x0426}, +{"Tshecyrillic",0x040B}, +{"Tsmall",0xF774}, +{"Twelveroman",0x216B}, +{"Tworoman",0x2161}, +{"U",0x0055}, +{"Uacute",0x00DA}, +{"Uacutesmall",0xF7FA}, +{"Ubreve",0x016C}, +{"Ucaron",0x01D3}, +{"Ucircle",0x24CA}, +{"Ucircumflex",0x00DB}, +{"Ucircumflexbelow",0x1E76}, +{"Ucircumflexsmall",0xF7FB}, +{"Ucyrillic",0x0423}, +{"Udblacute",0x0170}, +{"Udblgrave",0x0214}, +{"Udieresis",0x00DC}, +{"Udieresisacute",0x01D7}, +{"Udieresisbelow",0x1E72}, +{"Udieresiscaron",0x01D9}, +{"Udieresiscyrillic",0x04F0}, +{"Udieresisgrave",0x01DB}, +{"Udieresismacron",0x01D5}, +{"Udieresissmall",0xF7FC}, +{"Udotbelow",0x1EE4}, +{"Ugrave",0x00D9}, +{"Ugravesmall",0xF7F9}, +{"Uhookabove",0x1EE6}, +{"Uhorn",0x01AF}, +{"Uhornacute",0x1EE8}, +{"Uhorndotbelow",0x1EF0}, +{"Uhorngrave",0x1EEA}, +{"Uhornhookabove",0x1EEC}, +{"Uhorntilde",0x1EEE}, +{"Uhungarumlaut",0x0170}, +{"Uhungarumlautcyrillic",0x04F2}, +{"Uinvertedbreve",0x0216}, +{"Ukcyrillic",0x0478}, +{"Umacron",0x016A}, +{"Umacroncyrillic",0x04EE}, +{"Umacrondieresis",0x1E7A}, +{"Umonospace",0xFF35}, +{"Uogonek",0x0172}, +{"Upsilon",0x03A5}, +{"Upsilon1",0x03D2}, +{"Upsilonacutehooksymbolgreek",0x03D3}, +{"Upsilonafrican",0x01B1}, +{"Upsilondieresis",0x03AB}, +{"Upsilondieresishooksymbolgreek",0x03D4}, +{"Upsilonhooksymbol",0x03D2}, +{"Upsilontonos",0x038E}, +{"Uring",0x016E}, +{"Ushortcyrillic",0x040E}, +{"Usmall",0xF775}, +{"Ustraightcyrillic",0x04AE}, +{"Ustraightstrokecyrillic",0x04B0}, +{"Utilde",0x0168}, +{"Utildeacute",0x1E78}, +{"Utildebelow",0x1E74}, +{"V",0x0056}, +{"Vcircle",0x24CB}, +{"Vdotbelow",0x1E7E}, +{"Vecyrillic",0x0412}, +{"Vewarmenian",0x054E}, +{"Vhook",0x01B2}, +{"Vmonospace",0xFF36}, +{"Voarmenian",0x0548}, +{"Vsmall",0xF776}, +{"Vtilde",0x1E7C}, +{"W",0x0057}, +{"Wacute",0x1E82}, +{"Wcircle",0x24CC}, +{"Wcircumflex",0x0174}, +{"Wdieresis",0x1E84}, +{"Wdotaccent",0x1E86}, +{"Wdotbelow",0x1E88}, +{"Wgrave",0x1E80}, +{"Wmonospace",0xFF37}, +{"Wsmall",0xF777}, +{"X",0x0058}, +{"Xcircle",0x24CD}, +{"Xdieresis",0x1E8C}, +{"Xdotaccent",0x1E8A}, +{"Xeharmenian",0x053D}, +{"Xi",0x039E}, +{"Xmonospace",0xFF38}, +{"Xsmall",0xF778}, +{"Y",0x0059}, +{"Yacute",0x00DD}, +{"Yacutesmall",0xF7FD}, +{"Yatcyrillic",0x0462}, +{"Ycircle",0x24CE}, +{"Ycircumflex",0x0176}, +{"Ydieresis",0x0178}, +{"Ydieresissmall",0xF7FF}, +{"Ydotaccent",0x1E8E}, +{"Ydotbelow",0x1EF4}, +{"Yericyrillic",0x042B}, +{"Yerudieresiscyrillic",0x04F8}, +{"Ygrave",0x1EF2}, +{"Yhook",0x01B3}, +{"Yhookabove",0x1EF6}, +{"Yiarmenian",0x0545}, +{"Yicyrillic",0x0407}, +{"Yiwnarmenian",0x0552}, +{"Ymonospace",0xFF39}, +{"Ysmall",0xF779}, +{"Ytilde",0x1EF8}, +{"Yusbigcyrillic",0x046A}, +{"Yusbigiotifiedcyrillic",0x046C}, +{"Yuslittlecyrillic",0x0466}, +{"Yuslittleiotifiedcyrillic",0x0468}, +{"Z",0x005A}, +{"Zaarmenian",0x0536}, +{"Zacute",0x0179}, +{"Zcaron",0x017D}, +{"Zcaronsmall",0xF6FF}, +{"Zcircle",0x24CF}, +{"Zcircumflex",0x1E90}, +{"Zdot",0x017B}, +{"Zdotaccent",0x017B}, +{"Zdotbelow",0x1E92}, +{"Zecyrillic",0x0417}, +{"Zedescendercyrillic",0x0498}, +{"Zedieresiscyrillic",0x04DE}, +{"Zeta",0x0396}, +{"Zhearmenian",0x053A}, +{"Zhebrevecyrillic",0x04C1}, +{"Zhecyrillic",0x0416}, +{"Zhedescendercyrillic",0x0496}, +{"Zhedieresiscyrillic",0x04DC}, +{"Zlinebelow",0x1E94}, +{"Zmonospace",0xFF3A}, +{"Zsmall",0xF77A}, +{"Zstroke",0x01B5}, +{"a",0x0061}, +{"aabengali",0x0986}, +{"aacute",0x00E1}, +{"aadeva",0x0906}, +{"aagujarati",0x0A86}, +{"aagurmukhi",0x0A06}, +{"aamatragurmukhi",0x0A3E}, +{"aarusquare",0x3303}, +{"aavowelsignbengali",0x09BE}, +{"aavowelsigndeva",0x093E}, +{"aavowelsigngujarati",0x0ABE}, +{"abbreviationmarkarmenian",0x055F}, +{"abbreviationsigndeva",0x0970}, +{"abengali",0x0985}, +{"abopomofo",0x311A}, +{"abreve",0x0103}, +{"abreveacute",0x1EAF}, +{"abrevecyrillic",0x04D1}, +{"abrevedotbelow",0x1EB7}, +{"abrevegrave",0x1EB1}, +{"abrevehookabove",0x1EB3}, +{"abrevetilde",0x1EB5}, +{"acaron",0x01CE}, +{"acircle",0x24D0}, +{"acircumflex",0x00E2}, +{"acircumflexacute",0x1EA5}, +{"acircumflexdotbelow",0x1EAD}, +{"acircumflexgrave",0x1EA7}, +{"acircumflexhookabove",0x1EA9}, +{"acircumflextilde",0x1EAB}, +{"acute",0x00B4}, +{"acutebelowcmb",0x0317}, +{"acutecmb",0x0301}, +{"acutecomb",0x0301}, +{"acutedeva",0x0954}, +{"acutelowmod",0x02CF}, +{"acutetonecmb",0x0341}, +{"acyrillic",0x0430}, +{"adblgrave",0x0201}, +{"addakgurmukhi",0x0A71}, +{"adeva",0x0905}, +{"adieresis",0x00E4}, +{"adieresiscyrillic",0x04D3}, +{"adieresismacron",0x01DF}, +{"adotbelow",0x1EA1}, +{"adotmacron",0x01E1}, +{"ae",0x00E6}, +{"aeacute",0x01FD}, +{"aekorean",0x3150}, +{"aemacron",0x01E3}, +{"afii00208",0x2015}, +{"afii08941",0x20A4}, +{"afii10017",0x0410}, +{"afii10018",0x0411}, +{"afii10019",0x0412}, +{"afii10020",0x0413}, +{"afii10021",0x0414}, +{"afii10022",0x0415}, +{"afii10023",0x0401}, +{"afii10024",0x0416}, +{"afii10025",0x0417}, +{"afii10026",0x0418}, +{"afii10027",0x0419}, +{"afii10028",0x041A}, +{"afii10029",0x041B}, +{"afii10030",0x041C}, +{"afii10031",0x041D}, +{"afii10032",0x041E}, +{"afii10033",0x041F}, +{"afii10034",0x0420}, +{"afii10035",0x0421}, +{"afii10036",0x0422}, +{"afii10037",0x0423}, +{"afii10038",0x0424}, +{"afii10039",0x0425}, +{"afii10040",0x0426}, +{"afii10041",0x0427}, +{"afii10042",0x0428}, +{"afii10043",0x0429}, +{"afii10044",0x042A}, +{"afii10045",0x042B}, +{"afii10046",0x042C}, +{"afii10047",0x042D}, +{"afii10048",0x042E}, +{"afii10049",0x042F}, +{"afii10050",0x0490}, +{"afii10051",0x0402}, +{"afii10052",0x0403}, +{"afii10053",0x0404}, +{"afii10054",0x0405}, +{"afii10055",0x0406}, +{"afii10056",0x0407}, +{"afii10057",0x0408}, +{"afii10058",0x0409}, +{"afii10059",0x040A}, +{"afii10060",0x040B}, +{"afii10061",0x040C}, +{"afii10062",0x040E}, +{"afii10063",0xF6C4}, +{"afii10064",0xF6C5}, +{"afii10065",0x0430}, +{"afii10066",0x0431}, +{"afii10067",0x0432}, +{"afii10068",0x0433}, +{"afii10069",0x0434}, +{"afii10070",0x0435}, +{"afii10071",0x0451}, +{"afii10072",0x0436}, +{"afii10073",0x0437}, +{"afii10074",0x0438}, +{"afii10075",0x0439}, +{"afii10076",0x043A}, +{"afii10077",0x043B}, +{"afii10078",0x043C}, +{"afii10079",0x043D}, +{"afii10080",0x043E}, +{"afii10081",0x043F}, +{"afii10082",0x0440}, +{"afii10083",0x0441}, +{"afii10084",0x0442}, +{"afii10085",0x0443}, +{"afii10086",0x0444}, +{"afii10087",0x0445}, +{"afii10088",0x0446}, +{"afii10089",0x0447}, +{"afii10090",0x0448}, +{"afii10091",0x0449}, +{"afii10092",0x044A}, +{"afii10093",0x044B}, +{"afii10094",0x044C}, +{"afii10095",0x044D}, +{"afii10096",0x044E}, +{"afii10097",0x044F}, +{"afii10098",0x0491}, +{"afii10099",0x0452}, +{"afii10100",0x0453}, +{"afii10101",0x0454}, +{"afii10102",0x0455}, +{"afii10103",0x0456}, +{"afii10104",0x0457}, +{"afii10105",0x0458}, +{"afii10106",0x0459}, +{"afii10107",0x045A}, +{"afii10108",0x045B}, +{"afii10109",0x045C}, +{"afii10110",0x045E}, +{"afii10145",0x040F}, +{"afii10146",0x0462}, +{"afii10147",0x0472}, +{"afii10148",0x0474}, +{"afii10192",0xF6C6}, +{"afii10193",0x045F}, +{"afii10194",0x0463}, +{"afii10195",0x0473}, +{"afii10196",0x0475}, +{"afii10831",0xF6C7}, +{"afii10832",0xF6C8}, +{"afii10846",0x04D9}, +{"afii299",0x200E}, +{"afii300",0x200F}, +{"afii301",0x200D}, +{"afii57381",0x066A}, +{"afii57388",0x060C}, +{"afii57392",0x0660}, +{"afii57393",0x0661}, +{"afii57394",0x0662}, +{"afii57395",0x0663}, +{"afii57396",0x0664}, +{"afii57397",0x0665}, +{"afii57398",0x0666}, +{"afii57399",0x0667}, +{"afii57400",0x0668}, +{"afii57401",0x0669}, +{"afii57403",0x061B}, +{"afii57407",0x061F}, +{"afii57409",0x0621}, +{"afii57410",0x0622}, +{"afii57411",0x0623}, +{"afii57412",0x0624}, +{"afii57413",0x0625}, +{"afii57414",0x0626}, +{"afii57415",0x0627}, +{"afii57416",0x0628}, +{"afii57417",0x0629}, +{"afii57418",0x062A}, +{"afii57419",0x062B}, +{"afii57420",0x062C}, +{"afii57421",0x062D}, +{"afii57422",0x062E}, +{"afii57423",0x062F}, +{"afii57424",0x0630}, +{"afii57425",0x0631}, +{"afii57426",0x0632}, +{"afii57427",0x0633}, +{"afii57428",0x0634}, +{"afii57429",0x0635}, +{"afii57430",0x0636}, +{"afii57431",0x0637}, +{"afii57432",0x0638}, +{"afii57433",0x0639}, +{"afii57434",0x063A}, +{"afii57440",0x0640}, +{"afii57441",0x0641}, +{"afii57442",0x0642}, +{"afii57443",0x0643}, +{"afii57444",0x0644}, +{"afii57445",0x0645}, +{"afii57446",0x0646}, +{"afii57448",0x0648}, +{"afii57449",0x0649}, +{"afii57450",0x064A}, +{"afii57451",0x064B}, +{"afii57452",0x064C}, +{"afii57453",0x064D}, +{"afii57454",0x064E}, +{"afii57455",0x064F}, +{"afii57456",0x0650}, +{"afii57457",0x0651}, +{"afii57458",0x0652}, +{"afii57470",0x0647}, +{"afii57505",0x06A4}, +{"afii57506",0x067E}, +{"afii57507",0x0686}, +{"afii57508",0x0698}, +{"afii57509",0x06AF}, +{"afii57511",0x0679}, +{"afii57512",0x0688}, +{"afii57513",0x0691}, +{"afii57514",0x06BA}, +{"afii57519",0x06D2}, +{"afii57534",0x06D5}, +{"afii57636",0x20AA}, +{"afii57645",0x05BE}, +{"afii57658",0x05C3}, +{"afii57664",0x05D0}, +{"afii57665",0x05D1}, +{"afii57666",0x05D2}, +{"afii57667",0x05D3}, +{"afii57668",0x05D4}, +{"afii57669",0x05D5}, +{"afii57670",0x05D6}, +{"afii57671",0x05D7}, +{"afii57672",0x05D8}, +{"afii57673",0x05D9}, +{"afii57674",0x05DA}, +{"afii57675",0x05DB}, +{"afii57676",0x05DC}, +{"afii57677",0x05DD}, +{"afii57678",0x05DE}, +{"afii57679",0x05DF}, +{"afii57680",0x05E0}, +{"afii57681",0x05E1}, +{"afii57682",0x05E2}, +{"afii57683",0x05E3}, +{"afii57684",0x05E4}, +{"afii57685",0x05E5}, +{"afii57686",0x05E6}, +{"afii57687",0x05E7}, +{"afii57688",0x05E8}, +{"afii57689",0x05E9}, +{"afii57690",0x05EA}, +{"afii57694",0xFB2A}, +{"afii57695",0xFB2B}, +{"afii57700",0xFB4B}, +{"afii57705",0xFB1F}, +{"afii57716",0x05F0}, +{"afii57717",0x05F1}, +{"afii57718",0x05F2}, +{"afii57723",0xFB35}, +{"afii57793",0x05B4}, +{"afii57794",0x05B5}, +{"afii57795",0x05B6}, +{"afii57796",0x05BB}, +{"afii57797",0x05B8}, +{"afii57798",0x05B7}, +{"afii57799",0x05B0}, +{"afii57800",0x05B2}, +{"afii57801",0x05B1}, +{"afii57802",0x05B3}, +{"afii57803",0x05C2}, +{"afii57804",0x05C1}, +{"afii57806",0x05B9}, +{"afii57807",0x05BC}, +{"afii57839",0x05BD}, +{"afii57841",0x05BF}, +{"afii57842",0x05C0}, +{"afii57929",0x02BC}, +{"afii61248",0x2105}, +{"afii61289",0x2113}, +{"afii61352",0x2116}, +{"afii61573",0x202C}, +{"afii61574",0x202D}, +{"afii61575",0x202E}, +{"afii61664",0x200C}, +{"afii63167",0x066D}, +{"afii64937",0x02BD}, +{"agrave",0x00E0}, +{"agujarati",0x0A85}, +{"agurmukhi",0x0A05}, +{"ahiragana",0x3042}, +{"ahookabove",0x1EA3}, +{"aibengali",0x0990}, +{"aibopomofo",0x311E}, +{"aideva",0x0910}, +{"aiecyrillic",0x04D5}, +{"aigujarati",0x0A90}, +{"aigurmukhi",0x0A10}, +{"aimatragurmukhi",0x0A48}, +{"ainarabic",0x0639}, +{"ainfinalarabic",0xFECA}, +{"aininitialarabic",0xFECB}, +{"ainmedialarabic",0xFECC}, +{"ainvertedbreve",0x0203}, +{"aivowelsignbengali",0x09C8}, +{"aivowelsigndeva",0x0948}, +{"aivowelsigngujarati",0x0AC8}, +{"akatakana",0x30A2}, +{"akatakanahalfwidth",0xFF71}, +{"akorean",0x314F}, +{"alef",0x05D0}, +{"alefarabic",0x0627}, +{"alefdageshhebrew",0xFB30}, +{"aleffinalarabic",0xFE8E}, +{"alefhamzaabovearabic",0x0623}, +{"alefhamzaabovefinalarabic",0xFE84}, +{"alefhamzabelowarabic",0x0625}, +{"alefhamzabelowfinalarabic",0xFE88}, +{"alefhebrew",0x05D0}, +{"aleflamedhebrew",0xFB4F}, +{"alefmaddaabovearabic",0x0622}, +{"alefmaddaabovefinalarabic",0xFE82}, +{"alefmaksuraarabic",0x0649}, +{"alefmaksurafinalarabic",0xFEF0}, +{"alefmaksurainitialarabic",0xFEF3}, +{"alefmaksuramedialarabic",0xFEF4}, +{"alefpatahhebrew",0xFB2E}, +{"alefqamatshebrew",0xFB2F}, +{"aleph",0x2135}, +{"allequal",0x224C}, +{"alpha",0x03B1}, +{"alphatonos",0x03AC}, +{"amacron",0x0101}, +{"amonospace",0xFF41}, +{"ampersand",0x0026}, +{"ampersandmonospace",0xFF06}, +{"ampersandsmall",0xF726}, +{"amsquare",0x33C2}, +{"anbopomofo",0x3122}, +{"angbopomofo",0x3124}, +{"angkhankhuthai",0x0E5A}, +{"angle",0x2220}, +{"anglebracketleft",0x3008}, +{"anglebracketleftvertical",0xFE3F}, +{"anglebracketright",0x3009}, +{"anglebracketrightvertical",0xFE40}, +{"angleleft",0x2329}, +{"angleright",0x232A}, +{"angstrom",0x212B}, +{"anoteleia",0x0387}, +{"anudattadeva",0x0952}, +{"anusvarabengali",0x0982}, +{"anusvaradeva",0x0902}, +{"anusvaragujarati",0x0A82}, +{"aogonek",0x0105}, +{"apaatosquare",0x3300}, +{"aparen",0x249C}, +{"apostrophearmenian",0x055A}, +{"apostrophemod",0x02BC}, +{"apple",0xF8FF}, +{"approaches",0x2250}, +{"approxequal",0x2248}, +{"approxequalorimage",0x2252}, +{"approximatelyequal",0x2245}, +{"araeaekorean",0x318E}, +{"araeakorean",0x318D}, +{"arc",0x2312}, +{"arighthalfring",0x1E9A}, +{"aring",0x00E5}, +{"aringacute",0x01FB}, +{"aringbelow",0x1E01}, +{"arrowboth",0x2194}, +{"arrowdashdown",0x21E3}, +{"arrowdashleft",0x21E0}, +{"arrowdashright",0x21E2}, +{"arrowdashup",0x21E1}, +{"arrowdblboth",0x21D4}, +{"arrowdbldown",0x21D3}, +{"arrowdblleft",0x21D0}, +{"arrowdblright",0x21D2}, +{"arrowdblup",0x21D1}, +{"arrowdown",0x2193}, +{"arrowdownleft",0x2199}, +{"arrowdownright",0x2198}, +{"arrowdownwhite",0x21E9}, +{"arrowheaddownmod",0x02C5}, +{"arrowheadleftmod",0x02C2}, +{"arrowheadrightmod",0x02C3}, +{"arrowheadupmod",0x02C4}, +{"arrowhorizex",0xF8E7}, +{"arrowleft",0x2190}, +{"arrowleftdbl",0x21D0}, +{"arrowleftdblstroke",0x21CD}, +{"arrowleftoverright",0x21C6}, +{"arrowleftwhite",0x21E6}, +{"arrowright",0x2192}, +{"arrowrightdblstroke",0x21CF}, +{"arrowrightheavy",0x279E}, +{"arrowrightoverleft",0x21C4}, +{"arrowrightwhite",0x21E8}, +{"arrowtableft",0x21E4}, +{"arrowtabright",0x21E5}, +{"arrowup",0x2191}, +{"arrowupdn",0x2195}, +{"arrowupdnbse",0x21A8}, +{"arrowupdownbase",0x21A8}, +{"arrowupleft",0x2196}, +{"arrowupleftofdown",0x21C5}, +{"arrowupright",0x2197}, +{"arrowupwhite",0x21E7}, +{"arrowvertex",0xF8E6}, +{"asciicircum",0x005E}, +{"asciicircummonospace",0xFF3E}, +{"asciitilde",0x007E}, +{"asciitildemonospace",0xFF5E}, +{"ascript",0x0251}, +{"ascriptturned",0x0252}, +{"asmallhiragana",0x3041}, +{"asmallkatakana",0x30A1}, +{"asmallkatakanahalfwidth",0xFF67}, +{"asterisk",0x002A}, +{"asteriskaltonearabic",0x066D}, +{"asteriskarabic",0x066D}, +{"asteriskmath",0x2217}, +{"asteriskmonospace",0xFF0A}, +{"asterisksmall",0xFE61}, +{"asterism",0x2042}, +{"asuperior",0xF6E9}, +{"asymptoticallyequal",0x2243}, +{"at",0x0040}, +{"atilde",0x00E3}, +{"atmonospace",0xFF20}, +{"atsmall",0xFE6B}, +{"aturned",0x0250}, +{"aubengali",0x0994}, +{"aubopomofo",0x3120}, +{"audeva",0x0914}, +{"augujarati",0x0A94}, +{"augurmukhi",0x0A14}, +{"aulengthmarkbengali",0x09D7}, +{"aumatragurmukhi",0x0A4C}, +{"auvowelsignbengali",0x09CC}, +{"auvowelsigndeva",0x094C}, +{"auvowelsigngujarati",0x0ACC}, +{"avagrahadeva",0x093D}, +{"aybarmenian",0x0561}, +{"ayin",0x05E2}, +{"ayinaltonehebrew",0xFB20}, +{"ayinhebrew",0x05E2}, +{"b",0x0062}, +{"babengali",0x09AC}, +{"backslash",0x005C}, +{"backslashmonospace",0xFF3C}, +{"badeva",0x092C}, +{"bagujarati",0x0AAC}, +{"bagurmukhi",0x0A2C}, +{"bahiragana",0x3070}, +{"bahtthai",0x0E3F}, +{"bakatakana",0x30D0}, +{"bar",0x007C}, +{"barmonospace",0xFF5C}, +{"bbopomofo",0x3105}, +{"bcircle",0x24D1}, +{"bdotaccent",0x1E03}, +{"bdotbelow",0x1E05}, +{"beamedsixteenthnotes",0x266C}, +{"because",0x2235}, +{"becyrillic",0x0431}, +{"beharabic",0x0628}, +{"behfinalarabic",0xFE90}, +{"behinitialarabic",0xFE91}, +{"behiragana",0x3079}, +{"behmedialarabic",0xFE92}, +{"behmeeminitialarabic",0xFC9F}, +{"behmeemisolatedarabic",0xFC08}, +{"behnoonfinalarabic",0xFC6D}, +{"bekatakana",0x30D9}, +{"benarmenian",0x0562}, +{"bet",0x05D1}, +{"beta",0x03B2}, +{"betasymbolgreek",0x03D0}, +{"betdagesh",0xFB31}, +{"betdageshhebrew",0xFB31}, +{"bethebrew",0x05D1}, +{"betrafehebrew",0xFB4C}, +{"bhabengali",0x09AD}, +{"bhadeva",0x092D}, +{"bhagujarati",0x0AAD}, +{"bhagurmukhi",0x0A2D}, +{"bhook",0x0253}, +{"bihiragana",0x3073}, +{"bikatakana",0x30D3}, +{"bilabialclick",0x0298}, +{"bindigurmukhi",0x0A02}, +{"birusquare",0x3331}, +{"blackcircle",0x25CF}, +{"blackdiamond",0x25C6}, +{"blackdownpointingtriangle",0x25BC}, +{"blackleftpointingpointer",0x25C4}, +{"blackleftpointingtriangle",0x25C0}, +{"blacklenticularbracketleft",0x3010}, +{"blacklenticularbracketleftvertical",0xFE3B}, +{"blacklenticularbracketright",0x3011}, +{"blacklenticularbracketrightvertical",0xFE3C}, +{"blacklowerlefttriangle",0x25E3}, +{"blacklowerrighttriangle",0x25E2}, +{"blackrectangle",0x25AC}, +{"blackrightpointingpointer",0x25BA}, +{"blackrightpointingtriangle",0x25B6}, +{"blacksmallsquare",0x25AA}, +{"blacksmilingface",0x263B}, +{"blacksquare",0x25A0}, +{"blackstar",0x2605}, +{"blackupperlefttriangle",0x25E4}, +{"blackupperrighttriangle",0x25E5}, +{"blackuppointingsmalltriangle",0x25B4}, +{"blackuppointingtriangle",0x25B2}, +{"blank",0x2423}, +{"blinebelow",0x1E07}, +{"block",0x2588}, +{"bmonospace",0xFF42}, +{"bobaimaithai",0x0E1A}, +{"bohiragana",0x307C}, +{"bokatakana",0x30DC}, +{"bparen",0x249D}, +{"bqsquare",0x33C3}, +{"braceex",0xF8F4}, +{"braceleft",0x007B}, +{"braceleftbt",0xF8F3}, +{"braceleftmid",0xF8F2}, +{"braceleftmonospace",0xFF5B}, +{"braceleftsmall",0xFE5B}, +{"bracelefttp",0xF8F1}, +{"braceleftvertical",0xFE37}, +{"braceright",0x007D}, +{"bracerightbt",0xF8FE}, +{"bracerightmid",0xF8FD}, +{"bracerightmonospace",0xFF5D}, +{"bracerightsmall",0xFE5C}, +{"bracerighttp",0xF8FC}, +{"bracerightvertical",0xFE38}, +{"bracketleft",0x005B}, +{"bracketleftbt",0xF8F0}, +{"bracketleftex",0xF8EF}, +{"bracketleftmonospace",0xFF3B}, +{"bracketlefttp",0xF8EE}, +{"bracketright",0x005D}, +{"bracketrightbt",0xF8FB}, +{"bracketrightex",0xF8FA}, +{"bracketrightmonospace",0xFF3D}, +{"bracketrighttp",0xF8F9}, +{"breve",0x02D8}, +{"brevebelowcmb",0x032E}, +{"brevecmb",0x0306}, +{"breveinvertedbelowcmb",0x032F}, +{"breveinvertedcmb",0x0311}, +{"breveinverteddoublecmb",0x0361}, +{"bridgebelowcmb",0x032A}, +{"bridgeinvertedbelowcmb",0x033A}, +{"brokenbar",0x00A6}, +{"bstroke",0x0180}, +{"bsuperior",0xF6EA}, +{"btopbar",0x0183}, +{"buhiragana",0x3076}, +{"bukatakana",0x30D6}, +{"bullet",0x2022}, +{"bulletinverse",0x25D8}, +{"bulletoperator",0x2219}, +{"bullseye",0x25CE}, +{"c",0x0063}, +{"caarmenian",0x056E}, +{"cabengali",0x099A}, +{"cacute",0x0107}, +{"cadeva",0x091A}, +{"cagujarati",0x0A9A}, +{"cagurmukhi",0x0A1A}, +{"calsquare",0x3388}, +{"candrabindubengali",0x0981}, +{"candrabinducmb",0x0310}, +{"candrabindudeva",0x0901}, +{"candrabindugujarati",0x0A81}, +{"capslock",0x21EA}, +{"careof",0x2105}, +{"caron",0x02C7}, +{"caronbelowcmb",0x032C}, +{"caroncmb",0x030C}, +{"carriagereturn",0x21B5}, +{"cbopomofo",0x3118}, +{"ccaron",0x010D}, +{"ccedilla",0x00E7}, +{"ccedillaacute",0x1E09}, +{"ccircle",0x24D2}, +{"ccircumflex",0x0109}, +{"ccurl",0x0255}, +{"cdot",0x010B}, +{"cdotaccent",0x010B}, +{"cdsquare",0x33C5}, +{"cedilla",0x00B8}, +{"cedillacmb",0x0327}, +{"cent",0x00A2}, +{"centigrade",0x2103}, +{"centinferior",0xF6DF}, +{"centmonospace",0xFFE0}, +{"centoldstyle",0xF7A2}, +{"centsuperior",0xF6E0}, +{"chaarmenian",0x0579}, +{"chabengali",0x099B}, +{"chadeva",0x091B}, +{"chagujarati",0x0A9B}, +{"chagurmukhi",0x0A1B}, +{"chbopomofo",0x3114}, +{"cheabkhasiancyrillic",0x04BD}, +{"checkmark",0x2713}, +{"checyrillic",0x0447}, +{"chedescenderabkhasiancyrillic",0x04BF}, +{"chedescendercyrillic",0x04B7}, +{"chedieresiscyrillic",0x04F5}, +{"cheharmenian",0x0573}, +{"chekhakassiancyrillic",0x04CC}, +{"cheverticalstrokecyrillic",0x04B9}, +{"chi",0x03C7}, +{"chieuchacirclekorean",0x3277}, +{"chieuchaparenkorean",0x3217}, +{"chieuchcirclekorean",0x3269}, +{"chieuchkorean",0x314A}, +{"chieuchparenkorean",0x3209}, +{"chochangthai",0x0E0A}, +{"chochanthai",0x0E08}, +{"chochingthai",0x0E09}, +{"chochoethai",0x0E0C}, +{"chook",0x0188}, +{"cieucacirclekorean",0x3276}, +{"cieucaparenkorean",0x3216}, +{"cieuccirclekorean",0x3268}, +{"cieuckorean",0x3148}, +{"cieucparenkorean",0x3208}, +{"cieucuparenkorean",0x321C}, +{"circle",0x25CB}, +{"circlemultiply",0x2297}, +{"circleot",0x2299}, +{"circleplus",0x2295}, +{"circlepostalmark",0x3036}, +{"circlewithlefthalfblack",0x25D0}, +{"circlewithrighthalfblack",0x25D1}, +{"circumflex",0x02C6}, +{"circumflexbelowcmb",0x032D}, +{"circumflexcmb",0x0302}, +{"clear",0x2327}, +{"clickalveolar",0x01C2}, +{"clickdental",0x01C0}, +{"clicklateral",0x01C1}, +{"clickretroflex",0x01C3}, +{"club",0x2663}, +{"clubsuitblack",0x2663}, +{"clubsuitwhite",0x2667}, +{"cmcubedsquare",0x33A4}, +{"cmonospace",0xFF43}, +{"cmsquaredsquare",0x33A0}, +{"coarmenian",0x0581}, +{"colon",0x003A}, +{"colonmonetary",0x20A1}, +{"colonmonospace",0xFF1A}, +{"colonsign",0x20A1}, +{"colonsmall",0xFE55}, +{"colontriangularhalfmod",0x02D1}, +{"colontriangularmod",0x02D0}, +{"comma",0x002C}, +{"commaabovecmb",0x0313}, +{"commaaboverightcmb",0x0315}, +{"commaaccent",0xF6C3}, +{"commaarabic",0x060C}, +{"commaarmenian",0x055D}, +{"commainferior",0xF6E1}, +{"commamonospace",0xFF0C}, +{"commareversedabovecmb",0x0314}, +{"commareversedmod",0x02BD}, +{"commasmall",0xFE50}, +{"commasuperior",0xF6E2}, +{"commaturnedabovecmb",0x0312}, +{"commaturnedmod",0x02BB}, +{"compass",0x263C}, +{"congruent",0x2245}, +{"contourintegral",0x222E}, +{"control",0x2303}, +{"controlACK",0x0006}, +{"controlBEL",0x0007}, +{"controlBS",0x0008}, +{"controlCAN",0x0018}, +{"controlCR",0x000D}, +{"controlDC1",0x0011}, +{"controlDC2",0x0012}, +{"controlDC3",0x0013}, +{"controlDC4",0x0014}, +{"controlDEL",0x007F}, +{"controlDLE",0x0010}, +{"controlEM",0x0019}, +{"controlENQ",0x0005}, +{"controlEOT",0x0004}, +{"controlESC",0x001B}, +{"controlETB",0x0017}, +{"controlETX",0x0003}, +{"controlFF",0x000C}, +{"controlFS",0x001C}, +{"controlGS",0x001D}, +{"controlHT",0x0009}, +{"controlLF",0x000A}, +{"controlNAK",0x0015}, +{"controlRS",0x001E}, +{"controlSI",0x000F}, +{"controlSO",0x000E}, +{"controlSOT",0x0002}, +{"controlSTX",0x0001}, +{"controlSUB",0x001A}, +{"controlSYN",0x0016}, +{"controlUS",0x001F}, +{"controlVT",0x000B}, +{"copyright",0x00A9}, +{"copyrightsans",0xF8E9}, +{"copyrightserif",0xF6D9}, +{"cornerbracketleft",0x300C}, +{"cornerbracketlefthalfwidth",0xFF62}, +{"cornerbracketleftvertical",0xFE41}, +{"cornerbracketright",0x300D}, +{"cornerbracketrighthalfwidth",0xFF63}, +{"cornerbracketrightvertical",0xFE42}, +{"corporationsquare",0x337F}, +{"cosquare",0x33C7}, +{"coverkgsquare",0x33C6}, +{"cparen",0x249E}, +{"cruzeiro",0x20A2}, +{"cstretched",0x0297}, +{"curlyand",0x22CF}, +{"curlyor",0x22CE}, +{"currency",0x00A4}, +{"cyrBreve",0xF6D1}, +{"cyrFlex",0xF6D2}, +{"cyrbreve",0xF6D4}, +{"cyrflex",0xF6D5}, +{"d",0x0064}, +{"daarmenian",0x0564}, +{"dabengali",0x09A6}, +{"dadarabic",0x0636}, +{"dadeva",0x0926}, +{"dadfinalarabic",0xFEBE}, +{"dadinitialarabic",0xFEBF}, +{"dadmedialarabic",0xFEC0}, +{"dagesh",0x05BC}, +{"dageshhebrew",0x05BC}, +{"dagger",0x2020}, +{"daggerdbl",0x2021}, +{"dagujarati",0x0AA6}, +{"dagurmukhi",0x0A26}, +{"dahiragana",0x3060}, +{"dakatakana",0x30C0}, +{"dalarabic",0x062F}, +{"dalet",0x05D3}, +{"daletdagesh",0xFB33}, +{"daletdageshhebrew",0xFB33}, +{"dalethebrew",0x05D3}, +{"dalfinalarabic",0xFEAA}, +{"dammaarabic",0x064F}, +{"dammalowarabic",0x064F}, +{"dammatanaltonearabic",0x064C}, +{"dammatanarabic",0x064C}, +{"danda",0x0964}, +{"dargahebrew",0x05A7}, +{"dargalefthebrew",0x05A7}, +{"dasiapneumatacyrilliccmb",0x0485}, +{"dblGrave",0xF6D3}, +{"dblanglebracketleft",0x300A}, +{"dblanglebracketleftvertical",0xFE3D}, +{"dblanglebracketright",0x300B}, +{"dblanglebracketrightvertical",0xFE3E}, +{"dblarchinvertedbelowcmb",0x032B}, +{"dblarrowleft",0x21D4}, +{"dblarrowright",0x21D2}, +{"dbldanda",0x0965}, +{"dblgrave",0xF6D6}, +{"dblgravecmb",0x030F}, +{"dblintegral",0x222C}, +{"dbllowline",0x2017}, +{"dbllowlinecmb",0x0333}, +{"dbloverlinecmb",0x033F}, +{"dblprimemod",0x02BA}, +{"dblverticalbar",0x2016}, +{"dblverticallineabovecmb",0x030E}, +{"dbopomofo",0x3109}, +{"dbsquare",0x33C8}, +{"dcaron",0x010F}, +{"dcedilla",0x1E11}, +{"dcircle",0x24D3}, +{"dcircumflexbelow",0x1E13}, +{"dcroat",0x0111}, +{"ddabengali",0x09A1}, +{"ddadeva",0x0921}, +{"ddagujarati",0x0AA1}, +{"ddagurmukhi",0x0A21}, +{"ddalarabic",0x0688}, +{"ddalfinalarabic",0xFB89}, +{"dddhadeva",0x095C}, +{"ddhabengali",0x09A2}, +{"ddhadeva",0x0922}, +{"ddhagujarati",0x0AA2}, +{"ddhagurmukhi",0x0A22}, +{"ddotaccent",0x1E0B}, +{"ddotbelow",0x1E0D}, +{"decimalseparatorarabic",0x066B}, +{"decimalseparatorpersian",0x066B}, +{"decyrillic",0x0434}, +{"degree",0x00B0}, +{"dehihebrew",0x05AD}, +{"dehiragana",0x3067}, +{"deicoptic",0x03EF}, +{"dekatakana",0x30C7}, +{"deleteleft",0x232B}, +{"deleteright",0x2326}, +{"delta",0x03B4}, +{"deltaturned",0x018D}, +{"denominatorminusonenumeratorbengali",0x09F8}, +{"dezh",0x02A4}, +{"dhabengali",0x09A7}, +{"dhadeva",0x0927}, +{"dhagujarati",0x0AA7}, +{"dhagurmukhi",0x0A27}, +{"dhook",0x0257}, +{"dialytikatonos",0x0385}, +{"dialytikatonoscmb",0x0344}, +{"diamond",0x2666}, +{"diamondsuitwhite",0x2662}, +{"dieresis",0x00A8}, +{"dieresisacute",0xF6D7}, +{"dieresisbelowcmb",0x0324}, +{"dieresiscmb",0x0308}, +{"dieresisgrave",0xF6D8}, +{"dieresistonos",0x0385}, +{"dihiragana",0x3062}, +{"dikatakana",0x30C2}, +{"dittomark",0x3003}, +{"divide",0x00F7}, +{"divides",0x2223}, +{"divisionslash",0x2215}, +{"djecyrillic",0x0452}, +{"dkshade",0x2593}, +{"dlinebelow",0x1E0F}, +{"dlsquare",0x3397}, +{"dmacron",0x0111}, +{"dmonospace",0xFF44}, +{"dnblock",0x2584}, +{"dochadathai",0x0E0E}, +{"dodekthai",0x0E14}, +{"dohiragana",0x3069}, +{"dokatakana",0x30C9}, +{"dollar",0x0024}, +{"dollarinferior",0xF6E3}, +{"dollarmonospace",0xFF04}, +{"dollaroldstyle",0xF724}, +{"dollarsmall",0xFE69}, +{"dollarsuperior",0xF6E4}, +{"dong",0x20AB}, +{"dorusquare",0x3326}, +{"dotaccent",0x02D9}, +{"dotaccentcmb",0x0307}, +{"dotbelowcmb",0x0323}, +{"dotbelowcomb",0x0323}, +{"dotkatakana",0x30FB}, +{"dotlessi",0x0131}, +{"dotlessj",0xF6BE}, +{"dotlessjstrokehook",0x0284}, +{"dotmath",0x22C5}, +{"dottedcircle",0x25CC}, +{"doubleyodpatah",0xFB1F}, +{"doubleyodpatahhebrew",0xFB1F}, +{"downtackbelowcmb",0x031E}, +{"downtackmod",0x02D5}, +{"dparen",0x249F}, +{"dsuperior",0xF6EB}, +{"dtail",0x0256}, +{"dtopbar",0x018C}, +{"duhiragana",0x3065}, +{"dukatakana",0x30C5}, +{"dz",0x01F3}, +{"dzaltone",0x02A3}, +{"dzcaron",0x01C6}, +{"dzcurl",0x02A5}, +{"dzeabkhasiancyrillic",0x04E1}, +{"dzecyrillic",0x0455}, +{"dzhecyrillic",0x045F}, +{"e",0x0065}, +{"eacute",0x00E9}, +{"earth",0x2641}, +{"ebengali",0x098F}, +{"ebopomofo",0x311C}, +{"ebreve",0x0115}, +{"ecandradeva",0x090D}, +{"ecandragujarati",0x0A8D}, +{"ecandravowelsigndeva",0x0945}, +{"ecandravowelsigngujarati",0x0AC5}, +{"ecaron",0x011B}, +{"ecedillabreve",0x1E1D}, +{"echarmenian",0x0565}, +{"echyiwnarmenian",0x0587}, +{"ecircle",0x24D4}, +{"ecircumflex",0x00EA}, +{"ecircumflexacute",0x1EBF}, +{"ecircumflexbelow",0x1E19}, +{"ecircumflexdotbelow",0x1EC7}, +{"ecircumflexgrave",0x1EC1}, +{"ecircumflexhookabove",0x1EC3}, +{"ecircumflextilde",0x1EC5}, +{"ecyrillic",0x0454}, +{"edblgrave",0x0205}, +{"edeva",0x090F}, +{"edieresis",0x00EB}, +{"edot",0x0117}, +{"edotaccent",0x0117}, +{"edotbelow",0x1EB9}, +{"eegurmukhi",0x0A0F}, +{"eematragurmukhi",0x0A47}, +{"efcyrillic",0x0444}, +{"egrave",0x00E8}, +{"egujarati",0x0A8F}, +{"eharmenian",0x0567}, +{"ehbopomofo",0x311D}, +{"ehiragana",0x3048}, +{"ehookabove",0x1EBB}, +{"eibopomofo",0x311F}, +{"eight",0x0038}, +{"eightarabic",0x0668}, +{"eightbengali",0x09EE}, +{"eightcircle",0x2467}, +{"eightcircleinversesansserif",0x2791}, +{"eightdeva",0x096E}, +{"eighteencircle",0x2471}, +{"eighteenparen",0x2485}, +{"eighteenperiod",0x2499}, +{"eightgujarati",0x0AEE}, +{"eightgurmukhi",0x0A6E}, +{"eighthackarabic",0x0668}, +{"eighthangzhou",0x3028}, +{"eighthnotebeamed",0x266B}, +{"eightideographicparen",0x3227}, +{"eightinferior",0x2088}, +{"eightmonospace",0xFF18}, +{"eightoldstyle",0xF738}, +{"eightparen",0x247B}, +{"eightperiod",0x248F}, +{"eightpersian",0x06F8}, +{"eightroman",0x2177}, +{"eightsuperior",0x2078}, +{"eightthai",0x0E58}, +{"einvertedbreve",0x0207}, +{"eiotifiedcyrillic",0x0465}, +{"ekatakana",0x30A8}, +{"ekatakanahalfwidth",0xFF74}, +{"ekonkargurmukhi",0x0A74}, +{"ekorean",0x3154}, +{"elcyrillic",0x043B}, +{"element",0x2208}, +{"elevencircle",0x246A}, +{"elevenparen",0x247E}, +{"elevenperiod",0x2492}, +{"elevenroman",0x217A}, +{"ellipsis",0x2026}, +{"ellipsisvertical",0x22EE}, +{"emacron",0x0113}, +{"emacronacute",0x1E17}, +{"emacrongrave",0x1E15}, +{"emcyrillic",0x043C}, +{"emdash",0x2014}, +{"emdashvertical",0xFE31}, +{"emonospace",0xFF45}, +{"emphasismarkarmenian",0x055B}, +{"emptyset",0x2205}, +{"enbopomofo",0x3123}, +{"encyrillic",0x043D}, +{"endash",0x2013}, +{"endashvertical",0xFE32}, +{"endescendercyrillic",0x04A3}, +{"eng",0x014B}, +{"engbopomofo",0x3125}, +{"enghecyrillic",0x04A5}, +{"enhookcyrillic",0x04C8}, +{"enspace",0x2002}, +{"eogonek",0x0119}, +{"eokorean",0x3153}, +{"eopen",0x025B}, +{"eopenclosed",0x029A}, +{"eopenreversed",0x025C}, +{"eopenreversedclosed",0x025E}, +{"eopenreversedhook",0x025D}, +{"eparen",0x24A0}, +{"epsilon",0x03B5}, +{"epsilontonos",0x03AD}, +{"equal",0x003D}, +{"equalmonospace",0xFF1D}, +{"equalsmall",0xFE66}, +{"equalsuperior",0x207C}, +{"equivalence",0x2261}, +{"erbopomofo",0x3126}, +{"ercyrillic",0x0440}, +{"ereversed",0x0258}, +{"ereversedcyrillic",0x044D}, +{"escyrillic",0x0441}, +{"esdescendercyrillic",0x04AB}, +{"esh",0x0283}, +{"eshcurl",0x0286}, +{"eshortdeva",0x090E}, +{"eshortvowelsigndeva",0x0946}, +{"eshreversedloop",0x01AA}, +{"eshsquatreversed",0x0285}, +{"esmallhiragana",0x3047}, +{"esmallkatakana",0x30A7}, +{"esmallkatakanahalfwidth",0xFF6A}, +{"estimated",0x212E}, +{"esuperior",0xF6EC}, +{"eta",0x03B7}, +{"etarmenian",0x0568}, +{"etatonos",0x03AE}, +{"eth",0x00F0}, +{"etilde",0x1EBD}, +{"etildebelow",0x1E1B}, +{"etnahtafoukhhebrew",0x0591}, +{"etnahtafoukhlefthebrew",0x0591}, +{"etnahtahebrew",0x0591}, +{"etnahtalefthebrew",0x0591}, +{"eturned",0x01DD}, +{"eukorean",0x3161}, +{"euro",0x20AC}, +{"evowelsignbengali",0x09C7}, +{"evowelsigndeva",0x0947}, +{"evowelsigngujarati",0x0AC7}, +{"exclam",0x0021}, +{"exclamarmenian",0x055C}, +{"exclamdbl",0x203C}, +{"exclamdown",0x00A1}, +{"exclamdownsmall",0xF7A1}, +{"exclammonospace",0xFF01}, +{"exclamsmall",0xF721}, +{"existential",0x2203}, +{"ezh",0x0292}, +{"ezhcaron",0x01EF}, +{"ezhcurl",0x0293}, +{"ezhreversed",0x01B9}, +{"ezhtail",0x01BA}, +{"f",0x0066}, +{"fadeva",0x095E}, +{"fagurmukhi",0x0A5E}, +{"fahrenheit",0x2109}, +{"fathaarabic",0x064E}, +{"fathalowarabic",0x064E}, +{"fathatanarabic",0x064B}, +{"fbopomofo",0x3108}, +{"fcircle",0x24D5}, +{"fdotaccent",0x1E1F}, +{"feharabic",0x0641}, +{"feharmenian",0x0586}, +{"fehfinalarabic",0xFED2}, +{"fehinitialarabic",0xFED3}, +{"fehmedialarabic",0xFED4}, +{"feicoptic",0x03E5}, +{"female",0x2640}, +{"ff",0xFB00}, +{"ffi",0xFB03}, +{"ffl",0xFB04}, +{"fi",0xFB01}, +{"fifteencircle",0x246E}, +{"fifteenparen",0x2482}, +{"fifteenperiod",0x2496}, +{"figuredash",0x2012}, +{"filledbox",0x25A0}, +{"filledrect",0x25AC}, +{"finalkaf",0x05DA}, +{"finalkafdagesh",0xFB3A}, +{"finalkafdageshhebrew",0xFB3A}, +{"finalkafhebrew",0x05DA}, +{"finalmem",0x05DD}, +{"finalmemhebrew",0x05DD}, +{"finalnun",0x05DF}, +{"finalnunhebrew",0x05DF}, +{"finalpe",0x05E3}, +{"finalpehebrew",0x05E3}, +{"finaltsadi",0x05E5}, +{"finaltsadihebrew",0x05E5}, +{"firsttonechinese",0x02C9}, +{"fisheye",0x25C9}, +{"fitacyrillic",0x0473}, +{"five",0x0035}, +{"fivearabic",0x0665}, +{"fivebengali",0x09EB}, +{"fivecircle",0x2464}, +{"fivecircleinversesansserif",0x278E}, +{"fivedeva",0x096B}, +{"fiveeighths",0x215D}, +{"fivegujarati",0x0AEB}, +{"fivegurmukhi",0x0A6B}, +{"fivehackarabic",0x0665}, +{"fivehangzhou",0x3025}, +{"fiveideographicparen",0x3224}, +{"fiveinferior",0x2085}, +{"fivemonospace",0xFF15}, +{"fiveoldstyle",0xF735}, +{"fiveparen",0x2478}, +{"fiveperiod",0x248C}, +{"fivepersian",0x06F5}, +{"fiveroman",0x2174}, +{"fivesuperior",0x2075}, +{"fivethai",0x0E55}, +{"fl",0xFB02}, +{"florin",0x0192}, +{"fmonospace",0xFF46}, +{"fmsquare",0x3399}, +{"fofanthai",0x0E1F}, +{"fofathai",0x0E1D}, +{"fongmanthai",0x0E4F}, +{"forall",0x2200}, +{"four",0x0034}, +{"fourarabic",0x0664}, +{"fourbengali",0x09EA}, +{"fourcircle",0x2463}, +{"fourcircleinversesansserif",0x278D}, +{"fourdeva",0x096A}, +{"fourgujarati",0x0AEA}, +{"fourgurmukhi",0x0A6A}, +{"fourhackarabic",0x0664}, +{"fourhangzhou",0x3024}, +{"fourideographicparen",0x3223}, +{"fourinferior",0x2084}, +{"fourmonospace",0xFF14}, +{"fournumeratorbengali",0x09F7}, +{"fouroldstyle",0xF734}, +{"fourparen",0x2477}, +{"fourperiod",0x248B}, +{"fourpersian",0x06F4}, +{"fourroman",0x2173}, +{"foursuperior",0x2074}, +{"fourteencircle",0x246D}, +{"fourteenparen",0x2481}, +{"fourteenperiod",0x2495}, +{"fourthai",0x0E54}, +{"fourthtonechinese",0x02CB}, +{"fparen",0x24A1}, +{"fraction",0x2044}, +{"franc",0x20A3}, +{"g",0x0067}, +{"gabengali",0x0997}, +{"gacute",0x01F5}, +{"gadeva",0x0917}, +{"gafarabic",0x06AF}, +{"gaffinalarabic",0xFB93}, +{"gafinitialarabic",0xFB94}, +{"gafmedialarabic",0xFB95}, +{"gagujarati",0x0A97}, +{"gagurmukhi",0x0A17}, +{"gahiragana",0x304C}, +{"gakatakana",0x30AC}, +{"gamma",0x03B3}, +{"gammalatinsmall",0x0263}, +{"gammasuperior",0x02E0}, +{"gangiacoptic",0x03EB}, +{"gbopomofo",0x310D}, +{"gbreve",0x011F}, +{"gcaron",0x01E7}, +{"gcedilla",0x0123}, +{"gcircle",0x24D6}, +{"gcircumflex",0x011D}, +{"gcommaaccent",0x0123}, +{"gdot",0x0121}, +{"gdotaccent",0x0121}, +{"gecyrillic",0x0433}, +{"gehiragana",0x3052}, +{"gekatakana",0x30B2}, +{"geometricallyequal",0x2251}, +{"gereshaccenthebrew",0x059C}, +{"gereshhebrew",0x05F3}, +{"gereshmuqdamhebrew",0x059D}, +{"germandbls",0x00DF}, +{"gershayimaccenthebrew",0x059E}, +{"gershayimhebrew",0x05F4}, +{"getamark",0x3013}, +{"ghabengali",0x0998}, +{"ghadarmenian",0x0572}, +{"ghadeva",0x0918}, +{"ghagujarati",0x0A98}, +{"ghagurmukhi",0x0A18}, +{"ghainarabic",0x063A}, +{"ghainfinalarabic",0xFECE}, +{"ghaininitialarabic",0xFECF}, +{"ghainmedialarabic",0xFED0}, +{"ghemiddlehookcyrillic",0x0495}, +{"ghestrokecyrillic",0x0493}, +{"gheupturncyrillic",0x0491}, +{"ghhadeva",0x095A}, +{"ghhagurmukhi",0x0A5A}, +{"ghook",0x0260}, +{"ghzsquare",0x3393}, +{"gihiragana",0x304E}, +{"gikatakana",0x30AE}, +{"gimarmenian",0x0563}, +{"gimel",0x05D2}, +{"gimeldagesh",0xFB32}, +{"gimeldageshhebrew",0xFB32}, +{"gimelhebrew",0x05D2}, +{"gjecyrillic",0x0453}, +{"glottalinvertedstroke",0x01BE}, +{"glottalstop",0x0294}, +{"glottalstopinverted",0x0296}, +{"glottalstopmod",0x02C0}, +{"glottalstopreversed",0x0295}, +{"glottalstopreversedmod",0x02C1}, +{"glottalstopreversedsuperior",0x02E4}, +{"glottalstopstroke",0x02A1}, +{"glottalstopstrokereversed",0x02A2}, +{"gmacron",0x1E21}, +{"gmonospace",0xFF47}, +{"gohiragana",0x3054}, +{"gokatakana",0x30B4}, +{"gparen",0x24A2}, +{"gpasquare",0x33AC}, +{"gradient",0x2207}, +{"grave",0x0060}, +{"gravebelowcmb",0x0316}, +{"gravecmb",0x0300}, +{"gravecomb",0x0300}, +{"gravedeva",0x0953}, +{"gravelowmod",0x02CE}, +{"gravemonospace",0xFF40}, +{"gravetonecmb",0x0340}, +{"greater",0x003E}, +{"greaterequal",0x2265}, +{"greaterequalorless",0x22DB}, +{"greatermonospace",0xFF1E}, +{"greaterorequivalent",0x2273}, +{"greaterorless",0x2277}, +{"greateroverequal",0x2267}, +{"greatersmall",0xFE65}, +{"gscript",0x0261}, +{"gstroke",0x01E5}, +{"guhiragana",0x3050}, +{"guillemotleft",0x00AB}, +{"guillemotright",0x00BB}, +{"guilsinglleft",0x2039}, +{"guilsinglright",0x203A}, +{"gukatakana",0x30B0}, +{"guramusquare",0x3318}, +{"gysquare",0x33C9}, +{"h",0x0068}, +{"haabkhasiancyrillic",0x04A9}, +{"haaltonearabic",0x06C1}, +{"habengali",0x09B9}, +{"hadescendercyrillic",0x04B3}, +{"hadeva",0x0939}, +{"hagujarati",0x0AB9}, +{"hagurmukhi",0x0A39}, +{"haharabic",0x062D}, +{"hahfinalarabic",0xFEA2}, +{"hahinitialarabic",0xFEA3}, +{"hahiragana",0x306F}, +{"hahmedialarabic",0xFEA4}, +{"haitusquare",0x332A}, +{"hakatakana",0x30CF}, +{"hakatakanahalfwidth",0xFF8A}, +{"halantgurmukhi",0x0A4D}, +{"hamzaarabic",0x0621}, +{"hamzalowarabic",0x0621}, +{"hangulfiller",0x3164}, +{"hardsigncyrillic",0x044A}, +{"harpoonleftbarbup",0x21BC}, +{"harpoonrightbarbup",0x21C0}, +{"hasquare",0x33CA}, +{"hatafpatah",0x05B2}, +{"hatafpatah16",0x05B2}, +{"hatafpatah23",0x05B2}, +{"hatafpatah2f",0x05B2}, +{"hatafpatahhebrew",0x05B2}, +{"hatafpatahnarrowhebrew",0x05B2}, +{"hatafpatahquarterhebrew",0x05B2}, +{"hatafpatahwidehebrew",0x05B2}, +{"hatafqamats",0x05B3}, +{"hatafqamats1b",0x05B3}, +{"hatafqamats28",0x05B3}, +{"hatafqamats34",0x05B3}, +{"hatafqamatshebrew",0x05B3}, +{"hatafqamatsnarrowhebrew",0x05B3}, +{"hatafqamatsquarterhebrew",0x05B3}, +{"hatafqamatswidehebrew",0x05B3}, +{"hatafsegol",0x05B1}, +{"hatafsegol17",0x05B1}, +{"hatafsegol24",0x05B1}, +{"hatafsegol30",0x05B1}, +{"hatafsegolhebrew",0x05B1}, +{"hatafsegolnarrowhebrew",0x05B1}, +{"hatafsegolquarterhebrew",0x05B1}, +{"hatafsegolwidehebrew",0x05B1}, +{"hbar",0x0127}, +{"hbopomofo",0x310F}, +{"hbrevebelow",0x1E2B}, +{"hcedilla",0x1E29}, +{"hcircle",0x24D7}, +{"hcircumflex",0x0125}, +{"hdieresis",0x1E27}, +{"hdotaccent",0x1E23}, +{"hdotbelow",0x1E25}, +{"he",0x05D4}, +{"heart",0x2665}, +{"heartsuitblack",0x2665}, +{"heartsuitwhite",0x2661}, +{"hedagesh",0xFB34}, +{"hedageshhebrew",0xFB34}, +{"hehaltonearabic",0x06C1}, +{"heharabic",0x0647}, +{"hehebrew",0x05D4}, +{"hehfinalaltonearabic",0xFBA7}, +{"hehfinalalttwoarabic",0xFEEA}, +{"hehfinalarabic",0xFEEA}, +{"hehhamzaabovefinalarabic",0xFBA5}, +{"hehhamzaaboveisolatedarabic",0xFBA4}, +{"hehinitialaltonearabic",0xFBA8}, +{"hehinitialarabic",0xFEEB}, +{"hehiragana",0x3078}, +{"hehmedialaltonearabic",0xFBA9}, +{"hehmedialarabic",0xFEEC}, +{"heiseierasquare",0x337B}, +{"hekatakana",0x30D8}, +{"hekatakanahalfwidth",0xFF8D}, +{"hekutaarusquare",0x3336}, +{"henghook",0x0267}, +{"herutusquare",0x3339}, +{"het",0x05D7}, +{"hethebrew",0x05D7}, +{"hhook",0x0266}, +{"hhooksuperior",0x02B1}, +{"hieuhacirclekorean",0x327B}, +{"hieuhaparenkorean",0x321B}, +{"hieuhcirclekorean",0x326D}, +{"hieuhkorean",0x314E}, +{"hieuhparenkorean",0x320D}, +{"hihiragana",0x3072}, +{"hikatakana",0x30D2}, +{"hikatakanahalfwidth",0xFF8B}, +{"hiriq",0x05B4}, +{"hiriq14",0x05B4}, +{"hiriq21",0x05B4}, +{"hiriq2d",0x05B4}, +{"hiriqhebrew",0x05B4}, +{"hiriqnarrowhebrew",0x05B4}, +{"hiriqquarterhebrew",0x05B4}, +{"hiriqwidehebrew",0x05B4}, +{"hlinebelow",0x1E96}, +{"hmonospace",0xFF48}, +{"hoarmenian",0x0570}, +{"hohipthai",0x0E2B}, +{"hohiragana",0x307B}, +{"hokatakana",0x30DB}, +{"hokatakanahalfwidth",0xFF8E}, +{"holam",0x05B9}, +{"holam19",0x05B9}, +{"holam26",0x05B9}, +{"holam32",0x05B9}, +{"holamhebrew",0x05B9}, +{"holamnarrowhebrew",0x05B9}, +{"holamquarterhebrew",0x05B9}, +{"holamwidehebrew",0x05B9}, +{"honokhukthai",0x0E2E}, +{"hookabovecomb",0x0309}, +{"hookcmb",0x0309}, +{"hookpalatalizedbelowcmb",0x0321}, +{"hookretroflexbelowcmb",0x0322}, +{"hoonsquare",0x3342}, +{"horicoptic",0x03E9}, +{"horizontalbar",0x2015}, +{"horncmb",0x031B}, +{"hotsprings",0x2668}, +{"house",0x2302}, +{"hparen",0x24A3}, +{"hsuperior",0x02B0}, +{"hturned",0x0265}, +{"huhiragana",0x3075}, +{"huiitosquare",0x3333}, +{"hukatakana",0x30D5}, +{"hukatakanahalfwidth",0xFF8C}, +{"hungarumlaut",0x02DD}, +{"hungarumlautcmb",0x030B}, +{"hv",0x0195}, +{"hyphen",0x002D}, +{"hypheninferior",0xF6E5}, +{"hyphenmonospace",0xFF0D}, +{"hyphensmall",0xFE63}, +{"hyphensuperior",0xF6E6}, +{"hyphentwo",0x2010}, +{"i",0x0069}, +{"iacute",0x00ED}, +{"iacyrillic",0x044F}, +{"ibengali",0x0987}, +{"ibopomofo",0x3127}, +{"ibreve",0x012D}, +{"icaron",0x01D0}, +{"icircle",0x24D8}, +{"icircumflex",0x00EE}, +{"icyrillic",0x0456}, +{"idblgrave",0x0209}, +{"ideographearthcircle",0x328F}, +{"ideographfirecircle",0x328B}, +{"ideographicallianceparen",0x323F}, +{"ideographiccallparen",0x323A}, +{"ideographiccentrecircle",0x32A5}, +{"ideographicclose",0x3006}, +{"ideographiccomma",0x3001}, +{"ideographiccommaleft",0xFF64}, +{"ideographiccongratulationparen",0x3237}, +{"ideographiccorrectcircle",0x32A3}, +{"ideographicearthparen",0x322F}, +{"ideographicenterpriseparen",0x323D}, +{"ideographicexcellentcircle",0x329D}, +{"ideographicfestivalparen",0x3240}, +{"ideographicfinancialcircle",0x3296}, +{"ideographicfinancialparen",0x3236}, +{"ideographicfireparen",0x322B}, +{"ideographichaveparen",0x3232}, +{"ideographichighcircle",0x32A4}, +{"ideographiciterationmark",0x3005}, +{"ideographiclaborcircle",0x3298}, +{"ideographiclaborparen",0x3238}, +{"ideographicleftcircle",0x32A7}, +{"ideographiclowcircle",0x32A6}, +{"ideographicmedicinecircle",0x32A9}, +{"ideographicmetalparen",0x322E}, +{"ideographicmoonparen",0x322A}, +{"ideographicnameparen",0x3234}, +{"ideographicperiod",0x3002}, +{"ideographicprintcircle",0x329E}, +{"ideographicreachparen",0x3243}, +{"ideographicrepresentparen",0x3239}, +{"ideographicresourceparen",0x323E}, +{"ideographicrightcircle",0x32A8}, +{"ideographicsecretcircle",0x3299}, +{"ideographicselfparen",0x3242}, +{"ideographicsocietyparen",0x3233}, +{"ideographicspace",0x3000}, +{"ideographicspecialparen",0x3235}, +{"ideographicstockparen",0x3231}, +{"ideographicstudyparen",0x323B}, +{"ideographicsunparen",0x3230}, +{"ideographicsuperviseparen",0x323C}, +{"ideographicwaterparen",0x322C}, +{"ideographicwoodparen",0x322D}, +{"ideographiczero",0x3007}, +{"ideographmetalcircle",0x328E}, +{"ideographmooncircle",0x328A}, +{"ideographnamecircle",0x3294}, +{"ideographsuncircle",0x3290}, +{"ideographwatercircle",0x328C}, +{"ideographwoodcircle",0x328D}, +{"ideva",0x0907}, +{"idieresis",0x00EF}, +{"idieresisacute",0x1E2F}, +{"idieresiscyrillic",0x04E5}, +{"idotbelow",0x1ECB}, +{"iebrevecyrillic",0x04D7}, +{"iecyrillic",0x0435}, +{"ieungacirclekorean",0x3275}, +{"ieungaparenkorean",0x3215}, +{"ieungcirclekorean",0x3267}, +{"ieungkorean",0x3147}, +{"ieungparenkorean",0x3207}, +{"igrave",0x00EC}, +{"igujarati",0x0A87}, +{"igurmukhi",0x0A07}, +{"ihiragana",0x3044}, +{"ihookabove",0x1EC9}, +{"iibengali",0x0988}, +{"iicyrillic",0x0438}, +{"iideva",0x0908}, +{"iigujarati",0x0A88}, +{"iigurmukhi",0x0A08}, +{"iimatragurmukhi",0x0A40}, +{"iinvertedbreve",0x020B}, +{"iishortcyrillic",0x0439}, +{"iivowelsignbengali",0x09C0}, +{"iivowelsigndeva",0x0940}, +{"iivowelsigngujarati",0x0AC0}, +{"ij",0x0133}, +{"ikatakana",0x30A4}, +{"ikatakanahalfwidth",0xFF72}, +{"ikorean",0x3163}, +{"ilde",0x02DC}, +{"iluyhebrew",0x05AC}, +{"imacron",0x012B}, +{"imacroncyrillic",0x04E3}, +{"imageorapproximatelyequal",0x2253}, +{"imatragurmukhi",0x0A3F}, +{"imonospace",0xFF49}, +{"increment",0x2206}, +{"infinity",0x221E}, +{"iniarmenian",0x056B}, +{"integral",0x222B}, +{"integralbottom",0x2321}, +{"integralbt",0x2321}, +{"integralex",0xF8F5}, +{"integraltop",0x2320}, +{"integraltp",0x2320}, +{"intersection",0x2229}, +{"intisquare",0x3305}, +{"invbullet",0x25D8}, +{"invcircle",0x25D9}, +{"invsmileface",0x263B}, +{"iocyrillic",0x0451}, +{"iogonek",0x012F}, +{"iota",0x03B9}, +{"iotadieresis",0x03CA}, +{"iotadieresistonos",0x0390}, +{"iotalatin",0x0269}, +{"iotatonos",0x03AF}, +{"iparen",0x24A4}, +{"irigurmukhi",0x0A72}, +{"ismallhiragana",0x3043}, +{"ismallkatakana",0x30A3}, +{"ismallkatakanahalfwidth",0xFF68}, +{"issharbengali",0x09FA}, +{"istroke",0x0268}, +{"isuperior",0xF6ED}, +{"iterationhiragana",0x309D}, +{"iterationkatakana",0x30FD}, +{"itilde",0x0129}, +{"itildebelow",0x1E2D}, +{"iubopomofo",0x3129}, +{"iucyrillic",0x044E}, +{"ivowelsignbengali",0x09BF}, +{"ivowelsigndeva",0x093F}, +{"ivowelsigngujarati",0x0ABF}, +{"izhitsacyrillic",0x0475}, +{"izhitsadblgravecyrillic",0x0477}, +{"j",0x006A}, +{"jaarmenian",0x0571}, +{"jabengali",0x099C}, +{"jadeva",0x091C}, +{"jagujarati",0x0A9C}, +{"jagurmukhi",0x0A1C}, +{"jbopomofo",0x3110}, +{"jcaron",0x01F0}, +{"jcircle",0x24D9}, +{"jcircumflex",0x0135}, +{"jcrossedtail",0x029D}, +{"jdotlessstroke",0x025F}, +{"jecyrillic",0x0458}, +{"jeemarabic",0x062C}, +{"jeemfinalarabic",0xFE9E}, +{"jeeminitialarabic",0xFE9F}, +{"jeemmedialarabic",0xFEA0}, +{"jeharabic",0x0698}, +{"jehfinalarabic",0xFB8B}, +{"jhabengali",0x099D}, +{"jhadeva",0x091D}, +{"jhagujarati",0x0A9D}, +{"jhagurmukhi",0x0A1D}, +{"jheharmenian",0x057B}, +{"jis",0x3004}, +{"jmonospace",0xFF4A}, +{"jparen",0x24A5}, +{"jsuperior",0x02B2}, +{"k",0x006B}, +{"kabashkircyrillic",0x04A1}, +{"kabengali",0x0995}, +{"kacute",0x1E31}, +{"kacyrillic",0x043A}, +{"kadescendercyrillic",0x049B}, +{"kadeva",0x0915}, +{"kaf",0x05DB}, +{"kafarabic",0x0643}, +{"kafdagesh",0xFB3B}, +{"kafdageshhebrew",0xFB3B}, +{"kaffinalarabic",0xFEDA}, +{"kafhebrew",0x05DB}, +{"kafinitialarabic",0xFEDB}, +{"kafmedialarabic",0xFEDC}, +{"kafrafehebrew",0xFB4D}, +{"kagujarati",0x0A95}, +{"kagurmukhi",0x0A15}, +{"kahiragana",0x304B}, +{"kahookcyrillic",0x04C4}, +{"kakatakana",0x30AB}, +{"kakatakanahalfwidth",0xFF76}, +{"kappa",0x03BA}, +{"kappasymbolgreek",0x03F0}, +{"kapyeounmieumkorean",0x3171}, +{"kapyeounphieuphkorean",0x3184}, +{"kapyeounpieupkorean",0x3178}, +{"kapyeounssangpieupkorean",0x3179}, +{"karoriisquare",0x330D}, +{"kashidaautoarabic",0x0640}, +{"kashidaautonosidebearingarabic",0x0640}, +{"kasmallkatakana",0x30F5}, +{"kasquare",0x3384}, +{"kasraarabic",0x0650}, +{"kasratanarabic",0x064D}, +{"kastrokecyrillic",0x049F}, +{"katahiraprolongmarkhalfwidth",0xFF70}, +{"kaverticalstrokecyrillic",0x049D}, +{"kbopomofo",0x310E}, +{"kcalsquare",0x3389}, +{"kcaron",0x01E9}, +{"kcedilla",0x0137}, +{"kcircle",0x24DA}, +{"kcommaaccent",0x0137}, +{"kdotbelow",0x1E33}, +{"keharmenian",0x0584}, +{"kehiragana",0x3051}, +{"kekatakana",0x30B1}, +{"kekatakanahalfwidth",0xFF79}, +{"kenarmenian",0x056F}, +{"kesmallkatakana",0x30F6}, +{"kgreenlandic",0x0138}, +{"khabengali",0x0996}, +{"khacyrillic",0x0445}, +{"khadeva",0x0916}, +{"khagujarati",0x0A96}, +{"khagurmukhi",0x0A16}, +{"khaharabic",0x062E}, +{"khahfinalarabic",0xFEA6}, +{"khahinitialarabic",0xFEA7}, +{"khahmedialarabic",0xFEA8}, +{"kheicoptic",0x03E7}, +{"khhadeva",0x0959}, +{"khhagurmukhi",0x0A59}, +{"khieukhacirclekorean",0x3278}, +{"khieukhaparenkorean",0x3218}, +{"khieukhcirclekorean",0x326A}, +{"khieukhkorean",0x314B}, +{"khieukhparenkorean",0x320A}, +{"khokhaithai",0x0E02}, +{"khokhonthai",0x0E05}, +{"khokhuatthai",0x0E03}, +{"khokhwaithai",0x0E04}, +{"khomutthai",0x0E5B}, +{"khook",0x0199}, +{"khorakhangthai",0x0E06}, +{"khzsquare",0x3391}, +{"kihiragana",0x304D}, +{"kikatakana",0x30AD}, +{"kikatakanahalfwidth",0xFF77}, +{"kiroguramusquare",0x3315}, +{"kiromeetorusquare",0x3316}, +{"kirosquare",0x3314}, +{"kiyeokacirclekorean",0x326E}, +{"kiyeokaparenkorean",0x320E}, +{"kiyeokcirclekorean",0x3260}, +{"kiyeokkorean",0x3131}, +{"kiyeokparenkorean",0x3200}, +{"kiyeoksioskorean",0x3133}, +{"kjecyrillic",0x045C}, +{"klinebelow",0x1E35}, +{"klsquare",0x3398}, +{"kmcubedsquare",0x33A6}, +{"kmonospace",0xFF4B}, +{"kmsquaredsquare",0x33A2}, +{"kohiragana",0x3053}, +{"kohmsquare",0x33C0}, +{"kokaithai",0x0E01}, +{"kokatakana",0x30B3}, +{"kokatakanahalfwidth",0xFF7A}, +{"kooposquare",0x331E}, +{"koppacyrillic",0x0481}, +{"koreanstandardsymbol",0x327F}, +{"koroniscmb",0x0343}, +{"kparen",0x24A6}, +{"kpasquare",0x33AA}, +{"ksicyrillic",0x046F}, +{"ktsquare",0x33CF}, +{"kturned",0x029E}, +{"kuhiragana",0x304F}, +{"kukatakana",0x30AF}, +{"kukatakanahalfwidth",0xFF78}, +{"kvsquare",0x33B8}, +{"kwsquare",0x33BE}, +{"l",0x006C}, +{"labengali",0x09B2}, +{"lacute",0x013A}, +{"ladeva",0x0932}, +{"lagujarati",0x0AB2}, +{"lagurmukhi",0x0A32}, +{"lakkhangyaothai",0x0E45}, +{"lamaleffinalarabic",0xFEFC}, +{"lamalefhamzaabovefinalarabic",0xFEF8}, +{"lamalefhamzaaboveisolatedarabic",0xFEF7}, +{"lamalefhamzabelowfinalarabic",0xFEFA}, +{"lamalefhamzabelowisolatedarabic",0xFEF9}, +{"lamalefisolatedarabic",0xFEFB}, +{"lamalefmaddaabovefinalarabic",0xFEF6}, +{"lamalefmaddaaboveisolatedarabic",0xFEF5}, +{"lamarabic",0x0644}, +{"lambda",0x03BB}, +{"lambdastroke",0x019B}, +{"lamed",0x05DC}, +{"lameddagesh",0xFB3C}, +{"lameddageshhebrew",0xFB3C}, +{"lamedhebrew",0x05DC}, +{"lamfinalarabic",0xFEDE}, +{"lamhahinitialarabic",0xFCCA}, +{"laminitialarabic",0xFEDF}, +{"lamjeeminitialarabic",0xFCC9}, +{"lamkhahinitialarabic",0xFCCB}, +{"lamlamhehisolatedarabic",0xFDF2}, +{"lammedialarabic",0xFEE0}, +{"lammeemhahinitialarabic",0xFD88}, +{"lammeeminitialarabic",0xFCCC}, +{"largecircle",0x25EF}, +{"lbar",0x019A}, +{"lbelt",0x026C}, +{"lbopomofo",0x310C}, +{"lcaron",0x013E}, +{"lcedilla",0x013C}, +{"lcircle",0x24DB}, +{"lcircumflexbelow",0x1E3D}, +{"lcommaaccent",0x013C}, +{"ldot",0x0140}, +{"ldotaccent",0x0140}, +{"ldotbelow",0x1E37}, +{"ldotbelowmacron",0x1E39}, +{"leftangleabovecmb",0x031A}, +{"lefttackbelowcmb",0x0318}, +{"less",0x003C}, +{"lessequal",0x2264}, +{"lessequalorgreater",0x22DA}, +{"lessmonospace",0xFF1C}, +{"lessorequivalent",0x2272}, +{"lessorgreater",0x2276}, +{"lessoverequal",0x2266}, +{"lesssmall",0xFE64}, +{"lezh",0x026E}, +{"lfblock",0x258C}, +{"lhookretroflex",0x026D}, +{"lira",0x20A4}, +{"liwnarmenian",0x056C}, +{"lj",0x01C9}, +{"ljecyrillic",0x0459}, +{"ll",0xF6C0}, +{"lladeva",0x0933}, +{"llagujarati",0x0AB3}, +{"llinebelow",0x1E3B}, +{"llladeva",0x0934}, +{"llvocalicbengali",0x09E1}, +{"llvocalicdeva",0x0961}, +{"llvocalicvowelsignbengali",0x09E3}, +{"llvocalicvowelsigndeva",0x0963}, +{"lmiddletilde",0x026B}, +{"lmonospace",0xFF4C}, +{"lmsquare",0x33D0}, +{"lochulathai",0x0E2C}, +{"logicaland",0x2227}, +{"logicalnot",0x00AC}, +{"logicalnotreversed",0x2310}, +{"logicalor",0x2228}, +{"lolingthai",0x0E25}, +{"longs",0x017F}, +{"lowlinecenterline",0xFE4E}, +{"lowlinecmb",0x0332}, +{"lowlinedashed",0xFE4D}, +{"lozenge",0x25CA}, +{"lparen",0x24A7}, +{"lslash",0x0142}, +{"lsquare",0x2113}, +{"lsuperior",0xF6EE}, +{"ltshade",0x2591}, +{"luthai",0x0E26}, +{"lvocalicbengali",0x098C}, +{"lvocalicdeva",0x090C}, +{"lvocalicvowelsignbengali",0x09E2}, +{"lvocalicvowelsigndeva",0x0962}, +{"lxsquare",0x33D3}, +{"m",0x006D}, +{"mabengali",0x09AE}, +{"macron",0x00AF}, +{"macronbelowcmb",0x0331}, +{"macroncmb",0x0304}, +{"macronlowmod",0x02CD}, +{"macronmonospace",0xFFE3}, +{"macute",0x1E3F}, +{"madeva",0x092E}, +{"magujarati",0x0AAE}, +{"magurmukhi",0x0A2E}, +{"mahapakhhebrew",0x05A4}, +{"mahapakhlefthebrew",0x05A4}, +{"mahiragana",0x307E}, +{"maichattawalowleftthai",0xF895}, +{"maichattawalowrightthai",0xF894}, +{"maichattawathai",0x0E4B}, +{"maichattawaupperleftthai",0xF893}, +{"maieklowleftthai",0xF88C}, +{"maieklowrightthai",0xF88B}, +{"maiekthai",0x0E48}, +{"maiekupperleftthai",0xF88A}, +{"maihanakatleftthai",0xF884}, +{"maihanakatthai",0x0E31}, +{"maitaikhuleftthai",0xF889}, +{"maitaikhuthai",0x0E47}, +{"maitholowleftthai",0xF88F}, +{"maitholowrightthai",0xF88E}, +{"maithothai",0x0E49}, +{"maithoupperleftthai",0xF88D}, +{"maitrilowleftthai",0xF892}, +{"maitrilowrightthai",0xF891}, +{"maitrithai",0x0E4A}, +{"maitriupperleftthai",0xF890}, +{"maiyamokthai",0x0E46}, +{"makatakana",0x30DE}, +{"makatakanahalfwidth",0xFF8F}, +{"male",0x2642}, +{"mansyonsquare",0x3347}, +{"maqafhebrew",0x05BE}, +{"mars",0x2642}, +{"masoracirclehebrew",0x05AF}, +{"masquare",0x3383}, +{"mbopomofo",0x3107}, +{"mbsquare",0x33D4}, +{"mcircle",0x24DC}, +{"mcubedsquare",0x33A5}, +{"mdotaccent",0x1E41}, +{"mdotbelow",0x1E43}, +{"meemarabic",0x0645}, +{"meemfinalarabic",0xFEE2}, +{"meeminitialarabic",0xFEE3}, +{"meemmedialarabic",0xFEE4}, +{"meemmeeminitialarabic",0xFCD1}, +{"meemmeemisolatedarabic",0xFC48}, +{"meetorusquare",0x334D}, +{"mehiragana",0x3081}, +{"meizierasquare",0x337E}, +{"mekatakana",0x30E1}, +{"mekatakanahalfwidth",0xFF92}, +{"mem",0x05DE}, +{"memdagesh",0xFB3E}, +{"memdageshhebrew",0xFB3E}, +{"memhebrew",0x05DE}, +{"menarmenian",0x0574}, +{"merkhahebrew",0x05A5}, +{"merkhakefulahebrew",0x05A6}, +{"merkhakefulalefthebrew",0x05A6}, +{"merkhalefthebrew",0x05A5}, +{"mhook",0x0271}, +{"mhzsquare",0x3392}, +{"middledotkatakanahalfwidth",0xFF65}, +{"middot",0x00B7}, +{"mieumacirclekorean",0x3272}, +{"mieumaparenkorean",0x3212}, +{"mieumcirclekorean",0x3264}, +{"mieumkorean",0x3141}, +{"mieumpansioskorean",0x3170}, +{"mieumparenkorean",0x3204}, +{"mieumpieupkorean",0x316E}, +{"mieumsioskorean",0x316F}, +{"mihiragana",0x307F}, +{"mikatakana",0x30DF}, +{"mikatakanahalfwidth",0xFF90}, +{"minus",0x2212}, +{"minusbelowcmb",0x0320}, +{"minuscircle",0x2296}, +{"minusmod",0x02D7}, +{"minusplus",0x2213}, +{"minute",0x2032}, +{"miribaarusquare",0x334A}, +{"mirisquare",0x3349}, +{"mlonglegturned",0x0270}, +{"mlsquare",0x3396}, +{"mmcubedsquare",0x33A3}, +{"mmonospace",0xFF4D}, +{"mmsquaredsquare",0x339F}, +{"mohiragana",0x3082}, +{"mohmsquare",0x33C1}, +{"mokatakana",0x30E2}, +{"mokatakanahalfwidth",0xFF93}, +{"molsquare",0x33D6}, +{"momathai",0x0E21}, +{"moverssquare",0x33A7}, +{"moverssquaredsquare",0x33A8}, +{"mparen",0x24A8}, +{"mpasquare",0x33AB}, +{"mssquare",0x33B3}, +{"msuperior",0xF6EF}, +{"mturned",0x026F}, +{"mu",0x00B5}, +{"mu1",0x00B5}, +{"muasquare",0x3382}, +{"muchgreater",0x226B}, +{"muchless",0x226A}, +{"mufsquare",0x338C}, +{"mugreek",0x03BC}, +{"mugsquare",0x338D}, +{"muhiragana",0x3080}, +{"mukatakana",0x30E0}, +{"mukatakanahalfwidth",0xFF91}, +{"mulsquare",0x3395}, +{"multiply",0x00D7}, +{"mumsquare",0x339B}, +{"munahhebrew",0x05A3}, +{"munahlefthebrew",0x05A3}, +{"musicalnote",0x266A}, +{"musicalnotedbl",0x266B}, +{"musicflatsign",0x266D}, +{"musicsharpsign",0x266F}, +{"mussquare",0x33B2}, +{"muvsquare",0x33B6}, +{"muwsquare",0x33BC}, +{"mvmegasquare",0x33B9}, +{"mvsquare",0x33B7}, +{"mwmegasquare",0x33BF}, +{"mwsquare",0x33BD}, +{"n",0x006E}, +{"nabengali",0x09A8}, +{"nabla",0x2207}, +{"nacute",0x0144}, +{"nadeva",0x0928}, +{"nagujarati",0x0AA8}, +{"nagurmukhi",0x0A28}, +{"nahiragana",0x306A}, +{"nakatakana",0x30CA}, +{"nakatakanahalfwidth",0xFF85}, +{"napostrophe",0x0149}, +{"nasquare",0x3381}, +{"nbopomofo",0x310B}, +{"nbspace",0x00A0}, +{"ncaron",0x0148}, +{"ncedilla",0x0146}, +{"ncircle",0x24DD}, +{"ncircumflexbelow",0x1E4B}, +{"ncommaaccent",0x0146}, +{"ndotaccent",0x1E45}, +{"ndotbelow",0x1E47}, +{"nehiragana",0x306D}, +{"nekatakana",0x30CD}, +{"nekatakanahalfwidth",0xFF88}, +{"newsheqelsign",0x20AA}, +{"nfsquare",0x338B}, +{"ngabengali",0x0999}, +{"ngadeva",0x0919}, +{"ngagujarati",0x0A99}, +{"ngagurmukhi",0x0A19}, +{"ngonguthai",0x0E07}, +{"nhiragana",0x3093}, +{"nhookleft",0x0272}, +{"nhookretroflex",0x0273}, +{"nieunacirclekorean",0x326F}, +{"nieunaparenkorean",0x320F}, +{"nieuncieuckorean",0x3135}, +{"nieuncirclekorean",0x3261}, +{"nieunhieuhkorean",0x3136}, +{"nieunkorean",0x3134}, +{"nieunpansioskorean",0x3168}, +{"nieunparenkorean",0x3201}, +{"nieunsioskorean",0x3167}, +{"nieuntikeutkorean",0x3166}, +{"nihiragana",0x306B}, +{"nikatakana",0x30CB}, +{"nikatakanahalfwidth",0xFF86}, +{"nikhahitleftthai",0xF899}, +{"nikhahitthai",0x0E4D}, +{"nine",0x0039}, +{"ninearabic",0x0669}, +{"ninebengali",0x09EF}, +{"ninecircle",0x2468}, +{"ninecircleinversesansserif",0x2792}, +{"ninedeva",0x096F}, +{"ninegujarati",0x0AEF}, +{"ninegurmukhi",0x0A6F}, +{"ninehackarabic",0x0669}, +{"ninehangzhou",0x3029}, +{"nineideographicparen",0x3228}, +{"nineinferior",0x2089}, +{"ninemonospace",0xFF19}, +{"nineoldstyle",0xF739}, +{"nineparen",0x247C}, +{"nineperiod",0x2490}, +{"ninepersian",0x06F9}, +{"nineroman",0x2178}, +{"ninesuperior",0x2079}, +{"nineteencircle",0x2472}, +{"nineteenparen",0x2486}, +{"nineteenperiod",0x249A}, +{"ninethai",0x0E59}, +{"nj",0x01CC}, +{"njecyrillic",0x045A}, +{"nkatakana",0x30F3}, +{"nkatakanahalfwidth",0xFF9D}, +{"nlegrightlong",0x019E}, +{"nlinebelow",0x1E49}, +{"nmonospace",0xFF4E}, +{"nmsquare",0x339A}, +{"nnabengali",0x09A3}, +{"nnadeva",0x0923}, +{"nnagujarati",0x0AA3}, +{"nnagurmukhi",0x0A23}, +{"nnnadeva",0x0929}, +{"nohiragana",0x306E}, +{"nokatakana",0x30CE}, +{"nokatakanahalfwidth",0xFF89}, +{"nonbreakingspace",0x00A0}, +{"nonenthai",0x0E13}, +{"nonuthai",0x0E19}, +{"noonarabic",0x0646}, +{"noonfinalarabic",0xFEE6}, +{"noonghunnaarabic",0x06BA}, +{"noonghunnafinalarabic",0xFB9F}, +{"nooninitialarabic",0xFEE7}, +{"noonjeeminitialarabic",0xFCD2}, +{"noonjeemisolatedarabic",0xFC4B}, +{"noonmedialarabic",0xFEE8}, +{"noonmeeminitialarabic",0xFCD5}, +{"noonmeemisolatedarabic",0xFC4E}, +{"noonnoonfinalarabic",0xFC8D}, +{"notcontains",0x220C}, +{"notelement",0x2209}, +{"notelementof",0x2209}, +{"notequal",0x2260}, +{"notgreater",0x226F}, +{"notgreaternorequal",0x2271}, +{"notgreaternorless",0x2279}, +{"notidentical",0x2262}, +{"notless",0x226E}, +{"notlessnorequal",0x2270}, +{"notparallel",0x2226}, +{"notprecedes",0x2280}, +{"notsubset",0x2284}, +{"notsucceeds",0x2281}, +{"notsuperset",0x2285}, +{"nowarmenian",0x0576}, +{"nparen",0x24A9}, +{"nssquare",0x33B1}, +{"nsuperior",0x207F}, +{"ntilde",0x00F1}, +{"nu",0x03BD}, +{"nuhiragana",0x306C}, +{"nukatakana",0x30CC}, +{"nukatakanahalfwidth",0xFF87}, +{"nuktabengali",0x09BC}, +{"nuktadeva",0x093C}, +{"nuktagujarati",0x0ABC}, +{"nuktagurmukhi",0x0A3C}, +{"numbersign",0x0023}, +{"numbersignmonospace",0xFF03}, +{"numbersignsmall",0xFE5F}, +{"numeralsigngreek",0x0374}, +{"numeralsignlowergreek",0x0375}, +{"numero",0x2116}, +{"nun",0x05E0}, +{"nundagesh",0xFB40}, +{"nundageshhebrew",0xFB40}, +{"nunhebrew",0x05E0}, +{"nvsquare",0x33B5}, +{"nwsquare",0x33BB}, +{"nyabengali",0x099E}, +{"nyadeva",0x091E}, +{"nyagujarati",0x0A9E}, +{"nyagurmukhi",0x0A1E}, +{"o",0x006F}, +{"oacute",0x00F3}, +{"oangthai",0x0E2D}, +{"obarred",0x0275}, +{"obarredcyrillic",0x04E9}, +{"obarreddieresiscyrillic",0x04EB}, +{"obengali",0x0993}, +{"obopomofo",0x311B}, +{"obreve",0x014F}, +{"ocandradeva",0x0911}, +{"ocandragujarati",0x0A91}, +{"ocandravowelsigndeva",0x0949}, +{"ocandravowelsigngujarati",0x0AC9}, +{"ocaron",0x01D2}, +{"ocircle",0x24DE}, +{"ocircumflex",0x00F4}, +{"ocircumflexacute",0x1ED1}, +{"ocircumflexdotbelow",0x1ED9}, +{"ocircumflexgrave",0x1ED3}, +{"ocircumflexhookabove",0x1ED5}, +{"ocircumflextilde",0x1ED7}, +{"ocyrillic",0x043E}, +{"odblacute",0x0151}, +{"odblgrave",0x020D}, +{"odeva",0x0913}, +{"odieresis",0x00F6}, +{"odieresiscyrillic",0x04E7}, +{"odotbelow",0x1ECD}, +{"oe",0x0153}, +{"oekorean",0x315A}, +{"ogonek",0x02DB}, +{"ogonekcmb",0x0328}, +{"ograve",0x00F2}, +{"ogujarati",0x0A93}, +{"oharmenian",0x0585}, +{"ohiragana",0x304A}, +{"ohookabove",0x1ECF}, +{"ohorn",0x01A1}, +{"ohornacute",0x1EDB}, +{"ohorndotbelow",0x1EE3}, +{"ohorngrave",0x1EDD}, +{"ohornhookabove",0x1EDF}, +{"ohorntilde",0x1EE1}, +{"ohungarumlaut",0x0151}, +{"oi",0x01A3}, +{"oinvertedbreve",0x020F}, +{"okatakana",0x30AA}, +{"okatakanahalfwidth",0xFF75}, +{"okorean",0x3157}, +{"olehebrew",0x05AB}, +{"omacron",0x014D}, +{"omacronacute",0x1E53}, +{"omacrongrave",0x1E51}, +{"omdeva",0x0950}, +{"omega",0x03C9}, +{"omega1",0x03D6}, +{"omegacyrillic",0x0461}, +{"omegalatinclosed",0x0277}, +{"omegaroundcyrillic",0x047B}, +{"omegatitlocyrillic",0x047D}, +{"omegatonos",0x03CE}, +{"omgujarati",0x0AD0}, +{"omicron",0x03BF}, +{"omicrontonos",0x03CC}, +{"omonospace",0xFF4F}, +{"one",0x0031}, +{"onearabic",0x0661}, +{"onebengali",0x09E7}, +{"onecircle",0x2460}, +{"onecircleinversesansserif",0x278A}, +{"onedeva",0x0967}, +{"onedotenleader",0x2024}, +{"oneeighth",0x215B}, +{"onefitted",0xF6DC}, +{"onegujarati",0x0AE7}, +{"onegurmukhi",0x0A67}, +{"onehackarabic",0x0661}, +{"onehalf",0x00BD}, +{"onehangzhou",0x3021}, +{"oneideographicparen",0x3220}, +{"oneinferior",0x2081}, +{"onemonospace",0xFF11}, +{"onenumeratorbengali",0x09F4}, +{"oneoldstyle",0xF731}, +{"oneparen",0x2474}, +{"oneperiod",0x2488}, +{"onepersian",0x06F1}, +{"onequarter",0x00BC}, +{"oneroman",0x2170}, +{"onesuperior",0x00B9}, +{"onethai",0x0E51}, +{"onethird",0x2153}, +{"oogonek",0x01EB}, +{"oogonekmacron",0x01ED}, +{"oogurmukhi",0x0A13}, +{"oomatragurmukhi",0x0A4B}, +{"oopen",0x0254}, +{"oparen",0x24AA}, +{"openbullet",0x25E6}, +{"option",0x2325}, +{"ordfeminine",0x00AA}, +{"ordmasculine",0x00BA}, +{"orthogonal",0x221F}, +{"oshortdeva",0x0912}, +{"oshortvowelsigndeva",0x094A}, +{"oslash",0x00F8}, +{"oslashacute",0x01FF}, +{"osmallhiragana",0x3049}, +{"osmallkatakana",0x30A9}, +{"osmallkatakanahalfwidth",0xFF6B}, +{"ostrokeacute",0x01FF}, +{"osuperior",0xF6F0}, +{"otcyrillic",0x047F}, +{"otilde",0x00F5}, +{"otildeacute",0x1E4D}, +{"otildedieresis",0x1E4F}, +{"oubopomofo",0x3121}, +{"overline",0x203E}, +{"overlinecenterline",0xFE4A}, +{"overlinecmb",0x0305}, +{"overlinedashed",0xFE49}, +{"overlinedblwavy",0xFE4C}, +{"overlinewavy",0xFE4B}, +{"overscore",0x00AF}, +{"ovowelsignbengali",0x09CB}, +{"ovowelsigndeva",0x094B}, +{"ovowelsigngujarati",0x0ACB}, +{"p",0x0070}, +{"paampssquare",0x3380}, +{"paasentosquare",0x332B}, +{"pabengali",0x09AA}, +{"pacute",0x1E55}, +{"padeva",0x092A}, +{"pagedown",0x21DF}, +{"pageup",0x21DE}, +{"pagujarati",0x0AAA}, +{"pagurmukhi",0x0A2A}, +{"pahiragana",0x3071}, +{"paiyannoithai",0x0E2F}, +{"pakatakana",0x30D1}, +{"palatalizationcyrilliccmb",0x0484}, +{"palochkacyrillic",0x04C0}, +{"pansioskorean",0x317F}, +{"paragraph",0x00B6}, +{"parallel",0x2225}, +{"parenleft",0x0028}, +{"parenleftaltonearabic",0xFD3E}, +{"parenleftbt",0xF8ED}, +{"parenleftex",0xF8EC}, +{"parenleftinferior",0x208D}, +{"parenleftmonospace",0xFF08}, +{"parenleftsmall",0xFE59}, +{"parenleftsuperior",0x207D}, +{"parenlefttp",0xF8EB}, +{"parenleftvertical",0xFE35}, +{"parenright",0x0029}, +{"parenrightaltonearabic",0xFD3F}, +{"parenrightbt",0xF8F8}, +{"parenrightex",0xF8F7}, +{"parenrightinferior",0x208E}, +{"parenrightmonospace",0xFF09}, +{"parenrightsmall",0xFE5A}, +{"parenrightsuperior",0x207E}, +{"parenrighttp",0xF8F6}, +{"parenrightvertical",0xFE36}, +{"partialdiff",0x2202}, +{"paseqhebrew",0x05C0}, +{"pashtahebrew",0x0599}, +{"pasquare",0x33A9}, +{"patah",0x05B7}, +{"patah11",0x05B7}, +{"patah1d",0x05B7}, +{"patah2a",0x05B7}, +{"patahhebrew",0x05B7}, +{"patahnarrowhebrew",0x05B7}, +{"patahquarterhebrew",0x05B7}, +{"patahwidehebrew",0x05B7}, +{"pazerhebrew",0x05A1}, +{"pbopomofo",0x3106}, +{"pcircle",0x24DF}, +{"pdotaccent",0x1E57}, +{"pe",0x05E4}, +{"pecyrillic",0x043F}, +{"pedagesh",0xFB44}, +{"pedageshhebrew",0xFB44}, +{"peezisquare",0x333B}, +{"pefinaldageshhebrew",0xFB43}, +{"peharabic",0x067E}, +{"peharmenian",0x057A}, +{"pehebrew",0x05E4}, +{"pehfinalarabic",0xFB57}, +{"pehinitialarabic",0xFB58}, +{"pehiragana",0x307A}, +{"pehmedialarabic",0xFB59}, +{"pekatakana",0x30DA}, +{"pemiddlehookcyrillic",0x04A7}, +{"perafehebrew",0xFB4E}, +{"percent",0x0025}, +{"percentarabic",0x066A}, +{"percentmonospace",0xFF05}, +{"percentsmall",0xFE6A}, +{"period",0x002E}, +{"periodarmenian",0x0589}, +{"periodcentered",0x00B7}, +{"periodhalfwidth",0xFF61}, +{"periodinferior",0xF6E7}, +{"periodmonospace",0xFF0E}, +{"periodsmall",0xFE52}, +{"periodsuperior",0xF6E8}, +{"perispomenigreekcmb",0x0342}, +{"perpendicular",0x22A5}, +{"perthousand",0x2030}, +{"peseta",0x20A7}, +{"pfsquare",0x338A}, +{"phabengali",0x09AB}, +{"phadeva",0x092B}, +{"phagujarati",0x0AAB}, +{"phagurmukhi",0x0A2B}, +{"phi",0x03C6}, +{"phi1",0x03D5}, +{"phieuphacirclekorean",0x327A}, +{"phieuphaparenkorean",0x321A}, +{"phieuphcirclekorean",0x326C}, +{"phieuphkorean",0x314D}, +{"phieuphparenkorean",0x320C}, +{"philatin",0x0278}, +{"phinthuthai",0x0E3A}, +{"phisymbolgreek",0x03D5}, +{"phook",0x01A5}, +{"phophanthai",0x0E1E}, +{"phophungthai",0x0E1C}, +{"phosamphaothai",0x0E20}, +{"pi",0x03C0}, +{"pieupacirclekorean",0x3273}, +{"pieupaparenkorean",0x3213}, +{"pieupcieuckorean",0x3176}, +{"pieupcirclekorean",0x3265}, +{"pieupkiyeokkorean",0x3172}, +{"pieupkorean",0x3142}, +{"pieupparenkorean",0x3205}, +{"pieupsioskiyeokkorean",0x3174}, +{"pieupsioskorean",0x3144}, +{"pieupsiostikeutkorean",0x3175}, +{"pieupthieuthkorean",0x3177}, +{"pieuptikeutkorean",0x3173}, +{"pihiragana",0x3074}, +{"pikatakana",0x30D4}, +{"pisymbolgreek",0x03D6}, +{"piwrarmenian",0x0583}, +{"plus",0x002B}, +{"plusbelowcmb",0x031F}, +{"pluscircle",0x2295}, +{"plusminus",0x00B1}, +{"plusmod",0x02D6}, +{"plusmonospace",0xFF0B}, +{"plussmall",0xFE62}, +{"plussuperior",0x207A}, +{"pmonospace",0xFF50}, +{"pmsquare",0x33D8}, +{"pohiragana",0x307D}, +{"pointingindexdownwhite",0x261F}, +{"pointingindexleftwhite",0x261C}, +{"pointingindexrightwhite",0x261E}, +{"pointingindexupwhite",0x261D}, +{"pokatakana",0x30DD}, +{"poplathai",0x0E1B}, +{"postalmark",0x3012}, +{"postalmarkface",0x3020}, +{"pparen",0x24AB}, +{"precedes",0x227A}, +{"prescription",0x211E}, +{"primemod",0x02B9}, +{"primereversed",0x2035}, +{"product",0x220F}, +{"projective",0x2305}, +{"prolongedkana",0x30FC}, +{"propellor",0x2318}, +{"propersubset",0x2282}, +{"propersuperset",0x2283}, +{"proportion",0x2237}, +{"proportional",0x221D}, +{"psi",0x03C8}, +{"psicyrillic",0x0471}, +{"psilipneumatacyrilliccmb",0x0486}, +{"pssquare",0x33B0}, +{"puhiragana",0x3077}, +{"pukatakana",0x30D7}, +{"pvsquare",0x33B4}, +{"pwsquare",0x33BA}, +{"q",0x0071}, +{"qadeva",0x0958}, +{"qadmahebrew",0x05A8}, +{"qafarabic",0x0642}, +{"qaffinalarabic",0xFED6}, +{"qafinitialarabic",0xFED7}, +{"qafmedialarabic",0xFED8}, +{"qamats",0x05B8}, +{"qamats10",0x05B8}, +{"qamats1a",0x05B8}, +{"qamats1c",0x05B8}, +{"qamats27",0x05B8}, +{"qamats29",0x05B8}, +{"qamats33",0x05B8}, +{"qamatsde",0x05B8}, +{"qamatshebrew",0x05B8}, +{"qamatsnarrowhebrew",0x05B8}, +{"qamatsqatanhebrew",0x05B8}, +{"qamatsqatannarrowhebrew",0x05B8}, +{"qamatsqatanquarterhebrew",0x05B8}, +{"qamatsqatanwidehebrew",0x05B8}, +{"qamatsquarterhebrew",0x05B8}, +{"qamatswidehebrew",0x05B8}, +{"qarneyparahebrew",0x059F}, +{"qbopomofo",0x3111}, +{"qcircle",0x24E0}, +{"qhook",0x02A0}, +{"qmonospace",0xFF51}, +{"qof",0x05E7}, +{"qofdagesh",0xFB47}, +{"qofdageshhebrew",0xFB47}, +{"qofhebrew",0x05E7}, +{"qparen",0x24AC}, +{"quarternote",0x2669}, +{"qubuts",0x05BB}, +{"qubuts18",0x05BB}, +{"qubuts25",0x05BB}, +{"qubuts31",0x05BB}, +{"qubutshebrew",0x05BB}, +{"qubutsnarrowhebrew",0x05BB}, +{"qubutsquarterhebrew",0x05BB}, +{"qubutswidehebrew",0x05BB}, +{"question",0x003F}, +{"questionarabic",0x061F}, +{"questionarmenian",0x055E}, +{"questiondown",0x00BF}, +{"questiondownsmall",0xF7BF}, +{"questiongreek",0x037E}, +{"questionmonospace",0xFF1F}, +{"questionsmall",0xF73F}, +{"quotedbl",0x0022}, +{"quotedblbase",0x201E}, +{"quotedblleft",0x201C}, +{"quotedblmonospace",0xFF02}, +{"quotedblprime",0x301E}, +{"quotedblprimereversed",0x301D}, +{"quotedblright",0x201D}, +{"quoteleft",0x2018}, +{"quoteleftreversed",0x201B}, +{"quotereversed",0x201B}, +{"quoteright",0x2019}, +{"quoterightn",0x0149}, +{"quotesinglbase",0x201A}, +{"quotesingle",0x0027}, +{"quotesinglemonospace",0xFF07}, +{"r",0x0072}, +{"raarmenian",0x057C}, +{"rabengali",0x09B0}, +{"racute",0x0155}, +{"radeva",0x0930}, +{"radical",0x221A}, +{"radicalex",0xF8E5}, +{"radoverssquare",0x33AE}, +{"radoverssquaredsquare",0x33AF}, +{"radsquare",0x33AD}, +{"rafe",0x05BF}, +{"rafehebrew",0x05BF}, +{"ragujarati",0x0AB0}, +{"ragurmukhi",0x0A30}, +{"rahiragana",0x3089}, +{"rakatakana",0x30E9}, +{"rakatakanahalfwidth",0xFF97}, +{"ralowerdiagonalbengali",0x09F1}, +{"ramiddlediagonalbengali",0x09F0}, +{"ramshorn",0x0264}, +{"ratio",0x2236}, +{"rbopomofo",0x3116}, +{"rcaron",0x0159}, +{"rcedilla",0x0157}, +{"rcircle",0x24E1}, +{"rcommaaccent",0x0157}, +{"rdblgrave",0x0211}, +{"rdotaccent",0x1E59}, +{"rdotbelow",0x1E5B}, +{"rdotbelowmacron",0x1E5D}, +{"referencemark",0x203B}, +{"reflexsubset",0x2286}, +{"reflexsuperset",0x2287}, +{"registered",0x00AE}, +{"registersans",0xF8E8}, +{"registerserif",0xF6DA}, +{"reharabic",0x0631}, +{"reharmenian",0x0580}, +{"rehfinalarabic",0xFEAE}, +{"rehiragana",0x308C}, +{"rekatakana",0x30EC}, +{"rekatakanahalfwidth",0xFF9A}, +{"resh",0x05E8}, +{"reshdageshhebrew",0xFB48}, +{"reshhebrew",0x05E8}, +{"reversedtilde",0x223D}, +{"reviahebrew",0x0597}, +{"reviamugrashhebrew",0x0597}, +{"revlogicalnot",0x2310}, +{"rfishhook",0x027E}, +{"rfishhookreversed",0x027F}, +{"rhabengali",0x09DD}, +{"rhadeva",0x095D}, +{"rho",0x03C1}, +{"rhook",0x027D}, +{"rhookturned",0x027B}, +{"rhookturnedsuperior",0x02B5}, +{"rhosymbolgreek",0x03F1}, +{"rhotichookmod",0x02DE}, +{"rieulacirclekorean",0x3271}, +{"rieulaparenkorean",0x3211}, +{"rieulcirclekorean",0x3263}, +{"rieulhieuhkorean",0x3140}, +{"rieulkiyeokkorean",0x313A}, +{"rieulkiyeoksioskorean",0x3169}, +{"rieulkorean",0x3139}, +{"rieulmieumkorean",0x313B}, +{"rieulpansioskorean",0x316C}, +{"rieulparenkorean",0x3203}, +{"rieulphieuphkorean",0x313F}, +{"rieulpieupkorean",0x313C}, +{"rieulpieupsioskorean",0x316B}, +{"rieulsioskorean",0x313D}, +{"rieulthieuthkorean",0x313E}, +{"rieultikeutkorean",0x316A}, +{"rieulyeorinhieuhkorean",0x316D}, +{"rightangle",0x221F}, +{"righttackbelowcmb",0x0319}, +{"righttriangle",0x22BF}, +{"rihiragana",0x308A}, +{"rikatakana",0x30EA}, +{"rikatakanahalfwidth",0xFF98}, +{"ring",0x02DA}, +{"ringbelowcmb",0x0325}, +{"ringcmb",0x030A}, +{"ringhalfleft",0x02BF}, +{"ringhalfleftarmenian",0x0559}, +{"ringhalfleftbelowcmb",0x031C}, +{"ringhalfleftcentered",0x02D3}, +{"ringhalfright",0x02BE}, +{"ringhalfrightbelowcmb",0x0339}, +{"ringhalfrightcentered",0x02D2}, +{"rinvertedbreve",0x0213}, +{"rittorusquare",0x3351}, +{"rlinebelow",0x1E5F}, +{"rlongleg",0x027C}, +{"rlonglegturned",0x027A}, +{"rmonospace",0xFF52}, +{"rohiragana",0x308D}, +{"rokatakana",0x30ED}, +{"rokatakanahalfwidth",0xFF9B}, +{"roruathai",0x0E23}, +{"rparen",0x24AD}, +{"rrabengali",0x09DC}, +{"rradeva",0x0931}, +{"rragurmukhi",0x0A5C}, +{"rreharabic",0x0691}, +{"rrehfinalarabic",0xFB8D}, +{"rrvocalicbengali",0x09E0}, +{"rrvocalicdeva",0x0960}, +{"rrvocalicgujarati",0x0AE0}, +{"rrvocalicvowelsignbengali",0x09C4}, +{"rrvocalicvowelsigndeva",0x0944}, +{"rrvocalicvowelsigngujarati",0x0AC4}, +{"rsuperior",0xF6F1}, +{"rtblock",0x2590}, +{"rturned",0x0279}, +{"rturnedsuperior",0x02B4}, +{"ruhiragana",0x308B}, +{"rukatakana",0x30EB}, +{"rukatakanahalfwidth",0xFF99}, +{"rupeemarkbengali",0x09F2}, +{"rupeesignbengali",0x09F3}, +{"rupiah",0xF6DD}, +{"ruthai",0x0E24}, +{"rvocalicbengali",0x098B}, +{"rvocalicdeva",0x090B}, +{"rvocalicgujarati",0x0A8B}, +{"rvocalicvowelsignbengali",0x09C3}, +{"rvocalicvowelsigndeva",0x0943}, +{"rvocalicvowelsigngujarati",0x0AC3}, +{"s",0x0073}, +{"sabengali",0x09B8}, +{"sacute",0x015B}, +{"sacutedotaccent",0x1E65}, +{"sadarabic",0x0635}, +{"sadeva",0x0938}, +{"sadfinalarabic",0xFEBA}, +{"sadinitialarabic",0xFEBB}, +{"sadmedialarabic",0xFEBC}, +{"sagujarati",0x0AB8}, +{"sagurmukhi",0x0A38}, +{"sahiragana",0x3055}, +{"sakatakana",0x30B5}, +{"sakatakanahalfwidth",0xFF7B}, +{"sallallahoualayhewasallamarabic",0xFDFA}, +{"samekh",0x05E1}, +{"samekhdagesh",0xFB41}, +{"samekhdageshhebrew",0xFB41}, +{"samekhhebrew",0x05E1}, +{"saraaathai",0x0E32}, +{"saraaethai",0x0E41}, +{"saraaimaimalaithai",0x0E44}, +{"saraaimaimuanthai",0x0E43}, +{"saraamthai",0x0E33}, +{"saraathai",0x0E30}, +{"saraethai",0x0E40}, +{"saraiileftthai",0xF886}, +{"saraiithai",0x0E35}, +{"saraileftthai",0xF885}, +{"saraithai",0x0E34}, +{"saraothai",0x0E42}, +{"saraueeleftthai",0xF888}, +{"saraueethai",0x0E37}, +{"saraueleftthai",0xF887}, +{"sarauethai",0x0E36}, +{"sarauthai",0x0E38}, +{"sarauuthai",0x0E39}, +{"sbopomofo",0x3119}, +{"scaron",0x0161}, +{"scarondotaccent",0x1E67}, +{"scedilla",0x015F}, +{"schwa",0x0259}, +{"schwacyrillic",0x04D9}, +{"schwadieresiscyrillic",0x04DB}, +{"schwahook",0x025A}, +{"scircle",0x24E2}, +{"scircumflex",0x015D}, +{"scommaaccent",0x0219}, +{"sdotaccent",0x1E61}, +{"sdotbelow",0x1E63}, +{"sdotbelowdotaccent",0x1E69}, +{"seagullbelowcmb",0x033C}, +{"second",0x2033}, +{"secondtonechinese",0x02CA}, +{"section",0x00A7}, +{"seenarabic",0x0633}, +{"seenfinalarabic",0xFEB2}, +{"seeninitialarabic",0xFEB3}, +{"seenmedialarabic",0xFEB4}, +{"segol",0x05B6}, +{"segol13",0x05B6}, +{"segol1f",0x05B6}, +{"segol2c",0x05B6}, +{"segolhebrew",0x05B6}, +{"segolnarrowhebrew",0x05B6}, +{"segolquarterhebrew",0x05B6}, +{"segoltahebrew",0x0592}, +{"segolwidehebrew",0x05B6}, +{"seharmenian",0x057D}, +{"sehiragana",0x305B}, +{"sekatakana",0x30BB}, +{"sekatakanahalfwidth",0xFF7E}, +{"semicolon",0x003B}, +{"semicolonarabic",0x061B}, +{"semicolonmonospace",0xFF1B}, +{"semicolonsmall",0xFE54}, +{"semivoicedmarkkana",0x309C}, +{"semivoicedmarkkanahalfwidth",0xFF9F}, +{"sentisquare",0x3322}, +{"sentosquare",0x3323}, +{"seven",0x0037}, +{"sevenarabic",0x0667}, +{"sevenbengali",0x09ED}, +{"sevencircle",0x2466}, +{"sevencircleinversesansserif",0x2790}, +{"sevendeva",0x096D}, +{"seveneighths",0x215E}, +{"sevengujarati",0x0AED}, +{"sevengurmukhi",0x0A6D}, +{"sevenhackarabic",0x0667}, +{"sevenhangzhou",0x3027}, +{"sevenideographicparen",0x3226}, +{"seveninferior",0x2087}, +{"sevenmonospace",0xFF17}, +{"sevenoldstyle",0xF737}, +{"sevenparen",0x247A}, +{"sevenperiod",0x248E}, +{"sevenpersian",0x06F7}, +{"sevenroman",0x2176}, +{"sevensuperior",0x2077}, +{"seventeencircle",0x2470}, +{"seventeenparen",0x2484}, +{"seventeenperiod",0x2498}, +{"seventhai",0x0E57}, +{"sfthyphen",0x00AD}, +{"shaarmenian",0x0577}, +{"shabengali",0x09B6}, +{"shacyrillic",0x0448}, +{"shaddaarabic",0x0651}, +{"shaddadammaarabic",0xFC61}, +{"shaddadammatanarabic",0xFC5E}, +{"shaddafathaarabic",0xFC60}, +{"shaddakasraarabic",0xFC62}, +{"shaddakasratanarabic",0xFC5F}, +{"shade",0x2592}, +{"shadedark",0x2593}, +{"shadelight",0x2591}, +{"shademedium",0x2592}, +{"shadeva",0x0936}, +{"shagujarati",0x0AB6}, +{"shagurmukhi",0x0A36}, +{"shalshelethebrew",0x0593}, +{"shbopomofo",0x3115}, +{"shchacyrillic",0x0449}, +{"sheenarabic",0x0634}, +{"sheenfinalarabic",0xFEB6}, +{"sheeninitialarabic",0xFEB7}, +{"sheenmedialarabic",0xFEB8}, +{"sheicoptic",0x03E3}, +{"sheqel",0x20AA}, +{"sheqelhebrew",0x20AA}, +{"sheva",0x05B0}, +{"sheva115",0x05B0}, +{"sheva15",0x05B0}, +{"sheva22",0x05B0}, +{"sheva2e",0x05B0}, +{"shevahebrew",0x05B0}, +{"shevanarrowhebrew",0x05B0}, +{"shevaquarterhebrew",0x05B0}, +{"shevawidehebrew",0x05B0}, +{"shhacyrillic",0x04BB}, +{"shimacoptic",0x03ED}, +{"shin",0x05E9}, +{"shindagesh",0xFB49}, +{"shindageshhebrew",0xFB49}, +{"shindageshshindot",0xFB2C}, +{"shindageshshindothebrew",0xFB2C}, +{"shindageshsindot",0xFB2D}, +{"shindageshsindothebrew",0xFB2D}, +{"shindothebrew",0x05C1}, +{"shinhebrew",0x05E9}, +{"shinshindot",0xFB2A}, +{"shinshindothebrew",0xFB2A}, +{"shinsindot",0xFB2B}, +{"shinsindothebrew",0xFB2B}, +{"shook",0x0282}, +{"sigma",0x03C3}, +{"sigma1",0x03C2}, +{"sigmafinal",0x03C2}, +{"sigmalunatesymbolgreek",0x03F2}, +{"sihiragana",0x3057}, +{"sikatakana",0x30B7}, +{"sikatakanahalfwidth",0xFF7C}, +{"siluqhebrew",0x05BD}, +{"siluqlefthebrew",0x05BD}, +{"similar",0x223C}, +{"sindothebrew",0x05C2}, +{"siosacirclekorean",0x3274}, +{"siosaparenkorean",0x3214}, +{"sioscieuckorean",0x317E}, +{"sioscirclekorean",0x3266}, +{"sioskiyeokkorean",0x317A}, +{"sioskorean",0x3145}, +{"siosnieunkorean",0x317B}, +{"siosparenkorean",0x3206}, +{"siospieupkorean",0x317D}, +{"siostikeutkorean",0x317C}, +{"six",0x0036}, +{"sixarabic",0x0666}, +{"sixbengali",0x09EC}, +{"sixcircle",0x2465}, +{"sixcircleinversesansserif",0x278F}, +{"sixdeva",0x096C}, +{"sixgujarati",0x0AEC}, +{"sixgurmukhi",0x0A6C}, +{"sixhackarabic",0x0666}, +{"sixhangzhou",0x3026}, +{"sixideographicparen",0x3225}, +{"sixinferior",0x2086}, +{"sixmonospace",0xFF16}, +{"sixoldstyle",0xF736}, +{"sixparen",0x2479}, +{"sixperiod",0x248D}, +{"sixpersian",0x06F6}, +{"sixroman",0x2175}, +{"sixsuperior",0x2076}, +{"sixteencircle",0x246F}, +{"sixteencurrencydenominatorbengali",0x09F9}, +{"sixteenparen",0x2483}, +{"sixteenperiod",0x2497}, +{"sixthai",0x0E56}, +{"slash",0x002F}, +{"slashmonospace",0xFF0F}, +{"slong",0x017F}, +{"slongdotaccent",0x1E9B}, +{"smileface",0x263A}, +{"smonospace",0xFF53}, +{"sofpasuqhebrew",0x05C3}, +{"softhyphen",0x00AD}, +{"softsigncyrillic",0x044C}, +{"sohiragana",0x305D}, +{"sokatakana",0x30BD}, +{"sokatakanahalfwidth",0xFF7F}, +{"soliduslongoverlaycmb",0x0338}, +{"solidusshortoverlaycmb",0x0337}, +{"sorusithai",0x0E29}, +{"sosalathai",0x0E28}, +{"sosothai",0x0E0B}, +{"sosuathai",0x0E2A}, +{"space",0x0020}, +{"spacehackarabic",0x0020}, +{"spade",0x2660}, +{"spadesuitblack",0x2660}, +{"spadesuitwhite",0x2664}, +{"sparen",0x24AE}, +{"squarebelowcmb",0x033B}, +{"squarecc",0x33C4}, +{"squarecm",0x339D}, +{"squarediagonalcrosshatchfill",0x25A9}, +{"squarehorizontalfill",0x25A4}, +{"squarekg",0x338F}, +{"squarekm",0x339E}, +{"squarekmcapital",0x33CE}, +{"squareln",0x33D1}, +{"squarelog",0x33D2}, +{"squaremg",0x338E}, +{"squaremil",0x33D5}, +{"squaremm",0x339C}, +{"squaremsquared",0x33A1}, +{"squareorthogonalcrosshatchfill",0x25A6}, +{"squareupperlefttolowerrightfill",0x25A7}, +{"squareupperrighttolowerleftfill",0x25A8}, +{"squareverticalfill",0x25A5}, +{"squarewhitewithsmallblack",0x25A3}, +{"srsquare",0x33DB}, +{"ssabengali",0x09B7}, +{"ssadeva",0x0937}, +{"ssagujarati",0x0AB7}, +{"ssangcieuckorean",0x3149}, +{"ssanghieuhkorean",0x3185}, +{"ssangieungkorean",0x3180}, +{"ssangkiyeokkorean",0x3132}, +{"ssangnieunkorean",0x3165}, +{"ssangpieupkorean",0x3143}, +{"ssangsioskorean",0x3146}, +{"ssangtikeutkorean",0x3138}, +{"ssuperior",0xF6F2}, +{"sterling",0x00A3}, +{"sterlingmonospace",0xFFE1}, +{"strokelongoverlaycmb",0x0336}, +{"strokeshortoverlaycmb",0x0335}, +{"subset",0x2282}, +{"subsetnotequal",0x228A}, +{"subsetorequal",0x2286}, +{"succeeds",0x227B}, +{"suchthat",0x220B}, +{"suhiragana",0x3059}, +{"sukatakana",0x30B9}, +{"sukatakanahalfwidth",0xFF7D}, +{"sukunarabic",0x0652}, +{"summation",0x2211}, +{"sun",0x263C}, +{"superset",0x2283}, +{"supersetnotequal",0x228B}, +{"supersetorequal",0x2287}, +{"svsquare",0x33DC}, +{"syouwaerasquare",0x337C}, +{"t",0x0074}, +{"tabengali",0x09A4}, +{"tackdown",0x22A4}, +{"tackleft",0x22A3}, +{"tadeva",0x0924}, +{"tagujarati",0x0AA4}, +{"tagurmukhi",0x0A24}, +{"taharabic",0x0637}, +{"tahfinalarabic",0xFEC2}, +{"tahinitialarabic",0xFEC3}, +{"tahiragana",0x305F}, +{"tahmedialarabic",0xFEC4}, +{"taisyouerasquare",0x337D}, +{"takatakana",0x30BF}, +{"takatakanahalfwidth",0xFF80}, +{"tatweelarabic",0x0640}, +{"tau",0x03C4}, +{"tav",0x05EA}, +{"tavdages",0xFB4A}, +{"tavdagesh",0xFB4A}, +{"tavdageshhebrew",0xFB4A}, +{"tavhebrew",0x05EA}, +{"tbar",0x0167}, +{"tbopomofo",0x310A}, +{"tcaron",0x0165}, +{"tccurl",0x02A8}, +{"tcedilla",0x0163}, +{"tcheharabic",0x0686}, +{"tchehfinalarabic",0xFB7B}, +{"tchehinitialarabic",0xFB7C}, +{"tchehmedialarabic",0xFB7D}, +{"tcircle",0x24E3}, +{"tcircumflexbelow",0x1E71}, +{"tcommaaccent",0x0163}, +{"tdieresis",0x1E97}, +{"tdotaccent",0x1E6B}, +{"tdotbelow",0x1E6D}, +{"tecyrillic",0x0442}, +{"tedescendercyrillic",0x04AD}, +{"teharabic",0x062A}, +{"tehfinalarabic",0xFE96}, +{"tehhahinitialarabic",0xFCA2}, +{"tehhahisolatedarabic",0xFC0C}, +{"tehinitialarabic",0xFE97}, +{"tehiragana",0x3066}, +{"tehjeeminitialarabic",0xFCA1}, +{"tehjeemisolatedarabic",0xFC0B}, +{"tehmarbutaarabic",0x0629}, +{"tehmarbutafinalarabic",0xFE94}, +{"tehmedialarabic",0xFE98}, +{"tehmeeminitialarabic",0xFCA4}, +{"tehmeemisolatedarabic",0xFC0E}, +{"tehnoonfinalarabic",0xFC73}, +{"tekatakana",0x30C6}, +{"tekatakanahalfwidth",0xFF83}, +{"telephone",0x2121}, +{"telephoneblack",0x260E}, +{"telishagedolahebrew",0x05A0}, +{"telishaqetanahebrew",0x05A9}, +{"tencircle",0x2469}, +{"tenideographicparen",0x3229}, +{"tenparen",0x247D}, +{"tenperiod",0x2491}, +{"tenroman",0x2179}, +{"tesh",0x02A7}, +{"tet",0x05D8}, +{"tetdagesh",0xFB38}, +{"tetdageshhebrew",0xFB38}, +{"tethebrew",0x05D8}, +{"tetsecyrillic",0x04B5}, +{"tevirhebrew",0x059B}, +{"tevirlefthebrew",0x059B}, +{"thabengali",0x09A5}, +{"thadeva",0x0925}, +{"thagujarati",0x0AA5}, +{"thagurmukhi",0x0A25}, +{"thalarabic",0x0630}, +{"thalfinalarabic",0xFEAC}, +{"thanthakhatlowleftthai",0xF898}, +{"thanthakhatlowrightthai",0xF897}, +{"thanthakhatthai",0x0E4C}, +{"thanthakhatupperleftthai",0xF896}, +{"theharabic",0x062B}, +{"thehfinalarabic",0xFE9A}, +{"thehinitialarabic",0xFE9B}, +{"thehmedialarabic",0xFE9C}, +{"thereexists",0x2203}, +{"therefore",0x2234}, +{"theta",0x03B8}, +{"theta1",0x03D1}, +{"thetasymbolgreek",0x03D1}, +{"thieuthacirclekorean",0x3279}, +{"thieuthaparenkorean",0x3219}, +{"thieuthcirclekorean",0x326B}, +{"thieuthkorean",0x314C}, +{"thieuthparenkorean",0x320B}, +{"thirteencircle",0x246C}, +{"thirteenparen",0x2480}, +{"thirteenperiod",0x2494}, +{"thonangmonthothai",0x0E11}, +{"thook",0x01AD}, +{"thophuthaothai",0x0E12}, +{"thorn",0x00FE}, +{"thothahanthai",0x0E17}, +{"thothanthai",0x0E10}, +{"thothongthai",0x0E18}, +{"thothungthai",0x0E16}, +{"thousandcyrillic",0x0482}, +{"thousandsseparatorarabic",0x066C}, +{"thousandsseparatorpersian",0x066C}, +{"three",0x0033}, +{"threearabic",0x0663}, +{"threebengali",0x09E9}, +{"threecircle",0x2462}, +{"threecircleinversesansserif",0x278C}, +{"threedeva",0x0969}, +{"threeeighths",0x215C}, +{"threegujarati",0x0AE9}, +{"threegurmukhi",0x0A69}, +{"threehackarabic",0x0663}, +{"threehangzhou",0x3023}, +{"threeideographicparen",0x3222}, +{"threeinferior",0x2083}, +{"threemonospace",0xFF13}, +{"threenumeratorbengali",0x09F6}, +{"threeoldstyle",0xF733}, +{"threeparen",0x2476}, +{"threeperiod",0x248A}, +{"threepersian",0x06F3}, +{"threequarters",0x00BE}, +{"threequartersemdash",0xF6DE}, +{"threeroman",0x2172}, +{"threesuperior",0x00B3}, +{"threethai",0x0E53}, +{"thzsquare",0x3394}, +{"tihiragana",0x3061}, +{"tikatakana",0x30C1}, +{"tikatakanahalfwidth",0xFF81}, +{"tikeutacirclekorean",0x3270}, +{"tikeutaparenkorean",0x3210}, +{"tikeutcirclekorean",0x3262}, +{"tikeutkorean",0x3137}, +{"tikeutparenkorean",0x3202}, +{"tilde",0x02DC}, +{"tildebelowcmb",0x0330}, +{"tildecmb",0x0303}, +{"tildecomb",0x0303}, +{"tildedoublecmb",0x0360}, +{"tildeoperator",0x223C}, +{"tildeoverlaycmb",0x0334}, +{"tildeverticalcmb",0x033E}, +{"timescircle",0x2297}, +{"tipehahebrew",0x0596}, +{"tipehalefthebrew",0x0596}, +{"tippigurmukhi",0x0A70}, +{"titlocyrilliccmb",0x0483}, +{"tiwnarmenian",0x057F}, +{"tlinebelow",0x1E6F}, +{"tmonospace",0xFF54}, +{"toarmenian",0x0569}, +{"tohiragana",0x3068}, +{"tokatakana",0x30C8}, +{"tokatakanahalfwidth",0xFF84}, +{"tonebarextrahighmod",0x02E5}, +{"tonebarextralowmod",0x02E9}, +{"tonebarhighmod",0x02E6}, +{"tonebarlowmod",0x02E8}, +{"tonebarmidmod",0x02E7}, +{"tonefive",0x01BD}, +{"tonesix",0x0185}, +{"tonetwo",0x01A8}, +{"tonos",0x0384}, +{"tonsquare",0x3327}, +{"topatakthai",0x0E0F}, +{"tortoiseshellbracketleft",0x3014}, +{"tortoiseshellbracketleftsmall",0xFE5D}, +{"tortoiseshellbracketleftvertical",0xFE39}, +{"tortoiseshellbracketright",0x3015}, +{"tortoiseshellbracketrightsmall",0xFE5E}, +{"tortoiseshellbracketrightvertical",0xFE3A}, +{"totaothai",0x0E15}, +{"tpalatalhook",0x01AB}, +{"tparen",0x24AF}, +{"trademark",0x2122}, +{"trademarksans",0xF8EA}, +{"trademarkserif",0xF6DB}, +{"tretroflexhook",0x0288}, +{"triagdn",0x25BC}, +{"triaglf",0x25C4}, +{"triagrt",0x25BA}, +{"triagup",0x25B2}, +{"ts",0x02A6}, +{"tsadi",0x05E6}, +{"tsadidagesh",0xFB46}, +{"tsadidageshhebrew",0xFB46}, +{"tsadihebrew",0x05E6}, +{"tsecyrillic",0x0446}, +{"tsere",0x05B5}, +{"tsere12",0x05B5}, +{"tsere1e",0x05B5}, +{"tsere2b",0x05B5}, +{"tserehebrew",0x05B5}, +{"tserenarrowhebrew",0x05B5}, +{"tserequarterhebrew",0x05B5}, +{"tserewidehebrew",0x05B5}, +{"tshecyrillic",0x045B}, +{"tsuperior",0xF6F3}, +{"ttabengali",0x099F}, +{"ttadeva",0x091F}, +{"ttagujarati",0x0A9F}, +{"ttagurmukhi",0x0A1F}, +{"tteharabic",0x0679}, +{"ttehfinalarabic",0xFB67}, +{"ttehinitialarabic",0xFB68}, +{"ttehmedialarabic",0xFB69}, +{"tthabengali",0x09A0}, +{"tthadeva",0x0920}, +{"tthagujarati",0x0AA0}, +{"tthagurmukhi",0x0A20}, +{"tturned",0x0287}, +{"tuhiragana",0x3064}, +{"tukatakana",0x30C4}, +{"tukatakanahalfwidth",0xFF82}, +{"tusmallhiragana",0x3063}, +{"tusmallkatakana",0x30C3}, +{"tusmallkatakanahalfwidth",0xFF6F}, +{"twelvecircle",0x246B}, +{"twelveparen",0x247F}, +{"twelveperiod",0x2493}, +{"twelveroman",0x217B}, +{"twentycircle",0x2473}, +{"twentyhangzhou",0x5344}, +{"twentyparen",0x2487}, +{"twentyperiod",0x249B}, +{"two",0x0032}, +{"twoarabic",0x0662}, +{"twobengali",0x09E8}, +{"twocircle",0x2461}, +{"twocircleinversesansserif",0x278B}, +{"twodeva",0x0968}, +{"twodotenleader",0x2025}, +{"twodotleader",0x2025}, +{"twodotleadervertical",0xFE30}, +{"twogujarati",0x0AE8}, +{"twogurmukhi",0x0A68}, +{"twohackarabic",0x0662}, +{"twohangzhou",0x3022}, +{"twoideographicparen",0x3221}, +{"twoinferior",0x2082}, +{"twomonospace",0xFF12}, +{"twonumeratorbengali",0x09F5}, +{"twooldstyle",0xF732}, +{"twoparen",0x2475}, +{"twoperiod",0x2489}, +{"twopersian",0x06F2}, +{"tworoman",0x2171}, +{"twostroke",0x01BB}, +{"twosuperior",0x00B2}, +{"twothai",0x0E52}, +{"twothirds",0x2154}, +{"u",0x0075}, +{"uacute",0x00FA}, +{"ubar",0x0289}, +{"ubengali",0x0989}, +{"ubopomofo",0x3128}, +{"ubreve",0x016D}, +{"ucaron",0x01D4}, +{"ucircle",0x24E4}, +{"ucircumflex",0x00FB}, +{"ucircumflexbelow",0x1E77}, +{"ucyrillic",0x0443}, +{"udattadeva",0x0951}, +{"udblacute",0x0171}, +{"udblgrave",0x0215}, +{"udeva",0x0909}, +{"udieresis",0x00FC}, +{"udieresisacute",0x01D8}, +{"udieresisbelow",0x1E73}, +{"udieresiscaron",0x01DA}, +{"udieresiscyrillic",0x04F1}, +{"udieresisgrave",0x01DC}, +{"udieresismacron",0x01D6}, +{"udotbelow",0x1EE5}, +{"ugrave",0x00F9}, +{"ugujarati",0x0A89}, +{"ugurmukhi",0x0A09}, +{"uhiragana",0x3046}, +{"uhookabove",0x1EE7}, +{"uhorn",0x01B0}, +{"uhornacute",0x1EE9}, +{"uhorndotbelow",0x1EF1}, +{"uhorngrave",0x1EEB}, +{"uhornhookabove",0x1EED}, +{"uhorntilde",0x1EEF}, +{"uhungarumlaut",0x0171}, +{"uhungarumlautcyrillic",0x04F3}, +{"uinvertedbreve",0x0217}, +{"ukatakana",0x30A6}, +{"ukatakanahalfwidth",0xFF73}, +{"ukcyrillic",0x0479}, +{"ukorean",0x315C}, +{"umacron",0x016B}, +{"umacroncyrillic",0x04EF}, +{"umacrondieresis",0x1E7B}, +{"umatragurmukhi",0x0A41}, +{"umonospace",0xFF55}, +{"underscore",0x005F}, +{"underscoredbl",0x2017}, +{"underscoremonospace",0xFF3F}, +{"underscorevertical",0xFE33}, +{"underscorewavy",0xFE4F}, +{"union",0x222A}, +{"universal",0x2200}, +{"uogonek",0x0173}, +{"uparen",0x24B0}, +{"upblock",0x2580}, +{"upperdothebrew",0x05C4}, +{"upsilon",0x03C5}, +{"upsilondieresis",0x03CB}, +{"upsilondieresistonos",0x03B0}, +{"upsilonlatin",0x028A}, +{"upsilontonos",0x03CD}, +{"uptackbelowcmb",0x031D}, +{"uptackmod",0x02D4}, +{"uragurmukhi",0x0A73}, +{"uring",0x016F}, +{"ushortcyrillic",0x045E}, +{"usmallhiragana",0x3045}, +{"usmallkatakana",0x30A5}, +{"usmallkatakanahalfwidth",0xFF69}, +{"ustraightcyrillic",0x04AF}, +{"ustraightstrokecyrillic",0x04B1}, +{"utilde",0x0169}, +{"utildeacute",0x1E79}, +{"utildebelow",0x1E75}, +{"uubengali",0x098A}, +{"uudeva",0x090A}, +{"uugujarati",0x0A8A}, +{"uugurmukhi",0x0A0A}, +{"uumatragurmukhi",0x0A42}, +{"uuvowelsignbengali",0x09C2}, +{"uuvowelsigndeva",0x0942}, +{"uuvowelsigngujarati",0x0AC2}, +{"uvowelsignbengali",0x09C1}, +{"uvowelsigndeva",0x0941}, +{"uvowelsigngujarati",0x0AC1}, +{"v",0x0076}, +{"vadeva",0x0935}, +{"vagujarati",0x0AB5}, +{"vagurmukhi",0x0A35}, +{"vakatakana",0x30F7}, +{"vav",0x05D5}, +{"vavdagesh",0xFB35}, +{"vavdagesh65",0xFB35}, +{"vavdageshhebrew",0xFB35}, +{"vavhebrew",0x05D5}, +{"vavholam",0xFB4B}, +{"vavholamhebrew",0xFB4B}, +{"vavvavhebrew",0x05F0}, +{"vavyodhebrew",0x05F1}, +{"vcircle",0x24E5}, +{"vdotbelow",0x1E7F}, +{"vecyrillic",0x0432}, +{"veharabic",0x06A4}, +{"vehfinalarabic",0xFB6B}, +{"vehinitialarabic",0xFB6C}, +{"vehmedialarabic",0xFB6D}, +{"vekatakana",0x30F9}, +{"venus",0x2640}, +{"verticalbar",0x007C}, +{"verticallineabovecmb",0x030D}, +{"verticallinebelowcmb",0x0329}, +{"verticallinelowmod",0x02CC}, +{"verticallinemod",0x02C8}, +{"vewarmenian",0x057E}, +{"vhook",0x028B}, +{"vikatakana",0x30F8}, +{"viramabengali",0x09CD}, +{"viramadeva",0x094D}, +{"viramagujarati",0x0ACD}, +{"visargabengali",0x0983}, +{"visargadeva",0x0903}, +{"visargagujarati",0x0A83}, +{"vmonospace",0xFF56}, +{"voarmenian",0x0578}, +{"voicediterationhiragana",0x309E}, +{"voicediterationkatakana",0x30FE}, +{"voicedmarkkana",0x309B}, +{"voicedmarkkanahalfwidth",0xFF9E}, +{"vokatakana",0x30FA}, +{"vparen",0x24B1}, +{"vtilde",0x1E7D}, +{"vturned",0x028C}, +{"vuhiragana",0x3094}, +{"vukatakana",0x30F4}, +{"w",0x0077}, +{"wacute",0x1E83}, +{"waekorean",0x3159}, +{"wahiragana",0x308F}, +{"wakatakana",0x30EF}, +{"wakatakanahalfwidth",0xFF9C}, +{"wakorean",0x3158}, +{"wasmallhiragana",0x308E}, +{"wasmallkatakana",0x30EE}, +{"wattosquare",0x3357}, +{"wavedash",0x301C}, +{"wavyunderscorevertical",0xFE34}, +{"wawarabic",0x0648}, +{"wawfinalarabic",0xFEEE}, +{"wawhamzaabovearabic",0x0624}, +{"wawhamzaabovefinalarabic",0xFE86}, +{"wbsquare",0x33DD}, +{"wcircle",0x24E6}, +{"wcircumflex",0x0175}, +{"wdieresis",0x1E85}, +{"wdotaccent",0x1E87}, +{"wdotbelow",0x1E89}, +{"wehiragana",0x3091}, +{"weierstrass",0x2118}, +{"wekatakana",0x30F1}, +{"wekorean",0x315E}, +{"weokorean",0x315D}, +{"wgrave",0x1E81}, +{"whitebullet",0x25E6}, +{"whitecircle",0x25CB}, +{"whitecircleinverse",0x25D9}, +{"whitecornerbracketleft",0x300E}, +{"whitecornerbracketleftvertical",0xFE43}, +{"whitecornerbracketright",0x300F}, +{"whitecornerbracketrightvertical",0xFE44}, +{"whitediamond",0x25C7}, +{"whitediamondcontainingblacksmalldiamond",0x25C8}, +{"whitedownpointingsmalltriangle",0x25BF}, +{"whitedownpointingtriangle",0x25BD}, +{"whiteleftpointingsmalltriangle",0x25C3}, +{"whiteleftpointingtriangle",0x25C1}, +{"whitelenticularbracketleft",0x3016}, +{"whitelenticularbracketright",0x3017}, +{"whiterightpointingsmalltriangle",0x25B9}, +{"whiterightpointingtriangle",0x25B7}, +{"whitesmallsquare",0x25AB}, +{"whitesmilingface",0x263A}, +{"whitesquare",0x25A1}, +{"whitestar",0x2606}, +{"whitetelephone",0x260F}, +{"whitetortoiseshellbracketleft",0x3018}, +{"whitetortoiseshellbracketright",0x3019}, +{"whiteuppointingsmalltriangle",0x25B5}, +{"whiteuppointingtriangle",0x25B3}, +{"wihiragana",0x3090}, +{"wikatakana",0x30F0}, +{"wikorean",0x315F}, +{"wmonospace",0xFF57}, +{"wohiragana",0x3092}, +{"wokatakana",0x30F2}, +{"wokatakanahalfwidth",0xFF66}, +{"won",0x20A9}, +{"wonmonospace",0xFFE6}, +{"wowaenthai",0x0E27}, +{"wparen",0x24B2}, +{"wring",0x1E98}, +{"wsuperior",0x02B7}, +{"wturned",0x028D}, +{"wynn",0x01BF}, +{"x",0x0078}, +{"xabovecmb",0x033D}, +{"xbopomofo",0x3112}, +{"xcircle",0x24E7}, +{"xdieresis",0x1E8D}, +{"xdotaccent",0x1E8B}, +{"xeharmenian",0x056D}, +{"xi",0x03BE}, +{"xmonospace",0xFF58}, +{"xparen",0x24B3}, +{"xsuperior",0x02E3}, +{"y",0x0079}, +{"yaadosquare",0x334E}, +{"yabengali",0x09AF}, +{"yacute",0x00FD}, +{"yadeva",0x092F}, +{"yaekorean",0x3152}, +{"yagujarati",0x0AAF}, +{"yagurmukhi",0x0A2F}, +{"yahiragana",0x3084}, +{"yakatakana",0x30E4}, +{"yakatakanahalfwidth",0xFF94}, +{"yakorean",0x3151}, +{"yamakkanthai",0x0E4E}, +{"yasmallhiragana",0x3083}, +{"yasmallkatakana",0x30E3}, +{"yasmallkatakanahalfwidth",0xFF6C}, +{"yatcyrillic",0x0463}, +{"ycircle",0x24E8}, +{"ycircumflex",0x0177}, +{"ydieresis",0x00FF}, +{"ydotaccent",0x1E8F}, +{"ydotbelow",0x1EF5}, +{"yeharabic",0x064A}, +{"yehbarreearabic",0x06D2}, +{"yehbarreefinalarabic",0xFBAF}, +{"yehfinalarabic",0xFEF2}, +{"yehhamzaabovearabic",0x0626}, +{"yehhamzaabovefinalarabic",0xFE8A}, +{"yehhamzaaboveinitialarabic",0xFE8B}, +{"yehhamzaabovemedialarabic",0xFE8C}, +{"yehinitialarabic",0xFEF3}, +{"yehmedialarabic",0xFEF4}, +{"yehmeeminitialarabic",0xFCDD}, +{"yehmeemisolatedarabic",0xFC58}, +{"yehnoonfinalarabic",0xFC94}, +{"yehthreedotsbelowarabic",0x06D1}, +{"yekorean",0x3156}, +{"yen",0x00A5}, +{"yenmonospace",0xFFE5}, +{"yeokorean",0x3155}, +{"yeorinhieuhkorean",0x3186}, +{"yerahbenyomohebrew",0x05AA}, +{"yerahbenyomolefthebrew",0x05AA}, +{"yericyrillic",0x044B}, +{"yerudieresiscyrillic",0x04F9}, +{"yesieungkorean",0x3181}, +{"yesieungpansioskorean",0x3183}, +{"yesieungsioskorean",0x3182}, +{"yetivhebrew",0x059A}, +{"ygrave",0x1EF3}, +{"yhook",0x01B4}, +{"yhookabove",0x1EF7}, +{"yiarmenian",0x0575}, +{"yicyrillic",0x0457}, +{"yikorean",0x3162}, +{"yinyang",0x262F}, +{"yiwnarmenian",0x0582}, +{"ymonospace",0xFF59}, +{"yod",0x05D9}, +{"yoddagesh",0xFB39}, +{"yoddageshhebrew",0xFB39}, +{"yodhebrew",0x05D9}, +{"yodyodhebrew",0x05F2}, +{"yodyodpatahhebrew",0xFB1F}, +{"yohiragana",0x3088}, +{"yoikorean",0x3189}, +{"yokatakana",0x30E8}, +{"yokatakanahalfwidth",0xFF96}, +{"yokorean",0x315B}, +{"yosmallhiragana",0x3087}, +{"yosmallkatakana",0x30E7}, +{"yosmallkatakanahalfwidth",0xFF6E}, +{"yotgreek",0x03F3}, +{"yoyaekorean",0x3188}, +{"yoyakorean",0x3187}, +{"yoyakthai",0x0E22}, +{"yoyingthai",0x0E0D}, +{"yparen",0x24B4}, +{"ypogegrammeni",0x037A}, +{"ypogegrammenigreekcmb",0x0345}, +{"yr",0x01A6}, +{"yring",0x1E99}, +{"ysuperior",0x02B8}, +{"ytilde",0x1EF9}, +{"yturned",0x028E}, +{"yuhiragana",0x3086}, +{"yuikorean",0x318C}, +{"yukatakana",0x30E6}, +{"yukatakanahalfwidth",0xFF95}, +{"yukorean",0x3160}, +{"yusbigcyrillic",0x046B}, +{"yusbigiotifiedcyrillic",0x046D}, +{"yuslittlecyrillic",0x0467}, +{"yuslittleiotifiedcyrillic",0x0469}, +{"yusmallhiragana",0x3085}, +{"yusmallkatakana",0x30E5}, +{"yusmallkatakanahalfwidth",0xFF6D}, +{"yuyekorean",0x318B}, +{"yuyeokorean",0x318A}, +{"yyabengali",0x09DF}, +{"yyadeva",0x095F}, +{"z",0x007A}, +{"zaarmenian",0x0566}, +{"zacute",0x017A}, +{"zadeva",0x095B}, +{"zagurmukhi",0x0A5B}, +{"zaharabic",0x0638}, +{"zahfinalarabic",0xFEC6}, +{"zahinitialarabic",0xFEC7}, +{"zahiragana",0x3056}, +{"zahmedialarabic",0xFEC8}, +{"zainarabic",0x0632}, +{"zainfinalarabic",0xFEB0}, +{"zakatakana",0x30B6}, +{"zaqefgadolhebrew",0x0595}, +{"zaqefqatanhebrew",0x0594}, +{"zarqahebrew",0x0598}, +{"zayin",0x05D6}, +{"zayindagesh",0xFB36}, +{"zayindageshhebrew",0xFB36}, +{"zayinhebrew",0x05D6}, +{"zbopomofo",0x3117}, +{"zcaron",0x017E}, +{"zcircle",0x24E9}, +{"zcircumflex",0x1E91}, +{"zcurl",0x0291}, +{"zdot",0x017C}, +{"zdotaccent",0x017C}, +{"zdotbelow",0x1E93}, +{"zecyrillic",0x0437}, +{"zedescendercyrillic",0x0499}, +{"zedieresiscyrillic",0x04DF}, +{"zehiragana",0x305C}, +{"zekatakana",0x30BC}, +{"zero",0x0030}, +{"zeroarabic",0x0660}, +{"zerobengali",0x09E6}, +{"zerodeva",0x0966}, +{"zerogujarati",0x0AE6}, +{"zerogurmukhi",0x0A66}, +{"zerohackarabic",0x0660}, +{"zeroinferior",0x2080}, +{"zeromonospace",0xFF10}, +{"zerooldstyle",0xF730}, +{"zeropersian",0x06F0}, +{"zerosuperior",0x2070}, +{"zerothai",0x0E50}, +{"zerowidthjoiner",0xFEFF}, +{"zerowidthnonjoiner",0x200C}, +{"zerowidthspace",0x200B}, +{"zeta",0x03B6}, +{"zhbopomofo",0x3113}, +{"zhearmenian",0x056A}, +{"zhebrevecyrillic",0x04C2}, +{"zhecyrillic",0x0436}, +{"zhedescendercyrillic",0x0497}, +{"zhedieresiscyrillic",0x04DD}, +{"zihiragana",0x3058}, +{"zikatakana",0x30B8}, +{"zinorhebrew",0x05AE}, +{"zlinebelow",0x1E95}, +{"zmonospace",0xFF5A}, +{"zohiragana",0x305E}, +{"zokatakana",0x30BE}, +{"zparen",0x24B5}, +{"zretroflexhook",0x0290}, +{"zstroke",0x01B6}, +{"zuhiragana",0x305A}, +{"zukatakana",0x30BA}, + +/* deleted because they decompose to more than one character */ +/* +{"dalethatafpatah",0x05D3 05B2}, +{"dalethatafpatahhebrew",0x05D3 05B2}, +{"dalethatafsegol",0x05D3 05B1}, +{"dalethatafsegolhebrew",0x05D3 05B1}, +{"dalethiriq",0x05D3 05B4}, +{"dalethiriqhebrew",0x05D3 05B4}, +{"daletholam",0x05D3 05B9}, +{"daletholamhebrew",0x05D3 05B9}, +{"daletpatah",0x05D3 05B7}, +{"daletpatahhebrew",0x05D3 05B7}, +{"daletqamats",0x05D3 05B8}, +{"daletqamatshebrew",0x05D3 05B8}, +{"daletqubuts",0x05D3 05BB}, +{"daletqubutshebrew",0x05D3 05BB}, +{"daletsegol",0x05D3 05B6}, +{"daletsegolhebrew",0x05D3 05B6}, +{"daletsheva",0x05D3 05B0}, +{"daletshevahebrew",0x05D3 05B0}, +{"dalettsere",0x05D3 05B5}, +{"dalettserehebrew",0x05D3 05B5}, +{"finalkafqamats",0x05DA 05B8}, +{"finalkafqamatshebrew",0x05DA 05B8}, +{"finalkafsheva",0x05DA 05B0}, +{"finalkafshevahebrew",0x05DA 05B0}, +{"hamzadammaarabic",0x0621 064F}, +{"hamzadammatanarabic",0x0621 064C}, +{"hamzafathaarabic",0x0621 064E}, +{"hamzafathatanarabic",0x0621 064B}, +{"hamzalowkasraarabic",0x0621 0650}, +{"hamzalowkasratanarabic",0x0621 064D}, +{"hamzasukunarabic",0x0621 0652}, +{"lamedholam",0x05DC 05B9}, +{"lamedholamdagesh",0x05DC 05B9 05BC}, +{"lamedholamdageshhebrew",0x05DC 05B9 05BC}, +{"lamedholamhebrew",0x05DC 05B9}, +{"lammeemjeeminitialarabic",0xFEDF FEE4 FEA0}, +{"lammeemkhahinitialarabic",0xFEDF FEE4 FEA8}, +{"noonhehinitialarabic",0xFEE7 FEEC}, +{"qofhatafpatah",0x05E7 05B2}, +{"qofhatafpatahhebrew",0x05E7 05B2}, +{"qofhatafsegol",0x05E7 05B1}, +{"qofhatafsegolhebrew",0x05E7 05B1}, +{"qofhiriq",0x05E7 05B4}, +{"qofhiriqhebrew",0x05E7 05B4}, +{"qofholam",0x05E7 05B9}, +{"qofholamhebrew",0x05E7 05B9}, +{"qofpatah",0x05E7 05B7}, +{"qofpatahhebrew",0x05E7 05B7}, +{"qofqamats",0x05E7 05B8}, +{"qofqamatshebrew",0x05E7 05B8}, +{"qofqubuts",0x05E7 05BB}, +{"qofqubutshebrew",0x05E7 05BB}, +{"qofsegol",0x05E7 05B6}, +{"qofsegolhebrew",0x05E7 05B6}, +{"qofsheva",0x05E7 05B0}, +{"qofshevahebrew",0x05E7 05B0}, +{"qoftsere",0x05E7 05B5}, +{"qoftserehebrew",0x05E7 05B5}, +{"rehyehaleflamarabic",0x0631 FEF3 FE8E 0644}, +{"reshhatafpatah",0x05E8 05B2}, +{"reshhatafpatahhebrew",0x05E8 05B2}, +{"reshhatafsegol",0x05E8 05B1}, +{"reshhatafsegolhebrew",0x05E8 05B1}, +{"reshhiriq",0x05E8 05B4}, +{"reshhiriqhebrew",0x05E8 05B4}, +{"reshholam",0x05E8 05B9}, +{"reshholamhebrew",0x05E8 05B9}, +{"reshpatah",0x05E8 05B7}, +{"reshpatahhebrew",0x05E8 05B7}, +{"reshqamats",0x05E8 05B8}, +{"reshqamatshebrew",0x05E8 05B8}, +{"reshqubuts",0x05E8 05BB}, +{"reshqubutshebrew",0x05E8 05BB}, +{"reshsegol",0x05E8 05B6}, +{"reshsegolhebrew",0x05E8 05B6}, +{"reshsheva",0x05E8 05B0}, +{"reshshevahebrew",0x05E8 05B0}, +{"reshtsere",0x05E8 05B5}, +{"reshtserehebrew",0x05E8 05B5}, +{"shaddafathatanarabic",0x0651 064B}, +{"tchehmeeminitialarabic",0xFB7C FEE4}, +*/ +}; + diff --git a/mupdf/fontenc.h b/mupdf/fontenc.h new file mode 100644 index 00000000..376a3f2d --- /dev/null +++ b/mupdf/fontenc.h @@ -0,0 +1,349 @@ +/* + * Built-in font tables + */ + +#define _notdef 0 + +static char *basefontnames[14][7] = +{ + { "Courier", "CourierNew", "CourierNewPSMT", 0 }, + { "Courier-Bold", "CourierNew,Bold", "Courier,Bold", + "CourierNewPS-BoldMT", "CourierNew-Bold", 0 }, + { "Courier-Oblique", "CourierNew,Italic", "Courier,Italic", + "CourierNewPS-ItalicMT", "CourierNew-Italic", 0 }, + { "Courier-BoldOblique", "CourierNew,BoldItalic", "Courier,BoldItalic", + "CourierNewPS-BoldItalicMT", "CourierNew-BoldItalic", 0 }, + { "Helvetica", "ArialMT", "Arial", 0 }, + { "Helvetica-Bold", "Arial-BoldMT", "Arial,Bold", "Arial-Bold", + "Helvetica,Bold", 0 }, + { "Helvetica-Oblique", "Arial-ItalicMT", "Arial,Italic", "Arial-Italic", + "Helvetica,Italic", "Helvetica-Italic", 0 }, + { "Helvetica-BoldOblique", "Arial-BoldItalicMT", + "Arial,BoldItalic", "Arial-BoldItalic", + "Helvetica,BoldItalic", "Helvetica-BoldItalic", 0 }, + { "Times-Roman", "TimesNewRomanPSMT", "TimesNewRoman", + "TimesNewRomanPS", 0 }, + { "Times-Bold", "TimesNewRomanPS-BoldMT", "TimesNewRoman,Bold", + "TimesNewRomanPS-Bold", "TimesNewRoman-Bold", 0 }, + { "Times-Italic", "TimesNewRomanPS-ItalicMT", "TimesNewRoman,Italic", + "TimesNewRomanPS-Italic", "TimesNewRoman-Italic", 0 }, + { "Times-BoldItalic", "TimesNewRomanPS-BoldItalicMT", + "TimesNewRoman,BoldItalic", "TimesNewRomanPS-BoldItalic", + "TimesNewRoman-BoldItalic", 0 }, + { "Symbol", 0 }, + { "ZapfDingbats", 0 } +}; + +static char *macroman[256] = { _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", + "ampersand", "quotesingle", "parenleft", "parenright", "asterisk", + "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", + "three", "four", "five", "six", "seven", "eight", "nine", "colon", + "semicolon", "less", "equal", "greater", "question", "at", "A", + "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", + "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", + "grave", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", + "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", + "y", "z", "braceleft", "bar", "braceright", "asciitilde", _notdef, + "Adieresis", "Aring", "Ccedilla", "Eacute", "Ntilde", "Odieresis", + "Udieresis", "aacute", "agrave", "acircumflex", "adieresis", "atilde", + "aring", "ccedilla", "eacute", "egrave", "ecircumflex", "edieresis", + "iacute", "igrave", "icircumflex", "idieresis", "ntilde", "oacute", + "ograve", "ocircumflex", "odieresis", "otilde", "uacute", "ugrave", + "ucircumflex", "udieresis", "dagger", "degree", "cent", "sterling", + "section", "bullet", "paragraph", "germandbls", "registered", + "copyright", "trademark", "acute", "dieresis", _notdef, "AE", + "Oslash", _notdef, "plusminus", _notdef, _notdef, "yen", "mu", + _notdef, _notdef, _notdef, _notdef, _notdef, "ordfeminine", + "ordmasculine", _notdef, "ae", "oslash", "questiondown", "exclamdown", + "logicalnot", _notdef, "florin", _notdef, _notdef, "guillemotleft", + "guillemotright", "ellipsis", "space", "Agrave", "Atilde", "Otilde", + "OE", "oe", "endash", "emdash", "quotedblleft", "quotedblright", + "quoteleft", "quoteright", "divide", _notdef, "ydieresis", + "Ydieresis", "fraction", "currency", "guilsinglleft", "guilsinglright", + "fi", "fl", "daggerdbl", "periodcentered", "quotesinglbase", + "quotedblbase", "perthousand", "Acircumflex", "Ecircumflex", "Aacute", + "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave", + "Oacute", "Ocircumflex", _notdef, "Ograve", "Uacute", "Ucircumflex", + "Ugrave", "dotlessi", "circumflex", "tilde", "macron", "breve", + "dotaccent", "ring", "cedilla", "hungarumlaut", "ogonek", "caron" }; + +static char *macexpert[256] = { _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + "space", "exclamsmall", "Hungarumlautsmall", "centoldstyle", + "dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall", + "parenleftsuperior", "parenrightsuperior", "twodotenleader", + "onedotenleader", "comma", "hyphen", "period", "fraction", + "zerooldstyle", "oneoldstyle", "twooldstyle", "threeoldstyle", + "fouroldstyle", "fiveoldstyle", "sixoldstyle", "sevenoldstyle", + "eightoldstyle", "nineoldstyle", "colon", "semicolon", _notdef, + "threequartersemdash", _notdef, "questionsmall", _notdef, + _notdef, _notdef, _notdef, "Ethsmall", _notdef, _notdef, + "onequarter", "onehalf", "threequarters", "oneeighth", "threeeighths", + "fiveeighths", "seveneighths", "onethird", "twothirds", _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, "ff", "fi", + "fl", "ffi", "ffl", "parenleftinferior", _notdef, "parenrightinferior", + "Circumflexsmall", "hypheninferior", "Gravesmall", "Asmall", "Bsmall", + "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall", "Ismall", + "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall", + "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", + "Xsmall", "Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", + "Tildesmall", _notdef, _notdef, "asuperior", "centsuperior", + _notdef, _notdef, _notdef, _notdef, "Aacutesmall", + "Agravesmall", "Acircumflexsmall", "Adieresissmall", "Atildesmall", + "Aringsmall", "Ccedillasmall", "Eacutesmall", "Egravesmall", + "Ecircumflexsmall", "Edieresissmall", "Iacutesmall", "Igravesmall", + "Icircumflexsmall", "Idieresissmall", "Ntildesmall", "Oacutesmall", + "Ogravesmall", "Ocircumflexsmall", "Odieresissmall", "Otildesmall", + "Uacutesmall", "Ugravesmall", "Ucircumflexsmall", "Udieresissmall", + _notdef, "eightsuperior", "fourinferior", "threeinferior", + "sixinferior", "eightinferior", "seveninferior", "Scaronsmall", + _notdef, "centinferior", "twoinferior", _notdef, "Dieresissmall", + _notdef, "Caronsmall", "osuperior", "fiveinferior", _notdef, + "commainferior", "periodinferior", "Yacutesmall", _notdef, + "dollarinferior", _notdef, _notdef, "Thornsmall", _notdef, + "nineinferior", "zeroinferior", "Zcaronsmall", "AEsmall", "Oslashsmall", + "questiondownsmall", "oneinferior", "Lslashsmall", _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, "Cedillasmall", + _notdef, _notdef, _notdef, _notdef, _notdef, "OEsmall", + "figuredash", "hyphensuperior", _notdef, _notdef, _notdef, + _notdef, "exclamdownsmall", _notdef, "Ydieresissmall", _notdef, + "onesuperior", "twosuperior", "threesuperior", "foursuperior", + "fivesuperior", "sixsuperior", "sevensuperior", "ninesuperior", + "zerosuperior", _notdef, "esuperior", "rsuperior", "tsuperior", + _notdef, _notdef, "isuperior", "ssuperior", "dsuperior", + _notdef, _notdef, _notdef, _notdef, _notdef, "lsuperior", + "Ogoneksmall", "Brevesmall", "Macronsmall", "bsuperior", "nsuperior", + "msuperior", "commasuperior", "periodsuperior", "Dotaccentsmall", + "Ringsmall", _notdef, _notdef, _notdef, _notdef }; + +static char *winansi[256] = { _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, "space", + "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", + "quotesingle", "parenleft", "parenright", "asterisk", "plus", + "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", + "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", + "less", "equal", "greater", "question", "at", "A", "B", "C", "D", + "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", + "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", + "backslash", "bracketright", "asciicircum", "underscore", "grave", + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", + "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", + "braceleft", "bar", "braceright", "asciitilde", "bullet", "Euro", + "bullet", "quotesinglbase", "florin", "quotedblbase", "ellipsis", + "dagger", "daggerdbl", "circumflex", "perthousand", "Scaron", + "guilsinglleft", "OE", "bullet", "Zcaron", "bullet", "bullet", + "quoteleft", "quoteright", "quotedblleft", "quotedblright", "bullet", + "endash", "emdash", "tilde", "trademark", "scaron", "guilsinglright", + "oe", "bullet", "zcaron", "Ydieresis", "space", "exclamdown", "cent", + "sterling", "currency", "yen", "brokenbar", "section", "dieresis", + "copyright", "ordfeminine", "guillemotleft", "logicalnot", "hyphen", + "registered", "macron", "degree", "plusminus", "twosuperior", + "threesuperior", "acute", "mu", "paragraph", "periodcentered", + "cedilla", "onesuperior", "ordmasculine", "guillemotright", + "onequarter", "onehalf", "threequarters", "questiondown", "Agrave", + "Aacute", "Acircumflex", "Atilde", "Adieresis", "Aring", "AE", + "Ccedilla", "Egrave", "Eacute", "Ecircumflex", "Edieresis", "Igrave", + "Iacute", "Icircumflex", "Idieresis", "Eth", "Ntilde", "Ograve", + "Oacute", "Ocircumflex", "Otilde", "Odieresis", "multiply", "Oslash", + "Ugrave", "Uacute", "Ucircumflex", "Udieresis", "Yacute", "Thorn", + "germandbls", "agrave", "aacute", "acircumflex", "atilde", "adieresis", + "aring", "ae", "ccedilla", "egrave", "eacute", "ecircumflex", + "edieresis", "igrave", "iacute", "icircumflex", "idieresis", "eth", + "ntilde", "ograve", "oacute", "ocircumflex", "otilde", "odieresis", + "divide", "oslash", "ugrave", "uacute", "ucircumflex", "udieresis", + "yacute", "thorn", "ydieresis" }; + +#if 0 + +static char *standard[256] = { _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", + "ampersand", "quoteright", "parenleft", "parenright", "asterisk", + "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", + "three", "four", "five", "six", "seven", "eight", "nine", "colon", + "semicolon", "less", "equal", "greater", "question", "at", "A", + "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", + "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", + "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", + "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", + "y", "z", "braceleft", "bar", "braceright", "asciitilde", _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, "exclamdown", "cent", "sterling", + "fraction", "yen", "florin", "section", "currency", "quotesingle", + "quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", + "fi", "fl", _notdef, "endash", "dagger", "daggerdbl", "periodcentered", + _notdef, "paragraph", "bullet", "quotesinglbase", "quotedblbase", + "quotedblright", "guillemotright", "ellipsis", "perthousand", + _notdef, "questiondown", _notdef, "grave", "acute", "circumflex", + "tilde", "macron", "breve", "dotaccent", "dieresis", _notdef, + "ring", "cedilla", _notdef, "hungarumlaut", "ogonek", "caron", + "emdash", _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, "AE", + _notdef, "ordfeminine", _notdef, _notdef, _notdef, _notdef, + "Lslash", "Oslash", "OE", "ordmasculine", _notdef, _notdef, + _notdef, _notdef, _notdef, "ae", _notdef, _notdef, + _notdef, "dotlessi", _notdef, _notdef, "lslash", "oslash", + "oe", "germandbls", _notdef, _notdef, _notdef, _notdef }; + +static char *expert[256] = { _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, "space", + "exclamsmall", "Hungarumlautsmall", _notdef, "dollaroldstyle", + "dollarsuperior", "ampersandsmall", "Acutesmall", "parenleftsuperior", + "parenrightsuperior", "twodotenleader", "onedotenleader", "comma", + "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle", + "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", + "sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", + "colon", "semicolon", "commasuperior", "threequartersemdash", + "periodsuperior", "questionsmall", _notdef, "asuperior", "bsuperior", + "centsuperior", "dsuperior", "esuperior", _notdef, _notdef, + _notdef, "isuperior", _notdef, _notdef, "lsuperior", "msuperior", + "nsuperior", "osuperior", _notdef, _notdef, "rsuperior", + "ssuperior", "tsuperior", _notdef, "ff", "fi", "fl", "ffi", "ffl", + "parenleftinferior", _notdef, "parenrightinferior", "Circumflexsmall", + "hyphensuperior", "Gravesmall", "Asmall", "Bsmall", "Csmall", + "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall", "Ismall", "Jsmall", + "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall", "Qsmall", + "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall", + "Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", + "Tildesmall", _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, "exclamdownsmall", + "centoldstyle", "Lslashsmall", _notdef, _notdef, "Scaronsmall", + "Zcaronsmall", "Dieresissmall", "Brevesmall", "Caronsmall", _notdef, + "Dotaccentsmall", _notdef, _notdef, "Macronsmall", _notdef, + _notdef, "figuredash", "hypheninferior", _notdef, _notdef, + "Ogoneksmall", "Ringsmall", "Cedillasmall", _notdef, _notdef, + _notdef, "onequarter", "onehalf", "threequarters", "questiondownsmall", + "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird", + "twothirds", _notdef, _notdef, "zerosuperior", "onesuperior", + "twosuperior", "threesuperior", "foursuperior", "fivesuperior", + "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior", + "zeroinferior", "oneinferior", "twoinferior", "threeinferior", + "fourinferior", "fiveinferior", "sixinferior", "seveninferior", + "eightinferior", "nineinferior", "centinferior", "dollarinferior", + "periodinferior", "commainferior", "Agravesmall", "Aacutesmall", + "Acircumflexsmall", "Atildesmall", "Adieresissmall", "Aringsmall", + "AEsmall", "Ccedillasmall", "Egravesmall", "Eacutesmall", + "Ecircumflexsmall", "Edieresissmall", "Igravesmall", "Iacutesmall", + "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall", + "Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall", + "Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", + "Uacutesmall", "Ucircumflexsmall", "Udieresissmall", "Yacutesmall", + "Thornsmall", "Ydieresissmall" }; + +static char *symbol[256] = { _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, "space", + "exclam", "universal", "numbersign", "existential", "percent", + "ampersand", "suchthat", "parenleft", "parenright", "asteriskmath", + "plus", "comma", "minus", "period", "slash", "zero", "one", "two", + "three", "four", "five", "six", "seven", "eight", "nine", "colon", + "semicolon", "less", "equal", "greater", "question", "congruent", + "Alpha", "Beta", "Chi", "Delta", "Epsilon", "Phi", "Gamma", "Eta", + "Iota", "theta1", "Kappa", "Lambda", "Mu", "Nu", "Omicron", "Pi", + "Theta", "Rho", "Sigma", "Tau", "Upsilon", "sigma1", "Omega", "Xi", + "Psi", "Zeta", "bracketleft", "therefore", "bracketright", + "perpendicular", "underscore", "radicalex", "alpha", "beta", "chi", + "delta", "epsilon", "phi", "gamma", "eta", "iota", "phi1", "kappa", + "lambda", "mu", "nu", "omicron", "pi", "theta", "rho", "sigma", + "tau", "upsilon", "omega1", "omega", "xi", "psi", "zeta", "braceleft", + "bar", "braceright", "similar", _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, "Upsilon1", "minute", "lessequal", "fraction", "infinity", + "florin", "club", "diamond", "heart", "spade", "arrowboth", + "arrowleft", "arrowup", "arrowright", "arrowdown", "degree", + "plusminus", "second", "greaterequal", "multiply", "proportional", + "partialdiff", "bullet", "divide", "notequal", "equivalence", + "approxequal", "ellipsis", "arrowvertex", "arrowhorizex", + "carriagereturn", "aleph", "Ifraktur", "Rfraktur", "weierstrass", + "circlemultiply", "circleplus", "emptyset", "intersection", "union", + "propersuperset", "reflexsuperset", "notsubset", "propersubset", + "reflexsubset", "element", "notelement", "angle", "gradient", + "registerserif", "copyrightserif", "trademarkserif", "product", + "radical", "dotmath", "logicalnot", "logicaland", "logicalor", + "arrowdblboth", "arrowdblleft", "arrowdblup", "arrowdblright", + "arrowdbldown", "lozenge", "angleleft", "registersans", "copyrightsans", + "trademarksans", "summation", "parenlefttp", "parenleftex", + "parenleftbt", "bracketlefttp", "bracketleftex", "bracketleftbt", + "bracelefttp", "braceleftmid", "braceleftbt", "braceex", _notdef, + "angleright", "integral", "integraltp", "integralex", "integralbt", + "parenrighttp", "parenrightex", "parenrightbt", "bracketrighttp", + "bracketrightex", "bracketrightbt", "bracerighttp", "bracerightmid", + "bracerightbt", _notdef }; + +static char *zapfdingbats[256] = { _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + "space", "a1", "a2", "a202", "a3", "a4", "a5", "a119", "a118", + "a117", "a11", "a12", "a13", "a14", "a15", "a16", "a105", "a17", + "a18", "a19", "a20", "a21", "a22", "a23", "a24", "a25", "a26", + "a27", "a28", "a6", "a7", "a8", "a9", "a10", "a29", "a30", "a31", + "a32", "a33", "a34", "a35", "a36", "a37", "a38", "a39", "a40", + "a41", "a42", "a43", "a44", "a45", "a46", "a47", "a48", "a49", + "a50", "a51", "a52", "a53", "a54", "a55", "a56", "a57", "a58", + "a59", "a60", "a61", "a62", "a63", "a64", "a65", "a66", "a67", + "a68", "a69", "a70", "a71", "a72", "a73", "a74", "a203", "a75", + "a204", "a76", "a77", "a78", "a79", "a81", "a82", "a83", "a84", + "a97", "a98", "a99", "a100", _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, _notdef, _notdef, _notdef, _notdef, _notdef, + _notdef, "a101", "a102", "a103", "a104", "a106", "a107", "a108", + "a112", "a111", "a110", "a109", "a120", "a121", "a122", "a123", + "a124", "a125", "a126", "a127", "a128", "a129", "a130", "a131", + "a132", "a133", "a134", "a135", "a136", "a137", "a138", "a139", + "a140", "a141", "a142", "a143", "a144", "a145", "a146", "a147", + "a148", "a149", "a150", "a151", "a152", "a153", "a154", "a155", + "a156", "a157", "a158", "a159", "a160", "a161", "a163", "a164", + "a196", "a165", "a192", "a166", "a167", "a168", "a169", "a170", + "a171", "a172", "a173", "a162", "a174", "a175", "a176", "a177", + "a178", "a179", "a193", "a180", "a199", "a181", "a200", "a182", + _notdef, "a201", "a183", "a184", "a197", "a185", "a194", "a198", + "a186", "a195", "a187", "a188", "a189", "a190", "a191", _notdef }; + +#endif + diff --git a/mupdf/fontfile.c b/mupdf/fontfile.c new file mode 100644 index 00000000..3c0470f7 --- /dev/null +++ b/mupdf/fontfile.c @@ -0,0 +1,263 @@ +#include <fitz.h> +#include <mupdf.h> + +#include <ft2build.h> +#include FT_FREETYPE_H +#include <fontconfig/fontconfig.h> + +static FT_Library ftlib = nil; +static FcConfig *fclib = nil; + +static char *basenames[14] = +{ + "Courier", + "Courier-Bold", + "Courier-Oblique", + "Courier-BoldOblique", + "Helvetica", + "Helvetica-Bold", + "Helvetica-Oblique", + "Helvetica-BoldOblique", + "Times-Roman", + "Times-Bold", + "Times-Italic", + "Times-BoldItalic", + "Symbol", + "ZapfDingbats" +}; + +static char *basepatterns[14] = +{ + "Courier,Nimbus Mono L,Courier New:style=Regular,Roman", + "Courier,Nimbus Mono L,Courier New:style=Bold", + "Courier,Nimbus Mono L,Courier New:style=Oblique,Italic", + "Courier,Nimbus Mono L,Courier New:style=BoldOblique,BoldItalic", + "Helvetica,Nimbus Sans L,Arial:style=Regular,Roman", + "Helvetica,Nimbus Sans L,Arial:style=Bold", + "Helvetica,Nimbus Sans L,Arial:style=Oblique,Italic", + "Helvetica,Nimbus Sans L,Arial:style=BoldOblique,BoldItalic", + "Times,Nimbus Roman No9 L,Times New Roman:style=Regular,Roman", + "Times,Nimbus Roman No9 L,Times New Roman:style=Bold,Medium", + "Times,Nimbus Roman No9 L,Times New Roman:style=Italic,Regular Italic", + "Times,Nimbus Roman No9 L,Times New Roman:style=BoldItalic,Medium Italic", + "Standard Symbols L,Symbol", + "Zapf Dingbats,Dingbats" +}; + +static fz_error *initfontlibs(void) +{ + int fterr; + + if (ftlib) + return nil; + + fterr = FT_Init_FreeType(&ftlib); + if (fterr) + return fz_throw("freetype failed initialisation: 0x%x", fterr); + + fclib = FcInitLoadConfigAndFonts(); + if (!fclib) + return fz_throw("fontconfig failed initialisation"); + + return nil; +} + +fz_error * +pdf_loadbuiltinfont(void **fontp, char *pattern) +{ + fz_error *error; + FcResult fcerr; + int fterr; + + FcPattern *searchpat; + FcPattern *matchpat; + FT_Face face; + char *file; + + error = initfontlibs(); + if (error) + return error; + + fcerr = FcResultMatch; + searchpat = FcNameParse(pattern); + FcDefaultSubstitute(searchpat); + FcConfigSubstitute(fclib, searchpat, FcMatchPattern); + + matchpat = FcFontMatch(fclib, searchpat, &fcerr); + if (fcerr != FcResultMatch) + return fz_throw("fontconfig could not find font %s", pattern); + + fcerr = FcPatternGetString(matchpat, "file", 0, (FcChar8**)&file); + if (fcerr != FcResultMatch) + return fz_throw("fontconfig could not find font %s", pattern); + + fterr = FT_New_Face(ftlib, file, 0, &face); + if (fterr) + return fz_throw("freetype could not load font file '%s': 0x%x", file, fterr); + + FcPatternDestroy(matchpat); + FcPatternDestroy(searchpat); + + *fontp = face; + + return nil; +} + +fz_error * +pdf_loadsystemfont(void **fontp, char *basefont, char *collection) +{ + fz_error *error; + FcResult fcerr; + int fterr; + + char fontname[200]; + FcPattern *searchpat; + FcPattern *matchpat; + FT_Face face; + char *style; + char *file; + + error = initfontlibs(); + if (error) + return error; + + /* parse windows-style font name descriptors Font,Style or Font-Style */ + strlcpy(fontname, basefont, sizeof fontname); + + style = strchr(fontname, ','); + if (style) { + *style++ = 0; + } + else { + style = strchr(fontname, '-'); + if (style) + *style++ = 0; + } + + searchpat = FcPatternCreate(); + if (!searchpat) + return fz_outofmem; + + error = fz_outofmem; + + if (!FcPatternAddString(searchpat, FC_FAMILY, fontname)) + goto cleanup; + if (collection) + if (!FcPatternAddString(searchpat, FC_FAMILY, collection)) + goto cleanup; + if (style) + if (!FcPatternAddString(searchpat, FC_STYLE, style)) + goto cleanup; + if (!FcPatternAddBool(searchpat, FC_OUTLINE, 1)) + goto cleanup; + +file = FcNameUnparse(searchpat); +printf(" system font pattern %s\n", file); +free(file); + + fcerr = FcResultMatch; + FcDefaultSubstitute(searchpat); + FcConfigSubstitute(fclib, searchpat, FcMatchPattern); + + matchpat = FcFontMatch(fclib, searchpat, &fcerr); + if (fcerr != FcResultMatch) + return fz_throw("fontconfig could not find font %s", basefont); + + fcerr = FcPatternGetString(matchpat, "file", 0, (FcChar8**)&file); + if (fcerr != FcResultMatch) + return fz_throw("fontconfig could not find font %s", basefont); + +printf(" system font file %s\n", file); + + fterr = FT_New_Face(ftlib, file, 0, &face); + if (fterr) { + FcPatternDestroy(matchpat); + FcPatternDestroy(searchpat); + return fz_throw("freetype could not load font file '%s': 0x%x", file, fterr); + } + + FcPatternDestroy(matchpat); + FcPatternDestroy(searchpat); + + *fontp = face; + + return nil; + +cleanup: + FcPatternDestroy(searchpat); + return error; +} + +fz_error * +pdf_loadembeddedfont(void **fontp, pdf_xref *xref, fz_obj *stmref) +{ + fz_error *error; + int fterr; + FT_Face face; + unsigned char *buf; + int len; + + error = initfontlibs(); + if (error) + return error; + + error = pdf_readstream(&buf, &len, xref, stmref); + if (error) + return error; + +printf("readstream $%p %d\n", buf, len); + + fterr = FT_New_Memory_Face(ftlib, buf, len, 0, &face); + + if (fterr) { + fz_free(buf); + return fz_throw("freetype could not load embedded font: 0x%x", fterr); + } + + *fontp = face; + + /* TODO: figure out how to free 'buf' when the FT_Face is freed */ + + return nil; +} + +fz_error * +pdf_loadfontdescriptor(void **facep, pdf_xref *xref, fz_obj *desc, char *collection) +{ + fz_error *error; + fz_obj *obj1, *obj2, *obj3, *obj; + char *fontname; + + error = pdf_resolve(&desc, xref); + if (error) + return error; + + fontname = fz_toname(fz_dictgets(desc, "FontName")); + + obj1 = fz_dictgets(desc, "FontFile"); + obj2 = fz_dictgets(desc, "FontFile2"); + obj3 = fz_dictgets(desc, "FontFile3"); + obj = obj1 ? obj1 : obj2 ? obj2 : obj3; + + if (fz_isindirect(obj)) + { + error = pdf_loadembeddedfont(facep, xref, obj); + if (error) + goto cleanup; + } + else + { + error = pdf_loadsystemfont(facep, fontname, collection); + if (error) + goto cleanup; + } + + fz_dropobj(desc); + + return nil; + +cleanup: + fz_dropobj(desc); + return error; +} + diff --git a/mupdf/interpret.c b/mupdf/interpret.c new file mode 100644 index 00000000..6ad24d11 --- /dev/null +++ b/mupdf/interpret.c @@ -0,0 +1,786 @@ +#include <fitz.h> +#include <mupdf.h> + +void pdf_initgstate(pdf_gstate *gs); + +fz_error *pdf_buildstrokepath(pdf_gstate *gs, fz_path *path); +fz_error *pdf_buildfillpath(pdf_gstate *gs, fz_path *path, int evenodd); + +fz_error *pdf_addfillshape(pdf_gstate *gs, fz_node *shape); +fz_error *pdf_addstrokeshape(pdf_gstate *gs, fz_node *shape); +fz_error *pdf_addclipmask(pdf_gstate *gs, fz_node *shape); +fz_error *pdf_addtransform(pdf_gstate *gs, fz_node *affine); + +fz_error *pdf_showpath(pdf_csi *, int doclose, int dofill, int dostroke, int evenodd); +fz_error *pdf_showtext(pdf_csi *, fz_obj *text); +fz_error *pdf_flushtext(pdf_csi *); + +fz_error * +pdf_newcsi(pdf_csi **csip) +{ + fz_error *error; + pdf_csi *csi; + fz_node *node; + + csi = *csip = fz_malloc(sizeof(pdf_csi)); + if (!csi) + return fz_outofmem; + + pdf_initgstate(&csi->gstate[0]); + + csi->gtop = 0; + csi->top = 0; + + csi->xbalance = 0; + + error = fz_newpath(&csi->path); + if (error) { + fz_free(csi); + return error; + } + + error = fz_newtree(&csi->tree); + if (error) { + fz_freepath(csi->path); + fz_free(csi); + return error; + } + + error = fz_newover(&node); + csi->tree->root = node; + csi->gstate[0].head = node; + + csi->clip = nil; + + csi->text = nil; + csi->tm = fz_identity(); + csi->tlm = fz_identity(); + + return nil; +} + +static void +clearstack(pdf_csi *csi) +{ + int i; + for (i = 0; i < csi->top; i++) + fz_dropobj(csi->stack[i]); + csi->top = 0; +} + +void +pdf_freecsi(pdf_csi *csi) +{ + if (csi->path) fz_freepath(csi->path); + if (csi->clip) fz_freepath(csi->clip); + if (csi->text) fz_freetext(csi->text); + clearstack(csi); + fz_free(csi); +} + +static fz_error * +runextgstate(pdf_gstate *gstate, pdf_resources *rdb, fz_obj *extgstate) +{ + char name[64]; + int i, k; + + for (i = 0; i < fz_dictlen(extgstate); i++) + { + fz_obj *key = fz_dictgetkey(extgstate, i); + fz_obj *val = fz_dictgetval(extgstate, i); + char *s = fz_toname(key); + + if (!strcmp(s, "Font")) + { + if (fz_isarray(val) && fz_arraylen(val) == 2) + { + fz_obj *ref, *obj; + ref = fz_arrayget(val, 0); + sprintf(name, "$f.%d.%d", fz_toobjid(ref), fz_togenid(ref)); + obj = fz_dictgets(rdb->font, name); + if (!obj) + return fz_throw("syntaxerror: missing resource"); + gstate->font = fz_topointer(obj); + gstate->size = fz_toreal(fz_arrayget(val, 1)); + } + else + return fz_throw("syntaxerror in ExtGState/Font"); + } + + else if (!strcmp(s, "LW")) + gstate->linewidth = fz_toreal(val); + else if (!strcmp(s, "LC")) + gstate->linecap = fz_toint(val); + else if (!strcmp(s, "LJ")) + gstate->linejoin = fz_toint(val); + else if (!strcmp(s, "ML")) + gstate->miterlimit = fz_toreal(val); + + else if (!strcmp(s, "D")) + { + if (fz_isarray(val) && fz_arraylen(val) == 2) + { + fz_obj *dashes = fz_arrayget(val, 0); + gstate->dashlen = MAX(fz_arraylen(dashes), 32); + for (k = 0; k < gstate->dashlen; k++) + gstate->dashlist[k] = fz_toreal(fz_arrayget(dashes, k)); + gstate->dashphase = fz_toreal(fz_arrayget(val, 1)); + } + else + return fz_throw("syntaxerror in ExtGState/D"); + } + } + + return nil; +} + +static fz_error * +runkeyword(pdf_csi *csi, pdf_resources *rdb, char *buf) +{ + pdf_gstate *gstate = csi->gstate + csi->gtop; + fz_error *error; + float a, b, c, d, e, f; + float x, y, w, h; + fz_matrix m; + + if (strlen(buf) > 1) + { + if (!strcmp(buf, "BX")) + { + if (csi->top != 0) + goto syntaxerror; + csi->xbalance ++; + } + + else if (!strcmp(buf, "EX")) + { + if (csi->top != 0) + goto syntaxerror; + csi->xbalance --; + } + + else if (!strcmp(buf, "MP")) + { + fz_node *meta; + if (csi->top != 1) + goto syntaxerror; + error = fz_newmeta(&meta, csi->stack[0]); + if (error) return error; + fz_insertnode(gstate->head, meta); + } + + else if (!strcmp(buf, "DP")) + { + fz_node *meta; + fz_obj *info; + if (csi->top != 2) + goto syntaxerror; + error = fz_packobj(&info, "<< %o %o >>", + csi->stack[0], csi->stack[1]); + if (error) return error; + error = fz_newmeta(&meta, info); + fz_dropobj(info); + if (error) return error; + fz_insertnode(gstate->head, meta); + } + + else if (!strcmp(buf, "cm")) + { + fz_matrix m; + fz_node *affine; + + if (csi->top != 6) + goto syntaxerror; + + m.a = fz_toreal(csi->stack[0]); + m.b = fz_toreal(csi->stack[1]); + m.c = fz_toreal(csi->stack[2]); + m.d = fz_toreal(csi->stack[3]); + m.e = fz_toreal(csi->stack[4]); + m.f = fz_toreal(csi->stack[5]); + + error = fz_newtransform(&affine, m); + if (error) return error; + error = pdf_addtransform(gstate, affine); + if (error) return error; + } + + else if (!strcmp(buf, "ri")) + { + if (csi->top != 1) + goto syntaxerror; + } + + else if (!strcmp(buf, "gs")) + { + fz_obj *obj; + + if (csi->top != 1) + goto syntaxerror; + + obj = fz_dictget(rdb->extgstate, csi->stack[0]); + if (!obj) + return fz_throw("syntaxerror: missing resource"); + + runextgstate(gstate, rdb, obj); + } + + else if (!strcmp(buf, "re")) + { + if (csi->top != 4) + goto syntaxerror; + x = fz_toreal(csi->stack[0]); + y = fz_toreal(csi->stack[1]); + w = fz_toreal(csi->stack[2]); + h = fz_toreal(csi->stack[3]); + error = fz_moveto(csi->path, x, y); + if (error) return error; + error = fz_lineto(csi->path, x + w, y); + if (error) return error; + error = fz_lineto(csi->path, x + w, y + h); + if (error) return error; + error = fz_lineto(csi->path, x, y + h); + if (error) return error; + error = fz_closepath(csi->path); + if (error) return error; + } + + else if (!strcmp(buf, "f*")) + { + if (csi->top != 0) + goto syntaxerror; + error = pdf_showpath(csi, 0, 1, 0, 1); + if (error) return error; + } + + else if (!strcmp(buf, "B*")) + { + if (csi->top != 0) + goto syntaxerror; + error = pdf_showpath(csi, 0, 1, 1, 1); + if (error) return error; + } + + else if (!strcmp(buf, "b*")) + { + if (csi->top != 0) + goto syntaxerror; + error = pdf_showpath(csi, 1, 1, 1, 1); + if (error) return error; + } + + else if (!strcmp(buf, "W*")) + { + if (csi->top != 0) + goto syntaxerror; + error = fz_clonepath(&csi->clip, csi->path); + if (error) return error; + error = fz_endpath(csi->clip, FZ_EOFILL, nil, nil); + if (error) return error; + } + + else if (!strcmp(buf, "rg")) + { + if (csi->top != 3) + goto syntaxerror; + gstate->fill.r = fz_toreal(csi->stack[0]); + gstate->fill.g = fz_toreal(csi->stack[1]); + gstate->fill.b = fz_toreal(csi->stack[2]); + } + + else if (!strcmp(buf, "RG")) + { + if (csi->top != 3) + goto syntaxerror; + gstate->stroke.r = fz_toreal(csi->stack[0]); + gstate->stroke.g = fz_toreal(csi->stack[1]); + gstate->stroke.b = fz_toreal(csi->stack[2]); + } + + else if (!strcmp(buf, "BT")) + { + if (csi->top != 0) + goto syntaxerror; + csi->tm = fz_identity(); + csi->tlm = fz_identity(); + } + + else if (!strcmp(buf, "ET")) + { + if (csi->top != 0) + goto syntaxerror; + error = pdf_flushtext(csi); + if (error) + return error; + } + + else if (!strcmp(buf, "Tc")) + { + if (csi->top != 1) + goto syntaxerror; + gstate->charspace = fz_toreal(csi->stack[0]); + } + + else if (!strcmp(buf, "Tw")) + { + if (csi->top != 1) + goto syntaxerror; + gstate->wordspace = fz_toreal(csi->stack[0]); + } + + else if (!strcmp(buf, "Tz")) + { + if (csi->top != 1) + goto syntaxerror; + + error = pdf_flushtext(csi); + if (error) return error; + + gstate->scale = fz_toreal(csi->stack[0]) / 100.0; + } + + else if (!strcmp(buf, "TL")) + { + if (csi->top != 1) + goto syntaxerror; + gstate->leading = fz_toreal(csi->stack[0]); + } + + else if (!strcmp(buf, "Tf")) + { + fz_obj *obj; + + if (csi->top != 2) + goto syntaxerror; + + obj = fz_dictget(rdb->font, csi->stack[0]); + if (!obj) + return fz_throw("syntaxerror: missing resource"); + + gstate->font = fz_topointer(obj); + gstate->size = fz_toreal(csi->stack[1]); + } + + else if (!strcmp(buf, "Tr")) + { + if (csi->top != 1) + goto syntaxerror; + gstate->render = fz_toint(csi->stack[0]); + } + + else if (!strcmp(buf, "Ts")) + { + if (csi->top != 1) + goto syntaxerror; + gstate->rise = fz_toreal(csi->stack[0]); + } + + else if (!strcmp(buf, "Td")) + { + if (csi->top != 2) + goto syntaxerror; + m = fz_translate(fz_toreal(csi->stack[0]), fz_toreal(csi->stack[1])); + csi->tlm = fz_concat(m, csi->tlm); + csi->tm = csi->tlm; + } + + else if (!strcmp(buf, "TD")) + { + if (csi->top != 2) + goto syntaxerror; + gstate->leading = -fz_toreal(csi->stack[1]); + m = fz_translate(fz_toreal(csi->stack[0]), fz_toreal(csi->stack[1])); + csi->tlm = fz_concat(m, csi->tlm); + csi->tm = csi->tlm; + } + + else if (!strcmp(buf, "Tm")) + { + if (csi->top != 6) + goto syntaxerror; + + error = pdf_flushtext(csi); + if (error) return error; + + csi->tm.a = fz_toreal(csi->stack[0]); + csi->tm.b = fz_toreal(csi->stack[1]); + csi->tm.c = fz_toreal(csi->stack[2]); + csi->tm.d = fz_toreal(csi->stack[3]); + csi->tm.e = fz_toreal(csi->stack[4]); + csi->tm.f = fz_toreal(csi->stack[5]); + csi->tlm = csi->tm; + } + + else if (!strcmp(buf, "T*")) + { + if (csi->top != 0) + goto syntaxerror; + m = fz_translate(0, -gstate->leading); + csi->tlm = fz_concat(m, csi->tlm); + csi->tm = csi->tlm; + } + + else if (!strcmp(buf, "Tj")) + { + if (csi->top != 1) + goto syntaxerror; + error = pdf_showtext(csi, csi->stack[0]); + if (error) return error; + } + + else if (!strcmp(buf, "TJ")) + { + if (csi->top != 1) + goto syntaxerror; + error = pdf_showtext(csi, csi->stack[0]); + if (error) return error; + } + + else +fprintf(stderr, "syntaxerror: unknown keyword '%s'\n", buf); + //return fz_throw("syntaxerror: unknown keyword '%s'", buf); + //if (!csi->xbalance) goto syntaxerror; + } + + else switch (buf[0]) + { + + case 'q': + if (csi->top != 0) + goto syntaxerror; + if (csi->gtop == 31) + return fz_throw("gstate overflow in content stream"); + memcpy(&csi->gstate[csi->gtop + 1], + &csi->gstate[csi->gtop], + sizeof (pdf_gstate)); + csi->gtop ++; + break; + + case 'Q': + if (csi->top != 0) + goto syntaxerror; + if (csi->gtop == 0) + return fz_throw("gstate underflow in content stream"); + csi->gtop --; + break; + + case 'w': + if (csi->top != 1) + goto syntaxerror; + gstate->linewidth = fz_toreal(csi->stack[0]); + break; + + case 'J': + if (csi->top != 1) + goto syntaxerror; + gstate->linecap = fz_toint(csi->stack[0]); + break; + + case 'j': + if (csi->top != 1) + goto syntaxerror; + gstate->linejoin = fz_toint(csi->stack[0]); + break; + + case 'M': + if (csi->top != 1) + goto syntaxerror; + gstate->miterlimit = fz_toreal(csi->stack[0]); + break; + + case 'd': + if (csi->top != 2) + goto syntaxerror; + { + int i; + fz_obj *array = csi->stack[0]; + gstate->dashlen = fz_arraylen(array); + if (gstate->dashlen > 32) + return fz_throw("rangecheck: too large dash pattern"); + for (i = 0; i < gstate->dashlen; i++) + gstate->dashlist[i] = fz_toreal(fz_arrayget(array, i)); + gstate->dashphase = fz_toreal(csi->stack[1]); + } + break; + + case 'i': + if (csi->top != 1) + goto syntaxerror; + /* flatness */ + break; + + case 'm': + if (csi->top != 2) + goto syntaxerror; + a = fz_toreal(csi->stack[0]); + b = fz_toreal(csi->stack[1]); + return fz_moveto(csi->path, a, b); + + case 'l': + if (csi->top != 2) + goto syntaxerror; + a = fz_toreal(csi->stack[0]); + b = fz_toreal(csi->stack[1]); + return fz_lineto(csi->path, a, b); + + case 'c': + if (csi->top != 6) + goto syntaxerror; + a = fz_toreal(csi->stack[0]); + b = fz_toreal(csi->stack[1]); + c = fz_toreal(csi->stack[2]); + d = fz_toreal(csi->stack[3]); + e = fz_toreal(csi->stack[4]); + f = fz_toreal(csi->stack[5]); + return fz_curveto(csi->path, a, b, c, d, e, f); + + case 'v': + if (csi->top != 4) + goto syntaxerror; + a = fz_toreal(csi->stack[0]); + b = fz_toreal(csi->stack[1]); + c = fz_toreal(csi->stack[2]); + d = fz_toreal(csi->stack[3]); + return fz_curvetov(csi->path, a, b, c, d); + + case 'y': + if (csi->top != 4) + goto syntaxerror; + a = fz_toreal(csi->stack[0]); + b = fz_toreal(csi->stack[1]); + c = fz_toreal(csi->stack[2]); + d = fz_toreal(csi->stack[3]); + return fz_curvetoy(csi->path, a, b, c, d); + + case 'h': + if (csi->top != 0) + goto syntaxerror; + return fz_closepath(csi->path); + + case 'S': + if (csi->top != 0) + goto syntaxerror; + error = pdf_showpath(csi, 0, 0, 1, 0); + if (error) return error; + break; + + case 's': + if (csi->top != 0) + goto syntaxerror; + error = pdf_showpath(csi, 1, 0, 1, 0); + if (error) return error; + break; + + case 'F': + case 'f': + if (csi->top != 0) + goto syntaxerror; + error = pdf_showpath(csi, 0, 1, 0, 0); + if (error) return error; + break; + + case 'B': + if (csi->top != 0) + goto syntaxerror; + error = pdf_showpath(csi, 0, 1, 1, 0); + if (error) return error; + break; + + case 'b': + if (csi->top != 0) + goto syntaxerror; + error = pdf_showpath(csi, 1, 1, 1, 0); + if (error) return error; + break; + + case 'n': + if (csi->top != 0) + goto syntaxerror; + error = pdf_showpath(csi, 0, 0, 0, 0); + if (error) return error; + break; + + case 'W': + if (csi->top != 0) + goto syntaxerror; + error = fz_clonepath(&csi->clip, csi->path); + if (error) return error; + error = fz_endpath(csi->clip, FZ_FILL, nil, nil); + if (error) return error; + break; + + case 'g': + if (csi->top != 1) + goto syntaxerror; + a = fz_toreal(csi->stack[0]); + gstate->fill.r = a; + gstate->fill.g = a; + gstate->fill.b = a; + break; + + case 'G': + if (csi->top != 1) + goto syntaxerror; + a = fz_toreal(csi->stack[0]); + gstate->stroke.r = a; + gstate->stroke.g = a; + gstate->stroke.b = a; + break; + + case 'k': + if (csi->top != 4) + goto syntaxerror; + a = fz_toreal(csi->stack[0]); + b = fz_toreal(csi->stack[1]); + c = fz_toreal(csi->stack[2]); + d = fz_toreal(csi->stack[3]); + gstate->fill.r = 1.0 - MIN(1.0, a + d); + gstate->fill.g = 1.0 - MIN(1.0, b + d); + gstate->fill.b = 1.0 - MIN(1.0, c + d); + break; + + case 'K': + if (csi->top != 4) + goto syntaxerror; + a = fz_toreal(csi->stack[0]); + b = fz_toreal(csi->stack[1]); + c = fz_toreal(csi->stack[2]); + d = fz_toreal(csi->stack[3]); + gstate->stroke.r = 1.0 - MIN(1.0, a + d); + gstate->stroke.g = 1.0 - MIN(1.0, b + d); + gstate->stroke.b = 1.0 - MIN(1.0, c + d); + break; + + case '\'': + if (csi->top != 1) + goto syntaxerror; + + m = fz_translate(0, -gstate->leading); + csi->tlm = fz_concat(m, csi->tlm); + csi->tm = csi->tlm; + + error = pdf_showtext(csi, csi->stack[0]); + if (error) return error; + break; + + case '"': + if (csi->top != 3) + goto syntaxerror; + + gstate->wordspace = fz_toreal(csi->stack[0]); + gstate->charspace = fz_toreal(csi->stack[1]); + + m = fz_translate(0, -gstate->leading); + csi->tlm = fz_concat(m, csi->tlm); + csi->tm = csi->tlm; + + error = pdf_showtext(csi, csi->stack[2]); + if (error) return error; + break; + + default: +fprintf(stderr, "syntaxerror: unknown keyword '%s'\n", buf); + //return fz_throw("syntaxerror: unknown keyword '%s'", buf); + //if (!csi->xbalance) goto syntaxerror; + } + + return nil; + +syntaxerror: + return fz_throw("syntaxerror in content stream: '%s'", buf); +} + +fz_error * +pdf_runcsi(pdf_csi *csi, pdf_resources *rdb, fz_file *file) +{ + fz_error *error; + unsigned char buf[65536]; + int token, len; + + while (1) + { + if (csi->top == 31) + return fz_throw("stack overflow in content stream"); + + token = pdf_lex(file, buf, sizeof buf, &len); + + switch (token) + { + case PDF_TEOF: + return nil; + + /* FIXME: need to make array parsing be able to span files for + those stupid pdf files that split TJ arrays across content + streams... + */ + case PDF_TOARRAY: + error = pdf_parsearray(&csi->stack[csi->top], file, buf, sizeof buf); + if (error) return error; + csi->top ++; + break; + + /* drop down to normal pdf object parsing for dictionaries, + and pray that they are not split in the middle with the beginning + and end in different streams + */ + case PDF_TODICT: + error = pdf_parsedict(&csi->stack[csi->top], file, buf, sizeof buf); + if (error) return error; + csi->top ++; + break; + + case PDF_TNAME: + error = fz_newname(&csi->stack[csi->top], buf); + if (error) return error; + csi->top ++; + break; + + case PDF_TINT: + error = fz_newint(&csi->stack[csi->top], atoi(buf)); + if (error) return error; + csi->top ++; + break; + + case PDF_TREAL: + error = fz_newreal(&csi->stack[csi->top], atof(buf)); + if (error) return error; + csi->top ++; + break; + + case PDF_TSTRING: + error = fz_newstring(&csi->stack[csi->top], buf, len); + if (error) return error; + csi->top ++; + break; + + case PDF_TTRUE: + error = fz_newbool(&csi->stack[csi->top], 1); + if (error) return error; + csi->top ++; + break; + + case PDF_TFALSE: + error = fz_newbool(&csi->stack[csi->top], 0); + if (error) return error; + csi->top ++; + break; + + case PDF_TNULL: + error = fz_newnull(&csi->stack[csi->top]); + if (error) return error; + csi->top ++; + break; + + case PDF_TKEYWORD: + error = runkeyword(csi, rdb, buf); + if (error) return error; + clearstack(csi); + break; + + default: + clearstack(csi); + return fz_throw("syntaxerror in content stream"); + } + } +} + diff --git a/mupdf/lex.c b/mupdf/lex.c new file mode 100644 index 00000000..d8ec7d75 --- /dev/null +++ b/mupdf/lex.c @@ -0,0 +1,342 @@ +#include <fitz.h> +#include <mupdf.h> + +static inline int iswhite(int ch) +{ + return ch == '\000' || + ch == '\011' || + ch == '\012' || + ch == '\014' || + ch == '\015' || + ch == '\040'; +} + +static inline int isdelim(int ch) +{ + return ch == '(' || ch == ')' || + ch == '<' || ch == '>' || + ch == '[' || ch == ']' || + ch == '{' || ch == '}' || + ch == '/' || + ch == '%'; +} + +static inline int isregular(int ch) +{ + return !isdelim(ch) && !iswhite(ch) && ch != EOF; +} + +static inline int isnumber(int ch) +{ + return ch == '+' || ch == '-' || ch == '.' || (ch >= '0' && ch <= '9'); +} + +static inline int ishex(int ch) +{ + return (ch >= '0' && ch <= '9') || + (ch >= 'A' && ch <= 'F') || + (ch >= 'a' && ch <= 'f'); +} + +static inline int fromhex(int ch) +{ + if (ch >= '0' && ch <= '9') + return ch - '0'; + else if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 0xA; + else if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 0xA; + return 0; +} + +static void +lexwhite(fz_file *f) +{ + int c; + while (1) + { + c = fz_peekbyte(f); + if (!iswhite(c)) + break; + fz_readbyte(f); + } +} + +static void +lexcomment(fz_file *f) +{ + int c; + while (1) + { + c = fz_readbyte(f); + if (c == '\012') break; + if (c == '\015') break; + if (c == EOF) break; + } +} + +static void +lexnumber(fz_file *f, unsigned char *s, int n) +{ + while (n > 1) + { + if (!isnumber(fz_peekbyte(f))) + break; + *s++ = fz_readbyte(f); + n--; + } + *s = '\0'; +} + +static void +lexname(fz_file *f, unsigned char *s, int n) +{ +#if 0 + unsigned char *p = s; + unsigned char *q = s; +#endif + + while (n > 1) + { + if (!isregular(fz_peekbyte(f))) + break; + *s++ = fz_readbyte(f); + n--; + } + *s = '\0'; + +#if 0 + while (*p) + { + if (p[0] == '#' && p[1] != 0 && p[2] != 0) + { + *q++ = fromhex(p[1]) * 16 + fromhex(p[2]); + p += 3; + } + else + *q++ = *p++; + } + *q = '\0'; +#endif +} + +static int +lexstring(fz_file *f, unsigned char *buf, int n) +{ + unsigned char *s = buf; + unsigned char *e = buf + n; + int bal = 1; + int oct; + int c; + + while (s < e) + { + c = fz_readbyte(f); + if (c == '(') + { + bal++; + *s++ = c; + } + else if (c == ')') + { + bal --; + if (bal == 0) + break; + *s++ = c; + } + else if (c == '\\') + { + c = fz_readbyte(f); + if (c == 'n') *s++ = '\n'; + else if (c == 'r') *s++ = '\r'; + else if (c == 't') *s++ = '\t'; + else if (c == 'b') *s++ = '\b'; + else if (c == 'f') *s++ = '\f'; + else if (c == '(') *s++ = '('; + else if (c == ')') *s++ = ')'; + else if (c == '\\') *s++ = '\\'; + + else if (c >= '0' && c <= '9') + { + oct = c - '0'; + c = fz_peekbyte(f); + if (c >= '0' && c <= '9') + { + fz_readbyte(f); + oct = oct * 8 + (c - '0'); + c = fz_peekbyte(f); + if (c >= '0' && c <= '9') + { + fz_readbyte(f); + oct = oct * 8 + (c - '0'); + } + } + *s++ = oct; + } + + else if (c == '\n') + ; + else if (c == '\r') + { + c = fz_peekbyte(f); + if (c == '\n') + fz_readbyte(f); + } + else *s++ = c; + } + else + { + *s++ = c; + } + } + + return s - buf; +} + +static int +lexhexstring(fz_file *f, unsigned char *buf, int n) +{ + unsigned char *s = buf; + unsigned char *e = buf + n; + int a = 0, x = 0; + int c; + + while (s < e) + { + c = fz_readbyte(f); + if (c == '>') + break; + else if (iswhite(c)) + continue; + else if (ishex(c)) + { + if (x) + { + *s++ = a * 16 + fromhex(c); + x = !x; + } + else + { + a = fromhex(c); + x = !x; + } + } + else + break; + } + + return s - buf; +} + +static int +tokenfromkeyword(unsigned char *key) +{ + if (!strcmp(key, "R")) return PDF_TR; + if (!strcmp(key, "true")) return PDF_TTRUE; + if (!strcmp(key, "false")) return PDF_TFALSE; + if (!strcmp(key, "null")) return PDF_TNULL; + + if (!strcmp(key, "obj")) return PDF_TOBJ; + if (!strcmp(key, "endobj")) return PDF_TENDOBJ; + if (!strcmp(key, "stream")) return PDF_TSTREAM; + if (!strcmp(key, "endstream")) return PDF_TENDSTREAM; + + if (!strcmp(key, "xref")) return PDF_TXREF; + if (!strcmp(key, "trailer")) return PDF_TTRAILER; + if (!strcmp(key, "startxref")) return PDF_TSTARTXREF; + + return PDF_TKEYWORD; +} + +int +pdf_lex(fz_file *f, unsigned char *buf, int n, int *sl) +{ + int c; + + while (1) + { + c = fz_peekbyte(f); + + if (c == EOF) + return PDF_TEOF; + + else if (iswhite(c)) + lexwhite(f); + + else if (c == '%') + lexcomment(f); + + + else if (c == '/') + { + fz_readbyte(f); + lexname(f, buf, n); + *sl = strlen(buf); + return PDF_TNAME; + } + + else if (c == '(') + { + fz_readbyte(f); + *sl = lexstring(f, buf, n); + return PDF_TSTRING; + } + + else if (c == '<') + { + fz_readbyte(f); + c = fz_peekbyte(f); + if (c == '<') + { + fz_readbyte(f); + return PDF_TODICT; + } + else + { + *sl = lexhexstring(f, buf, n); + return PDF_TSTRING; + } + } + + else if (c == '>') + { + fz_readbyte(f); + c = fz_readbyte(f); + if (c == '>') + return PDF_TCDICT; + return PDF_TERROR; + } + + else if (c == '[') + { + fz_readbyte(f); + return PDF_TOARRAY; + } + + else if (c == ']') + { + fz_readbyte(f); + return PDF_TCARRAY; + } + + else if (isnumber(c)) + { + lexnumber(f, buf, n); + *sl = strlen(buf); + if (strchr(buf, '.')) + return PDF_TREAL; + return PDF_TINT; + } + + else if (isregular(c)) + { + lexname(f, buf, n); + *sl = strlen(buf); + return tokenfromkeyword(buf); + } + + else + return PDF_TERROR; + } +} + diff --git a/mupdf/open.c b/mupdf/open.c new file mode 100644 index 00000000..016cebf4 --- /dev/null +++ b/mupdf/open.c @@ -0,0 +1,492 @@ +#include <fitz.h> +#include <mupdf.h> + +static inline int iswhite(int ch) +{ + return ch == '\000' || ch == '\011' || ch == '\012' || + ch == '\014' || ch == '\015' || ch == '\040'; +} + +/* + * magic version tag and startxref + */ + +static fz_error * +loadversion(float *version, fz_file *file) +{ + char buf[20]; + int n; + + n = fz_seek(file, 0); + if (n < 0) + return fz_ferror(file); + + fz_readline(file, buf, sizeof buf); + if (memcmp(buf, "%PDF-", 5) != 0) + return fz_throw("syntaxerror: missing magic pdf marker"); + + *version = atof(buf + 5); + + return nil; +} + +static fz_error * +readstartxref(int *ofs, int fd) +{ + unsigned char buf[1024]; + int t, n; + int i; + + t = lseek(fd, 0, 2); + if (t == -1) + return fz_throw("ioerror in startxref: lseek: %s", strerror(errno)); + + t = lseek(fd, MAX(0, t - ((int)sizeof buf)), 0); + if (t == -1) + return fz_throw("ioerror in startxref: lseek: %s", strerror(errno)); + + n = read(fd, buf, sizeof buf); + if (n == -1) + return fz_throw("ioerror in startxref: read: %s", strerror(errno)); + + for (i = n - 9; i >= 0; i--) { + if (memcmp(buf + i, "startxref", 9) == 0) { + i += 9; + while (iswhite(buf[i]) && i < n) + i ++; + *ofs = atoi(buf + i); + return nil; + } + } + + return fz_throw("syntaxerror: missing startxref"); +} + +/* + * trailer dictionary + */ + +static fz_error * +readoldtrailer(fz_obj **objp, fz_file *file, unsigned char *buf, int cap) +{ + int ofs, len; + char *s; + int n; + int t; + int c; + + fz_readline(file, buf, cap); + if (strcmp(buf, "xref") != 0) + return fz_throw("syntaxerror: missing xref"); + + while (1) + { + c = fz_peekbyte(file); + if (!(c >= '0' && c <= '9')) + break; + + n = fz_readline(file, buf, cap); + if (n < 0) return fz_ferror(file); + + s = buf; + ofs = atoi(strsep(&s, " ")); + len = atoi(strsep(&s, " ")); + + t = fz_tell(file); + if (t < 0) return fz_ferror(file); + + n = fz_seek(file, t + 20 * len); + if (n < 0) return fz_ferror(file); + } + + t = pdf_lex(file, buf, cap, &n); + if (t != PDF_TTRAILER) + return fz_throw("syntaxerror: missing trailer"); + + t = pdf_lex(file, buf, cap, &n); + if (t != PDF_TODICT) + return fz_throw("syntaxerror: trailer must be dictionary"); + + return pdf_parsedict(objp, file, buf, cap); +} + +static fz_error * +readnewtrailer(fz_obj **objp, fz_file *file, unsigned char *buf, int cap) +{ + return pdf_parseindobj(objp, file, buf, cap, nil, nil, nil); +} + +static fz_error * +readtrailer(fz_obj **objp, fz_file *file, int ofs, unsigned char *buf, int cap) +{ + int n; + int c; + + n = fz_seek(file, ofs); + if (n < 0) + return fz_ferror(file); + + c = fz_peekbyte(file); + if (c == 'x') + return readoldtrailer(objp, file, buf, cap); + else if (c >= '0' && c <= '9') + return readnewtrailer(objp, file, buf, cap); + + return fz_throw("syntaxerror: missing xref"); +} + +/* + * xref tables + */ + +static fz_error * +readoldxref(fz_obj **trailerp, pdf_xref *xref, unsigned char *buf, int cap) +{ + int ofs, len; + char *s; + int n; + int t; + int i; + int c; + + fz_readline(xref->file, buf, cap); + if (strcmp(buf, "xref") != 0) + return fz_throw("syntaxerror: missing xref"); + + while (1) + { + c = fz_peekbyte(xref->file); + if (!(c >= '0' && c <= '9')) + break; + + n = fz_readline(xref->file, buf, cap); + if (n < 0) return fz_ferror(xref->file); + + s = buf; + ofs = atoi(strsep(&s, " ")); + len = atoi(strsep(&s, " ")); + + for (i = 0; i < len; i++) + { + n = fz_read(xref->file, buf, 20); + if (n < 0) return fz_ferror(xref->file); + if (n != 20) return fz_throw("syntaxerror: truncated xref table"); + if (!xref->table[ofs + i].type) + { + s = buf; + xref->table[ofs + i].ofs = atoi(strsep(&s, " ")); + xref->table[ofs + i].gen = atoi(strsep(&s, " ")); + xref->table[ofs + i].type = strsep(&s, " ")[0]; + } + } + } + + t = pdf_lex(xref->file, buf, cap, &n); + if (t != PDF_TTRAILER) + return fz_throw("syntaxerror: missing trailer"); + t = pdf_lex(xref->file, buf, cap, &n); + if (t != PDF_TODICT) + return fz_throw("syntaxerror: trailer must be dictionary"); + + return pdf_parsedict(trailerp, xref->file, buf, cap); +} + +static fz_error * +readnewxref(fz_obj **trailerp, pdf_xref *xref, unsigned char *buf, int cap) +{ + fz_error *error; + fz_obj *trailer; + fz_obj *obj; + int oid, gid, stmofs; + int size, w0, w1, w2, i0, i1; + int i, n; + + error = pdf_parseindobj(&trailer, xref->file, buf, cap, &oid, &gid, &stmofs); + if (error) + return error; + + obj = fz_dictgets(trailer, "Size"); + if (!obj) { + error = fz_throw("syntaxerror: xref stream missing Size entry"); + goto cleanup; + } + size = fz_toint(obj); + + obj = fz_dictgets(trailer, "W"); + if (!obj) { + error = fz_throw("syntaxerror: xref stream missing W entry"); + goto cleanup; + } + w0 = fz_toint(fz_arrayget(obj, 0)); + w1 = fz_toint(fz_arrayget(obj, 1)); + w2 = fz_toint(fz_arrayget(obj, 2)); + + obj = fz_dictgets(trailer, "Index"); + if (obj) { + i0 = fz_toint(fz_arrayget(obj, 0)); + i1 = fz_toint(fz_arrayget(obj, 1)); + } + else { + i0 = 0; + i1 = size; + } + + if (i0 < 0 || i1 > xref->size) { + error = fz_throw("syntaxerror: xref stream has too many entries"); + goto cleanup; + } + + error = pdf_openstream0(xref, trailer, oid, gid, stmofs); + if (error) + goto cleanup; + + for (i = i0; i < i0 + i1; i++) + { + int a = 0; + int b = 0; + int c = 0; + + if (fz_peekbyte(xref->file) == EOF) + { + error = fz_ferror(xref->file); + if (!error) + error = fz_throw("syntaxerror: truncated xref stream"); + pdf_closestream(xref); + goto cleanup; + } + + for (n = 0; n < w0; n++) + a = (a << 8) + fz_readbyte(xref->file); + for (n = 0; n < w1; n++) + b = (b << 8) + fz_readbyte(xref->file); + for (n = 0; n < w2; n++) + c = (c << 8) + fz_readbyte(xref->file); + + if (!xref->table[i].type) + { + int t = w0 ? a : 1; + xref->table[i].type = t == 0 ? 'f' : t == 1 ? 'n' : t == 2 ? 'o' : 0; + xref->table[i].ofs = w2 ? b : 0; + xref->table[i].gen = w1 ? c : 0; + } + } + + pdf_closestream(xref); + + *trailerp = trailer; + + return nil; + +cleanup: + fz_dropobj(trailer); + return error; +} + +static fz_error * +readxref(fz_obj **trailerp, pdf_xref *xref, int ofs, unsigned char *buf, int cap) +{ + int n; + int c; + + n = fz_seek(xref->file, ofs); + if (n < 0) + return fz_ferror(xref->file); + + c = fz_peekbyte(xref->file); + if (c == 'x') + return readoldxref(trailerp, xref, buf, cap); + else if (c >= '0' && c <= '9') + return readnewxref(trailerp, xref, buf, cap); + + return fz_throw("syntaxerror: missing xref"); +} + +static fz_error * +readxrefsections(pdf_xref *xref, int ofs, unsigned char *buf, int cap) +{ + fz_error *error; + fz_obj *trailer; + fz_obj *prev; + fz_obj *xrefstm; + + error = readxref(&trailer, xref, ofs, buf, cap); + if (error) + return error; + + /* FIXME: do we overwrite free entries properly? */ + xrefstm = fz_dictgets(trailer, "XrefStm"); + if (xrefstm) + { + error = readxrefsections(xref, fz_toint(xrefstm), buf, cap); + if (error) + goto cleanup; + } + + prev = fz_dictgets(trailer, "Prev"); + if (prev) + { + error = readxrefsections(xref, fz_toint(prev), buf, cap); + if (error) + goto cleanup; + } + + fz_dropobj(trailer); + return nil; + +cleanup: + fz_dropobj(trailer); + return error; +} + +/* + * compressed object streams + */ + +fz_error * +pdf_readobjstm(pdf_xref *xref, int oid, int gid, unsigned char *buf, int cap) +{ + fz_error *error; + fz_obj *objstm; + int *oidbuf; + int *ofsbuf; + + fz_obj *obj; + int stmofs; + int first; + int count; + int i, n, t; + + error = pdf_loadobject0(&objstm, xref, oid, gid, &stmofs); + if (error) + return error; + + count = fz_toint(fz_dictgets(objstm, "N")); + first = fz_toint(fz_dictgets(objstm, "First")); + + oidbuf = fz_malloc(count * sizeof(int)); + if (!oidbuf) { error = fz_outofmem; goto cleanup1; } + + ofsbuf = fz_malloc(count * sizeof(int)); + if (!ofsbuf) { error = fz_outofmem; goto cleanup2; } + + error = pdf_openstream0(xref, objstm, oid, gid, stmofs); + if (error) + goto cleanup3; + + for (i = 0; i < count; i++) + { + t = pdf_lex(xref->file, buf, cap, &n); + if (t != PDF_TINT) + { + error = fz_throw("syntaxerror: corrupt object stream"); + goto cleanup4; + } + oidbuf[i] = atoi(buf); + + t = pdf_lex(xref->file, buf, cap, &n); + if (t != PDF_TINT) + { + error = fz_throw("syntaxerror: corrupt object stream"); + goto cleanup4; + } + ofsbuf[i] = atoi(buf); + } + + n = fz_seek(xref->file, first); + if (n < 0) + { + error = fz_ferror(xref->file); + goto cleanup4; + } + + for (i = 0; i < count; i++) + { + /* FIXME: seek to first + ofsbuf[i] */ + + error = pdf_parsestmobj(&obj, xref->file, buf, cap); + if (error) + goto cleanup4; + + if (oidbuf[i] < 1 || oidbuf[i] >= xref->size) + { + error = fz_throw("rangecheck: object number out of range"); + goto cleanup4; + } + + error = pdf_storeobject(xref->store, oidbuf[i], 0, obj); + if (error) + goto cleanup4; + } + + pdf_closestream(xref); + fz_free(ofsbuf); + fz_free(oidbuf); + fz_dropobj(objstm); + return nil; + +cleanup4: + pdf_closestream(xref); +cleanup3: + fz_free(ofsbuf); +cleanup2: + fz_free(oidbuf); +cleanup1: + fz_dropobj(objstm); + return error; +} + +/* + * open xref in normal mode (as opposed to repair mode) + */ + +fz_error * +pdf_openxref(pdf_xref *xref, char *filename) +{ + fz_error *error; + fz_obj *size; + int i; + + unsigned char buf[65536]; /* yeowch! */ + + error = fz_openfile(&xref->file, filename, O_RDONLY); + if (error) + return error; + + error = loadversion(&xref->version, xref->file); + if (error) + return error; + + error = readstartxref(&xref->startxref, xref->file->fd); + if (error) + return error; + + error = readtrailer(&xref->trailer, xref->file, xref->startxref, buf, sizeof buf); + if (error) + return error; + + size = fz_dictgets(xref->trailer, "Size"); + if (!size) + return fz_throw("syntaxerror: trailer missing Size entry"); + + xref->capacity = fz_toint(size); + xref->size = fz_toint(size); + + xref->table = fz_malloc(xref->capacity * sizeof(pdf_xrefentry)); + if (!xref->table) + return fz_outofmem; + + for (i = 0; i < xref->size; i++) + { + xref->table[i].ofs = 0; + xref->table[i].gen = 0; + xref->table[i].type = 0; + xref->table[i].mark = 0; + } + + error = readxrefsections(xref, xref->startxref, buf, sizeof buf); + if (error) + return error; + + return nil; +} + diff --git a/mupdf/pagetree.c b/mupdf/pagetree.c new file mode 100644 index 00000000..de500f18 --- /dev/null +++ b/mupdf/pagetree.c @@ -0,0 +1,174 @@ +#include <fitz.h> +#include <mupdf.h> + +struct stuff +{ + fz_obj *resources; + fz_obj *mediabox; + fz_obj *cropbox; + fz_obj *rotate; +}; + +static fz_error * +loadpagetree(pdf_xref *xref, pdf_pagetree *pages, + struct stuff inherit, fz_obj *obj, fz_obj *ref) +{ + fz_error *err; + fz_obj *type; + fz_obj *kids; + fz_obj *kref, *kobj; + fz_obj *inh; + int i; + + type = fz_dictgets(obj, "Type"); + + if (strcmp(fz_toname(type), "Page") == 0) + { + if (inherit.resources && !fz_dictgets(obj, "Resources")) { + err = fz_dictputs(obj, "Resources", inherit.resources); + if (err) return err; + } + + if (inherit.mediabox && !fz_dictgets(obj, "MediaBox")) { + err = fz_dictputs(obj, "MediaBox", inherit.mediabox); + if (err) return err; + } + + if (inherit.cropbox && !fz_dictgets(obj, "CropBox")) { + err = fz_dictputs(obj, "CropBox", inherit.cropbox); + if (err) return err; + } + + if (inherit.rotate && !fz_dictgets(obj, "Rotate")) { + err = fz_dictputs(obj, "Rotate", inherit.rotate); + if (err) return err; + } + + pages->pref[pages->cursor] = fz_keepobj(ref); + pages->pobj[pages->cursor] = fz_keepobj(obj); + pages->cursor ++; + } + + else if (strcmp(fz_toname(type), "Pages") == 0) + { + inh = fz_dictgets(obj, "Resources"); + if (inh) inherit.resources = inh; + + inh = fz_dictgets(obj, "MediaBox"); + if (inh) inherit.mediabox = inh; + + inh = fz_dictgets(obj, "CropBox"); + if (inh) inherit.cropbox = inh; + + inh = fz_dictgets(obj, "Rotate"); + if (inh) inherit.rotate = inh; + + kids = fz_dictgets(obj, "Kids"); + for (i = 0; i < fz_arraylen(kids); i++) + { + kref = fz_arrayget(kids, i); + + err = pdf_loadobject(&kobj, xref, kref, nil); + if (err) return err; + + err = loadpagetree(xref, pages, inherit, kobj, kref); + fz_dropobj(kobj); + if (err) return err; + } + } + + return nil; +} + +void +pdf_debugpagetree(pdf_pagetree *pages) +{ + int i; + printf("<<\n /Type /Pages\n /Count %d\n /Kids [\n", pages->count); + for (i = 0; i < pages->count; i++) { + printf(" "); + fz_fprintobj(stdout, pages->pref[i]); + printf("\t%% page %d\n", i + 1); + //fz_fprintobj(stdout, pages->pobj[i]); + //printf("\n"); + } + printf(" ]\n>>\n"); +} + +fz_error * +pdf_loadpagetree(pdf_pagetree **pp, pdf_xref *xref) +{ + fz_error *err; + struct stuff inherit; + pdf_pagetree *p = nil; + fz_obj *catalog = nil; + fz_obj *pages = nil; + fz_obj *trailer; + fz_obj *ref; + int count; + + inherit.resources = nil; + inherit.mediabox = nil; + inherit.cropbox = nil; + inherit.rotate = nil; + + trailer = xref->trailer; + + ref = fz_dictgets(trailer, "Root"); + err = pdf_loadobject(&catalog, xref, ref, nil); + if (err) goto error; + + ref = fz_dictgets(catalog, "Pages"); + err = pdf_loadobject(&pages, xref, ref, nil); + if (err) goto error; + + ref = fz_dictgets(pages, "Count"); + count = fz_toint(ref); + + p = *pp = fz_malloc(sizeof(pdf_pagetree)); + if (!p) { err = fz_outofmem; goto error; } + + p->pref = nil; + p->pobj = nil; + p->count = count; + p->cursor = 0; + + p->pref = fz_malloc(sizeof(fz_obj*) * count); + if (!p->pref) { err = fz_outofmem; goto error; } + + p->pobj = fz_malloc(sizeof(fz_obj*) * count); + if (!p->pobj) { err = fz_outofmem; goto error; } + + err = loadpagetree(xref, p, inherit, pages, ref); + if (err) goto error; + + fz_dropobj(pages); + fz_dropobj(catalog); + return nil; + +error: + if (pages) fz_dropobj(pages); + if (catalog) fz_dropobj(catalog); + if (p) { + fz_free(p->pref); + fz_free(p->pobj); + fz_free(p); + } + return nil; +} + +void +pdf_freepagetree(pdf_pagetree *pages) +{ + int i; + for (i = 0; i < pages->count; i++) { + if (pages->pref[i]) + fz_dropobj(pages->pref[i]); + if (pages->pobj[i]) + fz_dropobj(pages->pobj[i]); + } + fz_free(pages->pref); + fz_free(pages->pobj); + fz_free(pages); +} + diff --git a/mupdf/parse.c b/mupdf/parse.c new file mode 100644 index 00000000..90f7c453 --- /dev/null +++ b/mupdf/parse.c @@ -0,0 +1,305 @@ +#include <fitz.h> +#include <mupdf.h> + +fz_error * +pdf_parsearray(fz_obj **op, fz_file *file, unsigned char *buf, int cap) +{ + fz_error *error = nil; + fz_obj *ary = nil; + fz_obj *obj = nil; + int a = 0, b = 0, n = 0; + int tok, len; + + error = fz_newarray(op, 4); + if (error) return error; + ary = *op; + + while (1) + { + tok = pdf_lex(file, buf, cap, &len); + + if (tok != PDF_TINT && tok != PDF_TR) + { + if (n > 0) + { + error = fz_newint(&obj, a); + if (error) goto cleanup; + error = fz_arraypush(ary, obj); + if (error) goto cleanup; + fz_dropobj(obj); + obj = nil; + } + if (n > 1) + { + error = fz_newint(&obj, b); + if (error) goto cleanup; + error = fz_arraypush(ary, obj); + if (error) goto cleanup; + fz_dropobj(obj); + obj = nil; + } + n = 0; + } + + if (tok == PDF_TINT && n == 2) + { + error = fz_newint(&obj, a); + if (error) goto cleanup; + error = fz_arraypush(ary, obj); + if (error) goto cleanup; + fz_dropobj(obj); + obj = nil; + a = b; + n --; + } + + switch (tok) + { + case PDF_TCARRAY: + return nil; + case PDF_TINT: + if (n == 0) + a = atoi(buf); + if (n == 1) + b = atoi(buf); + n ++; + break; + case PDF_TR: + if (n != 2) + goto cleanup; + error = fz_newindirect(&obj, a, b); + if (error) goto cleanup; + n = 0; + break; + case PDF_TOARRAY: error = pdf_parsearray(&obj, file, buf, cap); break; + case PDF_TODICT: error = pdf_parsedict(&obj, file, buf, cap); break; + case PDF_TNAME: error = fz_newname(&obj, buf); break; + case PDF_TREAL: error = fz_newreal(&obj, atof(buf)); break; + case PDF_TSTRING: error = fz_newstring(&obj, buf, len); break; + case PDF_TTRUE: error = fz_newbool(&obj, 1); break; + case PDF_TFALSE: error = fz_newbool(&obj, 0); break; + case PDF_TNULL: error = fz_newnull(&obj); break; + default: goto cleanup; + } + if (error) goto cleanup; + + if (obj) + { + error = fz_arraypush(ary, obj); + if (error) goto cleanup; + fz_dropobj(obj); + } + + obj = nil; + } + +cleanup: + if (obj) fz_dropobj(obj); + if (ary) fz_dropobj(ary); + if (error) return error; + return fz_throw("syntaxerror in array"); +} + +fz_error * +pdf_parsedict(fz_obj **op, fz_file *file, unsigned char *buf, int cap) +{ + fz_error *error = nil; + fz_obj *dict = nil; + fz_obj *key = nil; + fz_obj *val = nil; + int tok, len; + int a, b; + + error = fz_newdict(op, 8); + if (error) return error; + dict = *op; + + while (1) + { + tok = pdf_lex(file, buf, cap, &len); + +skip: + if (tok == PDF_TCDICT) + return nil; + + if (tok != PDF_TNAME) + goto cleanup; + + error = fz_newname(&key, buf); + if (error) goto cleanup; + + tok = pdf_lex(file, buf, cap, &len); + + switch (tok) + { + case PDF_TOARRAY: error = pdf_parsearray(&val, file, buf, cap); break; + case PDF_TODICT: error = pdf_parsedict(&val, file, buf, cap); break; + case PDF_TNAME: error = fz_newname(&val, buf); break; + case PDF_TREAL: error = fz_newreal(&val, atof(buf)); break; + case PDF_TSTRING: error = fz_newstring(&val, buf, len); break; + case PDF_TTRUE: error = fz_newbool(&val, 1); break; + case PDF_TFALSE: error = fz_newbool(&val, 0); break; + case PDF_TNULL: error = fz_newnull(&val); break; + case PDF_TINT: + a = atoi(buf); + tok = pdf_lex(file, buf, cap, &len); + if (tok == PDF_TCDICT || tok == PDF_TNAME) + { + error = fz_newint(&val, a); + if (error) goto cleanup; + error = fz_dictput(dict, key, val); + if (error) goto cleanup; + fz_dropobj(val); + fz_dropobj(key); + key = val = nil; + goto skip; + } + if (tok == PDF_TINT) + { + b = atoi(buf); + tok = pdf_lex(file, buf, cap, &len); + if (tok == PDF_TR) + { + error = fz_newindirect(&val, a, b); + break; + } + } + goto cleanup; + default: + goto cleanup; + } + + if (error) goto cleanup; + + error = fz_dictput(dict, key, val); + if (error) goto cleanup; + + fz_dropobj(val); + fz_dropobj(key); + key = val = nil; + } + +cleanup: + if (key) fz_dropobj(key); + if (val) fz_dropobj(val); + if (dict) fz_dropobj(dict); + if (error) return error; + return fz_throw("syntaxerror in dictionary"); +} + +fz_error * +pdf_parsestmobj(fz_obj **op, fz_file *file, unsigned char *buf, int cap) +{ + int tok, len; + + tok = pdf_lex(file, buf, cap, &len); + + switch (tok) + { + case PDF_TOARRAY: return pdf_parsearray(op, file, buf, cap); + case PDF_TODICT: return pdf_parsedict(op, file, buf, cap); + case PDF_TNAME: return fz_newname(op, buf); + case PDF_TREAL: return fz_newreal(op, atof(buf)); + case PDF_TSTRING: return fz_newstring(op, buf, len); + case PDF_TTRUE: return fz_newbool(op, 1); + case PDF_TFALSE: return fz_newbool(op, 0); + case PDF_TNULL: return fz_newnull(op); + case PDF_TINT: return fz_newint(op, atoi(buf)); + } + + return fz_throw("syntaxerror in object stream"); +} + +fz_error * +pdf_parseindobj(fz_obj **op, fz_file *file, unsigned char *buf, int cap, + int *ooid, int *ogid, int *ostmofs) +{ + fz_error *error = nil; + fz_obj *obj = nil; + int oid, gid, stmofs; + int tok, len; + int a, b; + + tok = pdf_lex(file, buf, cap, &len); + if (tok != PDF_TINT) + goto cleanup; + oid = atoi(buf); + + tok = pdf_lex(file, buf, cap, &len); + if (tok != PDF_TINT) + goto cleanup; + gid = atoi(buf); + + tok = pdf_lex(file, buf, cap, &len); + if (tok != PDF_TOBJ) + goto cleanup; + + tok = pdf_lex(file, buf, cap, &len); + switch (tok) + { + case PDF_TOARRAY: error = pdf_parsearray(&obj, file, buf, cap); break; + case PDF_TODICT: error = pdf_parsedict(&obj, file, buf, cap); break; + case PDF_TNAME: error = fz_newname(&obj, buf); break; + case PDF_TREAL: error = fz_newreal(&obj, atof(buf)); break; + case PDF_TSTRING: error = fz_newstring(&obj, buf, len); break; + case PDF_TTRUE: error = fz_newbool(&obj, 1); break; + case PDF_TFALSE: error = fz_newbool(&obj, 0); break; + case PDF_TNULL: error = fz_newnull(&obj); break; + case PDF_TINT: + a = atoi(buf); + tok = pdf_lex(file, buf, cap, &len); + if (tok == PDF_TSTREAM || tok == PDF_TENDOBJ) + { + error = fz_newint(&obj, a); + if (error) goto cleanup; + goto skip; + } + if (tok == PDF_TINT) + { + b = atoi(buf); + tok = pdf_lex(file, buf, cap, &len); + if (tok == PDF_TR) + { + error = fz_newindirect(&obj, a, b); + break; + } + } + goto cleanup; + default: + goto cleanup; + } + if (error) goto cleanup; + + tok = pdf_lex(file, buf, cap, &len); + +skip: + if (tok == PDF_TSTREAM) + { + int c = fz_readbyte(file); + if (c == '\r') + { + c = fz_peekbyte(file); + if (c != '\n') + fz_warn("corrupt pdf stream (%d %d)\n", oid, gid); + else + c = fz_readbyte(file); + } + stmofs = fz_tell(file); + } + else if (tok == PDF_TENDOBJ) + stmofs = -1; + else + goto cleanup; + + if (ooid) *ooid = oid; + if (ogid) *ogid = gid; + if (ostmofs) *ostmofs = stmofs; + *op = obj; + return nil; + +cleanup: + if (obj) fz_dropobj(obj); + if (error) return error; + return fz_throw("syntaxerror in indirect object"); +} + diff --git a/mupdf/repair.c b/mupdf/repair.c new file mode 100644 index 00000000..abc69cbc --- /dev/null +++ b/mupdf/repair.c @@ -0,0 +1,279 @@ +#include <fitz.h> +#include <mupdf.h> + +struct entry +{ + int oid; + int gid; + int ofs; + int stmlen; +}; + +static fz_error * +parseobj(fz_file *file, unsigned char *buf, int cap, int *stmlen, + int *isroot, int *isinfo) +{ + fz_error *error; + fz_obj *dict = nil; + fz_obj *length; + fz_obj *filter; + fz_obj *type; + int tok, len; + int stmofs; + + *stmlen = -1; + *isroot = 0; + *isinfo = 0; + + tok = pdf_lex(file, buf, cap, &len); + if (tok == PDF_TODICT) + { + error = pdf_parsedict(&dict, file, buf, cap); + if (error) + return error; + } + + if (fz_isdict(dict)) + { + type = fz_dictgets(dict, "Type"); + if (fz_isname(type) && !strcmp(fz_toname(type), "Catalog")) + *isroot = 1; + + filter = fz_dictgets(dict, "Filter"); + if (fz_isname(filter) && !strcmp(fz_toname(filter), "Standard")) + return fz_throw("cannot repair encrypted files"); + + if (fz_dictgets(dict, "Producer")) + if (fz_dictgets(dict, "Creator")) + if (fz_dictgets(dict, "Title")) + *isinfo = 1; + } + + while ( tok != PDF_TSTREAM && + tok != PDF_TENDOBJ && + tok != PDF_TERROR && + tok != PDF_TEOF ) + tok = pdf_lex(file, buf, cap, &len); + + if (tok == PDF_TSTREAM) + { + int c = fz_readbyte(file); + if (c == '\r') { + c = fz_peekbyte(file); + if (c == '\n') + fz_readbyte(file); + } + + stmofs = fz_tell(file); + + length = fz_dictgets(dict, "Length"); + if (fz_isint(length)) + { + fz_seek(file, stmofs + fz_toint(length)); + tok = pdf_lex(file, buf, cap, &len); + if (tok == PDF_TENDSTREAM) + goto atobjend; + fz_seek(file, stmofs); + } + + fz_read(file, buf, 8); + while (memcmp(buf, "endstream", 8) != 0) + { + c = fz_readbyte(file); + if (c == EOF) + break; + memmove(buf, buf + 1, 7); + buf[7] = c; + } + + *stmlen = fz_tell(file) - stmofs - 8; + +atobjend: + tok = pdf_lex(file, buf, cap, &len); + if (tok == PDF_TENDOBJ) + ; + } + + if (dict) + fz_dropobj(dict); + + return nil; +} + +fz_error * +pdf_repairxref(pdf_xref *xref, char *filename) +{ + fz_error *error; + fz_file *file; + + struct entry *list = nil; + int listlen; + int listcap; + int maxoid; + + unsigned char buf[65536]; + + int oid, gid; + int tmpofs, oidofs, gidofs; + int stmlen; + int isroot, rootoid = 0, rootgid = 0; + int isinfo, infooid = 0, infogid = 0; + int tok, len; + int next; + int n; + int i; + + listlen = 0; + listcap = 1024; + list = fz_malloc(listcap * sizeof(struct entry)); + if (!list) + return fz_outofmem; + + error = fz_openfile(&xref->file, filename, O_RDONLY); + if (error) + goto cleanup; + + file = xref->file; + + n = fz_seek(file, 0); + if (n < 0) { + error = fz_ferror(file); + goto cleanup; + } + + maxoid = 0; + oid = 0; + gid = 0; + oidofs = 0; + gidofs = 0; + + while (1) + { + tmpofs = fz_tell(file); + + tok = pdf_lex(file, buf, sizeof buf, &len); + if (tok == PDF_TINT) + { + oidofs = gidofs; + oid = gid; + gidofs = tmpofs; + gid = atoi(buf); + } + + if (tok == PDF_TOBJ) + { + + error = parseobj(file, buf, sizeof buf, &stmlen, &isroot, &isinfo); + if (error) + goto cleanup; + + if (isroot) { + rootoid = oid; + rootgid = gid; + } + + if (isinfo) { + infooid = oid; + infogid = gid; + } + + if (listlen + 1 == listcap) + { + struct entry *newlist; + listcap = listcap * 2; + newlist = fz_realloc(list, listcap * sizeof(struct entry)); + if (!newlist) { + error = fz_outofmem; + goto cleanup; + } + list = newlist; + } + + list[listlen].oid = oid; + list[listlen].gid = gid; + list[listlen].ofs = oidofs; + list[listlen].stmlen = stmlen; + listlen ++; + + if (oid > maxoid) + maxoid = oid; + } + + if (tok == PDF_TEOF) + break; + } + + error = fz_packobj(&xref->trailer, + "<< /Size %i /Root %r >>", + maxoid + 1, rootoid, rootgid); + if (error) + goto cleanup; + + xref->version = 1.3; /* FIXME */ + xref->size = maxoid + 1; + xref->capacity = xref->size; + xref->table = fz_malloc(xref->capacity * sizeof(pdf_xrefentry)); + if (!xref->table) { + error = fz_outofmem; + goto cleanup; + } + + xref->table[0].type = 'f'; + xref->table[0].mark = 0; + xref->table[0].ofs = 0; + xref->table[0].gen = 65535; + + for (i = 1; i < xref->size; i++) + { + xref->table[i].type = 'f'; + xref->table[i].mark = 0; + xref->table[i].ofs = 0; + xref->table[i].gen = 0; + } + + for (i = 0; i < listlen; i++) + { + xref->table[list[i].oid].type = 'n'; + xref->table[list[i].oid].ofs = list[i].ofs; + xref->table[list[i].oid].gen = list[i].gid; + xref->table[list[i].oid].mark = 0; + + /* corrected stream length */ + if (list[i].stmlen >= 0) + { + fz_obj *dict, *length; + error = pdf_loadobject0(&dict, xref, list[i].oid, list[i].gid, nil); + if (error) + goto cleanup; + error = fz_newint(&length, list[i].stmlen); + if (error) + goto cleanup; + error = fz_dictputs(dict, "Length", length); + if (error) + goto cleanup; + error = pdf_saveobject(xref, list[i].oid, list[i].gid, dict); + if (error) + goto cleanup; + } + } + + next = 0; + for (i = xref->size - 1; i >= 0; i--) + { + if (xref->table[i].type == 'f') + { + xref->table[i].ofs = next; + if (xref->table[i].gen < 65535) + xref->table[i].gen ++; + next = i; + } + } + + fz_free(list); + return nil; + +cleanup: + fz_free(list); + return error; +} + diff --git a/mupdf/resources.c b/mupdf/resources.c new file mode 100644 index 00000000..050b003d --- /dev/null +++ b/mupdf/resources.c @@ -0,0 +1,184 @@ +#include <fitz.h> +#include <mupdf.h> + +static fz_error * +loadextgstatefonts(pdf_resources *rdb, pdf_xref *xref) +{ + fz_error *err; + char name[64]; + pdf_font *font; + fz_obj *extgstate; + fz_obj *obj; + fz_obj *ptr; + fz_obj *ref; + int i; + + for (i = 0; i < fz_dictlen(rdb->extgstate); i++) + { + extgstate = fz_dictgetval(rdb->extgstate, i); + + obj = fz_dictgets(extgstate, "Font"); + if (obj) + { + font = nil; + ptr = nil; + + if (!fz_isarray(obj) || fz_arraylen(obj) != 2) + return fz_throw("syntaxerror in ExtGState/Font"); + + ref = fz_arrayget(obj, 0); + sprintf(name, "$f.%d.%d", fz_toobjid(ref), fz_togenid(ref)); + + err = pdf_resolve(&ref, xref); + if (err) return err; + + err = pdf_loadfont(&font, xref, ref); + if (err) goto cleanup; + + err = fz_newpointer(&ptr, font); + if (err) goto cleanup; + + err = fz_dictputs(rdb->font, name, ptr); + if (err) goto cleanup; + + fz_dropobj(ptr); + fz_dropobj(ref); + } + } + + return nil; + +cleanup: + if (font) fz_freefont((fz_font*)font); + if (ptr) fz_dropobj(ptr); + fz_dropobj(ref); + return err; +} + +static fz_error * +loadextgstates(pdf_resources *rdb, pdf_xref *xref, fz_obj *dict) +{ + fz_error *err; + fz_obj *key, *val; + int i; + + for (i = 0; i < fz_dictlen(dict); i++) + { + key = fz_dictgetkey(dict, i); + val = fz_dictgetval(dict, i); + + err = pdf_resolve(&val, xref); + if (err) return err; + + err = fz_dictput(rdb->extgstate, key, val); + if (err) { fz_dropobj(val); return err; } + + fz_dropobj(val); + } + + return nil; +} + +static fz_error * +loadfonts(pdf_resources *rdb, pdf_xref *xref, fz_obj *dict) +{ + fz_error *err; + pdf_font *font; + fz_obj *key, *val; + fz_obj *ptr; + int i; + + for (i = 0; i < fz_dictlen(dict); i++) + { + font = nil; + ptr = nil; + + key = fz_dictgetkey(dict, i); + val = fz_dictgetval(dict, i); + + err = pdf_resolve(&val, xref); + if (err) return err; + + err = pdf_loadfont(&font, xref, val); + if (err) goto cleanup; + + err = fz_newpointer(&ptr, font); + if (err) goto cleanup; + + err = fz_dictput(rdb->font, key, ptr); + if (err) goto cleanup; + + fz_dropobj(ptr); + fz_dropobj(val); + } + + return nil; + +cleanup: + if (font) fz_freefont((fz_font*)font); + if (ptr) fz_dropobj(ptr); + fz_dropobj(val); + return err; +} + +fz_error * +pdf_loadresources(pdf_resources **rdbp, pdf_xref *xref, fz_obj *topdict) +{ + fz_error *err; + pdf_resources *rdb; + fz_obj *subdict; + + rdb = *rdbp = fz_malloc(sizeof (pdf_resources)); + if (!rdb) + return fz_outofmem; + + rdb->extgstate = nil; + rdb->font = nil; + rdb->colorspace = nil; + rdb->ximage = nil; + rdb->xform = nil; + + err = fz_newdict(&rdb->extgstate, 5); + if (err) { pdf_freeresources(rdb); return err; } + + subdict = fz_dictgets(topdict, "ExtGState"); + if (subdict) + { + err = pdf_resolve(&subdict, xref); + if (err) { pdf_freeresources(rdb); return err; } + err = loadextgstates(rdb, xref, subdict); + fz_dropobj(subdict); + if (err) { pdf_freeresources(rdb); return err; } + } + + err = fz_newdict(&rdb->font, 15); + if (err) { pdf_freeresources(rdb); return err; } + + err = loadextgstatefonts(rdb, xref); + if (err) { pdf_freeresources(rdb); return err; } + + subdict = fz_dictgets(topdict, "Font"); + if (subdict) + { + err = pdf_resolve(&subdict, xref); + if (err) { pdf_freeresources(rdb); return err; } + err = loadfonts(rdb, xref, subdict); + fz_dropobj(subdict); + if (err) { pdf_freeresources(rdb); return err; } + } + + return nil; +} + +void +pdf_freeresources(pdf_resources *rdb) +{ + /* TODO freefont */ + if (rdb->extgstate) fz_dropobj(rdb->extgstate); + if (rdb->colorspace) fz_dropobj(rdb->colorspace); + if (rdb->font) fz_dropobj(rdb->font); + if (rdb->ximage) fz_dropobj(rdb->ximage); + if (rdb->xform) fz_dropobj(rdb->xform); + fz_free(rdb); +} + diff --git a/mupdf/save.c b/mupdf/save.c new file mode 100644 index 00000000..c86c9e72 --- /dev/null +++ b/mupdf/save.c @@ -0,0 +1,314 @@ +#include <fitz.h> +#include <mupdf.h> + +static fz_error *writestored(FILE *out, pdf_xref *xref, int oid) +{ + pdf_xrefentry *x = xref->table + oid; + fz_error *error; + fz_obj *obj; + fz_buffer *stm; + + obj = pdf_findstoredobject(xref->store, oid, x->gen); + stm = pdf_findstoredstream(xref->store, oid, x->gen); + + if (!obj) + return fz_throw("could not find stored object"); + + if (xref->crypt) + pdf_cryptobj(xref->crypt, obj, oid, x->gen); + + fprintf(out, "%d %d obj\n", oid, x->gen); + fz_fprintobj(out, obj); + fprintf(out, "\n"); + + if (stm) + { + fprintf(out, "stream\n"); + fwrite(stm->rp, 1, stm->wp - stm->rp, out); + fprintf(out, "endstream\n"); + } + + fprintf(out, "endobj\n\n"); + + if (ferror(out)) + return fz_throw("ioerror: write failed"); + + return nil; +} + +static fz_error *writecopy(FILE *out, pdf_xref *xref, int oid) +{ + pdf_xrefentry *x = xref->table + oid; + fz_error *error; + fz_obj *length; + fz_obj *obj; + int stmofs; + fz_filter *cf; + fz_filter *nf; + fz_filter *pipe; + int n; + unsigned char buf[4096]; + + error = pdf_loadobject0(&obj, xref, oid, x->gen, &stmofs); + if (error) + return error; + + fprintf(out, "%d %d obj\n", oid, x->gen); + fz_fprintobj(out, obj); + fprintf(out, "\n"); + + if (stmofs != -1) + { + fprintf(out, "stream\n"); + + length = fz_dictgets(obj, "Length"); + error = pdf_resolve(&length, xref); + if (error) + goto cleanup; + + if (xref->crypt) + { + error = fz_newnullfilter(&nf, fz_toint(length)); + if (error) + goto cleanup; + error = pdf_cryptstm(&cf, xref->crypt, oid, x->gen); + if (error) + goto cleanup; + error = fz_newpipeline(&pipe, nf, cf); + if (error) + goto cleanup; + } + else + { + error = fz_newnullfilter(&pipe, fz_toint(length)); + if (error) + goto cleanup; + } + + fz_seek(xref->file, stmofs); + fz_pushfilter(xref->file, pipe); + + while (1) + { + n = fz_read(xref->file, buf, sizeof buf); + if (n == 0) + break; + if (n < 0) + { + error = fz_ferror(xref->file); + fz_popfilter(xref->file); + goto cleanup; + } + fwrite(buf, 1, n, out); + } + + fz_popfilter(xref->file); + + fprintf(out, "endstream\n"); + } + + fprintf(out, "endobj\n\n"); + + fz_dropobj(obj); + + if (ferror(out)) + return fz_throw("ioerror: write failed"); + + return nil; + +cleanup: + fz_dropobj(obj); + return error; +} + +static int countmodified(pdf_xref *xref, int oid) +{ + int i; + for (i = oid; i < xref->size; i++) + if (xref->table[i].type != 'a' && xref->table[i].type != 'd') + return i - oid; + return i - oid; +} + +fz_error * +pdf_saveincrementalpdf(pdf_xref *xref, char *path) +{ + fz_error *error; + FILE *out; + int oid; + int i, n; + int startxref; + fz_obj *obj; + + out = fopen(path, "ab"); + if (!out) + return fz_throw("ioerror: could not open '%s': %s", path, strerror(errno)); + fprintf(out, "\n"); + + for (oid = 0; oid < xref->size; oid++) + { + if (xref->table[oid].type == 'a') + { + xref->table[oid].ofs = ftell(out); + error = writestored(out, xref, oid); + if (error) + goto cleanup; + } + } + + /* always write out entry 0 in appended xref sections */ + xref->table[0].type = 'd'; + + startxref = ftell(out); + fprintf(out, "xref\n"); + + oid = 0; + while (oid < xref->size) + { + n = countmodified(xref, oid); + + fprintf(out, "%d %d\n", oid, n); + + for (i = 0; i < n; i++) + { + if (xref->table[oid + i].type == 'd') + xref->table[oid + i].type = 'f'; + if (xref->table[oid + i].type == 'a') + xref->table[oid + i].type = 'n'; + + fprintf(out, "%010d %05d %c \n", + xref->table[oid + i].ofs, + xref->table[oid + i].gen, + xref->table[oid + i].type); + } + + oid += n; + while (oid < xref->size && + xref->table[oid].type != 'a' && + xref->table[oid].type != 'd') + oid ++; + } + + fprintf(out, "\n"); + + fprintf(out, "trailer\n<<\n /Size %d\n /Prev %d", xref->size, xref->startxref); + + obj = fz_dictgets(xref->trailer, "Root"); + fprintf(out,"\n /Root %d %d R", fz_toobjid(obj), fz_togenid(obj)); + + obj = fz_dictgets(xref->trailer, "Info"); + if (obj) + fprintf(out,"\n /Info %d %d R", fz_toobjid(obj), fz_togenid(obj)); + + obj = fz_dictgets(xref->trailer, "Encrypt"); + if (obj) { + fprintf(out,"\n /Encrypt "); + fz_fprintobj(out, obj); + } + + obj = fz_dictgets(xref->trailer, "ID"); + if (obj) { + fprintf(out,"\n /ID "); + fz_fprintobj(out, obj); + } + + fprintf(out, "\n>>\n\n"); + + fprintf(out, "startxref\n"); + fprintf(out, "%d\n", startxref); + fprintf(out, "%%%%EOF\n"); + + xref->startxref = startxref; + + fclose(out); + return nil; + +cleanup: + fclose(out); + return error; +} + +fz_error * +pdf_savepdf(pdf_xref *xref, char *path) +{ + fz_error *error; + FILE *out; + int oid; + int startxref; + int *ofsbuf; + fz_obj *obj; + + ofsbuf = fz_malloc(sizeof(int) * xref->size); + if (!ofsbuf) + return fz_outofmem; + + out = fopen(path, "wb"); + if (!out) + { + fz_free(ofsbuf); + return fz_throw("ioerror: could not open '%s': %s", path, strerror(errno)); + } + + fprintf(out, "%%PDF-%1.1f\n", xref->version); + fprintf(out, "%%\342\343\317\323\n\n"); + + for (oid = 0; oid < xref->size; oid++) + { + if (xref->table[oid].type == 'n' || xref->table[oid].type == 'o') + { + ofsbuf[oid] = ftell(out); + error = writecopy(out, xref, oid); + if (error) + goto cleanup; + } + else if (xref->table[oid].type == 'a') + { + ofsbuf[oid] = ftell(out); + error = writestored(out, xref, oid); + if (error) + goto cleanup; + } + else + { + ofsbuf[oid] = xref->table[oid].ofs; + } + } + + startxref = ftell(out); + fprintf(out, "xref\n"); + fprintf(out, "0 %d\n", xref->size); + + for (oid = 0; oid < xref->size; oid++) + { + int type = xref->table[oid].type; + if (type == 'a' || type == 'o') + type = 'n'; + if (type == 'd') + type = 'f'; + fprintf(out, "%010d %05d %c \n", ofsbuf[oid], xref->table[oid].gen, type); + } + + fprintf(out, "\n"); + + fprintf(out, "trailer\n<<\n /Size %d", xref->size); + obj = fz_dictgets(xref->trailer, "Root"); + fprintf(out,"\n /Root %d %d R", fz_toobjid(obj), fz_togenid(obj)); + obj = fz_dictgets(xref->trailer, "Info"); + if (obj) + fprintf(out,"\n /Info %d %d R", fz_toobjid(obj), fz_togenid(obj)); + fprintf(out, "\n>>\n\n"); + + fprintf(out, "startxref\n"); + fprintf(out, "%d\n", startxref); + fprintf(out, "%%%%EOF\n"); + + xref->startxref = startxref; + + fclose(out); + return nil; + +cleanup: + fclose(out); + return error; +} + diff --git a/mupdf/stream.c b/mupdf/stream.c new file mode 100644 index 00000000..f3f40e53 --- /dev/null +++ b/mupdf/stream.c @@ -0,0 +1,262 @@ +#include <fitz.h> +#include <mupdf.h> + +static fz_error * +makefilter(fz_filter **fp, fz_obj *f, fz_obj *p) +{ + fz_filter *predf = nil; + fz_filter *realf = nil; + fz_error *error; + char *s; + + s = fz_toname(f); + + if (!strcmp(s, "ASCIIHexDecode") || !strcmp(s, "AHx")) + return fz_newahxd(fp, p); + + if (!strcmp(s, "ASCII85Decode") || !strcmp(s, "A85")) + return fz_newa85d(fp, p); + + if (!strcmp(s, "CCITTFaxDecode") || !strcmp(s, "CCF")) + return fz_newfaxd(fp, p); + + if (!strcmp(s, "DCTDecode") || !strcmp(s, "DCT")) + return fz_newdctd(fp, p); + + if (!strcmp(s, "RunLengthDecode") || !strcmp(s, "RL")) + return fz_newrld(fp, p); + + if (!strcmp(s, "JPXDecode")) + return fz_newjpxd(fp, p); + + if (!strcmp(s, "FlateDecode") || !strcmp(s, "Fl")) + { + if (fz_isdict(p)) { + fz_obj *obj = fz_dictgets(p, "Predictor"); + if (obj) { + error = fz_newflated(&realf, p); + if (error) goto cleanup; + error = fz_newpredictd(&predf, p); + if (error) goto cleanup; + error = fz_newpipeline(fp, realf, predf); + if (error) goto cleanup; + return nil; + } + } + return fz_newflated(fp, p); + } + + if (!strcmp(s, "LZWDecode") || !strcmp(s, "LZW")) + { + if (fz_isdict(p)) { + fz_obj *obj = fz_dictgets(p, "Predictor"); + if (obj) { + error = fz_newlzwd(&realf, p); + if (error) goto cleanup; + error = fz_newpredictd(&predf, p); + if (error) goto cleanup; + error = fz_newpipeline(fp, realf, predf); + if (error) goto cleanup; + return nil; + } + } + return fz_newlzwd(fp, p); + } + + if (!strcmp(s, "JBIG2Decode")) { + /* TODO: extract and feed JBIG2Global */ + return fz_newjbig2d(fp, p); + } + + return fz_throw("syntaxerror: unknown filter: %s", s); + +cleanup: + if (realf) fz_freefilter(realf); + if (predf) fz_freefilter(predf); + return error; +} + +static fz_error * +makepipeline(fz_filter **fp, fz_obj *fs, fz_obj *ps) +{ + fz_error *error; + fz_filter *filter = nil; + fz_filter *pipe = nil; + fz_obj *f; + fz_obj *p; + int i; + + for (i = 0; i < fz_arraylen(fs); i++) + { + f = fz_arrayget(fs, i); + + if (fz_isarray(ps)) + p = fz_arrayget(ps, i); + else + p = nil; + + error = makefilter(&filter, f, p); + if (error) { if (pipe) fz_freefilter(pipe); return error; } + + if (pipe) { + fz_filter *np; + error = fz_newpipeline(&np, pipe, filter); + if (error) { fz_freefilter(pipe); return error; } + pipe = np; + } + else pipe = filter; + } + + *fp = pipe; + return nil; +} + +fz_error * +pdf_buildfilter(fz_filter **fp, pdf_xref *xref, fz_obj *stmobj, int oid, int gid) +{ + fz_error *error; + fz_filter *filter; + fz_obj *filters; + fz_obj *params; + fz_obj *obj; + int len; + + obj = fz_dictgets(stmobj, "Length"); + error = pdf_resolve(&obj, xref); + if (error) + return error; + len = fz_toint(obj); + fz_dropobj(obj); + + filters = fz_dictgets(stmobj, "Filter"); + params = fz_dictgets(stmobj, "DecodeParms"); + + if (!filters) + { + error = fz_newnullfilter(&filter, len); + if (error) + return error; + } + + else if (fz_isname(filters)) + { + error = makefilter(&filter, filters, params); + if (error) + return error; + } + + else if (fz_isarray(filters)) + { + if (fz_arraylen(filters) == 0) + error = fz_newnullfilter(&filter, len); + else + error = makepipeline(&filter, filters, params); + if (error) + return error; + } + + else + { + return fz_throw("typecheck in buildstream"); + } + + if (xref->crypt) + { + fz_filter *cryptfilter; + fz_filter *pipeline; + + error = pdf_cryptstm(&cryptfilter, xref->crypt, oid, gid); + if (error) + { + fz_freefilter(filter); + return error; + } + + error = fz_newpipeline(&pipeline, cryptfilter, filter); + if (error) + { + fz_freefilter(cryptfilter); + fz_freefilter(filter); + return error; + } + + filter = pipeline; + } + + *fp = filter; + + return nil; +} + +fz_error * +pdf_openstream0(pdf_xref *xref, fz_obj *stmobj, int oid, int gid, int ofs) +{ + fz_error *error; + fz_filter *filter; + + error = pdf_buildfilter(&filter, xref, stmobj, oid, gid); + if (error) + return error; + + ofs = fz_seek(xref->file, ofs); + if (ofs < 0) { + fz_freefilter(filter); + return fz_ferror(xref->file); + } + + error = fz_pushfilter(xref->file, filter); + if (error) { + fz_freefilter(filter); + return error; + } + + return nil; +} + +fz_error * +pdf_openstream(pdf_xref *xref, fz_obj *stmref) +{ + fz_error *error; + fz_obj *stmobj; + int oid, gid, ofs; + + oid = fz_toobjid(stmref); + gid = fz_togenid(stmref); + + error = pdf_loadobject0(&stmobj, xref, oid, gid, &ofs); + if (error) + return error; + + error = pdf_openstream0(xref, stmobj, oid, gid, ofs); + if (error) { + fz_dropobj(stmobj); + return error; + } + + fz_dropobj(stmobj); + + return nil; +} + +void +pdf_closestream(pdf_xref *xref) +{ + fz_popfilter(xref->file); +} + +fz_error * +pdf_readstream(unsigned char **bufp, int *lenp, pdf_xref *xref, fz_obj *stmref) +{ + fz_error *error; + + error = pdf_openstream(xref, stmref); + if (error) + return error; + + error = fz_readfile(bufp, lenp, xref->file); + + pdf_closestream(xref); + + return error; +} + diff --git a/mupdf/xref.c b/mupdf/xref.c new file mode 100644 index 00000000..37cc16c2 --- /dev/null +++ b/mupdf/xref.c @@ -0,0 +1,410 @@ +#include <fitz.h> +#include <mupdf.h> + +/* + * initialize xref + */ + +fz_error * +pdf_newxref(pdf_xref **xrefp) +{ + fz_error *error; + pdf_xref *xref; + + xref = *xrefp = fz_malloc(sizeof(pdf_xref)); + if (!xref) + return fz_outofmem; + + xref->version = 1.3; + xref->crypt = nil; + xref->file = nil; + xref->size = 0; + xref->capacity = 0; + xref->table = nil; + xref->trailer = nil; + xref->startxref = -1; + + error = fz_newhash(&xref->store, 256, sizeof(int) * 3); + if (error) + { + fz_free(xref); + return error; + } + + return nil; +} + +fz_error * +pdf_decryptxref(pdf_xref *xref) +{ + fz_error *error; + fz_obj *encrypt; + fz_obj *id; + + if (xref->size < 0) + return fz_throw("rangecheck: xref missing first slot"); + + encrypt = fz_dictgets(xref->trailer, "Encrypt"); + id = fz_dictgets(xref->trailer, "ID"); + + if (encrypt && id) + { + error = pdf_resolve(&encrypt, xref); + if (error) + goto cleanup; + + error = pdf_resolve(&id, xref); + if (error) + goto cleanup; + + error = pdf_newdecrypt(&xref->crypt, encrypt, id); + if (error) + goto cleanup; + + fz_dropobj(encrypt); + fz_dropobj(id); + } + + return nil; + +cleanup: + if (encrypt) fz_dropobj(encrypt); + if (id) fz_dropobj(id); + return error; +} + +void +pdf_closexref(pdf_xref *xref) +{ + int *key; + void *val; + int i; + + if (xref->store) + { + for (i = 0; i < fz_hashlen(xref->store); i++) + { + key = fz_gethashkey(xref->store, i); + val = fz_gethashval(xref->store, i); + if (val && key[2] == 0) + fz_dropobj((fz_obj*)val); + if (val && key[2] == 1) + fz_freebuffer((fz_buffer*)val); + } + fz_freehash(xref->store); + } + + if (xref->table) + fz_free(xref->table); + if (xref->trailer) + fz_dropobj(xref->trailer); + if (xref->file) + fz_closefile(xref->file); + fz_free(xref); +} + +void +pdf_debugxref(pdf_xref *xref) +{ + int i; + printf("%%!PDF-%g\n", xref->version); + printf("xref\n0 %d\n", xref->size); + for (i = 0; i < xref->size; i++) + { + printf("%010d %05d %c \n", + xref->table[i].ofs, + xref->table[i].gen, + xref->table[i].type); + } + printf("trailer\n"); + fz_fprintobj(stdout, xref->trailer); + printf("\n"); +} + +/* + * object and stream store (cached from objstm and saved for mutation) + */ + +fz_obj * +pdf_findstoredobject(fz_hashtable *store, int oid, int gid) +{ + int key[3]; + key[0] = oid; + key[1] = gid; + key[2] = 0; + return fz_hashfind(store, key); +} + +fz_buffer * +pdf_findstoredstream(fz_hashtable *store, int oid, int gid) +{ + int key[3]; + key[0] = oid; + key[1] = gid; + key[2] = 1; + return fz_hashfind(store, key); +} + +fz_error * +pdf_deletestoredobject(fz_hashtable *store, int oid, int gid) +{ + int key[3]; + fz_obj *obj; + key[0] = oid; + key[1] = gid; + key[2] = 0; + obj = fz_hashfind(store, key); + if (obj) + fz_dropobj(obj); + return fz_hashremove(store, key); +} + +fz_error * +pdf_deletestoredstream(fz_hashtable *store, int oid, int gid) +{ + int key[3]; + fz_buffer *stm; + key[0] = oid; + key[1] = gid; + key[2] = 1; + stm = fz_hashfind(store, key); + if (stm) + fz_freebuffer(stm); + return fz_hashremove(store, key); +} + +fz_error * +pdf_storeobject(fz_hashtable *store, int oid, int gid, fz_obj *obj) +{ + int key[3]; + key[0] = oid; + key[1] = gid; + key[2] = 0; + return fz_hashinsert(store, key, obj); +} + +fz_error * +pdf_storestream(fz_hashtable *store, int oid, int gid, fz_buffer *buf) +{ + int key[3]; + key[0] = oid; + key[1] = gid; + key[2] = 1; + return fz_hashinsert(store, key, buf); +} + +/* + * mutate objects + */ + +static int findprev(pdf_xref *xref, int oid) +{ + int prev; + for (prev = oid - 1; prev >= 0; prev--) + if (xref->table[prev].type == 'f' || xref->table[prev].type == 'd') + return prev; + return 0; +} + +static int findnext(pdf_xref *xref, int oid) +{ + int next; + for (next = oid + 1; next < xref->size; next++) + if (xref->table[next].type == 'f' || xref->table[next].type == 'd') + return next; + return 0; +} + +fz_error * +pdf_createobject(pdf_xref *xref, int *oidp, int *gidp) +{ + pdf_xrefentry *x; + int prev; + int oid = 0; + + while (1) + { + x = xref->table + oid; + + if (x->type == 'f' || x->type == 'd') + { + if (x->gen < 65535) + { + *oidp = oid; + *gidp = x->gen; + return nil; + } + } + + oid = x->ofs; + + if (oid == 0) + break; + } + + if (xref->size + 1 >= xref->capacity) + { + int newcap = xref->capacity + 256; + pdf_xrefentry *newtable; + + newtable = fz_realloc(xref->table, sizeof(pdf_xrefentry) * newcap); + if (!newtable) + return fz_outofmem; + + xref->table = newtable; + xref->capacity = newcap; + } + + oid = xref->size ++; + + xref->table[oid].type = 'd'; + xref->table[oid].mark = 0; + xref->table[oid].ofs = 0; + xref->table[oid].gen = 0; + + prev = findprev(xref, oid); + xref->table[prev].type = 'd'; + xref->table[prev].ofs = oid; + + *oidp = oid; + *gidp = 0; + + return nil; +} + +fz_error * +pdf_deleteobject(pdf_xref *xref, int oid, int gid) +{ + pdf_xrefentry *x; + int prev; + + if (oid < 0 || oid >= xref->size) + return fz_throw("rangecheck: invalid object number"); + + x = xref->table + oid; + + if (x->type != 'n' && x->type != 'o' && x->type == 'a') + return fz_throw("rangecheck: delete nonexistant object"); + + x->type = 'd'; + x->ofs = findnext(xref, oid); + x->gen ++; + + prev = findprev(xref, oid); + xref->table[prev].type = 'd'; + xref->table[prev].ofs = oid; + + return nil; +} + +fz_error * +pdf_saveobject(pdf_xref *xref, int oid, int gid, fz_obj *obj) +{ + fz_error *error; + pdf_xrefentry *x; + + if (oid < 0 || oid >= xref->size) + return fz_throw("rangecheck: invalid object number"); + + error = pdf_storeobject(xref->store, oid, gid, obj); + if (error) + return error; + + x = xref->table + oid; + + if (x->type == 'f' || x->type == 'd') + { + int prev = findprev(xref, oid); + int next = findnext(xref, oid); + xref->table[prev].type = 'd'; + xref->table[prev].ofs = next; + } + + x->type = 'a'; + + return nil; +} + +/* + * object loading + */ + +fz_error * +pdf_loadobject0(fz_obj **objp, pdf_xref *xref, int oid, int gid, int *stmofs) +{ + unsigned char buf[65536]; /* yeowch! */ + + fz_error *error; + pdf_xrefentry *x; + int roid, rgid; + int n; + + if (oid < 0 || oid >= xref->size) + return fz_throw("rangecheck: object number out of range: %d", oid); + + if (stmofs) + *stmofs = -1; + + x = &xref->table[oid]; + + if (x->type == 'f' || x->type == 'd') + return fz_throw("rangecheck: tried to load free object"); + + else if (x->type == 'n') + { + n = fz_seek(xref->file, x->ofs); + if (n < 0) + return fz_ferror(xref->file); + + error = pdf_parseindobj(objp, xref->file, buf, sizeof buf, + &roid, &rgid, stmofs); + if (error) + return error; + + if (xref->crypt) + pdf_cryptobj(xref->crypt, *objp, oid, gid); + } + + else if (x->type == 'o') + { + *objp = pdf_findstoredobject(xref->store, oid, gid); + if (*objp) + return nil; + + error = pdf_readobjstm(xref, x->ofs, 0, buf, sizeof buf); + if (error) + return error; + + *objp = pdf_findstoredobject(xref->store, oid, gid); + if (!*objp) + return fz_throw("rangecheck: could not find object"); + } + + else if (x->type == 'a') + { + *objp = pdf_findstoredobject(xref->store, oid, gid); + if (!*objp) + return fz_throw("rangecheck: could not find object"); + } + + else + return fz_throw("rangecheck: unknown object type"); + + return nil; +} + +fz_error * +pdf_loadobject(fz_obj **objp, pdf_xref *xref, fz_obj *ref, int *stmofs) +{ + return pdf_loadobject0(objp, xref, fz_toobjid(ref), fz_togenid(ref), stmofs); +} + +fz_error * +pdf_resolve(fz_obj **objp, pdf_xref *xref) +{ + if (fz_isindirect(*objp)) + return pdf_loadobject(objp, xref, *objp, nil); + fz_keepobj(*objp); + return nil; +} + diff --git a/object/array.c b/object/array.c new file mode 100644 index 00000000..d7beeb38 --- /dev/null +++ b/object/array.c @@ -0,0 +1,182 @@ +#include <fitz.h> + +fz_error * +fz_newarray(fz_obj **op, int initialcap) +{ + fz_obj *obj; + int i; + + obj = *op = fz_malloc(sizeof (fz_obj)); + if (!obj) return fz_outofmem; + + obj->kind = FZ_ARRAY; + obj->refcount = 1; + + obj->u.a.len = 0; + obj->u.a.cap = initialcap > 0 ? initialcap : 6; + + obj->u.a.items = fz_malloc(sizeof (fz_obj*) * obj->u.a.cap); + if (!obj->u.a.items) { fz_free(obj); return fz_outofmem; } + + for (i = 0; i < obj->u.a.cap; i++) + obj->u.a.items[i] = nil; + + return nil; +} + +fz_error * +fz_copyarray(fz_obj **op, fz_obj *obj) +{ + fz_error *err; + fz_obj *new; + int i; + + if (!fz_isarray(obj)) + return fz_throw("typecheck in copyarray"); + + err = fz_newarray(&new, fz_arraylen(obj)); + if (err) return err; + *op = new; + + for (i = 0; i < fz_arraylen(obj); i++) { + err = fz_arraypush(new, fz_arrayget(obj, i)); + if (err) { fz_freearray(new); return err; } + } + + return nil; +} + +fz_error * +fz_deepcopyarray(fz_obj **op, fz_obj *obj) +{ + fz_error *err; + fz_obj *new; + fz_obj *val; + int i; + + if (!fz_isarray(obj)) + return fz_throw("typecheck in deepcopyarray"); + + err = fz_newarray(&new, fz_arraylen(obj)); + if (err) return err; + *op = new; + + for (i = 0; i < fz_arraylen(obj); i++) + { + val = fz_arrayget(obj, i); + + if (fz_isarray(val)) { + err = fz_deepcopyarray(&val, val); + if (err) { fz_freearray(new); return err; } + err = fz_arraypush(new, val); + if (err) { fz_dropobj(val); fz_freearray(new); return err; } + fz_dropobj(val); + } + + else if (fz_isdict(val)) { + err = fz_deepcopydict(&val, val); + if (err) { fz_freearray(new); return err; } + err = fz_arraypush(new, val); + if (err) { fz_dropobj(val); fz_freearray(new); return err; } + fz_dropobj(val); + } + + else { + err = fz_arraypush(new, val); + if (err) { fz_freearray(new); return err; } + } + } + + return nil; +} + +int +fz_arraylen(fz_obj *obj) +{ + if (!fz_isarray(obj)) + return 0; + return obj->u.a.len; +} + +fz_obj * +fz_arrayget(fz_obj *obj, int i) +{ + if (!fz_isarray(obj)) + return nil; + + if (i < 0 || i >= obj->u.a.len) + return nil; + + return obj->u.a.items[i]; +} + +fz_error * +fz_arrayput(fz_obj *obj, int i, fz_obj *item) +{ + if (!fz_isarray(obj)) + return fz_throw("typecheck in arrayput"); + if (i < 0) + return fz_throw("rangecheck in arrayput: %d < 0", i); + if (i >= obj->u.a.len) + return fz_throw("rangecheck in arrayput: %d > %d", i, obj->u.a.len); + + if (obj->u.a.items[i]) + fz_dropobj(obj->u.a.items[i]); + obj->u.a.items[i] = fz_keepobj(item); + + return nil; +} + +static fz_error * +growarray(fz_obj *obj) +{ + fz_obj **newitems; + int newcap; + int i; + + newcap = obj->u.a.cap * 2; + newitems = fz_realloc(obj->u.a.items, sizeof (fz_obj*) * newcap); + if (!newitems) return fz_outofmem; + + obj->u.a.items = newitems; + for (i = obj->u.a.cap ; i < newcap; i++) + obj->u.a.items[i] = nil; + obj->u.a.cap = newcap; + + return nil; +} + +fz_error * +fz_arraypush(fz_obj *obj, fz_obj *item) +{ + fz_error *err; + + if (!fz_isarray(obj)) + return fz_throw("typecheck in arraypush"); + + if (obj->u.a.len + 1 > obj->u.a.cap) { + err = growarray(obj); + if (err) return err; + } + + obj->u.a.items[obj->u.a.len] = fz_keepobj(item); + obj->u.a.len++; + + return nil; +} + +void +fz_freearray(fz_obj *obj) +{ + int i; + + assert(obj->kind == FZ_ARRAY); + + for (i = 0; i < obj->u.a.len; i++) + if (obj->u.a.items[i]) + fz_dropobj(obj->u.a.items[i]); + + fz_free(obj->u.a.items); + fz_free(obj); +} + diff --git a/object/dict.c b/object/dict.c new file mode 100644 index 00000000..fd2f3382 --- /dev/null +++ b/object/dict.c @@ -0,0 +1,260 @@ +#include <fitz.h> + +fz_error * +fz_newdict(fz_obj **op, int initialcap) +{ + fz_obj *obj; + int i; + + obj = *op = fz_malloc(sizeof (fz_obj)); + if (!obj) return fz_outofmem; + + obj->kind = FZ_DICT; + obj->refcount = 1; + + obj->u.d.len = 0; + obj->u.d.cap = initialcap > 0 ? initialcap : 10; + + obj->u.d.items = fz_malloc(sizeof(struct fz_keyval_s) * obj->u.d.cap); + if (!obj->u.d.items) { fz_free(obj); return fz_outofmem; } + + for (i = 0; i < obj->u.d.cap; i++) { + obj->u.d.items[i].k = nil; + obj->u.d.items[i].v = nil; + } + + return nil; +} + +fz_error * +fz_copydict(fz_obj **op, fz_obj *obj) +{ + fz_error *err; + fz_obj *new; + int i; + + if (!fz_isdict(obj)) + return fz_throw("typecheck in copydict"); + + err = fz_newdict(&new, obj->u.d.cap); + if (err) return err; + *op = new; + + for (i = 0; i < fz_dictlen(obj); i++) { + err = fz_dictput(new, fz_dictgetkey(obj, i), fz_dictgetval(obj, i)); + if (err) { fz_freedict(new); return err; } + } + + return nil; +} + +fz_error * +fz_deepcopydict(fz_obj **op, fz_obj *obj) +{ + fz_error *err; + fz_obj *new; + fz_obj *val; + int i; + + if (!fz_isdict(obj)) + return fz_throw("typecheck in deepcopydict"); + + err = fz_newdict(&new, obj->u.d.cap); + if (err) return err; + *op = new; + + for (i = 0; i < fz_dictlen(obj); i++) + { + val = fz_dictgetval(obj, i); + + if (fz_isarray(val)) { + err = fz_deepcopyarray(&val, val); + if (err) { fz_freedict(new); return err; } + err = fz_dictput(new, fz_dictgetkey(obj, i), val); + if (err) { fz_dropobj(val); fz_freedict(new); return err; } + fz_dropobj(val); + } + + else if (fz_isdict(val)) { + err = fz_deepcopydict(&val, val); + if (err) { fz_freedict(new); return err; } + err = fz_dictput(new, fz_dictgetkey(obj, i), val); + if (err) { fz_dropobj(val); fz_freedict(new); return err; } + fz_dropobj(val); + } + + else { + err = fz_dictput(new, fz_dictgetkey(obj, i), val); + if (err) { fz_freedict(new); return err; } + } + } + + return nil; +} + +static fz_error * +growdict(fz_obj *obj) +{ + struct fz_keyval_s *newitems; + int newcap; + int i; + + newcap = obj->u.d.cap * 2; + + newitems = fz_realloc(obj->u.d.items, sizeof(struct fz_keyval_s) * newcap); + if (!newitems) return fz_outofmem; + + obj->u.d.items = newitems; + for (i = obj->u.d.cap; i < newcap; i++) { + obj->u.d.items[i].k = nil; + obj->u.d.items[i].v = nil; + } + obj->u.d.cap = newcap; + + return nil; +} + +int +fz_dictlen(fz_obj *obj) +{ + if (!fz_isdict(obj)) + return 0; + return obj->u.d.len; +} + +fz_obj * +fz_dictgetkey(fz_obj *obj, int i) +{ + if (!fz_isdict(obj)) + return nil; + + if (i < 0 || i >= obj->u.d.len) + return nil; + + return obj->u.d.items[i].k; +} + +fz_obj * +fz_dictgetval(fz_obj *obj, int i) +{ + if (!fz_isdict(obj)) + return nil; + + if (i < 0 || i >= obj->u.d.len) + return nil; + + return obj->u.d.items[i].v; +} + +fz_obj * +fz_dictgets(fz_obj *obj, char *key) +{ + int i; + + if (!fz_isdict(obj)) + return nil; + + for (i = 0; i < obj->u.d.len; i++) + if (strcmp(fz_toname(obj->u.d.items[i].k), key) == 0) + return obj->u.d.items[i].v; + + return nil; +} + +fz_obj * +fz_dictget(fz_obj *obj, fz_obj *key) +{ + return fz_dictgets(obj, fz_toname(key)); +} + +fz_error * +fz_dictput(fz_obj *obj, fz_obj *key, fz_obj *val) +{ + fz_error *err; + int i; + char *s; + + if (!fz_isdict(obj)) + return fz_throw("typecheck in dictput"); + if (!fz_isname(key)) + return fz_throw("typecheck in dictput"); + + s = fz_toname(key); + + for (i = 0; i < obj->u.d.len; i++) { + if (strcmp(fz_toname(obj->u.d.items[i].k), s) == 0) { + fz_dropobj(obj->u.d.items[i].v); + obj->u.d.items[i].v = fz_keepobj(val); + return nil; + } + } + + if (obj->u.d.len + 1 > obj->u.d.cap) { + err = growdict(obj); + if (err) return err; + } + + obj->u.d.items[obj->u.d.len].k = fz_keepobj(key); + obj->u.d.items[obj->u.d.len].v = fz_keepobj(val); + obj->u.d.len ++; + + return nil; +} + +fz_error * +fz_dictputs(fz_obj *obj, char *key, fz_obj *val) +{ + fz_error *err; + fz_obj *keyobj; + err = fz_newname(&keyobj, key); + if (err) return err; + err = fz_dictput(obj, keyobj, val); + fz_dropobj(keyobj); + return err; +} + +fz_error * +fz_dictdels(fz_obj *obj, char *key) +{ + int i; + + if (!fz_isdict(obj)) + return fz_throw("typecheck in dictdel"); + + for (i = 0; i < obj->u.d.len; i++) { + if (strcmp(fz_toname(obj->u.d.items[i].k), key) == 0) { + fz_dropobj(obj->u.d.items[i].k); + fz_dropobj(obj->u.d.items[i].v); + obj->u.d.items[i] = obj->u.d.items[obj->u.d.len-1]; + obj->u.d.len --; + } + } + + return nil; +} + +fz_error * +fz_dictdel(fz_obj *obj, fz_obj *key) +{ + return fz_dictdels(obj, fz_toname(key)); +} + +void +fz_freedict(fz_obj *obj) +{ + int i; + + if (!fz_isdict(obj)) + return; + + for (i = 0; i < obj->u.d.len; i++) { + if (obj->u.d.items[i].k) + fz_dropobj(obj->u.d.items[i].k); + if (obj->u.d.items[i].v) + fz_dropobj(obj->u.d.items[i].v); + } + + fz_free(obj->u.d.items); + fz_free(obj); +} + diff --git a/object/parse.c b/object/parse.c new file mode 100644 index 00000000..c065a132 --- /dev/null +++ b/object/parse.c @@ -0,0 +1,400 @@ +#include <fitz.h> + +struct vap { va_list ap; }; + +static inline int iswhite(int ch) +{ + return + ch == '\000' || + ch == '\011' || + ch == '\012' || + ch == '\014' || + ch == '\015' || + ch == '\040'; +} + +static inline int isdelim(int ch) +{ + return + ch == '(' || ch == ')' || + ch == '<' || ch == '>' || + ch == '[' || ch == ']' || + ch == '{' || ch == '}' || + ch == '/' || + ch == '%'; +} + +static inline int isregular(int ch) +{ + return !isdelim(ch) && !iswhite(ch) && ch != EOF; +} + +static fz_error *parseobj(fz_obj **obj, char **sp, struct vap *v); + +static inline int fromhex(char ch) +{ + if (ch >= '0' && ch <= '9') + return ch - '0'; + else if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 0xA; + else if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 0xA; + return 0; +} + +static inline void skipwhite(char **sp) +{ + char *s = *sp; + while (iswhite(*s)) + s ++; + *sp = s; +} + +static void parsekeyword(char **sp, char *b, char *eb) +{ + char *s = *sp; + while (b < eb && isregular(*s)) + *b++ = *s++; + *b++ = 0; + *sp = s; +} + +static fz_error *parsename(fz_obj **obj, char **sp) +{ + char buf[64]; + char *s = *sp; + char *p = buf; + + s ++; /* skip '/' */ + while (p < buf + sizeof buf - 1 && isregular(*s)) + *p++ = *s++; + *p++ = 0; + *sp = s; + + return fz_newname(obj, buf); +} + +static fz_error *parsenumber(fz_obj **obj, char **sp) +{ + char buf[32]; + char *s = *sp; + char *p = buf; + + while (p < buf + sizeof buf - 1) + { + if (s[0] == '-' || s[0] == '.' || (s[0] >= '0' && s[0] <= '9')) + *p++ = *s++; + else + break; + } + *p++ = 0; + *sp = s; + + if (strchr(buf, '.')) + return fz_newreal(obj, atof(buf)); + return fz_newint(obj, atoi(buf)); +} + +static fz_error *parsedict(fz_obj **obj, char **sp, struct vap *v) +{ + fz_error *err = nil; + fz_obj *dict = nil; + fz_obj *key = nil; + fz_obj *val = nil; + char *s = *sp; + + err = fz_newdict(&dict, 8); + if (err) return err; + *obj = dict; + + s += 2; /* skip "<<" */ + + while (*s) + { + skipwhite(&s); + + /* end-of-dict marker >> */ + if (*s == '>') { + s ++; + if (*s == '>') { + s ++; + break; + } + err = fz_throw("syntaxerror in parsedict"); + goto error; + } + + /* non-name as key, bail */ + if (*s != '/') { + err = fz_throw("syntaxerror in parsedict"); + goto error; + } + + err = parsename(&key, &s); + if (err) goto error; + + skipwhite(&s); + + err = parseobj(&val, &s, v); + if (err) goto error; + + err = fz_dictput(dict, key, val); + if (err) goto error; + + fz_dropobj(val); val = nil; + fz_dropobj(key); key = nil; + } + + *sp = s; + return nil; + +error: + if (val) fz_dropobj(val); + if (key) fz_dropobj(key); + if (dict) fz_freedict(dict); + *obj = nil; + *sp = s; + return err; +} + +static fz_error *parsearray(fz_obj **obj, char **sp, struct vap *v) +{ + fz_error *err; + fz_obj *a; + fz_obj *o; + char *s = *sp; + + err = fz_newarray(&a, 8); + if (err) return err; + *obj = a; + + s ++; /* skip '[' */ + + while (*s) + { + skipwhite(&s); + + if (*s == ']') { + s ++; + break; + } + + err = parseobj(&o, &s, v); + if (err) { *obj = nil; fz_freearray(a); return err; } + + err = fz_arraypush(a, o); + if (err) { fz_dropobj(o); *obj = nil; fz_freearray(a); return err; } + + fz_dropobj(o); + } + + *sp = s; + return nil; +} + +static fz_error *parsestring(fz_obj **obj, char **sp) +{ + char buf[512]; + char *s = *sp; + char *p = buf; + int balance = 1; + int oct; + + s ++; /* skip '(' */ + + while (*s && p < buf + sizeof buf) + { + if (*s == '(') + { + balance ++; + *p++ = *s++; + } + else if (*s == ')') + { + balance --; + *p++ = *s++; + } + else if (*s == '\\') + { + s ++; + if (*s >= '0' && *s <= '9') + { + oct = *s - '0'; + s ++; + if (*s >= '0' && *s <= '9') + { + oct = oct * 8 + (*s - '0'); + s ++; + if (*s >= '0' && *s <= '9') + { + oct = oct * 8 + (*s - '0'); + s ++; + } + } + *p++ = oct; + } + else switch (*s) + { + case 'n': *p++ = '\n'; s++; break; + case 'r': *p++ = '\r'; s++; break; + case 't': *p++ = '\t'; s++; break; + case 'b': *p++ = '\b'; s++; break; + case 'f': *p++ = '\f'; s++; break; + default: *p++ = *s++; break; + } + } + else + *p++ = *s++; + + if (balance == 0) + break; + } + + *sp = s; + return fz_newstring(obj, buf, p - buf - 1); +} + +static fz_error *parsehexstring(fz_obj **obj, char **sp) +{ + char buf[512]; + char *s = *sp; + char *p = buf; + int a, b; + + s ++; /* skip '<' */ + + while (*s && p < buf + sizeof buf) + { + skipwhite(&s); + if (*s == '>') { + s ++; + break; + } + a = *s++; + + if (*s == '\0') + break; + + skipwhite(&s); + if (*s == '>') { + s ++; + break; + } + b = *s++; + + *p++ = fromhex(a) * 16 + fromhex(b); + } + + *sp = s; + return fz_newstring(obj, buf, p - buf); +} + +static fz_error *parseobj(fz_obj **obj, char **sp, struct vap *v) +{ + fz_error *err; + char buf[32]; + int oid, gid, len; + char *tmp; + char *s = *sp; + + if (*s == '\0') + return fz_throw("syntaxerror in parseobj: end-of-string"); + + skipwhite(&s); + + err = nil; + + if (v != nil && *s == '%') + { + s ++; + switch (*s) + { + case 'o': *obj = fz_keepobj(va_arg(v->ap, fz_obj*)); break; + case 'b': err = fz_newbool(obj, va_arg(v->ap, int)); break; + case 'i': err = fz_newint(obj, va_arg(v->ap, int)); break; + case 'f': err = fz_newreal(obj, (float)va_arg(v->ap, double)); break; + case 'n': err = fz_newname(obj, va_arg(v->ap, char*)); break; + case 'r': + oid = va_arg(v->ap, int); + gid = va_arg(v->ap, int); + err = fz_newindirect(obj, oid, gid); + break; + case 's': + tmp = va_arg(v->ap, char*); + err = fz_newstring(obj, tmp, strlen(tmp)); + break; + case '#': + tmp = va_arg(v->ap, char*); + len = va_arg(v->ap, int); + err = fz_newstring(obj, tmp, len); + break; + default: + err = fz_throw("unknown format specifier in packobj: '%c'", *s); + break; + } + s ++; + } + + else if (*s == '/') + err = parsename(obj, &s); + + else if (*s == '(') + err = parsestring(obj, &s); + + else if (*s == '<') { + if (s[1] == '<') + err = parsedict(obj, &s, v); + else + err = parsehexstring(obj, &s); + } + + else if (*s == '[') + err = parsearray(obj, &s, v); + + else if (*s == '-' || *s == '.' || (*s >= '0' && *s <= '9')) + err = parsenumber(obj, &s); + + else if (isregular(*s)) + { + parsekeyword(&s, buf, buf + sizeof buf); + + if (strcmp("true", buf) == 0) + err = fz_newbool(obj, 1); + else if (strcmp("false", buf) == 0) + err = fz_newbool(obj, 0); + else if (strcmp("null", buf) == 0) + err = fz_newnull(obj); + else + err = fz_throw("syntaxerror in parseobj: undefined keyword %s", buf); + } + + else + err = fz_throw("syntaxerror in parseobj"); + + *sp = s; + return err; +} + +fz_error * +fz_packobj(fz_obj **op, char *fmt, ...) +{ + fz_error *err; + struct vap v; + va_list ap; + + va_start(ap, fmt); + va_copy(v.ap, ap); + + err = parseobj(op, &fmt, &v); + + va_end(ap); + + return err; +} + +fz_error * +fz_parseobj(fz_obj **op, char *str) +{ + return parseobj(op, &str, nil); +} + diff --git a/object/print.c b/object/print.c new file mode 100644 index 00000000..af6941f9 --- /dev/null +++ b/object/print.c @@ -0,0 +1,332 @@ +#include <fitz.h> + +struct fmt +{ + FILE *file; + char *buf; + int len; + + int indent; + int tight; + int col; + int sep; + int last; +}; + +static void fmtobj(struct fmt *fmt, fz_obj *obj); + +static inline int isdelim(int ch) +{ + return ch == '(' || ch == ')' || + ch == '<' || ch == '>' || + ch == '[' || ch == ']' || + ch == '{' || ch == '}' || + ch == '/' || + ch == '%'; +} + +static inline void fmtputc(struct fmt *fmt, int c) +{ + if (fmt->sep && !isdelim(fmt->last) && !isdelim(c)) { + fmt->sep = 0; + fmtputc(fmt, ' '); + } + fmt->sep = 0; + + if (fmt->file) + putc(c, fmt->file); + if (fmt->buf) + fmt->buf[fmt->len] = c; + + if (c == '\n') + fmt->col = 0; + else + fmt->col ++; + + fmt->len ++; + + fmt->last = c; +} + +static void fmtindent(struct fmt *fmt) +{ + int i = fmt->indent; + while (i--) { + fmtputc(fmt, ' '); + fmtputc(fmt, ' '); + } +} + +static inline void fmtputs(struct fmt *fmt, char *s) +{ + while (*s) + fmtputc(fmt, *s++); +} + +static inline void fmtsep(struct fmt *fmt) +{ + fmt->sep = 1; +} + +static void fmtstr(struct fmt *fmt, fz_obj *obj) +{ + int i; + int c; + + fmtputc(fmt, '('); + for (i = 0; i < obj->u.s.len; i++) + { + c = obj->u.s.buf[i]; + if (c == '\n') + fmtputs(fmt, "\\n"); + else if (c == '\r') + fmtputs(fmt, "\\r"); + else if (c == '\t') + fmtputs(fmt, "\\t"); + else if (c == '\b') + fmtputs(fmt, "\\b"); + else if (c == '\f') + fmtputs(fmt, "\\f"); + else if (c == '(') + fmtputs(fmt, "\\("); + else if (c == ')') + fmtputs(fmt, "\\)"); + else if (c < 32 || c > 126) { + char buf[16]; + fmtputc(fmt, '\\'); + sprintf(buf, "%o", c); + fmtputs(fmt, buf); + //fmtputc(fmt, ((c >> 6) & 7) + '0'); + //fmtputc(fmt, ((c >> 3) & 7) + '0'); + //fmtputc(fmt, ((c) & 7) + '0'); + } + else + fmtputc(fmt, c); + } + fmtputc(fmt, ')'); +} + +static void fmthex(struct fmt *fmt, fz_obj *obj) +{ + int i; + int c; + + fmtputc(fmt, '<'); + for (i = 0; i < obj->u.s.len; i++) { + c = (obj->u.s.buf[i] >> 4) & 0x0f; + fmtputc(fmt, c < 0xA ? c + '0' : c + 'A' - 0xA); + c = (obj->u.s.buf[i]) & 0x0f; + fmtputc(fmt, c < 0xA ? c + '0' : c + 'A' - 0xA); + } + fmtputc(fmt, '>'); +} + +static void fmtarray(struct fmt *fmt, fz_obj *obj) +{ + int i; + + if (fmt->tight) { + fmtputc(fmt, '['); + for (i = 0; i < fz_arraylen(obj); i++) { + fmtobj(fmt, fz_arrayget(obj, i)); + fmtsep(fmt); + } + fmtputc(fmt, ']'); + } + else { + fmtputs(fmt, "[ "); + for (i = 0; i < fz_arraylen(obj); i++) { + if (fmt->col > 60) { + fmtputc(fmt, '\n'); + fmtindent(fmt); + } + fmtobj(fmt, fz_arrayget(obj, i)); + fmtputc(fmt, ' '); + } + fmtputc(fmt, ']'); + fmtsep(fmt); + } +} + +static void fmtdict(struct fmt *fmt, fz_obj *obj) +{ + int i; + fz_obj *key, *val; + + if (fmt->tight) { + fmtputs(fmt, "<<"); + for (i = 0; i < fz_dictlen(obj); i++) { + fmtobj(fmt, fz_dictgetkey(obj, i)); + fmtsep(fmt); + fmtobj(fmt, fz_dictgetval(obj, i)); + fmtsep(fmt); + } + fmtputs(fmt, ">>"); + } + else { + fmtputs(fmt, "<<\n"); + fmt->indent ++; + for (i = 0; i < fz_dictlen(obj); i++) { + key = fz_dictgetkey(obj, i); + val = fz_dictgetval(obj, i); + fmtindent(fmt); + fmtobj(fmt, key); + fmtputc(fmt, ' '); + if (fz_isarray(val)) + fmt->indent ++; + fmtobj(fmt, val); + fmtputc(fmt, '\n'); + if (fz_isarray(val)) + fmt->indent --; + } + fmt->indent --; + fmtindent(fmt); + fmtputs(fmt, ">>"); + } +} + +static void fmtobj(struct fmt *fmt, fz_obj *obj) +{ + char buf[256]; + + if (!obj) { + fmtputs(fmt, "<nil>"); + return; + } + + switch (obj->kind) + { + case FZ_NULL: + fmtputs(fmt, "null"); + break; + case FZ_BOOL: + fmtputs(fmt, fz_tobool(obj) ? "true" : "false"); + break; + case FZ_INT: + sprintf(buf, "%d", fz_toint(obj)); + fmtputs(fmt, buf); + break; + case FZ_REAL: + sprintf(buf, "%g", fz_toreal(obj)); + if (strchr(buf, 'e')) /* bad news! */ + sprintf(buf, fabs(fz_toreal(obj)) > 1 ? "%1.1f" : "%1.8f", fz_toreal(obj)); + fmtputs(fmt, buf); + break; + case FZ_STRING: + { + int added = 0; + int i, c; + for (i = 0; i < obj->u.s.len; i++) { + c = (unsigned char)obj->u.s.buf[i]; + if (strchr("()\\\n\r\t\b\f", c) != 0) + added ++; + else if (c < 8) + added ++; + else if (c < 32) + added += 2; + else if (c >= 127) + added += 3; + } + if (added < obj->u.s.len) + fmtstr(fmt, obj); + else + fmthex(fmt, obj); + } + break; + case FZ_NAME: + sprintf(buf, "/%s", fz_toname(obj)); + fmtputs(fmt, buf); + break; + case FZ_ARRAY: + fmtarray(fmt, obj); + break; + case FZ_DICT: + fmtdict(fmt, obj); + break; + case FZ_INDIRECT: + sprintf(buf, "%d %d R", obj->u.r.oid, obj->u.r.gid); + fmtputs(fmt, buf); + break; + case FZ_POINTER: + sprintf(buf, "$%p", obj->u.p); + fmtputs(fmt, buf); + break; + default: + sprintf(buf, "<unknown object type %d>", obj->kind); + fmtputs(fmt, buf); + break; + } +} + +int +fz_sprintobj(char *s, fz_obj *obj) +{ + struct fmt fmt; + + fmt.indent = 0; + fmt.tight = 0; + fmt.col = 0; + fmt.sep = 0; + fmt.last = 0; + + fmt.file = nil; + fmt.buf = s; + fmt.len = 0; + fmtobj(&fmt, obj); + return fmt.len; +} + +int +fz_sprintcobj(char *s, fz_obj *obj) +{ + struct fmt fmt; + + fmt.indent = 0; + fmt.tight = 1; + fmt.col = 0; + fmt.sep = 0; + fmt.last = 0; + + fmt.file = nil; + fmt.buf = s; + fmt.len = 0; + fmtobj(&fmt, obj); + return fmt.len; +} + +int +fz_fprintobj(FILE *file, fz_obj *obj) +{ + struct fmt fmt; + + fmt.indent = 0; + fmt.tight = 0; + fmt.col = 0; + fmt.sep = 0; + fmt.last = 0; + + fmt.file = file; + fmt.buf = nil; + fmt.len = 0; + fmtobj(&fmt, obj); + return fmt.len; +} + +int +fz_fprintcobj(FILE *file, fz_obj *obj) +{ + struct fmt fmt; + + fmt.indent = 0; + fmt.tight = 1; + fmt.col = 0; + fmt.sep = 0; + fmt.last = 0; + + fmt.file = file; + fmt.buf = nil; + fmt.len = 0; + fmtobj(&fmt, obj); + return fmt.len; +} + diff --git a/object/simple.c b/object/simple.c new file mode 100644 index 00000000..02da3cec --- /dev/null +++ b/object/simple.c @@ -0,0 +1,230 @@ +#include <fitz.h> + +#define NEWOBJ(KIND,SIZE) \ + fz_obj *o; \ + o = *op = fz_malloc(SIZE); \ + if (!o) return fz_outofmem; \ + o->kind = KIND; \ + o->refcount = 1 + +fz_error * +fz_newnull(fz_obj **op) +{ + NEWOBJ(FZ_NULL, sizeof (fz_obj)); + return nil; +} + +fz_error * +fz_newbool(fz_obj **op, int b) +{ + NEWOBJ(FZ_BOOL, sizeof (fz_obj)); + o->u.b = b; + return nil; +} + +fz_error * +fz_newint(fz_obj **op, int i) +{ + NEWOBJ(FZ_INT, sizeof (fz_obj)); + o->u.i = i; + return nil; +} + +fz_error * +fz_newreal(fz_obj **op, float f) +{ + NEWOBJ(FZ_REAL, sizeof (fz_obj)); + o->u.f = f; + return nil; +} + +fz_error * +fz_newstring(fz_obj **op, char *str, int len) +{ + NEWOBJ(FZ_STRING, offsetof(fz_obj, u.s.buf) + len); + o->u.s.len = len; + memcpy(o->u.s.buf, str, len); + return nil; +} + +fz_error * +fz_newname(fz_obj **op, char *str) +{ + NEWOBJ(FZ_NAME, offsetof(fz_obj, u.n) + strlen(str) + 1); + strcpy(o->u.n, str); + return nil; +} + +fz_error * +fz_newindirect(fz_obj **op, int objid, int genid) +{ + NEWOBJ(FZ_INDIRECT, sizeof (fz_obj)); + o->u.r.oid = objid; + o->u.r.gid = genid; + return nil; +} + +fz_error * +fz_newpointer(fz_obj **op, void *p) +{ + NEWOBJ(FZ_POINTER, sizeof (fz_obj)); + o->u.p = p; + return nil; +} + +fz_obj * +fz_keepobj(fz_obj *o) +{ + o->refcount ++; + return o; +} + +fz_obj * +fz_dropobj(fz_obj *o) +{ + o->refcount --; + if (o->refcount == 0) { + if (o->kind == FZ_ARRAY) + fz_freearray(o); + else if (o->kind == FZ_DICT) + fz_freedict(o); + else + fz_free(o); + } + return nil; +} + +int +fz_isnull(fz_obj *obj) +{ + return obj ? obj->kind == FZ_NULL : 0; +} + +int +fz_isbool(fz_obj *obj) +{ + return obj ? obj->kind == FZ_BOOL : 0; +} + +int +fz_isint(fz_obj *obj) +{ + return obj ? obj->kind == FZ_INT : 0; +} + +int +fz_isreal(fz_obj *obj) +{ + return obj ? obj->kind == FZ_REAL : 0; +} + +int +fz_isstring(fz_obj *obj) +{ + return obj ? obj->kind == FZ_STRING : 0; +} + +int +fz_isname(fz_obj *obj) +{ + return obj ? obj->kind == FZ_NAME : 0; +} + +int +fz_isarray(fz_obj *obj) +{ + return obj ? obj->kind == FZ_ARRAY : 0; +} + +int +fz_isdict(fz_obj *obj) +{ + return obj ? obj->kind == FZ_DICT : 0; +} + +int +fz_isindirect(fz_obj *obj) +{ + return obj ? obj->kind == FZ_INDIRECT : 0; +} + +int +fz_ispointer(fz_obj *obj) +{ + return obj ? obj->kind == FZ_POINTER : 0; +} + +int +fz_tobool(fz_obj *obj) +{ + if (fz_isbool(obj)) + return obj->u.b; + return 0; +} + +int +fz_toint(fz_obj *obj) +{ + if (fz_isint(obj)) + return obj->u.i; + return 0; +} + +float +fz_toreal(fz_obj *obj) +{ + if (fz_isreal(obj)) + return obj->u.f; + if (fz_isint(obj)) + return obj->u.i; + return 0; +} + +char * +fz_toname(fz_obj *obj) +{ + if (fz_isname(obj)) + return obj->u.n; + return ""; +} + +char * +fz_tostringbuf(fz_obj *obj) +{ + if (fz_isstring(obj)) + return obj->u.s.buf; + return ""; +} + +int +fz_tostringlen(fz_obj *obj) +{ + if (fz_isstring(obj)) + return obj->u.s.len; + return 0; +} + +int +fz_toobjid(fz_obj *obj) +{ + if (fz_isindirect(obj)) + return obj->u.r.oid; + return 0; +} + +int +fz_togenid(fz_obj *obj) +{ + if (fz_isindirect(obj)) + return obj->u.r.gid; + return 0; +} + +void * +fz_topointer(fz_obj *obj) +{ + if (fz_ispointer(obj)) + return obj->u.p; + return nil; +} + diff --git a/render/edgelist.c b/render/edgelist.c new file mode 100644 index 00000000..d62ffad4 --- /dev/null +++ b/render/edgelist.c @@ -0,0 +1,288 @@ +#include <fitz.h> + +/* + * Global Edge List -- list of straight path segments for scan conversion + * + * Stepping along the edges is with bresenham's line algorithm. + * + * See Mike Abrash -- Graphics Programming Black Book (notably chapter 40) + */ + +fz_error * +fz_newgel(fz_gel **gelp) +{ + fz_gel *gel; + + gel = *gelp = fz_malloc(sizeof(fz_gel)); + if (!gel) + return fz_outofmem; + + gel->edges = nil; + + gel->cap = 512; + gel->len = 0; + gel->edges = fz_malloc(sizeof(fz_edge) * gel->cap); + if (!gel->edges) { + fz_free(gel); + return fz_outofmem; + } + + gel->xmin = gel->ymin = INT_MAX; + gel->xmax = gel->ymax = INT_MIN; + gel->hs = 1; + gel->vs = 1; + + return nil; +} + +void +fz_resetgel(fz_gel *gel, int hs, int vs) +{ + gel->xmin = gel->ymin = INT_MAX; + gel->xmax = gel->ymax = INT_MIN; + gel->hs = hs; + gel->vs = vs; + gel->len = 0; +} + +void +fz_freegel(fz_gel *gel) +{ + fz_free(gel->edges); + fz_free(gel); +} + +fz_error * +fz_insertgel(fz_gel *gel, float fx0, float fy0, float fx1, float fy1) +{ + fz_edge *edge; + int dx, dy; + int winding; + int width; + int tmp; + + fx0 *= gel->hs; + fy0 *= gel->vs; + fx1 *= gel->hs; + fy1 *= gel->vs; + + /* TODO: should we round or truncate? */ + int x0 = fx0 < 0 ? fx0 - 0.5 : fx0 + 0.5; + int y0 = fy0 < 0 ? fy0 - 0.5 : fy0 + 0.5; + int x1 = fx1 < 0 ? fx1 - 0.5 : fx1 + 0.5; + int y1 = fy1 < 0 ? fy1 - 0.5 : fy1 + 0.5; + + if (y0 == y1) + return nil; + + if (y0 > y1) { + winding = -1; + tmp = x0; x0 = x1; x1 = tmp; + tmp = y0; y0 = y1; y1 = tmp; + } + else + winding = 1; + + if (x0 < gel->xmin) gel->xmin = x0; + if (x0 > gel->xmax) gel->xmax = x0; + if (x1 < gel->xmin) gel->xmin = x1; + if (x1 > gel->xmax) gel->xmax = x1; + + if (y0 < gel->ymin) gel->ymin = y0; + if (y1 > gel->ymax) gel->ymax = y1; + + if (gel->len + 1 == gel->cap) { + int newcap = gel->cap + 512; + fz_edge *newedges = fz_realloc(gel->edges, sizeof(fz_edge) * newcap); + if (!newedges) + return fz_outofmem; + gel->cap = newcap; + gel->edges = newedges; + } + + edge = &gel->edges[gel->len++]; + + dy = y1 - y0; + dx = x1 - x0; + width = dx < 0 ? -dx : dx; + + edge->xdir = dx > 0 ? 1 : -1; + edge->ydir = winding; + edge->x = x0; + edge->y = y0; + edge->h = dy; + edge->adjdown = dy; + + /* initial error term going l->r and r->l */ + if (dx >= 0) + edge->e = 0; + else + edge->e = -dy + 1; + + /* y-major edge */ + if (dy >= width) { + edge->xmove = 0; + edge->adjup = width; + } + + /* x-major edge */ + else { + edge->xmove = (width / dy) * edge->xdir; + edge->adjup = width % dy; + } + + return nil; +} + +void +fz_sortgel(fz_gel *gel) +{ + fz_edge *a = gel->edges; + int n = gel->len; + + int h, i, k; + fz_edge t; + + h = 1; + if (n < 14) { + h = 1; + } + else { + while (h < n) + h = 3 * h + 1; + h /= 3; + h /= 3; + } + + while (h > 0) + { + for (i = 0; i < n; i++) { + t = a[i]; + k = i - h; + /* TODO: sort on y major, x minor */ + while (k >= 0 && a[k].y > t.y) { + a[k + h] = a[k]; + k -= h; + } + a[k + h] = t; + } + + h /= 3; + } +} + +/* + * Active Edge List -- keep track of active edges while sweeping + */ + +fz_error * +fz_newael(fz_ael **aelp) +{ + fz_ael *ael; + + ael = *aelp = fz_malloc(sizeof(fz_ael)); + if (!ael) + return fz_outofmem; + + ael->cap = 64; + ael->len = 0; + ael->edges = fz_malloc(sizeof(fz_edge*) * ael->cap); + if (!ael->edges) { + fz_free(ael); + return fz_outofmem; + } + + return nil; +} + +void +fz_freeael(fz_ael *ael) +{ + fz_free(ael->edges); + fz_free(ael); +} + +static inline void +sortael(fz_edge **a, int n) +{ + int h, i, k; + fz_edge *t; + + h = 1; + if (n < 14) { + h = 1; + } + else { + while (h < n) + h = 3 * h + 1; + h /= 3; + h /= 3; + } + + while (h > 0) + { + for (i = 0; i < n; i++) { + t = a[i]; + k = i - h; + while (k >= 0 && a[k]->x > t->x) { + a[k + h] = a[k]; + k -= h; + } + a[k + h] = t; + } + + h /= 3; + } +} + +fz_error * +fz_insertael(fz_ael *ael, fz_gel *gel, int y, int *e) +{ + /* insert edges that start here */ + while (*e < gel->len && gel->edges[*e].y == y) { + if (ael->len + 1 == ael->cap) { + int newcap = ael->cap + 64; + fz_edge **newedges = fz_realloc(ael->edges, sizeof(fz_edge*) * newcap); + if (!newedges) + return fz_outofmem; + ael->edges = newedges; + ael->cap = newcap; + } + ael->edges[ael->len++] = &gel->edges[(*e)++]; + } + + /* shell-sort the edges by increasing x */ + sortael(ael->edges, ael->len); + + return nil; +} + +void +fz_advanceael(fz_ael *ael) +{ + fz_edge *edge; + int i = 0; + + while (i < ael->len) + { + edge = ael->edges[i]; + + edge->h --; + + /* terminator! */ + if (edge->h == 0) { + ael->edges[i] = ael->edges[--ael->len]; + } + + else { + edge->x += edge->xmove; + edge->e += edge->adjup; + if (edge->e > 0) { + edge->x += edge->xdir; + edge->e -= edge->adjdown; + } + i ++; + } + } +} + diff --git a/render/fill.c b/render/fill.c new file mode 100644 index 00000000..4605726d --- /dev/null +++ b/render/fill.c @@ -0,0 +1,123 @@ +#include <fitz.h> + +static fz_error * +line(fz_gel *gel, fz_matrix *ctm, float x0, float y0, float x1, float y1) +{ + float tx0 = ctm->a * x0 + ctm->c * y0 + ctm->e; + float ty0 = ctm->b * x0 + ctm->d * y0 + ctm->f; + float tx1 = ctm->a * x1 + ctm->c * y1 + ctm->e; + float ty1 = ctm->b * x1 + ctm->d * y1 + ctm->f; + return fz_insertgel(gel, tx0, ty0, tx1, ty1); +} + +static fz_error * +bezier(fz_gel *gel, fz_matrix *ctm, float flatness, + float xa, float ya, + float xb, float yb, + float xc, float yc, + float xd, float yd) +{ + fz_error *error; + float dmax; + float xab, yab; + float xbc, ybc; + float xcd, ycd; + float xabc, yabc; + float xbcd, ybcd; + float xabcd, yabcd; + + /* termination check */ + dmax = ABS(xa - xb); + dmax = MAX(dmax, ABS(ya - yb)); + dmax = MAX(dmax, ABS(xd - xc)); + dmax = MAX(dmax, ABS(yd - yc)); + if (dmax < flatness) + return line(gel, ctm, xa, ya, xd, yd); + + xab = xa + xb; + yab = ya + yb; + xbc = xb + xc; + ybc = yb + yc; + xcd = xc + xd; + ycd = yc + yd; + + xabc = xab + xbc; + yabc = yab + ybc; + xbcd = xbc + xcd; + ybcd = ybc + ycd; + + xabcd = xabc + xbcd; + yabcd = yabc + ybcd; + + xab *= 0.5f; yab *= 0.5f; + xbc *= 0.5f; ybc *= 0.5f; + xcd *= 0.5f; ycd *= 0.5f; + + xabc *= 0.25f; yabc *= 0.25f; + xbcd *= 0.25f; ybcd *= 0.25f; + + xabcd *= 0.125f; yabcd *= 0.125f; + + error = bezier(gel, ctm, flatness, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd); + if (error) + return error; + return bezier(gel, ctm, flatness, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd); +} + +fz_error * +fz_fillpath(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness) +{ + fz_error *error; + float x1, y1, x2, y2, x3, y3; + float cx = 0; + float cy = 0; + float bx = 0; + float by = 0; + int i = 0; + + while (i < path->len) + { + switch (path->els[i++].k) + { + case FZ_MOVETO: + x1 = path->els[i++].v; + y1 = path->els[i++].v; + cx = bx = x1; + cy = by = y1; + break; + + case FZ_LINETO: + x1 = path->els[i++].v; + y1 = path->els[i++].v; + error = line(gel, &ctm, cx, cy, x1, y1); + if (error) + return error; + cx = x1; + cy = y1; + break; + + case FZ_CURVETO: + x1 = path->els[i++].v; + y1 = path->els[i++].v; + x2 = path->els[i++].v; + y2 = path->els[i++].v; + x3 = path->els[i++].v; + y3 = path->els[i++].v; + error = bezier(gel, &ctm, flatness, cx, cy, x1, y1, x2, y2, x3, y3); + if (error) + return error; + cx = x3; + cy = y3; + break; + + case FZ_CLOSEPATH: + error = line(gel, &ctm, cx, cy, bx, by); + if (error) + return error; + break; + } + } + + return nil; +} + diff --git a/render/glyphcache.c b/render/glyphcache.c new file mode 100644 index 00000000..81b1f5d5 --- /dev/null +++ b/render/glyphcache.c @@ -0,0 +1,370 @@ +#include <fitz.h> + +#define HSUBPIX 5.0 +#define VSUBPIX 5.0 + +typedef struct fz_hash_s fz_hash; +typedef struct fz_key_s fz_key; +typedef struct fz_val_s fz_val; + +struct fz_glyphcache_s +{ + int slots; + int size; + fz_hash *hash; + fz_val *lru; + unsigned char *buffer; + int load; + int used; +}; + +struct fz_key_s +{ + void *fid; + int a, b; + int c, d; + unsigned short gid; + unsigned char e, f; +}; + +struct fz_hash_s +{ + fz_key key; + fz_val *val; +}; + +struct fz_val_s +{ + fz_hash *ent; + unsigned char *data; + short w, h, lsb, top; + int uses; +}; + +static unsigned int hashkey(fz_key *key) +{ + unsigned char *s = (char*)key; + unsigned int hash = 0; + unsigned int i; + for (i = 0; i < sizeof(fz_key); i++) + { + hash += s[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; +} + +fz_error * +fz_newglyphcache(fz_glyphcache **arenap, int slots, int size) +{ + fz_glyphcache *arena; + + arena = *arenap = fz_malloc(sizeof(fz_glyphcache)); + if (!arena) + return fz_outofmem; + + arena->slots = slots; + arena->size = size; + + arena->hash = nil; + arena->lru = nil; + arena->buffer = nil; + + arena->hash = fz_malloc(sizeof(fz_hash) * slots); + if (!arena->hash) + goto cleanup; + + arena->lru = fz_malloc(sizeof(fz_val) * slots); + if (!arena->lru) + goto cleanup; + + arena->buffer = fz_malloc(size); + if (!arena->buffer) + goto cleanup; + + memset(arena->hash, 0, sizeof(fz_hash) * slots); + memset(arena->lru, 0, sizeof(fz_val) * slots); + memset(arena->buffer, 0, size); + arena->load = 0; + arena->used = 0; + + return nil; + +cleanup: + fz_free(arena->hash); + fz_free(arena->lru); + fz_free(arena->buffer); + fz_free(arena); + return fz_outofmem; +} + +void +fz_freeglyphcache(fz_glyphcache *arena) +{ + fz_free(arena->hash); + fz_free(arena->lru); + fz_free(arena->buffer); + fz_free(arena); +} + +static int hokay = 0; +static int hcoll = 0; +static int hdist = 0; +static int coos = 0; +static int covf = 0; + +static fz_val * +hashfind(fz_glyphcache *arena, fz_key *key) +{ + fz_hash *tab = arena->hash; + int pos = hashkey(key) % arena->slots; + + while (1) + { + if (!tab[pos].val) + return nil; + + if (memcmp(key, &tab[pos].key, sizeof (fz_key)) == 0) + return tab[pos].val; + + pos = pos + 1; + if (pos == arena->slots) + pos = 0; + } +} + +static void +hashinsert(fz_glyphcache *arena, fz_key *key, fz_val *val) +{ + fz_hash *tab = arena->hash; + int pos = hashkey(key) % arena->slots; +int didcoll = 0; + + while (1) + { + if (!tab[pos].val) + { + tab[pos].key = *key; + tab[pos].val = val; + tab[pos].val->ent = &tab[pos]; +if (didcoll) hcoll ++; else hokay ++; hdist += didcoll; + return; + } + + pos = pos + 1; + if (pos == arena->slots) + pos = 0; +didcoll ++; + } +} + +static void +hashremove(fz_glyphcache *arena, fz_key *key) +{ + fz_hash *tab = arena->hash; + unsigned int pos = hashkey(key) % arena->slots; + unsigned int hole; + unsigned int look; + unsigned int code; + + while (1) + { + if (!tab[pos].val) + return; /* boo hoo! tried to remove non-existant key */ + + if (memcmp(key, &tab[pos].key, sizeof (fz_key)) == 0) + { + tab[pos].val = nil; + + hole = pos; + look = hole + 1; + if (look == arena->slots) + look = 0; + + while (tab[look].val) + { + code = (hashkey(&tab[look].key) % arena->slots); + if ((code <= hole && hole < look) || + (look < code && code <= hole) || + (hole < look && look < code)) + { + tab[hole] = tab[look]; + tab[hole].val->ent = &tab[hole]; + tab[look].val = nil; + hole = look; + } + + look = look + 1; + if (look == arena->slots) + look = 0; + } + + return; + } + + pos = pos + 1; + if (pos == arena->slots) + pos = 0; + } +} + +void +fz_debugglyphcache(fz_glyphcache *arena) +{ + int i; + + printf("cache load %d / %d (%d / %d bytes)\n", + arena->load, arena->slots, arena->used, arena->size); + printf("no-colliders: %d colliders: %d\n", hokay, hcoll); + printf("avg dist: %d / %d: %g\n", hdist, hcoll, (double)hdist / hcoll); + printf("out-of-space evicts: %d\n", coos); + printf("out-of-hash evicts: %d\n", covf); + + for (i = 0; i < arena->slots; i++) + { + if (!arena->hash[i].val) + printf("glyph % 4d: empty\n", i); + else { + fz_key *k = &arena->hash[i].key; + fz_val *b = arena->hash[i].val; + printf("glyph % 4d: %p %d [%g %g %g %g + %d %d] " + "-> [%dx%d %d,%d]\n", i, + k->fid, k->gid, + k->a / 65536.0, + k->b / 65536.0, + k->c / 65536.0, + k->d / 65536.0, + k->e, k->f, + b->w, b->h, b->lsb, b->top); + } + } + + for (i = 0; i < arena->load; i++) + printf("lru %04d: glyph %d (%d)\n", i, + arena->lru[i].ent - arena->hash, arena->lru[i].uses); +} + +static void +bubble(fz_glyphcache *arena, int i) +{ + fz_val tmp; + + if (i == 0 || arena->load < 2) + return; + + tmp = arena->lru[i - 1]; + arena->lru[i - 1] = arena->lru[i]; + arena->lru[i] = tmp; + + arena->lru[i - 1].ent->val = &arena->lru[i - 1]; + arena->lru[i].ent->val = &arena->lru[i]; +} + +static void +evictlast(fz_glyphcache *arena) +{ + fz_val *lru = arena->lru; + unsigned char *s, *e; + int i, k; + fz_key key; + + if (arena->load == 0) + return; + + k = arena->load - 1; + s = lru[k].data; + e = s + lru[k].w * lru[k].h; + + /* pack buffer to fill hole */ + memmove(s, e, arena->buffer + arena->used - e); + memset(arena->buffer + arena->used - (e - s), 0, e - s); + arena->used -= e - s; + + /* update lru pointers */ + for (i = 0; i < k; i++) + if (lru[i].data >= e) + lru[i].data -= e - s; + + /* remove hash entry */ + key = lru[k].ent->key; + hashremove(arena, &key); + + arena->load --; +} + +fz_error * +fz_renderglyph(fz_glyphcache *arena, fz_glyph *glyph, fz_font *font, int gid, fz_matrix ctm) +{ + fz_error *error; + fz_key key; + fz_val *val; + int size; + + key.fid = font; + key.gid = gid; + key.a = ctm.a * 65536; + key.b = ctm.b * 65536; + key.c = ctm.c * 65536; + key.d = ctm.d * 65536; + key.e = (ctm.e - floor(ctm.e)) * HSUBPIX; + key.f = (ctm.f - floor(ctm.f)) * VSUBPIX; + + val = hashfind(arena, &key); + if (val) + { + val->uses ++; + glyph->w = val->w; + glyph->h = val->h; + glyph->lsb = val->lsb; + glyph->top = val->top; + glyph->bitmap = val->data; + + bubble(arena, val - arena->lru); + + return nil; + } + + ctm.e = floor(ctm.e) + key.e / HSUBPIX; + ctm.f = floor(ctm.f) + key.f / HSUBPIX; + + error = font->render(glyph, font, gid, ctm); + if (error) + return error; + + size = glyph->w * glyph->h; + + if (size > arena->size / 6) + return nil; + + while (arena->load > arena->slots * 75 / 100) { + covf ++; + evictlast(arena); + } + + while (arena->used + size >= arena->size) { + coos ++; + evictlast(arena); + } + + val = &arena->lru[arena->load++]; + val->uses = 0; + val->w = glyph->w; + val->h = glyph->h; + val->lsb = glyph->lsb; + val->top = glyph->top; + val->data = arena->buffer + arena->used; + + arena->used += size; + + memcpy(val->data, glyph->bitmap, glyph->w * glyph->h); + glyph->bitmap = val->data; + + hashinsert(arena, &key, val); + + return nil; +} + diff --git a/render/pixmap.c b/render/pixmap.c new file mode 100644 index 00000000..57402d81 --- /dev/null +++ b/render/pixmap.c @@ -0,0 +1,60 @@ +#include <fitz.h> + +fz_error * +fz_newpixmap(fz_pixmap **pixp, int x, int y, int w, int h, int n, int a) +{ + fz_pixmap *pix; + + pix = *pixp = fz_malloc(sizeof (fz_pixmap)); + if (!pix) + return fz_outofmem; + + pix->x = x; + pix->y = y; + pix->w = w; + pix->h = h; + pix->n = n; + pix->a = a; + pix->cs = nil; + pix->stride = (pix->n + pix->a) * pix->w; + + pix->samples = fz_malloc(sizeof(short) * pix->stride * pix->h); + if (!pix->samples) { + fz_free(pix); + return fz_outofmem; + } + + memset(pix->samples, 0, sizeof(short) * pix->stride * pix->h); + + return nil; +} + +void +fz_freepixmap(fz_pixmap *pix) +{ + fz_free(pix->samples); + fz_free(pix); +} + +void +fz_clearpixmap(fz_pixmap *pix) +{ + memset(pix->samples, 0, sizeof(short) * pix->stride * pix->h); +} + +void +fz_debugpixmap(fz_pixmap *pix) +{ + int x, y; + FILE *f = fopen("out.ppm", "w"); + fprintf(f, "P6\n%d %d\n255\n", pix->w, pix->h); + for (y = 0; y < pix->h; y++) + for (x = 0; x < pix->w; x++) + { + putc(255 - pix->samples[x + y * pix->stride], f); + putc(255 - pix->samples[x + y * pix->stride], f); + putc(255 - pix->samples[x + y * pix->stride], f); + } + fclose(f); +} + diff --git a/render/porterduff.c b/render/porterduff.c new file mode 100644 index 00000000..1cedbc38 --- /dev/null +++ b/render/porterduff.c @@ -0,0 +1,74 @@ +#include <fitz.h> + +/* Porter-Duff compositing arithmetic on premultiplied ARGB buffers */ + +void +fz_blendover(unsigned char *C, unsigned char *A, unsigned char *B, int n) +{ + while (n--) + { + unsigned char Fa = 255; + unsigned char Fb = 255 - A[0]; + *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb); + *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb); + *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb); + *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb); + } +} + +void +fz_blendin(unsigned char *C, unsigned char *A, unsigned char *B, int n) +{ + while (n--) + { + unsigned char Fa = B[0]; + unsigned char Fb = 0; + *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb); + *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb); + *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb); + *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb); + } +} + +void +fz_blendout(unsigned char *C, unsigned char *A, unsigned char *B, int n) +{ + while (n--) + { + unsigned char Fa = 255 - B[0]; + unsigned char Fb = 0; + *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb); + *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb); + *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb); + *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb); + } +} + +void +fz_blendatop(unsigned char *C, unsigned char *A, unsigned char *B, int n) +{ + while (n--) + { + unsigned char Fa = B[0]; + unsigned char Fb = 255 - A[0]; + *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb); + *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb); + *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb); + *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb); + } +} + +void +fz_blendxor(unsigned char *C, unsigned char *A, unsigned char *B, int n) +{ + while (n--) + { + unsigned char Fa = 255 - B[0]; + unsigned char Fb = 255 - A[0]; + *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb); + *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb); + *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb); + *C++ = fz_mul255(*A++, Fa) + fz_mul255(*B++, Fb); + } +} + diff --git a/render/render.c b/render/render.c new file mode 100644 index 00000000..64856dd2 --- /dev/null +++ b/render/render.c @@ -0,0 +1,183 @@ +#include <fitz.h> + +struct fz_renderer_s +{ + fz_glyphcache *cache; + fz_gel *gel; + fz_ael *ael; +}; + +fz_error * +fz_newrenderer(fz_renderer **gcp) +{ + fz_error *error; + fz_renderer *gc; + + gc = *gcp = fz_malloc(sizeof(fz_renderer)); + if (!gc) + return fz_outofmem; + + gc->cache = nil; + gc->gel = nil; + gc->ael = nil; + + error = fz_newglyphcache(&gc->cache, 1024, 65536); + if (error) + goto cleanup; + + error = fz_newgel(&gc->gel); + if (error) + goto cleanup; + + error = fz_newael(&gc->ael); + if (error) + goto cleanup; + + return nil; + +cleanup: + if (gc->cache) + fz_freeglyphcache(gc->cache); + if (gc->gel) + fz_freegel(gc->gel); + if (gc->ael) + fz_freeael(gc->ael); + fz_free(gc); + + return error; +} + +void +fz_freerenderer(fz_renderer *gc) +{ + if (gc->cache) + fz_freeglyphcache(gc->cache); + if (gc->gel) + fz_freegel(gc->gel); + if (gc->ael) + fz_freeael(gc->ael); + fz_free(gc); +} + +fz_error * +fz_renderover(fz_renderer *gc, fz_over *over, fz_matrix ctm, fz_pixmap *out) +{ + fz_error *error; + fz_node *node; + + for (node = over->child; node; node = node->next) + { + error = fz_rendernode(gc, node, ctm, out); + if (error) + return error; + } + + return nil; +} + +fz_error * +fz_rendermask(fz_renderer *gc, fz_mask *mask, fz_matrix ctm, fz_pixmap *out) +{ + fz_error *error; + fz_node *node; + + for (node = mask->child; node; node = node->next) + { + error = fz_rendernode(gc, node, ctm, out); + if (error) + return error; + } + + return nil; +} + +fz_error * +fz_rendertransform(fz_renderer *gc, fz_transform *xform, fz_matrix ctm, fz_pixmap *out) +{ + ctm = fz_concat(ctm, xform->m); + return fz_rendernode(gc, xform->child, ctm, out); +} + + +void composite(fz_pixmap *out, fz_glyph *gl, int xo, int yo) +{ + int sx, sy, dx, dy, a, b, c; + + for (sy = 0; sy < gl->h; sy++) + { + for (sx = 0; sx < gl->w; sx++) + { + dx = xo + sx + gl->lsb; + dy = yo - sy + gl->top; + + if (dx < 0) continue; + if (dy < 0) continue; + if (dx >= out->w) continue; + if (dy >= out->h) continue; + + a = gl->bitmap[sx + sy * gl->w]; + b = out->samples[dx + dy * out->stride]; + c = MAX(a, b); + out->samples[dx + dy * out->stride] = c; + } + } +} + +fz_error * +fz_rendertext(fz_renderer *gc, fz_text *text, fz_matrix ctm, fz_pixmap *out) +{ + fz_error *error; + fz_glyph gl; + float x, y; + int g, i, ix, iy; + fz_matrix tm, trm; + + tm = text->trm; + + for (i = 0; i < text->len; i++) + { + g = text->els[i].g; + x = text->els[i].x; + y = text->els[i].y; + + tm.e = x; + tm.f = y; + trm = fz_concat(tm, ctm); + + ix = floor(trm.e); + iy = floor(trm.f); + + trm.e = (trm.e - floor(trm.e)); + trm.f = (trm.f - floor(trm.f)); + + error = fz_renderglyph(gc->cache, &gl, text->font, g, trm); + if (error) + return error; + + composite(out, &gl, ix, iy); + } + + return nil; +} + +fz_error * +fz_rendernode(fz_renderer *gc, fz_node *node, fz_matrix ctm, fz_pixmap *out) +{ + if (!node) + return nil; + + switch (node->kind) + { + case FZ_NOVER: + return fz_renderover(gc, (fz_over*)node, ctm, out); + case FZ_NMASK: + return fz_rendermask(gc, (fz_mask*)node, ctm, out); + case FZ_NTRANSFORM: + return fz_rendertransform(gc, (fz_transform*)node, ctm, out); + case FZ_NTEXT: + return fz_rendertext(gc, (fz_text*)node, ctm, out); + default: + return nil; + } +} + diff --git a/render/scanconv.c b/render/scanconv.c new file mode 100644 index 00000000..b2d32363 --- /dev/null +++ b/render/scanconv.c @@ -0,0 +1,176 @@ +#include <fitz.h> + +static inline void +addspan(short *list, int x0, int x1, int xofs, int hs) +{ + int x0pix, x0sub; + int x1pix, x1sub; + + if (x0 == x1) + return; + + /* x between 0 and width of bbox */ + x0 -= xofs; + x1 -= xofs; + + x0pix = x0 / hs; + x0sub = x0 % hs; + x1pix = x1 / hs; + x1sub = x1 % hs; + + if (x0pix == x1pix) + { + list[x0pix] += x1sub - x0sub; + list[x0pix+1] += x0sub - x1sub; + } + + else + { + list[x0pix] += hs - x0sub; + list[x0pix+1] += x0sub; + list[x1pix] += x1sub - hs; + list[x1pix+1] += -x1sub; + } +} + +static inline void +nonzerowinding(fz_ael *ael, short *list, int xofs, int hs) +{ + int winding = 0; + int x = 0; + int i; + for (i = 0; i < ael->len; i++) + { + if (!winding && (winding + ael->edges[i]->ydir)) + x = ael->edges[i]->x; + if (winding && !(winding + ael->edges[i]->ydir)) + addspan(list, x, ael->edges[i]->x, xofs, hs); + winding += ael->edges[i]->ydir; + } +} + +static inline void +evenodd(fz_ael *ael, short *list, int xofs, int hs) +{ + int even = 0; + int x = 0; + int i; + for (i = 0; i < ael->len; i++) + { + if (!even) + x = ael->edges[i]->x; + else + addspan(list, x, ael->edges[i]->x, xofs, hs); + even = !even; + } +} + +/* XXX */ + +unsigned char pixmap[1000 * 1000]; + +void +fz_emitdeltas(short *list, int y, int x, int n) +{ + short a = 0; + short d = 0; + while (n--) { + d = *list++; + a += d; + pixmap[y * 1000 + x] = 0xff - a; + x ++; + } +} + +void +savestuff(void) +{ + FILE *f = fopen("out.pgm", "w"); + fprintf(f, "P5\n1000 1000\n255\n"); + fwrite(pixmap, 1, 1000 * 1000, f); + fclose(f); +} + +/* XXX */ + +void +fz_emitdeltas0(short *list, int y, int xofs, int n) +{ + int d = 0; + while (n--) + d += *list++; +} + +fz_error * +fz_scanconvert(fz_gel *gel, fz_ael *ael, int eofill) +{ + fz_error *error; + short *deltas; + int y, e; + int yd, yc; + + int xmin = fz_idiv(gel->xmin, gel->hs); + int xmax = fz_idiv(gel->xmax, gel->hs) + 1; + int ymin = fz_idiv(gel->ymin, gel->vs); + int ymax = fz_idiv(gel->ymax, gel->vs) + 1; + + int xofs = xmin * gel->hs; + int hs = gel->hs; + int vs = gel->vs; + + if (gel->len == 0) + return nil; + +memset(pixmap, 0xff, sizeof pixmap); + +printf("bbox [%d %d %d %d] samp %d/%d edges %d\n", + xmin, ymin, xmax, ymax, hs, vs, gel->len); + + deltas = fz_malloc(sizeof(short) * (xmax - xmin)); + if (!deltas) + return fz_outofmem; + memset(deltas, 0, sizeof(short) * (xmax - xmin)); + + e = 0; + y = gel->edges[0].y; + yc = fz_idiv(y, vs); + yd = yc; + + while (ael->len > 0 || e < gel->len) + { + yc = fz_idiv(y, vs); + if (yc != yd) { + fz_emitdeltas(deltas, yd, xmin, xmax - xmin); + memset(deltas, 0, sizeof(short) * (xmax - xmin)); + } + yd = yc; + + error = fz_insertael(ael, gel, y, &e); + if (error) { + fz_free(deltas); + return error; + } + +// { int i; for (i = 0; i < ael->len; i++) +// pixmap[yd * 1000 + (ael->edges[i]->x / hs)] = 0; } + + if (eofill) + evenodd(ael, deltas, xofs, hs); + else + nonzerowinding(ael, deltas, xofs, hs); + + fz_advanceael(ael); + + if (ael->len > 0) + y ++; + else if (e < gel->len) + y = gel->edges[e].y; + } + + fz_emitdeltas(deltas, yd, xmin, xmax - xmin); + + savestuff(); + + return nil; +} + diff --git a/render/stroke.c b/render/stroke.c new file mode 100644 index 00000000..1203f71b --- /dev/null +++ b/render/stroke.c @@ -0,0 +1,716 @@ +#include <fitz.h> + +enum { BUTT = 0, ROUND = 1, SQUARE = 2, MITER = 0, BEVEL = 2 }; + +struct sctx +{ + fz_gel *gel; + fz_matrix *ctm; + float flatness; + + fz_stroke *stroke; + fz_point beg[2]; + fz_point seg[2]; + int sn, bn; + int dot; + + fz_dash *dash; + int toggle; + int offset; + float phase; + fz_point cur; +}; + +static fz_error * +line(struct sctx *s, float x0, float y0, float x1, float y1) +{ + float tx0 = s->ctm->a * x0 + s->ctm->c * y0 + s->ctm->e; + float ty0 = s->ctm->b * x0 + s->ctm->d * y0 + s->ctm->f; + float tx1 = s->ctm->a * x1 + s->ctm->c * y1 + s->ctm->e; + float ty1 = s->ctm->b * x1 + s->ctm->d * y1 + s->ctm->f; + return fz_insertgel(s->gel, tx0, ty0, tx1, ty1); +} + +static fz_error * +arc(struct sctx *s, + float xc, float yc, + float x0, float y0, + float x1, float y1) +{ + fz_error *error; + float th0, th1, r; + float theta; + float ox, oy, nx, ny; + int n, i; + + r = fabs(s->stroke->linewidth); + theta = 2 * M_SQRT2 * sqrt(s->flatness / r); + th0 = atan2(y0, x0); + th1 = atan2(y1, x1); + + if (r > 0) + { + if (th0 < th1) + th0 += M_PI * 2; + n = ceil((th0 - th1) / theta); + } + else + { + if (th1 < th0) + th1 += M_PI * 2; + n = ceil((th1 - th0) / theta); + } + + ox = x0; + oy = y0; + for (i = 1; i < n; i++) + { + theta = th0 + (th1 - th0) * i / n; + nx = cos(theta) * r; + ny = sin(theta) * r; + error = line(s, xc + ox, yc + oy, xc + nx, yc + ny); + if (error) return error; + ox = nx; + oy = ny; + } + + error = line(s, xc + ox, yc + oy, xc + x1, yc + y1); + if (error) return error; + + return nil; +} + +static fz_error * +linestroke(struct sctx *s, fz_point a, fz_point b) +{ + fz_error *error; + + float dx = b.x - a.x; + float dy = b.y - a.y; + float scale = s->stroke->linewidth / sqrt(dx * dx + dy * dy); + float dlx = dy * scale; + float dly = -dx * scale; + + error = line(s, a.x - dlx, a.y - dly, b.x - dlx, b.y - dly); + if (error) return error; + + error = line(s, b.x + dlx, b.y + dly, a.x + dlx, a.y + dly); + if (error) return error; + + return nil; +} + +static fz_error * +linejoin(struct sctx *s, fz_point a, fz_point b, fz_point c) +{ + fz_error *error; + float miterlimit = s->stroke->miterlimit; + float linewidth = s->stroke->linewidth; + int linejoin = s->stroke->linejoin; + float dx0, dy0; + float dx1, dy1; + float dlx0, dly0; + float dlx1, dly1; + float dmx, dmy; + float dmr2; + float scale; + float cross; + + dx0 = b.x - a.x; + dy0 = b.y - a.y; + + dx1 = c.x - b.x; + dy1 = c.y - b.y; + + if (dx0 * dx0 + dy0 * dy0 < FLT_EPSILON) + return nil; + if (dx1 * dx1 + dy1 * dy1 < FLT_EPSILON) + return nil; + + scale = linewidth / sqrt(dx0 * dx0 + dy0 * dy0); + dlx0 = dy0 * scale; + dly0 = -dx0 * scale; + + scale = linewidth / sqrt(dx1 * dx1 + dy1 * dy1); + dlx1 = dy1 * scale; + dly1 = -dx1 * scale; + + cross = dx1 * dy0 - dx0 * dy1; + + dmx = (dlx0 + dlx1) * 0.5; + dmy = (dly0 + dly1) * 0.5; + dmr2 = dmx * dmx + dmy * dmy; + + if (cross * cross < FLT_EPSILON && dx0 * dx1 + dy0 * dy1 >= 0) + linejoin = BEVEL; + + if (linejoin == MITER) + if (dmr2 * miterlimit * miterlimit < linewidth * linewidth) + linejoin = BEVEL; + + if (linejoin == BEVEL) + { + error = line(s, b.x - dlx0, b.y - dly0, b.x - dlx1, b.y - dly1); + if (error) return error; + error = line(s, b.x + dlx1, b.y + dly1, b.x + dlx0, b.y + dly0); + if (error) return error; + } + + if (linejoin == MITER) + { + scale = linewidth * linewidth / dmr2; + dmx *= scale; + dmy *= scale; + + if (cross < 0) + { + error = line(s, b.x - dlx0, b.y - dly0, b.x - dlx1, b.y - dly1); + if (error) return error; + error = line(s, b.x + dlx1, b.y + dly1, b.x + dmx, b.y + dmy); + if (error) return error; + error = line(s, b.x + dmx, b.y + dmy, b.x + dlx0, b.y + dly0); + if (error) return error; + } + else + { + error = line(s, b.x + dlx1, b.y + dly1, b.x + dlx0, b.y + dly0); + if (error) return error; + error = line(s, b.x - dlx0, b.y - dly0, b.x - dmx, b.y - dmy); + if (error) return error; + error = line(s, b.x - dmx, b.y - dmy, b.x - dlx1, b.y - dly1); + if (error) return error; + } + } + + if (linejoin == ROUND) + { + if (cross < 0) + { + error = line(s, b.x - dlx0, b.y - dly0, b.x - dlx1, b.y - dly1); + if (error) return error; + error = arc(s, b.x, b.y, dlx1, dly1, dlx0, dly0); + if (error) return error; + } + else + { + error = line(s, b.x + dlx1, b.y + dly1, b.x + dlx0, b.y + dly0); + if (error) return error; + error = arc(s, b.x, b.y, -dlx0, -dly0, -dlx1, -dly1); + if (error) return error; + } + } + + return nil; +} + +static fz_error * +linecap(struct sctx *s, fz_point a, fz_point b) +{ + fz_error *error; + float flatness = s->flatness; + float linewidth = s->stroke->linewidth; + int linecap = s->stroke->linecap; + + float dx = b.x - a.x; + float dy = b.y - a.y; + + float scale = linewidth / sqrt(dx * dx + dy * dy); + float dlx = dy * scale; + float dly = -dx * scale; + + if (linecap == BUTT) + return line(s, b.x - dlx, b.y - dly, b.x + dlx, b.y + dly); + + if (linecap == ROUND) + { + int i; + int n = ceil(M_PI / (2.0 * M_SQRT2 * sqrt(flatness / linewidth))); + float ox = b.x - dlx; + float oy = b.y - dly; + for (i = 1; i < n; i++) + { + float theta = M_PI * i / n; + float cth = cos(theta); + float sth = sin(theta); + float nx = b.x - dlx * cth - dly * sth; + float ny = b.y - dly * cth + dlx * sth; + error = line(s, ox, oy, nx, ny); + if (error) return error; + ox = nx; + oy = ny; + } + error = line(s, ox, oy, b.x + dlx, b.y + dly); + if (error) return error; + } + + if (linecap == SQUARE) + { + error = line(s, b.x - dlx, b.y - dly, + b.x - dlx - dly, + b.y - dly + dlx); + if (error) return error; + error = line(s, b.x - dlx - dly, + b.y - dly + dlx, + b.x + dlx - dly, + b.y + dly + dlx); + if (error) return error; + error = line(s, b.x + dlx - dly, + b.y + dly + dlx, + b.x + dlx, b.y + dly); + if (error) return error; + } + + return nil; +} + +static fz_error * +linedot(struct sctx *s, fz_point a) +{ + fz_error *error; + float flatness = s->flatness; + float linewidth = s->stroke->linewidth; + int n = ceil(M_PI / (M_SQRT2 * sqrt(flatness / linewidth))); + float ox = a.x - linewidth; + float oy = a.y; + int i; + for (i = 1; i < n; i++) + { + float theta = M_PI * 2 * i / n; + float cth = cos(theta); + float sth = sin(theta); + float nx = a.x - cth * linewidth; + float ny = a.y + sth * linewidth; + error = line(s, ox, oy, nx, ny); + if (error) return error; + ox = nx; + oy = ny; + } + error = line(s, ox, oy, a.x - linewidth, a.y); + if (error) return error; + return nil; +} + +static fz_error * +strokeflush(struct sctx *s) +{ + fz_error *error; + + if (s->sn == 2) + { + error = linecap(s, s->beg[1], s->beg[0]); + if (error) return error; + error = linecap(s, s->seg[0], s->seg[1]); + if (error) return error; + } + else if (s->dot) + { + error = linedot(s, s->beg[0]); + if (error) return error; + } + + s->dot = 0; + + return nil; +} + +static fz_error * +strokemoveto(struct sctx *s, fz_point cur) +{ + fz_error *error; + + error = strokeflush(s); + if (error) return error; + + s->seg[0] = cur; + s->beg[0] = cur; + s->sn = 1; + s->bn = 1; + + return nil; +} + +static fz_error * +strokelineto(struct sctx *s, fz_point cur) +{ + fz_error *error; + + float dx = cur.x - s->seg[s->sn-1].x; + float dy = cur.y - s->seg[s->sn-1].y; + + if (dx * dx + dy * dy < s->flatness * s->flatness * 0.25) + { + s->dot = 1; + return nil; + } + + error = linestroke(s, s->seg[s->sn-1], cur); + if (error) return error; + + if (s->sn == 2) + { + error = linejoin(s, s->seg[0], s->seg[1], cur); + if (error) return error; + + s->seg[0] = s->seg[1]; + s->seg[1] = cur; + } + + if (s->sn == 1) + s->seg[s->sn++] = cur; + if (s->bn == 1) + s->beg[s->bn++] = cur; + + return nil; +} + +static fz_error * +strokeclosepath(struct sctx *s) +{ + fz_error *error; + + if (s->sn == 2) + { + error = strokelineto(s, s->beg[0]); + if (error) return error; + + if (s->seg[1].x == s->beg[0].x && s->seg[1].y == s->beg[0].y) + error = linejoin(s, s->seg[0], s->beg[0], s->beg[1]); + else + error = linejoin(s, s->seg[1], s->beg[0], s->beg[1]); + if (error) return error; + } + + else if (s->dot) + { + error = linedot(s, s->beg[0]); + if (error) return error; + } + + s->bn = 0; + s->sn = 0; + s->dot = 0; + + return nil; +} + +static fz_error * +strokebezier(struct sctx *s, + float xa, float ya, + float xb, float yb, + float xc, float yc, + float xd, float yd) +{ + fz_error *error; + float dmax; + float xab, yab; + float xbc, ybc; + float xcd, ycd; + float xabc, yabc; + float xbcd, ybcd; + float xabcd, yabcd; + + /* termination check */ + dmax = ABS(xa - xb); + dmax = MAX(dmax, ABS(ya - yb)); + dmax = MAX(dmax, ABS(xd - xc)); + dmax = MAX(dmax, ABS(yd - yc)); + if (dmax < s->flatness) { + fz_point p; + p.x = xd; + p.y = yd; + return strokelineto(s, p); + } + + xab = xa + xb; + yab = ya + yb; + xbc = xb + xc; + ybc = yb + yc; + xcd = xc + xd; + ycd = yc + yd; + + xabc = xab + xbc; + yabc = yab + ybc; + xbcd = xbc + xcd; + ybcd = ybc + ycd; + + xabcd = xabc + xbcd; + yabcd = yabc + ybcd; + + xab *= 0.5f; yab *= 0.5f; + xbc *= 0.5f; ybc *= 0.5f; + xcd *= 0.5f; ycd *= 0.5f; + + xabc *= 0.25f; yabc *= 0.25f; + xbcd *= 0.25f; ybcd *= 0.25f; + + xabcd *= 0.125f; yabcd *= 0.125f; + + error = strokebezier(s, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd); + if (error) + return error; + + return strokebezier(s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd); +} + +fz_error * +fz_strokepath(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness) +{ + fz_error *error; + struct sctx s; + fz_point p0, p1, p2, p3; + int i; + + s.gel = gel; + s.ctm = &ctm; + s.flatness = flatness; + + s.stroke = path->stroke; + s.sn = 0; + s.bn = 0; + s.dot = 0; + + i = 0; + + while (i < path->len) + { + switch (path->els[i++].k) + { + case FZ_MOVETO: + p1.x = path->els[i++].v; + p1.y = path->els[i++].v; + error = strokemoveto(&s, p1); + if (error) + return error; + p0 = p1; + break; + + case FZ_LINETO: + p1.x = path->els[i++].v; + p1.y = path->els[i++].v; + error = strokelineto(&s, p1); + if (error) + return error; + p0 = p1; + break; + + case FZ_CURVETO: + p1.x = path->els[i++].v; + p1.y = path->els[i++].v; + p2.x = path->els[i++].v; + p2.y = path->els[i++].v; + p3.x = path->els[i++].v; + p3.y = path->els[i++].v; + error = strokebezier(&s, p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y); + if (error) + return error; + p0 = p3; + break; + + case FZ_CLOSEPATH: + error = strokeclosepath(&s); + if (error) + return error; + break; + } + } + + return strokeflush(&s); +} + +static fz_error * +dashmoveto(struct sctx *s, fz_point a) +{ + s->toggle = 1; + s->offset = 0; + s->phase = s->dash->phase; + + while (s->phase >= s->dash->array[s->offset]) + { + s->toggle = !s->toggle; + s->phase -= s->dash->array[s->offset]; + s->offset ++; + if (s->offset == s->dash->len) + s->offset = 0; + } + + s->cur = a; + + if (s->toggle) + return strokemoveto(s, a); + + return nil; +} + +static fz_error * +dashlineto(struct sctx *s, fz_point b) +{ + fz_error *error; + float dx, dy; + float total, used, ratio; + fz_point a; + fz_point m; + + a = s->cur; + dx = b.x - a.x; + dy = b.y - a.y; + total = sqrt(dx * dx + dy * dy); + used = 0; + + while (total - used > s->dash->array[s->offset] - s->phase) + { + used += s->dash->array[s->offset] - s->phase; + ratio = used / total; + m.x = a.x + ratio * dx; + m.y = a.y + ratio * dy; + + if (s->toggle) + error = strokelineto(s, m); + else + error = strokemoveto(s, m); + if (error) + return error; + + s->toggle = !s->toggle; + s->phase = 0; + s->offset ++; + if (s->offset == s->dash->len) + s->offset = 0; + } + + s->phase += total - used; + + s->cur = b; + + if (s->toggle) + return strokelineto(s, b); + + return nil; +} + +static fz_error * +dashbezier(struct sctx *s, + float xa, float ya, + float xb, float yb, + float xc, float yc, + float xd, float yd) +{ + fz_error *error; + float dmax; + float xab, yab; + float xbc, ybc; + float xcd, ycd; + float xabc, yabc; + float xbcd, ybcd; + float xabcd, yabcd; + + /* termination check */ + dmax = ABS(xa - xb); + dmax = MAX(dmax, ABS(ya - yb)); + dmax = MAX(dmax, ABS(xd - xc)); + dmax = MAX(dmax, ABS(yd - yc)); + if (dmax < s->flatness) { + fz_point p; + p.x = xd; + p.y = yd; + return dashlineto(s, p); + } + + xab = xa + xb; + yab = ya + yb; + xbc = xb + xc; + ybc = yb + yc; + xcd = xc + xd; + ycd = yc + yd; + + xabc = xab + xbc; + yabc = yab + ybc; + xbcd = xbc + xcd; + ybcd = ybc + ycd; + + xabcd = xabc + xbcd; + yabcd = yabc + ybcd; + + xab *= 0.5f; yab *= 0.5f; + xbc *= 0.5f; ybc *= 0.5f; + xcd *= 0.5f; ycd *= 0.5f; + + xabc *= 0.25f; yabc *= 0.25f; + xbcd *= 0.25f; ybcd *= 0.25f; + + xabcd *= 0.125f; yabcd *= 0.125f; + + error = dashbezier(s, xa, ya, xab, yab, xabc, yabc, xabcd, yabcd); + if (error) return error; + return dashbezier(s, xabcd, yabcd, xbcd, ybcd, xcd, ycd, xd, yd); +} + +fz_error * +fz_dashpath(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness) +{ + fz_error *error; + struct sctx s; + fz_point p0, p1, p2, p3, beg; + int i; + + s.gel = gel; + s.ctm = &ctm; + s.flatness = flatness; + + s.stroke = path->stroke; + s.sn = 0; + s.bn = 0; + s.dot = 0; + + s.dash = path->dash; + s.toggle = 0; + s.offset = 0; + s.phase = 0; + + i = 0; + + while (i < path->len) + { + switch (path->els[i++].k) + { + case FZ_MOVETO: + p1.x = path->els[i++].v; + p1.y = path->els[i++].v; + error = dashmoveto(&s, p1); + if (error) + return error; + beg = p0 = p1; + break; + + case FZ_LINETO: + p1.x = path->els[i++].v; + p1.y = path->els[i++].v; + error = dashlineto(&s, p1); + if (error) + return error; + p0 = p1; + break; + + case FZ_CURVETO: + p1.x = path->els[i++].v; + p1.y = path->els[i++].v; + p2.x = path->els[i++].v; + p2.y = path->els[i++].v; + p3.x = path->els[i++].v; + p3.y = path->els[i++].v; + error = dashbezier(&s, p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y); + if (error) + return error; + p0 = p3; + break; + + case FZ_CLOSEPATH: + error = dashlineto(&s, beg); + if (error) + return error; + break; + } + } + + return strokeflush(&s); +} + diff --git a/tests/gsrfilt.rc b/tests/gsrfilt.rc new file mode 100644 index 00000000..72d4e4d3 --- /dev/null +++ b/tests/gsrfilt.rc @@ -0,0 +1,10 @@ +# test ghostscript filters +# +# usage: rc gsfilter.rc CCITTFaxDecode '<< ... >>' < in > out +# + +builtin gs -q -dBATCH '-sDEVICE=nullpage' -c \ + '/myin (%stdin) (r) file ' ^ $2 ^ ' /' ^ $1 ^ ' filter def + /s 1024 string def + { myin s readstring { print } { print exit } ifelse } loop' + diff --git a/tests/gswfilt.rc b/tests/gswfilt.rc new file mode 100644 index 00000000..7ce65af3 --- /dev/null +++ b/tests/gswfilt.rc @@ -0,0 +1,11 @@ +# test ghostscript filters +# +# usage: rc gsfilter.rc CCITTFaxDecode '<< ... >>' < in > out +# + +builtin gs -q -dBATCH '-sDEVICE=nullpage' -c \ + '/myout (%stdout) (w) file ' ^ $2 ^ ' /' ^ $1 ^ ' filter def + /myin (%stdin) (r) file def + /s 1024 string def + { myin s readstring { myout s writestring } { myout s writestring myout closefile exit } ifelse } loop' + diff --git a/tests/oscar.raw b/tests/oscar.raw Binary files differnew file mode 100644 index 00000000..fcbf201c --- /dev/null +++ b/tests/oscar.raw diff --git a/tests/other.h b/tests/other.h new file mode 100644 index 00000000..f934b519 --- /dev/null +++ b/tests/other.h @@ -0,0 +1,17 @@ +{'g', {0}}, +{'m', {15, 15}}, {'l', {15, 25}}, {'l', {25, 25}}, {'l', {25, 15}}, {'h', {}}, {'f', {}}, +{'m', {10, 10}}, {'l', {10, 30}}, {'l', {30, 30}}, {'l', {30, 10}}, {'h', {}}, {'S', {}}, +{'r', {1, 1, 0}}, +{'q', {}}, +{'T', {1,0,0,1, 15, 15}}, +{'m', {15, 15}}, {'l', {15, 25}}, {'l', {25, 25}}, {'l', {25, 15}}, {'h', {}}, {'f', {}}, +{'m', {10, 10}}, {'l', {10, 30}}, {'l', {30, 30}}, {'l', {30, 10}}, {'h', {}}, {'S', {}}, +{'T', {1,0,0,1, 15, 15}}, +{'r', {1, 0, 1}}, +{'m', {15, 15}}, {'l', {15, 25}}, {'l', {25, 25}}, {'l', {25, 15}}, {'h', {}}, {'f', {}}, +{'m', {10, 10}}, {'l', {10, 30}}, {'l', {30, 30}}, {'l', {30, 10}}, {'h', {}}, {'S', {}}, +{'Q', {}}, +{'r', {0, 1, 1}}, +{'a', {0.5}}, +{'m', {10, 20}}, {'l', {20, 30}}, {'l', {30, 20}}, {'l', {20, 10}}, {'h', {}}, {'f', {}}, +{'!'} diff --git a/tests/pred.rc b/tests/pred.rc new file mode 100644 index 00000000..ba019566 --- /dev/null +++ b/tests/pred.rc @@ -0,0 +1,23 @@ +# test fax codec + +FILE=oscar + +ARGS='<< /Predictor 15 /Columns 80 /Colors 3 /BitsPerComponent 8 >>' + +gs -q -dBATCH -sDEVICE'='nullpage -c ' +/runfilt { /fo exch def /fi exch def + { fi read not { exit } if fo exch write } loop + fo closefile +} def +(' ^ $FILE ^ '.raw) (r) file +(' ^ $FILE ^ '.pz-gs) (w) file ' ^ $ARGS ^ ' /FlateEncode filter runfilt' + +./t-filter.exe FlateDecode '<< >>' < $FILE.pz-gs > $FILE.p-gs +./t-filter.exe PredictDecode $ARGS < $FILE.p-gs > $FILE.d-gs + +./t-filter.exe PredictEncode $ARGS < $FILE.raw > $FILE.p-fz +./t-filter.exe FlateEncode '<< >>' < $FILE.p-fz > $FILE.pz-fz + +./t-filter.exe FlateDecode '<< >>' < $FILE.pz-fz > $FILE.p-fz2 +./t-filter.exe PredictDecode $ARGS < $FILE.p-fz2 > $FILE.d-fz2 + diff --git a/tests/ptt3.pbm b/tests/ptt3.pbm Binary files differnew file mode 100644 index 00000000..a91f7193 --- /dev/null +++ b/tests/ptt3.pbm diff --git a/tests/t-filter.c b/tests/t-filter.c new file mode 100644 index 00000000..c7bc2c7f --- /dev/null +++ b/tests/t-filter.c @@ -0,0 +1,111 @@ +#include <fitz.h> + +void +usage(void) +{ + printf("usage: filter filt0 param0 filt1 param1 filt2 param2 ...\n"); + exit(1); +} + +struct { + char *name; + fz_error *(*newf)(fz_filter **, fz_obj*); +} table[] = { + { "ASCIIHexDecode", fz_newahxd }, + { "ASCII85Decode", fz_newa85d }, + { "CCITTFaxDecode", fz_newfaxd }, + { "DCTDecode", fz_newdctd }, + { "RunLengthDecode", fz_newrld }, + { "LZWDecode", fz_newlzwd }, + { "FlateDecode", fz_newflated }, + + { "ASCIIHexEncode", fz_newahxe }, + { "ASCII85Encode", fz_newa85e }, + { "CCITTFaxEncode", fz_newfaxe }, + { "DCTEncode", fz_newdcte }, + { "RunLengthEncode", fz_newrle }, + { "LZWEncode", fz_newlzwe }, + { "FlateEncode", fz_newflatee }, + + { "PredictDecode", fz_newpredictd }, + { "PredictEncode", fz_newpredicte }, + + { "JBIG2Decode", fz_newjbig2d }, + { "JPXDecode", fz_newjpxd }, + + { nil, nil, } +}; + +fz_error * +makefilter(fz_filter **fp, char *f, fz_obj *p) +{ + int i; + + for (i = 0; table[i].name; i++) + if (strcmp(f, table[i].name) == 0) + return table[i].newf(fp, p); + + return fz_throw("unknown filter type '%s'", f); +} + +int +main(int argc, char **argv) +{ + unsigned char buf[512]; + int i; + + fz_error *err; + fz_filter *filter; + fz_filter *pipe; + fz_file *file; + fz_obj *param; + + if (argc == 1) + usage(); + + filter = nil; + pipe = nil; + + for (i = 1; i < argc - 1; i += 2) + { + err = fz_parseobj(¶m, argv[i+1]); + if (err) fz_abort(err); + + err = makefilter(&filter, argv[i], param); + if (err) fz_abort(err); + + if (pipe) { + err = fz_newpipeline(&pipe, pipe, filter); + if (err) fz_abort(err); + } + else + pipe = filter; + } + + err = fz_openfile(&file, "/dev/stdin", O_RDONLY); + if (err) fz_abort(err); + + if (pipe) + { + err = fz_pushfilter(file, pipe); + if (err) fz_abort(err); + } + + while (1) + { + i = fz_read(file, buf, sizeof buf); + if (i < 0) + fz_abort(fz_ferror(file)); + if (i == 0) + break; + write(1, buf, i); + } + + if (pipe) + fz_popfilter(file); + + fz_closefile(file); + + return 0; +} + diff --git a/tests/t-parse.c b/tests/t-parse.c new file mode 100644 index 00000000..fabd2060 --- /dev/null +++ b/tests/t-parse.c @@ -0,0 +1,44 @@ +#include <fitz.h> + +int +main(int argc, char **argv) +{ + fz_error *err; + fz_obj *obj; + int i; + + if (argc == 1) + { + err = fz_packobj(&obj, + "[ %s %r [ %i ] " + "<< /Float %f /BinString %# /Name %n /Int %i >> " + "(foo) /bar 3223 [ [ 1 2 3 %i ] %i [ 23 ] %i ]", + "Hello, world", + 3, 0, + 42, + 23.5, + "f\0obar", 4, + "Foo", + 666, + -1, -2 , -3 + ); + if (err) fz_abort(err); + + printf("pretty: "); fz_fprintobj(stdout, obj); printf("\n"); + printf("comapct: "); fz_fprintcobj(stdout, obj); printf("\n"); + fz_dropobj(obj); + printf("\n"); + } + + for (i = 1; i < argc; i++) { + err = fz_parseobj(&obj, argv[i]); + if (err) fz_abort(err); + printf("pretty: "); fz_fprintobj(stdout, obj); printf("\n"); + printf("compact: "); fz_fprintcobj(stdout, obj); printf("\n"); + fz_dropobj(obj); + printf("\n"); + } + + return 0; +} + diff --git a/tests/t-scanconv.c b/tests/t-scanconv.c new file mode 100644 index 00000000..84e09867 --- /dev/null +++ b/tests/t-scanconv.c @@ -0,0 +1,204 @@ +#include <fitz.h> + +fz_error *fz_fillpath(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness); +fz_error *fz_strokepath(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness); +fz_error *fz_dashpath(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness); + +int main(int argc, char **argv) +{ + fz_path *path; + fz_gel *gel; + fz_ael *ael; + fz_matrix ctm; + fz_dash *dash; + fz_stroke stroke = { 1, 1, 4.0, 15.0 }; + float dashes[] = { 20, 20, 5, 10 }; + int i; + + fz_newdash(&dash, 0.0, 2, dashes); + + fz_newpath(&path); + + /* AGaramond-Regular: 'X' */ + if (getenv("X")) { + fz_moveto(path, 631, 25); + fz_lineto(path, 666, 22); + fz_curveto(path, 673, 16, 672, 2, 665, -3); + fz_curveto(path, 619, -1, 580, 0, 539, 0); + fz_curveto(path, 494, 0, 446, -1, 410, -3); + fz_curveto(path, 404, 3, 403, 16, 408, 22); + fz_lineto(path, 437, 25); + fz_curveto(path, 460, 27, 471, 31, 471, 38); + fz_curveto(path, 471, 44, 468, 54, 449, 82); + fz_curveto(path, 405, 143, 350, 226, 306, 283); + fz_curveto(path, 279, 249, 205, 140, 165, 70); + fz_curveto(path, 157, 56, 152, 45, 152, 38); + fz_curveto(path, 152, 32, 161, 27, 181, 25); + fz_lineto(path, 208, 22); + fz_curveto(path, 215, 16, 213, 2, 207, -3); + fz_curveto(path, 171, -1, 133, 0, 99, 0); + fz_curveto(path, 65, 0, 28, -1, -4, -3); + fz_curveto(path, -13, 1, -14, 16, -7, 22); + fz_lineto(path, 18, 25); + fz_curveto(path, 72, 31, 107, 74, 137, 114); + fz_curveto(path, 149, 130, 218, 218, 273, 298); + fz_curveto(path, 280, 308, 282, 315, 282, 318); + fz_curveto(path, 282, 321, 278, 330, 270, 341); + fz_lineto(path, 120, 556); + fz_curveto(path, 87, 603, 68, 625, 33, 631); + fz_lineto(path, 1, 638); + fz_curveto(path, -4, 644, -3, 660, 3, 663); + fz_curveto(path, 50, 661, 83, 660, 119, 660); + fz_curveto(path, 158, 660, 203, 661, 232, 663); + fz_curveto(path, 239, 660, 240, 645, 234, 638); + fz_lineto(path, 206, 636); + fz_curveto(path, 188, 635, 176, 629, 176, 623); + fz_curveto(path, 176, 614, 187, 597, 206, 568); + fz_curveto(path, 240, 520, 298, 430, 334, 385); + fz_curveto(path, 360, 417, 450, 559, 468, 589); + fz_curveto(path, 476, 602, 482, 615, 482, 622); + fz_curveto(path, 482, 627, 467, 633, 449, 635); + fz_lineto(path, 423, 638); + fz_curveto(path, 417, 645, 417, 659, 425, 663); + fz_curveto(path, 460, 661, 492, 660, 531, 660); + fz_curveto(path, 568, 660, 596, 661, 627, 663); + fz_curveto(path, 634, 658, 635, 644, 629, 638); + fz_lineto(path, 605, 636); + fz_curveto(path, 575, 633, 539, 603, 498, 551); + fz_curveto(path, 459, 501, 416, 441, 368, 374); + fz_curveto(path, 362, 365, 358, 358, 358, 355); + fz_curveto(path, 358, 352, 359, 345, 371, 329); + fz_lineto(path, 540, 91); + fz_curveto(path, 574, 44, 600, 28, 631, 25); + fz_closepath(path); + } + + /* AGaramond-Regular: 'g' */ + if (getenv("g")) { + fz_moveto(path, 365, 372); + fz_lineto(path, 446, 372); + fz_curveto(path, 457, 367, 455, 333, 440, 331); + fz_lineto(path, 368, 331); + fz_curveto(path, 370, 314, 370, 297, 370, 280); + fz_curveto(path, 370, 211, 329, 121, 202, 121); + fz_curveto(path, 184, 121, 170, 123, 158, 124); + fz_curveto(path, 146, 117, 114, 98, 114, 69); + fz_curveto(path, 114, 46, 137, 27, 184, 27); + fz_curveto(path, 218, 27, 259, 30, 303, 30); + fz_curveto(path, 359, 30, 443, 20, 443, -80); + fz_curveto(path, 443, -189, 324, -269, 194, -269); + fz_curveto(path, 71, -269, 28, -203, 28, -153); + fz_curveto(path, 28, -137, 32, -124, 39, -116); + fz_curveto(path, 56, -98, 84, -72, 107, -49); + fz_curveto(path, 116, -40, 124, -31, 115, -25); + fz_curveto(path, 76, -15, 42, 19, 42, 54); + fz_curveto(path, 42, 59, 46, 63, 57, 71); + fz_curveto(path, 74, 82, 93, 99, 110, 117); + fz_curveto(path, 115, 123, 120, 131, 120, 136); + fz_curveto(path, 86, 154, 45, 193, 45, 257); + fz_curveto(path, 45, 343, 119, 408, 208, 408); + fz_curveto(path, 246, 408, 281, 398, 304, 388); + fz_curveto(path, 336, 374, 343, 372, 365, 372); + fz_closepath(path); + fz_moveto(path, 271, -32); + fz_lineto(path, 237, -32); + fz_curveto(path, 205, -32, 165, -34, 151, -43); + fz_curveto(path, 127, -58, 103, -89, 103, -127); + fz_curveto(path, 103, -181, 146, -226, 237, -226); + fz_curveto(path, 326, -226, 385, -176, 385, -119); + fz_curveto(path, 385, -58, 343, -32, 271, -32); + fz_closepath(path); + fz_moveto(path, 217, 150); + fz_curveto(path, 271, 150, 299, 193, 299, 254); + fz_curveto(path, 299, 322, 271, 379, 210, 379); + fz_curveto(path, 162, 379, 126, 335, 126, 267); + fz_curveto(path, 126, 196, 169, 150, 217, 150); + fz_closepath(path); + } + + /* AGaramond-Regular: 'i' */ + if (getenv("i")) + { + fz_moveto(path, 98, 112); + fz_lineto(path, 98, 287); + fz_curveto(path, 98, 326, 98, 331, 71, 349); + fz_lineto(path, 62, 355); + fz_curveto(path, 58, 359, 58, 370, 63, 373); + fz_curveto(path, 86, 381, 143, 407, 166, 422); + fz_curveto(path, 171, 422, 175, 420, 176, 416); + fz_curveto(path, 174, 381, 172, 333, 172, 292); + fz_lineto(path, 172, 112); + fz_curveto(path, 172, 40, 174, 30, 210, 25); + fz_lineto(path, 231, 22); + fz_curveto(path, 238, 17, 236, 0, 229, -3); + fz_curveto(path, 199, -1, 170, 0, 135, 0); + fz_curveto(path, 99, 0, 69, -1, 41, -3); + fz_curveto(path, 34, 0, 32, 17, 39, 22); + fz_lineto(path, 60, 25); + fz_curveto(path, 97, 30, 98, 40, 98, 112); + fz_closepath(path); + fz_moveto(path, 131, 662); + fz_curveto(path, 161, 662, 180, 639, 180, 611); + fz_curveto(path, 180, 576, 155, 560, 128, 560); + fz_curveto(path, 97, 560, 77, 582, 77, 609); + fz_curveto(path, 77, 642, 102, 662, 131, 662); + fz_closepath(path); + } + + if (getenv("rect")) + { + //fz_moveto(path, 103, 100); + //fz_lineto(path, 432, 400); + //fz_lineto(path, 400, 100); + //fz_closepath(path); + +// fz_moveto(path, 103, 100); +// fz_lineto(path, 432, 400); +// fz_lineto(path, 100, 400); +// fz_closepath(path); + + fz_moveto(path, 199, 100); + fz_lineto(path, 100, 200); + fz_lineto(path, 300, 200); + fz_lineto(path, 201, 100); + + fz_moveto(path, 150, 100); + fz_lineto(path, 150, 100); + fz_closepath(path); + } + + fz_moveto(path, 100, 100); + for (i = 0; i < 30; i++) + fz_lineto(path, 100 + i * 10, 100 + i * 5); + + fz_endpath(path, FZ_STROKE, &stroke, dash); + + ctm = fz_identity(); + ctm = fz_concat(ctm, fz_rotate(0)); + ctm = fz_concat(ctm, fz_translate(100, -700)); + ctm = fz_concat(ctm, fz_scale(1.0, -1.0)); + + fz_newgel(&gel); + fz_newael(&ael); + + fz_resetgel(gel, 17, 15); + // fz_fillpath(gel, path, ctm, 0.25); + // fz_strokepath(gel, path, ctm, 0.25); + fz_dashpath(gel, path, ctm, 0.25); + fz_sortgel(gel); + +/* + for (i = 0; i < gel->len; i++) + { + printf("edge %d,%d to ?,%d\n", + gel->edges[i].x, + gel->edges[i].y, + gel->edges[i].y + gel->edges[i].h); + } +*/ + + fz_scanconvert(gel, ael, 0); + + return 0; +} + diff --git a/tests/testfax.rc b/tests/testfax.rc new file mode 100755 index 00000000..516d7dfd --- /dev/null +++ b/tests/testfax.rc @@ -0,0 +1,40 @@ +#!/usr/local/bin/rc + +head = 'P4 +1728 2376' + +params = '<< /XRows 2376 /Columns 1728 /K 0 /EndOfLine true >>' + +echo Encoding with GS +rc gswfilt.rc CCITTFaxEncode $params < ptt3.raw > /tmp/e-gs + +echo Encoding with Fz +../t-filter CCITTFaxEncode $params < ptt3.raw > /tmp/e-fz >[2] /tmp/e-fz.trace + +od -t x1 /tmp/e-gs > /tmp/e-gs.hx +od -t x1 /tmp/e-fz > /tmp/e-fz.hx + +echo HEXDUMP +diff -u /tmp/e-gs.hx /tmp/e-fz.hx | head -20 +echo + +echo $head > /tmp/e-gs,d-gs.pbm +echo $head > /tmp/e-fz,d-gs.pbm +echo $head > /tmp/e-gs,d-fz.pbm +echo $head > /tmp/e-fz,d-fz.pbm + +echo Decoding GS output with GS +rc gsrfilt.rc CCITTFaxDecode $params < /tmp/e-gs >> /tmp/e-gs,d-gs.pbm + +echo Decoding Fz output with GS +rc gsrfilt.rc CCITTFaxDecode $params < /tmp/e-fz >> /tmp/e-fz,d-gs.pbm + +echo Decoding GS output with Fz +../t-filter CCITTFaxDecode $params </tmp/e-gs >>/tmp/e-gs,d-fz.pbm >[2]/tmp/d-gs.tr + +echo Decoding Fz output with Fz +../t-filter CCITTFaxDecode $params </tmp/e-fz >>/tmp/e-fz,d-fz.pbm >[2]/tmp/d-fz.tr + +echo TRACE +diff -uw /tmp/d-gs.tr /tmp/d-fz.tr | head -200 + diff --git a/tests/tiger.c b/tests/tiger.c new file mode 100644 index 00000000..11d84013 --- /dev/null +++ b/tests/tiger.c @@ -0,0 +1,213 @@ +#include <fitz.h> + +static struct { char c; double f[6]; } cmd[] = { +#include "tiger.h" +}; + +static fz_pathbuilder *pbuild; +static fz_textbuilder *tbuild; +static fz_tree *tree; + +static fz_stroke stroke = { 0, 0, 1.0, 10.0 }; +static float frgb[3] = { 0, 0, 0 }; +static float srgb[3] = { 0, 0, 0 }; +static fz_node *gstack[32]; +static int gtop = 0; + +void gsave(void) +{ + gstack[gtop++] = tree->head; +} + +void grestore(void) +{ + tree->head = gstack[--gtop]; +} + +void dostroke(void) +{ + fz_node *path; + fz_node *solid; + fz_node *mask; + + fz_makepath(&path, pbuild, FZ_PSTROKE, &stroke, nil); + fz_newsolid(&solid, 1.0f, srgb[0], srgb[1], srgb[2]); + fz_newblend(&mask, FZ_BMASK); + + fz_insertnode(mask, path); + fz_insertnode(mask, solid); + fz_insertnode(tree->head, mask); +} + +void dofill(void) +{ + fz_node *path; + fz_node *solid; + fz_node *mask; + + fz_makepath(&path, pbuild, FZ_PFILL, nil, nil); + fz_newsolid(&solid, 1.0f, frgb[0], frgb[1], frgb[2]); + fz_newblend(&mask, FZ_BMASK); + + fz_insertnode(mask, path); + fz_insertnode(mask, solid); + fz_insertnode(tree->head, mask); +} + +void doxform(fz_matrix ctm) +{ + fz_node *xform; + fz_node *over; + + fz_newtransform(&xform, ctm); + fz_newblend(&over, FZ_BOVER); + + fz_insertnode(xform, over); + fz_insertnode(tree->head, xform); + + tree->head = over; +} + +int main(int argc, char **argv) +{ + fz_node *node; + fz_matrix ctm; + fz_rect r; + int i; + + fz_newpathbuilder(&pbuild); + fz_newtextbuilder(&tbuild); + fz_newtree(&tree); + + fz_newblend(&node, FZ_BOVER); + tree->root = tree->head = node; + + fz_newsolid(&node, 1, .8, .8, .8); + fz_insertnode(tree->head, node); + + for (i = 0; cmd[i].c != '!'; i++) + { + switch (cmd[i].c) + { + case 'q': + gsave(); + break; + case 'Q': + grestore(); + break; + + /* 'cm' -> 'T' ... insert xform node */ + case 'T': + ctm.xx = cmd[i].f[0]; + ctm.xy = cmd[i].f[1]; + ctm.yx = cmd[i].f[2]; + ctm.yy = cmd[i].f[3]; + ctm.tx = cmd[i].f[4]; + ctm.ty = cmd[i].f[5]; + doxform(ctm); + break; + + /* current color */ + case 'g': + frgb[1] = cmd[i].f[0]; + frgb[2] = cmd[i].f[0]; + frgb[3] = cmd[i].f[0]; + break; + case 'G': + srgb[1] = cmd[i].f[0]; + srgb[2] = cmd[i].f[0]; + srgb[3] = cmd[i].f[0]; + break; + case 'r': + frgb[1] = cmd[i].f[0]; + frgb[2] = cmd[i].f[1]; + frgb[3] = cmd[i].f[2]; + break; + case 'R': + srgb[1] = cmd[i].f[0]; + srgb[2] = cmd[i].f[1]; + srgb[3] = cmd[i].f[2]; + break; + case 'a': + frgb[0] = cmd[i].f[0]; + break; + case 'A': + srgb[0] = cmd[i].f[0]; + break; + + /* line attrs */ + case 'w': stroke.linewidth = cmd[i].f[0]; break; + case 'J': stroke.linecap = cmd[i].f[0]; break; + case 'j': stroke.linejoin = cmd[i].f[0]; break; + case 'M': stroke.miterlimit = cmd[i].f[0]; break; + case 'i': break; + + /* path construction */ + case 'm': + fz_moveto(pbuild, cmd[i].f[0], cmd[i].f[1]); + break; + case 'l': + fz_lineto(pbuild, cmd[i].f[0], cmd[i].f[1]); + break; + case 'c': + fz_curveto(pbuild, + cmd[i].f[0], cmd[i].f[1], + cmd[i].f[2], cmd[i].f[3], + cmd[i].f[4], cmd[i].f[5]); + break; + case 'v': + fz_curvetov(pbuild, + cmd[i].f[0], cmd[i].f[1], + cmd[i].f[2], cmd[i].f[3]); + break; + case 'y': + fz_curvetoy(pbuild, + cmd[i].f[0], cmd[i].f[1], + cmd[i].f[2], cmd[i].f[3]); + break; + case 'h': + fz_closepath(pbuild); + break; + + /* insert path nodes */ + case 's': + fz_closepath(pbuild); + case 'S': + dostroke(); + break; + + case 'f': + dofill(); + break; + } + } + + r = fz_boundtree(tree, fz_scale(1, -1)); + printf("/* [%g %g %g %g] */\n", r.min.x, r.min.y, r.max.x, r.max.y); + fz_debugtree(tree); + + r.min.x -= 10; + r.min.y -= 10; + r.max.x += 10; + r.max.y += 10; + +#if 0 + { + fz_pixmap *img; + img = fz_newpixmap(r.min.x, r.min.y, + r.max.x - r.min.x, r.max.y - r.min.y); + fz_clearpixmap(img); + + fz_rendernode(tree, tree->root, fz_scale(1, -1), img); + + f = fopen("o.ppm", "w"); + fz_savepixmap(img, f); + fclose(f); + + fz_freepixmap(img); + } +#endif + + return 0; +} + diff --git a/tests/tiger.h b/tests/tiger.h new file mode 100644 index 00000000..28f39a41 --- /dev/null +++ b/tests/tiger.h @@ -0,0 +1,3162 @@ +{'T', {0.1, 0, 0, 0.1, 0, 0}}, +{'g', {1}}, +{'m', {854.246, 4873.25}}, +{'v', {855.766, 4852.03, 846.25, 4852.25}}, +{'c', {837.637, 4852.45, 656.645, 5381.27, 431.246, 5356.25}}, +{'v', {626.449, 5437.48, 854.246, 4873.25}}, +{'f', {}}, +{'w', {1.892}}, +{'m', {854.246, 4873.25}}, +{'v', {855.766, 4852.03, 846.25, 4852.25}}, +{'c', {837.637, 4852.45, 656.645, 5381.27, 431.246, 5356.25}}, +{'v', {626.449, 5437.48, 854.246, 4873.25}}, +{'h', {}}, +{'S', {}}, +{'m', {893.246, 4906.25}}, +{'v', {887.445, 4886.14, 879.25, 4889.25}}, +{'c', {870.539, 4892.71, 880.25, 5451.57, 659.25, 5505.25}}, +{'c', {659.25, 5505.24, 870.977, 5514.69, 893.246, 4906.25}}, +{'f', {}}, +{'m', {893.246, 4906.25}}, +{'v', {887.445, 4886.14, 879.25, 4889.25}}, +{'c', {870.539, 4892.71, 880.25, 5451.57, 659.25, 5505.25}}, +{'c', {659.25, 5505.24, 870.977, 5514.69, 893.246, 4906.25}}, +{'h', {}}, +{'S', {}}, +{'m', {1196.25, 4440.25}}, +{'c', {1196.25, 4440.24, 1213.87, 4429.95, 1208.25, 4422.25}}, +{'c', {1203.52, 4415.05, 662.605, 4555.88, 558.25, 4354.25}}, +{'c', {558.25, 4354.24, 599.07, 4561.71, 1196.25, 4440.25}}, +{'f', {}}, +{'m', {1196.25, 4440.25}}, +{'c', {1196.25, 4440.24, 1213.87, 4429.95, 1208.25, 4422.25}}, +{'c', {1203.52, 4415.05, 662.605, 4555.88, 558.25, 4354.25}}, +{'c', {558.25, 4354.24, 599.07, 4561.71, 1196.25, 4440.25}}, +{'h', {}}, +{'S', {}}, +{'m', {1165.25, 4328.25}}, +{'c', {1165.25, 4328.24, 1185.39, 4323.83, 1183.25, 4315.25}}, +{'c', {1180.25, 4306.43, 622.52, 4269.67, 587.25, 4045.25}}, +{'v', {560.395, 4255.19, 1165.25, 4328.25}}, +{'f', {}}, +{'m', {1165.25, 4328.25}}, +{'c', {1165.25, 4328.24, 1185.39, 4323.83, 1183.25, 4315.25}}, +{'c', {1180.25, 4306.43, 622.52, 4269.67, 587.25, 4045.25}}, +{'v', {560.395, 4255.19, 1165.25, 4328.25}}, +{'h', {}}, +{'S', {}}, +{'m', {1118.25, 4389.25}}, +{'v', {1138.21, 4381.67, 1134.25, 4373.25}}, +{'c', {1130.6, 4365.21, 573.473, 4410.19, 506.246, 4193.25}}, +{'c', {506.242, 4193.25, 509.891, 4404.92, 1118.25, 4389.25}}, +{'f', {}}, +{'m', {1118.25, 4389.25}}, +{'v', {1138.21, 4381.67, 1134.25, 4373.25}}, +{'c', {1130.6, 4365.21, 573.473, 4410.19, 506.246, 4193.25}}, +{'c', {506.242, 4193.25, 509.891, 4404.92, 1118.25, 4389.25}}, +{'h', {}}, +{'S', {}}, +{'m', {1001.25, 4589.25}}, +{'c', {1001.24, 4589.25, 1015.29, 4574.09, 1008.25, 4568.25}}, +{'c', {1001.23, 4562.63, 520.055, 4847.02, 364.246, 4681.25}}, +{'v', {460.59, 4870.14, 1001.25, 4589.25}}, +{'f', {}}, +{'m', {1001.25, 4589.25}}, +{'c', {1001.24, 4589.25, 1015.29, 4574.09, 1008.25, 4568.25}}, +{'c', {1001.23, 4562.63, 520.055, 4847.02, 364.246, 4681.25}}, +{'v', {460.59, 4870.14, 1001.25, 4589.25}}, +{'h', {}}, +{'S', {}}, +{'m', {918.25, 4543.25}}, +{'v', {933.922, 4529.72, 927.25, 4523.25}}, +{'c', {921.25, 4516.73, 410.98, 4744.86, 275.25, 4563.25}}, +{'c', {275.25, 4563.24, 349.281, 4761.08, 918.25, 4543.25}}, +{'f', {}}, +{'m', {918.25, 4543.25}}, +{'v', {933.922, 4529.72, 927.25, 4523.25}}, +{'c', {921.25, 4516.73, 410.98, 4744.86, 275.25, 4563.25}}, +{'c', {275.25, 4563.24, 349.281, 4761.08, 918.25, 4543.25}}, +{'h', {}}, +{'S', {}}, +{'m', {889.25, 4498.25}}, +{'v', {906.992, 4487.23, 901.25, 4480.25}}, +{'c', {896.211, 4472.64, 359.609, 4629.09, 250.25, 4430.25}}, +{'v', {296.273, 4636.76, 889.25, 4498.25}}, +{'f', {}}, +{'m', {889.25, 4498.25}}, +{'v', {906.992, 4487.23, 901.25, 4480.25}}, +{'c', {896.211, 4472.64, 359.609, 4629.09, 250.25, 4430.25}}, +{'v', {296.273, 4636.76, 889.25, 4498.25}}, +{'h', {}}, +{'S', {}}, +{'m', {1007.25, 4491.25}}, +{'c', {1007.24, 4491.25, 1019.72, 4474.72, 1012.25, 4470.25}}, +{'c', {1004.3, 4465.15, 563.539, 4808.88, 388.25, 4664.25}}, +{'v', {507.516, 4839.41, 1007.25, 4491.25}}, +{'f', {}}, +{'m', {1007.25, 4491.25}}, +{'c', {1007.24, 4491.25, 1019.72, 4474.72, 1012.25, 4470.25}}, +{'c', {1004.3, 4465.15, 563.539, 4808.88, 388.25, 4664.25}}, +{'v', {507.516, 4839.41, 1007.25, 4491.25}}, +{'h', {}}, +{'S', {}}, +{'m', {790.25, 4810.25}}, +{'v', {796.398, 4790.2, 787.246, 4788.25}}, +{'c', {778.797, 4785.8, 464.199, 5247.8, 253.25, 5164.25}}, +{'c', {253.25, 5164.24, 420.199, 5294, 790.25, 4810.25}}, +{'f', {}}, +{'m', {790.25, 4810.25}}, +{'v', {796.398, 4790.2, 787.246, 4788.25}}, +{'c', {778.797, 4785.8, 464.199, 5247.8, 253.25, 5164.25}}, +{'c', {253.25, 5164.24, 420.199, 5294, 790.25, 4810.25}}, +{'h', {}}, +{'S', {}}, +{'m', {797.25, 4733.25}}, +{'v', {808.168, 4715.31, 800.25, 4711.25}}, +{'c', {791.922, 4707.23, 385.086, 5090.51, 197.246, 4963.25}}, +{'v', {332.152, 5126.13, 797.25, 4733.25}}, +{'f', {}}, +{'m', {797.25, 4733.25}}, +{'v', {808.168, 4715.31, 800.25, 4711.25}}, +{'c', {791.922, 4707.23, 385.086, 5090.51, 197.246, 4963.25}}, +{'v', {332.152, 5126.13, 797.25, 4733.25}}, +{'h', {}}, +{'S', {}}, +{'m', {796.25, 4685.25}}, +{'v', {808.52, 4668.32, 801.25, 4663.25}}, +{'c', {793.098, 4658.76, 352.34, 5002.48, 177.25, 4858.25}}, +{'c', {177.25, 4858.24, 296.316, 5033.01, 796.25, 4685.25}}, +{'f', {}}, +{'m', {796.25, 4685.25}}, +{'v', {808.52, 4668.32, 801.25, 4663.25}}, +{'c', {793.098, 4658.76, 352.34, 5002.48, 177.25, 4858.25}}, +{'c', {177.25, 4858.24, 296.316, 5033.01, 796.25, 4685.25}}, +{'h', {}}, +{'S', {}}, +{'m', {772.246, 4666.25}}, +{'c', {777.402, 4599.75, 788.27, 4527.5, 807.25, 4493.25}}, +{'c', {807.25, 4493.24, 767.797, 4356.79, 864.246, 4211.25}}, +{'c', {864.242, 4211.24, 860.199, 4132.39, 878.25, 4097.25}}, +{'c', {878.25, 4097.24, 921.797, 4004.79, 974.246, 3996.25}}, +{'c', {1017.32, 3988.87, 1113.27, 3955.11, 1221.25, 3939.25}}, +{'c', {1221.24, 3939.25, 1410.2, 3784.79, 1375.25, 3644.25}}, +{'c', {1375.24, 3644.25, 1370.6, 3463.59, 1331.25, 3446.25}}, +{'c', {1331.24, 3446.25, 1458.6, 3569.19, 1353.25, 3384.25}}, +{'l', {1304.25, 3177.25}}, +{'c', {1304.24, 3177.24, 1586.2, 3415.19, 1414.25, 3213.25}}, +{'l', {1304.25, 2927.25}}, +{'c', {1304.24, 2927.25, 1520.2, 3129.19, 1441.25, 3037.25}}, +{'l', {1406.25, 2940.25}}, +{'v', {1881, 3239.19, 1542.25, 2913.25}}, +{'c', {1542.25, 2913.24, 1630.2, 2953.19, 1678.25, 2922.25}}, +{'c', {1678.24, 2922.24, 1753.4, 2935.59, 1744.25, 2918.25}}, +{'v', {1515.8, 2803.59, 1476.25, 2601.25}}, +{'v', {1568.6, 2711.19, 1533.25, 2592.25}}, +{'l', {1538.25, 2465.25}}, +{'v', {1581.8, 2702.39, 1577.25, 2289.25}}, +{'v', {1788.6, 2486.79, 1661.25, 2258.25}}, +{'l', {1661.25, 2073.25}}, +{'c', {1661.24, 2073.25, 1828.2, 2253.59, 1758.25, 2113.25}}, +{'v', {1867.8, 2209.59, 1824.25, 2042.25}}, +{'v', {1815, 1927.99, 1863.25, 2051.25}}, +{'v', {2039.4, 2387.79, 1973.25, 2099.25}}, +{'v', {1964.6, 1888.39, 2017.25, 2051.25}}, +{'v', {2021.8, 1936.79, 2123.25, 1857.25}}, +{'v', {2109.8, 2416.39, 2250.25, 2020.25}}, +{'l', {2294.25, 1840.25}}, +{'v', {2325.4, 1941.19, 2321.25, 1998.25}}, +{'v', {2483.8, 2178.79, 2409.25, 1910.25}}, +{'v', {2576.2, 2161.19, 2541.25, 2016.25}}, +{'v', {2457.4, 1839.99, 2475.25, 1787.25}}, +{'v', {2659.8, 2169.99, 2673.25, 2187.25}}, +{'v', {2651, 1721.18, 2770.25, 2117.25}}, +{'v', {2831.4, 1985.19, 2800.25, 1937.25}}, +{'v', {2888.6, 2024.79, 2880.25, 2060.25}}, +{'v', {2930.4, 2150.19, 2961.25, 2000.25}}, +{'v', {2981, 1897.19, 2998.25, 1932.25}}, +{'v', {3042.6, 1668.38, 3056.25, 1919.25}}, +{'v', {3073.4, 2068.79, 2994.25, 2196.25}}, +{'v', {3003, 2231.59, 2972.25, 2275.25}}, +{'v', {3121.8, 2037.99, 3042.25, 2355.25}}, +{'v', {3165.81, 2266.79, 3179.25, 2267.25}}, +{'v', {3029.4, 2521.99, 3126.25, 2469.25}}, +{'v', {3069, 2583.59, 3267.25, 2451.25}}, +{'v', {3091, 2627.59, 3284.25, 2522.25}}, +{'v', {3372.61, 2451.59, 3289.25, 2561.25}}, +{'v', {3130.6, 2737.59, 3372.25, 2539.25}}, +{'v', {3500.21, 2359.19, 3509.25, 2328.25}}, +{'v', {3399.01, 2649.59, 3350.25, 2680.25}}, +{'c', {3350.25, 2680.24, 3443.01, 3080.79, 3896.25, 2909.25}}, +{'c', {3896.25, 2909.24, 3971.01, 2719.99, 4019.25, 2922.25}}, +{'c', {4019.25, 2922.24, 4160.21, 2992.79, 4283.25, 2689.25}}, +{'c', {4283.25, 2689.24, 4327.41, 2838.79, 4318.25, 2869.25}}, +{'c', {4318.25, 2869.24, 4393.41, 2856.39, 4384.25, 2869.25}}, +{'c', {4384.25, 2869.24, 4529.81, 2821.19, 4543.25, 2830.25}}, +{'v', {4617.81, 2755.19, 4622.25, 2795.25}}, +{'c', {4622.24, 2795.25, 4723.41, 2763.99, 4701.25, 2803.25}}, +{'c', {4701.24, 2803.24, 4798.21, 2631.99, 4802.25, 2592.25}}, +{'l', {4829.25, 2746.25}}, +{'l', {4851.25, 2715.25}}, +{'c', {4851.25, 2715.24, 4868.61, 2799.19, 4860.25, 2812.25}}, +{'c', {4851.01, 2825.59, 5079.81, 2737.59, 5132.25, 2509.25}}, +{'l', {5154.25, 2416.25}}, +{'c', {5154.24, 2416.25, 5220.61, 2579.19, 5203.25, 2623.25}}, +{'v', {5260.21, 2614.39, 5264.25, 2566.25}}, +{'c', {5264.24, 2566.25, 5308.61, 2821.19, 5256.25, 2887.25}}, +{'c', {5256.25, 2887.24, 5304.21, 2895.99, 5317.25, 2856.25}}, +{'l', {5317.25, 2935.25}}, +{'c', {5317.24, 2935.24, 5396.61, 2926.79, 5396.25, 2953.25}}, +{'c', {5396.24, 2953.24, 5445.01, 2997.19, 5467.25, 2944.25}}, +{'c', {5467.25, 2944.24, 5330.61, 3331.59, 5533.25, 3120.25}}, +{'c', {5533.25, 3120.24, 5612.21, 3001.59, 5572.25, 3208.25}}, +{'c', {5533.01, 3415.19, 5489.01, 3432.79, 5542.25, 3437.25}}, +{'c', {5542.25, 3437.24, 5550.61, 3476.79, 5528.25, 3494.25}}, +{'y', {5506.61, 3511.99, 5542.25, 3494.25}}, +{'c', {5542.25, 3494.24, 5594.61, 3450.39, 5537.25, 3692.25}}, +{'c', {5537.24, 3692.24, 5607.81, 3674.79, 5476.25, 3996.25}}, +{'v', {5506.61, 4022.39, 5462.25, 4115.25}}, +{'c', {5462.24, 4115.25, 5550.61, 4066.39, 5581.25, 4084.25}}, +{'c', {5581.24, 4084.25, 5577.01, 4101.59, 5542.25, 4145.25}}, +{'c', {5542.25, 4145.24, 5304.21, 4748.4, 5528.25, 4506.25}}, +{'c', {5528.24, 4506.24, 5658.96, 4357.34, 5588.25, 4608.25}}, +{'v', {5488.34, 4872.25, 5497.25, 4919.25}}, +{'l', {772.246, 4666.25}}, +{'f', {}}, +{'w', {11}}, +{'m', {772.246, 4666.25}}, +{'c', {777.402, 4599.75, 788.27, 4527.5, 807.25, 4493.25}}, +{'c', {807.25, 4493.24, 767.797, 4356.79, 864.246, 4211.25}}, +{'c', {864.242, 4211.24, 860.199, 4132.39, 878.25, 4097.25}}, +{'c', {878.25, 4097.24, 921.797, 4004.79, 974.246, 3996.25}}, +{'c', {1017.32, 3988.87, 1113.27, 3955.11, 1221.25, 3939.25}}, +{'c', {1221.24, 3939.25, 1410.2, 3784.79, 1375.25, 3644.25}}, +{'c', {1375.24, 3644.25, 1370.6, 3463.59, 1331.25, 3446.25}}, +{'c', {1331.24, 3446.25, 1458.6, 3569.19, 1353.25, 3384.25}}, +{'l', {1304.25, 3177.25}}, +{'c', {1304.24, 3177.24, 1586.2, 3415.19, 1414.25, 3213.25}}, +{'l', {1304.25, 2927.25}}, +{'c', {1304.24, 2927.25, 1520.2, 3129.19, 1441.25, 3037.25}}, +{'l', {1406.25, 2940.25}}, +{'v', {1881, 3239.19, 1542.25, 2913.25}}, +{'c', {1542.25, 2913.24, 1630.2, 2953.19, 1678.25, 2922.25}}, +{'c', {1678.24, 2922.24, 1753.4, 2935.59, 1744.25, 2918.25}}, +{'v', {1515.8, 2803.59, 1476.25, 2601.25}}, +{'v', {1568.6, 2711.19, 1533.25, 2592.25}}, +{'l', {1538.25, 2465.25}}, +{'v', {1581.8, 2702.39, 1577.25, 2289.25}}, +{'v', {1788.6, 2486.79, 1661.25, 2258.25}}, +{'l', {1661.25, 2073.25}}, +{'c', {1661.24, 2073.25, 1828.2, 2253.59, 1758.25, 2113.25}}, +{'v', {1867.8, 2209.59, 1824.25, 2042.25}}, +{'v', {1815, 1927.99, 1863.25, 2051.25}}, +{'v', {2039.4, 2387.79, 1973.25, 2099.25}}, +{'v', {1964.6, 1888.39, 2017.25, 2051.25}}, +{'v', {2021.8, 1936.79, 2123.25, 1857.25}}, +{'v', {2109.8, 2416.39, 2250.25, 2020.25}}, +{'l', {2294.25, 1840.25}}, +{'v', {2325.4, 1941.19, 2321.25, 1998.25}}, +{'v', {2483.8, 2178.79, 2409.25, 1910.25}}, +{'v', {2576.2, 2161.19, 2541.25, 2016.25}}, +{'v', {2457.4, 1839.99, 2475.25, 1787.25}}, +{'v', {2659.8, 2169.99, 2673.25, 2187.25}}, +{'v', {2651, 1721.18, 2770.25, 2117.25}}, +{'v', {2831.4, 1985.19, 2800.25, 1937.25}}, +{'v', {2888.6, 2024.79, 2880.25, 2060.25}}, +{'v', {2930.4, 2150.19, 2961.25, 2000.25}}, +{'v', {2981, 1897.19, 2998.25, 1932.25}}, +{'v', {3042.6, 1668.38, 3056.25, 1919.25}}, +{'v', {3073.4, 2068.79, 2994.25, 2196.25}}, +{'v', {3003, 2231.59, 2972.25, 2275.25}}, +{'v', {3121.8, 2037.99, 3042.25, 2355.25}}, +{'v', {3165.81, 2266.79, 3179.25, 2267.25}}, +{'v', {3029.4, 2521.99, 3126.25, 2469.25}}, +{'v', {3069, 2583.59, 3267.25, 2451.25}}, +{'v', {3091, 2627.59, 3284.25, 2522.25}}, +{'v', {3372.61, 2451.59, 3289.25, 2561.25}}, +{'v', {3130.6, 2737.59, 3372.25, 2539.25}}, +{'v', {3500.21, 2359.19, 3509.25, 2328.25}}, +{'v', {3399.01, 2649.59, 3350.25, 2680.25}}, +{'c', {3350.25, 2680.24, 3443.01, 3080.79, 3896.25, 2909.25}}, +{'c', {3896.25, 2909.24, 3971.01, 2719.99, 4019.25, 2922.25}}, +{'c', {4019.25, 2922.24, 4160.21, 2992.79, 4283.25, 2689.25}}, +{'c', {4283.25, 2689.24, 4327.41, 2838.79, 4318.25, 2869.25}}, +{'c', {4318.25, 2869.24, 4393.41, 2856.39, 4384.25, 2869.25}}, +{'c', {4384.25, 2869.24, 4529.81, 2821.19, 4543.25, 2830.25}}, +{'v', {4617.81, 2755.19, 4622.25, 2795.25}}, +{'c', {4622.24, 2795.25, 4723.41, 2763.99, 4701.25, 2803.25}}, +{'c', {4701.24, 2803.24, 4798.21, 2631.99, 4802.25, 2592.25}}, +{'l', {4829.25, 2746.25}}, +{'l', {4851.25, 2715.25}}, +{'c', {4851.25, 2715.24, 4868.61, 2799.19, 4860.25, 2812.25}}, +{'c', {4851.01, 2825.59, 5079.81, 2737.59, 5132.25, 2509.25}}, +{'l', {5154.25, 2416.25}}, +{'c', {5154.24, 2416.25, 5220.61, 2579.19, 5203.25, 2623.25}}, +{'v', {5260.21, 2614.39, 5264.25, 2566.25}}, +{'c', {5264.24, 2566.25, 5308.61, 2821.19, 5256.25, 2887.25}}, +{'c', {5256.25, 2887.24, 5304.21, 2895.99, 5317.25, 2856.25}}, +{'l', {5317.25, 2935.25}}, +{'c', {5317.24, 2935.24, 5396.61, 2926.79, 5396.25, 2953.25}}, +{'c', {5396.24, 2953.24, 5445.01, 2997.19, 5467.25, 2944.25}}, +{'c', {5467.25, 2944.24, 5330.61, 3331.59, 5533.25, 3120.25}}, +{'c', {5533.25, 3120.24, 5612.21, 3001.59, 5572.25, 3208.25}}, +{'c', {5533.01, 3415.19, 5489.01, 3432.79, 5542.25, 3437.25}}, +{'c', {5542.25, 3437.24, 5550.61, 3476.79, 5528.25, 3494.25}}, +{'y', {5506.61, 3511.99, 5542.25, 3494.25}}, +{'c', {5542.25, 3494.24, 5594.61, 3450.39, 5537.25, 3692.25}}, +{'c', {5537.24, 3692.24, 5607.81, 3674.79, 5476.25, 3996.25}}, +{'v', {5506.61, 4022.39, 5462.25, 4115.25}}, +{'c', {5462.24, 4115.25, 5550.61, 4066.39, 5581.25, 4084.25}}, +{'c', {5581.24, 4084.25, 5577.01, 4101.59, 5542.25, 4145.25}}, +{'c', {5542.25, 4145.24, 5304.21, 4748.4, 5528.25, 4506.25}}, +{'c', {5528.24, 4506.24, 5658.96, 4357.34, 5588.25, 4608.25}}, +{'v', {5488.34, 4872.25, 5497.25, 4919.25}}, +{'l', {772.246, 4666.25}}, +{'h', {}}, +{'S', {}}, +{'r', {0.8, 0.451, 0.149}}, +{'m', {5497.25, 4917.25}}, +{'c', {5503.79, 4915.31, 5528.06, 4902.95, 5542.25, 4885.25}}, +{'v', {5616.61, 4766, 5559.25, 4968.25}}, +{'c', {5559.24, 4968.24, 5458.21, 5285.2, 5555.25, 5162.25}}, +{'v', {5621.01, 5082.8, 5586.25, 5232.25}}, +{'y', {5543.29, 5413.09, 5515.25, 5483.25}}, +{'c', {5515.24, 5483.24, 5643.01, 5430.4, 5348.25, 5866.25}}, +{'l', {5445.25, 5826.25}}, +{'c', {5445.25, 5826.24, 5229.41, 6262, 4992.25, 6319.25}}, +{'l', {4904.25, 6385.25}}, +{'c', {4904.25, 6385.24, 5326.21, 6803.2, 5185.25, 7208.25}}, +{'c', {5185.24, 7208.25, 5110.61, 7265.2, 5005.25, 7164.25}}, +{'v', {4934.61, 7111.2, 4868.25, 7129.25}}, +{'c', {4868.24, 7129.25, 4529.81, 7115.6, 4508.25, 7115.25}}, +{'c', {4485.81, 7115.6, 4103.01, 7524.8, 3381.25, 7331.25}}, +{'c', {3381.25, 7331.24, 3324.21, 7309.2, 3276.25, 7322.25}}, +{'c', {3276.25, 7322.24, 3073.4, 7498.4, 2536.25, 7247.25}}, +{'c', {2536.25, 7247.24, 2426.6, 7225.6, 2409.25, 7225.25}}, +{'c', {2391.4, 7225.6, 2360.6, 7225.6, 2272.25, 7155.25}}, +{'c', {2184.6, 7084.8, 2180.2, 7076, 2158.25, 7058.25}}, +{'c', {2158.25, 7058.24, 1977.8, 6935.2, 1925.25, 6926.25}}, +{'c', {1925.25, 6926.24, 1797.4, 6856, 1749.25, 6746.25}}, +{'l', {1709.25, 6733.25}}, +{'v', {1691.8, 6653.6, 1687.25, 6640.25}}, +{'c', {1687.25, 6640.24, 1634.6, 6600.8, 1626.25, 6539.25}}, +{'c', {1626.25, 6539.24, 1529, 6473.2, 1533.25, 6425.25}}, +{'v', {1515.8, 6367.6, 1507.25, 6315.25}}, +{'c', {1507.24, 6315.25, 1427.8, 6262, 1436.25, 6231.25}}, +{'c', {1436.24, 6231.24, 1353, 6077.2, 1366.25, 6002.25}}, +{'c', {1366.25, 6002.24, 1295.8, 6006.8, 1265.25, 5980.25}}, +{'c', {1265.24, 5980.24, 1256.2, 5927.6, 1238.25, 5923.25}}, +{'c', {1238.24, 5923.24, 1207.8, 5910, 1234.25, 5866.25}}, +{'v', {1216.6, 5835.2, 1212.25, 5817.25}}, +{'c', {1212.25, 5817.24, 1221, 5786.8, 1172.25, 5725.25}}, +{'c', {1172.24, 5725.24, 1102.2, 5518.4, 1124.25, 5461.25}}, +{'c', {1124.25, 5461.24, 1128.6, 5408.4, 1098.25, 5391.25}}, +{'v', {1058.2, 5395.2, 1150.25, 5263.25}}, +{'c', {1150.24, 5263.24, 1159.4, 5250, 1124.25, 5223.25}}, +{'c', {1124.25, 5223.24, 935, 5184, 908.246, 5003.25}}, +{'c', {908.242, 5003.24, 759, 4840.8, 759.246, 4783.25}}, +{'c', {759, 4758.24, 761.98, 4723.62, 769.25, 4673.25}}, +{'c', {769.25, 4673.24, 763.398, 4581.19, 1067.25, 4572.25}}, +{'y', {1370.6, 4563.59, 5497.25, 4917.25}}, +{'f', {}}, +{'m', {5497.25, 4917.25}}, +{'c', {5503.79, 4915.31, 5528.06, 4902.95, 5542.25, 4885.25}}, +{'v', {5616.61, 4766, 5559.25, 4968.25}}, +{'c', {5559.24, 4968.24, 5458.21, 5285.2, 5555.25, 5162.25}}, +{'v', {5621.01, 5082.8, 5586.25, 5232.25}}, +{'y', {5543.29, 5413.09, 5515.25, 5483.25}}, +{'c', {5515.24, 5483.24, 5643.01, 5430.4, 5348.25, 5866.25}}, +{'l', {5445.25, 5826.25}}, +{'c', {5445.25, 5826.24, 5229.41, 6262, 4992.25, 6319.25}}, +{'l', {4904.25, 6385.25}}, +{'c', {4904.25, 6385.24, 5326.21, 6803.2, 5185.25, 7208.25}}, +{'c', {5185.24, 7208.25, 5110.61, 7265.2, 5005.25, 7164.25}}, +{'v', {4934.61, 7111.2, 4868.25, 7129.25}}, +{'c', {4868.24, 7129.25, 4529.81, 7115.6, 4508.25, 7115.25}}, +{'c', {4485.81, 7115.6, 4103.01, 7524.8, 3381.25, 7331.25}}, +{'c', {3381.25, 7331.24, 3324.21, 7309.2, 3276.25, 7322.25}}, +{'c', {3276.25, 7322.24, 3073.4, 7498.4, 2536.25, 7247.25}}, +{'c', {2536.25, 7247.24, 2426.6, 7225.6, 2409.25, 7225.25}}, +{'c', {2391.4, 7225.6, 2360.6, 7225.6, 2272.25, 7155.25}}, +{'c', {2184.6, 7084.8, 2180.2, 7076, 2158.25, 7058.25}}, +{'c', {2158.25, 7058.24, 1977.8, 6935.2, 1925.25, 6926.25}}, +{'c', {1925.25, 6926.24, 1797.4, 6856, 1749.25, 6746.25}}, +{'l', {1709.25, 6733.25}}, +{'v', {1691.8, 6653.6, 1687.25, 6640.25}}, +{'c', {1687.25, 6640.24, 1634.6, 6600.8, 1626.25, 6539.25}}, +{'c', {1626.25, 6539.24, 1529, 6473.2, 1533.25, 6425.25}}, +{'v', {1515.8, 6367.6, 1507.25, 6315.25}}, +{'c', {1507.24, 6315.25, 1427.8, 6262, 1436.25, 6231.25}}, +{'c', {1436.24, 6231.24, 1353, 6077.2, 1366.25, 6002.25}}, +{'c', {1366.25, 6002.24, 1295.8, 6006.8, 1265.25, 5980.25}}, +{'c', {1265.24, 5980.24, 1256.2, 5927.6, 1238.25, 5923.25}}, +{'c', {1238.24, 5923.24, 1207.8, 5910, 1234.25, 5866.25}}, +{'v', {1216.6, 5835.2, 1212.25, 5817.25}}, +{'c', {1212.25, 5817.24, 1221, 5786.8, 1172.25, 5725.25}}, +{'c', {1172.24, 5725.24, 1102.2, 5518.4, 1124.25, 5461.25}}, +{'c', {1124.25, 5461.24, 1128.6, 5408.4, 1098.25, 5391.25}}, +{'v', {1058.2, 5395.2, 1150.25, 5263.25}}, +{'c', {1150.24, 5263.24, 1159.4, 5250, 1124.25, 5223.25}}, +{'c', {1124.25, 5223.24, 935, 5184, 908.246, 5003.25}}, +{'c', {908.242, 5003.24, 759, 4840.8, 759.246, 4783.25}}, +{'c', {759, 4758.24, 761.98, 4723.62, 769.25, 4673.25}}, +{'c', {769.25, 4673.24, 763.398, 4581.19, 1067.25, 4572.25}}, +{'y', {1370.6, 4563.59, 5497.25, 4917.25}}, +{'h', {}}, +{'S', {}}, +{'m', {928.25, 4671.25}}, +{'y', {653.398, 5104.8, 812.25, 4484.25}}, +{'y', {908.598, 4105.99, 2334.25, 4519.25}}, +{'c', {2334.25, 4519.24, 4191.01, 4854, 4314.25, 4898.25}}, +{'y', {4437.41, 4942, 5484.25, 4871.25}}, +{'l', {5423.25, 5056.25}}, +{'c', {4710.21, 5566.8, 4499.01, 5311.6, 4349.25, 5355.25}}, +{'c', {4199.81, 5399.6, 4226.21, 5294, 4191.25, 5285.25}}, +{'c', {4155.81, 5276.4, 3724.61, 5549.2, 3654.25, 5540.25}}, +{'c', {3583.81, 5531.6, 3305.05, 5792.86, 3469.25, 5443.25}}, +{'c', {3645.41, 5069.6, 2827, 5012.4, 2642.25, 5135.25}}, +{'y', {2457.4, 5258.8, 2721.25, 4933.25}}, +{'y', {2923.8, 4713.2, 2545.25, 4898.25}}, +{'c', {2167, 5038.8, 1903, 4757.2, 1868.25, 4748.25}}, +{'c', {1832.6, 4739.6, 1779.8, 4704.4, 1771.25, 4775.25}}, +{'c', {1762.2, 4845.2, 1679.59, 5028.91, 1331.25, 4739.25}}, +{'y', {1111, 4556.99, 959.246, 4799.25}}, +{'l', {928.25, 4671.25}}, +{'f', {}}, +{'r', {0.91, 0.498, 0.227}}, +{'m', {3668.25, 5521.25}}, +{'c', {3598.21, 5512.39, 3319.17, 5773.52, 3484.25, 5424.25}}, +{'c', {3666.41, 5037.19, 2841.41, 4993.19, 2656.25, 5116.25}}, +{'y', {2471.8, 5239.59, 2736.25, 4914.25}}, +{'y', {2938.21, 4693.99, 2560.25, 4879.25}}, +{'c', {2181.4, 5019.59, 1917.4, 4737.99, 1882.25, 4729.25}}, +{'c', {1847, 4720.39, 1794.2, 4685.19, 1785.25, 4755.25}}, +{'c', {1776.6, 4825.99, 1695.43, 5007.87, 1345.25, 4720.25}}, +{'y', {1111.8, 4529.59, 963.246, 4765.25}}, +{'l', {928.25, 4654.25}}, +{'y', {653.199, 5092.19, 819.25, 4451.25}}, +{'y', {915.805, 4072.38, 2348.25, 4500.25}}, +{'v', {4205.41, 4834.79, 4328.25, 4879.25}}, +{'y', {4451.81, 4922.79, 5489.25, 4853.25}}, +{'l', {5428.25, 5043.25}}, +{'c', {4715.81, 5553.99, 4513.41, 5292.39, 4364.25, 5336.25}}, +{'c', {4214.21, 5380.39, 4240.61, 5274.79, 4205.25, 5266.25}}, +{'c', {4170.21, 5257.19, 3739.01, 5529.99, 3668.25, 5521.25}}, +{'f', {}}, +{'r', {0.922, 0.549, 0.306}}, +{'m', {3683.25, 5502.25}}, +{'c', {3612.61, 5493.2, 3340.53, 5757.52, 3498.25, 5405.25}}, +{'c', {3676.41, 5006.98, 2855.81, 4973.98, 2671.25, 5097.25}}, +{'y', {2486.21, 5220.39, 2750.25, 4895.25}}, +{'y', {2952.61, 4674.79, 2574.25, 4859.25}}, +{'c', {2195.81, 5000.39, 1931.8, 4718.79, 1896.25, 4710.25}}, +{'c', {1861.39, 4701.19, 1808.6, 4665.98, 1800.25, 4736.25}}, +{'c', {1791, 4806.79, 1711.27, 4986.82, 1360.25, 4701.25}}, +{'y', {1112.59, 4502.18, 967.25, 4732.25}}, +{'l', {928.25, 4637.25}}, +{'y', {666.203, 5066.39, 826.246, 4417.25}}, +{'y', {922.996, 4038.79, 2363.25, 4481.25}}, +{'v', {4219.81, 4815.59, 4343.25, 4859.25}}, +{'y', {4466.21, 4903.59, 5494.25, 4835.25}}, +{'l', {5434.25, 5031.25}}, +{'c', {4721.41, 5541.19, 4527.81, 5273.2, 4378.25, 5317.25}}, +{'c', {4228.61, 5361.2, 4255.01, 5255.6, 4220.25, 5247.25}}, +{'c', {4184.61, 5237.98, 3753.41, 5510.8, 3683.25, 5502.25}}, +{'f', {}}, +{'r', {0.929, 0.6, 0.38}}, +{'m', {3697.25, 5483.25}}, +{'c', {3627.01, 5473.99, 3354.99, 5738.34, 3512.25, 5386.25}}, +{'c', {3697.41, 4972.88, 2864.33, 4958.7, 2685.25, 5078.25}}, +{'y', {2500.61, 5201.19, 2764.25, 4875.25}}, +{'y', {2967.01, 4655.59, 2588.25, 4840.25}}, +{'c', {2210.21, 4981.19, 1946.21, 4699.59, 1911.25, 4691.25}}, +{'c', {1875.8, 4681.99, 1823.01, 4646.79, 1814.25, 4717.25}}, +{'c', {1805.41, 4787.59, 1727.12, 4965.77, 1374.25, 4682.25}}, +{'y', {1113.39, 4474.79, 972.25, 4699.25}}, +{'l', {928.25, 4620.25}}, +{'y', {687.992, 5025.19, 833.25, 4383.25}}, +{'y', {930.203, 4005.18, 2377.25, 4462.25}}, +{'c', {2377.25, 4462.24, 4234.21, 4796.39, 4357.25, 4840.25}}, +{'y', {4480.62, 4884.39, 5499.25, 4816.25}}, +{'l', {5440.25, 5018.25}}, +{'c', {4727.02, 5528.4, 4542.22, 5253.99, 4392.25, 5298.25}}, +{'c', {4243.01, 5341.99, 4269.41, 5236.39, 4234.25, 5227.25}}, +{'c', {4199.01, 5218.79, 3767.8, 5491.59, 3697.25, 5483.25}}, +{'f', {}}, +{'r', {0.937, 0.651, 0.459}}, +{'m', {3712.25, 5463.25}}, +{'c', {3641.41, 5454.79, 3374.95, 5721.58, 3527.25, 5367.25}}, +{'c', {3711.82, 4935.59, 2884.61, 4935.59, 2700.25, 5059.25}}, +{'y', {2515, 5181.98, 2779.25, 4856.25}}, +{'y', {2981.41, 4636.39, 2603.25, 4821.25}}, +{'c', {2224.61, 4961.98, 1960.61, 4680.39, 1925.25, 4671.25}}, +{'c', {1890.21, 4662.79, 1837.41, 4627.59, 1828.25, 4698.25}}, +{'c', {1819.8, 4768.39, 1742.97, 4944.73, 1388.25, 4663.25}}, +{'y', {1114.2, 4447.38, 976.25, 4665.25}}, +{'l', {927.25, 4603.25}}, +{'y', {709.797, 4986.2, 840.25, 4350.25}}, +{'y', {937.395, 3971.58, 2392.25, 4443.25}}, +{'v', {4248.62, 4777.18, 4372.25, 4821.25}}, +{'y', {4495.02, 4865.18, 5504.25, 4798.25}}, +{'l', {5445.25, 5005.25}}, +{'c', {4732.62, 5515.59, 4556.62, 5234.79, 4407.25, 5279.25}}, +{'c', {4257.41, 5322.79, 4283.82, 5217.18, 4248.25, 5208.25}}, +{'c', {4213.41, 5199.59, 3782.21, 5472.39, 3712.25, 5463.25}}, +{'f', {}}, +{'r', {0.949, 0.702, 0.537}}, +{'m', {3726.25, 5444.25}}, +{'c', {3655.82, 5435.59, 3370.21, 5693.55, 3541.25, 5347.25}}, +{'c', {3752.61, 4920.79, 2899, 4916.39, 2714.25, 5039.25}}, +{'y', {2529.4, 5162.79, 2793.25, 4837.25}}, +{'y', {2995.8, 4617.19, 2617.25, 4802.25}}, +{'c', {2239, 4942.79, 1975, 4661.19, 1940.25, 4652.25}}, +{'c', {1904.61, 4643.59, 1851.8, 4608.39, 1843.25, 4679.25}}, +{'c', {1834.2, 4749.19, 1758.81, 4923.68, 1403.25, 4643.25}}, +{'y', {1115, 4419.98, 980.246, 4632.25}}, +{'l', {927.25, 4586.25}}, +{'y', {729.398, 4951.59, 848.246, 4316.25}}, +{'y', {944.602, 3937.98, 2406.25, 4423.25}}, +{'v', {4263.02, 4757.99, 4386.25, 4802.25}}, +{'y', {4509.41, 4845.99, 5508.25, 4779.25}}, +{'l', {5451.25, 4992.25}}, +{'c', {4738.21, 5502.79, 4571.02, 5215.59, 4421.25, 5259.25}}, +{'c', {4271.82, 5303.59, 4298.21, 5197.99, 4263.25, 5189.25}}, +{'c', {4227.82, 5180.39, 3796.61, 5453.19, 3726.25, 5444.25}}, +{'f', {}}, +{'r', {0.957, 0.749, 0.616}}, +{'m', {3740.25, 5425.25}}, +{'c', {3670.2, 5416.39, 3380.44, 5672.25, 3556.25, 5328.25}}, +{'c', {3780.21, 4888.38, 2913.4, 4897.18, 2728.25, 5020.25}}, +{'y', {2543.8, 5143.58, 2808.25, 4818.25}}, +{'y', {3010.2, 4597.98, 2632.25, 4783.25}}, +{'c', {2253.4, 4923.58, 1989.4, 4641.98, 1954.25, 4633.25}}, +{'c', {1919, 4624.38, 1866.2, 4589.18, 1857.25, 4659.25}}, +{'c', {1848.6, 4729.98, 1774.65, 4902.64, 1417.25, 4624.25}}, +{'y', {1115.79, 4392.58, 984.25, 4598.25}}, +{'l', {927.25, 4569.25}}, +{'y', {751.199, 4901.58, 855.25, 4283.25}}, +{'y', {951.797, 3904.38, 2420.25, 4404.25}}, +{'v', {4277.41, 4738.79, 4400.25, 4783.25}}, +{'y', {4523.81, 4826.79, 5513.25, 4761.25}}, +{'l', {5456.25, 4979.25}}, +{'c', {4743.81, 5489.98, 4585.41, 5196.38, 4436.25, 5240.25}}, +{'c', {4286.21, 5284.39, 4312.61, 5178.79, 4277.25, 5170.25}}, +{'c', {4242.21, 5161.18, 3811.02, 5434, 3740.25, 5425.25}}, +{'f', {}}, +{'r', {0.965, 0.8, 0.69}}, +{'m', {3755.25, 5406.25}}, +{'c', {3684.61, 5397.19, 3382.75, 5646.61, 3570.25, 5309.25}}, +{'c', {3812.21, 4873.59, 2927.8, 4877.99, 2743.25, 5001.25}}, +{'y', {2558.2, 5124.39, 2822.25, 4799.25}}, +{'y', {3024.6, 4578.79, 2646.25, 4763.25}}, +{'c', {2267.8, 4904.39, 2003.8, 4622.79, 1968.25, 4614.25}}, +{'c', {1933.4, 4605.19, 1880.6, 4569.99, 1872.25, 4640.25}}, +{'c', {1863, 4710.79, 1790.5, 4881.59, 1432.25, 4605.25}}, +{'y', {1116.6, 4365.18, 988.25, 4565.25}}, +{'l', {927.25, 4552.25}}, +{'y', {768.59, 4864.79, 862.25, 4249.25}}, +{'y', {959, 3870.77, 2435.25, 4385.25}}, +{'c', {2435.25, 4385.24, 4291.81, 4719.59, 4415.25, 4763.25}}, +{'y', {4538.21, 4807.59, 5518.25, 4743.25}}, +{'l', {5462.25, 4967.25}}, +{'c', {4749.41, 5477.19, 4599.81, 5177.19, 4450.25, 5221.25}}, +{'c', {4300.61, 5265.19, 4327.01, 5159.59, 4292.25, 5151.25}}, +{'c', {4256.61, 5141.99, 3825.41, 5414.79, 3755.25, 5406.25}}, +{'f', {}}, +{'r', {0.976, 0.851, 0.769}}, +{'m', {3769.25, 5387.25}}, +{'c', {3699.01, 5377.98, 3397.15, 5627.42, 3584.25, 5290.25}}, +{'c', {3826.61, 4854.38, 2942.21, 4858.78, 2757.25, 4982.25}}, +{'y', {2572.61, 5105.18, 2836.25, 4779.25}}, +{'y', {3039.01, 4559.58, 2660.25, 4744.25}}, +{'c', {2282.2, 4885.18, 2018.2, 4603.58, 1983.25, 4595.25}}, +{'c', {1947.8, 4585.98, 1895, 4550.78, 1886.25, 4621.25}}, +{'c', {1877.4, 4691.58, 1806.35, 4860.54, 1446.25, 4586.25}}, +{'y', {1117.4, 4337.78, 993.25, 4532.25}}, +{'l', {927.25, 4535.25}}, +{'y', {777.191, 4825.79, 869.246, 4215.25}}, +{'y', {966.207, 3837.18, 2449.25, 4366.25}}, +{'c', {2449.24, 4366.25, 4306.21, 4700.38, 4429.25, 4744.25}}, +{'y', {4552.61, 4788.38, 5523.25, 4724.25}}, +{'l', {5468.25, 4954.25}}, +{'c', {4755.01, 5464.39, 4614.21, 5157.98, 4464.25, 5202.25}}, +{'c', {4315.01, 5245.98, 4341.41, 5140.38, 4306.25, 5131.25}}, +{'c', {4271.01, 5122.78, 3839.81, 5395.58, 3769.25, 5387.25}}, +{'f', {}}, +{'r', {0.984, 0.902, 0.847}}, +{'m', {3784.25, 5367.25}}, +{'c', {3713.41, 5358.79, 3415.82, 5610.54, 3599.25, 5271.25}}, +{'c', {3841.01, 4821.99, 2956.61, 4839.59, 2772.25, 4963.25}}, +{'y', {2587.01, 5085.99, 2851.25, 4760.25}}, +{'y', {3053.41, 4540.39, 2675.25, 4725.25}}, +{'c', {2296.61, 4865.99, 2032.61, 4584.39, 1997.25, 4575.25}}, +{'c', {1962.2, 4566.79, 1909.4, 4531.59, 1900.25, 4602.25}}, +{'c', {1891.8, 4672.39, 1822.19, 4839.5, 1460.25, 4567.25}}, +{'y', {1118.2, 4310.38, 997.246, 4498.25}}, +{'l', {926.246, 4518.25}}, +{'y', {783.594, 4782.39, 876.246, 4182.25}}, +{'y', {973.398, 3803.57, 2464.25, 4347.25}}, +{'v', {4320.61, 4681.19, 4444.25, 4725.25}}, +{'y', {4567.01, 4769.19, 5528.25, 4706.25}}, +{'l', {5473.25, 4941.25}}, +{'c', {4760.61, 5451.59, 4628.61, 5138.79, 4479.25, 5183.25}}, +{'c', {4329.41, 5226.79, 4355.81, 5121.19, 4320.25, 5112.25}}, +{'c', {4285.41, 5103.59, 3854.21, 5376.39, 3784.25, 5367.25}}, +{'f', {}}, +{'r', {0.992, 0.953, 0.925}}, +{'m', {3798.25, 5348.25}}, +{'c', {3727.81, 5339.59, 3442.09, 5597.48, 3613.25, 5251.25}}, +{'c', {3846.61, 4780.78, 2971.01, 4820.38, 2786.25, 4943.25}}, +{'y', {2601.41, 5066.78, 2865.25, 4741.25}}, +{'y', {3067.81, 4521.18, 2689.25, 4706.25}}, +{'c', {2311.01, 4846.78, 2047.01, 4565.18, 2012.25, 4556.25}}, +{'c', {1976.61, 4547.58, 1923.81, 4512.38, 1915.25, 4583.25}}, +{'c', {1906.21, 4653.18, 1838.04, 4818.45, 1475.25, 4547.25}}, +{'y', {1119, 4282.98, 1001.25, 4465.25}}, +{'l', {926.246, 4501.25}}, +{'y', {785.598, 4756.58, 884.25, 4148.25}}, +{'y', {980.605, 3769.98, 2478.25, 4327.25}}, +{'v', {4335.02, 4661.98, 4458.25, 4706.25}}, +{'y', {4581.42, 4749.98, 5532.25, 4687.25}}, +{'l', {5479.25, 4928.25}}, +{'c', {4766.21, 5438.79, 4643.02, 5119.58, 4493.25, 5163.25}}, +{'c', {4343.82, 5207.59, 4370.22, 5101.98, 4335.25, 5093.25}}, +{'c', {4299.81, 5084.38, 3868.61, 5357.19, 3798.25, 5348.25}}, +{'f', {}}, +{'g', {1}}, +{'m', {926.246, 4484.25}}, +{'y', {785.398, 4726.4, 891.246, 4115.25}}, +{'y', {987.797, 3736.39, 2492.25, 4308.25}}, +{'c', {2492.25, 4308.24, 4349.41, 4642.8, 4472.25, 4687.25}}, +{'y', {4595.81, 4730.8, 5537.25, 4669.25}}, +{'l', {5484.25, 4915.25}}, +{'c', {4771.81, 5426, 4657.41, 5100.4, 4508.25, 5144.25}}, +{'c', {4358.21, 5188.4, 4384.61, 5082.8, 4349.25, 5074.25}}, +{'c', {4314.21, 5065.2, 3883.01, 5338, 3812.25, 5329.25}}, +{'c', {3742.21, 5320.4, 3464.79, 5582.27, 3628.25, 5232.25}}, +{'c', {3872.3, 4707.62, 2959.45, 4818.5, 2800.25, 4924.25}}, +{'y', {2615.8, 5047.6, 2880.25, 4722.25}}, +{'y', {3082.2, 4501.99, 2704.25, 4687.25}}, +{'c', {2325.4, 4827.6, 2061.4, 4545.99, 2026.25, 4537.25}}, +{'c', {1991, 4528.39, 1938.2, 4493.19, 1929.25, 4563.25}}, +{'c', {1920.6, 4634, 1853.88, 4797.41, 1489.25, 4528.25}}, +{'y', {1119.8, 4255.59, 1005.25, 4431.25}}, +{'l', {926.246, 4484.25}}, +{'f', {}}, +{'g', {0}}, +{'m', {1384.25, 4154.25}}, +{'c', {1384.25, 4154.24, 1304.6, 4026.79, 1533.25, 3881.25}}, +{'c', {1533.25, 3881.24, 1548.8, 3866.19, 1351.25, 3912.25}}, +{'c', {1351.25, 3912.24, 1282.6, 3934.39, 1265.25, 4049.25}}, +{'c', {1265.24, 4049.25, 1212.2, 4097.19, 1159.25, 4159.25}}, +{'y', {1106.6, 4220.39, 1384.25, 4154.25}}, +{'f', {}}, +{'g', {0.8}}, +{'m', {2924.25, 4678.25}}, +{'v', {3118.48, 4382.97, 3112.25, 4330.25}}, +{'c', {3097.6, 4215.99, 3095.4, 4110.39, 3130.25, 4066.25}}, +{'y', {3165.81, 4022.39, 3262.25, 3657.25}}, +{'c', {3262.25, 3657.24, 3258.21, 3643.99, 3394.25, 4062.25}}, +{'v', {3522.21, 4237.99, 3302.25, 4440.25}}, +{'c', {3302.25, 4440.24, 2915, 4757.2, 2924.25, 4678.25}}, +{'f', {}}, +{'g', {0}}, +{'m', {1604.25, 3859.25}}, +{'c', {1604.25, 3859.24, 1727, 3780.39, 1568.25, 3437.25}}, +{'l', {1639.25, 3463.25}}, +{'c', {1639.24, 3463.24, 1630.2, 3340.39, 1595.25, 3314.25}}, +{'l', {1674.25, 3349.25}}, +{'c', {1674.25, 3349.24, 1727, 3261.19, 1683.25, 3208.25}}, +{'c', {1683.24, 3208.24, 1867.8, 3120.39, 1859.25, 3050.25}}, +{'v', {1929.4, 3137.99, 1885.25, 3208.25}}, +{'c', {1841.4, 3278.79, 1762.2, 3234.79, 1771.25, 3437.25}}, +{'l', {1674.25, 3402.25}}, +{'v', {1735.8, 3498.79, 1736.25, 3569.25}}, +{'l', {1648.25, 3543.25}}, +{'v', {1817.96, 3835.14, 1700.25, 3851.25}}, +{'y', {1634.6, 3859.59, 1604.25, 3859.25}}, +{'f', {}}, +{'g', {0.8}}, +{'m', {1960.25, 3675.25}}, +{'v', {1991, 3723.19, 1960.25, 3714.25}}, +{'c', {1929.4, 3705.59, 1586.2, 3542.79, 1520.25, 3437.25}}, +{'c', {1520.25, 3437.24, 1898.6, 3705.59, 1960.25, 3675.25}}, +{'f', {}}, +{'m', {2074.25, 3587.25}}, +{'v', {2105.4, 3635.19, 2074.25, 3626.25}}, +{'c', {2043.8, 3617.59, 1700.6, 3454.79, 1634.25, 3349.25}}, +{'c', {1634.24, 3349.24, 2013, 3617.59, 2074.25, 3587.25}}, +{'f', {}}, +{'m', {2220.25, 3754.25}}, +{'v', {2250.6, 3802.39, 2220.25, 3793.25}}, +{'c', {2189, 3784.79, 1845.8, 3621.99, 1780.25, 3516.25}}, +{'c', {1780.25, 3516.24, 2158.2, 3784.79, 2220.25, 3754.25}}, +{'f', {}}, +{'m', {1964.25, 3274.25}}, +{'c', {1964.25, 3274.24, 1964.6, 3340.39, 1934.25, 3331.25}}, +{'c', {1903, 3322.79, 1507, 3129.19, 1441.25, 3023.25}}, +{'c', {1441.24, 3023.24, 1903, 3305.19, 1964.25, 3274.25}}, +{'f', {}}, +{'m', {1978.25, 3393.25}}, +{'c', {1978.25, 3393.24, 1991, 3445.99, 1960.25, 3437.25}}, +{'c', {1938.2, 3437.19, 1647.8, 3309.59, 1582.25, 3204.25}}, +{'v', {1907.4, 3441.59, 1978.25, 3393.25}}, +{'f', {}}, +{'m', {1819.25, 2869.25}}, +{'l', {1709.25, 2786.25}}, +{'v', {1823.8, 2869.59, 1863.25, 2856.25}}, +{'c', {1863.25, 2856.24, 1788.6, 2733.19, 1780.25, 2676.25}}, +{'v', {1894.2, 2816.79, 1956.25, 2812.25}}, +{'c', {1956.25, 2812.24, 2039.4, 2807.99, 2039.25, 2689.25}}, +{'c', {2039.25, 2689.24, 2101, 2803.59, 2136.25, 2799.25}}, +{'c', {2136.25, 2799.24, 2149.4, 2728.79, 2136.25, 2654.25}}, +{'v', {2180.2, 2737.59, 2224.25, 2720.25}}, +{'v', {2294.6, 2741.99, 2286.25, 2614.25}}, +{'v', {2285.8, 2499.99, 2277.25, 2469.25}}, +{'v', {2338.6, 2759.59, 2365.25, 2764.25}}, +{'v', {2453, 2777.19, 2506.25, 2680.25}}, +{'c', {2506.25, 2680.24, 2461.8, 2763.99, 2514.25, 2742.25}}, +{'v', {2633.4, 2724.39, 2668.25, 2649.25}}, +{'c', {2668.25, 2649.24, 2593.8, 2781.59, 2655.25, 2746.25}}, +{'c', {2655.25, 2746.24, 2730.2, 2746.39, 2743.25, 2676.25}}, +{'v', {2835.8, 2442.79, 2858.25, 2425.25}}, +{'v', {2774.2, 2662.79, 2792.25, 2663.25}}, +{'v', {2769.8, 2794.79, 2827.25, 2632.25}}, +{'v', {2791.8, 2785.99, 2853.25, 2777.25}}, +{'c', {2915, 2768.39, 2963.4, 2658.39, 3056.25, 2685.25}}, +{'v', {3161.41, 2623.19, 3183.25, 3384.25}}, +{'l', {1819.25, 2869.25}}, +{'f', {}}, +{'g', {0}}, +{'m', {1872.25, 3890.25}}, +{'c', {1872.25, 3890.24, 2035, 3956.39, 2475.25, 3890.25}}, +{'c', {2475.25, 3890.24, 2554.2, 3885.99, 2629.25, 3983.25}}, +{'c', {2703.8, 4079.59, 2998.6, 4158.79, 3069.25, 4137.25}}, +{'l', {3174.25, 4066.25}}, +{'l', {3183.25, 4053.25}}, +{'c', {3183.25, 4053.24, 3319.81, 3938.79, 3324.25, 3855.25}}, +{'c', {3328.61, 3771.59, 3165.81, 3243.59, 3060.25, 3067.25}}, +{'c', {2954.6, 2891.59, 2849, 2755.19, 2638.25, 2781.25}}, +{'c', {2638.25, 2781.24, 2409, 2825.59, 2127.25, 2781.25}}, +{'c', {2127.25, 2781.24, 1806.2, 2799.19, 1775.25, 2887.25}}, +{'y', {1744.6, 2975.19, 1898.25, 3142.25}}, +{'c', {1898.25, 3142.24, 1947, 3234.79, 1934.25, 3393.25}}, +{'c', {1920.6, 3551.59, 1925, 3859.59, 1872.25, 3890.25}}, +{'f', {}}, +{'r', {0.902, 0.4, 0.549}}, +{'m', {2114.25, 3868.25}}, +{'y', {2206.6, 3665.99, 1881.25, 2949.25}}, +{'c', {1859, 2931.19, 2020.26, 2864.69, 2132.25, 2891.25}}, +{'y', {2252.2, 2920.61, 2695.25, 2874.25}}, +{'y', {2954.6, 3045.59, 3095.25, 3534.25}}, +{'v', {3209.81, 3797.99, 3016.25, 3833.25}}, +{'y', {2822.6, 3868.39, 2114.25, 3868.25}}, +{'f', {}}, +{'r', {0.702, 0.2, 0.349}}, +{'m', {2092.25, 3528.25}}, +{'c', {2128.44, 3669.22, 2145.87, 3799.03, 2114.25, 3868.25}}, +{'c', {2114.25, 3868.24, 2800.6, 3797.99, 2924.25, 4027.25}}, +{'c', {2970.45, 4113.41, 3132.81, 3775.99, 3128.25, 3670.25}}, +{'c', {3128.25, 3670.24, 2435.4, 3511.99, 2272.25, 3635.25}}, +{'l', {2092.25, 3528.25}}, +{'f', {}}, +{'r', {0.651, 0.149, 0.298}}, +{'m', {2140.25, 3349.25}}, +{'c', {2140.25, 3349.24, 2162.6, 3269.99, 2136.25, 3226.25}}, +{'v', {2118.6, 3217.19, 2105.25, 3213.25}}, +{'v', {2118.6, 3173.19, 2184.25, 3155.25}}, +{'c', {2184.25, 3155.24, 2206.6, 3107.19, 2233.25, 3103.25}}, +{'c', {2259.4, 3098.39, 2312.2, 3036.79, 2356.25, 3050.25}}, +{'y', {2400.2, 3063.19, 2523.25, 3107.25}}, +{'c', {2523.25, 3107.24, 2585, 3142.39, 2682.25, 3103.25}}, +{'v', {2707.92, 3111.61, 2712.25, 3155.25}}, +{'c', {2718.1, 3207.29, 2752.2, 3247.99, 2774.25, 3270.25}}, +{'c', {2796.2, 3291.99, 2901.8, 3432.79, 2888.25, 3437.25}}, +{'y', {2875.4, 3441.59, 2140.25, 3349.25}}, +{'f', {}}, +{'r', {1, 0.451, 0.498}}, +{'m', {2092.25, 3881.25}}, +{'c', {2092.25, 3881.24, 2061.4, 3635.19, 2096.25, 3543.25}}, +{'c', {2131.8, 3450.39, 2123, 3428.39, 2114.25, 3384.25}}, +{'c', {2105.4, 3340.39, 2153.8, 3230.39, 2215.25, 3164.25}}, +{'l', {2347.25, 3147.25}}, +{'v', {2514.6, 3186.39, 2616.25, 3155.25}}, +{'c', {2616.25, 3155.24, 2714.73, 3140.81, 2752.25, 3305.25}}, +{'c', {2752.25, 3305.24, 2805, 3375.59, 2884.25, 3406.25}}, +{'c', {2963.4, 3437.19, 3042.6, 3894.79, 2998.25, 3983.25}}, +{'c', {2954.6, 4070.79, 2796.2, 4119.19, 2620.25, 3947.25}}, +{'c', {2444.2, 3775.99, 2422.2, 3960.79, 2092.25, 3881.25}}, +{'f', {}}, +{'m', {2092.25, 3881.25}}, +{'c', {2092.25, 3881.24, 2061.4, 3635.19, 2096.25, 3543.25}}, +{'c', {2131.8, 3450.39, 2123, 3428.39, 2114.25, 3384.25}}, +{'c', {2105.4, 3340.39, 2153.8, 3230.39, 2215.25, 3164.25}}, +{'l', {2347.25, 3147.25}}, +{'v', {2514.6, 3186.39, 2616.25, 3155.25}}, +{'c', {2616.25, 3155.24, 2714.73, 3140.81, 2752.25, 3305.25}}, +{'c', {2752.25, 3305.24, 2805, 3375.59, 2884.25, 3406.25}}, +{'c', {2963.4, 3437.19, 3042.6, 3894.79, 2998.25, 3983.25}}, +{'c', {2954.6, 4070.79, 2796.2, 4119.19, 2620.25, 3947.25}}, +{'c', {2444.2, 3775.99, 2422.2, 3960.79, 2092.25, 3881.25}}, +{'h', {}}, +{'S', {}}, +{'r', {1, 1, 0.8}}, +{'m', {2110.25, 3059.25}}, +{'v', {2101, 3080.79, 2052.25, 3085.25}}, +{'c', {2052.25, 3085.24, 1806.2, 3124.79, 1714.25, 3261.25}}, +{'c', {1714.25, 3261.24, 1639, 3322.79, 1687.25, 3195.25}}, +{'c', {1687.25, 3195.24, 1801.8, 2970.79, 1876.25, 2940.25}}, +{'v', {2057, 2895.99, 2110.25, 3059.25}}, +{'f', {}}, +{'w', {5.5}}, +{'m', {2110.25, 3059.25}}, +{'v', {2101, 3080.79, 2052.25, 3085.25}}, +{'c', {2052.25, 3085.24, 1806.2, 3124.79, 1714.25, 3261.25}}, +{'c', {1714.25, 3261.24, 1639, 3322.79, 1687.25, 3195.25}}, +{'c', {1687.25, 3195.24, 1801.8, 2970.79, 1876.25, 2940.25}}, +{'v', {2057, 2895.99, 2110.25, 3059.25}}, +{'h', {}}, +{'S', {}}, +{'r', {0.8, 0.247, 0.298}}, +{'m', {2989.25, 3762.25}}, +{'c', {2996.41, 3849.45, 3017.89, 3944.2, 2998.25, 3983.25}}, +{'c', {2927.69, 4124.62, 2740.99, 4065.35, 2620.25, 3947.25}}, +{'c', {2444.2, 3775.99, 2422.2, 3960.79, 2092.25, 3881.25}}, +{'c', {2092.25, 3881.24, 2073, 3727.99, 2082.25, 3618.25}}, +{'v', {2492.6, 3745.19, 2501.25, 3683.25}}, +{'c', {2501.25, 3683.24, 2519, 3718.79, 2620.25, 3719.25}}, +{'c', {2721.4, 3718.79, 2971.56, 3731.68, 2989.25, 3762.25}}, +{'f', {}}, +{'w', {22}}, +{'R', {0.651, 0.098, 0.149}}, +{'m', {2514.25, 3873.25}}, +{'v', {2567.4, 3819.99, 2528.25, 3714.25}}, +{'c', {2528.25, 3714.24, 2369.4, 3538.39, 2391.25, 3384.25}}, +{'S', {}}, +{'r', {1, 1, 0.8}}, +{'m', {1986.25, 2940.25}}, +{'v', {1938.2, 3080.79, 2035.25, 3006.25}}, +{'v', {2087.8, 2983.99, 2074.25, 2966.25}}, +{'c', {2061.4, 2948.79, 1999.8, 2904.79, 1986.25, 2940.25}}, +{'f', {}}, +{'w', {5.5}}, +{'G', {0}}, +{'m', {1986.25, 2940.25}}, +{'v', {1938.2, 3080.79, 2035.25, 3006.25}}, +{'v', {2087.8, 2983.99, 2074.25, 2966.25}}, +{'c', {2061.4, 2948.79, 1999.8, 2904.79, 1986.25, 2940.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2042.25, 2927.25}}, +{'c', {2042.24, 2927.25, 2003.32, 3039.43, 2081.25, 2979.25}}, +{'c', {2081.25, 2979.24, 2129.39, 2952.66, 2112.25, 2948.25}}, +{'c', {2062.28, 2933.83, 2112.44, 2905.67, 2042.25, 2927.25}}, +{'f', {}}, +{'m', {2042.25, 2927.25}}, +{'c', {2042.24, 2927.25, 2003.32, 3039.43, 2081.25, 2979.25}}, +{'c', {2081.25, 2979.24, 2129.39, 2952.66, 2112.25, 2948.25}}, +{'c', {2062.28, 2933.83, 2112.44, 2905.67, 2042.25, 2927.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2095.25, 2927.25}}, +{'v', {2056.12, 3039.43, 2133.25, 2979.25}}, +{'c', {2133.25, 2979.24, 2181.68, 2954.18, 2165.25, 2948.25}}, +{'c', {2128.28, 2933.83, 2165.24, 2905.67, 2095.25, 2927.25}}, +{'f', {}}, +{'m', {2095.25, 2927.25}}, +{'v', {2056.12, 3039.43, 2133.25, 2979.25}}, +{'c', {2133.25, 2979.24, 2181.68, 2954.18, 2165.25, 2948.25}}, +{'c', {2128.28, 2933.83, 2165.24, 2905.67, 2095.25, 2927.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2167.25, 2924.25}}, +{'c', {2167.25, 2924.24, 2128.72, 3037.23, 2206.25, 2977.25}}, +{'c', {2206.25, 2977.24, 2254.37, 2951.73, 2238.25, 2945.25}}, +{'c', {2205.28, 2933.83, 2237.85, 2903.47, 2167.25, 2924.25}}, +{'f', {}}, +{'m', {2167.25, 2924.25}}, +{'c', {2167.25, 2924.24, 2128.72, 3037.23, 2206.25, 2977.25}}, +{'c', {2206.25, 2977.24, 2254.37, 2951.73, 2238.25, 2945.25}}, +{'c', {2205.28, 2933.83, 2237.85, 2903.47, 2167.25, 2924.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2238.25, 2925.25}}, +{'v', {2200, 3038.11, 2277.25, 2978.25}}, +{'v', {2319.69, 2960.67, 2309.25, 2946.25}}, +{'c', {2298.57, 2932.51, 2309.13, 2904.35, 2238.25, 2925.25}}, +{'f', {}}, +{'m', {2238.25, 2925.25}}, +{'v', {2200, 3038.11, 2277.25, 2978.25}}, +{'v', {2319.69, 2960.67, 2309.25, 2946.25}}, +{'c', {2298.57, 2932.51, 2309.13, 2904.35, 2238.25, 2925.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2312.25, 2918.25}}, +{'v', {2259.4, 3054.39, 2360.25, 2984.25}}, +{'v', {2413.4, 2961.99, 2400.25, 2944.25}}, +{'c', {2387, 2926.79, 2400.2, 2891.59, 2312.25, 2918.25}}, +{'f', {}}, +{'m', {2312.25, 2918.25}}, +{'v', {2259.4, 3054.39, 2360.25, 2984.25}}, +{'v', {2413.4, 2961.99, 2400.25, 2944.25}}, +{'c', {2387, 2926.79, 2400.2, 2891.59, 2312.25, 2918.25}}, +{'h', {}}, +{'S', {}}, +{'w', {22}}, +{'R', {0.651, 0.149, 0.298}}, +{'m', {2000.25, 3107.25}}, +{'c', {2000.25, 3107.24, 2145, 3137.99, 2211.25, 3103.25}}, +{'v', {2277, 3089.59, 2290.25, 3094.25}}, +{'y', {2303.4, 3098.39, 2338.25, 3103.25}}, +{'S', {}}, +{'m', {2374.25, 3010.25}}, +{'c', {2374.25, 3010.24, 2505.8, 3159.99, 2638.25, 3111.25}}, +{'c', {2714.97, 3083.29, 2703.8, 3120.39, 2712.25, 3151.25}}, +{'c', {2721.4, 3181.99, 2723.6, 3228.19, 2778.25, 3261.25}}, +{'S', {}}, +{'m', {2563.25, 3186.25}}, +{'c', {2563.25, 3186.24, 2519, 3305.19, 2488.25, 3164.25}}, +{'c', {2457.4, 3023.59, 2422.2, 2983.99, 2404.25, 2953.25}}, +{'c', {2404.25, 2953.24, 2404.6, 2895.99, 2497.25, 2900.25}}, +{'c', {2497.25, 2900.24, 2615.8, 2904.79, 2620.25, 2935.25}}, +{'c', {2624.6, 2966.39, 2607, 3093.99, 2563.25, 3186.25}}, +{'f', {}}, +{'w', {5.5}}, +{'G', {0}}, +{'m', {2563.25, 3186.25}}, +{'c', {2563.25, 3186.24, 2519, 3305.19, 2488.25, 3164.25}}, +{'c', {2457.4, 3023.59, 2422.2, 2983.99, 2404.25, 2953.25}}, +{'c', {2404.25, 2953.24, 2404.6, 2895.99, 2497.25, 2900.25}}, +{'c', {2497.25, 2900.24, 2615.8, 2904.79, 2620.25, 2935.25}}, +{'c', {2624.6, 2966.39, 2607, 3093.99, 2563.25, 3186.25}}, +{'h', {}}, +{'S', {}}, +{'w', {22}}, +{'R', {0.651, 0.149, 0.298}}, +{'m', {2717.25, 3107.25}}, +{'c', {2717.25, 3107.24, 2756.6, 3133.59, 2783.25, 3120.25}}, +{'S', {}}, +{'m', {2788.25, 3287.25}}, +{'c', {2788.25, 3287.24, 2820.4, 3341.49, 2873.25, 3350.25}}, +{'S', {}}, +{'g', {0.702}}, +{'m', {1916.25, 2883.25}}, +{'v', {2114.2, 2847.59, 2162.25, 2865.25}}, +{'c', {2162.25, 2865.24, 2259.4, 2865.19, 2167.25, 2843.25}}, +{'c', {2167.25, 2843.24, 2026.2, 2843.19, 1938.25, 2856.25}}, +{'c', {1938.25, 2856.24, 1810.6, 2917.99, 1916.25, 2883.25}}, +{'f', {}}, +{'r', {1, 1, 0.8}}, +{'m', {2070.25, 3908.25}}, +{'v', {2263.8, 3907.99, 2286.25, 3899.25}}, +{'c', {2286.25, 3899.24, 2365, 3560.39, 2325.25, 3477.25}}, +{'v', {2312.2, 3445.99, 2281.25, 3507.25}}, +{'c', {2281.25, 3507.24, 2079, 3868.39, 2044.25, 3890.25}}, +{'c', {2008.6, 3912.39, 2057, 3907.99, 2070.25, 3908.25}}, +{'f', {}}, +{'w', {5.5}}, +{'G', {0}}, +{'m', {2070.25, 3908.25}}, +{'v', {2263.8, 3907.99, 2286.25, 3899.25}}, +{'c', {2286.25, 3899.24, 2365, 3560.39, 2325.25, 3477.25}}, +{'v', {2312.2, 3445.99, 2281.25, 3507.25}}, +{'c', {2281.25, 3507.24, 2079, 3868.39, 2044.25, 3890.25}}, +{'c', {2008.6, 3912.39, 2057, 3907.99, 2070.25, 3908.25}}, +{'h', {}}, +{'S', {}}, +{'m', {1222.25, 3937.25}}, +{'v', {1320, 3918.99, 1458.25, 3890.25}}, +{'c', {1458.24, 3890.24, 1511.4, 3643.99, 1546.25, 3591.25}}, +{'c', {1581.8, 3538.39, 1542.2, 3538.39, 1502.25, 3569.25}}, +{'c', {1463, 3599.99, 1300.2, 3753.99, 1278.25, 3802.25}}, +{'y', {1256.2, 3850.79, 1222.25, 3937.25}}, +{'f', {}}, +{'m', {1222.25, 3937.25}}, +{'v', {1320, 3918.99, 1458.25, 3890.25}}, +{'c', {1458.24, 3890.24, 1511.4, 3643.99, 1546.25, 3591.25}}, +{'c', {1581.8, 3538.39, 1542.2, 3538.39, 1502.25, 3569.25}}, +{'c', {1463, 3599.99, 1300.2, 3753.99, 1278.25, 3802.25}}, +{'y', {1256.2, 3850.79, 1222.25, 3937.25}}, +{'h', {}}, +{'S', {}}, +{'m', {1462.25, 3888.25}}, +{'c', {1462.25, 3888.24, 1526.37, 3870.97, 1537.25, 3846.25}}, +{'y', {1548.55, 3822.25, 1524.25, 3786.25}}, +{'c', {1524.24, 3786.25, 1513.25, 3749.63, 1500.25, 3773.25}}, +{'c', {1486.8, 3797.21, 1455.21, 3881.46, 1462.25, 3888.25}}, +{'f', {}}, +{'m', {1462.25, 3888.25}}, +{'c', {1462.25, 3888.24, 1526.37, 3870.97, 1537.25, 3846.25}}, +{'y', {1548.55, 3822.25, 1524.25, 3786.25}}, +{'c', {1524.24, 3786.25, 1513.25, 3749.63, 1500.25, 3773.25}}, +{'c', {1486.8, 3797.21, 1455.21, 3881.46, 1462.25, 3888.25}}, +{'h', {}}, +{'S', {}}, +{'g', {0}}, +{'m', {1463.25, 3890.25}}, +{'c', {1463.24, 3890.24, 1502.6, 3833.19, 1542.25, 3833.25}}, +{'c', {1581.8, 3833.19, 1586, 3837.73, 1617.25, 3831.25}}, +{'c', {1667.6, 3819.99, 1663.2, 3841.99, 1736.25, 3829.25}}, +{'c', {1764.84, 3823.51, 1793, 3833.19, 1824.25, 3820.25}}, +{'c', {1854.6, 3806.79, 1889.8, 3815.59, 1903.25, 3837.25}}, +{'y', {1916.2, 3859.59, 1969.25, 3906.25}}, +{'v', {1828.2, 3885.99, 1797.25, 3877.25}}, +{'c', {1797.25, 3877.24, 1551, 3863.99, 1463.25, 3890.25}}, +{'f', {}}, +{'r', {1, 1, 0.8}}, +{'m', {1953.25, 3888.25}}, +{'c', {1953.25, 3888.24, 1882.65, 3849.69, 1878.25, 3823.25}}, +{'y', {1873.85, 3796.89, 1936.25, 3756.25}}, +{'c', {1936.25, 3756.24, 1966.25, 3705.59, 1973.25, 3732.25}}, +{'c', {1979.45, 3758.39, 1962.4, 3883.79, 1953.25, 3888.25}}, +{'f', {}}, +{'m', {1953.25, 3888.25}}, +{'c', {1953.25, 3888.24, 1882.65, 3849.69, 1878.25, 3823.25}}, +{'y', {1873.85, 3796.89, 1936.25, 3756.25}}, +{'c', {1936.25, 3756.24, 1966.25, 3705.59, 1973.25, 3732.25}}, +{'c', {1979.45, 3758.39, 1962.4, 3883.79, 1953.25, 3888.25}}, +{'h', {}}, +{'S', {}}, +{'m', {1541.25, 3828.25}}, +{'v', {1618.34, 3705.02, 1620.25, 3828.25}}, +{'v', {1626.86, 3842.18, 1607.25, 3842.25}}, +{'c', {1540.67, 3843.25, 1557.4, 3888.38, 1541.25, 3828.25}}, +{'f', {}}, +{'m', {1541.25, 3828.25}}, +{'v', {1618.34, 3705.02, 1620.25, 3828.25}}, +{'v', {1626.86, 3842.18, 1607.25, 3842.25}}, +{'c', {1540.67, 3843.25, 1557.4, 3888.38, 1541.25, 3828.25}}, +{'h', {}}, +{'S', {}}, +{'m', {1620.25, 3825.25}}, +{'v', {1707.35, 3702.29, 1700.25, 3826.25}}, +{'c', {1700.24, 3826.24, 1700.43, 3830.02, 1681.25, 3831.25}}, +{'c', {1628.93, 3836.26, 1632.73, 3885.95, 1620.25, 3825.25}}, +{'f', {}}, +{'m', {1620.25, 3825.25}}, +{'v', {1707.35, 3702.29, 1700.25, 3826.25}}, +{'c', {1700.24, 3826.24, 1700.43, 3830.02, 1681.25, 3831.25}}, +{'c', {1628.93, 3836.26, 1632.73, 3885.95, 1620.25, 3825.25}}, +{'h', {}}, +{'S', {}}, +{'m', {1699.25, 3825.25}}, +{'v', {1787.12, 3708.35, 1780.25, 3814.25}}, +{'v', {1781.07, 3828.24, 1763.25, 3831.25}}, +{'c', {1720.07, 3839.31, 1717.75, 3875.05, 1699.25, 3825.25}}, +{'f', {}}, +{'m', {1699.25, 3825.25}}, +{'v', {1787.12, 3708.35, 1780.25, 3814.25}}, +{'v', {1781.07, 3828.24, 1763.25, 3831.25}}, +{'c', {1720.07, 3839.31, 1717.75, 3875.05, 1699.25, 3825.25}}, +{'h', {}}, +{'S', {}}, +{'m', {1775.25, 3824.25}}, +{'c', {1775.25, 3824.24, 1862.1, 3697.2, 1866.25, 3805.25}}, +{'c', {1866.24, 3805.25, 1884.01, 3819.95, 1865.25, 3822.25}}, +{'c', {1800.81, 3830.54, 1809.59, 3879.53, 1775.25, 3824.25}}, +{'f', {}}, +{'m', {1775.25, 3824.25}}, +{'c', {1775.25, 3824.24, 1862.1, 3697.2, 1866.25, 3805.25}}, +{'c', {1866.24, 3805.25, 1884.01, 3819.95, 1865.25, 3822.25}}, +{'c', {1800.81, 3830.54, 1809.59, 3879.53, 1775.25, 3824.25}}, +{'h', {}}, +{'S', {}}, +{'r', {0.902, 0.902, 0.702}}, +{'m', {1377.25, 3785.25}}, +{'l', {1293.25, 3802.25}}, +{'y', {1264.45, 3857.39, 1241.25, 3925.25}}, +{'c', {1241.25, 3925.24, 1311.2, 3914.04, 1448.25, 3883.25}}, +{'v', {1458.34, 3846.74, 1474.25, 3783.25}}, +{'l', {1377.25, 3785.25}}, +{'f', {}}, +{'m', {2093.25, 3837.25}}, +{'c', {2074.71, 3864.39, 2060.22, 3883.73, 2053.25, 3888.25}}, +{'c', {2019.93, 3908.91, 2065.5, 3904.77, 2078.25, 3905.25}}, +{'v', {2260.2, 3904.77, 2281.25, 3896.25}}, +{'v', {2286.68, 3871.82, 2294.25, 3834.25}}, +{'v', {2183.23, 3856.63, 2093.25, 3837.25}}, +{'f', {}}, +{'r', {0.8, 0.451, 0.149}}, +{'m', {2682.25, 5356.25}}, +{'c', {2987.61, 5312.91, 3268.33, 5704.95, 3287.25, 5811.25}}, +{'y', {3307.05, 5917.92, 3195.25, 6048.25}}, +{'c', {3210.25, 6082.48, 3157.01, 6237.36, 3099.25, 6339.25}}, +{'c', {3040.85, 6440.64, 2865.94, 6429.93, 2673.25, 6440.25}}, +{'c', {2498.76, 6450.32, 2295.48, 6193.8, 2281.25, 6174.25}}, +{'c', {2266.44, 6155.08, 2334.2, 5733.99, 2348.25, 5671.25}}, +{'y', {2363.24, 5608.15, 2334.25, 5317.25}}, +{'c', {2710.84, 5417.85, 2377.76, 5400.03, 2682.25, 5356.25}}, +{'f', {}}, +{'r', {0.922, 0.561, 0.318}}, +{'m', {2289.25, 6167.25}}, +{'c', {2274.71, 6148.3, 2341.24, 5734.87, 2355.25, 5673.25}}, +{'y', {2369.75, 5611.32, 2341.25, 5326.25}}, +{'c', {2700.63, 5423.88, 2384.01, 5406.98, 2683.25, 5364.25}}, +{'c', {2982.77, 5321.44, 3258.39, 5706.36, 3277.25, 5811.25}}, +{'y', {3296.4, 5915.45, 3187.25, 6044.25}}, +{'c', {3201.36, 6077.02, 3149.09, 6229.09, 3092.25, 6329.25}}, +{'c', {3035.04, 6428.67, 2863.32, 6418.14, 2674.25, 6428.25}}, +{'c', {2502.8, 6438.18, 2303.22, 6186.32, 2289.25, 6167.25}}, +{'f', {}}, +{'r', {0.941, 0.671, 0.49}}, +{'m', {2297.25, 6160.25}}, +{'c', {2282.98, 6141.53, 2348.28, 5735.75, 2362.25, 5675.25}}, +{'y', {2376.26, 5614.48, 2348.25, 5334.25}}, +{'c', {2693.72, 5435.4, 2390.25, 5413.93, 2684.25, 5372.25}}, +{'c', {2977.93, 5329.98, 3248.44, 5707.76, 3267.25, 5810.25}}, +{'y', {3285.75, 5912.99, 3178.25, 6039.25}}, +{'c', {3192.47, 6071.57, 3141.17, 6220.81, 3085.25, 6319.25}}, +{'c', {3029.23, 6416.7, 2860.69, 6406.38, 2675.25, 6416.25}}, +{'c', {2506.86, 6426.03, 2310.96, 6178.84, 2297.25, 6160.25}}, +{'f', {}}, +{'r', {0.961, 0.78, 0.659}}, +{'m', {2305.25, 6153.25}}, +{'c', {2291.25, 6134.75, 2355.32, 5736.63, 2369.25, 5677.25}}, +{'y', {2382.77, 5617.65, 2355.25, 5343.25}}, +{'c', {2679.11, 5442.53, 2396.5, 5420.88, 2685.25, 5379.25}}, +{'c', {2973.09, 5338.52, 3238.5, 5709.17, 3257.25, 5810.25}}, +{'y', {3275.11, 5910.53, 3170.25, 6034.25}}, +{'c', {3183.59, 6066.11, 3133.25, 6212.54, 3078.25, 6308.25}}, +{'c', {3023.43, 6404.73, 2858.06, 6394.6, 2675.25, 6404.25}}, +{'c', {2510.9, 6413.89, 2318.71, 6171.36, 2305.25, 6153.25}}, +{'f', {}}, +{'r', {0.98, 0.89, 0.831}}, +{'m', {2313.25, 6146.25}}, +{'c', {2299.53, 6127.97, 2362.36, 5737.51, 2376.25, 5679.25}}, +{'y', {2389.29, 5620.82, 2362.25, 5351.25}}, +{'c', {2668.91, 5449.66, 2402.75, 5427.84, 2685.25, 5387.25}}, +{'c', {2968.25, 5347.05, 3228.55, 5710.58, 3246.25, 5809.25}}, +{'y', {3264.46, 5908.06, 3161.25, 6029.25}}, +{'c', {3174.7, 6060.65, 3125.33, 6204.27, 3071.25, 6298.25}}, +{'c', {3017.62, 6392.77, 2855.43, 6382.83, 2676.25, 6393.25}}, +{'c', {2514.95, 6401.74, 2326.45, 6163.88, 2313.25, 6146.25}}, +{'f', {}}, +{'g', {1}}, +{'m', {2686.25, 5395.25}}, +{'c', {2963.4, 5355.6, 3218.61, 5712, 3236.25, 5809.25}}, +{'y', {3253.81, 5905.6, 3152.25, 6024.25}}, +{'c', {3165.81, 6055.2, 3117.4, 6196, 3064.25, 6288.25}}, +{'c', {3011.8, 6380.8, 2852.8, 6371.05, 2677.25, 6381.25}}, +{'c', {2519, 6389.6, 2334.2, 6156.4, 2321.25, 6139.25}}, +{'c', {2307.8, 6121.2, 2369.4, 5738.4, 2382.25, 5681.25}}, +{'y', {2395.8, 5624, 2369.25, 5360.25}}, +{'c', {2649.9, 5454.6, 2409, 5434.8, 2686.25, 5395.25}}, +{'f', {}}, +{'g', {0.8}}, +{'m', {3196.25, 5769.25}}, +{'c', {3196.25, 5769.24, 2890.8, 5685.6, 2763.25, 5703.25}}, +{'c', {2763.25, 5703.24, 2589.4, 5775.8, 2492.25, 5536.25}}, +{'v', {2453, 5456.8, 2431.25, 5435.25}}, +{'y', {2409, 5412.8, 3196.25, 5769.25}}, +{'f', {}}, +{'g', {0}}, +{'m', {3238.25, 5793.25}}, +{'c', {3238.25, 5793.24, 2919.4, 5659.2, 2809.25, 5663.25}}, +{'c', {2809.25, 5663.24, 2629, 5714.2, 2536.25, 5553.25}}, +{'c', {2536.25, 5553.24, 2444.2, 5452.4, 2409.25, 5435.25}}, +{'v', {2404.6, 5417.2, 2475.25, 5461.25}}, +{'l', {2589.25, 5404.25}}, +{'v', {2752.2, 5298.4, 2858.25, 5474.25}}, +{'c', {2858.25, 5474.24, 2901.8, 5597.6, 2902.25, 5619.25}}, +{'c', {2901.8, 5641.6, 3135, 5703.2, 3152.25, 5707.25}}, +{'c', {3170.21, 5712, 3242.81, 5758.2, 3238.25, 5793.25}}, +{'f', {}}, +{'r', {0.6, 0.8, 0.2}}, +{'m', {2717.25, 5398.25}}, +{'c', {2641.41, 5398.34, 2549.3, 5440.86, 2549.25, 5509.25}}, +{'c', {2549.3, 5578.33, 2641.41, 5647.24, 2717.25, 5647.25}}, +{'c', {2792.61, 5647.24, 2853.9, 5591.53, 2854.25, 5523.25}}, +{'c', {2853.9, 5454.06, 2792.61, 5398.34, 2717.25, 5398.25}}, +{'f', {}}, +{'r', {0.4, 0.6, 0}}, +{'m', {2677.25, 5582.25}}, +{'c', {2623.84, 5573.93, 2567.86, 5557.39, 2568.25, 5560.25}}, +{'c', {2585.59, 5610.58, 2655.56, 5647.24, 2717.25, 5647.25}}, +{'c', {2764.25, 5647.24, 2805.92, 5625.48, 2830.25, 5592.25}}, +{'v', {2772.04, 5596, 2677.25, 5582.25}}, +{'f', {}}, +{'g', {1}}, +{'m', {2809.25, 5584.25}}, +{'c', {2809.25, 5584.24, 2761, 5619.6, 2761.25, 5595.25}}, +{'c', {2761.25, 5595.24, 2800.6, 5547, 2809.25, 5584.25}}, +{'f', {}}, +{'g', {0}}, +{'m', {2699.25, 5495.25}}, +{'c', {2671.91, 5495.01, 2649.62, 5517.3, 2649.25, 5545.25}}, +{'c', {2649.62, 5572.29, 2671.91, 5594.57, 2699.25, 5594.25}}, +{'c', {2726.9, 5594.57, 2749.18, 5572.29, 2749.25, 5545.25}}, +{'c', {2749.18, 5517.3, 2726.9, 5495.01, 2699.25, 5495.25}}, +{'f', {}}, +{'r', {0.8, 0.451, 0.149}}, +{'m', {1555.25, 5641.25}}, +{'c', {1555.25, 5641.24, 1520.2, 5874.8, 1546.25, 5923.25}}, +{'c', {1546.24, 5923.24, 1665.4, 6033.2, 1661.25, 6073.25}}, +{'c', {1661.24, 6073.25, 1656.6, 6270.8, 1643.25, 6279.25}}, +{'c', {1630.2, 6288.4, 1546.6, 6354.4, 1480.25, 6284.25}}, +{'c', {1480.24, 6284.25, 1366.2, 6086, 1375.25, 6015.25}}, +{'l', {1375.25, 5993.25}}, +{'c', {1375.24, 5993.24, 1291.4, 5998, 1274.25, 5976.25}}, +{'v', {1260.6, 5918.8, 1247.25, 5914.25}}, +{'c', {1247.25, 5914.24, 1216.6, 5888, 1238.25, 5857.25}}, +{'c', {1238.24, 5857.24, 1216.6, 5830.8, 1221.25, 5787.25}}, +{'l', {1304.25, 5743.25}}, +{'c', {1304.24, 5743.25, 1326.6, 5584.4, 1445.25, 5527.25}}, +{'c', {1498.59, 5501.58, 1533.4, 5575.6, 1555.25, 5641.25}}, +{'f', {}}, +{'g', {1}}, +{'m', {1544.25, 5662.25}}, +{'v', {1512.72, 5871.72, 1536.25, 5915.25}}, +{'v', {1643.4, 6014.28, 1639.25, 6050.25}}, +{'c', {1639.24, 6050.25, 1635.48, 6228.12, 1623.25, 6236.25}}, +{'c', {1611.72, 6243.96, 1536.48, 6303.36, 1477.25, 6240.25}}, +{'v', {1374.12, 6061.8, 1382.25, 5998.25}}, +{'l', {1382.25, 5978.25}}, +{'c', {1382.25, 5978.24, 1306.8, 5982.6, 1291.25, 5963.25}}, +{'v', {1279.08, 5911.32, 1267.25, 5907.25}}, +{'v', {1239.48, 5883.6, 1259.25, 5856.25}}, +{'v', {1239.48, 5832.12, 1243.25, 5792.25}}, +{'l', {1318.25, 5753.25}}, +{'v', {1338.48, 5610.36, 1445.25, 5559.25}}, +{'c', {1493.27, 5535.83, 1524.6, 5602.44, 1544.25, 5662.25}}, +{'f', {}}, +{'r', {0.925, 0.588, 0.361}}, +{'m', {1638.25, 6268.25}}, +{'c', {1626.46, 6278.17, 1544.07, 6341.64, 1479.25, 6273.25}}, +{'v', {1368.18, 6079.95, 1377.25, 6011.25}}, +{'l', {1377.25, 5990.25}}, +{'v', {1295.25, 5994.15, 1278.25, 5972.25}}, +{'v', {1265.22, 5916.93, 1252.25, 5912.25}}, +{'c', {1252.25, 5912.24, 1222.32, 5886.9, 1244.25, 5857.25}}, +{'c', {1244.25, 5857.24, 1222.32, 5831.13, 1226.25, 5788.25}}, +{'l', {1308.25, 5745.25}}, +{'v', {1329.57, 5590.89, 1445.25, 5535.25}}, +{'c', {1497.25, 5510.15, 1531.2, 5582.31, 1552.25, 5646.25}}, +{'v', {1518.33, 5874.03, 1544.25, 5921.25}}, +{'v', {1659.9, 6028.47, 1655.25, 6067.25}}, +{'v', {1651.32, 6260.13, 1638.25, 6268.25}}, +{'f', {}}, +{'r', {0.953, 0.725, 0.576}}, +{'m', {1633.25, 6258.25}}, +{'c', {1622.72, 6267.94, 1541.54, 6328.88, 1479.25, 6262.25}}, +{'v', {1370.16, 6073.9, 1378.25, 6007.25}}, +{'l', {1378.25, 5986.25}}, +{'v', {1299.1, 5990.3, 1282.25, 5969.25}}, +{'c', {1282.24, 5969.24, 1269.84, 5915.06, 1257.25, 5911.25}}, +{'v', {1228.04, 5885.8, 1249.25, 5856.25}}, +{'c', {1249.24, 5856.25, 1228.04, 5831.46, 1232.25, 5789.25}}, +{'l', {1311.25, 5748.25}}, +{'v', {1332.54, 5597.38, 1445.25, 5543.25}}, +{'c', {1495.93, 5518.71, 1529, 5589.02, 1550.25, 5651.25}}, +{'v', {1516.46, 5873.26, 1541.25, 5919.25}}, +{'v', {1654.4, 6023.74, 1650.25, 6061.25}}, +{'c', {1650.24, 6061.25, 1646.04, 6249.46, 1633.25, 6258.25}}, +{'f', {}}, +{'r', {0.976, 0.863, 0.788}}, +{'m', {1628.25, 6247.25}}, +{'c', {1618.98, 6257.71, 1539.01, 6316.12, 1478.25, 6251.25}}, +{'v', {1372.14, 6067.85, 1380.25, 6002.25}}, +{'l', {1380.25, 5982.25}}, +{'c', {1380.25, 5982.24, 1302.95, 5986.45, 1286.25, 5966.25}}, +{'v', {1274.46, 5913.19, 1262.25, 5909.25}}, +{'v', {1233.76, 5884.7, 1254.25, 5856.25}}, +{'c', {1254.24, 5856.25, 1233.76, 5831.79, 1238.25, 5791.25}}, +{'l', {1315.25, 5750.25}}, +{'c', {1315.24, 5750.25, 1335.51, 5603.87, 1445.25, 5551.25}}, +{'c', {1494.6, 5527.27, 1526.8, 5595.73, 1547.25, 5657.25}}, +{'v', {1514.59, 5872.49, 1539.25, 5917.25}}, +{'v', {1648.9, 6019.01, 1645.25, 6055.25}}, +{'c', {1645.24, 6055.24, 1640.76, 6238.79, 1628.25, 6247.25}}, +{'f', {}}, +{'g', {1}}, +{'m', {1544.25, 5663.25}}, +{'c', {1544.25, 5663.24, 1512.72, 5871.72, 1536.25, 5915.25}}, +{'v', {1643.4, 6014.28, 1639.25, 6050.25}}, +{'c', {1639.24, 6050.25, 1635.48, 6228.12, 1623.25, 6236.25}}, +{'c', {1615.24, 6247.48, 1536.48, 6303.36, 1477.25, 6240.25}}, +{'v', {1374.12, 6061.8, 1382.25, 5998.25}}, +{'l', {1382.25, 5978.25}}, +{'c', {1382.25, 5978.24, 1306.8, 5982.6, 1291.25, 5963.25}}, +{'v', {1279.08, 5911.32, 1267.25, 5907.25}}, +{'v', {1239.48, 5883.6, 1259.25, 5856.25}}, +{'v', {1239.48, 5832.12, 1243.25, 5792.25}}, +{'l', {1318.25, 5753.25}}, +{'v', {1338.48, 5610.36, 1445.25, 5559.25}}, +{'c', {1493.27, 5535.83, 1524.6, 5603.54, 1544.25, 5663.25}}, +{'f', {}}, +{'g', {0.8}}, +{'m', {1510.25, 5732.25}}, +{'v', {1272.7, 5844, 1263.25, 5853.25}}, +{'v', {1362.9, 5762.6, 1371.25, 5762.25}}, +{'y', {1380.5, 5762.6, 1510.25, 5732.25}}, +{'f', {}}, +{'g', {0}}, +{'m', {1322.25, 5800.25}}, +{'v', {1524.6, 5760.4, 1524.25, 5712.25}}, +{'c', {1524.6, 5679.96, 1521.93, 5532.36, 1463.25, 5549.25}}, +{'c', {1370.6, 5575.6, 1410.2, 5734, 1322.25, 5800.25}}, +{'f', {}}, +{'r', {0.6, 0.8, 0.2}}, +{'m', {1414.25, 5758.25}}, +{'c', {1414.24, 5758.24, 1513.36, 5741.98, 1524.25, 5712.25}}, +{'c', {1531.2, 5694.4, 1538.49, 5603.01, 1478.25, 5591.25}}, +{'c', {1428.32, 5580.98, 1403.8, 5693.41, 1414.25, 5758.25}}, +{'f', {}}, +{'g', {0}}, +{'m', {2360.25, 5290.25}}, +{'c', {2355.07, 5309.32, 2369.5, 5307.88, 2389.25, 5314.25}}, +{'c', {2411.2, 5320.4, 2545.4, 5362.2, 2554.25, 5391.25}}, +{'y', {2563, 5419.4, 2708.25, 5371.25}}, +{'y', {2728, 5362.2, 2776.25, 5333.25}}, +{'y', {2829.2, 5320.4, 2902.25, 5316.25}}, +{'y', {2928.2, 5305, 2965.25, 5274.25}}, +{'y', {3126.2, 5162, 3262.25, 5241.25}}, +{'y', {3482.61, 5313.8, 3416.25, 5503.25}}, +{'y', {3383.61, 5602, 3419.25, 5639.25}}, +{'y', {3421.01, 5681.2, 3500.25, 5611.25}}, +{'y', {3528.81, 5564.6, 3537.25, 5509.25}}, +{'y', {3625.61, 5386.4, 3588.25, 5582.25}}, +{'c', {3590.41, 5593.2, 3559.61, 5632.8, 3559.25, 5646.25}}, +{'y', {3559.61, 5659.2, 3540.25, 5696.25}}, +{'y', {3506.81, 5734, 3533.25, 5811.25}}, +{'y', {3553.01, 5962.8, 3529.25, 5943.25}}, +{'y', {3515.61, 5962.8, 3414.25, 5853.25}}, +{'y', {3390.21, 5815.4, 3324.25, 5798.25}}, +{'y', {3293.41, 5778, 3256.25, 5793.25}}, +{'y', {3227.41, 5797.8, 3166.25, 5721.25}}, +{'c', {3196.61, 5723, 3223.01, 5674.6, 3249.25, 5672.25}}, +{'c', {3275.81, 5670.2, 3295.61, 5698.8, 3313.25, 5705.25}}, +{'y', {3330.81, 5712, 3361.25, 5648.25}}, +{'y', {3366.01, 5619.6, 3304.25, 5567.25}}, +{'y', {3300.01, 5516.2, 3282.25, 5534.25}}, +{'c', {3249.41, 5540.4, 3236.21, 5498.6, 3225.25, 5448.25}}, +{'y', {3214.21, 5397.4, 3168.25, 5393.25}}, +{'y', {3150.41, 5311.6, 3137.25, 5344.25}}, +{'y', {3135, 5406.2, 3069.25, 5342.25}}, +{'y', {3055.8, 5320.4, 3005.25, 5344.25}}, +{'y', {2930.4, 5366.6, 2957.25, 5388.25}}, +{'y', {2976.6, 5412.8, 3100.25, 5388.25}}, +{'y', {3124, 5406.2, 3036.25, 5450.25}}, +{'y', {3029.4, 5470, 3040.25, 5518.25}}, +{'y', {3053.6, 5553.6, 3128.25, 5615.25}}, +{'y', {3231.81, 5628.4, 3201.25, 5646.25}}, +{'y', {3132.81, 5703.2, 3069.25, 5619.25}}, +{'y', {3044.8, 5551.4, 2853.25, 5386.25}}, +{'c', {2800.6, 5349, 2829.2, 5423.8, 2785.25, 5386.25}}, +{'y', {2741.2, 5349, 2514.25, 5448.25}}, +{'c', {2387.41, 5461.12, 2357.36, 5287.96, 2318.25, 5322.25}}, +{'c', {2318.25, 5322.24, 2378.14, 5228.56, 2360.25, 5290.25}}, +{'f', {}}, +{'m', {4503.25, 7120.25}}, +{'v', {4221.81, 7032, 4191.25, 6825.25}}, +{'c', {4191.25, 6825.24, 4164.61, 6574.4, 4389.25, 6381.25}}, +{'v', {4393.41, 6310.4, 4415.25, 6275.25}}, +{'c', {4415.25, 6275.24, 4397.81, 6222.4, 4604.25, 6306.25}}, +{'l', {4904.25, 6398.25}}, +{'c', {4904.25, 6398.24, 4974.21, 6424.8, 5031.25, 6521.25}}, +{'c', {5088.61, 6618.4, 5255.81, 6825.2, 5216.25, 7102.25}}, +{'c', {5216.24, 7102.24, 5229.41, 7225.6, 5163.25, 7230.25}}, +{'c', {5163.24, 7230.25, 5071.01, 7247.6, 4992.25, 7164.25}}, +{'v', {4917.01, 7128.8, 4890.25, 7133.25}}, +{'l', {4503.25, 7120.25}}, +{'f', {}}, +{'m', {5104.25, 7131.25}}, +{'v', {5127.34, 7229.12, 5074.25, 7176.25}}, +{'v', {4996.66, 7112.96, 4914.25, 7113.25}}, +{'c', {4914.25, 7113.24, 4754.65, 7088.76, 4706.25, 6943.25}}, +{'v', {4662.69, 6648.32, 4750.25, 6585.25}}, +{'c', {4750.25, 6585.24, 4803.05, 6503.12, 4880.25, 6575.25}}, +{'c', {4957.94, 6648.32, 5128.44, 6980.85, 5104.25, 7131.25}}, +{'f', {}}, +{'g', {0.2}}, +{'m', {5100.25, 7127.25}}, +{'v', {5123.12, 7223.13, 5071.25, 7171.25}}, +{'v', {4994.81, 7109.09, 4914.25, 7109.25}}, +{'v', {4757.2, 7085.33, 4709.25, 6943.25}}, +{'v', {4666.91, 6652.89, 4752.25, 6591.25}}, +{'v', {4804.72, 6510.33, 4881.25, 6581.25}}, +{'c', {4956.8, 6652.89, 5123.89, 6979.64, 5100.25, 7127.25}}, +{'f', {}}, +{'g', {0.4}}, +{'m', {5096.25, 7123.25}}, +{'v', {5118.89, 7217.15, 5067.25, 7166.25}}, +{'c', {5067.25, 7166.24, 4992.96, 7105.21, 4913.25, 7105.25}}, +{'v', {4759.75, 7081.89, 4713.25, 6942.25}}, +{'v', {4671.14, 6657.47, 4755.25, 6597.25}}, +{'v', {4806.39, 6517.55, 4881.25, 6587.25}}, +{'c', {4955.65, 6657.47, 5119.33, 6978.43, 5096.25, 7123.25}}, +{'f', {}}, +{'g', {0.6}}, +{'m', {5092.25, 7119.25}}, +{'v', {5114.67, 7211.17, 5064.25, 7161.25}}, +{'c', {5064.24, 7161.25, 4991.12, 7101.34, 4913.25, 7101.25}}, +{'v', {4762.3, 7078.46, 4716.25, 6941.25}}, +{'v', {4675.36, 6662.05, 4757.25, 6602.25}}, +{'v', {4808.07, 6524.77, 4881.25, 6593.25}}, +{'c', {4954.51, 6662.05, 5114.78, 6977.22, 5092.25, 7119.25}}, +{'f', {}}, +{'g', {0.8}}, +{'m', {5088.25, 7115.25}}, +{'c', {5088.24, 7115.24, 5110.45, 7205.18, 5061.25, 7156.25}}, +{'v', {4989.26, 7097.47, 4913.25, 7097.25}}, +{'v', {4764.86, 7075.03, 4720.25, 6940.25}}, +{'v', {4679.59, 6666.62, 4760.25, 6608.25}}, +{'v', {4809.74, 6531.98, 4881.25, 6599.25}}, +{'c', {4953.35, 6666.62, 5110.22, 6976.01, 5088.25, 7115.25}}, +{'f', {}}, +{'g', {1}}, +{'m', {5083.25, 7111.25}}, +{'c', {5083.25, 7111.24, 5106.21, 7199.2, 5058.25, 7151.25}}, +{'v', {4987.41, 7093.6, 4912.25, 7093.25}}, +{'c', {4912.24, 7093.24, 4767.41, 7071.6, 4723.25, 6939.25}}, +{'c', {4723.24, 6939.24, 4683.81, 6671.2, 4763.25, 6614.25}}, +{'v', {4811.41, 6539.2, 4882.25, 6605.25}}, +{'c', {4952.21, 6671.2, 5105.66, 6974.8, 5083.25, 7111.25}}, +{'f', {}}, +{'r', {0.6, 0.149, 0}}, +{'m', {2756.25, 4876.25}}, +{'v', {2532.2, 5087.2, 2444.25, 5096.25}}, +{'v', {2065.8, 5140, 1903.25, 4942.25}}, +{'v', {2096.6, 5166.4, 2400.25, 5105.25}}, +{'v', {2162.6, 5153.2, 2026.25, 5118.25}}, +{'v', {1841.4, 5118, 1736.25, 4964.25}}, +{'l', {1705.25, 4911.25}}, +{'c', {1705.24, 4911.24, 1749, 5074, 1951.25, 5140.25}}, +{'v', {2202.2, 5192.8, 2321.25, 5140.25}}, +{'v', {2083.4, 5214.8, 1973.25, 5193.25}}, +{'v', {1639, 5219.2, 1498.25, 4929.25}}, +{'v', {1542.2, 5087.2, 1705.25, 5166.25}}, +{'c', {1705.24, 5166.24, 1854.6, 5263.2, 2079.25, 5232.25}}, +{'c', {2079.25, 5232.24, 2237.4, 5197.2, 2294.25, 5171.25}}, +{'c', {2351.8, 5144.4, 2338.6, 5175.2, 2246.25, 5228.25}}, +{'v', {2184.6, 5338, 2030.25, 5333.25}}, +{'c', {2030.25, 5333.24, 1559.8, 5294, 1445.25, 5162.25}}, +{'v', {1595, 5285.2, 1709.25, 5316.25}}, +{'v', {1955.8, 5404, 2048.25, 5395.25}}, +{'c', {2048.25, 5395.24, 2321, 5384.2, 2404.25, 5428.25}}, +{'c', {2404.25, 5428.24, 2281.4, 5373.2, 2316.25, 5338.25}}, +{'c', {2351.8, 5302.8, 2426.6, 5219.2, 2426.25, 5206.25}}, +{'c', {2426.6, 5192.8, 2692.8, 4949.7, 2732.25, 4901.25}}, +{'l', {2756.25, 4876.25}}, +{'f', {}}, +{'g', {0.8}}, +{'m', {4279.25, 2742.25}}, +{'v', {4108.5, 3143.5, 3971.25, 3248.25}}, +{'v', {4257, 3072, 4295.25, 2874.25}}, +{'v', {4295.5, 2764, 4279.25, 2742.25}}, +{'f', {}}, +{'m', {4796.25, 2659.25}}, +{'v', {4504.5, 3264.5, 4301.25, 3528.25}}, +{'v', {4779.5, 3116, 4834.25, 2824.25}}, +{'l', {4840.25, 2764.25}}, +{'l', {4807.25, 2791.25}}, +{'v', {4801.5, 2692.5, 4796.25, 2659.25}}, +{'f', {}}, +{'m', {5417.25, 3193.25}}, +{'v', {4730, 3847.5, 4713.25, 3875.25}}, +{'v', {5379, 3149, 5412.25, 3066.25}}, +{'v', {5390, 3165.5, 5417.25, 3193.25}}, +{'f', {}}, +{'m', {3344.25, 2714.25}}, +{'v', {3558.5, 3286.5, 3767.25, 3039.25}}, +{'v', {3932.5, 2929, 3927.25, 2896.25}}, +{'v', {3883, 2967.5, 3685.25, 2962.25}}, +{'v', {3476, 2995, 3344.25, 2714.25}}, +{'f', {}}, +{'m', {5439.25, 4117.25}}, +{'v', {4944.5, 4430.5, 4862.25, 4447.25}}, +{'c', {4732.12, 4472.97, 5406.5, 4128, 5461.25, 4012.25}}, +{'v', {5483.5, 4040, 5439.25, 4117.25}}, +{'f', {}}, +{'g', {0}}, +{'m', {3782.25, 2944.25}}, +{'c', {3782.25, 2944.24, 4006.21, 2966.39, 4081.25, 3041.25}}, +{'l', {4129.25, 3001.25}}, +{'l', {4323.25, 3424.25}}, +{'l', {4362.25, 3367.25}}, +{'v', {4521.01, 3529.59, 4512.25, 3617.25}}, +{'y', {4503.41, 3705.59, 4653.25, 3551.25}}, +{'c', {4653.25, 3551.24, 4644.21, 3679.19, 4723.25, 3604.25}}, +{'c', {4723.24, 3604.24, 4697.01, 3775.99, 4789.25, 3688.25}}, +{'c', {4789.24, 3688.25, 4673.5, 4019.71, 4921.25, 3736.25}}, +{'y', {4983.01, 3665.99, 4934.25, 3741.25}}, +{'c', {4934.24, 3741.25, 4648.61, 4268.79, 4886.25, 4110.25}}, +{'c', {4886.24, 4110.24, 4908.21, 4361.19, 4895.25, 4409.25}}, +{'c', {4881.81, 4457.99, 4859.81, 4704.4, 4807.25, 4761.25}}, +{'c', {4754.21, 4818.8, 4811.41, 4836.4, 4873.25, 4779.25}}, +{'c', {4873.25, 4779.24, 4749.81, 5043.2, 4895.25, 4911.25}}, +{'c', {4895.25, 4911.24, 4855.41, 5078.4, 4807.25, 5109.25}}, +{'c', {4807.25, 5109.24, 4745.41, 5298.4, 4912.25, 5179.25}}, +{'c', {4912.24, 5179.24, 4864.21, 5316, 4829.25, 5351.25}}, +{'c', {4829.25, 5351.24, 4701.41, 5654.8, 4780.25, 5602.25}}, +{'l', {4829.25, 5562.25}}, +{'c', {4829.25, 5562.24, 4754.21, 5716.4, 4824.25, 5668.25}}, +{'y', {4895.01, 5619.6, 4895.25, 5624.25}}, +{'v', {4661.81, 5989.2, 4886.25, 5795.25}}, +{'c', {4886.24, 5795.24, 4796.46, 5948.7, 4758.25, 6024.25}}, +{'c', {4758.24, 6024.24, 4551.81, 6248.8, 4710.25, 6178.25}}, +{'l', {4763.25, 6161.25}}, +{'v', {4666.21, 6270.8, 4578.25, 6288.25}}, +{'c', {4490.21, 6306, 4604.61, 6376.4, 4675.25, 6354.25}}, +{'y', {4745.41, 6332.4, 4917.25, 6249.25}}, +{'v', {5057.81, 6042, 5102.25, 6037.25}}, +{'c', {5102.25, 6037.24, 4881.81, 6121.2, 4948.25, 6033.25}}, +{'c', {4948.25, 6033.24, 5106.21, 5879.2, 5027.25, 5883.25}}, +{'c', {5027.25, 5883.24, 4961.01, 5804.4, 5014.25, 5707.25}}, +{'c', {5014.25, 5707.24, 4810.76, 5909.9, 4974.25, 5628.25}}, +{'l', {5049.25, 5448.25}}, +{'v', {4780.61, 5720.8, 4904.25, 5479.25}}, +{'v', {5093.01, 5219.2, 5115.25, 5215.25}}, +{'y', {5137.01, 5210.4, 5185.25, 5113.25}}, +{'l', {5137.25, 5135.25}}, +{'l', {5194.25, 5039.25}}, +{'c', {5194.24, 5039.25, 5071.01, 5170.8, 5137.25, 5025.25}}, +{'l', {5198.25, 4867.25}}, +{'c', {5198.24, 4867.24, 4974.21, 5109.2, 5124.25, 4783.25}}, +{'c', {5124.25, 4783.24, 4943.41, 4840.8, 5040.25, 4651.25}}, +{'c', {5040.24, 4651.24, 5022.61, 4475.59, 5027.25, 4418.25}}, +{'c', {5031.41, 4361.19, 5044.61, 4048.79, 4996.25, 3961.25}}, +{'c', {4947.81, 3872.79, 5062.21, 3661.59, 5084.25, 3617.25}}, +{'c', {5106.21, 3573.59, 5145.81, 3454.79, 5049.25, 3556.25}}, +{'c', {4952.21, 3657.19, 5000.61, 3595.59, 5022.25, 3499.25}}, +{'c', {5044.61, 3401.99, 5110.61, 3230.39, 5102.25, 3169.25}}, +{'v', {5088.61, 3155.59, 5053.25, 3195.25}}, +{'c', {5053.24, 3195.24, 4890.61, 3445.99, 4908.25, 3287.25}}, +{'c', {4908.24, 3287.24, 4895.01, 3199.59, 4860.25, 3103.25}}, +{'v', {4824.61, 2983.99, 4824.25, 3081.25}}, +{'c', {4824.24, 3081.25, 4789.41, 3265.59, 4758.25, 3182.25}}, +{'c', {4727.81, 3098.39, 4688.21, 3032.39, 4657.25, 3006.25}}, +{'c', {4626.61, 2979.59, 4569.41, 3230.39, 4556.25, 3116.25}}, +{'c', {4556.24, 3116.25, 4424.21, 3252.39, 4371.25, 3072.25}}, +{'l', {4244.25, 2891.25}}, +{'c', {4244.25, 2891.24, 4239.41, 3027.99, 4226.25, 2962.25}}, +{'v', {3896.21, 2895.99, 3782.25, 2944.25}}, +{'f', {}}, +{'m', {3403.25, 6869.25}}, +{'c', {3403.25, 6869.24, 3275.81, 6957.2, 3232.25, 6953.25}}, +{'c', {3187.81, 6948.4, 3535.41, 7049.6, 3988.25, 6746.25}}, +{'v', {4041.41, 6715.2, 4081.25, 6719.25}}, +{'c', {4081.25, 6719.24, 4116.21, 6693.2, 4085.25, 6653.25}}, +{'c', {4085.25, 6653.24, 3988.61, 6548, 4112.25, 6425.25}}, +{'v', {4314.21, 6350, 4252.25, 6447.25}}, +{'v', {4371.41, 6402.8, 4398.25, 6359.25}}, +{'y', {4424.21, 6314.8, 4411.25, 6359.25}}, +{'v', {4340.61, 6438, 4274.25, 6495.25}}, +{'c', {4274.25, 6495.24, 4217.41, 6517.2, 4186.25, 6609.25}}, +{'c', {4155.81, 6702, 4129.41, 6812, 4178.25, 6847.25}}, +{'c', {4178.25, 6847.24, 4133.81, 6798.8, 4142.25, 6843.25}}, +{'c', {4151.41, 6886.8, 4191.01, 6926.4, 4208.25, 6931.25}}, +{'c', {4226.21, 6935.2, 4406.61, 7109, 4481.25, 7113.25}}, +{'c', {4481.25, 7113.24, 4380.21, 7098, 4347.25, 7109.25}}, +{'c', {4314.21, 7120, 4021.61, 7245.4, 3955.25, 7258.25}}, +{'c', {3955.25, 7258.24, 3770.81, 7331.2, 3903.25, 7309.25}}, +{'c', {3903.25, 7309.24, 4296.61, 7267.4, 4497.25, 7122.25}}, +{'c', {4497.25, 7122.24, 4417.61, 7214.6, 4215.25, 7291.25}}, +{'c', {4215.25, 7291.24, 3971.01, 7430.2, 3584.25, 7375.25}}, +{'c', {3584.25, 7375.24, 3388.01, 7340, 3302.25, 7320.25}}, +{'c', {3302.25, 7320.24, 3273.61, 7326.8, 3267.25, 7331.25}}, +{'c', {3260.41, 7335.6, 3130.6, 7434.6, 2827.25, 7357.25}}, +{'c', {2827.25, 7357.24, 2640, 7307, 2545.25, 7254.25}}, +{'c', {2545.25, 7254.24, 2378.2, 7241, 2338.25, 7206.25}}, +{'v', {2134, 7045.2, 2112.25, 7036.25}}, +{'c', {2090, 7027.6, 1964.6, 6944, 1956.25, 6939.25}}, +{'c', {1956.25, 6939.24, 2226.4, 7012.2, 2253.25, 7038.25}}, +{'c', {2279.2, 7065, 2470.6, 7093.6, 2497.25, 7078.25}}, +{'c', {2523.4, 7062.8, 2615.8, 7069.4, 2510.25, 7063.25}}, +{'v', {3341.81, 6900, 3350.25, 6878.25}}, +{'y', {3359.41, 6856, 3403.25, 6869.25}}, +{'f', {}}, +{'r', {0.8, 0.451, 0.149}}, +{'m', {4189.25, 6970.25}}, +{'c', {4189.25, 6970.24, 4076.61, 7051.8, 4054.25, 7052.25}}, +{'c', {4032.61, 7051.8, 3896.21, 7164, 3850.25, 7159.25}}, +{'c', {3803.81, 7155.2, 3669.61, 7265.2, 3368.25, 7175.25}}, +{'v', {3361.61, 7197, 3401.25, 7206.25}}, +{'v', {3471.61, 7230, 3476.25, 7236.25}}, +{'c', {3476.25, 7236.24, 3698.21, 7282.8, 3777.25, 7243.25}}, +{'c', {3777.25, 7243.24, 3878.61, 7214.6, 3947.25, 7146.25}}, +{'c', {3947.25, 7146.24, 4070.01, 7111.2, 4105.25, 7122.25}}, +{'c', {4105.25, 7122.24, 4202.01, 7098, 4206.25, 7078.25}}, +{'c', {4206.25, 7078.24, 4270.21, 7045.2, 4250.25, 7016.25}}, +{'c', {4250.25, 7016.24, 4254.81, 6999, 4189.25, 6970.25}}, +{'f', {}}, +{'m', {4051.25, 6993.25}}, +{'c', {4060.5, 6986.64, 4071.71, 6985.57, 4078.25, 6976.25}}, +{'c', {4080.94, 6973.21, 4077.77, 6969.63, 4074.25, 6968.25}}, +{'c', {4062.82, 6965.07, 4051.21, 6971.34, 4038.25, 6965.25}}, +{'c', {4034.23, 6962.6, 4027.16, 6964.6, 4021.25, 6966.25}}, +{'c', {4003.13, 6971.09, 3983.01, 6971.37, 3964.25, 6964.25}}, +{'c', {3942.57, 6976.22, 3916.61, 6969.79, 3893.25, 6981.25}}, +{'c', {3893.09, 6981.1, 3890.64, 6977.29, 3890.25, 6977.25}}, +{'c', {3856.32, 6990.19, 3815.15, 6987.07, 3788.25, 7012.25}}, +{'c', {3761.7, 7016.73, 3735.77, 7021.81, 3709.25, 7029.25}}, +{'c', {3689.07, 7034.89, 3673.57, 7045.77, 3656.25, 7055.25}}, +{'c', {3640.6, 7062.71, 3624.57, 7068.45, 3607.25, 7072.25}}, +{'c', {3587.21, 7077.52, 3567.07, 7076.29, 3546.25, 7082.25}}, +{'c', {3545.22, 7082.28, 3543.11, 7078.5, 3542.25, 7079.25}}, +{'c', {3538.64, 7079.98, 3535.45, 7086.39, 3533.25, 7086.25}}, +{'c', {3515.08, 7080.07, 3498.71, 7090.76, 3480.25, 7087.25}}, +{'c', {3467.45, 7100.46, 3448.44, 7097.77, 3431.25, 7102.25}}, +{'c', {3398.83, 7111.96, 3364.14, 7097.99, 3331.25, 7109.25}}, +{'c', {3376.05, 7129.26, 3427.61, 7115.71, 3472.25, 7138.25}}, +{'c', {3497.9, 7151.51, 3527.18, 7139.39, 3555.25, 7148.25}}, +{'c', {3560.81, 7149.34, 3568.41, 7151.56, 3573.25, 7144.25}}, +{'c', {3574.28, 7145.68, 3576.29, 7148.31, 3577.25, 7148.25}}, +{'c', {3603.88, 7135.28, 3629.34, 7121.26, 3656.25, 7109.25}}, +{'c', {3660.39, 7107.93, 3666.24, 7110.48, 3669.25, 7108.25}}, +{'c', {3685.73, 7094.89, 3707.11, 7096.02, 3722.25, 7082.25}}, +{'c', {3741.09, 7088.12, 3760.81, 7083.94, 3780.25, 7091.25}}, +{'c', {3780.68, 7091.1, 3783.25, 7087.35, 3783.25, 7087.25}}, +{'c', {3796.04, 7095.82, 3808.75, 7092.84, 3818.25, 7089.25}}, +{'c', {3822.28, 7088.23, 3829.57, 7085.51, 3833.25, 7084.25}}, +{'c', {3845.31, 7081.66, 3854.73, 7076.38, 3867.25, 7074.25}}, +{'c', {3868.95, 7074.17, 3870.98, 7077.91, 3872.25, 7077.25}}, +{'c', {3884.13, 7072.79, 3895.59, 7073.4, 3903.25, 7060.25}}, +{'c', {3904.29, 7062.08, 3906.11, 7064.7, 3907.25, 7064.25}}, +{'c', {3918.31, 7060.69, 3925.45, 7052.38, 3937.25, 7049.25}}, +{'c', {3942.89, 7048.56, 3949.62, 7041.68, 3956.25, 7039.25}}, +{'c', {3982.27, 7031.7, 4002.55, 7014.88, 4026.25, 7005.25}}, +{'c', {4035.03, 7001.63, 4044.66, 6999.05, 4051.25, 6993.25}}, +{'f', {}}, +{'m', {3208.25, 7150.25}}, +{'c', {3180.96, 7169.1, 3154.91, 7181.27, 3128.25, 7201.25}}, +{'c', {3126.05, 7202.38, 3122.09, 7200.51, 3120.25, 7202.25}}, +{'c', {3108.8, 7208.55, 3099.03, 7214.94, 3088.25, 7222.25}}, +{'c', {3082.3, 7226.8, 3073.27, 7226.68, 3067.25, 7229.25}}, +{'c', {3039.7, 7243.73, 3010.77, 7249.86, 2983.25, 7263.25}}, +{'c', {2990.7, 7270.02, 3003.04, 7267.33, 3009.25, 7276.25}}, +{'c', {3011.74, 7273.12, 3014.36, 7269.99, 3018.25, 7272.25}}, +{'c', {3036.7, 7282.04, 3057.05, 7283.72, 3075.25, 7282.25}}, +{'c', {3094.23, 7281.72, 3113.15, 7278.36, 3132.25, 7275.25}}, +{'c', {3136.05, 7274.86, 3138.24, 7269.1, 3142.25, 7268.25}}, +{'c', {3166.3, 7260.74, 3192.58, 7266.52, 3216.25, 7257.25}}, +{'c', {3233.39, 7250.38, 3250.66, 7241.33, 3264.25, 7227.25}}, +{'c', {3267.06, 7224.44, 3263.49, 7221.13, 3260.25, 7219.25}}, +{'c', {3264.68, 7220.2, 3267.68, 7217.93, 3269.25, 7214.25}}, +{'c', {3269.82, 7211.86, 3269.82, 7208.54, 3269.25, 7206.25}}, +{'c', {3267.67, 7202.48, 3264.6, 7201.36, 3260.25, 7201.25}}, +{'c', {3245.01, 7198.71, 3264.5, 7213.89, 3257.25, 7209.25}}, +{'c', {3242.8, 7200.01, 3251, 7184.55, 3243.25, 7170.25}}, +{'c', {3239.73, 7172.73, 3237.2, 7175.13, 3238.25, 7179.25}}, +{'c', {3240.98, 7173.66, 3234.36, 7170.46, 3232.25, 7167.25}}, +{'c', {3228.28, 7159.02, 3218.39, 7143.45, 3208.25, 7150.25}}, +{'f', {}}, +{'m', {2851.25, 7069.25}}, +{'c', {2816.48, 7078.03, 2782.93, 7076.77, 2750.25, 7091.25}}, +{'c', {2749.02, 7091.11, 2746.57, 7087.29, 2746.25, 7087.25}}, +{'c', {2730.84, 7094.12, 2720.74, 7104.75, 2709.25, 7116.25}}, +{'c', {2698.86, 7126.03, 2680.62, 7121.77, 2666.25, 7127.25}}, +{'c', {2663.04, 7128.49, 2660.81, 7134.12, 2657.25, 7134.25}}, +{'c', {2644.09, 7136.28, 2633.89, 7146.72, 2622.25, 7153.25}}, +{'c', {2648.09, 7161.74, 2674.61, 7161.4, 2701.25, 7165.25}}, +{'c', {2702.94, 7165.82, 2704.54, 7162.29, 2706.25, 7162.25}}, +{'c', {2707.49, 7162.29, 2708.93, 7164.72, 2710.25, 7166.25}}, +{'c', {2712.54, 7163.12, 2715.86, 7159.53, 2718.25, 7162.25}}, +{'c', {2724.72, 7168.27, 2730.84, 7166.18, 2737.25, 7165.25}}, +{'c', {2738.36, 7165.62, 2739.73, 7162.29, 2741.25, 7162.25}}, +{'c', {2742.7, 7162.29, 2744.14, 7165.71, 2745.25, 7165.25}}, +{'c', {2747.09, 7165.7, 2748.54, 7162.29, 2750.25, 7162.25}}, +{'c', {2751.49, 7162.29, 2752.93, 7164.72, 2754.25, 7166.25}}, +{'c', {2762.01, 7157.6, 2771.75, 7163.69, 2781.25, 7161.25}}, +{'c', {2792.21, 7159.22, 2795.14, 7146.6, 2807.25, 7143.25}}, +{'c', {2858.82, 7128.97, 2904.33, 7105.38, 2952.25, 7082.25}}, +{'c', {2955.7, 7080.8, 2958.01, 7078.08, 2957.25, 7074.25}}, +{'c', {2959.73, 7073.8, 2963.18, 7074.77, 2965.25, 7073.25}}, +{'c', {2976.91, 7065.26, 2988.36, 7059.23, 2996.25, 7047.25}}, +{'c', {2998.4, 7043.43, 2994.78, 7038.95, 2992.25, 7039.25}}, +{'c', {2943.5, 7050.47, 2899.43, 7057.16, 2851.25, 7069.25}}, +{'f', {}}, +{'m', {2698.25, 6583.25}}, +{'c', {2681.2, 6596.38, 2674.78, 6618.72, 2662.25, 6638.25}}, +{'c', {2660.02, 6642.17, 2663.03, 6645.62, 2666.25, 6646.25}}, +{'c', {2672.68, 6648.35, 2678.65, 6643.09, 2683.25, 6641.25}}, +{'c', {2702.35, 6631.39, 2719.23, 6617.73, 2741.25, 6616.25}}, +{'c', {2763.09, 6591.62, 2809.95, 6587.39, 2810.25, 6550.25}}, +{'c', {2810.04, 6540.75, 2794.33, 6550.85, 2789.25, 6541.25}}, +{'c', {2762.64, 6552.43, 2736.36, 6551.3, 2710.25, 6567.25}}, +{'c', {2703.39, 6571.62, 2707.02, 6576.61, 2698.25, 6583.25}}, +{'f', {}}, +{'m', {2396.25, 7161.25}}, +{'c', {2397.28, 7161.3, 2474.62, 7158.74, 2474.25, 7157.25}}, +{'c', {2473.94, 7154.09, 2388.91, 7142.55, 2385.25, 7144.25}}, +{'c', {2383.11, 7145.25, 2300.47, 7118.52, 2299.25, 7120.25}}, +{'c', {2301.95, 7121.48, 2392.87, 7161.31, 2396.25, 7161.25}}, +{'f', {}}, +{'g', {0}}, +{'m', {2565.25, 7054.25}}, +{'v', {2402.4, 7034.2, 2354.25, 7021.25}}, +{'c', {2305.6, 7007.8, 2101, 6924.2, 2068.25, 6902.25}}, +{'c', {2068.25, 6902.24, 1920.6, 6842.8, 1733.25, 6623.25}}, +{'v', {1817.2, 6660.2, 1841.25, 6691.25}}, +{'v', {1991, 6829.6, 1989.25, 6801.25}}, +{'v', {2123, 6895.6, 2116.25, 6871.25}}, +{'c', {2116.25, 6871.24, 2384.8, 6994.6, 2363.25, 6959.25}}, +{'c', {2363.25, 6959.24, 2600.4, 7010, 2589.25, 6988.25}}, +{'v', {2796.2, 6939.6, 2765.25, 6937.25}}, +{'c', {2765.25, 6937.24, 2701.6, 6924.2, 2772.25, 6884.25}}, +{'c', {2772.25, 6884.24, 2734.6, 6836.2, 2675.25, 6880.25}}, +{'c', {2615.8, 6924.2, 2648.8, 6900, 2594.25, 6889.25}}, +{'v', {2565.2, 6880.2, 2514.25, 6924.25}}, +{'c', {2514.25, 6924.24, 2453, 6974.8, 2356.25, 6935.25}}, +{'c', {2356.25, 6935.24, 2019.6, 6796.6, 1997.25, 6790.25}}, +{'v', {1958, 6759.2, 1931.25, 6719.25}}, +{'c', {1931.25, 6719.24, 1867.8, 6671.2, 1835.25, 6656.25}}, +{'v', {1694, 6528.2, 1681.25, 6513.25}}, +{'v', {1643.4, 6455.6, 1634.25, 6451.25}}, +{'c', {1634.24, 6451.24, 1705, 6493, 1727.25, 6515.25}}, +{'v', {1881, 6625, 1940.25, 6634.25}}, +{'v', {1988.8, 6666.8, 1997.25, 6682.25}}, +{'c', {1997.25, 6682.24, 2156, 6783.4, 2202.25, 6783.25}}, +{'c', {2202.25, 6783.24, 2303.4, 6726.2, 2330.25, 6803.25}}, +{'c', {2330.25, 6803.24, 2393.6, 6823, 2455.25, 6810.25}}, +{'v', {2490.4, 6838.4, 2481.25, 6862.25}}, +{'c', {2481.25, 6862.24, 2499.2, 6882.4, 2510.25, 6840.25}}, +{'c', {2510.25, 6840.24, 2547.6, 6801, 2600.25, 6823.25}}, +{'v', {2644.4, 6825.2, 2622.25, 6799.25}}, +{'v', {2574, 6757, 2444.25, 6755.25}}, +{'v', {2307.8, 6748.2, 2127.25, 6664.25}}, +{'c', {2127.25, 6664.24, 1799.6, 6550.2, 1698.25, 6436.25}}, +{'v', {1628, 6339, 1568.25, 6326.25}}, +{'c', {1568.24, 6326.25, 1504.8, 6317, 1439.25, 6235.25}}, +{'c', {1439.25, 6235.24, 1546.6, 6299.4, 1645.25, 6299.25}}, +{'c', {1645.24, 6299.24, 1689.6, 6325.8, 1648.25, 6286.25}}, +{'c', {1648.25, 6286.24, 1608.2, 6202.6, 1626.25, 6143.25}}, +{'c', {1626.25, 6143.24, 1619.2, 6086, 1610.25, 6068.25}}, +{'c', {1610.25, 6068.24, 1524.6, 5927.6, 1524.25, 5901.25}}, +{'c', {1524.6, 5874.8, 1537.8, 5767, 1542.25, 5760.25}}, +{'c', {1546.6, 5753.8, 1531.2, 5778, 1573.25, 5751.25}}, +{'c', {1614.8, 5725.2, 1645.6, 5707.6, 1654.25, 5677.25}}, +{'c', {1663.2, 5646, 1632.4, 5736.2, 1630.25, 5756.25}}, +{'c', {1628, 5775.8, 1581.8, 5855, 1590.25, 5881.25}}, +{'c', {1590.24, 5881.24, 1601.6, 5870.4, 1610.25, 5855.25}}, +{'v', {1603.8, 5861.6, 1610.25, 5901.25}}, +{'c', {1610.25, 5901.24, 1619.2, 5958.4, 1634.25, 5993.25}}, +{'c', {1650, 6028.8, 1672, 6070.6, 1676.25, 6079.25}}, +{'c', {1680.8, 6088.2, 1680.8, 6152, 1696.25, 6123.25}}, +{'l', {1733.25, 6095.25}}, +{'v', {1702.8, 6123.4, 1727.25, 6147.25}}, +{'c', {1727.25, 6147.24, 1716, 6209.2, 1736.25, 6238.25}}, +{'v', {1812.8, 6330.2, 1830.25, 6341.25}}, +{'y', {1848, 6352.2, 1832.25, 6348.25}}, +{'v', {1898.6, 6394, 1835.25, 6376.25}}, +{'c', {1835.25, 6376.24, 1790.8, 6358.8, 1758.25, 6359.25}}, +{'v', {1674.2, 6336.8, 1718.25, 6383.25}}, +{'c', {1762.2, 6429.2, 1872.2, 6488.6, 1914.25, 6486.25}}, +{'l', {1923.25, 6469.25}}, +{'l', {2046.25, 6495.25}}, +{'l', {2033.25, 6486.25}}, +{'c', {2033.25, 6486.24, 2030.6, 6488.6, 2077.25, 6493.25}}, +{'c', {2123, 6497.4, 2186.8, 6482, 2202.25, 6502.25}}, +{'c', {2217.6, 6521.6, 2255, 6532.6, 2250.25, 6517.25}}, +{'y', {2246.2, 6501.8, 2244.25, 6480.25}}, +{'v', {2299, 6543.6, 2292.25, 6519.25}}, +{'c', {2285.8, 6495.2, 2195.6, 6438, 2180.25, 6370.25}}, +{'l', {2294.25, 6460.25}}, +{'l', {2334.25, 6493.25}}, +{'v', {2373.8, 6468.8, 2376.25, 6486.25}}, +{'c', {2378.2, 6504, 2428.8, 6567.8, 2442.25, 6565.25}}, +{'c', {2455.2, 6563.4, 2477.2, 6594.2, 2475.25, 6565.25}}, +{'y', {2472.8, 6537, 2556.25, 6477.25}}, +{'c', {2556.25, 6477.24, 2591.6, 6497.4, 2607.25, 6482.25}}, +{'y', {2622.4, 6466.6, 2668.25, 6700.25}}, +{'l', {2943.25, 6816.25}}, +{'l', {3423.25, 6854.25}}, +{'l', {3236.25, 6928.25}}, +{'l', {2565.25, 7054.25}}, +{'f', {}}, +{'w', {22}}, +{'J', {1}}, +{'R', {0.298, 0, 0}}, +{'m', {2765.25, 4865.25}}, +{'v', {2600.4, 5049.8, 2508.25, 5078.25}}, +{'c', {2508.25, 5078.24, 2360.6, 5153.2, 2090.25, 5067.25}}, +{'S', {}}, +{'m', {2473.25, 5094.25}}, +{'v', {2195.6, 5181.8, 2026.25, 5135.25}}, +{'c', {2026.25, 5135.24, 1823.8, 5113.6, 1731.25, 4962.25}}, +{'S', {}}, +{'m', {2433.25, 5107.25}}, +{'v', {2246.2, 5186.2, 2083.25, 5210.25}}, +{'c', {2083.25, 5210.24, 1900.8, 5239, 1718.25, 5160.25}}, +{'v', {1584, 5093.8, 1524.25, 4981.25}}, +{'S', {}}, +{'m', {2444.25, 5102.25}}, +{'c', {2444.25, 5102.24, 2274.8, 5223.6, 2264.25, 5239.25}}, +{'v', {2186.8, 5360, 2044.25, 5364.25}}, +{'c', {2044.25, 5364.24, 1808.4, 5355.6, 1619.25, 5267.25}}, +{'S', {}}, +{'m', {2430.25, 5201.25}}, +{'y', {2446.8, 5185.43, 2743.25, 4867.25}}, +{'y', {3130.6, 4464.59, 2822.25, 4841.25}}, +{'y', {2739, 4893.6, 2638.25, 5100.25}}, +{'y', {2624.6, 5131.2, 2792.25, 5021.25}}, +{'y', {2835.8, 5012.4, 2985.25, 4801.25}}, +{'y', {2910.6, 4827.6, 2963.25, 4748.25}}, +{'y', {2994.2, 4726.4, 3218.25, 4555.25}}, +{'y', {3258.21, 4510.79, 3302.25, 4493.25}}, +{'y', {3456.21, 4550.39, 3386.25, 4405.25}}, +{'y', {3412.21, 4330.39, 3474.25, 4458.25}}, +{'y', {3597.01, 4642.8, 3416.25, 4616.25}}, +{'y', {3086.6, 4585.59, 3012.25, 4761.25}}, +{'y', {2985.4, 4788, 3082.25, 4761.25}}, +{'y', {3174.61, 4739.6, 3003.25, 4898.25}}, +{'y', {3029.4, 4898, 3130.25, 4823.25}}, +{'y', {3245.01, 4722, 3267.25, 4744.25}}, +{'y', {3465.01, 4840.8, 3579.25, 4757.25}}, +{'c', {3601.41, 4739.6, 3539.81, 4664.8, 3557.25, 4607.25}}, +{'y', {3575.01, 4550.39, 3628.25, 4414.25}}, +{'y', {3601.41, 4396.39, 3606.25, 4277.25}}, +{'y', {3790.61, 4022.39, 3685.25, 4044.25}}, +{'y', {3513.41, 4048.79, 3676.25, 3965.25}}, +{'y', {3711.41, 3943.19, 3808.25, 3864.25}}, +{'y', {3777.41, 3877.19, 3760.25, 3820.25}}, +{'y', {3812.61, 3775.99, 3782.25, 3723.25}}, +{'y', {3715.81, 3709.99, 3702.25, 3666.25}}, +{'y', {3777.41, 3577.99, 3667.25, 3573.25}}, +{'y', {3707.01, 3525.19, 3654.25, 3393.25}}, +{'y', {3601.41, 3393.19, 3531.25, 3331.25}}, +{'y', {3557.41, 3278.79, 3443.25, 3217.25}}, +{'y', {3350.61, 3199.59, 3381.25, 3125.25}}, +{'y', {3293.41, 3058.79, 3267.25, 2883.25}}, +{'c', {3258.21, 2768.39, 3231.81, 2733.19, 3289.25, 2755.25}}, +{'y', {3346.21, 2777.19, 3337.25, 2913.25}}, +{'y', {3284.61, 3085.19, 3755.25, 3261.25}}, +{'y', {3799.41, 3278.79, 3808.25, 3336.25}}, +{'y', {3830.21, 3331.59, 3927.25, 3248.25}}, +{'y', {4010.61, 3124.79, 4015.25, 3226.25}}, +{'y', {4028.21, 3265.59, 4010.25, 3331.25}}, +{'y', {4076.61, 3569.19, 3922.25, 3639.25}}, +{'y', {3812.61, 4009.19, 3966.25, 3917.25}}, +{'y', {3997.41, 3855.19, 4116.25, 3798.25}}, +{'l', {4156.25, 3824.25}}, +{'y', {4138.21, 3877.19, 4230.25, 3943.25}}, +{'y', {4261.41, 3872.79, 4327.25, 3961.25}}, +{'y', {4367.01, 4229.19, 4503.25, 4071.25}}, +{'y', {4547.41, 4057.59, 4560.25, 4132.25}}, +{'y', {4600.21, 4246.79, 4560.25, 4396.25}}, +{'y', {4600.21, 4400.79, 4706.25, 4335.25}}, +{'c', {4736.61, 4374.39, 4635.41, 4559.19, 4679.25, 4533.25}}, +{'y', {4723.41, 4506.39, 4772.25, 4489.25}}, +{'y', {4780.61, 4510.79, 4670.25, 4647.25}}, +{'y', {4622.21, 4678, 4565.25, 4902.25}}, +{'y', {4644.21, 4862.8, 4534.25, 5030.25}}, +{'y', {4534.21, 5065.2, 4600.25, 5188.25}}, +{'y', {4591.41, 5263.2, 4600.25, 5259.25}}, +{'c', {4631.01, 5245.6, 4719.01, 5228, 4644.25, 5298.25}}, +{'y', {4569.41, 5368.8, 4653.25, 5421.25}}, +{'y', {4701.41, 5452.4, 4552.25, 5448.25}}, +{'y', {4494.61, 5496.4, 4499.25, 5540.25}}, +{'c', {4587.01, 5518.4, 4428.61, 5676.8, 4402.25, 5716.25}}, +{'y', {4375.81, 5756, 4481.25, 5813.25}}, +{'y', {4626.61, 5852.8, 4499.25, 5888.25}}, +{'y', {4283.41, 5883.6, 4402.25, 6002.25}}, +{'y', {4468.21, 5998, 4450.25, 6024.25}}, +{'y', {4393.41, 6037.6, 4288.25, 6108.25}}, +{'y', {4243.81, 6147.6, 4283.25, 6139.25}}, +{'y', {4468.21, 6125.6, 4151.25, 6249.25}}, +{'y', {4239.41, 6248.8, 4041.25, 6363.25}}, +{'y', {4019.41, 6380.8, 3984.25, 6464.25}}, +{'y', {3918.21, 6521.6, 3865.25, 6596.25}}, +{'y', {3861.01, 6644.8, 3808.25, 6697.25}}, +{'y', {3680.61, 6847.2, 3619.25, 6843.25}}, +{'y', {3456.21, 6882.4, 3399.25, 6873.25}}, +{'l', {2818.25, 6825.25}}, +{'y', {2527.8, 6684.4, 2613.25, 6453.25}}, +{'y', {2684, 6361, 2785.25, 6403.25}}, +{'y', {2835.8, 6471, 2963.25, 6447.25}}, +{'y', {3187.81, 6411.6, 3159.25, 6451.25}}, +{'c', {3132.81, 6501.8, 2954.6, 6570, 2952.25, 6576.25}}, +{'y', {2950.2, 6583.2, 2853.25, 6620.25}}, +{'y', {2820.4, 6633.8, 2772.25, 6735.25}}, +{'y', {2736.8, 6772.4, 2910.25, 6708.25}}, +{'y', {2897.4, 6697.6, 2979.25, 6653.25}}, +{'y', {3170.21, 6664.6, 3287.25, 6546.25}}, +{'y', {3405.61, 6363.2, 3408.25, 6453.25}}, +{'y', {3438.61, 6556.8, 3309.25, 6790.25}}, +{'y', {3313.21, 6812, 3403.25, 6739.25}}, +{'y', {3418.81, 6761.4, 3427.25, 6697.25}}, +{'y', {3429.81, 6671.2, 3471.25, 6583.25}}, +{'y', {3502.41, 6440.2, 3542.25, 6521.25}}, +{'l', {3592.25, 6418.25}}, +{'y', {3608.01, 6389.6, 3542.25, 6306.25}}, +{'c', {3539.81, 6275.2, 3548.61, 6277.4, 3487.25, 6194.25}}, +{'y', {3425.41, 6110.2, 3463.25, 6062.25}}, +{'y', {3447.41, 5989.2, 3544.25, 5993.25}}, +{'y', {3572.81, 5969.4, 3610.25, 5969.25}}, +{'y', {3630.01, 5947.4, 3656.25, 5954.25}}, +{'y', {3674.01, 5995.8, 3742.25, 5974.25}}, +{'y', {3757.61, 6000.2, 3848.25, 6004.25}}, +{'c', {3858.81, 6033.2, 3863.21, 6050.8, 3900.25, 6057.25}}, +{'y', {3938.01, 6064, 3667.25, 6537.25}}, +{'y', {3737.81, 6545.8, 3647.25, 6682.25}}, +{'c', {3623.41, 6754.8, 3748.81, 6594.2, 3773.25, 6579.25}}, +{'c', {3797.21, 6563.4, 3808.21, 6539.2, 3790.25, 6541.25}}, +{'c', {3773.01, 6543.6, 3753.21, 6519.4, 3768.25, 6517.25}}, +{'c', {3784.01, 6515, 3927.01, 6350, 3964.25, 6238.25}}, +{'c', {4001.81, 6125.6, 4067.81, 6081.6, 4136.25, 6015.25}}, +{'y', {4204.21, 5949.6, 4195.25, 5683.25}}, +{'y', {4191.01, 5586.6, 4257.25, 5470.25}}, +{'y', {4279.01, 5428.2, 4233.25, 5228.25}}, +{'y', {4210.81, 5203.8, 4226.25, 5195.25}}, +{'y', {4237.21, 5181.8, 4312.25, 5036.25}}, +{'y', {4292.21, 5038.8, 4332.25, 4999.25}}, +{'y', {4389.01, 4933.2, 4318.25, 4966.25}}, +{'y', {4252.61, 4983.8, 4329.25, 4876.25}}, +{'y', {4342.81, 4856.2, 4244.25, 4907.25}}, +{'y', {4142.61, 4913.4, 4270.25, 4834.25}}, +{'y', {4364.81, 4755, 4239.25, 4803.25}}, +{'y', {4188.81, 4823.2, 4224.25, 4748.25}}, +{'y', {4259.21, 4730.8, 4448.25, 4654.25}}, +{'y', {4452.81, 4611.99, 4420.25, 4557.25}}, +{'y', {4424.21, 4512.99, 4400.25, 4475.25}}, +{'y', {4386.81, 4385.39, 4380.25, 4376.25}}, +{'y', {4334.01, 4374.39, 4252.25, 4222.25}}, +{'y', {4232.81, 4193.99, 4120.25, 4062.25}}, +{'y', {4098.61, 3984.99, 3900.25, 4064.25}}, +{'y', {3828.01, 4026.79, 3850.25, 4064.25}}, +{'y', {3845.61, 4088.39, 3898.25, 4154.25}}, +{'y', {3975.41, 4182.99, 3947.25, 4302.25}}, +{'c', {3990.81, 4317.19, 3867.61, 4347.99, 3870.25, 4361.25}}, +{'y', {3872.01, 4374.39, 3936.25, 4390.25}}, +{'y', {4023.81, 4411.79, 3975.25, 4438.25}}, +{'y', {3968.81, 4482.19, 4002.25, 4544.25}}, +{'y', {4129.41, 4552.59, 4002.25, 4731.25}}, +{'y', {3883.01, 4814.4, 3872.25, 4878.25}}, +{'c', {4010.61, 4968.4, 3920.41, 5104.8, 3922.25, 5144.25}}, +{'y', {3924.81, 5184, 3938.25, 5421.25}}, +{'y', {3916.01, 5489.8, 3883.25, 5639.25}}, +{'y', {3907.21, 5696.6, 3988.25, 5835.25}}, +{'c', {4019.41, 5881.4, 4116.21, 5934.2, 4092.25, 5967.25}}, +{'y', {4067.81, 6000.2, 3982.25, 5980.25}}, +{'y', {3896.21, 5995.8, 3903.25, 5938.25}}, +{'y', {3885.21, 5927.6, 3876.25, 5872.25}}, +{'y', {3868.48, 5785.34, 3771.25, 5716.25}}, +{'y', {3647.61, 5648.2, 3749.25, 5604.25}}, +{'y', {3814.81, 5531.6, 3707.25, 5529.25}}, +{'y', {3586.01, 5549.2, 3676.25, 5437.25}}, +{'y', {3795.01, 5296.2, 3762.25, 5265.25}}, +{'y', {3649.81, 5254.4, 3788.25, 5153.25}}, +{'c', {3788.25, 5153.24, 3779.61, 5175.2, 3782.25, 5155.25}}, +{'c', {3784.01, 5135.6, 3817.01, 5089.4, 3826.25, 5067.25}}, +{'y', {3834.61, 5045.4, 3790.25, 5043.25}}, +{'y', {3797.21, 4937.6, 3628.25, 4984.25}}, +{'v', {3627.81, 4983.8, 3610.25, 4981.25}}, +{'c', {3592.61, 4979.4, 3469.41, 4988.2, 3405.25, 5012.25}}, +{'y', {3341.81, 5036.6, 3267.25, 5036.25}}, +{'c', {3267.25, 5036.24, 3223.01, 5016.8, 3139.25, 5019.25}}, +{'y', {3055.8, 5021.2, 2968.25, 4990.25}}, +{'c', {2919.4, 4994.8, 3014, 5043.2, 3016.25, 5041.25}}, +{'c', {3018.4, 5038.8, 3080, 5100.4, 2992.25, 5094.25}}, +{'y', {2752.23, 5075.81, 2633.25, 5188.25}}, +{'y', {2611.4, 5203.8, 2583.25, 5234.25}}, +{'y', {2472.8, 5256.6, 2598.25, 5098.25}}, +{'y', {2611.4, 5082.8, 2596.25, 5072.25}}, +{'y', {2587.2, 5089.4, 2501.25, 5149.25}}, +{'c', {2470.48, 5159.5, 2455.48, 5174.18, 2430.25, 5201.25}}, +{'f', {}}, +{'r', {0.298, 0, 0}}, +{'m', {2167.25, 5329.25}}, +{'c', {2167.25, 5329.24, 2294.6, 5267.6, 2323.25, 5237.25}}, +{'y', {2351.8, 5206, 2506.25, 5080.25}}, +{'c', {2506.25, 5080.24, 2446.4, 5102.6, 2418.25, 5122.25}}, +{'y', {2389.2, 5142.2, 2270.25, 5232.25}}, +{'c', {2270.25, 5232.24, 2228.6, 5298.4, 2167.25, 5329.25}}, +{'f', {}}, +{'r', {0.6, 0.8, 0.2}}, +{'m', {1529.25, 5672.25}}, +{'c', {1532.61, 5673.99, 1526.84, 5703.82, 1524.25, 5710.25}}, +{'y', {1513.36, 5739.79, 1414.25, 5756.25}}, +{'c', {1412.1, 5740.98, 1411.5, 5723.43, 1412.25, 5705.25}}, +{'c', {1412.25, 5705.24, 1466.21, 5644.7, 1529.25, 5672.25}}, +{'f', {}}, +{'r', {0.4, 0.6, 0}}, +{'m', {1529.25, 5674.25}}, +{'c', {1523.96, 5672.83, 1528.73, 5704.64, 1527.25, 5710.25}}, +{'y', {1515.55, 5739.79, 1414.25, 5757.25}}, +{'c', {1412.1, 5742.08, 1411.5, 5724.53, 1412.25, 5706.25}}, +{'v', {1459.61, 5650.2, 1529.25, 5674.25}}, +{'f', {}}, +{'g', {0}}, +{'m', {1480.25, 5673.25}}, +{'c', {1473.72, 5672.99, 1468.16, 5685.53, 1468.25, 5701.25}}, +{'c', {1468.16, 5716.45, 1473.72, 5728.99, 1480.25, 5729.25}}, +{'c', {1487.47, 5728.99, 1493.05, 5716.45, 1493.25, 5701.25}}, +{'c', {1493.05, 5685.53, 1487.47, 5672.99, 1480.25, 5673.25}}, +{'f', {}}, +{'m', {979.246, 4594.25}}, +{'c', {979.242, 4594.24, 917.398, 4484.39, 1190.25, 4550.25}}, +{'c', {1190.25, 4550.24, 1344.2, 4563.59, 1370.25, 4590.25}}, +{'c', {1383.8, 4581.19, 1475.82, 4548.93, 1507.25, 4541.25}}, +{'y', {1581.8, 4523.99, 1674.25, 4634.25}}, +{'v', {1724.8, 4749.5, 1755.25, 4749.25}}, +{'y', {1786.4, 4749.5, 1751.25, 4732.25}}, +{'v', {1678.6, 4620.79, 1683.25, 4603.25}}, +{'c', {1683.24, 4603.24, 1625.8, 4383.19, 1450.25, 4374.25}}, +{'c', {1450.25, 4374.24, 1272.15, 4363.94, 1287.25, 4299.25}}, +{'c', {1287.24, 4299.24, 1383.8, 4325.99, 1410.25, 4299.25}}, +{'c', {1410.25, 4299.24, 1529, 4303.99, 1441.25, 4233.25}}, +{'l', {1366.25, 4106.25}}, +{'v', {1367.74, 4062.89, 1256.25, 4101.25}}, +{'y', {1148.4, 4138.99, 1035.25, 4281.25}}, +{'v', {859.648, 4440.94, 979.246, 4594.25}}, +{'f', {}}, +{'r', {0.902, 0.6, 0.6}}, +{'m', {966.25, 4550.25}}, +{'c', {966.25, 4550.24, 943.797, 4444.79, 1348.25, 4559.25}}, +{'c', {1348.24, 4559.24, 1397, 4559.19, 1423.25, 4550.25}}, +{'c', {1449.8, 4541.59, 1581.8, 4510.79, 1604.25, 4524.25}}, +{'v', {1524.6, 4374.39, 1397.25, 4392.25}}, +{'c', {1397.24, 4392.25, 1251.8, 4374.39, 1256.25, 4321.25}}, +{'c', {1256.25, 4321.24, 1300.2, 4242.39, 1353.25, 4216.25}}, +{'c', {1353.24, 4216.25, 1383.8, 4189.59, 1379.25, 4154.25}}, +{'c', {1375, 4119.19, 1344.2, 4101.59, 1322.25, 4093.25}}, +{'c', {1300.2, 4083.99, 1265, 4119.19, 1247.25, 4119.25}}, +{'c', {1229.8, 4119.19, 1137.4, 4189.59, 1089.25, 4242.25}}, +{'c', {1040.6, 4295.19, 948.199, 4427.19, 952.246, 4458.25}}, +{'y', {957, 4488.79, 966.25, 4550.25}}, +{'f', {}}, +{'r', {0.702, 0.4, 0.4}}, +{'m', {1001.25, 4358.25}}, +{'c', {1029.6, 4314.99, 1064.8, 4268.79, 1089.25, 4242.25}}, +{'c', {1137.4, 4189.59, 1229.8, 4119.19, 1247.25, 4119.25}}, +{'c', {1265, 4119.19, 1300.2, 4083.99, 1322.25, 4093.25}}, +{'c', {1344.2, 4101.59, 1375, 4119.19, 1379.25, 4154.25}}, +{'y', {1383.8, 4189.59, 1353.25, 4216.25}}, +{'c', {1319.27, 4232.85, 1289.13, 4271.26, 1272.25, 4297.25}}, +{'c', {1272.25, 4297.24, 1273.8, 4268.79, 1216.25, 4277.25}}, +{'c', {1159.4, 4286.39, 1102.2, 4317.19, 1084.25, 4352.25}}, +{'c', {1067, 4387.59, 1040.6, 4413.99, 1058.25, 4374.25}}, +{'c', {1075.8, 4334.79, 1102.2, 4295.19, 1120.25, 4291.25}}, +{'c', {1137.4, 4286.39, 1133, 4273.19, 1106.25, 4277.25}}, +{'c', {1080.2, 4281.99, 1049.4, 4286.39, 1001.25, 4343.25}}, +{'f', {}}, +{'r', {0.6, 0.149, 0}}, +{'m', {972.25, 4590.25}}, +{'v', {992.199, 4739.6, 1005.25, 4783.25}}, +{'c', {1005.25, 4783.24, 996.598, 4858.4, 1023.25, 4904.25}}, +{'c', {1049.4, 4950.8, 1071.4, 5019, 1104.25, 5078.25}}, +{'c', {1137.4, 5137.8, 1139.6, 5181.8, 1183.25, 5199.25}}, +{'c', {1227.6, 5217, 1293.6, 5311.6, 1324.25, 5322.25}}, +{'y', {1355.2, 5333.6, 1353.25, 5325.25}}, +{'c', {1353.24, 5325.25, 1427.8, 5487.6, 1577.25, 5443.25}}, +{'c', {1577.25, 5443.24, 1399.2, 5474.4, 1573.25, 5578.25}}, +{'c', {1573.24, 5578.25, 1520.2, 5565.7, 1556.25, 5642.25}}, +{'c', {1580.71, 5694.05, 1575.2, 5619.6, 1454.25, 5490.25}}, +{'v', {1399.2, 5395.2, 1342.25, 5362.25}}, +{'c', {1284.8, 5329.2, 1152.8, 5252.2, 1139.25, 5210.25}}, +{'c', {1126.4, 5168.6, 1091.2, 5104.8, 1069.25, 5087.25}}, +{'c', {1047.2, 5069.6, 1016.4, 5023.4, 1012.25, 4986.25}}, +{'c', {1012.24, 4986.25, 998.797, 4942, 983.25, 4929.25}}, +{'c', {968, 4915.6, 965.797, 4880.4, 966.25, 4858.25}}, +{'c', {965.797, 4836.4, 943.797, 4805.6, 946.246, 4779.25}}, +{'c', {946.242, 4779.24, 954.797, 4570.19, 950.25, 4548.25}}, +{'l', {972.25, 4590.25}}, +{'f', {}}, +{'g', {1}}, +{'m', {878.25, 4539.25}}, +{'c', {878.25, 4539.24, 855.797, 4554.79, 807.25, 4489.25}}, +{'v', {887.699, 4125.79, 887.246, 4110.25}}, +{'c', {887.246, 4110.24, 899.797, 4133.49, 885.25, 4212.25}}, +{'y', {871.199, 4291.89, 861.25, 4431.25}}, +{'l', {878.25, 4539.25}}, +{'f', {}}, +{'r', {0.6, 0.149, 0}}, +{'m', {1115.25, 5206.25}}, +{'v', {921.797, 5170.8, 926.246, 4849.25}}, +{'l', {917.25, 4577.25}}, +{'v', {904.199, 4858.4, 891.246, 4876.25}}, +{'c', {877.797, 4893.6, 921.797, 5016.8, 886.246, 4951.25}}, +{'c', {886.242, 4951.25, 732.598, 4796.8, 820.246, 4563.25}}, +{'c', {820.242, 4563.24, 837.098, 4527.29, 804.246, 4578.25}}, +{'v', {753.5, 4716.5, 765.246, 4787.25}}, +{'c', {765.242, 4787.25, 767.797, 4811.1, 788.246, 4842.25}}, +{'v', {883.297, 4970.6, 913.246, 4996.25}}, +{'c', {913.242, 4996.25, 932.797, 5154.3, 1102.25, 5211.25}}, +{'v', {1164.9, 5236.8, 1115.25, 5206.25}}, +{'f', {}}, +{'g', {0}}, +{'m', {2649.25, 5934.25}}, +{'c', {2656.06, 5938.09, 2655.96, 5948.76, 2662.25, 5950.25}}, +{'c', {2674.98, 5954.45, 2676.78, 5966.19, 2682.25, 5975.25}}, +{'c', {2692.08, 5989.32, 2694.21, 6006.18, 2700.25, 6022.25}}, +{'c', {2703.6, 6030.28, 2703.9, 6040.79, 2700.25, 6048.25}}, +{'c', {2687.54, 6075.34, 2679.82, 6102.28, 2664.25, 6129.25}}, +{'c', {2661.01, 6133.91, 2658.12, 6142.65, 2656.25, 6149.25}}, +{'c', {2650.82, 6164.45, 2637, 6175.35, 2627.25, 6190.25}}, +{'c', {2624.37, 6194.72, 2630.14, 6204.9, 2622.25, 6206.25}}, +{'c', {2612.4, 6206.98, 2596.46, 6213.36, 2594.25, 6202.25}}, +{'c', {2587.43, 6173.61, 2598.55, 6145.86, 2609.25, 6119.25}}, +{'c', {2600.6, 6111.39, 2604.27, 6101.25, 2606.25, 6093.25}}, +{'c', {2613.75, 6053.15, 2600.57, 6016.28, 2592.25, 5978.25}}, +{'c', {2591.95, 5976.92, 2595.71, 5974.74, 2595.25, 5974.25}}, +{'c', {2581.96, 5944.52, 2565.99, 5917.62, 2546.25, 5891.25}}, +{'c', {2537.64, 5880.52, 2528.05, 5870.44, 2522.25, 5859.25}}, +{'c', {2518.54, 5850.09, 2514.05, 5839.46, 2517.25, 5828.25}}, +{'c', {2479.01, 5798.02, 2454.26, 5755.72, 2425.25, 5713.25}}, +{'c', {2420.46, 5705.94, 2423.66, 5692.64, 2429.25, 5689.25}}, +{'c', {2438.66, 5685.3, 2449.18, 5696.57, 2454.25, 5706.25}}, +{'c', {2458.63, 5713.55, 2462.51, 5720.7, 2468.25, 5728.25}}, +{'c', {2469.38, 5729.7, 2467.41, 5734.3, 2469.25, 5735.25}}, +{'c', {2497.86, 5761.45, 2516.21, 5793.75, 2543.25, 5820.25}}, +{'c', {2564.65, 5823.42, 2581.55, 5834.39, 2601.25, 5845.25}}, +{'c', {2604.17, 5847.51, 2609.93, 5844.77, 2613.25, 5847.25}}, +{'c', {2632.41, 5859.86, 2632.39, 5882.26, 2633.25, 5903.25}}, +{'c', {2634.1, 5912.99, 2636.26, 5927.48, 2649.25, 5934.25}}, +{'f', {}}, +{'m', {2551.25, 5983.25}}, +{'c', {2552.91, 5984.17, 2551.21, 5988.82, 2552.25, 5991.25}}, +{'c', {2554.19, 5994.77, 2558.62, 5996.83, 2560.25, 6000.25}}, +{'c', {2561.6, 6002.77, 2559.81, 6006.95, 2561.25, 6008.25}}, +{'c', {2586.97, 6035.43, 2589.79, 6068.7, 2578.25, 6101.25}}, +{'c', {2589.66, 6108.22, 2590.33, 6122.1, 2585.25, 6131.25}}, +{'c', {2576.27, 6150.12, 2574.15, 6171.3, 2563.25, 6188.25}}, +{'c', {2554.61, 6202.23, 2537.25, 6215.92, 2522.25, 6201.25}}, +{'c', {2517.54, 6197.19, 2514, 6188.53, 2517.25, 6180.25}}, +{'c', {2517.77, 6178.9, 2520.9, 6177.3, 2520.25, 6176.25}}, +{'c', {2519.43, 6172.83, 2513.42, 6170.37, 2513.25, 6167.25}}, +{'c', {2513.02, 6150.92, 2502.53, 6134.3, 2509.25, 6119.25}}, +{'c', {2517.95, 6101.76, 2526.96, 6081.33, 2534.25, 6062.25}}, +{'c', {2520.82, 6038.52, 2532.19, 6011.58, 2513.25, 5991.25}}, +{'c', {2511.41, 5989.33, 2511.51, 5985.08, 2513.25, 5983.25}}, +{'c', {2516.06, 5977.52, 2520.53, 5973.05, 2526.25, 5970.25}}, +{'c', {2528.09, 5968.51, 2531.92, 5968.48, 2534.25, 5970.25}}, +{'c', {2540.48, 5973.83, 2544.86, 5979.19, 2551.25, 5983.25}}, +{'f', {}}, +{'m', {3242.25, 6096.25}}, +{'c', {3257.76, 6077.03, 3260.95, 6046.29, 3238.25, 6031.25}}, +{'c', {3244.46, 5994.6, 3281.32, 6016.37, 3304.25, 6022.25}}, +{'c', {3303.21, 6026.46, 3305.71, 6030.25, 3309.25, 6030.25}}, +{'c', {3320.45, 6030.47, 3327.93, 6041.82, 3339.25, 6040.25}}, +{'c', {3344.38, 6056.93, 3362.4, 6064.65, 3369.25, 6080.25}}, +{'c', {3388.47, 6120.47, 3381.97, 6167.48, 3353.25, 6203.25}}, +{'c', {3351.32, 6205.98, 3353.71, 6211.66, 3352.25, 6215.25}}, +{'c', {3344.03, 6239.96, 3321.28, 6243.43, 3300.25, 6251.25}}, +{'c', {3287.06, 6293.62, 3279.79, 6337.96, 3260.25, 6378.25}}, +{'c', {3242.66, 6381.35, 3234.98, 6400.48, 3220.25, 6408.25}}, +{'c', {3205.61, 6416.3, 3199.36, 6398.94, 3200.25, 6387.25}}, +{'c', {3199.89, 6385.18, 3204.86, 6382.71, 3203.25, 6378.25}}, +{'c', {3202.15, 6376.89, 3199.29, 6375.67, 3199.25, 6374.25}}, +{'c', {3199.3, 6372.71, 3201.73, 6371.27, 3203.25, 6370.25}}, +{'c', {3193.11, 6360.78, 3177.46, 6355.53, 3174.25, 6343.25}}, +{'c', {3163.72, 6302.12, 3192.02, 6267.75, 3210.25, 6232.25}}, +{'c', {3216.87, 6220.05, 3208.77, 6206.07, 3200.25, 6193.25}}, +{'c', {3195.6, 6185.14, 3196.79, 6172.74, 3200.25, 6163.25}}, +{'c', {3208.09, 6137.69, 3225.59, 6118.56, 3242.25, 6096.25}}, +{'f', {}}, +{'m', {2833.25, 5894.25}}, +{'c', {2817.36, 5874.14, 2779.83, 5845.88, 2812.25, 5824.25}}, +{'c', {2814.05, 5823.25, 2818.12, 5823.21, 2820.25, 5824.25}}, +{'c', {2842.25, 5841.9, 2864.29, 5851.83, 2891.25, 5858.25}}, +{'c', {2892.45, 5858.55, 2895.46, 5853.41, 2899.25, 5855.25}}, +{'c', {2917.27, 5862.92, 2939.38, 5862.28, 2952.25, 5877.25}}, +{'c', {2993.84, 5874.54, 3033.05, 5886.81, 3070.25, 5901.25}}, +{'c', {3083.12, 5906.12, 3097.09, 5912.27, 3110.25, 5918.25}}, +{'c', {3125.44, 5924.13, 3138.68, 5934.34, 3151.25, 5947.25}}, +{'c', {3152.38, 5948.39, 3156.27, 5947.4, 3159.25, 5947.25}}, +{'c', {3158.77, 5956.89, 3169.35, 5958.29, 3172.25, 5965.25}}, +{'c', {3173.08, 5967.68, 3171.43, 5972.04, 3173.25, 5973.25}}, +{'c', {3196.41, 5991.09, 3206.53, 6013.29, 3193.25, 6039.25}}, +{'c', {3190.54, 6045.79, 3187.8, 6052.59, 3181.25, 6058.25}}, +{'c', {3169.64, 6067.85, 3157.52, 6058.45, 3146.25, 6062.25}}, +{'c', {3144.25, 6054.89, 3136.23, 6056.78, 3132.25, 6054.25}}, +{'c', {3122.54, 6049.77, 3107.87, 6056.21, 3098.25, 6051.25}}, +{'c', {3083.44, 6043.75, 3069.79, 6041.89, 3054.25, 6037.25}}, +{'c', {3050.71, 6036.84, 3042.16, 6037.89, 3040.25, 6031.25}}, +{'c', {3038.93, 6032.47, 3037.3, 6035.05, 3036.25, 6035.25}}, +{'c', {3015.14, 6030.45, 3001.28, 6028.09, 2987.25, 6009.25}}, +{'c', {2985.84, 6008.02, 2980.98, 6009.99, 2979.25, 6008.25}}, +{'c', {2968.7, 5999.61, 2964.25, 5986.04, 2952.25, 5978.25}}, +{'c', {2949.92, 5977.3, 2946.05, 5979.14, 2944.25, 5977.25}}, +{'c', {2936.81, 5972.98, 2932.77, 5965.93, 2925.25, 5961.25}}, +{'c', {2922.09, 5958.5, 2917.69, 5962.07, 2918.25, 5965.25}}, +{'c', {2920.74, 5986.3, 2927.92, 6005.96, 2921.25, 6026.25}}, +{'c', {2944.45, 6054.32, 2972.13, 6075.76, 2992.25, 6106.25}}, +{'c', {2992.16, 6129.62, 2999.77, 6153.23, 2998.25, 6176.25}}, +{'c', {2998.42, 6178.2, 2995.25, 6186.26, 2994.25, 6190.25}}, +{'c', {2990.44, 6199.47, 3000.4, 6211.2, 2991.25, 6219.25}}, +{'c', {2975.68, 6232.4, 2961.46, 6222.76, 2952.25, 6207.25}}, +{'c', {2932.18, 6202.71, 2909.45, 6195.09, 2892.25, 6208.25}}, +{'c', {2881.02, 6217.32, 2874.66, 6227.28, 2866.25, 6239.25}}, +{'c', {2855.04, 6253.22, 2858.72, 6269.02, 2859.25, 6286.25}}, +{'c', {2859.56, 6287.63, 2856.09, 6289.12, 2856.25, 6290.25}}, +{'c', {2856.11, 6292.08, 2858.52, 6293.52, 2860.25, 6295.25}}, +{'c', {2852.23, 6301.88, 2849.21, 6313.52, 2838.25, 6317.25}}, +{'c', {2841.35, 6329.01, 2833.92, 6338.73, 2824.25, 6342.25}}, +{'c', {2802.39, 6349.66, 2783.95, 6328.62, 2763.25, 6328.25}}, +{'c', {2757.48, 6327.61, 2752.34, 6339.03, 2745.25, 6342.25}}, +{'c', {2740.45, 6344.98, 2732.71, 6345.34, 2728.25, 6342.25}}, +{'c', {2721.16, 6336.88, 2714.68, 6335.62, 2706.25, 6333.25}}, +{'c', {2688.55, 6328.57, 2674.49, 6316.54, 2658.25, 6307.25}}, +{'c', {2641.88, 6297.7, 2631.38, 6281.9, 2619.25, 6267.25}}, +{'c', {2609.14, 6254.7, 2607.65, 6228.15, 2623.25, 6222.25}}, +{'c', {2643.54, 6215.5, 2657.88, 6245.23, 2679.25, 6241.25}}, +{'c', {2682.94, 6241.09, 2685.21, 6237.67, 2684.25, 6233.25}}, +{'c', {2688.28, 6232.2, 2690.68, 6234.72, 2693.25, 6238.25}}, +{'c', {2702.23, 6226.59, 2715.04, 6222.88, 2725.25, 6213.25}}, +{'c', {2736.03, 6203.34, 2755.23, 6207.93, 2766.25, 6197.25}}, +{'c', {2783.22, 6180.45, 2777.07, 6150.95, 2798.25, 6136.25}}, +{'c', {2791.95, 6122.16, 2785.27, 6108.11, 2781.25, 6092.25}}, +{'c', {2778.49, 6079.14, 2789.42, 6065.98, 2802.25, 6067.25}}, +{'c', {2816.58, 6068.01, 2819.46, 6076.36, 2825.25, 6088.25}}, +{'c', {2827.73, 6085.26, 2832.89, 6081.99, 2832.25, 6079.25}}, +{'c', {2827.41, 6053.36, 2816.12, 6031.44, 2811.25, 6004.25}}, +{'c', {2810.43, 6001.21, 2807.08, 5998.99, 2803.25, 6000.25}}, +{'c', {2797.65, 5954.56, 2753.08, 5928.23, 2725.25, 5893.25}}, +{'c', {2720.65, 5887.89, 2720.61, 5874.15, 2725.25, 5869.25}}, +{'c', {2740.54, 5854.46, 2762.1, 5871.46, 2781.25, 5877.25}}, +{'c', {2783.14, 5890.27, 2792.59, 5900.62, 2807.25, 5900.25}}, +{'c', {2810.06, 5899.9, 2812.64, 5905.86, 2816.25, 5907.25}}, +{'c', {2820.04, 5908.81, 2825.56, 5906.35, 2828.25, 5908.25}}, +{'c', {2847.72, 5921.67, 2863.21, 5933.84, 2882.25, 5947.25}}, +{'c', {2884.44, 5948.36, 2888.22, 5946.61, 2890.25, 5948.25}}, +{'c', {2894.18, 5949.58, 2896.23, 5953.83, 2900.25, 5956.25}}, +{'c', {2903.61, 5958.07, 2906.27, 5954.88, 2908.25, 5952.25}}, +{'c', {2901.29, 5947.96, 2901.25, 5937.43, 2895.25, 5935.25}}, +{'c', {2886.32, 5932.26, 2880.15, 5926.62, 2872.25, 5921.25}}, +{'c', {2869.52, 5919.58, 2862.27, 5922.31, 2861.25, 5920.25}}, +{'c', {2854.55, 5909.03, 2841.53, 5904.21, 2833.25, 5894.25}}, +{'f', {}}, +{'m', {2224.25, 6438.25}}, +{'v', {2122.58, 6469.59, 2000.25, 6187.25}}, +{'c', {2000.25, 6187.24, 1973.4, 6130, 1947.25, 6108.25}}, +{'c', {1920.6, 6086, 1797.4, 6046.4, 1775.25, 6002.25}}, +{'l', {1661.25, 5826.25}}, +{'c', {1661.24, 5826.24, 1823.8, 6002.4, 1859.25, 6029.25}}, +{'v', {1947, 6121.2, 1912.25, 6046.25}}, +{'c', {1912.25, 6046.24, 1757.8, 5927.6, 1771.25, 5826.25}}, +{'c', {1771.25, 5826.24, 1709.4, 5668, 1700.25, 5646.25}}, +{'c', {1700.24, 5646.25, 1876.6, 5998, 1903.25, 6011.25}}, +{'c', {1929.4, 6024.4, 1942.6, 6024.4, 1929.25, 5985.25}}, +{'c', {1916.2, 5945.2, 1911.8, 5764.8, 1881.25, 5743.25}}, +{'v', {1969, 5967.2, 1960.25, 6002.25}}, +{'c', {1960.25, 6002.24, 1995.4, 6042, 2022.25, 5985.25}}, +{'l', {2008.25, 5809.25}}, +{'l', {2057.25, 5677.25}}, +{'v', {2030.6, 5800, 2048.25, 5971.25}}, +{'c', {2048.25, 5971.24, 2026.2, 6086, 2070.25, 6024.25}}, +{'c', {2114.2, 5962.8, 2219.8, 5896.8, 2220.25, 5844.25}}, +{'v', {2162.6, 6037.6, 2061.25, 6090.25}}, +{'l', {2017.25, 6024.25}}, +{'l', {2004.25, 6046.25}}, +{'c', {2004.25, 6046.24, 1964.6, 6055.2, 2013.25, 6130.25}}, +{'y', {2061.4, 6204.8, 2057.25, 6213.25}}, +{'c', {2057.25, 6213.24, 2127.4, 6134.4, 2145.25, 6134.25}}, +{'c', {2145.25, 6134.24, 2290.2, 6218, 2303.25, 5949.25}}, +{'c', {2303.25, 5949.24, 2378.2, 6108, 2277.25, 6183.25}}, +{'v', {2114.2, 6204.8, 2127.25, 6262.25}}, +{'l', {2206.25, 6398.25}}, +{'y', {2246.2, 6455.6, 2228.25, 6425.25}}, +{'f', {}}, +{'m', {2004.25, 6257.25}}, +{'c', {2004.25, 6257.24, 1863.4, 6257.6, 1828.25, 6200.25}}, +{'l', {1749.25, 6095.25}}, +{'v', {1938.2, 6204.8, 1982.25, 6218.25}}, +{'y', {2026.2, 6231.2, 2004.25, 6257.25}}, +{'f', {}}, +{'m', {1564.25, 6187.25}}, +{'c', {1564.25, 6187.24, 1542.2, 6174, 1538.25, 6143.25}}, +{'c', {1533.4, 6112.4, 1507, 6108, 1516.25, 6077.25}}, +{'c', {1524.6, 6046.4, 1546.6, 6020, 1546.25, 6064.25}}, +{'c', {1546.6, 6108, 1564.2, 6130, 1573.25, 6143.25}}, +{'c', {1581.8, 6156.4, 1599.4, 6204.8, 1564.25, 6187.25}}, +{'f', {}}, +{'m', {1467.25, 5514.25}}, +{'v', {1375, 5558, 1340.25, 5597.25}}, +{'c', {1304.6, 5637.2, 1309.57, 5580.37, 1256.25, 5584.25}}, +{'y', {1191.88, 5589.25, 1203.25, 5765.25}}, +{'l', {1159.25, 5681.25}}, +{'c', {1159.25, 5681.24, 1146.2, 5522.8, 1234.25, 5549.25}}, +{'c', {1277.18, 5562.09, 1291.4, 5544.8, 1274.25, 5536.25}}, +{'c', {1256.2, 5527.2, 1335.4, 5522.8, 1304.25, 5505.25}}, +{'c', {1273.8, 5487.6, 1432.2, 5544.8, 1406.25, 5430.25}}, +{'l', {1467.25, 5514.25}}, +{'f', {}}, +{'m', {1329.25, 5355.25}}, +{'c', {1329.25, 5355.24, 1159.4, 5307.2, 1120.25, 5413.25}}, +{'v', {1067, 5386.4, 1091.25, 5353.25}}, +{'y', {1115.4, 5320.4, 1128.25, 5316.25}}, +{'c', {1128.24, 5316.25, 1188, 5302.8, 1181.25, 5294.25}}, +{'y', {1174.8, 5285.2, 1148.25, 5248.25}}, +{'v', {1260.6, 5313.8, 1329.25, 5355.25}}, +{'f', {}}, +{'g', {1}}, +{'m', {3841.25, 4495.25}}, +{'c', {3836.51, 4471.91, 3818.13, 4463.1, 3797.25, 4456.25}}, +{'c', {3776.12, 4466.32, 3747.54, 4500.49, 3727.25, 4478.25}}, +{'c', {3721.59, 4483.07, 3713.03, 4483.73, 3709.25, 4491.25}}, +{'c', {3704.13, 4500.89, 3707.12, 4512.39, 3703.25, 4521.25}}, +{'c', {3697.36, 4535.16, 3689.56, 4550.19, 3691.25, 4566.25}}, +{'c', {3711.48, 4573.62, 3718.02, 4594.59, 3713.25, 4614.25}}, +{'c', {3712.05, 4616.8, 3707.33, 4618.97, 3709.25, 4623.25}}, +{'c', {3711.57, 4626.36, 3715.07, 4628.86, 3718.25, 4632.25}}, +{'c', {3716.52, 4630.31, 3714.93, 4627.75, 3713.25, 4628.25}}, +{'c', {3707, 4629.14, 3708.36, 4636.14, 3710.25, 4640.25}}, +{'c', {3716.23, 4659.61, 3738.38, 4662.51, 3753.25, 4649.25}}, +{'c', {3756.02, 4655.61, 3761.62, 4653.48, 3766.25, 4654.25}}, +{'c', {3765.86, 4660.17, 3770.35, 4665.96, 3772.25, 4670.25}}, +{'c', {3778.22, 4682.66, 3796.14, 4670.5, 3805.25, 4677.25}}, +{'c', {3816.75, 4686.3, 3828.44, 4693.99, 3840.25, 4687.25}}, +{'c', {3860.2, 4676.21, 3878.99, 4662.88, 3892.25, 4643.25}}, +{'c', {3898.51, 4634.56, 3901.15, 4620.47, 3900.25, 4610.25}}, +{'c', {3900.09, 4602.81, 3884.9, 4606.68, 3881.25, 4596.25}}, +{'c', {3874.03, 4577.24, 3894.18, 4571.53, 3902.25, 4557.25}}, +{'c', {3904.67, 4552.97, 3901.77, 4549.63, 3898.25, 4548.25}}, +{'c', {3893.78, 4547.14, 3885.15, 4549.22, 3886.25, 4544.25}}, +{'c', {3897.36, 4509.11, 3867.05, 4501.66, 3841.25, 4495.25}}, +{'f', {}}, +{'m', {3735.25, 4280.25}}, +{'c', {3735.52, 4298.91, 3717.91, 4318.22, 3731.25, 4337.25}}, +{'c', {3732.7, 4335.51, 3734.14, 4333.08, 3735.25, 4333.25}}, +{'c', {3737.09, 4333.08, 3738.54, 4335.51, 3740.25, 4337.25}}, +{'c', {3756.45, 4312.61, 3796.62, 4302.4, 3795.25, 4271.25}}, +{'c', {3794.82, 4266.18, 3782.86, 4256.08, 3793.25, 4249.25}}, +{'c', {3772.87, 4234.16, 3772.26, 4208.09, 3762.25, 4187.25}}, +{'c', {3748.39, 4190.53, 3735.06, 4194.53, 3722.25, 4200.25}}, +{'c', {3726.28, 4216.93, 3724.99, 4235.46, 3734.25, 4250.25}}, +{'c', {3738.79, 4257.66, 3735.63, 4269.52, 3735.25, 4280.25}}, +{'f', {}}, +{'g', {0.8}}, +{'m', {1907.25, 4379.25}}, +{'v', {1721.96, 4267.29, 1876.25, 4436.25}}, +{'y', {1973.4, 4541.59, 2083.25, 4603.25}}, +{'c', {2083.25, 4603.24, 2197.8, 4651.6, 2237.25, 4665.25}}, +{'c', {2277, 4678, 2444.2, 4735.2, 2479.25, 4739.25}}, +{'c', {2514.6, 4744, 2620.2, 4788, 2695.25, 4744.25}}, +{'y', {2769.8, 4700, 2858.25, 4651.25}}, +{'c', {2858.25, 4651.24, 2677.4, 4744, 2638.25, 4717.25}}, +{'c', {2598.2, 4691.2, 2519, 4695.6, 2453.25, 4660.25}}, +{'c', {2453.25, 4660.24, 2290.2, 4611.99, 2255.25, 4590.25}}, +{'c', {2219.8, 4567.99, 2105.4, 4440.39, 2088.25, 4449.25}}, +{'c', {2070.2, 4457.99, 2092.2, 4462.39, 2105.25, 4493.25}}, +{'c', {2118.6, 4523.99, 2096.6, 4541.59, 2008.25, 4471.25}}, +{'y', {1920.6, 4400.79, 1907.25, 4379.25}}, +{'f', {}}, +{'g', {0}}, +{'m', {1989.25, 4444.25}}, +{'v', {2004.36, 4587.86, 2097.25, 4569.25}}, +{'c', {2097.24, 4569.25, 2188.11, 4615.42, 2218.25, 4637.25}}, +{'c', {2218.24, 4637.25, 2307.58, 4655.79, 2322.25, 4662.25}}, +{'c', {2525.26, 4758.28, 2687.16, 4708.57, 2693.25, 4721.25}}, +{'c', {2699.19, 4733.2, 2915.06, 4654.91, 2955.25, 4610.25}}, +{'c', {2959.06, 4605.09, 2842.22, 4671.53, 2735.25, 4692.25}}, +{'c', {2644.54, 4710.09, 2406.92, 4689.61, 2287.25, 4628.25}}, +{'c', {2254.25, 4612.12, 2155.86, 4548.5, 2128.25, 4549.25}}, +{'y', {2100.17, 4550.95, 1989.25, 4444.25}}, +{'f', {}}, +{'g', {0.8}}, +{'m', {1947.25, 4163.25}}, +{'c', {1947.25, 4163.24, 1779.8, 4189.59, 1964.25, 4207.25}}, +{'c', {1964.25, 4207.24, 2162.6, 4229.19, 2206.25, 4286.25}}, +{'c', {2206.25, 4286.24, 2356.2, 4387.59, 2387.25, 4392.25}}, +{'c', {2417.8, 4396.39, 2747.8, 4475.59, 2752.25, 4502.25}}, +{'c', {2756.6, 4528.39, 2818.2, 4528.39, 2836.25, 4519.25}}, +{'c', {2853.4, 4510.79, 2844.6, 4497.59, 2814.25, 4489.25}}, +{'c', {2783, 4479.99, 2439.8, 4299.59, 2369.25, 4286.25}}, +{'c', {2299, 4273.19, 2171.4, 4189.59, 2118.25, 4176.25}}, +{'y', {2065.8, 4163.19, 1947.25, 4163.25}}, +{'f', {}}, +{'g', {0}}, +{'m', {2161.25, 4244.25}}, +{'c', {2161.25, 4244.24, 2067.32, 4253.68, 2162.25, 4262.25}}, +{'c', {2162.25, 4262.24, 2258.9, 4300.36, 2281.25, 4329.25}}, +{'v', {2358.04, 4381.47, 2374.25, 4383.25}}, +{'c', {2389.57, 4385.97, 2543.13, 4426.53, 2545.25, 4440.25}}, +{'c', {2547.64, 4453.55, 2922.37, 4591.95, 2971.25, 4557.25}}, +{'c', {3003.01, 4533.74, 2894.1, 4552.19, 2788.25, 4503.25}}, +{'c', {2773.22, 4496.86, 2400.84, 4336.4, 2365.25, 4329.25}}, +{'c', {2328.74, 4322.89, 2263.41, 4280.08, 2236.25, 4273.25}}, +{'y', {2209.35, 4266.57, 2161.25, 4244.25}}, +{'f', {}}, +{'m', {2074.25, 4220.25}}, +{'c', {2074.25, 4220.24, 2131.8, 4224.79, 2118.25, 4207.25}}, +{'y', {2105.4, 4189.59, 2079.25, 4198.25}}, +{'l', {2074.25, 4220.25}}, +{'f', {}}, +{'m', {1995.25, 4203.25}}, +{'v', {2052.6, 4207.19, 2039.25, 4189.25}}, +{'y', {2026.2, 4171.99, 2000.25, 4181.25}}, +{'l', {1995.25, 4203.25}}, +{'f', {}}, +{'m', {1881.25, 4185.25}}, +{'c', {1881.25, 4185.24, 1938.2, 4189.59, 1925.25, 4172.25}}, +{'y', {1911.8, 4154.39, 1885.25, 4163.25}}, +{'l', {1881.25, 4185.25}}, +{'f', {}}, +{'m', {1797.25, 4176.25}}, +{'c', {1797.25, 4176.24, 1854.6, 4180.79, 1841.25, 4163.25}}, +{'y', {1828.2, 4145.59, 1802.25, 4154.25}}, +{'l', {1797.25, 4176.25}}, +{'f', {}}, +{'m', {2220.25, 4612.25}}, +{'v', {2268.2, 4611.99, 2255.25, 4594.25}}, +{'y', {2241.8, 4576.79, 2206.25, 4581.25}}, +{'l', {2220.25, 4612.25}}, +{'f', {}}, +{'m', {2110.25, 4550.25}}, +{'c', {2110.25, 4550.24, 2181.36, 4573.94, 2154.25, 4537.25}}, +{'y', {2140.6, 4519.59, 2114.25, 4528.25}}, +{'l', {2110.25, 4550.25}}, +{'f', {}}, +{'m', {1986.25, 4497.25}}, +{'c', {1986.25, 4497.24, 2043.8, 4501.99, 2030.25, 4484.25}}, +{'y', {2017.4, 4466.79, 1991.25, 4475.25}}, +{'l', {1986.25, 4497.25}}, +{'f', {}}, +{'m', {1903.25, 4431.25}}, +{'c', {1903.25, 4431.24, 1960.2, 4435.99, 1947.25, 4418.25}}, +{'y', {1933.8, 4400.79, 1907.25, 4409.25}}, +{'l', {1903.25, 4431.25}}, +{'f', {}}, +{'m', {1828.25, 4379.25}}, +{'v', {1885.4, 4383.19, 1872.25, 4365.25}}, +{'y', {1859, 4347.99, 1832.25, 4357.25}}, +{'l', {1828.25, 4379.25}}, +{'f', {}}, +{'m', {2258.25, 4308.25}}, +{'c', {2258.25, 4308.24, 2334.23, 4314.27, 2316.25, 4291.25}}, +{'y', {2299.1, 4267.42, 2264.25, 4279.25}}, +{'l', {2258.25, 4308.25}}, +{'f', {}}, +{'m', {2372.25, 4361.25}}, +{'c', {2372.24, 4361.24, 2448.63, 4367.07, 2431.25, 4343.25}}, +{'y', {2413.5, 4320.22, 2378.25, 4332.25}}, +{'l', {2372.25, 4361.25}}, +{'f', {}}, +{'m', {2491.25, 4409.25}}, +{'c', {2491.25, 4409.24, 2567.43, 4415.47, 2550.25, 4392.25}}, +{'y', {2532.3, 4368.62, 2497.25, 4380.25}}, +{'l', {2491.25, 4409.25}}, +{'f', {}}, +{'m', {2605.25, 4462.25}}, +{'c', {2605.25, 4462.24, 2681.83, 4468.27, 2664.25, 4445.25}}, +{'y', {2646.7, 4421.42, 2611.25, 4433.25}}, +{'l', {2605.25, 4462.25}}, +{'f', {}}, +{'m', {2302.25, 4660.25}}, +{'c', {2302.25, 4660.24, 2378.23, 4666.27, 2360.25, 4643.25}}, +{'y', {2343.1, 4619.42, 2299.25, 4622.25}}, +{'l', {2302.25, 4660.25}}, +{'f', {}}, +{'m', {2412.25, 4695.25}}, +{'c', {2412.25, 4695.24, 2488.23, 4701.47, 2470.25, 4678.25}}, +{'y', {2453.1, 4654.62, 2405.25, 4657.25}}, +{'l', {2412.25, 4695.25}}, +{'f', {}}, +{'m', {2162.25, 4255.25}}, +{'c', {2162.25, 4255.24, 2219.8, 4259.99, 2206.25, 4242.25}}, +{'y', {2193.4, 4224.79, 2167.25, 4233.25}}, +{'l', {2162.25, 4255.25}}, +{'f', {}}, +{'r', {0.6, 0.149, 0}}, +{'m', {1357.25, 5347.25}}, +{'v', {1309, 5250, 1304.25, 5215.25}}, +{'c', {1304.24, 5215.25, 1313.4, 5311.6, 1326.25, 5333.25}}, +{'y', {1339.8, 5355.6, 1357.25, 5347.25}}, +{'f', {}}, +{'m', {1155.25, 5193.25}}, +{'c', {1155.24, 5193.25, 1119.8, 5034.4, 1124.25, 5003.25}}, +{'c', {1124.25, 5003.24, 1111, 5131.2, 1115.25, 5144.25}}, +{'y', {1119.8, 5157.6, 1155.25, 5193.25}}, +{'f', {}}, +{'g', {0.8}}, +{'m', {1384.25, 6013.25}}, +{'l', {1381.25, 5978.25}}, +{'l', {1357.25, 5976.25}}, +{'v', {1513.6, 5837.4, 1520.25, 5754.25}}, +{'v', {1529, 5844, 1384.25, 6013.25}}, +{'f', {}}, +{'g', {0}}, +{'m', {1427.25, 5999.25}}, +{'c', {1422.88, 6004.06, 1425.29, 6012.25, 1421.25, 6015.25}}, +{'c', {1411.9, 6020.79, 1435.08, 6021.42, 1432.25, 6029.25}}, +{'c', {1428.36, 6042.56, 1430.41, 6042.79, 1429.25, 6057.25}}, +{'c', {1428.45, 6063.4, 1434.91, 6080.34, 1439.25, 6085.25}}, +{'c', {1455.05, 6103.36, 1440.49, 6135.61, 1458.25, 6152.25}}, +{'c', {1461.88, 6155.85, 1466.01, 6161.92, 1469.25, 6166.25}}, +{'c', {1476.39, 6177.11, 1489.42, 6182.48, 1500.25, 6191.25}}, +{'c', {1503.37, 6194.62, 1501.16, 6203.72, 1507.25, 6202.25}}, +{'c', {1515.27, 6201.44, 1528.92, 6202.88, 1528.25, 6193.25}}, +{'c', {1527.18, 6170.3, 1512.56, 6151.38, 1498.25, 6133.25}}, +{'c', {1503.45, 6125.29, 1498.41, 6118.3, 1495.25, 6112.25}}, +{'c', {1480.6, 6083.36, 1482.68, 6052.11, 1481.25, 6021.25}}, +{'c', {1480.71, 6019.93, 1477.33, 6019.07, 1477.25, 6018.25}}, +{'c', {1481.3, 5992.8, 1487.73, 5968.62, 1497.25, 5944.25}}, +{'c', {1501.42, 5933.87, 1506.6, 5924.02, 1508.25, 5914.25}}, +{'c', {1509.47, 5906.28, 1510.61, 5897.12, 1506.25, 5889.25}}, +{'c', {1528.5, 5857.57, 1514.11, 5828.41, 1527.25, 5789.25}}, +{'c', {1529.24, 5782.7, 1547.85, 5761.64, 1543.25, 5763.25}}, +{'c', {1515.12, 5772.99, 1513.88, 5777.37, 1512.25, 5785.25}}, +{'c', {1510.33, 5792.51, 1506.7, 5807.65, 1504.25, 5814.25}}, +{'c', {1503.39, 5816.08, 1501.51, 5836.8, 1500.25, 5838.25}}, +{'c', {1484.01, 5864.35, 1498.95, 5862.41, 1484.25, 5888.25}}, +{'c', {1468.17, 5895.83, 1457.54, 5907.97, 1445.25, 5921.25}}, +{'c', {1443.01, 5923, 1455.59, 5930.96, 1453.25, 5933.25}}, +{'c', {1441.58, 5947.48, 1428.9, 5955.92, 1432.25, 5972.25}}, +{'c', {1434.3, 5979.94, 1435.8, 5991.56, 1427.25, 5999.25}}, +{'f', {}}, +{'m', {1388.25, 5980.25}}, +{'c', {1388.25, 5980.24, 1392.6, 5905.6, 1419.25, 5888.25}}, +{'c', {1445.4, 5870.4, 1432.2, 5879.2, 1397.25, 5892.25}}, +{'y', {1361.8, 5905.6, 1375.25, 5914.25}}, +{'c', {1375.24, 5914.24, 1344.2, 5910, 1370.25, 5888.25}}, +{'c', {1397, 5866, 1436.6, 5839.6, 1419.25, 5839.25}}, +{'c', {1401.4, 5839.6, 1317.8, 5883.6, 1318.25, 5914.25}}, +{'y', {1317.8, 5945.2, 1307.25, 5990.25}}, +{'v', {1318.9, 5999.1, 1371.25, 5998.25}}, +{'v', {1387.1, 5990.3, 1388.25, 5980.25}}, +{'f', {}}, +{'g', {1}}, +{'m', {1379.25, 5776.25}}, +{'v', {1285.68, 5806.5, 1082.25, 5769.25}}, +{'c', {1082.25, 5769.24, 1181.74, 5792.05, 1388.25, 5767.25}}, +{'y', {1501.5, 5753.25, 1379.25, 5776.25}}, +{'f', {}}, +{'w', {1.1}}, +{'G', {0}}, +{'m', {1379.25, 5776.25}}, +{'v', {1285.68, 5806.5, 1082.25, 5769.25}}, +{'c', {1082.25, 5769.24, 1181.74, 5792.05, 1388.25, 5767.25}}, +{'y', {1501.5, 5753.25, 1379.25, 5776.25}}, +{'h', {}}, +{'S', {}}, +{'m', {1402.25, 5776.25}}, +{'v', {1311.77, 5815.28, 1106.25, 5795.25}}, +{'c', {1106.24, 5795.24, 1206.97, 5809.86, 1410.25, 5767.25}}, +{'y', {1522.18, 5743.56, 1402.25, 5776.25}}, +{'f', {}}, +{'m', {1402.25, 5776.25}}, +{'v', {1311.77, 5815.28, 1106.25, 5795.25}}, +{'c', {1106.24, 5795.24, 1206.97, 5809.86, 1410.25, 5767.25}}, +{'y', {1522.18, 5743.56, 1402.25, 5776.25}}, +{'h', {}}, +{'S', {}}, +{'m', {1422.25, 5775.25}}, +{'v', {1334.56, 5820.89, 1128.25, 5816.25}}, +{'c', {1128.24, 5816.25, 1229.66, 5823.3, 1429.25, 5765.25}}, +{'y', {1539.05, 5733.72, 1422.25, 5775.25}}, +{'f', {}}, +{'m', {1422.25, 5775.25}}, +{'v', {1334.56, 5820.89, 1128.25, 5816.25}}, +{'c', {1128.24, 5816.25, 1229.66, 5823.3, 1429.25, 5765.25}}, +{'y', {1539.05, 5733.72, 1422.25, 5775.25}}, +{'h', {}}, +{'S', {}}, +{'m', {1436.25, 5773.25}}, +{'c', {1436.24, 5773.24, 1361.05, 5820.48, 1175.25, 5832.25}}, +{'v', {1267.16, 5830.69, 1442.25, 5763.25}}, +{'y', {1537.73, 5726.61, 1436.25, 5773.25}}, +{'f', {}}, +{'m', {1436.25, 5773.25}}, +{'c', {1436.24, 5773.24, 1361.05, 5820.48, 1175.25, 5832.25}}, +{'v', {1267.16, 5830.69, 1442.25, 5763.25}}, +{'y', {1537.73, 5726.61, 1436.25, 5773.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2704.25, 5657.25}}, +{'v', {2694.01, 5650.34, 2696.25, 5663.25}}, +{'c', {2698.73, 5676.32, 3009.02, 5821.2, 3049.25, 5818.25}}, +{'v', {2716.04, 5673.18, 2704.25, 5657.25}}, +{'f', {}}, +{'m', {2704.25, 5657.25}}, +{'v', {2694.01, 5650.34, 2696.25, 5663.25}}, +{'c', {2698.73, 5676.32, 3009.02, 5821.2, 3049.25, 5818.25}}, +{'v', {2716.04, 5673.18, 2704.25, 5657.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2667.25, 5650.25}}, +{'c', {2667.25, 5650.24, 2657.27, 5642.53, 2658.25, 5655.25}}, +{'c', {2659.93, 5668.8, 2957.89, 5837.6, 2997.25, 5838.25}}, +{'v', {2677.44, 5667.02, 2667.25, 5650.25}}, +{'f', {}}, +{'m', {2667.25, 5650.25}}, +{'c', {2667.25, 5650.24, 2657.27, 5642.53, 2658.25, 5655.25}}, +{'c', {2659.93, 5668.8, 2957.89, 5837.6, 2997.25, 5838.25}}, +{'v', {2677.44, 5667.02, 2667.25, 5650.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2631.25, 5635.25}}, +{'v', {2621.65, 5626.78, 2622.25, 5640.25}}, +{'c', {2622.57, 5653.17, 2840.56, 5823.64, 2948.25, 5844.25}}, +{'v', {2750.16, 5754.86, 2631.25, 5635.25}}, +{'f', {}}, +{'m', {2631.25, 5635.25}}, +{'v', {2621.65, 5626.78, 2622.25, 5640.25}}, +{'c', {2622.57, 5653.17, 2840.56, 5823.64, 2948.25, 5844.25}}, +{'v', {2750.16, 5754.86, 2631.25, 5635.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2599.25, 5615.25}}, +{'c', {2599.25, 5615.24, 2590.93, 5607.14, 2591.25, 5619.25}}, +{'c', {2591.76, 5630.89, 2787.94, 5784.31, 2885.25, 5803.25}}, +{'v', {2706.59, 5722.4, 2599.25, 5615.25}}, +{'f', {}}, +{'m', {2599.25, 5615.25}}, +{'c', {2599.25, 5615.24, 2590.93, 5607.14, 2591.25, 5619.25}}, +{'c', {2591.76, 5630.89, 2787.94, 5784.31, 2885.25, 5803.25}}, +{'v', {2706.59, 5722.4, 2599.25, 5615.25}}, +{'h', {}}, +{'S', {}}, +{'g', {0.8}}, +{'m', {2250.25, 3987.25}}, +{'c', {2250.25, 3987.24, 2083.4, 4013.59, 2268.25, 4031.25}}, +{'c', {2268.25, 4031.24, 2466.2, 4053.19, 2510.25, 4110.25}}, +{'c', {2510.25, 4110.24, 2659.8, 4211.59, 2690.25, 4216.25}}, +{'c', {2721.4, 4220.39, 2901.8, 4259.99, 2906.25, 4286.25}}, +{'c', {2910.6, 4312.79, 2976.6, 4339.19, 2994.25, 4330.25}}, +{'c', {3011.8, 4321.59, 3011.8, 4220.39, 2981.25, 4211.25}}, +{'c', {2950.2, 4202.79, 2743.4, 4123.59, 2673.25, 4110.25}}, +{'c', {2602.6, 4097.19, 2475, 4013.59, 2422.25, 4000.25}}, +{'y', {2369.4, 3987.19, 2250.25, 3987.25}}, +{'f', {}}, +{'g', {0}}, +{'m', {3053.25, 4398.25}}, +{'c', {3053.25, 4398.24, 3020.6, 4380.99, 3007.25, 4352.25}}, +{'c', {3007.25, 4352.24, 2937, 4235.79, 2781.25, 4200.25}}, +{'c', {2781.25, 4200.24, 2527.8, 4101.59, 2442.25, 4079.25}}, +{'c', {2442.25, 4079.24, 2294.6, 4024.59, 2213.25, 4033.25}}, +{'c', {2213.25, 4033.24, 2136.2, 4031.19, 2204.25, 4013.25}}, +{'c', {2204.25, 4013.24, 2426.6, 4035.59, 2464.25, 4055.25}}, +{'c', {2464.25, 4055.24, 2635.6, 4112.59, 2668.25, 4141.25}}, +{'c', {2701.6, 4169.79, 2901.8, 4224.79, 2926.25, 4247.25}}, +{'c', {2950.2, 4268.79, 3058, 4361.19, 3053.25, 4398.25}}, +{'f', {}}, +{'m', {2407.25, 4052.25}}, +{'v', {2465.22, 4054.46, 2452.25, 4037.25}}, +{'y', {2439.85, 4020.38, 2413.25, 4030.25}}, +{'l', {2407.25, 4052.25}}, +{'f', {}}, +{'m', {2328.25, 4037.25}}, +{'c', {2328.24, 4037.25, 2385.98, 4039.59, 2373.25, 4022.25}}, +{'y', {2360.63, 4005.5, 2333.25, 4015.25}}, +{'l', {2328.25, 4037.25}}, +{'f', {}}, +{'m', {2214.25, 4023.25}}, +{'v', {2271.28, 4025.88, 2258.25, 4009.25}}, +{'y', {2245.91, 3991.79, 2219.25, 4001.25}}, +{'l', {2214.25, 4023.25}}, +{'f', {}}, +{'m', {2130.25, 4017.25}}, +{'c', {2130.24, 4017.25, 2187.3, 4019.88, 2174.25, 4003.25}}, +{'y', {2161.94, 3985.79, 2135.25, 3995.25}}, +{'l', {2130.25, 4017.25}}, +{'f', {}}, +{'m', {2589.25, 4133.25}}, +{'v', {2666.12, 4136.67, 2649.25, 4114.25}}, +{'y', {2632.36, 4091.3, 2596.25, 4104.25}}, +{'l', {2589.25, 4133.25}}, +{'f', {}}, +{'m', {2703.25, 4182.25}}, +{'v', {2768.58, 4218.34, 2762.25, 4162.25}}, +{'y', {2759.73, 4134.54, 2710.25, 4153.25}}, +{'l', {2703.25, 4182.25}}, +{'f', {}}, +{'m', {2803.25, 4213.25}}, +{'v', {2882.23, 4253.7, 2863.25, 4193.25}}, +{'y', {2854.6, 4166.66, 2810.25, 4183.25}}, +{'l', {2803.25, 4213.25}}, +{'f', {}}, +{'m', {2908.25, 4266.25}}, +{'c', {2908.25, 4266.24, 2956.08, 4319.97, 2968.25, 4246.25}}, +{'y', {2972.27, 4218.77, 2915.25, 4237.25}}, +{'l', {2908.25, 4266.25}}, +{'f', {}}, +{'m', {2495.25, 4084.25}}, +{'v', {2552.68, 4086.53, 2540.25, 4069.25}}, +{'y', {2527.32, 4052.44, 2500.25, 4062.25}}, +{'l', {2495.25, 4084.25}}, +{'f', {}}, +{'g', {1}}, +{'m', {2887.25, 5419.25}}, +{'c', {2887.25, 5419.24, 2878.79, 5412.05, 2879.25, 5424.25}}, +{'c', {2878.77, 5435.6, 3071.1, 5581.5, 3168.25, 5596.25}}, +{'v', {2991.28, 5522.71, 2887.25, 5419.25}}, +{'f', {}}, +{'m', {2887.25, 5419.25}}, +{'c', {2887.25, 5419.24, 2878.79, 5412.05, 2879.25, 5424.25}}, +{'c', {2878.77, 5435.6, 3071.1, 5581.5, 3168.25, 5596.25}}, +{'v', {2991.28, 5522.71, 2887.25, 5419.25}}, +{'h', {}}, +{'S', {}}, +{'g', {0}}, +{'m', {2919.25, 4717.25}}, +{'c', {2919.25, 4717.24, 3161.41, 4471.19, 3262.25, 4431.25}}, +{'c', {3262.25, 4431.24, 3363.81, 4308.39, 3320.25, 4022.25}}, +{'c', {3320.25, 4022.24, 3284.61, 3938.79, 3249.25, 4167.25}}, +{'c', {3249.25, 4167.24, 3284.61, 4444.79, 3161.25, 4269.25}}, +{'v', {3069, 4377.69, 3139.25, 4374.25}}, +{'c', {3139.25, 4374.24, 3174.61, 4352.39, 3179.25, 4370.25}}, +{'c', {3183.41, 4387.59, 3095.4, 4537.19, 2906.25, 4695.25}}, +{'y', {2717, 4854, 2919.25, 4717.25}}, +{'f', {}}, +{'g', {1}}, +{'m', {2277.25, 4291.25}}, +{'v', {2274.8, 4310.59, 2294.25, 4302.25}}, +{'c', {2314.4, 4292.99, 3350.61, 4224.79, 3698.25, 3961.25}}, +{'v', {3201.01, 4215.99, 2277.25, 4291.25}}, +{'f', {}}, +{'m', {2277.25, 4291.25}}, +{'v', {2274.8, 4310.59, 2294.25, 4302.25}}, +{'c', {2314.4, 4292.99, 3350.61, 4224.79, 3698.25, 3961.25}}, +{'v', {3201.01, 4215.99, 2277.25, 4291.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2391.25, 4339.25}}, +{'c', {2391.25, 4339.24, 2389.2, 4358.99, 2409.25, 4350.25}}, +{'c', {2428.8, 4341.39, 3931.41, 4352.39, 4191.25, 3996.25}}, +{'v', {3949.01, 4273.19, 2391.25, 4339.25}}, +{'f', {}}, +{'m', {2391.25, 4339.25}}, +{'c', {2391.25, 4339.24, 2389.2, 4358.99, 2409.25, 4350.25}}, +{'c', {2428.8, 4341.39, 3931.41, 4352.39, 4191.25, 3996.25}}, +{'v', {3949.01, 4273.19, 2391.25, 4339.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2519.25, 4383.25}}, +{'c', {2519.25, 4383.24, 2516.8, 4402.99, 2536.25, 4394.25}}, +{'c', {2556.4, 4385.39, 4463.81, 4528.39, 4723.25, 4172.25}}, +{'c', {4723.24, 4172.25, 4617.81, 4453.59, 2519.25, 4383.25}}, +{'f', {}}, +{'m', {2519.25, 4383.25}}, +{'c', {2519.25, 4383.24, 2516.8, 4402.99, 2536.25, 4394.25}}, +{'c', {2556.4, 4385.39, 4463.81, 4528.39, 4723.25, 4172.25}}, +{'c', {4723.24, 4172.25, 4617.81, 4453.59, 2519.25, 4383.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2629.25, 4436.25}}, +{'v', {2626.8, 4455.79, 2646.25, 4447.25}}, +{'c', {2666.4, 4438.19, 4010.61, 4862.8, 4270.25, 4506.25}}, +{'c', {4270.25, 4506.24, 4122.81, 4777, 2629.25, 4436.25}}, +{'f', {}}, +{'m', {2629.25, 4436.25}}, +{'v', {2626.8, 4455.79, 2646.25, 4447.25}}, +{'c', {2666.4, 4438.19, 4010.61, 4862.8, 4270.25, 4506.25}}, +{'c', {4270.25, 4506.24, 4122.81, 4777, 2629.25, 4436.25}}, +{'h', {}}, +{'S', {}}, +{'m', {1991.25, 4185.25}}, +{'c', {1991.25, 4185.24, 1988.8, 4204.99, 2008.25, 4196.25}}, +{'c', {2028.4, 4187.39, 2224.2, 4163.19, 2246.25, 3736.25}}, +{'c', {2246.25, 3736.24, 2167, 4198.39, 1991.25, 4185.25}}, +{'f', {}}, +{'m', {1991.25, 4185.25}}, +{'c', {1991.25, 4185.24, 1988.8, 4204.99, 2008.25, 4196.25}}, +{'c', {2028.4, 4187.39, 2224.2, 4163.19, 2246.25, 3736.25}}, +{'c', {2246.25, 3736.24, 2167, 4198.39, 1991.25, 4185.25}}, +{'h', {}}, +{'S', {}}, +{'m', {1894.25, 4167.25}}, +{'c', {1894.25, 4167.24, 1892, 4187.39, 1912.25, 4178.25}}, +{'c', {1931.6, 4169.79, 2087.8, 4220.39, 2057.25, 3793.25}}, +{'c', {2057.25, 3793.24, 2070.2, 4180.79, 1894.25, 4167.25}}, +{'f', {}}, +{'m', {1894.25, 4167.25}}, +{'c', {1894.25, 4167.24, 1892, 4187.39, 1912.25, 4178.25}}, +{'c', {1931.6, 4169.79, 2087.8, 4220.39, 2057.25, 3793.25}}, +{'c', {2057.25, 3793.24, 2070.2, 4180.79, 1894.25, 4167.25}}, +{'h', {}}, +{'S', {}}, +{'m', {1806.25, 4163.25}}, +{'c', {1806.25, 4163.24, 1804, 4182.99, 1824.25, 4174.25}}, +{'c', {1843.6, 4165.39, 2013, 4158.79, 1876.25, 3912.25}}, +{'c', {1876.25, 3912.24, 1982.2, 4176.39, 1806.25, 4163.25}}, +{'f', {}}, +{'m', {1806.25, 4163.25}}, +{'c', {1806.25, 4163.24, 1804, 4182.99, 1824.25, 4174.25}}, +{'c', {1843.6, 4165.39, 2013, 4158.79, 1876.25, 3912.25}}, +{'c', {1876.25, 3912.24, 1982.2, 4176.39, 1806.25, 4163.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2327.25, 4651.25}}, +{'c', {2327.25, 4651.24, 2321.9, 4628.89, 2339.25, 4642.25}}, +{'c', {2515.69, 4781.16, 2872.55, 5429.08, 3485.25, 5491.25}}, +{'v', {3063.7, 5624.26, 2327.25, 4651.25}}, +{'f', {}}, +{'m', {2327.25, 4651.25}}, +{'c', {2327.25, 4651.24, 2321.9, 4628.89, 2339.25, 4642.25}}, +{'c', {2515.69, 4781.16, 2872.55, 5429.08, 3485.25, 5491.25}}, +{'v', {3063.7, 5624.26, 2327.25, 4651.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2450.25, 4670.25}}, +{'v', {2434.99, 4683.8, 2455.25, 4690.25}}, +{'c', {2476.09, 4697.55, 3712.95, 5495.08, 4145.25, 5407.25}}, +{'v', {3846.91, 5490.06, 2450.25, 4670.25}}, +{'f', {}}, +{'m', {2450.25, 4670.25}}, +{'v', {2434.99, 4683.8, 2455.25, 4690.25}}, +{'c', {2476.09, 4697.55, 3712.95, 5495.08, 4145.25, 5407.25}}, +{'v', {3846.91, 5490.06, 2450.25, 4670.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2220.25, 4603.25}}, +{'c', {2220.25, 4603.24, 2215.13, 4585.97, 2234.25, 4596.25}}, +{'c', {2332.68, 4653.03, 2372.45, 5282.35, 2852.25, 5296.25}}, +{'c', {2852.25, 5296.24, 2495.27, 5458.01, 2220.25, 4603.25}}, +{'f', {}}, +{'m', {2220.25, 4603.25}}, +{'c', {2220.25, 4603.24, 2215.13, 4585.97, 2234.25, 4596.25}}, +{'c', {2332.68, 4653.03, 2372.45, 5282.35, 2852.25, 5296.25}}, +{'c', {2852.25, 5296.24, 2495.27, 5458.01, 2220.25, 4603.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2001.25, 4482.25}}, +{'c', {2001.25, 4482.24, 1989.73, 4468.13, 2011.25, 4471.25}}, +{'c', {2123.92, 4485.57, 2359.38, 4939.55, 2848.25, 4883.25}}, +{'v', {2496.58, 5041.49, 2001.25, 4482.25}}, +{'f', {}}, +{'m', {2001.25, 4482.25}}, +{'c', {2001.25, 4482.24, 1989.73, 4468.13, 2011.25, 4471.25}}, +{'c', {2123.92, 4485.57, 2359.38, 4939.55, 2848.25, 4883.25}}, +{'v', {2496.58, 5041.49, 2001.25, 4482.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2125.25, 4549.25}}, +{'v', {2116.28, 4533.5, 2137.25, 4540.25}}, +{'c', {2244.62, 4576.39, 2388.55, 5067.12, 2879.25, 5106.25}}, +{'v', {2503.63, 5193.51, 2125.25, 4549.25}}, +{'f', {}}, +{'m', {2125.25, 4549.25}}, +{'v', {2116.28, 4533.5, 2137.25, 4540.25}}, +{'c', {2244.62, 4576.39, 2388.55, 5067.12, 2879.25, 5106.25}}, +{'v', {2503.63, 5193.51, 2125.25, 4549.25}}, +{'h', {}}, +{'S', {}}, +{'m', {1924.25, 4426.25}}, +{'v', {1914.54, 4414.5, 1932.25, 4417.25}}, +{'c', {2023.23, 4428.64, 2213.95, 4796.36, 2610.25, 4751.25}}, +{'v', {2325.08, 4878.93, 1924.25, 4426.25}}, +{'f', {}}, +{'m', {1924.25, 4426.25}}, +{'v', {1914.54, 4414.5, 1932.25, 4417.25}}, +{'c', {2023.23, 4428.64, 2213.95, 4796.36, 2610.25, 4751.25}}, +{'v', {2325.08, 4878.93, 1924.25, 4426.25}}, +{'h', {}}, +{'S', {}}, +{'m', {1840.25, 4361.25}}, +{'c', {1840.25, 4361.24, 1829.5, 4349.47, 1847.25, 4351.25}}, +{'c', {1892.84, 4354.17, 2229.86, 4718.04, 2433.25, 4547.25}}, +{'v', {2300.85, 4753.47, 1840.25, 4361.25}}, +{'f', {}}, +{'m', {1840.25, 4361.25}}, +{'c', {1840.25, 4361.24, 1829.5, 4349.47, 1847.25, 4351.25}}, +{'c', {1892.84, 4354.17, 2229.86, 4718.04, 2433.25, 4547.25}}, +{'v', {2300.85, 4753.47, 1840.25, 4361.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2605.25, 4712.25}}, +{'v', {2592.19, 4727.02, 2613.25, 4731.25}}, +{'c', {2634.7, 4735.48, 3962.43, 5370.33, 4380.25, 5229.25}}, +{'v', {4094.67, 5348.41, 2605.25, 4712.25}}, +{'f', {}}, +{'m', {2605.25, 4712.25}}, +{'v', {2592.19, 4727.02, 2613.25, 4731.25}}, +{'c', {2634.7, 4735.48, 3962.43, 5370.33, 4380.25, 5229.25}}, +{'v', {4094.67, 5348.41, 2605.25, 4712.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2237.25, 4005.25}}, +{'v', {2235.2, 4024.59, 2255.25, 4016.25}}, +{'c', {2274.8, 4006.99, 2444.2, 4000.39, 2308.25, 3754.25}}, +{'v', {2413.4, 4017.99, 2237.25, 4005.25}}, +{'f', {}}, +{'m', {2237.25, 4005.25}}, +{'v', {2235.2, 4024.59, 2255.25, 4016.25}}, +{'c', {2274.8, 4006.99, 2444.2, 4000.39, 2308.25, 3754.25}}, +{'v', {2413.4, 4017.99, 2237.25, 4005.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2352.25, 4022.25}}, +{'c', {2352.25, 4022.24, 2349.6, 4042.19, 2369.25, 4033.25}}, +{'c', {2389.2, 4024.59, 2585, 4000.39, 2607.25, 3573.25}}, +{'c', {2607.25, 3573.24, 2527.8, 4035.59, 2352.25, 4022.25}}, +{'f', {}}, +{'m', {2352.25, 4022.25}}, +{'c', {2352.25, 4022.24, 2349.6, 4042.19, 2369.25, 4033.25}}, +{'c', {2389.2, 4024.59, 2585, 4000.39, 2607.25, 3573.25}}, +{'c', {2607.25, 3573.24, 2527.8, 4035.59, 2352.25, 4022.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2426.25, 4040.25}}, +{'v', {2424.4, 4059.79, 2444.25, 4051.25}}, +{'c', {2464, 4042.19, 2734.6, 4004.79, 2994.25, 3648.25}}, +{'c', {2994.25, 3648.24, 2602.6, 4053.19, 2426.25, 4040.25}}, +{'f', {}}, +{'m', {2426.25, 4040.25}}, +{'v', {2424.4, 4059.79, 2444.25, 4051.25}}, +{'c', {2464, 4042.19, 2734.6, 4004.79, 2994.25, 3648.25}}, +{'c', {2994.25, 3648.24, 2602.6, 4053.19, 2426.25, 4040.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2510.25, 4062.25}}, +{'v', {2505.66, 4081.64, 2526.25, 4075.25}}, +{'c', {2547.25, 4069.46, 2772.02, 4090.34, 3191.25, 3714.25}}, +{'c', {3191.25, 3714.24, 2683.16, 4098.78, 2510.25, 4062.25}}, +{'f', {}}, +{'m', {2510.25, 4062.25}}, +{'v', {2505.66, 4081.64, 2526.25, 4075.25}}, +{'c', {2547.25, 4069.46, 2772.02, 4090.34, 3191.25, 3714.25}}, +{'c', {3191.25, 3714.24, 2683.16, 4098.78, 2510.25, 4062.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2625.25, 4110.25}}, +{'c', {2625.24, 4110.24, 2620.07, 4130.04, 2641.25, 4124.25}}, +{'c', {2661.64, 4117.86, 3044.82, 4068.34, 3613.25, 3683.25}}, +{'c', {3613.25, 3683.24, 2797.56, 4147.18, 2625.25, 4110.25}}, +{'f', {}}, +{'m', {2625.25, 4110.25}}, +{'c', {2625.24, 4110.24, 2620.07, 4130.04, 2641.25, 4124.25}}, +{'c', {2661.64, 4117.86, 3044.82, 4068.34, 3613.25, 3683.25}}, +{'c', {3613.25, 3683.24, 2797.56, 4147.18, 2625.25, 4110.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2180.25, 4238.25}}, +{'v', {2178, 4257.79, 2198.25, 4249.25}}, +{'c', {2217.6, 4240.19, 2805, 4211.59, 3139.25, 3917.25}}, +{'v', {2755.49, 4189.31, 2180.25, 4238.25}}, +{'f', {}}, +{'m', {2180.25, 4238.25}}, +{'v', {2178, 4257.79, 2198.25, 4249.25}}, +{'c', {2217.6, 4240.19, 2805, 4211.59, 3139.25, 3917.25}}, +{'v', {2755.49, 4189.31, 2180.25, 4238.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2070.25, 4194.25}}, +{'v', {2068, 4213.79, 2088.25, 4205.25}}, +{'c', {2107.6, 4196.19, 2378.2, 4158.79, 2638.25, 3802.25}}, +{'c', {2638.25, 3802.24, 2246.2, 4207.19, 2070.25, 4194.25}}, +{'f', {}}, +{'m', {2070.25, 4194.25}}, +{'v', {2068, 4213.79, 2088.25, 4205.25}}, +{'c', {2107.6, 4196.19, 2378.2, 4158.79, 2638.25, 3802.25}}, +{'c', {2638.25, 3802.24, 2246.2, 4207.19, 2070.25, 4194.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2744.25, 4161.25}}, +{'v', {2738.32, 4180.35, 2759.25, 4176.25}}, +{'c', {2780.69, 4171.25, 3166.46, 4149.95, 3762.25, 3808.25}}, +{'v', {2907.48, 4186.25, 2744.25, 4161.25}}, +{'f', {}}, +{'m', {2744.25, 4161.25}}, +{'v', {2738.32, 4180.35, 2759.25, 4176.25}}, +{'c', {2780.69, 4171.25, 3166.46, 4149.95, 3762.25, 3808.25}}, +{'v', {2907.48, 4186.25, 2744.25, 4161.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2837.25, 4188.25}}, +{'v', {2830.71, 4206.75, 2852.25, 4202.25}}, +{'c', {2873.09, 4197.65, 3258.86, 4176.36, 3854.25, 3834.25}}, +{'v', {3006.48, 4217.05, 2837.25, 4188.25}}, +{'f', {}}, +{'m', {2837.25, 4188.25}}, +{'v', {2830.71, 4206.75, 2852.25, 4202.25}}, +{'c', {2873.09, 4197.65, 3258.86, 4176.36, 3854.25, 3834.25}}, +{'v', {3006.48, 4217.05, 2837.25, 4188.25}}, +{'h', {}}, +{'S', {}}, +{'m', {2942.25, 4243.25}}, +{'v', {2936.32, 4261.75, 2957.25, 4257.25}}, +{'c', {2978.69, 4252.65, 3452.46, 4204.95, 4439.25, 3827.25}}, +{'v', {3112.09, 4272.05, 2942.25, 4243.25}}, +{'f', {}}, +{'m', {2942.25, 4243.25}}, +{'v', {2936.32, 4261.75, 2957.25, 4257.25}}, +{'c', {2978.69, 4252.65, 3452.46, 4204.95, 4439.25, 3827.25}}, +{'v', {3112.09, 4272.05, 2942.25, 4243.25}}, +{'h', {}}, +{'S', {}}, +{'g', {0}}, +{'m', {1718.25, 4167.25}}, +{'c', {1718.25, 4167.24, 1775.4, 4171.99, 1762.25, 4154.25}}, +{'y', {1749, 4136.79, 1722.25, 4145.25}}, +{'l', {1718.25, 4167.25}}, +{'f', {}}, +{'m', {2057.25, 4013.25}}, +{'c', {2057.25, 4013.24, 2114.2, 4017.99, 2101.25, 4000.25}}, +{'y', {2087.8, 3982.79, 2061.25, 3991.25}}, +{'l', {2057.25, 4013.25}}, +{'f', {}}, +{'m', {1960.25, 4018.25}}, +{'v', {2017.4, 4022.39, 2004.25, 4005.25}}, +{'y', {1991, 3987.19, 1964.25, 3996.25}}, +{'l', {1960.25, 4018.25}}, +{'f', {}}, +{'m', {911.25, 4148.25}}, +{'v', {966.633, 4133.44, 948.246, 4121.25}}, +{'y', {930.168, 4108.81, 908.246, 4126.25}}, +{'l', {911.25, 4148.25}}, +{'f', {}}, +{'m', {933.25, 4253.25}}, +{'c', {933.25, 4253.24, 988.633, 4239.04, 970.246, 4226.25}}, +{'y', {952.168, 4214.41, 930.246, 4231.25}}, +{'l', {933.25, 4253.25}}, +{'f', {}}, +{'m', {854.246, 4302.25}}, +{'v', {909.434, 4287.44, 891.246, 4275.25}}, +{'y', {872.969, 4262.81, 851.25, 4280.25}}, +{'l', {854.246, 4302.25}}, +{'f', {}}, +{'g', {0.8}}, +{'m', {1731.25, 3477.25}}, +{'v', {1713.8, 3476.79, 1670.25, 3455.25}}, +{'c', {1647.8, 3454.79, 1524.6, 3415.19, 1463.25, 3305.25}}, +{'c', {1463.24, 3305.24, 1599.4, 3410.79, 1731.25, 3477.25}}, +{'f', {}}, +{'m', {2696.25, 2457.25}}, +{'c', {2697.82, 2454.84, 2698.43, 2450.22, 2701.25, 2450.25}}, +{'c', {2708.88, 2449.6, 2722.44, 2446.29, 2721.25, 2453.25}}, +{'c', {2711.74, 2500.65, 2702.12, 2554.95, 2656.25, 2575.25}}, +{'c', {2649.63, 2577.9, 2633.77, 2573.36, 2633.25, 2563.25}}, +{'c', {2631.56, 2546.87, 2630.27, 2532.03, 2633.25, 2516.25}}, +{'c', {2636.97, 2500.27, 2660.71, 2500.16, 2671.25, 2515.25}}, +{'c', {2681.09, 2497, 2685.85, 2476.72, 2696.25, 2457.25}}, +{'f', {}}, +{'m', {2574.25, 2405.25}}, +{'c', {2582.64, 2390.06, 2581.25, 2369.61, 2597.25, 2363.25}}, +{'c', {2604.95, 2360.19, 2625.3, 2370.91, 2620.25, 2382.25}}, +{'c', {2611.88, 2405.4, 2607.61, 2430.03, 2592.25, 2450.25}}, +{'c', {2590.14, 2452.83, 2592.77, 2458.69, 2591.25, 2462.25}}, +{'c', {2584.48, 2475.72, 2571.89, 2483.8, 2556.25, 2480.25}}, +{'c', {2544.13, 2455.95, 2556.76, 2432.54, 2573.25, 2413.25}}, +{'c', {2575, 2412.06, 2573.17, 2407.93, 2574.25, 2405.25}}, +{'f', {}}, +{'m', {2139.25, 2462.25}}, +{'c', {2137.61, 2466.84, 2137.19, 2471.94, 2139.25, 2475.25}}, +{'c', {2144.84, 2486.83, 2153.57, 2499.17, 2150.25, 2510.25}}, +{'c', {2146.03, 2522.03, 2133.76, 2520.03, 2126.25, 2514.25}}, +{'c', {2114.12, 2502.84, 2113.58, 2482.75, 2106.25, 2468.25}}, +{'c', {2103.82, 2463.55, 2104.39, 2457.25, 2099.25, 2453.25}}, +{'c', {2094.32, 2448.32, 2089.48, 2431.55, 2090.25, 2425.25}}, +{'c', {2091.12, 2421.83, 2089.11, 2312.84, 2091.25, 2315.25}}, +{'c', {2098.36, 2323.8, 2131.91, 2430.88, 2132.25, 2440.25}}, +{'c', {2133.29, 2448.73, 2141.35, 2453.84, 2139.25, 2462.25}}, +{'f', {}}, +{'m', {1857.25, 2537.25}}, +{'c', {1885.75, 2564.9, 1916.44, 2597.47, 1911.25, 2638.25}}, +{'c', {1910.3, 2649.04, 1890.78, 2643.24, 1888.25, 2634.25}}, +{'c', {1879.9, 2594.06, 1857.47, 2564.79, 1829.25, 2538.25}}, +{'c', {1805.07, 2515.96, 1784.58, 2446.18, 1782.25, 2440.25}}, +{'c', {1822.66, 2498.4, 1847.49, 2528.61, 1857.25, 2537.25}}, +{'f', {}}, +{'m', {1707.25, 2603.25}}, +{'c', {1713.21, 2607.08, 1709.89, 2612.48, 1712.25, 2616.25}}, +{'c', {1722.76, 2632.18, 1737.07, 2645.88, 1737.25, 2665.25}}, +{'c', {1737.3, 2668.04, 1733.15, 2671.45, 1729.25, 2669.25}}, +{'c', {1726.41, 2666.96, 1722.65, 2665.53, 1721.25, 2664.25}}, +{'c', {1699.14, 2637.16, 1683.73, 2607.78, 1668.25, 2577.25}}, +{'c', {1665.85, 2572.92, 1653.27, 2523.52, 1656.25, 2522.25}}, +{'c', {1659.34, 2521.36, 1678.26, 2567.35, 1680.25, 2568.25}}, +{'c', {1694.46, 2576.15, 1694.59, 2593.91, 1707.25, 2603.25}}, +{'f', {}}, +{'m', {1891.25, 2388.25}}, +{'c', {1896.41, 2397.59, 1913.75, 2410.81, 1912.25, 2420.25}}, +{'c', {1911.2, 2430.8, 1916.35, 2446.67, 1906.25, 2439.25}}, +{'c', {1892.49, 2428.68, 1854.55, 2413.84, 1851.25, 2350.25}}, +{'c', {1851.3, 2343.66, 1884.01, 2373.29, 1891.25, 2388.25}}, +{'f', {}}, +{'m', {2050.25, 2577.25}}, +{'c', {2054.8, 2584.34, 2062.59, 2579.13, 2067.25, 2582.25}}, +{'c', {2074.76, 2586.12, 2081.48, 2592.25, 2084.25, 2599.25}}, +{'c', {2095.37, 2623.03, 2114.82, 2643.05, 2116.25, 2669.25}}, +{'c', {2100, 2684.84, 2092.53, 2662.4, 2085.25, 2652.25}}, +{'c', {2071.05, 2669.9, 2060.05, 2649.29, 2046.25, 2643.25}}, +{'c', {2044.98, 2643.27, 2042.64, 2647.09, 2042.25, 2647.25}}, +{'c', {2028.87, 2641.95, 2021.39, 2630.06, 2010.25, 2621.25}}, +{'c', {2008.53, 2620, 2004.08, 2621.98, 2002.25, 2620.25}}, +{'c', {1995.29, 2613.85, 1984.59, 2610.27, 1981.25, 2603.25}}, +{'c', {1969.3, 2572.99, 1934.75, 2549.95, 1914.25, 2467.25}}, +{'c', {1918.19, 2456.99, 1963.63, 2540.05, 1969.25, 2547.25}}, +{'c', {1978.18, 2560.88, 1979.48, 2529.46, 1993.25, 2537.25}}, +{'c', {1994.08, 2537.08, 1996.12, 2534.46, 1997.25, 2533.25}}, +{'c', {1999.73, 2536.07, 2002.13, 2538.59, 2006.25, 2537.25}}, +{'c', {2006.4, 2541.79, 2004.93, 2547.9, 2007.25, 2549.25}}, +{'c', {2020.74, 2560.4, 2019.82, 2572.15, 2028.25, 2586.25}}, +{'c', {2033.44, 2577.11, 2045.11, 2585.02, 2050.25, 2577.25}}, +{'f', {}}, +{'m', {2708.25, 1978.25}}, +{'v', {2789.6, 2202.99, 2741.25, 2326.25}}, +{'v', {2866.6, 2088.59, 2816.25, 1965.25}}, +{'v', {2811.6, 2079.79, 2767.25, 2135.25}}, +{'v', {2723.6, 1993.99, 2708.25, 1978.25}}, +{'f', {}}, +{'m', {2545.25, 2007.25}}, +{'v', {2604.8, 2103.99, 2517.25, 2306.25}}, +{'v', {2508, 2081.99, 2433.25, 1961.25}}, +{'v', {2589.4, 2183.19, 2545.25, 2007.25}}, +{'f', {}}, +{'m', {2435.25, 2029.25}}, +{'v', {2433.2, 2249.19, 2437.25, 2282.25}}, +{'v', {2395.8, 2099.59, 2283.25, 1994.25}}, +{'v', {2442, 2125.99, 2435.25, 2029.25}}, +{'f', {}}, +{'m', {2330.25, 2381.25}}, +{'v', {2395.8, 2231.59, 2286.25, 2029.25}}, +{'v', {2356.2, 2163.39, 2303.25, 2240.25}}, +{'v', {2332, 2277.79, 2330.25, 2381.25}}, +{'f', {}}, +{'m', {2118.25, 2033.25}}, +{'v', {2107.6, 2205.19, 2127.25, 2229.25}}, +{'v', {2129.6, 2299.79, 2125.25, 2311.25}}, +{'v', {2169.2, 2378.99, 2171.25, 2297.25}}, +{'v', {2186.8, 2211.79, 2217.25, 2161.25}}, +{'v', {2257.2, 2101.79, 2255.25, 2031.25}}, +{'v', {2145, 2363.59, 2118.25, 2033.25}}, +{'f', {}}, +{'m', {2079.25, 2337.25}}, +{'v', {2006.4, 2218.39, 1986.25, 2009.25}}, +{'v', {1971.2, 2077.59, 2013.25, 2236.25}}, +{'v', {2059.2, 2405.39, 2079.25, 2337.25}}, +{'f', {}}, +{'m', {1839.25, 2119.25}}, +{'v', {1894.2, 2178.79, 1909.25, 2234.25}}, +{'v', {1949.2, 2407.59, 1879.25, 2313.25}}, +{'v', {1881, 2224.99, 1791.25, 2143.25}}, +{'v', {1843.6, 2169.99, 1839.25, 2119.25}}, +{'f', {}}, +{'m', {1775.25, 2174.25}}, +{'v', {1812.8, 2365.79, 1821.25, 2374.25}}, +{'v', {1841.4, 2411.99, 1810.25, 2377.25}}, +{'v', {1713.8, 2165.59, 1670.25, 2093.25}}, +{'v', {1757.8, 2194.19, 1775.25, 2174.25}}, +{'f', {}}, +{'m', {1711.25, 2357.25}}, +{'c', {1711.24, 2357.25, 1839.2, 2603.39, 1599.25, 2319.25}}, +{'v', {1720.4, 2427.39, 1711.25, 2357.25}}, +{'f', {}}, +{'m', {1542.25, 2517.25}}, +{'v', {1595, 2724.39, 1623.25, 2722.25}}, +{'c', {1623.24, 2722.24, 1713.8, 2821.19, 1641.25, 2704.25}}, +{'c', {1641.25, 2704.24, 1575.2, 2598.99, 1582.25, 2491.25}}, +{'v', {1575.2, 2596.79, 1542.25, 2517.25}}, +{'f', {}}, +{'m', {5175.25, 2643.25}}, +{'v', {5043.5, 2753, 5016.25, 2791.25}}, +{'v', {5164.5, 2588, 5164.25, 2511.25}}, +{'v', {5192, 2593.5, 5175.25, 2643.25}}, +{'f', {}}, +{'m', {5236.25, 2885.25}}, +{'v', {5005, 3050, 4966.25, 3132.25}}, +{'v', {5258, 2808, 5258.25, 2758.25}}, +{'v', {5263.5, 2857.5, 5236.25, 2885.25}}, +{'f', {}}, +{'m', {5423.25, 4579.25}}, +{'v', {5291, 4667, 5274.25, 4645.25}}, +{'v', {5390, 4573.5, 5417.25, 4480.25}}, +{'v', {5401, 4579, 5423.25, 4579.25}}, +{'f', {}}, +{'m', {5516.25, 3693.25}}, +{'l', {5324.25, 3825.25}}, +{'v', {5533, 3638.5, 5538.25, 3594.25}}, +{'l', {5516.25, 3693.25}}, +{'f', {}}, +{'w', {11}}, +{'m', {1218.25, 3941.25}}, +{'l', {1460.25, 3889.25}}, +{'S', {}}, +{'m', {1771.25, 2159.25}}, +{'v', {1765.5, 2197.5, 1666.25, 2082.25}}, +{'S', {}}, +{'m', {1831.25, 2104.25}}, +{'v', {1853.5, 2175.5, 1782.25, 2126.25}}, +{'S', {}}, +{'m', {2425.25, 2010.25}}, +{'v', {2442, 2131.5, 2315.25, 1988.25}}, +{'S', {}}, +{'!', {}} diff --git a/tests/zapf.raw b/tests/zapf.raw Binary files differnew file mode 100644 index 00000000..76987ada --- /dev/null +++ b/tests/zapf.raw diff --git a/tree/blend.c b/tree/blend.c new file mode 100644 index 00000000..d2154240 --- /dev/null +++ b/tree/blend.c @@ -0,0 +1,53 @@ +#include <fitz.h> + +fz_error * +fz_newblend(fz_node **nodep, fz_blendkind b, int k, int i) +{ + fz_blend *node; + + node = fz_malloc(sizeof (fz_blend)); + if (!node) + return fz_outofmem; + *nodep = (fz_node*)node; + + fz_initnode((fz_node*)node, FZ_NBLEND); + node->child = nil; + node->mode = b; + node->knockout = k; + node->isolated = i; + + return nil; +} + +void +fz_freeblend(fz_blend *node) +{ + if (node->child) + fz_freenode(node->child); + fz_free(node); +} + +fz_rect +fz_boundblend(fz_blend *node, fz_matrix ctm) +{ + fz_node *child; + fz_rect bbox; + fz_rect r; + + bbox = FZ_INFRECT; + + for (child = node->child; child; child = child->next) + { + r = fz_boundnode(child, ctm); + if (r.max.x >= r.min.x) + { + if (bbox.max.x >= bbox.min.x) + bbox = fz_mergerects(r, bbox); + else + bbox = r; + } + } + + return bbox; +} + diff --git a/tree/cache.c b/tree/cache.c new file mode 100644 index 00000000..f4544844 --- /dev/null +++ b/tree/cache.c @@ -0,0 +1,98 @@ +#include <fitz.h> + +static int +hash(fz_cachekey *k) +{ + int h; + int i; + + h = (int)k->tree ^ (int)k->node ^ k->tag; + + /* http://www.cs.yorku.ca/~oz/hash.html -- sdbm */ + for (i = 0; i < k->len; i++) + h = k->key[i] + (h << 6) + (h << 16) - h; + + return h; +} + +static int +equal(fz_cachekey *a, fz_cachekey *b) +{ + if (a->tree != b->tree) return 0; + if (a->node != b->node) return 0; + if (a->tag != b->tag) return 0; + if (a->len != b->len) return 0; + return memcmp(a->key, b->key, a->len) == 0; +} + +fz_cache * +fz_newcache(int maxentries, int maxdatasize) +{ + fz_cache *cache; + int i; + + cache = fz_malloc(sizeof(fz_cache)); + if (!cache) return nil; + + cache->maxsize = maxdatasize; + cache->cursize = 0; + cache->len = maxentries; + cache->table = fz_malloc(sizeof(fz_cachebucket) * maxentries); + if (!cache->table) { + fz_free(cache); + return nil; + } + + for (i = 0; i < cache->len; i++) { + cache->table[i].val = nil; + } + + return cache; +} + +void * +fz_findincache(fz_cache *cache, fz_cachekey *key) +{ + int h; + int i; + + h = hash(key); + + i = h % cache->len; + if (equal(key, &cache->table[i].key)) + return cache->table[i].val; + return nil; +} + +int +fz_insertincache(fz_cache *cache, fz_cachekey *key, void *data, int size) +{ + int h; + int i; + + h = hash(key); + i = h % cache->len; + + if (cache->table[i].val) + fz_free(cache->table[i].val); + cache->table[i].key = *key; + + return FZ_OKAY; +} + +int +fz_evictfromcache(fz_cache *cache, fz_cachekey *key) +{ + /* TODO */ + return FZ_OKAY; +} + +void +fz_freecache(fz_cache *cache) +{ + /* FIXME evict everything from cache first? */ + if (cache->table) + fz_free(cache->table); + fz_free(cache); +} + diff --git a/tree/cmap.c b/tree/cmap.c new file mode 100644 index 00000000..21b08274 --- /dev/null +++ b/tree/cmap.c @@ -0,0 +1,452 @@ +#include <fitz.h> + +typedef struct fz_range_s fz_range; + +enum { MAXCODESPACE = 10 }; +enum { SINGLE, RANGE, LOOKUP }; + +struct fz_range_s +{ + int low; + int high; + int flag; + int offset; +}; + +struct fz_cmap_s +{ + char cmapname[32]; + + char usecmapname[32]; + fz_cmap *usecmap; + + int wmode; + + int ncspace; + struct { + int n; + unsigned char lo[4]; + unsigned char hi[4]; + } cspace[MAXCODESPACE]; + + int rlen, rcap; + fz_range *ranges; + + int tlen, tcap; + int *lookup; +}; + +fz_error * +fz_newcmap(fz_cmap **cmapp) +{ + fz_cmap *cmap; + + cmap = *cmapp = fz_malloc(sizeof(fz_cmap)); + if (!cmap) + return fz_outofmem; + + strcpy(cmap->cmapname, ""); + + strcpy(cmap->usecmapname, ""); + cmap->usecmap = nil; + + cmap->wmode = 0; + + cmap->ncspace = 0; + + cmap->rlen = 0; + cmap->rcap = 0; + cmap->ranges = nil; + + cmap->tlen = 0; + cmap->tcap = 0; + cmap->lookup = nil; + + return nil; +} + +void +fz_freecmap(fz_cmap *cmap) +{ + if (cmap->usecmap) + fz_freecmap(cmap->usecmap); + fz_free(cmap->ranges); + fz_free(cmap->lookup); + fz_free(cmap); +} + +char * +fz_getcmapname(fz_cmap *cmap) +{ + if (cmap->cmapname[0]) + return cmap->cmapname; + return nil; +} + +void +fz_setcmapname(fz_cmap *cmap, char *cmapname) +{ + strlcpy(cmap->cmapname, cmapname, sizeof cmap->cmapname); +} + +char * +fz_getusecmapname(fz_cmap *cmap) +{ + if (cmap->usecmapname[0]) + return cmap->usecmapname; + return nil; +} + +void +fz_setusecmapname(fz_cmap *cmap, char *usecmap) +{ + strlcpy(cmap->usecmapname, usecmap, sizeof cmap->usecmapname); +} + +fz_cmap * +fz_getusecmap(fz_cmap *cmap) +{ + return cmap->usecmap; +} + +void +fz_setusecmap(fz_cmap *cmap, fz_cmap *usecmap) +{ + int i; + + cmap->usecmap = usecmap; + + if (cmap->ncspace == 0) + { + cmap->ncspace = usecmap->ncspace; + for (i = 0; i < usecmap->ncspace; i++) + cmap->cspace[i] = usecmap->cspace[i]; + } +} + +void +fz_setwmode(fz_cmap *cmap, int wmode) +{ + cmap->wmode = wmode; +} + +int +fz_getwmode(fz_cmap *cmap) +{ + return cmap->wmode; +} + +fz_error * +fz_addcodespacerange(fz_cmap *cmap, unsigned lo, unsigned hi, int n) +{ + int i; + + if (cmap->ncspace + 1 == MAXCODESPACE) + return fz_throw("rangelimit: too many code space ranges"); + + cmap->cspace[cmap->ncspace].n = n; + + for (i = 0; i < n; i++) + { + int o = (n - i - 1) * 8; + cmap->cspace[cmap->ncspace].lo[i] = (lo >> o) & 0xFF; + cmap->cspace[cmap->ncspace].hi[i] = (hi >> o) & 0xFF; + } + + cmap->ncspace ++; + + return nil; +} + +fz_error * +fz_addcidrange(fz_cmap *cmap, int low, int high, int offset) +{ + if (cmap->rlen + 1 > cmap->rcap) + { + fz_range *newranges; + int newcap = cmap->rcap == 0 ? 256 : cmap->rcap * 2; + newranges = fz_realloc(cmap->ranges, newcap * sizeof(fz_range)); + if (!newranges) + return fz_outofmem; + cmap->rcap = newcap; + cmap->ranges = newranges; + } + + cmap->ranges[cmap->rlen].low = low; + cmap->ranges[cmap->rlen].high = high; + cmap->ranges[cmap->rlen].flag = high - low == 0 ? SINGLE : RANGE; + cmap->ranges[cmap->rlen].offset = offset; + cmap->rlen ++; + + return nil; +} + +static fz_error * +addlookup(fz_cmap *cmap, int value) +{ + if (cmap->tlen + 1 > cmap->tcap) + { + int newcap = cmap->tcap == 0 ? 256 : cmap->tcap * 2; + int *newlookup = fz_realloc(cmap->lookup, newcap * sizeof(int)); + if (!newlookup) + return fz_outofmem; + cmap->tcap = newcap; + cmap->lookup = newlookup; + } + + cmap->lookup[cmap->tlen++] = value; + + return nil; +} + +static int compare(const void *va, const void *vb) +{ + return ((const fz_range*)va)->low - ((const fz_range*)vb)->low; +} + +fz_error * +fz_endcidrange(fz_cmap *cmap) +{ + fz_error *err; + fz_range *newranges; + int *newlookup; + fz_range *a; /* last written range on output */ + fz_range *b; /* current range examined on input */ + + qsort(cmap->ranges, cmap->rlen, sizeof(fz_range), compare); + + a = cmap->ranges; + b = cmap->ranges + 1; + + while (b < cmap->ranges + cmap->rlen) + { + /* input contiguous */ + if (a->high + 1 == b->low) + { + /* output contiguous */ + if (a->high - a->low + a->offset + 1 == b->offset) + { + /* SR -> R and SS -> R and RR -> R and RS -> R */ + if (a->flag == SINGLE || a->flag == RANGE) + { + a->flag = RANGE; + a->high = b->high; + } + + /* LS -> L */ + else if (a->flag == LOOKUP && b->flag == SINGLE) + { + a->high = b->high; + err = addlookup(cmap, b->offset); + if (err) + return err; + } + + /* LR -> LR */ + else if (a->flag == LOOKUP && b->flag == RANGE) + { + *(++a) = *b; + } + } + + /* output separated */ + else + { + /* SS -> L */ + if (a->flag == SINGLE && b->flag == SINGLE) + { + a->flag = LOOKUP; + a->high = b->high; + + err = addlookup(cmap, a->offset); + if (err) + return err; + + err = addlookup(cmap, b->offset); + if (err) + return err; + + a->offset = cmap->tlen - 2; + } + + /* LS -> L */ + else if (a->flag == LOOKUP && b->flag == SINGLE) + { + a->high = b->high; + err = addlookup(cmap, b->offset); + if (err) + return err; + } + + /* XX -> XX */ + else + { + *(++a) = *b; + } + } + } + + /* input separated: XX -> XX */ + else + { + *(++a) = *b; + } + + b ++; + } + + cmap->rlen = a - cmap->ranges + 1; + + assert(cmap->rlen > 0); + + newranges = fz_realloc(cmap->ranges, cmap->rlen * sizeof(fz_range)); + if (!newranges) + return fz_outofmem; + cmap->rcap = cmap->rlen; + cmap->ranges = newranges; + + if (cmap->tlen) + { + newlookup = fz_realloc(cmap->lookup, cmap->tlen * sizeof(int)); + if (!newlookup) + return fz_outofmem; + cmap->tcap = cmap->tlen; + cmap->lookup = newlookup; + } + + return nil; +} + +fz_error * +fz_setcidlookup(fz_cmap *cmap, int map[256]) +{ + int i; + + cmap->rlen = cmap->rcap = 1; + cmap->ranges = fz_malloc(sizeof (fz_range)); + if (!cmap->ranges) { + return fz_outofmem; + } + + cmap->tlen = cmap->tcap = 256; + cmap->lookup = fz_malloc(sizeof (int) * 256); + if (!cmap->lookup) { + fz_free(cmap->ranges); + return fz_outofmem; + } + + cmap->ranges[0].low = 0; + cmap->ranges[0].high = 255; + cmap->ranges[0].flag = LOOKUP; + cmap->ranges[0].offset = 0; + + for (i = 0; i < 256; i++) + cmap->lookup[i] = map[i]; + + return nil; +} + +int +fz_lookupcid(fz_cmap *cmap, int cpt) +{ + int l = 0; + int r = cmap->rlen - 1; + int m; + + while (l <= r) + { + m = (l + r) >> 1; + if (cpt < cmap->ranges[m].low) + r = m - 1; + else if (cpt > cmap->ranges[m].high) + l = m + 1; + else + { + int i = cpt - cmap->ranges[m].low + cmap->ranges[m].offset; + if (cmap->ranges[m].flag == LOOKUP) + return cmap->lookup[i]; + return i; + } + } + + if (cmap->usecmap) + return fz_lookupcid(cmap->usecmap, cpt); + + return -1; +} + +char * +fz_decodecpt(fz_cmap *cmap, unsigned char *buf, int *cpt) +{ + int i, k; + + for (k = 0; k < cmap->ncspace; k++) + { + unsigned char *lo = cmap->cspace[k].lo; + unsigned char *hi = cmap->cspace[k].hi; + int n = cmap->cspace[k].n; + int c = 0; + + for (i = 0; i < n; i++) + { + if (lo[i] <= buf[i] && buf[i] <= hi[i]) + c = (c << 8) | buf[i]; + else + break; + } + + if (i == n) { + *cpt = c; + return buf + n; + } + } + + *cpt = 0; + return buf + 1; +} + +void +fz_debugcmap(fz_cmap *cmap) +{ + int i, k; + + printf("cmap $%p /%s {\n", cmap, cmap->cmapname); + + if (cmap->usecmapname[0]) + printf(" usecmap /%s\n", cmap->usecmapname); + if (cmap->usecmap) + printf(" usecmap $%p\n", cmap->usecmap); + + printf(" wmode %d\n", cmap->wmode); + + printf(" codespaces {\n"); + for (i = 0; i < cmap->ncspace; i++) + { + printf(" <"); + for (k = 0; k < cmap->cspace[i].n; k++) + printf("%02x", cmap->cspace[i].lo[k]); + printf("> <"); + for (k = 0; k < cmap->cspace[i].n; k++) + printf("%02x", cmap->cspace[i].hi[k]); + printf(">\n"); + } + printf(" }\n"); + + printf(" ranges (%d,%d) {\n", cmap->rlen, cmap->tlen); + for (i = 0; i < cmap->rlen; i++) + { + fz_range *r = &cmap->ranges[i]; + printf(" <%04x> <%04x> ", r->low, r->high); + if (r->flag == LOOKUP) + { + printf("[ "); + for (k = 0; k < r->high - r->low + 1; k++) + printf("%d ", cmap->lookup[r->offset + k]); + printf("]\n"); + } + else + printf("%d\n", r->offset); + } + printf(" }\n}\n"); +} + diff --git a/tree/debug.c b/tree/debug.c new file mode 100644 index 00000000..f5012c60 --- /dev/null +++ b/tree/debug.c @@ -0,0 +1,177 @@ +#include <fitz.h> + +static void indent(int level) +{ + while (level--) + putchar(' '); +} + +static void lispnode(fz_node *node, int level); + +static void lispmeta(fz_meta *node, int level) +{ + fz_node *child; + indent(level); + printf("(meta "); + fz_fprintcobj(stdout, node->info); + printf("\n"); + for (child = node->child; child; child = child->next) + lispnode(child, level + 1); + indent(level); + printf(")\n"); +} + +static void lispover(fz_over *node, int level) +{ + fz_node *child; + indent(level); + printf("(over\n"); + for (child = node->child; child; child = child->next) + lispnode(child, level + 1); + indent(level); + printf(")\n"); +} + +static void lispmask(fz_mask *node, int level) +{ + fz_node *child; + indent(level); + printf("(mask\n"); + for (child = node->child; child; child = child->next) + lispnode(child, level + 1); + indent(level); + printf(")\n"); +} + +static void lispblend(fz_blend *node, int level) +{ + fz_node *child; + indent(level); + printf("(blend-%d\n", node->mode); + for (child = node->child; child; child = child->next) + lispnode(child, level + 1); + indent(level); + printf(")\n"); +} + +static void lisptransform(fz_transform *node, int level) +{ + indent(level); + printf("(transform %g %g %g %g %g %g\n", + node->m.a, node->m.b, + node->m.c, node->m.d, + node->m.e, node->m.f); + lispnode(node->child, level + 1); + indent(level); + printf(")\n"); +} + +static void lispsolid(fz_solid *node, int level) +{ + indent(level); + printf("(solid %g %g %g)\n", node->r, node->g, node->b); +} + +static void lispform(fz_form *node, int level) +{ + indent(level); + printf("(form %p)\n", node->tree); +} + +static void lisppath(fz_path *node, int level) +{ + int i; + + indent(level); + + if (node->paint == FZ_STROKE) + { + printf("(path 'stroke %d %d %g %g ", + node->stroke->linecap, + node->stroke->linejoin, + node->stroke->linewidth, + node->stroke->miterlimit); + if (node->dash) + { + printf("%g '( ", node->dash->phase); + for (i = 0; i < node->dash->len; i++) + printf("%g ", node->dash->array[i]); + printf(")"); + } + else + printf("0 '()"); + } + else + { + printf("(path '%s", node->paint == FZ_FILL ? "fill" : "eofill"); + } + + printf("\n"); + fz_debugpath(node); + + indent(level); + printf(")\n"); +} + +static void lisptext(fz_text *node, int level) +{ + int i; + + indent(level); + printf("(text %s [%g %g %g %g]\n", node->font->name, + node->trm.a, node->trm.b, node->trm.c, node->trm.d); + + for (i = 0; i < node->len; i++) + { + indent(level + 1); + printf("(g %d %g %g)\n", node->els[i].g, node->els[i].x, node->els[i].y); + } + + indent(level); + printf(")\n"); +} + +static void lispimage(fz_image *node, int level) +{ + indent(level); + printf("(image %d %d %d %d '", node->w, node->h, node->n, node->bpc); + switch (node->cs) + { + case FZ_CSGRAY: printf("gray"); break; + case FZ_CSRGB: printf("rgb"); break; + case FZ_CSCMYK: printf("cmyk"); break; + default: printf("unknown"); break; + } + printf(")\n"); +} + +static void lispnode(fz_node *node, int level) +{ + if (!node) + { + indent(level); + printf("(nil)\n"); + return; + } + + switch (node->kind) + { + case FZ_NMETA: lispmeta((fz_meta*)node, level); break; + case FZ_NOVER: lispover((fz_over*)node, level); break; + case FZ_NMASK: lispmask((fz_mask*)node, level); break; + case FZ_NBLEND: lispblend((fz_blend*)node, level); break; + case FZ_NTRANSFORM: lisptransform((fz_transform*)node, level); break; + case FZ_NSOLID: lispsolid((fz_solid*)node, level); break; + case FZ_NPATH: lisppath((fz_path*)node, level); break; + case FZ_NTEXT: lisptext((fz_text*)node, level); break; + case FZ_NIMAGE: lispimage((fz_image*)node, level); break; + case FZ_NFORM: lispform((fz_form*)node, level); break; + } +} + +void +fz_debugtree(fz_tree *tree) +{ + lispnode(tree->root, 0); +} + diff --git a/tree/font.c b/tree/font.c new file mode 100644 index 00000000..9ddbad04 --- /dev/null +++ b/tree/font.c @@ -0,0 +1,275 @@ +#include <fitz.h> + +void +fz_initfont(fz_font *font, char *name) +{ + strlcpy(font->name, name, sizeof font->name); + + font->wmode = 0; + + font->bbox.min.x = 0; + font->bbox.min.y = 0; + font->bbox.max.x = 1000; + font->bbox.max.y = 1000; + + font->hmtxcap = 0; + font->vmtxcap = 0; + font->nhmtx = 0; + font->nvmtx = 0; + font->hmtx = nil; + font->vmtx = nil; + + font->dhmtx.c = 0x0000; + font->dhmtx.w = 0; + + font->dvmtx.c = 0x0000; + font->dvmtx.x = 0; + font->dvmtx.y = 880; + font->dvmtx.w = -1000; +} + +void +fz_setfontwmode(fz_font *font, int wmode) +{ + font->wmode = wmode; +} + +void +fz_setfontbbox(fz_font *font, int xmin, int ymin, int xmax, int ymax) +{ + font->bbox.min.x = xmin; + font->bbox.min.y = ymin; + font->bbox.max.x = xmax; + font->bbox.max.y = ymax; +} + +void +fz_setdefaulthmtx(fz_font *font, int w) +{ + font->dhmtx.w = w; +} + +void +fz_setdefaultvmtx(fz_font *font, int y, int w) +{ + font->dvmtx.y = y; + font->dvmtx.w = w; +} + +fz_error * +fz_addhmtx(fz_font *font, int c, int w) +{ + int newcap; + fz_hmtx *newmtx; + + if (font->nhmtx + 1 >= font->hmtxcap) + { + newcap = font->hmtxcap + 16; + newmtx = fz_realloc(font->hmtx, sizeof(fz_hmtx) * newcap); + if (!newmtx) + return fz_outofmem; + font->hmtxcap = newcap; + font->hmtx = newmtx; + } + + font->hmtx[font->nhmtx].c = c; + font->hmtx[font->nhmtx].w = w; + font->nhmtx++; + + return nil; +} + +fz_error * +fz_addvmtx(fz_font *font, int c, int x, int y, int w) +{ + int newcap; + fz_vmtx *newmtx; + + if (font->nvmtx + 1 >= font->vmtxcap) + { + newcap = font->vmtxcap + 16; + newmtx = fz_realloc(font->vmtx, sizeof(fz_vmtx) * newcap); + if (!newmtx) + return fz_outofmem; + font->vmtxcap = newcap; + font->vmtx = newmtx; + } + + font->vmtx[font->nvmtx].c = c; + font->vmtx[font->nvmtx].x = x; + font->vmtx[font->nvmtx].y = y; + font->vmtx[font->nvmtx].w = w; + font->nvmtx++; + + return nil; +} + +static int cmph(const void *a0, const void *b0) +{ + fz_hmtx *a = (fz_hmtx*)a0; + fz_hmtx *b = (fz_hmtx*)b0; + return a->c - b->c; +} + +static int cmpv(const void *a0, const void *b0) +{ + fz_vmtx *a = (fz_vmtx*)a0; + fz_vmtx *b = (fz_vmtx*)b0; + return a->c - b->c; +} + +static int uniq(void *ptr, int count, int size) +{ + char *a = ptr; + char *b = ((char*)ptr) + size; + + while (count--) + { + if (memcmp(a, b, sizeof(short)) != 0) + { + a += size; + if (a != b) + memcpy(a, b, size); + } + b += size; + } + + return (a - ((char*)ptr)) / size; +} + +fz_error * +fz_endhmtx(fz_font *font) +{ + fz_hmtx *newmtx; + + if (!font->hmtx) + return nil; + + qsort(font->hmtx, font->nhmtx, sizeof(fz_hmtx), cmph); + font->nhmtx = uniq(font->hmtx, font->nhmtx, sizeof(fz_hmtx)); + + newmtx = fz_realloc(font->hmtx, sizeof(fz_hmtx) * font->nhmtx); + if (!newmtx) + return fz_outofmem; + font->hmtxcap = font->nhmtx; + font->hmtx = newmtx; + + return nil; +} + +fz_error * +fz_endvmtx(fz_font *font) +{ + fz_vmtx *newmtx; + + if (!font->vmtx) + return nil; + + qsort(font->vmtx, font->nvmtx, sizeof(fz_vmtx), cmpv); + font->nvmtx = uniq(font->vmtx, font->nvmtx, sizeof(fz_vmtx)); + + newmtx = fz_realloc(font->vmtx, sizeof(fz_vmtx) * font->nvmtx); + if (!newmtx) + return fz_outofmem; + font->vmtxcap = font->nvmtx; + font->vmtx = newmtx; + + return nil; +} + +fz_hmtx +fz_gethmtx(fz_font *font, int gid) +{ + int l = 0; + int r = font->nhmtx; + int m; + + if (!font->hmtx) + goto notfound; + + while (l <= r) + { + m = (l + r) >> 1; + if (gid < font->hmtx[m].c) + r = m - 1; + else if (gid > font->hmtx[m].c) + l = m + 1; + else + return font->hmtx[m]; + } + +notfound: + return font->dhmtx; +} + +fz_vmtx +fz_getvmtx(fz_font *font, int gid) +{ + fz_hmtx h; + fz_vmtx v; + int l = 0; + int r = font->nvmtx; + int m; + + if (!font->vmtx) + goto notfound; + + while (l <= r) + { + m = (l + r) >> 1; + if (gid < font->vmtx[m].c) + r = m - 1; + else if (gid > font->vmtx[m].c) + l = m + 1; + else + return font->vmtx[m]; + } + +notfound: + h = fz_gethmtx(font, gid); + v = font->dvmtx; + v.x = h.w / 2; + return v; +} + +void +fz_freefont(fz_font *font) +{ + if (font->free) + font->free(font); + fz_free(font->hmtx); + fz_free(font->vmtx); + fz_free(font); +} + +void +fz_debugfont(fz_font *font) +{ + int i; + + printf("font '%s' {\n", font->name); + printf(" wmode %d\n", font->wmode); + printf(" bbox [%d %d %d %d]\n", + font->bbox.min.x, font->bbox.min.y, + font->bbox.max.x, font->bbox.max.y); + printf(" DW %d\n", font->dhmtx.w); + + printf(" W {\n"); + for (i = 0; i < font->nhmtx; i++) + printf(" <%04x> %d\n", + font->hmtx[i].c, font->hmtx[i].w); + printf(" }\n"); + + if (font->wmode) + { + printf(" DW2 [%d %d]\n", font->dvmtx.y, font->dvmtx.w); + printf(" W2 {\n"); + for (i = 0; i < font->nvmtx; i++) + printf(" <%04x> %d %d %d\n", font->vmtx[i].c, + font->vmtx[i].x, font->vmtx[i].y, font->vmtx[i].w); + printf(" }\n"); + } + + printf("}\n"); +} + diff --git a/tree/form.c b/tree/form.c new file mode 100644 index 00000000..dd45aad9 --- /dev/null +++ b/tree/form.c @@ -0,0 +1,30 @@ +#include <fitz.h> + +fz_error * +fz_newform(fz_node **nodep, fz_tree *child) +{ + fz_form *node; + + node = fz_malloc(sizeof (fz_form)); + if (!node) + return fz_outofmem; + *nodep = (fz_node*)node; + + fz_initnode((fz_node*)node, FZ_NFORM); + node->tree = child; + + return nil; +} + +void +fz_freeform(fz_form *node) +{ + fz_free(node); +} + +fz_rect +fz_boundform(fz_form *node, fz_matrix ctm) +{ + return fz_boundtree(node->tree, ctm); +} + diff --git a/tree/image.c b/tree/image.c new file mode 100644 index 00000000..7991ded3 --- /dev/null +++ b/tree/image.c @@ -0,0 +1,49 @@ +#include <fitz.h> + +fz_error * +fz_newimage(fz_node **nodep, int w, int h, int n, int bpc, int cs) +{ + fz_image *node; + + node = fz_malloc(sizeof (fz_image)); + if (!node) + return fz_outofmem; + *nodep = (fz_node*)node; + + fz_initnode((fz_node*)node, FZ_NIMAGE); + node->w = w; + node->h = h; + node->n = n; + node->bpc = bpc; + node->cs = cs; + node->data = nil; + + return nil; +} + +void +fz_freeimage(fz_image *node) +{ + fz_free(node->data); + fz_free(node); +} + +fz_rect +fz_boundimage(fz_image *node, fz_matrix ctm) +{ + fz_point ll, lr, ul, ur; + fz_rect r; + + ll = fz_transformpoint(ctm, (fz_point){0,0}); + lr = fz_transformpoint(ctm, (fz_point){1,0}); + ul = fz_transformpoint(ctm, (fz_point){0,1}); + ur = fz_transformpoint(ctm, (fz_point){1,1}); + + r.min.x = MIN4(ll.x, lr.x, ul.x, ur.x); + r.min.y = MIN4(ll.y, lr.y, ul.y, ur.y); + r.max.x = MAX4(ll.x, lr.x, ul.x, ur.x); + r.max.y = MAX4(ll.y, lr.y, ul.y, ur.y); + + return r; +} + diff --git a/tree/mask.c b/tree/mask.c new file mode 100644 index 00000000..62a469ff --- /dev/null +++ b/tree/mask.c @@ -0,0 +1,50 @@ +#include <fitz.h> + +fz_error * +fz_newmask(fz_node **nodep) +{ + fz_mask *node; + + node = fz_malloc(sizeof (fz_mask)); + if (!node) + return fz_outofmem; + *nodep = (fz_node*)node; + + fz_initnode((fz_node*)node, FZ_NMASK); + node->child = nil; + + return nil; +} + +void +fz_freemask(fz_mask *node) +{ + if (node->child) + fz_freenode(node->child); + fz_free(node); +} + +fz_rect +fz_boundmask(fz_mask* node, fz_matrix ctm) +{ + fz_node *child; + fz_rect bbox; + fz_rect r; + + bbox = FZ_INFRECT; + + for (child = node->child; child; child = child->next) + { + r = fz_boundnode(child, ctm); + if (r.max.x >= r.min.x) + { + if (bbox.max.x >= bbox.min.x) + bbox = fz_intersectrects(r, bbox); + else + bbox = r; + } + } + + return bbox; +} + diff --git a/tree/meta.c b/tree/meta.c new file mode 100644 index 00000000..4d3a2a74 --- /dev/null +++ b/tree/meta.c @@ -0,0 +1,37 @@ +#include <fitz.h> + +fz_error * +fz_newmeta(fz_node **nodep, fz_obj *info) +{ + fz_meta *node; + + node = fz_malloc(sizeof (fz_meta)); + if (!node) + return fz_outofmem; + *nodep = (fz_node*)node; + + fz_initnode((fz_node*)node, FZ_NMETA); + node->info = fz_keepobj(info); + node->child = nil; + + return nil; +} + +void +fz_freemeta(fz_meta *node) +{ + if (node->child) + fz_freenode(node->child); + if (node->info) + fz_dropobj(node->info); + fz_free(node); +} + +fz_rect +fz_boundmeta(fz_meta *node, fz_matrix ctm) +{ + if (!node->child) + return FZ_INFRECT; + return fz_boundnode(node->child, ctm); +} + diff --git a/tree/node.c b/tree/node.c new file mode 100644 index 00000000..8b9d48cc --- /dev/null +++ b/tree/node.c @@ -0,0 +1,148 @@ +#include <fitz.h> + +void fz_freeover(fz_over* node); +void fz_freemask(fz_mask* node); +void fz_freeblend(fz_blend* node); +void fz_freetransform(fz_transform* node); +void fz_freeform(fz_form* node); +void fz_freesolid(fz_solid* node); +void fz_freepath(fz_path* node); +void fz_freetext(fz_text* node); +void fz_freeimage(fz_image* node); + +fz_rect fz_boundover(fz_over* node, fz_matrix ctm); +fz_rect fz_boundmask(fz_mask* node, fz_matrix ctm); +fz_rect fz_boundblend(fz_blend* node, fz_matrix ctm); +fz_rect fz_boundtransform(fz_transform* node, fz_matrix ctm); +fz_rect fz_boundform(fz_form* node, fz_matrix ctm); +fz_rect fz_boundsolid(fz_solid* node, fz_matrix ctm); +fz_rect fz_boundpath(fz_path* node, fz_matrix ctm); +fz_rect fz_boundtext(fz_text* node, fz_matrix ctm); +fz_rect fz_boundimage(fz_image* node, fz_matrix ctm); + +void +fz_initnode(fz_node *node, fz_nodekind kind) +{ + node->kind = kind; + node->parent = nil; +} + +void +fz_freenode(fz_node *node) +{ + if (node->next) + fz_freenode(node->next); + + switch (node->kind) + { + case FZ_NOVER: + fz_freeover((fz_over *) node); + break; + case FZ_NMASK: + fz_freemask((fz_mask *) node); + break; + case FZ_NBLEND: + fz_freeblend((fz_blend *) node); + break; + case FZ_NTRANSFORM: + fz_freetransform((fz_transform *) node); + break; + case FZ_NFORM: + fz_freeform((fz_form *) node); + break; + case FZ_NSOLID: + fz_freesolid((fz_solid *) node); + break; + case FZ_NPATH: + fz_freepath((fz_path *) node); + break; + case FZ_NTEXT: + fz_freetext((fz_text *) node); + break; + case FZ_NIMAGE: + fz_freeimage((fz_image *) node); + break; + } +} + +fz_rect +fz_boundnode(fz_node *node, fz_matrix ctm) +{ + switch (node->kind) + { + case FZ_NOVER: + return fz_boundover((fz_over *) node, ctm); + case FZ_NMASK: + return fz_boundmask((fz_mask *) node, ctm); + case FZ_NBLEND: + return fz_boundblend((fz_blend *) node, ctm); + case FZ_NTRANSFORM: + return fz_boundtransform((fz_transform *) node, ctm); + case FZ_NFORM: + return fz_boundform((fz_form *) node, ctm); + case FZ_NSOLID: + return fz_boundsolid((fz_solid *) node, ctm); + case FZ_NPATH: + return fz_boundpath((fz_path *) node, ctm); + case FZ_NTEXT: + return fz_boundtext((fz_text *) node, ctm); + case FZ_NIMAGE: + return fz_boundimage((fz_image *) node, ctm); + } + return FZ_INFRECT; +} + +int +fz_isover(fz_node *node) +{ + return node ? node->kind == FZ_NOVER : 0; +} + +int +fz_ismask(fz_node *node) +{ + return node ? node->kind == FZ_NMASK : 0; +} + +int +fz_isblend(fz_node *node) +{ + return node ? node->kind == FZ_NBLEND : 0; +} + +int +fz_istransform(fz_node *node) +{ + return node ? node->kind == FZ_NTRANSFORM : 0; +} + +int +fz_isform(fz_node *node) +{ + return node ? node->kind == FZ_NFORM : 0; +} + +int +fz_issolid(fz_node *node) +{ + return node ? node->kind == FZ_NSOLID : 0; +} + +int +fz_ispath(fz_node *node) +{ + return node ? node->kind == FZ_NPATH : 0; +} + +int +fz_istext(fz_node *node) +{ + return node ? node->kind == FZ_NTEXT : 0; +} + +int +fz_isimage(fz_node *node) +{ + return node ? node->kind == FZ_NIMAGE : 0; +} + diff --git a/tree/over.c b/tree/over.c new file mode 100644 index 00000000..d55bb297 --- /dev/null +++ b/tree/over.c @@ -0,0 +1,50 @@ +#include <fitz.h> + +fz_error * +fz_newover(fz_node **nodep) +{ + fz_over *node; + + node = fz_malloc(sizeof (fz_over)); + if (!node) + return fz_outofmem; + *nodep = (fz_node*)node; + + fz_initnode((fz_node*)node, FZ_NOVER); + node->child = nil; + + return nil; +} + +void +fz_freeover(fz_over *node) +{ + if (node->child) + fz_freenode(node->child); + fz_free(node); +} + +fz_rect +fz_boundover(fz_over* node, fz_matrix ctm) +{ + fz_node *child; + fz_rect bbox; + fz_rect r; + + bbox = FZ_INFRECT; + + for (child = node->child; child; child = child->next) + { + r = fz_boundnode(child, ctm); + if (r.max.x >= r.min.x) + { + if (bbox.max.x >= bbox.min.x) + bbox = fz_mergerects(r, bbox); + else + bbox = r; + } + } + + return bbox; +} + diff --git a/tree/path.c b/tree/path.c new file mode 100644 index 00000000..d21fda56 --- /dev/null +++ b/tree/path.c @@ -0,0 +1,291 @@ +#include <fitz.h> + +fz_error * +fz_newpath(fz_path **pathp) +{ + fz_path *path; + + path = *pathp = fz_malloc(sizeof(fz_path)); + if (!path) + return fz_outofmem; + + fz_initnode((fz_node*)path, FZ_NPATH); + + path->paint = FZ_FILL; + path->stroke = nil; + path->dash = nil; + path->len = 0; + path->cap = 0; + path->els = nil; + + return nil; +} + +fz_error * +fz_clonepath(fz_path **pathp, fz_path *oldpath) +{ + fz_path *path; + + path = *pathp = fz_malloc(sizeof(fz_path)); + if (!path) + return fz_outofmem; + + fz_initnode((fz_node*)path, FZ_NPATH); + + path->paint = FZ_FILL; + path->stroke = nil; + path->dash = nil; + path->len = oldpath->len; + path->cap = oldpath->len; + + path->els = fz_malloc(sizeof (fz_pathel) * path->len); + if (!path->els) { + fz_free(path); + return fz_outofmem; + } + memcpy(path->els, oldpath->els, sizeof(fz_pathel) * path->len); + + return nil; +} + +void +fz_freepath(fz_path *node) +{ + fz_free(node->stroke); + fz_free(node->dash); + fz_free(node->els); + fz_free(node); +} + +static fz_error * +growpath(fz_path *path, int n) +{ + int newcap; + fz_pathel *newels; + + while (path->len + n > path->cap) + { + newcap = path->cap + 36; + newels = fz_realloc(path->els, sizeof (fz_pathel) * newcap); + if (!newels) + return fz_outofmem; + path->cap = newcap; + path->els = newels; + } + + return nil; +} + +fz_error * +fz_moveto(fz_path *path, float x, float y) +{ + if (growpath(path, 3) != nil) + return fz_outofmem; + path->els[path->len++].k = FZ_MOVETO; + path->els[path->len++].v = x; + path->els[path->len++].v = y; + return nil; +} + +fz_error * +fz_lineto(fz_path *path, float x, float y) +{ + if (growpath(path, 3) != nil) + return fz_outofmem; + path->els[path->len++].k = FZ_LINETO; + path->els[path->len++].v = x; + path->els[path->len++].v = y; + return nil; +} + +fz_error * +fz_curveto(fz_path *path, + float x1, float y1, + float x2, float y2, + float x3, float y3) +{ + if (growpath(path, 7) != nil) + return fz_outofmem; + path->els[path->len++].k = FZ_CURVETO; + path->els[path->len++].v = x1; + path->els[path->len++].v = y1; + path->els[path->len++].v = x2; + path->els[path->len++].v = y2; + path->els[path->len++].v = x3; + path->els[path->len++].v = y3; + return nil; +} + +fz_error * +fz_curvetov(fz_path *path, float x2, float y2, float x3, float y3) +{ + float x1 = path->els[path->len-2].v; + float y1 = path->els[path->len-1].v; + return fz_curveto(path, x1, y1, x2, y2, x3, y3); +} + +fz_error * +fz_curvetoy(fz_path *path, float x1, float y1, float x3, float y3) +{ + return fz_curveto(path, x1, y1, x3, y3, x3, y3); +} + +fz_error * +fz_closepath(fz_path *path) +{ + if (growpath(path, 1) != nil) + return fz_outofmem; + path->els[path->len++].k = FZ_CLOSEPATH; + return nil; +} + +fz_error * +fz_endpath(fz_path *path, fz_pathkind paint, fz_stroke *stroke, fz_dash *dash) +{ + fz_pathel *newels; + + newels = fz_realloc(path->els, path->len * sizeof(fz_pathel)); + if (!newels) + return fz_outofmem; + path->els = newels; + + path->paint = paint; + path->stroke = stroke; + path->dash = dash; + + return nil; +} + +static inline fz_rect boundexpand(fz_rect r, fz_point p) +{ + if (r.min.x > r.max.x) + { + r.min.x = r.max.x = p.x; + r.min.y = r.max.y = p.y; + } + else + { + if (p.x < r.min.x) r.min.x = p.x; + if (p.y < r.min.y) r.min.y = p.y; + if (p.x > r.max.x) r.max.x = p.x; + if (p.y > r.max.y) r.max.y = p.y; + } + return r; +} + +fz_rect +fz_boundpath(fz_path *path, fz_matrix ctm) +{ + fz_point p; + fz_rect r = FZ_INFRECT; + int i = 0; + + while (i < path->len) + { + switch (path->els[i++].k) + { + case FZ_CURVETO: + p.x = path->els[i++].v; + p.y = path->els[i++].v; + r = boundexpand(r, fz_transformpoint(ctm, p)); + p.x = path->els[i++].v; + p.y = path->els[i++].v; + r = boundexpand(r, fz_transformpoint(ctm, p)); + case FZ_MOVETO: + case FZ_LINETO: + p.x = path->els[i++].v; + p.y = path->els[i++].v; + r = boundexpand(r, fz_transformpoint(ctm, p)); + break; + case FZ_CLOSEPATH: + break; + } + } + + if (path->paint == FZ_STROKE) + { + float miterlength = sin(path->stroke->miterlimit / 2.0); + float linewidth = path->stroke->linewidth; + float expand = MAX(miterlength, linewidth) / 2.0; + r.min.x -= expand; + r.min.y -= expand; + r.max.x += expand; + r.max.y += expand; + } + + return r; +} + +void +fz_debugpath(fz_path *path) +{ + float x, y; + int i = 0; + while (i < path->len) + { + switch (path->els[i++].k) + { + case FZ_MOVETO: + x = path->els[i++].v; + y = path->els[i++].v; + printf("%g %g m\n", x, y); + break; + case FZ_LINETO: + x = path->els[i++].v; + y = path->els[i++].v; + printf("%g %g l\n", x, y); + break; + case FZ_CURVETO: + x = path->els[i++].v; + y = path->els[i++].v; + printf("%g %g ", x, y); + x = path->els[i++].v; + y = path->els[i++].v; + printf("%g %g ", x, y); + x = path->els[i++].v; + y = path->els[i++].v; + printf("%g %g c\n", x, y); + break; + case FZ_CLOSEPATH: + printf("h\n"); + } + } + + switch (path->paint) + { + case FZ_STROKE: + printf("S\n"); + break; + case FZ_FILL: + printf("f\n"); + break; + case FZ_EOFILL: + printf("f*\n"); + break; + } +} + +fz_error * +fz_newdash(fz_dash **dashp, float phase, int len, float *array) +{ + fz_dash *dash; + int i; + + dash = *dashp = fz_malloc(sizeof(fz_dash) + sizeof(float) * len); + if (!dash) + return fz_outofmem; + + dash->len = len; + dash->phase = phase; + for (i = 0; i < len; i++) + dash->array[i] = array[i]; + + return nil; +} + +void +fz_freedash(fz_dash *dash) +{ + fz_free(dash); +} + diff --git a/tree/solid.c b/tree/solid.c new file mode 100644 index 00000000..37ff55eb --- /dev/null +++ b/tree/solid.c @@ -0,0 +1,33 @@ +#include <fitz.h> + +fz_error * +fz_newsolid(fz_node **nodep, float r, float g, float b) +{ + fz_solid *node; + + node = fz_malloc(sizeof (fz_solid)); + if (!node) + return fz_outofmem; + *nodep = (fz_node*)node; + + fz_initnode((fz_node*)node, FZ_NSOLID); + node->r = r; + node->g = g; + node->b = b; + + return nil; +} + +void +fz_freesolid(fz_solid *node) +{ + fz_free(node); +} + +fz_rect +fz_boundsolid(fz_solid *node, fz_matrix ctm) +{ + /* min > max => no bounds */ + return (fz_rect) { {1,1}, {-1,-1} }; +} + diff --git a/tree/text.c b/tree/text.c new file mode 100644 index 00000000..312e3bde --- /dev/null +++ b/tree/text.c @@ -0,0 +1,67 @@ +#include <fitz.h> + +fz_error * +fz_newtext(fz_text **textp, fz_font *font) +{ + fz_text *text; + + text = *textp = fz_malloc(sizeof(fz_text)); + if (!text) + return fz_outofmem; + + fz_initnode((fz_node*)text, FZ_NTEXT); + + text->font = font; + text->trm = fz_identity(); + text->len = 0; + text->cap = 0; + text->els = nil; + + return nil; +} + +void +fz_freetext(fz_text *text) +{ + fz_free(text->els); + fz_free(text); +}; + +fz_rect +fz_boundtext(fz_text *text, fz_matrix ctm) +{ + /* fz_rect bounds = fz_boundglyph(text->font, text->els[0], ctm); */ + return FZ_INFRECT; +} + +static fz_error * +growtext(fz_text *text, int n) +{ + int newcap; + fz_textel *newels; + + while (text->len + n > text->cap) + { + newcap = text->cap + 36; + newels = fz_realloc(text->els, sizeof (fz_textel) * newcap); + if (!newels) + return fz_outofmem; + text->cap = newcap; + text->els = newels; + } + + return nil; +} + +fz_error * +fz_addtext(fz_text *text, int g, float x, float y) +{ + if (growtext(text, 1) != nil) + return fz_outofmem; + text->els[text->len].g = g; + text->els[text->len].x = x; + text->els[text->len].y = y; + text->len++; + return nil; +} + diff --git a/tree/transform.c b/tree/transform.c new file mode 100644 index 00000000..18910d6b --- /dev/null +++ b/tree/transform.c @@ -0,0 +1,35 @@ +#include <fitz.h> + +fz_error * +fz_newtransform(fz_node **nodep, fz_matrix m) +{ + fz_transform *node; + + node = fz_malloc(sizeof (fz_transform)); + if (!node) + return fz_outofmem; + *nodep = (fz_node*)node; + + fz_initnode((fz_node*)node, FZ_NTRANSFORM); + node->child = nil; + node->m = m; + + return nil; +} + +void +fz_freetransform(fz_transform *node) +{ + if (node->child) + fz_freenode(node->child); + fz_free(node); +} + +fz_rect +fz_boundtransform(fz_transform *node, fz_matrix ctm) +{ + if (!node->child) + return FZ_INFRECT; + return fz_boundnode(node->child, fz_concat(node->m, ctm)); +} + diff --git a/tree/tree.c b/tree/tree.c new file mode 100644 index 00000000..1a96ec38 --- /dev/null +++ b/tree/tree.c @@ -0,0 +1,63 @@ +#include <fitz.h> + +fz_error * +fz_newtree(fz_tree **treep) +{ + fz_tree *tree; + + tree = *treep = fz_malloc(sizeof (fz_tree)); + if (!tree) + return fz_outofmem; + + tree->root = nil; + tree->head = nil; + + return nil; +} + +void +fz_freetree(fz_tree *tree) +{ + if (tree->root) + fz_freenode(tree->root); + fz_free(tree); +} + +fz_rect +fz_boundtree(fz_tree *tree, fz_matrix ctm) +{ + if (tree->root) + return fz_boundnode(tree->root, ctm); + return FZ_INFRECT; +} + +void +fz_insertnode(fz_node *node, fz_node *child) +{ + child->parent = node; + + if (fz_isover(node)) + { + child->next = ((fz_over*)node)->child; + ((fz_over*)node)->child = child; + } + + if (fz_ismask(node)) + { + child->next = ((fz_mask*)node)->child; + ((fz_mask*)node)->child = child; + } + + if (fz_isblend(node)) + { + child->next = ((fz_blend*)node)->child; + ((fz_blend*)node)->child = child; + } + + if (fz_istransform(node)) + { + child->next = ((fz_transform*)node)->child; + ((fz_transform*)node)->child = child; + } +} + diff --git a/util/cleanname.c b/util/cleanname.c new file mode 100644 index 00000000..e8a8ed86 --- /dev/null +++ b/util/cleanname.c @@ -0,0 +1,63 @@ +/* + * In place, rewrite name to compress multiple /, eliminate ., and process .. + */ + +#define SEP(x) ((x)=='/' || (x) == 0) + +char * +cleanname(char *name) +{ + char *p, *q, *dotdot; + int rooted; + + rooted = name[0] == '/'; + + /* + * invariants: + * p points at beginning of path element we're considering. + * q points just past the last path element we wrote (no slash). + * dotdot points just past the point where .. cannot backtrack + * any further (no slash). + */ + + p = q = dotdot = name + rooted; + + while (*p) + { + if (p[0] == '/') /* null element */ + p++; + + else if (p[0] == '.' && SEP(p[1])) + p += 1; /* don't count the separator in case it is nul */ + + else if (p[0] == '.' && p[1] == '.' && SEP(p[2])) + { + p += 2; + if (q > dotdot) { /* can backtrack */ + while (--q > dotdot && *q != '/') + ; + } else if (!rooted) { /* /.. is / but ./../ is .. */ + if (q != name) + *q++ = '/'; + *q++ = '.'; + *q++ = '.'; + dotdot = q; + } + } + + else { /* real path element */ + if (q != name + rooted) + *q++ = '/'; + while ((*q = *p) != '/' && *q != 0) + p++, q++; + } + } + + if (q == name) /* empty string is really ``.'' */ + *q++ = '.'; + + *q = '\0'; + + return name; +} + diff --git a/util/getopt.c b/util/getopt.c new file mode 100644 index 00000000..c870e206 --- /dev/null +++ b/util/getopt.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 1987 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)getopt.c 4.13 (Berkeley) 2/23/91"; +#endif /* LIBC_SCCS and not lint */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* + * get option letter from argument vector + */ +int opterr = 1, /* if error message should be printed */ + optind = 1, /* index into parent argv vector */ + optopt; /* character checked for validity */ +char *optarg; /* argument associated with option */ + +#define BADCH (int)'?' +#define EMSG "" + +int getopt(int nargc, char * const * nargv, const char *ostr) +{ + static char *place = EMSG; /* option letter processing */ + register char *oli; /* option letter list index */ + char *p; + + if (!*place) { /* update scanning pointer */ + if (optind >= nargc || *(place = nargv[optind]) != '-') { + place = EMSG; + return(EOF); + } + if (place[1] && *++place == '-') { /* found "--" */ + ++optind; + place = EMSG; + return(EOF); + } + } /* option letter okay? */ + if ((optopt = (int)*place++) == (int)':' || + !(oli = strchr(ostr, optopt))) { + /* + * if the user didn't specify '-' as an option, + * assume it means EOF. + */ + if (optopt == (int)'-') + return(EOF); + if (!*place) + ++optind; + if (opterr) { + if (!(p = strrchr(*nargv, '/'))) + p = *nargv; + else + ++p; + (void)fprintf(stderr, "%s: illegal option -- %c\n", + p, optopt); + } + return(BADCH); + } + if (*++oli != ':') { /* don't need argument */ + optarg = NULL; + if (!*place) + ++optind; + } + else { /* need an argument */ + if (*place) /* no white space */ + optarg = place; + else if (nargc <= ++optind) { /* no arg */ + place = EMSG; + if (!(p = strrchr(*nargv, '/'))) + p = *nargv; + else + ++p; + if (opterr) + (void)fprintf(stderr, + "%s: option requires an argument -- %c\n", + p, optopt); + return(BADCH); + } + else /* white space */ + optarg = nargv[optind]; + place = EMSG; + ++optind; + } + return(optopt); /* dump back option letter */ +} diff --git a/util/strlcat.c b/util/strlcat.c new file mode 100644 index 00000000..5f260abb --- /dev/null +++ b/util/strlcat.c @@ -0,0 +1,34 @@ +/* Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ + +#include <string.h> + +int strlcat(char *dst, const char *src, int siz) +{ + register char *d = dst; + register const char *s = src; + register int n = siz; + int dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (*d != '\0' && n-- != 0) + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return dlen + strlen(s); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return dlen + (s - src); /* count does not include NUL */ +} diff --git a/util/strlcpy.c b/util/strlcpy.c new file mode 100644 index 00000000..73416ae1 --- /dev/null +++ b/util/strlcpy.c @@ -0,0 +1,32 @@ +/* Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ + +#include <string.h> + +int strlcpy(char *dst, const char *src, int siz) +{ + register char *d = dst; + register const char *s = src; + register int n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} + diff --git a/util/strsep.c b/util/strsep.c new file mode 100644 index 00000000..e54903ce --- /dev/null +++ b/util/strsep.c @@ -0,0 +1,11 @@ +#include <string.h> + +char *strsep(char **stringp, const char *delim) +{ + char *ret = *stringp; + if (ret == NULL) return NULL; + if ((*stringp = strpbrk(*stringp, delim)) != NULL) + *((*stringp)++) = '\0'; + return ret; +} + |