0,0 → 1,802 |
#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; |
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; |
int len; |
int cap; |
struct keyval *items; |
} d; |
struct { |
int num; |
int gen; |
struct pdf_xref_s *xref; |
} r; |
} u; |
}; |
|
static fz_obj *fz_resolve_indirect_null(fz_obj *ref) |
{ |
return ref; |
} |
|
fz_obj* (*fz_resolve_indirect)(fz_obj*) = fz_resolve_indirect_null; |
|
fz_obj * |
fz_new_null(void) |
{ |
fz_obj *obj = fz_malloc(sizeof(fz_obj)); |
obj->refs = 1; |
obj->kind = FZ_NULL; |
return obj; |
} |
|
fz_obj * |
fz_new_bool(int b) |
{ |
fz_obj *obj = fz_malloc(sizeof(fz_obj)); |
obj->refs = 1; |
obj->kind = FZ_BOOL; |
obj->u.b = b; |
return obj; |
} |
|
fz_obj * |
fz_new_int(int i) |
{ |
fz_obj *obj = fz_malloc(sizeof(fz_obj)); |
obj->refs = 1; |
obj->kind = FZ_INT; |
obj->u.i = i; |
return obj; |
} |
|
fz_obj * |
fz_new_real(float f) |
{ |
fz_obj *obj = fz_malloc(sizeof(fz_obj)); |
obj->refs = 1; |
obj->kind = FZ_REAL; |
obj->u.f = f; |
return obj; |
} |
|
fz_obj * |
fz_new_string(char *str, int len) |
{ |
fz_obj *obj = fz_malloc(offsetof(fz_obj, u.s.buf) + len + 1); |
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(char *str) |
{ |
fz_obj *obj = fz_malloc(offsetof(fz_obj, u.n) + strlen(str) + 1); |
obj->refs = 1; |
obj->kind = FZ_NAME; |
strcpy(obj->u.n, str); |
return obj; |
} |
|
fz_obj * |
fz_new_indirect(int num, int gen, void *xref) |
{ |
fz_obj *obj = fz_malloc(sizeof(fz_obj)); |
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 != NULL); |
obj->refs ++; |
return obj; |
} |
|
int fz_is_indirect(fz_obj *obj) |
{ |
return obj ? obj->kind == FZ_INDIRECT : 0; |
} |
|
int fz_is_null(fz_obj *obj) |
{ |
obj = fz_resolve_indirect(obj); |
return obj ? obj->kind == FZ_NULL : 0; |
} |
|
int fz_is_bool(fz_obj *obj) |
{ |
obj = fz_resolve_indirect(obj); |
return obj ? obj->kind == FZ_BOOL : 0; |
} |
|
int fz_is_int(fz_obj *obj) |
{ |
obj = fz_resolve_indirect(obj); |
return obj ? obj->kind == FZ_INT : 0; |
} |
|
int fz_is_real(fz_obj *obj) |
{ |
obj = fz_resolve_indirect(obj); |
return obj ? obj->kind == FZ_REAL : 0; |
} |
|
int fz_is_string(fz_obj *obj) |
{ |
obj = fz_resolve_indirect(obj); |
return obj ? obj->kind == FZ_STRING : 0; |
} |
|
int fz_is_name(fz_obj *obj) |
{ |
obj = fz_resolve_indirect(obj); |
return obj ? obj->kind == FZ_NAME : 0; |
} |
|
int fz_is_array(fz_obj *obj) |
{ |
obj = fz_resolve_indirect(obj); |
return obj ? obj->kind == FZ_ARRAY : 0; |
} |
|
int fz_is_dict(fz_obj *obj) |
{ |
obj = fz_resolve_indirect(obj); |
return obj ? obj->kind == FZ_DICT : 0; |
} |
|
int fz_to_bool(fz_obj *obj) |
{ |
obj = fz_resolve_indirect(obj); |
if (fz_is_bool(obj)) |
return obj->u.b; |
return 0; |
} |
|
int fz_to_int(fz_obj *obj) |
{ |
obj = fz_resolve_indirect(obj); |
if (fz_is_int(obj)) |
return obj->u.i; |
if (fz_is_real(obj)) |
return obj->u.f; |
return 0; |
} |
|
float fz_to_real(fz_obj *obj) |
{ |
obj = fz_resolve_indirect(obj); |
if (fz_is_real(obj)) |
return obj->u.f; |
if (fz_is_int(obj)) |
return obj->u.i; |
return 0; |
} |
|
char *fz_to_name(fz_obj *obj) |
{ |
obj = fz_resolve_indirect(obj); |
if (fz_is_name(obj)) |
return obj->u.n; |
return ""; |
} |
|
char *fz_to_str_buf(fz_obj *obj) |
{ |
obj = fz_resolve_indirect(obj); |
if (fz_is_string(obj)) |
return obj->u.s.buf; |
return ""; |
} |
|
int fz_to_str_len(fz_obj *obj) |
{ |
obj = fz_resolve_indirect(obj); |
if (fz_is_string(obj)) |
return obj->u.s.len; |
return 0; |
} |
|
/* for use by pdf_crypt_obj_imp to decrypt AES string in place */ |
void fz_set_str_len(fz_obj *obj, int newlen) |
{ |
obj = fz_resolve_indirect(obj); |
if (fz_is_string(obj)) |
if (newlen < obj->u.s.len) |
obj->u.s.len = newlen; |
} |
|
int fz_to_num(fz_obj *obj) |
{ |
if (fz_is_indirect(obj)) |
return obj->u.r.num; |
return 0; |
} |
|
int fz_to_gen(fz_obj *obj) |
{ |
if (fz_is_indirect(obj)) |
return obj->u.r.gen; |
return 0; |
} |
|
void *fz_get_indirect_xref(fz_obj *obj) |
{ |
if (fz_is_indirect(obj)) |
return obj->u.r.xref; |
return NULL; |
} |
|
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 == NULL) |
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(int initialcap) |
{ |
fz_obj *obj; |
int i; |
|
obj = fz_malloc(sizeof(fz_obj)); |
obj->refs = 1; |
obj->kind = FZ_ARRAY; |
|
obj->u.a.len = 0; |
obj->u.a.cap = initialcap > 1 ? initialcap : 6; |
|
obj->u.a.items = fz_calloc(obj->u.a.cap, sizeof(fz_obj*)); |
for (i = 0; i < obj->u.a.cap; i++) |
obj->u.a.items[i] = NULL; |
|
return obj; |
} |
|
fz_obj * |
fz_copy_array(fz_obj *obj) |
{ |
fz_obj *new; |
int i; |
|
if (fz_is_indirect(obj) || !fz_is_array(obj)) |
fz_warn("assert: not an array (%s)", fz_objkindstr(obj)); |
|
new = fz_new_array(fz_array_len(obj)); |
for (i = 0; i < fz_array_len(obj); i++) |
fz_array_push(new, fz_array_get(obj, i)); |
|
return new; |
} |
|
int |
fz_array_len(fz_obj *obj) |
{ |
obj = fz_resolve_indirect(obj); |
if (!fz_is_array(obj)) |
return 0; |
return obj->u.a.len; |
} |
|
fz_obj * |
fz_array_get(fz_obj *obj, int i) |
{ |
obj = fz_resolve_indirect(obj); |
|
if (!fz_is_array(obj)) |
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) |
{ |
obj = fz_resolve_indirect(obj); |
|
if (!fz_is_array(obj)) |
fz_warn("assert: not an array (%s)", fz_objkindstr(obj)); |
else if (i < 0) |
fz_warn("assert: index %d < 0", i); |
else if (i >= obj->u.a.len) |
fz_warn("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) |
{ |
obj = fz_resolve_indirect(obj); |
|
if (!fz_is_array(obj)) |
fz_warn("assert: not an array (%s)", fz_objkindstr(obj)); |
else |
{ |
if (obj->u.a.len + 1 > obj->u.a.cap) |
{ |
int i; |
obj->u.a.cap = (obj->u.a.cap * 3) / 2; |
obj->u.a.items = fz_realloc(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; |
} |
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) |
{ |
obj = fz_resolve_indirect(obj); |
|
if (!fz_is_array(obj)) |
fz_warn("assert: not an array (%s)", fz_objkindstr(obj)); |
else |
{ |
if (obj->u.a.len + 1 > obj->u.a.cap) |
{ |
int i; |
obj->u.a.cap = (obj->u.a.cap * 3) / 2; |
obj->u.a.items = fz_realloc(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; |
} |
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++; |
} |
} |
|
/* 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(int initialcap) |
{ |
fz_obj *obj; |
int i; |
|
obj = fz_malloc(sizeof(fz_obj)); |
obj->refs = 1; |
obj->kind = FZ_DICT; |
|
obj->u.d.sorted = 1; |
obj->u.d.len = 0; |
obj->u.d.cap = initialcap > 1 ? initialcap : 10; |
|
obj->u.d.items = fz_calloc(obj->u.d.cap, sizeof(struct keyval)); |
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; |
} |
|
fz_obj * |
fz_copy_dict(fz_obj *obj) |
{ |
fz_obj *new; |
int i; |
|
if (fz_is_indirect(obj) || !fz_is_dict(obj)) |
fz_throw("assert: not a dict (%s)", fz_objkindstr(obj)); |
|
new = fz_new_dict(fz_dict_len(obj)); |
for (i = 0; i < fz_dict_len(obj); i++) |
fz_dict_put(new, fz_dict_get_key(obj, i), fz_dict_get_val(obj, i)); |
|
return new; |
} |
|
int |
fz_dict_len(fz_obj *obj) |
{ |
obj = fz_resolve_indirect(obj); |
if (!fz_is_dict(obj)) |
return 0; |
return obj->u.d.len; |
} |
|
fz_obj * |
fz_dict_get_key(fz_obj *obj, int i) |
{ |
obj = fz_resolve_indirect(obj); |
|
if (!fz_is_dict(obj)) |
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) |
{ |
obj = fz_resolve_indirect(obj); |
|
if (!fz_is_dict(obj)) |
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) |
{ |
if (obj->u.d.sorted) |
{ |
int l = 0; |
int r = obj->u.d.len - 1; |
while (l <= r) |
{ |
int m = (l + r) >> 1; |
int c = -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; |
} |
} |
|
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; |
} |
|
return -1; |
} |
|
fz_obj * |
fz_dict_gets(fz_obj *obj, char *key) |
{ |
int i; |
|
obj = fz_resolve_indirect(obj); |
|
if (!fz_is_dict(obj)) |
return NULL; |
|
i = fz_dict_finds(obj, key); |
if (i >= 0) |
return obj->u.d.items[i].v; |
|
return NULL; |
} |
|
fz_obj * |
fz_dict_get(fz_obj *obj, fz_obj *key) |
{ |
if (fz_is_name(key)) |
return fz_dict_gets(obj, fz_to_name(key)); |
return NULL; |
} |
|
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) |
{ |
char *s; |
int i; |
|
obj = fz_resolve_indirect(obj); |
|
if (!fz_is_dict(obj)) |
{ |
fz_warn("assert: not a dict (%s)", fz_objkindstr(obj)); |
return; |
} |
|
if (fz_is_name(key)) |
s = fz_to_name(key); |
else |
{ |
fz_warn("assert: key is not a name (%s)", fz_objkindstr(obj)); |
return; |
} |
|
if (!val) |
{ |
fz_warn("assert: val does not exist for key (%s)", s); |
return; |
} |
|
i = fz_dict_finds(obj, s); |
if (i >= 0) |
{ |
fz_drop_obj(obj->u.d.items[i].v); |
obj->u.d.items[i].v = fz_keep_obj(val); |
return; |
} |
|
if (obj->u.d.len + 1 > obj->u.d.cap) |
{ |
obj->u.d.cap = (obj->u.d.cap * 3) / 2; |
obj->u.d.items = fz_realloc(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; |
} |
} |
|
/* borked! */ |
if (obj->u.d.len) |
if (strcmp(fz_to_name(obj->u.d.items[obj->u.d.len - 1].k), s) > 0) |
obj->u.d.sorted = 0; |
|
obj->u.d.items[obj->u.d.len].k = fz_keep_obj(key); |
obj->u.d.items[obj->u.d.len].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(key); |
fz_dict_put(obj, keyobj, val); |
fz_drop_obj(keyobj); |
} |
|
void |
fz_dict_dels(fz_obj *obj, char *key) |
{ |
obj = fz_resolve_indirect(obj); |
|
if (!fz_is_dict(obj)) |
fz_warn("assert: not a dict (%s)", fz_objkindstr(obj)); |
else |
{ |
int i = fz_dict_finds(obj, key); |
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) |
{ |
if (fz_is_name(key)) |
fz_dict_dels(obj, fz_to_name(key)); |
else |
fz_warn("assert: key is not a name (%s)", fz_objkindstr(obj)); |
} |
|
void |
fz_sort_dict(fz_obj *obj) |
{ |
obj = fz_resolve_indirect(obj); |
if (!fz_is_dict(obj)) |
return; |
if (!obj->u.d.sorted) |
{ |
qsort(obj->u.d.items, obj->u.d.len, sizeof(struct keyval), keyvalcmp); |
obj->u.d.sorted = 1; |
} |
} |
|
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->u.a.items); |
fz_free(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->u.d.items); |
fz_free(obj); |
} |
|
void |
fz_drop_obj(fz_obj *obj) |
{ |
assert(obj != NULL); |
if (--obj->refs == 0) |
{ |
if (obj->kind == FZ_ARRAY) |
fz_free_array(obj); |
else if (obj->kind == FZ_DICT) |
fz_free_dict(obj); |
else |
fz_free(obj); |
} |
} |