Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/source/pdf/pdf-form.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children | aa33339d6b8a |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 // Copyright (C) 2004-2025 Artifex Software, Inc. | |
| 2 // | |
| 3 // This file is part of MuPDF. | |
| 4 // | |
| 5 // MuPDF is free software: you can redistribute it and/or modify it under the | |
| 6 // terms of the GNU Affero General Public License as published by the Free | |
| 7 // Software Foundation, either version 3 of the License, or (at your option) | |
| 8 // any later version. | |
| 9 // | |
| 10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY | |
| 11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
| 12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more | |
| 13 // details. | |
| 14 // | |
| 15 // You should have received a copy of the GNU Affero General Public License | |
| 16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> | |
| 17 // | |
| 18 // Alternative licensing terms are available from the licensor. | |
| 19 // For commercial licensing, see <https://www.artifex.com/> or contact | |
| 20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, | |
| 21 // CA 94129, USA, for further information. | |
| 22 | |
| 23 #include "mupdf/fitz.h" | |
| 24 #include "pdf-annot-imp.h" | |
| 25 | |
| 26 #include <string.h> | |
| 27 | |
| 28 /* Must be kept in sync with definitions in pdf_util.js */ | |
| 29 enum | |
| 30 { | |
| 31 Display_Visible, | |
| 32 Display_Hidden, | |
| 33 Display_NoPrint, | |
| 34 Display_NoView | |
| 35 }; | |
| 36 | |
| 37 enum | |
| 38 { | |
| 39 SigFlag_SignaturesExist = 1, | |
| 40 SigFlag_AppendOnly = 2 | |
| 41 }; | |
| 42 | |
| 43 const char *pdf_field_value(fz_context *ctx, pdf_obj *field) | |
| 44 { | |
| 45 pdf_obj *v = pdf_dict_get_inheritable(ctx, field, PDF_NAME(V)); | |
| 46 if (pdf_is_name(ctx, v)) | |
| 47 return pdf_to_name(ctx, v); | |
| 48 if (pdf_is_stream(ctx, v)) | |
| 49 { | |
| 50 // FIXME: pdf_dict_put_inheritable... | |
| 51 char *str = pdf_new_utf8_from_pdf_stream_obj(ctx, v); | |
| 52 fz_try(ctx) | |
| 53 pdf_dict_put_text_string(ctx, field, PDF_NAME(V), str); | |
| 54 fz_always(ctx) | |
| 55 fz_free(ctx, str); | |
| 56 fz_catch(ctx) | |
| 57 fz_rethrow(ctx); | |
| 58 v = pdf_dict_get(ctx, field, PDF_NAME(V)); | |
| 59 } | |
| 60 return pdf_to_text_string(ctx, v); | |
| 61 } | |
| 62 | |
| 63 int pdf_field_flags(fz_context *ctx, pdf_obj *obj) | |
| 64 { | |
| 65 return pdf_dict_get_inheritable_int(ctx, obj, PDF_NAME(Ff)); | |
| 66 } | |
| 67 | |
| 68 int pdf_field_type(fz_context *ctx, pdf_obj *obj) | |
| 69 { | |
| 70 pdf_obj *type = pdf_dict_get_inheritable(ctx, obj, PDF_NAME(FT)); | |
| 71 int flags = pdf_field_flags(ctx, obj); | |
| 72 if (pdf_name_eq(ctx, type, PDF_NAME(Btn))) | |
| 73 { | |
| 74 if (flags & PDF_BTN_FIELD_IS_PUSHBUTTON) | |
| 75 return PDF_WIDGET_TYPE_BUTTON; | |
| 76 else if (flags & PDF_BTN_FIELD_IS_RADIO) | |
| 77 return PDF_WIDGET_TYPE_RADIOBUTTON; | |
| 78 else | |
| 79 return PDF_WIDGET_TYPE_CHECKBOX; | |
| 80 } | |
| 81 else if (pdf_name_eq(ctx, type, PDF_NAME(Tx))) | |
| 82 return PDF_WIDGET_TYPE_TEXT; | |
| 83 else if (pdf_name_eq(ctx, type, PDF_NAME(Ch))) | |
| 84 { | |
| 85 if (flags & PDF_CH_FIELD_IS_COMBO) | |
| 86 return PDF_WIDGET_TYPE_COMBOBOX; | |
| 87 else | |
| 88 return PDF_WIDGET_TYPE_LISTBOX; | |
| 89 } | |
| 90 else if (pdf_name_eq(ctx, type, PDF_NAME(Sig))) | |
| 91 return PDF_WIDGET_TYPE_SIGNATURE; | |
| 92 else | |
| 93 return PDF_WIDGET_TYPE_BUTTON; | |
| 94 } | |
| 95 | |
| 96 const char *pdf_field_type_string(fz_context *ctx, pdf_obj *obj) | |
| 97 { | |
| 98 switch (pdf_field_type(ctx, obj)) | |
| 99 { | |
| 100 default: | |
| 101 case PDF_WIDGET_TYPE_BUTTON: return "button"; | |
| 102 case PDF_WIDGET_TYPE_CHECKBOX: return "checkbox"; | |
| 103 case PDF_WIDGET_TYPE_COMBOBOX: return "combobox"; | |
| 104 case PDF_WIDGET_TYPE_LISTBOX: return "listbox"; | |
| 105 case PDF_WIDGET_TYPE_RADIOBUTTON: return "radiobutton"; | |
| 106 case PDF_WIDGET_TYPE_SIGNATURE: return "signature"; | |
| 107 case PDF_WIDGET_TYPE_TEXT: return "text"; | |
| 108 } | |
| 109 } | |
| 110 | |
| 111 /* Find the point in a field hierarchy where all descendants | |
| 112 * share the same name */ | |
| 113 static pdf_obj *find_head_of_field_group(fz_context *ctx, pdf_obj *obj) | |
| 114 { | |
| 115 if (obj == NULL || pdf_dict_get(ctx, obj, PDF_NAME(T))) | |
| 116 return obj; | |
| 117 else | |
| 118 return find_head_of_field_group(ctx, pdf_dict_get(ctx, obj, PDF_NAME(Parent))); | |
| 119 } | |
| 120 | |
| 121 static void pdf_field_mark_dirty(fz_context *ctx, pdf_obj *field) | |
| 122 { | |
| 123 pdf_document *doc = pdf_get_bound_document(ctx, field); | |
| 124 pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME(Kids)); | |
| 125 if (kids) | |
| 126 { | |
| 127 int i, n = pdf_array_len(ctx, kids); | |
| 128 for (i = 0; i < n; i++) | |
| 129 pdf_field_mark_dirty(ctx, pdf_array_get(ctx, kids, i)); | |
| 130 } | |
| 131 pdf_dirty_obj(ctx, field); | |
| 132 if (doc) | |
| 133 doc->resynth_required = 1; | |
| 134 } | |
| 135 | |
| 136 static void update_field_value(fz_context *ctx, pdf_document *doc, pdf_obj *obj, const char *text) | |
| 137 { | |
| 138 const char *old_text; | |
| 139 pdf_obj *grp; | |
| 140 | |
| 141 if (!text) | |
| 142 text = ""; | |
| 143 | |
| 144 /* All fields of the same name should be updated, so | |
| 145 * set the value at the head of the group */ | |
| 146 grp = find_head_of_field_group(ctx, obj); | |
| 147 if (grp) | |
| 148 obj = grp; | |
| 149 | |
| 150 /* Only update if we change the actual value. */ | |
| 151 old_text = pdf_dict_get_text_string(ctx, obj, PDF_NAME(V)); | |
| 152 if (old_text && !strcmp(old_text, text)) | |
| 153 return; | |
| 154 | |
| 155 pdf_dict_put_text_string(ctx, obj, PDF_NAME(V), text); | |
| 156 | |
| 157 pdf_field_mark_dirty(ctx, obj); | |
| 158 } | |
| 159 | |
| 160 static pdf_obj * | |
| 161 pdf_lookup_field_imp(fz_context *ctx, pdf_obj *arr, const char *str, pdf_cycle_list *cycle_up); | |
| 162 | |
| 163 static pdf_obj * | |
| 164 lookup_field_sub(fz_context *ctx, pdf_obj *dict, const char *str, pdf_cycle_list *cycle_up) | |
| 165 { | |
| 166 pdf_obj *kids; | |
| 167 pdf_obj *name; | |
| 168 | |
| 169 name = pdf_dict_get(ctx, dict, PDF_NAME(T)); | |
| 170 | |
| 171 /* If we have a name, check it matches. If it matches, consume that | |
| 172 * portion of str. If not, exit. */ | |
| 173 if (name) | |
| 174 { | |
| 175 const char *match = pdf_to_text_string(ctx, name); | |
| 176 const char *e = str; | |
| 177 size_t len; | |
| 178 while (*e && *e != '.') | |
| 179 e++; | |
| 180 len = e-str; | |
| 181 if (strncmp(str, match, len) != 0 || (match[len] != 0 && match[len] != '.')) | |
| 182 /* name doesn't match. */ | |
| 183 return NULL; | |
| 184 str = e; | |
| 185 if (*str == '.') | |
| 186 str++; | |
| 187 } | |
| 188 | |
| 189 /* If there is a kids array, but the search string is not empty, we have | |
| 190 encountered an internal field which represents a set of terminal fields. */ | |
| 191 | |
| 192 /* If there is a kids array and the search string is not empty, | |
| 193 walk those looking for the appropriate one. */ | |
| 194 kids = pdf_dict_get(ctx, dict, PDF_NAME(Kids)); | |
| 195 if (kids && *str != 0) | |
| 196 return pdf_lookup_field_imp(ctx, kids, str, cycle_up); | |
| 197 | |
| 198 /* The field may be a terminal or an internal field at this point. | |
| 199 Accept it as the match if the match string is exhausted. */ | |
| 200 if (*str == 0) | |
| 201 return dict; | |
| 202 | |
| 203 return NULL; | |
| 204 } | |
| 205 | |
| 206 static pdf_obj * | |
| 207 pdf_lookup_field_imp(fz_context *ctx, pdf_obj *arr, const char *str, pdf_cycle_list *cycle_up) | |
| 208 { | |
| 209 pdf_cycle_list cycle; | |
| 210 int len = pdf_array_len(ctx, arr); | |
| 211 int i; | |
| 212 | |
| 213 for (i = 0; i < len; i++) | |
| 214 { | |
| 215 pdf_obj *k = pdf_array_get(ctx, arr, i); | |
| 216 pdf_obj *found; | |
| 217 if (pdf_cycle(ctx, &cycle, cycle_up, k)) | |
| 218 fz_throw(ctx, FZ_ERROR_FORMAT, "cycle in fields"); | |
| 219 found = lookup_field_sub(ctx, k, str, &cycle); | |
| 220 if (found) | |
| 221 return found; | |
| 222 } | |
| 223 | |
| 224 return NULL; | |
| 225 } | |
| 226 | |
| 227 pdf_obj * | |
| 228 pdf_lookup_field(fz_context *ctx, pdf_obj *arr, const char *str) | |
| 229 { | |
| 230 return pdf_lookup_field_imp(ctx, arr, str, NULL); | |
| 231 } | |
| 232 | |
| 233 static void reset_form_field(fz_context *ctx, pdf_document *doc, pdf_obj *field) | |
| 234 { | |
| 235 /* Set V to DV wherever DV is present, and delete V where DV is not. | |
| 236 * FIXME: we assume for now that V has not been set unequal | |
| 237 * to DV higher in the hierarchy than "field". | |
| 238 * | |
| 239 * At the bottom of the hierarchy we may find widget annotations | |
| 240 * that aren't also fields, but DV and V will not be present in their | |
| 241 * dictionaries, and attempts to remove V will be harmless. */ | |
| 242 pdf_obj *dv = pdf_dict_get(ctx, field, PDF_NAME(DV)); | |
| 243 pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME(Kids)); | |
| 244 | |
| 245 if (dv) | |
| 246 pdf_dict_put(ctx, field, PDF_NAME(V), dv); | |
| 247 else | |
| 248 pdf_dict_del(ctx, field, PDF_NAME(V)); | |
| 249 | |
| 250 if (kids == NULL) | |
| 251 { | |
| 252 /* The leaves of the tree are widget annotations | |
| 253 * In some cases we need to update the appearance state; | |
| 254 * in others we need to mark the field as dirty so that | |
| 255 * the appearance stream will be regenerated. */ | |
| 256 switch (pdf_field_type(ctx, field)) | |
| 257 { | |
| 258 case PDF_WIDGET_TYPE_CHECKBOX: | |
| 259 case PDF_WIDGET_TYPE_RADIOBUTTON: | |
| 260 { | |
| 261 pdf_obj *leafv = pdf_dict_get_inheritable(ctx, field, PDF_NAME(V)); | |
| 262 pdf_obj *ap = pdf_dict_get(ctx, field, PDF_NAME(AP)); | |
| 263 pdf_obj *n = pdf_dict_get(ctx, ap, PDF_NAME(N)); | |
| 264 | |
| 265 /* Value does not refer to any appearance state in the | |
| 266 normal appearance stream dictionary, default to Off instead. */ | |
| 267 if (pdf_is_dict(ctx, n) && !pdf_dict_get(ctx, n, leafv)) | |
| 268 leafv = NULL; | |
| 269 | |
| 270 if (!pdf_is_name(ctx, leafv)) | |
| 271 leafv = PDF_NAME(Off); | |
| 272 pdf_dict_put(ctx, field, PDF_NAME(AS), leafv); | |
| 273 } | |
| 274 pdf_field_mark_dirty(ctx, field); | |
| 275 break; | |
| 276 | |
| 277 case PDF_WIDGET_TYPE_BUTTON: | |
| 278 case PDF_WIDGET_TYPE_SIGNATURE: | |
| 279 /* Pushbuttons and signatures have no value to reset. */ | |
| 280 break; | |
| 281 | |
| 282 default: | |
| 283 pdf_field_mark_dirty(ctx, field); | |
| 284 break; | |
| 285 } | |
| 286 } | |
| 287 } | |
| 288 | |
| 289 void pdf_field_reset(fz_context *ctx, pdf_document *doc, pdf_obj *field) | |
| 290 { | |
| 291 pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME(Kids)); | |
| 292 | |
| 293 reset_form_field(ctx, doc, field); | |
| 294 | |
| 295 if (kids) | |
| 296 { | |
| 297 int i, n = pdf_array_len(ctx, kids); | |
| 298 | |
| 299 for (i = 0; i < n; i++) | |
| 300 pdf_field_reset(ctx, doc, pdf_array_get(ctx, kids, i)); | |
| 301 } | |
| 302 } | |
| 303 | |
| 304 static void add_field_hierarchy_to_array(fz_context *ctx, pdf_obj *array, pdf_obj *field, pdf_obj *fields, int exclude) | |
| 305 { | |
| 306 pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME(Kids)); | |
| 307 char *needle = pdf_load_field_name(ctx, field); | |
| 308 int i, n; | |
| 309 | |
| 310 fz_try(ctx) | |
| 311 { | |
| 312 n = pdf_array_len(ctx, fields); | |
| 313 for (i = 0; i < n; i++) | |
| 314 { | |
| 315 char *name = pdf_load_field_name(ctx, pdf_array_get(ctx, fields, i)); | |
| 316 int found = !strcmp(needle, name); | |
| 317 fz_free(ctx, name); | |
| 318 if (found) | |
| 319 break; | |
| 320 } | |
| 321 } | |
| 322 fz_always(ctx) | |
| 323 fz_free(ctx, needle); | |
| 324 fz_catch(ctx) | |
| 325 fz_rethrow(ctx); | |
| 326 | |
| 327 if ((exclude && i < n) || (!exclude && i == n)) | |
| 328 return; | |
| 329 | |
| 330 pdf_array_push(ctx, array, field); | |
| 331 | |
| 332 if (kids) | |
| 333 { | |
| 334 n = pdf_array_len(ctx, kids); | |
| 335 | |
| 336 for (i = 0; i < n; i++) | |
| 337 add_field_hierarchy_to_array(ctx, array, pdf_array_get(ctx, kids, i), fields, exclude); | |
| 338 } | |
| 339 } | |
| 340 | |
| 341 /* | |
| 342 When resetting or submitting a form, the fields to act upon are defined | |
| 343 by an array of either field references or field names, plus a flag determining | |
| 344 whether to act upon the fields in the array, or all fields other than those in | |
| 345 the array. specified_fields interprets this information and produces the array | |
| 346 of fields to be acted upon. | |
| 347 */ | |
| 348 static pdf_obj *specified_fields(fz_context *ctx, pdf_document *doc, pdf_obj *fields, int exclude) | |
| 349 { | |
| 350 pdf_obj *form = pdf_dict_getl(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root), PDF_NAME(AcroForm), PDF_NAME(Fields), NULL); | |
| 351 int i, n; | |
| 352 pdf_obj *result = pdf_new_array(ctx, doc, 0); | |
| 353 | |
| 354 fz_try(ctx) | |
| 355 { | |
| 356 n = pdf_array_len(ctx, fields); | |
| 357 | |
| 358 for (i = 0; i < n; i++) | |
| 359 { | |
| 360 pdf_obj *field = pdf_array_get(ctx, fields, i); | |
| 361 | |
| 362 if (pdf_is_string(ctx, field)) | |
| 363 field = pdf_lookup_field(ctx, form, pdf_to_str_buf(ctx, field)); | |
| 364 | |
| 365 if (field) | |
| 366 add_field_hierarchy_to_array(ctx, result, field, fields, exclude); | |
| 367 } | |
| 368 } | |
| 369 fz_catch(ctx) | |
| 370 { | |
| 371 pdf_drop_obj(ctx, result); | |
| 372 fz_rethrow(ctx); | |
| 373 } | |
| 374 | |
| 375 return result; | |
| 376 } | |
| 377 | |
| 378 void pdf_reset_form(fz_context *ctx, pdf_document *doc, pdf_obj *fields, int exclude) | |
| 379 { | |
| 380 pdf_obj *sfields = specified_fields(ctx, doc, fields, exclude); | |
| 381 fz_try(ctx) | |
| 382 { | |
| 383 int i, n = pdf_array_len(ctx, sfields); | |
| 384 for (i = 0; i < n; i++) | |
| 385 reset_form_field(ctx, doc, pdf_array_get(ctx, sfields, i)); | |
| 386 doc->recalculate = 1; | |
| 387 } | |
| 388 fz_always(ctx) | |
| 389 pdf_drop_obj(ctx, sfields); | |
| 390 fz_catch(ctx) | |
| 391 fz_rethrow(ctx); | |
| 392 } | |
| 393 | |
| 394 typedef struct | |
| 395 { | |
| 396 pdf_obj *pageobj; | |
| 397 pdf_obj *chk; | |
| 398 } lookup_state; | |
| 399 | |
| 400 static void *find_widget_on_page(fz_context *ctx, fz_page *page_, void *state_) | |
| 401 { | |
| 402 lookup_state *state = (lookup_state *) state_; | |
| 403 pdf_page *page = (pdf_page *) page_; | |
| 404 pdf_annot *widget; | |
| 405 | |
| 406 if (state->pageobj && pdf_objcmp_resolve(ctx, state->pageobj, page->obj)) | |
| 407 return NULL; | |
| 408 | |
| 409 for (widget = pdf_first_widget(ctx, page); widget != NULL; widget = pdf_next_widget(ctx, widget)) | |
| 410 { | |
| 411 if (!pdf_objcmp_resolve(ctx, state->chk, widget->obj)) | |
| 412 return widget; | |
| 413 } | |
| 414 | |
| 415 return NULL; | |
| 416 } | |
| 417 | |
| 418 static pdf_annot *find_widget(fz_context *ctx, pdf_document *doc, pdf_obj *chk) | |
| 419 { | |
| 420 lookup_state state; | |
| 421 | |
| 422 state.pageobj = pdf_dict_get(ctx, chk, PDF_NAME(P)); | |
| 423 state.chk = chk; | |
| 424 | |
| 425 return fz_process_opened_pages(ctx, (fz_document *) doc, find_widget_on_page, &state); | |
| 426 } | |
| 427 | |
| 428 static void set_check(fz_context *ctx, pdf_document *doc, pdf_obj *chk, pdf_obj *name) | |
| 429 { | |
| 430 pdf_obj *n = pdf_dict_getp(ctx, chk, "AP/N"); | |
| 431 pdf_obj *val; | |
| 432 | |
| 433 /* If name is a possible value of this check | |
| 434 * box then use it, otherwise use "Off" */ | |
| 435 if (pdf_dict_get(ctx, n, name)) | |
| 436 val = name; | |
| 437 else | |
| 438 val = PDF_NAME(Off); | |
| 439 | |
| 440 if (pdf_name_eq(ctx, pdf_dict_get(ctx, chk, PDF_NAME(AS)), val)) | |
| 441 return; | |
| 442 | |
| 443 pdf_dict_put(ctx, chk, PDF_NAME(AS), val); | |
| 444 pdf_set_annot_has_changed(ctx, find_widget(ctx, doc, chk)); | |
| 445 } | |
| 446 | |
| 447 /* Set the values of all fields in a group defined by a node | |
| 448 * in the hierarchy */ | |
| 449 static void set_check_grp(fz_context *ctx, pdf_document *doc, pdf_obj *grp, pdf_obj *val) | |
| 450 { | |
| 451 pdf_obj *kids = pdf_dict_get(ctx, grp, PDF_NAME(Kids)); | |
| 452 | |
| 453 if (kids == NULL) | |
| 454 { | |
| 455 set_check(ctx, doc, grp, val); | |
| 456 } | |
| 457 else | |
| 458 { | |
| 459 int i, n = pdf_array_len(ctx, kids); | |
| 460 | |
| 461 for (i = 0; i < n; i++) | |
| 462 set_check_grp(ctx, doc, pdf_array_get(ctx, kids, i), val); | |
| 463 } | |
| 464 } | |
| 465 | |
| 466 void pdf_calculate_form(fz_context *ctx, pdf_document *doc) | |
| 467 { | |
| 468 if (doc->js) | |
| 469 { | |
| 470 fz_try(ctx) | |
| 471 { | |
| 472 pdf_obj *co = pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/AcroForm/CO"); | |
| 473 int i, n = pdf_array_len(ctx, co); | |
| 474 for (i = 0; i < n; i++) | |
| 475 { | |
| 476 pdf_obj *field = pdf_array_get(ctx, co, i); | |
| 477 pdf_field_event_calculate(ctx, doc, field); | |
| 478 } | |
| 479 } | |
| 480 fz_always(ctx) | |
| 481 doc->recalculate = 0; | |
| 482 fz_catch(ctx) | |
| 483 fz_rethrow(ctx); | |
| 484 } | |
| 485 } | |
| 486 | |
| 487 static pdf_obj *find_on_state(fz_context *ctx, pdf_obj *dict) | |
| 488 { | |
| 489 int i, n = pdf_dict_len(ctx, dict); | |
| 490 for (i = 0; i < n; ++i) | |
| 491 { | |
| 492 pdf_obj *key = pdf_dict_get_key(ctx, dict, i); | |
| 493 if (key != PDF_NAME(Off)) | |
| 494 return key; | |
| 495 } | |
| 496 return NULL; | |
| 497 } | |
| 498 | |
| 499 pdf_obj *pdf_button_field_on_state(fz_context *ctx, pdf_obj *field) | |
| 500 { | |
| 501 pdf_obj *ap = pdf_dict_get(ctx, field, PDF_NAME(AP)); | |
| 502 pdf_obj *on = find_on_state(ctx, pdf_dict_get(ctx, ap, PDF_NAME(N))); | |
| 503 if (!on) on = find_on_state(ctx, pdf_dict_get(ctx, ap, PDF_NAME(D))); | |
| 504 if (!on) on = PDF_NAME(Yes); | |
| 505 return on; | |
| 506 } | |
| 507 | |
| 508 static void | |
| 509 begin_annot_op(fz_context *ctx, pdf_annot *annot, const char *op) | |
| 510 { | |
| 511 if (!annot->page) | |
| 512 fz_throw(ctx, FZ_ERROR_ARGUMENT, "annotation not bound to any page"); | |
| 513 | |
| 514 pdf_begin_operation(ctx, annot->page->doc, op); | |
| 515 } | |
| 516 | |
| 517 static void | |
| 518 end_annot_op(fz_context *ctx, pdf_annot *annot) | |
| 519 { | |
| 520 pdf_end_operation(ctx, annot->page->doc); | |
| 521 } | |
| 522 | |
| 523 static void | |
| 524 abandon_annot_op(fz_context *ctx, pdf_annot *annot) | |
| 525 { | |
| 526 pdf_abandon_operation(ctx, annot->page->doc); | |
| 527 } | |
| 528 | |
| 529 static void toggle_check_box(fz_context *ctx, pdf_annot *annot) | |
| 530 { | |
| 531 pdf_document *doc = annot->page->doc; | |
| 532 | |
| 533 begin_annot_op(ctx, annot, "Toggle checkbox"); | |
| 534 | |
| 535 fz_try(ctx) | |
| 536 { | |
| 537 pdf_obj *field = annot->obj; | |
| 538 int ff = pdf_field_flags(ctx, field); | |
| 539 int is_radio = (ff & PDF_BTN_FIELD_IS_RADIO); | |
| 540 int is_no_toggle_to_off = (ff & PDF_BTN_FIELD_IS_NO_TOGGLE_TO_OFF); | |
| 541 pdf_obj *grp, *as, *val; | |
| 542 | |
| 543 grp = find_head_of_field_group(ctx, field); | |
| 544 if (!grp) | |
| 545 grp = field; | |
| 546 | |
| 547 /* TODO: check V value as well as or instead of AS? */ | |
| 548 as = pdf_dict_get(ctx, field, PDF_NAME(AS)); | |
| 549 if (pdf_is_name(ctx, as) && !pdf_name_eq(ctx, as, PDF_NAME(Off))) | |
| 550 { | |
| 551 if (is_radio && is_no_toggle_to_off) | |
| 552 { | |
| 553 end_annot_op(ctx, annot); | |
| 554 break; | |
| 555 } | |
| 556 val = PDF_NAME(Off); | |
| 557 } | |
| 558 else | |
| 559 { | |
| 560 val = pdf_button_field_on_state(ctx, field); | |
| 561 } | |
| 562 | |
| 563 pdf_dict_put(ctx, grp, PDF_NAME(V), val); | |
| 564 set_check_grp(ctx, doc, grp, val); | |
| 565 doc->recalculate = 1; | |
| 566 end_annot_op(ctx, annot); | |
| 567 } | |
| 568 fz_catch(ctx) | |
| 569 { | |
| 570 abandon_annot_op(ctx, annot); | |
| 571 fz_rethrow(ctx); | |
| 572 } | |
| 573 | |
| 574 pdf_set_annot_has_changed(ctx, annot); | |
| 575 } | |
| 576 | |
| 577 int pdf_has_unsaved_changes(fz_context *ctx, pdf_document *doc) | |
| 578 { | |
| 579 int i; | |
| 580 | |
| 581 if (doc->num_incremental_sections == 0) | |
| 582 return 0; | |
| 583 | |
| 584 for (i = 0; i < doc->xref_sections->num_objects; i++) | |
| 585 if (doc->xref_sections->subsec->table[i].type != 0) | |
| 586 break; | |
| 587 return i != doc->xref_sections->num_objects; | |
| 588 } | |
| 589 | |
| 590 int pdf_was_repaired(fz_context *ctx, pdf_document *doc) | |
| 591 { | |
| 592 return doc->repair_attempted; | |
| 593 } | |
| 594 | |
| 595 int pdf_toggle_widget(fz_context *ctx, pdf_annot *widget) | |
| 596 { | |
| 597 switch (pdf_widget_type(ctx, widget)) | |
| 598 { | |
| 599 default: | |
| 600 return 0; | |
| 601 case PDF_WIDGET_TYPE_CHECKBOX: | |
| 602 case PDF_WIDGET_TYPE_RADIOBUTTON: | |
| 603 toggle_check_box(ctx, widget); | |
| 604 return 1; | |
| 605 } | |
| 606 } | |
| 607 | |
| 608 int | |
| 609 pdf_update_page(fz_context *ctx, pdf_page *page) | |
| 610 { | |
| 611 pdf_annot *annot; | |
| 612 pdf_annot *widget; | |
| 613 int changed = 0; | |
| 614 | |
| 615 fz_try(ctx) | |
| 616 { | |
| 617 pdf_begin_implicit_operation(ctx, page->doc); | |
| 618 if (page->doc->recalculate) | |
| 619 pdf_calculate_form(ctx, page->doc); | |
| 620 | |
| 621 for (annot = page->annots; annot; annot = annot->next) | |
| 622 if (pdf_update_annot(ctx, annot)) | |
| 623 changed = 1; | |
| 624 for (widget = page->widgets; widget; widget = widget->next) | |
| 625 if (pdf_update_annot(ctx, widget)) | |
| 626 changed = 1; | |
| 627 pdf_end_operation(ctx, page->doc); | |
| 628 } | |
| 629 fz_catch(ctx) | |
| 630 { | |
| 631 pdf_abandon_operation(ctx, page->doc); | |
| 632 fz_rethrow(ctx); | |
| 633 } | |
| 634 | |
| 635 return changed; | |
| 636 } | |
| 637 | |
| 638 pdf_annot *pdf_first_widget(fz_context *ctx, pdf_page *page) | |
| 639 { | |
| 640 return page->widgets; | |
| 641 } | |
| 642 | |
| 643 pdf_annot *pdf_next_widget(fz_context *ctx, pdf_annot *widget) | |
| 644 { | |
| 645 return widget->next; | |
| 646 } | |
| 647 | |
| 648 enum pdf_widget_type pdf_widget_type(fz_context *ctx, pdf_annot *widget) | |
| 649 { | |
| 650 enum pdf_widget_type ret = PDF_WIDGET_TYPE_BUTTON; | |
| 651 | |
| 652 pdf_annot_push_local_xref(ctx, widget); | |
| 653 | |
| 654 fz_try(ctx) | |
| 655 { | |
| 656 pdf_obj *subtype = pdf_dict_get(ctx, widget->obj, PDF_NAME(Subtype)); | |
| 657 if (pdf_name_eq(ctx, subtype, PDF_NAME(Widget))) | |
| 658 ret = pdf_field_type(ctx, widget->obj); | |
| 659 } | |
| 660 fz_always(ctx) | |
| 661 pdf_annot_pop_local_xref(ctx, widget); | |
| 662 fz_catch(ctx) | |
| 663 fz_rethrow(ctx); | |
| 664 | |
| 665 return ret; | |
| 666 } | |
| 667 | |
| 668 static int set_validated_field_value(fz_context *ctx, pdf_document *doc, pdf_obj *field, const char *text, int ignore_trigger_events) | |
| 669 { | |
| 670 char *newtext = NULL; | |
| 671 | |
| 672 if (!ignore_trigger_events) | |
| 673 { | |
| 674 if (!pdf_field_event_validate(ctx, doc, field, text, &newtext)) | |
| 675 return 0; | |
| 676 } | |
| 677 | |
| 678 update_field_value(ctx, doc, field, newtext ? newtext : text); | |
| 679 | |
| 680 fz_free(ctx, newtext); | |
| 681 | |
| 682 return 1; | |
| 683 } | |
| 684 | |
| 685 static void update_checkbox_selector(fz_context *ctx, pdf_document *doc, pdf_obj *field, const char *val) | |
| 686 { | |
| 687 pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME(Kids)); | |
| 688 | |
| 689 if (kids) | |
| 690 { | |
| 691 int i, n = pdf_array_len(ctx, kids); | |
| 692 | |
| 693 for (i = 0; i < n; i++) | |
| 694 update_checkbox_selector(ctx, doc, pdf_array_get(ctx, kids, i), val); | |
| 695 } | |
| 696 else | |
| 697 { | |
| 698 pdf_obj *n = pdf_dict_getp(ctx, field, "AP/N"); | |
| 699 pdf_obj *oval; | |
| 700 | |
| 701 if (pdf_dict_gets(ctx, n, val)) | |
| 702 oval = pdf_new_name(ctx, val); | |
| 703 else | |
| 704 oval = PDF_NAME(Off); | |
| 705 pdf_dict_put_drop(ctx, field, PDF_NAME(AS), oval); | |
| 706 } | |
| 707 } | |
| 708 | |
| 709 static int set_checkbox_value(fz_context *ctx, pdf_document *doc, pdf_obj *field, const char *val) | |
| 710 { | |
| 711 update_checkbox_selector(ctx, doc, field, val); | |
| 712 update_field_value(ctx, doc, field, val); | |
| 713 return 1; | |
| 714 } | |
| 715 | |
| 716 int pdf_set_field_value(fz_context *ctx, pdf_document *doc, pdf_obj *field, const char *text, int ignore_trigger_events) | |
| 717 { | |
| 718 int accepted = 0; | |
| 719 | |
| 720 switch (pdf_field_type(ctx, field)) | |
| 721 { | |
| 722 case PDF_WIDGET_TYPE_TEXT: | |
| 723 case PDF_WIDGET_TYPE_COMBOBOX: | |
| 724 case PDF_WIDGET_TYPE_LISTBOX: | |
| 725 accepted = set_validated_field_value(ctx, doc, field, text, ignore_trigger_events); | |
| 726 break; | |
| 727 | |
| 728 case PDF_WIDGET_TYPE_CHECKBOX: | |
| 729 case PDF_WIDGET_TYPE_RADIOBUTTON: | |
| 730 accepted = set_checkbox_value(ctx, doc, field, text); | |
| 731 break; | |
| 732 | |
| 733 default: | |
| 734 update_field_value(ctx, doc, field, text); | |
| 735 accepted = 1; | |
| 736 break; | |
| 737 } | |
| 738 | |
| 739 if (!ignore_trigger_events) | |
| 740 doc->recalculate = 1; | |
| 741 | |
| 742 return accepted; | |
| 743 } | |
| 744 | |
| 745 char *pdf_field_border_style(fz_context *ctx, pdf_obj *field) | |
| 746 { | |
| 747 const char *bs = pdf_to_name(ctx, pdf_dict_getl(ctx, field, PDF_NAME(BS), PDF_NAME(S), NULL)); | |
| 748 switch (*bs) | |
| 749 { | |
| 750 case 'S': return "Solid"; | |
| 751 case 'D': return "Dashed"; | |
| 752 case 'B': return "Beveled"; | |
| 753 case 'I': return "Inset"; | |
| 754 case 'U': return "Underline"; | |
| 755 } | |
| 756 return "Solid"; | |
| 757 } | |
| 758 | |
| 759 void pdf_field_set_border_style(fz_context *ctx, pdf_obj *field, const char *text) | |
| 760 { | |
| 761 pdf_obj *val; | |
| 762 | |
| 763 if (!strcmp(text, "Solid")) | |
| 764 val = PDF_NAME(S); | |
| 765 else if (!strcmp(text, "Dashed")) | |
| 766 val = PDF_NAME(D); | |
| 767 else if (!strcmp(text, "Beveled")) | |
| 768 val = PDF_NAME(B); | |
| 769 else if (!strcmp(text, "Inset")) | |
| 770 val = PDF_NAME(I); | |
| 771 else if (!strcmp(text, "Underline")) | |
| 772 val = PDF_NAME(U); | |
| 773 else | |
| 774 return; | |
| 775 | |
| 776 pdf_dict_putl_drop(ctx, field, val, PDF_NAME(BS), PDF_NAME(S), NULL); | |
| 777 pdf_field_mark_dirty(ctx, field); | |
| 778 } | |
| 779 | |
| 780 void pdf_field_set_button_caption(fz_context *ctx, pdf_obj *field, const char *text) | |
| 781 { | |
| 782 if (pdf_field_type(ctx, field) == PDF_WIDGET_TYPE_BUTTON) | |
| 783 { | |
| 784 pdf_obj *val = pdf_new_text_string(ctx, text); | |
| 785 pdf_dict_putl_drop(ctx, field, val, PDF_NAME(MK), PDF_NAME(CA), NULL); | |
| 786 pdf_field_mark_dirty(ctx, field); | |
| 787 } | |
| 788 } | |
| 789 | |
| 790 int pdf_field_display(fz_context *ctx, pdf_obj *field) | |
| 791 { | |
| 792 pdf_obj *kids; | |
| 793 int f, res = Display_Visible; | |
| 794 | |
| 795 /* Base response on first of children. Not ideal, | |
| 796 * but not clear how to handle children with | |
| 797 * differing values */ | |
| 798 while ((kids = pdf_dict_get(ctx, field, PDF_NAME(Kids))) != NULL) | |
| 799 field = pdf_array_get(ctx, kids, 0); | |
| 800 | |
| 801 f = pdf_dict_get_int(ctx, field, PDF_NAME(F)); | |
| 802 | |
| 803 if (f & PDF_ANNOT_IS_HIDDEN) | |
| 804 { | |
| 805 res = Display_Hidden; | |
| 806 } | |
| 807 else if (f & PDF_ANNOT_IS_PRINT) | |
| 808 { | |
| 809 if (f & PDF_ANNOT_IS_NO_VIEW) | |
| 810 res = Display_NoView; | |
| 811 } | |
| 812 else | |
| 813 { | |
| 814 if (f & PDF_ANNOT_IS_NO_VIEW) | |
| 815 res = Display_Hidden; | |
| 816 else | |
| 817 res = Display_NoPrint; | |
| 818 } | |
| 819 | |
| 820 return res; | |
| 821 } | |
| 822 | |
| 823 /* | |
| 824 * get the field name in a char buffer that has spare room to | |
| 825 * add more characters at the end. | |
| 826 */ | |
| 827 static char *load_field_name(fz_context *ctx, pdf_obj *field, int spare, pdf_cycle_list *cycle_up) | |
| 828 { | |
| 829 pdf_cycle_list cycle; | |
| 830 char *res = NULL; | |
| 831 pdf_obj *parent; | |
| 832 const char *lname; | |
| 833 int llen; | |
| 834 | |
| 835 if (pdf_cycle(ctx, &cycle, cycle_up, field)) | |
| 836 fz_throw(ctx, FZ_ERROR_FORMAT, "Cycle in field parents"); | |
| 837 | |
| 838 parent = pdf_dict_get(ctx, field, PDF_NAME(Parent)); | |
| 839 lname = pdf_dict_get_text_string(ctx, field, PDF_NAME(T)); | |
| 840 llen = (int)strlen(lname); | |
| 841 | |
| 842 // Limit fields to 16K | |
| 843 if (llen > (16 << 10) || llen + spare > (16 << 10)) | |
| 844 fz_throw(ctx, FZ_ERROR_LIMIT, "Field name too long"); | |
| 845 | |
| 846 /* | |
| 847 * If we found a name at this point in the field hierarchy | |
| 848 * then we'll need extra space for it and a dot | |
| 849 */ | |
| 850 if (llen) | |
| 851 spare += llen+1; | |
| 852 | |
| 853 if (parent) | |
| 854 { | |
| 855 res = load_field_name(ctx, parent, spare, &cycle); | |
| 856 } | |
| 857 else | |
| 858 { | |
| 859 res = Memento_label(fz_malloc(ctx, spare+1), "form_field_name"); | |
| 860 res[0] = 0; | |
| 861 } | |
| 862 | |
| 863 if (llen) | |
| 864 { | |
| 865 if (res[0]) | |
| 866 strcat(res, "."); | |
| 867 | |
| 868 strcat(res, lname); | |
| 869 } | |
| 870 | |
| 871 return res; | |
| 872 } | |
| 873 | |
| 874 char *pdf_load_field_name(fz_context *ctx, pdf_obj *field) | |
| 875 { | |
| 876 return load_field_name(ctx, field, 0, NULL); | |
| 877 } | |
| 878 | |
| 879 void pdf_create_field_name(fz_context *ctx, pdf_document *doc, const char *prefix, char *buf, size_t len) | |
| 880 { | |
| 881 pdf_obj *form = pdf_dict_getl(ctx, pdf_trailer(ctx, doc), | |
| 882 PDF_NAME(Root), PDF_NAME(AcroForm), PDF_NAME(Fields), NULL); | |
| 883 int i; | |
| 884 for (i = 0; i < 65536; ++i) { | |
| 885 fz_snprintf(buf, len, "%s%d", prefix, i); | |
| 886 if (!pdf_lookup_field(ctx, form, buf)) | |
| 887 return; | |
| 888 } | |
| 889 fz_throw(ctx, FZ_ERROR_LIMIT, "Could not create unique field name."); | |
| 890 } | |
| 891 | |
| 892 const char *pdf_field_label(fz_context *ctx, pdf_obj *field) | |
| 893 { | |
| 894 pdf_obj *label = pdf_dict_get_inheritable(ctx, field, PDF_NAME(TU)); | |
| 895 if (!label) | |
| 896 label = pdf_dict_get_inheritable(ctx, field, PDF_NAME(T)); | |
| 897 if (label) | |
| 898 return pdf_to_text_string(ctx, label); | |
| 899 return "Unnamed"; | |
| 900 } | |
| 901 | |
| 902 void pdf_field_set_display(fz_context *ctx, pdf_obj *field, int d) | |
| 903 { | |
| 904 pdf_obj *kids = pdf_dict_get(ctx, field, PDF_NAME(Kids)); | |
| 905 | |
| 906 if (!kids) | |
| 907 { | |
| 908 int mask = (PDF_ANNOT_IS_HIDDEN|PDF_ANNOT_IS_PRINT|PDF_ANNOT_IS_NO_VIEW); | |
| 909 int f = pdf_dict_get_int(ctx, field, PDF_NAME(F)) & ~mask; | |
| 910 | |
| 911 switch (d) | |
| 912 { | |
| 913 case Display_Visible: | |
| 914 f |= PDF_ANNOT_IS_PRINT; | |
| 915 break; | |
| 916 case Display_Hidden: | |
| 917 f |= PDF_ANNOT_IS_HIDDEN; | |
| 918 break; | |
| 919 case Display_NoView: | |
| 920 f |= (PDF_ANNOT_IS_PRINT|PDF_ANNOT_IS_NO_VIEW); | |
| 921 break; | |
| 922 case Display_NoPrint: | |
| 923 break; | |
| 924 } | |
| 925 | |
| 926 pdf_dict_put_int(ctx, field, PDF_NAME(F), f); | |
| 927 } | |
| 928 else | |
| 929 { | |
| 930 int i, n = pdf_array_len(ctx, kids); | |
| 931 | |
| 932 for (i = 0; i < n; i++) | |
| 933 pdf_field_set_display(ctx, pdf_array_get(ctx, kids, i), d); | |
| 934 } | |
| 935 } | |
| 936 | |
| 937 void pdf_field_set_fill_color(fz_context *ctx, pdf_obj *field, pdf_obj *col) | |
| 938 { | |
| 939 /* col == NULL mean transparent, but we can simply pass it on as with | |
| 940 * non-NULL values because pdf_dict_putp interprets a NULL value as | |
| 941 * delete */ | |
| 942 pdf_dict_putl(ctx, field, col, PDF_NAME(MK), PDF_NAME(BG), NULL); | |
| 943 pdf_field_mark_dirty(ctx, field); | |
| 944 } | |
| 945 | |
| 946 void pdf_field_set_text_color(fz_context *ctx, pdf_obj *field, pdf_obj *col) | |
| 947 { | |
| 948 char buf[100]; | |
| 949 const char *font; | |
| 950 float size, color[4]; | |
| 951 /* TODO? */ | |
| 952 const char *da = pdf_to_str_buf(ctx, pdf_dict_get_inheritable(ctx, field, PDF_NAME(DA))); | |
| 953 int n; | |
| 954 | |
| 955 pdf_parse_default_appearance(ctx, da, &font, &size, &n, color); | |
| 956 | |
| 957 switch (pdf_array_len(ctx, col)) | |
| 958 { | |
| 959 default: | |
| 960 n = 0; | |
| 961 color[0] = color[1] = color[2] = color[3] = 0; | |
| 962 break; | |
| 963 case 1: | |
| 964 n = 1; | |
| 965 color[0] = pdf_array_get_real(ctx, col, 0); | |
| 966 break; | |
| 967 case 3: | |
| 968 n = 3; | |
| 969 color[0] = pdf_array_get_real(ctx, col, 0); | |
| 970 color[1] = pdf_array_get_real(ctx, col, 1); | |
| 971 color[2] = pdf_array_get_real(ctx, col, 2); | |
| 972 break; | |
| 973 case 4: | |
| 974 n = 4; | |
| 975 color[0] = pdf_array_get_real(ctx, col, 0); | |
| 976 color[1] = pdf_array_get_real(ctx, col, 1); | |
| 977 color[2] = pdf_array_get_real(ctx, col, 2); | |
| 978 color[3] = pdf_array_get_real(ctx, col, 3); | |
| 979 break; | |
| 980 } | |
| 981 | |
| 982 pdf_print_default_appearance(ctx, buf, sizeof buf, font, size, n, color); | |
| 983 pdf_dict_put_string(ctx, field, PDF_NAME(DA), buf, strlen(buf)); | |
| 984 pdf_field_mark_dirty(ctx, field); | |
| 985 } | |
| 986 | |
| 987 pdf_annot * | |
| 988 pdf_keep_widget(fz_context *ctx, pdf_annot *widget) | |
| 989 { | |
| 990 return pdf_keep_annot(ctx, widget); | |
| 991 } | |
| 992 | |
| 993 void | |
| 994 pdf_drop_widget(fz_context *ctx, pdf_annot *widget) | |
| 995 { | |
| 996 pdf_drop_annot(ctx, widget); | |
| 997 } | |
| 998 | |
| 999 void | |
| 1000 pdf_drop_widgets(fz_context *ctx, pdf_annot *widget) | |
| 1001 { | |
| 1002 while (widget) | |
| 1003 { | |
| 1004 pdf_annot *next = widget->next; | |
| 1005 pdf_drop_widget(ctx, widget); | |
| 1006 widget = next; | |
| 1007 } | |
| 1008 } | |
| 1009 | |
| 1010 pdf_annot * | |
| 1011 pdf_create_signature_widget(fz_context *ctx, pdf_page *page, char *name) | |
| 1012 { | |
| 1013 fz_rect rect = { 12, 12, 12+100, 12+50 }; | |
| 1014 pdf_annot *annot; | |
| 1015 | |
| 1016 pdf_begin_operation(ctx, page->doc, "Create signature"); | |
| 1017 | |
| 1018 annot = pdf_create_annot_raw(ctx, page, PDF_ANNOT_WIDGET); | |
| 1019 | |
| 1020 fz_try(ctx) | |
| 1021 { | |
| 1022 pdf_obj *obj = annot->obj; | |
| 1023 pdf_obj *root = pdf_dict_get(ctx, pdf_trailer(ctx, page->doc), PDF_NAME(Root)); | |
| 1024 pdf_obj *acroform = pdf_dict_get(ctx, root, PDF_NAME(AcroForm)); | |
| 1025 pdf_obj *fields, *lock; | |
| 1026 if (!acroform) | |
| 1027 { | |
| 1028 acroform = pdf_new_dict(ctx, page->doc, 1); | |
| 1029 pdf_dict_put_drop(ctx, root, PDF_NAME(AcroForm), acroform); | |
| 1030 } | |
| 1031 fields = pdf_dict_get(ctx, acroform, PDF_NAME(Fields)); | |
| 1032 if (!fields) | |
| 1033 { | |
| 1034 fields = pdf_new_array(ctx, page->doc, 1); | |
| 1035 pdf_dict_put_drop(ctx, acroform, PDF_NAME(Fields), fields); | |
| 1036 } | |
| 1037 pdf_set_annot_rect(ctx, annot, rect); | |
| 1038 pdf_dict_put(ctx, obj, PDF_NAME(FT), PDF_NAME(Sig)); | |
| 1039 pdf_dict_put_int(ctx, obj, PDF_NAME(F), PDF_ANNOT_IS_PRINT); | |
| 1040 pdf_dict_put_text_string(ctx, obj, PDF_NAME(DA), "/Helv 0 Tf 0 g"); | |
| 1041 pdf_dict_put_text_string(ctx, obj, PDF_NAME(T), name); | |
| 1042 pdf_array_push(ctx, fields, obj); | |
| 1043 lock = pdf_dict_put_dict(ctx, obj, PDF_NAME(Lock), 1); | |
| 1044 pdf_dict_put(ctx, lock, PDF_NAME(Action), PDF_NAME(All)); | |
| 1045 pdf_end_operation(ctx, page->doc); | |
| 1046 } | |
| 1047 fz_catch(ctx) | |
| 1048 { | |
| 1049 pdf_abandon_operation(ctx, page->doc); | |
| 1050 pdf_delete_annot(ctx, page, annot); | |
| 1051 } | |
| 1052 return (pdf_annot *)annot; | |
| 1053 } | |
| 1054 | |
| 1055 fz_rect | |
| 1056 pdf_bound_widget(fz_context *ctx, pdf_annot *widget) | |
| 1057 { | |
| 1058 return pdf_bound_annot(ctx, widget); | |
| 1059 } | |
| 1060 | |
| 1061 int | |
| 1062 pdf_update_widget(fz_context *ctx, pdf_annot *widget) | |
| 1063 { | |
| 1064 return pdf_update_annot(ctx, widget); | |
| 1065 } | |
| 1066 | |
| 1067 int pdf_text_widget_max_len(fz_context *ctx, pdf_annot *tw) | |
| 1068 { | |
| 1069 pdf_annot *annot = (pdf_annot *)tw; | |
| 1070 return pdf_dict_get_inheritable_int(ctx, annot->obj, PDF_NAME(MaxLen)); | |
| 1071 } | |
| 1072 | |
| 1073 int pdf_text_widget_format(fz_context *ctx, pdf_annot *tw) | |
| 1074 { | |
| 1075 pdf_annot *annot = (pdf_annot *)tw; | |
| 1076 int type = PDF_WIDGET_TX_FORMAT_NONE; | |
| 1077 pdf_obj *js = pdf_dict_getl(ctx, annot->obj, PDF_NAME(AA), PDF_NAME(F), PDF_NAME(JS), NULL); | |
| 1078 if (js) | |
| 1079 { | |
| 1080 char *code = pdf_load_stream_or_string_as_utf8(ctx, js); | |
| 1081 if (strstr(code, "AFNumber_Format")) | |
| 1082 type = PDF_WIDGET_TX_FORMAT_NUMBER; | |
| 1083 else if (strstr(code, "AFSpecial_Format")) | |
| 1084 type = PDF_WIDGET_TX_FORMAT_SPECIAL; | |
| 1085 else if (strstr(code, "AFDate_FormatEx")) | |
| 1086 type = PDF_WIDGET_TX_FORMAT_DATE; | |
| 1087 else if (strstr(code, "AFTime_FormatEx")) | |
| 1088 type = PDF_WIDGET_TX_FORMAT_TIME; | |
| 1089 fz_free(ctx, code); | |
| 1090 } | |
| 1091 | |
| 1092 return type; | |
| 1093 } | |
| 1094 | |
| 1095 static char * | |
| 1096 merge_changes(fz_context *ctx, const char *value, int start, int end, const char *change) | |
| 1097 { | |
| 1098 int changelen = change ? (int)strlen(change) : 0; | |
| 1099 int valuelen = value ? (int)strlen(value) : 0; | |
| 1100 int prelen = (start >= 0 ? (start < valuelen ? start : valuelen) : 0); | |
| 1101 int postlen = (end >= 0 && end <= valuelen ? valuelen - end : 0); | |
| 1102 int newlen = prelen + changelen + postlen + 1; | |
| 1103 char *merged = fz_malloc(ctx, newlen); | |
| 1104 char *m = merged; | |
| 1105 | |
| 1106 if (prelen) | |
| 1107 { | |
| 1108 memcpy(m, value, prelen); | |
| 1109 m += prelen; | |
| 1110 } | |
| 1111 if (changelen) | |
| 1112 { | |
| 1113 memcpy(m, change, changelen); | |
| 1114 m += changelen; | |
| 1115 } | |
| 1116 if (postlen) | |
| 1117 { | |
| 1118 memcpy(m, &value[end], postlen); | |
| 1119 m += postlen; | |
| 1120 } | |
| 1121 *m = 0; | |
| 1122 | |
| 1123 return merged; | |
| 1124 } | |
| 1125 | |
| 1126 int pdf_set_text_field_value(fz_context *ctx, pdf_annot *widget, const char *update) | |
| 1127 { | |
| 1128 pdf_document *doc; | |
| 1129 pdf_keystroke_event evt = { 0 }; | |
| 1130 char *new_change = NULL; | |
| 1131 char *new_value = NULL; | |
| 1132 char *merged_value = NULL; | |
| 1133 int rc = 1; | |
| 1134 | |
| 1135 begin_annot_op(ctx, widget, "Edit text field"); | |
| 1136 doc = widget->page->doc; | |
| 1137 | |
| 1138 fz_var(new_value); | |
| 1139 fz_var(new_change); | |
| 1140 fz_var(merged_value); | |
| 1141 fz_try(ctx) | |
| 1142 { | |
| 1143 if (!widget->ignore_trigger_events) | |
| 1144 { | |
| 1145 evt.value = pdf_annot_field_value(ctx, widget); | |
| 1146 evt.change = update; | |
| 1147 evt.selStart = 0; | |
| 1148 evt.selEnd = (int)strlen(evt.value); | |
| 1149 evt.willCommit = 0; | |
| 1150 rc = pdf_annot_field_event_keystroke(ctx, doc, widget, &evt); | |
| 1151 new_change = evt.newChange; | |
| 1152 new_value = evt.newValue; | |
| 1153 evt.newValue = NULL; | |
| 1154 evt.newChange = NULL; | |
| 1155 if (rc) | |
| 1156 { | |
| 1157 merged_value = merge_changes(ctx, new_value, evt.selStart, evt.selEnd, new_change); | |
| 1158 evt.value = merged_value; | |
| 1159 evt.change = ""; | |
| 1160 evt.selStart = -1; | |
| 1161 evt.selEnd = -1; | |
| 1162 evt.willCommit = 1; | |
| 1163 rc = pdf_annot_field_event_keystroke(ctx, doc, widget, &evt); | |
| 1164 if (rc) | |
| 1165 rc = pdf_set_annot_field_value(ctx, doc, widget, evt.newValue, 0); | |
| 1166 } | |
| 1167 } | |
| 1168 else | |
| 1169 { | |
| 1170 rc = pdf_set_annot_field_value(ctx, doc, widget, update, 1); | |
| 1171 } | |
| 1172 end_annot_op(ctx, widget); | |
| 1173 } | |
| 1174 fz_always(ctx) | |
| 1175 { | |
| 1176 fz_free(ctx, new_value); | |
| 1177 fz_free(ctx, evt.newValue); | |
| 1178 fz_free(ctx, new_change); | |
| 1179 fz_free(ctx, evt.newChange); | |
| 1180 fz_free(ctx, merged_value); | |
| 1181 } | |
| 1182 fz_catch(ctx) | |
| 1183 { | |
| 1184 abandon_annot_op(ctx, widget); | |
| 1185 fz_warn(ctx, "could not set widget text"); | |
| 1186 rc = 0; | |
| 1187 } | |
| 1188 return rc; | |
| 1189 } | |
| 1190 | |
| 1191 int pdf_edit_text_field_value(fz_context *ctx, pdf_annot *widget, const char *value, const char *change, int *selStart, int *selEnd, char **result) | |
| 1192 { | |
| 1193 pdf_document *doc = widget->page->doc; | |
| 1194 pdf_keystroke_event evt = {0}; | |
| 1195 int rc = 1; | |
| 1196 | |
| 1197 pdf_begin_operation(ctx, doc, "Text field keystroke"); | |
| 1198 | |
| 1199 fz_try(ctx) | |
| 1200 { | |
| 1201 if (!widget->ignore_trigger_events) | |
| 1202 { | |
| 1203 evt.value = value; | |
| 1204 evt.change = change; | |
| 1205 evt.selStart = *selStart; | |
| 1206 evt.selEnd = *selEnd; | |
| 1207 evt.willCommit = 0; | |
| 1208 rc = pdf_annot_field_event_keystroke(ctx, doc, widget, &evt); | |
| 1209 if (rc) | |
| 1210 { | |
| 1211 *result = merge_changes(ctx, evt.newValue, evt.selStart, evt.selEnd, evt.newChange); | |
| 1212 *selStart = evt.selStart + (int)strlen(evt.newChange); | |
| 1213 *selEnd = *selStart; | |
| 1214 } | |
| 1215 } | |
| 1216 else | |
| 1217 { | |
| 1218 *result = merge_changes(ctx, value, *selStart, *selEnd, change); | |
| 1219 *selStart = evt.selStart + (int)strlen(change); | |
| 1220 *selEnd = *selStart; | |
| 1221 } | |
| 1222 pdf_end_operation(ctx, doc); | |
| 1223 } | |
| 1224 fz_always(ctx) | |
| 1225 { | |
| 1226 fz_free(ctx, evt.newValue); | |
| 1227 fz_free(ctx, evt.newChange); | |
| 1228 } | |
| 1229 fz_catch(ctx) | |
| 1230 { | |
| 1231 pdf_abandon_operation(ctx, doc); | |
| 1232 fz_warn(ctx, "could not process text widget keystroke"); | |
| 1233 rc = 0; | |
| 1234 } | |
| 1235 return rc; | |
| 1236 } | |
| 1237 | |
| 1238 int pdf_set_choice_field_value(fz_context *ctx, pdf_annot *widget, const char *new_value) | |
| 1239 { | |
| 1240 /* Choice widgets use almost the same keystroke processing as text fields. */ | |
| 1241 return pdf_set_text_field_value(ctx, widget, new_value); | |
| 1242 } | |
| 1243 | |
| 1244 int pdf_choice_widget_options(fz_context *ctx, pdf_annot *tw, int exportval, const char *opts[]) | |
| 1245 { | |
| 1246 pdf_annot *annot = (pdf_annot *)tw; | |
| 1247 pdf_obj *optarr; | |
| 1248 int i, n, m; | |
| 1249 | |
| 1250 optarr = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(Opt)); | |
| 1251 n = pdf_array_len(ctx, optarr); | |
| 1252 | |
| 1253 if (opts) | |
| 1254 { | |
| 1255 for (i = 0; i < n; i++) | |
| 1256 { | |
| 1257 m = pdf_array_len(ctx, pdf_array_get(ctx, optarr, i)); | |
| 1258 /* If it is a two element array, the second item is the one that we want if we want the listing value. */ | |
| 1259 if (m == 2) | |
| 1260 if (exportval) | |
| 1261 opts[i] = pdf_array_get_text_string(ctx, pdf_array_get(ctx, optarr, i), 0); | |
| 1262 else | |
| 1263 opts[i] = pdf_array_get_text_string(ctx, pdf_array_get(ctx, optarr, i), 1); | |
| 1264 else | |
| 1265 opts[i] = pdf_array_get_text_string(ctx, optarr, i); | |
| 1266 } | |
| 1267 } | |
| 1268 return n; | |
| 1269 } | |
| 1270 | |
| 1271 int pdf_choice_field_option_count(fz_context *ctx, pdf_obj *field) | |
| 1272 { | |
| 1273 pdf_obj *opt = pdf_dict_get_inheritable(ctx, field, PDF_NAME(Opt)); | |
| 1274 return pdf_array_len(ctx, opt); | |
| 1275 } | |
| 1276 | |
| 1277 const char *pdf_choice_field_option(fz_context *ctx, pdf_obj *field, int export, int i) | |
| 1278 { | |
| 1279 pdf_obj *opt = pdf_dict_get_inheritable(ctx, field, PDF_NAME(Opt)); | |
| 1280 pdf_obj *ent = pdf_array_get(ctx, opt, i); | |
| 1281 if (pdf_array_len(ctx, ent) == 2) | |
| 1282 return pdf_array_get_text_string(ctx, ent, export ? 0 : 1); | |
| 1283 else | |
| 1284 return pdf_to_text_string(ctx, ent); | |
| 1285 } | |
| 1286 | |
| 1287 int pdf_choice_widget_is_multiselect(fz_context *ctx, pdf_annot *tw) | |
| 1288 { | |
| 1289 pdf_annot *annot = (pdf_annot *)tw; | |
| 1290 | |
| 1291 if (!annot) return 0; | |
| 1292 | |
| 1293 switch (pdf_field_type(ctx, annot->obj)) | |
| 1294 { | |
| 1295 case PDF_WIDGET_TYPE_LISTBOX: | |
| 1296 return (pdf_field_flags(ctx, annot->obj) & PDF_CH_FIELD_IS_MULTI_SELECT) != 0; | |
| 1297 default: | |
| 1298 return 0; | |
| 1299 } | |
| 1300 } | |
| 1301 | |
| 1302 int pdf_choice_widget_value(fz_context *ctx, pdf_annot *tw, const char *opts[]) | |
| 1303 { | |
| 1304 pdf_annot *annot = (pdf_annot *)tw; | |
| 1305 pdf_obj *optarr; | |
| 1306 int i, n; | |
| 1307 | |
| 1308 if (!annot) | |
| 1309 return 0; | |
| 1310 | |
| 1311 optarr = pdf_dict_get(ctx, annot->obj, PDF_NAME(V)); | |
| 1312 | |
| 1313 if (pdf_is_string(ctx, optarr)) | |
| 1314 { | |
| 1315 if (opts) | |
| 1316 opts[0] = pdf_to_text_string(ctx, optarr); | |
| 1317 return 1; | |
| 1318 } | |
| 1319 else | |
| 1320 { | |
| 1321 n = pdf_array_len(ctx, optarr); | |
| 1322 if (opts) | |
| 1323 { | |
| 1324 for (i = 0; i < n; i++) | |
| 1325 { | |
| 1326 pdf_obj *elem = pdf_array_get(ctx, optarr, i); | |
| 1327 if (pdf_is_array(ctx, elem)) | |
| 1328 elem = pdf_array_get(ctx, elem, 1); | |
| 1329 opts[i] = pdf_to_text_string(ctx, elem); | |
| 1330 } | |
| 1331 } | |
| 1332 return n; | |
| 1333 } | |
| 1334 } | |
| 1335 | |
| 1336 void pdf_choice_widget_set_value(fz_context *ctx, pdf_annot *tw, int n, const char *opts[]) | |
| 1337 { | |
| 1338 pdf_annot *annot = (pdf_annot *)tw; | |
| 1339 pdf_obj *optarr = NULL; | |
| 1340 int i; | |
| 1341 | |
| 1342 if (!annot) | |
| 1343 return; | |
| 1344 | |
| 1345 begin_annot_op(ctx, annot, "Set choice"); | |
| 1346 | |
| 1347 fz_var(optarr); | |
| 1348 fz_try(ctx) | |
| 1349 { | |
| 1350 if (n != 1) | |
| 1351 { | |
| 1352 optarr = pdf_new_array(ctx, annot->page->doc, n); | |
| 1353 | |
| 1354 for (i = 0; i < n; i++) | |
| 1355 pdf_array_push_text_string(ctx, optarr, opts[i]); | |
| 1356 | |
| 1357 pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(V), optarr); | |
| 1358 } | |
| 1359 else | |
| 1360 pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(V), opts[0]); | |
| 1361 | |
| 1362 /* FIXME: when n > 1, we should be regenerating the indexes */ | |
| 1363 pdf_dict_del(ctx, annot->obj, PDF_NAME(I)); | |
| 1364 | |
| 1365 pdf_field_mark_dirty(ctx, annot->obj); | |
| 1366 end_annot_op(ctx, annot); | |
| 1367 } | |
| 1368 fz_catch(ctx) | |
| 1369 { | |
| 1370 abandon_annot_op(ctx, annot); | |
| 1371 pdf_drop_obj(ctx, optarr); | |
| 1372 fz_rethrow(ctx); | |
| 1373 } | |
| 1374 } | |
| 1375 | |
| 1376 int pdf_signature_byte_range(fz_context *ctx, pdf_document *doc, pdf_obj *signature, fz_range *byte_range) | |
| 1377 { | |
| 1378 pdf_obj *br = pdf_dict_getl(ctx, signature, PDF_NAME(V), PDF_NAME(ByteRange), NULL); | |
| 1379 int i, n = pdf_array_len(ctx, br)/2; | |
| 1380 | |
| 1381 if (byte_range) | |
| 1382 { | |
| 1383 for (i = 0; i < n; i++) | |
| 1384 { | |
| 1385 int64_t offset = pdf_array_get_int(ctx, br, 2*i); | |
| 1386 int length = pdf_array_get_int(ctx, br, 2*i+1); | |
| 1387 | |
| 1388 if (offset < 0 || offset > doc->file_size) | |
| 1389 fz_throw(ctx, FZ_ERROR_FORMAT, "offset of signature byte range outside of file"); | |
| 1390 else if (length < 0) | |
| 1391 fz_throw(ctx, FZ_ERROR_FORMAT, "length of signature byte range negative"); | |
| 1392 else if (offset + length > doc->file_size) | |
| 1393 fz_throw(ctx, FZ_ERROR_FORMAT, "signature byte range extends past end of file"); | |
| 1394 | |
| 1395 byte_range[i].offset = offset; | |
| 1396 byte_range[i].length = length; | |
| 1397 } | |
| 1398 } | |
| 1399 | |
| 1400 return n; | |
| 1401 } | |
| 1402 | |
| 1403 static int is_white(int c) | |
| 1404 { | |
| 1405 return c == '\x00' || c == '\x09' || c == '\x0a' || c == '\x0c' || c == '\x0d' || c == '\x20'; | |
| 1406 } | |
| 1407 | |
| 1408 static int is_hex_or_white(int c) | |
| 1409 { | |
| 1410 return (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') || (c >= '0' && c <= '9') || is_white(c); | |
| 1411 } | |
| 1412 | |
| 1413 static void validate_certificate_data(fz_context *ctx, pdf_document *doc, fz_range *hole) | |
| 1414 { | |
| 1415 fz_stream *stm; | |
| 1416 int c; | |
| 1417 | |
| 1418 stm = fz_open_range_filter(ctx, doc->file, hole, 1); | |
| 1419 fz_try(ctx) | |
| 1420 { | |
| 1421 while (is_white((c = fz_read_byte(ctx, stm)))) | |
| 1422 ; | |
| 1423 | |
| 1424 if (c == '<') | |
| 1425 c = fz_read_byte(ctx, stm); | |
| 1426 | |
| 1427 while (is_hex_or_white(c)) | |
| 1428 c = fz_read_byte(ctx, stm); | |
| 1429 | |
| 1430 if (c == '>') | |
| 1431 c = fz_read_byte(ctx, stm); | |
| 1432 | |
| 1433 while (is_white(c)) | |
| 1434 c = fz_read_byte(ctx, stm); | |
| 1435 | |
| 1436 if (c != EOF) | |
| 1437 fz_throw(ctx, FZ_ERROR_FORMAT, "signature certificate data contains invalid character"); | |
| 1438 if ((size_t)fz_tell(ctx, stm) != hole->length) | |
| 1439 fz_throw(ctx, FZ_ERROR_FORMAT, "premature end of signature certificate data"); | |
| 1440 } | |
| 1441 fz_always(ctx) | |
| 1442 fz_drop_stream(ctx, stm); | |
| 1443 fz_catch(ctx) | |
| 1444 fz_rethrow(ctx); | |
| 1445 } | |
| 1446 | |
| 1447 static int rangecmp(const void *a_, const void *b_) | |
| 1448 { | |
| 1449 const fz_range *a = (const fz_range *) a_; | |
| 1450 const fz_range *b = (const fz_range *) b_; | |
| 1451 return (int) (a->offset - b->offset); | |
| 1452 } | |
| 1453 | |
| 1454 static void validate_byte_ranges(fz_context *ctx, pdf_document *doc, fz_range *unsorted, int nranges) | |
| 1455 { | |
| 1456 int64_t offset = 0; | |
| 1457 fz_range *sorted; | |
| 1458 int i; | |
| 1459 | |
| 1460 sorted = fz_calloc(ctx, nranges, sizeof(*sorted)); | |
| 1461 memcpy(sorted, unsorted, nranges * sizeof(*sorted)); | |
| 1462 qsort(sorted, nranges, sizeof(*sorted), rangecmp); | |
| 1463 | |
| 1464 fz_try(ctx) | |
| 1465 { | |
| 1466 offset = 0; | |
| 1467 | |
| 1468 for (i = 0; i < nranges; i++) | |
| 1469 { | |
| 1470 if (sorted[i].offset > offset) | |
| 1471 { | |
| 1472 fz_range hole; | |
| 1473 | |
| 1474 hole.offset = offset; | |
| 1475 hole.length = sorted[i].offset - offset; | |
| 1476 | |
| 1477 validate_certificate_data(ctx, doc, &hole); | |
| 1478 } | |
| 1479 | |
| 1480 offset = fz_maxi64(offset, sorted[i].offset + sorted[i].length); | |
| 1481 } | |
| 1482 } | |
| 1483 fz_always(ctx) | |
| 1484 fz_free(ctx, sorted); | |
| 1485 fz_catch(ctx) | |
| 1486 fz_rethrow(ctx); | |
| 1487 } | |
| 1488 | |
| 1489 fz_stream *pdf_signature_hash_bytes(fz_context *ctx, pdf_document *doc, pdf_obj *signature) | |
| 1490 { | |
| 1491 fz_range *byte_range = NULL; | |
| 1492 int byte_range_len; | |
| 1493 fz_stream *bytes = NULL; | |
| 1494 | |
| 1495 fz_var(byte_range); | |
| 1496 fz_try(ctx) | |
| 1497 { | |
| 1498 byte_range_len = pdf_signature_byte_range(ctx, doc, signature, NULL); | |
| 1499 if (byte_range_len) | |
| 1500 { | |
| 1501 byte_range = fz_calloc(ctx, byte_range_len, sizeof(*byte_range)); | |
| 1502 pdf_signature_byte_range(ctx, doc, signature, byte_range); | |
| 1503 } | |
| 1504 | |
| 1505 validate_byte_ranges(ctx, doc, byte_range, byte_range_len); | |
| 1506 bytes = fz_open_range_filter(ctx, doc->file, byte_range, byte_range_len); | |
| 1507 } | |
| 1508 fz_always(ctx) | |
| 1509 { | |
| 1510 fz_free(ctx, byte_range); | |
| 1511 } | |
| 1512 fz_catch(ctx) | |
| 1513 { | |
| 1514 fz_rethrow(ctx); | |
| 1515 } | |
| 1516 | |
| 1517 return bytes; | |
| 1518 } | |
| 1519 | |
| 1520 int pdf_incremental_change_since_signing_widget(fz_context *ctx, pdf_annot *widget) | |
| 1521 { | |
| 1522 if (!widget->page) | |
| 1523 fz_throw(ctx, FZ_ERROR_ARGUMENT, "annotation not bound to any page"); | |
| 1524 return pdf_signature_incremental_change_since_signing(ctx, widget->page->doc, widget->obj); | |
| 1525 } | |
| 1526 | |
| 1527 int pdf_signature_incremental_change_since_signing(fz_context *ctx, pdf_document *doc, pdf_obj *signature) | |
| 1528 { | |
| 1529 fz_range *byte_range = NULL; | |
| 1530 int byte_range_len; | |
| 1531 int changed = 0; | |
| 1532 | |
| 1533 if (pdf_dict_get_inheritable(ctx, signature, PDF_NAME(FT)) != PDF_NAME(Sig)) | |
| 1534 fz_throw(ctx, FZ_ERROR_ARGUMENT, "annotation is not a signature widget"); | |
| 1535 if (!pdf_signature_is_signed(ctx, doc, signature)) | |
| 1536 return 0; | |
| 1537 | |
| 1538 fz_var(byte_range); | |
| 1539 fz_try(ctx) | |
| 1540 { | |
| 1541 byte_range_len = pdf_signature_byte_range(ctx, doc, signature, NULL); | |
| 1542 if (byte_range_len) | |
| 1543 { | |
| 1544 fz_range *last_range; | |
| 1545 int64_t end_of_range; | |
| 1546 | |
| 1547 byte_range = fz_calloc(ctx, byte_range_len, sizeof(*byte_range)); | |
| 1548 pdf_signature_byte_range(ctx, doc, signature, byte_range); | |
| 1549 | |
| 1550 last_range = &byte_range[byte_range_len -1]; | |
| 1551 end_of_range = last_range->offset + last_range->length; | |
| 1552 | |
| 1553 /* We can see how long the document was when signed by inspecting the byte | |
| 1554 * ranges of the signature. The document, when read in, may have already | |
| 1555 * had changes tagged on to it, past its extent when signed, or we may have | |
| 1556 * made changes since reading it, which will be held in a new incremental | |
| 1557 * xref section. */ | |
| 1558 if (doc->file_size > end_of_range || doc->num_incremental_sections > 0) | |
| 1559 changed = 1; | |
| 1560 } | |
| 1561 } | |
| 1562 fz_always(ctx) | |
| 1563 { | |
| 1564 fz_free(ctx, byte_range); | |
| 1565 } | |
| 1566 fz_catch(ctx) | |
| 1567 { | |
| 1568 fz_rethrow(ctx); | |
| 1569 } | |
| 1570 | |
| 1571 return changed; | |
| 1572 } | |
| 1573 | |
| 1574 int pdf_signature_is_signed(fz_context *ctx, pdf_document *doc, pdf_obj *field) | |
| 1575 { | |
| 1576 pdf_obj *v; | |
| 1577 pdf_obj* vtype; | |
| 1578 | |
| 1579 if (pdf_dict_get_inheritable(ctx, field, PDF_NAME(FT)) != PDF_NAME(Sig)) | |
| 1580 return 0; | |
| 1581 /* Signatures can only be signed if the value is a dictionary, | |
| 1582 * and if the value has a Type, it should be Sig. */ | |
| 1583 v = pdf_dict_get_inheritable(ctx, field, PDF_NAME(V)); | |
| 1584 vtype = pdf_dict_get(ctx, v, PDF_NAME(Type)); | |
| 1585 return pdf_is_dict(ctx, v) && (vtype ? pdf_name_eq(ctx, vtype, PDF_NAME(Sig)) : 1); | |
| 1586 } | |
| 1587 | |
| 1588 int pdf_widget_is_signed(fz_context *ctx, pdf_annot *widget) | |
| 1589 { | |
| 1590 if (widget == NULL) | |
| 1591 return 0; | |
| 1592 | |
| 1593 if (!widget->page) | |
| 1594 fz_throw(ctx, FZ_ERROR_ARGUMENT, "annotation not bound to any page"); | |
| 1595 | |
| 1596 return pdf_signature_is_signed(ctx, widget->page->doc, widget->obj); | |
| 1597 } | |
| 1598 | |
| 1599 int pdf_widget_is_readonly(fz_context *ctx, pdf_annot *widget) | |
| 1600 { | |
| 1601 int fflags; | |
| 1602 if (widget == NULL) | |
| 1603 return 0; | |
| 1604 fflags = pdf_field_flags(ctx, ((pdf_annot *) widget)->obj); | |
| 1605 return fflags & PDF_FIELD_IS_READ_ONLY; | |
| 1606 } | |
| 1607 | |
| 1608 size_t pdf_signature_contents(fz_context *ctx, pdf_document *doc, pdf_obj *signature, char **contents) | |
| 1609 { | |
| 1610 pdf_obj *v_ref = pdf_dict_get_inheritable(ctx, signature, PDF_NAME(V)); | |
| 1611 pdf_obj *v_obj = pdf_load_unencrypted_object(ctx, doc, pdf_to_num(ctx, v_ref)); | |
| 1612 char *copy = NULL; | |
| 1613 size_t len; | |
| 1614 | |
| 1615 fz_var(copy); | |
| 1616 fz_try(ctx) | |
| 1617 { | |
| 1618 pdf_obj *c = pdf_dict_get(ctx, v_obj, PDF_NAME(Contents)); | |
| 1619 char *s; | |
| 1620 | |
| 1621 s = pdf_to_str_buf(ctx, c); | |
| 1622 len = pdf_to_str_len(ctx, c); | |
| 1623 | |
| 1624 if (contents) | |
| 1625 { | |
| 1626 copy = Memento_label(fz_malloc(ctx, len), "sig_contents"); | |
| 1627 memcpy(copy, s, len); | |
| 1628 } | |
| 1629 } | |
| 1630 fz_always(ctx) | |
| 1631 pdf_drop_obj(ctx, v_obj); | |
| 1632 fz_catch(ctx) | |
| 1633 { | |
| 1634 fz_free(ctx, copy); | |
| 1635 fz_rethrow(ctx); | |
| 1636 } | |
| 1637 | |
| 1638 if (contents) | |
| 1639 *contents = copy; | |
| 1640 return len; | |
| 1641 } | |
| 1642 | |
| 1643 static fz_xml_doc *load_xfa(fz_context *ctx, pdf_document *doc) | |
| 1644 { | |
| 1645 pdf_obj *xfa; | |
| 1646 fz_buffer *buf = NULL; | |
| 1647 fz_buffer *packet = NULL; | |
| 1648 int i; | |
| 1649 | |
| 1650 if (doc->xfa) | |
| 1651 return doc->xfa; /* Already loaded, and present. */ | |
| 1652 | |
| 1653 xfa = pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/AcroForm/XFA"); | |
| 1654 if (!pdf_is_array(ctx, xfa) && !pdf_is_stream(ctx, xfa)) | |
| 1655 return NULL; /* No XFA */ | |
| 1656 | |
| 1657 fz_var(buf); | |
| 1658 fz_var(packet); | |
| 1659 | |
| 1660 fz_try(ctx) | |
| 1661 { | |
| 1662 if (pdf_is_stream(ctx, xfa)) | |
| 1663 { | |
| 1664 /* Load entire XFA resource */ | |
| 1665 buf = pdf_load_stream(ctx, xfa); | |
| 1666 } | |
| 1667 else | |
| 1668 { | |
| 1669 /* Concatenate packets to create entire XFA resource */ | |
| 1670 buf = fz_new_buffer(ctx, 1024); | |
| 1671 for(i = 0; i < pdf_array_len(ctx, xfa); ++i) | |
| 1672 { | |
| 1673 pdf_obj *ref = pdf_array_get(ctx, xfa, i); | |
| 1674 if (pdf_is_stream(ctx, ref)) | |
| 1675 { | |
| 1676 packet = pdf_load_stream(ctx, ref); | |
| 1677 fz_append_buffer(ctx, buf, packet); | |
| 1678 fz_drop_buffer(ctx, packet); | |
| 1679 packet = NULL; | |
| 1680 } | |
| 1681 } | |
| 1682 } | |
| 1683 | |
| 1684 /* Parse and stow away XFA resource in document */ | |
| 1685 doc->xfa = fz_parse_xml(ctx, buf, 0); | |
| 1686 } | |
| 1687 fz_always(ctx) | |
| 1688 { | |
| 1689 fz_drop_buffer(ctx, packet); | |
| 1690 fz_drop_buffer(ctx, buf); | |
| 1691 } | |
| 1692 fz_catch(ctx) | |
| 1693 { | |
| 1694 fz_rethrow(ctx); | |
| 1695 } | |
| 1696 | |
| 1697 return doc->xfa; | |
| 1698 } | |
| 1699 | |
| 1700 static fz_xml * | |
| 1701 get_xfa_resource(fz_context *ctx, pdf_document *doc, const char *str) | |
| 1702 { | |
| 1703 fz_xml_doc *xfa; | |
| 1704 | |
| 1705 xfa = load_xfa(ctx, doc); | |
| 1706 if (!xfa) | |
| 1707 return NULL; | |
| 1708 | |
| 1709 return fz_xml_find_down(fz_xml_root(xfa), str); | |
| 1710 } | |
| 1711 | |
| 1712 static int | |
| 1713 find_name_component(char **np, char **sp, char **ep) | |
| 1714 { | |
| 1715 char *n = *np; | |
| 1716 char *s, *e; | |
| 1717 int idx = 0; | |
| 1718 | |
| 1719 if (*n == '.') | |
| 1720 n++; | |
| 1721 | |
| 1722 /* Find the next name we are looking for. */ | |
| 1723 s = e = n; | |
| 1724 while (*e && *e != '[' && *e != '.') | |
| 1725 e++; | |
| 1726 | |
| 1727 /* So the next name is s..e */ | |
| 1728 n = e; | |
| 1729 if (*n == '[') | |
| 1730 { | |
| 1731 n++; | |
| 1732 while (*n >= '0' && *n <= '9') | |
| 1733 idx = idx*10 + *n++ - '0'; | |
| 1734 while (*n && *n != ']') | |
| 1735 n++; | |
| 1736 if (*n == ']') | |
| 1737 n++; | |
| 1738 } | |
| 1739 *np = n; | |
| 1740 *sp = s; | |
| 1741 *ep = e; | |
| 1742 | |
| 1743 return idx; | |
| 1744 } | |
| 1745 | |
| 1746 static pdf_obj * | |
| 1747 annot_from_name(fz_context *ctx, pdf_document *doc, const char *str) | |
| 1748 { | |
| 1749 pdf_obj *fields = pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/AcroForm/Fields"); | |
| 1750 | |
| 1751 if (strncmp(str, "xfa[0].", 7) == 0) | |
| 1752 str += 7; | |
| 1753 if (strncmp(str, "template[0].", 12) == 0) | |
| 1754 str += 12; | |
| 1755 | |
| 1756 return pdf_lookup_field(ctx, fields, str); | |
| 1757 } | |
| 1758 | |
| 1759 static pdf_obj * | |
| 1760 get_locked_fields_from_xfa(fz_context *ctx, pdf_document *doc, pdf_obj *field) | |
| 1761 { | |
| 1762 char *name = pdf_load_field_name(ctx, field); | |
| 1763 char *n = name; | |
| 1764 const char *use; | |
| 1765 fz_xml *node; | |
| 1766 | |
| 1767 if (name == NULL) | |
| 1768 return NULL; | |
| 1769 | |
| 1770 fz_try(ctx) | |
| 1771 { | |
| 1772 node = get_xfa_resource(ctx, doc, "template"); | |
| 1773 | |
| 1774 do | |
| 1775 { | |
| 1776 char c, *s, *e; | |
| 1777 int idx = 0; | |
| 1778 char *key; | |
| 1779 | |
| 1780 idx = find_name_component(&n, &s, &e); | |
| 1781 /* We want the idx'th occurrence of s..e */ | |
| 1782 | |
| 1783 /* Hacky */ | |
| 1784 c = *e; | |
| 1785 *e = 0; | |
| 1786 key = *n ? "subform" : "field"; | |
| 1787 node = fz_xml_find_down_match(node, key, "name", s); | |
| 1788 while (node && idx > 0) | |
| 1789 { | |
| 1790 node = fz_xml_find_next_match(node, key, "name", s); | |
| 1791 idx--; | |
| 1792 } | |
| 1793 *e = c; | |
| 1794 } | |
| 1795 while (node && *n == '.'); | |
| 1796 } | |
| 1797 fz_always(ctx) | |
| 1798 fz_free(ctx, name); | |
| 1799 fz_catch(ctx) | |
| 1800 fz_rethrow(ctx); | |
| 1801 | |
| 1802 if (node == NULL) | |
| 1803 return NULL; | |
| 1804 | |
| 1805 node = fz_xml_find_down(node, "ui"); | |
| 1806 node = fz_xml_find_down(node, "signature"); | |
| 1807 node = fz_xml_find_down(node, "manifest"); | |
| 1808 | |
| 1809 use = fz_xml_att(node, "use"); | |
| 1810 if (use == NULL) | |
| 1811 return NULL; | |
| 1812 if (*use == '#') | |
| 1813 use++; | |
| 1814 | |
| 1815 /* Now look for a variables entry in a subform that defines this. */ | |
| 1816 while (node) | |
| 1817 { | |
| 1818 fz_xml *variables, *manifest, *ref; | |
| 1819 pdf_obj *arr; | |
| 1820 | |
| 1821 /* Find the enclosing subform */ | |
| 1822 do { | |
| 1823 node = fz_xml_up(node); | |
| 1824 } while (node && strcmp(fz_xml_tag(node), "subform")); | |
| 1825 | |
| 1826 /* Look for a variables within that. */ | |
| 1827 variables = fz_xml_find_down(node, "variables"); | |
| 1828 if (variables == NULL) | |
| 1829 continue; | |
| 1830 | |
| 1831 manifest = fz_xml_find_down_match(variables, "manifest", "id", use); | |
| 1832 if (manifest == NULL) | |
| 1833 continue; | |
| 1834 | |
| 1835 arr = pdf_new_array(ctx, doc, 16); | |
| 1836 fz_try(ctx) | |
| 1837 { | |
| 1838 ref = fz_xml_find_down(manifest, "ref"); | |
| 1839 while (ref) | |
| 1840 { | |
| 1841 const char *s = fz_xml_text(fz_xml_down(ref)); | |
| 1842 pdf_array_push(ctx, arr, annot_from_name(ctx, doc, s)); | |
| 1843 ref = fz_xml_find_next(ref, "ref"); | |
| 1844 } | |
| 1845 } | |
| 1846 fz_catch(ctx) | |
| 1847 { | |
| 1848 pdf_drop_obj(ctx, arr); | |
| 1849 fz_rethrow(ctx); | |
| 1850 } | |
| 1851 return arr; | |
| 1852 } | |
| 1853 | |
| 1854 return NULL; | |
| 1855 } | |
| 1856 | |
| 1857 static void | |
| 1858 lock_field(fz_context *ctx, pdf_obj *f) | |
| 1859 { | |
| 1860 int ff = pdf_dict_get_inheritable_int(ctx, f, PDF_NAME(Ff)); | |
| 1861 | |
| 1862 if ((ff & PDF_FIELD_IS_READ_ONLY) || | |
| 1863 !pdf_name_eq(ctx, pdf_dict_get(ctx, f, PDF_NAME(Type)), PDF_NAME(Annot)) || | |
| 1864 !pdf_name_eq(ctx, pdf_dict_get(ctx, f, PDF_NAME(Subtype)), PDF_NAME(Widget))) | |
| 1865 return; | |
| 1866 | |
| 1867 pdf_dict_put_int(ctx, f, PDF_NAME(Ff), ff | PDF_FIELD_IS_READ_ONLY); | |
| 1868 } | |
| 1869 | |
| 1870 static void | |
| 1871 lock_xfa_locked_fields(fz_context *ctx, pdf_obj *a) | |
| 1872 { | |
| 1873 int i; | |
| 1874 int len = pdf_array_len(ctx, a); | |
| 1875 | |
| 1876 for (i = 0; i < len; i++) | |
| 1877 { | |
| 1878 lock_field(ctx, pdf_array_get(ctx, a, i)); | |
| 1879 } | |
| 1880 } | |
| 1881 | |
| 1882 | |
| 1883 void pdf_signature_set_value(fz_context *ctx, pdf_document *doc, pdf_obj *field, pdf_pkcs7_signer *signer, int64_t stime) | |
| 1884 { | |
| 1885 pdf_obj *v = NULL; | |
| 1886 pdf_obj *o = NULL; | |
| 1887 pdf_obj *r = NULL; | |
| 1888 pdf_obj *t = NULL; | |
| 1889 pdf_obj *a = NULL; | |
| 1890 pdf_obj *b = NULL; | |
| 1891 pdf_obj *l = NULL; | |
| 1892 pdf_obj *indv; | |
| 1893 int vnum; | |
| 1894 size_t max_digest_size; | |
| 1895 char *buf = NULL; | |
| 1896 | |
| 1897 vnum = pdf_create_object(ctx, doc); | |
| 1898 indv = pdf_new_indirect(ctx, doc, vnum, 0); | |
| 1899 pdf_dict_put_drop(ctx, field, PDF_NAME(V), indv); | |
| 1900 | |
| 1901 max_digest_size = signer->max_digest_size(ctx, signer); | |
| 1902 | |
| 1903 fz_var(v); | |
| 1904 fz_var(o); | |
| 1905 fz_var(r); | |
| 1906 fz_var(t); | |
| 1907 fz_var(a); | |
| 1908 fz_var(b); | |
| 1909 fz_var(l); | |
| 1910 fz_var(buf); | |
| 1911 fz_try(ctx) | |
| 1912 { | |
| 1913 v = pdf_new_dict(ctx, doc, 4); | |
| 1914 pdf_update_object(ctx, doc, vnum, v); | |
| 1915 | |
| 1916 buf = fz_calloc(ctx, max_digest_size, 1); | |
| 1917 | |
| 1918 /* Ensure that the /Filter entry is the first entry in the | |
| 1919 dictionary after the digest contents since we look for | |
| 1920 this tag when completing signatures in pdf-write.c in order | |
| 1921 to generate the correct byte range. */ | |
| 1922 pdf_dict_put_array(ctx, v, PDF_NAME(ByteRange), 4); | |
| 1923 pdf_dict_put_string(ctx, v, PDF_NAME(Contents), buf, max_digest_size); | |
| 1924 pdf_dict_put(ctx, v, PDF_NAME(Filter), PDF_NAME(Adobe_PPKLite)); | |
| 1925 pdf_dict_put(ctx, v, PDF_NAME(SubFilter), PDF_NAME(adbe_pkcs7_detached)); | |
| 1926 pdf_dict_put(ctx, v, PDF_NAME(Type), PDF_NAME(Sig)); | |
| 1927 pdf_dict_put_date(ctx, v, PDF_NAME(M), stime); | |
| 1928 | |
| 1929 o = pdf_dict_put_array(ctx, v, PDF_NAME(Reference), 1); | |
| 1930 r = pdf_array_put_dict(ctx, o, 0, 4); | |
| 1931 pdf_dict_put(ctx, r, PDF_NAME(Data), pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root))); | |
| 1932 pdf_dict_put(ctx, r, PDF_NAME(TransformMethod), PDF_NAME(FieldMDP)); | |
| 1933 pdf_dict_put(ctx, r, PDF_NAME(Type), PDF_NAME(SigRef)); | |
| 1934 t = pdf_dict_put_dict(ctx, r, PDF_NAME(TransformParams), 5); | |
| 1935 | |
| 1936 l = pdf_dict_getp(ctx, field, "Lock/Action"); | |
| 1937 if (l) | |
| 1938 { | |
| 1939 a = pdf_dict_getp(ctx, field, "Lock/Fields"); | |
| 1940 } | |
| 1941 else | |
| 1942 { | |
| 1943 /* Lock action wasn't specified so we need to encode an Include. | |
| 1944 * Before we just use an empty array, check in the XFA for locking | |
| 1945 * details. */ | |
| 1946 a = get_locked_fields_from_xfa(ctx, doc, field); | |
| 1947 if (a) | |
| 1948 lock_xfa_locked_fields(ctx, a); | |
| 1949 | |
| 1950 /* If we don't get a result from the XFA, just encode an empty array | |
| 1951 * (leave a == NULL), even if Lock/Fields exists because we don't really | |
| 1952 * know what to do with the information if the action isn't defined. */ | |
| 1953 l = PDF_NAME(Include); | |
| 1954 } | |
| 1955 | |
| 1956 pdf_dict_put(ctx, t, PDF_NAME(Action), l); | |
| 1957 | |
| 1958 if (pdf_name_eq(ctx, l, PDF_NAME(Include)) || pdf_name_eq(ctx, l, PDF_NAME(Exclude))) | |
| 1959 { | |
| 1960 /* For action Include and Exclude, we need to encode a Fields array */ | |
| 1961 if (!a) | |
| 1962 { | |
| 1963 /* If one wasn't defined or we chose to ignore it because no action | |
| 1964 * was defined then use an empty one. */ | |
| 1965 b = pdf_new_array(ctx, doc, 0); | |
| 1966 a = b; | |
| 1967 } | |
| 1968 | |
| 1969 pdf_dict_put_drop(ctx, t, PDF_NAME(Fields), pdf_copy_array(ctx, a)); | |
| 1970 } | |
| 1971 | |
| 1972 pdf_dict_put(ctx, t, PDF_NAME(Type), PDF_NAME(TransformParams)); | |
| 1973 pdf_dict_put(ctx, t, PDF_NAME(V), PDF_NAME(1_2)); | |
| 1974 | |
| 1975 /* Record details within the document structure so that contents | |
| 1976 * and byte_range can be updated with their correct values at | |
| 1977 * saving time */ | |
| 1978 pdf_xref_store_unsaved_signature(ctx, doc, field, signer); | |
| 1979 } | |
| 1980 fz_always(ctx) | |
| 1981 { | |
| 1982 pdf_drop_obj(ctx, v); | |
| 1983 pdf_drop_obj(ctx, b); | |
| 1984 fz_free(ctx, buf); | |
| 1985 } | |
| 1986 fz_catch(ctx) | |
| 1987 { | |
| 1988 fz_rethrow(ctx); | |
| 1989 } | |
| 1990 } | |
| 1991 | |
| 1992 void pdf_set_widget_editing_state(fz_context *ctx, pdf_annot *widget, int editing) | |
| 1993 { | |
| 1994 widget->ignore_trigger_events = editing; | |
| 1995 } | |
| 1996 | |
| 1997 int pdf_get_widget_editing_state(fz_context *ctx, pdf_annot *widget) | |
| 1998 { | |
| 1999 return widget->ignore_trigger_events; | |
| 2000 } | |
| 2001 | |
| 2002 static void pdf_execute_js_action(fz_context *ctx, pdf_document *doc, pdf_obj *target, const char *path, pdf_obj *js) | |
| 2003 { | |
| 2004 if (js) | |
| 2005 { | |
| 2006 char *code = pdf_load_stream_or_string_as_utf8(ctx, js); | |
| 2007 int in_op = 0; | |
| 2008 | |
| 2009 fz_var(in_op); | |
| 2010 fz_try(ctx) | |
| 2011 { | |
| 2012 char buf[100]; | |
| 2013 fz_snprintf(buf, sizeof buf, "%d/%s", pdf_to_num(ctx, target), path); | |
| 2014 pdf_begin_operation(ctx, doc, "Javascript Event"); | |
| 2015 in_op = 1; | |
| 2016 pdf_js_execute(doc->js, buf, code, NULL); | |
| 2017 pdf_end_operation(ctx, doc); | |
| 2018 } | |
| 2019 fz_always(ctx) | |
| 2020 { | |
| 2021 fz_free(ctx, code); | |
| 2022 } | |
| 2023 fz_catch(ctx) | |
| 2024 { | |
| 2025 if (in_op) | |
| 2026 pdf_abandon_operation(ctx, doc); | |
| 2027 fz_rethrow(ctx); | |
| 2028 } | |
| 2029 } | |
| 2030 } | |
| 2031 | |
| 2032 static void pdf_execute_action_imp(fz_context *ctx, pdf_document *doc, pdf_obj *target, const char *path, pdf_obj *action) | |
| 2033 { | |
| 2034 pdf_obj *S = pdf_dict_get(ctx, action, PDF_NAME(S)); | |
| 2035 if (pdf_name_eq(ctx, S, PDF_NAME(JavaScript))) | |
| 2036 { | |
| 2037 if (doc->js) | |
| 2038 pdf_execute_js_action(ctx, doc, target, path, pdf_dict_get(ctx, action, PDF_NAME(JS))); | |
| 2039 } | |
| 2040 if (pdf_name_eq(ctx, S, PDF_NAME(ResetForm))) | |
| 2041 { | |
| 2042 pdf_obj *fields = pdf_dict_get(ctx, action, PDF_NAME(Fields)); | |
| 2043 int flags = pdf_dict_get_int(ctx, action, PDF_NAME(Flags)); | |
| 2044 pdf_reset_form(ctx, doc, fields, flags & 1); | |
| 2045 } | |
| 2046 } | |
| 2047 | |
| 2048 static void pdf_execute_action_chain(fz_context *ctx, pdf_document *doc, pdf_obj *target, const char *path, pdf_obj *action, pdf_cycle_list *cycle_up) | |
| 2049 { | |
| 2050 pdf_cycle_list cycle; | |
| 2051 pdf_obj *next; | |
| 2052 | |
| 2053 if (pdf_cycle(ctx, &cycle, cycle_up, action)) | |
| 2054 fz_throw(ctx, FZ_ERROR_FORMAT, "cycle in action chain"); | |
| 2055 | |
| 2056 if (pdf_is_array(ctx, action)) | |
| 2057 { | |
| 2058 int i, n = pdf_array_len(ctx, action); | |
| 2059 for (i = 0; i < n; ++i) | |
| 2060 pdf_execute_action_chain(ctx, doc, target, path, pdf_array_get(ctx, action, i), &cycle); | |
| 2061 } | |
| 2062 else | |
| 2063 { | |
| 2064 pdf_execute_action_imp(ctx, doc, target, path, action); | |
| 2065 next = pdf_dict_get(ctx, action, PDF_NAME(Next)); | |
| 2066 if (next) | |
| 2067 pdf_execute_action_chain(ctx, doc, target, path, next, &cycle); | |
| 2068 } | |
| 2069 } | |
| 2070 | |
| 2071 static void pdf_execute_action(fz_context *ctx, pdf_document *doc, pdf_obj *target, const char *path) | |
| 2072 { | |
| 2073 pdf_obj *action = pdf_dict_getp_inheritable(ctx, target, path); | |
| 2074 if (action) | |
| 2075 pdf_execute_action_chain(ctx, doc, target, path, action, NULL); | |
| 2076 } | |
| 2077 | |
| 2078 void pdf_document_event_will_close(fz_context *ctx, pdf_document *doc) | |
| 2079 { | |
| 2080 pdf_execute_action(ctx, doc, pdf_trailer(ctx, doc), "Root/AA/WC"); | |
| 2081 } | |
| 2082 | |
| 2083 void pdf_document_event_will_save(fz_context *ctx, pdf_document *doc) | |
| 2084 { | |
| 2085 pdf_execute_action(ctx, doc, pdf_trailer(ctx, doc), "Root/AA/WS"); | |
| 2086 } | |
| 2087 | |
| 2088 void pdf_document_event_did_save(fz_context *ctx, pdf_document *doc) | |
| 2089 { | |
| 2090 pdf_execute_action(ctx, doc, pdf_trailer(ctx, doc), "Root/AA/DS"); | |
| 2091 } | |
| 2092 | |
| 2093 void pdf_document_event_will_print(fz_context *ctx, pdf_document *doc) | |
| 2094 { | |
| 2095 pdf_execute_action(ctx, doc, pdf_trailer(ctx, doc), "Root/AA/WP"); | |
| 2096 } | |
| 2097 | |
| 2098 void pdf_document_event_did_print(fz_context *ctx, pdf_document *doc) | |
| 2099 { | |
| 2100 pdf_execute_action(ctx, doc, pdf_trailer(ctx, doc), "Root/AA/DP"); | |
| 2101 } | |
| 2102 | |
| 2103 void pdf_page_event_open(fz_context *ctx, pdf_page *page) | |
| 2104 { | |
| 2105 pdf_execute_action(ctx, page->doc, page->obj, "AA/O"); | |
| 2106 } | |
| 2107 | |
| 2108 void pdf_page_event_close(fz_context *ctx, pdf_page *page) | |
| 2109 { | |
| 2110 pdf_execute_action(ctx, page->doc, page->obj, "AA/C"); | |
| 2111 } | |
| 2112 | |
| 2113 static void | |
| 2114 annot_execute_action(fz_context *ctx, pdf_annot *annot, const char *act) | |
| 2115 { | |
| 2116 begin_annot_op(ctx, annot, "JavaScript action"); | |
| 2117 | |
| 2118 fz_try(ctx) | |
| 2119 { | |
| 2120 pdf_execute_action(ctx, annot->page->doc, annot->obj, act); | |
| 2121 end_annot_op(ctx, annot); | |
| 2122 } | |
| 2123 fz_catch(ctx) | |
| 2124 { | |
| 2125 abandon_annot_op(ctx, annot); | |
| 2126 fz_rethrow(ctx); | |
| 2127 } | |
| 2128 } | |
| 2129 | |
| 2130 void pdf_annot_event_enter(fz_context *ctx, pdf_annot *annot) | |
| 2131 { | |
| 2132 annot_execute_action(ctx, annot, "AA/E"); | |
| 2133 } | |
| 2134 | |
| 2135 void pdf_annot_event_exit(fz_context *ctx, pdf_annot *annot) | |
| 2136 { | |
| 2137 annot_execute_action(ctx, annot, "AA/X"); | |
| 2138 } | |
| 2139 | |
| 2140 void pdf_annot_event_down(fz_context *ctx, pdf_annot *annot) | |
| 2141 { | |
| 2142 annot_execute_action(ctx, annot, "AA/D"); | |
| 2143 } | |
| 2144 | |
| 2145 void pdf_annot_event_up(fz_context *ctx, pdf_annot *annot) | |
| 2146 { | |
| 2147 pdf_obj *action; | |
| 2148 | |
| 2149 begin_annot_op(ctx, annot, "JavaScript action"); | |
| 2150 | |
| 2151 fz_try(ctx) | |
| 2152 { | |
| 2153 action = pdf_dict_get(ctx, annot->obj, PDF_NAME(A)); | |
| 2154 if (action) | |
| 2155 pdf_execute_action_chain(ctx, annot->page->doc, annot->obj, "A", action, NULL); | |
| 2156 else | |
| 2157 pdf_execute_action(ctx, annot->page->doc, annot->obj, "AA/U"); | |
| 2158 end_annot_op(ctx, annot); | |
| 2159 } | |
| 2160 fz_catch(ctx) | |
| 2161 { | |
| 2162 abandon_annot_op(ctx, annot); | |
| 2163 fz_rethrow(ctx); | |
| 2164 } | |
| 2165 } | |
| 2166 | |
| 2167 void pdf_annot_event_focus(fz_context *ctx, pdf_annot *annot) | |
| 2168 { | |
| 2169 annot_execute_action(ctx, annot, "AA/Fo"); | |
| 2170 } | |
| 2171 | |
| 2172 void pdf_annot_event_blur(fz_context *ctx, pdf_annot *annot) | |
| 2173 { | |
| 2174 annot_execute_action(ctx, annot, "AA/Bl"); | |
| 2175 } | |
| 2176 | |
| 2177 void pdf_annot_event_page_open(fz_context *ctx, pdf_annot *annot) | |
| 2178 { | |
| 2179 annot_execute_action(ctx, annot, "AA/PO"); | |
| 2180 } | |
| 2181 | |
| 2182 void pdf_annot_event_page_close(fz_context *ctx, pdf_annot *annot) | |
| 2183 { | |
| 2184 annot_execute_action(ctx, annot, "AA/PC"); | |
| 2185 } | |
| 2186 | |
| 2187 void pdf_annot_event_page_visible(fz_context *ctx, pdf_annot *annot) | |
| 2188 { | |
| 2189 annot_execute_action(ctx, annot, "AA/PV"); | |
| 2190 } | |
| 2191 | |
| 2192 void pdf_annot_event_page_invisible(fz_context *ctx, pdf_annot *annot) | |
| 2193 { | |
| 2194 annot_execute_action(ctx, annot, "AA/PI"); | |
| 2195 } | |
| 2196 | |
| 2197 int pdf_field_event_keystroke(fz_context *ctx, pdf_document *doc, pdf_obj *field, pdf_keystroke_event *evt) | |
| 2198 { | |
| 2199 pdf_js *js = doc->js; | |
| 2200 if (js) | |
| 2201 { | |
| 2202 pdf_obj *action = pdf_dict_getp_inheritable(ctx, field, "AA/K/JS"); | |
| 2203 if (action) | |
| 2204 { | |
| 2205 pdf_js_event_init_keystroke(js, field, evt); | |
| 2206 pdf_execute_js_action(ctx, doc, field, "AA/K/JS", action); | |
| 2207 return pdf_js_event_result_keystroke(js, evt); | |
| 2208 } | |
| 2209 } | |
| 2210 evt->newChange = fz_strdup(ctx, evt->change); | |
| 2211 evt->newValue = fz_strdup(ctx, evt->value); | |
| 2212 return 1; | |
| 2213 } | |
| 2214 | |
| 2215 int pdf_annot_field_event_keystroke(fz_context *ctx, pdf_document *doc, pdf_annot *annot, pdf_keystroke_event *evt) | |
| 2216 { | |
| 2217 int ret; | |
| 2218 | |
| 2219 pdf_annot_push_local_xref(ctx, annot); | |
| 2220 | |
| 2221 fz_try(ctx) | |
| 2222 ret = pdf_field_event_keystroke(ctx, doc, annot->obj, evt); | |
| 2223 fz_always(ctx) | |
| 2224 pdf_annot_pop_local_xref(ctx, annot); | |
| 2225 fz_catch(ctx) | |
| 2226 fz_rethrow(ctx); | |
| 2227 | |
| 2228 return ret; | |
| 2229 } | |
| 2230 | |
| 2231 char *pdf_field_event_format(fz_context *ctx, pdf_document *doc, pdf_obj *field) | |
| 2232 { | |
| 2233 pdf_js *js = doc->js; | |
| 2234 if (js) | |
| 2235 { | |
| 2236 pdf_obj *action = pdf_dict_getp_inheritable(ctx, field, "AA/F/JS"); | |
| 2237 if (action) | |
| 2238 { | |
| 2239 const char *value = pdf_field_value(ctx, field); | |
| 2240 pdf_js_event_init(js, field, value, 1); | |
| 2241 pdf_execute_js_action(ctx, doc, field, "AA/F/JS", action); | |
| 2242 return pdf_js_event_value(js); | |
| 2243 } | |
| 2244 } | |
| 2245 return NULL; | |
| 2246 } | |
| 2247 | |
| 2248 int pdf_field_event_validate(fz_context *ctx, pdf_document *doc, pdf_obj *field, const char *value, char **newvalue) | |
| 2249 { | |
| 2250 pdf_js *js = doc->js; | |
| 2251 | |
| 2252 *newvalue = NULL; | |
| 2253 if (js) | |
| 2254 { | |
| 2255 pdf_obj *action = pdf_dict_getp_inheritable(ctx, field, "AA/V/JS"); | |
| 2256 if (action) | |
| 2257 { | |
| 2258 pdf_js_event_init(js, field, value, 1); | |
| 2259 pdf_execute_js_action(ctx, doc, field, "AA/V/JS", action); | |
| 2260 return pdf_js_event_result_validate(js, newvalue); | |
| 2261 } | |
| 2262 } | |
| 2263 return 1; | |
| 2264 } | |
| 2265 | |
| 2266 void pdf_field_event_calculate(fz_context *ctx, pdf_document *doc, pdf_obj *field) | |
| 2267 { | |
| 2268 pdf_js *js = doc->js; | |
| 2269 if (js) | |
| 2270 { | |
| 2271 pdf_obj *action = pdf_dict_getp_inheritable(ctx, field, "AA/C/JS"); | |
| 2272 if (action) | |
| 2273 { | |
| 2274 char *old_value = fz_strdup(ctx, pdf_field_value(ctx, field)); | |
| 2275 char *new_value = NULL; | |
| 2276 fz_var(new_value); | |
| 2277 fz_try(ctx) | |
| 2278 { | |
| 2279 pdf_js_event_init(js, field, old_value, 1); | |
| 2280 pdf_execute_js_action(ctx, doc, field, "AA/C/JS", action); | |
| 2281 if (pdf_js_event_result(js)) | |
| 2282 { | |
| 2283 new_value = pdf_js_event_value(js); | |
| 2284 if (strcmp(old_value, new_value)) | |
| 2285 pdf_set_field_value(ctx, doc, field, new_value, 0); | |
| 2286 } | |
| 2287 } | |
| 2288 fz_always(ctx) | |
| 2289 { | |
| 2290 fz_free(ctx, old_value); | |
| 2291 fz_free(ctx, new_value); | |
| 2292 } | |
| 2293 fz_catch(ctx) | |
| 2294 fz_rethrow(ctx); | |
| 2295 } | |
| 2296 } | |
| 2297 } | |
| 2298 | |
| 2299 static void | |
| 2300 count_sigs(fz_context *ctx, pdf_obj *field, void *arg, pdf_obj **ft) | |
| 2301 { | |
| 2302 int *n = (int *)arg; | |
| 2303 | |
| 2304 if (!pdf_name_eq(ctx, pdf_dict_get(ctx, field, PDF_NAME(Type)), PDF_NAME(Annot)) || | |
| 2305 !pdf_name_eq(ctx, pdf_dict_get(ctx, field, PDF_NAME(Subtype)), PDF_NAME(Widget)) || | |
| 2306 !pdf_name_eq(ctx, *ft, PDF_NAME(Sig))) | |
| 2307 return; | |
| 2308 | |
| 2309 (*n)++; | |
| 2310 } | |
| 2311 | |
| 2312 static pdf_obj *ft_name[2] = { PDF_NAME(FT), NULL }; | |
| 2313 | |
| 2314 int pdf_count_signatures(fz_context *ctx, pdf_document *doc) | |
| 2315 { | |
| 2316 int n = 0; | |
| 2317 pdf_obj *ft = NULL; | |
| 2318 pdf_obj *form_fields = pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/AcroForm/Fields"); | |
| 2319 pdf_walk_tree(ctx, form_fields, PDF_NAME(Kids), count_sigs, NULL, &n, ft_name, &ft); | |
| 2320 return n; | |
| 2321 } | |
| 2322 | |
| 2323 /* | |
| 2324 * Bake interactive form fields into static content. | |
| 2325 */ | |
| 2326 | |
| 2327 static pdf_obj *get_annot_ap(fz_context *ctx, pdf_obj *annot) | |
| 2328 { | |
| 2329 pdf_obj *ap = pdf_dict_get(ctx, annot, PDF_NAME(AP)); | |
| 2330 pdf_obj *as = pdf_dict_get(ctx, annot, PDF_NAME(AS)); | |
| 2331 if (ap) | |
| 2332 { | |
| 2333 as = pdf_resolve_indirect_chain(ctx, as); | |
| 2334 ap = pdf_dict_get(ctx, ap, PDF_NAME(N)); | |
| 2335 if (pdf_is_stream(ctx, ap)) | |
| 2336 return ap; | |
| 2337 ap = pdf_dict_get(ctx, ap, as); | |
| 2338 if (pdf_is_stream(ctx, ap)) | |
| 2339 return ap; | |
| 2340 } | |
| 2341 return NULL; | |
| 2342 } | |
| 2343 | |
| 2344 static fz_matrix get_annot_transform(fz_context *ctx, pdf_obj *annot, pdf_obj *ap) | |
| 2345 { | |
| 2346 float w, h, x, y; | |
| 2347 fz_matrix transform; | |
| 2348 fz_rect bbox; | |
| 2349 fz_rect rect; | |
| 2350 | |
| 2351 rect = pdf_dict_get_rect(ctx, annot, PDF_NAME(Rect)); | |
| 2352 bbox = pdf_dict_get_rect(ctx, ap, PDF_NAME(BBox)); | |
| 2353 transform = pdf_dict_get_matrix(ctx, ap, PDF_NAME(Matrix)); | |
| 2354 | |
| 2355 bbox = fz_transform_rect(bbox, transform); | |
| 2356 w = (rect.x1 - rect.x0) / (bbox.x1 - bbox.x0); | |
| 2357 h = (rect.y1 - rect.y0) / (bbox.y1 - bbox.y0); | |
| 2358 x = rect.x0 - bbox.x0 * w; | |
| 2359 y = rect.y0 - bbox.y0 * h; | |
| 2360 | |
| 2361 return fz_make_matrix(w, 0, 0, h, x, y); | |
| 2362 } | |
| 2363 | |
| 2364 static void pdf_bake_annot(fz_context *ctx, fz_buffer *buf, pdf_document *doc, pdf_obj *page, pdf_obj *res_xobj, pdf_obj *annot) | |
| 2365 { | |
| 2366 fz_matrix m; | |
| 2367 pdf_obj *ap; | |
| 2368 char name[20]; | |
| 2369 | |
| 2370 ap = get_annot_ap(ctx, annot); | |
| 2371 if (ap) | |
| 2372 { | |
| 2373 fz_snprintf(name, sizeof name, "Annot%d", pdf_to_num(ctx, annot)); | |
| 2374 pdf_dict_puts(ctx, res_xobj, name, ap); | |
| 2375 pdf_dict_put(ctx, ap, PDF_NAME(Type), PDF_NAME(XObject)); | |
| 2376 pdf_dict_put(ctx, ap, PDF_NAME(Subtype), PDF_NAME(Form)); | |
| 2377 m = get_annot_transform(ctx, annot, ap); | |
| 2378 fz_append_printf(ctx, buf, | |
| 2379 "q\n%g %g %g %g %g %g cm\n/%s Do\nQ\n", | |
| 2380 m.a, m.b, m.c, m.d, m.e, m.f, | |
| 2381 name | |
| 2382 ); | |
| 2383 } | |
| 2384 } | |
| 2385 | |
| 2386 static void pdf_bake_page(fz_context *ctx, pdf_document *doc, pdf_obj *page, int bake_annots, int bake_widgets) | |
| 2387 { | |
| 2388 pdf_obj *res; | |
| 2389 pdf_obj *res_xobj; | |
| 2390 pdf_obj *contents; | |
| 2391 pdf_obj *new_contents = NULL; | |
| 2392 pdf_obj *annots; | |
| 2393 pdf_obj *annot; | |
| 2394 pdf_obj *subtype; | |
| 2395 pdf_obj *prologue = NULL; | |
| 2396 fz_buffer *buf = NULL; | |
| 2397 int prepend, append; | |
| 2398 int i; | |
| 2399 | |
| 2400 fz_var(buf); | |
| 2401 fz_var(prologue); | |
| 2402 fz_var(new_contents); | |
| 2403 | |
| 2404 annots = pdf_dict_get(ctx, page, PDF_NAME(Annots)); | |
| 2405 if (pdf_array_len(ctx, annots) == 0) | |
| 2406 return; | |
| 2407 | |
| 2408 res = pdf_dict_get(ctx, page, PDF_NAME(Resources)); | |
| 2409 if (!res) | |
| 2410 res = pdf_dict_put_dict(ctx, page, PDF_NAME(Resources), 4); | |
| 2411 | |
| 2412 res_xobj = pdf_dict_get(ctx, res, PDF_NAME(XObject)); | |
| 2413 if (!res_xobj) | |
| 2414 res_xobj = pdf_dict_put_dict(ctx, res, PDF_NAME(XObject), 8); | |
| 2415 | |
| 2416 fz_try(ctx) | |
| 2417 { | |
| 2418 // Ensure that the graphics state is balanced. | |
| 2419 contents = pdf_dict_get(ctx, page, PDF_NAME(Contents)); | |
| 2420 pdf_count_q_balance(ctx, doc, res, contents, &prepend, &append); | |
| 2421 | |
| 2422 if (prepend) | |
| 2423 { | |
| 2424 // Prepend enough 'q' to ensure we can get back to initial state. | |
| 2425 buf = fz_new_buffer(ctx, 1024); | |
| 2426 while (prepend-- > 0) | |
| 2427 fz_append_string(ctx, buf, "q\n"); | |
| 2428 | |
| 2429 prologue = pdf_add_stream(ctx, doc, buf, NULL, 0); | |
| 2430 fz_drop_buffer(ctx, buf); | |
| 2431 buf = NULL; | |
| 2432 } | |
| 2433 | |
| 2434 // Append enough 'Q' to get back to initial state. | |
| 2435 buf = fz_new_buffer(ctx, 1024); | |
| 2436 while (append-- > 0) | |
| 2437 fz_append_string(ctx, buf, "Q\n"); | |
| 2438 | |
| 2439 for (i = 0; i < pdf_array_len(ctx, annots); ) | |
| 2440 { | |
| 2441 annot = pdf_array_get(ctx, annots, i); | |
| 2442 subtype = pdf_dict_get(ctx, annot, PDF_NAME(Subtype)); | |
| 2443 if (subtype == PDF_NAME(Link)) | |
| 2444 { | |
| 2445 ++i; | |
| 2446 } | |
| 2447 else if (subtype == PDF_NAME(Widget)) | |
| 2448 { | |
| 2449 if (bake_widgets) | |
| 2450 { | |
| 2451 pdf_bake_annot(ctx, buf, doc, page, res_xobj, annot); | |
| 2452 pdf_array_delete(ctx, annots, i); | |
| 2453 } | |
| 2454 else | |
| 2455 { | |
| 2456 ++i; | |
| 2457 } | |
| 2458 } | |
| 2459 else | |
| 2460 { | |
| 2461 if (bake_annots) | |
| 2462 { | |
| 2463 pdf_bake_annot(ctx, buf, doc, page, res_xobj, annot); | |
| 2464 pdf_array_delete(ctx, annots, i); | |
| 2465 } | |
| 2466 else | |
| 2467 { | |
| 2468 ++i; | |
| 2469 } | |
| 2470 } | |
| 2471 } | |
| 2472 | |
| 2473 if (!pdf_is_array(ctx, contents)) | |
| 2474 { | |
| 2475 new_contents = pdf_new_array(ctx, doc, 10); | |
| 2476 if (prologue) | |
| 2477 pdf_array_push(ctx, new_contents, prologue); | |
| 2478 if (contents) | |
| 2479 pdf_array_push(ctx, new_contents, contents); | |
| 2480 pdf_dict_put(ctx, page, PDF_NAME(Contents), new_contents); | |
| 2481 pdf_drop_obj(ctx, new_contents); | |
| 2482 contents = new_contents; | |
| 2483 new_contents = NULL; | |
| 2484 } | |
| 2485 else if (prologue) | |
| 2486 { | |
| 2487 pdf_array_insert(ctx, contents, prologue, 0); | |
| 2488 } | |
| 2489 | |
| 2490 pdf_array_push_drop(ctx, contents, pdf_add_stream(ctx, doc, buf, NULL, 0)); | |
| 2491 } | |
| 2492 fz_always(ctx) | |
| 2493 { | |
| 2494 fz_drop_buffer(ctx, buf); | |
| 2495 pdf_drop_obj(ctx, prologue); | |
| 2496 pdf_drop_obj(ctx, new_contents); | |
| 2497 } | |
| 2498 fz_catch(ctx) | |
| 2499 { | |
| 2500 fz_rethrow(ctx); | |
| 2501 } | |
| 2502 } | |
| 2503 | |
| 2504 void pdf_bake_document(fz_context *ctx, pdf_document *doc, int bake_annots, int bake_widgets) | |
| 2505 { | |
| 2506 pdf_page *page = NULL; | |
| 2507 pdf_annot *annot; | |
| 2508 int i, n; | |
| 2509 | |
| 2510 fz_var(page); | |
| 2511 | |
| 2512 pdf_begin_operation(ctx, doc, "Bake interactive content"); | |
| 2513 fz_try(ctx) | |
| 2514 { | |
| 2515 n = pdf_count_pages(ctx, doc); | |
| 2516 for (i = 0; i < n; ++i) | |
| 2517 { | |
| 2518 page = pdf_load_page(ctx, doc, i); | |
| 2519 | |
| 2520 if (bake_annots) | |
| 2521 for (annot = pdf_first_annot(ctx, page); annot; annot = pdf_next_annot(ctx, annot)) | |
| 2522 pdf_annot_request_synthesis(ctx, annot); | |
| 2523 if (bake_widgets) | |
| 2524 for (annot = pdf_first_widget(ctx, page); annot; annot = pdf_next_widget(ctx, annot)) | |
| 2525 pdf_annot_request_synthesis(ctx, annot); | |
| 2526 pdf_update_page(ctx, page); | |
| 2527 | |
| 2528 pdf_bake_page(ctx, doc, page->obj, bake_annots, bake_widgets); | |
| 2529 | |
| 2530 fz_drop_page(ctx, (fz_page*)page); | |
| 2531 page = NULL; | |
| 2532 } | |
| 2533 | |
| 2534 if (bake_widgets) | |
| 2535 { | |
| 2536 pdf_obj *trailer = pdf_trailer(ctx, doc); | |
| 2537 pdf_obj *root = pdf_dict_get(ctx, trailer, PDF_NAME(Root)); | |
| 2538 pdf_dict_del(ctx, root, PDF_NAME(AcroForm)); | |
| 2539 } | |
| 2540 pdf_end_operation(ctx, doc); | |
| 2541 } | |
| 2542 fz_always(ctx) | |
| 2543 { | |
| 2544 fz_drop_page(ctx, (fz_page*)page); | |
| 2545 } | |
| 2546 fz_catch(ctx) | |
| 2547 { | |
| 2548 pdf_abandon_operation(ctx, doc); | |
| 2549 } | |
| 2550 } |
