Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
7643 views
1
#include "mupdf/pdf.h"
2
3
/* Must be kept in sync with definitions in pdf_util.js */
4
enum
5
{
6
Display_Visible,
7
Display_Hidden,
8
Display_NoPrint,
9
Display_NoView
10
};
11
12
enum
13
{
14
SigFlag_SignaturesExist = 1,
15
SigFlag_AppendOnly = 2
16
};
17
18
static int pdf_field_dirties_document(fz_context *ctx, pdf_document *doc, pdf_obj *field)
19
{
20
int ff = pdf_get_field_flags(ctx, doc, field);
21
if (ff & Ff_NoExport) return 0;
22
if (ff & Ff_ReadOnly) return 0;
23
return 1;
24
}
25
26
/* Find the point in a field hierarchy where all descendents
27
* share the same name */
28
static pdf_obj *find_head_of_field_group(fz_context *ctx, pdf_obj *obj)
29
{
30
if (obj == NULL || pdf_dict_get(ctx, obj, PDF_NAME_T))
31
return obj;
32
else
33
return find_head_of_field_group(ctx, pdf_dict_get(ctx, obj, PDF_NAME_Parent));
34
}
35
36
static void pdf_field_mark_dirty(fz_context *ctx, pdf_document *doc, pdf_obj *field)
37
{
38
pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME_Kids);
39
if (kids)
40
{
41
int i, n = pdf_array_len(ctx, kids);
42
43
for (i = 0; i < n; i++)
44
pdf_field_mark_dirty(ctx, doc, pdf_array_get(ctx, kids, i));
45
}
46
else
47
{
48
pdf_dirty_obj(ctx, field);
49
}
50
}
51
52
static void update_field_value(fz_context *ctx, pdf_document *doc, pdf_obj *obj, char *text)
53
{
54
pdf_obj *sobj = NULL;
55
pdf_obj *grp;
56
57
if (!text)
58
text = "";
59
60
/* All fields of the same name should be updated, so
61
* set the value at the head of the group */
62
grp = find_head_of_field_group(ctx, obj);
63
if (grp)
64
obj = grp;
65
66
fz_var(sobj);
67
fz_try(ctx)
68
{
69
sobj = pdf_new_string(ctx, doc, text, strlen(text));
70
pdf_dict_put(ctx, obj, PDF_NAME_V, sobj);
71
}
72
fz_always(ctx)
73
{
74
pdf_drop_obj(ctx, sobj);
75
}
76
fz_catch(ctx)
77
{
78
fz_rethrow(ctx);
79
}
80
81
pdf_field_mark_dirty(ctx, doc, obj);
82
}
83
84
static pdf_obj *find_field(fz_context *ctx, pdf_obj *dict, char *name, int len)
85
{
86
pdf_obj *field;
87
88
int i, n = pdf_array_len(ctx, dict);
89
90
for (i = 0; i < n; i++)
91
{
92
char *part;
93
94
field = pdf_array_get(ctx, dict, i);
95
part = pdf_to_str_buf(ctx, pdf_dict_get(ctx, field, PDF_NAME_T));
96
if (strlen(part) == (size_t)len && !memcmp(part, name, len))
97
return field;
98
}
99
100
return NULL;
101
}
102
103
pdf_obj *pdf_lookup_field(fz_context *ctx, pdf_obj *form, char *name)
104
{
105
char *dot;
106
char *namep;
107
pdf_obj *dict = NULL;
108
int len;
109
110
/* Process the fully qualified field name which has
111
* the partial names delimited by '.'. Pretend there
112
* was a preceding '.' to simplify the loop */
113
dot = name - 1;
114
115
while (dot && form)
116
{
117
namep = dot + 1;
118
dot = strchr(namep, '.');
119
len = dot ? dot - namep : strlen(namep);
120
dict = find_field(ctx, form, namep, len);
121
if (dot)
122
form = pdf_dict_get(ctx, dict, PDF_NAME_Kids);
123
}
124
125
return dict;
126
}
127
128
static void reset_field(fz_context *ctx, pdf_document *doc, pdf_obj *field)
129
{
130
/* Set V to DV whereever DV is present, and delete V where DV is not.
131
* FIXME: we assume for now that V has not been set unequal
132
* to DV higher in the hierarchy than "field".
133
*
134
* At the bottom of the hierarchy we may find widget annotations
135
* that aren't also fields, but DV and V will not be present in their
136
* dictionaries, and attempts to remove V will be harmless. */
137
pdf_obj *dv = pdf_dict_get(ctx, field, PDF_NAME_DV);
138
pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME_Kids);
139
140
if (dv)
141
pdf_dict_put(ctx, field, PDF_NAME_V, dv);
142
else
143
pdf_dict_del(ctx, field, PDF_NAME_V);
144
145
if (kids == NULL)
146
{
147
/* The leaves of the tree are widget annotations
148
* In some cases we need to update the appearance state;
149
* in others we need to mark the field as dirty so that
150
* the appearance stream will be regenerated. */
151
switch (pdf_field_type(ctx, doc, field))
152
{
153
case PDF_WIDGET_TYPE_RADIOBUTTON:
154
case PDF_WIDGET_TYPE_CHECKBOX:
155
{
156
pdf_obj *leafv = pdf_get_inheritable(ctx, doc, field, PDF_NAME_V);
157
158
if (leafv)
159
pdf_keep_obj(ctx, leafv);
160
else
161
leafv = PDF_NAME_Off;
162
163
fz_try(ctx)
164
{
165
pdf_dict_put(ctx, field, PDF_NAME_AS, leafv);
166
}
167
fz_always(ctx)
168
{
169
pdf_drop_obj(ctx, leafv);
170
}
171
fz_catch(ctx)
172
{
173
fz_rethrow(ctx);
174
}
175
}
176
break;
177
178
case PDF_WIDGET_TYPE_PUSHBUTTON:
179
break;
180
181
default:
182
pdf_field_mark_dirty(ctx, doc, field);
183
break;
184
}
185
}
186
187
if (pdf_field_dirties_document(ctx, doc, field))
188
doc->dirty = 1;
189
}
190
191
void pdf_field_reset(fz_context *ctx, pdf_document *doc, pdf_obj *field)
192
{
193
pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME_Kids);
194
195
reset_field(ctx, doc, field);
196
197
if (kids)
198
{
199
int i, n = pdf_array_len(ctx, kids);
200
201
for (i = 0; i < n; i++)
202
pdf_field_reset(ctx, doc, pdf_array_get(ctx, kids, i));
203
}
204
}
205
206
static void add_field_hierarchy_to_array(fz_context *ctx, pdf_obj *array, pdf_obj *field)
207
{
208
pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME_Kids);
209
pdf_obj *exclude = pdf_dict_get(ctx, field, PDF_NAME_Exclude);
210
211
if (exclude)
212
return;
213
214
pdf_array_push(ctx, array, field);
215
216
if (kids)
217
{
218
int i, n = pdf_array_len(ctx, kids);
219
220
for (i = 0; i < n; i++)
221
add_field_hierarchy_to_array(ctx, array, pdf_array_get(ctx, kids, i));
222
}
223
}
224
225
/*
226
When resetting or submitting a form, the fields to act upon are defined
227
by an array of either field references or field names, plus a flag determining
228
whether to act upon the fields in the array, or all fields other than those in
229
the array. specified_fields interprets this information and produces the array
230
of fields to be acted upon.
231
*/
232
static pdf_obj *specified_fields(fz_context *ctx, pdf_document *doc, pdf_obj *fields, int exclude)
233
{
234
pdf_obj *form = pdf_dict_getl(ctx, pdf_trailer(ctx, doc), PDF_NAME_Root, PDF_NAME_AcroForm, PDF_NAME_Fields, NULL);
235
int i, n;
236
pdf_obj *result = pdf_new_array(ctx, doc, 0);
237
pdf_obj *nil = NULL;
238
239
fz_var(nil);
240
fz_try(ctx)
241
{
242
/* The 'fields' array not being present signals that all fields
243
* should be acted upon, so handle it using the exclude case - excluding none */
244
if (exclude || !fields)
245
{
246
/* mark the fields we don't want to act upon */
247
nil = pdf_new_null(ctx, doc);
248
249
n = pdf_array_len(ctx, fields);
250
251
for (i = 0; i < n; i++)
252
{
253
pdf_obj *field = pdf_array_get(ctx, fields, i);
254
255
if (pdf_is_string(ctx, field))
256
field = pdf_lookup_field(ctx, form, pdf_to_str_buf(ctx, field));
257
258
if (field)
259
pdf_dict_put(ctx, field, PDF_NAME_Exclude, nil);
260
}
261
262
/* Act upon all unmarked fields */
263
n = pdf_array_len(ctx, form);
264
265
for (i = 0; i < n; i++)
266
add_field_hierarchy_to_array(ctx, result, pdf_array_get(ctx, form, i));
267
268
/* Unmark the marked fields */
269
n = pdf_array_len(ctx, fields);
270
271
for (i = 0; i < n; i++)
272
{
273
pdf_obj *field = pdf_array_get(ctx, fields, i);
274
275
if (pdf_is_string(ctx, field))
276
field = pdf_lookup_field(ctx, form, pdf_to_str_buf(ctx, field));
277
278
if (field)
279
pdf_dict_del(ctx, field, PDF_NAME_Exclude);
280
}
281
}
282
else
283
{
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
add_field_hierarchy_to_array(ctx, result, field);
295
}
296
}
297
}
298
fz_always(ctx)
299
{
300
pdf_drop_obj(ctx, nil);
301
}
302
fz_catch(ctx)
303
{
304
pdf_drop_obj(ctx, result);
305
fz_rethrow(ctx);
306
}
307
308
return result;
309
}
310
311
static void reset_form(fz_context *ctx, pdf_document *doc, pdf_obj *fields, int exclude)
312
{
313
pdf_obj *sfields = specified_fields(ctx, doc, fields, exclude);
314
315
fz_try(ctx)
316
{
317
int i, n = pdf_array_len(ctx, sfields);
318
319
for (i = 0; i < n; i++)
320
reset_field(ctx, doc, pdf_array_get(ctx, sfields, i));
321
}
322
fz_always(ctx)
323
{
324
pdf_drop_obj(ctx, sfields);
325
}
326
fz_catch(ctx)
327
{
328
fz_rethrow(ctx);
329
}
330
}
331
332
static void execute_action(fz_context *ctx, pdf_document *doc, pdf_obj *obj, pdf_obj *a)
333
{
334
if (a)
335
{
336
pdf_obj *type = pdf_dict_get(ctx, a, PDF_NAME_S);
337
338
if (pdf_name_eq(ctx, type, PDF_NAME_JavaScript))
339
{
340
pdf_obj *js = pdf_dict_get(ctx, a, PDF_NAME_JS);
341
if (js)
342
{
343
char *code = pdf_to_utf8(ctx, doc, js);
344
fz_try(ctx)
345
{
346
pdf_js_execute(doc->js, code);
347
}
348
fz_always(ctx)
349
{
350
fz_free(ctx, code);
351
}
352
fz_catch(ctx)
353
{
354
fz_rethrow(ctx);
355
}
356
}
357
}
358
else if (pdf_name_eq(ctx, type, PDF_NAME_ResetForm))
359
{
360
reset_form(ctx, doc, pdf_dict_get(ctx, a, PDF_NAME_Fields), pdf_to_int(ctx, pdf_dict_get(ctx, a, PDF_NAME_Flags)) & 1);
361
}
362
else if (pdf_name_eq(ctx, type, PDF_NAME_Named))
363
{
364
pdf_obj *name = pdf_dict_get(ctx, a, PDF_NAME_N);
365
366
if (pdf_name_eq(ctx, name, PDF_NAME_Print))
367
pdf_event_issue_print(ctx, doc);
368
}
369
}
370
}
371
372
static void execute_action_chain(fz_context *ctx, pdf_document *doc, pdf_obj *obj)
373
{
374
pdf_obj *a = pdf_dict_get(ctx, obj, PDF_NAME_A);
375
pdf_js_event e;
376
377
e.target = obj;
378
e.value = "";
379
pdf_js_setup_event(doc->js, &e);
380
381
while (a)
382
{
383
execute_action(ctx, doc, obj, a);
384
a = pdf_dict_get(ctx, a, PDF_NAME_Next);
385
}
386
}
387
388
static void execute_additional_action(fz_context *ctx, pdf_document *doc, pdf_obj *obj, char *act)
389
{
390
pdf_obj *a = pdf_dict_getp(ctx, obj, act);
391
392
if (a)
393
{
394
pdf_js_event e;
395
396
e.target = obj;
397
e.value = "";
398
pdf_js_setup_event(doc->js, &e);
399
execute_action(ctx, doc, obj, a);
400
}
401
}
402
403
static void check_off(fz_context *ctx, pdf_document *doc, pdf_obj *obj)
404
{
405
pdf_dict_put(ctx, obj, PDF_NAME_AS, PDF_NAME_Off);
406
}
407
408
static void set_check(fz_context *ctx, pdf_document *doc, pdf_obj *chk, pdf_obj *name)
409
{
410
pdf_obj *n = pdf_dict_getp(ctx, chk, "AP/N");
411
pdf_obj *val;
412
413
/* If name is a possible value of this check
414
* box then use it, otherwise use "Off" */
415
if (pdf_dict_get(ctx, n, name))
416
val = name;
417
else
418
val = PDF_NAME_Off;
419
420
pdf_dict_put(ctx, chk, PDF_NAME_AS, val);
421
}
422
423
/* Set the values of all fields in a group defined by a node
424
* in the hierarchy */
425
static void set_check_grp(fz_context *ctx, pdf_document *doc, pdf_obj *grp, pdf_obj *val)
426
{
427
pdf_obj *kids = pdf_dict_get(ctx, grp, PDF_NAME_Kids);
428
429
if (kids == NULL)
430
{
431
set_check(ctx, doc, grp, val);
432
}
433
else
434
{
435
int i, n = pdf_array_len(ctx, kids);
436
437
for (i = 0; i < n; i++)
438
set_check_grp(ctx, doc, pdf_array_get(ctx, kids, i), val);
439
}
440
}
441
442
static void recalculate(fz_context *ctx, pdf_document *doc)
443
{
444
if (doc->recalculating)
445
return;
446
447
doc->recalculating = 1;
448
fz_try(ctx)
449
{
450
pdf_obj *co = pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/AcroForm/CO");
451
452
if (co && doc->js)
453
{
454
int i, n = pdf_array_len(ctx, co);
455
456
for (i = 0; i < n; i++)
457
{
458
pdf_obj *field = pdf_array_get(ctx, co, i);
459
pdf_obj *calc = pdf_dict_getp(ctx, field, "AA/C");
460
461
if (calc)
462
{
463
pdf_js_event e;
464
465
e.target = field;
466
e.value = pdf_field_value(ctx, doc, field);
467
pdf_js_setup_event(doc->js, &e);
468
execute_action(ctx, doc, field, calc);
469
/* A calculate action, updates event.value. We need
470
* to place the value in the field */
471
update_field_value(ctx, doc, field, pdf_js_get_event(doc->js)->value);
472
}
473
}
474
}
475
}
476
fz_always(ctx)
477
{
478
doc->recalculating = 0;
479
}
480
fz_catch(ctx)
481
{
482
fz_rethrow(ctx);
483
}
484
}
485
486
static void toggle_check_box(fz_context *ctx, pdf_document *doc, pdf_obj *obj)
487
{
488
pdf_obj *as = pdf_dict_get(ctx, obj, PDF_NAME_AS);
489
int ff = pdf_get_field_flags(ctx, doc, obj);
490
int radio = ((ff & (Ff_Pushbutton|Ff_Radio)) == Ff_Radio);
491
char *val = NULL;
492
pdf_obj *grp = radio ? pdf_dict_get(ctx, obj, PDF_NAME_Parent) : find_head_of_field_group(ctx, obj);
493
494
if (!grp)
495
grp = obj;
496
497
if (as && !pdf_name_eq(ctx, as, PDF_NAME_Off))
498
{
499
/* "as" neither missing nor set to Off. Set it to Off, unless
500
* this is a non-toggle-off radio button. */
501
if ((ff & (Ff_Pushbutton|Ff_NoToggleToOff|Ff_Radio)) != (Ff_NoToggleToOff|Ff_Radio))
502
{
503
check_off(ctx, doc, obj);
504
val = "Off";
505
}
506
}
507
else
508
{
509
pdf_obj *n, *key = NULL;
510
int len, i;
511
512
n = pdf_dict_getp(ctx, obj, "AP/N");
513
514
/* Look for a key that isn't "Off" */
515
len = pdf_dict_len(ctx, n);
516
for (i = 0; i < len; i++)
517
{
518
key = pdf_dict_get_key(ctx, n, i);
519
if (pdf_is_name(ctx, key) && !pdf_name_eq(ctx, key, PDF_NAME_Off))
520
break;
521
}
522
523
/* If we found no alternative value to Off then we have no value to use */
524
if (!key)
525
return;
526
527
if (radio)
528
{
529
/* For radio buttons, first turn off all buttons in the group and
530
* then set the one that was clicked */
531
pdf_obj *kids = pdf_dict_get(ctx, grp, PDF_NAME_Kids);
532
533
len = pdf_array_len(ctx, kids);
534
for (i = 0; i < len; i++)
535
check_off(ctx, doc, pdf_array_get(ctx, kids, i));
536
537
pdf_dict_put(ctx, obj, PDF_NAME_AS, key);
538
}
539
else
540
{
541
/* For check boxes, we have located the node of the field hierarchy
542
* below which all fields share a name with the clicked one. Set
543
* all to the same value. This may cause the group to act like
544
* radio buttons, if each have distinct "On" values */
545
if (grp)
546
set_check_grp(ctx, doc, grp, key);
547
else
548
set_check(ctx, doc, obj, key);
549
}
550
}
551
552
if (val && grp)
553
{
554
pdf_obj *v = NULL;
555
556
fz_var(v);
557
fz_try(ctx)
558
{
559
v = pdf_new_string(ctx, doc, val, strlen(val));
560
pdf_dict_put(ctx, grp, PDF_NAME_V, v);
561
}
562
fz_always(ctx)
563
{
564
pdf_drop_obj(ctx, v);
565
}
566
fz_catch(ctx)
567
{
568
fz_rethrow(ctx);
569
}
570
571
recalculate(ctx, doc);
572
}
573
}
574
575
int pdf_has_unsaved_changes(fz_context *ctx, pdf_document *doc)
576
{
577
return doc->dirty;
578
}
579
580
int pdf_pass_event(fz_context *ctx, pdf_document *doc, pdf_page *page, pdf_ui_event *ui_event)
581
{
582
pdf_annot *annot;
583
pdf_hotspot *hp = &doc->hotspot;
584
fz_point *pt = &(ui_event->event.pointer.pt);
585
int changed = 0;
586
587
if (page == NULL)
588
return 0;
589
590
for (annot = page->annots; annot; annot = annot->next)
591
{
592
if (pt->x >= annot->pagerect.x0 && pt->x <= annot->pagerect.x1)
593
if (pt->y >= annot->pagerect.y0 && pt->y <= annot->pagerect.y1)
594
break;
595
}
596
597
if (annot)
598
{
599
int f = pdf_to_int(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME_F));
600
601
if (f & (F_Hidden|F_NoView))
602
annot = NULL;
603
}
604
605
switch (ui_event->etype)
606
{
607
case PDF_EVENT_TYPE_POINTER:
608
{
609
switch (ui_event->event.pointer.ptype)
610
{
611
case PDF_POINTER_DOWN:
612
if (doc->focus_obj)
613
{
614
/* Execute the blur action */
615
execute_additional_action(ctx, doc, doc->focus_obj, "AA/Bl");
616
doc->focus = NULL;
617
pdf_drop_obj(ctx, doc->focus_obj);
618
doc->focus_obj = NULL;
619
}
620
621
if (annot)
622
{
623
doc->focus = annot;
624
doc->focus_obj = pdf_keep_obj(ctx, annot->obj);
625
626
hp->num = pdf_to_num(ctx, annot->obj);
627
hp->gen = pdf_to_gen(ctx, annot->obj);
628
hp->state = HOTSPOT_POINTER_DOWN;
629
changed = 1;
630
/* Exectute the down and focus actions */
631
execute_additional_action(ctx, doc, annot->obj, "AA/Fo");
632
execute_additional_action(ctx, doc, annot->obj, "AA/D");
633
}
634
break;
635
636
case PDF_POINTER_UP:
637
if (hp->state != 0)
638
changed = 1;
639
640
hp->num = 0;
641
hp->gen = 0;
642
hp->state = 0;
643
644
if (annot)
645
{
646
switch (annot->widget_type)
647
{
648
case PDF_WIDGET_TYPE_RADIOBUTTON:
649
case PDF_WIDGET_TYPE_CHECKBOX:
650
/* FIXME: treating radio buttons like check boxes, for now */
651
toggle_check_box(ctx, doc, annot->obj);
652
changed = 1;
653
break;
654
}
655
656
/* Execute the up action */
657
execute_additional_action(ctx, doc, annot->obj, "AA/U");
658
/* Execute the main action chain */
659
execute_action_chain(ctx, doc, annot->obj);
660
}
661
break;
662
}
663
}
664
break;
665
}
666
667
return changed;
668
}
669
670
void pdf_update_page(fz_context *ctx, pdf_document *doc, pdf_page *page)
671
{
672
pdf_annot *annot;
673
674
/* Reset changed_annots to empty */
675
page->changed_annots = NULL;
676
677
/*
678
Free all annots in tmp_annots, since these were
679
referenced only from changed_annots.
680
*/
681
if (page->tmp_annots)
682
{
683
pdf_drop_annot(ctx, page->tmp_annots);
684
page->tmp_annots = NULL;
685
}
686
687
/* Add all changed annots to the list */
688
for (annot = page->annots; annot; annot = annot->next)
689
{
690
pdf_xobject *ap = pdf_keep_xobject(ctx, annot->ap);
691
int ap_iteration = annot->ap_iteration;
692
693
fz_try(ctx)
694
{
695
pdf_update_annot(ctx, doc, annot);
696
697
if ((ap != annot->ap || ap_iteration != annot->ap_iteration))
698
{
699
annot->next_changed = page->changed_annots;
700
page->changed_annots = annot;
701
}
702
}
703
fz_always(ctx)
704
{
705
pdf_drop_xobject(ctx, ap);
706
}
707
fz_catch(ctx)
708
{
709
fz_rethrow(ctx);
710
}
711
}
712
713
/*
714
Add all deleted annots to the list, since these also
715
warrant a screen update
716
*/
717
for (annot = page->deleted_annots; annot; annot = annot->next)
718
{
719
annot->next_changed = page->changed_annots;
720
page->changed_annots = annot;
721
}
722
723
/*
724
Move deleted_annots to tmp_annots to keep them separate
725
from any future deleted ones. They cannot yet be freed
726
since they are linked into changed_annots
727
*/
728
page->tmp_annots = page->deleted_annots;
729
page->deleted_annots = NULL;
730
}
731
732
pdf_annot *pdf_poll_changed_annot(fz_context *ctx, pdf_document *idoc, pdf_page *page)
733
{
734
pdf_annot *annot = page->changed_annots;
735
736
if (annot)
737
page->changed_annots = annot->next_changed;
738
739
return annot;
740
}
741
742
pdf_widget *pdf_focused_widget(fz_context *ctx, pdf_document *doc)
743
{
744
return (pdf_widget *)doc->focus;
745
}
746
747
pdf_widget *pdf_first_widget(fz_context *ctx, pdf_document *doc, pdf_page *page)
748
{
749
pdf_annot *annot = page->annots;
750
751
while (annot && annot->widget_type == PDF_WIDGET_TYPE_NOT_WIDGET)
752
annot = annot->next;
753
754
return (pdf_widget *)annot;
755
}
756
757
pdf_widget *pdf_next_widget(fz_context *ctx, pdf_widget *previous)
758
{
759
pdf_annot *annot = (pdf_annot *)previous;
760
761
if (annot)
762
annot = annot->next;
763
764
while (annot && annot->widget_type == PDF_WIDGET_TYPE_NOT_WIDGET)
765
annot = annot->next;
766
767
return (pdf_widget *)annot;
768
}
769
770
pdf_widget *pdf_create_widget(fz_context *ctx, pdf_document *doc, pdf_page *page, int type, char *fieldname)
771
{
772
pdf_obj *form = NULL;
773
int old_sigflags = pdf_to_int(ctx, pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/AcroForm/SigFlags"));
774
pdf_annot *annot = pdf_create_annot(ctx, doc, page, FZ_ANNOT_WIDGET);
775
776
fz_try(ctx)
777
{
778
pdf_set_field_type(ctx, doc, annot->obj, type);
779
pdf_dict_put_drop(ctx, annot->obj, PDF_NAME_T, pdf_new_string(ctx, doc, fieldname, strlen(fieldname)));
780
annot->widget_type = type;
781
782
if (type == PDF_WIDGET_TYPE_SIGNATURE)
783
{
784
int sigflags = (old_sigflags | (SigFlag_SignaturesExist|SigFlag_AppendOnly));
785
pdf_dict_putl_drop(ctx, pdf_trailer(ctx, doc), pdf_new_int(ctx, doc, sigflags), PDF_NAME_Root, PDF_NAME_AcroForm, PDF_NAME_SigFlags, NULL);
786
}
787
788
/*
789
pdf_create_annot will have linked the new widget into the page's
790
annot array. We also need it linked into the document's form
791
*/
792
form = pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/AcroForm/Fields");
793
if (!form)
794
{
795
form = pdf_new_array(ctx, doc, 1);
796
pdf_dict_putl_drop(ctx, pdf_trailer(ctx, doc), form, PDF_NAME_Root, PDF_NAME_AcroForm, PDF_NAME_Fields, NULL);
797
}
798
799
pdf_array_push(ctx, form, annot->obj); /* Cleanup relies on this statement being last */
800
}
801
fz_catch(ctx)
802
{
803
pdf_delete_annot(ctx, doc, page, annot);
804
805
/* An empty Fields array may have been created, but that is harmless */
806
807
if (type == PDF_WIDGET_TYPE_SIGNATURE)
808
pdf_dict_putl_drop(ctx, pdf_trailer(ctx, doc), pdf_new_int(ctx, doc, old_sigflags), PDF_NAME_Root, PDF_NAME_AcroForm, PDF_NAME_SigFlags, NULL);
809
810
fz_rethrow(ctx);
811
}
812
813
return (pdf_widget *)annot;
814
}
815
816
int pdf_widget_get_type(fz_context *ctx, pdf_widget *widget)
817
{
818
pdf_annot *annot = (pdf_annot *)widget;
819
return annot->widget_type;
820
}
821
822
static int set_text_field_value(fz_context *ctx, pdf_document *doc, pdf_obj *field, char *text)
823
{
824
pdf_obj *v = pdf_dict_getp(ctx, field, "AA/V");
825
826
if (v && doc->js)
827
{
828
pdf_js_event e;
829
830
e.target = field;
831
e.value = text;
832
pdf_js_setup_event(doc->js, &e);
833
execute_action(ctx, doc, field, v);
834
835
if (!pdf_js_get_event(doc->js)->rc)
836
return 0;
837
838
text = pdf_js_get_event(doc->js)->value;
839
}
840
841
if (pdf_field_dirties_document(ctx, doc, field))
842
doc->dirty = 1;
843
update_field_value(ctx, doc, field, text);
844
845
return 1;
846
}
847
848
static void update_checkbox_selector(fz_context *ctx, pdf_document *doc, pdf_obj *field, char *val)
849
{
850
pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME_Kids);
851
852
if (kids)
853
{
854
int i, n = pdf_array_len(ctx, kids);
855
856
for (i = 0; i < n; i++)
857
update_checkbox_selector(ctx, doc, pdf_array_get(ctx, kids, i), val);
858
}
859
else
860
{
861
pdf_obj *n = pdf_dict_getp(ctx, field, "AP/N");
862
pdf_obj *oval = NULL;
863
864
fz_var(oval);
865
fz_try(ctx)
866
{
867
if (pdf_dict_gets(ctx, n, val))
868
oval = pdf_new_name(ctx, doc, val);
869
else
870
oval = PDF_NAME_Off;
871
872
pdf_dict_put(ctx, field, PDF_NAME_AS, oval);
873
}
874
fz_always(ctx)
875
{
876
pdf_drop_obj(ctx, oval);
877
}
878
fz_catch(ctx)
879
{
880
fz_rethrow(ctx);
881
}
882
}
883
}
884
885
static int set_checkbox_value(fz_context *ctx, pdf_document *doc, pdf_obj *field, char *val)
886
{
887
update_checkbox_selector(ctx, doc, field, val);
888
update_field_value(ctx, doc, field, val);
889
return 1;
890
}
891
892
int pdf_field_set_value(fz_context *ctx, pdf_document *doc, pdf_obj *field, char *text)
893
{
894
int res = 0;
895
896
switch (pdf_field_type(ctx, doc, field))
897
{
898
case PDF_WIDGET_TYPE_TEXT:
899
res = set_text_field_value(ctx, doc, field, text);
900
break;
901
902
case PDF_WIDGET_TYPE_CHECKBOX:
903
case PDF_WIDGET_TYPE_RADIOBUTTON:
904
res = set_checkbox_value(ctx, doc, field, text);
905
break;
906
907
default:
908
/* text updater will do in most cases */
909
update_field_value(ctx, doc, field, text);
910
res = 1;
911
break;
912
}
913
914
recalculate(ctx, doc);
915
916
return res;
917
}
918
919
char *pdf_field_border_style(fz_context *ctx, pdf_document *doc, pdf_obj *field)
920
{
921
char *bs = pdf_to_name(ctx, pdf_dict_getl(ctx, field, PDF_NAME_BS, PDF_NAME_S, NULL));
922
923
switch (*bs)
924
{
925
case 'S': return "Solid";
926
case 'D': return "Dashed";
927
case 'B': return "Beveled";
928
case 'I': return "Inset";
929
case 'U': return "Underline";
930
}
931
932
return "Solid";
933
}
934
935
void pdf_field_set_border_style(fz_context *ctx, pdf_document *doc, pdf_obj *field, char *text)
936
{
937
pdf_obj *val = NULL;
938
939
if (!strcmp(text, "Solid"))
940
val = PDF_NAME_S;
941
else if (!strcmp(text, "Dashed"))
942
val = PDF_NAME_D;
943
else if (!strcmp(text, "Beveled"))
944
val = PDF_NAME_B;
945
else if (!strcmp(text, "Inset"))
946
val = PDF_NAME_I;
947
else if (!strcmp(text, "Underline"))
948
val = PDF_NAME_U;
949
else
950
return;
951
952
fz_try(ctx);
953
{
954
pdf_dict_putl(ctx, field, val, PDF_NAME_BS, PDF_NAME_S, NULL);
955
pdf_field_mark_dirty(ctx, doc, field);
956
}
957
fz_always(ctx)
958
{
959
pdf_drop_obj(ctx, val);
960
}
961
fz_catch(ctx)
962
{
963
fz_rethrow(ctx);
964
}
965
}
966
967
void pdf_field_set_button_caption(fz_context *ctx, pdf_document *doc, pdf_obj *field, char *text)
968
{
969
pdf_obj *val = pdf_new_string(ctx, doc, text, strlen(text));
970
971
fz_try(ctx);
972
{
973
if (pdf_field_type(ctx, doc, field) == PDF_WIDGET_TYPE_PUSHBUTTON)
974
{
975
pdf_dict_putl(ctx, field, val, PDF_NAME_MK, PDF_NAME_CA, NULL);
976
pdf_field_mark_dirty(ctx, doc, field);
977
}
978
}
979
fz_always(ctx)
980
{
981
pdf_drop_obj(ctx, val);
982
}
983
fz_catch(ctx)
984
{
985
fz_rethrow(ctx);
986
}
987
}
988
989
int pdf_field_display(fz_context *ctx, pdf_document *doc, pdf_obj *field)
990
{
991
pdf_obj *kids;
992
int f, res = Display_Visible;
993
994
/* Base response on first of children. Not ideal,
995
* but not clear how to handle children with
996
* differing values */
997
while ((kids = pdf_dict_get(ctx, field, PDF_NAME_Kids)) != NULL)
998
field = pdf_array_get(ctx, kids, 0);
999
1000
f = pdf_to_int(ctx, pdf_dict_get(ctx, field, PDF_NAME_F));
1001
1002
if (f & F_Hidden)
1003
{
1004
res = Display_Hidden;
1005
}
1006
else if (f & F_Print)
1007
{
1008
if (f & F_NoView)
1009
res = Display_NoView;
1010
}
1011
else
1012
{
1013
if (f & F_NoView)
1014
res = Display_Hidden;
1015
else
1016
res = Display_NoPrint;
1017
}
1018
1019
return res;
1020
}
1021
1022
/*
1023
* get the field name in a char buffer that has spare room to
1024
* add more characters at the end.
1025
*/
1026
static char *get_field_name(fz_context *ctx, pdf_document *doc, pdf_obj *field, int spare)
1027
{
1028
char *res = NULL;
1029
pdf_obj *parent = pdf_dict_get(ctx, field, PDF_NAME_Parent);
1030
char *lname = pdf_to_str_buf(ctx, pdf_dict_get(ctx, field, PDF_NAME_T));
1031
int llen = strlen(lname);
1032
1033
/*
1034
* If we found a name at this point in the field hierarchy
1035
* then we'll need extra space for it and a dot
1036
*/
1037
if (llen)
1038
spare += llen+1;
1039
1040
if (parent)
1041
{
1042
res = get_field_name(ctx, doc, parent, spare);
1043
}
1044
else
1045
{
1046
res = fz_malloc(ctx, spare+1);
1047
res[0] = 0;
1048
}
1049
1050
if (llen)
1051
{
1052
if (res[0])
1053
strcat(res, ".");
1054
1055
strcat(res, lname);
1056
}
1057
1058
return res;
1059
}
1060
1061
char *pdf_field_name(fz_context *ctx, pdf_document *doc, pdf_obj *field)
1062
{
1063
return get_field_name(ctx, doc, field, 0);
1064
}
1065
1066
void pdf_field_set_display(fz_context *ctx, pdf_document *doc, pdf_obj *field, int d)
1067
{
1068
pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME_Kids);
1069
1070
if (!kids)
1071
{
1072
int mask = (F_Hidden|F_Print|F_NoView);
1073
int f = pdf_to_int(ctx, pdf_dict_get(ctx, field, PDF_NAME_F)) & ~mask;
1074
pdf_obj *fo = NULL;
1075
1076
switch (d)
1077
{
1078
case Display_Visible:
1079
f |= F_Print;
1080
break;
1081
case Display_Hidden:
1082
f |= F_Hidden;
1083
break;
1084
case Display_NoView:
1085
f |= (F_Print|F_NoView);
1086
break;
1087
case Display_NoPrint:
1088
break;
1089
}
1090
1091
fz_var(fo);
1092
fz_try(ctx)
1093
{
1094
fo = pdf_new_int(ctx, doc, f);
1095
pdf_dict_put(ctx, field, PDF_NAME_F, fo);
1096
}
1097
fz_always(ctx)
1098
{
1099
pdf_drop_obj(ctx, fo);
1100
}
1101
fz_catch(ctx)
1102
{
1103
fz_rethrow(ctx);
1104
}
1105
}
1106
else
1107
{
1108
int i, n = pdf_array_len(ctx, kids);
1109
1110
for (i = 0; i < n; i++)
1111
pdf_field_set_display(ctx, doc, pdf_array_get(ctx, kids, i), d);
1112
}
1113
}
1114
1115
void pdf_field_set_fill_color(fz_context *ctx, pdf_document *doc, pdf_obj *field, pdf_obj *col)
1116
{
1117
/* col == NULL mean transparent, but we can simply pass it on as with
1118
* non-NULL values because pdf_dict_putp interprets a NULL value as
1119
* delete */
1120
pdf_dict_putl(ctx, field, col, PDF_NAME_MK, PDF_NAME_BG, NULL);
1121
pdf_field_mark_dirty(ctx, doc, field);
1122
}
1123
1124
void pdf_field_set_text_color(fz_context *ctx, pdf_document *doc, pdf_obj *field, pdf_obj *col)
1125
{
1126
pdf_da_info di;
1127
fz_buffer *fzbuf = NULL;
1128
char *da = pdf_to_str_buf(ctx, pdf_get_inheritable(ctx, doc, field, PDF_NAME_DA));
1129
unsigned char *buf;
1130
int len;
1131
pdf_obj *daobj = NULL;
1132
1133
memset(&di, 0, sizeof(di));
1134
1135
fz_var(fzbuf);
1136
fz_var(di);
1137
fz_var(daobj);
1138
fz_try(ctx)
1139
{
1140
int i;
1141
1142
pdf_parse_da(ctx, da, &di);
1143
di.col_size = pdf_array_len(ctx, col);
1144
1145
len = fz_mini(di.col_size, nelem(di.col));
1146
for (i = 0; i < len; i++)
1147
di.col[i] = pdf_to_real(ctx, pdf_array_get(ctx, col, i));
1148
1149
fzbuf = fz_new_buffer(ctx, 0);
1150
pdf_fzbuf_print_da(ctx, fzbuf, &di);
1151
len = fz_buffer_storage(ctx, fzbuf, &buf);
1152
daobj = pdf_new_string(ctx, doc, (char *)buf, len);
1153
pdf_dict_put(ctx, field, PDF_NAME_DA, daobj);
1154
pdf_field_mark_dirty(ctx, doc, field);
1155
}
1156
fz_always(ctx)
1157
{
1158
pdf_da_info_fin(ctx, &di);
1159
fz_drop_buffer(ctx, fzbuf);
1160
pdf_drop_obj(ctx, daobj);
1161
}
1162
fz_catch(ctx)
1163
{
1164
fz_warn(ctx, "%s", fz_caught_message(ctx));
1165
}
1166
}
1167
1168
fz_rect *pdf_bound_widget(fz_context *ctx, pdf_widget *widget, fz_rect *rect)
1169
{
1170
pdf_annot *annot = (pdf_annot *)widget;
1171
1172
if (rect == NULL)
1173
return NULL;
1174
*rect = annot->pagerect;
1175
1176
return rect;
1177
}
1178
1179
char *pdf_text_widget_text(fz_context *ctx, pdf_document *doc, pdf_widget *tw)
1180
{
1181
pdf_annot *annot = (pdf_annot *)tw;
1182
char *text = NULL;
1183
1184
fz_var(text);
1185
fz_try(ctx)
1186
{
1187
text = pdf_field_value(ctx, doc, annot->obj);
1188
}
1189
fz_catch(ctx)
1190
{
1191
fz_warn(ctx, "failed allocation in fz_text_widget_text");
1192
}
1193
1194
return text;
1195
}
1196
1197
int pdf_text_widget_max_len(fz_context *ctx, pdf_document *doc, pdf_widget *tw)
1198
{
1199
pdf_annot *annot = (pdf_annot *)tw;
1200
1201
return pdf_to_int(ctx, pdf_get_inheritable(ctx, doc, annot->obj, PDF_NAME_MaxLen));
1202
}
1203
1204
int pdf_text_widget_content_type(fz_context *ctx, pdf_document *doc, pdf_widget *tw)
1205
{
1206
pdf_annot *annot = (pdf_annot *)tw;
1207
char *code = NULL;
1208
int type = PDF_WIDGET_CONTENT_UNRESTRAINED;
1209
1210
fz_var(code);
1211
fz_try(ctx)
1212
{
1213
code = pdf_get_string_or_stream(ctx, doc, pdf_dict_getl(ctx, annot->obj, PDF_NAME_AA, PDF_NAME_F, PDF_NAME_JS, NULL));
1214
if (code)
1215
{
1216
if (strstr(code, "AFNumber_Format"))
1217
type = PDF_WIDGET_CONTENT_NUMBER;
1218
else if (strstr(code, "AFSpecial_Format"))
1219
type = PDF_WIDGET_CONTENT_SPECIAL;
1220
else if (strstr(code, "AFDate_FormatEx"))
1221
type = PDF_WIDGET_CONTENT_DATE;
1222
else if (strstr(code, "AFTime_FormatEx"))
1223
type = PDF_WIDGET_CONTENT_TIME;
1224
}
1225
}
1226
fz_always(ctx)
1227
{
1228
fz_free(ctx, code);
1229
}
1230
fz_catch(ctx)
1231
{
1232
fz_warn(ctx, "failure in fz_text_widget_content_type");
1233
}
1234
1235
return type;
1236
}
1237
1238
static int run_keystroke(fz_context *ctx, pdf_document *doc, pdf_obj *field, char **text)
1239
{
1240
pdf_obj *k = pdf_dict_getl(ctx, field, PDF_NAME_AA, PDF_NAME_K, NULL);
1241
1242
if (k && doc->js)
1243
{
1244
pdf_js_event e;
1245
1246
e.target = field;
1247
e.value = *text;
1248
pdf_js_setup_event(doc->js, &e);
1249
execute_action(ctx, doc, field, k);
1250
1251
if (!pdf_js_get_event(doc->js)->rc)
1252
return 0;
1253
1254
*text = pdf_js_get_event(doc->js)->value;
1255
}
1256
1257
return 1;
1258
}
1259
1260
int pdf_text_widget_set_text(fz_context *ctx, pdf_document *doc, pdf_widget *tw, char *text)
1261
{
1262
pdf_annot *annot = (pdf_annot *)tw;
1263
int accepted = 0;
1264
1265
fz_try(ctx)
1266
{
1267
accepted = run_keystroke(ctx, doc, annot->obj, &text);
1268
if (accepted)
1269
accepted = pdf_field_set_value(ctx, doc, annot->obj, text);
1270
}
1271
fz_catch(ctx)
1272
{
1273
fz_warn(ctx, "fz_text_widget_set_text failed");
1274
}
1275
1276
return accepted;
1277
}
1278
1279
int pdf_choice_widget_options(fz_context *ctx, pdf_document *doc, pdf_widget *tw, char *opts[])
1280
{
1281
pdf_annot *annot = (pdf_annot *)tw;
1282
pdf_obj *optarr;
1283
int i, n;
1284
1285
if (!annot)
1286
return 0;
1287
1288
optarr = pdf_dict_get(ctx, annot->obj, PDF_NAME_Opt);
1289
n = pdf_array_len(ctx, optarr);
1290
1291
if (opts)
1292
{
1293
for (i = 0; i < n; i++)
1294
{
1295
opts[i] = pdf_to_str_buf(ctx, pdf_array_get(ctx, optarr, i));
1296
}
1297
}
1298
1299
return n;
1300
}
1301
1302
int pdf_choice_widget_is_multiselect(fz_context *ctx, pdf_document *doc, pdf_widget *tw)
1303
{
1304
pdf_annot *annot = (pdf_annot *)tw;
1305
1306
if (!annot) return 0;
1307
1308
switch (pdf_field_type(ctx, doc, annot->obj))
1309
{
1310
case PDF_WIDGET_TYPE_LISTBOX:
1311
case PDF_WIDGET_TYPE_COMBOBOX:
1312
return (pdf_get_field_flags(ctx, doc, annot->obj) & Ff_MultiSelect) != 0;
1313
default:
1314
return 0;
1315
}
1316
}
1317
1318
int pdf_choice_widget_value(fz_context *ctx, pdf_document *doc, pdf_widget *tw, char *opts[])
1319
{
1320
pdf_annot *annot = (pdf_annot *)tw;
1321
pdf_obj *optarr;
1322
int i, n;
1323
1324
if (!annot)
1325
return 0;
1326
1327
optarr = pdf_dict_get(ctx, annot->obj, PDF_NAME_V);
1328
1329
if (pdf_is_string(ctx, optarr))
1330
{
1331
if (opts)
1332
opts[0] = pdf_to_str_buf(ctx, optarr);
1333
1334
return 1;
1335
}
1336
else
1337
{
1338
n = pdf_array_len(ctx, optarr);
1339
1340
if (opts)
1341
{
1342
for (i = 0; i < n; i++)
1343
{
1344
pdf_obj *elem = pdf_array_get(ctx, optarr, i);
1345
1346
if (pdf_is_array(ctx, elem))
1347
elem = pdf_array_get(ctx, elem, 1);
1348
1349
opts[i] = pdf_to_str_buf(ctx, elem);
1350
}
1351
}
1352
1353
return n;
1354
}
1355
}
1356
1357
void pdf_choice_widget_set_value(fz_context *ctx, pdf_document *doc, pdf_widget *tw, int n, char *opts[])
1358
{
1359
pdf_annot *annot = (pdf_annot *)tw;
1360
pdf_obj *optarr = NULL, *opt = NULL;
1361
int i;
1362
1363
if (!annot)
1364
return;
1365
1366
fz_var(optarr);
1367
fz_var(opt);
1368
fz_try(ctx)
1369
{
1370
if (n != 1)
1371
{
1372
optarr = pdf_new_array(ctx, doc, n);
1373
1374
for (i = 0; i < n; i++)
1375
{
1376
opt = pdf_new_string(ctx, doc, opts[i], strlen(opts[i]));
1377
pdf_array_push(ctx, optarr, opt);
1378
pdf_drop_obj(ctx, opt);
1379
opt = NULL;
1380
}
1381
1382
pdf_dict_put(ctx, annot->obj, PDF_NAME_V, optarr);
1383
pdf_drop_obj(ctx, optarr);
1384
}
1385
else
1386
{
1387
opt = pdf_new_string(ctx, doc, opts[0], strlen(opts[0]));
1388
pdf_dict_put(ctx, annot->obj, PDF_NAME_V, opt);
1389
pdf_drop_obj(ctx, opt);
1390
}
1391
1392
/* FIXME: when n > 1, we should be regenerating the indexes */
1393
pdf_dict_del(ctx, annot->obj, PDF_NAME_I);
1394
1395
pdf_field_mark_dirty(ctx, doc, annot->obj);
1396
if (pdf_field_dirties_document(ctx, doc, annot->obj))
1397
doc->dirty = 1;
1398
}
1399
fz_catch(ctx)
1400
{
1401
pdf_drop_obj(ctx, optarr);
1402
pdf_drop_obj(ctx, opt);
1403
fz_rethrow(ctx);
1404
}
1405
}
1406
1407
int pdf_signature_widget_byte_range(fz_context *ctx, pdf_document *doc, pdf_widget *widget, int (*byte_range)[2])
1408
{
1409
pdf_annot *annot = (pdf_annot *)widget;
1410
pdf_obj *br = pdf_dict_getl(ctx, annot->obj, PDF_NAME_V, PDF_NAME_ByteRange, NULL);
1411
int i, n = pdf_array_len(ctx, br)/2;
1412
1413
if (byte_range)
1414
{
1415
for (i = 0; i < n; i++)
1416
{
1417
byte_range[i][0] = pdf_to_int(ctx, pdf_array_get(ctx, br, 2*i));
1418
byte_range[i][1] = pdf_to_int(ctx, pdf_array_get(ctx, br, 2*i+1));
1419
}
1420
}
1421
1422
return n;
1423
}
1424
1425
int pdf_signature_widget_contents(fz_context *ctx, pdf_document *doc, pdf_widget *widget, char **contents)
1426
{
1427
pdf_annot *annot = (pdf_annot *)widget;
1428
pdf_obj *c = pdf_dict_getl(ctx, annot->obj, PDF_NAME_V, PDF_NAME_Contents, NULL);
1429
if (contents)
1430
*contents = pdf_to_str_buf(ctx, c);
1431
return pdf_to_str_len(ctx, c);
1432
}
1433
1434
void pdf_signature_set_value(fz_context *ctx, pdf_document *doc, pdf_obj *field, pdf_signer *signer)
1435
{
1436
pdf_obj *v;
1437
pdf_obj *indv;
1438
int vnum;
1439
pdf_obj *byte_range;
1440
pdf_obj *contents;
1441
char buf[2048];
1442
pdf_unsaved_sig *unsaved_sig;
1443
1444
memset(buf, 0, sizeof(buf));
1445
1446
vnum = pdf_create_object(ctx, doc);
1447
indv = pdf_new_indirect(ctx, doc, vnum, 0);
1448
pdf_dict_put_drop(ctx, field, PDF_NAME_V, indv);
1449
1450
fz_var(v);
1451
fz_try(ctx)
1452
{
1453
v = pdf_new_dict(ctx, doc, 4);
1454
pdf_update_object(ctx, doc, vnum, v);
1455
}
1456
fz_always(ctx)
1457
{
1458
pdf_drop_obj(ctx, v);
1459
}
1460
fz_catch(ctx)
1461
{
1462
fz_rethrow(ctx);
1463
}
1464
1465
byte_range = pdf_new_array(ctx, doc, 4);
1466
pdf_dict_put_drop(ctx, v, PDF_NAME_ByteRange, byte_range);
1467
1468
contents = pdf_new_string(ctx, doc, buf, sizeof(buf));
1469
pdf_dict_put_drop(ctx, v, PDF_NAME_Contents, contents);
1470
1471
pdf_dict_put_drop(ctx, v, PDF_NAME_Filter, PDF_NAME_Adobe_PPKLite);
1472
pdf_dict_put_drop(ctx, v, PDF_NAME_SubFilter, PDF_NAME_adbe_pkcs7_detached);
1473
1474
/* Record details within the document structure so that contents
1475
* and byte_range can be updated with their correct values at
1476
* saving time */
1477
unsaved_sig = fz_malloc_struct(ctx, pdf_unsaved_sig);
1478
unsaved_sig->field = pdf_keep_obj(ctx, field);
1479
unsaved_sig->signer = pdf_keep_signer(ctx, signer);
1480
unsaved_sig->next = doc->unsaved_sigs;
1481
doc->unsaved_sigs = unsaved_sig;
1482
}
1483
1484