summaryrefslogtreecommitdiff
path: root/source/pdf/js
diff options
context:
space:
mode:
authorTor Andersson <tor.andersson@artifex.com>2013-06-19 15:29:44 +0200
committerTor Andersson <tor.andersson@artifex.com>2013-06-20 16:45:35 +0200
commit0a927854a10e1e6b9770a81e2e1d9f3093631757 (patch)
tree3d65d820d9fdba2d0d394d99c36290c851b78ca0 /source/pdf/js
parent1ae8f19179c5f0f8c6352b3c7855465325d5449a (diff)
downloadmupdf-0a927854a10e1e6b9770a81e2e1d9f3093631757.tar.xz
Rearrange source files.
Diffstat (limited to 'source/pdf/js')
-rw-r--r--source/pdf/js/pdf-js-none.c36
-rw-r--r--source/pdf/js/pdf-js.c919
-rw-r--r--source/pdf/js/pdf-jsimp-cpp.c225
-rw-r--r--source/pdf/js/pdf-jsimp-cpp.h29
-rw-r--r--source/pdf/js/pdf-jsimp-v8.cpp476
-rw-r--r--source/pdf/js/pdf-util.js875
6 files changed, 2560 insertions, 0 deletions
diff --git a/source/pdf/js/pdf-js-none.c b/source/pdf/js/pdf-js-none.c
new file mode 100644
index 00000000..00cc5b54
--- /dev/null
+++ b/source/pdf/js/pdf-js-none.c
@@ -0,0 +1,36 @@
+#include "mupdf/pdf.h"
+
+pdf_js *pdf_new_js(pdf_document *doc)
+{
+ return NULL;
+}
+
+void pdf_js_load_document_level(pdf_js *js)
+{
+}
+
+void pdf_drop_js(pdf_js *js)
+{
+}
+
+void pdf_js_setup_event(pdf_js *js, pdf_js_event *e)
+{
+}
+
+pdf_js_event *pdf_js_get_event(pdf_js *js)
+{
+ return NULL;
+}
+
+void pdf_js_execute(pdf_js *js, char *code)
+{
+}
+
+void pdf_js_execute_count(pdf_js *js, char *code, int count)
+{
+}
+
+int pdf_js_supported(void)
+{
+ return 0;
+}
diff --git a/source/pdf/js/pdf-js.c b/source/pdf/js/pdf-js.c
new file mode 100644
index 00000000..4a2313f1
--- /dev/null
+++ b/source/pdf/js/pdf-js.c
@@ -0,0 +1,919 @@
+#include "mupdf/pdf.h"
+
+struct pdf_js_s
+{
+ pdf_document *doc;
+ pdf_obj *form;
+ pdf_js_event event;
+ pdf_jsimp *imp;
+ pdf_jsimp_type *doctype;
+ pdf_jsimp_type *eventtype;
+ pdf_jsimp_type *fieldtype;
+ pdf_jsimp_type *apptype;
+};
+
+static pdf_jsimp_obj *app_alert(void *jsctx, void *obj, int argc, pdf_jsimp_obj *args[])
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ fz_context *ctx = js->doc->ctx;
+ pdf_jsimp_obj *cMsg_obj = NULL;
+ pdf_jsimp_obj *nIcon_obj = NULL;
+ pdf_jsimp_obj *nType_obj = NULL;
+ pdf_jsimp_obj *cTitle_obj = NULL;
+ pdf_jsimp_obj *nButton_obj = NULL;
+ pdf_alert_event event;
+ int arg_is_obj = 0;
+
+ if (argc < 1 || argc > 6)
+ return NULL;
+
+ event.message = "";
+ event.icon_type = PDF_ALERT_ICON_ERROR;
+ event.button_group_type = PDF_ALERT_BUTTON_GROUP_OK;
+ event.title = "MuPDF";
+ event.check_box_message = NULL;
+ event.button_pressed = 0;
+
+ fz_var(cMsg_obj);
+ fz_var(nIcon_obj);
+ fz_var(nType_obj);
+ fz_var(cTitle_obj);
+ fz_try(ctx)
+ {
+ arg_is_obj = (argc == 1 && pdf_jsimp_to_type(js->imp, args[0]) != JS_TYPE_STRING);
+ if (arg_is_obj)
+ {
+ cMsg_obj = pdf_jsimp_property(js->imp, args[0], "cMsg");
+ nIcon_obj = pdf_jsimp_property(js->imp, args[0], "nIcon");
+ nType_obj = pdf_jsimp_property(js->imp, args[0], "nType");
+ cTitle_obj = pdf_jsimp_property(js->imp, args[0], "cTitle");
+ }
+ else
+ {
+ switch (argc)
+ {
+ case 6:
+ case 5:
+ case 4:
+ cTitle_obj = args[3];
+ case 3:
+ nType_obj = args[2];
+ case 2:
+ nIcon_obj = args[1];
+ case 1:
+ cMsg_obj = args[0];
+ }
+ }
+
+ if (cMsg_obj)
+ event.message = pdf_jsimp_to_string(js->imp, cMsg_obj);
+
+ if (nIcon_obj)
+ event.icon_type = (int)pdf_jsimp_to_number(js->imp, nIcon_obj);
+
+ if (nType_obj)
+ event.button_group_type = (int)pdf_jsimp_to_number(js->imp, nType_obj);
+
+ if (cTitle_obj)
+ event.title = pdf_jsimp_to_string(js->imp, cTitle_obj);
+
+ pdf_event_issue_alert(js->doc, &event);
+ nButton_obj = pdf_jsimp_from_number(js->imp, (double)event.button_pressed);
+ }
+ fz_always(ctx)
+ {
+ if (arg_is_obj)
+ {
+ pdf_jsimp_drop_obj(js->imp, cMsg_obj);
+ pdf_jsimp_drop_obj(js->imp, nIcon_obj);
+ pdf_jsimp_drop_obj(js->imp, nType_obj);
+ pdf_jsimp_drop_obj(js->imp, cTitle_obj);
+ }
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+
+ return nButton_obj;
+}
+
+static pdf_jsimp_obj *app_execDialog(void *jsctx, void *obj, int argc, pdf_jsimp_obj *args[])
+{
+ pdf_js *js = (pdf_js *)jsctx;
+
+ pdf_event_issue_exec_dialog(js->doc);
+
+ return NULL;
+}
+
+static pdf_jsimp_obj *app_execMenuItem(void *jsctx, void *obj, int argc, pdf_jsimp_obj *args[])
+{
+ pdf_js *js = (pdf_js *)jsctx;
+
+ if (argc == 1)
+ pdf_event_issue_exec_menu_item(js->doc, pdf_jsimp_to_string(js->imp, args[0]));
+
+ return NULL;
+}
+
+static pdf_jsimp_obj *app_launchURL(void *jsctx, void *obj, int argc, pdf_jsimp_obj *args[])
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ char *cUrl;
+ int bNewFrame = 0;
+
+ switch (argc)
+ {
+ default:
+ return NULL;
+ case 2:
+ bNewFrame = (int)pdf_jsimp_to_number(js->imp, args[1]);
+ case 1:
+ cUrl = pdf_jsimp_to_string(js->imp, args[0]);
+ }
+
+ pdf_event_issue_launch_url(js->doc, cUrl, bNewFrame);
+
+ return NULL;
+}
+
+static pdf_obj *load_color(fz_context *ctx, pdf_jsimp *imp, pdf_jsimp_obj *val)
+{
+ pdf_obj *col = NULL;
+ pdf_obj *comp = NULL;
+ pdf_jsimp_obj *jscomp = NULL;
+ int i;
+ int n;
+
+ n = pdf_jsimp_array_len(imp, val);
+
+ /* The only legitimate color expressed as an array of length 1
+ * is [T], meaning transparent. Return a NULL object to represent
+ * transparent */
+ if (n <= 1)
+ return NULL;
+
+ col = pdf_new_array(ctx, n-1);
+
+ fz_var(comp);
+ fz_var(jscomp);
+ fz_try(ctx)
+ {
+ for (i = 0; i < n-1; i++)
+ {
+ jscomp = pdf_jsimp_array_item(imp, val, i+1);
+ comp = pdf_new_real(ctx, pdf_jsimp_to_number(imp, jscomp));
+ pdf_array_push(col, comp);
+ pdf_jsimp_drop_obj(imp, jscomp);
+ jscomp = NULL;
+ pdf_drop_obj(comp);
+ comp = NULL;
+ }
+ }
+ fz_catch(ctx)
+ {
+ pdf_jsimp_drop_obj(imp, jscomp);
+ pdf_drop_obj(comp);
+ pdf_drop_obj(col);
+ fz_rethrow(ctx);
+ }
+
+ return col;
+}
+
+static pdf_jsimp_obj *field_buttonSetCaption(void *jsctx, void *obj, int argc, pdf_jsimp_obj *args[])
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ pdf_obj *field = (pdf_obj *)obj;
+ char *name;
+
+ if (argc != 1)
+ return NULL;
+
+ name = pdf_jsimp_to_string(js->imp, args[0]);
+ pdf_field_set_button_caption(js->doc, field, name);
+
+ return NULL;
+}
+
+static pdf_jsimp_obj *field_getName(void *jsctx, void *obj)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ fz_context *ctx = js->doc->ctx;
+ pdf_obj *field = (pdf_obj *)obj;
+ char *name;
+ pdf_jsimp_obj *oname = NULL;
+
+ if (field == NULL)
+ return NULL;
+
+ name = pdf_field_name(js->doc, field);
+ fz_try(ctx)
+ {
+ oname = pdf_jsimp_from_string(js->imp, name);
+ }
+ fz_always(ctx)
+ {
+ fz_free(ctx, name);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+
+ return oname;
+}
+
+static void field_setName(void *jsctx, void *obj, pdf_jsimp_obj *val)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ fz_warn(js->doc->ctx, "Unexpected call to field_setName");
+}
+
+static pdf_jsimp_obj *field_getDisplay(void *jsctx, void *obj)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ pdf_obj *field = (pdf_obj *)obj;
+
+ return field ? pdf_jsimp_from_number(js->imp, (double)pdf_field_display(js->doc, field)) : NULL;
+}
+
+static void field_setDisplay(void *jsctx, void *obj, pdf_jsimp_obj *val)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ fz_context *ctx = js->doc->ctx;
+ pdf_obj *field = (pdf_obj *)obj;
+
+ if (field)
+ pdf_field_set_display(js->doc, field, (int)pdf_jsimp_to_number(js->imp, val));
+}
+
+static pdf_jsimp_obj *field_getFillColor(void *jsctx, void *obj)
+{
+ return NULL;
+}
+
+static void field_setFillColor(void *jsctx, void *obj, pdf_jsimp_obj *val)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ fz_context *ctx = js->doc->ctx;
+ pdf_obj *field = (pdf_obj *)obj;
+ pdf_obj *col;
+
+ if (!field)
+ return;
+
+ col = load_color(js->doc->ctx, js->imp, val);
+ fz_try(ctx)
+ {
+ pdf_field_set_fill_color(js->doc, field, col);
+ }
+ fz_always(ctx)
+ {
+ pdf_drop_obj(col);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+}
+
+static pdf_jsimp_obj *field_getTextColor(void *jsctx, void *obj)
+{
+ return NULL;
+}
+
+static void field_setTextColor(void *jsctx, void *obj, pdf_jsimp_obj *val)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ fz_context *ctx = js->doc->ctx;
+ pdf_obj *field = (pdf_obj *)obj;
+ pdf_obj *col;
+
+ if (!field)
+ return;
+
+ col = load_color(js->doc->ctx, js->imp, val);
+ fz_try(ctx)
+ {
+ pdf_field_set_text_color(js->doc, field, col);
+ }
+ fz_always(ctx)
+ {
+ pdf_drop_obj(col);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+}
+
+static pdf_jsimp_obj *field_getBorderStyle(void *jsctx, void *obj)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ pdf_obj *field = (pdf_obj *)obj;
+
+ return field ? pdf_jsimp_from_string(js->imp, pdf_field_border_style(js->doc, field)) : NULL;
+}
+
+static void field_setBorderStyle(void *jsctx, void *obj, pdf_jsimp_obj *val)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ pdf_obj *field = (pdf_obj *)obj;
+
+ if (field)
+ pdf_field_set_border_style(js->doc, field, pdf_jsimp_to_string(js->imp, val));
+}
+
+static pdf_jsimp_obj *field_getValue(void *jsctx, void *obj)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ pdf_obj *field = (pdf_obj *)obj;
+ char *fval;
+
+ if (!field)
+ return NULL;
+
+ fval = pdf_field_value(js->doc, field);
+ return pdf_jsimp_from_string(js->imp, fval?fval:"");
+}
+
+static void field_setValue(void *jsctx, void *obj, pdf_jsimp_obj *val)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ pdf_obj *field = (pdf_obj *)obj;
+
+ if (field)
+ (void)pdf_field_set_value(js->doc, field, pdf_jsimp_to_string(js->imp, val));
+}
+
+static pdf_jsimp_obj *event_getTarget(void *jsctx, void *obj)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+
+ return pdf_jsimp_new_obj(js->imp, js->fieldtype, js->event.target);
+}
+
+static void event_setTarget(void *jsctx, void *obj, pdf_jsimp_obj *val)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ fz_warn(js->doc->ctx, "Unexpected call to event_setTarget");
+}
+
+static pdf_jsimp_obj *event_getValue(void *jsctx, void *obj)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ char *v = js->event.value;
+
+ return pdf_jsimp_from_string(js->imp, v?v:"");
+}
+
+static void event_setValue(void *jsctx, void *obj, pdf_jsimp_obj *val)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ fz_context *ctx = js->doc->ctx;
+ fz_free(ctx, js->event.value);
+ js->event.value = NULL;
+ js->event.value = fz_strdup(ctx, pdf_jsimp_to_string(js->imp, val));
+}
+
+static pdf_jsimp_obj *event_getWillCommit(void *jsctx, void *obj)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+
+ return pdf_jsimp_from_number(js->imp, 1.0);
+}
+
+static void event_setWillCommit(void *jsctx, void *obj, pdf_jsimp_obj *val)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ fz_warn(js->doc->ctx, "Unexpected call to event_setWillCommit");
+}
+
+static pdf_jsimp_obj *event_getRC(void *jsctx, void *obj)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+
+ return pdf_jsimp_from_number(js->imp, (double)js->event.rc);
+}
+
+static void event_setRC(void *jsctx, void *obj, pdf_jsimp_obj *val)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+
+ js->event.rc = (int)pdf_jsimp_to_number(js->imp, val);
+}
+
+static pdf_jsimp_obj *doc_getEvent(void *jsctx, void *obj)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+
+ return pdf_jsimp_new_obj(js->imp, js->eventtype, &js->event);
+}
+
+static void doc_setEvent(void *jsctx, void *obj, pdf_jsimp_obj *val)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ fz_warn(js->doc->ctx, "Unexpected call to doc_setEvent");
+}
+
+static pdf_jsimp_obj *doc_getApp(void *jsctx, void *obj)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+
+ return pdf_jsimp_new_obj(js->imp, js->apptype, NULL);
+}
+
+static void doc_setApp(void *jsctx, void *obj, pdf_jsimp_obj *val)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ fz_warn(js->doc->ctx, "Unexpected call to doc_setApp");
+}
+
+static char *utf8_to_pdf(fz_context *ctx, char *utf8)
+{
+ char *pdf = fz_malloc(ctx, strlen(utf8)+1);
+ int i = 0;
+ unsigned char c;
+
+ while ((c = *utf8) != 0)
+ {
+ if ((c & 0x80) == 0 && pdf_doc_encoding[c] == c)
+ {
+ pdf[i++] = c;
+ utf8++ ;
+ }
+ else
+ {
+ int rune;
+ int j;
+
+ utf8 += fz_chartorune(&rune, utf8);
+
+ for (j = 0; j < sizeof(pdf_doc_encoding) && pdf_doc_encoding[j] != rune; j++)
+ ;
+
+ if (j < sizeof(pdf_doc_encoding))
+ pdf[i++] = j;
+ }
+ }
+
+ pdf[i] = 0;
+
+ return pdf;
+}
+
+static pdf_jsimp_obj *doc_getField(void *jsctx, void *obj, int argc, pdf_jsimp_obj *args[])
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ fz_context *ctx = js->doc->ctx;
+ pdf_obj *dict = NULL;
+ char *utf8;
+ char *name = NULL;
+
+ if (argc != 1)
+ return NULL;
+
+ fz_var(dict);
+ fz_var(name);
+ fz_try(ctx)
+ {
+ utf8 = pdf_jsimp_to_string(js->imp, args[0]);
+
+ if (utf8)
+ {
+ name = utf8_to_pdf(ctx, utf8);
+ dict = pdf_lookup_field(js->form, name);
+ }
+ }
+ fz_always(ctx)
+ {
+ fz_free(ctx, name);
+ }
+ fz_catch(ctx)
+ {
+ /* FIXME: TryLater ? */
+ fz_warn(ctx, "doc_getField failed: %s", fz_caught_message(ctx));
+ dict = NULL;
+ }
+
+ return dict ? pdf_jsimp_new_obj(js->imp, js->fieldtype, dict) : NULL;
+}
+
+static void reset_field(pdf_js *js, pdf_jsimp_obj *item)
+{
+ fz_context *ctx = js->doc->ctx;
+ char *name = NULL;
+ char *utf8 = pdf_jsimp_to_string(js->imp, item);
+
+ if (utf8)
+ {
+ pdf_obj *field;
+
+ fz_var(name);
+ fz_try(ctx)
+ {
+ name = utf8_to_pdf(ctx, utf8);
+ field = pdf_lookup_field(js->form, name);
+ if (field)
+ pdf_field_reset(js->doc, field);
+ }
+ fz_always(ctx)
+ {
+ fz_free(ctx, name);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+ }
+}
+
+static pdf_jsimp_obj *doc_resetForm(void *jsctx, void *obj, int argc, pdf_jsimp_obj *args[])
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ fz_context *ctx = js->doc->ctx;
+ pdf_jsimp_obj *arr = NULL;
+ pdf_jsimp_obj *elem = NULL;
+
+ switch (argc)
+ {
+ case 0:
+ break;
+ case 1:
+ switch (pdf_jsimp_to_type(js->imp, args[0]))
+ {
+ case JS_TYPE_NULL:
+ break;
+ case JS_TYPE_ARRAY:
+ arr = args[0];
+ break;
+ case JS_TYPE_STRING:
+ elem = args[0];
+ break;
+ default:
+ return NULL;
+ }
+ break;
+ default:
+ return NULL;
+ }
+
+ fz_try(ctx)
+ {
+ if(arr)
+ {
+ /* An array of fields has been passed in. Call
+ * pdf_reset_field on each */
+ int i, n = pdf_jsimp_array_len(js->imp, arr);
+
+ for (i = 0; i < n; i++)
+ {
+ pdf_jsimp_obj *item = pdf_jsimp_array_item(js->imp, arr, i);
+
+ if (item)
+ reset_field(js, item);
+
+ }
+ }
+ else if (elem)
+ {
+ reset_field(js, elem);
+ }
+ else
+ {
+ /* No argument or null passed in means reset all. */
+ int i, n = pdf_array_len(js->form);
+
+ for (i = 0; i < n; i++)
+ pdf_field_reset(js->doc, pdf_array_get(js->form, i));
+ }
+ }
+ fz_catch(ctx)
+ {
+ fz_warn(ctx, "doc_resetForm failed: %s", fz_caught_message(ctx));
+ }
+
+ return NULL;
+}
+
+static pdf_jsimp_obj *doc_print(void *jsctx, void *obj, int argc, pdf_jsimp_obj *args[])
+{
+ pdf_js *js = (pdf_js *)jsctx;
+
+ pdf_event_issue_print(js->doc);
+
+ return NULL;
+}
+
+static pdf_jsimp_obj *doc_mailDoc(void *jsctx, void *obj, int argc, pdf_jsimp_obj *args[])
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ fz_context *ctx = js->doc->ctx;
+ pdf_jsimp_obj *bUI_obj = NULL;
+ pdf_jsimp_obj *cTo_obj = NULL;
+ pdf_jsimp_obj *cCc_obj = NULL;
+ pdf_jsimp_obj *cBcc_obj = NULL;
+ pdf_jsimp_obj *cSubject_obj = NULL;
+ pdf_jsimp_obj *cMessage_obj = NULL;
+ pdf_mail_doc_event event;
+ int arg_is_obj = 0;
+
+ if (argc < 1 || argc > 6)
+ return NULL;
+
+ event.ask_user = 1;
+ event.to = "";
+ event.cc = "";
+ event.bcc = "";
+ event.subject = "";
+ event.message = "";
+
+ fz_var(bUI_obj);
+ fz_var(cTo_obj);
+ fz_var(cCc_obj);
+ fz_var(cBcc_obj);
+ fz_var(cSubject_obj);
+ fz_var(cMessage_obj);
+ fz_try(ctx)
+ {
+ arg_is_obj = (argc == 1 && pdf_jsimp_to_type(js->imp, args[0]) != JS_TYPE_BOOLEAN);
+ if (arg_is_obj)
+ {
+ bUI_obj = pdf_jsimp_property(js->imp, args[0], "bUI");
+ cTo_obj = pdf_jsimp_property(js->imp, args[0], "cTo");
+ cCc_obj = pdf_jsimp_property(js->imp, args[0], "cCc");
+ cBcc_obj = pdf_jsimp_property(js->imp, args[0], "cBcc");
+ cSubject_obj = pdf_jsimp_property(js->imp, args[0], "cSubject");
+ cMessage_obj = pdf_jsimp_property(js->imp, args[0], "cMessage");
+ }
+ else
+ {
+ switch (argc)
+ {
+ case 6:
+ cMessage_obj = args[5];
+ case 5:
+ cSubject_obj = args[4];
+ case 4:
+ cBcc_obj = args[3];
+ case 3:
+ cCc_obj = args[2];
+ case 2:
+ cTo_obj = args[1];
+ case 1:
+ bUI_obj = args[0];
+ }
+ }
+
+ if (bUI_obj)
+ event.ask_user = (int)pdf_jsimp_to_number(js->imp, bUI_obj);
+
+ if (cTo_obj)
+ event.to = pdf_jsimp_to_string(js->imp, cTo_obj);
+
+ if (cCc_obj)
+ event.cc = pdf_jsimp_to_string(js->imp, cCc_obj);
+
+ if (cBcc_obj)
+ event.bcc = pdf_jsimp_to_string(js->imp, cBcc_obj);
+
+ if (cSubject_obj)
+ event.subject = pdf_jsimp_to_string(js->imp, cSubject_obj);
+
+ if (cMessage_obj)
+ event.message = pdf_jsimp_to_string(js->imp, cMessage_obj);
+
+ pdf_event_issue_mail_doc(js->doc, &event);
+ }
+ fz_always(ctx)
+ {
+ if (arg_is_obj)
+ {
+ pdf_jsimp_drop_obj(js->imp, bUI_obj);
+ pdf_jsimp_drop_obj(js->imp, cTo_obj);
+ pdf_jsimp_drop_obj(js->imp, cCc_obj);
+ pdf_jsimp_drop_obj(js->imp, cBcc_obj);
+ pdf_jsimp_drop_obj(js->imp, cSubject_obj);
+ pdf_jsimp_drop_obj(js->imp, cMessage_obj);
+ }
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+
+ return NULL;
+}
+
+static void declare_dom(pdf_js *js)
+{
+ pdf_jsimp *imp = js->imp;
+
+ /* Create the document type */
+ js->doctype = pdf_jsimp_new_type(imp, NULL);
+ pdf_jsimp_addmethod(imp, js->doctype, "getField", doc_getField);
+ pdf_jsimp_addmethod(imp, js->doctype, "resetForm", doc_resetForm);
+ pdf_jsimp_addmethod(imp, js->doctype, "print", doc_print);
+ pdf_jsimp_addmethod(imp, js->doctype, "mailDoc", doc_mailDoc);
+ pdf_jsimp_addproperty(imp, js->doctype, "event", doc_getEvent, doc_setEvent);
+ pdf_jsimp_addproperty(imp, js->doctype, "app", doc_getApp, doc_setApp);
+
+ /* Create the event type */
+ js->eventtype = pdf_jsimp_new_type(imp, NULL);
+ pdf_jsimp_addproperty(imp, js->eventtype, "target", event_getTarget, event_setTarget);
+ pdf_jsimp_addproperty(imp, js->eventtype, "value", event_getValue, event_setValue);
+ pdf_jsimp_addproperty(imp, js->eventtype, "willCommit", event_getWillCommit, event_setWillCommit);
+ pdf_jsimp_addproperty(imp, js->eventtype, "rc", event_getRC, event_setRC);
+
+ /* Create the field type */
+ js->fieldtype = pdf_jsimp_new_type(imp, NULL);
+ pdf_jsimp_addproperty(imp, js->fieldtype, "value", field_getValue, field_setValue);
+ pdf_jsimp_addproperty(imp, js->fieldtype, "borderStyle", field_getBorderStyle, field_setBorderStyle);
+ pdf_jsimp_addproperty(imp, js->fieldtype, "textColor", field_getTextColor, field_setTextColor);
+ pdf_jsimp_addproperty(imp, js->fieldtype, "fillColor", field_getFillColor, field_setFillColor);
+ pdf_jsimp_addproperty(imp, js->fieldtype, "display", field_getDisplay, field_setDisplay);
+ pdf_jsimp_addproperty(imp, js->fieldtype, "name", field_getName, field_setName);
+ pdf_jsimp_addmethod(imp, js->fieldtype, "buttonSetCaption", field_buttonSetCaption);
+
+ /* Create the app type */
+ js->apptype = pdf_jsimp_new_type(imp, NULL);
+ pdf_jsimp_addmethod(imp, js->apptype, "alert", app_alert);
+ pdf_jsimp_addmethod(imp, js->apptype, "execDialog", app_execDialog);
+ pdf_jsimp_addmethod(imp, js->apptype, "execMenuItem", app_execMenuItem);
+ pdf_jsimp_addmethod(imp, js->apptype, "launchURL", app_launchURL);
+
+ /* Create the document object and tell the engine to use */
+ pdf_jsimp_set_global_type(js->imp, js->doctype);
+}
+
+static void preload_helpers(pdf_js *js)
+{
+ /* When testing on the cluster, redefine the Date object
+ * to use a fixed date */
+#ifdef CLUSTER
+ pdf_jsimp_execute(js->imp,
+"var MuPDFOldDate = Date\n"
+"Date = function() { return new MuPDFOldDate(1979,5,15); }\n"
+ );
+#endif
+
+ pdf_jsimp_execute(js->imp,
+#include "gen_js_util.h"
+ );
+}
+
+pdf_js *pdf_new_js(pdf_document *doc)
+{
+ fz_context *ctx = doc->ctx;
+ pdf_js *js = NULL;
+
+ fz_var(js);
+ fz_try(ctx)
+ {
+ pdf_obj *root, *acroform;
+
+ js = fz_malloc_struct(ctx, pdf_js);
+ js->doc = doc;
+
+ /* Find the form array */
+ root = pdf_dict_gets(pdf_trailer(doc), "Root");
+ acroform = pdf_dict_gets(root, "AcroForm");
+ js->form = pdf_dict_gets(acroform, "Fields");
+
+ /* Initialise the javascript engine, passing the main context
+ * for use in memory allocation and exception handling. Also
+ * pass our js context, for it to pass back to us. */
+ js->imp = pdf_new_jsimp(ctx, js);
+ declare_dom(js);
+
+ preload_helpers(js);
+ }
+ fz_catch(ctx)
+ {
+ pdf_drop_js(js);
+ js = NULL;
+ }
+
+ return js;
+}
+
+void pdf_js_load_document_level(pdf_js *js)
+{
+ pdf_document *doc = js->doc;
+ fz_context *ctx = doc->ctx;
+ pdf_obj *javascript = NULL;
+ char *codebuf = NULL;
+
+ fz_var(javascript);
+ fz_var(codebuf);
+ fz_try(ctx)
+ {
+ int len, i;
+
+ javascript = pdf_load_name_tree(doc, "JavaScript");
+ len = pdf_dict_len(javascript);
+
+ for (i = 0; i < len; i++)
+ {
+ pdf_obj *fragment = pdf_dict_get_val(javascript, i);
+ pdf_obj *code = pdf_dict_gets(fragment, "JS");
+
+ fz_var(codebuf);
+ fz_try(ctx)
+ {
+ codebuf = pdf_to_utf8(doc, code);
+ pdf_jsimp_execute(js->imp, codebuf);
+ }
+ fz_always(ctx)
+ {
+ fz_free(ctx, codebuf);
+ codebuf = NULL;
+ }
+ fz_catch(ctx)
+ {
+ /* FIXME: TryLater ? */
+ fz_warn(ctx, "Warning: %s", fz_caught_message(ctx));
+ }
+ }
+ }
+ fz_always(ctx)
+ {
+ pdf_drop_obj(javascript);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+}
+
+void pdf_drop_js(pdf_js *js)
+{
+ if (js)
+ {
+ fz_context *ctx = js->doc->ctx;
+ fz_free(ctx, js->event.value);
+ pdf_jsimp_drop_type(js->imp, js->apptype);
+ pdf_jsimp_drop_type(js->imp, js->fieldtype);
+ pdf_jsimp_drop_type(js->imp, js->doctype);
+ pdf_drop_jsimp(js->imp);
+ fz_free(ctx, js);
+ }
+}
+
+void pdf_js_setup_event(pdf_js *js, pdf_js_event *e)
+{
+ if (js)
+ {
+ fz_context *ctx = js->doc->ctx;
+ char *ev = e->value ? e->value : "";
+ char *v = fz_strdup(ctx, ev);
+
+ fz_free(ctx, js->event.value);
+ js->event.value = v;
+
+ js->event.target = e->target;
+ js->event.rc = 1;
+ }
+}
+
+pdf_js_event *pdf_js_get_event(pdf_js *js)
+{
+ return js ? &js->event : NULL;
+}
+
+void pdf_js_execute(pdf_js *js, char *code)
+{
+ if (js)
+ {
+ fz_context *ctx = js->doc->ctx;
+ fz_try(ctx)
+ {
+ pdf_jsimp_execute(js->imp, code);
+ }
+ fz_catch(ctx)
+ {
+ }
+ }
+}
+
+void pdf_js_execute_count(pdf_js *js, char *code, int count)
+{
+ if (js)
+ {
+ fz_context *ctx = js->doc->ctx;
+ fz_try(ctx)
+ {
+ pdf_jsimp_execute_count(js->imp, code, count);
+ }
+ fz_catch(ctx)
+ {
+ }
+ }
+}
+
+int pdf_js_supported(void)
+{
+ return 1;
+}
diff --git a/source/pdf/js/pdf-jsimp-cpp.c b/source/pdf/js/pdf-jsimp-cpp.c
new file mode 100644
index 00000000..7e8031a6
--- /dev/null
+++ b/source/pdf/js/pdf-jsimp-cpp.c
@@ -0,0 +1,225 @@
+/* This file contains wrapper functions for pdf_jsimp functions implemented
+ * in C++, from which calls to fz_throw aren't safe. The C++ versions
+ * return errors explicitly, and these wrappers then throw them. */
+
+#include "mupdf/pdf.h"
+#include "pdf-jsimp-cpp.h"
+
+pdf_jsimp *pdf_new_jsimp(fz_context *ctx, void *jsctx)
+{
+ pdf_jsimp *jsi = NULL;
+ const char *err = pdf_new_jsimp_cpp(ctx, jsctx, &jsi);
+ if (err != NULL)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "%s", err);
+
+ return jsi;
+}
+
+void pdf_drop_jsimp(pdf_jsimp *imp)
+{
+ if (imp)
+ {
+ fz_context *ctx = pdf_jsimp_ctx_cpp(imp);
+ const char *err = pdf_drop_jsimp_cpp(imp);
+ if (err != NULL)
+ fz_warn(ctx, "%s", err);
+ }
+}
+
+pdf_jsimp_type *pdf_jsimp_new_type(pdf_jsimp *imp, pdf_jsimp_dtr *dtr)
+{
+ pdf_jsimp_type *type = NULL;
+ const char *err = pdf_jsimp_new_type_cpp(imp, dtr, &type);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), FZ_ERROR_GENERIC, "%s", err);
+
+ return type;
+}
+
+void pdf_jsimp_drop_type(pdf_jsimp *imp, pdf_jsimp_type *type)
+{
+ const char *err = pdf_jsimp_drop_type_cpp(imp, type);
+ if (err != NULL)
+ fz_warn(pdf_jsimp_ctx_cpp(imp), "%s", err);
+}
+
+void pdf_jsimp_addmethod(pdf_jsimp *imp, pdf_jsimp_type *type, char *name, pdf_jsimp_method *meth)
+{
+ const char *err = pdf_jsimp_addmethod_cpp(imp, type, name, meth);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), FZ_ERROR_GENERIC, "%s", err);
+}
+
+void pdf_jsimp_addproperty(pdf_jsimp *imp, pdf_jsimp_type *type, char *name, pdf_jsimp_getter *get, pdf_jsimp_setter *set)
+{
+ const char *err = pdf_jsimp_addproperty_cpp(imp, type, name, get, set);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), FZ_ERROR_GENERIC, "%s", err);
+}
+
+void pdf_jsimp_set_global_type(pdf_jsimp *imp, pdf_jsimp_type *type)
+{
+ const char *err = pdf_jsimp_set_global_type_cpp(imp, type);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), FZ_ERROR_GENERIC, "%s", err);
+}
+
+pdf_jsimp_obj *pdf_jsimp_new_obj(pdf_jsimp *imp, pdf_jsimp_type *type, void *natobj)
+{
+ pdf_jsimp_obj *obj = NULL;
+ const char *err = pdf_jsimp_new_obj_cpp(imp, type, natobj, &obj);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), FZ_ERROR_GENERIC, "%s", err);
+
+ return obj;
+}
+
+void pdf_jsimp_drop_obj(pdf_jsimp *imp, pdf_jsimp_obj *obj)
+{
+ const char *err = pdf_jsimp_drop_obj_cpp(imp, obj);
+ if (err != NULL)
+ fz_warn(pdf_jsimp_ctx_cpp(imp), "%s", err);
+}
+
+int pdf_jsimp_to_type(pdf_jsimp *imp, pdf_jsimp_obj *obj)
+{
+ int type = 0;
+ const char *err = pdf_jsimp_to_type_cpp(imp, obj, &type);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), FZ_ERROR_GENERIC, "%s", err);
+
+ return type;
+}
+
+pdf_jsimp_obj *pdf_jsimp_from_string(pdf_jsimp *imp, char *str)
+{
+ pdf_jsimp_obj *obj = NULL;
+ const char *err = pdf_jsimp_from_string_cpp(imp, str, &obj);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), FZ_ERROR_GENERIC, "%s", err);
+
+ return obj;
+}
+
+char *pdf_jsimp_to_string(pdf_jsimp *imp, pdf_jsimp_obj *obj)
+{
+ char *str = NULL;
+ const char *err = pdf_jsimp_to_string_cpp(imp, obj, &str);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), FZ_ERROR_GENERIC, "%s", err);
+
+ return str;
+}
+
+pdf_jsimp_obj *pdf_jsimp_from_number(pdf_jsimp *imp, double num)
+{
+ pdf_jsimp_obj *obj = NULL;
+ const char *err = pdf_jsimp_from_number_cpp(imp, num, &obj);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), FZ_ERROR_GENERIC, "%s", err);
+
+ return obj;
+}
+
+double pdf_jsimp_to_number(pdf_jsimp *imp, pdf_jsimp_obj *obj)
+{
+ double num;
+ const char *err = pdf_jsimp_to_number_cpp(imp, obj, &num);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), FZ_ERROR_GENERIC, "%s", err);
+
+ return num;
+}
+
+int pdf_jsimp_array_len(pdf_jsimp *imp, pdf_jsimp_obj *obj)
+{
+ int len = 0;
+ const char *err = pdf_jsimp_array_len_cpp(imp, obj, &len);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), FZ_ERROR_GENERIC, "%s", err);
+
+ return len;
+}
+
+pdf_jsimp_obj *pdf_jsimp_array_item(pdf_jsimp *imp, pdf_jsimp_obj *obj, int i)
+{
+ pdf_jsimp_obj *item = NULL;
+ const char *err = pdf_jsimp_array_item_cpp(imp, obj, i, &item);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), FZ_ERROR_GENERIC, "%s", err);
+
+ return item;
+}
+
+pdf_jsimp_obj *pdf_jsimp_property(pdf_jsimp *imp, pdf_jsimp_obj *obj, char *prop)
+{
+ pdf_jsimp_obj *pobj = NULL;
+ const char *err = pdf_jsimp_property_cpp(imp, obj, prop, &pobj);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), FZ_ERROR_GENERIC, "%s", err);
+
+ return pobj;
+}
+
+void pdf_jsimp_execute(pdf_jsimp *imp, char *code)
+{
+ const char *err = pdf_jsimp_execute_cpp(imp, code);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), FZ_ERROR_GENERIC, "%s", err);
+}
+
+void pdf_jsimp_execute_count(pdf_jsimp *imp, char *code, int count)
+{
+ const char *err = pdf_jsimp_execute_count_cpp(imp, code, count);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), FZ_ERROR_GENERIC, "%s", err);
+}
+pdf_jsimp_obj *pdf_jsimp_call_method(pdf_jsimp *imp, pdf_jsimp_method *meth, void *jsctx, void *obj, int argc, pdf_jsimp_obj *args[])
+{
+ fz_context *ctx = pdf_jsimp_ctx_cpp(imp);
+ pdf_jsimp_obj *res;
+
+ fz_try(ctx)
+ {
+ res = meth(jsctx, obj, argc, args);
+ }
+ fz_catch(ctx)
+ {
+ res = NULL;
+ fz_warn(ctx, "%s", fz_caught_message(ctx));
+ }
+
+ return res;
+}
+
+pdf_jsimp_obj *pdf_jsimp_call_getter(pdf_jsimp *imp, pdf_jsimp_getter *get, void *jsctx, void *obj)
+{
+ fz_context *ctx = pdf_jsimp_ctx_cpp(imp);
+ pdf_jsimp_obj *res;
+
+ fz_try(ctx)
+ {
+ res = get(jsctx, obj);
+ }
+ fz_catch(ctx)
+ {
+ res = NULL;
+ fz_warn(ctx, "%s", fz_caught_message(ctx));
+ }
+
+ return res;
+}
+
+void pdf_jsimp_call_setter(pdf_jsimp *imp, pdf_jsimp_setter *set, void *jsctx, void *obj, pdf_jsimp_obj *val)
+{
+ fz_context *ctx = pdf_jsimp_ctx_cpp(imp);
+
+ fz_try(ctx)
+ {
+ set(jsctx, obj, val);
+ }
+ fz_catch(ctx)
+ {
+ fz_warn(ctx, "%s", fz_caught_message(ctx));
+ }
+}
diff --git a/source/pdf/js/pdf-jsimp-cpp.h b/source/pdf/js/pdf-jsimp-cpp.h
new file mode 100644
index 00000000..83af1d23
--- /dev/null
+++ b/source/pdf/js/pdf-jsimp-cpp.h
@@ -0,0 +1,29 @@
+/* C++ version of the pdf_jsimp api. C++ cannot safely call fz_throw,
+ * so C++ implementations return explicit errors in char * form. */
+
+fz_context *pdf_jsimp_ctx_cpp(pdf_jsimp *imp);
+const char *pdf_new_jsimp_cpp(fz_context *ctx, void *jsctx, pdf_jsimp **imp);
+const char *pdf_drop_jsimp_cpp(pdf_jsimp *imp);
+const char *pdf_jsimp_new_type_cpp(pdf_jsimp *imp, pdf_jsimp_dtr *dtr, pdf_jsimp_type **type);
+const char *pdf_jsimp_drop_type_cpp(pdf_jsimp *imp, pdf_jsimp_type *type);
+const char *pdf_jsimp_addmethod_cpp(pdf_jsimp *imp, pdf_jsimp_type *type, char *name, pdf_jsimp_method *meth);
+const char *pdf_jsimp_addproperty_cpp(pdf_jsimp *imp, pdf_jsimp_type *type, char *name, pdf_jsimp_getter *get, pdf_jsimp_setter *set);
+const char *pdf_jsimp_set_global_type_cpp(pdf_jsimp *imp, pdf_jsimp_type *type);
+const char *pdf_jsimp_new_obj_cpp(pdf_jsimp *imp, pdf_jsimp_type *type, void *natobj, pdf_jsimp_obj **obj);
+const char *pdf_jsimp_drop_obj_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj);
+const char *pdf_jsimp_to_type_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, int *type);
+const char *pdf_jsimp_from_string_cpp(pdf_jsimp *imp, char *str, pdf_jsimp_obj **obj);
+const char *pdf_jsimp_to_string_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, char **str);
+const char *pdf_jsimp_from_number_cpp(pdf_jsimp *imp, double num, pdf_jsimp_obj **obj);
+const char *pdf_jsimp_to_number_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, double *num);
+const char *pdf_jsimp_array_len_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, int *len);
+const char *pdf_jsimp_array_item_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, int i, pdf_jsimp_obj **item);
+const char *pdf_jsimp_property_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, char *prop, pdf_jsimp_obj **pobj);
+const char *pdf_jsimp_execute_cpp(pdf_jsimp *imp, char *code);
+const char *pdf_jsimp_execute_count_cpp(pdf_jsimp *imp, char *code, int count);
+
+/* Also when calling back into mupdf, all exceptions must be caught. The functions bellow
+ * wrap these calls */
+pdf_jsimp_obj *pdf_jsimp_call_method(pdf_jsimp *imp, pdf_jsimp_method *meth, void *jsctx, void *obj, int argc, pdf_jsimp_obj *args[]);
+pdf_jsimp_obj *pdf_jsimp_call_getter(pdf_jsimp *imp, pdf_jsimp_getter *get, void *jsctx, void *obj);
+void pdf_jsimp_call_setter(pdf_jsimp *imp, pdf_jsimp_setter *set, void *jsctx, void *obj, pdf_jsimp_obj *val);
diff --git a/source/pdf/js/pdf-jsimp-v8.cpp b/source/pdf/js/pdf-jsimp-v8.cpp
new file mode 100644
index 00000000..f3894b6b
--- /dev/null
+++ b/source/pdf/js/pdf-jsimp-v8.cpp
@@ -0,0 +1,476 @@
+/*
+ * This is a dummy JavaScript engine. It cheats by recognising the specific
+ * strings in calc.pdf, and hence will work only for that file. It is for
+ * testing only.
+ */
+
+extern "C" {
+#include "mupdf/fitz.h"
+#include "mupdf/pdf.h"
+#include "pdf-jsimp-cpp.h"
+}
+
+#include <vector>
+#include <set>
+#include <v8.h>
+
+using namespace v8;
+using namespace std;
+
+struct PDFJSImp;
+
+/* Object we pass to FunctionTemplate::New, which v8 passes back to us in
+ * callMethod, allowing us to call our client's, passed-in method. */
+struct PDFJSImpMethod
+{
+ PDFJSImp *imp;
+ pdf_jsimp_method *meth;
+
+ PDFJSImpMethod(PDFJSImp *imp, pdf_jsimp_method *meth) : imp(imp), meth(meth) {}
+};
+
+/* Object we pass to ObjectTemplate::SetAccessor, which v8 passes back to us in
+ * setProp and getProp, allowing us to call our client's, passed-in set/get methods. */
+struct PDFJSImpProperty
+{
+ PDFJSImp *imp;
+ pdf_jsimp_getter *get;
+ pdf_jsimp_setter *set;
+
+ PDFJSImpProperty(PDFJSImp *imp, pdf_jsimp_getter *get, pdf_jsimp_setter *set) : imp(imp), get(get), set(set) {}
+};
+
+/* Internal representation of the pdf_jsimp_type object */
+struct PDFJSImpType
+{
+ PDFJSImp *imp;
+ Persistent<ObjectTemplate> templ;
+ pdf_jsimp_dtr *dtr;
+ vector<PDFJSImpMethod *> methods;
+ vector<PDFJSImpProperty *> properties;
+
+ PDFJSImpType(PDFJSImp *imp, pdf_jsimp_dtr *dtr): imp(imp), dtr(dtr)
+ {
+ HandleScope scope;
+ templ = Persistent<ObjectTemplate>::New(ObjectTemplate::New());
+ templ->SetInternalFieldCount(1);
+ }
+
+ ~PDFJSImpType()
+ {
+ vector<PDFJSImpMethod *>::iterator mit;
+ for (mit = methods.begin(); mit < methods.end(); mit++)
+ delete *mit;
+
+ vector<PDFJSImpProperty *>::iterator pit;
+ for (pit = properties.begin(); pit < properties.end(); pit++)
+ delete *pit;
+
+ templ.Dispose();
+ }
+};
+
+/* Info via which we destroy the client side part of objects that
+ * v8 garbage collects */
+struct PDFJSImpGCObj
+{
+ Persistent<Object> pobj;
+ PDFJSImpType *type;
+
+ PDFJSImpGCObj(Handle<Object> obj, PDFJSImpType *type): type(type)
+ {
+ pobj = Persistent<Object>::New(obj);
+ }
+
+ ~PDFJSImpGCObj()
+ {
+ pobj.Dispose();
+ }
+};
+
+/* Internal representation of the pdf_jsimp object */
+struct PDFJSImp
+{
+ fz_context *ctx;
+ void *jsctx;
+ Persistent<Context> context;
+ vector<PDFJSImpType *> types;
+ set<PDFJSImpGCObj *> gclist;
+
+ PDFJSImp(fz_context *ctx, void *jsctx) : ctx(ctx), jsctx(jsctx)
+ {
+ HandleScope scope;
+ context = Persistent<Context>::New(Context::New());
+ }
+
+ ~PDFJSImp()
+ {
+ HandleScope scope;
+ /* Tell v8 our context will not be used again */
+ context.Dispose();
+
+ /* Unlink and destroy all the objects that v8 has yet to gc */
+ set<PDFJSImpGCObj *>::iterator oit;
+ for (oit = gclist.begin(); oit != gclist.end(); oit++)
+ {
+ (*oit)->pobj.ClearWeak(); /* So that gcCallback wont get called */
+ PDFJSImpType *vType = (*oit)->type;
+ Local<External> owrap = Local<External>::Cast((*oit)->pobj->GetInternalField(0));
+ vType->dtr(vType->imp->jsctx, owrap->Value());
+ delete *oit;
+ }
+
+ vector<PDFJSImpType *>::iterator it;
+ for (it = types.begin(); it < types.end(); it++)
+ delete *it;
+ }
+};
+
+/* Internal representation of the pdf_jsimp_obj object */
+class PDFJSImpObject
+{
+ Persistent<Value> pobj;
+ String::Utf8Value *utf8;
+
+public:
+ PDFJSImpObject(Handle<Value> obj): utf8(NULL)
+ {
+ pobj = Persistent<Value>::New(obj);
+ }
+
+ PDFJSImpObject(const char *str): utf8(NULL)
+ {
+ pobj = Persistent<Value>::New(String::New(str));
+ }
+
+ PDFJSImpObject(double num): utf8(NULL)
+ {
+ pobj = Persistent<Value>::New(Number::New(num));
+ }
+
+ ~PDFJSImpObject()
+ {
+ delete utf8;
+ pobj.Dispose();
+ }
+
+ int type()
+ {
+ if (pobj->IsNull())
+ return JS_TYPE_NULL;
+ else if (pobj->IsString() || pobj->IsStringObject())
+ return JS_TYPE_STRING;
+ else if (pobj->IsNumber() || pobj->IsNumberObject())
+ return JS_TYPE_NUMBER;
+ else if (pobj->IsArray())
+ return JS_TYPE_ARRAY;
+ else if (pobj->IsBoolean() || pobj->IsBooleanObject())
+ return JS_TYPE_BOOLEAN;
+ else
+ return JS_TYPE_UNKNOWN;
+ }
+
+ char *toString()
+ {
+ delete utf8;
+ utf8 = new String::Utf8Value(pobj);
+ return **utf8;
+ }
+
+ double toNumber()
+ {
+ return pobj->NumberValue();
+ }
+
+ Handle<Value> toValue()
+ {
+ return pobj;
+ }
+};
+
+extern "C" fz_context *pdf_jsimp_ctx_cpp(pdf_jsimp *imp)
+{
+ return reinterpret_cast<PDFJSImp *>(imp)->ctx;
+}
+
+extern "C" const char *pdf_new_jsimp_cpp(fz_context *ctx, void *jsctx, pdf_jsimp **imp)
+{
+ Locker lock;
+ *imp = reinterpret_cast<pdf_jsimp *>(new PDFJSImp(ctx, jsctx));
+
+ return NULL;
+}
+
+extern "C" const char *pdf_drop_jsimp_cpp(pdf_jsimp *imp)
+{
+ Locker lock;
+ delete reinterpret_cast<PDFJSImp *>(imp);
+ return NULL;
+}
+
+extern "C" const char *pdf_jsimp_new_type_cpp(pdf_jsimp *imp, pdf_jsimp_dtr *dtr, pdf_jsimp_type **type)
+{
+ Locker lock;
+ PDFJSImp *vImp = reinterpret_cast<PDFJSImp *>(imp);
+ PDFJSImpType *vType = new PDFJSImpType(vImp, dtr);
+ vImp->types.push_back(vType);
+ *type = reinterpret_cast<pdf_jsimp_type *>(vType);
+ return NULL;
+}
+
+extern "C" const char *pdf_jsimp_drop_type_cpp(pdf_jsimp *imp, pdf_jsimp_type *type)
+{
+ /* Types are recorded and destroyed as part of PDFJSImp */
+ return NULL;
+}
+
+static Handle<Value> callMethod(const Arguments &args)
+{
+ HandleScope scope;
+ Local<External> mwrap = Local<External>::Cast(args.Data());
+ PDFJSImpMethod *m = (PDFJSImpMethod *)mwrap->Value();
+
+ Local<Object> self = args.Holder();
+ Local<External> owrap;
+ void *nself = NULL;
+ if (self->InternalFieldCount() > 0)
+ {
+ owrap = Local<External>::Cast(self->GetInternalField(0));
+ nself = owrap->Value();
+ }
+
+ int c = args.Length();
+ PDFJSImpObject **native_args = new PDFJSImpObject*[c];
+ for (int i = 0; i < c; i++)
+ native_args[i] = new PDFJSImpObject(args[i]);
+
+ PDFJSImpObject *obj = reinterpret_cast<PDFJSImpObject *>(pdf_jsimp_call_method(reinterpret_cast<pdf_jsimp *>(m->imp), m->meth, m->imp->jsctx, nself, c, reinterpret_cast<pdf_jsimp_obj **>(native_args)));
+ Handle<Value> val;
+ if (obj)
+ val = obj->toValue();
+ delete obj;
+
+ for (int i = 0; i < c; i++)
+ delete native_args[i];
+
+ delete native_args;
+
+ return scope.Close(val);
+}
+
+extern "C" const char *pdf_jsimp_addmethod_cpp(pdf_jsimp *imp, pdf_jsimp_type *type, char *name, pdf_jsimp_method *meth)
+{
+ Locker lock;
+ PDFJSImpType *vType = reinterpret_cast<PDFJSImpType *>(type);
+ HandleScope scope;
+
+ PDFJSImpMethod *pmeth = new PDFJSImpMethod(vType->imp, meth);
+ vType->templ->Set(String::New(name), FunctionTemplate::New(callMethod, External::New(pmeth)));
+ vType->methods.push_back(pmeth);
+ return NULL;
+}
+
+static Handle<Value> getProp(Local<String> property, const AccessorInfo &info)
+{
+ HandleScope scope;
+ Local<External> pwrap = Local<External>::Cast(info.Data());
+ PDFJSImpProperty *p = reinterpret_cast<PDFJSImpProperty *>(pwrap->Value());
+
+ Local<Object> self = info.Holder();
+ Local<External> owrap;
+ void *nself = NULL;
+ if (self->InternalFieldCount() > 0)
+ {
+ Local<Value> val = self->GetInternalField(0);
+ if (val->IsExternal())
+ {
+ owrap = Local<External>::Cast(val);
+ nself = owrap->Value();
+ }
+ }
+
+ PDFJSImpObject *obj = reinterpret_cast<PDFJSImpObject *>(pdf_jsimp_call_getter(reinterpret_cast<pdf_jsimp *>(p->imp), p->get, p->imp->jsctx, nself));
+ Handle<Value> val;
+ if (obj)
+ val = obj->toValue();
+ delete obj;
+ return scope.Close(val);
+}
+
+static void setProp(Local<String> property, Local<Value> value, const AccessorInfo &info)
+{
+ HandleScope scope;
+ Local<External> wrap = Local<External>::Cast(info.Data());
+ PDFJSImpProperty *p = reinterpret_cast<PDFJSImpProperty *>(wrap->Value());
+
+ Local<Object> self = info.Holder();
+ Local<External> owrap;
+ void *nself = NULL;
+ if (self->InternalFieldCount() > 0)
+ {
+ owrap = Local<External>::Cast(self->GetInternalField(0));
+ nself = owrap->Value();
+ }
+
+ PDFJSImpObject *obj = new PDFJSImpObject(value);
+
+ pdf_jsimp_call_setter(reinterpret_cast<pdf_jsimp *>(p->imp), p->set, p->imp->jsctx, nself, reinterpret_cast<pdf_jsimp_obj *>(obj));
+ delete obj;
+}
+
+extern "C" const char *pdf_jsimp_addproperty_cpp(pdf_jsimp *imp, pdf_jsimp_type *type, char *name, pdf_jsimp_getter *get, pdf_jsimp_setter *set)
+{
+ Locker lock;
+ PDFJSImpType *vType = reinterpret_cast<PDFJSImpType *>(type);
+ HandleScope scope;
+
+ PDFJSImpProperty *prop = new PDFJSImpProperty(vType->imp, get, set);
+ vType->templ->SetAccessor(String::New(name), getProp, setProp, External::New(prop));
+ vType->properties.push_back(prop);
+ return NULL;
+}
+
+extern "C" const char *pdf_jsimp_set_global_type_cpp(pdf_jsimp *imp, pdf_jsimp_type *type)
+{
+ Locker lock;
+ PDFJSImp *vImp = reinterpret_cast<PDFJSImp *>(imp);
+ PDFJSImpType *vType = reinterpret_cast<PDFJSImpType *>(type);
+ HandleScope scope;
+
+ vImp->context = Persistent<Context>::New(Context::New(NULL, vType->templ));
+ return NULL;
+}
+
+static void gcCallback(Persistent<Value> val, void *parm)
+{
+ PDFJSImpGCObj *gco = reinterpret_cast<PDFJSImpGCObj *>(parm);
+ PDFJSImpType *vType = gco->type;
+ HandleScope scope;
+ Persistent<Object> obj = Persistent<Object>::Cast(val);
+
+ Local<External> owrap = Local<External>::Cast(obj->GetInternalField(0));
+ vType->dtr(vType->imp->jsctx, owrap->Value());
+ vType->imp->gclist.erase(gco);
+ delete gco; /* Disposes of the persistent handle */
+}
+
+extern "C" const char *pdf_jsimp_new_obj_cpp(pdf_jsimp *imp, pdf_jsimp_type *type, void *natobj, pdf_jsimp_obj **robj)
+{
+ Locker lock;
+ PDFJSImpType *vType = reinterpret_cast<PDFJSImpType *>(type);
+ HandleScope scope;
+ Local<Object> obj = vType->templ->NewInstance();
+ obj->SetInternalField(0, External::New(natobj));
+
+ /* Arrange for destructor to be called on the client-side object
+ * when the v8 object is garbage collected */
+ if (vType->dtr)
+ {
+ /* Wrap obj in a PDFJSImpGCObj, which takes a persistent handle to
+ * obj, and stores its type with it. The persistent handle tells v8
+ * it cannot just destroy obj leaving the client-side object hanging */
+ PDFJSImpGCObj *gco = new PDFJSImpGCObj(obj, vType);
+ /* Keep the wrapped object in a list, so that we can take back control
+ * of destroying client-side objects when shutting down this context */
+ vType->imp->gclist.insert(gco);
+ /* Tell v8 that it can destroy the persistent handle to obj when it has
+ * no further need for it, but it must inform us via gcCallback */
+ gco->pobj.MakeWeak(gco, gcCallback);
+ }
+
+ *robj = reinterpret_cast<pdf_jsimp_obj *>(new PDFJSImpObject(obj));
+ return NULL;
+}
+
+extern "C" const char *pdf_jsimp_drop_obj_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj)
+{
+ Locker lock;
+ delete reinterpret_cast<PDFJSImpObject *>(obj);
+ return NULL;
+}
+
+extern "C" const char *pdf_jsimp_to_type_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, int *type)
+{
+ Locker lock;
+ *type = reinterpret_cast<PDFJSImpObject *>(obj)->type();
+ return NULL;
+}
+
+extern "C" const char *pdf_jsimp_from_string_cpp(pdf_jsimp *imp, char *str, pdf_jsimp_obj **obj)
+{
+ Locker lock;
+ *obj = reinterpret_cast<pdf_jsimp_obj *>(new PDFJSImpObject(str));
+ return NULL;
+}
+
+extern "C" const char *pdf_jsimp_to_string_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, char **str)
+{
+ Locker lock;
+ *str = reinterpret_cast<PDFJSImpObject *>(obj)->toString();
+ return NULL;
+}
+
+extern "C" const char *pdf_jsimp_from_number_cpp(pdf_jsimp *imp, double num, pdf_jsimp_obj **obj)
+{
+ Locker lock;
+ *obj = reinterpret_cast<pdf_jsimp_obj *>(new PDFJSImpObject(num));
+ return NULL;
+}
+
+extern "C" const char *pdf_jsimp_to_number_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, double *num)
+{
+ Locker lock;
+ *num = reinterpret_cast<PDFJSImpObject *>(obj)->toNumber();
+ return NULL;
+}
+
+extern "C" const char *pdf_jsimp_array_len_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, int *len)
+{
+ Locker lock;
+ Local<Object> jsobj = reinterpret_cast<PDFJSImpObject *>(obj)->toValue()->ToObject();
+ Local<Array> arr = Local<Array>::Cast(jsobj);
+ *len = arr->Length();
+ return NULL;
+}
+
+extern "C" const char *pdf_jsimp_array_item_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, int i, pdf_jsimp_obj **item)
+{
+ Locker lock;
+ Local<Object> jsobj = reinterpret_cast<PDFJSImpObject *>(obj)->toValue()->ToObject();
+ *item = reinterpret_cast<pdf_jsimp_obj *>(new PDFJSImpObject(jsobj->Get(Number::New(i))));
+ return NULL;
+}
+
+extern "C" const char *pdf_jsimp_property_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, char *prop, pdf_jsimp_obj **pobj)
+{
+ Locker lock;
+ Local<Object> jsobj = reinterpret_cast<PDFJSImpObject *>(obj)->toValue()->ToObject();
+ *pobj = reinterpret_cast<pdf_jsimp_obj *>(new PDFJSImpObject(jsobj->Get(String::New(prop))));
+ return NULL;
+}
+
+extern "C" const char *pdf_jsimp_execute_cpp(pdf_jsimp *imp, char *code)
+{
+ Locker lock;
+ PDFJSImp *vImp = reinterpret_cast<PDFJSImp *>(imp);
+ HandleScope scope;
+ Context::Scope context_scope(vImp->context);
+ Handle<Script> script = Script::Compile(String::New(code));
+ if (script.IsEmpty())
+ return "compile failed in pdf_jsimp_execute";
+ script->Run();
+ return NULL;
+}
+
+extern "C" const char *pdf_jsimp_execute_count_cpp(pdf_jsimp *imp, char *code, int count)
+{
+ Locker lock;
+ PDFJSImp *vImp = reinterpret_cast<PDFJSImp *>(imp);
+ HandleScope scope;
+ Context::Scope context_scope(vImp->context);
+ Handle<Script> script = Script::Compile(String::New(code, count));
+ if (script.IsEmpty())
+ return "compile failed in pdf_jsimp_execute_count";
+ script->Run();
+ return NULL;
+}
diff --git a/source/pdf/js/pdf-util.js b/source/pdf/js/pdf-util.js
new file mode 100644
index 00000000..06f4874b
--- /dev/null
+++ b/source/pdf/js/pdf-util.js
@@ -0,0 +1,875 @@
+var MuPDF = new Array();
+
+MuPDF.monthName = ['January','February','March','April','May','June','July','August','September','October','November','December'];
+MuPDF.dayName = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
+
+MuPDF.shortMonthName = new Array();
+
+for (var i = 0; i < MuPDF.monthName.length; i++)
+ MuPDF.shortMonthName.push(MuPDF.monthName[i].substr(0,3));
+
+MuPDF.monthPattern = new RegExp();
+MuPDF.monthPattern.compile('('+MuPDF.shortMonthName.join('|')+')');
+
+MuPDF.padZeros = function(num, places)
+{
+ var s = num.toString();
+
+ if (s.length < places)
+ s = new Array(places-s.length+1).join('0') + s;
+
+ return s;
+}
+
+MuPDF.convertCase = function(str,cmd)
+{
+ switch (cmd)
+ {
+ case '>': return str.toUpperCase();
+ case '<': return str.toLowerCase();
+ default: return str;
+ }
+}
+
+/* display must be kept in sync with an enum in pdf_form.c */
+var display = new Array();
+display.visible = 0;
+display.hidden = 1;
+display.noPrint = 2;
+display.noView = 3;
+var border = new Array();
+border.s = "Solid";
+border.d = "Dashed";
+border.b = "Beveled";
+border.i = "Inset";
+border.u = "Underline";
+var color = new Array();
+color.transparent = [ "T" ];
+color.black = [ "G", 0];
+color.white = [ "G", 1];
+color.red = [ "RGB", 1,0,0 ];
+color.green = [ "RGB", 0,1,0 ];
+color.blue = [ "RGB", 0,0,1 ];
+color.cyan = [ "CMYK", 1,0,0,0 ];
+color.magenta = [ "CMYK", 0,1,0,0 ];
+color.yellow = [ "CMYK", 0,0,1,0 ];
+color.dkGray = [ "G", 0.25];
+color.gray = [ "G", 0.5];
+color.ltGray = [ "G", 0.75];
+
+var util = new Array();
+
+util.printd = function(fmt, d)
+{
+ var regexp = /(m+|d+|y+|H+|h+|M+|s+|t+|[^mdyHhMst]+)/g;
+ var res = '';
+
+ if (!d)
+ return null;
+
+ var tokens = fmt.match(regexp);
+ var length = tokens ? tokens.length : 0;
+
+ for (var i = 0; i < length; i++)
+ {
+ switch(tokens[i])
+ {
+ case 'mmmm': res += MuPDF.monthName[d.getMonth()]; break;
+ case 'mmm': res += MuPDF.monthName[d.getMonth()].substr(0,3); break;
+ case 'mm': res += MuPDF.padZeros(d.getMonth()+1, 2); break;
+ case 'm': res += d.getMonth()+1; break;
+ case 'dddd': res += MuPDF.dayName[d.getDay()]; break;
+ case 'ddd': res += MuPDF.dayName[d.getDay()].substr(0,3); break;
+ case 'dd': res += MuPDF.padZeros(d.getDate(), 2); break;
+ case 'd': res += d.getDate(); break;
+ case 'yyyy': res += d.getFullYear(); break;
+ case 'yy': res += d.getFullYear()%100; break;
+ case 'HH': res += MuPDF.padZeros(d.getHours(), 2); break;
+ case 'H': res += d.getHours(); break;
+ case 'hh': res += MuPDF.padZeros((d.getHours()+11)%12+1, 2); break;
+ case 'h': res += (d.getHours()+11)%12+1; break;
+ case 'MM': res += MuPDF.padZeros(d.getMinutes(), 2); break;
+ case 'M': res += d.getMinutes(); break;
+ case 'ss': res += MuPDF.padZeros(d.getSeconds(), 2); break;
+ case 's': res += d.getSeconds(); break;
+ case 'tt': res += d.getHours() < 12 ? 'am' : 'pm'; break;
+ case 't': res += d.getHours() < 12 ? 'a' : 'p'; break;
+ default: res += tokens[i];
+ }
+ }
+
+ return res;
+}
+
+util.printx = function(fmt, val)
+{
+ var cs = '=';
+ var res = '';
+ var i = 0;
+ var m;
+ var length = fmt ? fmt.length : 0;
+
+ while (i < length)
+ {
+ switch (fmt.charAt(i))
+ {
+ case '\\':
+ i++;
+ if (i >= length) return res;
+ res += fmt.charAt(i);
+ break;
+
+ case 'X':
+ m = val.match(/\w/);
+ if (!m) return res;
+ res += MuPDF.convertCase(m[0],cs);
+ val = val.replace(/^\W*\w/,'');
+ break;
+
+ case 'A':
+ m = val.match(/[A-z]/);
+ if (!m) return res;
+ res += MuPDF.convertCase(m[0],cs);
+ val = val.replace(/^[^A-z]*[A-z]/,'');
+ break;
+
+ case '9':
+ m = val.match(/\d/);
+ if (!m) return res;
+ res += m[0];
+ val = val.replace(/^\D*\d/,'');
+ break;
+
+ case '*':
+ res += val;
+ val = '';
+ break;
+
+ case '?':
+ if (!val) return res;
+ res += MuPDF.convertCase(val.charAt(0),cs);
+ val = val.substr(1);
+ break;
+
+ case '=':
+ case '>':
+ case '<':
+ cs = fmt.charAt(i);
+ break;
+
+ default:
+ res += MuPDF.convertCase(fmt.charAt(i),cs);
+ break;
+ }
+
+ i++;
+ }
+
+ return res;
+}
+
+util.printf = function()
+{
+ if (arguments.length < 1)
+ return "";
+
+ var res = "";
+ var arg_i = 1;
+ var regexp = /%[^dfsx]*[dfsx]|[^%]*/g;
+ var tokens = arguments[0].match(regexp);
+ var length = tokens ? tokens.length : 0;
+
+ for (var i = 0; i < length; i++)
+ {
+ var tok = tokens[i];
+ if (tok.match(/^%/))
+ {
+ if (arg_i < arguments.length)
+ {
+ var val = arguments[arg_i++];
+ var fval = '';
+ var neg = false;
+ var decsep_re = /^,[0123]/;
+ var flags_re = /^[+ 0#]+/;
+ var width_re = /^\d+/;
+ var prec_re = /^\.\d+/;
+ var conv_re = /^[dfsx]/;
+
+ tok = tok.replace(/^%/, "");
+ var decsep = tok.match(decsep_re);
+ if (decsep) decsep = decsep[0];
+ tok = tok.replace(decsep_re, "");
+ var flags = tok.match(flags_re);
+ if (flags) flags = flags[0];
+ tok = tok.replace(flags_re, "");
+ var width = tok.match(width_re);
+ if (width) width = width[0];
+ tok = tok.replace(width_re, "");
+ var prec = tok.match(prec_re);
+ if (prec) prec = prec[0];
+ tok = tok.replace(prec_re, "");
+ var conv = tok.match(conv_re);
+ if (conv) conv = conv[0];
+
+ prec = prec ? Number(prec.replace(/^\./, '')) : 0;
+ var poschar = (flags && flags.match(/[+ ]/)) ? flags.match(/[+ ]/)[0] : '';
+ var pad = (flags && flags.match(/0/)) ? '0' : ' ';
+
+ var point = '.';
+ var thou = '';
+
+ if (decsep)
+ {
+ switch(decsep)
+ {
+ case ',0': thou = ','; break;
+ case ',1': break;
+ case ',2': thou = '.'; point = ','; break;
+ case ',3': point = ','; break;
+ }
+ }
+
+ switch(conv)
+ {
+ case 'x':
+ val = Math.floor(val);
+ neg = (val < 0);
+ if (neg)
+ val = -val;
+
+ // Convert to hex
+ while (val)
+ {
+ fval = '0123456789ABCDEF'.charAt(val%16) + fval;
+ val = Math.floor(val/16);
+ }
+
+ if (neg)
+ fval = '-' + fval;
+ else
+ fval = poschar + fval;
+ break;
+
+ case 'd':
+ fval = String(Math.floor(val));
+ break;
+
+ case 's':
+ // Always pad strings with space
+ pad = ' ';
+ fval = String(val);
+ break;
+
+ case 'f':
+ fval = String(val);
+
+ if (prec)
+ {
+ var frac = fval.match(/\.\d+/);
+ if (frac)
+ {
+ frac = frac[0];
+ // Matched string includes the dot, so make it
+ // prec+1 in length
+ if (frac.length > prec+1)
+ frac = frac.substr(0, prec+1);
+ else if(frac.length < prec+1)
+ frac += new Array(prec+1-frac.length+1).join('0');
+
+ fval = fval.replace(/\.\d+/, frac);
+ }
+ }
+ break;
+ }
+
+ if (conv.match(/[fd]/))
+ {
+ if (fval >= 0)
+ fval = poschar + fval;
+
+ if (point != '.')
+ fval.replace(/\./, point);
+
+ if (thou)
+ {
+ var intpart = fval.match(/\d+/)[0];
+ intpart = new Array(2-(intpart.length+2)%3+1).join('0') + intpart;
+ intpart = intpart.match(/.../g).join(thou).replace(/^0*[,.]?/,'');
+ fval = fval.replace(/\d+/, intpart);
+ }
+ }
+
+ if (width && fval.length < width)
+ fval = new Array(width - fval.length + 1).join(pad) + fval;
+
+ res += fval;
+ }
+ }
+ else
+ {
+ res += tok;
+ }
+ }
+
+ return res;
+}
+
+function AFMergeChange(event)
+{
+ return event.value;
+}
+
+function AFMakeNumber(str)
+{
+ var nums = str.match(/\d+/g);
+
+ if (!nums)
+ return null;
+
+ var res = nums.join('.');
+
+ if (str.match(/^[^0-9]*\./))
+ res = '0.'+res;
+
+ return res * (str.match(/-/) ? -1.0 : 1.0);
+}
+
+function AFExtractTime(dt)
+{
+ var ampm = dt.match(/(am|pm)/);
+ dt = dt.replace(/(am|pm)/, '');
+ var t = dt.match(/\d{1,2}:\d{1,2}:\d{1,2}/);
+ dt = dt.replace(/\d{1,2}:\d{1,2}:\d{1,2}/, '');
+ if (!t)
+ {
+ t = dt.match(/\d{1,2}:\d{1,2}/);
+ dt = dt.replace(/\d{1,2}:\d{1,2}/, '');
+ }
+
+ return [dt, t?t[0]+(ampm?ampm[0]:''):''];
+}
+
+function AFParseDateOrder(fmt)
+{
+ var order = '';
+
+ // Ensure all present with those not added in default order
+ fmt += "mdy";
+
+ for (var i = 0; i < fmt.length; i++)
+ {
+ var c = fmt.charAt(i);
+
+ if ('ymd'.indexOf(c) != -1 && order.indexOf(c) == -1)
+ order += c;
+ }
+
+ return order;
+}
+
+function AFMatchMonth(d)
+{
+ var m = d.match(MuPDF.monthPattern);
+
+ return m ? MuPDF.shortMonthName.indexOf(m[0]) : null;
+}
+
+function AFParseTime(str, d)
+{
+ if (!str)
+ return d;
+
+ if (!d)
+ d = new Date();
+
+ var ampm = str.match(/(am|pm)/);
+ var nums = str.match(/\d+/g);
+ var hour, min, sec;
+
+ if (!nums)
+ return null;
+
+ sec = 0;
+
+ switch (nums.length)
+ {
+ case 3:
+ sec = nums[2];
+ case 2:
+ hour = nums[0];
+ min = nums[1];
+ break;
+
+ default:
+ return null;
+ }
+
+ if (ampm == 'am' && hour < 12)
+ hour = 12 + hour;
+
+ if (ampm == 'pm' && hour >= 12)
+ hour = 0 + hour - 12;
+
+ d.setHours(hour, min, sec);
+
+ if (d.getHours() != hour || d.getMinutes() != min || d.getSeconds() != sec)
+ return null;
+
+ return d;
+}
+
+function AFParseDateEx(d, fmt)
+{
+ var dt = AFExtractTime(d);
+ var nums = dt[0].match(/\d+/g);
+ var order = AFParseDateOrder(fmt);
+ var text_month = AFMatchMonth(dt[0]);
+ var dout = new Date();
+ var year = dout.getFullYear();
+ var month = dout.getMonth();
+ var date = dout.getDate();
+
+ dout.setHours(12,0,0);
+
+ if (!nums || nums.length < 1 || nums.length > 3)
+ return null;
+
+ if (nums.length < 3 && text_month)
+ {
+ // Use the text month rather than one of the numbers
+ month = text_month;
+ order = order.replace('m','');
+ }
+
+ order = order.substr(0, nums.length);
+
+ // If year and month specified but not date then use the 1st
+ if (order == "ym" || (order == "y" && text_month))
+ date = 1;
+
+ for (var i = 0; i < nums.length; i++)
+ {
+ switch (order.charAt(i))
+ {
+ case 'y': year = nums[i]; break;
+ case 'm': month = nums[i] - 1; break;
+ case 'd': date = nums[i]; break;
+ }
+ }
+
+ if (year < 100)
+ {
+ if (fmt.search("yyyy") != -1)
+ return null;
+
+ if (year >= 50)
+ year = 1900 + year;
+ else if (year >= 0)
+ year = 2000 + year;
+ }
+
+ dout.setFullYear(year, month, date);
+
+ if (dout.getFullYear() != year || dout.getMonth() != month || dout.getDate() != date)
+ return null;
+
+ return AFParseTime(dt[1], dout);
+}
+
+function AFDate_KeystrokeEx(fmt)
+{
+ if (event.willCommit && !AFParseDateEx(event.value, fmt))
+ {
+ app.alert("Invalid date/time. please ensure that the date/time exists. Field [ "+event.target.name+" ] should match format "+fmt);
+ event.rc = false;
+ }
+}
+
+function AFDate_Keystroke(index)
+{
+ var formats = ['m/d','m/d/yy','mm/dd/yy','mm/yy','d-mmm','d-mmm-yy','dd-mm-yy','yy-mm-dd',
+ 'mmm-yy','mmmm-yy','mmm d, yyyy','mmmm d, yyyy','m/d/yy h:MM tt','m/d/yy HH:MM'];
+ AFDate_KeystrokeEx(formats[index]);
+}
+
+function AFDate_FormatEx(fmt)
+{
+ var d = AFParseDateEx(event.value, fmt);
+
+ event.value = d ? util.printd(fmt, d) : "";
+}
+
+function AFDate_Format(index)
+{
+ var formats = ['m/d','m/d/yy','mm/dd/yy','mm/yy','d-mmm','d-mmm-yy','dd-mm-yy','yy-mm-dd',
+ 'mmm-yy','mmmm-yy','mmm d, yyyy','mmmm d, yyyy','m/d/yy h:MM tt','m/d/yy HH:MM'];
+ AFDate_FormatEx(formats[index]);
+}
+
+function AFTime_Keystroke(index)
+{
+ if (event.willCommit && !AFParseTime(event.value, null))
+ {
+ app.alert("The value entered does not match the format of the field [ "+event.target.name+" ]");
+ event.rc = false;
+ }
+}
+
+function AFTime_FormatEx(fmt)
+{
+ var d = AFParseTime(event.value, null);
+
+ event.value = d ? util.printd(fmt, d) : '';
+}
+
+function AFTime_Format(index)
+{
+ var formats = ['HH:MM','h:MM tt','HH:MM:ss','h:MM:ss tt'];
+
+ AFTime_FormatEx(formats[index]);
+}
+
+function AFSpecial_KeystrokeEx(fmt)
+{
+ var cs = '=';
+ var val = event.value;
+ var res = '';
+ var i = 0;
+ var m;
+ var length = fmt ? fmt.length : 0;
+
+ while (i < length)
+ {
+ switch (fmt.charAt(i))
+ {
+ case '\\':
+ i++;
+ if (i >= length)
+ break;
+ res += fmt.charAt(i);
+ if (val && val.charAt(0) == fmt.charAt(i))
+ val = val.substr(1);
+ break;
+
+ case 'X':
+ m = val.match(/^\w/);
+ if (!m)
+ {
+ event.rc = false;
+ break;
+ }
+ res += MuPDF.convertCase(m[0],cs);
+ val = val.substr(1);
+ break;
+
+ case 'A':
+ m = val.match(/^[A-z]/);
+ if (!m)
+ {
+ event.rc = false;
+ break;
+ }
+ res += MuPDF.convertCase(m[0],cs);
+ val = val.substr(1);
+ break;
+
+ case '9':
+ m = val.match(/^\d/);
+ if (!m)
+ {
+ event.rc = false;
+ break;
+ }
+ res += m[0];
+ val = val.substr(1);
+ break;
+
+ case '*':
+ res += val;
+ val = '';
+ break;
+
+ case '?':
+ if (!val)
+ {
+ event.rc = false;
+ break;
+ }
+ res += MuPDF.convertCase(val.charAt(0),cs);
+ val = val.substr(1);
+ break;
+
+ case '=':
+ case '>':
+ case '<':
+ cs = fmt.charAt(i);
+ break;
+
+ default:
+ res += fmt.charAt(i);
+ if (val && val.charAt(0) == fmt.charAt(i))
+ val = val.substr(1);
+ break;
+ }
+
+ i++;
+ }
+
+ if (event.rc)
+ event.value = res;
+ else if (event.willCommit)
+ app.alert("The value entered does not match the format of the field [ "+event.target.name+" ] should be "+fmt);
+}
+
+function AFSpecial_Keystroke(index)
+{
+ if (event.willCommit)
+ {
+ switch (index)
+ {
+ case 0:
+ if (!event.value.match(/^\d{5}$/))
+ event.rc = false;
+ break;
+ case 1:
+ if (!event.value.match(/^\d{5}[-. ]?\d{4}$/))
+ event.rc = false;
+ break;
+ case 2:
+ if (!event.value.match(/^((\(\d{3}\)|\d{3})[-. ]?)?\d{3}[-. ]?\d{4}$/))
+ event.rc = false;
+ break;
+ case 3:
+ if (!event.value.match(/^\d{3}[-. ]?\d{2}[-. ]?\d{4}$/))
+ event.rc = false;
+ break;
+ }
+
+ if (!event.rc)
+ app.alert("The value entered does not match the format of the field [ "+event.target.name+" ]");
+ }
+}
+
+function AFSpecial_Format(index)
+{
+ var res;
+
+ switch (index)
+ {
+ case 0:
+ res = util.printx('99999', event.value);
+ break;
+ case 1:
+ res = util.printx('99999-9999', event.value);
+ break;
+ case 2:
+ res = util.printx('9999999999', event.value);
+ res = util.printx(res.length >= 10 ? '(999) 999-9999' : '999-9999', event.value);
+ break;
+ case 3:
+ res = util.printx('999-99-9999', event.value);
+ break;
+ }
+
+ event.value = res ? res : '';
+}
+
+function AFNumber_Keystroke(nDec, sepStyle, negStyle, currStyle, strCurrency, bCurrencyPrepend)
+{
+ if (sepStyle & 2)
+ {
+ if (!event.value.match(/^[+-]?\d*[,.]?\d*$/))
+ event.rc = false;
+ }
+ else
+ {
+ if (!event.value.match(/^[+-]?\d*\.?\d*$/))
+ event.rc = false;
+ }
+
+ if (event.willCommit)
+ {
+ if (!event.value.match(/\d/))
+ event.rc = false;
+
+ if (!event.rc)
+ app.alert("The value entered does not match the format of the field [ "+event.target.name+" ]");
+ }
+}
+
+function AFNumber_Format(nDec,sepStyle,negStyle,currStyle,strCurrency,bCurrencyPrepend)
+{
+ var val = event.value;
+ var fracpart;
+ var intpart;
+ var point = sepStyle&2 ? ',' : '.';
+ var separator = sepStyle&2 ? '.' : ',';
+
+ if (/^\D*\./.test(val))
+ val = '0'+val;
+
+ var groups = val.match(/\d+/g);
+
+ if (!groups)
+ return;
+
+ switch (groups.length)
+ {
+ case 0:
+ return;
+ case 1:
+ fracpart = '';
+ intpart = groups[0];
+ break;
+ default:
+ fracpart = groups.pop();
+ intpart = groups.join('');
+ break;
+ }
+
+ // Remove leading zeros
+ intpart = intpart.replace(/^0*/,'');
+ if (!intpart)
+ intpart = '0';
+
+ if ((sepStyle & 1) == 0)
+ {
+ // Add the thousands sepearators: pad to length multiple of 3 with zeros,
+ // split into 3s, join with separator, and remove the leading zeros
+ intpart = new Array(2-(intpart.length+2)%3+1).join('0') + intpart;
+ intpart = intpart.match(/.../g).join(separator).replace(/^0*/,'');
+ }
+
+ if (!intpart)
+ intpart = '0';
+
+ // Adjust fractional part to correct number of decimal places
+ fracpart += new Array(nDec+1).join('0');
+ fracpart = fracpart.substr(0,nDec);
+
+ if (fracpart)
+ intpart += point+fracpart;
+
+ if (bCurrencyPrepend)
+ intpart = strCurrency+intpart;
+ else
+ intpart += strCurrency;
+
+ if (/-/.test(val))
+ {
+ switch (negStyle)
+ {
+ case 0:
+ intpart = '-'+intpart;
+ break;
+ case 1:
+ break;
+ case 2:
+ case 3:
+ intpart = '('+intpart+')';
+ break;
+ }
+ }
+
+ if (negStyle&1)
+ event.target.textColor = /-/.test(val) ? color.red : color.black;
+
+ event.value = intpart;
+}
+
+function AFPercent_Keystroke(nDec, sepStyle)
+{
+ AFNumber_Keystroke(nDec, sepStyle, 0, 0, "", true);
+}
+
+function AFPercent_Format(nDec, sepStyle)
+{
+ var val = AFMakeNumber(event.value);
+
+ if (!val)
+ {
+ event.value = '';
+ return;
+ }
+
+ event.value = (val * 100) + '';
+
+ AFNumber_Format(nDec, sepStyle, 0, 0, "%", false);
+}
+
+function AFSimple_Calculate(op, list)
+{
+ var res;
+
+ switch (op)
+ {
+ case 'SUM':
+ res = 0;
+ break;
+ case 'PRD':
+ res = 1;
+ break;
+ case 'AVG':
+ res = 0;
+ break;
+ }
+
+ if (typeof list == 'string')
+ list = list.split(/ *, */);
+
+ for (var i = 0; i < list.length; i++)
+ {
+ var field = getField(list[i]);
+ var value = Number(field.value);
+
+ switch (op)
+ {
+ case 'SUM':
+ res += value;
+ break;
+ case 'PRD':
+ res *= value;
+ break;
+ case 'AVG':
+ res += value;
+ break;
+ case 'MIN':
+ if (i == 0 || value < res)
+ res = value;
+ break;
+ case 'MAX':
+ if (i == 0 || value > res)
+ res = value;
+ break;
+ }
+ }
+
+ if (op == 'AVG')
+ res /= list.length;
+
+ event.value = res;
+}
+
+function AFRange_Validate(lowerCheck, lowerLimit, upperCheck, upperLimit)
+{
+ if (upperCheck && event.value > upperLimit)
+ {
+ event.rc = false;
+ }
+
+ if (lowerCheck && event.value < lowerLimit)
+ {
+ event.rc = false;
+ }
+
+
+ if (!event.rc)
+ {
+ if (lowerCheck && upperCheck)
+ app.alert(util.printf("Invalid value: must be greater than or equal to %s and less than or equal to %s", lowerLimit, upperLimit));
+ else if (lowerCheck)
+ app.alert(util.printf("Invalid value: must be greater than or equal to %s", lowerLimit));
+ else
+ app.alert(util.printf("Invalid value: must be less than or equal to %s", upperLimit));
+ }
+}