1 #include "mupdf/fitz.h"
2 #include "mupdf/pdf.h"
3
4 #include <string.h>
5
6 pdf_annot *
pdf_keep_annot(fz_context * ctx,pdf_annot * annot)7 pdf_keep_annot(fz_context *ctx, pdf_annot *annot)
8 {
9 return fz_keep_imp(ctx, annot, &annot->refs);
10 }
11
12 void
pdf_drop_annot(fz_context * ctx,pdf_annot * annot)13 pdf_drop_annot(fz_context *ctx, pdf_annot *annot)
14 {
15 if (fz_drop_imp(ctx, annot, &annot->refs))
16 {
17 pdf_drop_obj(ctx, annot->ap);
18 pdf_drop_obj(ctx, annot->obj);
19 fz_free(ctx, annot);
20 }
21 }
22
23 void
pdf_drop_annots(fz_context * ctx,pdf_annot * annot)24 pdf_drop_annots(fz_context *ctx, pdf_annot *annot)
25 {
26 while (annot)
27 {
28 pdf_annot *next = annot->next;
29 pdf_drop_annot(ctx, annot);
30 annot = next;
31 }
32 }
33
34 fz_matrix
pdf_annot_transform(fz_context * ctx,pdf_annot * annot)35 pdf_annot_transform(fz_context *ctx, pdf_annot *annot)
36 {
37 fz_rect bbox, rect;
38 fz_matrix matrix;
39 float w, h, x, y;
40
41 rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
42 bbox = pdf_xobject_bbox(ctx, annot->ap);
43 matrix = pdf_xobject_matrix(ctx, annot->ap);
44
45 bbox = fz_transform_rect(bbox, matrix);
46 if (bbox.x1 == bbox.x0)
47 w = 0;
48 else
49 w = (rect.x1 - rect.x0) / (bbox.x1 - bbox.x0);
50 if (bbox.y1 == bbox.y0)
51 h = 0;
52 else
53 h = (rect.y1 - rect.y0) / (bbox.y1 - bbox.y0);
54 x = rect.x0 - (bbox.x0 * w);
55 y = rect.y0 - (bbox.y0 * h);
56
57 return fz_pre_scale(fz_translate(x, y), w, h);
58 }
59
60 /*
61 Internal function for creating a new pdf annotation.
62 */
63 static pdf_annot *
pdf_new_annot(fz_context * ctx,pdf_page * page,pdf_obj * obj)64 pdf_new_annot(fz_context *ctx, pdf_page *page, pdf_obj *obj)
65 {
66 pdf_annot *annot;
67
68 annot = fz_malloc_struct(ctx, pdf_annot);
69 annot->refs = 1;
70 annot->page = page; /* only borrowed, as the page owns the annot */
71 annot->obj = pdf_keep_obj(ctx, obj);
72
73 return annot;
74 }
75
76 void
pdf_load_annots(fz_context * ctx,pdf_page * page,pdf_obj * annots)77 pdf_load_annots(fz_context *ctx, pdf_page *page, pdf_obj *annots)
78 {
79 pdf_annot *annot;
80 pdf_obj *subtype;
81 int i, n;
82
83 n = pdf_array_len(ctx, annots);
84 for (i = 0; i < n; ++i)
85 {
86 pdf_obj *obj = pdf_array_get(ctx, annots, i);
87 if (pdf_is_dict(ctx, obj))
88 {
89 subtype = pdf_dict_get(ctx, obj, PDF_NAME(Subtype));
90 if (pdf_name_eq(ctx, subtype, PDF_NAME(Link)))
91 continue;
92 if (pdf_name_eq(ctx, subtype, PDF_NAME(Popup)))
93 continue;
94
95 annot = pdf_new_annot(ctx, page, obj);
96 fz_try(ctx)
97 {
98 pdf_update_annot(ctx, annot);
99 annot->has_new_ap = 0;
100 }
101 fz_catch(ctx)
102 fz_warn(ctx, "could not update appearance for annotation");
103
104 if (pdf_name_eq(ctx, subtype, PDF_NAME(Widget)))
105 {
106 *page->widget_tailp = annot;
107 page->widget_tailp = &annot->next;
108 }
109 else
110 {
111 *page->annot_tailp = annot;
112 page->annot_tailp = &annot->next;
113 }
114 }
115 }
116 }
117
118 pdf_annot *
pdf_first_annot(fz_context * ctx,pdf_page * page)119 pdf_first_annot(fz_context *ctx, pdf_page *page)
120 {
121 return page->annots;
122 }
123
124 pdf_annot *
pdf_next_annot(fz_context * ctx,pdf_annot * annot)125 pdf_next_annot(fz_context *ctx, pdf_annot *annot)
126 {
127 return annot->next;
128 }
129
130 fz_rect
pdf_bound_annot(fz_context * ctx,pdf_annot * annot)131 pdf_bound_annot(fz_context *ctx, pdf_annot *annot)
132 {
133 fz_matrix page_ctm;
134 fz_rect rect;
135 int flags;
136
137 rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
138 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
139
140 flags = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(F));
141 if (flags & PDF_ANNOT_IS_NO_ROTATE)
142 {
143 int rotate = pdf_to_int(ctx, pdf_dict_get_inheritable(ctx, annot->page->obj, PDF_NAME(Rotate)));
144 fz_point tp = fz_transform_point_xy(rect.x0, rect.y1, page_ctm);
145 page_ctm = fz_concat(page_ctm, fz_translate(-tp.x, -tp.y));
146 page_ctm = fz_concat(page_ctm, fz_rotate(-rotate));
147 page_ctm = fz_concat(page_ctm, fz_translate(tp.x, tp.y));
148 }
149
150 return fz_transform_rect(rect, page_ctm);
151 }
152
153 void
pdf_dirty_annot(fz_context * ctx,pdf_annot * annot)154 pdf_dirty_annot(fz_context *ctx, pdf_annot *annot)
155 {
156 annot->needs_new_ap = 1;
157 if (annot->page && annot->page->doc)
158 annot->page->doc->dirty = 1;
159 }
160
161 const char *
pdf_string_from_annot_type(fz_context * ctx,enum pdf_annot_type type)162 pdf_string_from_annot_type(fz_context *ctx, enum pdf_annot_type type)
163 {
164 switch (type)
165 {
166 case PDF_ANNOT_TEXT: return "Text";
167 case PDF_ANNOT_LINK: return "Link";
168 case PDF_ANNOT_FREE_TEXT: return "FreeText";
169 case PDF_ANNOT_LINE: return "Line";
170 case PDF_ANNOT_SQUARE: return "Square";
171 case PDF_ANNOT_CIRCLE: return "Circle";
172 case PDF_ANNOT_POLYGON: return "Polygon";
173 case PDF_ANNOT_POLY_LINE: return "PolyLine";
174 case PDF_ANNOT_HIGHLIGHT: return "Highlight";
175 case PDF_ANNOT_UNDERLINE: return "Underline";
176 case PDF_ANNOT_SQUIGGLY: return "Squiggly";
177 case PDF_ANNOT_STRIKE_OUT: return "StrikeOut";
178 case PDF_ANNOT_REDACT: return "Redact";
179 case PDF_ANNOT_STAMP: return "Stamp";
180 case PDF_ANNOT_CARET: return "Caret";
181 case PDF_ANNOT_INK: return "Ink";
182 case PDF_ANNOT_POPUP: return "Popup";
183 case PDF_ANNOT_FILE_ATTACHMENT: return "FileAttachment";
184 case PDF_ANNOT_SOUND: return "Sound";
185 case PDF_ANNOT_MOVIE: return "Movie";
186 case PDF_ANNOT_RICH_MEDIA: return "RichMedia";
187 case PDF_ANNOT_WIDGET: return "Widget";
188 case PDF_ANNOT_SCREEN: return "Screen";
189 case PDF_ANNOT_PRINTER_MARK: return "PrinterMark";
190 case PDF_ANNOT_TRAP_NET: return "TrapNet";
191 case PDF_ANNOT_WATERMARK: return "Watermark";
192 case PDF_ANNOT_3D: return "3D";
193 case PDF_ANNOT_PROJECTION: return "Projection";
194 default: return "UNKNOWN";
195 }
196 }
197
198 int
pdf_annot_type_from_string(fz_context * ctx,const char * subtype)199 pdf_annot_type_from_string(fz_context *ctx, const char *subtype)
200 {
201 if (!strcmp("Text", subtype)) return PDF_ANNOT_TEXT;
202 if (!strcmp("Link", subtype)) return PDF_ANNOT_LINK;
203 if (!strcmp("FreeText", subtype)) return PDF_ANNOT_FREE_TEXT;
204 if (!strcmp("Line", subtype)) return PDF_ANNOT_LINE;
205 if (!strcmp("Square", subtype)) return PDF_ANNOT_SQUARE;
206 if (!strcmp("Circle", subtype)) return PDF_ANNOT_CIRCLE;
207 if (!strcmp("Polygon", subtype)) return PDF_ANNOT_POLYGON;
208 if (!strcmp("PolyLine", subtype)) return PDF_ANNOT_POLY_LINE;
209 if (!strcmp("Highlight", subtype)) return PDF_ANNOT_HIGHLIGHT;
210 if (!strcmp("Underline", subtype)) return PDF_ANNOT_UNDERLINE;
211 if (!strcmp("Squiggly", subtype)) return PDF_ANNOT_SQUIGGLY;
212 if (!strcmp("StrikeOut", subtype)) return PDF_ANNOT_STRIKE_OUT;
213 if (!strcmp("Redact", subtype)) return PDF_ANNOT_REDACT;
214 if (!strcmp("Stamp", subtype)) return PDF_ANNOT_STAMP;
215 if (!strcmp("Caret", subtype)) return PDF_ANNOT_CARET;
216 if (!strcmp("Ink", subtype)) return PDF_ANNOT_INK;
217 if (!strcmp("Popup", subtype)) return PDF_ANNOT_POPUP;
218 if (!strcmp("FileAttachment", subtype)) return PDF_ANNOT_FILE_ATTACHMENT;
219 if (!strcmp("Sound", subtype)) return PDF_ANNOT_SOUND;
220 if (!strcmp("Movie", subtype)) return PDF_ANNOT_MOVIE;
221 if (!strcmp("RichMedia", subtype)) return PDF_ANNOT_RICH_MEDIA;
222 if (!strcmp("Widget", subtype)) return PDF_ANNOT_WIDGET;
223 if (!strcmp("Screen", subtype)) return PDF_ANNOT_SCREEN;
224 if (!strcmp("PrinterMark", subtype)) return PDF_ANNOT_PRINTER_MARK;
225 if (!strcmp("TrapNet", subtype)) return PDF_ANNOT_TRAP_NET;
226 if (!strcmp("Watermark", subtype)) return PDF_ANNOT_WATERMARK;
227 if (!strcmp("3D", subtype)) return PDF_ANNOT_3D;
228 if (!strcmp("Projection", subtype)) return PDF_ANNOT_PROJECTION;
229 return PDF_ANNOT_UNKNOWN;
230 }
231
is_allowed_subtype(fz_context * ctx,pdf_annot * annot,pdf_obj * property,pdf_obj ** allowed)232 static int is_allowed_subtype(fz_context *ctx, pdf_annot *annot, pdf_obj *property, pdf_obj **allowed)
233 {
234 pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
235 while (*allowed) {
236 if (pdf_name_eq(ctx, subtype, *allowed))
237 return 1;
238 allowed++;
239 }
240
241 return 0;
242 }
243
check_allowed_subtypes(fz_context * ctx,pdf_annot * annot,pdf_obj * property,pdf_obj ** allowed)244 static void check_allowed_subtypes(fz_context *ctx, pdf_annot *annot, pdf_obj *property, pdf_obj **allowed)
245 {
246 pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
247 if (!is_allowed_subtype(ctx, annot, property, allowed))
248 fz_throw(ctx, FZ_ERROR_GENERIC, "%s annotations have no %s property", pdf_to_name(ctx, subtype), pdf_to_name(ctx, property));
249 }
250
251 pdf_annot *
pdf_create_annot_raw(fz_context * ctx,pdf_page * page,enum pdf_annot_type type)252 pdf_create_annot_raw(fz_context *ctx, pdf_page *page, enum pdf_annot_type type)
253 {
254 pdf_annot *annot = NULL;
255 pdf_document *doc = page->doc;
256 pdf_obj *annot_obj = pdf_new_dict(ctx, doc, 0);
257 pdf_obj *ind_obj = NULL;
258
259 fz_var(annot);
260 fz_var(ind_obj);
261 fz_try(ctx)
262 {
263 int ind_obj_num;
264 const char *type_str;
265 pdf_obj *annot_arr;
266
267 type_str = pdf_string_from_annot_type(ctx, type);
268 if (type == PDF_ANNOT_UNKNOWN)
269 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot create unknown annotation");
270
271 annot_arr = pdf_dict_get(ctx, page->obj, PDF_NAME(Annots));
272 if (annot_arr == NULL)
273 {
274 annot_arr = pdf_new_array(ctx, doc, 0);
275 pdf_dict_put_drop(ctx, page->obj, PDF_NAME(Annots), annot_arr);
276 }
277
278 pdf_dict_put(ctx, annot_obj, PDF_NAME(Type), PDF_NAME(Annot));
279 pdf_dict_put_name(ctx, annot_obj, PDF_NAME(Subtype), type_str);
280
281 /*
282 Both annotation object and annotation structure are now created.
283 Insert the object in the hierarchy and the structure in the
284 page's array.
285 */
286 ind_obj_num = pdf_create_object(ctx, doc);
287 pdf_update_object(ctx, doc, ind_obj_num, annot_obj);
288 ind_obj = pdf_new_indirect(ctx, doc, ind_obj_num, 0);
289 pdf_array_push(ctx, annot_arr, ind_obj);
290
291 annot = pdf_new_annot(ctx, page, ind_obj);
292 annot->ap = NULL;
293
294 /*
295 Linking must be done after any call that might throw because
296 pdf_drop_annots below actually frees a list. Put the new annot
297 at the end of the list, so that it will be drawn last.
298 */
299 if (type == PDF_ANNOT_WIDGET)
300 {
301 *page->widget_tailp = annot;
302 page->widget_tailp = &annot->next;
303 }
304 else
305 {
306 *page->annot_tailp = annot;
307 page->annot_tailp = &annot->next;
308 }
309
310 doc->dirty = 1;
311 }
312 fz_always(ctx)
313 {
314 pdf_drop_obj(ctx, annot_obj);
315 pdf_drop_obj(ctx, ind_obj);
316 }
317 fz_catch(ctx)
318 {
319 pdf_drop_annots(ctx, annot);
320 fz_rethrow(ctx);
321 }
322
323 return annot;
324 }
325
326 static pdf_obj *
pdf_add_popup_annot(fz_context * ctx,pdf_annot * annot)327 pdf_add_popup_annot(fz_context *ctx, pdf_annot *annot)
328 {
329 pdf_obj *annots, *popup;
330
331 popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
332 if (popup)
333 return popup;
334
335 annots = pdf_dict_get(ctx, annot->page->obj, PDF_NAME(Annots));
336 if (!annots)
337 return NULL;
338
339 popup = pdf_add_new_dict(ctx, annot->page->doc, 4);
340 pdf_array_push_drop(ctx, annots, popup);
341
342 pdf_dict_put(ctx, popup, PDF_NAME(Type), PDF_NAME(Annot));
343 pdf_dict_put(ctx, popup, PDF_NAME(Subtype), PDF_NAME(Popup));
344 pdf_dict_put(ctx, popup, PDF_NAME(Parent), annot->obj);
345 pdf_dict_put_rect(ctx, popup, PDF_NAME(Rect), fz_make_rect(0,0,0,0));
346
347 pdf_dict_put(ctx, annot->obj, PDF_NAME(Popup), popup);
348
349 return popup;
350 }
351
pdf_set_annot_popup(fz_context * ctx,pdf_annot * annot,fz_rect rect)352 void pdf_set_annot_popup(fz_context *ctx, pdf_annot *annot, fz_rect rect)
353 {
354 fz_matrix page_ctm, inv_page_ctm;
355 pdf_obj *popup;
356 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
357 inv_page_ctm = fz_invert_matrix(page_ctm);
358 rect = fz_transform_rect(rect, inv_page_ctm);
359 popup = pdf_add_popup_annot(ctx, annot);
360 pdf_dict_put_rect(ctx, popup, PDF_NAME(Rect), rect);
361 }
362
pdf_annot_popup(fz_context * ctx,pdf_annot * annot)363 fz_rect pdf_annot_popup(fz_context *ctx, pdf_annot *annot)
364 {
365 fz_matrix page_ctm;
366 fz_rect rect;
367 pdf_obj *popup;
368 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
369 popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
370 rect = pdf_dict_get_rect(ctx, popup, PDF_NAME(Rect));
371 return fz_transform_rect(rect, page_ctm);
372 }
373
374 pdf_annot *
pdf_create_annot(fz_context * ctx,pdf_page * page,enum pdf_annot_type type)375 pdf_create_annot(fz_context *ctx, pdf_page *page, enum pdf_annot_type type)
376 {
377 static const float black[3] = { 0, 0, 0 };
378 static const float red[3] = { 1, 0, 0 };
379 static const float green[3] = { 0, 1, 0 };
380 static const float blue[3] = { 0, 0, 1 };
381 static const float yellow[3] = { 1, 1, 0 };
382 static const float magenta[3] = { 1, 0, 1 };
383
384 int flags = PDF_ANNOT_IS_PRINT; /* Make printable as default */
385
386 pdf_annot *annot = pdf_create_annot_raw(ctx, page, type);
387
388 switch (type)
389 {
390 default:
391 break;
392
393 case PDF_ANNOT_TEXT:
394 case PDF_ANNOT_FILE_ATTACHMENT:
395 case PDF_ANNOT_SOUND:
396 {
397 fz_rect icon_rect = { 12, 12, 12+20, 12+20 };
398 flags = PDF_ANNOT_IS_PRINT | PDF_ANNOT_IS_NO_ZOOM | PDF_ANNOT_IS_NO_ROTATE;
399 pdf_set_annot_rect(ctx, annot, icon_rect);
400 pdf_set_annot_color(ctx, annot, 3, yellow);
401 pdf_set_annot_popup(ctx, annot, fz_make_rect(32, 12, 32+200, 12+100));
402 }
403 break;
404
405 case PDF_ANNOT_FREE_TEXT:
406 {
407 fz_rect text_rect = { 12, 12, 12+200, 12+100 };
408
409 /* Use undocumented Adobe property to match page rotation. */
410 int rot = pdf_to_int(ctx, pdf_dict_get_inheritable(ctx, page->obj, PDF_NAME(Rotate)));
411 if (rot != 0)
412 pdf_dict_put_int(ctx, annot->obj, PDF_NAME(Rotate), rot);
413
414 pdf_set_annot_rect(ctx, annot, text_rect);
415 pdf_set_annot_border(ctx, annot, 0);
416 pdf_set_annot_default_appearance(ctx, annot, "Helv", 12, black);
417 }
418 break;
419
420 case PDF_ANNOT_STAMP:
421 {
422 fz_rect stamp_rect = { 12, 12, 12+190, 12+50 };
423 pdf_set_annot_rect(ctx, annot, stamp_rect);
424 pdf_set_annot_color(ctx, annot, 3, red);
425 }
426 break;
427
428 case PDF_ANNOT_CARET:
429 {
430 fz_rect caret_rect = { 12, 12, 12+18, 12+15 };
431 pdf_set_annot_rect(ctx, annot, caret_rect);
432 pdf_set_annot_color(ctx, annot, 3, blue);
433 }
434 break;
435
436 case PDF_ANNOT_LINE:
437 {
438 fz_point a = { 12, 12 }, b = { 12 + 100, 12 + 50 };
439 pdf_set_annot_line(ctx, annot, a, b);
440 pdf_set_annot_border(ctx, annot, 1);
441 pdf_set_annot_color(ctx, annot, 3, red);
442 }
443 break;
444
445 case PDF_ANNOT_SQUARE:
446 case PDF_ANNOT_CIRCLE:
447 {
448 fz_rect shape_rect = { 12, 12, 12+100, 12+50 };
449 pdf_set_annot_rect(ctx, annot, shape_rect);
450 pdf_set_annot_border(ctx, annot, 1);
451 pdf_set_annot_color(ctx, annot, 3, red);
452 }
453 break;
454
455 case PDF_ANNOT_POLYGON:
456 case PDF_ANNOT_POLY_LINE:
457 case PDF_ANNOT_INK:
458 pdf_set_annot_border(ctx, annot, 1);
459 pdf_set_annot_color(ctx, annot, 3, red);
460 break;
461
462 case PDF_ANNOT_HIGHLIGHT:
463 pdf_set_annot_color(ctx, annot, 3, yellow);
464 break;
465 case PDF_ANNOT_UNDERLINE:
466 pdf_set_annot_color(ctx, annot, 3, green);
467 break;
468 case PDF_ANNOT_STRIKE_OUT:
469 pdf_set_annot_color(ctx, annot, 3, red);
470 break;
471 case PDF_ANNOT_SQUIGGLY:
472 pdf_set_annot_color(ctx, annot, 3, magenta);
473 break;
474 }
475
476 pdf_dict_put(ctx, annot->obj, PDF_NAME(P), page->obj);
477 pdf_dict_put_int(ctx, annot->obj, PDF_NAME(F), flags);
478
479 return pdf_keep_annot(ctx, annot);
480 }
481
482 void
pdf_delete_annot(fz_context * ctx,pdf_page * page,pdf_annot * annot)483 pdf_delete_annot(fz_context *ctx, pdf_page *page, pdf_annot *annot)
484 {
485 pdf_document *doc = annot->page->doc;
486 pdf_annot **annotptr;
487 pdf_obj *annot_arr, *popup;
488 int i;
489
490 if (annot == NULL)
491 return;
492
493 /* Remove annot from page's list */
494 for (annotptr = &page->annots; *annotptr; annotptr = &(*annotptr)->next)
495 {
496 if (*annotptr == annot)
497 break;
498 }
499
500 /* Check the passed annotation was of this page */
501 if (*annotptr == NULL)
502 return;
503
504 *annotptr = annot->next;
505
506 /* If the removed annotation was the last in the list adjust the end pointer */
507 if (*annotptr == NULL)
508 page->annot_tailp = annotptr;
509
510 /* Remove the annot from the "Annots" array. */
511 annot_arr = pdf_dict_get(ctx, page->obj, PDF_NAME(Annots));
512 i = pdf_array_find(ctx, annot_arr, annot->obj);
513 if (i >= 0)
514 pdf_array_delete(ctx, annot_arr, i);
515
516 /* Remove the associated Popup annotation from the Annots array */
517 popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
518 if (popup)
519 {
520 i = pdf_array_find(ctx, annot_arr, popup);
521 if (i >= 0)
522 pdf_array_delete(ctx, annot_arr, i);
523 }
524
525 /* The garbage collection pass when saving will remove the annot object,
526 * removing it here may break files if multiple pages use the same annot. */
527
528 /* And free it. */
529 pdf_drop_annot(ctx, annot);
530
531 doc->dirty = 1;
532 }
533
534 enum pdf_annot_type
pdf_annot_type(fz_context * ctx,pdf_annot * annot)535 pdf_annot_type(fz_context *ctx, pdf_annot *annot)
536 {
537 pdf_obj *obj = annot->obj;
538 pdf_obj *subtype = pdf_dict_get(ctx, obj, PDF_NAME(Subtype));
539 return pdf_annot_type_from_string(ctx, pdf_to_name(ctx, subtype));
540 }
541
542 int
pdf_annot_flags(fz_context * ctx,pdf_annot * annot)543 pdf_annot_flags(fz_context *ctx, pdf_annot *annot)
544 {
545 return pdf_dict_get_int(ctx, annot->obj, PDF_NAME(F));
546 }
547
548 void
pdf_set_annot_flags(fz_context * ctx,pdf_annot * annot,int flags)549 pdf_set_annot_flags(fz_context *ctx, pdf_annot *annot, int flags)
550 {
551 pdf_dict_put_int(ctx, annot->obj, PDF_NAME(F), flags);
552 pdf_dirty_annot(ctx, annot);
553 }
554
555 fz_rect
pdf_annot_rect(fz_context * ctx,pdf_annot * annot)556 pdf_annot_rect(fz_context *ctx, pdf_annot *annot)
557 {
558 fz_matrix page_ctm;
559 fz_rect annot_rect;
560 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
561 annot_rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
562 return fz_transform_rect(annot_rect, page_ctm);
563 }
564
565 void
pdf_set_annot_rect(fz_context * ctx,pdf_annot * annot,fz_rect rect)566 pdf_set_annot_rect(fz_context *ctx, pdf_annot *annot, fz_rect rect)
567 {
568 fz_matrix page_ctm, inv_page_ctm;
569
570 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
571 inv_page_ctm = fz_invert_matrix(page_ctm);
572 rect = fz_transform_rect(rect, inv_page_ctm);
573
574 pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(Rect), rect);
575 pdf_dirty_annot(ctx, annot);
576 }
577
578 const char *
pdf_annot_contents(fz_context * ctx,pdf_annot * annot)579 pdf_annot_contents(fz_context *ctx, pdf_annot *annot)
580 {
581 return pdf_dict_get_text_string(ctx, annot->obj, PDF_NAME(Contents));
582 }
583
584 void
pdf_set_annot_contents(fz_context * ctx,pdf_annot * annot,const char * text)585 pdf_set_annot_contents(fz_context *ctx, pdf_annot *annot, const char *text)
586 {
587 pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(Contents), text);
588 pdf_dict_del(ctx, annot->obj, PDF_NAME(RC)); /* not supported */
589 pdf_dirty_annot(ctx, annot);
590 }
591
592 int
pdf_annot_has_open(fz_context * ctx,pdf_annot * annot)593 pdf_annot_has_open(fz_context *ctx, pdf_annot *annot)
594 {
595 pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
596 pdf_obj *popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
597 return (subtype == PDF_NAME(Text) || popup);
598 }
599
600 int
pdf_annot_is_open(fz_context * ctx,pdf_annot * annot)601 pdf_annot_is_open(fz_context *ctx, pdf_annot *annot)
602 {
603 pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
604 pdf_obj *popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
605 if (popup)
606 return pdf_dict_get_bool(ctx, popup, PDF_NAME(Open));
607 else if (subtype == PDF_NAME(Text))
608 return pdf_dict_get_bool(ctx, annot->obj, PDF_NAME(Open));
609 return 0;
610 }
611
612 void
pdf_set_annot_is_open(fz_context * ctx,pdf_annot * annot,int is_open)613 pdf_set_annot_is_open(fz_context *ctx, pdf_annot *annot, int is_open)
614 {
615 pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
616 pdf_obj *popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
617 if (popup)
618 {
619 pdf_dict_put_bool(ctx, popup, PDF_NAME(Open), is_open);
620 pdf_dirty_annot(ctx, annot);
621 }
622 else if (subtype == PDF_NAME(Text))
623 {
624 pdf_dict_put_bool(ctx, annot->obj, PDF_NAME(Open), is_open);
625 pdf_dirty_annot(ctx, annot);
626 }
627 }
628
629 static pdf_obj *icon_name_subtypes[] = {
630 PDF_NAME(FileAttachment),
631 PDF_NAME(Sound),
632 PDF_NAME(Stamp),
633 PDF_NAME(Text),
634 NULL,
635 };
636
637 int
pdf_annot_has_icon_name(fz_context * ctx,pdf_annot * annot)638 pdf_annot_has_icon_name(fz_context *ctx, pdf_annot *annot)
639 {
640 return is_allowed_subtype(ctx, annot, PDF_NAME(Name), icon_name_subtypes);
641 }
642
643 const char *
pdf_annot_icon_name(fz_context * ctx,pdf_annot * annot)644 pdf_annot_icon_name(fz_context *ctx, pdf_annot *annot)
645 {
646 pdf_obj *name;
647 check_allowed_subtypes(ctx, annot, PDF_NAME(Name), icon_name_subtypes);
648 name = pdf_dict_get(ctx, annot->obj, PDF_NAME(Name));
649 if (!name)
650 {
651 pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
652 if (pdf_name_eq(ctx, subtype, PDF_NAME(Text)))
653 return "Note";
654 if (pdf_name_eq(ctx, subtype, PDF_NAME(Stamp)))
655 return "Draft";
656 if (pdf_name_eq(ctx, subtype, PDF_NAME(FileAttachment)))
657 return "PushPin";
658 if (pdf_name_eq(ctx, subtype, PDF_NAME(Sound)))
659 return "Speaker";
660 }
661 return pdf_to_name(ctx, name);
662 }
663
664 void
pdf_set_annot_icon_name(fz_context * ctx,pdf_annot * annot,const char * name)665 pdf_set_annot_icon_name(fz_context *ctx, pdf_annot *annot, const char *name)
666 {
667 check_allowed_subtypes(ctx, annot, PDF_NAME(Name), icon_name_subtypes);
668 pdf_dict_put_name(ctx, annot->obj, PDF_NAME(Name), name);
669 pdf_dirty_annot(ctx, annot);
670 }
671
pdf_line_ending_from_name(fz_context * ctx,pdf_obj * end)672 enum pdf_line_ending pdf_line_ending_from_name(fz_context *ctx, pdf_obj *end)
673 {
674 if (pdf_name_eq(ctx, end, PDF_NAME(None))) return PDF_ANNOT_LE_NONE;
675 else if (pdf_name_eq(ctx, end, PDF_NAME(Square))) return PDF_ANNOT_LE_SQUARE;
676 else if (pdf_name_eq(ctx, end, PDF_NAME(Circle))) return PDF_ANNOT_LE_CIRCLE;
677 else if (pdf_name_eq(ctx, end, PDF_NAME(Diamond))) return PDF_ANNOT_LE_DIAMOND;
678 else if (pdf_name_eq(ctx, end, PDF_NAME(OpenArrow))) return PDF_ANNOT_LE_OPEN_ARROW;
679 else if (pdf_name_eq(ctx, end, PDF_NAME(ClosedArrow))) return PDF_ANNOT_LE_CLOSED_ARROW;
680 else if (pdf_name_eq(ctx, end, PDF_NAME(Butt))) return PDF_ANNOT_LE_BUTT;
681 else if (pdf_name_eq(ctx, end, PDF_NAME(ROpenArrow))) return PDF_ANNOT_LE_R_OPEN_ARROW;
682 else if (pdf_name_eq(ctx, end, PDF_NAME(RClosedArrow))) return PDF_ANNOT_LE_R_CLOSED_ARROW;
683 else if (pdf_name_eq(ctx, end, PDF_NAME(Slash))) return PDF_ANNOT_LE_SLASH;
684 else return PDF_ANNOT_LE_NONE;
685 }
686
pdf_line_ending_from_string(fz_context * ctx,const char * end)687 enum pdf_line_ending pdf_line_ending_from_string(fz_context *ctx, const char *end)
688 {
689 if (!strcmp(end, "None")) return PDF_ANNOT_LE_NONE;
690 else if (!strcmp(end, "Square")) return PDF_ANNOT_LE_SQUARE;
691 else if (!strcmp(end, "Circle")) return PDF_ANNOT_LE_CIRCLE;
692 else if (!strcmp(end, "Diamond")) return PDF_ANNOT_LE_DIAMOND;
693 else if (!strcmp(end, "OpenArrow")) return PDF_ANNOT_LE_OPEN_ARROW;
694 else if (!strcmp(end, "ClosedArrow")) return PDF_ANNOT_LE_CLOSED_ARROW;
695 else if (!strcmp(end, "Butt")) return PDF_ANNOT_LE_BUTT;
696 else if (!strcmp(end, "ROpenArrow")) return PDF_ANNOT_LE_R_OPEN_ARROW;
697 else if (!strcmp(end, "RClosedArrow")) return PDF_ANNOT_LE_R_CLOSED_ARROW;
698 else if (!strcmp(end, "Slash")) return PDF_ANNOT_LE_SLASH;
699 else return PDF_ANNOT_LE_NONE;
700 }
701
pdf_name_from_line_ending(fz_context * ctx,enum pdf_line_ending end)702 pdf_obj *pdf_name_from_line_ending(fz_context *ctx, enum pdf_line_ending end)
703 {
704 switch (end)
705 {
706 default:
707 case PDF_ANNOT_LE_NONE: return PDF_NAME(None);
708 case PDF_ANNOT_LE_SQUARE: return PDF_NAME(Square);
709 case PDF_ANNOT_LE_CIRCLE: return PDF_NAME(Circle);
710 case PDF_ANNOT_LE_DIAMOND: return PDF_NAME(Diamond);
711 case PDF_ANNOT_LE_OPEN_ARROW: return PDF_NAME(OpenArrow);
712 case PDF_ANNOT_LE_CLOSED_ARROW: return PDF_NAME(ClosedArrow);
713 case PDF_ANNOT_LE_BUTT: return PDF_NAME(Butt);
714 case PDF_ANNOT_LE_R_OPEN_ARROW: return PDF_NAME(ROpenArrow);
715 case PDF_ANNOT_LE_R_CLOSED_ARROW: return PDF_NAME(RClosedArrow);
716 case PDF_ANNOT_LE_SLASH: return PDF_NAME(Slash);
717 }
718 }
719
pdf_string_from_line_ending(fz_context * ctx,enum pdf_line_ending end)720 const char *pdf_string_from_line_ending(fz_context *ctx, enum pdf_line_ending end)
721 {
722 switch (end)
723 {
724 default:
725 case PDF_ANNOT_LE_NONE: return "None";
726 case PDF_ANNOT_LE_SQUARE: return "Square";
727 case PDF_ANNOT_LE_CIRCLE: return "Circle";
728 case PDF_ANNOT_LE_DIAMOND: return "Diamond";
729 case PDF_ANNOT_LE_OPEN_ARROW: return "OpenArrow";
730 case PDF_ANNOT_LE_CLOSED_ARROW: return "ClosedArrow";
731 case PDF_ANNOT_LE_BUTT: return "Butt";
732 case PDF_ANNOT_LE_R_OPEN_ARROW: return "ROpenArrow";
733 case PDF_ANNOT_LE_R_CLOSED_ARROW: return "RClosedArrow";
734 case PDF_ANNOT_LE_SLASH: return "Slash";
735 }
736 }
737
738 static pdf_obj *line_ending_subtypes[] = {
739 PDF_NAME(FreeText),
740 PDF_NAME(Line),
741 PDF_NAME(PolyLine),
742 PDF_NAME(Polygon),
743 NULL,
744 };
745
746 int
pdf_annot_has_line_ending_styles(fz_context * ctx,pdf_annot * annot)747 pdf_annot_has_line_ending_styles(fz_context *ctx, pdf_annot *annot)
748 {
749 return is_allowed_subtype(ctx, annot, PDF_NAME(LE), line_ending_subtypes);
750 }
751
752 void
pdf_annot_line_ending_styles(fz_context * ctx,pdf_annot * annot,enum pdf_line_ending * start_style,enum pdf_line_ending * end_style)753 pdf_annot_line_ending_styles(fz_context *ctx, pdf_annot *annot,
754 enum pdf_line_ending *start_style,
755 enum pdf_line_ending *end_style)
756 {
757 pdf_obj *style;
758 check_allowed_subtypes(ctx, annot, PDF_NAME(LE), line_ending_subtypes);
759 style = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE));
760 *start_style = pdf_line_ending_from_name(ctx, pdf_array_get(ctx, style, 0));
761 *end_style = pdf_line_ending_from_name(ctx, pdf_array_get(ctx, style, 1));
762 }
763
764 enum pdf_line_ending
pdf_annot_line_start_style(fz_context * ctx,pdf_annot * annot)765 pdf_annot_line_start_style(fz_context *ctx, pdf_annot *annot)
766 {
767 pdf_obj *le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE));
768 return pdf_line_ending_from_name(ctx, pdf_array_get(ctx, le, 0));
769 }
770
771 enum pdf_line_ending
pdf_annot_line_end_style(fz_context * ctx,pdf_annot * annot)772 pdf_annot_line_end_style(fz_context *ctx, pdf_annot *annot)
773 {
774 pdf_obj *le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE));
775 return pdf_line_ending_from_name(ctx, pdf_array_get(ctx, le, 1));
776 }
777
778 void
pdf_set_annot_line_ending_styles(fz_context * ctx,pdf_annot * annot,enum pdf_line_ending start_style,enum pdf_line_ending end_style)779 pdf_set_annot_line_ending_styles(fz_context *ctx, pdf_annot *annot,
780 enum pdf_line_ending start_style,
781 enum pdf_line_ending end_style)
782 {
783 pdf_document *doc = annot->page->doc;
784 pdf_obj *style;
785 check_allowed_subtypes(ctx, annot, PDF_NAME(LE), line_ending_subtypes);
786 style = pdf_new_array(ctx, doc, 2);
787 pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(LE), style);
788 pdf_array_put_drop(ctx, style, 0, pdf_name_from_line_ending(ctx, start_style));
789 pdf_array_put_drop(ctx, style, 1, pdf_name_from_line_ending(ctx, end_style));
790 pdf_dirty_annot(ctx, annot);
791 }
792
793 void
pdf_set_annot_line_start_style(fz_context * ctx,pdf_annot * annot,enum pdf_line_ending s)794 pdf_set_annot_line_start_style(fz_context *ctx, pdf_annot *annot, enum pdf_line_ending s)
795 {
796 enum pdf_line_ending e = pdf_annot_line_end_style(ctx, annot);
797 pdf_set_annot_line_ending_styles(ctx, annot, s, e);
798 }
799
800 void
pdf_set_annot_line_end_style(fz_context * ctx,pdf_annot * annot,enum pdf_line_ending e)801 pdf_set_annot_line_end_style(fz_context *ctx, pdf_annot *annot, enum pdf_line_ending e)
802 {
803 enum pdf_line_ending s = pdf_annot_line_start_style(ctx, annot);
804 pdf_set_annot_line_ending_styles(ctx, annot, s, e);
805 }
806
807 float
pdf_annot_border(fz_context * ctx,pdf_annot * annot)808 pdf_annot_border(fz_context *ctx, pdf_annot *annot)
809 {
810 pdf_obj *bs, *bs_w, *border;
811 bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS));
812 bs_w = pdf_dict_get(ctx, bs, PDF_NAME(W));
813 if (pdf_is_number(ctx, bs_w))
814 return pdf_to_real(ctx, bs_w);
815 border = pdf_dict_get(ctx, annot->obj, PDF_NAME(Border));
816 bs_w = pdf_array_get(ctx, border, 2);
817 if (pdf_is_number(ctx, bs_w))
818 return pdf_to_real(ctx, bs_w);
819 return 1;
820 }
821
822 void
pdf_set_annot_border(fz_context * ctx,pdf_annot * annot,float w)823 pdf_set_annot_border(fz_context *ctx, pdf_annot *annot, float w)
824 {
825 pdf_obj *bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS));
826 if (!pdf_is_dict(ctx, bs))
827 bs = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(BS), 1);
828 pdf_dict_put_real(ctx, bs, PDF_NAME(W), w);
829
830 pdf_dict_del(ctx, annot->obj, PDF_NAME(Border)); /* deprecated */
831 pdf_dict_del(ctx, annot->obj, PDF_NAME(BE)); /* not supported */
832
833 pdf_dirty_annot(ctx, annot);
834 }
835
836 fz_text_language
pdf_document_language(fz_context * ctx,pdf_document * doc)837 pdf_document_language(fz_context *ctx, pdf_document *doc)
838 {
839 pdf_obj *trailer = pdf_trailer(ctx, doc);
840 pdf_obj *root = pdf_dict_get(ctx, trailer, PDF_NAME(Root));
841 pdf_obj *lang = pdf_dict_get(ctx, root, PDF_NAME(Lang));
842 return fz_text_language_from_string(pdf_to_text_string(ctx, lang));
843 }
844
pdf_set_document_language(fz_context * ctx,pdf_document * doc,fz_text_language lang)845 void pdf_set_document_language(fz_context *ctx, pdf_document *doc, fz_text_language lang)
846 {
847 pdf_obj *trailer = pdf_trailer(ctx, doc);
848 pdf_obj *root = pdf_dict_get(ctx, trailer, PDF_NAME(Root));
849 char buf[8];
850 if (lang == FZ_LANG_UNSET)
851 pdf_dict_del(ctx, root, PDF_NAME(Lang));
852 else
853 pdf_dict_put_text_string(ctx, root, PDF_NAME(Lang), fz_string_from_text_language(buf, lang));
854 }
855
856 fz_text_language
pdf_annot_language(fz_context * ctx,pdf_annot * annot)857 pdf_annot_language(fz_context *ctx, pdf_annot *annot)
858 {
859 pdf_obj *lang = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(Lang));
860 if (lang)
861 return fz_text_language_from_string(pdf_to_str_buf(ctx, lang));
862 return pdf_document_language(ctx, annot->page->doc);
863 }
864
865 void
pdf_set_annot_language(fz_context * ctx,pdf_annot * annot,fz_text_language lang)866 pdf_set_annot_language(fz_context *ctx, pdf_annot *annot, fz_text_language lang)
867 {
868 char buf[8];
869 if (lang == FZ_LANG_UNSET)
870 pdf_dict_del(ctx, annot->obj, PDF_NAME(Lang));
871 else
872 pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(Lang), fz_string_from_text_language(buf, lang));
873 pdf_dirty_annot(ctx, annot);
874 }
875
876 int
pdf_annot_quadding(fz_context * ctx,pdf_annot * annot)877 pdf_annot_quadding(fz_context *ctx, pdf_annot *annot)
878 {
879 int q = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(Q));
880 return (q < 0 || q > 2) ? 0 : q;
881 }
882
883 void
pdf_set_annot_quadding(fz_context * ctx,pdf_annot * annot,int q)884 pdf_set_annot_quadding(fz_context *ctx, pdf_annot *annot, int q)
885 {
886 q = (q < 0 || q > 2) ? 0 : q;
887 pdf_dict_put_int(ctx, annot->obj, PDF_NAME(Q), q);
888 pdf_dirty_annot(ctx, annot);
889 }
890
pdf_annot_opacity(fz_context * ctx,pdf_annot * annot)891 float pdf_annot_opacity(fz_context *ctx, pdf_annot *annot)
892 {
893 pdf_obj *ca = pdf_dict_get(ctx, annot->obj, PDF_NAME(CA));
894 if (pdf_is_number(ctx, ca))
895 return pdf_to_real(ctx, ca);
896 return 1;
897 }
898
pdf_set_annot_opacity(fz_context * ctx,pdf_annot * annot,float opacity)899 void pdf_set_annot_opacity(fz_context *ctx, pdf_annot *annot, float opacity)
900 {
901 if (opacity != 1)
902 pdf_dict_put_real(ctx, annot->obj, PDF_NAME(CA), opacity);
903 else
904 pdf_dict_del(ctx, annot->obj, PDF_NAME(CA));
905 pdf_dirty_annot(ctx, annot);
906 }
907
pdf_annot_color_imp(fz_context * ctx,pdf_obj * arr,int * n,float color[4])908 static void pdf_annot_color_imp(fz_context *ctx, pdf_obj *arr, int *n, float color[4])
909 {
910 switch (pdf_array_len(ctx, arr))
911 {
912 case 0:
913 if (n)
914 *n = 0;
915 break;
916 case 1:
917 case 2:
918 if (n)
919 *n = 1;
920 if (color)
921 color[0] = pdf_array_get_real(ctx, arr, 0);
922 break;
923 case 3:
924 if (n)
925 *n = 3;
926 if (color)
927 {
928 color[0] = pdf_array_get_real(ctx, arr, 0);
929 color[1] = pdf_array_get_real(ctx, arr, 1);
930 color[2] = pdf_array_get_real(ctx, arr, 2);
931 }
932 break;
933 case 4:
934 default:
935 if (n)
936 *n = 4;
937 if (color)
938 {
939 color[0] = pdf_array_get_real(ctx, arr, 0);
940 color[1] = pdf_array_get_real(ctx, arr, 1);
941 color[2] = pdf_array_get_real(ctx, arr, 2);
942 color[3] = pdf_array_get_real(ctx, arr, 3);
943 }
944 break;
945 }
946 }
947
pdf_annot_color_rgb(fz_context * ctx,pdf_obj * arr,float rgb[3])948 static int pdf_annot_color_rgb(fz_context *ctx, pdf_obj *arr, float rgb[3])
949 {
950 float color[4];
951 int n;
952 pdf_annot_color_imp(ctx, arr, &n, color);
953 if (n == 0)
954 {
955 return 0;
956 }
957 else if (n == 1)
958 {
959 rgb[0] = rgb[1] = rgb[2] = color[0];
960 }
961 else if (n == 3)
962 {
963 rgb[0] = color[0];
964 rgb[1] = color[1];
965 rgb[2] = color[2];
966 }
967 else if (n == 4)
968 {
969 rgb[0] = 1 - fz_min(1, color[0] + color[3]);
970 rgb[1] = 1 - fz_min(1, color[1] + color[3]);
971 rgb[2] = 1 - fz_min(1, color[2] + color[3]);
972 }
973 return 1;
974 }
975
pdf_set_annot_color_imp(fz_context * ctx,pdf_annot * annot,pdf_obj * key,int n,const float * color,pdf_obj ** allowed)976 static void pdf_set_annot_color_imp(fz_context *ctx, pdf_annot *annot, pdf_obj *key, int n, const float *color, pdf_obj **allowed)
977 {
978 pdf_document *doc = annot->page->doc;
979 pdf_obj *arr;
980
981 if (allowed)
982 check_allowed_subtypes(ctx, annot, key, allowed);
983 if (n != 0 && n != 1 && n != 3 && n != 4)
984 fz_throw(ctx, FZ_ERROR_GENERIC, "color must be 0, 1, 3 or 4 components");
985 if (!color)
986 fz_throw(ctx, FZ_ERROR_GENERIC, "no color given");
987
988 arr = pdf_new_array(ctx, doc, n);
989 fz_try(ctx)
990 {
991 switch (n)
992 {
993 case 1:
994 pdf_array_push_real(ctx, arr, color[0]);
995 break;
996 case 3:
997 pdf_array_push_real(ctx, arr, color[0]);
998 pdf_array_push_real(ctx, arr, color[1]);
999 pdf_array_push_real(ctx, arr, color[2]);
1000 break;
1001 case 4:
1002 pdf_array_push_real(ctx, arr, color[0]);
1003 pdf_array_push_real(ctx, arr, color[1]);
1004 pdf_array_push_real(ctx, arr, color[2]);
1005 pdf_array_push_real(ctx, arr, color[3]);
1006 break;
1007 }
1008 }
1009 fz_catch(ctx)
1010 {
1011 pdf_drop_obj(ctx, arr);
1012 fz_rethrow(ctx);
1013 }
1014
1015 pdf_dict_put_drop(ctx, annot->obj, key, arr);
1016 pdf_dirty_annot(ctx, annot);
1017 }
1018
1019 void
pdf_annot_color(fz_context * ctx,pdf_annot * annot,int * n,float color[4])1020 pdf_annot_color(fz_context *ctx, pdf_annot *annot, int *n, float color[4])
1021 {
1022 pdf_obj *c = pdf_dict_get(ctx, annot->obj, PDF_NAME(C));
1023 pdf_annot_color_imp(ctx, c, n, color);
1024 }
1025
1026 void
pdf_annot_MK_BG(fz_context * ctx,pdf_annot * annot,int * n,float color[4])1027 pdf_annot_MK_BG(fz_context *ctx, pdf_annot *annot, int *n, float color[4])
1028 {
1029 pdf_obj *mk_bg = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BG));
1030 pdf_annot_color_imp(ctx, mk_bg, n, color);
1031 }
1032
1033 int
pdf_annot_MK_BG_rgb(fz_context * ctx,pdf_annot * annot,float rgb[3])1034 pdf_annot_MK_BG_rgb(fz_context *ctx, pdf_annot *annot, float rgb[3])
1035 {
1036 pdf_obj *mk_bg = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BG));
1037 return pdf_annot_color_rgb(ctx, mk_bg, rgb);
1038 }
1039
1040 void
pdf_annot_MK_BC(fz_context * ctx,pdf_annot * annot,int * n,float color[4])1041 pdf_annot_MK_BC(fz_context *ctx, pdf_annot *annot, int *n, float color[4])
1042 {
1043 pdf_obj *mk_bc = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BC));
1044 pdf_annot_color_imp(ctx, mk_bc, n, color);
1045 }
1046
1047 int
pdf_annot_MK_BC_rgb(fz_context * ctx,pdf_annot * annot,float rgb[3])1048 pdf_annot_MK_BC_rgb(fz_context *ctx, pdf_annot *annot, float rgb[3])
1049 {
1050 pdf_obj *mk_bc = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BC));
1051 return pdf_annot_color_rgb(ctx, mk_bc, rgb);
1052 }
1053
1054 void
pdf_set_annot_color(fz_context * ctx,pdf_annot * annot,int n,const float * color)1055 pdf_set_annot_color(fz_context *ctx, pdf_annot *annot, int n, const float *color)
1056 {
1057 pdf_set_annot_color_imp(ctx, annot, PDF_NAME(C), n, color, NULL);
1058 }
1059
1060 static pdf_obj *interior_color_subtypes[] = {
1061 PDF_NAME(Circle),
1062 PDF_NAME(Line),
1063 PDF_NAME(PolyLine),
1064 PDF_NAME(Polygon),
1065 PDF_NAME(Square),
1066 NULL,
1067 };
1068
1069 int
pdf_annot_has_interior_color(fz_context * ctx,pdf_annot * annot)1070 pdf_annot_has_interior_color(fz_context *ctx, pdf_annot *annot)
1071 {
1072 return is_allowed_subtype(ctx, annot, PDF_NAME(IC), interior_color_subtypes);
1073 }
1074
1075 void
pdf_annot_interior_color(fz_context * ctx,pdf_annot * annot,int * n,float color[4])1076 pdf_annot_interior_color(fz_context *ctx, pdf_annot *annot, int *n, float color[4])
1077 {
1078 pdf_obj *ic = pdf_dict_get(ctx, annot->obj, PDF_NAME(IC));
1079 pdf_annot_color_imp(ctx, ic, n, color);
1080 }
1081
1082 void
pdf_set_annot_interior_color(fz_context * ctx,pdf_annot * annot,int n,const float * color)1083 pdf_set_annot_interior_color(fz_context *ctx, pdf_annot *annot, int n, const float *color)
1084 {
1085 pdf_set_annot_color_imp(ctx, annot, PDF_NAME(IC), n, color, interior_color_subtypes);
1086 }
1087
1088 static pdf_obj *line_subtypes[] = {
1089 PDF_NAME(Line),
1090 NULL,
1091 };
1092
1093 int
pdf_annot_has_line(fz_context * ctx,pdf_annot * annot)1094 pdf_annot_has_line(fz_context *ctx, pdf_annot *annot)
1095 {
1096 return is_allowed_subtype(ctx, annot, PDF_NAME(L), line_subtypes);
1097 }
1098
1099 void
pdf_annot_line(fz_context * ctx,pdf_annot * annot,fz_point * a,fz_point * b)1100 pdf_annot_line(fz_context *ctx, pdf_annot *annot, fz_point *a, fz_point *b)
1101 {
1102 fz_matrix page_ctm;
1103 pdf_obj *line;
1104
1105 check_allowed_subtypes(ctx, annot, PDF_NAME(L), line_subtypes);
1106
1107 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1108
1109 line = pdf_dict_get(ctx, annot->obj, PDF_NAME(L));
1110 a->x = pdf_array_get_real(ctx, line, 0);
1111 a->y = pdf_array_get_real(ctx, line, 1);
1112 b->x = pdf_array_get_real(ctx, line, 2);
1113 b->y = pdf_array_get_real(ctx, line, 3);
1114 *a = fz_transform_point(*a, page_ctm);
1115 *b = fz_transform_point(*b, page_ctm);
1116 }
1117
1118 void
pdf_set_annot_line(fz_context * ctx,pdf_annot * annot,fz_point a,fz_point b)1119 pdf_set_annot_line(fz_context *ctx, pdf_annot *annot, fz_point a, fz_point b)
1120 {
1121 fz_matrix page_ctm, inv_page_ctm;
1122 pdf_obj *line;
1123
1124 check_allowed_subtypes(ctx, annot, PDF_NAME(L), line_subtypes);
1125
1126 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1127 inv_page_ctm = fz_invert_matrix(page_ctm);
1128
1129 a = fz_transform_point(a, inv_page_ctm);
1130 b = fz_transform_point(b, inv_page_ctm);
1131
1132 line = pdf_new_array(ctx, annot->page->doc, 4);
1133 pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(L), line);
1134 pdf_array_push_real(ctx, line, a.x);
1135 pdf_array_push_real(ctx, line, a.y);
1136 pdf_array_push_real(ctx, line, b.x);
1137 pdf_array_push_real(ctx, line, b.y);
1138
1139 pdf_dirty_annot(ctx, annot);
1140 }
1141
1142 static pdf_obj *vertices_subtypes[] = {
1143 PDF_NAME(PolyLine),
1144 PDF_NAME(Polygon),
1145 NULL,
1146 };
1147
1148 int
pdf_annot_has_vertices(fz_context * ctx,pdf_annot * annot)1149 pdf_annot_has_vertices(fz_context *ctx, pdf_annot *annot)
1150 {
1151 return is_allowed_subtype(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
1152 }
1153
1154 int
pdf_annot_vertex_count(fz_context * ctx,pdf_annot * annot)1155 pdf_annot_vertex_count(fz_context *ctx, pdf_annot *annot)
1156 {
1157 pdf_obj *vertices;
1158 check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
1159 vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices));
1160 return pdf_array_len(ctx, vertices) / 2;
1161 }
1162
1163 fz_point
pdf_annot_vertex(fz_context * ctx,pdf_annot * annot,int i)1164 pdf_annot_vertex(fz_context *ctx, pdf_annot *annot, int i)
1165 {
1166 pdf_obj *vertices;
1167 fz_matrix page_ctm;
1168 fz_point point;
1169
1170 check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
1171
1172 vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices));
1173
1174 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1175
1176 point.x = pdf_array_get_real(ctx, vertices, i * 2);
1177 point.y = pdf_array_get_real(ctx, vertices, i * 2 + 1);
1178 return fz_transform_point(point, page_ctm);
1179 }
1180
1181 void
pdf_set_annot_vertices(fz_context * ctx,pdf_annot * annot,int n,const fz_point * v)1182 pdf_set_annot_vertices(fz_context *ctx, pdf_annot *annot, int n, const fz_point *v)
1183 {
1184 pdf_document *doc = annot->page->doc;
1185 fz_matrix page_ctm, inv_page_ctm;
1186 pdf_obj *vertices;
1187 fz_point point;
1188 int i;
1189
1190 check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
1191 if (n <= 0 || !v)
1192 fz_throw(ctx, FZ_ERROR_GENERIC, "invalid number of vertices");
1193
1194 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1195 inv_page_ctm = fz_invert_matrix(page_ctm);
1196
1197 vertices = pdf_new_array(ctx, doc, n * 2);
1198 for (i = 0; i < n; ++i)
1199 {
1200 point = fz_transform_point(v[i], inv_page_ctm);
1201 pdf_array_push_real(ctx, vertices, point.x);
1202 pdf_array_push_real(ctx, vertices, point.y);
1203 }
1204 pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(Vertices), vertices);
1205 pdf_dirty_annot(ctx, annot);
1206 }
1207
pdf_clear_annot_vertices(fz_context * ctx,pdf_annot * annot)1208 void pdf_clear_annot_vertices(fz_context *ctx, pdf_annot *annot)
1209 {
1210 check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
1211 pdf_dict_del(ctx, annot->obj, PDF_NAME(Vertices));
1212 pdf_dirty_annot(ctx, annot);
1213 }
1214
pdf_add_annot_vertex(fz_context * ctx,pdf_annot * annot,fz_point p)1215 void pdf_add_annot_vertex(fz_context *ctx, pdf_annot *annot, fz_point p)
1216 {
1217 pdf_document *doc = annot->page->doc;
1218 fz_matrix page_ctm, inv_page_ctm;
1219 pdf_obj *vertices;
1220
1221 check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
1222
1223 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1224 inv_page_ctm = fz_invert_matrix(page_ctm);
1225
1226 vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices));
1227 if (!pdf_is_array(ctx, vertices))
1228 {
1229 vertices = pdf_new_array(ctx, doc, 32);
1230 pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(Vertices), vertices);
1231 }
1232
1233 p = fz_transform_point(p, inv_page_ctm);
1234 pdf_array_push_real(ctx, vertices, p.x);
1235 pdf_array_push_real(ctx, vertices, p.y);
1236
1237 pdf_dirty_annot(ctx, annot);
1238 }
1239
pdf_set_annot_vertex(fz_context * ctx,pdf_annot * annot,int i,fz_point p)1240 void pdf_set_annot_vertex(fz_context *ctx, pdf_annot *annot, int i, fz_point p)
1241 {
1242 fz_matrix page_ctm, inv_page_ctm;
1243 pdf_obj *vertices;
1244
1245 check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
1246
1247 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1248 inv_page_ctm = fz_invert_matrix(page_ctm);
1249
1250 p = fz_transform_point(p, inv_page_ctm);
1251
1252 vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices));
1253 pdf_array_put_drop(ctx, vertices, i * 2 + 0, pdf_new_real(ctx, p.x));
1254 pdf_array_put_drop(ctx, vertices, i * 2 + 1, pdf_new_real(ctx, p.y));
1255 }
1256
1257 static pdf_obj *quad_point_subtypes[] = {
1258 PDF_NAME(Highlight),
1259 PDF_NAME(Link),
1260 PDF_NAME(Squiggly),
1261 PDF_NAME(StrikeOut),
1262 PDF_NAME(Underline),
1263 PDF_NAME(Redact),
1264 NULL,
1265 };
1266
1267 int
pdf_annot_has_quad_points(fz_context * ctx,pdf_annot * annot)1268 pdf_annot_has_quad_points(fz_context *ctx, pdf_annot *annot)
1269 {
1270 return is_allowed_subtype(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
1271 }
1272
1273 int
pdf_annot_quad_point_count(fz_context * ctx,pdf_annot * annot)1274 pdf_annot_quad_point_count(fz_context *ctx, pdf_annot *annot)
1275 {
1276 pdf_obj *quad_points;
1277 check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
1278 quad_points = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
1279 return pdf_array_len(ctx, quad_points) / 8;
1280 }
1281
1282 fz_quad
pdf_annot_quad_point(fz_context * ctx,pdf_annot * annot,int idx)1283 pdf_annot_quad_point(fz_context *ctx, pdf_annot *annot, int idx)
1284 {
1285 pdf_obj *quad_points;
1286 fz_matrix page_ctm;
1287 float v[8];
1288 int i;
1289
1290 check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
1291 quad_points = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
1292 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1293
1294 for (i = 0; i < 8; i += 2)
1295 {
1296 fz_point point;
1297 point.x = pdf_array_get_real(ctx, quad_points, idx * 8 + i + 0);
1298 point.y = pdf_array_get_real(ctx, quad_points, idx * 8 + i + 1);
1299 point = fz_transform_point(point, page_ctm);
1300 v[i+0] = point.x;
1301 v[i+1] = point.y;
1302 }
1303
1304 return fz_make_quad(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
1305 }
1306
1307 void
pdf_set_annot_quad_points(fz_context * ctx,pdf_annot * annot,int n,const fz_quad * q)1308 pdf_set_annot_quad_points(fz_context *ctx, pdf_annot *annot, int n, const fz_quad *q)
1309 {
1310 pdf_document *doc = annot->page->doc;
1311 fz_matrix page_ctm, inv_page_ctm;
1312 pdf_obj *quad_points;
1313 fz_quad quad;
1314 int i;
1315
1316 check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
1317 if (n <= 0 || !q)
1318 fz_throw(ctx, FZ_ERROR_GENERIC, "invalid number of quadrilaterals");
1319
1320 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1321 inv_page_ctm = fz_invert_matrix(page_ctm);
1322
1323 quad_points = pdf_new_array(ctx, doc, n);
1324 for (i = 0; i < n; ++i)
1325 {
1326 quad = fz_transform_quad(q[i], inv_page_ctm);
1327 pdf_array_push_real(ctx, quad_points, quad.ul.x);
1328 pdf_array_push_real(ctx, quad_points, quad.ul.y);
1329 pdf_array_push_real(ctx, quad_points, quad.ur.x);
1330 pdf_array_push_real(ctx, quad_points, quad.ur.y);
1331 pdf_array_push_real(ctx, quad_points, quad.ll.x);
1332 pdf_array_push_real(ctx, quad_points, quad.ll.y);
1333 pdf_array_push_real(ctx, quad_points, quad.lr.x);
1334 pdf_array_push_real(ctx, quad_points, quad.lr.y);
1335 }
1336 pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(QuadPoints), quad_points);
1337 pdf_dirty_annot(ctx, annot);
1338 }
1339
1340 void
pdf_clear_annot_quad_points(fz_context * ctx,pdf_annot * annot)1341 pdf_clear_annot_quad_points(fz_context *ctx, pdf_annot *annot)
1342 {
1343 check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
1344 pdf_dict_del(ctx, annot->obj, PDF_NAME(QuadPoints));
1345 pdf_dirty_annot(ctx, annot);
1346 }
1347
1348 void
pdf_add_annot_quad_point(fz_context * ctx,pdf_annot * annot,fz_quad quad)1349 pdf_add_annot_quad_point(fz_context *ctx, pdf_annot *annot, fz_quad quad)
1350 {
1351 pdf_document *doc = annot->page->doc;
1352 fz_matrix page_ctm, inv_page_ctm;
1353 pdf_obj *quad_points;
1354
1355 check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
1356
1357 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1358 inv_page_ctm = fz_invert_matrix(page_ctm);
1359
1360 quad_points = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
1361 if (!pdf_is_array(ctx, quad_points))
1362 {
1363 quad_points = pdf_new_array(ctx, doc, 8);
1364 pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(QuadPoints), quad_points);
1365 }
1366
1367 /* Contrary to the specification, the points within a QuadPoint are NOT ordered
1368 * in a counterclockwise fashion. Experiments with Adobe's implementation
1369 * indicates a cross-wise ordering is intended: ul, ur, ll, lr.
1370 */
1371 quad = fz_transform_quad(quad, inv_page_ctm);
1372 pdf_array_push_real(ctx, quad_points, quad.ul.x);
1373 pdf_array_push_real(ctx, quad_points, quad.ul.y);
1374 pdf_array_push_real(ctx, quad_points, quad.ur.x);
1375 pdf_array_push_real(ctx, quad_points, quad.ur.y);
1376 pdf_array_push_real(ctx, quad_points, quad.ll.x);
1377 pdf_array_push_real(ctx, quad_points, quad.ll.y);
1378 pdf_array_push_real(ctx, quad_points, quad.lr.x);
1379 pdf_array_push_real(ctx, quad_points, quad.lr.y);
1380
1381 pdf_dirty_annot(ctx, annot);
1382 }
1383
1384 static pdf_obj *ink_list_subtypes[] = {
1385 PDF_NAME(Ink),
1386 NULL,
1387 };
1388
1389 int
pdf_annot_has_ink_list(fz_context * ctx,pdf_annot * annot)1390 pdf_annot_has_ink_list(fz_context *ctx, pdf_annot *annot)
1391 {
1392 return is_allowed_subtype(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
1393 }
1394
1395 int
pdf_annot_ink_list_count(fz_context * ctx,pdf_annot * annot)1396 pdf_annot_ink_list_count(fz_context *ctx, pdf_annot *annot)
1397 {
1398 pdf_obj *ink_list;
1399 check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
1400 ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
1401 return pdf_array_len(ctx, ink_list);
1402 }
1403
1404 int
pdf_annot_ink_list_stroke_count(fz_context * ctx,pdf_annot * annot,int i)1405 pdf_annot_ink_list_stroke_count(fz_context *ctx, pdf_annot *annot, int i)
1406 {
1407 pdf_obj *ink_list;
1408 pdf_obj *stroke;
1409 check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
1410 ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
1411 stroke = pdf_array_get(ctx, ink_list, i);
1412 return pdf_array_len(ctx, stroke) / 2;
1413 }
1414
1415 fz_point
pdf_annot_ink_list_stroke_vertex(fz_context * ctx,pdf_annot * annot,int i,int k)1416 pdf_annot_ink_list_stroke_vertex(fz_context *ctx, pdf_annot *annot, int i, int k)
1417 {
1418 pdf_obj *ink_list;
1419 pdf_obj *stroke;
1420 fz_matrix page_ctm;
1421 fz_point point;
1422
1423 check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
1424
1425 ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
1426 stroke = pdf_array_get(ctx, ink_list, i);
1427
1428 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1429
1430 point.x = pdf_array_get_real(ctx, stroke, k * 2 + 0);
1431 point.y = pdf_array_get_real(ctx, stroke, k * 2 + 1);
1432 return fz_transform_point(point, page_ctm);
1433 }
1434
1435 /* FIXME: try/catch required for memory exhaustion */
1436 void
pdf_set_annot_ink_list(fz_context * ctx,pdf_annot * annot,int n,const int * count,const fz_point * v)1437 pdf_set_annot_ink_list(fz_context *ctx, pdf_annot *annot, int n, const int *count, const fz_point *v)
1438 {
1439 pdf_document *doc = annot->page->doc;
1440 fz_matrix page_ctm, inv_page_ctm;
1441 pdf_obj *ink_list, *stroke;
1442 fz_point point;
1443 int i, k;
1444
1445 check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
1446
1447 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1448 inv_page_ctm = fz_invert_matrix(page_ctm);
1449
1450 // TODO: update Rect (in update appearance perhaps?)
1451
1452 ink_list = pdf_new_array(ctx, doc, n);
1453 for (i = 0; i < n; ++i)
1454 {
1455 stroke = pdf_new_array(ctx, doc, count[i] * 2);
1456 for (k = 0; k < count[i]; ++k)
1457 {
1458 point = fz_transform_point(*v++, inv_page_ctm);
1459 pdf_array_push_real(ctx, stroke, point.x);
1460 pdf_array_push_real(ctx, stroke, point.y);
1461 }
1462 pdf_array_push_drop(ctx, ink_list, stroke);
1463 }
1464 pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(InkList), ink_list);
1465 pdf_dirty_annot(ctx, annot);
1466 }
1467
1468 void
pdf_clear_annot_ink_list(fz_context * ctx,pdf_annot * annot)1469 pdf_clear_annot_ink_list(fz_context *ctx, pdf_annot *annot)
1470 {
1471 pdf_dict_del(ctx, annot->obj, PDF_NAME(InkList));
1472 pdf_dirty_annot(ctx, annot);
1473 }
1474
pdf_add_annot_ink_list_stroke(fz_context * ctx,pdf_annot * annot)1475 void pdf_add_annot_ink_list_stroke(fz_context *ctx, pdf_annot *annot)
1476 {
1477 pdf_obj *ink_list;
1478
1479 ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
1480 if (!pdf_is_array(ctx, ink_list))
1481 ink_list = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(InkList), 10);
1482
1483 pdf_array_push_array(ctx, ink_list, 16);
1484
1485 pdf_dirty_annot(ctx, annot);
1486 }
1487
pdf_add_annot_ink_list_stroke_vertex(fz_context * ctx,pdf_annot * annot,fz_point p)1488 void pdf_add_annot_ink_list_stroke_vertex(fz_context *ctx, pdf_annot *annot, fz_point p)
1489 {
1490 fz_matrix page_ctm, inv_page_ctm;
1491 pdf_obj *ink_list, *stroke;
1492
1493 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1494 inv_page_ctm = fz_invert_matrix(page_ctm);
1495
1496 ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
1497 stroke = pdf_array_get(ctx, ink_list, pdf_array_len(ctx, ink_list)-1);
1498
1499 p = fz_transform_point(p, inv_page_ctm);
1500 pdf_array_push_real(ctx, stroke, p.x);
1501 pdf_array_push_real(ctx, stroke, p.y);
1502
1503 pdf_dirty_annot(ctx, annot);
1504 }
1505
1506 void
pdf_add_annot_ink_list(fz_context * ctx,pdf_annot * annot,int n,fz_point p[])1507 pdf_add_annot_ink_list(fz_context *ctx, pdf_annot *annot, int n, fz_point p[])
1508 {
1509 fz_matrix page_ctm, inv_page_ctm;
1510 pdf_obj *ink_list, *stroke;
1511 int i;
1512
1513 check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
1514
1515 pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1516 inv_page_ctm = fz_invert_matrix(page_ctm);
1517
1518 ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
1519 if (!pdf_is_array(ctx, ink_list))
1520 ink_list = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(InkList), 10);
1521
1522 stroke = pdf_array_push_array(ctx, ink_list, n * 2);
1523 for (i = 0; i < n; ++i)
1524 {
1525 fz_point tp = fz_transform_point(p[i], inv_page_ctm);
1526 pdf_array_push_real(ctx, stroke, tp.x);
1527 pdf_array_push_real(ctx, stroke, tp.y);
1528 }
1529
1530 pdf_dirty_annot(ctx, annot);
1531 }
1532
1533 static pdf_obj *markup_subtypes[] = {
1534 PDF_NAME(Text),
1535 PDF_NAME(FreeText),
1536 PDF_NAME(Line),
1537 PDF_NAME(Square),
1538 PDF_NAME(Circle),
1539 PDF_NAME(Polygon),
1540 PDF_NAME(PolyLine),
1541 PDF_NAME(Highlight),
1542 PDF_NAME(Underline),
1543 PDF_NAME(Squiggly),
1544 PDF_NAME(StrikeOut),
1545 PDF_NAME(Redact),
1546 PDF_NAME(Stamp),
1547 PDF_NAME(Caret),
1548 PDF_NAME(Ink),
1549 PDF_NAME(FileAttachment),
1550 PDF_NAME(Sound),
1551 NULL,
1552 };
1553
1554 /*
1555 Get annotation's modification date in seconds since the epoch.
1556 */
1557 int64_t
pdf_annot_modification_date(fz_context * ctx,pdf_annot * annot)1558 pdf_annot_modification_date(fz_context *ctx, pdf_annot *annot)
1559 {
1560 return pdf_dict_get_date(ctx, annot->obj, PDF_NAME(M));
1561 }
1562
1563 /*
1564 Get annotation's creation date in seconds since the epoch.
1565 */
1566 int64_t
pdf_annot_creation_date(fz_context * ctx,pdf_annot * annot)1567 pdf_annot_creation_date(fz_context *ctx, pdf_annot *annot)
1568 {
1569 return pdf_dict_get_date(ctx, annot->obj, PDF_NAME(CreationDate));
1570 }
1571
1572 /*
1573 Set annotation's modification date in seconds since the epoch.
1574 */
1575 void
pdf_set_annot_modification_date(fz_context * ctx,pdf_annot * annot,int64_t secs)1576 pdf_set_annot_modification_date(fz_context *ctx, pdf_annot *annot, int64_t secs)
1577 {
1578 check_allowed_subtypes(ctx, annot, PDF_NAME(M), markup_subtypes);
1579 pdf_dict_put_date(ctx, annot->obj, PDF_NAME(M), secs);
1580 pdf_dirty_annot(ctx, annot);
1581 }
1582
1583 /*
1584 Set annotation's creation date in seconds since the epoch.
1585 */
1586 void
pdf_set_annot_creation_date(fz_context * ctx,pdf_annot * annot,int64_t secs)1587 pdf_set_annot_creation_date(fz_context *ctx, pdf_annot *annot, int64_t secs)
1588 {
1589 check_allowed_subtypes(ctx, annot, PDF_NAME(CreationDate), markup_subtypes);
1590 pdf_dict_put_date(ctx, annot->obj, PDF_NAME(CreationDate), secs);
1591 pdf_dirty_annot(ctx, annot);
1592 }
1593
1594 int
pdf_annot_has_author(fz_context * ctx,pdf_annot * annot)1595 pdf_annot_has_author(fz_context *ctx, pdf_annot *annot)
1596 {
1597 return is_allowed_subtype(ctx, annot, PDF_NAME(T), markup_subtypes);
1598 }
1599
1600 const char *
pdf_annot_author(fz_context * ctx,pdf_annot * annot)1601 pdf_annot_author(fz_context *ctx, pdf_annot *annot)
1602 {
1603 check_allowed_subtypes(ctx, annot, PDF_NAME(T), markup_subtypes);
1604 return pdf_dict_get_text_string(ctx, annot->obj, PDF_NAME(T));
1605 }
1606
1607 void
pdf_set_annot_author(fz_context * ctx,pdf_annot * annot,const char * author)1608 pdf_set_annot_author(fz_context *ctx, pdf_annot *annot, const char *author)
1609 {
1610 check_allowed_subtypes(ctx, annot, PDF_NAME(T), markup_subtypes);
1611 pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(T), author);
1612 pdf_dirty_annot(ctx, annot);
1613 }
1614
1615 void
pdf_parse_default_appearance(fz_context * ctx,const char * da,const char ** font,float * size,float color[3])1616 pdf_parse_default_appearance(fz_context *ctx, const char *da, const char **font, float *size, float color[3])
1617 {
1618 char buf[100], *p = buf, *tok, *end;
1619 float stack[3] = { 0, 0, 0 };
1620 int top = 0;
1621
1622 *font = "Helv";
1623 *size = 12;
1624 color[0] = color[1] = color[2] = 0;
1625
1626 fz_strlcpy(buf, da, sizeof buf);
1627 while ((tok = fz_strsep(&p, " \n\r\t")) != NULL)
1628 {
1629 if (tok[0] == 0)
1630 ;
1631 else if (tok[0] == '/')
1632 {
1633 if (!strcmp(tok+1, "Cour")) *font = "Cour";
1634 if (!strcmp(tok+1, "Helv")) *font = "Helv";
1635 if (!strcmp(tok+1, "TiRo")) *font = "TiRo";
1636 if (!strcmp(tok+1, "Symb")) *font = "Symb";
1637 if (!strcmp(tok+1, "ZaDb")) *font = "ZaDb";
1638 }
1639 else if (!strcmp(tok, "Tf"))
1640 {
1641 *size = stack[0];
1642 top = 0;
1643 }
1644 else if (!strcmp(tok, "g"))
1645 {
1646 color[0] = color[1] = color[2] = stack[0];
1647 top = 0;
1648 }
1649 else if (!strcmp(tok, "rg"))
1650 {
1651 color[0] = stack[0];
1652 color[1] = stack[1];
1653 color[2] = stack[2];
1654 top=0;
1655 }
1656 else
1657 {
1658 if (top < 3)
1659 stack[top] = fz_strtof(tok, &end);
1660 if (*end == 0)
1661 ++top;
1662 else
1663 top = 0;
1664 }
1665 }
1666 }
1667
1668 void
pdf_print_default_appearance(fz_context * ctx,char * buf,int nbuf,const char * font,float size,const float color[3])1669 pdf_print_default_appearance(fz_context *ctx, char *buf, int nbuf, const char *font, float size, const float color[3])
1670 {
1671 if (color[0] > 0 || color[1] > 0 || color[2] > 0)
1672 fz_snprintf(buf, nbuf, "/%s %g Tf %g %g %g rg", font, size, color[0], color[1], color[2]);
1673 else
1674 fz_snprintf(buf, nbuf, "/%s %g Tf", font, size);
1675 }
1676
1677 void
pdf_annot_default_appearance(fz_context * ctx,pdf_annot * annot,const char ** font,float * size,float color[3])1678 pdf_annot_default_appearance(fz_context *ctx, pdf_annot *annot, const char **font, float *size, float color[3])
1679 {
1680 pdf_obj *da = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(DA));
1681 if (!da)
1682 {
1683 pdf_obj *trailer = pdf_trailer(ctx, annot->page->doc);
1684 da = pdf_dict_getl(ctx, trailer, PDF_NAME(Root), PDF_NAME(AcroForm), PDF_NAME(DA), NULL);
1685 }
1686 pdf_parse_default_appearance(ctx, pdf_to_str_buf(ctx, da), font, size, color);
1687 }
1688
1689 void
pdf_set_annot_default_appearance(fz_context * ctx,pdf_annot * annot,const char * font,float size,const float color[3])1690 pdf_set_annot_default_appearance(fz_context *ctx, pdf_annot *annot, const char *font, float size, const float color[3])
1691 {
1692 char buf[100];
1693
1694 pdf_print_default_appearance(ctx, buf, sizeof buf, font, size, color);
1695
1696 pdf_dict_put_string(ctx, annot->obj, PDF_NAME(DA), buf, strlen(buf));
1697
1698 pdf_dict_del(ctx, annot->obj, PDF_NAME(DS)); /* not supported */
1699 pdf_dict_del(ctx, annot->obj, PDF_NAME(RC)); /* not supported */
1700
1701 pdf_dirty_annot(ctx, annot);
1702 }
1703