diff options
Diffstat (limited to 'fitz')
46 files changed, 3591 insertions, 2893 deletions
diff --git a/fitz/base_context.c b/fitz/base_context.c index fe69ff8e..075a3d02 100644 --- a/fitz/base_context.c +++ b/fitz/base_context.c @@ -1,12 +1,4 @@ -#include "fitz.h" - -static fz_obj * -fz_resolve_indirect_null(fz_obj *ref) -{ - return ref; -} - -fz_obj *(*fz_resolve_indirect)(fz_obj*) = fz_resolve_indirect_null; +#include "fitz-internal.h" void fz_free_context(fz_context *ctx) @@ -130,7 +122,9 @@ fz_clone_context_internal(fz_context *ctx) if (ctx == NULL || ctx->alloc == NULL) return NULL; new_ctx = new_context_phase1(ctx->alloc, ctx->locks); - new_ctx->store = fz_store_keep(ctx); + /* Inherit AA defaults from old context. */ + fz_copy_aa_context(new_ctx, ctx); + new_ctx->store = fz_keep_store_context(ctx); new_ctx->glyph_cache = fz_keep_glyph_cache(ctx); new_ctx->font = fz_keep_font_context(ctx); return new_ctx; diff --git a/fitz/base_error.c b/fitz/base_error.c index 6b54fb69..71a32a2e 100644 --- a/fitz/base_error.c +++ b/fitz/base_error.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" /* Warning context */ @@ -46,7 +46,7 @@ void fz_warn(fz_context *ctx, char *fmt, ...) static void throw(fz_error_context *ex) { if (ex->top >= 0) { - longjmp(ex->stack[ex->top].buffer, 1); + fz_longjmp(ex->stack[ex->top].buffer, 1); } else { fprintf(stderr, "uncaught exception: %s\n", ex->message); LOGE("uncaught exception: %s\n", ex->message); diff --git a/fitz/base_geometry.c b/fitz/base_geometry.c index fc5ce517..84134179 100644 --- a/fitz/base_geometry.c +++ b/fitz/base_geometry.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" #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)) diff --git a/fitz/base_hash.c b/fitz/base_hash.c index 630f6b6a..4ba02f4d 100644 --- a/fitz/base_hash.c +++ b/fitz/base_hash.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" /* Simple hashtable with open addressing linear probe. @@ -22,6 +22,7 @@ struct fz_hash_table_s int keylen; int size; int load; + int lock; /* -1 or the lock used to protect this hash table */ fz_hash_entry *ents; }; @@ -42,7 +43,7 @@ static unsigned hash(unsigned char *s, int len) } fz_hash_table * -fz_new_hash_table(fz_context *ctx, int initialsize, int keylen) +fz_new_hash_table(fz_context *ctx, int initialsize, int keylen, int lock) { fz_hash_table *table; @@ -52,6 +53,7 @@ fz_new_hash_table(fz_context *ctx, int initialsize, int keylen) table->keylen = keylen; table->size = initialsize; table->load = 0; + table->lock = lock; fz_try(ctx) { table->ents = fz_malloc_array(ctx, table->size, sizeof(fz_hash_entry)); @@ -98,10 +100,45 @@ fz_free_hash(fz_context *ctx, fz_hash_table *table) fz_free(ctx, table); } +static void * +do_hash_insert(fz_context *ctx, fz_hash_table *table, void *key, void *val) +{ + fz_hash_entry *ents; + unsigned size; + unsigned pos; + + ents = table->ents; + size = table->size; + pos = hash(key, table->keylen) % size; + + if (table->lock >= 0) + fz_assert_lock_held(ctx, table->lock); + + while (1) + { + if (!ents[pos].val) + { + memcpy(ents[pos].key, key, table->keylen); + ents[pos].val = val; + table->load ++; + return NULL; + } + + if (memcmp(key, ents[pos].key, table->keylen) == 0) + { + fz_warn(ctx, "assert: overwrite hash slot"); + return ents[pos].val; + } + + pos = (pos + 1) % size; + } +} + static void fz_resize_hash(fz_context *ctx, fz_hash_table *table, int newsize) { fz_hash_entry *oldents = table->ents; + fz_hash_entry *newents = table->ents; int oldsize = table->size; int oldload = table->load; int i; @@ -112,7 +149,22 @@ fz_resize_hash(fz_context *ctx, fz_hash_table *table, int newsize) return; } - table->ents = fz_malloc_array(ctx, newsize, sizeof(fz_hash_entry)); + if (table->lock == FZ_LOCK_ALLOC) + fz_unlock(ctx, FZ_LOCK_ALLOC); + newents = fz_malloc_array(ctx, newsize, sizeof(fz_hash_entry)); + if (table->lock == FZ_LOCK_ALLOC) + fz_lock(ctx, FZ_LOCK_ALLOC); + if (table->lock >= 0) + { + if (table->size >= newsize) + { + /* Someone else fixed it before we could lock! */ + fz_unlock(ctx, table->lock); + fz_free(ctx, newents); + return; + } + } + table->ents = newents; memset(table->ents, 0, sizeof(fz_hash_entry) * newsize); table->size = newsize; table->load = 0; @@ -121,11 +173,15 @@ fz_resize_hash(fz_context *ctx, fz_hash_table *table, int newsize) { if (oldents[i].val) { - fz_hash_insert(ctx, table, oldents[i].key, oldents[i].val); + do_hash_insert(ctx, table, oldents[i].key, oldents[i].val); } } + if (table->lock == FZ_LOCK_ALLOC) + fz_unlock(ctx, FZ_LOCK_ALLOC); fz_free(ctx, oldents); + if (table->lock == FZ_LOCK_ALLOC) + fz_lock(ctx, FZ_LOCK_ALLOC); } void * @@ -135,6 +191,9 @@ fz_hash_find(fz_context *ctx, fz_hash_table *table, void *key) unsigned size = table->size; unsigned pos = hash(key, table->keylen) % size; + if (table->lock >= 0) + fz_assert_lock_held(ctx, table->lock); + while (1) { if (!ents[pos].val) @@ -150,37 +209,12 @@ fz_hash_find(fz_context *ctx, fz_hash_table *table, void *key) void * fz_hash_insert(fz_context *ctx, fz_hash_table *table, void *key, void *val) { - fz_hash_entry *ents; - unsigned size; - unsigned pos; - if (table->load > table->size * 8 / 10) { fz_resize_hash(ctx, table, table->size * 2); } - ents = table->ents; - size = table->size; - pos = hash(key, table->keylen) % size; - - while (1) - { - if (!ents[pos].val) - { - memcpy(ents[pos].key, key, table->keylen); - ents[pos].val = val; - table->load ++; - return NULL; - } - - if (memcmp(key, ents[pos].key, table->keylen) == 0) - { - fz_warn(ctx, "assert: overwrite hash slot"); - return ents[pos].val; - } - - pos = (pos + 1) % size; - } + return do_hash_insert(ctx, table, key, val); } void @@ -191,11 +225,14 @@ fz_hash_remove(fz_context *ctx, fz_hash_table *table, void *key) unsigned pos = hash(key, table->keylen) % size; unsigned hole, look, code; + if (table->lock >= 0) + fz_assert_lock_held(ctx, table->lock); + while (1) { if (!ents[pos].val) { - fz_warn(ctx, "assert: remove inexistent hash entry"); + fz_warn(ctx, "assert: remove non-existent hash entry"); return; } @@ -231,22 +268,22 @@ fz_hash_remove(fz_context *ctx, fz_hash_table *table, void *key) } void -fz_debug_hash(fz_context *ctx, fz_hash_table *table) +fz_print_hash(fz_context *ctx, FILE *out, fz_hash_table *table) { int i, k; - printf("cache load %d / %d\n", table->load, table->size); + fprintf(out, "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); + fprintf(out, "table % 4d: empty\n", i); else { - printf("table % 4d: key=", i); + fprintf(out, "table % 4d: key=", i); for (k = 0; k < MAX_KEY_LEN; k++) - printf("%02x", ((char*)table->ents[i].key)[k]); - printf(" val=$%p\n", table->ents[i].val); + fprintf(out, "%02x", ((char*)table->ents[i].key)[k]); + fprintf(out, " val=$%p\n", table->ents[i].val); } } } diff --git a/fitz/base_memory.c b/fitz/base_memory.c index c9ec2628..32c7ff84 100644 --- a/fitz/base_memory.c +++ b/fitz/base_memory.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" static void * do_scavenging_malloc(fz_context *ctx, unsigned int size) diff --git a/fitz/base_object.c b/fitz/base_object.c deleted file mode 100644 index 964c5bb4..00000000 --- a/fitz/base_object.c +++ /dev/null @@ -1,1265 +0,0 @@ -#include "fitz.h" - -typedef enum fz_objkind_e -{ - FZ_NULL, - FZ_BOOL, - FZ_INT, - FZ_REAL, - FZ_STRING, - FZ_NAME, - FZ_ARRAY, - FZ_DICT, - FZ_INDIRECT -} fz_objkind; - -struct keyval -{ - fz_obj *k; - fz_obj *v; -}; - -struct fz_obj_s -{ - int refs; - fz_objkind kind; - fz_context *ctx; - union - { - int b; - int i; - float f; - struct { - unsigned short len; - char buf[1]; - } s; - char n[1]; - struct { - int len; - int cap; - fz_obj **items; - } a; - struct { - char sorted; - char marked; - int len; - int cap; - struct keyval *items; - } d; - struct { - int num; - int gen; - struct pdf_xref_s *xref; - } r; - } u; -}; - -fz_obj * -fz_new_null(fz_context *ctx) -{ - fz_obj *obj; - obj = Memento_label(fz_malloc(ctx, sizeof(fz_obj)), "fz_obj(null)"); - obj->ctx = ctx; - obj->refs = 1; - obj->kind = FZ_NULL; - return obj; -} - -fz_obj * -fz_new_bool(fz_context *ctx, int b) -{ - fz_obj *obj; - obj = Memento_label(fz_malloc(ctx, sizeof(fz_obj)), "fz_obj(bool)"); - obj->ctx = ctx; - obj->refs = 1; - obj->kind = FZ_BOOL; - obj->u.b = b; - return obj; -} - -fz_obj * -fz_new_int(fz_context *ctx, int i) -{ - fz_obj *obj; - obj = Memento_label(fz_malloc(ctx, sizeof(fz_obj)), "fz_obj(int)"); - obj->ctx = ctx; - obj->refs = 1; - obj->kind = FZ_INT; - obj->u.i = i; - return obj; -} - -fz_obj * -fz_new_real(fz_context *ctx, float f) -{ - fz_obj *obj; - obj = Memento_label(fz_malloc(ctx, sizeof(fz_obj)), "fz_obj(real)"); - obj->ctx = ctx; - obj->refs = 1; - obj->kind = FZ_REAL; - obj->u.f = f; - return obj; -} - -fz_obj * -fz_new_string(fz_context *ctx, char *str, int len) -{ - fz_obj *obj; - obj = Memento_label(fz_malloc(ctx, offsetof(fz_obj, u.s.buf) + len + 1), "fz_obj(string)"); - obj->ctx = ctx; - obj->refs = 1; - obj->kind = FZ_STRING; - obj->u.s.len = len; - memcpy(obj->u.s.buf, str, len); - obj->u.s.buf[len] = '\0'; - return obj; -} - -fz_obj * -fz_new_name(fz_context *ctx, char *str) -{ - fz_obj *obj; - obj = Memento_label(fz_malloc(ctx, offsetof(fz_obj, u.n) + strlen(str) + 1), "fz_obj(name)"); - obj->ctx = ctx; - obj->refs = 1; - obj->kind = FZ_NAME; - strcpy(obj->u.n, str); - return obj; -} - -fz_obj * -fz_new_indirect(fz_context *ctx, int num, int gen, void *xref) -{ - fz_obj *obj; - obj = Memento_label(fz_malloc(ctx, sizeof(fz_obj)), "fz_obj(indirect)"); - obj->ctx = ctx; - obj->refs = 1; - obj->kind = FZ_INDIRECT; - obj->u.r.num = num; - obj->u.r.gen = gen; - obj->u.r.xref = xref; - return obj; -} - -fz_obj * -fz_keep_obj(fz_obj *obj) -{ - assert(obj); - obj->refs ++; - return obj; -} - -int fz_is_indirect(fz_obj *obj) -{ - return obj ? obj->kind == FZ_INDIRECT : 0; -} - -#define RESOLVE(obj) \ - do { \ - if (obj && obj->kind == FZ_INDIRECT) \ - {\ - fz_assert_lock_not_held(obj->ctx, FZ_LOCK_FILE); \ - obj = fz_resolve_indirect(obj); \ - } \ - } while (0) - -int fz_is_null(fz_obj *obj) -{ - RESOLVE(obj); - return obj ? obj->kind == FZ_NULL : 0; -} - -int fz_is_bool(fz_obj *obj) -{ - RESOLVE(obj); - return obj ? obj->kind == FZ_BOOL : 0; -} - -int fz_is_int(fz_obj *obj) -{ - RESOLVE(obj); - return obj ? obj->kind == FZ_INT : 0; -} - -int fz_is_real(fz_obj *obj) -{ - RESOLVE(obj); - return obj ? obj->kind == FZ_REAL : 0; -} - -int fz_is_string(fz_obj *obj) -{ - RESOLVE(obj); - return obj ? obj->kind == FZ_STRING : 0; -} - -int fz_is_name(fz_obj *obj) -{ - RESOLVE(obj); - return obj ? obj->kind == FZ_NAME : 0; -} - -int fz_is_array(fz_obj *obj) -{ - RESOLVE(obj); - return obj ? obj->kind == FZ_ARRAY : 0; -} - -int fz_is_dict(fz_obj *obj) -{ - RESOLVE(obj); - return obj ? obj->kind == FZ_DICT : 0; -} - -int fz_to_bool(fz_obj *obj) -{ - RESOLVE(obj); - if (!obj) - return 0; - return obj->kind == FZ_BOOL ? obj->u.b : 0; -} - -int fz_to_int(fz_obj *obj) -{ - RESOLVE(obj); - if (!obj) - return 0; - if (obj->kind == FZ_INT) - return obj->u.i; - if (obj->kind == FZ_REAL) - return (int)(obj->u.f + 0.5f); /* No roundf in MSVC */ - return 0; -} - -float fz_to_real(fz_obj *obj) -{ - RESOLVE(obj); - if (!obj) - return 0; - if (obj->kind == FZ_REAL) - return obj->u.f; - if (obj->kind == FZ_INT) - return obj->u.i; - return 0; -} - -char *fz_to_name(fz_obj *obj) -{ - RESOLVE(obj); - if (!obj || obj->kind != FZ_NAME) - return ""; - return obj->u.n; -} - -char *fz_to_str_buf(fz_obj *obj) -{ - RESOLVE(obj); - if (!obj || obj->kind != FZ_STRING) - return ""; - return obj->u.s.buf; -} - -int fz_to_str_len(fz_obj *obj) -{ - RESOLVE(obj); - if (!obj || obj->kind != FZ_STRING) - return 0; - return obj->u.s.len; -} - -/* for use by pdf_crypt_obj_imp to decrypt AES string in place */ -void fz_set_str_len(fz_obj *obj, int newlen) -{ - RESOLVE(obj); - if (!obj || obj->kind != FZ_STRING) - return; /* This should never happen */ - if (newlen > obj->u.s.len) - return; /* This should never happen */ - obj->u.s.len = newlen; -} - -fz_obj *fz_to_dict(fz_obj *obj) -{ - RESOLVE(obj); - return (obj && obj->kind == FZ_DICT ? obj : NULL); -} - -int fz_to_num(fz_obj *obj) -{ - if (!obj || obj->kind != FZ_INDIRECT) - return 0; - return obj->u.r.num; -} - -int fz_to_gen(fz_obj *obj) -{ - if (!obj || obj->kind != FZ_INDIRECT) - return 0; - return obj->u.r.gen; -} - -void *fz_get_indirect_document(fz_obj *obj) -{ - if (!obj || obj->kind != FZ_INDIRECT) - return NULL; - return obj->u.r.xref; -} - -int -fz_objcmp(fz_obj *a, fz_obj *b) -{ - int i; - - if (a == b) - return 0; - - if (!a || !b) - return 1; - - if (a->kind != b->kind) - return 1; - - switch (a->kind) - { - case FZ_NULL: - return 0; - - case FZ_BOOL: - return a->u.b - b->u.b; - - case FZ_INT: - return a->u.i - b->u.i; - - case FZ_REAL: - if (a->u.f < b->u.f) - return -1; - if (a->u.f > b->u.f) - return 1; - return 0; - - case FZ_STRING: - if (a->u.s.len < b->u.s.len) - { - if (memcmp(a->u.s.buf, b->u.s.buf, a->u.s.len) <= 0) - return -1; - return 1; - } - if (a->u.s.len > b->u.s.len) - { - if (memcmp(a->u.s.buf, b->u.s.buf, b->u.s.len) >= 0) - return 1; - return -1; - } - return memcmp(a->u.s.buf, b->u.s.buf, a->u.s.len); - - case FZ_NAME: - return strcmp(a->u.n, b->u.n); - - case FZ_INDIRECT: - if (a->u.r.num == b->u.r.num) - return a->u.r.gen - b->u.r.gen; - return a->u.r.num - b->u.r.num; - - case FZ_ARRAY: - if (a->u.a.len != b->u.a.len) - return a->u.a.len - b->u.a.len; - for (i = 0; i < a->u.a.len; i++) - if (fz_objcmp(a->u.a.items[i], b->u.a.items[i])) - return 1; - return 0; - - case FZ_DICT: - if (a->u.d.len != b->u.d.len) - return a->u.d.len - b->u.d.len; - for (i = 0; i < a->u.d.len; i++) - { - if (fz_objcmp(a->u.d.items[i].k, b->u.d.items[i].k)) - return 1; - if (fz_objcmp(a->u.d.items[i].v, b->u.d.items[i].v)) - return 1; - } - return 0; - - } - return 1; -} - -static char * -fz_objkindstr(fz_obj *obj) -{ - if (!obj) - return "<NULL>"; - switch (obj->kind) - { - case FZ_NULL: return "null"; - case FZ_BOOL: return "boolean"; - case FZ_INT: return "integer"; - case FZ_REAL: return "real"; - case FZ_STRING: return "string"; - case FZ_NAME: return "name"; - case FZ_ARRAY: return "array"; - case FZ_DICT: return "dictionary"; - case FZ_INDIRECT: return "reference"; - } - return "<unknown>"; -} - -fz_obj * -fz_new_array(fz_context *ctx, int initialcap) -{ - fz_obj *obj; - int i; - - obj = Memento_label(fz_malloc(ctx, sizeof(fz_obj)), "fz_obj(array)"); - obj->ctx = ctx; - obj->refs = 1; - obj->kind = FZ_ARRAY; - - obj->u.a.len = 0; - obj->u.a.cap = initialcap > 1 ? initialcap : 6; - - fz_try(ctx) - { - obj->u.a.items = Memento_label(fz_malloc_array(ctx, obj->u.a.cap, sizeof(fz_obj*)), "fz_obj(array items)"); - } - fz_catch(ctx) - { - fz_free(ctx, obj); - fz_rethrow(ctx); - } - for (i = 0; i < obj->u.a.cap; i++) - obj->u.a.items[i] = NULL; - - return obj; -} - -static void -fz_array_grow(fz_obj *obj) -{ - int i; - - obj->u.a.cap = (obj->u.a.cap * 3) / 2; - obj->u.a.items = fz_resize_array(obj->ctx, obj->u.a.items, obj->u.a.cap, sizeof(fz_obj*)); - - for (i = obj->u.a.len ; i < obj->u.a.cap; i++) - obj->u.a.items[i] = NULL; -} - -fz_obj * -fz_copy_array(fz_context *ctx, fz_obj *obj) -{ - fz_obj *arr; - int i; - int n; - - RESOLVE(obj); - if (!obj || obj->kind != FZ_ARRAY) - fz_warn(ctx, "assert: not an array (%s)", fz_objkindstr(obj)); - - arr = fz_new_array(ctx, fz_array_len(obj)); - n = fz_array_len(obj); - for (i = 0; i < n; i++) - fz_array_push(arr, fz_array_get(obj, i)); - - return arr; -} - -int -fz_array_len(fz_obj *obj) -{ - RESOLVE(obj); - if (!obj || obj->kind != FZ_ARRAY) - return 0; - return obj->u.a.len; -} - -fz_obj * -fz_array_get(fz_obj *obj, int i) -{ - RESOLVE(obj); - - if (!obj || obj->kind != FZ_ARRAY) - return NULL; - - if (i < 0 || i >= obj->u.a.len) - return NULL; - - return obj->u.a.items[i]; -} - -void -fz_array_put(fz_obj *obj, int i, fz_obj *item) -{ - RESOLVE(obj); - - if (!obj) - return; /* Can't warn :( */ - if (obj->kind != FZ_ARRAY) - fz_warn(obj->ctx, "assert: not an array (%s)", fz_objkindstr(obj)); - else if (i < 0) - fz_warn(obj->ctx, "assert: index %d < 0", i); - else if (i >= obj->u.a.len) - fz_warn(obj->ctx, "assert: index %d > length %d", i, obj->u.a.len); - else - { - if (obj->u.a.items[i]) - fz_drop_obj(obj->u.a.items[i]); - obj->u.a.items[i] = fz_keep_obj(item); - } -} - -void -fz_array_push(fz_obj *obj, fz_obj *item) -{ - RESOLVE(obj); - - if (!obj) - return; /* Can't warn :( */ - if (obj->kind != FZ_ARRAY) - fz_warn(obj->ctx, "assert: not an array (%s)", fz_objkindstr(obj)); - else - { - if (obj->u.a.len + 1 > obj->u.a.cap) - fz_array_grow(obj); - obj->u.a.items[obj->u.a.len] = fz_keep_obj(item); - obj->u.a.len++; - } -} - -void -fz_array_insert(fz_obj *obj, fz_obj *item) -{ - RESOLVE(obj); - - if (!obj) - return; /* Can't warn :( */ - if (obj->kind != FZ_ARRAY) - fz_warn(obj->ctx, "assert: not an array (%s)", fz_objkindstr(obj)); - else - { - if (obj->u.a.len + 1 > obj->u.a.cap) - fz_array_grow(obj); - memmove(obj->u.a.items + 1, obj->u.a.items, obj->u.a.len * sizeof(fz_obj*)); - obj->u.a.items[0] = fz_keep_obj(item); - obj->u.a.len++; - } -} - -int -fz_array_contains(fz_obj *arr, fz_obj *obj) -{ - int i; - - for (i = 0; i < fz_array_len(arr); i++) - if (!fz_objcmp(fz_array_get(arr, i), obj)) - return 1; - - return 0; -} - -/* dicts may only have names as keys! */ - -static int keyvalcmp(const void *ap, const void *bp) -{ - const struct keyval *a = ap; - const struct keyval *b = bp; - return strcmp(fz_to_name(a->k), fz_to_name(b->k)); -} - -fz_obj * -fz_new_dict(fz_context *ctx, int initialcap) -{ - fz_obj *obj; - int i; - - obj = Memento_label(fz_malloc(ctx, sizeof(fz_obj)), "fz_obj(dict)"); - obj->ctx = ctx; - obj->refs = 1; - obj->kind = FZ_DICT; - - obj->u.d.sorted = 0; - obj->u.d.marked = 0; - obj->u.d.len = 0; - obj->u.d.cap = initialcap > 1 ? initialcap : 10; - - fz_try(ctx) - { - obj->u.d.items = Memento_label(fz_malloc_array(ctx, obj->u.d.cap, sizeof(struct keyval)), "fz_obj(dict items)"); - } - fz_catch(ctx) - { - fz_free(ctx, obj); - fz_rethrow(ctx); - } - for (i = 0; i < obj->u.d.cap; i++) - { - obj->u.d.items[i].k = NULL; - obj->u.d.items[i].v = NULL; - } - - return obj; -} - -static void -fz_dict_grow(fz_obj *obj) -{ - int i; - - obj->u.d.cap = (obj->u.d.cap * 3) / 2; - obj->u.d.items = fz_resize_array(obj->ctx, obj->u.d.items, obj->u.d.cap, sizeof(struct keyval)); - - for (i = obj->u.d.len; i < obj->u.d.cap; i++) - { - obj->u.d.items[i].k = NULL; - obj->u.d.items[i].v = NULL; - } -} - -fz_obj * -fz_copy_dict(fz_context *ctx, fz_obj *obj) -{ - fz_obj *dict; - int i, n; - - RESOLVE(obj); - if (obj && obj->kind != FZ_DICT) - fz_warn(ctx, "assert: not a dict (%s)", fz_objkindstr(obj)); - - n = fz_dict_len(obj); - dict = fz_new_dict(ctx, n); - for (i = 0; i < n; i++) - fz_dict_put(dict, fz_dict_get_key(obj, i), fz_dict_get_val(obj, i)); - - return dict; -} - -int -fz_dict_len(fz_obj *obj) -{ - RESOLVE(obj); - if (!obj || obj->kind != FZ_DICT) - return 0; - return obj->u.d.len; -} - -fz_obj * -fz_dict_get_key(fz_obj *obj, int i) -{ - RESOLVE(obj); - if (!obj || obj->kind != FZ_DICT) - return NULL; - - if (i < 0 || i >= obj->u.d.len) - return NULL; - - return obj->u.d.items[i].k; -} - -fz_obj * -fz_dict_get_val(fz_obj *obj, int i) -{ - RESOLVE(obj); - if (!obj || obj->kind != FZ_DICT) - return NULL; - - if (i < 0 || i >= obj->u.d.len) - return NULL; - - return obj->u.d.items[i].v; -} - -static int -fz_dict_finds(fz_obj *obj, char *key, int *location) -{ - if (obj->u.d.sorted && obj->u.d.len > 0) - { - int l = 0; - int r = obj->u.d.len - 1; - - if (strcmp(fz_to_name(obj->u.d.items[r].k), key) < 0) - { - if (location) - *location = r + 1; - return -1; - } - - while (l <= r) - { - int m = (l + r) >> 1; - int c = -strcmp(fz_to_name(obj->u.d.items[m].k), key); - if (c < 0) - r = m - 1; - else if (c > 0) - l = m + 1; - else - return m; - - if (location) - *location = l; - } - } - - else - { - int i; - for (i = 0; i < obj->u.d.len; i++) - if (strcmp(fz_to_name(obj->u.d.items[i].k), key) == 0) - return i; - - if (location) - *location = obj->u.d.len; - } - - return -1; -} - -fz_obj * -fz_dict_gets(fz_obj *obj, char *key) -{ - int i; - - RESOLVE(obj); - if (!obj || obj->kind != FZ_DICT) - return NULL; - - i = fz_dict_finds(obj, key, NULL); - if (i >= 0) - return obj->u.d.items[i].v; - - return NULL; -} - -fz_obj * -fz_dict_get(fz_obj *obj, fz_obj *key) -{ - if (!key || key->kind != FZ_NAME) - return NULL; - return fz_dict_gets(obj, fz_to_name(key)); -} - -fz_obj * -fz_dict_getsa(fz_obj *obj, char *key, char *abbrev) -{ - fz_obj *v; - v = fz_dict_gets(obj, key); - if (v) - return v; - return fz_dict_gets(obj, abbrev); -} - -void -fz_dict_put(fz_obj *obj, fz_obj *key, fz_obj *val) -{ - int location; - char *s; - int i; - - RESOLVE(obj); - if (!obj || obj->kind != FZ_DICT) - { - fz_warn(obj->ctx, "assert: not a dict (%s)", fz_objkindstr(obj)); - return; - } - - RESOLVE(key); - if (!key || key->kind != FZ_NAME) - { - fz_warn(obj->ctx, "assert: key is not a name (%s)", fz_objkindstr(obj)); - return; - } - else - s = fz_to_name(key); - - if (!val) - { - fz_warn(obj->ctx, "assert: val does not exist for key (%s)", s); - return; - } - - if (obj->u.d.len > 100 && !obj->u.d.sorted) - fz_sort_dict(obj); - - i = fz_dict_finds(obj, s, &location); - if (i >= 0 && i < obj->u.d.len) - { - fz_drop_obj(obj->u.d.items[i].v); - obj->u.d.items[i].v = fz_keep_obj(val); - } - else - { - if (obj->u.d.len + 1 > obj->u.d.cap) - fz_dict_grow(obj); - - i = location; - if (obj->u.d.sorted && obj->u.d.len > 0) - memmove(&obj->u.d.items[i + 1], - &obj->u.d.items[i], - (obj->u.d.len - i) * sizeof(struct keyval)); - - obj->u.d.items[i].k = fz_keep_obj(key); - obj->u.d.items[i].v = fz_keep_obj(val); - obj->u.d.len ++; - } -} - -void -fz_dict_puts(fz_obj *obj, char *key, fz_obj *val) -{ - fz_obj *keyobj = fz_new_name(obj->ctx, key); - fz_dict_put(obj, keyobj, val); - fz_drop_obj(keyobj); -} - -void -fz_dict_dels(fz_obj *obj, char *key) -{ - RESOLVE(obj); - - if (!obj || obj->kind != FZ_DICT) - fz_warn(obj->ctx, "assert: not a dict (%s)", fz_objkindstr(obj)); - else - { - int i = fz_dict_finds(obj, key, NULL); - if (i >= 0) - { - fz_drop_obj(obj->u.d.items[i].k); - fz_drop_obj(obj->u.d.items[i].v); - obj->u.d.sorted = 0; - obj->u.d.items[i] = obj->u.d.items[obj->u.d.len-1]; - obj->u.d.len --; - } - } -} - -void -fz_dict_del(fz_obj *obj, fz_obj *key) -{ - RESOLVE(key); - if (!key || key->kind != FZ_NAME) - fz_warn(obj->ctx, "assert: key is not a name (%s)", fz_objkindstr(obj)); - else - fz_dict_dels(obj, key->u.n); -} - -void -fz_sort_dict(fz_obj *obj) -{ - RESOLVE(obj); - if (!obj || obj->kind != FZ_DICT) - return; - if (!obj->u.d.sorted) - { - qsort(obj->u.d.items, obj->u.d.len, sizeof(struct keyval), keyvalcmp); - obj->u.d.sorted = 1; - } -} - -int -fz_dict_marked(fz_obj *obj) -{ - RESOLVE(obj); - if (!obj || obj->kind != FZ_DICT) - return 0; - return obj->u.d.marked; -} - -int -fz_dict_mark(fz_obj *obj) -{ - int marked; - RESOLVE(obj); - if (!obj || obj->kind != FZ_DICT) - return 0; - marked = obj->u.d.marked; - obj->u.d.marked = 1; - return marked; -} - -void -fz_dict_unmark(fz_obj *obj) -{ - RESOLVE(obj); - if (!obj || obj->kind != FZ_DICT) - return; - obj->u.d.marked = 0; -} - -static void -fz_free_array(fz_obj *obj) -{ - int i; - - for (i = 0; i < obj->u.a.len; i++) - if (obj->u.a.items[i]) - fz_drop_obj(obj->u.a.items[i]); - - fz_free(obj->ctx, obj->u.a.items); - fz_free(obj->ctx, obj); -} - -static void -fz_free_dict(fz_obj *obj) -{ - int i; - - for (i = 0; i < obj->u.d.len; i++) { - if (obj->u.d.items[i].k) - fz_drop_obj(obj->u.d.items[i].k); - if (obj->u.d.items[i].v) - fz_drop_obj(obj->u.d.items[i].v); - } - - fz_free(obj->ctx, obj->u.d.items); - fz_free(obj->ctx, obj); -} - -void -fz_drop_obj(fz_obj *obj) -{ - if (!obj) - return; - if (--obj->refs) - return; - if (obj->kind == FZ_ARRAY) - fz_free_array(obj); - else if (obj->kind == FZ_DICT) - fz_free_dict(obj); - else - fz_free(obj->ctx, obj); -} - -/* Pretty printing objects */ - -struct fmt -{ - char *buf; - int cap; - int len; - int indent; - int tight; - int col; - int sep; - int last; -}; - -static void fmt_obj(struct fmt *fmt, fz_obj *obj); - -static inline int iswhite(int ch) -{ - return - ch == '\000' || - ch == '\011' || - ch == '\012' || - ch == '\014' || - ch == '\015' || - ch == '\040'; -} - -static inline int isdelim(int ch) -{ - return ch == '(' || ch == ')' || - ch == '<' || ch == '>' || - ch == '[' || ch == ']' || - ch == '{' || ch == '}' || - ch == '/' || - ch == '%'; -} - -static inline void fmt_putc(struct fmt *fmt, int c) -{ - if (fmt->sep && !isdelim(fmt->last) && !isdelim(c)) { - fmt->sep = 0; - fmt_putc(fmt, ' '); - } - fmt->sep = 0; - - if (fmt->buf && fmt->len < fmt->cap) - fmt->buf[fmt->len] = c; - - if (c == '\n') - fmt->col = 0; - else - fmt->col ++; - - fmt->len ++; - - fmt->last = c; -} - -static inline void fmt_indent(struct fmt *fmt) -{ - int i = fmt->indent; - while (i--) { - fmt_putc(fmt, ' '); - fmt_putc(fmt, ' '); - } -} - -static inline void fmt_puts(struct fmt *fmt, char *s) -{ - while (*s) - fmt_putc(fmt, *s++); -} - -static inline void fmt_sep(struct fmt *fmt) -{ - fmt->sep = 1; -} - -static void fmt_str(struct fmt *fmt, fz_obj *obj) -{ - char *s = fz_to_str_buf(obj); - int n = fz_to_str_len(obj); - int i, c; - - fmt_putc(fmt, '('); - for (i = 0; i < n; i++) - { - c = (unsigned char)s[i]; - if (c == '\n') - fmt_puts(fmt, "\\n"); - else if (c == '\r') - fmt_puts(fmt, "\\r"); - else if (c == '\t') - fmt_puts(fmt, "\\t"); - else if (c == '\b') - fmt_puts(fmt, "\\b"); - else if (c == '\f') - fmt_puts(fmt, "\\f"); - else if (c == '(') - fmt_puts(fmt, "\\("); - else if (c == ')') - fmt_puts(fmt, "\\)"); - else if (c < 32 || c >= 127) { - char buf[16]; - fmt_putc(fmt, '\\'); - sprintf(buf, "%03o", c); - fmt_puts(fmt, buf); - } - else - fmt_putc(fmt, c); - } - fmt_putc(fmt, ')'); -} - -static void fmt_hex(struct fmt *fmt, fz_obj *obj) -{ - char *s = fz_to_str_buf(obj); - int n = fz_to_str_len(obj); - int i, b, c; - - fmt_putc(fmt, '<'); - for (i = 0; i < n; i++) { - b = (unsigned char) s[i]; - c = (b >> 4) & 0x0f; - fmt_putc(fmt, c < 0xA ? c + '0' : c + 'A' - 0xA); - c = (b) & 0x0f; - fmt_putc(fmt, c < 0xA ? c + '0' : c + 'A' - 0xA); - } - fmt_putc(fmt, '>'); -} - -static void fmt_name(struct fmt *fmt, fz_obj *obj) -{ - unsigned char *s = (unsigned char *) fz_to_name(obj); - int i, c; - - fmt_putc(fmt, '/'); - - for (i = 0; s[i]; i++) - { - if (isdelim(s[i]) || iswhite(s[i]) || - s[i] == '#' || s[i] < 32 || s[i] >= 127) - { - fmt_putc(fmt, '#'); - c = (s[i] >> 4) & 0xf; - fmt_putc(fmt, c < 0xA ? c + '0' : c + 'A' - 0xA); - c = s[i] & 0xf; - fmt_putc(fmt, c < 0xA ? c + '0' : c + 'A' - 0xA); - } - else - { - fmt_putc(fmt, s[i]); - } - } -} - -static void fmt_array(struct fmt *fmt, fz_obj *obj) -{ - int i, n; - - n = fz_array_len(obj); - if (fmt->tight) { - fmt_putc(fmt, '['); - for (i = 0; i < n; i++) { - fmt_obj(fmt, fz_array_get(obj, i)); - fmt_sep(fmt); - } - fmt_putc(fmt, ']'); - } - else { - fmt_puts(fmt, "[ "); - for (i = 0; i < n; i++) { - if (fmt->col > 60) { - fmt_putc(fmt, '\n'); - fmt_indent(fmt); - } - fmt_obj(fmt, fz_array_get(obj, i)); - fmt_putc(fmt, ' '); - } - fmt_putc(fmt, ']'); - fmt_sep(fmt); - } -} - -static void fmt_dict(struct fmt *fmt, fz_obj *obj) -{ - int i, n; - fz_obj *key, *val; - - n = fz_dict_len(obj); - if (fmt->tight) { - fmt_puts(fmt, "<<"); - for (i = 0; i < n; i++) { - fmt_obj(fmt, fz_dict_get_key(obj, i)); - fmt_sep(fmt); - fmt_obj(fmt, fz_dict_get_val(obj, i)); - fmt_sep(fmt); - } - fmt_puts(fmt, ">>"); - } - else { - fmt_puts(fmt, "<<\n"); - fmt->indent ++; - for (i = 0; i < n; i++) { - key = fz_dict_get_key(obj, i); - val = fz_dict_get_val(obj, i); - fmt_indent(fmt); - fmt_obj(fmt, key); - fmt_putc(fmt, ' '); - if (!fz_is_indirect(val) && fz_is_array(val)) - fmt->indent ++; - fmt_obj(fmt, val); - fmt_putc(fmt, '\n'); - if (!fz_is_indirect(val) && fz_is_array(val)) - fmt->indent --; - } - fmt->indent --; - fmt_indent(fmt); - fmt_puts(fmt, ">>"); - } -} - -static void fmt_obj(struct fmt *fmt, fz_obj *obj) -{ - char buf[256]; - - if (!obj) - fmt_puts(fmt, "<NULL>"); - else if (fz_is_indirect(obj)) - { - sprintf(buf, "%d %d R", fz_to_num(obj), fz_to_gen(obj)); - fmt_puts(fmt, buf); - } - else if (fz_is_null(obj)) - fmt_puts(fmt, "null"); - else if (fz_is_bool(obj)) - fmt_puts(fmt, fz_to_bool(obj) ? "true" : "false"); - else if (fz_is_int(obj)) - { - sprintf(buf, "%d", fz_to_int(obj)); - fmt_puts(fmt, buf); - } - else if (fz_is_real(obj)) - { - sprintf(buf, "%g", fz_to_real(obj)); - if (strchr(buf, 'e')) /* bad news! */ - sprintf(buf, fabsf(fz_to_real(obj)) > 1 ? "%1.1f" : "%1.8f", fz_to_real(obj)); - fmt_puts(fmt, buf); - } - else if (fz_is_string(obj)) - { - char *str = fz_to_str_buf(obj); - int len = fz_to_str_len(obj); - int added = 0; - int i, c; - for (i = 0; i < len; i++) { - c = (unsigned char)str[i]; - if (strchr("()\\\n\r\t\b\f", c)) - added ++; - else if (c < 32 || c >= 127) - added += 3; - } - if (added < len) - fmt_str(fmt, obj); - else - fmt_hex(fmt, obj); - } - else if (fz_is_name(obj)) - fmt_name(fmt, obj); - else if (fz_is_array(obj)) - fmt_array(fmt, obj); - else if (fz_is_dict(obj)) - fmt_dict(fmt, obj); - else - fmt_puts(fmt, "<unknown object>"); -} - -static int -fz_sprint_obj(char *s, int n, fz_obj *obj, int tight) -{ - struct fmt fmt; - - fmt.indent = 0; - fmt.col = 0; - fmt.sep = 0; - fmt.last = 0; - - fmt.tight = tight; - fmt.buf = s; - fmt.cap = n; - fmt.len = 0; - fmt_obj(&fmt, obj); - - if (fmt.buf && fmt.len < fmt.cap) - fmt.buf[fmt.len] = '\0'; - - return fmt.len; -} - -int -fz_fprint_obj(FILE *fp, fz_obj *obj, int tight) -{ - char buf[1024]; - char *ptr; - int n; - - n = fz_sprint_obj(NULL, 0, obj, tight); - if ((n + 1) < sizeof buf) - { - fz_sprint_obj(buf, sizeof buf, obj, tight); - fputs(buf, fp); - fputc('\n', fp); - } - else - { - ptr = fz_malloc(obj->ctx, n + 1); - fz_sprint_obj(ptr, n + 1, obj, tight); - fputs(ptr, fp); - fputc('\n', fp); - fz_free(obj->ctx, ptr); - } - return n; -} - -void -fz_debug_obj(fz_obj *obj) -{ - fz_fprint_obj(stdout, obj, 0); -} - -void -fz_debug_ref(fz_obj *ref) -{ - fz_debug_obj(fz_resolve_indirect(ref)); -} diff --git a/fitz/base_string.c b/fitz/base_string.c index dd9f8ea2..8ed08911 100644 --- a/fitz/base_string.c +++ b/fitz/base_string.c @@ -1,11 +1,4 @@ -#include "fitz.h" - -int -fz_is_big_endian(void) -{ - static const int one = 1; - return *(char*)&one == 0; -} +#include "fitz-internal.h" char * fz_strsep(char **stringp, const char *delim) @@ -36,8 +29,8 @@ fz_strlcpy(char *dst, const char *src, int siz) if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ - while (*s++) - ; + while (*s++) + ; } return(s - src - 1); /* count does not include NUL */ @@ -108,7 +101,7 @@ enum }; int -chartorune(int *rune, char *str) +fz_chartorune(int *rune, char *str) { int c, c1, c2, c3; long l; @@ -183,16 +176,15 @@ bad: } int -runetochar(char *str, int *rune) +fz_runetochar(char *str, int rune) { /* Runes are signed, so convert to unsigned for range check. */ - unsigned long c; + unsigned long c = (unsigned long)rune; /* * one character sequence * 00000-0007F => 00-7F */ - c = *rune; if(c <= Rune1) { str[0] = c; return 1; @@ -240,10 +232,10 @@ runetochar(char *str, int *rune) } int -runelen(int c) +fz_runelen(int c) { char str[10]; - return runetochar(str, &c); + return fz_runetochar(str, c); } float fz_atof(const char *s) @@ -256,10 +248,11 @@ float fz_atof(const char *s) * as we convert to a float. */ errno = 0; d = strtod(s, NULL); - if (errno == ERANGE || d > FLT_MAX || d < -FLT_MAX) { + if (errno == ERANGE || isnan(d)) { /* Return 1.0, as it's a small known value that won't cause a - * divide by 0. */ + divide by 0. */ return 1.0; } + d = CLAMP(d, -FLT_MAX, FLT_MAX); return (float)d; } diff --git a/fitz/crypt_aes.c b/fitz/crypt_aes.c index afdff0fe..4d8c4498 100644 --- a/fitz/crypt_aes.c +++ b/fitz/crypt_aes.c @@ -36,7 +36,7 @@ * http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf */ -#include "fitz.h" +#include "fitz-internal.h" #define aes_context fz_aes diff --git a/fitz/crypt_arc4.c b/fitz/crypt_arc4.c index 272891ce..596bf652 100644 --- a/fitz/crypt_arc4.c +++ b/fitz/crypt_arc4.c @@ -21,7 +21,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "fitz.h" +#include "fitz-internal.h" void fz_arc4_init(fz_arc4 *arc4, const unsigned char *key, unsigned keylen) diff --git a/fitz/crypt_md5.c b/fitz/crypt_md5.c index b6e06845..87c0909d 100644 --- a/fitz/crypt_md5.c +++ b/fitz/crypt_md5.c @@ -23,7 +23,7 @@ These notices must be retained in any copies of any part of this documentation and/or software. */ -#include "fitz.h" +#include "fitz-internal.h" /* Constants for MD5Transform routine */ enum diff --git a/fitz/crypt_sha2.c b/fitz/crypt_sha2.c index f17146c6..64284cfa 100644 --- a/fitz/crypt_sha2.c +++ b/fitz/crypt_sha2.c @@ -7,7 +7,7 @@ This file has been put into the public domain. You can do whatever you want with this file. */ -#include "fitz.h" +#include "fitz-internal.h" static inline int isbigendian(void) { diff --git a/fitz/dev_bbox.c b/fitz/dev_bbox.c index 636ceb94..163780d0 100644 --- a/fitz/dev_bbox.c +++ b/fitz/dev_bbox.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" /* TODO: add clip stack and use to intersect bboxes */ @@ -47,7 +47,7 @@ fz_bbox_fill_shade(fz_device *dev, fz_shade *shade, fz_matrix ctm, float alpha) } static void -fz_bbox_fill_image(fz_device *dev, fz_pixmap *image, fz_matrix ctm, float alpha) +fz_bbox_fill_image(fz_device *dev, fz_image *image, fz_matrix ctm, float alpha) { fz_bbox *result = dev->user; fz_bbox bbox = fz_round_rect(fz_transform_rect(ctm, fz_unit_rect)); @@ -55,7 +55,7 @@ fz_bbox_fill_image(fz_device *dev, fz_pixmap *image, fz_matrix ctm, float alpha) } static void -fz_bbox_fill_image_mask(fz_device *dev, fz_pixmap *image, fz_matrix ctm, +fz_bbox_fill_image_mask(fz_device *dev, fz_image *image, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha) { fz_bbox_fill_image(dev, image, ctm, alpha); diff --git a/fitz/dev_list.c b/fitz/dev_list.c index adc691d0..d0dfabc5 100644 --- a/fitz/dev_list.c +++ b/fitz/dev_list.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" typedef struct fz_display_node_s fz_display_node; @@ -37,7 +37,7 @@ struct fz_display_node_s fz_path *path; fz_text *text; fz_shade *shade; - fz_pixmap *image; + fz_image *image; int blendmode; } item; fz_stroke_state *stroke; @@ -207,7 +207,7 @@ fz_free_display_node(fz_context *ctx, fz_display_node *node) case FZ_CMD_FILL_IMAGE: case FZ_CMD_FILL_IMAGE_MASK: case FZ_CMD_CLIP_IMAGE_MASK: - fz_drop_pixmap(ctx, node->item.image); + fz_drop_image(ctx, node->item.image); break; case FZ_CMD_POP_CLIP: case FZ_CMD_BEGIN_MASK: @@ -435,35 +435,35 @@ fz_list_fill_shade(fz_device *dev, fz_shade *shade, fz_matrix ctm, float alpha) } static void -fz_list_fill_image(fz_device *dev, fz_pixmap *image, fz_matrix ctm, float alpha) +fz_list_fill_image(fz_device *dev, fz_image *image, fz_matrix ctm, float alpha) { fz_display_node *node; node = fz_new_display_node(dev->ctx, FZ_CMD_FILL_IMAGE, ctm, NULL, NULL, alpha); node->rect = fz_transform_rect(ctm, fz_unit_rect); - node->item.image = fz_keep_pixmap(dev->ctx, image); + node->item.image = fz_keep_image(dev->ctx, image); fz_append_display_node(dev->user, node); } static void -fz_list_fill_image_mask(fz_device *dev, fz_pixmap *image, fz_matrix ctm, +fz_list_fill_image_mask(fz_device *dev, fz_image *image, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha) { fz_display_node *node; node = fz_new_display_node(dev->ctx, FZ_CMD_FILL_IMAGE_MASK, ctm, colorspace, color, alpha); node->rect = fz_transform_rect(ctm, fz_unit_rect); - node->item.image = fz_keep_pixmap(dev->ctx, image); + node->item.image = fz_keep_image(dev->ctx, image); fz_append_display_node(dev->user, node); } static void -fz_list_clip_image_mask(fz_device *dev, fz_pixmap *image, fz_rect *rect, fz_matrix ctm) +fz_list_clip_image_mask(fz_device *dev, fz_image *image, fz_rect *rect, fz_matrix ctm) { fz_display_node *node; node = fz_new_display_node(dev->ctx, FZ_CMD_CLIP_IMAGE_MASK, ctm, NULL, NULL, 0); node->rect = fz_transform_rect(ctm, fz_unit_rect); if (rect) node->rect = fz_intersect_rect(node->rect, *rect); - node->item.image = fz_keep_pixmap(dev->ctx, image); + node->item.image = fz_keep_image(dev->ctx, image); fz_append_display_node(dev->user, node); } diff --git a/fitz/dev_null.c b/fitz/dev_null.c index b4ba5cbe..886cebe1 100644 --- a/fitz/dev_null.c +++ b/fitz/dev_null.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" fz_device * fz_new_device(fz_context *ctx, void *user) @@ -103,14 +103,14 @@ fz_fill_shade(fz_device *dev, fz_shade *shade, fz_matrix ctm, float alpha) } void -fz_fill_image(fz_device *dev, fz_pixmap *image, fz_matrix ctm, float alpha) +fz_fill_image(fz_device *dev, fz_image *image, fz_matrix ctm, float alpha) { if (dev->fill_image) dev->fill_image(dev, image, ctm, alpha); } void -fz_fill_image_mask(fz_device *dev, fz_pixmap *image, fz_matrix ctm, +fz_fill_image_mask(fz_device *dev, fz_image *image, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha) { if (dev->fill_image_mask) @@ -118,7 +118,7 @@ fz_fill_image_mask(fz_device *dev, fz_pixmap *image, fz_matrix ctm, } void -fz_clip_image_mask(fz_device *dev, fz_pixmap *image, fz_rect *rect, fz_matrix ctm) +fz_clip_image_mask(fz_device *dev, fz_image *image, fz_rect *rect, fz_matrix ctm) { if (dev->clip_image_mask) dev->clip_image_mask(dev, image, rect, ctm); diff --git a/fitz/dev_text.c b/fitz/dev_text.c index d38fab21..7a059e5a 100644 --- a/fitz/dev_text.c +++ b/fitz/dev_text.c @@ -1,7 +1,8 @@ -#include "fitz.h" +#include "fitz-internal.h" #define LINE_DIST 0.9f #define SPACE_DIST 0.2f +#define PARAGRAPH_DIST 0.5f #include <ft2build.h> #include FT_FREETYPE_H @@ -11,60 +12,211 @@ typedef struct fz_text_device_s fz_text_device; struct fz_text_device_s { + fz_text_sheet *sheet; + fz_text_page *page; + fz_text_line cur_line; + fz_text_span cur_span; fz_point point; - fz_text_span *head; - fz_text_span *span; }; -fz_text_span * -fz_new_text_span(fz_context *ctx) +fz_text_sheet * +fz_new_text_sheet(fz_context *ctx) { - fz_text_span *span; - span = fz_malloc_struct(ctx, fz_text_span); - span->font = NULL; - span->wmode = 0; - span->size = 0; - span->len = 0; - span->cap = 0; - span->text = NULL; - span->next = NULL; - span->eol = 0; - return span; + fz_text_sheet *sheet = fz_malloc(ctx, sizeof *sheet); + sheet->maxid = 0; + sheet->style = NULL; + return sheet; } void -fz_free_text_span(fz_context *ctx, fz_text_span *span) +fz_free_text_sheet(fz_context *ctx, fz_text_sheet *sheet) +{ + fz_text_style *style = sheet->style; + while (style) + { + fz_text_style *next = style->next; + fz_drop_font(ctx, style->font); + fz_free(ctx, style); + style = next; + } +} + +static fz_text_style * +fz_lookup_text_style_imp(fz_context *ctx, fz_text_sheet *sheet, + float size, fz_font *font, int wmode, int script) +{ + fz_text_style *style; + + for (style = sheet->style; style; style = style->next) + { + if (style->font == font && + style->size == size && + style->wmode == wmode && + style->script == script) /* FIXME: others */ + { + return style; + } + } + + /* Better make a new one and add it to our list */ + style = fz_malloc(ctx, sizeof *style); + style->id = sheet->maxid++; + style->font = fz_keep_font(ctx, font); + style->size = size; + style->wmode = wmode; + style->script = script; + style->next = sheet->style; + sheet->style = style; + return style; +} + +static fz_text_style * +fz_lookup_text_style(fz_context *ctx, fz_text_sheet *sheet, fz_text *text, fz_matrix *ctm, + fz_colorspace *colorspace, float *color, float alpha, fz_stroke_state *stroke) { - fz_text_span *next; + float size = 1.0f; + fz_font *font = text ? text->font : NULL; + int wmode = text ? text->wmode : 0; + if (ctm && text) + { + fz_matrix tm = text->trm; + fz_matrix trm; + tm.e = 0; + tm.f = 0; + trm = fz_concat(tm, *ctm); + size = fz_matrix_expansion(trm); + } + return fz_lookup_text_style_imp(ctx, sheet, size, font, wmode, 0); +} - while (span) +fz_text_page * +fz_new_text_page(fz_context *ctx, fz_rect mediabox) +{ + fz_text_page *page = fz_malloc(ctx, sizeof(*page)); + page->mediabox = mediabox; + page->len = 0; + page->cap = 0; + page->blocks = NULL; + return page; +} + +void +fz_free_text_page(fz_context *ctx, fz_text_page *page) +{ + fz_text_block *block; + fz_text_line *line; + fz_text_span *span; + for (block = page->blocks; block < page->blocks + page->len; block++) { - if (span->font) - fz_drop_font(ctx, span->font); - next = span->next; - fz_free(ctx, span->text); - fz_free(ctx, span); - span = next; + for (line = block->lines; line < block->lines + block->len; line++) + { + for (span = line->spans; span < line->spans + line->len; span++) + { + fz_free(ctx, span->text); + } + fz_free(ctx, line->spans); + } + fz_free(ctx, block->lines); } + fz_free(ctx, page->blocks); + fz_free(ctx, page); } static void -fz_add_text_char_imp(fz_context *ctx, fz_text_span *span, int c, fz_bbox bbox) +append_char(fz_context *ctx, fz_text_span *span, int c, fz_rect bbox) { - if (span->len + 1 >= span->cap) + if (span->len == span->cap) { - span->cap = span->cap > 1 ? (span->cap * 3) / 2 : 80; - span->text = fz_resize_array(ctx, span->text, span->cap, sizeof(fz_text_char)); + span->cap = MAX(64, span->cap * 2); + span->text = fz_resize_array(ctx, span->text, span->cap, sizeof(*span->text)); } + span->bbox = fz_union_rect(span->bbox, bbox); span->text[span->len].c = c; span->text[span->len].bbox = bbox; - span->len ++; + span->len++; +} + +static void +init_span(fz_context *ctx, fz_text_span *span, fz_text_style *style) +{ + span->style = style; + span->bbox = fz_empty_rect; + span->len = span->cap = 0; + span->text = NULL; +} + +static void +append_span(fz_context *ctx, fz_text_line *line, fz_text_span *span) +{ + if (line->len == line->cap) + { + line->cap = MAX(8, line->cap * 2); + line->spans = fz_resize_array(ctx, line->spans, line->cap, sizeof(*line->spans)); + } + line->bbox = fz_union_rect(line->bbox, span->bbox); + line->spans[line->len++] = *span; } -static fz_bbox -fz_split_bbox(fz_bbox bbox, int i, int n) +static void +init_line(fz_context *ctx, fz_text_line *line) +{ + line->bbox = fz_empty_rect; + line->len = line->cap = 0; + line->spans = NULL; +} + +static void +append_line(fz_context *ctx, fz_text_block *block, fz_text_line *line) { - float w = (float)(bbox.x1 - bbox.x0) / n; + if (block->len == block->cap) + { + block->cap = MAX(16, block->cap * 2); + block->lines = fz_resize_array(ctx, block->lines, block->cap, sizeof *block->lines); + } + block->bbox = fz_union_rect(block->bbox, line->bbox); + block->lines[block->len++] = *line; +} + +static fz_text_block * +lookup_block_for_line(fz_context *ctx, fz_text_page *page, fz_text_line *line) +{ + float size = line->len > 0 && line->spans[0].len > 0 ? line->spans[0].style->size : 1; + int i; + + for (i = 0; i < page->len; i++) + { + fz_text_block *block = page->blocks + i; + int w = block->bbox.x1 - block->bbox.x0; + if (block->bbox.y0 - line->bbox.y1 < size * PARAGRAPH_DIST) + if (line->bbox.x0 < block->bbox.x1 && line->bbox.x1 > block->bbox.x0) + if (ABS(line->bbox.x0 - block->bbox.x0) < w / 4) + return block; + } + + if (page->len == page->cap) + { + page->cap = MAX(16, page->cap * 2); + page->blocks = fz_resize_array(ctx, page->blocks, page->cap, sizeof(*page->blocks)); + } + + page->blocks[page->len].bbox = fz_empty_rect; + page->blocks[page->len].len = 0; + page->blocks[page->len].cap = 0; + page->blocks[page->len].lines = NULL; + + return &page->blocks[page->len++]; +} + +static void +insert_line(fz_context *ctx, fz_text_page *page, fz_text_line *line) +{ + append_line(ctx, lookup_block_for_line(ctx, page, line), line); +} + +static fz_rect +fz_split_bbox(fz_rect bbox, int i, int n) +{ + float w = (bbox.x1 - bbox.x0) / n; float x0 = bbox.x0; bbox.x0 = x0 + i * w; bbox.x1 = x0 + (i + 1) * w; @@ -72,154 +224,71 @@ fz_split_bbox(fz_bbox bbox, int i, int n) } static void -fz_add_text_char(fz_context *ctx, fz_text_span **last, fz_font *font, float size, int wmode, int c, fz_bbox bbox) +fz_flush_text_line(fz_context *ctx, fz_text_device *dev, fz_text_style *style) { - fz_text_span *span = *last; - - if (!span->font) - { - span->font = fz_keep_font(ctx, font); - span->size = size; - } + append_span(ctx, &dev->cur_line, &dev->cur_span); + insert_line(ctx, dev->page, &dev->cur_line); + init_span(ctx, &dev->cur_span, style); + init_line(ctx, &dev->cur_line); +} - if ((span->font != font || span->size != size || span->wmode != wmode) && c != 32) +static void +fz_add_text_char_imp(fz_context *ctx, fz_text_device *dev, fz_text_style *style, int c, fz_rect bbox) +{ + if (!dev->cur_span.style) + dev->cur_span.style = style; + if (style != dev->cur_span.style) { - span = fz_new_text_span(ctx); - span->font = fz_keep_font(ctx, font); - span->size = size; - span->wmode = wmode; - (*last)->next = span; - *last = span; + append_span(ctx, &dev->cur_line, &dev->cur_span); + init_span(ctx, &dev->cur_span, style); } + append_char(ctx, &dev->cur_span, c, bbox); +} +static void +fz_add_text_char(fz_context *ctx, fz_text_device *dev, fz_text_style *style, int c, fz_rect bbox) +{ switch (c) { case -1: /* ignore when one unicode character maps to multiple glyphs */ break; case 0xFB00: /* ff */ - fz_add_text_char_imp(ctx, span, 'f', fz_split_bbox(bbox, 0, 2)); - fz_add_text_char_imp(ctx, span, 'f', fz_split_bbox(bbox, 1, 2)); + fz_add_text_char_imp(ctx, dev, style, 'f', fz_split_bbox(bbox, 0, 2)); + fz_add_text_char_imp(ctx, dev, style, 'f', fz_split_bbox(bbox, 1, 2)); break; case 0xFB01: /* fi */ - fz_add_text_char_imp(ctx, span, 'f', fz_split_bbox(bbox, 0, 2)); - fz_add_text_char_imp(ctx, span, 'i', fz_split_bbox(bbox, 1, 2)); + fz_add_text_char_imp(ctx, dev, style, 'f', fz_split_bbox(bbox, 0, 2)); + fz_add_text_char_imp(ctx, dev, style, 'i', fz_split_bbox(bbox, 1, 2)); break; case 0xFB02: /* fl */ - fz_add_text_char_imp(ctx, span, 'f', fz_split_bbox(bbox, 0, 2)); - fz_add_text_char_imp(ctx, span, 'l', fz_split_bbox(bbox, 1, 2)); + fz_add_text_char_imp(ctx, dev, style, 'f', fz_split_bbox(bbox, 0, 2)); + fz_add_text_char_imp(ctx, dev, style, 'l', fz_split_bbox(bbox, 1, 2)); break; case 0xFB03: /* ffi */ - fz_add_text_char_imp(ctx, span, 'f', fz_split_bbox(bbox, 0, 3)); - fz_add_text_char_imp(ctx, span, 'f', fz_split_bbox(bbox, 1, 3)); - fz_add_text_char_imp(ctx, span, 'i', fz_split_bbox(bbox, 2, 3)); + fz_add_text_char_imp(ctx, dev, style, 'f', fz_split_bbox(bbox, 0, 3)); + fz_add_text_char_imp(ctx, dev, style, 'f', fz_split_bbox(bbox, 1, 3)); + fz_add_text_char_imp(ctx, dev, style, 'i', fz_split_bbox(bbox, 2, 3)); break; case 0xFB04: /* ffl */ - fz_add_text_char_imp(ctx, span, 'f', fz_split_bbox(bbox, 0, 3)); - fz_add_text_char_imp(ctx, span, 'f', fz_split_bbox(bbox, 1, 3)); - fz_add_text_char_imp(ctx, span, 'l', fz_split_bbox(bbox, 2, 3)); + fz_add_text_char_imp(ctx, dev, style, 'f', fz_split_bbox(bbox, 0, 3)); + fz_add_text_char_imp(ctx, dev, style, 'f', fz_split_bbox(bbox, 1, 3)); + fz_add_text_char_imp(ctx, dev, style, 'l', fz_split_bbox(bbox, 2, 3)); break; case 0xFB05: /* long st */ case 0xFB06: /* st */ - fz_add_text_char_imp(ctx, span, 's', fz_split_bbox(bbox, 0, 2)); - fz_add_text_char_imp(ctx, span, 't', fz_split_bbox(bbox, 1, 2)); + fz_add_text_char_imp(ctx, dev, style, 's', fz_split_bbox(bbox, 0, 2)); + fz_add_text_char_imp(ctx, dev, style, 't', fz_split_bbox(bbox, 1, 2)); break; default: - fz_add_text_char_imp(ctx, span, c, bbox); + fz_add_text_char_imp(ctx, dev, style, c, bbox); break; } } static void -fz_divide_text_chars(fz_text_span **last, int n, fz_bbox bbox) -{ - fz_text_span *span = *last; - int i, x; - x = span->len - n; - if (x >= 0) - for (i = 0; i < n; i++) - span->text[x + i].bbox = fz_split_bbox(bbox, i, n); -} - -static void -fz_add_text_newline(fz_context *ctx, fz_text_span **last, fz_font *font, float size, int wmode) -{ - fz_text_span *span; - span = fz_new_text_span(ctx); - span->font = fz_keep_font(ctx, font); - span->size = size; - span->wmode = wmode; - (*last)->eol = 1; - (*last)->next = span; - *last = span; -} - -void -fz_debug_text_span_xml(fz_text_span *span) -{ - char buf[10]; - int c, n, k, i; - - while (span) - { - printf("<span font=\"%s\" size=\"%g\" wmode=\"%d\" eol=\"%d\">\n", - span->font ? span->font->name : "NULL", span->size, span->wmode, span->eol); - - for (i = 0; i < span->len; i++) - { - printf("\t<char ucs=\""); - c = span->text[i].c; - if (c < 128) - putchar(c); - else - { - n = runetochar(buf, &c); - for (k = 0; k < n; k++) - putchar(buf[k]); - } - printf("\" bbox=\"%d %d %d %d\" />\n", - span->text[i].bbox.x0, - span->text[i].bbox.y0, - span->text[i].bbox.x1, - span->text[i].bbox.y1); - } - - printf("</span>\n"); - - span = span->next; - } -} - -void -fz_debug_text_span(fz_text_span *span) -{ - char buf[10]; - int c, n, k, i; - - while (span) - { - for (i = 0; i < span->len; i++) - { - c = span->text[i].c; - if (c < 128) - putchar(c); - else - { - n = runetochar(buf, &c); - for (k = 0; k < n; k++) - putchar(buf[k]); - } - } - - if (span->eol) - putchar('\n'); - - span = span->next; - } -} - -static void -fz_text_extract_span(fz_context *ctx, fz_text_span **last, fz_text *text, fz_matrix ctm, fz_point *pen) +fz_text_extract(fz_context *ctx, fz_text_device *dev, fz_text *text, fz_matrix ctm, fz_text_style *style) { + fz_point *pen = &dev->point; fz_font *font = text->font; FT_Face face = font->ft_face; fz_matrix tm = text->trm; @@ -233,19 +302,21 @@ fz_text_extract_span(fz_context *ctx, fz_text_span **last, fz_text *text, fz_mat float ascender = 1; float descender = 0; int multi; - int i, err; + int i, j, err; + int lastchar = ' '; if (text->len == 0) return; - fz_lock(ctx, FZ_LOCK_FREETYPE); if (font->ft_face) { + fz_lock(ctx, FZ_LOCK_FREETYPE); err = FT_Set_Char_Size(font->ft_face, 64, 64, 72, 72); if (err) fz_warn(ctx, "freetype set character size: %s", ft_error_string(err)); ascender = (float)face->ascender / face->units_per_EM; descender = (float)face->descender / face->units_per_EM; + fz_unlock(ctx, FZ_LOCK_FREETYPE); } rect = fz_empty_rect; @@ -264,6 +335,7 @@ fz_text_extract_span(fz_context *ctx, fz_text_span **last, fz_text *text, fz_mat tm.e = 0; tm.f = 0; trm = fz_concat(tm, ctm); + dir = fz_transform_vector(trm, dir); dist = sqrtf(dir.x * dir.x + dir.y * dir.y); ndir.x = dir.x / dist; @@ -271,19 +343,8 @@ fz_text_extract_span(fz_context *ctx, fz_text_span **last, fz_text *text, fz_mat size = fz_matrix_expansion(trm); - multi = 1; - for (i = 0; i < text->len; i++) { - if (text->items[i].gid < 0) - { - fz_add_text_char(ctx, last, font, size, text->wmode, text->items[i].ucs, fz_round_rect(rect)); - multi ++; - fz_divide_text_chars(last, multi, fz_round_rect(rect)); - continue; - } - multi = 1; - /* Calculate new pen location and delta */ tm.e = text->items[i].x; tm.f = text->items[i].y; @@ -305,20 +366,19 @@ fz_text_extract_span(fz_context *ctx, fz_text_span **last, fz_text *text, fz_mat if (dist > size * LINE_DIST) { - fz_add_text_newline(ctx, last, font, size, text->wmode); + fz_flush_text_line(ctx, dev, style); + lastchar = ' '; } - else if (fabsf(dot) > 0.95f && dist > size * SPACE_DIST) + else if (fabsf(dot) > 0.95f && dist > size * SPACE_DIST && lastchar != ' ') { - if ((*last)->len > 0 && (*last)->text[(*last)->len - 1].c != ' ') - { - fz_rect spacerect; - spacerect.x0 = -0.2f; - spacerect.y0 = 0; - spacerect.x1 = 0; - spacerect.y1 = 1; - spacerect = fz_transform_rect(trm, spacerect); - fz_add_text_char(ctx, last, font, size, text->wmode, ' ', fz_round_rect(spacerect)); - } + fz_rect spacerect; + spacerect.x0 = -0.2f; + spacerect.y0 = 0; + spacerect.x1 = 0; + spacerect.y1 = 1; + spacerect = fz_transform_rect(trm, spacerect); + fz_add_text_char(ctx, dev, style, ' ', spacerect); + lastchar = ' '; } } @@ -331,8 +391,13 @@ fz_text_extract_span(fz_context *ctx, fz_text_span **last, fz_text *text, fz_mat /* TODO: freetype returns broken vertical metrics */ /* if (text->wmode) mask |= FT_LOAD_VERTICAL_LAYOUT; */ + fz_lock(ctx, FZ_LOCK_FREETYPE); + err = FT_Set_Char_Size(font->ft_face, 64, 64, 72, 72); + if (err) + fz_warn(ctx, "freetype set character size: %s", ft_error_string(err)); FT_Get_Advance(font->ft_face, text->items[i].gid, mask, &ftadv); adv = ftadv / 65536.0f; + fz_unlock(ctx, FZ_LOCK_FREETYPE); rect.x0 = 0; rect.y0 = descender; @@ -352,9 +417,27 @@ fz_text_extract_span(fz_context *ctx, fz_text_span **last, fz_text *text, fz_mat pen->x = trm.e + dir.x * adv; pen->y = trm.f + dir.y * adv; - fz_add_text_char(ctx, last, font, size, text->wmode, text->items[i].ucs, fz_round_rect(rect)); + /* Check for one glyph to many char mapping */ + for (j = i + 1; j < text->len; j++) + if (text->items[j].gid >= 0) + break; + multi = j - i; + + if (multi == 1) + { + fz_add_text_char(ctx, dev, style, text->items[i].ucs, rect); + } + else + { + for (j = 0; j < multi; j++) + { + fz_rect part = fz_split_bbox(rect, j, multi); + fz_add_text_char(ctx, dev, style, text->items[i].ucs, part); + } + } + + lastchar = text->items[i].ucs; } - fz_unlock(ctx, FZ_LOCK_FREETYPE); } static void @@ -362,7 +445,9 @@ fz_text_fill_text(fz_device *dev, fz_text *text, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha) { fz_text_device *tdev = dev->user; - fz_text_extract_span(dev->ctx, &tdev->span, text, ctm, &tdev->point); + fz_text_style *style; + style = fz_lookup_text_style(dev->ctx, tdev->sheet, text, &ctm, colorspace, color, alpha, NULL); + fz_text_extract(dev->ctx, tdev, text, ctm, style); } static void @@ -370,36 +455,57 @@ fz_text_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, fz_m fz_colorspace *colorspace, float *color, float alpha) { fz_text_device *tdev = dev->user; - fz_text_extract_span(dev->ctx, &tdev->span, text, ctm, &tdev->point); + fz_text_style *style; + style = fz_lookup_text_style(dev->ctx, tdev->sheet, text, &ctm, colorspace, color, alpha, stroke); + fz_text_extract(dev->ctx, tdev, text, ctm, style); } static void fz_text_clip_text(fz_device *dev, fz_text *text, fz_matrix ctm, int accumulate) { fz_text_device *tdev = dev->user; - fz_text_extract_span(dev->ctx, &tdev->span, text, ctm, &tdev->point); + fz_text_style *style; + style = fz_lookup_text_style(dev->ctx, tdev->sheet, text, &ctm, NULL, NULL, 0, NULL); + fz_text_extract(dev->ctx, tdev, text, ctm, style); } static void fz_text_clip_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, fz_matrix ctm) { fz_text_device *tdev = dev->user; - fz_text_extract_span(dev->ctx, &tdev->span, text, ctm, &tdev->point); + fz_text_style *style; + style = fz_lookup_text_style(dev->ctx, tdev->sheet, text, &ctm, NULL, NULL, 0, stroke); + fz_text_extract(dev->ctx, tdev, text, ctm, style); } static void fz_text_ignore_text(fz_device *dev, fz_text *text, fz_matrix ctm) { fz_text_device *tdev = dev->user; - fz_text_extract_span(dev->ctx, &tdev->span, text, ctm, &tdev->point); + fz_text_style *style; + style = fz_lookup_text_style(dev->ctx, tdev->sheet, text, &ctm, NULL, NULL, 0, NULL); + fz_text_extract(dev->ctx, tdev, text, ctm, style); +} + +static int cmp_block(const void *av, const void *bv) +{ + const fz_text_block *a = av; + const fz_text_block *b = bv; + int x = a->bbox.x0 - b->bbox.x0; + if (x) return x; + return -(a->bbox.y0 - b->bbox.y0); } static void fz_text_free_user(fz_device *dev) { + fz_context *ctx = dev->ctx; fz_text_device *tdev = dev->user; - tdev->span->eol = 1; + append_span(ctx, &tdev->cur_line, &tdev->cur_span); + insert_line(ctx, tdev->page, &tdev->cur_line); + + qsort(tdev->page->blocks, tdev->page->len, sizeof *tdev->page->blocks, cmp_block); /* TODO: unicode NFC normalization */ /* TODO: bidi logical reordering */ @@ -408,15 +514,19 @@ fz_text_free_user(fz_device *dev) } fz_device * -fz_new_text_device(fz_context *ctx, fz_text_span *root) +fz_new_text_device(fz_context *ctx, fz_text_sheet *sheet, fz_text_page *page) { fz_device *dev; + fz_text_device *tdev = fz_malloc_struct(ctx, fz_text_device); - tdev->head = root; - tdev->span = root; + tdev->sheet = sheet; + tdev->page = page; tdev->point.x = -1; tdev->point.y = -1; + init_line(ctx, &tdev->cur_line); + init_span(ctx, &tdev->cur_span, NULL); + dev = fz_new_device(ctx, tdev); dev->hints = FZ_IGNORE_IMAGE | FZ_IGNORE_SHADE; dev->free_user = fz_text_free_user; @@ -427,3 +537,209 @@ fz_new_text_device(fz_context *ctx, fz_text_span *root) dev->ignore_text = fz_text_ignore_text; return dev; } + +/* XML, HTML and plain-text output */ + +static int font_is_bold(fz_font *font) +{ + FT_Face face = font->ft_face; + if (face && (face->style_flags & FT_STYLE_FLAG_BOLD)) + return 1; + if (strstr(font->name, "Bold")) + return 1; + return 0; +} + +static int font_is_italic(fz_font *font) +{ + FT_Face face = font->ft_face; + if (face && (face->style_flags & FT_STYLE_FLAG_ITALIC)) + return 1; + if (strstr(font->name, "Italic") || strstr(font->name, "Oblique")) + return 1; + return 0; +} + +static void +fz_print_style_begin(FILE *out, fz_text_style *style) +{ + int script = style->script; + fprintf(out, "<span class=\"s%d\">", style->id); + while (script-- > 0) + fprintf(out, "<sup>"); + while (++script < 0) + fprintf(out, "<sub>"); +} + +static void +fz_print_style_end(FILE *out, fz_text_style *style) +{ + int script = style->script; + while (script-- > 0) + fprintf(out, "</sup>"); + while (++script < 0) + fprintf(out, "</sub>"); + fprintf(out, "</span>"); +} + +static void +fz_print_style(FILE *out, fz_text_style *style) +{ + char *s = strchr(style->font->name, '+'); + s = s ? s + 1 : style->font->name; + fprintf(out, "span.s%d{font-family:\"%s\";font-size:%gpt;", + style->id, s, style->size); + if (font_is_italic(style->font)) + fprintf(out, "font-style:italic;"); + if (font_is_bold(style->font)) + fprintf(out, "font-weight:bold;"); + fprintf(out, "}\n"); +} + +void +fz_print_text_sheet(fz_context *ctx, FILE *out, fz_text_sheet *sheet) +{ + fz_text_style *style; + for (style = sheet->style; style; style = style->next) + fz_print_style(out, style); +} + +void +fz_print_text_page_html(fz_context *ctx, FILE *out, fz_text_page *page) +{ + int block_n, line_n, span_n, ch_n; + fz_text_style *style = NULL; + fz_text_block *block; + fz_text_line *line; + fz_text_span *span; + + fprintf(out, "<div class=\"page\">\n"); + + for (block_n = 0; block_n < page->len; block_n++) + { + block = &page->blocks[block_n]; + fprintf(out, "<div class=\"block\">\n"); + for (line_n = 0; line_n < block->len; line_n++) + { + line = &block->lines[line_n]; + fprintf(out, "<p>"); + style = NULL; + + for (span_n = 0; span_n < line->len; span_n++) + { + span = &line->spans[span_n]; + if (style != span->style) + { + if (style != NULL) + fz_print_style_end(out, style); + fz_print_style_begin(out, span->style); + style = span->style; + } + + for (ch_n = 0; ch_n < span->len; ch_n++) + { + fz_text_char *ch = &span->text[ch_n]; + if (ch->c == '<') + fprintf(out, "<"); + else if (ch->c == '>') + fprintf(out, ">"); + else if (ch->c == '&') + fprintf(out, "&"); + else if (ch->c >= 32 && ch->c <= 127) + fprintf(out, "%c", ch->c); + else + fprintf(out, "&#x%x;", ch->c); + } + } + fz_print_style_end(out, style); + fprintf(out, "</p>\n"); + } + fprintf(out, "</div>\n"); + } + + fprintf(out, "</div>\n"); +} + +void +fz_print_text_page_xml(fz_context *ctx, FILE *out, fz_text_page *page) +{ + fz_text_block *block; + fz_text_line *line; + fz_text_span *span; + fz_text_char *ch; + char *s; + + fprintf(out, "<page>\n"); + for (block = page->blocks; block < page->blocks + page->len; block++) + { + fprintf(out, "<block bbox=\"%g %g %g %g\">\n", + block->bbox.x0, block->bbox.y0, block->bbox.x1, block->bbox.y1); + for (line = block->lines; line < block->lines + block->len; line++) + { + fprintf(out, "<line bbox=\"%g %g %g %g\">\n", + line->bbox.x0, line->bbox.y0, line->bbox.x1, line->bbox.y1); + for (span = line->spans; span < line->spans + line->len; span++) + { + fz_text_style *style = span->style; + s = strchr(style->font->name, '+'); + s = s ? s + 1 : style->font->name; + fprintf(out, "<span bbox=\"%g %g %g %g\" font=\"%s\" size=\"%g\">\n", + span->bbox.x0, span->bbox.y0, span->bbox.x1, span->bbox.y1, + s, style->size); + for (ch = span->text; ch < span->text + span->len; ch++) + { + fprintf(out, "<char bbox=\"%g %g %g %g\" c=\"", + ch->bbox.x0, ch->bbox.y0, ch->bbox.x1, ch->bbox.y1); + switch (ch->c) + { + case '<': fprintf(out, "<"); break; + case '>': fprintf(out, ">"); break; + case '&': fprintf(out, "&"); break; + case '"': fprintf(out, """); break; + case '\'': fprintf(out, "'"); break; + default: + if (ch->c >= 32 && ch->c <= 127) + fprintf(out, "%c", ch->c); + else + fprintf(out, "&#x%x;", ch->c); + break; + } + fprintf(out, "\"/>\n"); + } + fprintf(out, "</span>\n"); + } + fprintf(out, "</line>\n"); + } + fprintf(out, "</block>\n"); + } + fprintf(out, "</page>\n"); +} + +void +fz_print_text_page(fz_context *ctx, FILE *out, fz_text_page *page) +{ + fz_text_block *block; + fz_text_line *line; + fz_text_span *span; + fz_text_char *ch; + char utf[10]; + int i, n; + + for (block = page->blocks; block < page->blocks + page->len; block++) + { + for (line = block->lines; line < block->lines + block->len; line++) + { + for (span = line->spans; span < line->spans + line->len; span++) + { + for (ch = span->text; ch < span->text + span->len; ch++) + { + n = fz_runetochar(utf, ch->c); + for (i = 0; i < n; i++) + putc(utf[i], out); + } + } + fprintf(out, "\n"); + } + fprintf(out, "\n"); + } +} diff --git a/fitz/dev_trace.c b/fitz/dev_trace.c index 1c2e1ed1..b3c73ff2 100644 --- a/fitz/dev_trace.c +++ b/fitz/dev_trace.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" static void fz_trace_matrix(fz_matrix ctm) @@ -145,7 +145,7 @@ fz_trace_fill_text(fz_device *dev, fz_text *text, fz_matrix ctm, fz_trace_matrix(ctm); fz_trace_trm(text->trm); printf(">\n"); - fz_debug_text(dev->ctx, text, 0); + fz_print_text(dev->ctx, stdout, text); printf("</fill_text>\n"); } @@ -158,7 +158,7 @@ fz_trace_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, fz_ fz_trace_matrix(ctm); fz_trace_trm(text->trm); printf(">\n"); - fz_debug_text(dev->ctx, text, 0); + fz_print_text(dev->ctx, stdout, text); printf("</stroke_text>\n"); } @@ -170,7 +170,7 @@ fz_trace_clip_text(fz_device *dev, fz_text *text, fz_matrix ctm, int accumulate) fz_trace_matrix(ctm); fz_trace_trm(text->trm); printf(">\n"); - fz_debug_text(dev->ctx, text, 0); + fz_print_text(dev->ctx, stdout, text); printf("</clip_text>\n"); } @@ -181,7 +181,7 @@ fz_trace_clip_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke fz_trace_matrix(ctm); fz_trace_trm(text->trm); printf(">\n"); - fz_debug_text(dev->ctx, text, 0); + fz_print_text(dev->ctx, stdout, text); printf("</clip_stroke_text>\n"); } @@ -192,12 +192,12 @@ fz_trace_ignore_text(fz_device *dev, fz_text *text, fz_matrix ctm) fz_trace_matrix(ctm); fz_trace_trm(text->trm); printf(">\n"); - fz_debug_text(dev->ctx, text, 0); + fz_print_text(dev->ctx, stdout, text); printf("</ignore_text>\n"); } static void -fz_trace_fill_image(fz_device *dev, fz_pixmap *image, fz_matrix ctm, float alpha) +fz_trace_fill_image(fz_device *dev, fz_image *image, fz_matrix ctm, float alpha) { printf("<fill_image alpha=\"%g\" ", alpha); fz_trace_matrix(ctm); @@ -213,7 +213,7 @@ fz_trace_fill_shade(fz_device *dev, fz_shade *shade, fz_matrix ctm, float alpha) } static void -fz_trace_fill_image_mask(fz_device *dev, fz_pixmap *image, fz_matrix ctm, +fz_trace_fill_image_mask(fz_device *dev, fz_image *image, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha) { printf("<fill_image_mask "); @@ -223,7 +223,7 @@ fz_colorspace *colorspace, float *color, float alpha) } static void -fz_trace_clip_image_mask(fz_device *dev, fz_pixmap *image, fz_rect *rect, fz_matrix ctm) +fz_trace_clip_image_mask(fz_device *dev, fz_image *image, fz_rect *rect, fz_matrix ctm) { printf("<clip_image_mask "); fz_trace_matrix(ctm); diff --git a/fitz/doc_document.c b/fitz/doc_document.c index ca13af72..2da7a110 100644 --- a/fitz/doc_document.c +++ b/fitz/doc_document.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" /* Yuck! Promiscuous we are. */ extern struct pdf_document *pdf_open_document(fz_context *ctx, char *filename); diff --git a/fitz/doc_link.c b/fitz/doc_link.c index 71f5dfbf..d558d18a 100644 --- a/fitz/doc_link.c +++ b/fitz/doc_link.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" void fz_free_link_dest(fz_context *ctx, fz_link_dest *dest) diff --git a/fitz/doc_outline.c b/fitz/doc_outline.c index b69debaf..71694851 100644 --- a/fitz/doc_outline.c +++ b/fitz/doc_outline.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" void fz_free_outline(fz_context *ctx, fz_outline *outline) @@ -14,37 +14,49 @@ fz_free_outline(fz_context *ctx, fz_outline *outline) } } -void -fz_debug_outline_xml(fz_context *ctx, fz_outline *outline, int level) +static void +do_debug_outline_xml(FILE *out, fz_outline *outline, int level) { while (outline) { - printf("<outline title=\"%s\" page=\"%d\"", outline->title, outline->dest.kind == FZ_LINK_GOTO ? outline->dest.ld.gotor.page + 1 : 0); + fprintf(out, "<outline title=\"%s\" page=\"%d\"", outline->title, outline->dest.kind == FZ_LINK_GOTO ? outline->dest.ld.gotor.page + 1 : 0); if (outline->down) { - printf(">\n"); - fz_debug_outline_xml(ctx, outline->down, level + 1); - printf("</outline>\n"); + fprintf(out, ">\n"); + do_debug_outline_xml(out, outline->down, level + 1); + fprintf(out, "</outline>\n"); } else { - printf(" />\n"); + fprintf(out, " />\n"); } outline = outline->next; } } void -fz_debug_outline(fz_context *ctx, fz_outline *outline, int level) +fz_print_outline_xml(fz_context *ctx, FILE *out, fz_outline *outline) +{ + do_debug_outline_xml(out, outline, 0); +} + +static void +do_debug_outline(FILE *out, fz_outline *outline, int level) { int i; while (outline) { for (i = 0; i < level; i++) - putchar('\t'); - printf("%s\t%d\n", outline->title, outline->dest.kind == FZ_LINK_GOTO ? outline->dest.ld.gotor.page + 1 : 0); + fputc('\t', out); + fprintf(out, "%s\t%d\n", outline->title, outline->dest.kind == FZ_LINK_GOTO ? outline->dest.ld.gotor.page + 1 : 0); if (outline->down) - fz_debug_outline(ctx, outline->down, level + 1); + do_debug_outline(out, outline->down, level + 1); outline = outline->next; } } + +void +fz_print_outline(fz_context *ctx, FILE *out, fz_outline *outline) +{ + do_debug_outline(out, outline, 0); +} diff --git a/fitz/filt_basic.c b/fitz/filt_basic.c index ae239fed..09d63402 100644 --- a/fitz/filt_basic.c +++ b/fitz/filt_basic.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" /* Pretend we have a filter that just copies data forever */ diff --git a/fitz/filt_dctd.c b/fitz/filt_dctd.c index 4357f3d7..23744f01 100644 --- a/fitz/filt_dctd.c +++ b/fitz/filt_dctd.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" #include <jpeglib.h> #include <setjmp.h> @@ -12,6 +12,7 @@ struct fz_dctd_s int color_transform; int init; int stride; + int factor; unsigned char *scanline; unsigned char *rp, *wp; struct jpeg_decompress_struct cinfo; @@ -150,6 +151,9 @@ read_dctd(fz_stream *stm, unsigned char *buf, int len) break; } + cinfo->scale_num = 8/state->factor; + cinfo->scale_denom = 8; + jpeg_start_decompress(cinfo); state->stride = cinfo->output_width * cinfo->output_components; @@ -216,6 +220,12 @@ skip: fz_stream * fz_open_dctd(fz_stream *chain, int color_transform) { + return fz_open_resized_dctd(chain, color_transform, 1); +} + +fz_stream * +fz_open_resized_dctd(fz_stream *chain, int color_transform, int factor) +{ fz_context *ctx = chain->ctx; fz_dctd *state = NULL; @@ -228,6 +238,7 @@ fz_open_dctd(fz_stream *chain, int color_transform) state->chain = chain; state->color_transform = color_transform; state->init = 0; + state->factor = factor; } fz_catch(ctx) { diff --git a/fitz/filt_faxd.c b/fitz/filt_faxd.c index 4e522eb5..ada7e87b 100644 --- a/fitz/filt_faxd.c +++ b/fitz/filt_faxd.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" /* Fax G3/G4 decoder */ diff --git a/fitz/filt_flate.c b/fitz/filt_flate.c index 2eb0c563..24b6c081 100644 --- a/fitz/filt_flate.c +++ b/fitz/filt_flate.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" #include <zlib.h> diff --git a/fitz/filt_jbig2d.c b/fitz/filt_jbig2d.c index 3afcbcb0..415534c0 100644 --- a/fitz/filt_jbig2d.c +++ b/fitz/filt_jbig2d.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" #ifdef _WIN32 /* Microsoft Visual C++ */ diff --git a/fitz/filt_lzwd.c b/fitz/filt_lzwd.c index ac952ccf..3ee4d34c 100644 --- a/fitz/filt_lzwd.c +++ b/fitz/filt_lzwd.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" /* TODO: error checking */ diff --git a/fitz/filt_predict.c b/fitz/filt_predict.c index 8221c251..e68743d3 100644 --- a/fitz/filt_predict.c +++ b/fitz/filt_predict.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" /* TODO: check if this works with 16bpp images */ diff --git a/fitz/fitz-internal.h b/fitz/fitz-internal.h new file mode 100644 index 00000000..0a639b1c --- /dev/null +++ b/fitz/fitz-internal.h @@ -0,0 +1,1092 @@ +#ifndef FITZ_INTERNAL_H +#define FITZ_INTERNAL_H + +#include "fitz.h" + +struct fz_warn_context_s +{ + char message[256]; + int count; +}; + + +fz_context *fz_clone_context_internal(fz_context *ctx); + +void fz_new_aa_context(fz_context *ctx); +void fz_free_aa_context(fz_context *ctx); +void fz_copy_aa_context(fz_context *dst, fz_context *src); + +/* Default allocator */ +extern fz_alloc_context fz_alloc_default; + +/* Default locks */ +extern fz_locks_context fz_locks_default; + +#if defined(MEMENTO) || defined(DEBUG) +#define FITZ_DEBUG_LOCKING +#endif + +#ifdef FITZ_DEBUG_LOCKING + +void fz_assert_lock_held(fz_context *ctx, int lock); +void fz_assert_lock_not_held(fz_context *ctx, int lock); +void fz_lock_debug_lock(fz_context *ctx, int lock); +void fz_lock_debug_unlock(fz_context *ctx, int lock); + +#else + +#define fz_assert_lock_held(A,B) do { } while (0) +#define fz_assert_lock_not_held(A,B) do { } while (0) +#define fz_lock_debug_lock(A,B) do { } while (0) +#define fz_lock_debug_unlock(A,B) do { } while (0) + +#endif /* !FITZ_DEBUG_LOCKING */ + +static inline void +fz_lock(fz_context *ctx, int lock) +{ + fz_lock_debug_lock(ctx, lock); + ctx->locks->lock(ctx->locks->user, lock); +} + +static inline void +fz_unlock(fz_context *ctx, int lock) +{ + fz_lock_debug_unlock(ctx, lock); + ctx->locks->unlock(ctx->locks->user, lock); +} + + +/* + * Basic runtime and utility functions + */ + +/* + fz_malloc_struct: Allocate storage for a structure (with scavenging), + clear it, and (in Memento builds) tag the pointer as belonging to a + struct of this type. + + CTX: The context. + + STRUCT: The structure type. + + Returns a pointer to allocated (and cleared) structure. Throws + exception on failure to allocate. +*/ +/* alloc and zero a struct, and tag it for memento */ +#define fz_malloc_struct(CTX, STRUCT) \ + Memento_label(fz_calloc(CTX,1,sizeof(STRUCT)), #STRUCT) + +/* Range checking atof */ +float fz_atof(const char *s); + +/* + * Generic hash-table with fixed-length keys. + */ + +typedef struct fz_hash_table_s fz_hash_table; + +fz_hash_table *fz_new_hash_table(fz_context *ctx, int initialsize, int keylen, int lock); +void fz_print_hash(fz_context *ctx, FILE *out, fz_hash_table *table); +void fz_empty_hash(fz_context *ctx, fz_hash_table *table); +void fz_free_hash(fz_context *ctx, fz_hash_table *table); + +void *fz_hash_find(fz_context *ctx, fz_hash_table *table, void *key); +void *fz_hash_insert(fz_context *ctx, fz_hash_table *table, void *key, void *val); +void fz_hash_remove(fz_context *ctx, fz_hash_table *table, void *key); + +int fz_hash_len(fz_context *ctx, fz_hash_table *table); +void *fz_hash_get_key(fz_context *ctx, fz_hash_table *table, int idx); +void *fz_hash_get_val(fz_context *ctx, fz_hash_table *table, int idx); + +/* + * Math and geometry + */ + +/* Multiply scaled two integers in the 0..255 range */ +static inline int fz_mul255(int a, int b) +{ + /* see Jim Blinn's book "Dirty Pixels" for how this works */ + int x = a * b + 128; + x += x >> 8; + return x >> 8; +} + +/* Expand a value A from the 0...255 range to the 0..256 range */ +#define FZ_EXPAND(A) ((A)+((A)>>7)) + +/* Combine values A (in any range) and B (in the 0..256 range), + * to give a single value in the same range as A was. */ +#define FZ_COMBINE(A,B) (((A)*(B))>>8) + +/* Combine values A and C (in the same (any) range) and B and D (in the + * 0..256 range), to give a single value in the same range as A and C were. */ +#define FZ_COMBINE2(A,B,C,D) (FZ_COMBINE((A), (B)) + FZ_COMBINE((C), (D))) + +/* Blend SRC and DST (in the same range) together according to + * AMOUNT (in the 0...256 range). */ +#define FZ_BLEND(SRC, DST, AMOUNT) ((((SRC)-(DST))*(AMOUNT) + ((DST)<<8))>>8) + +void fz_gridfit_matrix(fz_matrix *m); +float fz_matrix_max_expansion(fz_matrix m); + +/* + * Basic crypto functions. + * Independent of the rest of fitz. + * For further encapsulation in filters, or not. + */ + +/* md5 digests */ + +typedef struct fz_md5_s fz_md5; + +struct fz_md5_s +{ + unsigned int state[4]; + unsigned int count[2]; + unsigned char buffer[64]; +}; + +void fz_md5_init(fz_md5 *state); +void fz_md5_update(fz_md5 *state, const unsigned char *input, unsigned inlen); +void fz_md5_final(fz_md5 *state, unsigned char digest[16]); + +/* sha-256 digests */ + +typedef struct fz_sha256_s fz_sha256; + +struct fz_sha256_s +{ + unsigned int state[8]; + unsigned int count[2]; + union { + unsigned char u8[64]; + unsigned int u32[16]; + } buffer; +}; + +void fz_sha256_init(fz_sha256 *state); +void fz_sha256_update(fz_sha256 *state, const unsigned char *input, unsigned int inlen); +void fz_sha256_final(fz_sha256 *state, unsigned char digest[32]); + +/* arc4 crypto */ + +typedef struct fz_arc4_s fz_arc4; + +struct fz_arc4_s +{ + unsigned x; + unsigned y; + unsigned char state[256]; +}; + +void fz_arc4_init(fz_arc4 *state, const unsigned char *key, unsigned len); +void fz_arc4_encrypt(fz_arc4 *state, unsigned char *dest, const unsigned char *src, unsigned len); + +/* AES block cipher implementation from XYSSL */ + +typedef struct fz_aes_s fz_aes; + +#define AES_DECRYPT 0 +#define AES_ENCRYPT 1 + +struct fz_aes_s +{ + int nr; /* number of rounds */ + unsigned long *rk; /* AES round keys */ + unsigned long buf[68]; /* unaligned data */ +}; + +void aes_setkey_enc( fz_aes *ctx, const unsigned char *key, int keysize ); +void aes_setkey_dec( fz_aes *ctx, const unsigned char *key, int keysize ); +void aes_crypt_cbc( fz_aes *ctx, int mode, int length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ); + +/* + Resource store + + MuPDF stores decoded "objects" into a store for potential reuse. + If the size of the store gets too big, objects stored within it can + be evicted and freed to recover space. When MuPDF comes to decode + such an object, it will check to see if a version of this object is + already in the store - if it is, it will simply reuse it. If not, it + will decode it and place it into the store. + + All objects that can be placed into the store are derived from the + fz_storable type (i.e. this should be the first component of the + objects structure). This allows for consistent (thread safe) + reference counting, and includes a function that will be called to + free the object as soon as the reference count reaches zero. + + Most objects offer fz_keep_XXXX/fz_drop_XXXX functions derived + from fz_keep_storable/fz_drop_storable. Creation of such objects + includes a call to FZ_INIT_STORABLE to set up the fz_storable header. + */ + +typedef struct fz_storable_s fz_storable; + +typedef void (fz_store_free_fn)(fz_context *, fz_storable *); + +struct fz_storable_s { + int refs; + fz_store_free_fn *free; +}; + +#define FZ_INIT_STORABLE(S_,RC,FREE) \ + do { fz_storable *S = &(S_)->storable; S->refs = (RC); \ + S->free = (FREE); \ + } while (0) + +void *fz_keep_storable(fz_context *, fz_storable *); +void fz_drop_storable(fz_context *, fz_storable *); + +/* + The store can be seen as a dictionary that maps keys to fz_storable + values. In order to allow keys of different types to be stored, we + have a structure full of functions for each key 'type'; this + fz_store_type pointer is stored with each key, and tells the store + how to perform certain operations (like taking/dropping a reference, + comparing two keys, outputting details for debugging etc). + + The store uses a hash table internally for speed where possible. In + order for this to work, we need a mechanism for turning a generic + 'key' into 'a hashable string'. For this purpose the type structure + contains a make_hash_key function pointer that maps from a void * + to an fz_store_hash structure. If make_hash_key function returns 0, + then the key is determined not to be hashable, and the value is + not stored in the hash table. +*/ +typedef struct fz_store_hash_s fz_store_hash; + +struct fz_store_hash_s +{ + fz_store_free_fn *free; + union + { + struct + { + int i0; + int i1; + } i; + struct + { + void *ptr; + int i; + } pi; + } u; +}; + +typedef struct fz_store_type_s fz_store_type; + +struct fz_store_type_s +{ + int (*make_hash_key)(fz_store_hash *, void *); + void *(*keep_key)(fz_context *,void *); + void (*drop_key)(fz_context *,void *); + int (*cmp_key)(void *, void *); + void (*debug)(void *); +}; + +/* + fz_store_new_context: Create a new store inside the context + + max: The maximum size (in bytes) that the store is allowed to grow + to. FZ_STORE_UNLIMITED means no limit. +*/ +void fz_new_store_context(fz_context *ctx, unsigned int max); + +/* + fz_drop_store_context: Drop a reference to the store. +*/ +void fz_drop_store_context(fz_context *ctx); + +/* + fz_keep_store_context: Take a reference to the store. +*/ +fz_store *fz_keep_store_context(fz_context *ctx); + +/* + fz_print_store: Dump the contents of the store for debugging. +*/ +void fz_print_store(fz_context *ctx, FILE *out); + +/* + fz_store_item: Add an item to the store. + + Add an item into the store, returning NULL for success. If an item + with the same key is found in the store, then our item will not be + inserted, and the function will return a pointer to that value + instead. This function takes its own reference to val, as required + (i.e. the caller maintains ownership of its own reference). + + key: The key to use to index the item. + + val: The value to store. + + itemsize: The size in bytes of the value (as counted towards the + store size). + + type: Functions used to manipulate the key. +*/ +void *fz_store_item(fz_context *ctx, void *key, void *val, unsigned int itemsize, fz_store_type *type); + +/* + fz_find_item: Find an item within the store. + + free: The function used to free the value (to ensure we get a value + of the correct type). + + key: The key to use to index the item. + + type: Functions used to manipulate the key. + + Returns NULL for not found, otherwise returns a pointer to the value + indexed by key to which a reference has been taken. +*/ +void *fz_find_item(fz_context *ctx, fz_store_free_fn *free, void *key, fz_store_type *type); + +/* + fz_remove_item: Remove an item from the store. + + If an item indexed by the given key exists in the store, remove it. + + free: The function used to free the value (to ensure we get a value + of the correct type). + + key: The key to use to find the item to remove. + + type: Functions used to manipulate the key. +*/ +void fz_remove_item(fz_context *ctx, fz_store_free_fn *free, void *key, fz_store_type *type); + +/* + fz_empty_store: Evict everything from the store. +*/ +void fz_empty_store(fz_context *ctx); + +/* + fz_store_scavenge: Internal function used as part of the scavenging + allocator; when we fail to allocate memory, before returning a + failure to the caller, we try to scavenge space within the store by + evicting at least 'size' bytes. The allocator then retries. + + size: The number of bytes we are trying to have free. + + phase: What phase of the scavenge we are in. Updated on exit. + + Returns non zero if we managed to free any memory. +*/ +int fz_store_scavenge(fz_context *ctx, unsigned int size, int *phase); + +struct fz_buffer_s +{ + int refs; + unsigned char *data; + int cap, len; +}; + +/* + fz_new_buffer: Create a new buffer. + + capacity: Initial capacity. + + Returns pointer to new buffer. Throws exception on allocation + failure. +*/ +fz_buffer *fz_new_buffer(fz_context *ctx, int capacity); + +/* + fz_resize_buffer: Ensure that a buffer has a given capacity, + truncating data if required. + + buf: The buffer to alter. + + capacity: The desired capacity for the buffer. If the current size + of the buffer contents is smaller than capacity, it is truncated. + +*/ +void fz_resize_buffer(fz_context *ctx, fz_buffer *buf, int capacity); + +/* + fz_grow_buffer: Make some space within a buffer (i.e. ensure that + capacity > size). + + buf: The buffer to grow. + + May throw exception on failure to allocate. +*/ +void fz_grow_buffer(fz_context *ctx, fz_buffer *buf); + +/* + fz_trim_buffer: Trim wasted capacity from a buffer. + + buf: The buffer to trim. +*/ +void fz_trim_buffer(fz_context *ctx, fz_buffer *buf); + +struct fz_stream_s +{ + fz_context *ctx; + int refs; + int error; + int eof; + int pos; + int avail; + int bits; + int locked; + unsigned char *bp, *rp, *wp, *ep; + void *state; + int (*read)(fz_stream *stm, unsigned char *buf, int len); + void (*close)(fz_context *ctx, void *state); + void (*seek)(fz_stream *stm, int offset, int whence); + unsigned char buf[4096]; +}; + +void fz_lock_stream(fz_stream *stm); + +fz_stream *fz_new_stream(fz_context *ctx, void*, int(*)(fz_stream*, unsigned char*, int), void(*)(fz_context *, void *)); +fz_stream *fz_keep_stream(fz_stream *stm); +void fz_fill_buffer(fz_stream *stm); + +void fz_read_line(fz_stream *stm, char *buf, int max); + +static inline int fz_read_byte(fz_stream *stm) +{ + if (stm->rp == stm->wp) + { + fz_fill_buffer(stm); + return stm->rp < stm->wp ? *stm->rp++ : EOF; + } + return *stm->rp++; +} + +static inline int fz_peek_byte(fz_stream *stm) +{ + if (stm->rp == stm->wp) + { + fz_fill_buffer(stm); + return stm->rp < stm->wp ? *stm->rp : EOF; + } + return *stm->rp; +} + +static inline void fz_unread_byte(fz_stream *stm) +{ + if (stm->rp > stm->bp) + stm->rp--; +} + +static inline int fz_is_eof(fz_stream *stm) +{ + if (stm->rp == stm->wp) + { + if (stm->eof) + return 1; + return fz_peek_byte(stm) == EOF; + } + return 0; +} + +static inline unsigned int fz_read_bits(fz_stream *stm, int n) +{ + unsigned int x; + + if (n <= stm->avail) + { + stm->avail -= n; + x = (stm->bits >> stm->avail) & ((1 << n) - 1); + } + else + { + x = stm->bits & ((1 << stm->avail) - 1); + n -= stm->avail; + stm->avail = 0; + + while (n > 8) + { + x = (x << 8) | fz_read_byte(stm); + n -= 8; + } + + if (n > 0) + { + stm->bits = fz_read_byte(stm); + stm->avail = 8 - n; + x = (x << n) | (stm->bits >> stm->avail); + } + } + + return x; +} + +static inline void fz_sync_bits(fz_stream *stm) +{ + stm->avail = 0; +} + +static inline int fz_is_eof_bits(fz_stream *stm) +{ + return fz_is_eof(stm) && (stm->avail == 0 || stm->bits == EOF); +} + +/* + * Data filters. + */ + +fz_stream *fz_open_copy(fz_stream *chain); +fz_stream *fz_open_null(fz_stream *chain, int len); +fz_stream *fz_open_arc4(fz_stream *chain, unsigned char *key, unsigned keylen); +fz_stream *fz_open_aesd(fz_stream *chain, unsigned char *key, unsigned keylen); +fz_stream *fz_open_a85d(fz_stream *chain); +fz_stream *fz_open_ahxd(fz_stream *chain); +fz_stream *fz_open_rld(fz_stream *chain); +fz_stream *fz_open_dctd(fz_stream *chain, int color_transform); +fz_stream *fz_open_resized_dctd(fz_stream *chain, int color_transform, int factor); +fz_stream *fz_open_faxd(fz_stream *chain, + int k, int end_of_line, int encoded_byte_align, + int columns, int rows, int end_of_block, int black_is_1); +fz_stream *fz_open_flated(fz_stream *chain); +fz_stream *fz_open_lzwd(fz_stream *chain, int early_change); +fz_stream *fz_open_predict(fz_stream *chain, int predictor, int columns, int colors, int bpc); +fz_stream *fz_open_jbig2d(fz_stream *chain, fz_buffer *global); + +/* + * Resources and other graphics related objects. + */ + +enum { FZ_MAX_COLORS = 32 }; + +int fz_lookup_blendmode(char *name); +char *fz_blendmode_name(int blendmode); + +struct fz_bitmap_s +{ + int refs; + int w, h, stride, n; + unsigned char *samples; +}; + +fz_bitmap *fz_new_bitmap(fz_context *ctx, int w, int h, int n); + +void fz_bitmap_details(fz_bitmap *bitmap, int *w, int *h, int *n, int *stride); + +void fz_clear_bitmap(fz_context *ctx, fz_bitmap *bit); + +/* + Pixmaps represent a set of pixels for a 2 dimensional region of a + plane. Each pixel has n components per pixel, the last of which is + always alpha. The data is in premultiplied alpha when rendering, but + non-premultiplied for colorspace conversions and rescaling. + + x, y: The minimum x and y coord of the region in pixels. + + w, h: The width and height of the region in pixels. + + n: The number of color components in the image. Always + includes a separate alpha channel. For mask images n=1, for greyscale + (plus alpha) images n=2, for rgb (plus alpha) images n=3. + + interpolate: A boolean flag set to non-zero if the image + will be drawn using linear interpolation, or set to zero if + image will be using nearest neighbour sampling. + + xres, yres: Image resolution in dpi. Default is 96 dpi. + + colorspace: Pointer to a colorspace object describing the colorspace + the pixmap is in. If NULL, the image is a mask. + + samples: A simple block of memory w * h * n bytes of memory in which + the components are stored. The first n bytes are components 0 to n-1 + for the pixel at (x,y). Each successive n bytes gives another pixel + in scanline order. Subsequent scanlines follow on with no padding. + + free_samples: Is zero when an application has provided its own + buffer for pixel data through fz_new_pixmap_with_bbox_and_data. + If not zero the buffer will be freed when fz_drop_pixmap is + called for the pixmap. +*/ +struct fz_pixmap_s +{ + fz_storable storable; + int x, y, w, h, n; + int interpolate; + int xres, yres; + fz_colorspace *colorspace; + unsigned char *samples; + int free_samples; +}; + +void fz_free_pixmap_imp(fz_context *ctx, fz_storable *pix); + +void fz_clear_pixmap_rect_with_value(fz_context *ctx, fz_pixmap *pix, int value, fz_bbox r); +void fz_copy_pixmap_rect(fz_context *ctx, fz_pixmap *dest, fz_pixmap *src, fz_bbox r); +void fz_premultiply_pixmap(fz_context *ctx, fz_pixmap *pix); +fz_pixmap *fz_alpha_from_gray(fz_context *ctx, fz_pixmap *gray, int luminosity); +unsigned int fz_pixmap_size(fz_context *ctx, fz_pixmap *pix); + +fz_pixmap *fz_scale_pixmap(fz_context *ctx, fz_pixmap *src, float x, float y, float w, float h, fz_bbox *clip); + +fz_bbox fz_pixmap_bbox_no_ctx(fz_pixmap *src); + +struct fz_image_s +{ + fz_storable storable; + int w, h; + fz_image *mask; + fz_colorspace *colorspace; + fz_pixmap *(*get_pixmap)(fz_context *, fz_image *, int w, int h); +}; + +fz_pixmap *fz_load_jpx(fz_context *ctx, unsigned char *data, int size, fz_colorspace *cs); +fz_pixmap *fz_load_jpeg(fz_context *doc, unsigned char *data, int size); +fz_pixmap *fz_load_png(fz_context *doc, unsigned char *data, int size); +fz_pixmap *fz_load_tiff(fz_context *doc, unsigned char *data, int size); + +struct fz_halftone_s +{ + int refs; + int n; + fz_pixmap *comp[1]; +}; + +fz_halftone *fz_new_halftone(fz_context *ctx, int num_comps); +fz_halftone *fz_default_halftone(fz_context *ctx, int num_comps); +void fz_drop_halftone(fz_context *ctx, fz_halftone *half); +fz_halftone *fz_keep_halftone(fz_context *ctx, fz_halftone *half); + +struct fz_colorspace_s +{ + fz_storable storable; + unsigned int size; + char name[16]; + int n; + void (*to_rgb)(fz_context *ctx, fz_colorspace *, float *src, float *rgb); + void (*from_rgb)(fz_context *ctx, fz_colorspace *, float *rgb, float *dst); + void (*free_data)(fz_context *Ctx, fz_colorspace *); + void *data; +}; + +fz_colorspace *fz_new_colorspace(fz_context *ctx, char *name, int n); +fz_colorspace *fz_keep_colorspace(fz_context *ctx, fz_colorspace *colorspace); +void fz_drop_colorspace(fz_context *ctx, fz_colorspace *colorspace); +void fz_free_colorspace_imp(fz_context *ctx, fz_storable *colorspace); + +void fz_convert_color(fz_context *ctx, fz_colorspace *dsts, float *dstv, fz_colorspace *srcs, float *srcv); + +/* + * Fonts come in two variants: + * Regular fonts are handled by FreeType. + * Type 3 fonts have callbacks to the interpreter. + */ + +char *ft_error_string(int err); + +struct fz_font_s +{ + int refs; + char name[32]; + + void *ft_face; /* has an FT_Face if used */ + int ft_substitute; /* ... substitute metrics */ + int ft_bold; /* ... synthesize bold */ + int ft_italic; /* ... synthesize italic */ + int ft_hint; /* ... force hinting for DynaLab fonts */ + + /* origin of font data */ + char *ft_file; + unsigned char *ft_data; + int ft_size; + + fz_matrix t3matrix; + void *t3resources; + fz_buffer **t3procs; /* has 256 entries if used */ + float *t3widths; /* has 256 entries if used */ + char *t3flags; /* has 256 entries if used */ + void *t3doc; /* a pdf_document for the callback */ + void (*t3run)(void *doc, void *resources, fz_buffer *contents, fz_device *dev, fz_matrix ctm, void *gstate); + void (*t3freeres)(void *doc, void *resources); + + fz_rect bbox; /* font bbox is used only for t3 fonts */ + + /* per glyph bounding box cache */ + int use_glyph_bbox; + int bbox_count; + fz_rect *bbox_table; + + /* substitute metrics */ + int width_count; + int *width_table; /* in 1000 units */ +}; + +void fz_new_font_context(fz_context *ctx); +fz_font_context *fz_keep_font_context(fz_context *ctx); +void fz_drop_font_context(fz_context *ctx); + +fz_font *fz_new_type3_font(fz_context *ctx, char *name, fz_matrix matrix); + +fz_font *fz_new_font_from_memory(fz_context *ctx, unsigned char *data, int len, int index, int use_glyph_bbox); +fz_font *fz_new_font_from_file(fz_context *ctx, char *path, int index, int use_glyph_bbox); + +fz_font *fz_keep_font(fz_context *ctx, fz_font *font); +void fz_drop_font(fz_context *ctx, fz_font *font); + +void fz_print_font(fz_context *ctx, FILE *out, fz_font *font); + +void fz_set_font_bbox(fz_context *ctx, fz_font *font, float xmin, float ymin, float xmax, float ymax); +fz_rect fz_bound_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix trm); +int fz_glyph_cacheable(fz_context *ctx, fz_font *font, int gid); + +/* + * Vector path buffer. + * It can be stroked and dashed, or be filled. + * It has a fill rule (nonzero or even_odd). + * + * When rendering, they are flattened, stroked and dashed straight + * into the Global Edge List. + */ + +typedef struct fz_path_s fz_path; +typedef struct fz_stroke_state_s fz_stroke_state; + +typedef union fz_path_item_s fz_path_item; + +typedef enum fz_path_item_kind_e +{ + FZ_MOVETO, + FZ_LINETO, + FZ_CURVETO, + FZ_CLOSE_PATH +} fz_path_item_kind; + +typedef enum fz_linecap_e +{ + FZ_LINECAP_BUTT = 0, + FZ_LINECAP_ROUND = 1, + FZ_LINECAP_SQUARE = 2, + FZ_LINECAP_TRIANGLE = 3 +} fz_linecap; + +typedef enum fz_linejoin_e +{ + FZ_LINEJOIN_MITER = 0, + FZ_LINEJOIN_ROUND = 1, + FZ_LINEJOIN_BEVEL = 2, + FZ_LINEJOIN_MITER_XPS = 3 +} fz_linejoin; + +union fz_path_item_s +{ + fz_path_item_kind k; + float v; +}; + +struct fz_path_s +{ + int len, cap; + fz_path_item *items; + int last; +}; + +struct fz_stroke_state_s +{ + fz_linecap start_cap, dash_cap, end_cap; + fz_linejoin linejoin; + float linewidth; + float miterlimit; + float dash_phase; + int dash_len; + float dash_list[32]; +}; + +fz_path *fz_new_path(fz_context *ctx); +void fz_moveto(fz_context*, fz_path*, float x, float y); +void fz_lineto(fz_context*, fz_path*, float x, float y); +void fz_curveto(fz_context*,fz_path*, float, float, float, float, float, float); +void fz_curvetov(fz_context*,fz_path*, float, float, float, float); +void fz_curvetoy(fz_context*,fz_path*, float, float, float, float); +void fz_closepath(fz_context*,fz_path*); +void fz_free_path(fz_context *ctx, fz_path *path); + +void fz_transform_path(fz_context *ctx, fz_path *path, fz_matrix transform); + +fz_path *fz_clone_path(fz_context *ctx, fz_path *old); + +fz_rect fz_bound_path(fz_context *ctx, fz_path *path, fz_stroke_state *stroke, fz_matrix ctm); +void fz_print_path(fz_context *ctx, FILE *out, fz_path *, int indent); + +/* + * Glyph cache + */ + +void fz_new_glyph_cache_context(fz_context *ctx); +fz_glyph_cache *fz_keep_glyph_cache(fz_context *ctx); +void fz_drop_glyph_cache_context(fz_context *ctx); +void fz_purge_glyph_cache(fz_context *ctx); + +fz_pixmap *fz_render_ft_glyph(fz_context *ctx, fz_font *font, int cid, fz_matrix trm, int aa); +fz_pixmap *fz_render_t3_glyph(fz_context *ctx, fz_font *font, int cid, fz_matrix trm, fz_colorspace *model); +fz_pixmap *fz_render_ft_stroked_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix trm, fz_matrix ctm, fz_stroke_state *state); +fz_pixmap *fz_render_glyph(fz_context *ctx, fz_font*, int, fz_matrix, fz_colorspace *model); +fz_pixmap *fz_render_stroked_glyph(fz_context *ctx, fz_font*, int, fz_matrix, fz_matrix, fz_stroke_state *stroke); +void fz_render_t3_glyph_direct(fz_context *ctx, fz_device *dev, fz_font *font, int gid, fz_matrix trm, void *gstate); + +/* + * Text buffer. + * + * The trm field contains the a, b, c and d coefficients. + * The e and f coefficients come from the individual elements, + * together they form the transform matrix for the glyph. + * + * Glyphs are referenced by glyph ID. + * The Unicode text equivalent is kept in a separate array + * with indexes into the glyph array. + */ + +typedef struct fz_text_s fz_text; +typedef struct fz_text_item_s fz_text_item; + +struct fz_text_item_s +{ + float x, y; + int gid; /* -1 for one gid to many ucs mappings */ + int ucs; /* -1 for one ucs to many gid mappings */ +}; + +struct fz_text_s +{ + fz_font *font; + fz_matrix trm; + int wmode; + int len, cap; + fz_text_item *items; +}; + +fz_text *fz_new_text(fz_context *ctx, fz_font *face, fz_matrix trm, int wmode); +void fz_add_text(fz_context *ctx, fz_text *text, int gid, int ucs, float x, float y); +void fz_free_text(fz_context *ctx, fz_text *text); +fz_rect fz_bound_text(fz_context *ctx, fz_text *text, fz_matrix ctm); +fz_text *fz_clone_text(fz_context *ctx, fz_text *old); +void fz_print_text(fz_context *ctx, FILE *out, fz_text*); + +/* + * The shading code uses gouraud shaded triangle meshes. + */ + +enum +{ + FZ_LINEAR, + FZ_RADIAL, + FZ_MESH, +}; + +typedef struct fz_shade_s fz_shade; + +struct fz_shade_s +{ + fz_storable storable; + + fz_rect bbox; /* can be fz_infinite_rect */ + fz_colorspace *colorspace; + + fz_matrix matrix; /* matrix from pattern dict */ + int use_background; /* background color for fills but not 'sh' */ + float background[FZ_MAX_COLORS]; + + int use_function; + float function[256][FZ_MAX_COLORS + 1]; + + int type; /* linear, radial, mesh */ + int extend[2]; + + int mesh_len; + int mesh_cap; + float *mesh; /* [x y 0], [x y r], [x y t] or [x y c1 ... cn] */ +}; + +fz_shade *fz_keep_shade(fz_context *ctx, fz_shade *shade); +void fz_drop_shade(fz_context *ctx, fz_shade *shade); +void fz_free_shade_imp(fz_context *ctx, fz_storable *shade); +void fz_print_shade(fz_context *ctx, FILE *out, fz_shade *shade); + +fz_rect fz_bound_shade(fz_context *ctx, fz_shade *shade, fz_matrix ctm); +void fz_paint_shade(fz_context *ctx, fz_shade *shade, fz_matrix ctm, fz_pixmap *dest, fz_bbox bbox); + +/* + * Scan converter + */ + +typedef struct fz_gel_s fz_gel; + +fz_gel *fz_new_gel(fz_context *ctx); +void fz_insert_gel(fz_gel *gel, float x0, float y0, float x1, float y1); +void fz_reset_gel(fz_gel *gel, fz_bbox clip); +void fz_sort_gel(fz_gel *gel); +fz_bbox fz_bound_gel(fz_gel *gel); +void fz_free_gel(fz_gel *gel); +int fz_is_rect_gel(fz_gel *gel); + +void fz_scan_convert(fz_gel *gel, int eofill, fz_bbox clip, fz_pixmap *pix, unsigned char *colorbv); + +void fz_flatten_fill_path(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness); +void fz_flatten_stroke_path(fz_gel *gel, fz_path *path, fz_stroke_state *stroke, fz_matrix ctm, float flatness, float linewidth); +void fz_flatten_dash_path(fz_gel *gel, fz_path *path, fz_stroke_state *stroke, fz_matrix ctm, float flatness, float linewidth); + +/* + * The device interface. + */ + +fz_device *fz_new_draw_device_type3(fz_context *ctx, fz_pixmap *dest); + +enum +{ + /* Hints */ + FZ_IGNORE_IMAGE = 1, + FZ_IGNORE_SHADE = 2, + + /* Flags */ + FZ_DEVFLAG_MASK = 1, + FZ_DEVFLAG_COLOR = 2, + FZ_DEVFLAG_UNCACHEABLE = 4, + FZ_DEVFLAG_FILLCOLOR_UNDEFINED = 8, + FZ_DEVFLAG_STROKECOLOR_UNDEFINED = 16, + FZ_DEVFLAG_STARTCAP_UNDEFINED = 32, + FZ_DEVFLAG_DASHCAP_UNDEFINED = 64, + FZ_DEVFLAG_ENDCAP_UNDEFINED = 128, + FZ_DEVFLAG_LINEJOIN_UNDEFINED = 256, + FZ_DEVFLAG_MITERLIMIT_UNDEFINED = 512, + FZ_DEVFLAG_LINEWIDTH_UNDEFINED = 1024, + /* Arguably we should have a bit for the dash pattern itself being + * undefined, but that causes problems; do we assume that it should + * always be set to non-dashing at the start of every glyph? */ +}; + +struct fz_device_s +{ + int hints; + int flags; + + void *user; + void (*free_user)(fz_device *); + fz_context *ctx; + + void (*fill_path)(fz_device *, fz_path *, int even_odd, fz_matrix, fz_colorspace *, float *color, float alpha); + void (*stroke_path)(fz_device *, fz_path *, fz_stroke_state *, fz_matrix, fz_colorspace *, float *color, float alpha); + void (*clip_path)(fz_device *, fz_path *, fz_rect *rect, int even_odd, fz_matrix); + void (*clip_stroke_path)(fz_device *, fz_path *, fz_rect *rect, fz_stroke_state *, fz_matrix); + + void (*fill_text)(fz_device *, fz_text *, fz_matrix, fz_colorspace *, float *color, float alpha); + void (*stroke_text)(fz_device *, fz_text *, fz_stroke_state *, fz_matrix, fz_colorspace *, float *color, float alpha); + void (*clip_text)(fz_device *, fz_text *, fz_matrix, int accumulate); + void (*clip_stroke_text)(fz_device *, fz_text *, fz_stroke_state *, fz_matrix); + void (*ignore_text)(fz_device *, fz_text *, fz_matrix); + + void (*fill_shade)(fz_device *, fz_shade *shd, fz_matrix ctm, float alpha); + void (*fill_image)(fz_device *, fz_image *img, fz_matrix ctm, float alpha); + void (*fill_image_mask)(fz_device *, fz_image *img, fz_matrix ctm, fz_colorspace *, float *color, float alpha); + void (*clip_image_mask)(fz_device *, fz_image *img, fz_rect *rect, fz_matrix ctm); + + void (*pop_clip)(fz_device *); + + void (*begin_mask)(fz_device *, fz_rect, int luminosity, fz_colorspace *, float *bc); + void (*end_mask)(fz_device *); + void (*begin_group)(fz_device *, fz_rect, int isolated, int knockout, int blendmode, float alpha); + void (*end_group)(fz_device *); + + void (*begin_tile)(fz_device *, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm); + void (*end_tile)(fz_device *); +}; + +void fz_fill_path(fz_device *dev, fz_path *path, int even_odd, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha); +void fz_stroke_path(fz_device *dev, fz_path *path, fz_stroke_state *stroke, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha); +void fz_clip_path(fz_device *dev, fz_path *path, fz_rect *rect, int even_odd, fz_matrix ctm); +void fz_clip_stroke_path(fz_device *dev, fz_path *path, fz_rect *rect, fz_stroke_state *stroke, fz_matrix ctm); +void fz_fill_text(fz_device *dev, fz_text *text, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha); +void fz_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha); +void fz_clip_text(fz_device *dev, fz_text *text, fz_matrix ctm, int accumulate); +void fz_clip_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, fz_matrix ctm); +void fz_ignore_text(fz_device *dev, fz_text *text, fz_matrix ctm); +void fz_pop_clip(fz_device *dev); +void fz_fill_shade(fz_device *dev, fz_shade *shade, fz_matrix ctm, float alpha); +void fz_fill_image(fz_device *dev, fz_image *image, fz_matrix ctm, float alpha); +void fz_fill_image_mask(fz_device *dev, fz_image *image, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha); +void fz_clip_image_mask(fz_device *dev, fz_image *image, fz_rect *rect, fz_matrix ctm); +void fz_begin_mask(fz_device *dev, fz_rect area, int luminosity, fz_colorspace *colorspace, float *bc); +void fz_end_mask(fz_device *dev); +void fz_begin_group(fz_device *dev, fz_rect area, int isolated, int knockout, int blendmode, float alpha); +void fz_end_group(fz_device *dev); +void fz_begin_tile(fz_device *dev, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm); +void fz_end_tile(fz_device *dev); + +fz_device *fz_new_device(fz_context *ctx, void *user); + + + +/* + * Plotting functions. + */ + +void fz_decode_tile(fz_pixmap *pix, float *decode); +void fz_decode_indexed_tile(fz_pixmap *pix, float *decode, int maxval); +void fz_unpack_tile(fz_pixmap *dst, unsigned char * restrict src, int n, int depth, int stride, int scale); + +void fz_paint_solid_alpha(unsigned char * restrict dp, int w, int alpha); +void fz_paint_solid_color(unsigned char * restrict dp, int n, int w, unsigned char *color); + +void fz_paint_span(unsigned char * restrict dp, unsigned char * restrict sp, int n, int w, int alpha); +void fz_paint_span_with_color(unsigned char * restrict dp, unsigned char * restrict mp, int n, int w, unsigned char *color); + +void fz_paint_image(fz_pixmap *dst, fz_bbox scissor, fz_pixmap *shape, fz_pixmap *img, fz_matrix ctm, int alpha); +void fz_paint_image_with_color(fz_pixmap *dst, fz_bbox scissor, fz_pixmap *shape, fz_pixmap *img, fz_matrix ctm, unsigned char *colorbv); + +void fz_paint_pixmap(fz_pixmap *dst, fz_pixmap *src, int alpha); +void fz_paint_pixmap_with_mask(fz_pixmap *dst, fz_pixmap *src, fz_pixmap *msk); +void fz_paint_pixmap_with_rect(fz_pixmap *dst, fz_pixmap *src, int alpha, fz_bbox bbox); + +void fz_blend_pixmap(fz_pixmap *dst, fz_pixmap *src, int alpha, int blendmode, int isolated, fz_pixmap *shape); +void fz_blend_pixel(unsigned char dp[3], unsigned char bp[3], unsigned char sp[3], int blendmode); + +enum +{ + /* PDF 1.4 -- standard separable */ + FZ_BLEND_NORMAL, + FZ_BLEND_MULTIPLY, + FZ_BLEND_SCREEN, + FZ_BLEND_OVERLAY, + FZ_BLEND_DARKEN, + FZ_BLEND_LIGHTEN, + FZ_BLEND_COLOR_DODGE, + FZ_BLEND_COLOR_BURN, + FZ_BLEND_HARD_LIGHT, + FZ_BLEND_SOFT_LIGHT, + FZ_BLEND_DIFFERENCE, + FZ_BLEND_EXCLUSION, + + /* PDF 1.4 -- standard non-separable */ + FZ_BLEND_HUE, + FZ_BLEND_SATURATION, + FZ_BLEND_COLOR, + FZ_BLEND_LUMINOSITY, + + /* For packing purposes */ + FZ_BLEND_MODEMASK = 15, + FZ_BLEND_ISOLATED = 16, + FZ_BLEND_KNOCKOUT = 32 +}; + +struct fz_document_s +{ + void (*close)(fz_document *); + int (*needs_password)(fz_document *doc); + int (*authenticate_password)(fz_document *doc, char *password); + fz_outline *(*load_outline)(fz_document *doc); + int (*count_pages)(fz_document *doc); + fz_page *(*load_page)(fz_document *doc, int number); + fz_link *(*load_links)(fz_document *doc, fz_page *page); + fz_rect (*bound_page)(fz_document *doc, fz_page *page); + void (*run_page)(fz_document *doc, fz_page *page, fz_device *dev, fz_matrix transform, fz_cookie *cookie); + void (*free_page)(fz_document *doc, fz_page *page); +}; + +#endif diff --git a/fitz/fitz.h b/fitz/fitz.h index d2672031..26751824 100644 --- a/fitz/fitz.h +++ b/fitz/fitz.h @@ -1,9 +1,9 @@ -#ifndef _FITZ_H_ -#define _FITZ_H_ +#ifndef FITZ_H +#define FITZ_H /* - * Include the standard libc headers. - */ + Include the standard libc headers. +*/ #include <stdio.h> #include <stdlib.h> @@ -15,13 +15,27 @@ #include <assert.h> #include <errno.h> #include <limits.h> /* INT_MAX & co */ -#include <float.h> /* FLT_EPSILON */ +#include <float.h> /* FLT_EPSILON, FLT_MAX & co */ #include <fcntl.h> /* O_RDONLY & co */ #include <setjmp.h> #include "memento.h" +/* + Some versions of setjmp/longjmp (notably MacOSX and ios) store/restore + signal handlers too. We don't alter signal handlers within mupdf, so + there is no need for us to store/restore - hence we use the + non-restoring variants. This makes a large speed difference. +*/ +#ifdef __APPLE__ +#define fz_setjmp _setjmp +#define fz_longjmp _longjmp +#else +#define fz_setjmp setjmp +#define fz_longjmp longjmp +#endif + #ifdef __ANDROID__ #include <android/log.h> #define LOG_TAG "libmupdf" @@ -38,10 +52,11 @@ #define MIN(a,b) ( (a) < (b) ? (a) : (b) ) #define MAX(a,b) ( (a) > (b) ? (a) : (b) ) #define CLAMP(x,a,b) ( (x) > (b) ? (b) : ( (x) < (a) ? (a) : (x) ) ) +#define DIV_BY_ZERO(a, b, min, max) (((a) < 0) ^ ((b) < 0) ? (min) : (max)) /* - * Some differences in libc can be smoothed over - */ + Some differences in libc can be smoothed over +*/ #ifdef _MSC_VER /* Microsoft Visual C */ @@ -54,7 +69,7 @@ int gettimeofday(struct timeval *tv, struct timezone *tz); #define snprintf _snprintf -#define strtoll _strtoi64 +#define isnan _isnan #else /* Unix or close enough */ @@ -75,8 +90,8 @@ int gettimeofday(struct timeval *tv, struct timezone *tz); #endif /* - * Variadic macros, inline and restrict keywords - */ + Variadic macros, inline and restrict keywords +*/ #if __STDC_VERSION__ == 199901L /* C99 */ #elif _MSC_VER >= 1500 /* MSVC 9 or newer */ @@ -91,8 +106,8 @@ int gettimeofday(struct timeval *tv, struct timezone *tz); #endif /* - * GCC can do type checking of printf strings - */ + GCC can do type checking of printf strings +*/ #ifndef __printflike #if __GNUC__ > 2 || __GNUC__ == 2 && __GNUC_MINOR__ >= 7 @@ -103,7 +118,9 @@ int gettimeofday(struct timeval *tv, struct timezone *tz); #endif #endif -/* Contexts */ +/* + Contexts +*/ typedef struct fz_alloc_context_s fz_alloc_context; typedef struct fz_error_context_s fz_error_context; @@ -123,9 +140,6 @@ struct fz_alloc_context_s void (*free)(void *, void *); }; -/* Default allocator */ -extern fz_alloc_context fz_alloc_default; - struct fz_error_context_s { int top; @@ -140,139 +154,13 @@ void fz_var_imp(void *); #define fz_var(var) fz_var_imp((void *)&(var)) /* - -MuPDF uses a set of exception handling macros to simplify error return -and cleanup. Conceptually, they work a lot like C++'s try/catch system, -but do not require any special compiler support. - -The basic formulation is as follows: - - fz_try(ctx) - { - // Try to perform a task. Never 'return', 'goto' or 'longjmp' out - // of here. 'break' may be used to safely exit (just) the try block - // scope. - } - fz_always(ctx) - { - // Any code here is always executed, regardless of whether an - // exception was thrown within the try or not. Never 'return', 'goto' - // or longjmp out from here. 'break' may be used to safely exit (just) - // the always block scope. - } - fz_catch(ctx) - { - // This code is called (after any always block) only if something - // within the fz_try block (including any functions it called) threw - // an exception. The code here is expected to handle the exception - // (maybe record/report the error, cleanup any stray state etc) and - // can then either exit the block, or pass on the exception to a - // higher level (enclosing) fz_try block (using fz_throw, or - // fz_rethrow). - } - -The fz_always block is optional, and can safely be omitted. - -The macro based nature of this system has 3 main limitations: - -1) Never return from within try (or 'goto' or longjmp out of it). - This upsets the internal housekeeping of the macros and will cause - problems later on. The code will detect such things happening, but - by then it is too late to give a helpful error report as to where the - original infraction occurred. - -2) The fz_try(ctx) { ... } fz_always(ctx) { ... } fz_catch(ctx) { ... } - is not one atomic C statement. That is to say, if you do: - - if (condition) - fz_try(ctx) { ... } - fz_catch(ctx) { ... } - - then you will not get what you want. Use the following instead: - - if (condition) { - fz_try(ctx) { ... } - fz_catch(ctx) { ... } - } - -3) The macros are implemented using setjmp and longjmp, and so the standard - C restrictions on the use of those functions apply to fz_try/fz_catch - too. In particular, any "truly local" variable that is set between the - start of fz_try and something in fz_try throwing an exception may become - undefined as part of the process of throwing that exception. - - As a way of mitigating this problem, we provide an fz_var() macro that - tells the compiler to ensure that that variable is not unset by the - act of throwing the exception. - -A model piece of code using these macros then might be: - - house build_house(plans *p) - { - material m = NULL; - walls w = NULL; - roof r = NULL; - house h = NULL; - tiles t = make_tiles(); - - fz_var(w); - fz_var(r); - fz_var(h); - - fz_try(ctx) - { - fz_try(ctx) - { - m = make_bricks(); - } - fz_catch(ctx) - { - // No bricks available, make do with straw? - m = make_straw(); - } - w = make_walls(m, p); - r = make_roof(m, t); - h = combine(w, r); // Note, NOT: return combine(w,r); - } - fz_always(ctx) - { - drop_walls(w); - drop_roof(r); - drop_material(m); - drop_tiles(t); - } - fz_catch(ctx) - { - fz_throw(ctx, "build_house failed"); - } - return h; - } - -Things to note about this: - -a) If make_tiles throws an exception, this will immediately be handled - by some higher level exception handler. If it succeeds, t will be - set before fz_try starts, so there is no need to fz_var(t); - -b) We try first off to make some bricks as our building material. If - this fails, we fall back to straw. If this fails, we'll end up in - the fz_catch, and the process will fail neatly. - -c) We assume in this code that combine takes new reference to both the - walls and the roof it uses, and therefore that w and r need to be - cleaned up in all cases. - -d) We assume the standard C convention that it is safe to destroy - NULL things. - + Exception macro definitions. Just treat these as a black box - pay no + attention to the man behind the curtain. */ -/* Exception macro definitions. Just treat these as a black box - pay no - * attention to the man behind the curtain. */ - #define fz_try(ctx) \ if (fz_push_try(ctx->error), \ - (ctx->error->stack[ctx->error->top].code = setjmp(ctx->error->stack[ctx->error->top].buffer)) == 0) \ + (ctx->error->stack[ctx->error->top].code = fz_setjmp(ctx->error->stack[ctx->error->top].buffer)) == 0) \ { do { #define fz_always(ctx) \ @@ -285,78 +173,22 @@ d) We assume the standard C convention that it is safe to destroy } \ if (ctx->error->stack[ctx->error->top--].code) -/* - -We also include a couple of other formulations of the macros, with -different strengths and weaknesses. These will be removed shortly, but -I want them in git for at least 1 revision so I have a record of them. - -A formulation of try/always/catch that lifts limitation 2 above, but -has problems when try/catch are nested in the same function; the inner -nestings need to use fz_always_(ctx, label) and fz_catch_(ctx, label) -instead. This was held as too high a price to pay to drop limitation 2. - -#define fz_try(ctx) \ - if (fz_push_try(ctx->error), \ - (ctx->error->stack[ctx->error->top].code = setjmp(ctx->error->stack[ctx->error->top].buffer)) == 0) \ - { do { - -#define fz_always_(ctx, label) \ - } while (0); \ - goto ALWAYS_LABEL_ ## label ; \ - } \ - else if (ctx->error->stack[ctx->error->top].code) \ - { ALWAYS_LABEL_ ## label : \ - do { - -#define fz_catch_(ctx, label) \ - } while(0); \ - if (ctx->error->stack[ctx->error->top--].code) \ - goto CATCH_LABEL_ ## label; \ - } \ - else if (ctx->error->top--, 1) \ - CATCH_LABEL ## label: - -#define fz_always(ctx) fz_always_(ctx, TOP) -#define fz_catch(ctx) fz_catch_(ctx, TOP) - -Another alternative formulation, that again removes limitation 2, but at -the cost of an always block always costing us 1 extra longjmp per -execution. Again this was felt to be too high a cost to use. - -#define fz_try(ctx) \ - if (fz_push_try(ctx->error), \ - (ctx->error->stack[ctx->error->top].code = setjmp(ctx->error->stack[ctx->error->top].buffer)) == 0) \ - { do { - -#define fz_always(ctx) \ - } while (0); \ - longjmp(ctx->error->stack[ctx->error->top].buffer, 3); \ - } \ - else if (ctx->error->stack[ctx->error->top].code & 1) \ - { do { - -#define fz_catch(ctx) \ - } while(0); \ - if (ctx->error->stack[ctx->error->top].code == 1) \ - longjmp(ctx->error->stack[ctx->error->top].buffer, 2); \ - ctx->error->top--;\ - } \ - else if (ctx->error->top--, 1) - -*/ - void fz_push_try(fz_error_context *ex); void fz_throw(fz_context *, char *, ...) __printflike(2, 3); void fz_rethrow(fz_context *); +void fz_warn(fz_context *ctx, char *fmt, ...) __printflike(2, 3); -struct fz_warn_context_s -{ - char message[256]; - int count; -}; +/* + fz_flush_warnings: Flush any repeated warnings. -void fz_warn(fz_context *ctx, char *fmt, ...) __printflike(2, 3); + Repeated warnings are buffered, counted and eventually printed + along with the number of repetitions. Call fz_flush_warnings + to force printing of the latest buffered warning and the + number of repetitions, for example to make sure that all + warnings are printed before exiting an application. + + Does not throw exceptions. +*/ void fz_flush_warnings(fz_context *ctx); struct fz_context_s @@ -371,42 +203,118 @@ struct fz_context_s fz_glyph_cache *glyph_cache; }; +/* + Specifies the maximum size in bytes of the resource store in + fz_context. Given as argument to fz_new_context. + + FZ_STORE_UNLIMITED: Let resource store grow unbounded. + + FZ_STORE_DEFAULT: A reasonable upper bound on the size, for + devices that are not memory constrained. +*/ +enum { + FZ_STORE_UNLIMITED = 0, + FZ_STORE_DEFAULT = 256 << 20, +}; + +/* + fz_new_context: Allocate context containing global state. + + The global state contains an exception stack, resource store, + etc. Most functions in MuPDF take a context argument to be + able to reference the global state. See fz_free_context for + freeing an allocated context. + + alloc: Supply a custom memory allocator through a set of + function pointers. Set to NULL for the standard library + allocator. The context will keep the allocator pointer, so the + data it points to must not be modified or freed during the + lifetime of the context. + + locks: Supply a set of locks and functions to lock/unlock + them, intended for multi-threaded applications. Set to NULL + when using MuPDF in a single-threaded applications. The + context will keep the locks pointer, so the data it points to + must not be modified or freed during the lifetime of the + context. + + max_store: Maximum size in bytes of the resource store, before + it will start evicting cached resources such as fonts and + images. FZ_STORE_UNLIMITED can be used if a hard limit is not + desired. Use FZ_STORE_DEFAULT to get a reasonable size. + + Does not throw exceptions, but may return NULL. +*/ fz_context *fz_new_context(fz_alloc_context *alloc, fz_locks_context *locks, unsigned int max_store); + +/* + fz_clone_context: Make a clone of an existing context. + + This function is meant to be used in multi-threaded + applications where each thread requires its own context, yet + parts of the global state, for example caching, is shared. + + ctx: Context obtained from fz_new_context to make a copy of. + ctx must have had locks and lock/functions setup when created. + The two contexts will share the memory allocator, resource + store, locks and lock/unlock functions. They will each have + their own exception stacks though. + + Does not throw exception, but may return NULL. +*/ fz_context *fz_clone_context(fz_context *ctx); -fz_context *fz_clone_context_internal(fz_context *ctx); + +/* + fz_free_context: Free a context and its global state. + + The context and all of its global state is freed, and any + buffered warnings are flushed (see fz_flush_warnings). If NULL + is passed in nothing will happen. + + Does not throw exceptions. +*/ void fz_free_context(fz_context *ctx); -void fz_new_aa_context(fz_context *ctx); -void fz_free_aa_context(fz_context *ctx); - -/* Locking functions - * - * MuPDF is kept deliberately free of any knowledge of particular threading - * systems. As such, in order for safe multi-threaded operation, we rely on - * callbacks to client provided functions. - * - * A client is expected to provide FZ_LOCK_MAX mutexes, and a function to - * lock/unlock each of them. These may be recursive mutexes, but do not have - * to be. - * - * If a client does not intend to use multiple threads, then it may pass - * NULL instead of the address of a lock structure. - * - * In order to avoid deadlocks, we have 1 simple rules internally as to how - * we use locks: We can never take lock n when we already hold any lock i, - * where 0 <= i <= n. In order to verify this, we have some debugging code - * built in, that is enabled by defining FITZ_DEBUG_LOCKING. - */ +/* + fz_aa_level: Get the number of bits of antialiasing we are + using. Between 0 and 8. +*/ +int fz_aa_level(fz_context *ctx); -#if defined(MEMENTO) || defined(DEBUG) -#define FITZ_DEBUG_LOCKING -#endif +/* + fz_set_aa_level: Set the number of bits of antialiasing we should use. + + bits: The number of bits of antialiasing to use (values are clamped + to within the 0 to 8 range). +*/ +void fz_set_aa_level(fz_context *ctx, int bits); + +/* + Locking functions + + MuPDF is kept deliberately free of any knowledge of particular + threading systems. As such, in order for safe multi-threaded + operation, we rely on callbacks to client provided functions. + + A client is expected to provide FZ_LOCK_MAX number of mutexes, + and a function to lock/unlock each of them. These may be + recursive mutexes, but do not have to be. + + If a client does not intend to use multiple threads, then it + may pass NULL instead of a lock structure. + + In order to avoid deadlocks, we have one simple rule + internally as to how we use locks: We can never take lock n + when we already hold any lock i, where 0 <= i <= n. In order + to verify this, we have some debugging code, that can be + enabled by defining FITZ_DEBUG_LOCKING. +*/ struct fz_locks_context_s { void *user; - void (*lock)(void *, int); - void (*unlock)(void *, int); + void (*lock)(void *user, int lock); + void (*unlock)(void *user, int lock); }; enum { @@ -417,1076 +325,1257 @@ enum { FZ_LOCK_MAX }; -/* Default locks */ -extern fz_locks_context fz_locks_default; +/* + Memory Allocation and Scavenging: -#ifdef FITZ_DEBUG_LOCKING + All calls to MuPDFs allocator functions pass through to the + underlying allocators passed in when the initial context is + created, after locks are taken (using the supplied locking function) + to ensure that only one thread at a time calls through. -void fz_assert_lock_held(fz_context *ctx, int lock); -void fz_assert_lock_not_held(fz_context *ctx, int lock); -void fz_lock_debug_lock(fz_context *ctx, int lock); -void fz_lock_debug_unlock(fz_context *ctx, int lock); + If the underlying allocator fails, MuPDF attempts to make room for + the allocation by evicting elements from the store, then retrying. -#else + Any call to allocate may then result in several calls to the underlying + allocator, and result in elements that are only referred to by the + store being freed. +*/ -#define fz_assert_lock_held(A,B) do { } while (0) -#define fz_assert_lock_not_held(A,B) do { } while (0) -#define fz_lock_debug_lock(A,B) do { } while (0) -#define fz_lock_debug_unlock(A,B) do { } while (0) +/* + fz_malloc: Allocate a block of memory (with scavenging) -#endif /* !FITZ_DEBUG_LOCKING */ + size: The number of bytes to allocate. -static inline void -fz_lock(fz_context *ctx, int lock) -{ - fz_lock_debug_lock(ctx, lock); - ctx->locks->lock(ctx->locks->user, lock); -} + Returns a pointer to the allocated block. May return NULL if size is + 0. Throws exception on failure to allocate. +*/ +void *fz_malloc(fz_context *ctx, unsigned int size); -static inline void -fz_unlock(fz_context *ctx, int lock) -{ - fz_lock_debug_unlock(ctx, lock); - ctx->locks->unlock(ctx->locks->user, lock); -} +/* + fz_calloc: Allocate a zeroed block of memory (with scavenging) + + count: The number of objects to allocate space for. + size: The size (in bytes) of each object. + + Returns a pointer to the allocated block. May return NULL if size + and/or count are 0. Throws exception on failure to allocate. +*/ +void *fz_calloc(fz_context *ctx, unsigned int count, unsigned int size); /* - * Basic runtime and utility functions - */ + fz_malloc_array: Allocate a block of (non zeroed) memory (with + scavenging). Equivalent to fz_calloc without the memory clearing. -/* memory allocation */ + count: The number of objects to allocate space for. -/* The following throw exceptions on failure to allocate */ -void *fz_malloc(fz_context *ctx, unsigned int size); -void *fz_calloc(fz_context *ctx, unsigned int count, unsigned int size); + size: The size (in bytes) of each object. + + Returns a pointer to the allocated block. May return NULL if size + and/or count are 0. Throws exception on failure to allocate. +*/ void *fz_malloc_array(fz_context *ctx, unsigned int count, unsigned int size); + +/* + fz_resize_array: Resize a block of memory (with scavenging). + + p: The existing block to resize + + count: The number of objects to resize to. + + size: The size (in bytes) of each object. + + Returns a pointer to the resized block. May return NULL if size + and/or count are 0. Throws exception on failure to resize (original + block is left unchanged). +*/ void *fz_resize_array(fz_context *ctx, void *p, unsigned int count, unsigned int size); + +/* + fz_strdup: Duplicate a C string (with scavenging) + + s: The string to duplicate. + + Returns a pointer to a duplicated string. Throws exception on failure + to allocate. +*/ char *fz_strdup(fz_context *ctx, char *s); +/* + fz_free: Frees an allocation. + + Does not throw exceptions. +*/ void fz_free(fz_context *ctx, void *p); -/* The following returns NULL on failure to allocate */ +/* + fz_malloc_no_throw: Allocate a block of memory (with scavenging) + + size: The number of bytes to allocate. + + Returns a pointer to the allocated block. May return NULL if size is + 0. Returns NULL on failure to allocate. +*/ void *fz_malloc_no_throw(fz_context *ctx, unsigned int size); -void *fz_malloc_array_no_throw(fz_context *ctx, unsigned int count, unsigned int size); + +/* + fz_calloc_no_throw: Allocate a zeroed block of memory (with scavenging) + + count: The number of objects to allocate space for. + + size: The size (in bytes) of each object. + + Returns a pointer to the allocated block. May return NULL if size + and/or count are 0. Returns NULL on failure to allocate. +*/ void *fz_calloc_no_throw(fz_context *ctx, unsigned int count, unsigned int size); + +/* + fz_malloc_array_no_throw: Allocate a block of (non zeroed) memory + (with scavenging). Equivalent to fz_calloc_no_throw without the + memory clearing. + + count: The number of objects to allocate space for. + + size: The size (in bytes) of each object. + + Returns a pointer to the allocated block. May return NULL if size + and/or count are 0. Returns NULL on failure to allocate. +*/ +void *fz_malloc_array_no_throw(fz_context *ctx, unsigned int count, unsigned int size); + +/* + fz_resize_array_no_throw: Resize a block of memory (with scavenging). + + p: The existing block to resize + + count: The number of objects to resize to. + + size: The size (in bytes) of each object. + + Returns a pointer to the resized block. May return NULL if size + and/or count are 0. Returns NULL on failure to resize (original + block is left unchanged). +*/ void *fz_resize_array_no_throw(fz_context *ctx, void *p, unsigned int count, unsigned int size); + +/* + fz_strdup_no_throw: Duplicate a C string (with scavenging) + + s: The string to duplicate. + + Returns a pointer to a duplicated string. Returns NULL on failure + to allocate. +*/ char *fz_strdup_no_throw(fz_context *ctx, char *s); -/* alloc and zero a struct, and tag it for memento */ -#define fz_malloc_struct(CTX, STRUCT) \ - Memento_label(fz_calloc(CTX,1,sizeof(STRUCT)), #STRUCT) +/* + Safe string functions +*/ +/* + fz_strsep: Given a pointer to a C string (or a pointer to NULL) break + it at the first occurence of a delimiter char (from a given set). -/* runtime (hah!) test for endian-ness */ -int fz_is_big_endian(void); + stringp: Pointer to a C string pointer (or NULL). Updated on exit to + point to the first char of the string after the delimiter that was + found. The string pointed to by stringp will be corrupted by this + call (as the found delimiter will be overwritten by 0). -/* safe string functions */ + delim: A C string of acceptable delimiter characters. + + Returns a pointer to a C string containing the chars of stringp up + to the first delimiter char (or the end of the string), or NULL. +*/ char *fz_strsep(char **stringp, const char *delim); -int fz_strlcpy(char *dst, const char *src, int n); -int fz_strlcat(char *dst, const char *src, int n); -/* Range checking atof */ -float fz_atof(const char *s); +/* + fz_strlcpy: Copy at most n-1 chars of a string into a destination + buffer with null termination, returning the real length of the + initial string (excluding terminator). -/* utf-8 encoding and decoding */ -int chartorune(int *rune, char *str); -int runetochar(char *str, int *rune); -int runelen(int c); + dst: Destination buffer, at least n bytes long. -/* getopt */ -extern int fz_getopt(int nargc, char * const *nargv, const char *ostr); -extern int fz_optind; -extern char *fz_optarg; + src: C string (non-NULL). + + n: Size of dst buffer in bytes. + + Returns the length (excluding terminator) of src. +*/ +int fz_strlcpy(char *dst, const char *src, int n); /* - * Generic hash-table with fixed-length keys. - */ + fz_strlcat: Concatenate 2 strings, with a maximum length. -typedef struct fz_hash_table_s fz_hash_table; + dst: pointer to first string in a buffer of n bytes. -fz_hash_table *fz_new_hash_table(fz_context *ctx, int initialsize, int keylen); -void fz_debug_hash(fz_context *ctx, fz_hash_table *table); -void fz_empty_hash(fz_context *ctx, fz_hash_table *table); -void fz_free_hash(fz_context *ctx, fz_hash_table *table); + src: pointer to string to concatenate. -void *fz_hash_find(fz_context *ctx, fz_hash_table *table, void *key); -void *fz_hash_insert(fz_context *ctx, fz_hash_table *table, void *key, void *val); -void fz_hash_remove(fz_context *ctx, fz_hash_table *table, void *key); + n: Size (in bytes) of buffer that dst is in. -int fz_hash_len(fz_context *ctx, fz_hash_table *table); -void *fz_hash_get_key(fz_context *ctx, fz_hash_table *table, int idx); -void *fz_hash_get_val(fz_context *ctx, fz_hash_table *table, int idx); + Returns the real length that a concatenated dst + src would have been + (not including terminator). +*/ +int fz_strlcat(char *dst, const char *src, int n); /* - * Math and geometry - */ + fz_chartorune: UTF8 decode a string of chars to a rune. -/* Multiply scaled two integers in the 0..255 range */ -static inline int fz_mul255(int a, int b) -{ - /* see Jim Blinn's book "Dirty Pixels" for how this works */ - int x = a * b + 128; - x += x >> 8; - return x >> 8; -} + rune: Pointer to an int to assign the decoded 'rune' to. -/* Expand a value A from the 0...255 range to the 0..256 range */ -#define FZ_EXPAND(A) ((A)+((A)>>7)) + str: Pointer to a UTF8 encoded string -/* Combine values A (in any range) and B (in the 0..256 range), - * to give a single value in the same range as A was. */ -#define FZ_COMBINE(A,B) (((A)*(B))>>8) + Returns the number of bytes consumed. Does not throw exceptions. +*/ +int fz_chartorune(int *rune, char *str); -/* Combine values A and C (in the same (any) range) and B and D (in the - * 0..256 range), to give a single value in the same range as A and C were. */ -#define FZ_COMBINE2(A,B,C,D) (FZ_COMBINE((A), (B)) + FZ_COMBINE((C), (D))) +/* + runetochar: UTF8 encode a run to a string of chars. -/* Blend SRC and DST (in the same range) together according to - * AMOUNT (in the 0...256 range). */ -#define FZ_BLEND(SRC, DST, AMOUNT) ((((SRC)-(DST))*(AMOUNT) + ((DST)<<8))>>8) + str: Pointer to a place to put the UTF8 encoded string. -typedef struct fz_matrix_s fz_matrix; -typedef struct fz_point_s fz_point; -typedef struct fz_rect_s fz_rect; -typedef struct fz_bbox_s fz_bbox; + rune: Pointer to a 'rune'. -extern const fz_rect fz_unit_rect; -extern const fz_rect fz_empty_rect; -extern const fz_rect fz_infinite_rect; + Returns the number of bytes the rune took to output. Does not throw + exceptions. +*/ +int fz_runetochar(char *str, int rune); -extern const fz_bbox fz_unit_bbox; -extern const fz_bbox fz_empty_bbox; -extern const fz_bbox fz_infinite_bbox; +/* + fz_runelen: Count many chars are required to represent a rune. -#define fz_is_empty_rect(r) ((r).x0 == (r).x1) -#define fz_is_infinite_rect(r) ((r).x0 > (r).x1) -#define fz_is_empty_bbox(b) ((b).x0 == (b).x1) -#define fz_is_infinite_bbox(b) ((b).x0 > (b).x1) + rune: The rune to encode. -struct fz_matrix_s -{ - float a, b, c, d, e, f; -}; + Returns the number of bytes required to represent this run in UTF8. +*/ +int fz_runelen(int rune); +/* getopt */ +extern int fz_getopt(int nargc, char * const *nargv, const char *ostr); +extern int fz_optind; +extern char *fz_optarg; + +/* + fz_point is a point in a two-dimensional space. +*/ +typedef struct fz_point_s fz_point; struct fz_point_s { float x, y; }; +/* + fz_rect is a rectangle represented by two diagonally opposite + corners at arbitrary coordinates. + + Rectangles are always axis-aligned with the X- and Y- axes. + The relationship between the coordinates are that x0 <= x1 and + y0 <= y1 in all cases except for infinte rectangles. The area + of a rectangle is defined as (x1 - x0) * (y1 - y0). If either + x0 > x1 or y0 > y1 is true for a given rectangle then it is + defined to be infinite. + + To check for empty or infinite rectangles use fz_is_empty_rect + and fz_is_infinite_rect. Compare to fz_bbox which has corners + at integer coordinates. + + x0, y0: The top left corner. + + x1, y1: The botton right corner. +*/ +typedef struct fz_rect_s fz_rect; struct fz_rect_s { float x0, y0; float x1, y1; }; +/* + fz_bbox is a bounding box similar to a fz_rect, except that + all corner coordinates are rounded to integer coordinates. + To check for empty or infinite bounding boxes use + fz_is_empty_bbox and fz_is_infinite_bbox. + + x0, y0: The top left corner. + + x1, y1: The bottom right corner. +*/ +typedef struct fz_bbox_s fz_bbox; struct fz_bbox_s { int x0, y0; int x1, y1; }; +/* + A rectangle with sides of length one. + + The bottom left corner is at (0, 0) and the top right corner + is at (1, 1). +*/ +extern const fz_rect fz_unit_rect; + +/* + A bounding box with sides of length one. See fz_unit_rect. +*/ +extern const fz_bbox fz_unit_bbox; + +/* + An empty rectangle with an area equal to zero. + + Both the top left and bottom right corner are at (0, 0). +*/ +extern const fz_rect fz_empty_rect; + +/* + An empty bounding box. See fz_empty_rect. +*/ +extern const fz_bbox fz_empty_bbox; + +/* + An infinite rectangle with negative area. + + The corner (x0, y0) is at (1, 1) while the corner (x1, y1) is + at (-1, -1). +*/ +extern const fz_rect fz_infinite_rect; + +/* + An infinite bounding box. See fz_infinite_rect. +*/ +extern const fz_bbox fz_infinite_bbox; + +/* + fz_is_empty_rect: Check if rectangle is empty. + + An empty rectangle is defined as one whose area is zero. +*/ +#define fz_is_empty_rect(r) ((r).x0 == (r).x1 || (r).y0 == (r).y1) + +/* + fz_is_empty_bbox: Check if bounding box is empty. + + Same definition of empty bounding boxes as for empty + rectangles. See fz_is_empty_rect. +*/ +#define fz_is_empty_bbox(b) ((b).x0 == (b).x1 || (b).y0 == (b).y1) + +/* + fz_is_infinite: Check if rectangle is infinite. + + An infinite rectangle is defined as one where either of the + two relationships between corner coordinates are not true. +*/ +#define fz_is_infinite_rect(r) ((r).x0 > (r).x1 || (r).y0 > (r).y1) + +/* + fz_is_infinite_bbox: Check if bounding box is infinite. + + Same definition of infinite bounding boxes as for infinite + rectangles. See fz_is_infinite_rect. +*/ +#define fz_is_infinite_bbox(b) ((b).x0 > (b).x1 || (b).y0 > (b).y1) + +/* + fz_matrix is a a row-major 3x3 matrix used for representing + transformations of coordinates throughout MuPDF. + + Since all points reside in a two-dimensional space, one vector + is always a constant unit vector; hence only some elements may + vary in a matrix. Below is how the elements map between + different representations. + + / a b 0 \ + | c d 0 | normally represented as [ a b c d e f ]. + \ e f 1 / +*/ +typedef struct fz_matrix_s fz_matrix; +struct fz_matrix_s +{ + float a, b, c, d, e, f; +}; + + +/* + fz_identity: Identity transform matrix. +*/ extern const fz_matrix fz_identity; -fz_matrix fz_concat(fz_matrix one, fz_matrix two); +/* + fz_concat: Multiply two matrices. + + The order of the two matrices are important since matrix + multiplication is not commutative. + + Does not throw exceptions. +*/ +fz_matrix fz_concat(fz_matrix left, fz_matrix right); + +/* + fz_scale: Create a scaling matrix. + + The returned matrix is of the form [ sx 0 0 sy 0 0 ]. + + sx, sy: Scaling factors along the X- and Y-axes. A scaling + factor of 1.0 will not cause any scaling along the relevant + axis. + + Does not throw exceptions. +*/ fz_matrix fz_scale(float sx, float sy); + +/* + fz_shear: Create a shearing matrix. + + The returned matrix is of the form [ 1 sy sx 1 0 0 ]. + + sx, sy: Shearing factors. A shearing factor of 0.0 will not + cause any shearing along the relevant axis. + + Does not throw exceptions. +*/ fz_matrix fz_shear(float sx, float sy); -fz_matrix fz_rotate(float theta); + +/* + fz_rotate: Create a rotation matrix. + + The returned matrix is of the form + [ cos(deg) sin(deg) -sin(deg) cos(deg) 0 0 ]. + + degrees: Degrees of counter clockwise rotation. Values less + than zero and greater than 360 are handled as expected. + + Does not throw exceptions. +*/ +fz_matrix fz_rotate(float degrees); + +/* + fz_translate: Create a translation matrix. + + The returned matrix is of the form [ 1 0 0 1 tx ty ]. + + tx, ty: Translation distances along the X- and Y-axes. A + translation of 0 will not cause any translation along the + relevant axis. + + Does not throw exceptions. +*/ fz_matrix fz_translate(float tx, float ty); -fz_matrix fz_invert_matrix(fz_matrix m); + +/* + fz_invert_matrix: Create an inverse matrix. + + matrix: Matrix to invert. A degenerate matrix, where the + determinant is equal to zero, can not be inverted and the + original matrix is returned instead. + + Does not throw exceptions. +*/ +fz_matrix fz_invert_matrix(fz_matrix matrix); + +/* + fz_is_rectilinear: Check if a transformation is rectilinear. + + Rectilinear means that no shearing is present and that any + rotations present are a multiple of 90 degrees. Usually this + is used to make sure that axis-aligned rectangles before the + transformation are still axis-aligned rectangles afterwards. + + Does not throw exceptions. +*/ int fz_is_rectilinear(fz_matrix m); -float fz_matrix_expansion(fz_matrix m); -float fz_matrix_max_expansion(fz_matrix m); -fz_bbox fz_round_rect(fz_rect r); -fz_bbox fz_intersect_bbox(fz_bbox a, fz_bbox b); -fz_rect fz_intersect_rect(fz_rect a, fz_rect b); -fz_bbox fz_union_bbox(fz_bbox a, fz_bbox b); -fz_rect fz_union_rect(fz_rect a, fz_rect b); +/* + fz_matrix_expansion: Calculate average scaling factor of matrix. +*/ +float fz_matrix_expansion(fz_matrix m); /* sumatrapdf */ + +/* + fz_round_rect: Convert a rect into a bounding box. -fz_point fz_transform_point(fz_matrix m, fz_point p); -fz_point fz_transform_vector(fz_matrix m, fz_point p); -fz_rect fz_transform_rect(fz_matrix m, fz_rect r); -fz_bbox fz_transform_bbox(fz_matrix m, fz_bbox b); + Coordinates in a bounding box are integers, so rounding of the + rects coordinates takes place. The top left corner is rounded + upwards and left while the bottom right corner is rounded + downwards and to the right. Overflows or underflowing + coordinates are clamped to INT_MIN/INT_MAX. -void fz_gridfit_matrix(fz_matrix *m); + Does not throw exceptions. +*/ +fz_bbox fz_round_rect(fz_rect rect); /* - * Basic crypto functions. - * Independent of the rest of fitz. - * For further encapsulation in filters, or not. - */ + fz_intersect_rect: Compute intersection of two rectangles. -/* md5 digests */ + Compute the largest axis-aligned rectangle that covers the + area covered by both given rectangles. If either rectangle is + empty then the intersection is also empty. If either rectangle + is infinite then the intersection is simply the non-infinite + rectangle. Should both rectangles be infinite, then the + intersection is also infinite. -typedef struct fz_md5_s fz_md5; + Does not throw exceptions. +*/ +fz_rect fz_intersect_rect(fz_rect a, fz_rect b); -struct fz_md5_s -{ - unsigned int state[4]; - unsigned int count[2]; - unsigned char buffer[64]; -}; +/* + fz_intersect_bbox: Compute intersection of two bounding boxes. -void fz_md5_init(fz_md5 *state); -void fz_md5_update(fz_md5 *state, const unsigned char *input, unsigned inlen); -void fz_md5_final(fz_md5 *state, unsigned char digest[16]); + Similar to fz_intersect_rect but operates on two bounding + boxes instead of two rectangles. -/* sha-256 digests */ + Does not throw exceptions. +*/ +fz_bbox fz_intersect_bbox(fz_bbox a, fz_bbox b); -typedef struct fz_sha256_s fz_sha256; +/* + fz_union_rect: Compute union of two rectangles. -struct fz_sha256_s -{ - unsigned int state[8]; - unsigned int count[2]; - union { - unsigned char u8[64]; - unsigned int u32[16]; - } buffer; -}; + Compute the smallest axis-aligned rectangle that encompasses + both given rectangles. If either rectangle is infinite then + the union is also infinite. If either rectangle is empty then + the union is simply the non-empty rectangle. Should both + rectangles be empty, then the union is also empty. -void fz_sha256_init(fz_sha256 *state); -void fz_sha256_update(fz_sha256 *state, const unsigned char *input, unsigned int inlen); -void fz_sha256_final(fz_sha256 *state, unsigned char digest[32]); + Does not throw exceptions. +*/ +fz_rect fz_union_rect(fz_rect a, fz_rect b); -/* arc4 crypto */ +/* + fz_union_bbox: Compute union of two bounding boxes. -typedef struct fz_arc4_s fz_arc4; + Similar to fz_union_rect but operates on two bounding boxes + instead of two rectangles. -struct fz_arc4_s -{ - unsigned x; - unsigned y; - unsigned char state[256]; -}; + Does not throw exceptions. +*/ +fz_bbox fz_union_bbox(fz_bbox a, fz_bbox b); -void fz_arc4_init(fz_arc4 *state, const unsigned char *key, unsigned len); -void fz_arc4_encrypt(fz_arc4 *state, unsigned char *dest, const unsigned char *src, unsigned len); +/* + fz_transform_point: Apply a transformation to a point. -/* AES block cipher implementation from XYSSL */ + transform: Transformation matrix to apply. See fz_concat, + fz_scale, fz_rotate and fz_translate for how to create a + matrix. -typedef struct fz_aes_s fz_aes; + Does not throw exceptions. +*/ +fz_point fz_transform_point(fz_matrix transform, fz_point point); -#define AES_DECRYPT 0 -#define AES_ENCRYPT 1 +/* + fz_transform_vector: Apply a transformation to a vector. -struct fz_aes_s -{ - int nr; /* number of rounds */ - unsigned long *rk; /* AES round keys */ - unsigned long buf[68]; /* unaligned data */ -}; + transform: Transformation matrix to apply. See fz_concat, + fz_scale and fz_rotate for how to create a matrix. Any + translation will be ignored. -void aes_setkey_enc( fz_aes *ctx, const unsigned char *key, int keysize ); -void aes_setkey_dec( fz_aes *ctx, const unsigned char *key, int keysize ); -void aes_crypt_cbc( fz_aes *ctx, int mode, int length, - unsigned char iv[16], - const unsigned char *input, - unsigned char *output ); + Does not throw exceptions. +*/ +fz_point fz_transform_vector(fz_matrix transform, fz_point vector); /* - * Dynamic objects. - * The same type of objects as found in PDF and PostScript. - * Used by the filters and the mupdf parser. - */ + fz_transform_rect: Apply a transform to a rectangle. -typedef struct fz_obj_s fz_obj; - -extern fz_obj *(*fz_resolve_indirect)(fz_obj *obj); - -fz_obj *fz_new_null(fz_context *ctx); -fz_obj *fz_new_bool(fz_context *ctx, int b); -fz_obj *fz_new_int(fz_context *ctx, int i); -fz_obj *fz_new_real(fz_context *ctx, float f); -fz_obj *fz_new_name(fz_context *ctx, char *str); -fz_obj *fz_new_string(fz_context *ctx, char *str, int len); -fz_obj *fz_new_indirect(fz_context *ctx, int num, int gen, void *doc); - -fz_obj *fz_new_array(fz_context *ctx, int initialcap); -fz_obj *fz_new_dict(fz_context *ctx, int initialcap); -fz_obj *fz_copy_array(fz_context *ctx, fz_obj *array); -fz_obj *fz_copy_dict(fz_context *ctx, fz_obj *dict); - -fz_obj *fz_keep_obj(fz_obj *obj); -void fz_drop_obj(fz_obj *obj); - -/* type queries */ -int fz_is_null(fz_obj *obj); -int fz_is_bool(fz_obj *obj); -int fz_is_int(fz_obj *obj); -int fz_is_real(fz_obj *obj); -int fz_is_name(fz_obj *obj); -int fz_is_string(fz_obj *obj); -int fz_is_array(fz_obj *obj); -int fz_is_dict(fz_obj *obj); -int fz_is_indirect(fz_obj *obj); - -int fz_objcmp(fz_obj *a, fz_obj *b); - -/* dict marking and unmarking functions - to avoid infinite recursions */ -int fz_dict_marked(fz_obj *obj); -int fz_dict_mark(fz_obj *obj); -void fz_dict_unmark(fz_obj *obj); - -/* safe, silent failure, no error reporting on type mismatches */ -int fz_to_bool(fz_obj *obj); -int fz_to_int(fz_obj *obj); -float fz_to_real(fz_obj *obj); -char *fz_to_name(fz_obj *obj); -char *fz_to_str_buf(fz_obj *obj); -fz_obj *fz_to_dict(fz_obj *obj); -int fz_to_str_len(fz_obj *obj); -int fz_to_num(fz_obj *obj); -int fz_to_gen(fz_obj *obj); - -int fz_array_len(fz_obj *array); -fz_obj *fz_array_get(fz_obj *array, int i); -void fz_array_put(fz_obj *array, int i, fz_obj *obj); -void fz_array_push(fz_obj *array, fz_obj *obj); -void fz_array_insert(fz_obj *array, fz_obj *obj); -int fz_array_contains(fz_obj *array, fz_obj *obj); - -int fz_dict_len(fz_obj *dict); -fz_obj *fz_dict_get_key(fz_obj *dict, int idx); -fz_obj *fz_dict_get_val(fz_obj *dict, int idx); -fz_obj *fz_dict_get(fz_obj *dict, fz_obj *key); -fz_obj *fz_dict_gets(fz_obj *dict, char *key); -fz_obj *fz_dict_getsa(fz_obj *dict, char *key, char *abbrev); -void fz_dict_put(fz_obj *dict, fz_obj *key, fz_obj *val); -void fz_dict_puts(fz_obj *dict, char *key, fz_obj *val); -void fz_dict_del(fz_obj *dict, fz_obj *key); -void fz_dict_dels(fz_obj *dict, char *key); -void fz_sort_dict(fz_obj *dict); - -int fz_fprint_obj(FILE *fp, fz_obj *obj, int tight); -void fz_debug_obj(fz_obj *obj); -void fz_debug_ref(fz_obj *obj); - -void fz_set_str_len(fz_obj *obj, int newlen); /* private */ -void *fz_get_indirect_document(fz_obj *obj); /* private */ - -/* - * Data buffers. - */ + After the four corner points of the axis-aligned rectangle + have been transformed it may not longer be axis-aligned. So a + new axis-aligned rectangle is created covering at least the + area of the transformed rectangle. + transform: Transformation matrix to apply. See fz_concat, + fz_scale and fz_rotate for how to create a matrix. + + rect: Rectangle to be transformed. The two special cases + fz_empty_rect and fz_infinite_rect, may be used but are + returned unchanged as expected. + + Does not throw exceptions. +*/ +fz_rect fz_transform_rect(fz_matrix transform, fz_rect rect); + +/* + fz_transform_bbox: Transform a given bounding box. + + Similar to fz_transform_rect, but operates on a bounding box + instead of a rectangle. + + Does not throw exceptions. +*/ +fz_bbox fz_transform_bbox(fz_matrix matrix, fz_bbox bbox); + +/* + fz_buffer is a wrapper around a dynamically allocated array of bytes. + + Buffers have a capacity (the number of bytes storage immediately + available) and a current size. +*/ typedef struct fz_buffer_s fz_buffer; -struct fz_buffer_s -{ - int refs; - unsigned char *data; - int cap, len; -}; +/* + fz_keep_buffer: Increment the reference count for a buffer. -fz_buffer *fz_new_buffer(fz_context *ctx, int size); + buf: The buffer to increment the reference count for. + + Returns a pointer to the buffer. Does not throw exceptions. +*/ fz_buffer *fz_keep_buffer(fz_context *ctx, fz_buffer *buf); + +/* + fz_drop_buffer: Decrement the reference count for a buffer. + + buf: The buffer to decrement the reference count for. +*/ void fz_drop_buffer(fz_context *ctx, fz_buffer *buf); -void fz_resize_buffer(fz_context *ctx, fz_buffer *buf, int size); -void fz_grow_buffer(fz_context *ctx, fz_buffer *buf); +/* + fz_buffer_storage: Retrieve information on the storage currently used + by a buffer. + + data: Pointer to place to retrieve data pointer. + + Returns length of stream. +*/ +int fz_buffer_storage(fz_context *ctx, fz_buffer *buf, unsigned char **data); /* - * Resource store - */ + fz_stream is a buffered reader capable of seeking in both + directions. -typedef struct fz_storable_s fz_storable; + Streams are reference counted, so references must be dropped + by a call to fz_close. -typedef struct fz_item_s fz_item; + Only the data between rp and wp is valid. +*/ +typedef struct fz_stream_s fz_stream; -typedef void (fz_store_free_fn)(fz_context *, fz_storable *); +/* + fz_open_file: Open the named file and wrap it in a stream. -struct fz_storable_s { - int refs; - fz_store_free_fn *free; -}; + filename: Path to a file as it would be given to open(2). +*/ +fz_stream *fz_open_file(fz_context *ctx, const char *filename); -#define FZ_INIT_STORABLE(S_,RC,FREE) \ - do { fz_storable *S = &(S_)->storable; S->refs = (RC); \ - S->free = (FREE); \ - } while (0) +/* + fz_open_file_w: Open the named file and wrap it in a stream. -enum { - FZ_STORE_UNLIMITED = 0, - FZ_STORE_DEFAULT = 256 << 20, -}; + This function is only available when compiling for Win32. -void fz_new_store_context(fz_context *ctx, unsigned int max); -void fz_drop_store_context(fz_context *ctx); -fz_store *fz_store_keep(fz_context *ctx); -void fz_debug_store(fz_context *ctx); + filename: Wide character path to the file as it would be given + to _wopen(). +*/ +fz_stream *fz_open_file_w(fz_context *ctx, const wchar_t *filename); -void *fz_keep_storable(fz_context *, fz_storable *); -void fz_drop_storable(fz_context *, fz_storable *); +/* + fz_open_fd: Wrap an open file descriptor in a stream. -void fz_store_item(fz_context *ctx, fz_obj *key, void *val, unsigned int itemsize); -void *fz_find_item(fz_context *ctx, fz_store_free_fn *freefn, fz_obj *key); -void fz_remove_item(fz_context *ctx, fz_store_free_fn *freefn, fz_obj *key); -void fz_empty_store(fz_context *ctx); -int fz_store_scavenge(fz_context *ctx, unsigned int size, int *phase); + file: An open file descriptor supporting bidirectional + seeking. The stream will take ownership of the file + descriptor, so it may not be modified or closed after the call + to fz_open_fd. When the stream is closed it will also close + the file descriptor. +*/ +fz_stream *fz_open_fd(fz_context *ctx, int file); /* - * Buffered reader. - * Only the data between rp and wp is valid data. - */ + fz_open_memory: Open a block of memory as a stream. -typedef struct fz_stream_s fz_stream; + data: Pointer to start of data block. Ownership of the data block is + NOT passed in. -struct fz_stream_s -{ - fz_context *ctx; - int refs; - int error; - int eof; - int pos; - int avail; - int bits; - int locked; - unsigned char *bp, *rp, *wp, *ep; - void *state; - int (*read)(fz_stream *stm, unsigned char *buf, int len); - void (*close)(fz_context *ctx, void *state); - void (*seek)(fz_stream *stm, int offset, int whence); - unsigned char buf[4096]; -}; + len: Number of bytes in data block. -fz_stream *fz_open_fd(fz_context *ctx, int file); -fz_stream *fz_open_file(fz_context *ctx, const char *filename); -fz_stream *fz_open_file_w(fz_context *ctx, const wchar_t *filename); /* only on win32 */ -fz_stream *fz_open_buffer(fz_context *ctx, fz_buffer *buf); + Returns pointer to newly created stream. May throw exceptions on + failure to allocate. +*/ fz_stream *fz_open_memory(fz_context *ctx, unsigned char *data, int len); -void fz_close(fz_stream *stm); -void fz_lock_stream(fz_stream *stm); -fz_stream *fz_new_stream(fz_context *ctx, void*, int(*)(fz_stream*, unsigned char*, int), void(*)(fz_context *, void *)); -fz_stream *fz_keep_stream(fz_stream *stm); -void fz_fill_buffer(fz_stream *stm); +/* + fz_open_buffer: Open a buffer as a stream. + + buf: The buffer to open. Ownership of the buffer is NOT passed in + (this function takes it's own reference). + Returns pointer to newly created stream. May throw exceptions on + failure to allocate. +*/ +fz_stream *fz_open_buffer(fz_context *ctx, fz_buffer *buf); + +/* + fz_close: Close an open stream. + + Drops a reference for the stream. Once no references remain + the stream will be closed, as will any file descriptor the + stream is using. + + Does not throw exceptions. +*/ +void fz_close(fz_stream *stm); + +/* + fz_tell: return the current reading position within a stream +*/ int fz_tell(fz_stream *stm); + +/* + fz_seek: Seek within a stream. + + stm: The stream to seek within. + + offset: The offset to seek to. + + whence: From where the offset is measured (see fseek). +*/ void fz_seek(fz_stream *stm, int offset, int whence); -int fz_read(fz_stream *stm, unsigned char *buf, int len); -void fz_read_line(fz_stream *stm, char *buf, int max); -fz_buffer *fz_read_all(fz_stream *stm, int initial); +/* + fz_read: Read from a stream into a given data block. -static inline int fz_read_byte(fz_stream *stm) -{ - if (stm->rp == stm->wp) - { - fz_fill_buffer(stm); - return stm->rp < stm->wp ? *stm->rp++ : EOF; - } - return *stm->rp++; -} + stm: The stream to read from. -static inline int fz_peek_byte(fz_stream *stm) -{ - if (stm->rp == stm->wp) - { - fz_fill_buffer(stm); - return stm->rp < stm->wp ? *stm->rp : EOF; - } - return *stm->rp; -} + data: The data block to read into. -static inline void fz_unread_byte(fz_stream *stm) -{ - if (stm->rp > stm->bp) - stm->rp--; -} + len: The length of the data block (in bytes). -static inline int fz_is_eof(fz_stream *stm) -{ - if (stm->rp == stm->wp) - { - if (stm->eof) - return 1; - return fz_peek_byte(stm) == EOF; - } - return 0; -} + Returns the number of bytes read. May throw exceptions. +*/ +int fz_read(fz_stream *stm, unsigned char *data, int len); -static inline unsigned int fz_read_bits(fz_stream *stm, int n) -{ - unsigned int x; +/* + fz_read_all: Read all of a stream into a buffer. - if (n <= stm->avail) - { - stm->avail -= n; - x = (stm->bits >> stm->avail) & ((1 << n) - 1); - } - else - { - x = stm->bits & ((1 << stm->avail) - 1); - n -= stm->avail; - stm->avail = 0; + stm: The stream to read from - while (n > 8) - { - x = (x << 8) | fz_read_byte(stm); - n -= 8; - } + initial: Suggested initial size for the buffer. - if (n > 0) - { - stm->bits = fz_read_byte(stm); - stm->avail = 8 - n; - x = (x << n) | (stm->bits >> stm->avail); - } - } + Returns a buffer created from reading from the stream. May throw + exceptions on failure to allocate. +*/ +fz_buffer *fz_read_all(fz_stream *stm, int initial); - return x; -} +/* + Bitmaps have 1 bit per component. Only used for creating halftoned + versions of contone buffers, and saving out. Samples are stored msb + first, akin to pbms. +*/ +typedef struct fz_bitmap_s fz_bitmap; -static inline void fz_sync_bits(fz_stream *stm) -{ - stm->avail = 0; -} +/* + fz_keep_bitmap: Take a reference to a bitmap. -static inline int fz_is_eof_bits(fz_stream *stm) -{ - return fz_is_eof(stm) && (stm->avail == 0 || stm->bits == EOF); -} + bit: The bitmap to increment the reference for. + + Returns bit. Does not throw exceptions. +*/ +fz_bitmap *fz_keep_bitmap(fz_context *ctx, fz_bitmap *bit); /* - * Data filters. - */ + fz_drop_bitmap: Drop a reference and free a bitmap. -fz_stream *fz_open_copy(fz_stream *chain); -fz_stream *fz_open_null(fz_stream *chain, int len); -fz_stream *fz_open_arc4(fz_stream *chain, unsigned char *key, unsigned keylen); -fz_stream *fz_open_aesd(fz_stream *chain, unsigned char *key, unsigned keylen); -fz_stream *fz_open_a85d(fz_stream *chain); -fz_stream *fz_open_ahxd(fz_stream *chain); -fz_stream *fz_open_rld(fz_stream *chain); -fz_stream *fz_open_dctd(fz_stream *chain, int color_transform); -fz_stream *fz_open_faxd(fz_stream *chain, - int k, int end_of_line, int encoded_byte_align, - int columns, int rows, int end_of_block, int black_is_1); -fz_stream *fz_open_flated(fz_stream *chain); -fz_stream *fz_open_lzwd(fz_stream *chain, int early_change); -fz_stream *fz_open_predict(fz_stream *chain, int predictor, int columns, int colors, int bpc); -fz_stream *fz_open_jbig2d(fz_stream *chain, fz_buffer *global); - -/* - * Resources and other graphics related objects. - */ + Decrement the reference count for the bitmap. When no + references remain the pixmap will be freed. -enum { FZ_MAX_COLORS = 32 }; + Does not throw exceptions. +*/ +void fz_drop_bitmap(fz_context *ctx, fz_bitmap *bit); -int fz_find_blendmode(char *name); -char *fz_blendmode_name(int blendmode); +/* + An fz_colorspace object represents an abstract colorspace. While + this should be treated as a black box by callers of the library at + this stage, know that it encapsulates knowledge of how to convert + colors to and from the colorspace, any lookup tables generated, the + number of components in the colorspace etc. +*/ +typedef struct fz_colorspace_s fz_colorspace; /* - * Pixmaps have n components per pixel. the last is always alpha. - * premultiplied alpha when rendering, but non-premultiplied for colorspace - * conversions and rescaling. - */ + fz_find_device_colorspace: Find a standard colorspace based upon + it's name. +*/ +fz_colorspace *fz_find_device_colorspace(fz_context *ctx, char *name); +/* + fz_device_gray: Abstract colorspace representing device specific + gray. +*/ +extern fz_colorspace *fz_device_gray; + +/* + fz_device_rgb: Abstract colorspace representing device specific + rgb. +*/ +extern fz_colorspace *fz_device_rgb; + +/* + fz_device_bgr: Abstract colorspace representing device specific + bgr. +*/ +extern fz_colorspace *fz_device_bgr; + +/* + fz_device_cmyk: Abstract colorspace representing device specific + CMYK. +*/ +extern fz_colorspace *fz_device_cmyk; + +/* + Pixmaps represent a set of pixels for a 2 dimensional region of a + plane. Each pixel has n components per pixel, the last of which is + always alpha. The data is in premultiplied alpha when rendering, but + non-premultiplied for colorspace conversions and rescaling. +*/ typedef struct fz_pixmap_s fz_pixmap; -typedef struct fz_colorspace_s fz_colorspace; -struct fz_pixmap_s -{ - fz_storable storable; - int x, y, w, h, n; - fz_pixmap *mask; /* explicit soft/image mask */ - int interpolate; - int xres, yres; - fz_colorspace *colorspace; - unsigned char *samples; - int free_samples; -}; +/* + fz_pixmap_bbox: Return a bounding box for a pixmap. -fz_bbox fz_bound_pixmap(fz_pixmap *pix); + Returns an exact bounding box for the supplied pixmap. +*/ +fz_bbox fz_pixmap_bbox(fz_context *ctx, fz_pixmap *pix); -fz_pixmap *fz_new_pixmap_with_data(fz_context *ctx, fz_colorspace *colorspace, int w, int h, unsigned char *samples); -fz_pixmap *fz_new_pixmap_with_rect(fz_context *ctx, fz_colorspace *, fz_bbox bbox); -fz_pixmap *fz_new_pixmap_with_rect_and_data(fz_context *ctx, fz_colorspace *, fz_bbox bbox, unsigned char *samples); -fz_pixmap *fz_new_pixmap(fz_context *ctx, fz_colorspace *, int w, int h); -fz_pixmap *fz_keep_pixmap(fz_context *ctx, fz_pixmap *pix); -void fz_drop_pixmap(fz_context *ctx, fz_pixmap *pix); -void fz_free_pixmap_imp(fz_context *ctx, fz_storable *pix); -void fz_clear_pixmap(fz_context *ctx, fz_pixmap *pix); -void fz_clear_pixmap_with_value(fz_context *ctx, fz_pixmap *pix, int value); -void fz_clear_pixmap_rect_with_value(fz_context *ctx, fz_pixmap *pix, int value, fz_bbox r); -void fz_copy_pixmap_rect(fz_context *ctx, fz_pixmap *dest, fz_pixmap *src, fz_bbox r); -void fz_premultiply_pixmap(fz_context *ctx, fz_pixmap *pix); -void fz_unmultiply_pixmap(fz_context *ctx, fz_pixmap *pix); -fz_pixmap *fz_alpha_from_gray(fz_context *ctx, fz_pixmap *gray, int luminosity); -void fz_invert_pixmap(fz_context *ctx, fz_pixmap *pix); -void fz_gamma_pixmap(fz_context *ctx, fz_pixmap *pix, float gamma); -unsigned int fz_pixmap_size(fz_context *ctx, fz_pixmap *pix); +/* + fz_pixmap_width: Return the width of the pixmap in pixels. +*/ +int fz_pixmap_width(fz_context *ctx, fz_pixmap *pix); -fz_pixmap *fz_scale_pixmap(fz_context *ctx, fz_pixmap *src, float x, float y, float w, float h, fz_bbox *clip); +/* + fz_pixmap_height: Return the height of the pixmap in pixels. +*/ +int fz_pixmap_height(fz_context *ctx, fz_pixmap *pix); -void fz_write_pnm(fz_context *ctx, fz_pixmap *pixmap, char *filename); -void fz_write_pam(fz_context *ctx, fz_pixmap *pixmap, char *filename, int savealpha); -void fz_write_png(fz_context *ctx, fz_pixmap *pixmap, char *filename, int savealpha); +/* + fz_new_pixmap: Create a new pixmap, with it's origin at (0,0) + + cs: The colorspace to use for the pixmap, or NULL for an alpha + plane/mask. -fz_pixmap *fz_load_jpx(fz_context *ctx, unsigned char *data, int size, fz_colorspace *cs); -fz_pixmap *fz_load_jpeg(fz_context *doc, unsigned char *data, int size); -fz_pixmap *fz_load_png(fz_context *doc, unsigned char *data, int size); -fz_pixmap *fz_load_tiff(fz_context *doc, unsigned char *data, int size); + w: The width of the pixmap (in pixels) + + h: The height of the pixmap (in pixels) + + Returns a pointer to the new pixmap. Throws exception on failure to + allocate. +*/ +fz_pixmap *fz_new_pixmap(fz_context *ctx, fz_colorspace *cs, int w, int h); /* - * Bitmaps have 1 component per bit. Only used for creating halftoned versions - * of contone buffers, and saving out. Samples are stored msb first, akin to - * pbms. - */ + fz_new_pixmap_with_bbox: Create a pixmap of a given size, + location and pixel format. -typedef struct fz_bitmap_s fz_bitmap; + The bounding box specifies the size of the created pixmap and + where it will be located. The colorspace determines the number + of components per pixel. Alpha is always present. Pixmaps are + reference counted, so drop references using fz_drop_pixmap. -struct fz_bitmap_s -{ - int refs; - int w, h, stride, n; - unsigned char *samples; -}; + colorspace: Colorspace format used for the created pixmap. The + pixmap will keep a reference to the colorspace. -fz_bitmap *fz_new_bitmap(fz_context *ctx, int w, int h, int n); -fz_bitmap *fz_keep_bitmap(fz_context *ctx, fz_bitmap *bit); -void fz_clear_bitmap(fz_context *ctx, fz_bitmap *bit); -void fz_drop_bitmap(fz_context *ctx, fz_bitmap *bit); + bbox: Bounding box specifying location/size of created pixmap. -void fz_write_pbm(fz_context *ctx, fz_bitmap *bitmap, char *filename); + Returns a pointer to the new pixmap. Throws exception on failure to + allocate. +*/ +fz_pixmap *fz_new_pixmap_with_bbox(fz_context *ctx, fz_colorspace *colorspace, fz_bbox bbox); /* - * A halftone is a set of threshold tiles, one per component. Each threshold - * tile is a pixmap, possibly of varying sizes and phases. - */ + fz_new_pixmap_with_data: Create a new pixmap, with it's origin at + (0,0) using the supplied data block. -typedef struct fz_halftone_s fz_halftone; + cs: The colorspace to use for the pixmap, or NULL for an alpha + plane/mask. -struct fz_halftone_s -{ - int refs; - int n; - fz_pixmap *comp[1]; -}; + w: The width of the pixmap (in pixels) -fz_halftone *fz_new_halftone(fz_context *ctx, int num_comps); -fz_halftone *fz_get_default_halftone(fz_context *ctx, int num_comps); -fz_halftone *fz_keep_halftone(fz_context *ctx, fz_halftone *half); -void fz_drop_halftone(fz_context *ctx, fz_halftone *half); + h: The height of the pixmap (in pixels) -fz_bitmap *fz_halftone_pixmap(fz_context *ctx, fz_pixmap *pix, fz_halftone *ht); + samples: The data block to keep the samples in. + + Returns a pointer to the new pixmap. Throws exception on failure to + allocate. +*/ +fz_pixmap *fz_new_pixmap_with_data(fz_context *ctx, fz_colorspace *colorspace, int w, int h, unsigned char *samples); /* - * Colorspace resources. - */ + fz_new_pixmap_with_bbox_and_data: Create a pixmap of a given size, + location and pixel format, using the supplied data block. -extern fz_colorspace *fz_device_gray; -extern fz_colorspace *fz_device_rgb; -extern fz_colorspace *fz_device_bgr; -extern fz_colorspace *fz_device_cmyk; + The bounding box specifies the size of the created pixmap and + where it will be located. The colorspace determines the number + of components per pixel. Alpha is always present. Pixmaps are + reference counted, so drop references using fz_drop_pixmap. -struct fz_colorspace_s -{ - fz_storable storable; - unsigned int size; - char name[16]; - int n; - void (*to_rgb)(fz_context *ctx, fz_colorspace *, float *src, float *rgb); - void (*from_rgb)(fz_context *ctx, fz_colorspace *, float *rgb, float *dst); - void (*free_data)(fz_context *Ctx, fz_colorspace *); - void *data; -}; + colorspace: Colorspace format used for the created pixmap. The + pixmap will keep a reference to the colorspace. -fz_colorspace *fz_new_colorspace(fz_context *ctx, char *name, int n); -fz_colorspace *fz_keep_colorspace(fz_context *ctx, fz_colorspace *colorspace); -void fz_drop_colorspace(fz_context *ctx, fz_colorspace *colorspace); -void fz_free_colorspace_imp(fz_context *ctx, fz_storable *colorspace); + bbox: Bounding box specifying location/size of created pixmap. -void fz_convert_color(fz_context *ctx, fz_colorspace *srcs, float *srcv, fz_colorspace *dsts, float *dstv); -void fz_convert_pixmap(fz_context *ctx, fz_pixmap *src, fz_pixmap *dst); + samples: The data block to keep the samples in. -fz_colorspace *fz_find_device_colorspace(char *name); + Returns a pointer to the new pixmap. Throws exception on failure to + allocate. +*/ +fz_pixmap *fz_new_pixmap_with_bbox_and_data(fz_context *ctx, fz_colorspace *colorspace, fz_bbox bbox, unsigned char *samples); /* - * Fonts come in two variants: - * Regular fonts are handled by FreeType. - * Type 3 fonts have callbacks to the interpreter. - */ + fz_keep_pixmap: Take a reference to a pixmap. -typedef struct fz_device_s fz_device; + pix: The pixmap to increment the reference for. -typedef struct fz_font_s fz_font; -char *ft_error_string(int err); + Returns pix. Does not throw exceptions. +*/ +fz_pixmap *fz_keep_pixmap(fz_context *ctx, fz_pixmap *pix); -struct fz_font_s -{ - int refs; - char name[32]; - - void *ft_face; /* has an FT_Face if used */ - int ft_substitute; /* ... substitute metrics */ - int ft_bold; /* ... synthesize bold */ - int ft_italic; /* ... synthesize italic */ - int ft_hint; /* ... force hinting for DynaLab fonts */ - - /* origin of font data */ - char *ft_file; - unsigned char *ft_data; - int ft_size; - - fz_matrix t3matrix; - fz_obj *t3resources; - fz_buffer **t3procs; /* has 256 entries if used */ - float *t3widths; /* has 256 entries if used */ - char *t3flags; /* has 256 entries if used */ - void *t3doc; /* a pdf_document for the callback */ - void (*t3run)(void *doc, fz_obj *resources, fz_buffer *contents, fz_device *dev, fz_matrix ctm, void *gstate); - - fz_rect bbox; /* font bbox is used only for t3 fonts */ - - /* per glyph bounding box cache */ - int use_glyph_bbox; - int bbox_count; - fz_rect *bbox_table; - - /* substitute metrics */ - int width_count; - int *width_table; /* in 1000 units */ -}; +/* + fz_drop_pixmap: Drop a reference and free a pixmap. -void fz_new_font_context(fz_context *ctx); -fz_font_context *fz_keep_font_context(fz_context *ctx); -void fz_drop_font_context(fz_context *ctx); + Decrement the reference count for the pixmap. When no + references remain the pixmap will be freed. -fz_font *fz_new_type3_font(fz_context *ctx, char *name, fz_matrix matrix); + Does not throw exceptions. +*/ +void fz_drop_pixmap(fz_context *ctx, fz_pixmap *pix); -fz_font *fz_new_font_from_memory(fz_context *ctx, unsigned char *data, int len, int index, int use_glyph_bbox); -fz_font *fz_new_font_from_file(fz_context *ctx, char *path, int index, int use_glyph_bbox); +/* + fz_pixmap_colorspace: Return the colorspace of a pixmap -fz_font *fz_keep_font(fz_context *ctx, fz_font *font); -void fz_drop_font(fz_context *ctx, fz_font *font); + Returns colorspace. Does not throw exceptions. +*/ +fz_colorspace *fz_pixmap_colorspace(fz_context *ctx, fz_pixmap *pix); -void fz_debug_font(fz_context *ctx, fz_font *font); +/* + fz_pixmap_components: Return the number of components in a pixmap. -void fz_set_font_bbox(fz_context *ctx, fz_font *font, float xmin, float ymin, float xmax, float ymax); -fz_rect fz_bound_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix trm); -int fz_glyph_cacheable(fz_context *ctx, fz_font *font, int gid); + Returns the number of components. Does not throw exceptions. +*/ +int fz_pixmap_components(fz_context *ctx, fz_pixmap *pix); /* - * Vector path buffer. - * It can be stroked and dashed, or be filled. - * It has a fill rule (nonzero or even_odd). - * - * When rendering, they are flattened, stroked and dashed straight - * into the Global Edge List. - */ + fz_pixmap_samples: Returns a pointer to the pixel data of a pixmap. -typedef struct fz_path_s fz_path; -typedef struct fz_stroke_state_s fz_stroke_state; + Returns the pointer. Does not throw exceptions. +*/ +unsigned char *fz_pixmap_samples(fz_context *ctx, fz_pixmap *pix); -typedef union fz_path_item_s fz_path_item; +/* + fz_clear_pixmap_with_value: Clears a pixmap with the given value. -typedef enum fz_path_item_kind_e -{ - FZ_MOVETO, - FZ_LINETO, - FZ_CURVETO, - FZ_CLOSE_PATH -} fz_path_item_kind; + pix: The pixmap to clear. -typedef enum fz_linecap_e -{ - FZ_LINECAP_BUTT = 0, - FZ_LINECAP_ROUND = 1, - FZ_LINECAP_SQUARE = 2, - FZ_LINECAP_TRIANGLE = 3 -} fz_linecap; + value: Values in the range 0 to 255 are valid. Each component + sample for each pixel in the pixmap will be set to this value, + while alpha will always be set to 255 (non-transparent). -typedef enum fz_linejoin_e -{ - FZ_LINEJOIN_MITER = 0, - FZ_LINEJOIN_ROUND = 1, - FZ_LINEJOIN_BEVEL = 2, - FZ_LINEJOIN_MITER_XPS = 3 -} fz_linejoin; + Does not throw exceptions. +*/ +void fz_clear_pixmap_with_value(fz_context *ctx, fz_pixmap *pix, int value); -union fz_path_item_s -{ - fz_path_item_kind k; - float v; -}; +/* + fz_clear_pixmap_with_value: Sets all components (including alpha) of + all pixels in a pixmap to 0. -struct fz_path_s -{ - int len, cap; - fz_path_item *items; - int last; -}; + pix: The pixmap to clear. -struct fz_stroke_state_s -{ - fz_linecap start_cap, dash_cap, end_cap; - fz_linejoin linejoin; - float linewidth; - float miterlimit; - float dash_phase; - int dash_len; - float dash_list[32]; -}; + Does not throw exceptions. +*/ +void fz_clear_pixmap(fz_context *ctx, fz_pixmap *pix); -fz_path *fz_new_path(fz_context *ctx); -void fz_moveto(fz_context*, fz_path*, float x, float y); -void fz_lineto(fz_context*, fz_path*, float x, float y); -void fz_curveto(fz_context*,fz_path*, float, float, float, float, float, float); -void fz_curvetov(fz_context*,fz_path*, float, float, float, float); -void fz_curvetoy(fz_context*,fz_path*, float, float, float, float); -void fz_closepath(fz_context*,fz_path*); -void fz_free_path(fz_context *ctx, fz_path *path); +/* + fz_invert_pixmap: Invert all the pixels in a pixmap. All components + of all pixels are inverted (except alpha, which is unchanged). -void fz_transform_path(fz_context *ctx, fz_path *path, fz_matrix transform); + Does not throw exceptions. +*/ +void fz_invert_pixmap(fz_context *ctx, fz_pixmap *pix); -fz_path *fz_clone_path(fz_context *ctx, fz_path *old); +/* + fz_invert_pixmap: Invert all the pixels in a given rectangle of a + pixmap. All components of all pixels in the rectangle are inverted + (except alpha, which is unchanged). -fz_rect fz_bound_path(fz_context *ctx, fz_path *path, fz_stroke_state *stroke, fz_matrix ctm); -void fz_debug_path(fz_context *ctx, fz_path *, int indent); + Does not throw exceptions. +*/ +void fz_invert_pixmap_rect(fz_pixmap *image, fz_bbox rect); /* - * Glyph cache - */ + fz_gamma_pixmap: Apply gamma correction to a pixmap. All components + of all pixels are modified (except alpha, which is unchanged). -void fz_new_glyph_cache_context(fz_context *ctx); -fz_glyph_cache *fz_keep_glyph_cache(fz_context *ctx); -void fz_drop_glyph_cache_context(fz_context *ctx); -void fz_purge_glyph_cache(fz_context *ctx); - -fz_pixmap *fz_render_ft_glyph(fz_context *ctx, fz_font *font, int cid, fz_matrix trm); -fz_pixmap *fz_render_t3_glyph(fz_context *ctx, fz_font *font, int cid, fz_matrix trm, fz_colorspace *model); -fz_pixmap *fz_render_ft_stroked_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix trm, fz_matrix ctm, fz_stroke_state *state); -fz_pixmap *fz_render_glyph(fz_context *ctx, fz_font*, int, fz_matrix, fz_colorspace *model); -fz_pixmap *fz_render_stroked_glyph(fz_context *ctx, fz_font*, int, fz_matrix, fz_matrix, fz_stroke_state *stroke); -void fz_render_t3_glyph_direct(fz_context *ctx, fz_device *dev, fz_font *font, int gid, fz_matrix trm, void *gstate); - -/* - * Text buffer. - * - * The trm field contains the a, b, c and d coefficients. - * The e and f coefficients come from the individual elements, - * together they form the transform matrix for the glyph. - * - * Glyphs are referenced by glyph ID. - * The Unicode text equivalent is kept in a separate array - * with indexes into the glyph array. - */ + gamma: The gamma value to apply; 1.0 for no change. -typedef struct fz_text_s fz_text; -typedef struct fz_text_item_s fz_text_item; + Does not throw exceptions. +*/ +void fz_gamma_pixmap(fz_context *ctx, fz_pixmap *pix, float gamma); -struct fz_text_item_s -{ - float x, y; - int gid; /* -1 for one gid to many ucs mappings */ - int ucs; /* -1 for one ucs to many gid mappings */ -}; +/* + fz_unmultiply_pixmap: Convert a pixmap from premultiplied to + non-premultiplied format. -struct fz_text_s -{ - fz_font *font; - fz_matrix trm; - int wmode; - int len, cap; - fz_text_item *items; -}; + Does not throw exceptions. +*/ +void fz_unmultiply_pixmap(fz_context *ctx, fz_pixmap *pix); + +/* + fz_convert_pixmap: Convert from one pixmap to another (assumed to be + the same size, but possibly with a different colorspace). + + dst: the source pixmap. -fz_text *fz_new_text(fz_context *ctx, fz_font *face, fz_matrix trm, int wmode); -void fz_add_text(fz_context *ctx, fz_text *text, int gid, int ucs, float x, float y); -void fz_free_text(fz_context *ctx, fz_text *text); -fz_rect fz_bound_text(fz_context *ctx, fz_text *text, fz_matrix ctm); -fz_text *fz_clone_text(fz_context *ctx, fz_text *old); -void fz_debug_text(fz_context *ctx, fz_text*, int indent); + src: the destination pixmap. +*/ +void fz_convert_pixmap(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src); /* - * The shading code uses gouraud shaded triangle meshes. - */ + fz_write_pixmap: Save a pixmap out. -enum -{ - FZ_LINEAR, - FZ_RADIAL, - FZ_MESH, -}; + name: The prefix for the name of the pixmap. The pixmap will be saved + as "name.png" if the pixmap is RGB or Greyscale, "name.pam" otherwise. -typedef struct fz_shade_s fz_shade; + rgb: If non zero, the pixmap is converted to rgb (if possible) before + saving. +*/ +void fz_write_pixmap(fz_context *ctx, fz_pixmap *img, char *name, int rgb); -struct fz_shade_s -{ - fz_storable storable; +/* + fz_write_pnm: Save a pixmap as a pnm - fz_rect bbox; /* can be fz_infinite_rect */ - fz_colorspace *colorspace; + filename: The filename to save as (including extension). +*/ +void fz_write_pnm(fz_context *ctx, fz_pixmap *pixmap, char *filename); - fz_matrix matrix; /* matrix from pattern dict */ - int use_background; /* background color for fills but not 'sh' */ - float background[FZ_MAX_COLORS]; +/* + fz_write_pam: Save a pixmap as a pam - int use_function; - float function[256][FZ_MAX_COLORS + 1]; + filename: The filename to save as (including extension). +*/ +void fz_write_pam(fz_context *ctx, fz_pixmap *pixmap, char *filename, int savealpha); - int type; /* linear, radial, mesh */ - int extend[2]; +/* + fz_write_png: Save a pixmap as a png - int mesh_len; - int mesh_cap; - float *mesh; /* [x y 0], [x y r], [x y t] or [x y c1 ... cn] */ -}; + filename: The filename to save as (including extension). +*/ +void fz_write_png(fz_context *ctx, fz_pixmap *pixmap, char *filename, int savealpha); + +/* + fz_write_pbm: Save a bitmap as a pbm + + filename: The filename to save as (including extension). +*/ +void fz_write_pbm(fz_context *ctx, fz_bitmap *bitmap, char *filename); -fz_shade *fz_keep_shade(fz_context *ctx, fz_shade *shade); -void fz_drop_shade(fz_context *ctx, fz_shade *shade); -void fz_free_shade_imp(fz_context *ctx, fz_storable *shade); -void fz_debug_shade(fz_context *ctx, fz_shade *shade); +/* + fz_md5_pixmap: Return the md5 digest for a pixmap -fz_rect fz_bound_shade(fz_context *ctx, fz_shade *shade, fz_matrix ctm); -void fz_paint_shade(fz_context *ctx, fz_shade *shade, fz_matrix ctm, fz_pixmap *dest, fz_bbox bbox); + filename: The filename to save as (including extension). +*/ +void fz_md5_pixmap(fz_pixmap *pixmap, unsigned char digest[16]); /* - * Scan converter + Images are storable objects from which we can obtain fz_pixmaps. + These may be implemented as simple wrappers around a pixmap, or as + more complex things that decode at different subsample settings on + demand. */ +typedef struct fz_image_s fz_image; -int fz_get_aa_level(fz_context *ctx); -void fz_set_aa_level(fz_context *ctx, int bits); +/* + fz_image_to_pixmap: Called to get a handle to a pixmap from an image. -typedef struct fz_gel_s fz_gel; + image: The image to retrieve a pixmap from. -fz_gel *fz_new_gel(fz_context *ctx); -void fz_insert_gel(fz_gel *gel, float x0, float y0, float x1, float y1); -void fz_reset_gel(fz_gel *gel, fz_bbox clip); -void fz_sort_gel(fz_gel *gel); -fz_bbox fz_bound_gel(fz_gel *gel); -void fz_free_gel(fz_gel *gel); -int fz_is_rect_gel(fz_gel *gel); + w: The desired width (in pixels). This may be completely ignored, but + may serve as an indication of a suitable subsample factor to use for + image types that support this. -void fz_scan_convert(fz_gel *gel, int eofill, fz_bbox clip, fz_pixmap *pix, unsigned char *colorbv); + h: The desired height (in pixels). This may be completely ignored, but + may serve as an indication of a suitable subsample factor to use for + image types that support this. -void fz_flatten_fill_path(fz_gel *gel, fz_path *path, fz_matrix ctm, float flatness); -void fz_flatten_stroke_path(fz_gel *gel, fz_path *path, fz_stroke_state *stroke, fz_matrix ctm, float flatness, float linewidth); -void fz_flatten_dash_path(fz_gel *gel, fz_path *path, fz_stroke_state *stroke, fz_matrix ctm, float flatness, float linewidth); + Returns a non NULL pixmap pointer. May throw exceptions. +*/ +fz_pixmap *fz_image_to_pixmap(fz_context *ctx, fz_image *image, int w, int h); /* - * The device interface. - */ + fz_drop_image: Drop a reference to an image. -enum -{ - /* Hints */ - FZ_IGNORE_IMAGE = 1, - FZ_IGNORE_SHADE = 2, - - /* Flags */ - FZ_DEVFLAG_MASK = 1, - FZ_DEVFLAG_COLOR = 2, - FZ_DEVFLAG_UNCACHEABLE = 4, - FZ_DEVFLAG_FILLCOLOR_UNDEFINED = 8, - FZ_DEVFLAG_STROKECOLOR_UNDEFINED = 16, - FZ_DEVFLAG_STARTCAP_UNDEFINED = 32, - FZ_DEVFLAG_DASHCAP_UNDEFINED = 64, - FZ_DEVFLAG_ENDCAP_UNDEFINED = 128, - FZ_DEVFLAG_LINEJOIN_UNDEFINED = 256, - FZ_DEVFLAG_MITERLIMIT_UNDEFINED = 512, - FZ_DEVFLAG_LINEWIDTH_UNDEFINED = 1024, - /* Arguably we should have a bit for the dash pattern itself being - * undefined, but that causes problems; do we assume that it should - * always be set to non-dashing at the start of every glyph? */ -}; + image: The image to drop a reference to. +*/ +void fz_drop_image(fz_context *ctx, fz_image *image); -struct fz_device_s -{ - int hints; - int flags; +/* + fz_keep_image: Increment the reference count of an image. - void *user; - void (*free_user)(fz_device *); - fz_context *ctx; - - void (*fill_path)(fz_device *, fz_path *, int even_odd, fz_matrix, fz_colorspace *, float *color, float alpha); - void (*stroke_path)(fz_device *, fz_path *, fz_stroke_state *, fz_matrix, fz_colorspace *, float *color, float alpha); - void (*clip_path)(fz_device *, fz_path *, fz_rect *rect, int even_odd, fz_matrix); - void (*clip_stroke_path)(fz_device *, fz_path *, fz_rect *rect, fz_stroke_state *, fz_matrix); - - void (*fill_text)(fz_device *, fz_text *, fz_matrix, fz_colorspace *, float *color, float alpha); - void (*stroke_text)(fz_device *, fz_text *, fz_stroke_state *, fz_matrix, fz_colorspace *, float *color, float alpha); - void (*clip_text)(fz_device *, fz_text *, fz_matrix, int accumulate); - void (*clip_stroke_text)(fz_device *, fz_text *, fz_stroke_state *, fz_matrix); - void (*ignore_text)(fz_device *, fz_text *, fz_matrix); - - void (*fill_shade)(fz_device *, fz_shade *shd, fz_matrix ctm, float alpha); - void (*fill_image)(fz_device *, fz_pixmap *img, fz_matrix ctm, float alpha); - void (*fill_image_mask)(fz_device *, fz_pixmap *img, fz_matrix ctm, fz_colorspace *, float *color, float alpha); - void (*clip_image_mask)(fz_device *, fz_pixmap *img, fz_rect *rect, fz_matrix ctm); - - void (*pop_clip)(fz_device *); - - void (*begin_mask)(fz_device *, fz_rect, int luminosity, fz_colorspace *, float *bc); - void (*end_mask)(fz_device *); - void (*begin_group)(fz_device *, fz_rect, int isolated, int knockout, int blendmode, float alpha); - void (*end_group)(fz_device *); - - void (*begin_tile)(fz_device *, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm); - void (*end_tile)(fz_device *); -}; + image: The image to take a reference to. -void fz_fill_path(fz_device *dev, fz_path *path, int even_odd, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha); -void fz_stroke_path(fz_device *dev, fz_path *path, fz_stroke_state *stroke, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha); -void fz_clip_path(fz_device *dev, fz_path *path, fz_rect *rect, int even_odd, fz_matrix ctm); -void fz_clip_stroke_path(fz_device *dev, fz_path *path, fz_rect *rect, fz_stroke_state *stroke, fz_matrix ctm); -void fz_fill_text(fz_device *dev, fz_text *text, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha); -void fz_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha); -void fz_clip_text(fz_device *dev, fz_text *text, fz_matrix ctm, int accumulate); -void fz_clip_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, fz_matrix ctm); -void fz_ignore_text(fz_device *dev, fz_text *text, fz_matrix ctm); -void fz_pop_clip(fz_device *dev); -void fz_fill_shade(fz_device *dev, fz_shade *shade, fz_matrix ctm, float alpha); -void fz_fill_image(fz_device *dev, fz_pixmap *image, fz_matrix ctm, float alpha); -void fz_fill_image_mask(fz_device *dev, fz_pixmap *image, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha); -void fz_clip_image_mask(fz_device *dev, fz_pixmap *image, fz_rect *rect, fz_matrix ctm); -void fz_begin_mask(fz_device *dev, fz_rect area, int luminosity, fz_colorspace *colorspace, float *bc); -void fz_end_mask(fz_device *dev); -void fz_begin_group(fz_device *dev, fz_rect area, int isolated, int knockout, int blendmode, float alpha); -void fz_end_group(fz_device *dev); -void fz_begin_tile(fz_device *dev, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm); -void fz_end_tile(fz_device *dev); - -fz_device *fz_new_device(fz_context *ctx, void *user); + Returns a pointer to the image. +*/ +fz_image *fz_keep_image(fz_context *ctx, fz_image *image); + +/* + A halftone is a set of threshold tiles, one per component. Each + threshold tile is a pixmap, possibly of varying sizes and phases. + Currently, we only provide one 'default' halftone tile for operating + on 1 component plus alpha pixmaps (where the alpha is ignored). This + is signified by an fz_halftone pointer to NULL. +*/ +typedef struct fz_halftone_s fz_halftone; + +/* + fz_halftone_pixmap: Make a bitmap from a pixmap and a halftone. + + pix: The pixmap to generate from. Currently must be a single color + component + alpha (where the alpha is assumed to be solid). + + ht: The halftone to use. NULL implies the default halftone. + + Returns the resultant bitmap. Throws exceptions in the case of + failure to allocate. +*/ +fz_bitmap *fz_halftone_pixmap(fz_context *ctx, fz_pixmap *pix, fz_halftone *ht); + +/* + An abstract font handle. Currently there are no public API functions + for handling these. +*/ +typedef struct fz_font_s fz_font; + +/* + The different format handlers (pdf, xps etc) interpret pages to a + device. These devices can then process the stream of calls they + recieve in various ways: + The trace device outputs debugging information for the calls. + The draw device will render them. + The list device stores them in a list to play back later. + The text device performs text extraction and searching. + The bbox device calculates the bounding box for the page. + Other devices can (and will) be written in future. +*/ +typedef struct fz_device_s fz_device; + +/* + fz_free_device: Free a devices of any type and its resources. +*/ void fz_free_device(fz_device *dev); +/* + fz_new_trace_device: Create a device to print a debug trace of + all device calls. +*/ fz_device *fz_new_trace_device(fz_context *ctx); + +/* + fz_new_bbox_device: Create a device to compute the bounding + box of all marks on a page. + + The returned bounding box will be the union of all bounding + boxes of all objects on a page. +*/ fz_device *fz_new_bbox_device(fz_context *ctx, fz_bbox *bboxp); + +/* + fz_new_draw_device: Create a device to draw on a pixmap. + + dest: Target pixmap for the draw device. See fz_new_pixmap* + for how to obtain a pixmap. The pixmap is not cleared by the + draw device, see fz_clear_pixmap* for how to clear it prior to + calling fz_new_draw_device. Free the device by calling + fz_free_device. +*/ fz_device *fz_new_draw_device(fz_context *ctx, fz_pixmap *dest); -fz_device *fz_new_draw_device_type3(fz_context *ctx, fz_pixmap *dest); /* - * Text extraction device + Text extraction device: Used for searching, format conversion etc. */ -typedef struct fz_text_span_s fz_text_span; +typedef struct fz_text_style_s fz_text_style; typedef struct fz_text_char_s fz_text_char; +typedef struct fz_text_span_s fz_text_span; +typedef struct fz_text_line_s fz_text_line; +typedef struct fz_text_block_s fz_text_block; + +typedef struct fz_text_sheet_s fz_text_sheet; +typedef struct fz_text_page_s fz_text_page; + +struct fz_text_style_s +{ + int id; + fz_font *font; + float size; + int wmode; + int script; + /* etc... */ + fz_text_style *next; +}; + +struct fz_text_sheet_s +{ + int maxid; + fz_text_style *style; +}; struct fz_text_char_s { + fz_rect bbox; int c; - fz_bbox bbox; }; struct fz_text_span_s { - fz_font *font; - float size; - int wmode; + fz_rect bbox; int len, cap; fz_text_char *text; - fz_text_span *next; - int eol; + fz_text_style *style; }; -fz_text_span *fz_new_text_span(fz_context *ctx); -void fz_free_text_span(fz_context *ctx, fz_text_span *line); -void fz_debug_text_span(fz_text_span *line); -void fz_debug_text_span_xml(fz_text_span *span); +struct fz_text_line_s +{ + fz_rect bbox; + int len, cap; + fz_text_span *spans; +}; -fz_device *fz_new_text_device(fz_context *ctx, fz_text_span *text); +struct fz_text_block_s +{ + fz_rect bbox; + int len, cap; + fz_text_line *lines; +}; + +struct fz_text_page_s +{ + fz_rect mediabox; + int len, cap; + fz_text_block *blocks; +}; + +/* + fz_new_text_device: Create a device to extract the text on a page. + + Gather and sort the text on a page into spans of uniform style, + arranged into lines and blocks by reading order. The reading order + is determined by various heuristics, so may not be accurate. +*/ +fz_device *fz_new_text_device(fz_context *ctx, fz_text_sheet *sheet, fz_text_page *page); + +/* + fz_new_text_sheet: Create an empty style sheet. + + The style sheet is filled out by the text device, creating + one style for each unique font, color, size combination that + is used. +*/ +fz_text_sheet *fz_new_text_sheet(fz_context *ctx); +void fz_free_text_sheet(fz_context *ctx, fz_text_sheet *sheet); + +/* + fz_new_text_page: Create an empty text page. + + The text page is filled out by the text device to contain the blocks, + lines and spans of text on the page. +*/ +fz_text_page *fz_new_text_page(fz_context *ctx, fz_rect mediabox); +void fz_free_text_page(fz_context *ctx, fz_text_page *page); + +void fz_print_text_sheet(fz_context *ctx, FILE *out, fz_text_sheet *sheet); +void fz_print_text_page_html(fz_context *ctx, FILE *out, fz_text_page *page); +void fz_print_text_page_xml(fz_context *ctx, FILE *out, fz_text_page *page); +void fz_print_text_page(fz_context *ctx, FILE *out, fz_text_page *page); /* * Cookie support - simple communication channel between app/library. @@ -1494,6 +1583,40 @@ fz_device *fz_new_text_device(fz_context *ctx, fz_text_span *text); typedef struct fz_cookie_s fz_cookie; +/* + Provide two-way communication between application and library. + Intended for multi-threaded applications where one thread is + rendering pages and another thread wants read progress + feedback or abort a job that takes a long time to finish. The + communication is unsynchronized without locking. + + abort: The appliation should set this field to 0 before + calling fz_run_page to render a page. At any point when the + page is being rendered the application my set this field to 1 + which will cause the rendering to finish soon. This field is + checked periodically when the page is rendered, but exactly + when is not known, therefore there is no upper bound on + exactly when the the rendering will abort. If the application + did not provide a set of locks to fz_new_context, it must also + await the completion of fz_run_page before issuing another + call to fz_run_page. Note that once the application has set + this field to 1 after it called fz_run_page it may not change + the value again. + + progress: Communicates rendering progress back to the + application and is read only. Increments as a page is being + rendered. The value starts out at 0 and is limited to less + than or equal to progress_max, unless progress_max is -1. + + progress_max: Communicates the known upper bound of rendering + back to the application and is read only. The maximum value + that the progress field may take. If there is no known upper + bound on how long the rendering may take this value is -1 and + progress is not limited. Note that the value of progress_max + may change from -1 to a positive value once an upper bound is + known, so take this into consideration when comparing the + value of progress to that of progress_max. +*/ struct fz_cookie_s { int abort; @@ -1502,67 +1625,81 @@ struct fz_cookie_s }; /* - * Display list device -- record and play back device commands. - */ + Display list device -- record and play back device commands. +*/ +/* + fz_display_list is a list containing drawing commands (text, + images, etc.). The intent is two-fold: as a caching-mechanism + to reduce parsing of a page, and to be used as a data + structure in multi-threading where one thread parses the page + and another renders pages. + + Create a displaylist with fz_new_display_list, hand it over to + fz_new_list_device to have it populated, and later replay the + list (once or many times) by calling fz_run_display_list. When + the list is no longer needed free it with fz_free_display_list. +*/ typedef struct fz_display_list_s fz_display_list; +/* + fz_new_display_list: Create an empty display list. + + A display list contains drawing commands (text, images, etc.). + Use fz_new_list_device for populating the list. +*/ fz_display_list *fz_new_display_list(fz_context *ctx); -void fz_free_display_list(fz_context *ctx, fz_display_list *list); + +/* + fz_new_list_device: Create a rendering device for a display list. + + When the device is rendering a page it will populate the + display list with drawing commsnds (text, images, etc.). The + display list can later be reused to render a page many times + without having to re-interpret the page from the document file + for each rendering. Once the device is no longer needed, free + it with fz_free_device. + + list: A display list that the list device takes ownership of. +*/ fz_device *fz_new_list_device(fz_context *ctx, fz_display_list *list); -void fz_run_display_list(fz_display_list *list, fz_device *dev, fz_matrix ctm, fz_bbox area, fz_cookie *cookie); /* - * Plotting functions. - */ + fz_run_display_list: (Re)-run a display list through a device. -void fz_decode_tile(fz_pixmap *pix, float *decode); -void fz_decode_indexed_tile(fz_pixmap *pix, float *decode, int maxval); -void fz_unpack_tile(fz_pixmap *dst, unsigned char * restrict src, int n, int depth, int stride, int scale); + list: A display list, created by fz_new_display_list and + populated with objects from a page by running fz_run_page on a + device obtained from fz_new_list_device. -void fz_paint_solid_alpha(unsigned char * restrict dp, int w, int alpha); -void fz_paint_solid_color(unsigned char * restrict dp, int n, int w, unsigned char *color); + dev: Device obtained from fz_new_*_device. -void fz_paint_span(unsigned char * restrict dp, unsigned char * restrict sp, int n, int w, int alpha); -void fz_paint_span_with_color(unsigned char * restrict dp, unsigned char * restrict mp, int n, int w, unsigned char *color); + ctm: Transform to apply to display list contents. May include + for example scaling and rotation, see fz_scale, fz_rotate and + fz_concat. Set to fz_identity if no transformation is desired. -void fz_paint_image(fz_pixmap *dst, fz_bbox scissor, fz_pixmap *shape, fz_pixmap *img, fz_matrix ctm, int alpha); -void fz_paint_image_with_color(fz_pixmap *dst, fz_bbox scissor, fz_pixmap *shape, fz_pixmap *img, fz_matrix ctm, unsigned char *colorbv); + area: Only the part of the contents of the display list + visible within this area will be considered when the list is + run through the device. This does not imply for tile objects + contained in the display list. -void fz_paint_pixmap(fz_pixmap *dst, fz_pixmap *src, int alpha); -void fz_paint_pixmap_with_mask(fz_pixmap *dst, fz_pixmap *src, fz_pixmap *msk); -void fz_paint_pixmap_with_rect(fz_pixmap *dst, fz_pixmap *src, int alpha, fz_bbox bbox); + cookie: Communication mechanism between caller and library + running the page. Intended for multi-threaded applications, + while single-threaded applications set cookie to NULL. The + caller may abort an ongoing page run. Cookie also communicates + progress information back to the caller. The fields inside + cookie are continually updated while the page is being run. +*/ +void fz_run_display_list(fz_display_list *list, fz_device *dev, fz_matrix ctm, fz_bbox area, fz_cookie *cookie); -void fz_blend_pixmap(fz_pixmap *dst, fz_pixmap *src, int alpha, int blendmode, int isolated, fz_pixmap *shape); -void fz_blend_pixel(unsigned char dp[3], unsigned char bp[3], unsigned char sp[3], int blendmode); +/* + fz_free_display_list: Frees a display list. -enum -{ - /* PDF 1.4 -- standard separable */ - FZ_BLEND_NORMAL, - FZ_BLEND_MULTIPLY, - FZ_BLEND_SCREEN, - FZ_BLEND_OVERLAY, - FZ_BLEND_DARKEN, - FZ_BLEND_LIGHTEN, - FZ_BLEND_COLOR_DODGE, - FZ_BLEND_COLOR_BURN, - FZ_BLEND_HARD_LIGHT, - FZ_BLEND_SOFT_LIGHT, - FZ_BLEND_DIFFERENCE, - FZ_BLEND_EXCLUSION, - - /* PDF 1.4 -- standard non-separable */ - FZ_BLEND_HUE, - FZ_BLEND_SATURATION, - FZ_BLEND_COLOR, - FZ_BLEND_LUMINOSITY, - - /* For packing purposes */ - FZ_BLEND_MODEMASK = 15, - FZ_BLEND_ISOLATED = 16, - FZ_BLEND_KNOCKOUT = 32 -}; + list: Display list to be freed. Any objects put into the + display list by a list device will also be freed. + + Does not throw exceptions. +*/ +void fz_free_display_list(fz_context *ctx, fz_display_list *list); /* Links */ @@ -1590,6 +1727,35 @@ enum { fz_link_flag_r_is_zoom = 64 /* rb.x is actually a zoom figure */ }; +/* + fz_link_dest: XXX + + kind: Set to one of FZ_LINK_* to tell what what type of link + destination this is, and where in the union to look for + information. XXX + + gotor.page: Page number, 0 is the first page of the document. XXX + + gotor.flags: A bitfield consisting of fz_link_flag_* telling + what parts of gotor.lt and gotor.rb are valid, whether + fitting to width/height should be used, or if an arbitrary + zoom factor is used. XXX + + gotor.lt: The top left corner of the destination bounding box. XXX + gotor.rb: The bottom right corner of the destination bounding box. XXX + + gotor.file_spec: XXX + + gotor.new_window: XXX + + uri.uri: XXX + uri.is_map: XXX + + launch.file_spec: XXX + launch.new_window: XXX + + named.named: XXX +*/ struct fz_link_dest_s { fz_link_kind kind; @@ -1626,6 +1792,25 @@ struct fz_link_dest_s ld; }; +/* + fz_link is a list of interactive links on a page. + + There is no relation between the order of the links in the + list and the order they appear on the page. The list of links + for a given page can be obtained from fz_load_links. + + A link is reference counted. Dropping a reference to a link is + done by calling fz_drop_link. + + rect: The hot zone. The area that can be clicked in + untransformed coordinates. + + dest: Link destinations come in two forms: Page and area that + an application should display when this link is activated. Or + as an URI that can be given to a browser. + + next: A pointer to the next link on the same page. +*/ struct fz_link_s { int refs; @@ -1636,13 +1821,37 @@ struct fz_link_s fz_link *fz_new_link(fz_context *ctx, fz_rect bbox, fz_link_dest dest); fz_link *fz_keep_link(fz_context *ctx, fz_link *link); + +/* + fz_drop_link: Drop and free a list of links. + + Does not throw exceptions. +*/ void fz_drop_link(fz_context *ctx, fz_link *link); + void fz_free_link_dest(fz_context *ctx, fz_link_dest *dest); /* Outline */ typedef struct fz_outline_s fz_outline; +/* + fz_outline is a tree of the outline of a document (also known + as table of contents). + + title: Title of outline item using UTF-8 encoding. May be NULL + if the outline item has no text string. + + dest: Destination in the document to be displayed when this + outline item is activated. May be FZ_LINK_NONE if the outline + item does not have a destination. + + next: The next outline item at the same level as this outline + item. May be NULL if no more outline items exist at this level. + + down: The outline items immediate children in the hierarchy. + May be NULL if no children exist. +*/ struct fz_outline_s { char *title; @@ -1651,43 +1860,155 @@ struct fz_outline_s fz_outline *down; }; -void fz_debug_outline_xml(fz_context *ctx, fz_outline *outline, int level); -void fz_debug_outline(fz_context *ctx, fz_outline *outline, int level); -void fz_free_outline(fz_context *ctx, fz_outline *outline); +/* + fz_print_outline_xml: Dump the given outlines as (pseudo) XML. + + out: The file handle to output to. -/* Document interface */ + outline: The outlines to output. +*/ +void fz_print_outline_xml(fz_context *ctx, FILE *out, fz_outline *outline); +/* + fz_print_outline: Dump the given outlines to as text. + + out: The file handle to output to. + + outline: The outlines to output. +*/ +void fz_print_outline(fz_context *ctx, FILE *out, fz_outline *outline); + +/* + fz_free_outline: Free hierarchical outline. + + Free an outline obtained from fz_load_outline. + + Does not throw exceptions. +*/ +void fz_free_outline(fz_context *ctx, fz_outline *outline); + +/* + Document interface +*/ typedef struct fz_document_s fz_document; -typedef struct fz_page_s fz_page; /* doesn't have a definition -- always cast to *_page */ +typedef struct fz_page_s fz_page; -struct fz_document_s -{ - void (*close)(fz_document *); - int (*needs_password)(fz_document *doc); - int (*authenticate_password)(fz_document *doc, char *password); - fz_outline *(*load_outline)(fz_document *doc); - int (*count_pages)(fz_document *doc); - fz_page *(*load_page)(fz_document *doc, int number); - fz_link *(*load_links)(fz_document *doc, fz_page *page); - fz_rect (*bound_page)(fz_document *doc, fz_page *page); - void (*run_page)(fz_document *doc, fz_page *page, fz_device *dev, fz_matrix transform, fz_cookie *cookie); - void (*free_page)(fz_document *doc, fz_page *page); -}; +/* + fz_open_document: Open a PDF, XPS or CBZ document. + + Open a document file and read its basic structure so pages and + objects can be located. MuPDF will try to repair broken + documents (without actually changing the file contents). + The returned fz_document is used when calling most other + document related functions. Note that it wraps the context, so + those functions implicitly can access the global state in + context. + + filename: a path to a file as it would be given to open(2). +*/ fz_document *fz_open_document(fz_context *ctx, char *filename); +/* + fz_close_document: Close and free an open document. + + The resource store in the context associated with fz_document + is emptied, and any allocations for the document are freed. + + Does not throw exceptions. +*/ void fz_close_document(fz_document *doc); +/* + fz_needs_password: Check if a document is encrypted with a + non-blank password. + + Does not throw exceptions. +*/ int fz_needs_password(fz_document *doc); + +/* + fz_authenticate_password: Test if the given password can + decrypt the document. + + password: The password string to be checked. Some document + specifications do not specify any particular text encoding, so + neither do we. + + Does not throw exceptions. +*/ int fz_authenticate_password(fz_document *doc, char *password); +/* + fz_load_outline: Load the hierarchical document outline. + + Should be freed by fz_free_outline. +*/ fz_outline *fz_load_outline(fz_document *doc); +/* + fz_count_pages: Return the number of pages in document + + May return 0 for documents with no pages. +*/ int fz_count_pages(fz_document *doc); + +/* + fz_load_page: Load a page. + + After fz_load_page is it possible to retrieve the size of the + page using fz_bound_page, or to render the page using + fz_run_page_*. Free the page by calling fz_free_page. + + number: page number, 0 is the first page of the document. +*/ fz_page *fz_load_page(fz_document *doc, int number); + +/* + fz_load_links: Load the list of links for a page. + + Returns a linked list of all the links on the page, each with + its clickable region and link destination. Each link is + reference counted so drop and free the list of links by + calling fz_drop_link on the pointer return from fz_load_links. + + page: Page obtained from fz_load_page. +*/ fz_link *fz_load_links(fz_document *doc, fz_page *page); + +/* + fz_bound_page: Determine the size of a page at 72 dpi. + + Does not throw exceptions. +*/ fz_rect fz_bound_page(fz_document *doc, fz_page *page); + +/* + fz_run_page: Run a page through a device. + + page: Page obtained from fz_load_page. + + dev: Device obtained from fz_new_*_device. + + transform: Transform to apply to page. May include for example + scaling and rotation, see fz_scale, fz_rotate and fz_concat. + Set to fz_identity if no transformation is desired. + + cookie: Communication mechanism between caller and library + rendering the page. Intended for multi-threaded applications, + while single-threaded applications set cookie to NULL. The + caller may abort an ongoing rendering of a page. Cookie also + communicates progress information back to the caller. The + fields inside cookie are continually updated while the page is + rendering. +*/ void fz_run_page(fz_document *doc, fz_page *page, fz_device *dev, fz_matrix transform, fz_cookie *cookie); + +/* + fz_free_page: Free a loaded page. + + Does not throw exceptions. +*/ void fz_free_page(fz_document *doc, fz_page *page); #endif diff --git a/fitz/image_jpeg.c b/fitz/image_jpeg.c index f53b6124..2840adbf 100644 --- a/fitz/image_jpeg.c +++ b/fitz/image_jpeg.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" #include <jpeglib.h> #include <setjmp.h> @@ -115,6 +115,9 @@ fz_load_jpeg(fz_context *ctx, unsigned char *rbuf, int rlen) image->yres = cinfo.Y_density * 254 / 100; } + if (image->xres <= 0) image->xres = 72; + if (image->yres <= 0) image->yres = 72; + fz_clear_pixmap(ctx, image); row[0] = fz_malloc(ctx, cinfo.output_components * cinfo.output_width); diff --git a/fitz/image_jpx.c b/fitz/image_jpx.c index 4d76b4e8..ac1db35a 100644 --- a/fitz/image_jpx.c +++ b/fitz/image_jpx.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" #define OPJ_STATIC #include <openjpeg.h> @@ -144,7 +144,7 @@ fz_load_jpx(fz_context *ctx, unsigned char *data, int size, fz_colorspace *defcs if (n == 4) { fz_pixmap *tmp = fz_new_pixmap(ctx, fz_device_rgb, w, h); - fz_convert_pixmap(ctx, img, tmp); + fz_convert_pixmap(ctx, tmp, img); fz_drop_pixmap(ctx, img); img = tmp; } diff --git a/fitz/image_md5.c b/fitz/image_md5.c new file mode 100644 index 00000000..86ba0a12 --- /dev/null +++ b/fitz/image_md5.c @@ -0,0 +1,11 @@ +#include "fitz-internal.h" + +void fz_md5_pixmap(fz_pixmap *pix, unsigned char digest[16]) +{ + fz_md5 md5; + + fz_md5_init(&md5); + if (pix) + fz_md5_update(&md5, pix->samples, pix->w * pix->h * pix->n); + fz_md5_final(&md5, digest); +} diff --git a/fitz/image_png.c b/fitz/image_png.c index b3fe39cc..e2092c10 100644 --- a/fitz/image_png.c +++ b/fitz/image_png.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" #include <zlib.h> diff --git a/fitz/image_save.c b/fitz/image_save.c new file mode 100644 index 00000000..95be1cd3 --- /dev/null +++ b/fitz/image_save.c @@ -0,0 +1,32 @@ +#include "fitz-internal.h" + +void fz_write_pixmap(fz_context *ctx, fz_pixmap *img, char *file, int rgb) +{ + char name[1024]; + fz_pixmap *converted = NULL; + + if (!img) + return; + + if (rgb && img->colorspace && img->colorspace != fz_device_rgb) + { + converted = fz_new_pixmap_with_bbox(ctx, fz_device_rgb, fz_pixmap_bbox(ctx, img)); + fz_convert_pixmap(ctx, converted, img); + img = converted; + } + + if (img->n <= 4) + { + sprintf(name, "%s.png", file); + printf("extracting image %s\n", name); + fz_write_png(ctx, img, name, 0); + } + else + { + sprintf(name, "%s.pam", file); + printf("extracting image %s\n", name); + fz_write_pam(ctx, img, name, 0); + } + + fz_drop_pixmap(ctx, converted); +} diff --git a/fitz/image_tiff.c b/fitz/image_tiff.c index fff91a9e..ffd0ea88 100644 --- a/fitz/image_tiff.c +++ b/fitz/image_tiff.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" /* * TIFF image loader. Should be enough to support TIFF files in XPS. @@ -789,7 +789,7 @@ fz_load_tiff(fz_context *ctx, unsigned char *buf, int len) if (image->n == 5) { fz_pixmap *rgb = fz_new_pixmap(tiff.ctx, fz_device_rgb, image->w, image->h); - fz_convert_pixmap(tiff.ctx, image, rgb); + fz_convert_pixmap(tiff.ctx, rgb, image); rgb->xres = image->xres; rgb->yres = image->yres; fz_drop_pixmap(ctx, image); diff --git a/fitz/res_bitmap.c b/fitz/res_bitmap.c index 2aa28b02..1bd2827d 100644 --- a/fitz/res_bitmap.c +++ b/fitz/res_bitmap.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" fz_bitmap * fz_new_bitmap(fz_context *ctx, int w, int h, int n) @@ -74,3 +74,48 @@ fz_write_pbm(fz_context *ctx, fz_bitmap *bitmap, char *filename) fclose(fp); } + +fz_colorspace *fz_pixmap_colorspace(fz_context *ctx, fz_pixmap *pix) +{ + if (!pix) + return NULL; + return pix->colorspace; +} + +int fz_pixmap_components(fz_context *ctx, fz_pixmap *pix) +{ + if (!pix) + return 0; + return pix->n; +} + +unsigned char *fz_pixmap_samples(fz_context *ctx, fz_pixmap *pix) +{ + if (!pix) + return NULL; + return pix->samples; +} + +void fz_bitmap_details(fz_bitmap *bit, int *w, int *h, int *n, int *stride) +{ + if (!bit) + { + if (w) + *w = 0; + if (h) + *h = 0; + if (n) + *n = 0; + if (stride) + *stride = 0; + return; + } + if (w) + *w = bit->w; + if (h) + *h = bit->h; + if (n) + *n = bit->n; + if (stride) + *w = bit->stride; +} diff --git a/fitz/res_colorspace.c b/fitz/res_colorspace.c index 413da3ae..587db381 100644 --- a/fitz/res_colorspace.c +++ b/fitz/res_colorspace.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" #define SLOWCMYK @@ -164,7 +164,7 @@ fz_colorspace *fz_device_bgr = &k_device_bgr; fz_colorspace *fz_device_cmyk = &k_device_cmyk; fz_colorspace * -fz_find_device_colorspace(char *name) +fz_find_device_colorspace(fz_context *ctx, char *name) { if (!strcmp(name, "DeviceGray")) return fz_device_gray; @@ -180,7 +180,7 @@ fz_find_device_colorspace(char *name) /* Fast pixmap color conversions */ -static void fast_gray_to_rgb(fz_pixmap *src, fz_pixmap *dst) +static void fast_gray_to_rgb(fz_pixmap *dst, fz_pixmap *src) { unsigned char *s = src->samples; unsigned char *d = dst->samples; @@ -196,7 +196,7 @@ static void fast_gray_to_rgb(fz_pixmap *src, fz_pixmap *dst) } } -static void fast_gray_to_cmyk(fz_pixmap *src, fz_pixmap *dst) +static void fast_gray_to_cmyk(fz_pixmap *dst, fz_pixmap *src) { unsigned char *s = src->samples; unsigned char *d = dst->samples; @@ -213,7 +213,7 @@ static void fast_gray_to_cmyk(fz_pixmap *src, fz_pixmap *dst) } } -static void fast_rgb_to_gray(fz_pixmap *src, fz_pixmap *dst) +static void fast_rgb_to_gray(fz_pixmap *dst, fz_pixmap *src) { unsigned char *s = src->samples; unsigned char *d = dst->samples; @@ -227,7 +227,7 @@ static void fast_rgb_to_gray(fz_pixmap *src, fz_pixmap *dst) } } -static void fast_bgr_to_gray(fz_pixmap *src, fz_pixmap *dst) +static void fast_bgr_to_gray(fz_pixmap *dst, fz_pixmap *src) { unsigned char *s = src->samples; unsigned char *d = dst->samples; @@ -241,7 +241,7 @@ static void fast_bgr_to_gray(fz_pixmap *src, fz_pixmap *dst) } } -static void fast_rgb_to_cmyk(fz_pixmap *src, fz_pixmap *dst) +static void fast_rgb_to_cmyk(fz_pixmap *dst, fz_pixmap *src) { unsigned char *s = src->samples; unsigned char *d = dst->samples; @@ -262,7 +262,7 @@ static void fast_rgb_to_cmyk(fz_pixmap *src, fz_pixmap *dst) } } -static void fast_bgr_to_cmyk(fz_pixmap *src, fz_pixmap *dst) +static void fast_bgr_to_cmyk(fz_pixmap *dst, fz_pixmap *src) { unsigned char *s = src->samples; unsigned char *d = dst->samples; @@ -283,7 +283,7 @@ static void fast_bgr_to_cmyk(fz_pixmap *src, fz_pixmap *dst) } } -static void fast_cmyk_to_gray(fz_pixmap *src, fz_pixmap *dst) +static void fast_cmyk_to_gray(fz_pixmap *dst, fz_pixmap *src) { unsigned char *s = src->samples; unsigned char *d = dst->samples; @@ -300,7 +300,7 @@ static void fast_cmyk_to_gray(fz_pixmap *src, fz_pixmap *dst) } } -static void fast_cmyk_to_rgb(fz_context *ctx, fz_pixmap *src, fz_pixmap *dst) +static void fast_cmyk_to_rgb(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) { unsigned char *s = src->samples; unsigned char *d = dst->samples; @@ -328,7 +328,7 @@ static void fast_cmyk_to_rgb(fz_context *ctx, fz_pixmap *src, fz_pixmap *dst) } } -static void fast_cmyk_to_bgr(fz_context *ctx, fz_pixmap *src, fz_pixmap *dst) +static void fast_cmyk_to_bgr(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) { unsigned char *s = src->samples; unsigned char *d = dst->samples; @@ -356,7 +356,7 @@ static void fast_cmyk_to_bgr(fz_context *ctx, fz_pixmap *src, fz_pixmap *dst) } } -static void fast_rgb_to_bgr(fz_pixmap *src, fz_pixmap *dst) +static void fast_rgb_to_bgr(fz_pixmap *dst, fz_pixmap *src) { unsigned char *s = src->samples; unsigned char *d = dst->samples; @@ -373,7 +373,7 @@ static void fast_rgb_to_bgr(fz_pixmap *src, fz_pixmap *dst) } static void -fz_std_conv_pixmap(fz_context *ctx, fz_pixmap *src, fz_pixmap *dst) +fz_std_conv_pixmap(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) { float srcv[FZ_MAX_COLORS]; float dstv[FZ_MAX_COLORS]; @@ -404,7 +404,7 @@ fz_std_conv_pixmap(fz_context *ctx, fz_pixmap *src, fz_pixmap *dst) srcv[1] = *s++ - 128; srcv[2] = *s++ - 128; - fz_convert_color(ctx, ss, srcv, ds, dstv); + fz_convert_color(ctx, ds, dstv, ss, srcv); for (k = 0; k < dstn; k++) *d++ = dstv[k] * 255; @@ -424,7 +424,7 @@ fz_std_conv_pixmap(fz_context *ctx, fz_pixmap *src, fz_pixmap *dst) for (k = 0; k < srcn; k++) srcv[k] = *s++ / 255.0f; - fz_convert_color(ctx, ss, srcv, ds, dstv); + fz_convert_color(ctx, ds, dstv, ss, srcv); for (k = 0; k < dstn; k++) *d++ = dstv[k] * 255; @@ -442,7 +442,7 @@ fz_std_conv_pixmap(fz_context *ctx, fz_pixmap *src, fz_pixmap *dst) for (i = 0; i < 256; i++) { srcv[0] = i / 255.0f; - fz_convert_color(ctx, ss, srcv, ds, dstv); + fz_convert_color(ctx, ds, dstv, ss, srcv); for (k = 0; k < dstn; k++) lookup[i * dstn + k] = dstv[k] * 255; } @@ -465,7 +465,7 @@ fz_std_conv_pixmap(fz_context *ctx, fz_pixmap *src, fz_pixmap *dst) fz_hash_table *lookup; unsigned char *color; - lookup = fz_new_hash_table(ctx, 509, srcn); + lookup = fz_new_hash_table(ctx, 509, srcn, -1); for (y = 0; y < src->h; y++) { @@ -483,7 +483,7 @@ fz_std_conv_pixmap(fz_context *ctx, fz_pixmap *src, fz_pixmap *dst) { for (k = 0; k < srcn; k++) srcv[k] = *s++ / 255.0f; - fz_convert_color(ctx, ss, srcv, ds, dstv); + fz_convert_color(ctx, ds, dstv, ss, srcv); for (k = 0; k < dstn; k++) *d++ = dstv[k] * 255; @@ -499,50 +499,48 @@ fz_std_conv_pixmap(fz_context *ctx, fz_pixmap *src, fz_pixmap *dst) } void -fz_convert_pixmap(fz_context *ctx, fz_pixmap *sp, fz_pixmap *dp) +fz_convert_pixmap(fz_context *ctx, fz_pixmap *dp, fz_pixmap *sp) { fz_colorspace *ss = sp->colorspace; fz_colorspace *ds = dp->colorspace; assert(ss && ds); - if (sp->mask) - dp->mask = fz_keep_pixmap(ctx, sp->mask); dp->interpolate = sp->interpolate; if (ss == fz_device_gray) { - if (ds == fz_device_rgb) fast_gray_to_rgb(sp, dp); - else if (ds == fz_device_bgr) fast_gray_to_rgb(sp, dp); /* bgr == rgb here */ - else if (ds == fz_device_cmyk) fast_gray_to_cmyk(sp, dp); - else fz_std_conv_pixmap(ctx, sp, dp); + if (ds == fz_device_rgb) fast_gray_to_rgb(dp, sp); + else if (ds == fz_device_bgr) fast_gray_to_rgb(dp, sp); /* bgr == rgb here */ + else if (ds == fz_device_cmyk) fast_gray_to_cmyk(dp, sp); + else fz_std_conv_pixmap(ctx, dp, sp); } else if (ss == fz_device_rgb) { - if (ds == fz_device_gray) fast_rgb_to_gray(sp, dp); - else if (ds == fz_device_bgr) fast_rgb_to_bgr(sp, dp); - else if (ds == fz_device_cmyk) fast_rgb_to_cmyk(sp, dp); - else fz_std_conv_pixmap(ctx, sp, dp); + if (ds == fz_device_gray) fast_rgb_to_gray(dp, sp); + else if (ds == fz_device_bgr) fast_rgb_to_bgr(dp, sp); + else if (ds == fz_device_cmyk) fast_rgb_to_cmyk(dp, sp); + else fz_std_conv_pixmap(ctx, dp, sp); } else if (ss == fz_device_bgr) { - if (ds == fz_device_gray) fast_bgr_to_gray(sp, dp); - else if (ds == fz_device_rgb) fast_rgb_to_bgr(sp, dp); /* bgr = rgb here */ + if (ds == fz_device_gray) fast_bgr_to_gray(dp, sp); + else if (ds == fz_device_rgb) fast_rgb_to_bgr(dp, sp); /* bgr = rgb here */ else if (ds == fz_device_cmyk) fast_bgr_to_cmyk(sp, dp); - else fz_std_conv_pixmap(ctx, sp, dp); + else fz_std_conv_pixmap(ctx, dp, sp); } else if (ss == fz_device_cmyk) { - if (ds == fz_device_gray) fast_cmyk_to_gray(sp, dp); - else if (ds == fz_device_bgr) fast_cmyk_to_bgr(ctx, sp, dp); - else if (ds == fz_device_rgb) fast_cmyk_to_rgb(ctx, sp, dp); - else fz_std_conv_pixmap(ctx, sp, dp); + if (ds == fz_device_gray) fast_cmyk_to_gray(dp, sp); + else if (ds == fz_device_bgr) fast_cmyk_to_bgr(ctx, dp, sp); + else if (ds == fz_device_rgb) fast_cmyk_to_rgb(ctx, dp, sp); + else fz_std_conv_pixmap(ctx, dp, sp); } - else fz_std_conv_pixmap(ctx, sp, dp); + else fz_std_conv_pixmap(ctx, dp, sp); } /* Convert a single color */ @@ -569,7 +567,7 @@ fz_std_conv_color(fz_context *ctx, fz_colorspace *srcs, float *srcv, fz_colorspa } void -fz_convert_color(fz_context *ctx, fz_colorspace *ss, float *sv, fz_colorspace *ds, float *dv) +fz_convert_color(fz_context *ctx, fz_colorspace *ds, float *dv, fz_colorspace *ss, float *sv) { if (ss == fz_device_gray) { diff --git a/fitz/res_font.c b/fitz/res_font.c index 7a194da6..c1db35d8 100644 --- a/fitz/res_font.c +++ b/fitz/res_font.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" #include <ft2build.h> #include FT_FREETYPE_H @@ -93,7 +93,7 @@ fz_drop_font(fz_context *ctx, fz_font *font) if (font->t3procs) { if (font->t3resources) - fz_drop_obj(font->t3resources); + font->t3freeres(font->t3doc, font->t3resources); for (i = 0; i < 256; i++) if (font->t3procs[i]) fz_drop_buffer(ctx, font->t3procs[i]); @@ -387,12 +387,13 @@ fz_copy_ft_bitmap(fz_context *ctx, int left, int top, FT_Bitmap *bitmap) /* The glyph cache lock is always taken when this is called. */ fz_pixmap * -fz_render_ft_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix trm) +fz_render_ft_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix trm, int aa) { FT_Face face = font->ft_face; FT_Matrix m; FT_Vector v; FT_Error fterr; + fz_pixmap *result; trm = fz_adjust_ft_glyph_width(ctx, font, gid, trm); @@ -420,7 +421,7 @@ fz_render_ft_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix trm) fz_warn(ctx, "freetype setting character size: %s", ft_error_string(fterr)); FT_Set_Transform(face, &m, &v); - if (fz_get_aa_level(ctx) == 0) + if (aa == 0) { /* If you really want grid fitting, enable this code. */ float scale = fz_matrix_expansion(trm); @@ -470,16 +471,17 @@ fz_render_ft_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix trm) FT_Outline_Translate(&face->glyph->outline, -strength * 32, -strength * 32); } - fterr = FT_Render_Glyph(face->glyph, fz_get_aa_level(ctx) > 0 ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO); + fterr = FT_Render_Glyph(face->glyph, fz_aa_level(ctx) > 0 ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO); if (fterr) { fz_warn(ctx, "freetype render glyph (gid %d): %s", gid, ft_error_string(fterr)); fz_unlock(ctx, FZ_LOCK_FREETYPE); return NULL; } - fz_unlock(ctx, FZ_LOCK_FREETYPE); - return fz_copy_ft_bitmap(ctx, face->glyph->bitmap_left, face->glyph->bitmap_top, &face->glyph->bitmap); + result = fz_copy_ft_bitmap(ctx, face->glyph->bitmap_left, face->glyph->bitmap_top, &face->glyph->bitmap); + fz_unlock(ctx, FZ_LOCK_FREETYPE); + return result; } fz_pixmap * @@ -573,7 +575,7 @@ fz_render_ft_stroked_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix tr FT_Stroker_Done(stroker); - fterr = FT_Glyph_To_Bitmap(&glyph, fz_get_aa_level(ctx) > 0 ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, 0, 1); + fterr = FT_Glyph_To_Bitmap(&glyph, fz_aa_level(ctx) > 0 ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, 0, 1); if (fterr) { fz_warn(ctx, "FT_Glyph_To_Bitmap: %s", ft_error_string(fterr)); @@ -754,7 +756,7 @@ fz_render_t3_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix trm, fz_co bbox.x1++; bbox.y1++; - glyph = fz_new_pixmap_with_rect(ctx, model ? model : fz_device_gray, bbox); + glyph = fz_new_pixmap_with_bbox(ctx, model ? model : fz_device_gray, bbox); fz_clear_pixmap(ctx, glyph); ctm = fz_concat(font->t3matrix, trm); @@ -806,29 +808,29 @@ fz_render_t3_glyph_direct(fz_context *ctx, fz_device *dev, fz_font *font, int gi } void -fz_debug_font(fz_context *ctx, fz_font *font) +fz_print_font(fz_context *ctx, FILE *out, fz_font *font) { - printf("font '%s' {\n", font->name); + fprintf(out, "font '%s' {\n", font->name); if (font->ft_face) { - printf("\tfreetype face %p\n", font->ft_face); + fprintf(out, "\tfreetype face %p\n", font->ft_face); if (font->ft_substitute) - printf("\tsubstitute font\n"); + fprintf(out, "\tsubstitute font\n"); } if (font->t3procs) { - printf("\ttype3 matrix [%g %g %g %g]\n", + fprintf(out, "\ttype3 matrix [%g %g %g %g]\n", font->t3matrix.a, font->t3matrix.b, font->t3matrix.c, font->t3matrix.d); - printf("\ttype3 bbox [%g %g %g %g]\n", + fprintf(out, "\ttype3 bbox [%g %g %g %g]\n", font->bbox.x0, font->bbox.y0, font->bbox.x1, font->bbox.y1); } - printf("}\n"); + fprintf(out, "}\n"); } fz_rect diff --git a/fitz/res_halftone.c b/fitz/res_halftone.c index 7258ad01..daad6e37 100644 --- a/fitz/res_halftone.c +++ b/fitz/res_halftone.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" fz_halftone * fz_new_halftone(fz_context *ctx, int comps) @@ -56,7 +56,7 @@ static unsigned char mono_ht[] = 0xF2, 0x72, 0xD2, 0x52, 0xFA, 0x7A, 0xDA, 0x5A, 0xF0, 0x70, 0xD0, 0x50, 0xF8, 0x78, 0xD8, 0x58 }; -fz_halftone *fz_get_default_halftone(fz_context *ctx, int num_comps) +fz_halftone *fz_default_halftone(fz_context *ctx, int num_comps) { fz_halftone *ht = fz_new_halftone(ctx, num_comps); assert(num_comps == 1); /* Only support 1 component for now */ @@ -162,13 +162,18 @@ fz_bitmap *fz_halftone_pixmap(fz_context *ctx, fz_pixmap *pix, fz_halftone *ht) fz_bitmap *out; unsigned char *ht_line, *o, *p; int w, h, x, y, n, pstride, ostride; + fz_halftone *ht_orig = ht; - if (!pix || !ht) + if (!pix) return NULL; assert(pix->n == 2); /* Mono + Alpha */ n = pix->n-1; /* Remove alpha */ + if (ht == NULL) + { + ht = fz_default_halftone(ctx, n); + } ht_line = fz_malloc(ctx, pix->w * n); out = fz_new_bitmap(ctx, pix->w, pix->h, n); o = out->samples; @@ -187,5 +192,7 @@ fz_bitmap *fz_halftone_pixmap(fz_context *ctx, fz_pixmap *pix, fz_halftone *ht) o += ostride; p += pstride; } + if (!ht_orig) + fz_drop_halftone(ctx, ht); return out; } diff --git a/fitz/res_path.c b/fitz/res_path.c index a645f9d5..45ead677 100644 --- a/fitz/res_path.c +++ b/fitz/res_path.c @@ -1,5 +1,5 @@ #include <assert.h> -#include "fitz.h" +#include "fitz-internal.h" fz_path * fz_new_path(fz_context *ctx) @@ -316,7 +316,7 @@ fz_transform_path(fz_context *ctx, fz_path *path, fz_matrix ctm) } void -fz_debug_path(fz_context *ctx, fz_path *path, int indent) +fz_print_path(fz_context *ctx, FILE *out, fz_path *path, int indent) { float x, y; int i = 0; @@ -324,32 +324,32 @@ fz_debug_path(fz_context *ctx, fz_path *path, int indent) while (i < path->len) { for (n = 0; n < indent; n++) - putchar(' '); + fputc(' ', out); switch (path->items[i++].k) { case FZ_MOVETO: x = path->items[i++].v; y = path->items[i++].v; - printf("%g %g m\n", x, y); + fprintf(out, "%g %g m\n", x, y); break; case FZ_LINETO: x = path->items[i++].v; y = path->items[i++].v; - printf("%g %g l\n", x, y); + fprintf(out, "%g %g l\n", x, y); break; case FZ_CURVETO: x = path->items[i++].v; y = path->items[i++].v; - printf("%g %g ", x, y); + fprintf(out, "%g %g ", x, y); x = path->items[i++].v; y = path->items[i++].v; - printf("%g %g ", x, y); + fprintf(out, "%g %g ", x, y); x = path->items[i++].v; y = path->items[i++].v; - printf("%g %g c\n", x, y); + fprintf(out, "%g %g c\n", x, y); break; case FZ_CLOSE_PATH: - printf("h\n"); + fprintf(out, "h\n"); break; } } diff --git a/fitz/res_pixmap.c b/fitz/res_pixmap.c index dbed0842..a160b6ca 100644 --- a/fitz/res_pixmap.c +++ b/fitz/res_pixmap.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" fz_pixmap * fz_keep_pixmap(fz_context *ctx, fz_pixmap *pix) @@ -17,8 +17,6 @@ fz_free_pixmap_imp(fz_context *ctx, fz_storable *pix_) { fz_pixmap *pix = (fz_pixmap *)pix_; - if (pix->mask) - fz_drop_pixmap(ctx, pix->mask); if (pix->colorspace) fz_drop_colorspace(ctx, pix->colorspace); if (pix->free_samples) @@ -37,7 +35,6 @@ fz_new_pixmap_with_data(fz_context *ctx, fz_colorspace *colorspace, int w, int h pix->y = 0; pix->w = w; pix->h = h; - pix->mask = NULL; pix->interpolate = 1; pix->xres = 96; pix->yres = 96; @@ -65,6 +62,8 @@ fz_new_pixmap_with_data(fz_context *ctx, fz_colorspace *colorspace, int w, int h } fz_catch(ctx) { + if (colorspace) + fz_drop_colorspace(ctx, colorspace); fz_free(ctx, pix); fz_rethrow(ctx); } @@ -81,7 +80,7 @@ fz_new_pixmap(fz_context *ctx, fz_colorspace *colorspace, int w, int h) } fz_pixmap * -fz_new_pixmap_with_rect(fz_context *ctx, fz_colorspace *colorspace, fz_bbox r) +fz_new_pixmap_with_bbox(fz_context *ctx, fz_colorspace *colorspace, fz_bbox r) { fz_pixmap *pixmap; pixmap = fz_new_pixmap(ctx, colorspace, r.x1 - r.x0, r.y1 - r.y0); @@ -91,7 +90,7 @@ fz_new_pixmap_with_rect(fz_context *ctx, fz_colorspace *colorspace, fz_bbox r) } fz_pixmap * -fz_new_pixmap_with_rect_and_data(fz_context *ctx, fz_colorspace *colorspace, fz_bbox r, unsigned char *samples) +fz_new_pixmap_with_bbox_and_data(fz_context *ctx, fz_colorspace *colorspace, fz_bbox r, unsigned char *samples) { fz_pixmap *pixmap; pixmap = fz_new_pixmap_with_data(ctx, colorspace, r.x1 - r.x0, r.y1 - r.y0, samples); @@ -101,7 +100,7 @@ fz_new_pixmap_with_rect_and_data(fz_context *ctx, fz_colorspace *colorspace, fz_ } fz_bbox -fz_bound_pixmap(fz_pixmap *pix) +fz_pixmap_bbox(fz_context *ctx, fz_pixmap *pix) { fz_bbox bbox; bbox.x0 = pix->x; @@ -111,6 +110,29 @@ fz_bound_pixmap(fz_pixmap *pix) return bbox; } +fz_bbox +fz_pixmap_bbox_no_ctx(fz_pixmap *pix) +{ + fz_bbox bbox; + bbox.x0 = pix->x; + bbox.y0 = pix->y; + bbox.x1 = pix->x + pix->w; + bbox.y1 = pix->y + pix->h; + return bbox; +} + +int +fz_pixmap_width(fz_context *ctx, fz_pixmap *pix) +{ + return pix->w; +} + +int +fz_pixmap_height(fz_context *ctx, fz_pixmap *pix) +{ + return pix->h; +} + void fz_clear_pixmap(fz_context *ctx, fz_pixmap *pix) { @@ -145,8 +167,8 @@ fz_copy_pixmap_rect(fz_context *ctx, fz_pixmap *dest, fz_pixmap *src, fz_bbox r) unsigned char *destp; int y, w, destspan, srcspan; - r = fz_intersect_bbox(r, fz_bound_pixmap(dest)); - r = fz_intersect_bbox(r, fz_bound_pixmap(src)); + r = fz_intersect_bbox(r, fz_pixmap_bbox(ctx, dest)); + r = fz_intersect_bbox(r, fz_pixmap_bbox(ctx, src)); w = r.x1 - r.x0; y = r.y1 - r.y0; if (w <= 0 || y <= 0) @@ -172,7 +194,7 @@ fz_clear_pixmap_rect_with_value(fz_context *ctx, fz_pixmap *dest, int value, fz_ unsigned char *destp; int x, y, w, k, destspan; - r = fz_intersect_bbox(r, fz_bound_pixmap(dest)); + r = fz_intersect_bbox(r, fz_pixmap_bbox(ctx, dest)); w = r.x1 - r.x0; y = r.y1 - r.y0; if (w <= 0 || y <= 0) @@ -250,7 +272,7 @@ fz_alpha_from_gray(fz_context *ctx, fz_pixmap *gray, int luminosity) assert(gray->n == 2); - alpha = fz_new_pixmap_with_rect(ctx, NULL, fz_bound_pixmap(gray)); + alpha = fz_new_pixmap_with_bbox(ctx, NULL, fz_pixmap_bbox(ctx, gray)); dp = alpha->samples; sp = gray->samples; if (!luminosity) @@ -283,6 +305,27 @@ fz_invert_pixmap(fz_context *ctx, fz_pixmap *pix) } } +void fz_invert_pixmap_rect(fz_pixmap *image, fz_bbox rect) +{ + unsigned char *p; + int x, y, n; + + int x0 = CLAMP(rect.x0 - image->x, 0, image->w - 1); + int x1 = CLAMP(rect.x1 - image->x, 0, image->w - 1); + int y0 = CLAMP(rect.y0 - image->y, 0, image->h - 1); + int y1 = CLAMP(rect.y1 - image->y, 0, image->h - 1); + + for (y = y0; y < y1; y++) + { + p = image->samples + (y * image->w + x0) * image->n; + for (x = x0; x < x1; x++) + { + for (n = image->n; n > 0; n--, p++) + *p = 255 - *p; + } + } +} + void fz_gamma_pixmap(fz_context *ctx, fz_pixmap *pix, float gamma) { @@ -549,3 +592,23 @@ fz_pixmap_size(fz_context *ctx, fz_pixmap * pix) return 0; return sizeof(*pix) + pix->n * pix->w * pix->h; } + +fz_pixmap * +fz_image_to_pixmap(fz_context *ctx, fz_image *image, int w, int h) +{ + if (image == NULL) + return NULL; + return image->get_pixmap(ctx, image, w, h); +} + +fz_image * +fz_keep_image(fz_context *ctx, fz_image *image) +{ + return (fz_image *)fz_keep_storable(ctx, &image->storable); +} + +void +fz_drop_image(fz_context *ctx, fz_image *image) +{ + fz_drop_storable(ctx, &image->storable); +} diff --git a/fitz/res_shade.c b/fitz/res_shade.c index 3fdf2e15..d2b2f44b 100644 --- a/fitz/res_shade.c +++ b/fitz/res_shade.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" fz_shade * fz_keep_shade(fz_context *ctx, fz_shade *shade) @@ -68,59 +68,59 @@ fz_bound_shade(fz_context *ctx, fz_shade *shade, fz_matrix ctm) } void -fz_debug_shade(fz_context *ctx, fz_shade *shade) +fz_print_shade(fz_context *ctx, FILE *out, fz_shade *shade) { int i, j, n; float *vertex; int triangle; - printf("shading {\n"); + fprintf(out, "shading {\n"); switch (shade->type) { - case FZ_LINEAR: printf("\ttype linear\n"); break; - case FZ_RADIAL: printf("\ttype radial\n"); break; - case FZ_MESH: printf("\ttype mesh\n"); break; + case FZ_LINEAR: fprintf(out, "\ttype linear\n"); break; + case FZ_RADIAL: fprintf(out, "\ttype radial\n"); break; + case FZ_MESH: fprintf(out, "\ttype mesh\n"); break; } - printf("\tbbox [%g %g %g %g]\n", + fprintf(out, "\tbbox [%g %g %g %g]\n", shade->bbox.x0, shade->bbox.y0, shade->bbox.x1, shade->bbox.y1); - printf("\tcolorspace %s\n", shade->colorspace->name); + fprintf(out, "\tcolorspace %s\n", shade->colorspace->name); - printf("\tmatrix [%g %g %g %g %g %g]\n", + fprintf(out, "\tmatrix [%g %g %g %g %g %g]\n", shade->matrix.a, shade->matrix.b, shade->matrix.c, shade->matrix.d, shade->matrix.e, shade->matrix.f); if (shade->use_background) { - printf("\tbackground ["); + fprintf(out, "\tbackground ["); for (i = 0; i < shade->colorspace->n; i++) - printf("%s%g", i == 0 ? "" : " ", shade->background[i]); - printf("]\n"); + fprintf(out, "%s%g", i == 0 ? "" : " ", shade->background[i]); + fprintf(out, "]\n"); } if (shade->use_function) { - printf("\tfunction\n"); + fprintf(out, "\tfunction\n"); n = 3; } else n = 2 + shade->colorspace->n; - printf("\tvertices: %d\n", shade->mesh_len); + fprintf(out, "\tvertices: %d\n", shade->mesh_len); vertex = shade->mesh; triangle = 0; i = 0; while (i < shade->mesh_len) { - printf("\t%d:(%g, %g): ", triangle, vertex[0], vertex[1]); + fprintf(out, "\t%d:(%g, %g): ", triangle, vertex[0], vertex[1]); for (j = 2; j < n; j++) - printf("%s%g", j == 2 ? "" : " ", vertex[j]); - printf("\n"); + fprintf(out, "%s%g", j == 2 ? "" : " ", vertex[j]); + fprintf(out, "\n"); vertex += n; i++; @@ -128,5 +128,5 @@ fz_debug_shade(fz_context *ctx, fz_shade *shade) triangle++; } - printf("}\n"); + fprintf(out, "}\n"); } diff --git a/fitz/res_store.c b/fitz/res_store.c index 5eec1f0d..8e5375d6 100644 --- a/fitz/res_store.c +++ b/fitz/res_store.c @@ -1,21 +1,16 @@ -#include "fitz.h" -#include "mupdf.h" +#include "fitz-internal.h" + +typedef struct fz_item_s fz_item; struct fz_item_s { - fz_obj *key; + void *key; fz_storable *val; unsigned int size; fz_item *next; fz_item *prev; fz_store *store; -}; - -struct refkey -{ - fz_store_free_fn *free; - int num; - int gen; + fz_store_type *type; }; struct fz_store_s @@ -43,7 +38,7 @@ fz_new_store_context(fz_context *ctx, unsigned int max) store = fz_malloc_struct(ctx, fz_store); fz_try(ctx) { - store->hash = fz_new_hash_table(ctx, 4096, sizeof(struct refkey)); + store->hash = fz_new_hash_table(ctx, 4096, sizeof(fz_store_hash), FZ_LOCK_ALLOC); } fz_catch(ctx) { @@ -114,20 +109,19 @@ evict(fz_context *ctx, fz_item *item) store->head = item->next; /* Drop a reference to the value (freeing if required) */ drop = (item->val->refs > 0 && --item->val->refs == 0); - fz_unlock(ctx, FZ_LOCK_ALLOC); /* Remove from the hash table */ - if (fz_is_indirect(item->key)) + if (item->type->make_hash_key) { - struct refkey refkey; - refkey.free = item->val->free; - refkey.num = fz_to_num(item->key); - refkey.gen = fz_to_gen(item->key); - fz_hash_remove(ctx, store->hash, &refkey); + fz_store_hash hash = { NULL }; + hash.free = item->val->free; + if (item->type->make_hash_key(&hash, item->key)) + fz_hash_remove(ctx, store->hash, &hash); } + fz_unlock(ctx, FZ_LOCK_ALLOC); if (drop) item->val->free(ctx, item->val); /* Always drops the key and free the item */ - fz_drop_obj(item->key); + item->type->drop_key(ctx, item->key); fz_free(ctx, item); fz_lock(ctx, FZ_LOCK_ALLOC); } @@ -194,30 +188,21 @@ ensure_space(fz_context *ctx, unsigned int tofree) return count; } -void -fz_store_item(fz_context *ctx, fz_obj *key, void *val_, unsigned int itemsize) +void * +fz_store_item(fz_context *ctx, void *key, void *val_, unsigned int itemsize, fz_store_type *type) { fz_item *item = NULL; unsigned int size; fz_storable *val = (fz_storable *)val_; fz_store *store = ctx->store; - int indirect; - struct refkey refkey; + fz_store_hash hash = { NULL }; + int use_hash = 0; if (!store) - return; + return NULL; fz_var(item); - /* Form the key before we take the lock */ - indirect = fz_is_indirect(key); - if (indirect) - { - refkey.free = val->free; - refkey.num = fz_to_num(key); - refkey.gen = fz_to_gen(key); - } - /* If we fail for any reason, we swallow the exception and continue. * All that the above program will see is that we failed to store * the item. */ @@ -227,9 +212,16 @@ fz_store_item(fz_context *ctx, fz_obj *key, void *val_, unsigned int itemsize) } fz_catch(ctx) { - return; + return NULL; } + if (type->make_hash_key) + { + hash.free = val->free; + use_hash = type->make_hash_key(&hash, key); + } + + type->keep_key(ctx, key); fz_lock(ctx, FZ_LOCK_ALLOC); if (store->max != FZ_STORE_UNLIMITED) { @@ -242,34 +234,44 @@ fz_store_item(fz_context *ctx, fz_obj *key, void *val_, unsigned int itemsize) /* Failed to free any space */ fz_unlock(ctx, FZ_LOCK_ALLOC); fz_free(ctx, item); - return; + type->drop_key(ctx, key); + return NULL; } } } store->size += itemsize; - item->key = fz_keep_obj(key); + item->key = key; item->val = val; item->size = itemsize; item->next = NULL; + item->type = type; /* If we can index it fast, put it into the hash table */ - if (indirect) + if (use_hash) { - fz_unlock(ctx, FZ_LOCK_ALLOC); + fz_item *existing; + fz_try(ctx) { - fz_hash_insert(ctx, store->hash, &refkey, item); + /* May drop and retake the lock */ + existing = fz_hash_insert(ctx, store->hash, &hash, item); } fz_catch(ctx) { - fz_free(ctx, item); - fz_lock(ctx, FZ_LOCK_ALLOC); store->size -= itemsize; fz_unlock(ctx, FZ_LOCK_ALLOC); - return; + fz_free(ctx, item); + return NULL; + } + if (existing) + { + /* Take a new reference */ + existing->val->refs++; + fz_unlock(ctx, FZ_LOCK_ALLOC); + fz_free(ctx, item); + return existing->val; } - fz_lock(ctx, FZ_LOCK_ALLOC); } /* Now we can never fail, bump the ref */ if (val->refs > 0) @@ -283,15 +285,17 @@ fz_store_item(fz_context *ctx, fz_obj *key, void *val_, unsigned int itemsize) store->head = item; item->prev = NULL; fz_unlock(ctx, FZ_LOCK_ALLOC); + + return NULL; } void * -fz_find_item(fz_context *ctx, fz_store_free_fn *free, fz_obj *key) +fz_find_item(fz_context *ctx, fz_store_free_fn *free, void *key, fz_store_type *type) { - struct refkey refkey; fz_item *item; fz_store *store = ctx->store; - int indirect; + fz_store_hash hash = { NULL }; + int use_hash = 0; if (!store) return NULL; @@ -299,27 +303,24 @@ fz_find_item(fz_context *ctx, fz_store_free_fn *free, fz_obj *key) if (!key) return NULL; - /* Form the key before we take the lock */ - indirect = fz_is_indirect(key); - if (indirect) + if (type->make_hash_key) { - refkey.free = free; - refkey.num = fz_to_num(key); - refkey.gen = fz_to_gen(key); + hash.free = free; + use_hash = type->make_hash_key(&hash, key); } fz_lock(ctx, FZ_LOCK_ALLOC); - if (indirect) + if (use_hash) { /* We can find objects keyed on indirected objects quickly */ - item = fz_hash_find(ctx, store->hash, &refkey); + item = fz_hash_find(ctx, store->hash, &hash); } else { /* Others we have to hunt for slowly */ for (item = store->head; item; item = item->next) { - if (item->val->free == free && !fz_objcmp(item->key, key)) + if (item->val->free == free && !type->cmp_key(item->key, key)) break; } } @@ -355,35 +356,33 @@ fz_find_item(fz_context *ctx, fz_store_free_fn *free, fz_obj *key) } void -fz_remove_item(fz_context *ctx, fz_store_free_fn *free, fz_obj *key) +fz_remove_item(fz_context *ctx, fz_store_free_fn *free, void *key, fz_store_type *type) { - struct refkey refkey; fz_item *item; fz_store *store = ctx->store; - int drop, indirect; + int drop; + fz_store_hash hash; + int use_hash = 0; - /* Form the key before we take the lock */ - indirect = fz_is_indirect(key); - if (indirect) + if (type->make_hash_key) { - refkey.free = free; - refkey.num = fz_to_num(key); - refkey.gen = fz_to_gen(key); + hash.free = free; + use_hash = type->make_hash_key(&hash, key); } fz_lock(ctx, FZ_LOCK_ALLOC); - if (indirect) + if (use_hash) { /* We can find objects keyed on indirect objects quickly */ - item = fz_hash_find(ctx, store->hash, &refkey); + item = fz_hash_find(ctx, store->hash, &hash); if (item) - fz_hash_remove(ctx, store->hash, &refkey); + fz_hash_remove(ctx, store->hash, &hash); } else { /* Others we have to hunt for slowly */ for (item = store->head; item; item = item->next) - if (item->val->free == free && !fz_objcmp(item->key, key)) + if (item->val->free == free && !type->cmp_key(item->key, key)) break; } if (item) @@ -400,7 +399,7 @@ fz_remove_item(fz_context *ctx, fz_store_free_fn *free, fz_obj *key) fz_unlock(ctx, FZ_LOCK_ALLOC); if (drop) item->val->free(ctx, item->val); - fz_drop_obj(item->key); + type->drop_key(ctx, item->key); fz_free(ctx, item); } else @@ -425,7 +424,7 @@ fz_empty_store(fz_context *ctx) } fz_store * -fz_store_keep(fz_context *ctx) +fz_keep_store_context(fz_context *ctx) { if (ctx == NULL || ctx->store == NULL) return NULL; @@ -454,28 +453,26 @@ fz_drop_store_context(fz_context *ctx) } void -fz_debug_store(fz_context *ctx) +fz_print_store(fz_context *ctx, FILE *out) { fz_item *item, *next; fz_store *store = ctx->store; - printf("-- resource store contents --\n"); + fprintf(out, "-- resource store contents --\n"); fz_lock(ctx, FZ_LOCK_ALLOC); for (item = store->head; item; item = next) { next = item->next; - next->val->refs++; - printf("store[*][refs=%d][size=%d] ", item->val->refs, item->size); + if (next) + next->val->refs++; + fprintf(out, "store[*][refs=%d][size=%d] ", item->val->refs, item->size); fz_unlock(ctx, FZ_LOCK_ALLOC); - if (fz_is_indirect(item->key)) - { - printf("(%d %d R) ", fz_to_num(item->key), fz_to_gen(item->key)); - } else - fz_debug_obj(item->key); - printf(" = %p\n", item->val); + item->type->debug(item->key); + fprintf(out, " = %p\n", item->val); fz_lock(ctx, FZ_LOCK_ALLOC); - next->val->refs--; + if (next) + next->val->refs--; } fz_unlock(ctx, FZ_LOCK_ALLOC); } @@ -524,7 +521,7 @@ int fz_store_scavenge(fz_context *ctx, unsigned int size, int *phase) #ifdef DEBUG_SCAVENGING printf("Scavenging: store=%d size=%d phase=%d\n", store->size, size, *phase); - fz_debug_store(ctx); + fz_print_store(ctx, stderr); Memento_stats(); #endif do @@ -552,7 +549,7 @@ int fz_store_scavenge(fz_context *ctx, unsigned int size, int *phase) { #ifdef DEBUG_SCAVENGING printf("scavenged: store=%d\n", store->size); - fz_debug_store(ctx); + fz_print_store(ctx, stderr); Memento_stats(); #endif return 1; @@ -562,7 +559,7 @@ int fz_store_scavenge(fz_context *ctx, unsigned int size, int *phase) #ifdef DEBUG_SCAVENGING printf("scavenging failed\n"); - fz_debug_store(ctx); + fz_print_store(ctx, stderr); Memento_listBlocks(); #endif return 0; diff --git a/fitz/res_text.c b/fitz/res_text.c index cdfaaa98..643b4c9f 100644 --- a/fitz/res_text.c +++ b/fitz/res_text.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" fz_text * fz_new_text(fz_context *ctx, fz_font *font, fz_matrix trm, int wmode) @@ -118,23 +118,30 @@ fz_add_text(fz_context *ctx, fz_text *text, int gid, int ucs, float x, float y) text->len++; } -static int isxmlmeta(int c) +static int +isxmlmeta(int c) { return c < 32 || c >= 128 || c == '&' || c == '<' || c == '>' || c == '\'' || c == '"'; } -void fz_debug_text(fz_context *ctx, fz_text *text, int indent) +static void +do_print_text(FILE *out, fz_text *text, int indent) { int i, n; for (i = 0; i < text->len; i++) { for (n = 0; n < indent; n++) - putchar(' '); + fputc(' ', out); if (!isxmlmeta(text->items[i].ucs)) - printf("<g ucs=\"%c\" gid=\"%d\" x=\"%g\" y=\"%g\" />\n", + fprintf(out, "<g ucs=\"%c\" gid=\"%d\" x=\"%g\" y=\"%g\" />\n", text->items[i].ucs, text->items[i].gid, text->items[i].x, text->items[i].y); else - printf("<g ucs=\"U+%04X\" gid=\"%d\" x=\"%g\" y=\"%g\" />\n", + fprintf(out, "<g ucs=\"U+%04X\" gid=\"%d\" x=\"%g\" y=\"%g\" />\n", text->items[i].ucs, text->items[i].gid, text->items[i].x, text->items[i].y); } } + +void fz_print_text(fz_context *ctx, FILE *out, fz_text *text) +{ + do_print_text(out, text, 0); +} diff --git a/fitz/stm_buffer.c b/fitz/stm_buffer.c index 7f9d521d..4be0165a 100644 --- a/fitz/stm_buffer.c +++ b/fitz/stm_buffer.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" fz_buffer * fz_new_buffer(fz_context *ctx, int size) @@ -28,7 +28,12 @@ fz_buffer * fz_keep_buffer(fz_context *ctx, fz_buffer *buf) { if (buf) + { + if (buf->refs == 1 && buf->cap > buf->len+1) + fz_resize_buffer(ctx, buf, buf->len); buf->refs ++; + } + return buf; } @@ -58,3 +63,18 @@ fz_grow_buffer(fz_context *ctx, fz_buffer *buf) { fz_resize_buffer(ctx, buf, (buf->cap * 3) / 2); } + +void +fz_trim_buffer(fz_context *ctx, fz_buffer *buf) +{ + if (buf->cap > buf->len+1) + fz_resize_buffer(ctx, buf, buf->len); +} + +int +fz_buffer_storage(fz_context *ctx, fz_buffer *buf, unsigned char **datap) +{ + if (datap) + *datap = (buf ? buf->data : NULL); + return (buf ? buf->len : 0); +} diff --git a/fitz/stm_open.c b/fitz/stm_open.c index e634f729..1e303825 100644 --- a/fitz/stm_open.c +++ b/fitz/stm_open.c @@ -1,4 +1,4 @@ -#include "fitz.h" +#include "fitz-internal.h" fz_stream * fz_new_stream(fz_context *ctx, void *state, diff --git a/fitz/stm_read.c b/fitz/stm_read.c index 2066b14c..216d2207 100644 --- a/fitz/stm_read.c +++ b/fitz/stm_read.c @@ -1,4 +1,6 @@ -#include "fitz.h" +#include "fitz-internal.h" + +#define MIN_BOMB (100 << 20) int fz_read(fz_stream *stm, unsigned char *buf, int len) @@ -101,16 +103,15 @@ fz_read_all(fz_stream *stm, int initial) if (initial < 1024) initial = 1024; - buf = fz_new_buffer(ctx, initial); + buf = fz_new_buffer(ctx, initial+1); while (1) { if (buf->len == buf->cap) fz_grow_buffer(ctx, buf); - if (buf->len / 200 > initial) + if (buf->len >= MIN_BOMB && buf->len / 200 > initial) { - fz_drop_buffer(ctx, buf); fz_throw(ctx, "compression bomb detected"); } @@ -126,6 +127,7 @@ fz_read_all(fz_stream *stm, int initial) fz_drop_buffer(ctx, buf); fz_rethrow(ctx); } + fz_trim_buffer(ctx, buf); return buf; } |