Do not perform useless redisplays
[llpp.git] / link.c
bloba8542e454073b82b7e402d8f0f22a7ff4247792e
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 #define ARSERT(cond) for (;;) { \
103 if (!(cond)) { \
104 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
106 break; \
109 struct slice {
110 int texindex;
111 int w, h;
114 struct page {
115 int pageno;
116 int slicecount;
117 fz_textspan *text;
118 fz_pixmap *pixmap;
119 pdf_page *drawpage;
120 struct pagedim *pagedim;
121 struct page *prev;
122 struct mark {
123 int i;
124 fz_textspan *span;
125 } fmark, lmark;
126 struct slice slices[];
129 struct pagedim {
130 int pageno;
131 int rotate;
132 fz_rect box;
133 fz_bbox bbox;
134 fz_matrix ctm, ctm1;
137 #if !defined _WIN32 && !defined __APPLE__
138 #define USE_XSEL
139 #endif
141 struct {
142 int sock;
143 int sliceheight;
144 struct page *pages;
145 struct pagedim *pagedims;
146 int pagecount;
147 int pagedimcount;
148 pdf_xref *xref;
149 fz_glyphcache *cache;
150 int w, h;
152 int texindex;
153 int texcount;
154 GLuint *texids;
156 GLenum texform;
157 GLenum texty;
159 int lotsamemory;
161 struct {
162 int w, h;
163 struct slice *slice;
164 } *texowners;
166 int rotate;
168 #ifdef _WIN32
169 HANDLE thread;
170 #else
171 pthread_t thread;
172 #endif
173 FILE *xselpipe;
174 } state;
176 #ifdef _WIN32
177 static CRITICAL_SECTION critsec;
179 static void lock (void *unused)
181 (void) unused;
182 EnterCriticalSection (&critsec);
185 static void unlock (void *unused)
187 (void) unused;
188 LeaveCriticalSection (&critsec);
191 static int trylock (void *unused)
193 return TryEnterCriticalSection (&critsec) == 0;
195 #else
196 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
198 static void lock (const char *cap)
200 int ret = pthread_mutex_lock (&mutex);
201 if (ret) {
202 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
206 static void unlock (const char *cap)
208 int ret = pthread_mutex_unlock (&mutex);
209 if (ret) {
210 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
214 static int trylock (const char *cap)
216 int ret = pthread_mutex_trylock (&mutex);
218 if (ret && ret != EBUSY) {
219 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
221 return ret == EBUSY;
223 #endif
225 static void *parse_pointer (const char *cap, const char *s)
227 int ret;
228 void *ptr;
230 ret = sscanf (s, "%p", &ptr);
231 if (ret != 1) {
232 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
234 return ptr;
237 static int hasdata (int sock)
239 int n;
240 fd_set s;
241 struct timeval tv;
242 FD_ZERO (&s);
243 FD_SET (sock, &s);
244 tv.tv_sec = 0;
245 tv.tv_usec = 0;
246 n = select (sock + 1, &s, NULL, NULL, &tv);
247 if (n == 0) return 0;
248 if (n == 1) return 1;
249 sockerr (1, "hasdata: select error ret=%d", n);
252 static double now (void)
254 struct timeval tv;
256 if (gettimeofday (&tv, NULL)) {
257 err (1, "gettimeofday");
259 return tv.tv_sec + tv.tv_usec*1e-6;
262 static void readdata (int fd, char *p, int size)
264 ssize_t n;
266 n = recv (fd, p, size, 0);
267 if (n - size) {
268 if (!n) errx (1, "EOF while reading");
269 sockerr (1, "recv (req %d, ret " FMT_ss ")", size, n);
273 static void writedata (int fd, char *p, int size)
275 char buf[4];
276 ssize_t n;
278 buf[0] = (size >> 24) & 0xff;
279 buf[1] = (size >> 16) & 0xff;
280 buf[2] = (size >> 8) & 0xff;
281 buf[3] = (size >> 0) & 0xff;
283 n = send (fd, buf, 4, 0);
284 if (n != 4) {
285 if (!n) errx (1, "EOF while writing length");
286 sockerr (1, "send " FMT_ss, n);
289 n = send (fd, p, size, 0);
290 if (n - size) {
291 if (!n) errx (1, "EOF while writing data");
292 sockerr (1, "send (req %d, ret " FMT_ss ")", size, n);
296 static void
297 #ifdef __GNUC__
298 __attribute__ ((format (printf, 2, 3)))
299 #endif
300 printd (int fd, const char *fmt, ...)
302 int len;
303 va_list ap;
304 char buf[200];
306 va_start (ap, fmt);
307 len = vsnprintf (buf, sizeof (buf), fmt, ap);
308 va_end (ap);
309 writedata (fd, buf, len);
312 static void die (fz_error error)
314 fz_catch (error, "aborting");
315 if (state.xref)
316 pdf_freexref (state.xref);
317 exit (1);
320 static void openxref (char *filename)
322 int fd;
323 fz_error error;
324 fz_stream *file;
326 fd = open (filename, O_BINARY | O_RDONLY, 0666);
327 if (fd < 0)
328 die (fz_throw ("cannot open file '%s'", filename));
330 file = fz_openfile (fd);
331 error = pdf_openxrefwithstream (&state.xref, file, NULL);
332 if (error)
333 die (fz_rethrow(error, "cannot open document '%s'", filename));
334 fz_close (file);
336 if (pdf_needspassword (state.xref)) {
337 die (fz_throw ("password protected"));
340 error = pdf_loadpagetree (state.xref);
341 if (error) {
342 die (fz_throw ("cannot load page tree"));
345 state.pagecount = pdf_getpagecount (state.xref);
348 static int readlen (int fd)
350 ssize_t n;
351 char p[4];
353 n = recv (fd, p, 4, 0);
354 if (n != 4) {
355 if (!n) errx (1, "EOF while reading length");
356 sockerr (1, "recv " FMT_ss, n);
359 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
362 static void freepage (struct page *page)
364 int i;
365 struct page *p;
367 fz_droppixmap (page->pixmap);
368 for (p = state.pages; p; p = p->prev) {
369 if (p->prev == page) {
370 p->prev = page->prev;
371 break;
374 for (i = 0; i < page->slicecount; ++i) {
375 struct slice *s = &page->slices[i];
376 if (s->texindex != -1) {
377 if (state.texowners[s->texindex].slice == s) {
378 state.texowners[s->texindex].slice = NULL;
379 ARSERT (state.texowners[s->texindex].w == s->w);
380 ARSERT (state.texowners[s->texindex].h >= s->h);
384 if (page->text) {
385 fz_freetextspan (page->text);
387 if (page->drawpage) {
388 pdf_freepage (page->drawpage);
391 free (page);
394 static void subdivide (struct page *p)
396 int i;
397 int h = p->pixmap->h;
398 int th = MIN (h, state.sliceheight);
400 for (i = 0; i < p->slicecount; ++i) {
401 struct slice *s = &p->slices[i];
402 s->texindex = -1;
403 s->h = MIN (th, h);
404 s->w = p->pixmap->w;
405 h -= th;
409 static void *render (int pageno, int pindex)
411 fz_error error;
412 int slicecount;
413 fz_obj *pageobj;
414 struct page *page;
415 double start, end;
416 pdf_page *drawpage;
417 fz_device *idev;
418 struct pagedim *pagedim;
420 start = now ();
421 printd (state.sock, "V rendering %d", pageno);
423 pagedim = &state.pagedims[pindex];
424 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
425 + state.sliceheight - 1) / state.sliceheight;
426 slicecount += slicecount == 0;
428 page = calloc (sizeof (*page)
429 + (slicecount * sizeof (struct slice)), 1);
430 if (!page) {
431 err (1, "calloc page %d\n", pageno);
433 page->slicecount = slicecount;
434 page->prev = state.pages;
435 state.pages = page;
437 pageobj = pdf_getpageobject (state.xref, pageno);
438 if (!pageobj)
439 die (fz_throw ("cannot retrieve info from page %d", pageno));
441 error = pdf_loadpage (&drawpage, state.xref, pageobj);
442 if (error)
443 die (error);
445 page->pixmap = fz_newpixmapwithrect (fz_devicergb, pagedim->bbox);
446 fz_clearpixmap (page->pixmap, 0xFF);
448 idev = fz_newdrawdevice (state.cache, page->pixmap);
449 if (!idev)
450 die (fz_throw ("fz_newdrawdevice failed"));
451 error = pdf_runpage (state.xref, drawpage, idev, pagedim->ctm);
452 if (error)
453 die (fz_rethrow (error, "pdf_runpage failed"));
454 fz_freedevice (idev);
456 page->drawpage = drawpage;
457 page->pagedim = pagedim;
458 page->pageno = pageno;
459 subdivide (page);
460 end = now ();
462 if (!state.lotsamemory) {
463 pdf_agestore(state.xref->store, 3);
466 printd (state.sock, "V rendering %d took %f sec", pageno, end - start);
467 return page;
470 static void initpdims (void)
472 int pageno;
473 double start, end;
475 start = now ();
476 for (pageno = 0; pageno < state.pagecount; ++pageno) {
477 int rotate;
478 fz_rect box;
479 struct pagedim *p;
480 fz_obj *obj, *pageobj;
482 pageobj = pdf_getpageobject (state.xref, pageno + 1);
484 obj = fz_dictgets (pageobj, "CropBox");
485 if (!fz_isarray (obj)) {
486 obj = fz_dictgets (pageobj, "MediaBox");
487 if (!fz_isarray (obj)) {
488 die (fz_throw ("cannot find page bounds %d (%d Rd)",
489 fz_tonum (pageobj), fz_togen (pageobj)));
492 box = pdf_torect (obj);
494 obj = fz_dictgets (pageobj, "Rotate");
495 if (fz_isint (obj)) {
496 rotate = fz_toint (obj);
498 else {
499 rotate = 0;
501 rotate += state.rotate;
503 p = &state.pagedims[state.pagedimcount - 1];
504 if ((state.pagedimcount == 0)
505 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
506 size_t size;
508 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
509 state.pagedims = realloc (state.pagedims, size);
510 if (!state.pagedims) {
511 err (1, "realloc pagedims to " FMT_s " (%d elems)",
512 size, state.pagedimcount + 1);
514 p = &state.pagedims[state.pagedimcount++];
515 p->rotate = rotate;
516 p->box = box;
517 p->pageno = pageno;
520 end = now ();
521 printd (state.sock, "T Processed %d pages in %f seconds",
522 state.pagecount, end - start);
525 static void layout (void)
527 int pindex;
528 fz_matrix ctm;
529 fz_rect box, box2;
530 double zoom, w;
531 struct pagedim *p = state.pagedims;
533 pindex = 0;
534 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
535 box.x0 = MIN (p->box.x0, p->box.x1);
536 box.y0 = MIN (p->box.y0, p->box.y1);
537 box.x1 = MAX (p->box.x0, p->box.x1);
538 box.y1 = MAX (p->box.y0, p->box.y1);
540 ctm = fz_identity;
541 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
542 ctm = fz_concat (ctm, fz_rotate (p->rotate));
543 box2 = fz_transformrect (ctm, box);
544 w = box2.x1 - box2.x0;
546 zoom = (state.w / w);
547 ctm = fz_identity;
548 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
549 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
550 memcpy (&p->ctm1, &ctm, sizeof (ctm));
551 ctm = fz_concat (ctm, fz_rotate (p->rotate));
552 p->bbox = fz_roundrect (fz_transformrect (ctm, box));
553 memcpy (&p->ctm, &ctm, sizeof (ctm));
556 while (p-- != state.pagedims) {
557 printd (state.sock, "l %d %d %d",
558 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0);
562 static void recurse_outline (pdf_outline *outline, int level)
564 while (outline) {
565 int i;
566 fz_obj *obj;
567 int top = 0;
568 int pageno = -1;
570 if (!outline->link) goto next;
572 obj = outline->link->dest;
573 if (fz_isindirect (obj)) {
574 obj = fz_resolveindirect (obj);
576 if (fz_isarray (obj)) {
577 fz_obj *obj2;
579 obj2 = fz_arrayget (obj, 0);
580 if (fz_isint (obj2)) {
581 pageno = fz_toint (obj2);
583 else {
584 pageno = pdf_findpageobject (state.xref, obj2) - 1;
587 if (fz_arraylen (obj) > 3) {
588 fz_point p;
589 struct pagedim *pagedim = state.pagedims;
591 for (i = 0; i < state.pagedimcount; ++i) {
592 if (state.pagedims[i].pageno > pageno)
593 break;
594 pagedim = &state.pagedims[i];
597 p.x = fz_toint (fz_arrayget (obj, 2));
598 p.y = fz_toint (fz_arrayget (obj, 3));
599 p = fz_transformpoint (pagedim->ctm, p);
600 top = p.y;
604 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
605 printd (state.sock, "o %d %d %d %s",
606 level, pageno, top, outline->title);
607 next:
608 if (outline->child) {
609 recurse_outline (outline->child, level + 1);
611 outline = outline->next;
615 static void process_outline (void)
617 pdf_outline *outline;
619 outline = pdf_loadoutline (state.xref);
620 if (outline) {
621 recurse_outline (outline, 0);
622 pdf_freeoutline (outline);
626 static int comparespans (const void *l, const void *r)
628 fz_textspan const *const*ls = l;
629 fz_textspan const *const*rs = r;
630 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
633 /* wishful thinking function */
634 static void search (regex_t *re, int pageno, int y, int forward)
636 int i, j;
637 int ret;
638 char *p;
639 char buf[256];
640 fz_matrix ctm;
641 fz_error error;
642 fz_obj *pageobj;
643 fz_device *tdev;
644 pdf_page *drawpage;
645 fz_textspan *text, *span, **pspan;
646 struct pagedim *pdim, *pdimprev;
647 int stop = 0;
648 int niters = 0;
649 int nspans;
650 double start, end;
652 start = now ();
653 while (pageno >= 0 && pageno < state.pagecount && !stop) {
654 if (niters++ == 5) {
655 if (!state.lotsamemory) {
656 pdf_agestore(state.xref->store, 3);
658 niters = 0;
659 if (hasdata (state.sock)) {
660 printd (state.sock, "T attention requested aborting search at %d",
661 pageno);
662 stop = 1;
664 else {
665 printd (state.sock, "T searching in page %d", pageno);
668 pdimprev = NULL;
669 for (i = 0; i < state.pagedimcount; ++i) {
670 pdim = &state.pagedims[i];
671 if (pdim->pageno == pageno) {
672 goto found;
674 if (pdim->pageno > pageno) {
675 pdim = pdimprev;
676 goto found;
678 pdimprev = pdim;
680 pdim = pdimprev;
681 found:
683 pageobj = pdf_getpageobject (state.xref, pageno + 1);
684 if (!pageobj)
685 die (fz_throw ("cannot retrieve info from page %d", pageno));
687 error = pdf_loadpage (&drawpage, state.xref, pageobj);
688 if (error)
689 die (error);
691 ctm = fz_rotate (pdim->rotate);
693 text = fz_newtextspan ();
694 tdev = fz_newtextdevice (text);
695 error = pdf_runpage (state.xref, drawpage, tdev, pdim->ctm1);
696 if (error) die (error);
697 fz_freedevice (tdev);
699 nspans = 0;
700 for (span = text; span; span = span->next) {
701 nspans++;
703 pspan = malloc (sizeof (void *) * nspans);
704 if (!pspan) {
705 err (1, "malloc span pointers " FMT_s, sizeof (void *) * nspans);
707 for (i = 0, span = text; span; span = span->next, ++i) {
708 pspan[i] = span;
710 qsort (pspan, nspans, sizeof (fz_textspan *), comparespans);
712 j = forward ? 0 : nspans - 1;
713 while (nspans--) {
714 regmatch_t rm;
716 span = pspan[j];
717 j += forward ? 1 : -1;
718 p = buf;
719 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
720 if (forward) {
721 if (span->text[i].bbox.y0 < y + 1) {
722 continue;
725 else {
726 if (span->text[i].bbox.y0 > y - 1) {
727 continue;
730 if (span->text[i].c < 256) {
731 *p++ = span->text[i].c;
733 else {
734 *p++ = '?';
737 if (p == buf) {
738 continue;
740 *p++ = 0;
742 ret = regexec (re, buf, 1, &rm, 0);
743 if (ret) {
744 if (ret != REG_NOMATCH) {
745 size_t size;
746 char errbuf[80];
747 size = regerror (ret, re, errbuf, sizeof (errbuf));
748 printd (state.sock,
749 "T regexec error `%.*s'",
750 (int) size, errbuf);
751 fz_freetextspan (text);
752 pdf_freepage (drawpage);
753 free (pspan);
754 return;
757 else {
758 int xoff, yoff;
759 fz_bbox *sb, *eb;
760 fz_point p1, p2, p3, p4;
762 xoff = -pdim->bbox.x0;
763 yoff = -pdim->bbox.y0;
765 sb = &span->text[rm.rm_so].bbox;
766 eb = &span->text[rm.rm_eo - 1].bbox;
768 p1.x = sb->x0;
769 p1.y = sb->y0;
770 p2.x = eb->x1;
771 p2.y = sb->y0;
772 p3.x = eb->x1;
773 p3.y = eb->y1;
774 p4.x = sb->x0;
775 p4.y = eb->y1;
777 p1 = fz_transformpoint (ctm, p1);
778 p2 = fz_transformpoint (ctm, p2);
779 p3 = fz_transformpoint (ctm, p3);
780 p4 = fz_transformpoint (ctm, p4);
782 if (!stop) {
783 printd (state.sock, "F %d %d %f %f %f %f %f %f %f %f",
784 pageno, 1,
785 p1.x + xoff, p1.y + yoff,
786 p2.x + xoff, p2.y + yoff,
787 p3.x + xoff, p3.y + yoff,
788 p4.x + xoff, p4.y + yoff);
790 printd (state.sock, "T found at %d `%.*s' in %f sec",
791 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
792 now () - start);
794 else {
795 printd (state.sock, "R %d %d %f %f %f %f %f %f %f %f",
796 pageno, 2,
797 p1.x + xoff, p1.y + yoff,
798 p2.x + xoff, p2.y + yoff,
799 p3.x + xoff, p3.y + yoff,
800 p4.x + xoff, p4.y + yoff);
802 stop = 1;
805 if (forward) {
806 pageno += 1;
807 y = 0;
809 else {
810 pageno -= 1;
811 y = INT_MAX;
813 fz_freetextspan (text);
814 pdf_freepage (drawpage);
815 free (pspan);
817 end = now ();
818 if (!stop) {
819 printd (state.sock, "T no matches %f sec", end - start);
821 printd (state.sock, "D");
824 static
825 #ifdef _WIN32
826 DWORD _stdcall
827 #else
828 void *
829 #endif
830 mainloop (void *unused)
832 char *p = NULL;
833 int len, ret, oldlen = 0;
835 for (;;) {
836 len = readlen (state.sock);
837 if (len == 0) {
838 errx (1, "readlen returned 0");
841 if (oldlen < len + 1) {
842 p = realloc (p, len + 1);
843 if (!p) {
844 err (1, "realloc %d failed", len + 1);
846 oldlen = len + 1;
848 readdata (state.sock, p, len);
849 p[len] = 0;
851 if (!strncmp ("open", p, 4)) {
852 fz_obj *obj;
853 char *filename = p + 5;
855 openxref (filename);
856 initpdims ();
858 obj = fz_dictgets (state.xref->trailer, "Info");
859 if (obj) {
860 obj = fz_dictgets (obj, "Title");
861 if (obj) {
862 printd (state.sock, "t %s", pdf_toutf8 (obj));
866 else if (!strncmp ("free", p, 4)) {
867 void *ptr;
869 ret = sscanf (p + 4, " %p", &ptr);
870 if (ret != 1) {
871 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
873 lock ("free");
874 freepage (ptr);
875 unlock ("free");
877 else if (!strncmp ("search", p, 6)) {
878 int icase, pageno, y, ret, len2, forward;
879 char *pattern;
880 regex_t re;
882 ret = sscanf (p + 6, " %d %d %d %d,%n",
883 &icase, &pageno, &y, &forward, &len2);
884 if (ret != 4) {
885 errx (1, "malformed search `%s' ret=%d", p, ret);
888 pattern = p + 6 + len2;
889 ret = regcomp (&re, pattern,
890 REG_EXTENDED | (icase ? REG_ICASE : 0));
891 if (ret) {
892 char errbuf[80];
893 size_t size;
895 size = regerror (ret, &re, errbuf, sizeof (errbuf));
896 printd (state.sock, "T regcomp failed `%.*s'",
897 (int) size, errbuf);
899 else {
900 search (&re, pageno, y, forward);
901 regfree (&re);
904 else if (!strncmp ("geometry", p, 8)) {
905 int w, h;
907 printd (state.sock, "c");
908 ret = sscanf (p + 8, " %d %d", &w, &h);
909 if (ret != 2) {
910 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
913 lock ("geometry");
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 layout ();
923 process_outline ();
924 unlock ("geometry");
925 printd (state.sock, "C %d", state.pagecount);
927 else if (!strncmp ("rotate", p, 6)) {
928 float rotate;
930 printd (state.sock, "c");
931 ret = sscanf (p + 6, " %f", &rotate);
932 if (ret != 1) {
933 errx (1, "bad rotate line `%.*s' ret=%d", len, p, ret);
935 lock ("rotate");
936 state.rotate = rotate;
937 state.pagedimcount = 0;
938 free (state.pagedims);
939 state.pagedims = NULL;
940 initpdims ();
941 layout ();
942 process_outline ();
943 unlock ("rotate");
944 printd (state.sock, "C %d", state.pagecount);
946 else if (!strncmp ("render", p, 6)) {
947 int pageno, pindex, w, h, ret;
948 struct page *page;
950 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
951 if (ret != 4) {
952 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
955 lock ("render");
956 page = render (pageno, pindex);
957 unlock ("render");
959 printd (state.sock, "r %d %d %d %d %p\n",
960 pageno,
961 state.w,
962 state.h,
963 state.rotate,
964 page);
966 else if (!strncmp ("interrupt", p, 8)) {
967 printd (state.sock, "V interrupted");
969 else {
970 errx (1, "unknown command %.*s", len, p);
973 return 0;
976 static void showsel (struct page *page, int oy)
978 int ox;
979 fz_bbox bbox;
980 fz_textspan *span;
981 struct mark first, last;
983 first = page->fmark;
984 last = page->lmark;
986 if (!first.span || !last.span) return;
988 glEnable (GL_BLEND);
989 glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA);
990 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
992 ox = -page->pixmap->x;
993 oy = -page->pixmap->y + oy;
994 for (span = first.span; span; span = span->next) {
995 int i, j, k;
997 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0;
999 j = 0;
1000 k = span->len - 1;
1002 if (span == page->fmark.span && span == page->lmark.span) {
1003 j = MIN (first.i, last.i);
1004 k = MAX (first.i, last.i);
1006 else if (span == first.span) {
1007 j = first.i;
1009 else if (span == last.span) {
1010 k = last.i;
1013 for (i = j; i <= k; ++i) {
1014 bbox = fz_unionbbox (bbox, span->text[i].bbox);
1016 lprintf ("%d %d %d %d oy=%d ox=%d\n",
1017 bbox.x0,
1018 bbox.y0,
1019 bbox.x1,
1020 bbox.y1,
1021 oy, ox);
1023 glRecti (bbox.x0 + ox, bbox.y0 + oy, bbox.x1 + ox, bbox.y1 + oy);
1025 if (span == last.span) break;
1027 glDisable (GL_BLEND);
1030 static void upload2 (struct page *page, int slicenum, const char *cap)
1032 int i;
1033 int w, h;
1034 double start, end;
1035 struct slice *slice = &page->slices[slicenum];
1037 w = page->pixmap->w;
1038 h = page->pixmap->h;
1040 ARSERT (w == slice->w);
1041 if (slice->texindex != -1
1042 && state.texowners[slice->texindex].slice == slice) {
1043 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1045 else {
1046 int subimage = 0;
1047 int index = (state.texindex++ % state.texcount);
1048 size_t offset = 0;
1050 for (i = 0; i < slicenum; ++i) {
1051 offset += w * page->slices[i].h * 4;
1054 if (state.texowners[index].w == slice->w) {
1055 if (state.texowners[index].h >= slice->h ) {
1056 subimage = 1;
1058 else {
1059 state.texowners[index].h = slice->h;
1062 else {
1063 state.texowners[index].h = slice->h;
1066 state.texowners[index].slice = slice;
1067 state.texowners[index].w = slice->w;
1068 slice->texindex = index;
1070 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1071 start = now ();
1072 if (subimage) {
1074 GLenum err = glGetError ();
1075 if (err != GL_NO_ERROR) {
1076 printf ("\033[0;31mERROR1 %d %d %#x\033[0m\n", w, slice->h, err);
1077 abort ();
1080 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1085 slice->h,
1086 state.texform,
1087 state.texty,
1088 page->pixmap->samples + offset
1091 GLenum err = glGetError ();
1092 if (err != GL_NO_ERROR) {
1093 printf ("\033[0;31mERROR %d %d %#x\033[0m\n", w, slice->h, err);
1094 abort ();
1098 else {
1099 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1101 GL_RGBA8,
1103 slice->h,
1105 state.texform,
1106 state.texty,
1107 page->pixmap->samples + offset
1111 end = now ();
1112 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
1113 subimage ? "sub" : "img",
1114 page->pageno, slicenum,
1115 slice->w, slice->h,
1116 state.texids[slice->texindex],
1117 end - start);
1121 CAMLprim value ml_draw (value dispy_v, value w_v, value h_v,
1122 value py_v, value ptr_v)
1124 CAMLparam5 (dispy_v, w_v, h_v, py_v, ptr_v);
1125 int dispy = Int_val (dispy_v);
1126 int w = Int_val (w_v);
1127 int h = Int_val (h_v);
1128 int py = Int_val (py_v);
1129 char *s = String_val (ptr_v);
1130 int ret;
1131 void *ptr;
1132 struct page *page;
1133 int slicenum = 0;
1135 ret = sscanf (s, "%p", &ptr);
1136 if (ret != 1) {
1137 errx (1, "cannot parse pointer `%s'", s);
1139 page = ptr;
1141 w = page->pixmap->w;
1143 ARSERT (h >= 0 && "ml_draw wrong h");
1144 ARSERT (py <= page->pixmap->h && "ml_draw wrong py");
1146 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1148 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
1149 struct slice *slice = &page->slices[slicenum];
1150 if (slice->h > py) {
1151 break;
1153 py -= slice->h;
1156 h = MIN (state.h, h);
1157 while (h) {
1158 int th;
1159 struct slice *slice = &page->slices[slicenum];
1161 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
1163 th = MIN (h, slice->h - py);
1164 upload2 (page, slicenum, "upload");
1166 glBegin (GL_QUADS);
1168 glTexCoord2i (0, py);
1169 glVertex2i (0, dispy);
1171 glTexCoord2i (w, py);
1172 glVertex2i (w, dispy);
1174 glTexCoord2i (w, py+th);
1175 glVertex2i (w, dispy + th);
1177 glTexCoord2i (0, py+th);
1178 glVertex2i (0, dispy + th);
1180 glEnd ();
1182 h -= th;
1183 py = 0;
1184 dispy += th;
1185 slicenum += 1;
1188 showsel (page, Int_val (dispy_v) - Int_val (py_v));
1189 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1191 CAMLreturn (Val_unit);
1194 static pdf_link *getlink (struct page *page, int x, int y)
1196 fz_point p;
1197 fz_matrix ctm;
1198 pdf_link *link;
1200 p.x = x + page->pixmap->x;
1201 p.y = y + page->pixmap->y;
1203 ctm = fz_invertmatrix (page->pagedim->ctm);
1204 p = fz_transformpoint (ctm, p);
1206 for (link = page->drawpage->links; link; link = link->next) {
1207 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1208 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1209 return link;
1213 return NULL;
1216 CAMLprim value ml_highlightlinks (value ptr_v, value yoff_v)
1218 CAMLparam2 (ptr_v, yoff_v);
1219 pdf_link *link;
1220 struct page *page;
1221 int xoff, yoff = Int_val (yoff_v);
1222 const char *s = String_val (ptr_v);
1224 if (trylock ("ml_highlightlinks")) {
1225 goto done;
1228 page = parse_pointer ("ml_highlightlinks", s);
1230 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1231 glEnable (GL_LINE_STIPPLE);
1232 glLineStipple (0.5, 0xcccc);
1234 xoff = -page->pixmap->x;
1235 yoff -= page->pixmap->y;
1237 glBegin (GL_QUADS);
1238 for (link = page->drawpage->links; link; link = link->next) {
1239 fz_point p1, p2, p3, p4;
1240 fz_matrix ctm = page->pagedim->ctm;
1242 p1.x = link->rect.x0;
1243 p1.y = link->rect.y0;
1245 p2.x = link->rect.x1;
1246 p2.y = link->rect.y0;
1248 p3.x = link->rect.x1;
1249 p3.y = link->rect.y1;
1251 p4.x = link->rect.x0;
1252 p4.y = link->rect.y1;
1254 p1 = fz_transformpoint (ctm, p1);
1255 p2 = fz_transformpoint (ctm, p2);
1256 p3 = fz_transformpoint (ctm, p3);
1257 p4 = fz_transformpoint (ctm, p4);
1259 switch (link->kind) {
1260 case PDF_LGOTO: glColor3ub (255, 0, 0); break;
1261 case PDF_LURI: glColor3ub (0, 0, 255); break;
1262 default: glColor3ub (0, 0, 0); break;
1265 glVertex2f (p1.x + xoff, p1.y + yoff);
1266 glVertex2f (p2.x + xoff, p2.y + yoff);
1267 glVertex2f (p3.x + xoff, p3.y + yoff);
1268 glVertex2f (p4.x + xoff, p4.y + yoff);
1270 glEnd ();
1272 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1273 glDisable (GL_LINE_STIPPLE);
1274 unlock ("ml_highlightlinks");
1276 done:
1277 CAMLreturn (Val_unit);
1280 static void ensuretext (struct page *page)
1282 if (!page->text) {
1283 fz_error error;
1284 fz_device *tdev;
1286 page->text = fz_newtextspan ();
1287 tdev = fz_newtextdevice (page->text);
1288 error = pdf_runpage (state.xref, page->drawpage, tdev,
1289 page->pagedim->ctm);
1290 if (error) die (error);
1291 fz_freedevice (tdev);
1295 CAMLprim value ml_whatsunder (value ptr_v, value x_v, value y_v)
1297 CAMLparam3 (ptr_v, x_v, y_v);
1298 CAMLlocal3 (ret_v, tup_v, str_v);
1299 pdf_link *link;
1300 struct page *page;
1301 char *s = String_val (ptr_v);
1303 ret_v = Val_int (0);
1304 if (trylock ("ml_whatsunder")) {
1305 goto done;
1308 page = parse_pointer ("ml_whatsunder", s);
1309 link = getlink (page, Int_val (x_v), Int_val (y_v));
1310 if (link) {
1311 switch (link->kind) {
1312 case PDF_LGOTO:
1314 int pageno;
1315 fz_point p;
1316 fz_obj *obj;
1318 pageno = -1;
1319 obj = fz_arrayget (link->dest, 0);
1320 if (fz_isindirect (obj)) {
1321 pageno = pdf_findpageobject (state.xref, obj) - 1;
1323 else if (fz_isint (obj)) {
1324 pageno = fz_toint (obj);
1327 p.x = 0.0;
1328 p.y = 0.0;
1329 if (fz_arraylen (link->dest) > 3) {
1330 fz_obj *xo, *yo;
1332 xo = fz_arrayget (link->dest, 2);
1333 yo = fz_arrayget (link->dest, 3);
1334 if (!fz_isnull (xo) && !fz_isnull (yo)) {
1335 p.x = fz_toint (xo);
1336 p.y = fz_toint (yo);
1337 p = fz_transformpoint (page->pagedim->ctm, p);
1341 tup_v = caml_alloc_tuple (2);
1342 ret_v = caml_alloc_small (1, 1);
1343 Field (tup_v, 0) = Val_int (pageno);
1344 Field (tup_v, 1) = Val_int (p.y);
1345 Field (ret_v, 0) = tup_v;
1347 break;
1349 case PDF_LURI:
1350 str_v = caml_copy_string (fz_tostrbuf (link->dest));
1351 ret_v = caml_alloc_small (1, 0);
1352 Field (ret_v, 0) = str_v;
1353 break;
1355 default:
1356 printd (state.sock, "T unhandled link kind %d", link->kind);
1357 break;
1360 else {
1361 int i, x, y;
1362 fz_textspan *span;
1364 ensuretext (page);
1365 x = Int_val (x_v) + page->pixmap->x;
1366 y = Int_val (y_v) + page->pixmap->y;
1368 for (span = page->text; span; span = span->next) {
1369 for (i = 0; i < span->len; ++i) {
1370 fz_bbox *b;
1371 b = &span->text[i].bbox;
1372 if ((x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1)) {
1373 str_v = caml_copy_string (span->font->name);
1374 ret_v = caml_alloc_small (1, 2);
1375 Field (ret_v, 0) = str_v;
1376 goto unlock;
1381 unlock:
1382 unlock ("ml_whatsunder");
1384 done:
1385 CAMLreturn (ret_v);
1388 CAMLprim value ml_seltext (value ptr_v, value rect_v, value oy_v)
1390 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1391 fz_bbox *b;
1392 struct page *page;
1393 fz_textspan *span;
1394 struct mark first, last;
1395 int i, x0, x1, y0, y1, oy;
1396 char *s = String_val (ptr_v);
1398 if (trylock ("ml_seltext")) {
1399 goto done;
1402 page = parse_pointer ("ml_seltext", s);
1403 ensuretext (page);
1405 oy = Int_val (oy_v);
1406 x0 = Int_val (Field (rect_v, 0));
1407 y0 = Int_val (Field (rect_v, 1));
1408 x1 = Int_val (Field (rect_v, 2));
1409 y1 = Int_val (Field (rect_v, 3));
1411 if (0) {
1412 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1413 glColor3ub (128, 128, 128);
1414 glRecti (x0, y0, x1, y1);
1415 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1418 x0 += page->pixmap->x;
1419 y0 += page->pixmap->y - oy;
1420 x1 += page->pixmap->x;
1421 y1 += page->pixmap->y - oy;
1423 first.span = NULL;
1424 last.span = NULL;
1426 last.i = first.i = 0;
1427 first.span = page->text;
1428 for (span = page->text; span; span = span->next) {
1429 for (i = 0; i < span->len; ++i) {
1430 b = &span->text[i].bbox;
1431 if (x0 >= b->x0 && x0 <= b->x1 && y0 >= b->y0 && y0 <= b->y1) {
1432 first.i = i;
1433 first.span = span;
1435 if (x1 >= b->x0 && x1 <= b->x1 && y1 >= b->y0 && y1 <= b->y1) {
1436 last.i = i;
1437 last.span = span;
1442 if (y1 < y0 || x1 < x0) {
1443 int swap = 0;
1445 if (first.span == last.span) {
1446 swap = 1;
1448 else {
1449 if (y1 < y0) {
1450 for (span = first.span; span && span != last.span;
1451 span = span->next) {
1452 if (span->eol) {
1453 swap = 1;
1454 break;
1460 if (swap) {
1461 i = first.i;
1462 span = first.span;
1463 first.i = last.i;
1464 first.span = last.span;
1465 last.i = i;
1466 last.span = span;
1470 page->fmark = first;
1471 page->lmark = last;
1473 unlock ("ml_seltext");
1475 done:
1476 CAMLreturn (Val_unit);
1479 static int pipespan (FILE *f, fz_textspan *span, int a, int b)
1481 char buf[4];
1482 int i, len, ret;
1484 for (i = a; i <= b; ++i) {
1485 len = runetochar (buf, &span->text[i].c);
1486 ret = fwrite (buf, len, 1, f);
1488 if (ret != 1) {
1489 printd (state.sock, "T failed to write %d bytes ret=%d: %s",
1490 len, ret, strerror (errno));
1491 return -1;
1494 return 0;
1497 CAMLprim value ml_copysel (value ptr_v)
1499 CAMLparam1 (ptr_v);
1500 FILE *f;
1501 struct page *page;
1502 char *s = String_val (ptr_v);
1504 if (trylock ("ml_copysel")) {
1505 goto done;
1508 if (!*s) {
1509 close:
1510 #ifdef USE_XSEL
1511 if (state.xselpipe) {
1512 int ret = pclose (state.xselpipe);
1513 if (ret) {
1514 printd (state.sock, "T failed to close xsel pipe `%s'",
1515 strerror (errno));
1517 state.xselpipe = NULL;
1519 #else
1520 printf ("========================================\n");
1521 #endif
1523 else {
1524 fz_textspan *span;
1526 page = parse_pointer ("ml_sopysel", s);
1528 if (!page->fmark.span || !page->lmark.span) {
1529 printd (state.sock, "T nothing to copy");
1530 goto unlock;
1533 f = stdout;
1534 #ifdef USE_XSEL
1535 if (!state.xselpipe) {
1536 state.xselpipe = popen ("xsel -i", "w");
1537 if (!state.xselpipe) {
1538 printd (state.sock, "T failed to open xsel pipe `%s'",
1539 strerror (errno));
1541 else {
1542 f = state.xselpipe;
1545 else {
1546 f = state.xselpipe;
1548 #endif
1550 for (span = page->fmark.span;
1551 span && span != page->lmark.span->next;
1552 span = span->next) {
1553 int a = span == page->fmark.span ? page->fmark.i : 0;
1554 int b = span == page->lmark.span ? page->lmark.i : span->len - 1;
1555 if (pipespan (f, span, a, b)) {
1556 goto close;
1558 if (span->eol) {
1559 if (putc ('\n', f) == EOF) {
1560 printd (state.sock, "T failed break line on xsel pipe `%s'",
1561 strerror (errno));
1562 goto close;
1566 page->lmark.span = NULL;
1567 page->fmark.span = NULL;
1570 unlock:
1571 unlock ("ml_copysel");
1573 done:
1574 CAMLreturn (Val_unit);
1577 CAMLprim value ml_getpagewh (value pagedimno_v)
1579 CAMLparam1 (pagedimno_v);
1580 CAMLlocal1 (ret_v);
1581 int pagedimno = Int_val (pagedimno_v);
1583 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1584 Store_double_field (ret_v, 0, state.pagedims[pagedimno].box.x0);
1585 Store_double_field (ret_v, 1, state.pagedims[pagedimno].box.x1);
1586 Store_double_field (ret_v, 2, state.pagedims[pagedimno].box.y0);
1587 Store_double_field (ret_v, 3, state.pagedims[pagedimno].box.y1);
1588 CAMLreturn (ret_v);
1591 CAMLprim value ml_init (value sock_v)
1593 #ifndef _WIN32
1594 int ret;
1595 #endif
1596 CAMLparam1 (sock_v);
1598 state.texcount = 256;
1599 state.sliceheight = 24;
1600 state.texform = GL_RGBA;
1601 state.texty = GL_UNSIGNED_BYTE;
1603 fz_accelerate ();
1604 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1605 if (!state.texids) {
1606 err (1, "calloc texids " FMT_s,
1607 state.texcount * sizeof (*state.texids));
1610 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1611 if (!state.texowners) {
1612 err (1, "calloc texowners " FMT_s,
1613 state.texcount * sizeof (*state.texowners));
1616 glGenTextures (state.texcount, state.texids);
1618 #ifdef _WIN32
1619 state.sock = Socket_val (sock_v);
1620 #else
1621 state.sock = Int_val (sock_v);
1622 #endif
1624 state.cache = fz_newglyphcache ();
1625 if (!state.cache) {
1626 errx (1, "fz_newglyphcache failed");
1629 #ifdef _WIN32
1630 InitializeCriticalSection (&critsec);
1631 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
1632 if (state.thread == INVALID_HANDLE_VALUE) {
1633 errx (1, "CreateThread failed: %lx", GetLastError ());
1635 #else
1636 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1637 if (ret) {
1638 errx (1, "pthread_create: %s", strerror (errno));
1640 #endif
1642 CAMLreturn (Val_unit);