Subversion Repositories Kolibri OS

Rev

Rev 7475 | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. #include "fitz.h"
  2. #include "mupdf.h"
  3. #include "pdfapp.h"
  4.  
  5. #include <ctype.h> /* for tolower() */
  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. {
  43.         return " ";
  44.         /*
  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.  
  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. {
  177.         pdfapp_open_pdf(app, filename, fd);
  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.         {
  347.                 sprintf(buf, "%s - %d/%d (%d dpi)", app->doctitle,
  348.                                 app->pageno, app->pagecount, app->resolution);
  349.                 wintitle(app, buf);
  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.                 {
  378.                         __menuet__debug_out ("SHRINK\n");
  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. }
  1172.