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; }
static void gatherforms(int page, pdf_obj *pageref, pdf_obj *pageobj, pdf_obj *dict) { int i, n; n = pdf_dict_len(dict); for (i = 0; i < n; i++) { pdf_obj *xobjdict; pdf_obj *type; pdf_obj *subtype; pdf_obj *group; pdf_obj *groupsubtype; pdf_obj *reference; int k; xobjdict = pdf_dict_get_val(dict, i); if (!pdf_is_dict(xobjdict)) { fz_warn(ctx, "not a xobject dict (%d %d R)", pdf_to_num(xobjdict), pdf_to_gen(xobjdict)); continue; } type = pdf_dict_gets(xobjdict, "Subtype"); if (strcmp(pdf_to_name(type), "Form")) continue; subtype = pdf_dict_gets(xobjdict, "Subtype2"); if (!strcmp(pdf_to_name(subtype), "PS")) continue; group = pdf_dict_gets(xobjdict, "Group"); groupsubtype = pdf_dict_gets(group, "S"); reference = pdf_dict_gets(xobjdict, "Ref"); for (k = 0; k < forms; k++) if (!pdf_objcmp(form[k].u.form.obj, xobjdict)) break; if (k < forms) continue; form = fz_resize_array(ctx, form, forms+1, sizeof(struct info)); forms++; form[forms - 1].page = page; form[forms - 1].pageref = pageref; form[forms - 1].pageobj = pageobj; form[forms - 1].u.form.obj = xobjdict; form[forms - 1].u.form.groupsubtype = groupsubtype; form[forms - 1].u.form.reference = reference; } }
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; }
/* ** If key doesn't exist, puts key,value pair. ** If key does exist, converts by merging (if dict) or adding (if array) */ static void wmupdf_dict_merge_keyval(fz_context *ctx,pdf_obj *dstdict,pdf_obj *key,pdf_obj *value) { pdf_obj *dstval; dstval=pdf_dict_get(dstdict,key); if (!dstval) { pdf_dict_put(dstdict,key,value); return; } /* Values are same--no action required */ if (!pdf_objcmp(dstval,value)) return; if (pdf_is_dict(dstval) && pdf_is_dict(value)) { static char *okay_to_merge[] = {"Resources","XObject",""}; int i; for (i=0;okay_to_merge[i][0]!='\0';i++) if (!stricmp(okay_to_merge[i],pdf_to_name(key))) break; if (okay_to_merge[i][0]!='\0') { /* Merge source dict into dest dict */ wmupdf_dict_merge(ctx,pdf_to_name(key),dstval,value); pdf_dict_put(dstdict,key,dstval); } else /* Just overwrite dest dict with source dict */ pdf_dict_put(dstdict,key,value); return; } /* This works for ProcSet array, but maybe not for any array (e.g. rectangle) */ if (pdf_is_array(dstval) && pdf_is_array(value)) { wmupdf_array_merge(ctx,pdf_to_name(key),dstval,value); return; } /* Last resort: overwrite with new value */ pdf_dict_put(dstdict,key,value); /* This does NOT work--you can't just convert the value into an array of values */ /* PDF will become non-conformant. */ /* array=pdf_new_array(ctx,2); pdf_array_push(array,dstval); pdf_array_push(array,value); pdf_dict_put(dstdict,key,array); pdf_drop_obj(array); */ }
static void gatherfonts(int page, pdf_obj *pageref, pdf_obj *pageobj, pdf_obj *dict) { int i, n; n = pdf_dict_len(dict); for (i = 0; i < n; i++) { pdf_obj *fontdict = NULL; pdf_obj *subtype = NULL; pdf_obj *basefont = NULL; pdf_obj *name = NULL; int k; fontdict = pdf_dict_get_val(dict, i); if (!pdf_is_dict(fontdict)) { fz_warn(ctx, "not a font dict (%d %d R)", pdf_to_num(fontdict), pdf_to_gen(fontdict)); continue; } subtype = pdf_dict_gets(fontdict, "Subtype"); basefont = pdf_dict_gets(fontdict, "BaseFont"); if (!basefont || pdf_is_null(basefont)) name = pdf_dict_gets(fontdict, "Name"); for (k = 0; k < fonts; k++) if (!pdf_objcmp(font[k].u.font.obj, fontdict)) break; if (k < fonts) continue; font = fz_resize_array(ctx, font, fonts+1, sizeof(struct info)); fonts++; font[fonts - 1].page = page; font[fonts - 1].pageref = pageref; font[fonts - 1].pageobj = pageobj; font[fonts - 1].u.font.obj = fontdict; font[fonts - 1].u.font.subtype = subtype; font[fonts - 1].u.font.name = basefont ? basefont : name; } }
static void gathershadings(int page, pdf_obj *pageref, pdf_obj *pageobj, pdf_obj *dict) { int i, n; n = pdf_dict_len(dict); for (i = 0; i < n; i++) { pdf_obj *shade; pdf_obj *type; int k; shade = pdf_dict_get_val(dict, i); if (!pdf_is_dict(shade)) { fz_warn(ctx, "not a shading dict (%d %d R)", pdf_to_num(shade), pdf_to_gen(shade)); continue; } type = pdf_dict_gets(shade, "ShadingType"); if (!pdf_is_int(type) || pdf_to_int(type) < 1 || pdf_to_int(type) > 7) { fz_warn(ctx, "not a shading type (%d %d R)", pdf_to_num(shade), pdf_to_gen(shade)); type = NULL; } for (k = 0; k < shadings; k++) if (!pdf_objcmp(shading[k].u.shading.obj, shade)) break; if (k < shadings) continue; shading = fz_resize_array(ctx, shading, shadings+1, sizeof(struct info)); shadings++; shading[shadings - 1].page = page; shading[shadings - 1].pageref = pageref; shading[shadings - 1].pageobj = pageobj; shading[shadings - 1].u.shading.obj = shade; shading[shadings - 1].u.shading.type = type; } }
static void gatherpsobjs(int page, pdf_obj *pageref, pdf_obj *pageobj, pdf_obj *dict) { int i, n; n = pdf_dict_len(dict); for (i = 0; i < n; i++) { pdf_obj *xobjdict; pdf_obj *type; pdf_obj *subtype; int k; xobjdict = pdf_dict_get_val(dict, i); if (!pdf_is_dict(xobjdict)) { fz_warn(ctx, "not a xobject dict (%d %d R)", pdf_to_num(xobjdict), pdf_to_gen(xobjdict)); continue; } type = pdf_dict_gets(xobjdict, "Subtype"); subtype = pdf_dict_gets(xobjdict, "Subtype2"); if (strcmp(pdf_to_name(type), "PS") && (strcmp(pdf_to_name(type), "Form") || strcmp(pdf_to_name(subtype), "PS"))) continue; for (k = 0; k < psobjs; k++) if (!pdf_objcmp(psobj[k].u.form.obj, xobjdict)) break; if (k < psobjs) continue; psobj = fz_resize_array(ctx, psobj, psobjs+1, sizeof(struct info)); psobjs++; psobj[psobjs - 1].page = page; psobj[psobjs - 1].pageref = pageref; psobj[psobjs - 1].pageobj = pageobj; psobj[psobjs - 1].u.form.obj = xobjdict; } }
static int find_position(fz_context *ctx, pdf_obj *container, pdf_obj *key, pdf_obj *val, void *arg) { find_data *data = (find_data *)arg; if (data->found) return 0; data->entry++; if (data->last != container) { data->last = container; data->last_index = 0; } else data->last_index++; if (pdf_objcmp(ctx, key, data->key) > 0) { data->found = container; data->found_index = data->last_index; } return 0; }
static void gatherimages(int page, pdf_obj *pageref, pdf_obj *pageobj, pdf_obj *dict) { int i, n; n = pdf_dict_len(dict); for (i = 0; i < n; i++) { pdf_obj *imagedict; pdf_obj *type; pdf_obj *width; pdf_obj *height; pdf_obj *bpc = NULL; pdf_obj *filter = NULL; pdf_obj *cs = NULL; pdf_obj *altcs; int k; imagedict = pdf_dict_get_val(dict, i); if (!pdf_is_dict(imagedict)) { fz_warn(ctx, "not an image dict (%d %d R)", pdf_to_num(imagedict), pdf_to_gen(imagedict)); continue; } type = pdf_dict_gets(imagedict, "Subtype"); if (strcmp(pdf_to_name(type), "Image")) continue; filter = pdf_dict_gets(imagedict, "Filter"); altcs = NULL; cs = pdf_dict_gets(imagedict, "ColorSpace"); if (pdf_is_array(cs)) { pdf_obj *cses = cs; cs = pdf_array_get(cses, 0); if (pdf_is_name(cs) && (!strcmp(pdf_to_name(cs), "DeviceN") || !strcmp(pdf_to_name(cs), "Separation"))) { altcs = pdf_array_get(cses, 2); if (pdf_is_array(altcs)) altcs = pdf_array_get(altcs, 0); } } width = pdf_dict_gets(imagedict, "Width"); height = pdf_dict_gets(imagedict, "Height"); bpc = pdf_dict_gets(imagedict, "BitsPerComponent"); for (k = 0; k < images; k++) if (!pdf_objcmp(image[k].u.image.obj, imagedict)) break; if (k < images) continue; image = fz_resize_array(ctx, image, images+1, sizeof(struct info)); images++; image[images - 1].page = page; image[images - 1].pageref = pageref; image[images - 1].pageobj = pageobj; image[images - 1].u.image.obj = imagedict; image[images - 1].u.image.width = width; image[images - 1].u.image.height = height; image[images - 1].u.image.bpc = bpc; image[images - 1].u.image.filter = filter; image[images - 1].u.image.cs = cs; image[images - 1].u.image.altcs = altcs; } }
static int pdf_cmp_key(fz_context *ctx, void *k0, void *k1) { return pdf_objcmp(ctx, (pdf_obj *)k0, (pdf_obj *)k1); }
static pdf_obj * pdf_lookup_name_imp(fz_context *ctx, pdf_obj *node, pdf_obj *needle) { pdf_obj *kids = pdf_dict_gets(node, "Kids"); pdf_obj *names = pdf_dict_gets(node, "Names"); if (pdf_is_array(kids)) { int l = 0; int r = pdf_array_len(kids) - 1; while (l <= r) { int m = (l + r) >> 1; pdf_obj *kid = pdf_array_get(kids, m); pdf_obj *limits = pdf_dict_gets(kid, "Limits"); pdf_obj *first = pdf_array_get(limits, 0); pdf_obj *last = pdf_array_get(limits, 1); if (pdf_objcmp(needle, first) < 0) r = m - 1; else if (pdf_objcmp(needle, last) > 0) l = m + 1; else { pdf_obj *obj; if (pdf_obj_mark(node)) break; obj = pdf_lookup_name_imp(ctx, kid, needle); pdf_obj_unmark(node); return obj; } } } if (pdf_is_array(names)) { int l = 0; int r = (pdf_array_len(names) / 2) - 1; while (l <= r) { int m = (l + r) >> 1; int c; pdf_obj *key = pdf_array_get(names, m * 2); pdf_obj *val = pdf_array_get(names, m * 2 + 1); c = pdf_objcmp(needle, key); if (c < 0) r = m - 1; else if (c > 0) l = m + 1; else return val; } /* Spec says names should be sorted (hence the binary search, * above), but Acrobat copes with non-sorted. Drop back to a * simple search if the binary search fails. */ r = pdf_array_len(names)/2; for (l = 0; l < r; l++) if (!pdf_objcmp(needle, pdf_array_get(names, l * 2))) return pdf_array_get(names, l * 2 + 1); } return NULL; }
static void gatherresourceinfo(int page, pdf_obj *rsrc) { pdf_obj *pageobj; pdf_obj *pageref; pdf_obj *font; pdf_obj *xobj; pdf_obj *shade; pdf_obj *pattern; pdf_obj *subrsrc; int i; pageobj = xref->page_objs[page-1]; pageref = xref->page_refs[page-1]; if (!pageobj) fz_throw(ctx, "cannot retrieve info from page %d", page); font = pdf_dict_gets(rsrc, "Font"); if (font) { int n; gatherfonts(page, pageref, pageobj, font); n = pdf_dict_len(font); for (i = 0; i < n; i++) { pdf_obj *obj = pdf_dict_get_val(font, i); subrsrc = pdf_dict_gets(obj, "Resources"); if (subrsrc && pdf_objcmp(rsrc, subrsrc)) gatherresourceinfo(page, subrsrc); } } xobj = pdf_dict_gets(rsrc, "XObject"); if (xobj) { int n; gatherimages(page, pageref, pageobj, xobj); gatherforms(page, pageref, pageobj, xobj); gatherpsobjs(page, pageref, pageobj, xobj); n = pdf_dict_len(xobj); for (i = 0; i < n; i++) { pdf_obj *obj = pdf_dict_get_val(xobj, i); subrsrc = pdf_dict_gets(obj, "Resources"); if (subrsrc && pdf_objcmp(rsrc, subrsrc)) gatherresourceinfo(page, subrsrc); } } shade = pdf_dict_gets(rsrc, "Shading"); if (shade) gathershadings(page, pageref, pageobj, shade); pattern = pdf_dict_gets(rsrc, "Pattern"); if (pattern) { int n; gatherpatterns(page, pageref, pageobj, pattern); n = pdf_dict_len(pattern); for (i = 0; i < n; i++) { pdf_obj *obj = pdf_dict_get_val(pattern, i); subrsrc = pdf_dict_gets(obj, "Resources"); if (subrsrc && pdf_objcmp(rsrc, subrsrc)) gatherresourceinfo(page, subrsrc); } } }
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 int pdf_cmp_key(void *k0, void *k1) { return pdf_objcmp((pdf_obj *)k0, (pdf_obj *)k1); }
static void gatherpatterns(int page, pdf_obj *pageref, pdf_obj *pageobj, pdf_obj *dict) { int i, n; n = pdf_dict_len(dict); for (i = 0; i < n; i++) { pdf_obj *patterndict; pdf_obj *type; pdf_obj *paint = NULL; pdf_obj *tiling = NULL; pdf_obj *shading = NULL; int k; patterndict = pdf_dict_get_val(dict, i); if (!pdf_is_dict(patterndict)) { fz_warn(ctx, "not a pattern dict (%d %d R)", pdf_to_num(patterndict), pdf_to_gen(patterndict)); continue; } type = pdf_dict_gets(patterndict, "PatternType"); if (!pdf_is_int(type) || pdf_to_int(type) < 1 || pdf_to_int(type) > 2) { fz_warn(ctx, "not a pattern type (%d %d R)", pdf_to_num(patterndict), pdf_to_gen(patterndict)); type = NULL; } if (pdf_to_int(type) == 1) { paint = pdf_dict_gets(patterndict, "PaintType"); if (!pdf_is_int(paint) || pdf_to_int(paint) < 1 || pdf_to_int(paint) > 2) { fz_warn(ctx, "not a pattern paint type (%d %d R)", pdf_to_num(patterndict), pdf_to_gen(patterndict)); paint = NULL; } tiling = pdf_dict_gets(patterndict, "TilingType"); if (!pdf_is_int(tiling) || pdf_to_int(tiling) < 1 || pdf_to_int(tiling) > 3) { fz_warn(ctx, "not a pattern tiling type (%d %d R)", pdf_to_num(patterndict), pdf_to_gen(patterndict)); tiling = NULL; } } else { shading = pdf_dict_gets(patterndict, "Shading"); } for (k = 0; k < patterns; k++) if (!pdf_objcmp(pattern[k].u.pattern.obj, patterndict)) break; if (k < patterns) continue; pattern = fz_resize_array(ctx, pattern, patterns+1, sizeof(struct info)); patterns++; pattern[patterns - 1].page = page; pattern[patterns - 1].pageref = pageref; pattern[patterns - 1].pageobj = pageobj; pattern[patterns - 1].u.pattern.obj = patterndict; pattern[patterns - 1].u.pattern.type = type; pattern[patterns - 1].u.pattern.paint = paint; pattern[patterns - 1].u.pattern.tiling = tiling; pattern[patterns - 1].u.pattern.shading = shading; } }
int pdf_objcmp(fz_context *ctx, pdf_obj *a, pdf_obj *b) { int i; if (a == b) return 0; if (!a || !b) return 1; if (a < PDF_OBJ_NAME__LIMIT) { if (b < PDF_OBJ_NAME__LIMIT) return a != b; if (b < PDF_OBJ__LIMIT) return 1; if (b->kind != PDF_NAME) return 1; return strcmp(NAME(b)->n, PDF_NAMES[(intptr_t)a]); } if (b < PDF_OBJ_NAME__LIMIT) { if (a < PDF_OBJ__LIMIT) return 1; if (a->kind != PDF_NAME) return 1; return strcmp(NAME(a)->n, PDF_NAMES[(intptr_t)b]); } if (a < PDF_OBJ__LIMIT || b < PDF_OBJ__LIMIT) return a != b; 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_objcmp_resolve(fz_context *ctx, pdf_obj *a, pdf_obj *b) { RESOLVE(a); RESOLVE(b); return pdf_objcmp(ctx, a, b); }
static void gatherresourceinfo(int page, pdf_obj *rsrc, int show) { pdf_obj *pageobj; pdf_obj *pageref; pdf_obj *font; pdf_obj *xobj; pdf_obj *shade; pdf_obj *pattern; pdf_obj *subrsrc; int i; pageref = pdf_lookup_page_obj(doc, page-1); pageobj = pdf_resolve_indirect(pageref); if (!pageobj) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot retrieve info from page %d", page); font = pdf_dict_gets(rsrc, "Font"); if (show & FONTS && font) { int n; gatherfonts(page, pageref, pageobj, font); n = pdf_dict_len(font); for (i = 0; i < n; i++) { pdf_obj *obj = pdf_dict_get_val(font, i); subrsrc = pdf_dict_gets(obj, "Resources"); if (subrsrc && pdf_objcmp(rsrc, subrsrc)) gatherresourceinfo(page, subrsrc, show); } } xobj = pdf_dict_gets(rsrc, "XObject"); if (show & XOBJS && xobj) { int n; gatherimages(page, pageref, pageobj, xobj); gatherforms(page, pageref, pageobj, xobj); gatherpsobjs(page, pageref, pageobj, xobj); n = pdf_dict_len(xobj); for (i = 0; i < n; i++) { pdf_obj *obj = pdf_dict_get_val(xobj, i); subrsrc = pdf_dict_gets(obj, "Resources"); if (subrsrc && pdf_objcmp(rsrc, subrsrc)) gatherresourceinfo(page, subrsrc, show); } } shade = pdf_dict_gets(rsrc, "Shading"); if (show & SHADINGS && shade) gathershadings(page, pageref, pageobj, shade); pattern = pdf_dict_gets(rsrc, "Pattern"); if (show & PATTERNS && pattern) { int n; gatherpatterns(page, pageref, pageobj, pattern); n = pdf_dict_len(pattern); for (i = 0; i < n; i++) { pdf_obj *obj = pdf_dict_get_val(pattern, i); subrsrc = pdf_dict_gets(obj, "Resources"); if (subrsrc && pdf_objcmp(rsrc, subrsrc)) gatherresourceinfo(page, subrsrc, show); } } }
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; }