Subversion Repositories Kolibri OS

Rev

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