Change the way outlines/bookmarks are stored
[llpp.git] / link.c
blob72e32d792858b0c0331eb22cec9f0bc7454e6649
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;
169 int needoutline;
171 #ifdef _WIN32
172 HANDLE thread;
173 #else
174 pthread_t thread;
175 #endif
176 FILE *xselpipe;
177 } state;
179 #ifdef _WIN32
180 static CRITICAL_SECTION critsec;
182 static void lock (void *unused)
184 (void) unused;
185 EnterCriticalSection (&critsec);
188 static void unlock (void *unused)
190 (void) unused;
191 LeaveCriticalSection (&critsec);
194 static int trylock (void *unused)
196 return TryEnterCriticalSection (&critsec) == 0;
198 #else
199 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
201 static void lock (const char *cap)
203 int ret = pthread_mutex_lock (&mutex);
204 if (ret) {
205 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
209 static void unlock (const char *cap)
211 int ret = pthread_mutex_unlock (&mutex);
212 if (ret) {
213 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
217 static int trylock (const char *cap)
219 int ret = pthread_mutex_trylock (&mutex);
221 if (ret && ret != EBUSY) {
222 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
224 return ret == EBUSY;
226 #endif
228 static void *parse_pointer (const char *cap, const char *s)
230 int ret;
231 void *ptr;
233 ret = sscanf (s, "%p", &ptr);
234 if (ret != 1) {
235 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
237 return ptr;
240 static int hasdata (int sock)
242 int n;
243 fd_set s;
244 struct timeval tv;
245 FD_ZERO (&s);
246 FD_SET (sock, &s);
247 tv.tv_sec = 0;
248 tv.tv_usec = 0;
249 n = select (sock + 1, &s, NULL, NULL, &tv);
250 if (n == 0) return 0;
251 if (n == 1) return 1;
252 sockerr (1, "hasdata: select error ret=%d", n);
255 static double now (void)
257 struct timeval tv;
259 if (gettimeofday (&tv, NULL)) {
260 err (1, "gettimeofday");
262 return tv.tv_sec + tv.tv_usec*1e-6;
265 static void readdata (int fd, char *p, int size)
267 ssize_t n;
269 n = recv (fd, p, size, 0);
270 if (n - size) {
271 if (!n) errx (1, "EOF while reading");
272 sockerr (1, "recv (req %d, ret " FMT_ss ")", size, n);
276 static void writedata (int fd, char *p, int size)
278 char buf[4];
279 ssize_t n;
281 buf[0] = (size >> 24) & 0xff;
282 buf[1] = (size >> 16) & 0xff;
283 buf[2] = (size >> 8) & 0xff;
284 buf[3] = (size >> 0) & 0xff;
286 n = send (fd, buf, 4, 0);
287 if (n != 4) {
288 if (!n) errx (1, "EOF while writing length");
289 sockerr (1, "send " FMT_ss, n);
292 n = send (fd, p, size, 0);
293 if (n - size) {
294 if (!n) errx (1, "EOF while writing data");
295 sockerr (1, "send (req %d, ret " FMT_ss ")", size, n);
299 static void
300 #ifdef __GNUC__
301 __attribute__ ((format (printf, 2, 3)))
302 #endif
303 printd (int fd, const char *fmt, ...)
305 int len;
306 va_list ap;
307 char buf[200];
309 va_start (ap, fmt);
310 len = vsnprintf (buf, sizeof (buf), fmt, ap);
311 va_end (ap);
312 writedata (fd, buf, len);
315 static void die (fz_error error)
317 fz_catch (error, "aborting");
318 if (state.xref)
319 pdf_freexref (state.xref);
320 exit (1);
323 static void openxref (char *filename)
325 int fd;
326 fz_error error;
327 fz_stream *file;
329 fd = open (filename, O_BINARY | O_RDONLY, 0666);
330 if (fd < 0)
331 die (fz_throw ("cannot open file '%s'", filename));
333 file = fz_openfile (fd);
334 error = pdf_openxrefwithstream (&state.xref, file, NULL);
335 if (error)
336 die (fz_rethrow(error, "cannot open document '%s'", filename));
337 fz_close (file);
339 if (pdf_needspassword (state.xref)) {
340 die (fz_throw ("password protected"));
343 error = pdf_loadpagetree (state.xref);
344 if (error) {
345 die (fz_throw ("cannot load page tree"));
348 state.pagecount = pdf_getpagecount (state.xref);
351 static int readlen (int fd)
353 ssize_t n;
354 char p[4];
356 n = recv (fd, p, 4, 0);
357 if (n != 4) {
358 if (!n) errx (1, "EOF while reading length");
359 sockerr (1, "recv " FMT_ss, n);
362 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
365 static void unlinkpage (struct page *page)
367 int i;
368 struct page *p;
370 for (p = state.pages; p; p = p->prev) {
371 if (p->prev == page) {
372 p->prev = page->prev;
373 break;
376 for (i = 0; i < page->slicecount; ++i) {
377 struct slice *s = &page->slices[i];
378 if (s->texindex != -1) {
379 if (state.texowners[s->texindex].slice == s) {
380 state.texowners[s->texindex].slice = NULL;
381 ARSERT (state.texowners[s->texindex].w == s->w);
382 ARSERT (state.texowners[s->texindex].h >= s->h);
388 static void freepage (struct page *page)
390 fz_droppixmap (page->pixmap);
392 unlinkpage (page);
394 if (page->text) {
395 fz_freetextspan (page->text);
397 if (page->drawpage) {
398 pdf_freepage (page->drawpage);
401 free (page);
404 static void subdivide (struct page *p)
406 int i;
407 int h = p->pixmap->h;
408 int th = MIN (h, state.sliceheight);
410 for (i = 0; i < p->slicecount; ++i) {
411 struct slice *s = &p->slices[i];
412 s->texindex = -1;
413 s->h = MIN (th, h);
414 s->w = p->pixmap->w;
415 h -= th;
419 int compatpdims (struct pagedim *p1, struct pagedim *p2)
421 return p1->rotate == p2->rotate
422 && !memcmp (&p1->bbox, &p2->bbox, sizeof (p1->bbox))
423 && !memcmp (&p1->ctm, &p2->ctm, sizeof (p1->ctm));
426 static void *render (int pageno, int pindex)
428 fz_error error;
429 int slicecount;
430 fz_obj *pageobj;
431 struct page *page = NULL;
432 double start, end;
433 pdf_page *drawpage;
434 fz_device *idev;
435 struct pagedim *pagedim;
437 start = now ();
438 printd (state.sock, "V rendering %d", pageno);
440 pagedim = &state.pagedims[pindex];
441 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
442 + state.sliceheight - 1) / state.sliceheight;
443 slicecount += slicecount == 0;
445 if (state.pig) {
446 if (compatpdims (&state.pig->pagedim, pagedim)) {
447 page = state.pig;
448 if (page->text) {
449 fz_freetextspan (page->text);
450 page->text = NULL;
452 if (page->drawpage) {
453 pdf_freepage (page->drawpage);
454 page->drawpage = NULL;
457 else {
458 freepage (state.pig);
461 if (!page) {
462 page = calloc (sizeof (*page)
463 + (slicecount * sizeof (struct slice)), 1);
464 if (!page) {
465 err (1, "calloc page %d\n", pageno);
467 page->pixmap = fz_newpixmapwithrect (fz_devicergb, pagedim->bbox);
470 page->slicecount = slicecount;
471 page->prev = state.pages;
472 state.pages = page;
474 pageobj = pdf_getpageobject (state.xref, pageno);
475 if (!pageobj)
476 die (fz_throw ("cannot retrieve info from page %d", pageno));
478 error = pdf_loadpage (&drawpage, state.xref, pageobj);
479 if (error)
480 die (error);
482 fz_clearpixmap (page->pixmap, 0xFF);
484 idev = fz_newdrawdevice (state.cache, page->pixmap);
485 if (!idev)
486 die (fz_throw ("fz_newdrawdevice failed"));
487 error = pdf_runpage (state.xref, drawpage, idev, pagedim->ctm);
488 if (error)
489 die (fz_rethrow (error, "pdf_runpage failed"));
490 fz_freedevice (idev);
492 page->drawpage = drawpage;
493 page->pagedim = *pagedim;
494 page->pageno = pageno;
495 subdivide (page);
496 end = now ();
498 pdf_agestore(state.xref->store, 3);
500 printd (state.sock, "V rendering %d took %f sec", pageno, end - start);
501 state.pig = NULL;
502 return page;
505 static void initpdims (void)
507 int pageno;
508 double start, end;
510 start = now ();
511 for (pageno = 0; pageno < state.pagecount; ++pageno) {
512 int rotate;
513 fz_rect box;
514 struct pagedim *p;
515 fz_obj *obj, *pageobj;
517 pageobj = pdf_getpageobject (state.xref, pageno + 1);
519 obj = fz_dictgets (pageobj, "CropBox");
520 if (!fz_isarray (obj)) {
521 obj = fz_dictgets (pageobj, "MediaBox");
522 if (!fz_isarray (obj)) {
523 die (fz_throw ("cannot find page bounds %d (%d Rd)",
524 fz_tonum (pageobj), fz_togen (pageobj)));
527 box = pdf_torect (obj);
529 obj = fz_dictgets (pageobj, "Rotate");
530 if (fz_isint (obj)) {
531 rotate = fz_toint (obj);
533 else {
534 rotate = 0;
536 rotate += state.rotate;
538 p = &state.pagedims[state.pagedimcount - 1];
539 if ((state.pagedimcount == 0)
540 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
541 size_t size;
543 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
544 state.pagedims = realloc (state.pagedims, size);
545 if (!state.pagedims) {
546 err (1, "realloc pagedims to " FMT_s " (%d elems)",
547 size, state.pagedimcount + 1);
549 p = &state.pagedims[state.pagedimcount++];
550 p->rotate = rotate;
551 p->box = box;
552 p->pageno = pageno;
555 end = now ();
556 printd (state.sock, "T Processed %d pages in %f seconds",
557 state.pagecount, end - start);
560 static void layout (void)
562 int pindex;
563 fz_matrix ctm;
564 fz_rect box, box2;
565 double zoom, w;
566 struct pagedim *p = state.pagedims;
568 pindex = 0;
569 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
570 box.x0 = MIN (p->box.x0, p->box.x1);
571 box.y0 = MIN (p->box.y0, p->box.y1);
572 box.x1 = MAX (p->box.x0, p->box.x1);
573 box.y1 = MAX (p->box.y0, p->box.y1);
575 ctm = fz_identity;
576 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
577 ctm = fz_concat (ctm, fz_rotate (p->rotate));
578 box2 = fz_transformrect (ctm, box);
579 w = box2.x1 - box2.x0;
581 zoom = (state.w / w);
582 ctm = fz_identity;
583 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
584 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
585 memcpy (&p->ctm1, &ctm, sizeof (ctm));
586 ctm = fz_concat (ctm, fz_rotate (p->rotate));
587 p->bbox = fz_roundrect (fz_transformrect (ctm, box));
588 memcpy (&p->ctm, &ctm, sizeof (ctm));
591 while (p-- != state.pagedims) {
592 printd (state.sock, "l %d %d %d",
593 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0);
597 static void recurse_outline (pdf_outline *outline, int level)
599 while (outline) {
600 int i;
601 fz_obj *obj;
602 int pageno = -1;
603 int top = 0, h = 0;
605 if (!outline->link) goto next;
607 obj = outline->link->dest;
608 if (fz_isindirect (obj)) {
609 obj = fz_resolveindirect (obj);
611 if (fz_isarray (obj)) {
612 fz_obj *obj2;
614 obj2 = fz_arrayget (obj, 0);
615 if (fz_isint (obj2)) {
616 pageno = fz_toint (obj2);
618 else {
619 pageno = pdf_findpageobject (state.xref, obj2) - 1;
622 if (fz_arraylen (obj) > 3) {
623 fz_point p;
624 struct pagedim *pagedim = state.pagedims;
626 for (i = 0; i < state.pagedimcount; ++i) {
627 if (state.pagedims[i].pageno > pageno)
628 break;
629 pagedim = &state.pagedims[i];
632 p.x = fz_toint (fz_arrayget (obj, 2));
633 p.y = fz_toint (fz_arrayget (obj, 3));
634 p = fz_transformpoint (pagedim->ctm, p);
635 top = p.y;
636 h = pagedim->bbox.y1 - pagedim->bbox.y0;
639 else {
640 pageno = pdf_findpageobject (state.xref, outline->link->dest) - 1;
643 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
644 printd (state.sock, "o %d %d %d %d %s",
645 level, pageno, top, h, outline->title);
646 next:
647 if (outline->child) {
648 recurse_outline (outline->child, level + 1);
650 outline = outline->next;
654 static void process_outline (void)
656 pdf_outline *outline;
658 if (!state.needoutline) return;
660 state.needoutline = 0;
661 outline = pdf_loadoutline (state.xref);
662 if (outline) {
663 recurse_outline (outline, 0);
664 pdf_freeoutline (outline);
668 static int comparespans (const void *l, const void *r)
670 fz_textspan const *const*ls = l;
671 fz_textspan const *const*rs = r;
672 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
675 /* wishful thinking function */
676 static void search (regex_t *re, int pageno, int y, int forward)
678 int i, j;
679 int ret;
680 char *p;
681 char buf[256];
682 fz_matrix ctm;
683 fz_error error;
684 fz_obj *pageobj;
685 fz_device *tdev;
686 pdf_page *drawpage;
687 fz_textspan *text, *span, **pspan;
688 struct pagedim *pdim, *pdimprev;
689 int stop = 0;
690 int niters = 0;
691 int nspans;
692 double start, end;
694 start = now ();
695 while (pageno >= 0 && pageno < state.pagecount && !stop) {
696 if (niters++ == 5) {
697 pdf_agestore(state.xref->store, 3);
698 niters = 0;
699 if (hasdata (state.sock)) {
700 printd (state.sock, "T attention requested aborting search at %d",
701 pageno);
702 stop = 1;
704 else {
705 printd (state.sock, "T searching in page %d", pageno);
708 pdimprev = NULL;
709 for (i = 0; i < state.pagedimcount; ++i) {
710 pdim = &state.pagedims[i];
711 if (pdim->pageno == pageno) {
712 goto found;
714 if (pdim->pageno > pageno) {
715 pdim = pdimprev;
716 goto found;
718 pdimprev = pdim;
720 pdim = pdimprev;
721 found:
723 pageobj = pdf_getpageobject (state.xref, pageno + 1);
724 if (!pageobj)
725 die (fz_throw ("cannot retrieve info from page %d", pageno));
727 error = pdf_loadpage (&drawpage, state.xref, pageobj);
728 if (error)
729 die (error);
731 ctm = fz_rotate (pdim->rotate);
733 text = fz_newtextspan ();
734 tdev = fz_newtextdevice (text);
735 error = pdf_runpage (state.xref, drawpage, tdev, pdim->ctm1);
736 if (error) die (error);
737 fz_freedevice (tdev);
739 nspans = 0;
740 for (span = text; span; span = span->next) {
741 nspans++;
743 pspan = malloc (sizeof (void *) * nspans);
744 if (!pspan) {
745 err (1, "malloc span pointers " FMT_s, sizeof (void *) * nspans);
747 for (i = 0, span = text; span; span = span->next, ++i) {
748 pspan[i] = span;
750 qsort (pspan, nspans, sizeof (fz_textspan *), comparespans);
752 j = forward ? 0 : nspans - 1;
753 while (nspans--) {
754 regmatch_t rm;
756 span = pspan[j];
757 j += forward ? 1 : -1;
758 p = buf;
759 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
760 if (forward) {
761 if (span->text[i].bbox.y0 < y + 1) {
762 continue;
765 else {
766 if (span->text[i].bbox.y0 > y - 1) {
767 continue;
770 if (span->text[i].c < 256) {
771 *p++ = span->text[i].c;
773 else {
774 *p++ = '?';
777 if (p == buf) {
778 continue;
780 *p++ = 0;
782 ret = regexec (re, buf, 1, &rm, 0);
783 if (ret) {
784 if (ret != REG_NOMATCH) {
785 size_t size;
786 char errbuf[80];
787 size = regerror (ret, re, errbuf, sizeof (errbuf));
788 printd (state.sock,
789 "T regexec error `%.*s'",
790 (int) size, errbuf);
791 fz_freetextspan (text);
792 pdf_freepage (drawpage);
793 free (pspan);
794 return;
797 else {
798 int xoff, yoff;
799 fz_bbox *sb, *eb;
800 fz_point p1, p2, p3, p4;
802 xoff = -pdim->bbox.x0;
803 yoff = -pdim->bbox.y0;
805 sb = &span->text[rm.rm_so].bbox;
806 eb = &span->text[rm.rm_eo - 1].bbox;
808 p1.x = sb->x0;
809 p1.y = sb->y0;
810 p2.x = eb->x1;
811 p2.y = sb->y0;
812 p3.x = eb->x1;
813 p3.y = eb->y1;
814 p4.x = sb->x0;
815 p4.y = eb->y1;
817 p1 = fz_transformpoint (ctm, p1);
818 p2 = fz_transformpoint (ctm, p2);
819 p3 = fz_transformpoint (ctm, p3);
820 p4 = fz_transformpoint (ctm, p4);
822 if (!stop) {
823 printd (state.sock, "F %d %d %f %f %f %f %f %f %f %f",
824 pageno, 1,
825 p1.x + xoff, p1.y + yoff,
826 p2.x + xoff, p2.y + yoff,
827 p3.x + xoff, p3.y + yoff,
828 p4.x + xoff, p4.y + yoff);
830 printd (state.sock, "T found at %d `%.*s' in %f sec",
831 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
832 now () - start);
834 else {
835 printd (state.sock, "R %d %d %f %f %f %f %f %f %f %f",
836 pageno, 2,
837 p1.x + xoff, p1.y + yoff,
838 p2.x + xoff, p2.y + yoff,
839 p3.x + xoff, p3.y + yoff,
840 p4.x + xoff, p4.y + yoff);
842 stop = 1;
845 if (forward) {
846 pageno += 1;
847 y = 0;
849 else {
850 pageno -= 1;
851 y = INT_MAX;
853 fz_freetextspan (text);
854 pdf_freepage (drawpage);
855 free (pspan);
857 end = now ();
858 if (!stop) {
859 printd (state.sock, "T no matches %f sec", end - start);
861 printd (state.sock, "D");
864 static
865 #ifdef _WIN32
866 DWORD _stdcall
867 #else
868 void *
869 #endif
870 mainloop (void *unused)
872 char *p = NULL;
873 int len, ret, oldlen = 0;
875 for (;;) {
876 len = readlen (state.sock);
877 if (len == 0) {
878 errx (1, "readlen returned 0");
881 if (oldlen < len + 1) {
882 p = realloc (p, len + 1);
883 if (!p) {
884 err (1, "realloc %d failed", len + 1);
886 oldlen = len + 1;
888 readdata (state.sock, p, len);
889 p[len] = 0;
891 if (!strncmp ("open", p, 4)) {
892 fz_obj *obj;
893 char *filename = p + 5;
895 openxref (filename);
896 initpdims ();
898 obj = fz_dictgets (state.xref->trailer, "Info");
899 if (obj) {
900 obj = fz_dictgets (obj, "Title");
901 if (obj) {
902 printd (state.sock, "t %s", pdf_toutf8 (obj));
905 state.needoutline = 1;
907 else if (!strncmp ("free", p, 4)) {
908 void *ptr;
910 ret = sscanf (p + 4, " %p", &ptr);
911 if (ret != 1) {
912 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
914 unlinkpage (ptr);
915 state.pig = ptr;
917 else if (!strncmp ("search", p, 6)) {
918 int icase, pageno, y, ret, len2, forward;
919 char *pattern;
920 regex_t re;
922 ret = sscanf (p + 6, " %d %d %d %d,%n",
923 &icase, &pageno, &y, &forward, &len2);
924 if (ret != 4) {
925 errx (1, "malformed search `%s' ret=%d", p, ret);
928 pattern = p + 6 + len2;
929 ret = regcomp (&re, pattern,
930 REG_EXTENDED | (icase ? REG_ICASE : 0));
931 if (ret) {
932 char errbuf[80];
933 size_t size;
935 size = regerror (ret, &re, errbuf, sizeof (errbuf));
936 printd (state.sock, "T regcomp failed `%.*s'",
937 (int) size, errbuf);
939 else {
940 search (&re, pageno, y, forward);
941 regfree (&re);
944 else if (!strncmp ("geometry", p, 8)) {
945 int w, h;
947 printd (state.sock, "c");
948 ret = sscanf (p + 8, " %d %d", &w, &h);
949 if (ret != 2) {
950 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
953 lock ("geometry");
954 state.h = h;
955 if (w != state.w) {
956 int i;
957 state.w = w;
958 for (i = 0; i < state.texcount; ++i) {
959 state.texowners[i].slice = NULL;
962 layout ();
963 process_outline ();
964 unlock ("geometry");
965 printd (state.sock, "C %d", state.pagecount);
967 else if (!strncmp ("rotate", p, 6)) {
968 float rotate;
970 printd (state.sock, "c");
971 ret = sscanf (p + 6, " %f", &rotate);
972 if (ret != 1) {
973 errx (1, "bad rotate line `%.*s' ret=%d", len, p, ret);
975 lock ("rotate");
976 state.rotate = rotate;
977 state.pagedimcount = 0;
978 free (state.pagedims);
979 state.pagedims = NULL;
980 initpdims ();
981 layout ();
982 process_outline ();
983 if (state.pig) {
984 freepage (state.pig);
985 state.pig = NULL;
987 unlock ("rotate");
988 printd (state.sock, "C %d", state.pagecount);
990 else if (!strncmp ("render", p, 6)) {
991 int pageno, pindex, w, h, ret;
992 struct page *page;
994 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
995 if (ret != 4) {
996 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
999 lock ("render");
1000 page = render (pageno, pindex);
1001 unlock ("render");
1003 printd (state.sock, "r %d %d %d %d %p",
1004 pageno,
1005 state.w,
1006 state.h,
1007 state.rotate,
1008 page);
1010 else if (!strncmp ("interrupt", p, 9)) {
1011 printd (state.sock, "V interrupted");
1013 else {
1014 errx (1, "unknown command %.*s", len, p);
1017 return 0;
1020 static void showsel (struct page *page, int oy)
1022 int ox;
1023 fz_bbox bbox;
1024 fz_textspan *span;
1025 struct mark first, last;
1027 first = page->fmark;
1028 last = page->lmark;
1030 if (!first.span || !last.span) return;
1032 glEnable (GL_BLEND);
1033 glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA);
1034 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1036 ox = -page->pixmap->x;
1037 oy = -page->pixmap->y + oy;
1038 for (span = first.span; span; span = span->next) {
1039 int i, j, k;
1041 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0;
1043 j = 0;
1044 k = span->len - 1;
1046 if (span == page->fmark.span && span == page->lmark.span) {
1047 j = MIN (first.i, last.i);
1048 k = MAX (first.i, last.i);
1050 else if (span == first.span) {
1051 j = first.i;
1053 else if (span == last.span) {
1054 k = last.i;
1057 for (i = j; i <= k; ++i) {
1058 bbox = fz_unionbbox (bbox, span->text[i].bbox);
1060 lprintf ("%d %d %d %d oy=%d ox=%d\n",
1061 bbox.x0,
1062 bbox.y0,
1063 bbox.x1,
1064 bbox.y1,
1065 oy, ox);
1067 glRecti (bbox.x0 + ox, bbox.y0 + oy, bbox.x1 + ox, bbox.y1 + oy);
1069 if (span == last.span) break;
1071 glDisable (GL_BLEND);
1074 static void upload2 (struct page *page, int slicenum, const char *cap)
1076 int i;
1077 int w, h;
1078 double start, end;
1079 struct slice *slice = &page->slices[slicenum];
1081 w = page->pixmap->w;
1082 h = page->pixmap->h;
1084 ARSERT (w == slice->w);
1085 if (slice->texindex != -1
1086 && state.texowners[slice->texindex].slice == slice) {
1087 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1089 else {
1090 int subimage = 0;
1091 int index = (state.texindex++ % state.texcount);
1092 size_t offset = 0;
1094 for (i = 0; i < slicenum; ++i) {
1095 offset += w * page->slices[i].h * 4;
1098 if (state.texowners[index].w == slice->w) {
1099 if (state.texowners[index].h >= slice->h ) {
1100 subimage = 1;
1102 else {
1103 state.texowners[index].h = slice->h;
1106 else {
1107 state.texowners[index].h = slice->h;
1110 state.texowners[index].slice = slice;
1111 state.texowners[index].w = slice->w;
1112 slice->texindex = index;
1114 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1115 start = now ();
1116 if (subimage) {
1118 GLenum err = glGetError ();
1119 if (err != GL_NO_ERROR) {
1120 printf ("\033[0;31mERROR1 %d %d %#x\033[0m\n", w, slice->h, err);
1121 abort ();
1124 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1129 slice->h,
1130 state.texform,
1131 state.texty,
1132 page->pixmap->samples + offset
1135 GLenum err = glGetError ();
1136 if (err != GL_NO_ERROR) {
1137 printf ("\033[0;31mERROR %d %d %#x\033[0m\n", w, slice->h, err);
1138 abort ();
1142 else {
1143 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1145 GL_RGBA8,
1147 slice->h,
1149 state.texform,
1150 state.texty,
1151 page->pixmap->samples + offset
1155 end = now ();
1156 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
1157 subimage ? "sub" : "img",
1158 page->pageno, slicenum,
1159 slice->w, slice->h,
1160 state.texids[slice->texindex],
1161 end - start);
1165 CAMLprim value ml_draw (value dispy_v, value w_v, value h_v,
1166 value py_v, value ptr_v)
1168 CAMLparam5 (dispy_v, w_v, h_v, py_v, ptr_v);
1169 int dispy = Int_val (dispy_v);
1170 int w = Int_val (w_v);
1171 int h = Int_val (h_v);
1172 int py = Int_val (py_v);
1173 char *s = String_val (ptr_v);
1174 int ret;
1175 void *ptr;
1176 struct page *page;
1177 int slicenum = 0;
1179 ret = sscanf (s, "%p", &ptr);
1180 if (ret != 1) {
1181 errx (1, "cannot parse pointer `%s'", s);
1183 page = ptr;
1185 w = page->pixmap->w;
1187 ARSERT (h >= 0 && "ml_draw wrong h");
1188 ARSERT (py <= page->pixmap->h && "ml_draw wrong py");
1190 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1192 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
1193 struct slice *slice = &page->slices[slicenum];
1194 if (slice->h > py) {
1195 break;
1197 py -= slice->h;
1200 h = MIN (state.h, h);
1201 while (h) {
1202 int th;
1203 struct slice *slice = &page->slices[slicenum];
1205 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
1207 th = MIN (h, slice->h - py);
1208 upload2 (page, slicenum, "upload");
1210 glBegin (GL_QUADS);
1212 glTexCoord2i (0, py);
1213 glVertex2i (0, dispy);
1215 glTexCoord2i (w, py);
1216 glVertex2i (w, dispy);
1218 glTexCoord2i (w, py+th);
1219 glVertex2i (w, dispy + th);
1221 glTexCoord2i (0, py+th);
1222 glVertex2i (0, dispy + th);
1224 glEnd ();
1226 h -= th;
1227 py = 0;
1228 dispy += th;
1229 slicenum += 1;
1232 showsel (page, Int_val (dispy_v) - Int_val (py_v));
1233 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1235 CAMLreturn (Val_unit);
1238 static pdf_link *getlink (struct page *page, int x, int y)
1240 fz_point p;
1241 fz_matrix ctm;
1242 pdf_link *link;
1244 p.x = x + page->pixmap->x;
1245 p.y = y + page->pixmap->y;
1247 ctm = fz_invertmatrix (page->pagedim.ctm);
1248 p = fz_transformpoint (ctm, p);
1250 for (link = page->drawpage->links; link; link = link->next) {
1251 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1252 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1253 return link;
1257 return NULL;
1260 CAMLprim value ml_highlightlinks (value ptr_v, value yoff_v)
1262 CAMLparam2 (ptr_v, yoff_v);
1263 pdf_link *link;
1264 struct page *page;
1265 int xoff, yoff = Int_val (yoff_v);
1266 const char *s = String_val (ptr_v);
1268 if (trylock ("ml_highlightlinks")) {
1269 goto done;
1272 page = parse_pointer ("ml_highlightlinks", s);
1274 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1275 glEnable (GL_LINE_STIPPLE);
1276 glLineStipple (0.5, 0xcccc);
1278 xoff = -page->pixmap->x;
1279 yoff -= page->pixmap->y;
1281 glBegin (GL_QUADS);
1282 for (link = page->drawpage->links; link; link = link->next) {
1283 fz_point p1, p2, p3, p4;
1284 fz_matrix ctm = page->pagedim.ctm;
1286 p1.x = link->rect.x0;
1287 p1.y = link->rect.y0;
1289 p2.x = link->rect.x1;
1290 p2.y = link->rect.y0;
1292 p3.x = link->rect.x1;
1293 p3.y = link->rect.y1;
1295 p4.x = link->rect.x0;
1296 p4.y = link->rect.y1;
1298 p1 = fz_transformpoint (ctm, p1);
1299 p2 = fz_transformpoint (ctm, p2);
1300 p3 = fz_transformpoint (ctm, p3);
1301 p4 = fz_transformpoint (ctm, p4);
1303 switch (link->kind) {
1304 case PDF_LGOTO: glColor3ub (255, 0, 0); break;
1305 case PDF_LURI: glColor3ub (0, 0, 255); break;
1306 default: glColor3ub (0, 0, 0); break;
1309 glVertex2f (p1.x + xoff, p1.y + yoff);
1310 glVertex2f (p2.x + xoff, p2.y + yoff);
1311 glVertex2f (p3.x + xoff, p3.y + yoff);
1312 glVertex2f (p4.x + xoff, p4.y + yoff);
1314 glEnd ();
1316 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1317 glDisable (GL_LINE_STIPPLE);
1318 unlock ("ml_highlightlinks");
1320 done:
1321 CAMLreturn (Val_unit);
1324 static void ensuretext (struct page *page)
1326 if (!page->text) {
1327 fz_error error;
1328 fz_device *tdev;
1330 page->text = fz_newtextspan ();
1331 tdev = fz_newtextdevice (page->text);
1332 error = pdf_runpage (state.xref, page->drawpage, tdev,
1333 page->pagedim.ctm);
1334 if (error) die (error);
1335 fz_freedevice (tdev);
1339 CAMLprim value ml_whatsunder (value ptr_v, value x_v, value y_v)
1341 CAMLparam3 (ptr_v, x_v, y_v);
1342 CAMLlocal3 (ret_v, tup_v, str_v);
1343 pdf_link *link;
1344 struct page *page;
1345 char *s = String_val (ptr_v);
1347 ret_v = Val_int (0);
1348 if (trylock ("ml_whatsunder")) {
1349 goto done;
1352 page = parse_pointer ("ml_whatsunder", s);
1353 link = getlink (page, Int_val (x_v), Int_val (y_v));
1354 if (link) {
1355 switch (link->kind) {
1356 case PDF_LGOTO:
1358 int pageno;
1359 fz_point p;
1360 fz_obj *obj;
1362 pageno = -1;
1363 p.x = 0;
1364 p.y = 0;
1366 if (fz_isarray (link->dest)) {
1367 obj = fz_arrayget (link->dest, 0);
1368 if (fz_isindirect (obj)) {
1369 pageno = pdf_findpageobject (state.xref, obj) - 1;
1371 else if (fz_isint (obj)) {
1372 pageno = fz_toint (obj);
1375 if (fz_arraylen (link->dest) > 3) {
1376 fz_obj *xo, *yo;
1378 xo = fz_arrayget (link->dest, 2);
1379 yo = fz_arrayget (link->dest, 3);
1380 if (!fz_isnull (xo) && !fz_isnull (yo)) {
1381 p.x = fz_toint (xo);
1382 p.y = fz_toint (yo);
1383 p = fz_transformpoint (page->pagedim.ctm, p);
1387 else {
1388 pageno = pdf_findpageobject (state.xref, link->dest) - 1;
1390 tup_v = caml_alloc_tuple (2);
1391 ret_v = caml_alloc_small (1, 1);
1392 Field (tup_v, 0) = Val_int (pageno);
1393 Field (tup_v, 1) = Val_int (p.y);
1394 Field (ret_v, 0) = tup_v;
1396 break;
1398 case PDF_LURI:
1399 str_v = caml_copy_string (fz_tostrbuf (link->dest));
1400 ret_v = caml_alloc_small (1, 0);
1401 Field (ret_v, 0) = str_v;
1402 break;
1404 default:
1405 printd (state.sock, "T unhandled link kind %d", link->kind);
1406 break;
1409 else {
1410 int i, x, y;
1411 fz_textspan *span;
1413 ensuretext (page);
1414 x = Int_val (x_v) + page->pixmap->x;
1415 y = Int_val (y_v) + page->pixmap->y;
1417 for (span = page->text; span; span = span->next) {
1418 for (i = 0; i < span->len; ++i) {
1419 fz_bbox *b;
1420 b = &span->text[i].bbox;
1421 if ((x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1)) {
1422 #ifdef FT_FREETYPE_H
1423 FT_FaceRec *face = span->font->ftface;
1424 if (face) {
1425 char *s;
1426 char *n1 = face->family_name;
1427 char *n2 = span->font->name;
1428 size_t l1 = strlen (n1);
1429 size_t l2 = strlen (n2);
1431 if (l1 != l2 || memcmp (n1, n2, l1)) {
1432 s = malloc (l1 + l2 + 2);
1433 if (s) {
1434 memcpy (s, n2, l2);
1435 s[l2] = '=';
1436 memcpy (s + l2 + 1, n1, l1 + 1);
1437 str_v = caml_copy_string (s);
1438 free (s);
1442 if (str_v == 0) {
1443 str_v = caml_copy_string (span->font->name);
1445 #else
1446 str_v = caml_copy_string (span->font->name);
1447 #endif
1448 ret_v = caml_alloc_small (1, 2);
1449 Field (ret_v, 0) = str_v;
1450 goto unlock;
1455 unlock:
1456 unlock ("ml_whatsunder");
1458 done:
1459 CAMLreturn (ret_v);
1462 CAMLprim value ml_seltext (value ptr_v, value rect_v, value oy_v)
1464 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1465 fz_bbox *b;
1466 struct page *page;
1467 fz_textspan *span;
1468 struct mark first, last;
1469 int i, x0, x1, y0, y1, oy;
1470 char *s = String_val (ptr_v);
1472 if (trylock ("ml_seltext")) {
1473 goto done;
1476 page = parse_pointer ("ml_seltext", s);
1477 ensuretext (page);
1479 oy = Int_val (oy_v);
1480 x0 = Int_val (Field (rect_v, 0));
1481 y0 = Int_val (Field (rect_v, 1));
1482 x1 = Int_val (Field (rect_v, 2));
1483 y1 = Int_val (Field (rect_v, 3));
1485 if (0) {
1486 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1487 glColor3ub (128, 128, 128);
1488 glRecti (x0, y0, x1, y1);
1489 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1492 x0 += page->pixmap->x;
1493 y0 += page->pixmap->y - oy;
1494 x1 += page->pixmap->x;
1495 y1 += page->pixmap->y - oy;
1497 first.span = NULL;
1498 last.span = NULL;
1500 last.i = first.i = 0;
1501 first.span = page->text;
1502 for (span = page->text; span; span = span->next) {
1503 for (i = 0; i < span->len; ++i) {
1504 b = &span->text[i].bbox;
1505 if (x0 >= b->x0 && x0 <= b->x1 && y0 >= b->y0 && y0 <= b->y1) {
1506 first.i = i;
1507 first.span = span;
1509 if (x1 >= b->x0 && x1 <= b->x1 && y1 >= b->y0 && y1 <= b->y1) {
1510 last.i = i;
1511 last.span = span;
1516 if (y1 < y0 || x1 < x0) {
1517 int swap = 0;
1519 if (first.span == last.span) {
1520 swap = 1;
1522 else {
1523 if (y1 < y0) {
1524 for (span = first.span; span && span != last.span;
1525 span = span->next) {
1526 if (span->eol) {
1527 swap = 1;
1528 break;
1534 if (swap) {
1535 i = first.i;
1536 span = first.span;
1537 first.i = last.i;
1538 first.span = last.span;
1539 last.i = i;
1540 last.span = span;
1544 page->fmark = first;
1545 page->lmark = last;
1547 unlock ("ml_seltext");
1549 done:
1550 CAMLreturn (Val_unit);
1553 static int pipespan (FILE *f, fz_textspan *span, int a, int b)
1555 char buf[4];
1556 int i, len, ret;
1558 for (i = a; i <= b; ++i) {
1559 len = runetochar (buf, &span->text[i].c);
1560 ret = fwrite (buf, len, 1, f);
1562 if (ret != 1) {
1563 printd (state.sock, "T failed to write %d bytes ret=%d: %s",
1564 len, ret, strerror (errno));
1565 return -1;
1568 return 0;
1571 CAMLprim value ml_copysel (value ptr_v)
1573 CAMLparam1 (ptr_v);
1574 FILE *f;
1575 struct page *page;
1576 char *s = String_val (ptr_v);
1578 if (trylock ("ml_copysel")) {
1579 goto done;
1582 if (!*s) {
1583 close:
1584 #ifdef USE_XSEL
1585 if (state.xselpipe) {
1586 int ret = pclose (state.xselpipe);
1587 if (ret) {
1588 printd (state.sock, "T failed to close xsel pipe `%s'",
1589 strerror (errno));
1591 state.xselpipe = NULL;
1593 #else
1594 printf ("========================================\n");
1595 #endif
1597 else {
1598 fz_textspan *span;
1600 page = parse_pointer ("ml_sopysel", s);
1602 if (!page->fmark.span || !page->lmark.span) {
1603 printd (state.sock, "T nothing to copy");
1604 goto unlock;
1607 f = stdout;
1608 #ifdef USE_XSEL
1609 if (!state.xselpipe) {
1610 state.xselpipe = popen ("xsel -i", "w");
1611 if (!state.xselpipe) {
1612 printd (state.sock, "T failed to open xsel pipe `%s'",
1613 strerror (errno));
1615 else {
1616 f = state.xselpipe;
1619 else {
1620 f = state.xselpipe;
1622 #endif
1624 for (span = page->fmark.span;
1625 span && span != page->lmark.span->next;
1626 span = span->next) {
1627 int a = span == page->fmark.span ? page->fmark.i : 0;
1628 int b = span == page->lmark.span ? page->lmark.i : span->len - 1;
1629 if (pipespan (f, span, a, b)) {
1630 goto close;
1632 if (span->eol) {
1633 if (putc ('\n', f) == EOF) {
1634 printd (state.sock, "T failed break line on xsel pipe `%s'",
1635 strerror (errno));
1636 goto close;
1640 page->lmark.span = NULL;
1641 page->fmark.span = NULL;
1644 unlock:
1645 unlock ("ml_copysel");
1647 done:
1648 CAMLreturn (Val_unit);
1651 CAMLprim value ml_getpagewh (value pagedimno_v)
1653 CAMLparam1 (pagedimno_v);
1654 CAMLlocal1 (ret_v);
1655 int pagedimno = Int_val (pagedimno_v);
1657 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1658 Store_double_field (ret_v, 0, state.pagedims[pagedimno].box.x0);
1659 Store_double_field (ret_v, 1, state.pagedims[pagedimno].box.x1);
1660 Store_double_field (ret_v, 2, state.pagedims[pagedimno].box.y0);
1661 Store_double_field (ret_v, 3, state.pagedims[pagedimno].box.y1);
1662 CAMLreturn (ret_v);
1665 CAMLprim value ml_init (value sock_v)
1667 #ifndef _WIN32
1668 int ret;
1669 #endif
1670 CAMLparam1 (sock_v);
1672 state.texcount = 256;
1673 state.sliceheight = 24;
1674 state.texform = GL_RGBA;
1675 state.texty = GL_UNSIGNED_BYTE;
1677 fz_accelerate ();
1678 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1679 if (!state.texids) {
1680 err (1, "calloc texids " FMT_s,
1681 state.texcount * sizeof (*state.texids));
1684 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1685 if (!state.texowners) {
1686 err (1, "calloc texowners " FMT_s,
1687 state.texcount * sizeof (*state.texowners));
1690 glGenTextures (state.texcount, state.texids);
1692 #ifdef _WIN32
1693 state.sock = Socket_val (sock_v);
1694 #else
1695 state.sock = Int_val (sock_v);
1696 #endif
1698 state.cache = fz_newglyphcache ();
1699 if (!state.cache) {
1700 errx (1, "fz_newglyphcache failed");
1703 #ifdef _WIN32
1704 InitializeCriticalSection (&critsec);
1705 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
1706 if (state.thread == INVALID_HANDLE_VALUE) {
1707 errx (1, "CreateThread failed: %lx", GetLastError ());
1709 #else
1710 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1711 if (ret) {
1712 errx (1, "pthread_create: %s", strerror (errno));
1714 #endif
1716 CAMLreturn (Val_unit);