Avoid deadlocks
[llpp.git] / link.c
blob12093a02dbe80d9cc0e21c537e832075e64ee590
1 /* lots of code c&p-ed directly from mupdf */
2 #include <errno.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <stdlib.h>
7 #define PIGGYBACK
9 #ifdef _WIN32
10 #define WIN32_LEAN_AND_MEAN
11 #include <windows.h>
12 #include <winsock2.h>
13 #define fionread_arg long
14 #define ssize_t int
15 #define FMT_ss "d"
16 #ifdef _WIN64
17 #define FMT_s "i64u"
18 #else
19 #define FMT_s "u"
20 #endif
21 #endif
23 #ifdef _MSC_VER
24 #pragma warning (disable:4244)
25 #pragma warning (disable:4996)
26 #pragma warning (disable:4995)
27 #define NORETURN __declspec (noreturn)
28 #define UNUSED
29 #define OPTIMIZE
30 #elif defined __GNUC__
31 #define NORETURN __attribute__ ((noreturn))
32 #define UNUSED __attribute__ ((unused))
33 #define OPTIMIZE(n) __attribute__ ((optimize ("O"#n)))
34 #else
35 #define NORETURN
36 #define UNUSED
37 #define OPTIMIZE
38 #endif
40 #ifdef __MINGW32__
41 /* some versions of MingW have non idempotent %p */
42 #include <inttypes.h>
43 #define FMT_ptr PRIxPTR
44 #define FMT_ptr_cast(p) ((intptr_t *) (p))
45 #define FMT_ptr_cast2(p) ((intptr_t) (p))
46 #else
47 #define FMT_ptr "p"
48 #define FMT_ptr_cast(p) (p)
49 #define FMT_ptr_cast2(p) (p)
50 #endif
52 #ifdef _WIN32
53 static void __declspec (noreturn) sockerr (int exitcode, const char *fmt, ...)
55 va_list ap;
57 va_start (ap, fmt);
58 vfprintf (stderr, fmt, ap);
59 va_end (ap);
60 fprintf (stderr, ": wsaerror 0x%x\n", WSAGetLastError ());
61 _exit (exitcode);
63 #else
64 #define FMT_ss "zd"
65 #define FMT_s "zu"
66 #define fionread_arg int
67 #define ioctlsocket ioctl
68 #define sockerr err
69 #include <unistd.h>
70 #endif
72 #include <regex.h>
73 #include <ctype.h>
74 #include <stdarg.h>
75 #include <limits.h>
77 #ifndef _WIN32
78 #include <pthread.h>
79 #include <sys/time.h>
80 #include <sys/types.h>
81 #include <sys/socket.h>
82 #include <sys/ioctl.h>
83 #endif
85 static void NORETURN err (int exitcode, const char *fmt, ...)
87 va_list ap;
88 int savederrno;
90 savederrno = errno;
91 va_start (ap, fmt);
92 vfprintf (stderr, fmt, ap);
93 va_end (ap);
94 fprintf (stderr, ": %s\n", strerror (savederrno));
95 fflush (stderr);
96 _exit (exitcode);
99 static void NORETURN errx (int exitcode, const char *fmt, ...)
101 va_list ap;
103 va_start (ap, fmt);
104 vfprintf (stderr, fmt, ap);
105 va_end (ap);
106 fputc ('\n', stderr);
107 fflush (stderr);
108 _exit (exitcode);
111 #ifdef __APPLE__
112 #include <OpenGL/gl.h>
113 #else
114 #include <GL/gl.h>
115 #endif
117 #ifndef GL_TEXTURE_RECTANGLE_ARB
118 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
119 #endif
121 #ifndef GL_BGRA
122 #define GL_BGRA 0x80E1
123 #endif
125 #ifndef GL_UNSIGNED_INT_8_8_8_8
126 #define GL_UNSIGNED_INT_8_8_8_8 0x8035
127 #endif
129 #ifndef GL_UNSIGNED_INT_8_8_8_8_REV
130 #define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
131 #endif
133 #include <caml/fail.h>
134 #include <caml/alloc.h>
135 #include <caml/memory.h>
136 #include <caml/unixsupport.h>
138 #include <fitz.h>
139 #include <mupdf.h>
141 #include FT_FREETYPE_H
143 #if 0
144 #define lprintf printf
145 #else
146 #define lprintf(...)
147 #endif
149 #define ARSERT(cond) for (;;) { \
150 if (!(cond)) { \
151 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
153 break; \
156 struct slice {
157 int h;
158 int texindex;
161 struct tile {
162 int x, y, w, h;
163 int slicecount;
164 int sliceheight;
165 fz_pixmap *pixmap;
166 struct slice slices[1];
169 struct pagedim {
170 int pageno;
171 int rotate;
172 int left;
173 int tctmready;
174 fz_bbox bounds;
175 fz_rect pagebox;
176 fz_rect mediabox;
177 fz_matrix ctm, zoomctm, lctm, tctm;
180 struct page {
181 int gen;
182 int pageno;
183 int pdimno;
184 fz_text_span *text;
185 pdf_page *drawpage;
186 fz_display_list *dlist;
187 struct mark {
188 int i;
189 fz_text_span *span;
190 } fmark, lmark;
193 #if !defined _WIN32 && !defined __APPLE__
194 #define USE_XSEL
195 #endif
197 struct {
198 int sock;
199 int sliceheight;
200 struct pagedim *pagedims;
201 int pagecount;
202 int pagedimcount;
203 pdf_document *doc;
204 fz_context *ctx;
205 fz_glyph_cache *cache;
206 int w, h;
208 int texindex;
209 int texcount;
210 GLuint *texids;
212 GLenum texiform;
213 GLenum texform;
214 GLenum texty;
216 fz_colorspace *colorspace;
218 struct {
219 int w, h;
220 struct slice *slice;
221 } *texowners;
223 int rotate;
224 int proportional;
225 int trimmargins;
226 int needoutline;
227 int gen;
228 int aalevel;
230 int trimanew;
231 fz_bbox trimfuzz;
232 fz_pixmap *pig;
234 #ifdef _WIN32
235 HANDLE thread;
236 #else
237 pthread_t thread;
238 #endif
239 FILE *xselpipe;
241 FT_Face face;
242 } state;
244 static void UNUSED debug_rect (const char *cap, fz_rect r)
246 printf ("%s(rect) %.2f,%.2f,%.2f,%.2f\n", cap, r.x0, r.y0, r.x1, r.y1);
249 static void UNUSED debug_bbox (const char *cap, fz_bbox r)
251 printf ("%s(bbox) %d,%d,%d,%d\n", cap, r.x0, r.y0, r.x1, r.y1);
254 static void UNUSED debug_matrix (const char *cap, fz_matrix m)
256 printf ("%s(matrix) %.2f,%.2f,%.2f,%.2f %.2f %.2f\n", cap,
257 m.a, m.b, m.c, m.d, m.e, m.f);
260 #ifdef _WIN32
261 static CRITICAL_SECTION critsec;
263 static void lock (void *unused)
265 (void) unused;
266 EnterCriticalSection (&critsec);
269 static void unlock (void *unused)
271 (void) unused;
272 LeaveCriticalSection (&critsec);
275 static int trylock (void *unused)
277 return TryEnterCriticalSection (&critsec) == 0;
279 #else
280 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
282 static void lock (const char *cap)
284 int ret = pthread_mutex_lock (&mutex);
285 if (ret) {
286 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
290 static void unlock (const char *cap)
292 int ret = pthread_mutex_unlock (&mutex);
293 if (ret) {
294 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
298 static int trylock (const char *cap)
300 int ret = pthread_mutex_trylock (&mutex);
302 if (ret && ret != EBUSY) {
303 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
305 return ret == EBUSY;
307 #endif
309 static void *parse_pointer (const char *cap, const char *s)
311 int ret;
312 void *ptr;
314 ret = sscanf (s, "%" FMT_ptr, FMT_ptr_cast (&ptr));
315 if (ret != 1) {
316 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
318 return ptr;
321 static int hasdata (int sock)
323 int ret;
324 fionread_arg avail;
325 ret = ioctlsocket (sock, FIONREAD, &avail);
326 if (ret) sockerr (1, "hasdata: FIONREAD error ret=%d", ret);
327 return avail > 0;
330 static double now (void)
332 #ifdef _WIN32
333 FILETIME ft;
334 uint64 tmp;
336 GetSystemTimeAsFileTime (&ft);
337 tmp = ft.dwHighDateTime;
338 tmp <<= 32;
339 tmp |= ft.dwLowDateTime;
340 return tmp * 1e-7;
341 #else
342 struct timeval tv;
344 if (gettimeofday (&tv, NULL)) {
345 err (1, "gettimeofday");
347 return tv.tv_sec + tv.tv_usec*1e-6;
348 #endif
351 static void readdata (int fd, char *p, int size)
353 ssize_t n;
355 n = recv (fd, p, size, 0);
356 if (n - size) {
357 if (!n) errx (1, "EOF while reading");
358 sockerr (1, "recv (req %d, ret %" FMT_ss ")", size, n);
362 static void writedata (int fd, char *p, int size)
364 char buf[4];
365 ssize_t n;
367 buf[0] = (size >> 24) & 0xff;
368 buf[1] = (size >> 16) & 0xff;
369 buf[2] = (size >> 8) & 0xff;
370 buf[3] = (size >> 0) & 0xff;
372 n = send (fd, buf, 4, 0);
373 if (n != 4) {
374 if (!n) errx (1, "EOF while writing length");
375 sockerr (1, "send %" FMT_ss, n);
378 n = send (fd, p, size, 0);
379 if (n - size) {
380 if (!n) errx (1, "EOF while writing data");
381 sockerr (1, "send (req %d, ret %" FMT_ss ")", size, n);
385 static void
386 #ifdef __GNUC__
387 __attribute__ ((format (printf, 2, 3)))
388 #endif
389 printd (int fd, const char *fmt, ...)
391 int size = 200, len;
392 va_list ap;
393 char *buf;
395 buf = malloc (size);
396 for (;;) {
397 if (!buf) err (errno, "malloc for temp buf (%d bytes) failed", size);
399 va_start (ap, fmt);
400 len = vsnprintf (buf, size, fmt, ap);
401 va_end (ap);
403 if (len > -1 && len < size) {
404 writedata (fd, buf, len);
405 break;
408 if (len > -1) {
409 size = len + 1;
411 else {
412 size *= 2;
414 buf = realloc (buf, size);
416 free (buf);
419 static void openxref (char *filename, char *password)
421 int i;
423 for (i = 0; i < state.texcount; ++i) {
424 state.texowners[i].w = -1;
425 state.texowners[i].slice = NULL;
428 if (state.doc) {
429 pdf_close_document (state.doc);
430 state.doc = NULL;
433 if (state.pagedims) {
434 free (state.pagedims);
435 state.pagedims = NULL;
437 state.pagedimcount = 0;
439 fz_set_aa_level (state.ctx, state.aalevel);
440 state.doc = pdf_open_document (state.ctx, filename);
441 if (pdf_needs_password (state.doc)) {
442 int okay = pdf_authenticate_password (state.doc, password);
443 if (!okay) {
444 errx (1, "invalid password");
447 state.pagecount = pdf_count_pages (state.doc);
450 static void pdfinfo (void)
452 fz_obj *infoobj;
454 printd (state.sock, "info PDF version\t%d.%d",
455 state.doc->version / 10, state.doc->version % 10);
457 infoobj = fz_dict_gets (state.doc->trailer, "Info");
458 if (infoobj) {
459 int i;
460 char *s;
461 char *items[] = { "Title", "Author", "Creator",
462 "Producer", "CreationDate" };
464 for (i = 0; i < sizeof (items) / sizeof (*items); ++i) {
465 fz_obj *obj = fz_dict_gets (infoobj, items[i]);
466 s = pdf_to_utf8 (state.ctx, obj);
467 if (*s) {
468 if (i == 0) {
469 printd (state.sock, "title %s", s);
471 printd (state.sock, "info %s\t%s", items[i], s);
473 fz_free (state.ctx, s);
476 printd (state.sock, "info Pages\t%d", state.pagecount);
477 printd (state.sock, "info Dimensions\t%d", state.pagedimcount);
478 printd (state.sock, "infoend");
481 static int readlen (int fd)
483 ssize_t n;
484 unsigned char p[4];
486 n = recv (fd, p, 4, 0);
487 if (n != 4) {
488 if (!n) errx (1, "EOF while reading length");
489 sockerr (1, "recv %" FMT_ss, n);
492 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
495 static void unlinktile (struct tile *tile)
497 int i;
499 for (i = 0; i < tile->slicecount; ++i) {
500 struct slice *s = &tile->slices[i];
502 if (s->texindex != -1) {
503 if (state.texowners[s->texindex].slice == s) {
504 state.texowners[s->texindex].slice = NULL;
510 static void freepage (struct page *page)
512 if (page->text) {
513 fz_free_text_span (state.ctx, page->text);
515 pdf_free_page (state.ctx, page->drawpage);
516 fz_free_display_list (state.ctx, page->dlist);
517 free (page);
520 static void freetile (struct tile *tile)
522 unlinktile (tile);
523 #ifndef PIGGYBACK
524 fz_drop_pixmap (state.ctx, tile->pixmap);
525 #else
526 if (state.pig) {
527 fz_drop_pixmap (state.ctx, state.pig);
529 state.pig = tile->pixmap;
530 #endif
531 free (tile);
534 #ifdef __ALTIVEC__
535 #include <altivec.h>
537 static int cacheline32bytes;
538 extern char **environ;
540 static void __attribute__ ((constructor)) clcheck (void)
542 char **envp = environ;
543 unsigned long *auxv;
545 while (*envp++);
547 for (auxv = (unsigned long *) envp; *auxv != 0; auxv += 2) {
548 if (*auxv == 19) {
549 cacheline32bytes = auxv[1] == 32;
550 return;
555 static void OPTIMIZE (3) clearpixmap (fz_pixmap *pixmap)
557 if (cacheline32bytes) {
558 intptr_t a1, a2, diff;
559 size_t sizea, i, size = pixmap->w * pixmap->h * pixmap->n;
560 vector unsigned char v = vec_splat_u8 (-1);
561 vector unsigned char *p;
563 a1 = a2 = (intptr_t) pixmap->samples;
564 a2 = (a1 + 31) & ~31;
565 diff = a2 - a1;
566 sizea = size - diff;
567 p = (void *) a2;
569 while (a1 != a2) *(char *) a1++ = 0xff;
570 for (i = 0; i < (sizea & ~31); i += 32) {
571 __asm volatile ("dcbz %0, %1"::"b"(a2),"r"(i));
572 vec_st (v, i, p);
573 vec_st (v, i + 16, p);
575 while (i < sizea) *((char *) a1 + i++) = 0xff;
577 else fz_clear_pixmap_with_color (pixmap, 0xff);
579 #else
580 #define clearpixmap(p) fz_clear_pixmap_with_color (p, 0xff)
581 #endif
583 static fz_matrix trimctm (pdf_page *page, int pindex)
585 fz_matrix ctm;
586 struct pagedim *pdim = &state.pagedims[pindex];
588 if (!pdim->tctmready) {
589 if (state.trimmargins) {
590 fz_rect realbox;
592 ctm = fz_concat (fz_rotate (-pdim->rotate), fz_scale (1, -1));
593 realbox = fz_transform_rect (ctm, pdim->mediabox);
594 ctm = fz_concat (ctm, fz_translate (-realbox.x0, -realbox.y0));
595 ctm = fz_concat (fz_invert_matrix (page->ctm), ctm);
597 else {
598 ctm = fz_identity;
600 pdim->tctm = ctm;
601 pdim->tctmready = 1;
603 return pdim->tctm;
606 static fz_matrix pagectm (struct page *page)
608 return fz_concat (trimctm (page->drawpage, page->pdimno),
609 state.pagedims[page->pdimno].ctm);
612 static void *loadpage (int pageno, int pindex)
614 fz_device *dev;
615 struct page *page = NULL;
617 page = calloc (sizeof (struct page), 1);
618 if (!page) {
619 err (1, "calloc page %d", pageno);
622 page->drawpage = pdf_load_page (state.doc, pageno);
623 page->dlist = fz_new_display_list (state.ctx);
624 dev = fz_new_list_device (state.ctx, page->dlist);
625 pdf_run_page (state.doc, page->drawpage, dev, fz_identity, NULL);
626 fz_free_device (dev);
628 page->pdimno = pindex;
629 page->pageno = pageno;
630 page->gen = state.gen;
632 return page;
635 static struct tile *alloctile (int h)
637 int i;
638 int slicecount;
639 size_t tilesize;
640 struct tile *tile;
642 slicecount = (h + state.sliceheight - 1) / state.sliceheight;
643 tilesize = sizeof (*tile) + ((slicecount - 1) * sizeof (struct slice));
644 tile = calloc (tilesize, 1);
645 if (!tile) {
646 err (1, "can not allocate tile (%" FMT_s " bytes)", tilesize);
648 for (i = 0; i < slicecount; ++i) {
649 int sh = MIN (h, state.sliceheight);
650 tile->slices[i].h = sh;
651 tile->slices[i].texindex = -1;
652 h -= sh;
654 tile->slicecount = slicecount;
655 tile->sliceheight = state.sliceheight;
656 return tile;
659 static struct tile *rendertile (struct page *page, int x, int y, int w, int h)
661 fz_bbox bbox;
662 fz_device *dev;
663 struct tile *tile;
664 struct pagedim *pdim;
666 tile = alloctile (h);
667 pdim = &state.pagedims[page->pdimno];
669 bbox = pdim->bounds;
670 bbox.x0 += x;
671 bbox.y0 += y;
672 bbox.x1 = bbox.x0 + w;
673 bbox.y1 = bbox.y0 + h;
675 if (state.pig) {
676 if (state.pig->w == w
677 && state.pig->h == h
678 && state.pig->colorspace == state.colorspace) {
679 tile->pixmap = state.pig;
680 tile->pixmap->x = bbox.x0;
681 tile->pixmap->y = bbox.y0;
683 else {
684 fz_drop_pixmap (state.ctx, state.pig);
686 state.pig = NULL;
688 if (!tile->pixmap) {
689 tile->pixmap =
690 fz_new_pixmap_with_rect (state.ctx, state.colorspace, bbox);
693 tile->w = w;
694 tile->h = h;
695 clearpixmap (tile->pixmap);
696 dev = fz_new_draw_device (state.ctx, tile->pixmap);
697 fz_execute_display_list (page->dlist, dev, pagectm (page), bbox, NULL);
698 fz_free_device (dev);
700 return tile;
703 static void initpdims (void)
705 int pageno;
706 double start, end;
708 start = now ();
709 for (pageno = 0; pageno < state.pagecount; ++pageno) {
710 int rotate;
711 fz_obj *pageobj;
712 struct pagedim *p;
713 fz_rect mediabox, cropbox;
715 pageobj = state.doc->page_objs[pageno];
717 if (state.trimmargins) {
718 fz_obj *obj;
719 pdf_page *page;
721 page = pdf_load_page (state.doc, pageno);
722 obj = fz_dict_gets (pageobj, "llpp.TrimBox");
723 if (state.trimanew || !obj) {
724 fz_rect rect;
725 fz_bbox bbox;
726 fz_matrix ctm;
727 fz_device *dev;
729 dev = fz_new_bbox_device (state.ctx, &bbox);
730 dev->hints |= FZ_IGNORE_SHADE;
731 ctm = fz_invert_matrix (page->ctm);
732 pdf_run_page (state.doc, page, dev, fz_identity, NULL);
733 fz_free_device (dev);
735 rect.x0 = bbox.x0 + state.trimfuzz.x0;
736 rect.x1 = bbox.x1 + state.trimfuzz.x1;
737 rect.y0 = bbox.y0 + state.trimfuzz.y0;
738 rect.y1 = bbox.y1 + state.trimfuzz.y1;
739 rect = fz_transform_rect (ctm, rect);
740 rect = fz_intersect_rect (rect, page->mediabox);
742 if (fz_is_empty_rect (rect)) {
743 mediabox = page->mediabox;
745 else {
746 mediabox = rect;
749 obj = fz_new_array (state.ctx, 4);
750 fz_array_push (obj, fz_new_real (state.ctx, mediabox.x0));
751 fz_array_push (obj, fz_new_real (state.ctx, mediabox.y0));
752 fz_array_push (obj, fz_new_real (state.ctx, mediabox.x1));
753 fz_array_push (obj, fz_new_real (state.ctx, mediabox.y1));
754 fz_dict_puts (pageobj, "llpp.TrimBox", obj);
756 else {
757 mediabox.x0 = fz_to_real (fz_array_get (obj, 0));
758 mediabox.y0 = fz_to_real (fz_array_get (obj, 1));
759 mediabox.x1 = fz_to_real (fz_array_get (obj, 2));
760 mediabox.y1 = fz_to_real (fz_array_get (obj, 3));
763 rotate = page->rotate;
764 pdf_free_page (state.ctx, page);
766 printd (state.sock, "progress %f Trimming %d",
767 (double) (pageno + 1) / state.pagecount,
768 pageno + 1);
770 else {
771 mediabox = pdf_to_rect (state.ctx, fz_dict_gets (pageobj, "MediaBox"));
772 if (fz_is_empty_rect (mediabox)) {
773 fprintf (stderr, "cannot find page size for page %d\n", pageno+1);
774 mediabox.x0 = 0;
775 mediabox.y0 = 0;
776 mediabox.x1 = 612;
777 mediabox.y1 = 792;
780 cropbox = pdf_to_rect (state.ctx, fz_dict_gets (pageobj, "CropBox"));
781 if (!fz_is_empty_rect (cropbox)) {
782 mediabox = fz_intersect_rect (mediabox, cropbox);
784 rotate = fz_to_int (fz_dict_gets (pageobj, "Rotate"));
787 if (state.pagedimcount == 0
788 || (p = &state.pagedims[state.pagedimcount-1], p->rotate != rotate)
789 || memcmp (&p->mediabox, &mediabox, sizeof (mediabox))) {
790 size_t size;
792 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
793 state.pagedims = realloc (state.pagedims, size);
794 if (!state.pagedims) {
795 err (1, "realloc pagedims to %" FMT_s " (%d elems)",
796 size, state.pagedimcount + 1);
799 p = &state.pagedims[state.pagedimcount++];
800 p->rotate = rotate;
801 p->mediabox = mediabox;
802 p->pageno = pageno;
805 end = now ();
806 if (state.trimmargins) {
807 printd (state.sock, "progress 1 Trimmed %d pages in %f seconds",
808 state.pagecount, end - start);
810 else {
811 printd (state.sock, "vmsg Processed %d pages in %f seconds",
812 state.pagecount, end - start);
814 state.trimanew = 0;
817 static void layout (void)
819 int pindex;
820 fz_rect box;
821 fz_matrix ctm;
822 double zoom, w, maxw = 0;
823 struct pagedim *p = state.pagedims;
825 if (state.proportional) {
826 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
827 box = fz_transform_rect (fz_rotate (p->rotate + state.rotate),
828 p->mediabox);
829 w = box.x1 - box.x0;
830 maxw = MAX (w, maxw);
834 p = state.pagedims;
835 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
836 fz_bbox bbox;
838 ctm = fz_rotate (state.rotate);
839 box = fz_transform_rect (fz_rotate (p->rotate + state.rotate),
840 p->mediabox);
841 w = box.x1 - box.x0;
843 if (state.proportional) {
844 double scale = w / maxw;
845 zoom = (state.w / w) * scale;
847 else {
848 zoom = state.w / w;
851 p->zoomctm = fz_scale (zoom, zoom);
852 ctm = fz_concat (p->zoomctm, ctm);
854 p->pagebox = fz_transform_rect (fz_rotate (p->rotate), p->mediabox);
855 p->pagebox.x1 -= p->pagebox.x0;
856 p->pagebox.y1 -= p->pagebox.y0;
857 p->pagebox.x0 = 0;
858 p->pagebox.y0 = 0;
859 bbox = fz_round_rect (fz_transform_rect (ctm, p->pagebox));
861 p->bounds = bbox;
862 p->left = state.proportional ? ((maxw - w) * zoom) / 2.0 : 0;
863 p->ctm = ctm;
865 ctm = fz_identity;
866 ctm = fz_concat (ctm, fz_translate (0, -p->mediabox.y1));
867 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
868 ctm = fz_concat (ctm, fz_rotate (p->rotate + state.rotate));
869 p->lctm = ctm;
871 p->tctmready = 0;
874 while (p-- != state.pagedims) {
875 int w = p->bounds.x1 - p->bounds.x0;
876 int h = p->bounds.y1 - p->bounds.y0;
878 printd (state.sock, "pdim %d %d %d %d", p->pageno, w, h, p->left);
882 static void recurse_outline (fz_outline *outline, int level)
884 while (outline) {
885 fz_link_dest *dest;
886 int i, top = 0;
887 struct pagedim *pdim = state.pagedims;
889 dest = &outline->dest;
890 for (i = 0; i < state.pagedimcount; ++i) {
891 if (state.pagedims[i].pageno > dest->ld.gotor.page)
892 break;
893 pdim = &state.pagedims[i];
895 if (dest->ld.gotor.flags & fz_link_flag_t_valid) {
896 fz_point p;
897 p.x = 0;
898 p.y = dest->ld.gotor.lt.y;
899 p = fz_transform_point (pdim->lctm, p);
900 top = p.y;
902 if (dest->ld.gotor.page >= 0 && dest->ld.gotor.page < 1<<30) {
903 int h;
904 double y0, y1;
906 y0 = MIN (pdim->bounds.y0, pdim->bounds.y1);
907 y1 = MAX (pdim->bounds.y0, pdim->bounds.y1);
908 h = y1 - y0;
909 printd (state.sock, "o %d %d %d %d %s",
910 level, dest->ld.gotor.page, top, h, outline->title);
912 if (outline->down) {
913 recurse_outline (outline->down, level + 1);
915 outline = outline->next;
919 static void process_outline (void)
921 fz_outline *outline;
923 if (!state.needoutline) return;
925 state.needoutline = 0;
926 outline = pdf_load_outline (state.doc);
927 if (outline) {
928 recurse_outline (outline, 0);
929 fz_free_outline (outline);
933 static int comparespans (const void *l, const void *r)
935 fz_text_span const *const*ls = l;
936 fz_text_span const *const*rs = r;
937 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
940 /* wishful thinking function */
941 static void search (regex_t *re, int pageno, int y, int forward)
943 int i, j;
944 int ret;
945 char *p;
946 char buf[256];
947 fz_matrix ctm;
948 fz_device *tdev;
949 pdf_page *drawpage;
950 fz_text_span *text, *span, **pspan;
951 struct pagedim *pdim, *pdimprev;
952 int stop = 0;
953 int niters = 0;
954 int nspans;
955 double start, end;
957 start = now ();
958 while (pageno >= 0 && pageno < state.pagecount && !stop) {
959 if (niters++ == 5) {
960 niters = 0;
961 if (hasdata (state.sock)) {
962 printd (state.sock,
963 "progress 1 attention requested aborting search at %d",
964 pageno);
965 stop = 1;
967 else {
968 printd (state.sock, "progress %f searching in page %d",
969 (double) (pageno + 1) / state.pagecount,
970 pageno);
973 pdimprev = NULL;
974 for (i = 0; i < state.pagedimcount; ++i) {
975 pdim = &state.pagedims[i];
976 if (pdim->pageno == pageno) {
977 goto found;
979 if (pdim->pageno > pageno) {
980 pdim = pdimprev;
981 goto found;
983 pdimprev = pdim;
985 pdim = pdimprev;
986 found:
988 drawpage = pdf_load_page (state.doc, pageno);
990 text = fz_new_text_span (state.ctx);
991 tdev = fz_new_text_device (state.ctx, text);
992 pdf_run_page (state.doc, drawpage, tdev, fz_identity, NULL);
993 fz_free_device (tdev);
995 nspans = 0;
996 for (span = text; span; span = span->next) {
997 nspans++;
999 pspan = malloc (sizeof (void *) * nspans);
1000 if (!pspan) {
1001 err (1, "malloc span pointers %" FMT_s, sizeof (void *) * nspans);
1003 for (i = 0, span = text; span; span = span->next, ++i) {
1004 pspan[i] = span;
1006 qsort (pspan, nspans, sizeof (fz_text_span *), comparespans);
1008 j = forward ? 0 : nspans - 1;
1009 while (nspans--) {
1010 regmatch_t rm;
1012 span = pspan[j];
1013 j += forward ? 1 : -1;
1014 p = buf;
1015 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
1016 if (forward) {
1017 if (span->text[i].bbox.y0 < y + 1) {
1018 continue;
1021 else {
1022 if (span->text[i].bbox.y0 > y - 1) {
1023 continue;
1026 if (span->text[i].c < 256) {
1027 *p++ = span->text[i].c;
1029 else {
1030 *p++ = '?';
1033 if (p == buf) {
1034 continue;
1036 *p++ = 0;
1038 ret = regexec (re, buf, 1, &rm, 0);
1039 if (ret) {
1040 if (ret != REG_NOMATCH) {
1041 size_t size;
1042 char errbuf[80];
1043 size = regerror (ret, re, errbuf, sizeof (errbuf));
1044 printd (state.sock,
1045 "msg regexec error `%.*s'",
1046 (int) size, errbuf);
1047 fz_free_text_span (state.ctx, text);
1048 pdf_free_page (state.ctx, drawpage);
1049 free (pspan);
1050 return;
1053 else {
1054 fz_bbox *sb, *eb;
1055 fz_point p1, p2, p3, p4;
1057 sb = &span->text[rm.rm_so].bbox;
1058 eb = &span->text[rm.rm_eo - 1].bbox;
1060 p1.x = sb->x0;
1061 p1.y = sb->y0;
1062 p2.x = eb->x1;
1063 p2.y = sb->y0;
1064 p3.x = eb->x1;
1065 p3.y = eb->y1;
1066 p4.x = sb->x0;
1067 p4.y = eb->y1;
1069 trimctm (drawpage, pdim - state.pagedims);
1070 ctm = fz_concat (pdim->tctm, pdim->zoomctm);
1072 p1 = fz_transform_point (ctm, p1);
1073 p2 = fz_transform_point (ctm, p2);
1074 p3 = fz_transform_point (ctm, p3);
1075 p4 = fz_transform_point (ctm, p4);
1077 if (!stop) {
1078 printd (state.sock,
1079 "firstmatch %d %d %f %f %f %f %f %f %f %f",
1080 pageno, 1,
1081 p1.x, p1.y,
1082 p2.x, p2.y,
1083 p3.x, p3.y,
1084 p4.x, p4.y);
1086 printd (state.sock,
1087 "progress 1 found at %d `%.*s' in %f sec",
1088 pageno, (int) (rm.rm_eo - rm.rm_so), &buf[rm.rm_so],
1089 now () - start);
1091 else {
1092 printd (state.sock,
1093 "match %d %d %f %f %f %f %f %f %f %f",
1094 pageno, 2,
1095 p1.x, p1.y,
1096 p2.x, p2.y,
1097 p3.x, p3.y,
1098 p4.x, p4.y);
1100 stop = 1;
1103 if (forward) {
1104 pageno += 1;
1105 y = 0;
1107 else {
1108 pageno -= 1;
1109 y = INT_MAX;
1111 fz_free_text_span (state.ctx, text);
1112 pdf_free_page (state.ctx, drawpage);
1113 free (pspan);
1115 end = now ();
1116 if (!stop) {
1117 printd (state.sock, "progress 1 no matches %f sec", end - start);
1119 printd (state.sock, "clearrects");
1122 static void set_tex_params (int colorspace)
1124 switch (colorspace) {
1125 case 0:
1126 state.texiform = GL_RGBA8;
1127 state.texform = GL_RGBA;
1128 state.texty = GL_UNSIGNED_BYTE;
1129 state.colorspace = fz_device_rgb;
1130 break;
1131 case 1:
1132 state.texiform = GL_RGBA8;
1133 state.texform = GL_BGRA;
1134 state.texty = fz_is_big_endian ()
1135 ? GL_UNSIGNED_INT_8_8_8_8
1136 : GL_UNSIGNED_INT_8_8_8_8_REV;
1137 state.colorspace = fz_device_bgr;
1138 break;
1139 case 2:
1140 state.texiform = GL_LUMINANCE_ALPHA;
1141 state.texform = GL_LUMINANCE_ALPHA;
1142 state.texty = GL_UNSIGNED_BYTE;
1143 state.colorspace = fz_device_gray;
1144 break;
1145 default:
1146 errx (1, "invalid colorspce %d", colorspace);
1150 static void realloctexts (int texcount)
1152 size_t size;
1154 if (texcount == state.texcount) return;
1156 if (texcount < state.texcount) {
1157 glDeleteTextures (state.texcount - texcount,
1158 state.texids + texcount);
1161 size = texcount * sizeof (*state.texids);
1162 state.texids = realloc (state.texids, size);
1163 if (!state.texids) {
1164 err (1, "realloc texids %" FMT_s, size);
1167 size = texcount * sizeof (*state.texowners);
1168 state.texowners = realloc (state.texowners, size);
1169 if (!state.texowners) {
1170 err (1, "realloc texowners %" FMT_s, size);
1172 if (texcount > state.texcount) {
1173 int i;
1175 glGenTextures (texcount - state.texcount,
1176 state.texids + state.texcount);
1177 for (i = state.texcount; i < texcount; ++i) {
1178 state.texowners[i].w = -1;
1179 state.texowners[i].slice = NULL;
1182 state.texcount = texcount;
1183 state.texindex = 0;
1186 static
1187 #ifdef _WIN32
1188 DWORD _stdcall
1189 #else
1190 void *
1191 #endif
1192 mainloop (void *unused)
1194 char *p = NULL;
1195 int len, ret, oldlen = 0;
1197 for (;;) {
1198 len = readlen (state.sock);
1199 if (len == 0) {
1200 errx (1, "readlen returned 0");
1203 if (oldlen < len + 1) {
1204 p = realloc (p, len + 1);
1205 if (!p) {
1206 err (1, "realloc %d failed", len + 1);
1208 oldlen = len + 1;
1210 readdata (state.sock, p, len);
1211 p[len] = 0;
1213 if (!strncmp ("open", p, 4)) {
1214 size_t filenamelen;
1215 char *password;
1216 char *filename = p + 5;
1218 filenamelen = strlen (filename);
1219 password = filename + filenamelen + 1;
1221 openxref (filename, password);
1222 printd (state.sock, "msg Opened %s (press h/F1 to get help)",
1223 filename);
1224 pdfinfo ();
1225 initpdims ();
1226 state.needoutline = 1;
1228 else if (!strncmp ("cs", p, 2)) {
1229 int i, colorspace;
1231 ret = sscanf (p + 2, " %d", &colorspace);
1232 if (ret != 1) {
1233 errx (1, "malformed cs `%.*s' ret=%d", len, p, ret);
1235 lock ("cs");
1236 set_tex_params (colorspace);
1237 for (i = 0; i < state.texcount; ++i) {
1238 state.texowners[i].w = -1;
1239 state.texowners[i].slice = NULL;
1241 unlock ("cs");
1243 else if (!strncmp ("freepage", p, 8)) {
1244 void *ptr;
1246 ret = sscanf (p + 8, " %" FMT_ptr, FMT_ptr_cast (&ptr));
1247 if (ret != 1) {
1248 errx (1, "malformed freepage `%.*s' ret=%d", len, p, ret);
1250 freepage (ptr);
1252 else if (!strncmp ("freetile", p, 8)) {
1253 void *ptr;
1255 ret = sscanf (p + 8, " %" FMT_ptr, FMT_ptr_cast (&ptr));
1256 if (ret != 1) {
1257 errx (1, "malformed freetile `%.*s' ret=%d", len, p, ret);
1259 freetile (ptr);
1261 else if (!strncmp ("search", p, 6)) {
1262 int icase, pageno, y, ret, len2, forward;
1263 char *pattern;
1264 regex_t re;
1266 ret = sscanf (p + 6, " %d %d %d %d,%n",
1267 &icase, &pageno, &y, &forward, &len2);
1268 if (ret != 4) {
1269 errx (1, "malformed search `%s' ret=%d", p, ret);
1272 pattern = p + 6 + len2;
1273 ret = regcomp (&re, pattern,
1274 REG_EXTENDED | (icase ? REG_ICASE : 0));
1275 if (ret) {
1276 char errbuf[80];
1277 size_t size;
1279 size = regerror (ret, &re, errbuf, sizeof (errbuf));
1280 printd (state.sock, "msg regcomp failed `%.*s'",
1281 (int) size, errbuf);
1283 else {
1284 search (&re, pageno, y, forward);
1285 regfree (&re);
1288 else if (!strncmp ("geometry", p, 8)) {
1289 int w, h;
1291 printd (state.sock, "clear");
1292 ret = sscanf (p + 8, " %d %d", &w, &h);
1293 if (ret != 2) {
1294 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
1297 lock ("geometry");
1298 state.h = h;
1299 if (w != state.w) {
1300 int i;
1301 state.w = w;
1302 for (i = 0; i < state.texcount; ++i) {
1303 state.texowners[i].slice = NULL;
1306 layout ();
1307 process_outline ();
1308 state.gen++;
1309 unlock ("geometry");
1310 printd (state.sock, "continue %d", state.pagecount);
1312 else if (!strncmp ("reqlayout", p, 9)) {
1313 int rotate, proportional;
1315 printd (state.sock, "clear");
1316 ret = sscanf (p + 9, " %d %d", &rotate, &proportional);
1317 if (ret != 2) {
1318 errx (1, "bad reqlayout line `%.*s' ret=%d", len, p, ret);
1320 lock ("reqlayout");
1321 state.rotate = rotate;
1322 state.proportional = proportional;
1323 layout ();
1324 unlock ("reqlayout");
1325 printd (state.sock, "continue %d", state.pagecount);
1327 else if (!strncmp ("page", p, 4)) {
1328 double a, b;
1329 struct page *page;
1330 int pageno, pindex, ret;
1332 ret = sscanf (p + 4, " %d %d", &pageno, &pindex);
1333 if (ret != 2) {
1334 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
1337 lock ("page");
1338 a = now ();
1339 page = loadpage (pageno, pindex);
1340 b = now ();
1341 unlock ("page");
1343 printd (state.sock, "page %" FMT_ptr " %f",
1344 FMT_ptr_cast2 (page), b - a);
1346 else if (!strncmp ("tile", p, 4)) {
1347 int x, y, w, h, ret;
1348 struct page *page;
1349 struct tile *tile;
1350 double a, b;
1352 ret = sscanf (p + 4, " %" FMT_ptr " %d %d %d %d",
1353 FMT_ptr_cast (&page), &x, &y, &w, &h);
1354 if (ret != 5) {
1355 errx (1, "bad tile line `%.*s' ret=%d", len, p, ret);
1358 lock ("tile");
1359 a = now ();
1360 tile = rendertile (page, x, y, w, h);
1361 b = now ();
1362 unlock ("tile");
1364 printd (state.sock, "tile %d %d %" FMT_ptr " %u %f",
1365 x, y,
1366 FMT_ptr_cast2 (tile),
1367 tile->w * tile->h * tile->pixmap->n,
1368 b - a);
1370 else if (!strncmp ("settrim", p, 7)) {
1371 int trimmargins;
1372 fz_bbox fuzz;
1374 ret = sscanf (p + 7, " %d %d %d %d %d", &trimmargins,
1375 &fuzz.x0, &fuzz.y0, &fuzz.x1, &fuzz.y1);
1376 if (ret != 5) {
1377 errx (1, "malformed settrim `%.*s' ret=%d", len, p, ret);
1379 printd (state.sock, "clear");
1380 lock ("settrim");
1381 state.trimmargins = trimmargins;
1382 state.needoutline = 1;
1383 if (memcmp (&fuzz, &state.trimfuzz, sizeof (fuzz))) {
1384 state.trimanew = 1;
1385 state.trimfuzz = fuzz;
1387 state.pagedimcount = 0;
1388 free (state.pagedims);
1389 state.pagedims = NULL;
1390 initpdims ();
1391 layout ();
1392 process_outline ();
1393 unlock ("settrim");
1394 printd (state.sock, "continue %d", state.pagecount);
1396 else if (!strncmp ("sliceh", p, 6)) {
1397 int h;
1399 ret = sscanf (p + 6, " %d", &h);
1400 if (ret != 1) {
1401 errx (1, "malformed sliceh `%.*s' ret=%d", len, p, ret);
1403 if (h != state.sliceheight) {
1404 int i;
1406 state.sliceheight = h;
1407 for (i = 0; i < state.texcount; ++i) {
1408 state.texowners[i].w = -1;
1409 state.texowners[i].h = -1;
1410 state.texowners[i].slice = NULL;
1414 else if (!strncmp ("interrupt", p, 9)) {
1415 printd (state.sock, "vmsg interrupted");
1417 else if (!strncmp ("quit", p, 4)) {
1418 return 0;
1420 else {
1421 errx (1, "unknown command %.*s", len, p);
1424 return 0;
1427 CAMLprim value ml_realloctexts (value texcount_v)
1429 CAMLparam1 (texcount_v);
1430 int ok;
1432 if (trylock ("ml_realloctexts")) {
1433 ok = 0;
1434 goto done;
1436 realloctexts (Int_val (texcount_v));
1437 ok = 1;
1438 unlock ("ml_realloctexts");
1440 done:
1441 CAMLreturn (Val_bool (ok));
1444 static void showsel (struct page *page, int ox, int oy)
1446 fz_bbox bbox;
1447 fz_text_span *span;
1448 struct mark first, last;
1450 first = page->fmark;
1451 last = page->lmark;
1453 if (!first.span || !last.span) return;
1455 glEnable (GL_BLEND);
1456 glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA);
1457 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1459 ox -= state.pagedims[page->pdimno].bounds.x0;
1460 oy -= state.pagedims[page->pdimno].bounds.y0;
1461 for (span = first.span; span; span = span->next) {
1462 int i, j, k;
1464 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0;
1466 j = 0;
1467 k = span->len - 1;
1469 if (span == page->fmark.span && span == page->lmark.span) {
1470 j = MIN (first.i, last.i);
1471 k = MAX (first.i, last.i);
1473 else if (span == first.span) {
1474 j = first.i;
1476 else if (span == last.span) {
1477 k = last.i;
1480 for (i = j; i <= k; ++i) {
1481 bbox = fz_union_bbox (bbox, span->text[i].bbox);
1483 lprintf ("%d %d %d %d oy=%d ox=%d\n",
1484 bbox.x0,
1485 bbox.y0,
1486 bbox.x1,
1487 bbox.y1,
1488 oy, ox);
1490 glRecti (bbox.x0 + ox, bbox.y0 + oy, bbox.x1 + ox, bbox.y1 + oy);
1492 if (span == last.span) break;
1494 glDisable (GL_BLEND);
1497 static void highlightlinks (struct page *page, int xoff, int yoff)
1499 fz_link *link;
1500 fz_matrix ctm;
1502 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1503 glEnable (GL_LINE_STIPPLE);
1504 glLineStipple (0.5, 0xcccc);
1506 ctm = fz_concat (pagectm (page), fz_translate (xoff, yoff));
1508 glBegin (GL_QUADS);
1509 for (link = page->drawpage->links; link; link = link->next) {
1510 fz_point p1, p2, p3, p4;
1512 p1.x = link->rect.x0;
1513 p1.y = link->rect.y0;
1515 p2.x = link->rect.x1;
1516 p2.y = link->rect.y0;
1518 p3.x = link->rect.x1;
1519 p3.y = link->rect.y1;
1521 p4.x = link->rect.x0;
1522 p4.y = link->rect.y1;
1524 p1 = fz_transform_point (ctm, p1);
1525 p2 = fz_transform_point (ctm, p2);
1526 p3 = fz_transform_point (ctm, p3);
1527 p4 = fz_transform_point (ctm, p4);
1529 switch (link->dest.kind) {
1530 case FZ_LINK_GOTO: glColor3ub (255, 0, 0); break;
1531 case FZ_LINK_URI: glColor3ub (0, 0, 255); break;
1532 default: glColor3ub (0, 0, 0); break;
1535 glVertex2f (p1.x, p1.y);
1536 glVertex2f (p2.x, p2.y);
1537 glVertex2f (p3.x, p3.y);
1538 glVertex2f (p4.x, p4.y);
1540 glEnd ();
1542 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1543 glDisable (GL_LINE_STIPPLE);
1546 static void uploadslice (struct tile *tile, struct slice *slice)
1548 int offset;
1549 struct slice *slice1;
1551 offset = 0;
1552 for (slice1 = tile->slices; slice != slice1; slice1++) {
1553 offset += slice1->h * tile->w * tile->pixmap->n;
1555 if (slice->texindex != -1 && slice->texindex < state.texcount
1556 && state.texowners[slice->texindex].slice == slice) {
1557 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1559 else {
1560 int subimage = 0;
1561 int texindex = state.texindex++ % state.texcount;
1563 if (state.texowners[texindex].w == tile->w) {
1564 if (state.texowners[texindex].h >= slice->h) {
1565 subimage = 1;
1567 else {
1568 state.texowners[texindex].h = slice->h;
1571 else {
1572 state.texowners[texindex].h = slice->h;
1575 state.texowners[texindex].w = tile->w;
1576 state.texowners[texindex].slice = slice;
1577 slice->texindex = texindex;
1579 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[texindex]);
1580 if (subimage) {
1581 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1585 tile->w,
1586 slice->h,
1587 state.texform,
1588 state.texty,
1589 tile->pixmap->samples+offset
1592 else {
1593 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1595 state.texiform,
1596 tile->w,
1597 slice->h,
1599 state.texform,
1600 state.texty,
1601 tile->pixmap->samples+offset
1607 CAMLprim value ml_drawtile (value args_v, value ptr_v)
1609 CAMLparam2 (args_v, ptr_v);
1610 int dispx = Int_val (Field (args_v, 0));
1611 int dispy = Int_val (Field (args_v, 1));
1612 int dispw = Int_val (Field (args_v, 2));
1613 int disph = Int_val (Field (args_v, 3));
1614 int tilex = Int_val (Field (args_v, 4));
1615 int tiley = Int_val (Field (args_v, 5));
1616 char *s = String_val (ptr_v);
1617 struct tile *tile = parse_pointer ("ml_drawtile", s);
1619 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1621 int slicey, firstslice;
1622 struct slice *slice;
1624 firstslice = tiley / tile->sliceheight;
1625 slice = &tile->slices[firstslice];
1626 slicey = tiley % tile->sliceheight;
1628 while (disph > 0) {
1629 int dh;
1631 dh = slice->h - slicey;
1632 dh = MIN (disph, dh);
1633 uploadslice (tile, slice);
1635 glBegin (GL_QUADS);
1637 glTexCoord2i (tilex, slicey);
1638 glVertex2i (dispx, dispy);
1640 glTexCoord2i (tilex+dispw, slicey);
1641 glVertex2i (dispx+dispw, dispy);
1643 glTexCoord2i (tilex+dispw, slicey+dh);
1644 glVertex2i (dispx+dispw, dispy+dh);
1646 glTexCoord2i (tilex, slicey+dh);
1647 glVertex2i (dispx, dispy+dh);
1649 glEnd ();
1651 dispy += dh;
1652 disph -= dh;
1653 slice++;
1654 ARSERT (!(slice - tile->slices >= tile->slicecount && disph > 0));
1655 slicey = 0;
1658 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1659 CAMLreturn (Val_unit);
1662 CAMLprim value ml_postprocess (value ptr_v, value hlinks_v,
1663 value xoff_v, value yoff_v)
1665 CAMLparam4 (ptr_v, hlinks_v, xoff_v, yoff_v);
1666 int xoff = Int_val (xoff_v);
1667 int yoff = Int_val (yoff_v);
1668 char *s = String_val (ptr_v);
1669 struct page *page = parse_pointer ("ml_postprocess", s);
1671 if (Bool_val (hlinks_v)) highlightlinks (page, xoff, yoff);
1673 if (trylock ("ml_postprocess")) {
1674 goto done;
1676 showsel (page, xoff, yoff);
1677 unlock ("ml_postprocess");
1679 done:
1680 CAMLreturn (Val_unit);
1683 static fz_link *getlink (struct page *page, int x, int y)
1685 fz_point p;
1686 fz_matrix ctm;
1687 fz_link *link;
1689 p.x = x;
1690 p.y = y;
1692 ctm = fz_concat (trimctm (page->drawpage, page->pdimno),
1693 state.pagedims[page->pdimno].ctm);
1694 ctm = fz_invert_matrix (ctm);
1695 p = fz_transform_point (ctm, p);
1697 for (link = page->drawpage->links; link; link = link->next) {
1698 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1699 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1700 return link;
1704 return NULL;
1707 static void droptext (struct page *page)
1709 if (page->text) {
1710 fz_free_text_span (state.ctx, page->text);
1711 page->fmark.i = -1;
1712 page->lmark.i = -1;
1713 page->fmark.span = NULL;
1714 page->lmark.span = NULL;
1715 page->text = NULL;
1719 static void ensuretext (struct page *page)
1721 if (state.gen != page->gen) {
1722 droptext (page);
1723 page->gen = state.gen;
1725 if (!page->text) {
1726 fz_device *tdev;
1728 page->text = fz_new_text_span (state.ctx);
1729 tdev = fz_new_text_device (state.ctx, page->text);
1730 fz_execute_display_list (page->dlist,
1731 tdev,
1732 pagectm (page),
1733 fz_infinite_bbox, NULL);
1734 fz_free_device (tdev);
1738 CAMLprim value ml_whatsunder (value ptr_v, value x_v, value y_v)
1740 CAMLparam3 (ptr_v, x_v, y_v);
1741 CAMLlocal3 (ret_v, tup_v, str_v);
1742 fz_link *link;
1743 struct page *page;
1744 char *s = String_val (ptr_v);
1745 int x = Int_val (x_v), y = Int_val (y_v);
1746 struct pagedim *pdim;
1748 ret_v = Val_int (0);
1749 if (trylock ("ml_whatsunder")) {
1750 goto done;
1753 page = parse_pointer ("ml_whatsunder", s);
1754 pdim = &state.pagedims[page->pdimno];
1755 x += pdim->bounds.x0;
1756 y += pdim->bounds.y0;
1757 link = getlink (page, x, y);
1758 if (link) {
1759 switch (link->dest.kind) {
1760 case FZ_LINK_GOTO:
1762 int pageno;
1763 fz_point p;
1765 pageno = link->dest.ld.gotor.page;
1766 p.x = 0;
1767 p.y = 0;
1769 if (link->dest.ld.gotor.flags & fz_link_flag_t_valid) {
1770 p.y = link->dest.ld.gotor.lt.y;
1771 p = fz_transform_point (pdim->lctm, p);
1773 tup_v = caml_alloc_tuple (2);
1774 ret_v = caml_alloc_small (1, 1);
1775 Field (tup_v, 0) = Val_int (pageno);
1776 Field (tup_v, 1) = Val_int (p.y);
1777 Field (ret_v, 0) = tup_v;
1779 break;
1781 case FZ_LINK_URI:
1782 str_v = caml_copy_string (link->dest.ld.uri.uri);
1783 ret_v = caml_alloc_small (1, 0);
1784 Field (ret_v, 0) = str_v;
1785 break;
1787 default:
1788 printd (state.sock, "msg unhandled link kind %d", link->dest.kind);
1789 break;
1792 else {
1793 int i;
1794 fz_text_span *span;
1796 ensuretext (page);
1797 for (span = page->text; span; span = span->next) {
1798 for (i = 0; i < span->len; ++i) {
1799 fz_bbox *b;
1800 b = &span->text[i].bbox;
1801 if ((x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1)) {
1802 const char *n2 =
1803 span->font && span->font->name
1804 ? span->font->name
1805 : "Span has no font name"
1807 FT_FaceRec *face = span->font->ft_face;
1808 if (face && face->family_name) {
1809 char *s;
1810 char *n1 = face->family_name;
1811 size_t l1 = strlen (n1);
1812 size_t l2 = strlen (n2);
1814 if (l1 != l2 || memcmp (n1, n2, l1)) {
1815 s = malloc (l1 + l2 + 2);
1816 if (s) {
1817 memcpy (s, n2, l2);
1818 s[l2] = '=';
1819 memcpy (s + l2 + 1, n1, l1 + 1);
1820 str_v = caml_copy_string (s);
1821 free (s);
1825 if (str_v == 0) {
1826 str_v = caml_copy_string (n2);
1828 ret_v = caml_alloc_small (1, 2);
1829 Field (ret_v, 0) = str_v;
1830 goto unlock;
1835 unlock:
1836 unlock ("ml_whatsunder");
1838 done:
1839 CAMLreturn (ret_v);
1842 CAMLprim value ml_seltext (value ptr_v, value rect_v)
1844 CAMLparam2 (ptr_v, rect_v);
1845 fz_bbox *b;
1846 struct page *page;
1847 fz_text_span *span;
1848 struct mark first, last;
1849 int i, x0, x1, y0, y1;
1850 struct pagedim *pdim;
1851 char *s = String_val (ptr_v);
1853 if (trylock ("ml_seltext")) {
1854 goto done;
1857 page = parse_pointer ("ml_seltext", s);
1858 ensuretext (page);
1860 pdim = &state.pagedims[page->pdimno];
1862 x0 = Int_val (Field (rect_v, 0)) + pdim->bounds.x0;
1863 y0 = Int_val (Field (rect_v, 1)) + pdim->bounds.y0;
1864 x1 = Int_val (Field (rect_v, 2)) + pdim->bounds.x0;
1865 y1 = Int_val (Field (rect_v, 3)) + pdim->bounds.y0;
1867 if (0) {
1868 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1869 glColor3ub (128, 128, 128);
1870 glRecti (x0, y0, x1, y1);
1871 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1874 first.span = NULL;
1875 last.span = NULL;
1877 last.i = first.i = 0;
1878 first.span = page->text;
1879 for (span = page->text; span; span = span->next) {
1880 for (i = 0; i < span->len; ++i) {
1881 b = &span->text[i].bbox;
1882 int selected = 0;
1884 if (x0 >= b->x0 && x0 <= b->x1 && y0 >= b->y0 && y0 <= b->y1) {
1885 first.i = i;
1886 first.span = span;
1887 selected = 1;
1889 if (x1 >= b->x0 && x1 <= b->x1 && y1 >= b->y0 && y1 <= b->y1) {
1890 last.i = i;
1891 last.span = span;
1892 selected = 1;
1894 if (0 && selected) {
1895 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1896 glColor3ub (128, 128, 128);
1897 glRecti (b->x0, b->y0, b->x1, b->y1);
1898 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1903 if (y1 < y0 || x1 < x0) {
1904 int swap = 0;
1906 if (first.span == last.span) {
1907 swap = 1;
1909 else {
1910 if (y1 < y0) {
1911 for (span = first.span; span && span != last.span;
1912 span = span->next) {
1913 if (span->eol) {
1914 swap = 1;
1915 break;
1921 if (swap) {
1922 i = first.i;
1923 span = first.span;
1924 first.i = last.i;
1925 first.span = last.span;
1926 last.i = i;
1927 last.span = span;
1931 page->fmark = first;
1932 page->lmark = last;
1934 unlock ("ml_seltext");
1936 done:
1937 CAMLreturn (Val_unit);
1940 static int pipespan (FILE *f, fz_text_span *span, int a, int b)
1942 char buf[4];
1943 int i, len, ret;
1945 for (i = a; i <= b; ++i) {
1946 len = runetochar (buf, &span->text[i].c);
1947 ret = fwrite (buf, len, 1, f);
1949 if (ret != 1) {
1950 printd (state.sock, "msg failed to write %d bytes ret=%d: %s",
1951 len, ret, strerror (errno));
1952 return -1;
1955 return 0;
1958 CAMLprim value ml_copysel (value ptr_v)
1960 CAMLparam1 (ptr_v);
1961 FILE *f;
1962 struct page *page;
1963 char *s = String_val (ptr_v);
1965 if (trylock ("ml_copysel")) {
1966 goto done;
1969 if (!*s) {
1970 close:
1971 #ifdef USE_XSEL
1972 if (state.xselpipe) {
1973 int ret = pclose (state.xselpipe);
1974 if (ret) {
1975 printd (state.sock, "msg failed to close xsel pipe `%s'",
1976 strerror (errno));
1978 state.xselpipe = NULL;
1980 #else
1981 printf ("========================================\n");
1982 #endif
1984 else {
1985 fz_text_span *span;
1987 page = parse_pointer ("ml_sopysel", s);
1989 if (!page->fmark.span || !page->lmark.span) {
1990 printd (state.sock, "msg nothing to copy");
1991 goto unlock;
1994 f = stdout;
1995 #ifdef USE_XSEL
1996 if (!state.xselpipe) {
1997 state.xselpipe = popen ("xsel -i", "w");
1998 if (!state.xselpipe) {
1999 printd (state.sock, "msg failed to open xsel pipe `%s'",
2000 strerror (errno));
2002 else {
2003 f = state.xselpipe;
2006 else {
2007 f = state.xselpipe;
2009 #endif
2011 for (span = page->fmark.span;
2012 span && span != page->lmark.span->next;
2013 span = span->next) {
2014 int a = span == page->fmark.span ? page->fmark.i : 0;
2015 int b = span == page->lmark.span ? page->lmark.i : span->len - 1;
2016 if (pipespan (f, span, a, b)) {
2017 goto close;
2019 if (span->eol) {
2020 if (putc ('\n', f) == EOF) {
2021 printd (state.sock,
2022 "msg failed break line on xsel pipe `%s'",
2023 strerror (errno));
2024 goto close;
2028 page->lmark.span = NULL;
2029 page->fmark.span = NULL;
2032 unlock:
2033 unlock ("ml_copysel");
2035 done:
2036 CAMLreturn (Val_unit);
2039 CAMLprim value ml_getpdimrect (value pagedimno_v)
2041 CAMLparam1 (pagedimno_v);
2042 CAMLlocal1 (ret_v);
2043 int pagedimno = Int_val (pagedimno_v);
2044 fz_rect box;
2046 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
2047 if (trylock ("ml_getpdimrect")) {
2048 box = fz_empty_rect;
2050 else {
2051 box = state.pagedims[pagedimno].mediabox;
2052 unlock ("ml_getpdimrect");
2055 Store_double_field (ret_v, 0, box.x0);
2056 Store_double_field (ret_v, 1, box.x1);
2057 Store_double_field (ret_v, 2, box.y0);
2058 Store_double_field (ret_v, 3, box.y1);
2060 CAMLreturn (ret_v);
2063 static double getmaxw (void)
2065 int i;
2066 struct pagedim *p;
2067 double maxw = 0.0;
2069 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
2070 double x0, x1, w;
2072 x0 = MIN (p->mediabox.x0, p->mediabox.x1);
2073 x1 = MAX (p->mediabox.x0, p->mediabox.x1);
2075 w = x1 - x0;
2076 maxw = MAX (w, maxw);
2078 return maxw;
2081 CAMLprim value ml_getmaxw (value unit_v)
2083 CAMLparam1 (unit_v);
2084 CAMLlocal1 (ret_v);
2085 double maxw = 0.0;
2087 if (trylock ("ml_getmaxw")) {
2088 goto done;
2090 maxw = getmaxw ();
2091 unlock ("ml_getmaxw");
2092 done:
2093 ret_v = caml_copy_double (maxw);
2094 CAMLreturn (ret_v);
2097 CAMLprim value ml_zoom_for_height (value winw_v, value winh_v, value dw_v)
2099 CAMLparam3 (winw_v, winh_v, dw_v);
2100 CAMLlocal1 (ret_v);
2101 int i;
2102 double zoom = 1.0;
2103 double maxw = 0.0, maxh = 0.0;
2104 struct pagedim *p;
2105 double winw = Int_val (winw_v);
2106 double winh = Int_val (winh_v);
2107 double dw = Int_val (dw_v);
2108 double pw = 1.0, ph = 1.0, num, den;
2110 if (trylock ("ml_zoom_for_height")) {
2111 goto done;
2114 if (state.proportional) {
2115 maxw = getmaxw ();
2118 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
2119 double x0, x1, y0, y1, w, h, scaledh, scale;
2121 x0 = MIN (p->mediabox.x0, p->mediabox.x1);
2122 x1 = MAX (p->mediabox.x0, p->mediabox.x1);
2123 y0 = MIN (p->mediabox.y0, p->mediabox.y1);
2124 y1 = MAX (p->mediabox.y0, p->mediabox.y1);
2126 w = x1 - x0;
2127 h = y1 - y0;
2129 if (state.proportional) {
2130 scale = w / maxw;
2131 scaledh = h * scale;
2133 else {
2134 scale = 1.0;
2135 scaledh = h;
2138 if (scaledh > maxh) {
2139 maxh = scaledh;
2140 ph = scaledh;
2141 pw = w * scale;
2145 num = (winh * pw) + (ph * dw);
2146 den = ph * winw;
2147 zoom = num / den;
2149 unlock ("ml_zoom_for_height");
2150 done:
2151 ret_v = caml_copy_double (zoom);
2152 CAMLreturn (ret_v);
2155 #include "glfont.c"
2157 CAMLprim value ml_draw_string (value pt_v, value x_v, value y_v, value string_v)
2159 CAMLparam4 (pt_v, x_v, y_v, string_v);
2160 CAMLlocal1 (ret_v);
2161 int pt = Int_val(pt_v);
2162 int x = Int_val (x_v);
2163 int y = Int_val (y_v);
2164 double w;
2166 w = draw_string (state.face, pt, x, y, String_val (string_v));
2167 ret_v = caml_copy_double (w);
2168 CAMLreturn (ret_v);
2171 CAMLprim value ml_measure_string (value pt_v, value string_v)
2173 CAMLparam2 (pt_v, string_v);
2174 CAMLlocal1 (ret_v);
2175 int pt = Int_val (pt_v);
2176 double w;
2178 w = measure_string (state.face, pt, String_val (string_v));
2179 ret_v = caml_copy_double (w);
2180 CAMLreturn (ret_v);
2183 CAMLprim value ml_getpagebox (value opaque_v)
2185 CAMLparam1 (opaque_v);
2186 CAMLlocal1 (ret_v);
2187 fz_bbox bbox;
2188 fz_device *dev;
2189 char *s = String_val (opaque_v);
2190 struct page *page = parse_pointer ("ml_getpagebox", s);
2192 ret_v = caml_alloc_tuple (4);
2193 dev = fz_new_bbox_device (state.ctx, &bbox);
2194 dev->hints |= FZ_IGNORE_SHADE;
2195 pdf_run_page (state.doc, page->drawpage, dev, pagectm (page), NULL);
2196 fz_free_device (dev);
2198 Field (ret_v, 0) = Val_int (bbox.x0);
2199 Field (ret_v, 1) = Val_int (bbox.y0);
2200 Field (ret_v, 2) = Val_int (bbox.x1);
2201 Field (ret_v, 3) = Val_int (bbox.y1);
2203 CAMLreturn (ret_v);
2206 CAMLprim value ml_setaalevel (value level_v)
2208 CAMLparam1 (level_v);
2210 state.aalevel = Int_val (level_v);
2211 CAMLreturn (Val_unit);
2214 #if !defined _WIN32 && !defined __APPLE__
2215 #undef pixel
2216 #include <X11/X.h>
2217 #include <X11/Xlib.h>
2218 #include <X11/Xutil.h>
2219 #include <GL/glx.h>
2221 static void set_wm_class (int hack)
2223 if (hack) {
2224 Display *dpy;
2225 Window win;
2226 XClassHint hint;
2227 char *display;
2229 display = getenv ("DISPLAY");
2230 dpy = XOpenDisplay (display);
2231 if (!dpy) {
2232 fprintf (stderr, "XOpenDisplay `%s' failed\n",
2233 display ? display : "null");
2234 return;
2236 hint.res_name = "llpp";
2237 hint.res_class = "llpp";
2238 win = glXGetCurrentDrawable ();
2239 if (win == None) {
2240 fprintf (stderr, "glXGetCurrentDrawable returned None\n");
2241 XCloseDisplay (dpy);
2242 return;
2244 XSetClassHint (dpy, win, &hint);
2245 XCloseDisplay (dpy);
2248 #else
2249 #define set_wm_class(h) (void) (h)
2250 #endif
2252 enum { piunknown, pilinux, piwindows, piosx,
2253 pisun, pifreebsd, pidragonflybsd,
2254 piopenbsd, pimingw, picygwin };
2256 CAMLprim value ml_platform (value unit_v)
2258 CAMLparam1 (unit_v);
2259 int platid = piunknown;
2261 #if defined __linux__
2262 platid = pilinux;
2263 #elif defined __CYGWIN__
2264 platid = picygwin;
2265 #elif defined __MINGW32__
2266 platid = pimingw;
2267 #elif defined _WIN32
2268 platid = piwindows;
2269 #elif defined __DragonFly__
2270 platid = pidragonflybsd;
2271 #elif defined __FreeBSD__
2272 platid = pifreebsd;
2273 #elif defined __OpenBSD__
2274 platid = piopenbsd;
2275 #elif defined __sun__
2276 platid = pisun;
2277 #elif defined __APPLE__
2278 platid = piosx;
2279 #endif
2280 CAMLreturn (Val_int (platid));
2283 CAMLprim value ml_init (value sock_v, value params_v)
2285 CAMLparam2 (sock_v, params_v);
2286 CAMLlocal2 (trim_v, fuzz_v);
2287 #ifndef _WIN32
2288 int ret;
2289 #endif
2290 char *fontpath;
2291 int texcount;
2292 int wmclasshack;
2293 int colorspace;
2294 int mumemlimit;
2296 state.rotate = Int_val (Field (params_v, 0));
2297 state.proportional = Bool_val (Field (params_v, 1));
2298 trim_v = Field (params_v, 2);
2299 texcount = Int_val (Field (params_v, 3));
2300 state.sliceheight = Int_val (Field (params_v, 4));
2301 mumemlimit = Int_val (Field (params_v, 5));
2302 state.ctx = fz_new_context (&fz_alloc_default, mumemlimit);
2303 colorspace = Int_val (Field (params_v, 6));
2304 wmclasshack = Bool_val (Field (params_v, 7));
2305 fontpath = String_val (Field (params_v, 8));
2307 state.trimmargins = Bool_val (Field (trim_v, 0));
2308 fuzz_v = Field (trim_v, 1);
2309 state.trimfuzz.x0 = Int_val (Field (fuzz_v, 0));
2310 state.trimfuzz.y0 = Int_val (Field (fuzz_v, 1));
2311 state.trimfuzz.x1 = Int_val (Field (fuzz_v, 2));
2312 state.trimfuzz.y1 = Int_val (Field (fuzz_v, 3));
2314 set_tex_params (colorspace);
2315 set_wm_class (wmclasshack);
2317 if (*fontpath) {
2318 state.face = load_font (fontpath);
2320 else {
2321 unsigned int len;
2322 void *base = pdf_find_substitute_font (0, 0, 0, 0, &len);
2324 state.face = load_builtin_font (base, len);
2326 if (!state.face) _exit (1);
2328 fz_accelerate ();
2329 realloctexts (texcount);
2331 #ifdef _WIN32
2332 state.sock = Socket_val (sock_v);
2333 InitializeCriticalSection (&critsec);
2334 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
2335 if (state.thread == INVALID_HANDLE_VALUE) {
2336 errx (1, "CreateThread failed: %lx", GetLastError ());
2338 #else
2339 state.sock = Int_val (sock_v);
2340 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
2341 if (ret) {
2342 errx (1, "pthread_create: %s", strerror (ret));
2344 #endif
2346 CAMLreturn (Val_unit);