Cosmetics
[llpp.git] / link.c
blob7f615a18c18d3fc1fe8fced538fd922c3d75afb3
1 /* lot's of code c&p-ed directly from mupdf */
3 #define _GNU_SOURCE
4 #include <err.h>
5 #include <regex.h>
6 #include <errno.h>
7 #include <ctype.h>
8 #include <stdio.h>
9 #include <stdarg.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <pthread.h>
13 #include <sys/poll.h>
14 #include <sys/time.h>
16 /* fugly as hell and GCC specific but... */
17 #ifdef _BIG_ENDIAN
18 #define GL_GLEXT_PROTOTYPES
19 #endif
21 #include <GL/gl.h>
22 #include <GL/glext.h>
24 #include <caml/fail.h>
25 #include <caml/alloc.h>
26 #include <caml/memory.h>
27 #include <caml/unixsupport.h>
29 #include <fitz.h>
30 #include <mupdf.h>
32 #if 0
33 #define lprintf printf
34 #else
35 #define lprintf(...)
36 #endif
38 #define ARSERT(cond) for (;;) { \
39 if (!(cond)) { \
40 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
41 } \
42 break; \
45 struct slice {
46 int texindex;
47 int w, h;
50 struct page {
51 int pageno;
52 int slicecount;
53 fz_textspan *text;
54 fz_pixmap *pixmap;
55 pdf_page *drawpage;
56 struct pagedim *pagedim;
57 struct page *prev;
58 struct slice slices[];
61 struct pagedim {
62 int pageno;
63 fz_bbox bbox;
64 fz_matrix ctm;
67 struct {
68 int sock;
69 int sliceheight;
70 pthread_t thread;
71 struct page *pages;
72 struct pagedim *pagedims;
73 int pagecount;
74 int pagedimcount;
75 pdf_xref *xref;
76 fz_glyphcache *cache;
77 int w, h;
79 int useatifs;
81 int texindex;
82 int texcount;
83 GLuint *texids;
85 GLenum texform;
86 GLenum texty;
88 int lotsamemory;
90 int *pagetbl;
91 struct {
92 int w, h;
93 struct slice *slice;
94 } *texowners;
95 } state;
97 static void *parse_pointer (const char *cap, const char *s)
99 int ret;
100 void *ptr;
102 ret = sscanf (s, "%p", &ptr);
103 if (ret != 1) {
104 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
106 return ptr;
109 static int hasdata (int sock)
111 int ret;
112 struct pollfd pfd;
114 pfd.fd = sock;
115 pfd.events = POLLIN;
116 ret = poll (&pfd, 1, 0);
117 if (ret == 0) {
118 return 0;
120 if (ret != 1) {
121 err (1, "poll");
123 return pfd.revents & POLLIN;
126 static double now (void)
128 struct timeval tv;
130 if (gettimeofday (&tv, NULL)) {
131 err (1, "gettimeofday");
133 return tv.tv_sec + tv.tv_usec*1e-6;
136 static void readdata (int fd, char *p, int size)
138 ssize_t n;
140 n = read (fd, p, size);
141 if (n - size) {
142 err (1, "read (req %d, ret %zd)", size, n);
146 static void writedata (int fd, char *p, int size)
148 char buf[4];
149 ssize_t n;
151 buf[0] = (size >> 24) & 0xff;
152 buf[1] = (size >> 16) & 0xff;
153 buf[2] = (size >> 8) & 0xff;
154 buf[3] = (size >> 0) & 0xff;
156 n = write (fd, buf, 4);
157 if (n != 4) {
158 err (1, "write %zd", n);
161 n = write (fd, p, size);
162 if (n - size) {
163 err (1, "write (req %d, ret %zd)", size, n);
167 static void __attribute__ ((format (printf, 2, 3)))
168 printd (int fd, const char *fmt, ...)
170 int len;
171 va_list ap;
172 char buf[200];
174 va_start (ap, fmt);
175 len = vsnprintf (buf, sizeof (buf), fmt, ap);
176 va_end (ap);
177 writedata (fd, buf, len);
180 static void die (fz_error error)
182 fz_catch (error, "aborting");
183 if (state.xref)
184 pdf_closexref (state.xref);
185 exit (1);
188 void openxref (char *filename)
190 int fd;
191 fz_stream *file;
193 fd = open (filename, O_BINARY | O_RDONLY, 0666);
194 if (fd < 0)
195 die (fz_throw ("cannot open file '%s'", filename));
197 file = fz_openfile (fd);
198 state.xref = pdf_openxref (file);
199 if (!state.xref)
200 die (fz_throw ("cannot open PDF file '%s'", filename));
201 fz_dropstream (file);
203 if (pdf_needspassword (state.xref)) {
204 die (fz_throw ("password protected"));
207 state.pagecount = pdf_getpagecount (state.xref);
208 state.pagetbl = stat_alloc (state.pagecount * sizeof (*state.pagetbl));
211 static int readlen (int fd)
213 ssize_t n;
214 char p[4];
216 n = read (fd, p, 4);
217 if (n != 4) {
218 err (1, "read %zd", n);
221 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
224 static void freepage (struct page *page)
226 int i;
227 struct page *p;
229 fz_droppixmap (page->pixmap);
230 for (p = state.pages; p; p = p->prev) {
231 if (p->prev == page) {
232 p->prev = page->prev;
233 break;
236 for (i = 0; i < page->slicecount; ++i) {
237 struct slice *s = &page->slices[i];
238 if (s->texindex != -1) {
239 if (state.texowners[s->texindex].slice == s) {
240 state.texowners[s->texindex].slice = NULL;
241 ARSERT (state.texowners[s->texindex].w == s->w);
242 ARSERT (state.texowners[s->texindex].h >= s->h);
246 if (page->text) {
247 fz_freetextspan (page->text);
249 if (page->drawpage) {
250 pdf_droppage (page->drawpage);
253 free (page);
256 static void subdivide (struct page *p)
258 int i;
259 int h = p->pixmap->h;
260 int th = MIN (h, state.sliceheight);
262 for (i = 0; i < p->slicecount; ++i) {
263 struct slice *s = &p->slices[i];
264 s->texindex = -1;
265 s->h = MIN (th, h);
266 s->w = p->pixmap->w;
267 h -= th;
271 static void *render (int pageno, int pindex)
273 fz_error error;
274 int slicecount;
275 fz_obj *pageobj;
276 struct page *page;
277 double start, end;
278 pdf_page *drawpage;
279 fz_displaylist *list;
280 fz_device *idev, *mdev;
281 struct pagedim *pagedim;
283 start = now ();
284 /* printd (state.sock, "T \"rendering %d\"", pageno); */
285 pdf_flushxref (state.xref, 0);
287 pagedim = &state.pagedims[pindex];
288 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
289 + state.sliceheight - 1) / state.sliceheight;
290 slicecount += slicecount == 0;
292 page = calloc (sizeof (*page)
293 + (slicecount * sizeof (struct slice)), 1);
294 if (!page) {
295 err (1, "calloc page %d\n", pageno);
297 page->slicecount = slicecount;
298 page->prev = state.pages;
299 state.pages = page;
301 pageobj = pdf_getpageobject (state.xref, pageno);
302 if (!pageobj)
303 die (fz_throw ("cannot retrieve info from page %d", pageno));
305 error = pdf_loadpage (&drawpage, state.xref, pageobj);
306 if (error)
307 die (error);
309 page->pixmap = fz_newpixmapwithrect (pdf_devicergb, pagedim->bbox);
310 if (error)
311 die (error);
312 fz_clearpixmap (page->pixmap, 0xFF);
314 list = fz_newdisplaylist ();
315 if (!list)
316 die (fz_throw ("fz_newdisplaylist failed"));
318 mdev = fz_newlistdevice (list);
319 error = pdf_runcontentstream (mdev, fz_identity (), state.xref,
320 drawpage->resources,
321 drawpage->contents);
322 if (error)
323 die (error);
324 fz_freedevice (mdev);
326 idev = fz_newdrawdevice (state.cache, page->pixmap);
327 if (!idev)
328 die (fz_throw ("fz_newdrawdevice failed"));
329 fz_executedisplaylist (list, idev, pagedim->ctm);
330 fz_freedevice (idev);
332 fz_freedisplaylist (list);
334 page->drawpage = drawpage;
335 page->pagedim = pagedim;
336 page->pageno = pageno;
337 subdivide (page);
338 end = now ();
340 if (!state.lotsamemory) {
341 pdf_agestoreditems (state.xref->store);
342 pdf_evictageditems (state.xref->store);
345 /* printd (state.sock, "T \"rendering %d took %f sec\"", pageno, end - start); */
346 return page;
349 static void layout (void)
351 int pageno;
352 double a, b, c, d;
353 int prevrotate;
354 fz_rect prevbox;
355 int i, pindex;
356 asize_t size;
357 struct pagedim *p;
359 size = 0;
360 pindex = 0;
361 a = now ();
362 c = 0.0;
363 printd (state.sock, "c");
364 for (pageno = 1; pageno <= state.pagecount; ++pageno) {
365 float w;
366 float zoom;
367 int rotate;
368 fz_obj *obj;
369 fz_rect box;
370 fz_rect box2;
371 fz_matrix ctm;
372 fz_bbox bbox;
373 fz_obj *pageobj;
375 if (!(pageno & 31)) {
376 if (!c) {
377 c = a;
379 d = now ();
380 printd (state.sock, "T \"processing page %d %f\"", pageno, d - c);
381 c = d;
383 pageobj = pdf_getpageobject (state.xref, pageno);
384 if (!pageobj)
385 die (fz_throw ("cannot retrieve info from page %d", pageno));
387 state.pagetbl[pageno - 1] = fz_tonum (pageobj);
388 obj = fz_dictgets (pageobj, "CropBox");
389 if (!fz_isarray (obj)) {
390 obj = fz_dictgets (pageobj, "MediaBox");
391 if (!fz_isarray (obj))
392 die (fz_throw ("cannot find page bounds %d (%d R)",
393 fz_tonum (obj), fz_togen (obj)));
395 box = pdf_torect (obj);
396 obj = fz_dictgets (pageobj, "Rotate");
397 if (fz_isint (obj))
398 rotate = fz_toint (obj);
399 else
400 rotate = 0;
402 if (pageno != 1
403 && (prevrotate == rotate
404 && !memcmp (&prevbox, &box, sizeof (box)))) {
405 continue;
408 memcpy (&prevbox, &box, sizeof (box));
409 prevrotate = rotate;
411 box.x0 = MIN (prevbox.x0, prevbox.x1);
412 box.y0 = MIN (prevbox.y0, prevbox.y1);
413 box.x1 = MAX (prevbox.x0, prevbox.x1);
414 box.y1 = MAX (prevbox.y0, prevbox.y1);
416 ctm = fz_identity ();
417 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
418 ctm = fz_concat (ctm, fz_rotate (rotate));
419 box2 = fz_transformrect (ctm, box);
420 w = box2.x1 - box2.x0;
422 zoom = (state.w / w);
423 ctm = fz_identity ();
424 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
425 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
426 ctm = fz_concat (ctm, fz_rotate (rotate));
427 bbox = fz_roundrect (fz_transformrect (ctm, box));
429 size += sizeof (*state.pagedims);
430 state.pagedims = caml_stat_resize (state.pagedims, size);
432 p = &state.pagedims[pindex++];
433 memcpy (&p->bbox, &bbox, sizeof (bbox));
434 memcpy (&p->ctm, &ctm, sizeof (ctm));
436 p->pageno = pageno - 1;
439 state.pagedimcount = pindex;
440 for (i = pindex - 1; i >= 0; --i) {
441 p = &state.pagedims[i];
442 printd (state.sock, "l %d %d %d",
443 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0);
446 b = now ();
447 printd (state.sock, "T \"Processed %d pages in %f seconds\"",
448 state.pagecount, b - a);
449 printd (state.sock, "C %d", state.pagecount);
452 static void recurse_outline (pdf_outline *outline, int level)
454 while (outline) {
455 fz_obj *obj;
456 int top = 0;
457 int pageno = -1;
459 if (!outline->link) goto next;
461 obj = outline->link->dest;
462 if (fz_isarray (obj)) {
463 int i;
464 int num;
465 fz_obj *obj2;
466 struct pagedim *pagedim = state.pagedims;
468 obj2 = fz_arrayget (obj, 0);
469 if (fz_isint (obj2)) {
470 pageno = fz_toint (obj2);
472 else {
473 num = fz_tonum (obj2);
474 for (i = 0; i < state.pagecount; ++i) {
475 if (state.pagetbl[i] == num) {
476 pageno = i;
477 break;
482 for (i = 0; i < state.pagedimcount; ++i) {
483 if (state.pagedims[i].pageno > pageno)
484 break;
485 pagedim = &state.pagedims[i];
488 if (fz_arraylen (obj) > 3) {
489 fz_point p;
491 p.x = fz_toint (fz_arrayget (obj, 2));
492 p.y = fz_toint (fz_arrayget (obj, 3));
493 p = fz_transformpoint (pagedim->ctm, p);
494 top = p.y;
498 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
499 printd (state.sock, "o %d %d %d %s",
500 level, pageno, top, outline->title);
501 next:
502 if (outline->child) {
503 recurse_outline (outline->child, level + 1);
505 outline = outline->next;
509 static void process_outline (void)
511 pdf_outline *outline;
513 outline = pdf_loadoutline (state.xref);
514 if (outline) {
515 recurse_outline (outline, 0);
516 pdf_dropoutline (outline);
520 /* wishful thinking function */
521 static void search (regex_t *re, int pageno, int y, int forward)
523 int i;
524 int ret;
525 char *p;
526 char buf[256];
527 fz_error error;
528 fz_obj *pageobj;
529 fz_device *tdev;
530 pdf_page *drawpage;
531 fz_textspan *text, *span;
532 struct pagedim *pdim, *pdimprev;
533 int stop = 0;
534 int niters = 0;
535 double start, end;
537 start = now ();
538 while (pageno >= 0 && pageno < state.pagecount && !stop) {
539 if (niters++ == 5) {
540 niters = 0;
541 if (hasdata (state.sock)) {
542 printd (state.sock, "T \"attention requested aborting search at %d\"",
543 pageno);
544 stop = 1;
546 else {
547 printd (state.sock, "T \"searching in page %d\"", pageno);
550 pdimprev = NULL;
551 for (i = 0; i < state.pagedimcount; ++i) {
552 pdim = &state.pagedims[i];
553 if (pdim->pageno == pageno) {
554 goto found;
556 if (pdim->pageno > pageno) {
557 pdim = pdimprev;
558 goto found;
560 pdimprev = pdim;
562 pdim = pdimprev;
563 found:
565 pageobj = pdf_getpageobject (state.xref, pageno + 1);
566 if (!pageobj)
567 die (fz_throw ("cannot retrieve info from page %d", pageno));
569 error = pdf_loadpage (&drawpage, state.xref, pageobj);
570 if (error)
571 die (error);
573 text = fz_newtextspan ();
574 tdev = fz_newtextdevice (text);
575 error = pdf_runcontentstream (tdev, pdim->ctm, state.xref,
576 drawpage->resources,
577 drawpage->contents);
578 if (error) die (error);
579 fz_freedevice (tdev);
581 for (span = text; span; span = span->next) {
582 regmatch_t rm;
584 p = buf;
585 /* XXX: spans are not sorted "visually" */
586 for (i = 0; i < MIN (span->len, sizeof (buf) - 1); ++i) {
587 if (forward) {
588 if (span->text[i].bbox.y0 < y + 1) {
589 continue;
592 else {
593 if (span->text[i].bbox.y0 > y - 1) {
594 continue;
597 if (span->text[i].c < 256) {
598 *p++ = span->text[i].c;
600 else {
601 *p++ = '?';
604 if (p == buf) {
605 continue;
607 *p++ = 0;
609 ret = regexec (re, buf, 1, &rm, 0);
610 if (ret) {
611 if (ret != REG_NOMATCH) {
612 size_t size;
613 char errbuf[80];
614 size = regerror (ret, re, errbuf, sizeof (errbuf));
615 printd (state.sock,
616 "T \"regexec error `%.*s'\"",
617 (int) size, errbuf);
620 else {
621 fz_rect r;
623 r.x0 = span->text[rm.rm_so].bbox.x0 - pdim->bbox.x0;
624 r.y0 = span->text[rm.rm_so].bbox.y0;
625 r.x1 = span->text[rm.rm_eo - 1].bbox.x1 - pdim->bbox.x0;
626 r.y1 = span->text[rm.rm_eo - 1].bbox.y1;
628 if (!stop) {
629 printd (state.sock, "F %d %d %f %f %f %f",
630 pageno, 1,
631 r.x0, r.y0,
632 r.x1, r.y1);
634 else {
635 printd (state.sock, "R %d %d %f %f %f %f",
636 pageno, 2,
637 r.x0, r.y0,
638 r.x1, r.y1);
640 printd (state.sock, "T \"found at %d `%.*s' %f in %f sec\"",
641 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
642 span->text[0].bbox.y0 - drawpage->mediabox.y0,
643 now () - start);
644 stop = 1;
647 if (forward) {
648 pageno += 1;
649 y = 0;
651 else {
652 pageno -= 1;
653 y = INT_MAX;
655 fz_freetextspan (text);
656 pdf_droppage (drawpage);
658 end = now ();
659 if (!stop) {
660 printd (state.sock, "T \"no matches %f sec\"", end - start);
662 printd (state.sock, "d");
665 static void *mainloop (void *unused)
667 char *p = NULL;
668 int len, ret, oldlen = 0;
670 for (;;) {
671 len = readlen (state.sock);
672 if (len == 0) {
673 errx (1, "readlen returned 0");
676 if (oldlen < len + 1) {
677 p = realloc (p, len + 1);
678 if (!p) {
679 err (1, "realloc %d failed", len + 1);
681 oldlen = len + 1;
683 readdata (state.sock, p, len);
684 p[len] = 0;
686 if (!strncmp ("open", p, 4)) {
687 char *filename = p + 5;
689 openxref (filename);
691 else if (!strncmp ("free", p, 4)) {
692 void *ptr;
694 ret = sscanf (p + 4, " %p", &ptr);
695 if (ret != 1) {
696 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
698 freepage (ptr);
699 printd (state.sock, "f");
701 else if (!strncmp ("search", p, 6)) {
702 int icase, pageno, y, ret, len2, forward;
703 char *pattern;
704 regex_t re;
706 ret = sscanf (p + 6, " %d %d %d %d %n",
707 &icase, &pageno, &y, &forward, &len2);
708 if (ret != 4) {
709 errx (1, "malformed search `%s' ret=%d", p, ret);
712 pattern = p + 6 + len2;
713 ret = regcomp (&re, pattern,
714 REG_EXTENDED | (icase ? REG_ICASE : 0));
715 if (ret) {
716 char errbuf[80];
717 size_t size;
719 size = regerror (ret, &re, errbuf, sizeof (errbuf));
720 printd (state.sock, "T \"regcomp failed `%.*s'\"", (int) size, errbuf);
722 else {
723 search (&re, pageno, y, forward);
724 regfree (&re);
727 else if (!strncmp ("geometry", p, 8)) {
728 int w, h;
730 ret = sscanf (p + 8, " %d %d", &w, &h);
731 if (ret != 2) {
732 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
734 state.h = h;
735 if (w != state.w) {
736 int i;
737 state.w = w;
738 for (i = 0; i < state.texcount; ++i) {
739 state.texowners[i].slice = NULL;
742 layout ();
743 process_outline ();
745 else if (!strncmp ("render", p, 6)) {
746 int pageno, pindex, w, h, ret;
747 struct page *page;
749 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
750 if (ret != 4) {
751 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
754 page = render (pageno, pindex);
755 printd (state.sock, "r %d %d %d %p\n",
756 pageno,
757 state.w,
758 state.h,
759 page);
761 else {
762 errx (1, "unknown command %.*s", len, p);
765 return NULL;
768 static void upload2 (struct page *page, int slicenum, const char *cap)
770 int i;
771 int w, h;
772 double start, end;
773 struct slice *slice = &page->slices[slicenum];
775 w = page->pixmap->w;
776 h = page->pixmap->h;
778 ARSERT (w == slice->w);
779 if (slice->texindex != -1
780 && state.texowners[slice->texindex].slice == slice) {
781 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
783 else {
784 int subimage = 0;
785 int index = (state.texindex++ % state.texcount);
786 size_t offset = 0;
788 for (i = 0; i < slicenum; ++i) {
789 offset += w * page->slices[i].h * 4;
792 if (state.texowners[index].w == slice->w) {
793 if (state.texowners[index].h >= slice->h ) {
794 subimage = 1;
796 else {
797 state.texowners[index].h = slice->h;
800 else {
801 state.texowners[index].h = slice->h;
804 state.texowners[index].slice = slice;
805 state.texowners[index].w = slice->w;
806 slice->texindex = index;
808 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
809 start = now ();
810 if (subimage) {
812 GLenum err = glGetError ();
813 if (err != GL_NO_ERROR) {
814 printf ("\e[0;31mERROR1 %d %d %#x\e[0m\n", w, slice->h, err);
815 abort ();
818 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
823 slice->h,
824 state.texform,
825 state.texty,
826 page->pixmap->samples + offset
829 GLenum err = glGetError ();
830 if (err != GL_NO_ERROR) {
831 printf ("\e[0;31mERROR %d %d %#x\e[0m\n", w, slice->h, err);
832 abort ();
836 else {
837 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
839 GL_RGBA8,
841 slice->h,
843 state.texform,
844 state.texty,
845 page->pixmap->samples + offset
849 end = now ();
850 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
851 subimage ? "sub" : "img",
852 page->pageno, slicenum,
853 slice->w, slice->h,
854 state.texids[slice->texindex],
855 end - start);
859 CAMLprim value ml_preload (value ptr_v)
861 int i;
862 int ret;
863 void *ptr;
864 CAMLparam1 (ptr_v);
865 char *s = String_val (ptr_v);
866 struct page *page;
868 ret = sscanf (s, "%p", &ptr);
869 if (ret != 1) {
870 errx (1, "cannot parse pointer `%s'", s);
873 page = ptr;
874 for (i = 0; i < page->slicecount; ++i) {
875 upload2 (ptr, i, "preload");
878 CAMLreturn (Val_unit);
881 CAMLprim value ml_draw (value dispy_v, value w_v, value h_v,
882 value py_v, value ptr_v)
884 CAMLparam5 (dispy_v, w_v, h_v, py_v, ptr_v);
885 int dispy = Int_val (dispy_v);
886 int w = Int_val (w_v);
887 int h = Int_val (h_v);
888 int py = Int_val (py_v);
889 char *s = String_val (ptr_v);
890 int ret;
891 void *ptr;
892 struct page *page;
893 int slicenum = 0;
895 ret = sscanf (s, "%p", &ptr);
896 if (ret != 1) {
897 errx (1, "cannot parse pointer `%s'", s);
899 page = ptr;
901 w = page->pixmap->w;
903 ARSERT (h >= 0 && "ml_draw wrong h");
905 glEnable (GL_TEXTURE_RECTANGLE_ARB);
906 if (state.useatifs) {
907 glEnable (GL_FRAGMENT_SHADER_ATI);
910 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
911 struct slice *slice = &page->slices[slicenum];
912 if (slice->h > py) {
913 break;
915 py -= slice->h;
918 h = MIN (state.h, h);
919 while (h) {
920 int th;
921 struct slice *slice = &page->slices[slicenum];
923 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
925 th = MIN (h, slice->h - py);
926 upload2 (page, slicenum, "upload");
928 glBegin (GL_QUADS);
930 glTexCoord2i (0, py);
931 glVertex2i (0, dispy);
933 glTexCoord2i (w, py);
934 glVertex2i (w, dispy);
936 glTexCoord2i (w, py+th);
937 glVertex2i (w, dispy + th);
939 glTexCoord2i (0, py+th);
940 glVertex2i (0, dispy + th);
942 glEnd ();
944 h -= th;
945 py = 0;
946 dispy += th;
947 slicenum += 1;
950 glDisable (GL_TEXTURE_RECTANGLE_ARB);
951 if (state.useatifs) {
952 glDisable (GL_FRAGMENT_SHADER_ATI);
954 CAMLreturn (Val_unit);
957 static pdf_link *getlink (struct page *page, int x, int y)
959 fz_point p;
960 fz_matrix ctm;
961 pdf_link *link;
963 p.x = x;
964 p.y = y;
966 ctm = fz_invertmatrix (page->pagedim->ctm);
967 p = fz_transformpoint (ctm, p);
969 for (link = page->drawpage->links; link; link = link->next) {
970 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
971 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
972 if (link->kind == PDF_LGOTO) {
973 return link;
978 return NULL;
981 CAMLprim value ml_checklink (value ptr_v, value x_v, value y_v)
983 CAMLparam3 (ptr_v, x_v, y_v);
984 char *s = String_val (ptr_v);
986 CAMLreturn (Val_bool (NULL != getlink (parse_pointer ("ml_checklink", s),
987 Int_val (x_v), Int_val (y_v))));
990 CAMLprim value ml_getlink (value ptr_v, value x_v, value y_v)
992 CAMLparam3 (ptr_v, x_v, y_v);
993 CAMLlocal2 (ret_v, tup_v);
994 pdf_link *link;
995 struct page *page;
996 char *s = String_val (ptr_v);
998 page = parse_pointer ("ml_getlink", s);
1000 link = getlink (page, Int_val (x_v), Int_val (y_v));
1001 if (link) {
1002 int pageno;
1003 fz_point p;
1004 fz_obj *obj;
1006 pageno = -1;
1007 obj = fz_arrayget (link->dest, 0);
1008 if (fz_isindirect (obj)) {
1009 pageno = pdf_findpageobject (state.xref, obj) - 1;
1011 else if (fz_isint (obj)) {
1012 pageno = fz_toint (obj);
1015 if (fz_arraylen (link->dest) > 3) {
1016 p.x = fz_toint (fz_arrayget (link->dest, 2));
1017 p.y = fz_toint (fz_arrayget (link->dest, 3));
1018 p = fz_transformpoint (page->pagedim->ctm, p);
1020 else {
1021 p.x = 0.0;
1022 p.y = 0.0;
1025 tup_v = caml_alloc_tuple (2);
1026 ret_v = caml_alloc_small (1, 1);
1027 Field (tup_v, 0) = Val_int (pageno);
1028 Field (tup_v, 1) = Val_int (p.y);
1029 Field (ret_v, 0) = tup_v;
1031 else {
1032 ret_v = Val_int (0);
1035 CAMLreturn (ret_v);
1038 CAMLprim value ml_gettext (value ptr_v, value rect_v, value oy_v, value rectsel_v)
1040 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1041 fz_matrix ctm;
1042 fz_point p1, p2;
1043 struct page *page;
1044 fz_textspan *span;
1045 char *s = String_val (ptr_v);
1046 int rectsel = Bool_val (rectsel_v);
1047 int i, bx0, bx1, by0, by1, x0, x1, y0, y1, oy;
1049 /* stop GCC from complaining about uninitialized variables */
1050 int rx0 = rx0, rx1 = rx1, ry0 = ry0, ry1 = ry1;
1052 page = parse_pointer ("ml_gettext", s);
1054 oy = Int_val (oy_v);
1055 p1.x = Int_val (Field (rect_v, 0));
1056 p1.y = Int_val (Field (rect_v, 1));
1057 p2.x = Int_val (Field (rect_v, 2));
1058 p2.y = Int_val (Field (rect_v, 3));
1060 if (0) {
1061 glEnable (GL_BLEND);
1062 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1063 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1064 glColor4f (0, 0, 0, 0.2);
1065 glRecti (p1.x, p1.y, p2.x, p2.y);
1066 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1067 glDisable (GL_BLEND);
1070 ctm = page->pagedim->ctm;
1071 if (!page->text) {
1072 fz_error error;
1073 fz_device *tdev;
1075 page->text = fz_newtextspan ();
1076 tdev = fz_newtextdevice (page->text);
1077 error = pdf_runcontentstream (tdev, page->pagedim->ctm, state.xref,
1078 page->drawpage->resources,
1079 page->drawpage->contents);
1080 if (error) die (error);
1081 fz_freedevice (tdev);
1084 printf ("\ec");
1086 printf ("BBox %f %f %f %f\n", p1.x, p1.y, p2.x, p2.y);
1087 p1.x += page->pixmap->x;
1088 p1.y += page->pixmap->y;
1089 p2.x += page->pixmap->x;
1090 p2.y += page->pixmap->y;
1091 x0 = p1.x;
1092 y0 = p1.y;
1093 x1 = p2.x;
1094 y1 = p2.y;
1095 printf ("BBox %d %d %d %d %d %d\n", x0, y0, x1, y1, oy, page->pageno);
1097 for (span = page->text; span; span = span->next) {
1098 int seen = 0;
1100 /* fz_debugtextspanxml (span); */
1101 for (i = 0; i < span->len; ++i) {
1102 long c;
1104 bx0 = span->text[i].bbox.x0;
1105 bx1 = span->text[i].bbox.x1;
1106 by0 = span->text[i].bbox.y0 + oy;
1107 by1 = span->text[i].bbox.y1 + oy;
1109 if ((bx1 >= x0 && bx0 <= x1 && by1 >= y0 && by0 <= y1)) {
1110 if (!seen) {
1111 rx0 = bx0 - page->pixmap->x;
1112 rx1 = bx1 - page->pixmap->x;
1113 ry0 = by0;
1114 ry1 = by1;
1117 seen = 1;
1118 c = span->text[i].c;
1119 if (c < 256) {
1120 if ((isprint (c) && !isspace (c))) {
1121 if (!rectsel) {
1122 bx0 -= page->pixmap->x;
1123 bx1 -= page->pixmap->x;
1124 glEnable (GL_BLEND);
1125 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1126 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1127 glColor4f (0.5, 0.5, 0.0, 0.6);
1128 glRecti (bx0, by0, bx1, by1);
1129 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1130 glDisable (GL_BLEND);
1132 if (isprint (c) || c ==' ') {
1133 rx1 = bx1;
1134 ry1 = by1;
1137 putc (c, stdout);
1139 else {
1140 putc ('?', stdout);
1145 if (rectsel) {
1146 if (seen) {
1147 glEnable (GL_BLEND);
1148 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1149 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1150 glColor4f (0.5, 0.5, 0.0, 0.6);
1151 glRecti (rx0, ry0, rx1, ry1);
1152 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1153 glDisable (GL_BLEND);
1157 if (seen && span->eol) {
1158 x0 = page->pixmap->x;
1159 putc ('\n', stdout);
1163 CAMLreturn (Val_unit);
1166 static void initgl (void)
1168 #ifdef _BIG_ENDIAN
1169 if (strstr ((char *) glGetString (GL_EXTENSIONS),
1170 "GL_ATI_fragment_shader")) {
1171 /* Here, with MESA, rv280, powerpc32: BGRA(rev) is slow while
1172 ABGR is fast, so fix things in the shader */
1173 state.texform = GL_ABGR_EXT;
1174 state.texty = GL_UNSIGNED_INT_8_8_8_8;
1176 glBindFragmentShaderATI (1);
1177 glBeginFragmentShaderATI ();
1179 glSampleMapATI (GL_REG_0_ATI, GL_TEXTURE0_ARB, GL_SWIZZLE_STR_ATI);
1181 glColorFragmentOp1ATI (GL_MOV_ATI,
1182 GL_REG_1_ATI, GL_RED_BIT_ATI, GL_NONE,
1183 GL_REG_0_ATI, GL_BLUE, GL_NONE);
1184 glColorFragmentOp1ATI (GL_MOV_ATI,
1185 GL_REG_1_ATI, GL_BLUE_BIT_ATI, GL_NONE,
1186 GL_REG_0_ATI, GL_RED, GL_NONE);
1187 glColorFragmentOp1ATI (
1188 GL_MOV_ATI,
1189 GL_REG_0_ATI, GL_RED_BIT_ATI | GL_BLUE_BIT_ATI, GL_NONE,
1190 GL_REG_1_ATI, GL_NONE, GL_NONE
1193 glEndFragmentShaderATI ();
1194 state.useatifs = 1;
1196 else {
1197 state.texform = GL_BGRA_EXT;
1198 state.texty = GL_UNSIGNED_INT_8_8_8_8_REV;
1200 #else
1201 state.texform = GL_BGRA_EXT;
1202 state.texty = GL_UNSIGNED_INT_8_8_8_8;
1203 #endif
1206 CAMLprim value ml_init (value sock_v)
1208 int ret;
1209 CAMLparam1 (sock_v);
1211 state.texcount = 128;
1212 state.sliceheight = 64;
1214 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1215 if (!state.texids) {
1216 err (1, "calloc texids %zu", state.texcount * sizeof (*state.texids));
1219 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1220 if (!state.texowners) {
1221 err (1, "calloc texowners %zu",
1222 state.texcount * sizeof (*state.texowners));
1225 glGenTextures (state.texcount, state.texids);
1227 state.sock = Int_val (sock_v);
1228 initgl ();
1230 state.cache = fz_newglyphcache ();
1231 if (!state.cache) {
1232 errx (1, "fz_newglyphcache failed");
1235 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1236 if (ret) {
1237 unix_error (ret, "pthread_create", Nothing);
1240 CAMLreturn (Val_unit);