pdf_devicergb is now fz_devicergb upstream
[llpp.git] / link.c
blob63562d4e9524904f1ebbb818d65e7f3139591bc9
1 /* lot's of code c&p-ed directly from mupdf */
2 #ifdef _WIN32
3 #define WIN32_LEAN_AND_MEAN
4 #include <windows.h>
5 #include <winsock2.h>
6 #define ssize_t int
7 #pragma warning (disable:4244)
8 #pragma warning (disable:4996)
9 #pragma warning (disable:4995)
10 #endif
12 #ifdef _MSC_VER
13 #include <errno.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <stdlib.h>
17 static void __declspec (noreturn) err (int exitcode, const char *fmt, ...)
19 va_list ap;
20 int errcode;
22 errcode = errno;
23 va_start (ap, fmt);
24 vfprintf (stderr, fmt, ap);
25 va_end (ap);
26 fprintf (stderr, ": %s\n", strerror (errno));
27 exit (exitcode);
29 static void __declspec (noreturn) errx (int exitcode, const char *fmt, ...)
31 va_list ap;
32 int errcode;
34 errcode = errno;
35 va_start (ap, fmt);
36 vfprintf (stderr, fmt, ap);
37 va_end (ap);
38 fputc ('\n', stderr);
39 exit (exitcode);
41 static void __declspec (noreturn) sockerr (int exitcode, const char *fmt, ...)
43 va_list ap;
44 int errcode;
46 errcode = errno;
47 va_start (ap, fmt);
48 vfprintf (stderr, fmt, ap);
49 va_end (ap);
50 fprintf (stderr, ": wsaerror %lx\n", WSAGetLastError ());
51 exit (exitcode);
53 #else
54 #define _GNU_SOURCE
55 #include <err.h>
56 #define sockerr err
57 #endif
58 #include <regex.h>
59 #include <errno.h>
60 #include <ctype.h>
61 #include <stdio.h>
62 #include <stdarg.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <limits.h>
66 #ifndef _WIN32
67 #include <pthread.h>
68 #include <sys/time.h>
69 #include <sys/types.h>
70 #include <sys/socket.h>
71 #include <sys/select.h>
72 #endif
74 #ifdef __APPLE__
75 #include <OpenGL/gl.h>
76 #else
77 #include <GL/gl.h>
78 #endif
80 #ifndef GL_TEXTURE_RECTANGLE_ARB
81 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
82 #endif
84 #include <caml/fail.h>
85 #include <caml/alloc.h>
86 #include <caml/memory.h>
87 #include <caml/unixsupport.h>
89 #include <fitz.h>
90 #include <mupdf.h>
92 #if 0
93 #define lprintf printf
94 #else
95 #define lprintf(...)
96 #endif
98 #define ARSERT(cond) for (;;) { \
99 if (!(cond)) { \
100 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
102 break; \
105 struct slice {
106 int texindex;
107 int w, h;
110 struct page {
111 int pageno;
112 int slicecount;
113 fz_textspan *text;
114 fz_pixmap *pixmap;
115 pdf_page *drawpage;
116 struct pagedim *pagedim;
117 struct page *prev;
118 struct slice slices[];
121 struct pagedim {
122 int pageno;
123 int rotate;
124 fz_rect box;
125 fz_bbox bbox;
126 fz_matrix ctm, ctm1;
129 struct {
130 int sock;
131 int sliceheight;
132 struct page *pages;
133 struct pagedim *pagedims;
134 int pagecount;
135 int pagedimcount;
136 pdf_xref *xref;
137 fz_glyphcache *cache;
138 int w, h;
140 int texindex;
141 int texcount;
142 GLuint *texids;
144 GLenum texform;
145 GLenum texty;
147 int lotsamemory;
149 int *pagetbl;
150 struct {
151 int w, h;
152 struct slice *slice;
153 } *texowners;
155 int rotate;
157 #ifdef _WIN32
158 HANDLE thread;
159 #else
160 pthread_t thread;
161 #endif
162 } state;
164 #ifdef _WIN32
165 static CRITICAL_SECTION critsec;
167 static void lock (void *unused)
169 (void) unused;
170 EnterCriticalSection (&critsec);
173 static void unlock (void *unused)
175 (void) unused;
176 LeaveCriticalSection (&critsec);
179 static int trylock (void *unused)
181 return TryEnterCriticalSection (&critsec) == 0;
183 #else
184 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
186 static void lock (const char *cap)
188 int ret = pthread_mutex_lock (&mutex);
189 if (ret) {
190 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
194 static void unlock (const char *cap)
196 int ret = pthread_mutex_unlock (&mutex);
197 if (ret) {
198 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
202 static int trylock (const char *cap)
204 int ret = pthread_mutex_trylock (&mutex);
206 if (ret && ret != EBUSY) {
207 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
209 return ret == EBUSY;
211 #endif
213 static void *parse_pointer (const char *cap, const char *s)
215 int ret;
216 void *ptr;
218 ret = sscanf (s, "%p", &ptr);
219 if (ret != 1) {
220 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
222 return ptr;
225 static int hasdata (int sock)
227 int n;
228 fd_set s;
229 struct timeval tv;
230 FD_ZERO (&s);
231 FD_SET (sock, &s);
232 tv.tv_sec = 0;
233 tv.tv_usec = 0;
234 n = select (sock + 1, &s, NULL, NULL, &tv);
235 if (n == 0) return 0;
236 if (n == 1) return 1;
237 sockerr (1, "hasdata: select error ret=%d", n);
240 static double now (void)
242 struct timeval tv;
244 if (gettimeofday (&tv, NULL)) {
245 err (1, "gettimeofday");
247 return tv.tv_sec + tv.tv_usec*1e-6;
250 static void readdata (int fd, char *p, int size)
252 ssize_t n;
254 n = recv (fd, p, size, 0);
255 if (n - size) {
256 if (!n) errx (1, "EOF while reading");
257 sockerr (1, "recv (req %d, ret %zd)", size, n);
261 static void writedata (int fd, char *p, int size)
263 char buf[4];
264 ssize_t n;
266 buf[0] = (size >> 24) & 0xff;
267 buf[1] = (size >> 16) & 0xff;
268 buf[2] = (size >> 8) & 0xff;
269 buf[3] = (size >> 0) & 0xff;
271 n = send (fd, buf, 4, 0);
272 if (n != 4) {
273 if (!n) errx (1, "EOF while writing length");
274 sockerr (1, "send %zd", n);
277 n = send (fd, p, size, 0);
278 if (n - size) {
279 if (!n) errx (1, "EOF while writing data");
280 sockerr (1, "send (req %d, ret %zd)", size, n);
284 static void
285 #ifdef __GNUC__
286 __attribute__ ((format (printf, 2, 3)))
287 #endif
288 printd (int fd, const char *fmt, ...)
290 int len;
291 va_list ap;
292 char buf[200];
294 va_start (ap, fmt);
295 len = vsnprintf (buf, sizeof (buf), fmt, ap);
296 va_end (ap);
297 writedata (fd, buf, len);
300 static void die (fz_error error)
302 fz_catch (error, "aborting");
303 if (state.xref)
304 pdf_freexref (state.xref);
305 exit (1);
308 static void openxref (char *filename)
310 int fd;
311 fz_error error;
312 fz_stream *file;
314 fd = open (filename, O_BINARY | O_RDONLY, 0666);
315 if (fd < 0)
316 die (fz_throw ("cannot open file '%s'", filename));
318 file = fz_openfile (fd);
319 error = pdf_openxrefwithstream (&state.xref, file, NULL);
320 if (error)
321 die (fz_rethrow(error, "cannot open document '%s'", filename));
322 fz_dropstream (file);
324 if (pdf_needspassword (state.xref)) {
325 die (fz_throw ("password protected"));
328 error = pdf_loadpagetree (state.xref);
329 if (error) {
330 die (fz_throw ("cannot load page tree"));
333 state.pagecount = pdf_getpagecount (state.xref);
334 state.pagetbl = stat_alloc (state.pagecount * sizeof (*state.pagetbl));
337 static int readlen (int fd)
339 ssize_t n;
340 char p[4];
342 n = recv (fd, p, 4, 0);
343 if (n != 4) {
344 if (!n) errx (1, "EOF while reading length");
345 sockerr (1, "recv %zd", n);
348 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
351 static void freepage (struct page *page)
353 int i;
354 struct page *p;
356 fz_droppixmap (page->pixmap);
357 for (p = state.pages; p; p = p->prev) {
358 if (p->prev == page) {
359 p->prev = page->prev;
360 break;
363 for (i = 0; i < page->slicecount; ++i) {
364 struct slice *s = &page->slices[i];
365 if (s->texindex != -1) {
366 if (state.texowners[s->texindex].slice == s) {
367 state.texowners[s->texindex].slice = NULL;
368 ARSERT (state.texowners[s->texindex].w == s->w);
369 ARSERT (state.texowners[s->texindex].h >= s->h);
373 if (page->text) {
374 fz_freetextspan (page->text);
376 if (page->drawpage) {
377 pdf_freepage (page->drawpage);
380 free (page);
383 static void subdivide (struct page *p)
385 int i;
386 int h = p->pixmap->h;
387 int th = MIN (h, state.sliceheight);
389 for (i = 0; i < p->slicecount; ++i) {
390 struct slice *s = &p->slices[i];
391 s->texindex = -1;
392 s->h = MIN (th, h);
393 s->w = p->pixmap->w;
394 h -= th;
398 static void *render (int pageno, int pindex)
400 fz_error error;
401 int slicecount;
402 fz_obj *pageobj;
403 struct page *page;
404 double start, end;
405 pdf_page *drawpage;
406 fz_device *idev;
407 struct pagedim *pagedim;
409 start = now ();
410 printd (state.sock, "V rendering %d", pageno);
412 pagedim = &state.pagedims[pindex];
413 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
414 + state.sliceheight - 1) / state.sliceheight;
415 slicecount += slicecount == 0;
417 page = calloc (sizeof (*page)
418 + (slicecount * sizeof (struct slice)), 1);
419 if (!page) {
420 err (1, "calloc page %d\n", pageno);
422 page->slicecount = slicecount;
423 page->prev = state.pages;
424 state.pages = page;
426 pageobj = pdf_getpageobject (state.xref, pageno);
427 if (!pageobj)
428 die (fz_throw ("cannot retrieve info from page %d", pageno));
430 error = pdf_loadpage (&drawpage, state.xref, pageobj);
431 if (error)
432 die (error);
434 page->pixmap = fz_newpixmapwithrect (fz_devicergb, pagedim->bbox);
435 fz_clearpixmap (page->pixmap, 0xFF);
437 idev = fz_newdrawdevice (state.cache, page->pixmap);
438 if (!idev)
439 die (fz_throw ("fz_newdrawdevice failed"));
440 error = pdf_runpage (state.xref, drawpage, idev, pagedim->ctm);
441 fz_freedevice (idev);
443 page->drawpage = drawpage;
444 page->pagedim = pagedim;
445 page->pageno = pageno;
446 subdivide (page);
447 end = now ();
449 if (!state.lotsamemory) {
450 pdf_agestore(state.xref->store, 3);
453 printd (state.sock, "V rendering %d took %f sec", pageno, end - start);
454 return page;
457 static void initpdims (void)
459 int pageno;
460 double start, end;
462 start = now ();
463 for (pageno = 0; pageno < state.pagecount; ++pageno) {
464 int rotate;
465 fz_rect box;
466 struct pagedim *p;
467 fz_obj *obj, *pageobj;
469 pageobj = pdf_getpageobject (state.xref, pageno + 1);
471 obj = fz_dictgets (pageobj, "CropBox");
472 if (!fz_isarray (obj)) {
473 obj = fz_dictgets (pageobj, "MediaBox");
474 if (!fz_isarray (obj)) {
475 die (fz_throw ("cannot find page bounds %d (%d Rd)",
476 fz_tonum (pageobj), fz_togen (pageobj)));
479 box = pdf_torect (obj);
481 obj = fz_dictgets (pageobj, "Rotate");
482 if (fz_isint (obj)) {
483 rotate = fz_toint (obj);
485 else {
486 rotate = 0;
488 rotate += state.rotate;
490 state.pagetbl[pageno] = fz_tonum (state.xref->pagerefs[pageno]);
491 p = &state.pagedims[state.pagedimcount - 1];
492 if ((state.pagedimcount == 0)
493 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
494 size_t size;
496 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
497 state.pagedims = realloc (state.pagedims, size);
498 if (!state.pagedims) {
499 err (1, "realloc pagedims to %zu (%d elems)",
500 size, state.pagedimcount + 1);
502 p = &state.pagedims[state.pagedimcount++];
503 p->rotate = rotate;
504 p->box = box;
505 p->pageno = pageno;
508 end = now ();
509 printd (state.sock, "T Processed %d pages in %f seconds",
510 state.pagecount, end - start);
513 static void layout (void)
515 int pindex;
516 fz_matrix ctm;
517 fz_rect box, box2;
518 double zoom, w;
519 struct pagedim *p = state.pagedims;
521 pindex = 0;
522 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
523 box.x0 = MIN (p->box.x0, p->box.x1);
524 box.y0 = MIN (p->box.y0, p->box.y1);
525 box.x1 = MAX (p->box.x0, p->box.x1);
526 box.y1 = MAX (p->box.y0, p->box.y1);
528 ctm = fz_identity;
529 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
530 ctm = fz_concat (ctm, fz_rotate (p->rotate));
531 box2 = fz_transformrect (ctm, box);
532 w = box2.x1 - box2.x0;
534 zoom = (state.w / w);
535 ctm = fz_identity;
536 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
537 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
538 memcpy (&p->ctm1, &ctm, sizeof (ctm));
539 ctm = fz_concat (ctm, fz_rotate (p->rotate));
540 p->bbox = fz_roundrect (fz_transformrect (ctm, box));
541 memcpy (&p->ctm, &ctm, sizeof (ctm));
544 while (p-- != state.pagedims) {
545 printd (state.sock, "l %d %d %d",
546 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0);
550 static void recurse_outline (pdf_outline *outline, int level)
552 while (outline) {
553 int i, num;
554 fz_obj *obj;
555 int top = 0;
556 int pageno = -1;
558 if (!outline->link) goto next;
560 obj = outline->link->dest;
561 if (fz_isindirect (obj)) {
562 num = fz_tonum (obj);
564 for (i = 0; i < state.pagecount; ++i) {
565 if (state.pagetbl[i] == num) {
566 pageno = i;
567 break;
571 else if (fz_isarray (obj)) {
572 fz_obj *obj2;
574 obj2 = fz_arrayget (obj, 0);
575 if (fz_isint (obj2)) {
576 pageno = fz_toint (obj2);
578 else {
579 num = fz_tonum (obj2);
580 for (i = 0; i < state.pagecount; ++i) {
581 if (state.pagetbl[i] == num) {
582 pageno = i;
583 break;
588 if (fz_arraylen (obj) > 3) {
589 fz_point p;
590 struct pagedim *pagedim = state.pagedims;
592 for (i = 0; i < state.pagedimcount; ++i) {
593 if (state.pagedims[i].pageno > pageno)
594 break;
595 pagedim = &state.pagedims[i];
598 p.x = fz_toint (fz_arrayget (obj, 2));
599 p.y = fz_toint (fz_arrayget (obj, 3));
600 p = fz_transformpoint (pagedim->ctm, p);
601 top = p.y;
605 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
606 printd (state.sock, "o %d %d %d %s",
607 level, pageno, top, outline->title);
608 next:
609 if (outline->child) {
610 recurse_outline (outline->child, level + 1);
612 outline = outline->next;
616 static void process_outline (void)
618 pdf_outline *outline;
620 outline = pdf_loadoutline (state.xref);
621 if (outline) {
622 recurse_outline (outline, 0);
623 pdf_freeoutline (outline);
627 static int comparespans (const void *l, const void *r)
629 fz_textspan const *const*ls = l;
630 fz_textspan const *const*rs = r;
631 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
634 /* wishful thinking function */
635 static void search (regex_t *re, int pageno, int y, int forward)
637 int i, j;
638 int ret;
639 char *p;
640 char buf[256];
641 fz_matrix ctm;
642 fz_error error;
643 fz_obj *pageobj;
644 fz_device *tdev;
645 pdf_page *drawpage;
646 fz_textspan *text, *span, **pspan;
647 struct pagedim *pdim, *pdimprev;
648 int stop = 0;
649 int niters = 0;
650 int nspans;
651 double start, end;
653 start = now ();
654 while (pageno >= 0 && pageno < state.pagecount && !stop) {
655 if (niters++ == 5) {
656 if (!state.lotsamemory) {
657 pdf_agestore(state.xref->store, 3);
659 niters = 0;
660 if (hasdata (state.sock)) {
661 printd (state.sock, "T attention requested aborting search at %d",
662 pageno);
663 stop = 1;
665 else {
666 printd (state.sock, "T searching in page %d", pageno);
669 pdimprev = NULL;
670 for (i = 0; i < state.pagedimcount; ++i) {
671 pdim = &state.pagedims[i];
672 if (pdim->pageno == pageno) {
673 goto found;
675 if (pdim->pageno > pageno) {
676 pdim = pdimprev;
677 goto found;
679 pdimprev = pdim;
681 pdim = pdimprev;
682 found:
684 pageobj = pdf_getpageobject (state.xref, pageno + 1);
685 if (!pageobj)
686 die (fz_throw ("cannot retrieve info from page %d", pageno));
688 error = pdf_loadpage (&drawpage, state.xref, pageobj);
689 if (error)
690 die (error);
692 ctm = fz_rotate (pdim->rotate);
694 text = fz_newtextspan ();
695 tdev = fz_newtextdevice (text);
696 error = pdf_runpage (state.xref, drawpage, tdev, pdim->ctm1);
697 if (error) die (error);
698 fz_freedevice (tdev);
700 nspans = 0;
701 for (span = text; span; span = span->next) {
702 nspans++;
704 pspan = malloc (sizeof (void *) * nspans);
705 if (!pspan) {
706 err (1, "malloc span pointers %zu", sizeof (void *) * nspans);
708 for (i = 0, span = text; span; span = span->next, ++i) {
709 pspan[i] = span;
711 qsort (pspan, nspans, sizeof (fz_textspan *), comparespans);
713 j = forward ? 0 : nspans - 1;
714 while (nspans--) {
715 regmatch_t rm;
717 span = pspan[j];
718 j += forward ? 1 : -1;
719 p = buf;
720 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
721 if (forward) {
722 if (span->text[i].bbox.y0 < y + 1) {
723 continue;
726 else {
727 if (span->text[i].bbox.y0 > y - 1) {
728 continue;
731 if (span->text[i].c < 256) {
732 *p++ = span->text[i].c;
734 else {
735 *p++ = '?';
738 if (p == buf) {
739 continue;
741 *p++ = 0;
743 ret = regexec (re, buf, 1, &rm, 0);
744 if (ret) {
745 if (ret != REG_NOMATCH) {
746 size_t size;
747 char errbuf[80];
748 size = regerror (ret, re, errbuf, sizeof (errbuf));
749 printd (state.sock,
750 "T regexec error `%.*s'",
751 (int) size, errbuf);
752 fz_freetextspan (text);
753 pdf_freepage (drawpage);
754 free (pspan);
755 return;
758 else {
759 int xoff, yoff;
760 fz_bbox *sb, *eb;
761 fz_point p1, p2, p3, p4;
763 xoff = -pdim->bbox.x0;
764 yoff = -pdim->bbox.y0;
766 sb = &span->text[rm.rm_so].bbox;
767 eb = &span->text[rm.rm_eo - 1].bbox;
769 p1.x = sb->x0;
770 p1.y = sb->y0;
771 p2.x = eb->x1;
772 p2.y = sb->y0;
773 p3.x = eb->x1;
774 p3.y = eb->y1;
775 p4.x = sb->x0;
776 p4.y = eb->y1;
778 p1 = fz_transformpoint (ctm, p1);
779 p2 = fz_transformpoint (ctm, p2);
780 p3 = fz_transformpoint (ctm, p3);
781 p4 = fz_transformpoint (ctm, p4);
783 if (!stop) {
784 printd (state.sock, "F %d %d %f %f %f %f %f %f %f %f",
785 pageno, 1,
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 printd (state.sock, "T found at %d `%.*s' in %f sec",
792 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
793 now () - start);
795 else {
796 printd (state.sock, "R %d %d %f %f %f %f %f %f %f %f",
797 pageno, 2,
798 p1.x + xoff, p1.y + yoff,
799 p2.x + xoff, p2.y + yoff,
800 p3.x + xoff, p3.y + yoff,
801 p4.x + xoff, p4.y + yoff);
803 stop = 1;
806 if (forward) {
807 pageno += 1;
808 y = 0;
810 else {
811 pageno -= 1;
812 y = INT_MAX;
814 fz_freetextspan (text);
815 pdf_freepage (drawpage);
816 free (pspan);
818 end = now ();
819 if (!stop) {
820 printd (state.sock, "T no matches %f sec", end - start);
822 printd (state.sock, "D");
825 static
826 #ifdef _WIN32
827 DWORD _stdcall
828 #else
829 void *
830 #endif
831 mainloop (void *unused)
833 char *p = NULL;
834 int len, ret, oldlen = 0;
836 for (;;) {
837 len = readlen (state.sock);
838 if (len == 0) {
839 errx (1, "readlen returned 0");
842 if (oldlen < len + 1) {
843 p = realloc (p, len + 1);
844 if (!p) {
845 err (1, "realloc %d failed", len + 1);
847 oldlen = len + 1;
849 readdata (state.sock, p, len);
850 p[len] = 0;
852 if (!strncmp ("open", p, 4)) {
853 fz_obj *obj;
854 char *filename = p + 5;
856 openxref (filename);
857 initpdims ();
859 obj = fz_dictgets (state.xref->trailer, "Info");
860 if (obj) {
861 obj = fz_dictgets (obj, "Title");
862 if (obj) {
863 printd (state.sock, "t %s", pdf_toutf8 (obj));
867 else if (!strncmp ("free", p, 4)) {
868 void *ptr;
870 ret = sscanf (p + 4, " %p", &ptr);
871 if (ret != 1) {
872 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
874 lock ("free");
875 freepage (ptr);
876 unlock ("free");
877 printd (state.sock, "d");
879 else if (!strncmp ("search", p, 6)) {
880 int icase, pageno, y, ret, len2, forward;
881 char *pattern;
882 regex_t re;
884 ret = sscanf (p + 6, " %d %d %d %d,%n",
885 &icase, &pageno, &y, &forward, &len2);
886 if (ret != 4) {
887 errx (1, "malformed search `%s' ret=%d", p, ret);
890 pattern = p + 6 + len2;
891 ret = regcomp (&re, pattern,
892 REG_EXTENDED | (icase ? REG_ICASE : 0));
893 if (ret) {
894 char errbuf[80];
895 size_t size;
897 size = regerror (ret, &re, errbuf, sizeof (errbuf));
898 printd (state.sock, "T regcomp failed `%.*s'",
899 (int) size, errbuf);
901 else {
902 search (&re, pageno, y, forward);
903 regfree (&re);
906 else if (!strncmp ("geometry", p, 8)) {
907 int w, h;
909 printd (state.sock, "c");
910 ret = sscanf (p + 8, " %d %d", &w, &h);
911 if (ret != 2) {
912 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
914 state.h = h;
915 if (w != state.w) {
916 int i;
917 state.w = w;
918 for (i = 0; i < state.texcount; ++i) {
919 state.texowners[i].slice = NULL;
922 lock ("geometry");
923 layout ();
924 process_outline ();
925 unlock ("geometry");
926 printd (state.sock, "C %d", state.pagecount);
928 else if (!strncmp ("rotate", p, 6)) {
929 float rotate;
931 printd (state.sock, "c");
932 ret = sscanf (p + 6, " %f", &rotate);
933 if (ret != 1) {
934 errx (1, "bad rotate line `%.*s' ret=%d", len, p, ret);
936 state.rotate = rotate;
937 lock ("rotate");
938 state.pagedimcount = 0;
939 free (state.pagedims);
940 state.pagedims = NULL;
941 initpdims ();
942 layout ();
943 process_outline ();
944 unlock ("rotate");
945 printd (state.sock, "C %d", state.pagecount);
947 else if (!strncmp ("render", p, 6)) {
948 int pageno, pindex, w, h, ret;
949 struct page *page;
951 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
952 if (ret != 4) {
953 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
956 page = render (pageno, pindex);
957 printd (state.sock, "r %d %d %d %d %p\n",
958 pageno,
959 state.w,
960 state.h,
961 state.rotate,
962 page);
964 else if (!strncmp ("interrupt", p, 8)) {
965 printd (state.sock, "V interrupted");
967 else {
968 errx (1, "unknown command %.*s", len, p);
971 return 0;
974 static void upload2 (struct page *page, int slicenum, const char *cap)
976 int i;
977 int w, h;
978 double start, end;
979 struct slice *slice = &page->slices[slicenum];
981 w = page->pixmap->w;
982 h = page->pixmap->h;
984 ARSERT (w == slice->w);
985 if (slice->texindex != -1
986 && state.texowners[slice->texindex].slice == slice) {
987 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
989 else {
990 int subimage = 0;
991 int index = (state.texindex++ % state.texcount);
992 size_t offset = 0;
994 for (i = 0; i < slicenum; ++i) {
995 offset += w * page->slices[i].h * 4;
998 if (state.texowners[index].w == slice->w) {
999 if (state.texowners[index].h >= slice->h ) {
1000 subimage = 1;
1002 else {
1003 state.texowners[index].h = slice->h;
1006 else {
1007 state.texowners[index].h = slice->h;
1010 state.texowners[index].slice = slice;
1011 state.texowners[index].w = slice->w;
1012 slice->texindex = index;
1014 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1015 start = now ();
1016 if (subimage) {
1018 GLenum err = glGetError ();
1019 if (err != GL_NO_ERROR) {
1020 printf ("\033[0;31mERROR1 %d %d %#x\033[0m\n", w, slice->h, err);
1021 abort ();
1024 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1029 slice->h,
1030 state.texform,
1031 state.texty,
1032 page->pixmap->samples + offset
1035 GLenum err = glGetError ();
1036 if (err != GL_NO_ERROR) {
1037 printf ("\033[0;31mERROR %d %d %#x\033[0m\n", w, slice->h, err);
1038 abort ();
1042 else {
1043 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1045 GL_RGBA8,
1047 slice->h,
1049 state.texform,
1050 state.texty,
1051 page->pixmap->samples + offset
1055 end = now ();
1056 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
1057 subimage ? "sub" : "img",
1058 page->pageno, slicenum,
1059 slice->w, slice->h,
1060 state.texids[slice->texindex],
1061 end - start);
1065 CAMLprim value ml_draw (value dispy_v, value w_v, value h_v,
1066 value py_v, value ptr_v)
1068 CAMLparam5 (dispy_v, w_v, h_v, py_v, ptr_v);
1069 int dispy = Int_val (dispy_v);
1070 int w = Int_val (w_v);
1071 int h = Int_val (h_v);
1072 int py = Int_val (py_v);
1073 char *s = String_val (ptr_v);
1074 int ret;
1075 void *ptr;
1076 struct page *page;
1077 int slicenum = 0;
1079 if (trylock ("ml_draw")) {
1080 goto done;
1083 ret = sscanf (s, "%p", &ptr);
1084 if (ret != 1) {
1085 errx (1, "cannot parse pointer `%s'", s);
1087 page = ptr;
1089 w = page->pixmap->w;
1091 ARSERT (h >= 0 && "ml_draw wrong h");
1092 ARSERT (py <= page->pixmap->h && "ml_draw wrong py");
1094 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1096 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
1097 struct slice *slice = &page->slices[slicenum];
1098 if (slice->h > py) {
1099 break;
1101 py -= slice->h;
1104 h = MIN (state.h, h);
1105 while (h) {
1106 int th;
1107 struct slice *slice = &page->slices[slicenum];
1109 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
1111 th = MIN (h, slice->h - py);
1112 upload2 (page, slicenum, "upload");
1114 glBegin (GL_QUADS);
1116 glTexCoord2i (0, py);
1117 glVertex2i (0, dispy);
1119 glTexCoord2i (w, py);
1120 glVertex2i (w, dispy);
1122 glTexCoord2i (w, py+th);
1123 glVertex2i (w, dispy + th);
1125 glTexCoord2i (0, py+th);
1126 glVertex2i (0, dispy + th);
1128 glEnd ();
1130 h -= th;
1131 py = 0;
1132 dispy += th;
1133 slicenum += 1;
1136 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1138 unlock ("ml_draw");
1139 done:
1140 CAMLreturn (Val_unit);
1143 static pdf_link *getlink (struct page *page, int x, int y)
1145 fz_point p;
1146 fz_matrix ctm;
1147 pdf_link *link;
1149 p.x = x + page->pixmap->x;
1150 p.y = y + page->pixmap->y;
1152 ctm = fz_invertmatrix (page->pagedim->ctm);
1153 p = fz_transformpoint (ctm, p);
1155 for (link = page->drawpage->links; link; link = link->next) {
1156 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1157 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1158 return link;
1162 return NULL;
1165 CAMLprim value ml_highlightlinks (value ptr_v, value yoff_v)
1167 CAMLparam2 (ptr_v, yoff_v);
1168 pdf_link *link;
1169 struct page *page;
1170 int xoff, yoff = Int_val (yoff_v);
1171 const char *s = String_val (ptr_v);
1173 if (trylock ("ml_highlightlinks")) {
1174 goto done;
1177 page = parse_pointer ("ml_highlightlinks", s);
1179 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1180 glEnable (GL_LINE_STIPPLE);
1181 glLineStipple (0.5, 0xcccc);
1183 xoff = -page->pixmap->x;
1184 yoff -= page->pixmap->y;
1186 glBegin (GL_QUADS);
1187 for (link = page->drawpage->links; link; link = link->next) {
1188 fz_point p1, p2, p3, p4;
1189 fz_matrix ctm = page->pagedim->ctm;
1191 p1.x = link->rect.x0;
1192 p1.y = link->rect.y0;
1194 p2.x = link->rect.x1;
1195 p2.y = link->rect.y0;
1197 p3.x = link->rect.x1;
1198 p3.y = link->rect.y1;
1200 p4.x = link->rect.x0;
1201 p4.y = link->rect.y1;
1203 p1 = fz_transformpoint (ctm, p1);
1204 p2 = fz_transformpoint (ctm, p2);
1205 p3 = fz_transformpoint (ctm, p3);
1206 p4 = fz_transformpoint (ctm, p4);
1208 switch (link->kind) {
1209 case PDF_LGOTO: glColor3ub (255, 0, 0); break;
1210 case PDF_LURI: glColor3ub (0, 0, 255); break;
1211 default: glColor3ub (0, 0, 0); break;
1214 glVertex2f (p1.x + xoff, p1.y + yoff);
1215 glVertex2f (p2.x + xoff, p2.y + yoff);
1216 glVertex2f (p3.x + xoff, p3.y + yoff);
1217 glVertex2f (p4.x + xoff, p4.y + yoff);
1219 glEnd ();
1221 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1222 glDisable (GL_LINE_STIPPLE);
1223 unlock ("ml_highlightlinks");
1225 done:
1226 CAMLreturn (Val_unit);
1229 CAMLprim value ml_checklink (value ptr_v, value x_v, value y_v)
1231 CAMLparam3 (ptr_v, x_v, y_v);
1232 char *s = String_val (ptr_v);
1233 int ret;
1235 if (trylock ("ml_checklink")) {
1236 ret = 0;
1238 else {
1239 pdf_link *link;
1241 link = getlink (parse_pointer ("ml_checklink", s),
1242 Int_val (x_v), Int_val (y_v));
1244 if (link == NULL) {
1245 ret = 0;
1247 else {
1248 if (link->kind == PDF_LURI) {
1249 printd (state.sock, "T %s", fz_tostrbuf (link->dest));
1252 unlock ("ml_checklink");
1254 CAMLreturn (Val_bool (ret));
1257 CAMLprim value ml_getlink (value ptr_v, value x_v, value y_v)
1259 CAMLparam3 (ptr_v, x_v, y_v);
1260 CAMLlocal2 (ret_v, tup_v);
1261 pdf_link *link;
1262 struct page *page;
1263 char *s = String_val (ptr_v);
1265 ret_v = Val_int (0);
1266 if (trylock ("ml_getlink")) {
1267 ret_v = Val_int (0);
1268 goto done;
1271 page = parse_pointer ("ml_getlink", s);
1273 link = getlink (page, Int_val (x_v), Int_val (y_v));
1274 if (link) {
1275 switch (link->kind) {
1276 case PDF_LGOTO:
1278 int pageno;
1279 fz_point p;
1280 fz_obj *obj;
1282 pageno = -1;
1283 obj = fz_arrayget (link->dest, 0);
1284 if (fz_isindirect (obj)) {
1285 pageno = pdf_findpageobject (state.xref, obj) - 1;
1287 else if (fz_isint (obj)) {
1288 pageno = fz_toint (obj);
1291 if (fz_arraylen (link->dest) > 3) {
1292 p.x = fz_toint (fz_arrayget (link->dest, 2));
1293 p.y = fz_toint (fz_arrayget (link->dest, 3));
1294 p = fz_transformpoint (page->pagedim->ctm, p);
1296 else {
1297 p.x = 0.0;
1298 p.y = 0.0;
1301 tup_v = caml_alloc_tuple (2);
1302 ret_v = caml_alloc_small (1, 1);
1303 Field (tup_v, 0) = Val_int (pageno);
1304 Field (tup_v, 1) = Val_int (p.y);
1305 Field (ret_v, 0) = tup_v;
1307 break;
1309 case PDF_LURI:
1310 printf ("%s\n", fz_tostrbuf (link->dest));
1311 tup_v = caml_alloc_tuple (2);
1312 ret_v = caml_alloc_small (1, 1);
1313 Field (tup_v, 0) = Val_int (-1);
1314 Field (tup_v, 1) = Val_int (0);
1315 Field (ret_v, 0) = tup_v;
1316 break;
1318 default:
1319 printd (state.sock, "T unhandled link kind %d", link->kind);
1320 break;
1323 unlock ("ml_getlink");
1325 done:
1326 CAMLreturn (ret_v);
1329 CAMLprim value ml_gettext (value ptr_v, value rect_v, value oy_v, value rectsel_v)
1331 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1332 fz_matrix ctm;
1333 fz_point p1, p2;
1334 struct page *page;
1335 fz_textspan *span;
1336 char *s = String_val (ptr_v);
1337 int rectsel = Bool_val (rectsel_v);
1338 int i, bx0, bx1, by0, by1, x0, x1, y0, y1, oy;
1340 /* stop GCC from complaining about uninitialized variables */
1341 #ifdef __GNUC__
1342 int rx0 = rx0, rx1 = rx1, ry0 = ry0, ry1 = ry1;
1343 #else
1344 int rx0, rx1, ry0, ry1;
1345 #endif
1347 if (trylock ("ml_gettext")) {
1348 goto done;
1351 page = parse_pointer ("ml_gettext", s);
1353 oy = Int_val (oy_v);
1354 p1.x = Int_val (Field (rect_v, 0));
1355 p1.y = Int_val (Field (rect_v, 1));
1356 p2.x = Int_val (Field (rect_v, 2));
1357 p2.y = Int_val (Field (rect_v, 3));
1359 if (0) {
1360 glEnable (GL_BLEND);
1361 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1362 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1363 glColor4f (0.0f, 0.0f, 0.0f, 0.2f);
1364 glRecti (p1.x, p1.y, p2.x, p2.y);
1365 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1366 glDisable (GL_BLEND);
1369 ctm = page->pagedim->ctm;
1370 if (!page->text) {
1371 fz_error error;
1372 fz_device *tdev;
1374 page->text = fz_newtextspan ();
1375 tdev = fz_newtextdevice (page->text);
1376 error = pdf_runpage (state.xref, page->drawpage, tdev,
1377 page->pagedim->ctm);
1378 if (error) die (error);
1379 fz_freedevice (tdev);
1382 printf ("\033c");
1384 printf ("BBox %f %f %f %f\n", p1.x, p1.y, p2.x, p2.y);
1385 p1.x += page->pixmap->x;
1386 p1.y += page->pixmap->y;
1387 p2.x += page->pixmap->x;
1388 p2.y += page->pixmap->y;
1389 x0 = p1.x;
1390 y0 = p1.y;
1391 x1 = p2.x;
1392 y1 = p2.y;
1393 printf ("BBox %d %d %d %d %d %d\n", x0, y0, x1, y1, oy, page->pageno);
1395 for (span = page->text; span; span = span->next) {
1396 int seen = 0;
1398 /* fz_debugtextspanxml (span); */
1399 for (i = 0; i < span->len; ++i) {
1400 long c;
1402 bx0 = span->text[i].bbox.x0;
1403 bx1 = span->text[i].bbox.x1;
1404 by0 = span->text[i].bbox.y0 + oy;
1405 by1 = span->text[i].bbox.y1 + oy;
1407 if ((bx1 >= x0 && bx0 <= x1 && by1 >= y0 && by0 <= y1)) {
1408 if (!seen) {
1409 rx0 = bx0 - page->pixmap->x;
1410 rx1 = bx1 - page->pixmap->x;
1411 ry0 = by0;
1412 ry1 = by1;
1415 seen = 1;
1416 c = span->text[i].c;
1417 if (c < 256) {
1418 if ((isprint (c) && !isspace (c))) {
1419 if (!rectsel) {
1420 bx0 -= page->pixmap->x;
1421 bx1 -= page->pixmap->x;
1422 glEnable (GL_BLEND);
1423 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1424 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1425 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1426 glRecti (bx0, by0, bx1, by1);
1427 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1428 glDisable (GL_BLEND);
1430 if (isprint (c) || c ==' ') {
1431 rx1 = bx1;
1432 ry1 = by1;
1435 putc (c, stdout);
1437 else {
1438 putc ('?', stdout);
1443 if (rectsel) {
1444 if (seen) {
1445 glEnable (GL_BLEND);
1446 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1447 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1448 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1449 glRecti (rx0, ry0, rx1, ry1);
1450 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1451 glDisable (GL_BLEND);
1455 if (seen && span->eol) {
1456 x0 = page->pixmap->x;
1457 putc ('\n', stdout);
1460 unlock ("ml_gettext");
1462 done:
1463 CAMLreturn (Val_unit);
1466 CAMLprim value ml_getpagewh (value pagedimno_v)
1468 CAMLparam1 (pagedimno_v);
1469 CAMLlocal1 (ret_v);
1470 int pagedimno = Int_val (pagedimno_v);
1472 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1473 Store_double_field (ret_v, 0, state.pagedims[pagedimno].box.x0);
1474 Store_double_field (ret_v, 1, state.pagedims[pagedimno].box.x1);
1475 Store_double_field (ret_v, 2, state.pagedims[pagedimno].box.y0);
1476 Store_double_field (ret_v, 3, state.pagedims[pagedimno].box.y1);
1477 CAMLreturn (ret_v);
1480 CAMLprim value ml_init (value sock_v)
1482 #ifndef _WIN32
1483 int ret;
1484 #endif
1485 CAMLparam1 (sock_v);
1487 state.texcount = 256;
1488 state.sliceheight = 24;
1489 state.texform = GL_RGBA;
1490 state.texty = GL_UNSIGNED_BYTE;
1492 fz_accelerate ();
1493 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1494 if (!state.texids) {
1495 err (1, "calloc texids %zu", state.texcount * sizeof (*state.texids));
1498 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1499 if (!state.texowners) {
1500 err (1, "calloc texowners %zu",
1501 state.texcount * sizeof (*state.texowners));
1504 glGenTextures (state.texcount, state.texids);
1506 #ifdef _WIN32
1507 state.sock = Socket_val (sock_v);
1508 #else
1509 state.sock = Int_val (sock_v);
1510 #endif
1512 state.cache = fz_newglyphcache ();
1513 if (!state.cache) {
1514 errx (1, "fz_newglyphcache failed");
1517 #ifdef _WIN32
1518 InitializeCriticalSection (&critsec);
1519 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
1520 if (state.thread == INVALID_HANDLE_VALUE) {
1521 errx (1, "CreateThread failed: %lx", GetLastError ());
1523 #else
1524 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1525 if (ret) {
1526 errx (1, "pthread_create: %s", strerror (errno));
1528 #endif
1530 CAMLreturn (Val_unit);