summaryrefslogtreecommitdiff
path: root/pdf/base_object.c
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2012-02-26 11:15:16 -0800
committerRobin Watts <robin@ghostscript.com>2012-02-26 19:36:30 +0000
commitbbfe635555dce16858403706e2031dd3bfa1a9f1 (patch)
tree6e414005f04f233a83dbeec5607c0665a6d498bc /pdf/base_object.c
parentca578b08dc1243dc6cbb3235272d52d9e2336925 (diff)
downloadmupdf-bbfe635555dce16858403706e2031dd3bfa1a9f1.tar.xz
Move fz_obj to be pdf_obj.
Currently, we are in the slightly strange position of having the PDF specific object types as part of fitz. Here we pull them out into the pdf layer instead. This has been made possible by the recent changes to make the store no longer be tied to having fz_obj's as keys. Most of this work is a simple huge rename; to help customers who may have code that use such functions we have provided a sed script to do the renaming; scripts/rename2.sed. Various other small tweaks are required; the store used to have some debugging code that still required knowledge of fz_obj types - we extract that into a nicer 'type' based function pointer. Also, the type 3 font handling used to have an fz_obj pointer for type 3 resources, and therefore needed to know how to free this; this has become a void * with a function to free it.
Diffstat (limited to 'pdf/base_object.c')
-rw-r--r--pdf/base_object.c1266
1 files changed, 1266 insertions, 0 deletions
diff --git a/pdf/base_object.c b/pdf/base_object.c
new file mode 100644
index 00000000..94f1acfc
--- /dev/null
+++ b/pdf/base_object.c
@@ -0,0 +1,1266 @@
+#include "fitz.h"
+#include "mupdf.h"
+
+typedef enum pdf_objkind_e
+{
+ PDF_NULL,
+ PDF_BOOL,
+ PDF_INT,
+ PDF_REAL,
+ PDF_STRING,
+ PDF_NAME,
+ PDF_ARRAY,
+ PDF_DICT,
+ PDF_INDIRECT
+} pdf_objkind;
+
+struct keyval
+{
+ pdf_obj *k;
+ pdf_obj *v;
+};
+
+struct pdf_obj_s
+{
+ int refs;
+ pdf_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;
+ pdf_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;
+};
+
+pdf_obj *
+pdf_new_null(fz_context *ctx)
+{
+ pdf_obj *obj;
+ obj = Memento_label(fz_malloc(ctx, sizeof(pdf_obj)), "pdf_obj(null)");
+ obj->ctx = ctx;
+ obj->refs = 1;
+ obj->kind = PDF_NULL;
+ return obj;
+}
+
+pdf_obj *
+pdf_new_bool(fz_context *ctx, int b)
+{
+ pdf_obj *obj;
+ obj = Memento_label(fz_malloc(ctx, sizeof(pdf_obj)), "pdf_obj(bool)");
+ obj->ctx = ctx;
+ obj->refs = 1;
+ obj->kind = PDF_BOOL;
+ obj->u.b = b;
+ return obj;
+}
+
+pdf_obj *
+pdf_new_int(fz_context *ctx, int i)
+{
+ pdf_obj *obj;
+ obj = Memento_label(fz_malloc(ctx, sizeof(pdf_obj)), "pdf_obj(int)");
+ obj->ctx = ctx;
+ obj->refs = 1;
+ obj->kind = PDF_INT;
+ obj->u.i = i;
+ return obj;
+}
+
+pdf_obj *
+pdf_new_real(fz_context *ctx, float f)
+{
+ pdf_obj *obj;
+ obj = Memento_label(fz_malloc(ctx, sizeof(pdf_obj)), "pdf_obj(real)");
+ obj->ctx = ctx;
+ obj->refs = 1;
+ obj->kind = PDF_REAL;
+ obj->u.f = f;
+ return obj;
+}
+
+pdf_obj *
+pdf_new_string(fz_context *ctx, char *str, int len)
+{
+ pdf_obj *obj;
+ obj = Memento_label(fz_malloc(ctx, offsetof(pdf_obj, u.s.buf) + len + 1), "pdf_obj(string)");
+ obj->ctx = ctx;
+ obj->refs = 1;
+ obj->kind = PDF_STRING;
+ obj->u.s.len = len;
+ memcpy(obj->u.s.buf, str, len);
+ obj->u.s.buf[len] = '\0';
+ return obj;
+}
+
+pdf_obj *
+fz_new_name(fz_context *ctx, char *str)
+{
+ pdf_obj *obj;
+ obj = Memento_label(fz_malloc(ctx, offsetof(pdf_obj, u.n) + strlen(str) + 1), "pdf_obj(name)");
+ obj->ctx = ctx;
+ obj->refs = 1;
+ obj->kind = PDF_NAME;
+ strcpy(obj->u.n, str);
+ return obj;
+}
+
+pdf_obj *
+pdf_new_indirect(fz_context *ctx, int num, int gen, void *xref)
+{
+ pdf_obj *obj;
+ obj = Memento_label(fz_malloc(ctx, sizeof(pdf_obj)), "pdf_obj(indirect)");
+ obj->ctx = ctx;
+ obj->refs = 1;
+ obj->kind = PDF_INDIRECT;
+ obj->u.r.num = num;
+ obj->u.r.gen = gen;
+ obj->u.r.xref = xref;
+ return obj;
+}
+
+pdf_obj *
+pdf_keep_obj(pdf_obj *obj)
+{
+ assert(obj);
+ obj->refs ++;
+ return obj;
+}
+
+int pdf_is_indirect(pdf_obj *obj)
+{
+ return obj ? obj->kind == PDF_INDIRECT : 0;
+}
+
+#define RESOLVE(obj) \
+ do { \
+ if (obj && obj->kind == PDF_INDIRECT) \
+ {\
+ fz_assert_lock_not_held(obj->ctx, FZ_LOCK_FILE); \
+ obj = pdf_resolve_indirect(obj); \
+ } \
+ } while (0)
+
+int pdf_is_null(pdf_obj *obj)
+{
+ RESOLVE(obj);
+ return obj ? obj->kind == PDF_NULL : 0;
+}
+
+int pdf_is_bool(pdf_obj *obj)
+{
+ RESOLVE(obj);
+ return obj ? obj->kind == PDF_BOOL : 0;
+}
+
+int pdf_is_int(pdf_obj *obj)
+{
+ RESOLVE(obj);
+ return obj ? obj->kind == PDF_INT : 0;
+}
+
+int pdf_is_real(pdf_obj *obj)
+{
+ RESOLVE(obj);
+ return obj ? obj->kind == PDF_REAL : 0;
+}
+
+int pdf_is_string(pdf_obj *obj)
+{
+ RESOLVE(obj);
+ return obj ? obj->kind == PDF_STRING : 0;
+}
+
+int pdf_is_name(pdf_obj *obj)
+{
+ RESOLVE(obj);
+ return obj ? obj->kind == PDF_NAME : 0;
+}
+
+int pdf_is_array(pdf_obj *obj)
+{
+ RESOLVE(obj);
+ return obj ? obj->kind == PDF_ARRAY : 0;
+}
+
+int pdf_is_dict(pdf_obj *obj)
+{
+ RESOLVE(obj);
+ return obj ? obj->kind == PDF_DICT : 0;
+}
+
+int pdf_to_bool(pdf_obj *obj)
+{
+ RESOLVE(obj);
+ if (!obj)
+ return 0;
+ return obj->kind == PDF_BOOL ? obj->u.b : 0;
+}
+
+int pdf_to_int(pdf_obj *obj)
+{
+ RESOLVE(obj);
+ if (!obj)
+ return 0;
+ if (obj->kind == PDF_INT)
+ return obj->u.i;
+ if (obj->kind == PDF_REAL)
+ return (int)(obj->u.f + 0.5f); /* No roundf in MSVC */
+ return 0;
+}
+
+float pdf_to_real(pdf_obj *obj)
+{
+ RESOLVE(obj);
+ if (!obj)
+ return 0;
+ if (obj->kind == PDF_REAL)
+ return obj->u.f;
+ if (obj->kind == PDF_INT)
+ return obj->u.i;
+ return 0;
+}
+
+char *pdf_to_name(pdf_obj *obj)
+{
+ RESOLVE(obj);
+ if (!obj || obj->kind != PDF_NAME)
+ return "";
+ return obj->u.n;
+}
+
+char *pdf_to_str_buf(pdf_obj *obj)
+{
+ RESOLVE(obj);
+ if (!obj || obj->kind != PDF_STRING)
+ return "";
+ return obj->u.s.buf;
+}
+
+int pdf_to_str_len(pdf_obj *obj)
+{
+ RESOLVE(obj);
+ if (!obj || obj->kind != PDF_STRING)
+ return 0;
+ return obj->u.s.len;
+}
+
+/* for use by pdf_crypt_obj_imp to decrypt AES string in place */
+void pdf_set_str_len(pdf_obj *obj, int newlen)
+{
+ RESOLVE(obj);
+ if (!obj || obj->kind != PDF_STRING)
+ return; /* This should never happen */
+ if (newlen > obj->u.s.len)
+ return; /* This should never happen */
+ obj->u.s.len = newlen;
+}
+
+pdf_obj *pdf_to_dict(pdf_obj *obj)
+{
+ RESOLVE(obj);
+ return (obj && obj->kind == PDF_DICT ? obj : NULL);
+}
+
+int pdf_to_num(pdf_obj *obj)
+{
+ if (!obj || obj->kind != PDF_INDIRECT)
+ return 0;
+ return obj->u.r.num;
+}
+
+int pdf_to_gen(pdf_obj *obj)
+{
+ if (!obj || obj->kind != PDF_INDIRECT)
+ return 0;
+ return obj->u.r.gen;
+}
+
+void *pdf_get_indirect_document(pdf_obj *obj)
+{
+ if (!obj || obj->kind != PDF_INDIRECT)
+ return NULL;
+ return obj->u.r.xref;
+}
+
+int
+pdf_objcmp(pdf_obj *a, pdf_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 PDF_NULL:
+ return 0;
+
+ case PDF_BOOL:
+ return a->u.b - b->u.b;
+
+ case PDF_INT:
+ return a->u.i - b->u.i;
+
+ case PDF_REAL:
+ if (a->u.f < b->u.f)
+ return -1;
+ if (a->u.f > b->u.f)
+ return 1;
+ return 0;
+
+ case PDF_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 PDF_NAME:
+ return strcmp(a->u.n, b->u.n);
+
+ case PDF_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 PDF_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 (pdf_objcmp(a->u.a.items[i], b->u.a.items[i]))
+ return 1;
+ return 0;
+
+ case PDF_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 (pdf_objcmp(a->u.d.items[i].k, b->u.d.items[i].k))
+ return 1;
+ if (pdf_objcmp(a->u.d.items[i].v, b->u.d.items[i].v))
+ return 1;
+ }
+ return 0;
+
+ }
+ return 1;
+}
+
+static char *
+pdf_objkindstr(pdf_obj *obj)
+{
+ if (!obj)
+ return "<NULL>";
+ switch (obj->kind)
+ {
+ case PDF_NULL: return "null";
+ case PDF_BOOL: return "boolean";
+ case PDF_INT: return "integer";
+ case PDF_REAL: return "real";
+ case PDF_STRING: return "string";
+ case PDF_NAME: return "name";
+ case PDF_ARRAY: return "array";
+ case PDF_DICT: return "dictionary";
+ case PDF_INDIRECT: return "reference";
+ }
+ return "<unknown>";
+}
+
+pdf_obj *
+pdf_new_array(fz_context *ctx, int initialcap)
+{
+ pdf_obj *obj;
+ int i;
+
+ obj = Memento_label(fz_malloc(ctx, sizeof(pdf_obj)), "pdf_obj(array)");
+ obj->ctx = ctx;
+ obj->refs = 1;
+ obj->kind = PDF_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(pdf_obj*)), "pdf_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
+pdf_array_grow(pdf_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(pdf_obj*));
+
+ for (i = obj->u.a.len ; i < obj->u.a.cap; i++)
+ obj->u.a.items[i] = NULL;
+}
+
+pdf_obj *
+pdf_copy_array(fz_context *ctx, pdf_obj *obj)
+{
+ pdf_obj *arr;
+ int i;
+ int n;
+
+ RESOLVE(obj);
+ if (!obj || obj->kind != PDF_ARRAY)
+ fz_warn(ctx, "assert: not an array (%s)", pdf_objkindstr(obj));
+
+ arr = pdf_new_array(ctx, pdf_array_len(obj));
+ n = pdf_array_len(obj);
+ for (i = 0; i < n; i++)
+ pdf_array_push(arr, pdf_array_get(obj, i));
+
+ return arr;
+}
+
+int
+pdf_array_len(pdf_obj *obj)
+{
+ RESOLVE(obj);
+ if (!obj || obj->kind != PDF_ARRAY)
+ return 0;
+ return obj->u.a.len;
+}
+
+pdf_obj *
+pdf_array_get(pdf_obj *obj, int i)
+{
+ RESOLVE(obj);
+
+ if (!obj || obj->kind != PDF_ARRAY)
+ return NULL;
+
+ if (i < 0 || i >= obj->u.a.len)
+ return NULL;
+
+ return obj->u.a.items[i];
+}
+
+void
+pdf_array_put(pdf_obj *obj, int i, pdf_obj *item)
+{
+ RESOLVE(obj);
+
+ if (!obj)
+ return; /* Can't warn :( */
+ if (obj->kind != PDF_ARRAY)
+ fz_warn(obj->ctx, "assert: not an array (%s)", pdf_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])
+ pdf_drop_obj(obj->u.a.items[i]);
+ obj->u.a.items[i] = pdf_keep_obj(item);
+ }
+}
+
+void
+pdf_array_push(pdf_obj *obj, pdf_obj *item)
+{
+ RESOLVE(obj);
+
+ if (!obj)
+ return; /* Can't warn :( */
+ if (obj->kind != PDF_ARRAY)
+ fz_warn(obj->ctx, "assert: not an array (%s)", pdf_objkindstr(obj));
+ else
+ {
+ if (obj->u.a.len + 1 > obj->u.a.cap)
+ pdf_array_grow(obj);
+ obj->u.a.items[obj->u.a.len] = pdf_keep_obj(item);
+ obj->u.a.len++;
+ }
+}
+
+void
+pdf_array_insert(pdf_obj *obj, pdf_obj *item)
+{
+ RESOLVE(obj);
+
+ if (!obj)
+ return; /* Can't warn :( */
+ if (obj->kind != PDF_ARRAY)
+ fz_warn(obj->ctx, "assert: not an array (%s)", pdf_objkindstr(obj));
+ else
+ {
+ if (obj->u.a.len + 1 > obj->u.a.cap)
+ pdf_array_grow(obj);
+ memmove(obj->u.a.items + 1, obj->u.a.items, obj->u.a.len * sizeof(pdf_obj*));
+ obj->u.a.items[0] = pdf_keep_obj(item);
+ obj->u.a.len++;
+ }
+}
+
+int
+pdf_array_contains(pdf_obj *arr, pdf_obj *obj)
+{
+ int i;
+
+ for (i = 0; i < pdf_array_len(arr); i++)
+ if (!pdf_objcmp(pdf_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(pdf_to_name(a->k), pdf_to_name(b->k));
+}
+
+pdf_obj *
+pdf_new_dict(fz_context *ctx, int initialcap)
+{
+ pdf_obj *obj;
+ int i;
+
+ obj = Memento_label(fz_malloc(ctx, sizeof(pdf_obj)), "pdf_obj(dict)");
+ obj->ctx = ctx;
+ obj->refs = 1;
+ obj->kind = PDF_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)), "pdf_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
+pdf_dict_grow(pdf_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;
+ }
+}
+
+pdf_obj *
+pdf_copy_dict(fz_context *ctx, pdf_obj *obj)
+{
+ pdf_obj *dict;
+ int i, n;
+
+ RESOLVE(obj);
+ if (obj && obj->kind != PDF_DICT)
+ fz_warn(ctx, "assert: not a dict (%s)", pdf_objkindstr(obj));
+
+ n = pdf_dict_len(obj);
+ dict = pdf_new_dict(ctx, n);
+ for (i = 0; i < n; i++)
+ fz_dict_put(dict, pdf_dict_get_key(obj, i), pdf_dict_get_val(obj, i));
+
+ return dict;
+}
+
+int
+pdf_dict_len(pdf_obj *obj)
+{
+ RESOLVE(obj);
+ if (!obj || obj->kind != PDF_DICT)
+ return 0;
+ return obj->u.d.len;
+}
+
+pdf_obj *
+pdf_dict_get_key(pdf_obj *obj, int i)
+{
+ RESOLVE(obj);
+ if (!obj || obj->kind != PDF_DICT)
+ return NULL;
+
+ if (i < 0 || i >= obj->u.d.len)
+ return NULL;
+
+ return obj->u.d.items[i].k;
+}
+
+pdf_obj *
+pdf_dict_get_val(pdf_obj *obj, int i)
+{
+ RESOLVE(obj);
+ if (!obj || obj->kind != PDF_DICT)
+ return NULL;
+
+ if (i < 0 || i >= obj->u.d.len)
+ return NULL;
+
+ return obj->u.d.items[i].v;
+}
+
+static int
+pdf_dict_finds(pdf_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(pdf_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(pdf_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(pdf_to_name(obj->u.d.items[i].k), key) == 0)
+ return i;
+
+ if (location)
+ *location = obj->u.d.len;
+ }
+
+ return -1;
+}
+
+pdf_obj *
+pdf_dict_gets(pdf_obj *obj, char *key)
+{
+ int i;
+
+ RESOLVE(obj);
+ if (!obj || obj->kind != PDF_DICT)
+ return NULL;
+
+ i = pdf_dict_finds(obj, key, NULL);
+ if (i >= 0)
+ return obj->u.d.items[i].v;
+
+ return NULL;
+}
+
+pdf_obj *
+pdf_dict_get(pdf_obj *obj, pdf_obj *key)
+{
+ if (!key || key->kind != PDF_NAME)
+ return NULL;
+ return pdf_dict_gets(obj, pdf_to_name(key));
+}
+
+pdf_obj *
+pdf_dict_getsa(pdf_obj *obj, char *key, char *abbrev)
+{
+ pdf_obj *v;
+ v = pdf_dict_gets(obj, key);
+ if (v)
+ return v;
+ return pdf_dict_gets(obj, abbrev);
+}
+
+void
+fz_dict_put(pdf_obj *obj, pdf_obj *key, pdf_obj *val)
+{
+ int location;
+ char *s;
+ int i;
+
+ RESOLVE(obj);
+ if (!obj || obj->kind != PDF_DICT)
+ {
+ fz_warn(obj->ctx, "assert: not a dict (%s)", pdf_objkindstr(obj));
+ return;
+ }
+
+ RESOLVE(key);
+ if (!key || key->kind != PDF_NAME)
+ {
+ fz_warn(obj->ctx, "assert: key is not a name (%s)", pdf_objkindstr(obj));
+ return;
+ }
+ else
+ s = pdf_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)
+ pdf_sort_dict(obj);
+
+ i = pdf_dict_finds(obj, s, &location);
+ if (i >= 0 && i < obj->u.d.len)
+ {
+ pdf_drop_obj(obj->u.d.items[i].v);
+ obj->u.d.items[i].v = pdf_keep_obj(val);
+ }
+ else
+ {
+ if (obj->u.d.len + 1 > obj->u.d.cap)
+ pdf_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 = pdf_keep_obj(key);
+ obj->u.d.items[i].v = pdf_keep_obj(val);
+ obj->u.d.len ++;
+ }
+}
+
+void
+pdf_dict_puts(pdf_obj *obj, char *key, pdf_obj *val)
+{
+ pdf_obj *keyobj = fz_new_name(obj->ctx, key);
+ fz_dict_put(obj, keyobj, val);
+ pdf_drop_obj(keyobj);
+}
+
+void
+pdf_dict_dels(pdf_obj *obj, char *key)
+{
+ RESOLVE(obj);
+
+ if (!obj || obj->kind != PDF_DICT)
+ fz_warn(obj->ctx, "assert: not a dict (%s)", pdf_objkindstr(obj));
+ else
+ {
+ int i = pdf_dict_finds(obj, key, NULL);
+ if (i >= 0)
+ {
+ pdf_drop_obj(obj->u.d.items[i].k);
+ pdf_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
+pdf_dict_del(pdf_obj *obj, pdf_obj *key)
+{
+ RESOLVE(key);
+ if (!key || key->kind != PDF_NAME)
+ fz_warn(obj->ctx, "assert: key is not a name (%s)", pdf_objkindstr(obj));
+ else
+ pdf_dict_dels(obj, key->u.n);
+}
+
+void
+pdf_sort_dict(pdf_obj *obj)
+{
+ RESOLVE(obj);
+ if (!obj || obj->kind != PDF_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
+pdf_dict_marked(pdf_obj *obj)
+{
+ RESOLVE(obj);
+ if (!obj || obj->kind != PDF_DICT)
+ return 0;
+ return obj->u.d.marked;
+}
+
+int
+pdf_dict_mark(pdf_obj *obj)
+{
+ int marked;
+ RESOLVE(obj);
+ if (!obj || obj->kind != PDF_DICT)
+ return 0;
+ marked = obj->u.d.marked;
+ obj->u.d.marked = 1;
+ return marked;
+}
+
+void
+pdf_dict_unmark(pdf_obj *obj)
+{
+ RESOLVE(obj);
+ if (!obj || obj->kind != PDF_DICT)
+ return;
+ obj->u.d.marked = 0;
+}
+
+static void
+pdf_free_array(pdf_obj *obj)
+{
+ int i;
+
+ for (i = 0; i < obj->u.a.len; i++)
+ if (obj->u.a.items[i])
+ pdf_drop_obj(obj->u.a.items[i]);
+
+ fz_free(obj->ctx, obj->u.a.items);
+ fz_free(obj->ctx, obj);
+}
+
+static void
+pdf_free_dict(pdf_obj *obj)
+{
+ int i;
+
+ for (i = 0; i < obj->u.d.len; i++) {
+ if (obj->u.d.items[i].k)
+ pdf_drop_obj(obj->u.d.items[i].k);
+ if (obj->u.d.items[i].v)
+ pdf_drop_obj(obj->u.d.items[i].v);
+ }
+
+ fz_free(obj->ctx, obj->u.d.items);
+ fz_free(obj->ctx, obj);
+}
+
+void
+pdf_drop_obj(pdf_obj *obj)
+{
+ if (!obj)
+ return;
+ if (--obj->refs)
+ return;
+ if (obj->kind == PDF_ARRAY)
+ pdf_free_array(obj);
+ else if (obj->kind == PDF_DICT)
+ pdf_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, pdf_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, pdf_obj *obj)
+{
+ char *s = pdf_to_str_buf(obj);
+ int n = pdf_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, pdf_obj *obj)
+{
+ char *s = pdf_to_str_buf(obj);
+ int n = pdf_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, pdf_obj *obj)
+{
+ unsigned char *s = (unsigned char *) pdf_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, pdf_obj *obj)
+{
+ int i, n;
+
+ n = pdf_array_len(obj);
+ if (fmt->tight) {
+ fmt_putc(fmt, '[');
+ for (i = 0; i < n; i++) {
+ fmt_obj(fmt, pdf_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, pdf_array_get(obj, i));
+ fmt_putc(fmt, ' ');
+ }
+ fmt_putc(fmt, ']');
+ fmt_sep(fmt);
+ }
+}
+
+static void fmt_dict(struct fmt *fmt, pdf_obj *obj)
+{
+ int i, n;
+ pdf_obj *key, *val;
+
+ n = pdf_dict_len(obj);
+ if (fmt->tight) {
+ fmt_puts(fmt, "<<");
+ for (i = 0; i < n; i++) {
+ fmt_obj(fmt, pdf_dict_get_key(obj, i));
+ fmt_sep(fmt);
+ fmt_obj(fmt, pdf_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 = pdf_dict_get_key(obj, i);
+ val = pdf_dict_get_val(obj, i);
+ fmt_indent(fmt);
+ fmt_obj(fmt, key);
+ fmt_putc(fmt, ' ');
+ if (!pdf_is_indirect(val) && pdf_is_array(val))
+ fmt->indent ++;
+ fmt_obj(fmt, val);
+ fmt_putc(fmt, '\n');
+ if (!pdf_is_indirect(val) && pdf_is_array(val))
+ fmt->indent --;
+ }
+ fmt->indent --;
+ fmt_indent(fmt);
+ fmt_puts(fmt, ">>");
+ }
+}
+
+static void fmt_obj(struct fmt *fmt, pdf_obj *obj)
+{
+ char buf[256];
+
+ if (!obj)
+ fmt_puts(fmt, "<NULL>");
+ else if (pdf_is_indirect(obj))
+ {
+ sprintf(buf, "%d %d R", pdf_to_num(obj), pdf_to_gen(obj));
+ fmt_puts(fmt, buf);
+ }
+ else if (pdf_is_null(obj))
+ fmt_puts(fmt, "null");
+ else if (pdf_is_bool(obj))
+ fmt_puts(fmt, pdf_to_bool(obj) ? "true" : "false");
+ else if (pdf_is_int(obj))
+ {
+ sprintf(buf, "%d", pdf_to_int(obj));
+ fmt_puts(fmt, buf);
+ }
+ else if (pdf_is_real(obj))
+ {
+ sprintf(buf, "%g", pdf_to_real(obj));
+ if (strchr(buf, 'e')) /* bad news! */
+ sprintf(buf, fabsf(pdf_to_real(obj)) > 1 ? "%1.1f" : "%1.8f", pdf_to_real(obj));
+ fmt_puts(fmt, buf);
+ }
+ else if (pdf_is_string(obj))
+ {
+ char *str = pdf_to_str_buf(obj);
+ int len = pdf_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 (pdf_is_name(obj))
+ fmt_name(fmt, obj);
+ else if (pdf_is_array(obj))
+ fmt_array(fmt, obj);
+ else if (pdf_is_dict(obj))
+ fmt_dict(fmt, obj);
+ else
+ fmt_puts(fmt, "<unknown object>");
+}
+
+static int
+pdf_sprint_obj(char *s, int n, pdf_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
+pdf_fprint_obj(FILE *fp, pdf_obj *obj, int tight)
+{
+ char buf[1024];
+ char *ptr;
+ int n;
+
+ n = pdf_sprint_obj(NULL, 0, obj, tight);
+ if ((n + 1) < sizeof buf)
+ {
+ pdf_sprint_obj(buf, sizeof buf, obj, tight);
+ fputs(buf, fp);
+ fputc('\n', fp);
+ }
+ else
+ {
+ ptr = fz_malloc(obj->ctx, n + 1);
+ pdf_sprint_obj(ptr, n + 1, obj, tight);
+ fputs(ptr, fp);
+ fputc('\n', fp);
+ fz_free(obj->ctx, ptr);
+ }
+ return n;
+}
+
+void
+pdf_debug_obj(pdf_obj *obj)
+{
+ pdf_fprint_obj(stdout, obj, 0);
+}
+
+void
+pdf_debug_ref(pdf_obj *ref)
+{
+ pdf_debug_obj(pdf_resolve_indirect(ref));
+}