Windows support
[llpp.git] / link.c
blob6c6c80debfbc3991e05a643591615ac598260042
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 #define ssize_t int
6 #pragma warning (disable:4244)
7 #pragma warning (disable:4996)
8 #pragma warning (disable:4995)
9 #endif
11 #ifdef _MSC_VER
12 #include <errno.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <stdlib.h>
16 static void __declspec (noreturn) err (int exitcode, const char *fmt, ...)
18 va_list ap;
19 int errcode;
21 errcode = errno;
22 va_start (ap, fmt);
23 vfprintf (stderr, fmt, ap);
24 va_end (ap);
25 fprintf (stderr, ": %s\n", strerror (errno));
26 exit (exitcode);
28 static void __declspec (noreturn) errx (int exitcode, const char *fmt, ...)
30 va_list ap;
31 int errcode;
33 errcode = errno;
34 va_start (ap, fmt);
35 vfprintf (stderr, fmt, ap);
36 va_end (ap);
37 fputc ('\n', stderr);
38 exit (exitcode);
40 #else
41 #define _GNU_SOURCE
42 #include <err.h>
43 #endif
44 #include <regex.h>
45 #include <errno.h>
46 #include <ctype.h>
47 #include <stdio.h>
48 #include <stdarg.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #ifndef _WIN32
52 #include <pthread.h>
53 #include <sys/poll.h>
54 #include <sys/time.h>
55 #include <sys/types.h>
56 #include <sys/socket.h>
57 #endif
59 /* fugly as hell and GCC specific but... */
60 #ifdef _BIG_ENDIAN
61 #define GL_GLEXT_PROTOTYPES
62 #endif
64 #include <GL/gl.h>
65 #ifndef _WIN32
66 #include <GL/glext.h>
67 #else
68 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
69 #define GL_FRAGMENT_SHADER_ATI 0x8920
70 #define GL_UNSIGNED_INT_8_8_8_8 0x8035
71 #endif
73 #include <caml/fail.h>
74 #include <caml/alloc.h>
75 #include <caml/memory.h>
76 #include <caml/unixsupport.h>
78 #include <fitz.h>
79 #include <mupdf.h>
81 #if 0
82 #define lprintf printf
83 #else
84 #define lprintf(...)
85 #endif
87 #define ARSERT(cond) for (;;) { \
88 if (!(cond)) { \
89 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
90 } \
91 break; \
94 struct slice {
95 int texindex;
96 int w, h;
99 struct page {
100 int pageno;
101 int slicecount;
102 fz_textspan *text;
103 fz_pixmap *pixmap;
104 pdf_page *drawpage;
105 struct pagedim *pagedim;
106 struct page *prev;
107 struct slice slices[];
110 struct pagedim {
111 int pageno;
112 int rotate;
113 fz_rect box;
114 fz_bbox bbox;
115 fz_matrix ctm;
118 struct {
119 int sock;
120 int sliceheight;
121 struct page *pages;
122 struct pagedim *pagedims;
123 int pagecount;
124 int pagedimcount;
125 pdf_xref *xref;
126 fz_glyphcache *cache;
127 int w, h;
129 int useatifs;
131 int texindex;
132 int texcount;
133 GLuint *texids;
135 GLenum texform;
136 GLenum texty;
138 int lotsamemory;
140 int *pagetbl;
141 struct {
142 int w, h;
143 struct slice *slice;
144 } *texowners;
146 #ifdef _WIN32
147 HANDLE thread;
148 #else
149 pthread_t thread;
150 #endif
151 } state;
153 #ifdef _WIN32
154 static CRITICAL_SECTION critsec;
156 static void lock (void *unused)
158 (void) unused;
159 EnterCriticalSection (&critsec);
162 static void unlock (void *unused)
164 (void) unused;
165 LeaveCriticalSection (&critsec);
168 static int trylock (void *unused)
170 return TryEnterCriticalSection (&critsec) == 0;
172 #else
173 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
175 static void lock (const char *cap)
177 int ret = pthread_mutex_lock (&mutex);
178 if (ret) {
179 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
183 static void unlock (const char *cap)
185 int ret = pthread_mutex_unlock (&mutex);
186 if (ret) {
187 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
191 static int trylock (const char *cap)
193 int ret = pthread_mutex_trylock (&mutex);
195 if (ret && ret != EBUSY) {
196 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
198 return ret == EBUSY;
200 #endif
202 static void *parse_pointer (const char *cap, const char *s)
204 int ret;
205 void *ptr;
207 ret = sscanf (s, "%p", &ptr);
208 if (ret != 1) {
209 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
211 return ptr;
214 #ifdef _WIN32
215 static int hasdata (int sock)
217 return 0;
219 #else
220 static int hasdata (int sock)
222 int ret;
223 struct pollfd pfd;
225 pfd.fd = sock;
226 pfd.events = POLLIN;
227 ret = poll (&pfd, 1, 0);
228 if (ret == 0) {
229 return 0;
231 if (ret != 1) {
232 err (1, "poll");
234 return pfd.revents & POLLIN;
236 #endif
238 static double now (void)
240 struct timeval tv;
242 if (gettimeofday (&tv, NULL)) {
243 err (1, "gettimeofday");
245 return tv.tv_sec + tv.tv_usec*1e-6;
248 static void readdata (int fd, char *p, int size)
250 ssize_t n;
252 n = recv (fd, p, size, 0);
253 if (n - size) {
254 if (!n) errx (1, "EOF while reading");
255 err (1, "recv (req %d, ret %zd)", size, n);
259 static void writedata (int fd, char *p, int size)
261 char buf[4];
262 ssize_t n;
264 buf[0] = (size >> 24) & 0xff;
265 buf[1] = (size >> 16) & 0xff;
266 buf[2] = (size >> 8) & 0xff;
267 buf[3] = (size >> 0) & 0xff;
269 n = send (fd, buf, 4, 0);
270 if (n != 4) {
271 if (!n) errx (1, "EOF while writing length");
272 err (1, "send %zd", n);
275 n = send (fd, p, size, 0);
276 if (n - size) {
277 if (!n) errx (1, "EOF while writing data");
278 err (1, "send (req %d, ret %zd)", size, n);
282 static void
283 #ifdef __GNUC__
284 __attribute__ ((format (printf, 2, 3)))
285 #endif
286 printd (int fd, const char *fmt, ...)
288 int len;
289 va_list ap;
290 char buf[200];
292 va_start (ap, fmt);
293 len = vsnprintf (buf, sizeof (buf), fmt, ap);
294 va_end (ap);
295 writedata (fd, buf, len);
298 static void die (fz_error error)
300 fz_catch (error, "aborting");
301 if (state.xref)
302 pdf_closexref (state.xref);
303 exit (1);
306 static void openxref (char *filename)
308 int fd;
309 fz_error error;
310 fz_stream *file;
312 fd = open (filename, O_BINARY | O_RDONLY, 0666);
313 if (fd < 0)
314 die (fz_throw ("cannot open file '%s'", filename));
316 file = fz_openfile (fd);
317 state.xref = pdf_openxref (file);
318 if (!state.xref)
319 die (fz_throw ("cannot open PDF file '%s'", filename));
320 fz_dropstream (file);
322 if (pdf_needspassword (state.xref)) {
323 die (fz_throw ("password protected"));
326 error = pdf_loadpagetree (state.xref);
327 if (error) {
328 die (fz_throw ("cannot load page tree"));
331 state.pagecount = pdf_getpagecount (state.xref);
332 state.pagetbl = stat_alloc (state.pagecount * sizeof (*state.pagetbl));
335 static int readlen (int fd)
337 ssize_t n;
338 char p[4];
340 n = recv (fd, p, 4, 0);
341 if (n != 4) {
342 if (!n) errx (1, "EOF while reading length");
343 err (1, "read %zd", n);
346 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
349 static void freepage (struct page *page)
351 int i;
352 struct page *p;
354 fz_droppixmap (page->pixmap);
355 for (p = state.pages; p; p = p->prev) {
356 if (p->prev == page) {
357 p->prev = page->prev;
358 break;
361 for (i = 0; i < page->slicecount; ++i) {
362 struct slice *s = &page->slices[i];
363 if (s->texindex != -1) {
364 if (state.texowners[s->texindex].slice == s) {
365 state.texowners[s->texindex].slice = NULL;
366 ARSERT (state.texowners[s->texindex].w == s->w);
367 ARSERT (state.texowners[s->texindex].h >= s->h);
371 if (page->text) {
372 fz_freetextspan (page->text);
374 if (page->drawpage) {
375 pdf_freepage (page->drawpage);
378 free (page);
381 static void subdivide (struct page *p)
383 int i;
384 int h = p->pixmap->h;
385 int th = MIN (h, state.sliceheight);
387 for (i = 0; i < p->slicecount; ++i) {
388 struct slice *s = &p->slices[i];
389 s->texindex = -1;
390 s->h = MIN (th, h);
391 s->w = p->pixmap->w;
392 h -= th;
396 static void *render (int pageno, int pindex)
398 fz_error error;
399 int slicecount;
400 fz_obj *pageobj;
401 struct page *page;
402 double start, end;
403 pdf_page *drawpage;
404 fz_device *idev;
405 struct pagedim *pagedim;
407 start = now ();
408 printd (state.sock, "V rendering %d", pageno);
409 pdf_flushxref (state.xref, 0);
411 pagedim = &state.pagedims[pindex];
412 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
413 + state.sliceheight - 1) / state.sliceheight;
414 slicecount += slicecount == 0;
416 page = calloc (sizeof (*page)
417 + (slicecount * sizeof (struct slice)), 1);
418 if (!page) {
419 err (1, "calloc page %d\n", pageno);
421 page->slicecount = slicecount;
422 page->prev = state.pages;
423 state.pages = page;
425 pageobj = pdf_getpageobject (state.xref, pageno);
426 if (!pageobj)
427 die (fz_throw ("cannot retrieve info from page %d", pageno));
429 error = pdf_loadpage (&drawpage, state.xref, pageobj);
430 if (error)
431 die (error);
433 page->pixmap = fz_newpixmapwithrect (pdf_devicergb, pagedim->bbox);
434 if (error)
435 die (error);
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;
493 state.pagetbl[pageno] = fz_tonum (state.xref->pagerefs[pageno]);
494 p = &state.pagedims[state.pagedimcount - 1];
495 if ((state.pagedimcount == 0)
496 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
497 size_t size;
499 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
500 state.pagedims = realloc (state.pagedims, size);
501 if (!state.pagedims) {
502 err (1, "realloc pagedims to %zu (%d elems)",
503 size, state.pagedimcount + 1);
505 p = &state.pagedims[state.pagedimcount++];
506 p->rotate = rotate;
507 p->box = box;
508 p->pageno = pageno;
511 end = now ();
512 printd (state.sock, "T Processed %d pages in %f seconds",
513 state.pagecount, end - start);
516 static void layout (void)
518 int pindex;
519 fz_matrix ctm;
520 fz_rect box, box2;
521 double zoom, w;
522 struct pagedim *p = state.pagedims;
524 pindex = 0;
525 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
526 box.x0 = MIN (p->box.x0, p->box.x1);
527 box.y0 = MIN (p->box.y0, p->box.y1);
528 box.x1 = MAX (p->box.x0, p->box.x1);
529 box.y1 = MAX (p->box.y0, p->box.y1);
531 ctm = fz_identity ();
532 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
533 ctm = fz_concat (ctm, fz_rotate (p->rotate));
534 box2 = fz_transformrect (ctm, box);
535 w = box2.x1 - box2.x0;
537 zoom = (state.w / w);
538 ctm = fz_identity ();
539 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
540 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
541 ctm = fz_concat (ctm, fz_rotate (p->rotate));
542 p->bbox = fz_roundrect (fz_transformrect (ctm, box));
543 memcpy (&p->ctm, &ctm, sizeof (ctm));
546 while (p-- != state.pagedims) {
547 printd (state.sock, "l %d %d %d",
548 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0);
552 static void recurse_outline (pdf_outline *outline, int level)
554 while (outline) {
555 int i, num;
556 fz_obj *obj;
557 int top = 0;
558 int pageno = -1;
560 if (!outline->link) goto next;
562 obj = outline->link->dest;
563 if (fz_isindirect (obj)) {
564 num = fz_tonum (obj);
566 for (i = 0; i < state.pagecount; ++i) {
567 if (state.pagetbl[i] == num) {
568 pageno = i;
569 break;
573 else if (fz_isarray (obj)) {
574 fz_obj *obj2;
576 obj2 = fz_arrayget (obj, 0);
577 if (fz_isint (obj2)) {
578 pageno = fz_toint (obj2);
580 else {
581 num = fz_tonum (obj2);
582 for (i = 0; i < state.pagecount; ++i) {
583 if (state.pagetbl[i] == num) {
584 pageno = i;
585 break;
590 if (fz_arraylen (obj) > 3) {
591 fz_point p;
592 struct pagedim *pagedim = state.pagedims;
594 for (i = 0; i < state.pagedimcount; ++i) {
595 if (state.pagedims[i].pageno > pageno)
596 break;
597 pagedim = &state.pagedims[i];
600 p.x = fz_toint (fz_arrayget (obj, 2));
601 p.y = fz_toint (fz_arrayget (obj, 3));
602 p = fz_transformpoint (pagedim->ctm, p);
603 top = p.y;
607 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
608 printd (state.sock, "o %d %d %d %s",
609 level, pageno, top, outline->title);
610 next:
611 if (outline->child) {
612 recurse_outline (outline->child, level + 1);
614 outline = outline->next;
618 static void process_outline (void)
620 pdf_outline *outline;
622 outline = pdf_loadoutline (state.xref);
623 if (outline) {
624 recurse_outline (outline, 0);
625 pdf_freeoutline (outline);
629 static int comparespans (const void *l, const void *r)
631 #ifdef _MSC_VER
632 fz_textspan const**ls = l;
633 fz_textspan const**rs = r;
634 #else
635 fz_textspan *const*ls = l;
636 fz_textspan *const*rs = r;
637 #endif
638 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
641 /* wishful thinking function */
642 static void search (regex_t *re, int pageno, int y, int forward)
644 int i, j;
645 int ret;
646 char *p;
647 char buf[256];
648 fz_error error;
649 fz_obj *pageobj;
650 fz_device *tdev;
651 pdf_page *drawpage;
652 fz_textspan *text, *span, **pspan;
653 struct pagedim *pdim, *pdimprev;
654 int stop = 0;
655 int niters = 0;
656 int nspans;
657 double start, end;
659 start = now ();
660 while (pageno >= 0 && pageno < state.pagecount && !stop) {
661 if (niters++ == 5) {
662 if (!state.lotsamemory) {
663 pdf_agestoreditems (state.xref->store);
664 pdf_evictageditems (state.xref->store);
666 niters = 0;
667 if (hasdata (state.sock)) {
668 printd (state.sock, "T attention requested aborting search at %d",
669 pageno);
670 stop = 1;
672 else {
673 printd (state.sock, "T searching in page %d", pageno);
676 pdimprev = NULL;
677 for (i = 0; i < state.pagedimcount; ++i) {
678 pdim = &state.pagedims[i];
679 if (pdim->pageno == pageno) {
680 goto found;
682 if (pdim->pageno > pageno) {
683 pdim = pdimprev;
684 goto found;
686 pdimprev = pdim;
688 pdim = pdimprev;
689 found:
691 pageobj = pdf_getpageobject (state.xref, pageno + 1);
692 if (!pageobj)
693 die (fz_throw ("cannot retrieve info from page %d", pageno));
695 error = pdf_loadpage (&drawpage, state.xref, pageobj);
696 if (error)
697 die (error);
699 text = fz_newtextspan ();
700 tdev = fz_newtextdevice (text);
701 error = pdf_runcontentstream (tdev, pdim->ctm, 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 /* XXX: spans are not sorted "visually" */
728 for (i = 0; i < MIN (span->len, sizeof (buf) - 1); ++i) {
729 if (forward) {
730 if (span->text[i].bbox.y0 < y + 1) {
731 continue;
734 else {
735 if (span->text[i].bbox.y0 > y - 1) {
736 continue;
739 if (span->text[i].c < 256) {
740 *p++ = span->text[i].c;
742 else {
743 *p++ = '?';
746 if (p == buf) {
747 continue;
749 *p++ = 0;
751 ret = regexec (re, buf, 1, &rm, 0);
752 if (ret) {
753 if (ret != REG_NOMATCH) {
754 size_t size;
755 char errbuf[80];
756 size = regerror (ret, re, errbuf, sizeof (errbuf));
757 printd (state.sock,
758 "T regexec error `%.*s'",
759 (int) size, errbuf);
760 fz_freetextspan (text);
761 pdf_freepage (drawpage);
762 free (pspan);
763 return;
766 else {
767 fz_rect r;
769 r.x0 = span->text[rm.rm_so].bbox.x0 - pdim->bbox.x0;
770 r.y0 = span->text[rm.rm_so].bbox.y0;
771 r.x1 = span->text[rm.rm_eo - 1].bbox.x1 - pdim->bbox.x0;
772 r.y1 = span->text[rm.rm_eo - 1].bbox.y1;
774 if (!stop) {
775 printd (state.sock, "F %d %d %f %f %f %f",
776 pageno, 1,
777 r.x0, r.y0,
778 r.x1, r.y1);
780 else {
781 printd (state.sock, "R %d %d %f %f %f %f",
782 pageno, 2,
783 r.x0, r.y0,
784 r.x1, r.y1);
786 printd (state.sock, "T found at %d `%.*s' %f in %f sec",
787 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
788 span->text[0].bbox.y0 - drawpage->mediabox.y0,
789 now () - start);
790 stop = 1;
793 if (forward) {
794 pageno += 1;
795 y = 0;
797 else {
798 pageno -= 1;
799 y = INT_MAX;
801 fz_freetextspan (text);
802 pdf_freepage (drawpage);
803 free (pspan);
805 end = now ();
806 if (!stop) {
807 printd (state.sock, "T no matches %f sec", end - start);
809 printd (state.sock, "D");
812 static
813 #ifdef _WIN32
814 DWORD _stdcall
815 #else
816 void *
817 #endif
818 mainloop (void *unused)
820 char *p = NULL;
821 int len, ret, oldlen = 0;
823 for (;;) {
824 len = readlen (state.sock);
825 if (len == 0) {
826 errx (1, "readlen returned 0");
829 if (oldlen < len + 1) {
830 p = realloc (p, len + 1);
831 if (!p) {
832 err (1, "realloc %d failed", len + 1);
834 oldlen = len + 1;
836 readdata (state.sock, p, len);
837 p[len] = 0;
839 if (!strncmp ("open", p, 4)) {
840 fz_obj *obj;
841 char *filename = p + 5;
843 openxref (filename);
844 initpdims ();
846 obj = fz_dictgets (state.xref->trailer, "Info");
847 if (obj) {
848 obj = fz_dictgets (obj, "Title");
849 if (obj) {
850 printd (state.sock, "t %s", pdf_toutf8 (obj));
854 else if (!strncmp ("free", p, 4)) {
855 void *ptr;
857 ret = sscanf (p + 4, " %p", &ptr);
858 if (ret != 1) {
859 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
861 lock ("free");
862 freepage (ptr);
863 unlock ("free");
864 printd (state.sock, "d");
866 else if (!strncmp ("search", p, 6)) {
867 int icase, pageno, y, ret, len2, forward;
868 char *pattern;
869 regex_t re;
871 ret = sscanf (p + 6, " %d %d %d %d,%n",
872 &icase, &pageno, &y, &forward, &len2);
873 if (ret != 4) {
874 errx (1, "malformed search `%s' ret=%d", p, ret);
877 pattern = p + 6 + len2;
878 ret = regcomp (&re, pattern,
879 REG_EXTENDED | (icase ? REG_ICASE : 0));
880 if (ret) {
881 char errbuf[80];
882 size_t size;
884 size = regerror (ret, &re, errbuf, sizeof (errbuf));
885 printd (state.sock, "T regcomp failed `%.*s'",
886 (int) size, errbuf);
888 else {
889 search (&re, pageno, y, forward);
890 regfree (&re);
893 else if (!strncmp ("geometry", p, 8)) {
894 int w, h;
896 printd (state.sock, "c");
897 ret = sscanf (p + 8, " %d %d", &w, &h);
898 if (ret != 2) {
899 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
901 state.h = h;
902 if (w != state.w) {
903 int i;
904 state.w = w;
905 for (i = 0; i < state.texcount; ++i) {
906 state.texowners[i].slice = NULL;
909 lock ("geometry");
910 layout ();
911 process_outline ();
912 unlock ("geometry");
913 printd (state.sock, "C %d", state.pagecount);
915 else if (!strncmp ("render", p, 6)) {
916 int pageno, pindex, w, h, ret;
917 struct page *page;
919 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
920 if (ret != 4) {
921 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
924 page = render (pageno, pindex);
925 printd (state.sock, "r %d %d %d %p\n",
926 pageno,
927 state.w,
928 state.h,
929 page);
931 else {
932 errx (1, "unknown command %.*s", len, p);
935 return 0;
938 static void upload2 (struct page *page, int slicenum, const char *cap)
940 int i;
941 int w, h;
942 double start, end;
943 struct slice *slice = &page->slices[slicenum];
945 w = page->pixmap->w;
946 h = page->pixmap->h;
948 ARSERT (w == slice->w);
949 if (slice->texindex != -1
950 && state.texowners[slice->texindex].slice == slice) {
951 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
953 else {
954 int subimage = 0;
955 int index = (state.texindex++ % state.texcount);
956 size_t offset = 0;
958 for (i = 0; i < slicenum; ++i) {
959 offset += w * page->slices[i].h * 4;
962 if (state.texowners[index].w == slice->w) {
963 if (state.texowners[index].h >= slice->h ) {
964 subimage = 1;
966 else {
967 state.texowners[index].h = slice->h;
970 else {
971 state.texowners[index].h = slice->h;
974 state.texowners[index].slice = slice;
975 state.texowners[index].w = slice->w;
976 slice->texindex = index;
978 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
979 start = now ();
980 if (subimage) {
982 GLenum err = glGetError ();
983 if (err != GL_NO_ERROR) {
984 printf ("\033[0;31mERROR1 %d %d %#x\033[0m\n", w, slice->h, err);
985 abort ();
988 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
993 slice->h,
994 state.texform,
995 state.texty,
996 page->pixmap->samples + offset
999 GLenum err = glGetError ();
1000 if (err != GL_NO_ERROR) {
1001 printf ("\033[0;31mERROR %d %d %#x\033[0m\n", w, slice->h, err);
1002 abort ();
1006 else {
1007 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1009 GL_RGBA8,
1011 slice->h,
1013 state.texform,
1014 state.texty,
1015 page->pixmap->samples + offset
1019 end = now ();
1020 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
1021 subimage ? "sub" : "img",
1022 page->pageno, slicenum,
1023 slice->w, slice->h,
1024 state.texids[slice->texindex],
1025 end - start);
1029 CAMLprim value ml_draw (value dispy_v, value w_v, value h_v,
1030 value py_v, value ptr_v)
1032 CAMLparam5 (dispy_v, w_v, h_v, py_v, ptr_v);
1033 int dispy = Int_val (dispy_v);
1034 int w = Int_val (w_v);
1035 int h = Int_val (h_v);
1036 int py = Int_val (py_v);
1037 char *s = String_val (ptr_v);
1038 int ret;
1039 void *ptr;
1040 struct page *page;
1041 int slicenum = 0;
1043 if (trylock ("ml_draw")) {
1044 goto done;
1047 ret = sscanf (s, "%p", &ptr);
1048 if (ret != 1) {
1049 errx (1, "cannot parse pointer `%s'", s);
1051 page = ptr;
1053 w = page->pixmap->w;
1055 ARSERT (h >= 0 && "ml_draw wrong h");
1057 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1058 if (state.useatifs) {
1059 glEnable (GL_FRAGMENT_SHADER_ATI);
1062 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
1063 struct slice *slice = &page->slices[slicenum];
1064 if (slice->h > py) {
1065 break;
1067 py -= slice->h;
1070 h = MIN (state.h, h);
1071 while (h) {
1072 int th;
1073 struct slice *slice = &page->slices[slicenum];
1075 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
1077 th = MIN (h, slice->h - py);
1078 upload2 (page, slicenum, "upload");
1080 glBegin (GL_QUADS);
1082 glTexCoord2i (0, py);
1083 glVertex2i (0, dispy);
1085 glTexCoord2i (w, py);
1086 glVertex2i (w, dispy);
1088 glTexCoord2i (w, py+th);
1089 glVertex2i (w, dispy + th);
1091 glTexCoord2i (0, py+th);
1092 glVertex2i (0, dispy + th);
1094 glEnd ();
1096 h -= th;
1097 py = 0;
1098 dispy += th;
1099 slicenum += 1;
1102 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1103 if (state.useatifs) {
1104 glDisable (GL_FRAGMENT_SHADER_ATI);
1107 unlock ("ml_draw");
1108 done:
1109 CAMLreturn (Val_unit);
1112 static pdf_link *getlink (struct page *page, int x, int y)
1114 fz_point p;
1115 fz_matrix ctm;
1116 pdf_link *link;
1118 p.x = x;
1119 p.y = y;
1121 ctm = fz_invertmatrix (page->pagedim->ctm);
1122 p = fz_transformpoint (ctm, p);
1124 for (link = page->drawpage->links; link; link = link->next) {
1125 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1126 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1127 if (link->kind == PDF_LGOTO) {
1128 return link;
1133 return NULL;
1136 CAMLprim value ml_checklink (value ptr_v, value x_v, value y_v)
1138 CAMLparam3 (ptr_v, x_v, y_v);
1139 char *s = String_val (ptr_v);
1140 int ret;
1142 if (trylock ("ml_checklink")) {
1143 ret = 0;
1145 else {
1146 ret = NULL != getlink (parse_pointer ("ml_checklink", s),
1147 Int_val (x_v), Int_val (y_v));
1148 unlock ("ml_checklink");
1150 CAMLreturn (Val_bool (ret));
1153 CAMLprim value ml_getlink (value ptr_v, value x_v, value y_v)
1155 CAMLparam3 (ptr_v, x_v, y_v);
1156 CAMLlocal2 (ret_v, tup_v);
1157 pdf_link *link;
1158 struct page *page;
1159 char *s = String_val (ptr_v);
1161 if (trylock ("ml_getlink")) {
1162 ret_v = Val_int (0);
1163 goto done;
1166 page = parse_pointer ("ml_getlink", s);
1168 link = getlink (page, Int_val (x_v), Int_val (y_v));
1169 if (link) {
1170 int pageno;
1171 fz_point p;
1172 fz_obj *obj;
1174 pageno = -1;
1175 obj = fz_arrayget (link->dest, 0);
1176 if (fz_isindirect (obj)) {
1177 pageno = pdf_findpageobject (state.xref, obj) - 1;
1179 else if (fz_isint (obj)) {
1180 pageno = fz_toint (obj);
1183 if (fz_arraylen (link->dest) > 3) {
1184 p.x = fz_toint (fz_arrayget (link->dest, 2));
1185 p.y = fz_toint (fz_arrayget (link->dest, 3));
1186 p = fz_transformpoint (page->pagedim->ctm, p);
1188 else {
1189 p.x = 0.0;
1190 p.y = 0.0;
1193 tup_v = caml_alloc_tuple (2);
1194 ret_v = caml_alloc_small (1, 1);
1195 Field (tup_v, 0) = Val_int (pageno);
1196 Field (tup_v, 1) = Val_int (p.y);
1197 Field (ret_v, 0) = tup_v;
1199 else {
1200 ret_v = Val_int (0);
1202 unlock ("ml_getlink");
1204 done:
1205 CAMLreturn (ret_v);
1208 CAMLprim value ml_gettext (value ptr_v, value rect_v, value oy_v, value rectsel_v)
1210 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1211 fz_matrix ctm;
1212 fz_point p1, p2;
1213 struct page *page;
1214 fz_textspan *span;
1215 char *s = String_val (ptr_v);
1216 int rectsel = Bool_val (rectsel_v);
1217 int i, bx0, bx1, by0, by1, x0, x1, y0, y1, oy;
1219 /* stop GCC from complaining about uninitialized variables */
1220 #ifdef __GNUC__
1221 int rx0 = rx0, rx1 = rx1, ry0 = ry0, ry1 = ry1;
1222 #else
1223 int rx0, rx1, ry0, ry1;
1224 #endif
1226 if (trylock ("ml_gettext")) {
1227 goto done;
1230 page = parse_pointer ("ml_gettext", s);
1232 oy = Int_val (oy_v);
1233 p1.x = Int_val (Field (rect_v, 0));
1234 p1.y = Int_val (Field (rect_v, 1));
1235 p2.x = Int_val (Field (rect_v, 2));
1236 p2.y = Int_val (Field (rect_v, 3));
1238 if (0) {
1239 glEnable (GL_BLEND);
1240 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1241 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1242 glColor4f (0, 0, 0, 0.2);
1243 glRecti (p1.x, p1.y, p2.x, p2.y);
1244 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1245 glDisable (GL_BLEND);
1248 ctm = page->pagedim->ctm;
1249 if (!page->text) {
1250 fz_error error;
1251 fz_device *tdev;
1253 page->text = fz_newtextspan ();
1254 tdev = fz_newtextdevice (page->text);
1255 error = pdf_runcontentstream (tdev, page->pagedim->ctm, state.xref,
1256 page->drawpage->resources,
1257 page->drawpage->contents);
1258 if (error) die (error);
1259 fz_freedevice (tdev);
1262 printf ("\033c");
1264 printf ("BBox %f %f %f %f\n", p1.x, p1.y, p2.x, p2.y);
1265 p1.x += page->pixmap->x;
1266 p1.y += page->pixmap->y;
1267 p2.x += page->pixmap->x;
1268 p2.y += page->pixmap->y;
1269 x0 = p1.x;
1270 y0 = p1.y;
1271 x1 = p2.x;
1272 y1 = p2.y;
1273 printf ("BBox %d %d %d %d %d %d\n", x0, y0, x1, y1, oy, page->pageno);
1275 for (span = page->text; span; span = span->next) {
1276 int seen = 0;
1278 /* fz_debugtextspanxml (span); */
1279 for (i = 0; i < span->len; ++i) {
1280 long c;
1282 bx0 = span->text[i].bbox.x0;
1283 bx1 = span->text[i].bbox.x1;
1284 by0 = span->text[i].bbox.y0 + oy;
1285 by1 = span->text[i].bbox.y1 + oy;
1287 if ((bx1 >= x0 && bx0 <= x1 && by1 >= y0 && by0 <= y1)) {
1288 if (!seen) {
1289 rx0 = bx0 - page->pixmap->x;
1290 rx1 = bx1 - page->pixmap->x;
1291 ry0 = by0;
1292 ry1 = by1;
1295 seen = 1;
1296 c = span->text[i].c;
1297 if (c < 256) {
1298 if ((isprint (c) && !isspace (c))) {
1299 if (!rectsel) {
1300 bx0 -= page->pixmap->x;
1301 bx1 -= page->pixmap->x;
1302 glEnable (GL_BLEND);
1303 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1304 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1305 glColor4f (0.5, 0.5, 0.0, 0.6);
1306 glRecti (bx0, by0, bx1, by1);
1307 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1308 glDisable (GL_BLEND);
1310 if (isprint (c) || c ==' ') {
1311 rx1 = bx1;
1312 ry1 = by1;
1315 putc (c, stdout);
1317 else {
1318 putc ('?', stdout);
1323 if (rectsel) {
1324 if (seen) {
1325 glEnable (GL_BLEND);
1326 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1327 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1328 glColor4f (0.5, 0.5, 0.0, 0.6);
1329 glRecti (rx0, ry0, rx1, ry1);
1330 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1331 glDisable (GL_BLEND);
1335 if (seen && span->eol) {
1336 x0 = page->pixmap->x;
1337 putc ('\n', stdout);
1340 unlock ("ml_gettext");
1342 done:
1343 CAMLreturn (Val_unit);
1346 CAMLprim value ml_getpagewh (value pagedimno_v)
1348 CAMLparam1 (pagedimno_v);
1349 CAMLlocal1 (ret_v);
1350 int pagedimno = Int_val (pagedimno_v);
1352 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1353 Store_double_field (ret_v, 0, state.pagedims[pagedimno].box.x0);
1354 Store_double_field (ret_v, 1, state.pagedims[pagedimno].box.x1);
1355 Store_double_field (ret_v, 2, state.pagedims[pagedimno].box.y0);
1356 Store_double_field (ret_v, 3, state.pagedims[pagedimno].box.y1);
1357 CAMLreturn (ret_v);
1360 static void initgl (void)
1362 #ifdef _BIG_ENDIAN
1363 if (strstr ((char *) glGetString (GL_EXTENSIONS),
1364 "GL_ATI_fragment_shader")) {
1365 /* Here, with MESA, rv280, powerpc32: BGRA(rev) is slow while
1366 ABGR is fast, so fix things in the shader */
1367 state.texform = GL_ABGR_EXT;
1368 state.texty = GL_UNSIGNED_INT_8_8_8_8;
1370 glBindFragmentShaderATI (1);
1371 glBeginFragmentShaderATI ();
1373 glSampleMapATI (GL_REG_0_ATI, GL_TEXTURE0_ARB, GL_SWIZZLE_STR_ATI);
1375 glColorFragmentOp1ATI (GL_MOV_ATI,
1376 GL_REG_1_ATI, GL_RED_BIT_ATI, GL_NONE,
1377 GL_REG_0_ATI, GL_BLUE, GL_NONE);
1378 glColorFragmentOp1ATI (GL_MOV_ATI,
1379 GL_REG_1_ATI, GL_BLUE_BIT_ATI, GL_NONE,
1380 GL_REG_0_ATI, GL_RED, GL_NONE);
1381 glColorFragmentOp1ATI (
1382 GL_MOV_ATI,
1383 GL_REG_0_ATI, GL_RED_BIT_ATI | GL_BLUE_BIT_ATI, GL_NONE,
1384 GL_REG_1_ATI, GL_NONE, GL_NONE
1387 glEndFragmentShaderATI ();
1388 state.useatifs = 1;
1390 else {
1391 state.texform = GL_BGRA_EXT;
1392 state.texty = GL_UNSIGNED_INT_8_8_8_8_REV;
1394 #else
1395 state.texform = GL_BGRA_EXT;
1396 state.texty = GL_UNSIGNED_INT_8_8_8_8;
1397 #endif
1400 CAMLprim value ml_init (value sock_v)
1402 int ret;
1403 CAMLparam1 (sock_v);
1405 state.texcount = 256;
1406 state.sliceheight = 24;
1408 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1409 if (!state.texids) {
1410 err (1, "calloc texids %zu", state.texcount * sizeof (*state.texids));
1413 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1414 if (!state.texowners) {
1415 err (1, "calloc texowners %zu",
1416 state.texcount * sizeof (*state.texowners));
1419 glGenTextures (state.texcount, state.texids);
1421 #ifdef _WIN32
1422 state.sock = Socket_val (sock_v);
1423 #else
1424 state.sock = Int_val (sock_v);
1425 #endif
1426 initgl ();
1428 state.cache = fz_newglyphcache ();
1429 if (!state.cache) {
1430 errx (1, "fz_newglyphcache failed");
1433 #ifdef _WIN32
1434 InitializeCriticalSection (&critsec);
1435 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
1436 if (state.thread == INVALID_HANDLE_VALUE) {
1437 errx (1, "CreateThread failed: %lx", GetLastError ());
1439 #else
1440 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1441 if (ret) {
1442 errx (1, "pthread_create: %s", strerror (errno));
1444 #endif
1446 CAMLreturn (Val_unit);