Fix navigation corner case
[llpp.git] / link.c
bloba4208d11d8b61d637234cfb56012d6ab786d26c2
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 #include <limits.h>
66 #ifndef _WIN32
67 #include <pthread.h>
68 #include <sys/time.h>
69 #include <sys/types.h>
70 #include <sys/socket.h>
71 #include <sys/select.h>
72 #endif
74 #ifdef __APPLE__
75 #include <OpenGL/gl.h>
76 #else
77 #include <GL/gl.h>
78 #endif
80 #ifndef GL_TEXTURE_RECTANGLE_ARB
81 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
82 #endif
84 #include <caml/fail.h>
85 #include <caml/alloc.h>
86 #include <caml/memory.h>
87 #include <caml/unixsupport.h>
89 #include <fitz.h>
90 #include <mupdf.h>
92 #if 0
93 #define lprintf printf
94 #else
95 #define lprintf(...)
96 #endif
98 #define ARSERT(cond) for (;;) { \
99 if (!(cond)) { \
100 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
102 break; \
105 struct slice {
106 int texindex;
107 int w, h;
110 struct page {
111 int pageno;
112 int slicecount;
113 fz_textspan *text;
114 fz_pixmap *pixmap;
115 pdf_page *drawpage;
116 struct pagedim *pagedim;
117 struct page *prev;
118 struct slice slices[];
121 struct pagedim {
122 int pageno;
123 int rotate;
124 fz_rect box;
125 fz_bbox bbox;
126 fz_matrix ctm, ctm1;
129 struct {
130 int sock;
131 int sliceheight;
132 struct page *pages;
133 struct pagedim *pagedims;
134 int pagecount;
135 int pagedimcount;
136 pdf_xref *xref;
137 fz_glyphcache *cache;
138 int w, h;
140 int texindex;
141 int texcount;
142 GLuint *texids;
144 GLenum texform;
145 GLenum texty;
147 int lotsamemory;
149 int *pagetbl;
150 struct {
151 int w, h;
152 struct slice *slice;
153 } *texowners;
155 int rotate;
157 #ifdef _WIN32
158 HANDLE thread;
159 #else
160 pthread_t thread;
161 #endif
162 } state;
164 #ifdef _WIN32
165 static CRITICAL_SECTION critsec;
167 static void lock (void *unused)
169 (void) unused;
170 EnterCriticalSection (&critsec);
173 static void unlock (void *unused)
175 (void) unused;
176 LeaveCriticalSection (&critsec);
179 static int trylock (void *unused)
181 return TryEnterCriticalSection (&critsec) == 0;
183 #else
184 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
186 static void lock (const char *cap)
188 int ret = pthread_mutex_lock (&mutex);
189 if (ret) {
190 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
194 static void unlock (const char *cap)
196 int ret = pthread_mutex_unlock (&mutex);
197 if (ret) {
198 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
202 static int trylock (const char *cap)
204 int ret = pthread_mutex_trylock (&mutex);
206 if (ret && ret != EBUSY) {
207 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
209 return ret == EBUSY;
211 #endif
213 static void *parse_pointer (const char *cap, const char *s)
215 int ret;
216 void *ptr;
218 ret = sscanf (s, "%p", &ptr);
219 if (ret != 1) {
220 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
222 return ptr;
225 static int hasdata (int sock)
227 int n;
228 fd_set s;
229 struct timeval tv;
230 FD_ZERO (&s);
231 FD_SET (sock, &s);
232 tv.tv_sec = 0;
233 tv.tv_usec = 0;
234 n = select (sock + 1, &s, NULL, NULL, &tv);
235 if (n == 0) return 0;
236 if (n == 1) return 1;
237 sockerr (1, "hasdata: select error ret=%d", n);
240 static double now (void)
242 struct timeval tv;
244 if (gettimeofday (&tv, NULL)) {
245 err (1, "gettimeofday");
247 return tv.tv_sec + tv.tv_usec*1e-6;
250 static void readdata (int fd, char *p, int size)
252 ssize_t n;
254 n = recv (fd, p, size, 0);
255 if (n - size) {
256 if (!n) errx (1, "EOF while reading");
257 sockerr (1, "recv (req %d, ret %zd)", size, n);
261 static void writedata (int fd, char *p, int size)
263 char buf[4];
264 ssize_t n;
266 buf[0] = (size >> 24) & 0xff;
267 buf[1] = (size >> 16) & 0xff;
268 buf[2] = (size >> 8) & 0xff;
269 buf[3] = (size >> 0) & 0xff;
271 n = send (fd, buf, 4, 0);
272 if (n != 4) {
273 if (!n) errx (1, "EOF while writing length");
274 sockerr (1, "send %zd", n);
277 n = send (fd, p, size, 0);
278 if (n - size) {
279 if (!n) errx (1, "EOF while writing data");
280 sockerr (1, "send (req %d, ret %zd)", size, n);
284 static void
285 #ifdef __GNUC__
286 __attribute__ ((format (printf, 2, 3)))
287 #endif
288 printd (int fd, const char *fmt, ...)
290 int len;
291 va_list ap;
292 char buf[200];
294 va_start (ap, fmt);
295 len = vsnprintf (buf, sizeof (buf), fmt, ap);
296 va_end (ap);
297 writedata (fd, buf, len);
300 static void die (fz_error error)
302 fz_catch (error, "aborting");
303 if (state.xref)
304 pdf_closexref (state.xref);
305 exit (1);
308 static void openxref (char *filename)
310 int fd;
311 fz_error error;
312 fz_stream *file;
314 fd = open (filename, O_BINARY | O_RDONLY, 0666);
315 if (fd < 0)
316 die (fz_throw ("cannot open file '%s'", filename));
318 file = fz_openfile (fd);
319 state.xref = pdf_openxref (file);
320 if (!state.xref)
321 die (fz_throw ("cannot open PDF file '%s'", filename));
322 fz_dropstream (file);
324 if (pdf_needspassword (state.xref)) {
325 die (fz_throw ("password protected"));
328 error = pdf_loadpagetree (state.xref);
329 if (error) {
330 die (fz_throw ("cannot load page tree"));
333 state.pagecount = pdf_getpagecount (state.xref);
334 state.pagetbl = stat_alloc (state.pagecount * sizeof (*state.pagetbl));
337 static int readlen (int fd)
339 ssize_t n;
340 char p[4];
342 n = recv (fd, p, 4, 0);
343 if (n != 4) {
344 if (!n) errx (1, "EOF while reading length");
345 sockerr (1, "recv %zd", n);
348 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
351 static void freepage (struct page *page)
353 int i;
354 struct page *p;
356 fz_droppixmap (page->pixmap);
357 for (p = state.pages; p; p = p->prev) {
358 if (p->prev == page) {
359 p->prev = page->prev;
360 break;
363 for (i = 0; i < page->slicecount; ++i) {
364 struct slice *s = &page->slices[i];
365 if (s->texindex != -1) {
366 if (state.texowners[s->texindex].slice == s) {
367 state.texowners[s->texindex].slice = NULL;
368 ARSERT (state.texowners[s->texindex].w == s->w);
369 ARSERT (state.texowners[s->texindex].h >= s->h);
373 if (page->text) {
374 fz_freetextspan (page->text);
376 if (page->drawpage) {
377 pdf_freepage (page->drawpage);
380 free (page);
383 static void subdivide (struct page *p)
385 int i;
386 int h = p->pixmap->h;
387 int th = MIN (h, state.sliceheight);
389 for (i = 0; i < p->slicecount; ++i) {
390 struct slice *s = &p->slices[i];
391 s->texindex = -1;
392 s->h = MIN (th, h);
393 s->w = p->pixmap->w;
394 h -= th;
398 static void *render (int pageno, int pindex)
400 fz_error error;
401 int slicecount;
402 fz_obj *pageobj;
403 struct page *page;
404 double start, end;
405 pdf_page *drawpage;
406 fz_device *idev;
407 struct pagedim *pagedim;
409 start = now ();
410 printd (state.sock, "V rendering %d", pageno);
411 pdf_flushxref (state.xref, 0);
413 pagedim = &state.pagedims[pindex];
414 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
415 + state.sliceheight - 1) / state.sliceheight;
416 slicecount += slicecount == 0;
418 page = calloc (sizeof (*page)
419 + (slicecount * sizeof (struct slice)), 1);
420 if (!page) {
421 err (1, "calloc page %d\n", pageno);
423 page->slicecount = slicecount;
424 page->prev = state.pages;
425 state.pages = page;
427 pageobj = pdf_getpageobject (state.xref, pageno);
428 if (!pageobj)
429 die (fz_throw ("cannot retrieve info from page %d", pageno));
431 error = pdf_loadpage (&drawpage, state.xref, pageobj);
432 if (error)
433 die (error);
435 page->pixmap = fz_newpixmapwithrect (pdf_devicergb, pagedim->bbox);
436 fz_clearpixmap (page->pixmap, 0xFF);
438 idev = fz_newdrawdevice (state.cache, page->pixmap);
439 if (!idev)
440 die (fz_throw ("fz_newdrawdevice failed"));
441 error = pdf_runcontentstream (idev, pagedim->ctm, state.xref,
442 drawpage->resources,
443 drawpage->contents);
444 fz_freedevice (idev);
446 page->drawpage = drawpage;
447 page->pagedim = pagedim;
448 page->pageno = pageno;
449 subdivide (page);
450 end = now ();
452 if (!state.lotsamemory) {
453 pdf_agestoreditems (state.xref->store);
454 pdf_evictageditems (state.xref->store);
457 printd (state.sock, "V rendering %d took %f sec", pageno, end - start);
458 return page;
461 static void initpdims (void)
463 int pageno;
464 double start, end;
466 start = now ();
467 for (pageno = 0; pageno < state.pagecount; ++pageno) {
468 int rotate;
469 fz_rect box;
470 struct pagedim *p;
471 fz_obj *obj, *pageobj;
473 pageobj = pdf_getpageobject (state.xref, pageno + 1);
475 obj = fz_dictgets (pageobj, "CropBox");
476 if (!fz_isarray (obj)) {
477 obj = fz_dictgets (pageobj, "MediaBox");
478 if (!fz_isarray (obj)) {
479 die (fz_throw ("cannot find page bounds %d (%d Rd)",
480 fz_tonum (pageobj), fz_togen (pageobj)));
483 box = pdf_torect (obj);
485 obj = fz_dictgets (pageobj, "Rotate");
486 if (fz_isint (obj)) {
487 rotate = fz_toint (obj);
489 else {
490 rotate = 0;
492 rotate += state.rotate;
494 state.pagetbl[pageno] = fz_tonum (state.xref->pagerefs[pageno]);
495 p = &state.pagedims[state.pagedimcount - 1];
496 if ((state.pagedimcount == 0)
497 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
498 size_t size;
500 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
501 state.pagedims = realloc (state.pagedims, size);
502 if (!state.pagedims) {
503 err (1, "realloc pagedims to %zu (%d elems)",
504 size, state.pagedimcount + 1);
506 p = &state.pagedims[state.pagedimcount++];
507 p->rotate = rotate;
508 p->box = box;
509 p->pageno = pageno;
512 end = now ();
513 printd (state.sock, "T Processed %d pages in %f seconds",
514 state.pagecount, end - start);
517 static void layout (void)
519 int pindex;
520 fz_matrix ctm;
521 fz_rect box, box2;
522 double zoom, w;
523 struct pagedim *p = state.pagedims;
525 pindex = 0;
526 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
527 box.x0 = MIN (p->box.x0, p->box.x1);
528 box.y0 = MIN (p->box.y0, p->box.y1);
529 box.x1 = MAX (p->box.x0, p->box.x1);
530 box.y1 = MAX (p->box.y0, p->box.y1);
532 ctm = fz_identity;
533 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
534 ctm = fz_concat (ctm, fz_rotate (p->rotate));
535 box2 = fz_transformrect (ctm, box);
536 w = box2.x1 - box2.x0;
538 zoom = (state.w / w);
539 ctm = fz_identity;
540 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
541 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
542 memcpy (&p->ctm1, &ctm, sizeof (ctm));
543 ctm = fz_concat (ctm, fz_rotate (p->rotate));
544 p->bbox = fz_roundrect (fz_transformrect (ctm, box));
545 memcpy (&p->ctm, &ctm, sizeof (ctm));
548 while (p-- != state.pagedims) {
549 printd (state.sock, "l %d %d %d",
550 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0);
554 static void recurse_outline (pdf_outline *outline, int level)
556 while (outline) {
557 int i, num;
558 fz_obj *obj;
559 int top = 0;
560 int pageno = -1;
562 if (!outline->link) goto next;
564 obj = outline->link->dest;
565 if (fz_isindirect (obj)) {
566 num = fz_tonum (obj);
568 for (i = 0; i < state.pagecount; ++i) {
569 if (state.pagetbl[i] == num) {
570 pageno = i;
571 break;
575 else if (fz_isarray (obj)) {
576 fz_obj *obj2;
578 obj2 = fz_arrayget (obj, 0);
579 if (fz_isint (obj2)) {
580 pageno = fz_toint (obj2);
582 else {
583 num = fz_tonum (obj2);
584 for (i = 0; i < state.pagecount; ++i) {
585 if (state.pagetbl[i] == num) {
586 pageno = i;
587 break;
592 if (fz_arraylen (obj) > 3) {
593 fz_point p;
594 struct pagedim *pagedim = state.pagedims;
596 for (i = 0; i < state.pagedimcount; ++i) {
597 if (state.pagedims[i].pageno > pageno)
598 break;
599 pagedim = &state.pagedims[i];
602 p.x = fz_toint (fz_arrayget (obj, 2));
603 p.y = fz_toint (fz_arrayget (obj, 3));
604 p = fz_transformpoint (pagedim->ctm, p);
605 top = p.y;
609 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
610 printd (state.sock, "o %d %d %d %s",
611 level, pageno, top, outline->title);
612 next:
613 if (outline->child) {
614 recurse_outline (outline->child, level + 1);
616 outline = outline->next;
620 static void process_outline (void)
622 pdf_outline *outline;
624 outline = pdf_loadoutline (state.xref);
625 if (outline) {
626 recurse_outline (outline, 0);
627 pdf_freeoutline (outline);
631 static int comparespans (const void *l, const void *r)
633 fz_textspan const *const*ls = l;
634 fz_textspan const *const*rs = r;
635 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
638 /* wishful thinking function */
639 static void search (regex_t *re, int pageno, int y, int forward)
641 int i, j;
642 int ret;
643 char *p;
644 char buf[256];
645 fz_matrix ctm;
646 fz_error error;
647 fz_obj *pageobj;
648 fz_device *tdev;
649 pdf_page *drawpage;
650 fz_textspan *text, *span, **pspan;
651 struct pagedim *pdim, *pdimprev;
652 int stop = 0;
653 int niters = 0;
654 int nspans;
655 double start, end;
657 start = now ();
658 while (pageno >= 0 && pageno < state.pagecount && !stop) {
659 if (niters++ == 5) {
660 if (!state.lotsamemory) {
661 pdf_agestoreditems (state.xref->store);
662 pdf_evictageditems (state.xref->store);
664 niters = 0;
665 if (hasdata (state.sock)) {
666 printd (state.sock, "T attention requested aborting search at %d",
667 pageno);
668 stop = 1;
670 else {
671 printd (state.sock, "T searching in page %d", pageno);
674 pdimprev = NULL;
675 for (i = 0; i < state.pagedimcount; ++i) {
676 pdim = &state.pagedims[i];
677 if (pdim->pageno == pageno) {
678 goto found;
680 if (pdim->pageno > pageno) {
681 pdim = pdimprev;
682 goto found;
684 pdimprev = pdim;
686 pdim = pdimprev;
687 found:
689 pageobj = pdf_getpageobject (state.xref, pageno + 1);
690 if (!pageobj)
691 die (fz_throw ("cannot retrieve info from page %d", pageno));
693 error = pdf_loadpage (&drawpage, state.xref, pageobj);
694 if (error)
695 die (error);
697 ctm = fz_rotate (pdim->rotate);
699 text = fz_newtextspan ();
700 tdev = fz_newtextdevice (text);
701 error = pdf_runcontentstream (tdev, pdim->ctm1, state.xref,
702 drawpage->resources,
703 drawpage->contents);
704 if (error) die (error);
705 fz_freedevice (tdev);
707 nspans = 0;
708 for (span = text; span; span = span->next) {
709 nspans++;
711 pspan = malloc (sizeof (void *) * nspans);
712 if (!pspan) {
713 err (1, "malloc span pointers %zu", sizeof (void *) * nspans);
715 for (i = 0, span = text; span; span = span->next, ++i) {
716 pspan[i] = span;
718 qsort (pspan, nspans, sizeof (fz_textspan *), comparespans);
720 j = forward ? 0 : nspans - 1;
721 while (nspans--) {
722 regmatch_t rm;
724 span = pspan[j];
725 j += forward ? 1 : -1;
726 p = buf;
727 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
728 if (forward) {
729 if (span->text[i].bbox.y0 < y + 1) {
730 continue;
733 else {
734 if (span->text[i].bbox.y0 > y - 1) {
735 continue;
738 if (span->text[i].c < 256) {
739 *p++ = span->text[i].c;
741 else {
742 *p++ = '?';
745 if (p == buf) {
746 continue;
748 *p++ = 0;
750 ret = regexec (re, buf, 1, &rm, 0);
751 if (ret) {
752 if (ret != REG_NOMATCH) {
753 size_t size;
754 char errbuf[80];
755 size = regerror (ret, re, errbuf, sizeof (errbuf));
756 printd (state.sock,
757 "T regexec error `%.*s'",
758 (int) size, errbuf);
759 fz_freetextspan (text);
760 pdf_freepage (drawpage);
761 free (pspan);
762 return;
765 else {
766 int xoff, yoff;
767 fz_bbox *sb, *eb;
768 fz_point p1, p2, p3, p4;
770 xoff = -pdim->bbox.x0;
771 yoff = -pdim->bbox.y0;
773 sb = &span->text[rm.rm_so].bbox;
774 eb = &span->text[rm.rm_eo - 1].bbox;
776 p1.x = sb->x0;
777 p1.y = sb->y0;
778 p2.x = eb->x1;
779 p2.y = sb->y0;
780 p3.x = eb->x1;
781 p3.y = eb->y1;
782 p4.x = sb->x0;
783 p4.y = eb->y1;
785 p1 = fz_transformpoint (ctm, p1);
786 p2 = fz_transformpoint (ctm, p2);
787 p3 = fz_transformpoint (ctm, p3);
788 p4 = fz_transformpoint (ctm, p4);
790 if (!stop) {
791 printd (state.sock, "F %d %d %f %f %f %f %f %f %f %f",
792 pageno, 1,
793 p1.x + xoff, p1.y + yoff,
794 p2.x + xoff, p2.y + yoff,
795 p3.x + xoff, p3.y + yoff,
796 p4.x + xoff, p4.y + yoff);
798 printd (state.sock, "T found at %d `%.*s' in %f sec",
799 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
800 now () - start);
802 else {
803 printd (state.sock, "R %d %d %f %f %f %f %f %f %f %f",
804 pageno, 2,
805 p1.x + xoff, p1.y + yoff,
806 p2.x + xoff, p2.y + yoff,
807 p3.x + xoff, p3.y + yoff,
808 p4.x + xoff, p4.y + yoff);
810 stop = 1;
813 if (forward) {
814 pageno += 1;
815 y = 0;
817 else {
818 pageno -= 1;
819 y = INT_MAX;
821 fz_freetextspan (text);
822 pdf_freepage (drawpage);
823 free (pspan);
825 end = now ();
826 if (!stop) {
827 printd (state.sock, "T no matches %f sec", end - start);
829 printd (state.sock, "D");
832 static
833 #ifdef _WIN32
834 DWORD _stdcall
835 #else
836 void *
837 #endif
838 mainloop (void *unused)
840 char *p = NULL;
841 int len, ret, oldlen = 0;
843 for (;;) {
844 len = readlen (state.sock);
845 if (len == 0) {
846 errx (1, "readlen returned 0");
849 if (oldlen < len + 1) {
850 p = realloc (p, len + 1);
851 if (!p) {
852 err (1, "realloc %d failed", len + 1);
854 oldlen = len + 1;
856 readdata (state.sock, p, len);
857 p[len] = 0;
859 if (!strncmp ("open", p, 4)) {
860 fz_obj *obj;
861 char *filename = p + 5;
863 openxref (filename);
864 initpdims ();
866 obj = fz_dictgets (state.xref->trailer, "Info");
867 if (obj) {
868 obj = fz_dictgets (obj, "Title");
869 if (obj) {
870 printd (state.sock, "t %s", pdf_toutf8 (obj));
874 else if (!strncmp ("free", p, 4)) {
875 void *ptr;
877 ret = sscanf (p + 4, " %p", &ptr);
878 if (ret != 1) {
879 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
881 lock ("free");
882 freepage (ptr);
883 unlock ("free");
884 printd (state.sock, "d");
886 else if (!strncmp ("search", p, 6)) {
887 int icase, pageno, y, ret, len2, forward;
888 char *pattern;
889 regex_t re;
891 ret = sscanf (p + 6, " %d %d %d %d,%n",
892 &icase, &pageno, &y, &forward, &len2);
893 if (ret != 4) {
894 errx (1, "malformed search `%s' ret=%d", p, ret);
897 pattern = p + 6 + len2;
898 ret = regcomp (&re, pattern,
899 REG_EXTENDED | (icase ? REG_ICASE : 0));
900 if (ret) {
901 char errbuf[80];
902 size_t size;
904 size = regerror (ret, &re, errbuf, sizeof (errbuf));
905 printd (state.sock, "T regcomp failed `%.*s'",
906 (int) size, errbuf);
908 else {
909 search (&re, pageno, y, forward);
910 regfree (&re);
913 else if (!strncmp ("geometry", p, 8)) {
914 int w, h;
916 printd (state.sock, "c");
917 ret = sscanf (p + 8, " %d %d", &w, &h);
918 if (ret != 2) {
919 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
921 state.h = h;
922 if (w != state.w) {
923 int i;
924 state.w = w;
925 for (i = 0; i < state.texcount; ++i) {
926 state.texowners[i].slice = NULL;
929 lock ("geometry");
930 layout ();
931 process_outline ();
932 unlock ("geometry");
933 printd (state.sock, "C %d", state.pagecount);
935 else if (!strncmp ("rotate", p, 6)) {
936 float rotate;
938 printd (state.sock, "c");
939 ret = sscanf (p + 6, " %f", &rotate);
940 if (ret != 1) {
941 errx (1, "bad rotate line `%.*s' ret=%d", len, p, ret);
943 state.rotate = rotate;
944 lock ("rotate");
945 state.pagedimcount = 0;
946 free (state.pagedims);
947 state.pagedims = NULL;
948 initpdims ();
949 layout ();
950 process_outline ();
951 unlock ("rotate");
952 printd (state.sock, "C %d", state.pagecount);
954 else if (!strncmp ("render", p, 6)) {
955 int pageno, pindex, w, h, ret;
956 struct page *page;
958 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
959 if (ret != 4) {
960 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
963 page = render (pageno, pindex);
964 printd (state.sock, "r %d %d %d %d %p\n",
965 pageno,
966 state.w,
967 state.h,
968 state.rotate,
969 page);
971 else if (!strncmp ("interrupt", p, 8)) {
972 printd (state.sock, "V interrupted");
974 else {
975 errx (1, "unknown command %.*s", len, p);
978 return 0;
981 static void upload2 (struct page *page, int slicenum, const char *cap)
983 int i;
984 int w, h;
985 double start, end;
986 struct slice *slice = &page->slices[slicenum];
988 w = page->pixmap->w;
989 h = page->pixmap->h;
991 ARSERT (w == slice->w);
992 if (slice->texindex != -1
993 && state.texowners[slice->texindex].slice == slice) {
994 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
996 else {
997 int subimage = 0;
998 int index = (state.texindex++ % state.texcount);
999 size_t offset = 0;
1001 for (i = 0; i < slicenum; ++i) {
1002 offset += w * page->slices[i].h * 4;
1005 if (state.texowners[index].w == slice->w) {
1006 if (state.texowners[index].h >= slice->h ) {
1007 subimage = 1;
1009 else {
1010 state.texowners[index].h = slice->h;
1013 else {
1014 state.texowners[index].h = slice->h;
1017 state.texowners[index].slice = slice;
1018 state.texowners[index].w = slice->w;
1019 slice->texindex = index;
1021 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1022 start = now ();
1023 if (subimage) {
1025 GLenum err = glGetError ();
1026 if (err != GL_NO_ERROR) {
1027 printf ("\033[0;31mERROR1 %d %d %#x\033[0m\n", w, slice->h, err);
1028 abort ();
1031 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1036 slice->h,
1037 state.texform,
1038 state.texty,
1039 page->pixmap->samples + offset
1042 GLenum err = glGetError ();
1043 if (err != GL_NO_ERROR) {
1044 printf ("\033[0;31mERROR %d %d %#x\033[0m\n", w, slice->h, err);
1045 abort ();
1049 else {
1050 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1052 GL_RGBA8,
1054 slice->h,
1056 state.texform,
1057 state.texty,
1058 page->pixmap->samples + offset
1062 end = now ();
1063 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
1064 subimage ? "sub" : "img",
1065 page->pageno, slicenum,
1066 slice->w, slice->h,
1067 state.texids[slice->texindex],
1068 end - start);
1072 CAMLprim value ml_draw (value dispy_v, value w_v, value h_v,
1073 value py_v, value ptr_v)
1075 CAMLparam5 (dispy_v, w_v, h_v, py_v, ptr_v);
1076 int dispy = Int_val (dispy_v);
1077 int w = Int_val (w_v);
1078 int h = Int_val (h_v);
1079 int py = Int_val (py_v);
1080 char *s = String_val (ptr_v);
1081 int ret;
1082 void *ptr;
1083 struct page *page;
1084 int slicenum = 0;
1086 if (trylock ("ml_draw")) {
1087 goto done;
1090 ret = sscanf (s, "%p", &ptr);
1091 if (ret != 1) {
1092 errx (1, "cannot parse pointer `%s'", s);
1094 page = ptr;
1096 w = page->pixmap->w;
1098 ARSERT (h >= 0 && "ml_draw wrong h");
1100 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1102 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
1103 struct slice *slice = &page->slices[slicenum];
1104 if (slice->h > py) {
1105 break;
1107 py -= slice->h;
1110 h = MIN (state.h, h);
1111 while (h) {
1112 int th;
1113 struct slice *slice = &page->slices[slicenum];
1115 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
1117 th = MIN (h, slice->h - py);
1118 upload2 (page, slicenum, "upload");
1120 glBegin (GL_QUADS);
1122 glTexCoord2i (0, py);
1123 glVertex2i (0, dispy);
1125 glTexCoord2i (w, py);
1126 glVertex2i (w, dispy);
1128 glTexCoord2i (w, py+th);
1129 glVertex2i (w, dispy + th);
1131 glTexCoord2i (0, py+th);
1132 glVertex2i (0, dispy + th);
1134 glEnd ();
1136 h -= th;
1137 py = 0;
1138 dispy += th;
1139 slicenum += 1;
1142 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1144 unlock ("ml_draw");
1145 done:
1146 CAMLreturn (Val_unit);
1149 static pdf_link *getlink (struct page *page, int x, int y)
1151 fz_point p;
1152 fz_matrix ctm;
1153 pdf_link *link;
1155 p.x = x + page->pixmap->x;
1156 p.y = y + page->pixmap->y;
1158 ctm = fz_invertmatrix (page->pagedim->ctm);
1159 p = fz_transformpoint (ctm, p);
1161 for (link = page->drawpage->links; link; link = link->next) {
1162 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1163 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1164 return link;
1168 return NULL;
1171 CAMLprim value ml_highlightlinks (value ptr_v, value yoff_v)
1173 CAMLparam2 (ptr_v, yoff_v);
1174 pdf_link *link;
1175 struct page *page;
1176 int xoff, yoff = Int_val (yoff_v);
1177 const char *s = String_val (ptr_v);
1179 if (trylock ("ml_highlightlinks")) {
1180 goto done;
1183 page = parse_pointer ("ml_highlightlinks", s);
1185 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1186 glEnable (GL_LINE_STIPPLE);
1187 glLineStipple (0.5, 0xcccc);
1189 xoff = -page->pixmap->x;
1190 yoff -= page->pixmap->y;
1192 glBegin (GL_QUADS);
1193 for (link = page->drawpage->links; link; link = link->next) {
1194 fz_point p1, p2, p3, p4;
1195 fz_matrix ctm = page->pagedim->ctm;
1197 p1.x = link->rect.x0;
1198 p1.y = link->rect.y0;
1200 p2.x = link->rect.x1;
1201 p2.y = link->rect.y0;
1203 p3.x = link->rect.x1;
1204 p3.y = link->rect.y1;
1206 p4.x = link->rect.x0;
1207 p4.y = link->rect.y1;
1209 p1 = fz_transformpoint (ctm, p1);
1210 p2 = fz_transformpoint (ctm, p2);
1211 p3 = fz_transformpoint (ctm, p3);
1212 p4 = fz_transformpoint (ctm, p4);
1214 switch (link->kind) {
1215 case PDF_LGOTO: glColor3ub (255, 0, 0); break;
1216 case PDF_LURI: glColor3ub (0, 0, 255); break;
1217 default: glColor3ub (0, 0, 0); break;
1220 glVertex2f (p1.x + xoff, p1.y + yoff);
1221 glVertex2f (p2.x + xoff, p2.y + yoff);
1222 glVertex2f (p3.x + xoff, p3.y + yoff);
1223 glVertex2f (p4.x + xoff, p4.y + yoff);
1225 glEnd ();
1227 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1228 glDisable (GL_LINE_STIPPLE);
1229 unlock ("ml_highlightlinks");
1231 done:
1232 CAMLreturn (Val_unit);
1235 CAMLprim value ml_checklink (value ptr_v, value x_v, value y_v)
1237 CAMLparam3 (ptr_v, x_v, y_v);
1238 char *s = String_val (ptr_v);
1239 int ret;
1241 if (trylock ("ml_checklink")) {
1242 ret = 0;
1244 else {
1245 pdf_link *link;
1247 link = getlink (parse_pointer ("ml_checklink", s),
1248 Int_val (x_v), Int_val (y_v));
1250 if (link == NULL) {
1251 ret = 0;
1253 else {
1254 if (link->kind == PDF_LURI) {
1255 printd (state.sock, "T %s", fz_tostrbuf (link->dest));
1258 unlock ("ml_checklink");
1260 CAMLreturn (Val_bool (ret));
1263 CAMLprim value ml_getlink (value ptr_v, value x_v, value y_v)
1265 CAMLparam3 (ptr_v, x_v, y_v);
1266 CAMLlocal2 (ret_v, tup_v);
1267 pdf_link *link;
1268 struct page *page;
1269 char *s = String_val (ptr_v);
1271 ret_v = Val_int (0);
1272 if (trylock ("ml_getlink")) {
1273 ret_v = Val_int (0);
1274 goto done;
1277 page = parse_pointer ("ml_getlink", s);
1279 link = getlink (page, Int_val (x_v), Int_val (y_v));
1280 if (link) {
1281 switch (link->kind) {
1282 case PDF_LGOTO:
1284 int pageno;
1285 fz_point p;
1286 fz_obj *obj;
1288 pageno = -1;
1289 obj = fz_arrayget (link->dest, 0);
1290 if (fz_isindirect (obj)) {
1291 pageno = pdf_findpageobject (state.xref, obj) - 1;
1293 else if (fz_isint (obj)) {
1294 pageno = fz_toint (obj);
1297 if (fz_arraylen (link->dest) > 3) {
1298 p.x = fz_toint (fz_arrayget (link->dest, 2));
1299 p.y = fz_toint (fz_arrayget (link->dest, 3));
1300 p = fz_transformpoint (page->pagedim->ctm, p);
1302 else {
1303 p.x = 0.0;
1304 p.y = 0.0;
1307 tup_v = caml_alloc_tuple (2);
1308 ret_v = caml_alloc_small (1, 1);
1309 Field (tup_v, 0) = Val_int (pageno);
1310 Field (tup_v, 1) = Val_int (p.y);
1311 Field (ret_v, 0) = tup_v;
1313 break;
1315 case PDF_LURI:
1316 printf ("%s\n", fz_tostrbuf (link->dest));
1317 tup_v = caml_alloc_tuple (2);
1318 ret_v = caml_alloc_small (1, 1);
1319 Field (tup_v, 0) = Val_int (-1);
1320 Field (tup_v, 1) = Val_int (0);
1321 Field (ret_v, 0) = tup_v;
1322 break;
1324 default:
1325 printd (state.sock, "T unhandled link kind %d", link->kind);
1326 break;
1329 unlock ("ml_getlink");
1331 done:
1332 CAMLreturn (ret_v);
1335 CAMLprim value ml_gettext (value ptr_v, value rect_v, value oy_v, value rectsel_v)
1337 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1338 fz_matrix ctm;
1339 fz_point p1, p2;
1340 struct page *page;
1341 fz_textspan *span;
1342 char *s = String_val (ptr_v);
1343 int rectsel = Bool_val (rectsel_v);
1344 int i, bx0, bx1, by0, by1, x0, x1, y0, y1, oy;
1346 /* stop GCC from complaining about uninitialized variables */
1347 #ifdef __GNUC__
1348 int rx0 = rx0, rx1 = rx1, ry0 = ry0, ry1 = ry1;
1349 #else
1350 int rx0, rx1, ry0, ry1;
1351 #endif
1353 if (trylock ("ml_gettext")) {
1354 goto done;
1357 page = parse_pointer ("ml_gettext", s);
1359 oy = Int_val (oy_v);
1360 p1.x = Int_val (Field (rect_v, 0));
1361 p1.y = Int_val (Field (rect_v, 1));
1362 p2.x = Int_val (Field (rect_v, 2));
1363 p2.y = Int_val (Field (rect_v, 3));
1365 if (0) {
1366 glEnable (GL_BLEND);
1367 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1368 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1369 glColor4f (0.0f, 0.0f, 0.0f, 0.2f);
1370 glRecti (p1.x, p1.y, p2.x, p2.y);
1371 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1372 glDisable (GL_BLEND);
1375 ctm = page->pagedim->ctm;
1376 if (!page->text) {
1377 fz_error error;
1378 fz_device *tdev;
1380 page->text = fz_newtextspan ();
1381 tdev = fz_newtextdevice (page->text);
1382 error = pdf_runcontentstream (tdev, page->pagedim->ctm, state.xref,
1383 page->drawpage->resources,
1384 page->drawpage->contents);
1385 if (error) die (error);
1386 fz_freedevice (tdev);
1389 printf ("\033c");
1391 printf ("BBox %f %f %f %f\n", p1.x, p1.y, p2.x, p2.y);
1392 p1.x += page->pixmap->x;
1393 p1.y += page->pixmap->y;
1394 p2.x += page->pixmap->x;
1395 p2.y += page->pixmap->y;
1396 x0 = p1.x;
1397 y0 = p1.y;
1398 x1 = p2.x;
1399 y1 = p2.y;
1400 printf ("BBox %d %d %d %d %d %d\n", x0, y0, x1, y1, oy, page->pageno);
1402 for (span = page->text; span; span = span->next) {
1403 int seen = 0;
1405 /* fz_debugtextspanxml (span); */
1406 for (i = 0; i < span->len; ++i) {
1407 long c;
1409 bx0 = span->text[i].bbox.x0;
1410 bx1 = span->text[i].bbox.x1;
1411 by0 = span->text[i].bbox.y0 + oy;
1412 by1 = span->text[i].bbox.y1 + oy;
1414 if ((bx1 >= x0 && bx0 <= x1 && by1 >= y0 && by0 <= y1)) {
1415 if (!seen) {
1416 rx0 = bx0 - page->pixmap->x;
1417 rx1 = bx1 - page->pixmap->x;
1418 ry0 = by0;
1419 ry1 = by1;
1422 seen = 1;
1423 c = span->text[i].c;
1424 if (c < 256) {
1425 if ((isprint (c) && !isspace (c))) {
1426 if (!rectsel) {
1427 bx0 -= page->pixmap->x;
1428 bx1 -= page->pixmap->x;
1429 glEnable (GL_BLEND);
1430 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1431 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1432 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1433 glRecti (bx0, by0, bx1, by1);
1434 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1435 glDisable (GL_BLEND);
1437 if (isprint (c) || c ==' ') {
1438 rx1 = bx1;
1439 ry1 = by1;
1442 putc (c, stdout);
1444 else {
1445 putc ('?', stdout);
1450 if (rectsel) {
1451 if (seen) {
1452 glEnable (GL_BLEND);
1453 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1454 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1455 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1456 glRecti (rx0, ry0, rx1, ry1);
1457 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1458 glDisable (GL_BLEND);
1462 if (seen && span->eol) {
1463 x0 = page->pixmap->x;
1464 putc ('\n', stdout);
1467 unlock ("ml_gettext");
1469 done:
1470 CAMLreturn (Val_unit);
1473 CAMLprim value ml_getpagewh (value pagedimno_v)
1475 CAMLparam1 (pagedimno_v);
1476 CAMLlocal1 (ret_v);
1477 int pagedimno = Int_val (pagedimno_v);
1479 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1480 Store_double_field (ret_v, 0, state.pagedims[pagedimno].box.x0);
1481 Store_double_field (ret_v, 1, state.pagedims[pagedimno].box.x1);
1482 Store_double_field (ret_v, 2, state.pagedims[pagedimno].box.y0);
1483 Store_double_field (ret_v, 3, state.pagedims[pagedimno].box.y1);
1484 CAMLreturn (ret_v);
1487 CAMLprim value ml_init (value sock_v)
1489 #ifndef _WIN32
1490 int ret;
1491 #endif
1492 CAMLparam1 (sock_v);
1494 state.texcount = 256;
1495 state.sliceheight = 24;
1496 state.texform = GL_RGBA;
1497 state.texty = GL_UNSIGNED_BYTE;
1499 fz_accelerate ();
1500 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1501 if (!state.texids) {
1502 err (1, "calloc texids %zu", state.texcount * sizeof (*state.texids));
1505 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1506 if (!state.texowners) {
1507 err (1, "calloc texowners %zu",
1508 state.texcount * sizeof (*state.texowners));
1511 glGenTextures (state.texcount, state.texids);
1513 #ifdef _WIN32
1514 state.sock = Socket_val (sock_v);
1515 #else
1516 state.sock = Int_val (sock_v);
1517 #endif
1519 state.cache = fz_newglyphcache ();
1520 if (!state.cache) {
1521 errx (1, "fz_newglyphcache failed");
1524 #ifdef _WIN32
1525 InitializeCriticalSection (&critsec);
1526 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
1527 if (state.thread == INVALID_HANDLE_VALUE) {
1528 errx (1, "CreateThread failed: %lx", GetLastError ());
1530 #else
1531 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1532 if (ret) {
1533 errx (1, "pthread_create: %s", strerror (errno));
1535 #endif
1537 CAMLreturn (Val_unit);