Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/source/pdf/pdf-annot.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/source/pdf/pdf-annot.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,4395 @@ +// Copyright (C) 2004-2025 Artifex Software, Inc. +// +// This file is part of MuPDF. +// +// MuPDF is free software: you can redistribute it and/or modify it under the +// terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License +// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> +// +// Alternative licensing terms are available from the licensor. +// For commercial licensing, see <https://www.artifex.com/> or contact +// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, +// CA 94129, USA, for further information. + +#include "mupdf/fitz.h" +#include "pdf-annot-imp.h" + +#include <string.h> +#include <float.h> + +pdf_annot * +pdf_keep_annot(fz_context *ctx, pdf_annot *annot) +{ + return fz_keep_imp(ctx, annot, &annot->refs); +} + +void +pdf_drop_annot(fz_context *ctx, pdf_annot *annot) +{ + if (fz_drop_imp(ctx, annot, &annot->refs)) + { + pdf_drop_obj(ctx, annot->obj); + fz_free(ctx, annot); + } +} + +void +pdf_drop_annots(fz_context *ctx, pdf_annot *annot) +{ + while (annot) + { + pdf_annot *next = annot->next; + pdf_drop_annot(ctx, annot); + annot = next; + } +} + +pdf_obj * +pdf_annot_ap(fz_context *ctx, pdf_annot *annot) +{ + int flags = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(F)); + int readonly = flags & PDF_ANNOT_IS_READ_ONLY; + + pdf_obj *ap = pdf_dict_get(ctx, annot->obj, PDF_NAME(AP)); + pdf_obj *ap_n = pdf_dict_get(ctx, ap, PDF_NAME(N)); + pdf_obj *ap_r = pdf_dict_get(ctx, ap, PDF_NAME(R)); + pdf_obj *ap_d = pdf_dict_get(ctx, ap, PDF_NAME(D)); + + if (!readonly && annot->is_hot && annot->is_active && ap_d) + ap = ap_d; + else if (!readonly && annot->is_hot && ap_r) + ap = ap_r; + else + ap = ap_n; + + /* AP/N, AP/R and AP/D may be streams, or dictionaries of streams. */ + + /* If it's a stream, we have a winner! */ + if (pdf_is_indirect(ctx, ap) && pdf_obj_num_is_stream(ctx, annot->page->doc, pdf_to_num(ctx, ap))) + return ap; + + /* If it's not a stream, it may be a dictionary containing + * a range of possible values, that should be indexed by + * AS. */ + return pdf_dict_get(ctx, ap, pdf_resolve_indirect_chain(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(AS)))); +} + +int pdf_annot_active(fz_context *ctx, pdf_annot *annot) +{ + return annot ? annot->is_active : 0; +} + +void pdf_set_annot_active(fz_context *ctx, pdf_annot *annot, int active) +{ + int old; + + if (!annot) + return; + + old = annot->is_active; + annot->is_active = !!active; + if (old != annot->is_active) + pdf_set_annot_has_changed(ctx, annot); +} + +int pdf_annot_hot(fz_context *ctx, pdf_annot *annot) +{ + return annot ? annot->is_hot : 0; +} + +void pdf_set_annot_hot(fz_context *ctx, pdf_annot *annot, int hot) +{ + int old; + + if (!annot) + return; + + old = annot->is_hot; + annot->is_hot = !!hot; + if (old != annot->is_hot) + pdf_set_annot_has_changed(ctx, annot); +} + +fz_matrix +pdf_annot_transform(fz_context *ctx, pdf_annot *annot) +{ + fz_rect bbox, rect; + fz_matrix matrix; + float w, h, x, y; + pdf_obj *ap = pdf_annot_ap(ctx, annot); + + rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect)); + bbox = pdf_xobject_bbox(ctx, ap); + matrix = pdf_xobject_matrix(ctx, ap); + + bbox = fz_transform_rect(bbox, matrix); + if (bbox.x1 == bbox.x0) + w = 0; + else + w = (rect.x1 - rect.x0) / (bbox.x1 - bbox.x0); + if (bbox.y1 == bbox.y0) + h = 0; + else + h = (rect.y1 - rect.y0) / (bbox.y1 - bbox.y0); + x = rect.x0 - (bbox.x0 * w); + y = rect.y0 - (bbox.y0 * h); + + return fz_pre_scale(fz_translate(x, y), w, h); +} + +/* + Internal function for creating a new pdf annotation. +*/ +static pdf_annot * +pdf_new_annot(fz_context *ctx, pdf_page *page, pdf_obj *obj) +{ + pdf_annot *annot; + + annot = fz_malloc_struct(ctx, pdf_annot); + annot->refs = 1; + annot->page = page; /* only borrowed, as the page owns the annot */ + annot->obj = pdf_keep_obj(ctx, obj); + + return annot; +} + +void +pdf_nuke_annots(fz_context *ctx, pdf_page *page) +{ + pdf_annot *annot; + pdf_annot *next; + + for (annot = page->annots; annot; annot = next) + { + next = annot->next; + pdf_drop_obj(ctx, annot->obj); + annot->obj = NULL; + pdf_drop_annot(ctx, annot); + } + for (annot = page->widgets; annot; annot = next) + { + next = annot->next; + pdf_drop_obj(ctx, annot->obj); + annot->obj = NULL; + pdf_drop_annot(ctx, annot); + } + + page->annots = NULL; + page->widgets = NULL; + page->annot_tailp = &page->annots; + page->widget_tailp = &page->widgets; +} + +static pdf_annot *find_and_unlink_annot_from_list(fz_context *ctx, pdf_annot **prev, pdf_obj *obj) +{ + int num = pdf_to_num(ctx, obj); + pdf_annot *node; + + node = *prev; + while (node) + { + if (pdf_to_num(ctx, node->obj) == num) + { + *prev = node->next; + node->next = NULL; + return node; + } + else + { + prev = &node->next; + node = node->next; + } + } + + return NULL; +} + +void +pdf_sync_annots(fz_context *ctx, pdf_page *page) +{ + pdf_annot *old_annots; + pdf_annot *old_widgets; + pdf_annot *annot, *next; + pdf_obj *annots; + pdf_obj *subtype; + int i, n; + + // Save list of annots loaded last time (if any). + old_annots = page->annots; + old_widgets = page->widgets; + page->annots = NULL; + page->widgets = NULL; + page->annot_tailp = &page->annots; + page->widget_tailp = &page->widgets; + + // Create new list of annots (reusing old annots when possible) + annots = pdf_dict_get(ctx, page->obj, PDF_NAME(Annots)); + n = pdf_array_len(ctx, annots); + for (i = 0; i < n; ++i) + { + pdf_obj *obj = pdf_array_get(ctx, annots, i); + if (pdf_is_dict(ctx, obj)) + { + subtype = pdf_dict_get(ctx, obj, PDF_NAME(Subtype)); + if (pdf_name_eq(ctx, subtype, PDF_NAME(Link))) + continue; + if (pdf_name_eq(ctx, subtype, PDF_NAME(Popup))) + continue; + + if (pdf_name_eq(ctx, subtype, PDF_NAME(Widget))) + { + annot = find_and_unlink_annot_from_list(ctx, &old_widgets, obj); + if (!annot) + annot = pdf_new_annot(ctx, page, obj); + *page->widget_tailp = annot; + page->widget_tailp = &annot->next; + } + else + { + annot = find_and_unlink_annot_from_list(ctx, &old_annots, obj); + if (!annot) + annot = pdf_new_annot(ctx, page, obj); + *page->annot_tailp = annot; + page->annot_tailp = &annot->next; + } + } + } + + // Nuke the annot structs that are no longer used on the page + for (annot = old_annots; annot; annot = next) + { + next = annot->next; + pdf_drop_obj(ctx, annot->obj); + annot->obj = NULL; + pdf_drop_annot(ctx, annot); + } + for (annot = old_widgets; annot; annot = next) + { + next = annot->next; + pdf_drop_obj(ctx, annot->obj); + annot->obj = NULL; + pdf_drop_annot(ctx, annot); + } +} + +void +pdf_load_annots(fz_context *ctx, pdf_page *page) +{ + pdf_sync_annots(ctx, page); + + /* We need to run a resynth pass on the annotations on this + * page. That means rerunning it on the complete document. */ + page->doc->resynth_required = 1; + /* And actually update the page so that any annotations required + * get synthesised. */ + pdf_update_page(ctx, page); +} + +pdf_annot * +pdf_first_annot(fz_context *ctx, pdf_page *page) +{ + return page ? page->annots : NULL; +} + +pdf_annot * +pdf_next_annot(fz_context *ctx, pdf_annot *annot) +{ + return annot ? annot->next : NULL; +} + +pdf_obj *pdf_annot_obj(fz_context *ctx, pdf_annot *annot) +{ + return annot ? annot->obj : NULL; +} + +pdf_page *pdf_annot_page(fz_context *ctx, pdf_annot *annot) +{ + return annot ? annot->page : NULL; +} + +fz_rect +pdf_bound_annot(fz_context *ctx, pdf_annot *annot) +{ + fz_matrix page_ctm; + fz_rect rect; + int flags; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect)); + pdf_page_transform(ctx, annot->page, NULL, &page_ctm); + + flags = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(F)); + if (flags & PDF_ANNOT_IS_NO_ROTATE) + { + int rotate = pdf_dict_get_inheritable_int(ctx, annot->page->obj, PDF_NAME(Rotate)); + fz_point tp = fz_transform_point_xy(rect.x0, rect.y1, page_ctm); + page_ctm = fz_concat(page_ctm, fz_translate(-tp.x, -tp.y)); + page_ctm = fz_concat(page_ctm, fz_rotate(-rotate)); + page_ctm = fz_concat(page_ctm, fz_translate(tp.x, tp.y)); + } + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return fz_transform_rect(rect, page_ctm); +} + +void +pdf_annot_request_resynthesis(fz_context *ctx, pdf_annot *annot) +{ + if (annot == NULL) + return; + + annot->needs_new_ap = 1; + annot->page->doc->resynth_required = 1; +} + +void +pdf_annot_request_synthesis(fz_context *ctx, pdf_annot *annot) +{ + if (annot == NULL) + return; + if (!pdf_annot_ap(ctx, annot)) + pdf_annot_request_resynthesis(ctx, annot); +} + +int +pdf_annot_needs_resynthesis(fz_context *ctx, pdf_annot *annot) +{ + return annot ? annot->needs_new_ap : 0; +} + +void pdf_set_annot_resynthesised(fz_context *ctx, pdf_annot *annot) +{ + if (annot == NULL) + return; + + annot->needs_new_ap = 0; + pdf_set_annot_has_changed(ctx, annot); +} + +void pdf_set_annot_has_changed(fz_context *ctx, pdf_annot *annot) +{ + if (annot == NULL) + return; + + annot->has_new_ap = 1; +} + +void +pdf_dirty_annot(fz_context *ctx, pdf_annot *annot) +{ + pdf_annot_request_resynthesis(ctx, annot); +} + +const char * +pdf_string_from_annot_type(fz_context *ctx, enum pdf_annot_type type) +{ + switch (type) + { + case PDF_ANNOT_TEXT: return "Text"; + case PDF_ANNOT_LINK: return "Link"; + case PDF_ANNOT_FREE_TEXT: return "FreeText"; + case PDF_ANNOT_LINE: return "Line"; + case PDF_ANNOT_SQUARE: return "Square"; + case PDF_ANNOT_CIRCLE: return "Circle"; + case PDF_ANNOT_POLYGON: return "Polygon"; + case PDF_ANNOT_POLY_LINE: return "PolyLine"; + case PDF_ANNOT_HIGHLIGHT: return "Highlight"; + case PDF_ANNOT_UNDERLINE: return "Underline"; + case PDF_ANNOT_SQUIGGLY: return "Squiggly"; + case PDF_ANNOT_STRIKE_OUT: return "StrikeOut"; + case PDF_ANNOT_REDACT: return "Redact"; + case PDF_ANNOT_STAMP: return "Stamp"; + case PDF_ANNOT_CARET: return "Caret"; + case PDF_ANNOT_INK: return "Ink"; + case PDF_ANNOT_POPUP: return "Popup"; + case PDF_ANNOT_FILE_ATTACHMENT: return "FileAttachment"; + case PDF_ANNOT_SOUND: return "Sound"; + case PDF_ANNOT_MOVIE: return "Movie"; + case PDF_ANNOT_RICH_MEDIA: return "RichMedia"; + case PDF_ANNOT_WIDGET: return "Widget"; + case PDF_ANNOT_SCREEN: return "Screen"; + case PDF_ANNOT_PRINTER_MARK: return "PrinterMark"; + case PDF_ANNOT_TRAP_NET: return "TrapNet"; + case PDF_ANNOT_WATERMARK: return "Watermark"; + case PDF_ANNOT_3D: return "3D"; + case PDF_ANNOT_PROJECTION: return "Projection"; + default: return "UNKNOWN"; + } +} + +enum pdf_annot_type +pdf_annot_type_from_string(fz_context *ctx, const char *subtype) +{ + if (!strcmp("Text", subtype)) return PDF_ANNOT_TEXT; + if (!strcmp("Link", subtype)) return PDF_ANNOT_LINK; + if (!strcmp("FreeText", subtype)) return PDF_ANNOT_FREE_TEXT; + if (!strcmp("Line", subtype)) return PDF_ANNOT_LINE; + if (!strcmp("Square", subtype)) return PDF_ANNOT_SQUARE; + if (!strcmp("Circle", subtype)) return PDF_ANNOT_CIRCLE; + if (!strcmp("Polygon", subtype)) return PDF_ANNOT_POLYGON; + if (!strcmp("PolyLine", subtype)) return PDF_ANNOT_POLY_LINE; + if (!strcmp("Highlight", subtype)) return PDF_ANNOT_HIGHLIGHT; + if (!strcmp("Underline", subtype)) return PDF_ANNOT_UNDERLINE; + if (!strcmp("Squiggly", subtype)) return PDF_ANNOT_SQUIGGLY; + if (!strcmp("StrikeOut", subtype)) return PDF_ANNOT_STRIKE_OUT; + if (!strcmp("Redact", subtype)) return PDF_ANNOT_REDACT; + if (!strcmp("Stamp", subtype)) return PDF_ANNOT_STAMP; + if (!strcmp("Caret", subtype)) return PDF_ANNOT_CARET; + if (!strcmp("Ink", subtype)) return PDF_ANNOT_INK; + if (!strcmp("Popup", subtype)) return PDF_ANNOT_POPUP; + if (!strcmp("FileAttachment", subtype)) return PDF_ANNOT_FILE_ATTACHMENT; + if (!strcmp("Sound", subtype)) return PDF_ANNOT_SOUND; + if (!strcmp("Movie", subtype)) return PDF_ANNOT_MOVIE; + if (!strcmp("RichMedia", subtype)) return PDF_ANNOT_RICH_MEDIA; + if (!strcmp("Widget", subtype)) return PDF_ANNOT_WIDGET; + if (!strcmp("Screen", subtype)) return PDF_ANNOT_SCREEN; + if (!strcmp("PrinterMark", subtype)) return PDF_ANNOT_PRINTER_MARK; + if (!strcmp("TrapNet", subtype)) return PDF_ANNOT_TRAP_NET; + if (!strcmp("Watermark", subtype)) return PDF_ANNOT_WATERMARK; + if (!strcmp("3D", subtype)) return PDF_ANNOT_3D; + if (!strcmp("Projection", subtype)) return PDF_ANNOT_PROJECTION; + return PDF_ANNOT_UNKNOWN; +} + +static void +begin_annot_op(fz_context *ctx, pdf_annot *annot, const char *op) +{ + if (!annot->page) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "annotation not bound to any page"); + + pdf_begin_operation(ctx, annot->page->doc, op); +} + +static void +end_annot_op(fz_context *ctx, pdf_annot *annot) +{ + pdf_end_operation(ctx, annot->page->doc); +} + +static void +abandon_annot_op(fz_context *ctx, pdf_annot *annot) +{ + pdf_abandon_operation(ctx, annot->page->doc); +} + +static int is_allowed_subtype(fz_context *ctx, pdf_annot *annot, pdf_obj *property, pdf_obj **allowed) +{ + pdf_obj *subtype; + + subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype)); + while (*allowed) { + if (pdf_name_eq(ctx, subtype, *allowed)) + return 1; + allowed++; + } + + return 0; +} + +static int is_allowed_subtype_wrap(fz_context *ctx, pdf_annot *annot, pdf_obj *property, pdf_obj **allowed) +{ + int ret; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + ret = is_allowed_subtype(ctx, annot, property, allowed); + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +static void check_allowed_subtypes(fz_context *ctx, pdf_annot *annot, pdf_obj *property, pdf_obj **allowed) +{ + pdf_obj *subtype; + + subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype)); + if (!is_allowed_subtype(ctx, annot, property, allowed)) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "%s annotations have no %s property", pdf_to_name(ctx, subtype), pdf_to_name(ctx, property)); +} + +pdf_annot * +pdf_create_annot_raw(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; + pdf_obj *free_arr = NULL; + + fz_var(annot); + fz_var(ind_obj); + fz_var(free_arr); + 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_ARGUMENT, "cannot create unknown annotation"); + + annot_arr = pdf_dict_get(ctx, page->obj, PDF_NAME(Annots)); + if (!pdf_is_array(ctx, annot_arr)) + annot_arr = pdf_dict_put_array(ctx, page->obj, PDF_NAME(Annots), 0); + else if (pdf_is_indirect(ctx, annot_arr)) + { + free_arr = annot_arr = pdf_copy_array(ctx, annot_arr); + pdf_dict_put(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); + + /* + 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); + + /* + 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. + */ + if (type == PDF_ANNOT_WIDGET) + { + *page->widget_tailp = annot; + page->widget_tailp = &annot->next; + } + else + { + *page->annot_tailp = annot; + page->annot_tailp = &annot->next; + } + } + fz_always(ctx) + { + pdf_drop_obj(ctx, free_arr); + pdf_drop_obj(ctx, annot_obj); + pdf_drop_obj(ctx, ind_obj); + } + fz_catch(ctx) + { + pdf_drop_annots(ctx, annot); + fz_rethrow(ctx); + } + + return pdf_keep_annot(ctx, annot); +} + +fz_link * +pdf_create_link(fz_context *ctx, pdf_page *page, fz_rect bbox, const char *uri) +{ + fz_link *link = NULL; + pdf_document *doc = page->doc; + pdf_obj *annot_obj = pdf_new_dict(ctx, doc, 0); + pdf_obj *ind_obj = NULL; + pdf_obj *bs = NULL; + pdf_obj *a = NULL; + fz_link **linkp; + fz_rect page_mediabox; + fz_matrix page_ctm; + fz_rect rect; + pdf_obj *free_arr = NULL; + + fz_var(link); + fz_var(ind_obj); + fz_var(bs); + fz_var(a); + fz_var(free_arr); + + pdf_begin_operation(ctx, page->doc, "Create Link"); + + fz_try(ctx) + { + int ind_obj_num; + pdf_obj *annot_arr; + + pdf_page_transform(ctx, page, &page_mediabox, &page_ctm); + page_ctm = fz_invert_matrix(page_ctm); + rect = fz_transform_rect(bbox, page_ctm); + + annot_arr = pdf_dict_get(ctx, page->obj, PDF_NAME(Annots)); + if (!pdf_is_array(ctx, annot_arr)) + annot_arr = pdf_dict_put_array(ctx, page->obj, PDF_NAME(Annots), 0); + else if (pdf_is_indirect(ctx, annot_arr)) + { + free_arr = annot_arr = pdf_copy_array(ctx, annot_arr); + pdf_dict_put(ctx, page->obj, PDF_NAME(Annots), annot_arr); + } + + pdf_dict_put(ctx, annot_obj, PDF_NAME(Type), PDF_NAME(Annot)); + pdf_dict_put(ctx, annot_obj, PDF_NAME(Subtype), PDF_NAME(Link)); + pdf_dict_put_rect(ctx, annot_obj, PDF_NAME(Rect), rect); + + bs = pdf_dict_put_dict(ctx, annot_obj, PDF_NAME(BS), 4); + pdf_dict_put(ctx, bs, PDF_NAME(S), PDF_NAME(S)); + pdf_dict_put(ctx, bs, PDF_NAME(Type), PDF_NAME(Border)); + pdf_dict_put_int(ctx, bs, PDF_NAME(W), 0); + + pdf_dict_put_drop(ctx, annot_obj, PDF_NAME(A), + pdf_new_action_from_link(ctx, doc, uri)); + + /* + 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); + + link = (fz_link *) pdf_new_link(ctx, page, bbox, uri, annot_obj); + + linkp = &page->links; + + while (*linkp != NULL) + linkp = &(*linkp)->next; + + *linkp = link; + pdf_end_operation(ctx, page->doc); + } + fz_always(ctx) + { + pdf_drop_obj(ctx, free_arr); + pdf_drop_obj(ctx, annot_obj); + pdf_drop_obj(ctx, ind_obj); + } + fz_catch(ctx) + { + pdf_abandon_operation(ctx, page->doc); + fz_rethrow(ctx); + } + + return fz_keep_link(ctx, link); +} + +void pdf_delete_link(fz_context *ctx, pdf_page *page, fz_link *link) +{ + fz_link **linkptr; + pdf_obj *annots; + int i; + + if (link == NULL || page == NULL || page != ((pdf_link *) link)->page) + return; + + for (linkptr = &page->links; *linkptr; linkptr = &((*linkptr)->next)) + { + if (*linkptr == link) + break; + } + + if (*linkptr == NULL) + return; + + /* Link may no longer borrow page pointer, since they are separated */ + ((pdf_link *) link)->page = NULL; + + pdf_begin_operation(ctx, page->doc, "Delete Link"); + + fz_try(ctx) + { + + annots = pdf_dict_get(ctx, page->obj, PDF_NAME(Annots)); + i = pdf_array_find(ctx, annots, ((pdf_link *) link)->obj); + if (i >= 0) + pdf_array_delete(ctx, annots, i); + *linkptr = link->next; + link->next = NULL; + fz_drop_link(ctx, link); + pdf_end_operation(ctx, page->doc); + } + fz_catch(ctx) + { + pdf_abandon_operation(ctx, page->doc); + fz_rethrow(ctx); + } +} + +static pdf_obj * +pdf_add_popup_annot(fz_context *ctx, pdf_annot *annot) +{ + pdf_obj *annots, *popup; + + popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup)); + if (popup) + return popup; + + annots = pdf_dict_get(ctx, annot->page->obj, PDF_NAME(Annots)); + if (!annots) + return NULL; + + popup = pdf_add_new_dict(ctx, annot->page->doc, 4); + pdf_array_push_drop(ctx, annots, popup); + + pdf_dict_put(ctx, popup, PDF_NAME(Type), PDF_NAME(Annot)); + pdf_dict_put(ctx, popup, PDF_NAME(Subtype), PDF_NAME(Popup)); + pdf_dict_put(ctx, popup, PDF_NAME(Parent), annot->obj); + pdf_dict_put_rect(ctx, popup, PDF_NAME(Rect), fz_make_rect(0,0,0,0)); + + pdf_dict_put(ctx, annot->obj, PDF_NAME(Popup), popup); + + return popup; +} + +static pdf_obj *popup_subtypes[] = { + PDF_NAME(Text), + PDF_NAME(FreeText), + PDF_NAME(Line), + PDF_NAME(Square), + PDF_NAME(Circle), + PDF_NAME(Polygon), + PDF_NAME(PolyLine), + PDF_NAME(Highlight), + PDF_NAME(Underline), + PDF_NAME(Squiggly), + PDF_NAME(StrikeOut), + PDF_NAME(Stamp), + PDF_NAME(Caret), + PDF_NAME(Ink), + PDF_NAME(FileAttachment), + PDF_NAME(Sound), + PDF_NAME(Redact), + NULL, +}; + +int +pdf_annot_has_popup(fz_context *ctx, pdf_annot *annot) +{ + return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(Popup), popup_subtypes); +} + +void pdf_set_annot_popup(fz_context *ctx, pdf_annot *annot, fz_rect rect) +{ + fz_matrix page_ctm, inv_page_ctm; + pdf_obj *popup; + + begin_annot_op(ctx, annot, "Set popup"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(Popup), popup_subtypes); + pdf_page_transform(ctx, annot->page, NULL, &page_ctm); + inv_page_ctm = fz_invert_matrix(page_ctm); + rect = fz_transform_rect(rect, inv_page_ctm); + popup = pdf_add_popup_annot(ctx, annot); + pdf_dict_put_rect(ctx, popup, PDF_NAME(Rect), rect); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +fz_rect pdf_annot_popup(fz_context *ctx, pdf_annot *annot) +{ + fz_matrix page_ctm; + fz_rect rect; + pdf_obj *popup; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(Popup), popup_subtypes); + pdf_page_transform(ctx, annot->page, NULL, &page_ctm); + popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup)); + rect = pdf_dict_get_rect(ctx, popup, PDF_NAME(Rect)); + rect = fz_transform_rect(rect, page_ctm); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return rect; +} + +pdf_annot * +pdf_create_annot(fz_context *ctx, pdf_page *page, enum pdf_annot_type type) +{ + static const float black[3] = { 0, 0, 0 }; + static const float red[3] = { 1, 0, 0 }; + static const float green[3] = { 0, 1, 0 }; + static const float blue[3] = { 0, 0, 1 }; + static const float yellow[3] = { 1, 1, 0 }; + static const float magenta[3] = { 1, 0, 1 }; + + int flags = PDF_ANNOT_IS_PRINT; /* Make printable as default */ + + pdf_annot *annot = NULL; + + fz_var(annot); + + pdf_begin_operation(ctx, page->doc, "Create Annotation"); + + fz_try(ctx) + { + annot = pdf_create_annot_raw(ctx, page, type); + + switch (type) + { + default: + break; + + case PDF_ANNOT_TEXT: + case PDF_ANNOT_FILE_ATTACHMENT: + case PDF_ANNOT_SOUND: + { + fz_rect icon_rect = { 12, 12, 12+20, 12+20 }; + flags = PDF_ANNOT_IS_PRINT | PDF_ANNOT_IS_NO_ZOOM | PDF_ANNOT_IS_NO_ROTATE; + pdf_set_annot_rect(ctx, annot, icon_rect); + pdf_set_annot_color(ctx, annot, 3, yellow); + pdf_set_annot_popup(ctx, annot, fz_make_rect(32, 12, 32+200, 12+100)); + } + break; + + case PDF_ANNOT_FREE_TEXT: + { + fz_rect text_rect = { 12, 12, 12+200, 12+100 }; + + /* Use undocumented Adobe property to match page rotation. */ + int rot = pdf_dict_get_inheritable_int(ctx, page->obj, PDF_NAME(Rotate)); + if (rot != 0) + pdf_dict_put_int(ctx, annot->obj, PDF_NAME(Rotate), rot); + + pdf_set_annot_rect(ctx, annot, text_rect); + pdf_set_annot_border_width(ctx, annot, 0); + pdf_set_annot_default_appearance(ctx, annot, "Helv", 12, nelem(black), black); + } + break; + + case PDF_ANNOT_STAMP: + { + fz_rect stamp_rect = { 12, 12, 12+190, 12+50 }; + pdf_set_annot_rect(ctx, annot, stamp_rect); + pdf_set_annot_color(ctx, annot, 3, red); + pdf_set_annot_icon_name(ctx, annot, "Draft"); + } + break; + + case PDF_ANNOT_CARET: + { + fz_rect caret_rect = { 12, 12, 12+18, 12+15 }; + pdf_set_annot_rect(ctx, annot, caret_rect); + pdf_set_annot_color(ctx, annot, 3, blue); + } + break; + + case PDF_ANNOT_LINE: + { + fz_point a = { 12, 12 }, b = { 12 + 100, 12 + 50 }; + pdf_set_annot_line(ctx, annot, a, b); + pdf_set_annot_border_width(ctx, annot, 1); + pdf_set_annot_color(ctx, annot, 3, red); + } + break; + + case PDF_ANNOT_SQUARE: + case PDF_ANNOT_CIRCLE: + { + fz_rect shape_rect = { 12, 12, 12+100, 12+50 }; + fz_rect rd = { 0.5, 0.5, 0.5, 0.5 }; + pdf_set_annot_rect(ctx, annot, shape_rect); + pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), rd); + pdf_set_annot_border_width(ctx, annot, 1); + pdf_set_annot_color(ctx, annot, 3, red); + } + break; + + case PDF_ANNOT_POLYGON: + case PDF_ANNOT_POLY_LINE: + case PDF_ANNOT_INK: + { + fz_rect rd = { 0.5, 0.5, 0.5, 0.5 }; + pdf_set_annot_border_width(ctx, annot, 1); + pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), rd); + pdf_set_annot_color(ctx, annot, 3, red); + } + break; + + case PDF_ANNOT_HIGHLIGHT: + pdf_set_annot_color(ctx, annot, 3, yellow); + break; + case PDF_ANNOT_UNDERLINE: + pdf_set_annot_color(ctx, annot, 3, green); + break; + case PDF_ANNOT_STRIKE_OUT: + pdf_set_annot_color(ctx, annot, 3, red); + break; + case PDF_ANNOT_SQUIGGLY: + pdf_set_annot_color(ctx, annot, 3, magenta); + break; + } + + pdf_dict_put(ctx, annot->obj, PDF_NAME(P), page->obj); + pdf_dict_put_int(ctx, annot->obj, PDF_NAME(F), flags); + pdf_end_operation(ctx, page->doc); + } + fz_catch(ctx) + { + pdf_drop_annot(ctx, annot); + pdf_abandon_operation(ctx, page->doc); + fz_rethrow(ctx); + } + + return annot; +} + +static int +remove_from_tree(fz_context *ctx, pdf_obj *arr, pdf_obj *item, pdf_cycle_list *cycle_up) +{ + pdf_cycle_list cycle; + int i, n, res = 0; + + if (arr == NULL || pdf_cycle(ctx, &cycle, cycle_up, arr)) + return 0; + + n = pdf_array_len(ctx, arr); + for (i = 0; i < n; ++i) + { + pdf_obj *obj = pdf_array_get(ctx, arr, i); + if (!pdf_objcmp(ctx, obj, item)) + { + pdf_array_delete(ctx, arr, i); + res = 1; + break; + } + + if (remove_from_tree(ctx, pdf_dict_get(ctx, obj, PDF_NAME(Kids)), item, &cycle)) + { + res = 1; + break; + } + } + + return res; +} + +void +pdf_delete_annot(fz_context *ctx, pdf_page *page, pdf_annot *annot) +{ + pdf_document *doc; + pdf_annot **annotptr; + pdf_obj *annot_arr, *popup; + int i; + int is_widget = 0; + + if (annot == NULL || page == NULL || page != annot->page) + return; + + doc = page->doc; + + /* Look for the annot in the page's list */ + for (annotptr = &page->annots; *annotptr; annotptr = &(*annotptr)->next) + { + if (*annotptr == annot) + break; + } + + if (*annotptr == NULL) + { + is_widget = 1; + + /* Look also in the widget list*/ + for (annotptr = &page->widgets; *annotptr; annotptr = &(*annotptr)->next) + { + if (*annotptr == annot) + break; + } + } + + /* Check the passed annotation was of this page */ + if (*annotptr == NULL) + return; + + pdf_begin_operation(ctx, page->doc, "Delete Annotation"); + + /* Remove annot from page's list */ + *annotptr = annot->next; + + /* Annot may no longer borrow page pointer, since they are separated */ + annot->page = NULL; + + /* If the removed annotation was the last in the list adjust the end pointer */ + if (*annotptr == NULL) + { + if (is_widget) + page->widget_tailp = annotptr; + else + page->annot_tailp = annotptr; + } + + fz_try(ctx) + { + /* Remove the annot from the "Annots" array. */ + annot_arr = pdf_dict_get(ctx, page->obj, PDF_NAME(Annots)); + i = pdf_array_find(ctx, annot_arr, annot->obj); + if (i >= 0) + pdf_array_delete(ctx, annot_arr, i); + + /* Remove the associated Popup annotation from the Annots array */ + popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup)); + if (popup) + { + i = pdf_array_find(ctx, annot_arr, popup); + if (i >= 0) + pdf_array_delete(ctx, annot_arr, i); + } + + /* For a widget, remove also from the AcroForm tree */ + if (is_widget) + { + pdf_obj *root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root)); + pdf_obj *acroform = pdf_dict_get(ctx, root, PDF_NAME(AcroForm)); + pdf_obj *fields = pdf_dict_get(ctx, acroform, PDF_NAME(Fields)); + (void)remove_from_tree(ctx, fields, annot->obj, NULL); + } + + /* The garbage collection pass when saving will remove the annot object, + * removing it here may break files if multiple pages use the same annot. */ + pdf_end_operation(ctx, page->doc); + } + fz_always(ctx) + { + /* Drop the reference to annot previously held by the page list. */ + pdf_drop_annot(ctx, annot); + } + fz_catch(ctx) + { + pdf_abandon_operation(ctx, page->doc); + fz_rethrow(ctx); + } +} + +enum pdf_annot_type +pdf_annot_type(fz_context *ctx, pdf_annot *annot) +{ + enum pdf_annot_type ret; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + const char *subtype = pdf_dict_get_name(ctx, annot->obj, PDF_NAME(Subtype)); + ret = pdf_annot_type_from_string(ctx, subtype); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +int +pdf_annot_flags(fz_context *ctx, pdf_annot *annot) +{ + int ret; + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + ret = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(F)); + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +void +pdf_set_annot_flags(fz_context *ctx, pdf_annot *annot, int flags) +{ + begin_annot_op(ctx, annot, "Set flags"); + + fz_try(ctx) + { + pdf_dict_put_int(ctx, annot->obj, PDF_NAME(F), flags); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +static pdf_obj *rect_subtypes[] = { + PDF_NAME(Text), + PDF_NAME(FreeText), + PDF_NAME(Square), + PDF_NAME(Circle), + PDF_NAME(Redact), + PDF_NAME(Stamp), + PDF_NAME(Caret), + PDF_NAME(Popup), + PDF_NAME(FileAttachment), + PDF_NAME(Sound), + PDF_NAME(Movie), + PDF_NAME(Widget), + NULL, +}; + +int +pdf_annot_has_rect(fz_context *ctx, pdf_annot *annot) +{ + /* True for annotations where the user can manipulate the size or location + * of the annotation through the Rect. + * False for annotations where the Rect is computed from other + * annotation data such as InkList, QuadPoints, and Vertices. + */ + return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(Rect), rect_subtypes); +} + +fz_rect +pdf_annot_rect(fz_context *ctx, pdf_annot *annot) +{ + fz_matrix page_ctm; + fz_rect rect; + fz_rect rd; + + pdf_annot_push_local_xref(ctx, annot); + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(Rect), rect_subtypes); + pdf_page_transform(ctx, annot->page, NULL, &page_ctm); + rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect)); + + /* remove RD adjustment from bounding box to get design box */ + rd = pdf_annot_rect_diff(ctx, annot); + rect.x0 += rd.x0; + rect.x1 -= rd.x1; + rect.y0 += rd.y0; + rect.y1 -= rd.y1; + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return fz_transform_rect(rect, page_ctm); +} + +void +pdf_set_annot_rect(fz_context *ctx, pdf_annot *annot, fz_rect rect) +{ + fz_matrix page_ctm, inv_page_ctm; + fz_rect rd; + + begin_annot_op(ctx, annot, "Set rectangle"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(Rect), rect_subtypes); + + pdf_page_transform(ctx, annot->page, NULL, &page_ctm); + inv_page_ctm = fz_invert_matrix(page_ctm); + rect = fz_transform_rect(rect, inv_page_ctm); + + /* add RD adjustment to design box to get bounding box */ + rd = pdf_annot_rect_diff(ctx, annot); + rect.x0 -= rd.x0; + rect.x1 += rd.x1; + rect.y0 -= rd.y0; + rect.y1 += rd.y1; + + pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(Rect), rect); + + /* recalculate callout line as necessary */ + if (pdf_annot_has_callout(ctx, annot)) + { + pdf_set_annot_callout_point(ctx, annot, pdf_annot_callout_point(ctx, annot)); + } + + pdf_dirty_annot(ctx, annot); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } +} + +const char * +pdf_annot_contents(fz_context *ctx, pdf_annot *annot) +{ + return pdf_dict_get_text_string(ctx, annot->obj, PDF_NAME(Contents)); +} + +void +pdf_set_annot_contents(fz_context *ctx, pdf_annot *annot, const char *text) +{ + begin_annot_op(ctx, annot, "Set contents"); + + fz_try(ctx) + { + pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(Contents), text); + pdf_dict_del(ctx, annot->obj, PDF_NAME(RC)); /* not supported */ + pdf_dirty_annot(ctx, annot); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } +} + +static pdf_obj *markup_subtypes[] = { + PDF_NAME(Text), + PDF_NAME(FreeText), + PDF_NAME(Line), + PDF_NAME(Square), + PDF_NAME(Circle), + PDF_NAME(Polygon), + PDF_NAME(PolyLine), + PDF_NAME(Highlight), + PDF_NAME(Underline), + PDF_NAME(Squiggly), + PDF_NAME(StrikeOut), + PDF_NAME(Redact), + PDF_NAME(Stamp), + PDF_NAME(Caret), + PDF_NAME(Ink), + PDF_NAME(FileAttachment), + PDF_NAME(Sound), + NULL, +}; + +int +pdf_annot_has_rich_contents(fz_context *ctx, pdf_annot *annot) +{ + return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(RC), markup_subtypes); +} + +const char * +pdf_annot_rich_contents(fz_context *ctx, pdf_annot *annot) +{ + check_allowed_subtypes(ctx, annot, PDF_NAME(RC), markup_subtypes); + return pdf_dict_get_text_string(ctx, annot->obj, PDF_NAME(RC)); +} + +void +pdf_set_annot_rich_contents(fz_context *ctx, pdf_annot *annot, const char *plain, const char *rich) +{ + begin_annot_op(ctx, annot, "Set rich contents"); + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(RC), markup_subtypes); + pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(Contents), plain); + pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(RC), rich); + pdf_dirty_annot(ctx, annot); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } +} + +static pdf_obj *default_style_subtypes[] = { + PDF_NAME(FreeText), + PDF_NAME(Widget), + NULL, +}; + +int +pdf_annot_has_rich_defaults(fz_context *ctx, pdf_annot *annot) +{ + return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(DS), default_style_subtypes); +} + +const char * +pdf_annot_rich_defaults(fz_context *ctx, pdf_annot *annot) +{ + check_allowed_subtypes(ctx, annot, PDF_NAME(DS), default_style_subtypes); + return pdf_dict_get_text_string(ctx, annot->obj, PDF_NAME(DS)); +} + +void +pdf_set_annot_rich_defaults(fz_context *ctx, pdf_annot *annot, const char *style) +{ + begin_annot_op(ctx, annot, "Set rich defaults"); + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(DS), default_style_subtypes); + pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(DS), style); + pdf_dirty_annot(ctx, annot); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } +} + +int +pdf_annot_has_open(fz_context *ctx, pdf_annot *annot) +{ + int ret; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype)); + pdf_obj *popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup)); + ret = (subtype == PDF_NAME(Text) || popup); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +int +pdf_annot_is_open(fz_context *ctx, pdf_annot *annot) +{ + int ret = 0; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype)); + pdf_obj *popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup)); + if (popup) + ret = pdf_dict_get_bool(ctx, popup, PDF_NAME(Open)); + else if (subtype == PDF_NAME(Text)) + ret = pdf_dict_get_bool(ctx, annot->obj, PDF_NAME(Open)); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +void +pdf_set_annot_is_open(fz_context *ctx, pdf_annot *annot, int is_open) +{ + begin_annot_op(ctx, annot, is_open ? "Open" : "Close"); + + fz_try(ctx) + { + pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype)); + pdf_obj *popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup)); + if (popup) + { + pdf_dict_put_bool(ctx, popup, PDF_NAME(Open), is_open); + pdf_dirty_annot(ctx, annot); + } + else if (subtype == PDF_NAME(Text)) + { + pdf_dict_put_bool(ctx, annot->obj, PDF_NAME(Open), is_open); + pdf_dirty_annot(ctx, annot); + } + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } +} + +static pdf_obj *icon_name_subtypes[] = { + PDF_NAME(FileAttachment), + PDF_NAME(Sound), + PDF_NAME(Stamp), + PDF_NAME(Text), + NULL, +}; + +int +pdf_annot_has_icon_name(fz_context *ctx, pdf_annot *annot) +{ + return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(Name), icon_name_subtypes); +} + +const char * +pdf_annot_icon_name(fz_context *ctx, pdf_annot *annot) +{ + const char *ret; + pdf_obj *name; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(Name), icon_name_subtypes); + name = pdf_dict_get(ctx, annot->obj, PDF_NAME(Name)); + if (!name) + { + pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype)); + if (pdf_name_eq(ctx, subtype, PDF_NAME(Text))) + { + ret = "Note"; + break; + } + if (pdf_name_eq(ctx, subtype, PDF_NAME(Stamp))) + { + ret = ""; // Should be "Draft" according to spec + break; + } + if (pdf_name_eq(ctx, subtype, PDF_NAME(FileAttachment))) + { + ret = "PushPin"; + break; + } + if (pdf_name_eq(ctx, subtype, PDF_NAME(Sound))) + { + ret = "Speaker"; + break; + } + } + ret = pdf_to_name(ctx, name); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +void +pdf_set_annot_icon_name(fz_context *ctx, pdf_annot *annot, const char *name) +{ + begin_annot_op(ctx, annot, "Set icon name"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(Name), icon_name_subtypes); + if (name) + pdf_dict_put_name(ctx, annot->obj, PDF_NAME(Name), name); + else + pdf_dict_del(ctx, annot->obj, PDF_NAME(Name)); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +int +pdf_annot_is_standard_stamp(fz_context *ctx, pdf_annot *annot) +{ + pdf_obj *name = pdf_dict_get(ctx, annot->obj, PDF_NAME(Name)); + if (pdf_name_eq(ctx, name, PDF_NAME(Approved))) return 1; + else if (pdf_name_eq(ctx, name, PDF_NAME(AsIs))) return 1; + else if (pdf_name_eq(ctx, name, PDF_NAME(Confidential))) return 1; + else if (pdf_name_eq(ctx, name, PDF_NAME(Departmental))) return 1; + else if (pdf_name_eq(ctx, name, PDF_NAME(Draft))) return 1; + else if (pdf_name_eq(ctx, name, PDF_NAME(Experimental))) return 1; + else if (pdf_name_eq(ctx, name, PDF_NAME(Expired))) return 1; + else if (pdf_name_eq(ctx, name, PDF_NAME(Final))) return 1; + else if (pdf_name_eq(ctx, name, PDF_NAME(ForComment))) return 1; + else if (pdf_name_eq(ctx, name, PDF_NAME(ForPublicRelease))) return 1; + else if (pdf_name_eq(ctx, name, PDF_NAME(NotApproved))) return 1; + else if (pdf_name_eq(ctx, name, PDF_NAME(NotForPublicRelease))) return 1; + else if (pdf_name_eq(ctx, name, PDF_NAME(Sold))) return 1; + else if (pdf_name_eq(ctx, name, PDF_NAME(TopSecret))) return 1; + else if (pdf_annot_stamp_image_obj(ctx, annot) != NULL) return 1; + else return 0; +} + +enum pdf_line_ending pdf_line_ending_from_name(fz_context *ctx, pdf_obj *end) +{ + if (pdf_name_eq(ctx, end, PDF_NAME(None))) return PDF_ANNOT_LE_NONE; + else if (pdf_name_eq(ctx, end, PDF_NAME(Square))) return PDF_ANNOT_LE_SQUARE; + else if (pdf_name_eq(ctx, end, PDF_NAME(Circle))) return PDF_ANNOT_LE_CIRCLE; + else if (pdf_name_eq(ctx, end, PDF_NAME(Diamond))) return PDF_ANNOT_LE_DIAMOND; + else if (pdf_name_eq(ctx, end, PDF_NAME(OpenArrow))) return PDF_ANNOT_LE_OPEN_ARROW; + else if (pdf_name_eq(ctx, end, PDF_NAME(ClosedArrow))) return PDF_ANNOT_LE_CLOSED_ARROW; + else if (pdf_name_eq(ctx, end, PDF_NAME(Butt))) return PDF_ANNOT_LE_BUTT; + else if (pdf_name_eq(ctx, end, PDF_NAME(ROpenArrow))) return PDF_ANNOT_LE_R_OPEN_ARROW; + else if (pdf_name_eq(ctx, end, PDF_NAME(RClosedArrow))) return PDF_ANNOT_LE_R_CLOSED_ARROW; + else if (pdf_name_eq(ctx, end, PDF_NAME(Slash))) return PDF_ANNOT_LE_SLASH; + else return PDF_ANNOT_LE_NONE; +} + +enum pdf_line_ending pdf_line_ending_from_string(fz_context *ctx, const char *end) +{ + if (!strcmp(end, "None")) return PDF_ANNOT_LE_NONE; + else if (!strcmp(end, "Square")) return PDF_ANNOT_LE_SQUARE; + else if (!strcmp(end, "Circle")) return PDF_ANNOT_LE_CIRCLE; + else if (!strcmp(end, "Diamond")) return PDF_ANNOT_LE_DIAMOND; + else if (!strcmp(end, "OpenArrow")) return PDF_ANNOT_LE_OPEN_ARROW; + else if (!strcmp(end, "ClosedArrow")) return PDF_ANNOT_LE_CLOSED_ARROW; + else if (!strcmp(end, "Butt")) return PDF_ANNOT_LE_BUTT; + else if (!strcmp(end, "ROpenArrow")) return PDF_ANNOT_LE_R_OPEN_ARROW; + else if (!strcmp(end, "RClosedArrow")) return PDF_ANNOT_LE_R_CLOSED_ARROW; + else if (!strcmp(end, "Slash")) return PDF_ANNOT_LE_SLASH; + else return PDF_ANNOT_LE_NONE; +} + +pdf_obj *pdf_name_from_line_ending(fz_context *ctx, enum pdf_line_ending end) +{ + switch (end) + { + default: + case PDF_ANNOT_LE_NONE: return PDF_NAME(None); + case PDF_ANNOT_LE_SQUARE: return PDF_NAME(Square); + case PDF_ANNOT_LE_CIRCLE: return PDF_NAME(Circle); + case PDF_ANNOT_LE_DIAMOND: return PDF_NAME(Diamond); + case PDF_ANNOT_LE_OPEN_ARROW: return PDF_NAME(OpenArrow); + case PDF_ANNOT_LE_CLOSED_ARROW: return PDF_NAME(ClosedArrow); + case PDF_ANNOT_LE_BUTT: return PDF_NAME(Butt); + case PDF_ANNOT_LE_R_OPEN_ARROW: return PDF_NAME(ROpenArrow); + case PDF_ANNOT_LE_R_CLOSED_ARROW: return PDF_NAME(RClosedArrow); + case PDF_ANNOT_LE_SLASH: return PDF_NAME(Slash); + } +} + +const char *pdf_string_from_line_ending(fz_context *ctx, enum pdf_line_ending end) +{ + switch (end) + { + default: + case PDF_ANNOT_LE_NONE: return "None"; + case PDF_ANNOT_LE_SQUARE: return "Square"; + case PDF_ANNOT_LE_CIRCLE: return "Circle"; + case PDF_ANNOT_LE_DIAMOND: return "Diamond"; + case PDF_ANNOT_LE_OPEN_ARROW: return "OpenArrow"; + case PDF_ANNOT_LE_CLOSED_ARROW: return "ClosedArrow"; + case PDF_ANNOT_LE_BUTT: return "Butt"; + case PDF_ANNOT_LE_R_OPEN_ARROW: return "ROpenArrow"; + case PDF_ANNOT_LE_R_CLOSED_ARROW: return "RClosedArrow"; + case PDF_ANNOT_LE_SLASH: return "Slash"; + } +} + +static pdf_obj *line_ending_subtypes[] = { + PDF_NAME(FreeText), + PDF_NAME(Line), + PDF_NAME(PolyLine), + PDF_NAME(Polygon), + NULL, +}; + +int +pdf_annot_has_line_ending_styles(fz_context *ctx, pdf_annot *annot) +{ + return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(LE), line_ending_subtypes); +} + +void +pdf_annot_line_ending_styles(fz_context *ctx, pdf_annot *annot, + enum pdf_line_ending *start_style, + enum pdf_line_ending *end_style) +{ + pdf_obj *style; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(LE), line_ending_subtypes); + + style = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE)); + *start_style = pdf_line_ending_from_name(ctx, pdf_array_get(ctx, style, 0)); + *end_style = pdf_line_ending_from_name(ctx, pdf_array_get(ctx, style, 1)); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); +} + +enum pdf_line_ending +pdf_annot_line_start_style(fz_context *ctx, pdf_annot *annot) +{ + pdf_obj *le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE)); + return pdf_line_ending_from_name(ctx, pdf_array_get(ctx, le, 0)); +} + +enum pdf_line_ending +pdf_annot_line_end_style(fz_context *ctx, pdf_annot *annot) +{ + pdf_obj *le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE)); + return pdf_line_ending_from_name(ctx, pdf_array_get(ctx, le, 1)); +} + +void +pdf_set_annot_line_ending_styles(fz_context *ctx, pdf_annot *annot, + enum pdf_line_ending start_style, + enum pdf_line_ending end_style) +{ + pdf_obj *style; + + begin_annot_op(ctx, annot, "Set line endings"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(LE), line_ending_subtypes); + style = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(LE), 2); + pdf_array_put_drop(ctx, style, 0, pdf_name_from_line_ending(ctx, start_style)); + pdf_array_put_drop(ctx, style, 1, pdf_name_from_line_ending(ctx, end_style)); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +void +pdf_set_annot_line_start_style(fz_context *ctx, pdf_annot *annot, enum pdf_line_ending s) +{ + enum pdf_line_ending e = pdf_annot_line_end_style(ctx, annot); + pdf_set_annot_line_ending_styles(ctx, annot, s, e); +} + +void +pdf_set_annot_line_end_style(fz_context *ctx, pdf_annot *annot, enum pdf_line_ending e) +{ + enum pdf_line_ending s = pdf_annot_line_start_style(ctx, annot); + pdf_set_annot_line_ending_styles(ctx, annot, s, e); +} + +static pdf_obj *border_style_subtypes[] = { + PDF_NAME(Circle), + PDF_NAME(FreeText), + PDF_NAME(Ink), + PDF_NAME(Line), + PDF_NAME(Polygon), + PDF_NAME(PolyLine), + PDF_NAME(Square), + PDF_NAME(Widget), + NULL, +}; + +static pdf_obj *border_effect_subtypes[] = { + PDF_NAME(Circle), + PDF_NAME(FreeText), + PDF_NAME(Polygon), + PDF_NAME(Square), + NULL, +}; + +int +pdf_annot_has_border(fz_context *ctx, pdf_annot *annot) +{ + return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(BS), border_style_subtypes); +} + +int +pdf_annot_has_border_effect(fz_context *ctx, pdf_annot *annot) +{ + return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(BE), border_effect_subtypes); +} + +enum pdf_border_style +pdf_annot_border_style(fz_context *ctx, pdf_annot *annot) +{ + pdf_obj *bs, *s; + enum pdf_border_style style; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes); + bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS)); + s = pdf_dict_get(ctx, bs, PDF_NAME(S)); + + if (s == PDF_NAME(D)) + style = PDF_BORDER_STYLE_DASHED; + else if (s == PDF_NAME(B)) + style = PDF_BORDER_STYLE_BEVELED; + else if (s == PDF_NAME(I)) + style = PDF_BORDER_STYLE_INSET; + else if (s == PDF_NAME(U)) + style = PDF_BORDER_STYLE_UNDERLINE; + else + style = PDF_BORDER_STYLE_SOLID; + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return style; +} + +float +pdf_annot_border_width(fz_context *ctx, pdf_annot *annot) +{ + pdf_obj *bs, *bs_w, *border; + float ret = 1; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes); + + bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS)); + bs_w = pdf_dict_get(ctx, bs, PDF_NAME(W)); + if (pdf_is_number(ctx, bs_w)) + { + ret = pdf_to_real(ctx, bs_w); + break; + } + border = pdf_dict_get(ctx, annot->obj, PDF_NAME(Border)); + bs_w = pdf_array_get(ctx, border, 2); + if (pdf_is_number(ctx, bs_w)) + ret = pdf_to_real(ctx, bs_w); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +float +pdf_annot_border(fz_context *ctx, pdf_annot *annot) +{ + /* DEPRECATED */ + return pdf_annot_border_width(ctx, annot); +} + +int +pdf_annot_border_dash_count(fz_context *ctx, pdf_annot *annot) +{ + pdf_obj *bs, *d, *border; + int count; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes); + bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS)); + d = pdf_dict_get(ctx, bs, PDF_NAME(D)); + /* Query legacy dash pattern as a fallback */ + border = pdf_dict_get(ctx, annot->obj, PDF_NAME(Border)); + if (!pdf_is_array(ctx, d) && pdf_is_array(ctx, border)) + d = pdf_array_get(ctx, border, 3); + count = pdf_array_len(ctx, d); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return count; +} + +float +pdf_annot_border_dash_item(fz_context *ctx, pdf_annot *annot, int i) +{ + pdf_obj *bs, *d, *border; + float length; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes); + bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS)); + d = pdf_dict_get(ctx, bs, PDF_NAME(D)); + /* Query legacy dash pattern as a fallback */ + border = pdf_dict_get(ctx, annot->obj, PDF_NAME(Border)); + if (!pdf_is_array(ctx, d) && pdf_is_array(ctx, border)) + d = pdf_array_get(ctx, border, 3); + length = pdf_array_get_real(ctx, d, i); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return length; +} + +enum pdf_border_effect +pdf_annot_border_effect(fz_context *ctx, pdf_annot *annot) +{ + pdf_obj *be; + enum pdf_border_effect effect; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(BE), border_effect_subtypes); + be = pdf_dict_get(ctx, annot->obj, PDF_NAME(BE)); + if (pdf_dict_get(ctx, be, PDF_NAME(S)) == PDF_NAME(C)) + effect = PDF_BORDER_EFFECT_CLOUDY; + else + effect = PDF_BORDER_EFFECT_NONE; + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return effect; +} + +float +pdf_annot_border_effect_intensity(fz_context *ctx, pdf_annot *annot) +{ + pdf_obj *be; + float intensity; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(BE), border_effect_subtypes); + be = pdf_dict_get(ctx, annot->obj, PDF_NAME(BE)); + intensity = pdf_dict_get_real(ctx, be, PDF_NAME(I)); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return intensity; +} + +void +pdf_set_annot_border_width(fz_context *ctx, pdf_annot *annot, float width) +{ + pdf_obj *bs; + pdf_obj *type; + float old_width, adj; + pdf_obj *rectobj; + + begin_annot_op(ctx, annot, "Set border width"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes); + bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS)); + if (!pdf_is_dict(ctx, bs)) + bs = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(BS), 1); + pdf_dict_put(ctx, bs, PDF_NAME(Type), PDF_NAME(Border)); + old_width = pdf_dict_get_real(ctx, bs, PDF_NAME(W)); + pdf_dict_put_real(ctx, bs, PDF_NAME(W), width); + rectobj = pdf_dict_get(ctx, annot->obj, PDF_NAME(Rect)); + if (pdf_is_array(ctx, rectobj)) { + fz_rect rect = pdf_to_rect(ctx, rectobj); + adj = (width - old_width)/2; + rect.x0 -= adj; + rect.x1 += adj; + rect.y0 -= adj; + rect.y1 += adj; + pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(Rect), rect); + /* For any of these types, we want to adjust the Rect and RD + * together so that Rect+RD doesn't change, but the border stroke + * stays centred on the same point. */ + type = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype)); + if (pdf_name_eq(ctx, type, PDF_NAME(Square)) || + pdf_name_eq(ctx, type, PDF_NAME(Circle))) + { + fz_rect rd = pdf_annot_rect_diff(ctx, annot); + rd.x0 += adj; + rd.x1 += adj; + rd.y0 += adj; + rd.y1 += adj; + pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), rd); + } + } + pdf_dict_del(ctx, annot->obj, PDF_NAME(Border)); /* deprecated */ + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +void +pdf_set_annot_border(fz_context *ctx, pdf_annot *annot, float w) +{ + /* DEPRECATED */ + pdf_set_annot_border_width(ctx, annot, w); +} + +void +pdf_set_annot_border_style(fz_context *ctx, pdf_annot *annot, enum pdf_border_style style) +{ + pdf_obj *bs, *s; + + begin_annot_op(ctx, annot, "Set border style"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes); + bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS)); + if (!pdf_is_dict(ctx, bs)) + bs = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(BS), 1); + pdf_dict_put(ctx, bs, PDF_NAME(Type), PDF_NAME(Border)); + switch (style) + { + default: + case PDF_BORDER_STYLE_SOLID: s = PDF_NAME(S); break; + case PDF_BORDER_STYLE_DASHED: s = PDF_NAME(D); break; + case PDF_BORDER_STYLE_BEVELED: s = PDF_NAME(B); break; + case PDF_BORDER_STYLE_INSET: s = PDF_NAME(I); break; + case PDF_BORDER_STYLE_UNDERLINE: s = PDF_NAME(U); break; + } + pdf_dict_put(ctx, bs, PDF_NAME(S), s); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +void +pdf_clear_annot_border_dash(fz_context *ctx, pdf_annot *annot) +{ + pdf_obj *bs, *border; + + begin_annot_op(ctx, annot, "Clear border dash pattern"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes); + bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS)); + if (!pdf_is_dict(ctx, bs)) + bs = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(BS), 1); + pdf_dict_del(ctx, bs, PDF_NAME(D)); + /* Remove legacy dash pattern */ + border = pdf_dict_get(ctx, annot->obj, PDF_NAME(Border)); + if (pdf_is_array(ctx, border)) + pdf_array_delete(ctx, border, 3); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +void +pdf_add_annot_border_dash_item(fz_context *ctx, pdf_annot *annot, float length) +{ + pdf_obj *bs, *d, *border; + + begin_annot_op(ctx, annot, "Add border dash pattern item"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes); + bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS)); + if (!pdf_is_dict(ctx, bs)) + bs = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(BS), 1); + d = pdf_dict_get(ctx, bs, PDF_NAME(D)); + if (!pdf_is_array(ctx, d)) + d = pdf_dict_put_array(ctx, bs, PDF_NAME(D), 1); + pdf_array_push_real(ctx, d, length); + /* Remove legacy dash pattern */ + border = pdf_dict_get(ctx, annot->obj, PDF_NAME(Border)); + if (pdf_is_array(ctx, border)) + pdf_array_delete(ctx, border, 3); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +void +pdf_set_annot_border_effect(fz_context *ctx, pdf_annot *annot, enum pdf_border_effect effect) +{ + pdf_obj *be, *s; + + begin_annot_op(ctx, annot, "Set border effect"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(BE), border_effect_subtypes); + be = pdf_dict_get(ctx, annot->obj, PDF_NAME(BE)); + if (!pdf_is_dict(ctx, be)) + be = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(BE), 1); + switch (effect) + { + default: + case PDF_BORDER_EFFECT_NONE: s = PDF_NAME(S); break; + case PDF_BORDER_EFFECT_CLOUDY: s = PDF_NAME(C); break; + } + pdf_dict_put(ctx, be, PDF_NAME(S), s); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +void +pdf_set_annot_border_effect_intensity(fz_context *ctx, pdf_annot *annot, float intensity) +{ + pdf_obj *be; + + begin_annot_op(ctx, annot, "Set border effect intensity"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(BE), border_effect_subtypes); + be = pdf_dict_get(ctx, annot->obj, PDF_NAME(BE)); + if (!pdf_is_dict(ctx, be)) + be = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(BE), 1); + pdf_dict_put_real(ctx, be, PDF_NAME(I), intensity); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +fz_text_language +pdf_document_language(fz_context *ctx, pdf_document *doc) +{ + pdf_obj *trailer = pdf_trailer(ctx, doc); + pdf_obj *root = pdf_dict_get(ctx, trailer, PDF_NAME(Root)); + pdf_obj *lang = pdf_dict_get(ctx, root, PDF_NAME(Lang)); + return fz_text_language_from_string(pdf_to_text_string(ctx, lang)); +} + +void pdf_set_document_language(fz_context *ctx, pdf_document *doc, fz_text_language lang) +{ + pdf_obj *trailer = pdf_trailer(ctx, doc); + pdf_obj *root = pdf_dict_get(ctx, trailer, PDF_NAME(Root)); + char buf[8]; + if (lang == FZ_LANG_UNSET) + pdf_dict_del(ctx, root, PDF_NAME(Lang)); + else + pdf_dict_put_text_string(ctx, root, PDF_NAME(Lang), fz_string_from_text_language(buf, lang)); +} + +fz_text_language +pdf_annot_language(fz_context *ctx, pdf_annot *annot) +{ + fz_text_language ret; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + pdf_obj *lang = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(Lang)); + if (lang) + ret = fz_text_language_from_string(pdf_to_str_buf(ctx, lang)); + else + ret = pdf_document_language(ctx, annot->page->doc); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +void +pdf_set_annot_language(fz_context *ctx, pdf_annot *annot, fz_text_language lang) +{ + char buf[8]; + + begin_annot_op(ctx, annot, "Set language"); + + fz_try(ctx) + { + if (lang == FZ_LANG_UNSET) + pdf_dict_del(ctx, annot->obj, PDF_NAME(Lang)); + else + pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(Lang), fz_string_from_text_language(buf, lang)); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +static pdf_obj *quadding_subtypes[] = { + PDF_NAME(FreeText), + PDF_NAME(Widget), + NULL, +}; + +int +pdf_annot_has_quadding(fz_context *ctx, pdf_annot *annot) +{ + return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(Q), quadding_subtypes); +} + +int +pdf_annot_quadding(fz_context *ctx, pdf_annot *annot) +{ + int q; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(Q), quadding_subtypes); + q = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(Q)); + q = (q < 0 || q > 2) ? 0 : q; + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return q; +} + +void +pdf_set_annot_quadding(fz_context *ctx, pdf_annot *annot, int q) +{ + q = (q < 0 || q > 2) ? 0 : q; + + begin_annot_op(ctx, annot, "Set quadding"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(Q), quadding_subtypes); + pdf_dict_put_int(ctx, annot->obj, PDF_NAME(Q), q); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +float pdf_annot_opacity(fz_context *ctx, pdf_annot *annot) +{ + float ret; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + ret = pdf_dict_get_real_default(ctx, annot->obj, PDF_NAME(CA), 1); + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +void pdf_set_annot_opacity(fz_context *ctx, pdf_annot *annot, float opacity) +{ + begin_annot_op(ctx, annot, "Set opacity"); + + fz_try(ctx) + { + if (opacity != 1) + pdf_dict_put_real(ctx, annot->obj, PDF_NAME(CA), opacity); + else + pdf_dict_del(ctx, annot->obj, PDF_NAME(CA)); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +static void pdf_annot_color_imp(fz_context *ctx, pdf_obj *arr, int *n, float color[4]) +{ + switch (pdf_array_len(ctx, arr)) + { + case 0: + if (n) + *n = 0; + break; + case 1: + case 2: + if (n) + *n = 1; + if (color) + color[0] = pdf_array_get_real(ctx, arr, 0); + break; + case 3: + if (n) + *n = 3; + if (color) + { + color[0] = pdf_array_get_real(ctx, arr, 0); + color[1] = pdf_array_get_real(ctx, arr, 1); + color[2] = pdf_array_get_real(ctx, arr, 2); + } + break; + case 4: + default: + if (n) + *n = 4; + if (color) + { + color[0] = pdf_array_get_real(ctx, arr, 0); + color[1] = pdf_array_get_real(ctx, arr, 1); + color[2] = pdf_array_get_real(ctx, arr, 2); + color[3] = pdf_array_get_real(ctx, arr, 3); + } + break; + } +} + +static int pdf_annot_color_rgb(fz_context *ctx, pdf_obj *arr, float rgb[3]) +{ + float color[4]; + int n; + pdf_annot_color_imp(ctx, arr, &n, color); + if (n == 0) + { + return 0; + } + else if (n == 1) + { + rgb[0] = rgb[1] = rgb[2] = color[0]; + } + else if (n == 3) + { + rgb[0] = color[0]; + rgb[1] = color[1]; + rgb[2] = color[2]; + } + else if (n == 4) + { + rgb[0] = 1 - fz_min(1, color[0] + color[3]); + rgb[1] = 1 - fz_min(1, color[1] + color[3]); + rgb[2] = 1 - fz_min(1, color[2] + color[3]); + } + return 1; +} + +static void pdf_set_annot_color_imp(fz_context *ctx, pdf_annot *annot, pdf_obj *key, int n, const float *color, pdf_obj **allowed) +{ + pdf_obj *arr; + + if (allowed) + check_allowed_subtypes(ctx, annot, key, allowed); + if (n != 0 && n != 1 && n != 3 && n != 4) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "color must be 0, 1, 3 or 4 components"); + if (!color) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "no color given"); + + arr = pdf_dict_put_array(ctx, annot->obj, key, n); + fz_try(ctx) + { + switch (n) + { + case 1: + pdf_array_push_real(ctx, arr, color[0]); + break; + case 3: + pdf_array_push_real(ctx, arr, color[0]); + pdf_array_push_real(ctx, arr, color[1]); + pdf_array_push_real(ctx, arr, color[2]); + break; + case 4: + pdf_array_push_real(ctx, arr, color[0]); + pdf_array_push_real(ctx, arr, color[1]); + pdf_array_push_real(ctx, arr, color[2]); + pdf_array_push_real(ctx, arr, color[3]); + break; + } + } + fz_catch(ctx) + fz_rethrow(ctx); + + pdf_dirty_annot(ctx, annot); +} + +static void +do_pdf_annot_color(fz_context *ctx, pdf_annot *annot, int *n, float color[4], pdf_obj *name) +{ + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + pdf_obj *c = pdf_dict_get(ctx, annot->obj, name); + pdf_annot_color_imp(ctx, c, n, color); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); +} + +void +pdf_annot_color(fz_context *ctx, pdf_annot *annot, int *n, float color[4]) +{ + do_pdf_annot_color(ctx, annot, n, color, PDF_NAME(C)); +} + +void +pdf_annot_MK_BG(fz_context *ctx, pdf_annot *annot, int *n, float color[4]) +{ + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + pdf_obj *mk_bg = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BG)); + pdf_annot_color_imp(ctx, mk_bg, n, color); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); +} + +int +pdf_annot_MK_BG_rgb(fz_context *ctx, pdf_annot *annot, float rgb[3]) +{ + int ret; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + pdf_obj *mk_bg = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BG)); + ret = pdf_annot_color_rgb(ctx, mk_bg, rgb); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +void +pdf_annot_MK_BC(fz_context *ctx, pdf_annot *annot, int *n, float color[4]) +{ + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + pdf_obj *mk_bc = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BC)); + pdf_annot_color_imp(ctx, mk_bc, n, color); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); +} + +int +pdf_annot_MK_BC_rgb(fz_context *ctx, pdf_annot *annot, float rgb[3]) +{ + int ret; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + pdf_obj *mk_bc = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BC)); + ret = pdf_annot_color_rgb(ctx, mk_bc, rgb); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +void +pdf_set_annot_color(fz_context *ctx, pdf_annot *annot, int n, const float *color) +{ + begin_annot_op(ctx, annot, "Set color"); + + fz_try(ctx) + { + pdf_set_annot_color_imp(ctx, annot, PDF_NAME(C), n, color, NULL); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } +} + +static pdf_obj *interior_color_subtypes[] = { + PDF_NAME(Circle), + PDF_NAME(Line), + PDF_NAME(PolyLine), + PDF_NAME(Polygon), + PDF_NAME(Square), + NULL, +}; + +int +pdf_annot_has_interior_color(fz_context *ctx, pdf_annot *annot) +{ + return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(IC), interior_color_subtypes); +} + +void +pdf_annot_interior_color(fz_context *ctx, pdf_annot *annot, int *n, float color[4]) +{ + check_allowed_subtypes(ctx, annot, PDF_NAME(IC), interior_color_subtypes); + do_pdf_annot_color(ctx, annot, n, color, PDF_NAME(IC)); +} + +void +pdf_set_annot_interior_color(fz_context *ctx, pdf_annot *annot, int n, const float *color) +{ + begin_annot_op(ctx, annot, "Set interior color"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(IC), interior_color_subtypes); + pdf_set_annot_color_imp(ctx, annot, PDF_NAME(IC), n, color, interior_color_subtypes); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } +} + +static pdf_obj *line_subtypes[] = { + PDF_NAME(Line), + NULL, +}; + +int +pdf_annot_has_line(fz_context *ctx, pdf_annot *annot) +{ + return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(L), line_subtypes); +} + +void +pdf_annot_line(fz_context *ctx, pdf_annot *annot, fz_point *a, fz_point *b) +{ + fz_matrix page_ctm; + pdf_obj *line; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(L), line_subtypes); + + pdf_page_transform(ctx, annot->page, NULL, &page_ctm); + + line = pdf_dict_get(ctx, annot->obj, PDF_NAME(L)); + a->x = pdf_array_get_real(ctx, line, 0); + a->y = pdf_array_get_real(ctx, line, 1); + b->x = pdf_array_get_real(ctx, line, 2); + b->y = pdf_array_get_real(ctx, line, 3); + *a = fz_transform_point(*a, page_ctm); + *b = fz_transform_point(*b, page_ctm); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); +} + +void +pdf_set_annot_line(fz_context *ctx, pdf_annot *annot, fz_point a, fz_point b) +{ + fz_matrix page_ctm, inv_page_ctm; + pdf_obj *line; + + begin_annot_op(ctx, annot, "Set line"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(L), line_subtypes); + + pdf_page_transform(ctx, annot->page, NULL, &page_ctm); + inv_page_ctm = fz_invert_matrix(page_ctm); + + a = fz_transform_point(a, inv_page_ctm); + b = fz_transform_point(b, inv_page_ctm); + + line = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(L), 4); + pdf_array_push_real(ctx, line, a.x); + pdf_array_push_real(ctx, line, a.y); + pdf_array_push_real(ctx, line, b.x); + pdf_array_push_real(ctx, line, b.y); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +float +pdf_annot_line_leader(fz_context *ctx, pdf_annot *annot) +{ + float value; + pdf_annot_push_local_xref(ctx, annot); + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(LL), line_subtypes); + value = pdf_dict_get_real(ctx, annot->obj, PDF_NAME(LL)); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + return value; +} + +float +pdf_annot_line_leader_extension(fz_context *ctx, pdf_annot *annot) +{ + float value; + pdf_annot_push_local_xref(ctx, annot); + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(LLE), line_subtypes); + value = pdf_dict_get_real(ctx, annot->obj, PDF_NAME(LLE)); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + return value; +} + +float +pdf_annot_line_leader_offset(fz_context *ctx, pdf_annot *annot) +{ + float value; + pdf_annot_push_local_xref(ctx, annot); + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(LLO), line_subtypes); + value = pdf_dict_get_real(ctx, annot->obj, PDF_NAME(LLO)); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + return value; +} + +void +pdf_set_annot_line_leader(fz_context *ctx, pdf_annot *annot, float value) +{ + begin_annot_op(ctx, annot, "Set line leader"); + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(LL), line_subtypes); + if (value) + pdf_dict_put_real(ctx, annot->obj, PDF_NAME(LL), value); + else + pdf_dict_del(ctx, annot->obj, PDF_NAME(LL)); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + pdf_dirty_annot(ctx, annot); +} + +void +pdf_set_annot_line_leader_extension(fz_context *ctx, pdf_annot *annot, float value) +{ + begin_annot_op(ctx, annot, "Set line leader_extension"); + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(LLE), line_subtypes); + if (value) + pdf_dict_put_real(ctx, annot->obj, PDF_NAME(LLE), value); + else + pdf_dict_del(ctx, annot->obj, PDF_NAME(LLE)); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + pdf_dirty_annot(ctx, annot); +} + +void +pdf_set_annot_line_leader_offset(fz_context *ctx, pdf_annot *annot, float value) +{ + begin_annot_op(ctx, annot, "Set line leader offset"); + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(LLO), line_subtypes); + if (value) + pdf_dict_put_real(ctx, annot->obj, PDF_NAME(LLO), value); + else + pdf_dict_del(ctx, annot->obj, PDF_NAME(LLO)); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + pdf_dirty_annot(ctx, annot); +} + +int +pdf_annot_line_caption(fz_context *ctx, pdf_annot *annot) +{ + int cap = 0; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(Cap), line_subtypes); + + cap = pdf_dict_get_bool(ctx, annot->obj, PDF_NAME(Cap)); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return cap; +} + +void +pdf_set_annot_line_caption(fz_context *ctx, pdf_annot *annot, int cap) +{ + begin_annot_op(ctx, annot, "Set line caption"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(Cap), line_subtypes); + + pdf_dict_put_bool(ctx, annot->obj, PDF_NAME(Cap), cap); + + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +fz_point +pdf_annot_line_caption_offset(fz_context *ctx, pdf_annot *annot) +{ + fz_point offset = fz_make_point(0, 0); + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(CO), line_subtypes); + + offset = pdf_dict_get_point(ctx, annot->obj, PDF_NAME(CO)); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return offset; +} + +void +pdf_set_annot_line_caption_offset(fz_context *ctx, pdf_annot *annot, fz_point offset) +{ + begin_annot_op(ctx, annot, "Set line caption"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(CO), line_subtypes); + + if (offset.x == 0 && offset.y == 0) + pdf_dict_del(ctx, annot->obj, PDF_NAME(CO)); + else + pdf_dict_put_point(ctx, annot->obj, PDF_NAME(CO), offset); + + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +static pdf_obj *vertices_subtypes[] = { + PDF_NAME(PolyLine), + PDF_NAME(Polygon), + NULL, +}; + +int +pdf_annot_has_vertices(fz_context *ctx, pdf_annot *annot) +{ + return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(Vertices), vertices_subtypes); +} + +int +pdf_annot_vertex_count(fz_context *ctx, pdf_annot *annot) +{ + pdf_obj *vertices; + int ret; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes); + vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices)); + ret = pdf_array_len(ctx, vertices) / 2; + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +fz_point +pdf_annot_vertex(fz_context *ctx, pdf_annot *annot, int i) +{ + pdf_obj *vertices; + fz_matrix page_ctm; + fz_point point; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes); + + vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices)); + + pdf_page_transform(ctx, annot->page, NULL, &page_ctm); + + point.x = pdf_array_get_real(ctx, vertices, i * 2); + point.y = pdf_array_get_real(ctx, vertices, i * 2 + 1); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return fz_transform_point(point, page_ctm); +} + +void +pdf_set_annot_vertices(fz_context *ctx, pdf_annot *annot, int n, const fz_point *v) +{ + fz_matrix page_ctm, inv_page_ctm; + pdf_obj *vertices; + fz_point point; + int i; + + begin_annot_op(ctx, annot, "Set points"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes); + if (n <= 0 || !v) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "invalid number of vertices"); + + pdf_page_transform(ctx, annot->page, NULL, &page_ctm); + inv_page_ctm = fz_invert_matrix(page_ctm); + + vertices = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(Vertices), n * 2); + for (i = 0; i < n; ++i) + { + point = fz_transform_point(v[i], inv_page_ctm); + pdf_array_push_real(ctx, vertices, point.x); + pdf_array_push_real(ctx, vertices, point.y); + } + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +void pdf_clear_annot_vertices(fz_context *ctx, pdf_annot *annot) +{ + begin_annot_op(ctx, annot, "Clear vertices"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes); + pdf_dict_del(ctx, annot->obj, PDF_NAME(Vertices)); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +void pdf_add_annot_vertex(fz_context *ctx, pdf_annot *annot, fz_point p) +{ + fz_matrix page_ctm, inv_page_ctm; + pdf_obj *vertices; + + begin_annot_op(ctx, annot, "Add point"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes); + + pdf_page_transform(ctx, annot->page, NULL, &page_ctm); + inv_page_ctm = fz_invert_matrix(page_ctm); + + vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices)); + if (!pdf_is_array(ctx, vertices)) + vertices = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(Vertices), 32); + + p = fz_transform_point(p, inv_page_ctm); + pdf_array_push_real(ctx, vertices, p.x); + pdf_array_push_real(ctx, vertices, p.y); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +void pdf_set_annot_vertex(fz_context *ctx, pdf_annot *annot, int i, fz_point p) +{ + fz_matrix page_ctm, inv_page_ctm; + pdf_obj *vertices; + + begin_annot_op(ctx, annot, "Set point"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes); + + pdf_page_transform(ctx, annot->page, NULL, &page_ctm); + inv_page_ctm = fz_invert_matrix(page_ctm); + + p = fz_transform_point(p, inv_page_ctm); + + vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices)); + pdf_array_put_real(ctx, vertices, i * 2 + 0, p.x); + pdf_array_put_real(ctx, vertices, i * 2 + 1, p.x); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } +} + +static pdf_obj *quad_point_subtypes[] = { + PDF_NAME(Highlight), + PDF_NAME(Link), + PDF_NAME(Squiggly), + PDF_NAME(StrikeOut), + PDF_NAME(Underline), + PDF_NAME(Redact), + NULL, +}; + +int +pdf_annot_has_quad_points(fz_context *ctx, pdf_annot *annot) +{ + return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes); +} + +int +pdf_annot_quad_point_count(fz_context *ctx, pdf_annot *annot) +{ + pdf_obj *quad_points; + int ret; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes); + quad_points = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints)); + ret = pdf_array_len(ctx, quad_points) / 8; + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +fz_quad +pdf_annot_quad_point(fz_context *ctx, pdf_annot *annot, int idx) +{ + pdf_obj *quad_points; + fz_matrix page_ctm; + float v[8]; + int i; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes); + quad_points = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints)); + pdf_page_transform(ctx, annot->page, NULL, &page_ctm); + + for (i = 0; i < 8; i += 2) + { + fz_point point; + point.x = pdf_array_get_real(ctx, quad_points, idx * 8 + i + 0); + point.y = pdf_array_get_real(ctx, quad_points, idx * 8 + i + 1); + point = fz_transform_point(point, page_ctm); + v[i+0] = point.x; + v[i+1] = point.y; + } + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return fz_make_quad(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]); +} + +void +pdf_set_annot_quad_points(fz_context *ctx, pdf_annot *annot, int n, const fz_quad *q) +{ + fz_matrix page_ctm, inv_page_ctm; + pdf_obj *quad_points; + fz_quad quad; + int i; + + begin_annot_op(ctx, annot, "Set quad points"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes); + if (n <= 0 || !q) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "invalid number of quadrilaterals"); + + pdf_page_transform(ctx, annot->page, NULL, &page_ctm); + inv_page_ctm = fz_invert_matrix(page_ctm); + + quad_points = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(QuadPoints), n); + for (i = 0; i < n; ++i) + { + quad = fz_transform_quad(q[i], inv_page_ctm); + pdf_array_push_real(ctx, quad_points, quad.ul.x); + pdf_array_push_real(ctx, quad_points, quad.ul.y); + pdf_array_push_real(ctx, quad_points, quad.ur.x); + pdf_array_push_real(ctx, quad_points, quad.ur.y); + pdf_array_push_real(ctx, quad_points, quad.ll.x); + pdf_array_push_real(ctx, quad_points, quad.ll.y); + pdf_array_push_real(ctx, quad_points, quad.lr.x); + pdf_array_push_real(ctx, quad_points, quad.lr.y); + } + + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +void +pdf_clear_annot_quad_points(fz_context *ctx, pdf_annot *annot) +{ + begin_annot_op(ctx, annot, "Clear quad points"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes); + pdf_dict_del(ctx, annot->obj, PDF_NAME(QuadPoints)); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +void +pdf_add_annot_quad_point(fz_context *ctx, pdf_annot *annot, fz_quad quad) +{ + fz_matrix page_ctm, inv_page_ctm; + pdf_obj *quad_points; + + begin_annot_op(ctx, annot, "Add quad point"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes); + + pdf_page_transform(ctx, annot->page, NULL, &page_ctm); + inv_page_ctm = fz_invert_matrix(page_ctm); + + quad_points = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints)); + if (!pdf_is_array(ctx, quad_points)) + quad_points = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(QuadPoints), 8); + + /* Contrary to the specification, the points within a QuadPoint are NOT ordered + * in a counterclockwise fashion. Experiments with Adobe's implementation + * indicates a cross-wise ordering is intended: ul, ur, ll, lr. + */ + quad = fz_transform_quad(quad, inv_page_ctm); + pdf_array_push_real(ctx, quad_points, quad.ul.x); + pdf_array_push_real(ctx, quad_points, quad.ul.y); + pdf_array_push_real(ctx, quad_points, quad.ur.x); + pdf_array_push_real(ctx, quad_points, quad.ur.y); + pdf_array_push_real(ctx, quad_points, quad.ll.x); + pdf_array_push_real(ctx, quad_points, quad.ll.y); + pdf_array_push_real(ctx, quad_points, quad.lr.x); + pdf_array_push_real(ctx, quad_points, quad.lr.y); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +static pdf_obj *ink_list_subtypes[] = { + PDF_NAME(Ink), + NULL, +}; + +int +pdf_annot_has_ink_list(fz_context *ctx, pdf_annot *annot) +{ + return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(InkList), ink_list_subtypes); +} + +int +pdf_annot_ink_list_count(fz_context *ctx, pdf_annot *annot) +{ + int ret; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + pdf_obj *ink_list; + check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes); + ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList)); + ret = pdf_array_len(ctx, ink_list); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +int +pdf_annot_ink_list_stroke_count(fz_context *ctx, pdf_annot *annot, int i) +{ + pdf_obj *ink_list; + pdf_obj *stroke; + int ret; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes); + ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList)); + stroke = pdf_array_get(ctx, ink_list, i); + ret = pdf_array_len(ctx, stroke) / 2; + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +fz_point +pdf_annot_ink_list_stroke_vertex(fz_context *ctx, pdf_annot *annot, int i, int k) +{ + pdf_obj *ink_list; + pdf_obj *stroke; + fz_matrix page_ctm; + fz_point point; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes); + + ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList)); + stroke = pdf_array_get(ctx, ink_list, i); + + pdf_page_transform(ctx, annot->page, NULL, &page_ctm); + + point.x = pdf_array_get_real(ctx, stroke, k * 2 + 0); + point.y = pdf_array_get_real(ctx, stroke, k * 2 + 1); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return fz_transform_point(point, page_ctm); +} + +/* FIXME: try/catch required for memory exhaustion */ +void +pdf_set_annot_ink_list(fz_context *ctx, pdf_annot *annot, int n, const int *count, const fz_point *v) +{ + fz_matrix page_ctm, inv_page_ctm; + pdf_obj *ink_list = NULL, *stroke; + fz_point point; + int i, k; + + fz_var(ink_list); + + begin_annot_op(ctx, annot, "Set ink list"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes); + + pdf_page_transform(ctx, annot->page, NULL, &page_ctm); + inv_page_ctm = fz_invert_matrix(page_ctm); + + ink_list = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(InkList), n); + for (i = 0; i < n; ++i) + { + stroke = pdf_array_push_array(ctx, ink_list, count[i] * 2); + + /* Although we have dropped our reference to stroke, + * it's still valid because we ink_list holds one, and + * we hold a reference to that. */ + for (k = 0; k < count[i]; ++k) + { + point = fz_transform_point(*v++, inv_page_ctm); + pdf_array_push_real(ctx, stroke, point.x); + pdf_array_push_real(ctx, stroke, point.y); + } + } + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +void +pdf_clear_annot_ink_list(fz_context *ctx, pdf_annot *annot) +{ + begin_annot_op(ctx, annot, "Clear ink list"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes); + pdf_dict_del(ctx, annot->obj, PDF_NAME(InkList)); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +void pdf_add_annot_ink_list_stroke(fz_context *ctx, pdf_annot *annot) +{ + pdf_obj *ink_list; + + begin_annot_op(ctx, annot, "Add ink list stroke"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes); + ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList)); + if (!pdf_is_array(ctx, ink_list)) + ink_list = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(InkList), 10); + + pdf_array_push_array(ctx, ink_list, 16); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +void pdf_add_annot_ink_list_stroke_vertex(fz_context *ctx, pdf_annot *annot, fz_point p) +{ + fz_matrix page_ctm, inv_page_ctm; + pdf_obj *ink_list, *stroke; + + begin_annot_op(ctx, annot, "Add ink list stroke point"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes); + pdf_page_transform(ctx, annot->page, NULL, &page_ctm); + inv_page_ctm = fz_invert_matrix(page_ctm); + + ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList)); + if (!pdf_is_array(ctx, ink_list)) + ink_list = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(InkList), 10); + stroke = pdf_array_get(ctx, ink_list, pdf_array_len(ctx, ink_list)-1); + if (!pdf_is_array(ctx, stroke)) + { + int len = pdf_array_len(ctx, ink_list); + stroke = pdf_new_array(ctx, pdf_get_bound_document(ctx, ink_list), 16); + pdf_array_put_drop(ctx, ink_list, len ? len-1 : 0, stroke); + } + + p = fz_transform_point(p, inv_page_ctm); + pdf_array_push_real(ctx, stroke, p.x); + pdf_array_push_real(ctx, stroke, p.y); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +void +pdf_add_annot_ink_list(fz_context *ctx, pdf_annot *annot, int n, fz_point p[]) +{ + fz_matrix page_ctm, inv_page_ctm; + pdf_obj *ink_list, *stroke; + int i; + + begin_annot_op(ctx, annot, "Add ink list"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes); + + pdf_page_transform(ctx, annot->page, NULL, &page_ctm); + inv_page_ctm = fz_invert_matrix(page_ctm); + + ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList)); + if (!pdf_is_array(ctx, ink_list)) + ink_list = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(InkList), 10); + + stroke = pdf_array_push_array(ctx, ink_list, n * 2); + for (i = 0; i < n; ++i) + { + fz_point tp = fz_transform_point(p[i], inv_page_ctm); + pdf_array_push_real(ctx, stroke, tp.x); + pdf_array_push_real(ctx, stroke, tp.y); + } + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +/* + Get annotation's modification date in seconds since the epoch. +*/ +int64_t +pdf_annot_modification_date(fz_context *ctx, pdf_annot *annot) +{ + int64_t ret; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + ret = pdf_dict_get_date(ctx, annot->obj, PDF_NAME(M)); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +/* + Get annotation's creation date in seconds since the epoch. +*/ +int64_t +pdf_annot_creation_date(fz_context *ctx, pdf_annot *annot) +{ + int64_t ret; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + ret = pdf_dict_get_date(ctx, annot->obj, PDF_NAME(CreationDate)); + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +/* + Set annotation's modification date in seconds since the epoch. +*/ +void +pdf_set_annot_modification_date(fz_context *ctx, pdf_annot *annot, int64_t secs) +{ + begin_annot_op(ctx, annot, "Set modification date"); + + fz_try(ctx) + { + pdf_dict_put_date(ctx, annot->obj, PDF_NAME(M), secs); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +/* + Set annotation's creation date in seconds since the epoch. +*/ +void +pdf_set_annot_creation_date(fz_context *ctx, pdf_annot *annot, int64_t secs) +{ + begin_annot_op(ctx, annot, "Set creation date"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(CreationDate), markup_subtypes); + pdf_dict_put_date(ctx, annot->obj, PDF_NAME(CreationDate), secs); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +int +pdf_annot_has_author(fz_context *ctx, pdf_annot *annot) +{ + return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(T), markup_subtypes); +} + +const char * +pdf_annot_author(fz_context *ctx, pdf_annot *annot) +{ + const char *ret; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(T), markup_subtypes); + ret = pdf_dict_get_text_string(ctx, annot->obj, PDF_NAME(T)); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +void +pdf_set_annot_author(fz_context *ctx, pdf_annot *annot, const char *author) +{ + begin_annot_op(ctx, annot, "Set author"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(T), markup_subtypes); + pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(T), author); + pdf_dirty_annot(ctx, annot); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } +} + +static pdf_obj *intent_subtypes[] = { + PDF_NAME(FreeText), + PDF_NAME(Line), + PDF_NAME(Polygon), + PDF_NAME(PolyLine), + PDF_NAME(Stamp), + NULL, +}; + +enum pdf_intent pdf_intent_from_name(fz_context *ctx, pdf_obj *it) +{ + if ( + it == PDF_NULL || + it == PDF_NAME(FreeText) || + it == PDF_NAME(Line) || + it == PDF_NAME(PolyLine) || + it == PDF_NAME(Polygon) || + it == PDF_NAME(Stamp) + ) + return PDF_ANNOT_IT_DEFAULT; + if (it == PDF_NAME(FreeTextCallout)) + return PDF_ANNOT_IT_FREETEXT_CALLOUT; + if (it == PDF_NAME(FreeTextTypeWriter)) + return PDF_ANNOT_IT_FREETEXT_TYPEWRITER; + if (it == PDF_NAME(LineArrow)) + return PDF_ANNOT_IT_LINE_ARROW; + if (it == PDF_NAME(LineDimension)) + return PDF_ANNOT_IT_LINE_DIMENSION; + if (it == PDF_NAME(PolyLineDimension)) + return PDF_ANNOT_IT_POLYLINE_DIMENSION; + if (it == PDF_NAME(PolygonCloud)) + return PDF_ANNOT_IT_POLYGON_CLOUD; + if (it == PDF_NAME(PolygonDimension)) + return PDF_ANNOT_IT_POLYGON_DIMENSION; + if (it == PDF_NAME(StampImage)) + return PDF_ANNOT_IT_STAMP_IMAGE; + if (it == PDF_NAME(StampSnapshot)) + return PDF_ANNOT_IT_STAMP_SNAPSHOT; + return PDF_ANNOT_IT_UNKNOWN; +} + +enum pdf_intent pdf_intent_from_string(fz_context *ctx, const char *it) +{ + if ( + it == NULL || + !strcmp(it, "FreeText") || + !strcmp(it, "Line") || + !strcmp(it, "PolyLine") || + !strcmp(it, "Polygon") || + !strcmp(it, "Stamp") + ) + return PDF_ANNOT_IT_DEFAULT; + if (!strcmp(it, "FreeTextCallout")) + return PDF_ANNOT_IT_FREETEXT_CALLOUT; + if (!strcmp(it, "FreeTextTypeWriter")) + return PDF_ANNOT_IT_FREETEXT_TYPEWRITER; + if (!strcmp(it, "LineArrow")) + return PDF_ANNOT_IT_LINE_ARROW; + if (!strcmp(it, "LineDimension")) + return PDF_ANNOT_IT_LINE_DIMENSION; + if (!strcmp(it, "PolyLineDimension")) + return PDF_ANNOT_IT_POLYLINE_DIMENSION; + if (!strcmp(it, "PolygonCloud")) + return PDF_ANNOT_IT_POLYGON_CLOUD; + if (!strcmp(it, "PolygonDimension")) + return PDF_ANNOT_IT_POLYGON_DIMENSION; + if (!strcmp(it, "StampImage")) + return PDF_ANNOT_IT_STAMP_IMAGE; + if (!strcmp(it, "StampSnapshot")) + return PDF_ANNOT_IT_STAMP_SNAPSHOT; + return PDF_ANNOT_IT_UNKNOWN; +} + +pdf_obj *pdf_name_from_intent(fz_context *ctx, enum pdf_intent it) +{ + switch (it) + { + default: + case PDF_ANNOT_IT_DEFAULT: return PDF_NULL; + case PDF_ANNOT_IT_FREETEXT_CALLOUT: return PDF_NAME(FreeTextCallout); + case PDF_ANNOT_IT_FREETEXT_TYPEWRITER: return PDF_NAME(FreeTextTypeWriter); + case PDF_ANNOT_IT_LINE_ARROW: return PDF_NAME(LineArrow); + case PDF_ANNOT_IT_LINE_DIMENSION: return PDF_NAME(LineDimension); + case PDF_ANNOT_IT_POLYLINE_DIMENSION: return PDF_NAME(PolyLineDimension); + case PDF_ANNOT_IT_POLYGON_CLOUD: return PDF_NAME(PolygonCloud); + case PDF_ANNOT_IT_POLYGON_DIMENSION: return PDF_NAME(PolygonDimension); + } +} + +const char *pdf_string_from_intent(fz_context *ctx, enum pdf_intent it) +{ + switch (it) + { + default: + case PDF_ANNOT_IT_DEFAULT: return NULL; + case PDF_ANNOT_IT_FREETEXT_CALLOUT: return "FreeTextCallout"; + case PDF_ANNOT_IT_FREETEXT_TYPEWRITER: return "FreeTextTypeWriter"; + case PDF_ANNOT_IT_LINE_ARROW: return "LineArrow"; + case PDF_ANNOT_IT_LINE_DIMENSION: return "LineDimension"; + case PDF_ANNOT_IT_POLYLINE_DIMENSION: return "PolyLineDimension"; + case PDF_ANNOT_IT_POLYGON_CLOUD: return "PolygonCloud"; + case PDF_ANNOT_IT_POLYGON_DIMENSION: return "PolygonDimension"; + } +} + +int +pdf_annot_has_intent(fz_context *ctx, pdf_annot *annot) +{ + /* Only a subset of intents are defined in the spec, so we limit this API to the defined ones. + * FreeText: Callout, TypeWriter + * Line: Arrow, Dimension + * Polygon: Cloud, Dimension + * PolyLine: Dimension + */ + return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(IT), intent_subtypes); +} + +enum pdf_intent +pdf_annot_intent(fz_context *ctx, pdf_annot *annot) +{ + enum pdf_intent ret; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(IT), intent_subtypes); + ret = pdf_intent_from_name(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(IT))); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +void +pdf_set_annot_intent(fz_context *ctx, pdf_annot *annot, enum pdf_intent it) +{ + begin_annot_op(ctx, annot, "Set intent"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(IT), intent_subtypes); + pdf_dict_put(ctx, annot->obj, PDF_NAME(IT), pdf_name_from_intent(ctx, it)); + pdf_dirty_annot(ctx, annot); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } +} + +static pdf_obj *callout_subtypes[] = { + PDF_NAME(FreeText), + NULL, +}; + +int pdf_annot_has_callout(fz_context *ctx, pdf_annot *annot) +{ + return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(CL), callout_subtypes); +} + +enum pdf_line_ending pdf_annot_callout_style(fz_context *ctx, pdf_annot *annot) +{ + enum pdf_line_ending style = PDF_ANNOT_LE_NONE; + pdf_obj *obj; + pdf_annot_push_local_xref(ctx, annot); + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(CL), callout_subtypes); + obj = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE)); + style = pdf_line_ending_from_name(ctx, obj); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + return style; +} + +void pdf_set_annot_callout_style(fz_context *ctx, pdf_annot *annot, enum pdf_line_ending style) +{ + begin_annot_op(ctx, annot, "Set callout style"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(CL), callout_subtypes); + pdf_dict_put(ctx, annot->obj, PDF_NAME(LE), pdf_name_from_line_ending(ctx, style)); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +void pdf_annot_callout_line(fz_context *ctx, pdf_annot *annot, fz_point callout[3], int *np) +{ + pdf_annot_push_local_xref(ctx, annot); + fz_try(ctx) + { + fz_matrix page_ctm; + pdf_obj *obj; + int n; + + check_allowed_subtypes(ctx, annot, PDF_NAME(CL), callout_subtypes); + + pdf_page_transform(ctx, annot->page, NULL, &page_ctm); + + obj = pdf_dict_get(ctx, annot->obj, PDF_NAME(CL)); + n = pdf_array_len(ctx, obj); + if (n == 4 || n == 6) + { + callout[0] = fz_transform_point_xy(pdf_array_get_real(ctx, obj, 0), pdf_array_get_real(ctx, obj, 1), page_ctm); + callout[1] = fz_transform_point_xy(pdf_array_get_real(ctx, obj, 2), pdf_array_get_real(ctx, obj, 3), page_ctm); + if (n == 4) + { + *np = 2; + callout[2] = fz_make_point(0, 0); + } + else + { + *np = 3; + callout[2] = fz_transform_point_xy(pdf_array_get_real(ctx, obj, 4), pdf_array_get_real(ctx, obj, 5), page_ctm); + } + } + else + { + callout[0] = fz_make_point(0, 0); + callout[1] = fz_make_point(0, 0); + callout[2] = fz_make_point(0, 0); + *np = 0; + } + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); +} + +void pdf_set_annot_callout_line(fz_context *ctx, pdf_annot *annot, fz_point callout[3], int n) +{ + begin_annot_op(ctx, annot, "Set callout"); + + fz_try(ctx) + { + fz_matrix page_ctm; + fz_point p; + int i; + pdf_obj *obj; + + check_allowed_subtypes(ctx, annot, PDF_NAME(CL), callout_subtypes); + + if (n == 2 || n == 3) + { + pdf_page_transform(ctx, annot->page, NULL, &page_ctm); + obj = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(CL), n * 2); + for (i = 0; i < n; ++i) + { + p = fz_transform_point(callout[i], page_ctm); + pdf_array_push_real(ctx, obj, p.x); + pdf_array_push_real(ctx, obj, p.y); + } + } + else + { + pdf_dict_del(ctx, annot->obj, PDF_NAME(CL)); + } + + pdf_dirty_annot(ctx, annot); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } +} + +fz_point pdf_annot_callout_point(fz_context *ctx, pdf_annot *annot) +{ + fz_point line[3]; + int n; + + pdf_annot_callout_line(ctx, annot, line, &n); + if (n > 0) + return line[0]; + return fz_make_point(0, 0); +} + +void pdf_set_annot_callout_point(fz_context *ctx, pdf_annot *annot, fz_point p) +{ + fz_rect rect; + fz_point a, b, line[3]; + float m; + + rect = pdf_annot_rect(ctx, annot); + + // Make line from center of text box to designated end point. + a = fz_make_point((rect.x0 + rect.x1) / 2, (rect.y0 + rect.y1) / 2); + b = p; + + // No CalloutLine if end point is within the text box itself. + if (fz_is_point_inside_rect(p, rect)) + { + line[0] = p; + line[1] = a; + pdf_set_annot_callout_line(ctx, annot, line, 2); + return; + } + + // Simplified Cohen-Sutherland algorithm to find intersection of line and text box. + m = (b.y - a.y) / (b.x - a.x); + for (;;) + { + if (b.y < rect.y0) { + b.x = a.x + (rect.y0 - a.y) / m; + b.y = rect.y0; + } + else + if (b.y > rect.y1) { + b.x = a.x + (rect.y1 - a.y) / m; + b.y = rect.y1; + } + else + if (b.x < rect.x0) { + b.y = a.y + (rect.x0 - a.x) * m; + b.x = rect.x0; + } + else + if (b.x > rect.x1) { + b.y = a.y + (rect.x1 - a.x) * m; + b.x = rect.x1; + } + else + break; + } + + // Draw line from intersection to end point. + line[0] = p; + line[1] = b; + pdf_set_annot_callout_line(ctx, annot, line, 2); +} + +void +pdf_parse_default_appearance_unmapped(fz_context *ctx, const char *da, char *font_name, int font_name_size, float *size, int *n, float color[4]) +{ + char buf[100], *p = buf, *tok, *end; + float stack[4] = { 0, 0, 0, 0 }; + int top = 0; + + fz_strlcpy(font_name, "Helv", font_name_size); + *size = 12; + *n = 0; + color[0] = color[1] = color[2] = color[3] = 0; + + fz_strlcpy(buf, da, sizeof buf); + while ((tok = fz_strsep(&p, " \n\r\t")) != NULL) + { + if (tok[0] == 0) + ; + else if (tok[0] == '/') + { + fz_strlcpy(font_name, tok+1, font_name_size); + } + else if (!strcmp(tok, "Tf")) + { + *size = stack[0]; + top = 0; + } + else if (!strcmp(tok, "g")) + { + *n = 1; + color[0] = stack[0]; + top = 0; + } + else if (!strcmp(tok, "rg")) + { + *n = 3; + color[0] = stack[0]; + color[1] = stack[1]; + color[2] = stack[2]; + top=0; + } + else if (!strcmp(tok, "k")) + { + *n = 4; + color[0] = stack[0]; + color[1] = stack[1]; + color[2] = stack[2]; + color[3] = stack[3]; + top=0; + } + else + { + float v = fz_strtof(tok, &end); + if (top < 4) + stack[top] = v; + if (*end == 0) + ++top; + else + top = 0; + } + } +} + +void +pdf_parse_default_appearance(fz_context *ctx, const char *da, const char **font, float *size, int *n, float color[4]) +{ + char font_name[100]; + + pdf_parse_default_appearance_unmapped(ctx, da, font_name, sizeof font_name, size, n, color); + + if (!strcmp(font_name, "Cour")) *font = "Cour"; + else if (!strcmp(font_name, "Helv")) *font = "Helv"; + else if (!strcmp(font_name, "TiRo")) *font = "TiRo"; + else if (!strcmp(font_name, "Symb")) *font = "Symb"; + else if (!strcmp(font_name, "ZaDb")) *font = "ZaDb"; + else *font = "Helv"; +} + +void +pdf_print_default_appearance(fz_context *ctx, char *buf, int nbuf, const char *font, float size, int n, const float *color) +{ + if (n == 4) + fz_snprintf(buf, nbuf, "/%s %g Tf %g %g %g %g k", font, size, color[0], color[1], color[2], color[3]); + else if (n == 3) + fz_snprintf(buf, nbuf, "/%s %g Tf %g %g %g rg", font, size, color[0], color[1], color[2]); + else if (n == 1) + fz_snprintf(buf, nbuf, "/%s %g Tf %g g", font, size, color[0]); + else + fz_snprintf(buf, nbuf, "/%s %g Tf", font, size); +} + +void +pdf_annot_default_appearance_unmapped(fz_context *ctx, pdf_annot *annot, char *font_name, int font_name_len, float *size, int *n, float color[4]) +{ + pdf_obj *da; + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + da = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(DA)); + if (!da) + { + pdf_obj *trailer = pdf_trailer(ctx, annot->page->doc); + da = pdf_dict_getl(ctx, trailer, PDF_NAME(Root), PDF_NAME(AcroForm), PDF_NAME(DA), NULL); + } + pdf_parse_default_appearance_unmapped(ctx, pdf_to_str_buf(ctx, da), font_name, font_name_len, size, n, color); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); +} + +static pdf_obj *default_appearance_subtypes[] = { + PDF_NAME(FreeText), + PDF_NAME(Widget), + NULL, +}; + +int +pdf_annot_has_default_appearance(fz_context *ctx, pdf_annot *annot) +{ + return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(DA), default_appearance_subtypes); +} + +void +pdf_annot_default_appearance(fz_context *ctx, pdf_annot *annot, const char **font, float *size, int *n, float color[4]) +{ + pdf_obj *da; + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(DA), default_appearance_subtypes); + da = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(DA)); + if (!da) + { + pdf_obj *trailer = pdf_trailer(ctx, annot->page->doc); + da = pdf_dict_getl(ctx, trailer, PDF_NAME(Root), PDF_NAME(AcroForm), PDF_NAME(DA), NULL); + } + pdf_parse_default_appearance(ctx, pdf_to_str_buf(ctx, da), font, size, n, color); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); +} + +void +pdf_set_annot_default_appearance(fz_context *ctx, pdf_annot *annot, const char *font, float size, int n, const float *color) +{ + char buf[100]; + + begin_annot_op(ctx, annot, "Set default appearance"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(DA), default_appearance_subtypes); + pdf_print_default_appearance(ctx, buf, sizeof buf, font, size, n, color); + + pdf_dict_put_string(ctx, annot->obj, PDF_NAME(DA), buf, strlen(buf)); + + pdf_dict_del(ctx, annot->obj, PDF_NAME(DS)); /* not supported */ + pdf_dict_del(ctx, annot->obj, PDF_NAME(RC)); /* not supported */ + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +int pdf_annot_field_flags(fz_context *ctx, pdf_annot *annot) +{ + int ret; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + ret = pdf_field_flags(ctx, annot->obj); + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +const char *pdf_annot_field_value(fz_context *ctx, pdf_annot *widget) +{ + const char *ret; + + pdf_annot_push_local_xref(ctx, widget); + + fz_try(ctx) + ret = pdf_field_value(ctx, widget->obj); + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, widget); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +const char *pdf_annot_field_label(fz_context *ctx, pdf_annot *widget) +{ + const char *ret; + + pdf_annot_push_local_xref(ctx, widget); + + fz_try(ctx) + ret = pdf_field_label(ctx, widget->obj); + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, widget); + fz_catch(ctx) + fz_rethrow(ctx); + + return ret; +} + +int pdf_set_annot_field_value(fz_context *ctx, pdf_document *doc, pdf_annot *annot, const char *text, int ignore_trigger_events) +{ + int ret; + + begin_annot_op(ctx, annot, "Set field value"); + + fz_try(ctx) + { + ret = pdf_set_field_value(ctx, doc, annot->obj, text, ignore_trigger_events); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); + + return ret; +} + +void +pdf_set_annot_appearance(fz_context *ctx, pdf_annot *annot, const char *appearance, const char *state, fz_matrix ctm, fz_rect bbox, pdf_obj *res, fz_buffer *contents) +{ + pdf_obj *form = NULL; + pdf_obj *ap, *app; + pdf_obj *app_name = NULL; + + begin_annot_op(ctx, annot, "Set appearance stream"); + + if (!appearance) + appearance = "N"; + + fz_var(form); + fz_var(app_name); + + fz_try(ctx) + { + ap = pdf_dict_get(ctx, annot->obj, PDF_NAME(AP)); + if (!ap) + ap = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(AP), 1); + + if (!state) + form = pdf_keep_obj(ctx, pdf_dict_gets(ctx, ap, appearance)); + else + { + if (strcmp(appearance, "N") && strcmp(appearance, "R") && strcmp(appearance, "D")) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "Unknown annotation appearance"); + + app_name = pdf_new_name(ctx, appearance); + app = pdf_dict_get(ctx, ap, app_name); + if (!app) + app = pdf_dict_put_dict(ctx, ap, app_name, 2); + form = pdf_keep_obj(ctx, pdf_dict_gets(ctx, ap, appearance)); + } + /* Care required here. Some files have multiple annotations, which share + * appearance streams. As such, we must NOT reuse such appearance streams. + * On the other hand, we cannot afford to always recreate appearance + * streams, as this can lead to leakage of partial edits into the document. + * Any appearance we generate will be in the incremental section, and we + * will never generate shared appearances. As such, we can reuse an + * appearance object only if it is in the incremental section. */ + if (!pdf_obj_is_incremental(ctx, form)) + { + pdf_drop_obj(ctx, form); + form = NULL; + } + if (!pdf_is_dict(ctx, form)) + { + pdf_drop_obj(ctx, form); + form = NULL; + form = pdf_new_xobject(ctx, annot->page->doc, bbox, ctm, res, contents); + } + else + pdf_update_xobject(ctx, annot->page->doc, form, bbox, ctm, res, contents); + + if (!state) + pdf_dict_puts(ctx, ap, appearance, form); + else + pdf_dict_puts(ctx, app, state, form); + end_annot_op(ctx, annot); + } + fz_always(ctx) + { + pdf_drop_obj(ctx, form); + pdf_drop_obj(ctx, app_name); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_set_annot_resynthesised(ctx, annot); +} + +void +pdf_set_annot_appearance_from_display_list(fz_context *ctx, pdf_annot *annot, const char *appearance, const char *state, fz_matrix ctm, fz_display_list *list) +{ + pdf_document *doc; + fz_device *dev = NULL; + pdf_obj *res = NULL; + fz_buffer *contents = NULL; + + /* Convert fitz-space mediabox to pdf-space bbox */ + fz_rect mediabox = fz_bound_display_list(ctx, list); + fz_matrix transform = { 1, 0, 0, -1, -mediabox.x0, mediabox.y1 }; + fz_rect bbox = fz_transform_rect(mediabox, transform); + + fz_var(dev); + fz_var(contents); + fz_var(res); + + begin_annot_op(ctx, annot, "Set appearance stream"); + doc = annot->page->doc; + + fz_try(ctx) + { + res = pdf_new_dict(ctx, doc, 1); + contents = fz_new_buffer(ctx, 0); + dev = pdf_new_pdf_device(ctx, doc, transform, res, contents); + fz_run_display_list(ctx, list, dev, fz_identity, fz_infinite_rect, NULL); + fz_close_device(ctx, dev); + fz_drop_device(ctx, dev); + dev = NULL; + + pdf_set_annot_appearance(ctx, annot, appearance, state, ctm, bbox, res, contents); + end_annot_op(ctx, annot); + } + fz_always(ctx) + { + fz_drop_device(ctx, dev); + fz_drop_buffer(ctx, contents); + pdf_drop_obj(ctx, res); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } +} + +static pdf_obj *stamp_subtypes[] = { + PDF_NAME(Stamp), + NULL, +}; + +pdf_obj * +pdf_annot_stamp_image_obj(fz_context *ctx, pdf_annot *annot) +{ + pdf_obj *obj, *imgobj = NULL; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + obj = pdf_dict_getp(ctx, annot->obj, "AP/N/Resources/XObject"); + if (pdf_dict_len(ctx, obj) == 1) + { + obj = pdf_dict_get_val(ctx, obj, 0); + if (pdf_is_image_stream(ctx, obj)) + imgobj = obj; + } + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return imgobj; +} + +void pdf_set_annot_stamp_image_obj(fz_context *ctx, pdf_annot *annot, pdf_obj *ref) +{ + begin_annot_op(ctx, annot, "Set stamp image"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(Stamp), stamp_subtypes); + + pdf_dict_del(ctx, annot->obj, PDF_NAME(AP)); + pdf_dict_putp(ctx, annot->obj, "AP/N/Resources/XObject/I", ref); + + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +void pdf_set_annot_stamp_image(fz_context *ctx, pdf_annot *annot, fz_image *img) +{ + pdf_obj *ref = pdf_add_image(ctx, annot->page->doc, img); + fz_try(ctx) + pdf_set_annot_stamp_image_obj(ctx, annot, ref); + fz_always(ctx) + pdf_drop_obj(ctx, ref); + fz_catch(ctx) + fz_rethrow(ctx); +} + +static pdf_obj *filespec_subtypes[] = { + PDF_NAME(FileAttachment), + NULL, +}; + +int +pdf_annot_has_filespec(fz_context *ctx, pdf_annot *annot) +{ + return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(FS), filespec_subtypes); +} + +pdf_obj * +pdf_annot_filespec(fz_context *ctx, pdf_annot *annot) +{ + pdf_obj *filespec; + + pdf_annot_push_local_xref(ctx, annot); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(FS), filespec_subtypes); + filespec = pdf_dict_get(ctx, annot->obj, PDF_NAME(FS)); + } + fz_always(ctx) + pdf_annot_pop_local_xref(ctx, annot); + fz_catch(ctx) + fz_rethrow(ctx); + + return filespec; +} + +void +pdf_set_annot_filespec(fz_context *ctx, pdf_annot *annot, pdf_obj *fs) +{ + if (fs != PDF_NULL && !pdf_is_embedded_file(ctx, fs)) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot set non-filespec as annotation filespec"); + + begin_annot_op(ctx, annot, "Set filespec"); + + fz_try(ctx) + { + check_allowed_subtypes(ctx, annot, PDF_NAME(M), filespec_subtypes); + pdf_dict_put(ctx, pdf_annot_obj(ctx, annot), PDF_NAME(FS), fs); + end_annot_op(ctx, annot); + } + fz_catch(ctx) + { + abandon_annot_op(ctx, annot); + fz_rethrow(ctx); + } + + pdf_dirty_annot(ctx, annot); +} + +int +pdf_annot_hidden_for_editing(fz_context *ctx, pdf_annot *annot) +{ + return annot->hidden_editing; +} + +void +pdf_set_annot_hidden_for_editing(fz_context *ctx, pdf_annot *annot, int hidden) +{ + annot->hidden_editing = hidden; +}
