Fix percent computation
[llpp.git] / link.c
bloba6b477cdc6a80bccdd1760dafdb332f40646ffda
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 #include <GL/gl.h>
75 #ifndef GL_TEXTURE_RECTANGLE_ARB
76 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
77 #endif
79 #include <caml/fail.h>
80 #include <caml/alloc.h>
81 #include <caml/memory.h>
82 #include <caml/unixsupport.h>
84 #include <fitz.h>
85 #include <mupdf.h>
87 #if 0
88 #define lprintf printf
89 #else
90 #define lprintf(...)
91 #endif
93 #define ARSERT(cond) for (;;) { \
94 if (!(cond)) { \
95 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
96 } \
97 break; \
100 struct slice {
101 int texindex;
102 int w, h;
105 struct page {
106 int pageno;
107 int slicecount;
108 fz_textspan *text;
109 fz_pixmap *pixmap;
110 pdf_page *drawpage;
111 struct pagedim *pagedim;
112 struct page *prev;
113 struct slice slices[];
116 struct pagedim {
117 int pageno;
118 int rotate;
119 fz_rect box;
120 fz_bbox bbox;
121 fz_matrix ctm;
124 struct {
125 int sock;
126 int sliceheight;
127 struct page *pages;
128 struct pagedim *pagedims;
129 int pagecount;
130 int pagedimcount;
131 pdf_xref *xref;
132 fz_glyphcache *cache;
133 int w, h;
135 int texindex;
136 int texcount;
137 GLuint *texids;
139 GLenum texform;
140 GLenum texty;
142 int lotsamemory;
144 int *pagetbl;
145 struct {
146 int w, h;
147 struct slice *slice;
148 } *texowners;
150 #ifdef _WIN32
151 HANDLE thread;
152 #else
153 pthread_t thread;
154 #endif
155 } state;
157 #ifdef _WIN32
158 static CRITICAL_SECTION critsec;
160 static void lock (void *unused)
162 (void) unused;
163 EnterCriticalSection (&critsec);
166 static void unlock (void *unused)
168 (void) unused;
169 LeaveCriticalSection (&critsec);
172 static int trylock (void *unused)
174 return TryEnterCriticalSection (&critsec) == 0;
176 #else
177 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
179 static void lock (const char *cap)
181 int ret = pthread_mutex_lock (&mutex);
182 if (ret) {
183 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
187 static void unlock (const char *cap)
189 int ret = pthread_mutex_unlock (&mutex);
190 if (ret) {
191 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
195 static int trylock (const char *cap)
197 int ret = pthread_mutex_trylock (&mutex);
199 if (ret && ret != EBUSY) {
200 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
202 return ret == EBUSY;
204 #endif
206 static void *parse_pointer (const char *cap, const char *s)
208 int ret;
209 void *ptr;
211 ret = sscanf (s, "%p", &ptr);
212 if (ret != 1) {
213 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
215 return ptr;
218 static int hasdata (int sock)
220 int n;
221 fd_set s;
222 struct timeval tv;
223 FD_ZERO (&s);
224 FD_SET (sock, &s);
225 tv.tv_sec = 0;
226 tv.tv_usec = 0;
227 n = select (sock + 1, &s, NULL, NULL, &tv);
228 if (n == 0) return 0;
229 if (n == 1) return 1;
230 sockerr (1, "hasdata: select error ret=%d", n);
233 static double now (void)
235 struct timeval tv;
237 if (gettimeofday (&tv, NULL)) {
238 err (1, "gettimeofday");
240 return tv.tv_sec + tv.tv_usec*1e-6;
243 static void readdata (int fd, char *p, int size)
245 ssize_t n;
247 n = recv (fd, p, size, 0);
248 if (n - size) {
249 if (!n) errx (1, "EOF while reading");
250 sockerr (1, "recv (req %d, ret %zd)", size, n);
254 static void writedata (int fd, char *p, int size)
256 char buf[4];
257 ssize_t n;
259 buf[0] = (size >> 24) & 0xff;
260 buf[1] = (size >> 16) & 0xff;
261 buf[2] = (size >> 8) & 0xff;
262 buf[3] = (size >> 0) & 0xff;
264 n = send (fd, buf, 4, 0);
265 if (n != 4) {
266 if (!n) errx (1, "EOF while writing length");
267 sockerr (1, "send %zd", n);
270 n = send (fd, p, size, 0);
271 if (n - size) {
272 if (!n) errx (1, "EOF while writing data");
273 sockerr (1, "send (req %d, ret %zd)", size, n);
277 static void
278 #ifdef __GNUC__
279 __attribute__ ((format (printf, 2, 3)))
280 #endif
281 printd (int fd, const char *fmt, ...)
283 int len;
284 va_list ap;
285 char buf[200];
287 va_start (ap, fmt);
288 len = vsnprintf (buf, sizeof (buf), fmt, ap);
289 va_end (ap);
290 writedata (fd, buf, len);
293 static void die (fz_error error)
295 fz_catch (error, "aborting");
296 if (state.xref)
297 pdf_closexref (state.xref);
298 exit (1);
301 static void openxref (char *filename)
303 int fd;
304 fz_error error;
305 fz_stream *file;
307 fd = open (filename, O_BINARY | O_RDONLY, 0666);
308 if (fd < 0)
309 die (fz_throw ("cannot open file '%s'", filename));
311 file = fz_openfile (fd);
312 state.xref = pdf_openxref (file);
313 if (!state.xref)
314 die (fz_throw ("cannot open PDF file '%s'", filename));
315 fz_dropstream (file);
317 if (pdf_needspassword (state.xref)) {
318 die (fz_throw ("password protected"));
321 error = pdf_loadpagetree (state.xref);
322 if (error) {
323 die (fz_throw ("cannot load page tree"));
326 state.pagecount = pdf_getpagecount (state.xref);
327 state.pagetbl = stat_alloc (state.pagecount * sizeof (*state.pagetbl));
330 static int readlen (int fd)
332 ssize_t n;
333 char p[4];
335 n = recv (fd, p, 4, 0);
336 if (n != 4) {
337 if (!n) errx (1, "EOF while reading length");
338 sockerr (1, "read %zd", n);
341 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
344 static void freepage (struct page *page)
346 int i;
347 struct page *p;
349 fz_droppixmap (page->pixmap);
350 for (p = state.pages; p; p = p->prev) {
351 if (p->prev == page) {
352 p->prev = page->prev;
353 break;
356 for (i = 0; i < page->slicecount; ++i) {
357 struct slice *s = &page->slices[i];
358 if (s->texindex != -1) {
359 if (state.texowners[s->texindex].slice == s) {
360 state.texowners[s->texindex].slice = NULL;
361 ARSERT (state.texowners[s->texindex].w == s->w);
362 ARSERT (state.texowners[s->texindex].h >= s->h);
366 if (page->text) {
367 fz_freetextspan (page->text);
369 if (page->drawpage) {
370 pdf_freepage (page->drawpage);
373 free (page);
376 static void subdivide (struct page *p)
378 int i;
379 int h = p->pixmap->h;
380 int th = MIN (h, state.sliceheight);
382 for (i = 0; i < p->slicecount; ++i) {
383 struct slice *s = &p->slices[i];
384 s->texindex = -1;
385 s->h = MIN (th, h);
386 s->w = p->pixmap->w;
387 h -= th;
391 static void *render (int pageno, int pindex)
393 fz_error error;
394 int slicecount;
395 fz_obj *pageobj;
396 struct page *page;
397 double start, end;
398 pdf_page *drawpage;
399 fz_device *idev;
400 struct pagedim *pagedim;
402 start = now ();
403 printd (state.sock, "V rendering %d", pageno);
404 pdf_flushxref (state.xref, 0);
406 pagedim = &state.pagedims[pindex];
407 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
408 + state.sliceheight - 1) / state.sliceheight;
409 slicecount += slicecount == 0;
411 page = calloc (sizeof (*page)
412 + (slicecount * sizeof (struct slice)), 1);
413 if (!page) {
414 err (1, "calloc page %d\n", pageno);
416 page->slicecount = slicecount;
417 page->prev = state.pages;
418 state.pages = page;
420 pageobj = pdf_getpageobject (state.xref, pageno);
421 if (!pageobj)
422 die (fz_throw ("cannot retrieve info from page %d", pageno));
424 error = pdf_loadpage (&drawpage, state.xref, pageobj);
425 if (error)
426 die (error);
428 page->pixmap = fz_newpixmapwithrect (pdf_devicergb, pagedim->bbox);
429 if (error)
430 die (error);
431 fz_clearpixmap (page->pixmap, 0xFF);
433 idev = fz_newdrawdevice (state.cache, page->pixmap);
434 if (!idev)
435 die (fz_throw ("fz_newdrawdevice failed"));
436 error = pdf_runcontentstream (idev, pagedim->ctm, state.xref,
437 drawpage->resources,
438 drawpage->contents);
439 fz_freedevice (idev);
441 page->drawpage = drawpage;
442 page->pagedim = pagedim;
443 page->pageno = pageno;
444 subdivide (page);
445 end = now ();
447 if (!state.lotsamemory) {
448 pdf_agestoreditems (state.xref->store);
449 pdf_evictageditems (state.xref->store);
452 printd (state.sock, "V rendering %d took %f sec", pageno, end - start);
453 return page;
456 static void initpdims (void)
458 int pageno;
459 double start, end;
461 start = now ();
462 for (pageno = 0; pageno < state.pagecount; ++pageno) {
463 int rotate;
464 fz_rect box;
465 struct pagedim *p;
466 fz_obj *obj, *pageobj;
468 pageobj = pdf_getpageobject (state.xref, pageno + 1);
470 obj = fz_dictgets (pageobj, "CropBox");
471 if (!fz_isarray (obj)) {
472 obj = fz_dictgets (pageobj, "MediaBox");
473 if (!fz_isarray (obj)) {
474 die (fz_throw ("cannot find page bounds %d (%d Rd)",
475 fz_tonum (pageobj), fz_togen (pageobj)));
478 box = pdf_torect (obj);
480 obj = fz_dictgets (pageobj, "Rotate");
481 if (fz_isint (obj)) {
482 rotate = fz_toint (obj);
484 else {
485 rotate = 0;
488 state.pagetbl[pageno] = fz_tonum (state.xref->pagerefs[pageno]);
489 p = &state.pagedims[state.pagedimcount - 1];
490 if ((state.pagedimcount == 0)
491 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
492 size_t size;
494 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
495 state.pagedims = realloc (state.pagedims, size);
496 if (!state.pagedims) {
497 err (1, "realloc pagedims to %zu (%d elems)",
498 size, state.pagedimcount + 1);
500 p = &state.pagedims[state.pagedimcount++];
501 p->rotate = rotate;
502 p->box = box;
503 p->pageno = pageno;
506 end = now ();
507 printd (state.sock, "T Processed %d pages in %f seconds",
508 state.pagecount, end - start);
511 static void layout (void)
513 int pindex;
514 fz_matrix ctm;
515 fz_rect box, box2;
516 double zoom, w;
517 struct pagedim *p = state.pagedims;
519 pindex = 0;
520 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
521 box.x0 = MIN (p->box.x0, p->box.x1);
522 box.y0 = MIN (p->box.y0, p->box.y1);
523 box.x1 = MAX (p->box.x0, p->box.x1);
524 box.y1 = MAX (p->box.y0, p->box.y1);
526 ctm = fz_identity ();
527 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
528 ctm = fz_concat (ctm, fz_rotate (p->rotate));
529 box2 = fz_transformrect (ctm, box);
530 w = box2.x1 - box2.x0;
532 zoom = (state.w / w);
533 ctm = fz_identity ();
534 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
535 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
536 ctm = fz_concat (ctm, fz_rotate (p->rotate));
537 p->bbox = fz_roundrect (fz_transformrect (ctm, box));
538 memcpy (&p->ctm, &ctm, sizeof (ctm));
541 while (p-- != state.pagedims) {
542 printd (state.sock, "l %d %d %d",
543 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0);
547 static void recurse_outline (pdf_outline *outline, int level)
549 while (outline) {
550 int i, num;
551 fz_obj *obj;
552 int top = 0;
553 int pageno = -1;
555 if (!outline->link) goto next;
557 obj = outline->link->dest;
558 if (fz_isindirect (obj)) {
559 num = fz_tonum (obj);
561 for (i = 0; i < state.pagecount; ++i) {
562 if (state.pagetbl[i] == num) {
563 pageno = i;
564 break;
568 else if (fz_isarray (obj)) {
569 fz_obj *obj2;
571 obj2 = fz_arrayget (obj, 0);
572 if (fz_isint (obj2)) {
573 pageno = fz_toint (obj2);
575 else {
576 num = fz_tonum (obj2);
577 for (i = 0; i < state.pagecount; ++i) {
578 if (state.pagetbl[i] == num) {
579 pageno = i;
580 break;
585 if (fz_arraylen (obj) > 3) {
586 fz_point p;
587 struct pagedim *pagedim = state.pagedims;
589 for (i = 0; i < state.pagedimcount; ++i) {
590 if (state.pagedims[i].pageno > pageno)
591 break;
592 pagedim = &state.pagedims[i];
595 p.x = fz_toint (fz_arrayget (obj, 2));
596 p.y = fz_toint (fz_arrayget (obj, 3));
597 p = fz_transformpoint (pagedim->ctm, p);
598 top = p.y;
602 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
603 printd (state.sock, "o %d %d %d %s",
604 level, pageno, top, outline->title);
605 next:
606 if (outline->child) {
607 recurse_outline (outline->child, level + 1);
609 outline = outline->next;
613 static void process_outline (void)
615 pdf_outline *outline;
617 outline = pdf_loadoutline (state.xref);
618 if (outline) {
619 recurse_outline (outline, 0);
620 pdf_freeoutline (outline);
624 static int comparespans (const void *l, const void *r)
626 fz_textspan const *const*ls = l;
627 fz_textspan const *const*rs = r;
628 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
631 /* wishful thinking function */
632 static void search (regex_t *re, int pageno, int y, int forward)
634 int i, j;
635 int ret;
636 char *p;
637 char buf[256];
638 fz_error error;
639 fz_obj *pageobj;
640 fz_device *tdev;
641 pdf_page *drawpage;
642 fz_textspan *text, *span, **pspan;
643 struct pagedim *pdim, *pdimprev;
644 int stop = 0;
645 int niters = 0;
646 int nspans;
647 double start, end;
649 start = now ();
650 while (pageno >= 0 && pageno < state.pagecount && !stop) {
651 if (niters++ == 5) {
652 if (!state.lotsamemory) {
653 pdf_agestoreditems (state.xref->store);
654 pdf_evictageditems (state.xref->store);
656 niters = 0;
657 if (hasdata (state.sock)) {
658 printd (state.sock, "T attention requested aborting search at %d",
659 pageno);
660 stop = 1;
662 else {
663 printd (state.sock, "T searching in page %d", pageno);
666 pdimprev = NULL;
667 for (i = 0; i < state.pagedimcount; ++i) {
668 pdim = &state.pagedims[i];
669 if (pdim->pageno == pageno) {
670 goto found;
672 if (pdim->pageno > pageno) {
673 pdim = pdimprev;
674 goto found;
676 pdimprev = pdim;
678 pdim = pdimprev;
679 found:
681 pageobj = pdf_getpageobject (state.xref, pageno + 1);
682 if (!pageobj)
683 die (fz_throw ("cannot retrieve info from page %d", pageno));
685 error = pdf_loadpage (&drawpage, state.xref, pageobj);
686 if (error)
687 die (error);
689 text = fz_newtextspan ();
690 tdev = fz_newtextdevice (text);
691 error = pdf_runcontentstream (tdev, pdim->ctm, state.xref,
692 drawpage->resources,
693 drawpage->contents);
694 if (error) die (error);
695 fz_freedevice (tdev);
697 nspans = 0;
698 for (span = text; span; span = span->next) {
699 nspans++;
701 pspan = malloc (sizeof (void *) * nspans);
702 if (!pspan) {
703 err (1, "malloc span pointers %zu", sizeof (void *) * nspans);
705 for (i = 0, span = text; span; span = span->next, ++i) {
706 pspan[i] = span;
708 qsort (pspan, nspans, sizeof (fz_textspan *), comparespans);
710 j = forward ? 0 : nspans - 1;
711 while (nspans--) {
712 regmatch_t rm;
714 span = pspan[j];
715 j += forward ? 1 : -1;
716 p = buf;
717 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
718 if (forward) {
719 if (span->text[i].bbox.y0 < y + 1) {
720 continue;
723 else {
724 if (span->text[i].bbox.y0 > y - 1) {
725 continue;
728 if (span->text[i].c < 256) {
729 *p++ = span->text[i].c;
731 else {
732 *p++ = '?';
735 if (p == buf) {
736 continue;
738 *p++ = 0;
740 ret = regexec (re, buf, 1, &rm, 0);
741 if (ret) {
742 if (ret != REG_NOMATCH) {
743 size_t size;
744 char errbuf[80];
745 size = regerror (ret, re, errbuf, sizeof (errbuf));
746 printd (state.sock,
747 "T regexec error `%.*s'",
748 (int) size, errbuf);
749 fz_freetextspan (text);
750 pdf_freepage (drawpage);
751 free (pspan);
752 return;
755 else {
756 fz_rect r;
758 r.x0 = span->text[rm.rm_so].bbox.x0 - pdim->bbox.x0;
759 r.y0 = span->text[rm.rm_so].bbox.y0;
760 r.x1 = span->text[rm.rm_eo - 1].bbox.x1 - pdim->bbox.x0;
761 r.y1 = span->text[rm.rm_eo - 1].bbox.y1;
763 if (!stop) {
764 printd (state.sock, "F %d %d %f %f %f %f",
765 pageno, 1,
766 r.x0, r.y0,
767 r.x1, r.y1);
769 else {
770 printd (state.sock, "R %d %d %f %f %f %f",
771 pageno, 2,
772 r.x0, r.y0,
773 r.x1, r.y1);
775 printd (state.sock, "T found at %d `%.*s' %f in %f sec",
776 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
777 span->text[0].bbox.y0 - drawpage->mediabox.y0,
778 now () - start);
779 stop = 1;
782 if (forward) {
783 pageno += 1;
784 y = 0;
786 else {
787 pageno -= 1;
788 y = INT_MAX;
790 fz_freetextspan (text);
791 pdf_freepage (drawpage);
792 free (pspan);
794 end = now ();
795 if (!stop) {
796 printd (state.sock, "T no matches %f sec", end - start);
798 printd (state.sock, "D");
801 static
802 #ifdef _WIN32
803 DWORD _stdcall
804 #else
805 void *
806 #endif
807 mainloop (void *unused)
809 char *p = NULL;
810 int len, ret, oldlen = 0;
812 for (;;) {
813 len = readlen (state.sock);
814 if (len == 0) {
815 errx (1, "readlen returned 0");
818 if (oldlen < len + 1) {
819 p = realloc (p, len + 1);
820 if (!p) {
821 err (1, "realloc %d failed", len + 1);
823 oldlen = len + 1;
825 readdata (state.sock, p, len);
826 p[len] = 0;
828 if (!strncmp ("open", p, 4)) {
829 fz_obj *obj;
830 char *filename = p + 5;
832 openxref (filename);
833 initpdims ();
835 obj = fz_dictgets (state.xref->trailer, "Info");
836 if (obj) {
837 obj = fz_dictgets (obj, "Title");
838 if (obj) {
839 printd (state.sock, "t %s", pdf_toutf8 (obj));
843 else if (!strncmp ("free", p, 4)) {
844 void *ptr;
846 ret = sscanf (p + 4, " %p", &ptr);
847 if (ret != 1) {
848 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
850 lock ("free");
851 freepage (ptr);
852 unlock ("free");
853 printd (state.sock, "d");
855 else if (!strncmp ("search", p, 6)) {
856 int icase, pageno, y, ret, len2, forward;
857 char *pattern;
858 regex_t re;
860 ret = sscanf (p + 6, " %d %d %d %d,%n",
861 &icase, &pageno, &y, &forward, &len2);
862 if (ret != 4) {
863 errx (1, "malformed search `%s' ret=%d", p, ret);
866 pattern = p + 6 + len2;
867 ret = regcomp (&re, pattern,
868 REG_EXTENDED | (icase ? REG_ICASE : 0));
869 if (ret) {
870 char errbuf[80];
871 size_t size;
873 size = regerror (ret, &re, errbuf, sizeof (errbuf));
874 printd (state.sock, "T regcomp failed `%.*s'",
875 (int) size, errbuf);
877 else {
878 search (&re, pageno, y, forward);
879 regfree (&re);
882 else if (!strncmp ("geometry", p, 8)) {
883 int w, h;
885 printd (state.sock, "c");
886 ret = sscanf (p + 8, " %d %d", &w, &h);
887 if (ret != 2) {
888 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
890 state.h = h;
891 if (w != state.w) {
892 int i;
893 state.w = w;
894 for (i = 0; i < state.texcount; ++i) {
895 state.texowners[i].slice = NULL;
898 lock ("geometry");
899 layout ();
900 process_outline ();
901 unlock ("geometry");
902 printd (state.sock, "C %d", state.pagecount);
904 else if (!strncmp ("render", p, 6)) {
905 int pageno, pindex, w, h, ret;
906 struct page *page;
908 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
909 if (ret != 4) {
910 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
913 page = render (pageno, pindex);
914 printd (state.sock, "r %d %d %d %p\n",
915 pageno,
916 state.w,
917 state.h,
918 page);
920 else {
921 errx (1, "unknown command %.*s", len, p);
924 return 0;
927 static void upload2 (struct page *page, int slicenum, const char *cap)
929 int i;
930 int w, h;
931 double start, end;
932 struct slice *slice = &page->slices[slicenum];
934 w = page->pixmap->w;
935 h = page->pixmap->h;
937 ARSERT (w == slice->w);
938 if (slice->texindex != -1
939 && state.texowners[slice->texindex].slice == slice) {
940 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
942 else {
943 int subimage = 0;
944 int index = (state.texindex++ % state.texcount);
945 size_t offset = 0;
947 for (i = 0; i < slicenum; ++i) {
948 offset += w * page->slices[i].h * 4;
951 if (state.texowners[index].w == slice->w) {
952 if (state.texowners[index].h >= slice->h ) {
953 subimage = 1;
955 else {
956 state.texowners[index].h = slice->h;
959 else {
960 state.texowners[index].h = slice->h;
963 state.texowners[index].slice = slice;
964 state.texowners[index].w = slice->w;
965 slice->texindex = index;
967 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
968 start = now ();
969 if (subimage) {
971 GLenum err = glGetError ();
972 if (err != GL_NO_ERROR) {
973 printf ("\033[0;31mERROR1 %d %d %#x\033[0m\n", w, slice->h, err);
974 abort ();
977 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
982 slice->h,
983 state.texform,
984 state.texty,
985 page->pixmap->samples + offset
988 GLenum err = glGetError ();
989 if (err != GL_NO_ERROR) {
990 printf ("\033[0;31mERROR %d %d %#x\033[0m\n", w, slice->h, err);
991 abort ();
995 else {
996 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
998 GL_RGBA8,
1000 slice->h,
1002 state.texform,
1003 state.texty,
1004 page->pixmap->samples + offset
1008 end = now ();
1009 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
1010 subimage ? "sub" : "img",
1011 page->pageno, slicenum,
1012 slice->w, slice->h,
1013 state.texids[slice->texindex],
1014 end - start);
1018 CAMLprim value ml_draw (value dispy_v, value w_v, value h_v,
1019 value py_v, value ptr_v)
1021 CAMLparam5 (dispy_v, w_v, h_v, py_v, ptr_v);
1022 int dispy = Int_val (dispy_v);
1023 int w = Int_val (w_v);
1024 int h = Int_val (h_v);
1025 int py = Int_val (py_v);
1026 char *s = String_val (ptr_v);
1027 int ret;
1028 void *ptr;
1029 struct page *page;
1030 int slicenum = 0;
1032 if (trylock ("ml_draw")) {
1033 goto done;
1036 ret = sscanf (s, "%p", &ptr);
1037 if (ret != 1) {
1038 errx (1, "cannot parse pointer `%s'", s);
1040 page = ptr;
1042 w = page->pixmap->w;
1044 ARSERT (h >= 0 && "ml_draw wrong h");
1046 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1048 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
1049 struct slice *slice = &page->slices[slicenum];
1050 if (slice->h > py) {
1051 break;
1053 py -= slice->h;
1056 h = MIN (state.h, h);
1057 while (h) {
1058 int th;
1059 struct slice *slice = &page->slices[slicenum];
1061 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
1063 th = MIN (h, slice->h - py);
1064 upload2 (page, slicenum, "upload");
1066 glBegin (GL_QUADS);
1068 glTexCoord2i (0, py);
1069 glVertex2i (0, dispy);
1071 glTexCoord2i (w, py);
1072 glVertex2i (w, dispy);
1074 glTexCoord2i (w, py+th);
1075 glVertex2i (w, dispy + th);
1077 glTexCoord2i (0, py+th);
1078 glVertex2i (0, dispy + th);
1080 glEnd ();
1082 h -= th;
1083 py = 0;
1084 dispy += th;
1085 slicenum += 1;
1088 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1090 unlock ("ml_draw");
1091 done:
1092 CAMLreturn (Val_unit);
1095 static pdf_link *getlink (struct page *page, int x, int y)
1097 fz_point p;
1098 fz_matrix ctm;
1099 pdf_link *link;
1101 p.x = x;
1102 p.y = y;
1104 ctm = fz_invertmatrix (page->pagedim->ctm);
1105 p = fz_transformpoint (ctm, p);
1107 for (link = page->drawpage->links; link; link = link->next) {
1108 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1109 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1110 if (link->kind == PDF_LGOTO) {
1111 return link;
1116 return NULL;
1119 CAMLprim value ml_checklink (value ptr_v, value x_v, value y_v)
1121 CAMLparam3 (ptr_v, x_v, y_v);
1122 char *s = String_val (ptr_v);
1123 int ret;
1125 if (trylock ("ml_checklink")) {
1126 ret = 0;
1128 else {
1129 ret = NULL != getlink (parse_pointer ("ml_checklink", s),
1130 Int_val (x_v), Int_val (y_v));
1131 unlock ("ml_checklink");
1133 CAMLreturn (Val_bool (ret));
1136 CAMLprim value ml_getlink (value ptr_v, value x_v, value y_v)
1138 CAMLparam3 (ptr_v, x_v, y_v);
1139 CAMLlocal2 (ret_v, tup_v);
1140 pdf_link *link;
1141 struct page *page;
1142 char *s = String_val (ptr_v);
1144 if (trylock ("ml_getlink")) {
1145 ret_v = Val_int (0);
1146 goto done;
1149 page = parse_pointer ("ml_getlink", s);
1151 link = getlink (page, Int_val (x_v), Int_val (y_v));
1152 if (link) {
1153 int pageno;
1154 fz_point p;
1155 fz_obj *obj;
1157 pageno = -1;
1158 obj = fz_arrayget (link->dest, 0);
1159 if (fz_isindirect (obj)) {
1160 pageno = pdf_findpageobject (state.xref, obj) - 1;
1162 else if (fz_isint (obj)) {
1163 pageno = fz_toint (obj);
1166 if (fz_arraylen (link->dest) > 3) {
1167 p.x = fz_toint (fz_arrayget (link->dest, 2));
1168 p.y = fz_toint (fz_arrayget (link->dest, 3));
1169 p = fz_transformpoint (page->pagedim->ctm, p);
1171 else {
1172 p.x = 0.0;
1173 p.y = 0.0;
1176 tup_v = caml_alloc_tuple (2);
1177 ret_v = caml_alloc_small (1, 1);
1178 Field (tup_v, 0) = Val_int (pageno);
1179 Field (tup_v, 1) = Val_int (p.y);
1180 Field (ret_v, 0) = tup_v;
1182 else {
1183 ret_v = Val_int (0);
1185 unlock ("ml_getlink");
1187 done:
1188 CAMLreturn (ret_v);
1191 CAMLprim value ml_gettext (value ptr_v, value rect_v, value oy_v, value rectsel_v)
1193 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1194 fz_matrix ctm;
1195 fz_point p1, p2;
1196 struct page *page;
1197 fz_textspan *span;
1198 char *s = String_val (ptr_v);
1199 int rectsel = Bool_val (rectsel_v);
1200 int i, bx0, bx1, by0, by1, x0, x1, y0, y1, oy;
1202 /* stop GCC from complaining about uninitialized variables */
1203 #ifdef __GNUC__
1204 int rx0 = rx0, rx1 = rx1, ry0 = ry0, ry1 = ry1;
1205 #else
1206 int rx0, rx1, ry0, ry1;
1207 #endif
1209 if (trylock ("ml_gettext")) {
1210 goto done;
1213 page = parse_pointer ("ml_gettext", s);
1215 oy = Int_val (oy_v);
1216 p1.x = Int_val (Field (rect_v, 0));
1217 p1.y = Int_val (Field (rect_v, 1));
1218 p2.x = Int_val (Field (rect_v, 2));
1219 p2.y = Int_val (Field (rect_v, 3));
1221 if (0) {
1222 glEnable (GL_BLEND);
1223 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1224 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1225 glColor4f (0.0f, 0.0f, 0.0f, 0.2f);
1226 glRecti (p1.x, p1.y, p2.x, p2.y);
1227 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1228 glDisable (GL_BLEND);
1231 ctm = page->pagedim->ctm;
1232 if (!page->text) {
1233 fz_error error;
1234 fz_device *tdev;
1236 page->text = fz_newtextspan ();
1237 tdev = fz_newtextdevice (page->text);
1238 error = pdf_runcontentstream (tdev, page->pagedim->ctm, state.xref,
1239 page->drawpage->resources,
1240 page->drawpage->contents);
1241 if (error) die (error);
1242 fz_freedevice (tdev);
1245 printf ("\033c");
1247 printf ("BBox %f %f %f %f\n", p1.x, p1.y, p2.x, p2.y);
1248 p1.x += page->pixmap->x;
1249 p1.y += page->pixmap->y;
1250 p2.x += page->pixmap->x;
1251 p2.y += page->pixmap->y;
1252 x0 = p1.x;
1253 y0 = p1.y;
1254 x1 = p2.x;
1255 y1 = p2.y;
1256 printf ("BBox %d %d %d %d %d %d\n", x0, y0, x1, y1, oy, page->pageno);
1258 for (span = page->text; span; span = span->next) {
1259 int seen = 0;
1261 /* fz_debugtextspanxml (span); */
1262 for (i = 0; i < span->len; ++i) {
1263 long c;
1265 bx0 = span->text[i].bbox.x0;
1266 bx1 = span->text[i].bbox.x1;
1267 by0 = span->text[i].bbox.y0 + oy;
1268 by1 = span->text[i].bbox.y1 + oy;
1270 if ((bx1 >= x0 && bx0 <= x1 && by1 >= y0 && by0 <= y1)) {
1271 if (!seen) {
1272 rx0 = bx0 - page->pixmap->x;
1273 rx1 = bx1 - page->pixmap->x;
1274 ry0 = by0;
1275 ry1 = by1;
1278 seen = 1;
1279 c = span->text[i].c;
1280 if (c < 256) {
1281 if ((isprint (c) && !isspace (c))) {
1282 if (!rectsel) {
1283 bx0 -= page->pixmap->x;
1284 bx1 -= page->pixmap->x;
1285 glEnable (GL_BLEND);
1286 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1287 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1288 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1289 glRecti (bx0, by0, bx1, by1);
1290 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1291 glDisable (GL_BLEND);
1293 if (isprint (c) || c ==' ') {
1294 rx1 = bx1;
1295 ry1 = by1;
1298 putc (c, stdout);
1300 else {
1301 putc ('?', stdout);
1306 if (rectsel) {
1307 if (seen) {
1308 glEnable (GL_BLEND);
1309 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1310 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1311 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1312 glRecti (rx0, ry0, rx1, ry1);
1313 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1314 glDisable (GL_BLEND);
1318 if (seen && span->eol) {
1319 x0 = page->pixmap->x;
1320 putc ('\n', stdout);
1323 unlock ("ml_gettext");
1325 done:
1326 CAMLreturn (Val_unit);
1329 CAMLprim value ml_getpagewh (value pagedimno_v)
1331 CAMLparam1 (pagedimno_v);
1332 CAMLlocal1 (ret_v);
1333 int pagedimno = Int_val (pagedimno_v);
1335 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1336 Store_double_field (ret_v, 0, state.pagedims[pagedimno].box.x0);
1337 Store_double_field (ret_v, 1, state.pagedims[pagedimno].box.x1);
1338 Store_double_field (ret_v, 2, state.pagedims[pagedimno].box.y0);
1339 Store_double_field (ret_v, 3, state.pagedims[pagedimno].box.y1);
1340 CAMLreturn (ret_v);
1343 CAMLprim value ml_init (value sock_v)
1345 #ifndef _WIN32
1346 int ret;
1347 #endif
1348 CAMLparam1 (sock_v);
1350 state.texcount = 256;
1351 state.sliceheight = 24;
1352 state.texform = GL_RGBA;
1353 state.texty = GL_UNSIGNED_BYTE;
1355 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1356 if (!state.texids) {
1357 err (1, "calloc texids %zu", state.texcount * sizeof (*state.texids));
1360 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1361 if (!state.texowners) {
1362 err (1, "calloc texowners %zu",
1363 state.texcount * sizeof (*state.texowners));
1366 glGenTextures (state.texcount, state.texids);
1368 #ifdef _WIN32
1369 state.sock = Socket_val (sock_v);
1370 #else
1371 state.sock = Int_val (sock_v);
1372 #endif
1374 state.cache = fz_newglyphcache ();
1375 if (!state.cache) {
1376 errx (1, "fz_newglyphcache failed");
1379 #ifdef _WIN32
1380 InitializeCriticalSection (&critsec);
1381 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
1382 if (state.thread == INVALID_HANDLE_VALUE) {
1383 errx (1, "CreateThread failed: %lx", GetLastError ());
1385 #else
1386 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1387 if (ret) {
1388 errx (1, "pthread_create: %s", strerror (errno));
1390 #endif
1392 CAMLreturn (Val_unit);