summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile49
-rw-r--r--Makethird29
-rw-r--r--apps/jstest_main.c396
-rw-r--r--apps/mudraw.c213
-rw-r--r--apps/pdfapp.c174
-rw-r--r--apps/pdfapp.h13
-rw-r--r--apps/win_main.c158
-rw-r--r--apps/win_res.rc20
-rw-r--r--apps/x11_main.c32
-rw-r--r--cbz/mucbz.c57
-rw-r--r--fitz/doc_document.c31
-rw-r--r--fitz/doc_interactive.c77
-rw-r--r--fitz/fitz-internal.h23
-rw-r--r--fitz/fitz.h219
-rw-r--r--fitz/stm_buffer.c91
-rw-r--r--pdf/mupdf-internal.h109
-rw-r--r--pdf/mupdf.h31
-rw-r--r--pdf/pdf_annot.c83
-rw-r--r--pdf/pdf_font.c52
-rw-r--r--pdf/pdf_form.c2227
-rw-r--r--pdf/pdf_js.c426
-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.cpp419
-rw-r--r--pdf/pdf_lex.c11
-rw-r--r--pdf/pdf_nametree.c2
-rw-r--r--pdf/pdf_object.c2
-rw-r--r--pdf/pdf_outline.c2
-rw-r--r--pdf/pdf_page.c9
-rw-r--r--pdf/pdf_parse.c109
-rw-r--r--pdf/pdf_repair.c2
-rw-r--r--pdf/pdf_util.js484
-rw-r--r--pdf/pdf_write.c1
-rw-r--r--pdf/pdf_xobject.c24
-rw-r--r--pdf/pdf_xref.c90
-rw-r--r--pdf/pdf_xref_aux.c9
-rw-r--r--scripts/cquote.c134
-rw-r--r--win32/generate.bat6
-rw-r--r--win32/generated.vcproj4
-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
46 files changed, 7064 insertions, 286 deletions
diff --git a/Makefile b/Makefile
index 05bb4e8b..9ce2be45 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) cr $@ $^
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,12 +96,20 @@ $(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 ---
+# --- Generated CMAP, FONT and JAVASCRIPT files ---
CMAPDUMP := $(OUT)/cmapdump
FONTDUMP := $(OUT)/fontdump
+CQUOTE := $(OUT)/cquote
CMAP_CNS_SRC := $(wildcard cmaps/cns/*)
CMAP_GB_SRC := $(wildcard cmaps/gb/*)
@@ -100,6 +118,7 @@ CMAP_KOREA_SRC := $(wildcard cmaps/korea/*)
FONT_BASE14_SRC := $(wildcard fonts/*.cff)
FONT_DROID_SRC := fonts/droid/DroidSans.ttf fonts/droid/DroidSansMono.ttf
FONT_CJK_SRC := fonts/droid/DroidSansFallback.ttf
+JAVASCRIPT_SRC := pdf/pdf_util.js
$(GEN)/cmap_cns.h : $(CMAP_CNS_SRC)
$(QUIET_GEN) ./$(CMAPDUMP) $@ $(CMAP_CNS_SRC)
@@ -117,18 +136,24 @@ $(GEN)/font_droid.h : $(FONT_DROID_SRC)
$(GEN)/font_cjk.h : $(FONT_CJK_SRC)
$(QUIET_GEN) ./$(FONTDUMP) $@ $(FONT_CJK_SRC)
+$(GEN)/js_util.h : $(JAVASCRIPT_SRC)
+ $(QUIET_GEN) ./$(CQUOTE) $@ $(JAVASCRIPT_SRC)
+
CMAP_HDR := $(addprefix $(GEN)/, cmap_cns.h cmap_gb.h cmap_japan.h cmap_korea.h)
FONT_HDR := $(GEN)/font_base14.h $(GEN)/font_droid.h $(GEN)/font_cjk.h
+JAVASCRIPT_HDR := $(GEN)/js_util.h
ifeq "$(CROSSCOMPILE)" ""
$(CMAP_HDR) : $(CMAPDUMP) | $(GEN)
$(FONT_HDR) : $(FONTDUMP) | $(GEN)
+$(JAVASCRIPT_HDR) : $(CQUOTE) | $(GEN)
endif
-generate: $(CMAP_HDR) $(FONT_HDR)
+generate: $(CMAP_HDR) $(FONT_HDR) $(JAVASCRIPT_HDR)
$(OUT)/pdf_cmap_table.o : $(CMAP_HDR)
$(OUT)/pdf_fontfile.o : $(FONT_HDR)
+$(OUT)/pdf_js.o : $(JAVASCRIPT_HDR)
$(OUT)/cmapdump.o : pdf/pdf_cmap.c pdf/pdf_cmap_parse.c
# --- Tools and Apps ---
@@ -144,6 +169,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 +214,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..cc61b89c
--- /dev/null
+++ b/apps/jstest_main.c
@@ -0,0 +1,396 @@
+#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;
+}
+
+int winchoiceinput(pdfapp_t *app, int nopts, char *opts[], int *nvals, char *vals[])
+{
+ return 0;
+}
+
+void winhelp(pdfapp_t*app)
+{
+}
+
+void winclose(pdfapp_t *app)
+{
+ pdfapp_close(app);
+ exit(0);
+}
+
+int winsavequery(pdfapp_t *app)
+{
+ return DISCARD;
+}
+
+int wingetsavepath(pdfapp_t *app, char *buf, int len)
+{
+ return 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 07b9e1c3..a0aab51a 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;
@@ -38,6 +88,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;
@@ -69,6 +123,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"
"\t-i\tignore errors and continue with the next file\n"
"\tpages\tcomma separated list of ranges\n");
exit(1);
@@ -99,6 +154,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;
@@ -106,6 +186,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);
@@ -124,6 +205,106 @@ 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_interactive *inter = fz_interact(doc);
+ fz_widget *widget = NULL;
+
+ if (inter)
+ widget = fz_first_widget(inter, page);
+
+ if (widget)
+ {
+ fprintf(mujstest_file, "GOTO %d\n", pagenum);
+ needshot = 1;
+ }
+ for (;widget; widget = fz_next_widget(inter, widget))
+ {
+ fz_rect rect = *fz_widget_get_bbox(widget);
+ int w = (rect.x1-rect.x0);
+ int h = (rect.y1-rect.y0);
+ int len;
+ int type = fz_widget_get_type(widget);
+
+ ++mujstest_count;
+ switch (type)
+ {
+ default:
+ fprintf(mujstest_file, "%% UNKNOWN %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1);
+ break;
+ case FZ_WIDGET_TYPE_PUSHBUTTON:
+ fprintf(mujstest_file, "%% PUSHBUTTON %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1);
+ break;
+ case FZ_WIDGET_TYPE_CHECKBOX:
+ fprintf(mujstest_file, "%% CHECKBOX %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1);
+ break;
+ case FZ_WIDGET_TYPE_RADIOBUTTON:
+ fprintf(mujstest_file, "%% RADIOBUTTON %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1);
+ break;
+ case FZ_WIDGET_TYPE_TEXT:
+ {
+ int maxlen = fz_widget_text_get_max_len(inter, widget);
+ int texttype = fz_widget_text_get_content_type(inter, widget);
+
+ /* 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;
+ if (len > maxlen)
+ len = maxlen;
+ fprintf(mujstest_file, "%% TEXT %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1);
+ switch (texttype)
+ {
+ default:
+ case FZ_WIDGET_CONTENT_UNRESTRAINED:
+ fprintf(mujstest_file, "TEXT %d ", mujstest_count);
+ escape_string(mujstest_file, len-3, lorem);
+ fprintf(mujstest_file, "\n");
+ break;
+ case FZ_WIDGET_CONTENT_NUMBER:
+ fprintf(mujstest_file, "TEXT %d\n", mujstest_count);
+ break;
+ case FZ_WIDGET_CONTENT_SPECIAL:
+ fprintf(mujstest_file, "TEXT %d\n", 46702919800 + mujstest_count);
+ break;
+ case FZ_WIDGET_CONTENT_DATE:
+ fprintf(mujstest_file, "TEXT Jun %d 1979\n", 1 + ((13 + mujstest_count) % 30));
+ break;
+ case FZ_WIDGET_CONTENT_TIME:
+ ++mujstest_count;
+ fprintf(mujstest_file, "TEXT %02d:%02d\n", ((mujstest_count/60) % 24), mujstest_count % 60);
+ break;
+ }
+ break;
+ }
+ case FZ_WIDGET_TYPE_LISTBOX:
+ fprintf(mujstest_file, "%% LISTBOX %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1);
+ break;
+ case FZ_WIDGET_TYPE_COMBOBOX:
+ fprintf(mujstest_file, "%% COMBOBOX %0.2f %0.2f %0.2f %0.2f\n", rect.x0, rect.y0, rect.x1, rect.y1);
+ break;
+ }
+ fprintf(mujstest_file, "CLICK %0.2f %0.2f\n", (rect.x0+rect.x1)/2, (rect.y0+rect.y1)/2);
+ }
+ }
+
if (uselist)
{
fz_try(ctx)
@@ -383,6 +564,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;
}
@@ -449,7 +635,7 @@ int main(int argc, char **argv)
fz_var(doc);
- while ((c = fz_getopt(argc, argv, "lo:p:r:R:ab:dgmtx5G:Iw:h:fi")) != -1)
+ while ((c = fz_getopt(argc, argv, "lo:p:r:R:ab:dgmtx5G:Iw:h:fij:")) != -1)
{
switch (c)
{
@@ -471,6 +657,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;
case 'i': ignore_errors = 1; break;
default: usage(); break;
}
@@ -479,12 +666,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)
{
@@ -549,8 +744,17 @@ 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);
@@ -558,7 +762,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-");
@@ -619,6 +823,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 1a17e172..57bc3e33 100644
--- a/apps/pdfapp.c
+++ b/apps/pdfapp.c
@@ -7,6 +7,13 @@
#define ZOOMSTEP 1.142857
#define BEYOND_THRESHHOLD 40
+#ifndef PATH_MAX
+#define PATH_MAX (1024)
+#endif
+
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
enum panning
{
@@ -82,6 +89,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)
@@ -190,6 +202,51 @@ void pdfapp_close(pdfapp_t *app)
fz_flush_warnings(app->ctx);
}
+static int pdfapp_save(pdfapp_t *app)
+{
+ char buf[PATH_MAX];
+
+ if (wingetsavepath(app, buf, PATH_MAX))
+ {
+ fz_write_options opts;
+
+ opts.do_ascii = 1;
+ opts.do_expand = 0;
+ opts.do_garbage = 1;
+ opts.do_linear = 0;
+
+ fz_write_document(app->doc, buf, &opts);
+
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+int pdfapp_preclose(pdfapp_t *app)
+{
+ fz_interactive *idoc = fz_interact(app->doc);
+
+ if (idoc && fz_has_unsaved_changes(idoc))
+ {
+ switch (winsavequery(app))
+ {
+ case DISCARD:
+ return 1;
+
+ case CANCEL:
+ return 0;
+
+ case SAVE:
+ return pdfapp_save(app);
+ }
+ }
+
+ return 1;
+}
+
static fz_matrix pdfapp_viewctm(pdfapp_t *app)
{
fz_matrix ctm;
@@ -313,7 +370,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)
{
@@ -361,11 +419,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);
if (app->page_list)
@@ -417,7 +471,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);
@@ -925,6 +979,13 @@ void pdfapp_onkey(pdfapp_t *app, int c)
break;
/*
+ * Saving the file
+ */
+ case 'S':
+ pdfapp_save(app);
+ break;
+
+ /*
* Reloading the file...
*/
@@ -1001,10 +1062,12 @@ void pdfapp_onkey(pdfapp_t *app, int c)
void pdfapp_onmouse(pdfapp_t *app, int x, int y, int btn, int modifiers, int state)
{
+ fz_context *ctx = app->ctx;
fz_bbox rect = fz_pixmap_bbox(app->ctx, app->image);
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;
@@ -1014,6 +1077,83 @@ 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)
+ {
+ switch (fz_widget_get_type(widget))
+ {
+ case FZ_WIDGET_TYPE_TEXT:
+ {
+ char *text = fz_widget_text_get_text(idoc, widget);
+ char *newtext = wintextinput(app, text);
+ fz_free(app->ctx, text);
+ if (newtext)
+ fz_widget_text_set_text(idoc, widget, newtext);
+ }
+ break;
+
+ case FZ_WIDGET_TYPE_LISTBOX:
+ case FZ_WIDGET_TYPE_COMBOBOX:
+ {
+ int nopts;
+ int nvals;
+ char **opts = NULL;
+ char **vals = NULL;
+
+ fz_var(opts);
+ fz_var(vals);
+
+ fz_try(ctx)
+ {
+ nopts = fz_widget_choice_get_options(idoc, widget, NULL);
+ opts = fz_malloc(ctx, nopts * sizeof(*opts));
+ (void)fz_widget_choice_get_options(idoc, widget, opts);
+
+ nvals = fz_widget_choice_get_value(idoc, widget, NULL);
+ vals = fz_malloc(ctx, MAX(nvals,nopts) * sizeof(*vals));
+ (void)fz_widget_choice_get_value(idoc, widget, vals);
+
+ if (winchoiceinput(app, nopts, opts, &nvals, vals))
+ fz_widget_choice_set_value(idoc, widget, nvals, vals);
+ }
+ fz_always(ctx)
+ {
+ fz_free(ctx, opts);
+ fz_free(ctx, vals);
+ }
+ fz_catch(ctx)
+ {
+ pdfapp_warn(app, "setting of choice failed");
+ }
+ }
+ break;
+ }
+ }
+
+ 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)
@@ -1024,7 +1164,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);
@@ -1035,10 +1175,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)
{
@@ -1099,8 +1250,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..ecdabcfc 100644
--- a/apps/pdfapp.h
+++ b/apps/pdfapp.h
@@ -14,7 +14,9 @@
typedef struct pdfapp_s pdfapp_t;
-enum { ARROW, HAND, WAIT };
+enum { ARROW, HAND, WAIT, CARET };
+
+enum { DISCARD, SAVE, CANCEL };
extern void winwarn(pdfapp_t*, char *s);
extern void winerror(pdfapp_t*, char *s);
@@ -23,6 +25,8 @@ 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 int winchoiceinput(pdfapp_t*, int nopts, char *opts[], int *nvals, char *vals[]);
extern void winopenuri(pdfapp_t*, char *s);
extern void wincursor(pdfapp_t*, int curs);
extern void windocopy(pdfapp_t*);
@@ -31,6 +35,8 @@ extern void windrawstring(pdfapp_t*, int x, int y, char *s);
extern void winclose(pdfapp_t*);
extern void winhelp(pdfapp_t*);
extern void winfullscreen(pdfapp_t*, int state);
+extern int winsavequery(pdfapp_t*);
+extern int wingetsavepath(pdfapp_t*, char *buf, int len);
struct pdfapp_s
{
@@ -46,6 +52,7 @@ struct pdfapp_s
int rotate;
fz_pixmap *image;
int grayscale;
+ fz_colorspace *colorspace;
int invert;
/* current page params */
@@ -87,6 +94,8 @@ struct pdfapp_s
int beyondy;
fz_bbox selr;
+ int nowaitcursor;
+
/* search state */
int isediting;
int searchdir;
@@ -103,6 +112,7 @@ struct pdfapp_s
void pdfapp_init(fz_context *ctx, pdfapp_t *app);
void pdfapp_open(pdfapp_t *app, char *filename, int reload);
void pdfapp_close(pdfapp_t *app);
+int pdfapp_preclose(pdfapp_t *app);
char *pdfapp_version(pdfapp_t *app);
char *pdfapp_usage(pdfapp_t *app);
@@ -111,6 +121,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..a232d2f2 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);
@@ -99,6 +99,16 @@ void winerror(pdfapp_t *app, char *msg)
exit(1);
}
+int winsavequery(pdfapp_t *app)
+{
+ switch(MessageBoxA(hwndframe, "File has unsaved changes. Do you want to save", "MuPDF", MB_YESNOCANCEL))
+ {
+ case IDYES: return SAVE;
+ case IDNO: return DISCARD;
+ default: return CANCEL;
+ }
+}
+
int winfilename(wchar_t *buf, int len)
{
OPENFILENAME ofn;
@@ -115,8 +125,41 @@ int winfilename(wchar_t *buf, int len)
return GetOpenFileNameW(&ofn);
}
+int wingetsavepath(pdfapp_t *app, char *buf, int len)
+{
+ OPENFILENAMEA ofn;
+ buf[0] = 0;
+ if (strlen(filename) < (unsigned int)len)
+ strcpy(buf, filename);
+ memset(&ofn, 0, sizeof(OPENFILENAME));
+ ofn.lStructSize = sizeof(OPENFILENAME);
+ ofn.hwndOwner = hwndframe;
+ ofn.lpstrFile = buf;
+ ofn.nMaxFile = len;
+ ofn.lpstrInitialDir = NULL;
+ ofn.lpstrTitle = "MuPDF: Save PDF file";
+ ofn.lpstrFilter = "Documents (*.pdf;*.xps;*.cbz;*.zip)\0*.zip;*.cbz;*.xps;*.pdf\0PDF Files (*.pdf)\0*.pdf\0XPS Files (*.xps)\0*.xps\0CBZ Files (*.cbz;*.zip)\0*.zip;*.cbz\0All Files\0*\0\0";
+ ofn.Flags = OFN_HIDEREADONLY;
+ if (GetSaveFileNameA(&ofn))
+ {
+ if (strlen(buf) < sizeof(filename))
+ strcpy(filename, buf);
+
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
static char pd_filename[256] = "The file is encrypted.";
static char pd_password[256] = "";
+static char td_textinput[1024] = "";
+static int cd_nopts;
+static int *cd_nvals;
+static char **cd_opts;
+static char **cd_vals;
static int pd_okay = 0;
INT CALLBACK
@@ -145,6 +188,80 @@ dlogpassproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
return FALSE;
}
+INT CALLBACK
+dlogtextproc(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;
+}
+
+INT CALLBACK
+dlogchoiceproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ HWND listbox;
+ int i;
+ int item;
+ int sel;
+ switch(message)
+ {
+ case WM_INITDIALOG:
+ listbox = GetDlgItem(hwnd, 3);
+ for (i = 0; i < cd_nopts; i++)
+ SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)cd_opts[i]);
+
+ /* FIXME: handle multiple select */
+ if (*cd_nvals > 0)
+ {
+ item = SendMessageA(listbox, LB_FINDSTRINGEXACT, -1, (LPARAM)cd_vals[0]);
+ if (item != LB_ERR)
+ SendMessageA(listbox, LB_SETCURSEL, item, 0);
+ }
+ return TRUE;
+ case WM_COMMAND:
+ switch(wParam)
+ {
+ case 1:
+ listbox = GetDlgItem(hwnd, 3);
+ *cd_nvals = 0;
+ for (i = 0; i < cd_nopts; i++)
+ {
+ item = SendMessageA(listbox, LB_FINDSTRINGEXACT, -1, (LPARAM)cd_opts[i]);
+ sel = SendMessageA(listbox, LB_GETSEL, item, 0);
+ if (sel && sel != LB_ERR)
+ cd_vals[(*cd_nvals)++] = cd_opts[i];
+ }
+ pd_okay = 1;
+ 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 +281,31 @@ 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, dlogtextproc);
+ if (code <= 0)
+ winerror(app, "cannot create text input dialog");
+ if (pd_okay)
+ return td_textinput;
+ return NULL;
+}
+
+int winchoiceinput(pdfapp_t *app, int nopts, char *opts[], int *nvals, char *vals[])
+{
+ int code;
+ cd_nopts = nopts;
+ cd_nvals = nvals;
+ cd_opts = opts;
+ cd_vals = vals;
+ code = DialogBoxW(NULL, L"IDD_DLOGLIST", hwndframe, dlogchoiceproc);
+ if (code <= 0)
+ winerror(app, "cannot create text input dialog");
+ return pd_okay;
+}
+
INT CALLBACK
dloginfoproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
@@ -316,6 +458,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));
@@ -371,8 +514,11 @@ void winopen()
void winclose(pdfapp_t *app)
{
- pdfapp_close(app);
- exit(0);
+ if (pdfapp_preclose(app))
+ {
+ pdfapp_close(app);
+ exit(0);
+ }
}
void wincursor(pdfapp_t *app, int curs)
@@ -383,6 +529,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)
@@ -698,6 +846,10 @@ frameproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
case WM_NOTIFY:
case WM_COMMAND:
return SendMessage(hwndview, message, wParam, lParam);
+
+ case WM_CLOSE:
+ if (!pdfapp_preclose(&gapp))
+ return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
diff --git a/apps/win_res.rc b/apps/win_res.rc
index 0efd1411..3cf601a3 100644
--- a/apps/win_res.rc
+++ b/apps/win_res.rc
@@ -49,6 +49,26 @@ BEGIN
LTEXT "", 0x27, 60, 125, 100, 10, 0
END
+IDD_DLOGTEXT DIALOG 50, 50, 204, 85
+STYLE 128 | 0x80000000
+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_DLOGLIST DIALOG 50, 50, 204, 85
+STYLE 128 | 0x80000000
+CAPTION " MuPDF: select an item"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LISTBOX 3,8,7,183,50,0x210102
+ 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 da156b06..f927e6d0 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,20 @@ 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;
+}
+
+int winchoiceinput(pdfapp_t *app, int nopts, char *opts[], int *nvals, char *vals[])
+{
+ /* FIXME: temporary dummy implementation */
+ return 0;
+}
+
/*
* X11 magic
*/
@@ -144,6 +158,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;
@@ -217,6 +232,18 @@ void winclose(pdfapp_t *app)
closing = 1;
}
+int winsavequery(pdfapp_t *app)
+{
+ /* FIXME: temporary dummy implementation */
+ return DISCARD;
+}
+
+int wingetsavepath(pdfapp_t *app, char *buf, int len)
+{
+ /* FIXME: temporary dummy implementation */
+ return 0;
+}
+
void cleanup(pdfapp_t *app)
{
fz_context *ctx = app->ctx;
@@ -227,6 +254,7 @@ void cleanup(pdfapp_t *app)
XFreePixmap(xdpy, xicon);
+ XFreeCursor(xdpy, xccaret);
XFreeCursor(xdpy, xcwait);
XFreeCursor(xdpy, xchand);
XFreeCursor(xdpy, xcarrow);
@@ -252,6 +280,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..61f2f4f1
--- /dev/null
+++ b/fitz/doc_interactive.c
@@ -0,0 +1,77 @@
+#include "fitz.h"
+#include "mupdf-internal.h"
+
+/*
+ PDF is currently the only interactive format, so no need
+ to indirect through function pointers.
+*/
+
+int fz_has_unsaved_changes(fz_interactive *idoc)
+{
+ return pdf_has_unsaved_changes((pdf_document*)idoc);
+}
+
+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);
+}
+
+fz_widget *fz_first_widget(fz_interactive *idoc, fz_page *page)
+{
+ return pdf_first_widget((pdf_document*)idoc, (pdf_page*)page);
+}
+
+fz_widget *fz_next_widget(fz_interactive *idoc, fz_widget *previous)
+{
+ return pdf_next_widget(previous);
+}
+
+char *fz_widget_text_get_text(fz_interactive *idoc, fz_widget *tw)
+{
+ return pdf_widget_text_get_text((pdf_document *)idoc, tw);
+}
+
+int fz_widget_text_get_max_len(fz_interactive *idoc, fz_widget *tw)
+{
+ return pdf_widget_text_get_max_len((pdf_document *)idoc, tw);
+}
+
+int fz_widget_text_get_content_type(fz_interactive *idoc, fz_widget *tw)
+{
+ return pdf_widget_text_get_content_type((pdf_document *)idoc, tw);
+}
+
+void fz_widget_text_set_text(fz_interactive *idoc, fz_widget *tw, char *text)
+{
+ pdf_widget_text_set_text((pdf_document *)idoc, tw, text);
+}
+
+int fz_widget_choice_get_options(fz_interactive *idoc, fz_widget *tw, char *opts[])
+{
+ return pdf_widget_choice_get_options((pdf_document *)idoc, tw, opts);
+}
+
+int fz_widget_choice_is_multiselect(fz_interactive *idoc, fz_widget *tw)
+{
+ return pdf_widget_choice_is_multiselect((pdf_document *)idoc, tw);
+}
+
+int fz_widget_choice_get_value(fz_interactive *idoc, fz_widget *tw, char *opts[])
+{
+ return pdf_widget_choice_get_value((pdf_document *)idoc, tw, opts);
+}
+
+void fz_widget_choice_set_value(fz_interactive *idoc, fz_widget *tw, int n, char *opts[])
+{
+ pdf_widget_choice_set_value((pdf_document *)idoc, tw, n, opts);
+}
diff --git a/fitz/fitz-internal.h b/fitz/fitz-internal.h
index 20e9e8b0..02d452f4 100644
--- a/fitz/fitz-internal.h
+++ b/fitz/fitz-internal.h
@@ -418,6 +418,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);
@@ -431,7 +441,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
{
@@ -1114,7 +1131,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 dd9424a9..8bbc2ec4 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,199 @@ 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 text widget content */
+enum
+{
+ FZ_WIDGET_CONTENT_UNRESTRAINED,
+ FZ_WIDGET_CONTENT_NUMBER,
+ FZ_WIDGET_CONTENT_SPECIAL,
+ FZ_WIDGET_CONTENT_DATE,
+ FZ_WIDGET_CONTENT_TIME
+};
+
+/* 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;
+
+/*
+ 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);
+
+/*
+ Determine whether changes have been made since the
+ document was opened or last saved.
+*/
+int fz_has_unsaved_changes(fz_interactive *idoc);
+
+/*
+ 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_first_widget: get first widget when enumerating
+*/
+fz_widget *fz_first_widget(fz_interactive *idoc, fz_page *page);
+
+/*
+ fz_next_widget: get next widget when enumerating
+*/
+fz_widget *fz_next_widget(fz_interactive *idoc, fz_widget *previous);
+
+/*
+ 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_rect *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_interactive *idoc, fz_widget *tw);
+
+/*
+ fz_widget_text_max_len: get the maximum number of
+ characters permitted in a text widget
+*/
+int fz_widget_text_get_max_len(fz_interactive *idoc, fz_widget *tw);
+
+/*
+ fz_widget_text_get_content_type: get the type of content
+ required by a text widget
+*/
+int fz_widget_text_get_content_type(fz_interactive *idoc, fz_widget *tw);
+
+/*
+ fz_widget_text_set_text: Update the text of a text widget.
+*/
+void fz_widget_text_set_text(fz_interactive *idoc, fz_widget *tw, char *text);
+
+/*
+ fz_widget_choice_get_options: get the list of options for a list
+ box or combo box. Returns the number of options and fills in their
+ names within the supplied array. Should first be called with a
+ NULL array to find out how big the array should be.
+*/
+int fz_widget_choice_get_options(fz_interactive *idoc, fz_widget *tw, char *opts[]);
+
+/*
+ fz_widget_choice_is_multiselect: returns whether a list box or
+ combo box supports selection of multiple options
+*/
+int fz_widget_choice_is_multiselect(fz_interactive *idoc, fz_widget *tw);
+
+/*
+ fz_widget_choice_get_value: get the value of a choice widget.
+ Returns the number of options curently selected and fills in
+ the supplied array with their strings. Should first be called
+ with NULL as the array to find out how big the array need to
+ be. The filled in elements should not be freed by the caller.
+*/
+int fz_widget_choice_get_value(fz_interactive *idoc, fz_widget *tw, char *opts[]);
+
+/*
+ fz_widget_set_value: set the value of a choice widget. The
+ caller should pass the number of options selected and an
+ array of their names
+*/
+void fz_widget_choice_set_value(fz_interactive *idoc, fz_widget *tw, int n, char *opts[]);
+
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 efd9e109..65e968fb 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,13 @@ struct pdf_document_s
pdf_obj **page_refs;
pdf_lexbuf_large lexbuf;
+
+ pdf_annot *focus;
+ pdf_obj *focus_obj;
+
+ pdf_js *js;
+ int recalculating;
+ int dirty;
};
pdf_document *pdf_open_document_no_run(fz_context *ctx, const char *filename);
@@ -315,8 +339,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
@@ -511,19 +536,22 @@ void pdf_drop_font(fz_context *ctx, pdf_font_desc *font);
void pdf_print_font(fz_context *ctx, pdf_font_desc *fontdesc);
#endif
+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;
+ int type;
};
fz_link_dest pdf_parse_link_dest(pdf_document *doc, pdf_obj *dest);
@@ -534,9 +562,18 @@ 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);
+int pdf_field_getType(pdf_document *doc, pdf_obj *field);
+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
*/
@@ -562,9 +599,71 @@ 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_has_unsaved_changes(pdf_document *doc);
+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);
+fz_widget *pdf_first_widget(pdf_document *doc, pdf_page *page);
+fz_widget *pdf_next_widget(fz_widget *previous);
+char *pdf_widget_text_get_text(pdf_document *doc, fz_widget *tw);
+int pdf_widget_text_get_max_len(pdf_document *doc, fz_widget *tw);
+int pdf_widget_text_get_content_type(pdf_document *doc, fz_widget *tw);
+void pdf_widget_text_set_text(pdf_document *doc, fz_widget *tw, char *text);
+int pdf_widget_choice_get_options(pdf_document *doc, fz_widget *tw, char *opts[]);
+int pdf_widget_choice_is_multiselect(pdf_document *doc, fz_widget *tw);
+int pdf_widget_choice_get_value(pdf_document *doc, fz_widget *tw, char *opts[]);
+void pdf_widget_choice_set_value(pdf_document *doc, fz_widget *tw, int n, char *opts[]);
+
+/*
+ * 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 59302951..f2d235b9 100644
--- a/pdf/mupdf.h
+++ b/pdf/mupdf.h
@@ -88,10 +88,10 @@ void pdf_print_obj(pdf_obj *obj);
void pdf_print_ref(pdf_obj *obj);
#endif
-char *pdf_to_utf8(fz_context *ctx, pdf_obj *src);
-unsigned short *pdf_to_ucs2(fz_context *ctx, pdf_obj *src); /* sumatrapdf */
-pdf_obj *pdf_to_utf8_name(fz_context *ctx, pdf_obj *src);
-char *pdf_from_ucs2(fz_context *ctx, unsigned short *str);
+char *pdf_to_utf8(pdf_document *xref, pdf_obj *src);
+unsigned short *pdf_to_ucs2(pdf_document *xref, pdf_obj *src); /* sumatrapdf */
+pdf_obj *pdf_to_utf8_name(pdf_document *xref, pdf_obj *src);
+char *pdf_from_ucs2(pdf_document *xref, unsigned short *str);
fz_rect pdf_to_rect(fz_context *ctx, pdf_obj *array);
fz_matrix pdf_to_matrix(fz_context *ctx, pdf_obj *array);
@@ -235,6 +235,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 d6b5096e..5a463336 100644
--- a/pdf/pdf_annot.c
+++ b/pdf/pdf_annot.c
@@ -213,7 +213,7 @@ pdf_parse_action(pdf_document *xref, pdf_obj *action)
{
ld.kind = FZ_LINK_URI;
ld.ld.uri.is_map = pdf_to_bool(pdf_dict_gets(action, "IsMap"));
- ld.ld.uri.uri = pdf_to_utf8(ctx, pdf_dict_gets(action, "URI"));
+ ld.ld.uri.uri = pdf_to_utf8(xref, pdf_dict_gets(action, "URI"));
}
else if (!strcmp(pdf_to_name(obj), "Launch"))
{
@@ -221,20 +221,20 @@ pdf_parse_action(pdf_document *xref, pdf_obj *action)
ld.kind = FZ_LINK_LAUNCH;
if (pdf_is_dict(dest))
dest = pdf_dict_gets(dest, "F");
- ld.ld.launch.file_spec = pdf_to_utf8(ctx, dest);
+ ld.ld.launch.file_spec = pdf_to_utf8(xref, dest);
ld.ld.launch.new_window = pdf_to_int(pdf_dict_gets(action, "NewWindow"));
}
else if (!strcmp(pdf_to_name(obj), "Named"))
{
ld.kind = FZ_LINK_NAMED;
- ld.ld.named.named = pdf_to_utf8(ctx, pdf_dict_gets(action, "N"));
+ ld.ld.named.named = pdf_to_utf8(xref, pdf_dict_gets(action, "N"));
}
else if (!strcmp(pdf_to_name(obj), "GoToR"))
{
dest = pdf_dict_gets(action, "D");
ld = pdf_parse_link_dest(xref, dest);
ld.kind = FZ_LINK_GOTOR;
- ld.ld.gotor.file_spec = pdf_to_utf8(ctx, pdf_dict_gets(action, "F"));
+ ld.ld.gotor.file_spec = pdf_to_utf8(xref, pdf_dict_gets(action, "F"));
ld.ld.gotor.new_window = pdf_to_int(pdf_dict_gets(action, "NewWindow"));
}
return ld;
@@ -351,11 +351,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;
@@ -367,47 +366,87 @@ 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;
+ annot->type = pdf_field_getType(xref, obj);
+
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;
+ annot->next = NULL;
- pdf_transform_annot(annot);
+ if (obj == xref->focus_obj)
+ xref->focus = annot;
- 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 d92b67d8..33a1a651 100644
--- a/pdf/pdf_font.c
+++ b/pdf/pdf_font.c
@@ -1181,3 +1181,55 @@ pdf_print_font(fz_context *ctx, pdf_font_desc *fontdesc)
}
}
#endif
+
+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 = 0;
+ float x = 0.0;
+
+ while(i < len)
+ {
+ float span;
+
+ gid = pdf_font_cid_to_gid(ctx, fontdesc, buf[i]);
+ h = pdf_lookup_hmtx(ctx, fontdesc, buf[i]);
+
+ span = h.w * fontsize / 1000.0;
+
+ if (x + span > room)
+ break;
+
+ x += span;
+ i ++;
+ }
+
+ if (count)
+ *count = i;
+
+ return x;
+}
diff --git a/pdf/pdf_form.c b/pdf/pdf_form.c
new file mode 100644
index 00000000..10b70824
--- /dev/null
+++ b/pdf/pdf_form.c
@@ -0,0 +1,2227 @@
+#include "fitz-internal.h"
+#include "mupdf-internal.h"
+
+#define MATRIX_COEFS (6)
+
+#define FZ_WIDGET_TYPE_NOT_WIDGET (-1)
+
+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_MultiSelect = 1 << (22-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
+};
+
+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"));
+}
+
+int pdf_field_getType(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 FZ_WIDGET_TYPE_NOT_WIDGET;
+}
+
+/* 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 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 char *to_font_encoding(fz_context *ctx, pdf_font_desc *font, char *utf8)
+{
+ int i;
+ int needs_converting = 0;
+
+ /* Temporay partial solution. We are using a slow lookup in the conversion
+ * below, so we avoid performing the conversion unnecessarily. We check for
+ * top-bit-set chars, and convert only if they are present. We should also
+ * check that the font encoding is one that agrees with utf8 from 0 to 7f,
+ * but for now we get away without doing so. This is after all an improvement
+ * on just strdup */
+ for (i = 0; utf8[i] != '\0'; i++)
+ {
+ if (utf8[i] & 0x80)
+ needs_converting = 1;
+ }
+
+ /* Even if we need to convert, we cannot do so if the font has no cid_to_ucs mapping */
+ if (needs_converting && font->cid_to_ucs)
+ {
+ char *buf = fz_malloc(ctx, strlen(utf8) + 1);
+ char *bufp = buf;
+
+ fz_try(ctx)
+ {
+ while(*utf8)
+ {
+ if (*utf8 & 0x80)
+ {
+ int rune;
+
+ utf8 += fz_chartorune(&rune, utf8);
+
+ /* Slow search for the cid that maps to the unicode value held in 'rune" */
+ for (i = 0; i < font->cid_to_ucs_len && font->cid_to_ucs[i] != rune; i++)
+ ;
+
+ /* If found store the cid */
+ if (i < font->cid_to_ucs_len)
+ *bufp++ = i;
+ }
+ else
+ {
+ *bufp++ = *utf8++;
+ }
+ }
+
+ *bufp = '\0';
+ }
+ fz_catch(ctx)
+ {
+ fz_free(ctx, buf);
+ fz_rethrow(ctx);
+ }
+
+ return buf;
+ }
+ else
+ {
+ /* If either no conversion is needed or the font has no cid_to_ucs
+ * mapping then leave unconverted, although in the latter case the result
+ * is likely incorrect */
+ return fz_strdup(ctx, utf8);
+ }
+}
+
+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)
+ {
+ get_text_widget_info(doc, obj, &info);
+
+ if (eventValue)
+ text = to_font_encoding(ctx, info.font_rec.font, eventValue);
+ else
+ text = pdf_field_getValue(doc, obj);
+
+ 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 update_combobox_appearance(pdf_document *doc, pdf_obj *obj)
+{
+ 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;
+ pdf_obj *val;
+ char *text;
+
+ memset(&info, 0, sizeof(info));
+
+ fz_var(info);
+ fz_var(form);
+ fz_var(fzbuf);
+ fz_try(ctx)
+ {
+ get_text_widget_info(doc, obj, &info);
+
+ val = get_inheritable(doc, obj, "V");
+
+ if (pdf_is_array(val))
+ val = pdf_array_get(val, 0);
+
+ text = pdf_to_str_buf(val);
+
+ if (!text)
+ text = "";
+
+ 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)
+ {
+ 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;
+
+ doc->dirty = 1;
+
+ switch (pdf_field_getType(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 = pdf_to_utf8(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(pdf_field_getType(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;
+ case FZ_WIDGET_TYPE_LISTBOX:
+ case FZ_WIDGET_TYPE_COMBOBOX:
+ /* Treating listbox and combobox identically for now,
+ * and the behaviour is most appropriate for a combobox */
+ update_combobox_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_has_unsaved_changes(pdf_document *doc)
+{
+ return doc->dirty;
+}
+
+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:
+ doc->focus = NULL;
+ pdf_drop_obj(doc->focus_obj);
+ doc->focus_obj = NULL;
+
+ if (annot)
+ {
+ doc->focus = annot;
+ doc->focus_obj = pdf_keep_obj(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(annot->type)
+ {
+ 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 (fz_widget *)doc->focus;
+}
+
+fz_widget *pdf_first_widget(pdf_document *doc, pdf_page *page)
+{
+ pdf_annot *annot = page->annots;
+
+ while (annot && annot->type == FZ_WIDGET_TYPE_NOT_WIDGET)
+ annot = annot->next;
+
+ return (fz_widget *)annot;
+}
+
+fz_widget *pdf_next_widget(fz_widget *previous)
+{
+ pdf_annot *annot = (pdf_annot *)previous;
+
+ if (annot)
+ annot = annot->next;
+
+ while (annot && annot->type == FZ_WIDGET_TYPE_NOT_WIDGET)
+ annot = annot->next;
+
+ return (fz_widget *)annot;
+}
+
+int fz_widget_get_type(fz_widget *widget)
+{
+ pdf_annot *annot = (pdf_annot *)widget;
+ return annot->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)
+{
+ fz_context *ctx = doc->ctx;
+
+ if (doc->recalculating)
+ return;
+
+ doc->recalculating = 1;
+ fz_try(ctx)
+ {
+ 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);
+ }
+ }
+ }
+ }
+ fz_always(ctx)
+ {
+ doc->recalculating = 0;
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+}
+
+void pdf_field_setValue(pdf_document *doc, pdf_obj *field, char *text)
+{
+ doc->dirty = 1;
+
+ 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 (pdf_field_getType(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);
+ }
+}
+
+fz_rect *fz_widget_get_bbox(fz_widget *widget)
+{
+ pdf_annot *annot = (pdf_annot *)widget;
+
+ return &annot->pagerect;
+}
+
+char *pdf_widget_text_get_text(pdf_document *doc, fz_widget *tw)
+{
+ pdf_annot *annot = (pdf_annot *)tw;
+ fz_context *ctx = doc->ctx;
+ char *text = NULL;
+
+ fz_var(text);
+ fz_try(ctx)
+ {
+ text = pdf_field_getValue(doc, annot->obj);
+ }
+ fz_catch(ctx)
+ {
+ fz_warn(ctx, "failed allocation in fz_widget_text_get_text");
+ }
+
+ return text;
+}
+
+int pdf_widget_text_get_max_len(pdf_document *doc, fz_widget *tw)
+{
+ pdf_annot *annot = (pdf_annot *)tw;
+
+ return pdf_to_int(get_inheritable(doc, annot->obj, "MaxLen"));
+}
+
+int pdf_widget_text_get_content_type(pdf_document *doc, fz_widget *tw)
+{
+ pdf_annot *annot = (pdf_annot *)tw;
+ fz_context *ctx = doc->ctx;
+ char *code = NULL;
+ int type = FZ_WIDGET_CONTENT_UNRESTRAINED;
+
+ fz_var(code);
+ fz_try(ctx)
+ {
+ code = get_string_or_stream(doc, pdf_dict_getp(annot->obj, "AA/F/JS"));
+ if (code)
+ {
+ if (strstr(code, "AFNumber_Format"))
+ type = FZ_WIDGET_CONTENT_NUMBER;
+ else if (strstr(code, "AFSpecial_Format"))
+ type = FZ_WIDGET_CONTENT_SPECIAL;
+ else if (strstr(code, "AFDate_FormatEx"))
+ type = FZ_WIDGET_CONTENT_DATE;
+ else if (strstr(code, "AFTime_FormatEx"))
+ type = FZ_WIDGET_CONTENT_TIME;
+ }
+ }
+ fz_always(ctx)
+ {
+ fz_free(ctx, code);
+ }
+ fz_catch(ctx)
+ {
+ fz_warn(ctx, "failure in fz_widget_text_get_content_type");
+ }
+
+ return type;
+}
+
+void pdf_widget_text_set_text(pdf_document *doc, fz_widget *tw, char *text)
+{
+ pdf_annot *annot = (pdf_annot *)tw;
+ fz_context *ctx = doc->ctx;
+
+ fz_try(ctx)
+ {
+ pdf_field_setValue(doc, annot->obj, text);
+ }
+ fz_catch(ctx)
+ {
+ fz_warn(ctx, "fz_widget_text_set_text failed");
+ }
+}
+
+int pdf_widget_choice_get_options(pdf_document *doc, fz_widget *tw, char *opts[])
+{
+ pdf_annot *annot = (pdf_annot *)tw;
+ pdf_obj *optarr;
+ int i, n;
+
+ if (!annot)
+ return 0;
+
+ optarr = pdf_dict_gets(annot->obj, "Opt");
+ n = pdf_array_len(optarr);
+
+ if (opts)
+ {
+ for (i = 0; i < n; i++)
+ {
+ opts[i] = pdf_to_str_buf(pdf_array_get(optarr, i));
+ }
+ }
+
+ return n;
+}
+
+int pdf_widget_choice_is_multiselect(pdf_document *doc, fz_widget *tw)
+{
+ pdf_annot *annot = (pdf_annot *)tw;
+
+ if (!annot) return 0;
+
+ switch(pdf_field_getType(doc, annot->obj))
+ {
+ case FZ_WIDGET_TYPE_LISTBOX:
+ case FZ_WIDGET_TYPE_COMBOBOX:
+ return (get_field_flags(doc, annot->obj) & Ff_MultiSelect) != 0;
+ default:
+ return 0;
+ }
+}
+
+int pdf_widget_choice_get_value(pdf_document *doc, fz_widget *tw, char *opts[])
+{
+ pdf_annot *annot = (pdf_annot *)tw;
+ pdf_obj *optarr;
+ int i, n;
+
+ if (!annot)
+ return 0;
+
+ optarr = pdf_dict_gets(annot->obj, "V");
+
+ if (pdf_is_string(optarr))
+ {
+ if (opts)
+ opts[0] = pdf_to_str_buf(optarr);
+
+ return 1;
+ }
+ else
+ {
+ n = pdf_array_len(optarr);
+
+ if (opts)
+ {
+ for (i = 0; i < n; i++)
+ {
+ pdf_obj *elem = pdf_array_get(optarr, i);
+
+ if (pdf_is_array(elem))
+ elem = pdf_array_get(elem, 1);
+
+ opts[i] = pdf_to_str_buf(elem);
+ }
+ }
+
+ return n;
+ }
+}
+
+void pdf_widget_choice_set_value(pdf_document *doc, fz_widget *tw, int n, char *opts[])
+{
+ fz_context *ctx = doc->ctx;
+ pdf_annot *annot = (pdf_annot *)tw;
+ pdf_obj *optarr = NULL, *opt = NULL;
+ int i;
+
+ if (!annot)
+ return;
+
+ fz_var(optarr);
+ fz_var(opt);
+ fz_try(ctx)
+ {
+ if (n != 1)
+ {
+ optarr = pdf_new_array(ctx, n);
+
+ for (i = 0; i < n; i++)
+ {
+ opt = pdf_new_string(ctx, opts[i], strlen(opts[i]));
+ pdf_array_push(optarr, opt);
+ pdf_drop_obj(opt);
+ opt = NULL;
+ }
+
+ pdf_dict_puts(annot->obj, "V", optarr);
+ pdf_drop_obj(optarr);
+ }
+ else
+ {
+ opt = pdf_new_string(ctx, opts[0], strlen(opts[0]));
+ pdf_dict_puts(annot->obj, "V", opt);
+ pdf_drop_obj(opt);
+ }
+
+ /* FIXME: when n > 1, we should be regenerating the indexes */
+ pdf_dict_dels(annot->obj, "I");
+
+ pdf_field_mark_dirty(ctx, annot->obj);
+ doc->dirty = 1;
+ }
+ fz_catch(ctx)
+ {
+ pdf_drop_obj(optarr);
+ pdf_drop_obj(opt);
+ fz_rethrow(ctx);
+ }
+}
diff --git a/pdf/pdf_js.c b/pdf/pdf_js.c
new file mode 100644
index 00000000..e8e56c26
--- /dev/null
+++ b/pdf/pdf_js.c
@@ -0,0 +1,426 @@
+#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)
+{
+ /* When testing on the cluster, redefine the Date object
+ * to use a fixed date */
+#ifdef CLUSTER
+ pdf_jsimp_execute(js->imp,
+"var MuPDFOldDate = Date\n"
+"Date = function() { return new MuPDFOldDate(1979,5,15); }\n"
+ );
+#endif
+
+ pdf_jsimp_execute(js->imp,
+#include "../generated/js_util.h"
+ );
+}
+
+pdf_js *pdf_new_js(pdf_document *doc)
+{
+ fz_context *ctx = doc->ctx;
+ pdf_js *js = NULL;
+ pdf_obj *javascript = NULL;
+ char *codebuf = NULL;
+
+ fz_var(js);
+ fz_var(javascript);
+ fz_var(codebuf);
+ 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)))
+ {
+ fz_try(ctx)
+ {
+ codebuf = pdf_to_utf8(doc, code);
+ pdf_jsimp_execute(js->imp, codebuf);
+ }
+ fz_always(ctx)
+ {
+ fz_free(ctx, codebuf);
+ codebuf = NULL;
+ }
+ fz_catch(ctx)
+ {
+ 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..70234d0f
--- /dev/null
+++ b/pdf/pdf_jsimp_v8.cpp
@@ -0,0 +1,419 @@
+/*
+ * 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-side object
+ * when the v8 object is garbage collected */
+ if (vType->dtr)
+ {
+ /* Wrap obj in a PDFJSImpGCObj, which takes a persistent handle to
+ * obj, and stores its type with it. The persistent handle tells v8
+ * it cannot just destroy obj leaving the client-side object hanging */
+ PDFJSImpGCObj *gco = new PDFJSImpGCObj(obj, vType);
+ /* Keep the wrapped object in a list, so that we can take back control
+ * of destroying client-side objects when shutting down this context */
+ vType->imp->gclist.insert(gco);
+ /* Tell v8 that it can destroy the persistent handle to obj when it has
+ * no further need for it, but it must inform us via gcCallback */
+ gco->pobj.MakeWeak(gco, gcCallback);
+ }
+
+ *robj = reinterpret_cast<pdf_jsimp_obj *>(new PDFJSImpObject(obj));
+ return NULL;
+}
+
+extern "C" 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_nametree.c b/pdf/pdf_nametree.c
index 7d8ac319..25fced52 100644
--- a/pdf/pdf_nametree.c
+++ b/pdf/pdf_nametree.c
@@ -135,7 +135,7 @@ pdf_load_name_tree_imp(pdf_obj *dict, pdf_document *xref, pdf_obj *node)
pdf_obj *val = pdf_array_get(names, i + 1);
if (pdf_is_string(key))
{
- key = pdf_to_utf8_name(ctx, key);
+ key = pdf_to_utf8_name(xref, key);
pdf_dict_put(dict, key, val);
pdf_drop_obj(key);
}
diff --git a/pdf/pdf_object.c b/pdf/pdf_object.c
index 955cd5cf..fb53d9b8 100644
--- a/pdf/pdf_object.c
+++ b/pdf/pdf_object.c
@@ -1226,6 +1226,8 @@ static void fmt_str(struct fmt *fmt, pdf_obj *obj)
fmt_puts(fmt, "\\(");
else if (c == ')')
fmt_puts(fmt, "\\)");
+ else if (c == '\\')
+ fmt_puts(fmt, "\\\\");
else if (c < 32 || c >= 127) {
char buf[16];
fmt_putc(fmt, '\\');
diff --git a/pdf/pdf_outline.c b/pdf/pdf_outline.c
index 8f93f5cb..616f5a15 100644
--- a/pdf/pdf_outline.c
+++ b/pdf/pdf_outline.c
@@ -29,7 +29,7 @@ pdf_load_outline_imp(pdf_document *xref, pdf_obj *dict)
obj = pdf_dict_gets(dict, "Title");
if (obj)
- node->title = pdf_to_utf8(ctx, obj);
+ node->title = pdf_to_utf8(xref, obj);
if ((obj = pdf_dict_gets(dict, "Dest")))
node->dest = pdf_parse_link_dest(xref, obj);
diff --git a/pdf/pdf_page.c b/pdf/pdf_page.c
index a6a0950b..458ab4d4 100644
--- a/pdf/pdf_page.c
+++ b/pdf/pdf_page.c
@@ -361,7 +361,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");
@@ -377,7 +377,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)
@@ -414,5 +414,10 @@ pdf_free_page(pdf_document *xref, pdf_page *page)
fz_drop_link(xref->ctx, page->links);
if (page->annots)
pdf_free_annot(xref->ctx, page->annots);
+ /* xref->focus, when not NULL, refers to one of
+ * the annotations and must be NULLed when the
+ * annotations are destroyed. xref->focus_obj
+ * keeps track of the actual annotation object. */
+ xref->focus = NULL;
fz_free(xref->ctx, page);
}
diff --git a/pdf/pdf_parse.c b/pdf/pdf_parse.c
index dd710891..4a7e421f 100644
--- a/pdf/pdf_parse.c
+++ b/pdf/pdf_parse.c
@@ -31,60 +31,89 @@ pdf_to_matrix(fz_context *ctx, pdf_obj *array)
/* Convert Unicode/PdfDocEncoding string into utf-8 */
char *
-pdf_to_utf8(fz_context *ctx, pdf_obj *src)
+pdf_to_utf8(pdf_document *xref, pdf_obj *src)
{
- unsigned char *srcptr = (unsigned char *) pdf_to_str_buf(src);
+ fz_context *ctx = xref->ctx;
+ fz_buffer *strmbuf = NULL;
+ unsigned char *srcptr;
char *dstptr, *dst;
- int srclen = pdf_to_str_len(src);
+ int srclen;
int dstlen = 0;
int ucs;
int i;
- if (srclen >= 2 && srcptr[0] == 254 && srcptr[1] == 255)
+ fz_var(strmbuf);
+ fz_try(ctx)
{
- for (i = 2; i + 1 < srclen; i += 2)
+ if (pdf_is_string(src))
{
- ucs = srcptr[i] << 8 | srcptr[i+1];
- dstlen += fz_runelen(ucs);
+ srcptr = (unsigned char *) pdf_to_str_buf(src);
+ srclen = pdf_to_str_len(src);
}
-
- dstptr = dst = fz_malloc(ctx, dstlen + 1);
-
- for (i = 2; i + 1 < srclen; i += 2)
+ else if (pdf_is_stream(xref, pdf_to_num(src), pdf_to_gen(src)))
{
- ucs = srcptr[i] << 8 | srcptr[i+1];
- dstptr += fz_runetochar(dstptr, ucs);
+ strmbuf = pdf_load_stream(xref, pdf_to_num(src), pdf_to_gen(src));
+ srclen = fz_buffer_storage(ctx, strmbuf, (unsigned char **)&srcptr);
}
- }
- else if (srclen >= 2 && srcptr[0] == 255 && srcptr[1] == 254)
- {
- for (i = 2; i + 1 < srclen; i += 2)
+ else
{
- ucs = srcptr[i] | srcptr[i+1] << 8;
- dstlen += fz_runelen(ucs);
+ srclen = 0;
}
- dstptr = dst = fz_malloc(ctx, dstlen + 1);
-
- for (i = 2; i + 1 < srclen; i += 2)
+ if (srclen >= 2 && srcptr[0] == 254 && srcptr[1] == 255)
{
- ucs = srcptr[i] | srcptr[i+1] << 8;
- dstptr += fz_runetochar(dstptr, ucs);
+ for (i = 2; i + 1 < srclen; i += 2)
+ {
+ ucs = srcptr[i] << 8 | srcptr[i+1];
+ dstlen += fz_runelen(ucs);
+ }
+
+ dstptr = dst = fz_malloc(ctx, dstlen + 1);
+
+ for (i = 2; i + 1 < srclen; i += 2)
+ {
+ ucs = srcptr[i] << 8 | srcptr[i+1];
+ dstptr += fz_runetochar(dstptr, ucs);
+ }
}
- }
- else
- {
- for (i = 0; i < srclen; i++)
- dstlen += fz_runelen(pdf_doc_encoding[srcptr[i]]);
+ else if (srclen >= 2 && srcptr[0] == 255 && srcptr[1] == 254)
+ {
+ for (i = 2; i + 1 < srclen; i += 2)
+ {
+ ucs = srcptr[i] | srcptr[i+1] << 8;
+ dstlen += fz_runelen(ucs);
+ }
- dstptr = dst = fz_malloc(ctx, dstlen + 1);
+ dstptr = dst = fz_malloc(ctx, dstlen + 1);
- for (i = 0; i < srclen; i++)
+ for (i = 2; i + 1 < srclen; i += 2)
+ {
+ ucs = srcptr[i] | srcptr[i+1] << 8;
+ dstptr += fz_runetochar(dstptr, ucs);
+ }
+ }
+ else
{
- ucs = pdf_doc_encoding[srcptr[i]];
- dstptr += fz_runetochar(dstptr, ucs);
+ for (i = 0; i < srclen; i++)
+ dstlen += fz_runelen(pdf_doc_encoding[srcptr[i]]);
+
+ dstptr = dst = fz_malloc(ctx, dstlen + 1);
+
+ for (i = 0; i < srclen; i++)
+ {
+ ucs = pdf_doc_encoding[srcptr[i]];
+ dstptr += fz_runetochar(dstptr, ucs);
+ }
}
}
+ fz_always(ctx)
+ {
+ fz_drop_buffer(ctx, strmbuf);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
*dstptr = '\0';
return dst;
@@ -92,8 +121,9 @@ pdf_to_utf8(fz_context *ctx, pdf_obj *src)
/* Convert Unicode/PdfDocEncoding string into ucs-2 */
unsigned short *
-pdf_to_ucs2(fz_context *ctx, pdf_obj *src)
+pdf_to_ucs2(pdf_document *xref, pdf_obj *src)
{
+ fz_context *ctx = xref->ctx;
unsigned char *srcptr = (unsigned char *) pdf_to_str_buf(src);
unsigned short *dstptr, *dst;
int srclen = pdf_to_str_len(src);
@@ -124,8 +154,9 @@ pdf_to_ucs2(fz_context *ctx, pdf_obj *src)
/* Convert UCS-2 string into PdfDocEncoding for authentication */
char *
-pdf_from_ucs2(fz_context *ctx, unsigned short *src)
+pdf_from_ucs2(pdf_document *xref, unsigned short *src)
{
+ fz_context *ctx = xref->ctx;
int i, j, len;
char *docstr;
@@ -162,11 +193,11 @@ pdf_from_ucs2(fz_context *ctx, unsigned short *src)
}
pdf_obj *
-pdf_to_utf8_name(fz_context *ctx, pdf_obj *src)
+pdf_to_utf8_name(pdf_document *xref, pdf_obj *src)
{
- char *buf = pdf_to_utf8(ctx, src);
- pdf_obj *dst = fz_new_name(ctx, buf);
- fz_free(ctx, buf);
+ char *buf = pdf_to_utf8(xref, src);
+ pdf_obj *dst = fz_new_name(xref->ctx, buf);
+ fz_free(xref->ctx, buf);
return dst;
}
diff --git a/pdf/pdf_repair.c b/pdf/pdf_repair.c
index a3efa030..10762dde 100644
--- a/pdf/pdf_repair.c
+++ b/pdf/pdf_repair.c
@@ -237,6 +237,8 @@ pdf_repair_xref(pdf_document *xref, pdf_lexbuf *buf)
fz_var(info);
fz_var(list);
+ xref->dirty = 1;
+
fz_seek(xref->file, 0, 0);
fz_try(ctx)
diff --git a/pdf/pdf_util.js b/pdf/pdf_util.js
new file mode 100644
index 00000000..d709661b
--- /dev/null
+++ b/pdf/pdf_util.js
@@ -0,0 +1,484 @@
+var MuPDF = new Array();
+
+MuPDF.monthName = ['January','February','March','April','May','June','July','August','September','October','November','December'];
+MuPDF.dayName = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
+
+MuPDF.shortMonthName = new Array();
+
+for (var i = 0; i < MuPDF.monthName.length; i++)
+ MuPDF.shortMonthName.push(MuPDF.monthName[i].substr(0,3));
+
+MuPDF.monthPattern = new RegExp();
+MuPDF.monthPattern.compile('('+MuPDF.shortMonthName.join('|')+')');
+
+MuPDF.padZeros = function(num, places)
+{
+ var s = num.toString();
+
+ if (s.length < places)
+ s = new Array(places-s.length+1).join('0') + s;
+
+ return s;
+}
+
+MuPDF.convertCase = function(str,cmd)
+{
+ switch (cmd)
+ {
+ case '>': return str.toUpperCase();
+ case '<': return str.toLowerCase();
+ default: return str;
+ }
+}
+
+var border = new Array();
+border.s = "Solid";
+border.d = "Dashed";
+border.b = "Beveled";
+border.i = "Inset";
+border.u = "Underline";
+var color = new Array();
+color.transparent = [ "T" ];
+color.black = [ "G", 0];
+color.white = [ "G", 1];
+color.red = [ "RGB", 1,0,0 ];
+color.green = [ "RGB", 0,1,0 ];
+color.blue = [ "RGB", 0,0,1 ];
+color.cyan = [ "CMYK", 1,0,0,0 ];
+color.magenta = [ "CMYK", 0,1,0,0 ];
+color.yellow = [ "CMYK", 0,0,1,0 ];
+color.dkGray = [ "G", 0.25];
+color.gray = [ "G", 0.5];
+color.ltGray = [ "G", 0.75];
+
+var util = new Array();
+
+util.printd = function(fmt, d)
+{
+ var regexp = /(m+|d+|y+|H+|h+|M+|s+|t+|[^mdyHhMst]+)/g;
+ var res = '';
+
+ if (!d)
+ return null;
+
+ var tokens = fmt.match(regexp);
+ for (var i = 0; i < tokens.length; i++)
+ {
+ switch(tokens[i])
+ {
+ case 'mmmm': res += MuPDF.monthName[d.getMonth()]; break;
+ case 'mmm': res += MuPDF.monthName[d.getMonth()].substr(0,3); break;
+ case 'mm': res += MuPDF.padZeros(d.getMonth()+1, 2); break;
+ case 'm': res += d.getMonth()+1; break;
+ case 'dddd': res += MuPDF.dayName[d.getDay()]; break;
+ case 'ddd': res += MuPDF.dayName[d.getDay()].substr(0,3); break;
+ case 'dd': res += MuPDF.padZeros(d.getDate(), 2); break;
+ case 'd': res += d.getDate(); break;
+ case 'yyyy': res += d.getFullYear(); break;
+ case 'yy': res += d.getFullYear()%100; break;
+ case 'HH': res += MuPDF.padZeros(d.getHours(), 2); break;
+ case 'H': res += d.getHours(); break;
+ case 'hh': res += MuPDF.padZeros((d.getHours()+11)%12+1, 2); break;
+ case 'h': res += (d.getHours()+11)%12+1; break;
+ case 'MM': res += MuPDF.padZeros(d.getMinutes(), 2); break;
+ case 'M': res += d.getMinutes(); break;
+ case 'ss': res += MuPDF.padZeros(d.getSeconds(), 2); break;
+ case 's': res += d.getSeconds(); break;
+ case 'tt': res += d.getHours() < 12 ? 'am' : 'pm'; break;
+ case 't': res += d.getHours() < 12 ? 'a' : 'p'; break;
+ default: res += tokens[i];
+ }
+ }
+
+ return res;
+}
+
+util.printx = function(fmt, val)
+{
+ var cs = '=';
+ var res = '';
+ var i = 0;
+ var m;
+
+ while (i < fmt.length)
+ {
+ switch (fmt.charAt(i))
+ {
+ case '\\':
+ i++;
+ if (i >= fmt.length) return res;
+ res += fmt.charAt(i);
+ break;
+
+ case 'X':
+ m = val.match(/\w/);
+ if (!m) return res;
+ res += MuPDF.convertCase(m[0],cs);
+ val = val.replace(/^\W*\w/,'');
+ break;
+
+ case 'A':
+ m = val.match(/[A-z]/);
+ if (!m) return res;
+ res += MuPDF.convertCase(m[0],cs);
+ val = val.replace(/^[^A-z]*[A-z]/,'');
+ break;
+
+ case '9':
+ m = val.match(/\d/);
+ if (!m) return res;
+ res += m[0];
+ val = val.replace(/^\D*\d/,'');
+ break;
+
+ case '*':
+ res += val;
+ val = '';
+ break;
+
+ case '?':
+ if (!val) return res;
+ res += MuPDF.convertCase(val.charAt(0),cs);
+ val = val.substr(1);
+ break;
+
+ case '=':
+ case '>':
+ case '<':
+ cs = fmt.charAt(i);
+ break;
+
+ default:
+ res += MuPDF.convertCase(fmt.charAt(i),cs);
+ break;
+ }
+
+ i++;
+ }
+
+ return res;
+}
+
+function AFExtractTime(dt)
+{
+ var ampm = dt.match(/(am|pm)/);
+ dt = dt.replace(/(am|pm)/, '');
+ var t = dt.match(/\d{1,2}:\d{1,2}:\d{1,2}/);
+ dt = dt.replace(/\d{1,2}:\d{1,2}:\d{1,2}/, '');
+ if (!t)
+ {
+ t = dt.match(/\d{1,2}:\d{1,2}/);
+ dt = dt.replace(/\d{1,2}:\d{1,2}/, '');
+ }
+
+ return [dt, t?t[0]+(ampm?ampm[0]:''):''];
+}
+
+function AFParseDateOrder(fmt)
+{
+ var order = '';
+
+ // Ensure all present with those not added in default order
+ fmt += "mdy";
+
+ for (var i = 0; i < fmt.length; i++)
+ {
+ var c = fmt.charAt(i);
+
+ if ('ymd'.indexOf(c) != -1 && order.indexOf(c) == -1)
+ order += c;
+ }
+
+ return order;
+}
+
+function AFMatchMonth(d)
+{
+ var m = d.match(MuPDF.monthPattern);
+
+ return m ? MuPDF.shortMonthName.indexOf(m[0]) : null;
+}
+
+function AFParseTime(str, d)
+{
+ if (!str)
+ return d;
+
+ if (!d)
+ d = new Date();
+
+ var ampm = str.match(/(am|pm)/);
+ var nums = str.match(/\d+/g);
+ var hour, min, sec;
+
+ if (!nums)
+ return null;
+
+ sec = 0;
+
+ switch (nums.length)
+ {
+ case 3:
+ sec = nums[2];
+ case 2:
+ hour = nums[0];
+ min = nums[1];
+ break;
+
+ default:
+ return null;
+ }
+
+ if (ampm == 'am' && hour < 12)
+ hour = 12 + hour;
+
+ if (ampm == 'pm' && hour >= 12)
+ hour = 0 + hour - 12;
+
+ d.setHours(hour, min, sec);
+
+ if (d.getHours() != hour || d.getMinutes() != min || d.getSeconds() != sec)
+ return null;
+
+ return d;
+}
+
+function AFParseDateEx(d, fmt)
+{
+ var dt = AFExtractTime(d);
+ var nums = dt[0].match(/\d+/g);
+ var order = AFParseDateOrder(fmt);
+ var text_month = AFMatchMonth(dt[0]);
+ var dout = new Date();
+ var year = dout.getFullYear();
+ var month = dout.getMonth();
+ var date = dout.getDate();
+
+ dout.setHours(12,0,0);
+
+ if (nums.length < 1 || nums.length > 3)
+ return null;
+
+ if (nums.length < 3 && text_month)
+ {
+ // Use the text month rather than one of the numbers
+ month = text_month;
+ order = order.replace('m','');
+ }
+
+ order = order.substr(0, nums.length);
+
+ // If year and month specified but not date then use the 1st
+ if (order == "ym" || (order == "y" && text_month))
+ date = 1;
+
+ for (var i = 0; i < nums.length; i++)
+ {
+ switch (order.charAt(i))
+ {
+ case 'y': year = nums[i]; break;
+ case 'm': month = nums[i] - 1; break;
+ case 'd': date = nums[i]; break;
+ }
+ }
+
+ if (year < 100)
+ {
+ if (fmt.search("yyyy") != -1)
+ return null;
+
+ if (year >= 50)
+ year = 1900 + year;
+ else if (year >= 0)
+ year = 2000 + year;
+ }
+
+ dout.setFullYear(year, month, date);
+
+ if (dout.getFullYear() != year || dout.getMonth() != month || dout.getDate() != date)
+ return null;
+
+ return AFParseTime(dt[1], dout);
+}
+
+function AFDate_FormatEx(fmt)
+{
+ var d = AFParseDateEx(event.value, fmt);
+
+ event.value = d ? util.printd(fmt, d) : "";
+}
+
+function AFDate_Format(index)
+{
+ var formats = ['m/d','m/d/yy','mm/dd/yy','mm/yy','d-mmm','d-mmm-yy','dd-mm-yy','yy-mm-dd',
+ 'mmm-yy','mmmm-yy','mmm d, yyyy','mmmm d, yyyy','m/d/yy h:MM tt','m/d/yy HH:MM'];
+ AFDate_FormatEx(formats[index]);
+}
+
+function AFTime_FormatEx(fmt)
+{
+ var d = AFParseTime(event.value, null);
+
+ event.value = d ? util.printd(fmt, d) : '';
+}
+
+function AFTime_Format(index)
+{
+ var formats = ['HH:MM','h:MM tt','HH:MM:ss','h:MM:ss tt'];
+
+ AFTime_FormatEx(formats[index]);
+}
+
+function AFSpecial_Format(index)
+{
+ var res;
+
+ switch (index)
+ {
+ case 0:
+ res = util.printx('99999', event.value);
+ break;
+ case 1:
+ res = util.printx('99999-9999', event.value);
+ break;
+ case 2:
+ res = util.printx('9999999999', event.value);
+ res = util.printx(res.length >= 10 ? '(999) 999-9999' : '999-9999', event.value);
+ break;
+ case 3:
+ res = util.printx('999-99-9999', event.value);
+ break;
+ }
+
+ event.value = res ? res : '';
+}
+
+function AFNumber_Format(nDec,sepStyle,negStyle,currStyle,strCurrency,bCurrencyPrepend)
+{
+ var val = event.value;
+ var fracpart;
+ var intpart;
+ var point = sepStyle&2 ? ',' : '.';
+ var separator = sepStyle&2 ? '.' : ',';
+
+ if (/^\D*\./.test(val))
+ val = '0'+val;
+
+ var groups = val.match(/\d+/g);
+
+ switch (groups.length)
+ {
+ case 0:
+ return;
+ case 1:
+ fracpart = '';
+ intpart = groups[0];
+ break;
+ default:
+ fracpart = groups.pop();
+ intpart = groups.join('');
+ break;
+ }
+
+ // Remove leading zeros
+ intpart = intpart.replace(/^0*/,'');
+ if (!intpart)
+ intpart = '0';
+
+ if ((sepStyle & 1) == 0)
+ {
+ // Add the thousands sepearators: pad to length multiple of 3 with zeros,
+ // split into 3s, join with separator, and remove the leading zeros
+ intpart = new Array(2-(intpart.length+2)%3+1).join('0') + intpart;
+ intpart = intpart.match(/.../g).join(separator).replace(/^0*/,'');
+ }
+
+ if (!intpart)
+ intpart = '0';
+
+ // Adjust fractional part to correct number of decimal places
+ fracpart += new Array(nDec+1).join('0');
+ fracpart = fracpart.substr(0,nDec);
+
+ if (fracpart)
+ intpart += point+fracpart;
+
+ if (bCurrencyPrepend)
+ intpart = strCurrency+intpart;
+ else
+ intpart += strCurrency;
+
+ if (/-/.test(val))
+ {
+ switch (negStyle)
+ {
+ case 0:
+ intpart = '-'+intpart;
+ break;
+ case 1:
+ break;
+ case 2:
+ case 3:
+ intpart = '('+intpart+')';
+ break;
+ }
+ }
+
+ if (negStyle&1)
+ event.target.textColor = /-/.text(val) ? color.red : color.black;
+
+ event.value = intpart;
+}
+
+function AFSimple_Calculate(op, list)
+{
+ var res;
+
+ switch (op)
+ {
+ case 'SUM':
+ res = 0;
+ break;
+ case 'PRD':
+ res = 1;
+ break;
+ case 'AVG':
+ res = 0;
+ break;
+ }
+
+ if (typeof list == 'string')
+ list = list.split(/ *, */);
+
+ for (var i = 0; i < list.length; i++)
+ {
+ var field = getField(list[i]);
+ var value = Number(field.value);
+
+ switch (op)
+ {
+ case 'SUM':
+ res += value;
+ break;
+ case 'PRD':
+ res *= value;
+ break;
+ case 'AVG':
+ res += value;
+ break;
+ case 'MIN':
+ if (i == 0 || value < res)
+ res = value;
+ break;
+ case 'MAX':
+ if (i == 0 || value > res)
+ res = value;
+ break;
+ }
+ }
+
+ if (op == 'AVG')
+ res /= list.length;
+
+ event.value = res;
+}
diff --git a/pdf/pdf_write.c b/pdf/pdf_write.c
index 6a07894a..5d03ffe3 100644
--- a/pdf/pdf_write.c
+++ b/pdf/pdf_write.c
@@ -2217,6 +2217,7 @@ void pdf_write_document(pdf_document *xref, char *filename, fz_write_options *fz
writexref(xref, &opts, 0, xref->len, 1, 0, opts.first_xref_offset);
}
+ xref->dirty = 0;
}
fz_always(ctx)
{
diff --git a/pdf/pdf_xobject.c b/pdf/pdf_xobject.c
index c5fc2a83..afa86527 100644
--- a/pdf/pdf_xobject.c
+++ b/pdf/pdf_xobject.c
@@ -185,7 +185,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;
@@ -224,6 +224,24 @@ pdf_new_xobject(pdf_document *xref, fz_rect *bbox, fz_matrix *mat)
void pdf_update_xobject_contents(pdf_document *xref, pdf_xobject *form, fz_buffer *buffer)
{
- pdf_dict_dels(form->contents, "Filter");
- pdf_update_stream(xref, pdf_to_num(form->contents), buffer);
+ fz_context *ctx = xref->ctx;
+ pdf_obj *len = NULL;
+
+ fz_var(len);
+
+ fz_try(ctx)
+ {
+ len = pdf_new_int(ctx, buffer->len);
+ pdf_dict_dels(form->contents, "Filter");
+ pdf_dict_puts(form->contents, "Length", len);
+ pdf_update_stream(xref, pdf_to_num(form->contents), buffer);
+ }
+ fz_always(ctx)
+ {
+ pdf_drop_obj(len);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
}
diff --git a/pdf/pdf_xref.c b/pdf/pdf_xref.c
index 050a6f53..7c14265d 100644
--- a/pdf/pdf_xref.c
+++ b/pdf/pdf_xref.c
@@ -781,6 +781,7 @@ pdf_init_document(pdf_document *xref)
dict = NULL;
}
}
+ xref->js = pdf_new_js(xref);
}
fz_catch(ctx)
{
@@ -810,6 +811,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++)
@@ -838,6 +841,8 @@ pdf_close_document(pdf_document *xref)
fz_free(ctx, xref->page_refs);
}
+ if (xref->focus_obj)
+ pdf_drop_obj(xref->focus_obj);
if (xref->file)
fz_close(xref->file);
pdf_drop_obj(xref->trailer);
@@ -1234,7 +1239,7 @@ pdf_meta(pdf_document *doc, int key, void *ptr, int size)
}
if (info && ptr && size)
{
- char *utf8 = pdf_to_utf8(doc->ctx, info);
+ char *utf8 = pdf_to_utf8(doc, info);
strncpy(ptr, utf8, size);
((char *)ptr)[size-1] = 0;
fz_free(doc->ctx, utf8);
@@ -1246,8 +1251,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.
@@ -1256,73 +1267,28 @@ 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;
+ doc->super.write = (void*)pdf_write_document;
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/scripts/cquote.c b/scripts/cquote.c
new file mode 100644
index 00000000..6383f80f
--- /dev/null
+++ b/scripts/cquote.c
@@ -0,0 +1,134 @@
+/* cquote.c -- Turn the contents of a file into a quoted string */
+
+#include <stdio.h>
+#include <string.h>
+
+/* We never want to build memento versions of the cquote util */
+#undef MEMENTO
+
+static void
+clean(char *p)
+{
+ while (*p)
+ {
+ if ((*p == '/') || (*p == '.') || (*p == '\\') || (*p == '-'))
+ *p = '_';
+ p ++;
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ FILE *fi, *fo;
+ char name[256];
+ char *realname;
+ int i, c;
+ int bol = 1;
+
+ if (argc < 3)
+ {
+ fprintf(stderr, "usage: cquote output.c lots of text files\n");
+ return 1;
+ }
+
+ fo = fopen(argv[1], "wb");
+ if (!fo)
+ {
+ fprintf(stderr, "cquote: could not open output file '%s'\n", argv[1]);
+ return 1;
+ }
+
+ fprintf(fo, "/* This is an automatically generated file. Do not edit. */\n");
+
+ for (i = 2; i < argc; i++)
+ {
+ realname = strrchr(argv[i], '/');
+ if (!realname)
+ realname = strrchr(argv[i], '\\');
+ if (realname)
+ realname ++;
+ else
+ realname = argv[i];
+
+ if (strlen(realname) > (sizeof name - 1))
+ {
+ fprintf(stderr, "cquote: file name too long\n");
+ if (fclose(fo))
+ {
+ fprintf(stderr, "cquote: could not close output file '%s'\n", argv[1]);
+ return 1;
+ }
+ return 1;
+ }
+
+ strcpy(name, realname);
+ clean(name);
+
+ fi = fopen(argv[i], "rb");
+
+ fprintf(fo, "\n/* %s */\n\n", name);
+
+ c = fgetc(fi);
+ while (c != EOF)
+ {
+ int eol = 0;
+
+ if (bol)
+ {
+ fputc('\"', fo);
+ bol = 0;
+ }
+
+ switch (c)
+ {
+ case '\"':
+ fprintf(fo, "\\\"");
+ break;
+
+ case '\\':
+ fprintf(fo, "\\\\");
+ break;
+
+ case '\r':
+ case '\n':
+ eol = 1;
+ break;
+
+ default:
+ fputc(c, fo);
+ break;
+ }
+
+ if (eol)
+ {
+ fprintf(fo, "\\n\"\n");
+ while ((c = fgetc(fi)) == '\r' || c == '\n')
+ ;
+ bol = 1;
+ }
+ else
+ {
+ c = fgetc(fi);
+ }
+ }
+
+ if (!bol)
+ fprintf(fi, "\\n\"\n");
+
+ if (fclose(fi))
+ {
+ fprintf(stderr, "cquote: could not close input file '%s'\n", argv[i]);
+ return 1;
+ }
+
+ }
+
+ if (fclose(fo))
+ {
+ fprintf(stderr, "cquote: could not close output file '%s'\n", argv[1]);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/win32/generate.bat b/win32/generate.bat
index 0a5d7f49..78393359 100644
--- a/win32/generate.bat
+++ b/win32/generate.bat
@@ -5,9 +5,11 @@ if not exist generated mkdir generated
cl /nologo -Ifitz -Ipdf scripts/fontdump.c
cl /nologo -Ifitz -Ipdf scripts/cmapdump.c
+cl /nologo -Ifitz -Ipdf scripts/cquote.c
if not exist fontdump.exe goto usage
if not exist cmapdump.exe goto usage
+if not exist cquote.exe goto usage
if not exist generated/font_base14.h fontdump.exe generated/font_base14.h fonts/Dingbats.cff fonts/NimbusMonL-Bold.cff fonts/NimbusMonL-BoldObli.cff fonts/NimbusMonL-Regu.cff fonts/NimbusMonL-ReguObli.cff fonts/NimbusRomNo9L-Medi.cff fonts/NimbusRomNo9L-MediItal.cff fonts/NimbusRomNo9L-Regu.cff fonts/NimbusRomNo9L-ReguItal.cff fonts/NimbusSanL-Bold.cff fonts/NimbusSanL-BoldItal.cff fonts/NimbusSanL-Regu.cff fonts/NimbusSanL-ReguItal.cff fonts/StandardSymL.cff
@@ -23,7 +25,9 @@ if not exist generated/cmap_japan.h cmapdump.exe generated/cmap_japan.h cmaps/ja
if not exist generated/cmap_korea.h cmapdump.exe generated/cmap_korea.h cmaps/korea/Adobe-Korea1-UCS2 cmaps/korea/Adobe-Korea1-0 cmaps/korea/Adobe-Korea1-1 cmaps/korea/Adobe-Korea1-2 cmaps/korea/KSC-EUC-H cmaps/korea/KSC-EUC-V cmaps/korea/KSC-H cmaps/korea/KSC-Johab-H cmaps/korea/KSC-Johab-V cmaps/korea/KSC-V cmaps/korea/KSCms-UHC-H cmaps/korea/KSCms-UHC-HW-H cmaps/korea/KSCms-UHC-HW-V cmaps/korea/KSCms-UHC-V cmaps/korea/KSCpc-EUC-H cmaps/korea/KSCpc-EUC-V cmaps/korea/UniKS-UCS2-H cmaps/korea/UniKS-UCS2-V cmaps/korea/UniKS-UTF16-H cmaps/korea/UniKS-UTF16-V
-del cmapdump.obj fontdump.obj cmapdump.exe fontdump.exe
+if not exist generated/js_util.h cquote.exe generated/js_util.h pdf/pdf_util.js
+
+del cmapdump.obj fontdump.obj cquote.obj cmapdump.exe fontdump.exe cquote.exe
goto fin
diff --git a/win32/generated.vcproj b/win32/generated.vcproj
index e5c06f0b..a692a9d4 100644
--- a/win32/generated.vcproj
+++ b/win32/generated.vcproj
@@ -88,6 +88,10 @@
>
</File>
<File
+ RelativePath="..\scripts\cquote.c"
+ >
+ </File>
+ <File
RelativePath="..\scripts\fontdump.c"
>
</File>
diff --git a/win32/libmupdf-v8.vcproj b/win32/libmupdf-v8.vcproj
new file mode 100644
index 00000000..81c16c79
--- /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-9&quot;;&quot;..\thirdparty\openjpeg-1.5.0-patched\libopenjpeg&quot;;&quot;..\thirdparty\zlib-1.2.7&quot;;&quot;..\thirdparty\freetype-2.4.10\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 b9403073..2715588d 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 e7756f89..e36f9c0f 100644
--- a/xps/xps_zip.c
+++ b/xps/xps_zip.c
@@ -670,54 +670,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:
@@ -731,15 +686,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;
}