Remove pagetbl and use pdf_findpageobject instead
[llpp.git] / link.c
blobcd4cb2743b4e88a2a673560fe9cda0c3edea898e
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 #define FMT_ss "%d"
8 #ifdef _WIN64
9 #define FMT_s "%i64u"
10 #else
11 #define FMT_s "%u"
12 #endif
13 #pragma warning (disable:4244)
14 #pragma warning (disable:4996)
15 #pragma warning (disable:4995)
16 #endif
18 #ifdef _MSC_VER
19 #include <errno.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdlib.h>
23 static void __declspec (noreturn) err (int exitcode, const char *fmt, ...)
25 va_list ap;
26 int errcode;
28 errcode = errno;
29 va_start (ap, fmt);
30 vfprintf (stderr, fmt, ap);
31 va_end (ap);
32 fprintf (stderr, ": %s\n", strerror (errno));
33 exit (exitcode);
35 static void __declspec (noreturn) errx (int exitcode, const char *fmt, ...)
37 va_list ap;
39 va_start (ap, fmt);
40 vfprintf (stderr, fmt, ap);
41 va_end (ap);
42 fputc ('\n', stderr);
43 exit (exitcode);
45 static void __declspec (noreturn) sockerr (int exitcode, const char *fmt, ...)
47 va_list ap;
49 va_start (ap, fmt);
50 vfprintf (stderr, fmt, ap);
51 va_end (ap);
52 fprintf (stderr, ": wsaerror 0x%x\n", WSAGetLastError ());
53 exit (exitcode);
55 #else
56 #define FMT_ss "%zd"
57 #define FMT_s "%zu"
58 #define _GNU_SOURCE
59 #include <err.h>
60 #define sockerr err
61 #endif
62 #include <regex.h>
63 #include <errno.h>
64 #include <ctype.h>
65 #include <stdio.h>
66 #include <stdarg.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <limits.h>
70 #ifndef _WIN32
71 #include <pthread.h>
72 #include <sys/time.h>
73 #include <sys/types.h>
74 #include <sys/socket.h>
75 #include <sys/select.h>
76 #endif
78 #ifdef __APPLE__
79 #include <OpenGL/gl.h>
80 #else
81 #include <GL/gl.h>
82 #endif
84 #ifndef GL_TEXTURE_RECTANGLE_ARB
85 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
86 #endif
88 #include <caml/fail.h>
89 #include <caml/alloc.h>
90 #include <caml/memory.h>
91 #include <caml/unixsupport.h>
93 #include <fitz.h>
94 #include <mupdf.h>
96 #if 0
97 #define lprintf printf
98 #else
99 #define lprintf(...)
100 #endif
102 #define ARSERT(cond) for (;;) { \
103 if (!(cond)) { \
104 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
106 break; \
109 struct slice {
110 int texindex;
111 int w, h;
114 struct page {
115 int pageno;
116 int slicecount;
117 fz_textspan *text;
118 fz_pixmap *pixmap;
119 pdf_page *drawpage;
120 struct pagedim *pagedim;
121 struct page *prev;
122 struct slice slices[];
125 struct pagedim {
126 int pageno;
127 int rotate;
128 fz_rect box;
129 fz_bbox bbox;
130 fz_matrix ctm, ctm1;
133 struct {
134 int sock;
135 int sliceheight;
136 struct page *pages;
137 struct pagedim *pagedims;
138 int pagecount;
139 int pagedimcount;
140 pdf_xref *xref;
141 fz_glyphcache *cache;
142 int w, h;
144 int texindex;
145 int texcount;
146 GLuint *texids;
148 GLenum texform;
149 GLenum texty;
151 int lotsamemory;
153 struct {
154 int w, h;
155 struct slice *slice;
156 } *texowners;
158 int rotate;
160 #ifdef _WIN32
161 HANDLE thread;
162 #else
163 pthread_t thread;
164 #endif
165 } state;
167 #ifdef _WIN32
168 static CRITICAL_SECTION critsec;
170 static void lock (void *unused)
172 (void) unused;
173 EnterCriticalSection (&critsec);
176 static void unlock (void *unused)
178 (void) unused;
179 LeaveCriticalSection (&critsec);
182 static int trylock (void *unused)
184 return TryEnterCriticalSection (&critsec) == 0;
186 #else
187 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
189 static void lock (const char *cap)
191 int ret = pthread_mutex_lock (&mutex);
192 if (ret) {
193 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
197 static void unlock (const char *cap)
199 int ret = pthread_mutex_unlock (&mutex);
200 if (ret) {
201 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
205 static int trylock (const char *cap)
207 int ret = pthread_mutex_trylock (&mutex);
209 if (ret && ret != EBUSY) {
210 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
212 return ret == EBUSY;
214 #endif
216 static void *parse_pointer (const char *cap, const char *s)
218 int ret;
219 void *ptr;
221 ret = sscanf (s, "%p", &ptr);
222 if (ret != 1) {
223 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
225 return ptr;
228 static int hasdata (int sock)
230 int n;
231 fd_set s;
232 struct timeval tv;
233 FD_ZERO (&s);
234 FD_SET (sock, &s);
235 tv.tv_sec = 0;
236 tv.tv_usec = 0;
237 n = select (sock + 1, &s, NULL, NULL, &tv);
238 if (n == 0) return 0;
239 if (n == 1) return 1;
240 sockerr (1, "hasdata: select error ret=%d", n);
243 static double now (void)
245 struct timeval tv;
247 if (gettimeofday (&tv, NULL)) {
248 err (1, "gettimeofday");
250 return tv.tv_sec + tv.tv_usec*1e-6;
253 static void readdata (int fd, char *p, int size)
255 ssize_t n;
257 n = recv (fd, p, size, 0);
258 if (n - size) {
259 if (!n) errx (1, "EOF while reading");
260 sockerr (1, "recv (req %d, ret " FMT_ss ")", size, n);
264 static void writedata (int fd, char *p, int size)
266 char buf[4];
267 ssize_t n;
269 buf[0] = (size >> 24) & 0xff;
270 buf[1] = (size >> 16) & 0xff;
271 buf[2] = (size >> 8) & 0xff;
272 buf[3] = (size >> 0) & 0xff;
274 n = send (fd, buf, 4, 0);
275 if (n != 4) {
276 if (!n) errx (1, "EOF while writing length");
277 sockerr (1, "send " FMT_ss, n);
280 n = send (fd, p, size, 0);
281 if (n - size) {
282 if (!n) errx (1, "EOF while writing data");
283 sockerr (1, "send (req %d, ret " FMT_ss ")", size, n);
287 static void
288 #ifdef __GNUC__
289 __attribute__ ((format (printf, 2, 3)))
290 #endif
291 printd (int fd, const char *fmt, ...)
293 int len;
294 va_list ap;
295 char buf[200];
297 va_start (ap, fmt);
298 len = vsnprintf (buf, sizeof (buf), fmt, ap);
299 va_end (ap);
300 writedata (fd, buf, len);
303 static void die (fz_error error)
305 fz_catch (error, "aborting");
306 if (state.xref)
307 pdf_freexref (state.xref);
308 exit (1);
311 static void openxref (char *filename)
313 int fd;
314 fz_error error;
315 fz_stream *file;
317 fd = open (filename, O_BINARY | O_RDONLY, 0666);
318 if (fd < 0)
319 die (fz_throw ("cannot open file '%s'", filename));
321 file = fz_openfile (fd);
322 error = pdf_openxrefwithstream (&state.xref, file, NULL);
323 if (error)
324 die (fz_rethrow(error, "cannot open document '%s'", filename));
325 fz_close (file);
327 if (pdf_needspassword (state.xref)) {
328 die (fz_throw ("password protected"));
331 error = pdf_loadpagetree (state.xref);
332 if (error) {
333 die (fz_throw ("cannot load page tree"));
336 state.pagecount = pdf_getpagecount (state.xref);
339 static int readlen (int fd)
341 ssize_t n;
342 char p[4];
344 n = recv (fd, p, 4, 0);
345 if (n != 4) {
346 if (!n) errx (1, "EOF while reading length");
347 sockerr (1, "recv " FMT_ss, n);
350 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
353 static void freepage (struct page *page)
355 int i;
356 struct page *p;
358 fz_droppixmap (page->pixmap);
359 for (p = state.pages; p; p = p->prev) {
360 if (p->prev == page) {
361 p->prev = page->prev;
362 break;
365 for (i = 0; i < page->slicecount; ++i) {
366 struct slice *s = &page->slices[i];
367 if (s->texindex != -1) {
368 if (state.texowners[s->texindex].slice == s) {
369 state.texowners[s->texindex].slice = NULL;
370 ARSERT (state.texowners[s->texindex].w == s->w);
371 ARSERT (state.texowners[s->texindex].h >= s->h);
375 if (page->text) {
376 fz_freetextspan (page->text);
378 if (page->drawpage) {
379 pdf_freepage (page->drawpage);
382 free (page);
385 static void subdivide (struct page *p)
387 int i;
388 int h = p->pixmap->h;
389 int th = MIN (h, state.sliceheight);
391 for (i = 0; i < p->slicecount; ++i) {
392 struct slice *s = &p->slices[i];
393 s->texindex = -1;
394 s->h = MIN (th, h);
395 s->w = p->pixmap->w;
396 h -= th;
400 static void *render (int pageno, int pindex)
402 fz_error error;
403 int slicecount;
404 fz_obj *pageobj;
405 struct page *page;
406 double start, end;
407 pdf_page *drawpage;
408 fz_device *idev;
409 struct pagedim *pagedim;
411 start = now ();
412 printd (state.sock, "V rendering %d", pageno);
414 pagedim = &state.pagedims[pindex];
415 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
416 + state.sliceheight - 1) / state.sliceheight;
417 slicecount += slicecount == 0;
419 page = calloc (sizeof (*page)
420 + (slicecount * sizeof (struct slice)), 1);
421 if (!page) {
422 err (1, "calloc page %d\n", pageno);
424 page->slicecount = slicecount;
425 page->prev = state.pages;
426 state.pages = page;
428 pageobj = pdf_getpageobject (state.xref, pageno);
429 if (!pageobj)
430 die (fz_throw ("cannot retrieve info from page %d", pageno));
432 error = pdf_loadpage (&drawpage, state.xref, pageobj);
433 if (error)
434 die (error);
436 page->pixmap = fz_newpixmapwithrect (fz_devicergb, pagedim->bbox);
437 fz_clearpixmap (page->pixmap, 0xFF);
439 idev = fz_newdrawdevice (state.cache, page->pixmap);
440 if (!idev)
441 die (fz_throw ("fz_newdrawdevice failed"));
442 error = pdf_runpage (state.xref, drawpage, idev, pagedim->ctm);
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_agestore(state.xref->store, 3);
455 printd (state.sock, "V rendering %d took %f sec", pageno, end - start);
456 return page;
459 static void initpdims (void)
461 int pageno;
462 double start, end;
464 start = now ();
465 for (pageno = 0; pageno < state.pagecount; ++pageno) {
466 int rotate;
467 fz_rect box;
468 struct pagedim *p;
469 fz_obj *obj, *pageobj;
471 pageobj = pdf_getpageobject (state.xref, pageno + 1);
473 obj = fz_dictgets (pageobj, "CropBox");
474 if (!fz_isarray (obj)) {
475 obj = fz_dictgets (pageobj, "MediaBox");
476 if (!fz_isarray (obj)) {
477 die (fz_throw ("cannot find page bounds %d (%d Rd)",
478 fz_tonum (pageobj), fz_togen (pageobj)));
481 box = pdf_torect (obj);
483 obj = fz_dictgets (pageobj, "Rotate");
484 if (fz_isint (obj)) {
485 rotate = fz_toint (obj);
487 else {
488 rotate = 0;
490 rotate += state.rotate;
492 p = &state.pagedims[state.pagedimcount - 1];
493 if ((state.pagedimcount == 0)
494 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
495 size_t size;
497 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
498 state.pagedims = realloc (state.pagedims, size);
499 if (!state.pagedims) {
500 err (1, "realloc pagedims to " FMT_s " (%d elems)",
501 size, state.pagedimcount + 1);
503 p = &state.pagedims[state.pagedimcount++];
504 p->rotate = rotate;
505 p->box = box;
506 p->pageno = pageno;
509 end = now ();
510 printd (state.sock, "T Processed %d pages in %f seconds",
511 state.pagecount, end - start);
514 static void layout (void)
516 int pindex;
517 fz_matrix ctm;
518 fz_rect box, box2;
519 double zoom, w;
520 struct pagedim *p = state.pagedims;
522 pindex = 0;
523 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
524 box.x0 = MIN (p->box.x0, p->box.x1);
525 box.y0 = MIN (p->box.y0, p->box.y1);
526 box.x1 = MAX (p->box.x0, p->box.x1);
527 box.y1 = MAX (p->box.y0, p->box.y1);
529 ctm = fz_identity;
530 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
531 ctm = fz_concat (ctm, fz_rotate (p->rotate));
532 box2 = fz_transformrect (ctm, box);
533 w = box2.x1 - box2.x0;
535 zoom = (state.w / w);
536 ctm = fz_identity;
537 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
538 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
539 memcpy (&p->ctm1, &ctm, sizeof (ctm));
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;
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 obj = fz_resolveindirect (obj);
565 if (fz_isarray (obj)) {
566 fz_obj *obj2;
568 obj2 = fz_arrayget (obj, 0);
569 if (fz_isint (obj2)) {
570 pageno = fz_toint (obj2);
572 else {
573 pageno = pdf_findpageobject (state.xref, obj2) - 1;
576 if (fz_arraylen (obj) > 3) {
577 fz_point p;
578 struct pagedim *pagedim = state.pagedims;
580 for (i = 0; i < state.pagedimcount; ++i) {
581 if (state.pagedims[i].pageno > pageno)
582 break;
583 pagedim = &state.pagedims[i];
586 p.x = fz_toint (fz_arrayget (obj, 2));
587 p.y = fz_toint (fz_arrayget (obj, 3));
588 p = fz_transformpoint (pagedim->ctm, p);
589 top = p.y;
593 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
594 printd (state.sock, "o %d %d %d %s",
595 level, pageno, top, outline->title);
596 next:
597 if (outline->child) {
598 recurse_outline (outline->child, level + 1);
600 outline = outline->next;
604 static void process_outline (void)
606 pdf_outline *outline;
608 outline = pdf_loadoutline (state.xref);
609 if (outline) {
610 recurse_outline (outline, 0);
611 pdf_freeoutline (outline);
615 static int comparespans (const void *l, const void *r)
617 fz_textspan const *const*ls = l;
618 fz_textspan const *const*rs = r;
619 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
622 /* wishful thinking function */
623 static void search (regex_t *re, int pageno, int y, int forward)
625 int i, j;
626 int ret;
627 char *p;
628 char buf[256];
629 fz_matrix ctm;
630 fz_error error;
631 fz_obj *pageobj;
632 fz_device *tdev;
633 pdf_page *drawpage;
634 fz_textspan *text, *span, **pspan;
635 struct pagedim *pdim, *pdimprev;
636 int stop = 0;
637 int niters = 0;
638 int nspans;
639 double start, end;
641 start = now ();
642 while (pageno >= 0 && pageno < state.pagecount && !stop) {
643 if (niters++ == 5) {
644 if (!state.lotsamemory) {
645 pdf_agestore(state.xref->store, 3);
647 niters = 0;
648 if (hasdata (state.sock)) {
649 printd (state.sock, "T attention requested aborting search at %d",
650 pageno);
651 stop = 1;
653 else {
654 printd (state.sock, "T searching in page %d", pageno);
657 pdimprev = NULL;
658 for (i = 0; i < state.pagedimcount; ++i) {
659 pdim = &state.pagedims[i];
660 if (pdim->pageno == pageno) {
661 goto found;
663 if (pdim->pageno > pageno) {
664 pdim = pdimprev;
665 goto found;
667 pdimprev = pdim;
669 pdim = pdimprev;
670 found:
672 pageobj = pdf_getpageobject (state.xref, pageno + 1);
673 if (!pageobj)
674 die (fz_throw ("cannot retrieve info from page %d", pageno));
676 error = pdf_loadpage (&drawpage, state.xref, pageobj);
677 if (error)
678 die (error);
680 ctm = fz_rotate (pdim->rotate);
682 text = fz_newtextspan ();
683 tdev = fz_newtextdevice (text);
684 error = pdf_runpage (state.xref, drawpage, tdev, pdim->ctm1);
685 if (error) die (error);
686 fz_freedevice (tdev);
688 nspans = 0;
689 for (span = text; span; span = span->next) {
690 nspans++;
692 pspan = malloc (sizeof (void *) * nspans);
693 if (!pspan) {
694 err (1, "malloc span pointers " FMT_s, sizeof (void *) * nspans);
696 for (i = 0, span = text; span; span = span->next, ++i) {
697 pspan[i] = span;
699 qsort (pspan, nspans, sizeof (fz_textspan *), comparespans);
701 j = forward ? 0 : nspans - 1;
702 while (nspans--) {
703 regmatch_t rm;
705 span = pspan[j];
706 j += forward ? 1 : -1;
707 p = buf;
708 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
709 if (forward) {
710 if (span->text[i].bbox.y0 < y + 1) {
711 continue;
714 else {
715 if (span->text[i].bbox.y0 > y - 1) {
716 continue;
719 if (span->text[i].c < 256) {
720 *p++ = span->text[i].c;
722 else {
723 *p++ = '?';
726 if (p == buf) {
727 continue;
729 *p++ = 0;
731 ret = regexec (re, buf, 1, &rm, 0);
732 if (ret) {
733 if (ret != REG_NOMATCH) {
734 size_t size;
735 char errbuf[80];
736 size = regerror (ret, re, errbuf, sizeof (errbuf));
737 printd (state.sock,
738 "T regexec error `%.*s'",
739 (int) size, errbuf);
740 fz_freetextspan (text);
741 pdf_freepage (drawpage);
742 free (pspan);
743 return;
746 else {
747 int xoff, yoff;
748 fz_bbox *sb, *eb;
749 fz_point p1, p2, p3, p4;
751 xoff = -pdim->bbox.x0;
752 yoff = -pdim->bbox.y0;
754 sb = &span->text[rm.rm_so].bbox;
755 eb = &span->text[rm.rm_eo - 1].bbox;
757 p1.x = sb->x0;
758 p1.y = sb->y0;
759 p2.x = eb->x1;
760 p2.y = sb->y0;
761 p3.x = eb->x1;
762 p3.y = eb->y1;
763 p4.x = sb->x0;
764 p4.y = eb->y1;
766 p1 = fz_transformpoint (ctm, p1);
767 p2 = fz_transformpoint (ctm, p2);
768 p3 = fz_transformpoint (ctm, p3);
769 p4 = fz_transformpoint (ctm, p4);
771 if (!stop) {
772 printd (state.sock, "F %d %d %f %f %f %f %f %f %f %f",
773 pageno, 1,
774 p1.x + xoff, p1.y + yoff,
775 p2.x + xoff, p2.y + yoff,
776 p3.x + xoff, p3.y + yoff,
777 p4.x + xoff, p4.y + yoff);
779 printd (state.sock, "T found at %d `%.*s' in %f sec",
780 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
781 now () - start);
783 else {
784 printd (state.sock, "R %d %d %f %f %f %f %f %f %f %f",
785 pageno, 2,
786 p1.x + xoff, p1.y + yoff,
787 p2.x + xoff, p2.y + yoff,
788 p3.x + xoff, p3.y + yoff,
789 p4.x + xoff, p4.y + yoff);
791 stop = 1;
794 if (forward) {
795 pageno += 1;
796 y = 0;
798 else {
799 pageno -= 1;
800 y = INT_MAX;
802 fz_freetextspan (text);
803 pdf_freepage (drawpage);
804 free (pspan);
806 end = now ();
807 if (!stop) {
808 printd (state.sock, "T no matches %f sec", end - start);
810 printd (state.sock, "D");
813 static
814 #ifdef _WIN32
815 DWORD _stdcall
816 #else
817 void *
818 #endif
819 mainloop (void *unused)
821 char *p = NULL;
822 int len, ret, oldlen = 0;
824 for (;;) {
825 len = readlen (state.sock);
826 if (len == 0) {
827 errx (1, "readlen returned 0");
830 if (oldlen < len + 1) {
831 p = realloc (p, len + 1);
832 if (!p) {
833 err (1, "realloc %d failed", len + 1);
835 oldlen = len + 1;
837 readdata (state.sock, p, len);
838 p[len] = 0;
840 if (!strncmp ("open", p, 4)) {
841 fz_obj *obj;
842 char *filename = p + 5;
844 openxref (filename);
845 initpdims ();
847 obj = fz_dictgets (state.xref->trailer, "Info");
848 if (obj) {
849 obj = fz_dictgets (obj, "Title");
850 if (obj) {
851 printd (state.sock, "t %s", pdf_toutf8 (obj));
855 else if (!strncmp ("free", p, 4)) {
856 void *ptr;
858 ret = sscanf (p + 4, " %p", &ptr);
859 if (ret != 1) {
860 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
862 lock ("free");
863 freepage (ptr);
864 unlock ("free");
865 printd (state.sock, "d");
867 else if (!strncmp ("search", p, 6)) {
868 int icase, pageno, y, ret, len2, forward;
869 char *pattern;
870 regex_t re;
872 ret = sscanf (p + 6, " %d %d %d %d,%n",
873 &icase, &pageno, &y, &forward, &len2);
874 if (ret != 4) {
875 errx (1, "malformed search `%s' ret=%d", p, ret);
878 pattern = p + 6 + len2;
879 ret = regcomp (&re, pattern,
880 REG_EXTENDED | (icase ? REG_ICASE : 0));
881 if (ret) {
882 char errbuf[80];
883 size_t size;
885 size = regerror (ret, &re, errbuf, sizeof (errbuf));
886 printd (state.sock, "T regcomp failed `%.*s'",
887 (int) size, errbuf);
889 else {
890 search (&re, pageno, y, forward);
891 regfree (&re);
894 else if (!strncmp ("geometry", p, 8)) {
895 int w, h;
897 printd (state.sock, "c");
898 ret = sscanf (p + 8, " %d %d", &w, &h);
899 if (ret != 2) {
900 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
902 state.h = h;
903 if (w != state.w) {
904 int i;
905 state.w = w;
906 for (i = 0; i < state.texcount; ++i) {
907 state.texowners[i].slice = NULL;
910 lock ("geometry");
911 layout ();
912 process_outline ();
913 unlock ("geometry");
914 printd (state.sock, "C %d", state.pagecount);
916 else if (!strncmp ("rotate", p, 6)) {
917 float rotate;
919 printd (state.sock, "c");
920 ret = sscanf (p + 6, " %f", &rotate);
921 if (ret != 1) {
922 errx (1, "bad rotate line `%.*s' ret=%d", len, p, ret);
924 state.rotate = rotate;
925 lock ("rotate");
926 state.pagedimcount = 0;
927 free (state.pagedims);
928 state.pagedims = NULL;
929 initpdims ();
930 layout ();
931 process_outline ();
932 unlock ("rotate");
933 printd (state.sock, "C %d", state.pagecount);
935 else if (!strncmp ("render", p, 6)) {
936 int pageno, pindex, w, h, ret;
937 struct page *page;
939 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
940 if (ret != 4) {
941 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
944 page = render (pageno, pindex);
945 printd (state.sock, "r %d %d %d %d %p\n",
946 pageno,
947 state.w,
948 state.h,
949 state.rotate,
950 page);
952 else if (!strncmp ("interrupt", p, 8)) {
953 printd (state.sock, "V interrupted");
955 else {
956 errx (1, "unknown command %.*s", len, p);
959 return 0;
962 static void upload2 (struct page *page, int slicenum, const char *cap)
964 int i;
965 int w, h;
966 double start, end;
967 struct slice *slice = &page->slices[slicenum];
969 w = page->pixmap->w;
970 h = page->pixmap->h;
972 ARSERT (w == slice->w);
973 if (slice->texindex != -1
974 && state.texowners[slice->texindex].slice == slice) {
975 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
977 else {
978 int subimage = 0;
979 int index = (state.texindex++ % state.texcount);
980 size_t offset = 0;
982 for (i = 0; i < slicenum; ++i) {
983 offset += w * page->slices[i].h * 4;
986 if (state.texowners[index].w == slice->w) {
987 if (state.texowners[index].h >= slice->h ) {
988 subimage = 1;
990 else {
991 state.texowners[index].h = slice->h;
994 else {
995 state.texowners[index].h = slice->h;
998 state.texowners[index].slice = slice;
999 state.texowners[index].w = slice->w;
1000 slice->texindex = index;
1002 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1003 start = now ();
1004 if (subimage) {
1006 GLenum err = glGetError ();
1007 if (err != GL_NO_ERROR) {
1008 printf ("\033[0;31mERROR1 %d %d %#x\033[0m\n", w, slice->h, err);
1009 abort ();
1012 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1017 slice->h,
1018 state.texform,
1019 state.texty,
1020 page->pixmap->samples + offset
1023 GLenum err = glGetError ();
1024 if (err != GL_NO_ERROR) {
1025 printf ("\033[0;31mERROR %d %d %#x\033[0m\n", w, slice->h, err);
1026 abort ();
1030 else {
1031 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1033 GL_RGBA8,
1035 slice->h,
1037 state.texform,
1038 state.texty,
1039 page->pixmap->samples + offset
1043 end = now ();
1044 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
1045 subimage ? "sub" : "img",
1046 page->pageno, slicenum,
1047 slice->w, slice->h,
1048 state.texids[slice->texindex],
1049 end - start);
1053 CAMLprim value ml_draw (value dispy_v, value w_v, value h_v,
1054 value py_v, value ptr_v)
1056 CAMLparam5 (dispy_v, w_v, h_v, py_v, ptr_v);
1057 int dispy = Int_val (dispy_v);
1058 int w = Int_val (w_v);
1059 int h = Int_val (h_v);
1060 int py = Int_val (py_v);
1061 char *s = String_val (ptr_v);
1062 int ret;
1063 void *ptr;
1064 struct page *page;
1065 int slicenum = 0;
1067 if (trylock ("ml_draw")) {
1068 goto done;
1071 ret = sscanf (s, "%p", &ptr);
1072 if (ret != 1) {
1073 errx (1, "cannot parse pointer `%s'", s);
1075 page = ptr;
1077 w = page->pixmap->w;
1079 ARSERT (h >= 0 && "ml_draw wrong h");
1080 ARSERT (py <= page->pixmap->h && "ml_draw wrong py");
1082 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1084 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
1085 struct slice *slice = &page->slices[slicenum];
1086 if (slice->h > py) {
1087 break;
1089 py -= slice->h;
1092 h = MIN (state.h, h);
1093 while (h) {
1094 int th;
1095 struct slice *slice = &page->slices[slicenum];
1097 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
1099 th = MIN (h, slice->h - py);
1100 upload2 (page, slicenum, "upload");
1102 glBegin (GL_QUADS);
1104 glTexCoord2i (0, py);
1105 glVertex2i (0, dispy);
1107 glTexCoord2i (w, py);
1108 glVertex2i (w, dispy);
1110 glTexCoord2i (w, py+th);
1111 glVertex2i (w, dispy + th);
1113 glTexCoord2i (0, py+th);
1114 glVertex2i (0, dispy + th);
1116 glEnd ();
1118 h -= th;
1119 py = 0;
1120 dispy += th;
1121 slicenum += 1;
1124 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1126 unlock ("ml_draw");
1127 done:
1128 CAMLreturn (Val_unit);
1131 static pdf_link *getlink (struct page *page, int x, int y)
1133 fz_point p;
1134 fz_matrix ctm;
1135 pdf_link *link;
1137 p.x = x + page->pixmap->x;
1138 p.y = y + page->pixmap->y;
1140 ctm = fz_invertmatrix (page->pagedim->ctm);
1141 p = fz_transformpoint (ctm, p);
1143 for (link = page->drawpage->links; link; link = link->next) {
1144 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1145 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1146 return link;
1150 return NULL;
1153 CAMLprim value ml_highlightlinks (value ptr_v, value yoff_v)
1155 CAMLparam2 (ptr_v, yoff_v);
1156 pdf_link *link;
1157 struct page *page;
1158 int xoff, yoff = Int_val (yoff_v);
1159 const char *s = String_val (ptr_v);
1161 if (trylock ("ml_highlightlinks")) {
1162 goto done;
1165 page = parse_pointer ("ml_highlightlinks", s);
1167 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1168 glEnable (GL_LINE_STIPPLE);
1169 glLineStipple (0.5, 0xcccc);
1171 xoff = -page->pixmap->x;
1172 yoff -= page->pixmap->y;
1174 glBegin (GL_QUADS);
1175 for (link = page->drawpage->links; link; link = link->next) {
1176 fz_point p1, p2, p3, p4;
1177 fz_matrix ctm = page->pagedim->ctm;
1179 p1.x = link->rect.x0;
1180 p1.y = link->rect.y0;
1182 p2.x = link->rect.x1;
1183 p2.y = link->rect.y0;
1185 p3.x = link->rect.x1;
1186 p3.y = link->rect.y1;
1188 p4.x = link->rect.x0;
1189 p4.y = link->rect.y1;
1191 p1 = fz_transformpoint (ctm, p1);
1192 p2 = fz_transformpoint (ctm, p2);
1193 p3 = fz_transformpoint (ctm, p3);
1194 p4 = fz_transformpoint (ctm, p4);
1196 switch (link->kind) {
1197 case PDF_LGOTO: glColor3ub (255, 0, 0); break;
1198 case PDF_LURI: glColor3ub (0, 0, 255); break;
1199 default: glColor3ub (0, 0, 0); break;
1202 glVertex2f (p1.x + xoff, p1.y + yoff);
1203 glVertex2f (p2.x + xoff, p2.y + yoff);
1204 glVertex2f (p3.x + xoff, p3.y + yoff);
1205 glVertex2f (p4.x + xoff, p4.y + yoff);
1207 glEnd ();
1209 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1210 glDisable (GL_LINE_STIPPLE);
1211 unlock ("ml_highlightlinks");
1213 done:
1214 CAMLreturn (Val_unit);
1217 CAMLprim value ml_getlink (value ptr_v, value x_v, value y_v)
1219 CAMLparam3 (ptr_v, x_v, y_v);
1220 CAMLlocal2 (ret_v, tup_v);
1221 pdf_link *link;
1222 struct page *page;
1223 char *s = String_val (ptr_v);
1225 ret_v = Val_int (0);
1226 if (trylock ("ml_getlink")) {
1227 ret_v = Val_int (0);
1228 goto done;
1231 page = parse_pointer ("ml_getlink", s);
1233 link = getlink (page, Int_val (x_v), Int_val (y_v));
1234 if (link) {
1235 switch (link->kind) {
1236 case PDF_LGOTO:
1238 int pageno;
1239 fz_point p;
1240 fz_obj *obj;
1242 pageno = -1;
1243 obj = fz_arrayget (link->dest, 0);
1244 if (fz_isindirect (obj)) {
1245 pageno = pdf_findpageobject (state.xref, obj) - 1;
1247 else if (fz_isint (obj)) {
1248 pageno = fz_toint (obj);
1251 if (fz_arraylen (link->dest) > 3) {
1252 p.x = fz_toint (fz_arrayget (link->dest, 2));
1253 p.y = fz_toint (fz_arrayget (link->dest, 3));
1254 p = fz_transformpoint (page->pagedim->ctm, p);
1256 else {
1257 p.x = 0.0;
1258 p.y = 0.0;
1261 tup_v = caml_alloc_tuple (2);
1262 ret_v = caml_alloc_small (1, 1);
1263 Field (tup_v, 0) = Val_int (pageno);
1264 Field (tup_v, 1) = Val_int (p.y);
1265 Field (ret_v, 0) = tup_v;
1267 break;
1269 case PDF_LURI:
1270 ret_v = caml_alloc_small (1, 0);
1271 Field (ret_v, 0) = caml_copy_string (fz_tostrbuf (link->dest));
1272 break;
1274 default:
1275 printd (state.sock, "T unhandled link kind %d", link->kind);
1276 break;
1279 unlock ("ml_getlink");
1281 done:
1282 CAMLreturn (ret_v);
1285 CAMLprim value ml_gettext (value ptr_v, value rect_v, value oy_v, value rectsel_v)
1287 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1288 fz_matrix ctm;
1289 fz_point p1, p2;
1290 struct page *page;
1291 fz_textspan *span;
1292 char *s = String_val (ptr_v);
1293 int rectsel = Bool_val (rectsel_v);
1294 int i, bx0, bx1, by0, by1, x0, x1, y0, y1, oy;
1296 /* stop GCC from complaining about uninitialized variables */
1297 #ifdef __GNUC__
1298 int rx0 = rx0, rx1 = rx1, ry0 = ry0, ry1 = ry1;
1299 #else
1300 int rx0, rx1, ry0, ry1;
1301 #endif
1303 if (trylock ("ml_gettext")) {
1304 goto done;
1307 page = parse_pointer ("ml_gettext", s);
1309 oy = Int_val (oy_v);
1310 p1.x = Int_val (Field (rect_v, 0));
1311 p1.y = Int_val (Field (rect_v, 1));
1312 p2.x = Int_val (Field (rect_v, 2));
1313 p2.y = Int_val (Field (rect_v, 3));
1315 if (0) {
1316 glEnable (GL_BLEND);
1317 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1318 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1319 glColor4f (0.0f, 0.0f, 0.0f, 0.2f);
1320 glRecti (p1.x, p1.y, p2.x, p2.y);
1321 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1322 glDisable (GL_BLEND);
1325 ctm = page->pagedim->ctm;
1326 if (!page->text) {
1327 fz_error error;
1328 fz_device *tdev;
1330 page->text = fz_newtextspan ();
1331 tdev = fz_newtextdevice (page->text);
1332 error = pdf_runpage (state.xref, page->drawpage, tdev,
1333 page->pagedim->ctm);
1334 if (error) die (error);
1335 fz_freedevice (tdev);
1338 printf ("\033c");
1340 printf ("BBox %f %f %f %f\n", p1.x, p1.y, p2.x, p2.y);
1341 p1.x += page->pixmap->x;
1342 p1.y += page->pixmap->y;
1343 p2.x += page->pixmap->x;
1344 p2.y += page->pixmap->y;
1345 x0 = p1.x;
1346 y0 = p1.y;
1347 x1 = p2.x;
1348 y1 = p2.y;
1349 printf ("BBox %d %d %d %d %d %d\n", x0, y0, x1, y1, oy, page->pageno);
1351 for (span = page->text; span; span = span->next) {
1352 int seen = 0;
1354 /* fz_debugtextspanxml (span); */
1355 for (i = 0; i < span->len; ++i) {
1356 long c;
1358 bx0 = span->text[i].bbox.x0;
1359 bx1 = span->text[i].bbox.x1;
1360 by0 = span->text[i].bbox.y0 + oy;
1361 by1 = span->text[i].bbox.y1 + oy;
1363 if ((bx1 >= x0 && bx0 <= x1 && by1 >= y0 && by0 <= y1)) {
1364 if (!seen) {
1365 rx0 = bx0 - page->pixmap->x;
1366 rx1 = bx1 - page->pixmap->x;
1367 ry0 = by0;
1368 ry1 = by1;
1371 seen = 1;
1372 c = span->text[i].c;
1373 if (c < 256) {
1374 if ((isprint (c) && !isspace (c))) {
1375 if (!rectsel) {
1376 bx0 -= page->pixmap->x;
1377 bx1 -= page->pixmap->x;
1378 glEnable (GL_BLEND);
1379 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1380 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1381 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1382 glRecti (bx0, by0, bx1, by1);
1383 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1384 glDisable (GL_BLEND);
1386 if (isprint (c) || c ==' ') {
1387 rx1 = bx1;
1388 ry1 = by1;
1391 putc (c, stdout);
1393 else {
1394 putc ('?', stdout);
1399 if (rectsel) {
1400 if (seen) {
1401 glEnable (GL_BLEND);
1402 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1403 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1404 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1405 glRecti (rx0, ry0, rx1, ry1);
1406 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1407 glDisable (GL_BLEND);
1411 if (seen && span->eol) {
1412 x0 = page->pixmap->x;
1413 putc ('\n', stdout);
1416 unlock ("ml_gettext");
1418 done:
1419 CAMLreturn (Val_unit);
1422 CAMLprim value ml_getpagewh (value pagedimno_v)
1424 CAMLparam1 (pagedimno_v);
1425 CAMLlocal1 (ret_v);
1426 int pagedimno = Int_val (pagedimno_v);
1428 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1429 Store_double_field (ret_v, 0, state.pagedims[pagedimno].box.x0);
1430 Store_double_field (ret_v, 1, state.pagedims[pagedimno].box.x1);
1431 Store_double_field (ret_v, 2, state.pagedims[pagedimno].box.y0);
1432 Store_double_field (ret_v, 3, state.pagedims[pagedimno].box.y1);
1433 CAMLreturn (ret_v);
1436 CAMLprim value ml_init (value sock_v)
1438 #ifndef _WIN32
1439 int ret;
1440 #endif
1441 CAMLparam1 (sock_v);
1443 state.texcount = 256;
1444 state.sliceheight = 24;
1445 state.texform = GL_RGBA;
1446 state.texty = GL_UNSIGNED_BYTE;
1448 fz_accelerate ();
1449 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1450 if (!state.texids) {
1451 err (1, "calloc texids " FMT_s,
1452 state.texcount * sizeof (*state.texids));
1455 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1456 if (!state.texowners) {
1457 err (1, "calloc texowners " FMT_s,
1458 state.texcount * sizeof (*state.texowners));
1461 glGenTextures (state.texcount, state.texids);
1463 #ifdef _WIN32
1464 state.sock = Socket_val (sock_v);
1465 #else
1466 state.sock = Int_val (sock_v);
1467 #endif
1469 state.cache = fz_newglyphcache ();
1470 if (!state.cache) {
1471 errx (1, "fz_newglyphcache failed");
1474 #ifdef _WIN32
1475 InitializeCriticalSection (&critsec);
1476 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
1477 if (state.thread == INVALID_HANDLE_VALUE) {
1478 errx (1, "CreateThread failed: %lx", GetLastError ());
1480 #else
1481 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1482 if (ret) {
1483 errx (1, "pthread_create: %s", strerror (errno));
1485 #endif
1487 CAMLreturn (Val_unit);