Subversion Repositories Kolibri OS

Rev

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