Rev 7475 | Rev 8429 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4680 | right-hear | 1 | #include "fitz.h" |
2 | #include "mupdf.h" |
||
3 | #include "pdfapp.h" |
||
4 | |||
5 | #include |
||
6 | |||
7 | #define ZOOMSTEP 1.142857 |
||
8 | #define BEYOND_THRESHHOLD 40 |
||
9 | |||
10 | enum panning |
||
11 | { |
||
12 | DONT_PAN = 0, |
||
13 | PAN_TO_TOP, |
||
14 | PAN_TO_BOTTOM |
||
15 | }; |
||
16 | |||
17 | static void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage, int repaint); |
||
18 | |||
19 | static void pdfapp_warn(pdfapp_t *app, const char *fmt, ...) |
||
20 | { |
||
21 | char buf[1024]; |
||
22 | va_list ap; |
||
23 | va_start(ap, fmt); |
||
24 | vsprintf(buf, fmt, ap); |
||
25 | va_end(ap); |
||
26 | winwarn(app, buf); |
||
27 | } |
||
28 | |||
29 | static void pdfapp_error(pdfapp_t *app, fz_error error) |
||
30 | { |
||
31 | winerror(app, error); |
||
32 | } |
||
33 | |||
34 | char *pdfapp_version(pdfapp_t *app) |
||
35 | { |
||
36 | return |
||
37 | "MuPDF 0.9\n" |
||
38 | "Copyright 2006-2011 Artifex Sofware, Inc.\n"; |
||
39 | } |
||
40 | |||
41 | char *pdfapp_usage(pdfapp_t *app) |
||
42 | { |
||
5821 | leency | 43 | return " "; |
44 | /* |
||
4680 | right-hear | 45 | "L\t\t-- rotate left\n" |
46 | "R\t\t-- rotate right\n" |
||
47 | "h\t\t-- scroll left\n" |
||
48 | "j down\t\t-- scroll down\n" |
||
49 | "k up\t\t-- scroll up\n" |
||
50 | "l\t\t-- scroll right\n" |
||
51 | "+\t\t-- zoom in\n" |
||
52 | "-\t\t-- zoom out\n" |
||
53 | "w\t\t-- shrinkwrap\n" |
||
54 | "r\t\t-- reload file\n" |
||
55 | ". pgdn right space\t-- next page\n" |
||
56 | ", pgup left b\t-- previous page\n" |
||
57 | ">\t\t-- next 10 pages\n" |
||
58 | "<\t\t-- back 10 pages\n" |
||
59 | "m\t\t-- mark page for snap back\n" |
||
60 | "t\t\t-- pop back to latest mark\n" |
||
61 | "1m\t\t-- mark page in register 1\n" |
||
62 | "1t\t\t-- go to page in register 1\n" |
||
63 | "123g\t\t-- go to page 123\n" |
||
64 | "/\t\t-- search for text\n" |
||
65 | "n\t\t-- find next search result\n" |
||
66 | "N\t\t-- find previous search result\n" |
||
67 | "c\t\t-- toggle between color and grayscale\n" |
||
7621 | leency | 68 | ; |
69 | */ |
||
4680 | right-hear | 70 | } |
71 | |||
72 | void pdfapp_init(pdfapp_t *app) |
||
73 | { |
||
74 | memset(app, 0, sizeof(pdfapp_t)); |
||
75 | app->scrw = 640; |
||
76 | app->scrh = 480; |
||
77 | app->resolution = 72; |
||
78 | } |
||
79 | |||
80 | void pdfapp_invert(pdfapp_t *app, fz_bbox rect) |
||
81 | { |
||
82 | unsigned char *p; |
||
83 | int x, y, n; |
||
84 | |||
85 | int x0 = CLAMP(rect.x0 - app->image->x, 0, app->image->w - 1); |
||
86 | int x1 = CLAMP(rect.x1 - app->image->x, 0, app->image->w - 1); |
||
87 | int y0 = CLAMP(rect.y0 - app->image->y, 0, app->image->h - 1); |
||
88 | int y1 = CLAMP(rect.y1 - app->image->y, 0, app->image->h - 1); |
||
89 | |||
90 | for (y = y0; y < y1; y++) |
||
91 | { |
||
92 | p = app->image->samples + (y * app->image->w + x0) * app->image->n; |
||
93 | for (x = x0; x < x1; x++) |
||
94 | { |
||
95 | for (n = app->image->n; n > 0; n--, p++) |
||
96 | *p = 255 - *p; |
||
97 | } |
||
98 | } |
||
99 | } |
||
100 | |||
101 | static void pdfapp_open_pdf(pdfapp_t *app, char *filename, int fd) |
||
102 | { |
||
103 | fz_error error; |
||
104 | fz_stream *file; |
||
105 | char *password = ""; |
||
106 | fz_obj *obj; |
||
107 | fz_obj *info; |
||
108 | |||
109 | /* |
||
110 | * Open PDF and load xref table |
||
111 | */ |
||
112 | __menuet__debug_out("FZ OPEN\n"); |
||
113 | //file = fz_open_fd(fd); |
||
114 | __menuet__debug_out("FZ ready\n"); |
||
115 | error = pdf_open_xref(&app->xref, filename, NULL); |
||
116 | if (error){ |
||
117 | __menuet__debug_out("FZ can't open\n"); |
||
118 | pdfapp_error(app, fz_rethrow(error, "cannot open document '%s'", filename));} |
||
119 | fz_close(file); |
||
120 | |||
121 | /* |
||
122 | * Handle encrypted PDF files |
||
123 | */ |
||
124 | /* |
||
125 | if (pdf_needs_password(app->xref)) |
||
126 | { |
||
127 | int okay = pdf_authenticate_password(app->xref, password); |
||
128 | while (!okay) |
||
129 | { |
||
130 | password = winpassword(app, filename); |
||
131 | if (!password) |
||
132 | exit(1); |
||
133 | okay = pdf_authenticate_password(app->xref, password); |
||
134 | if (!okay) |
||
135 | pdfapp_warn(app, "Invalid password."); |
||
136 | } |
||
137 | } |
||
138 | * */ |
||
139 | |||
140 | /* |
||
141 | * Load meta information |
||
142 | */ |
||
143 | |||
144 | /* |
||
145 | app->outline = pdf_load_outline(app->xref); |
||
146 | |||
147 | app->doctitle = filename; |
||
148 | if (strrchr(app->doctitle, '\\')) |
||
149 | app->doctitle = strrchr(app->doctitle, '\\') + 1; |
||
150 | if (strrchr(app->doctitle, '/')) |
||
151 | app->doctitle = strrchr(app->doctitle, '/') + 1; |
||
152 | info = fz_dict_gets(app->xref->trailer, "Info"); |
||
153 | if (info) |
||
154 | { |
||
155 | obj = fz_dict_gets(info, "Title"); |
||
156 | if (obj) |
||
157 | app->doctitle = pdf_to_utf8(obj); |
||
158 | } */ |
||
159 | |||
160 | /* |
||
161 | * Start at first page |
||
162 | */ |
||
163 | __menuet__debug_out("Start at first page\n"); |
||
164 | |||
165 | error = pdf_load_page_tree(app->xref); |
||
166 | if (error) { |
||
167 | __menuet__debug_out("Can't load tree\n"); |
||
168 | pdfapp_error(app, fz_rethrow(error, "cannot load page tree"));} |
||
169 | |||
170 | __menuet__debug_out("Page counter\n"); |
||
171 | app->pagecount = pdf_count_pages(app->xref); |
||
172 | __menuet__debug_out("All is set!\n"); |
||
173 | } |
||
174 | |||
175 | void pdfapp_open(pdfapp_t *app, char *filename, int fd, int reload) |
||
176 | { |
||
7621 | leency | 177 | pdfapp_open_pdf(app, filename, fd); |
4680 | right-hear | 178 | |
179 | app->cache = fz_new_glyph_cache(); |
||
180 | |||
181 | if (app->pageno < 1) |
||
182 | app->pageno = 1; |
||
183 | if (app->pageno > app->pagecount) |
||
184 | app->pageno = app->pagecount; |
||
185 | if (app->resolution < MINRES) |
||
186 | app->resolution = MINRES; |
||
187 | if (app->resolution > MAXRES) |
||
188 | app->resolution = MAXRES; |
||
189 | |||
190 | if (!reload) |
||
191 | { |
||
192 | app->shrinkwrap = 1; |
||
193 | app->rotate = 0; |
||
194 | app->panx = 0; |
||
195 | app->pany = 0; |
||
196 | } |
||
197 | |||
198 | pdfapp_showpage(app, 1, 1, 1); |
||
199 | } |
||
200 | |||
201 | void pdfapp_close(pdfapp_t *app) |
||
202 | { |
||
203 | if (app->cache) |
||
204 | fz_free_glyph_cache(app->cache); |
||
205 | app->cache = NULL; |
||
206 | |||
207 | if (app->image) |
||
208 | fz_drop_pixmap(app->image); |
||
209 | app->image = NULL; |
||
210 | |||
211 | if (app->outline) |
||
212 | pdf_free_outline(app->outline); |
||
213 | app->outline = NULL; |
||
214 | |||
215 | if (app->xref) |
||
216 | { |
||
217 | if (app->xref->store) |
||
218 | pdf_free_store(app->xref->store); |
||
219 | app->xref->store = NULL; |
||
220 | |||
221 | pdf_free_xref(app->xref); |
||
222 | app->xref = NULL; |
||
223 | } |
||
224 | |||
225 | fz_flush_warnings(); |
||
226 | } |
||
227 | |||
228 | static fz_matrix pdfapp_viewctm(pdfapp_t *app) |
||
229 | { |
||
230 | fz_matrix ctm; |
||
231 | ctm = fz_identity; |
||
232 | ctm = fz_concat(ctm, fz_translate(0, -app->page_bbox.y1)); |
||
233 | if (app->xref) |
||
234 | ctm = fz_concat(ctm, fz_scale(app->resolution/72.0f, -app->resolution/72.0f)); |
||
235 | else |
||
236 | ctm = fz_concat(ctm, fz_scale(app->resolution/96.0f, app->resolution/96.0f)); |
||
237 | ctm = fz_concat(ctm, fz_rotate(app->rotate + app->page_rotate)); |
||
238 | return ctm; |
||
239 | } |
||
240 | |||
241 | static void pdfapp_panview(pdfapp_t *app, int newx, int newy) |
||
242 | { |
||
243 | if (newx < 0) |
||
244 | newx = 0; |
||
245 | if (newy < 0) |
||
246 | newy = 0; |
||
247 | |||
248 | if (newx + app->image->w < app->winw) |
||
249 | newx = app->winw - app->image->w; |
||
250 | if (newy + app->image->h < app->winh) |
||
251 | newy = app->winh - app->image->h; |
||
252 | |||
253 | if (app->winw >= app->image->w) |
||
254 | newx = (app->winw - app->image->w) / 2; |
||
255 | if (app->winh >= app->image->h) |
||
256 | newy = (app->winh - app->image->h) / 2; |
||
257 | |||
258 | if (newx != app->panx || newy != app->pany) |
||
259 | winrepaint(app); |
||
260 | |||
261 | if (newy > app->image->h) { |
||
262 | |||
263 | app->pageno++; |
||
264 | |||
265 | |||
266 | if (app->pageno > app->pagecount) |
||
267 | app->pageno = app->pagecount; |
||
268 | |||
269 | |||
270 | newy = 0; |
||
271 | app->pany = newy; |
||
272 | |||
273 | pdfapp_showpage(app, 1, 1, 1); |
||
274 | } |
||
275 | |||
276 | app->panx = newx; |
||
277 | app->pany = newy; |
||
278 | } |
||
279 | |||
280 | static void pdfapp_loadpage_pdf(pdfapp_t *app) |
||
281 | { |
||
282 | pdf_page *page; |
||
283 | fz_error error; |
||
284 | fz_device *mdev; |
||
285 | |||
286 | error = pdf_load_page(&page, app->xref, app->pageno - 1); |
||
287 | if (error) |
||
288 | pdfapp_error(app, error); |
||
289 | |||
290 | app->page_bbox = page->mediabox; |
||
291 | app->page_rotate = page->rotate; |
||
292 | app->page_links = page->links; |
||
293 | page->links = NULL; |
||
294 | |||
295 | /* Create display list */ |
||
296 | app->page_list = fz_new_display_list(); |
||
297 | mdev = fz_new_list_device(app->page_list); |
||
298 | error = pdf_run_page(app->xref, page, mdev, fz_identity); |
||
299 | if (error) |
||
300 | { |
||
301 | error = fz_rethrow(error, "cannot draw page %d in '%s'", app->pageno, app->doctitle); |
||
302 | pdfapp_error(app, error); |
||
303 | } |
||
304 | fz_free_device(mdev); |
||
305 | |||
306 | pdf_free_page(page); |
||
307 | |||
308 | pdf_age_store(app->xref->store, 3); |
||
309 | } |
||
310 | |||
311 | static void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage, int repaint) |
||
312 | { |
||
313 | char buf[256]; |
||
314 | fz_device *idev; |
||
315 | fz_device *tdev; |
||
316 | fz_colorspace *colorspace; |
||
317 | fz_matrix ctm; |
||
318 | fz_bbox bbox; |
||
319 | |||
320 | wincursor(app, WAIT); |
||
321 | |||
322 | if (loadpage) |
||
323 | { |
||
324 | if (app->page_list) |
||
325 | fz_free_display_list(app->page_list); |
||
326 | if (app->page_text) |
||
327 | fz_free_text_span(app->page_text); |
||
328 | if (app->page_links) |
||
329 | pdf_free_link(app->page_links); |
||
330 | |||
331 | if (app->xref) |
||
332 | pdfapp_loadpage_pdf(app); |
||
333 | |||
334 | /* Zero search hit position */ |
||
335 | app->hit = -1; |
||
336 | app->hitlen = 0; |
||
337 | |||
338 | /* Extract text */ |
||
339 | app->page_text = fz_new_text_span(); |
||
340 | tdev = fz_new_text_device(app->page_text); |
||
341 | fz_execute_display_list(app->page_list, tdev, fz_identity, fz_infinite_bbox); |
||
342 | fz_free_device(tdev); |
||
343 | } |
||
344 | |||
345 | if (drawpage) |
||
346 | { |
||
7621 | leency | 347 | sprintf(buf, "%s - %d/%d (%d dpi)", app->doctitle, |
348 | app->pageno, app->pagecount, app->resolution); |
||
349 | wintitle(app, buf); |
||
4680 | right-hear | 350 | |
351 | ctm = pdfapp_viewctm(app); |
||
352 | bbox = fz_round_rect(fz_transform_rect(ctm, app->page_bbox)); |
||
353 | |||
354 | /* Draw */ |
||
355 | if (app->image) |
||
356 | fz_drop_pixmap(app->image); |
||
357 | if (app->grayscale) |
||
358 | colorspace = fz_device_gray; |
||
359 | else |
||
360 | //#ifdef _WIN32 |
||
361 | colorspace = fz_device_bgr; |
||
362 | //#else |
||
363 | // colorspace = fz_device_rgb; |
||
364 | //#endif |
||
365 | app->image = fz_new_pixmap_with_rect(colorspace, bbox); |
||
366 | fz_clear_pixmap_with_color(app->image, 255); |
||
367 | idev = fz_new_draw_device(app->cache, app->image); |
||
368 | fz_execute_display_list(app->page_list, idev, ctm, bbox); |
||
369 | fz_free_device(idev); |
||
370 | } |
||
371 | |||
372 | if (repaint) |
||
373 | { |
||
374 | pdfapp_panview(app, app->panx, app->pany); |
||
375 | |||
376 | if (app->shrinkwrap) |
||
377 | { |
||
7621 | leency | 378 | __menuet__debug_out ("SHRINK\n"); |
4680 | right-hear | 379 | int w = app->image->w; |
380 | int h = app->image->h; |
||
381 | if (app->winw == w) |
||
382 | app->panx = 0; |
||
383 | if (app->winh == h) |
||
384 | app->pany = 0; |
||
385 | if (w > app->scrw * 90 / 100) |
||
386 | w = app->scrw * 90 / 100; |
||
387 | if (h > app->scrh * 90 / 100) |
||
388 | h = app->scrh * 90 / 100; |
||
389 | if (w != app->winw || h != app->winh) |
||
390 | winresize(app, w, h); |
||
391 | } |
||
392 | |||
393 | winrepaint(app); |
||
394 | |||
395 | wincursor(app, ARROW); |
||
396 | } |
||
397 | |||
398 | fz_flush_warnings(); |
||
399 | } |
||
400 | |||
401 | static void pdfapp_gotouri(pdfapp_t *app, fz_obj *uri) |
||
402 | { |
||
403 | char *buf; |
||
404 | buf = fz_malloc(fz_to_str_len(uri) + 1); |
||
405 | memcpy(buf, fz_to_str_buf(uri), fz_to_str_len(uri)); |
||
406 | buf[fz_to_str_len(uri)] = 0; |
||
407 | winopenuri(app, buf); |
||
408 | fz_free(buf); |
||
409 | } |
||
410 | |||
411 | static void pdfapp_gotopage(pdfapp_t *app, fz_obj *obj) |
||
412 | { |
||
413 | int number; |
||
414 | |||
415 | number = pdf_find_page_number(app->xref, obj); |
||
416 | if (number < 0) |
||
417 | return; |
||
418 | |||
419 | if (app->histlen + 1 == 256) |
||
420 | { |
||
421 | memmove(app->hist, app->hist + 1, sizeof(int) * 255); |
||
422 | app->histlen --; |
||
423 | } |
||
424 | app->hist[app->histlen++] = app->pageno; |
||
425 | app->pageno = number + 1; |
||
426 | pdfapp_showpage(app, 1, 1, 1); |
||
427 | } |
||
428 | |||
429 | static inline fz_bbox bboxcharat(fz_text_span *span, int idx) |
||
430 | { |
||
431 | int ofs = 0; |
||
432 | while (span) |
||
433 | { |
||
434 | if (idx < ofs + span->len) |
||
435 | return span->text[idx - ofs].bbox; |
||
436 | if (span->eol) |
||
437 | { |
||
438 | if (idx == ofs + span->len) |
||
439 | return fz_empty_bbox; |
||
440 | ofs ++; |
||
441 | } |
||
442 | ofs += span->len; |
||
443 | span = span->next; |
||
444 | } |
||
445 | return fz_empty_bbox; |
||
446 | } |
||
447 | |||
448 | void pdfapp_inverthit(pdfapp_t *app) |
||
449 | { |
||
450 | fz_bbox hitbox, bbox; |
||
451 | fz_matrix ctm; |
||
452 | int i; |
||
453 | |||
454 | if (app->hit < 0) |
||
455 | return; |
||
456 | |||
457 | hitbox = fz_empty_bbox; |
||
458 | ctm = pdfapp_viewctm(app); |
||
459 | |||
460 | for (i = app->hit; i < app->hit + app->hitlen; i++) |
||
461 | { |
||
462 | bbox = bboxcharat(app->page_text, i); |
||
463 | if (fz_is_empty_rect(bbox)) |
||
464 | { |
||
465 | if (!fz_is_empty_rect(hitbox)) |
||
466 | pdfapp_invert(app, fz_transform_bbox(ctm, hitbox)); |
||
467 | hitbox = fz_empty_bbox; |
||
468 | } |
||
469 | else |
||
470 | { |
||
471 | hitbox = fz_union_bbox(hitbox, bbox); |
||
472 | } |
||
473 | } |
||
474 | |||
475 | if (!fz_is_empty_rect(hitbox)) |
||
476 | pdfapp_invert(app, fz_transform_bbox(ctm, hitbox)); |
||
477 | } |
||
478 | |||
479 | static inline int charat(fz_text_span *span, int idx) |
||
480 | { |
||
481 | int ofs = 0; |
||
482 | while (span) |
||
483 | { |
||
484 | if (idx < ofs + span->len) |
||
485 | return span->text[idx - ofs].c; |
||
486 | if (span->eol) |
||
487 | { |
||
488 | if (idx == ofs + span->len) |
||
489 | return ' '; |
||
490 | ofs ++; |
||
491 | } |
||
492 | ofs += span->len; |
||
493 | span = span->next; |
||
494 | } |
||
495 | return 0; |
||
496 | } |
||
497 | |||
498 | static int textlen(fz_text_span *span) |
||
499 | { |
||
500 | int len = 0; |
||
501 | while (span) |
||
502 | { |
||
503 | len += span->len; |
||
504 | if (span->eol) |
||
505 | len ++; |
||
506 | span = span->next; |
||
507 | } |
||
508 | return len; |
||
509 | } |
||
510 | |||
511 | static int match(char *s, fz_text_span *span, int n) |
||
512 | { |
||
513 | int orig = n; |
||
514 | int c; |
||
515 | while ((c = *s++)) |
||
516 | { |
||
517 | if (c == ' ' && charat(span, n) == ' ') |
||
518 | { |
||
519 | while (charat(span, n) == ' ') |
||
520 | n++; |
||
521 | } |
||
522 | else |
||
523 | { |
||
524 | if (tolower(c) != tolower(charat(span, n))) |
||
525 | return 0; |
||
526 | n++; |
||
527 | } |
||
528 | } |
||
529 | return n - orig; |
||
530 | } |
||
531 | |||
532 | static void pdfapp_searchforward(pdfapp_t *app, enum panning *panto) |
||
533 | { |
||
534 | int matchlen; |
||
535 | int test; |
||
536 | int len; |
||
537 | int startpage; |
||
538 | |||
539 | wincursor(app, WAIT); |
||
540 | |||
541 | startpage = app->pageno; |
||
542 | |||
543 | do |
||
544 | { |
||
545 | len = textlen(app->page_text); |
||
546 | |||
547 | if (app->hit >= 0) |
||
548 | test = app->hit + strlen(app->search); |
||
549 | else |
||
550 | test = 0; |
||
551 | |||
552 | while (test < len) |
||
553 | { |
||
554 | matchlen = match(app->search, app->page_text, test); |
||
555 | if (matchlen) |
||
556 | { |
||
557 | app->hit = test; |
||
558 | app->hitlen = matchlen; |
||
559 | wincursor(app, HAND); |
||
560 | winrepaint(app); |
||
561 | return; |
||
562 | } |
||
563 | test++; |
||
564 | } |
||
565 | |||
566 | app->pageno++; |
||
567 | if (app->pageno > app->pagecount) |
||
568 | app->pageno = 1; |
||
569 | |||
570 | pdfapp_showpage(app, 1, 0, 0); |
||
571 | *panto = PAN_TO_TOP; |
||
572 | |||
573 | } while (app->pageno != startpage); |
||
574 | |||
575 | if (app->pageno == startpage) |
||
576 | { |
||
577 | pdfapp_warn(app, "String '%s' not found.", app->search); |
||
578 | winrepaintsearch(app); |
||
579 | } |
||
580 | else |
||
581 | winrepaint(app); |
||
582 | |||
583 | wincursor(app, HAND); |
||
584 | } |
||
585 | |||
586 | static void pdfapp_searchbackward(pdfapp_t *app, enum panning *panto) |
||
587 | { |
||
588 | int matchlen; |
||
589 | int test; |
||
590 | int len; |
||
591 | int startpage; |
||
592 | |||
593 | wincursor(app, WAIT); |
||
594 | |||
595 | startpage = app->pageno; |
||
596 | |||
597 | do |
||
598 | { |
||
599 | len = textlen(app->page_text); |
||
600 | |||
601 | if (app->hit >= 0) |
||
602 | test = app->hit - 1; |
||
603 | else |
||
604 | test = len; |
||
605 | |||
606 | while (test >= 0) |
||
607 | { |
||
608 | matchlen = match(app->search, app->page_text, test); |
||
609 | if (matchlen) |
||
610 | { |
||
611 | app->hit = test; |
||
612 | app->hitlen = matchlen; |
||
613 | wincursor(app, HAND); |
||
614 | winrepaint(app); |
||
615 | return; |
||
616 | } |
||
617 | test--; |
||
618 | } |
||
619 | |||
620 | app->pageno--; |
||
621 | if (app->pageno < 1) |
||
622 | app->pageno = app->pagecount; |
||
623 | |||
624 | pdfapp_showpage(app, 1, 0, 0); |
||
625 | *panto = PAN_TO_BOTTOM; |
||
626 | |||
627 | } while (app->pageno != startpage); |
||
628 | |||
629 | if (app->pageno == startpage) |
||
630 | { |
||
631 | pdfapp_warn(app, "String '%s' not found.", app->search); |
||
632 | winrepaintsearch(app); |
||
633 | } |
||
634 | else |
||
635 | winrepaint(app); |
||
636 | |||
637 | wincursor(app, HAND); |
||
638 | } |
||
639 | |||
640 | void pdfapp_onresize(pdfapp_t *app, int w, int h) |
||
641 | { |
||
642 | if (app->winw != w || app->winh != h) |
||
643 | { |
||
644 | app->winw = w; |
||
645 | app->winh = h; |
||
646 | pdfapp_panview(app, app->panx, app->pany); |
||
647 | winrepaint(app); |
||
648 | } |
||
649 | } |
||
650 | |||
651 | void pdfapp_onkey(pdfapp_t *app, int c) |
||
652 | { |
||
653 | int oldpage = app->pageno; |
||
654 | enum panning panto = PAN_TO_TOP; |
||
655 | int loadpage = 1; |
||
656 | |||
657 | if (app->isediting) |
||
658 | { |
||
659 | int n = strlen(app->search); |
||
660 | if (c < ' ') |
||
661 | { |
||
662 | if (c == '\b' && n > 0) |
||
663 | { |
||
664 | app->search[n - 1] = 0; |
||
665 | winrepaintsearch(app); |
||
666 | } |
||
667 | if (c == '\n' || c == '\r') |
||
668 | { |
||
669 | app->isediting = 0; |
||
670 | if (n > 0) |
||
671 | { |
||
672 | winrepaintsearch(app); |
||
673 | pdfapp_onkey(app, 'n'); |
||
674 | } |
||
675 | else |
||
676 | winrepaint(app); |
||
677 | } |
||
678 | if (c == '\033') |
||
679 | { |
||
680 | app->isediting = 0; |
||
681 | winrepaint(app); |
||
682 | } |
||
683 | } |
||
684 | else |
||
685 | { |
||
686 | if (n + 2 < sizeof app->search) |
||
687 | { |
||
688 | app->search[n] = c; |
||
689 | app->search[n + 1] = 0; |
||
690 | winrepaintsearch(app); |
||
691 | } |
||
692 | } |
||
693 | return; |
||
694 | } |
||
695 | |||
696 | /* |
||
697 | * Save numbers typed for later |
||
698 | */ |
||
699 | |||
700 | if (c >= '0' && c <= '9') |
||
701 | { |
||
702 | app->number[app->numberlen++] = c; |
||
703 | app->number[app->numberlen] = '\0'; |
||
704 | } |
||
705 | |||
706 | switch (c) |
||
707 | { |
||
708 | |||
709 | case '?': |
||
710 | winhelp(app); |
||
711 | break; |
||
712 | |||
713 | case 'q': |
||
714 | winclose(app); |
||
715 | break; |
||
716 | |||
717 | /* |
||
718 | * Zoom and rotate |
||
719 | */ |
||
720 | |||
721 | case '+': |
||
722 | case '=': |
||
723 | app->resolution *= ZOOMSTEP; |
||
724 | if (app->resolution > MAXRES) |
||
725 | app->resolution = MAXRES; |
||
726 | pdfapp_showpage(app, 0, 1, 1); |
||
727 | break; |
||
728 | case '-': |
||
729 | app->resolution /= ZOOMSTEP; |
||
730 | if (app->resolution < MINRES) |
||
731 | app->resolution = MINRES; |
||
732 | pdfapp_showpage(app, 0, 1, 1); |
||
733 | break; |
||
734 | |||
735 | case 'L': |
||
736 | app->rotate -= 90; |
||
737 | pdfapp_showpage(app, 0, 1, 1); |
||
738 | break; |
||
739 | case 'R': |
||
740 | app->rotate += 90; |
||
741 | pdfapp_showpage(app, 0, 1, 1); |
||
742 | break; |
||
743 | |||
744 | case 'c': |
||
745 | app->grayscale ^= 1; |
||
746 | pdfapp_showpage(app, 0, 1, 1); |
||
747 | break; |
||
748 | |||
749 | #ifndef NDEBUG |
||
750 | case 'a': |
||
751 | app->rotate -= 15; |
||
752 | pdfapp_showpage(app, 0, 1, 1); |
||
753 | break; |
||
754 | case 's': |
||
755 | app->rotate += 15; |
||
756 | pdfapp_showpage(app, 0, 1, 1); |
||
757 | break; |
||
758 | #endif |
||
759 | |||
760 | /* |
||
761 | * Pan view, but dont need to repaint image |
||
762 | */ |
||
763 | |||
764 | case 'w': |
||
765 | app->shrinkwrap = 1; |
||
766 | app->panx = app->pany = 0; |
||
767 | pdfapp_showpage(app, 0, 0, 1); |
||
768 | break; |
||
769 | |||
770 | case 'h': |
||
771 | app->panx += app->image->w / 10; |
||
772 | pdfapp_showpage(app, 0, 0, 1); |
||
773 | break; |
||
774 | |||
775 | case 'j': |
||
776 | app->pany -= app->image->h / 10; |
||
777 | pdfapp_showpage(app, 0, 0, 1); |
||
778 | break; |
||
779 | |||
780 | case 'k': |
||
781 | app->pany += app->image->h / 10; |
||
782 | pdfapp_showpage(app, 0, 0, 1); |
||
783 | break; |
||
784 | |||
785 | case 'l': |
||
786 | app->panx -= app->image->w / 10; |
||
787 | pdfapp_showpage(app, 0, 0, 1); |
||
788 | break; |
||
789 | |||
790 | /* |
||
791 | * Page navigation |
||
792 | */ |
||
793 | |||
794 | case 'g': |
||
795 | case '\n': |
||
796 | case '\r': |
||
797 | if (app->numberlen > 0) |
||
798 | app->pageno = atoi(app->number); |
||
799 | else |
||
800 | app->pageno = 1; |
||
801 | break; |
||
802 | |||
803 | case 'G': |
||
804 | app->pageno = app->pagecount; |
||
805 | break; |
||
806 | |||
807 | case 'm': |
||
808 | if (app->numberlen > 0) |
||
809 | { |
||
810 | int idx = atoi(app->number); |
||
811 | |||
812 | if (idx >= 0 && idx < nelem(app->marks)) |
||
813 | app->marks[idx] = app->pageno; |
||
814 | } |
||
815 | else |
||
816 | { |
||
817 | if (app->histlen + 1 == 256) |
||
818 | { |
||
819 | memmove(app->hist, app->hist + 1, sizeof(int) * 255); |
||
820 | app->histlen --; |
||
821 | } |
||
822 | app->hist[app->histlen++] = app->pageno; |
||
823 | } |
||
824 | break; |
||
825 | |||
826 | case 't': |
||
827 | if (app->numberlen > 0) |
||
828 | { |
||
829 | int idx = atoi(app->number); |
||
830 | |||
831 | if (idx >= 0 && idx < nelem(app->marks)) |
||
832 | if (app->marks[idx] > 0) |
||
833 | app->pageno = app->marks[idx]; |
||
834 | } |
||
835 | else if (app->histlen > 0) |
||
836 | app->pageno = app->hist[--app->histlen]; |
||
837 | break; |
||
838 | |||
839 | /* |
||
840 | * Back and forth ... |
||
841 | */ |
||
842 | |||
843 | case ',': |
||
844 | panto = PAN_TO_BOTTOM; |
||
845 | if (app->numberlen > 0) |
||
846 | app->pageno -= atoi(app->number); |
||
847 | else |
||
848 | app->pageno--; |
||
849 | break; |
||
850 | |||
851 | case '.': |
||
852 | panto = PAN_TO_TOP; |
||
853 | if (app->numberlen > 0) |
||
854 | app->pageno += atoi(app->number); |
||
855 | else |
||
856 | app->pageno++; |
||
857 | break; |
||
858 | |||
859 | case 'b': |
||
860 | panto = DONT_PAN; |
||
861 | if (app->numberlen > 0) |
||
862 | app->pageno -= atoi(app->number); |
||
863 | else |
||
864 | app->pageno--; |
||
865 | break; |
||
866 | |||
867 | case ' ': |
||
868 | panto = DONT_PAN; |
||
869 | if (app->numberlen > 0) |
||
870 | app->pageno += atoi(app->number); |
||
871 | else |
||
872 | app->pageno++; |
||
873 | break; |
||
874 | |||
875 | case ']': |
||
876 | panto = PAN_TO_TOP; |
||
877 | app->pageno++; |
||
878 | break; |
||
879 | |||
880 | case '[': |
||
881 | panto = PAN_TO_TOP; |
||
882 | app->pageno--; |
||
883 | break; |
||
884 | |||
885 | |||
886 | case '<': |
||
887 | panto = PAN_TO_TOP; |
||
888 | app->pageno -= 10; |
||
889 | break; |
||
890 | case '>': |
||
891 | panto = PAN_TO_TOP; |
||
892 | app->pageno += 10; |
||
893 | break; |
||
894 | |||
895 | /* |
||
896 | * Reloading the file... |
||
897 | */ |
||
898 | |||
899 | case 'r': |
||
900 | panto = DONT_PAN; |
||
901 | oldpage = -1; |
||
902 | winreloadfile(app); |
||
903 | break; |
||
904 | |||
905 | /* |
||
906 | * Searching |
||
907 | */ |
||
908 | |||
909 | case '/': |
||
910 | app->isediting = 1; |
||
911 | app->search[0] = 0; |
||
912 | app->hit = -1; |
||
913 | app->hitlen = 0; |
||
914 | winrepaintsearch(app); |
||
915 | break; |
||
916 | |||
917 | case 'n': |
||
918 | pdfapp_searchforward(app, &panto); |
||
919 | loadpage = 0; |
||
920 | break; |
||
921 | |||
922 | case 'N': |
||
923 | pdfapp_searchbackward(app, &panto); |
||
924 | loadpage = 0; |
||
925 | break; |
||
926 | |||
927 | } |
||
928 | |||
929 | if (c < '0' || c > '9') |
||
930 | app->numberlen = 0; |
||
931 | |||
932 | if (app->pageno < 1) |
||
933 | app->pageno = 1; |
||
934 | if (app->pageno > app->pagecount) |
||
935 | app->pageno = app->pagecount; |
||
936 | |||
937 | if (app->pageno != oldpage) |
||
938 | { |
||
939 | switch (panto) |
||
940 | { |
||
941 | case PAN_TO_TOP: |
||
942 | app->pany = 0; |
||
943 | break; |
||
944 | case PAN_TO_BOTTOM: |
||
945 | app->pany = -2000; |
||
946 | break; |
||
947 | case DONT_PAN: |
||
948 | break; |
||
949 | } |
||
950 | pdfapp_showpage(app, loadpage, 1, 1); |
||
951 | } |
||
952 | } |
||
953 | |||
954 | void pdfapp_onmouse(pdfapp_t *app, int x, int y, int btn, int modifiers, int state) |
||
955 | { |
||
956 | pdf_link *link; |
||
957 | fz_matrix ctm; |
||
958 | fz_point p; |
||
959 | |||
960 | p.x = x - app->panx + app->image->x; |
||
961 | p.y = y - app->pany + app->image->y; |
||
962 | |||
963 | ctm = pdfapp_viewctm(app); |
||
964 | ctm = fz_invert_matrix(ctm); |
||
965 | |||
966 | p = fz_transform_point(ctm, p); |
||
967 | |||
968 | for (link = app->page_links; link; link = link->next) |
||
969 | { |
||
970 | if (p.x >= link->rect.x0 && p.x <= link->rect.x1) |
||
971 | if (p.y >= link->rect.y0 && p.y <= link->rect.y1) |
||
972 | break; |
||
973 | } |
||
974 | |||
975 | if (link) |
||
976 | { |
||
977 | wincursor(app, HAND); |
||
978 | if (btn == 1 && state == 1) |
||
979 | { |
||
980 | if (link->kind == PDF_LINK_URI) |
||
981 | pdfapp_gotouri(app, link->dest); |
||
982 | else if (link->kind == PDF_LINK_GOTO) |
||
983 | pdfapp_gotopage(app, fz_array_get(link->dest, 0)); /* [ pageobj ... ] */ |
||
984 | return; |
||
985 | } |
||
986 | } |
||
987 | else |
||
988 | { |
||
989 | wincursor(app, ARROW); |
||
990 | } |
||
991 | |||
992 | if (state == 1) |
||
993 | { |
||
994 | if (btn == 1 && !app->iscopying) |
||
995 | { |
||
996 | app->ispanning = 1; |
||
997 | app->selx = x; |
||
998 | app->sely = y; |
||
999 | app->beyondy = 0; |
||
1000 | } |
||
1001 | if (btn == 3 && !app->ispanning) |
||
1002 | { |
||
1003 | app->iscopying = 1; |
||
1004 | app->selx = x; |
||
1005 | app->sely = y; |
||
1006 | app->selr.x0 = x; |
||
1007 | app->selr.x1 = x; |
||
1008 | app->selr.y0 = y; |
||
1009 | app->selr.y1 = y; |
||
1010 | } |
||
1011 | if (btn == 4 || btn == 5) /* scroll wheel */ |
||
1012 | { |
||
1013 | int dir = btn == 4 ? 1 : -1; |
||
1014 | app->ispanning = app->iscopying = 0; |
||
1015 | if (modifiers & (1<<2)) |
||
1016 | { |
||
1017 | /* zoom in/out if ctrl is pressed */ |
||
1018 | if (dir > 0) |
||
1019 | app->resolution *= ZOOMSTEP; |
||
1020 | else |
||
1021 | app->resolution /= ZOOMSTEP; |
||
1022 | if (app->resolution > MAXRES) |
||
1023 | app->resolution = MAXRES; |
||
1024 | if (app->resolution < MINRES) |
||
1025 | app->resolution = MINRES; |
||
1026 | pdfapp_showpage(app, 0, 1, 1); |
||
1027 | } |
||
1028 | else |
||
1029 | { |
||
1030 | /* scroll up/down, or left/right if |
||
1031 | shift is pressed */ |
||
1032 | int isx = (modifiers & (1<<0)); |
||
1033 | int xstep = isx ? 20 * dir : 0; |
||
1034 | int ystep = !isx ? 20 * dir : 0; |
||
1035 | pdfapp_panview(app, app->panx + xstep, app->pany + ystep); |
||
1036 | } |
||
1037 | } |
||
1038 | } |
||
1039 | |||
1040 | else if (state == -1) |
||
1041 | { |
||
1042 | if (app->iscopying) |
||
1043 | { |
||
1044 | app->iscopying = 0; |
||
1045 | app->selr.x0 = MIN(app->selx, x) - app->panx + app->image->x; |
||
1046 | app->selr.x1 = MAX(app->selx, x) - app->panx + app->image->x; |
||
1047 | app->selr.y0 = MIN(app->sely, y) - app->pany + app->image->y; |
||
1048 | app->selr.y1 = MAX(app->sely, y) - app->pany + app->image->y; |
||
1049 | winrepaint(app); |
||
1050 | if (app->selr.x0 < app->selr.x1 && app->selr.y0 < app->selr.y1) |
||
1051 | windocopy(app); |
||
1052 | } |
||
1053 | if (app->ispanning) |
||
1054 | app->ispanning = 0; |
||
1055 | } |
||
1056 | |||
1057 | else if (app->ispanning) |
||
1058 | { |
||
1059 | int newx = app->panx + x - app->selx; |
||
1060 | int newy = app->pany + y - app->sely; |
||
1061 | /* Scrolling beyond limits implies flipping pages */ |
||
1062 | /* Are we requested to scroll beyond limits? */ |
||
1063 | if (newy + app->image->h < app->winh || newy > 0) |
||
1064 | { |
||
1065 | /* Yes. We can assume that deltay != 0 */ |
||
1066 | int deltay = y - app->sely; |
||
1067 | /* Check whether the panning has occured in the |
||
1068 | * direction that we are already crossing the |
||
1069 | * limit it. If not, we can conclude that we |
||
1070 | * have switched ends of the page and will thus |
||
1071 | * start over counting. |
||
1072 | */ |
||
1073 | if( app->beyondy == 0 || (app->beyondy ^ deltay) >= 0 ) |
||
1074 | { |
||
1075 | /* Updating how far we are beyond and |
||
1076 | * flipping pages if beyond threshhold |
||
1077 | */ |
||
1078 | app->beyondy += deltay; |
||
1079 | if (app->beyondy > BEYOND_THRESHHOLD) |
||
1080 | { |
||
1081 | if( app->pageno > 1 ) |
||
1082 | { |
||
1083 | app->pageno--; |
||
1084 | pdfapp_showpage(app, 1, 1, 1); |
||
1085 | newy = -app->image->h; |
||
1086 | } |
||
1087 | app->beyondy = 0; |
||
1088 | } |
||
1089 | else if (app->beyondy < -BEYOND_THRESHHOLD) |
||
1090 | { |
||
1091 | if( app->pageno < app->pagecount ) |
||
1092 | { |
||
1093 | app->pageno++; |
||
1094 | pdfapp_showpage(app, 1, 1, 1); |
||
1095 | newy = 0; |
||
1096 | } |
||
1097 | app->beyondy = 0; |
||
1098 | } |
||
1099 | } |
||
1100 | else |
||
1101 | app->beyondy = 0; |
||
1102 | } |
||
1103 | /* Although at this point we've already determined that |
||
1104 | * or that no scrolling will be performed in |
||
1105 | * y-direction, the x-direction has not yet been taken |
||
1106 | * care off. Therefore |
||
1107 | */ |
||
1108 | pdfapp_panview(app, newx, newy); |
||
1109 | |||
1110 | app->selx = x; |
||
1111 | app->sely = y; |
||
1112 | } |
||
1113 | |||
1114 | else if (app->iscopying) |
||
1115 | { |
||
1116 | app->selr.x0 = MIN(app->selx, x) - app->panx + app->image->x; |
||
1117 | app->selr.x1 = MAX(app->selx, x) - app->panx + app->image->x; |
||
1118 | app->selr.y0 = MIN(app->sely, y) - app->pany + app->image->y; |
||
1119 | app->selr.y1 = MAX(app->sely, y) - app->pany + app->image->y; |
||
1120 | winrepaint(app); |
||
1121 | } |
||
1122 | |||
1123 | } |
||
1124 | |||
1125 | void pdfapp_oncopy(pdfapp_t *app, unsigned short *ucsbuf, int ucslen) |
||
1126 | { |
||
1127 | fz_bbox hitbox; |
||
1128 | fz_matrix ctm; |
||
1129 | fz_text_span *span; |
||
1130 | int c, i, p; |
||
1131 | int seen; |
||
1132 | |||
1133 | int x0 = app->selr.x0; |
||
1134 | int x1 = app->selr.x1; |
||
1135 | int y0 = app->selr.y0; |
||
1136 | int y1 = app->selr.y1; |
||
1137 | |||
1138 | ctm = pdfapp_viewctm(app); |
||
1139 | |||
1140 | p = 0; |
||
1141 | for (span = app->page_text; span; span = span->next) |
||
1142 | { |
||
1143 | seen = 0; |
||
1144 | |||
1145 | for (i = 0; i < span->len; i++) |
||
1146 | { |
||
1147 | hitbox = fz_transform_bbox(ctm, span->text[i].bbox); |
||
1148 | c = span->text[i].c; |
||
1149 | if (c < 32) |
||
1150 | c = '?'; |
||
1151 | if (hitbox.x1 >= x0 && hitbox.x0 <= x1 && hitbox.y1 >= y0 && hitbox.y0 <= y1) |
||
1152 | { |
||
1153 | if (p < ucslen - 1) |
||
1154 | ucsbuf[p++] = c; |
||
1155 | seen = 1; |
||
1156 | } |
||
1157 | } |
||
1158 | |||
1159 | if (seen && span->eol) |
||
1160 | { |
||
1161 | #ifdef _WIN32 |
||
1162 | if (p < ucslen - 1) |
||
1163 | ucsbuf[p++] = '\r'; |
||
1164 | #endif |
||
1165 | if (p < ucslen - 1) |
||
1166 | ucsbuf[p++] = '\n'; |
||
1167 | } |
||
1168 | } |
||
1169 | |||
1170 | ucsbuf[p] = 0; |
||
1171 | }>>>=>=>>>>>>>>0)); |