eBookReaderSwitch/mupdf/source/pdf/pdf-object.c

2440 lines
51 KiB
C
Raw Normal View History

#include "mupdf/fitz.h"
#include "mupdf/pdf.h"
#include "../fitz/fitz-imp.h"
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#define PDF_MAKE_NAME(STRING,NAME) STRING,
static const char *PDF_NAME_LIST[] = {
"", "", "", /* dummy slots for null, true, and false */
#include "mupdf/pdf/name-table.h"
};
#undef PDF_MAKE_NAME
typedef enum pdf_objkind_e
{
PDF_INT = 'i',
PDF_REAL = 'f',
PDF_STRING = 's',
PDF_NAME = 'n',
PDF_ARRAY = 'a',
PDF_DICT = 'd',
PDF_INDIRECT = 'r'
} pdf_objkind;
struct keyval
{
pdf_obj *k;
pdf_obj *v;
};
enum
{
PDF_FLAGS_MARKED = 1,
PDF_FLAGS_SORTED = 2,
PDF_FLAGS_DIRTY = 4,
PDF_FLAGS_MEMO_BASE = 8,
PDF_FLAGS_MEMO_BASE_BOOL = 16
};
struct pdf_obj_s
{
short refs;
unsigned char kind;
unsigned char flags;
};
typedef struct pdf_obj_num_s
{
pdf_obj super;
union
{
int64_t i;
float f;
} u;
} pdf_obj_num;
typedef struct pdf_obj_string_s
{
pdf_obj super;
char *text; /* utf8 encoded text string */
unsigned int len;
char buf[1];
} pdf_obj_string;
typedef struct pdf_obj_name_s
{
pdf_obj super;
char n[1];
} pdf_obj_name;
typedef struct pdf_obj_array_s
{
pdf_obj super;
pdf_document *doc;
int parent_num;
int len;
int cap;
pdf_obj **items;
} pdf_obj_array;
typedef struct pdf_obj_dict_s
{
pdf_obj super;
pdf_document *doc;
int parent_num;
int len;
int cap;
struct keyval *items;
} pdf_obj_dict;
typedef struct pdf_obj_ref_s
{
pdf_obj super;
pdf_document *doc; /* Only needed for arrays, dicts and indirects */
int num;
int gen;
} pdf_obj_ref;
#define NAME(obj) ((pdf_obj_name *)(obj))
#define NUM(obj) ((pdf_obj_num *)(obj))
#define STRING(obj) ((pdf_obj_string *)(obj))
#define DICT(obj) ((pdf_obj_dict *)(obj))
#define ARRAY(obj) ((pdf_obj_array *)(obj))
#define REF(obj) ((pdf_obj_ref *)(obj))
pdf_obj *
pdf_new_int(fz_context *ctx, int64_t i)
{
pdf_obj_num *obj;
obj = Memento_label(fz_malloc(ctx, sizeof(pdf_obj_num)), "pdf_obj(int)");
obj->super.refs = 1;
obj->super.kind = PDF_INT;
obj->super.flags = 0;
obj->u.i = i;
return &obj->super;
}
pdf_obj *
pdf_new_real(fz_context *ctx, float f)
{
pdf_obj_num *obj;
obj = Memento_label(fz_malloc(ctx, sizeof(pdf_obj_num)), "pdf_obj(real)");
obj->super.refs = 1;
obj->super.kind = PDF_REAL;
obj->super.flags = 0;
obj->u.f = f;
return &obj->super;
}
pdf_obj *
pdf_new_string(fz_context *ctx, const char *str, size_t len)
{
pdf_obj_string *obj;
unsigned int l = (unsigned int)len;
if ((size_t)l != len)
fz_throw(ctx, FZ_ERROR_GENERIC, "Overflow in pdf string");
obj = Memento_label(fz_malloc(ctx, offsetof(pdf_obj_string, buf) + len + 1), "pdf_obj(string)");
obj->super.refs = 1;
obj->super.kind = PDF_STRING;
obj->super.flags = 0;
obj->text = NULL;
obj->len = l;
memcpy(obj->buf, str, len);
obj->buf[len] = '\0';
return &obj->super;
}
pdf_obj *
pdf_new_name(fz_context *ctx, const char *str)
{
pdf_obj_name *obj;
int l = 3; /* skip dummy slots */
int r = nelem(PDF_NAME_LIST) - 1;
while (l <= r)
{
int m = (l + r) >> 1;
int c = strcmp(str, PDF_NAME_LIST[m]);
if (c < 0)
r = m - 1;
else if (c > 0)
l = m + 1;
else
return (pdf_obj*)(intptr_t)m;
}
obj = Memento_label(fz_malloc(ctx, offsetof(pdf_obj_name, n) + strlen(str) + 1), "pdf_obj(name)");
obj->super.refs = 1;
obj->super.kind = PDF_NAME;
obj->super.flags = 0;
strcpy(obj->n, str);
return &obj->super;
}
pdf_obj *
pdf_new_indirect(fz_context *ctx, pdf_document *doc, int num, int gen)
{
pdf_obj_ref *obj;
if (num < 0 || num > PDF_MAX_OBJECT_NUMBER)
fz_throw(ctx, FZ_ERROR_SYNTAX, "invalid object number (%d)", num);
if (gen < 0 || gen > PDF_MAX_GEN_NUMBER)
fz_throw(ctx, FZ_ERROR_SYNTAX, "invalid generation number (%d)", gen);
obj = Memento_label(fz_malloc(ctx, sizeof(pdf_obj_ref)), "pdf_obj(indirect)");
obj->super.refs = 1;
obj->super.kind = PDF_INDIRECT;
obj->super.flags = 0;
obj->doc = doc;
obj->num = num;
obj->gen = gen;
return &obj->super;
}
#define OBJ_IS_NULL(obj) (obj == PDF_NULL)
#define OBJ_IS_BOOL(obj) (obj == PDF_TRUE || obj == PDF_FALSE)
#define OBJ_IS_NAME(obj) ((obj > PDF_FALSE && obj < PDF_LIMIT) || (obj >= PDF_LIMIT && obj->kind == PDF_NAME))
#define OBJ_IS_INT(obj) \
(obj >= PDF_LIMIT && obj->kind == PDF_INT)
#define OBJ_IS_REAL(obj) \
(obj >= PDF_LIMIT && obj->kind == PDF_REAL)
#define OBJ_IS_NUMBER(obj) \
(obj >= PDF_LIMIT && (obj->kind == PDF_REAL || obj->kind == PDF_INT))
#define OBJ_IS_STRING(obj) \
(obj >= PDF_LIMIT && obj->kind == PDF_STRING)
#define OBJ_IS_ARRAY(obj) \
(obj >= PDF_LIMIT && obj->kind == PDF_ARRAY)
#define OBJ_IS_DICT(obj) \
(obj >= PDF_LIMIT && obj->kind == PDF_DICT)
#define OBJ_IS_INDIRECT(obj) \
(obj >= PDF_LIMIT && obj->kind == PDF_INDIRECT)
#define RESOLVE(obj) \
if (OBJ_IS_INDIRECT(obj)) \
obj = pdf_resolve_indirect_chain(ctx, obj); \
int pdf_is_indirect(fz_context *ctx, pdf_obj *obj)
{
return OBJ_IS_INDIRECT(obj);
}
int pdf_is_null(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
return OBJ_IS_NULL(obj);
}
int pdf_is_bool(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
return OBJ_IS_BOOL(obj);
}
int pdf_is_int(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
return OBJ_IS_INT(obj);
}
int pdf_is_real(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
return OBJ_IS_REAL(obj);
}
int pdf_is_number(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
return OBJ_IS_NUMBER(obj);
}
int pdf_is_string(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
return OBJ_IS_STRING(obj);
}
int pdf_is_name(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
return OBJ_IS_NAME(obj);
}
int pdf_is_array(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
return OBJ_IS_ARRAY(obj);
}
int pdf_is_dict(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
return OBJ_IS_DICT(obj);
}
/* safe, silent failure, no error reporting on type mismatches */
int pdf_to_bool(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
return obj == PDF_TRUE;
}
int pdf_to_int(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
if (obj < PDF_LIMIT)
return 0;
if (obj->kind == PDF_INT)
return (int)NUM(obj)->u.i;
if (obj->kind == PDF_REAL)
return (int)(NUM(obj)->u.f + 0.5f); /* No roundf in MSVC */
return 0;
}
int64_t pdf_to_int64(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
if (obj < PDF_LIMIT)
return 0;
if (obj->kind == PDF_INT)
return NUM(obj)->u.i;
if (obj->kind == PDF_REAL)
return (NUM(obj)->u.f + 0.5f); /* No roundf in MSVC */
return 0;
}
float pdf_to_real(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
if (obj < PDF_LIMIT)
return 0;
if (obj->kind == PDF_REAL)
return NUM(obj)->u.f;
if (obj->kind == PDF_INT)
return NUM(obj)->u.i;
return 0;
}
const char *pdf_to_name(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
if (obj < PDF_LIMIT)
return PDF_NAME_LIST[((intptr_t)obj)];
if (obj->kind == PDF_NAME)
return NAME(obj)->n;
return "";
}
char *pdf_to_str_buf(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
if (OBJ_IS_STRING(obj))
return STRING(obj)->buf;
return "";
}
int pdf_to_str_len(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
if (OBJ_IS_STRING(obj))
return STRING(obj)->len;
return 0;
}
const char *pdf_to_string(fz_context *ctx, pdf_obj *obj, size_t *sizep)
{
RESOLVE(obj);
if (OBJ_IS_STRING(obj))
{
if (sizep)
*sizep = STRING(obj)->len;
return STRING(obj)->buf;
}
if (sizep)
*sizep = 0;
return "";
}
const char *pdf_to_text_string(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
if (OBJ_IS_STRING(obj))
{
if (!STRING(obj)->text)
STRING(obj)->text = pdf_new_utf8_from_pdf_string(ctx, STRING(obj)->buf, STRING(obj)->len);
return STRING(obj)->text;
}
return "";
}
void pdf_set_int(fz_context *ctx, pdf_obj *obj, int64_t i)
{
if (OBJ_IS_INT(obj))
NUM(obj)->u.i = i;
}
/* for use by pdf_crypt_obj_imp to decrypt AES string in place */
void pdf_set_str_len(fz_context *ctx, pdf_obj *obj, int newlen)
{
RESOLVE(obj);
if (!OBJ_IS_STRING(obj))
return; /* This should never happen */
if (newlen < 0 || (unsigned int)newlen > STRING(obj)->len)
return; /* This should never happen */
STRING(obj)->buf[newlen] = 0;
STRING(obj)->len = newlen;
}
int pdf_to_num(fz_context *ctx, pdf_obj *obj)
{
if (OBJ_IS_INDIRECT(obj))
return REF(obj)->num;
return 0;
}
int pdf_to_gen(fz_context *ctx, pdf_obj *obj)
{
if (OBJ_IS_INDIRECT(obj))
return REF(obj)->gen;
return 0;
}
pdf_document *pdf_get_indirect_document(fz_context *ctx, pdf_obj *obj)
{
if (OBJ_IS_INDIRECT(obj))
return REF(obj)->doc;
return NULL;
}
pdf_document *pdf_get_bound_document(fz_context *ctx, pdf_obj *obj)
{
if (obj < PDF_LIMIT)
return NULL;
if (obj->kind == PDF_INDIRECT)
return REF(obj)->doc;
if (obj->kind == PDF_ARRAY)
return ARRAY(obj)->doc;
if (obj->kind == PDF_DICT)
return DICT(obj)->doc;
return NULL;
}
int pdf_objcmp_resolve(fz_context *ctx, pdf_obj *a, pdf_obj *b)
{
RESOLVE(a);
RESOLVE(b);
return pdf_objcmp(ctx, a, b);
}
int
pdf_objcmp(fz_context *ctx, pdf_obj *a, pdf_obj *b)
{
int i;
if (a == b)
return 0;
/* a or b is null, true, or false */
if (a <= PDF_FALSE || b <= PDF_FALSE)
return 1;
/* a is a constant name */
if (a < PDF_LIMIT)
{
if (b < PDF_LIMIT)
return a != b;
if (b->kind != PDF_NAME)
return 1;
return strcmp(PDF_NAME_LIST[(intptr_t)a], NAME(b)->n);
}
/* b is a constant name */
if (b < PDF_LIMIT)
{
if (a->kind != PDF_NAME)
return 1;
return strcmp(NAME(a)->n, PDF_NAME_LIST[(intptr_t)b]);
}
/* both a and b are allocated objects */
if (a->kind != b->kind)
return 1;
switch (a->kind)
{
case PDF_INT:
return NUM(a)->u.i - NUM(b)->u.i;
case PDF_REAL:
if (NUM(a)->u.f < NUM(b)->u.f)
return -1;
if (NUM(a)->u.f > NUM(b)->u.f)
return 1;
return 0;
case PDF_STRING:
if (STRING(a)->len < STRING(b)->len)
{
if (memcmp(STRING(a)->buf, STRING(b)->buf, STRING(a)->len) <= 0)
return -1;
return 1;
}
if (STRING(a)->len > STRING(b)->len)
{
if (memcmp(STRING(a)->buf, STRING(b)->buf, STRING(b)->len) >= 0)
return 1;
return -1;
}
return memcmp(STRING(a)->buf, STRING(b)->buf, STRING(a)->len);
case PDF_NAME:
return strcmp(NAME(a)->n, NAME(b)->n);
case PDF_INDIRECT:
if (REF(a)->num == REF(b)->num)
return REF(a)->gen - REF(b)->gen;
return REF(a)->num - REF(b)->num;
case PDF_ARRAY:
if (ARRAY(a)->len != ARRAY(b)->len)
return ARRAY(a)->len - ARRAY(b)->len;
for (i = 0; i < ARRAY(a)->len; i++)
if (pdf_objcmp(ctx, ARRAY(a)->items[i], ARRAY(b)->items[i]))
return 1;
return 0;
case PDF_DICT:
if (DICT(a)->len != DICT(b)->len)
return DICT(a)->len - DICT(b)->len;
for (i = 0; i < DICT(a)->len; i++)
{
if (pdf_objcmp(ctx, DICT(a)->items[i].k, DICT(b)->items[i].k))
return 1;
if (pdf_objcmp(ctx, DICT(a)->items[i].v, DICT(b)->items[i].v))
return 1;
}
return 0;
}
return 1;
}
int pdf_name_eq(fz_context *ctx, pdf_obj *a, pdf_obj *b)
{
RESOLVE(a);
RESOLVE(b);
if (a <= PDF_FALSE || b <= PDF_FALSE)
return 0;
if (a < PDF_LIMIT || b < PDF_LIMIT)
return (a == b);
if (a->kind == PDF_NAME && b->kind == PDF_NAME)
return !strcmp(NAME(a)->n, NAME(b)->n);
return 0;
}
static char *
pdf_objkindstr(pdf_obj *obj)
{
if (obj == PDF_NULL)
return "null";
if (obj == PDF_TRUE || obj == PDF_FALSE)
return "boolean";
if (obj < PDF_LIMIT)
return "name";
switch (obj->kind)
{
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, pdf_document *doc, int initialcap)
{
pdf_obj_array *obj;
int i;
obj = Memento_label(fz_malloc(ctx, sizeof(pdf_obj_array)), "pdf_obj(array)");
obj->super.refs = 1;
obj->super.kind = PDF_ARRAY;
obj->super.flags = 0;
obj->doc = doc;
obj->parent_num = 0;
obj->len = 0;
obj->cap = initialcap > 1 ? initialcap : 6;
fz_try(ctx)
{
obj->items = fz_malloc_array(ctx, obj->cap, pdf_obj*);
}
fz_catch(ctx)
{
fz_free(ctx, obj);
fz_rethrow(ctx);
}
for (i = 0; i < obj->cap; i++)
obj->items[i] = NULL;
return &obj->super;
}
static void
pdf_array_grow(fz_context *ctx, pdf_obj_array *obj)
{
int i;
int new_cap = (obj->cap * 3) / 2;
obj->items = fz_realloc_array(ctx, obj->items, new_cap, pdf_obj*);
obj->cap = new_cap;
for (i = obj->len ; i < obj->cap; i++)
obj->items[i] = NULL;
}
pdf_obj *
pdf_copy_array(fz_context *ctx, pdf_obj *obj)
{
pdf_document *doc;
pdf_obj *arr;
int i;
int n;
RESOLVE(obj);
if (!OBJ_IS_ARRAY(obj))
fz_throw(ctx, FZ_ERROR_GENERIC, "not an array (%s)", pdf_objkindstr(obj));
doc = ARRAY(obj)->doc;
n = pdf_array_len(ctx, obj);
arr = pdf_new_array(ctx, doc, n);
fz_try(ctx)
for (i = 0; i < n; i++)
pdf_array_push(ctx, arr, pdf_array_get(ctx, obj, i));
fz_catch(ctx)
{
pdf_drop_obj(ctx, arr);
fz_rethrow(ctx);
}
return arr;
}
int
pdf_array_len(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
if (!OBJ_IS_ARRAY(obj))
return 0;
return ARRAY(obj)->len;
}
pdf_obj *
pdf_array_get(fz_context *ctx, pdf_obj *obj, int i)
{
RESOLVE(obj);
if (!OBJ_IS_ARRAY(obj))
return NULL;
if (i < 0 || i >= ARRAY(obj)->len)
return NULL;
return ARRAY(obj)->items[i];
}
static void prepare_object_for_alteration(fz_context *ctx, pdf_obj *obj, pdf_obj *val)
{
pdf_document *doc, *val_doc;
int parent;
/*
obj should be a dict or an array. We don't care about
any other types, as they aren't 'containers'.
*/
if (obj < PDF_LIMIT)
return;
switch (obj->kind)
{
case PDF_DICT:
doc = DICT(obj)->doc;
parent = DICT(obj)->parent_num;
break;
case PDF_ARRAY:
doc = ARRAY(obj)->doc;
parent = ARRAY(obj)->parent_num;
break;
default:
return;
}
if (val)
{
val_doc = pdf_get_bound_document(ctx, val);
if (doc && val_doc && val_doc != doc)
fz_throw(ctx, FZ_ERROR_GENERIC, "container and item belong to different documents");
}
/*
parent_num == 0 while an object is being parsed from the file.
No further action is necessary.
*/
if (parent == 0 || doc->save_in_progress || doc->repair_attempted)
return;
/*
Otherwise we need to ensure that the containing hierarchy of objects
has been moved to the incremental xref section and the newly linked
object needs to record the parent_num
*/
pdf_xref_ensure_incremental_object(ctx, doc, parent);
pdf_set_obj_parent(ctx, val, parent);
}
void
pdf_array_put(fz_context *ctx, pdf_obj *obj, int i, pdf_obj *item)
{
RESOLVE(obj);
if (!OBJ_IS_ARRAY(obj))
fz_throw(ctx, FZ_ERROR_GENERIC, "not an array (%s)", pdf_objkindstr(obj));
if (i == ARRAY(obj)->len)
{
pdf_array_push(ctx, obj, item);
return;
}
if (i < 0 || i > ARRAY(obj)->len)
fz_throw(ctx, FZ_ERROR_GENERIC, "index out of bounds");
prepare_object_for_alteration(ctx, obj, item);
pdf_drop_obj(ctx, ARRAY(obj)->items[i]);
ARRAY(obj)->items[i] = pdf_keep_obj(ctx, item);
}
void
pdf_array_put_drop(fz_context *ctx, pdf_obj *obj, int i, pdf_obj *item)
{
fz_try(ctx)
pdf_array_put(ctx, obj, i, item);
fz_always(ctx)
pdf_drop_obj(ctx, item);
fz_catch(ctx)
fz_rethrow(ctx);
}
void
pdf_array_push(fz_context *ctx, pdf_obj *obj, pdf_obj *item)
{
RESOLVE(obj);
if (!OBJ_IS_ARRAY(obj))
fz_throw(ctx, FZ_ERROR_GENERIC, "not an array (%s)", pdf_objkindstr(obj));
prepare_object_for_alteration(ctx, obj, item);
if (ARRAY(obj)->len + 1 > ARRAY(obj)->cap)
pdf_array_grow(ctx, ARRAY(obj));
ARRAY(obj)->items[ARRAY(obj)->len] = pdf_keep_obj(ctx, item);
ARRAY(obj)->len++;
}
void
pdf_array_push_drop(fz_context *ctx, pdf_obj *obj, pdf_obj *item)
{
fz_try(ctx)
pdf_array_push(ctx, obj, item);
fz_always(ctx)
pdf_drop_obj(ctx, item);
fz_catch(ctx)
fz_rethrow(ctx);
}
void
pdf_array_insert(fz_context *ctx, pdf_obj *obj, pdf_obj *item, int i)
{
RESOLVE(obj);
if (!OBJ_IS_ARRAY(obj))
fz_throw(ctx, FZ_ERROR_GENERIC, "not an array (%s)", pdf_objkindstr(obj));
if (i < 0 || i > ARRAY(obj)->len)
fz_throw(ctx, FZ_ERROR_GENERIC, "index out of bounds");
prepare_object_for_alteration(ctx, obj, item);
if (ARRAY(obj)->len + 1 > ARRAY(obj)->cap)
pdf_array_grow(ctx, ARRAY(obj));
memmove(ARRAY(obj)->items + i + 1, ARRAY(obj)->items + i, (ARRAY(obj)->len - i) * sizeof(pdf_obj*));
ARRAY(obj)->items[i] = pdf_keep_obj(ctx, item);
ARRAY(obj)->len++;
}
void
pdf_array_insert_drop(fz_context *ctx, pdf_obj *obj, pdf_obj *item, int i)
{
fz_try(ctx)
pdf_array_insert(ctx, obj, item, i);
fz_always(ctx)
pdf_drop_obj(ctx, item);
fz_catch(ctx)
fz_rethrow(ctx);
}
void
pdf_array_delete(fz_context *ctx, pdf_obj *obj, int i)
{
RESOLVE(obj);
if (!OBJ_IS_ARRAY(obj))
fz_throw(ctx, FZ_ERROR_GENERIC, "not an array (%s)", pdf_objkindstr(obj));
if (i < 0 || i >= ARRAY(obj)->len)
fz_throw(ctx, FZ_ERROR_GENERIC, "index out of bounds");
prepare_object_for_alteration(ctx, obj, NULL);
pdf_drop_obj(ctx, ARRAY(obj)->items[i]);
ARRAY(obj)->items[i] = 0;
ARRAY(obj)->len--;
memmove(ARRAY(obj)->items + i, ARRAY(obj)->items + i + 1, (ARRAY(obj)->len - i) * sizeof(pdf_obj*));
}
int
pdf_array_contains(fz_context *ctx, pdf_obj *arr, pdf_obj *obj)
{
int i, len;
len = pdf_array_len(ctx, arr);
for (i = 0; i < len; i++)
if (!pdf_objcmp(ctx, pdf_array_get(ctx, arr, i), obj))
return 1;
return 0;
}
int
pdf_array_find(fz_context *ctx, pdf_obj *arr, pdf_obj *obj)
{
int i, len;
len = pdf_array_len(ctx, arr);
for (i = 0; i < len; i++)
if (!pdf_objcmp(ctx, pdf_array_get(ctx, arr, i), obj))
return i;
return -1;
}
pdf_obj *pdf_new_rect(fz_context *ctx, pdf_document *doc, fz_rect rect)
{
pdf_obj *arr = pdf_new_array(ctx, doc, 4);
fz_try(ctx)
{
pdf_array_push_real(ctx, arr, rect.x0);
pdf_array_push_real(ctx, arr, rect.y0);
pdf_array_push_real(ctx, arr, rect.x1);
pdf_array_push_real(ctx, arr, rect.y1);
}
fz_catch(ctx)
{
pdf_drop_obj(ctx, arr);
fz_rethrow(ctx);
}
return arr;
}
pdf_obj *pdf_new_matrix(fz_context *ctx, pdf_document *doc, fz_matrix mtx)
{
pdf_obj *arr = pdf_new_array(ctx, doc, 6);
fz_try(ctx)
{
pdf_array_push_real(ctx, arr, mtx.a);
pdf_array_push_real(ctx, arr, mtx.b);
pdf_array_push_real(ctx, arr, mtx.c);
pdf_array_push_real(ctx, arr, mtx.d);
pdf_array_push_real(ctx, arr, mtx.e);
pdf_array_push_real(ctx, arr, mtx.f);
}
fz_catch(ctx)
{
pdf_drop_obj(ctx, arr);
fz_rethrow(ctx);
}
return arr;
}
/* 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;
const char *an;
const char *bn;
/* We should never get a->k == NULL or b->k == NULL. If we
* do, then they match. */
if (a->k < PDF_LIMIT)
an = PDF_NAME_LIST[(intptr_t)a->k];
else if (a->k >= PDF_LIMIT && a->k->kind == PDF_NAME)
an = NAME(a->k)->n;
else
return 0;
if (b->k < PDF_LIMIT)
bn = PDF_NAME_LIST[(intptr_t)b->k];
else if (b->k >= PDF_LIMIT && b->k->kind == PDF_NAME)
bn = NAME(b->k)->n;
else
return 0;
return strcmp(an, bn);
}
pdf_obj *
pdf_new_dict(fz_context *ctx, pdf_document *doc, int initialcap)
{
pdf_obj_dict *obj;
int i;
obj = Memento_label(fz_malloc(ctx, sizeof(pdf_obj_dict)), "pdf_obj(dict)");
obj->super.refs = 1;
obj->super.kind = PDF_DICT;
obj->super.flags = 0;
obj->doc = doc;
obj->parent_num = 0;
obj->len = 0;
obj->cap = initialcap > 1 ? initialcap : 10;
fz_try(ctx)
{
DICT(obj)->items = fz_malloc_array(ctx, DICT(obj)->cap, struct keyval);
}
fz_catch(ctx)
{
fz_free(ctx, obj);
fz_rethrow(ctx);
}
for (i = 0; i < DICT(obj)->cap; i++)
{
DICT(obj)->items[i].k = NULL;
DICT(obj)->items[i].v = NULL;
}
return &obj->super;
}
static void
pdf_dict_grow(fz_context *ctx, pdf_obj *obj)
{
int i;
int new_cap = (DICT(obj)->cap * 3) / 2;
DICT(obj)->items = fz_realloc_array(ctx, DICT(obj)->items, new_cap, struct keyval);
DICT(obj)->cap = new_cap;
for (i = DICT(obj)->len; i < DICT(obj)->cap; i++)
{
DICT(obj)->items[i].k = NULL;
DICT(obj)->items[i].v = NULL;
}
}
pdf_obj *
pdf_copy_dict(fz_context *ctx, pdf_obj *obj)
{
pdf_document *doc;
pdf_obj *dict;
int i, n;
RESOLVE(obj);
if (!OBJ_IS_DICT(obj))
fz_throw(ctx, FZ_ERROR_GENERIC, "not a dict (%s)", pdf_objkindstr(obj));
doc = DICT(obj)->doc;
n = pdf_dict_len(ctx, obj);
dict = pdf_new_dict(ctx, doc, n);
fz_try(ctx)
for (i = 0; i < n; i++)
pdf_dict_put(ctx, dict, pdf_dict_get_key(ctx, obj, i), pdf_dict_get_val(ctx, obj, i));
fz_catch(ctx)
{
pdf_drop_obj(ctx, dict);
fz_rethrow(ctx);
}
return dict;
}
int
pdf_dict_len(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
if (!OBJ_IS_DICT(obj))
return 0;
return DICT(obj)->len;
}
pdf_obj *
pdf_dict_get_key(fz_context *ctx, pdf_obj *obj, int i)
{
RESOLVE(obj);
if (!OBJ_IS_DICT(obj))
return NULL;
if (i < 0 || i >= DICT(obj)->len)
return NULL;
return DICT(obj)->items[i].k;
}
pdf_obj *
pdf_dict_get_val(fz_context *ctx, pdf_obj *obj, int i)
{
RESOLVE(obj);
if (!OBJ_IS_DICT(obj))
return NULL;
if (i < 0 || i >= DICT(obj)->len)
return NULL;
return DICT(obj)->items[i].v;
}
void
pdf_dict_put_val_null(fz_context *ctx, pdf_obj *obj, int idx)
{
RESOLVE(obj);
if (!OBJ_IS_DICT(obj))
fz_throw(ctx, FZ_ERROR_GENERIC, "not a dict (%s)", pdf_objkindstr(obj));
if (idx < 0 || idx >= DICT(obj)->len)
fz_throw(ctx, FZ_ERROR_GENERIC, "index out of bounds");
prepare_object_for_alteration(ctx, obj, NULL);
pdf_drop_obj(ctx, DICT(obj)->items[idx].v);
DICT(obj)->items[idx].v = PDF_NULL;
}
/* Returns 0 <= i < len for key found. Returns -1-len < i <= -1 for key
* not found, but with insertion point -1-i. */
static int
pdf_dict_finds(fz_context *ctx, pdf_obj *obj, const char *key)
{
int len = DICT(obj)->len;
if ((obj->flags & PDF_FLAGS_SORTED) && len > 0)
{
int l = 0;
int r = len - 1;
if (strcmp(pdf_to_name(ctx, DICT(obj)->items[r].k), key) < 0)
{
return -1 - (r+1);
}
while (l <= r)
{
int m = (l + r) >> 1;
int c = -strcmp(pdf_to_name(ctx, DICT(obj)->items[m].k), key);
if (c < 0)
r = m - 1;
else if (c > 0)
l = m + 1;
else
return m;
}
return -1 - l;
}
else
{
int i;
for (i = 0; i < len; i++)
if (strcmp(pdf_to_name(ctx, DICT(obj)->items[i].k), key) == 0)
return i;
return -1 - len;
}
}
static int
pdf_dict_find(fz_context *ctx, pdf_obj *obj, pdf_obj *key)
{
int len = DICT(obj)->len;
if ((obj->flags & PDF_FLAGS_SORTED) && len > 0)
{
int l = 0;
int r = len - 1;
pdf_obj *k = DICT(obj)->items[r].k;
if (k == key || (k >= PDF_LIMIT && strcmp(NAME(k)->n, PDF_NAME_LIST[(intptr_t)key]) < 0))
{
return -1 - (r+1);
}
while (l <= r)
{
int m = (l + r) >> 1;
int c;
k = DICT(obj)->items[m].k;
c = (k < PDF_LIMIT ? (char *)key-(char *)k : -strcmp(NAME(k)->n, PDF_NAME_LIST[(intptr_t)key]));
if (c < 0)
r = m - 1;
else if (c > 0)
l = m + 1;
else
return m;
}
return -1 - l;
}
else
{
int i;
for (i = 0; i < len; i++)
{
pdf_obj *k = DICT(obj)->items[i].k;
if (k < PDF_LIMIT)
{
if (k == key)
return i;
}
else
{
if (!strcmp(PDF_NAME_LIST[(intptr_t)key], NAME(k)->n))
return i;
}
}
return -1 - len;
}
}
pdf_obj *
pdf_dict_gets(fz_context *ctx, pdf_obj *obj, const char *key)
{
int i;
RESOLVE(obj);
if (!OBJ_IS_DICT(obj))
return NULL;
if (!key)
return NULL;
i = pdf_dict_finds(ctx, obj, key);
if (i >= 0)
return DICT(obj)->items[i].v;
return NULL;
}
pdf_obj *
pdf_dict_getp(fz_context *ctx, pdf_obj *obj, const char *keys)
{
char buf[256];
char *k, *e;
RESOLVE(obj);
if (!OBJ_IS_DICT(obj))
return NULL;
if (strlen(keys)+1 > 256)
fz_throw(ctx, FZ_ERROR_GENERIC, "path too long");
strcpy(buf, keys);
e = buf;
while (*e && obj)
{
k = e;
while (*e != '/' && *e != '\0')
e++;
if (*e == '/')
{
*e = '\0';
e++;
}
obj = pdf_dict_gets(ctx, obj, k);
}
return obj;
}
pdf_obj *
pdf_dict_getl(fz_context *ctx, pdf_obj *obj, ...)
{
va_list keys;
pdf_obj *key;
va_start(keys, obj);
while (obj != NULL && (key = va_arg(keys, pdf_obj *)) != NULL)
{
obj = pdf_dict_get(ctx, obj, key);
}
va_end(keys);
return obj;
}
pdf_obj *
pdf_dict_get(fz_context *ctx, pdf_obj *obj, pdf_obj *key)
{
int i;
RESOLVE(obj);
if (!OBJ_IS_DICT(obj))
return NULL;
if (!OBJ_IS_NAME(key))
return NULL;
if (key < PDF_LIMIT)
i = pdf_dict_find(ctx, obj, key);
else
i = pdf_dict_finds(ctx, obj, pdf_to_name(ctx, key));
if (i >= 0)
return DICT(obj)->items[i].v;
return NULL;
}
pdf_obj *
pdf_dict_getsa(fz_context *ctx, pdf_obj *obj, const char *key, const char *abbrev)
{
pdf_obj *v;
v = pdf_dict_gets(ctx, obj, key);
if (v)
return v;
return pdf_dict_gets(ctx, obj, abbrev);
}
pdf_obj *
pdf_dict_geta(fz_context *ctx, pdf_obj *obj, pdf_obj *key, pdf_obj *abbrev)
{
pdf_obj *v;
v = pdf_dict_get(ctx, obj, key);
if (v)
return v;
return pdf_dict_get(ctx, obj, abbrev);
}
static void
pdf_dict_get_put(fz_context *ctx, pdf_obj *obj, pdf_obj *key, pdf_obj *val, pdf_obj **old_val)
{
int i;
if (old_val)
*old_val = NULL;
RESOLVE(obj);
if (!OBJ_IS_DICT(obj))
fz_throw(ctx, FZ_ERROR_GENERIC, "not a dict (%s)", pdf_objkindstr(obj));
if (!OBJ_IS_NAME(key))
fz_throw(ctx, FZ_ERROR_GENERIC, "key is not a name (%s)", pdf_objkindstr(obj));
if (DICT(obj)->len > 100 && !(obj->flags & PDF_FLAGS_SORTED))
pdf_sort_dict(ctx, obj);
if (key < PDF_LIMIT)
i = pdf_dict_find(ctx, obj, key);
else
i = pdf_dict_finds(ctx, obj, pdf_to_name(ctx, key));
prepare_object_for_alteration(ctx, obj, val);
if (i >= 0 && i < DICT(obj)->len)
{
if (DICT(obj)->items[i].v != val)
{
pdf_obj *d = DICT(obj)->items[i].v;
DICT(obj)->items[i].v = pdf_keep_obj(ctx, val);
if (old_val)
*old_val = d;
else
pdf_drop_obj(ctx, d);
}
}
else
{
if (DICT(obj)->len + 1 > DICT(obj)->cap)
pdf_dict_grow(ctx, obj);
i = -1-i;
if ((obj->flags & PDF_FLAGS_SORTED) && DICT(obj)->len > 0)
memmove(&DICT(obj)->items[i + 1],
&DICT(obj)->items[i],
(DICT(obj)->len - i) * sizeof(struct keyval));
DICT(obj)->items[i].k = pdf_keep_obj(ctx, key);
DICT(obj)->items[i].v = pdf_keep_obj(ctx, val);
DICT(obj)->len ++;
}
}
void
pdf_dict_put(fz_context *ctx, pdf_obj *obj, pdf_obj *key, pdf_obj *val)
{
pdf_dict_get_put(ctx, obj, key, val, NULL);
}
void
pdf_dict_put_drop(fz_context *ctx, pdf_obj *obj, pdf_obj *key, pdf_obj *val)
{
fz_try(ctx)
pdf_dict_get_put(ctx, obj, key, val, NULL);
fz_always(ctx)
pdf_drop_obj(ctx, val);
fz_catch(ctx)
fz_rethrow(ctx);
}
void
pdf_dict_get_put_drop(fz_context *ctx, pdf_obj *obj, pdf_obj *key, pdf_obj *val, pdf_obj **old_val)
{
fz_try(ctx)
pdf_dict_get_put(ctx, obj, key, val, old_val);
fz_always(ctx)
pdf_drop_obj(ctx, val);
fz_catch(ctx)
fz_rethrow(ctx);
}
void
pdf_dict_puts(fz_context *ctx, pdf_obj *obj, const char *key, pdf_obj *val)
{
pdf_obj *keyobj;
RESOLVE(obj);
if (!OBJ_IS_DICT(obj))
fz_throw(ctx, FZ_ERROR_GENERIC, "not a dict (%s)", pdf_objkindstr(obj));
keyobj = pdf_new_name(ctx, key);
fz_try(ctx)
pdf_dict_put(ctx, obj, keyobj, val);
fz_always(ctx)
pdf_drop_obj(ctx, keyobj);
fz_catch(ctx)
fz_rethrow(ctx);
}
void
pdf_dict_puts_drop(fz_context *ctx, pdf_obj *obj, const char *key, pdf_obj *val)
{
pdf_obj *keyobj;
RESOLVE(obj);
if (!OBJ_IS_DICT(obj))
fz_throw(ctx, FZ_ERROR_GENERIC, "not a dict (%s)", pdf_objkindstr(obj));
keyobj = pdf_new_name(ctx, key);
fz_var(keyobj);
fz_try(ctx)
pdf_dict_put(ctx, obj, keyobj, val);
fz_always(ctx)
{
pdf_drop_obj(ctx, keyobj);
pdf_drop_obj(ctx, val);
}
fz_catch(ctx)
{
fz_rethrow(ctx);
}
}
void
pdf_dict_putp(fz_context *ctx, pdf_obj *obj, const char *keys, pdf_obj *val)
{
pdf_document *doc;
char buf[256];
char *k, *e;
pdf_obj *cobj = NULL;
RESOLVE(obj);
if (!OBJ_IS_DICT(obj))
fz_throw(ctx, FZ_ERROR_GENERIC, "not a dict (%s)", pdf_objkindstr(obj));
if (strlen(keys)+1 > 256)
fz_throw(ctx, FZ_ERROR_GENERIC, "buffer overflow in pdf_dict_putp");
doc = DICT(obj)->doc;
strcpy(buf, keys);
e = buf;
while (*e)
{
k = e;
while (*e != '/' && *e != '\0')
e++;
if (*e == '/')
{
*e = '\0';
e++;
}
if (*e)
{
/* Not the last key in the key path. Create subdict if not already there. */
cobj = pdf_dict_gets(ctx, obj, k);
if (cobj == NULL)
{
cobj = pdf_new_dict(ctx, doc, 1);
fz_try(ctx)
pdf_dict_puts(ctx, obj, k, cobj);
fz_always(ctx)
pdf_drop_obj(ctx, cobj);
fz_catch(ctx)
fz_rethrow(ctx);
}
/* Move to subdict */
obj = cobj;
}
else
{
/* Last key. Use it to store the value */
/* Use val = NULL to request delete */
if (val)
pdf_dict_puts(ctx, obj, k, val);
else
pdf_dict_dels(ctx, obj, k);
}
}
}
void
pdf_dict_putp_drop(fz_context *ctx, pdf_obj *obj, const char *keys, pdf_obj *val)
{
fz_try(ctx)
pdf_dict_putp(ctx, obj, keys, val);
fz_always(ctx)
pdf_drop_obj(ctx, val);
fz_catch(ctx)
fz_rethrow(ctx);
}
static void
pdf_dict_vputl(fz_context *ctx, pdf_obj *obj, pdf_obj *val, va_list keys)
{
pdf_obj *key;
pdf_obj *next_key;
pdf_obj *next_obj;
pdf_document *doc;
RESOLVE(obj);
if (!OBJ_IS_DICT(obj))
fz_throw(ctx, FZ_ERROR_GENERIC, "not a dict (%s)", pdf_objkindstr(obj));
doc = DICT(obj)->doc;
key = va_arg(keys, pdf_obj *);
if (key == NULL)
return;
while ((next_key = va_arg(keys, pdf_obj *)) != NULL)
{
next_obj = pdf_dict_get(ctx, obj, key);
if (next_obj == NULL)
goto new_obj;
obj = next_obj;
key = next_key;
}
pdf_dict_put(ctx, obj, key, val);
return;
new_obj:
/* We have to create entries */
do
{
next_obj = pdf_new_dict(ctx, doc, 1);
pdf_dict_put_drop(ctx, obj, key, next_obj);
obj = next_obj;
key = next_key;
}
while ((next_key = va_arg(keys, pdf_obj *)) != NULL);
pdf_dict_put(ctx, obj, key, val);
return;
}
void
pdf_dict_putl(fz_context *ctx, pdf_obj *obj, pdf_obj *val, ...)
{
va_list keys;
va_start(keys, val);
fz_try(ctx)
pdf_dict_vputl(ctx, obj, val, keys);
fz_always(ctx)
va_end(keys);
fz_catch(ctx)
fz_rethrow(ctx);
}
void
pdf_dict_putl_drop(fz_context *ctx, pdf_obj *obj, pdf_obj *val, ...)
{
va_list keys;
va_start(keys, val);
fz_try(ctx)
pdf_dict_vputl(ctx, obj, val, keys);
fz_always(ctx)
{
pdf_drop_obj(ctx, val);
va_end(keys);
}
fz_catch(ctx)
fz_rethrow(ctx);
}
void
pdf_dict_dels(fz_context *ctx, pdf_obj *obj, const char *key)
{
int i;
RESOLVE(obj);
if (!OBJ_IS_DICT(obj))
fz_throw(ctx, FZ_ERROR_GENERIC, "not a dict (%s)", pdf_objkindstr(obj));
if (!key)
fz_throw(ctx, FZ_ERROR_GENERIC, "key is null");
prepare_object_for_alteration(ctx, obj, NULL);
i = pdf_dict_finds(ctx, obj, key);
if (i >= 0)
{
pdf_drop_obj(ctx, DICT(obj)->items[i].k);
pdf_drop_obj(ctx, DICT(obj)->items[i].v);
obj->flags &= ~PDF_FLAGS_SORTED;
DICT(obj)->items[i] = DICT(obj)->items[DICT(obj)->len-1];
DICT(obj)->len --;
}
}
void
pdf_dict_del(fz_context *ctx, pdf_obj *obj, pdf_obj *key)
{
if (!OBJ_IS_NAME(key))
fz_throw(ctx, FZ_ERROR_GENERIC, "key is not a name (%s)", pdf_objkindstr(key));
if (key < PDF_LIMIT)
pdf_dict_dels(ctx, obj, PDF_NAME_LIST[(intptr_t)key]);
else
pdf_dict_dels(ctx, obj, NAME(key)->n);
}
void
pdf_sort_dict(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
if (!OBJ_IS_DICT(obj))
return;
if (!(obj->flags & PDF_FLAGS_SORTED))
{
qsort(DICT(obj)->items, DICT(obj)->len, sizeof(struct keyval), keyvalcmp);
obj->flags |= PDF_FLAGS_SORTED;
}
}
pdf_obj *
pdf_deep_copy_obj(fz_context *ctx, pdf_obj *obj)
{
if (obj < PDF_LIMIT)
{
return obj;
}
if (obj->kind == PDF_DICT)
{
pdf_document *doc = DICT(obj)->doc;
int n = pdf_dict_len(ctx, obj);
pdf_obj *dict = pdf_new_dict(ctx, doc, n);
int i;
fz_try(ctx)
for (i = 0; i < n; i++)
{
pdf_obj *obj_copy = pdf_deep_copy_obj(ctx, pdf_dict_get_val(ctx, obj, i));
pdf_dict_put_drop(ctx, dict, pdf_dict_get_key(ctx, obj, i), obj_copy);
}
fz_catch(ctx)
{
pdf_drop_obj(ctx, dict);
fz_rethrow(ctx);
}
return dict;
}
else if (obj->kind == PDF_ARRAY)
{
pdf_document *doc = ARRAY(obj)->doc;
int n = pdf_array_len(ctx, obj);
pdf_obj *arr = pdf_new_array(ctx, doc, n);
int i;
fz_try(ctx)
for (i = 0; i < n; i++)
{
pdf_obj *obj_copy = pdf_deep_copy_obj(ctx, pdf_array_get(ctx, obj, i));
pdf_array_push_drop(ctx, arr, obj_copy);
}
fz_catch(ctx)
{
pdf_drop_obj(ctx, arr);
fz_rethrow(ctx);
}
return arr;
}
else
{
return pdf_keep_obj(ctx, obj);
}
}
/* obj marking and unmarking functions - to avoid infinite recursions. */
int
pdf_obj_marked(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
if (obj < PDF_LIMIT)
return 0;
return !!(obj->flags & PDF_FLAGS_MARKED);
}
int
pdf_mark_obj(fz_context *ctx, pdf_obj *obj)
{
int marked;
RESOLVE(obj);
if (obj < PDF_LIMIT)
return 0;
marked = !!(obj->flags & PDF_FLAGS_MARKED);
obj->flags |= PDF_FLAGS_MARKED;
return marked;
}
void
pdf_unmark_obj(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
if (obj < PDF_LIMIT)
return;
obj->flags &= ~PDF_FLAGS_MARKED;
}
void
pdf_set_obj_memo(fz_context *ctx, pdf_obj *obj, int bit, int memo)
{
if (obj < PDF_LIMIT)
return;
bit <<= 1;
obj->flags |= PDF_FLAGS_MEMO_BASE << bit;
if (memo)
obj->flags |= PDF_FLAGS_MEMO_BASE_BOOL << bit;
else
obj->flags &= ~(PDF_FLAGS_MEMO_BASE_BOOL << bit);
}
int
pdf_obj_memo(fz_context *ctx, pdf_obj *obj, int bit, int *memo)
{
if (obj < PDF_LIMIT)
return 0;
bit <<= 1;
if (!(obj->flags & (PDF_FLAGS_MEMO_BASE<<bit)))
return 0;
*memo = !!(obj->flags & (PDF_FLAGS_MEMO_BASE_BOOL<<bit));
return 1;
}
/* obj dirty bit support. */
int pdf_obj_is_dirty(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
if (obj < PDF_LIMIT)
return 0;
return !!(obj->flags & PDF_FLAGS_DIRTY);
}
void pdf_dirty_obj(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
if (obj < PDF_LIMIT)
return;
obj->flags |= PDF_FLAGS_DIRTY;
}
void pdf_clean_obj(fz_context *ctx, pdf_obj *obj)
{
RESOLVE(obj);
if (obj < PDF_LIMIT)
return;
obj->flags &= ~PDF_FLAGS_DIRTY;
}
static void
pdf_drop_array(fz_context *ctx, pdf_obj *obj)
{
int i;
for (i = 0; i < DICT(obj)->len; i++)
pdf_drop_obj(ctx, ARRAY(obj)->items[i]);
fz_free(ctx, DICT(obj)->items);
fz_free(ctx, obj);
}
static void
pdf_drop_dict(fz_context *ctx, pdf_obj *obj)
{
int i;
for (i = 0; i < DICT(obj)->len; i++) {
pdf_drop_obj(ctx, DICT(obj)->items[i].k);
pdf_drop_obj(ctx, DICT(obj)->items[i].v);
}
fz_free(ctx, DICT(obj)->items);
fz_free(ctx, obj);
}
pdf_obj *
pdf_keep_obj(fz_context *ctx, pdf_obj *obj)
{
if (obj >= PDF_LIMIT)
return fz_keep_imp16(ctx, obj, &obj->refs);
return obj;
}
void
pdf_drop_obj(fz_context *ctx, pdf_obj *obj)
{
if (obj >= PDF_LIMIT)
{
if (fz_drop_imp16(ctx, obj, &obj->refs))
{
if (obj->kind == PDF_ARRAY)
pdf_drop_array(ctx, obj);
else if (obj->kind == PDF_DICT)
pdf_drop_dict(ctx, obj);
else if (obj->kind == PDF_STRING)
{
fz_free(ctx, STRING(obj)->text);
fz_free(ctx, obj);
}
else
fz_free(ctx, obj);
}
}
}
/*
Recurse through the object structure setting the node's parent_num to num.
parent_num is used when a subobject is to be changed during a document edit.
The whole containing hierarchy is moved to the incremental xref section, so
to be later written out as an incremental file update.
*/
void
pdf_set_obj_parent(fz_context *ctx, pdf_obj *obj, int num)
{
int n, i;
if (obj < PDF_LIMIT)
return;
switch (obj->kind)
{
case PDF_ARRAY:
ARRAY(obj)->parent_num = num;
n = pdf_array_len(ctx, obj);
for (i = 0; i < n; i++)
pdf_set_obj_parent(ctx, pdf_array_get(ctx, obj, i), num);
break;
case PDF_DICT:
DICT(obj)->parent_num = num;
n = pdf_dict_len(ctx, obj);
for (i = 0; i < n; i++)
pdf_set_obj_parent(ctx, pdf_dict_get_val(ctx, obj, i), num);
break;
}
}
int pdf_obj_parent_num(fz_context *ctx, pdf_obj *obj)
{
if (obj < PDF_LIMIT)
return 0;
switch (obj->kind)
{
case PDF_INDIRECT:
return REF(obj)->num;
case PDF_ARRAY:
return ARRAY(obj)->parent_num;
case PDF_DICT:
return DICT(obj)->parent_num;
default:
return 0;
}
}
/* Pretty printing objects */
struct fmt
{
char *buf; /* original static buffer */
char *ptr; /* buffer we're writing to, maybe dynamically reallocated */
int cap;
int len;
int indent;
int tight;
int ascii;
int col;
int sep;
int last;
pdf_crypt *crypt;
int num;
int gen;
};
static void fmt_obj(fz_context *ctx, 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(fz_context *ctx, struct fmt *fmt, int c)
{
if (fmt->sep && !isdelim(fmt->last) && !isdelim(c)) {
fmt->sep = 0;
fmt_putc(ctx, fmt, ' ');
}
fmt->sep = 0;
if (fmt->len >= fmt->cap)
{
fmt->cap *= 2;
if (fmt->buf == fmt->ptr)
{
fmt->ptr = fz_malloc(ctx, fmt->cap);
memcpy(fmt->ptr, fmt->buf, fmt->len);
}
else
{
fmt->ptr = fz_realloc(ctx, fmt->ptr, fmt->cap);
}
}
fmt->ptr[fmt->len] = c;
if (c == '\n')
fmt->col = 0;
else
fmt->col ++;
fmt->len ++;
fmt->last = c;
}
static inline void fmt_indent(fz_context *ctx, struct fmt *fmt)
{
int i = fmt->indent;
while (i--) {
fmt_putc(ctx, fmt, ' ');
fmt_putc(ctx, fmt, ' ');
}
}
static inline void fmt_puts(fz_context *ctx, struct fmt *fmt, char *s)
{
while (*s)
fmt_putc(ctx, fmt, *s++);
}
static inline void fmt_sep(fz_context *ctx, struct fmt *fmt)
{
fmt->sep = 1;
}
static int is_binary_string(fz_context *ctx, pdf_obj *obj)
{
unsigned char *s = (unsigned char *)pdf_to_str_buf(ctx, obj);
int i, n = pdf_to_str_len(ctx, obj);
for (i = 0; i < n; ++i)
{
if (s[i] > 126) return 1;
if (s[i] < 32 && (s[i] != '\t' && s[i] != '\n' && s[i] != '\r')) return 1;
}
return 0;
}
static int is_longer_than_hex(fz_context *ctx, pdf_obj *obj)
{
unsigned char *s = (unsigned char *)pdf_to_str_buf(ctx, obj);
int i, n = pdf_to_str_len(ctx, obj);
int m = 0;
for (i = 0; i < n; ++i)
{
if (s[i] > 126)
m += 4;
else if (s[i] == 0)
m += 4;
else if (strchr("\n\r\t\b\f()\\", s[i]))
m += 2;
else if (s[i] < 32)
m += 4;
else
m += 1;
}
return m > (n * 2);
}
static void fmt_str_out(fz_context *ctx, void *fmt_, const unsigned char *s, int n)
{
struct fmt *fmt = (struct fmt *)fmt_;
int i, c;
for (i = 0; i < n; i++)
{
c = (unsigned char)s[i];
if (c == '\n')
fmt_puts(ctx, fmt, "\\n");
else if (c == '\r')
fmt_puts(ctx, fmt, "\\r");
else if (c == '\t')
fmt_puts(ctx, fmt, "\\t");
else if (c == '\b')
fmt_puts(ctx, fmt, "\\b");
else if (c == '\f')
fmt_puts(ctx, fmt, "\\f");
else if (c == '(')
fmt_puts(ctx, fmt, "\\(");
else if (c == ')')
fmt_puts(ctx, fmt, "\\)");
else if (c == '\\')
fmt_puts(ctx, fmt, "\\\\");
else if (c < 32 || c >= 127) {
fmt_putc(ctx, fmt, '\\');
fmt_putc(ctx, fmt, '0' + ((c / 64) & 7));
fmt_putc(ctx, fmt, '0' + ((c / 8) & 7));
fmt_putc(ctx, fmt, '0' + ((c) & 7));
}
else
fmt_putc(ctx, fmt, c);
}
}
static void fmt_str(fz_context *ctx, struct fmt *fmt, pdf_obj *obj)
{
unsigned char *s = (unsigned char *)pdf_to_str_buf(ctx, obj);
int n = pdf_to_str_len(ctx, obj);
fmt_putc(ctx, fmt, '(');
pdf_encrypt_data(ctx, fmt->crypt, fmt->num, fmt->gen, fmt_str_out, fmt, s, n);
fmt_putc(ctx, fmt, ')');
}
static void fmt_hex_out(fz_context *ctx, void *arg, const unsigned char *s, int n)
{
struct fmt *fmt = (struct fmt *)arg;
int i, b, c;
for (i = 0; i < n; i++) {
b = (unsigned char) s[i];
c = (b >> 4) & 0x0f;
fmt_putc(ctx, fmt, c < 0xA ? c + '0' : c + 'A' - 0xA);
c = (b) & 0x0f;
fmt_putc(ctx, fmt, c < 0xA ? c + '0' : c + 'A' - 0xA);
}
}
static void fmt_hex(fz_context *ctx, struct fmt *fmt, pdf_obj *obj)
{
unsigned char *s = (unsigned char *)pdf_to_str_buf(ctx, obj);
int n = pdf_to_str_len(ctx, obj);
fmt_putc(ctx, fmt, '<');
pdf_encrypt_data(ctx, fmt->crypt, fmt->num, fmt->gen, fmt_hex_out, fmt, s, n);
fmt_putc(ctx, fmt, '>');
}
static void fmt_name(fz_context *ctx, struct fmt *fmt, pdf_obj *obj)
{
unsigned char *s = (unsigned char *) pdf_to_name(ctx, obj);
int i, c;
fmt_putc(ctx, fmt, '/');
for (i = 0; s[i]; i++)
{
if (isdelim(s[i]) || iswhite(s[i]) ||
s[i] == '#' || s[i] < 32 || s[i] >= 127)
{
fmt_putc(ctx, fmt, '#');
c = (s[i] >> 4) & 0xf;
fmt_putc(ctx, fmt, c < 0xA ? c + '0' : c + 'A' - 0xA);
c = s[i] & 0xf;
fmt_putc(ctx, fmt, c < 0xA ? c + '0' : c + 'A' - 0xA);
}
else
{
fmt_putc(ctx, fmt, s[i]);
}
}
}
static void fmt_array(fz_context *ctx, struct fmt *fmt, pdf_obj *obj)
{
int i, n;
n = pdf_array_len(ctx, obj);
if (fmt->tight) {
fmt_putc(ctx, fmt, '[');
for (i = 0; i < n; i++) {
fmt_obj(ctx, fmt, pdf_array_get(ctx, obj, i));
fmt_sep(ctx, fmt);
}
fmt_putc(ctx, fmt, ']');
}
else {
fmt_putc(ctx, fmt, '[');
fmt->indent ++;
for (i = 0; i < n; i++) {
if (fmt->col > 60) {
fmt_putc(ctx, fmt, '\n');
fmt_indent(ctx, fmt);
} else {
fmt_putc(ctx, fmt, ' ');
}
fmt_obj(ctx, fmt, pdf_array_get(ctx, obj, i));
}
fmt->indent --;
fmt_putc(ctx, fmt, ' ');
fmt_putc(ctx, fmt, ']');
fmt_sep(ctx, fmt);
}
}
static void fmt_dict(fz_context *ctx, struct fmt *fmt, pdf_obj *obj)
{
int i, n;
pdf_obj *key, *val;
n = pdf_dict_len(ctx, obj);
if (fmt->tight) {
fmt_puts(ctx, fmt, "<<");
for (i = 0; i < n; i++) {
fmt_obj(ctx, fmt, pdf_dict_get_key(ctx, obj, i));
fmt_sep(ctx, fmt);
fmt_obj(ctx, fmt, pdf_dict_get_val(ctx, obj, i));
fmt_sep(ctx, fmt);
}
fmt_puts(ctx, fmt, ">>");
}
else {
fmt_puts(ctx, fmt, "<<\n");
fmt->indent ++;
for (i = 0; i < n; i++) {
key = pdf_dict_get_key(ctx, obj, i);
val = pdf_dict_get_val(ctx, obj, i);
fmt_indent(ctx, fmt);
fmt_obj(ctx, fmt, key);
fmt_putc(ctx, fmt, ' ');
if (!pdf_is_indirect(ctx, val) && pdf_is_array(ctx, val))
fmt->indent ++;
fmt_obj(ctx, fmt, val);
fmt_putc(ctx, fmt, '\n');
if (!pdf_is_indirect(ctx, val) && pdf_is_array(ctx, val))
fmt->indent --;
}
fmt->indent --;
fmt_indent(ctx, fmt);
fmt_puts(ctx, fmt, ">>");
}
}
static void fmt_obj(fz_context *ctx, struct fmt *fmt, pdf_obj *obj)
{
char buf[256];
if (obj == PDF_NULL)
fmt_puts(ctx, fmt, "null");
else if (obj == PDF_TRUE)
fmt_puts(ctx, fmt, "true");
else if (obj == PDF_FALSE)
fmt_puts(ctx, fmt, "false");
else if (pdf_is_indirect(ctx, obj))
{
fz_snprintf(buf, sizeof buf, "%d %d R", pdf_to_num(ctx, obj), pdf_to_gen(ctx, obj));
fmt_puts(ctx, fmt, buf);
}
else if (pdf_is_int(ctx, obj))
{
fz_snprintf(buf, sizeof buf, "%d", pdf_to_int(ctx, obj));
fmt_puts(ctx, fmt, buf);
}
else if (pdf_is_real(ctx, obj))
{
fz_snprintf(buf, sizeof buf, "%g", pdf_to_real(ctx, obj));
fmt_puts(ctx, fmt, buf);
}
else if (pdf_is_string(ctx, obj))
{
unsigned char *str = (unsigned char *)pdf_to_str_buf(ctx, obj);
if (fmt->crypt
|| (fmt->ascii && is_binary_string(ctx, obj))
|| (str[0]==0xff && str[1]==0xfe)
|| (str[0]==0xfe && str[1] == 0xff)
|| is_longer_than_hex(ctx, obj)
)
fmt_hex(ctx, fmt, obj);
else
fmt_str(ctx, fmt, obj);
}
else if (pdf_is_name(ctx, obj))
fmt_name(ctx, fmt, obj);
else if (pdf_is_array(ctx, obj))
fmt_array(ctx, fmt, obj);
else if (pdf_is_dict(ctx, obj))
fmt_dict(ctx, fmt, obj);
else
fmt_puts(ctx, fmt, "<unknown object>");
}
static char *
pdf_sprint_encrypted_obj(fz_context *ctx, char *buf, int cap, int *len, pdf_obj *obj, int tight, int ascii, pdf_crypt *crypt, int num, int gen)
{
struct fmt fmt;
fmt.indent = 0;
fmt.col = 0;
fmt.sep = 0;
fmt.last = 0;
if (!buf || cap == 0)
{
fmt.cap = 1024;
fmt.buf = NULL;
fmt.ptr = fz_malloc(ctx, fmt.cap);
}
else
{
fmt.cap = cap;
fmt.buf = buf;
fmt.ptr = buf;
}
fmt.tight = tight;
fmt.ascii = ascii;
fmt.len = 0;
fmt.crypt = crypt;
fmt.num = num;
fmt.gen = gen;
fmt_obj(ctx, &fmt, obj);
fmt_putc(ctx, &fmt, 0);
return *len = fmt.len-1, fmt.ptr;
}
char *
pdf_sprint_obj(fz_context *ctx, char *buf, int cap, int *len, pdf_obj *obj, int tight, int ascii)
{
return pdf_sprint_encrypted_obj(ctx, buf, cap, len, obj, tight, ascii, NULL, 0, 0);
}
void pdf_print_encrypted_obj(fz_context *ctx, fz_output *out, pdf_obj *obj, int tight, int ascii, pdf_crypt *crypt, int num, int gen)
{
char buf[1024];
char *ptr;
int n;
ptr = pdf_sprint_encrypted_obj(ctx, buf, sizeof buf, &n, obj, tight, ascii,crypt, num, gen);
fz_try(ctx)
fz_write_data(ctx, out, ptr, n);
fz_always(ctx)
if (ptr != buf)
fz_free(ctx, ptr);
fz_catch(ctx)
fz_rethrow(ctx);
}
void pdf_print_obj(fz_context *ctx, fz_output *out, pdf_obj *obj, int tight, int ascii)
{
pdf_print_encrypted_obj(ctx, out, obj, tight, ascii, NULL, 0, 0);
}
static void pdf_debug_encrypted_obj(fz_context *ctx, pdf_obj *obj, int tight, pdf_crypt *crypt, int num, int gen)
{
char buf[1024];
char *ptr;
int n;
int ascii = 1;
ptr = pdf_sprint_encrypted_obj(ctx, buf, sizeof buf, &n, obj, tight, ascii, crypt, num, gen);
fwrite(ptr, 1, n, stdout);
if (ptr != buf)
fz_free(ctx, ptr);
}
void pdf_debug_obj(fz_context *ctx, pdf_obj *obj)
{
pdf_debug_encrypted_obj(ctx, pdf_resolve_indirect(ctx, obj), 0, NULL, 0, 0);
putchar('\n');
}
void pdf_debug_ref(fz_context *ctx, pdf_obj *obj)
{
pdf_debug_encrypted_obj(ctx, obj, 0, NULL, 0, 0);
putchar('\n');
}
int pdf_obj_refs(fz_context *ctx, pdf_obj *obj)
{
if (obj < PDF_LIMIT)
return 0;
return obj->refs;
}
/* Convenience functions */
pdf_obj *
pdf_dict_get_inheritable(fz_context *ctx, pdf_obj *node, pdf_obj *key)
{
pdf_obj *node2 = node;
pdf_obj *val = NULL;
pdf_obj *marked = NULL;
fz_var(node);
fz_var(marked);
fz_try(ctx)
{
do
{
val = pdf_dict_get(ctx, node, key);
if (val)
break;
if (pdf_mark_obj(ctx, node))
fz_throw(ctx, FZ_ERROR_GENERIC, "cycle in tree (parents)");
marked = node;
node = pdf_dict_get(ctx, node, PDF_NAME(Parent));
}
while (node);
}
fz_always(ctx)
{
/* We assume that if we have marked an object, without an exception
* being thrown, that we can always unmark the same object again
* without an exception being thrown. */
if (marked)
{
do
{
pdf_unmark_obj(ctx, node2);
if (node2 == marked)
break;
node2 = pdf_dict_get(ctx, node2, PDF_NAME(Parent));
}
while (node2);
}
}
fz_catch(ctx)
{
fz_rethrow(ctx);
}
return val;
}
void pdf_dict_put_bool(fz_context *ctx, pdf_obj *dict, pdf_obj *key, int x)
{
pdf_dict_put(ctx, dict, key, x ? PDF_TRUE : PDF_FALSE);
}
void pdf_dict_put_int(fz_context *ctx, pdf_obj *dict, pdf_obj *key, int64_t x)
{
pdf_dict_put_drop(ctx, dict, key, pdf_new_int(ctx, x));
}
void pdf_dict_put_real(fz_context *ctx, pdf_obj *dict, pdf_obj *key, double x)
{
pdf_dict_put_drop(ctx, dict, key, pdf_new_real(ctx, x));
}
void pdf_dict_put_name(fz_context *ctx, pdf_obj *dict, pdf_obj *key, const char *x)
{
pdf_dict_put_drop(ctx, dict, key, pdf_new_name(ctx, x));
}
void pdf_dict_put_string(fz_context *ctx, pdf_obj *dict, pdf_obj *key, const char *x, size_t n)
{
pdf_dict_put_drop(ctx, dict, key, pdf_new_string(ctx, x, n));
}
void pdf_dict_put_text_string(fz_context *ctx, pdf_obj *dict, pdf_obj *key, const char *x)
{
pdf_dict_put_drop(ctx, dict, key, pdf_new_text_string(ctx, x));
}
void pdf_dict_put_rect(fz_context *ctx, pdf_obj *dict, pdf_obj *key, fz_rect x)
{
pdf_dict_put_drop(ctx, dict, key, pdf_new_rect(ctx, NULL, x));
}
void pdf_dict_put_matrix(fz_context *ctx, pdf_obj *dict, pdf_obj *key, fz_matrix x)
{
pdf_dict_put_drop(ctx, dict, key, pdf_new_matrix(ctx, NULL, x));
}
pdf_obj *pdf_dict_put_array(fz_context *ctx, pdf_obj *dict, pdf_obj *key, int initial)
{
pdf_obj *obj = pdf_new_array(ctx, pdf_get_bound_document(ctx, dict), initial);
pdf_dict_put_drop(ctx, dict, key, obj);
return obj;
}
pdf_obj *pdf_dict_put_dict(fz_context *ctx, pdf_obj *dict, pdf_obj *key, int initial)
{
pdf_obj *obj = pdf_new_dict(ctx, pdf_get_bound_document(ctx, dict), initial);
pdf_dict_put_drop(ctx, dict, key, obj);
return obj;
}
pdf_obj *pdf_dict_puts_dict(fz_context *ctx, pdf_obj *dict, const char *key, int initial)
{
pdf_obj *obj = pdf_new_dict(ctx, pdf_get_bound_document(ctx, dict), initial);
pdf_dict_puts_drop(ctx, dict, key, obj);
return obj;
}
void pdf_array_push_bool(fz_context *ctx, pdf_obj *array, int x)
{
pdf_array_push(ctx, array, x ? PDF_TRUE : PDF_FALSE);
}
void pdf_array_push_int(fz_context *ctx, pdf_obj *array, int64_t x)
{
pdf_array_push_drop(ctx, array, pdf_new_int(ctx, x));
}
void pdf_array_push_real(fz_context *ctx, pdf_obj *array, double x)
{
pdf_array_push_drop(ctx, array, pdf_new_real(ctx, x));
}
void pdf_array_push_name(fz_context *ctx, pdf_obj *array, const char *x)
{
pdf_array_push_drop(ctx, array, pdf_new_name(ctx, x));
}
void pdf_array_push_string(fz_context *ctx, pdf_obj *array, const char *x, size_t n)
{
pdf_array_push_drop(ctx, array, pdf_new_string(ctx, x, n));
}
void pdf_array_push_text_string(fz_context *ctx, pdf_obj *array, const char *x)
{
pdf_array_push_drop(ctx, array, pdf_new_text_string(ctx, x));
}
pdf_obj *pdf_array_push_array(fz_context *ctx, pdf_obj *array, int initial)
{
pdf_obj *obj = pdf_new_array(ctx, pdf_get_bound_document(ctx, array), initial);
pdf_array_push_drop(ctx, array, obj);
return obj;
}
pdf_obj *pdf_array_push_dict(fz_context *ctx, pdf_obj *array, int initial)
{
pdf_obj *obj = pdf_new_dict(ctx, pdf_get_bound_document(ctx, array), initial);
pdf_array_push_drop(ctx, array, obj);
return obj;
}
int pdf_dict_get_bool(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
{
return pdf_to_bool(ctx, pdf_dict_get(ctx, dict, key));
}
int pdf_dict_get_int(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
{
return pdf_to_int(ctx, pdf_dict_get(ctx, dict, key));
}
float pdf_dict_get_real(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
{
return pdf_to_real(ctx, pdf_dict_get(ctx, dict, key));
}
const char *pdf_dict_get_name(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
{
return pdf_to_name(ctx, pdf_dict_get(ctx, dict, key));
}
const char *pdf_dict_get_string(fz_context *ctx, pdf_obj *dict, pdf_obj *key, size_t *sizep)
{
return pdf_to_string(ctx, pdf_dict_get(ctx, dict, key), sizep);
}
const char *pdf_dict_get_text_string(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
{
return pdf_to_text_string(ctx, pdf_dict_get(ctx, dict, key));
}
fz_rect pdf_dict_get_rect(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
{
return pdf_to_rect(ctx, pdf_dict_get(ctx, dict, key));
}
fz_matrix pdf_dict_get_matrix(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
{
return pdf_to_matrix(ctx, pdf_dict_get(ctx, dict, key));
}
int pdf_array_get_bool(fz_context *ctx, pdf_obj *array, int index)
{
return pdf_to_bool(ctx, pdf_array_get(ctx, array, index));
}
int pdf_array_get_int(fz_context *ctx, pdf_obj *array, int index)
{
return pdf_to_int(ctx, pdf_array_get(ctx, array, index));
}
float pdf_array_get_real(fz_context *ctx, pdf_obj *array, int index)
{
return pdf_to_real(ctx, pdf_array_get(ctx, array, index));
}
const char *pdf_array_get_name(fz_context *ctx, pdf_obj *array, int index)
{
return pdf_to_name(ctx, pdf_array_get(ctx, array, index));
}
const char *pdf_array_get_string(fz_context *ctx, pdf_obj *array, int index, size_t *sizep)
{
return pdf_to_string(ctx, pdf_array_get(ctx, array, index), sizep);
}
const char *pdf_array_get_text_string(fz_context *ctx, pdf_obj *array, int index)
{
return pdf_to_text_string(ctx, pdf_array_get(ctx, array, index));
}
fz_rect pdf_array_get_rect(fz_context *ctx, pdf_obj *array, int index)
{
return pdf_to_rect(ctx, pdf_array_get(ctx, array, index));
}
fz_matrix pdf_array_get_matrix(fz_context *ctx, pdf_obj *array, int index)
{
return pdf_to_matrix(ctx, pdf_array_get(ctx, array, index));
}