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