Fix some ordering/synchronization issues
[llpp.git] / link.c
blob26de0ad5007de828801d8d482b50d1591d36c1fb
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 int rotate;
64 fz_rect box;
65 fz_bbox bbox;
66 fz_matrix ctm;
69 struct {
70 int sock;
71 int sliceheight;
72 pthread_t thread;
73 struct page *pages;
74 struct pagedim *pagedims;
75 int pagecount;
76 int pagedimcount;
77 pdf_xref *xref;
78 fz_glyphcache *cache;
79 int w, h;
81 int useatifs;
83 int texindex;
84 int texcount;
85 GLuint *texids;
87 GLenum texform;
88 GLenum texty;
90 int lotsamemory;
92 int *pagetbl;
93 struct {
94 int w, h;
95 struct slice *slice;
96 } *texowners;
97 } state;
99 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
101 static void lock (const char *cap)
103 int ret = pthread_mutex_lock (&mutex);
104 if (ret) {
105 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
109 static void unlock (const char *cap)
111 int ret = pthread_mutex_unlock (&mutex);
112 if (ret) {
113 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
117 static int trylock (const char *cap)
119 int ret = pthread_mutex_trylock (&mutex);
121 if (ret && ret != EBUSY) {
122 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
124 return ret == EBUSY;
127 static void *parse_pointer (const char *cap, const char *s)
129 int ret;
130 void *ptr;
132 ret = sscanf (s, "%p", &ptr);
133 if (ret != 1) {
134 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
136 return ptr;
139 static int hasdata (int sock)
141 int ret;
142 struct pollfd pfd;
144 pfd.fd = sock;
145 pfd.events = POLLIN;
146 ret = poll (&pfd, 1, 0);
147 if (ret == 0) {
148 return 0;
150 if (ret != 1) {
151 err (1, "poll");
153 return pfd.revents & POLLIN;
156 static double now (void)
158 struct timeval tv;
160 if (gettimeofday (&tv, NULL)) {
161 err (1, "gettimeofday");
163 return tv.tv_sec + tv.tv_usec*1e-6;
166 static void readdata (int fd, char *p, int size)
168 ssize_t n;
170 n = read (fd, p, size);
171 if (n - size) {
172 err (1, "read (req %d, ret %zd)", size, n);
176 static void writedata (int fd, char *p, int size)
178 char buf[4];
179 ssize_t n;
181 buf[0] = (size >> 24) & 0xff;
182 buf[1] = (size >> 16) & 0xff;
183 buf[2] = (size >> 8) & 0xff;
184 buf[3] = (size >> 0) & 0xff;
186 n = write (fd, buf, 4);
187 if (n != 4) {
188 err (1, "write %zd", n);
191 n = write (fd, p, size);
192 if (n - size) {
193 err (1, "write (req %d, ret %zd)", size, n);
197 static void __attribute__ ((format (printf, 2, 3)))
198 printd (int fd, const char *fmt, ...)
200 int len;
201 va_list ap;
202 char buf[200];
204 va_start (ap, fmt);
205 len = vsnprintf (buf, sizeof (buf), fmt, ap);
206 va_end (ap);
207 writedata (fd, buf, len);
210 static void die (fz_error error)
212 fz_catch (error, "aborting");
213 if (state.xref)
214 pdf_closexref (state.xref);
215 exit (1);
218 static void openxref (char *filename)
220 int fd;
221 fz_stream *file;
223 fd = open (filename, O_BINARY | O_RDONLY, 0666);
224 if (fd < 0)
225 die (fz_throw ("cannot open file '%s'", filename));
227 file = fz_openfile (fd);
228 state.xref = pdf_openxref (file);
229 if (!state.xref)
230 die (fz_throw ("cannot open PDF file '%s'", filename));
231 fz_dropstream (file);
233 if (pdf_needspassword (state.xref)) {
234 die (fz_throw ("password protected"));
237 state.pagecount = pdf_getpagecount (state.xref);
238 state.pagetbl = stat_alloc (state.pagecount * sizeof (*state.pagetbl));
241 static int readlen (int fd)
243 ssize_t n;
244 char p[4];
246 n = read (fd, p, 4);
247 if (n != 4) {
248 err (1, "read %zd", n);
251 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
254 static void freepage (struct page *page)
256 int i;
257 struct page *p;
259 fz_droppixmap (page->pixmap);
260 for (p = state.pages; p; p = p->prev) {
261 if (p->prev == page) {
262 p->prev = page->prev;
263 break;
266 for (i = 0; i < page->slicecount; ++i) {
267 struct slice *s = &page->slices[i];
268 if (s->texindex != -1) {
269 if (state.texowners[s->texindex].slice == s) {
270 state.texowners[s->texindex].slice = NULL;
271 ARSERT (state.texowners[s->texindex].w == s->w);
272 ARSERT (state.texowners[s->texindex].h >= s->h);
276 if (page->text) {
277 fz_freetextspan (page->text);
279 if (page->drawpage) {
280 pdf_droppage (page->drawpage);
283 free (page);
286 static void subdivide (struct page *p)
288 int i;
289 int h = p->pixmap->h;
290 int th = MIN (h, state.sliceheight);
292 for (i = 0; i < p->slicecount; ++i) {
293 struct slice *s = &p->slices[i];
294 s->texindex = -1;
295 s->h = MIN (th, h);
296 s->w = p->pixmap->w;
297 h -= th;
301 static void *render (int pageno, int pindex)
303 fz_error error;
304 int slicecount;
305 fz_obj *pageobj;
306 struct page *page;
307 double start, end;
308 pdf_page *drawpage;
309 fz_displaylist *list;
310 fz_device *idev, *mdev;
311 struct pagedim *pagedim;
313 start = now ();
314 /* printd (state.sock, "T rendering %d", pageno); */
315 pdf_flushxref (state.xref, 0);
317 pagedim = &state.pagedims[pindex];
318 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
319 + state.sliceheight - 1) / state.sliceheight;
320 slicecount += slicecount == 0;
322 page = calloc (sizeof (*page)
323 + (slicecount * sizeof (struct slice)), 1);
324 if (!page) {
325 err (1, "calloc page %d\n", pageno);
327 page->slicecount = slicecount;
328 page->prev = state.pages;
329 state.pages = page;
331 pageobj = pdf_getpageobject (state.xref, pageno);
332 if (!pageobj)
333 die (fz_throw ("cannot retrieve info from page %d", pageno));
335 error = pdf_loadpage (&drawpage, state.xref, pageobj);
336 if (error)
337 die (error);
339 page->pixmap = fz_newpixmapwithrect (pdf_devicergb, pagedim->bbox);
340 if (error)
341 die (error);
342 fz_clearpixmap (page->pixmap, 0xFF);
344 list = fz_newdisplaylist ();
345 if (!list)
346 die (fz_throw ("fz_newdisplaylist failed"));
348 mdev = fz_newlistdevice (list);
349 error = pdf_runcontentstream (mdev, fz_identity (), state.xref,
350 drawpage->resources,
351 drawpage->contents);
352 if (error)
353 die (error);
354 fz_freedevice (mdev);
356 idev = fz_newdrawdevice (state.cache, page->pixmap);
357 if (!idev)
358 die (fz_throw ("fz_newdrawdevice failed"));
359 fz_executedisplaylist (list, idev, pagedim->ctm);
360 fz_freedevice (idev);
362 fz_freedisplaylist (list);
364 page->drawpage = drawpage;
365 page->pagedim = pagedim;
366 page->pageno = pageno;
367 subdivide (page);
368 end = now ();
370 if (!state.lotsamemory) {
371 pdf_agestoreditems (state.xref->store);
372 pdf_evictageditems (state.xref->store);
375 /* printd (state.sock, "T rendering %d took %f sec", pageno, end - start); */
376 return page;
379 /* almost verbatim copy of pdf_getpagecountimp */
380 struct stuff
382 fz_obj *resources;
383 fz_obj *mediabox;
384 fz_obj *cropbox;
385 fz_obj *rotate;
388 static void
389 recurse_page (fz_obj *node, int bias, int *pagesp, struct stuff inherit)
391 fz_obj *type;
392 fz_obj *kids;
393 fz_obj *count;
394 char *typestr;
395 int pages = 0;
396 int i;
398 if (!fz_isdict(node))
400 fz_warn("pagetree node is missing, igoring missing pages...");
401 return;
404 type = fz_dictgets(node, "Type");
405 kids = fz_dictgets(node, "Kids");
406 count = fz_dictgets(node, "Count");
408 if (fz_isname(type))
409 typestr = fz_toname(type);
410 else
412 fz_warn("pagetree node (%d %d R) lacks required type", fz_tonum(node), fz_togen(node));
414 kids = fz_dictgets(node, "Kids");
415 if (kids)
417 fz_warn("guessing it may be a pagetree node, continuing...");
418 typestr = "Pages";
420 else
422 fz_warn("guessing it may be a page, continuing...");
423 typestr = "Page";
427 if (!strcmp(typestr, "Page")) {
428 int rotate;
429 fz_obj *obj;
430 fz_rect box;
431 struct pagedim *p;
432 int pageno = *pagesp;
434 state.pagetbl[pageno + bias] = fz_tonum (node);
436 obj = fz_dictgets (node, "CropBox");
437 if (!obj) obj = inherit.cropbox;
438 if (!fz_isarray (obj)) {
439 obj = fz_dictgets (node, "MediaBox");
440 if (!obj) obj = inherit.mediabox;
442 if (!fz_isarray (obj)) {
443 die (fz_throw ("cannot find page bounds %d (%d Rd)",
444 fz_tonum (node), fz_togen (node)));
447 box = pdf_torect (obj);
449 obj = fz_dictgets (node, "Rotate");
450 if (!obj) obj = inherit.rotate;
451 if (fz_isint (obj)) {
452 rotate = fz_toint (obj);
454 else {
455 rotate = 0;
458 p = &state.pagedims[state.pagedimcount - 1];
459 if ((state.pagedimcount == 0)
460 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
461 size_t size;
463 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
464 state.pagedims = realloc (state.pagedims, size);
465 if (!state.pagedims) {
466 err (1, "realloc pagedims to %zu (%d elems)",
467 size, state.pagedimcount + 1);
469 p = &state.pagedims[state.pagedimcount++];
470 p->rotate = rotate;
471 p->box = box;
472 p->pageno = pageno + bias;
474 (*pagesp)++;
476 else if (!strcmp(typestr, "Pages"))
478 fz_obj *inh;
480 if (!fz_isarray(kids))
481 fz_warn("page tree node contains no pages");
483 pdf_logpage("subtree (%d %d R) {\n", fz_tonum(node), fz_togen(node));
485 inh = fz_dictgets(node, "Resources");
486 if (inh) inherit.resources = inh;
488 inh = fz_dictgets(node, "MediaBox");
489 if (inh) inherit.mediabox = inh;
491 inh = fz_dictgets(node, "CropBox");
492 if (inh) inherit.cropbox = inh;
494 inh = fz_dictgets(node, "Rotate");
495 if (inh) inherit.rotate = inh;
497 for (i = 0; i < fz_arraylen(kids); i++)
499 fz_obj *obj = fz_arrayget(kids, i);
501 /* prevent infinite recursion possible in maliciously crafted PDFs */
502 if (obj == node)
504 fz_warn("cyclic page tree");
505 return;
508 recurse_page (obj, *pagesp + bias, &pages, inherit);
511 if (pages != fz_toint(count))
513 fz_warn("page tree node contains incorrect number of pages, continuing...");
514 count = fz_newint(pages);
515 fz_dictputs(node, "Count", count);
516 fz_dropobj(count);
519 pdf_logpage("%d pages\n", pages);
521 (*pagesp) += pages;
523 pdf_logpage("}\n");
527 static void initpdims (void)
529 fz_obj *catalog;
530 fz_obj *pages;
531 int count;
532 double start, end;
533 struct stuff inherit;
535 start = now ();
536 catalog = fz_dictgets (state.xref->trailer, "Root");
537 pages = fz_dictgets (catalog, "Pages");
539 inherit.resources = nil;
540 inherit.mediabox = nil;
541 inherit.cropbox = nil;
542 inherit.rotate = nil;
544 count = 0;
545 recurse_page (pages, 0, &count, inherit);
546 end = now ();
547 printd (state.sock, "T Processed %d pages in %f seconds",
548 count, end - start);
551 static void layout (void)
553 int pindex;
554 fz_matrix ctm;
555 fz_rect box, box2;
556 double zoom, w;
557 struct pagedim *p = state.pagedims;
559 pindex = 0;
560 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
561 box.x0 = MIN (p->box.x0, p->box.x1);
562 box.y0 = MIN (p->box.y0, p->box.y1);
563 box.x1 = MAX (p->box.x0, p->box.x1);
564 box.y1 = MAX (p->box.y0, p->box.y1);
566 ctm = fz_identity ();
567 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
568 ctm = fz_concat (ctm, fz_rotate (p->rotate));
569 box2 = fz_transformrect (ctm, box);
570 w = box2.x1 - box2.x0;
572 zoom = (state.w / w);
573 ctm = fz_identity ();
574 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
575 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
576 ctm = fz_concat (ctm, fz_rotate (p->rotate));
577 p->bbox = fz_roundrect (fz_transformrect (ctm, box));
578 memcpy (&p->ctm, &ctm, sizeof (ctm));
581 while (p-- != state.pagedims) {
582 printd (state.sock, "l %d %d %d",
583 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0);
587 static void recurse_outline (pdf_outline *outline, int level)
589 while (outline) {
590 int i, num;
591 fz_obj *obj;
592 int top = 0;
593 int pageno = -1;
595 if (!outline->link) goto next;
597 obj = outline->link->dest;
598 if (fz_isindirect (obj)) {
599 num = fz_tonum (obj);
601 for (i = 0; i < state.pagecount; ++i) {
602 if (state.pagetbl[i] == num) {
603 pageno = i;
604 break;
608 else if (fz_isarray (obj)) {
609 fz_obj *obj2;
611 obj2 = fz_arrayget (obj, 0);
612 if (fz_isint (obj2)) {
613 pageno = fz_toint (obj2);
615 else {
616 num = fz_tonum (obj2);
617 for (i = 0; i < state.pagecount; ++i) {
618 if (state.pagetbl[i] == num) {
619 pageno = i;
620 break;
625 if (fz_arraylen (obj) > 3) {
626 fz_point p;
627 struct pagedim *pagedim = state.pagedims;
629 for (i = 0; i < state.pagedimcount; ++i) {
630 if (state.pagedims[i].pageno > pageno)
631 break;
632 pagedim = &state.pagedims[i];
635 p.x = fz_toint (fz_arrayget (obj, 2));
636 p.y = fz_toint (fz_arrayget (obj, 3));
637 p = fz_transformpoint (pagedim->ctm, p);
638 top = p.y;
642 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
643 printd (state.sock, "o %d %d %d %s",
644 level, pageno, top, outline->title);
645 next:
646 if (outline->child) {
647 recurse_outline (outline->child, level + 1);
649 outline = outline->next;
653 static void process_outline (void)
655 pdf_outline *outline;
657 outline = pdf_loadoutline (state.xref);
658 if (outline) {
659 recurse_outline (outline, 0);
660 pdf_dropoutline (outline);
664 static int comparespans (const void *l, const void *r)
666 fz_textspan *const*ls = l;
667 fz_textspan *const*rs = r;
669 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
672 /* wishful thinking function */
673 static void search (regex_t *re, int pageno, int y, int forward)
675 int i, j;
676 int ret;
677 char *p;
678 char buf[256];
679 fz_error error;
680 fz_obj *pageobj;
681 fz_device *tdev;
682 pdf_page *drawpage;
683 fz_textspan *text, *span, **pspan;
684 struct pagedim *pdim, *pdimprev;
685 int stop = 0;
686 int niters = 0;
687 int nspans;
688 double start, end;
690 start = now ();
691 while (pageno >= 0 && pageno < state.pagecount && !stop) {
692 if (niters++ == 5) {
693 if (!state.lotsamemory) {
694 pdf_agestoreditems (state.xref->store);
695 pdf_evictageditems (state.xref->store);
697 niters = 0;
698 if (hasdata (state.sock)) {
699 printd (state.sock, "T attention requested aborting search at %d",
700 pageno);
701 stop = 1;
703 else {
704 printd (state.sock, "T searching in page %d", pageno);
707 pdimprev = NULL;
708 for (i = 0; i < state.pagedimcount; ++i) {
709 pdim = &state.pagedims[i];
710 if (pdim->pageno == pageno) {
711 goto found;
713 if (pdim->pageno > pageno) {
714 pdim = pdimprev;
715 goto found;
717 pdimprev = pdim;
719 pdim = pdimprev;
720 found:
722 pageobj = pdf_getpageobject (state.xref, pageno + 1);
723 if (!pageobj)
724 die (fz_throw ("cannot retrieve info from page %d", pageno));
726 error = pdf_loadpage (&drawpage, state.xref, pageobj);
727 if (error)
728 die (error);
730 text = fz_newtextspan ();
731 tdev = fz_newtextdevice (text);
732 error = pdf_runcontentstream (tdev, pdim->ctm, state.xref,
733 drawpage->resources,
734 drawpage->contents);
735 if (error) die (error);
736 fz_freedevice (tdev);
738 nspans = 0;
739 for (span = text; span; span = span->next) {
740 nspans++;
742 pspan = malloc (sizeof (void *) * nspans);
743 if (!pspan) {
744 err (1, "malloc span pointers %zu", sizeof (void *) * nspans);
746 for (i = 0, span = text; span; span = span->next, ++i) {
747 pspan[i] = span;
749 qsort (pspan, nspans, sizeof (fz_textspan *), comparespans);
751 j = forward ? 0 : nspans - 1;
752 while (nspans--) {
753 regmatch_t rm;
755 span = pspan[j];
756 j += forward ? 1 : -1;
757 p = buf;
758 /* XXX: spans are not sorted "visually" */
759 for (i = 0; i < MIN (span->len, sizeof (buf) - 1); ++i) {
760 if (forward) {
761 if (span->text[i].bbox.y0 < y + 1) {
762 continue;
765 else {
766 if (span->text[i].bbox.y0 > y - 1) {
767 continue;
770 if (span->text[i].c < 256) {
771 *p++ = span->text[i].c;
773 else {
774 *p++ = '?';
777 if (p == buf) {
778 continue;
780 *p++ = 0;
782 ret = regexec (re, buf, 1, &rm, 0);
783 if (ret) {
784 if (ret != REG_NOMATCH) {
785 size_t size;
786 char errbuf[80];
787 size = regerror (ret, re, errbuf, sizeof (errbuf));
788 printd (state.sock,
789 "T regexec error `%.*s'",
790 (int) size, errbuf);
791 fz_freetextspan (text);
792 pdf_droppage (drawpage);
793 free (pspan);
794 return;
797 else {
798 fz_rect r;
800 r.x0 = span->text[rm.rm_so].bbox.x0 - pdim->bbox.x0;
801 r.y0 = span->text[rm.rm_so].bbox.y0;
802 r.x1 = span->text[rm.rm_eo - 1].bbox.x1 - pdim->bbox.x0;
803 r.y1 = span->text[rm.rm_eo - 1].bbox.y1;
805 if (!stop) {
806 printd (state.sock, "F %d %d %f %f %f %f",
807 pageno, 1,
808 r.x0, r.y0,
809 r.x1, r.y1);
811 else {
812 printd (state.sock, "R %d %d %f %f %f %f",
813 pageno, 2,
814 r.x0, r.y0,
815 r.x1, r.y1);
817 printd (state.sock, "T found at %d `%.*s' %f in %f sec",
818 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
819 span->text[0].bbox.y0 - drawpage->mediabox.y0,
820 now () - start);
821 stop = 1;
824 if (forward) {
825 pageno += 1;
826 y = 0;
828 else {
829 pageno -= 1;
830 y = INT_MAX;
832 fz_freetextspan (text);
833 pdf_droppage (drawpage);
834 free (pspan);
836 end = now ();
837 if (!stop) {
838 printd (state.sock, "T no matches %f sec", end - start);
840 printd (state.sock, "D");
843 static void *mainloop (void *unused)
845 char *p = NULL;
846 int len, ret, oldlen = 0;
848 for (;;) {
849 len = readlen (state.sock);
850 if (len == 0) {
851 errx (1, "readlen returned 0");
854 if (oldlen < len + 1) {
855 p = realloc (p, len + 1);
856 if (!p) {
857 err (1, "realloc %d failed", len + 1);
859 oldlen = len + 1;
861 readdata (state.sock, p, len);
862 p[len] = 0;
864 if (!strncmp ("open", p, 4)) {
865 char *filename = p + 5;
867 openxref (filename);
868 initpdims ();
870 else if (!strncmp ("free", p, 4)) {
871 void *ptr;
873 ret = sscanf (p + 4, " %p", &ptr);
874 if (ret != 1) {
875 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
877 lock ("free");
878 freepage (ptr);
879 unlock ("free");
880 printd (state.sock, "d");
882 else if (!strncmp ("search", p, 6)) {
883 int icase, pageno, y, ret, len2, forward;
884 char *pattern;
885 regex_t re;
887 ret = sscanf (p + 6, " %d %d %d %d,%n",
888 &icase, &pageno, &y, &forward, &len2);
889 if (ret != 4) {
890 errx (1, "malformed search `%s' ret=%d", p, ret);
893 pattern = p + 6 + len2;
894 ret = regcomp (&re, pattern,
895 REG_EXTENDED | (icase ? REG_ICASE : 0));
896 if (ret) {
897 char errbuf[80];
898 size_t size;
900 size = regerror (ret, &re, errbuf, sizeof (errbuf));
901 printd (state.sock, "T regcomp failed `%.*s'",
902 (int) size, errbuf);
904 else {
905 search (&re, pageno, y, forward);
906 regfree (&re);
909 else if (!strncmp ("geometry", p, 8)) {
910 int w, h;
912 printd (state.sock, "c");
913 ret = sscanf (p + 8, " %d %d", &w, &h);
914 if (ret != 2) {
915 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
917 state.h = h;
918 if (w != state.w) {
919 int i;
920 state.w = w;
921 for (i = 0; i < state.texcount; ++i) {
922 state.texowners[i].slice = NULL;
925 lock ("geometry");
926 layout ();
927 process_outline ();
928 unlock ("geometry");
929 printd (state.sock, "C %d", state.pagecount);
931 else if (!strncmp ("render", p, 6)) {
932 int pageno, pindex, w, h, ret;
933 struct page *page;
935 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
936 if (ret != 4) {
937 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
940 page = render (pageno, pindex);
941 printd (state.sock, "r %d %d %d %p\n",
942 pageno,
943 state.w,
944 state.h,
945 page);
947 else {
948 errx (1, "unknown command %.*s", len, p);
951 return NULL;
954 static void upload2 (struct page *page, int slicenum, const char *cap)
956 int i;
957 int w, h;
958 double start, end;
959 struct slice *slice = &page->slices[slicenum];
961 w = page->pixmap->w;
962 h = page->pixmap->h;
964 ARSERT (w == slice->w);
965 if (slice->texindex != -1
966 && state.texowners[slice->texindex].slice == slice) {
967 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
969 else {
970 int subimage = 0;
971 int index = (state.texindex++ % state.texcount);
972 size_t offset = 0;
974 for (i = 0; i < slicenum; ++i) {
975 offset += w * page->slices[i].h * 4;
978 if (state.texowners[index].w == slice->w) {
979 if (state.texowners[index].h >= slice->h ) {
980 subimage = 1;
982 else {
983 state.texowners[index].h = slice->h;
986 else {
987 state.texowners[index].h = slice->h;
990 state.texowners[index].slice = slice;
991 state.texowners[index].w = slice->w;
992 slice->texindex = index;
994 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
995 start = now ();
996 if (subimage) {
998 GLenum err = glGetError ();
999 if (err != GL_NO_ERROR) {
1000 printf ("\e[0;31mERROR1 %d %d %#x\e[0m\n", w, slice->h, err);
1001 abort ();
1004 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1009 slice->h,
1010 state.texform,
1011 state.texty,
1012 page->pixmap->samples + offset
1015 GLenum err = glGetError ();
1016 if (err != GL_NO_ERROR) {
1017 printf ("\e[0;31mERROR %d %d %#x\e[0m\n", w, slice->h, err);
1018 abort ();
1022 else {
1023 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1025 GL_RGBA8,
1027 slice->h,
1029 state.texform,
1030 state.texty,
1031 page->pixmap->samples + offset
1035 end = now ();
1036 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
1037 subimage ? "sub" : "img",
1038 page->pageno, slicenum,
1039 slice->w, slice->h,
1040 state.texids[slice->texindex],
1041 end - start);
1045 CAMLprim value ml_draw (value dispy_v, value w_v, value h_v,
1046 value py_v, value ptr_v)
1048 CAMLparam5 (dispy_v, w_v, h_v, py_v, ptr_v);
1049 int dispy = Int_val (dispy_v);
1050 int w = Int_val (w_v);
1051 int h = Int_val (h_v);
1052 int py = Int_val (py_v);
1053 char *s = String_val (ptr_v);
1054 int ret;
1055 void *ptr;
1056 struct page *page;
1057 int slicenum = 0;
1059 if (trylock ("ml_draw")) {
1060 goto done;
1063 ret = sscanf (s, "%p", &ptr);
1064 if (ret != 1) {
1065 errx (1, "cannot parse pointer `%s'", s);
1067 page = ptr;
1069 w = page->pixmap->w;
1071 ARSERT (h >= 0 && "ml_draw wrong h");
1073 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1074 if (state.useatifs) {
1075 glEnable (GL_FRAGMENT_SHADER_ATI);
1078 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
1079 struct slice *slice = &page->slices[slicenum];
1080 if (slice->h > py) {
1081 break;
1083 py -= slice->h;
1086 h = MIN (state.h, h);
1087 while (h) {
1088 int th;
1089 struct slice *slice = &page->slices[slicenum];
1091 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
1093 th = MIN (h, slice->h - py);
1094 upload2 (page, slicenum, "upload");
1096 glBegin (GL_QUADS);
1098 glTexCoord2i (0, py);
1099 glVertex2i (0, dispy);
1101 glTexCoord2i (w, py);
1102 glVertex2i (w, dispy);
1104 glTexCoord2i (w, py+th);
1105 glVertex2i (w, dispy + th);
1107 glTexCoord2i (0, py+th);
1108 glVertex2i (0, dispy + th);
1110 glEnd ();
1112 h -= th;
1113 py = 0;
1114 dispy += th;
1115 slicenum += 1;
1118 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1119 if (state.useatifs) {
1120 glDisable (GL_FRAGMENT_SHADER_ATI);
1123 unlock ("ml_draw");
1124 done:
1125 CAMLreturn (Val_unit);
1128 static pdf_link *getlink (struct page *page, int x, int y)
1130 fz_point p;
1131 fz_matrix ctm;
1132 pdf_link *link;
1134 p.x = x;
1135 p.y = y;
1137 ctm = fz_invertmatrix (page->pagedim->ctm);
1138 p = fz_transformpoint (ctm, p);
1140 for (link = page->drawpage->links; link; link = link->next) {
1141 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1142 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1143 if (link->kind == PDF_LGOTO) {
1144 return link;
1149 return NULL;
1152 CAMLprim value ml_checklink (value ptr_v, value x_v, value y_v)
1154 CAMLparam3 (ptr_v, x_v, y_v);
1155 char *s = String_val (ptr_v);
1156 int ret;
1158 if (trylock ("ml_checklink")) {
1159 ret = 0;
1161 else {
1162 ret = NULL != getlink (parse_pointer ("ml_checklink", s),
1163 Int_val (x_v), Int_val (y_v));
1164 unlock ("ml_checklink");
1166 CAMLreturn (Val_bool (ret));
1169 CAMLprim value ml_getlink (value ptr_v, value x_v, value y_v)
1171 CAMLparam3 (ptr_v, x_v, y_v);
1172 CAMLlocal2 (ret_v, tup_v);
1173 pdf_link *link;
1174 struct page *page;
1175 char *s = String_val (ptr_v);
1177 if (trylock ("ml_getlink")) {
1178 ret_v = Val_int (0);
1179 goto done;
1182 page = parse_pointer ("ml_getlink", s);
1184 link = getlink (page, Int_val (x_v), Int_val (y_v));
1185 if (link) {
1186 int pageno;
1187 fz_point p;
1188 fz_obj *obj;
1190 pageno = -1;
1191 obj = fz_arrayget (link->dest, 0);
1192 if (fz_isindirect (obj)) {
1193 pageno = pdf_findpageobject (state.xref, obj) - 1;
1195 else if (fz_isint (obj)) {
1196 pageno = fz_toint (obj);
1199 if (fz_arraylen (link->dest) > 3) {
1200 p.x = fz_toint (fz_arrayget (link->dest, 2));
1201 p.y = fz_toint (fz_arrayget (link->dest, 3));
1202 p = fz_transformpoint (page->pagedim->ctm, p);
1204 else {
1205 p.x = 0.0;
1206 p.y = 0.0;
1209 tup_v = caml_alloc_tuple (2);
1210 ret_v = caml_alloc_small (1, 1);
1211 Field (tup_v, 0) = Val_int (pageno);
1212 Field (tup_v, 1) = Val_int (p.y);
1213 Field (ret_v, 0) = tup_v;
1215 else {
1216 ret_v = Val_int (0);
1218 unlock ("ml_getlink");
1220 done:
1221 CAMLreturn (ret_v);
1224 CAMLprim value ml_gettext (value ptr_v, value rect_v, value oy_v, value rectsel_v)
1226 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1227 fz_matrix ctm;
1228 fz_point p1, p2;
1229 struct page *page;
1230 fz_textspan *span;
1231 char *s = String_val (ptr_v);
1232 int rectsel = Bool_val (rectsel_v);
1233 int i, bx0, bx1, by0, by1, x0, x1, y0, y1, oy;
1235 /* stop GCC from complaining about uninitialized variables */
1236 int rx0 = rx0, rx1 = rx1, ry0 = ry0, ry1 = ry1;
1238 if (trylock ("ml_gettext")) {
1239 goto done;
1242 page = parse_pointer ("ml_gettext", s);
1244 oy = Int_val (oy_v);
1245 p1.x = Int_val (Field (rect_v, 0));
1246 p1.y = Int_val (Field (rect_v, 1));
1247 p2.x = Int_val (Field (rect_v, 2));
1248 p2.y = Int_val (Field (rect_v, 3));
1250 if (0) {
1251 glEnable (GL_BLEND);
1252 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1253 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1254 glColor4f (0, 0, 0, 0.2);
1255 glRecti (p1.x, p1.y, p2.x, p2.y);
1256 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1257 glDisable (GL_BLEND);
1260 ctm = page->pagedim->ctm;
1261 if (!page->text) {
1262 fz_error error;
1263 fz_device *tdev;
1265 page->text = fz_newtextspan ();
1266 tdev = fz_newtextdevice (page->text);
1267 error = pdf_runcontentstream (tdev, page->pagedim->ctm, state.xref,
1268 page->drawpage->resources,
1269 page->drawpage->contents);
1270 if (error) die (error);
1271 fz_freedevice (tdev);
1274 printf ("\ec");
1276 printf ("BBox %f %f %f %f\n", p1.x, p1.y, p2.x, p2.y);
1277 p1.x += page->pixmap->x;
1278 p1.y += page->pixmap->y;
1279 p2.x += page->pixmap->x;
1280 p2.y += page->pixmap->y;
1281 x0 = p1.x;
1282 y0 = p1.y;
1283 x1 = p2.x;
1284 y1 = p2.y;
1285 printf ("BBox %d %d %d %d %d %d\n", x0, y0, x1, y1, oy, page->pageno);
1287 for (span = page->text; span; span = span->next) {
1288 int seen = 0;
1290 /* fz_debugtextspanxml (span); */
1291 for (i = 0; i < span->len; ++i) {
1292 long c;
1294 bx0 = span->text[i].bbox.x0;
1295 bx1 = span->text[i].bbox.x1;
1296 by0 = span->text[i].bbox.y0 + oy;
1297 by1 = span->text[i].bbox.y1 + oy;
1299 if ((bx1 >= x0 && bx0 <= x1 && by1 >= y0 && by0 <= y1)) {
1300 if (!seen) {
1301 rx0 = bx0 - page->pixmap->x;
1302 rx1 = bx1 - page->pixmap->x;
1303 ry0 = by0;
1304 ry1 = by1;
1307 seen = 1;
1308 c = span->text[i].c;
1309 if (c < 256) {
1310 if ((isprint (c) && !isspace (c))) {
1311 if (!rectsel) {
1312 bx0 -= page->pixmap->x;
1313 bx1 -= page->pixmap->x;
1314 glEnable (GL_BLEND);
1315 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1316 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1317 glColor4f (0.5, 0.5, 0.0, 0.6);
1318 glRecti (bx0, by0, bx1, by1);
1319 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1320 glDisable (GL_BLEND);
1322 if (isprint (c) || c ==' ') {
1323 rx1 = bx1;
1324 ry1 = by1;
1327 putc (c, stdout);
1329 else {
1330 putc ('?', stdout);
1335 if (rectsel) {
1336 if (seen) {
1337 glEnable (GL_BLEND);
1338 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1339 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1340 glColor4f (0.5, 0.5, 0.0, 0.6);
1341 glRecti (rx0, ry0, rx1, ry1);
1342 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1343 glDisable (GL_BLEND);
1347 if (seen && span->eol) {
1348 x0 = page->pixmap->x;
1349 putc ('\n', stdout);
1352 unlock ("ml_gettext");
1354 done:
1355 CAMLreturn (Val_unit);
1358 CAMLprim value ml_getpagewh (value pagedimno_v)
1360 CAMLparam1 (pagedimno_v);
1361 CAMLlocal1 (ret_v);
1362 int pagedimno = Int_val (pagedimno_v);
1364 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1365 Store_double_field (ret_v, 0, state.pagedims[pagedimno].box.x0);
1366 Store_double_field (ret_v, 1, state.pagedims[pagedimno].box.x1);
1367 Store_double_field (ret_v, 2, state.pagedims[pagedimno].box.y0);
1368 Store_double_field (ret_v, 3, state.pagedims[pagedimno].box.y1);
1369 CAMLreturn (ret_v);
1372 CAMLprim value ml_gettitle (value unit_v)
1374 CAMLparam1 (unit_v);
1375 CAMLlocal1 (title_v);
1376 fz_obj *obj;
1378 title_v = Val_int (0);
1379 if (!trylock ("ml_getttile")) {
1380 obj = fz_dictgets (state.xref->trailer, "Info");
1381 if (obj) {
1382 obj = fz_dictgets (obj, "Title");
1383 if (obj) {
1384 char *utf8 = pdf_toutf8 (obj);
1385 if (*utf8) {
1386 title_v = caml_alloc_small (1, 1);
1387 Store_field (title_v, 0, caml_copy_string (utf8));
1391 unlock ("ml_getttile");
1393 CAMLreturn (title_v);
1396 static void initgl (void)
1398 #ifdef _BIG_ENDIAN
1399 if (strstr ((char *) glGetString (GL_EXTENSIONS),
1400 "GL_ATI_fragment_shader")) {
1401 /* Here, with MESA, rv280, powerpc32: BGRA(rev) is slow while
1402 ABGR is fast, so fix things in the shader */
1403 state.texform = GL_ABGR_EXT;
1404 state.texty = GL_UNSIGNED_INT_8_8_8_8;
1406 glBindFragmentShaderATI (1);
1407 glBeginFragmentShaderATI ();
1409 glSampleMapATI (GL_REG_0_ATI, GL_TEXTURE0_ARB, GL_SWIZZLE_STR_ATI);
1411 glColorFragmentOp1ATI (GL_MOV_ATI,
1412 GL_REG_1_ATI, GL_RED_BIT_ATI, GL_NONE,
1413 GL_REG_0_ATI, GL_BLUE, GL_NONE);
1414 glColorFragmentOp1ATI (GL_MOV_ATI,
1415 GL_REG_1_ATI, GL_BLUE_BIT_ATI, GL_NONE,
1416 GL_REG_0_ATI, GL_RED, GL_NONE);
1417 glColorFragmentOp1ATI (
1418 GL_MOV_ATI,
1419 GL_REG_0_ATI, GL_RED_BIT_ATI | GL_BLUE_BIT_ATI, GL_NONE,
1420 GL_REG_1_ATI, GL_NONE, GL_NONE
1423 glEndFragmentShaderATI ();
1424 state.useatifs = 1;
1426 else {
1427 state.texform = GL_BGRA_EXT;
1428 state.texty = GL_UNSIGNED_INT_8_8_8_8_REV;
1430 #else
1431 state.texform = GL_BGRA_EXT;
1432 state.texty = GL_UNSIGNED_INT_8_8_8_8;
1433 #endif
1436 CAMLprim value ml_init (value sock_v)
1438 int ret;
1439 CAMLparam1 (sock_v);
1441 state.texcount = 128;
1442 state.sliceheight = 64;
1444 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1445 if (!state.texids) {
1446 err (1, "calloc texids %zu", state.texcount * sizeof (*state.texids));
1449 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1450 if (!state.texowners) {
1451 err (1, "calloc texowners %zu",
1452 state.texcount * sizeof (*state.texowners));
1455 glGenTextures (state.texcount, state.texids);
1457 state.sock = Int_val (sock_v);
1458 initgl ();
1460 state.cache = fz_newglyphcache ();
1461 if (!state.cache) {
1462 errx (1, "fz_newglyphcache failed");
1465 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1466 if (ret) {
1467 errx (1, "pthread_create: %s", strerror (errno));
1470 CAMLreturn (Val_unit);