Sigh...
[llpp.git] / link.c
blobc1455d8361c217e3f7992745963206ceb22a77eb
1 /* lot's of code c&p-ed directly from mupdf */
2 #ifdef _WIN32
3 #define WIN32_LEAN_AND_MEAN
4 #include <windows.h>
5 #include <winsock2.h>
6 #define ssize_t int
7 #pragma warning (disable:4244)
8 #pragma warning (disable:4996)
9 #pragma warning (disable:4995)
10 #endif
12 #ifdef _MSC_VER
13 #include <errno.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <stdlib.h>
17 static void __declspec (noreturn) err (int exitcode, const char *fmt, ...)
19 va_list ap;
20 int errcode;
22 errcode = errno;
23 va_start (ap, fmt);
24 vfprintf (stderr, fmt, ap);
25 va_end (ap);
26 fprintf (stderr, ": %s\n", strerror (errno));
27 exit (exitcode);
29 static void __declspec (noreturn) errx (int exitcode, const char *fmt, ...)
31 va_list ap;
32 int errcode;
34 errcode = errno;
35 va_start (ap, fmt);
36 vfprintf (stderr, fmt, ap);
37 va_end (ap);
38 fputc ('\n', stderr);
39 exit (exitcode);
41 static void __declspec (noreturn) sockerr (int exitcode, const char *fmt, ...)
43 va_list ap;
44 int errcode;
46 errcode = errno;
47 va_start (ap, fmt);
48 vfprintf (stderr, fmt, ap);
49 va_end (ap);
50 fprintf (stderr, ": wsaerror %lx\n", WSAGetLastError ());
51 exit (exitcode);
53 #else
54 #define _GNU_SOURCE
55 #include <err.h>
56 #define sockerr err
57 #endif
58 #include <regex.h>
59 #include <errno.h>
60 #include <ctype.h>
61 #include <stdio.h>
62 #include <stdarg.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #ifndef _WIN32
66 #include <pthread.h>
67 #include <sys/time.h>
68 #include <sys/types.h>
69 #include <sys/socket.h>
70 #include <sys/select.h>
71 #endif
73 #ifdef __APPLE__
74 #include <OpenGL/gl.h>
75 #else
76 #include <GL/gl.h>
77 #endif
79 #ifndef GL_TEXTURE_RECTANGLE_ARB
80 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
81 #endif
83 #include <caml/fail.h>
84 #include <caml/alloc.h>
85 #include <caml/memory.h>
86 #include <caml/unixsupport.h>
88 #include <fitz.h>
89 #include <mupdf.h>
91 #if 0
92 #define lprintf printf
93 #else
94 #define lprintf(...)
95 #endif
97 #define ARSERT(cond) for (;;) { \
98 if (!(cond)) { \
99 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
101 break; \
104 struct slice {
105 int texindex;
106 int w, h;
109 struct page {
110 int pageno;
111 int slicecount;
112 fz_textspan *text;
113 fz_pixmap *pixmap;
114 pdf_page *drawpage;
115 struct pagedim *pagedim;
116 struct page *prev;
117 struct slice slices[];
120 struct pagedim {
121 int pageno;
122 int rotate;
123 fz_rect box;
124 fz_bbox bbox;
125 fz_matrix ctm;
128 struct {
129 int sock;
130 int sliceheight;
131 struct page *pages;
132 struct pagedim *pagedims;
133 int pagecount;
134 int pagedimcount;
135 pdf_xref *xref;
136 fz_glyphcache *cache;
137 int w, h;
139 int texindex;
140 int texcount;
141 GLuint *texids;
143 GLenum texform;
144 GLenum texty;
146 int lotsamemory;
148 int *pagetbl;
149 struct {
150 int w, h;
151 struct slice *slice;
152 } *texowners;
154 #ifdef _WIN32
155 HANDLE thread;
156 #else
157 pthread_t thread;
158 #endif
159 } state;
161 #ifdef _WIN32
162 static CRITICAL_SECTION critsec;
164 static void lock (void *unused)
166 (void) unused;
167 EnterCriticalSection (&critsec);
170 static void unlock (void *unused)
172 (void) unused;
173 LeaveCriticalSection (&critsec);
176 static int trylock (void *unused)
178 return TryEnterCriticalSection (&critsec) == 0;
180 #else
181 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
183 static void lock (const char *cap)
185 int ret = pthread_mutex_lock (&mutex);
186 if (ret) {
187 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
191 static void unlock (const char *cap)
193 int ret = pthread_mutex_unlock (&mutex);
194 if (ret) {
195 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
199 static int trylock (const char *cap)
201 int ret = pthread_mutex_trylock (&mutex);
203 if (ret && ret != EBUSY) {
204 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
206 return ret == EBUSY;
208 #endif
210 static void *parse_pointer (const char *cap, const char *s)
212 int ret;
213 void *ptr;
215 ret = sscanf (s, "%p", &ptr);
216 if (ret != 1) {
217 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
219 return ptr;
222 static int hasdata (int sock)
224 int n;
225 fd_set s;
226 struct timeval tv;
227 FD_ZERO (&s);
228 FD_SET (sock, &s);
229 tv.tv_sec = 0;
230 tv.tv_usec = 0;
231 n = select (sock + 1, &s, NULL, NULL, &tv);
232 if (n == 0) return 0;
233 if (n == 1) return 1;
234 sockerr (1, "hasdata: select error ret=%d", n);
237 static double now (void)
239 struct timeval tv;
241 if (gettimeofday (&tv, NULL)) {
242 err (1, "gettimeofday");
244 return tv.tv_sec + tv.tv_usec*1e-6;
247 static void readdata (int fd, char *p, int size)
249 ssize_t n;
251 n = recv (fd, p, size, 0);
252 if (n - size) {
253 if (!n) errx (1, "EOF while reading");
254 sockerr (1, "recv (req %d, ret %zd)", size, n);
258 static void writedata (int fd, char *p, int size)
260 char buf[4];
261 ssize_t n;
263 buf[0] = (size >> 24) & 0xff;
264 buf[1] = (size >> 16) & 0xff;
265 buf[2] = (size >> 8) & 0xff;
266 buf[3] = (size >> 0) & 0xff;
268 n = send (fd, buf, 4, 0);
269 if (n != 4) {
270 if (!n) errx (1, "EOF while writing length");
271 sockerr (1, "send %zd", n);
274 n = send (fd, p, size, 0);
275 if (n - size) {
276 if (!n) errx (1, "EOF while writing data");
277 sockerr (1, "send (req %d, ret %zd)", size, n);
281 static void
282 #ifdef __GNUC__
283 __attribute__ ((format (printf, 2, 3)))
284 #endif
285 printd (int fd, const char *fmt, ...)
287 int len;
288 va_list ap;
289 char buf[200];
291 va_start (ap, fmt);
292 len = vsnprintf (buf, sizeof (buf), fmt, ap);
293 va_end (ap);
294 writedata (fd, buf, len);
297 static void die (fz_error error)
299 fz_catch (error, "aborting");
300 if (state.xref)
301 pdf_closexref (state.xref);
302 exit (1);
305 static void openxref (char *filename)
307 int fd;
308 fz_error error;
309 fz_stream *file;
311 fd = open (filename, O_BINARY | O_RDONLY, 0666);
312 if (fd < 0)
313 die (fz_throw ("cannot open file '%s'", filename));
315 file = fz_openfile (fd);
316 state.xref = pdf_openxref (file);
317 if (!state.xref)
318 die (fz_throw ("cannot open PDF file '%s'", filename));
319 fz_dropstream (file);
321 if (pdf_needspassword (state.xref)) {
322 die (fz_throw ("password protected"));
325 error = pdf_loadpagetree (state.xref);
326 if (error) {
327 die (fz_throw ("cannot load page tree"));
330 state.pagecount = pdf_getpagecount (state.xref);
331 state.pagetbl = stat_alloc (state.pagecount * sizeof (*state.pagetbl));
334 static int readlen (int fd)
336 ssize_t n;
337 char p[4];
339 n = recv (fd, p, 4, 0);
340 if (n != 4) {
341 if (!n) errx (1, "EOF while reading length");
342 sockerr (1, "read %zd", n);
345 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
348 static void freepage (struct page *page)
350 int i;
351 struct page *p;
353 fz_droppixmap (page->pixmap);
354 for (p = state.pages; p; p = p->prev) {
355 if (p->prev == page) {
356 p->prev = page->prev;
357 break;
360 for (i = 0; i < page->slicecount; ++i) {
361 struct slice *s = &page->slices[i];
362 if (s->texindex != -1) {
363 if (state.texowners[s->texindex].slice == s) {
364 state.texowners[s->texindex].slice = NULL;
365 ARSERT (state.texowners[s->texindex].w == s->w);
366 ARSERT (state.texowners[s->texindex].h >= s->h);
370 if (page->text) {
371 fz_freetextspan (page->text);
373 if (page->drawpage) {
374 pdf_freepage (page->drawpage);
377 free (page);
380 static void subdivide (struct page *p)
382 int i;
383 int h = p->pixmap->h;
384 int th = MIN (h, state.sliceheight);
386 for (i = 0; i < p->slicecount; ++i) {
387 struct slice *s = &p->slices[i];
388 s->texindex = -1;
389 s->h = MIN (th, h);
390 s->w = p->pixmap->w;
391 h -= th;
395 static void *render (int pageno, int pindex)
397 fz_error error;
398 int slicecount;
399 fz_obj *pageobj;
400 struct page *page;
401 double start, end;
402 pdf_page *drawpage;
403 fz_device *idev;
404 struct pagedim *pagedim;
406 start = now ();
407 printd (state.sock, "V rendering %d", pageno);
408 pdf_flushxref (state.xref, 0);
410 pagedim = &state.pagedims[pindex];
411 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
412 + state.sliceheight - 1) / state.sliceheight;
413 slicecount += slicecount == 0;
415 page = calloc (sizeof (*page)
416 + (slicecount * sizeof (struct slice)), 1);
417 if (!page) {
418 err (1, "calloc page %d\n", pageno);
420 page->slicecount = slicecount;
421 page->prev = state.pages;
422 state.pages = page;
424 pageobj = pdf_getpageobject (state.xref, pageno);
425 if (!pageobj)
426 die (fz_throw ("cannot retrieve info from page %d", pageno));
428 error = pdf_loadpage (&drawpage, state.xref, pageobj);
429 if (error)
430 die (error);
432 page->pixmap = fz_newpixmapwithrect (pdf_devicergb, pagedim->bbox);
433 if (error)
434 die (error);
435 fz_clearpixmap (page->pixmap, 0xFF);
437 idev = fz_newdrawdevice (state.cache, page->pixmap);
438 if (!idev)
439 die (fz_throw ("fz_newdrawdevice failed"));
440 error = pdf_runcontentstream (idev, pagedim->ctm, state.xref,
441 drawpage->resources,
442 drawpage->contents);
443 fz_freedevice (idev);
445 page->drawpage = drawpage;
446 page->pagedim = pagedim;
447 page->pageno = pageno;
448 subdivide (page);
449 end = now ();
451 if (!state.lotsamemory) {
452 pdf_agestoreditems (state.xref->store);
453 pdf_evictageditems (state.xref->store);
456 printd (state.sock, "V rendering %d took %f sec", pageno, end - start);
457 return page;
460 static void initpdims (void)
462 int pageno;
463 double start, end;
465 start = now ();
466 for (pageno = 0; pageno < state.pagecount; ++pageno) {
467 int rotate;
468 fz_rect box;
469 struct pagedim *p;
470 fz_obj *obj, *pageobj;
472 pageobj = pdf_getpageobject (state.xref, pageno + 1);
474 obj = fz_dictgets (pageobj, "CropBox");
475 if (!fz_isarray (obj)) {
476 obj = fz_dictgets (pageobj, "MediaBox");
477 if (!fz_isarray (obj)) {
478 die (fz_throw ("cannot find page bounds %d (%d Rd)",
479 fz_tonum (pageobj), fz_togen (pageobj)));
482 box = pdf_torect (obj);
484 obj = fz_dictgets (pageobj, "Rotate");
485 if (fz_isint (obj)) {
486 rotate = fz_toint (obj);
488 else {
489 rotate = 0;
492 state.pagetbl[pageno] = fz_tonum (state.xref->pagerefs[pageno]);
493 p = &state.pagedims[state.pagedimcount - 1];
494 if ((state.pagedimcount == 0)
495 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
496 size_t size;
498 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
499 state.pagedims = realloc (state.pagedims, size);
500 if (!state.pagedims) {
501 err (1, "realloc pagedims to %zu (%d elems)",
502 size, state.pagedimcount + 1);
504 p = &state.pagedims[state.pagedimcount++];
505 p->rotate = rotate;
506 p->box = box;
507 p->pageno = pageno;
510 end = now ();
511 printd (state.sock, "T Processed %d pages in %f seconds",
512 state.pagecount, end - start);
515 static void layout (void)
517 int pindex;
518 fz_matrix ctm;
519 fz_rect box, box2;
520 double zoom, w;
521 struct pagedim *p = state.pagedims;
523 pindex = 0;
524 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
525 box.x0 = MIN (p->box.x0, p->box.x1);
526 box.y0 = MIN (p->box.y0, p->box.y1);
527 box.x1 = MAX (p->box.x0, p->box.x1);
528 box.y1 = MAX (p->box.y0, p->box.y1);
530 ctm = fz_identity ();
531 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
532 ctm = fz_concat (ctm, fz_rotate (p->rotate));
533 box2 = fz_transformrect (ctm, box);
534 w = box2.x1 - box2.x0;
536 zoom = (state.w / w);
537 ctm = fz_identity ();
538 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
539 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
540 ctm = fz_concat (ctm, fz_rotate (p->rotate));
541 p->bbox = fz_roundrect (fz_transformrect (ctm, box));
542 memcpy (&p->ctm, &ctm, sizeof (ctm));
545 while (p-- != state.pagedims) {
546 printd (state.sock, "l %d %d %d",
547 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0);
551 static void recurse_outline (pdf_outline *outline, int level)
553 while (outline) {
554 int i, num;
555 fz_obj *obj;
556 int top = 0;
557 int pageno = -1;
559 if (!outline->link) goto next;
561 obj = outline->link->dest;
562 if (fz_isindirect (obj)) {
563 num = fz_tonum (obj);
565 for (i = 0; i < state.pagecount; ++i) {
566 if (state.pagetbl[i] == num) {
567 pageno = i;
568 break;
572 else if (fz_isarray (obj)) {
573 fz_obj *obj2;
575 obj2 = fz_arrayget (obj, 0);
576 if (fz_isint (obj2)) {
577 pageno = fz_toint (obj2);
579 else {
580 num = fz_tonum (obj2);
581 for (i = 0; i < state.pagecount; ++i) {
582 if (state.pagetbl[i] == num) {
583 pageno = i;
584 break;
589 if (fz_arraylen (obj) > 3) {
590 fz_point p;
591 struct pagedim *pagedim = state.pagedims;
593 for (i = 0; i < state.pagedimcount; ++i) {
594 if (state.pagedims[i].pageno > pageno)
595 break;
596 pagedim = &state.pagedims[i];
599 p.x = fz_toint (fz_arrayget (obj, 2));
600 p.y = fz_toint (fz_arrayget (obj, 3));
601 p = fz_transformpoint (pagedim->ctm, p);
602 top = p.y;
606 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
607 printd (state.sock, "o %d %d %d %s",
608 level, pageno, top, outline->title);
609 next:
610 if (outline->child) {
611 recurse_outline (outline->child, level + 1);
613 outline = outline->next;
617 static void process_outline (void)
619 pdf_outline *outline;
621 outline = pdf_loadoutline (state.xref);
622 if (outline) {
623 recurse_outline (outline, 0);
624 pdf_freeoutline (outline);
628 static int comparespans (const void *l, const void *r)
630 fz_textspan const *const*ls = l;
631 fz_textspan const *const*rs = r;
632 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
635 /* wishful thinking function */
636 static void search (regex_t *re, int pageno, int y, int forward)
638 int i, j;
639 int ret;
640 char *p;
641 char buf[256];
642 fz_error error;
643 fz_obj *pageobj;
644 fz_device *tdev;
645 pdf_page *drawpage;
646 fz_textspan *text, *span, **pspan;
647 struct pagedim *pdim, *pdimprev;
648 int stop = 0;
649 int niters = 0;
650 int nspans;
651 double start, end;
653 start = now ();
654 while (pageno >= 0 && pageno < state.pagecount && !stop) {
655 if (niters++ == 5) {
656 if (!state.lotsamemory) {
657 pdf_agestoreditems (state.xref->store);
658 pdf_evictageditems (state.xref->store);
660 niters = 0;
661 if (hasdata (state.sock)) {
662 printd (state.sock, "T attention requested aborting search at %d",
663 pageno);
664 stop = 1;
666 else {
667 printd (state.sock, "T searching in page %d", pageno);
670 pdimprev = NULL;
671 for (i = 0; i < state.pagedimcount; ++i) {
672 pdim = &state.pagedims[i];
673 if (pdim->pageno == pageno) {
674 goto found;
676 if (pdim->pageno > pageno) {
677 pdim = pdimprev;
678 goto found;
680 pdimprev = pdim;
682 pdim = pdimprev;
683 found:
685 pageobj = pdf_getpageobject (state.xref, pageno + 1);
686 if (!pageobj)
687 die (fz_throw ("cannot retrieve info from page %d", pageno));
689 error = pdf_loadpage (&drawpage, state.xref, pageobj);
690 if (error)
691 die (error);
693 text = fz_newtextspan ();
694 tdev = fz_newtextdevice (text);
695 error = pdf_runcontentstream (tdev, pdim->ctm, state.xref,
696 drawpage->resources,
697 drawpage->contents);
698 if (error) die (error);
699 fz_freedevice (tdev);
701 nspans = 0;
702 for (span = text; span; span = span->next) {
703 nspans++;
705 pspan = malloc (sizeof (void *) * nspans);
706 if (!pspan) {
707 err (1, "malloc span pointers %zu", sizeof (void *) * nspans);
709 for (i = 0, span = text; span; span = span->next, ++i) {
710 pspan[i] = span;
712 qsort (pspan, nspans, sizeof (fz_textspan *), comparespans);
714 j = forward ? 0 : nspans - 1;
715 while (nspans--) {
716 regmatch_t rm;
718 span = pspan[j];
719 j += forward ? 1 : -1;
720 p = buf;
721 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
722 if (forward) {
723 if (span->text[i].bbox.y0 < y + 1) {
724 continue;
727 else {
728 if (span->text[i].bbox.y0 > y - 1) {
729 continue;
732 if (span->text[i].c < 256) {
733 *p++ = span->text[i].c;
735 else {
736 *p++ = '?';
739 if (p == buf) {
740 continue;
742 *p++ = 0;
744 ret = regexec (re, buf, 1, &rm, 0);
745 if (ret) {
746 if (ret != REG_NOMATCH) {
747 size_t size;
748 char errbuf[80];
749 size = regerror (ret, re, errbuf, sizeof (errbuf));
750 printd (state.sock,
751 "T regexec error `%.*s'",
752 (int) size, errbuf);
753 fz_freetextspan (text);
754 pdf_freepage (drawpage);
755 free (pspan);
756 return;
759 else {
760 fz_rect r;
762 r.x0 = span->text[rm.rm_so].bbox.x0 - pdim->bbox.x0;
763 r.y0 = span->text[rm.rm_so].bbox.y0;
764 r.x1 = span->text[rm.rm_eo - 1].bbox.x1 - pdim->bbox.x0;
765 r.y1 = span->text[rm.rm_eo - 1].bbox.y1;
767 if (!stop) {
768 printd (state.sock, "F %d %d %f %f %f %f",
769 pageno, 1,
770 r.x0, r.y0,
771 r.x1, r.y1);
773 else {
774 printd (state.sock, "R %d %d %f %f %f %f",
775 pageno, 2,
776 r.x0, r.y0,
777 r.x1, r.y1);
779 printd (state.sock, "T found at %d `%.*s' %f in %f sec",
780 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
781 span->text[0].bbox.y0 - drawpage->mediabox.y0,
782 now () - start);
783 stop = 1;
786 if (forward) {
787 pageno += 1;
788 y = 0;
790 else {
791 pageno -= 1;
792 y = INT_MAX;
794 fz_freetextspan (text);
795 pdf_freepage (drawpage);
796 free (pspan);
798 end = now ();
799 if (!stop) {
800 printd (state.sock, "T no matches %f sec", end - start);
802 printd (state.sock, "D");
805 static
806 #ifdef _WIN32
807 DWORD _stdcall
808 #else
809 void *
810 #endif
811 mainloop (void *unused)
813 char *p = NULL;
814 int len, ret, oldlen = 0;
816 for (;;) {
817 len = readlen (state.sock);
818 if (len == 0) {
819 errx (1, "readlen returned 0");
822 if (oldlen < len + 1) {
823 p = realloc (p, len + 1);
824 if (!p) {
825 err (1, "realloc %d failed", len + 1);
827 oldlen = len + 1;
829 readdata (state.sock, p, len);
830 p[len] = 0;
832 if (!strncmp ("open", p, 4)) {
833 fz_obj *obj;
834 char *filename = p + 5;
836 openxref (filename);
837 initpdims ();
839 obj = fz_dictgets (state.xref->trailer, "Info");
840 if (obj) {
841 obj = fz_dictgets (obj, "Title");
842 if (obj) {
843 printd (state.sock, "t %s", pdf_toutf8 (obj));
847 else if (!strncmp ("free", p, 4)) {
848 void *ptr;
850 ret = sscanf (p + 4, " %p", &ptr);
851 if (ret != 1) {
852 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
854 lock ("free");
855 freepage (ptr);
856 unlock ("free");
857 printd (state.sock, "d");
859 else if (!strncmp ("search", p, 6)) {
860 int icase, pageno, y, ret, len2, forward;
861 char *pattern;
862 regex_t re;
864 ret = sscanf (p + 6, " %d %d %d %d,%n",
865 &icase, &pageno, &y, &forward, &len2);
866 if (ret != 4) {
867 errx (1, "malformed search `%s' ret=%d", p, ret);
870 pattern = p + 6 + len2;
871 ret = regcomp (&re, pattern,
872 REG_EXTENDED | (icase ? REG_ICASE : 0));
873 if (ret) {
874 char errbuf[80];
875 size_t size;
877 size = regerror (ret, &re, errbuf, sizeof (errbuf));
878 printd (state.sock, "T regcomp failed `%.*s'",
879 (int) size, errbuf);
881 else {
882 search (&re, pageno, y, forward);
883 regfree (&re);
886 else if (!strncmp ("geometry", p, 8)) {
887 int w, h;
889 printd (state.sock, "c");
890 ret = sscanf (p + 8, " %d %d", &w, &h);
891 if (ret != 2) {
892 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
894 state.h = h;
895 if (w != state.w) {
896 int i;
897 state.w = w;
898 for (i = 0; i < state.texcount; ++i) {
899 state.texowners[i].slice = NULL;
902 lock ("geometry");
903 layout ();
904 process_outline ();
905 unlock ("geometry");
906 printd (state.sock, "C %d", state.pagecount);
908 else if (!strncmp ("render", p, 6)) {
909 int pageno, pindex, w, h, ret;
910 struct page *page;
912 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
913 if (ret != 4) {
914 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
917 page = render (pageno, pindex);
918 printd (state.sock, "r %d %d %d %p\n",
919 pageno,
920 state.w,
921 state.h,
922 page);
924 else {
925 errx (1, "unknown command %.*s", len, p);
928 return 0;
931 static void upload2 (struct page *page, int slicenum, const char *cap)
933 int i;
934 int w, h;
935 double start, end;
936 struct slice *slice = &page->slices[slicenum];
938 w = page->pixmap->w;
939 h = page->pixmap->h;
941 ARSERT (w == slice->w);
942 if (slice->texindex != -1
943 && state.texowners[slice->texindex].slice == slice) {
944 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
946 else {
947 int subimage = 0;
948 int index = (state.texindex++ % state.texcount);
949 size_t offset = 0;
951 for (i = 0; i < slicenum; ++i) {
952 offset += w * page->slices[i].h * 4;
955 if (state.texowners[index].w == slice->w) {
956 if (state.texowners[index].h >= slice->h ) {
957 subimage = 1;
959 else {
960 state.texowners[index].h = slice->h;
963 else {
964 state.texowners[index].h = slice->h;
967 state.texowners[index].slice = slice;
968 state.texowners[index].w = slice->w;
969 slice->texindex = index;
971 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
972 start = now ();
973 if (subimage) {
975 GLenum err = glGetError ();
976 if (err != GL_NO_ERROR) {
977 printf ("\033[0;31mERROR1 %d %d %#x\033[0m\n", w, slice->h, err);
978 abort ();
981 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
986 slice->h,
987 state.texform,
988 state.texty,
989 page->pixmap->samples + offset
992 GLenum err = glGetError ();
993 if (err != GL_NO_ERROR) {
994 printf ("\033[0;31mERROR %d %d %#x\033[0m\n", w, slice->h, err);
995 abort ();
999 else {
1000 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1002 GL_RGBA8,
1004 slice->h,
1006 state.texform,
1007 state.texty,
1008 page->pixmap->samples + offset
1012 end = now ();
1013 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
1014 subimage ? "sub" : "img",
1015 page->pageno, slicenum,
1016 slice->w, slice->h,
1017 state.texids[slice->texindex],
1018 end - start);
1022 CAMLprim value ml_draw (value dispy_v, value w_v, value h_v,
1023 value py_v, value ptr_v)
1025 CAMLparam5 (dispy_v, w_v, h_v, py_v, ptr_v);
1026 int dispy = Int_val (dispy_v);
1027 int w = Int_val (w_v);
1028 int h = Int_val (h_v);
1029 int py = Int_val (py_v);
1030 char *s = String_val (ptr_v);
1031 int ret;
1032 void *ptr;
1033 struct page *page;
1034 int slicenum = 0;
1036 if (trylock ("ml_draw")) {
1037 goto done;
1040 ret = sscanf (s, "%p", &ptr);
1041 if (ret != 1) {
1042 errx (1, "cannot parse pointer `%s'", s);
1044 page = ptr;
1046 w = page->pixmap->w;
1048 ARSERT (h >= 0 && "ml_draw wrong h");
1050 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1052 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
1053 struct slice *slice = &page->slices[slicenum];
1054 if (slice->h > py) {
1055 break;
1057 py -= slice->h;
1060 h = MIN (state.h, h);
1061 while (h) {
1062 int th;
1063 struct slice *slice = &page->slices[slicenum];
1065 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
1067 th = MIN (h, slice->h - py);
1068 upload2 (page, slicenum, "upload");
1070 glBegin (GL_QUADS);
1072 glTexCoord2i (0, py);
1073 glVertex2i (0, dispy);
1075 glTexCoord2i (w, py);
1076 glVertex2i (w, dispy);
1078 glTexCoord2i (w, py+th);
1079 glVertex2i (w, dispy + th);
1081 glTexCoord2i (0, py+th);
1082 glVertex2i (0, dispy + th);
1084 glEnd ();
1086 h -= th;
1087 py = 0;
1088 dispy += th;
1089 slicenum += 1;
1092 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1094 unlock ("ml_draw");
1095 done:
1096 CAMLreturn (Val_unit);
1099 static pdf_link *getlink (struct page *page, int x, int y)
1101 fz_point p;
1102 fz_matrix ctm;
1103 pdf_link *link;
1105 p.x = x;
1106 p.y = y;
1108 ctm = fz_invertmatrix (page->pagedim->ctm);
1109 p = fz_transformpoint (ctm, p);
1111 for (link = page->drawpage->links; link; link = link->next) {
1112 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1113 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1114 if (link->kind == PDF_LGOTO) {
1115 return link;
1120 return NULL;
1123 CAMLprim value ml_checklink (value ptr_v, value x_v, value y_v)
1125 CAMLparam3 (ptr_v, x_v, y_v);
1126 char *s = String_val (ptr_v);
1127 int ret;
1129 if (trylock ("ml_checklink")) {
1130 ret = 0;
1132 else {
1133 ret = NULL != getlink (parse_pointer ("ml_checklink", s),
1134 Int_val (x_v), Int_val (y_v));
1135 unlock ("ml_checklink");
1137 CAMLreturn (Val_bool (ret));
1140 CAMLprim value ml_getlink (value ptr_v, value x_v, value y_v)
1142 CAMLparam3 (ptr_v, x_v, y_v);
1143 CAMLlocal2 (ret_v, tup_v);
1144 pdf_link *link;
1145 struct page *page;
1146 char *s = String_val (ptr_v);
1148 if (trylock ("ml_getlink")) {
1149 ret_v = Val_int (0);
1150 goto done;
1153 page = parse_pointer ("ml_getlink", s);
1155 link = getlink (page, Int_val (x_v), Int_val (y_v));
1156 if (link) {
1157 int pageno;
1158 fz_point p;
1159 fz_obj *obj;
1161 pageno = -1;
1162 obj = fz_arrayget (link->dest, 0);
1163 if (fz_isindirect (obj)) {
1164 pageno = pdf_findpageobject (state.xref, obj) - 1;
1166 else if (fz_isint (obj)) {
1167 pageno = fz_toint (obj);
1170 if (fz_arraylen (link->dest) > 3) {
1171 p.x = fz_toint (fz_arrayget (link->dest, 2));
1172 p.y = fz_toint (fz_arrayget (link->dest, 3));
1173 p = fz_transformpoint (page->pagedim->ctm, p);
1175 else {
1176 p.x = 0.0;
1177 p.y = 0.0;
1180 tup_v = caml_alloc_tuple (2);
1181 ret_v = caml_alloc_small (1, 1);
1182 Field (tup_v, 0) = Val_int (pageno);
1183 Field (tup_v, 1) = Val_int (p.y);
1184 Field (ret_v, 0) = tup_v;
1186 else {
1187 ret_v = Val_int (0);
1189 unlock ("ml_getlink");
1191 done:
1192 CAMLreturn (ret_v);
1195 CAMLprim value ml_gettext (value ptr_v, value rect_v, value oy_v, value rectsel_v)
1197 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1198 fz_matrix ctm;
1199 fz_point p1, p2;
1200 struct page *page;
1201 fz_textspan *span;
1202 char *s = String_val (ptr_v);
1203 int rectsel = Bool_val (rectsel_v);
1204 int i, bx0, bx1, by0, by1, x0, x1, y0, y1, oy;
1206 /* stop GCC from complaining about uninitialized variables */
1207 #ifdef __GNUC__
1208 int rx0 = rx0, rx1 = rx1, ry0 = ry0, ry1 = ry1;
1209 #else
1210 int rx0, rx1, ry0, ry1;
1211 #endif
1213 if (trylock ("ml_gettext")) {
1214 goto done;
1217 page = parse_pointer ("ml_gettext", s);
1219 oy = Int_val (oy_v);
1220 p1.x = Int_val (Field (rect_v, 0));
1221 p1.y = Int_val (Field (rect_v, 1));
1222 p2.x = Int_val (Field (rect_v, 2));
1223 p2.y = Int_val (Field (rect_v, 3));
1225 if (0) {
1226 glEnable (GL_BLEND);
1227 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1228 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1229 glColor4f (0.0f, 0.0f, 0.0f, 0.2f);
1230 glRecti (p1.x, p1.y, p2.x, p2.y);
1231 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1232 glDisable (GL_BLEND);
1235 ctm = page->pagedim->ctm;
1236 if (!page->text) {
1237 fz_error error;
1238 fz_device *tdev;
1240 page->text = fz_newtextspan ();
1241 tdev = fz_newtextdevice (page->text);
1242 error = pdf_runcontentstream (tdev, page->pagedim->ctm, state.xref,
1243 page->drawpage->resources,
1244 page->drawpage->contents);
1245 if (error) die (error);
1246 fz_freedevice (tdev);
1249 printf ("\033c");
1251 printf ("BBox %f %f %f %f\n", p1.x, p1.y, p2.x, p2.y);
1252 p1.x += page->pixmap->x;
1253 p1.y += page->pixmap->y;
1254 p2.x += page->pixmap->x;
1255 p2.y += page->pixmap->y;
1256 x0 = p1.x;
1257 y0 = p1.y;
1258 x1 = p2.x;
1259 y1 = p2.y;
1260 printf ("BBox %d %d %d %d %d %d\n", x0, y0, x1, y1, oy, page->pageno);
1262 for (span = page->text; span; span = span->next) {
1263 int seen = 0;
1265 /* fz_debugtextspanxml (span); */
1266 for (i = 0; i < span->len; ++i) {
1267 long c;
1269 bx0 = span->text[i].bbox.x0;
1270 bx1 = span->text[i].bbox.x1;
1271 by0 = span->text[i].bbox.y0 + oy;
1272 by1 = span->text[i].bbox.y1 + oy;
1274 if ((bx1 >= x0 && bx0 <= x1 && by1 >= y0 && by0 <= y1)) {
1275 if (!seen) {
1276 rx0 = bx0 - page->pixmap->x;
1277 rx1 = bx1 - page->pixmap->x;
1278 ry0 = by0;
1279 ry1 = by1;
1282 seen = 1;
1283 c = span->text[i].c;
1284 if (c < 256) {
1285 if ((isprint (c) && !isspace (c))) {
1286 if (!rectsel) {
1287 bx0 -= page->pixmap->x;
1288 bx1 -= page->pixmap->x;
1289 glEnable (GL_BLEND);
1290 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1291 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1292 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1293 glRecti (bx0, by0, bx1, by1);
1294 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1295 glDisable (GL_BLEND);
1297 if (isprint (c) || c ==' ') {
1298 rx1 = bx1;
1299 ry1 = by1;
1302 putc (c, stdout);
1304 else {
1305 putc ('?', stdout);
1310 if (rectsel) {
1311 if (seen) {
1312 glEnable (GL_BLEND);
1313 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1314 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1315 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1316 glRecti (rx0, ry0, rx1, ry1);
1317 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1318 glDisable (GL_BLEND);
1322 if (seen && span->eol) {
1323 x0 = page->pixmap->x;
1324 putc ('\n', stdout);
1327 unlock ("ml_gettext");
1329 done:
1330 CAMLreturn (Val_unit);
1333 CAMLprim value ml_getpagewh (value pagedimno_v)
1335 CAMLparam1 (pagedimno_v);
1336 CAMLlocal1 (ret_v);
1337 int pagedimno = Int_val (pagedimno_v);
1339 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1340 Store_double_field (ret_v, 0, state.pagedims[pagedimno].box.x0);
1341 Store_double_field (ret_v, 1, state.pagedims[pagedimno].box.x1);
1342 Store_double_field (ret_v, 2, state.pagedims[pagedimno].box.y0);
1343 Store_double_field (ret_v, 3, state.pagedims[pagedimno].box.y1);
1344 CAMLreturn (ret_v);
1347 CAMLprim value ml_init (value sock_v)
1349 #ifndef _WIN32
1350 int ret;
1351 #endif
1352 CAMLparam1 (sock_v);
1354 state.texcount = 256;
1355 state.sliceheight = 24;
1356 state.texform = GL_RGBA;
1357 state.texty = GL_UNSIGNED_BYTE;
1359 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1360 if (!state.texids) {
1361 err (1, "calloc texids %zu", state.texcount * sizeof (*state.texids));
1364 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1365 if (!state.texowners) {
1366 err (1, "calloc texowners %zu",
1367 state.texcount * sizeof (*state.texowners));
1370 glGenTextures (state.texcount, state.texids);
1372 #ifdef _WIN32
1373 state.sock = Socket_val (sock_v);
1374 #else
1375 state.sock = Int_val (sock_v);
1376 #endif
1378 state.cache = fz_newglyphcache ();
1379 if (!state.cache) {
1380 errx (1, "fz_newglyphcache failed");
1383 #ifdef _WIN32
1384 InitializeCriticalSection (&critsec);
1385 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
1386 if (state.thread == INVALID_HANDLE_VALUE) {
1387 errx (1, "CreateThread failed: %lx", GetLastError ());
1389 #else
1390 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1391 if (ret) {
1392 errx (1, "pthread_create: %s", strerror (errno));
1394 #endif
1396 CAMLreturn (Val_unit);