1#include "pdfapp.h"
2#include "curl_stream.h"
3#include "mupdf/helpers/pkcs7-check.h"
4#include "mupdf/helpers/pkcs7-openssl.h"
5
6#include <string.h>
7#include <limits.h>
8#include <stdlib.h>
9#include <stdio.h>
10
11#ifdef _WIN32
12#include <windows.h>
13#include <direct.h> /* for getcwd */
14#else
15#include <unistd.h> /* for getcwd */
16#endif
17
18#define BEYOND_THRESHHOLD 40
19
20#ifndef PATH_MAX
21#define PATH_MAX 4096
22#endif
23
24#ifndef MAX
25#define MAX(a,b) ((a) > (b) ? (a) : (b))
26#endif
27
28enum panning
29{
30 DONT_PAN = 0,
31 PAN_TO_TOP,
32 PAN_TO_BOTTOM
33};
34
35enum
36{
37 PDFAPP_OUTLINE_DEFERRED = 1,
38 PDFAPP_OUTLINE_LOAD_NOW = 2
39};
40
41#ifdef HAVE_CURL
42static void pdfapp_sleep(int ms)
43{
44#ifdef _WIN32
45 Sleep(ms);
46#else
47 usleep(ms * 1000);
48#endif
49}
50#endif
51
52static void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage, int repaint, int transition, int searching);
53
54static const int zoomlist[] = {
55 18, 24, 36, 54, 72, 96, 120, 144, 180,
56 216, 288, 360, 432, 504, 576, 648, 720,
57 792, 864, 936, 1008, 1080, 1152
58};
59
60static int zoom_in(int oldres)
61{
62 int i;
63 for (i = 0; i < nelem(zoomlist) - 1; ++i)
64 if (zoomlist[i] <= oldres && zoomlist[i+1] > oldres)
65 return zoomlist[i+1];
66 return zoomlist[i];
67}
68
69static int zoom_out(int oldres)
70{
71 int i;
72 for (i = 0; i < nelem(zoomlist) - 1; ++i)
73 if (zoomlist[i] < oldres && zoomlist[i+1] >= oldres)
74 return zoomlist[i];
75 return zoomlist[0];
76}
77
78static void pdfapp_warn(pdfapp_t *app, const char *fmt, ...)
79{
80 char buf[1024];
81 va_list ap;
82 va_start(ap, fmt);
83 fz_vsnprintf(buf, sizeof(buf), fmt, ap);
84 va_end(ap);
85 buf[sizeof(buf)-1] = 0;
86 winwarn(app, buf);
87}
88
89static void pdfapp_error(pdfapp_t *app, char *msg)
90{
91 winerror(app, msg);
92}
93
94char *pdfapp_version(pdfapp_t *app)
95{
96 return
97 "MuPDF " FZ_VERSION "\n"
98 "Copyright 2006-2017 Artifex Software, Inc.\n";
99}
100
101char *pdfapp_usage(pdfapp_t *app)
102{
103 return
104 "[\t\t-- rotate left\n"
105 "]\t\t-- rotate right\n"
106 "h left\t\t-- scroll left\n"
107 "j down\t\t-- scroll down\n"
108 "k up\t\t-- scroll up\n"
109 "l right\t\t-- scroll right\n"
110 "+\t\t-- zoom in\n"
111 "-\t\t-- zoom out\n"
112 "W\t\t-- zoom to fit window width\n"
113 "H\t\t-- zoom to fit window height\n"
114 "Z\t\t-- zoom to fit page\n"
115 "z\t\t-- reset zoom\n"
116 "<\t\t-- decrease font size (EPUB only)\n"
117 ">\t\t-- increase font size (EPUB only)\n"
118 "w\t\t-- shrinkwrap\n"
119 "f\t\t-- fullscreen\n"
120 "r\t\t-- reload file\n"
121 ". pgdn space\t-- next page\n"
122 ", pgup b\t-- previous page\n"
123 "m\t\t-- mark page for snap back\n"
124 "t\t\t-- pop back to latest mark\n"
125 "1m\t\t-- mark page in register 1\n"
126 "1t\t\t-- go to page in register 1\n"
127 "G\t\t-- go to last page\n"
128 "123g\t\t-- go to page 123\n"
129 "/\t\t-- search forwards for text\n"
130 "?\t\t-- search backwards for text\n"
131 "n\t\t-- find next search result\n"
132 "N\t\t-- find previous search result\n"
133 "c\t\t-- toggle between color and grayscale\n"
134 "I\t\t-- toggle inverted color mode\n"
135 "C\t\t-- toggle tinted color mode\n"
136 "E\t\t-- enable/disable ICC color mode\n"
137 "e\t\t-- enable/disable spot color mode\n"
138 "q\t\t-- quit\n"
139 ;
140}
141
142void pdfapp_init(fz_context *ctx, pdfapp_t *app)
143{
144 memset(app, 0, sizeof(pdfapp_t));
145 app->scrw = 640;
146 app->scrh = 480;
147 app->resolution = 72;
148 app->ctx = ctx;
149
150 app->layout_w = FZ_DEFAULT_LAYOUT_W;
151 app->layout_h = FZ_DEFAULT_LAYOUT_H;
152 app->layout_em = FZ_DEFAULT_LAYOUT_EM;
153 app->layout_css = NULL;
154 app->layout_use_doc_css = 1;
155
156 app->transition.duration = 0.25f;
157 app->transition.type = FZ_TRANSITION_FADE;
158#ifdef _WIN32
159 app->colorspace = fz_device_bgr(ctx);
160#else
161 app->colorspace = fz_device_rgb(ctx);
162#endif
163 app->tint_white = 0xFFFAF0;
164
165 app->useicc = 1;
166 app->useseparations = 0;
167 app->aalevel = 8;
168}
169
170void pdfapp_setresolution(pdfapp_t *app, int res)
171{
172 app->default_resolution = res;
173 app->resolution = res;
174}
175
176void pdfapp_invert(pdfapp_t *app, fz_rect rect)
177{
178 fz_invert_pixmap_rect(app->ctx, app->image, fz_round_rect(rect));
179}
180
181void pdfapp_reloadfile(pdfapp_t *app)
182{
183 char filename[PATH_MAX];
184 fz_strlcpy(filename, app->docpath, PATH_MAX);
185 pdfapp_close(app);
186 pdfapp_open(app, filename, 1);
187}
188
189static void event_cb(fz_context *ctx, pdf_document *doc, pdf_doc_event *event, void *data)
190{
191 pdfapp_t *app = (pdfapp_t *)data;
192
193 switch (event->type)
194 {
195 case PDF_DOCUMENT_EVENT_ALERT:
196 {
197 pdf_alert_event *alert = pdf_access_alert_event(ctx, event);
198 winalert(app, alert);
199 }
200 break;
201
202 case PDF_DOCUMENT_EVENT_PRINT:
203 winprint(app);
204 break;
205
206 case PDF_DOCUMENT_EVENT_EXEC_MENU_ITEM:
207 {
208 const char *item = pdf_access_exec_menu_item_event(ctx, event);
209
210 if (!strcmp(item, "Print"))
211 winprint(app);
212 else
213 pdfapp_warn(app, "The document attempted to execute menu item: %s. (Not supported)", item);
214 }
215 break;
216
217 case PDF_DOCUMENT_EVENT_LAUNCH_URL:
218 {
219 pdf_launch_url_event *launch_url = pdf_access_launch_url_event(ctx, event);
220
221 pdfapp_warn(app, "The document attempted to open url: %s. (Not supported by app)", launch_url->url);
222 }
223 break;
224
225 case PDF_DOCUMENT_EVENT_MAIL_DOC:
226 {
227 pdf_mail_doc_event *mail_doc = pdf_access_mail_doc_event(ctx, event);
228
229 pdfapp_warn(app, "The document attempted to mail the document%s%s%s%s%s%s%s%s (Not supported)",
230 mail_doc->to[0]?", To: ":"", mail_doc->to,
231 mail_doc->cc[0]?", Cc: ":"", mail_doc->cc,
232 mail_doc->bcc[0]?", Bcc: ":"", mail_doc->bcc,
233 mail_doc->subject[0]?", Subject: ":"", mail_doc->subject);
234 }
235 break;
236 }
237}
238
239void pdfapp_open(pdfapp_t *app, char *filename, int reload)
240{
241 pdfapp_open_progressive(app, filename, reload, 0);
242}
243
244#ifdef HAVE_CURL
245static void
246pdfapp_more_data(void *app_, int complete)
247{
248 pdfapp_t *app = (pdfapp_t *)app_;
249 if (complete && app->outline_deferred == PDFAPP_OUTLINE_DEFERRED)
250 {
251 app->outline_deferred = PDFAPP_OUTLINE_LOAD_NOW;
252 winreloadpage(app);
253 }
254 else if (app->incomplete)
255 winreloadpage(app);
256}
257#endif
258
259static int make_fake_doc(pdfapp_t *app)
260{
261 fz_context *ctx = app->ctx;
262 pdf_document *pdf = NULL;
263 fz_buffer *contents = NULL;
264 pdf_obj *page_obj = NULL;
265
266 fz_var(contents);
267 fz_var(page_obj);
268
269 fz_try(ctx)
270 {
271 fz_rect mediabox = { 0, 0, app->winw, app->winh };
272 int i;
273
274 pdf = pdf_create_document(ctx);
275
276 contents = fz_new_buffer(ctx, 100);
277 fz_append_printf(ctx, contents, "1 0 0 RG %g w 0 0 m %g %g l 0 %g m %g 0 l s\n",
278 fz_min(mediabox.x1, mediabox.y1) / 20,
279 mediabox.x1, mediabox.y1,
280 mediabox.y1, mediabox.x1);
281
282 /* Create enough copies of our blank(ish) page so that the
283 * page number is preserved if and when a subsequent load
284 * works. */
285 page_obj = pdf_add_page(ctx, pdf, mediabox, 0, NULL, contents);
286 for (i = 0; i < app->pagecount; i++)
287 pdf_insert_page(ctx, pdf, -1, page_obj);
288 }
289 fz_always(ctx)
290 {
291 pdf_drop_obj(ctx, page_obj);
292 fz_drop_buffer(ctx, contents);
293 }
294 fz_catch(ctx)
295 {
296 fz_drop_document(ctx, (fz_document *) pdf);
297 return 1;
298 }
299
300 app->doc = (fz_document*)pdf;
301 return 0;
302}
303
304void pdfapp_open_progressive(pdfapp_t *app, char *filename, int reload, int kbps)
305{
306 fz_context *ctx = app->ctx;
307 char *password = "";
308 pdf_document *idoc;
309
310 fz_try(ctx)
311 {
312 fz_register_document_handlers(ctx);
313
314 if (app->layout_css)
315 {
316 fz_buffer *buf = fz_read_file(ctx, app->layout_css);
317 fz_set_user_css(ctx, fz_string_from_buffer(ctx, buf));
318 fz_drop_buffer(ctx, buf);
319 }
320
321 fz_set_use_document_css(ctx, app->layout_use_doc_css);
322
323#ifdef HAVE_CURL
324 if (!strncmp(filename, "http://", 7) || !strncmp(filename, "https://", 8))
325 {
326 app->stream = fz_open_url(ctx, filename, kbps, pdfapp_more_data, app);
327 while (1)
328 {
329 fz_try(ctx)
330 {
331 fz_seek(ctx, app->stream, 0, SEEK_SET);
332 app->doc = fz_open_document_with_stream(ctx, filename, app->stream);
333 }
334 fz_catch(ctx)
335 {
336 if (fz_caught(ctx) == FZ_ERROR_TRYLATER)
337 {
338 pdfapp_sleep(100);
339 continue;
340 }
341 fz_rethrow(ctx);
342 }
343 break;
344 }
345 }
346 else if (kbps > 0)
347 {
348 fz_stream *stream = fz_open_file_progressive(ctx, filename, kbps, pdfapp_more_data, app);
349 while (1)
350 {
351 fz_try(ctx)
352 {
353 fz_seek(ctx, stream, 0, SEEK_SET);
354 app->doc = fz_open_document_with_stream(ctx, filename, stream);
355 }
356 fz_catch(ctx)
357 {
358 if (fz_caught(ctx) == FZ_ERROR_TRYLATER)
359 {
360 pdfapp_sleep(100);
361 continue;
362 }
363 fz_rethrow(ctx);
364 }
365 break;
366 }
367 }
368 else
369#endif
370 {
371 app->doc = fz_open_document(ctx, filename);
372 }
373 }
374 fz_catch(ctx)
375 {
376 if (!reload || make_fake_doc(app))
377 pdfapp_error(app, "cannot open document");
378 }
379
380 idoc = pdf_specifics(app->ctx, app->doc);
381 if (idoc)
382 {
383 fz_try(ctx)
384 {
385 pdf_enable_js(ctx, idoc);
386 pdf_set_doc_event_callback(ctx, idoc, event_cb, app);
387 }
388 fz_catch(ctx)
389 {
390 pdfapp_error(app, "cannot load javascript embedded in document");
391 }
392 }
393
394 fz_try(ctx)
395 {
396
397 if (fz_needs_password(app->ctx, app->doc))
398 {
399 int okay = fz_authenticate_password(app->ctx, app->doc, password);
400 while (!okay)
401 {
402 password = winpassword(app, filename);
403 if (!password)
404 fz_throw(ctx, FZ_ERROR_GENERIC, "Needs a password");
405 okay = fz_authenticate_password(app->ctx, app->doc, password);
406 if (!okay)
407 pdfapp_warn(app, "Invalid password.");
408 }
409 }
410
411 app->docpath = fz_strdup(ctx, filename);
412 app->doctitle = filename;
413 if (strrchr(app->doctitle, '\\'))
414 app->doctitle = strrchr(app->doctitle, '\\') + 1;
415 if (strrchr(app->doctitle, '/'))
416 app->doctitle = strrchr(app->doctitle, '/') + 1;
417 app->doctitle = fz_strdup(ctx, app->doctitle);
418
419 fz_layout_document(app->ctx, app->doc, app->layout_w, app->layout_h, app->layout_em);
420
421 while (1)
422 {
423 fz_try(ctx)
424 {
425 app->pagecount = fz_count_pages(app->ctx, app->doc);
426 if (app->pagecount <= 0)
427 fz_throw(ctx, FZ_ERROR_GENERIC, "No pages in document");
428 }
429 fz_catch(ctx)
430 {
431 if (fz_caught(ctx) == FZ_ERROR_TRYLATER)
432 {
433 continue;
434 }
435 fz_rethrow(ctx);
436 }
437 break;
438 }
439 while (1)
440 {
441 fz_try(ctx)
442 {
443 app->outline = fz_load_outline(app->ctx, app->doc);
444 }
445 fz_catch(ctx)
446 {
447 app->outline = NULL;
448 if (fz_caught(ctx) == FZ_ERROR_TRYLATER)
449 app->outline_deferred = PDFAPP_OUTLINE_DEFERRED;
450 else
451 pdfapp_warn(app, "Failed to load outline.");
452 }
453 break;
454 }
455 }
456 fz_catch(ctx)
457 {
458 pdfapp_error(app, "cannot open document");
459 }
460
461 if (app->pageno < 1)
462 app->pageno = 1;
463 if (app->pageno > app->pagecount)
464 app->pageno = app->pagecount;
465 if (app->resolution < MINRES)
466 app->resolution = MINRES;
467 if (app->resolution > MAXRES)
468 app->resolution = MAXRES;
469
470 if (!reload)
471 {
472 app->shrinkwrap = 1;
473 app->rotate = 0;
474 app->panx = 0;
475 app->pany = 0;
476 }
477
478 pdfapp_showpage(app, 1, 1, 1, 0, 0);
479}
480
481void pdfapp_close(pdfapp_t *app)
482{
483 fz_drop_display_list(app->ctx, app->page_list);
484 app->page_list = NULL;
485
486 fz_drop_display_list(app->ctx, app->annotations_list);
487 app->annotations_list = NULL;
488
489 fz_drop_separations(app->ctx, app->seps);
490 app->seps = NULL;
491
492 fz_drop_stext_page(app->ctx, app->page_text);
493 app->page_text = NULL;
494
495 fz_drop_link(app->ctx, app->page_links);
496 app->page_links = NULL;
497
498 fz_free(app->ctx, app->doctitle);
499 app->doctitle = NULL;
500
501 fz_free(app->ctx, app->docpath);
502 app->docpath = NULL;
503
504 fz_drop_pixmap(app->ctx, app->image);
505 app->image = NULL;
506
507 fz_drop_pixmap(app->ctx, app->new_image);
508 app->new_image = NULL;
509
510 fz_drop_pixmap(app->ctx, app->old_image);
511 app->old_image = NULL;
512
513 fz_drop_outline(app->ctx, app->outline);
514 app->outline = NULL;
515
516 fz_drop_page(app->ctx, app->page);
517 app->page = NULL;
518
519 fz_drop_document(app->ctx, app->doc);
520 app->doc = NULL;
521
522#ifdef HAVE_CURL
523 fz_drop_stream(app->ctx, app->stream);
524#endif
525
526 fz_flush_warnings(app->ctx);
527}
528
529static int gen_tmp_file(char *buf, int len)
530{
531 int i;
532 char *name = strrchr(buf, '/');
533
534 if (name == NULL)
535 name = strrchr(buf, '\\');
536
537 if (name != NULL)
538 name++;
539 else
540 name = buf;
541
542 for (i = 0; i < 10000; i++)
543 {
544 FILE *f;
545 sprintf(name, "tmp%04d", i);
546 f = fopen(buf, "r");
547 if (f == NULL)
548 return 1;
549 fclose(f);
550 }
551
552 return 0;
553}
554
555static int pdfapp_save(pdfapp_t *app)
556{
557 char buf[PATH_MAX];
558
559 pdf_document *idoc = pdf_specifics(app->ctx, app->doc);
560 if (!idoc)
561 return 0;
562
563 if (wingetsavepath(app, buf, PATH_MAX))
564 {
565 pdf_write_options opts = pdf_default_write_options;
566
567 opts.do_incremental = pdf_can_be_saved_incrementally(app->ctx, idoc);
568
569 if (strcmp(buf, app->docpath) != 0)
570 {
571 wincopyfile(app->docpath, buf);
572 pdf_save_document(app->ctx, idoc, buf, &opts);
573 pdfapp_close(app);
574 pdfapp_open(app, buf, 1);
575 return 1;
576 }
577
578 if (gen_tmp_file(buf, PATH_MAX))
579 {
580 int written = 0;
581
582 fz_try(app->ctx)
583 {
584 wincopyfile(app->docpath, buf);
585 pdf_save_document(app->ctx, idoc, buf, &opts);
586 written = 1;
587 }
588 fz_catch(app->ctx)
589 {
590 }
591
592 if (written)
593 {
594 char buf2[PATH_MAX];
595 fz_strlcpy(buf2, app->docpath, PATH_MAX);
596 pdfapp_close(app);
597 winreplacefile(buf, buf2);
598 pdfapp_open(app, buf2, 1);
599
600 return written;
601 }
602 }
603 }
604
605 return 0;
606}
607
608int pdfapp_preclose(pdfapp_t *app)
609{
610 pdf_document *idoc = pdf_specifics(app->ctx, app->doc);
611
612 if (idoc && pdf_has_unsaved_changes(app->ctx, idoc))
613 {
614 switch (winsavequery(app))
615 {
616 case DISCARD:
617 return 1;
618
619 case CANCEL:
620 return 0;
621
622 case SAVE:
623 return pdfapp_save(app);
624 }
625 }
626
627 return 1;
628}
629
630static void pdfapp_viewctm(fz_matrix *mat, pdfapp_t *app)
631{
632 *mat = fz_transform_page(app->page_bbox, app->resolution, app->rotate);
633}
634
635static void pdfapp_panview(pdfapp_t *app, int newx, int newy)
636{
637 if (newx > 0)
638 newx = 0;
639 if (newy > 0)
640 newy = 0;
641
642 if (newx + app->imgw < app->winw)
643 newx = app->winw - app->imgw;
644 if (newy + app->imgh < app->winh)
645 newy = app->winh - app->imgh;
646
647 if (app->winw >= app->imgw)
648 newx = (app->winw - app->imgw) / 2;
649 if (app->winh >= app->imgh)
650 newy = (app->winh - app->imgh) / 2;
651
652 if (newx != app->panx || newy != app->pany)
653 winrepaint(app);
654
655 app->panx = newx;
656 app->pany = newy;
657}
658
659static void pdfapp_loadpage(pdfapp_t *app, int no_cache)
660{
661 fz_device *mdev = NULL;
662 int errored = 0;
663 fz_cookie cookie = { 0 };
664
665 fz_var(mdev);
666
667 fz_drop_display_list(app->ctx, app->page_list);
668 fz_drop_display_list(app->ctx, app->annotations_list);
669 fz_drop_separations(app->ctx, app->seps);
670 fz_drop_stext_page(app->ctx, app->page_text);
671 fz_drop_link(app->ctx, app->page_links);
672 fz_drop_page(app->ctx, app->page);
673
674 app->page_list = NULL;
675 app->annotations_list = NULL;
676 app->seps = NULL;
677 app->page_text = NULL;
678 app->page_links = NULL;
679 app->page = NULL;
680 app->page_bbox.x0 = 0;
681 app->page_bbox.y0 = 0;
682 app->page_bbox.x1 = 100;
683 app->page_bbox.y1 = 100;
684
685 app->incomplete = 0;
686
687 fz_try(app->ctx)
688 {
689 app->page = fz_load_page(app->ctx, app->doc, app->pageno - 1);
690 if (app->page && app->page->incomplete)
691 app->incomplete = 1;
692 app->page_bbox = fz_bound_page(app->ctx, app->page);
693 app->page_links = fz_load_links(app->ctx, app->page);
694 }
695 fz_catch(app->ctx)
696 {
697 if (fz_caught(app->ctx) == FZ_ERROR_TRYLATER)
698 app->incomplete = 1;
699 else
700 pdfapp_warn(app, "Failed to load page.");
701 return;
702 }
703
704 if (app->useicc)
705 fz_enable_icc(app->ctx);
706 else
707 fz_disable_icc(app->ctx);
708
709 fz_set_aa_level(app->ctx, app->aalevel);
710
711 if (app->useseparations)
712 {
713 fz_try(app->ctx)
714 {
715 app->seps = fz_page_separations(app->ctx, app->page);
716 if (app->seps)
717 {
718 int i, n = fz_count_separations(app->ctx, app->seps);
719 for (i = 0; i < n; i++)
720 fz_set_separation_behavior(app->ctx, app->seps, i, FZ_SEPARATION_COMPOSITE);
721 }
722 else if (fz_page_uses_overprint(app->ctx, app->page))
723 {
724 /* This page uses overprint, so we need an empty
725 * sep object to force the overprint simulation on. */
726 app->seps = fz_new_separations(app->ctx, 0);
727 }
728 else if (fz_document_output_intent(app->ctx, app->doc))
729 {
730 /* We have an output intent. Force the overprint
731 *simulation on, because this ensures that
732 * we 'simulate' the output intent too. */
733 app->seps = fz_new_separations(app->ctx, 0);
734 }
735 }
736 fz_catch(app->ctx)
737 {
738 if (fz_caught(app->ctx) == FZ_ERROR_TRYLATER)
739 app->incomplete = 1;
740 else
741 pdfapp_warn(app, "Failed to load page.");
742 errored = 1;
743 }
744 }
745
746 fz_try(app->ctx)
747 {
748 /* Create display lists */
749 app->page_list = fz_new_display_list(app->ctx, fz_infinite_rect);
750 mdev = fz_new_list_device(app->ctx, app->page_list);
751 if (no_cache)
752 fz_enable_device_hints(app->ctx, mdev, FZ_NO_CACHE);
753 fz_run_page_contents(app->ctx, app->page, mdev, fz_identity, &cookie);
754 fz_close_device(app->ctx, mdev);
755 fz_drop_device(app->ctx, mdev);
756 mdev = NULL;
757 app->annotations_list = fz_new_display_list(app->ctx, fz_infinite_rect);
758 mdev = fz_new_list_device(app->ctx, app->annotations_list);
759 fz_run_page_annots(app->ctx, app->page, mdev, fz_identity, &cookie);
760 fz_run_page_widgets(app->ctx, app->page, mdev, fz_identity, &cookie);
761 if (cookie.incomplete)
762 {
763 app->incomplete = 1;
764 }
765 else if (cookie.errors)
766 {
767 pdfapp_warn(app, "Errors found on page.");
768 errored = 1;
769 }
770 fz_close_device(app->ctx, mdev);
771 }
772 fz_always(app->ctx)
773 {
774 fz_drop_device(app->ctx, mdev);
775 }
776 fz_catch(app->ctx)
777 {
778 if (fz_caught(app->ctx) == FZ_ERROR_TRYLATER)
779 app->incomplete = 1;
780 else
781 pdfapp_warn(app, "Failed to load page.");
782 errored = 1;
783 }
784
785 app->errored = errored;
786}
787
788static void pdfapp_runpage(pdfapp_t *app, fz_device *dev, const fz_matrix ctm, fz_rect scissor, fz_cookie *cookie)
789{
790 if (app->page_list)
791 fz_run_display_list(app->ctx, app->page_list, dev, ctm, scissor, cookie);
792 if (app->annotations_list)
793 fz_run_display_list(app->ctx, app->annotations_list, dev, ctm, scissor, cookie);
794}
795
796#define MAX_TITLE 256
797
798void pdfapp_reloadpage(pdfapp_t *app)
799{
800 if (app->outline_deferred == PDFAPP_OUTLINE_LOAD_NOW)
801 {
802 fz_try(app->ctx)
803 app->outline = fz_load_outline(app->ctx, app->doc);
804 fz_catch(app->ctx)
805 app->outline = NULL;
806 app->outline_deferred = 0;
807 }
808 pdfapp_showpage(app, 1, 1, 1, 0, 0);
809}
810
811static void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage, int repaint, int transition, int searching)
812{
813 char buf[MAX_TITLE];
814 fz_device *idev = NULL;
815 fz_device *tdev;
816 fz_colorspace *colorspace;
817 fz_matrix ctm;
818 fz_rect bounds;
819 fz_irect ibounds;
820 fz_cookie cookie = { 0 };
821
822 if (!app->nowaitcursor)
823 wincursor(app, WAIT);
824
825 if (!app->transitions_enabled || !app->presentation_mode)
826 transition = 0;
827
828 if (transition)
829 {
830 app->old_image = app->image;
831 app->image = NULL;
832 app->imgw = 0;
833 app->imgh = 0;
834 }
835
836 /* Always reload page if it was flagged incomplete */
837 if (app->incomplete)
838 loadpage = 1;
839
840 if (loadpage)
841 {
842 fz_rect mediabox;
843 pdfapp_loadpage(app, searching);
844
845 /* Zero search hit position */
846 app->hit_count = 0;
847
848 /* Extract text */
849 fz_try(app->ctx)
850 mediabox = fz_bound_page(app->ctx, app->page);
851 fz_catch(app->ctx)
852 {
853 if (fz_caught(app->ctx) != FZ_ERROR_TRYLATER)
854 fz_rethrow(app->ctx);
855 mediabox = fz_make_rect(0, 0, 100, 100);
856 app->incomplete = 1;
857 }
858
859 app->page_text = fz_new_stext_page(app->ctx, mediabox);
860
861 if (app->page_list || app->annotations_list)
862 {
863 tdev = fz_new_stext_device(app->ctx, app->page_text, NULL);
864 fz_try(app->ctx)
865 {
866 pdfapp_runpage(app, tdev, fz_identity, fz_infinite_rect, &cookie);
867 fz_close_device(app->ctx, tdev);
868 }
869 fz_always(app->ctx)
870 fz_drop_device(app->ctx, tdev);
871 fz_catch(app->ctx)
872 fz_rethrow(app->ctx);
873 }
874 }
875
876 if (drawpage)
877 {
878 char buf2[64];
879 size_t len;
880
881 sprintf(buf2, " - %d/%d (%g dpi)",
882 app->pageno, app->pagecount, app->resolution);
883 len = MAX_TITLE-strlen(buf2);
884 if (strlen(app->doctitle) > len)
885 {
886 fz_strlcpy(buf, app->doctitle, len-3);
887 fz_strlcat(buf, "...", MAX_TITLE);
888 fz_strlcat(buf, buf2, MAX_TITLE);
889 }
890 else
891 sprintf(buf, "%s%s", app->doctitle, buf2);
892 wintitle(app, buf);
893
894 pdfapp_viewctm(&ctm, app);
895 bounds = fz_transform_rect(app->page_bbox, ctm);
896 ibounds = fz_round_rect(bounds);
897 bounds = fz_rect_from_irect(ibounds);
898
899 /* Draw */
900 fz_drop_pixmap(app->ctx, app->image);
901 if (app->grayscale)
902 colorspace = fz_device_gray(app->ctx);
903 else
904 colorspace = app->colorspace;
905
906 app->image = NULL;
907 app->imgw = 0;
908 app->imgh = 0;
909
910 fz_var(app->image);
911 fz_var(idev);
912
913 fz_try(app->ctx)
914 {
915 app->image = fz_new_pixmap_with_bbox(app->ctx, colorspace, ibounds, app->seps, 1);
916 app->imgw = fz_pixmap_width(app->ctx, app->image);
917 app->imgh = fz_pixmap_height(app->ctx, app->image);
918
919 fz_clear_pixmap_with_value(app->ctx, app->image, 255);
920 if (app->page_list || app->annotations_list)
921 {
922 idev = fz_new_draw_device(app->ctx, fz_identity, app->image);
923 pdfapp_runpage(app, idev, ctm, bounds, &cookie);
924 fz_close_device(app->ctx, idev);
925 }
926 if (app->invert)
927 {
928 fz_invert_pixmap_luminance(app->ctx, app->image);
929 fz_gamma_pixmap(app->ctx, app->image, 1 / 1.4f);
930 }
931 if (app->tint)
932 fz_tint_pixmap(app->ctx, app->image, 0, app->tint_white);
933 }
934 fz_always(app->ctx)
935 fz_drop_device(app->ctx, idev);
936 fz_catch(app->ctx)
937 cookie.errors++;
938 }
939
940 if (transition)
941 {
942 app->new_image = app->image;
943 app->image = NULL;
944 app->imgw = 0;
945 app->imgh = 0;
946
947 if (app->grayscale)
948 colorspace = fz_device_gray(app->ctx);
949 else
950 colorspace = app->colorspace;
951 app->image = fz_new_pixmap_with_bbox(app->ctx, colorspace, ibounds, app->seps, 1);
952 app->imgw = fz_pixmap_width(app->ctx, app->image);
953 app->imgh = fz_pixmap_height(app->ctx, app->image);
954
955 app->duration = 0;
956 fz_page_presentation(app->ctx, app->page, &app->transition, &app->duration);
957 if (app->duration == 0)
958 app->duration = 5;
959 app->in_transit = fz_generate_transition(app->ctx, app->image, app->old_image, app->new_image, 0, &app->transition);
960 if (!app->in_transit)
961 {
962 if (app->duration != 0)
963 winadvancetimer(app, app->duration);
964 }
965 app->start_time = clock();
966 }
967
968 if (repaint)
969 {
970 pdfapp_panview(app, app->panx, app->pany);
971
972 if (!app->image)
973 {
974 /* there is no image to blit, but there might be an error message */
975 winresize(app, app->layout_w, app->layout_h);
976 }
977 else if (app->shrinkwrap)
978 {
979 int w = app->imgw;
980 int h = app->imgh;
981 if (app->winw == w)
982 app->panx = 0;
983 if (app->winh == h)
984 app->pany = 0;
985 if (w > app->scrw * 90 / 100)
986 w = app->scrw * 90 / 100;
987 if (h > app->scrh * 90 / 100)
988 h = app->scrh * 90 / 100;
989 if (w != app->winw || h != app->winh)
990 winresize(app, w, h);
991 }
992
993 winrepaint(app);
994
995 wincursor(app, ARROW);
996 }
997
998 if (cookie.errors && app->errored == 0)
999 {
1000 app->errored = 1;
1001 pdfapp_warn(app, "Errors found on page. Page rendering may be incomplete.");
1002 }
1003
1004 fz_flush_warnings(app->ctx);
1005}
1006
1007static void pdfapp_gotouri(pdfapp_t *app, char *uri)
1008{
1009 char buf[PATH_MAX];
1010
1011 /* Relative file:// URI, make it absolute! */
1012 if (!strncmp(uri, "file://", 7) && uri[7] != '/')
1013 {
1014 char buf_base[PATH_MAX];
1015 char buf_cwd[PATH_MAX];
1016 fz_dirname(buf_base, app->docpath, sizeof buf_base);
1017 getcwd(buf_cwd, sizeof buf_cwd);
1018 fz_snprintf(buf, sizeof buf, "file://%s/%s/%s", buf_cwd, buf_base, uri+7);
1019 fz_cleanname(buf+7);
1020 uri = buf;
1021 }
1022
1023 winopenuri(app, uri);
1024}
1025
1026void pdfapp_gotopage(pdfapp_t *app, int number)
1027{
1028 app->issearching = 0;
1029 winrepaint(app);
1030
1031 if (number < 1)
1032 number = 1;
1033 if (number > app->pagecount)
1034 number = app->pagecount;
1035
1036 if (number == app->pageno)
1037 return;
1038
1039 if (app->histlen + 1 == 256)
1040 {
1041 memmove(app->hist, app->hist + 1, sizeof(int) * 255);
1042 app->histlen --;
1043 }
1044 app->hist[app->histlen++] = app->pageno;
1045 app->pageno = number;
1046 pdfapp_showpage(app, 1, 1, 1, 0, 0);
1047}
1048
1049void pdfapp_inverthit(pdfapp_t *app)
1050{
1051 fz_rect bbox;
1052 fz_matrix ctm;
1053 int i;
1054
1055 pdfapp_viewctm(&ctm, app);
1056
1057 for (i = 0; i < app->hit_count; i++)
1058 {
1059 bbox = fz_rect_from_quad(app->hit_bbox[i]);
1060 bbox = fz_transform_rect(bbox, ctm);
1061 pdfapp_invert(app, bbox);
1062 }
1063}
1064
1065static void pdfapp_search_in_direction(pdfapp_t *app, enum panning *panto, int dir)
1066{
1067 int firstpage, page;
1068
1069 /* abort if no search string */
1070 if (app->search[0] == 0)
1071 {
1072 winrepaint(app);
1073 return;
1074 }
1075
1076 wincursor(app, WAIT);
1077
1078 firstpage = app->pageno;
1079 if (app->searchpage == app->pageno)
1080 page = app->pageno + dir;
1081 else
1082 page = app->pageno;
1083
1084 if (page < 1) page = app->pagecount;
1085 if (page > app->pagecount) page = 1;
1086
1087 do
1088 {
1089 if (page != app->pageno)
1090 {
1091 app->pageno = page;
1092 pdfapp_showpage(app, 1, 0, 0, 0, 1);
1093 }
1094
1095 app->hit_count = fz_search_stext_page(app->ctx, app->page_text, app->search, app->hit_bbox, nelem(app->hit_bbox));
1096 if (app->hit_count > 0)
1097 {
1098 *panto = dir == 1 ? PAN_TO_TOP : PAN_TO_BOTTOM;
1099 app->searchpage = app->pageno;
1100 wincursor(app, HAND);
1101 winrepaint(app);
1102 return;
1103 }
1104
1105 page += dir;
1106 if (page < 1) page = app->pagecount;
1107 if (page > app->pagecount) page = 1;
1108 } while (page != firstpage);
1109
1110 pdfapp_warn(app, "String '%s' not found.", app->search);
1111
1112 app->pageno = firstpage;
1113 pdfapp_showpage(app, 1, 0, 0, 0, 0);
1114 wincursor(app, HAND);
1115 winrepaint(app);
1116}
1117
1118void pdfapp_onresize(pdfapp_t *app, int w, int h)
1119{
1120 if (app->winw != w || app->winh != h)
1121 {
1122 app->winw = w;
1123 app->winh = h;
1124 pdfapp_panview(app, app->panx, app->pany);
1125 winrepaint(app);
1126 }
1127}
1128
1129void pdfapp_autozoom_vertical(pdfapp_t *app)
1130{
1131 app->resolution *= (float) app->winh / app->imgh;
1132 if (app->resolution > MAXRES)
1133 app->resolution = MAXRES;
1134 else if (app->resolution < MINRES)
1135 app->resolution = MINRES;
1136 pdfapp_showpage(app, 0, 1, 1, 0, 0);
1137}
1138
1139void pdfapp_autozoom_horizontal(pdfapp_t *app)
1140{
1141 app->resolution *= (float) app->winw / app->imgw;
1142 if (app->resolution > MAXRES)
1143 app->resolution = MAXRES;
1144 else if (app->resolution < MINRES)
1145 app->resolution = MINRES;
1146 pdfapp_showpage(app, 0, 1, 1, 0, 0);
1147}
1148
1149void pdfapp_autozoom(pdfapp_t *app)
1150{
1151 float page_aspect = (float) app->imgw / app->imgh;
1152 float win_aspect = (float) app->winw / app->winh;
1153 if (page_aspect > win_aspect)
1154 pdfapp_autozoom_horizontal(app);
1155 else
1156 pdfapp_autozoom_vertical(app);
1157}
1158
1159void pdfapp_onkey(pdfapp_t *app, int c, int modifiers)
1160{
1161 int oldpage = app->pageno;
1162 enum panning panto = PAN_TO_TOP;
1163 int loadpage = 1;
1164
1165 if (app->issearching)
1166 {
1167 size_t n = strlen(app->search);
1168 if (c < ' ')
1169 {
1170 if (c == '\b' && n > 0)
1171 {
1172 app->search[n - 1] = 0;
1173 winrepaintsearch(app);
1174 }
1175 if (c == '\n' || c == '\r')
1176 {
1177 app->issearching = 0;
1178 if (n > 0)
1179 {
1180 winrepaintsearch(app);
1181
1182 if (app->searchdir < 0)
1183 {
1184 if (app->pageno == 1)
1185 app->pageno = app->pagecount;
1186 else
1187 app->pageno--;
1188 pdfapp_showpage(app, 1, 1, 0, 0, 1);
1189 }
1190
1191 pdfapp_onkey(app, 'n', 0);
1192 }
1193 else
1194 winrepaint(app);
1195 }
1196 if (c == '\033')
1197 {
1198 app->issearching = 0;
1199 winrepaint(app);
1200 }
1201 }
1202 else
1203 {
1204 if (n + 2 < sizeof app->search)
1205 {
1206 app->search[n] = c;
1207 app->search[n + 1] = 0;
1208 winrepaintsearch(app);
1209 }
1210 }
1211 return;
1212 }
1213
1214 /*
1215 * Save numbers typed for later
1216 */
1217
1218 if (c >= '0' && c <= '9')
1219 {
1220 app->number[app->numberlen++] = c;
1221 app->number[app->numberlen] = '\0';
1222 }
1223
1224 switch (c)
1225 {
1226 case 'q':
1227 winclose(app);
1228 break;
1229
1230 case '<':
1231 if (app->layout_em > 6)
1232 {
1233 fz_bookmark mark = fz_make_bookmark(app->ctx, app->doc, app->pageno);
1234 app->layout_em -= 1;
1235 fz_layout_document(app->ctx, app->doc, app->layout_w, app->layout_h, app->layout_em);
1236 app->pagecount = fz_count_pages(app->ctx, app->doc);
1237 app->pageno = fz_lookup_bookmark(app->ctx, app->doc, mark);
1238 pdfapp_showpage(app, 1, 1, 1, 0, 0);
1239 }
1240 break;
1241 case '>':
1242 if (app->layout_em < 36)
1243 {
1244 fz_bookmark mark = fz_make_bookmark(app->ctx, app->doc, app->pageno);
1245 app->layout_em += 1;
1246 fz_layout_document(app->ctx, app->doc, app->layout_w, app->layout_h, app->layout_em);
1247 app->pagecount = fz_count_pages(app->ctx, app->doc);
1248 app->pageno = fz_lookup_bookmark(app->ctx, app->doc, mark);
1249 pdfapp_showpage(app, 1, 1, 1, 0, 0);
1250 }
1251 break;
1252
1253 /*
1254 * Zoom and rotate
1255 */
1256
1257 case '+':
1258 app->resolution = zoom_in(app->resolution);
1259 pdfapp_showpage(app, 0, 1, 1, 0, 0);
1260 break;
1261 case '-':
1262 app->resolution = zoom_out(app->resolution);
1263 pdfapp_showpage(app, 0, 1, 1, 0, 0);
1264 break;
1265
1266 case 'W':
1267 pdfapp_autozoom_horizontal(app);
1268 break;
1269 case 'H':
1270 pdfapp_autozoom_vertical(app);
1271 break;
1272 case 'Z':
1273 pdfapp_autozoom(app);
1274 break;
1275 case 'z':
1276 if (app->numberlen > 0)
1277 app->resolution = atoi(app->number);
1278 else
1279 app->resolution = app->default_resolution;
1280 pdfapp_showpage(app, 0, 1, 1, 0, 0);
1281 break;
1282
1283 case '[':
1284 if (app->numberlen > 0)
1285 app->rotate -= atoi(app->number);
1286 else
1287 app->rotate -= 90;
1288 pdfapp_showpage(app, 0, 1, 1, 0, 0);
1289 break;
1290 case ']':
1291 if (app->numberlen > 0)
1292 app->rotate += atoi(app->number);
1293 else
1294 app->rotate += 90;
1295 pdfapp_showpage(app, 0, 1, 1, 0, 0);
1296 break;
1297
1298 /*
1299 * Rendering and color management parameters.
1300 */
1301
1302 case 'C':
1303 app->tint ^= 1;
1304 pdfapp_showpage(app, 0, 1, 1, 0, 0);
1305 break;
1306
1307 case 'c':
1308 app->grayscale ^= 1;
1309 pdfapp_showpage(app, 0, 1, 1, 0, 0);
1310 break;
1311
1312 case 'I':
1313 app->invert ^= 1;
1314 pdfapp_showpage(app, 0, 1, 1, 0, 0);
1315 break;
1316
1317 case 'E':
1318 app->useicc ^= 1;
1319 if (app->useicc)
1320 pdfapp_warn(app, "Using icc.");
1321 else
1322 pdfapp_warn(app, "Not using icc.");
1323 pdfapp_showpage(app, 1, 1, 1, 0, 0);
1324 break;
1325
1326 case 'e':
1327 app->useseparations ^= 1;
1328 if (app->useseparations)
1329 pdfapp_warn(app, "Using separations.");
1330 else
1331 pdfapp_warn(app, "Not using separations.");
1332 pdfapp_showpage(app, 1, 1, 1, 0, 0);
1333 break;
1334
1335 case 'A':
1336 if (app->numberlen > 0)
1337 app->aalevel = atoi(app->number);
1338 else
1339 app->aalevel = (app->aalevel == 8 ? 0 : 8);
1340 pdfapp_showpage(app, 1, 1, 1, 0, 0);
1341 break;
1342
1343 /*
1344 * Pan view, but don't need to repaint image
1345 */
1346
1347 case 'f':
1348 app->shrinkwrap = 0;
1349 winfullscreen(app, !app->fullscreen);
1350 app->fullscreen = !app->fullscreen;
1351 break;
1352
1353 case 'w':
1354 if (app->fullscreen)
1355 {
1356 winfullscreen(app, 0);
1357 app->fullscreen = 0;
1358 }
1359 app->shrinkwrap = 1;
1360 app->panx = app->pany = 0;
1361 pdfapp_showpage(app, 0, 0, 1, 0, 0);
1362 break;
1363
1364 case 'h':
1365 app->panx += app->imgw / 10;
1366 pdfapp_showpage(app, 0, 0, 1, 0, 0);
1367 break;
1368
1369 case 'j':
1370 {
1371 if (app->imgh <= app->winh || app->pany <= app->winh - app->imgh)
1372 {
1373 panto = PAN_TO_TOP;
1374 app->pageno++;
1375 }
1376 else
1377 {
1378 app->pany -= app->imgh / 10;
1379 pdfapp_showpage(app, 0, 0, 1, 0, 0);
1380 }
1381 break;
1382 }
1383
1384 case 'k':
1385 {
1386 if (app->imgh <= app->winh || app->pany == 0)
1387 {
1388 panto = PAN_TO_BOTTOM;
1389 app->pageno--;
1390 }
1391 else
1392 {
1393 app->pany += app->imgh / 10;
1394 pdfapp_showpage(app, 0, 0, 1, 0, 0);
1395 }
1396 break;
1397 }
1398
1399 case 'l':
1400 app->panx -= app->imgw / 10;
1401 pdfapp_showpage(app, 0, 0, 1, 0, 0);
1402 break;
1403
1404 /*
1405 * Page navigation
1406 */
1407
1408 case 'g':
1409 if (app->numberlen > 0)
1410 pdfapp_gotopage(app, atoi(app->number));
1411 else
1412 pdfapp_gotopage(app, 1);
1413 break;
1414
1415 case 'G':
1416 pdfapp_gotopage(app, app->pagecount);
1417 break;
1418
1419 case 'm':
1420 if (app->numberlen > 0)
1421 {
1422 int idx = atoi(app->number);
1423 if (idx >= 0 && idx < nelem(app->marks))
1424 app->marks[idx] = app->pageno;
1425 }
1426 else
1427 {
1428 if (app->histlen + 1 == 256)
1429 {
1430 memmove(app->hist, app->hist + 1, sizeof(int) * 255);
1431 app->histlen --;
1432 }
1433 app->hist[app->histlen++] = app->pageno;
1434 }
1435 break;
1436
1437 case 't':
1438 if (app->numberlen > 0)
1439 {
1440 int idx = atoi(app->number);
1441
1442 if (idx >= 0 && idx < nelem(app->marks))
1443 if (app->marks[idx] > 0)
1444 app->pageno = app->marks[idx];
1445 }
1446 else if (app->histlen > 0)
1447 app->pageno = app->hist[--app->histlen];
1448 break;
1449
1450 case 'p':
1451 app->presentation_mode = !app->presentation_mode;
1452 break;
1453
1454 /*
1455 * Back and forth ...
1456 */
1457
1458 case ',':
1459 panto = DONT_PAN;
1460 if (app->numberlen > 0)
1461 app->pageno -= atoi(app->number);
1462 else
1463 app->pageno--;
1464 break;
1465
1466 case '.':
1467 panto = DONT_PAN;
1468 if (app->numberlen > 0)
1469 app->pageno += atoi(app->number);
1470 else
1471 app->pageno++;
1472 break;
1473
1474 case 'b':
1475 if (app->pany >= 0)
1476 {
1477 if (app->panx >= 0)
1478 {
1479 if (app->pageno - 1 > 0)
1480 {
1481 app->panx = INT_MIN;
1482 app->pany = INT_MIN;
1483 app->pageno--;
1484 panto = DONT_PAN;
1485 }
1486 }
1487 else
1488 {
1489 app->pany = -app->imgh;
1490 app->panx += app->winw * 9 / 10;
1491 pdfapp_showpage(app, 0, 0, 1, 0, 0);
1492 }
1493 }
1494 else
1495 {
1496 app->pany += app->winh * 9 / 10;
1497 pdfapp_showpage(app, 0, 0, 1, 0, 0);
1498 }
1499 break;
1500
1501 case ' ':
1502 if (app->imgh + app->pany <= app->winh)
1503 {
1504 if (app->imgw + app->panx <= app->winw)
1505 {
1506 if (app->pageno + 1 < app->pagecount)
1507 {
1508 app->panx = 0;
1509 app->pany = 0;
1510 app->pageno++;
1511 panto = DONT_PAN;
1512 }
1513 }
1514 else
1515 {
1516 app->pany = 0;
1517 app->panx -= app->winw * 9 / 10;
1518 pdfapp_showpage(app, 0, 0, 1, 0, 0);
1519 }
1520 }
1521 else
1522 {
1523 app->pany -= app->winh * 9 / 10;
1524 pdfapp_showpage(app, 0, 0, 1, 0, 0);
1525 }
1526 break;
1527
1528 /*
1529 * Saving the file
1530 */
1531 case 'S':
1532 pdfapp_save(app);
1533 break;
1534
1535 /*
1536 * Reloading the file...
1537 */
1538
1539 case 'r':
1540 panto = DONT_PAN;
1541 oldpage = -1;
1542 pdfapp_reloadfile(app);
1543 break;
1544
1545 /*
1546 * Searching
1547 */
1548
1549 case '?':
1550 app->issearching = 1;
1551 app->searchdir = -1;
1552 app->search[0] = 0;
1553 app->hit_count = 0;
1554 app->searchpage = -1;
1555 winrepaintsearch(app);
1556 break;
1557
1558 case '/':
1559 app->issearching = 1;
1560 app->searchdir = 1;
1561 app->search[0] = 0;
1562 app->hit_count = 0;
1563 app->searchpage = -1;
1564 winrepaintsearch(app);
1565 break;
1566
1567 case 'n':
1568 if (app->searchdir > 0)
1569 pdfapp_search_in_direction(app, &panto, 1);
1570 else
1571 pdfapp_search_in_direction(app, &panto, -1);
1572 loadpage = 0;
1573 break;
1574
1575 case 'N':
1576 if (app->searchdir > 0)
1577 pdfapp_search_in_direction(app, &panto, -1);
1578 else
1579 pdfapp_search_in_direction(app, &panto, 1);
1580 loadpage = 0;
1581 break;
1582 }
1583
1584 if (c < '0' || c > '9')
1585 app->numberlen = 0;
1586
1587 if (app->pageno < 1)
1588 app->pageno = 1;
1589 if (app->pageno > app->pagecount)
1590 app->pageno = app->pagecount;
1591
1592 if (app->pageno != oldpage)
1593 {
1594 switch (panto)
1595 {
1596 case PAN_TO_TOP:
1597 app->pany = 0;
1598 break;
1599 case PAN_TO_BOTTOM:
1600 app->pany = INT_MIN;
1601 break;
1602 case DONT_PAN:
1603 break;
1604 }
1605 pdfapp_showpage(app, loadpage, 1, 1, 1, 0);
1606 }
1607}
1608
1609static void handlescroll(pdfapp_t *app, int modifiers, int dir)
1610{
1611 app->ispanning = app->iscopying = 0;
1612 if (modifiers & (1<<2))
1613 {
1614 /* zoom in/out if ctrl is pressed */
1615 if (dir > 0)
1616 app->resolution = zoom_in(app->resolution);
1617 else
1618 app->resolution = zoom_out(app->resolution);
1619 if (app->resolution > MAXRES)
1620 app->resolution = MAXRES;
1621 if (app->resolution < MINRES)
1622 app->resolution = MINRES;
1623 pdfapp_showpage(app, 0, 1, 1, 0, 0);
1624 }
1625 else
1626 {
1627 /* scroll up/down, or left/right if
1628 shift is pressed */
1629 int xstep = 0;
1630 int ystep = 0;
1631 int pagestep = 0;
1632 if (modifiers & (1<<0))
1633 {
1634 if (dir > 0 && app->panx >= 0)
1635 pagestep = -1;
1636 else if (dir < 0 && app->panx <= app->winw - app->imgw)
1637 pagestep = 1;
1638 else
1639 xstep = 20 * dir;
1640 }
1641 else
1642 {
1643 if (dir > 0 && app->pany >= 0)
1644 pagestep = -1;
1645 else if (dir < 0 && app->pany <= app->winh - app->imgh)
1646 pagestep = 1;
1647 else
1648 ystep = 20 * dir;
1649 }
1650 if (pagestep == 0)
1651 pdfapp_panview(app, app->panx + xstep, app->pany + ystep);
1652 else if (pagestep > 0 && app->pageno < app->pagecount)
1653 {
1654 app->pageno++;
1655 app->pany = 0;
1656 pdfapp_showpage(app, 1, 1, 1, 0, 0);
1657 }
1658 else if (pagestep < 0 && app->pageno > 1)
1659 {
1660 app->pageno--;
1661 app->pany = INT_MIN;
1662 pdfapp_showpage(app, 1, 1, 1, 0, 0);
1663 }
1664 }
1665}
1666
1667void pdfapp_onmouse(pdfapp_t *app, int x, int y, int btn, int modifiers, int state)
1668{
1669 fz_context *ctx = app->ctx;
1670 fz_irect irect = { 0, 0, app->layout_w, app->layout_h };
1671 fz_link *link;
1672 fz_matrix ctm;
1673 fz_point p;
1674 int processed = 0;
1675
1676 if (app->image)
1677 irect = fz_pixmap_bbox(app->ctx, app->image);
1678 p.x = x - app->panx + irect.x0;
1679 p.y = y - app->pany + irect.y0;
1680
1681 pdfapp_viewctm(&ctm, app);
1682 ctm = fz_invert_matrix(ctm);
1683
1684 p = fz_transform_point(p, ctm);
1685
1686 for (link = app->page_links; link; link = link->next)
1687 {
1688 if (p.x >= link->rect.x0 && p.x <= link->rect.x1)
1689 if (p.y >= link->rect.y0 && p.y <= link->rect.y1)
1690 break;
1691 }
1692
1693 if (link)
1694 {
1695 wincursor(app, HAND);
1696 if (btn == 1 && state == 1 && !processed)
1697 {
1698 if (fz_is_external_link(ctx, link->uri))
1699 pdfapp_gotouri(app, link->uri);
1700 else
1701 pdfapp_gotopage(app, fz_resolve_link(ctx, app->doc, link->uri, NULL, NULL) + 1);
1702 return;
1703 }
1704 }
1705 else
1706 {
1707 wincursor(app, ARROW);
1708 }
1709
1710 if (state == 1 && !processed)
1711 {
1712 if (btn == 1 && !app->iscopying)
1713 {
1714 app->ispanning = 1;
1715 app->selx = x;
1716 app->sely = y;
1717 app->beyondy = 0;
1718 }
1719 if (btn == 3 && !app->ispanning)
1720 {
1721 app->iscopying = 1;
1722 app->selx = x;
1723 app->sely = y;
1724 app->selr.x0 = x;
1725 app->selr.x1 = x;
1726 app->selr.y0 = y;
1727 app->selr.y1 = y;
1728 }
1729 if (btn == 4 || btn == 5) /* scroll wheel */
1730 {
1731 handlescroll(app, modifiers, btn == 4 ? 1 : -1);
1732 }
1733 if (btn == 6 || btn == 7) /* scroll wheel (horizontal) */
1734 {
1735 /* scroll left/right or up/down if shift is pressed */
1736 handlescroll(app, modifiers ^ (1<<0), btn == 6 ? 1 : -1);
1737 }
1738 if (app->presentation_mode)
1739 {
1740 if (btn == 1 && app->pageno < app->pagecount)
1741 {
1742 app->pageno++;
1743 pdfapp_showpage(app, 1, 1, 1, 0, 0);
1744 }
1745 if (btn == 3 && app->pageno > 1)
1746 {
1747 app->pageno--;
1748 pdfapp_showpage(app, 1, 1, 1, 0, 0);
1749 }
1750 }
1751 }
1752
1753 else if (state == -1)
1754 {
1755 if (app->iscopying)
1756 {
1757 app->iscopying = 0;
1758 app->selr.x0 = fz_mini(app->selx, x) - app->panx + irect.x0;
1759 app->selr.x1 = fz_maxi(app->selx, x) - app->panx + irect.x0;
1760 app->selr.y0 = fz_mini(app->sely, y) - app->pany + irect.y0;
1761 app->selr.y1 = fz_maxi(app->sely, y) - app->pany + irect.y0;
1762 winrepaint(app);
1763 if (app->selr.x0 < app->selr.x1 && app->selr.y0 < app->selr.y1)
1764 windocopy(app);
1765 }
1766 app->ispanning = 0;
1767 }
1768
1769 else if (app->ispanning)
1770 {
1771 int newx = app->panx + x - app->selx;
1772 int newy = app->pany + y - app->sely;
1773 int imgh = app->winh;
1774 if (app->image)
1775 imgh = fz_pixmap_height(app->ctx, app->image);
1776
1777 /* Scrolling beyond limits implies flipping pages */
1778 /* Are we requested to scroll beyond limits? */
1779 if (newy + imgh < app->winh || newy > 0)
1780 {
1781 /* Yes. We can assume that deltay != 0 */
1782 int deltay = y - app->sely;
1783 /* Check whether the panning has occurred in the
1784 * direction that we are already crossing the
1785 * limit it. If not, we can conclude that we
1786 * have switched ends of the page and will thus
1787 * start over counting.
1788 */
1789 if( app->beyondy == 0 || (app->beyondy ^ deltay) >= 0 )
1790 {
1791 /* Updating how far we are beyond and
1792 * flipping pages if beyond threshold
1793 */
1794 app->beyondy += deltay;
1795 if (app->beyondy > BEYOND_THRESHHOLD)
1796 {
1797 if( app->pageno > 1 )
1798 {
1799 app->pageno--;
1800 pdfapp_showpage(app, 1, 1, 1, 0, 0);
1801 if (app->image)
1802 newy = -fz_pixmap_height(app->ctx, app->image);
1803 }
1804 app->beyondy = 0;
1805 }
1806 else if (app->beyondy < -BEYOND_THRESHHOLD)
1807 {
1808 if( app->pageno < app->pagecount )
1809 {
1810 app->pageno++;
1811 pdfapp_showpage(app, 1, 1, 1, 0, 0);
1812 newy = 0;
1813 }
1814 app->beyondy = 0;
1815 }
1816 }
1817 else
1818 app->beyondy = 0;
1819 }
1820 /* Although at this point we've already determined that
1821 * or that no scrolling will be performed in
1822 * y-direction, the x-direction has not yet been taken
1823 * care off. Therefore
1824 */
1825 pdfapp_panview(app, newx, newy);
1826
1827 app->selx = x;
1828 app->sely = y;
1829 }
1830
1831 else if (app->iscopying)
1832 {
1833 app->selr.x0 = fz_mini(app->selx, x) - app->panx + irect.x0;
1834 app->selr.x1 = fz_maxi(app->selx, x) - app->panx + irect.x0;
1835 app->selr.y0 = fz_mini(app->sely, y) - app->pany + irect.y0;
1836 app->selr.y1 = fz_maxi(app->sely, y) - app->pany + irect.y0;
1837 winrepaint(app);
1838 }
1839}
1840
1841void pdfapp_oncopy(pdfapp_t *app, unsigned short *ucsbuf, int ucslen)
1842{
1843 fz_matrix ctm;
1844 fz_stext_page *page = app->page_text;
1845 int p, need_newline;
1846 fz_stext_block *block;
1847 fz_stext_line *line;
1848 fz_stext_char *ch;
1849 fz_rect sel;
1850
1851 pdfapp_viewctm(&ctm, app);
1852 ctm = fz_invert_matrix(ctm);
1853 sel = fz_transform_rect(app->selr, ctm);
1854
1855 p = 0;
1856 need_newline = 0;
1857
1858 for (block = page->first_block; block; block = block->next)
1859 {
1860 if (block->type != FZ_STEXT_BLOCK_TEXT)
1861 continue;
1862
1863 for (line = block->u.t.first_line; line; line = line->next)
1864 {
1865 int saw_text = 0;
1866 for (ch = line->first_char; ch; ch = ch->next)
1867 {
1868 fz_rect bbox = fz_rect_from_quad(ch->quad);
1869 int c = ch->c;
1870 if (c < 32)
1871 c = 0xFFFD;
1872 if (bbox.x1 >= sel.x0 && bbox.x0 <= sel.x1 && bbox.y1 >= sel.y0 && bbox.y0 <= sel.y1)
1873 {
1874 saw_text = 1;
1875 if (need_newline)
1876 {
1877#ifdef _WIN32
1878 if (p < ucslen - 1)
1879 ucsbuf[p++] = '\r';
1880#endif
1881 if (p < ucslen - 1)
1882 ucsbuf[p++] = '\n';
1883 need_newline = 0;
1884 }
1885 if (p < ucslen - 1)
1886 ucsbuf[p++] = c;
1887 }
1888 }
1889 if (saw_text)
1890 need_newline = 1;
1891 }
1892 }
1893
1894 ucsbuf[p] = 0;
1895}
1896
1897void pdfapp_postblit(pdfapp_t *app)
1898{
1899 clock_t time;
1900 float seconds;
1901 int llama;
1902
1903 app->transitions_enabled = 1;
1904 if (!app->in_transit)
1905 return;
1906 time = clock();
1907 seconds = (float)(time - app->start_time) / CLOCKS_PER_SEC;
1908 llama = seconds * 256 / app->transition.duration;
1909 if (llama >= 256)
1910 {
1911 /* Completed. */
1912 fz_drop_pixmap(app->ctx, app->image);
1913 app->image = app->new_image;
1914 app->new_image = NULL;
1915 app->imgw = fz_pixmap_width(app->ctx, app->image);
1916 app->imgh = fz_pixmap_height(app->ctx, app->image);
1917 fz_drop_pixmap(app->ctx, app->old_image);
1918 app->old_image = NULL;
1919 if (app->duration != 0)
1920 winadvancetimer(app, app->duration);
1921 }
1922 else
1923 fz_generate_transition(app->ctx, app->image, app->old_image, app->new_image, llama, &app->transition);
1924 winrepaint(app);
1925 if (llama >= 256)
1926 {
1927 /* Completed. */
1928 app->in_transit = 0;
1929 }
1930}
1931