1#include "mupdf/fitz.h"
2#include "mupdf/pdf.h"
3
4#if FZ_ENABLE_JS
5
6#include "mujs.h"
7
8#include <stdarg.h>
9#include <string.h>
10
11struct pdf_js_s
12{
13 fz_context *ctx;
14 pdf_document *doc;
15 pdf_obj *form;
16 js_State *imp;
17};
18
19FZ_NORETURN static void rethrow(pdf_js *js)
20{
21 js_newerror(js->imp, fz_caught_message(js->ctx));
22 js_throw(js->imp);
23}
24
25/* Unpack argument object with named arguments into actual parameters. */
26static pdf_js *unpack_arguments(js_State *J, ...)
27{
28 if (js_isobject(J, 1))
29 {
30 int i = 1;
31 va_list args;
32
33 js_copy(J, 1);
34
35 va_start(args, J);
36 for (;;)
37 {
38 const char *s = va_arg(args, const char *);
39 if (!s)
40 break;
41 js_getproperty(J, -1, s);
42 js_replace(J, i++);
43 }
44 va_end(args);
45
46 js_pop(J, 1);
47 }
48 return js_getcontext(J);
49}
50
51static void app_alert(js_State *J)
52{
53 pdf_js *js = unpack_arguments(J, "cMsg", "nIcon", "nType", "cTitle", 0);
54 pdf_alert_event event;
55
56 event.message = js_tostring(J, 1);
57 event.icon_type = js_tointeger(J, 2);
58 event.button_group_type = js_tointeger(J, 3);
59 event.title = js_isdefined(J, 4) ? js_tostring(J, 4) : "PDF Alert";
60 event.button_pressed = 0; /* WIP WIP WIP IS THIS CORRECT? */
61
62 fz_try(js->ctx)
63 pdf_event_issue_alert(js->ctx, js->doc, &event);
64 fz_catch(js->ctx)
65 rethrow(js);
66
67 js_pushnumber(J, event.button_pressed);
68}
69
70static void app_execMenuItem(js_State *J)
71{
72 pdf_js *js = js_getcontext(J);
73 const char *cMenuItem = js_tostring(J, 1);
74 fz_try(js->ctx)
75 pdf_event_issue_exec_menu_item(js->ctx, js->doc, cMenuItem);
76 fz_catch(js->ctx)
77 rethrow(js);
78}
79
80static void app_launchURL(js_State *J)
81{
82 pdf_js *js = js_getcontext(J);
83 const char *cUrl = js_tostring(J, 1);
84 int bNewFrame = js_toboolean(J, 1);
85 fz_try(js->ctx)
86 pdf_event_issue_launch_url(js->ctx, js->doc, cUrl, bNewFrame);
87 fz_catch(js->ctx)
88 rethrow(js);
89}
90
91static void field_finalize(js_State *J, void *p)
92{
93 pdf_js *js = js_getcontext(J);
94 pdf_drop_obj(js->ctx, p);
95}
96
97static void field_buttonSetCaption(js_State *J)
98{
99 pdf_js *js = js_getcontext(J);
100 pdf_obj *field = js_touserdata(J, 0, "Field");
101 const char *cCaption = js_tostring(J, 1);
102 fz_try(js->ctx)
103 pdf_field_set_button_caption(js->ctx, field, cCaption);
104 fz_catch(js->ctx)
105 rethrow(js);
106}
107
108static void field_getName(js_State *J)
109{
110 pdf_js *js = js_getcontext(J);
111 pdf_obj *field = js_touserdata(J, 0, "Field");
112 char *name = NULL;
113 fz_try(js->ctx)
114 name = pdf_field_name(js->ctx, field);
115 fz_catch(js->ctx)
116 rethrow(js);
117 js_pushstring(J, name);
118}
119
120static void field_setName(js_State *J)
121{
122 pdf_js *js = js_getcontext(J);
123 fz_warn(js->ctx, "Unexpected call to field_setName");
124}
125
126static void field_getDisplay(js_State *J)
127{
128 pdf_js *js = js_getcontext(J);
129 pdf_obj *field = js_touserdata(J, 0, "Field");
130 int display = 0;
131 fz_try(js->ctx)
132 display = pdf_field_display(js->ctx, field);
133 fz_catch(js->ctx)
134 rethrow(js);
135 js_pushnumber(J, display);
136}
137
138static void field_setDisplay(js_State *J)
139{
140 pdf_js *js = js_getcontext(J);
141 pdf_obj *field = js_touserdata(J, 0, "Field");
142 int display = js_tonumber(J, 1);
143 fz_try(js->ctx)
144 pdf_field_set_display(js->ctx, field, display);
145 fz_catch(js->ctx)
146 rethrow(js);
147}
148
149static pdf_obj *load_color(pdf_js *js, int idx)
150{
151 fz_context *ctx = js->ctx;
152 pdf_document *doc = js->doc;
153 js_State *J = js->imp;
154
155 pdf_obj *color = NULL;
156 int i, n;
157 float c;
158
159 n = js_getlength(J, idx);
160
161 /* The only legitimate color expressed as an array of length 1
162 * is [T], meaning transparent. Return a NULL object to represent
163 * transparent */
164 if (n <= 1)
165 return NULL;
166
167 fz_var(color);
168
169 fz_try(ctx)
170 {
171 color = pdf_new_array(ctx, doc, n-1);
172 for (i = 0; i < n-1; i++)
173 {
174 js_getindex(J, idx, i+1);
175 c = js_tonumber(J, -1);
176 js_pop(J, 1);
177
178 pdf_array_push_real(ctx, color, c);
179 }
180 }
181 fz_catch(ctx)
182 {
183 pdf_drop_obj(ctx, color);
184 rethrow(js);
185 }
186
187 return color;
188}
189
190static void field_getFillColor(js_State *J)
191{
192 js_pushundefined(J);
193}
194
195static void field_setFillColor(js_State *J)
196{
197 pdf_js *js = js_getcontext(J);
198 pdf_obj *field = js_touserdata(J, 0, "Field");
199 pdf_obj *color = load_color(js, 1);
200 fz_try(js->ctx)
201 pdf_field_set_fill_color(js->ctx, field, color);
202 fz_always(js->ctx)
203 pdf_drop_obj(js->ctx, color);
204 fz_catch(js->ctx)
205 rethrow(js);
206}
207
208static void field_getTextColor(js_State *J)
209{
210 js_pushundefined(J);
211}
212
213static void field_setTextColor(js_State *J)
214{
215 pdf_js *js = js_getcontext(J);
216 pdf_obj *field = js_touserdata(J, 0, "Field");
217 pdf_obj *color = load_color(js, 1);
218 fz_try(js->ctx)
219 pdf_field_set_text_color(js->ctx, field, color);
220 fz_always(js->ctx)
221 pdf_drop_obj(js->ctx, color);
222 fz_catch(js->ctx)
223 rethrow(js);
224}
225
226static void field_getBorderStyle(js_State *J)
227{
228 pdf_js *js = js_getcontext(J);
229 pdf_obj *field = js_touserdata(J, 0, "Field");
230 const char *border_style = NULL;
231 fz_try(js->ctx)
232 border_style = pdf_field_border_style(js->ctx, field);
233 fz_catch(js->ctx)
234 rethrow(js);
235 js_pushstring(J, border_style);
236}
237
238static void field_setBorderStyle(js_State *J)
239{
240 pdf_js *js = js_getcontext(J);
241 pdf_obj *field = js_touserdata(J, 0, "Field");
242 const char *border_style = js_tostring(J, 1);
243 fz_try(js->ctx)
244 pdf_field_set_border_style(js->ctx, field, border_style);
245 fz_catch(js->ctx)
246 rethrow(js);
247}
248
249static void field_getValue(js_State *J)
250{
251 pdf_js *js = js_getcontext(J);
252 pdf_obj *field = js_touserdata(J, 0, "Field");
253 const char *str = NULL;
254 char *end;
255 double num;
256
257 fz_try(js->ctx)
258 str = pdf_field_value(js->ctx, field);
259 fz_catch(js->ctx)
260 rethrow(js);
261
262 num = strtod(str, &end);
263 if (*str && *end == 0)
264 js_pushnumber(J, num);
265 else
266 js_pushstring(J, str);
267}
268
269static void field_setValue(js_State *J)
270{
271 pdf_js *js = js_getcontext(J);
272 pdf_obj *field = js_touserdata(J, 0, "Field");
273 const char *value = js_tostring(J, 1);
274
275 fz_try(js->ctx)
276 (void)pdf_set_field_value(js->ctx, js->doc, field, value, 0);
277 fz_catch(js->ctx)
278 rethrow(js);
279}
280
281static void doc_getField(js_State *J)
282{
283 pdf_js *js = js_getcontext(J);
284 fz_context *ctx = js->ctx;
285 const char *cName = js_tostring(J, 1);
286 pdf_obj *dict = NULL;
287
288 fz_try(ctx)
289 dict = pdf_lookup_field(ctx, js->form, cName);
290 fz_catch(ctx)
291 rethrow(js);
292
293 if (dict)
294 {
295 js_getregistry(J, "Field");
296 js_newuserdata(J, "Field", pdf_keep_obj(js->ctx, dict), field_finalize);
297 }
298 else
299 {
300 js_pushnull(J);
301 }
302}
303
304static void doc_getNumPages(js_State *J)
305{
306 pdf_js *js = js_getcontext(J);
307 int pages = pdf_count_pages(js->ctx, js->doc);
308 js_pushnumber(J, pages);
309}
310
311static void doc_setNumPages(js_State *J)
312{
313 pdf_js *js = js_getcontext(J);
314 fz_warn(js->ctx, "Unexpected call to doc_setNumPages");
315}
316
317static void doc_resetForm(js_State *J)
318{
319 pdf_js *js = js_getcontext(J);
320 pdf_obj *field;
321 fz_context *ctx = js->ctx;
322 int i, n;
323
324 /* An array of fields has been passed in. Call pdf_reset_field on each item. */
325 if (js_isarray(J, 1))
326 {
327 n = js_getlength(J, 1);
328 for (i = 0; i < n; ++i)
329 {
330 js_getindex(J, 1, i);
331 field = pdf_lookup_field(ctx, js->form, js_tostring(J, -1));
332 if (field)
333 pdf_field_reset(ctx, js->doc, field);
334 js_pop(J, 1);
335 }
336 }
337
338 /* No argument or null passed in means reset all. */
339 else
340 {
341 n = pdf_array_len(ctx, js->form);
342 for (i = 0; i < n; i++)
343 {
344 fz_try(ctx)
345 pdf_field_reset(ctx, js->doc, pdf_array_get(ctx, js->form, i));
346 fz_catch(ctx)
347 rethrow(js);
348 }
349 }
350}
351
352static void doc_print(js_State *J)
353{
354 pdf_js *js = js_getcontext(J);
355 fz_try(js->ctx)
356 pdf_event_issue_print(js->ctx, js->doc);
357 fz_catch(js->ctx)
358 rethrow(js);
359}
360
361static void doc_mailDoc(js_State *J)
362{
363 pdf_js *js = unpack_arguments(J, "bUI", "cTo", "cCc", "cBcc", "cSubject", "cMessage", 0);
364 pdf_mail_doc_event event;
365
366 event.ask_user = js_isdefined(J, 1) ? js_toboolean(J, 1) : 1;
367 event.to = js_tostring(J, 2);
368 event.cc = js_tostring(J, 3);
369 event.bcc = js_tostring(J, 4);
370 event.subject = js_tostring(J, 5);
371 event.message = js_tostring(J, 6);
372
373 fz_try(js->ctx)
374 pdf_event_issue_mail_doc(js->ctx, js->doc, &event);
375 fz_catch(js->ctx)
376 rethrow(js);
377}
378
379static void doc_calculateNow(js_State *J)
380{
381 pdf_js *js = js_getcontext(J);
382 fz_try(js->ctx)
383 pdf_calculate_form(js->ctx, js->doc);
384 fz_catch(js->ctx)
385 rethrow(js);
386}
387
388static void console_println(js_State *J)
389{
390 int i, top = js_gettop(J);
391 for (i = 1; i < top; ++i) {
392 const char *s = js_tostring(J, i);
393 if (i > 1) putchar(' ');
394 fputs(s, stdout);
395 }
396 putchar('\n');
397 js_pushundefined(J);
398}
399
400static void util_printf_d(fz_context *ctx, fz_buffer *out, int ds, int sign, int pad, int w, int base, int value)
401{
402 static const char *digits = "0123456789abcdef";
403 char buf[50];
404 unsigned int a;
405 int i, m = 0;
406
407 if (value < 0)
408 {
409 sign = '-';
410 a = -value;
411 }
412 else
413 {
414 a = value;
415 }
416
417 i = 0;
418 do
419 {
420 buf[i++] = digits[a % base];
421 a /= base;
422 if (a > 0 && ++m == 3)
423 {
424 if (ds == 0) buf[i++] = ',';
425 if (ds == 2) buf[i++] = '.';
426 m = 0;
427 }
428 } while (a);
429
430 if (sign)
431 {
432 if (pad == '0')
433 while (i < w - 1)
434 buf[i++] = pad;
435 buf[i++] = sign;
436 }
437 while (i < w)
438 buf[i++] = pad;
439
440 while (i > 0)
441 fz_append_byte(ctx, out, buf[--i]);
442}
443
444static void util_printf_f(fz_context *ctx, fz_buffer *out, int ds, int sign, int pad, int special, int w, int p, double value)
445{
446 char buf[40], *point, *digits = buf;
447 int n = 0;
448 int m = 0;
449
450 fz_snprintf(buf, sizeof buf, "%.*f", p, value);
451
452 if (*digits == '-')
453 {
454 sign = '-';
455 ++digits;
456 }
457
458 if (*digits != '.' && (*digits < '0' || *digits > '9'))
459 {
460 fz_append_string(ctx, out, "nan");
461 return;
462 }
463
464 n = strlen(digits);
465 if (sign)
466 ++n;
467 point = strchr(digits, '.');
468 if (point)
469 m = 3 - (point - digits) % 3;
470 else
471 {
472 m = 3 - n % 3;
473 if (special)
474 ++n;
475 }
476 if (m == 3)
477 m = 0;
478
479 if (pad == '0' && sign)
480 fz_append_byte(ctx, out, sign);
481 for (; n < w; ++n)
482 fz_append_byte(ctx, out, pad);
483 if (pad == ' ' && sign)
484 fz_append_byte(ctx, out, sign);
485
486 while (*digits && *digits != '.')
487 {
488 fz_append_byte(ctx, out, *digits++);
489 if (++m == 3 && *digits && *digits != '.')
490 {
491 if (ds == 0) fz_append_byte(ctx, out, ',');
492 if (ds == 2) fz_append_byte(ctx, out, '.');
493 m = 0;
494 }
495 }
496
497 if (*digits == '.' || special)
498 {
499 if (ds == 0 || ds == 1)
500 fz_append_byte(ctx, out, '.');
501 else
502 fz_append_byte(ctx, out, ',');
503 }
504
505 if (*digits == '.')
506 {
507 ++digits;
508 while (*digits)
509 fz_append_byte(ctx, out, *digits++);
510 }
511}
512
513static void util_printf(js_State *J)
514{
515 pdf_js *js = js_getcontext(J);
516 fz_context *ctx = js->ctx;
517 const char *fmt = js_tostring(J, 1);
518 fz_buffer *out = NULL;
519 int ds, w, p, sign, pad, special;
520 int c, i = 1;
521 int failed = 0;
522 const char *str;
523
524 fz_var(out);
525 fz_try(ctx)
526 {
527 out = fz_new_buffer(ctx, 256);
528
529 while ((c = *fmt++) != 0)
530 {
531 if (c == '%')
532 {
533 c = *fmt++;
534
535 ds = 1;
536 if (c == ',')
537 {
538 c = *fmt++;
539 if (!c)
540 break;
541 ds = c - '0';
542 }
543
544 special = 0;
545 sign = 0;
546 pad = ' ';
547 while (c == ' ' || c == '+' || c == '0' || c == '#')
548 {
549 if (c == '+') sign = '+';
550 else if (c == ' ') sign = ' ';
551 else if (c == '0') pad = '0';
552 else if (c == '#') special = 1;
553 c = *fmt++;
554 }
555 if (!pad)
556 pad = ' ';
557 if (!c)
558 break;
559
560 w = 0;
561 while (c >= '0' && c <= '9')
562 {
563 w = w * 10 + (c - '0');
564 c = *fmt++;
565 }
566 if (!c)
567 break;
568
569 p = 0;
570 if (c == '.')
571 {
572 c = *fmt++;
573 while (c >= '0' && c <= '9')
574 {
575 p = p * 10 + (c - '0');
576 c = *fmt++;
577 }
578 }
579 else
580 {
581 special = 1;
582 }
583 if (!c)
584 break;
585
586 switch (c)
587 {
588 case '%':
589 fz_append_byte(ctx, out, '%');
590 break;
591 case 'x':
592 util_printf_d(ctx, out, ds, sign, pad, w, 16, js_tryinteger(J, ++i, 0));
593 break;
594 case 'd':
595 util_printf_d(ctx, out, ds, sign, pad, w, 10, js_tryinteger(J, ++i, 0));
596 break;
597 case 'f':
598 util_printf_f(ctx, out, ds, sign, pad, special, w, p, js_trynumber(J, ++i, 0));
599 break;
600 case 's':
601 default:
602 fz_append_string(ctx, out, js_trystring(J, ++i, ""));
603 }
604 }
605 else
606 {
607 fz_append_byte(ctx, out, c);
608 }
609 }
610
611 str = fz_string_from_buffer(ctx, out);
612 if (js_try(J))
613 {
614 failed = 1;
615 }
616 else
617 {
618 js_pushstring(J, str);
619 js_endtry(J);
620 }
621 }
622 fz_always(ctx)
623 fz_drop_buffer(ctx, out);
624 fz_catch(ctx)
625 rethrow(js);
626
627 if (failed)
628 js_throw(J);
629}
630
631static void addmethod(js_State *J, const char *name, js_CFunction fun, int n)
632{
633 const char *realname = strchr(name, '.');
634 realname = realname ? realname + 1 : name;
635 js_newcfunction(J, fun, name, n);
636 js_defproperty(J, -2, realname, JS_READONLY | JS_DONTENUM | JS_DONTCONF);
637}
638
639static void addproperty(js_State *J, const char *name, js_CFunction getfun, js_CFunction setfun)
640{
641 const char *realname = strchr(name, '.');
642 realname = realname ? realname + 1 : name;
643 js_newcfunction(J, getfun, name, 0);
644 js_newcfunction(J, setfun, name, 1);
645 js_defaccessor(J, -3, realname, JS_READONLY | JS_DONTENUM | JS_DONTCONF);
646}
647
648static void declare_dom(pdf_js *js)
649{
650 js_State *J = js->imp;
651
652 /* Allow access to the global environment via the 'global' name */
653 js_pushglobal(J);
654 js_defglobal(J, "global", JS_READONLY | JS_DONTCONF | JS_DONTENUM);
655
656 /* Create the 'event' object */
657 js_newobject(J);
658 js_defglobal(J, "event", JS_READONLY | JS_DONTCONF | JS_DONTENUM);
659
660 /* Create the 'util' object */
661 js_newobject(J);
662 {
663 // TODO: util.printd
664 // TODO: util.printx
665 addmethod(J, "util.printf", util_printf, 1);
666 }
667 js_defglobal(J, "util", JS_READONLY | JS_DONTCONF | JS_DONTENUM);
668
669 /* Create the 'app' object */
670 js_newobject(J);
671 {
672#ifdef _WIN32
673 js_pushstring(J, "WIN");
674#elif defined(__APPLE__)
675 js_pushstring(J, "MAC");
676#else
677 js_pushstring(J, "UNIX");
678#endif
679 js_defproperty(J, -2, "app.platform", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
680
681 addmethod(J, "app.alert", app_alert, 4);
682 addmethod(J, "app.execMenuItem", app_execMenuItem, 1);
683 addmethod(J, "app.launchURL", app_launchURL, 2);
684 }
685 js_defglobal(J, "app", JS_READONLY | JS_DONTCONF | JS_DONTENUM);
686
687 /* Create the Field prototype object */
688 js_newobject(J);
689 {
690 addproperty(J, "Field.value", field_getValue, field_setValue);
691 addproperty(J, "Field.borderStyle", field_getBorderStyle, field_setBorderStyle);
692 addproperty(J, "Field.textColor", field_getTextColor, field_setTextColor);
693 addproperty(J, "Field.fillColor", field_getFillColor, field_setFillColor);
694 addproperty(J, "Field.display", field_getDisplay, field_setDisplay);
695 addproperty(J, "Field.name", field_getName, field_setName);
696 addmethod(J, "Field.buttonSetCaption", field_buttonSetCaption, 1);
697 }
698 js_setregistry(J, "Field");
699
700 /* Create the console object */
701 js_newobject(J);
702 {
703 addmethod(J, "console.println", console_println, 1);
704 }
705 js_defglobal(J, "console", JS_READONLY | JS_DONTCONF | JS_DONTENUM);
706
707 /* Put all of the Doc methods in the global object, which is used as
708 * the 'this' binding for regular non-strict function calls. */
709 js_pushglobal(J);
710 {
711 addproperty(J, "Doc.numPages", doc_getNumPages, doc_setNumPages);
712 addmethod(J, "Doc.getField", doc_getField, 1);
713 addmethod(J, "Doc.resetForm", doc_resetForm, 0);
714 addmethod(J, "Doc.calculateNow", doc_calculateNow, 0);
715 addmethod(J, "Doc.print", doc_print, 0);
716 addmethod(J, "Doc.mailDoc", doc_mailDoc, 6);
717 }
718 js_pop(J, 1);
719}
720
721static void preload_helpers(pdf_js *js)
722{
723 /* When testing on the cluster:
724 * Use a fixed date for "new Date" and Date.now().
725 * Sadly, this breaks uses of the Date function without the new keyword.
726 * Return a fixed number from Math.random().
727 */
728#ifdef CLUSTER
729 js_dostring(js->imp,
730"var MuPDFOldDate = Date\n"
731"Date = function() { return new MuPDFOldDate(298252800000); }\n"
732"Date.now = function() { return 298252800000; }\n"
733"Date.UTC = function() { return 298252800000; }\n"
734"Date.parse = MuPDFOldDate.parse;\n"
735"Math.random = function() { return 1/4; }\n"
736 );
737#endif
738
739 js_dostring(js->imp,
740#include "js/util.js.h"
741 );
742}
743
744void pdf_drop_js(fz_context *ctx, pdf_js *js)
745{
746 if (js)
747 {
748 js_freestate(js->imp);
749 fz_free(ctx, js);
750 }
751}
752
753static void *pdf_js_alloc(void *actx, void *ptr, int n)
754{
755 return fz_realloc_no_throw(actx, ptr, n);
756}
757
758static pdf_js *pdf_new_js(fz_context *ctx, pdf_document *doc)
759{
760 pdf_js *js = fz_malloc_struct(ctx, pdf_js);
761
762 js->ctx = ctx;
763 js->doc = doc;
764
765 fz_try(ctx)
766 {
767 pdf_obj *root, *acroform;
768
769 /* Find the form array */
770 root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root));
771 acroform = pdf_dict_get(ctx, root, PDF_NAME(AcroForm));
772 js->form = pdf_dict_get(ctx, acroform, PDF_NAME(Fields));
773
774 /* Initialise the javascript engine, passing the fz_context for use in memory allocation. */
775 js->imp = js_newstate(pdf_js_alloc, ctx, 0);
776 if (!js->imp)
777 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot initialize javascript engine");
778
779 /* Also set our pdf_js context, so we can retrieve it in callbacks. */
780 js_setcontext(js->imp, js);
781
782 declare_dom(js);
783 preload_helpers(js);
784 }
785 fz_catch(ctx)
786 {
787 pdf_drop_js(ctx, js);
788 fz_rethrow(ctx);
789 }
790
791 return js;
792}
793
794static void pdf_js_load_document_level(pdf_js *js)
795{
796 fz_context *ctx = js->ctx;
797 pdf_document *doc = js->doc;
798 pdf_obj *javascript;
799 int len, i;
800
801 javascript = pdf_load_name_tree(ctx, doc, PDF_NAME(JavaScript));
802 len = pdf_dict_len(ctx, javascript);
803
804 fz_try(ctx)
805 {
806 for (i = 0; i < len; i++)
807 {
808 pdf_obj *fragment = pdf_dict_get_val(ctx, javascript, i);
809 pdf_obj *code = pdf_dict_get(ctx, fragment, PDF_NAME(JS));
810 char *codebuf = pdf_load_stream_or_string_as_utf8(ctx, code);
811 char buf[100];
812 if (pdf_is_indirect(ctx, code))
813 fz_snprintf(buf, sizeof buf, "%d", pdf_to_num(ctx, code));
814 else
815 fz_snprintf(buf, sizeof buf, "Root/Names/JavaScript/Names/%d/JS", (i+1)*2);
816 pdf_js_execute(js, buf, codebuf);
817 fz_free(ctx, codebuf);
818 }
819 }
820 fz_always(ctx)
821 pdf_drop_obj(ctx, javascript);
822 fz_catch(ctx)
823 fz_rethrow(ctx);
824}
825
826void pdf_js_event_init(pdf_js *js, pdf_obj *target, const char *value, int willCommit)
827{
828 if (js)
829 {
830 js_getglobal(js->imp, "event");
831 {
832 js_pushboolean(js->imp, 1);
833 js_setproperty(js->imp, -2, "rc");
834
835 js_pushboolean(js->imp, willCommit);
836 js_setproperty(js->imp, -2, "willCommit");
837
838 js_getregistry(js->imp, "Field");
839 js_newuserdata(js->imp, "Field", pdf_keep_obj(js->ctx, target), field_finalize);
840 js_setproperty(js->imp, -2, "target");
841
842 js_pushstring(js->imp, value);
843 js_setproperty(js->imp, -2, "value");
844 }
845 js_pop(js->imp, 1);
846 }
847}
848
849int pdf_js_event_result(pdf_js *js)
850{
851 int rc = 1;
852 if (js)
853 {
854 js_getglobal(js->imp, "event");
855 js_getproperty(js->imp, -1, "rc");
856 rc = js_tryboolean(js->imp, -1, 1);
857 js_pop(js->imp, 2);
858 }
859 return rc;
860}
861
862void pdf_js_event_init_keystroke(pdf_js *js, pdf_obj *target, pdf_keystroke_event *event)
863{
864 if (js)
865 {
866 pdf_js_event_init(js, target, event->value, event->willCommit);
867 js_getglobal(js->imp, "event");
868 {
869 js_pushstring(js->imp, event->change);
870 js_setproperty(js->imp, -2, "change");
871 js_pushnumber(js->imp, event->selStart);
872 js_setproperty(js->imp, -2, "selStart");
873 js_pushnumber(js->imp, event->selEnd);
874 js_setproperty(js->imp, -2, "selEnd");
875 }
876 js_pop(js->imp, 1);
877 }
878}
879
880int pdf_js_event_result_keystroke(pdf_js *js, pdf_keystroke_event *evt)
881{
882 int rc = 1;
883 if (js)
884 {
885 js_getglobal(js->imp, "event");
886 {
887 js_getproperty(js->imp, -1, "rc");
888 rc = js_tryboolean(js->imp, -1, 1);
889 js_pop(js->imp, 1);
890 if (rc)
891 {
892 js_getproperty(js->imp, -1, "change");
893 evt->newChange = fz_strdup(js->ctx, js_trystring(js->imp, -1, ""));
894 js_pop(js->imp, 1);
895 js_getproperty(js->imp, -1, "selStart");
896 evt->selStart = js_tryinteger(js->imp, -1, 0);
897 js_pop(js->imp, 1);
898 js_getproperty(js->imp, -1, "selEnd");
899 evt->selEnd = js_tryinteger(js->imp, -1, 0);
900 js_pop(js->imp, 1);
901 }
902 }
903 js_pop(js->imp, 1);
904 }
905 return rc;
906}
907
908char *pdf_js_event_value(pdf_js *js)
909{
910 char *value = NULL;
911 if (js)
912 {
913 js_getglobal(js->imp, "event");
914 js_getproperty(js->imp, -1, "value");
915 value = fz_strdup(js->ctx, js_trystring(js->imp, -1, "undefined"));
916 js_pop(js->imp, 2);
917 }
918 return value;
919}
920
921void pdf_js_execute(pdf_js *js, const char *name, const char *source)
922{
923 if (js)
924 {
925 if (js_ploadstring(js->imp, name, source))
926 {
927 fz_warn(js->ctx, "%s", js_trystring(js->imp, -1, "Error"));
928 js_pop(js->imp, 1);
929 return;
930 }
931 js_pushundefined(js->imp);
932 if (js_pcall(js->imp, 0))
933 {
934 fz_warn(js->ctx, "%s", js_trystring(js->imp, -1, "Error"));
935 js_pop(js->imp, 1);
936 return;
937 }
938 js_pop(js->imp, 1);
939 }
940}
941
942void pdf_enable_js(fz_context *ctx, pdf_document *doc)
943{
944 if (!doc->js)
945 {
946 doc->js = pdf_new_js(ctx, doc);
947 pdf_js_load_document_level(doc->js);
948 }
949}
950
951void pdf_disable_js(fz_context *ctx, pdf_document *doc)
952{
953 pdf_drop_js(ctx, doc->js);
954 doc->js = NULL;
955}
956
957int pdf_js_supported(fz_context *ctx, pdf_document *doc)
958{
959 return doc->js != NULL;
960}
961
962#else /* FZ_ENABLE_JS */
963
964void pdf_drop_js(fz_context *ctx, pdf_js *js) { }
965void pdf_enable_js(fz_context *ctx, pdf_document *doc) { }
966void pdf_disable_js(fz_context *ctx, pdf_document *doc) { }
967int pdf_js_supported(fz_context *ctx, pdf_document *doc) { return 0; }
968void pdf_js_event_init(pdf_js *js, pdf_obj *target, const char *value, int willCommit) { }
969void pdf_js_event_init_keystroke(pdf_js *js, pdf_obj *target, pdf_keystroke_event *event) { }
970int pdf_js_event_result_keystroke(pdf_js *js, pdf_keystroke_event *evt) { return 1; }
971int pdf_js_event_result(pdf_js *js) { return 1; }
972char *pdf_js_event_value(pdf_js *js) { return ""; }
973void pdf_js_execute(pdf_js *js, const char *name, const char *source) { }
974
975#endif /* FZ_ENABLE_JS */
976