1#include "mupdf/fitz.h"
2#include "mupdf/pdf.h"
3
4#include <string.h>
5
6/* Must be kept in sync with definitions in pdf_util.js */
7enum
8{
9 Display_Visible,
10 Display_Hidden,
11 Display_NoPrint,
12 Display_NoView
13};
14
15enum
16{
17 SigFlag_SignaturesExist = 1,
18 SigFlag_AppendOnly = 2
19};
20
21const char *pdf_field_value(fz_context *ctx, pdf_obj *field)
22{
23 pdf_obj *v = pdf_dict_get_inheritable(ctx, field, PDF_NAME(V));
24 if (pdf_is_name(ctx, v))
25 return pdf_to_name(ctx, v);
26 if (pdf_is_stream(ctx, v))
27 {
28 // FIXME: pdf_dict_put_inheritable...
29 char *str = pdf_new_utf8_from_pdf_stream_obj(ctx, v);
30 fz_try(ctx)
31 pdf_dict_put_text_string(ctx, field, PDF_NAME(V), str);
32 fz_always(ctx)
33 fz_free(ctx, str);
34 fz_catch(ctx)
35 fz_rethrow(ctx);
36 v = pdf_dict_get(ctx, field, PDF_NAME(V));
37 }
38 return pdf_to_text_string(ctx, v);
39}
40
41int pdf_field_flags(fz_context *ctx, pdf_obj *obj)
42{
43 return pdf_to_int(ctx, pdf_dict_get_inheritable(ctx, obj, PDF_NAME(Ff)));
44}
45
46int pdf_field_type(fz_context *ctx, pdf_obj *obj)
47{
48 pdf_obj *type = pdf_dict_get_inheritable(ctx, obj, PDF_NAME(FT));
49 int flags = pdf_field_flags(ctx, obj);
50 if (pdf_name_eq(ctx, type, PDF_NAME(Btn)))
51 {
52 if (flags & PDF_BTN_FIELD_IS_PUSHBUTTON)
53 return PDF_WIDGET_TYPE_BUTTON;
54 else if (flags & PDF_BTN_FIELD_IS_RADIO)
55 return PDF_WIDGET_TYPE_RADIOBUTTON;
56 else
57 return PDF_WIDGET_TYPE_CHECKBOX;
58 }
59 else if (pdf_name_eq(ctx, type, PDF_NAME(Tx)))
60 return PDF_WIDGET_TYPE_TEXT;
61 else if (pdf_name_eq(ctx, type, PDF_NAME(Ch)))
62 {
63 if (flags & PDF_CH_FIELD_IS_COMBO)
64 return PDF_WIDGET_TYPE_COMBOBOX;
65 else
66 return PDF_WIDGET_TYPE_LISTBOX;
67 }
68 else if (pdf_name_eq(ctx, type, PDF_NAME(Sig)))
69 return PDF_WIDGET_TYPE_SIGNATURE;
70 else
71 return PDF_WIDGET_TYPE_BUTTON;
72}
73
74static int pdf_field_dirties_document(fz_context *ctx, pdf_document *doc, pdf_obj *field)
75{
76 int ff = pdf_field_flags(ctx, field);
77 if (ff & PDF_FIELD_IS_NO_EXPORT) return 0;
78 if (ff & PDF_FIELD_IS_READ_ONLY) return 0;
79 return 1;
80}
81
82/* Find the point in a field hierarchy where all descendants
83 * share the same name */
84static pdf_obj *find_head_of_field_group(fz_context *ctx, pdf_obj *obj)
85{
86 if (obj == NULL || pdf_dict_get(ctx, obj, PDF_NAME(T)))
87 return obj;
88 else
89 return find_head_of_field_group(ctx, pdf_dict_get(ctx, obj, PDF_NAME(Parent)));
90}
91
92static void pdf_field_mark_dirty(fz_context *ctx, pdf_obj *field)
93{
94 pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME(Kids));
95 if (kids)
96 {
97 int i, n = pdf_array_len(ctx, kids);
98 for (i = 0; i < n; i++)
99 pdf_field_mark_dirty(ctx, pdf_array_get(ctx, kids, i));
100 }
101 pdf_dirty_obj(ctx, field);
102}
103
104static void update_field_value(fz_context *ctx, pdf_document *doc, pdf_obj *obj, const char *text)
105{
106 pdf_obj *grp;
107
108 if (!text)
109 text = "";
110
111 /* All fields of the same name should be updated, so
112 * set the value at the head of the group */
113 grp = find_head_of_field_group(ctx, obj);
114 if (grp)
115 obj = grp;
116
117 pdf_dict_put_text_string(ctx, obj, PDF_NAME(V), text);
118
119 pdf_field_mark_dirty(ctx, obj);
120}
121
122static pdf_obj *find_field(fz_context *ctx, pdf_obj *dict, const char *name, int len)
123{
124 int i, n = pdf_array_len(ctx, dict);
125 for (i = 0; i < n; i++)
126 {
127 pdf_obj *field = pdf_array_get(ctx, dict, i);
128 const char *part = pdf_dict_get_text_string(ctx, field, PDF_NAME(T));
129 if (strlen(part) == (size_t)len && !memcmp(part, name, len))
130 return field;
131 }
132 return NULL;
133}
134
135pdf_obj *pdf_lookup_field(fz_context *ctx, pdf_obj *form, const char *name)
136{
137 const char *dot;
138 const char *namep;
139 pdf_obj *dict = NULL;
140 int len;
141
142 /* Process the fully qualified field name which has
143 * the partial names delimited by '.'. Pretend there
144 * was a preceding '.' to simplify the loop */
145 dot = name - 1;
146
147 while (dot && form)
148 {
149 namep = dot + 1;
150 dot = strchr(namep, '.');
151 len = dot ? dot - namep : (int)strlen(namep);
152 dict = find_field(ctx, form, namep, len);
153 if (dot)
154 form = pdf_dict_get(ctx, dict, PDF_NAME(Kids));
155 }
156
157 return dict;
158}
159
160static void reset_form_field(fz_context *ctx, pdf_document *doc, pdf_obj *field)
161{
162 /* Set V to DV wherever DV is present, and delete V where DV is not.
163 * FIXME: we assume for now that V has not been set unequal
164 * to DV higher in the hierarchy than "field".
165 *
166 * At the bottom of the hierarchy we may find widget annotations
167 * that aren't also fields, but DV and V will not be present in their
168 * dictionaries, and attempts to remove V will be harmless. */
169 pdf_obj *dv = pdf_dict_get(ctx, field, PDF_NAME(DV));
170 pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME(Kids));
171
172 if (dv)
173 pdf_dict_put(ctx, field, PDF_NAME(V), dv);
174 else
175 pdf_dict_del(ctx, field, PDF_NAME(V));
176
177 if (kids == NULL)
178 {
179 /* The leaves of the tree are widget annotations
180 * In some cases we need to update the appearance state;
181 * in others we need to mark the field as dirty so that
182 * the appearance stream will be regenerated. */
183 switch (pdf_field_type(ctx, field))
184 {
185 case PDF_WIDGET_TYPE_CHECKBOX:
186 case PDF_WIDGET_TYPE_RADIOBUTTON:
187 {
188 pdf_obj *leafv = pdf_dict_get_inheritable(ctx, field, PDF_NAME(V));
189 if (!leafv)
190 leafv = PDF_NAME(Off);
191 pdf_dict_put(ctx, field, PDF_NAME(AS), leafv);
192 }
193 pdf_field_mark_dirty(ctx, field);
194 break;
195
196 case PDF_WIDGET_TYPE_BUTTON:
197 case PDF_WIDGET_TYPE_SIGNATURE:
198 /* Pushbuttons and signatures have no value to reset. */
199 break;
200
201 default:
202 pdf_field_mark_dirty(ctx, field);
203 break;
204 }
205 }
206
207 if (pdf_field_dirties_document(ctx, doc, field))
208 doc->dirty = 1;
209}
210
211void pdf_field_reset(fz_context *ctx, pdf_document *doc, pdf_obj *field)
212{
213 pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME(Kids));
214
215 reset_form_field(ctx, doc, field);
216
217 if (kids)
218 {
219 int i, n = pdf_array_len(ctx, kids);
220
221 for (i = 0; i < n; i++)
222 pdf_field_reset(ctx, doc, pdf_array_get(ctx, kids, i));
223 }
224}
225
226static void add_field_hierarchy_to_array(fz_context *ctx, pdf_obj *array, pdf_obj *field)
227{
228 pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME(Kids));
229 pdf_obj *exclude = pdf_dict_get(ctx, field, PDF_NAME(Exclude));
230
231 if (exclude)
232 return;
233
234 pdf_array_push(ctx, array, field);
235
236 if (kids)
237 {
238 int i, n = pdf_array_len(ctx, kids);
239
240 for (i = 0; i < n; i++)
241 add_field_hierarchy_to_array(ctx, array, pdf_array_get(ctx, kids, i));
242 }
243}
244
245/*
246 When resetting or submitting a form, the fields to act upon are defined
247 by an array of either field references or field names, plus a flag determining
248 whether to act upon the fields in the array, or all fields other than those in
249 the array. specified_fields interprets this information and produces the array
250 of fields to be acted upon.
251*/
252static pdf_obj *specified_fields(fz_context *ctx, pdf_document *doc, pdf_obj *fields, int exclude)
253{
254 pdf_obj *form = pdf_dict_getl(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root), PDF_NAME(AcroForm), PDF_NAME(Fields), NULL);
255 int i, n;
256 pdf_obj *result = pdf_new_array(ctx, doc, 0);
257
258 fz_try(ctx)
259 {
260 /* The 'fields' array not being present signals that all fields
261 * should be acted upon, so handle it using the exclude case - excluding none */
262 if (exclude || !fields)
263 {
264 /* mark the fields we don't want to act upon */
265 n = pdf_array_len(ctx, fields);
266 for (i = 0; i < n; i++)
267 {
268 pdf_obj *field = pdf_array_get(ctx, fields, i);
269
270 if (pdf_is_string(ctx, field))
271 field = pdf_lookup_field(ctx, form, pdf_to_str_buf(ctx, field));
272
273 if (field)
274 pdf_dict_put(ctx, field, PDF_NAME(Exclude), PDF_NULL);
275 }
276
277 /* Act upon all unmarked fields */
278 n = pdf_array_len(ctx, form);
279
280 for (i = 0; i < n; i++)
281 add_field_hierarchy_to_array(ctx, result, pdf_array_get(ctx, form, i));
282
283 /* Unmark the marked fields */
284 n = pdf_array_len(ctx, fields);
285
286 for (i = 0; i < n; i++)
287 {
288 pdf_obj *field = pdf_array_get(ctx, fields, i);
289
290 if (pdf_is_string(ctx, field))
291 field = pdf_lookup_field(ctx, form, pdf_to_str_buf(ctx, field));
292
293 if (field)
294 pdf_dict_del(ctx, field, PDF_NAME(Exclude));
295 }
296 }
297 else
298 {
299 n = pdf_array_len(ctx, fields);
300
301 for (i = 0; i < n; i++)
302 {
303 pdf_obj *field = pdf_array_get(ctx, fields, i);
304
305 if (pdf_is_string(ctx, field))
306 field = pdf_lookup_field(ctx, form, pdf_to_str_buf(ctx, field));
307
308 if (field)
309 add_field_hierarchy_to_array(ctx, result, field);
310 }
311 }
312 }
313 fz_catch(ctx)
314 {
315 pdf_drop_obj(ctx, result);
316 fz_rethrow(ctx);
317 }
318
319 return result;
320}
321
322void pdf_reset_form(fz_context *ctx, pdf_document *doc, pdf_obj *fields, int exclude)
323{
324 pdf_obj *sfields = specified_fields(ctx, doc, fields, exclude);
325 fz_try(ctx)
326 {
327 int i, n = pdf_array_len(ctx, sfields);
328 for (i = 0; i < n; i++)
329 reset_form_field(ctx, doc, pdf_array_get(ctx, sfields, i));
330 doc->recalculate = 1;
331 }
332 fz_always(ctx)
333 pdf_drop_obj(ctx, sfields);
334 fz_catch(ctx)
335 fz_rethrow(ctx);
336}
337
338static void set_check(fz_context *ctx, pdf_document *doc, pdf_obj *chk, pdf_obj *name)
339{
340 pdf_obj *n = pdf_dict_getp(ctx, chk, "AP/N");
341 pdf_obj *val;
342
343 /* If name is a possible value of this check
344 * box then use it, otherwise use "Off" */
345 if (pdf_dict_get(ctx, n, name))
346 val = name;
347 else
348 val = PDF_NAME(Off);
349
350 pdf_dict_put(ctx, chk, PDF_NAME(AS), val);
351}
352
353/* Set the values of all fields in a group defined by a node
354 * in the hierarchy */
355static void set_check_grp(fz_context *ctx, pdf_document *doc, pdf_obj *grp, pdf_obj *val)
356{
357 pdf_obj *kids = pdf_dict_get(ctx, grp, PDF_NAME(Kids));
358
359 if (kids == NULL)
360 {
361 set_check(ctx, doc, grp, val);
362 }
363 else
364 {
365 int i, n = pdf_array_len(ctx, kids);
366
367 for (i = 0; i < n; i++)
368 set_check_grp(ctx, doc, pdf_array_get(ctx, kids, i), val);
369 }
370}
371
372void pdf_calculate_form(fz_context *ctx, pdf_document *doc)
373{
374 if (doc->js)
375 {
376 fz_try(ctx)
377 {
378 pdf_obj *co = pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/AcroForm/CO");
379 int i, n = pdf_array_len(ctx, co);
380 for (i = 0; i < n; i++)
381 {
382 pdf_obj *field = pdf_array_get(ctx, co, i);
383 pdf_field_event_calculate(ctx, doc, field);
384 }
385 }
386 fz_always(ctx)
387 doc->recalculate = 0;
388 fz_catch(ctx)
389 fz_rethrow(ctx);
390 }
391}
392
393static pdf_obj *find_on_state(fz_context *ctx, pdf_obj *dict)
394{
395 int i, n = pdf_dict_len(ctx, dict);
396 for (i = 0; i < n; ++i)
397 {
398 pdf_obj *key = pdf_dict_get_key(ctx, dict, i);
399 if (key != PDF_NAME(Off))
400 return key;
401 }
402 return NULL;
403}
404
405pdf_obj *pdf_button_field_on_state(fz_context *ctx, pdf_obj *field)
406{
407 pdf_obj *ap = pdf_dict_get(ctx, field, PDF_NAME(AP));
408 pdf_obj *on = find_on_state(ctx, pdf_dict_get(ctx, ap, PDF_NAME(N)));
409 if (!on) on = find_on_state(ctx, pdf_dict_get(ctx, ap, PDF_NAME(D)));
410 if (!on) on = PDF_NAME(Yes);
411 return on;
412}
413
414static void toggle_check_box(fz_context *ctx, pdf_document *doc, pdf_obj *field)
415{
416 int ff = pdf_field_flags(ctx, field);
417 int is_radio = (ff & PDF_BTN_FIELD_IS_RADIO);
418 int is_no_toggle_to_off = (ff & PDF_BTN_FIELD_IS_NO_TOGGLE_TO_OFF);
419 pdf_obj *grp, *as, *val;
420
421 grp = find_head_of_field_group(ctx, field);
422 if (!grp)
423 grp = field;
424
425 /* TODO: check V value as well as or instead of AS? */
426 as = pdf_dict_get(ctx, field, PDF_NAME(AS));
427 if (as && as != PDF_NAME(Off))
428 {
429 if (is_radio && is_no_toggle_to_off)
430 return;
431 val = PDF_NAME(Off);
432 }
433 else
434 {
435 val = pdf_button_field_on_state(ctx, field);
436 }
437
438 pdf_dict_put(ctx, grp, PDF_NAME(V), val);
439 set_check_grp(ctx, doc, grp, val);
440 doc->recalculate = 1;
441}
442
443/*
444 Determine whether changes have been made since the
445 document was opened or last saved.
446*/
447int pdf_has_unsaved_changes(fz_context *ctx, pdf_document *doc)
448{
449 return doc->dirty;
450}
451
452/*
453 Toggle the state of a specified annotation. Applies only to check-box
454 and radio-button widgets.
455*/
456int pdf_toggle_widget(fz_context *ctx, pdf_widget *widget)
457{
458 switch (pdf_widget_type(ctx, widget))
459 {
460 default:
461 return 0;
462 case PDF_WIDGET_TYPE_CHECKBOX:
463 case PDF_WIDGET_TYPE_RADIOBUTTON:
464 toggle_check_box(ctx, widget->page->doc, widget->obj);
465 return 1;
466 }
467 return 0;
468}
469
470/*
471 Recalculate form fields if necessary.
472
473 Loop through all annotations on the page and update them. Return true
474 if any of them were changed (by either event or javascript actions, or
475 by annotation editing) and need re-rendering.
476
477 If you need more granularity, loop through the annotations and call
478 pdf_update_annot for each one to detect changes on a per-annotation
479 basis.
480*/
481int
482pdf_update_page(fz_context *ctx, pdf_page *page)
483{
484 pdf_annot *annot;
485 pdf_widget *widget;
486 int changed = 0;
487
488 if (page->doc->recalculate)
489 pdf_calculate_form(ctx, page->doc);
490
491 for (annot = page->annots; annot; annot = annot->next)
492 if (pdf_update_annot(ctx, annot))
493 changed = 1;
494 for (widget = page->widgets; widget; widget = widget->next)
495 if (pdf_update_annot(ctx, widget))
496 changed = 1;
497
498 return changed;
499}
500
501pdf_widget *pdf_first_widget(fz_context *ctx, pdf_page *page)
502{
503 return page->widgets;
504}
505
506pdf_widget *pdf_next_widget(fz_context *ctx, pdf_widget *widget)
507{
508 return widget->next;
509}
510
511enum pdf_widget_type pdf_widget_type(fz_context *ctx, pdf_widget *widget)
512{
513 pdf_obj *subtype = pdf_dict_get(ctx, widget->obj, PDF_NAME(Subtype));
514 if (pdf_name_eq(ctx, subtype, PDF_NAME(Widget)))
515 return pdf_field_type(ctx, widget->obj);
516 return PDF_WIDGET_TYPE_BUTTON;
517}
518
519static int set_validated_field_value(fz_context *ctx, pdf_document *doc, pdf_obj *field, const char *text, int ignore_trigger_events)
520{
521 if (!ignore_trigger_events)
522 {
523 if (!pdf_field_event_validate(ctx, doc, field, text))
524 return 0;
525 }
526
527 if (pdf_field_dirties_document(ctx, doc, field))
528 doc->dirty = 1;
529 update_field_value(ctx, doc, field, text);
530
531 return 1;
532}
533
534static void update_checkbox_selector(fz_context *ctx, pdf_document *doc, pdf_obj *field, const char *val)
535{
536 pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME(Kids));
537
538 if (kids)
539 {
540 int i, n = pdf_array_len(ctx, kids);
541
542 for (i = 0; i < n; i++)
543 update_checkbox_selector(ctx, doc, pdf_array_get(ctx, kids, i), val);
544 }
545 else
546 {
547 pdf_obj *n = pdf_dict_getp(ctx, field, "AP/N");
548 pdf_obj *oval;
549
550 if (pdf_dict_gets(ctx, n, val))
551 oval = pdf_new_name(ctx, val);
552 else
553 oval = PDF_NAME(Off);
554 pdf_dict_put_drop(ctx, field, PDF_NAME(AS), oval);
555 }
556}
557
558static int set_checkbox_value(fz_context *ctx, pdf_document *doc, pdf_obj *field, const char *val)
559{
560 update_checkbox_selector(ctx, doc, field, val);
561 update_field_value(ctx, doc, field, val);
562 return 1;
563}
564
565int pdf_set_field_value(fz_context *ctx, pdf_document *doc, pdf_obj *field, const char *text, int ignore_trigger_events)
566{
567 int accepted = 0;
568
569 switch (pdf_field_type(ctx, field))
570 {
571 case PDF_WIDGET_TYPE_TEXT:
572 case PDF_WIDGET_TYPE_COMBOBOX:
573 case PDF_WIDGET_TYPE_LISTBOX:
574 accepted = set_validated_field_value(ctx, doc, field, text, ignore_trigger_events);
575 break;
576
577 case PDF_WIDGET_TYPE_CHECKBOX:
578 case PDF_WIDGET_TYPE_RADIOBUTTON:
579 accepted = set_checkbox_value(ctx, doc, field, text);
580 break;
581
582 default:
583 update_field_value(ctx, doc, field, text);
584 accepted = 1;
585 break;
586 }
587
588 if (!ignore_trigger_events)
589 doc->recalculate = 1;
590
591 return accepted;
592}
593
594char *pdf_field_border_style(fz_context *ctx, pdf_obj *field)
595{
596 const char *bs = pdf_to_name(ctx, pdf_dict_getl(ctx, field, PDF_NAME(BS), PDF_NAME(S), NULL));
597 switch (*bs)
598 {
599 case 'S': return "Solid";
600 case 'D': return "Dashed";
601 case 'B': return "Beveled";
602 case 'I': return "Inset";
603 case 'U': return "Underline";
604 }
605 return "Solid";
606}
607
608void pdf_field_set_border_style(fz_context *ctx, pdf_obj *field, const char *text)
609{
610 pdf_obj *val;
611
612 if (!strcmp(text, "Solid"))
613 val = PDF_NAME(S);
614 else if (!strcmp(text, "Dashed"))
615 val = PDF_NAME(D);
616 else if (!strcmp(text, "Beveled"))
617 val = PDF_NAME(B);
618 else if (!strcmp(text, "Inset"))
619 val = PDF_NAME(I);
620 else if (!strcmp(text, "Underline"))
621 val = PDF_NAME(U);
622 else
623 return;
624
625 pdf_dict_putl_drop(ctx, field, val, PDF_NAME(BS), PDF_NAME(S), NULL);
626 pdf_field_mark_dirty(ctx, field);
627}
628
629void pdf_field_set_button_caption(fz_context *ctx, pdf_obj *field, const char *text)
630{
631 if (pdf_field_type(ctx, field) == PDF_WIDGET_TYPE_BUTTON)
632 {
633 pdf_obj *val = pdf_new_text_string(ctx, text);
634 pdf_dict_putl_drop(ctx, field, val, PDF_NAME(MK), PDF_NAME(CA), NULL);
635 pdf_field_mark_dirty(ctx, field);
636 }
637}
638
639int pdf_field_display(fz_context *ctx, pdf_obj *field)
640{
641 pdf_obj *kids;
642 int f, res = Display_Visible;
643
644 /* Base response on first of children. Not ideal,
645 * but not clear how to handle children with
646 * differing values */
647 while ((kids = pdf_dict_get(ctx, field, PDF_NAME(Kids))) != NULL)
648 field = pdf_array_get(ctx, kids, 0);
649
650 f = pdf_dict_get_int(ctx, field, PDF_NAME(F));
651
652 if (f & PDF_ANNOT_IS_HIDDEN)
653 {
654 res = Display_Hidden;
655 }
656 else if (f & PDF_ANNOT_IS_PRINT)
657 {
658 if (f & PDF_ANNOT_IS_NO_VIEW)
659 res = Display_NoView;
660 }
661 else
662 {
663 if (f & PDF_ANNOT_IS_NO_VIEW)
664 res = Display_Hidden;
665 else
666 res = Display_NoPrint;
667 }
668
669 return res;
670}
671
672/*
673 * get the field name in a char buffer that has spare room to
674 * add more characters at the end.
675 */
676static char *get_field_name(fz_context *ctx, pdf_obj *field, int spare)
677{
678 char *res = NULL;
679 pdf_obj *parent = pdf_dict_get(ctx, field, PDF_NAME(Parent));
680 const char *lname = pdf_dict_get_text_string(ctx, field, PDF_NAME(T));
681 int llen = (int)strlen(lname);
682
683 /*
684 * If we found a name at this point in the field hierarchy
685 * then we'll need extra space for it and a dot
686 */
687 if (llen)
688 spare += llen+1;
689
690 if (parent)
691 {
692 res = get_field_name(ctx, parent, spare);
693 }
694 else
695 {
696 res = fz_malloc(ctx, spare+1);
697 res[0] = 0;
698 }
699
700 if (llen)
701 {
702 if (res[0])
703 strcat(res, ".");
704
705 strcat(res, lname);
706 }
707
708 return res;
709}
710
711/* Note: This function allocates a string for the return value that you must free manually. */
712char *pdf_field_name(fz_context *ctx, pdf_obj *field)
713{
714 return get_field_name(ctx, field, 0);
715}
716
717const char *pdf_field_label(fz_context *ctx, pdf_obj *field)
718{
719 pdf_obj *label = pdf_dict_get_inheritable(ctx, field, PDF_NAME(TU));
720 if (!label)
721 label = pdf_dict_get_inheritable(ctx, field, PDF_NAME(T));
722 if (label)
723 return pdf_to_text_string(ctx, label);
724 return "Unnamed";
725}
726
727void pdf_field_set_display(fz_context *ctx, pdf_obj *field, int d)
728{
729 pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME(Kids));
730
731 if (!kids)
732 {
733 int mask = (PDF_ANNOT_IS_HIDDEN|PDF_ANNOT_IS_PRINT|PDF_ANNOT_IS_NO_VIEW);
734 int f = pdf_dict_get_int(ctx, field, PDF_NAME(F)) & ~mask;
735 pdf_obj *fo;
736
737 switch (d)
738 {
739 case Display_Visible:
740 f |= PDF_ANNOT_IS_PRINT;
741 break;
742 case Display_Hidden:
743 f |= PDF_ANNOT_IS_HIDDEN;
744 break;
745 case Display_NoView:
746 f |= (PDF_ANNOT_IS_PRINT|PDF_ANNOT_IS_NO_VIEW);
747 break;
748 case Display_NoPrint:
749 break;
750 }
751
752 fo = pdf_new_int(ctx, f);
753 pdf_dict_put_drop(ctx, field, PDF_NAME(F), fo);
754 }
755 else
756 {
757 int i, n = pdf_array_len(ctx, kids);
758
759 for (i = 0; i < n; i++)
760 pdf_field_set_display(ctx, pdf_array_get(ctx, kids, i), d);
761 }
762}
763
764void pdf_field_set_fill_color(fz_context *ctx, pdf_obj *field, pdf_obj *col)
765{
766 /* col == NULL mean transparent, but we can simply pass it on as with
767 * non-NULL values because pdf_dict_putp interprets a NULL value as
768 * delete */
769 pdf_dict_putl(ctx, field, col, PDF_NAME(MK), PDF_NAME(BG), NULL);
770 pdf_field_mark_dirty(ctx, field);
771}
772
773void pdf_field_set_text_color(fz_context *ctx, pdf_obj *field, pdf_obj *col)
774{
775 char buf[100];
776 const char *font;
777 float size, color[3], black;
778 const char *da = pdf_to_str_buf(ctx, pdf_dict_get_inheritable(ctx, field, PDF_NAME(DA)));
779
780 pdf_parse_default_appearance(ctx, da, &font, &size, color);
781
782 switch (pdf_array_len(ctx, col))
783 {
784 default:
785 color[0] = color[1] = color[2] = 0;
786 break;
787 case 1:
788 color[0] = color[1] = color[2] = pdf_array_get_real(ctx, col, 0);
789 break;
790 case 3:
791 color[0] = pdf_array_get_real(ctx, col, 0);
792 color[1] = pdf_array_get_real(ctx, col, 1);
793 color[2] = pdf_array_get_real(ctx, col, 2);
794 break;
795 case 4:
796 black = pdf_array_get_real(ctx, col, 3);
797 color[0] = 1 - fz_min(1, pdf_array_get_real(ctx, col, 0) + black);
798 color[1] = 1 - fz_min(1, pdf_array_get_real(ctx, col, 1) + black);
799 color[2] = 1 - fz_min(1, pdf_array_get_real(ctx, col, 2) + black);
800 break;
801 }
802
803 pdf_print_default_appearance(ctx, buf, sizeof buf, font, size, color);
804 pdf_dict_put_string(ctx, field, PDF_NAME(DA), buf, strlen(buf));
805 pdf_field_mark_dirty(ctx, field);
806}
807
808pdf_widget *
809pdf_keep_widget(fz_context *ctx, pdf_widget *widget)
810{
811 return pdf_keep_annot(ctx, widget);
812}
813
814void
815pdf_drop_widget(fz_context *ctx, pdf_widget *widget)
816{
817 pdf_drop_annot(ctx, widget);
818}
819
820void
821pdf_drop_widgets(fz_context *ctx, pdf_widget *widget)
822{
823 while (widget)
824 {
825 pdf_widget *next = widget->next;
826 pdf_drop_widget(ctx, widget);
827 widget = next;
828 }
829}
830
831fz_rect
832pdf_bound_widget(fz_context *ctx, pdf_widget *widget)
833{
834 return pdf_bound_annot(ctx, widget);
835}
836
837int
838pdf_update_widget(fz_context *ctx, pdf_widget *widget)
839{
840 return pdf_update_annot(ctx, widget);
841}
842
843/*
844 get the maximum number of
845 characters permitted in a text widget
846*/
847int pdf_text_widget_max_len(fz_context *ctx, pdf_document *doc, pdf_widget *tw)
848{
849 pdf_annot *annot = (pdf_annot *)tw;
850
851 return pdf_to_int(ctx, pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(MaxLen)));
852}
853
854/*
855 get the type of content
856 required by a text widget
857*/
858int pdf_text_widget_format(fz_context *ctx, pdf_document *doc, pdf_widget *tw)
859{
860 pdf_annot *annot = (pdf_annot *)tw;
861 int type = PDF_WIDGET_TX_FORMAT_NONE;
862 pdf_obj *js = pdf_dict_getl(ctx, annot->obj, PDF_NAME(AA), PDF_NAME(F), PDF_NAME(JS), NULL);
863 if (js)
864 {
865 char *code = pdf_load_stream_or_string_as_utf8(ctx, js);
866 if (strstr(code, "AFNumber_Format"))
867 type = PDF_WIDGET_TX_FORMAT_NUMBER;
868 else if (strstr(code, "AFSpecial_Format"))
869 type = PDF_WIDGET_TX_FORMAT_SPECIAL;
870 else if (strstr(code, "AFDate_FormatEx"))
871 type = PDF_WIDGET_TX_FORMAT_DATE;
872 else if (strstr(code, "AFTime_FormatEx"))
873 type = PDF_WIDGET_TX_FORMAT_TIME;
874 fz_free(ctx, code);
875 }
876
877 return type;
878}
879
880/*
881 Update the text of a text widget.
882 The text is first validated by the Field/Keystroke event processing and accepted only if it passes.
883 The function returns whether validation passed.
884*/
885int pdf_set_text_field_value(fz_context *ctx, pdf_widget *widget, const char *new_value)
886{
887 pdf_document *doc = widget->page->doc;
888 pdf_keystroke_event event;
889 char *newChange = NULL;
890 int rc = 1;
891
892 event.newChange = NULL;
893
894 fz_var(newChange);
895 fz_var(event.newChange);
896 fz_try(ctx)
897 {
898 if (!widget->ignore_trigger_events)
899 {
900 event.value = pdf_field_value(ctx, widget->obj);
901 event.change = new_value;
902 event.selStart = 0;
903 event.selEnd = strlen(event.value);
904 event.willCommit = 0;
905 rc = pdf_field_event_keystroke(ctx, doc, widget->obj, &event);
906 if (rc)
907 {
908 if (event.newChange)
909 event.value = newChange = event.newChange;
910 else
911 event.value = new_value;
912 event.change = "";
913 event.selStart = -1;
914 event.selEnd = -1;
915 event.willCommit = 1;
916 event.newChange = NULL;
917 rc = pdf_field_event_keystroke(ctx, doc, widget->obj, &event);
918 if (rc)
919 rc = pdf_set_field_value(ctx, doc, widget->obj, event.value, 0);
920 }
921 }
922 else
923 {
924 rc = pdf_set_field_value(ctx, doc, widget->obj, new_value, 1);
925 }
926 }
927 fz_always(ctx)
928 {
929 fz_free(ctx, newChange);
930 fz_free(ctx, event.newChange);
931 }
932 fz_catch(ctx)
933 {
934 fz_warn(ctx, "could not set widget text");
935 rc = 0;
936 }
937 return rc;
938}
939
940int pdf_set_choice_field_value(fz_context *ctx, pdf_widget *widget, const char *new_value)
941{
942 /* Choice widgets use almost the same keystroke processing as text fields. */
943 return pdf_set_text_field_value(ctx, widget, new_value);
944}
945
946/*
947 get the list of options for a list
948 box or combo box. Returns the number of options and fills in their
949 names within the supplied array. Should first be called with a
950 NULL array to find out how big the array should be. If exportval
951 is true, then the export values will be returned and not the list
952 values if there are export values present.
953*/
954int pdf_choice_widget_options(fz_context *ctx, pdf_document *doc, pdf_widget *tw, int exportval, const char *opts[])
955{
956 pdf_annot *annot = (pdf_annot *)tw;
957 pdf_obj *optarr;
958 int i, n, m;
959
960 optarr = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(Opt));
961 n = pdf_array_len(ctx, optarr);
962
963 if (opts)
964 {
965 for (i = 0; i < n; i++)
966 {
967 m = pdf_array_len(ctx, pdf_array_get(ctx, optarr, i));
968 /* If it is a two element array, the second item is the one that we want if we want the listing value. */
969 if (m == 2)
970 if (exportval)
971 opts[i] = pdf_array_get_text_string(ctx, pdf_array_get(ctx, optarr, i), 0);
972 else
973 opts[i] = pdf_array_get_text_string(ctx, pdf_array_get(ctx, optarr, i), 1);
974 else
975 opts[i] = pdf_array_get_text_string(ctx, optarr, i);
976 }
977 }
978 return n;
979}
980
981int pdf_choice_widget_is_multiselect(fz_context *ctx, pdf_document *doc, pdf_widget *tw)
982{
983 pdf_annot *annot = (pdf_annot *)tw;
984
985 if (!annot) return 0;
986
987 switch (pdf_field_type(ctx, annot->obj))
988 {
989 case PDF_WIDGET_TYPE_LISTBOX:
990 return (pdf_field_flags(ctx, annot->obj) & PDF_CH_FIELD_IS_MULTI_SELECT) != 0;
991 default:
992 return 0;
993 }
994}
995
996/*
997 get the value of a choice widget.
998 Returns the number of options currently selected and fills in
999 the supplied array with their strings. Should first be called
1000 with NULL as the array to find out how big the array need to
1001 be. The filled in elements should not be freed by the caller.
1002*/
1003int pdf_choice_widget_value(fz_context *ctx, pdf_document *doc, pdf_widget *tw, const char *opts[])
1004{
1005 pdf_annot *annot = (pdf_annot *)tw;
1006 pdf_obj *optarr;
1007 int i, n;
1008
1009 if (!annot)
1010 return 0;
1011
1012 optarr = pdf_dict_get(ctx, annot->obj, PDF_NAME(V));
1013
1014 if (pdf_is_string(ctx, optarr))
1015 {
1016 if (opts)
1017 opts[0] = pdf_to_text_string(ctx, optarr);
1018 return 1;
1019 }
1020 else
1021 {
1022 n = pdf_array_len(ctx, optarr);
1023 if (opts)
1024 {
1025 for (i = 0; i < n; i++)
1026 {
1027 pdf_obj *elem = pdf_array_get(ctx, optarr, i);
1028 if (pdf_is_array(ctx, elem))
1029 elem = pdf_array_get(ctx, elem, 1);
1030 opts[i] = pdf_to_text_string(ctx, elem);
1031 }
1032 }
1033 return n;
1034 }
1035}
1036
1037/*
1038 set the value of a choice widget. The
1039 caller should pass the number of options selected and an
1040 array of their names
1041*/
1042void pdf_choice_widget_set_value(fz_context *ctx, pdf_document *doc, pdf_widget *tw, int n, const char *opts[])
1043{
1044 pdf_annot *annot = (pdf_annot *)tw;
1045 pdf_obj *optarr = NULL, *opt;
1046 int i;
1047
1048 if (!annot)
1049 return;
1050
1051 fz_var(optarr);
1052 fz_try(ctx)
1053 {
1054 if (n != 1)
1055 {
1056 optarr = pdf_new_array(ctx, doc, n);
1057
1058 for (i = 0; i < n; i++)
1059 {
1060 opt = pdf_new_text_string(ctx, opts[i]);
1061 pdf_array_push_drop(ctx, optarr, opt);
1062 }
1063
1064 pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(V), optarr);
1065 }
1066 else
1067 {
1068 opt = pdf_new_text_string(ctx, opts[0]);
1069 pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(V), opt);
1070 }
1071
1072 /* FIXME: when n > 1, we should be regenerating the indexes */
1073 pdf_dict_del(ctx, annot->obj, PDF_NAME(I));
1074
1075 pdf_field_mark_dirty(ctx, annot->obj);
1076 if (pdf_field_dirties_document(ctx, doc, annot->obj))
1077 doc->dirty = 1;
1078 }
1079 fz_catch(ctx)
1080 {
1081 pdf_drop_obj(ctx, optarr);
1082 fz_rethrow(ctx);
1083 }
1084}
1085
1086int pdf_signature_byte_range(fz_context *ctx, pdf_document *doc, pdf_obj *signature, fz_range *byte_range)
1087{
1088 pdf_obj *br = pdf_dict_getl(ctx, signature, PDF_NAME(V), PDF_NAME(ByteRange), NULL);
1089 int i, n = pdf_array_len(ctx, br)/2;
1090
1091 if (byte_range)
1092 {
1093 for (i = 0; i < n; i++)
1094 {
1095 int offset = pdf_array_get_int(ctx, br, 2*i);
1096 int length = pdf_array_get_int(ctx, br, 2*i+1);
1097
1098 if (offset < 0 || offset > doc->file_size)
1099 fz_throw(ctx, FZ_ERROR_GENERIC, "offset of signature byte range outside of file");
1100 else if (length < 0)
1101 fz_throw(ctx, FZ_ERROR_GENERIC, "length of signature byte range negative");
1102 else if (offset + length > doc->file_size)
1103 fz_throw(ctx, FZ_ERROR_GENERIC, "signature byte range extends past end of file");
1104
1105 byte_range[i].offset = offset;
1106 byte_range[i].length = length;
1107 }
1108 }
1109
1110 return n;
1111}
1112
1113static int is_white(int c)
1114{
1115 return c == '\x00' || c == '\x09' || c == '\x0a' || c == '\x0c' || c == '\x0d' || c == '\x20';
1116}
1117
1118static int is_hex_or_white(int c)
1119{
1120 return (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') || (c >= '0' && c <= '9') || is_white(c);
1121}
1122
1123static void validate_certificate_data(fz_context *ctx, pdf_document *doc, fz_range *hole)
1124{
1125 fz_stream *stm;
1126 int c;
1127
1128 stm = fz_open_range_filter(ctx, doc->file, hole, 1);
1129 fz_try(ctx)
1130 {
1131 while (is_white((c = fz_read_byte(ctx, stm))))
1132 ;
1133
1134 if (c == '<')
1135 c = fz_read_byte(ctx, stm);
1136
1137 while (is_hex_or_white((c = fz_read_byte(ctx, stm))))
1138 ;
1139
1140 if (c == '>')
1141 c = fz_read_byte(ctx, stm);
1142
1143 while (is_white((c = fz_read_byte(ctx, stm))))
1144 ;
1145
1146 if (c != EOF)
1147 fz_throw(ctx, FZ_ERROR_GENERIC, "signature certificate data contains invalid character");
1148 if (fz_tell(ctx, stm) != hole->length)
1149 fz_throw(ctx, FZ_ERROR_GENERIC, "premature end of signature certificate data");
1150 }
1151 fz_always(ctx)
1152 fz_drop_stream(ctx, stm);
1153 fz_catch(ctx)
1154 fz_rethrow(ctx);
1155}
1156
1157static int rangecmp(const void *a_, const void *b_)
1158{
1159 const fz_range *a = (const fz_range *) a_;
1160 const fz_range *b = (const fz_range *) b_;
1161 return (int) (a->offset - b->offset);
1162}
1163
1164static void validate_byte_ranges(fz_context *ctx, pdf_document *doc, fz_range *unsorted, int nranges)
1165{
1166 int64_t offset = 0;
1167 fz_range *sorted;
1168 int i;
1169
1170 sorted = fz_calloc(ctx, nranges, sizeof(*sorted));
1171 memcpy(sorted, unsorted, nranges * sizeof(*sorted));
1172 qsort(sorted, nranges, sizeof(*sorted), rangecmp);
1173
1174 fz_try(ctx)
1175 {
1176 offset = 0;
1177
1178 for (i = 0; i < nranges; i++)
1179 {
1180 if (sorted[i].offset > offset)
1181 {
1182 fz_range hole;
1183
1184 hole.offset = offset;
1185 hole.length = sorted[i].offset - offset;
1186
1187 validate_certificate_data(ctx, doc, &hole);
1188 }
1189
1190 offset = fz_maxi64(offset, sorted[i].offset + sorted[i].length);
1191 }
1192 }
1193 fz_always(ctx)
1194 fz_free(ctx, sorted);
1195 fz_catch(ctx)
1196 fz_rethrow(ctx);
1197}
1198
1199/*
1200 retrieve an fz_stream to read the bytes hashed for the signature
1201*/
1202fz_stream *pdf_signature_hash_bytes(fz_context *ctx, pdf_document *doc, pdf_obj *signature)
1203{
1204 fz_range *byte_range = NULL;
1205 int byte_range_len;
1206 fz_stream *bytes = NULL;
1207
1208 fz_var(byte_range);
1209 fz_try(ctx)
1210 {
1211 byte_range_len = pdf_signature_byte_range(ctx, doc, signature, NULL);
1212 if (byte_range_len)
1213 {
1214 byte_range = fz_calloc(ctx, byte_range_len, sizeof(*byte_range));
1215 pdf_signature_byte_range(ctx, doc, signature, byte_range);
1216 }
1217
1218 validate_byte_ranges(ctx, doc, byte_range, byte_range_len);
1219 bytes = fz_open_range_filter(ctx, doc->file, byte_range, byte_range_len);
1220 }
1221 fz_always(ctx)
1222 {
1223 fz_free(ctx, byte_range);
1224 }
1225 fz_catch(ctx)
1226 {
1227 fz_rethrow(ctx);
1228 }
1229
1230 return bytes;
1231}
1232
1233int pdf_signature_incremental_change_since_signing(fz_context *ctx, pdf_document *doc, pdf_obj *signature)
1234{
1235 fz_range *byte_range = NULL;
1236 int byte_range_len;
1237 int changed = 0;
1238
1239 fz_var(byte_range);
1240 fz_try(ctx)
1241 {
1242 byte_range_len = pdf_signature_byte_range(ctx, doc, signature, NULL);
1243 if (byte_range_len)
1244 {
1245 fz_range *last_range;
1246 int64_t end_of_range;
1247
1248 byte_range = fz_calloc(ctx, byte_range_len, sizeof(*byte_range));
1249 pdf_signature_byte_range(ctx, doc, signature, byte_range);
1250
1251 last_range = &byte_range[byte_range_len -1];
1252 end_of_range = last_range->offset + last_range->length;
1253
1254 /* We can see how long the document was when signed by inspecting the byte
1255 * ranges of the signature. The document, when read in, may have already
1256 * had changes tagged on to it, past its extent when signed, or we may have
1257 * made changes since reading it, which will be held in a new incremental
1258 * xref section. */
1259 if (doc->file_size > end_of_range || doc->num_incremental_sections > 0)
1260 changed = 1;
1261 }
1262 }
1263 fz_always(ctx)
1264 {
1265 fz_free(ctx, byte_range);
1266 }
1267 fz_catch(ctx)
1268 {
1269 fz_rethrow(ctx);
1270 }
1271
1272 return changed;
1273}
1274
1275int pdf_signature_is_signed(fz_context *ctx, pdf_document *doc, pdf_obj *field)
1276{
1277 if (pdf_dict_get_inheritable(ctx, field, PDF_NAME(FT)) != PDF_NAME(Sig))
1278 return 0;
1279 return pdf_dict_get_inheritable(ctx, field, PDF_NAME(V)) != NULL;
1280}
1281
1282/* NOTE: contents is allocated and must be freed by the caller! */
1283int pdf_signature_contents(fz_context *ctx, pdf_document *doc, pdf_obj *signature, char **contents)
1284{
1285 pdf_obj *v_ref = pdf_dict_get(ctx, signature, PDF_NAME(V));
1286 pdf_obj *v_obj = pdf_load_unencrypted_object(ctx, doc, pdf_to_num(ctx, v_ref));
1287 int len;
1288 fz_try(ctx)
1289 {
1290 pdf_obj *c = pdf_dict_get(ctx, v_obj, PDF_NAME(Contents));
1291 len = pdf_to_str_len(ctx, c);
1292 if (contents)
1293 {
1294 *contents = fz_malloc(ctx, len);
1295 memcpy(*contents, pdf_to_str_buf(ctx, c), len);
1296 }
1297 }
1298 fz_always(ctx)
1299 pdf_drop_obj(ctx, v_obj);
1300 fz_catch(ctx)
1301 fz_rethrow(ctx);
1302 return len;
1303}
1304
1305void pdf_signature_set_value(fz_context *ctx, pdf_document *doc, pdf_obj *field, pdf_pkcs7_signer *signer)
1306{
1307 pdf_obj *v = NULL;
1308 pdf_obj *indv;
1309 int vnum;
1310 pdf_obj *byte_range;
1311 pdf_obj *contents;
1312 int max_digest_size;
1313 char *buf = NULL;
1314
1315 vnum = pdf_create_object(ctx, doc);
1316 indv = pdf_new_indirect(ctx, doc, vnum, 0);
1317 pdf_dict_put_drop(ctx, field, PDF_NAME(V), indv);
1318
1319 max_digest_size = signer->max_digest_size(signer);
1320
1321 fz_var(v);
1322 fz_var(buf);
1323 fz_try(ctx)
1324 {
1325 v = pdf_new_dict(ctx, doc, 4);
1326 pdf_update_object(ctx, doc, vnum, v);
1327
1328 buf = fz_calloc(ctx, max_digest_size, 1);
1329
1330 byte_range = pdf_new_array(ctx, doc, 4);
1331 pdf_dict_put_drop(ctx, v, PDF_NAME(ByteRange), byte_range);
1332
1333 contents = pdf_new_string(ctx, buf, max_digest_size);
1334 pdf_dict_put_drop(ctx, v, PDF_NAME(Contents), contents);
1335
1336 pdf_dict_put(ctx, v, PDF_NAME(Type), PDF_NAME(Sig));
1337 pdf_dict_put(ctx, v, PDF_NAME(Filter), PDF_NAME(Adobe_PPKLite));
1338 pdf_dict_put(ctx, v, PDF_NAME(SubFilter), PDF_NAME(adbe_pkcs7_detached));
1339
1340 /* Record details within the document structure so that contents
1341 * and byte_range can be updated with their correct values at
1342 * saving time */
1343 pdf_xref_store_unsaved_signature(ctx, doc, field, signer);
1344 }
1345 fz_always(ctx)
1346 {
1347 pdf_drop_obj(ctx, v);
1348 fz_free(ctx, buf);
1349 }
1350 fz_catch(ctx)
1351 {
1352 fz_rethrow(ctx);
1353 }
1354}
1355
1356/*
1357 Update internal state appropriate for editing this field. When editing
1358 is true, updating the text of the text widget will not have any
1359 side-effects such as changing other widgets or running javascript.
1360 This state is intended for the period when a text widget is having
1361 characters typed into it. The state should be reverted at the end of
1362 the edit sequence and the text newly updated.
1363*/
1364void pdf_set_widget_editing_state(fz_context *ctx, pdf_widget *widget, int editing)
1365{
1366 widget->ignore_trigger_events = editing;
1367}
1368
1369int pdf_get_widget_editing_state(fz_context *ctx, pdf_widget *widget)
1370{
1371 return widget->ignore_trigger_events;
1372}
1373
1374static void pdf_execute_js_action(fz_context *ctx, pdf_document *doc, pdf_obj *target, const char *path, pdf_obj *js)
1375{
1376 if (js)
1377 {
1378 char *code = pdf_load_stream_or_string_as_utf8(ctx, js);
1379 fz_try(ctx)
1380 {
1381 char buf[100];
1382 fz_snprintf(buf, sizeof buf, "%d/%s", pdf_to_num(ctx, target), path);
1383 pdf_js_execute(doc->js, buf, code);
1384 }
1385 fz_always(ctx)
1386 fz_free(ctx, code);
1387 fz_catch(ctx)
1388 fz_rethrow(ctx);
1389 }
1390}
1391
1392static void pdf_execute_action_imp(fz_context *ctx, pdf_document *doc, pdf_obj *target, const char *path, pdf_obj *action)
1393{
1394 pdf_obj *S = pdf_dict_get(ctx, action, PDF_NAME(S));
1395 if (pdf_name_eq(ctx, S, PDF_NAME(JavaScript)))
1396 {
1397 if (doc->js)
1398 pdf_execute_js_action(ctx, doc, target, path, pdf_dict_get(ctx, action, PDF_NAME(JS)));
1399 }
1400 if (pdf_name_eq(ctx, S, PDF_NAME(ResetForm)))
1401 {
1402 pdf_obj *fields = pdf_dict_get(ctx, action, PDF_NAME(Fields));
1403 int flags = pdf_dict_get_int(ctx, action, PDF_NAME(Flags));
1404 pdf_reset_form(ctx, doc, fields, flags & 1);
1405 }
1406}
1407
1408static void pdf_execute_action_chain(fz_context *ctx, pdf_document *doc, pdf_obj *target, const char *path, pdf_obj *action)
1409{
1410 pdf_obj *next;
1411
1412 if (pdf_mark_obj(ctx, action))
1413 fz_throw(ctx, FZ_ERROR_GENERIC, "cycle in action chain");
1414 fz_try(ctx)
1415 {
1416 if (pdf_is_array(ctx, action))
1417 {
1418 int i, n = pdf_array_len(ctx, action);
1419 for (i = 0; i < n; ++i)
1420 pdf_execute_action_chain(ctx, doc, target, path, pdf_array_get(ctx, action, i));
1421 }
1422 else
1423 {
1424 pdf_execute_action_imp(ctx, doc, target, path, action);
1425 next = pdf_dict_get(ctx, action, PDF_NAME(Next));
1426 if (next)
1427 pdf_execute_action_chain(ctx, doc, target, path, next);
1428 }
1429 }
1430 fz_always(ctx)
1431 pdf_unmark_obj(ctx, action);
1432 fz_catch(ctx)
1433 fz_rethrow(ctx);
1434}
1435
1436static void pdf_execute_action(fz_context *ctx, pdf_document *doc, pdf_obj *target, const char *path)
1437{
1438 pdf_obj *action = pdf_dict_getp(ctx, target, path);
1439 if (action)
1440 pdf_execute_action_chain(ctx, doc, target, path, action);
1441}
1442
1443void pdf_document_event_will_close(fz_context *ctx, pdf_document *doc)
1444{
1445 pdf_execute_action(ctx, doc, pdf_trailer(ctx, doc), "Root/AA/WC");
1446}
1447
1448void pdf_document_event_will_save(fz_context *ctx, pdf_document *doc)
1449{
1450 pdf_execute_action(ctx, doc, pdf_trailer(ctx, doc), "Root/AA/WS");
1451}
1452
1453void pdf_document_event_did_save(fz_context *ctx, pdf_document *doc)
1454{
1455 pdf_execute_action(ctx, doc, pdf_trailer(ctx, doc), "Root/AA/DS");
1456}
1457
1458void pdf_document_event_will_print(fz_context *ctx, pdf_document *doc)
1459{
1460 pdf_execute_action(ctx, doc, pdf_trailer(ctx, doc), "Root/AA/WP");
1461}
1462
1463void pdf_document_event_did_print(fz_context *ctx, pdf_document *doc)
1464{
1465 pdf_execute_action(ctx, doc, pdf_trailer(ctx, doc), "Root/AA/DP");
1466}
1467
1468void pdf_page_event_open(fz_context *ctx, pdf_page *page)
1469{
1470 pdf_execute_action(ctx, page->doc, page->obj, "AA/O");
1471}
1472
1473void pdf_page_event_close(fz_context *ctx, pdf_page *page)
1474{
1475 pdf_execute_action(ctx, page->doc, page->obj, "AA/C");
1476}
1477
1478void pdf_annot_event_enter(fz_context *ctx, pdf_annot *annot)
1479{
1480 pdf_execute_action(ctx, annot->page->doc, annot->obj, "AA/E");
1481}
1482
1483void pdf_annot_event_exit(fz_context *ctx, pdf_annot *annot)
1484{
1485 pdf_execute_action(ctx, annot->page->doc, annot->obj, "AA/X");
1486}
1487
1488void pdf_annot_event_down(fz_context *ctx, pdf_annot *annot)
1489{
1490 pdf_execute_action(ctx, annot->page->doc, annot->obj, "AA/D");
1491}
1492
1493void pdf_annot_event_up(fz_context *ctx, pdf_annot *annot)
1494{
1495 pdf_obj *action = pdf_dict_get(ctx, annot->obj, PDF_NAME(A));
1496 if (action)
1497 pdf_execute_action_chain(ctx, annot->page->doc, annot->obj, "A", action);
1498 else
1499 pdf_execute_action(ctx, annot->page->doc, annot->obj, "AA/U");
1500}
1501
1502void pdf_annot_event_focus(fz_context *ctx, pdf_annot *annot)
1503{
1504 pdf_execute_action(ctx, annot->page->doc, annot->obj, "AA/Fo");
1505}
1506
1507void pdf_annot_event_blur(fz_context *ctx, pdf_annot *annot)
1508{
1509 pdf_execute_action(ctx, annot->page->doc, annot->obj, "AA/Bl");
1510}
1511
1512void pdf_annot_event_page_open(fz_context *ctx, pdf_annot *annot)
1513{
1514 pdf_execute_action(ctx, annot->page->doc, annot->obj, "AA/PO");
1515}
1516
1517void pdf_annot_event_page_close(fz_context *ctx, pdf_annot *annot)
1518{
1519 pdf_execute_action(ctx, annot->page->doc, annot->obj, "AA/PC");
1520}
1521
1522void pdf_annot_event_page_visible(fz_context *ctx, pdf_annot *annot)
1523{
1524 pdf_execute_action(ctx, annot->page->doc, annot->obj, "AA/PV");
1525}
1526
1527void pdf_annot_event_page_invisible(fz_context *ctx, pdf_annot *annot)
1528{
1529 pdf_execute_action(ctx, annot->page->doc, annot->obj, "AA/PI");
1530}
1531
1532int pdf_field_event_keystroke(fz_context *ctx, pdf_document *doc, pdf_obj *field, pdf_keystroke_event *evt)
1533{
1534 pdf_js *js = doc->js;
1535 if (js)
1536 {
1537 pdf_obj *action = pdf_dict_getp(ctx, field, "AA/K/JS");
1538 if (action)
1539 {
1540 pdf_js_event_init_keystroke(js, field, evt);
1541 pdf_execute_js_action(ctx, doc, field, "AA/K/JS", action);
1542 return pdf_js_event_result_keystroke(js, evt);
1543 }
1544 }
1545 return 1;
1546}
1547
1548char *pdf_field_event_format(fz_context *ctx, pdf_document *doc, pdf_obj *field)
1549{
1550 pdf_js *js = doc->js;
1551 if (js)
1552 {
1553 pdf_obj *action = pdf_dict_getp(ctx, field, "AA/F/JS");
1554 if (action)
1555 {
1556 const char *value = pdf_field_value(ctx, field);
1557 pdf_js_event_init(js, field, value, 1);
1558 pdf_execute_js_action(ctx, doc, field, "AA/F/JS", action);
1559 return pdf_js_event_value(js);
1560 }
1561 }
1562 return NULL;
1563}
1564
1565int pdf_field_event_validate(fz_context *ctx, pdf_document *doc, pdf_obj *field, const char *value)
1566{
1567 pdf_js *js = doc->js;
1568 if (js)
1569 {
1570 pdf_obj *action = pdf_dict_getp(ctx, field, "AA/V/JS");
1571 if (action)
1572 {
1573 pdf_js_event_init(js, field, value, 1);
1574 pdf_execute_js_action(ctx, doc, field, "AA/V/JS", action);
1575 return pdf_js_event_result(js);
1576 }
1577 }
1578 return 1;
1579}
1580
1581void pdf_field_event_calculate(fz_context *ctx, pdf_document *doc, pdf_obj *field)
1582{
1583 pdf_js *js = doc->js;
1584 if (js)
1585 {
1586 pdf_obj *action = pdf_dict_getp(ctx, field, "AA/C/JS");
1587 if (action)
1588 {
1589 char *old_value = fz_strdup(ctx, pdf_field_value(ctx, field));
1590 char *new_value = NULL;
1591 fz_var(new_value);
1592 fz_try(ctx)
1593 {
1594 pdf_js_event_init(js, field, old_value, 1);
1595 pdf_execute_js_action(ctx, doc, field, "AA/C/JS", action);
1596 if (pdf_js_event_result(js))
1597 {
1598 char *new_value = pdf_js_event_value(js);
1599 if (strcmp(old_value, new_value))
1600 pdf_set_field_value(ctx, doc, field, new_value, 0);
1601 }
1602 }
1603 fz_always(ctx)
1604 {
1605 fz_free(ctx, old_value);
1606 fz_free(ctx, new_value);
1607 }
1608 fz_catch(ctx)
1609 fz_rethrow(ctx);
1610 }
1611 }
1612}
1613