summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile37
-rw-r--r--Makethird29
-rw-r--r--apps/jstest_main.c381
-rw-r--r--apps/mudraw.c157
-rw-r--r--apps/pdfapp.c70
-rw-r--r--apps/pdfapp.h7
-rw-r--r--apps/win_main.c44
-rw-r--r--apps/win_res.rc10
-rw-r--r--apps/x11_main.c14
-rw-r--r--cbz/mucbz.c57
-rw-r--r--fitz/doc_document.c31
-rw-r--r--fitz/doc_interactive.c22
-rw-r--r--fitz/fitz-internal.h28
-rw-r--r--fitz/fitz.h157
-rw-r--r--fitz/stm_buffer.c91
-rw-r--r--pdf/mupdf-internal.h92
-rw-r--r--pdf/mupdf.h23
-rw-r--r--pdf/pdf_annot.c73
-rw-r--r--pdf/pdf_font.c50
-rw-r--r--pdf/pdf_form.c1935
-rw-r--r--pdf/pdf_js.c568
-rw-r--r--pdf/pdf_js_none.c31
-rw-r--r--pdf/pdf_jsimp_cpp.c148
-rw-r--r--pdf/pdf_jsimp_cpp.h21
-rw-r--r--pdf/pdf_jsimp_v8.cpp412
-rw-r--r--pdf/pdf_lex.c11
-rw-r--r--pdf/pdf_page.c4
-rw-r--r--pdf/pdf_xobject.c2
-rw-r--r--pdf/pdf_xref.c87
-rw-r--r--pdf/pdf_xref_aux.c9
-rw-r--r--win32/libmupdf-v8.vcproj689
-rw-r--r--win32/libmupdf.vcproj12
-rw-r--r--win32/mujstest-v8.vcproj261
-rw-r--r--win32/mupdf-v8.vcproj265
-rw-r--r--win32/mupdf.sln36
-rw-r--r--xps/xps_zip.c69
36 files changed, 5703 insertions, 230 deletions
diff --git a/Makefile b/Makefile
index 082556ca..e1596f4d 100644
--- a/Makefile
+++ b/Makefile
@@ -14,6 +14,7 @@ default: all
# set a variable that was set on the command line.
CFLAGS += $(XCFLAGS) -Ifitz -Ipdf -Ixps -Icbz -Iscripts
LIBS += $(XLIBS) -lfreetype -ljbig2dec -ljpeg -lopenjpeg -lz -lm
+LIBS_V8 = $(LIBS) $(V8LIBS)
include Makerules
include Makethird
@@ -27,14 +28,17 @@ THIRD_LIBS += $(ZLIB_LIB)
ifeq "$(verbose)" ""
QUIET_AR = @ echo ' ' ' ' AR $@ ;
QUIET_CC = @ echo ' ' ' ' CC $@ ;
+QUIET_CXX = @ echo ' ' ' ' CXX $@ ;
QUIET_GEN = @ echo ' ' ' ' GEN $@ ;
QUIET_LINK = @ echo ' ' ' ' LINK $@ ;
QUIET_MKDIR = @ echo ' ' ' ' MKDIR $@ ;
endif
CC_CMD = $(QUIET_CC) $(CC) $(CFLAGS) -o $@ -c $<
+CXX_CMD = $(QUIET_CXX) $(CXX) $(CFLAGS) -o $@ -c $<
AR_CMD = $(QUIET_AR) $(AR) cru $@ $^
LINK_CMD = $(QUIET_LINK) $(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
+LINK_V8_CMD = $(QUIET_LINK) $(CXX) $(LDFLAGS) -o $@ $^ $(LIBS_V8)
MKDIR_CMD = $(QUIET_MKDIR) mkdir -p $@
# --- Rules ---
@@ -60,6 +64,8 @@ $(OUT)/%.o : draw/%.c $(FITZ_HDR) | $(OUT)
$(CC_CMD)
$(OUT)/%.o : pdf/%.c $(MUPDF_HDR) | $(OUT)
$(CC_CMD)
+$(OUT)/%.o : pdf/%.cpp $(MUPDF_HDR) | $(OUT)
+ $(CXX_CMD)
$(OUT)/%.o : xps/%.c $(MUXPS_HDR) | $(OUT)
$(CC_CMD)
$(OUT)/%.o : cbz/%.c $(MUCBZ_HDR) | $(OUT)
@@ -74,10 +80,14 @@ $(OUT)/%.o : scripts/%.c | $(OUT)
# --- Fitz, MuPDF, MuXPS and MuCBZ library ---
FITZ_LIB := $(OUT)/libfitz.a
+FITZ_V8_LIB := $(OUT)/libfitzv8.a
FITZ_SRC := $(notdir $(wildcard fitz/*.c draw/*.c))
FITZ_SRC := $(filter-out draw_simple_scale.c, $(FITZ_SRC))
-MUPDF_SRC := $(notdir $(wildcard pdf/*.c))
+MUPDF_ALL_SRC := $(notdir $(wildcard pdf/*.c))
+MUPDF_SRC := $(filter-out pdf_js.c pdf_jsimp_cpp.c, $(MUPDF_ALL_SRC))
+MUPDF_V8_SRC := $(filter-out pdf_js_none.c, $(MUPDF_ALL_SRC))
+MUPDF_V8_CPP_SRC := $(notdir $(wildcard pdf/*.cpp))
MUXPS_SRC := $(notdir $(wildcard xps/*.c))
MUCBZ_SRC := $(notdir $(wildcard cbz/*.c))
@@ -86,7 +96,14 @@ $(FITZ_LIB) : $(addprefix $(OUT)/, $(MUPDF_SRC:%.c=%.o))
$(FITZ_LIB) : $(addprefix $(OUT)/, $(MUXPS_SRC:%.c=%.o))
$(FITZ_LIB) : $(addprefix $(OUT)/, $(MUCBZ_SRC:%.c=%.o))
+$(FITZ_V8_LIB) : $(addprefix $(OUT)/, $(FITZ_SRC:%.c=%.o))
+$(FITZ_V8_LIB) : $(addprefix $(OUT)/, $(MUPDF_V8_SRC:%.c=%.o))
+$(FITZ_V8_LIB) : $(addprefix $(OUT)/, $(MUPDF_V8_CPP_SRC:%.cpp=%.o))
+$(FITZ_V8_LIB) : $(addprefix $(OUT)/, $(MUXPS_SRC:%.c=%.o))
+$(FITZ_V8_LIB) : $(addprefix $(OUT)/, $(MUCBZ_SRC:%.c=%.o))
+
libs: $(FITZ_LIB) $(THIRD_LIBS)
+libs_v8: libs $(FITZ_V8_LIB)
# --- Generated CMAP and FONT files ---
@@ -144,6 +161,22 @@ MUVIEW := $(OUT)/mupdf
$(MUVIEW) : $(FITZ_LIB) $(THIRD_LIBS)
$(MUVIEW) : $(addprefix $(OUT)/, x11_main.o x11_image.o pdfapp.o)
$(LINK_CMD) $(X11_LIBS)
+
+MUVIEW_V8 := $(OUT)/mupdf-v8
+$(MUVIEW_V8) : $(FITZ_V8_LIB) $(THIRD_LIBS)
+$(MUVIEW_V8) : $(addprefix $(OUT)/, x11_main.o x11_image.o pdfapp.o)
+ $(LINK_V8_CMD) $(X11_LIBS)
+endif
+
+MUJSTEST_V8 := $(OUT)/mujstest-v8
+$(MUJSTEST_V8) : $(FITZ_V8_LIB) $(THIRD_LIBS)
+$(MUJSTEST_V8) : $(addprefix $(OUT)/, jstest_main.o pdfapp.o)
+ $(LINK_V8_CMD)
+
+ifeq "$(V8_PRESENT)" "1"
+JSTARGETS := $(MUJSTEST_V8) $(FITZ_V8_LIB) $(MUVIEW_V8)
+else
+JSTARGETS :=
endif
# --- Format man pages ---
@@ -173,7 +206,7 @@ install: $(FITZ_LIB) $(MUVIEW) $(MUDRAW) $(MUBUSY)
# --- Clean and Default ---
-all: all-nojs
+all: all-nojs $(JSTARGETS)
all-nojs: $(THIRD_LIBS) $(FITZ_LIB) $(MUVIEW) $(MUDRAW) $(MUBUSY)
diff --git a/Makethird b/Makethird
index 8b84efc4..1564950f 100644
--- a/Makethird
+++ b/Makethird
@@ -9,6 +9,35 @@ JBIG2DEC_DIR := $(wildcard thirdparty/jbig2dec*)
JPEG_DIR := $(wildcard thirdparty/jpeg*)
OPENJPEG_DIR := $(wildcard thirdparty/openjpeg*/libopenjpeg)
ZLIB_DIR := $(wildcard thirdparty/zlib*)
+V8_DIR := $(wildcard thirdparty/v8*)
+
+# --- V8 ---
+
+ifneq "$(V8_DIR)" ""
+
+CFLAGS += -I$(V8_DIR)/include
+ifeq "$(BUILD)" "release"
+V8LIB_CHOICE := release
+else ifeq "$(BUILD)" "profile"
+V8LIB_CHOICE := release
+else
+V8LIB_CHOICE := debug
+endif
+
+# If you are building for 32bit linux use V8_ARCH=ia32
+# MacOSX has a 64bit kernel, but a 32 bit userspace.
+ifeq "$(OS)" "Darwin"
+V8_ARCH ?= ia32
+V8LIBS = -L$(V8_DIR)/out-mac/$(V8_ARCH).$(V8LIB_CHOICE)/ -lv8_base -lv8_snapshot
+else
+V8_ARCH ?= x64
+V8LIBS = -L$(V8_DIR)/out/$(V8_ARCH).$(V8LIB_CHOICE)/obj.target/tools/gyp -lv8_base -lv8_snapshot -lpthread
+endif
+
+V8_PRESENT := 1
+else
+V8_PRESENT := 0
+endif
# --- FreeType 2 ---
diff --git a/apps/jstest_main.c b/apps/jstest_main.c
new file mode 100644
index 00000000..01493f47
--- /dev/null
+++ b/apps/jstest_main.c
@@ -0,0 +1,381 @@
+#include "fitz.h"
+#include "mupdf.h"
+#include "muxps.h"
+#include "mucbz.h"
+#include "pdfapp.h"
+#include <ctype.h>
+
+/*
+ A useful bit of bash script to call this is:
+ for f in ../ghostpcl/tests_private/pdf/forms/v1.3/ *.pdf ; do g=${f%.*} ; echo $g ; win32/debug/mujstest-v8.exe -o $g-%d.png -p ../ghostpcl/ $g.mjs > $g.log 2>&1 ; done
+
+ Remove the space from "/ *.pdf" before running - can't leave that
+ in here, as it causes a warning about a possibly malformed comment.
+*/
+
+static pdfapp_t gapp;
+static int file_open = 0;
+static char filename[1024] = "";
+static char *scriptname;
+static char *output = NULL;
+static char *prefix = NULL;
+static int shotcount = 0;
+static int verbosity = 0;
+
+#define LONGLINE 4096
+
+static char getline_buffer[LONGLINE];
+
+void winwarn(pdfapp_t *app, char *msg)
+{
+ fprintf(stderr, "warning: %s", msg);
+}
+
+void winerror(pdfapp_t *app, char *msg)
+{
+ fprintf(stderr, "%s", msg);
+ exit(1);
+}
+
+static char pd_password[256] = "";
+static char td_textinput[LONGLINE] = "";
+
+char *winpassword(pdfapp_t *app, char *filename)
+{
+ if (pd_password[0] == 0)
+ return NULL;
+ return pd_password;
+}
+
+char *wintextinput(pdfapp_t *app, char *inittext)
+{
+ if (td_textinput[0] != 0)
+ return td_textinput;
+ return inittext;
+}
+
+void winhelp(pdfapp_t*app)
+{
+}
+
+void winclose(pdfapp_t *app)
+{
+ pdfapp_close(app);
+ exit(0);
+}
+
+void wincursor(pdfapp_t *app, int curs)
+{
+}
+
+void wintitle(pdfapp_t *app, char *title)
+{
+}
+
+void windrawrect(pdfapp_t *app, int x0, int y0, int x1, int y1)
+{
+}
+
+void windrawstring(pdfapp_t *app, int x, int y, char *s)
+{
+}
+
+void winresize(pdfapp_t *app, int w, int h)
+{
+}
+
+void winrepaint(pdfapp_t *app)
+{
+}
+
+void winrepaintsearch(pdfapp_t *app)
+{
+}
+
+void winfullscreen(pdfapp_t *app, int state)
+{
+}
+
+/*
+ * Event handling
+ */
+
+void windocopy(pdfapp_t *app)
+{
+}
+
+void winreloadfile(pdfapp_t *app)
+{
+ pdfapp_close(app);
+ pdfapp_open(app, filename, 1);
+}
+
+void winopenuri(pdfapp_t *app, char *buf)
+{
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "mujstest: Scriptable tester for mupdf + js\n");
+ fprintf(stderr, "\nSyntax: mujstest -o <filename> [ -p <prefix> ] [-v] <scriptfile>\n");
+ fprintf(stderr, "\n<filename> should sensibly be of the form file-%%d.png\n");
+ fprintf(stderr, "\n<prefix> is a path prefix to apply to filenames within the script\n");
+ fprintf(stderr, "\n-v verbose\n");
+ fprintf(stderr, "\nscriptfile contains a list of commands:\n");
+ fprintf(stderr, "\tPASSWORD <password>\tSet the password\n");
+ fprintf(stderr, "\tOPEN <filename>\tOpen a file\n");
+ fprintf(stderr, "\tGOTO <page>\tJump to a particular page\n");
+ fprintf(stderr, "\tSCREENSHOT\tSave a screenshot\n");
+ fprintf(stderr, "\tRESIZE <w> <h>\tResize the screen to a given size\n");
+ fprintf(stderr, "\tCLICK <x> <y> <btn>\tClick at a given position\n");
+ fprintf(stderr, "\tTEXT <string>\tSet a value to be entered\n");
+ exit(1);
+}
+
+static char *
+my_getline(FILE *file)
+{
+ int c;
+ char *d = getline_buffer;
+ int space = sizeof(getline_buffer)-1;
+
+ /* Skip over any prefix of whitespace */
+ do
+ {
+ c = fgetc(file);
+ }
+ while (isspace(c));
+
+ if (c < 0)
+ return NULL;
+
+ /* Read the line in */
+ do
+ {
+ *d++ = (char)c;
+ c = fgetc(file);
+ }
+ while (c >= 32 && space--);
+
+ /* If we ran out of space, skip the rest of the line */
+ if (space == 0)
+ {
+ while (c >= 32)
+ c = fgetc(file);
+ }
+
+ *d = 0;
+
+ return getline_buffer;
+}
+
+static int
+match(char **line, const char *match)
+{
+ char *s = *line;
+
+ if (s == NULL)
+ return 0;
+
+ while (isspace(*(unsigned char *)s))
+ s++;
+
+ while (*s == *match)
+ {
+ if (*s == 0)
+ {
+ *line = s;
+ return 1;
+ }
+ s++;
+ match++;
+ }
+
+ if (*match != 0)
+ return 0;
+
+ /* We matched! Skip over any whitespace */
+ while (isspace(*(unsigned char *)s))
+ s++;
+
+ *line = s;
+
+ /* Trim whitespace off the end of the line */
+ /* Run to the end of the line */
+ while (*s)
+ s++;
+
+ /* Run back until we find where we started, or non whitespace */
+ while (s != *line && isspace((unsigned char)s[-1]))
+ s--;
+
+ /* Remove the suffix of whitespace */
+ *s = 0;
+
+ return 1;
+}
+
+static void unescape_string(char *d, const char *s)
+{
+ char c;
+
+ while (c = *s++)
+ {
+ if (c == '\\')
+ {
+ c = *s++;
+ switch(c)
+ {
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ }
+ }
+ *d++ = c;
+ }
+ *d = 0;
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ fz_context *ctx;
+ FILE *script = NULL;
+ int c;
+
+ while ((c = fz_getopt(argc, argv, "o:p:v")) != -1)
+ {
+ switch(c)
+ {
+ case 'o': output = fz_optarg; break;
+ case 'p': prefix = fz_optarg; break;
+ case 'v': verbosity ^= 1; break;
+ default: usage(); break;
+ }
+ }
+
+ if (fz_optind == argc)
+ usage();
+
+ ctx = fz_new_context(NULL, NULL, FZ_STORE_DEFAULT);
+ if (!ctx)
+ {
+ fprintf(stderr, "cannot initialise context\n");
+ exit(1);
+ }
+ pdfapp_init(ctx, &gapp);
+ gapp.scrw = 640;
+ gapp.scrh = 480;
+ gapp.colorspace = fz_device_rgb;
+
+ fz_try(ctx)
+ {
+ while (fz_optind < argc)
+ {
+ scriptname = argv[fz_optind++];
+ script = fopen(scriptname, "rb");
+ if (script == NULL)
+ fz_throw(ctx, "cannot open script: %s", scriptname);
+
+ do
+ {
+ char *line = my_getline(script);
+ if (line == NULL)
+ continue;
+ if (verbosity)
+ fprintf(stderr, "'%s'\n", line);
+ if (match(&line, "%"))
+ {
+ /* Comment */
+ }
+ else if (match(&line, "PASSWORD"))
+ {
+ strcpy(pd_password, line);
+ }
+ else if (match(&line, "OPEN"))
+ {
+ char path[1024];
+ if (file_open)
+ pdfapp_close(&gapp);
+ strcpy(filename, line);
+ if (prefix)
+ {
+ sprintf(path, "%s%s", prefix, line);
+ }
+ else
+ {
+ strcpy(path, line);
+ }
+ pdfapp_open(&gapp, path, 0);
+ file_open = 1;
+ }
+ else if (match(&line, "GOTO"))
+ {
+ pdfapp_gotopage(&gapp, atoi(line)-1);
+ }
+ else if (match(&line, "SCREENSHOT"))
+ {
+ char text[1024];
+
+ sprintf(text, output, ++shotcount);
+ if (strstr(text, ".pgm") || strstr(text, ".ppm") || strstr(text, ".pnm"))
+ fz_write_pnm(ctx, gapp.image, text);
+ else
+ fz_write_png(ctx, gapp.image, text, 0);
+ }
+ else if (match(&line, "RESIZE"))
+ {
+ int w, h;
+ sscanf(line, "%d %d", &w, &h);
+ pdfapp_onresize(&gapp, w, h);
+ }
+ else if (match(&line, "CLICK"))
+ {
+ float x, y, b;
+ int n;
+ n = sscanf(line, "%f %f %f", &x, &y, &b);
+ if (n < 1)
+ x = 0.0f;
+ if (n < 2)
+ y = 0.0f;
+ if (n < 3)
+ b = 1;
+ /* state = 1 = transition down */
+ pdfapp_onmouse(&gapp, (int)x, (int)y, b, 0, 1);
+ /* state = -1 = transition up */
+ pdfapp_onmouse(&gapp, (int)x, (int)y, b, 0, -1);
+ }
+ else if (match(&line, "TEXT"))
+ {
+ unescape_string(td_textinput, line);
+ }
+ else
+ {
+ fprintf(stderr, "Unmatched: %s\n", line);
+ }
+ }
+ while (!feof(script));
+
+ fclose(script);
+ }
+ }
+ fz_catch(ctx)
+ {
+ fprintf(stderr, "error: cannot execute '%s'\n", scriptname);
+ }
+
+ if (file_open)
+ pdfapp_close(&gapp);
+
+ fz_free_context(ctx);
+
+ return 0;
+}
diff --git a/apps/mudraw.c b/apps/mudraw.c
index 4fce251d..dd5d88db 100644
--- a/apps/mudraw.c
+++ b/apps/mudraw.c
@@ -12,6 +12,56 @@
enum { TEXT_PLAIN = 1, TEXT_HTML = 2, TEXT_XML = 3 };
+/*
+ A useful bit of bash script to call this to generate mjs files:
+ for f in tests_private/pdf/forms/v1.3/ *.pdf ; do g=${f%.*} ; echo $g ; ../mupdf.git/win32/debug/mudraw.exe -j $g.mjs $g.pdf ; done
+
+ Remove the space from "/ *.pdf" before running - can't leave that
+ in here, as it causes a warning about a possibly malformed comment.
+*/
+
+static char lorem[] =
+"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum "
+"vehicula augue id est lobortis mollis. Aenean vestibulum metus sed est "
+"gravida non tempus lacus aliquet. Nulla vehicula lobortis tincidunt. "
+"Donec malesuada nisl et lacus condimentum nec tincidunt urna gravida. "
+"Sed dapibus magna eu velit ultrices non rhoncus risus lacinia. Fusce "
+"vitae nulla volutpat elit dictum ornare at eu libero. Maecenas felis "
+"enim, tempor a tincidunt id, commodo consequat lectus.\n"
+"Morbi tincidunt adipiscing lacus eu dignissim. Pellentesque augue elit, "
+"ultrices vitae fermentum et, faucibus et purus. Nam ante libero, lacinia "
+"id tincidunt at, ultricies a lorem. Donec non neque at purus condimentum "
+"eleifend quis sit amet libero. Sed semper, mi ut tempus tincidunt, lacus "
+"eros pellentesque lacus, id vehicula est diam eu quam. Integer tristique "
+"fringilla rhoncus. Phasellus convallis, justo ut mollis viverra, dui odio "
+"euismod ante, nec fringilla nisl mi ac diam.\n"
+"Maecenas mi urna, ornare commodo feugiat id, cursus in massa. Vivamus "
+"augue augue, aliquam at varius eu, venenatis fermentum felis. Sed varius "
+"turpis a felis ultrices quis aliquet nunc tincidunt. Suspendisse posuere "
+"commodo nunc non viverra. Praesent condimentum varius quam, vel "
+"consectetur odio volutpat in. Sed malesuada augue ut lectus commodo porta. "
+"Vivamus eget mauris sit amet diam ultrices sollicitudin. Cras pharetra leo "
+"non elit lacinia vulputate.\n"
+"Donec ac enim justo, ornare scelerisque diam. Ut vel ante at lorem "
+"placerat bibendum ultricies mattis metus. Phasellus in imperdiet odio. "
+"Proin semper lacinia libero, sed rutrum eros blandit non. Duis tincidunt "
+"ligula est, non pellentesque mauris. Aliquam in erat scelerisque lacus "
+"dictum suscipit eget semper magna. Nullam luctus imperdiet risus a "
+"semper.\n"
+"Curabitur sit amet tempor sapien. Quisque et tortor in lacus dictum "
+"pulvinar. Nunc at nisl ut velit vehicula hendrerit. Mauris elementum "
+"sollicitudin leo ac ullamcorper. Proin vel leo nec justo tempus aliquet "
+"nec ut mi. Pellentesque vel nisl id dui hendrerit fermentum nec quis "
+"tortor. Proin eu sem luctus est consequat euismod. Vestibulum ante ipsum "
+"primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce "
+"consectetur ultricies nisl ornare dictum. Cras sagittis consectetur lorem "
+"sed posuere. Mauris accumsan laoreet arcu, id molestie lorem faucibus eu. "
+"Vivamus commodo, neque nec imperdiet pretium, lorem metus viverra turpis, "
+"malesuada vulputate justo eros sit amet neque. Nunc quis justo elit, non "
+"rutrum mauris. Maecenas blandit condimentum nibh, nec vulputate orci "
+"pulvinar at. Proin sed arcu vel odio tempus lobortis sed posuere ipsum. Ut "
+"feugiat pellentesque tortor nec ornare.\n";
+
static char *output = NULL;
static float resolution = 72;
static int res_specified = 0;
@@ -37,6 +87,10 @@ static fz_colorspace *colorspace;
static char *filename;
static int files = 0;
+static char *mujstest_filename = NULL;
+static FILE *mujstest_file = NULL;
+static int mujstest_count = 0;
+
static struct {
int count, total;
int min, max;
@@ -68,6 +122,7 @@ static void usage(void)
"\t-G gamma\tgamma correct output\n"
"\t-I\tinvert output\n"
"\t-l\tprint outline\n"
+ "\t-j -\tOutput mujstest file\n"
"\tpages\tcomma separated list of ranges\n");
exit(1);
}
@@ -97,6 +152,31 @@ static int isrange(char *s)
return 1;
}
+static void escape_string(FILE *out, int len, const char *string)
+{
+ while (len-- && *string)
+ {
+ char c = *string++;
+ switch (c)
+ {
+ case '\n':
+ fputc('\\', out);
+ fputc('n', out);
+ break;
+ case '\r':
+ fputc('\\', out);
+ fputc('r', out);
+ break;
+ case '\t':
+ fputc('\\', out);
+ fputc('t', out);
+ break;
+ default:
+ fputc(c, out);
+ }
+ }
+}
+
static void drawpage(fz_context *ctx, fz_document *doc, int pagenum)
{
fz_page *page;
@@ -104,6 +184,7 @@ static void drawpage(fz_context *ctx, fz_document *doc, int pagenum)
fz_device *dev = NULL;
int start;
fz_cookie cookie = { 0 };
+ int needshot = 0;
fz_var(list);
fz_var(dev);
@@ -122,6 +203,49 @@ static void drawpage(fz_context *ctx, fz_document *doc, int pagenum)
fz_throw(ctx, "cannot load page %d in file '%s'", pagenum, filename);
}
+ if (mujstest_file)
+ {
+ fz_annot *annot = fz_first_annot(doc, page);
+ if (annot)
+ {
+ fprintf(mujstest_file, "GOTO %d\n", pagenum);
+ needshot = 1;
+ }
+ for (;annot; annot = fz_next_annot(doc, annot))
+ {
+ fz_rect rect = fz_bound_annot(doc, annot);
+ int w = (rect.x1-rect.x0);
+ int h = (rect.y1-rect.y0);
+ int len;
+
+ /* If height is low, assume a single row, and base
+ * the width off that. */
+ if (h < 10)
+ {
+ w = (w+h-1) / (h ? h : 1);
+ h = 1;
+ }
+ /* Otherwise, if width is low, work off height */
+ else if (w < 10)
+ {
+ h = (w+h-1) / (w ? w : 1);
+ w = 1;
+ }
+ else
+ {
+ w = (w+9)/10;
+ h = (h+9)/10;
+ }
+ len = w*h;
+ if (len < 2)
+ len = 2;
+ fprintf(mujstest_file, "%% %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1);
+ fprintf(mujstest_file, "TEXT %d ", ++mujstest_count);
+ escape_string(mujstest_file, len-2, lorem);
+ fprintf(mujstest_file, "\nCLICK %0.2f %0.2f\n", (rect.x0+rect.x1)/2, (rect.y0+rect.y1)/2);
+ }
+ }
+
if (uselist)
{
fz_try(ctx)
@@ -378,6 +502,11 @@ static void drawpage(fz_context *ctx, fz_document *doc, int pagenum)
fz_flush_warnings(ctx);
+ if (mujstest_file && needshot)
+ {
+ fprintf(mujstest_file, "SCREENSHOT\n");
+ }
+
if (cookie.errors)
errored = 1;
}
@@ -444,7 +573,7 @@ int main(int argc, char **argv)
fz_var(doc);
- while ((c = fz_getopt(argc, argv, "lo:p:r:R:ab:dgmtx5G:Iw:h:f")) != -1)
+ while ((c = fz_getopt(argc, argv, "lo:p:r:R:ab:dgmtx5G:Iw:h:fj:")) != -1)
{
switch (c)
{
@@ -466,6 +595,7 @@ int main(int argc, char **argv)
case 'h': height = atof(fz_optarg); break;
case 'f': fit = 1; break;
case 'I': invert++; break;
+ case 'j': mujstest_filename = fz_optarg; break;
default: usage(); break;
}
}
@@ -473,12 +603,20 @@ int main(int argc, char **argv)
if (fz_optind == argc)
usage();
- if (!showtext && !showxml && !showtime && !showmd5 && !showoutline && !output)
+ if (!showtext && !showxml && !showtime && !showmd5 && !showoutline && !output && !mujstest_filename)
{
printf("nothing to do\n");
exit(0);
}
+ if (mujstest_filename)
+ {
+ if (strcmp(mujstest_filename, "-") == 0)
+ mujstest_file = stdout;
+ else
+ mujstest_file = fopen(mujstest_filename, "wb");
+ }
+
ctx = fz_new_context(NULL, NULL, FZ_STORE_DEFAULT);
if (!ctx)
{
@@ -541,8 +679,18 @@ int main(int argc, char **argv)
}
if (fz_needs_password(doc))
+ {
if (!fz_authenticate_password(doc, password))
fz_throw(ctx, "cannot authenticate password: %s", filename);
+ if (mujstest_file)
+ fprintf(mujstest_file, "PASSWORD %s\n", password);
+
+ }
+
+ if (mujstest_file)
+ {
+ fprintf(mujstest_file, "OPEN %s\n", filename);
+ }
if (showxml || showtext == TEXT_XML)
printf("<document name=\"%s\">\n", filename);
@@ -550,7 +698,7 @@ int main(int argc, char **argv)
if (showoutline)
drawoutline(ctx, doc);
- if (showtext || showxml || showtime || showmd5 || output)
+ if (showtext || showxml || showtime || showmd5 || output || mujstest_file)
{
if (fz_optind == argc || !isrange(argv[fz_optind]))
drawrange(ctx, doc, "1-");
@@ -601,6 +749,9 @@ int main(int argc, char **argv)
}
}
+ if (mujstest_file && mujstest_file != stdout)
+ fclose(mujstest_file);
+
fz_free_context(ctx);
return (errored != 0);
}
diff --git a/apps/pdfapp.c b/apps/pdfapp.c
index 50a89610..c02ed3f2 100644
--- a/apps/pdfapp.c
+++ b/apps/pdfapp.c
@@ -82,6 +82,11 @@ void pdfapp_init(fz_context *ctx, pdfapp_t *app)
app->scrh = 480;
app->resolution = 72;
app->ctx = ctx;
+#ifdef _WIN32
+ app->colorspace = fz_device_bgr;
+#else
+ app->colorspace = fz_device_rgb;
+#endif
}
void pdfapp_invert(pdfapp_t *app, fz_bbox rect)
@@ -313,7 +318,8 @@ static void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage, int repai
fz_bbox bbox;
fz_cookie cookie = { 0 };
- wincursor(app, WAIT);
+ if (!app->nowaitcursor)
+ wincursor(app, WAIT);
if (loadpage)
{
@@ -358,11 +364,7 @@ static void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage, int repai
if (app->grayscale)
colorspace = fz_device_gray;
else
-#ifdef _WIN32
- colorspace = fz_device_bgr;
-#else
- colorspace = fz_device_rgb;
-#endif
+ colorspace = app->colorspace;
app->image = fz_new_pixmap_with_bbox(app->ctx, colorspace, bbox);
fz_clear_pixmap_with_value(app->ctx, app->image, 255);
idev = fz_new_draw_device(app->ctx, app->image);
@@ -411,7 +413,7 @@ static void pdfapp_gotouri(pdfapp_t *app, char *uri)
winopenuri(app, uri);
}
-static void pdfapp_gotopage(pdfapp_t *app, int number)
+void pdfapp_gotopage(pdfapp_t *app, int number)
{
app->isediting = 0;
winrepaint(app);
@@ -999,6 +1001,7 @@ void pdfapp_onmouse(pdfapp_t *app, int x, int y, int btn, int modifiers, int sta
fz_link *link;
fz_matrix ctm;
fz_point p;
+ int processed = 0;
p.x = x - app->panx + rect.x0;
p.y = y - app->pany + rect.y0;
@@ -1008,6 +1011,39 @@ void pdfapp_onmouse(pdfapp_t *app, int x, int y, int btn, int modifiers, int sta
p = fz_transform_point(ctm, p);
+ if (btn == 1 && (state == 1 || state == -1))
+ {
+ fz_ui_event event;
+ fz_interactive *idoc = fz_interact(app->doc);
+
+ event.etype = FZ_EVENT_TYPE_POINTER;
+ event.event.pointer.pt = p;
+ if (state == 1)
+ event.event.pointer.ptype = FZ_POINTER_DOWN;
+ else /* state == -1 */
+ event.event.pointer.ptype = FZ_POINTER_UP;
+
+ if (idoc && fz_pass_event(idoc, app->page, &event))
+ {
+ fz_widget *widget;
+
+ widget = fz_get_focussed_widget(idoc);
+
+ if (widget && fz_widget_get_type(widget) == FZ_WIDGET_TYPE_TEXT)
+ {
+ char *text = fz_widget_text_get_text((fz_widget_text *)widget);
+ char *newtext = wintextinput(app, text);
+ if (newtext)
+ fz_widget_text_set_text((fz_widget_text *)widget, newtext);
+ }
+
+ app->nowaitcursor = 1;
+ pdfapp_showpage(app, 1, 1, 1);
+ app->nowaitcursor = 0;
+ processed = 1;
+ }
+ }
+
for (link = app->page_links; link; link = link->next)
{
if (p.x >= link->rect.x0 && p.x <= link->rect.x1)
@@ -1018,7 +1054,7 @@ void pdfapp_onmouse(pdfapp_t *app, int x, int y, int btn, int modifiers, int sta
if (link)
{
wincursor(app, HAND);
- if (btn == 1 && state == 1)
+ if (btn == 1 && state == 1 && !processed)
{
if (link->dest.kind == FZ_LINK_URI)
pdfapp_gotouri(app, link->dest.ld.uri.uri);
@@ -1029,10 +1065,21 @@ void pdfapp_onmouse(pdfapp_t *app, int x, int y, int btn, int modifiers, int sta
}
else
{
- wincursor(app, ARROW);
+ fz_annot *annot;
+ for (annot = fz_first_annot(app->doc, app->page); annot; annot = fz_next_annot(app->doc, annot))
+ {
+ fz_rect rect = fz_bound_annot(app->doc, annot);
+ if (x >= rect.x0 && x < rect.x1)
+ if (y >= rect.y0 && y < rect.y1)
+ break;
+ }
+ if (annot)
+ wincursor(app, CARET);
+ else
+ wincursor(app, ARROW);
}
- if (state == 1)
+ if (state == 1 && !processed)
{
if (btn == 1 && !app->iscopying)
{
@@ -1093,8 +1140,7 @@ void pdfapp_onmouse(pdfapp_t *app, int x, int y, int btn, int modifiers, int sta
if (app->selr.x0 < app->selr.x1 && app->selr.y0 < app->selr.y1)
windocopy(app);
}
- if (app->ispanning)
- app->ispanning = 0;
+ app->ispanning = 0;
}
else if (app->ispanning)
diff --git a/apps/pdfapp.h b/apps/pdfapp.h
index 31ad18e1..30b3d667 100644
--- a/apps/pdfapp.h
+++ b/apps/pdfapp.h
@@ -14,7 +14,7 @@
typedef struct pdfapp_s pdfapp_t;
-enum { ARROW, HAND, WAIT };
+enum { ARROW, HAND, WAIT, CARET };
extern void winwarn(pdfapp_t*, char *s);
extern void winerror(pdfapp_t*, char *s);
@@ -23,6 +23,7 @@ extern void winresize(pdfapp_t*, int w, int h);
extern void winrepaint(pdfapp_t*);
extern void winrepaintsearch(pdfapp_t*);
extern char *winpassword(pdfapp_t*, char *filename);
+extern char *wintextinput(pdfapp_t*, char *inittext);
extern void winopenuri(pdfapp_t*, char *s);
extern void wincursor(pdfapp_t*, int curs);
extern void windocopy(pdfapp_t*);
@@ -46,6 +47,7 @@ struct pdfapp_s
int rotate;
fz_pixmap *image;
int grayscale;
+ fz_colorspace *colorspace;
int invert;
/* current page params */
@@ -87,6 +89,8 @@ struct pdfapp_s
int beyondy;
fz_bbox selr;
+ int nowaitcursor;
+
/* search state */
int isediting;
int searchdir;
@@ -111,6 +115,7 @@ void pdfapp_onkey(pdfapp_t *app, int c);
void pdfapp_onmouse(pdfapp_t *app, int x, int y, int btn, int modifiers, int state);
void pdfapp_oncopy(pdfapp_t *app, unsigned short *ucsbuf, int ucslen);
void pdfapp_onresize(pdfapp_t *app, int w, int h);
+void pdfapp_gotopage(pdfapp_t *app, int number);
void pdfapp_invert(pdfapp_t *app, fz_bbox rect);
void pdfapp_inverthit(pdfapp_t *app);
diff --git a/apps/win_main.c b/apps/win_main.c
index 8f293beb..71cf9203 100644
--- a/apps/win_main.c
+++ b/apps/win_main.c
@@ -28,7 +28,7 @@ static HDC hdc;
static HBRUSH bgbrush;
static HBRUSH shbrush;
static BITMAPINFO *dibinf;
-static HCURSOR arrowcurs, handcurs, waitcurs;
+static HCURSOR arrowcurs, handcurs, waitcurs, caretcurs;
static LRESULT CALLBACK frameproc(HWND, UINT, WPARAM, LPARAM);
static LRESULT CALLBACK viewproc(HWND, UINT, WPARAM, LPARAM);
@@ -117,6 +117,7 @@ int winfilename(wchar_t *buf, int len)
static char pd_filename[256] = "The file is encrypted.";
static char pd_password[256] = "";
+static char td_textinput[1024] = "";
static int pd_okay = 0;
INT CALLBACK
@@ -145,6 +146,32 @@ dlogpassproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
return FALSE;
}
+INT CALLBACK
+dlogtextinput(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch(message)
+ {
+ case WM_INITDIALOG:
+ SetDlgItemTextA(hwnd, 3, td_textinput);
+ return TRUE;
+ case WM_COMMAND:
+ switch(wParam)
+ {
+ case 1:
+ pd_okay = 1;
+ GetDlgItemTextA(hwnd, 3, td_textinput, sizeof td_textinput);
+ EndDialog(hwnd, 1);
+ return TRUE;
+ case 2:
+ pd_okay = 0;
+ EndDialog(hwnd, 1);
+ return TRUE;
+ }
+ break;
+ }
+ return FALSE;
+}
+
char *winpassword(pdfapp_t *app, char *filename)
{
char buf[1024], *s;
@@ -164,6 +191,18 @@ char *winpassword(pdfapp_t *app, char *filename)
return NULL;
}
+char *wintextinput(pdfapp_t *app, char *inittext)
+{
+ int code;
+ strncpy(td_textinput, inittext?inittext:"", sizeof(td_textinput));
+ code = DialogBoxW(NULL, L"IDD_DLOGTEXT", hwndframe, dlogtextinput);
+ if (code <= 0)
+ winerror(app, "cannot create text input dialog");
+ if (pd_okay)
+ return td_textinput;
+ return NULL;
+}
+
INT CALLBACK
dloginfoproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
@@ -316,6 +355,7 @@ void winopen()
arrowcurs = LoadCursor(NULL, IDC_ARROW);
handcurs = LoadCursor(NULL, IDC_HAND);
waitcurs = LoadCursor(NULL, IDC_WAIT);
+ caretcurs = LoadCursor(NULL, IDC_IBEAM);
/* And a background color */
bgbrush = CreateSolidBrush(RGB(0x70,0x70,0x70));
@@ -383,6 +423,8 @@ void wincursor(pdfapp_t *app, int curs)
SetCursor(handcurs);
if (curs == WAIT)
SetCursor(waitcurs);
+ if (curs == CARET)
+ SetCursor(caretcurs);
}
void wintitle(pdfapp_t *app, char *title)
diff --git a/apps/win_res.rc b/apps/win_res.rc
index 0efd1411..ff296133 100644
--- a/apps/win_res.rc
+++ b/apps/win_res.rc
@@ -49,6 +49,16 @@ BEGIN
LTEXT "", 0x27, 60, 125, 100, 10, 0
END
+IDD_DLOGTEXT DIALOG 50, 50, 204, 85
+//STYLE DS_MODALFRAME | WS_POPUP
+CAPTION " MuPDF: fill out form"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ EDITTEXT 3,8,7,183,50,0x1004
+ DEFPUSHBUTTON "Okay",1,89,64,50,14
+ PUSHBUTTON "Cancel",2,147,64,50,14
+END
+
IDD_DLOGABOUT DIALOG 50, 50, 200, 300
STYLE 128 | 0x80000000
CAPTION " About MuPDF "
diff --git a/apps/x11_main.c b/apps/x11_main.c
index 941afcfa..d19b4f8c 100644
--- a/apps/x11_main.c
+++ b/apps/x11_main.c
@@ -75,7 +75,7 @@ static Pixmap xicon, xmask;
static GC xgc;
static XEvent xevt;
static int mapped = 0;
-static Cursor xcarrow, xchand, xcwait;
+static Cursor xcarrow, xchand, xcwait, xccaret;
static int justcopied = 0;
static int dirty = 0;
static int dirtysearch = 0;
@@ -117,6 +117,14 @@ char *winpassword(pdfapp_t *app, char *filename)
return r;
}
+char *wintextinput(pdfapp_t *app, char *inittext)
+{
+ static char buf[256];
+ printf("> [%s] ", inittext);
+ fgets(buf, sizeof buf, stdin);
+ return buf;
+}
+
/*
* X11 magic
*/
@@ -144,6 +152,7 @@ static void winopen(void)
xcarrow = XCreateFontCursor(xdpy, XC_left_ptr);
xchand = XCreateFontCursor(xdpy, XC_hand2);
xcwait = XCreateFontCursor(xdpy, XC_watch);
+ xccaret = XCreateFontCursor(xdpy, XC_xterm);
xbgcolor.red = 0x7000;
xbgcolor.green = 0x7000;
@@ -227,6 +236,7 @@ void cleanup(pdfapp_t *app)
XFreePixmap(xdpy, xicon);
+ XFreeCursor(xdpy, xccaret);
XFreeCursor(xdpy, xcwait);
XFreeCursor(xdpy, xchand);
XFreeCursor(xdpy, xcarrow);
@@ -252,6 +262,8 @@ void wincursor(pdfapp_t *app, int curs)
XDefineCursor(xdpy, xwin, xchand);
if (curs == WAIT)
XDefineCursor(xdpy, xwin, xcwait);
+ if (curs == CARET)
+ XDefineCursor(xdpy, xwin, xccaret);
XFlush(xdpy);
}
diff --git a/cbz/mucbz.c b/cbz/mucbz.c
index 7a6416e1..f557236f 100644
--- a/cbz/mucbz.c
+++ b/cbz/mucbz.c
@@ -458,44 +458,9 @@ cbz_run_page(cbz_document *doc, cbz_page *page, fz_device *dev, fz_matrix ctm, f
fz_fill_image(dev, &image->base, ctm, 1);
}
-/* Document interface wrappers */
-
-static void cbz_close_document_shim(fz_document *doc)
-{
- cbz_close_document((cbz_document*)doc);
-}
-
-static int cbz_count_pages_shim(fz_document *doc)
-{
- return cbz_count_pages((cbz_document*)doc);
-}
-
-static fz_page *cbz_load_page_shim(fz_document *doc, int number)
-{
- return (fz_page*) cbz_load_page((cbz_document*)doc, number);
-}
-
-static fz_rect cbz_bound_page_shim(fz_document *doc, fz_page *page)
-{
- return cbz_bound_page((cbz_document*)doc, (cbz_page*)page);
-}
-
-static void cbz_run_page_shim(fz_document *doc, fz_page *page, fz_device *dev, fz_matrix transform, fz_cookie *cookie)
-{
- cbz_run_page((cbz_document*)doc, (cbz_page*)page, dev, transform, cookie);
-}
-
-static void cbz_free_page_shim(fz_document *doc, fz_page *page)
-{
- cbz_free_page((cbz_document*)doc, (cbz_page*)page);
-}
-
-static int cbz_meta(fz_document *doc_, int key, void *ptr, int size)
+static int
+cbz_meta(cbz_document *doc, int key, void *ptr, int size)
{
- cbz_document *doc = (cbz_document *)doc_;
-
- doc = doc;
-
switch(key)
{
case FZ_META_FORMAT_INFO:
@@ -509,15 +474,11 @@ static int cbz_meta(fz_document *doc_, int key, void *ptr, int size)
static void
cbz_init_document(cbz_document *doc)
{
- doc->super.close = cbz_close_document_shim;
- doc->super.needs_password = NULL;
- doc->super.authenticate_password = NULL;
- doc->super.load_outline = NULL;
- doc->super.count_pages = cbz_count_pages_shim;
- doc->super.load_page = cbz_load_page_shim;
- doc->super.load_links = NULL;
- doc->super.bound_page = cbz_bound_page_shim;
- doc->super.run_page = cbz_run_page_shim;
- doc->super.free_page = cbz_free_page_shim;
- doc->super.meta = cbz_meta;
+ doc->super.close = (void*)cbz_close_document;
+ doc->super.count_pages = (void*)cbz_count_pages;
+ doc->super.load_page = (void*)cbz_load_page;
+ doc->super.bound_page = (void*)cbz_bound_page;
+ doc->super.run_page = (void*)cbz_run_page;
+ doc->super.free_page = (void*)cbz_free_page;
+ doc->super.meta = (void*)cbz_meta;
}
diff --git a/fitz/doc_document.c b/fitz/doc_document.c
index 1233aeb9..f60f3468 100644
--- a/fitz/doc_document.c
+++ b/fitz/doc_document.c
@@ -108,6 +108,30 @@ fz_bound_page(fz_document *doc, fz_page *page)
return fz_empty_rect;
}
+fz_annot *
+fz_first_annot(fz_document *doc, fz_page *page)
+{
+ if (doc && doc->first_annot && page)
+ return doc->first_annot(doc, page);
+ return NULL;
+}
+
+fz_annot *
+fz_next_annot(fz_document *doc, fz_annot *annot)
+{
+ if (doc && doc->next_annot && annot)
+ return doc->next_annot(doc, annot);
+ return NULL;
+}
+
+fz_rect
+fz_bound_annot(fz_document *doc, fz_annot *annot)
+{
+ if (doc && doc->bound_annot && annot)
+ return doc->bound_annot(doc, annot);
+ return fz_empty_rect;
+}
+
void
fz_run_page(fz_document *doc, fz_page *page, fz_device *dev, fz_matrix transform, fz_cookie *cookie)
{
@@ -130,6 +154,13 @@ fz_meta(fz_document *doc, int key, void *ptr, int size)
return FZ_META_UNKNOWN_KEY;
}
+fz_interactive *fz_interact(fz_document *doc)
+{
+ if (doc && doc->interact)
+ return doc->interact(doc);
+ return NULL;
+}
+
void
fz_write_document(fz_document *doc, char *filename, fz_write_options *opts)
{
diff --git a/fitz/doc_interactive.c b/fitz/doc_interactive.c
new file mode 100644
index 00000000..eccb9ffd
--- /dev/null
+++ b/fitz/doc_interactive.c
@@ -0,0 +1,22 @@
+#include "fitz.h"
+#include "mupdf-internal.h"
+
+/*
+ PDF is currently the only interactive format, so no need
+ to indirect through function pointers.
+*/
+
+int fz_pass_event(fz_interactive *idoc, fz_page *page, fz_ui_event *ui_event)
+{
+ return pdf_pass_event((pdf_document*)idoc, (pdf_page*)page, ui_event);
+}
+
+fz_rect *fz_get_screen_update(fz_interactive *idoc)
+{
+ return pdf_get_screen_update((pdf_document*)idoc);
+}
+
+fz_widget *fz_get_focussed_widget(fz_interactive *idoc)
+{
+ return pdf_get_focussed_widget((pdf_document*)idoc);
+}
diff --git a/fitz/fitz-internal.h b/fitz/fitz-internal.h
index 6fc40f49..7130460a 100644
--- a/fitz/fitz-internal.h
+++ b/fitz/fitz-internal.h
@@ -411,6 +411,16 @@ void fz_grow_buffer(fz_context *ctx, fz_buffer *buf);
*/
void fz_trim_buffer(fz_context *ctx, fz_buffer *buf);
+/*
+ fz_buffer_cat: Concatenate buffers
+
+ buf: first to concatenate and the holder of the result
+ extra: second to concatenate
+
+ May throw exception on failure to allocate.
+*/
+void fz_buffer_cat(fz_context *ctx, fz_buffer *buf, fz_buffer *extra);
+
void fz_write_buffer(fz_context *ctx, fz_buffer *buf, unsigned char *data, int len);
void fz_write_buffer_byte(fz_context *ctx, fz_buffer *buf, int val);
@@ -424,7 +434,14 @@ void fz_write_buffer_pad(fz_context *ctx, fz_buffer *buf);
grow, but the caller must ensure that no more than 256 bytes are
added to the buffer per call.
*/
-void fz_buffer_printf(fz_context *ctx, fz_buffer *buffer, char *fmt, ...);
+void fz_buffer_printf(fz_context *ctx, fz_buffer *buffer, const char *fmt, ...);
+
+/*
+ fz_buffer_printf: print a string formatted as a pdf string to a buffer.
+ The buffer will grow.
+*/
+void
+fz_buffer_cat_pdf_string(fz_context *ctx, fz_buffer *buffer, const char *text);
struct fz_stream_s
{
@@ -841,6 +858,11 @@ fz_pixmap *fz_render_stroked_glyph(fz_context *ctx, fz_font*, int, fz_matrix, fz
void fz_render_t3_glyph_direct(fz_context *ctx, fz_device *dev, fz_font *font, int gid, fz_matrix trm, void *gstate);
/*
+ * Form widgets
+ */
+void fz_free_widget(fz_context *ctx, fz_widget *widget);
+
+/*
* Text buffer.
*
* The trm field contains the a, b, c and d coefficients.
@@ -1096,7 +1118,11 @@ struct fz_document_s
void (*run_page)(fz_document *doc, fz_page *page, fz_device *dev, fz_matrix transform, fz_cookie *cookie);
void (*free_page)(fz_document *doc, fz_page *page);
int (*meta)(fz_document *doc, int key, void *ptr, int size);
+ fz_interactive *(*interact)(fz_document *doc);
void (*write)(fz_document *doc, char *filename, fz_write_options *opts);
+ fz_annot *(*first_annot)(fz_document *doc, fz_page *page);
+ fz_annot *(*next_annot)(fz_document *doc, fz_annot *annot);
+ fz_rect (*bound_annot)(fz_document *doc, fz_annot *annot);
};
#endif
diff --git a/fitz/fitz.h b/fitz/fitz.h
index ba7088ae..520058b1 100644
--- a/fitz/fitz.h
+++ b/fitz/fitz.h
@@ -2212,6 +2212,32 @@ fz_link *fz_load_links(fz_document *doc, fz_page *page);
fz_rect fz_bound_page(fz_document *doc, fz_page *page);
/*
+ fz_annot: opaque pointer to annotation details.
+*/
+typedef struct fz_annot_s fz_annot;
+
+/*
+ fz_first_annot: Return a pointer to the first annotation on a page.
+
+ Does not throw exceptions.
+*/
+fz_annot *fz_first_annot(fz_document *doc, fz_page *page);
+
+/*
+ fz_next_annot: Return a pointer to the next annotation on a page.
+
+ Does not throw exceptions.
+*/
+fz_annot *fz_next_annot(fz_document *doc, fz_annot *annot);
+
+/*
+ fz_bound_annot: Return the bounding rectangle of the annotation.
+
+ Does not throw exceptions.
+*/
+fz_rect fz_bound_annot(fz_document *doc, fz_annot *annot);
+
+/*
fz_run_page: Run a page through a device.
page: Page obtained from fz_load_page.
@@ -2311,6 +2337,137 @@ enum
FZ_META_INFO = 4,
};
+
+/* Interactive features */
+
+/* Types of widget */
+enum
+{
+ FZ_WIDGET_TYPE_PUSHBUTTON,
+ FZ_WIDGET_TYPE_CHECKBOX,
+ FZ_WIDGET_TYPE_RADIOBUTTON,
+ FZ_WIDGET_TYPE_TEXT,
+ FZ_WIDGET_TYPE_LISTBOX,
+ FZ_WIDGET_TYPE_COMBOBOX
+};
+
+/* Types of UI event */
+enum
+{
+ FZ_EVENT_TYPE_POINTER,
+};
+
+/* Types of pointer event */
+enum
+{
+ FZ_POINTER_DOWN,
+ FZ_POINTER_UP,
+};
+
+/*
+ Interface supported by some types of documents,
+ via which interactions (such as filling in forms)
+ can be achieved.
+*/
+typedef struct fz_interactive_s fz_interactive;
+
+/*
+ UI events that can be passed to an interactive document.
+*/
+typedef struct fz_ui_event_s
+{
+ int etype;
+ union
+ {
+ struct
+ {
+ int ptype;
+ fz_point pt;
+ } pointer;
+ } event;
+} fz_ui_event;
+
+/*
+ Widgets that may appear in PDF forms
+*/
+typedef struct fz_widget_s fz_widget;
+
+/*
+ Specific types of widget
+*/
+typedef struct fz_widget_text_s fz_widget_text;
+
+/*
+ Obtain an interface for interaction from a document.
+ For document types that don't support interaction, NULL
+ is returned.
+*/
+fz_interactive *fz_interact(fz_document *doc);
+
+/*
+ fz_pass_event: Pass a UI event to an interactive
+ document.
+
+ Returns a boolean indication of whether the ui_event was
+ handled. Example of use for the return value: when considering
+ passing the events that make up a drag, if the down event isn't
+ accepted then don't send the move events or the up event.
+*/
+int fz_pass_event(fz_interactive *idoc, fz_page *page, fz_ui_event *ui_event);
+
+/*
+ fz_ui_event_pointer: Set up a pointer event
+*/
+void fz_ui_event_pointer(fz_ui_event *event, int type, float x, float y);
+
+/*
+ fz_get_screen_update: Get the bounding box of an area needing
+ update because of a visual change.
+
+ After a sequence of interactions with a document that may cause
+ it to change in appearance - such as passing ui events - this
+ method should be called repeatedly until it returns NULL, to
+ enumerate the changed areas for which screen updates are
+ needed.
+*/
+fz_rect *fz_get_screen_update(fz_interactive *idoc);
+
+/*
+ fz_get_focussed_widget: returns the currently focussed widget
+
+ Widgets can become focussed as a result of passing in ui events.
+ NULL is returned if there is no currently focussed widget. An
+ app may wish to create a native representative of the focussed
+ widget, e.g., to collect the text for a text widget, rather than
+ routing key strokes through fz_pass_event.
+*/
+fz_widget *fz_get_focussed_widget(fz_interactive *idoc);
+
+/*
+ fz_widget_get_type: find out the type of a widget.
+
+ The type determines what widget subclass the widget
+ can safely be cast to.
+*/
+int fz_widget_get_type(fz_widget *widget);
+
+/*
+ fz_widget_get_bbox: get the bounding box of a widget.
+*/
+fz_bbox *fz_widget_get_bbox(fz_widget *widget);
+
+/*
+ fz_widget_text_get_text: Get the text currently displayed in
+ a text widget.
+*/
+char *fz_widget_text_get_text(fz_widget_text *tw);
+
+/*
+ fz_widget_text_set_text: Update the text of a text widget.
+*/
+void fz_widget_text_set_text(fz_widget_text *tw, char *text);
+
+
typedef struct fz_write_options_s fz_write_options;
/*
diff --git a/fitz/stm_buffer.c b/fitz/stm_buffer.c
index 706be39a..385817b4 100644
--- a/fitz/stm_buffer.c
+++ b/fitz/stm_buffer.c
@@ -91,6 +91,19 @@ fz_buffer_storage(fz_context *ctx, fz_buffer *buf, unsigned char **datap)
return (buf ? buf->len : 0);
}
+void
+fz_buffer_cat(fz_context *ctx, fz_buffer *buf, fz_buffer *extra)
+{
+ if (buf->cap - buf->len < extra->len)
+ {
+ buf->data = fz_resize_array(ctx, buf->data, buf->len + extra->len, 1);
+ buf->cap = buf->len + extra->len;
+ }
+
+ memcpy(buf->data + buf->len, extra->data, extra->len);
+ buf->len += extra->len;
+}
+
void fz_write_buffer(fz_context *ctx, fz_buffer *buf, unsigned char *data, int len)
{
if (buf->len + len > buf->cap)
@@ -175,7 +188,7 @@ void fz_write_buffer_pad(fz_context *ctx, fz_buffer *buf)
}
void
-fz_buffer_printf(fz_context *ctx, fz_buffer *buffer, char *fmt, ...)
+fz_buffer_printf(fz_context *ctx, fz_buffer *buffer, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
@@ -189,6 +202,82 @@ fz_buffer_printf(fz_context *ctx, fz_buffer *buffer, char *fmt, ...)
va_end(args);
}
+void
+fz_buffer_cat_pdf_string(fz_context *ctx, fz_buffer *buffer, const char *text)
+{
+ int len = 2;
+ const char *s = text;
+ char *d;
+ char c;
+
+ while ((c = *s++) != 0)
+ {
+ switch (c)
+ {
+ case '\n':
+ case '\r':
+ case '\t':
+ case '\b':
+ case '\f':
+ case '(':
+ case ')':
+ case '\\':
+ len++;
+ break;
+ }
+ len++;
+ }
+
+ while(buffer->cap - buffer->len < len)
+ fz_grow_buffer(ctx, buffer);
+
+ s = text;
+ d = (char *)buffer->data + buffer->len;
+ *d++ = '(';
+ while ((c = *s++) != 0)
+ {
+ switch (c)
+ {
+ case '\n':
+ *d++ = '\\';
+ *d++ = 'n';
+ break;
+ case '\r':
+ *d++ = '\\';
+ *d++ = 'r';
+ break;
+ case '\t':
+ *d++ = '\\';
+ *d++ = 't';
+ break;
+ case '\b':
+ *d++ = '\\';
+ *d++ = 'b';
+ break;
+ case '\f':
+ *d++ = '\\';
+ *d++ = 'f';
+ break;
+ case '(':
+ *d++ = '\\';
+ *d++ = '(';
+ break;
+ case ')':
+ *d++ = '\\';
+ *d++ = ')';
+ break;
+ case '\\':
+ *d++ = '\\';
+ *d++ = '\\';
+ break;
+ default:
+ *d++ = c;
+ }
+ }
+ *d++ = ')';
+ buffer->len += len;
+}
+
#ifdef TEST_BUFFER_WRITE
#define TEST_LEN 1024
diff --git a/pdf/mupdf-internal.h b/pdf/mupdf-internal.h
index b2097691..018565d6 100644
--- a/pdf/mupdf-internal.h
+++ b/pdf/mupdf-internal.h
@@ -165,6 +165,7 @@ struct pdf_xref_entry_s
typedef struct pdf_crypt_s pdf_crypt;
typedef struct pdf_ocg_descriptor_s pdf_ocg_descriptor;
typedef struct pdf_ocg_entry_s pdf_ocg_entry;
+typedef struct pdf_hotspot_s pdf_hotspot;
struct pdf_ocg_entry_s
{
@@ -180,6 +181,21 @@ struct pdf_ocg_descriptor_s
pdf_obj *intent;
};
+enum
+{
+ HOTSPOT_POINTER_DOWN = 0x1,
+ HOTSPOT_POINTER_OVER = 0x2
+};
+
+struct pdf_hotspot_s
+{
+ int num;
+ int gen;
+ int state;
+};
+
+typedef struct pdf_js_s pdf_js;
+
struct pdf_document_s
{
fz_document super;
@@ -193,6 +209,7 @@ struct pdf_document_s
pdf_crypt *crypt;
pdf_obj *trailer;
pdf_ocg_descriptor *ocg;
+ pdf_hotspot hotspot;
int len;
pdf_xref_entry *table;
@@ -203,6 +220,10 @@ struct pdf_document_s
pdf_obj **page_refs;
pdf_lexbuf_large lexbuf;
+
+ fz_widget *focus;
+
+ pdf_js *js;
};
pdf_document *pdf_open_document_no_run(fz_context *ctx, const char *filename);
@@ -311,8 +332,9 @@ pdf_xobject *pdf_load_xobject(pdf_document *doc, pdf_obj *obj);
pdf_obj *pdf_new_xobject(pdf_document *doc, fz_rect *bbox, fz_matrix *mat);
pdf_xobject *pdf_keep_xobject(fz_context *ctx, pdf_xobject *xobj);
void pdf_drop_xobject(fz_context *ctx, pdf_xobject *xobj);
-void pdf_update_xobject_contents(pdf_document *xref, pdf_xobject *from, fz_buffer *buffer);
+void pdf_update_xobject_contents(pdf_document *xref, pdf_xobject *form, fz_buffer *buffer);
+void pdf_update_appearance(pdf_document *doc, pdf_obj *obj);
/*
* CMap
@@ -501,17 +523,18 @@ pdf_font_desc *pdf_keep_font(fz_context *ctx, pdf_font_desc *fontdesc);
void pdf_drop_font(fz_context *ctx, pdf_font_desc *font);
void pdf_print_font(fz_context *ctx, pdf_font_desc *fontdesc);
+fz_rect pdf_measure_text(fz_context *ctx, pdf_font_desc *fontdesc, unsigned char *buf, int len);
+float pdf_text_stride(fz_context *ctx, pdf_font_desc *fontdesc, float fontsize, unsigned char *buf, int len, float room, int *count);
/*
* Interactive features
*/
-typedef struct pdf_annot_s pdf_annot;
-
struct pdf_annot_s
{
pdf_obj *obj;
fz_rect rect;
+ fz_rect pagerect;
pdf_xobject *ap;
fz_matrix matrix;
pdf_annot *next;
@@ -525,9 +548,17 @@ pdf_obj *pdf_load_name_tree(pdf_document *doc, char *which);
fz_link *pdf_load_link_annots(pdf_document *, pdf_obj *annots, fz_matrix page_ctm);
-pdf_annot *pdf_load_annots(pdf_document *, pdf_obj *annots);
+pdf_annot *pdf_load_annots(pdf_document *, pdf_obj *annots, fz_matrix page_ctm);
void pdf_free_annot(fz_context *ctx, pdf_annot *link);
+char *pdf_field_getValue(pdf_document *doc, pdf_obj *field);
+void pdf_field_setValue(pdf_document *doc, pdf_obj *field, char *text);
+char *pdf_field_getBorderStyle(pdf_document *doc, pdf_obj *field);
+void pdf_field_setBorderStyle(pdf_document *doc, pdf_obj *field, char *text);
+void pdf_field_buttonSetCaption(pdf_document *doc, pdf_obj *field, char *text);
+void pdf_field_setFillColor(pdf_document *doc, pdf_obj *field, pdf_obj *col);
+void pdf_field_setTextColor(pdf_document *doc, pdf_obj *field, pdf_obj *col);
+
/*
* Page tree, pages and related objects
*/
@@ -553,9 +584,60 @@ void pdf_run_glyph(pdf_document *doc, pdf_obj *resources, fz_buffer *contents, f
/*
* PDF interface to store
*/
-
void pdf_store_item(fz_context *ctx, pdf_obj *key, void *val, unsigned int itemsize);
void *pdf_find_item(fz_context *ctx, fz_store_free_fn *free, pdf_obj *key);
void pdf_remove_item(fz_context *ctx, fz_store_free_fn *free, pdf_obj *key);
+/*
+ * PDF interaction interface
+ */
+int pdf_pass_event(pdf_document *doc, pdf_page *page, fz_ui_event *ui_event);
+fz_rect *pdf_get_screen_update(pdf_document *doc);
+fz_widget *pdf_get_focussed_widget(pdf_document *doc);
+
+/*
+ * Javascript handler
+ */
+pdf_js *pdf_new_js(pdf_document *doc);
+void pdf_drop_js(pdf_js *js);
+void pdf_js_setup_event(pdf_js *js, pdf_obj *target);
+char *pdf_js_getEventValue(pdf_js *js);
+void pdf_js_execute(pdf_js *js, char *code);
+void pdf_js_execute_count(pdf_js *js, char *code, int count);
+
+/*
+ * Javascript engine interface
+ */
+typedef struct pdf_jsimp_s pdf_jsimp;
+typedef struct pdf_jsimp_type_s pdf_jsimp_type;
+typedef struct pdf_jsimp_obj_s pdf_jsimp_obj;
+
+typedef void (pdf_jsimp_dtr)(void *jsctx, void *obj);
+typedef pdf_jsimp_obj *(pdf_jsimp_method)(void *jsctx, void *obj, int argc, pdf_jsimp_obj *args[]);
+typedef pdf_jsimp_obj *(pdf_jsimp_getter)(void *jsctx, void *obj);
+typedef void (pdf_jsimp_setter)(void *jsctx, void *obj, pdf_jsimp_obj *val);
+
+pdf_jsimp *pdf_new_jsimp(fz_context *ctx, void *jsctx);
+void pdf_drop_jsimp(pdf_jsimp *imp);
+
+pdf_jsimp_type *pdf_jsimp_new_type(pdf_jsimp *imp, pdf_jsimp_dtr *dtr);
+void pdf_jsimp_drop_type(pdf_jsimp *imp, pdf_jsimp_type *type);
+void pdf_jsimp_addmethod(pdf_jsimp *imp, pdf_jsimp_type *type, char *name, pdf_jsimp_method *meth);
+void pdf_jsimp_addproperty(pdf_jsimp *imp, pdf_jsimp_type *type, char *name, pdf_jsimp_getter *get, pdf_jsimp_setter *set);
+void pdf_jsimp_set_global_type(pdf_jsimp *imp, pdf_jsimp_type *type);
+
+pdf_jsimp_obj *pdf_jsimp_new_obj(pdf_jsimp *imp, pdf_jsimp_type *type, void *obj);
+void pdf_jsimp_drop_obj(pdf_jsimp *imp, pdf_jsimp_obj *obj);
+
+pdf_jsimp_obj *pdf_jsimp_fromString(pdf_jsimp *imp, char *str);
+char *pdf_jsimp_toString(pdf_jsimp *imp, pdf_jsimp_obj *obj);
+
+double pdf_jsimp_toNumber(pdf_jsimp *imp, pdf_jsimp_obj *obj);
+
+int pdf_jsimp_array_len(pdf_jsimp *imp, pdf_jsimp_obj *obj);
+pdf_jsimp_obj *pdf_jsimp_array_item(pdf_jsimp *imp, pdf_jsimp_obj *obj, int i);
+
+void pdf_jsimp_execute(pdf_jsimp *imp, char *code);
+void pdf_jsimp_execute_count(pdf_jsimp *imp, char *code, int count);
+
#endif
diff --git a/pdf/mupdf.h b/pdf/mupdf.h
index b3807852..c2566c02 100644
--- a/pdf/mupdf.h
+++ b/pdf/mupdf.h
@@ -232,6 +232,29 @@ fz_rect pdf_bound_page(pdf_document *doc, pdf_page *page);
*/
void pdf_free_page(pdf_document *doc, pdf_page *page);
+typedef struct pdf_annot_s pdf_annot;
+
+/*
+ pdf_first_annot: Return the first annotation on a page.
+
+ Does not throw exceptions.
+*/
+pdf_annot *pdf_first_annot(pdf_document *doc, pdf_page *page);
+
+/*
+ pdf_next_annot: Return the next annotation on a page.
+
+ Does not throw exceptions.
+*/
+pdf_annot *pdf_next_annot(pdf_document *doc, pdf_annot *annot);
+
+/*
+ pdf_bound_annot: Return the rectangle for an annotation on a page.
+
+ Does not throw exceptions.
+*/
+fz_rect pdf_bound_annot(pdf_document *doc, pdf_annot *annot);
+
/*
pdf_run_page: Interpret a loaded page and render it on a device.
diff --git a/pdf/pdf_annot.c b/pdf/pdf_annot.c
index f6c70f90..02f91e33 100644
--- a/pdf/pdf_annot.c
+++ b/pdf/pdf_annot.c
@@ -352,11 +352,10 @@ pdf_transform_annot(pdf_annot *annot)
}
pdf_annot *
-pdf_load_annots(pdf_document *xref, pdf_obj *annots)
+pdf_load_annots(pdf_document *xref, pdf_obj *annots, fz_matrix page_ctm)
{
pdf_annot *annot, *head, *tail;
pdf_obj *obj, *ap, *as, *n, *rect;
- pdf_xobject *form;
int i, len;
fz_context *ctx = xref->ctx;
@@ -368,47 +367,83 @@ pdf_load_annots(pdf_document *xref, pdf_obj *annots)
{
obj = pdf_array_get(annots, i);
+ pdf_update_appearance(xref, obj);
+
rect = pdf_dict_gets(obj, "Rect");
ap = pdf_dict_gets(obj, "AP");
as = pdf_dict_gets(obj, "AS");
+
if (pdf_is_dict(ap))
{
- n = pdf_dict_gets(ap, "N"); /* normal state */
+ pdf_hotspot *hp = &xref->hotspot;
+
+ n = NULL;
+
+ if (hp->num == pdf_to_num(obj)
+ && hp->gen == pdf_to_gen(obj)
+ && (hp->state & HOTSPOT_POINTER_DOWN))
+ {
+ n = pdf_dict_gets(ap, "D"); /* down state */
+ }
+
+ if (n == NULL)
+ n = pdf_dict_gets(ap, "N"); /* normal state */
/* lookup current state in sub-dictionary */
if (!pdf_is_stream(xref, pdf_to_num(n), pdf_to_gen(n)))
n = pdf_dict_get(n, as);
+
+ annot = fz_malloc_struct(ctx, pdf_annot);
+ annot->obj = pdf_keep_obj(obj);
+ annot->rect = pdf_to_rect(ctx, rect);
+ annot->pagerect = fz_transform_rect(page_ctm, annot->rect);
+ annot->ap = NULL;
+
if (pdf_is_stream(xref, pdf_to_num(n), pdf_to_gen(n)))
{
fz_try(ctx)
{
- form = pdf_load_xobject(xref, n);
+ annot->ap = pdf_load_xobject(xref, n);
+ pdf_transform_annot(annot);
}
fz_catch(ctx)
{
fz_warn(ctx, "ignoring broken annotation");
- continue;
}
+ }
- annot = fz_malloc_struct(ctx, pdf_annot);
- annot->obj = pdf_keep_obj(obj);
- annot->rect = pdf_to_rect(ctx, rect);
- annot->ap = form;
- annot->next = NULL;
-
- pdf_transform_annot(annot);
+ annot->next = NULL;
- if (!head)
- head = tail = annot;
- else
- {
- tail->next = annot;
- tail = annot;
- }
+ if (!head)
+ head = tail = annot;
+ else
+ {
+ tail->next = annot;
+ tail = annot;
}
}
}
return head;
}
+
+pdf_annot *
+pdf_first_annot(pdf_document *doc, pdf_page *page)
+{
+ return page ? page->annots : NULL;
+}
+
+pdf_annot *
+pdf_next_annot(pdf_document *doc, pdf_annot *annot)
+{
+ return annot ? annot->next : NULL;
+}
+
+fz_rect
+pdf_bound_annot(pdf_document *doc, pdf_annot *annot)
+{
+ if (annot)
+ return annot->pagerect;
+ return fz_empty_rect;
+}
diff --git a/pdf/pdf_font.c b/pdf/pdf_font.c
index b94cbb78..f8fb643c 100644
--- a/pdf/pdf_font.c
+++ b/pdf/pdf_font.c
@@ -1175,3 +1175,53 @@ pdf_print_font(fz_context *ctx, pdf_font_desc *fontdesc)
printf("\t}\n");
}
}
+
+fz_rect pdf_measure_text(fz_context *ctx, pdf_font_desc *fontdesc, unsigned char *buf, int len)
+{
+ pdf_hmtx h;
+ int gid;
+ int i;
+ float x = 0.0;
+ fz_rect acc = fz_empty_rect;
+ fz_rect bbox;
+
+ for (i = 0; i < len; i++)
+ {
+ gid = pdf_font_cid_to_gid(ctx, fontdesc, buf[i]);
+ h = pdf_lookup_hmtx(ctx, fontdesc, buf[i]);
+ bbox = fz_bound_glyph(ctx, fontdesc->font, gid, fz_identity);
+ bbox.x0 += x;
+ bbox.x1 += x;
+ acc = fz_union_rect(acc, bbox);
+ x += h.w / 1000.0;
+ }
+
+ return acc;
+}
+
+float pdf_text_stride(fz_context *ctx, pdf_font_desc *fontdesc, float fontsize, unsigned char *buf, int len, float room, int *count)
+{
+ pdf_hmtx h;
+ int gid;
+ int i;
+ float x = 0.0;
+
+ for (i = 0; i < len && x <= room; i++)
+ {
+ gid = pdf_font_cid_to_gid(ctx, fontdesc, buf[i]);
+ h = pdf_lookup_hmtx(ctx, fontdesc, buf[i]);
+
+ x += h.w * fontsize / 1000.0;
+ }
+
+ if (x > room)
+ {
+ i --;
+ x -= h.w * fontsize / 1000.0;
+ }
+
+ if (count)
+ *count = i;
+
+ return x;
+}
diff --git a/pdf/pdf_form.c b/pdf/pdf_form.c
new file mode 100644
index 00000000..dbc3a326
--- /dev/null
+++ b/pdf/pdf_form.c
@@ -0,0 +1,1935 @@
+#include "fitz-internal.h"
+#include "mupdf-internal.h"
+
+#define MATRIX_COEFS (6)
+
+enum
+{
+ Ff_Multiline = 1 << (13-1),
+ Ff_Password = 1 << (14-1),
+ Ff_NoToggleToOff = 1 << (15-1),
+ Ff_Radio = 1 << (16-1),
+ Ff_Pushbutton = 1 << (17-1),
+ Ff_Combo = 1 << (18-1),
+ Ff_FileSelect = 1 << (21-1),
+ Ff_DoNotSpellCheck = 1 << (23-1),
+ Ff_DoNotScroll = 1 << (24-1),
+ Ff_Comb = 1 << (25-1),
+ Ff_RadioInUnison = 1 << (26-1)
+};
+
+enum
+{
+ BS_Solid,
+ BS_Dashed,
+ BS_Beveled,
+ BS_Inset,
+ BS_Underline
+};
+
+enum
+{
+ Q_Left = 0,
+ Q_Cent = 1,
+ Q_Right = 2
+};
+
+struct fz_widget_s
+{
+ pdf_document *doc;
+ int type;
+ pdf_obj *obj;
+};
+
+struct fz_widget_text_s
+{
+ fz_widget super;
+ char *text;
+};
+
+typedef struct da_info_s
+{
+ char *font_name;
+ int font_size;
+ float col[4];
+ int col_size;
+} da_info;
+
+typedef struct font_info_s
+{
+ da_info da_rec;
+ pdf_font_desc *font;
+} font_info;
+
+typedef struct text_widget_info_s
+{
+ pdf_obj *dr;
+ font_info font_rec;
+ int q;
+ int multiline;
+ int comb;
+ int max_len;
+} text_widget_info;
+
+static const char *fmt_re = "%f %f %f %f re\n";
+static const char *fmt_f = "f\n";
+static const char *fmt_s = "s\n";
+static const char *fmt_g = "%f g\n";
+static const char *fmt_m = "%f %f m\n";
+static const char *fmt_l = "%f %f l\n";
+static const char *fmt_w = "%f w\n";
+static const char *fmt_Tx_BMC = "/Tx BMC\n";
+static const char *fmt_q = "q\n";
+static const char *fmt_W = "W\n";
+static const char *fmt_n = "n\n";
+static const char *fmt_BT = "BT\n";
+static const char *fmt_Tm = "%1.2f %1.2f %1.2f %1.2f %1.2f %1.2f Tm\n";
+static const char *fmt_Td = "%f %f Td\n";
+static const char *fmt_Tj = " Tj\n";
+static const char *fmt_ET = "ET\n";
+static const char *fmt_Q = "Q\n";
+static const char *fmt_EMC = "EMC\n";
+
+static void account_for_rot(fz_rect *rect, fz_matrix *mat, int rot)
+{
+ float width = rect->x1;
+ float height = rect->y1;
+
+ switch (rot)
+ {
+ default:
+ *mat = fz_identity;
+ break;
+ case 90:
+ *mat = fz_concat(fz_rotate(rot), fz_translate(width, 0));
+ rect->x1 = height;
+ rect->y1 = width;
+ break;
+ case 180:
+ *mat = fz_concat(fz_rotate(rot), fz_translate(width, height));
+ break;
+ case 270:
+ *mat = fz_concat(fz_rotate(rot), fz_translate(0, height));
+ rect->x1 = height;
+ rect->y1 = width;
+ break;
+ }
+}
+
+static pdf_obj *get_inheritable(pdf_document *doc, pdf_obj *obj, char *key)
+{
+ pdf_obj *fobj = NULL;
+
+ while (!fobj && obj)
+ {
+ fobj = pdf_dict_gets(obj, key);
+
+ if (!fobj)
+ obj = pdf_dict_gets(obj, "Parent");
+ }
+
+ return fobj ? fobj
+ : pdf_dict_gets(pdf_dict_gets(pdf_dict_gets(doc->trailer, "Root"), "AcroForm"), key);
+}
+
+static char *get_string_or_stream(pdf_document *doc, pdf_obj *obj)
+{
+ fz_context *ctx = doc->ctx;
+ int len = 0;
+ char *buf = NULL;
+ fz_buffer *strmbuf = NULL;
+ char *text = NULL;
+
+ fz_var(strmbuf);
+ fz_var(text);
+ fz_try(ctx)
+ {
+ if (pdf_is_string(obj))
+ {
+ len = pdf_to_str_len(obj);
+ buf = pdf_to_str_buf(obj);
+ }
+ else if (pdf_is_stream(doc, pdf_to_num(obj), pdf_to_gen(obj)))
+ {
+ strmbuf = pdf_load_stream(doc, pdf_to_num(obj), pdf_to_gen(obj));
+ len = fz_buffer_storage(ctx, strmbuf, (unsigned char **)&buf);
+ }
+
+ if (buf)
+ {
+ text = fz_malloc(ctx, len+1);
+ memcpy(text, buf, len);
+ text[len] = 0;
+ }
+ }
+ fz_always(ctx)
+ {
+ fz_drop_buffer(ctx, strmbuf);
+ }
+ fz_catch(ctx)
+ {
+ fz_free(ctx, text);
+ fz_rethrow(ctx);
+ }
+
+ return text;
+}
+
+static char *get_field_type_name(pdf_document *doc, pdf_obj *obj)
+{
+ return pdf_to_name(get_inheritable(doc, obj, "FT"));
+}
+
+static int get_field_flags(pdf_document *doc, pdf_obj *obj)
+{
+ return pdf_to_int(get_inheritable(doc, obj, "Ff"));
+}
+
+static int get_field_type(pdf_document *doc, pdf_obj *obj)
+{
+ char *type = get_field_type_name(doc, obj);
+ int flags = get_field_flags(doc, obj);
+
+ if (!strcmp(type, "Btn"))
+ {
+ if (flags & Ff_Pushbutton)
+ return FZ_WIDGET_TYPE_PUSHBUTTON;
+ else if (flags & Ff_Radio)
+ return FZ_WIDGET_TYPE_RADIOBUTTON;
+ else
+ return FZ_WIDGET_TYPE_CHECKBOX;
+ }
+ else if (!strcmp(type, "Tx"))
+ return FZ_WIDGET_TYPE_TEXT;
+ else if (!strcmp(type, "Ch"))
+ {
+ if (flags & Ff_Combo)
+ return FZ_WIDGET_TYPE_COMBOBOX;
+ else
+ return FZ_WIDGET_TYPE_LISTBOX;
+ }
+ else
+ return -1;
+}
+
+/* Find the point in a field hierarchy where all descendents
+ * share the same name */
+static pdf_obj *find_head_of_field_group(pdf_obj *obj)
+{
+ if (obj == NULL || pdf_dict_gets(obj, "T"))
+ return obj;
+ else
+ return find_head_of_field_group(pdf_dict_gets(obj, "Parent"));
+}
+
+static void pdf_field_mark_dirty(fz_context *ctx, pdf_obj *field)
+{
+ if (!pdf_dict_gets(field, "Dirty"))
+ {
+ pdf_obj *nullobj = pdf_new_null(ctx);
+ fz_try(ctx)
+ {
+ pdf_dict_puts(field, "Dirty", nullobj);
+ }
+ fz_always(ctx)
+ {
+ pdf_drop_obj(nullobj);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+ }
+}
+
+static void copy_resources(pdf_obj *dst, pdf_obj *src)
+{
+ int i, len;
+
+ len = pdf_dict_len(src);
+ for (i = 0; i < len; i++)
+ {
+ pdf_obj *key = pdf_dict_get_key(src, i);
+
+ if (!pdf_dict_get(dst, key))
+ pdf_dict_put(dst, key, pdf_dict_get_val(src, i));
+ }
+}
+
+static fz_widget *new_widget(pdf_document *doc, pdf_obj *obj)
+{
+ fz_widget *widget = NULL;
+
+ fz_try(doc->ctx)
+ {
+ int type = get_field_type(doc, obj);
+
+ switch(type)
+ {
+ case FZ_WIDGET_TYPE_TEXT:
+ widget = &(fz_malloc_struct(doc->ctx, fz_widget_text)->super);
+ break;
+ default:
+ widget = fz_malloc_struct(doc->ctx, fz_widget);
+ break;
+ }
+
+ widget->doc = doc;
+ widget->type = type;
+ widget->obj = pdf_keep_obj(obj);
+ }
+ fz_catch(doc->ctx)
+ {
+ fz_warn(doc->ctx, "failed to load foccussed widget");
+ }
+
+ return widget;
+}
+
+static void da_info_fin(fz_context *ctx, da_info *di)
+{
+ fz_free(ctx, di->font_name);
+ di->font_name = NULL;
+}
+
+static void da_check_stack(float *stack, int *top)
+{
+ if (*top == 32)
+ {
+ memmove(stack, stack + 1, 31 * sizeof(stack[0]));
+ *top = 31;
+ }
+}
+
+static void parse_da(fz_context *ctx, char *da, da_info *di)
+{
+ float stack[32];
+ int top = 0;
+ int tok;
+ char *name = NULL;
+ pdf_lexbuf lbuf;
+ fz_stream *str = fz_open_memory(ctx, (unsigned char *)da, strlen(da));
+
+ pdf_lexbuf_init(ctx, &lbuf, PDF_LEXBUF_SMALL);
+
+ fz_var(str);
+ fz_var(name);
+ fz_try(ctx)
+ {
+ for (tok = pdf_lex(str, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(str, &lbuf))
+ {
+ switch (tok)
+ {
+ case PDF_TOK_NAME:
+ fz_free(ctx, name);
+ name = fz_strdup(ctx, lbuf.scratch);
+ break;
+
+ case PDF_TOK_INT:
+ da_check_stack(stack, &top);
+ stack[top] = lbuf.i;
+ top ++;
+ break;
+
+ case PDF_TOK_REAL:
+ da_check_stack(stack, &top);
+ stack[top] = lbuf.f;
+ top ++;
+ break;
+
+ case PDF_TOK_KEYWORD:
+ if (!strcmp(lbuf.scratch, "Tf"))
+ {
+ di->font_size = stack[0];
+ di->font_name = name;
+ name = NULL;
+ }
+ else if (!strcmp(lbuf.scratch, "rg"))
+ {
+ di->col[0] = stack[0];
+ di->col[1] = stack[1];
+ di->col[2] = stack[2];
+ di->col_size = 3;
+ }
+
+ fz_free(ctx, name);
+ name = NULL;
+ top = 0;
+ break;
+ }
+ }
+ }
+ fz_always(ctx)
+ {
+ fz_free(ctx, name);
+ fz_close(str);
+ pdf_lexbuf_fin(&lbuf);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+}
+
+static void get_font_info(pdf_document *doc, pdf_obj *dr, char *da, font_info *font_rec)
+{
+ fz_context *ctx = doc->ctx;
+
+ parse_da(ctx, da, &font_rec->da_rec);
+ if (font_rec->da_rec.font_name == NULL)
+ fz_throw(ctx, "No font name in default appearance");
+ font_rec->font = pdf_load_font(doc, dr, pdf_dict_gets(pdf_dict_gets(dr, "Font"), font_rec->da_rec.font_name));
+}
+
+static void font_info_fin(fz_context *ctx, font_info *font_rec)
+{
+ pdf_drop_font(ctx, font_rec->font);
+ font_rec->font = NULL;
+ da_info_fin(ctx, &font_rec->da_rec);
+}
+
+static void get_text_widget_info(pdf_document *doc, pdf_obj *widget, text_widget_info *info)
+{
+ char *da = pdf_to_str_buf(get_inheritable(doc, widget, "DA"));
+ int ff = get_field_flags(doc, widget);
+ pdf_obj *ml = get_inheritable(doc, widget, "MaxLen");
+
+ info->dr = get_inheritable(doc, widget, "DR");
+ info->q = pdf_to_int(get_inheritable(doc, widget, "Q"));
+ info->multiline = (ff & Ff_Multiline) != 0;
+ info->comb = (ff & (Ff_Multiline|Ff_Password|Ff_FileSelect|Ff_Comb)) == Ff_Comb;
+
+ if (ml == NULL)
+ info->comb = 0;
+ else
+ info->max_len = pdf_to_int(ml);
+
+ get_font_info(doc, info->dr, da, &info->font_rec);
+}
+
+static void fzbuf_print_da(fz_context *ctx, fz_buffer *fzbuf, da_info *di)
+{
+ if (di->font_name != NULL && di->font_size != 0)
+ fz_buffer_printf(ctx, fzbuf, "/%s %d Tf", di->font_name, di->font_size);
+
+ if (di->col_size != 0)
+ fz_buffer_printf(ctx, fzbuf, " %f %f %f rg", di->col[0], di->col[1], di->col[2]);
+ else
+ fz_buffer_printf(ctx, fzbuf, " 0 g");
+}
+
+static fz_rect measure_text(pdf_document *doc, font_info *font_rec, const fz_matrix *tm, char *text)
+{
+ fz_rect bbox = pdf_measure_text(doc->ctx, font_rec->font, (unsigned char *)text, strlen(text));
+
+ bbox.x0 *= font_rec->da_rec.font_size * tm->a;
+ bbox.y0 *= font_rec->da_rec.font_size * tm->d;
+ bbox.x1 *= font_rec->da_rec.font_size * tm->a;
+ bbox.y1 *= font_rec->da_rec.font_size * tm->d;
+
+ return bbox;
+}
+
+static void fzbuf_print_text(fz_context *ctx, fz_buffer *fzbuf, fz_rect *clip, font_info *font_rec, fz_matrix *tm, char *text)
+{
+ fz_buffer_printf(ctx, fzbuf, fmt_q);
+ if (clip)
+ {
+ fz_buffer_printf(ctx, fzbuf, fmt_re, clip->x0, clip->y0, clip->x1 - clip->x0, clip->y1 - clip->y0);
+ fz_buffer_printf(ctx, fzbuf, fmt_W);
+ fz_buffer_printf(ctx, fzbuf, fmt_n);
+ }
+
+ fz_buffer_printf(ctx, fzbuf, fmt_BT);
+
+ fzbuf_print_da(ctx, fzbuf, &font_rec->da_rec);
+
+ fz_buffer_printf(ctx, fzbuf, "\n");
+ if (tm)
+ fz_buffer_printf(ctx, fzbuf, fmt_Tm, tm->a, tm->b, tm->c, tm->d, tm->e, tm->f);
+
+ fz_buffer_cat_pdf_string(ctx, fzbuf, text);
+ fz_buffer_printf(ctx, fzbuf, fmt_Tj);
+ fz_buffer_printf(ctx, fzbuf, fmt_ET);
+ fz_buffer_printf(ctx, fzbuf, fmt_Q);
+}
+
+static fz_buffer *create_text_buffer(fz_context *ctx, fz_rect *clip, font_info *font_rec, fz_matrix *tm, char *text)
+{
+ fz_buffer *fzbuf = fz_new_buffer(ctx, 0);
+
+ fz_try(ctx)
+ {
+ fz_buffer_printf(ctx, fzbuf, fmt_Tx_BMC);
+ fzbuf_print_text(ctx, fzbuf, clip, font_rec, tm, text);
+ fz_buffer_printf(ctx, fzbuf, fmt_EMC);
+ }
+ fz_catch(ctx)
+ {
+ fz_drop_buffer(ctx, fzbuf);
+ fz_rethrow(ctx);
+ }
+
+ return fzbuf;
+}
+
+static fz_buffer *create_aligned_text_buffer(pdf_document *doc, fz_rect *clip, text_widget_info *info, fz_matrix *tm, char *text)
+{
+ fz_context *ctx = doc->ctx;
+ fz_matrix atm = *tm;
+
+ if (info->q != Q_Left)
+ {
+ fz_rect rect = measure_text(doc, &info->font_rec, tm, text);
+
+ atm.e -= info->q == Q_Right ? rect.x1
+ : (rect.x1 - rect.x0) / 2;
+ }
+
+ return create_text_buffer(ctx, clip, &info->font_rec, &atm, text);
+}
+
+static void measure_ascent_descent(pdf_document *doc, font_info *finf, char *text, float *ascent, float *descent)
+{
+ fz_context *ctx = doc->ctx;
+ char *testtext = NULL;
+ fz_rect bbox;
+ font_info tinf = *finf;
+
+ fz_var(testtext);
+ fz_try(ctx)
+ {
+ /* Heuristic: adding "My" to text will in most cases
+ * produce a measurement that will encompass all chars */
+ testtext = fz_malloc(ctx, strlen(text) + 3);
+ strcpy(testtext, "My");
+ strcat(testtext, text);
+ tinf.da_rec.font_size = 1;
+ bbox = measure_text(doc, &tinf, &fz_identity, testtext);
+ *descent = -bbox.y0;
+ *ascent = bbox.y1;
+ }
+ fz_always(ctx)
+ {
+ fz_free(ctx, testtext);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+}
+
+typedef struct text_splitter_s
+{
+ font_info *info;
+ float width;
+ float height;
+ float scale;
+ float unscaled_width;
+ float fontsize;
+ char *text;
+ int done;
+ float x_orig;
+ float y_orig;
+ float x;
+ float x_end;
+ int text_start;
+ int text_end;
+ int max_lines;
+ int retry;
+} text_splitter;
+
+static void text_splitter_init(text_splitter *splitter, font_info *info, char *text, float width, float height, int variable)
+{
+ float fontsize = info->da_rec.font_size;
+
+ memset(splitter, 0, sizeof(*splitter));
+ splitter->info = info;
+ splitter->text = text;
+ splitter->width = width;
+ splitter->unscaled_width = width;
+ splitter->height = height;
+ splitter->fontsize = fontsize;
+ splitter->scale = 1.0;
+ /* RJW: The cast in the following line is important, as otherwise
+ * under MSVC in the variable = 0 case, splitter->max_lines becomes
+ * INT_MIN. */
+ splitter->max_lines = variable ? (int)(height/fontsize) : INT_MAX;
+}
+
+static void text_splitter_start_pass(text_splitter *splitter)
+{
+ splitter->text_end = 0;
+ splitter->x_orig = 0;
+ splitter->y_orig = 0;
+}
+
+static void text_splitter_start_line(text_splitter *splitter)
+{
+ splitter->x_end = 0;
+}
+
+static int text_splitter_layout(fz_context *ctx, text_splitter *splitter)
+{
+ char *text;
+ float room;
+ float stride;
+ int count;
+ int len;
+ float fontsize = splitter->info->da_rec.font_size;
+
+ splitter->x = splitter->x_end;
+ splitter->text_start = splitter->text_end;
+
+ text = splitter->text + splitter->text_start;
+ room = splitter->unscaled_width - splitter->x;
+
+ if (strchr("\r\n", text[0]))
+ {
+ /* Consume return chars and report end of line */
+ splitter->text_end += strspn(text, "\r\n");
+ splitter->text_start = splitter->text_end;
+ splitter->done = (splitter->text[splitter->text_end] == '\0');
+ return 0;
+ }
+ else if (text[0] == ' ')
+ {
+ /* Treat each space as a word */
+ len = 1;
+ }
+ else
+ {
+ len = 0;
+ while (text[len] != '\0' && !strchr(" \r\n", text[len]))
+ len ++;
+ }
+
+ stride = pdf_text_stride(ctx, splitter->info->font, fontsize, (unsigned char *)text, len, room, &count);
+
+ /* If not a single char fits although the line is empty, then force one char */
+ if (count == 0 && splitter->x == 0.0)
+ stride = pdf_text_stride(ctx, splitter->info->font, fontsize, (unsigned char *)text, 1, FLT_MAX, &count);
+
+ if (count < len && splitter->retry)
+ {
+ /* The word didn't fit and we are in retry mode. Work out the
+ * least additional scaling that may help */
+ float fitwidth; /* width if we force the word in */
+ float hstretchwidth; /* width if we just bump by 10% */
+ float vstretchwidth; /* width resulting from forcing in another line */
+ float bestwidth;
+
+ fitwidth = splitter->x +
+ pdf_text_stride(ctx, splitter->info->font, fontsize, (unsigned char *)text, len, FLT_MAX, &count);
+ /* FIXME: temporary fiddle factor. Would be better to work in integers */
+ fitwidth *= 1.001f;
+
+ /* Stretching by 10% is worth trying only if processing the first word on the line */
+ hstretchwidth = splitter->x == 0.0
+ ? splitter->width * 1.1 / splitter->scale
+ : FLT_MAX;
+
+ vstretchwidth = splitter->width * (splitter->max_lines + 1) * splitter->fontsize
+ / splitter->height;
+
+ bestwidth = fz_min(fitwidth, fz_min(hstretchwidth, vstretchwidth));
+
+ if (bestwidth == vstretchwidth)
+ splitter->max_lines ++;
+
+ splitter->scale = splitter->width / bestwidth;
+ splitter->unscaled_width = bestwidth;
+
+ splitter->retry = 0;
+
+ /* Try again */
+ room = splitter->unscaled_width - splitter->x;
+ stride = pdf_text_stride(ctx, splitter->info->font, fontsize, (unsigned char *)text, len, room, &count);
+ }
+
+ /* This is not the first word on the line. Best to give up on this line and push
+ * the word onto the next */
+ if (count < len && splitter->x > 0.0)
+ return 0;
+
+ splitter->text_end = splitter->text_start + count;
+ splitter->x_end = splitter->x + stride;
+ splitter->done = (splitter->text[splitter->text_end] == '\0');
+ return 1;
+}
+
+static void text_splitter_move(text_splitter *splitter, float newy, float *relx, float *rely)
+{
+ *relx = splitter->x - splitter->x_orig;
+ *rely = newy - splitter->y_orig;
+
+ splitter->x_orig = splitter->x;
+ splitter->y_orig = newy;
+}
+
+static void text_splitter_retry(text_splitter *splitter)
+{
+ if (splitter->retry)
+ {
+ /* Already tried expanding lines. Overflow must
+ * be caused by carriage control */
+ splitter->max_lines ++;
+ splitter->retry = 0;
+ splitter->unscaled_width = splitter->width * splitter->max_lines * splitter->fontsize
+ / splitter->height;
+ splitter->scale = splitter->width / splitter->unscaled_width;
+ }
+ else
+ {
+ splitter->retry = 1;
+ }
+}
+
+static void fzbuf_print_text_start(fz_context *ctx, fz_buffer *fzbuf, fz_rect *clip, font_info *font, fz_matrix *tm)
+{
+ fz_buffer_printf(ctx, fzbuf, fmt_Tx_BMC);
+ fz_buffer_printf(ctx, fzbuf, fmt_q);
+
+ if (clip)
+ {
+ fz_buffer_printf(ctx, fzbuf, fmt_re, clip->x0, clip->y0, clip->x1 - clip->x0, clip->y1 - clip->y0);
+ fz_buffer_printf(ctx, fzbuf, fmt_W);
+ fz_buffer_printf(ctx, fzbuf, fmt_n);
+ }
+
+ fz_buffer_printf(ctx, fzbuf, fmt_BT);
+
+ fzbuf_print_da(ctx, fzbuf, &font->da_rec);
+ fz_buffer_printf(ctx, fzbuf, "\n");
+
+ fz_buffer_printf(ctx, fzbuf, fmt_Tm, tm->a, tm->b, tm->c, tm->d, tm->e, tm->f);
+}
+
+static void fzbuf_print_text_end(fz_context *ctx, fz_buffer *fzbuf)
+{
+ fz_buffer_printf(ctx, fzbuf, fmt_ET);
+ fz_buffer_printf(ctx, fzbuf, fmt_Q);
+ fz_buffer_printf(ctx, fzbuf, fmt_EMC);
+}
+
+static void fzbuf_print_text_word(fz_context *ctx, fz_buffer *fzbuf, float x, float y, char *text, int count)
+{
+ int i;
+
+ fz_buffer_printf(ctx, fzbuf, fmt_Td, x, y);
+ fz_buffer_printf(ctx, fzbuf, "(");
+
+ for (i = 0; i < count; i++)
+ fz_buffer_printf(ctx, fzbuf, "%c", text[i]);
+
+ fz_buffer_printf(ctx, fzbuf, ") Tj\n");
+}
+
+static fz_buffer *create_text_appearance(pdf_document *doc, fz_rect *bbox, fz_matrix *oldtm, text_widget_info *info, char *text)
+{
+ fz_context *ctx = doc->ctx;
+ int fontsize;
+ int variable;
+ float height, width, full_width;
+ fz_buffer *fzbuf = NULL;
+ fz_buffer *fztmp = NULL;
+ fz_rect rect;
+ fz_rect tbox;
+ rect = *bbox;
+
+ if (rect.x1 - rect.x0 > 3.0 && rect.y1 - rect.y0 > 3.0)
+ {
+ rect.x0 += 1.0;
+ rect.x1 -= 1.0;
+ rect.y0 += 1.0;
+ rect.y1 -= 1.0;
+ }
+
+ height = rect.y1 - rect.y0;
+ width = rect.x1 - rect.x0;
+ full_width = bbox->x1 - bbox->x0;
+
+ fz_var(fzbuf);
+ fz_var(fztmp);
+ fz_try(ctx)
+ {
+ float ascent, descent;
+ fz_matrix tm;
+
+ variable = (info->font_rec.da_rec.font_size == 0);
+ fontsize = variable
+ ? (info->multiline ? 14.0 : floor(height))
+ : info->font_rec.da_rec.font_size;
+
+ info->font_rec.da_rec.font_size = fontsize;
+
+ measure_ascent_descent(doc, &info->font_rec, text, &ascent, &descent);
+
+ if (info->multiline)
+ {
+ text_splitter splitter;
+
+ text_splitter_init(&splitter, &info->font_rec, text, width, height, variable);
+
+ while (!splitter.done)
+ {
+ /* Try a layout pass */
+ int line = 0;
+
+ fz_drop_buffer(ctx, fztmp);
+ fztmp = NULL;
+ fztmp = fz_new_buffer(ctx, 0);
+
+ text_splitter_start_pass(&splitter);
+
+ /* Layout unscaled text to a scaled-up width, so that
+ * the scaled-down text will fit the unscaled width */
+
+ while (!splitter.done && line < splitter.max_lines)
+ {
+ /* Layout a line */
+ text_splitter_start_line(&splitter);
+
+ while (!splitter.done && text_splitter_layout(ctx, &splitter))
+ {
+ if (splitter.text[splitter.text_start] != ' ')
+ {
+ float x, y;
+ char *word = text+splitter.text_start;
+ int wordlen = splitter.text_end-splitter.text_start;
+
+ text_splitter_move(&splitter, -line*fontsize, &x, &y);
+ fzbuf_print_text_word(ctx, fztmp, x, y, word, wordlen);
+ }
+ }
+
+ line ++;
+ }
+
+ if (!splitter.done)
+ text_splitter_retry(&splitter);
+ }
+
+ fzbuf = fz_new_buffer(ctx, 0);
+
+ tm.a = splitter.scale;
+ tm.b = 0.0;
+ tm.c = 0.0;
+ tm.d = splitter.scale;
+ tm.e = rect.x0;
+ tm.f = rect.y1 - (1.0+ascent-descent)*fontsize*splitter.scale/2.0;
+
+ fzbuf_print_text_start(ctx, fzbuf, &rect, &info->font_rec, &tm);
+
+ fz_buffer_cat(ctx, fzbuf, fztmp);
+
+ fzbuf_print_text_end(ctx, fzbuf);
+ }
+ else if (info->comb)
+ {
+ int i, n = fz_mini((int)strlen(text), info->max_len);
+ float comb_width = full_width/info->max_len;
+ float char_width = pdf_text_stride(ctx, info->font_rec.font, fontsize, (unsigned char *)"M", 1, FLT_MAX, NULL);
+ float init_skip = (comb_width - char_width)/2.0;
+
+ tm = fz_identity;
+ tm.e = rect.x0;
+ tm.f = rect.y1 - (height+(ascent-descent)*fontsize)/2.0;
+
+ fzbuf = fz_new_buffer(ctx, 0);
+
+ fzbuf_print_text_start(ctx, fzbuf, &rect, &info->font_rec, &tm);
+
+ for (i = 0; i < n; i++)
+ fzbuf_print_text_word(ctx, fzbuf, i == 0 ? init_skip : comb_width, 0.0, text+i, 1);
+
+ fzbuf_print_text_end(ctx, fzbuf);
+ }
+ else
+ {
+ if (oldtm)
+ {
+ tm = *oldtm;
+ }
+ else
+ {
+ tm = fz_identity;
+ tm.e = rect.x0;
+ tm.f = rect.y1 - (height+(ascent-descent)*fontsize)/2.0;
+
+ switch(info->q)
+ {
+ case Q_Right: tm.e += width; break;
+ case Q_Cent: tm.e += width/2; break;
+ }
+ }
+
+ if (variable)
+ {
+ tbox = measure_text(doc, &info->font_rec, &tm, text);
+
+ if (tbox.x1 - tbox.x0 > width)
+ {
+ /* Scale the text to fit but use the same offset
+ * to keep the baseline constant */
+ tm.a *= width / (tbox.x1 - tbox.x0);
+ tm.d *= width / (tbox.x1 - tbox.x0);
+ }
+ }
+
+ fzbuf = create_aligned_text_buffer(doc, &rect, info, &tm, text);
+ }
+ }
+ fz_always(ctx)
+ {
+ fz_drop_buffer(ctx, fztmp);
+ }
+ fz_catch(ctx)
+ {
+ fz_drop_buffer(ctx, fzbuf);
+ fz_rethrow(ctx);
+ }
+
+ return fzbuf;
+}
+
+static void update_marked_content(pdf_document *doc, pdf_xobject *form, fz_buffer *fzbuf)
+{
+ fz_context *ctx = doc->ctx;
+ int tok;
+ pdf_lexbuf lbuf;
+ fz_stream *str_outer = NULL;
+ fz_stream *str_inner = NULL;
+ unsigned char *buf;
+ int len;
+ fz_buffer *newbuf = NULL;
+
+ pdf_lexbuf_init(ctx, &lbuf, PDF_LEXBUF_SMALL);
+
+ fz_var(str_outer);
+ fz_var(str_inner);
+ fz_var(newbuf);
+ fz_try(ctx)
+ {
+ int bmc_found;
+ int first = 1;
+
+ newbuf = fz_new_buffer(ctx, 0);
+ str_outer = pdf_open_stream(doc, pdf_to_num(form->contents), pdf_to_gen(form->contents));
+ len = fz_buffer_storage(ctx, fzbuf, &buf);
+ str_inner = fz_open_memory(ctx, buf, len);
+
+ /* Copy the existing appearance stream to newbuf while looking for BMC */
+ for (tok = pdf_lex(str_outer, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(str_outer, &lbuf))
+ {
+ if (first)
+ first = 0;
+ else
+ fz_buffer_printf(ctx, newbuf, " ");
+
+ pdf_print_token(ctx, newbuf, tok, &lbuf);
+ if (tok == PDF_TOK_KEYWORD && !strcmp(lbuf.scratch, "BMC"))
+ break;
+ }
+
+ bmc_found = (tok != PDF_TOK_EOF);
+
+ if (bmc_found)
+ {
+ /* Drop Tx BMC from the replacement appearance stream */
+ (void)pdf_lex(str_inner, &lbuf);
+ (void)pdf_lex(str_inner, &lbuf);
+ }
+
+ /* Copy the replacement appearance stream to newbuf */
+ for (tok = pdf_lex(str_inner, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(str_inner, &lbuf))
+ {
+ fz_buffer_printf(ctx, newbuf, " ");
+ pdf_print_token(ctx, newbuf, tok, &lbuf);
+ }
+
+ if (bmc_found)
+ {
+ /* Drop the rest of the existing appearance stream until EMC found */
+ for (tok = pdf_lex(str_outer, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(str_outer, &lbuf))
+ {
+ if (tok == PDF_TOK_KEYWORD && !strcmp(lbuf.scratch, "EMC"))
+ break;
+ }
+
+ /* Copy the rest of the existing appearance stream to newbuf */
+ for (tok = pdf_lex(str_outer, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(str_outer, &lbuf))
+ {
+ fz_buffer_printf(ctx, newbuf, " ");
+ pdf_print_token(ctx, newbuf, tok, &lbuf);
+ }
+ }
+
+ /* Use newbuf in place of the existing appearance stream */
+ pdf_update_xobject_contents(doc, form, newbuf);
+ }
+ fz_always(ctx)
+ {
+ fz_close(str_outer);
+ fz_close(str_inner);
+ fz_drop_buffer(ctx, newbuf);
+ pdf_lexbuf_fin(&lbuf);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+}
+
+static int get_matrix(pdf_document *doc, pdf_xobject *form, int q, fz_matrix *mt)
+{
+ fz_context *ctx = doc->ctx;
+ int found = 0;
+ pdf_lexbuf lbuf;
+ fz_stream *str;
+
+ str = pdf_open_stream(doc, pdf_to_num(form->contents), pdf_to_gen(form->contents));
+
+ pdf_lexbuf_init(ctx, &lbuf, PDF_LEXBUF_SMALL);
+
+ fz_try(ctx)
+ {
+ int tok;
+ float coefs[MATRIX_COEFS];
+ int coef_i = 0;
+
+ /* Look for the text matrix Tm in the stream */
+ for (tok = pdf_lex(str, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(str, &lbuf))
+ {
+ if (tok == PDF_TOK_INT || tok == PDF_TOK_REAL)
+ {
+ if (coef_i >= MATRIX_COEFS)
+ {
+ int i;
+ for (i = 0; i < MATRIX_COEFS-1; i++)
+ coefs[i] = coefs[i+1];
+
+ coef_i = MATRIX_COEFS-1;
+ }
+
+ coefs[coef_i++] = tok == PDF_TOK_INT ? lbuf.i
+ : lbuf.f;
+ }
+ else
+ {
+ if (tok == PDF_TOK_KEYWORD && !strcmp(lbuf.scratch, "Tm") && coef_i == MATRIX_COEFS)
+ {
+ found = 1;
+ mt->a = coefs[0];
+ mt->b = coefs[1];
+ mt->c = coefs[2];
+ mt->d = coefs[3];
+ mt->e = coefs[4];
+ mt->f = coefs[5];
+ }
+
+ coef_i = 0;
+ }
+ }
+
+ if (found)
+ {
+ fz_rect bbox = pdf_to_rect(ctx, pdf_dict_gets(form->contents, "BBox"));
+
+ switch (q)
+ {
+ case Q_Left:
+ mt->e = bbox.x0 + 1;
+ break;
+
+ case Q_Cent:
+ mt->e = (bbox.x1 - bbox.x0) / 2;
+ break;
+
+ case Q_Right:
+ mt->e = bbox.x1 - 1;
+ break;
+ }
+ }
+ }
+ fz_always(ctx)
+ {
+ fz_close(str);
+ pdf_lexbuf_fin(&lbuf);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+
+ return found;
+}
+
+static void update_text_field_value(fz_context *ctx, pdf_obj *obj, char *text)
+{
+ pdf_obj *sobj = NULL;
+ pdf_obj *grp;
+
+ /* All fields of the same name should be updated, so
+ * set the value at the head of the group */
+ grp = find_head_of_field_group(obj);
+ if (grp)
+ obj = grp;
+
+ fz_var(sobj);
+ fz_try(ctx)
+ {
+ sobj = pdf_new_string(ctx, text, strlen(text));
+ pdf_dict_puts(obj, "V", sobj);
+ }
+ fz_always(ctx)
+ {
+ pdf_drop_obj(sobj);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+}
+
+static pdf_xobject *load_or_create_form(pdf_document *doc, pdf_obj *obj, fz_rect *rect)
+{
+ fz_context *ctx = doc->ctx;
+ pdf_obj *ap = NULL;
+ pdf_obj *tobj = NULL;
+ fz_matrix mat;
+ int rot;
+ pdf_obj *formobj = NULL;
+ pdf_xobject *form = NULL;
+ char *dn = "N";
+ fz_buffer *fzbuf = NULL;
+ int create_form = 0;
+
+ fz_var(formobj);
+ fz_var(tobj);
+ fz_var(form);
+ fz_var(fzbuf);
+ fz_try(ctx)
+ {
+ rot = pdf_to_int(pdf_dict_getp(obj, "MK/R"));
+ *rect = pdf_to_rect(ctx, pdf_dict_gets(obj, "Rect"));
+ rect->x1 -= rect->x0;
+ rect->y1 -= rect->y0;
+ rect->x0 = rect->y0 = 0;
+ account_for_rot(rect, &mat, rot);
+
+ ap = pdf_dict_gets(obj, "AP");
+ if (ap == NULL)
+ {
+ tobj = pdf_new_dict(ctx, 1);
+ pdf_dict_puts(obj, "AP", tobj);
+ ap = tobj;
+ tobj = NULL;
+ }
+
+ formobj = pdf_dict_gets(ap, dn);
+ if (formobj == NULL)
+ {
+ tobj = pdf_new_xobject(doc, rect, &mat);
+ pdf_dict_puts(ap, dn, tobj);
+ formobj = tobj;
+ tobj = NULL;
+ create_form = 1;
+ }
+
+ form = pdf_load_xobject(doc, formobj);
+ if (create_form)
+ {
+ fzbuf = fz_new_buffer(ctx, 1);
+ pdf_update_xobject_contents(doc, form, fzbuf);
+ }
+
+ copy_resources(form->resources, get_inheritable(doc, obj, "DR"));
+ }
+ fz_always(ctx)
+ {
+ pdf_drop_obj(tobj);
+ fz_drop_buffer(ctx, fzbuf);
+ }
+ fz_catch(ctx)
+ {
+ pdf_drop_xobject(ctx, form);
+ fz_rethrow(ctx);
+ }
+
+ return form;
+}
+
+static void update_text_appearance(pdf_document *doc, pdf_obj *obj, char *eventValue)
+{
+ fz_context *ctx = doc->ctx;
+ text_widget_info info;
+ pdf_xobject *form = NULL;
+ fz_buffer *fzbuf = NULL;
+ fz_matrix tm;
+ fz_rect rect;
+ int has_tm;
+ char *text = NULL;
+
+ memset(&info, 0, sizeof(info));
+
+ fz_var(info);
+ fz_var(form);
+ fz_var(fzbuf);
+ fz_var(text);
+ fz_try(ctx)
+ {
+ if (eventValue)
+ text = fz_strdup(ctx, eventValue);
+ else
+ text = pdf_field_getValue(doc, obj);
+
+ get_text_widget_info(doc, obj, &info);
+ form = load_or_create_form(doc, obj, &rect);
+
+ has_tm = get_matrix(doc, form, info.q, &tm);
+ fzbuf = create_text_appearance(doc, &form->bbox, has_tm ? &tm : NULL, &info,
+ text?text:"");
+ update_marked_content(doc, form, fzbuf);
+ }
+ fz_always(ctx)
+ {
+ fz_free(ctx, text);
+ pdf_drop_xobject(ctx, form);
+ fz_drop_buffer(ctx, fzbuf);
+ font_info_fin(ctx, &info.font_rec);
+ }
+ fz_catch(ctx)
+ {
+ fz_warn(ctx, "update_text_appearance failed");
+ }
+}
+
+static void fzbuf_print_color(fz_context *ctx, fz_buffer *fzbuf, pdf_obj *arr, int stroke, float adj)
+{
+ switch(pdf_array_len(arr))
+ {
+ case 1:
+ fz_buffer_printf(ctx, fzbuf, stroke?"%f G\n":"%f g\n",
+ pdf_to_real(pdf_array_get(arr, 0)) + adj);
+ break;
+ case 3:
+ fz_buffer_printf(ctx, fzbuf, stroke?"%f %f %f rg\n":"%f %f %f rg\n",
+ pdf_to_real(pdf_array_get(arr, 0)) + adj,
+ pdf_to_real(pdf_array_get(arr, 1)) + adj,
+ pdf_to_real(pdf_array_get(arr, 2)) + adj);
+ break;
+ case 4:
+ fz_buffer_printf(ctx, fzbuf, stroke?"%f %f %f %f k\n":"%f %f %f %f k\n",
+ pdf_to_real(pdf_array_get(arr, 0)),
+ pdf_to_real(pdf_array_get(arr, 1)),
+ pdf_to_real(pdf_array_get(arr, 2)),
+ pdf_to_real(pdf_array_get(arr, 3)));
+ break;
+ }
+}
+
+static int get_border_style(pdf_obj *obj)
+{
+ char *sname = pdf_to_name(pdf_dict_getp(obj, "BS/S"));
+
+ if (!strcmp(sname, "D"))
+ return BS_Dashed;
+ else if (!strcmp(sname, "B"))
+ return BS_Beveled;
+ else if (!strcmp(sname, "I"))
+ return BS_Inset;
+ else if (!strcmp(sname, "U"))
+ return BS_Underline;
+ else
+ return BS_Solid;
+}
+
+static float get_border_width(pdf_obj *obj)
+{
+ float w = pdf_to_real(pdf_dict_getp(obj, "BS/W"));
+ return w == 0.0 ? 1.0 : w;
+}
+
+static void update_pushbutton_appearance(pdf_document *doc, pdf_obj *obj)
+{
+ fz_context *ctx = doc->ctx;
+ fz_rect rect;
+ pdf_xobject *form = NULL;
+ fz_buffer *fzbuf = NULL;
+ pdf_obj *tobj = NULL;
+ font_info font_rec;
+ int bstyle;
+ float bwidth;
+ float btotal;
+
+ memset(&font_rec, 0, sizeof(font_rec));
+
+ fz_var(font_rec);
+ fz_var(form);
+ fz_var(fzbuf);
+ fz_try(ctx)
+ {
+ form = load_or_create_form(doc, obj, &rect);
+ fzbuf = fz_new_buffer(ctx, 0);
+ tobj = pdf_dict_getp(obj, "MK/BG");
+ if (pdf_is_array(tobj))
+ {
+ fzbuf_print_color(ctx, fzbuf, tobj, 0, 0.0);
+ fz_buffer_printf(ctx, fzbuf, fmt_re,
+ rect.x0, rect.y0, rect.x1, rect.y1);
+ fz_buffer_printf(ctx, fzbuf, fmt_f);
+ }
+ bstyle = get_border_style(obj);
+ bwidth = get_border_width(obj);
+ btotal = bwidth;
+ if (bstyle == BS_Beveled || bstyle == BS_Inset)
+ {
+ btotal += bwidth;
+
+ if (bstyle == BS_Beveled)
+ fz_buffer_printf(ctx, fzbuf, fmt_g, 1.0);
+ else
+ fz_buffer_printf(ctx, fzbuf, fmt_g, 0.33);
+ fz_buffer_printf(ctx, fzbuf, fmt_m, bwidth, bwidth);
+ fz_buffer_printf(ctx, fzbuf, fmt_l, bwidth, rect.y1 - bwidth);
+ fz_buffer_printf(ctx, fzbuf, fmt_l, rect.x1 - bwidth, rect.y1 - bwidth);
+ fz_buffer_printf(ctx, fzbuf, fmt_l, rect.x1 - 2 * bwidth, rect.y1 - 2 * bwidth);
+ fz_buffer_printf(ctx, fzbuf, fmt_l, 2 * bwidth, rect.y1 - 2 * bwidth);
+ fz_buffer_printf(ctx, fzbuf, fmt_l, 2 * bwidth, 2 * bwidth);
+ fz_buffer_printf(ctx, fzbuf, fmt_f);
+ if (bstyle == BS_Beveled)
+ fzbuf_print_color(ctx, fzbuf, tobj, 0, -0.25);
+ else
+ fz_buffer_printf(ctx, fzbuf, fmt_g, 0.66);
+ fz_buffer_printf(ctx, fzbuf, fmt_m, rect.x1 - bwidth, rect.y1 - bwidth);
+ fz_buffer_printf(ctx, fzbuf, fmt_l, rect.x1 - bwidth, bwidth);
+ fz_buffer_printf(ctx, fzbuf, fmt_l, bwidth, bwidth);
+ fz_buffer_printf(ctx, fzbuf, fmt_l, 2 * bwidth, 2 * bwidth);
+ fz_buffer_printf(ctx, fzbuf, fmt_l, rect.x1 - 2 * bwidth, 2 * bwidth);
+ fz_buffer_printf(ctx, fzbuf, fmt_l, rect.x1 - 2 * bwidth, rect.y1 - 2 * bwidth);
+ fz_buffer_printf(ctx, fzbuf, fmt_f);
+ }
+
+ tobj = pdf_dict_getp(obj, "MK/BC");
+ if (tobj)
+ {
+ fzbuf_print_color(ctx, fzbuf, tobj, 1, 0.0);
+ fz_buffer_printf(ctx, fzbuf, fmt_w, bwidth);
+ fz_buffer_printf(ctx, fzbuf, fmt_re,
+ bwidth/2, bwidth/2,
+ rect.x1 -bwidth/2, rect.y1 - bwidth/2);
+ fz_buffer_printf(ctx, fzbuf, fmt_s);
+ }
+
+ tobj = pdf_dict_getp(obj, "MK/CA");
+ if (tobj)
+ {
+ fz_rect clip = rect;
+ fz_rect bounds;
+ fz_matrix mat;
+ char *da = pdf_to_str_buf(pdf_dict_gets(obj, "DA"));
+ char *text = pdf_to_str_buf(tobj);
+
+ clip.x0 += btotal;
+ clip.y0 += btotal;
+ clip.x1 -= btotal;
+ clip.y1 -= btotal;
+
+ get_font_info(doc, form->resources, da, &font_rec);
+ bounds = measure_text(doc, &font_rec, &fz_identity, text);
+ mat = fz_translate((rect.x1 - bounds.x1)/2, (rect.y1 - bounds.y1)/2);
+ fzbuf_print_text(ctx, fzbuf, &clip, &font_rec, &mat, text);
+ }
+
+ pdf_update_xobject_contents(doc, form, fzbuf);
+ }
+ fz_always(ctx)
+ {
+ font_info_fin(ctx, &font_rec);
+ fz_drop_buffer(ctx, fzbuf);
+ pdf_drop_xobject(ctx, form);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+}
+
+static void reset_field(pdf_document *doc, pdf_obj *obj)
+{
+ fz_context *ctx = doc->ctx;
+
+ switch (get_field_type(doc, obj))
+ {
+ case FZ_WIDGET_TYPE_RADIOBUTTON:
+ case FZ_WIDGET_TYPE_CHECKBOX:
+ {
+ pdf_obj *name = NULL;
+
+ fz_var(name);
+ fz_try(ctx)
+ {
+ name = fz_new_name(ctx, "Off");
+ pdf_dict_puts(obj, "AS", name);
+ }
+ fz_always(ctx)
+ {
+ pdf_drop_obj(name);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+ }
+ break;
+ default:
+ {
+ pdf_obj *def_val = pdf_dict_gets(obj, "DV");
+
+ if (def_val)
+ {
+ pdf_dict_puts(obj, "V", def_val);
+ }
+ else
+ {
+ pdf_dict_dels(obj, "V");
+ }
+
+ pdf_field_mark_dirty(ctx, obj);
+ }
+ }
+}
+
+static void execute_action(pdf_document *doc, pdf_obj *obj, pdf_obj *a)
+{
+ fz_context *ctx = doc->ctx;
+ if (a)
+ {
+ char *type = pdf_to_name(pdf_dict_gets(a, "S"));
+
+ if (!strcmp(type, "JavaScript"))
+ {
+ pdf_obj *js = pdf_dict_gets(a, "JS");
+ if (js)
+ {
+ char *code = get_string_or_stream(doc, js);
+ fz_try(ctx)
+ {
+ pdf_js_setup_event(doc->js, obj);
+ pdf_js_execute(doc->js, code);
+ }
+ fz_always(ctx)
+ {
+ fz_free(ctx, code);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+ }
+ }
+ else if (!strcmp(type, "ResetForm"))
+ {
+ int flags = pdf_to_int(pdf_dict_gets(a, "Flags"));
+ pdf_obj *affected_fields = pdf_dict_gets(a, "Fields");
+ pdf_obj *all_fields = pdf_dict_getp(doc->trailer, "Root/AcroForm/Fields");
+ int i, n = pdf_array_len(all_fields);
+
+ for (i = 0; i < n; i++)
+ {
+ pdf_obj *field = pdf_array_get(all_fields, i);
+ char *name = pdf_to_str_buf(pdf_dict_gets(field, "T"));
+ int j, m = pdf_array_len(affected_fields);
+ int found = 0;
+
+ for (j = 0; j < m && !found; j++)
+ {
+ pdf_obj *tfield = pdf_array_get(affected_fields, j);
+ char *tname;
+
+ /* Elements if the array are either indirect references
+ * to fields or field names. */
+ tname = pdf_to_str_buf(pdf_is_string(tfield) ? tfield : pdf_dict_gets(tfield, "T"));
+
+ if (!strcmp(tname, name))
+ found = 1;
+ }
+
+ if (flags & 1)
+ found = !found;
+
+ if (found)
+ reset_field(doc, field);
+ }
+ }
+ }
+}
+
+void pdf_update_appearance(pdf_document *doc, pdf_obj *obj)
+{
+ if (!pdf_dict_gets(obj, "AP") || pdf_dict_gets(obj, "Dirty"))
+ {
+ if (!strcmp(pdf_to_name(pdf_dict_gets(obj, "Subtype")), "Widget"))
+ {
+ switch(get_field_type(doc, obj))
+ {
+ case FZ_WIDGET_TYPE_TEXT:
+ {
+ pdf_obj *formatting = pdf_dict_getp(obj, "AA/F");
+ if (formatting)
+ {
+ /* Apply formatting */
+ execute_action(doc, obj, formatting);
+ /* Update appearance from JS event.value */
+ update_text_appearance(doc, obj, pdf_js_getEventValue(doc->js));
+ }
+ else
+ {
+ /* Update appearance from field value */
+ update_text_appearance(doc, obj, NULL);
+ }
+ }
+ break;
+ case FZ_WIDGET_TYPE_PUSHBUTTON:
+ update_pushbutton_appearance(doc, obj);
+ break;
+ }
+ }
+
+ pdf_dict_dels(obj, "Dirty");
+ }
+}
+
+static void execute_action_chain(pdf_document *doc, pdf_obj *obj)
+{
+ fz_context *ctx = doc->ctx;
+ pdf_obj *a;
+
+ a = pdf_dict_gets(obj, "A");
+ while (a)
+ {
+ execute_action(doc, obj, a);
+ a = pdf_dict_gets(a, "Next");
+ }
+}
+
+static void check_off(fz_context *ctx, pdf_obj *obj)
+{
+ pdf_obj *off = NULL;
+
+ fz_var(off);
+ fz_try(ctx);
+ {
+ off = fz_new_name(ctx, "Off");
+ pdf_dict_puts(obj, "AS", off);
+ }
+ fz_always(ctx)
+ {
+ pdf_drop_obj(off);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+}
+
+static void set_check(fz_context *ctx, pdf_obj *chk, char *name)
+{
+ pdf_obj *n = pdf_dict_getp(chk, "AP/N");
+ pdf_obj *val = NULL;
+
+ fz_var(val);
+ fz_try(ctx)
+ {
+ /* If name is a possible value of this check
+ * box then use it, otherwise use "Off" */
+ if (pdf_dict_gets(n, name))
+ val = fz_new_name(ctx, name);
+ else
+ val = fz_new_name(ctx, "Off");
+
+ pdf_dict_puts(chk, "AS", val);
+ }
+ fz_always(ctx)
+ {
+ pdf_drop_obj(val);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+}
+
+/* Set the values of all fields in a group defined by a node
+ * in the hierarchy */
+static void set_check_grp(fz_context *ctx, pdf_obj *grp, char *val)
+{
+ pdf_obj *kids = pdf_dict_gets(grp, "Kids");
+
+ if (kids == NULL)
+ {
+ set_check(ctx, grp, val);
+ }
+ else
+ {
+ int i, n = pdf_array_len(kids);
+
+ for (i = 0; i < n; i++)
+ set_check_grp(ctx, pdf_array_get(kids, i), val);
+ }
+}
+
+static void toggle_check_box(pdf_document *doc, pdf_obj *obj)
+{
+ fz_context *ctx = doc->ctx;
+ pdf_obj *as = pdf_dict_gets(obj, "AS");
+ int ff = get_field_flags(doc, obj);
+
+ if (as && strcmp(pdf_to_name(as), "Off"))
+ {
+ /* "as" neither missing nor set to Off. Set it to Off, unless
+ * this is a non-toggle-off radio button. */
+ if ((ff & (Ff_Pushbutton|Ff_NoToggleToOff|Ff_Radio)) != (Ff_NoToggleToOff|Ff_Radio))
+ check_off(ctx, obj);
+ }
+ else
+ {
+ pdf_obj *n, *key = NULL;
+ int len, i;
+
+ n = pdf_dict_getp(obj, "AP/N");
+
+ /* Look for a key that isn't "Off" */
+ len = pdf_dict_len(n);
+ for (i = 0; i < len; i++)
+ {
+ key = pdf_dict_get_key(n, i);
+ if (pdf_is_name(key) && strcmp(pdf_to_name(key), "Off"))
+ break;
+ }
+
+ /* If we found no alternative value to Off then we have no value to use */
+ if (!key)
+ return;
+
+ /* For radio buttons, first turn off all buttons in the group and
+ * then set the one that was clicked */
+ if ((ff & (Ff_Pushbutton|Ff_Radio)) == Ff_Radio)
+ {
+ pdf_obj *kids = pdf_dict_getp(obj, "Parent/Kids");
+ int i, n = pdf_array_len(kids);
+
+ for (i = 0; i < n; i++)
+ check_off(ctx, pdf_array_get(kids, i));
+
+ pdf_dict_puts(obj, "AS", key);
+ }
+ else
+ {
+ /* For check boxes, locate the node of the field hierarchy below
+ * which all fields share a name with the clicked one, and set
+ * all to the same value. This may cause the group to act like
+ * radio buttons, if each have distinct "On" values */
+ pdf_obj *grp = find_head_of_field_group(obj);
+
+ if (grp)
+ set_check_grp(doc->ctx, grp, pdf_to_name(key));
+ else
+ set_check(doc->ctx, obj, pdf_to_name(key));
+ }
+ }
+
+ /* FIXME: should probably update the V entry in the field dictionary too */
+}
+
+int pdf_pass_event(pdf_document *doc, pdf_page *page, fz_ui_event *ui_event)
+{
+ pdf_annot *annot;
+ pdf_hotspot *hp = &doc->hotspot;
+ fz_point *pt = &(ui_event->event.pointer.pt);
+ int changed = 0;
+
+ for (annot = page->annots; annot; annot = annot->next)
+ {
+ if (pt->x >= annot->pagerect.x0 && pt->x <= annot->pagerect.x1)
+ if (pt->y >= annot->pagerect.y0 && pt->y <= annot->pagerect.y1)
+ break;
+ }
+
+ switch (ui_event->etype)
+ {
+ case FZ_EVENT_TYPE_POINTER:
+ {
+ switch (ui_event->event.pointer.ptype)
+ {
+ case FZ_POINTER_DOWN:
+ if (doc->focus)
+ {
+ fz_free_widget(doc->ctx, doc->focus);
+ doc->focus = NULL;
+ }
+
+ if (annot)
+ {
+ doc->focus = new_widget(doc, annot->obj);
+ hp->num = pdf_to_num(annot->obj);
+ hp->gen = pdf_to_gen(annot->obj);
+ hp->state = HOTSPOT_POINTER_DOWN;
+ changed = 1;
+ }
+ break;
+
+ case FZ_POINTER_UP:
+ if (hp->state != 0)
+ changed = 1;
+
+ hp->num = 0;
+ hp->gen = 0;
+ hp->state = 0;
+
+ if (annot)
+ {
+ switch(get_field_type(doc, annot->obj))
+ {
+ case FZ_WIDGET_TYPE_RADIOBUTTON:
+ case FZ_WIDGET_TYPE_CHECKBOX:
+ /* FIXME: treating radio buttons like check boxes, for now */
+ toggle_check_box(doc, annot->obj);
+ changed = 1;
+ break;
+ }
+
+ execute_action_chain(doc, annot->obj);
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ return changed;
+}
+
+fz_rect *pdf_get_screen_update(pdf_document *doc)
+{
+ return NULL;
+}
+
+fz_widget *pdf_get_focussed_widget(pdf_document *doc)
+{
+ return doc->focus;
+}
+
+void fz_free_widget(fz_context *ctx, fz_widget *widget)
+{
+ if (widget)
+ {
+ switch(widget->type)
+ {
+ case FZ_WIDGET_TYPE_TEXT:
+ fz_free(ctx, ((fz_widget_text *)widget)->text);
+ break;
+ }
+
+ pdf_drop_obj(widget->obj);
+ fz_free(ctx, widget);
+ }
+}
+
+int fz_widget_get_type(fz_widget *widget)
+{
+ return widget->type;
+}
+
+char *pdf_field_getValue(pdf_document *doc, pdf_obj *field)
+{
+ return get_string_or_stream(doc, get_inheritable(doc, field, "V"));
+}
+
+static void recalculate(pdf_document *doc)
+{
+ pdf_obj *co = pdf_dict_getp(doc->trailer, "Root/AcroForm/CO");
+
+ if (co)
+ {
+ int i, n = pdf_array_len(co);
+
+ for (i = 0; i < n; i++)
+ {
+ pdf_obj *field = pdf_array_get(co, i);
+ pdf_obj *calc = pdf_dict_getp(field, "AA/C");
+
+ if (calc)
+ {
+ execute_action(doc, field, calc);
+ /* A calculate action, updates event.value. We need
+ * to place the value in the field */
+ update_text_field_value(doc->ctx, field, pdf_js_getEventValue(doc->js));
+ pdf_field_mark_dirty(doc->ctx, field);
+ }
+ }
+ }
+}
+
+void pdf_field_setValue(pdf_document *doc, pdf_obj *field, char *text)
+{
+ update_text_field_value(doc->ctx, field, text);
+ recalculate(doc);
+ pdf_field_mark_dirty(doc->ctx, field);
+}
+
+char *pdf_field_getBorderStyle(pdf_document *doc, pdf_obj *field)
+{
+ char *bs = pdf_to_name(pdf_dict_getp(field, "BS/S"));
+
+ switch (*bs)
+ {
+ case 'S': return "Solid";
+ case 'D': return "Dashed";
+ case 'B': return "Beveled";
+ case 'I': return "Inset";
+ case 'U': return "Underline";
+ }
+
+ return "Solid";
+}
+
+void pdf_field_setBorderStyle(pdf_document *doc, pdf_obj *field, char *text)
+{
+ fz_context *ctx = doc->ctx;
+ pdf_obj *val = NULL;
+
+ if (!strcmp(text, "Solid"))
+ val = fz_new_name(ctx, "S");
+ else if (!strcmp(text, "Dashed"))
+ val = fz_new_name(ctx, "D");
+ else if (!strcmp(text, "Beveled"))
+ val = fz_new_name(ctx, "B");
+ else if (!strcmp(text, "Inset"))
+ val = fz_new_name(ctx, "I");
+ else if (!strcmp(text, "Underline"))
+ val = fz_new_name(ctx, "U");
+ else
+ return;
+
+ fz_try(ctx);
+ {
+ pdf_dict_putp(field, "BS/S", val);
+ pdf_field_mark_dirty(ctx, field);
+ }
+ fz_always(ctx)
+ {
+ pdf_drop_obj(val);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+}
+
+void pdf_field_buttonSetCaption(pdf_document *doc, pdf_obj *field, char *text)
+{
+ fz_context *ctx = doc->ctx;
+ pdf_obj *val = pdf_new_string(ctx, text, strlen(text));
+
+ fz_try(ctx);
+ {
+ if (get_field_type(doc, field) == FZ_WIDGET_TYPE_PUSHBUTTON)
+ {
+ pdf_dict_putp(field, "MK/CA", val);
+ pdf_field_mark_dirty(ctx, field);
+ }
+ }
+ fz_always(ctx)
+ {
+ pdf_drop_obj(val);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+}
+
+void pdf_field_setFillColor(pdf_document *doc, pdf_obj *field, pdf_obj *col)
+{
+ pdf_dict_putp(field, "MK/BG", col);
+ pdf_field_mark_dirty(doc->ctx, field);
+}
+
+void pdf_field_setTextColor(pdf_document *doc, pdf_obj *field, pdf_obj *col)
+{
+ fz_context *ctx = doc->ctx;
+ da_info di;
+ fz_buffer *fzbuf = NULL;
+ char *da = pdf_to_str_buf(pdf_dict_gets(field, "DA"));
+ unsigned char *buf;
+ int len;
+ pdf_obj *daobj = NULL;
+
+ memset(&di, 0, sizeof(di));
+
+ fz_var(fzbuf);
+ fz_var(di);
+ fz_var(daobj);
+ fz_try(ctx)
+ {
+ parse_da(ctx, da, &di);
+ di.col_size = 3;
+ di.col[0] = pdf_to_real(pdf_array_get(col, 0));
+ di.col[1] = pdf_to_real(pdf_array_get(col, 1));
+ di.col[2] = pdf_to_real(pdf_array_get(col, 2));
+ fzbuf = fz_new_buffer(ctx, 0);
+ fzbuf_print_da(ctx, fzbuf, &di);
+ len = fz_buffer_storage(ctx, fzbuf, &buf);
+ daobj = pdf_new_string(ctx, (char *)buf, len);
+ pdf_dict_puts(field, "DA", daobj);
+ pdf_field_mark_dirty(ctx, field);
+ }
+ fz_always(ctx)
+ {
+ da_info_fin(ctx, &di);
+ fz_drop_buffer(ctx, fzbuf);
+ pdf_drop_obj(daobj);
+ }
+ fz_catch(ctx)
+ {
+ fz_warn(ctx, "%s", ctx->error->message);
+ }
+}
+
+char *fz_widget_text_get_text(fz_widget_text *tw)
+{
+ pdf_document *doc = tw->super.doc;
+ fz_context *ctx = doc->ctx;
+
+ fz_free(ctx, tw->text);
+ tw->text = NULL;
+
+ fz_try(ctx)
+ {
+ tw->text = pdf_field_getValue(doc, tw->super.obj);
+ }
+ fz_catch(ctx)
+ {
+ fz_warn(ctx, "failed allocation in fz_widget_text_get_text");
+ }
+
+ return tw->text;
+}
+
+void fz_widget_text_set_text(fz_widget_text *tw, char *text)
+{
+ fz_context *ctx = tw->super.doc->ctx;
+
+ fz_try(ctx)
+ {
+ pdf_field_setValue(tw->super.doc, tw->super.obj, text);
+ fz_free(ctx, tw->text);
+ tw->text = fz_strdup(ctx, text);
+ }
+ fz_catch(ctx)
+ {
+ fz_warn(ctx, "fz_widget_text_set_text failed");
+ }
+}
diff --git a/pdf/pdf_js.c b/pdf/pdf_js.c
new file mode 100644
index 00000000..88766bf1
--- /dev/null
+++ b/pdf/pdf_js.c
@@ -0,0 +1,568 @@
+#include "fitz-internal.h"
+#include "mupdf-internal.h"
+
+typedef struct pdf_js_event_s
+{
+ pdf_obj *target;
+ char *value;
+} pdf_js_event;
+
+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;
+};
+
+static pdf_obj *load_color(fz_context *ctx, pdf_jsimp *imp, pdf_jsimp_obj *val)
+{
+ pdf_obj *col = NULL;
+
+ if (pdf_jsimp_array_len(imp, val) == 4)
+ {
+ pdf_obj *comp = NULL;
+ pdf_jsimp_obj *jscomp = NULL;
+ int i;
+
+ col = pdf_new_array(ctx, 3);
+
+ fz_var(comp);
+ fz_var(jscomp);
+ fz_try(ctx)
+ {
+ for (i = 0; i < 3; i++)
+ {
+ jscomp = pdf_jsimp_array_item(imp, val, i+1);
+ comp = pdf_new_real(ctx, pdf_jsimp_toNumber(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_toString(js->imp, args[0]);
+ pdf_field_buttonSetCaption(js->doc, field, name);
+
+ return NULL;
+}
+
+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 = load_color(js->doc->ctx, js->imp, val);
+
+ fz_try(ctx)
+ {
+ pdf_field_setFillColor(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 = load_color(js->doc->ctx, js->imp, val);
+
+ fz_try(ctx)
+ {
+ pdf_field_setTextColor(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 pdf_jsimp_fromString(js->imp, pdf_field_getBorderStyle(js->doc, field));
+}
+
+static void field_setBorderStyle(void *jsctx, void *obj, pdf_jsimp_obj *val)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ pdf_obj *field = (pdf_obj *)obj;
+
+ pdf_field_setBorderStyle(js->doc, field, pdf_jsimp_toString(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 = pdf_field_getValue(js->doc, field);
+
+ return pdf_jsimp_fromString(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;
+
+ pdf_field_setValue(js->doc, field, pdf_jsimp_toString(js->imp, val));
+}
+
+static pdf_jsimp_obj *event_getTarget(void *jsctx, void *obj)
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ pdf_js_event *e = (pdf_js_event *)obj;
+
+ return pdf_jsimp_new_obj(js->imp, js->fieldtype, e->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;
+ pdf_js_event *e = (pdf_js_event *)obj;
+
+ return pdf_jsimp_fromString(js->imp, js->event.value);
+}
+
+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_toString(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_obj *find_field(pdf_obj *dict, char *name, int len)
+{
+ pdf_obj *field;
+
+ int i, n = pdf_array_len(dict);
+
+ for (i = 0; i < n; i++)
+ {
+ char *part;
+
+ field = pdf_array_get(dict, i);
+ part = pdf_to_str_buf(pdf_dict_gets(field, "T"));
+ if (strlen(part) == len && !memcmp(part, name, len))
+ break;
+ }
+
+ return i < n ? field : NULL;
+}
+
+static pdf_jsimp_obj *doc_getField(void *jsctx, void *obj, int argc, pdf_jsimp_obj *args[])
+{
+ pdf_js *js = (pdf_js *)jsctx;
+ pdf_obj *arr = js->form;
+ pdf_obj *dict = NULL;
+ int len;
+ char *name, *dot;
+
+ if (argc != 1)
+ return NULL;
+
+ /* Process the fully qualified field name which has
+ * the partial names delimited by '.' */
+ name = pdf_jsimp_toString(js->imp, args[0]);
+ /* Pretend there was a preceding '.' to simplify the loop */
+ dot = name - 1;
+
+ while (dot && arr)
+ {
+ name = dot + 1;
+ dot = strchr(name, '.');
+ len = dot ? dot - name : strlen(name);
+ dict = find_field(arr, name, len);
+ if (dot)
+ arr = pdf_dict_gets(dict, "Kids");
+ }
+
+ return dict ? pdf_jsimp_new_obj(js->imp, js->fieldtype, dict) : 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_addproperty(imp, js->doctype, "event", doc_getEvent, doc_setEvent);
+
+ /* 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);
+
+ /* 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_addmethod(imp, js->fieldtype, "buttonSetCaption", field_buttonSetCaption);
+
+ /* 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)
+{
+ pdf_jsimp_execute(js->imp,
+ "var border = new Array();\n"
+ "border.s = \"Solid\";\n"
+ "border.d = \"Dashed\";\n"
+ "border.b = \"Beveled\";\n"
+ "border.i = \"Inset\";\n"
+ "border.u = \"Underline\";\n"
+ "var color = new Array();\n"
+ "color.transparent = [ \"T\" ];\n"
+ "color.black = [ \"G\", 0];\n"
+ "color.white = [ \"G\", 1];\n"
+ "color.red = [ \"RGB\", 1,0,0 ];\n"
+ "color.green = [ \"RGB\", 0,1,0 ];\n"
+ "color.blue = [ \"RGB\", 0,0,1 ];\n"
+ "color.cyan = [ \"CMYK\", 1,0,0,0 ];\n"
+ "color.magenta = [ \"CMYK\", 0,1,0,0 ];\n"
+ "color.yellow = [ \"CMYK\", 0,0,1,0 ];\n"
+ "color.dkGray = [ \"G\", 0.25];\n"
+ "color.gray = [ \"G\", 0.5];\n"
+ "color.ltGray = [ \"G\", 0.75];\n"
+ "\n"
+ "function AFNumber_Format(nDec,sepStyle,negStyle,currStyle,strCurrency,bCurrencyPrepend)\n"
+ "{\n"
+ " var val = event.value;\n"
+ " var fracpart;\n"
+ " var intpart;\n"
+ " var point = sepStyle&2 ? ',' : '.';\n"
+ " var separator = sepStyle&2 ? '.' : ',';\n"
+ "\n"
+ " if (/^\\D*\\./.test(val))\n"
+ " val = '0'+val;\n"
+ "\n"
+ " var groups = val.match(/\\d+/g);\n"
+ "\n"
+ " switch (groups.length)\n"
+ " {\n"
+ " case 0:\n"
+ " return;\n"
+ " case 1:\n"
+ " fracpart = '';\n"
+ " intpart = groups[0];\n"
+ " break;\n"
+ " default:\n"
+ " fracpart = groups.pop();\n"
+ " intpart = groups.join('');\n"
+ " break;\n"
+ " }\n"
+ "\n"
+ " // Remove leading zeros\n"
+ " intpart = intpart.replace(/^0*/,'');\n"
+ " if (!intpart)\n"
+ " intpart = '0';\n"
+ "\n"
+ " if ((sepStyle & 1) == 0)\n"
+ " {\n"
+ " // Add the thousands sepearators: pad to length multiple of 3 with zeros,\n"
+ " // split into 3s, join with separator, and remove the leading zeros\n"
+ " intpart = new Array(2-(intpart.length+2)%3+1).join('0') + intpart;\n"
+ " intpart = intpart.match(/.../g).join(separator).replace(/^0*/,'');\n"
+ " }\n"
+ "\n"
+ " if (!intpart)\n"
+ " intpart = '0';\n"
+ "\n"
+ " // Adjust fractional part to correct number of decimal places\n"
+ " fracpart += new Array(nDec+1).join('0');\n"
+ " fracpart = fracpart.substr(0,nDec);\n"
+ "\n"
+ " if (fracpart)\n"
+ " intpart += point+fracpart;\n"
+ "\n"
+ " if (bCurrencyPrepend)\n"
+ " intpart = strCurrency+intpart;\n"
+ " else\n"
+ " intpart += strCurrency;\n"
+ "\n"
+ " if (/-/.test(val))\n"
+ " {\n"
+ " switch (negStyle)\n"
+ " {\n"
+ " case 0:\n"
+ " intpart = '-'+intpart;\n"
+ " break;\n"
+ " case 1:\n"
+ " break;\n"
+ " case 2:\n"
+ " case 3:\n"
+ " intpart = '('+intpart+')';\n"
+ " break;\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " if (negStyle&1)\n"
+ " event.target.textColor = /-/.text(val) ? color.red : color.black;\n"
+ "\n"
+ " event.value = intpart;\n"
+ "}\n"
+ "\n"
+ "function AFSimple_Calculate(op, list)\n"
+ "{\n"
+ " var res;\n"
+ "\n"
+ " switch (op)\n"
+ " {\n"
+ " case 'SUM':\n"
+ " res = 0;\n"
+ " break;\n"
+ " case 'PRD':\n"
+ " res = 1;\n"
+ " break;\n"
+ " case 'AVG':\n"
+ " res = 0;\n"
+ " break;\n"
+ " }\n"
+ "\n"
+ " if (typeof list == 'string')\n"
+ " list = list.split(/ *, */);\n"
+ "\n"
+ " for (var i = 0; i < list.length; i++)\n"
+ " {\n"
+ " var field = getField(list[i]);\n"
+ " var value = Number(field.value);\n"
+ "\n"
+ " switch (op)\n"
+ " {\n"
+ " case 'SUM':\n"
+ " res += value;\n"
+ " break;\n"
+ " case 'PRD':\n"
+ " res *= value;\n"
+ " break;\n"
+ " case 'AVG':\n"
+ " res += value;\n"
+ " break;\n"
+ " case 'MIN':\n"
+ " if (i == 0 || value < res)\n"
+ " res = value;\n"
+ " break;\n"
+ " case 'MAX':\n"
+ " if (i == 0 || value > res)\n"
+ " res = value;\n"
+ " break;\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " if (op == 'AVG')\n"
+ " res /= list.length;\n"
+ "\n"
+ " event.value = res;\n"
+ "}\n");
+}
+
+pdf_js *pdf_new_js(pdf_document *doc)
+{
+ fz_context *ctx = doc->ctx;
+ pdf_js *js = NULL;
+ pdf_obj *javascript = NULL;
+ fz_buffer *fzbuf = NULL;
+
+ fz_var(js);
+ fz_var(javascript);
+ fz_var(fzbuf);
+ fz_try(ctx)
+ {
+ int len, i;
+ pdf_obj *root, *acroform;
+ js = fz_malloc_struct(ctx, pdf_js);
+ js->doc = doc;
+
+ /* Find the form array */
+ root = pdf_dict_gets(doc->trailer, "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);
+
+ 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");
+
+ if (pdf_is_stream(doc, pdf_to_num(code), pdf_to_gen(code)))
+ {
+ unsigned char *buf;
+ int len;
+
+ fz_try(ctx)
+ {
+ fzbuf = pdf_load_stream(doc, pdf_to_num(code), pdf_to_gen(code));
+ len = fz_buffer_storage(ctx, fzbuf, &buf);
+ pdf_jsimp_execute_count(js->imp, (char *)buf, len);
+ }
+ fz_always(ctx)
+ {
+ fz_drop_buffer(ctx, fzbuf);
+ fzbuf = NULL;
+ }
+ fz_catch(ctx)
+ {
+ fz_warn(ctx, "Warning: %s", ctx->error->message);
+ }
+ }
+ }
+ }
+ fz_always(ctx)
+ {
+ pdf_drop_obj(javascript);
+ }
+ fz_catch(ctx)
+ {
+ pdf_drop_js(js);
+ js = NULL;
+ }
+
+ return js;
+}
+
+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->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_obj *target)
+{
+ if (js)
+ {
+ fz_context *ctx = js->doc->ctx;
+ char *val = pdf_field_getValue(js->doc, target);
+
+ js->event.target = target;
+
+ fz_free(ctx, js->event.value);
+ js->event.value = NULL;
+ js->event.value = fz_strdup(ctx, val?val:"");
+ }
+}
+
+char *pdf_js_getEventValue(pdf_js *js)
+{
+ return js ? js->event.value : 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)
+ {
+ }
+ }
+}
diff --git a/pdf/pdf_js_none.c b/pdf/pdf_js_none.c
new file mode 100644
index 00000000..34c7c491
--- /dev/null
+++ b/pdf/pdf_js_none.c
@@ -0,0 +1,31 @@
+#include "fitz-internal.h"
+#include "mupdf-internal.h"
+
+
+pdf_js *pdf_new_js(pdf_document *doc)
+{
+ static int x;
+
+ return (pdf_js *)&x;
+}
+
+void pdf_drop_js(pdf_js *js)
+{
+}
+
+void pdf_js_setup_event(pdf_js *js, pdf_obj *target)
+{
+}
+
+char *pdf_js_getEventValue(pdf_js *js)
+{
+ return "";
+}
+
+void pdf_js_execute(pdf_js *js, char *code)
+{
+}
+
+void pdf_js_execute_count(pdf_js *js, char *code, int count)
+{
+}
diff --git a/pdf/pdf_jsimp_cpp.c b/pdf/pdf_jsimp_cpp.c
new file mode 100644
index 00000000..3457ee5b
--- /dev/null
+++ b/pdf/pdf_jsimp_cpp.c
@@ -0,0 +1,148 @@
+/* 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 "fitz-internal.h"
+#include "mupdf-internal.h"
+#include "pdf_jsimp_cpp.h"
+
+
+pdf_jsimp *pdf_new_jsimp(fz_context *ctx, void *jsctx)
+{
+ pdf_jsimp *jsi = NULL;
+ char *err = pdf_new_jsimp_cpp(ctx, jsctx, &jsi);
+ if (err != NULL)
+ fz_throw(ctx, "%s", err);
+
+ return jsi;
+}
+
+void pdf_drop_jsimp(pdf_jsimp *imp)
+{
+ if (imp)
+ {
+ fz_context *ctx = pdf_jsimp_ctx_cpp(imp);
+ 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;
+ char *err = pdf_jsimp_new_type_cpp(imp, dtr, &type);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), "%s", err);
+
+ return type;
+}
+
+void pdf_jsimp_drop_type(pdf_jsimp *imp, pdf_jsimp_type *type)
+{
+ 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)
+{
+ char *err = pdf_jsimp_addmethod_cpp(imp, type, name, meth);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), "%s", err);
+}
+
+void pdf_jsimp_addproperty(pdf_jsimp *imp, pdf_jsimp_type *type, char *name, pdf_jsimp_getter *get, pdf_jsimp_setter *set)
+{
+ char *err = pdf_jsimp_addproperty_cpp(imp, type, name, get, set);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), "%s", err);
+}
+
+void pdf_jsimp_set_global_type(pdf_jsimp *imp, pdf_jsimp_type *type)
+{
+ char *err = pdf_jsimp_set_global_type_cpp(imp, type);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), "%s", err);
+}
+
+pdf_jsimp_obj *pdf_jsimp_new_obj(pdf_jsimp *imp, pdf_jsimp_type *type, void *natobj)
+{
+ pdf_jsimp_obj *obj = NULL;
+ char *err = pdf_jsimp_new_obj_cpp(imp, type, natobj, &obj);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), "%s", err);
+
+ return obj;
+}
+
+void pdf_jsimp_drop_obj(pdf_jsimp *imp, pdf_jsimp_obj *obj)
+{
+ char *err = pdf_jsimp_drop_obj_cpp(imp, obj);
+ if (err != NULL)
+ fz_warn(pdf_jsimp_ctx_cpp(imp), "%s", err);
+}
+
+pdf_jsimp_obj *pdf_jsimp_fromString(pdf_jsimp *imp, char *str)
+{
+ pdf_jsimp_obj *obj = NULL;
+ char *err = pdf_jsimp_fromString_cpp(imp, str, &obj);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), "%s", err);
+
+ return obj;
+}
+
+char *pdf_jsimp_toString(pdf_jsimp *imp, pdf_jsimp_obj *obj)
+{
+ char *str = NULL;
+ char *err = pdf_jsimp_toString_cpp(imp, obj, &str);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), "%s", err);
+
+ return str;
+}
+
+double pdf_jsimp_toNumber(pdf_jsimp *imp, pdf_jsimp_obj *obj)
+{
+ double num;
+ char *err = pdf_jsimp_toNumber_cpp(imp, obj, &num);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), "%s", err);
+
+ return num;
+}
+
+int pdf_jsimp_array_len(pdf_jsimp *imp, pdf_jsimp_obj *obj)
+{
+ int len = 0;
+ char *err = pdf_jsimp_array_len_cpp(imp, obj, &len);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), "%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;
+ char *err = pdf_jsimp_array_item_cpp(imp, obj, i, &item);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), "%s", err);
+
+ return item;
+}
+
+void pdf_jsimp_execute(pdf_jsimp *imp, char *code)
+{
+ char *err = pdf_jsimp_execute_cpp(imp, code);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), "%s", err);
+}
+
+void pdf_jsimp_execute_count(pdf_jsimp *imp, char *code, int count)
+{
+ char *err = pdf_jsimp_execute_count_cpp(imp, code, count);
+ if (err != NULL)
+ fz_throw(pdf_jsimp_ctx_cpp(imp), "%s", err);
+}
diff --git a/pdf/pdf_jsimp_cpp.h b/pdf/pdf_jsimp_cpp.h
new file mode 100644
index 00000000..de57bf97
--- /dev/null
+++ b/pdf/pdf_jsimp_cpp.h
@@ -0,0 +1,21 @@
+/* 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);
+char *pdf_new_jsimp_cpp(fz_context *ctx, void *jsctx, pdf_jsimp **imp);
+char *pdf_drop_jsimp_cpp(pdf_jsimp *imp);
+char *pdf_jsimp_new_type_cpp(pdf_jsimp *imp, pdf_jsimp_dtr *dtr, pdf_jsimp_type **type);
+char *pdf_jsimp_drop_type_cpp(pdf_jsimp *imp, pdf_jsimp_type *type);
+char *pdf_jsimp_addmethod_cpp(pdf_jsimp *imp, pdf_jsimp_type *type, char *name, pdf_jsimp_method *meth);
+char *pdf_jsimp_addproperty_cpp(pdf_jsimp *imp, pdf_jsimp_type *type, char *name, pdf_jsimp_getter *get, pdf_jsimp_setter *set);
+char *pdf_jsimp_set_global_type_cpp(pdf_jsimp *imp, pdf_jsimp_type *type);
+char *pdf_jsimp_new_obj_cpp(pdf_jsimp *imp, pdf_jsimp_type *type, void *natobj, pdf_jsimp_obj **obj);
+char *pdf_jsimp_drop_obj_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj);
+char *pdf_jsimp_fromString_cpp(pdf_jsimp *imp, char *str, pdf_jsimp_obj **obj);
+char *pdf_jsimp_toString_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, char **str);
+char *pdf_jsimp_toNumber_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, double *num);
+char *pdf_jsimp_array_len_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, int *len);
+char *pdf_jsimp_array_item_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, int i, pdf_jsimp_obj **item);
+char *pdf_jsimp_execute_cpp(pdf_jsimp *imp, char *code);
+char *pdf_jsimp_execute_count_cpp(pdf_jsimp *imp, char *code, int count);
diff --git a/pdf/pdf_jsimp_v8.cpp b/pdf/pdf_jsimp_v8.cpp
new file mode 100644
index 00000000..27eb0a12
--- /dev/null
+++ b/pdf/pdf_jsimp_v8.cpp
@@ -0,0 +1,412 @@
+/*
+ * 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 "fitz-internal.h"
+#include "mupdf-internal.h"
+#include "pdf_jsimp_cpp.h"
+}
+
+#include <vector>
+#include <set>
+#include <v8.h>
+
+using namespace v8;
+using namespace std;
+
+/* 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
+{
+ void *jsctx;
+ pdf_jsimp_method *meth;
+
+ PDFJSImpMethod(void *jsctx, pdf_jsimp_method *meth) : jsctx(jsctx), 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
+{
+ void *jsctx;
+ pdf_jsimp_getter *get;
+ pdf_jsimp_setter *set;
+
+ PDFJSImpProperty(void *jsctx, pdf_jsimp_getter *get, pdf_jsimp_setter *set) : jsctx(jsctx), get(get), set(set) {}
+};
+
+struct PDFJSImp;
+
+/* 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()
+ {
+ delete utf8;
+ pobj.Dispose();
+ }
+
+ 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" char *pdf_new_jsimp_cpp(fz_context *ctx, void *jsctx, pdf_jsimp **imp)
+{
+ *imp = reinterpret_cast<pdf_jsimp *>(new PDFJSImp(ctx, jsctx));
+
+ return NULL;
+}
+
+extern "C" char *pdf_drop_jsimp_cpp(pdf_jsimp *imp)
+{
+ delete reinterpret_cast<PDFJSImp *>(imp);
+ return NULL;
+}
+
+extern "C" char *pdf_jsimp_new_type_cpp(pdf_jsimp *imp, pdf_jsimp_dtr *dtr, pdf_jsimp_type **type)
+{
+ 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" 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 *>(m->meth(m->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" char *pdf_jsimp_addmethod_cpp(pdf_jsimp *imp, pdf_jsimp_type *type, char *name, pdf_jsimp_method *meth)
+{
+ PDFJSImpType *vType = reinterpret_cast<PDFJSImpType *>(type);
+ HandleScope scope;
+
+ PDFJSImpMethod *pmeth = new PDFJSImpMethod(vType->imp->jsctx, 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 *>(p->get(p->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);
+
+ p->set(p->jsctx, nself, reinterpret_cast<pdf_jsimp_obj *>(obj));
+ delete obj;
+}
+
+extern "C" char *pdf_jsimp_addproperty_cpp(pdf_jsimp *imp, pdf_jsimp_type *type, char *name, pdf_jsimp_getter *get, pdf_jsimp_setter *set)
+{
+ PDFJSImpType *vType = reinterpret_cast<PDFJSImpType *>(type);
+ HandleScope scope;
+
+ PDFJSImpProperty *prop = new PDFJSImpProperty(vType->imp->jsctx, get, set);
+ vType->templ->SetAccessor(String::New(name), getProp, setProp, External::New(prop));
+ vType->properties.push_back(prop);
+ return NULL;
+}
+
+extern "C" char *pdf_jsimp_set_global_type_cpp(pdf_jsimp *imp, pdf_jsimp_type *type)
+{
+ 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" char *pdf_jsimp_new_obj_cpp(pdf_jsimp *imp, pdf_jsimp_type *type, void *natobj, pdf_jsimp_obj **robj)
+{
+ 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 sire object
+ * when the v8 object is garbage collected */
+ if (vType->dtr)
+ {
+ PDFJSImpGCObj *gco = new PDFJSImpGCObj(obj, vType);
+ vType->imp->gclist.insert(gco);
+ gco->pobj.MakeWeak(gco, gcCallback);
+ }
+
+ *robj = reinterpret_cast<pdf_jsimp_obj *>(new PDFJSImpObject(obj));
+ return NULL;
+}
+
+extern "C" char *pdf_jsimp_drop_obj_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj)
+{
+ delete reinterpret_cast<PDFJSImpObject *>(obj);
+ return NULL;
+}
+
+extern "C" char *pdf_jsimp_fromString_cpp(pdf_jsimp *imp, char *str, pdf_jsimp_obj **obj)
+{
+ *obj = reinterpret_cast<pdf_jsimp_obj *>(new PDFJSImpObject(str));
+ return NULL;
+}
+
+extern "C" char *pdf_jsimp_toString_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, char **str)
+{
+ *str = reinterpret_cast<PDFJSImpObject *>(obj)->toString();
+ return NULL;
+}
+
+extern "C" char *pdf_jsimp_toNumber_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, double *num)
+{
+ *num = reinterpret_cast<PDFJSImpObject *>(obj)->toNumber();
+ return NULL;
+}
+
+extern "C" char *pdf_jsimp_array_len_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, int *len)
+{
+ Local<Value> val = reinterpret_cast<PDFJSImpObject *>(obj)->toValue()->ToObject();
+ Local<Array> arr = Local<Array>::Cast(val);
+ *len = arr->Length();
+ return NULL;
+}
+
+extern "C" char *pdf_jsimp_array_item_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, int i, pdf_jsimp_obj **item)
+{
+ Local<Value> val = reinterpret_cast<PDFJSImpObject *>(obj)->toValue()->ToObject();
+ Local<Array> arr = Local<Array>::Cast(val);
+ *item = reinterpret_cast<pdf_jsimp_obj *>(new PDFJSImpObject(arr->Get(Number::New(i))));
+ return NULL;
+}
+
+extern "C" char *pdf_jsimp_execute_cpp(pdf_jsimp *imp, char *code)
+{
+ 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" char *pdf_jsimp_execute_count_cpp(pdf_jsimp *imp, char *code, int count)
+{
+ 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;
+} \ No newline at end of file
diff --git a/pdf/pdf_lex.c b/pdf/pdf_lex.c
index 967b6dcc..c0d72da9 100644
--- a/pdf/pdf_lex.c
+++ b/pdf/pdf_lex.c
@@ -511,13 +511,10 @@ void pdf_print_token(fz_context *ctx, fz_buffer *fzbuf, int tok, pdf_lexbuf *buf
fz_buffer_printf(ctx, fzbuf, "/%s", buf->scratch);
break;
case PDF_TOK_STRING:
- {
- int i;
- fz_buffer_printf(ctx, fzbuf, "<");
- for (i = 0; i < buf->len; i++)
- fz_buffer_printf(ctx, fzbuf, "%02X", buf->scratch[i]);
- fz_buffer_printf(ctx, fzbuf, ">");
- }
+ if (buf->len >= buf->size)
+ pdf_lexbuf_grow(buf);
+ buf->scratch[buf->len] = 0;
+ fz_buffer_cat_pdf_string(ctx, fzbuf, buf->scratch);
break;
case PDF_TOK_OPEN_DICT:
fz_buffer_printf(ctx, fzbuf, "<<");
diff --git a/pdf/pdf_page.c b/pdf/pdf_page.c
index 4adae3fd..8715590c 100644
--- a/pdf/pdf_page.c
+++ b/pdf/pdf_page.c
@@ -357,7 +357,7 @@ pdf_load_page(pdf_document *xref, int number)
if (obj)
{
page->links = pdf_load_link_annots(xref, obj, page->ctm);
- page->annots = pdf_load_annots(xref, obj);
+ page->annots = pdf_load_annots(xref, obj, page->ctm);
}
page->resources = pdf_dict_gets(pageobj, "Resources");
@@ -373,7 +373,7 @@ pdf_load_page(pdf_document *xref, int number)
page->transparency = 1;
for (annot = page->annots; annot && !page->transparency; annot = annot->next)
- if (pdf_resources_use_blending(ctx, annot->ap->resources))
+ if (annot->ap && pdf_resources_use_blending(ctx, annot->ap->resources))
page->transparency = 1;
}
fz_catch(ctx)
diff --git a/pdf/pdf_xobject.c b/pdf/pdf_xobject.c
index f21ecaf6..6af7d951 100644
--- a/pdf/pdf_xobject.c
+++ b/pdf/pdf_xobject.c
@@ -187,7 +187,7 @@ pdf_new_xobject(pdf_document *xref, fz_rect *bbox, fz_matrix *mat)
form->bbox = *bbox;
- form->matrix = fz_identity;
+ form->matrix = *mat;
form->isolated = 0;
form->knockout = 0;
diff --git a/pdf/pdf_xref.c b/pdf/pdf_xref.c
index 77bf5d28..2deebfbd 100644
--- a/pdf/pdf_xref.c
+++ b/pdf/pdf_xref.c
@@ -767,6 +767,7 @@ pdf_init_document(pdf_document *xref)
dict = NULL;
}
}
+ xref->js = pdf_new_js(xref);
}
fz_catch(ctx)
{
@@ -796,6 +797,8 @@ pdf_close_document(pdf_document *xref)
return;
ctx = xref->ctx;
+ pdf_drop_js(xref->js);
+
if (xref->table)
{
for (i = 0; i < xref->len; i++)
@@ -824,6 +827,8 @@ pdf_close_document(pdf_document *xref)
fz_free(ctx, xref->page_refs);
}
+ if (xref->focus)
+ fz_free_widget(ctx, xref->focus);
if (xref->file)
fz_close(xref->file);
if (xref->trailer)
@@ -1229,8 +1234,14 @@ pdf_meta(pdf_document *doc, int key, void *ptr, int size)
}
}
+static fz_interactive *
+pdf_interact(pdf_document *doc)
+{
+ return (fz_interactive *)doc;
+}
+
/*
- Wrappers to implement the fz_document interface for pdf_document.
+ Initializers for the fz_document interface.
The functions are split across two files to allow calls to a
version of the constructor that does not link in the interpreter.
@@ -1239,73 +1250,27 @@ pdf_meta(pdf_document *doc, int key, void *ptr, int size)
saves roughly 6MB of space.
*/
-static void pdf_close_document_shim(fz_document *doc)
-{
- pdf_close_document((pdf_document*)doc);
-}
-
-static int pdf_needs_password_shim(fz_document *doc)
-{
- return pdf_needs_password((pdf_document*)doc);
-}
-
-static int pdf_authenticate_password_shim(fz_document *doc, char *password)
-{
- return pdf_authenticate_password((pdf_document*)doc, password);
-}
-
-static fz_outline *pdf_load_outline_shim(fz_document *doc)
-{
- return pdf_load_outline((pdf_document*)doc);
-}
-
-static int pdf_count_pages_shim(fz_document *doc)
-{
- return pdf_count_pages((pdf_document*)doc);
-}
-
-static fz_page *pdf_load_page_shim(fz_document *doc, int number)
-{
- return (fz_page*) pdf_load_page((pdf_document*)doc, number);
-}
-
-static fz_link *pdf_load_links_shim(fz_document *doc, fz_page *page)
-{
- return pdf_load_links((pdf_document*)doc, (pdf_page*)page);
-}
-
-static fz_rect pdf_bound_page_shim(fz_document *doc, fz_page *page)
-{
- return pdf_bound_page((pdf_document*)doc, (pdf_page*)page);
-}
-
-static void pdf_free_page_shim(fz_document *doc, fz_page *page)
-{
- pdf_free_page((pdf_document*)doc, (pdf_page*)page);
-}
-
-static int pdf_meta_shim(fz_document *doc, int key, void *ptr, int size)
-{
- return pdf_meta((pdf_document*)doc, key, ptr, size);
-}
-
static pdf_document *
pdf_new_document(fz_stream *file)
{
fz_context *ctx = file->ctx;
pdf_document *doc = fz_malloc_struct(ctx, pdf_document);
- doc->super.close = pdf_close_document_shim;
- doc->super.needs_password = pdf_needs_password_shim;
- doc->super.authenticate_password = pdf_authenticate_password_shim;
- doc->super.load_outline = pdf_load_outline_shim;
- doc->super.count_pages = pdf_count_pages_shim;
- doc->super.load_page = pdf_load_page_shim;
- doc->super.load_links = pdf_load_links_shim;
- doc->super.bound_page = pdf_bound_page_shim;
+ doc->super.close = (void*)pdf_close_document;
+ doc->super.needs_password = (void*)pdf_needs_password;
+ doc->super.authenticate_password = (void*)pdf_authenticate_password;
+ doc->super.load_outline = (void*)pdf_load_outline;
+ doc->super.count_pages = (void*)pdf_count_pages;
+ doc->super.load_page = (void*)pdf_load_page;
+ doc->super.load_links = (void*)pdf_load_links;
+ doc->super.bound_page = (void*)pdf_bound_page;
+ doc->super.first_annot = (void*)pdf_first_annot;
+ doc->super.next_annot = (void*)pdf_next_annot;
+ doc->super.bound_annot = (void*)pdf_bound_annot;
doc->super.run_page = NULL; /* see pdf_xref_aux.c */
- doc->super.free_page = pdf_free_page_shim;
- doc->super.meta = pdf_meta_shim;
+ doc->super.free_page = (void*)pdf_free_page;
+ doc->super.meta = (void*)pdf_meta;
+ doc->super.interact = (void*)pdf_interact;
pdf_lexbuf_init(ctx, &doc->lexbuf.base, PDF_LEXBUF_LARGE);
doc->file = fz_keep_stream(file);
diff --git a/pdf/pdf_xref_aux.c b/pdf/pdf_xref_aux.c
index 907e6f17..531a7e75 100644
--- a/pdf/pdf_xref_aux.c
+++ b/pdf/pdf_xref_aux.c
@@ -9,16 +9,11 @@
resulting executables.
*/
-static void pdf_run_page_shim(fz_document *doc, fz_page *page, fz_device *dev, fz_matrix transform, fz_cookie *cookie)
-{
- pdf_run_page((pdf_document*)doc, (pdf_page*)page, dev, transform, cookie);
-}
-
pdf_document *
pdf_open_document_with_stream(fz_stream *file)
{
pdf_document *doc = pdf_open_document_no_run_with_stream(file);
- doc->super.run_page = pdf_run_page_shim;
+ doc->super.run_page = (void*)pdf_run_page;
return doc;
}
@@ -26,6 +21,6 @@ pdf_document *
pdf_open_document(fz_context *ctx, const char *filename)
{
pdf_document *doc = pdf_open_document_no_run(ctx, filename);
- doc->super.run_page = pdf_run_page_shim;
+ doc->super.run_page = (void*)pdf_run_page;
return doc;
}
diff --git a/win32/libmupdf-v8.vcproj b/win32/libmupdf-v8.vcproj
new file mode 100644
index 00000000..1604fac5
--- /dev/null
+++ b/win32/libmupdf-v8.vcproj
@@ -0,0 +1,689 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="libmupdf-v8"
+ ProjectGUID="{2E5DAFDB-A060-4011-B760-32F6A3A4BC9D}"
+ RootNamespace="mupdf"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)\$(ProjectName)"
+ ConfigurationType="4"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ Description="Generate CMap and Font source files"
+ CommandLine="generate.bat"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\scripts;..\fitz;..\pdf;..\thirdparty\jbig2dec;&quot;..\thirdparty\jpeg-8d&quot;;&quot;..\thirdparty\openjpeg-1.5.0\libopenjpeg&quot;;&quot;..\thirdparty\zlib-1.2.5&quot;;&quot;..\thirdparty\freetype-2.4.9\include&quot;;&quot;..\thirdparty\v8-3.9\include&quot;"
+ PreprocessorDefinitions="DEBUG=1"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ AdditionalLibraryDirectories=""
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)\$(ProjectName)"
+ ConfigurationType="4"
+ CharacterSet="2"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ Description="Generate CMap and Font source files"
+ CommandLine="generate.bat"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="..\scripts;..\fitz;..\pdf;..\thirdparty\jbig2dec;..\thirdparty\jpeg-8d;..\thirdparty\openjpeg-1.5.0\libopenjpeg;..\thirdparty\zlib-1.2.5;..\thirdparty\freetype-2.4.9\include;&quot;..\thirdparty\v8-3.9\include&quot;"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Memento|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)\$(ProjectName)"
+ ConfigurationType="4"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ CommandLine=""
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\scripts;..\fitz;..\pdf;..\thirdparty\jbig2dec;..\thirdparty\jpeg-8d;..\thirdparty\openjpeg-1.5.0\libopenjpeg;..\thirdparty\zlib-1.2.5;..\thirdparty\freetype-2.4.9\include;&quot;..\thirdparty\v8-3.9\include&quot;"
+ PreprocessorDefinitions="MEMENTO=1;DEBUG=1"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="pdf"
+ >
+ <File
+ RelativePath="..\pdf\data_encodings.h"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\data_glyphlist.h"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\mupdf-internal.h"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\mupdf.h"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_annot.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_cmap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_cmap_load.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_cmap_parse.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_cmap_table.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_colorspace.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_crypt.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_encoding.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_font.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_fontfile.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_form.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_function.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_image.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_interpret.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_js.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_jsimp_cpp.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_jsimp_cpp.h"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_jsimp_v8.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_lex.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_metrics.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_nametree.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_object.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_outline.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_page.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_parse.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_pattern.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_repair.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_shade.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_store.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_stream.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_type3.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_unicode.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_xobject.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_xref.c"
+ >
+ </File>
+ <File
+ RelativePath="..\pdf\pdf_xref_aux.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="fitz"
+ >
+ <File
+ RelativePath="..\fitz\base_context.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\base_error.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\base_geometry.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\base_getopt.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\base_hash.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\base_memory.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\base_string.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\base_time.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\crypt_aes.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\crypt_arc4.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\crypt_md5.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\crypt_sha2.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\dev_bbox.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\dev_list.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\dev_null.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\dev_text.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\dev_trace.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\doc_document.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\doc_interactive.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\doc_link.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\doc_outline.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\filt_basic.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\filt_dctd.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\filt_faxd.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\filt_flate.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\filt_jbig2d.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\filt_lzwd.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\filt_predict.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\fitz-internal.h"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\fitz.h"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\image_jpeg.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\image_jpx.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\image_md5.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\image_png.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\image_save.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\image_tiff.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\memento.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\memento.h"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\res_bitmap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\res_colorspace.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\res_font.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\res_halftone.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\res_path.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\res_pixmap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\res_shade.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\res_store.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\res_text.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\stm_buffer.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\stm_open.c"
+ >
+ </File>
+ <File
+ RelativePath="..\fitz\stm_read.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="draw"
+ >
+ <File
+ RelativePath="..\draw\draw_affine.c"
+ >
+ </File>
+ <File
+ RelativePath="..\draw\draw_blend.c"
+ >
+ </File>
+ <File
+ RelativePath="..\draw\draw_device.c"
+ >
+ </File>
+ <File
+ RelativePath="..\draw\draw_edge.c"
+ >
+ </File>
+ <File
+ RelativePath="..\draw\draw_glyph.c"
+ >
+ </File>
+ <File
+ RelativePath="..\draw\draw_mesh.c"
+ >
+ </File>
+ <File
+ RelativePath="..\draw\draw_paint.c"
+ >
+ </File>
+ <File
+ RelativePath="..\draw\draw_path.c"
+ >
+ </File>
+ <File
+ RelativePath="..\draw\draw_simple_scale.c"
+ >
+ </File>
+ <File
+ RelativePath="..\draw\draw_unpack.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="xps"
+ >
+ <File
+ RelativePath="..\xps\muxps.h"
+ >
+ </File>
+ <File
+ RelativePath="..\xps\xps_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\xps\xps_doc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\xps\xps_glyphs.c"
+ >
+ </File>
+ <File
+ RelativePath="..\xps\xps_gradient.c"
+ >
+ </File>
+ <File
+ RelativePath="..\xps\xps_image.c"
+ >
+ </File>
+ <File
+ RelativePath="..\xps\xps_outline.c"
+ >
+ </File>
+ <File
+ RelativePath="..\xps\xps_path.c"
+ >
+ </File>
+ <File
+ RelativePath="..\xps\xps_resource.c"
+ >
+ </File>
+ <File
+ RelativePath="..\xps\xps_tile.c"
+ >
+ </File>
+ <File
+ RelativePath="..\xps\xps_util.c"
+ >
+ </File>
+ <File
+ RelativePath="..\xps\xps_xml.c"
+ >
+ </File>
+ <File
+ RelativePath="..\xps\xps_zip.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="cbz"
+ >
+ <File
+ RelativePath="..\cbz\mucbz.c"
+ >
+ </File>
+ <File
+ RelativePath="..\cbz\mucbz.h"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/win32/libmupdf.vcproj b/win32/libmupdf.vcproj
index 8706176c..a15c8cb0 100644
--- a/win32/libmupdf.vcproj
+++ b/win32/libmupdf.vcproj
@@ -266,6 +266,10 @@
>
</File>
<File
+ RelativePath="..\pdf\pdf_form.c"
+ >
+ </File>
+ <File
RelativePath="..\pdf\pdf_function.c"
>
</File>
@@ -278,6 +282,10 @@
>
</File>
<File
+ RelativePath="..\pdf\pdf_js_none.c"
+ >
+ </File>
+ <File
RelativePath="..\pdf\pdf_lex.c"
>
</File>
@@ -426,6 +434,10 @@
>
</File>
<File
+ RelativePath="..\fitz\doc_interactive.c"
+ >
+ </File>
+ <File
RelativePath="..\fitz\doc_link.c"
>
</File>
diff --git a/win32/mujstest-v8.vcproj b/win32/mujstest-v8.vcproj
new file mode 100644
index 00000000..b075880e
--- /dev/null
+++ b/win32/mujstest-v8.vcproj
@@ -0,0 +1,261 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="mujstest-v8"
+ ProjectGUID="{21E28758-E4D2-4B84-8EC5-B631CEE66B30}"
+ RootNamespace="mupdf"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)\$(ProjectName)"
+ ConfigurationType="1"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\fitz;..\pdf;..\xps;..\cbz"
+ PreprocessorDefinitions="FT2_BUILD_LIBRARY;OPJ_STATIC;DEBUG=1"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/FORCE"
+ AdditionalDependencies="v8_base.lib v8_snapshot.lib ws2_32.lib winmm.lib"
+ AdditionalLibraryDirectories="&quot;..\thirdparty\v8-3.9\build\Debug\lib\&quot;"
+ GenerateDebugInformation="true"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)\$(ProjectName)"
+ ConfigurationType="1"
+ CharacterSet="2"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="..\fitz;..\pdf;..\xps;..\cbz"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="v8_base.lib v8_snapshot.lib ws2_32.lib winmm.lib"
+ AdditionalLibraryDirectories="&quot;..\thirdparty\v8-3.9\build\Release\lib&quot;"
+ GenerateDebugInformation="true"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Memento|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)\$(ProjectName)"
+ ConfigurationType="1"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\fitz;..\pdf;..\xps;..\cbz"
+ PreprocessorDefinitions="FT2_BUILD_LIBRARY;OPJ_STATIC;MEMENTO=1;DEBUG=1"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="v8_base.lib v8_snapshot.lib ws2_32.lib winmm.lib"
+ AdditionalLibraryDirectories="&quot;..\thirdparty\v8-3.9\Build\Debug\lib&quot;"
+ GenerateDebugInformation="true"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <File
+ RelativePath="..\apps\jstest_main.c"
+ >
+ </File>
+ <File
+ RelativePath="..\apps\pdfapp.c"
+ >
+ </File>
+ <File
+ RelativePath="..\apps\pdfapp.h"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/win32/mupdf-v8.vcproj b/win32/mupdf-v8.vcproj
new file mode 100644
index 00000000..4eb086c3
--- /dev/null
+++ b/win32/mupdf-v8.vcproj
@@ -0,0 +1,265 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="mupdf-v8"
+ ProjectGUID="{9035A4F3-4219-45A5-985D-FBF4D9609713}"
+ RootNamespace="mupdf"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)\$(ProjectName)"
+ ConfigurationType="1"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\fitz;..\pdf;..\xps;..\cbz"
+ PreprocessorDefinitions="FT2_BUILD_LIBRARY;OPJ_STATIC;DEBUG=1"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/FORCE"
+ AdditionalDependencies="v8_base.lib v8_snapshot.lib ws2_32.lib winmm.lib"
+ AdditionalLibraryDirectories="&quot;..\thirdparty\v8-3.9\build\Debug\lib&quot;"
+ GenerateDebugInformation="true"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)\$(ProjectName)"
+ ConfigurationType="1"
+ CharacterSet="2"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="..\fitz;..\pdf;..\xps;..\cbz"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="v8_base.lib v8_snapshot.lib ws2_32.lib winmm.lib"
+ AdditionalLibraryDirectories="&quot;..\thirdparty\v8-3.9\Build\release\lib&quot;"
+ GenerateDebugInformation="true"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Memento|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)\$(ProjectName)"
+ ConfigurationType="1"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\fitz;..\pdf;..\xps;..\cbz"
+ PreprocessorDefinitions="FT2_BUILD_LIBRARY;OPJ_STATIC;MEMENTO=1;DEBUG=1"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="v8_base.lib v8_snapshot.lib ws2_32.lib winmm.lib"
+ AdditionalLibraryDirectories="&quot;..\thirdparty\v8-3.9\Build\Debug\lib&quot;"
+ GenerateDebugInformation="true"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <File
+ RelativePath="..\apps\pdfapp.c"
+ >
+ </File>
+ <File
+ RelativePath="..\apps\pdfapp.h"
+ >
+ </File>
+ <File
+ RelativePath="..\apps\win_main.c"
+ >
+ </File>
+ <File
+ RelativePath="..\apps\win_res.rc"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/win32/mupdf.sln b/win32/mupdf.sln
index 30207a1b..6807711f 100644
--- a/win32/mupdf.sln
+++ b/win32/mupdf.sln
@@ -29,6 +29,24 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mubusy", "mubusy.vcproj", "
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "generated", "generated.vcproj", "{A5053AA7-02E5-4903-B596-04F17AEB1526}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmupdf-v8", "libmupdf-v8.vcproj", "{2E5DAFDB-A060-4011-B760-32F6A3A4BC9D}"
+ ProjectSection(ProjectDependencies) = postProject
+ {5F615F91-DFF8-4F05-BF48-6222B7D86519} = {5F615F91-DFF8-4F05-BF48-6222B7D86519}
+ {A5053AA7-02E5-4903-B596-04F17AEB1526} = {A5053AA7-02E5-4903-B596-04F17AEB1526}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mupdf-v8", "mupdf-v8.vcproj", "{9035A4F3-4219-45A5-985D-FBF4D9609713}"
+ ProjectSection(ProjectDependencies) = postProject
+ {2E5DAFDB-A060-4011-B760-32F6A3A4BC9D} = {2E5DAFDB-A060-4011-B760-32F6A3A4BC9D}
+ {5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C} = {5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mujstest-v8", "mujstest-v8.vcproj", "{21E28758-E4D2-4B84-8EC5-B631CEE66B30}"
+ ProjectSection(ProjectDependencies) = postProject
+ {5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C} = {5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C}
+ {2E5DAFDB-A060-4011-B760-32F6A3A4BC9D} = {2E5DAFDB-A060-4011-B760-32F6A3A4BC9D}
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@@ -72,6 +90,24 @@ Global
{A5053AA7-02E5-4903-B596-04F17AEB1526}.Memento|Win32.Build.0 = Memento|Win32
{A5053AA7-02E5-4903-B596-04F17AEB1526}.Release|Win32.ActiveCfg = Release|Win32
{A5053AA7-02E5-4903-B596-04F17AEB1526}.Release|Win32.Build.0 = Release|Win32
+ {2E5DAFDB-A060-4011-B760-32F6A3A4BC9D}.Debug|Win32.ActiveCfg = Debug|Win32
+ {2E5DAFDB-A060-4011-B760-32F6A3A4BC9D}.Debug|Win32.Build.0 = Debug|Win32
+ {2E5DAFDB-A060-4011-B760-32F6A3A4BC9D}.Memento|Win32.ActiveCfg = Memento|Win32
+ {2E5DAFDB-A060-4011-B760-32F6A3A4BC9D}.Memento|Win32.Build.0 = Memento|Win32
+ {2E5DAFDB-A060-4011-B760-32F6A3A4BC9D}.Release|Win32.ActiveCfg = Release|Win32
+ {2E5DAFDB-A060-4011-B760-32F6A3A4BC9D}.Release|Win32.Build.0 = Release|Win32
+ {9035A4F3-4219-45A5-985D-FBF4D9609713}.Debug|Win32.ActiveCfg = Debug|Win32
+ {9035A4F3-4219-45A5-985D-FBF4D9609713}.Debug|Win32.Build.0 = Debug|Win32
+ {9035A4F3-4219-45A5-985D-FBF4D9609713}.Memento|Win32.ActiveCfg = Memento|Win32
+ {9035A4F3-4219-45A5-985D-FBF4D9609713}.Memento|Win32.Build.0 = Memento|Win32
+ {9035A4F3-4219-45A5-985D-FBF4D9609713}.Release|Win32.ActiveCfg = Release|Win32
+ {9035A4F3-4219-45A5-985D-FBF4D9609713}.Release|Win32.Build.0 = Release|Win32
+ {21E28758-E4D2-4B84-8EC5-B631CEE66B30}.Debug|Win32.ActiveCfg = Debug|Win32
+ {21E28758-E4D2-4B84-8EC5-B631CEE66B30}.Debug|Win32.Build.0 = Debug|Win32
+ {21E28758-E4D2-4B84-8EC5-B631CEE66B30}.Memento|Win32.ActiveCfg = Memento|Win32
+ {21E28758-E4D2-4B84-8EC5-B631CEE66B30}.Memento|Win32.Build.0 = Memento|Win32
+ {21E28758-E4D2-4B84-8EC5-B631CEE66B30}.Release|Win32.ActiveCfg = Release|Win32
+ {21E28758-E4D2-4B84-8EC5-B631CEE66B30}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/xps/xps_zip.c b/xps/xps_zip.c
index 17523f10..58f04009 100644
--- a/xps/xps_zip.c
+++ b/xps/xps_zip.c
@@ -668,54 +668,9 @@ xps_close_document(xps_document *doc)
fz_free(doc->ctx, doc);
}
-/* Document interface wrappers */
-
-static void xps_close_document_shim(fz_document *doc)
-{
- xps_close_document((xps_document*)doc);
-}
-
-static fz_outline *xps_load_outline_shim(fz_document *doc)
-{
- return xps_load_outline((xps_document*)doc);
-}
-
-static int xps_count_pages_shim(fz_document *doc)
-{
- return xps_count_pages((xps_document*)doc);
-}
-
-static fz_page *xps_load_page_shim(fz_document *doc, int number)
-{
- return (fz_page*) xps_load_page((xps_document*)doc, number);
-}
-
-static fz_link *xps_load_links_shim(fz_document *doc, fz_page *page)
-{
- return xps_load_links((xps_document*)doc, (xps_page*)page);
-}
-
-static fz_rect xps_bound_page_shim(fz_document *doc, fz_page *page)
-{
- return xps_bound_page((xps_document*)doc, (xps_page*)page);
-}
-
-static void xps_run_page_shim(fz_document *doc, fz_page *page, fz_device *dev, fz_matrix transform, fz_cookie *cookie)
-{
- xps_run_page((xps_document*)doc, (xps_page*)page, dev, transform, cookie);
-}
-
-static void xps_free_page_shim(fz_document *doc, fz_page *page)
-{
- xps_free_page((xps_document*)doc, (xps_page*)page);
-}
-
-static int xps_meta(fz_document *doc_, int key, void *ptr, int size)
+static int
+xps_meta(xps_document *doc, int key, void *ptr, int size)
{
- xps_document *doc = (xps_document *)doc_;
-
- doc = doc;
-
switch(key)
{
case FZ_META_FORMAT_INFO:
@@ -729,15 +684,13 @@ static int xps_meta(fz_document *doc_, int key, void *ptr, int size)
static void
xps_init_document(xps_document *doc)
{
- doc->super.close = xps_close_document_shim;
- doc->super.needs_password = NULL;
- doc->super.authenticate_password = NULL;
- doc->super.load_outline = xps_load_outline_shim;
- doc->super.count_pages = xps_count_pages_shim;
- doc->super.load_page = xps_load_page_shim;
- doc->super.load_links = xps_load_links_shim;
- doc->super.bound_page = xps_bound_page_shim;
- doc->super.run_page = xps_run_page_shim;
- doc->super.free_page = xps_free_page_shim;
- doc->super.meta = xps_meta;
+ doc->super.close = (void*)xps_close_document;
+ doc->super.load_outline = (void*)xps_load_outline;
+ doc->super.count_pages = (void*)xps_count_pages;
+ doc->super.load_page = (void*)xps_load_page;
+ doc->super.load_links = (void*)xps_load_links;
+ doc->super.bound_page = (void*)xps_bound_page;
+ doc->super.run_page = (void*)xps_run_page;
+ doc->super.free_page = (void*)xps_free_page;
+ doc->super.meta = (void*)xps_meta;
}