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