Some docs
[llpp.git] / link.c
blob7ca7684bedc594a2fba7b2bb3846f5d9e366beb2
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/fitz.h"
30 #include "mupdf/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 struct {
89 int w, h;
90 struct slice *slice;
91 } *texowners;
92 } state;
94 static void *parse_pointer (const char *cap, const char *s)
96 int ret;
97 void *ptr;
99 ret = sscanf (s, "%p", &ptr);
100 if (ret != 1) {
101 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
103 return ptr;
106 static int hasdata (int sock)
108 int ret;
109 struct pollfd pfd;
111 pfd.fd = sock;
112 pfd.events = POLLIN;
113 ret = poll (&pfd, 1, 0);
114 if (ret == 0) {
115 return 0;
117 if (ret != 1) {
118 err (1, "poll");
120 return pfd.revents & POLLIN;
123 static double now (void)
125 struct timeval tv;
127 if (gettimeofday (&tv, NULL)) {
128 err (1, "gettimeofday");
130 return tv.tv_sec + tv.tv_usec*1e-6;
133 static void readdata (int fd, char *p, int size)
135 ssize_t n;
137 n = read (fd, p, size);
138 if (n - size) {
139 err (1, "read (req %d, ret %zd)", size, n);
143 static void writedata (int fd, char *p, int size)
145 char buf[4];
146 ssize_t n;
148 buf[0] = (size >> 24) & 0xff;
149 buf[1] = (size >> 16) & 0xff;
150 buf[2] = (size >> 8) & 0xff;
151 buf[3] = (size >> 0) & 0xff;
153 n = write (fd, buf, 4);
154 if (n != 4) {
155 err (1, "write %zd", n);
158 n = write (fd, p, size);
159 if (n - size) {
160 err (1, "write (req %d, ret %zd)", size, n);
164 static void __attribute__ ((format (printf, 2, 3)))
165 printd (int fd, const char *fmt, ...)
167 int len;
168 va_list ap;
169 char buf[200];
171 va_start (ap, fmt);
172 len = vsnprintf (buf, sizeof (buf), fmt, ap);
173 va_end (ap);
174 writedata (fd, buf, len);
177 static void die (fz_error error)
179 fz_catch (error, "aborting");
180 pdf_closexref (state.xref);
181 exit (1);
184 void openxref (char *filename)
186 int fd;
187 fz_stream *file;
189 fd = open (filename, O_BINARY | O_RDONLY, 0666);
190 if (fd < 0)
191 die (fz_throw ("cannot open file '%s'", filename));
193 file = fz_openfile (fd);
194 state.xref = pdf_openxref (file);
195 if (!state.xref)
196 die (fz_throw ("cannot open PDF file '%s'", filename));
197 fz_dropstream (file);
199 if (pdf_needspassword (state.xref)) {
200 die (fz_throw ("password protected"));
202 state.pagecount = pdf_getpagecount (state.xref);
205 static int readlen (int fd)
207 ssize_t n;
208 char p[4];
210 n = read (fd, p, 4);
211 if (n != 4) {
212 err (1, "read %zd", n);
215 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
218 static void freepage (struct page *page)
220 int i;
221 struct page *p;
223 fz_droppixmap (page->pixmap);
224 for (p = state.pages; p; p = p->prev) {
225 if (p->prev == page) {
226 p->prev = page->prev;
227 break;
230 for (i = 0; i < page->slicecount; ++i) {
231 struct slice *s = &page->slices[i];
232 if (s->texindex != -1) {
233 if (state.texowners[s->texindex].slice == s) {
234 state.texowners[s->texindex].slice = NULL;
235 ARSERT (state.texowners[s->texindex].w == s->w);
236 ARSERT (state.texowners[s->texindex].h >= s->h);
240 if (page->text) {
241 fz_freetextspan (page->text);
243 if (page->drawpage) {
244 pdf_droppage (page->drawpage);
246 free (page);
249 static void subdivide (struct page *p)
251 int i;
252 int h = p->pixmap->h;
253 int th = MIN (h, state.sliceheight);
255 for (i = 0; i < p->slicecount; ++i) {
256 struct slice *s = &p->slices[i];
257 s->texindex = -1;
258 s->h = MIN (th, h);
259 s->w = p->pixmap->w;
260 h -= th;
264 static void *render (int pageno, int pindex)
266 fz_error error;
267 int slicecount;
268 fz_obj *pageobj;
269 struct page *page;
270 double start, end;
271 pdf_page *drawpage;
272 fz_displaylist *list;
273 fz_device *idev, *mdev;
274 struct pagedim *pagedim;
276 start = now ();
277 /* printd (state.sock, "T \"rendering %d\"", pageno); */
278 pdf_flushxref (state.xref, 0);
279 pagedim = &state.pagedims[pindex];
280 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
281 + state.sliceheight - 1) / state.sliceheight;
282 slicecount += slicecount == 0;
284 page = calloc (sizeof (*page)
285 + (slicecount * sizeof (struct slice)), 1);
286 if (!page) {
287 err (1, "calloc page %d\n", pageno);
289 page->slicecount = slicecount;
290 page->prev = state.pages;
291 state.pages = page;
293 pageobj = pdf_getpageobject (state.xref, pageno);
294 if (!pageobj)
295 die (fz_throw ("cannot retrieve info from page %d", pageno));
297 error = pdf_loadpage (&drawpage, state.xref, pageobj);
298 if (error)
299 die (error);
301 page->pixmap = fz_newpixmapwithrect (pdf_devicergb, pagedim->bbox);
302 if (error)
303 die (error);
304 fz_clearpixmap (page->pixmap, 0xFF);
306 list = fz_newdisplaylist ();
307 if (!list)
308 die (fz_throw ("fz_newdisplaylist failed"));
310 mdev = fz_newlistdevice (list);
311 error = pdf_runcontentstream (mdev, fz_identity (), state.xref,
312 drawpage->resources,
313 drawpage->contents);
314 if (error)
315 die (error);
316 fz_freedevice (mdev);
318 idev = fz_newdrawdevice (state.cache, page->pixmap);
319 if (!idev)
320 die (fz_throw ("fz_newdrawdevice failed"));
321 fz_executedisplaylist (list, idev, pagedim->ctm);
322 fz_freedevice (idev);
324 fz_freedisplaylist (list);
326 page->drawpage = drawpage;
327 page->pagedim = pagedim;
328 page->pageno = pageno;
329 subdivide (page);
330 end = now ();
332 /* printd (state.sock, "T \"rendering %d took %f sec\"", pageno, end - start); */
333 return page;
336 static void layout (void)
338 int pageno;
339 double a, b, c, d;
340 int prevrotate;
341 fz_rect prevbox;
342 int i, pindex;
343 asize_t size;
344 int64 mapsize;
345 struct pagedim *p;
347 size = 0;
348 pindex = 0;
349 mapsize = 0;
350 a = now ();
351 c = 0.0;
352 printd (state.sock, "c");
353 for (pageno = 1; pageno <= state.pagecount; ++pageno) {
354 float w;
355 float zoom;
356 int rotate;
357 fz_obj *obj;
358 fz_rect box;
359 fz_rect box2;
360 fz_matrix ctm;
361 fz_bbox bbox;
362 fz_obj *pageobj;
364 if (!(pageno & 31)) {
365 if (!c) {
366 c = a;
368 d = now ();
369 printd (state.sock, "T \"processing page %d %f\"", pageno, d - c);
370 c = d;
372 pageobj = pdf_getpageobject (state.xref, pageno);
373 if (!pageobj)
374 die (fz_throw ("cannot retrieve info from page %d", pageno));
376 obj = fz_dictgets (pageobj, "CropBox");
377 if (!fz_isarray (obj)) {
378 obj = fz_dictgets (pageobj, "MediaBox");
379 if (!fz_isarray (obj))
380 die (fz_throw ("cannot find page bounds %d (%d R)",
381 fz_tonum (obj), fz_togen (obj)));
383 box = pdf_torect (obj);
384 obj = fz_dictgets (pageobj, "Rotate");
385 if (fz_isint (obj))
386 rotate = fz_toint (obj);
387 else
388 rotate = 0;
390 if (pageno != 1
391 && (prevrotate == rotate
392 && !memcmp (&prevbox, &box, sizeof (box)))) {
393 continue;
396 memcpy (&prevbox, &box, sizeof (box));
397 prevrotate = rotate;
399 box.x0 = MIN (prevbox.x0, prevbox.x1);
400 box.y0 = MIN (prevbox.y0, prevbox.y1);
401 box.x1 = MAX (prevbox.x0, prevbox.x1);
402 box.y1 = MAX (prevbox.y0, prevbox.y1);
404 ctm = fz_identity ();
405 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
406 ctm = fz_concat (ctm, fz_rotate (rotate));
407 box2 = fz_transformrect (ctm, box);
408 w = box2.x1 - box2.x0;
410 zoom = (state.w / w);
411 ctm = fz_identity ();
412 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
413 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
414 ctm = fz_concat (ctm, fz_rotate (rotate));
415 bbox = fz_roundrect (fz_transformrect (ctm, box));
417 size += sizeof (*state.pagedims);
418 state.pagedims = caml_stat_resize (state.pagedims, size);
420 p = &state.pagedims[pindex++];
421 memcpy (&p->bbox, &bbox, sizeof (bbox));
422 memcpy (&p->ctm, &ctm, sizeof (ctm));
424 p->pageno = pageno - 1;
427 state.pagedimcount = pindex;
428 for (i = pindex - 1; i >= 0; --i) {
429 p = &state.pagedims[i];
430 printd (state.sock, "l %d %d %d",
431 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0);
434 b = now ();
435 printd (state.sock, "T \"Processed %d pages in %f secons\"",
436 state.pagecount, b - a);
437 printd (state.sock, "C %d", state.pagecount);
440 /* wishful thinking function */
441 static void search (regex_t *re, int pageno, int y, int forward)
443 int i;
444 int ret;
445 char *p;
446 char buf[256];
447 fz_error error;
448 fz_obj *pageobj;
449 fz_device *tdev;
450 pdf_page *drawpage;
451 fz_textspan *text, *span;
452 struct pagedim *pdim, *pdimprev;
453 int stop = 0;
454 int niters = 0;
455 double start, end;
457 start = now ();
458 while (pageno >= 0 && pageno < state.pagecount && !stop) {
459 if (niters++ == 5) {
460 niters = 0;
461 if (hasdata (state.sock)) {
462 printd (state.sock, "T \"attention requested aborting search at %d\"",
463 pageno);
464 stop = 1;
466 else {
467 printd (state.sock, "T \"searching in page %d\"", pageno);
470 pdimprev = NULL;
471 for (i = 0; i < state.pagedimcount; ++i) {
472 pdim = &state.pagedims[i];
473 if (pdim->pageno == pageno) {
474 goto found;
476 if (pdim->pageno > pageno) {
477 pdim = pdimprev;
478 goto found;
480 pdimprev = pdim;
482 pdim = pdimprev;
483 found:
485 pageobj = pdf_getpageobject (state.xref, pageno + 1);
486 if (!pageobj)
487 die (fz_throw ("cannot retrieve info from page %d", pageno));
489 error = pdf_loadpage (&drawpage, state.xref, pageobj);
490 if (error)
491 die (error);
493 text = fz_newtextspan ();
494 tdev = fz_newtextdevice (text);
495 error = pdf_runcontentstream (tdev, pdim->ctm, state.xref,
496 drawpage->resources,
497 drawpage->contents);
498 if (error) die (error);
499 fz_freedevice (tdev);
501 for (span = text; span; span = span->next) {
502 regmatch_t rm;
504 p = buf;
505 /* XXX: spans are not sorted "visually" */
506 for (i = 0; i < MIN (span->len, sizeof (buf) - 1); ++i) {
507 if (forward) {
508 if (span->text[i].bbox.y0 < y + 1) {
509 continue;
512 else {
513 if (span->text[i].bbox.y0 > y - 1) {
514 continue;
517 if (span->text[i].c < 256) {
518 *p++ = span->text[i].c;
520 else {
521 *p++ = '?';
524 if (p == buf) {
525 continue;
527 *p++ = 0;
529 ret = regexec (re, buf, 1, &rm, 0);
530 if (ret) {
531 if (ret != REG_NOMATCH) {
532 size_t size;
533 char errbuf[80];
534 size = regerror (ret, re, errbuf, sizeof (errbuf));
535 printd (state.sock,
536 "T \"regexec error `%.*s'\"",
537 (int) size, errbuf);
540 else {
541 fz_rect r;
543 r.x0 = span->text[rm.rm_so].bbox.x0 - pdim->bbox.x0;
544 r.y0 = span->text[rm.rm_so].bbox.y0;
545 r.x1 = span->text[rm.rm_eo - 1].bbox.x1 - pdim->bbox.x0;
546 r.y1 = span->text[rm.rm_eo - 1].bbox.y1;
548 if (!stop) {
549 printd (state.sock, "F %d %d %f %f %f %f",
550 pageno, 1,
551 r.x0, r.y0,
552 r.x1, r.y1);
554 else {
555 printd (state.sock, "R %d %d %f %f %f %f",
556 pageno, 2,
557 r.x0, r.y0,
558 r.x1, r.y1);
560 printd (state.sock, "T \"found at %d `%.*s' %f in %f sec\"",
561 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
562 span->text[0].bbox.y0 - drawpage->mediabox.y0,
563 now () - start);
564 stop = 1;
567 if (forward) {
568 pageno += 1;
569 y = 0;
571 else {
572 pageno -= 1;
573 y = INT_MAX;
575 fz_freetextspan (text);
576 pdf_droppage (drawpage);
578 end = now ();
579 if (!stop) {
580 printd (state.sock, "T \"no matches %f sec\"", end - start);
582 printd (state.sock, "d");
585 static void *mainloop (void *unused)
587 char *p = NULL;
588 int len, ret, oldlen = 0;
590 for (;;) {
591 len = readlen (state.sock);
592 if (len == 0) {
593 errx (1, "readlen returned 0");
596 if (oldlen < len + 1) {
597 p = realloc (p, len + 1);
598 if (!p) {
599 err (1, "realloc %d failed", len + 1);
601 oldlen = len + 1;
603 readdata (state.sock, p, len);
604 p[len] = 0;
606 if (!strncmp ("open", p, 4)) {
607 char *filename = p + 5;
609 openxref (filename);
611 else if (!strncmp ("free", p, 4)) {
612 void *ptr;
614 ret = sscanf (p + 4, " %p", &ptr);
615 if (ret != 1) {
616 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
618 freepage (ptr);
619 printd (state.sock, "f");
621 else if (!strncmp ("search", p, 6)) {
622 int icase, pageno, y, ret, len2, forward;
623 char *pattern;
624 regex_t re;
626 ret = sscanf (p + 6, " %d %d %d %d %n",
627 &icase, &pageno, &y, &forward, &len2);
628 if (ret != 4) {
629 errx (1, "malformed search `%s' ret=%d", p, ret);
632 pattern = p + 6 + len2;
633 ret = regcomp (&re, pattern,
634 REG_EXTENDED | (icase ? REG_ICASE : 0));
635 if (ret) {
636 char errbuf[80];
637 size_t size;
639 size = regerror (ret, &re, errbuf, sizeof (errbuf));
640 printd (state.sock, "T \"regcomp failed `%.*s'\"", (int) size, errbuf);
642 else {
643 search (&re, pageno, y, forward);
644 regfree (&re);
647 else if (!strncmp ("geometry", p, 8)) {
648 int w, h;
650 ret = sscanf (p + 8, " %d %d", &w, &h);
651 if (ret != 2) {
652 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
654 state.h = h;
655 if (w != state.w) {
656 int i;
657 state.w = w;
658 for (i = 0; i < state.texcount; ++i) {
659 state.texowners[i].slice = NULL;
662 layout ();
664 else if (!strncmp ("render", p, 6)) {
665 int pageno, pindex, w, h, ret;
666 struct page *page;
668 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
669 if (ret != 4) {
670 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
673 page = render (pageno, pindex);
674 printd (state.sock, "r %d %d %d %p\n",
675 pageno,
676 state.w,
677 state.h,
678 page);
680 else {
681 errx (1, "unknown command %.*s", len, p);
684 return NULL;
687 static void upload2 (struct page *page, int slicenum, const char *cap)
689 int i;
690 int w, h;
691 double start, end;
692 struct slice *slice = &page->slices[slicenum];
694 w = page->pixmap->w;
695 h = page->pixmap->h;
697 ARSERT (w == slice->w);
698 if (slice->texindex != -1
699 && state.texowners[slice->texindex].slice == slice) {
700 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
702 else {
703 int subimage = 0;
704 int index = (state.texindex++ % state.texcount);
705 size_t offset = 0;
707 for (i = 0; i < slicenum; ++i) {
708 offset += w * page->slices[i].h * 4;
711 if (state.texowners[index].w == slice->w) {
712 if (state.texowners[index].h >= slice->h ) {
713 subimage = 1;
715 else {
716 state.texowners[index].h = slice->h;
719 else {
720 state.texowners[index].h = slice->h;
723 state.texowners[index].slice = slice;
724 state.texowners[index].w = slice->w;
725 slice->texindex = index;
727 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
728 start = now ();
729 if (subimage) {
731 GLenum err = glGetError ();
732 if (err != GL_NO_ERROR) {
733 printf ("\e[0;31mERROR1 %d %d %#x\e[0m\n", w, slice->h, err);
734 abort ();
737 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
742 slice->h,
743 state.texform,
744 state.texty,
745 page->pixmap->samples + offset
748 GLenum err = glGetError ();
749 if (err != GL_NO_ERROR) {
750 printf ("\e[0;31mERROR %d %d %#x\e[0m\n", w, slice->h, err);
751 abort ();
755 else {
756 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
758 GL_RGBA8,
760 slice->h,
762 state.texform,
763 state.texty,
764 page->pixmap->samples + offset
768 end = now ();
769 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
770 subimage ? "sub" : "img",
771 page->pageno, slicenum,
772 slice->w, slice->h,
773 state.texids[slice->texindex],
774 end - start);
778 CAMLprim value ml_preload (value ptr_v)
780 int i;
781 int ret;
782 void *ptr;
783 CAMLparam1 (ptr_v);
784 char *s = String_val (ptr_v);
785 struct page *page;
787 ret = sscanf (s, "%p", &ptr);
788 if (ret != 1) {
789 errx (1, "cannot parse pointer `%s'", s);
792 page = ptr;
793 for (i = 0; i < page->slicecount; ++i) {
794 upload2 (ptr, i, "preload");
797 CAMLreturn (Val_unit);
800 CAMLprim value ml_draw (value dispy_v, value w_v, value h_v,
801 value py_v, value ptr_v)
803 CAMLparam5 (dispy_v, w_v, h_v, py_v, ptr_v);
804 int dispy = Int_val (dispy_v);
805 int w = Int_val (w_v);
806 int h = Int_val (h_v);
807 int py = Int_val (py_v);
808 char *s = String_val (ptr_v);
809 int ret;
810 void *ptr;
811 struct page *page;
812 int slicenum = 0;
814 ret = sscanf (s, "%p", &ptr);
815 if (ret != 1) {
816 errx (1, "cannot parse pointer `%s'", s);
818 page = ptr;
820 w = page->pixmap->w;
822 ARSERT (h >= 0 && "ml_draw wrong h");
824 glEnable (GL_TEXTURE_RECTANGLE_ARB);
825 if (state.useatifs) {
826 glEnable (GL_FRAGMENT_SHADER_ATI);
829 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
830 struct slice *slice = &page->slices[slicenum];
831 if (slice->h > py) {
832 break;
834 py -= slice->h;
837 h = MIN (state.h, h);
838 while (h) {
839 int th;
840 struct slice *slice = &page->slices[slicenum];
842 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
844 th = MIN (h, slice->h - py);
845 upload2 (page, slicenum, "upload");
847 glBegin (GL_QUADS);
849 glTexCoord2i (0, py);
850 glVertex2i (0, dispy);
852 glTexCoord2i (w, py);
853 glVertex2i (w, dispy);
855 glTexCoord2i (w, py+th);
856 glVertex2i (w, dispy + th);
858 glTexCoord2i (0, py+th);
859 glVertex2i (0, dispy + th);
861 glEnd ();
863 h -= th;
864 py = 0;
865 dispy += th;
866 slicenum += 1;
869 glDisable (GL_TEXTURE_RECTANGLE_ARB);
870 if (state.useatifs) {
871 glDisable (GL_FRAGMENT_SHADER_ATI);
873 CAMLreturn (Val_unit);
876 CAMLprim value ml_checklink (value ptr_v, value x_v, value y_v)
878 CAMLparam3 (ptr_v, x_v, y_v);
879 fz_point p;
880 fz_matrix ctm;
881 pdf_link *link;
882 int pageno = -1;
883 struct page *page;
884 char *s = String_val (ptr_v);
886 p.x = Int_val (x_v);
887 p.y = Int_val (y_v);
889 page = parse_pointer ("ml_checklink", s);
891 ctm = fz_invertmatrix (page->pagedim->ctm);
892 p = fz_transformpoint (ctm, p);
894 for (link = page->drawpage->links; link; link = link->next) {
895 if (p.x >= link->rect.x0 && p.x <= link->rect.x1)
896 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
897 if (link->kind == PDF_LGOTO) {
898 pageno = pdf_findpageobject (state.xref,
899 fz_arrayget (link->dest, 0)) - 1;
900 /* link->dest); */
902 break;
906 CAMLreturn (Val_int (pageno));
909 CAMLprim value ml_gettext (value ptr_v, value rect_v, value oy_v, value rectsel_v)
911 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
912 fz_matrix ctm;
913 fz_point p1, p2;
914 struct page *page;
915 fz_textspan *span;
916 char *s = String_val (ptr_v);
917 int rectsel = Bool_val (rectsel_v);
918 int i, bx0, bx1, by0, by1, x0, x1, y0, y1, oy;
920 /* stop GCC from complaining about uninitialized variables */
921 int rx0 = rx0, rx1 = rx1, ry0 = ry0, ry1 = ry1;
923 page = parse_pointer ("ml_gettext", s);
925 oy = Int_val (oy_v);
926 p1.x = Int_val (Field (rect_v, 0));
927 p1.y = Int_val (Field (rect_v, 1));
928 p2.x = Int_val (Field (rect_v, 2));
929 p2.y = Int_val (Field (rect_v, 3));
931 if (0) {
932 glEnable (GL_BLEND);
933 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
934 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
935 glColor4f (0, 0, 0, 0.2);
936 glRecti (p1.x, p1.y, p2.x, p2.y);
937 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
938 glDisable (GL_BLEND);
941 ctm = page->pagedim->ctm;
942 if (!page->text) {
943 fz_error error;
944 fz_device *tdev;
946 page->text = fz_newtextspan ();
947 tdev = fz_newtextdevice (page->text);
948 error = pdf_runcontentstream (tdev, page->pagedim->ctm, state.xref,
949 page->drawpage->resources,
950 page->drawpage->contents);
951 if (error) die (error);
952 fz_freedevice (tdev);
955 printf ("\ec");
957 printf ("BBox %f %f %f %f\n", p1.x, p1.y, p2.x, p2.y);
958 p1.x += page->pixmap->x;
959 p1.y += page->pixmap->y;
960 p2.x += page->pixmap->x;
961 p2.y += page->pixmap->y;
962 x0 = p1.x;
963 y0 = p1.y;
964 x1 = p2.x;
965 y1 = p2.y;
966 printf ("BBox %d %d %d %d %d %d\n", x0, y0, x1, y1, oy, page->pageno);
968 for (span = page->text; span; span = span->next) {
969 int seen = 0;
971 /* fz_debugtextspanxml (span); */
972 for (i = 0; i < span->len; ++i) {
973 long c;
975 bx0 = span->text[i].bbox.x0;
976 bx1 = span->text[i].bbox.x1;
977 by0 = span->text[i].bbox.y0 + oy;
978 by1 = span->text[i].bbox.y1 + oy;
980 if ((bx1 >= x0 && bx0 <= x1 && by1 >= y0 && by0 <= y1)) {
981 if (!seen) {
982 rx0 = bx0 - page->pixmap->x;
983 rx1 = bx1 - page->pixmap->x;
984 ry0 = by0;
985 ry1 = by1;
988 seen = 1;
989 c = span->text[i].c;
990 if (c < 256) {
991 if ((isprint (c) && !isspace (c))) {
992 if (!rectsel) {
993 bx0 -= page->pixmap->x;
994 bx1 -= page->pixmap->x;
995 glEnable (GL_BLEND);
996 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
997 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
998 glColor4f (0.5, 0.5, 0.0, 0.6);
999 glRecti (bx0, by0, bx1, by1);
1000 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1001 glDisable (GL_BLEND);
1003 if (isprint (c) || c ==' ') {
1004 rx1 = bx1;
1005 ry1 = by1;
1008 putc (c, stdout);
1010 else {
1011 putc ('?', stdout);
1016 if (rectsel) {
1017 if (seen) {
1018 glEnable (GL_BLEND);
1019 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1020 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1021 glColor4f (0.5, 0.5, 0.0, 0.6);
1022 glRecti (rx0, ry0, rx1, ry1);
1023 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1024 glDisable (GL_BLEND);
1028 if (seen && span->eol) {
1029 x0 = page->pixmap->x;
1030 putc ('\n', stdout);
1034 CAMLreturn (Val_unit);
1037 static void initgl (void)
1039 #ifdef _BIG_ENDIAN
1040 if (strstr ((char *) glGetString (GL_EXTENSIONS),
1041 "GL_ATI_fragment_shader")) {
1042 /* Here, with MESA, rv280, powerpc32: BGRA(rev) is slow while
1043 ABGR is fast, so fix things in the shader */
1044 state.texform = GL_ABGR_EXT;
1045 state.texty = GL_UNSIGNED_INT_8_8_8_8;
1047 glBindFragmentShaderATI (1);
1048 glBeginFragmentShaderATI ();
1050 glSampleMapATI (GL_REG_0_ATI, GL_TEXTURE0_ARB, GL_SWIZZLE_STR_ATI);
1052 glColorFragmentOp1ATI (GL_MOV_ATI,
1053 GL_REG_1_ATI, GL_RED_BIT_ATI, GL_NONE,
1054 GL_REG_0_ATI, GL_BLUE, GL_NONE);
1055 glColorFragmentOp1ATI (GL_MOV_ATI,
1056 GL_REG_1_ATI, GL_BLUE_BIT_ATI, GL_NONE,
1057 GL_REG_0_ATI, GL_RED, GL_NONE);
1058 glColorFragmentOp1ATI (
1059 GL_MOV_ATI,
1060 GL_REG_0_ATI, GL_RED_BIT_ATI | GL_BLUE_BIT_ATI, GL_NONE,
1061 GL_REG_1_ATI, GL_NONE, GL_NONE
1064 glEndFragmentShaderATI ();
1065 state.useatifs = 1;
1067 else {
1068 state.texform = GL_BGRA_EXT;
1069 state.texty = GL_UNSIGNED_INT_8_8_8_8_REV;
1071 #else
1072 state.texform = GL_BGRA_EXT;
1073 state.texty = GL_UNSIGNED_INT_8_8_8_8;
1074 #endif
1077 CAMLprim value ml_init (value sock_v)
1079 int ret;
1080 CAMLparam1 (sock_v);
1082 state.texcount = 128;
1083 state.sliceheight = 64;
1085 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1086 if (!state.texids) {
1087 err (1, "calloc texids %zu", state.texcount * sizeof (*state.texids));
1090 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1091 if (!state.texowners) {
1092 err (1, "calloc texowners %zu",
1093 state.texcount * sizeof (*state.texowners));
1096 glGenTextures (state.texcount, state.texids);
1098 state.sock = Int_val (sock_v);
1099 initgl ();
1101 state.cache = fz_newglyphcache ();
1102 if (!state.cache) {
1103 errx (1, "fz_newglyphcache failed");
1106 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1107 if (ret) {
1108 unix_error (ret, "pthread_create", Nothing);
1111 CAMLreturn (Val_unit);