Fix handling of links where destination is not an array
[llpp.git] / link.c
blob8ffdf92165511a6a2e050bccde5d75db36ca5a22
1 /* lot's of code c&p-ed directly from mupdf */
2 #ifdef _WIN32
3 #define WIN32_LEAN_AND_MEAN
4 #include <windows.h>
5 #include <winsock2.h>
6 #define ssize_t int
7 #define FMT_ss "%d"
8 #ifdef _WIN64
9 #define FMT_s "%i64u"
10 #else
11 #define FMT_s "%u"
12 #endif
13 #pragma warning (disable:4244)
14 #pragma warning (disable:4996)
15 #pragma warning (disable:4995)
16 #endif
18 #ifdef _MSC_VER
19 #include <errno.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdlib.h>
23 static void __declspec (noreturn) err (int exitcode, const char *fmt, ...)
25 va_list ap;
26 int errcode;
28 errcode = errno;
29 va_start (ap, fmt);
30 vfprintf (stderr, fmt, ap);
31 va_end (ap);
32 fprintf (stderr, ": %s\n", strerror (errno));
33 exit (exitcode);
35 static void __declspec (noreturn) errx (int exitcode, const char *fmt, ...)
37 va_list ap;
39 va_start (ap, fmt);
40 vfprintf (stderr, fmt, ap);
41 va_end (ap);
42 fputc ('\n', stderr);
43 exit (exitcode);
45 static void __declspec (noreturn) sockerr (int exitcode, const char *fmt, ...)
47 va_list ap;
49 va_start (ap, fmt);
50 vfprintf (stderr, fmt, ap);
51 va_end (ap);
52 fprintf (stderr, ": wsaerror 0x%x\n", WSAGetLastError ());
53 exit (exitcode);
55 #else
56 #define FMT_ss "%zd"
57 #define FMT_s "%zu"
58 #define _GNU_SOURCE
59 #include <err.h>
60 #define sockerr err
61 #endif
62 #include <regex.h>
63 #include <errno.h>
64 #include <ctype.h>
65 #include <stdio.h>
66 #include <stdarg.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <limits.h>
70 #ifndef _WIN32
71 #include <pthread.h>
72 #include <sys/time.h>
73 #include <sys/types.h>
74 #include <sys/socket.h>
75 #include <sys/select.h>
76 #endif
78 #ifdef __APPLE__
79 #include <OpenGL/gl.h>
80 #else
81 #include <GL/gl.h>
82 #endif
84 #ifndef GL_TEXTURE_RECTANGLE_ARB
85 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
86 #endif
88 #include <caml/fail.h>
89 #include <caml/alloc.h>
90 #include <caml/memory.h>
91 #include <caml/unixsupport.h>
93 #include <fitz.h>
94 #include <mupdf.h>
96 #if 0
97 #define lprintf printf
98 #else
99 #define lprintf(...)
100 #endif
102 #ifdef FT_FREETYPE_H
103 #include FT_FREETYPE_H
104 #endif
106 #define ARSERT(cond) for (;;) { \
107 if (!(cond)) { \
108 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
110 break; \
113 struct slice {
114 int texindex;
115 int w, h;
118 struct pagedim {
119 int pageno;
120 int rotate;
121 fz_rect box;
122 fz_bbox bbox;
123 fz_matrix ctm, ctm1;
126 struct page {
127 int pageno;
128 int slicecount;
129 fz_textspan *text;
130 fz_pixmap *pixmap;
131 pdf_page *drawpage;
132 struct pagedim pagedim;
133 struct page *prev;
134 struct mark {
135 int i;
136 fz_textspan *span;
137 } fmark, lmark;
138 struct slice slices[];
141 #if !defined _WIN32 && !defined __APPLE__
142 #define USE_XSEL
143 #endif
145 struct {
146 int sock;
147 int sliceheight;
148 struct page *pages, *pig;
149 struct pagedim *pagedims;
150 int pagecount;
151 int pagedimcount;
152 pdf_xref *xref;
153 fz_glyphcache *cache;
154 int w, h;
156 int texindex;
157 int texcount;
158 GLuint *texids;
160 GLenum texform;
161 GLenum texty;
163 struct {
164 int w, h;
165 struct slice *slice;
166 } *texowners;
168 int rotate;
170 #ifdef _WIN32
171 HANDLE thread;
172 #else
173 pthread_t thread;
174 #endif
175 FILE *xselpipe;
176 } state;
178 #ifdef _WIN32
179 static CRITICAL_SECTION critsec;
181 static void lock (void *unused)
183 (void) unused;
184 EnterCriticalSection (&critsec);
187 static void unlock (void *unused)
189 (void) unused;
190 LeaveCriticalSection (&critsec);
193 static int trylock (void *unused)
195 return TryEnterCriticalSection (&critsec) == 0;
197 #else
198 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
200 static void lock (const char *cap)
202 int ret = pthread_mutex_lock (&mutex);
203 if (ret) {
204 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
208 static void unlock (const char *cap)
210 int ret = pthread_mutex_unlock (&mutex);
211 if (ret) {
212 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
216 static int trylock (const char *cap)
218 int ret = pthread_mutex_trylock (&mutex);
220 if (ret && ret != EBUSY) {
221 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
223 return ret == EBUSY;
225 #endif
227 static void *parse_pointer (const char *cap, const char *s)
229 int ret;
230 void *ptr;
232 ret = sscanf (s, "%p", &ptr);
233 if (ret != 1) {
234 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
236 return ptr;
239 static int hasdata (int sock)
241 int n;
242 fd_set s;
243 struct timeval tv;
244 FD_ZERO (&s);
245 FD_SET (sock, &s);
246 tv.tv_sec = 0;
247 tv.tv_usec = 0;
248 n = select (sock + 1, &s, NULL, NULL, &tv);
249 if (n == 0) return 0;
250 if (n == 1) return 1;
251 sockerr (1, "hasdata: select error ret=%d", n);
254 static double now (void)
256 struct timeval tv;
258 if (gettimeofday (&tv, NULL)) {
259 err (1, "gettimeofday");
261 return tv.tv_sec + tv.tv_usec*1e-6;
264 static void readdata (int fd, char *p, int size)
266 ssize_t n;
268 n = recv (fd, p, size, 0);
269 if (n - size) {
270 if (!n) errx (1, "EOF while reading");
271 sockerr (1, "recv (req %d, ret " FMT_ss ")", size, n);
275 static void writedata (int fd, char *p, int size)
277 char buf[4];
278 ssize_t n;
280 buf[0] = (size >> 24) & 0xff;
281 buf[1] = (size >> 16) & 0xff;
282 buf[2] = (size >> 8) & 0xff;
283 buf[3] = (size >> 0) & 0xff;
285 n = send (fd, buf, 4, 0);
286 if (n != 4) {
287 if (!n) errx (1, "EOF while writing length");
288 sockerr (1, "send " FMT_ss, n);
291 n = send (fd, p, size, 0);
292 if (n - size) {
293 if (!n) errx (1, "EOF while writing data");
294 sockerr (1, "send (req %d, ret " FMT_ss ")", size, n);
298 static void
299 #ifdef __GNUC__
300 __attribute__ ((format (printf, 2, 3)))
301 #endif
302 printd (int fd, const char *fmt, ...)
304 int len;
305 va_list ap;
306 char buf[200];
308 va_start (ap, fmt);
309 len = vsnprintf (buf, sizeof (buf), fmt, ap);
310 va_end (ap);
311 writedata (fd, buf, len);
314 static void die (fz_error error)
316 fz_catch (error, "aborting");
317 if (state.xref)
318 pdf_freexref (state.xref);
319 exit (1);
322 static void openxref (char *filename)
324 int fd;
325 fz_error error;
326 fz_stream *file;
328 fd = open (filename, O_BINARY | O_RDONLY, 0666);
329 if (fd < 0)
330 die (fz_throw ("cannot open file '%s'", filename));
332 file = fz_openfile (fd);
333 error = pdf_openxrefwithstream (&state.xref, file, NULL);
334 if (error)
335 die (fz_rethrow(error, "cannot open document '%s'", filename));
336 fz_close (file);
338 if (pdf_needspassword (state.xref)) {
339 die (fz_throw ("password protected"));
342 error = pdf_loadpagetree (state.xref);
343 if (error) {
344 die (fz_throw ("cannot load page tree"));
347 state.pagecount = pdf_getpagecount (state.xref);
350 static int readlen (int fd)
352 ssize_t n;
353 char p[4];
355 n = recv (fd, p, 4, 0);
356 if (n != 4) {
357 if (!n) errx (1, "EOF while reading length");
358 sockerr (1, "recv " FMT_ss, n);
361 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
364 static void unlinkpage (struct page *page)
366 int i;
367 struct page *p;
369 for (p = state.pages; p; p = p->prev) {
370 if (p->prev == page) {
371 p->prev = page->prev;
372 break;
375 for (i = 0; i < page->slicecount; ++i) {
376 struct slice *s = &page->slices[i];
377 if (s->texindex != -1) {
378 if (state.texowners[s->texindex].slice == s) {
379 state.texowners[s->texindex].slice = NULL;
380 ARSERT (state.texowners[s->texindex].w == s->w);
381 ARSERT (state.texowners[s->texindex].h >= s->h);
387 static void freepage (struct page *page)
389 fz_droppixmap (page->pixmap);
391 unlinkpage (page);
393 if (page->text) {
394 fz_freetextspan (page->text);
396 if (page->drawpage) {
397 pdf_freepage (page->drawpage);
400 free (page);
403 static void subdivide (struct page *p)
405 int i;
406 int h = p->pixmap->h;
407 int th = MIN (h, state.sliceheight);
409 for (i = 0; i < p->slicecount; ++i) {
410 struct slice *s = &p->slices[i];
411 s->texindex = -1;
412 s->h = MIN (th, h);
413 s->w = p->pixmap->w;
414 h -= th;
418 int compatpdims (struct pagedim *p1, struct pagedim *p2)
420 return p1->rotate == p2->rotate
421 && !memcmp (&p1->bbox, &p2->bbox, sizeof (p1->bbox))
422 && !memcmp (&p1->ctm, &p2->ctm, sizeof (p1->ctm));
425 static void *render (int pageno, int pindex)
427 fz_error error;
428 int slicecount;
429 fz_obj *pageobj;
430 struct page *page = NULL;
431 double start, end;
432 pdf_page *drawpage;
433 fz_device *idev;
434 struct pagedim *pagedim;
436 start = now ();
437 printd (state.sock, "V rendering %d", pageno);
439 pagedim = &state.pagedims[pindex];
440 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
441 + state.sliceheight - 1) / state.sliceheight;
442 slicecount += slicecount == 0;
444 if (state.pig) {
445 if (compatpdims (&state.pig->pagedim, pagedim)) {
446 page = state.pig;
447 if (page->text) {
448 fz_freetextspan (page->text);
449 page->text = NULL;
451 if (page->drawpage) {
452 pdf_freepage (page->drawpage);
453 page->drawpage = NULL;
456 else {
457 freepage (state.pig);
460 if (!page) {
461 page = calloc (sizeof (*page)
462 + (slicecount * sizeof (struct slice)), 1);
463 if (!page) {
464 err (1, "calloc page %d\n", pageno);
466 page->pixmap = fz_newpixmapwithrect (fz_devicergb, pagedim->bbox);
469 page->slicecount = slicecount;
470 page->prev = state.pages;
471 state.pages = page;
473 pageobj = pdf_getpageobject (state.xref, pageno);
474 if (!pageobj)
475 die (fz_throw ("cannot retrieve info from page %d", pageno));
477 error = pdf_loadpage (&drawpage, state.xref, pageobj);
478 if (error)
479 die (error);
481 fz_clearpixmap (page->pixmap, 0xFF);
483 idev = fz_newdrawdevice (state.cache, page->pixmap);
484 if (!idev)
485 die (fz_throw ("fz_newdrawdevice failed"));
486 error = pdf_runpage (state.xref, drawpage, idev, pagedim->ctm);
487 if (error)
488 die (fz_rethrow (error, "pdf_runpage failed"));
489 fz_freedevice (idev);
491 page->drawpage = drawpage;
492 page->pagedim = *pagedim;
493 page->pageno = pageno;
494 subdivide (page);
495 end = now ();
497 pdf_agestore(state.xref->store, 3);
499 printd (state.sock, "V rendering %d took %f sec", pageno, end - start);
500 state.pig = NULL;
501 return page;
504 static void initpdims (void)
506 int pageno;
507 double start, end;
509 start = now ();
510 for (pageno = 0; pageno < state.pagecount; ++pageno) {
511 int rotate;
512 fz_rect box;
513 struct pagedim *p;
514 fz_obj *obj, *pageobj;
516 pageobj = pdf_getpageobject (state.xref, pageno + 1);
518 obj = fz_dictgets (pageobj, "CropBox");
519 if (!fz_isarray (obj)) {
520 obj = fz_dictgets (pageobj, "MediaBox");
521 if (!fz_isarray (obj)) {
522 die (fz_throw ("cannot find page bounds %d (%d Rd)",
523 fz_tonum (pageobj), fz_togen (pageobj)));
526 box = pdf_torect (obj);
528 obj = fz_dictgets (pageobj, "Rotate");
529 if (fz_isint (obj)) {
530 rotate = fz_toint (obj);
532 else {
533 rotate = 0;
535 rotate += state.rotate;
537 p = &state.pagedims[state.pagedimcount - 1];
538 if ((state.pagedimcount == 0)
539 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
540 size_t size;
542 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
543 state.pagedims = realloc (state.pagedims, size);
544 if (!state.pagedims) {
545 err (1, "realloc pagedims to " FMT_s " (%d elems)",
546 size, state.pagedimcount + 1);
548 p = &state.pagedims[state.pagedimcount++];
549 p->rotate = rotate;
550 p->box = box;
551 p->pageno = pageno;
554 end = now ();
555 printd (state.sock, "T Processed %d pages in %f seconds",
556 state.pagecount, end - start);
559 static void layout (void)
561 int pindex;
562 fz_matrix ctm;
563 fz_rect box, box2;
564 double zoom, w;
565 struct pagedim *p = state.pagedims;
567 pindex = 0;
568 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
569 box.x0 = MIN (p->box.x0, p->box.x1);
570 box.y0 = MIN (p->box.y0, p->box.y1);
571 box.x1 = MAX (p->box.x0, p->box.x1);
572 box.y1 = MAX (p->box.y0, p->box.y1);
574 ctm = fz_identity;
575 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
576 ctm = fz_concat (ctm, fz_rotate (p->rotate));
577 box2 = fz_transformrect (ctm, box);
578 w = box2.x1 - box2.x0;
580 zoom = (state.w / w);
581 ctm = fz_identity;
582 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
583 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
584 memcpy (&p->ctm1, &ctm, sizeof (ctm));
585 ctm = fz_concat (ctm, fz_rotate (p->rotate));
586 p->bbox = fz_roundrect (fz_transformrect (ctm, box));
587 memcpy (&p->ctm, &ctm, sizeof (ctm));
590 while (p-- != state.pagedims) {
591 printd (state.sock, "l %d %d %d",
592 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0);
596 static void recurse_outline (pdf_outline *outline, int level)
598 while (outline) {
599 int i;
600 fz_obj *obj;
601 int top = 0;
602 int pageno = -1;
604 if (!outline->link) goto next;
606 obj = outline->link->dest;
607 if (fz_isindirect (obj)) {
608 obj = fz_resolveindirect (obj);
610 if (fz_isarray (obj)) {
611 fz_obj *obj2;
613 obj2 = fz_arrayget (obj, 0);
614 if (fz_isint (obj2)) {
615 pageno = fz_toint (obj2);
617 else {
618 pageno = pdf_findpageobject (state.xref, obj2) - 1;
621 if (fz_arraylen (obj) > 3) {
622 fz_point p;
623 struct pagedim *pagedim = state.pagedims;
625 for (i = 0; i < state.pagedimcount; ++i) {
626 if (state.pagedims[i].pageno > pageno)
627 break;
628 pagedim = &state.pagedims[i];
631 p.x = fz_toint (fz_arrayget (obj, 2));
632 p.y = fz_toint (fz_arrayget (obj, 3));
633 p = fz_transformpoint (pagedim->ctm, p);
634 top = p.y;
638 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
639 printd (state.sock, "o %d %d %d %s",
640 level, pageno, top, outline->title);
641 next:
642 if (outline->child) {
643 recurse_outline (outline->child, level + 1);
645 outline = outline->next;
649 static void process_outline (void)
651 pdf_outline *outline;
653 outline = pdf_loadoutline (state.xref);
654 if (outline) {
655 recurse_outline (outline, 0);
656 pdf_freeoutline (outline);
660 static int comparespans (const void *l, const void *r)
662 fz_textspan const *const*ls = l;
663 fz_textspan const *const*rs = r;
664 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
667 /* wishful thinking function */
668 static void search (regex_t *re, int pageno, int y, int forward)
670 int i, j;
671 int ret;
672 char *p;
673 char buf[256];
674 fz_matrix ctm;
675 fz_error error;
676 fz_obj *pageobj;
677 fz_device *tdev;
678 pdf_page *drawpage;
679 fz_textspan *text, *span, **pspan;
680 struct pagedim *pdim, *pdimprev;
681 int stop = 0;
682 int niters = 0;
683 int nspans;
684 double start, end;
686 start = now ();
687 while (pageno >= 0 && pageno < state.pagecount && !stop) {
688 if (niters++ == 5) {
689 pdf_agestore(state.xref->store, 3);
690 niters = 0;
691 if (hasdata (state.sock)) {
692 printd (state.sock, "T attention requested aborting search at %d",
693 pageno);
694 stop = 1;
696 else {
697 printd (state.sock, "T searching in page %d", pageno);
700 pdimprev = NULL;
701 for (i = 0; i < state.pagedimcount; ++i) {
702 pdim = &state.pagedims[i];
703 if (pdim->pageno == pageno) {
704 goto found;
706 if (pdim->pageno > pageno) {
707 pdim = pdimprev;
708 goto found;
710 pdimprev = pdim;
712 pdim = pdimprev;
713 found:
715 pageobj = pdf_getpageobject (state.xref, pageno + 1);
716 if (!pageobj)
717 die (fz_throw ("cannot retrieve info from page %d", pageno));
719 error = pdf_loadpage (&drawpage, state.xref, pageobj);
720 if (error)
721 die (error);
723 ctm = fz_rotate (pdim->rotate);
725 text = fz_newtextspan ();
726 tdev = fz_newtextdevice (text);
727 error = pdf_runpage (state.xref, drawpage, tdev, pdim->ctm1);
728 if (error) die (error);
729 fz_freedevice (tdev);
731 nspans = 0;
732 for (span = text; span; span = span->next) {
733 nspans++;
735 pspan = malloc (sizeof (void *) * nspans);
736 if (!pspan) {
737 err (1, "malloc span pointers " FMT_s, sizeof (void *) * nspans);
739 for (i = 0, span = text; span; span = span->next, ++i) {
740 pspan[i] = span;
742 qsort (pspan, nspans, sizeof (fz_textspan *), comparespans);
744 j = forward ? 0 : nspans - 1;
745 while (nspans--) {
746 regmatch_t rm;
748 span = pspan[j];
749 j += forward ? 1 : -1;
750 p = buf;
751 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
752 if (forward) {
753 if (span->text[i].bbox.y0 < y + 1) {
754 continue;
757 else {
758 if (span->text[i].bbox.y0 > y - 1) {
759 continue;
762 if (span->text[i].c < 256) {
763 *p++ = span->text[i].c;
765 else {
766 *p++ = '?';
769 if (p == buf) {
770 continue;
772 *p++ = 0;
774 ret = regexec (re, buf, 1, &rm, 0);
775 if (ret) {
776 if (ret != REG_NOMATCH) {
777 size_t size;
778 char errbuf[80];
779 size = regerror (ret, re, errbuf, sizeof (errbuf));
780 printd (state.sock,
781 "T regexec error `%.*s'",
782 (int) size, errbuf);
783 fz_freetextspan (text);
784 pdf_freepage (drawpage);
785 free (pspan);
786 return;
789 else {
790 int xoff, yoff;
791 fz_bbox *sb, *eb;
792 fz_point p1, p2, p3, p4;
794 xoff = -pdim->bbox.x0;
795 yoff = -pdim->bbox.y0;
797 sb = &span->text[rm.rm_so].bbox;
798 eb = &span->text[rm.rm_eo - 1].bbox;
800 p1.x = sb->x0;
801 p1.y = sb->y0;
802 p2.x = eb->x1;
803 p2.y = sb->y0;
804 p3.x = eb->x1;
805 p3.y = eb->y1;
806 p4.x = sb->x0;
807 p4.y = eb->y1;
809 p1 = fz_transformpoint (ctm, p1);
810 p2 = fz_transformpoint (ctm, p2);
811 p3 = fz_transformpoint (ctm, p3);
812 p4 = fz_transformpoint (ctm, p4);
814 if (!stop) {
815 printd (state.sock, "F %d %d %f %f %f %f %f %f %f %f",
816 pageno, 1,
817 p1.x + xoff, p1.y + yoff,
818 p2.x + xoff, p2.y + yoff,
819 p3.x + xoff, p3.y + yoff,
820 p4.x + xoff, p4.y + yoff);
822 printd (state.sock, "T found at %d `%.*s' in %f sec",
823 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
824 now () - start);
826 else {
827 printd (state.sock, "R %d %d %f %f %f %f %f %f %f %f",
828 pageno, 2,
829 p1.x + xoff, p1.y + yoff,
830 p2.x + xoff, p2.y + yoff,
831 p3.x + xoff, p3.y + yoff,
832 p4.x + xoff, p4.y + yoff);
834 stop = 1;
837 if (forward) {
838 pageno += 1;
839 y = 0;
841 else {
842 pageno -= 1;
843 y = INT_MAX;
845 fz_freetextspan (text);
846 pdf_freepage (drawpage);
847 free (pspan);
849 end = now ();
850 if (!stop) {
851 printd (state.sock, "T no matches %f sec", end - start);
853 printd (state.sock, "D");
856 static
857 #ifdef _WIN32
858 DWORD _stdcall
859 #else
860 void *
861 #endif
862 mainloop (void *unused)
864 char *p = NULL;
865 int len, ret, oldlen = 0;
867 for (;;) {
868 len = readlen (state.sock);
869 if (len == 0) {
870 errx (1, "readlen returned 0");
873 if (oldlen < len + 1) {
874 p = realloc (p, len + 1);
875 if (!p) {
876 err (1, "realloc %d failed", len + 1);
878 oldlen = len + 1;
880 readdata (state.sock, p, len);
881 p[len] = 0;
883 if (!strncmp ("open", p, 4)) {
884 fz_obj *obj;
885 char *filename = p + 5;
887 openxref (filename);
888 initpdims ();
890 obj = fz_dictgets (state.xref->trailer, "Info");
891 if (obj) {
892 obj = fz_dictgets (obj, "Title");
893 if (obj) {
894 printd (state.sock, "t %s", pdf_toutf8 (obj));
898 else if (!strncmp ("free", p, 4)) {
899 void *ptr;
901 ret = sscanf (p + 4, " %p", &ptr);
902 if (ret != 1) {
903 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
905 unlinkpage (ptr);
906 state.pig = ptr;
908 else if (!strncmp ("search", p, 6)) {
909 int icase, pageno, y, ret, len2, forward;
910 char *pattern;
911 regex_t re;
913 ret = sscanf (p + 6, " %d %d %d %d,%n",
914 &icase, &pageno, &y, &forward, &len2);
915 if (ret != 4) {
916 errx (1, "malformed search `%s' ret=%d", p, ret);
919 pattern = p + 6 + len2;
920 ret = regcomp (&re, pattern,
921 REG_EXTENDED | (icase ? REG_ICASE : 0));
922 if (ret) {
923 char errbuf[80];
924 size_t size;
926 size = regerror (ret, &re, errbuf, sizeof (errbuf));
927 printd (state.sock, "T regcomp failed `%.*s'",
928 (int) size, errbuf);
930 else {
931 search (&re, pageno, y, forward);
932 regfree (&re);
935 else if (!strncmp ("geometry", p, 8)) {
936 int w, h;
938 printd (state.sock, "c");
939 ret = sscanf (p + 8, " %d %d", &w, &h);
940 if (ret != 2) {
941 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
944 lock ("geometry");
945 state.h = h;
946 if (w != state.w) {
947 int i;
948 state.w = w;
949 for (i = 0; i < state.texcount; ++i) {
950 state.texowners[i].slice = NULL;
953 layout ();
954 process_outline ();
955 unlock ("geometry");
956 printd (state.sock, "C %d", state.pagecount);
958 else if (!strncmp ("rotate", p, 6)) {
959 float rotate;
961 printd (state.sock, "c");
962 ret = sscanf (p + 6, " %f", &rotate);
963 if (ret != 1) {
964 errx (1, "bad rotate line `%.*s' ret=%d", len, p, ret);
966 lock ("rotate");
967 state.rotate = rotate;
968 state.pagedimcount = 0;
969 free (state.pagedims);
970 state.pagedims = NULL;
971 initpdims ();
972 layout ();
973 process_outline ();
974 if (state.pig) {
975 freepage (state.pig);
976 state.pig = NULL;
978 unlock ("rotate");
979 printd (state.sock, "C %d", state.pagecount);
981 else if (!strncmp ("render", p, 6)) {
982 int pageno, pindex, w, h, ret;
983 struct page *page;
985 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
986 if (ret != 4) {
987 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
990 lock ("render");
991 page = render (pageno, pindex);
992 unlock ("render");
994 printd (state.sock, "r %d %d %d %d %p",
995 pageno,
996 state.w,
997 state.h,
998 state.rotate,
999 page);
1001 else if (!strncmp ("interrupt", p, 9)) {
1002 printd (state.sock, "V interrupted");
1004 else {
1005 errx (1, "unknown command %.*s", len, p);
1008 return 0;
1011 static void showsel (struct page *page, int oy)
1013 int ox;
1014 fz_bbox bbox;
1015 fz_textspan *span;
1016 struct mark first, last;
1018 first = page->fmark;
1019 last = page->lmark;
1021 if (!first.span || !last.span) return;
1023 glEnable (GL_BLEND);
1024 glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA);
1025 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1027 ox = -page->pixmap->x;
1028 oy = -page->pixmap->y + oy;
1029 for (span = first.span; span; span = span->next) {
1030 int i, j, k;
1032 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0;
1034 j = 0;
1035 k = span->len - 1;
1037 if (span == page->fmark.span && span == page->lmark.span) {
1038 j = MIN (first.i, last.i);
1039 k = MAX (first.i, last.i);
1041 else if (span == first.span) {
1042 j = first.i;
1044 else if (span == last.span) {
1045 k = last.i;
1048 for (i = j; i <= k; ++i) {
1049 bbox = fz_unionbbox (bbox, span->text[i].bbox);
1051 lprintf ("%d %d %d %d oy=%d ox=%d\n",
1052 bbox.x0,
1053 bbox.y0,
1054 bbox.x1,
1055 bbox.y1,
1056 oy, ox);
1058 glRecti (bbox.x0 + ox, bbox.y0 + oy, bbox.x1 + ox, bbox.y1 + oy);
1060 if (span == last.span) break;
1062 glDisable (GL_BLEND);
1065 static void upload2 (struct page *page, int slicenum, const char *cap)
1067 int i;
1068 int w, h;
1069 double start, end;
1070 struct slice *slice = &page->slices[slicenum];
1072 w = page->pixmap->w;
1073 h = page->pixmap->h;
1075 ARSERT (w == slice->w);
1076 if (slice->texindex != -1
1077 && state.texowners[slice->texindex].slice == slice) {
1078 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1080 else {
1081 int subimage = 0;
1082 int index = (state.texindex++ % state.texcount);
1083 size_t offset = 0;
1085 for (i = 0; i < slicenum; ++i) {
1086 offset += w * page->slices[i].h * 4;
1089 if (state.texowners[index].w == slice->w) {
1090 if (state.texowners[index].h >= slice->h ) {
1091 subimage = 0;
1093 else {
1094 state.texowners[index].h = slice->h;
1097 else {
1098 state.texowners[index].h = slice->h;
1101 state.texowners[index].slice = slice;
1102 state.texowners[index].w = slice->w;
1103 slice->texindex = index;
1105 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1106 start = now ();
1107 if (subimage) {
1109 GLenum err = glGetError ();
1110 if (err != GL_NO_ERROR) {
1111 printf ("\033[0;31mERROR1 %d %d %#x\033[0m\n", w, slice->h, err);
1112 abort ();
1115 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1120 slice->h,
1121 state.texform,
1122 state.texty,
1123 page->pixmap->samples + offset
1126 GLenum err = glGetError ();
1127 if (err != GL_NO_ERROR) {
1128 printf ("\033[0;31mERROR %d %d %#x\033[0m\n", w, slice->h, err);
1129 abort ();
1133 else {
1134 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1136 GL_RGBA8,
1138 slice->h,
1140 state.texform,
1141 state.texty,
1142 page->pixmap->samples + offset
1146 end = now ();
1147 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
1148 subimage ? "sub" : "img",
1149 page->pageno, slicenum,
1150 slice->w, slice->h,
1151 state.texids[slice->texindex],
1152 end - start);
1156 CAMLprim value ml_draw (value dispy_v, value w_v, value h_v,
1157 value py_v, value ptr_v)
1159 CAMLparam5 (dispy_v, w_v, h_v, py_v, ptr_v);
1160 int dispy = Int_val (dispy_v);
1161 int w = Int_val (w_v);
1162 int h = Int_val (h_v);
1163 int py = Int_val (py_v);
1164 char *s = String_val (ptr_v);
1165 int ret;
1166 void *ptr;
1167 struct page *page;
1168 int slicenum = 0;
1170 ret = sscanf (s, "%p", &ptr);
1171 if (ret != 1) {
1172 errx (1, "cannot parse pointer `%s'", s);
1174 page = ptr;
1176 w = page->pixmap->w;
1178 ARSERT (h >= 0 && "ml_draw wrong h");
1179 ARSERT (py <= page->pixmap->h && "ml_draw wrong py");
1181 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1183 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
1184 struct slice *slice = &page->slices[slicenum];
1185 if (slice->h > py) {
1186 break;
1188 py -= slice->h;
1191 h = MIN (state.h, h);
1192 while (h) {
1193 int th;
1194 struct slice *slice = &page->slices[slicenum];
1196 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
1198 th = MIN (h, slice->h - py);
1199 upload2 (page, slicenum, "upload");
1201 glBegin (GL_QUADS);
1203 glTexCoord2i (0, py);
1204 glVertex2i (0, dispy);
1206 glTexCoord2i (w, py);
1207 glVertex2i (w, dispy);
1209 glTexCoord2i (w, py+th);
1210 glVertex2i (w, dispy + th);
1212 glTexCoord2i (0, py+th);
1213 glVertex2i (0, dispy + th);
1215 glEnd ();
1217 h -= th;
1218 py = 0;
1219 dispy += th;
1220 slicenum += 1;
1223 showsel (page, Int_val (dispy_v) - Int_val (py_v));
1224 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1226 CAMLreturn (Val_unit);
1229 static pdf_link *getlink (struct page *page, int x, int y)
1231 fz_point p;
1232 fz_matrix ctm;
1233 pdf_link *link;
1235 p.x = x + page->pixmap->x;
1236 p.y = y + page->pixmap->y;
1238 ctm = fz_invertmatrix (page->pagedim.ctm);
1239 p = fz_transformpoint (ctm, p);
1241 for (link = page->drawpage->links; link; link = link->next) {
1242 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1243 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1244 return link;
1248 return NULL;
1251 CAMLprim value ml_highlightlinks (value ptr_v, value yoff_v)
1253 CAMLparam2 (ptr_v, yoff_v);
1254 pdf_link *link;
1255 struct page *page;
1256 int xoff, yoff = Int_val (yoff_v);
1257 const char *s = String_val (ptr_v);
1259 if (trylock ("ml_highlightlinks")) {
1260 goto done;
1263 page = parse_pointer ("ml_highlightlinks", s);
1265 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1266 glEnable (GL_LINE_STIPPLE);
1267 glLineStipple (0.5, 0xcccc);
1269 xoff = -page->pixmap->x;
1270 yoff -= page->pixmap->y;
1272 glBegin (GL_QUADS);
1273 for (link = page->drawpage->links; link; link = link->next) {
1274 fz_point p1, p2, p3, p4;
1275 fz_matrix ctm = page->pagedim.ctm;
1277 p1.x = link->rect.x0;
1278 p1.y = link->rect.y0;
1280 p2.x = link->rect.x1;
1281 p2.y = link->rect.y0;
1283 p3.x = link->rect.x1;
1284 p3.y = link->rect.y1;
1286 p4.x = link->rect.x0;
1287 p4.y = link->rect.y1;
1289 p1 = fz_transformpoint (ctm, p1);
1290 p2 = fz_transformpoint (ctm, p2);
1291 p3 = fz_transformpoint (ctm, p3);
1292 p4 = fz_transformpoint (ctm, p4);
1294 switch (link->kind) {
1295 case PDF_LGOTO: glColor3ub (255, 0, 0); break;
1296 case PDF_LURI: glColor3ub (0, 0, 255); break;
1297 default: glColor3ub (0, 0, 0); break;
1300 glVertex2f (p1.x + xoff, p1.y + yoff);
1301 glVertex2f (p2.x + xoff, p2.y + yoff);
1302 glVertex2f (p3.x + xoff, p3.y + yoff);
1303 glVertex2f (p4.x + xoff, p4.y + yoff);
1305 glEnd ();
1307 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1308 glDisable (GL_LINE_STIPPLE);
1309 unlock ("ml_highlightlinks");
1311 done:
1312 CAMLreturn (Val_unit);
1315 static void ensuretext (struct page *page)
1317 if (!page->text) {
1318 fz_error error;
1319 fz_device *tdev;
1321 page->text = fz_newtextspan ();
1322 tdev = fz_newtextdevice (page->text);
1323 error = pdf_runpage (state.xref, page->drawpage, tdev,
1324 page->pagedim.ctm);
1325 if (error) die (error);
1326 fz_freedevice (tdev);
1330 CAMLprim value ml_whatsunder (value ptr_v, value x_v, value y_v)
1332 CAMLparam3 (ptr_v, x_v, y_v);
1333 CAMLlocal3 (ret_v, tup_v, str_v);
1334 pdf_link *link;
1335 struct page *page;
1336 char *s = String_val (ptr_v);
1338 ret_v = Val_int (0);
1339 if (trylock ("ml_whatsunder")) {
1340 goto done;
1343 page = parse_pointer ("ml_whatsunder", s);
1344 link = getlink (page, Int_val (x_v), Int_val (y_v));
1345 if (link) {
1346 switch (link->kind) {
1347 case PDF_LGOTO:
1349 int pageno;
1350 fz_point p;
1351 fz_obj *obj;
1353 pageno = -1;
1354 p.x = 0;
1355 p.y = 0;
1357 if (fz_isarray (link->dest)) {
1358 obj = fz_arrayget (link->dest, 0);
1359 if (fz_isindirect (obj)) {
1360 pageno = pdf_findpageobject (state.xref, obj) - 1;
1362 else if (fz_isint (obj)) {
1363 pageno = fz_toint (obj);
1366 if (fz_arraylen (link->dest) > 3) {
1367 fz_obj *xo, *yo;
1369 xo = fz_arrayget (link->dest, 2);
1370 yo = fz_arrayget (link->dest, 3);
1371 if (!fz_isnull (xo) && !fz_isnull (yo)) {
1372 p.x = fz_toint (xo);
1373 p.y = fz_toint (yo);
1374 p = fz_transformpoint (page->pagedim.ctm, p);
1378 else {
1379 pageno = pdf_findpageobject (state.xref, link->dest) - 1;
1381 tup_v = caml_alloc_tuple (2);
1382 ret_v = caml_alloc_small (1, 1);
1383 Field (tup_v, 0) = Val_int (pageno);
1384 Field (tup_v, 1) = Val_int (p.y);
1385 Field (ret_v, 0) = tup_v;
1387 break;
1389 case PDF_LURI:
1390 str_v = caml_copy_string (fz_tostrbuf (link->dest));
1391 ret_v = caml_alloc_small (1, 0);
1392 Field (ret_v, 0) = str_v;
1393 break;
1395 default:
1396 printd (state.sock, "T unhandled link kind %d", link->kind);
1397 break;
1400 else {
1401 int i, x, y;
1402 fz_textspan *span;
1404 ensuretext (page);
1405 x = Int_val (x_v) + page->pixmap->x;
1406 y = Int_val (y_v) + page->pixmap->y;
1408 for (span = page->text; span; span = span->next) {
1409 for (i = 0; i < span->len; ++i) {
1410 fz_bbox *b;
1411 b = &span->text[i].bbox;
1412 if ((x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1)) {
1413 #ifdef FT_FREETYPE_H
1414 FT_FaceRec *face = span->font->ftface;
1415 if (face) {
1416 char *s;
1417 char *n1 = face->family_name;
1418 char *n2 = span->font->name;
1419 size_t l1 = strlen (n1);
1420 size_t l2 = strlen (n2);
1422 if (l1 != l2 || memcmp (n1, n2, l1)) {
1423 s = malloc (l1 + l2 + 2);
1424 if (s) {
1425 memcpy (s, n2, l2);
1426 s[l2] = '=';
1427 memcpy (s + l2 + 1, n1, l1 + 1);
1428 str_v = caml_copy_string (s);
1429 free (s);
1433 if (str_v == 0) {
1434 str_v = caml_copy_string (span->font->name);
1436 #else
1437 str_v = caml_copy_string (span->font->name);
1438 #endif
1439 ret_v = caml_alloc_small (1, 2);
1440 Field (ret_v, 0) = str_v;
1441 goto unlock;
1446 unlock:
1447 unlock ("ml_whatsunder");
1449 done:
1450 CAMLreturn (ret_v);
1453 CAMLprim value ml_seltext (value ptr_v, value rect_v, value oy_v)
1455 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1456 fz_bbox *b;
1457 struct page *page;
1458 fz_textspan *span;
1459 struct mark first, last;
1460 int i, x0, x1, y0, y1, oy;
1461 char *s = String_val (ptr_v);
1463 if (trylock ("ml_seltext")) {
1464 goto done;
1467 page = parse_pointer ("ml_seltext", s);
1468 ensuretext (page);
1470 oy = Int_val (oy_v);
1471 x0 = Int_val (Field (rect_v, 0));
1472 y0 = Int_val (Field (rect_v, 1));
1473 x1 = Int_val (Field (rect_v, 2));
1474 y1 = Int_val (Field (rect_v, 3));
1476 if (0) {
1477 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1478 glColor3ub (128, 128, 128);
1479 glRecti (x0, y0, x1, y1);
1480 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1483 x0 += page->pixmap->x;
1484 y0 += page->pixmap->y - oy;
1485 x1 += page->pixmap->x;
1486 y1 += page->pixmap->y - oy;
1488 first.span = NULL;
1489 last.span = NULL;
1491 last.i = first.i = 0;
1492 first.span = page->text;
1493 for (span = page->text; span; span = span->next) {
1494 for (i = 0; i < span->len; ++i) {
1495 b = &span->text[i].bbox;
1496 if (x0 >= b->x0 && x0 <= b->x1 && y0 >= b->y0 && y0 <= b->y1) {
1497 first.i = i;
1498 first.span = span;
1500 if (x1 >= b->x0 && x1 <= b->x1 && y1 >= b->y0 && y1 <= b->y1) {
1501 last.i = i;
1502 last.span = span;
1507 if (y1 < y0 || x1 < x0) {
1508 int swap = 0;
1510 if (first.span == last.span) {
1511 swap = 1;
1513 else {
1514 if (y1 < y0) {
1515 for (span = first.span; span && span != last.span;
1516 span = span->next) {
1517 if (span->eol) {
1518 swap = 1;
1519 break;
1525 if (swap) {
1526 i = first.i;
1527 span = first.span;
1528 first.i = last.i;
1529 first.span = last.span;
1530 last.i = i;
1531 last.span = span;
1535 page->fmark = first;
1536 page->lmark = last;
1538 unlock ("ml_seltext");
1540 done:
1541 CAMLreturn (Val_unit);
1544 static int pipespan (FILE *f, fz_textspan *span, int a, int b)
1546 char buf[4];
1547 int i, len, ret;
1549 for (i = a; i <= b; ++i) {
1550 len = runetochar (buf, &span->text[i].c);
1551 ret = fwrite (buf, len, 1, f);
1553 if (ret != 1) {
1554 printd (state.sock, "T failed to write %d bytes ret=%d: %s",
1555 len, ret, strerror (errno));
1556 return -1;
1559 return 0;
1562 CAMLprim value ml_copysel (value ptr_v)
1564 CAMLparam1 (ptr_v);
1565 FILE *f;
1566 struct page *page;
1567 char *s = String_val (ptr_v);
1569 if (trylock ("ml_copysel")) {
1570 goto done;
1573 if (!*s) {
1574 close:
1575 #ifdef USE_XSEL
1576 if (state.xselpipe) {
1577 int ret = pclose (state.xselpipe);
1578 if (ret) {
1579 printd (state.sock, "T failed to close xsel pipe `%s'",
1580 strerror (errno));
1582 state.xselpipe = NULL;
1584 #else
1585 printf ("========================================\n");
1586 #endif
1588 else {
1589 fz_textspan *span;
1591 page = parse_pointer ("ml_sopysel", s);
1593 if (!page->fmark.span || !page->lmark.span) {
1594 printd (state.sock, "T nothing to copy");
1595 goto unlock;
1598 f = stdout;
1599 #ifdef USE_XSEL
1600 if (!state.xselpipe) {
1601 state.xselpipe = popen ("xsel -i", "w");
1602 if (!state.xselpipe) {
1603 printd (state.sock, "T failed to open xsel pipe `%s'",
1604 strerror (errno));
1606 else {
1607 f = state.xselpipe;
1610 else {
1611 f = state.xselpipe;
1613 #endif
1615 for (span = page->fmark.span;
1616 span && span != page->lmark.span->next;
1617 span = span->next) {
1618 int a = span == page->fmark.span ? page->fmark.i : 0;
1619 int b = span == page->lmark.span ? page->lmark.i : span->len - 1;
1620 if (pipespan (f, span, a, b)) {
1621 goto close;
1623 if (span->eol) {
1624 if (putc ('\n', f) == EOF) {
1625 printd (state.sock, "T failed break line on xsel pipe `%s'",
1626 strerror (errno));
1627 goto close;
1631 page->lmark.span = NULL;
1632 page->fmark.span = NULL;
1635 unlock:
1636 unlock ("ml_copysel");
1638 done:
1639 CAMLreturn (Val_unit);
1642 CAMLprim value ml_getpagewh (value pagedimno_v)
1644 CAMLparam1 (pagedimno_v);
1645 CAMLlocal1 (ret_v);
1646 int pagedimno = Int_val (pagedimno_v);
1648 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1649 Store_double_field (ret_v, 0, state.pagedims[pagedimno].box.x0);
1650 Store_double_field (ret_v, 1, state.pagedims[pagedimno].box.x1);
1651 Store_double_field (ret_v, 2, state.pagedims[pagedimno].box.y0);
1652 Store_double_field (ret_v, 3, state.pagedims[pagedimno].box.y1);
1653 CAMLreturn (ret_v);
1656 CAMLprim value ml_init (value sock_v)
1658 #ifndef _WIN32
1659 int ret;
1660 #endif
1661 CAMLparam1 (sock_v);
1663 state.texcount = 256;
1664 state.sliceheight = 24;
1665 state.texform = GL_RGBA;
1666 state.texty = GL_UNSIGNED_BYTE;
1668 fz_accelerate ();
1669 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1670 if (!state.texids) {
1671 err (1, "calloc texids " FMT_s,
1672 state.texcount * sizeof (*state.texids));
1675 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1676 if (!state.texowners) {
1677 err (1, "calloc texowners " FMT_s,
1678 state.texcount * sizeof (*state.texowners));
1681 glGenTextures (state.texcount, state.texids);
1683 #ifdef _WIN32
1684 state.sock = Socket_val (sock_v);
1685 #else
1686 state.sock = Int_val (sock_v);
1687 #endif
1689 state.cache = fz_newglyphcache ();
1690 if (!state.cache) {
1691 errx (1, "fz_newglyphcache failed");
1694 #ifdef _WIN32
1695 InitializeCriticalSection (&critsec);
1696 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
1697 if (state.thread == INVALID_HANDLE_VALUE) {
1698 errx (1, "CreateThread failed: %lx", GetLastError ());
1700 #else
1701 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1702 if (ret) {
1703 errx (1, "pthread_create: %s", strerror (errno));
1705 #endif
1707 CAMLreturn (Val_unit);