comparison 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
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 // Copyright (C) 2004-2025 Artifex Software, Inc.
2 //
3 // This file is part of MuPDF.
4 //
5 // MuPDF is free software: you can redistribute it and/or modify it under the
6 // terms of the GNU Affero General Public License as published by the Free
7 // Software Foundation, either version 3 of the License, or (at your option)
8 // any later version.
9 //
10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU Affero General Public License
16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17 //
18 // Alternative licensing terms are available from the licensor.
19 // For commercial licensing, see <https://www.artifex.com/> or contact
20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21 // CA 94129, USA, for further information.
22
23 #include "mupdf/fitz.h"
24 #include "pdf-annot-imp.h"
25
26 #include <string.h>
27 #include <float.h>
28
29 pdf_annot *
30 pdf_keep_annot(fz_context *ctx, pdf_annot *annot)
31 {
32 return fz_keep_imp(ctx, annot, &annot->refs);
33 }
34
35 void
36 pdf_drop_annot(fz_context *ctx, pdf_annot *annot)
37 {
38 if (fz_drop_imp(ctx, annot, &annot->refs))
39 {
40 pdf_drop_obj(ctx, annot->obj);
41 fz_free(ctx, annot);
42 }
43 }
44
45 void
46 pdf_drop_annots(fz_context *ctx, pdf_annot *annot)
47 {
48 while (annot)
49 {
50 pdf_annot *next = annot->next;
51 pdf_drop_annot(ctx, annot);
52 annot = next;
53 }
54 }
55
56 pdf_obj *
57 pdf_annot_ap(fz_context *ctx, pdf_annot *annot)
58 {
59 int flags = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(F));
60 int readonly = flags & PDF_ANNOT_IS_READ_ONLY;
61
62 pdf_obj *ap = pdf_dict_get(ctx, annot->obj, PDF_NAME(AP));
63 pdf_obj *ap_n = pdf_dict_get(ctx, ap, PDF_NAME(N));
64 pdf_obj *ap_r = pdf_dict_get(ctx, ap, PDF_NAME(R));
65 pdf_obj *ap_d = pdf_dict_get(ctx, ap, PDF_NAME(D));
66
67 if (!readonly && annot->is_hot && annot->is_active && ap_d)
68 ap = ap_d;
69 else if (!readonly && annot->is_hot && ap_r)
70 ap = ap_r;
71 else
72 ap = ap_n;
73
74 /* AP/N, AP/R and AP/D may be streams, or dictionaries of streams. */
75
76 /* If it's a stream, we have a winner! */
77 if (pdf_is_indirect(ctx, ap) && pdf_obj_num_is_stream(ctx, annot->page->doc, pdf_to_num(ctx, ap)))
78 return ap;
79
80 /* If it's not a stream, it may be a dictionary containing
81 * a range of possible values, that should be indexed by
82 * AS. */
83 return pdf_dict_get(ctx, ap, pdf_resolve_indirect_chain(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(AS))));
84 }
85
86 int pdf_annot_active(fz_context *ctx, pdf_annot *annot)
87 {
88 return annot ? annot->is_active : 0;
89 }
90
91 void pdf_set_annot_active(fz_context *ctx, pdf_annot *annot, int active)
92 {
93 int old;
94
95 if (!annot)
96 return;
97
98 old = annot->is_active;
99 annot->is_active = !!active;
100 if (old != annot->is_active)
101 pdf_set_annot_has_changed(ctx, annot);
102 }
103
104 int pdf_annot_hot(fz_context *ctx, pdf_annot *annot)
105 {
106 return annot ? annot->is_hot : 0;
107 }
108
109 void pdf_set_annot_hot(fz_context *ctx, pdf_annot *annot, int hot)
110 {
111 int old;
112
113 if (!annot)
114 return;
115
116 old = annot->is_hot;
117 annot->is_hot = !!hot;
118 if (old != annot->is_hot)
119 pdf_set_annot_has_changed(ctx, annot);
120 }
121
122 fz_matrix
123 pdf_annot_transform(fz_context *ctx, pdf_annot *annot)
124 {
125 fz_rect bbox, rect;
126 fz_matrix matrix;
127 float w, h, x, y;
128 pdf_obj *ap = pdf_annot_ap(ctx, annot);
129
130 rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
131 bbox = pdf_xobject_bbox(ctx, ap);
132 matrix = pdf_xobject_matrix(ctx, ap);
133
134 bbox = fz_transform_rect(bbox, matrix);
135 if (bbox.x1 == bbox.x0)
136 w = 0;
137 else
138 w = (rect.x1 - rect.x0) / (bbox.x1 - bbox.x0);
139 if (bbox.y1 == bbox.y0)
140 h = 0;
141 else
142 h = (rect.y1 - rect.y0) / (bbox.y1 - bbox.y0);
143 x = rect.x0 - (bbox.x0 * w);
144 y = rect.y0 - (bbox.y0 * h);
145
146 return fz_pre_scale(fz_translate(x, y), w, h);
147 }
148
149 /*
150 Internal function for creating a new pdf annotation.
151 */
152 static pdf_annot *
153 pdf_new_annot(fz_context *ctx, pdf_page *page, pdf_obj *obj)
154 {
155 pdf_annot *annot;
156
157 annot = fz_malloc_struct(ctx, pdf_annot);
158 annot->refs = 1;
159 annot->page = page; /* only borrowed, as the page owns the annot */
160 annot->obj = pdf_keep_obj(ctx, obj);
161
162 return annot;
163 }
164
165 void
166 pdf_nuke_annots(fz_context *ctx, pdf_page *page)
167 {
168 pdf_annot *annot;
169 pdf_annot *next;
170
171 for (annot = page->annots; annot; annot = next)
172 {
173 next = annot->next;
174 pdf_drop_obj(ctx, annot->obj);
175 annot->obj = NULL;
176 pdf_drop_annot(ctx, annot);
177 }
178 for (annot = page->widgets; annot; annot = next)
179 {
180 next = annot->next;
181 pdf_drop_obj(ctx, annot->obj);
182 annot->obj = NULL;
183 pdf_drop_annot(ctx, annot);
184 }
185
186 page->annots = NULL;
187 page->widgets = NULL;
188 page->annot_tailp = &page->annots;
189 page->widget_tailp = &page->widgets;
190 }
191
192 static pdf_annot *find_and_unlink_annot_from_list(fz_context *ctx, pdf_annot **prev, pdf_obj *obj)
193 {
194 int num = pdf_to_num(ctx, obj);
195 pdf_annot *node;
196
197 node = *prev;
198 while (node)
199 {
200 if (pdf_to_num(ctx, node->obj) == num)
201 {
202 *prev = node->next;
203 node->next = NULL;
204 return node;
205 }
206 else
207 {
208 prev = &node->next;
209 node = node->next;
210 }
211 }
212
213 return NULL;
214 }
215
216 void
217 pdf_sync_annots(fz_context *ctx, pdf_page *page)
218 {
219 pdf_annot *old_annots;
220 pdf_annot *old_widgets;
221 pdf_annot *annot, *next;
222 pdf_obj *annots;
223 pdf_obj *subtype;
224 int i, n;
225
226 // Save list of annots loaded last time (if any).
227 old_annots = page->annots;
228 old_widgets = page->widgets;
229 page->annots = NULL;
230 page->widgets = NULL;
231 page->annot_tailp = &page->annots;
232 page->widget_tailp = &page->widgets;
233
234 // Create new list of annots (reusing old annots when possible)
235 annots = pdf_dict_get(ctx, page->obj, PDF_NAME(Annots));
236 n = pdf_array_len(ctx, annots);
237 for (i = 0; i < n; ++i)
238 {
239 pdf_obj *obj = pdf_array_get(ctx, annots, i);
240 if (pdf_is_dict(ctx, obj))
241 {
242 subtype = pdf_dict_get(ctx, obj, PDF_NAME(Subtype));
243 if (pdf_name_eq(ctx, subtype, PDF_NAME(Link)))
244 continue;
245 if (pdf_name_eq(ctx, subtype, PDF_NAME(Popup)))
246 continue;
247
248 if (pdf_name_eq(ctx, subtype, PDF_NAME(Widget)))
249 {
250 annot = find_and_unlink_annot_from_list(ctx, &old_widgets, obj);
251 if (!annot)
252 annot = pdf_new_annot(ctx, page, obj);
253 *page->widget_tailp = annot;
254 page->widget_tailp = &annot->next;
255 }
256 else
257 {
258 annot = find_and_unlink_annot_from_list(ctx, &old_annots, obj);
259 if (!annot)
260 annot = pdf_new_annot(ctx, page, obj);
261 *page->annot_tailp = annot;
262 page->annot_tailp = &annot->next;
263 }
264 }
265 }
266
267 // Nuke the annot structs that are no longer used on the page
268 for (annot = old_annots; annot; annot = next)
269 {
270 next = annot->next;
271 pdf_drop_obj(ctx, annot->obj);
272 annot->obj = NULL;
273 pdf_drop_annot(ctx, annot);
274 }
275 for (annot = old_widgets; annot; annot = next)
276 {
277 next = annot->next;
278 pdf_drop_obj(ctx, annot->obj);
279 annot->obj = NULL;
280 pdf_drop_annot(ctx, annot);
281 }
282 }
283
284 void
285 pdf_load_annots(fz_context *ctx, pdf_page *page)
286 {
287 pdf_sync_annots(ctx, page);
288
289 /* We need to run a resynth pass on the annotations on this
290 * page. That means rerunning it on the complete document. */
291 page->doc->resynth_required = 1;
292 /* And actually update the page so that any annotations required
293 * get synthesised. */
294 pdf_update_page(ctx, page);
295 }
296
297 pdf_annot *
298 pdf_first_annot(fz_context *ctx, pdf_page *page)
299 {
300 return page ? page->annots : NULL;
301 }
302
303 pdf_annot *
304 pdf_next_annot(fz_context *ctx, pdf_annot *annot)
305 {
306 return annot ? annot->next : NULL;
307 }
308
309 pdf_obj *pdf_annot_obj(fz_context *ctx, pdf_annot *annot)
310 {
311 return annot ? annot->obj : NULL;
312 }
313
314 pdf_page *pdf_annot_page(fz_context *ctx, pdf_annot *annot)
315 {
316 return annot ? annot->page : NULL;
317 }
318
319 fz_rect
320 pdf_bound_annot(fz_context *ctx, pdf_annot *annot)
321 {
322 fz_matrix page_ctm;
323 fz_rect rect;
324 int flags;
325
326 pdf_annot_push_local_xref(ctx, annot);
327
328 fz_try(ctx)
329 {
330 rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
331 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
332
333 flags = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(F));
334 if (flags & PDF_ANNOT_IS_NO_ROTATE)
335 {
336 int rotate = pdf_dict_get_inheritable_int(ctx, annot->page->obj, PDF_NAME(Rotate));
337 fz_point tp = fz_transform_point_xy(rect.x0, rect.y1, page_ctm);
338 page_ctm = fz_concat(page_ctm, fz_translate(-tp.x, -tp.y));
339 page_ctm = fz_concat(page_ctm, fz_rotate(-rotate));
340 page_ctm = fz_concat(page_ctm, fz_translate(tp.x, tp.y));
341 }
342 }
343 fz_always(ctx)
344 pdf_annot_pop_local_xref(ctx, annot);
345 fz_catch(ctx)
346 fz_rethrow(ctx);
347
348 return fz_transform_rect(rect, page_ctm);
349 }
350
351 void
352 pdf_annot_request_resynthesis(fz_context *ctx, pdf_annot *annot)
353 {
354 if (annot == NULL)
355 return;
356
357 annot->needs_new_ap = 1;
358 annot->page->doc->resynth_required = 1;
359 }
360
361 void
362 pdf_annot_request_synthesis(fz_context *ctx, pdf_annot *annot)
363 {
364 if (annot == NULL)
365 return;
366 if (!pdf_annot_ap(ctx, annot))
367 pdf_annot_request_resynthesis(ctx, annot);
368 }
369
370 int
371 pdf_annot_needs_resynthesis(fz_context *ctx, pdf_annot *annot)
372 {
373 return annot ? annot->needs_new_ap : 0;
374 }
375
376 void pdf_set_annot_resynthesised(fz_context *ctx, pdf_annot *annot)
377 {
378 if (annot == NULL)
379 return;
380
381 annot->needs_new_ap = 0;
382 pdf_set_annot_has_changed(ctx, annot);
383 }
384
385 void pdf_set_annot_has_changed(fz_context *ctx, pdf_annot *annot)
386 {
387 if (annot == NULL)
388 return;
389
390 annot->has_new_ap = 1;
391 }
392
393 void
394 pdf_dirty_annot(fz_context *ctx, pdf_annot *annot)
395 {
396 pdf_annot_request_resynthesis(ctx, annot);
397 }
398
399 const char *
400 pdf_string_from_annot_type(fz_context *ctx, enum pdf_annot_type type)
401 {
402 switch (type)
403 {
404 case PDF_ANNOT_TEXT: return "Text";
405 case PDF_ANNOT_LINK: return "Link";
406 case PDF_ANNOT_FREE_TEXT: return "FreeText";
407 case PDF_ANNOT_LINE: return "Line";
408 case PDF_ANNOT_SQUARE: return "Square";
409 case PDF_ANNOT_CIRCLE: return "Circle";
410 case PDF_ANNOT_POLYGON: return "Polygon";
411 case PDF_ANNOT_POLY_LINE: return "PolyLine";
412 case PDF_ANNOT_HIGHLIGHT: return "Highlight";
413 case PDF_ANNOT_UNDERLINE: return "Underline";
414 case PDF_ANNOT_SQUIGGLY: return "Squiggly";
415 case PDF_ANNOT_STRIKE_OUT: return "StrikeOut";
416 case PDF_ANNOT_REDACT: return "Redact";
417 case PDF_ANNOT_STAMP: return "Stamp";
418 case PDF_ANNOT_CARET: return "Caret";
419 case PDF_ANNOT_INK: return "Ink";
420 case PDF_ANNOT_POPUP: return "Popup";
421 case PDF_ANNOT_FILE_ATTACHMENT: return "FileAttachment";
422 case PDF_ANNOT_SOUND: return "Sound";
423 case PDF_ANNOT_MOVIE: return "Movie";
424 case PDF_ANNOT_RICH_MEDIA: return "RichMedia";
425 case PDF_ANNOT_WIDGET: return "Widget";
426 case PDF_ANNOT_SCREEN: return "Screen";
427 case PDF_ANNOT_PRINTER_MARK: return "PrinterMark";
428 case PDF_ANNOT_TRAP_NET: return "TrapNet";
429 case PDF_ANNOT_WATERMARK: return "Watermark";
430 case PDF_ANNOT_3D: return "3D";
431 case PDF_ANNOT_PROJECTION: return "Projection";
432 default: return "UNKNOWN";
433 }
434 }
435
436 enum pdf_annot_type
437 pdf_annot_type_from_string(fz_context *ctx, const char *subtype)
438 {
439 if (!strcmp("Text", subtype)) return PDF_ANNOT_TEXT;
440 if (!strcmp("Link", subtype)) return PDF_ANNOT_LINK;
441 if (!strcmp("FreeText", subtype)) return PDF_ANNOT_FREE_TEXT;
442 if (!strcmp("Line", subtype)) return PDF_ANNOT_LINE;
443 if (!strcmp("Square", subtype)) return PDF_ANNOT_SQUARE;
444 if (!strcmp("Circle", subtype)) return PDF_ANNOT_CIRCLE;
445 if (!strcmp("Polygon", subtype)) return PDF_ANNOT_POLYGON;
446 if (!strcmp("PolyLine", subtype)) return PDF_ANNOT_POLY_LINE;
447 if (!strcmp("Highlight", subtype)) return PDF_ANNOT_HIGHLIGHT;
448 if (!strcmp("Underline", subtype)) return PDF_ANNOT_UNDERLINE;
449 if (!strcmp("Squiggly", subtype)) return PDF_ANNOT_SQUIGGLY;
450 if (!strcmp("StrikeOut", subtype)) return PDF_ANNOT_STRIKE_OUT;
451 if (!strcmp("Redact", subtype)) return PDF_ANNOT_REDACT;
452 if (!strcmp("Stamp", subtype)) return PDF_ANNOT_STAMP;
453 if (!strcmp("Caret", subtype)) return PDF_ANNOT_CARET;
454 if (!strcmp("Ink", subtype)) return PDF_ANNOT_INK;
455 if (!strcmp("Popup", subtype)) return PDF_ANNOT_POPUP;
456 if (!strcmp("FileAttachment", subtype)) return PDF_ANNOT_FILE_ATTACHMENT;
457 if (!strcmp("Sound", subtype)) return PDF_ANNOT_SOUND;
458 if (!strcmp("Movie", subtype)) return PDF_ANNOT_MOVIE;
459 if (!strcmp("RichMedia", subtype)) return PDF_ANNOT_RICH_MEDIA;
460 if (!strcmp("Widget", subtype)) return PDF_ANNOT_WIDGET;
461 if (!strcmp("Screen", subtype)) return PDF_ANNOT_SCREEN;
462 if (!strcmp("PrinterMark", subtype)) return PDF_ANNOT_PRINTER_MARK;
463 if (!strcmp("TrapNet", subtype)) return PDF_ANNOT_TRAP_NET;
464 if (!strcmp("Watermark", subtype)) return PDF_ANNOT_WATERMARK;
465 if (!strcmp("3D", subtype)) return PDF_ANNOT_3D;
466 if (!strcmp("Projection", subtype)) return PDF_ANNOT_PROJECTION;
467 return PDF_ANNOT_UNKNOWN;
468 }
469
470 static void
471 begin_annot_op(fz_context *ctx, pdf_annot *annot, const char *op)
472 {
473 if (!annot->page)
474 fz_throw(ctx, FZ_ERROR_ARGUMENT, "annotation not bound to any page");
475
476 pdf_begin_operation(ctx, annot->page->doc, op);
477 }
478
479 static void
480 end_annot_op(fz_context *ctx, pdf_annot *annot)
481 {
482 pdf_end_operation(ctx, annot->page->doc);
483 }
484
485 static void
486 abandon_annot_op(fz_context *ctx, pdf_annot *annot)
487 {
488 pdf_abandon_operation(ctx, annot->page->doc);
489 }
490
491 static int is_allowed_subtype(fz_context *ctx, pdf_annot *annot, pdf_obj *property, pdf_obj **allowed)
492 {
493 pdf_obj *subtype;
494
495 subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
496 while (*allowed) {
497 if (pdf_name_eq(ctx, subtype, *allowed))
498 return 1;
499 allowed++;
500 }
501
502 return 0;
503 }
504
505 static int is_allowed_subtype_wrap(fz_context *ctx, pdf_annot *annot, pdf_obj *property, pdf_obj **allowed)
506 {
507 int ret;
508
509 pdf_annot_push_local_xref(ctx, annot);
510
511 fz_try(ctx)
512 ret = is_allowed_subtype(ctx, annot, property, allowed);
513 fz_always(ctx)
514 pdf_annot_pop_local_xref(ctx, annot);
515 fz_catch(ctx)
516 fz_rethrow(ctx);
517
518 return ret;
519 }
520
521 static void check_allowed_subtypes(fz_context *ctx, pdf_annot *annot, pdf_obj *property, pdf_obj **allowed)
522 {
523 pdf_obj *subtype;
524
525 subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
526 if (!is_allowed_subtype(ctx, annot, property, allowed))
527 fz_throw(ctx, FZ_ERROR_ARGUMENT, "%s annotations have no %s property", pdf_to_name(ctx, subtype), pdf_to_name(ctx, property));
528 }
529
530 pdf_annot *
531 pdf_create_annot_raw(fz_context *ctx, pdf_page *page, enum pdf_annot_type type)
532 {
533 pdf_annot *annot = NULL;
534 pdf_document *doc = page->doc;
535 pdf_obj *annot_obj = pdf_new_dict(ctx, doc, 0);
536 pdf_obj *ind_obj = NULL;
537 pdf_obj *free_arr = NULL;
538
539 fz_var(annot);
540 fz_var(ind_obj);
541 fz_var(free_arr);
542 fz_try(ctx)
543 {
544 int ind_obj_num;
545 const char *type_str;
546 pdf_obj *annot_arr;
547
548 type_str = pdf_string_from_annot_type(ctx, type);
549 if (type == PDF_ANNOT_UNKNOWN)
550 fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot create unknown annotation");
551
552 annot_arr = pdf_dict_get(ctx, page->obj, PDF_NAME(Annots));
553 if (!pdf_is_array(ctx, annot_arr))
554 annot_arr = pdf_dict_put_array(ctx, page->obj, PDF_NAME(Annots), 0);
555 else if (pdf_is_indirect(ctx, annot_arr))
556 {
557 free_arr = annot_arr = pdf_copy_array(ctx, annot_arr);
558 pdf_dict_put(ctx, page->obj, PDF_NAME(Annots), annot_arr);
559 }
560
561 pdf_dict_put(ctx, annot_obj, PDF_NAME(Type), PDF_NAME(Annot));
562 pdf_dict_put_name(ctx, annot_obj, PDF_NAME(Subtype), type_str);
563
564 /*
565 Both annotation object and annotation structure are now created.
566 Insert the object in the hierarchy and the structure in the
567 page's array.
568 */
569 ind_obj_num = pdf_create_object(ctx, doc);
570 pdf_update_object(ctx, doc, ind_obj_num, annot_obj);
571 ind_obj = pdf_new_indirect(ctx, doc, ind_obj_num, 0);
572 pdf_array_push(ctx, annot_arr, ind_obj);
573
574 annot = pdf_new_annot(ctx, page, ind_obj);
575
576 /*
577 Linking must be done after any call that might throw because
578 pdf_drop_annots below actually frees a list. Put the new annot
579 at the end of the list, so that it will be drawn last.
580 */
581 if (type == PDF_ANNOT_WIDGET)
582 {
583 *page->widget_tailp = annot;
584 page->widget_tailp = &annot->next;
585 }
586 else
587 {
588 *page->annot_tailp = annot;
589 page->annot_tailp = &annot->next;
590 }
591 }
592 fz_always(ctx)
593 {
594 pdf_drop_obj(ctx, free_arr);
595 pdf_drop_obj(ctx, annot_obj);
596 pdf_drop_obj(ctx, ind_obj);
597 }
598 fz_catch(ctx)
599 {
600 pdf_drop_annots(ctx, annot);
601 fz_rethrow(ctx);
602 }
603
604 return pdf_keep_annot(ctx, annot);
605 }
606
607 fz_link *
608 pdf_create_link(fz_context *ctx, pdf_page *page, fz_rect bbox, const char *uri)
609 {
610 fz_link *link = NULL;
611 pdf_document *doc = page->doc;
612 pdf_obj *annot_obj = pdf_new_dict(ctx, doc, 0);
613 pdf_obj *ind_obj = NULL;
614 pdf_obj *bs = NULL;
615 pdf_obj *a = NULL;
616 fz_link **linkp;
617 fz_rect page_mediabox;
618 fz_matrix page_ctm;
619 fz_rect rect;
620 pdf_obj *free_arr = NULL;
621
622 fz_var(link);
623 fz_var(ind_obj);
624 fz_var(bs);
625 fz_var(a);
626 fz_var(free_arr);
627
628 pdf_begin_operation(ctx, page->doc, "Create Link");
629
630 fz_try(ctx)
631 {
632 int ind_obj_num;
633 pdf_obj *annot_arr;
634
635 pdf_page_transform(ctx, page, &page_mediabox, &page_ctm);
636 page_ctm = fz_invert_matrix(page_ctm);
637 rect = fz_transform_rect(bbox, page_ctm);
638
639 annot_arr = pdf_dict_get(ctx, page->obj, PDF_NAME(Annots));
640 if (!pdf_is_array(ctx, annot_arr))
641 annot_arr = pdf_dict_put_array(ctx, page->obj, PDF_NAME(Annots), 0);
642 else if (pdf_is_indirect(ctx, annot_arr))
643 {
644 free_arr = annot_arr = pdf_copy_array(ctx, annot_arr);
645 pdf_dict_put(ctx, page->obj, PDF_NAME(Annots), annot_arr);
646 }
647
648 pdf_dict_put(ctx, annot_obj, PDF_NAME(Type), PDF_NAME(Annot));
649 pdf_dict_put(ctx, annot_obj, PDF_NAME(Subtype), PDF_NAME(Link));
650 pdf_dict_put_rect(ctx, annot_obj, PDF_NAME(Rect), rect);
651
652 bs = pdf_dict_put_dict(ctx, annot_obj, PDF_NAME(BS), 4);
653 pdf_dict_put(ctx, bs, PDF_NAME(S), PDF_NAME(S));
654 pdf_dict_put(ctx, bs, PDF_NAME(Type), PDF_NAME(Border));
655 pdf_dict_put_int(ctx, bs, PDF_NAME(W), 0);
656
657 pdf_dict_put_drop(ctx, annot_obj, PDF_NAME(A),
658 pdf_new_action_from_link(ctx, doc, uri));
659
660 /*
661 Both annotation object and annotation structure are now created.
662 Insert the object in the hierarchy and the structure in the
663 page's array.
664 */
665 ind_obj_num = pdf_create_object(ctx, doc);
666 pdf_update_object(ctx, doc, ind_obj_num, annot_obj);
667 ind_obj = pdf_new_indirect(ctx, doc, ind_obj_num, 0);
668 pdf_array_push(ctx, annot_arr, ind_obj);
669
670 link = (fz_link *) pdf_new_link(ctx, page, bbox, uri, annot_obj);
671
672 linkp = &page->links;
673
674 while (*linkp != NULL)
675 linkp = &(*linkp)->next;
676
677 *linkp = link;
678 pdf_end_operation(ctx, page->doc);
679 }
680 fz_always(ctx)
681 {
682 pdf_drop_obj(ctx, free_arr);
683 pdf_drop_obj(ctx, annot_obj);
684 pdf_drop_obj(ctx, ind_obj);
685 }
686 fz_catch(ctx)
687 {
688 pdf_abandon_operation(ctx, page->doc);
689 fz_rethrow(ctx);
690 }
691
692 return fz_keep_link(ctx, link);
693 }
694
695 void pdf_delete_link(fz_context *ctx, pdf_page *page, fz_link *link)
696 {
697 fz_link **linkptr;
698 pdf_obj *annots;
699 int i;
700
701 if (link == NULL || page == NULL || page != ((pdf_link *) link)->page)
702 return;
703
704 for (linkptr = &page->links; *linkptr; linkptr = &((*linkptr)->next))
705 {
706 if (*linkptr == link)
707 break;
708 }
709
710 if (*linkptr == NULL)
711 return;
712
713 /* Link may no longer borrow page pointer, since they are separated */
714 ((pdf_link *) link)->page = NULL;
715
716 pdf_begin_operation(ctx, page->doc, "Delete Link");
717
718 fz_try(ctx)
719 {
720
721 annots = pdf_dict_get(ctx, page->obj, PDF_NAME(Annots));
722 i = pdf_array_find(ctx, annots, ((pdf_link *) link)->obj);
723 if (i >= 0)
724 pdf_array_delete(ctx, annots, i);
725 *linkptr = link->next;
726 link->next = NULL;
727 fz_drop_link(ctx, link);
728 pdf_end_operation(ctx, page->doc);
729 }
730 fz_catch(ctx)
731 {
732 pdf_abandon_operation(ctx, page->doc);
733 fz_rethrow(ctx);
734 }
735 }
736
737 static pdf_obj *
738 pdf_add_popup_annot(fz_context *ctx, pdf_annot *annot)
739 {
740 pdf_obj *annots, *popup;
741
742 popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
743 if (popup)
744 return popup;
745
746 annots = pdf_dict_get(ctx, annot->page->obj, PDF_NAME(Annots));
747 if (!annots)
748 return NULL;
749
750 popup = pdf_add_new_dict(ctx, annot->page->doc, 4);
751 pdf_array_push_drop(ctx, annots, popup);
752
753 pdf_dict_put(ctx, popup, PDF_NAME(Type), PDF_NAME(Annot));
754 pdf_dict_put(ctx, popup, PDF_NAME(Subtype), PDF_NAME(Popup));
755 pdf_dict_put(ctx, popup, PDF_NAME(Parent), annot->obj);
756 pdf_dict_put_rect(ctx, popup, PDF_NAME(Rect), fz_make_rect(0,0,0,0));
757
758 pdf_dict_put(ctx, annot->obj, PDF_NAME(Popup), popup);
759
760 return popup;
761 }
762
763 static pdf_obj *popup_subtypes[] = {
764 PDF_NAME(Text),
765 PDF_NAME(FreeText),
766 PDF_NAME(Line),
767 PDF_NAME(Square),
768 PDF_NAME(Circle),
769 PDF_NAME(Polygon),
770 PDF_NAME(PolyLine),
771 PDF_NAME(Highlight),
772 PDF_NAME(Underline),
773 PDF_NAME(Squiggly),
774 PDF_NAME(StrikeOut),
775 PDF_NAME(Stamp),
776 PDF_NAME(Caret),
777 PDF_NAME(Ink),
778 PDF_NAME(FileAttachment),
779 PDF_NAME(Sound),
780 PDF_NAME(Redact),
781 NULL,
782 };
783
784 int
785 pdf_annot_has_popup(fz_context *ctx, pdf_annot *annot)
786 {
787 return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(Popup), popup_subtypes);
788 }
789
790 void pdf_set_annot_popup(fz_context *ctx, pdf_annot *annot, fz_rect rect)
791 {
792 fz_matrix page_ctm, inv_page_ctm;
793 pdf_obj *popup;
794
795 begin_annot_op(ctx, annot, "Set popup");
796
797 fz_try(ctx)
798 {
799 check_allowed_subtypes(ctx, annot, PDF_NAME(Popup), popup_subtypes);
800 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
801 inv_page_ctm = fz_invert_matrix(page_ctm);
802 rect = fz_transform_rect(rect, inv_page_ctm);
803 popup = pdf_add_popup_annot(ctx, annot);
804 pdf_dict_put_rect(ctx, popup, PDF_NAME(Rect), rect);
805 end_annot_op(ctx, annot);
806 }
807 fz_catch(ctx)
808 {
809 abandon_annot_op(ctx, annot);
810 fz_rethrow(ctx);
811 }
812
813 pdf_dirty_annot(ctx, annot);
814 }
815
816 fz_rect pdf_annot_popup(fz_context *ctx, pdf_annot *annot)
817 {
818 fz_matrix page_ctm;
819 fz_rect rect;
820 pdf_obj *popup;
821
822 pdf_annot_push_local_xref(ctx, annot);
823
824 fz_try(ctx)
825 {
826 check_allowed_subtypes(ctx, annot, PDF_NAME(Popup), popup_subtypes);
827 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
828 popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
829 rect = pdf_dict_get_rect(ctx, popup, PDF_NAME(Rect));
830 rect = fz_transform_rect(rect, page_ctm);
831 }
832 fz_always(ctx)
833 pdf_annot_pop_local_xref(ctx, annot);
834 fz_catch(ctx)
835 fz_rethrow(ctx);
836
837 return rect;
838 }
839
840 pdf_annot *
841 pdf_create_annot(fz_context *ctx, pdf_page *page, enum pdf_annot_type type)
842 {
843 static const float black[3] = { 0, 0, 0 };
844 static const float red[3] = { 1, 0, 0 };
845 static const float green[3] = { 0, 1, 0 };
846 static const float blue[3] = { 0, 0, 1 };
847 static const float yellow[3] = { 1, 1, 0 };
848 static const float magenta[3] = { 1, 0, 1 };
849
850 int flags = PDF_ANNOT_IS_PRINT; /* Make printable as default */
851
852 pdf_annot *annot = NULL;
853
854 fz_var(annot);
855
856 pdf_begin_operation(ctx, page->doc, "Create Annotation");
857
858 fz_try(ctx)
859 {
860 annot = pdf_create_annot_raw(ctx, page, type);
861
862 switch (type)
863 {
864 default:
865 break;
866
867 case PDF_ANNOT_TEXT:
868 case PDF_ANNOT_FILE_ATTACHMENT:
869 case PDF_ANNOT_SOUND:
870 {
871 fz_rect icon_rect = { 12, 12, 12+20, 12+20 };
872 flags = PDF_ANNOT_IS_PRINT | PDF_ANNOT_IS_NO_ZOOM | PDF_ANNOT_IS_NO_ROTATE;
873 pdf_set_annot_rect(ctx, annot, icon_rect);
874 pdf_set_annot_color(ctx, annot, 3, yellow);
875 pdf_set_annot_popup(ctx, annot, fz_make_rect(32, 12, 32+200, 12+100));
876 }
877 break;
878
879 case PDF_ANNOT_FREE_TEXT:
880 {
881 fz_rect text_rect = { 12, 12, 12+200, 12+100 };
882
883 /* Use undocumented Adobe property to match page rotation. */
884 int rot = pdf_dict_get_inheritable_int(ctx, page->obj, PDF_NAME(Rotate));
885 if (rot != 0)
886 pdf_dict_put_int(ctx, annot->obj, PDF_NAME(Rotate), rot);
887
888 pdf_set_annot_rect(ctx, annot, text_rect);
889 pdf_set_annot_border_width(ctx, annot, 0);
890 pdf_set_annot_default_appearance(ctx, annot, "Helv", 12, nelem(black), black);
891 }
892 break;
893
894 case PDF_ANNOT_STAMP:
895 {
896 fz_rect stamp_rect = { 12, 12, 12+190, 12+50 };
897 pdf_set_annot_rect(ctx, annot, stamp_rect);
898 pdf_set_annot_color(ctx, annot, 3, red);
899 pdf_set_annot_icon_name(ctx, annot, "Draft");
900 }
901 break;
902
903 case PDF_ANNOT_CARET:
904 {
905 fz_rect caret_rect = { 12, 12, 12+18, 12+15 };
906 pdf_set_annot_rect(ctx, annot, caret_rect);
907 pdf_set_annot_color(ctx, annot, 3, blue);
908 }
909 break;
910
911 case PDF_ANNOT_LINE:
912 {
913 fz_point a = { 12, 12 }, b = { 12 + 100, 12 + 50 };
914 pdf_set_annot_line(ctx, annot, a, b);
915 pdf_set_annot_border_width(ctx, annot, 1);
916 pdf_set_annot_color(ctx, annot, 3, red);
917 }
918 break;
919
920 case PDF_ANNOT_SQUARE:
921 case PDF_ANNOT_CIRCLE:
922 {
923 fz_rect shape_rect = { 12, 12, 12+100, 12+50 };
924 fz_rect rd = { 0.5, 0.5, 0.5, 0.5 };
925 pdf_set_annot_rect(ctx, annot, shape_rect);
926 pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), rd);
927 pdf_set_annot_border_width(ctx, annot, 1);
928 pdf_set_annot_color(ctx, annot, 3, red);
929 }
930 break;
931
932 case PDF_ANNOT_POLYGON:
933 case PDF_ANNOT_POLY_LINE:
934 case PDF_ANNOT_INK:
935 {
936 fz_rect rd = { 0.5, 0.5, 0.5, 0.5 };
937 pdf_set_annot_border_width(ctx, annot, 1);
938 pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), rd);
939 pdf_set_annot_color(ctx, annot, 3, red);
940 }
941 break;
942
943 case PDF_ANNOT_HIGHLIGHT:
944 pdf_set_annot_color(ctx, annot, 3, yellow);
945 break;
946 case PDF_ANNOT_UNDERLINE:
947 pdf_set_annot_color(ctx, annot, 3, green);
948 break;
949 case PDF_ANNOT_STRIKE_OUT:
950 pdf_set_annot_color(ctx, annot, 3, red);
951 break;
952 case PDF_ANNOT_SQUIGGLY:
953 pdf_set_annot_color(ctx, annot, 3, magenta);
954 break;
955 }
956
957 pdf_dict_put(ctx, annot->obj, PDF_NAME(P), page->obj);
958 pdf_dict_put_int(ctx, annot->obj, PDF_NAME(F), flags);
959 pdf_end_operation(ctx, page->doc);
960 }
961 fz_catch(ctx)
962 {
963 pdf_drop_annot(ctx, annot);
964 pdf_abandon_operation(ctx, page->doc);
965 fz_rethrow(ctx);
966 }
967
968 return annot;
969 }
970
971 static int
972 remove_from_tree(fz_context *ctx, pdf_obj *arr, pdf_obj *item, pdf_cycle_list *cycle_up)
973 {
974 pdf_cycle_list cycle;
975 int i, n, res = 0;
976
977 if (arr == NULL || pdf_cycle(ctx, &cycle, cycle_up, arr))
978 return 0;
979
980 n = pdf_array_len(ctx, arr);
981 for (i = 0; i < n; ++i)
982 {
983 pdf_obj *obj = pdf_array_get(ctx, arr, i);
984 if (!pdf_objcmp(ctx, obj, item))
985 {
986 pdf_array_delete(ctx, arr, i);
987 res = 1;
988 break;
989 }
990
991 if (remove_from_tree(ctx, pdf_dict_get(ctx, obj, PDF_NAME(Kids)), item, &cycle))
992 {
993 res = 1;
994 break;
995 }
996 }
997
998 return res;
999 }
1000
1001 void
1002 pdf_delete_annot(fz_context *ctx, pdf_page *page, pdf_annot *annot)
1003 {
1004 pdf_document *doc;
1005 pdf_annot **annotptr;
1006 pdf_obj *annot_arr, *popup;
1007 int i;
1008 int is_widget = 0;
1009
1010 if (annot == NULL || page == NULL || page != annot->page)
1011 return;
1012
1013 doc = page->doc;
1014
1015 /* Look for the annot in the page's list */
1016 for (annotptr = &page->annots; *annotptr; annotptr = &(*annotptr)->next)
1017 {
1018 if (*annotptr == annot)
1019 break;
1020 }
1021
1022 if (*annotptr == NULL)
1023 {
1024 is_widget = 1;
1025
1026 /* Look also in the widget list*/
1027 for (annotptr = &page->widgets; *annotptr; annotptr = &(*annotptr)->next)
1028 {
1029 if (*annotptr == annot)
1030 break;
1031 }
1032 }
1033
1034 /* Check the passed annotation was of this page */
1035 if (*annotptr == NULL)
1036 return;
1037
1038 pdf_begin_operation(ctx, page->doc, "Delete Annotation");
1039
1040 /* Remove annot from page's list */
1041 *annotptr = annot->next;
1042
1043 /* Annot may no longer borrow page pointer, since they are separated */
1044 annot->page = NULL;
1045
1046 /* If the removed annotation was the last in the list adjust the end pointer */
1047 if (*annotptr == NULL)
1048 {
1049 if (is_widget)
1050 page->widget_tailp = annotptr;
1051 else
1052 page->annot_tailp = annotptr;
1053 }
1054
1055 fz_try(ctx)
1056 {
1057 /* Remove the annot from the "Annots" array. */
1058 annot_arr = pdf_dict_get(ctx, page->obj, PDF_NAME(Annots));
1059 i = pdf_array_find(ctx, annot_arr, annot->obj);
1060 if (i >= 0)
1061 pdf_array_delete(ctx, annot_arr, i);
1062
1063 /* Remove the associated Popup annotation from the Annots array */
1064 popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
1065 if (popup)
1066 {
1067 i = pdf_array_find(ctx, annot_arr, popup);
1068 if (i >= 0)
1069 pdf_array_delete(ctx, annot_arr, i);
1070 }
1071
1072 /* For a widget, remove also from the AcroForm tree */
1073 if (is_widget)
1074 {
1075 pdf_obj *root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root));
1076 pdf_obj *acroform = pdf_dict_get(ctx, root, PDF_NAME(AcroForm));
1077 pdf_obj *fields = pdf_dict_get(ctx, acroform, PDF_NAME(Fields));
1078 (void)remove_from_tree(ctx, fields, annot->obj, NULL);
1079 }
1080
1081 /* The garbage collection pass when saving will remove the annot object,
1082 * removing it here may break files if multiple pages use the same annot. */
1083 pdf_end_operation(ctx, page->doc);
1084 }
1085 fz_always(ctx)
1086 {
1087 /* Drop the reference to annot previously held by the page list. */
1088 pdf_drop_annot(ctx, annot);
1089 }
1090 fz_catch(ctx)
1091 {
1092 pdf_abandon_operation(ctx, page->doc);
1093 fz_rethrow(ctx);
1094 }
1095 }
1096
1097 enum pdf_annot_type
1098 pdf_annot_type(fz_context *ctx, pdf_annot *annot)
1099 {
1100 enum pdf_annot_type ret;
1101
1102 pdf_annot_push_local_xref(ctx, annot);
1103
1104 fz_try(ctx)
1105 {
1106 const char *subtype = pdf_dict_get_name(ctx, annot->obj, PDF_NAME(Subtype));
1107 ret = pdf_annot_type_from_string(ctx, subtype);
1108 }
1109 fz_always(ctx)
1110 pdf_annot_pop_local_xref(ctx, annot);
1111 fz_catch(ctx)
1112 fz_rethrow(ctx);
1113
1114 return ret;
1115 }
1116
1117 int
1118 pdf_annot_flags(fz_context *ctx, pdf_annot *annot)
1119 {
1120 int ret;
1121 pdf_annot_push_local_xref(ctx, annot);
1122
1123 fz_try(ctx)
1124 ret = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(F));
1125 fz_always(ctx)
1126 pdf_annot_pop_local_xref(ctx, annot);
1127 fz_catch(ctx)
1128 fz_rethrow(ctx);
1129
1130 return ret;
1131 }
1132
1133 void
1134 pdf_set_annot_flags(fz_context *ctx, pdf_annot *annot, int flags)
1135 {
1136 begin_annot_op(ctx, annot, "Set flags");
1137
1138 fz_try(ctx)
1139 {
1140 pdf_dict_put_int(ctx, annot->obj, PDF_NAME(F), flags);
1141 end_annot_op(ctx, annot);
1142 }
1143 fz_catch(ctx)
1144 {
1145 abandon_annot_op(ctx, annot);
1146 fz_rethrow(ctx);
1147 }
1148
1149 pdf_dirty_annot(ctx, annot);
1150 }
1151
1152 static pdf_obj *rect_subtypes[] = {
1153 PDF_NAME(Text),
1154 PDF_NAME(FreeText),
1155 PDF_NAME(Square),
1156 PDF_NAME(Circle),
1157 PDF_NAME(Redact),
1158 PDF_NAME(Stamp),
1159 PDF_NAME(Caret),
1160 PDF_NAME(Popup),
1161 PDF_NAME(FileAttachment),
1162 PDF_NAME(Sound),
1163 PDF_NAME(Movie),
1164 PDF_NAME(Widget),
1165 NULL,
1166 };
1167
1168 int
1169 pdf_annot_has_rect(fz_context *ctx, pdf_annot *annot)
1170 {
1171 /* True for annotations where the user can manipulate the size or location
1172 * of the annotation through the Rect.
1173 * False for annotations where the Rect is computed from other
1174 * annotation data such as InkList, QuadPoints, and Vertices.
1175 */
1176 return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(Rect), rect_subtypes);
1177 }
1178
1179 fz_rect
1180 pdf_annot_rect(fz_context *ctx, pdf_annot *annot)
1181 {
1182 fz_matrix page_ctm;
1183 fz_rect rect;
1184 fz_rect rd;
1185
1186 pdf_annot_push_local_xref(ctx, annot);
1187 fz_try(ctx)
1188 {
1189 check_allowed_subtypes(ctx, annot, PDF_NAME(Rect), rect_subtypes);
1190 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1191 rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
1192
1193 /* remove RD adjustment from bounding box to get design box */
1194 rd = pdf_annot_rect_diff(ctx, annot);
1195 rect.x0 += rd.x0;
1196 rect.x1 -= rd.x1;
1197 rect.y0 += rd.y0;
1198 rect.y1 -= rd.y1;
1199 }
1200 fz_always(ctx)
1201 pdf_annot_pop_local_xref(ctx, annot);
1202 fz_catch(ctx)
1203 fz_rethrow(ctx);
1204
1205 return fz_transform_rect(rect, page_ctm);
1206 }
1207
1208 void
1209 pdf_set_annot_rect(fz_context *ctx, pdf_annot *annot, fz_rect rect)
1210 {
1211 fz_matrix page_ctm, inv_page_ctm;
1212 fz_rect rd;
1213
1214 begin_annot_op(ctx, annot, "Set rectangle");
1215
1216 fz_try(ctx)
1217 {
1218 check_allowed_subtypes(ctx, annot, PDF_NAME(Rect), rect_subtypes);
1219
1220 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1221 inv_page_ctm = fz_invert_matrix(page_ctm);
1222 rect = fz_transform_rect(rect, inv_page_ctm);
1223
1224 /* add RD adjustment to design box to get bounding box */
1225 rd = pdf_annot_rect_diff(ctx, annot);
1226 rect.x0 -= rd.x0;
1227 rect.x1 += rd.x1;
1228 rect.y0 -= rd.y0;
1229 rect.y1 += rd.y1;
1230
1231 pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(Rect), rect);
1232
1233 /* recalculate callout line as necessary */
1234 if (pdf_annot_has_callout(ctx, annot))
1235 {
1236 pdf_set_annot_callout_point(ctx, annot, pdf_annot_callout_point(ctx, annot));
1237 }
1238
1239 pdf_dirty_annot(ctx, annot);
1240 end_annot_op(ctx, annot);
1241 }
1242 fz_catch(ctx)
1243 {
1244 abandon_annot_op(ctx, annot);
1245 fz_rethrow(ctx);
1246 }
1247 }
1248
1249 const char *
1250 pdf_annot_contents(fz_context *ctx, pdf_annot *annot)
1251 {
1252 return pdf_dict_get_text_string(ctx, annot->obj, PDF_NAME(Contents));
1253 }
1254
1255 void
1256 pdf_set_annot_contents(fz_context *ctx, pdf_annot *annot, const char *text)
1257 {
1258 begin_annot_op(ctx, annot, "Set contents");
1259
1260 fz_try(ctx)
1261 {
1262 pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(Contents), text);
1263 pdf_dict_del(ctx, annot->obj, PDF_NAME(RC)); /* not supported */
1264 pdf_dirty_annot(ctx, annot);
1265 end_annot_op(ctx, annot);
1266 }
1267 fz_catch(ctx)
1268 {
1269 abandon_annot_op(ctx, annot);
1270 fz_rethrow(ctx);
1271 }
1272 }
1273
1274 static pdf_obj *markup_subtypes[] = {
1275 PDF_NAME(Text),
1276 PDF_NAME(FreeText),
1277 PDF_NAME(Line),
1278 PDF_NAME(Square),
1279 PDF_NAME(Circle),
1280 PDF_NAME(Polygon),
1281 PDF_NAME(PolyLine),
1282 PDF_NAME(Highlight),
1283 PDF_NAME(Underline),
1284 PDF_NAME(Squiggly),
1285 PDF_NAME(StrikeOut),
1286 PDF_NAME(Redact),
1287 PDF_NAME(Stamp),
1288 PDF_NAME(Caret),
1289 PDF_NAME(Ink),
1290 PDF_NAME(FileAttachment),
1291 PDF_NAME(Sound),
1292 NULL,
1293 };
1294
1295 int
1296 pdf_annot_has_rich_contents(fz_context *ctx, pdf_annot *annot)
1297 {
1298 return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(RC), markup_subtypes);
1299 }
1300
1301 const char *
1302 pdf_annot_rich_contents(fz_context *ctx, pdf_annot *annot)
1303 {
1304 check_allowed_subtypes(ctx, annot, PDF_NAME(RC), markup_subtypes);
1305 return pdf_dict_get_text_string(ctx, annot->obj, PDF_NAME(RC));
1306 }
1307
1308 void
1309 pdf_set_annot_rich_contents(fz_context *ctx, pdf_annot *annot, const char *plain, const char *rich)
1310 {
1311 begin_annot_op(ctx, annot, "Set rich contents");
1312 fz_try(ctx)
1313 {
1314 check_allowed_subtypes(ctx, annot, PDF_NAME(RC), markup_subtypes);
1315 pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(Contents), plain);
1316 pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(RC), rich);
1317 pdf_dirty_annot(ctx, annot);
1318 end_annot_op(ctx, annot);
1319 }
1320 fz_catch(ctx)
1321 {
1322 abandon_annot_op(ctx, annot);
1323 fz_rethrow(ctx);
1324 }
1325 }
1326
1327 static pdf_obj *default_style_subtypes[] = {
1328 PDF_NAME(FreeText),
1329 PDF_NAME(Widget),
1330 NULL,
1331 };
1332
1333 int
1334 pdf_annot_has_rich_defaults(fz_context *ctx, pdf_annot *annot)
1335 {
1336 return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(DS), default_style_subtypes);
1337 }
1338
1339 const char *
1340 pdf_annot_rich_defaults(fz_context *ctx, pdf_annot *annot)
1341 {
1342 check_allowed_subtypes(ctx, annot, PDF_NAME(DS), default_style_subtypes);
1343 return pdf_dict_get_text_string(ctx, annot->obj, PDF_NAME(DS));
1344 }
1345
1346 void
1347 pdf_set_annot_rich_defaults(fz_context *ctx, pdf_annot *annot, const char *style)
1348 {
1349 begin_annot_op(ctx, annot, "Set rich defaults");
1350 fz_try(ctx)
1351 {
1352 check_allowed_subtypes(ctx, annot, PDF_NAME(DS), default_style_subtypes);
1353 pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(DS), style);
1354 pdf_dirty_annot(ctx, annot);
1355 end_annot_op(ctx, annot);
1356 }
1357 fz_catch(ctx)
1358 {
1359 abandon_annot_op(ctx, annot);
1360 fz_rethrow(ctx);
1361 }
1362 }
1363
1364 int
1365 pdf_annot_has_open(fz_context *ctx, pdf_annot *annot)
1366 {
1367 int ret;
1368
1369 pdf_annot_push_local_xref(ctx, annot);
1370
1371 fz_try(ctx)
1372 {
1373 pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
1374 pdf_obj *popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
1375 ret = (subtype == PDF_NAME(Text) || popup);
1376 }
1377 fz_always(ctx)
1378 pdf_annot_pop_local_xref(ctx, annot);
1379 fz_catch(ctx)
1380 fz_rethrow(ctx);
1381
1382 return ret;
1383 }
1384
1385 int
1386 pdf_annot_is_open(fz_context *ctx, pdf_annot *annot)
1387 {
1388 int ret = 0;
1389
1390 pdf_annot_push_local_xref(ctx, annot);
1391
1392 fz_try(ctx)
1393 {
1394 pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
1395 pdf_obj *popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
1396 if (popup)
1397 ret = pdf_dict_get_bool(ctx, popup, PDF_NAME(Open));
1398 else if (subtype == PDF_NAME(Text))
1399 ret = pdf_dict_get_bool(ctx, annot->obj, PDF_NAME(Open));
1400 }
1401 fz_always(ctx)
1402 pdf_annot_pop_local_xref(ctx, annot);
1403 fz_catch(ctx)
1404 fz_rethrow(ctx);
1405
1406 return ret;
1407 }
1408
1409 void
1410 pdf_set_annot_is_open(fz_context *ctx, pdf_annot *annot, int is_open)
1411 {
1412 begin_annot_op(ctx, annot, is_open ? "Open" : "Close");
1413
1414 fz_try(ctx)
1415 {
1416 pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
1417 pdf_obj *popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
1418 if (popup)
1419 {
1420 pdf_dict_put_bool(ctx, popup, PDF_NAME(Open), is_open);
1421 pdf_dirty_annot(ctx, annot);
1422 }
1423 else if (subtype == PDF_NAME(Text))
1424 {
1425 pdf_dict_put_bool(ctx, annot->obj, PDF_NAME(Open), is_open);
1426 pdf_dirty_annot(ctx, annot);
1427 }
1428 end_annot_op(ctx, annot);
1429 }
1430 fz_catch(ctx)
1431 {
1432 abandon_annot_op(ctx, annot);
1433 fz_rethrow(ctx);
1434 }
1435 }
1436
1437 static pdf_obj *icon_name_subtypes[] = {
1438 PDF_NAME(FileAttachment),
1439 PDF_NAME(Sound),
1440 PDF_NAME(Stamp),
1441 PDF_NAME(Text),
1442 NULL,
1443 };
1444
1445 int
1446 pdf_annot_has_icon_name(fz_context *ctx, pdf_annot *annot)
1447 {
1448 return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(Name), icon_name_subtypes);
1449 }
1450
1451 const char *
1452 pdf_annot_icon_name(fz_context *ctx, pdf_annot *annot)
1453 {
1454 const char *ret;
1455 pdf_obj *name;
1456
1457 pdf_annot_push_local_xref(ctx, annot);
1458
1459 fz_try(ctx)
1460 {
1461 check_allowed_subtypes(ctx, annot, PDF_NAME(Name), icon_name_subtypes);
1462 name = pdf_dict_get(ctx, annot->obj, PDF_NAME(Name));
1463 if (!name)
1464 {
1465 pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
1466 if (pdf_name_eq(ctx, subtype, PDF_NAME(Text)))
1467 {
1468 ret = "Note";
1469 break;
1470 }
1471 if (pdf_name_eq(ctx, subtype, PDF_NAME(Stamp)))
1472 {
1473 ret = ""; // Should be "Draft" according to spec
1474 break;
1475 }
1476 if (pdf_name_eq(ctx, subtype, PDF_NAME(FileAttachment)))
1477 {
1478 ret = "PushPin";
1479 break;
1480 }
1481 if (pdf_name_eq(ctx, subtype, PDF_NAME(Sound)))
1482 {
1483 ret = "Speaker";
1484 break;
1485 }
1486 }
1487 ret = pdf_to_name(ctx, name);
1488 }
1489 fz_always(ctx)
1490 pdf_annot_pop_local_xref(ctx, annot);
1491 fz_catch(ctx)
1492 fz_rethrow(ctx);
1493
1494 return ret;
1495 }
1496
1497 void
1498 pdf_set_annot_icon_name(fz_context *ctx, pdf_annot *annot, const char *name)
1499 {
1500 begin_annot_op(ctx, annot, "Set icon name");
1501
1502 fz_try(ctx)
1503 {
1504 check_allowed_subtypes(ctx, annot, PDF_NAME(Name), icon_name_subtypes);
1505 if (name)
1506 pdf_dict_put_name(ctx, annot->obj, PDF_NAME(Name), name);
1507 else
1508 pdf_dict_del(ctx, annot->obj, PDF_NAME(Name));
1509 end_annot_op(ctx, annot);
1510 }
1511 fz_catch(ctx)
1512 {
1513 abandon_annot_op(ctx, annot);
1514 fz_rethrow(ctx);
1515 }
1516
1517 pdf_dirty_annot(ctx, annot);
1518 }
1519
1520 int
1521 pdf_annot_is_standard_stamp(fz_context *ctx, pdf_annot *annot)
1522 {
1523 pdf_obj *name = pdf_dict_get(ctx, annot->obj, PDF_NAME(Name));
1524 if (pdf_name_eq(ctx, name, PDF_NAME(Approved))) return 1;
1525 else if (pdf_name_eq(ctx, name, PDF_NAME(AsIs))) return 1;
1526 else if (pdf_name_eq(ctx, name, PDF_NAME(Confidential))) return 1;
1527 else if (pdf_name_eq(ctx, name, PDF_NAME(Departmental))) return 1;
1528 else if (pdf_name_eq(ctx, name, PDF_NAME(Draft))) return 1;
1529 else if (pdf_name_eq(ctx, name, PDF_NAME(Experimental))) return 1;
1530 else if (pdf_name_eq(ctx, name, PDF_NAME(Expired))) return 1;
1531 else if (pdf_name_eq(ctx, name, PDF_NAME(Final))) return 1;
1532 else if (pdf_name_eq(ctx, name, PDF_NAME(ForComment))) return 1;
1533 else if (pdf_name_eq(ctx, name, PDF_NAME(ForPublicRelease))) return 1;
1534 else if (pdf_name_eq(ctx, name, PDF_NAME(NotApproved))) return 1;
1535 else if (pdf_name_eq(ctx, name, PDF_NAME(NotForPublicRelease))) return 1;
1536 else if (pdf_name_eq(ctx, name, PDF_NAME(Sold))) return 1;
1537 else if (pdf_name_eq(ctx, name, PDF_NAME(TopSecret))) return 1;
1538 else if (pdf_annot_stamp_image_obj(ctx, annot) != NULL) return 1;
1539 else return 0;
1540 }
1541
1542 enum pdf_line_ending pdf_line_ending_from_name(fz_context *ctx, pdf_obj *end)
1543 {
1544 if (pdf_name_eq(ctx, end, PDF_NAME(None))) return PDF_ANNOT_LE_NONE;
1545 else if (pdf_name_eq(ctx, end, PDF_NAME(Square))) return PDF_ANNOT_LE_SQUARE;
1546 else if (pdf_name_eq(ctx, end, PDF_NAME(Circle))) return PDF_ANNOT_LE_CIRCLE;
1547 else if (pdf_name_eq(ctx, end, PDF_NAME(Diamond))) return PDF_ANNOT_LE_DIAMOND;
1548 else if (pdf_name_eq(ctx, end, PDF_NAME(OpenArrow))) return PDF_ANNOT_LE_OPEN_ARROW;
1549 else if (pdf_name_eq(ctx, end, PDF_NAME(ClosedArrow))) return PDF_ANNOT_LE_CLOSED_ARROW;
1550 else if (pdf_name_eq(ctx, end, PDF_NAME(Butt))) return PDF_ANNOT_LE_BUTT;
1551 else if (pdf_name_eq(ctx, end, PDF_NAME(ROpenArrow))) return PDF_ANNOT_LE_R_OPEN_ARROW;
1552 else if (pdf_name_eq(ctx, end, PDF_NAME(RClosedArrow))) return PDF_ANNOT_LE_R_CLOSED_ARROW;
1553 else if (pdf_name_eq(ctx, end, PDF_NAME(Slash))) return PDF_ANNOT_LE_SLASH;
1554 else return PDF_ANNOT_LE_NONE;
1555 }
1556
1557 enum pdf_line_ending pdf_line_ending_from_string(fz_context *ctx, const char *end)
1558 {
1559 if (!strcmp(end, "None")) return PDF_ANNOT_LE_NONE;
1560 else if (!strcmp(end, "Square")) return PDF_ANNOT_LE_SQUARE;
1561 else if (!strcmp(end, "Circle")) return PDF_ANNOT_LE_CIRCLE;
1562 else if (!strcmp(end, "Diamond")) return PDF_ANNOT_LE_DIAMOND;
1563 else if (!strcmp(end, "OpenArrow")) return PDF_ANNOT_LE_OPEN_ARROW;
1564 else if (!strcmp(end, "ClosedArrow")) return PDF_ANNOT_LE_CLOSED_ARROW;
1565 else if (!strcmp(end, "Butt")) return PDF_ANNOT_LE_BUTT;
1566 else if (!strcmp(end, "ROpenArrow")) return PDF_ANNOT_LE_R_OPEN_ARROW;
1567 else if (!strcmp(end, "RClosedArrow")) return PDF_ANNOT_LE_R_CLOSED_ARROW;
1568 else if (!strcmp(end, "Slash")) return PDF_ANNOT_LE_SLASH;
1569 else return PDF_ANNOT_LE_NONE;
1570 }
1571
1572 pdf_obj *pdf_name_from_line_ending(fz_context *ctx, enum pdf_line_ending end)
1573 {
1574 switch (end)
1575 {
1576 default:
1577 case PDF_ANNOT_LE_NONE: return PDF_NAME(None);
1578 case PDF_ANNOT_LE_SQUARE: return PDF_NAME(Square);
1579 case PDF_ANNOT_LE_CIRCLE: return PDF_NAME(Circle);
1580 case PDF_ANNOT_LE_DIAMOND: return PDF_NAME(Diamond);
1581 case PDF_ANNOT_LE_OPEN_ARROW: return PDF_NAME(OpenArrow);
1582 case PDF_ANNOT_LE_CLOSED_ARROW: return PDF_NAME(ClosedArrow);
1583 case PDF_ANNOT_LE_BUTT: return PDF_NAME(Butt);
1584 case PDF_ANNOT_LE_R_OPEN_ARROW: return PDF_NAME(ROpenArrow);
1585 case PDF_ANNOT_LE_R_CLOSED_ARROW: return PDF_NAME(RClosedArrow);
1586 case PDF_ANNOT_LE_SLASH: return PDF_NAME(Slash);
1587 }
1588 }
1589
1590 const char *pdf_string_from_line_ending(fz_context *ctx, enum pdf_line_ending end)
1591 {
1592 switch (end)
1593 {
1594 default:
1595 case PDF_ANNOT_LE_NONE: return "None";
1596 case PDF_ANNOT_LE_SQUARE: return "Square";
1597 case PDF_ANNOT_LE_CIRCLE: return "Circle";
1598 case PDF_ANNOT_LE_DIAMOND: return "Diamond";
1599 case PDF_ANNOT_LE_OPEN_ARROW: return "OpenArrow";
1600 case PDF_ANNOT_LE_CLOSED_ARROW: return "ClosedArrow";
1601 case PDF_ANNOT_LE_BUTT: return "Butt";
1602 case PDF_ANNOT_LE_R_OPEN_ARROW: return "ROpenArrow";
1603 case PDF_ANNOT_LE_R_CLOSED_ARROW: return "RClosedArrow";
1604 case PDF_ANNOT_LE_SLASH: return "Slash";
1605 }
1606 }
1607
1608 static pdf_obj *line_ending_subtypes[] = {
1609 PDF_NAME(FreeText),
1610 PDF_NAME(Line),
1611 PDF_NAME(PolyLine),
1612 PDF_NAME(Polygon),
1613 NULL,
1614 };
1615
1616 int
1617 pdf_annot_has_line_ending_styles(fz_context *ctx, pdf_annot *annot)
1618 {
1619 return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(LE), line_ending_subtypes);
1620 }
1621
1622 void
1623 pdf_annot_line_ending_styles(fz_context *ctx, pdf_annot *annot,
1624 enum pdf_line_ending *start_style,
1625 enum pdf_line_ending *end_style)
1626 {
1627 pdf_obj *style;
1628
1629 pdf_annot_push_local_xref(ctx, annot);
1630
1631 fz_try(ctx)
1632 {
1633 check_allowed_subtypes(ctx, annot, PDF_NAME(LE), line_ending_subtypes);
1634
1635 style = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE));
1636 *start_style = pdf_line_ending_from_name(ctx, pdf_array_get(ctx, style, 0));
1637 *end_style = pdf_line_ending_from_name(ctx, pdf_array_get(ctx, style, 1));
1638 }
1639 fz_always(ctx)
1640 pdf_annot_pop_local_xref(ctx, annot);
1641 fz_catch(ctx)
1642 fz_rethrow(ctx);
1643 }
1644
1645 enum pdf_line_ending
1646 pdf_annot_line_start_style(fz_context *ctx, pdf_annot *annot)
1647 {
1648 pdf_obj *le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE));
1649 return pdf_line_ending_from_name(ctx, pdf_array_get(ctx, le, 0));
1650 }
1651
1652 enum pdf_line_ending
1653 pdf_annot_line_end_style(fz_context *ctx, pdf_annot *annot)
1654 {
1655 pdf_obj *le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE));
1656 return pdf_line_ending_from_name(ctx, pdf_array_get(ctx, le, 1));
1657 }
1658
1659 void
1660 pdf_set_annot_line_ending_styles(fz_context *ctx, pdf_annot *annot,
1661 enum pdf_line_ending start_style,
1662 enum pdf_line_ending end_style)
1663 {
1664 pdf_obj *style;
1665
1666 begin_annot_op(ctx, annot, "Set line endings");
1667
1668 fz_try(ctx)
1669 {
1670 check_allowed_subtypes(ctx, annot, PDF_NAME(LE), line_ending_subtypes);
1671 style = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(LE), 2);
1672 pdf_array_put_drop(ctx, style, 0, pdf_name_from_line_ending(ctx, start_style));
1673 pdf_array_put_drop(ctx, style, 1, pdf_name_from_line_ending(ctx, end_style));
1674 end_annot_op(ctx, annot);
1675 }
1676 fz_catch(ctx)
1677 {
1678 abandon_annot_op(ctx, annot);
1679 fz_rethrow(ctx);
1680 }
1681
1682 pdf_dirty_annot(ctx, annot);
1683 }
1684
1685 void
1686 pdf_set_annot_line_start_style(fz_context *ctx, pdf_annot *annot, enum pdf_line_ending s)
1687 {
1688 enum pdf_line_ending e = pdf_annot_line_end_style(ctx, annot);
1689 pdf_set_annot_line_ending_styles(ctx, annot, s, e);
1690 }
1691
1692 void
1693 pdf_set_annot_line_end_style(fz_context *ctx, pdf_annot *annot, enum pdf_line_ending e)
1694 {
1695 enum pdf_line_ending s = pdf_annot_line_start_style(ctx, annot);
1696 pdf_set_annot_line_ending_styles(ctx, annot, s, e);
1697 }
1698
1699 static pdf_obj *border_style_subtypes[] = {
1700 PDF_NAME(Circle),
1701 PDF_NAME(FreeText),
1702 PDF_NAME(Ink),
1703 PDF_NAME(Line),
1704 PDF_NAME(Polygon),
1705 PDF_NAME(PolyLine),
1706 PDF_NAME(Square),
1707 PDF_NAME(Widget),
1708 NULL,
1709 };
1710
1711 static pdf_obj *border_effect_subtypes[] = {
1712 PDF_NAME(Circle),
1713 PDF_NAME(FreeText),
1714 PDF_NAME(Polygon),
1715 PDF_NAME(Square),
1716 NULL,
1717 };
1718
1719 int
1720 pdf_annot_has_border(fz_context *ctx, pdf_annot *annot)
1721 {
1722 return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(BS), border_style_subtypes);
1723 }
1724
1725 int
1726 pdf_annot_has_border_effect(fz_context *ctx, pdf_annot *annot)
1727 {
1728 return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(BE), border_effect_subtypes);
1729 }
1730
1731 enum pdf_border_style
1732 pdf_annot_border_style(fz_context *ctx, pdf_annot *annot)
1733 {
1734 pdf_obj *bs, *s;
1735 enum pdf_border_style style;
1736
1737 pdf_annot_push_local_xref(ctx, annot);
1738
1739 fz_try(ctx)
1740 {
1741 check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes);
1742 bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS));
1743 s = pdf_dict_get(ctx, bs, PDF_NAME(S));
1744
1745 if (s == PDF_NAME(D))
1746 style = PDF_BORDER_STYLE_DASHED;
1747 else if (s == PDF_NAME(B))
1748 style = PDF_BORDER_STYLE_BEVELED;
1749 else if (s == PDF_NAME(I))
1750 style = PDF_BORDER_STYLE_INSET;
1751 else if (s == PDF_NAME(U))
1752 style = PDF_BORDER_STYLE_UNDERLINE;
1753 else
1754 style = PDF_BORDER_STYLE_SOLID;
1755 }
1756 fz_always(ctx)
1757 pdf_annot_pop_local_xref(ctx, annot);
1758 fz_catch(ctx)
1759 fz_rethrow(ctx);
1760
1761 return style;
1762 }
1763
1764 float
1765 pdf_annot_border_width(fz_context *ctx, pdf_annot *annot)
1766 {
1767 pdf_obj *bs, *bs_w, *border;
1768 float ret = 1;
1769
1770 pdf_annot_push_local_xref(ctx, annot);
1771
1772 fz_try(ctx)
1773 {
1774 check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes);
1775
1776 bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS));
1777 bs_w = pdf_dict_get(ctx, bs, PDF_NAME(W));
1778 if (pdf_is_number(ctx, bs_w))
1779 {
1780 ret = pdf_to_real(ctx, bs_w);
1781 break;
1782 }
1783 border = pdf_dict_get(ctx, annot->obj, PDF_NAME(Border));
1784 bs_w = pdf_array_get(ctx, border, 2);
1785 if (pdf_is_number(ctx, bs_w))
1786 ret = pdf_to_real(ctx, bs_w);
1787 }
1788 fz_always(ctx)
1789 pdf_annot_pop_local_xref(ctx, annot);
1790 fz_catch(ctx)
1791 fz_rethrow(ctx);
1792
1793 return ret;
1794 }
1795
1796 float
1797 pdf_annot_border(fz_context *ctx, pdf_annot *annot)
1798 {
1799 /* DEPRECATED */
1800 return pdf_annot_border_width(ctx, annot);
1801 }
1802
1803 int
1804 pdf_annot_border_dash_count(fz_context *ctx, pdf_annot *annot)
1805 {
1806 pdf_obj *bs, *d, *border;
1807 int count;
1808
1809 pdf_annot_push_local_xref(ctx, annot);
1810
1811 fz_try(ctx)
1812 {
1813 check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes);
1814 bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS));
1815 d = pdf_dict_get(ctx, bs, PDF_NAME(D));
1816 /* Query legacy dash pattern as a fallback */
1817 border = pdf_dict_get(ctx, annot->obj, PDF_NAME(Border));
1818 if (!pdf_is_array(ctx, d) && pdf_is_array(ctx, border))
1819 d = pdf_array_get(ctx, border, 3);
1820 count = pdf_array_len(ctx, d);
1821 }
1822 fz_always(ctx)
1823 pdf_annot_pop_local_xref(ctx, annot);
1824 fz_catch(ctx)
1825 fz_rethrow(ctx);
1826
1827 return count;
1828 }
1829
1830 float
1831 pdf_annot_border_dash_item(fz_context *ctx, pdf_annot *annot, int i)
1832 {
1833 pdf_obj *bs, *d, *border;
1834 float length;
1835
1836 pdf_annot_push_local_xref(ctx, annot);
1837
1838 fz_try(ctx)
1839 {
1840 check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes);
1841 bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS));
1842 d = pdf_dict_get(ctx, bs, PDF_NAME(D));
1843 /* Query legacy dash pattern as a fallback */
1844 border = pdf_dict_get(ctx, annot->obj, PDF_NAME(Border));
1845 if (!pdf_is_array(ctx, d) && pdf_is_array(ctx, border))
1846 d = pdf_array_get(ctx, border, 3);
1847 length = pdf_array_get_real(ctx, d, i);
1848 }
1849 fz_always(ctx)
1850 pdf_annot_pop_local_xref(ctx, annot);
1851 fz_catch(ctx)
1852 fz_rethrow(ctx);
1853
1854 return length;
1855 }
1856
1857 enum pdf_border_effect
1858 pdf_annot_border_effect(fz_context *ctx, pdf_annot *annot)
1859 {
1860 pdf_obj *be;
1861 enum pdf_border_effect effect;
1862
1863 pdf_annot_push_local_xref(ctx, annot);
1864
1865 fz_try(ctx)
1866 {
1867 check_allowed_subtypes(ctx, annot, PDF_NAME(BE), border_effect_subtypes);
1868 be = pdf_dict_get(ctx, annot->obj, PDF_NAME(BE));
1869 if (pdf_dict_get(ctx, be, PDF_NAME(S)) == PDF_NAME(C))
1870 effect = PDF_BORDER_EFFECT_CLOUDY;
1871 else
1872 effect = PDF_BORDER_EFFECT_NONE;
1873 }
1874 fz_always(ctx)
1875 pdf_annot_pop_local_xref(ctx, annot);
1876 fz_catch(ctx)
1877 fz_rethrow(ctx);
1878
1879 return effect;
1880 }
1881
1882 float
1883 pdf_annot_border_effect_intensity(fz_context *ctx, pdf_annot *annot)
1884 {
1885 pdf_obj *be;
1886 float intensity;
1887
1888 pdf_annot_push_local_xref(ctx, annot);
1889
1890 fz_try(ctx)
1891 {
1892 check_allowed_subtypes(ctx, annot, PDF_NAME(BE), border_effect_subtypes);
1893 be = pdf_dict_get(ctx, annot->obj, PDF_NAME(BE));
1894 intensity = pdf_dict_get_real(ctx, be, PDF_NAME(I));
1895 }
1896 fz_always(ctx)
1897 pdf_annot_pop_local_xref(ctx, annot);
1898 fz_catch(ctx)
1899 fz_rethrow(ctx);
1900
1901 return intensity;
1902 }
1903
1904 void
1905 pdf_set_annot_border_width(fz_context *ctx, pdf_annot *annot, float width)
1906 {
1907 pdf_obj *bs;
1908 pdf_obj *type;
1909 float old_width, adj;
1910 pdf_obj *rectobj;
1911
1912 begin_annot_op(ctx, annot, "Set border width");
1913
1914 fz_try(ctx)
1915 {
1916 check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes);
1917 bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS));
1918 if (!pdf_is_dict(ctx, bs))
1919 bs = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(BS), 1);
1920 pdf_dict_put(ctx, bs, PDF_NAME(Type), PDF_NAME(Border));
1921 old_width = pdf_dict_get_real(ctx, bs, PDF_NAME(W));
1922 pdf_dict_put_real(ctx, bs, PDF_NAME(W), width);
1923 rectobj = pdf_dict_get(ctx, annot->obj, PDF_NAME(Rect));
1924 if (pdf_is_array(ctx, rectobj)) {
1925 fz_rect rect = pdf_to_rect(ctx, rectobj);
1926 adj = (width - old_width)/2;
1927 rect.x0 -= adj;
1928 rect.x1 += adj;
1929 rect.y0 -= adj;
1930 rect.y1 += adj;
1931 pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(Rect), rect);
1932 /* For any of these types, we want to adjust the Rect and RD
1933 * together so that Rect+RD doesn't change, but the border stroke
1934 * stays centred on the same point. */
1935 type = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
1936 if (pdf_name_eq(ctx, type, PDF_NAME(Square)) ||
1937 pdf_name_eq(ctx, type, PDF_NAME(Circle)))
1938 {
1939 fz_rect rd = pdf_annot_rect_diff(ctx, annot);
1940 rd.x0 += adj;
1941 rd.x1 += adj;
1942 rd.y0 += adj;
1943 rd.y1 += adj;
1944 pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), rd);
1945 }
1946 }
1947 pdf_dict_del(ctx, annot->obj, PDF_NAME(Border)); /* deprecated */
1948 end_annot_op(ctx, annot);
1949 }
1950 fz_catch(ctx)
1951 {
1952 abandon_annot_op(ctx, annot);
1953 fz_rethrow(ctx);
1954 }
1955
1956 pdf_dirty_annot(ctx, annot);
1957 }
1958
1959 void
1960 pdf_set_annot_border(fz_context *ctx, pdf_annot *annot, float w)
1961 {
1962 /* DEPRECATED */
1963 pdf_set_annot_border_width(ctx, annot, w);
1964 }
1965
1966 void
1967 pdf_set_annot_border_style(fz_context *ctx, pdf_annot *annot, enum pdf_border_style style)
1968 {
1969 pdf_obj *bs, *s;
1970
1971 begin_annot_op(ctx, annot, "Set border style");
1972
1973 fz_try(ctx)
1974 {
1975 check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes);
1976 bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS));
1977 if (!pdf_is_dict(ctx, bs))
1978 bs = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(BS), 1);
1979 pdf_dict_put(ctx, bs, PDF_NAME(Type), PDF_NAME(Border));
1980 switch (style)
1981 {
1982 default:
1983 case PDF_BORDER_STYLE_SOLID: s = PDF_NAME(S); break;
1984 case PDF_BORDER_STYLE_DASHED: s = PDF_NAME(D); break;
1985 case PDF_BORDER_STYLE_BEVELED: s = PDF_NAME(B); break;
1986 case PDF_BORDER_STYLE_INSET: s = PDF_NAME(I); break;
1987 case PDF_BORDER_STYLE_UNDERLINE: s = PDF_NAME(U); break;
1988 }
1989 pdf_dict_put(ctx, bs, PDF_NAME(S), s);
1990 end_annot_op(ctx, annot);
1991 }
1992 fz_catch(ctx)
1993 {
1994 abandon_annot_op(ctx, annot);
1995 fz_rethrow(ctx);
1996 }
1997
1998 pdf_dirty_annot(ctx, annot);
1999 }
2000
2001 void
2002 pdf_clear_annot_border_dash(fz_context *ctx, pdf_annot *annot)
2003 {
2004 pdf_obj *bs, *border;
2005
2006 begin_annot_op(ctx, annot, "Clear border dash pattern");
2007
2008 fz_try(ctx)
2009 {
2010 check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes);
2011 bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS));
2012 if (!pdf_is_dict(ctx, bs))
2013 bs = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(BS), 1);
2014 pdf_dict_del(ctx, bs, PDF_NAME(D));
2015 /* Remove legacy dash pattern */
2016 border = pdf_dict_get(ctx, annot->obj, PDF_NAME(Border));
2017 if (pdf_is_array(ctx, border))
2018 pdf_array_delete(ctx, border, 3);
2019 end_annot_op(ctx, annot);
2020 }
2021 fz_catch(ctx)
2022 {
2023 abandon_annot_op(ctx, annot);
2024 fz_rethrow(ctx);
2025 }
2026
2027 pdf_dirty_annot(ctx, annot);
2028 }
2029
2030 void
2031 pdf_add_annot_border_dash_item(fz_context *ctx, pdf_annot *annot, float length)
2032 {
2033 pdf_obj *bs, *d, *border;
2034
2035 begin_annot_op(ctx, annot, "Add border dash pattern item");
2036
2037 fz_try(ctx)
2038 {
2039 check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes);
2040 bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS));
2041 if (!pdf_is_dict(ctx, bs))
2042 bs = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(BS), 1);
2043 d = pdf_dict_get(ctx, bs, PDF_NAME(D));
2044 if (!pdf_is_array(ctx, d))
2045 d = pdf_dict_put_array(ctx, bs, PDF_NAME(D), 1);
2046 pdf_array_push_real(ctx, d, length);
2047 /* Remove legacy dash pattern */
2048 border = pdf_dict_get(ctx, annot->obj, PDF_NAME(Border));
2049 if (pdf_is_array(ctx, border))
2050 pdf_array_delete(ctx, border, 3);
2051 end_annot_op(ctx, annot);
2052 }
2053 fz_catch(ctx)
2054 {
2055 abandon_annot_op(ctx, annot);
2056 fz_rethrow(ctx);
2057 }
2058
2059 pdf_dirty_annot(ctx, annot);
2060 }
2061
2062 void
2063 pdf_set_annot_border_effect(fz_context *ctx, pdf_annot *annot, enum pdf_border_effect effect)
2064 {
2065 pdf_obj *be, *s;
2066
2067 begin_annot_op(ctx, annot, "Set border effect");
2068
2069 fz_try(ctx)
2070 {
2071 check_allowed_subtypes(ctx, annot, PDF_NAME(BE), border_effect_subtypes);
2072 be = pdf_dict_get(ctx, annot->obj, PDF_NAME(BE));
2073 if (!pdf_is_dict(ctx, be))
2074 be = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(BE), 1);
2075 switch (effect)
2076 {
2077 default:
2078 case PDF_BORDER_EFFECT_NONE: s = PDF_NAME(S); break;
2079 case PDF_BORDER_EFFECT_CLOUDY: s = PDF_NAME(C); break;
2080 }
2081 pdf_dict_put(ctx, be, PDF_NAME(S), s);
2082 end_annot_op(ctx, annot);
2083 }
2084 fz_catch(ctx)
2085 {
2086 abandon_annot_op(ctx, annot);
2087 fz_rethrow(ctx);
2088 }
2089
2090 pdf_dirty_annot(ctx, annot);
2091 }
2092
2093 void
2094 pdf_set_annot_border_effect_intensity(fz_context *ctx, pdf_annot *annot, float intensity)
2095 {
2096 pdf_obj *be;
2097
2098 begin_annot_op(ctx, annot, "Set border effect intensity");
2099
2100 fz_try(ctx)
2101 {
2102 check_allowed_subtypes(ctx, annot, PDF_NAME(BE), border_effect_subtypes);
2103 be = pdf_dict_get(ctx, annot->obj, PDF_NAME(BE));
2104 if (!pdf_is_dict(ctx, be))
2105 be = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(BE), 1);
2106 pdf_dict_put_real(ctx, be, PDF_NAME(I), intensity);
2107 end_annot_op(ctx, annot);
2108 }
2109 fz_catch(ctx)
2110 {
2111 abandon_annot_op(ctx, annot);
2112 fz_rethrow(ctx);
2113 }
2114
2115 pdf_dirty_annot(ctx, annot);
2116 }
2117
2118 fz_text_language
2119 pdf_document_language(fz_context *ctx, pdf_document *doc)
2120 {
2121 pdf_obj *trailer = pdf_trailer(ctx, doc);
2122 pdf_obj *root = pdf_dict_get(ctx, trailer, PDF_NAME(Root));
2123 pdf_obj *lang = pdf_dict_get(ctx, root, PDF_NAME(Lang));
2124 return fz_text_language_from_string(pdf_to_text_string(ctx, lang));
2125 }
2126
2127 void pdf_set_document_language(fz_context *ctx, pdf_document *doc, fz_text_language lang)
2128 {
2129 pdf_obj *trailer = pdf_trailer(ctx, doc);
2130 pdf_obj *root = pdf_dict_get(ctx, trailer, PDF_NAME(Root));
2131 char buf[8];
2132 if (lang == FZ_LANG_UNSET)
2133 pdf_dict_del(ctx, root, PDF_NAME(Lang));
2134 else
2135 pdf_dict_put_text_string(ctx, root, PDF_NAME(Lang), fz_string_from_text_language(buf, lang));
2136 }
2137
2138 fz_text_language
2139 pdf_annot_language(fz_context *ctx, pdf_annot *annot)
2140 {
2141 fz_text_language ret;
2142
2143 pdf_annot_push_local_xref(ctx, annot);
2144
2145 fz_try(ctx)
2146 {
2147 pdf_obj *lang = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(Lang));
2148 if (lang)
2149 ret = fz_text_language_from_string(pdf_to_str_buf(ctx, lang));
2150 else
2151 ret = pdf_document_language(ctx, annot->page->doc);
2152 }
2153 fz_always(ctx)
2154 pdf_annot_pop_local_xref(ctx, annot);
2155 fz_catch(ctx)
2156 fz_rethrow(ctx);
2157
2158 return ret;
2159 }
2160
2161 void
2162 pdf_set_annot_language(fz_context *ctx, pdf_annot *annot, fz_text_language lang)
2163 {
2164 char buf[8];
2165
2166 begin_annot_op(ctx, annot, "Set language");
2167
2168 fz_try(ctx)
2169 {
2170 if (lang == FZ_LANG_UNSET)
2171 pdf_dict_del(ctx, annot->obj, PDF_NAME(Lang));
2172 else
2173 pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(Lang), fz_string_from_text_language(buf, lang));
2174 end_annot_op(ctx, annot);
2175 }
2176 fz_catch(ctx)
2177 {
2178 abandon_annot_op(ctx, annot);
2179 fz_rethrow(ctx);
2180 }
2181
2182 pdf_dirty_annot(ctx, annot);
2183 }
2184
2185 static pdf_obj *quadding_subtypes[] = {
2186 PDF_NAME(FreeText),
2187 PDF_NAME(Widget),
2188 NULL,
2189 };
2190
2191 int
2192 pdf_annot_has_quadding(fz_context *ctx, pdf_annot *annot)
2193 {
2194 return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(Q), quadding_subtypes);
2195 }
2196
2197 int
2198 pdf_annot_quadding(fz_context *ctx, pdf_annot *annot)
2199 {
2200 int q;
2201
2202 pdf_annot_push_local_xref(ctx, annot);
2203
2204 fz_try(ctx)
2205 {
2206 check_allowed_subtypes(ctx, annot, PDF_NAME(Q), quadding_subtypes);
2207 q = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(Q));
2208 q = (q < 0 || q > 2) ? 0 : q;
2209 }
2210 fz_always(ctx)
2211 pdf_annot_pop_local_xref(ctx, annot);
2212 fz_catch(ctx)
2213 fz_rethrow(ctx);
2214
2215 return q;
2216 }
2217
2218 void
2219 pdf_set_annot_quadding(fz_context *ctx, pdf_annot *annot, int q)
2220 {
2221 q = (q < 0 || q > 2) ? 0 : q;
2222
2223 begin_annot_op(ctx, annot, "Set quadding");
2224
2225 fz_try(ctx)
2226 {
2227 check_allowed_subtypes(ctx, annot, PDF_NAME(Q), quadding_subtypes);
2228 pdf_dict_put_int(ctx, annot->obj, PDF_NAME(Q), q);
2229 end_annot_op(ctx, annot);
2230 }
2231 fz_catch(ctx)
2232 {
2233 abandon_annot_op(ctx, annot);
2234 fz_rethrow(ctx);
2235 }
2236
2237 pdf_dirty_annot(ctx, annot);
2238 }
2239
2240 float pdf_annot_opacity(fz_context *ctx, pdf_annot *annot)
2241 {
2242 float ret;
2243
2244 pdf_annot_push_local_xref(ctx, annot);
2245
2246 fz_try(ctx)
2247 ret = pdf_dict_get_real_default(ctx, annot->obj, PDF_NAME(CA), 1);
2248 fz_always(ctx)
2249 pdf_annot_pop_local_xref(ctx, annot);
2250 fz_catch(ctx)
2251 fz_rethrow(ctx);
2252
2253 return ret;
2254 }
2255
2256 void pdf_set_annot_opacity(fz_context *ctx, pdf_annot *annot, float opacity)
2257 {
2258 begin_annot_op(ctx, annot, "Set opacity");
2259
2260 fz_try(ctx)
2261 {
2262 if (opacity != 1)
2263 pdf_dict_put_real(ctx, annot->obj, PDF_NAME(CA), opacity);
2264 else
2265 pdf_dict_del(ctx, annot->obj, PDF_NAME(CA));
2266 end_annot_op(ctx, annot);
2267 }
2268 fz_catch(ctx)
2269 {
2270 abandon_annot_op(ctx, annot);
2271 fz_rethrow(ctx);
2272 }
2273
2274 pdf_dirty_annot(ctx, annot);
2275 }
2276
2277 static void pdf_annot_color_imp(fz_context *ctx, pdf_obj *arr, int *n, float color[4])
2278 {
2279 switch (pdf_array_len(ctx, arr))
2280 {
2281 case 0:
2282 if (n)
2283 *n = 0;
2284 break;
2285 case 1:
2286 case 2:
2287 if (n)
2288 *n = 1;
2289 if (color)
2290 color[0] = pdf_array_get_real(ctx, arr, 0);
2291 break;
2292 case 3:
2293 if (n)
2294 *n = 3;
2295 if (color)
2296 {
2297 color[0] = pdf_array_get_real(ctx, arr, 0);
2298 color[1] = pdf_array_get_real(ctx, arr, 1);
2299 color[2] = pdf_array_get_real(ctx, arr, 2);
2300 }
2301 break;
2302 case 4:
2303 default:
2304 if (n)
2305 *n = 4;
2306 if (color)
2307 {
2308 color[0] = pdf_array_get_real(ctx, arr, 0);
2309 color[1] = pdf_array_get_real(ctx, arr, 1);
2310 color[2] = pdf_array_get_real(ctx, arr, 2);
2311 color[3] = pdf_array_get_real(ctx, arr, 3);
2312 }
2313 break;
2314 }
2315 }
2316
2317 static int pdf_annot_color_rgb(fz_context *ctx, pdf_obj *arr, float rgb[3])
2318 {
2319 float color[4];
2320 int n;
2321 pdf_annot_color_imp(ctx, arr, &n, color);
2322 if (n == 0)
2323 {
2324 return 0;
2325 }
2326 else if (n == 1)
2327 {
2328 rgb[0] = rgb[1] = rgb[2] = color[0];
2329 }
2330 else if (n == 3)
2331 {
2332 rgb[0] = color[0];
2333 rgb[1] = color[1];
2334 rgb[2] = color[2];
2335 }
2336 else if (n == 4)
2337 {
2338 rgb[0] = 1 - fz_min(1, color[0] + color[3]);
2339 rgb[1] = 1 - fz_min(1, color[1] + color[3]);
2340 rgb[2] = 1 - fz_min(1, color[2] + color[3]);
2341 }
2342 return 1;
2343 }
2344
2345 static void pdf_set_annot_color_imp(fz_context *ctx, pdf_annot *annot, pdf_obj *key, int n, const float *color, pdf_obj **allowed)
2346 {
2347 pdf_obj *arr;
2348
2349 if (allowed)
2350 check_allowed_subtypes(ctx, annot, key, allowed);
2351 if (n != 0 && n != 1 && n != 3 && n != 4)
2352 fz_throw(ctx, FZ_ERROR_ARGUMENT, "color must be 0, 1, 3 or 4 components");
2353 if (!color)
2354 fz_throw(ctx, FZ_ERROR_ARGUMENT, "no color given");
2355
2356 arr = pdf_dict_put_array(ctx, annot->obj, key, n);
2357 fz_try(ctx)
2358 {
2359 switch (n)
2360 {
2361 case 1:
2362 pdf_array_push_real(ctx, arr, color[0]);
2363 break;
2364 case 3:
2365 pdf_array_push_real(ctx, arr, color[0]);
2366 pdf_array_push_real(ctx, arr, color[1]);
2367 pdf_array_push_real(ctx, arr, color[2]);
2368 break;
2369 case 4:
2370 pdf_array_push_real(ctx, arr, color[0]);
2371 pdf_array_push_real(ctx, arr, color[1]);
2372 pdf_array_push_real(ctx, arr, color[2]);
2373 pdf_array_push_real(ctx, arr, color[3]);
2374 break;
2375 }
2376 }
2377 fz_catch(ctx)
2378 fz_rethrow(ctx);
2379
2380 pdf_dirty_annot(ctx, annot);
2381 }
2382
2383 static void
2384 do_pdf_annot_color(fz_context *ctx, pdf_annot *annot, int *n, float color[4], pdf_obj *name)
2385 {
2386 pdf_annot_push_local_xref(ctx, annot);
2387
2388 fz_try(ctx)
2389 {
2390 pdf_obj *c = pdf_dict_get(ctx, annot->obj, name);
2391 pdf_annot_color_imp(ctx, c, n, color);
2392 }
2393 fz_always(ctx)
2394 pdf_annot_pop_local_xref(ctx, annot);
2395 fz_catch(ctx)
2396 fz_rethrow(ctx);
2397 }
2398
2399 void
2400 pdf_annot_color(fz_context *ctx, pdf_annot *annot, int *n, float color[4])
2401 {
2402 do_pdf_annot_color(ctx, annot, n, color, PDF_NAME(C));
2403 }
2404
2405 void
2406 pdf_annot_MK_BG(fz_context *ctx, pdf_annot *annot, int *n, float color[4])
2407 {
2408 pdf_annot_push_local_xref(ctx, annot);
2409
2410 fz_try(ctx)
2411 {
2412 pdf_obj *mk_bg = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BG));
2413 pdf_annot_color_imp(ctx, mk_bg, n, color);
2414 }
2415 fz_always(ctx)
2416 pdf_annot_pop_local_xref(ctx, annot);
2417 fz_catch(ctx)
2418 fz_rethrow(ctx);
2419 }
2420
2421 int
2422 pdf_annot_MK_BG_rgb(fz_context *ctx, pdf_annot *annot, float rgb[3])
2423 {
2424 int ret;
2425
2426 pdf_annot_push_local_xref(ctx, annot);
2427
2428 fz_try(ctx)
2429 {
2430 pdf_obj *mk_bg = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BG));
2431 ret = pdf_annot_color_rgb(ctx, mk_bg, rgb);
2432 }
2433 fz_always(ctx)
2434 pdf_annot_pop_local_xref(ctx, annot);
2435 fz_catch(ctx)
2436 fz_rethrow(ctx);
2437
2438 return ret;
2439 }
2440
2441 void
2442 pdf_annot_MK_BC(fz_context *ctx, pdf_annot *annot, int *n, float color[4])
2443 {
2444 pdf_annot_push_local_xref(ctx, annot);
2445
2446 fz_try(ctx)
2447 {
2448 pdf_obj *mk_bc = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BC));
2449 pdf_annot_color_imp(ctx, mk_bc, n, color);
2450 }
2451 fz_always(ctx)
2452 pdf_annot_pop_local_xref(ctx, annot);
2453 fz_catch(ctx)
2454 fz_rethrow(ctx);
2455 }
2456
2457 int
2458 pdf_annot_MK_BC_rgb(fz_context *ctx, pdf_annot *annot, float rgb[3])
2459 {
2460 int ret;
2461
2462 pdf_annot_push_local_xref(ctx, annot);
2463
2464 fz_try(ctx)
2465 {
2466 pdf_obj *mk_bc = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BC));
2467 ret = pdf_annot_color_rgb(ctx, mk_bc, rgb);
2468 }
2469 fz_always(ctx)
2470 pdf_annot_pop_local_xref(ctx, annot);
2471 fz_catch(ctx)
2472 fz_rethrow(ctx);
2473
2474 return ret;
2475 }
2476
2477 void
2478 pdf_set_annot_color(fz_context *ctx, pdf_annot *annot, int n, const float *color)
2479 {
2480 begin_annot_op(ctx, annot, "Set color");
2481
2482 fz_try(ctx)
2483 {
2484 pdf_set_annot_color_imp(ctx, annot, PDF_NAME(C), n, color, NULL);
2485 end_annot_op(ctx, annot);
2486 }
2487 fz_catch(ctx)
2488 {
2489 abandon_annot_op(ctx, annot);
2490 fz_rethrow(ctx);
2491 }
2492 }
2493
2494 static pdf_obj *interior_color_subtypes[] = {
2495 PDF_NAME(Circle),
2496 PDF_NAME(Line),
2497 PDF_NAME(PolyLine),
2498 PDF_NAME(Polygon),
2499 PDF_NAME(Square),
2500 NULL,
2501 };
2502
2503 int
2504 pdf_annot_has_interior_color(fz_context *ctx, pdf_annot *annot)
2505 {
2506 return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(IC), interior_color_subtypes);
2507 }
2508
2509 void
2510 pdf_annot_interior_color(fz_context *ctx, pdf_annot *annot, int *n, float color[4])
2511 {
2512 check_allowed_subtypes(ctx, annot, PDF_NAME(IC), interior_color_subtypes);
2513 do_pdf_annot_color(ctx, annot, n, color, PDF_NAME(IC));
2514 }
2515
2516 void
2517 pdf_set_annot_interior_color(fz_context *ctx, pdf_annot *annot, int n, const float *color)
2518 {
2519 begin_annot_op(ctx, annot, "Set interior color");
2520
2521 fz_try(ctx)
2522 {
2523 check_allowed_subtypes(ctx, annot, PDF_NAME(IC), interior_color_subtypes);
2524 pdf_set_annot_color_imp(ctx, annot, PDF_NAME(IC), n, color, interior_color_subtypes);
2525 end_annot_op(ctx, annot);
2526 }
2527 fz_catch(ctx)
2528 {
2529 abandon_annot_op(ctx, annot);
2530 fz_rethrow(ctx);
2531 }
2532 }
2533
2534 static pdf_obj *line_subtypes[] = {
2535 PDF_NAME(Line),
2536 NULL,
2537 };
2538
2539 int
2540 pdf_annot_has_line(fz_context *ctx, pdf_annot *annot)
2541 {
2542 return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(L), line_subtypes);
2543 }
2544
2545 void
2546 pdf_annot_line(fz_context *ctx, pdf_annot *annot, fz_point *a, fz_point *b)
2547 {
2548 fz_matrix page_ctm;
2549 pdf_obj *line;
2550
2551 pdf_annot_push_local_xref(ctx, annot);
2552
2553 fz_try(ctx)
2554 {
2555 check_allowed_subtypes(ctx, annot, PDF_NAME(L), line_subtypes);
2556
2557 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
2558
2559 line = pdf_dict_get(ctx, annot->obj, PDF_NAME(L));
2560 a->x = pdf_array_get_real(ctx, line, 0);
2561 a->y = pdf_array_get_real(ctx, line, 1);
2562 b->x = pdf_array_get_real(ctx, line, 2);
2563 b->y = pdf_array_get_real(ctx, line, 3);
2564 *a = fz_transform_point(*a, page_ctm);
2565 *b = fz_transform_point(*b, page_ctm);
2566 }
2567 fz_always(ctx)
2568 pdf_annot_pop_local_xref(ctx, annot);
2569 fz_catch(ctx)
2570 fz_rethrow(ctx);
2571 }
2572
2573 void
2574 pdf_set_annot_line(fz_context *ctx, pdf_annot *annot, fz_point a, fz_point b)
2575 {
2576 fz_matrix page_ctm, inv_page_ctm;
2577 pdf_obj *line;
2578
2579 begin_annot_op(ctx, annot, "Set line");
2580
2581 fz_try(ctx)
2582 {
2583 check_allowed_subtypes(ctx, annot, PDF_NAME(L), line_subtypes);
2584
2585 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
2586 inv_page_ctm = fz_invert_matrix(page_ctm);
2587
2588 a = fz_transform_point(a, inv_page_ctm);
2589 b = fz_transform_point(b, inv_page_ctm);
2590
2591 line = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(L), 4);
2592 pdf_array_push_real(ctx, line, a.x);
2593 pdf_array_push_real(ctx, line, a.y);
2594 pdf_array_push_real(ctx, line, b.x);
2595 pdf_array_push_real(ctx, line, b.y);
2596 end_annot_op(ctx, annot);
2597 }
2598 fz_catch(ctx)
2599 {
2600 abandon_annot_op(ctx, annot);
2601 fz_rethrow(ctx);
2602 }
2603
2604 pdf_dirty_annot(ctx, annot);
2605 }
2606
2607 float
2608 pdf_annot_line_leader(fz_context *ctx, pdf_annot *annot)
2609 {
2610 float value;
2611 pdf_annot_push_local_xref(ctx, annot);
2612 fz_try(ctx)
2613 {
2614 check_allowed_subtypes(ctx, annot, PDF_NAME(LL), line_subtypes);
2615 value = pdf_dict_get_real(ctx, annot->obj, PDF_NAME(LL));
2616 }
2617 fz_always(ctx)
2618 pdf_annot_pop_local_xref(ctx, annot);
2619 fz_catch(ctx)
2620 fz_rethrow(ctx);
2621 return value;
2622 }
2623
2624 float
2625 pdf_annot_line_leader_extension(fz_context *ctx, pdf_annot *annot)
2626 {
2627 float value;
2628 pdf_annot_push_local_xref(ctx, annot);
2629 fz_try(ctx)
2630 {
2631 check_allowed_subtypes(ctx, annot, PDF_NAME(LLE), line_subtypes);
2632 value = pdf_dict_get_real(ctx, annot->obj, PDF_NAME(LLE));
2633 }
2634 fz_always(ctx)
2635 pdf_annot_pop_local_xref(ctx, annot);
2636 fz_catch(ctx)
2637 fz_rethrow(ctx);
2638 return value;
2639 }
2640
2641 float
2642 pdf_annot_line_leader_offset(fz_context *ctx, pdf_annot *annot)
2643 {
2644 float value;
2645 pdf_annot_push_local_xref(ctx, annot);
2646 fz_try(ctx)
2647 {
2648 check_allowed_subtypes(ctx, annot, PDF_NAME(LLO), line_subtypes);
2649 value = pdf_dict_get_real(ctx, annot->obj, PDF_NAME(LLO));
2650 }
2651 fz_always(ctx)
2652 pdf_annot_pop_local_xref(ctx, annot);
2653 fz_catch(ctx)
2654 fz_rethrow(ctx);
2655 return value;
2656 }
2657
2658 void
2659 pdf_set_annot_line_leader(fz_context *ctx, pdf_annot *annot, float value)
2660 {
2661 begin_annot_op(ctx, annot, "Set line leader");
2662 fz_try(ctx)
2663 {
2664 check_allowed_subtypes(ctx, annot, PDF_NAME(LL), line_subtypes);
2665 if (value)
2666 pdf_dict_put_real(ctx, annot->obj, PDF_NAME(LL), value);
2667 else
2668 pdf_dict_del(ctx, annot->obj, PDF_NAME(LL));
2669 end_annot_op(ctx, annot);
2670 }
2671 fz_catch(ctx)
2672 {
2673 abandon_annot_op(ctx, annot);
2674 fz_rethrow(ctx);
2675 }
2676 pdf_dirty_annot(ctx, annot);
2677 }
2678
2679 void
2680 pdf_set_annot_line_leader_extension(fz_context *ctx, pdf_annot *annot, float value)
2681 {
2682 begin_annot_op(ctx, annot, "Set line leader_extension");
2683 fz_try(ctx)
2684 {
2685 check_allowed_subtypes(ctx, annot, PDF_NAME(LLE), line_subtypes);
2686 if (value)
2687 pdf_dict_put_real(ctx, annot->obj, PDF_NAME(LLE), value);
2688 else
2689 pdf_dict_del(ctx, annot->obj, PDF_NAME(LLE));
2690 end_annot_op(ctx, annot);
2691 }
2692 fz_catch(ctx)
2693 {
2694 abandon_annot_op(ctx, annot);
2695 fz_rethrow(ctx);
2696 }
2697 pdf_dirty_annot(ctx, annot);
2698 }
2699
2700 void
2701 pdf_set_annot_line_leader_offset(fz_context *ctx, pdf_annot *annot, float value)
2702 {
2703 begin_annot_op(ctx, annot, "Set line leader offset");
2704 fz_try(ctx)
2705 {
2706 check_allowed_subtypes(ctx, annot, PDF_NAME(LLO), line_subtypes);
2707 if (value)
2708 pdf_dict_put_real(ctx, annot->obj, PDF_NAME(LLO), value);
2709 else
2710 pdf_dict_del(ctx, annot->obj, PDF_NAME(LLO));
2711 end_annot_op(ctx, annot);
2712 }
2713 fz_catch(ctx)
2714 {
2715 abandon_annot_op(ctx, annot);
2716 fz_rethrow(ctx);
2717 }
2718 pdf_dirty_annot(ctx, annot);
2719 }
2720
2721 int
2722 pdf_annot_line_caption(fz_context *ctx, pdf_annot *annot)
2723 {
2724 int cap = 0;
2725
2726 pdf_annot_push_local_xref(ctx, annot);
2727
2728 fz_try(ctx)
2729 {
2730 check_allowed_subtypes(ctx, annot, PDF_NAME(Cap), line_subtypes);
2731
2732 cap = pdf_dict_get_bool(ctx, annot->obj, PDF_NAME(Cap));
2733 }
2734 fz_always(ctx)
2735 pdf_annot_pop_local_xref(ctx, annot);
2736 fz_catch(ctx)
2737 fz_rethrow(ctx);
2738
2739 return cap;
2740 }
2741
2742 void
2743 pdf_set_annot_line_caption(fz_context *ctx, pdf_annot *annot, int cap)
2744 {
2745 begin_annot_op(ctx, annot, "Set line caption");
2746
2747 fz_try(ctx)
2748 {
2749 check_allowed_subtypes(ctx, annot, PDF_NAME(Cap), line_subtypes);
2750
2751 pdf_dict_put_bool(ctx, annot->obj, PDF_NAME(Cap), cap);
2752
2753 end_annot_op(ctx, annot);
2754 }
2755 fz_catch(ctx)
2756 {
2757 abandon_annot_op(ctx, annot);
2758 fz_rethrow(ctx);
2759 }
2760
2761 pdf_dirty_annot(ctx, annot);
2762 }
2763
2764 fz_point
2765 pdf_annot_line_caption_offset(fz_context *ctx, pdf_annot *annot)
2766 {
2767 fz_point offset = fz_make_point(0, 0);
2768
2769 pdf_annot_push_local_xref(ctx, annot);
2770
2771 fz_try(ctx)
2772 {
2773 check_allowed_subtypes(ctx, annot, PDF_NAME(CO), line_subtypes);
2774
2775 offset = pdf_dict_get_point(ctx, annot->obj, PDF_NAME(CO));
2776 }
2777 fz_always(ctx)
2778 pdf_annot_pop_local_xref(ctx, annot);
2779 fz_catch(ctx)
2780 fz_rethrow(ctx);
2781
2782 return offset;
2783 }
2784
2785 void
2786 pdf_set_annot_line_caption_offset(fz_context *ctx, pdf_annot *annot, fz_point offset)
2787 {
2788 begin_annot_op(ctx, annot, "Set line caption");
2789
2790 fz_try(ctx)
2791 {
2792 check_allowed_subtypes(ctx, annot, PDF_NAME(CO), line_subtypes);
2793
2794 if (offset.x == 0 && offset.y == 0)
2795 pdf_dict_del(ctx, annot->obj, PDF_NAME(CO));
2796 else
2797 pdf_dict_put_point(ctx, annot->obj, PDF_NAME(CO), offset);
2798
2799 end_annot_op(ctx, annot);
2800 }
2801 fz_catch(ctx)
2802 {
2803 abandon_annot_op(ctx, annot);
2804 fz_rethrow(ctx);
2805 }
2806
2807 pdf_dirty_annot(ctx, annot);
2808 }
2809
2810 static pdf_obj *vertices_subtypes[] = {
2811 PDF_NAME(PolyLine),
2812 PDF_NAME(Polygon),
2813 NULL,
2814 };
2815
2816 int
2817 pdf_annot_has_vertices(fz_context *ctx, pdf_annot *annot)
2818 {
2819 return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
2820 }
2821
2822 int
2823 pdf_annot_vertex_count(fz_context *ctx, pdf_annot *annot)
2824 {
2825 pdf_obj *vertices;
2826 int ret;
2827
2828 pdf_annot_push_local_xref(ctx, annot);
2829
2830 fz_try(ctx)
2831 {
2832 check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
2833 vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices));
2834 ret = pdf_array_len(ctx, vertices) / 2;
2835 }
2836 fz_always(ctx)
2837 pdf_annot_pop_local_xref(ctx, annot);
2838 fz_catch(ctx)
2839 fz_rethrow(ctx);
2840
2841 return ret;
2842 }
2843
2844 fz_point
2845 pdf_annot_vertex(fz_context *ctx, pdf_annot *annot, int i)
2846 {
2847 pdf_obj *vertices;
2848 fz_matrix page_ctm;
2849 fz_point point;
2850
2851 pdf_annot_push_local_xref(ctx, annot);
2852
2853 fz_try(ctx)
2854 {
2855 check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
2856
2857 vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices));
2858
2859 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
2860
2861 point.x = pdf_array_get_real(ctx, vertices, i * 2);
2862 point.y = pdf_array_get_real(ctx, vertices, i * 2 + 1);
2863 }
2864 fz_always(ctx)
2865 pdf_annot_pop_local_xref(ctx, annot);
2866 fz_catch(ctx)
2867 fz_rethrow(ctx);
2868
2869 return fz_transform_point(point, page_ctm);
2870 }
2871
2872 void
2873 pdf_set_annot_vertices(fz_context *ctx, pdf_annot *annot, int n, const fz_point *v)
2874 {
2875 fz_matrix page_ctm, inv_page_ctm;
2876 pdf_obj *vertices;
2877 fz_point point;
2878 int i;
2879
2880 begin_annot_op(ctx, annot, "Set points");
2881
2882 fz_try(ctx)
2883 {
2884 check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
2885 if (n <= 0 || !v)
2886 fz_throw(ctx, FZ_ERROR_ARGUMENT, "invalid number of vertices");
2887
2888 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
2889 inv_page_ctm = fz_invert_matrix(page_ctm);
2890
2891 vertices = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(Vertices), n * 2);
2892 for (i = 0; i < n; ++i)
2893 {
2894 point = fz_transform_point(v[i], inv_page_ctm);
2895 pdf_array_push_real(ctx, vertices, point.x);
2896 pdf_array_push_real(ctx, vertices, point.y);
2897 }
2898 end_annot_op(ctx, annot);
2899 }
2900 fz_catch(ctx)
2901 {
2902 abandon_annot_op(ctx, annot);
2903 fz_rethrow(ctx);
2904 }
2905
2906 pdf_dirty_annot(ctx, annot);
2907 }
2908
2909 void pdf_clear_annot_vertices(fz_context *ctx, pdf_annot *annot)
2910 {
2911 begin_annot_op(ctx, annot, "Clear vertices");
2912
2913 fz_try(ctx)
2914 {
2915 check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
2916 pdf_dict_del(ctx, annot->obj, PDF_NAME(Vertices));
2917 end_annot_op(ctx, annot);
2918 }
2919 fz_catch(ctx)
2920 {
2921 abandon_annot_op(ctx, annot);
2922 fz_rethrow(ctx);
2923 }
2924
2925 pdf_dirty_annot(ctx, annot);
2926 }
2927
2928 void pdf_add_annot_vertex(fz_context *ctx, pdf_annot *annot, fz_point p)
2929 {
2930 fz_matrix page_ctm, inv_page_ctm;
2931 pdf_obj *vertices;
2932
2933 begin_annot_op(ctx, annot, "Add point");
2934
2935 fz_try(ctx)
2936 {
2937 check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
2938
2939 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
2940 inv_page_ctm = fz_invert_matrix(page_ctm);
2941
2942 vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices));
2943 if (!pdf_is_array(ctx, vertices))
2944 vertices = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(Vertices), 32);
2945
2946 p = fz_transform_point(p, inv_page_ctm);
2947 pdf_array_push_real(ctx, vertices, p.x);
2948 pdf_array_push_real(ctx, vertices, p.y);
2949 end_annot_op(ctx, annot);
2950 }
2951 fz_catch(ctx)
2952 {
2953 abandon_annot_op(ctx, annot);
2954 fz_rethrow(ctx);
2955 }
2956
2957 pdf_dirty_annot(ctx, annot);
2958 }
2959
2960 void pdf_set_annot_vertex(fz_context *ctx, pdf_annot *annot, int i, fz_point p)
2961 {
2962 fz_matrix page_ctm, inv_page_ctm;
2963 pdf_obj *vertices;
2964
2965 begin_annot_op(ctx, annot, "Set point");
2966
2967 fz_try(ctx)
2968 {
2969 check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
2970
2971 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
2972 inv_page_ctm = fz_invert_matrix(page_ctm);
2973
2974 p = fz_transform_point(p, inv_page_ctm);
2975
2976 vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices));
2977 pdf_array_put_real(ctx, vertices, i * 2 + 0, p.x);
2978 pdf_array_put_real(ctx, vertices, i * 2 + 1, p.x);
2979 end_annot_op(ctx, annot);
2980 }
2981 fz_catch(ctx)
2982 {
2983 abandon_annot_op(ctx, annot);
2984 fz_rethrow(ctx);
2985 }
2986 }
2987
2988 static pdf_obj *quad_point_subtypes[] = {
2989 PDF_NAME(Highlight),
2990 PDF_NAME(Link),
2991 PDF_NAME(Squiggly),
2992 PDF_NAME(StrikeOut),
2993 PDF_NAME(Underline),
2994 PDF_NAME(Redact),
2995 NULL,
2996 };
2997
2998 int
2999 pdf_annot_has_quad_points(fz_context *ctx, pdf_annot *annot)
3000 {
3001 return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
3002 }
3003
3004 int
3005 pdf_annot_quad_point_count(fz_context *ctx, pdf_annot *annot)
3006 {
3007 pdf_obj *quad_points;
3008 int ret;
3009
3010 pdf_annot_push_local_xref(ctx, annot);
3011
3012 fz_try(ctx)
3013 {
3014 check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
3015 quad_points = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
3016 ret = pdf_array_len(ctx, quad_points) / 8;
3017 }
3018 fz_always(ctx)
3019 pdf_annot_pop_local_xref(ctx, annot);
3020 fz_catch(ctx)
3021 fz_rethrow(ctx);
3022
3023 return ret;
3024 }
3025
3026 fz_quad
3027 pdf_annot_quad_point(fz_context *ctx, pdf_annot *annot, int idx)
3028 {
3029 pdf_obj *quad_points;
3030 fz_matrix page_ctm;
3031 float v[8];
3032 int i;
3033
3034 pdf_annot_push_local_xref(ctx, annot);
3035
3036 fz_try(ctx)
3037 {
3038 check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
3039 quad_points = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
3040 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
3041
3042 for (i = 0; i < 8; i += 2)
3043 {
3044 fz_point point;
3045 point.x = pdf_array_get_real(ctx, quad_points, idx * 8 + i + 0);
3046 point.y = pdf_array_get_real(ctx, quad_points, idx * 8 + i + 1);
3047 point = fz_transform_point(point, page_ctm);
3048 v[i+0] = point.x;
3049 v[i+1] = point.y;
3050 }
3051 }
3052 fz_always(ctx)
3053 pdf_annot_pop_local_xref(ctx, annot);
3054 fz_catch(ctx)
3055 fz_rethrow(ctx);
3056
3057 return fz_make_quad(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
3058 }
3059
3060 void
3061 pdf_set_annot_quad_points(fz_context *ctx, pdf_annot *annot, int n, const fz_quad *q)
3062 {
3063 fz_matrix page_ctm, inv_page_ctm;
3064 pdf_obj *quad_points;
3065 fz_quad quad;
3066 int i;
3067
3068 begin_annot_op(ctx, annot, "Set quad points");
3069
3070 fz_try(ctx)
3071 {
3072 check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
3073 if (n <= 0 || !q)
3074 fz_throw(ctx, FZ_ERROR_ARGUMENT, "invalid number of quadrilaterals");
3075
3076 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
3077 inv_page_ctm = fz_invert_matrix(page_ctm);
3078
3079 quad_points = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(QuadPoints), n);
3080 for (i = 0; i < n; ++i)
3081 {
3082 quad = fz_transform_quad(q[i], inv_page_ctm);
3083 pdf_array_push_real(ctx, quad_points, quad.ul.x);
3084 pdf_array_push_real(ctx, quad_points, quad.ul.y);
3085 pdf_array_push_real(ctx, quad_points, quad.ur.x);
3086 pdf_array_push_real(ctx, quad_points, quad.ur.y);
3087 pdf_array_push_real(ctx, quad_points, quad.ll.x);
3088 pdf_array_push_real(ctx, quad_points, quad.ll.y);
3089 pdf_array_push_real(ctx, quad_points, quad.lr.x);
3090 pdf_array_push_real(ctx, quad_points, quad.lr.y);
3091 }
3092
3093 end_annot_op(ctx, annot);
3094 }
3095 fz_catch(ctx)
3096 {
3097 abandon_annot_op(ctx, annot);
3098 fz_rethrow(ctx);
3099 }
3100
3101 pdf_dirty_annot(ctx, annot);
3102 }
3103
3104 void
3105 pdf_clear_annot_quad_points(fz_context *ctx, pdf_annot *annot)
3106 {
3107 begin_annot_op(ctx, annot, "Clear quad points");
3108
3109 fz_try(ctx)
3110 {
3111 check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
3112 pdf_dict_del(ctx, annot->obj, PDF_NAME(QuadPoints));
3113 end_annot_op(ctx, annot);
3114 }
3115 fz_catch(ctx)
3116 {
3117 abandon_annot_op(ctx, annot);
3118 fz_rethrow(ctx);
3119 }
3120
3121 pdf_dirty_annot(ctx, annot);
3122 }
3123
3124 void
3125 pdf_add_annot_quad_point(fz_context *ctx, pdf_annot *annot, fz_quad quad)
3126 {
3127 fz_matrix page_ctm, inv_page_ctm;
3128 pdf_obj *quad_points;
3129
3130 begin_annot_op(ctx, annot, "Add quad point");
3131
3132 fz_try(ctx)
3133 {
3134 check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
3135
3136 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
3137 inv_page_ctm = fz_invert_matrix(page_ctm);
3138
3139 quad_points = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
3140 if (!pdf_is_array(ctx, quad_points))
3141 quad_points = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(QuadPoints), 8);
3142
3143 /* Contrary to the specification, the points within a QuadPoint are NOT ordered
3144 * in a counterclockwise fashion. Experiments with Adobe's implementation
3145 * indicates a cross-wise ordering is intended: ul, ur, ll, lr.
3146 */
3147 quad = fz_transform_quad(quad, inv_page_ctm);
3148 pdf_array_push_real(ctx, quad_points, quad.ul.x);
3149 pdf_array_push_real(ctx, quad_points, quad.ul.y);
3150 pdf_array_push_real(ctx, quad_points, quad.ur.x);
3151 pdf_array_push_real(ctx, quad_points, quad.ur.y);
3152 pdf_array_push_real(ctx, quad_points, quad.ll.x);
3153 pdf_array_push_real(ctx, quad_points, quad.ll.y);
3154 pdf_array_push_real(ctx, quad_points, quad.lr.x);
3155 pdf_array_push_real(ctx, quad_points, quad.lr.y);
3156 end_annot_op(ctx, annot);
3157 }
3158 fz_catch(ctx)
3159 {
3160 abandon_annot_op(ctx, annot);
3161 fz_rethrow(ctx);
3162 }
3163
3164 pdf_dirty_annot(ctx, annot);
3165 }
3166
3167 static pdf_obj *ink_list_subtypes[] = {
3168 PDF_NAME(Ink),
3169 NULL,
3170 };
3171
3172 int
3173 pdf_annot_has_ink_list(fz_context *ctx, pdf_annot *annot)
3174 {
3175 return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
3176 }
3177
3178 int
3179 pdf_annot_ink_list_count(fz_context *ctx, pdf_annot *annot)
3180 {
3181 int ret;
3182
3183 pdf_annot_push_local_xref(ctx, annot);
3184
3185 fz_try(ctx)
3186 {
3187 pdf_obj *ink_list;
3188 check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
3189 ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
3190 ret = pdf_array_len(ctx, ink_list);
3191 }
3192 fz_always(ctx)
3193 pdf_annot_pop_local_xref(ctx, annot);
3194 fz_catch(ctx)
3195 fz_rethrow(ctx);
3196
3197 return ret;
3198 }
3199
3200 int
3201 pdf_annot_ink_list_stroke_count(fz_context *ctx, pdf_annot *annot, int i)
3202 {
3203 pdf_obj *ink_list;
3204 pdf_obj *stroke;
3205 int ret;
3206
3207 pdf_annot_push_local_xref(ctx, annot);
3208
3209 fz_try(ctx)
3210 {
3211 check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
3212 ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
3213 stroke = pdf_array_get(ctx, ink_list, i);
3214 ret = pdf_array_len(ctx, stroke) / 2;
3215 }
3216 fz_always(ctx)
3217 pdf_annot_pop_local_xref(ctx, annot);
3218 fz_catch(ctx)
3219 fz_rethrow(ctx);
3220
3221 return ret;
3222 }
3223
3224 fz_point
3225 pdf_annot_ink_list_stroke_vertex(fz_context *ctx, pdf_annot *annot, int i, int k)
3226 {
3227 pdf_obj *ink_list;
3228 pdf_obj *stroke;
3229 fz_matrix page_ctm;
3230 fz_point point;
3231
3232 pdf_annot_push_local_xref(ctx, annot);
3233
3234 fz_try(ctx)
3235 {
3236 check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
3237
3238 ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
3239 stroke = pdf_array_get(ctx, ink_list, i);
3240
3241 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
3242
3243 point.x = pdf_array_get_real(ctx, stroke, k * 2 + 0);
3244 point.y = pdf_array_get_real(ctx, stroke, k * 2 + 1);
3245 }
3246 fz_always(ctx)
3247 pdf_annot_pop_local_xref(ctx, annot);
3248 fz_catch(ctx)
3249 fz_rethrow(ctx);
3250
3251 return fz_transform_point(point, page_ctm);
3252 }
3253
3254 /* FIXME: try/catch required for memory exhaustion */
3255 void
3256 pdf_set_annot_ink_list(fz_context *ctx, pdf_annot *annot, int n, const int *count, const fz_point *v)
3257 {
3258 fz_matrix page_ctm, inv_page_ctm;
3259 pdf_obj *ink_list = NULL, *stroke;
3260 fz_point point;
3261 int i, k;
3262
3263 fz_var(ink_list);
3264
3265 begin_annot_op(ctx, annot, "Set ink list");
3266
3267 fz_try(ctx)
3268 {
3269 check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
3270
3271 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
3272 inv_page_ctm = fz_invert_matrix(page_ctm);
3273
3274 ink_list = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(InkList), n);
3275 for (i = 0; i < n; ++i)
3276 {
3277 stroke = pdf_array_push_array(ctx, ink_list, count[i] * 2);
3278
3279 /* Although we have dropped our reference to stroke,
3280 * it's still valid because we ink_list holds one, and
3281 * we hold a reference to that. */
3282 for (k = 0; k < count[i]; ++k)
3283 {
3284 point = fz_transform_point(*v++, inv_page_ctm);
3285 pdf_array_push_real(ctx, stroke, point.x);
3286 pdf_array_push_real(ctx, stroke, point.y);
3287 }
3288 }
3289 end_annot_op(ctx, annot);
3290 }
3291 fz_catch(ctx)
3292 {
3293 abandon_annot_op(ctx, annot);
3294 fz_rethrow(ctx);
3295 }
3296
3297 pdf_dirty_annot(ctx, annot);
3298 }
3299
3300 void
3301 pdf_clear_annot_ink_list(fz_context *ctx, pdf_annot *annot)
3302 {
3303 begin_annot_op(ctx, annot, "Clear ink list");
3304
3305 fz_try(ctx)
3306 {
3307 check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
3308 pdf_dict_del(ctx, annot->obj, PDF_NAME(InkList));
3309 end_annot_op(ctx, annot);
3310 }
3311 fz_catch(ctx)
3312 {
3313 abandon_annot_op(ctx, annot);
3314 fz_rethrow(ctx);
3315 }
3316
3317 pdf_dirty_annot(ctx, annot);
3318 }
3319
3320 void pdf_add_annot_ink_list_stroke(fz_context *ctx, pdf_annot *annot)
3321 {
3322 pdf_obj *ink_list;
3323
3324 begin_annot_op(ctx, annot, "Add ink list stroke");
3325
3326 fz_try(ctx)
3327 {
3328 check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
3329 ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
3330 if (!pdf_is_array(ctx, ink_list))
3331 ink_list = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(InkList), 10);
3332
3333 pdf_array_push_array(ctx, ink_list, 16);
3334 end_annot_op(ctx, annot);
3335 }
3336 fz_catch(ctx)
3337 {
3338 abandon_annot_op(ctx, annot);
3339 fz_rethrow(ctx);
3340 }
3341
3342 pdf_dirty_annot(ctx, annot);
3343 }
3344
3345 void pdf_add_annot_ink_list_stroke_vertex(fz_context *ctx, pdf_annot *annot, fz_point p)
3346 {
3347 fz_matrix page_ctm, inv_page_ctm;
3348 pdf_obj *ink_list, *stroke;
3349
3350 begin_annot_op(ctx, annot, "Add ink list stroke point");
3351
3352 fz_try(ctx)
3353 {
3354 check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
3355 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
3356 inv_page_ctm = fz_invert_matrix(page_ctm);
3357
3358 ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
3359 if (!pdf_is_array(ctx, ink_list))
3360 ink_list = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(InkList), 10);
3361 stroke = pdf_array_get(ctx, ink_list, pdf_array_len(ctx, ink_list)-1);
3362 if (!pdf_is_array(ctx, stroke))
3363 {
3364 int len = pdf_array_len(ctx, ink_list);
3365 stroke = pdf_new_array(ctx, pdf_get_bound_document(ctx, ink_list), 16);
3366 pdf_array_put_drop(ctx, ink_list, len ? len-1 : 0, stroke);
3367 }
3368
3369 p = fz_transform_point(p, inv_page_ctm);
3370 pdf_array_push_real(ctx, stroke, p.x);
3371 pdf_array_push_real(ctx, stroke, p.y);
3372 end_annot_op(ctx, annot);
3373 }
3374 fz_catch(ctx)
3375 {
3376 abandon_annot_op(ctx, annot);
3377 fz_rethrow(ctx);
3378 }
3379
3380 pdf_dirty_annot(ctx, annot);
3381 }
3382
3383 void
3384 pdf_add_annot_ink_list(fz_context *ctx, pdf_annot *annot, int n, fz_point p[])
3385 {
3386 fz_matrix page_ctm, inv_page_ctm;
3387 pdf_obj *ink_list, *stroke;
3388 int i;
3389
3390 begin_annot_op(ctx, annot, "Add ink list");
3391
3392 fz_try(ctx)
3393 {
3394 check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
3395
3396 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
3397 inv_page_ctm = fz_invert_matrix(page_ctm);
3398
3399 ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
3400 if (!pdf_is_array(ctx, ink_list))
3401 ink_list = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(InkList), 10);
3402
3403 stroke = pdf_array_push_array(ctx, ink_list, n * 2);
3404 for (i = 0; i < n; ++i)
3405 {
3406 fz_point tp = fz_transform_point(p[i], inv_page_ctm);
3407 pdf_array_push_real(ctx, stroke, tp.x);
3408 pdf_array_push_real(ctx, stroke, tp.y);
3409 }
3410 end_annot_op(ctx, annot);
3411 }
3412 fz_catch(ctx)
3413 {
3414 abandon_annot_op(ctx, annot);
3415 fz_rethrow(ctx);
3416 }
3417
3418 pdf_dirty_annot(ctx, annot);
3419 }
3420
3421 /*
3422 Get annotation's modification date in seconds since the epoch.
3423 */
3424 int64_t
3425 pdf_annot_modification_date(fz_context *ctx, pdf_annot *annot)
3426 {
3427 int64_t ret;
3428
3429 pdf_annot_push_local_xref(ctx, annot);
3430
3431 fz_try(ctx)
3432 {
3433 ret = pdf_dict_get_date(ctx, annot->obj, PDF_NAME(M));
3434 }
3435 fz_always(ctx)
3436 pdf_annot_pop_local_xref(ctx, annot);
3437 fz_catch(ctx)
3438 fz_rethrow(ctx);
3439
3440 return ret;
3441 }
3442
3443 /*
3444 Get annotation's creation date in seconds since the epoch.
3445 */
3446 int64_t
3447 pdf_annot_creation_date(fz_context *ctx, pdf_annot *annot)
3448 {
3449 int64_t ret;
3450
3451 pdf_annot_push_local_xref(ctx, annot);
3452
3453 fz_try(ctx)
3454 ret = pdf_dict_get_date(ctx, annot->obj, PDF_NAME(CreationDate));
3455 fz_always(ctx)
3456 pdf_annot_pop_local_xref(ctx, annot);
3457 fz_catch(ctx)
3458 fz_rethrow(ctx);
3459
3460 return ret;
3461 }
3462
3463 /*
3464 Set annotation's modification date in seconds since the epoch.
3465 */
3466 void
3467 pdf_set_annot_modification_date(fz_context *ctx, pdf_annot *annot, int64_t secs)
3468 {
3469 begin_annot_op(ctx, annot, "Set modification date");
3470
3471 fz_try(ctx)
3472 {
3473 pdf_dict_put_date(ctx, annot->obj, PDF_NAME(M), secs);
3474 end_annot_op(ctx, annot);
3475 }
3476 fz_catch(ctx)
3477 {
3478 abandon_annot_op(ctx, annot);
3479 fz_rethrow(ctx);
3480 }
3481
3482 pdf_dirty_annot(ctx, annot);
3483 }
3484
3485 /*
3486 Set annotation's creation date in seconds since the epoch.
3487 */
3488 void
3489 pdf_set_annot_creation_date(fz_context *ctx, pdf_annot *annot, int64_t secs)
3490 {
3491 begin_annot_op(ctx, annot, "Set creation date");
3492
3493 fz_try(ctx)
3494 {
3495 check_allowed_subtypes(ctx, annot, PDF_NAME(CreationDate), markup_subtypes);
3496 pdf_dict_put_date(ctx, annot->obj, PDF_NAME(CreationDate), secs);
3497 end_annot_op(ctx, annot);
3498 }
3499 fz_catch(ctx)
3500 {
3501 abandon_annot_op(ctx, annot);
3502 fz_rethrow(ctx);
3503 }
3504
3505 pdf_dirty_annot(ctx, annot);
3506 }
3507
3508 int
3509 pdf_annot_has_author(fz_context *ctx, pdf_annot *annot)
3510 {
3511 return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(T), markup_subtypes);
3512 }
3513
3514 const char *
3515 pdf_annot_author(fz_context *ctx, pdf_annot *annot)
3516 {
3517 const char *ret;
3518
3519 pdf_annot_push_local_xref(ctx, annot);
3520
3521 fz_try(ctx)
3522 {
3523 check_allowed_subtypes(ctx, annot, PDF_NAME(T), markup_subtypes);
3524 ret = pdf_dict_get_text_string(ctx, annot->obj, PDF_NAME(T));
3525 }
3526 fz_always(ctx)
3527 pdf_annot_pop_local_xref(ctx, annot);
3528 fz_catch(ctx)
3529 fz_rethrow(ctx);
3530
3531 return ret;
3532 }
3533
3534 void
3535 pdf_set_annot_author(fz_context *ctx, pdf_annot *annot, const char *author)
3536 {
3537 begin_annot_op(ctx, annot, "Set author");
3538
3539 fz_try(ctx)
3540 {
3541 check_allowed_subtypes(ctx, annot, PDF_NAME(T), markup_subtypes);
3542 pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(T), author);
3543 pdf_dirty_annot(ctx, annot);
3544 end_annot_op(ctx, annot);
3545 }
3546 fz_catch(ctx)
3547 {
3548 abandon_annot_op(ctx, annot);
3549 fz_rethrow(ctx);
3550 }
3551 }
3552
3553 static pdf_obj *intent_subtypes[] = {
3554 PDF_NAME(FreeText),
3555 PDF_NAME(Line),
3556 PDF_NAME(Polygon),
3557 PDF_NAME(PolyLine),
3558 PDF_NAME(Stamp),
3559 NULL,
3560 };
3561
3562 enum pdf_intent pdf_intent_from_name(fz_context *ctx, pdf_obj *it)
3563 {
3564 if (
3565 it == PDF_NULL ||
3566 it == PDF_NAME(FreeText) ||
3567 it == PDF_NAME(Line) ||
3568 it == PDF_NAME(PolyLine) ||
3569 it == PDF_NAME(Polygon) ||
3570 it == PDF_NAME(Stamp)
3571 )
3572 return PDF_ANNOT_IT_DEFAULT;
3573 if (it == PDF_NAME(FreeTextCallout))
3574 return PDF_ANNOT_IT_FREETEXT_CALLOUT;
3575 if (it == PDF_NAME(FreeTextTypeWriter))
3576 return PDF_ANNOT_IT_FREETEXT_TYPEWRITER;
3577 if (it == PDF_NAME(LineArrow))
3578 return PDF_ANNOT_IT_LINE_ARROW;
3579 if (it == PDF_NAME(LineDimension))
3580 return PDF_ANNOT_IT_LINE_DIMENSION;
3581 if (it == PDF_NAME(PolyLineDimension))
3582 return PDF_ANNOT_IT_POLYLINE_DIMENSION;
3583 if (it == PDF_NAME(PolygonCloud))
3584 return PDF_ANNOT_IT_POLYGON_CLOUD;
3585 if (it == PDF_NAME(PolygonDimension))
3586 return PDF_ANNOT_IT_POLYGON_DIMENSION;
3587 if (it == PDF_NAME(StampImage))
3588 return PDF_ANNOT_IT_STAMP_IMAGE;
3589 if (it == PDF_NAME(StampSnapshot))
3590 return PDF_ANNOT_IT_STAMP_SNAPSHOT;
3591 return PDF_ANNOT_IT_UNKNOWN;
3592 }
3593
3594 enum pdf_intent pdf_intent_from_string(fz_context *ctx, const char *it)
3595 {
3596 if (
3597 it == NULL ||
3598 !strcmp(it, "FreeText") ||
3599 !strcmp(it, "Line") ||
3600 !strcmp(it, "PolyLine") ||
3601 !strcmp(it, "Polygon") ||
3602 !strcmp(it, "Stamp")
3603 )
3604 return PDF_ANNOT_IT_DEFAULT;
3605 if (!strcmp(it, "FreeTextCallout"))
3606 return PDF_ANNOT_IT_FREETEXT_CALLOUT;
3607 if (!strcmp(it, "FreeTextTypeWriter"))
3608 return PDF_ANNOT_IT_FREETEXT_TYPEWRITER;
3609 if (!strcmp(it, "LineArrow"))
3610 return PDF_ANNOT_IT_LINE_ARROW;
3611 if (!strcmp(it, "LineDimension"))
3612 return PDF_ANNOT_IT_LINE_DIMENSION;
3613 if (!strcmp(it, "PolyLineDimension"))
3614 return PDF_ANNOT_IT_POLYLINE_DIMENSION;
3615 if (!strcmp(it, "PolygonCloud"))
3616 return PDF_ANNOT_IT_POLYGON_CLOUD;
3617 if (!strcmp(it, "PolygonDimension"))
3618 return PDF_ANNOT_IT_POLYGON_DIMENSION;
3619 if (!strcmp(it, "StampImage"))
3620 return PDF_ANNOT_IT_STAMP_IMAGE;
3621 if (!strcmp(it, "StampSnapshot"))
3622 return PDF_ANNOT_IT_STAMP_SNAPSHOT;
3623 return PDF_ANNOT_IT_UNKNOWN;
3624 }
3625
3626 pdf_obj *pdf_name_from_intent(fz_context *ctx, enum pdf_intent it)
3627 {
3628 switch (it)
3629 {
3630 default:
3631 case PDF_ANNOT_IT_DEFAULT: return PDF_NULL;
3632 case PDF_ANNOT_IT_FREETEXT_CALLOUT: return PDF_NAME(FreeTextCallout);
3633 case PDF_ANNOT_IT_FREETEXT_TYPEWRITER: return PDF_NAME(FreeTextTypeWriter);
3634 case PDF_ANNOT_IT_LINE_ARROW: return PDF_NAME(LineArrow);
3635 case PDF_ANNOT_IT_LINE_DIMENSION: return PDF_NAME(LineDimension);
3636 case PDF_ANNOT_IT_POLYLINE_DIMENSION: return PDF_NAME(PolyLineDimension);
3637 case PDF_ANNOT_IT_POLYGON_CLOUD: return PDF_NAME(PolygonCloud);
3638 case PDF_ANNOT_IT_POLYGON_DIMENSION: return PDF_NAME(PolygonDimension);
3639 }
3640 }
3641
3642 const char *pdf_string_from_intent(fz_context *ctx, enum pdf_intent it)
3643 {
3644 switch (it)
3645 {
3646 default:
3647 case PDF_ANNOT_IT_DEFAULT: return NULL;
3648 case PDF_ANNOT_IT_FREETEXT_CALLOUT: return "FreeTextCallout";
3649 case PDF_ANNOT_IT_FREETEXT_TYPEWRITER: return "FreeTextTypeWriter";
3650 case PDF_ANNOT_IT_LINE_ARROW: return "LineArrow";
3651 case PDF_ANNOT_IT_LINE_DIMENSION: return "LineDimension";
3652 case PDF_ANNOT_IT_POLYLINE_DIMENSION: return "PolyLineDimension";
3653 case PDF_ANNOT_IT_POLYGON_CLOUD: return "PolygonCloud";
3654 case PDF_ANNOT_IT_POLYGON_DIMENSION: return "PolygonDimension";
3655 }
3656 }
3657
3658 int
3659 pdf_annot_has_intent(fz_context *ctx, pdf_annot *annot)
3660 {
3661 /* Only a subset of intents are defined in the spec, so we limit this API to the defined ones.
3662 * FreeText: Callout, TypeWriter
3663 * Line: Arrow, Dimension
3664 * Polygon: Cloud, Dimension
3665 * PolyLine: Dimension
3666 */
3667 return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(IT), intent_subtypes);
3668 }
3669
3670 enum pdf_intent
3671 pdf_annot_intent(fz_context *ctx, pdf_annot *annot)
3672 {
3673 enum pdf_intent ret;
3674
3675 pdf_annot_push_local_xref(ctx, annot);
3676
3677 fz_try(ctx)
3678 {
3679 check_allowed_subtypes(ctx, annot, PDF_NAME(IT), intent_subtypes);
3680 ret = pdf_intent_from_name(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(IT)));
3681 }
3682 fz_always(ctx)
3683 pdf_annot_pop_local_xref(ctx, annot);
3684 fz_catch(ctx)
3685 fz_rethrow(ctx);
3686
3687 return ret;
3688 }
3689
3690 void
3691 pdf_set_annot_intent(fz_context *ctx, pdf_annot *annot, enum pdf_intent it)
3692 {
3693 begin_annot_op(ctx, annot, "Set intent");
3694
3695 fz_try(ctx)
3696 {
3697 check_allowed_subtypes(ctx, annot, PDF_NAME(IT), intent_subtypes);
3698 pdf_dict_put(ctx, annot->obj, PDF_NAME(IT), pdf_name_from_intent(ctx, it));
3699 pdf_dirty_annot(ctx, annot);
3700 end_annot_op(ctx, annot);
3701 }
3702 fz_catch(ctx)
3703 {
3704 abandon_annot_op(ctx, annot);
3705 fz_rethrow(ctx);
3706 }
3707 }
3708
3709 static pdf_obj *callout_subtypes[] = {
3710 PDF_NAME(FreeText),
3711 NULL,
3712 };
3713
3714 int pdf_annot_has_callout(fz_context *ctx, pdf_annot *annot)
3715 {
3716 return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(CL), callout_subtypes);
3717 }
3718
3719 enum pdf_line_ending pdf_annot_callout_style(fz_context *ctx, pdf_annot *annot)
3720 {
3721 enum pdf_line_ending style = PDF_ANNOT_LE_NONE;
3722 pdf_obj *obj;
3723 pdf_annot_push_local_xref(ctx, annot);
3724 fz_try(ctx)
3725 {
3726 check_allowed_subtypes(ctx, annot, PDF_NAME(CL), callout_subtypes);
3727 obj = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE));
3728 style = pdf_line_ending_from_name(ctx, obj);
3729 }
3730 fz_always(ctx)
3731 pdf_annot_pop_local_xref(ctx, annot);
3732 fz_catch(ctx)
3733 fz_rethrow(ctx);
3734 return style;
3735 }
3736
3737 void pdf_set_annot_callout_style(fz_context *ctx, pdf_annot *annot, enum pdf_line_ending style)
3738 {
3739 begin_annot_op(ctx, annot, "Set callout style");
3740
3741 fz_try(ctx)
3742 {
3743 check_allowed_subtypes(ctx, annot, PDF_NAME(CL), callout_subtypes);
3744 pdf_dict_put(ctx, annot->obj, PDF_NAME(LE), pdf_name_from_line_ending(ctx, style));
3745 }
3746 fz_catch(ctx)
3747 {
3748 abandon_annot_op(ctx, annot);
3749 fz_rethrow(ctx);
3750 }
3751
3752 pdf_dirty_annot(ctx, annot);
3753 }
3754
3755 void pdf_annot_callout_line(fz_context *ctx, pdf_annot *annot, fz_point callout[3], int *np)
3756 {
3757 pdf_annot_push_local_xref(ctx, annot);
3758 fz_try(ctx)
3759 {
3760 fz_matrix page_ctm;
3761 pdf_obj *obj;
3762 int n;
3763
3764 check_allowed_subtypes(ctx, annot, PDF_NAME(CL), callout_subtypes);
3765
3766 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
3767
3768 obj = pdf_dict_get(ctx, annot->obj, PDF_NAME(CL));
3769 n = pdf_array_len(ctx, obj);
3770 if (n == 4 || n == 6)
3771 {
3772 callout[0] = fz_transform_point_xy(pdf_array_get_real(ctx, obj, 0), pdf_array_get_real(ctx, obj, 1), page_ctm);
3773 callout[1] = fz_transform_point_xy(pdf_array_get_real(ctx, obj, 2), pdf_array_get_real(ctx, obj, 3), page_ctm);
3774 if (n == 4)
3775 {
3776 *np = 2;
3777 callout[2] = fz_make_point(0, 0);
3778 }
3779 else
3780 {
3781 *np = 3;
3782 callout[2] = fz_transform_point_xy(pdf_array_get_real(ctx, obj, 4), pdf_array_get_real(ctx, obj, 5), page_ctm);
3783 }
3784 }
3785 else
3786 {
3787 callout[0] = fz_make_point(0, 0);
3788 callout[1] = fz_make_point(0, 0);
3789 callout[2] = fz_make_point(0, 0);
3790 *np = 0;
3791 }
3792 }
3793 fz_always(ctx)
3794 pdf_annot_pop_local_xref(ctx, annot);
3795 fz_catch(ctx)
3796 fz_rethrow(ctx);
3797 }
3798
3799 void pdf_set_annot_callout_line(fz_context *ctx, pdf_annot *annot, fz_point callout[3], int n)
3800 {
3801 begin_annot_op(ctx, annot, "Set callout");
3802
3803 fz_try(ctx)
3804 {
3805 fz_matrix page_ctm;
3806 fz_point p;
3807 int i;
3808 pdf_obj *obj;
3809
3810 check_allowed_subtypes(ctx, annot, PDF_NAME(CL), callout_subtypes);
3811
3812 if (n == 2 || n == 3)
3813 {
3814 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
3815 obj = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(CL), n * 2);
3816 for (i = 0; i < n; ++i)
3817 {
3818 p = fz_transform_point(callout[i], page_ctm);
3819 pdf_array_push_real(ctx, obj, p.x);
3820 pdf_array_push_real(ctx, obj, p.y);
3821 }
3822 }
3823 else
3824 {
3825 pdf_dict_del(ctx, annot->obj, PDF_NAME(CL));
3826 }
3827
3828 pdf_dirty_annot(ctx, annot);
3829 end_annot_op(ctx, annot);
3830 }
3831 fz_catch(ctx)
3832 {
3833 abandon_annot_op(ctx, annot);
3834 fz_rethrow(ctx);
3835 }
3836 }
3837
3838 fz_point pdf_annot_callout_point(fz_context *ctx, pdf_annot *annot)
3839 {
3840 fz_point line[3];
3841 int n;
3842
3843 pdf_annot_callout_line(ctx, annot, line, &n);
3844 if (n > 0)
3845 return line[0];
3846 return fz_make_point(0, 0);
3847 }
3848
3849 void pdf_set_annot_callout_point(fz_context *ctx, pdf_annot *annot, fz_point p)
3850 {
3851 fz_rect rect;
3852 fz_point a, b, line[3];
3853 float m;
3854
3855 rect = pdf_annot_rect(ctx, annot);
3856
3857 // Make line from center of text box to designated end point.
3858 a = fz_make_point((rect.x0 + rect.x1) / 2, (rect.y0 + rect.y1) / 2);
3859 b = p;
3860
3861 // No CalloutLine if end point is within the text box itself.
3862 if (fz_is_point_inside_rect(p, rect))
3863 {
3864 line[0] = p;
3865 line[1] = a;
3866 pdf_set_annot_callout_line(ctx, annot, line, 2);
3867 return;
3868 }
3869
3870 // Simplified Cohen-Sutherland algorithm to find intersection of line and text box.
3871 m = (b.y - a.y) / (b.x - a.x);
3872 for (;;)
3873 {
3874 if (b.y < rect.y0) {
3875 b.x = a.x + (rect.y0 - a.y) / m;
3876 b.y = rect.y0;
3877 }
3878 else
3879 if (b.y > rect.y1) {
3880 b.x = a.x + (rect.y1 - a.y) / m;
3881 b.y = rect.y1;
3882 }
3883 else
3884 if (b.x < rect.x0) {
3885 b.y = a.y + (rect.x0 - a.x) * m;
3886 b.x = rect.x0;
3887 }
3888 else
3889 if (b.x > rect.x1) {
3890 b.y = a.y + (rect.x1 - a.x) * m;
3891 b.x = rect.x1;
3892 }
3893 else
3894 break;
3895 }
3896
3897 // Draw line from intersection to end point.
3898 line[0] = p;
3899 line[1] = b;
3900 pdf_set_annot_callout_line(ctx, annot, line, 2);
3901 }
3902
3903 void
3904 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])
3905 {
3906 char buf[100], *p = buf, *tok, *end;
3907 float stack[4] = { 0, 0, 0, 0 };
3908 int top = 0;
3909
3910 fz_strlcpy(font_name, "Helv", font_name_size);
3911 *size = 12;
3912 *n = 0;
3913 color[0] = color[1] = color[2] = color[3] = 0;
3914
3915 fz_strlcpy(buf, da, sizeof buf);
3916 while ((tok = fz_strsep(&p, " \n\r\t")) != NULL)
3917 {
3918 if (tok[0] == 0)
3919 ;
3920 else if (tok[0] == '/')
3921 {
3922 fz_strlcpy(font_name, tok+1, font_name_size);
3923 }
3924 else if (!strcmp(tok, "Tf"))
3925 {
3926 *size = stack[0];
3927 top = 0;
3928 }
3929 else if (!strcmp(tok, "g"))
3930 {
3931 *n = 1;
3932 color[0] = stack[0];
3933 top = 0;
3934 }
3935 else if (!strcmp(tok, "rg"))
3936 {
3937 *n = 3;
3938 color[0] = stack[0];
3939 color[1] = stack[1];
3940 color[2] = stack[2];
3941 top=0;
3942 }
3943 else if (!strcmp(tok, "k"))
3944 {
3945 *n = 4;
3946 color[0] = stack[0];
3947 color[1] = stack[1];
3948 color[2] = stack[2];
3949 color[3] = stack[3];
3950 top=0;
3951 }
3952 else
3953 {
3954 float v = fz_strtof(tok, &end);
3955 if (top < 4)
3956 stack[top] = v;
3957 if (*end == 0)
3958 ++top;
3959 else
3960 top = 0;
3961 }
3962 }
3963 }
3964
3965 void
3966 pdf_parse_default_appearance(fz_context *ctx, const char *da, const char **font, float *size, int *n, float color[4])
3967 {
3968 char font_name[100];
3969
3970 pdf_parse_default_appearance_unmapped(ctx, da, font_name, sizeof font_name, size, n, color);
3971
3972 if (!strcmp(font_name, "Cour")) *font = "Cour";
3973 else if (!strcmp(font_name, "Helv")) *font = "Helv";
3974 else if (!strcmp(font_name, "TiRo")) *font = "TiRo";
3975 else if (!strcmp(font_name, "Symb")) *font = "Symb";
3976 else if (!strcmp(font_name, "ZaDb")) *font = "ZaDb";
3977 else *font = "Helv";
3978 }
3979
3980 void
3981 pdf_print_default_appearance(fz_context *ctx, char *buf, int nbuf, const char *font, float size, int n, const float *color)
3982 {
3983 if (n == 4)
3984 fz_snprintf(buf, nbuf, "/%s %g Tf %g %g %g %g k", font, size, color[0], color[1], color[2], color[3]);
3985 else if (n == 3)
3986 fz_snprintf(buf, nbuf, "/%s %g Tf %g %g %g rg", font, size, color[0], color[1], color[2]);
3987 else if (n == 1)
3988 fz_snprintf(buf, nbuf, "/%s %g Tf %g g", font, size, color[0]);
3989 else
3990 fz_snprintf(buf, nbuf, "/%s %g Tf", font, size);
3991 }
3992
3993 void
3994 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])
3995 {
3996 pdf_obj *da;
3997 pdf_annot_push_local_xref(ctx, annot);
3998
3999 fz_try(ctx)
4000 {
4001 da = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(DA));
4002 if (!da)
4003 {
4004 pdf_obj *trailer = pdf_trailer(ctx, annot->page->doc);
4005 da = pdf_dict_getl(ctx, trailer, PDF_NAME(Root), PDF_NAME(AcroForm), PDF_NAME(DA), NULL);
4006 }
4007 pdf_parse_default_appearance_unmapped(ctx, pdf_to_str_buf(ctx, da), font_name, font_name_len, size, n, color);
4008 }
4009 fz_always(ctx)
4010 pdf_annot_pop_local_xref(ctx, annot);
4011 fz_catch(ctx)
4012 fz_rethrow(ctx);
4013 }
4014
4015 static pdf_obj *default_appearance_subtypes[] = {
4016 PDF_NAME(FreeText),
4017 PDF_NAME(Widget),
4018 NULL,
4019 };
4020
4021 int
4022 pdf_annot_has_default_appearance(fz_context *ctx, pdf_annot *annot)
4023 {
4024 return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(DA), default_appearance_subtypes);
4025 }
4026
4027 void
4028 pdf_annot_default_appearance(fz_context *ctx, pdf_annot *annot, const char **font, float *size, int *n, float color[4])
4029 {
4030 pdf_obj *da;
4031 pdf_annot_push_local_xref(ctx, annot);
4032
4033 fz_try(ctx)
4034 {
4035 check_allowed_subtypes(ctx, annot, PDF_NAME(DA), default_appearance_subtypes);
4036 da = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(DA));
4037 if (!da)
4038 {
4039 pdf_obj *trailer = pdf_trailer(ctx, annot->page->doc);
4040 da = pdf_dict_getl(ctx, trailer, PDF_NAME(Root), PDF_NAME(AcroForm), PDF_NAME(DA), NULL);
4041 }
4042 pdf_parse_default_appearance(ctx, pdf_to_str_buf(ctx, da), font, size, n, color);
4043 }
4044 fz_always(ctx)
4045 pdf_annot_pop_local_xref(ctx, annot);
4046 fz_catch(ctx)
4047 fz_rethrow(ctx);
4048 }
4049
4050 void
4051 pdf_set_annot_default_appearance(fz_context *ctx, pdf_annot *annot, const char *font, float size, int n, const float *color)
4052 {
4053 char buf[100];
4054
4055 begin_annot_op(ctx, annot, "Set default appearance");
4056
4057 fz_try(ctx)
4058 {
4059 check_allowed_subtypes(ctx, annot, PDF_NAME(DA), default_appearance_subtypes);
4060 pdf_print_default_appearance(ctx, buf, sizeof buf, font, size, n, color);
4061
4062 pdf_dict_put_string(ctx, annot->obj, PDF_NAME(DA), buf, strlen(buf));
4063
4064 pdf_dict_del(ctx, annot->obj, PDF_NAME(DS)); /* not supported */
4065 pdf_dict_del(ctx, annot->obj, PDF_NAME(RC)); /* not supported */
4066 end_annot_op(ctx, annot);
4067 }
4068 fz_catch(ctx)
4069 {
4070 abandon_annot_op(ctx, annot);
4071 fz_rethrow(ctx);
4072 }
4073
4074 pdf_dirty_annot(ctx, annot);
4075 }
4076
4077 int pdf_annot_field_flags(fz_context *ctx, pdf_annot *annot)
4078 {
4079 int ret;
4080
4081 pdf_annot_push_local_xref(ctx, annot);
4082
4083 fz_try(ctx)
4084 ret = pdf_field_flags(ctx, annot->obj);
4085 fz_always(ctx)
4086 pdf_annot_pop_local_xref(ctx, annot);
4087 fz_catch(ctx)
4088 fz_rethrow(ctx);
4089
4090 return ret;
4091 }
4092
4093 const char *pdf_annot_field_value(fz_context *ctx, pdf_annot *widget)
4094 {
4095 const char *ret;
4096
4097 pdf_annot_push_local_xref(ctx, widget);
4098
4099 fz_try(ctx)
4100 ret = pdf_field_value(ctx, widget->obj);
4101 fz_always(ctx)
4102 pdf_annot_pop_local_xref(ctx, widget);
4103 fz_catch(ctx)
4104 fz_rethrow(ctx);
4105
4106 return ret;
4107 }
4108
4109 const char *pdf_annot_field_label(fz_context *ctx, pdf_annot *widget)
4110 {
4111 const char *ret;
4112
4113 pdf_annot_push_local_xref(ctx, widget);
4114
4115 fz_try(ctx)
4116 ret = pdf_field_label(ctx, widget->obj);
4117 fz_always(ctx)
4118 pdf_annot_pop_local_xref(ctx, widget);
4119 fz_catch(ctx)
4120 fz_rethrow(ctx);
4121
4122 return ret;
4123 }
4124
4125 int pdf_set_annot_field_value(fz_context *ctx, pdf_document *doc, pdf_annot *annot, const char *text, int ignore_trigger_events)
4126 {
4127 int ret;
4128
4129 begin_annot_op(ctx, annot, "Set field value");
4130
4131 fz_try(ctx)
4132 {
4133 ret = pdf_set_field_value(ctx, doc, annot->obj, text, ignore_trigger_events);
4134 end_annot_op(ctx, annot);
4135 }
4136 fz_catch(ctx)
4137 {
4138 abandon_annot_op(ctx, annot);
4139 fz_rethrow(ctx);
4140 }
4141
4142 pdf_dirty_annot(ctx, annot);
4143
4144 return ret;
4145 }
4146
4147 void
4148 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)
4149 {
4150 pdf_obj *form = NULL;
4151 pdf_obj *ap, *app;
4152 pdf_obj *app_name = NULL;
4153
4154 begin_annot_op(ctx, annot, "Set appearance stream");
4155
4156 if (!appearance)
4157 appearance = "N";
4158
4159 fz_var(form);
4160 fz_var(app_name);
4161
4162 fz_try(ctx)
4163 {
4164 ap = pdf_dict_get(ctx, annot->obj, PDF_NAME(AP));
4165 if (!ap)
4166 ap = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(AP), 1);
4167
4168 if (!state)
4169 form = pdf_keep_obj(ctx, pdf_dict_gets(ctx, ap, appearance));
4170 else
4171 {
4172 if (strcmp(appearance, "N") && strcmp(appearance, "R") && strcmp(appearance, "D"))
4173 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Unknown annotation appearance");
4174
4175 app_name = pdf_new_name(ctx, appearance);
4176 app = pdf_dict_get(ctx, ap, app_name);
4177 if (!app)
4178 app = pdf_dict_put_dict(ctx, ap, app_name, 2);
4179 form = pdf_keep_obj(ctx, pdf_dict_gets(ctx, ap, appearance));
4180 }
4181 /* Care required here. Some files have multiple annotations, which share
4182 * appearance streams. As such, we must NOT reuse such appearance streams.
4183 * On the other hand, we cannot afford to always recreate appearance
4184 * streams, as this can lead to leakage of partial edits into the document.
4185 * Any appearance we generate will be in the incremental section, and we
4186 * will never generate shared appearances. As such, we can reuse an
4187 * appearance object only if it is in the incremental section. */
4188 if (!pdf_obj_is_incremental(ctx, form))
4189 {
4190 pdf_drop_obj(ctx, form);
4191 form = NULL;
4192 }
4193 if (!pdf_is_dict(ctx, form))
4194 {
4195 pdf_drop_obj(ctx, form);
4196 form = NULL;
4197 form = pdf_new_xobject(ctx, annot->page->doc, bbox, ctm, res, contents);
4198 }
4199 else
4200 pdf_update_xobject(ctx, annot->page->doc, form, bbox, ctm, res, contents);
4201
4202 if (!state)
4203 pdf_dict_puts(ctx, ap, appearance, form);
4204 else
4205 pdf_dict_puts(ctx, app, state, form);
4206 end_annot_op(ctx, annot);
4207 }
4208 fz_always(ctx)
4209 {
4210 pdf_drop_obj(ctx, form);
4211 pdf_drop_obj(ctx, app_name);
4212 }
4213 fz_catch(ctx)
4214 {
4215 abandon_annot_op(ctx, annot);
4216 fz_rethrow(ctx);
4217 }
4218
4219 pdf_set_annot_resynthesised(ctx, annot);
4220 }
4221
4222 void
4223 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)
4224 {
4225 pdf_document *doc;
4226 fz_device *dev = NULL;
4227 pdf_obj *res = NULL;
4228 fz_buffer *contents = NULL;
4229
4230 /* Convert fitz-space mediabox to pdf-space bbox */
4231 fz_rect mediabox = fz_bound_display_list(ctx, list);
4232 fz_matrix transform = { 1, 0, 0, -1, -mediabox.x0, mediabox.y1 };
4233 fz_rect bbox = fz_transform_rect(mediabox, transform);
4234
4235 fz_var(dev);
4236 fz_var(contents);
4237 fz_var(res);
4238
4239 begin_annot_op(ctx, annot, "Set appearance stream");
4240 doc = annot->page->doc;
4241
4242 fz_try(ctx)
4243 {
4244 res = pdf_new_dict(ctx, doc, 1);
4245 contents = fz_new_buffer(ctx, 0);
4246 dev = pdf_new_pdf_device(ctx, doc, transform, res, contents);
4247 fz_run_display_list(ctx, list, dev, fz_identity, fz_infinite_rect, NULL);
4248 fz_close_device(ctx, dev);
4249 fz_drop_device(ctx, dev);
4250 dev = NULL;
4251
4252 pdf_set_annot_appearance(ctx, annot, appearance, state, ctm, bbox, res, contents);
4253 end_annot_op(ctx, annot);
4254 }
4255 fz_always(ctx)
4256 {
4257 fz_drop_device(ctx, dev);
4258 fz_drop_buffer(ctx, contents);
4259 pdf_drop_obj(ctx, res);
4260 }
4261 fz_catch(ctx)
4262 {
4263 abandon_annot_op(ctx, annot);
4264 fz_rethrow(ctx);
4265 }
4266 }
4267
4268 static pdf_obj *stamp_subtypes[] = {
4269 PDF_NAME(Stamp),
4270 NULL,
4271 };
4272
4273 pdf_obj *
4274 pdf_annot_stamp_image_obj(fz_context *ctx, pdf_annot *annot)
4275 {
4276 pdf_obj *obj, *imgobj = NULL;
4277
4278 pdf_annot_push_local_xref(ctx, annot);
4279
4280 fz_try(ctx)
4281 {
4282 obj = pdf_dict_getp(ctx, annot->obj, "AP/N/Resources/XObject");
4283 if (pdf_dict_len(ctx, obj) == 1)
4284 {
4285 obj = pdf_dict_get_val(ctx, obj, 0);
4286 if (pdf_is_image_stream(ctx, obj))
4287 imgobj = obj;
4288 }
4289 }
4290 fz_always(ctx)
4291 pdf_annot_pop_local_xref(ctx, annot);
4292 fz_catch(ctx)
4293 fz_rethrow(ctx);
4294
4295 return imgobj;
4296 }
4297
4298 void pdf_set_annot_stamp_image_obj(fz_context *ctx, pdf_annot *annot, pdf_obj *ref)
4299 {
4300 begin_annot_op(ctx, annot, "Set stamp image");
4301
4302 fz_try(ctx)
4303 {
4304 check_allowed_subtypes(ctx, annot, PDF_NAME(Stamp), stamp_subtypes);
4305
4306 pdf_dict_del(ctx, annot->obj, PDF_NAME(AP));
4307 pdf_dict_putp(ctx, annot->obj, "AP/N/Resources/XObject/I", ref);
4308
4309 end_annot_op(ctx, annot);
4310 }
4311 fz_catch(ctx)
4312 {
4313 abandon_annot_op(ctx, annot);
4314 fz_rethrow(ctx);
4315 }
4316
4317 pdf_dirty_annot(ctx, annot);
4318 }
4319
4320 void pdf_set_annot_stamp_image(fz_context *ctx, pdf_annot *annot, fz_image *img)
4321 {
4322 pdf_obj *ref = pdf_add_image(ctx, annot->page->doc, img);
4323 fz_try(ctx)
4324 pdf_set_annot_stamp_image_obj(ctx, annot, ref);
4325 fz_always(ctx)
4326 pdf_drop_obj(ctx, ref);
4327 fz_catch(ctx)
4328 fz_rethrow(ctx);
4329 }
4330
4331 static pdf_obj *filespec_subtypes[] = {
4332 PDF_NAME(FileAttachment),
4333 NULL,
4334 };
4335
4336 int
4337 pdf_annot_has_filespec(fz_context *ctx, pdf_annot *annot)
4338 {
4339 return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(FS), filespec_subtypes);
4340 }
4341
4342 pdf_obj *
4343 pdf_annot_filespec(fz_context *ctx, pdf_annot *annot)
4344 {
4345 pdf_obj *filespec;
4346
4347 pdf_annot_push_local_xref(ctx, annot);
4348
4349 fz_try(ctx)
4350 {
4351 check_allowed_subtypes(ctx, annot, PDF_NAME(FS), filespec_subtypes);
4352 filespec = pdf_dict_get(ctx, annot->obj, PDF_NAME(FS));
4353 }
4354 fz_always(ctx)
4355 pdf_annot_pop_local_xref(ctx, annot);
4356 fz_catch(ctx)
4357 fz_rethrow(ctx);
4358
4359 return filespec;
4360 }
4361
4362 void
4363 pdf_set_annot_filespec(fz_context *ctx, pdf_annot *annot, pdf_obj *fs)
4364 {
4365 if (fs != PDF_NULL && !pdf_is_embedded_file(ctx, fs))
4366 fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot set non-filespec as annotation filespec");
4367
4368 begin_annot_op(ctx, annot, "Set filespec");
4369
4370 fz_try(ctx)
4371 {
4372 check_allowed_subtypes(ctx, annot, PDF_NAME(M), filespec_subtypes);
4373 pdf_dict_put(ctx, pdf_annot_obj(ctx, annot), PDF_NAME(FS), fs);
4374 end_annot_op(ctx, annot);
4375 }
4376 fz_catch(ctx)
4377 {
4378 abandon_annot_op(ctx, annot);
4379 fz_rethrow(ctx);
4380 }
4381
4382 pdf_dirty_annot(ctx, annot);
4383 }
4384
4385 int
4386 pdf_annot_hidden_for_editing(fz_context *ctx, pdf_annot *annot)
4387 {
4388 return annot->hidden_editing;
4389 }
4390
4391 void
4392 pdf_set_annot_hidden_for_editing(fz_context *ctx, pdf_annot *annot, int hidden)
4393 {
4394 annot->hidden_editing = hidden;
4395 }