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