links in XPS + make it less ugly
[llpp.git] / link.c
blob6547aea3dd6fa83fdd4071983857fcc4faa011fc
1 /* lots of code c&p-ed directly from mupdf */
2 #include <errno.h>
3 #include <stdio.h>
4 #include <ctype.h>
5 #include <string.h>
6 #include <stdlib.h>
8 #define PIGGYBACK
10 #ifdef _WIN32
11 #define WIN32_LEAN_AND_MEAN
12 #include <windows.h>
13 #include <winsock2.h>
14 #define fionread_arg long
15 #define ssize_t int
16 #define FMT_ss "d"
17 #ifdef _WIN64
18 #define FMT_s "i64u"
19 #else
20 #define FMT_s "u"
21 #endif
22 #endif
24 #ifdef _MSC_VER
25 #pragma warning (disable:4244)
26 #pragma warning (disable:4996)
27 #pragma warning (disable:4995)
28 #define NORETURN __declspec (noreturn)
29 #define UNUSED
30 #define OPTIMIZE
31 #elif defined __GNUC__
32 #define NORETURN __attribute__ ((noreturn))
33 #define UNUSED __attribute__ ((unused))
34 #define OPTIMIZE(n) __attribute__ ((optimize ("O"#n)))
35 #else
36 #define NORETURN
37 #define UNUSED
38 #define OPTIMIZE
39 #endif
41 #ifdef __MINGW32__
42 /* some versions of MingW have non idempotent %p */
43 #include <inttypes.h>
44 #define FMT_ptr PRIxPTR
45 #define FMT_ptr_cast(p) ((intptr_t *) (p))
46 #define FMT_ptr_cast2(p) ((intptr_t) (p))
47 #else
48 #define FMT_ptr "p"
49 #define FMT_ptr_cast(p) (p)
50 #define FMT_ptr_cast2(p) (p)
51 #endif
53 #ifdef _WIN32
54 static void __declspec (noreturn) sockerr (int exitcode, const char *fmt, ...)
56 va_list ap;
58 va_start (ap, fmt);
59 vfprintf (stderr, fmt, ap);
60 va_end (ap);
61 fprintf (stderr, ": wsaerror 0x%x\n", WSAGetLastError ());
62 _exit (exitcode);
64 #else
65 #define FMT_ss "zd"
66 #define FMT_s "zu"
67 #define fionread_arg int
68 #define ioctlsocket ioctl
69 #define sockerr err
70 #include <unistd.h>
71 #endif
73 #include <regex.h>
74 #include <ctype.h>
75 #include <stdarg.h>
76 #include <limits.h>
78 #ifndef _WIN32
79 #include <pthread.h>
80 #include <sys/time.h>
81 #include <sys/types.h>
82 #include <sys/socket.h>
83 #include <sys/ioctl.h>
84 #endif
86 static void NORETURN err (int exitcode, const char *fmt, ...)
88 va_list ap;
89 int savederrno;
91 savederrno = errno;
92 va_start (ap, fmt);
93 vfprintf (stderr, fmt, ap);
94 va_end (ap);
95 fprintf (stderr, ": %s\n", strerror (savederrno));
96 fflush (stderr);
97 _exit (exitcode);
100 static void NORETURN errx (int exitcode, const char *fmt, ...)
102 va_list ap;
104 va_start (ap, fmt);
105 vfprintf (stderr, fmt, ap);
106 va_end (ap);
107 fputc ('\n', stderr);
108 fflush (stderr);
109 _exit (exitcode);
112 #ifdef __APPLE__
113 #include <OpenGL/gl.h>
114 #else
115 #include <GL/gl.h>
116 #endif
118 #ifndef GL_TEXTURE_RECTANGLE_ARB
119 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
120 #endif
122 #ifndef GL_BGRA
123 #define GL_BGRA 0x80E1
124 #endif
126 #ifndef GL_UNSIGNED_INT_8_8_8_8
127 #define GL_UNSIGNED_INT_8_8_8_8 0x8035
128 #endif
130 #ifndef GL_UNSIGNED_INT_8_8_8_8_REV
131 #define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
132 #endif
134 #include <caml/fail.h>
135 #include <caml/alloc.h>
136 #include <caml/memory.h>
137 #include <caml/unixsupport.h>
139 #include <fitz.h>
140 #include <mupdf.h>
141 #include <muxps.h>
142 #include <mucbz.h>
144 #include FT_FREETYPE_H
146 #if 0
147 #define lprintf printf
148 #else
149 #define lprintf(...)
150 #endif
152 #define ARSERT(cond) for (;;) { \
153 if (!(cond)) { \
154 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
156 break; \
159 struct slice {
160 int h;
161 int texindex;
164 struct tile {
165 int x, y, w, h;
166 int slicecount;
167 int sliceheight;
168 fz_pixmap *pixmap;
169 struct slice slices[1];
172 struct pagedim {
173 int pageno;
174 int rotate;
175 int left;
176 int tctmready;
177 fz_bbox bounds;
178 fz_rect pagebox;
179 fz_rect mediabox;
180 fz_matrix ctm, zoomctm, lctm, tctm;
183 enum { DPDF, DXPS, DCBZ };
185 struct page {
186 int gen;
187 int type;
188 int pageno;
189 int pdimno;
190 fz_text_span *text;
191 union {
192 void *ptr;
193 pdf_page *pdfpage;
194 xps_page *xpspage;
195 cbz_page *cbzpage;
196 } u;
197 fz_display_list *dlist;
198 struct mark {
199 int i;
200 fz_text_span *span;
201 } fmark, lmark;
204 #if !defined _WIN32 && !defined __APPLE__
205 #define USE_XSEL
206 #endif
208 struct {
209 int sock;
210 int type;
211 int sliceheight;
212 struct pagedim *pagedims;
213 int pagecount;
214 int pagedimcount;
215 union {
216 pdf_document *pdf;
217 xps_document *xps;
218 cbz_document *cbz;
219 } u;
220 fz_context *ctx;
221 fz_glyph_cache *cache;
222 int w, h;
224 int texindex;
225 int texcount;
226 GLuint *texids;
228 GLenum texiform;
229 GLenum texform;
230 GLenum texty;
232 fz_colorspace *colorspace;
234 struct {
235 int w, h;
236 struct slice *slice;
237 } *texowners;
239 int rotate;
240 int proportional;
241 int trimmargins;
242 int needoutline;
243 int gen;
244 int aalevel;
246 int trimanew;
247 fz_bbox trimfuzz;
248 fz_pixmap *pig;
250 #ifdef _WIN32
251 HANDLE thread;
252 #else
253 pthread_t thread;
254 #endif
255 FILE *xselpipe;
257 FT_Face face;
259 void (*closedoc) (void);
260 void (*freepage) (void *);
261 } state;
263 static void UNUSED debug_rect (const char *cap, fz_rect r)
265 printf ("%s(rect) %.2f,%.2f,%.2f,%.2f\n", cap, r.x0, r.y0, r.x1, r.y1);
268 static void UNUSED debug_bbox (const char *cap, fz_bbox r)
270 printf ("%s(bbox) %d,%d,%d,%d\n", cap, r.x0, r.y0, r.x1, r.y1);
273 static void UNUSED debug_matrix (const char *cap, fz_matrix m)
275 printf ("%s(matrix) %.2f,%.2f,%.2f,%.2f %.2f %.2f\n", cap,
276 m.a, m.b, m.c, m.d, m.e, m.f);
279 #ifdef _WIN32
280 static CRITICAL_SECTION critsec;
282 static void lock (void *unused)
284 (void) unused;
285 EnterCriticalSection (&critsec);
288 static void unlock (void *unused)
290 (void) unused;
291 LeaveCriticalSection (&critsec);
294 static int trylock (void *unused)
296 return TryEnterCriticalSection (&critsec) == 0;
298 #else
299 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
301 static void lock (const char *cap)
303 int ret = pthread_mutex_lock (&mutex);
304 if (ret) {
305 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
309 static void unlock (const char *cap)
311 int ret = pthread_mutex_unlock (&mutex);
312 if (ret) {
313 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
317 static int trylock (const char *cap)
319 int ret = pthread_mutex_trylock (&mutex);
321 if (ret && ret != EBUSY) {
322 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
324 return ret == EBUSY;
326 #endif
328 static void *parse_pointer (const char *cap, const char *s)
330 int ret;
331 void *ptr;
333 ret = sscanf (s, "%" FMT_ptr, FMT_ptr_cast (&ptr));
334 if (ret != 1) {
335 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
337 return ptr;
340 static int hasdata (int sock)
342 int ret;
343 fionread_arg avail;
344 ret = ioctlsocket (sock, FIONREAD, &avail);
345 if (ret) sockerr (1, "hasdata: FIONREAD error ret=%d", ret);
346 return avail > 0;
349 static double now (void)
351 #ifdef _WIN32
352 FILETIME ft;
353 uint64 tmp;
355 GetSystemTimeAsFileTime (&ft);
356 tmp = ft.dwHighDateTime;
357 tmp <<= 32;
358 tmp |= ft.dwLowDateTime;
359 return tmp * 1e-7;
360 #else
361 struct timeval tv;
363 if (gettimeofday (&tv, NULL)) {
364 err (1, "gettimeofday");
366 return tv.tv_sec + tv.tv_usec*1e-6;
367 #endif
370 static void readdata (int fd, char *p, int size)
372 ssize_t n;
374 n = recv (fd, p, size, 0);
375 if (n - size) {
376 if (!n) errx (1, "EOF while reading");
377 sockerr (1, "recv (req %d, ret %" FMT_ss ")", size, n);
381 static void writedata (int fd, char *p, int size)
383 char buf[4];
384 ssize_t n;
386 buf[0] = (size >> 24) & 0xff;
387 buf[1] = (size >> 16) & 0xff;
388 buf[2] = (size >> 8) & 0xff;
389 buf[3] = (size >> 0) & 0xff;
391 n = send (fd, buf, 4, 0);
392 if (n != 4) {
393 if (!n) errx (1, "EOF while writing length");
394 sockerr (1, "send %" FMT_ss, n);
397 n = send (fd, p, size, 0);
398 if (n - size) {
399 if (!n) errx (1, "EOF while writing data");
400 sockerr (1, "send (req %d, ret %" FMT_ss ")", size, n);
404 static void
405 #ifdef __GNUC__
406 __attribute__ ((format (printf, 2, 3)))
407 #endif
408 printd (int fd, const char *fmt, ...)
410 int size = 200, len;
411 va_list ap;
412 char *buf;
414 buf = malloc (size);
415 for (;;) {
416 if (!buf) err (errno, "malloc for temp buf (%d bytes) failed", size);
418 va_start (ap, fmt);
419 len = vsnprintf (buf, size, fmt, ap);
420 va_end (ap);
422 if (len > -1 && len < size) {
423 writedata (fd, buf, len);
424 break;
427 if (len > -1) {
428 size = len + 1;
430 else {
431 size *= 2;
433 buf = realloc (buf, size);
435 free (buf);
438 static void closepdf (void)
440 if (state.u.pdf) {
441 pdf_close_document (state.u.pdf);
442 state.u.pdf = NULL;
446 static void closexps (void)
448 if (state.u.xps) {
449 xps_close_document (state.u.xps);
450 state.u.xps = NULL;
454 static void closecbz (void)
456 if (state.u.cbz) {
457 cbz_close_document (state.u.cbz);
458 state.u.cbz = NULL;
462 static void freepdfpage (void *ptr)
464 pdf_free_page (state.u.pdf, ptr);
467 static void freexpspage (void *ptr)
469 xps_free_page (state.u.xps, ptr);
472 static void freecbzpage (void *ptr)
474 cbz_free_page (state.u.cbz, ptr);
477 static void openxref (char *filename, char *password)
479 int i, len;
481 for (i = 0; i < state.texcount; ++i) {
482 state.texowners[i].w = -1;
483 state.texowners[i].slice = NULL;
486 if (state.closedoc) state.closedoc ();
488 len = strlen (filename);
490 state.type = DPDF;
491 if (len > 4) {
492 char ext[4];
494 ext[0] = tolower (filename[len-3]);
495 ext[1] = tolower (filename[len-2]);
496 ext[2] = tolower (filename[len-1]);
498 /**/ if (ext[0] == 'x' && ext[1] == 'p' && ext[2] == 's') {
499 state.type = DXPS;
501 else if (ext[0] == 'c' && ext[1] == 'b' && ext[2] == 'z') {
502 state.type = DCBZ;
506 if (state.pagedims) {
507 free (state.pagedims);
508 state.pagedims = NULL;
510 state.pagedimcount = 0;
512 fz_set_aa_level (state.ctx, state.aalevel);
513 switch (state.type) {
514 case DPDF:
515 state.u.pdf = pdf_open_document (state.ctx, filename);
516 if (pdf_needs_password (state.u.pdf)) {
517 int okay = pdf_authenticate_password (state.u.pdf, password);
518 if (!okay) {
519 errx (1, "invalid password");
522 state.pagecount = pdf_count_pages (state.u.pdf);
523 state.closedoc = closepdf;
524 state.freepage = freepdfpage;
525 break;
527 case DXPS:
528 state.u.xps = xps_open_document (state.ctx, filename);
529 state.pagecount = xps_count_pages (state.u.xps);
530 state.closedoc = closexps;
531 state.freepage = freexpspage;
532 break;
534 case DCBZ:
535 state.u.cbz = cbz_open_document (state.ctx, filename);
536 state.pagecount = cbz_count_pages (state.u.cbz);
537 state.closedoc = closecbz;
538 state.freepage = freecbzpage;
539 break;
543 static void pdfinfo (void)
545 if (state.type == DPDF) {
546 fz_obj *infoobj;
548 printd (state.sock, "info PDF version\t%d.%d",
549 state.u.pdf->version / 10, state.u.pdf->version % 10);
551 infoobj = fz_dict_gets (state.u.pdf->trailer, "Info");
552 if (infoobj) {
553 int i;
554 char *s;
555 char *items[] = { "Title", "Author", "Creator",
556 "Producer", "CreationDate" };
558 for (i = 0; i < sizeof (items) / sizeof (*items); ++i) {
559 fz_obj *obj = fz_dict_gets (infoobj, items[i]);
560 s = pdf_to_utf8 (state.ctx, obj);
561 if (*s) {
562 if (i == 0) {
563 printd (state.sock, "title %s", s);
565 printd (state.sock, "info %s\t%s", items[i], s);
567 fz_free (state.ctx, s);
570 printd (state.sock, "infoend");
574 static int readlen (int fd)
576 ssize_t n;
577 unsigned char p[4];
579 n = recv (fd, p, 4, 0);
580 if (n != 4) {
581 if (!n) errx (1, "EOF while reading length");
582 sockerr (1, "recv %" FMT_ss, n);
585 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
588 static void unlinktile (struct tile *tile)
590 int i;
592 for (i = 0; i < tile->slicecount; ++i) {
593 struct slice *s = &tile->slices[i];
595 if (s->texindex != -1) {
596 if (state.texowners[s->texindex].slice == s) {
597 state.texowners[s->texindex].slice = NULL;
603 static void freepage (struct page *page)
605 if (page->text) {
606 fz_free_text_span (state.ctx, page->text);
608 state.freepage (page->u.ptr);
609 fz_free_display_list (state.ctx, page->dlist);
610 free (page);
613 static void freetile (struct tile *tile)
615 unlinktile (tile);
616 #ifndef PIGGYBACK
617 fz_drop_pixmap (state.ctx, tile->pixmap);
618 #else
619 if (state.pig) {
620 fz_drop_pixmap (state.ctx, state.pig);
622 state.pig = tile->pixmap;
623 #endif
624 free (tile);
627 #ifdef __ALTIVEC__
628 #include <altivec.h>
630 static int cacheline32bytes;
631 extern char **environ;
633 static void __attribute__ ((constructor)) clcheck (void)
635 char **envp = environ;
636 unsigned long *auxv;
638 while (*envp++);
640 for (auxv = (unsigned long *) envp; *auxv != 0; auxv += 2) {
641 if (*auxv == 19) {
642 cacheline32bytes = auxv[1] == 32;
643 return;
648 static void OPTIMIZE (3) clearpixmap (fz_pixmap *pixmap)
650 if (cacheline32bytes) {
651 intptr_t a1, a2, diff;
652 size_t sizea, i, size = pixmap->w * pixmap->h * pixmap->n;
653 vector unsigned char v = vec_splat_u8 (-1);
654 vector unsigned char *p;
656 a1 = a2 = (intptr_t) pixmap->samples;
657 a2 = (a1 + 31) & ~31;
658 diff = a2 - a1;
659 sizea = size - diff;
660 p = (void *) a2;
662 while (a1 != a2) *(char *) a1++ = 0xff;
663 for (i = 0; i < (sizea & ~31); i += 32) {
664 __asm volatile ("dcbz %0, %1"::"b"(a2),"r"(i));
665 vec_st (v, i, p);
666 vec_st (v, i + 16, p);
668 while (i < sizea) *((char *) a1 + i++) = 0xff;
670 else fz_clear_pixmap_with_color (state.ctx, pixmap, 0xff);
672 #else
673 #define clearpixmap(p) fz_clear_pixmap_with_color (state.ctx, p, 0xff)
674 #endif
676 static fz_matrix trimctm (pdf_page *page, int pindex)
678 fz_matrix ctm;
679 struct pagedim *pdim = &state.pagedims[pindex];
681 if (!pdim->tctmready) {
682 if (state.trimmargins) {
683 fz_rect realbox;
685 ctm = fz_concat (fz_rotate (-pdim->rotate), fz_scale (1, -1));
686 realbox = fz_transform_rect (ctm, pdim->mediabox);
687 ctm = fz_concat (ctm, fz_translate (-realbox.x0, -realbox.y0));
688 ctm = fz_concat (fz_invert_matrix (page->ctm), ctm);
690 else {
691 ctm = fz_identity;
693 pdim->tctm = ctm;
694 pdim->tctmready = 1;
696 return pdim->tctm;
699 static fz_matrix pagectm (struct page *page)
701 if (page->type == DPDF) {
702 return fz_concat (trimctm (page->u.pdfpage, page->pdimno),
703 state.pagedims[page->pdimno].ctm);
705 else {
706 fz_matrix ctm;
707 struct pagedim *pdim = &state.pagedims[page->pdimno];
709 ctm = state.pagedims[page->pdimno].ctm;
710 ctm = fz_concat (fz_translate (-pdim->mediabox.x0,
711 -pdim->mediabox.y0), ctm);
712 return ctm;
716 static void *loadpage (int pageno, int pindex)
718 fz_device *dev;
719 struct page *page = NULL;
721 page = calloc (sizeof (struct page), 1);
722 if (!page) {
723 err (1, "calloc page %d", pageno);
726 page->dlist = fz_new_display_list (state.ctx);
727 dev = fz_new_list_device (state.ctx, page->dlist);
728 switch (state.type) {
729 case DPDF:
730 page->u.pdfpage = pdf_load_page (state.u.pdf, pageno);
731 pdf_run_page (state.u.pdf, page->u.pdfpage, dev, fz_identity, NULL);
732 break;
734 case DXPS:
735 page->u.xpspage = xps_load_page (state.u.xps, pageno);
736 xps_run_page (state.u.xps, page->u.xpspage, dev, fz_identity, NULL);
737 break;
739 case DCBZ:
740 page->u.cbzpage = cbz_load_page (state.u.cbz, pageno);
741 cbz_run_page (state.u.cbz, page->u.cbzpage, dev, fz_identity, NULL);
742 break;
744 fz_free_device (dev);
746 page->pdimno = pindex;
747 page->pageno = pageno;
748 page->gen = state.gen;
749 page->type = state.type;
751 return page;
754 static struct tile *alloctile (int h)
756 int i;
757 int slicecount;
758 size_t tilesize;
759 struct tile *tile;
761 slicecount = (h + state.sliceheight - 1) / state.sliceheight;
762 tilesize = sizeof (*tile) + ((slicecount - 1) * sizeof (struct slice));
763 tile = calloc (tilesize, 1);
764 if (!tile) {
765 err (1, "can not allocate tile (%" FMT_s " bytes)", tilesize);
767 for (i = 0; i < slicecount; ++i) {
768 int sh = MIN (h, state.sliceheight);
769 tile->slices[i].h = sh;
770 tile->slices[i].texindex = -1;
771 h -= sh;
773 tile->slicecount = slicecount;
774 tile->sliceheight = state.sliceheight;
775 return tile;
778 static struct tile *rendertile (struct page *page, int x, int y, int w, int h)
780 fz_bbox bbox;
781 fz_device *dev;
782 struct tile *tile;
783 struct pagedim *pdim;
785 tile = alloctile (h);
786 pdim = &state.pagedims[page->pdimno];
788 bbox = pdim->bounds;
789 bbox.x0 += x;
790 bbox.y0 += y;
791 bbox.x1 = bbox.x0 + w;
792 bbox.y1 = bbox.y0 + h;
794 if (state.pig) {
795 if (state.pig->w == w
796 && state.pig->h == h
797 && state.pig->colorspace == state.colorspace) {
798 tile->pixmap = state.pig;
799 tile->pixmap->x = bbox.x0;
800 tile->pixmap->y = bbox.y0;
802 else {
803 fz_drop_pixmap (state.ctx, state.pig);
805 state.pig = NULL;
807 if (!tile->pixmap) {
808 tile->pixmap =
809 fz_new_pixmap_with_rect (state.ctx, state.colorspace, bbox);
812 tile->w = w;
813 tile->h = h;
814 clearpixmap (tile->pixmap);
815 dev = fz_new_draw_device (state.ctx, tile->pixmap);
816 fz_execute_display_list (page->dlist, dev, pagectm (page), bbox, NULL);
817 fz_free_device (dev);
819 return tile;
822 static void initpdims (void)
824 int pageno;
825 double start, end;
827 start = now ();
828 for (pageno = 0; pageno < state.pagecount; ++pageno) {
829 int rotate;
830 fz_obj *pageobj;
831 struct pagedim *p;
832 fz_rect mediabox;
834 switch (state.type) {
835 case DPDF:
836 pageobj = state.u.pdf->page_objs[pageno];
838 if (state.trimmargins) {
839 fz_obj *obj;
840 pdf_page *page;
842 page = pdf_load_page (state.u.pdf, pageno);
843 obj = fz_dict_gets (pageobj, "llpp.TrimBox");
844 if (state.trimanew || !obj) {
845 fz_rect rect;
846 fz_bbox bbox;
847 fz_matrix ctm;
848 fz_device *dev;
850 dev = fz_new_bbox_device (state.ctx, &bbox);
851 dev->hints |= FZ_IGNORE_SHADE;
852 ctm = fz_invert_matrix (page->ctm);
853 pdf_run_page (state.u.pdf, page, dev, fz_identity, NULL);
854 fz_free_device (dev);
856 rect.x0 = bbox.x0 + state.trimfuzz.x0;
857 rect.x1 = bbox.x1 + state.trimfuzz.x1;
858 rect.y0 = bbox.y0 + state.trimfuzz.y0;
859 rect.y1 = bbox.y1 + state.trimfuzz.y1;
860 rect = fz_transform_rect (ctm, rect);
861 rect = fz_intersect_rect (rect, page->mediabox);
863 if (fz_is_empty_rect (rect)) {
864 mediabox = page->mediabox;
866 else {
867 mediabox = rect;
870 obj = fz_new_array (state.ctx, 4);
871 fz_array_push (obj, fz_new_real (state.ctx, mediabox.x0));
872 fz_array_push (obj, fz_new_real (state.ctx, mediabox.y0));
873 fz_array_push (obj, fz_new_real (state.ctx, mediabox.x1));
874 fz_array_push (obj, fz_new_real (state.ctx, mediabox.y1));
875 fz_dict_puts (pageobj, "llpp.TrimBox", obj);
877 else {
878 mediabox.x0 = fz_to_real (fz_array_get (obj, 0));
879 mediabox.y0 = fz_to_real (fz_array_get (obj, 1));
880 mediabox.x1 = fz_to_real (fz_array_get (obj, 2));
881 mediabox.y1 = fz_to_real (fz_array_get (obj, 3));
884 rotate = page->rotate;
885 pdf_free_page (state.u.pdf, page);
887 printd (state.sock, "progress %f Trimming %d",
888 (double) (pageno + 1) / state.pagecount,
889 pageno + 1);
891 else {
892 fz_rect cropbox;
894 mediabox = pdf_to_rect (state.ctx,
895 fz_dict_gets (pageobj, "MediaBox"));
896 if (fz_is_empty_rect (mediabox)) {
897 fprintf (stderr, "cannot find page size for page %d\n",
898 pageno+1);
899 mediabox.x0 = 0;
900 mediabox.y0 = 0;
901 mediabox.x1 = 612;
902 mediabox.y1 = 792;
905 cropbox = pdf_to_rect (state.ctx,
906 fz_dict_gets (pageobj, "CropBox"));
907 if (!fz_is_empty_rect (cropbox)) {
908 mediabox = fz_intersect_rect (mediabox, cropbox);
910 rotate = fz_to_int (fz_dict_gets (pageobj, "Rotate"));
912 break;
914 case DXPS:
916 xps_page *page;
918 page = xps_load_page (state.u.xps, pageno);
919 mediabox = xps_bound_page (state.u.xps, page);
920 rotate = 0;
921 if (state.trimmargins) {
922 fz_rect rect;
923 fz_bbox bbox;
924 fz_device *dev;
926 dev = fz_new_bbox_device (state.ctx, &bbox);
927 dev->hints |= FZ_IGNORE_SHADE;
928 xps_run_page (state.u.xps, page, dev, fz_identity, NULL);
929 fz_free_device (dev);
931 rect.x0 = bbox.x0 + state.trimfuzz.x0;
932 rect.x1 = bbox.x1 + state.trimfuzz.x1;
933 rect.y0 = bbox.y0 + state.trimfuzz.y0;
934 rect.y1 = bbox.y1 + state.trimfuzz.y1;
935 rect = fz_intersect_rect (rect, mediabox);
937 if (!fz_is_empty_rect (rect)) {
938 mediabox = rect;
941 xps_free_page (state.u.xps, page);
942 printd (state.sock, "progress %f loading %d",
943 (double) (pageno + 1) / state.pagecount,
944 pageno + 1);
946 break;
948 case DCBZ:
950 if (state.trimmargins) {
951 cbz_page *page;
953 page = cbz_load_page (state.u.cbz, pageno);
954 mediabox = cbz_bound_page (state.u.cbz, page);
955 rotate = 0;
956 cbz_free_page (state.u.cbz, page);
957 printd (state.sock, "progress %f Trimming %d",
958 (double) (pageno + 1) / state.pagecount,
959 pageno + 1);
961 else {
962 mediabox.x0 = mediabox.y0 = 0;
963 mediabox.x1 = 900;
964 mediabox.y1 = 900;
967 break;
970 if (state.pagedimcount == 0
971 || (p = &state.pagedims[state.pagedimcount-1], p->rotate != rotate)
972 || memcmp (&p->mediabox, &mediabox, sizeof (mediabox))) {
973 size_t size;
975 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
976 state.pagedims = realloc (state.pagedims, size);
977 if (!state.pagedims) {
978 err (1, "realloc pagedims to %" FMT_s " (%d elems)",
979 size, state.pagedimcount + 1);
982 p = &state.pagedims[state.pagedimcount++];
983 p->rotate = rotate;
984 p->mediabox = mediabox;
985 p->pageno = pageno;
988 end = now ();
989 if (state.trimmargins) {
990 printd (state.sock, "progress 1 Trimmed %d pages in %f seconds",
991 state.pagecount, end - start);
993 else {
994 printd (state.sock, "vmsg Processed %d pages in %f seconds",
995 state.pagecount, end - start);
997 state.trimanew = 0;
1000 static void layout (void)
1002 int pindex;
1003 fz_rect box;
1004 fz_matrix ctm;
1005 double zoom, w, maxw = 0;
1006 struct pagedim *p = state.pagedims;
1008 if (state.proportional) {
1009 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
1010 box = fz_transform_rect (fz_rotate (p->rotate + state.rotate),
1011 p->mediabox);
1012 w = box.x1 - box.x0;
1013 maxw = MAX (w, maxw);
1017 p = state.pagedims;
1018 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
1019 fz_bbox bbox;
1021 ctm = fz_rotate (state.rotate);
1022 box = fz_transform_rect (fz_rotate (p->rotate + state.rotate),
1023 p->mediabox);
1024 w = box.x1 - box.x0;
1026 if (state.proportional) {
1027 double scale = w / maxw;
1028 zoom = (state.w / w) * scale;
1030 else {
1031 zoom = state.w / w;
1034 p->zoomctm = fz_scale (zoom, zoom);
1035 ctm = fz_concat (p->zoomctm, ctm);
1037 p->pagebox = fz_transform_rect (fz_rotate (p->rotate), p->mediabox);
1038 p->pagebox.x1 -= p->pagebox.x0;
1039 p->pagebox.y1 -= p->pagebox.y0;
1040 p->pagebox.x0 = 0;
1041 p->pagebox.y0 = 0;
1042 bbox = fz_round_rect (fz_transform_rect (ctm, p->pagebox));
1044 p->bounds = bbox;
1045 p->left = state.proportional ? ((maxw - w) * zoom) / 2.0 : 0;
1046 p->ctm = ctm;
1048 ctm = fz_identity;
1049 ctm = fz_concat (ctm, fz_translate (0, -p->mediabox.y1));
1050 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
1051 ctm = fz_concat (ctm, fz_rotate (p->rotate + state.rotate));
1052 p->lctm = ctm;
1054 p->tctmready = 0;
1057 while (p-- != state.pagedims) {
1058 int w = p->bounds.x1 - p->bounds.x0;
1059 int h = p->bounds.y1 - p->bounds.y0;
1061 printd (state.sock, "pdim %d %d %d %d", p->pageno, w, h, p->left);
1065 static void recurse_outline (fz_outline *outline, int level)
1067 while (outline) {
1068 fz_link_dest *dest;
1069 int i, top = 0;
1070 struct pagedim *pdim = state.pagedims;
1072 dest = &outline->dest;
1073 for (i = 0; i < state.pagedimcount; ++i) {
1074 if (state.pagedims[i].pageno > dest->ld.gotor.page)
1075 break;
1076 pdim = &state.pagedims[i];
1078 if (dest->ld.gotor.flags & fz_link_flag_t_valid) {
1079 fz_point p;
1080 p.x = 0;
1081 p.y = dest->ld.gotor.lt.y;
1082 p = fz_transform_point (pdim->lctm, p);
1083 top = p.y;
1085 if (dest->ld.gotor.page >= 0 && dest->ld.gotor.page < 1<<30) {
1086 int h;
1087 double y0, y1;
1089 y0 = MIN (pdim->bounds.y0, pdim->bounds.y1);
1090 y1 = MAX (pdim->bounds.y0, pdim->bounds.y1);
1091 h = y1 - y0;
1092 printd (state.sock, "o %d %d %d %d %s",
1093 level, dest->ld.gotor.page, top, h, outline->title);
1095 if (outline->down) {
1096 recurse_outline (outline->down, level + 1);
1098 outline = outline->next;
1102 static void process_outline (void)
1104 fz_outline *outline;
1106 if (!state.needoutline) return;
1108 state.needoutline = 0;
1109 switch (state.type) {
1110 case DPDF:
1111 outline = pdf_load_outline (state.u.pdf);
1112 break;
1113 case DXPS:
1114 outline = xps_load_outline (state.u.xps);
1115 break;
1116 default:
1117 outline = NULL;
1118 break;
1120 if (outline) {
1121 recurse_outline (outline, 0);
1122 fz_free_outline (state.ctx, outline);
1126 static int comparespans (const void *l, const void *r)
1128 fz_text_span const *const*ls = l;
1129 fz_text_span const *const*rs = r;
1130 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
1133 /* wishful thinking function */
1134 static void search (regex_t *re, int pageno, int y, int forward)
1136 int i, j;
1137 int ret;
1138 char *p;
1139 char buf[256];
1140 fz_matrix ctm;
1141 fz_device *tdev;
1142 union { void *ptr; pdf_page *pdfpage; xps_page *xpspage; } u;
1143 fz_text_span *text, *span, **pspan;
1144 struct pagedim *pdim, *pdimprev;
1145 int stop = 0;
1146 int niters = 0;
1147 int nspans;
1148 double start, end;
1150 if (!(state.type == DPDF || state.type == DXPS))
1151 return;
1153 start = now ();
1154 while (pageno >= 0 && pageno < state.pagecount && !stop) {
1155 if (niters++ == 5) {
1156 niters = 0;
1157 if (hasdata (state.sock)) {
1158 printd (state.sock,
1159 "progress 1 attention requested aborting search at %d",
1160 pageno);
1161 stop = 1;
1163 else {
1164 printd (state.sock, "progress %f searching in page %d",
1165 (double) (pageno + 1) / state.pagecount,
1166 pageno);
1169 pdimprev = NULL;
1170 for (i = 0; i < state.pagedimcount; ++i) {
1171 pdim = &state.pagedims[i];
1172 if (pdim->pageno == pageno) {
1173 goto found;
1175 if (pdim->pageno > pageno) {
1176 pdim = pdimprev;
1177 goto found;
1179 pdimprev = pdim;
1181 pdim = pdimprev;
1182 found:
1184 text = fz_new_text_span (state.ctx);
1185 tdev = fz_new_text_device (state.ctx, text);
1187 switch (state.type) {
1188 case DPDF:
1189 u.pdfpage = pdf_load_page (state.u.pdf, pageno);
1190 pdf_run_page (state.u.pdf, u.pdfpage, tdev, fz_identity, NULL);
1191 break;
1193 case DXPS:
1194 u.xpspage = xps_load_page (state.u.xps, pageno);
1195 xps_run_page (state.u.xps, u.xpspage, tdev, fz_identity, NULL);
1196 break;
1199 fz_free_device (tdev);
1201 nspans = 0;
1202 for (span = text; span; span = span->next) {
1203 nspans++;
1205 pspan = malloc (sizeof (void *) * nspans);
1206 if (!pspan) {
1207 err (1, "malloc span pointers %" FMT_s, sizeof (void *) * nspans);
1209 for (i = 0, span = text; span; span = span->next, ++i) {
1210 pspan[i] = span;
1212 qsort (pspan, nspans, sizeof (fz_text_span *), comparespans);
1214 j = forward ? 0 : nspans - 1;
1215 while (nspans--) {
1216 regmatch_t rm;
1218 span = pspan[j];
1219 j += forward ? 1 : -1;
1220 p = buf;
1221 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
1222 if (forward) {
1223 if (span->text[i].bbox.y0 < y + 1) {
1224 continue;
1227 else {
1228 if (span->text[i].bbox.y0 > y - 1) {
1229 continue;
1232 if (span->text[i].c < 256) {
1233 *p++ = span->text[i].c;
1235 else {
1236 *p++ = '?';
1239 if (p == buf) {
1240 continue;
1242 *p++ = 0;
1244 ret = regexec (re, buf, 1, &rm, 0);
1245 if (ret) {
1246 if (ret != REG_NOMATCH) {
1247 size_t size;
1248 char errbuf[80];
1249 size = regerror (ret, re, errbuf, sizeof (errbuf));
1250 printd (state.sock,
1251 "msg regexec error `%.*s'",
1252 (int) size, errbuf);
1253 fz_free_text_span (state.ctx, text);
1254 state.freepage (u.ptr);
1255 free (pspan);
1256 return;
1259 else {
1260 fz_bbox *sb, *eb;
1261 fz_point p1, p2, p3, p4;
1263 sb = &span->text[rm.rm_so].bbox;
1264 eb = &span->text[rm.rm_eo - 1].bbox;
1266 p1.x = sb->x0;
1267 p1.y = sb->y0;
1268 p2.x = eb->x1;
1269 p2.y = sb->y0;
1270 p3.x = eb->x1;
1271 p3.y = eb->y1;
1272 p4.x = sb->x0;
1273 p4.y = eb->y1;
1275 switch (state.type) {
1276 case DPDF:
1277 trimctm (u.pdfpage, pdim - state.pagedims);
1278 ctm = fz_concat (pdim->tctm, pdim->zoomctm);
1279 break;
1281 case DXPS:
1282 ctm = pdim->ctm;
1283 break;
1286 p1 = fz_transform_point (ctm, p1);
1287 p2 = fz_transform_point (ctm, p2);
1288 p3 = fz_transform_point (ctm, p3);
1289 p4 = fz_transform_point (ctm, p4);
1291 if (!stop) {
1292 printd (state.sock,
1293 "firstmatch %d %d %f %f %f %f %f %f %f %f",
1294 pageno, 1,
1295 p1.x, p1.y,
1296 p2.x, p2.y,
1297 p3.x, p3.y,
1298 p4.x, p4.y);
1300 printd (state.sock,
1301 "progress 1 found at %d `%.*s' in %f sec",
1302 pageno, (int) (rm.rm_eo - rm.rm_so), &buf[rm.rm_so],
1303 now () - start);
1305 else {
1306 printd (state.sock,
1307 "match %d %d %f %f %f %f %f %f %f %f",
1308 pageno, 2,
1309 p1.x, p1.y,
1310 p2.x, p2.y,
1311 p3.x, p3.y,
1312 p4.x, p4.y);
1314 stop = 1;
1317 if (forward) {
1318 pageno += 1;
1319 y = 0;
1321 else {
1322 pageno -= 1;
1323 y = INT_MAX;
1325 fz_free_text_span (state.ctx, text);
1326 state.freepage (u.ptr);
1327 free (pspan);
1329 end = now ();
1330 if (!stop) {
1331 printd (state.sock, "progress 1 no matches %f sec", end - start);
1333 printd (state.sock, "clearrects");
1336 static void set_tex_params (int colorspace)
1338 switch (colorspace) {
1339 case 0:
1340 state.texiform = GL_RGBA8;
1341 state.texform = GL_RGBA;
1342 state.texty = GL_UNSIGNED_BYTE;
1343 state.colorspace = fz_device_rgb;
1344 break;
1345 case 1:
1346 state.texiform = GL_RGBA8;
1347 state.texform = GL_BGRA;
1348 state.texty = fz_is_big_endian ()
1349 ? GL_UNSIGNED_INT_8_8_8_8
1350 : GL_UNSIGNED_INT_8_8_8_8_REV;
1351 state.colorspace = fz_device_bgr;
1352 break;
1353 case 2:
1354 state.texiform = GL_LUMINANCE_ALPHA;
1355 state.texform = GL_LUMINANCE_ALPHA;
1356 state.texty = GL_UNSIGNED_BYTE;
1357 state.colorspace = fz_device_gray;
1358 break;
1359 default:
1360 errx (1, "invalid colorspce %d", colorspace);
1364 static void realloctexts (int texcount)
1366 size_t size;
1368 if (texcount == state.texcount) return;
1370 if (texcount < state.texcount) {
1371 glDeleteTextures (state.texcount - texcount,
1372 state.texids + texcount);
1375 size = texcount * sizeof (*state.texids);
1376 state.texids = realloc (state.texids, size);
1377 if (!state.texids) {
1378 err (1, "realloc texids %" FMT_s, size);
1381 size = texcount * sizeof (*state.texowners);
1382 state.texowners = realloc (state.texowners, size);
1383 if (!state.texowners) {
1384 err (1, "realloc texowners %" FMT_s, size);
1386 if (texcount > state.texcount) {
1387 int i;
1389 glGenTextures (texcount - state.texcount,
1390 state.texids + state.texcount);
1391 for (i = state.texcount; i < texcount; ++i) {
1392 state.texowners[i].w = -1;
1393 state.texowners[i].slice = NULL;
1396 state.texcount = texcount;
1397 state.texindex = 0;
1400 static
1401 #ifdef _WIN32
1402 DWORD _stdcall
1403 #else
1404 void *
1405 #endif
1406 mainloop (void *unused)
1408 char *p = NULL;
1409 int len, ret, oldlen = 0;
1411 for (;;) {
1412 len = readlen (state.sock);
1413 if (len == 0) {
1414 errx (1, "readlen returned 0");
1417 if (oldlen < len + 1) {
1418 p = realloc (p, len + 1);
1419 if (!p) {
1420 err (1, "realloc %d failed", len + 1);
1422 oldlen = len + 1;
1424 readdata (state.sock, p, len);
1425 p[len] = 0;
1427 if (!strncmp ("open", p, 4)) {
1428 size_t filenamelen;
1429 char *password;
1430 char *filename = p + 5;
1432 filenamelen = strlen (filename);
1433 password = filename + filenamelen + 1;
1435 openxref (filename, password);
1436 pdfinfo ();
1437 initpdims ();
1438 printd (state.sock, "msg Opened %s (press h/F1 to get help)",
1439 filename);
1440 state.needoutline = 1;
1442 else if (!strncmp ("cs", p, 2)) {
1443 int i, colorspace;
1445 ret = sscanf (p + 2, " %d", &colorspace);
1446 if (ret != 1) {
1447 errx (1, "malformed cs `%.*s' ret=%d", len, p, ret);
1449 lock ("cs");
1450 set_tex_params (colorspace);
1451 for (i = 0; i < state.texcount; ++i) {
1452 state.texowners[i].w = -1;
1453 state.texowners[i].slice = NULL;
1455 unlock ("cs");
1457 else if (!strncmp ("freepage", p, 8)) {
1458 void *ptr;
1460 ret = sscanf (p + 8, " %" FMT_ptr, FMT_ptr_cast (&ptr));
1461 if (ret != 1) {
1462 errx (1, "malformed freepage `%.*s' ret=%d", len, p, ret);
1464 freepage (ptr);
1466 else if (!strncmp ("freetile", p, 8)) {
1467 void *ptr;
1469 ret = sscanf (p + 8, " %" FMT_ptr, FMT_ptr_cast (&ptr));
1470 if (ret != 1) {
1471 errx (1, "malformed freetile `%.*s' ret=%d", len, p, ret);
1473 freetile (ptr);
1475 else if (!strncmp ("search", p, 6)) {
1476 int icase, pageno, y, ret, len2, forward;
1477 char *pattern;
1478 regex_t re;
1480 ret = sscanf (p + 6, " %d %d %d %d,%n",
1481 &icase, &pageno, &y, &forward, &len2);
1482 if (ret != 4) {
1483 errx (1, "malformed search `%s' ret=%d", p, ret);
1486 pattern = p + 6 + len2;
1487 ret = regcomp (&re, pattern,
1488 REG_EXTENDED | (icase ? REG_ICASE : 0));
1489 if (ret) {
1490 char errbuf[80];
1491 size_t size;
1493 size = regerror (ret, &re, errbuf, sizeof (errbuf));
1494 printd (state.sock, "msg regcomp failed `%.*s'",
1495 (int) size, errbuf);
1497 else {
1498 search (&re, pageno, y, forward);
1499 regfree (&re);
1502 else if (!strncmp ("geometry", p, 8)) {
1503 int w, h;
1505 printd (state.sock, "clear");
1506 ret = sscanf (p + 8, " %d %d", &w, &h);
1507 if (ret != 2) {
1508 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
1511 lock ("geometry");
1512 state.h = h;
1513 if (w != state.w) {
1514 int i;
1515 state.w = w;
1516 for (i = 0; i < state.texcount; ++i) {
1517 state.texowners[i].slice = NULL;
1520 layout ();
1521 process_outline ();
1522 state.gen++;
1523 unlock ("geometry");
1524 printd (state.sock, "continue %d", state.pagecount);
1526 else if (!strncmp ("reqlayout", p, 9)) {
1527 int rotate, proportional;
1529 printd (state.sock, "clear");
1530 ret = sscanf (p + 9, " %d %d", &rotate, &proportional);
1531 if (ret != 2) {
1532 errx (1, "bad reqlayout line `%.*s' ret=%d", len, p, ret);
1534 lock ("reqlayout");
1535 state.rotate = rotate;
1536 state.proportional = proportional;
1537 layout ();
1538 unlock ("reqlayout");
1539 printd (state.sock, "continue %d", state.pagecount);
1541 else if (!strncmp ("page", p, 4)) {
1542 double a, b;
1543 struct page *page;
1544 int pageno, pindex, ret;
1546 ret = sscanf (p + 4, " %d %d", &pageno, &pindex);
1547 if (ret != 2) {
1548 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
1551 lock ("page");
1552 a = now ();
1553 page = loadpage (pageno, pindex);
1554 b = now ();
1555 unlock ("page");
1557 printd (state.sock, "page %" FMT_ptr " %f",
1558 FMT_ptr_cast2 (page), b - a);
1560 else if (!strncmp ("tile", p, 4)) {
1561 int x, y, w, h, ret;
1562 struct page *page;
1563 struct tile *tile;
1564 double a, b;
1566 ret = sscanf (p + 4, " %" FMT_ptr " %d %d %d %d",
1567 FMT_ptr_cast (&page), &x, &y, &w, &h);
1568 if (ret != 5) {
1569 errx (1, "bad tile line `%.*s' ret=%d", len, p, ret);
1572 lock ("tile");
1573 a = now ();
1574 tile = rendertile (page, x, y, w, h);
1575 b = now ();
1576 unlock ("tile");
1578 printd (state.sock, "tile %d %d %" FMT_ptr " %u %f",
1579 x, y,
1580 FMT_ptr_cast2 (tile),
1581 tile->w * tile->h * tile->pixmap->n,
1582 b - a);
1584 else if (!strncmp ("settrim", p, 7)) {
1585 int trimmargins;
1586 fz_bbox fuzz;
1588 ret = sscanf (p + 7, " %d %d %d %d %d", &trimmargins,
1589 &fuzz.x0, &fuzz.y0, &fuzz.x1, &fuzz.y1);
1590 if (ret != 5) {
1591 errx (1, "malformed settrim `%.*s' ret=%d", len, p, ret);
1593 printd (state.sock, "clear");
1594 lock ("settrim");
1595 state.trimmargins = trimmargins;
1596 state.needoutline = 1;
1597 if (memcmp (&fuzz, &state.trimfuzz, sizeof (fuzz))) {
1598 state.trimanew = 1;
1599 state.trimfuzz = fuzz;
1601 state.pagedimcount = 0;
1602 free (state.pagedims);
1603 state.pagedims = NULL;
1604 initpdims ();
1605 layout ();
1606 process_outline ();
1607 unlock ("settrim");
1608 printd (state.sock, "continue %d", state.pagecount);
1610 else if (!strncmp ("sliceh", p, 6)) {
1611 int h;
1613 ret = sscanf (p + 6, " %d", &h);
1614 if (ret != 1) {
1615 errx (1, "malformed sliceh `%.*s' ret=%d", len, p, ret);
1617 if (h != state.sliceheight) {
1618 int i;
1620 state.sliceheight = h;
1621 for (i = 0; i < state.texcount; ++i) {
1622 state.texowners[i].w = -1;
1623 state.texowners[i].h = -1;
1624 state.texowners[i].slice = NULL;
1628 else if (!strncmp ("interrupt", p, 9)) {
1629 printd (state.sock, "vmsg interrupted");
1631 else if (!strncmp ("quit", p, 4)) {
1632 return 0;
1634 else {
1635 errx (1, "unknown command %.*s", len, p);
1638 return 0;
1641 CAMLprim value ml_realloctexts (value texcount_v)
1643 CAMLparam1 (texcount_v);
1644 int ok;
1646 if (trylock ("ml_realloctexts")) {
1647 ok = 0;
1648 goto done;
1650 realloctexts (Int_val (texcount_v));
1651 ok = 1;
1652 unlock ("ml_realloctexts");
1654 done:
1655 CAMLreturn (Val_bool (ok));
1658 static void showsel (struct page *page, int ox, int oy)
1660 fz_bbox bbox;
1661 fz_text_span *span;
1662 struct mark first, last;
1664 first = page->fmark;
1665 last = page->lmark;
1667 if (!first.span || !last.span) return;
1669 glEnable (GL_BLEND);
1670 glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA);
1671 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1673 ox -= state.pagedims[page->pdimno].bounds.x0;
1674 oy -= state.pagedims[page->pdimno].bounds.y0;
1675 for (span = first.span; span; span = span->next) {
1676 int i, j, k;
1678 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0;
1680 j = 0;
1681 k = span->len - 1;
1683 if (span == page->fmark.span && span == page->lmark.span) {
1684 j = MIN (first.i, last.i);
1685 k = MAX (first.i, last.i);
1687 else if (span == first.span) {
1688 j = first.i;
1690 else if (span == last.span) {
1691 k = last.i;
1694 for (i = j; i <= k; ++i) {
1695 bbox = fz_union_bbox (bbox, span->text[i].bbox);
1697 lprintf ("%d %d %d %d oy=%d ox=%d\n",
1698 bbox.x0,
1699 bbox.y0,
1700 bbox.x1,
1701 bbox.y1,
1702 oy, ox);
1704 glRecti (bbox.x0 + ox, bbox.y0 + oy, bbox.x1 + ox, bbox.y1 + oy);
1706 if (span == last.span) break;
1708 glDisable (GL_BLEND);
1711 static void highlightlinks (struct page *page, int xoff, int yoff)
1713 fz_matrix ctm;
1714 fz_link *link, *links;
1716 switch (page->type) {
1717 case DPDF:
1718 links = page->u.pdfpage->links;
1719 break;
1721 case DXPS:
1722 links = page->u.xpspage->links;
1723 break;
1725 default:
1726 links = NULL;
1727 break;
1730 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1731 glEnable (GL_LINE_STIPPLE);
1732 glLineStipple (0.5, 0xcccc);
1734 ctm = fz_concat (pagectm (page), fz_translate (xoff, yoff));
1736 glBegin (GL_QUADS);
1737 for (link = links; link; link = link->next) {
1738 fz_point p1, p2, p3, p4;
1740 p1.x = link->rect.x0;
1741 p1.y = link->rect.y0;
1743 p2.x = link->rect.x1;
1744 p2.y = link->rect.y0;
1746 p3.x = link->rect.x1;
1747 p3.y = link->rect.y1;
1749 p4.x = link->rect.x0;
1750 p4.y = link->rect.y1;
1752 p1 = fz_transform_point (ctm, p1);
1753 p2 = fz_transform_point (ctm, p2);
1754 p3 = fz_transform_point (ctm, p3);
1755 p4 = fz_transform_point (ctm, p4);
1757 switch (link->dest.kind) {
1758 case FZ_LINK_GOTO: glColor3ub (255, 0, 0); break;
1759 case FZ_LINK_URI: glColor3ub (0, 0, 255); break;
1760 default: glColor3ub (0, 0, 0); break;
1763 glVertex2f (p1.x, p1.y);
1764 glVertex2f (p2.x, p2.y);
1765 glVertex2f (p3.x, p3.y);
1766 glVertex2f (p4.x, p4.y);
1768 glEnd ();
1770 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1771 glDisable (GL_LINE_STIPPLE);
1774 static void uploadslice (struct tile *tile, struct slice *slice)
1776 int offset;
1777 struct slice *slice1;
1779 offset = 0;
1780 for (slice1 = tile->slices; slice != slice1; slice1++) {
1781 offset += slice1->h * tile->w * tile->pixmap->n;
1783 if (slice->texindex != -1 && slice->texindex < state.texcount
1784 && state.texowners[slice->texindex].slice == slice) {
1785 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1787 else {
1788 int subimage = 0;
1789 int texindex = state.texindex++ % state.texcount;
1791 if (state.texowners[texindex].w == tile->w) {
1792 if (state.texowners[texindex].h >= slice->h) {
1793 subimage = 1;
1795 else {
1796 state.texowners[texindex].h = slice->h;
1799 else {
1800 state.texowners[texindex].h = slice->h;
1803 state.texowners[texindex].w = tile->w;
1804 state.texowners[texindex].slice = slice;
1805 slice->texindex = texindex;
1807 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[texindex]);
1808 if (subimage) {
1809 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1813 tile->w,
1814 slice->h,
1815 state.texform,
1816 state.texty,
1817 tile->pixmap->samples+offset
1820 else {
1821 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1823 state.texiform,
1824 tile->w,
1825 slice->h,
1827 state.texform,
1828 state.texty,
1829 tile->pixmap->samples+offset
1835 CAMLprim value ml_drawtile (value args_v, value ptr_v)
1837 CAMLparam2 (args_v, ptr_v);
1838 int dispx = Int_val (Field (args_v, 0));
1839 int dispy = Int_val (Field (args_v, 1));
1840 int dispw = Int_val (Field (args_v, 2));
1841 int disph = Int_val (Field (args_v, 3));
1842 int tilex = Int_val (Field (args_v, 4));
1843 int tiley = Int_val (Field (args_v, 5));
1844 char *s = String_val (ptr_v);
1845 struct tile *tile = parse_pointer ("ml_drawtile", s);
1847 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1849 int slicey, firstslice;
1850 struct slice *slice;
1852 firstslice = tiley / tile->sliceheight;
1853 slice = &tile->slices[firstslice];
1854 slicey = tiley % tile->sliceheight;
1856 while (disph > 0) {
1857 int dh;
1859 dh = slice->h - slicey;
1860 dh = MIN (disph, dh);
1861 uploadslice (tile, slice);
1863 glBegin (GL_QUADS);
1865 glTexCoord2i (tilex, slicey);
1866 glVertex2i (dispx, dispy);
1868 glTexCoord2i (tilex+dispw, slicey);
1869 glVertex2i (dispx+dispw, dispy);
1871 glTexCoord2i (tilex+dispw, slicey+dh);
1872 glVertex2i (dispx+dispw, dispy+dh);
1874 glTexCoord2i (tilex, slicey+dh);
1875 glVertex2i (dispx, dispy+dh);
1877 glEnd ();
1879 dispy += dh;
1880 disph -= dh;
1881 slice++;
1882 ARSERT (!(slice - tile->slices >= tile->slicecount && disph > 0));
1883 slicey = 0;
1886 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1887 CAMLreturn (Val_unit);
1890 CAMLprim value ml_postprocess (value ptr_v, value hlinks_v,
1891 value xoff_v, value yoff_v)
1893 CAMLparam4 (ptr_v, hlinks_v, xoff_v, yoff_v);
1894 int xoff = Int_val (xoff_v);
1895 int yoff = Int_val (yoff_v);
1896 char *s = String_val (ptr_v);
1897 struct page *page = parse_pointer ("ml_postprocess", s);
1899 if (Bool_val (hlinks_v)) highlightlinks (page, xoff, yoff);
1901 if (trylock ("ml_postprocess")) {
1902 goto done;
1904 showsel (page, xoff, yoff);
1905 unlock ("ml_postprocess");
1907 done:
1908 CAMLreturn (Val_unit);
1911 static fz_link *getlink (struct page *page, int x, int y)
1913 fz_point p;
1914 fz_matrix ctm;
1915 fz_link *link, *links;
1917 switch (page->type) {
1918 case DPDF:
1919 links = page->u.pdfpage->links;
1920 break;
1922 case DXPS:
1923 links = page->u.xpspage->links;
1924 break;
1926 default:
1927 links = NULL;
1928 break;
1930 p.x = x;
1931 p.y = y;
1933 ctm = fz_concat (trimctm (page->u.pdfpage, page->pdimno),
1934 state.pagedims[page->pdimno].ctm);
1935 ctm = fz_invert_matrix (ctm);
1936 p = fz_transform_point (ctm, p);
1938 for (link = links; link; link = link->next) {
1939 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1940 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1941 return link;
1945 return NULL;
1948 static void droptext (struct page *page)
1950 if (page->text) {
1951 fz_free_text_span (state.ctx, page->text);
1952 page->fmark.i = -1;
1953 page->lmark.i = -1;
1954 page->fmark.span = NULL;
1955 page->lmark.span = NULL;
1956 page->text = NULL;
1960 static void ensuretext (struct page *page)
1962 if (state.gen != page->gen) {
1963 droptext (page);
1964 page->gen = state.gen;
1966 if (!page->text) {
1967 fz_device *tdev;
1969 page->text = fz_new_text_span (state.ctx);
1970 tdev = fz_new_text_device (state.ctx, page->text);
1971 fz_execute_display_list (page->dlist,
1972 tdev,
1973 pagectm (page),
1974 fz_infinite_bbox, NULL);
1975 fz_free_device (tdev);
1979 CAMLprim value ml_whatsunder (value ptr_v, value x_v, value y_v)
1981 CAMLparam3 (ptr_v, x_v, y_v);
1982 CAMLlocal3 (ret_v, tup_v, str_v);
1983 fz_link *link;
1984 struct page *page;
1985 char *s = String_val (ptr_v);
1986 int x = Int_val (x_v), y = Int_val (y_v);
1987 struct pagedim *pdim;
1989 ret_v = Val_int (0);
1990 if (trylock ("ml_whatsunder")) {
1991 goto done;
1994 page = parse_pointer ("ml_whatsunder", s);
1995 pdim = &state.pagedims[page->pdimno];
1996 x += pdim->bounds.x0;
1997 y += pdim->bounds.y0;
1998 link = getlink (page, x, y);
1999 if (link) {
2000 switch (link->dest.kind) {
2001 case FZ_LINK_GOTO:
2003 int pageno;
2004 fz_point p;
2006 pageno = link->dest.ld.gotor.page;
2007 p.x = 0;
2008 p.y = 0;
2010 if (link->dest.ld.gotor.flags & fz_link_flag_t_valid) {
2011 p.y = link->dest.ld.gotor.lt.y;
2012 p = fz_transform_point (pdim->lctm, p);
2014 tup_v = caml_alloc_tuple (2);
2015 ret_v = caml_alloc_small (1, 1);
2016 Field (tup_v, 0) = Val_int (pageno);
2017 Field (tup_v, 1) = Val_int (p.y);
2018 Field (ret_v, 0) = tup_v;
2020 break;
2022 case FZ_LINK_URI:
2023 str_v = caml_copy_string (link->dest.ld.uri.uri);
2024 ret_v = caml_alloc_small (1, 0);
2025 Field (ret_v, 0) = str_v;
2026 break;
2028 default:
2029 printd (state.sock, "msg unhandled link kind %d", link->dest.kind);
2030 break;
2033 else {
2034 int i;
2035 fz_text_span *span;
2037 ensuretext (page);
2038 for (span = page->text; span; span = span->next) {
2039 for (i = 0; i < span->len; ++i) {
2040 fz_bbox *b;
2041 b = &span->text[i].bbox;
2042 if ((x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1)) {
2043 const char *n2 =
2044 span->font && span->font->name
2045 ? span->font->name
2046 : "Span has no font name"
2048 FT_FaceRec *face = span->font->ft_face;
2049 if (face && face->family_name) {
2050 char *s;
2051 char *n1 = face->family_name;
2052 size_t l1 = strlen (n1);
2053 size_t l2 = strlen (n2);
2055 if (l1 != l2 || memcmp (n1, n2, l1)) {
2056 s = malloc (l1 + l2 + 2);
2057 if (s) {
2058 memcpy (s, n2, l2);
2059 s[l2] = '=';
2060 memcpy (s + l2 + 1, n1, l1 + 1);
2061 str_v = caml_copy_string (s);
2062 free (s);
2066 if (str_v == 0) {
2067 str_v = caml_copy_string (n2);
2069 ret_v = caml_alloc_small (1, 2);
2070 Field (ret_v, 0) = str_v;
2071 goto unlock;
2076 unlock:
2077 unlock ("ml_whatsunder");
2079 done:
2080 CAMLreturn (ret_v);
2083 CAMLprim value ml_seltext (value ptr_v, value rect_v)
2085 CAMLparam2 (ptr_v, rect_v);
2086 fz_bbox *b;
2087 struct page *page;
2088 fz_text_span *span;
2089 struct mark first, last;
2090 int i, x0, x1, y0, y1;
2091 struct pagedim *pdim;
2092 char *s = String_val (ptr_v);
2094 if (trylock ("ml_seltext")) {
2095 goto done;
2098 page = parse_pointer ("ml_seltext", s);
2099 ensuretext (page);
2101 pdim = &state.pagedims[page->pdimno];
2103 x0 = Int_val (Field (rect_v, 0)) + pdim->bounds.x0;
2104 y0 = Int_val (Field (rect_v, 1)) + pdim->bounds.y0;
2105 x1 = Int_val (Field (rect_v, 2)) + pdim->bounds.x0;
2106 y1 = Int_val (Field (rect_v, 3)) + pdim->bounds.y0;
2108 if (0) {
2109 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
2110 glColor3ub (128, 128, 128);
2111 glRecti (x0, y0, x1, y1);
2112 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
2115 first.span = NULL;
2116 last.span = NULL;
2118 last.i = first.i = 0;
2119 first.span = page->text;
2120 for (span = page->text; span; span = span->next) {
2121 for (i = 0; i < span->len; ++i) {
2122 b = &span->text[i].bbox;
2123 int selected = 0;
2125 if (x0 >= b->x0 && x0 <= b->x1 && y0 >= b->y0 && y0 <= b->y1) {
2126 first.i = i;
2127 first.span = span;
2128 selected = 1;
2130 if (x1 >= b->x0 && x1 <= b->x1 && y1 >= b->y0 && y1 <= b->y1) {
2131 last.i = i;
2132 last.span = span;
2133 selected = 1;
2135 if (0 && selected) {
2136 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
2137 glColor3ub (128, 128, 128);
2138 glRecti (b->x0, b->y0, b->x1, b->y1);
2139 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
2144 if (y1 < y0 || x1 < x0) {
2145 int swap = 0;
2147 if (first.span == last.span) {
2148 swap = 1;
2150 else {
2151 if (y1 < y0) {
2152 for (span = first.span; span && span != last.span;
2153 span = span->next) {
2154 if (span->eol) {
2155 swap = 1;
2156 break;
2162 if (swap) {
2163 i = first.i;
2164 span = first.span;
2165 first.i = last.i;
2166 first.span = last.span;
2167 last.i = i;
2168 last.span = span;
2172 page->fmark = first;
2173 page->lmark = last;
2175 unlock ("ml_seltext");
2177 done:
2178 CAMLreturn (Val_unit);
2181 static int pipespan (FILE *f, fz_text_span *span, int a, int b)
2183 char buf[4];
2184 int i, len, ret;
2186 for (i = a; i <= b; ++i) {
2187 len = runetochar (buf, &span->text[i].c);
2188 ret = fwrite (buf, len, 1, f);
2190 if (ret != 1) {
2191 printd (state.sock, "msg failed to write %d bytes ret=%d: %s",
2192 len, ret, strerror (errno));
2193 return -1;
2196 return 0;
2199 CAMLprim value ml_copysel (value ptr_v)
2201 CAMLparam1 (ptr_v);
2202 FILE *f;
2203 struct page *page;
2204 char *s = String_val (ptr_v);
2206 if (trylock ("ml_copysel")) {
2207 goto done;
2210 if (!*s) {
2211 close:
2212 #ifdef USE_XSEL
2213 if (state.xselpipe) {
2214 int ret = pclose (state.xselpipe);
2215 if (ret) {
2216 printd (state.sock, "msg failed to close xsel pipe `%s'",
2217 strerror (errno));
2219 state.xselpipe = NULL;
2221 #else
2222 printf ("========================================\n");
2223 #endif
2225 else {
2226 fz_text_span *span;
2228 page = parse_pointer ("ml_sopysel", s);
2230 if (!page->fmark.span || !page->lmark.span) {
2231 printd (state.sock, "msg nothing to copy");
2232 goto unlock;
2235 f = stdout;
2236 #ifdef USE_XSEL
2237 if (!state.xselpipe) {
2238 state.xselpipe = popen ("xsel -i", "w");
2239 if (!state.xselpipe) {
2240 printd (state.sock, "msg failed to open xsel pipe `%s'",
2241 strerror (errno));
2243 else {
2244 f = state.xselpipe;
2247 else {
2248 f = state.xselpipe;
2250 #endif
2252 for (span = page->fmark.span;
2253 span && span != page->lmark.span->next;
2254 span = span->next) {
2255 int a = span == page->fmark.span ? page->fmark.i : 0;
2256 int b = span == page->lmark.span ? page->lmark.i : span->len - 1;
2257 if (pipespan (f, span, a, b)) {
2258 goto close;
2260 if (span->eol) {
2261 if (putc ('\n', f) == EOF) {
2262 printd (state.sock,
2263 "msg failed break line on xsel pipe `%s'",
2264 strerror (errno));
2265 goto close;
2269 page->lmark.span = NULL;
2270 page->fmark.span = NULL;
2273 unlock:
2274 unlock ("ml_copysel");
2276 done:
2277 CAMLreturn (Val_unit);
2280 CAMLprim value ml_getpdimrect (value pagedimno_v)
2282 CAMLparam1 (pagedimno_v);
2283 CAMLlocal1 (ret_v);
2284 int pagedimno = Int_val (pagedimno_v);
2285 fz_rect box;
2287 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
2288 if (trylock ("ml_getpdimrect")) {
2289 box = fz_empty_rect;
2291 else {
2292 box = state.pagedims[pagedimno].mediabox;
2293 unlock ("ml_getpdimrect");
2296 Store_double_field (ret_v, 0, box.x0);
2297 Store_double_field (ret_v, 1, box.x1);
2298 Store_double_field (ret_v, 2, box.y0);
2299 Store_double_field (ret_v, 3, box.y1);
2301 CAMLreturn (ret_v);
2304 static double getmaxw (void)
2306 int i;
2307 struct pagedim *p;
2308 double maxw = 0.0;
2310 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
2311 double x0, x1, w;
2313 x0 = MIN (p->mediabox.x0, p->mediabox.x1);
2314 x1 = MAX (p->mediabox.x0, p->mediabox.x1);
2316 w = x1 - x0;
2317 maxw = MAX (w, maxw);
2319 return maxw;
2322 CAMLprim value ml_getmaxw (value unit_v)
2324 CAMLparam1 (unit_v);
2325 CAMLlocal1 (ret_v);
2326 double maxw = 0.0;
2328 if (trylock ("ml_getmaxw")) {
2329 goto done;
2331 maxw = getmaxw ();
2332 unlock ("ml_getmaxw");
2333 done:
2334 ret_v = caml_copy_double (maxw);
2335 CAMLreturn (ret_v);
2338 CAMLprim value ml_zoom_for_height (value winw_v, value winh_v, value dw_v)
2340 CAMLparam3 (winw_v, winh_v, dw_v);
2341 CAMLlocal1 (ret_v);
2342 int i;
2343 double zoom = 1.0;
2344 double maxw = 0.0, maxh = 0.0;
2345 struct pagedim *p;
2346 double winw = Int_val (winw_v);
2347 double winh = Int_val (winh_v);
2348 double dw = Int_val (dw_v);
2349 double pw = 1.0, ph = 1.0, num, den;
2351 if (trylock ("ml_zoom_for_height")) {
2352 goto done;
2355 if (state.proportional) {
2356 maxw = getmaxw ();
2359 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
2360 double x0, x1, y0, y1, w, h, scaledh, scale;
2362 x0 = MIN (p->mediabox.x0, p->mediabox.x1);
2363 x1 = MAX (p->mediabox.x0, p->mediabox.x1);
2364 y0 = MIN (p->mediabox.y0, p->mediabox.y1);
2365 y1 = MAX (p->mediabox.y0, p->mediabox.y1);
2367 w = x1 - x0;
2368 h = y1 - y0;
2370 if (state.proportional) {
2371 scale = w / maxw;
2372 scaledh = h * scale;
2374 else {
2375 scale = 1.0;
2376 scaledh = h;
2379 if (scaledh > maxh) {
2380 maxh = scaledh;
2381 ph = scaledh;
2382 pw = w * scale;
2386 num = (winh * pw) + (ph * dw);
2387 den = ph * winw;
2388 zoom = num / den;
2390 unlock ("ml_zoom_for_height");
2391 done:
2392 ret_v = caml_copy_double (zoom);
2393 CAMLreturn (ret_v);
2396 #include "glfont.c"
2398 CAMLprim value ml_draw_string (value pt_v, value x_v, value y_v, value string_v)
2400 CAMLparam4 (pt_v, x_v, y_v, string_v);
2401 CAMLlocal1 (ret_v);
2402 int pt = Int_val(pt_v);
2403 int x = Int_val (x_v);
2404 int y = Int_val (y_v);
2405 double w;
2407 w = draw_string (state.face, pt, x, y, String_val (string_v));
2408 ret_v = caml_copy_double (w);
2409 CAMLreturn (ret_v);
2412 CAMLprim value ml_measure_string (value pt_v, value string_v)
2414 CAMLparam2 (pt_v, string_v);
2415 CAMLlocal1 (ret_v);
2416 int pt = Int_val (pt_v);
2417 double w;
2419 w = measure_string (state.face, pt, String_val (string_v));
2420 ret_v = caml_copy_double (w);
2421 CAMLreturn (ret_v);
2424 CAMLprim value ml_getpagebox (value opaque_v)
2426 CAMLparam1 (opaque_v);
2427 CAMLlocal1 (ret_v);
2428 fz_bbox bbox;
2429 fz_device *dev;
2430 char *s = String_val (opaque_v);
2431 struct page *page = parse_pointer ("ml_getpagebox", s);
2433 ret_v = caml_alloc_tuple (4);
2434 dev = fz_new_bbox_device (state.ctx, &bbox);
2435 dev->hints |= FZ_IGNORE_SHADE;
2437 switch (page->type) {
2438 case DPDF:
2439 pdf_run_page (state.u.pdf, page->u.pdfpage, dev, pagectm (page), NULL);
2440 break;
2442 case DXPS:
2443 xps_run_page (state.u.xps, page->u.xpspage, dev, pagectm (page), NULL);
2444 break;
2446 default:
2447 bbox = fz_infinite_bbox;
2448 break;
2451 fz_free_device (dev);
2452 Field (ret_v, 0) = Val_int (bbox.x0);
2453 Field (ret_v, 1) = Val_int (bbox.y0);
2454 Field (ret_v, 2) = Val_int (bbox.x1);
2455 Field (ret_v, 3) = Val_int (bbox.y1);
2457 CAMLreturn (ret_v);
2460 CAMLprim value ml_setaalevel (value level_v)
2462 CAMLparam1 (level_v);
2464 state.aalevel = Int_val (level_v);
2465 CAMLreturn (Val_unit);
2468 #if !defined _WIN32 && !defined __APPLE__
2469 #undef pixel
2470 #include <X11/X.h>
2471 #include <X11/Xlib.h>
2472 #include <X11/Xutil.h>
2473 #include <GL/glx.h>
2475 static void set_wm_class (int hack)
2477 if (hack) {
2478 Display *dpy;
2479 Window win;
2480 XClassHint hint;
2481 char *display;
2483 display = getenv ("DISPLAY");
2484 dpy = XOpenDisplay (display);
2485 if (!dpy) {
2486 fprintf (stderr, "XOpenDisplay `%s' failed\n",
2487 display ? display : "null");
2488 return;
2490 hint.res_name = "llpp";
2491 hint.res_class = "llpp";
2492 win = glXGetCurrentDrawable ();
2493 if (win == None) {
2494 fprintf (stderr, "glXGetCurrentDrawable returned None\n");
2495 XCloseDisplay (dpy);
2496 return;
2498 XSetClassHint (dpy, win, &hint);
2499 XCloseDisplay (dpy);
2502 #else
2503 #define set_wm_class(h) (void) (h)
2504 #endif
2506 enum { piunknown, pilinux, piwindows, piosx,
2507 pisun, pifreebsd, pidragonflybsd,
2508 piopenbsd, pimingw, picygwin };
2510 CAMLprim value ml_platform (value unit_v)
2512 CAMLparam1 (unit_v);
2513 int platid = piunknown;
2515 #if defined __linux__
2516 platid = pilinux;
2517 #elif defined __CYGWIN__
2518 platid = picygwin;
2519 #elif defined __MINGW32__
2520 platid = pimingw;
2521 #elif defined _WIN32
2522 platid = piwindows;
2523 #elif defined __DragonFly__
2524 platid = pidragonflybsd;
2525 #elif defined __FreeBSD__
2526 platid = pifreebsd;
2527 #elif defined __OpenBSD__
2528 platid = piopenbsd;
2529 #elif defined __sun__
2530 platid = pisun;
2531 #elif defined __APPLE__
2532 platid = piosx;
2533 #endif
2534 CAMLreturn (Val_int (platid));
2537 CAMLprim value ml_init (value sock_v, value params_v)
2539 CAMLparam2 (sock_v, params_v);
2540 CAMLlocal2 (trim_v, fuzz_v);
2541 #ifndef _WIN32
2542 int ret;
2543 #endif
2544 char *fontpath;
2545 int texcount;
2546 int wmclasshack;
2547 int colorspace;
2548 int mumemlimit;
2550 state.rotate = Int_val (Field (params_v, 0));
2551 state.proportional = Bool_val (Field (params_v, 1));
2552 trim_v = Field (params_v, 2);
2553 texcount = Int_val (Field (params_v, 3));
2554 state.sliceheight = Int_val (Field (params_v, 4));
2555 mumemlimit = Int_val (Field (params_v, 5));
2556 state.ctx = fz_new_context (&fz_alloc_default, mumemlimit);
2557 colorspace = Int_val (Field (params_v, 6));
2558 wmclasshack = Bool_val (Field (params_v, 7));
2559 fontpath = String_val (Field (params_v, 8));
2561 state.trimmargins = Bool_val (Field (trim_v, 0));
2562 fuzz_v = Field (trim_v, 1);
2563 state.trimfuzz.x0 = Int_val (Field (fuzz_v, 0));
2564 state.trimfuzz.y0 = Int_val (Field (fuzz_v, 1));
2565 state.trimfuzz.x1 = Int_val (Field (fuzz_v, 2));
2566 state.trimfuzz.y1 = Int_val (Field (fuzz_v, 3));
2568 set_tex_params (colorspace);
2569 set_wm_class (wmclasshack);
2571 if (*fontpath) {
2572 state.face = load_font (fontpath);
2574 else {
2575 unsigned int len;
2576 void *base = pdf_find_substitute_font (0, 0, 0, 0, &len);
2578 state.face = load_builtin_font (base, len);
2580 if (!state.face) _exit (1);
2582 fz_accelerate ();
2583 realloctexts (texcount);
2585 #ifdef _WIN32
2586 state.sock = Socket_val (sock_v);
2587 InitializeCriticalSection (&critsec);
2588 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
2589 if (state.thread == INVALID_HANDLE_VALUE) {
2590 errx (1, "CreateThread failed: %lx", GetLastError ());
2592 #else
2593 state.sock = Int_val (sock_v);
2594 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
2595 if (ret) {
2596 errx (1, "pthread_create: %s", strerror (ret));
2598 #endif
2600 CAMLreturn (Val_unit);