Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/platform/gl/gl-main.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 // Copyright (C) 2004-2025 Artifex Software, Inc. | |
| 2 // | |
| 3 // This file is part of MuPDF. | |
| 4 // | |
| 5 // MuPDF is free software: you can redistribute it and/or modify it under the | |
| 6 // terms of the GNU Affero General Public License as published by the Free | |
| 7 // Software Foundation, either version 3 of the License, or (at your option) | |
| 8 // any later version. | |
| 9 // | |
| 10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY | |
| 11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
| 12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more | |
| 13 // details. | |
| 14 // | |
| 15 // You should have received a copy of the GNU Affero General Public License | |
| 16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> | |
| 17 // | |
| 18 // Alternative licensing terms are available from the licensor. | |
| 19 // For commercial licensing, see <https://www.artifex.com/> or contact | |
| 20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, | |
| 21 // CA 94129, USA, for further information. | |
| 22 | |
| 23 #include "gl-app.h" | |
| 24 | |
| 25 #include <limits.h> | |
| 26 #include <string.h> | |
| 27 #include <stdlib.h> | |
| 28 #include <stdio.h> | |
| 29 #include <time.h> | |
| 30 #ifndef _WIN32 | |
| 31 #include <sys/types.h> | |
| 32 #include <sys/wait.h> | |
| 33 #include <signal.h> | |
| 34 #endif | |
| 35 | |
| 36 #include "mupdf/helpers/pkcs7-openssl.h" | |
| 37 | |
| 38 #if FZ_ENABLE_JS | |
| 39 #include "mujs.h" | |
| 40 #endif | |
| 41 | |
| 42 #ifndef _WIN32 | |
| 43 #include <sys/stat.h> /* for mkdir */ | |
| 44 #include <unistd.h> /* for getcwd */ | |
| 45 #include <spawn.h> /* for posix_spawn */ | |
| 46 extern char **environ; /* see environ (7) */ | |
| 47 #else | |
| 48 #include <direct.h> /* for getcwd */ | |
| 49 #endif | |
| 50 | |
| 51 #ifdef __APPLE__ | |
| 52 static void cleanup(void); | |
| 53 void glutLeaveMainLoop(void) | |
| 54 { | |
| 55 cleanup(); | |
| 56 exit(0); | |
| 57 } | |
| 58 #endif | |
| 59 | |
| 60 fz_context *ctx = NULL; | |
| 61 fz_colorspace *profile = NULL; | |
| 62 pdf_document *pdf = NULL; | |
| 63 pdf_page *page = NULL; | |
| 64 fz_stext_page *page_text = NULL; | |
| 65 fz_matrix draw_page_ctm, view_page_ctm, view_page_inv_ctm; | |
| 66 fz_rect page_bounds, draw_page_bounds, view_page_bounds; | |
| 67 fz_irect view_page_area; | |
| 68 char filename[PATH_MAX]; | |
| 69 | |
| 70 enum | |
| 71 { | |
| 72 /* Screen furniture: aggregate size of unusable space from title bars, task bars, window borders, etc */ | |
| 73 SCREEN_FURNITURE_W = 20, | |
| 74 SCREEN_FURNITURE_H = 40, | |
| 75 }; | |
| 76 | |
| 77 static void open_browser(const char *uri) | |
| 78 { | |
| 79 #ifndef _WIN32 | |
| 80 char *argv[3]; | |
| 81 #endif | |
| 82 char buf[PATH_MAX]; | |
| 83 | |
| 84 #ifndef _WIN32 | |
| 85 pid_t pid; | |
| 86 int err; | |
| 87 #endif | |
| 88 | |
| 89 /* Relative file: URI, make it absolute! */ | |
| 90 if (!strncmp(uri, "file:", 5) && uri[5] != '/') | |
| 91 { | |
| 92 char buf_base[PATH_MAX]; | |
| 93 char buf_cwd[PATH_MAX]; | |
| 94 fz_dirname(buf_base, filename, sizeof buf_base); | |
| 95 if (getcwd(buf_cwd, sizeof buf_cwd)) | |
| 96 { | |
| 97 fz_snprintf(buf, sizeof buf, "file://%s/%s/%s", buf_cwd, buf_base, uri+5); | |
| 98 fz_cleanname(buf+7); | |
| 99 uri = buf; | |
| 100 } | |
| 101 } | |
| 102 | |
| 103 if (strncmp(uri, "file://", 7) && strncmp(uri, "http://", 7) && strncmp(uri, "https://", 8) && strncmp(uri, "mailto:", 7)) | |
| 104 { | |
| 105 fz_warn(ctx, "refusing to open unknown link (%s)", uri); | |
| 106 return; | |
| 107 } | |
| 108 | |
| 109 #ifdef _WIN32 | |
| 110 ShellExecuteA(NULL, "open", uri, 0, 0, SW_SHOWNORMAL); | |
| 111 #else | |
| 112 const char *browser = getenv("BROWSER"); | |
| 113 if (!browser) | |
| 114 { | |
| 115 #ifdef __APPLE__ | |
| 116 browser = "open"; | |
| 117 #else | |
| 118 browser = "xdg-open"; | |
| 119 #endif | |
| 120 } | |
| 121 | |
| 122 argv[0] = (char*) browser; | |
| 123 argv[1] = (char*) uri; | |
| 124 argv[2] = NULL; | |
| 125 err = posix_spawnp(&pid, browser, NULL, NULL, argv, environ); | |
| 126 if (err) | |
| 127 fz_warn(ctx, "cannot spawn browser '%s': %s", browser, strerror(err)); | |
| 128 | |
| 129 #endif | |
| 130 } | |
| 131 | |
| 132 static const int zoom_list[] = { | |
| 133 6, 12, 24, 36, 48, 60, 72, 84, 96, 108, | |
| 134 120, 144, 168, 192, 228, 264, | |
| 135 300, 350, 400, 450, 500, 550, 600 | |
| 136 }; | |
| 137 | |
| 138 static int zoom_in(int oldres) | |
| 139 { | |
| 140 int i; | |
| 141 for (i = 0; i < (int)nelem(zoom_list) - 1; ++i) | |
| 142 if (zoom_list[i] <= oldres && zoom_list[i+1] > oldres) | |
| 143 return zoom_list[i+1]; | |
| 144 return zoom_list[i]; | |
| 145 } | |
| 146 | |
| 147 static int zoom_out(int oldres) | |
| 148 { | |
| 149 int i; | |
| 150 for (i = 0; i < (int)nelem(zoom_list) - 1; ++i) | |
| 151 if (zoom_list[i] < oldres && zoom_list[i+1] >= oldres) | |
| 152 return zoom_list[i]; | |
| 153 return zoom_list[0]; | |
| 154 } | |
| 155 | |
| 156 static const char *paper_size_name(int w, int h) | |
| 157 { | |
| 158 /* ISO A */ | |
| 159 if (w == 2384 && h == 3370) return "A0"; | |
| 160 if (w == 1684 && h == 2384) return "A1"; | |
| 161 if (w == 1191 && h == 1684) return "A2"; | |
| 162 if (w == 842 && h == 1191) return "A3"; | |
| 163 if (w == 595 && h == 842) return "A4"; | |
| 164 if (w == 420 && h == 595) return "A5"; | |
| 165 if (w == 297 && h == 420) return "A6"; | |
| 166 | |
| 167 /* US */ | |
| 168 if (w == 612 && h == 792) return "Letter"; | |
| 169 if (w == 612 && h == 1008) return "Legal"; | |
| 170 if (w == 792 && h == 1224) return "Ledger"; | |
| 171 if (w == 1224 && h == 792) return "Tabloid"; | |
| 172 | |
| 173 return NULL; | |
| 174 } | |
| 175 | |
| 176 #define MINRES (zoom_list[0]) | |
| 177 #define MAXRES (zoom_list[nelem(zoom_list)-1]) | |
| 178 #define DEFRES 96 | |
| 179 | |
| 180 static char *password = ""; | |
| 181 static char *anchor = NULL; | |
| 182 static float layout_w = FZ_DEFAULT_LAYOUT_W; | |
| 183 static float layout_h = FZ_DEFAULT_LAYOUT_H; | |
| 184 static float layout_em = FZ_DEFAULT_LAYOUT_EM; | |
| 185 static char *layout_css = NULL; | |
| 186 static int layout_use_doc_css = 1; | |
| 187 static int enable_js = 1; | |
| 188 static int tint_white = 0xFFFFF0; | |
| 189 static int tint_black = 0x303030; | |
| 190 | |
| 191 static fz_document *doc = NULL; | |
| 192 static fz_page *fzpage = NULL; | |
| 193 static fz_separations *seps = NULL; | |
| 194 static fz_outline *outline = NULL; | |
| 195 static fz_link *links = NULL; | |
| 196 | |
| 197 static int number = 0; | |
| 198 | |
| 199 static fz_pixmap *page_contents = NULL; | |
| 200 static struct texture page_tex = { 0 }; | |
| 201 static int screen_w = 0, screen_h = 0; | |
| 202 static int scroll_x = 0, scroll_y = 0; | |
| 203 static int canvas_x = 0, canvas_w = 100; | |
| 204 static int canvas_y = 0, canvas_h = 100; | |
| 205 | |
| 206 static int outline_w = 14; /* to be scaled by lineheight */ | |
| 207 static int annotate_w = 12; /* to be scaled by lineheight */ | |
| 208 | |
| 209 static int outline_start_x = 0; | |
| 210 | |
| 211 static int oldbox = FZ_CROP_BOX, currentbox = FZ_CROP_BOX; | |
| 212 static int oldtint = 0, currenttint = 0; | |
| 213 static int oldinvert = 0, currentinvert = 0; | |
| 214 static int oldicc = 1, currenticc = 1; | |
| 215 static int oldaa = 8, currentaa = 8; | |
| 216 static int oldseparations = 1, currentseparations = 1; | |
| 217 static fz_location oldpage = {0,0}, currentpage = {0,0}; | |
| 218 static float oldzoom = DEFRES, currentzoom = DEFRES; | |
| 219 static float oldrotate = 0, currentrotate = 0; | |
| 220 int page_contents_changed = 0; | |
| 221 int page_annots_changed = 0; | |
| 222 | |
| 223 static fz_output *trace_file = NULL; | |
| 224 static char *reflow_options = NULL; | |
| 225 static int isfullscreen = 0; | |
| 226 static int showoutline = 0; | |
| 227 static int showundo = 0; | |
| 228 static int showlayers = 0; | |
| 229 static int showlinks = 0; | |
| 230 static int showsearch = 0; | |
| 231 int showannotate = 0; | |
| 232 int showform = 0; | |
| 233 | |
| 234 #if FZ_ENABLE_JS | |
| 235 static int showconsole = 0; | |
| 236 static int console_h = 14; /* to be scaled by lineheight */ | |
| 237 static pdf_js_console gl_js_console; | |
| 238 static int console_start_y = 0; | |
| 239 #endif | |
| 240 | |
| 241 static const char *tooltip = NULL; | |
| 242 | |
| 243 struct mark | |
| 244 { | |
| 245 fz_location loc; | |
| 246 fz_point scroll; | |
| 247 }; | |
| 248 | |
| 249 static int history_count = 0; | |
| 250 static struct mark history[256]; | |
| 251 static int future_count = 0; | |
| 252 static struct mark future[256]; | |
| 253 static struct mark marks[10]; | |
| 254 | |
| 255 static char *get_history_filename(void) | |
| 256 { | |
| 257 static char history_path[PATH_MAX]; | |
| 258 static int once = 0; | |
| 259 if (!once) | |
| 260 { | |
| 261 char *home = getenv("MUPDF_HISTORY"); | |
| 262 if (home) | |
| 263 return home; | |
| 264 home = getenv("XDG_CACHE_HOME"); | |
| 265 if (!home) | |
| 266 home = getenv("HOME"); | |
| 267 if (!home) | |
| 268 home = getenv("USERPROFILE"); | |
| 269 if (!home) | |
| 270 home = "/tmp"; | |
| 271 fz_snprintf(history_path, sizeof history_path, "%s/.mupdf.history", home); | |
| 272 fz_cleanname(history_path); | |
| 273 once = 1; | |
| 274 } | |
| 275 return history_path; | |
| 276 } | |
| 277 | |
| 278 static fz_json *read_history_file_as_json(fz_pool *pool) | |
| 279 { | |
| 280 fz_buffer *buf = NULL; | |
| 281 const char *json = "{}"; | |
| 282 const char *history_file; | |
| 283 fz_json *result = NULL; | |
| 284 | |
| 285 fz_var(buf); | |
| 286 | |
| 287 history_file = get_history_filename(); | |
| 288 if (strlen(history_file) == 0) | |
| 289 return NULL; | |
| 290 | |
| 291 if (fz_file_exists(ctx, history_file)) | |
| 292 { | |
| 293 fz_try(ctx) | |
| 294 { | |
| 295 buf = fz_read_file(ctx, history_file); | |
| 296 json = fz_string_from_buffer(ctx, buf); | |
| 297 } | |
| 298 fz_catch(ctx) | |
| 299 ; | |
| 300 } | |
| 301 | |
| 302 fz_try(ctx) | |
| 303 { | |
| 304 result = fz_parse_json(ctx, pool, json); | |
| 305 } | |
| 306 fz_catch(ctx) | |
| 307 { | |
| 308 fz_report_error(ctx); | |
| 309 fz_warn(ctx, "can't parse history file"); | |
| 310 result = NULL; | |
| 311 } | |
| 312 fz_drop_buffer(ctx, buf); | |
| 313 | |
| 314 if (result == NULL || result->type != FZ_JSON_OBJECT) | |
| 315 result = fz_json_new_object(ctx, pool); | |
| 316 return result; | |
| 317 } | |
| 318 | |
| 319 static fz_location load_location(fz_json *val) | |
| 320 { | |
| 321 if (fz_json_is_number(ctx, val)) | |
| 322 return fz_make_location(0, fz_json_to_number(ctx, val) - 1); | |
| 323 if (fz_json_is_array(ctx, val)) | |
| 324 return fz_make_location( | |
| 325 fz_json_to_number(ctx, fz_json_array_get(ctx, val, 0)) - 1, | |
| 326 fz_json_to_number(ctx, fz_json_array_get(ctx, val, 1)) - 1 | |
| 327 ); | |
| 328 return fz_make_location(0, 0); | |
| 329 } | |
| 330 | |
| 331 static fz_json *save_location(fz_pool *pool, fz_location loc) | |
| 332 { | |
| 333 fz_json *arr; | |
| 334 if (loc.chapter == 0) | |
| 335 { | |
| 336 return fz_json_new_number(ctx, pool, loc.page + 1); | |
| 337 } | |
| 338 else | |
| 339 { | |
| 340 arr = fz_json_new_array(ctx, pool); | |
| 341 fz_json_array_push(ctx, pool, arr, fz_json_new_number(ctx, pool, loc.chapter + 1)); | |
| 342 fz_json_array_push(ctx, pool, arr, fz_json_new_number(ctx, pool, loc.page + 1)); | |
| 343 return arr; | |
| 344 } | |
| 345 } | |
| 346 | |
| 347 static void load_history(void) | |
| 348 { | |
| 349 char absname[PATH_MAX]; | |
| 350 fz_pool *pool = NULL; | |
| 351 fz_json *json, *item, *arr, *val; | |
| 352 int i, n; | |
| 353 | |
| 354 fz_var(pool); | |
| 355 | |
| 356 if (!fz_realpath(filename, absname)) | |
| 357 return; | |
| 358 | |
| 359 fz_try(ctx) | |
| 360 { | |
| 361 pool = fz_new_pool(ctx); | |
| 362 json = read_history_file_as_json(pool); | |
| 363 if (json) | |
| 364 { | |
| 365 item = fz_json_object_get(ctx, json, absname); | |
| 366 if (item) | |
| 367 { | |
| 368 val = fz_json_object_get(ctx, item, "current"); | |
| 369 if (val) | |
| 370 currentpage = load_location(val); | |
| 371 | |
| 372 arr = fz_json_object_get(ctx, item, "history"); | |
| 373 if (fz_json_is_array(ctx, arr)) | |
| 374 { | |
| 375 history_count = fz_clampi(fz_json_array_length(ctx, arr), 0, nelem(history)); | |
| 376 for (i = 0; i < history_count; ++i) | |
| 377 history[i].loc = load_location(fz_json_array_get(ctx, arr, i)); | |
| 378 } | |
| 379 | |
| 380 arr = fz_json_object_get(ctx, item, "future"); | |
| 381 if (fz_json_is_array(ctx, arr)) | |
| 382 { | |
| 383 future_count = fz_clampi(fz_json_array_length(ctx, arr), 0, nelem(future)); | |
| 384 for (i = 0; i < future_count; ++i) | |
| 385 future[i].loc = load_location(fz_json_array_get(ctx, arr, i)); | |
| 386 } | |
| 387 | |
| 388 arr = fz_json_object_get(ctx, item, "marks"); | |
| 389 if (fz_json_is_array(ctx, arr)) | |
| 390 { | |
| 391 n = fz_clampi(fz_json_array_length(ctx, arr), 0, nelem(marks)); | |
| 392 for (i = 0; i < n; ++i) | |
| 393 marks[i].loc = load_location(fz_json_array_get(ctx, arr, i)); | |
| 394 } | |
| 395 } | |
| 396 | |
| 397 } | |
| 398 } | |
| 399 fz_always(ctx) | |
| 400 { | |
| 401 fz_drop_pool(ctx, pool); | |
| 402 } | |
| 403 fz_catch(ctx) | |
| 404 { | |
| 405 fz_report_error(ctx); | |
| 406 fz_warn(ctx, "Can't read history file."); | |
| 407 } | |
| 408 } | |
| 409 | |
| 410 static void save_history(void) | |
| 411 { | |
| 412 fz_pool *pool; | |
| 413 char absname[PATH_MAX]; | |
| 414 fz_output *out = NULL; | |
| 415 fz_json *json, *item, *arr; | |
| 416 const char *history_file; | |
| 417 int i; | |
| 418 | |
| 419 fz_var(pool); | |
| 420 fz_var(out); | |
| 421 | |
| 422 if (!doc) | |
| 423 return; | |
| 424 | |
| 425 if (!fz_realpath(filename, absname)) | |
| 426 return; | |
| 427 | |
| 428 fz_try(ctx) | |
| 429 { | |
| 430 pool = fz_new_pool(ctx); | |
| 431 json = read_history_file_as_json(pool); | |
| 432 if (json) | |
| 433 { | |
| 434 item = fz_json_new_object(ctx, pool); | |
| 435 fz_json_object_set(ctx, pool, item, "current", save_location(pool, currentpage)); | |
| 436 | |
| 437 arr = fz_json_new_array(ctx, pool); | |
| 438 for (i = 0; i < history_count; ++i) | |
| 439 fz_json_array_push(ctx, pool, arr, save_location(pool, history[i].loc)); | |
| 440 fz_json_object_set(ctx, pool, item, "history", arr); | |
| 441 | |
| 442 arr = fz_json_new_array(ctx, pool); | |
| 443 for (i = 0; i < future_count; ++i) | |
| 444 fz_json_array_push(ctx, pool, arr, save_location(pool, future[i].loc)); | |
| 445 fz_json_object_set(ctx, pool, item, "future", arr); | |
| 446 | |
| 447 arr = fz_json_new_array(ctx, pool); | |
| 448 for (i = 0; i < (int)nelem(marks); ++i) | |
| 449 fz_json_array_push(ctx, pool, arr, save_location(pool, marks[i].loc)); | |
| 450 fz_json_object_set(ctx, pool, item, "marks", arr); | |
| 451 | |
| 452 fz_json_object_set(ctx, pool, json, absname, item); | |
| 453 | |
| 454 history_file = get_history_filename(); | |
| 455 if (strlen(history_file) > 0) { | |
| 456 out = fz_new_output_with_path(ctx, history_file, 0); | |
| 457 fz_write_json(ctx, out, json); | |
| 458 fz_write_byte(ctx, out, '\n'); | |
| 459 fz_close_output(ctx, out); | |
| 460 } | |
| 461 } | |
| 462 } | |
| 463 fz_always(ctx) | |
| 464 { | |
| 465 fz_drop_pool(ctx, pool); | |
| 466 fz_drop_output(ctx, out); | |
| 467 } | |
| 468 fz_catch(ctx) | |
| 469 { | |
| 470 fz_report_error(ctx); | |
| 471 fz_warn(ctx, "Can't write history file."); | |
| 472 } | |
| 473 } | |
| 474 | |
| 475 | |
| 476 static int create_accel_path(char outname[], size_t len, int create, const char *absname, ...) | |
| 477 { | |
| 478 va_list args; | |
| 479 char *s = outname; | |
| 480 size_t z, remain = len; | |
| 481 char *arg; | |
| 482 | |
| 483 va_start(args, absname); | |
| 484 | |
| 485 while ((arg = va_arg(args, char *)) != NULL) | |
| 486 { | |
| 487 z = fz_snprintf(s, remain, "%s", arg); | |
| 488 if (z+1 > remain) | |
| 489 goto fail; /* won't fit */ | |
| 490 | |
| 491 if (create) | |
| 492 (void) fz_mkdir(outname); | |
| 493 if (!fz_is_directory(ctx, outname)) | |
| 494 goto fail; /* directory creation failed, or that dir doesn't exist! */ | |
| 495 #ifdef _WIN32 | |
| 496 s[z] = '\\'; | |
| 497 #else | |
| 498 s[z] = '/'; | |
| 499 #endif | |
| 500 s[z+1] = 0; | |
| 501 s += z+1; | |
| 502 remain -= z+1; | |
| 503 } | |
| 504 | |
| 505 if (fz_snprintf(s, remain, "%s.accel", absname) >= remain) | |
| 506 goto fail; /* won't fit */ | |
| 507 | |
| 508 va_end(args); | |
| 509 | |
| 510 return 1; | |
| 511 | |
| 512 fail: | |
| 513 va_end(args); | |
| 514 | |
| 515 return 0; | |
| 516 } | |
| 517 | |
| 518 static int convert_to_accel_path(char outname[], char *absname, size_t len, int create) | |
| 519 { | |
| 520 char *tmpdir; | |
| 521 char *s; | |
| 522 | |
| 523 if (absname[0] == '/' || absname[0] == '\\') | |
| 524 ++absname; | |
| 525 | |
| 526 s = absname; | |
| 527 while (*s) { | |
| 528 if (*s == '/' || *s == '\\' || *s == ':') | |
| 529 *s = '%'; | |
| 530 ++s; | |
| 531 } | |
| 532 | |
| 533 #ifdef _WIN32 | |
| 534 tmpdir = getenv("USERPROFILE"); | |
| 535 if (tmpdir && create_accel_path(outname, len, create, absname, tmpdir, ".config", "mupdf", NULL)) | |
| 536 return 1; /* OK! */ | |
| 537 /* TEMP and TMP are user-specific on modern windows. */ | |
| 538 tmpdir = getenv("TEMP"); | |
| 539 if (tmpdir && create_accel_path(outname, len, create, absname, tmpdir, "mupdf", NULL)) | |
| 540 return 1; /* OK! */ | |
| 541 tmpdir = getenv("TMP"); | |
| 542 if (tmpdir && create_accel_path(outname, len, create, absname, tmpdir, "mupdf", NULL)) | |
| 543 return 1; /* OK! */ | |
| 544 #else | |
| 545 tmpdir = getenv("XDG_CACHE_HOME"); | |
| 546 if (tmpdir && create_accel_path(outname, len, create, absname, tmpdir, "mupdf", NULL)) | |
| 547 return 1; /* OK! */ | |
| 548 tmpdir = getenv("HOME"); | |
| 549 if (tmpdir && create_accel_path(outname, len, create, absname, tmpdir, ".cache", "mupdf", NULL)) | |
| 550 return 1; /* OK! */ | |
| 551 #endif | |
| 552 return 0; /* Fail */ | |
| 553 } | |
| 554 | |
| 555 static int get_accelerator_filename(char outname[], size_t len, int create) | |
| 556 { | |
| 557 char absname[PATH_MAX]; | |
| 558 if (!fz_realpath(filename, absname)) | |
| 559 return 0; | |
| 560 if (!convert_to_accel_path(outname, absname, len, create)) | |
| 561 return 0; | |
| 562 return 1; | |
| 563 } | |
| 564 | |
| 565 static void save_accelerator(void) | |
| 566 { | |
| 567 char absname[PATH_MAX]; | |
| 568 | |
| 569 if (!doc) | |
| 570 return; | |
| 571 if (!fz_document_supports_accelerator(ctx, doc)) | |
| 572 return; | |
| 573 if (!get_accelerator_filename(absname, sizeof(absname), 1)) | |
| 574 return; | |
| 575 | |
| 576 fz_save_accelerator(ctx, doc, absname); | |
| 577 } | |
| 578 | |
| 579 static struct input search_input = { { 0 }, 0 }; | |
| 580 static int search_dir = 1; | |
| 581 static fz_location search_page = {-1, -1}; | |
| 582 static fz_location search_hit_page = {-1, -1}; | |
| 583 static int search_active = 0; | |
| 584 char *search_needle = 0; | |
| 585 int search_hit_count = 0; | |
| 586 fz_quad search_hit_quads[5000]; | |
| 587 | |
| 588 static char *help_dialog_text = | |
| 589 "The middle mouse button (scroll wheel button) pans the document view. " | |
| 590 "The right mouse button selects a region and copies the marked text to the clipboard." | |
| 591 "\n" | |
| 592 "\n" | |
| 593 "F1 - show this message\n" | |
| 594 "` F12 - show javascript console\n" | |
| 595 "i - show document information\n" | |
| 596 "o - show document outline\n" | |
| 597 "u - show undo history\n" | |
| 598 "Y - show layer list\n" | |
| 599 "a - show annotation editor\n" | |
| 600 "R - show redaction editor\n" | |
| 601 "L - highlight links\n" | |
| 602 "F - highlight form fields\n" | |
| 603 "r - reload file\n" | |
| 604 "S - save file (only for PDF)\n" | |
| 605 "q - quit\n" | |
| 606 "\n" | |
| 607 "< - decrease E-book font size\n" | |
| 608 "> - increase E-book font size\n" | |
| 609 "B - cycle between MediaBox, CropBox, ArtBox, etc.\n" | |
| 610 "A - toggle anti-aliasing\n" | |
| 611 "I - toggle inverted color mode\n" | |
| 612 "C - toggle tinted color mode\n" | |
| 613 "E - toggle ICC color management\n" | |
| 614 "e - toggle spot color emulation\n" | |
| 615 "\n" | |
| 616 "f - fullscreen window\n" | |
| 617 "w - shrink wrap window\n" | |
| 618 "W - fit to width\n" | |
| 619 "H - fit to height\n" | |
| 620 "Z - fit to page\n" | |
| 621 "z - reset zoom\n" | |
| 622 "[number] z - set zoom resolution in DPI\n" | |
| 623 "plus - zoom in\n" | |
| 624 "minus - zoom out\n" | |
| 625 "[ - rotate counter-clockwise\n" | |
| 626 "] - rotate clockwise\n" | |
| 627 "arrow keys - scroll in small increments\n" | |
| 628 "h, j, k, l - scroll in small increments\n" | |
| 629 "\n" | |
| 630 "b - smart move backward\n" | |
| 631 "space - smart move forward\n" | |
| 632 "comma or page up - go backward\n" | |
| 633 "period or page down - go forward\n" | |
| 634 "g - go to first page\n" | |
| 635 "G - go to last page\n" | |
| 636 "[number] g - go to page number\n" | |
| 637 "\n" | |
| 638 "m - save current location in history\n" | |
| 639 "t - go backward in history\n" | |
| 640 "T - go forward in history\n" | |
| 641 "[number] m - save current location in numbered bookmark\n" | |
| 642 "[number] t - go to numbered bookmark\n" | |
| 643 "\n" | |
| 644 "/ - search for text forward\n" | |
| 645 "? - search for text backward\n" | |
| 646 "n - repeat search\n" | |
| 647 "N - repeat search in reverse direction" | |
| 648 ; | |
| 649 | |
| 650 static void help_dialog(void) | |
| 651 { | |
| 652 static int scroll; | |
| 653 ui_dialog_begin(ui.gridsize*20, ui.gridsize*40); | |
| 654 ui_layout(T, X, W, ui.padsize, ui.padsize); | |
| 655 ui_label("MuPDF %s", FZ_VERSION); | |
| 656 ui_spacer(); | |
| 657 ui_layout(B, NONE, S, ui.padsize, ui.padsize); | |
| 658 if (ui_button("Okay") || ui.key == KEY_ENTER || ui.key == KEY_ESCAPE) | |
| 659 ui.dialog = NULL; | |
| 660 ui_spacer(); | |
| 661 ui_layout(ALL, BOTH, CENTER, ui.padsize, ui.padsize); | |
| 662 ui_label_with_scrollbar(help_dialog_text, 0, 0, &scroll, NULL); | |
| 663 ui_dialog_end(); | |
| 664 } | |
| 665 | |
| 666 static fz_buffer *format_info_text(); | |
| 667 | |
| 668 static void info_dialog(void) | |
| 669 { | |
| 670 static int scroll; | |
| 671 fz_buffer *info_text; | |
| 672 | |
| 673 ui_dialog_begin(ui.gridsize*20, ui.gridsize*20); | |
| 674 ui_layout(B, NONE, S, ui.padsize, ui.padsize); | |
| 675 if (ui_button("Okay") || ui.key == KEY_ENTER || ui.key == KEY_ESCAPE) | |
| 676 ui.dialog = NULL; | |
| 677 ui_spacer(); | |
| 678 ui_layout(ALL, BOTH, CENTER, ui.padsize, ui.padsize); | |
| 679 | |
| 680 info_text = format_info_text(); | |
| 681 ui_label_with_scrollbar((char*)fz_string_from_buffer(ctx, info_text), 0, 0, &scroll, NULL); | |
| 682 fz_drop_buffer(ctx, info_text); | |
| 683 | |
| 684 ui_dialog_end(); | |
| 685 } | |
| 686 | |
| 687 static char error_message[256]; | |
| 688 static void error_dialog(void) | |
| 689 { | |
| 690 ui_dialog_begin(ui.gridsize*20, (ui.gridsize+ui.padsize*2)*4); | |
| 691 ui_layout(T, NONE, NW, ui.padsize, ui.padsize); | |
| 692 ui_label("%C %s", 0x1f4a3, error_message); /* BOMB */ | |
| 693 ui_layout(B, NONE, S, ui.padsize, ui.padsize); | |
| 694 if (ui_button("Quit") || ui.key == KEY_ENTER || ui.key == KEY_ESCAPE || ui.key == 'q') | |
| 695 glutLeaveMainLoop(); | |
| 696 ui_dialog_end(); | |
| 697 } | |
| 698 void ui_show_error_dialog(const char *fmt, ...) | |
| 699 { | |
| 700 va_list ap; | |
| 701 va_start(ap, fmt); | |
| 702 fz_vsnprintf(error_message, sizeof error_message, fmt, ap); | |
| 703 va_end(ap); | |
| 704 ui.dialog = error_dialog; | |
| 705 } | |
| 706 | |
| 707 static char warning_message[256]; | |
| 708 static void warning_dialog(void) | |
| 709 { | |
| 710 ui_dialog_begin(ui.gridsize*20, (ui.gridsize+ui.padsize*2)*4); | |
| 711 ui_layout(T, NONE, NW, ui.padsize, ui.padsize); | |
| 712 ui_label("%C %s", 0x26a0, warning_message); /* WARNING SIGN */ | |
| 713 ui_layout(B, NONE, S, ui.padsize, ui.padsize); | |
| 714 if (ui_button("Okay") || ui.key == KEY_ENTER || ui.key == KEY_ESCAPE) | |
| 715 ui.dialog = NULL; | |
| 716 ui_dialog_end(); | |
| 717 } | |
| 718 void ui_show_warning_dialog(const char *fmt, ...) | |
| 719 { | |
| 720 va_list ap; | |
| 721 va_start(ap, fmt); | |
| 722 fz_vsnprintf(warning_message, sizeof warning_message, fmt, ap); | |
| 723 va_end(ap); | |
| 724 ui.dialog = warning_dialog; | |
| 725 } | |
| 726 | |
| 727 static void quit_dialog(void) | |
| 728 { | |
| 729 ui_dialog_begin(ui.gridsize*20, (ui.gridsize+ui.padsize*2)*3); | |
| 730 ui_layout(T, NONE, NW, ui.padsize, ui.padsize); | |
| 731 ui_label("%C The document has unsaved changes. Are you sure you want to quit?", 0x26a0); /* WARNING SIGN */ | |
| 732 ui_layout(B, X, S, ui.padsize, ui.padsize); | |
| 733 ui_panel_begin(0, ui.gridsize, 0, 0, 0); | |
| 734 { | |
| 735 ui_layout(R, NONE, S, 0, 0); | |
| 736 if (ui_button("Save")) | |
| 737 do_save_pdf_file(); | |
| 738 ui_spacer(); | |
| 739 if (ui_button("Discard") || ui.key == 'q') | |
| 740 glutLeaveMainLoop(); | |
| 741 ui_layout(L, NONE, S, 0, 0); | |
| 742 if (ui_button("Cancel") || ui.key == KEY_ESCAPE) | |
| 743 ui.dialog = NULL; | |
| 744 } | |
| 745 ui_panel_end(); | |
| 746 ui_dialog_end(); | |
| 747 } | |
| 748 | |
| 749 static void quit(void) | |
| 750 { | |
| 751 if (pdf && pdf_has_unsaved_changes(ctx, pdf)) | |
| 752 ui.dialog = quit_dialog; | |
| 753 else | |
| 754 glutLeaveMainLoop(); | |
| 755 } | |
| 756 | |
| 757 static void reload_dialog(void) | |
| 758 { | |
| 759 ui_dialog_begin(ui.gridsize*20, (ui.gridsize+ui.padsize*2)*3); | |
| 760 ui_layout(T, NONE, NW, ui.padsize, ui.padsize); | |
| 761 ui_label("%C The document has unsaved changes. Are you sure you want to reload?", 0x26a0); /* WARNING SIGN */ | |
| 762 ui_layout(B, X, S, ui.padsize, ui.padsize); | |
| 763 ui_panel_begin(0, ui.gridsize, 0, 0, 0); | |
| 764 { | |
| 765 ui_layout(R, NONE, S, 0, 0); | |
| 766 if (ui_button("Save")) | |
| 767 do_save_pdf_file(); | |
| 768 ui_spacer(); | |
| 769 if (ui_button("Reload") || ui.key == 'q') | |
| 770 { | |
| 771 ui.dialog = NULL; | |
| 772 reload_document(); | |
| 773 } | |
| 774 ui_layout(L, NONE, S, 0, 0); | |
| 775 if (ui_button("Cancel") || ui.key == KEY_ESCAPE) | |
| 776 ui.dialog = NULL; | |
| 777 } | |
| 778 ui_panel_end(); | |
| 779 ui_dialog_end(); | |
| 780 } | |
| 781 | |
| 782 void reload(void) | |
| 783 { | |
| 784 if (pdf && pdf_has_unsaved_changes(ctx, pdf)) | |
| 785 ui.dialog = reload_dialog; | |
| 786 else | |
| 787 reload_document(); | |
| 788 } | |
| 789 | |
| 790 void trace_action(const char *fmt, ...) | |
| 791 { | |
| 792 va_list args; | |
| 793 if (trace_file) | |
| 794 { | |
| 795 va_start(args, fmt); | |
| 796 fz_write_vprintf(ctx, trace_file, fmt, args); | |
| 797 fz_flush_output(ctx, trace_file); | |
| 798 va_end(args); | |
| 799 va_start(args, fmt); | |
| 800 fz_write_vprintf(ctx, fz_stdout(ctx), fmt, args); | |
| 801 fz_flush_output(ctx, fz_stdout(ctx)); | |
| 802 va_end(args); | |
| 803 } | |
| 804 } | |
| 805 | |
| 806 void trace_page_update(void) | |
| 807 { | |
| 808 trace_action("page.update();\n"); | |
| 809 } | |
| 810 | |
| 811 void trace_save_snapshot(void) | |
| 812 { | |
| 813 static int trace_idx = 1; | |
| 814 trace_action("page.toPixmap(Matrix.identity, ColorSpace.DeviceRGB).saveAsPNG(\"trace-%03d.png\");\n", trace_idx++); | |
| 815 } | |
| 816 | |
| 817 static int document_shown_as_dirty = 0; | |
| 818 | |
| 819 void update_title(void) | |
| 820 { | |
| 821 char buf[256]; | |
| 822 const char *title = "MuPDF/GL"; | |
| 823 char *extra = ""; | |
| 824 size_t n; | |
| 825 | |
| 826 int nc = fz_count_chapters(ctx, doc); | |
| 827 | |
| 828 title = fz_basename(filename); | |
| 829 | |
| 830 document_shown_as_dirty = pdf && pdf_has_unsaved_changes(ctx, pdf); | |
| 831 if (document_shown_as_dirty) | |
| 832 extra = "*"; | |
| 833 | |
| 834 n = strlen(title); | |
| 835 if (n > 50) | |
| 836 { | |
| 837 if (nc == 1) | |
| 838 sprintf(buf, "...%s%s - %d/%d", title + n - 50, extra, currentpage.page + 1, fz_count_pages(ctx, doc)); | |
| 839 else | |
| 840 sprintf(buf, "...%s%s - %d/%d - %d/%d", title + n - 50, extra, | |
| 841 currentpage.chapter + 1, nc, | |
| 842 currentpage.page + 1, fz_count_chapter_pages(ctx, doc, currentpage.chapter)); | |
| 843 } | |
| 844 else | |
| 845 { | |
| 846 if (nc == 1) | |
| 847 sprintf(buf, "%s%s - %d/%d", title, extra, currentpage.page + 1, fz_count_pages(ctx, doc)); | |
| 848 else | |
| 849 | |
| 850 sprintf(buf, "%s%s - %d/%d - %d/%d", title, extra, | |
| 851 currentpage.chapter + 1, nc, | |
| 852 currentpage.page + 1, fz_count_chapter_pages(ctx, doc, currentpage.chapter)); | |
| 853 } | |
| 854 glutSetWindowTitle(buf); | |
| 855 glutSetIconTitle(buf); | |
| 856 } | |
| 857 | |
| 858 void transform_page(void) | |
| 859 { | |
| 860 draw_page_ctm = fz_transform_page(page_bounds, currentzoom, currentrotate); | |
| 861 draw_page_bounds = fz_transform_rect(page_bounds, draw_page_ctm); | |
| 862 } | |
| 863 | |
| 864 static void clear_selected_annot(void) | |
| 865 { | |
| 866 /* clear all editor selections */ | |
| 867 if (ui.selected_annot && pdf_annot_type(ctx, ui.selected_annot) == PDF_ANNOT_WIDGET) | |
| 868 pdf_annot_event_blur(ctx, ui.selected_annot); | |
| 869 ui_select_annot(NULL); | |
| 870 } | |
| 871 | |
| 872 void load_page(void) | |
| 873 { | |
| 874 fz_irect area; | |
| 875 | |
| 876 clear_selected_annot(); | |
| 877 | |
| 878 if (trace_file) | |
| 879 trace_action("page = doc.loadPage(%d);\n", fz_page_number_from_location(ctx, doc, currentpage)); | |
| 880 | |
| 881 fz_drop_stext_page(ctx, page_text); | |
| 882 page_text = NULL; | |
| 883 fz_drop_separations(ctx, seps); | |
| 884 seps = NULL; | |
| 885 fz_drop_link(ctx, links); | |
| 886 links = NULL; | |
| 887 fz_drop_page(ctx, fzpage); | |
| 888 fzpage = NULL; | |
| 889 | |
| 890 fzpage = fz_load_chapter_page(ctx, doc, currentpage.chapter, currentpage.page); | |
| 891 if (pdf) | |
| 892 page = (pdf_page*)fzpage; | |
| 893 | |
| 894 if (trace_file) | |
| 895 { | |
| 896 pdf_annot *w; | |
| 897 int i, s; | |
| 898 | |
| 899 for (i = 0, s = 0, w = pdf_first_widget(ctx, page); w != NULL; i++, w = pdf_next_widget(ctx, w)) | |
| 900 if (pdf_widget_type(ctx, w) == PDF_WIDGET_TYPE_SIGNATURE) | |
| 901 { | |
| 902 int is_signed; | |
| 903 | |
| 904 s++; | |
| 905 trace_action("widget = page.getWidgets()[%d];\n", i); | |
| 906 trace_action("widgetstr = 'Signature %d on page %d';\n", | |
| 907 s, fz_page_number_from_location(ctx, doc, currentpage)); | |
| 908 | |
| 909 is_signed = pdf_widget_is_signed(ctx, w); | |
| 910 trace_action("tmp = widget.isSigned();\n"); | |
| 911 trace_action("if (tmp != %d)\n", is_signed); | |
| 912 trace_action(" throw new RegressionError(widgetstr, 'is signed:', tmp|0, 'expected:', %d);\n", is_signed); | |
| 913 | |
| 914 if (is_signed) | |
| 915 { | |
| 916 int valid_until, is_readonly; | |
| 917 char *cert_error, *digest_error; | |
| 918 pdf_pkcs7_distinguished_name *dn; | |
| 919 pdf_pkcs7_verifier *verifier; | |
| 920 char *signatory = NULL; | |
| 921 char buf[500]; | |
| 922 | |
| 923 valid_until = pdf_validate_signature(ctx, w); | |
| 924 is_readonly = pdf_widget_is_readonly(ctx, w); | |
| 925 verifier = pkcs7_openssl_new_verifier(ctx); | |
| 926 cert_error = pdf_signature_error_description(pdf_check_widget_certificate(ctx, verifier, w)); | |
| 927 digest_error = pdf_signature_error_description(pdf_check_widget_digest(ctx, verifier, w)); | |
| 928 dn = pdf_signature_get_widget_signatory(ctx, verifier, w); | |
| 929 if (dn) | |
| 930 { | |
| 931 char *s = pdf_signature_format_distinguished_name(ctx, dn); | |
| 932 fz_strlcpy(buf, s, sizeof buf); | |
| 933 fz_free(ctx, s); | |
| 934 pdf_signature_drop_distinguished_name(ctx, dn); | |
| 935 } | |
| 936 else | |
| 937 { | |
| 938 fz_strlcpy(buf, "Signature information missing.", sizeof buf); | |
| 939 } | |
| 940 signatory = &buf[0]; | |
| 941 pdf_drop_verifier(ctx, verifier); | |
| 942 | |
| 943 trace_action("tmp = widget.validateSignature();\n"); | |
| 944 trace_action("if (tmp != %d)\n", valid_until); | |
| 945 trace_action(" throw new RegressionError(widgetstr, 'valid until:', tmp, 'expected:', %d);\n", valid_until); | |
| 946 trace_action("tmp = widget.isReadOnly();\n"); | |
| 947 trace_action("if (tmp != %d)\n", is_readonly); | |
| 948 trace_action(" throw new RegressionError(widgetstr, 'is read-only:', tmp, 'expected:', %d);\n", is_readonly); | |
| 949 trace_action("tmp = widget.checkCertificate();\n"); | |
| 950 trace_action("if (tmp != '%s')\n", cert_error); | |
| 951 trace_action(" throw new RegressionError(widgetstr, 'certificate error:', tmp, 'expected:', %d);\n", cert_error); | |
| 952 trace_action("tmp = widget.checkDigest();\n"); | |
| 953 trace_action("if (tmp != %q)\n", digest_error); | |
| 954 trace_action(" throw new RegressionError(widgetstr, 'digest error:', tmp, 'expected:', %q);\n", digest_error); | |
| 955 trace_action("tmp = widget.getSignatory();\n"); | |
| 956 trace_action("if (tmp != '%s')\n", signatory); | |
| 957 trace_action(" throw new RegressionError(widgetstr, 'signatory:', '[', tmp, ']', 'expected:', '[', %q, ']');\n", signatory); | |
| 958 } | |
| 959 } | |
| 960 } | |
| 961 | |
| 962 links = fz_load_links(ctx, fzpage); | |
| 963 page_text = fz_new_stext_page_from_page(ctx, fzpage, NULL); | |
| 964 | |
| 965 if (currenticc) | |
| 966 fz_enable_icc(ctx); | |
| 967 else | |
| 968 fz_disable_icc(ctx); | |
| 969 | |
| 970 if (currentseparations) | |
| 971 { | |
| 972 seps = fz_page_separations(ctx, fzpage); | |
| 973 if (seps) | |
| 974 { | |
| 975 int i, n = fz_count_separations(ctx, seps); | |
| 976 for (i = 0; i < n; i++) | |
| 977 fz_set_separation_behavior(ctx, seps, i, FZ_SEPARATION_COMPOSITE); | |
| 978 } | |
| 979 else if (fz_page_uses_overprint(ctx, fzpage)) | |
| 980 seps = fz_new_separations(ctx, 0); | |
| 981 else if (fz_document_output_intent(ctx, doc)) | |
| 982 seps = fz_new_separations(ctx, 0); | |
| 983 } | |
| 984 | |
| 985 /* compute bounds here for initial window size */ | |
| 986 page_bounds = fz_bound_page_box(ctx, fzpage, currentbox); | |
| 987 transform_page(); | |
| 988 | |
| 989 area = fz_irect_from_rect(draw_page_bounds); | |
| 990 page_tex.w = area.x1 - area.x0; | |
| 991 page_tex.h = area.y1 - area.y0; | |
| 992 | |
| 993 page_contents_changed = 1; | |
| 994 } | |
| 995 | |
| 996 static void render_page(void) | |
| 997 { | |
| 998 fz_irect bbox; | |
| 999 fz_pixmap *pix; | |
| 1000 fz_device *dev; | |
| 1001 | |
| 1002 page_bounds = fz_bound_page_box(ctx, fzpage, currentbox); | |
| 1003 transform_page(); | |
| 1004 | |
| 1005 fz_set_aa_level(ctx, currentaa); | |
| 1006 | |
| 1007 if (page_contents_changed) | |
| 1008 { | |
| 1009 fz_drop_pixmap(ctx, page_contents); | |
| 1010 page_contents = NULL; | |
| 1011 | |
| 1012 bbox = fz_round_rect(fz_transform_rect(fz_bound_page_box(ctx, fzpage, currentbox), draw_page_ctm)); | |
| 1013 page_contents = fz_new_pixmap_with_bbox(ctx, profile, bbox, seps, 0); | |
| 1014 fz_clear_pixmap(ctx, page_contents); | |
| 1015 | |
| 1016 dev = fz_new_draw_device(ctx, draw_page_ctm, page_contents); | |
| 1017 | |
| 1018 fz_try(ctx) | |
| 1019 { | |
| 1020 fz_run_page_contents(ctx, fzpage, dev, fz_identity, NULL); | |
| 1021 fz_close_device(ctx, dev); | |
| 1022 } | |
| 1023 fz_always(ctx) | |
| 1024 fz_drop_device(ctx, dev); | |
| 1025 fz_catch(ctx) | |
| 1026 fz_rethrow(ctx); | |
| 1027 } | |
| 1028 | |
| 1029 pix = fz_clone_pixmap_area_with_different_seps(ctx, page_contents, NULL, profile, NULL, fz_default_color_params, NULL); | |
| 1030 { | |
| 1031 dev = fz_new_draw_device(ctx, draw_page_ctm, pix); | |
| 1032 fz_try(ctx) | |
| 1033 { | |
| 1034 fz_run_page_annots(ctx, fzpage, dev, fz_identity, NULL); | |
| 1035 fz_run_page_widgets(ctx, fzpage, dev, fz_identity, NULL); | |
| 1036 fz_close_device(ctx, dev); | |
| 1037 } | |
| 1038 fz_always(ctx) | |
| 1039 fz_drop_device(ctx, dev); | |
| 1040 fz_catch(ctx) | |
| 1041 fz_rethrow(ctx); | |
| 1042 } | |
| 1043 | |
| 1044 if (currentinvert) | |
| 1045 { | |
| 1046 fz_invert_pixmap_luminance(ctx, pix); | |
| 1047 fz_gamma_pixmap(ctx, pix, 1 / 1.4f); | |
| 1048 } | |
| 1049 if (currenttint) | |
| 1050 { | |
| 1051 fz_tint_pixmap(ctx, pix, tint_black, tint_white); | |
| 1052 } | |
| 1053 | |
| 1054 ui_texture_from_pixmap(&page_tex, pix); | |
| 1055 | |
| 1056 fz_drop_pixmap(ctx, pix); | |
| 1057 | |
| 1058 FZ_LOG_DUMP_STORE(ctx, "Store state after page render:\n"); | |
| 1059 } | |
| 1060 | |
| 1061 void render_page_if_changed(void) | |
| 1062 { | |
| 1063 if (pdf) | |
| 1064 { | |
| 1065 if (pdf_update_page(ctx, page)) | |
| 1066 { | |
| 1067 trace_page_update(); | |
| 1068 page_annots_changed = 1; | |
| 1069 } | |
| 1070 } | |
| 1071 | |
| 1072 if (oldpage.chapter != currentpage.chapter || | |
| 1073 oldpage.page != currentpage.page || | |
| 1074 oldzoom != currentzoom || | |
| 1075 oldrotate != currentrotate || | |
| 1076 oldinvert != currentinvert || | |
| 1077 oldtint != currenttint || | |
| 1078 oldicc != currenticc || | |
| 1079 oldseparations != currentseparations || | |
| 1080 oldaa != currentaa || | |
| 1081 oldbox != currentbox) | |
| 1082 { | |
| 1083 page_contents_changed = 1; | |
| 1084 } | |
| 1085 | |
| 1086 if (page_contents_changed || page_annots_changed) | |
| 1087 { | |
| 1088 render_page(); | |
| 1089 oldpage = currentpage; | |
| 1090 oldzoom = currentzoom; | |
| 1091 oldrotate = currentrotate; | |
| 1092 oldinvert = currentinvert; | |
| 1093 oldtint = currenttint; | |
| 1094 oldicc = currenticc; | |
| 1095 oldseparations = currentseparations; | |
| 1096 oldaa = currentaa; | |
| 1097 oldbox = currentbox; | |
| 1098 page_contents_changed = 0; | |
| 1099 page_annots_changed = 0; | |
| 1100 } | |
| 1101 } | |
| 1102 | |
| 1103 static struct mark save_mark() | |
| 1104 { | |
| 1105 struct mark mark; | |
| 1106 mark.loc = currentpage; | |
| 1107 mark.scroll = fz_transform_point_xy(scroll_x, scroll_y, view_page_inv_ctm); | |
| 1108 return mark; | |
| 1109 } | |
| 1110 | |
| 1111 static void restore_mark(struct mark mark) | |
| 1112 { | |
| 1113 currentpage = mark.loc; | |
| 1114 mark.scroll = fz_transform_point(mark.scroll, draw_page_ctm); | |
| 1115 scroll_x = mark.scroll.x; | |
| 1116 scroll_y = mark.scroll.y; | |
| 1117 } | |
| 1118 | |
| 1119 static int eqloc(fz_location a, fz_location b) | |
| 1120 { | |
| 1121 return a.chapter == b.chapter && a.page == b.page; | |
| 1122 } | |
| 1123 | |
| 1124 int search_has_results(void) | |
| 1125 { | |
| 1126 return !search_active && eqloc(search_hit_page, currentpage) && search_hit_count > 0; | |
| 1127 } | |
| 1128 | |
| 1129 static int is_first_page(fz_location loc) | |
| 1130 { | |
| 1131 return (loc.chapter == 0 && loc.page == 0); | |
| 1132 } | |
| 1133 | |
| 1134 static int is_last_page(fz_location loc) | |
| 1135 { | |
| 1136 fz_location last = fz_last_page(ctx, doc); | |
| 1137 return (loc.chapter == last.chapter && loc.page == last.page); | |
| 1138 } | |
| 1139 | |
| 1140 static void push_history(void) | |
| 1141 { | |
| 1142 if (history_count > 0 && eqloc(history[history_count-1].loc, currentpage)) | |
| 1143 return; | |
| 1144 if (history_count + 1 >= (int)nelem(history)) | |
| 1145 { | |
| 1146 memmove(history, history + 1, sizeof *history * (nelem(history) - 1)); | |
| 1147 history[history_count] = save_mark(); | |
| 1148 } | |
| 1149 else | |
| 1150 { | |
| 1151 history[history_count++] = save_mark(); | |
| 1152 } | |
| 1153 } | |
| 1154 | |
| 1155 static void push_future(void) | |
| 1156 { | |
| 1157 if (future_count + 1 >= (int)nelem(future)) | |
| 1158 { | |
| 1159 memmove(future, future + 1, sizeof *future * (nelem(future) - 1)); | |
| 1160 future[future_count] = save_mark(); | |
| 1161 } | |
| 1162 else | |
| 1163 { | |
| 1164 future[future_count++] = save_mark(); | |
| 1165 } | |
| 1166 } | |
| 1167 | |
| 1168 static void clear_future(void) | |
| 1169 { | |
| 1170 future_count = 0; | |
| 1171 } | |
| 1172 | |
| 1173 static void jump_to_location(fz_location loc) | |
| 1174 { | |
| 1175 clear_future(); | |
| 1176 push_history(); | |
| 1177 currentpage = fz_clamp_location(ctx, doc, loc); | |
| 1178 push_history(); | |
| 1179 } | |
| 1180 | |
| 1181 static void jump_to_location_xy(fz_location loc, float x, float y) | |
| 1182 { | |
| 1183 fz_point p = fz_transform_point_xy(x, y, draw_page_ctm); | |
| 1184 clear_future(); | |
| 1185 push_history(); | |
| 1186 currentpage = fz_clamp_location(ctx, doc, loc); | |
| 1187 scroll_x = p.x; | |
| 1188 scroll_y = p.y; | |
| 1189 push_history(); | |
| 1190 } | |
| 1191 | |
| 1192 static void jump_to_page(int newpage) | |
| 1193 { | |
| 1194 clear_future(); | |
| 1195 push_history(); | |
| 1196 currentpage = fz_location_from_page_number(ctx, doc, newpage); | |
| 1197 currentpage = fz_clamp_location(ctx, doc, currentpage); | |
| 1198 push_history(); | |
| 1199 } | |
| 1200 | |
| 1201 static void jump_to_page_xy(int newpage, float x, float y) | |
| 1202 { | |
| 1203 fz_point p = fz_transform_point_xy(x, y, draw_page_ctm); | |
| 1204 clear_future(); | |
| 1205 push_history(); | |
| 1206 currentpage = fz_location_from_page_number(ctx, doc, newpage); | |
| 1207 currentpage = fz_clamp_location(ctx, doc, currentpage); | |
| 1208 scroll_x = p.x; | |
| 1209 scroll_y = p.y; | |
| 1210 push_history(); | |
| 1211 } | |
| 1212 | |
| 1213 static void pop_history(void) | |
| 1214 { | |
| 1215 fz_location here = currentpage; | |
| 1216 push_future(); | |
| 1217 while (history_count > 0 && eqloc(currentpage, here)) | |
| 1218 restore_mark(history[--history_count]); | |
| 1219 } | |
| 1220 | |
| 1221 static void pop_future(void) | |
| 1222 { | |
| 1223 fz_location here = currentpage; | |
| 1224 push_history(); | |
| 1225 while (future_count > 0 && eqloc(currentpage, here)) | |
| 1226 restore_mark(future[--future_count]); | |
| 1227 push_history(); | |
| 1228 } | |
| 1229 | |
| 1230 static void relayout(void) | |
| 1231 { | |
| 1232 if (layout_em < 6) layout_em = 6; | |
| 1233 if (layout_em > 36) layout_em = 36; | |
| 1234 if (fz_is_document_reflowable(ctx, doc)) | |
| 1235 { | |
| 1236 fz_bookmark mark = fz_make_bookmark(ctx, doc, currentpage); | |
| 1237 fz_layout_document(ctx, doc, layout_w, layout_h, layout_em); | |
| 1238 currentpage = fz_lookup_bookmark(ctx, doc, mark); | |
| 1239 history_count = 0; | |
| 1240 future_count = 0; | |
| 1241 | |
| 1242 load_page(); | |
| 1243 update_title(); | |
| 1244 } | |
| 1245 } | |
| 1246 | |
| 1247 static int count_outline(fz_outline *node, int end) | |
| 1248 { | |
| 1249 int is_selected, n, p, np; | |
| 1250 int count = 0; | |
| 1251 | |
| 1252 if (!node) | |
| 1253 return 0; | |
| 1254 np = fz_page_number_from_location(ctx, doc, node->page); | |
| 1255 | |
| 1256 do | |
| 1257 { | |
| 1258 p = np; | |
| 1259 count += 1; | |
| 1260 n = end; | |
| 1261 if (node->next && (np = fz_page_number_from_location(ctx, doc, node->next->page)) >= 0) | |
| 1262 n = fz_page_number_from_location(ctx, doc, node->next->page); | |
| 1263 is_selected = 0; | |
| 1264 if (fz_count_chapters(ctx, doc) == 1) | |
| 1265 is_selected = (p>=0) && (currentpage.page == p || (currentpage.page > p && currentpage.page < n)); | |
| 1266 if (node->down && (node->is_open || is_selected)) | |
| 1267 count += count_outline(node->down, end); | |
| 1268 node = node->next; | |
| 1269 } | |
| 1270 while (node); | |
| 1271 | |
| 1272 return count; | |
| 1273 } | |
| 1274 | |
| 1275 static void do_outline_imp(struct list *list, int end, fz_outline *node, int depth) | |
| 1276 { | |
| 1277 int is_selected, is_open, was_open, n, np; | |
| 1278 | |
| 1279 if (!node) | |
| 1280 return; | |
| 1281 | |
| 1282 np = fz_page_number_from_location(ctx, doc, node->page); | |
| 1283 | |
| 1284 do | |
| 1285 { | |
| 1286 int p = np; | |
| 1287 n = end; | |
| 1288 if (node->next && (np = fz_page_number_from_location(ctx, doc, node->next->page)) >= 0) | |
| 1289 n = np; | |
| 1290 | |
| 1291 is_open = was_open = node->is_open; | |
| 1292 is_selected = 0; | |
| 1293 if (fz_count_chapters(ctx, doc) == 1) | |
| 1294 is_selected = (p>=0) && (currentpage.page == p || (currentpage.page > p && currentpage.page < n)); | |
| 1295 if (ui_tree_item(list, node, node->title, is_selected, depth, !!node->down, &is_open)) | |
| 1296 { | |
| 1297 node->is_open = is_open; | |
| 1298 if (p < 0) | |
| 1299 { | |
| 1300 currentpage = fz_resolve_link(ctx, doc, node->uri, &node->x, &node->y); | |
| 1301 jump_to_location_xy(currentpage, node->x, node->y); | |
| 1302 } | |
| 1303 else | |
| 1304 { | |
| 1305 jump_to_page_xy(p, node->x, node->y); | |
| 1306 } | |
| 1307 } | |
| 1308 node->is_open = is_open; | |
| 1309 | |
| 1310 if (node->down && (was_open || is_selected)) | |
| 1311 do_outline_imp(list, n, node->down, depth + 1); | |
| 1312 node = node->next; | |
| 1313 } | |
| 1314 while (node); | |
| 1315 } | |
| 1316 | |
| 1317 static void do_outline(fz_outline *node) | |
| 1318 { | |
| 1319 static struct list list; | |
| 1320 ui_layout(L, BOTH, NW, 0, 0); | |
| 1321 ui_tree_begin(&list, count_outline(node, 65535), outline_w, 0, 1); | |
| 1322 do_outline_imp(&list, 65535, node, 0); | |
| 1323 ui_tree_end(&list); | |
| 1324 } | |
| 1325 | |
| 1326 static void do_undo(void) | |
| 1327 { | |
| 1328 static struct list list; | |
| 1329 int count = 0; | |
| 1330 int pos; | |
| 1331 int i; | |
| 1332 int desired = -1; | |
| 1333 | |
| 1334 if (pdf) | |
| 1335 pos = pdf_undoredo_state(ctx, pdf, &count); | |
| 1336 else | |
| 1337 pos = 0; | |
| 1338 ui_layout(L, BOTH, NW, 0, 0); | |
| 1339 ui_panel_begin(outline_w, 0, ui.padsize*2, ui.padsize*2, 1); | |
| 1340 ui_layout(T, X, NW, ui.padsize, ui.padsize); | |
| 1341 ui_label("Undo history:"); | |
| 1342 | |
| 1343 ui_layout(B, X, NW, ui.padsize, ui.padsize); | |
| 1344 if (ui_button_aux("Redo", pos == count)) | |
| 1345 desired = pos+1; | |
| 1346 if (ui_button_aux("Undo", pos == 0)) | |
| 1347 desired = pos-1; | |
| 1348 | |
| 1349 ui_layout(ALL, BOTH, NW, ui.padsize, ui.padsize); | |
| 1350 ui_list_begin(&list, count+1, 0, ui.lineheight * 4 + 4); | |
| 1351 | |
| 1352 for (i = 0; i < count+1; i++) | |
| 1353 { | |
| 1354 const char *op; | |
| 1355 | |
| 1356 if (i == 0) | |
| 1357 op = "Original Document"; | |
| 1358 else | |
| 1359 op = pdf_undoredo_step(ctx, pdf, i-1); | |
| 1360 if (ui_list_item(&list, (void *)(intptr_t)(i+1), op, i <= pos)) | |
| 1361 { | |
| 1362 desired = i; | |
| 1363 } | |
| 1364 } | |
| 1365 | |
| 1366 ui_list_end(&list); | |
| 1367 | |
| 1368 if (desired != -1 && desired != pos) | |
| 1369 { | |
| 1370 clear_selected_annot(); | |
| 1371 page_contents_changed = 1; | |
| 1372 while (pos > desired) | |
| 1373 { | |
| 1374 trace_action("doc.undo();\n"); | |
| 1375 pdf_undo(ctx, pdf); | |
| 1376 pos--; | |
| 1377 } | |
| 1378 while (pos < desired) | |
| 1379 { | |
| 1380 trace_action("doc.redo();\n"); | |
| 1381 pdf_redo(ctx, pdf); | |
| 1382 pos++; | |
| 1383 } | |
| 1384 load_page(); | |
| 1385 } | |
| 1386 | |
| 1387 ui_panel_end(); | |
| 1388 } | |
| 1389 | |
| 1390 static void do_layers(void) | |
| 1391 { | |
| 1392 const char *name; | |
| 1393 int n, i, on; | |
| 1394 | |
| 1395 ui_layout(L, BOTH, NW, 0, 0); | |
| 1396 ui_panel_begin(outline_w, 0, ui.padsize*2, ui.padsize*2, 1); | |
| 1397 ui_layout(T, X, NW, ui.padsize, ui.padsize); | |
| 1398 ui_label("Layers:"); | |
| 1399 ui_layout(T, X, NW, ui.padsize*2, ui.padsize); | |
| 1400 | |
| 1401 if (pdf) | |
| 1402 { | |
| 1403 n = pdf_count_layers(ctx, pdf); | |
| 1404 for (i = 0; i < n; ++i) | |
| 1405 { | |
| 1406 name = pdf_layer_name(ctx, pdf, i); | |
| 1407 on = pdf_layer_is_enabled(ctx, pdf, i); | |
| 1408 if (ui_checkbox(name, &on)) | |
| 1409 { | |
| 1410 pdf_enable_layer(ctx, pdf, i, on); | |
| 1411 page_contents_changed = 1; | |
| 1412 } | |
| 1413 } | |
| 1414 if (n == 0) | |
| 1415 ui_label("None"); | |
| 1416 } | |
| 1417 else | |
| 1418 { | |
| 1419 ui_label("None"); | |
| 1420 } | |
| 1421 | |
| 1422 ui_panel_end(); | |
| 1423 } | |
| 1424 | |
| 1425 static void do_links(fz_link *link) | |
| 1426 { | |
| 1427 fz_rect bounds; | |
| 1428 fz_irect area; | |
| 1429 float link_x, link_y; | |
| 1430 | |
| 1431 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); | |
| 1432 glEnable(GL_BLEND); | |
| 1433 | |
| 1434 tooltip = NULL; | |
| 1435 | |
| 1436 while (link) | |
| 1437 { | |
| 1438 bounds = link->rect; | |
| 1439 bounds = fz_transform_rect(link->rect, view_page_ctm); | |
| 1440 area = fz_irect_from_rect(bounds); | |
| 1441 | |
| 1442 if (ui_mouse_inside(area)) | |
| 1443 { | |
| 1444 if (!tooltip) | |
| 1445 tooltip = link->uri; | |
| 1446 ui.hot = link; | |
| 1447 if (!ui.active && ui.down) | |
| 1448 ui.active = link; | |
| 1449 } | |
| 1450 | |
| 1451 if (ui.hot == link || showlinks) | |
| 1452 { | |
| 1453 if (ui.active == link && ui.hot == link) | |
| 1454 glColor4f(0, 0, 1, 0.4f); | |
| 1455 else if (ui.hot == link) | |
| 1456 glColor4f(0, 0, 1, 0.2f); | |
| 1457 else | |
| 1458 glColor4f(0, 0, 1, 0.1f); | |
| 1459 glRectf(area.x0, area.y0, area.x1, area.y1); | |
| 1460 } | |
| 1461 | |
| 1462 if (ui.active == link && !ui.down) | |
| 1463 { | |
| 1464 if (ui.hot == link) | |
| 1465 { | |
| 1466 if (fz_is_external_link(ctx, link->uri)) | |
| 1467 open_browser(link->uri); | |
| 1468 else | |
| 1469 { | |
| 1470 fz_location loc = fz_resolve_link(ctx, doc, link->uri, &link_x, &link_y); | |
| 1471 jump_to_location_xy(loc, link_x, link_y); | |
| 1472 } | |
| 1473 } | |
| 1474 } | |
| 1475 | |
| 1476 link = link->next; | |
| 1477 } | |
| 1478 | |
| 1479 glDisable(GL_BLEND); | |
| 1480 } | |
| 1481 | |
| 1482 static void do_page_selection(void) | |
| 1483 { | |
| 1484 static fz_point pt = { 0, 0 }; | |
| 1485 static fz_quad hits[1000]; | |
| 1486 fz_rect rect; | |
| 1487 int i, n; | |
| 1488 | |
| 1489 if (ui_mouse_inside(view_page_area)) | |
| 1490 { | |
| 1491 ui.hot = &pt; | |
| 1492 if (!ui.active && ui.right) | |
| 1493 { | |
| 1494 ui.active = &pt; | |
| 1495 pt.x = ui.x; | |
| 1496 pt.y = ui.y; | |
| 1497 } | |
| 1498 } | |
| 1499 | |
| 1500 if (ui.active == &pt) | |
| 1501 { | |
| 1502 fz_point page_a = { pt.x, pt.y }; | |
| 1503 fz_point page_b = { ui.x, ui.y }; | |
| 1504 | |
| 1505 page_a = fz_transform_point(page_a, view_page_inv_ctm); | |
| 1506 page_b = fz_transform_point(page_b, view_page_inv_ctm); | |
| 1507 | |
| 1508 if (ui.mod == GLUT_ACTIVE_CTRL) | |
| 1509 fz_snap_selection(ctx, page_text, &page_a, &page_b, FZ_SELECT_WORDS); | |
| 1510 else if (ui.mod == GLUT_ACTIVE_CTRL + GLUT_ACTIVE_SHIFT) | |
| 1511 fz_snap_selection(ctx, page_text, &page_a, &page_b, FZ_SELECT_LINES); | |
| 1512 | |
| 1513 if (ui.mod == GLUT_ACTIVE_SHIFT) | |
| 1514 { | |
| 1515 rect = fz_make_rect( | |
| 1516 fz_min(page_a.x, page_b.x), | |
| 1517 fz_min(page_a.y, page_b.y), | |
| 1518 fz_max(page_a.x, page_b.x), | |
| 1519 fz_max(page_a.y, page_b.y)); | |
| 1520 n = 1; | |
| 1521 hits[0] = fz_quad_from_rect(rect); | |
| 1522 } | |
| 1523 else | |
| 1524 { | |
| 1525 n = fz_highlight_selection(ctx, page_text, page_a, page_b, hits, nelem(hits)); | |
| 1526 } | |
| 1527 | |
| 1528 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); | |
| 1529 glEnable(GL_BLEND); | |
| 1530 glColor4f(0.0, 0.1, 0.4, 0.3f); | |
| 1531 | |
| 1532 glBegin(GL_QUADS); | |
| 1533 for (i = 0; i < n; ++i) | |
| 1534 { | |
| 1535 fz_quad thit = fz_transform_quad(hits[i], view_page_ctm); | |
| 1536 glVertex2f(thit.ul.x, thit.ul.y); | |
| 1537 glVertex2f(thit.ur.x, thit.ur.y); | |
| 1538 glVertex2f(thit.lr.x, thit.lr.y); | |
| 1539 glVertex2f(thit.ll.x, thit.ll.y); | |
| 1540 } | |
| 1541 glEnd(); | |
| 1542 | |
| 1543 glDisable(GL_BLEND); | |
| 1544 | |
| 1545 if (!ui.right) | |
| 1546 { | |
| 1547 char *s; | |
| 1548 #ifdef _WIN32 | |
| 1549 if (ui.mod == GLUT_ACTIVE_SHIFT) | |
| 1550 s = fz_copy_rectangle(ctx, page_text, rect, 1); | |
| 1551 else | |
| 1552 s = fz_copy_selection(ctx, page_text, page_a, page_b, 1); | |
| 1553 #else | |
| 1554 if (ui.mod == GLUT_ACTIVE_SHIFT) | |
| 1555 s = fz_copy_rectangle(ctx, page_text, rect, 0); | |
| 1556 else | |
| 1557 s = fz_copy_selection(ctx, page_text, page_a, page_b, 0); | |
| 1558 #endif | |
| 1559 ui_set_clipboard(s); | |
| 1560 fz_free(ctx, s); | |
| 1561 } | |
| 1562 } | |
| 1563 } | |
| 1564 | |
| 1565 static void do_search_hits(void) | |
| 1566 { | |
| 1567 int i; | |
| 1568 | |
| 1569 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); | |
| 1570 glEnable(GL_BLEND); | |
| 1571 | |
| 1572 glColor4f(1, 0, 0, 0.4f); | |
| 1573 glBegin(GL_QUADS); | |
| 1574 for (i = 0; i < search_hit_count; ++i) | |
| 1575 { | |
| 1576 fz_quad thit = fz_transform_quad(search_hit_quads[i], view_page_ctm); | |
| 1577 glVertex2f(thit.ul.x, thit.ul.y); | |
| 1578 glVertex2f(thit.ur.x, thit.ur.y); | |
| 1579 glVertex2f(thit.lr.x, thit.lr.y); | |
| 1580 glVertex2f(thit.ll.x, thit.ll.y); | |
| 1581 } | |
| 1582 | |
| 1583 glEnd(); | |
| 1584 glDisable(GL_BLEND); | |
| 1585 } | |
| 1586 | |
| 1587 static void toggle_fullscreen(void) | |
| 1588 { | |
| 1589 static int win_x = 0, win_y = 0; | |
| 1590 static int win_w = 100, win_h = 100; | |
| 1591 if (!isfullscreen) | |
| 1592 { | |
| 1593 win_w = glutGet(GLUT_WINDOW_WIDTH); | |
| 1594 win_h = glutGet(GLUT_WINDOW_HEIGHT); | |
| 1595 win_x = glutGet(GLUT_WINDOW_X); | |
| 1596 win_y = glutGet(GLUT_WINDOW_Y); | |
| 1597 glutFullScreen(); | |
| 1598 isfullscreen = 1; | |
| 1599 } | |
| 1600 else | |
| 1601 { | |
| 1602 glutPositionWindow(win_x, win_y); | |
| 1603 glutReshapeWindow(win_w, win_h); | |
| 1604 isfullscreen = 0; | |
| 1605 } | |
| 1606 } | |
| 1607 | |
| 1608 static void shrinkwrap(void) | |
| 1609 { | |
| 1610 int w = page_tex.w; | |
| 1611 int h = page_tex.h; | |
| 1612 if (showoutline || showundo || showlayers) | |
| 1613 w += outline_w + 4; | |
| 1614 if (showannotate) | |
| 1615 w += annotate_w; | |
| 1616 #if FZ_ENABLE_JS | |
| 1617 if (showconsole) | |
| 1618 h += console_h; | |
| 1619 #endif | |
| 1620 if (screen_w > 0 && w > screen_w) | |
| 1621 w = screen_w; | |
| 1622 if (screen_h > 0 && h > screen_h) | |
| 1623 h = screen_h; | |
| 1624 if (isfullscreen) | |
| 1625 toggle_fullscreen(); | |
| 1626 glutReshapeWindow(w, h); | |
| 1627 } | |
| 1628 | |
| 1629 static struct input input_password; | |
| 1630 static void password_dialog(void) | |
| 1631 { | |
| 1632 int is; | |
| 1633 ui_dialog_begin(ui.gridsize*16, (ui.gridsize+ui.padsize*2)*3); | |
| 1634 { | |
| 1635 ui_layout(T, X, NW, ui.padsize, ui.padsize); | |
| 1636 ui_label("Password:"); | |
| 1637 is = ui_input(&input_password, 200, 1); | |
| 1638 | |
| 1639 ui_layout(B, X, NW, ui.padsize, ui.padsize); | |
| 1640 ui_panel_begin(0, ui.gridsize, 0, 0, 0); | |
| 1641 { | |
| 1642 ui_layout(R, NONE, S, 0, 0); | |
| 1643 if (ui_button("Cancel") || (!ui.focus && ui.key == KEY_ESCAPE)) | |
| 1644 glutLeaveMainLoop(); | |
| 1645 ui_spacer(); | |
| 1646 if (ui_button("Okay") || is == UI_INPUT_ACCEPT) | |
| 1647 { | |
| 1648 password = input_password.text; | |
| 1649 ui.dialog = NULL; | |
| 1650 reload_document(); | |
| 1651 shrinkwrap(); | |
| 1652 } | |
| 1653 } | |
| 1654 ui_panel_end(); | |
| 1655 } | |
| 1656 ui_dialog_end(); | |
| 1657 } | |
| 1658 | |
| 1659 /* Parse "chapter:page" from anchor. "chapter:" is also accepted, | |
| 1660 * meaning first page. Return 1 if parsing succeeded, 0 if failed. | |
| 1661 */ | |
| 1662 static int | |
| 1663 parse_location(const char *anc, fz_location *loc) | |
| 1664 { | |
| 1665 const char *s, *p; | |
| 1666 | |
| 1667 if (anc == NULL) | |
| 1668 return 0; | |
| 1669 | |
| 1670 s = anc; | |
| 1671 while (*s >= '0' && *s <= '9') | |
| 1672 s++; | |
| 1673 loc->chapter = fz_atoi(anc)-1; | |
| 1674 if (*s == 0) | |
| 1675 { | |
| 1676 *loc = fz_location_from_page_number(ctx, doc, loc->chapter); | |
| 1677 return 1; | |
| 1678 } | |
| 1679 if (*s != ':') | |
| 1680 return 0; | |
| 1681 p = ++s; | |
| 1682 while (*s >= '0' && *s <= '9') | |
| 1683 s++; | |
| 1684 if (s == p) | |
| 1685 loc->page = 0; | |
| 1686 else | |
| 1687 loc->page = fz_atoi(p)-1; | |
| 1688 | |
| 1689 return 1; | |
| 1690 } | |
| 1691 | |
| 1692 static void | |
| 1693 reload_or_start_journalling(void) | |
| 1694 { | |
| 1695 char journal[PATH_MAX]; | |
| 1696 | |
| 1697 fz_strlcpy(journal, filename, sizeof(journal)); | |
| 1698 fz_strlcat(journal, ".journal", sizeof(journal)); | |
| 1699 | |
| 1700 fz_try(ctx) | |
| 1701 { | |
| 1702 /* Probe with fz_file_exists to avoid 'can't find' errors. */ | |
| 1703 if (fz_file_exists(ctx, journal)) | |
| 1704 pdf_load_journal(ctx, pdf, journal); | |
| 1705 } | |
| 1706 fz_catch(ctx) | |
| 1707 { | |
| 1708 /* Ignore any failures here. */ | |
| 1709 } | |
| 1710 trace_action("doc.enableJournal();\n"); | |
| 1711 pdf_enable_journal(ctx, pdf); | |
| 1712 } | |
| 1713 | |
| 1714 static void alert_box(const char *fmt, const char *str) | |
| 1715 { | |
| 1716 #ifdef _WIN32 | |
| 1717 MessageBoxA(NULL, str, "MuPDF Alert", MB_ICONERROR); | |
| 1718 #else | |
| 1719 fprintf(stderr, "MuPDF Alert: %s\n", str); | |
| 1720 #endif | |
| 1721 } | |
| 1722 | |
| 1723 | |
| 1724 static void event_cb(fz_context *callback_ctx, pdf_document *callback_doc, pdf_doc_event *evt, void *data) | |
| 1725 { | |
| 1726 switch (evt->type) | |
| 1727 { | |
| 1728 case PDF_DOCUMENT_EVENT_ALERT: | |
| 1729 { | |
| 1730 pdf_alert_event *alert = pdf_access_alert_event(callback_ctx, evt); | |
| 1731 alert_box("%s", alert->message); | |
| 1732 } | |
| 1733 break; | |
| 1734 | |
| 1735 default: | |
| 1736 fz_throw(callback_ctx, FZ_ERROR_UNSUPPORTED, "event not yet implemented"); | |
| 1737 break; | |
| 1738 } | |
| 1739 } | |
| 1740 | |
| 1741 static void load_document(void) | |
| 1742 { | |
| 1743 char accelpath[PATH_MAX]; | |
| 1744 char *accel = NULL; | |
| 1745 time_t atime; | |
| 1746 time_t dtime; | |
| 1747 fz_location location; | |
| 1748 | |
| 1749 fz_drop_outline(ctx, outline); | |
| 1750 outline = NULL; | |
| 1751 fz_drop_document(ctx, doc); | |
| 1752 doc = NULL; | |
| 1753 | |
| 1754 if (!strncmp(filename, "file://", 7)) | |
| 1755 { | |
| 1756 anchor = strchr(filename + 7, '#'); | |
| 1757 if (anchor) | |
| 1758 { | |
| 1759 memmove(anchor + 1, anchor, strlen(anchor) + 1); | |
| 1760 *anchor = 0; | |
| 1761 anchor++; | |
| 1762 } | |
| 1763 memmove(filename, filename + 7, strlen(filename)); | |
| 1764 } | |
| 1765 | |
| 1766 /* If there was an accelerator to load, what would it be called? */ | |
| 1767 if (get_accelerator_filename(accelpath, sizeof(accelpath), 0)) | |
| 1768 { | |
| 1769 /* Check whether that file exists, and isn't older than | |
| 1770 * the document. */ | |
| 1771 atime = fz_stat_mtime(accelpath); | |
| 1772 dtime = fz_stat_mtime(filename); | |
| 1773 if (atime == 0) | |
| 1774 { | |
| 1775 /* No accelerator */ | |
| 1776 } | |
| 1777 else if (atime > dtime) | |
| 1778 accel = accelpath; | |
| 1779 else | |
| 1780 { | |
| 1781 /* Accelerator data is out of date */ | |
| 1782 #ifdef _WIN32 | |
| 1783 fz_remove_utf8(accelpath); | |
| 1784 #else | |
| 1785 remove(accelpath); | |
| 1786 #endif | |
| 1787 accel = NULL; /* In case we have jumped up from below */ | |
| 1788 } | |
| 1789 } | |
| 1790 | |
| 1791 trace_action("doc = Document.openDocument(%q);\n", filename); | |
| 1792 | |
| 1793 doc = fz_open_accelerated_document(ctx, filename, accel); | |
| 1794 pdf = pdf_specifics(ctx, doc); | |
| 1795 | |
| 1796 if (pdf && trace_file) | |
| 1797 { | |
| 1798 int needspass = pdf_needs_password(ctx, pdf); | |
| 1799 trace_action( | |
| 1800 "tmp = doc.needsPassword();\n" | |
| 1801 "if (tmp != %s)\n" | |
| 1802 " throw new RegressionError('Document password needed:', tmp, 'expected:', %s);\n", | |
| 1803 needspass ? "true" : "false", | |
| 1804 needspass ? "true" : "false"); | |
| 1805 } | |
| 1806 | |
| 1807 if (fz_needs_password(ctx, doc)) | |
| 1808 { | |
| 1809 int result = fz_authenticate_password(ctx, doc, password); | |
| 1810 | |
| 1811 if (pdf && trace_file) | |
| 1812 { | |
| 1813 trace_action( | |
| 1814 "tmp = doc.authenticatePassword(%q);\n" | |
| 1815 "if (tmp != %s)\n" | |
| 1816 " throw new RegressionError('Open document with password %q result: %s', 'expected:', '%s');\n", | |
| 1817 password, | |
| 1818 result ? "true" : "false", | |
| 1819 password, | |
| 1820 !result ? "pass" : "fail", | |
| 1821 result ? "pass" : "fail"); | |
| 1822 } | |
| 1823 | |
| 1824 if (!result) | |
| 1825 { | |
| 1826 fz_drop_document(ctx, doc); | |
| 1827 doc = NULL; | |
| 1828 ui_input_init(&input_password, ""); | |
| 1829 ui.focus = &input_password; | |
| 1830 ui.dialog = password_dialog; | |
| 1831 return; | |
| 1832 } | |
| 1833 } | |
| 1834 | |
| 1835 fz_layout_document(ctx, doc, layout_w, layout_h, layout_em); | |
| 1836 | |
| 1837 fz_try(ctx) | |
| 1838 outline = fz_load_outline(ctx, doc); | |
| 1839 fz_catch(ctx) | |
| 1840 { | |
| 1841 fz_report_error(ctx); | |
| 1842 outline = NULL; | |
| 1843 } | |
| 1844 | |
| 1845 load_history(); | |
| 1846 | |
| 1847 if (pdf) | |
| 1848 { | |
| 1849 #if FZ_ENABLE_JS | |
| 1850 if (enable_js) | |
| 1851 { | |
| 1852 trace_action("doc.enableJS();\n"); | |
| 1853 pdf_enable_js(ctx, pdf); | |
| 1854 pdf_js_set_console(ctx, pdf, &gl_js_console, NULL); | |
| 1855 } | |
| 1856 #endif | |
| 1857 | |
| 1858 reload_or_start_journalling(); | |
| 1859 | |
| 1860 if (trace_file) | |
| 1861 { | |
| 1862 int vsns = pdf_count_versions(ctx, pdf); | |
| 1863 trace_action( | |
| 1864 "tmp = doc.countVersions();\n" | |
| 1865 "if (tmp != %d)\n" | |
| 1866 " throw new RegressionError('Document versions:', tmp, 'expected:', %d);\n", | |
| 1867 vsns, vsns); | |
| 1868 if (vsns > 1) | |
| 1869 { | |
| 1870 int valid = pdf_validate_change_history(ctx, pdf); | |
| 1871 trace_action("tmp = doc.validateChangeHistory();\n"); | |
| 1872 trace_action("if (tmp != %d)\n", valid); | |
| 1873 trace_action(" throw new RegressionError('History validation:', tmp, 'expected:', %d);\n", valid); | |
| 1874 } | |
| 1875 } | |
| 1876 } | |
| 1877 | |
| 1878 if (anchor) | |
| 1879 { | |
| 1880 if (parse_location(anchor, &location)) | |
| 1881 jump_to_location(location); | |
| 1882 else | |
| 1883 { | |
| 1884 location = fz_resolve_link(ctx, doc, anchor, NULL, NULL); | |
| 1885 if (location.page < 0) | |
| 1886 fz_warn(ctx, "cannot find location: %s", anchor); | |
| 1887 else | |
| 1888 jump_to_location(location); | |
| 1889 } | |
| 1890 } | |
| 1891 anchor = NULL; | |
| 1892 | |
| 1893 oldpage = currentpage = fz_clamp_location(ctx, doc, currentpage); | |
| 1894 | |
| 1895 if (pdf) | |
| 1896 pdf_set_doc_event_callback(ctx, pdf, event_cb, NULL, NULL); | |
| 1897 } | |
| 1898 | |
| 1899 static void reflow_document(void) | |
| 1900 { | |
| 1901 char buf[256]; | |
| 1902 fz_document *new_doc; | |
| 1903 fz_stext_options opts; | |
| 1904 | |
| 1905 if (fz_is_document_reflowable(ctx, doc)) | |
| 1906 return; | |
| 1907 | |
| 1908 fz_drop_outline(ctx, outline); | |
| 1909 outline = NULL; | |
| 1910 | |
| 1911 fz_parse_stext_options(ctx, &opts, reflow_options); | |
| 1912 | |
| 1913 new_doc = fz_open_reflowed_document(ctx, doc, &opts); | |
| 1914 fz_drop_document(ctx, doc); | |
| 1915 doc = new_doc; | |
| 1916 pdf = NULL; | |
| 1917 page = NULL; | |
| 1918 | |
| 1919 fz_layout_document(ctx, doc, layout_w, layout_h, layout_em); | |
| 1920 | |
| 1921 fz_try(ctx) | |
| 1922 outline = fz_load_outline(ctx, doc); | |
| 1923 fz_catch(ctx) | |
| 1924 outline = NULL; | |
| 1925 | |
| 1926 fz_strlcpy(buf, filename, sizeof buf); | |
| 1927 fz_snprintf(filename, sizeof filename, "%s.xhtml", buf); | |
| 1928 | |
| 1929 load_history(); | |
| 1930 | |
| 1931 if (anchor) | |
| 1932 jump_to_page(fz_atoi(anchor) - 1); | |
| 1933 anchor = NULL; | |
| 1934 | |
| 1935 currentpage = fz_clamp_location(ctx, doc, currentpage); | |
| 1936 } | |
| 1937 | |
| 1938 void reload_document(void) | |
| 1939 { | |
| 1940 save_history(); | |
| 1941 save_accelerator(); | |
| 1942 load_document(); | |
| 1943 if (doc) | |
| 1944 { | |
| 1945 if (reflow_options) | |
| 1946 reflow_document(); | |
| 1947 load_page(); | |
| 1948 update_title(); | |
| 1949 } | |
| 1950 } | |
| 1951 | |
| 1952 static void toggle_outline(void) | |
| 1953 { | |
| 1954 if (outline) | |
| 1955 { | |
| 1956 showoutline = !showoutline; | |
| 1957 showundo = showlayers = 0; | |
| 1958 if (canvas_w == page_tex.w && canvas_h == page_tex.h) | |
| 1959 shrinkwrap(); | |
| 1960 } | |
| 1961 } | |
| 1962 | |
| 1963 static void toggle_undo(void) | |
| 1964 { | |
| 1965 showundo = !showundo; | |
| 1966 showoutline = showlayers = 0; | |
| 1967 if (canvas_w == page_tex.w && canvas_h == page_tex.h) | |
| 1968 shrinkwrap(); | |
| 1969 } | |
| 1970 | |
| 1971 static void toggle_layers(void) | |
| 1972 { | |
| 1973 showlayers = !showlayers; | |
| 1974 showoutline = showundo = 0; | |
| 1975 if (canvas_w == page_tex.w && canvas_h == page_tex.h) | |
| 1976 shrinkwrap(); | |
| 1977 } | |
| 1978 | |
| 1979 void toggle_annotate(int mode) | |
| 1980 { | |
| 1981 if (pdf) | |
| 1982 { | |
| 1983 if (showannotate != mode) | |
| 1984 showannotate = mode; | |
| 1985 else | |
| 1986 showannotate = ANNOTATE_MODE_NONE; | |
| 1987 if (canvas_w == page_tex.w && canvas_h == page_tex.h) | |
| 1988 shrinkwrap(); | |
| 1989 } | |
| 1990 } | |
| 1991 | |
| 1992 static void set_zoom(int z, int cx, int cy) | |
| 1993 { | |
| 1994 z = fz_clamp(z, MINRES, MAXRES); | |
| 1995 scroll_x = (scroll_x + cx - canvas_x) * z / currentzoom - cx + canvas_x; | |
| 1996 scroll_y = (scroll_y + cy - canvas_y) * z / currentzoom - cy + canvas_y; | |
| 1997 currentzoom = z; | |
| 1998 } | |
| 1999 | |
| 2000 static void auto_zoom_w(void) | |
| 2001 { | |
| 2002 currentzoom = fz_clamp(currentzoom * canvas_w / page_tex.w, MINRES, MAXRES); | |
| 2003 } | |
| 2004 | |
| 2005 static void auto_zoom_h(void) | |
| 2006 { | |
| 2007 currentzoom = fz_clamp(currentzoom * canvas_h / page_tex.h, MINRES, MAXRES); | |
| 2008 } | |
| 2009 | |
| 2010 static void auto_zoom(void) | |
| 2011 { | |
| 2012 float page_a = (float) page_tex.w / page_tex.h; | |
| 2013 float screen_a = (float) canvas_w / canvas_h; | |
| 2014 if (page_a > screen_a) | |
| 2015 auto_zoom_w(); | |
| 2016 else | |
| 2017 auto_zoom_h(); | |
| 2018 } | |
| 2019 | |
| 2020 static void smart_move_backward(void) | |
| 2021 { | |
| 2022 int slop_x = page_tex.w / 20; | |
| 2023 int slop_y = page_tex.h / 20; | |
| 2024 if (scroll_y <= slop_y) | |
| 2025 { | |
| 2026 if (scroll_x <= slop_x) | |
| 2027 { | |
| 2028 fz_location prev = fz_previous_page(ctx, doc, currentpage); | |
| 2029 if (!eqloc(currentpage, prev)) | |
| 2030 { | |
| 2031 scroll_x = (page_tex.w <= canvas_w) ? 0 : page_tex.w - canvas_w; | |
| 2032 scroll_y = (page_tex.h <= canvas_h) ? 0 : page_tex.h - canvas_h; | |
| 2033 currentpage = prev; | |
| 2034 } | |
| 2035 } | |
| 2036 else | |
| 2037 { | |
| 2038 scroll_y = page_tex.h; | |
| 2039 scroll_x -= canvas_w * 9 / 10; | |
| 2040 } | |
| 2041 } | |
| 2042 else | |
| 2043 { | |
| 2044 scroll_y -= canvas_h * 9 / 10; | |
| 2045 } | |
| 2046 } | |
| 2047 | |
| 2048 static void smart_move_forward(void) | |
| 2049 { | |
| 2050 int slop_x = page_tex.w / 20; | |
| 2051 int slop_y = page_tex.h / 20; | |
| 2052 if (scroll_y + canvas_h >= page_tex.h - slop_y) | |
| 2053 { | |
| 2054 if (scroll_x + canvas_w >= page_tex.w - slop_x) | |
| 2055 { | |
| 2056 fz_location next = fz_next_page(ctx, doc, currentpage); | |
| 2057 if (!eqloc(currentpage, next)) | |
| 2058 { | |
| 2059 scroll_x = 0; | |
| 2060 scroll_y = 0; | |
| 2061 currentpage = next; | |
| 2062 } | |
| 2063 } | |
| 2064 else | |
| 2065 { | |
| 2066 scroll_y = 0; | |
| 2067 scroll_x += canvas_w * 9 / 10; | |
| 2068 } | |
| 2069 } | |
| 2070 else | |
| 2071 { | |
| 2072 scroll_y += canvas_h * 9 / 10; | |
| 2073 } | |
| 2074 } | |
| 2075 | |
| 2076 static void clear_search(void) | |
| 2077 { | |
| 2078 showsearch = 0; | |
| 2079 search_page = currentpage; | |
| 2080 search_hit_page = fz_make_location(-1, -1); | |
| 2081 search_hit_count = 0; | |
| 2082 } | |
| 2083 | |
| 2084 #if FZ_ENABLE_JS | |
| 2085 | |
| 2086 #define MAX_CONSOLE_LINES 500 | |
| 2087 | |
| 2088 static fz_buffer *console_buffer; | |
| 2089 static int console_scroll = 0; | |
| 2090 static int console_sticky = 1; | |
| 2091 static int console_lines = 0; | |
| 2092 static struct readline console_readline; | |
| 2093 static void (*warning_callback)(void *, const char *) = NULL; | |
| 2094 static void (*error_callback)(void *, const char *) = NULL; | |
| 2095 static void *warning_user = NULL; | |
| 2096 static void *error_user = NULL; | |
| 2097 | |
| 2098 static void | |
| 2099 remove_oldest_console_line() | |
| 2100 { | |
| 2101 unsigned char *s; | |
| 2102 size_t size = fz_buffer_storage(ctx, console_buffer, &s); | |
| 2103 unsigned char *p = s; | |
| 2104 unsigned char *e = s + size; | |
| 2105 | |
| 2106 while (p < e && *p != '\n') | |
| 2107 p++; | |
| 2108 | |
| 2109 if (p < e && *p == '\n') | |
| 2110 { | |
| 2111 p++; | |
| 2112 memmove(s, p, e - p); | |
| 2113 fz_resize_buffer(ctx, console_buffer, e - p); | |
| 2114 console_lines--; | |
| 2115 } | |
| 2116 } | |
| 2117 | |
| 2118 static void | |
| 2119 gl_js_console_write(void *user, const char *message) | |
| 2120 { | |
| 2121 const char *p = NULL; | |
| 2122 | |
| 2123 if (message == NULL) | |
| 2124 return; | |
| 2125 | |
| 2126 p = message; | |
| 2127 while (*p) | |
| 2128 { | |
| 2129 if (*p == '\n') | |
| 2130 console_lines++; | |
| 2131 if (console_lines >= MAX_CONSOLE_LINES) | |
| 2132 remove_oldest_console_line(); | |
| 2133 if (*p) | |
| 2134 fz_append_byte(ctx, console_buffer, *p); | |
| 2135 p++; | |
| 2136 } | |
| 2137 } | |
| 2138 | |
| 2139 static void | |
| 2140 gl_js_console_show(void *user) | |
| 2141 { | |
| 2142 if (showconsole) | |
| 2143 return; | |
| 2144 | |
| 2145 showconsole = 1; | |
| 2146 if (canvas_w == page_tex.w && canvas_h == page_tex.h) | |
| 2147 shrinkwrap(); | |
| 2148 ui.focus = &console_readline; | |
| 2149 } | |
| 2150 | |
| 2151 static void | |
| 2152 gl_js_console_hide(void *user) | |
| 2153 { | |
| 2154 if (!showconsole) | |
| 2155 return; | |
| 2156 | |
| 2157 showconsole = 0; | |
| 2158 if (canvas_w == page_tex.w && canvas_h == page_tex.h) | |
| 2159 shrinkwrap(); | |
| 2160 ui.focus = NULL; | |
| 2161 } | |
| 2162 | |
| 2163 static void | |
| 2164 gl_js_console_clear(void *user) | |
| 2165 { | |
| 2166 fz_resize_buffer(ctx, console_buffer, 0); | |
| 2167 console_lines = 0; | |
| 2168 } | |
| 2169 | |
| 2170 static void console_warn(void *user, const char *message) | |
| 2171 { | |
| 2172 gl_js_console_write(ctx, "\nwarning: "); | |
| 2173 gl_js_console_write(ctx, message); | |
| 2174 if (warning_callback) | |
| 2175 warning_callback(warning_user, message); | |
| 2176 } | |
| 2177 | |
| 2178 static void console_err(void *user, const char *message) | |
| 2179 { | |
| 2180 gl_js_console_write(ctx, "\nerror: "); | |
| 2181 gl_js_console_write(ctx, message); | |
| 2182 if (error_callback) | |
| 2183 error_callback(error_user, message); | |
| 2184 } | |
| 2185 | |
| 2186 static void console_init(void) | |
| 2187 { | |
| 2188 ui_readline_init(&console_readline, NULL); | |
| 2189 | |
| 2190 console_buffer = fz_new_buffer(ctx, 0); | |
| 2191 fz_append_printf(ctx, console_buffer, "Welcome to MuPDF %s with MuJS %d.%d.%d", | |
| 2192 FZ_VERSION, | |
| 2193 JS_VERSION_MAJOR, JS_VERSION_MINOR, JS_VERSION_PATCH); | |
| 2194 | |
| 2195 warning_callback = fz_warning_callback(ctx, &warning_user); | |
| 2196 fz_set_warning_callback(ctx, console_warn, NULL); | |
| 2197 error_callback = fz_error_callback(ctx, &error_user); | |
| 2198 fz_set_error_callback(ctx, console_err, NULL); | |
| 2199 } | |
| 2200 | |
| 2201 static void console_fin(void) | |
| 2202 { | |
| 2203 fz_set_warning_callback(ctx, warning_callback, warning_user); | |
| 2204 fz_set_error_callback(ctx, error_callback, error_user); | |
| 2205 fz_drop_buffer(ctx, console_buffer); | |
| 2206 console_buffer = NULL; | |
| 2207 } | |
| 2208 | |
| 2209 static pdf_js_console gl_js_console = { | |
| 2210 NULL, | |
| 2211 gl_js_console_show, | |
| 2212 gl_js_console_hide, | |
| 2213 gl_js_console_clear, | |
| 2214 gl_js_console_write, | |
| 2215 }; | |
| 2216 | |
| 2217 static void toggle_console(void) | |
| 2218 { | |
| 2219 showconsole = !showconsole; | |
| 2220 if (showconsole) | |
| 2221 ui.focus = &console_readline; | |
| 2222 if (canvas_w == page_tex.w && canvas_h == page_tex.h) | |
| 2223 shrinkwrap(); | |
| 2224 } | |
| 2225 | |
| 2226 void do_console(void) | |
| 2227 { | |
| 2228 pdf_js_console *console = pdf_js_get_console(ctx, pdf); | |
| 2229 char *result = NULL; | |
| 2230 const char *accepted = NULL; | |
| 2231 | |
| 2232 fz_var(result); | |
| 2233 | |
| 2234 ui_layout(B, BOTH, NW, 0, 0); | |
| 2235 ui_panel_begin(canvas_w, console_h, ui.padsize, ui.padsize, 1); | |
| 2236 | |
| 2237 ui_layout(B, X, NW, 0, 0); | |
| 2238 | |
| 2239 accepted = ui_readline(&console_readline, 0); | |
| 2240 if (accepted != NULL) | |
| 2241 { | |
| 2242 ui.focus = &console_readline; | |
| 2243 if (console_readline.input.text[0]) | |
| 2244 { | |
| 2245 fz_try(ctx) | |
| 2246 { | |
| 2247 if (console && console->write) | |
| 2248 { | |
| 2249 console->write(ctx, "\n> "); | |
| 2250 console->write(ctx, console_readline.input.text); | |
| 2251 } | |
| 2252 pdf_js_execute(pdf ? pdf->js : NULL, "console", console_readline.input.text, &result); | |
| 2253 if (result && console && console->write) | |
| 2254 { | |
| 2255 console->write(ctx, "\n"); | |
| 2256 console->write(ctx, result); | |
| 2257 } | |
| 2258 } | |
| 2259 fz_always(ctx) | |
| 2260 fz_free(ctx, result); | |
| 2261 fz_catch(ctx) | |
| 2262 { | |
| 2263 if (console) | |
| 2264 { | |
| 2265 console->write(ctx, "\nError: "); | |
| 2266 console->write(ctx, fz_caught_message(ctx)); | |
| 2267 fz_report_error(ctx); | |
| 2268 } | |
| 2269 } | |
| 2270 fz_flush_warnings(ctx); | |
| 2271 ui_input_init(&console_readline.input, ""); | |
| 2272 } | |
| 2273 } | |
| 2274 | |
| 2275 ui_layout(ALL, BOTH, NW, ui.padsize, ui.padsize); | |
| 2276 | |
| 2277 // White background! | |
| 2278 glColorHex(0xF5F5F5); | |
| 2279 glRectf(ui.cavity->x0, ui.cavity->y0, ui.cavity->x1, ui.cavity->y1); | |
| 2280 | |
| 2281 char *console_string = (char *) fz_string_from_buffer(ctx, console_buffer); | |
| 2282 ui_label_with_scrollbar(console_string, 0, 10, &console_scroll, &console_sticky); | |
| 2283 | |
| 2284 ui_panel_end(); | |
| 2285 } | |
| 2286 | |
| 2287 #endif | |
| 2288 | |
| 2289 static void do_app(void) | |
| 2290 { | |
| 2291 if (ui.mod == GLUT_ACTIVE_ALT) | |
| 2292 { | |
| 2293 if (ui.key == KEY_F4) | |
| 2294 quit(); | |
| 2295 | |
| 2296 if (ui.key == KEY_LEFT) | |
| 2297 ui.key = 't', ui.mod = 0, ui.plain = 1; | |
| 2298 if (ui.key == KEY_RIGHT) | |
| 2299 ui.key = 'T', ui.mod = 0, ui.plain = 1; | |
| 2300 } | |
| 2301 | |
| 2302 if (trace_file && ui.key == KEY_CTL_P) | |
| 2303 trace_save_snapshot(); | |
| 2304 | |
| 2305 if (!ui.focus && ui.key && ui.plain) | |
| 2306 { | |
| 2307 switch (ui.key) | |
| 2308 { | |
| 2309 case KEY_ESCAPE: clear_search(); ui_select_annot(NULL); break; | |
| 2310 case KEY_F1: ui.dialog = help_dialog; break; | |
| 2311 case 'a': toggle_annotate(ANNOTATE_MODE_NORMAL); break; | |
| 2312 case 'R': toggle_annotate(ANNOTATE_MODE_REDACT); break; | |
| 2313 case 'o': toggle_outline(); break; | |
| 2314 case 'u': toggle_undo(); break; | |
| 2315 case 'Y': toggle_layers(); break; | |
| 2316 case 'L': showlinks = !showlinks; break; | |
| 2317 case 'F': showform = !showform; break; | |
| 2318 case 'i': ui.dialog = info_dialog; break; | |
| 2319 #if FZ_ENABLE_JS | |
| 2320 case '`': case KEY_F12: toggle_console(); break; | |
| 2321 #endif | |
| 2322 case 'r': reload(); break; | |
| 2323 case 'q': quit(); break; | |
| 2324 case 'S': do_save_pdf_file(); break; | |
| 2325 | |
| 2326 case '>': layout_em = number > 0 ? number : layout_em + 1; relayout(); break; | |
| 2327 case '<': layout_em = number > 0 ? number : layout_em - 1; relayout(); break; | |
| 2328 | |
| 2329 case 'C': currenttint = !currenttint; break; | |
| 2330 case 'I': currentinvert = !currentinvert; break; | |
| 2331 case 'e': currentseparations = !currentseparations; break; | |
| 2332 case 'E': currenticc = !currenticc; break; | |
| 2333 case 'f': toggle_fullscreen(); break; | |
| 2334 case 'w': shrinkwrap(); break; | |
| 2335 case 'W': auto_zoom_w(); break; | |
| 2336 case 'H': auto_zoom_h(); break; | |
| 2337 case 'Z': auto_zoom(); break; | |
| 2338 case 'z': set_zoom(number > 0 ? number : DEFRES, canvas_w/2, canvas_h/2); break; | |
| 2339 case '+': set_zoom(zoom_in(currentzoom), ui.x, ui.y); break; | |
| 2340 case '-': set_zoom(zoom_out(currentzoom), ui.x, ui.y); break; | |
| 2341 case '[': currentrotate -= 90; break; | |
| 2342 case ']': currentrotate += 90; break; | |
| 2343 case 'k': case KEY_UP: scroll_y -= canvas_h/10; break; | |
| 2344 case 'j': case KEY_DOWN: scroll_y += canvas_h/10; break; | |
| 2345 case 'h': case KEY_LEFT: scroll_x -= canvas_w/10; break; | |
| 2346 case 'l': case KEY_RIGHT: scroll_x += canvas_w/10; break; | |
| 2347 | |
| 2348 case 'b': number = fz_maxi(number, 1); while (number--) smart_move_backward(); break; | |
| 2349 case ' ': number = fz_maxi(number, 1); while (number--) smart_move_forward(); break; | |
| 2350 case 'g': jump_to_page(number - 1); break; | |
| 2351 case 'G': jump_to_location(fz_last_page(ctx, doc)); break; | |
| 2352 | |
| 2353 case ',': case KEY_PAGE_UP: | |
| 2354 number = fz_maxi(number, 1); | |
| 2355 while (number--) | |
| 2356 currentpage = fz_previous_page(ctx, doc, currentpage); | |
| 2357 break; | |
| 2358 case '.': case KEY_PAGE_DOWN: | |
| 2359 number = fz_maxi(number, 1); | |
| 2360 while (number--) | |
| 2361 currentpage = fz_next_page(ctx, doc, currentpage); | |
| 2362 break; | |
| 2363 | |
| 2364 case 'A': | |
| 2365 if (number == 0) | |
| 2366 currentaa = (currentaa == 8 ? 0 : 8); | |
| 2367 else | |
| 2368 currentaa = number; | |
| 2369 break; | |
| 2370 | |
| 2371 case 'B': | |
| 2372 currentbox += 1; | |
| 2373 if (currentbox >= FZ_UNKNOWN_BOX) | |
| 2374 currentbox = FZ_MEDIA_BOX; | |
| 2375 break; | |
| 2376 | |
| 2377 case 'm': | |
| 2378 if (number == 0) | |
| 2379 push_history(); | |
| 2380 else if (number > 0 && number < (int)nelem(marks)) | |
| 2381 marks[number] = save_mark(); | |
| 2382 break; | |
| 2383 case 't': | |
| 2384 if (number == 0) | |
| 2385 { | |
| 2386 if (history_count > 0) | |
| 2387 pop_history(); | |
| 2388 } | |
| 2389 else if (number > 0 && number < (int)nelem(marks)) | |
| 2390 { | |
| 2391 struct mark mark = marks[number]; | |
| 2392 restore_mark(mark); | |
| 2393 jump_to_location(mark.loc); | |
| 2394 } | |
| 2395 break; | |
| 2396 case 'T': | |
| 2397 if (number == 0) | |
| 2398 { | |
| 2399 if (future_count > 0) | |
| 2400 pop_future(); | |
| 2401 } | |
| 2402 break; | |
| 2403 | |
| 2404 case '/': | |
| 2405 clear_search(); | |
| 2406 search_dir = 1; | |
| 2407 showsearch = 1; | |
| 2408 ui.focus = &search_input; | |
| 2409 search_input.p = search_input.text; | |
| 2410 search_input.q = search_input.end; | |
| 2411 break; | |
| 2412 case '?': | |
| 2413 clear_search(); | |
| 2414 search_dir = -1; | |
| 2415 showsearch = 1; | |
| 2416 ui.focus = &search_input; | |
| 2417 search_input.p = search_input.text; | |
| 2418 search_input.q = search_input.end; | |
| 2419 break; | |
| 2420 case 'N': | |
| 2421 search_dir = -1; | |
| 2422 search_active = !!search_needle; | |
| 2423 if (eqloc(search_hit_page, currentpage)) | |
| 2424 { | |
| 2425 if (is_first_page(search_page)) | |
| 2426 search_active = 0; | |
| 2427 else | |
| 2428 search_page = fz_previous_page(ctx, doc, currentpage); | |
| 2429 } | |
| 2430 else | |
| 2431 { | |
| 2432 search_page = currentpage; | |
| 2433 } | |
| 2434 search_hit_page = fz_make_location(-1, -1); | |
| 2435 break; | |
| 2436 case 'n': | |
| 2437 search_dir = 1; | |
| 2438 search_active = !!search_needle; | |
| 2439 if (eqloc(search_hit_page, currentpage)) | |
| 2440 { | |
| 2441 if (is_last_page(search_page)) | |
| 2442 search_active = 0; | |
| 2443 else | |
| 2444 search_page = fz_next_page(ctx, doc, currentpage); | |
| 2445 } | |
| 2446 else | |
| 2447 { | |
| 2448 search_page = currentpage; | |
| 2449 } | |
| 2450 search_hit_page = fz_make_location(-1, -1); | |
| 2451 break; | |
| 2452 default: | |
| 2453 if (ui.key < '0' || ui.key > '9') | |
| 2454 { | |
| 2455 number = 0; | |
| 2456 return; /* unrecognized key, pass it through */ | |
| 2457 } | |
| 2458 } | |
| 2459 | |
| 2460 if (ui.key >= '0' && ui.key <= '9') | |
| 2461 number = number * 10 + ui.key - '0'; | |
| 2462 else | |
| 2463 number = 0; | |
| 2464 | |
| 2465 currentpage = fz_clamp_location(ctx, doc, currentpage); | |
| 2466 while (currentrotate < 0) currentrotate += 360; | |
| 2467 while (currentrotate >= 360) currentrotate -= 360; | |
| 2468 | |
| 2469 if (!eqloc(search_hit_page, currentpage)) | |
| 2470 search_hit_page = fz_make_location(-1, -1); /* clear highlights when navigating */ | |
| 2471 | |
| 2472 ui.key = 0; /* we ate the key event, so zap it */ | |
| 2473 } | |
| 2474 } | |
| 2475 | |
| 2476 typedef struct | |
| 2477 { | |
| 2478 int max; | |
| 2479 int len; | |
| 2480 pdf_obj **sig; | |
| 2481 } sigs_list; | |
| 2482 | |
| 2483 static void | |
| 2484 process_sigs(fz_context *ctx_, pdf_obj *field, void *arg, pdf_obj **ft) | |
| 2485 { | |
| 2486 sigs_list *sigs = (sigs_list *)arg; | |
| 2487 | |
| 2488 if (!pdf_name_eq(ctx, pdf_dict_get(ctx, field, PDF_NAME(Type)), PDF_NAME(Annot)) || | |
| 2489 !pdf_name_eq(ctx, pdf_dict_get(ctx, field, PDF_NAME(Subtype)), PDF_NAME(Widget)) || | |
| 2490 !pdf_name_eq(ctx, *ft, PDF_NAME(Sig))) | |
| 2491 return; | |
| 2492 | |
| 2493 if (sigs->len == sigs->max) | |
| 2494 { | |
| 2495 int newsize = sigs->max * 2; | |
| 2496 if (newsize == 0) | |
| 2497 newsize = 4; | |
| 2498 sigs->sig = fz_realloc_array(ctx, sigs->sig, newsize, pdf_obj *); | |
| 2499 sigs->max = newsize; | |
| 2500 } | |
| 2501 | |
| 2502 sigs->sig[sigs->len++] = field; | |
| 2503 } | |
| 2504 | |
| 2505 static char *short_signature_error_desc(pdf_signature_error err) | |
| 2506 { | |
| 2507 switch (err) | |
| 2508 { | |
| 2509 case PDF_SIGNATURE_ERROR_OKAY: | |
| 2510 return "OK"; | |
| 2511 case PDF_SIGNATURE_ERROR_NO_SIGNATURES: | |
| 2512 return "No signatures"; | |
| 2513 case PDF_SIGNATURE_ERROR_NO_CERTIFICATE: | |
| 2514 return "No certificate"; | |
| 2515 case PDF_SIGNATURE_ERROR_DIGEST_FAILURE: | |
| 2516 return "Invalid"; | |
| 2517 case PDF_SIGNATURE_ERROR_SELF_SIGNED: | |
| 2518 return "Self-signed"; | |
| 2519 case PDF_SIGNATURE_ERROR_SELF_SIGNED_IN_CHAIN: | |
| 2520 return "Self-signed in chain"; | |
| 2521 case PDF_SIGNATURE_ERROR_NOT_TRUSTED: | |
| 2522 return "Untrusted"; | |
| 2523 case PDF_SIGNATURE_ERROR_NOT_SIGNED: | |
| 2524 return "Not signed"; | |
| 2525 default: | |
| 2526 case PDF_SIGNATURE_ERROR_UNKNOWN: | |
| 2527 return "Unknown error"; | |
| 2528 } | |
| 2529 } | |
| 2530 | |
| 2531 const char *format_date(int64_t secs64) | |
| 2532 { | |
| 2533 static char buf[100]; | |
| 2534 #ifdef _POSIX_SOURCE | |
| 2535 struct tm tmbuf, *tm; | |
| 2536 #else | |
| 2537 struct tm *tm; | |
| 2538 #endif | |
| 2539 time_t secs = (time_t)secs64; | |
| 2540 | |
| 2541 if (secs <= 0) | |
| 2542 return NULL; | |
| 2543 | |
| 2544 #ifdef _POSIX_SOURCE | |
| 2545 tm = gmtime_r(&secs, &tmbuf); | |
| 2546 #else | |
| 2547 tm = gmtime(&secs); | |
| 2548 #endif | |
| 2549 if (!tm) | |
| 2550 return NULL; | |
| 2551 | |
| 2552 strftime(buf, sizeof buf, "%Y-%m-%d %H:%M UTC", tm); | |
| 2553 return buf; | |
| 2554 } | |
| 2555 | |
| 2556 static fz_buffer *format_info_text() | |
| 2557 { | |
| 2558 fz_buffer *out = fz_new_buffer(ctx, 4096); | |
| 2559 pdf_document *pdoc = pdf_specifics(ctx, doc); | |
| 2560 sigs_list list = { 0, 0, NULL }; | |
| 2561 char buf[100]; | |
| 2562 | |
| 2563 if (pdoc) | |
| 2564 { | |
| 2565 static pdf_obj *ft_list[2] = { PDF_NAME(FT), NULL }; | |
| 2566 pdf_obj *ft = NULL; | |
| 2567 pdf_obj *form_fields = pdf_dict_getp(ctx, pdf_trailer(ctx, pdoc), "Root/AcroForm/Fields"); | |
| 2568 pdf_walk_tree(ctx, form_fields, PDF_NAME(Kids), process_sigs, NULL, &list, &ft_list[0], &ft); | |
| 2569 } | |
| 2570 | |
| 2571 fz_append_printf(ctx, out, "File: %s\n\n", filename); | |
| 2572 | |
| 2573 if (fz_lookup_metadata(ctx, doc, FZ_META_INFO_TITLE, buf, sizeof buf) > 0) | |
| 2574 fz_append_printf(ctx, out, "Title: %s\n", buf); | |
| 2575 if (fz_lookup_metadata(ctx, doc, FZ_META_INFO_AUTHOR, buf, sizeof buf) > 0) | |
| 2576 fz_append_printf(ctx, out, "Author: %s\n", buf); | |
| 2577 if (fz_lookup_metadata(ctx, doc, FZ_META_FORMAT, buf, sizeof buf) > 0) | |
| 2578 fz_append_printf(ctx, out, "Format: %s\n", buf); | |
| 2579 if (fz_lookup_metadata(ctx, doc, FZ_META_ENCRYPTION, buf, sizeof buf) > 0) | |
| 2580 fz_append_printf(ctx, out, "Encryption: %s\n", buf); | |
| 2581 | |
| 2582 fz_append_string(ctx, out, "\n"); | |
| 2583 | |
| 2584 if (pdoc) | |
| 2585 { | |
| 2586 int updates = pdf_count_versions(ctx, pdoc); | |
| 2587 | |
| 2588 if (fz_lookup_metadata(ctx, doc, FZ_META_INFO_CREATOR, buf, sizeof buf) > 0) | |
| 2589 fz_append_printf(ctx, out, "PDF Creator: %s\n", buf); | |
| 2590 if (fz_lookup_metadata(ctx, doc, FZ_META_INFO_PRODUCER, buf, sizeof buf) > 0) | |
| 2591 fz_append_printf(ctx, out, "PDF Producer: %s\n", buf); | |
| 2592 if (fz_lookup_metadata(ctx, doc, FZ_META_INFO_SUBJECT, buf, sizeof buf) > 0) | |
| 2593 fz_append_printf(ctx, out, "Subject: %s\n", buf); | |
| 2594 if (fz_lookup_metadata(ctx, doc, FZ_META_INFO_KEYWORDS, buf, sizeof buf) > 0) | |
| 2595 fz_append_printf(ctx, out, "Keywords: %s\n", buf); | |
| 2596 if (fz_lookup_metadata(ctx, doc, FZ_META_INFO_CREATIONDATE, buf, sizeof buf) > 0) | |
| 2597 { | |
| 2598 const char *s = format_date(pdf_parse_date(ctx, buf)); | |
| 2599 if (s) | |
| 2600 fz_append_printf(ctx, out, "Creation date: %s\n", s); | |
| 2601 } | |
| 2602 if (fz_lookup_metadata(ctx, doc, FZ_META_INFO_MODIFICATIONDATE, buf, sizeof buf) > 0) | |
| 2603 { | |
| 2604 const char *s = format_date(pdf_parse_date(ctx, buf)); | |
| 2605 if (s) | |
| 2606 fz_append_printf(ctx, out, "Modification date: %s\n", s); | |
| 2607 } | |
| 2608 | |
| 2609 buf[0] = 0; | |
| 2610 if (fz_has_permission(ctx, doc, FZ_PERMISSION_PRINT)) | |
| 2611 fz_strlcat(buf, "print, ", sizeof buf); | |
| 2612 if (fz_has_permission(ctx, doc, FZ_PERMISSION_COPY)) | |
| 2613 fz_strlcat(buf, "copy, ", sizeof buf); | |
| 2614 if (fz_has_permission(ctx, doc, FZ_PERMISSION_EDIT)) | |
| 2615 fz_strlcat(buf, "edit, ", sizeof buf); | |
| 2616 if (fz_has_permission(ctx, doc, FZ_PERMISSION_ANNOTATE)) | |
| 2617 fz_strlcat(buf, "annotate, ", sizeof buf); | |
| 2618 if (fz_has_permission(ctx, doc, FZ_PERMISSION_FORM)) | |
| 2619 fz_strlcat(buf, "form, ", sizeof buf); | |
| 2620 if (fz_has_permission(ctx, doc, FZ_PERMISSION_ACCESSIBILITY)) | |
| 2621 fz_strlcat(buf, "accessibility, ", sizeof buf); | |
| 2622 if (fz_has_permission(ctx, doc, FZ_PERMISSION_ASSEMBLE)) | |
| 2623 fz_strlcat(buf, "assemble, ", sizeof buf); | |
| 2624 if (fz_has_permission(ctx, doc, FZ_PERMISSION_PRINT_HQ)) | |
| 2625 fz_strlcat(buf, "print-hq, ", sizeof buf); | |
| 2626 if (strlen(buf) > 2) | |
| 2627 buf[strlen(buf)-2] = 0; | |
| 2628 else | |
| 2629 fz_strlcat(buf, "none", sizeof buf); | |
| 2630 fz_append_printf(ctx, out, "Permissions: %s\n", buf); | |
| 2631 | |
| 2632 fz_append_printf(ctx, out, "PDF %sdocument with %d update%s\n", | |
| 2633 pdf_doc_was_linearized(ctx, pdoc) ? "linearized " : "", | |
| 2634 updates, updates > 1 ? "s" : ""); | |
| 2635 if (updates > 0) | |
| 2636 { | |
| 2637 int n = pdf_validate_change_history(ctx, pdoc); | |
| 2638 if (n == 0) | |
| 2639 fz_append_printf(ctx, out, "Change history seems valid.\n"); | |
| 2640 else if (n == 1) | |
| 2641 fz_append_printf(ctx, out, "Invalid changes made to the document in the last update.\n"); | |
| 2642 else if (n == 2) | |
| 2643 fz_append_printf(ctx, out, "Invalid changes made to the document in the penultimate update.\n"); | |
| 2644 else | |
| 2645 fz_append_printf(ctx, out, "Invalid changes made to the document %d updates ago.\n", n); | |
| 2646 } | |
| 2647 | |
| 2648 if (list.len) | |
| 2649 { | |
| 2650 int i; | |
| 2651 for (i = 0; i < list.len; i++) | |
| 2652 { | |
| 2653 pdf_obj *field = list.sig[i]; | |
| 2654 fz_try(ctx) | |
| 2655 { | |
| 2656 if (pdf_signature_is_signed(ctx, pdf, field)) | |
| 2657 { | |
| 2658 pdf_pkcs7_verifier *verifier = pkcs7_openssl_new_verifier(ctx); | |
| 2659 pdf_signature_error sig_cert_error = pdf_check_certificate(ctx, verifier, pdf, field); | |
| 2660 pdf_signature_error sig_digest_error = pdf_check_digest(ctx, verifier, pdf, field); | |
| 2661 fz_append_printf(ctx, out, "Signature %d: CERT: %s, DIGEST: %s%s\n", i+1, | |
| 2662 short_signature_error_desc(sig_cert_error), | |
| 2663 short_signature_error_desc(sig_digest_error), | |
| 2664 pdf_signature_incremental_change_since_signing(ctx, pdf, field) ? ", Changed since": ""); | |
| 2665 pdf_drop_verifier(ctx, verifier); | |
| 2666 } | |
| 2667 else | |
| 2668 fz_append_printf(ctx, out, "Signature %d: Unsigned\n", i+1); | |
| 2669 } | |
| 2670 fz_catch(ctx) | |
| 2671 fz_append_printf(ctx, out, "Signature %d: Error\n", i+1); | |
| 2672 } | |
| 2673 fz_free(ctx, list.sig); | |
| 2674 | |
| 2675 if (updates == 0) | |
| 2676 fz_append_printf(ctx, out, "No updates since document creation\n"); | |
| 2677 else | |
| 2678 { | |
| 2679 int n = pdf_validate_change_history(ctx, pdf); | |
| 2680 if (n == 0) | |
| 2681 fz_append_printf(ctx, out, "Document changes conform to permissions\n"); | |
| 2682 else | |
| 2683 fz_append_printf(ctx, out, "Document permissions violated %d updates ago\n", n); | |
| 2684 } | |
| 2685 } | |
| 2686 | |
| 2687 fz_append_string(ctx, out, "\n"); | |
| 2688 } | |
| 2689 | |
| 2690 fz_append_printf(ctx, out, "Page: %d / %d\n", fz_page_number_from_location(ctx, doc, currentpage)+1, fz_count_pages(ctx, doc)); | |
| 2691 fz_append_printf(ctx, out, "Page Label: %s\n", fz_page_label(ctx, fzpage, buf, sizeof buf)); | |
| 2692 { | |
| 2693 int w = (int)(page_bounds.x1 - page_bounds.x0 + 0.5f); | |
| 2694 int h = (int)(page_bounds.y1 - page_bounds.y0 + 0.5f); | |
| 2695 const char *size = paper_size_name(w, h); | |
| 2696 if (!size) | |
| 2697 size = paper_size_name(h, w); | |
| 2698 if (size) | |
| 2699 fz_append_printf(ctx, out, "Size: %d x %d (%s - %s)\n", w, h, fz_string_from_box_type(currentbox), size); | |
| 2700 else | |
| 2701 fz_append_printf(ctx, out, "Size: %d x %d (%s)\n", w, h, fz_string_from_box_type(currentbox)); | |
| 2702 } | |
| 2703 fz_append_printf(ctx, out, "ICC rendering: %s.\n", currenticc ? "on" : "off"); | |
| 2704 fz_append_printf(ctx, out, "Spot rendering: %s.\n", currentseparations ? "on" : "off"); | |
| 2705 | |
| 2706 if (fz_is_document_reflowable(ctx, doc)) | |
| 2707 fz_append_printf(ctx, out, "Em size: %g\n", layout_em); | |
| 2708 | |
| 2709 return out; | |
| 2710 } | |
| 2711 | |
| 2712 static void do_canvas(void) | |
| 2713 { | |
| 2714 static int saved_scroll_x = 0; | |
| 2715 static int saved_scroll_y = 0; | |
| 2716 static int saved_ui_x = 0; | |
| 2717 static int saved_ui_y = 0; | |
| 2718 fz_irect area; | |
| 2719 int page_x, page_y; | |
| 2720 | |
| 2721 tooltip = NULL; | |
| 2722 | |
| 2723 ui_layout(ALL, BOTH, NW, 0, 0); | |
| 2724 ui_pack_push(area = ui_pack(0, 0)); | |
| 2725 glScissor(area.x0, ui.window_h-area.y1, area.x1-area.x0, area.y1-area.y0); | |
| 2726 glEnable(GL_SCISSOR_TEST); | |
| 2727 | |
| 2728 canvas_x = area.x0; | |
| 2729 canvas_y = area.y0; | |
| 2730 canvas_w = area.x1 - area.x0; | |
| 2731 canvas_h = area.y1 - area.y0; | |
| 2732 | |
| 2733 if (ui_mouse_inside(area)) | |
| 2734 { | |
| 2735 ui.hot = doc; | |
| 2736 if (!ui.active && ui.middle) | |
| 2737 { | |
| 2738 ui.active = doc; | |
| 2739 saved_scroll_x = scroll_x; | |
| 2740 saved_scroll_y = scroll_y; | |
| 2741 saved_ui_x = ui.x; | |
| 2742 saved_ui_y = ui.y; | |
| 2743 } | |
| 2744 } | |
| 2745 | |
| 2746 if (ui.hot == doc) | |
| 2747 { | |
| 2748 if (ui.mod == 0) | |
| 2749 { | |
| 2750 scroll_x -= ui.scroll_x * ui.lineheight * 3; | |
| 2751 scroll_y -= ui.scroll_y * ui.lineheight * 3; | |
| 2752 } | |
| 2753 else if (ui.mod == GLUT_ACTIVE_CTRL) | |
| 2754 { | |
| 2755 if (ui.scroll_y > 0) set_zoom(zoom_in(currentzoom), ui.x, ui.y); | |
| 2756 if (ui.scroll_y < 0) set_zoom(zoom_out(currentzoom), ui.x, ui.y); | |
| 2757 } | |
| 2758 } | |
| 2759 | |
| 2760 render_page_if_changed(); | |
| 2761 | |
| 2762 if (ui.active == doc) | |
| 2763 { | |
| 2764 scroll_x = saved_scroll_x + saved_ui_x - ui.x; | |
| 2765 scroll_y = saved_scroll_y + saved_ui_y - ui.y; | |
| 2766 } | |
| 2767 | |
| 2768 if (page_tex.w <= canvas_w) | |
| 2769 { | |
| 2770 scroll_x = 0; | |
| 2771 page_x = canvas_x + (canvas_w - page_tex.w) / 2; | |
| 2772 } | |
| 2773 else | |
| 2774 { | |
| 2775 scroll_x = fz_clamp(scroll_x, 0, page_tex.w - canvas_w); | |
| 2776 page_x = canvas_x - scroll_x; | |
| 2777 } | |
| 2778 | |
| 2779 if (page_tex.h <= canvas_h) | |
| 2780 { | |
| 2781 scroll_y = 0; | |
| 2782 page_y = canvas_y + (canvas_h - page_tex.h) / 2; | |
| 2783 } | |
| 2784 else | |
| 2785 { | |
| 2786 scroll_y = fz_clamp(scroll_y, 0, page_tex.h - canvas_h); | |
| 2787 page_y = canvas_y - scroll_y; | |
| 2788 } | |
| 2789 | |
| 2790 view_page_ctm = draw_page_ctm; | |
| 2791 view_page_ctm.e += page_x; | |
| 2792 view_page_ctm.f += page_y; | |
| 2793 view_page_inv_ctm = fz_invert_matrix(view_page_ctm); | |
| 2794 view_page_bounds = fz_transform_rect(page_bounds, view_page_ctm); | |
| 2795 view_page_area = fz_irect_from_rect(view_page_bounds); | |
| 2796 | |
| 2797 ui_draw_image(&page_tex, page_x, page_y); | |
| 2798 | |
| 2799 if (search_active) | |
| 2800 { | |
| 2801 int chapters = fz_count_chapters(ctx, doc); | |
| 2802 ui_layout(T, X, NW, 0, 0); | |
| 2803 ui_panel_begin(0, ui.gridsize + ui.padsize*4, ui.padsize*2, ui.padsize*2, 1); | |
| 2804 ui_layout(L, NONE, W, ui.padsize, 0); | |
| 2805 if (chapters == 1 && search_page.chapter == 0) | |
| 2806 ui_label("Searching page %d...", search_page.page); | |
| 2807 else | |
| 2808 ui_label("Searching chapter %d page %d...", search_page.chapter, search_page.page); | |
| 2809 ui_panel_end(); | |
| 2810 } | |
| 2811 else | |
| 2812 { | |
| 2813 if (pdf) | |
| 2814 { | |
| 2815 do_annotate_canvas(area); | |
| 2816 do_widget_canvas(area); | |
| 2817 } | |
| 2818 do_links(links); | |
| 2819 do_page_selection(); | |
| 2820 | |
| 2821 if (eqloc(search_hit_page, currentpage) && search_hit_count > 0) | |
| 2822 do_search_hits(); | |
| 2823 } | |
| 2824 | |
| 2825 if (showsearch) | |
| 2826 { | |
| 2827 ui_layout(T, X, NW, 0, 0); | |
| 2828 ui_panel_begin(0, ui.gridsize + ui.padsize*4, ui.padsize*2, ui.padsize*2, 1); | |
| 2829 ui_layout(L, NONE, W, ui.padsize, 0); | |
| 2830 ui_label("Search:"); | |
| 2831 ui_layout(ALL, X, E, ui.padsize, 0); | |
| 2832 if (ui_input(&search_input, 0, 1) == UI_INPUT_ACCEPT) | |
| 2833 { | |
| 2834 showsearch = 0; | |
| 2835 search_page = fz_make_location(-1, -1); | |
| 2836 if (search_needle) | |
| 2837 { | |
| 2838 fz_free(ctx, search_needle); | |
| 2839 search_needle = NULL; | |
| 2840 } | |
| 2841 if (search_input.end > search_input.text) | |
| 2842 { | |
| 2843 search_needle = fz_strdup(ctx, search_input.text); | |
| 2844 search_active = 1; | |
| 2845 search_page = currentpage; | |
| 2846 } | |
| 2847 } | |
| 2848 if (ui.focus != &search_input) | |
| 2849 showsearch = 0; | |
| 2850 ui_panel_end(); | |
| 2851 } | |
| 2852 | |
| 2853 if (tooltip) | |
| 2854 { | |
| 2855 ui_layout(B, X, N, 0, 0); | |
| 2856 ui_panel_begin(0, ui.gridsize, ui.padsize*2, ui.padsize*2, 1); | |
| 2857 ui_layout(L, NONE, W, ui.padsize, 0); | |
| 2858 ui_label("%s", tooltip); | |
| 2859 ui_panel_end(); | |
| 2860 } | |
| 2861 | |
| 2862 ui_pack_pop(); | |
| 2863 glDisable(GL_SCISSOR_TEST); | |
| 2864 } | |
| 2865 | |
| 2866 void do_main(void) | |
| 2867 { | |
| 2868 if (search_active) | |
| 2869 { | |
| 2870 int start_time = glutGet(GLUT_ELAPSED_TIME); | |
| 2871 | |
| 2872 if (ui.key == KEY_ESCAPE) | |
| 2873 search_active = 0; | |
| 2874 | |
| 2875 /* ignore events during search */ | |
| 2876 ui.key = ui.mod = ui.plain = 0; | |
| 2877 ui.down = ui.middle = ui.right = 0; | |
| 2878 | |
| 2879 while (search_active && glutGet(GLUT_ELAPSED_TIME) < start_time + 200) | |
| 2880 { | |
| 2881 search_hit_count = fz_search_chapter_page_number(ctx, doc, | |
| 2882 search_page.chapter, search_page.page, | |
| 2883 search_needle, | |
| 2884 NULL, search_hit_quads, nelem(search_hit_quads)); | |
| 2885 trace_action("hits = doc.loadPage(%d).search(%q);\n", fz_page_number_from_location(ctx, doc, search_page), search_needle); | |
| 2886 trace_action("print('Search page %d:', repr(%q), hits.length, repr(hits));\n", fz_page_number_from_location(ctx, doc, search_page), search_needle); | |
| 2887 if (search_hit_count) | |
| 2888 { | |
| 2889 float search_hit_x = search_hit_quads[0].ul.x; | |
| 2890 float search_hit_y = search_hit_quads[0].ul.y; | |
| 2891 search_active = 0; | |
| 2892 search_hit_page = search_page; | |
| 2893 jump_to_location_xy(search_hit_page, search_hit_x, search_hit_y); | |
| 2894 } | |
| 2895 else | |
| 2896 { | |
| 2897 if (search_dir > 0) | |
| 2898 { | |
| 2899 if (is_last_page(search_page)) | |
| 2900 search_active = 0; | |
| 2901 else | |
| 2902 search_page = fz_next_page(ctx, doc, search_page); | |
| 2903 } | |
| 2904 else | |
| 2905 { | |
| 2906 if (is_first_page(search_page)) | |
| 2907 search_active = 0; | |
| 2908 else | |
| 2909 search_page = fz_previous_page(ctx, doc, search_page); | |
| 2910 } | |
| 2911 } | |
| 2912 } | |
| 2913 | |
| 2914 /* keep searching later */ | |
| 2915 if (search_active) | |
| 2916 glutPostRedisplay(); | |
| 2917 } | |
| 2918 | |
| 2919 do_app(); | |
| 2920 | |
| 2921 if (showoutline) | |
| 2922 do_outline(outline); | |
| 2923 else if (showundo) | |
| 2924 do_undo(); | |
| 2925 else if (showlayers) | |
| 2926 do_layers(); | |
| 2927 if (showoutline || showundo || showlayers) | |
| 2928 ui_splitter(&outline_start_x, &outline_w, 6*ui.gridsize, 20*ui.gridsize, R); | |
| 2929 | |
| 2930 if (!eqloc(oldpage, currentpage) || oldseparations != currentseparations || oldicc != currenticc) | |
| 2931 { | |
| 2932 load_page(); | |
| 2933 update_title(); | |
| 2934 } | |
| 2935 | |
| 2936 if (showannotate) | |
| 2937 { | |
| 2938 ui_layout(R, BOTH, NW, 0, 0); | |
| 2939 ui_panel_begin(annotate_w, 0, ui.padsize*2, ui.padsize*2, 1); | |
| 2940 if (showannotate == ANNOTATE_MODE_NORMAL) | |
| 2941 do_annotate_panel(); | |
| 2942 else | |
| 2943 do_redact_panel(); | |
| 2944 ui_panel_end(); | |
| 2945 } | |
| 2946 | |
| 2947 #if FZ_ENABLE_JS | |
| 2948 if (showconsole) | |
| 2949 { | |
| 2950 do_console(); | |
| 2951 ui_splitter(&console_start_y, &console_h, 6*ui.lineheight, 25*ui.lineheight, T); | |
| 2952 } | |
| 2953 #endif | |
| 2954 | |
| 2955 do_canvas(); | |
| 2956 | |
| 2957 if (pdf) | |
| 2958 { | |
| 2959 if (document_shown_as_dirty != pdf_has_unsaved_changes(ctx, pdf)) | |
| 2960 update_title(); | |
| 2961 } | |
| 2962 } | |
| 2963 | |
| 2964 void run_main_loop(void) | |
| 2965 { | |
| 2966 if (currentinvert) | |
| 2967 glClearColor(0, 0, 0, 1); | |
| 2968 else | |
| 2969 glClearColor(0.3f, 0.3f, 0.3f, 1); | |
| 2970 ui_begin(); | |
| 2971 fz_try(ctx) | |
| 2972 { | |
| 2973 if (ui.dialog) | |
| 2974 ui.dialog(); | |
| 2975 else | |
| 2976 do_main(); | |
| 2977 } | |
| 2978 fz_catch(ctx) | |
| 2979 { | |
| 2980 ui_show_error_dialog("%s", fz_caught_message(ctx)); | |
| 2981 fz_report_error(ctx); | |
| 2982 } | |
| 2983 ui_end(); | |
| 2984 } | |
| 2985 | |
| 2986 static void usage(const char *argv0) | |
| 2987 { | |
| 2988 fprintf(stderr, "mupdf-gl version %s\n", FZ_VERSION); | |
| 2989 fprintf(stderr, "usage: %s [options] document [page]\n", argv0); | |
| 2990 fprintf(stderr, "\t-p -\tpassword\n"); | |
| 2991 fprintf(stderr, "\t-r -\tresolution\n"); | |
| 2992 fprintf(stderr, "\t-c -\tdisplay ICC profile\n"); | |
| 2993 fprintf(stderr, "\t-b -\tuse named page box (MediaBox, CropBox, BleedBox, TrimBox, or ArtBox)\n"); | |
| 2994 fprintf(stderr, "\t-I\tinvert colors\n"); | |
| 2995 fprintf(stderr, "\t-W -\tpage width for EPUB layout\n"); | |
| 2996 fprintf(stderr, "\t-H -\tpage height for EPUB layout\n"); | |
| 2997 fprintf(stderr, "\t-S -\tfont size for EPUB layout\n"); | |
| 2998 fprintf(stderr, "\t-U -\tuser style sheet for EPUB layout\n"); | |
| 2999 fprintf(stderr, "\t-X\tdisable document styles for EPUB layout\n"); | |
| 3000 fprintf(stderr, "\t-J\tdisable javascript in PDF forms\n"); | |
| 3001 fprintf(stderr, "\t-A -\tset anti-aliasing level (0-8,9,10)\n"); | |
| 3002 fprintf(stderr, "\t-B -\tset black tint color (default: 303030)\n"); | |
| 3003 fprintf(stderr, "\t-C -\tset white tint color (default: FFFFF0)\n"); | |
| 3004 fprintf(stderr, "\t-Y -\tset the UI scaling factor\n"); | |
| 3005 fprintf(stderr, "\t-R -\tenable reflow and set the text extraction options\n"); | |
| 3006 fprintf(stderr, "\t\t\texample: -R dehyphenate,preserve-images\n"); | |
| 3007 exit(1); | |
| 3008 } | |
| 3009 | |
| 3010 static int document_filter(const char *fname) | |
| 3011 { | |
| 3012 return !!fz_recognize_document(ctx, fname); | |
| 3013 } | |
| 3014 | |
| 3015 static void do_open_document_dialog(void) | |
| 3016 { | |
| 3017 if (ui_open_file(filename, "Select a document to open:")) | |
| 3018 { | |
| 3019 ui.dialog = NULL; | |
| 3020 if (filename[0] == 0) | |
| 3021 glutLeaveMainLoop(); | |
| 3022 else | |
| 3023 { | |
| 3024 load_document(); | |
| 3025 if (doc) | |
| 3026 { | |
| 3027 if (reflow_options) | |
| 3028 reflow_document(); | |
| 3029 load_page(); | |
| 3030 shrinkwrap(); | |
| 3031 update_title(); | |
| 3032 } | |
| 3033 } | |
| 3034 } | |
| 3035 } | |
| 3036 | |
| 3037 static void cleanup(void) | |
| 3038 { | |
| 3039 save_history(); | |
| 3040 fz_try(ctx) | |
| 3041 save_accelerator(); | |
| 3042 fz_catch(ctx) | |
| 3043 fz_warn(ctx, "cannot save accelerator file"); | |
| 3044 | |
| 3045 ui_finish(); | |
| 3046 | |
| 3047 fz_drop_pixmap(ctx, page_contents); | |
| 3048 page_contents = NULL; | |
| 3049 #ifndef NDEBUG | |
| 3050 if (fz_atoi(getenv("FZ_DEBUG_STORE"))) | |
| 3051 fz_debug_store(ctx, fz_stdout(ctx)); | |
| 3052 #endif | |
| 3053 | |
| 3054 trace_action("quit(0);\n"); | |
| 3055 | |
| 3056 fz_flush_warnings(ctx); | |
| 3057 | |
| 3058 #if FZ_ENABLE_JS | |
| 3059 console_fin(); | |
| 3060 #endif | |
| 3061 | |
| 3062 fz_drop_output(ctx, trace_file); | |
| 3063 fz_drop_stext_page(ctx, page_text); | |
| 3064 fz_drop_separations(ctx, seps); | |
| 3065 fz_drop_link(ctx, links); | |
| 3066 fz_drop_page(ctx, fzpage); | |
| 3067 fz_drop_outline(ctx, outline); | |
| 3068 fz_drop_document(ctx, doc); | |
| 3069 fz_drop_context(ctx); | |
| 3070 } | |
| 3071 | |
| 3072 int reloadrequested = 0; | |
| 3073 | |
| 3074 #ifndef _WIN32 | |
| 3075 static void signal_handler(int signal) | |
| 3076 { | |
| 3077 if (signal == SIGHUP) | |
| 3078 reloadrequested = 1; | |
| 3079 } | |
| 3080 #endif | |
| 3081 | |
| 3082 #ifdef _MSC_VER | |
| 3083 int main_utf8(int argc, char **argv) | |
| 3084 #else | |
| 3085 int main(int argc, char **argv) | |
| 3086 #endif | |
| 3087 { | |
| 3088 const char *trace_file_name = NULL; | |
| 3089 const char *profile_name = NULL; | |
| 3090 float scale = 0; | |
| 3091 int c; | |
| 3092 | |
| 3093 #ifndef _WIN32 | |
| 3094 | |
| 3095 /* Never wait for termination of child processes. */ | |
| 3096 struct sigaction arg = { | |
| 3097 .sa_handler=SIG_IGN, | |
| 3098 .sa_flags=SA_NOCLDWAIT | |
| 3099 }; | |
| 3100 sigaction(SIGCHLD, &arg, NULL); | |
| 3101 | |
| 3102 signal(SIGHUP, signal_handler); | |
| 3103 #endif | |
| 3104 | |
| 3105 glutInit(&argc, argv); | |
| 3106 | |
| 3107 while ((c = fz_getopt(argc, argv, "p:r:IW:H:S:U:XJb:A:B:C:T:Y:R:c:")) != -1) | |
| 3108 { | |
| 3109 switch (c) | |
| 3110 { | |
| 3111 default: usage(argv[0]); break; | |
| 3112 case 'p': password = fz_optarg; break; | |
| 3113 case 'r': currentzoom = fz_atof(fz_optarg); break; | |
| 3114 case 'c': profile_name = fz_optarg; break; | |
| 3115 case 'I': currentinvert = !currentinvert; break; | |
| 3116 case 'b': currentbox = fz_box_type_from_string(fz_optarg); break; | |
| 3117 case 'W': layout_w = fz_atof(fz_optarg); break; | |
| 3118 case 'H': layout_h = fz_atof(fz_optarg); break; | |
| 3119 case 'S': layout_em = fz_atof(fz_optarg); break; | |
| 3120 case 'U': layout_css = fz_optarg; break; | |
| 3121 case 'X': layout_use_doc_css = 0; break; | |
| 3122 case 'J': enable_js = !enable_js; break; | |
| 3123 case 'A': currentaa = fz_atoi(fz_optarg); break; | |
| 3124 case 'C': currenttint = 1; tint_white = strtol(fz_optarg, NULL, 16); break; | |
| 3125 case 'B': currenttint = 1; tint_black = strtol(fz_optarg, NULL, 16); break; | |
| 3126 case 'R': reflow_options = fz_optarg; break; | |
| 3127 case 'T': trace_file_name = fz_optpath(fz_optarg); break; | |
| 3128 case 'Y': scale = fz_atof(fz_optarg); break; | |
| 3129 } | |
| 3130 } | |
| 3131 | |
| 3132 screen_w = glutGet(GLUT_SCREEN_WIDTH) - SCREEN_FURNITURE_W; | |
| 3133 screen_h = glutGet(GLUT_SCREEN_HEIGHT) - SCREEN_FURNITURE_H; | |
| 3134 | |
| 3135 ui_init_dpi(scale); | |
| 3136 | |
| 3137 oldzoom = currentzoom = currentzoom * ui.scale; | |
| 3138 | |
| 3139 ctx = fz_new_context(NULL, NULL, FZ_STORE_DEFAULT); | |
| 3140 | |
| 3141 #ifdef _WIN32 | |
| 3142 /* stderr goes nowhere. Get us a debug stream we have a chance | |
| 3143 * of seeing. */ | |
| 3144 fz_set_stddbg(ctx, fz_stdods(ctx)); | |
| 3145 #endif | |
| 3146 | |
| 3147 #if FZ_ENABLE_JS | |
| 3148 console_init(); | |
| 3149 #endif | |
| 3150 | |
| 3151 fz_register_document_handlers(ctx); | |
| 3152 | |
| 3153 if (trace_file_name) | |
| 3154 { | |
| 3155 trace_file = fz_new_output_with_path(ctx, trace_file_name, 0); | |
| 3156 trace_action("var doc, page, annot, widget, widgetstr, hits, tmp;\n"); | |
| 3157 trace_action("function RegressionError() {\n"); | |
| 3158 trace_action(" var err = new Error(Array.prototype.join.call(arguments, ' '));\n"); | |
| 3159 trace_action(" err.name = 'RegressionError';\n"); | |
| 3160 trace_action(" return err;\n"); | |
| 3161 trace_action("}\n"); | |
| 3162 } | |
| 3163 | |
| 3164 if (profile_name) | |
| 3165 { | |
| 3166 fz_buffer *profile_data = fz_read_file(ctx, profile_name); | |
| 3167 profile = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_RGB, 0, NULL, profile_data); | |
| 3168 fz_drop_buffer(ctx, profile_data); | |
| 3169 } | |
| 3170 else | |
| 3171 { | |
| 3172 profile = fz_device_rgb(ctx); | |
| 3173 } | |
| 3174 | |
| 3175 if (layout_css) | |
| 3176 fz_load_user_css(ctx, layout_css); | |
| 3177 fz_set_use_document_css(ctx, layout_use_doc_css); | |
| 3178 | |
| 3179 if (fz_optind < argc) | |
| 3180 { | |
| 3181 fz_strlcpy(filename, argv[fz_optind++], sizeof filename); | |
| 3182 if (fz_optind < argc) | |
| 3183 anchor = argv[fz_optind++]; | |
| 3184 if (fz_optind < argc) | |
| 3185 usage(argv[0]); | |
| 3186 | |
| 3187 fz_try(ctx) | |
| 3188 { | |
| 3189 page_tex.w = 600; | |
| 3190 page_tex.h = 700; | |
| 3191 load_document(); | |
| 3192 if (doc) | |
| 3193 { | |
| 3194 if (reflow_options) | |
| 3195 reflow_document(); | |
| 3196 load_page(); | |
| 3197 } | |
| 3198 } | |
| 3199 fz_always(ctx) | |
| 3200 { | |
| 3201 float sx = 1, sy = 1; | |
| 3202 if (screen_w > 0 && page_tex.w > screen_w) | |
| 3203 sx = (float)screen_w / page_tex.w; | |
| 3204 if (screen_h > 0 && page_tex.h > screen_h) | |
| 3205 sy = (float)screen_h / page_tex.h; | |
| 3206 if (sy < sx) | |
| 3207 sx = sy; | |
| 3208 if (sx < 1) | |
| 3209 { | |
| 3210 fz_irect area; | |
| 3211 | |
| 3212 currentzoom *= sx; | |
| 3213 oldzoom = currentzoom; | |
| 3214 | |
| 3215 /* compute bounds here for initial window size */ | |
| 3216 page_bounds = fz_bound_page_box(ctx, fzpage, currentbox); | |
| 3217 transform_page(); | |
| 3218 | |
| 3219 area = fz_irect_from_rect(draw_page_bounds); | |
| 3220 page_tex.w = area.x1 - area.x0; | |
| 3221 page_tex.h = area.y1 - area.y0; | |
| 3222 } | |
| 3223 | |
| 3224 ui_init(page_tex.w, page_tex.h, "MuPDF: Loading..."); | |
| 3225 ui_input_init(&search_input, ""); | |
| 3226 } | |
| 3227 fz_catch(ctx) | |
| 3228 { | |
| 3229 ui_show_error_dialog("%s", fz_caught_message(ctx)); | |
| 3230 fz_report_error(ctx); | |
| 3231 } | |
| 3232 | |
| 3233 fz_try(ctx) | |
| 3234 { | |
| 3235 if (doc) | |
| 3236 update_title(); | |
| 3237 } | |
| 3238 fz_catch(ctx) | |
| 3239 { | |
| 3240 ui_show_error_dialog("%s", fz_caught_message(ctx)); | |
| 3241 fz_report_error(ctx); | |
| 3242 } | |
| 3243 } | |
| 3244 else | |
| 3245 { | |
| 3246 #ifdef _WIN32 | |
| 3247 win_install(); | |
| 3248 #endif | |
| 3249 ui_init(ui.gridsize * 26, ui.gridsize * 26, "MuPDF: Open document"); | |
| 3250 ui_input_init(&search_input, ""); | |
| 3251 ui_init_open_file(".", document_filter); | |
| 3252 ui.dialog = do_open_document_dialog; | |
| 3253 } | |
| 3254 | |
| 3255 annotate_w *= ui.lineheight; | |
| 3256 outline_w *= ui.lineheight; | |
| 3257 #if FZ_ENABLE_JS | |
| 3258 console_h *= ui.lineheight; | |
| 3259 #endif | |
| 3260 | |
| 3261 glutMainLoop(); | |
| 3262 | |
| 3263 cleanup(); | |
| 3264 | |
| 3265 return 0; | |
| 3266 } | |
| 3267 | |
| 3268 #ifdef _WIN32 | |
| 3269 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) | |
| 3270 { | |
| 3271 int argc; | |
| 3272 LPWSTR *wargv = CommandLineToArgvW(GetCommandLineW(), &argc); | |
| 3273 char **argv = fz_argv_from_wargv(argc, wargv); | |
| 3274 int ret = main_utf8(argc, argv); | |
| 3275 fz_free_argv(argc, argv); | |
| 3276 return ret; | |
| 3277 } | |
| 3278 #endif |
