Subversion Repositories Kolibri OS

Rev

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