diff options
author | Tor Andersson <tor.andersson@artifex.com> | 2013-06-19 15:29:44 +0200 |
---|---|---|
committer | Tor Andersson <tor.andersson@artifex.com> | 2013-06-20 16:45:35 +0200 |
commit | 0a927854a10e1e6b9770a81e2e1d9f3093631757 (patch) | |
tree | 3d65d820d9fdba2d0d394d99c36290c851b78ca0 /source/pdf/js | |
parent | 1ae8f19179c5f0f8c6352b3c7855465325d5449a (diff) | |
download | mupdf-0a927854a10e1e6b9770a81e2e1d9f3093631757.tar.xz |
Rearrange source files.
Diffstat (limited to 'source/pdf/js')
-rw-r--r-- | source/pdf/js/pdf-js-none.c | 36 | ||||
-rw-r--r-- | source/pdf/js/pdf-js.c | 919 | ||||
-rw-r--r-- | source/pdf/js/pdf-jsimp-cpp.c | 225 | ||||
-rw-r--r-- | source/pdf/js/pdf-jsimp-cpp.h | 29 | ||||
-rw-r--r-- | source/pdf/js/pdf-jsimp-v8.cpp | 476 | ||||
-rw-r--r-- | source/pdf/js/pdf-util.js | 875 |
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)); + } +} |