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