static int strip_outlines(fz_context *ctx, pdf_document *doc, pdf_obj *outlines, int page_count, int *page_object_nums, pdf_obj *names_list) { int nc; pdf_obj *first; pdf_obj *last; first = pdf_dict_get(ctx, outlines, PDF_NAME_First); if (first == NULL) nc = 0; else nc = strip_outline(ctx, doc, first, page_count, page_object_nums, names_list, &first, &last); if (nc == 0) { pdf_dict_del(ctx, outlines, PDF_NAME_First); pdf_dict_del(ctx, outlines, PDF_NAME_Last); pdf_dict_del(ctx, outlines, PDF_NAME_Count); } else { int old_count = pdf_to_int(ctx, pdf_dict_get(ctx, outlines, PDF_NAME_Count)); pdf_dict_put(ctx, outlines, PDF_NAME_First, first); pdf_dict_put(ctx, outlines, PDF_NAME_Last, last); pdf_dict_put(ctx, outlines, PDF_NAME_Count, pdf_new_int(ctx, doc, old_count > 0 ? nc : -nc)); } return nc; }
static void pdf_load_name_tree_imp(pdf_obj *dict, pdf_document *xref, pdf_obj *node) { fz_context *ctx = xref->ctx; pdf_obj *kids = pdf_dict_gets(node, "Kids"); pdf_obj *names = pdf_dict_gets(node, "Names"); int i; if (kids && !pdf_dict_mark(node)) { for (i = 0; i < pdf_array_len(kids); i++) pdf_load_name_tree_imp(dict, xref, pdf_array_get(kids, i)); pdf_dict_unmark(node); } if (names) { for (i = 0; i + 1 < pdf_array_len(names); i += 2) { pdf_obj *key = pdf_array_get(names, i); pdf_obj *val = pdf_array_get(names, i + 1); if (pdf_is_string(key)) { key = pdf_to_utf8_name(ctx, key); pdf_dict_put(dict, key, val); pdf_drop_obj(key); } else if (pdf_is_name(key)) { pdf_dict_put(dict, key, val); } } } }
void pdf_set_portfolio_entry_info(fz_context *ctx, pdf_document *doc, int entry, int schema_entry, pdf_obj *data) { pdf_portfolio *p; pdf_obj *obj, *lookup; int ef = 0; if (!doc) fz_throw(ctx, FZ_ERROR_GENERIC, "Bad pdf_add_portfolio_entry call"); if (doc->portfolio == NULL) load_portfolio(ctx, doc); obj = pdf_portfolio_entry_obj_name(ctx, doc, entry, NULL); if (!obj) fz_throw(ctx, FZ_ERROR_GENERIC, "Can't set info on non existent portfolio entry"); for (p = doc->portfolio; p != NULL && schema_entry > 0; p = p->next, schema_entry--); if (schema_entry) fz_throw(ctx, FZ_ERROR_GENERIC, "schema_entry out of range"); switch (p->entry.type) { default: case PDF_SCHEMA_TEXT: case PDF_SCHEMA_DATE: case PDF_SCHEMA_NUMBER: lookup = NULL; break; case PDF_SCHEMA_FILENAME: lookup = PDF_NAME_UF; break; case PDF_SCHEMA_DESC: lookup = PDF_NAME_Desc; break; case PDF_SCHEMA_MODDATE: lookup = PDF_NAME_ModDate; ef = 1; break; case PDF_SCHEMA_CREATIONDATE: lookup = PDF_NAME_CreationDate; ef = 1; break; case PDF_SCHEMA_SIZE: fz_throw(ctx, FZ_ERROR_GENERIC, "Can't set size!"); break; } if (lookup) { if (ef) obj = pdf_dict_getl(ctx, obj, PDF_NAME_EF, PDF_NAME_F, PDF_NAME_Params, NULL); pdf_dict_put(ctx, obj, lookup, data); if (lookup == PDF_NAME_UF) pdf_dict_put(ctx, obj, PDF_NAME_F, data); return; } pdf_dict_putl(ctx, obj, data, PDF_NAME_CI, p->key, NULL); }
/* ** 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 update_field_value(fz_context *ctx, pdf_document *doc, pdf_obj *obj, const char *text) { pdf_obj *sobj = NULL; pdf_obj *grp; if (!text) text = ""; /* All fields of the same name should be updated, so * set the value at the head of the group */ grp = find_head_of_field_group(ctx, obj); if (grp) obj = grp; fz_var(sobj); fz_try(ctx) { sobj = pdf_new_string(ctx, doc, text, strlen(text)); pdf_dict_put(ctx, obj, PDF_NAME_V, sobj); } fz_always(ctx) { pdf_drop_obj(ctx, sobj); } fz_catch(ctx) { fz_rethrow(ctx); } pdf_field_mark_dirty(ctx, doc, obj); }
static void intersect_box(fz_context *ctx, pdf_document *doc, pdf_obj *page, pdf_obj *box_name, const fz_rect *mb) { pdf_obj *box = pdf_dict_get(ctx, page, box_name); pdf_obj *newbox; fz_rect old_rect; if (box == NULL) return; old_rect.x0 = pdf_to_real(ctx, pdf_array_get(ctx, box, 0)); old_rect.y0 = pdf_to_real(ctx, pdf_array_get(ctx, box, 1)); old_rect.x1 = pdf_to_real(ctx, pdf_array_get(ctx, box, 2)); old_rect.y1 = pdf_to_real(ctx, pdf_array_get(ctx, box, 3)); if (old_rect.x0 < mb->x0) old_rect.x0 = mb->x0; if (old_rect.y0 < mb->y0) old_rect.y0 = mb->y0; if (old_rect.x1 > mb->x1) old_rect.x1 = mb->x1; if (old_rect.y1 > mb->y1) old_rect.y1 = mb->y1; newbox = pdf_new_array(ctx, doc, 4); pdf_array_push(ctx, newbox, pdf_new_real(ctx, doc, old_rect.x0)); pdf_array_push(ctx, newbox, pdf_new_real(ctx, doc, old_rect.y0)); pdf_array_push(ctx, newbox, pdf_new_real(ctx, doc, old_rect.x1)); pdf_array_push(ctx, newbox, pdf_new_real(ctx, doc, old_rect.y1)); pdf_dict_put(ctx, page, box_name, newbox); }
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; }
static void pdf_clean_stream_object(fz_context *ctx, pdf_document *doc, pdf_obj *obj, pdf_obj *orig_res, fz_cookie *cookie, int own_res) { pdf_processor *proc_buffer = NULL; pdf_processor *proc_filter = NULL; pdf_obj *res = NULL; pdf_obj *ref = NULL; fz_buffer *buffer; if (!obj) return; fz_var(res); fz_var(ref); fz_var(proc_buffer); fz_var(proc_filter); buffer = fz_new_buffer(ctx, 1024); fz_try(ctx) { if (own_res) { pdf_obj *r = pdf_dict_get(ctx, obj, PDF_NAME_Resources); if (r) orig_res = r; } res = pdf_new_dict(ctx, doc, 1); proc_buffer = pdf_new_buffer_processor(ctx, buffer); proc_filter = pdf_new_filter_processor(ctx, proc_buffer, doc, orig_res, res); pdf_process_contents(ctx, proc_filter, doc, orig_res, obj, cookie); pdf_update_stream(ctx, doc, obj, buffer, 0); if (own_res) { ref = pdf_new_ref(ctx, doc, res); pdf_dict_put(ctx, obj, PDF_NAME_Resources, ref); } } fz_always(ctx) { pdf_drop_processor(ctx, proc_filter); pdf_drop_processor(ctx, proc_buffer); fz_drop_buffer(ctx, buffer); pdf_drop_obj(ctx, res); pdf_drop_obj(ctx, ref); } fz_catch(ctx) { fz_rethrow_message(ctx, "Failed while cleaning xobject"); } }
static void retainpage(fz_context *ctx, pdf_document *doc, pdf_obj *parent, pdf_obj *kids, int page) { pdf_obj *pageref = pdf_lookup_page_obj(ctx, doc, page-1); pdf_obj *pageobj = pdf_resolve_indirect(ctx, pageref); pdf_dict_put(ctx, pageobj, PDF_NAME_Parent, parent); /* Store page object in new kids array */ pdf_array_push(ctx, kids, pageref); }
void pdf_update_xobject(fz_context *ctx, pdf_document *doc, pdf_obj *form, fz_rect bbox, fz_matrix matrix, pdf_obj *res, fz_buffer *contents) { pdf_dict_put_rect(ctx, form, PDF_NAME(BBox), bbox); pdf_dict_put_matrix(ctx, form, PDF_NAME(Matrix), matrix); if (res) pdf_dict_put(ctx, form, PDF_NAME(Resources), res); else pdf_dict_del(ctx, form, PDF_NAME(Resources)); pdf_update_stream(ctx, doc, form, contents, 0); }
static void retainpage(fz_context *ctx, pdf_document *doc, pdf_obj *parent, pdf_obj *kids, int page) { pdf_obj *pageref = pdf_lookup_page_obj(ctx, doc, page-1); pdf_flatten_inheritable_page_items(ctx, pageref); pdf_dict_put(ctx, pageref, PDF_NAME_Parent, parent); /* Store page object in new kids array */ pdf_array_push(ctx, kids, pageref); }
pdf_obj * pdf_new_xobject(fz_context *ctx, pdf_document *doc, fz_rect bbox, fz_matrix matrix, pdf_obj *res, fz_buffer *contents) { pdf_obj *ind = NULL; pdf_obj *form = pdf_new_dict(ctx, doc, 5); fz_try(ctx) { pdf_dict_put(ctx, form, PDF_NAME(Type), PDF_NAME(XObject)); pdf_dict_put(ctx, form, PDF_NAME(Subtype), PDF_NAME(Form)); pdf_dict_put_rect(ctx, form, PDF_NAME(BBox), bbox); pdf_dict_put_matrix(ctx, form, PDF_NAME(Matrix), matrix); if (res) pdf_dict_put(ctx, form, PDF_NAME(Resources), res); ind = pdf_add_stream(ctx, doc, contents, form, 0); } fz_always(ctx) pdf_drop_obj(ctx, form); fz_catch(ctx) fz_rethrow(ctx); return ind; }
static void page_merge(int page_from, int page_to, pdf_graft_map *graft_map) { pdf_obj *page_ref; pdf_obj *page_dict; pdf_obj *obj; pdf_obj *ref = NULL; int i; /* Copy as few key/value pairs as we can. Do not include items that reference other pages. */ static pdf_obj * const copy_list[] = { PDF_NAME(Contents), PDF_NAME(Resources), PDF_NAME(MediaBox), PDF_NAME(CropBox), PDF_NAME(BleedBox), PDF_NAME(TrimBox), PDF_NAME(ArtBox), PDF_NAME(Rotate), PDF_NAME(UserUnit) }; fz_var(ref); fz_try(ctx) { page_ref = pdf_lookup_page_obj(ctx, doc_src, page_from - 1); pdf_flatten_inheritable_page_items(ctx, page_ref); /* Make a new page object dictionary to hold the items we copy from the source page. */ page_dict = pdf_new_dict(ctx, doc_des, 4); pdf_dict_put(ctx, page_dict, PDF_NAME(Type), PDF_NAME(Page)); for (i = 0; i < nelem(copy_list); i++) { obj = pdf_dict_get(ctx, page_ref, copy_list[i]); if (obj != NULL) pdf_dict_put_drop(ctx, page_dict, copy_list[i], pdf_graft_mapped_object(ctx, graft_map, obj)); } /* Add the page object to the destination document. */ ref = pdf_add_object_drop(ctx, doc_des, page_dict); /* Insert it into the page tree. */ pdf_insert_page(ctx, doc_des, page_to - 1, ref); } fz_always(ctx) { pdf_drop_obj(ctx, ref); } fz_catch(ctx) { fz_rethrow(ctx); } }
pdf_obj * pdf_copy_dict(fz_context *ctx, pdf_obj *obj) { pdf_obj *dict; int i, n; RESOLVE(obj); if (!obj) return NULL; /* Can't warn :( */ if (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++) pdf_dict_put(dict, pdf_dict_get_key(obj, i), pdf_dict_get_val(obj, i)); return dict; }
void pdf_rename_portfolio_schema(fz_context *ctx, pdf_document *doc, int entry, const char *name, int name_len) { pdf_portfolio *p; pdf_obj *s; if (!doc) fz_throw(ctx, FZ_ERROR_GENERIC, "Bad pdf_rename_portfolio_schema call"); if (doc->portfolio == NULL) load_portfolio(ctx, doc); p = doc->portfolio; while (p && entry > 0) p = p->next, entry--; if (p == NULL || entry) fz_throw(ctx, FZ_ERROR_GENERIC, "entry out of range in pdf_rename_portfolio_schema"); s = pdf_new_string(ctx, doc, name, name_len); pdf_drop_obj(ctx, p->entry.name); p->entry.name = s; pdf_dict_put(ctx, p->val, PDF_NAME_N, s); }
pdf_obj * pdf_copy_dict(fz_context *ctx, pdf_obj *obj) { pdf_obj *dict; int i, n; RESOLVE(obj); if (obj >= PDF_OBJ__LIMIT) { pdf_document *doc = DICT(obj)->doc; if (obj->kind != PDF_DICT) fz_warn(ctx, "assert: not a dict (%s)", pdf_objkindstr(obj)); n = pdf_dict_len(ctx, obj); dict = pdf_new_dict(ctx, doc, n); 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)); return dict; } return NULL; /* Can't warn :( */ }
static void pdf_clean_type3(fz_context *ctx, pdf_document *doc, pdf_obj *obj, pdf_obj *orig_res, fz_cookie *cookie, int sanitize, int ascii) { pdf_processor *proc_buffer = NULL; pdf_processor *proc_filter = NULL; pdf_obj *res = NULL; pdf_obj *ref; pdf_obj *charprocs; int i, l; fz_var(res); fz_var(proc_buffer); fz_var(proc_filter); fz_try(ctx) { res = pdf_dict_get(ctx, obj, PDF_NAME(Resources)); if (res) orig_res = res; res = NULL; res = pdf_new_dict(ctx, doc, 1); charprocs = pdf_dict_get(ctx, obj, PDF_NAME(CharProcs)); l = pdf_dict_len(ctx, charprocs); for (i = 0; i < l; i++) { pdf_obj *val = pdf_dict_get_val(ctx, charprocs, i); fz_buffer *buffer = fz_new_buffer(ctx, 1024); fz_try(ctx) { proc_buffer = pdf_new_buffer_processor(ctx, buffer, ascii); if (sanitize) { proc_filter = pdf_new_filter_processor(ctx, doc, proc_buffer, orig_res, res); pdf_process_contents(ctx, proc_filter, doc, orig_res, val, cookie); pdf_close_processor(ctx, proc_filter); } else { pdf_process_contents(ctx, proc_filter, doc, orig_res, val, cookie); } pdf_close_processor(ctx, proc_buffer); pdf_update_stream(ctx, doc, val, buffer, 0); } fz_always(ctx) { pdf_drop_processor(ctx, proc_filter); pdf_drop_processor(ctx, proc_buffer); fz_drop_buffer(ctx, buffer); } fz_catch(ctx) { fz_rethrow(ctx); } } /* ProcSet - no cleaning possible. Inherit this from the old dict. */ pdf_dict_put(ctx, res, PDF_NAME(ProcSet), pdf_dict_get(ctx, orig_res, PDF_NAME(ProcSet))); ref = pdf_add_object(ctx, doc, res); pdf_dict_put_drop(ctx, obj, PDF_NAME(Resources), ref); } fz_always(ctx) { pdf_drop_obj(ctx, res); } fz_catch(ctx) { fz_rethrow(ctx); } }
/* Performs the same task as pdf_clean_page_contents, but with an optional text filter function. text_filter: Function to assess whether a given character should be kept (return 0) or removed (return 1). after_text: Function called after each text object is closed to allow other output to be sent. arg: Opaque value to be passed to callback functions. */ void pdf_filter_page_contents(fz_context *ctx, pdf_document *doc, pdf_page *page, fz_cookie *cookie, pdf_page_contents_process_fn *proc_fn, pdf_text_filter_fn *text_filter, pdf_after_text_object_fn *after_text, void *proc_arg, int sanitize, int ascii) { pdf_processor *proc_buffer = NULL; pdf_processor *proc_filter = NULL; pdf_obj *new_obj = NULL; pdf_obj *new_ref = NULL; pdf_obj *res = NULL; pdf_obj *obj; pdf_obj *contents; pdf_obj *resources; fz_buffer *buffer; fz_var(new_obj); fz_var(new_ref); fz_var(res); fz_var(proc_buffer); fz_var(proc_filter); buffer = fz_new_buffer(ctx, 1024); fz_try(ctx) { contents = pdf_page_contents(ctx, page); resources = pdf_page_resources(ctx, page); proc_buffer = pdf_new_buffer_processor(ctx, buffer, ascii); if (sanitize) { res = pdf_new_dict(ctx, doc, 1); proc_filter = pdf_new_filter_processor_with_text_filter(ctx, doc, proc_buffer, resources, res, text_filter, after_text, proc_arg); pdf_process_contents(ctx, proc_filter, doc, resources, contents, cookie); pdf_close_processor(ctx, proc_filter); } else { res = pdf_keep_obj(ctx, resources); pdf_process_contents(ctx, proc_buffer, doc, resources, contents, cookie); } pdf_close_processor(ctx, proc_buffer); /* Deal with page content stream. */ if (pdf_is_array(ctx, contents)) { /* create a new object to replace the array */ new_obj = pdf_new_dict(ctx, doc, 1); new_ref = pdf_add_object(ctx, doc, new_obj); contents = new_ref; pdf_dict_put(ctx, page->obj, PDF_NAME(Contents), contents); } else { pdf_dict_del(ctx, contents, PDF_NAME(Filter)); pdf_dict_del(ctx, contents, PDF_NAME(DecodeParms)); } pdf_update_stream(ctx, doc, contents, buffer, 0); /* Now deal with resources. The spec allows for Type3 fonts and form * XObjects to omit a resource dictionary and look in the parent. * Avoid that by flattening here as part of the cleaning. This could * conceivably cause changes in rendering, but we don't care. */ /* ExtGState */ obj = pdf_dict_get(ctx, res, PDF_NAME(ExtGState)); if (obj) { int i, l; l = pdf_dict_len(ctx, obj); for (i = 0; i < l; i++) { pdf_obj *o = pdf_dict_get(ctx, pdf_dict_get_val(ctx, obj, i), PDF_NAME(SMask)); if (!o) continue; o = pdf_dict_get(ctx, o, PDF_NAME(G)); if (!o) continue; /* Transparency group XObject */ pdf_clean_stream_object(ctx, doc, o, resources, cookie, 1, text_filter, after_text, proc_arg, sanitize, ascii); } } /* Pattern */ obj = pdf_dict_get(ctx, res, PDF_NAME(Pattern)); if (obj) { int i, l; l = pdf_dict_len(ctx, obj); for (i = 0; i < l; i++) { pdf_obj *pat_res; pdf_obj *pat = pdf_dict_get_val(ctx, obj, i); if (!pat) continue; pat_res = pdf_dict_get(ctx, pat, PDF_NAME(Resources)); if (pat_res == NULL) pat_res = resources; if (pdf_dict_get_int(ctx, pat, PDF_NAME(PatternType)) == 1) pdf_clean_stream_object(ctx, doc, pat, pat_res, cookie, 0, text_filter, after_text, proc_arg, sanitize, ascii); } } /* XObject */ obj = pdf_dict_get(ctx, res, PDF_NAME(XObject)); if (obj) { int i, l; l = pdf_dict_len(ctx, obj); for (i = 0; i < l; i++) { pdf_obj *xobj_res; pdf_obj *xobj = pdf_dict_get_val(ctx, obj, i); if (!xobj) continue; xobj_res = pdf_dict_get(ctx, xobj, PDF_NAME(Resources)); if (xobj_res == NULL) xobj_res = resources; if (pdf_name_eq(ctx, PDF_NAME(Form), pdf_dict_get(ctx, xobj, PDF_NAME(Subtype)))) pdf_clean_stream_object(ctx, doc, xobj, xobj_res, cookie, 1, text_filter, after_text, proc_arg, sanitize, ascii); } } /* Font */ obj = pdf_dict_get(ctx, res, PDF_NAME(Font)); if (obj) { int i, l; l = pdf_dict_len(ctx, obj); for (i = 0; i < l; i++) { pdf_obj *o = pdf_dict_get_val(ctx, obj, i); if (!o) continue; if (pdf_name_eq(ctx, PDF_NAME(Type3), pdf_dict_get(ctx, o, PDF_NAME(Subtype)))) pdf_clean_type3(ctx, doc, o, resources, cookie, sanitize, ascii); } } /* ProcSet - no cleaning possible. Inherit this from the old dict. */ obj = pdf_dict_get(ctx, resources, PDF_NAME(ProcSet)); if (obj) pdf_dict_put(ctx, res, PDF_NAME(ProcSet), obj); /* ColorSpace - no cleaning possible. */ /* Properties - no cleaning possible. */ if (proc_fn) (*proc_fn)(ctx, buffer, res, proc_arg); /* Update resource dictionary */ if (sanitize) { pdf_dict_put(ctx, page->obj, PDF_NAME(Resources), res); } } fz_always(ctx) { pdf_drop_processor(ctx, proc_filter); pdf_drop_processor(ctx, proc_buffer); fz_drop_buffer(ctx, buffer); pdf_drop_obj(ctx, new_obj); pdf_drop_obj(ctx, new_ref); pdf_drop_obj(ctx, res); } fz_catch(ctx) { fz_rethrow(ctx); } }
static void retainpages(fz_context *ctx, globals *glo, int argc, char **argv) { pdf_obj *oldroot, *root, *pages, *kids, *countobj, *parent, *olddests; pdf_document *doc = glo->doc; int argidx = 0; pdf_obj *names_list = NULL; int pagecount; int i; /* Keep only pages/type and (reduced) dest entries to avoid * references to unretained pages */ oldroot = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME_Root); pages = pdf_dict_get(ctx, oldroot, PDF_NAME_Pages); olddests = pdf_load_name_tree(ctx, doc, PDF_NAME_Dests); root = pdf_new_dict(ctx, doc, 2); pdf_dict_put(ctx, root, PDF_NAME_Type, pdf_dict_get(ctx, oldroot, PDF_NAME_Type)); pdf_dict_put(ctx, root, PDF_NAME_Pages, pdf_dict_get(ctx, oldroot, PDF_NAME_Pages)); pdf_update_object(ctx, doc, pdf_to_num(ctx, oldroot), root); pdf_drop_obj(ctx, root); /* Create a new kids array with only the pages we want to keep */ parent = pdf_new_indirect(ctx, doc, pdf_to_num(ctx, pages), pdf_to_gen(ctx, pages)); kids = pdf_new_array(ctx, doc, 1); /* Retain pages specified */ while (argc - argidx) { int page, spage, epage; char *spec, *dash; char *pagelist = argv[argidx]; pagecount = pdf_count_pages(ctx, doc); spec = fz_strsep(&pagelist, ","); while (spec) { dash = strchr(spec, '-'); if (dash == spec) spage = epage = pagecount; else spage = epage = atoi(spec); if (dash) { if (strlen(dash) > 1) epage = atoi(dash + 1); else epage = pagecount; } spage = fz_clampi(spage, 1, pagecount); epage = fz_clampi(epage, 1, pagecount); if (spage < epage) for (page = spage; page <= epage; ++page) retainpage(ctx, doc, parent, kids, page); else for (page = spage; page >= epage; --page) retainpage(ctx, doc, parent, kids, page); spec = fz_strsep(&pagelist, ","); } argidx++; } pdf_drop_obj(ctx, parent); /* Update page count and kids array */ countobj = pdf_new_int(ctx, doc, pdf_array_len(ctx, kids)); pdf_dict_put(ctx, pages, PDF_NAME_Count, countobj); pdf_drop_obj(ctx, countobj); pdf_dict_put(ctx, pages, PDF_NAME_Kids, kids); pdf_drop_obj(ctx, kids); /* Also preserve the (partial) Dests name tree */ if (olddests) { pdf_obj *names = pdf_new_dict(ctx, doc, 1); pdf_obj *dests = pdf_new_dict(ctx, doc, 1); int len = pdf_dict_len(ctx, olddests); names_list = pdf_new_array(ctx, doc, 32); for (i = 0; i < len; i++) { pdf_obj *key = pdf_dict_get_key(ctx, olddests, i); pdf_obj *val = pdf_dict_get_val(ctx, olddests, i); pdf_obj *dest = pdf_dict_get(ctx, val, PDF_NAME_D); dest = pdf_array_get(ctx, dest ? dest : val, 0); if (pdf_array_contains(ctx, pdf_dict_get(ctx, pages, PDF_NAME_Kids), dest)) { pdf_obj *key_str = pdf_new_string(ctx, doc, pdf_to_name(ctx, key), strlen(pdf_to_name(ctx, key))); pdf_array_push(ctx, names_list, key_str); pdf_array_push(ctx, names_list, val); pdf_drop_obj(ctx, key_str); } } root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME_Root); pdf_dict_put(ctx, dests, PDF_NAME_Names, names_list); pdf_dict_put(ctx, names, PDF_NAME_Dests, dests); pdf_dict_put(ctx, root, PDF_NAME_Names, names); pdf_drop_obj(ctx, names); pdf_drop_obj(ctx, dests); pdf_drop_obj(ctx, names_list); pdf_drop_obj(ctx, olddests); } /* Force the next call to pdf_count_pages to recount */ glo->doc->page_count = 0; /* Edit each pages /Annot list to remove any links that point to * nowhere. */ pagecount = pdf_count_pages(ctx, doc); for (i = 0; i < pagecount; i++) { pdf_obj *pageref = pdf_lookup_page_obj(ctx, doc, i); pdf_obj *pageobj = pdf_resolve_indirect(ctx, pageref); pdf_obj *annots = pdf_dict_get(ctx, pageobj, PDF_NAME_Annots); int len = pdf_array_len(ctx, annots); int j; for (j = 0; j < len; j++) { pdf_obj *o = pdf_array_get(ctx, annots, j); pdf_obj *p; if (!pdf_name_eq(ctx, pdf_dict_get(ctx, o, PDF_NAME_Subtype), PDF_NAME_Link)) continue; p = pdf_dict_get(ctx, o, PDF_NAME_A); if (!pdf_name_eq(ctx, pdf_dict_get(ctx, p, PDF_NAME_S), PDF_NAME_GoTo)) continue; if (string_in_names_list(ctx, pdf_dict_get(ctx, p, PDF_NAME_D), names_list)) continue; /* FIXME: Should probably look at Next too */ /* Remove this annotation */ pdf_array_delete(ctx, annots, j); j--; } } }
void pdf_clean_page_contents(fz_context *ctx, pdf_document *doc, pdf_page *page, fz_cookie *cookie, pdf_page_contents_process_fn *proc_fn, void *proc_arg) { pdf_processor *proc_buffer = NULL; pdf_processor *proc_filter = NULL; pdf_obj *new_obj = NULL; pdf_obj *new_ref = NULL; pdf_obj *res = NULL; pdf_obj *ref = NULL; pdf_obj *obj; pdf_obj *contents; fz_buffer *buffer; fz_var(new_obj); fz_var(new_ref); fz_var(res); fz_var(ref); fz_var(proc_buffer); fz_var(proc_filter); buffer = fz_new_buffer(ctx, 1024); fz_try(ctx) { res = pdf_new_dict(ctx, doc, 1); proc_buffer = pdf_new_buffer_processor(ctx, buffer); proc_filter = pdf_new_filter_processor(ctx, proc_buffer, doc, page->resources, res); pdf_process_contents(ctx, proc_filter, doc, page->resources, page->contents, cookie); contents = page->contents; if (pdf_is_array(ctx, contents)) { /* create a new object to replace the array */ new_obj = pdf_new_dict(ctx, doc, 1); new_ref = pdf_new_ref(ctx, doc, new_obj); page->contents = contents = new_ref; } else { pdf_dict_del(ctx, contents, PDF_NAME_Filter); pdf_dict_del(ctx, contents, PDF_NAME_DecodeParms); } /* Now deal with resources. The spec allows for Type3 fonts and form * XObjects to omit a resource dictionary and look in the parent. * Avoid that by flattening here as part of the cleaning. This could * conceivably cause changes in rendering, but we don't care. */ /* ExtGState */ obj = pdf_dict_get(ctx, res, PDF_NAME_ExtGState); if (obj) { int i, l; l = pdf_dict_len(ctx, obj); for (i = 0; i < l; i++) { pdf_obj *o = pdf_dict_get(ctx, pdf_dict_get_val(ctx, obj, i), PDF_NAME_SMask); if (!o) continue; o = pdf_dict_get(ctx, o, PDF_NAME_G); if (!o) continue; /* Transparency group XObject */ pdf_clean_stream_object(ctx, doc, o, page->resources, cookie, 1); } } /* ColorSpace - no cleaning possible */ /* Pattern */ obj = pdf_dict_get(ctx, res, PDF_NAME_Pattern); if (obj) { int i, l; l = pdf_dict_len(ctx, obj); for (i = 0; i < l; i++) { pdf_obj *pat = pdf_dict_get_val(ctx, obj, i); if (!pat) continue; if (pdf_to_int(ctx, pdf_dict_get(ctx, pat, PDF_NAME_PatternType)) == 1) pdf_clean_stream_object(ctx, doc, pat, page->resources, cookie, 0); } } /* Shading - no cleaning possible */ /* XObject */ obj = pdf_dict_get(ctx, res, PDF_NAME_XObject); if (obj) { int i, l; l = pdf_dict_len(ctx, obj); for (i = 0; i < l; i++) { pdf_obj *xobj = pdf_dict_get_val(ctx, obj, i); if (!pdf_name_eq(ctx, PDF_NAME_Form, pdf_dict_get(ctx, xobj, PDF_NAME_Subtype))) continue; pdf_clean_stream_object(ctx, doc, xobj, page->resources, cookie, 1); } } /* Font */ obj = pdf_dict_get(ctx, res, PDF_NAME_Font); if (obj) { int i, l; l = pdf_dict_len(ctx, obj); for (i = 0; i < l; i++) { pdf_obj *o = pdf_dict_get_val(ctx, obj, i); if (pdf_name_eq(ctx, PDF_NAME_Type3, pdf_dict_get(ctx, o, PDF_NAME_Subtype))) { pdf_clean_type3(ctx, doc, o, page->resources, cookie); } } } /* ProcSet - no cleaning possible. Inherit this from the old dict. */ obj = pdf_dict_get(ctx, page->resources, PDF_NAME_ProcSet); if (obj) pdf_dict_put(ctx, res, PDF_NAME_ProcSet, obj); /* Properties - no cleaning possible. */ if (proc_fn) (*proc_fn)(ctx, buffer, res, proc_arg); pdf_update_stream(ctx, doc, contents, buffer, 0); pdf_drop_obj(ctx, page->resources); ref = pdf_new_ref(ctx, doc, res); page->resources = pdf_keep_obj(ctx, ref); pdf_dict_put(ctx, page->me, PDF_NAME_Resources, ref); } fz_always(ctx) { pdf_drop_processor(ctx, proc_filter); pdf_drop_processor(ctx, proc_buffer); fz_drop_buffer(ctx, buffer); pdf_drop_obj(ctx, new_obj); pdf_drop_obj(ctx, new_ref); pdf_drop_obj(ctx, res); pdf_drop_obj(ctx, ref); } fz_catch(ctx) { fz_rethrow_message(ctx, "Failed while cleaning page"); } }
pdf_obj * pdf_parse_dict(pdf_document *doc, fz_stream *file, pdf_lexbuf *buf) { pdf_obj *dict; pdf_obj *key = NULL; pdf_obj *val = NULL; pdf_token tok; int a, b; fz_context *ctx = file->ctx; dict = pdf_new_dict(doc, 8); fz_var(key); fz_var(val); fz_try(ctx) { while (1) { tok = pdf_lex(file, buf); skip: if (tok == PDF_TOK_CLOSE_DICT) break; /* for BI .. ID .. EI in content streams */ if (tok == PDF_TOK_KEYWORD && !strcmp(buf->scratch, "ID")) break; if (tok != PDF_TOK_NAME) fz_throw(ctx, FZ_ERROR_GENERIC, "invalid key in dict"); key = pdf_new_name(doc, buf->scratch); tok = pdf_lex(file, buf); switch (tok) { case PDF_TOK_OPEN_ARRAY: /* cf. http://code.google.com/p/sumatrapdf/issues/detail?id=1643 */ fz_try(ctx) { val = pdf_parse_array(doc, file, buf); } fz_catch(ctx) { fz_warn(ctx, "ignoring broken array for '%s'", pdf_to_name(key)); pdf_drop_obj(key); val = key = NULL; do tok = pdf_lex(file, buf); while (tok != PDF_TOK_CLOSE_DICT && tok != PDF_TOK_CLOSE_ARRAY && tok != PDF_TOK_EOF && tok != PDF_TOK_OPEN_ARRAY && tok != PDF_TOK_OPEN_DICT); if (tok == PDF_TOK_CLOSE_DICT) goto skip; if (tok == PDF_TOK_CLOSE_ARRAY) continue; fz_throw(ctx, FZ_ERROR_GENERIC, "cannot make sense of broken array after all"); } break; case PDF_TOK_OPEN_DICT: val = pdf_parse_dict(doc, file, buf); break; case PDF_TOK_NAME: val = pdf_new_name(doc, buf->scratch); break; case PDF_TOK_REAL: val = pdf_new_real(doc, buf->f); break; case PDF_TOK_STRING: val = pdf_new_string(doc, buf->scratch, buf->len); break; case PDF_TOK_TRUE: val = pdf_new_bool(doc, 1); break; case PDF_TOK_FALSE: val = pdf_new_bool(doc, 0); break; case PDF_TOK_NULL: val = pdf_new_null(doc); break; case PDF_TOK_INT: /* 64-bit to allow for numbers > INT_MAX and overflow */ a = buf->i; tok = pdf_lex(file, buf); if (tok == PDF_TOK_CLOSE_DICT || tok == PDF_TOK_NAME || (tok == PDF_TOK_KEYWORD && !strcmp(buf->scratch, "ID"))) { val = pdf_new_int(doc, a); pdf_dict_put(dict, key, val); pdf_drop_obj(val); val = NULL; pdf_drop_obj(key); key = NULL; goto skip; } if (tok == PDF_TOK_INT) { b = buf->i; tok = pdf_lex(file, buf); if (tok == PDF_TOK_R) { val = pdf_new_indirect(doc, a, b); break; } } fz_throw(ctx, FZ_ERROR_GENERIC, "invalid indirect reference in dict"); default: fz_throw(ctx, FZ_ERROR_GENERIC, "unknown token in dict"); } pdf_dict_put(dict, key, val); pdf_drop_obj(val); val = NULL; pdf_drop_obj(key); key = NULL; } } fz_catch(ctx) { pdf_drop_obj(dict); pdf_drop_obj(key); pdf_drop_obj(val); fz_rethrow_message(ctx, "cannot parse dict"); } return dict; }
pdf_obj * pdf_parse_dict(fz_context *ctx, pdf_document *doc, fz_stream *file, pdf_lexbuf *buf) { pdf_obj *dict; pdf_obj *key = NULL; pdf_obj *val = NULL; pdf_token tok; fz_off_t a, b; dict = pdf_new_dict(ctx, doc, 8); fz_var(key); fz_var(val); fz_try(ctx) { while (1) { tok = pdf_lex(ctx, file, buf); skip: if (tok == PDF_TOK_CLOSE_DICT) break; /* for BI .. ID .. EI in content streams */ if (tok == PDF_TOK_KEYWORD && !strcmp(buf->scratch, "ID")) break; if (tok != PDF_TOK_NAME) fz_throw(ctx, FZ_ERROR_GENERIC, "invalid key in dict"); key = pdf_new_name(ctx, doc, buf->scratch); tok = pdf_lex(ctx, file, buf); switch (tok) { case PDF_TOK_OPEN_ARRAY: val = pdf_parse_array(ctx, doc, file, buf); break; case PDF_TOK_OPEN_DICT: val = pdf_parse_dict(ctx, doc, file, buf); break; case PDF_TOK_NAME: val = pdf_new_name(ctx, doc, buf->scratch); break; case PDF_TOK_REAL: val = pdf_new_real(ctx, doc, buf->f); break; case PDF_TOK_STRING: val = pdf_new_string(ctx, doc, buf->scratch, buf->len); break; case PDF_TOK_TRUE: val = pdf_new_bool(ctx, doc, 1); break; case PDF_TOK_FALSE: val = pdf_new_bool(ctx, doc, 0); break; case PDF_TOK_NULL: val = pdf_new_null(ctx, doc); break; case PDF_TOK_INT: /* 64-bit to allow for numbers > INT_MAX and overflow */ a = buf->i; tok = pdf_lex(ctx, file, buf); if (tok == PDF_TOK_CLOSE_DICT || tok == PDF_TOK_NAME || (tok == PDF_TOK_KEYWORD && !strcmp(buf->scratch, "ID"))) { val = pdf_new_int_offset(ctx, doc, a); pdf_dict_put(ctx, dict, key, val); pdf_drop_obj(ctx, val); val = NULL; pdf_drop_obj(ctx, key); key = NULL; goto skip; } if (tok == PDF_TOK_INT) { b = buf->i; tok = pdf_lex(ctx, file, buf); if (tok == PDF_TOK_R) { val = pdf_new_indirect(ctx, doc, a, b); break; } } fz_throw(ctx, FZ_ERROR_GENERIC, "invalid indirect reference in dict"); default: fz_throw(ctx, FZ_ERROR_GENERIC, "unknown token in dict"); } pdf_dict_put(ctx, dict, key, val); pdf_drop_obj(ctx, val); val = NULL; pdf_drop_obj(ctx, key); key = NULL; } } fz_catch(ctx) { pdf_drop_obj(ctx, dict); pdf_drop_obj(ctx, key); pdf_drop_obj(ctx, val); fz_rethrow(ctx); } return dict; }
// just copy one of the resource sub-entries (e.g. /Font) static int copy_and_rename_resource(fz_context *dest_ctx, pdf_obj *dest, fz_context *src_ctx, pdf_obj *src, char *prefix, struct put_info *info) { char new_name[64]; /* this buffer is big enough up to hold all digits for two 16-bit numbers */ int i; for(i = 0; i < pdf_dict_len(src_ctx, src); i++) { pdf_obj *src_key = pdf_dict_get_key(src_ctx, src, i); pdf_obj *src_val = pdf_dict_get_val(src_ctx, src, i); if(!pdf_is_name(src_ctx, src_key)) { return(2); } /* if this is an inline resource, just copy this object into the new resource dict */ if(!pdf_is_indirect(src_ctx, src_val)) { if(snprintf(new_name, sizeof(new_name) / sizeof(new_name[0]), "%sinline_%d", prefix, info->next_inline_id++) >= sizeof(new_name) / sizeof(new_name[0])) return(1); // not enough space pdf_obj *new_res = copy_unassigned_object_continue(dest_ctx, info->dest_doc, src_ctx, info->src_doc, src_val, &info->new_ids); //pdf_obj *new_res = pdf_new_dict(dest_ctx, info->dest_doc, 10); printf("dump it...\n"); pdf_fprint_obj(dest_ctx, stdout, new_res, 0); /* now reference this new object in the resource object of this sheet */ pdf_obj *dest_key = pdf_new_name(dest_ctx, info->dest_doc, new_name); pdf_dict_put(dest_ctx, dest, dest_key, new_res); pdf_drop_obj(dest_ctx, dest_key); pdf_drop_obj(dest_ctx, new_res); } else { /* The new name of resource objects is always the num/gen of the referenced object in the src-file. Thus we can check by that name if the object was already referenced by another page of this sheet. */ if(snprintf(new_name, sizeof(new_name) / sizeof(new_name[0]), "%s%d_%d", prefix, pdf_to_num(dest_ctx, src_val), pdf_to_gen(dest_ctx, src_val)) >= sizeof(new_name) / sizeof(new_name[0])) return(1); // not enough space if(pdf_dict_gets(dest_ctx, dest, new_name) == NULL) { /* if this resource is not inlined and not already in the resource-dict of the current sheet... */ /* ...copy the referenced resource to the new document! If this object has copied already (for another sheet in dest_doc), copy_object_continue() will do nothing */ pdf_obj *new_res = copy_object_continue(dest_ctx, info->dest_doc, src_ctx, info->src_doc, src_val, &info->new_ids); /* now reference this new object in the resource object of this sheet */ pdf_obj *dest_key = pdf_new_name(dest_ctx, info->dest_doc, new_name); pdf_dict_put(dest_ctx, dest, dest_key, new_res); pdf_drop_obj(dest_ctx, dest_key); pdf_drop_obj(dest_ctx, new_res); } } /* even if it was used on another sheet or on this sheet, add it to the rename-dict for this sheet! Because it could have different names on different source-pages */ pdf_obj *rename_key = pdf_new_name(dest_ctx, info->dest_doc, pdf_to_name(dest_ctx, src_key)); pdf_obj *rename_val = pdf_new_name(dest_ctx, info->dest_doc, new_name); pdf_dict_put(dest_ctx, info->rename_dict, rename_key, rename_val); pdf_drop_obj(dest_ctx, rename_key); pdf_drop_obj(dest_ctx, rename_val); } return(0); }
/* When resetting or submitting a form, the fields to act upon are defined by an array of either field references or field names, plus a flag determining whether to act upon the fields in the array, or all fields other than those in the array. specified_fields interprets this information and produces the array of fields to be acted upon. */ static pdf_obj *specified_fields(fz_context *ctx, pdf_document *doc, pdf_obj *fields, int exclude) { pdf_obj *form = pdf_dict_getl(ctx, pdf_trailer(ctx, doc), PDF_NAME_Root, PDF_NAME_AcroForm, PDF_NAME_Fields, NULL); int i, n; pdf_obj *result = pdf_new_array(ctx, doc, 0); pdf_obj *nil = NULL; fz_var(nil); fz_try(ctx) { /* The 'fields' array not being present signals that all fields * should be acted upon, so handle it using the exclude case - excluding none */ if (exclude || !fields) { /* mark the fields we don't want to act upon */ nil = pdf_new_null(ctx, doc); n = pdf_array_len(ctx, fields); for (i = 0; i < n; i++) { pdf_obj *field = pdf_array_get(ctx, fields, i); if (pdf_is_string(ctx, field)) field = pdf_lookup_field(ctx, form, pdf_to_str_buf(ctx, field)); if (field) pdf_dict_put(ctx, field, PDF_NAME_Exclude, nil); } /* Act upon all unmarked fields */ n = pdf_array_len(ctx, form); for (i = 0; i < n; i++) add_field_hierarchy_to_array(ctx, result, pdf_array_get(ctx, form, i)); /* Unmark the marked fields */ n = pdf_array_len(ctx, fields); for (i = 0; i < n; i++) { pdf_obj *field = pdf_array_get(ctx, fields, i); if (pdf_is_string(ctx, field)) field = pdf_lookup_field(ctx, form, pdf_to_str_buf(ctx, field)); if (field) pdf_dict_del(ctx, field, PDF_NAME_Exclude); } } else { n = pdf_array_len(ctx, fields); for (i = 0; i < n; i++) { pdf_obj *field = pdf_array_get(ctx, fields, i); if (pdf_is_string(ctx, field)) field = pdf_lookup_field(ctx, form, pdf_to_str_buf(ctx, field)); if (field) add_field_hierarchy_to_array(ctx, result, field); } } } fz_always(ctx) { pdf_drop_obj(ctx, nil); } fz_catch(ctx) { pdf_drop_obj(ctx, result); fz_rethrow(ctx); } return result; }
static void reset_field(fz_context *ctx, pdf_document *doc, pdf_obj *field) { /* Set V to DV whereever DV is present, and delete V where DV is not. * FIXME: we assume for now that V has not been set unequal * to DV higher in the hierarchy than "field". * * At the bottom of the hierarchy we may find widget annotations * that aren't also fields, but DV and V will not be present in their * dictionaries, and attempts to remove V will be harmless. */ pdf_obj *dv = pdf_dict_get(ctx, field, PDF_NAME_DV); pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME_Kids); if (dv) pdf_dict_put(ctx, field, PDF_NAME_V, dv); else pdf_dict_del(ctx, field, PDF_NAME_V); if (kids == NULL) { /* The leaves of the tree are widget annotations * In some cases we need to update the appearance state; * in others we need to mark the field as dirty so that * the appearance stream will be regenerated. */ switch (pdf_field_type(ctx, doc, field)) { case PDF_WIDGET_TYPE_RADIOBUTTON: case PDF_WIDGET_TYPE_CHECKBOX: { pdf_obj *leafv = pdf_get_inheritable(ctx, doc, field, PDF_NAME_V); if (leafv) pdf_keep_obj(ctx, leafv); else leafv = PDF_NAME_Off; fz_try(ctx) { pdf_dict_put(ctx, field, PDF_NAME_AS, leafv); } fz_always(ctx) { pdf_drop_obj(ctx, leafv); } fz_catch(ctx) { fz_rethrow(ctx); } } break; case PDF_WIDGET_TYPE_PUSHBUTTON: break; default: pdf_field_mark_dirty(ctx, doc, field); break; } } if (pdf_field_dirties_document(ctx, doc, field)) doc->dirty = 1; }
void pdf_set_free_text_details(fz_context *ctx, pdf_annot *annot, fz_point *pos, char *text, char *font_name, float font_size, float color[3]) { pdf_document *doc = annot->page->doc; char nbuf[32]; pdf_obj *dr; pdf_obj *form_fonts; pdf_obj *font = NULL; pdf_obj *ref; pdf_font_desc *font_desc = NULL; fz_matrix page_ctm, inv_page_ctm; pdf_da_info da_info; fz_buffer *fzbuf = NULL; fz_point page_pos; pdf_page_transform(ctx, annot->page, NULL, &page_ctm); fz_invert_matrix(&inv_page_ctm, &page_ctm); dr = pdf_dict_get(ctx, annot->page->obj, PDF_NAME(Resources)); if (!dr) { dr = pdf_new_dict(ctx, doc, 1); pdf_dict_put_drop(ctx, annot->page->obj, PDF_NAME(Resources), dr); } /* Ensure the resource dictionary includes a font dict */ form_fonts = pdf_dict_get(ctx, dr, PDF_NAME(Font)); if (!form_fonts) { form_fonts = pdf_new_dict(ctx, doc, 1); pdf_dict_put_drop(ctx, dr, PDF_NAME(Font), form_fonts); /* form_fonts is still valid if execution continues past the above call */ } fz_var(fzbuf); fz_var(font); fz_try(ctx) { unsigned char *da_str; size_t da_len; fz_rect bounds; find_free_font_name(ctx, form_fonts, nbuf, sizeof(nbuf)); font = pdf_new_dict(ctx, doc, 5); ref = pdf_add_object(ctx, doc, font); pdf_dict_puts_drop(ctx, form_fonts, nbuf, ref); pdf_dict_put(ctx, font, PDF_NAME(Type), PDF_NAME(Font)); pdf_dict_put(ctx, font, PDF_NAME(Subtype), PDF_NAME(Type1)); pdf_dict_put_name(ctx, font, PDF_NAME(BaseFont), font_name); pdf_dict_put(ctx, font, PDF_NAME(Encoding), PDF_NAME(WinAnsiEncoding)); memcpy(da_info.col, color, sizeof(float)*3); da_info.col_size = 3; da_info.font_name = nbuf; da_info.font_size = font_size; fzbuf = fz_new_buffer(ctx, 0); pdf_fzbuf_print_da(ctx, fzbuf, &da_info); da_len = fz_buffer_storage(ctx, fzbuf, &da_str); pdf_dict_put_string(ctx, annot->obj, PDF_NAME(DA), (char *)da_str, da_len); /* FIXME: should convert to WinAnsiEncoding */ pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(Contents), text); font_desc = pdf_load_font(ctx, doc, NULL, font, 0); pdf_measure_text(ctx, font_desc, (unsigned char *)text, strlen(text), &bounds); page_pos = *pos; fz_transform_point(&page_pos, &inv_page_ctm); bounds.x0 *= font_size; bounds.x1 *= font_size; bounds.y0 *= font_size; bounds.y1 *= font_size; bounds.x0 += page_pos.x; bounds.x1 += page_pos.x; bounds.y0 += page_pos.y; bounds.y1 += page_pos.y; pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(Rect), &bounds); } fz_always(ctx) { pdf_drop_obj(ctx, font); fz_drop_buffer(ctx, fzbuf); pdf_drop_font(ctx, font_desc); } fz_catch(ctx) { fz_rethrow(ctx); } }
void pdf_add_portfolio_schema(fz_context *ctx, pdf_document *doc, int entry, const pdf_portfolio_schema *info) { pdf_portfolio **pp; pdf_portfolio *p; pdf_obj *s; pdf_obj *sc; int num; char str_name[32]; pdf_obj *num_name = NULL; if (!doc) fz_throw(ctx, FZ_ERROR_GENERIC, "Bad pdf_add_portfolio_schema call"); if (doc->portfolio == NULL) load_portfolio(ctx, doc); fz_var(num_name); pp = &doc->portfolio; while (*pp && entry > 0) pp = &(*pp)->next, entry--; fz_try(ctx) { /* Find a name for the new schema entry */ num = 0; do { pdf_drop_obj(ctx, num_name); num_name = NULL; num++; sprintf(str_name, "%d", num); num_name = pdf_new_name(ctx, doc, str_name); p = doc->portfolio; for (p = doc->portfolio; p; p = p->next) if (pdf_name_eq(ctx, num_name, p->key)) break; } while (p); sc = pdf_new_dict(ctx, doc, 4); pdf_dict_put_drop(ctx, sc, PDF_NAME_E, pdf_new_bool(ctx, doc, !!info->editable)); pdf_dict_put_drop(ctx, sc, PDF_NAME_V, pdf_new_bool(ctx, doc, !!info->visible)); pdf_dict_put_drop(ctx, sc, PDF_NAME_N, info->name); pdf_dict_put(ctx, sc, PDF_NAME_Subtype, PDF_NAME_S); /* Add to our linked list (in the correct sorted place) */ p = fz_malloc_struct(ctx, pdf_portfolio); p->entry = *info; p->sort = 0; /* Will be rewritten in a mo */ p->key = pdf_keep_obj(ctx, num_name); p->val = pdf_keep_obj(ctx, sc); p->next = *pp; *pp = p; /* Add the key to the schema */ s = pdf_dict_getl(ctx, pdf_trailer(ctx, doc), PDF_NAME_Root, PDF_NAME_Collection, PDF_NAME_Schema, NULL); pdf_dict_put(ctx, s, num_name, sc); /* Renumber the schema entries */ for (num = 0, p = doc->portfolio; p; num++, p = p->next) { pdf_dict_put_drop(ctx, p->val, PDF_NAME_O, pdf_new_int(ctx, doc, num)); p->sort = num; } } fz_always(ctx) pdf_drop_obj(ctx, num_name); fz_catch(ctx) fz_rethrow(ctx); }
void pdf_delete_annot(fz_context *ctx, pdf_document *doc, pdf_page *page, pdf_annot *annot) { pdf_annot **annotptr; pdf_obj *old_annot_arr; pdf_obj *annot_arr; if (annot == NULL) return; /* Remove annot from page's list */ for (annotptr = &page->annots; *annotptr; annotptr = &(*annotptr)->next) { if (*annotptr == annot) break; } /* Check the passed annotation was of this page */ if (*annotptr == NULL) return; *annotptr = annot->next; /* If the removed annotation was the last in the list adjust the end pointer */ if (*annotptr == NULL) page->annot_tailp = annotptr; /* Stick it in the deleted list */ annot->next = page->deleted_annots; page->deleted_annots = annot; pdf_drop_xobject(ctx, annot->ap); annot->ap = NULL; /* Recreate the "Annots" array with this annot removed */ old_annot_arr = pdf_dict_get(ctx, page->me, PDF_NAME_Annots); if (old_annot_arr) { int i, n = pdf_array_len(ctx, old_annot_arr); annot_arr = pdf_new_array(ctx, doc, n?(n-1):0); fz_try(ctx) { for (i = 0; i < n; i++) { pdf_obj *obj = pdf_array_get(ctx, old_annot_arr, i); if (obj != annot->obj) pdf_array_push(ctx, annot_arr, obj); } if (pdf_is_indirect(ctx, old_annot_arr)) pdf_update_object(ctx, doc, pdf_to_num(ctx, old_annot_arr), annot_arr); else pdf_dict_put(ctx, page->me, PDF_NAME_Annots, annot_arr); if (pdf_is_indirect(ctx, annot->obj)) pdf_delete_object(ctx, doc, pdf_to_num(ctx, annot->obj)); } fz_always(ctx) { pdf_drop_obj(ctx, annot_arr); } fz_catch(ctx) { fz_rethrow(ctx); } } pdf_drop_obj(ctx, annot->obj); annot->obj = NULL; doc->dirty = 1; }
pdf_annot * pdf_create_annot(fz_context *ctx, pdf_page *page, enum pdf_annot_type type) { pdf_annot *annot = NULL; pdf_document *doc = page->doc; pdf_obj *annot_obj = pdf_new_dict(ctx, doc, 0); pdf_obj *ind_obj = NULL; fz_var(annot); fz_var(ind_obj); fz_try(ctx) { int ind_obj_num; const char *type_str; pdf_obj *annot_arr; type_str = pdf_string_from_annot_type(ctx, type); if (type == PDF_ANNOT_UNKNOWN) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot create unknown annotation"); annot_arr = pdf_dict_get(ctx, page->obj, PDF_NAME(Annots)); if (annot_arr == NULL) { annot_arr = pdf_new_array(ctx, doc, 0); pdf_dict_put_drop(ctx, page->obj, PDF_NAME(Annots), annot_arr); } pdf_dict_put(ctx, annot_obj, PDF_NAME(Type), PDF_NAME(Annot)); pdf_dict_put_name(ctx, annot_obj, PDF_NAME(Subtype), type_str); /* Make printable as default */ pdf_dict_put_int(ctx, annot_obj, PDF_NAME(F), PDF_ANNOT_IS_PRINT); /* Both annotation object and annotation structure are now created. Insert the object in the hierarchy and the structure in the page's array. */ ind_obj_num = pdf_create_object(ctx, doc); pdf_update_object(ctx, doc, ind_obj_num, annot_obj); ind_obj = pdf_new_indirect(ctx, doc, ind_obj_num, 0); pdf_array_push(ctx, annot_arr, ind_obj); annot = pdf_new_annot(ctx, page, ind_obj); annot->ap = NULL; /* Linking must be done after any call that might throw because pdf_drop_annots below actually frees a list. Put the new annot at the end of the list, so that it will be drawn last. */ *page->annot_tailp = annot; page->annot_tailp = &annot->next; doc->dirty = 1; } fz_always(ctx) { pdf_drop_obj(ctx, annot_obj); pdf_drop_obj(ctx, ind_obj); } fz_catch(ctx) { pdf_drop_annots(ctx, annot); fz_rethrow(ctx); } return annot; }
static void decimatepages(fz_context *ctx, pdf_document *doc) { pdf_obj *oldroot, *root, *pages, *kids, *parent; int num_pages = pdf_count_pages(ctx, doc); int page, kidcount; oldroot = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME_Root); pages = pdf_dict_get(ctx, oldroot, PDF_NAME_Pages); root = pdf_new_dict(ctx, doc, 2); pdf_dict_put(ctx, root, PDF_NAME_Type, pdf_dict_get(ctx, oldroot, PDF_NAME_Type)); pdf_dict_put(ctx, root, PDF_NAME_Pages, pdf_dict_get(ctx, oldroot, PDF_NAME_Pages)); pdf_update_object(ctx, doc, pdf_to_num(ctx, oldroot), root); pdf_drop_obj(ctx, root); /* Create a new kids array with our new pages in */ parent = pdf_new_indirect(ctx, doc, pdf_to_num(ctx, pages), pdf_to_gen(ctx, pages)); kids = pdf_new_array(ctx, doc, 1); kidcount = 0; for (page=0; page < num_pages; page++) { pdf_page *page_details = pdf_load_page(ctx, doc, page); int xf = x_factor, yf = y_factor; int x, y; float w = page_details->mediabox.x1 - page_details->mediabox.x0; float h = page_details->mediabox.y1 - page_details->mediabox.y0; if (xf == 0 && yf == 0) { /* Nothing specified, so split along the long edge */ if (w > h) xf = 2, yf = 1; else xf = 1, yf = 2; } else if (xf == 0) xf = 1; else if (yf == 0) yf = 1; for (y = yf-1; y >= 0; y--) { for (x = 0; x < xf; x++) { pdf_obj *newpageobj, *newpageref, *newmediabox; fz_rect mb; int num; newpageobj = pdf_copy_dict(ctx, pdf_lookup_page_obj(ctx, doc, page)); num = pdf_create_object(ctx, doc); pdf_update_object(ctx, doc, num, newpageobj); newpageref = pdf_new_indirect(ctx, doc, num, 0); newmediabox = pdf_new_array(ctx, doc, 4); mb.x0 = page_details->mediabox.x0 + (w/xf)*x; if (x == xf-1) mb.x1 = page_details->mediabox.x1; else mb.x1 = page_details->mediabox.x0 + (w/xf)*(x+1); mb.y0 = page_details->mediabox.y0 + (h/yf)*y; if (y == yf-1) mb.y1 = page_details->mediabox.y1; else mb.y1 = page_details->mediabox.y0 + (h/yf)*(y+1); pdf_array_push(ctx, newmediabox, pdf_new_real(ctx, doc, mb.x0)); pdf_array_push(ctx, newmediabox, pdf_new_real(ctx, doc, mb.y0)); pdf_array_push(ctx, newmediabox, pdf_new_real(ctx, doc, mb.x1)); pdf_array_push(ctx, newmediabox, pdf_new_real(ctx, doc, mb.y1)); pdf_dict_put(ctx, newpageobj, PDF_NAME_Parent, parent); pdf_dict_put(ctx, newpageobj, PDF_NAME_MediaBox, newmediabox); /* Store page object in new kids array */ pdf_array_push(ctx, kids, newpageref); kidcount++; } } } pdf_drop_obj(ctx, parent); /* Update page count and kids array */ pdf_dict_put(ctx, pages, PDF_NAME_Count, pdf_new_int(ctx, doc, kidcount)); pdf_dict_put(ctx, pages, PDF_NAME_Kids, kids); pdf_drop_obj(ctx, kids); }