#include "gl-app.h" #include #include #include #include #include #ifdef _MSC_VER #define stat _stat #endif #ifndef _WIN32 #include #endif #include "mujs.h" #ifndef PATH_MAX #define PATH_MAX 2048 #endif #ifndef _WIN32 #include /* for fork, exec, and getcwd */ #else #include /* for getcwd */ char *realpath(const char *path, char *resolved_path); /* in gl-file.c */ #endif #ifdef __APPLE__ static void cleanup(void); void glutLeaveMainLoop(void) { cleanup(); exit(0); } #endif time_t stat_mtime(const char *path) { struct stat info; if (stat(path, &info) < 0) return 0; return info.st_mtime; } fz_context *ctx = NULL; pdf_document *pdf = NULL; pdf_page *page = NULL; pdf_annot *selected_annot = NULL; fz_stext_page *page_text = NULL; fz_matrix draw_page_ctm, view_page_ctm, view_page_inv_ctm; fz_rect page_bounds, draw_page_bounds, view_page_bounds; fz_irect view_page_area; char filename[PATH_MAX]; enum { /* Screen furniture: aggregate size of unusable space from title bars, task bars, window borders, etc */ SCREEN_FURNITURE_W = 20, SCREEN_FURNITURE_H = 40, }; static void open_browser(const char *uri) { char buf[PATH_MAX]; /* Relative file:// URI, make it absolute! */ if (!strncmp(uri, "file://", 7) && uri[7] != '/') { char buf_base[PATH_MAX]; char buf_cwd[PATH_MAX]; fz_dirname(buf_base, filename, sizeof buf_base); getcwd(buf_cwd, sizeof buf_cwd); fz_snprintf(buf, sizeof buf, "file://%s/%s/%s", buf_cwd, buf_base, uri+7); fz_cleanname(buf+7); uri = buf; } #ifdef _WIN32 ShellExecuteA(NULL, "open", uri, 0, 0, SW_SHOWNORMAL); #else const char *browser = getenv("BROWSER"); if (!browser) { #ifdef __APPLE__ browser = "open"; #else browser = "xdg-open"; #endif } if (fork() == 0) { execlp(browser, browser, uri, (char*)0); fprintf(stderr, "cannot exec '%s'\n", browser); exit(0); } #endif } static const int zoom_list[] = { 24, 36, 48, 60, 72, 84, 96, 108, 120, 144, 168, 192, 228, 264, 300, 350, 400, 450, 500, 550, 600 }; static int zoom_in(int oldres) { int i; for (i = 0; i < (int)nelem(zoom_list) - 1; ++i) if (zoom_list[i] <= oldres && zoom_list[i+1] > oldres) return zoom_list[i+1]; return zoom_list[i]; } static int zoom_out(int oldres) { int i; for (i = 0; i < (int)nelem(zoom_list) - 1; ++i) if (zoom_list[i] < oldres && zoom_list[i+1] >= oldres) return zoom_list[i]; return zoom_list[0]; } static const char *paper_size_name(int w, int h) { /* ISO A */ if (w == 2384 && h == 3370) return "A0"; if (w == 1684 && h == 2384) return "A1"; if (w == 1191 && h == 1684) return "A2"; if (w == 842 && h == 1191) return "A3"; if (w == 595 && h == 842) return "A4"; if (w == 420 && h == 595) return "A5"; if (w == 297 && h == 420) return "A6"; /* US */ if (w == 612 && h == 792) return "Letter"; if (w == 612 && h == 1008) return "Legal"; if (w == 792 && h == 1224) return "Ledger"; if (w == 1224 && h == 792) return "Tabloid"; return NULL; } #define MINRES (zoom_list[0]) #define MAXRES (zoom_list[nelem(zoom_list)-1]) #define DEFRES 96 static char *password = ""; static char *anchor = NULL; static float layout_w = FZ_DEFAULT_LAYOUT_W; static float layout_h = FZ_DEFAULT_LAYOUT_H; static float layout_em = FZ_DEFAULT_LAYOUT_EM; static char *layout_css = NULL; static int layout_use_doc_css = 1; static int enable_js = 1; static int tint_white = 0xFFFFF0; static int tint_black = 0x303030; static fz_document *doc = NULL; static fz_page *fzpage = NULL; static fz_separations *seps = NULL; static fz_outline *outline = NULL; static fz_link *links = NULL; static int number = 0; static struct texture page_tex = { 0 }; static int screen_w = 0, screen_h = 0; static int scroll_x = 0, scroll_y = 0; static int canvas_x = 0, canvas_w = 100; static int canvas_y = 0, canvas_h = 100; static int outline_w = 14; /* to be scaled by lineheight */ static int annotate_w = 12; /* to be scaled by lineheight */ static int oldtint = 0, currenttint = 0; static int oldinvert = 0, currentinvert = 0; static int oldicc = 1, currenticc = 1; static int oldaa = 8, currentaa = 8; static int oldseparations = 0, currentseparations = 0; static fz_location oldpage = {0,0}, currentpage = {0,0}; static float oldzoom = DEFRES, currentzoom = DEFRES; static float oldrotate = 0, currentrotate = 0; static int isfullscreen = 0; static int showoutline = 0; static int showlinks = 0; static int showsearch = 0; static int showinfo = 0; int showannotate = 0; int showform = 0; static const char *tooltip = NULL; struct mark { fz_location loc; fz_point scroll; }; static int history_count = 0; static struct mark history[256]; static int future_count = 0; static struct mark future[256]; static struct mark marks[10]; static char *get_history_filename(void) { static char history_path[PATH_MAX]; static int once = 0; if (!once) { char *home = getenv("HOME"); if (!home) home = getenv("USERPROFILE"); if (!home) home = "/tmp"; fz_snprintf(history_path, sizeof history_path, "%s/.mupdf.history", home); fz_cleanname(history_path); once = 1; } return history_path; } static void read_history_file_as_json(js_State *J) { fz_buffer *buf = NULL; const char *json = "{}"; fz_var(buf); if (fz_file_exists(ctx, get_history_filename())) { fz_try(ctx) { buf = fz_read_file(ctx, get_history_filename()); json = fz_string_from_buffer(ctx, buf); } fz_catch(ctx) ; } js_getglobal(J, "JSON"); js_getproperty(J, -1, "parse"); js_pushnull(J); js_pushstring(J, json); if (js_pcall(J, 1)) { fz_warn(ctx, "Can't parse history file: %s", js_trystring(J, -1, "error")); js_pop(J, 1); js_newobject(J); } else { js_rot2pop1(J); } fz_drop_buffer(ctx, buf); } static fz_location try_location(js_State *J) { fz_location loc; if (js_isnumber(J, -1)) loc = fz_make_location(0, js_tryinteger(J, -1, 1) - 1); else { js_getindex(J, -1, 0); loc.chapter = js_tryinteger(J, -1, 1) - 1; js_pop(J, 1); js_getindex(J, -1, 1); loc.page = js_tryinteger(J, -1, 1) - 1; js_pop(J, 1); } return loc; } static void push_location(js_State *J, fz_location loc) { if (loc.chapter == 0) js_pushnumber(J, loc.page+1); else { js_newarray(J); js_pushnumber(J, loc.chapter+1); js_setindex(J, -2, 0); js_pushnumber(J, loc.page+1); js_setindex(J, -2, 1); } } static void load_history(void) { js_State *J; char absname[PATH_MAX]; int i, n; if (!realpath(filename, absname)) return; J = js_newstate(NULL, NULL, 0); read_history_file_as_json(J); if (js_hasproperty(J, -1, absname)) { if (js_hasproperty(J, -1, "current")) { currentpage = try_location(J); js_pop(J, 1); } if (js_hasproperty(J, -1, "history")) { if (js_isarray(J, -1)) { history_count = fz_clampi(js_getlength(J, -1), 0, nelem(history)); for (i = 0; i < history_count; ++i) { js_getindex(J, -1, i); history[i].loc = try_location(J); js_pop(J, 1); } } js_pop(J, 1); } if (js_hasproperty(J, -1, "future")) { if (js_isarray(J, -1)) { future_count = fz_clampi(js_getlength(J, -1), 0, nelem(future)); for (i = 0; i < future_count; ++i) { js_getindex(J, -1, i); future[i].loc = try_location(J); js_pop(J, 1); } } js_pop(J, 1); } if (js_hasproperty(J, -1, "marks")) { if (js_isarray(J, -1)) { n = fz_clampi(js_getlength(J, -1), 0, nelem(marks)); for (i = 0; i < n; ++i) { js_getindex(J, -1, i); marks[i].loc = try_location(J); js_pop(J, 1); } } js_pop(J, 1); } } js_freestate(J); } static void save_history(void) { js_State *J; char absname[PATH_MAX]; fz_output *out = NULL; const char *json; int i; fz_var(out); if (!doc) return; if (!realpath(filename, absname)) return; J = js_newstate(NULL, NULL, 0); read_history_file_as_json(J); js_newobject(J); { push_location(J, currentpage); js_setproperty(J, -2, "current"); js_newarray(J); for (i = 0; i < history_count; ++i) { push_location(J, history[i].loc); js_setindex(J, -2, i); } js_setproperty(J, -2, "history"); js_newarray(J); for (i = 0; i < future_count; ++i) { push_location(J, future[i].loc); js_setindex(J, -2, i); } js_setproperty(J, -2, "future"); js_newarray(J); for (i = 0; i < (int)nelem(marks); ++i) { push_location(J, marks[i].loc); js_setindex(J, -2, i); } js_setproperty(J, -2, "marks"); } js_setproperty(J, -2, absname); js_getglobal(J, "JSON"); js_getproperty(J, -1, "stringify"); js_pushnull(J); js_copy(J, -4); js_pushnull(J); js_pushnumber(J, 0); js_call(J, 3); js_rot2pop1(J); json = js_tostring(J, -1); fz_try(ctx) { out = fz_new_output_with_path(ctx, get_history_filename(), 0); fz_write_string(ctx, out, json); fz_write_byte(ctx, out, '\n'); fz_close_output(ctx, out); } fz_always(ctx) fz_drop_output(ctx, out); fz_catch(ctx) fz_warn(ctx, "Can't write history file."); js_freestate(J); } static int convert_to_accel_path(char outname[], char *absname, size_t len) { char *tmpdir; char *s; tmpdir = getenv("TEMP"); if (!tmpdir) tmpdir = getenv("TMP"); if (!tmpdir) tmpdir = "/var/tmp"; if (!fz_is_directory(ctx, tmpdir)) tmpdir = "/tmp"; if (absname[0] == '/' || absname[0] == '\\') ++absname; s = absname; while (*s) { if (*s == '/' || *s == '\\' || *s == ':') *s = '%'; ++s; } if (fz_snprintf(outname, len, "%s/%s.accel", tmpdir, absname) >= len) return 0; return 1; } static int get_accelerator_filename(char outname[], size_t len) { char absname[PATH_MAX]; if (!realpath(filename, absname)) return 0; if (!convert_to_accel_path(outname, absname, len)) return 0; return 1; } static void save_accelerator(void) { char absname[PATH_MAX]; if (!doc) return; if (!fz_document_supports_accelerator(ctx, doc)) return; if (!get_accelerator_filename(absname, sizeof(absname))) return; fz_save_accelerator(ctx, doc, absname); } static int search_active = 0; static struct input search_input = { { 0 }, 0 }; static char *search_needle = 0; static int search_dir = 1; static fz_location search_page = {-1, -1}; static fz_location search_hit_page = {-1, -1}; static int search_hit_count = 0; static fz_quad search_hit_quads[5000]; static char *help_dialog_text = "The middle mouse button (scroll wheel button) pans the document view. " "The right mouse button selects a region and copies the marked text to the clipboard." "\n" "\n" "F1 - show this message\n" "i - show document information\n" "o - show document outline\n" "a - show annotation editor\n" "L - highlight links\n" "F - highlight form fields\n" "r - reload file\n" "S - save file (only for PDF)\n" "q - quit\n" "\n" "< - decrease E-book font size\n" "> - increase E-book font size\n" "A - toggle anti-aliasing\n" "I - toggle inverted color mode\n" "C - toggle tinted color mode\n" "E - toggle ICC color management\n" "e - toggle spot color emulation\n" "\n" "f - fullscreen window\n" "w - shrink wrap window\n" "W - fit to width\n" "H - fit to height\n" "Z - fit to page\n" "z - reset zoom\n" "[number] z - set zoom resolution in DPI\n" "plus - zoom in\n" "minus - zoom out\n" "[ - rotate counter-clockwise\n" "] - rotate clockwise\n" "arrow keys - scroll in small increments\n" "h, j, k, l - scroll in small increments\n" "\n" "b - smart move backward\n" "space - smart move forward\n" "comma or page up - go backward\n" "period or page down - go forward\n" "g - go to first page\n" "G - go to last page\n" "[number] g - go to page number\n" "\n" "m - save current location in history\n" "t - go backward in history\n" "T - go forward in history\n" "[number] m - save current location in numbered bookmark\n" "[number] t - go to numbered bookmark\n" "\n" "/ - search for text forward\n" "? - search for text backward\n" "n - repeat search\n" "N - repeat search in reverse direction" ; static void help_dialog(void) { static int scroll; ui_dialog_begin(500, 1000); ui_layout(T, X, W, 2, 2); ui_label("MuPDF %s", FZ_VERSION); ui_spacer(); ui_layout(B, NONE, S, 2, 2); if (ui_button("Okay") || ui.key == KEY_ENTER || ui.key == KEY_ESCAPE) ui.dialog = NULL; ui_spacer(); ui_layout(ALL, BOTH, CENTER, 2, 2); ui_label_with_scrollbar(help_dialog_text, 0, 0, &scroll); ui_dialog_end(); } static char error_message[256]; static void error_dialog(void) { ui_dialog_begin(500, (ui.gridsize+4)*4); ui_layout(T, NONE, NW, 2, 2); ui_label("%C %s", 0x1f4a3, error_message); /* BOMB */ ui_layout(B, NONE, S, 2, 2); if (ui_button("Quit") || ui.key == KEY_ENTER || ui.key == KEY_ESCAPE || ui.key == 'q') glutLeaveMainLoop(); ui_dialog_end(); } void ui_show_error_dialog(const char *fmt, ...) { va_list ap; va_start(ap, fmt); fz_vsnprintf(error_message, sizeof error_message, fmt, ap); va_end(ap); ui.dialog = error_dialog; } static char warning_message[256]; static void warning_dialog(void) { ui_dialog_begin(500, (ui.gridsize+4)*4); ui_layout(T, NONE, NW, 2, 2); ui_label("%C %s", 0x26a0, warning_message); /* WARNING SIGN */ ui_layout(B, NONE, S, 2, 2); if (ui_button("Okay") || ui.key == KEY_ENTER || ui.key == KEY_ESCAPE) ui.dialog = NULL; ui_dialog_end(); } void ui_show_warning_dialog(const char *fmt, ...) { va_list ap; va_start(ap, fmt); fz_vsnprintf(warning_message, sizeof warning_message, fmt, ap); va_end(ap); ui.dialog = warning_dialog; } void update_title(void) { char buf[256]; char *title = "MuPDF/GL"; char *extra = ""; size_t n; int nc = fz_count_chapters(ctx, doc); title = strrchr(filename, '/'); if (!title) title = strrchr(filename, '\\'); if (title) ++title; else title = filename; if (pdf && pdf->dirty) extra = "*"; n = strlen(title); if (n > 50) { if (nc == 1) sprintf(buf, "...%s%s - %d/%d", title + n - 50, extra, currentpage.page + 1, fz_count_pages(ctx, doc)); else sprintf(buf, "...%s%s - %d/%d - %d/%d", title + n - 50, extra, currentpage.chapter + 1, nc, currentpage.page + 1, fz_count_chapter_pages(ctx, doc, currentpage.chapter)); } else { if (nc == 1) sprintf(buf, "%s%s - %d/%d", title, extra, currentpage.page + 1, fz_count_pages(ctx, doc)); else sprintf(buf, "%s%s - %d/%d - %d/%d", title, extra, currentpage.chapter + 1, nc, currentpage.page + 1, fz_count_chapter_pages(ctx, doc, currentpage.chapter)); } glutSetWindowTitle(buf); glutSetIconTitle(buf); } void transform_page(void) { draw_page_ctm = fz_transform_page(page_bounds, currentzoom, currentrotate); draw_page_bounds = fz_transform_rect(page_bounds, draw_page_ctm); } void load_page(void) { fz_irect area; /* clear all editor selections */ if (selected_annot && pdf_annot_type(ctx, selected_annot) == PDF_ANNOT_WIDGET) pdf_annot_event_blur(ctx, selected_annot); selected_annot = NULL; fz_drop_stext_page(ctx, page_text); page_text = NULL; fz_drop_separations(ctx, seps); seps = NULL; fz_drop_link(ctx, links); links = NULL; fz_drop_page(ctx, fzpage); fzpage = NULL; fzpage = fz_load_chapter_page(ctx, doc, currentpage.chapter, currentpage.page); if (pdf) page = (pdf_page*)fzpage; links = fz_load_links(ctx, fzpage); page_text = fz_new_stext_page_from_page(ctx, fzpage, NULL); if (currenticc) fz_enable_icc(ctx); else fz_disable_icc(ctx); if (currentseparations) { seps = fz_page_separations(ctx, &page->super); if (seps) { int i, n = fz_count_separations(ctx, seps); for (i = 0; i < n; i++) fz_set_separation_behavior(ctx, seps, i, FZ_SEPARATION_COMPOSITE); } else if (fz_page_uses_overprint(ctx, &page->super)) seps = fz_new_separations(ctx, 0); else if (fz_document_output_intent(ctx, doc)) seps = fz_new_separations(ctx, 0); } /* compute bounds here for initial window size */ page_bounds = fz_bound_page(ctx, fzpage); transform_page(); area = fz_irect_from_rect(draw_page_bounds); page_tex.w = area.x1 - area.x0; page_tex.h = area.y1 - area.y0; } void render_page(void) { fz_pixmap *pix; transform_page(); fz_set_aa_level(ctx, currentaa); pix = fz_new_pixmap_from_page_with_separations(ctx, fzpage, draw_page_ctm, fz_device_rgb(ctx), seps, 0); if (currentinvert) { fz_invert_pixmap_luminance(ctx, pix); fz_gamma_pixmap(ctx, pix, 1 / 1.4f); } if (currenttint) { fz_tint_pixmap(ctx, pix, tint_black, tint_white); } ui_texture_from_pixmap(&page_tex, pix); fz_drop_pixmap(ctx, pix); } void render_page_if_changed(void) { if (oldpage.chapter != currentpage.chapter || oldpage.page != currentpage.page || oldzoom != currentzoom || oldrotate != currentrotate || oldinvert != currentinvert || oldtint != currenttint || oldicc != currenticc || oldseparations != currentseparations || oldaa != currentaa) { render_page(); oldpage = currentpage; oldzoom = currentzoom; oldrotate = currentrotate; oldinvert = currentinvert; oldtint = currenttint; oldicc = currenticc; oldseparations = currentseparations; oldaa = currentaa; } } static struct mark save_mark() { struct mark mark; mark.loc = currentpage; mark.scroll = fz_transform_point_xy(scroll_x, scroll_y, view_page_inv_ctm); return mark; } static void restore_mark(struct mark mark) { currentpage = mark.loc; mark.scroll = fz_transform_point(mark.scroll, draw_page_ctm); scroll_x = mark.scroll.x; scroll_y = mark.scroll.y; } static int eqloc(fz_location a, fz_location b) { return a.chapter == b.chapter && a.page == b.page; } static int is_first_page(fz_location loc) { return (loc.chapter == 0 && loc.page == 0); } static int is_last_page(fz_location loc) { fz_location last = fz_last_page(ctx, doc); return (loc.chapter == last.chapter && loc.page == last.page); } static void push_history(void) { if (history_count > 0 && eqloc(history[history_count-1].loc, currentpage)) return; if (history_count + 1 >= (int)nelem(history)) { memmove(history, history + 1, sizeof *history * (nelem(history) - 1)); history[history_count] = save_mark(); } else { history[history_count++] = save_mark(); } } static void push_future(void) { if (future_count + 1 >= (int)nelem(future)) { memmove(future, future + 1, sizeof *future * (nelem(future) - 1)); future[future_count] = save_mark(); } else { future[future_count++] = save_mark(); } } static void clear_future(void) { future_count = 0; } static void jump_to_location(fz_location loc) { clear_future(); push_history(); currentpage = fz_clamp_location(ctx, doc, loc); push_history(); } static void jump_to_location_xy(fz_location loc, float x, float y) { fz_point p = fz_transform_point_xy(x, y, draw_page_ctm); clear_future(); push_history(); currentpage = fz_clamp_location(ctx, doc, loc); scroll_x = p.x; scroll_y = p.y; push_history(); } static void jump_to_page(int newpage) { clear_future(); push_history(); currentpage = fz_location_from_page_number(ctx, doc, newpage); currentpage = fz_clamp_location(ctx, doc, currentpage); push_history(); } static void jump_to_page_xy(int newpage, float x, float y) { fz_point p = fz_transform_point_xy(x, y, draw_page_ctm); clear_future(); push_history(); currentpage = fz_location_from_page_number(ctx, doc, newpage); currentpage = fz_clamp_location(ctx, doc, currentpage); scroll_x = p.x; scroll_y = p.y; push_history(); } static void pop_history(void) { fz_location here = currentpage; push_future(); while (history_count > 0 && eqloc(currentpage, here)) restore_mark(history[--history_count]); } static void pop_future(void) { fz_location here = currentpage; push_history(); while (future_count > 0 && eqloc(currentpage, here)) restore_mark(future[--future_count]); push_history(); } static void relayout(void) { if (layout_em < 6) layout_em = 6; if (layout_em > 36) layout_em = 36; if (fz_is_document_reflowable(ctx, doc)) { fz_bookmark mark = fz_make_bookmark(ctx, doc, currentpage); fz_layout_document(ctx, doc, layout_w, layout_h, layout_em); currentpage = fz_lookup_bookmark(ctx, doc, mark); history_count = 0; future_count = 0; load_page(); render_page(); update_title(); } } static int count_outline(fz_outline *node, int end) { int is_selected, n, p; int count = 0; while (node) { p = node->page; count += 1; n = end; if (node->next && node->next->page >= 0) n = node->next->page; is_selected = 0; if (fz_count_chapters(ctx, doc) == 1) is_selected = (p>=0) && (currentpage.page == p || (currentpage.page > p && currentpage.page < n)); if (node->down && (node->is_open || is_selected)) count += count_outline(node->down, end); node = node->next; } return count; } static void do_outline_imp(struct list *list, int end, fz_outline *node, int depth) { int is_selected, was_open, n; while (node) { int p = node->page; n = end; if (node->next && node->next->page >= 0) n = node->next->page; was_open = node->is_open; is_selected = 0; if (fz_count_chapters(ctx, doc) == 1) is_selected = (p>=0) && (currentpage.page == p || (currentpage.page > p && currentpage.page < n)); if (ui_tree_item(list, node, node->title, is_selected, depth, !!node->down, &node->is_open)) { if (p < 0) { currentpage = fz_resolve_link(ctx, doc, node->uri, &node->x, &node->y); jump_to_location_xy(currentpage, node->x, node->y); } else { jump_to_page_xy(p, node->x, node->y); } } if (node->down && (was_open || is_selected)) do_outline_imp(list, n, node->down, depth + 1); node = node->next; } } static void do_outline(fz_outline *node) { static struct list list; ui_layout(L, BOTH, NW, 0, 0); ui_tree_begin(&list, count_outline(node, 65535), outline_w, 0, 1); do_outline_imp(&list, 65535, node, 0); ui_tree_end(&list); ui_splitter(&outline_w, 150, 500, R); } static void do_links(fz_link *link) { fz_rect bounds; fz_irect area; float link_x, link_y; glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); while (link) { bounds = link->rect; bounds = fz_transform_rect(link->rect, view_page_ctm); area = fz_irect_from_rect(bounds); if (ui_mouse_inside(area)) { tooltip = link->uri; ui.hot = link; if (!ui.active && ui.down) ui.active = link; } if (ui.hot == link || showlinks) { if (ui.active == link && ui.hot == link) glColor4f(0, 0, 1, 0.4f); else if (ui.hot == link) glColor4f(0, 0, 1, 0.2f); else glColor4f(0, 0, 1, 0.1f); glRectf(area.x0, area.y0, area.x1, area.y1); } if (ui.active == link && !ui.down) { if (ui.hot == link) { if (fz_is_external_link(ctx, link->uri)) open_browser(link->uri); else { fz_location loc = fz_resolve_link(ctx, doc, link->uri, &link_x, &link_y); jump_to_location_xy(loc, link_x, link_y); } } } link = link->next; } glDisable(GL_BLEND); } static void do_page_selection(void) { static fz_point pt = { 0, 0 }; fz_quad hits[1000]; int i, n; if (ui_mouse_inside(view_page_area)) { ui.hot = &pt; if (!ui.active && ui.right) { ui.active = &pt; pt.x = ui.x; pt.y = ui.y; } } if (ui.active == &pt) { fz_point page_a = { pt.x, pt.y }; fz_point page_b = { ui.x, ui.y }; page_a = fz_transform_point(page_a, view_page_inv_ctm); page_b = fz_transform_point(page_b, view_page_inv_ctm); if (ui.mod == GLUT_ACTIVE_CTRL) fz_snap_selection(ctx, page_text, &page_a, &page_b, FZ_SELECT_WORDS); else if (ui.mod == GLUT_ACTIVE_CTRL + GLUT_ACTIVE_SHIFT) fz_snap_selection(ctx, page_text, &page_a, &page_b, FZ_SELECT_LINES); n = fz_highlight_selection(ctx, page_text, page_a, page_b, hits, nelem(hits)); glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO); /* invert destination color */ glEnable(GL_BLEND); glColor4f(1, 1, 1, 1); glBegin(GL_QUADS); for (i = 0; i < n; ++i) { fz_quad thit = fz_transform_quad(hits[i], view_page_ctm); glVertex2f(thit.ul.x, thit.ul.y); glVertex2f(thit.ur.x, thit.ur.y); glVertex2f(thit.lr.x, thit.lr.y); glVertex2f(thit.ll.x, thit.ll.y); } glEnd(); glDisable(GL_BLEND); if (!ui.right) { char *s; #ifdef _WIN32 s = fz_copy_selection(ctx, page_text, page_a, page_b, 1); #else s = fz_copy_selection(ctx, page_text, page_a, page_b, 0); #endif ui_set_clipboard(s); fz_free(ctx, s); } } } static void do_search_hits(void) { int i; glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glColor4f(1, 0, 0, 0.4f); glBegin(GL_QUADS); for (i = 0; i < search_hit_count; ++i) { fz_quad thit = fz_transform_quad(search_hit_quads[i], view_page_ctm); glVertex2f(thit.ul.x, thit.ul.y); glVertex2f(thit.ur.x, thit.ur.y); glVertex2f(thit.lr.x, thit.lr.y); glVertex2f(thit.ll.x, thit.ll.y); } glEnd(); glDisable(GL_BLEND); } static void toggle_fullscreen(void) { static int win_x = 0, win_y = 0; static int win_w = 100, win_h = 100; if (!isfullscreen) { win_w = glutGet(GLUT_WINDOW_WIDTH); win_h = glutGet(GLUT_WINDOW_HEIGHT); win_x = glutGet(GLUT_WINDOW_X); win_y = glutGet(GLUT_WINDOW_Y); glutFullScreen(); isfullscreen = 1; } else { glutPositionWindow(win_x, win_y); glutReshapeWindow(win_w, win_h); isfullscreen = 0; } } static void shrinkwrap(void) { int w = page_tex.w + (showoutline ? outline_w + 4 : 0) + (showannotate ? annotate_w : 0); int h = page_tex.h; if (screen_w > 0 && w > screen_w) w = screen_w; if (screen_h > 0 && h > screen_h) h = screen_h; if (isfullscreen) toggle_fullscreen(); glutReshapeWindow(w, h); } static struct input input_password; static void password_dialog(void) { int is; ui_dialog_begin(400, (ui.gridsize+4)*3); { ui_layout(T, X, NW, 2, 2); ui_label("Password:"); is = ui_input(&input_password, 200, 1); ui_layout(B, X, NW, 2, 2); ui_panel_begin(0, ui.gridsize, 0, 0, 0); { ui_layout(R, NONE, S, 0, 0); if (ui_button("Cancel") || (!ui.focus && ui.key == KEY_ESCAPE)) glutLeaveMainLoop(); ui_spacer(); if (ui_button("Okay") || is == UI_INPUT_ACCEPT) { password = input_password.text; ui.dialog = NULL; reload(); shrinkwrap(); } } ui_panel_end(); } ui_dialog_end(); } static void load_document(void) { char accelpath[PATH_MAX]; char *accel = NULL; time_t atime; time_t dtime; fz_drop_outline(ctx, outline); fz_drop_document(ctx, doc); /* If there was an accelerator to load, what would it be called? */ if (get_accelerator_filename(accelpath, sizeof(accelpath))) { /* Check whether that file exists, and isn't older than * the document. */ atime = stat_mtime(accelpath); dtime = stat_mtime(filename); if (atime == 0) { /* No accelerator */ } else if (atime > dtime) accel = accelpath; else { /* Accelerator data is out of date */ unlink(accelpath); accel = NULL; /* In case we have jumped up from below */ } } doc = fz_open_accelerated_document(ctx, filename, accel); if (fz_needs_password(ctx, doc)) { if (!fz_authenticate_password(ctx, doc, password)) { fz_drop_document(ctx, doc); doc = NULL; ui_input_init(&input_password, ""); ui.focus = &input_password; ui.dialog = password_dialog; return; } } fz_layout_document(ctx, doc, layout_w, layout_h, layout_em); fz_try(ctx) outline = fz_load_outline(ctx, doc); fz_catch(ctx) outline = NULL; load_history(); pdf = pdf_specifics(ctx, doc); if (pdf) { if (enable_js) pdf_enable_js(ctx, pdf); if (anchor) jump_to_page(pdf_lookup_anchor(ctx, pdf, anchor, NULL, NULL)); } else { if (anchor) jump_to_page(fz_atoi(anchor) - 1); } anchor = NULL; currentpage = fz_clamp_location(ctx, doc, currentpage); } void reload(void) { save_history(); save_accelerator(); load_document(); if (doc) { load_page(); render_page(); update_title(); } } static void toggle_outline(void) { if (outline) { showoutline = !showoutline; if (canvas_w == page_tex.w && canvas_h == page_tex.h) shrinkwrap(); } } void toggle_annotate(void) { if (pdf) { showannotate = !showannotate; if (canvas_w == page_tex.w && canvas_h == page_tex.h) shrinkwrap(); } } static void set_zoom(int z, int cx, int cy) { z = fz_clamp(z, MINRES, MAXRES); scroll_x = (scroll_x + cx - canvas_x) * z / currentzoom - cx + canvas_x; scroll_y = (scroll_y + cy - canvas_y) * z / currentzoom - cy + canvas_y; currentzoom = z; } static void auto_zoom_w(void) { currentzoom = fz_clamp(currentzoom * canvas_w / page_tex.w, MINRES, MAXRES); } static void auto_zoom_h(void) { currentzoom = fz_clamp(currentzoom * canvas_h / page_tex.h, MINRES, MAXRES); } static void auto_zoom(void) { float page_a = (float) page_tex.w / page_tex.h; float screen_a = (float) canvas_w / canvas_h; if (page_a > screen_a) auto_zoom_w(); else auto_zoom_h(); } static void smart_move_backward(void) { int slop_x = page_tex.w / 20; int slop_y = page_tex.h / 20; if (scroll_y <= slop_y) { if (scroll_x <= slop_x) { fz_location prev = fz_previous_page(ctx, doc, currentpage); if (!eqloc(currentpage, prev)) { scroll_x = (page_tex.w <= canvas_w) ? 0 : page_tex.w - canvas_w; scroll_y = (page_tex.h <= canvas_h) ? 0 : page_tex.h - canvas_h; currentpage = prev; } } else { scroll_y = page_tex.h; scroll_x -= canvas_w * 9 / 10; } } else { scroll_y -= canvas_h * 9 / 10; } } static void smart_move_forward(void) { int slop_x = page_tex.w / 20; int slop_y = page_tex.h / 20; if (scroll_y + canvas_h >= page_tex.h - slop_y) { if (scroll_x + canvas_w >= page_tex.w - slop_x) { fz_location next = fz_next_page(ctx, doc, currentpage); if (!eqloc(currentpage, next)) { scroll_x = 0; scroll_y = 0; currentpage = next; } } else { scroll_y = 0; scroll_x += canvas_w * 9 / 10; } } else { scroll_y += canvas_h * 9 / 10; } } static void clear_search(void) { showsearch = 0; search_page = currentpage; search_hit_page = fz_make_location(-1, -1); search_hit_count = 0; } static void do_app(void) { if (ui.key == KEY_F4 && ui.mod == GLUT_ACTIVE_ALT) glutLeaveMainLoop(); if (ui.down || ui.middle || ui.right || ui.key) showinfo = 0; if (!ui.focus && ui.key && ui.plain) { switch (ui.key) { case KEY_ESCAPE: clear_search(); selected_annot = NULL; break; case KEY_F1: ui.dialog = help_dialog; break; case 'a': toggle_annotate(); break; case 'o': toggle_outline(); break; case 'L': showlinks = !showlinks; break; case 'F': showform = !showform; break; case 'i': showinfo = !showinfo; break; case 'r': reload(); break; case 'q': glutLeaveMainLoop(); break; case 'S': do_save_pdf_file(); break; case '>': layout_em = number > 0 ? number : layout_em + 1; relayout(); break; case '<': layout_em = number > 0 ? number : layout_em - 1; relayout(); break; case 'C': currenttint = !currenttint; break; case 'I': currentinvert = !currentinvert; break; case 'e': currentseparations = !currentseparations; break; case 'E': currenticc = !currenticc; break; case 'f': toggle_fullscreen(); break; case 'w': shrinkwrap(); break; case 'W': auto_zoom_w(); break; case 'H': auto_zoom_h(); break; case 'Z': auto_zoom(); break; case 'z': set_zoom(number > 0 ? number : DEFRES, canvas_w/2, canvas_h/2); break; case '+': set_zoom(zoom_in(currentzoom), ui.x, ui.y); break; case '-': set_zoom(zoom_out(currentzoom), ui.x, ui.y); break; case '[': currentrotate -= 90; break; case ']': currentrotate += 90; break; case 'k': case KEY_UP: scroll_y -= 10; break; case 'j': case KEY_DOWN: scroll_y += 10; break; case 'h': case KEY_LEFT: scroll_x -= 10; break; case 'l': case KEY_RIGHT: scroll_x += 10; break; case 'b': number = fz_maxi(number, 1); while (number--) smart_move_backward(); break; case ' ': number = fz_maxi(number, 1); while (number--) smart_move_forward(); break; case 'g': jump_to_page(number - 1); break; case 'G': jump_to_location(fz_last_page(ctx, doc)); break; case ',': case KEY_PAGE_UP: number = fz_maxi(number, 1); while (number--) currentpage = fz_previous_page(ctx, doc, currentpage); break; case '.': case KEY_PAGE_DOWN: number = fz_maxi(number, 1); while (number--) currentpage = fz_next_page(ctx, doc, currentpage); break; case 'A': if (number == 0) currentaa = (currentaa == 8 ? 0 : 8); else currentaa = number; break; case 'm': if (number == 0) push_history(); else if (number > 0 && number < (int)nelem(marks)) marks[number] = save_mark(); break; case 't': if (number == 0) { if (history_count > 0) pop_history(); } else if (number > 0 && number < (int)nelem(marks)) { struct mark mark = marks[number]; restore_mark(mark); jump_to_location(mark.loc); } break; case 'T': if (number == 0) { if (future_count > 0) pop_future(); } break; case '/': clear_search(); search_dir = 1; showsearch = 1; ui.focus = &search_input; search_input.p = search_input.text; search_input.q = search_input.end; break; case '?': clear_search(); search_dir = -1; showsearch = 1; ui.focus = &search_input; search_input.p = search_input.text; search_input.q = search_input.end; break; case 'N': search_dir = -1; search_active = !!search_needle; if (eqloc(search_hit_page, currentpage)) { search_page = fz_previous_page(ctx, doc, currentpage); if (is_first_page(search_page)) search_active = 0; } else { search_page = currentpage; } search_hit_page = fz_make_location(-1, -1); break; case 'n': search_dir = 1; search_active = !!search_needle; if (eqloc(search_hit_page, currentpage)) { search_page = fz_next_page(ctx, doc, currentpage); if (is_last_page(search_page)) search_active = 0; } else { search_page = currentpage; } search_hit_page = fz_make_location(-1, -1); break; } if (ui.key >= '0' && ui.key <= '9') number = number * 10 + ui.key - '0'; else number = 0; currentpage = fz_clamp_location(ctx, doc, currentpage); while (currentrotate < 0) currentrotate += 360; while (currentrotate >= 360) currentrotate -= 360; if (!eqloc(search_hit_page, currentpage)) search_hit_page = fz_make_location(-1, -1); /* clear highlights when navigating */ ui.key = 0; /* we ate the key event, so zap it */ } } static void do_info(void) { char buf[100]; ui_dialog_begin(500, 14 * ui.lineheight); ui_layout(T, X, W, 0, 0); if (fz_lookup_metadata(ctx, doc, FZ_META_INFO_TITLE, buf, sizeof buf) > 0) ui_label("Title: %s", buf); if (fz_lookup_metadata(ctx, doc, FZ_META_INFO_AUTHOR, buf, sizeof buf) > 0) ui_label("Author: %s", buf); if (fz_lookup_metadata(ctx, doc, FZ_META_FORMAT, buf, sizeof buf) > 0) ui_label("Format: %s", buf); if (fz_lookup_metadata(ctx, doc, FZ_META_ENCRYPTION, buf, sizeof buf) > 0) ui_label("Encryption: %s", buf); if (pdf_specifics(ctx, doc)) { if (fz_lookup_metadata(ctx, doc, "info:Creator", buf, sizeof buf) > 0) ui_label("PDF Creator: %s", buf); if (fz_lookup_metadata(ctx, doc, "info:Producer", buf, sizeof buf) > 0) ui_label("PDF Producer: %s", buf); buf[0] = 0; if (fz_has_permission(ctx, doc, FZ_PERMISSION_PRINT)) fz_strlcat(buf, "print, ", sizeof buf); if (fz_has_permission(ctx, doc, FZ_PERMISSION_COPY)) fz_strlcat(buf, "copy, ", sizeof buf); if (fz_has_permission(ctx, doc, FZ_PERMISSION_EDIT)) fz_strlcat(buf, "edit, ", sizeof buf); if (fz_has_permission(ctx, doc, FZ_PERMISSION_ANNOTATE)) fz_strlcat(buf, "annotate, ", sizeof buf); if (strlen(buf) > 2) buf[strlen(buf)-2] = 0; else fz_strlcat(buf, "none", sizeof buf); ui_label("Permissions: %s", buf); } ui_label("Page: %d / %d", fz_page_number_from_location(ctx, doc, currentpage)+1, fz_count_pages(ctx, doc)); { int w = (int)(page_bounds.x1 - page_bounds.x0 + 0.5f); int h = (int)(page_bounds.y1 - page_bounds.y0 + 0.5f); const char *size = paper_size_name(w, h); if (!size) size = paper_size_name(h, w); if (size) ui_label("Size: %d x %d (%s)", w, h, size); else ui_label("Size: %d x %d", w, h); } ui_label("ICC rendering: %s.", currenticc ? "on" : "off"); ui_label("Spot rendering: %s.", currentseparations ? "on" : "off"); ui_dialog_end(); } static void do_canvas(void) { static int saved_scroll_x = 0; static int saved_scroll_y = 0; static int saved_ui_x = 0; static int saved_ui_y = 0; fz_irect area; int page_x, page_y; tooltip = NULL; ui_layout(ALL, BOTH, NW, 0, 0); ui_pack_push(area = ui_pack(0, 0)); glScissor(area.x0, ui.window_h-area.y1, area.x1-area.x0, area.y1-area.y0); glEnable(GL_SCISSOR_TEST); canvas_x = area.x0; canvas_y = area.y0; canvas_w = area.x1 - area.x0; canvas_h = area.y1 - area.y0; if (ui_mouse_inside(area)) { ui.hot = doc; if (!ui.active && ui.middle) { ui.active = doc; saved_scroll_x = scroll_x; saved_scroll_y = scroll_y; saved_ui_x = ui.x; saved_ui_y = ui.y; } } if (ui.hot == doc) { if (ui.mod == 0) { scroll_x -= ui.scroll_x * ui.lineheight * 3; scroll_y -= ui.scroll_y * ui.lineheight * 3; } else if (ui.mod == GLUT_ACTIVE_CTRL) { if (ui.scroll_y > 0) set_zoom(zoom_in(currentzoom), ui.x, ui.y); if (ui.scroll_y < 0) set_zoom(zoom_out(currentzoom), ui.x, ui.y); } } render_page_if_changed(); if (ui.active == doc) { scroll_x = saved_scroll_x + saved_ui_x - ui.x; scroll_y = saved_scroll_y + saved_ui_y - ui.y; } if (page_tex.w <= canvas_w) { scroll_x = 0; page_x = canvas_x + (canvas_w - page_tex.w) / 2; } else { scroll_x = fz_clamp(scroll_x, 0, page_tex.w - canvas_w); page_x = canvas_x - scroll_x; } if (page_tex.h <= canvas_h) { scroll_y = 0; page_y = canvas_y + (canvas_h - page_tex.h) / 2; } else { scroll_y = fz_clamp(scroll_y, 0, page_tex.h - canvas_h); page_y = canvas_y - scroll_y; } view_page_ctm = draw_page_ctm; view_page_ctm.e += page_x; view_page_ctm.f += page_y; view_page_inv_ctm = fz_invert_matrix(view_page_ctm); view_page_bounds = fz_transform_rect(page_bounds, view_page_ctm); view_page_area = fz_irect_from_rect(view_page_bounds); ui_draw_image(&page_tex, page_x, page_y); if (search_active) { ui_layout(T, X, NW, 0, 0); ui_panel_begin(0, ui.gridsize+8, 4, 4, 1); ui_layout(L, NONE, W, 2, 0); ui_label("Searching chapter %d page %d...", search_page.chapter, search_page.page); ui_panel_end(); } else { if (pdf) { do_annotate_canvas(area); do_widget_canvas(area); } do_links(links); do_page_selection(); if (eqloc(search_hit_page, currentpage) && search_hit_count > 0) do_search_hits(); } if (showsearch) { ui_layout(T, X, NW, 0, 0); ui_panel_begin(0, ui.gridsize+8, 4, 4, 1); ui_layout(L, NONE, W, 2, 0); ui_label("Search:"); ui_layout(ALL, X, E, 2, 0); if (ui_input(&search_input, 0, 1) == UI_INPUT_ACCEPT) { showsearch = 0; search_page = fz_make_location(-1, -1); if (search_needle) { fz_free(ctx, search_needle); search_needle = NULL; } if (search_input.end > search_input.text) { search_needle = fz_strdup(ctx, search_input.text); search_active = 1; search_page = currentpage; } } if (ui.focus != &search_input) showsearch = 0; ui_panel_end(); } if (tooltip) { ui_layout(B, X, N, 0, 0); ui_panel_begin(0, ui.gridsize, 4, 4, 1); ui_layout(L, NONE, W, 2, 0); ui_label("%s", tooltip); ui_panel_end(); } ui_pack_pop(); glDisable(GL_SCISSOR_TEST); } void do_main(void) { if (search_active) { int start_time = glutGet(GLUT_ELAPSED_TIME); if (ui.key == KEY_ESCAPE) search_active = 0; /* ignore events during search */ ui.key = ui.mod = ui.plain = 0; ui.down = ui.middle = ui.right = 0; while (search_active && glutGet(GLUT_ELAPSED_TIME) < start_time + 200) { search_hit_count = fz_search_chapter_page_number(ctx, doc, search_page.chapter, search_page.page, search_needle, search_hit_quads, nelem(search_hit_quads)); if (search_hit_count) { search_active = 0; search_hit_page = search_page; jump_to_location(search_hit_page); } else { if (search_dir > 0) { if (is_last_page(search_page)) search_active = 0; else search_page = fz_next_page(ctx, doc, search_page); } else { if (is_first_page(search_page)) search_active = 0; else search_page = fz_previous_page(ctx, doc, search_page); } } } /* keep searching later */ if (search_active) glutPostRedisplay(); } do_app(); if (showoutline) do_outline(outline); if (!eqloc(oldpage, currentpage) || oldseparations != currentseparations || oldicc != currenticc) { load_page(); update_title(); } if (showannotate) { ui_layout(R, BOTH, NW, 0, 0); ui_panel_begin(annotate_w, 0, 4, 4, 1); do_annotate_panel(); ui_panel_end(); } do_canvas(); if (showinfo) do_info(); } void run_main_loop(void) { if (currentinvert) glClearColor(0, 0, 0, 1); else glClearColor(0.3f, 0.3f, 0.3f, 1); ui_begin(); fz_try(ctx) { if (ui.dialog) ui.dialog(); else do_main(); } fz_catch(ctx) ui_show_error_dialog("%s", fz_caught_message(ctx)); ui_end(); } static void usage(const char *argv0) { fprintf(stderr, "mupdf-gl version %s\n", FZ_VERSION); fprintf(stderr, "usage: %s [options] document [page]\n", argv0); fprintf(stderr, "\t-p -\tpassword\n"); fprintf(stderr, "\t-r -\tresolution\n"); fprintf(stderr, "\t-I\tinvert colors\n"); fprintf(stderr, "\t-W -\tpage width for EPUB layout\n"); fprintf(stderr, "\t-H -\tpage height for EPUB layout\n"); fprintf(stderr, "\t-S -\tfont size for EPUB layout\n"); fprintf(stderr, "\t-U -\tuser style sheet for EPUB layout\n"); fprintf(stderr, "\t-X\tdisable document styles for EPUB layout\n"); fprintf(stderr, "\t-J\tdisable javascript in PDF forms\n"); fprintf(stderr, "\t-A -\tset anti-aliasing level (0-8,9,10)\n"); fprintf(stderr, "\t-B -\tset black tint color (default: 303030)\n"); fprintf(stderr, "\t-C -\tset white tint color (default: FFFFF0)\n"); exit(1); } static int document_filter(const char *filename) { return !!fz_recognize_document(ctx, filename); } static void do_open_document_dialog(void) { if (ui_open_file(filename)) { ui.dialog = NULL; if (filename[0] == 0) glutLeaveMainLoop(); else load_document(); if (doc) { load_page(); render_page(); shrinkwrap(); update_title(); } } } static void cleanup(void) { save_history(); save_accelerator(); ui_finish(); #ifndef NDEBUG if (fz_atoi(getenv("FZ_DEBUG_STORE"))) fz_debug_store(ctx); #endif fz_drop_stext_page(ctx, page_text); fz_drop_separations(ctx, seps); fz_drop_link(ctx, links); fz_drop_page(ctx, fzpage); fz_drop_outline(ctx, outline); fz_drop_document(ctx, doc); fz_drop_context(ctx); } int reloadrequested = 0; #ifndef _WIN32 static void signal_handler(int signal) { if (signal == SIGHUP) reloadrequested = 1; } #endif #ifdef _MSC_VER int main_utf8(int argc, char **argv) #else int main(int argc, char **argv) #endif { int c; #ifndef _WIN32 signal(SIGHUP, signal_handler); #endif glutInit(&argc, argv); screen_w = glutGet(GLUT_SCREEN_WIDTH) - SCREEN_FURNITURE_W; screen_h = glutGet(GLUT_SCREEN_HEIGHT) - SCREEN_FURNITURE_H; while ((c = fz_getopt(argc, argv, "p:r:IW:H:S:U:XJA:B:C:")) != -1) { switch (c) { default: usage(argv[0]); break; case 'p': password = fz_optarg; break; case 'r': currentzoom = fz_atof(fz_optarg); break; case 'I': currentinvert = !currentinvert; break; case 'W': layout_w = fz_atof(fz_optarg); break; case 'H': layout_h = fz_atof(fz_optarg); break; case 'S': layout_em = fz_atof(fz_optarg); break; case 'U': layout_css = fz_optarg; break; case 'X': layout_use_doc_css = 0; break; case 'J': enable_js = !enable_js; break; case 'A': currentaa = fz_atoi(fz_optarg); break; case 'C': currenttint = 1; tint_white = strtol(fz_optarg, NULL, 16); break; case 'B': currenttint = 1; tint_black = strtol(fz_optarg, NULL, 16); break; } } ctx = fz_new_context(NULL, NULL, FZ_STORE_DEFAULT); fz_register_document_handlers(ctx); if (layout_css) { fz_buffer *buf = fz_read_file(ctx, layout_css); fz_set_user_css(ctx, fz_string_from_buffer(ctx, buf)); fz_drop_buffer(ctx, buf); } fz_set_use_document_css(ctx, layout_use_doc_css); if (fz_optind < argc) { fz_strlcpy(filename, argv[fz_optind++], sizeof filename); if (fz_optind < argc) anchor = argv[fz_optind++]; fz_try(ctx) { page_tex.w = 600; page_tex.h = 700; load_document(); if (doc) load_page(); } fz_always(ctx) { float sx = 1, sy = 1; if (screen_w > 0 && page_tex.w > screen_w) sx = (float)screen_w / page_tex.w; if (screen_h > 0 && page_tex.h > screen_h) sy = (float)screen_h / page_tex.h; if (sy < sx) sx = sy; if (sx < 1) { fz_irect area; currentzoom *= sx; oldzoom = currentzoom; /* compute bounds here for initial window size */ page_bounds = fz_bound_page(ctx, fzpage); transform_page(); area = fz_irect_from_rect(draw_page_bounds); page_tex.w = area.x1 - area.x0; page_tex.h = area.y1 - area.y0; } ui_init(page_tex.w, page_tex.h, "MuPDF: Loading..."); ui_input_init(&search_input, ""); } fz_catch(ctx) { ui_show_error_dialog("%s", fz_caught_message(ctx)); } fz_try(ctx) { if (doc) { render_page(); update_title(); } } fz_catch(ctx) { ui_show_error_dialog("%s", fz_caught_message(ctx)); } } else { #ifdef _WIN32 win_install(); #endif ui_init(640, 700, "MuPDF: Open document"); ui_input_init(&search_input, ""); ui_init_open_file(".", document_filter); ui.dialog = do_open_document_dialog; } annotate_w *= ui.lineheight; outline_w *= ui.lineheight; glutMainLoop(); cleanup(); return 0; } #ifdef _MSC_VER int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { int argc; LPWSTR *wargv = CommandLineToArgvW(GetCommandLineW(), &argc); char **argv = fz_argv_from_wargv(argc, wargv); int ret = main_utf8(argc, argv); fz_free_argv(argc, argv); return ret; } #endif