Subversion Repositories Kolibri OS

Rev

Rev 8436 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

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