Kill stray empty line
[llpp.git] / link.c
blobabaef24427c7b3813d17fbed9d67ff92382ef0ed
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 _MSC_VER
11 #pragma warning (disable:4244)
12 #pragma warning (disable:4996)
13 #pragma warning (disable:4995)
14 #define NORETURN __declspec (noreturn)
15 #define UNUSED
16 #define OPTIMIZE(n)
17 #define GCC_FMT_ATTR(a, b)
18 #elif defined __GNUC__
19 #define NORETURN __attribute__ ((noreturn))
20 #define UNUSED __attribute__ ((unused))
21 #define OPTIMIZE(n) __attribute__ ((optimize ("O"#n)))
22 #define GCC_FMT_ATTR(a, b) __attribute__ ((format (printf, a, b)))
23 #else
24 #define NORETURN
25 #define UNUSED
26 #define OPTIMIZE(n)
27 #define GCC_FMT_ATTR(a, b)
28 #endif
30 #ifdef _WIN32
31 #define WIN32_LEAN_AND_MEAN
32 #include <windows.h>
33 #ifdef _WIN64
34 #define FMT_s "i64u"
35 #else
36 #define FMT_s "u"
37 #endif
38 static void NORETURN GCC_FMT_ATTR (2, 3)
39 winerr (int exitcode, const char *fmt, ...)
41 va_list ap;
42 DWORD savederror = GetLastError ();
44 va_start (ap, fmt);
45 vfprintf (stderr, fmt, ap);
46 va_end (ap);
47 fprintf (stderr, ": 0x%lx\n", savederror);
48 fflush (stderr);
49 _exit (exitcode);
51 #else
52 #define FMT_s "zu"
53 #include <unistd.h>
54 #include <pthread.h>
55 #include <sys/time.h>
56 #include <sys/types.h>
57 #include <sys/ioctl.h>
58 #endif
60 #ifdef __MINGW32__
61 /* some versions of MingW have non idempotent %p */
62 #include <inttypes.h>
63 #define FMT_ptr PRIxPTR
64 #define FMT_ptr_cast(p) ((intptr_t *) (p))
65 #define FMT_ptr_cast2(p) ((intptr_t) (p))
66 #else
67 #define FMT_ptr "p"
68 #define FMT_ptr_cast(p) (p)
69 #define FMT_ptr_cast2(p) (p)
70 #endif
72 #include <regex.h>
73 #include <ctype.h>
74 #include <stdarg.h>
75 #include <limits.h>
77 static void NORETURN GCC_FMT_ATTR (2, 3)
78 err (int exitcode, const char *fmt, ...)
80 va_list ap;
81 int savederrno;
83 savederrno = errno;
84 va_start (ap, fmt);
85 vfprintf (stderr, fmt, ap);
86 va_end (ap);
87 fprintf (stderr, ": %s\n", strerror (savederrno));
88 fflush (stderr);
89 _exit (exitcode);
92 static void NORETURN GCC_FMT_ATTR (2, 3)
93 errx (int exitcode, const char *fmt, ...)
95 va_list ap;
97 va_start (ap, fmt);
98 vfprintf (stderr, fmt, ap);
99 va_end (ap);
100 fputc ('\n', stderr);
101 fflush (stderr);
102 _exit (exitcode);
105 #ifdef __APPLE__
106 #include <OpenGL/gl.h>
107 #else
108 #include <GL/gl.h>
109 #endif
111 #ifndef GL_TEXTURE_RECTANGLE_ARB
112 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
113 #endif
115 #ifndef GL_BGRA
116 #define GL_BGRA 0x80E1
117 #endif
119 #ifndef GL_UNSIGNED_INT_8_8_8_8
120 #define GL_UNSIGNED_INT_8_8_8_8 0x8035
121 #endif
123 #ifndef GL_UNSIGNED_INT_8_8_8_8_REV
124 #define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
125 #endif
127 #include <caml/fail.h>
128 #include <caml/alloc.h>
129 #include <caml/memory.h>
130 #include <caml/unixsupport.h>
132 #include <fitz.h>
133 #include <mupdf.h>
134 #include <muxps.h>
135 #include <mucbz.h>
137 #include FT_FREETYPE_H
139 #if 0
140 #define lprintf printf
141 #else
142 #define lprintf(...)
143 #endif
145 #define ARSERT(cond) for (;;) { \
146 if (!(cond)) { \
147 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
149 break; \
152 struct slice {
153 int h;
154 int texindex;
157 struct tile {
158 int x, y, w, h;
159 int slicecount;
160 int sliceheight;
161 fz_pixmap *pixmap;
162 struct slice slices[1];
165 struct pagedim {
166 int pageno;
167 int rotate;
168 int left;
169 int tctmready;
170 fz_bbox bounds;
171 fz_rect pagebox;
172 fz_rect mediabox;
173 fz_matrix ctm, zoomctm, lctm, tctm;
176 enum { DPDF, DXPS, DCBZ };
178 struct page {
179 int gen;
180 int type;
181 int pageno;
182 int pdimno;
183 fz_text_span *text;
184 union {
185 void *ptr;
186 pdf_page *pdfpage;
187 xps_page *xpspage;
188 cbz_page *cbzpage;
189 } u;
190 fz_display_list *dlist;
191 struct mark {
192 int i;
193 fz_text_span *span;
194 } fmark, lmark;
195 void (*freepage) (void *);
198 struct {
199 int type;
200 int sliceheight;
201 struct pagedim *pagedims;
202 int pagecount;
203 int pagedimcount;
204 union {
205 pdf_document *pdf;
206 xps_document *xps;
207 cbz_document *cbz;
208 } u;
209 fz_context *ctx;
210 fz_glyph_cache *cache;
211 int w, h;
213 int texindex;
214 int texcount;
215 GLuint *texids;
217 GLenum texiform;
218 GLenum texform;
219 GLenum texty;
221 fz_colorspace *colorspace;
223 struct {
224 int w, h;
225 struct slice *slice;
226 } *texowners;
228 int rotate;
229 int proportional;
230 int trimmargins;
231 int needoutline;
232 int gen;
233 int aalevel;
235 int trimanew;
236 fz_bbox trimfuzz;
237 fz_pixmap *pig;
239 #ifdef _WIN32
240 HANDLE thread;
241 HANDLE cr, cw;
242 #else
243 pthread_t thread;
244 int cr, cw;
245 #endif
246 FT_Face face;
248 void (*closedoc) (void);
249 void (*freepage) (void *);
250 } state;
252 static void UNUSED debug_rect (const char *cap, fz_rect r)
254 printf ("%s(rect) %.2f,%.2f,%.2f,%.2f\n", cap, r.x0, r.y0, r.x1, r.y1);
257 static void UNUSED debug_bbox (const char *cap, fz_bbox r)
259 printf ("%s(bbox) %d,%d,%d,%d\n", cap, r.x0, r.y0, r.x1, r.y1);
262 static void UNUSED debug_matrix (const char *cap, fz_matrix m)
264 printf ("%s(matrix) %.2f,%.2f,%.2f,%.2f %.2f %.2f\n", cap,
265 m.a, m.b, m.c, m.d, m.e, m.f);
268 #ifdef _WIN32
269 static CRITICAL_SECTION critsec;
271 static void lock (void *unused)
273 (void) unused;
274 EnterCriticalSection (&critsec);
277 static void unlock (void *unused)
279 (void) unused;
280 LeaveCriticalSection (&critsec);
283 static int trylock (void *unused)
285 return TryEnterCriticalSection (&critsec) == 0;
288 CAMLprim value ml_seterrhandle (value is_gui_v, value handle_v)
290 /* http://stackoverflow.com/questions/5115569/c-win32-api-getstdhandlestd-output-handle-is-invalid-very-perplexing */
291 CAMLparam2 (is_gui_v, handle_v);
292 int fd;
294 if (!SetStdHandle (STD_ERROR_HANDLE, Handle_val (handle_v))) {
295 win32_maperr (GetLastError ());
296 uerror ("SetStdHandle", Nothing);
299 if (Bool_val (is_gui_v)) {
300 if (stderr) {
301 fclose (stderr);
303 fd = _open_osfhandle ((intptr_t) Handle_val (handle_v), 0);
304 if (fd < 0) {
305 uerror ("_open_osfhandle", Nothing);
307 *stderr = *_fdopen (fd, "w");
308 if (!stderr) {
309 _close (fd);
310 uerror ("_open_osfhandle", Nothing);
312 if (setvbuf (stderr, NULL, _IONBF, 0)) {
313 uerror ("stvbuf", Nothing);
316 CAMLreturn (Val_unit);
318 #else
319 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
321 static void lock (const char *cap)
323 int ret = pthread_mutex_lock (&mutex);
324 if (ret) {
325 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
329 static void unlock (const char *cap)
331 int ret = pthread_mutex_unlock (&mutex);
332 if (ret) {
333 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
337 static int trylock (const char *cap)
339 int ret = pthread_mutex_trylock (&mutex);
341 if (ret && ret != EBUSY) {
342 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
344 return ret == EBUSY;
347 CAMLprim value ml_seterrhandle (value is_gui_v, value handle_v)
349 CAMLparam2 (is_gui_v, handle_v);
350 (void) handle_v;
351 (void) is_gui_v;
352 CAMLreturn (Val_unit);
354 #endif
356 static void *parse_pointer (const char *cap, const char *s)
358 int ret;
359 void *ptr;
361 ret = sscanf (s, "%" FMT_ptr, FMT_ptr_cast (&ptr));
362 if (ret != 1) {
363 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
365 return ptr;
368 #ifdef _WIN32
369 static double now (void)
371 FILETIME ft;
372 uint64 tmp;
374 GetSystemTimeAsFileTime (&ft);
375 tmp = ft.dwHighDateTime;
376 tmp <<= 32;
377 tmp |= ft.dwLowDateTime;
378 return tmp * 1e-7;
381 static int hasdata (void)
383 BOOL okay;
384 DWORD avail;
386 okay = PeekNamedPipe (state.cr, NULL, 0, NULL, &avail, NULL);
387 if (!okay) winerr (1, "PeekNamedPipe");
388 return avail > 0;
391 static void readdata (void *p, int size)
393 BOOL okay;
394 DWORD nread;
396 okay = ReadFile (state.cr, p, size, &nread, NULL);
397 if (!okay || nread - size) {
398 err (1, "ReadFile (req %d, okay %d, ret %ld)", size, okay, nread);
402 static void writedata (char *p, int size)
404 BOOL okay;
405 char buf[4];
406 DWORD nwritten;
408 buf[0] = (size >> 24) & 0xff;
409 buf[1] = (size >> 16) & 0xff;
410 buf[2] = (size >> 8) & 0xff;
411 buf[3] = (size >> 0) & 0xff;
413 okay = WriteFile (state.cw, buf, 4, &nwritten, NULL);
414 if (!okay || nwritten != 4) {
415 winerr (1, "WriteFile okay %d ret %ld", okay, nwritten);
418 okay = WriteFile (state.cw, p, size, &nwritten, NULL);
419 if (!okay || nwritten - size) {
420 winerr (1, "WriteFile (req %d, okay %d, ret %ld)",
421 size, okay, nwritten);
424 #else
425 static double now (void)
427 struct timeval tv;
429 if (gettimeofday (&tv, NULL)) {
430 err (1, "gettimeofday");
432 return tv.tv_sec + tv.tv_usec*1e-6;
435 static int hasdata (void)
437 int ret, avail;
438 ret = ioctl (state.cr, FIONREAD, &avail);
439 if (ret) err (1, "hasdata: FIONREAD error ret=%d", ret);
440 return avail > 0;
443 static void readdata (void *p, int size)
445 ssize_t n;
447 n = read (state.cr, p, size);
448 if (n - size) {
449 if (!n) errx (1, "EOF while reading");
450 err (1, "read (req %d, ret %zd)", size, n);
454 static void writedata (char *p, int size)
456 char buf[4];
457 ssize_t n;
459 buf[0] = (size >> 24) & 0xff;
460 buf[1] = (size >> 16) & 0xff;
461 buf[2] = (size >> 8) & 0xff;
462 buf[3] = (size >> 0) & 0xff;
464 n = write (state.cw, buf, 4);
465 if (n != 4) {
466 if (!n) errx (1, "EOF while writing length");
467 err (1, "write %zd", n);
470 n = write (state.cw, p, size);
471 if (n - size) {
472 if (!n) errx (1, "EOF while writing data");
473 err (1, "write (req %d, ret %zd)", size, n);
476 #endif
478 static int readlen (void)
480 unsigned char p[4];
482 readdata (p, 4);
483 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
486 static void GCC_FMT_ATTR (1, 2) printd (const char *fmt, ...)
488 int size = 200, len;
489 va_list ap;
490 char *buf;
492 buf = malloc (size);
493 for (;;) {
494 if (!buf) err (errno, "malloc for temp buf (%d bytes) failed", size);
496 va_start (ap, fmt);
497 len = vsnprintf (buf, size, fmt, ap);
498 va_end (ap);
500 if (len > -1 && len < size) {
501 writedata (buf, len);
502 break;
505 if (len > -1) {
506 size = len + 1;
508 else {
509 size *= 2;
511 buf = realloc (buf, size);
513 free (buf);
516 static void closepdf (void)
518 if (state.u.pdf) {
519 pdf_close_document (state.u.pdf);
520 state.u.pdf = NULL;
524 static void closexps (void)
526 if (state.u.xps) {
527 xps_close_document (state.u.xps);
528 state.u.xps = NULL;
532 static void closecbz (void)
534 if (state.u.cbz) {
535 cbz_close_document (state.u.cbz);
536 state.u.cbz = NULL;
540 static void freepdfpage (void *ptr)
542 pdf_free_page (state.u.pdf, ptr);
545 static void freexpspage (void *ptr)
547 xps_free_page (state.u.xps, ptr);
550 static void freecbzpage (void *ptr)
552 cbz_free_page (state.u.cbz, ptr);
555 static void openxref (char *filename, char *password)
557 int i, len;
559 for (i = 0; i < state.texcount; ++i) {
560 state.texowners[i].w = -1;
561 state.texowners[i].slice = NULL;
564 if (state.closedoc) state.closedoc ();
566 len = strlen (filename);
568 state.type = DPDF;
569 if (len > 4) {
570 char ext[4];
572 ext[0] = tolower (filename[len-3]);
573 ext[1] = tolower (filename[len-2]);
574 ext[2] = tolower (filename[len-1]);
576 /**/ if (ext[0] == 'x' && ext[1] == 'p' && ext[2] == 's') {
577 state.type = DXPS;
579 else if (ext[0] == 'c' && ext[1] == 'b' && ext[2] == 'z') {
580 state.type = DCBZ;
584 if (state.pagedims) {
585 free (state.pagedims);
586 state.pagedims = NULL;
588 state.pagedimcount = 0;
590 fz_set_aa_level (state.ctx, state.aalevel);
591 switch (state.type) {
592 case DPDF:
593 state.u.pdf = pdf_open_document (state.ctx, filename);
594 if (pdf_needs_password (state.u.pdf)) {
595 int okay = pdf_authenticate_password (state.u.pdf, password);
596 if (!okay) {
597 errx (1, "invalid password");
600 state.pagecount = pdf_count_pages (state.u.pdf);
601 state.closedoc = closepdf;
602 state.freepage = freepdfpage;
603 break;
605 case DXPS:
606 state.u.xps = xps_open_document (state.ctx, filename);
607 state.pagecount = xps_count_pages (state.u.xps);
608 state.closedoc = closexps;
609 state.freepage = freexpspage;
610 break;
612 case DCBZ:
613 state.u.cbz = cbz_open_document (state.ctx, filename);
614 state.pagecount = cbz_count_pages (state.u.cbz);
615 state.closedoc = closecbz;
616 state.freepage = freecbzpage;
617 break;
621 static void pdfinfo (void)
623 if (state.type == DPDF) {
624 fz_obj *infoobj;
626 printd ("info PDF version\t%d.%d",
627 state.u.pdf->version / 10, state.u.pdf->version % 10);
629 infoobj = fz_dict_gets (state.u.pdf->trailer, "Info");
630 if (infoobj) {
631 int i;
632 char *s;
633 char *items[] = { "Title", "Author", "Creator",
634 "Producer", "CreationDate" };
636 for (i = 0; i < sizeof (items) / sizeof (*items); ++i) {
637 fz_obj *obj = fz_dict_gets (infoobj, items[i]);
638 s = pdf_to_utf8 (state.ctx, obj);
639 if (*s) {
640 if (i == 0) {
641 printd ("title %s", s);
643 printd ("info %s\t%s", items[i], s);
645 fz_free (state.ctx, s);
648 printd ("infoend");
652 static void unlinktile (struct tile *tile)
654 int i;
656 for (i = 0; i < tile->slicecount; ++i) {
657 struct slice *s = &tile->slices[i];
659 if (s->texindex != -1) {
660 if (state.texowners[s->texindex].slice == s) {
661 state.texowners[s->texindex].slice = NULL;
667 static void freepage (struct page *page)
669 if (page->text) {
670 fz_free_text_span (state.ctx, page->text);
672 page->freepage (page->u.ptr);
673 fz_free_display_list (state.ctx, page->dlist);
674 free (page);
677 static void freetile (struct tile *tile)
679 unlinktile (tile);
680 #ifndef PIGGYBACK
681 fz_drop_pixmap (state.ctx, tile->pixmap);
682 #else
683 if (state.pig) {
684 fz_drop_pixmap (state.ctx, state.pig);
686 state.pig = tile->pixmap;
687 #endif
688 free (tile);
691 #ifdef __ALTIVEC__
692 #include <altivec.h>
694 static int cacheline32bytes;
695 extern char **environ;
697 static void __attribute__ ((constructor)) clcheck (void)
699 char **envp = environ;
700 unsigned long *auxv;
702 while (*envp++);
704 for (auxv = (unsigned long *) envp; *auxv != 0; auxv += 2) {
705 if (*auxv == 19) {
706 cacheline32bytes = auxv[1] == 32;
707 return;
712 static void OPTIMIZE (3) clearpixmap (fz_pixmap *pixmap)
714 if (cacheline32bytes) {
715 intptr_t a1, a2, diff;
716 size_t sizea, i, size = pixmap->w * pixmap->h * pixmap->n;
717 vector unsigned char v = vec_splat_u8 (-1);
718 vector unsigned char *p;
720 a1 = a2 = (intptr_t) pixmap->samples;
721 a2 = (a1 + 31) & ~31;
722 diff = a2 - a1;
723 sizea = size - diff;
724 p = (void *) a2;
726 while (a1 != a2) *(char *) a1++ = 0xff;
727 for (i = 0; i < (sizea & ~31); i += 32) {
728 __asm volatile ("dcbz %0, %1"::"b"(a2),"r"(i));
729 vec_st (v, i, p);
730 vec_st (v, i + 16, p);
732 while (i < sizea) *((char *) a1 + i++) = 0xff;
734 else fz_clear_pixmap_with_value (state.ctx, pixmap, 0xff);
736 #else
737 #define clearpixmap(p) fz_clear_pixmap_with_value (state.ctx, p, 0xff)
738 #endif
740 static fz_matrix trimctm (pdf_page *page, int pindex)
742 fz_matrix ctm;
743 struct pagedim *pdim = &state.pagedims[pindex];
745 if (!pdim->tctmready) {
746 if (state.trimmargins) {
747 fz_rect realbox;
749 ctm = fz_concat (fz_rotate (-pdim->rotate), fz_scale (1, -1));
750 realbox = fz_transform_rect (ctm, pdim->mediabox);
751 ctm = fz_concat (ctm, fz_translate (-realbox.x0, -realbox.y0));
752 ctm = fz_concat (fz_invert_matrix (page->ctm), ctm);
754 else {
755 ctm = fz_identity;
757 pdim->tctm = ctm;
758 pdim->tctmready = 1;
760 return pdim->tctm;
763 static fz_matrix pagectm (struct page *page)
765 if (page->type == DPDF) {
766 return fz_concat (trimctm (page->u.pdfpage, page->pdimno),
767 state.pagedims[page->pdimno].ctm);
769 else {
770 fz_matrix ctm;
771 struct pagedim *pdim = &state.pagedims[page->pdimno];
773 ctm = state.pagedims[page->pdimno].ctm;
774 ctm = fz_concat (fz_translate (-pdim->mediabox.x0,
775 -pdim->mediabox.y0), ctm);
776 return ctm;
780 static void *loadpage (int pageno, int pindex)
782 fz_device *dev;
783 struct page *page = NULL;
785 page = calloc (sizeof (struct page), 1);
786 if (!page) {
787 err (1, "calloc page %d", pageno);
790 page->dlist = fz_new_display_list (state.ctx);
791 dev = fz_new_list_device (state.ctx, page->dlist);
792 switch (state.type) {
793 case DPDF:
794 page->u.pdfpage = pdf_load_page (state.u.pdf, pageno);
795 pdf_run_page (state.u.pdf, page->u.pdfpage, dev, fz_identity, NULL);
796 page->freepage = freepdfpage;
797 break;
799 case DXPS:
800 page->u.xpspage = xps_load_page (state.u.xps, pageno);
801 xps_run_page (state.u.xps, page->u.xpspage, dev, fz_identity, NULL);
802 page->freepage = freexpspage;
803 break;
805 case DCBZ:
806 page->u.cbzpage = cbz_load_page (state.u.cbz, pageno);
807 cbz_run_page (state.u.cbz, page->u.cbzpage, dev, fz_identity, NULL);
808 page->freepage = freecbzpage;
809 break;
811 fz_free_device (dev);
813 page->pdimno = pindex;
814 page->pageno = pageno;
815 page->gen = state.gen;
816 page->type = state.type;
818 return page;
821 static struct tile *alloctile (int h)
823 int i;
824 int slicecount;
825 size_t tilesize;
826 struct tile *tile;
828 slicecount = (h + state.sliceheight - 1) / state.sliceheight;
829 tilesize = sizeof (*tile) + ((slicecount - 1) * sizeof (struct slice));
830 tile = calloc (tilesize, 1);
831 if (!tile) {
832 err (1, "can not allocate tile (%" FMT_s " bytes)", tilesize);
834 for (i = 0; i < slicecount; ++i) {
835 int sh = MIN (h, state.sliceheight);
836 tile->slices[i].h = sh;
837 tile->slices[i].texindex = -1;
838 h -= sh;
840 tile->slicecount = slicecount;
841 tile->sliceheight = state.sliceheight;
842 return tile;
845 static struct tile *rendertile (struct page *page, int x, int y, int w, int h)
847 fz_bbox bbox;
848 fz_device *dev;
849 struct tile *tile;
850 struct pagedim *pdim;
852 tile = alloctile (h);
853 pdim = &state.pagedims[page->pdimno];
855 bbox = pdim->bounds;
856 bbox.x0 += x;
857 bbox.y0 += y;
858 bbox.x1 = bbox.x0 + w;
859 bbox.y1 = bbox.y0 + h;
861 if (state.pig) {
862 if (state.pig->w == w
863 && state.pig->h == h
864 && state.pig->colorspace == state.colorspace) {
865 tile->pixmap = state.pig;
866 tile->pixmap->x = bbox.x0;
867 tile->pixmap->y = bbox.y0;
869 else {
870 fz_drop_pixmap (state.ctx, state.pig);
872 state.pig = NULL;
874 if (!tile->pixmap) {
875 tile->pixmap =
876 fz_new_pixmap_with_rect (state.ctx, state.colorspace, bbox);
879 tile->w = w;
880 tile->h = h;
881 clearpixmap (tile->pixmap);
882 dev = fz_new_draw_device (state.ctx, tile->pixmap);
883 fz_run_display_list (page->dlist, dev, pagectm (page), bbox, NULL);
884 fz_free_device (dev);
886 return tile;
889 static void initpdims (void)
891 int pageno;
892 double start, end;
894 start = now ();
895 for (pageno = 0; pageno < state.pagecount; ++pageno) {
896 int rotate;
897 fz_obj *pageobj;
898 struct pagedim *p;
899 fz_rect mediabox;
901 switch (state.type) {
902 case DPDF:
903 pageobj = state.u.pdf->page_objs[pageno];
905 if (state.trimmargins) {
906 fz_obj *obj;
907 pdf_page *page;
909 page = pdf_load_page (state.u.pdf, pageno);
910 obj = fz_dict_gets (pageobj, "llpp.TrimBox");
911 if (state.trimanew || !obj) {
912 fz_rect rect;
913 fz_bbox bbox;
914 fz_matrix ctm;
915 fz_device *dev;
917 dev = fz_new_bbox_device (state.ctx, &bbox);
918 dev->hints |= FZ_IGNORE_SHADE;
919 ctm = fz_invert_matrix (page->ctm);
920 pdf_run_page (state.u.pdf, page, dev, fz_identity, NULL);
921 fz_free_device (dev);
923 rect.x0 = bbox.x0 + state.trimfuzz.x0;
924 rect.x1 = bbox.x1 + state.trimfuzz.x1;
925 rect.y0 = bbox.y0 + state.trimfuzz.y0;
926 rect.y1 = bbox.y1 + state.trimfuzz.y1;
927 rect = fz_transform_rect (ctm, rect);
928 rect = fz_intersect_rect (rect, page->mediabox);
930 if (fz_is_empty_rect (rect)) {
931 mediabox = page->mediabox;
933 else {
934 mediabox = rect;
937 obj = fz_new_array (state.ctx, 4);
938 fz_array_push (obj, fz_new_real (state.ctx, mediabox.x0));
939 fz_array_push (obj, fz_new_real (state.ctx, mediabox.y0));
940 fz_array_push (obj, fz_new_real (state.ctx, mediabox.x1));
941 fz_array_push (obj, fz_new_real (state.ctx, mediabox.y1));
942 fz_dict_puts (pageobj, "llpp.TrimBox", obj);
944 else {
945 mediabox.x0 = fz_to_real (fz_array_get (obj, 0));
946 mediabox.y0 = fz_to_real (fz_array_get (obj, 1));
947 mediabox.x1 = fz_to_real (fz_array_get (obj, 2));
948 mediabox.y1 = fz_to_real (fz_array_get (obj, 3));
951 rotate = page->rotate;
952 pdf_free_page (state.u.pdf, page);
954 printd ("progress %f Trimming %d",
955 (double) (pageno + 1) / state.pagecount,
956 pageno + 1);
958 else {
959 fz_rect cropbox;
961 mediabox = pdf_to_rect (state.ctx,
962 fz_dict_gets (pageobj, "MediaBox"));
963 if (fz_is_empty_rect (mediabox)) {
964 fprintf (stderr, "cannot find page size for page %d\n",
965 pageno+1);
966 mediabox.x0 = 0;
967 mediabox.y0 = 0;
968 mediabox.x1 = 612;
969 mediabox.y1 = 792;
972 cropbox = pdf_to_rect (state.ctx,
973 fz_dict_gets (pageobj, "CropBox"));
974 if (!fz_is_empty_rect (cropbox)) {
975 mediabox = fz_intersect_rect (mediabox, cropbox);
977 rotate = fz_to_int (fz_dict_gets (pageobj, "Rotate"));
979 break;
981 case DXPS:
983 xps_page *page;
985 page = xps_load_page (state.u.xps, pageno);
986 mediabox = xps_bound_page (state.u.xps, page);
987 rotate = 0;
988 if (state.trimmargins) {
989 fz_rect rect;
990 fz_bbox bbox;
991 fz_device *dev;
993 dev = fz_new_bbox_device (state.ctx, &bbox);
994 dev->hints |= FZ_IGNORE_SHADE;
995 xps_run_page (state.u.xps, page, dev, fz_identity, NULL);
996 fz_free_device (dev);
998 rect.x0 = bbox.x0 + state.trimfuzz.x0;
999 rect.x1 = bbox.x1 + state.trimfuzz.x1;
1000 rect.y0 = bbox.y0 + state.trimfuzz.y0;
1001 rect.y1 = bbox.y1 + state.trimfuzz.y1;
1002 rect = fz_intersect_rect (rect, mediabox);
1004 if (!fz_is_empty_rect (rect)) {
1005 mediabox = rect;
1008 xps_free_page (state.u.xps, page);
1009 printd ("progress %f loading %d",
1010 (double) (pageno + 1) / state.pagecount,
1011 pageno + 1);
1013 break;
1015 case DCBZ:
1017 rotate = 0;
1018 if (state.trimmargins) {
1019 cbz_page *page;
1021 page = cbz_load_page (state.u.cbz, pageno);
1022 mediabox = cbz_bound_page (state.u.cbz, page);
1023 cbz_free_page (state.u.cbz, page);
1024 printd ("progress %f Trimming %d",
1025 (double) (pageno + 1) / state.pagecount,
1026 pageno + 1);
1028 else {
1029 mediabox.x0 = mediabox.y0 = 0;
1030 mediabox.x1 = 900;
1031 mediabox.y1 = 900;
1034 break;
1036 default:
1037 ARSERT (0 && state.type);
1040 if (state.pagedimcount == 0
1041 || (p = &state.pagedims[state.pagedimcount-1], p->rotate != rotate)
1042 || memcmp (&p->mediabox, &mediabox, sizeof (mediabox))) {
1043 size_t size;
1045 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
1046 state.pagedims = realloc (state.pagedims, size);
1047 if (!state.pagedims) {
1048 err (1, "realloc pagedims to %" FMT_s " (%d elems)",
1049 size, state.pagedimcount + 1);
1052 p = &state.pagedims[state.pagedimcount++];
1053 p->rotate = rotate;
1054 p->mediabox = mediabox;
1055 p->pageno = pageno;
1058 end = now ();
1059 if (state.trimmargins) {
1060 printd ("progress 1 Trimmed %d pages in %f seconds",
1061 state.pagecount, end - start);
1063 else {
1064 printd ("vmsg Processed %d pages in %f seconds",
1065 state.pagecount, end - start);
1067 state.trimanew = 0;
1070 static void layout (void)
1072 int pindex;
1073 fz_rect box;
1074 fz_matrix ctm;
1075 double zoom, w, maxw = 0;
1076 struct pagedim *p = state.pagedims;
1078 if (state.proportional) {
1079 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
1080 box = fz_transform_rect (fz_rotate (p->rotate + state.rotate),
1081 p->mediabox);
1082 w = box.x1 - box.x0;
1083 maxw = MAX (w, maxw);
1087 p = state.pagedims;
1088 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
1089 fz_bbox bbox;
1091 ctm = fz_rotate (state.rotate);
1092 box = fz_transform_rect (fz_rotate (p->rotate + state.rotate),
1093 p->mediabox);
1094 w = box.x1 - box.x0;
1096 if (state.proportional) {
1097 double scale = w / maxw;
1098 zoom = (state.w / w) * scale;
1100 else {
1101 zoom = state.w / w;
1104 p->zoomctm = fz_scale (zoom, zoom);
1105 ctm = fz_concat (p->zoomctm, ctm);
1107 p->pagebox = fz_transform_rect (fz_rotate (p->rotate), p->mediabox);
1108 p->pagebox.x1 -= p->pagebox.x0;
1109 p->pagebox.y1 -= p->pagebox.y0;
1110 p->pagebox.x0 = 0;
1111 p->pagebox.y0 = 0;
1112 bbox = fz_round_rect (fz_transform_rect (ctm, p->pagebox));
1114 p->bounds = bbox;
1115 p->left = state.proportional ? ((maxw - w) * zoom) / 2.0 : 0;
1116 p->ctm = ctm;
1118 ctm = fz_identity;
1119 ctm = fz_concat (ctm, fz_translate (0, -p->mediabox.y1));
1120 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
1121 ctm = fz_concat (ctm, fz_rotate (p->rotate + state.rotate));
1122 p->lctm = ctm;
1124 p->tctmready = 0;
1127 while (p-- != state.pagedims) {
1128 int w = p->bounds.x1 - p->bounds.x0;
1129 int h = p->bounds.y1 - p->bounds.y0;
1131 printd ("pdim %d %d %d %d", p->pageno, w, h, p->left);
1135 static void recurse_outline (fz_outline *outline, int level)
1137 while (outline) {
1138 fz_link_dest *dest;
1139 int i, top = 0;
1140 struct pagedim *pdim = state.pagedims;
1142 dest = &outline->dest;
1143 for (i = 0; i < state.pagedimcount; ++i) {
1144 if (state.pagedims[i].pageno > dest->ld.gotor.page)
1145 break;
1146 pdim = &state.pagedims[i];
1148 if (dest->ld.gotor.flags & fz_link_flag_t_valid) {
1149 fz_point p;
1150 p.x = 0;
1151 p.y = dest->ld.gotor.lt.y;
1152 p = fz_transform_point (pdim->lctm, p);
1153 top = p.y;
1155 if (dest->ld.gotor.page >= 0 && dest->ld.gotor.page < 1<<30) {
1156 int h;
1157 double y0, y1;
1159 y0 = MIN (pdim->bounds.y0, pdim->bounds.y1);
1160 y1 = MAX (pdim->bounds.y0, pdim->bounds.y1);
1161 h = y1 - y0;
1162 printd ("o %d %d %d %d %s",
1163 level, dest->ld.gotor.page, top, h, outline->title);
1165 if (outline->down) {
1166 recurse_outline (outline->down, level + 1);
1168 outline = outline->next;
1172 static void process_outline (void)
1174 fz_outline *outline;
1176 if (!state.needoutline) return;
1178 state.needoutline = 0;
1179 switch (state.type) {
1180 case DPDF:
1181 outline = pdf_load_outline (state.u.pdf);
1182 break;
1183 case DXPS:
1184 outline = xps_load_outline (state.u.xps);
1185 break;
1186 default:
1187 outline = NULL;
1188 break;
1190 if (outline) {
1191 recurse_outline (outline, 0);
1192 fz_free_outline (state.ctx, outline);
1196 static int comparespans (const void *l, const void *r)
1198 fz_text_span const *const*ls = l;
1199 fz_text_span const *const*rs = r;
1200 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
1203 /* wishful thinking function */
1204 static void search (regex_t *re, int pageno, int y, int forward)
1206 int i, j;
1207 int ret;
1208 char *p;
1209 char buf[256];
1210 fz_matrix ctm;
1211 fz_device *tdev;
1212 union { void *ptr; pdf_page *pdfpage; xps_page *xpspage; } u;
1213 fz_text_span *text, *span, **pspan;
1214 struct pagedim *pdim, *pdimprev;
1215 int stop = 0;
1216 int niters = 0;
1217 int nspans;
1218 double start, end;
1220 if (!(state.type == DPDF || state.type == DXPS))
1221 return;
1223 start = now ();
1224 while (pageno >= 0 && pageno < state.pagecount && !stop) {
1225 if (niters++ == 5) {
1226 niters = 0;
1227 if (hasdata ()) {
1228 printd ("progress 1 attention requested aborting search at %d",
1229 pageno);
1230 stop = 1;
1232 else {
1233 printd ("progress %f searching in page %d",
1234 (double) (pageno + 1) / state.pagecount,
1235 pageno);
1238 pdimprev = NULL;
1239 for (i = 0; i < state.pagedimcount; ++i) {
1240 pdim = &state.pagedims[i];
1241 if (pdim->pageno == pageno) {
1242 goto found;
1244 if (pdim->pageno > pageno) {
1245 pdim = pdimprev;
1246 goto found;
1248 pdimprev = pdim;
1250 pdim = pdimprev;
1251 found:
1253 text = fz_new_text_span (state.ctx);
1254 tdev = fz_new_text_device (state.ctx, text);
1256 switch (state.type) {
1257 case DPDF:
1258 u.pdfpage = pdf_load_page (state.u.pdf, pageno);
1259 pdf_run_page (state.u.pdf, u.pdfpage, tdev, fz_identity, NULL);
1260 break;
1262 case DXPS:
1263 u.xpspage = xps_load_page (state.u.xps, pageno);
1264 xps_run_page (state.u.xps, u.xpspage, tdev, fz_identity, NULL);
1265 break;
1267 default:
1268 ARSERT (0 && state.type);
1271 fz_free_device (tdev);
1273 nspans = 0;
1274 for (span = text; span; span = span->next) {
1275 nspans++;
1277 pspan = malloc (sizeof (void *) * nspans);
1278 if (!pspan) {
1279 err (1, "malloc span pointers %" FMT_s, sizeof (void *) * nspans);
1281 for (i = 0, span = text; span; span = span->next, ++i) {
1282 pspan[i] = span;
1284 qsort (pspan, nspans, sizeof (fz_text_span *), comparespans);
1286 j = forward ? 0 : nspans - 1;
1287 while (nspans--) {
1288 regmatch_t rm;
1290 span = pspan[j];
1291 j += forward ? 1 : -1;
1292 p = buf;
1293 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
1294 if (forward) {
1295 if (span->text[i].bbox.y0 < y + 1) {
1296 continue;
1299 else {
1300 if (span->text[i].bbox.y0 > y - 1) {
1301 continue;
1304 if (span->text[i].c < 256) {
1305 *p++ = span->text[i].c;
1307 else {
1308 *p++ = '?';
1311 if (p == buf) {
1312 continue;
1314 *p++ = 0;
1316 ret = regexec (re, buf, 1, &rm, 0);
1317 if (ret) {
1318 if (ret != REG_NOMATCH) {
1319 size_t size;
1320 char errbuf[80];
1321 size = regerror (ret, re, errbuf, sizeof (errbuf));
1322 printd ("msg regexec error `%.*s'",
1323 (int) size, errbuf);
1324 fz_free_text_span (state.ctx, text);
1325 state.freepage (u.ptr);
1326 free (pspan);
1327 return;
1330 else {
1331 fz_bbox *sb, *eb;
1332 fz_point p1, p2, p3, p4;
1334 sb = &span->text[rm.rm_so].bbox;
1335 eb = &span->text[rm.rm_eo - 1].bbox;
1337 p1.x = sb->x0;
1338 p1.y = sb->y0;
1339 p2.x = eb->x1;
1340 p2.y = sb->y0;
1341 p3.x = eb->x1;
1342 p3.y = eb->y1;
1343 p4.x = sb->x0;
1344 p4.y = eb->y1;
1346 switch (state.type) {
1347 case DPDF:
1348 trimctm (u.pdfpage, pdim - state.pagedims);
1349 ctm = fz_concat (pdim->tctm, pdim->zoomctm);
1350 break;
1352 case DXPS:
1353 ctm = pdim->ctm;
1354 break;
1357 p1 = fz_transform_point (ctm, p1);
1358 p2 = fz_transform_point (ctm, p2);
1359 p3 = fz_transform_point (ctm, p3);
1360 p4 = fz_transform_point (ctm, p4);
1362 if (!stop) {
1363 printd ("firstmatch %d %d %f %f %f %f %f %f %f %f",
1364 pageno, 1,
1365 p1.x, p1.y,
1366 p2.x, p2.y,
1367 p3.x, p3.y,
1368 p4.x, p4.y);
1370 printd ("progress 1 found at %d `%.*s' in %f sec",
1371 pageno, (int) (rm.rm_eo - rm.rm_so), &buf[rm.rm_so],
1372 now () - start);
1374 else {
1375 printd ("match %d %d %f %f %f %f %f %f %f %f",
1376 pageno, 2,
1377 p1.x, p1.y,
1378 p2.x, p2.y,
1379 p3.x, p3.y,
1380 p4.x, p4.y);
1382 stop = 1;
1385 if (forward) {
1386 pageno += 1;
1387 y = 0;
1389 else {
1390 pageno -= 1;
1391 y = INT_MAX;
1393 fz_free_text_span (state.ctx, text);
1394 state.freepage (u.ptr);
1395 free (pspan);
1397 end = now ();
1398 if (!stop) {
1399 printd ("progress 1 no matches %f sec", end - start);
1401 printd ("clearrects");
1404 static void set_tex_params (int colorspace)
1406 switch (colorspace) {
1407 case 0:
1408 state.texiform = GL_RGBA8;
1409 state.texform = GL_RGBA;
1410 state.texty = GL_UNSIGNED_BYTE;
1411 state.colorspace = fz_device_rgb;
1412 break;
1413 case 1:
1414 state.texiform = GL_RGBA8;
1415 state.texform = GL_BGRA;
1416 state.texty = fz_is_big_endian ()
1417 ? GL_UNSIGNED_INT_8_8_8_8
1418 : GL_UNSIGNED_INT_8_8_8_8_REV;
1419 state.colorspace = fz_device_bgr;
1420 break;
1421 case 2:
1422 state.texiform = GL_LUMINANCE_ALPHA;
1423 state.texform = GL_LUMINANCE_ALPHA;
1424 state.texty = GL_UNSIGNED_BYTE;
1425 state.colorspace = fz_device_gray;
1426 break;
1427 default:
1428 errx (1, "invalid colorspce %d", colorspace);
1432 static void realloctexts (int texcount)
1434 size_t size;
1436 if (texcount == state.texcount) return;
1438 if (texcount < state.texcount) {
1439 glDeleteTextures (state.texcount - texcount,
1440 state.texids + texcount);
1443 size = texcount * sizeof (*state.texids);
1444 state.texids = realloc (state.texids, size);
1445 if (!state.texids) {
1446 err (1, "realloc texids %" FMT_s, size);
1449 size = texcount * sizeof (*state.texowners);
1450 state.texowners = realloc (state.texowners, size);
1451 if (!state.texowners) {
1452 err (1, "realloc texowners %" FMT_s, size);
1454 if (texcount > state.texcount) {
1455 int i;
1457 glGenTextures (texcount - state.texcount,
1458 state.texids + state.texcount);
1459 for (i = state.texcount; i < texcount; ++i) {
1460 state.texowners[i].w = -1;
1461 state.texowners[i].slice = NULL;
1464 state.texcount = texcount;
1465 state.texindex = 0;
1468 static
1469 #ifdef _WIN32
1470 DWORD _stdcall
1471 #else
1472 void *
1473 #endif
1474 mainloop (void *unused)
1476 char *p = NULL;
1477 int len, ret, oldlen = 0;
1479 for (;;) {
1480 len = readlen ();
1481 if (len == 0) {
1482 errx (1, "readlen returned 0");
1485 if (oldlen < len + 1) {
1486 p = realloc (p, len + 1);
1487 if (!p) {
1488 err (1, "realloc %d failed", len + 1);
1490 oldlen = len + 1;
1492 readdata (p, len);
1493 p[len] = 0;
1495 if (!strncmp ("open", p, 4)) {
1496 size_t filenamelen;
1497 char *password;
1498 char *filename = p + 5;
1500 filenamelen = strlen (filename);
1501 password = filename + filenamelen + 1;
1503 openxref (filename, password);
1504 pdfinfo ();
1505 initpdims ();
1506 printd ("msg Opened %s (press h/F1 to get help)", filename);
1507 state.needoutline = 1;
1509 else if (!strncmp ("cs", p, 2)) {
1510 int i, colorspace;
1512 ret = sscanf (p + 2, " %d", &colorspace);
1513 if (ret != 1) {
1514 errx (1, "malformed cs `%.*s' ret=%d", len, p, ret);
1516 lock ("cs");
1517 set_tex_params (colorspace);
1518 for (i = 0; i < state.texcount; ++i) {
1519 state.texowners[i].w = -1;
1520 state.texowners[i].slice = NULL;
1522 unlock ("cs");
1524 else if (!strncmp ("freepage", p, 8)) {
1525 void *ptr;
1527 ret = sscanf (p + 8, " %" FMT_ptr, FMT_ptr_cast (&ptr));
1528 if (ret != 1) {
1529 errx (1, "malformed freepage `%.*s' ret=%d", len, p, ret);
1531 freepage (ptr);
1533 else if (!strncmp ("freetile", p, 8)) {
1534 void *ptr;
1536 ret = sscanf (p + 8, " %" FMT_ptr, FMT_ptr_cast (&ptr));
1537 if (ret != 1) {
1538 errx (1, "malformed freetile `%.*s' ret=%d", len, p, ret);
1540 freetile (ptr);
1542 else if (!strncmp ("search", p, 6)) {
1543 int icase, pageno, y, ret, len2, forward;
1544 char *pattern;
1545 regex_t re;
1547 ret = sscanf (p + 6, " %d %d %d %d,%n",
1548 &icase, &pageno, &y, &forward, &len2);
1549 if (ret != 4) {
1550 errx (1, "malformed search `%s' ret=%d", p, ret);
1553 pattern = p + 6 + len2;
1554 ret = regcomp (&re, pattern,
1555 REG_EXTENDED | (icase ? REG_ICASE : 0));
1556 if (ret) {
1557 char errbuf[80];
1558 size_t size;
1560 size = regerror (ret, &re, errbuf, sizeof (errbuf));
1561 printd ("msg regcomp failed `%.*s'", (int) size, errbuf);
1563 else {
1564 search (&re, pageno, y, forward);
1565 regfree (&re);
1568 else if (!strncmp ("geometry", p, 8)) {
1569 int w, h;
1571 printd ("clear");
1572 ret = sscanf (p + 8, " %d %d", &w, &h);
1573 if (ret != 2) {
1574 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
1577 lock ("geometry");
1578 state.h = h;
1579 if (w != state.w) {
1580 int i;
1581 state.w = w;
1582 for (i = 0; i < state.texcount; ++i) {
1583 state.texowners[i].slice = NULL;
1586 layout ();
1587 process_outline ();
1588 state.gen++;
1589 unlock ("geometry");
1590 printd ("continue %d", state.pagecount);
1592 else if (!strncmp ("reqlayout", p, 9)) {
1593 int rotate, proportional;
1595 printd ("clear");
1596 ret = sscanf (p + 9, " %d %d", &rotate, &proportional);
1597 if (ret != 2) {
1598 errx (1, "bad reqlayout line `%.*s' ret=%d", len, p, ret);
1600 lock ("reqlayout");
1601 state.rotate = rotate;
1602 state.proportional = proportional;
1603 layout ();
1604 unlock ("reqlayout");
1605 printd ("continue %d", state.pagecount);
1607 else if (!strncmp ("page", p, 4)) {
1608 double a, b;
1609 struct page *page;
1610 int pageno, pindex, ret;
1612 ret = sscanf (p + 4, " %d %d", &pageno, &pindex);
1613 if (ret != 2) {
1614 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
1617 lock ("page");
1618 a = now ();
1619 page = loadpage (pageno, pindex);
1620 b = now ();
1621 unlock ("page");
1623 printd ("page %" FMT_ptr " %f", FMT_ptr_cast2 (page), b - a);
1625 else if (!strncmp ("tile", p, 4)) {
1626 int x, y, w, h, ret;
1627 struct page *page;
1628 struct tile *tile;
1629 double a, b;
1631 ret = sscanf (p + 4, " %" FMT_ptr " %d %d %d %d",
1632 FMT_ptr_cast (&page), &x, &y, &w, &h);
1633 if (ret != 5) {
1634 errx (1, "bad tile line `%.*s' ret=%d", len, p, ret);
1637 lock ("tile");
1638 a = now ();
1639 tile = rendertile (page, x, y, w, h);
1640 b = now ();
1641 unlock ("tile");
1643 printd ("tile %d %d %" FMT_ptr " %u %f",
1644 x, y,
1645 FMT_ptr_cast2 (tile),
1646 tile->w * tile->h * tile->pixmap->n,
1647 b - a);
1649 else if (!strncmp ("settrim", p, 7)) {
1650 int trimmargins;
1651 fz_bbox fuzz;
1653 ret = sscanf (p + 7, " %d %d %d %d %d", &trimmargins,
1654 &fuzz.x0, &fuzz.y0, &fuzz.x1, &fuzz.y1);
1655 if (ret != 5) {
1656 errx (1, "malformed settrim `%.*s' ret=%d", len, p, ret);
1658 printd ("clear");
1659 lock ("settrim");
1660 state.trimmargins = trimmargins;
1661 state.needoutline = 1;
1662 if (memcmp (&fuzz, &state.trimfuzz, sizeof (fuzz))) {
1663 state.trimanew = 1;
1664 state.trimfuzz = fuzz;
1666 state.pagedimcount = 0;
1667 free (state.pagedims);
1668 state.pagedims = NULL;
1669 initpdims ();
1670 layout ();
1671 process_outline ();
1672 unlock ("settrim");
1673 printd ("continue %d", state.pagecount);
1675 else if (!strncmp ("sliceh", p, 6)) {
1676 int h;
1678 ret = sscanf (p + 6, " %d", &h);
1679 if (ret != 1) {
1680 errx (1, "malformed sliceh `%.*s' ret=%d", len, p, ret);
1682 if (h != state.sliceheight) {
1683 int i;
1685 state.sliceheight = h;
1686 for (i = 0; i < state.texcount; ++i) {
1687 state.texowners[i].w = -1;
1688 state.texowners[i].h = -1;
1689 state.texowners[i].slice = NULL;
1693 else if (!strncmp ("interrupt", p, 9)) {
1694 printd ("vmsg interrupted");
1696 else if (!strncmp ("quit", p, 4)) {
1697 return 0;
1699 else {
1700 errx (1, "unknown command %.*s", len, p);
1703 return 0;
1706 CAMLprim value ml_realloctexts (value texcount_v)
1708 CAMLparam1 (texcount_v);
1709 int ok;
1711 if (trylock ("ml_realloctexts")) {
1712 ok = 0;
1713 goto done;
1715 realloctexts (Int_val (texcount_v));
1716 ok = 1;
1717 unlock ("ml_realloctexts");
1719 done:
1720 CAMLreturn (Val_bool (ok));
1723 static void showsel (struct page *page, int ox, int oy)
1725 fz_bbox bbox;
1726 fz_text_span *span;
1727 struct mark first, last;
1729 first = page->fmark;
1730 last = page->lmark;
1732 if (!first.span || !last.span) return;
1734 glEnable (GL_BLEND);
1735 glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA);
1736 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1738 ox -= state.pagedims[page->pdimno].bounds.x0;
1739 oy -= state.pagedims[page->pdimno].bounds.y0;
1740 for (span = first.span; span; span = span->next) {
1741 int i, j, k;
1743 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0;
1745 j = 0;
1746 k = span->len - 1;
1748 if (span == page->fmark.span && span == page->lmark.span) {
1749 j = MIN (first.i, last.i);
1750 k = MAX (first.i, last.i);
1752 else if (span == first.span) {
1753 j = first.i;
1755 else if (span == last.span) {
1756 k = last.i;
1759 for (i = j; i <= k; ++i) {
1760 bbox = fz_union_bbox (bbox, span->text[i].bbox);
1762 lprintf ("%d %d %d %d oy=%d ox=%d\n",
1763 bbox.x0,
1764 bbox.y0,
1765 bbox.x1,
1766 bbox.y1,
1767 oy, ox);
1769 glRecti (bbox.x0 + ox, bbox.y0 + oy, bbox.x1 + ox, bbox.y1 + oy);
1771 if (span == last.span) break;
1773 glDisable (GL_BLEND);
1776 static void highlightlinks (struct page *page, int xoff, int yoff)
1778 fz_matrix ctm;
1779 fz_link *link, *links;
1781 switch (page->type) {
1782 case DPDF:
1783 links = page->u.pdfpage->links;
1784 break;
1786 case DXPS:
1787 links = page->u.xpspage->links;
1788 break;
1790 default:
1791 return;
1794 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1795 glEnable (GL_LINE_STIPPLE);
1796 glLineStipple (0.5, 0xcccc);
1798 ctm = fz_concat (pagectm (page), fz_translate (xoff, yoff));
1800 glBegin (GL_QUADS);
1801 for (link = links; link; link = link->next) {
1802 fz_point p1, p2, p3, p4;
1804 p1.x = link->rect.x0;
1805 p1.y = link->rect.y0;
1807 p2.x = link->rect.x1;
1808 p2.y = link->rect.y0;
1810 p3.x = link->rect.x1;
1811 p3.y = link->rect.y1;
1813 p4.x = link->rect.x0;
1814 p4.y = link->rect.y1;
1816 p1 = fz_transform_point (ctm, p1);
1817 p2 = fz_transform_point (ctm, p2);
1818 p3 = fz_transform_point (ctm, p3);
1819 p4 = fz_transform_point (ctm, p4);
1821 switch (link->dest.kind) {
1822 case FZ_LINK_GOTO: glColor3ub (255, 0, 0); break;
1823 case FZ_LINK_URI: glColor3ub (0, 0, 255); break;
1824 default: glColor3ub (0, 0, 0); break;
1827 glVertex2f (p1.x, p1.y);
1828 glVertex2f (p2.x, p2.y);
1829 glVertex2f (p3.x, p3.y);
1830 glVertex2f (p4.x, p4.y);
1832 glEnd ();
1834 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1835 glDisable (GL_LINE_STIPPLE);
1838 static void uploadslice (struct tile *tile, struct slice *slice)
1840 int offset;
1841 struct slice *slice1;
1843 offset = 0;
1844 for (slice1 = tile->slices; slice != slice1; slice1++) {
1845 offset += slice1->h * tile->w * tile->pixmap->n;
1847 if (slice->texindex != -1 && slice->texindex < state.texcount
1848 && state.texowners[slice->texindex].slice == slice) {
1849 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1851 else {
1852 int subimage = 0;
1853 int texindex = state.texindex++ % state.texcount;
1855 if (state.texowners[texindex].w == tile->w) {
1856 if (state.texowners[texindex].h >= slice->h) {
1857 subimage = 1;
1859 else {
1860 state.texowners[texindex].h = slice->h;
1863 else {
1864 state.texowners[texindex].h = slice->h;
1867 state.texowners[texindex].w = tile->w;
1868 state.texowners[texindex].slice = slice;
1869 slice->texindex = texindex;
1871 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[texindex]);
1872 if (subimage) {
1873 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1877 tile->w,
1878 slice->h,
1879 state.texform,
1880 state.texty,
1881 tile->pixmap->samples+offset
1884 else {
1885 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1887 state.texiform,
1888 tile->w,
1889 slice->h,
1891 state.texform,
1892 state.texty,
1893 tile->pixmap->samples+offset
1899 CAMLprim value ml_drawtile (value args_v, value ptr_v)
1901 CAMLparam2 (args_v, ptr_v);
1902 int dispx = Int_val (Field (args_v, 0));
1903 int dispy = Int_val (Field (args_v, 1));
1904 int dispw = Int_val (Field (args_v, 2));
1905 int disph = Int_val (Field (args_v, 3));
1906 int tilex = Int_val (Field (args_v, 4));
1907 int tiley = Int_val (Field (args_v, 5));
1908 char *s = String_val (ptr_v);
1909 struct tile *tile = parse_pointer ("ml_drawtile", s);
1911 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1913 int slicey, firstslice;
1914 struct slice *slice;
1916 firstslice = tiley / tile->sliceheight;
1917 slice = &tile->slices[firstslice];
1918 slicey = tiley % tile->sliceheight;
1920 while (disph > 0) {
1921 int dh;
1923 dh = slice->h - slicey;
1924 dh = MIN (disph, dh);
1925 uploadslice (tile, slice);
1927 glBegin (GL_QUADS);
1929 glTexCoord2i (tilex, slicey);
1930 glVertex2i (dispx, dispy);
1932 glTexCoord2i (tilex+dispw, slicey);
1933 glVertex2i (dispx+dispw, dispy);
1935 glTexCoord2i (tilex+dispw, slicey+dh);
1936 glVertex2i (dispx+dispw, dispy+dh);
1938 glTexCoord2i (tilex, slicey+dh);
1939 glVertex2i (dispx, dispy+dh);
1941 glEnd ();
1943 dispy += dh;
1944 disph -= dh;
1945 slice++;
1946 ARSERT (!(slice - tile->slices >= tile->slicecount && disph > 0));
1947 slicey = 0;
1950 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1951 CAMLreturn (Val_unit);
1954 CAMLprim value ml_postprocess (value ptr_v, value hlinks_v,
1955 value xoff_v, value yoff_v)
1957 CAMLparam4 (ptr_v, hlinks_v, xoff_v, yoff_v);
1958 int xoff = Int_val (xoff_v);
1959 int yoff = Int_val (yoff_v);
1960 char *s = String_val (ptr_v);
1961 struct page *page = parse_pointer ("ml_postprocess", s);
1963 if (Bool_val (hlinks_v)) highlightlinks (page, xoff, yoff);
1965 if (trylock ("ml_postprocess")) {
1966 goto done;
1968 showsel (page, xoff, yoff);
1969 unlock ("ml_postprocess");
1971 done:
1972 CAMLreturn (Val_unit);
1975 static fz_link *getlink (struct page *page, int x, int y)
1977 fz_point p;
1978 fz_matrix ctm;
1979 fz_link *link, *links;
1981 switch (page->type) {
1982 case DPDF:
1983 links = page->u.pdfpage->links;
1984 break;
1986 case DXPS:
1987 links = page->u.xpspage->links;
1988 break;
1990 default:
1991 return NULL;
1993 p.x = x;
1994 p.y = y;
1996 ctm = fz_concat (trimctm (page->u.pdfpage, page->pdimno),
1997 state.pagedims[page->pdimno].ctm);
1998 ctm = fz_invert_matrix (ctm);
1999 p = fz_transform_point (ctm, p);
2001 for (link = links; link; link = link->next) {
2002 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
2003 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
2004 return link;
2008 return NULL;
2011 static void droptext (struct page *page)
2013 if (page->text) {
2014 fz_free_text_span (state.ctx, page->text);
2015 page->fmark.i = -1;
2016 page->lmark.i = -1;
2017 page->fmark.span = NULL;
2018 page->lmark.span = NULL;
2019 page->text = NULL;
2023 static void ensuretext (struct page *page)
2025 if (state.gen != page->gen) {
2026 droptext (page);
2027 page->gen = state.gen;
2029 if (!page->text) {
2030 fz_device *tdev;
2032 page->text = fz_new_text_span (state.ctx);
2033 tdev = fz_new_text_device (state.ctx, page->text);
2034 fz_run_display_list (page->dlist,
2035 tdev,
2036 pagectm (page),
2037 fz_infinite_bbox, NULL);
2038 fz_free_device (tdev);
2042 CAMLprim value ml_whatsunder (value ptr_v, value x_v, value y_v)
2044 CAMLparam3 (ptr_v, x_v, y_v);
2045 CAMLlocal3 (ret_v, tup_v, str_v);
2046 int pageno;
2047 fz_link *link;
2048 struct page *page;
2049 char *s = String_val (ptr_v);
2050 int x = Int_val (x_v), y = Int_val (y_v);
2051 struct pagedim *pdim;
2053 ret_v = Val_int (0);
2054 if (trylock ("ml_whatsunder")) {
2055 goto done;
2058 page = parse_pointer ("ml_whatsunder", s);
2059 pdim = &state.pagedims[page->pdimno];
2060 x += pdim->bounds.x0;
2061 y += pdim->bounds.y0;
2062 link = getlink (page, x, y);
2063 if (link) {
2064 switch (link->dest.kind) {
2065 case FZ_LINK_GOTO:
2067 fz_point p;
2069 pageno = link->dest.ld.gotor.page;
2070 p.x = 0;
2071 p.y = 0;
2073 if (link->dest.ld.gotor.flags & fz_link_flag_t_valid) {
2074 p.y = link->dest.ld.gotor.lt.y;
2075 p = fz_transform_point (pdim->lctm, p);
2077 tup_v = caml_alloc_tuple (2);
2078 ret_v = caml_alloc_small (1, 1);
2079 Field (tup_v, 0) = Val_int (pageno);
2080 Field (tup_v, 1) = Val_int (p.y);
2081 Field (ret_v, 0) = tup_v;
2083 break;
2085 case FZ_LINK_URI:
2086 str_v = caml_copy_string (link->dest.ld.uri.uri);
2087 ret_v = caml_alloc_small (1, 0);
2088 Field (ret_v, 0) = str_v;
2089 break;
2091 case FZ_LINK_LAUNCH:
2092 str_v = caml_copy_string (link->dest.ld.launch.file_spec);
2093 ret_v = caml_alloc_small (1, 4);
2094 Field (ret_v, 0) = str_v;
2095 break;
2097 case FZ_LINK_NAMED:
2098 str_v = caml_copy_string (link->dest.ld.named.named);
2099 ret_v = caml_alloc_small (1, 5);
2100 Field (ret_v, 0) = str_v;
2101 break;
2103 case FZ_LINK_GOTOR:
2104 str_v = caml_copy_string (link->dest.ld.gotor.file_spec);
2105 pageno = link->dest.ld.gotor.page;
2106 tup_v = caml_alloc_tuple (2);
2107 ret_v = caml_alloc_small (1, 6);
2108 Field (tup_v, 0) = str_v;
2109 Field (tup_v, 1) = Val_int (pageno);
2110 Field (ret_v, 0) = tup_v;
2111 break;
2113 default:
2115 char buf[80];
2117 snprintf (buf, sizeof (buf),
2118 "unhandled link kind %d", link->dest.kind);
2119 str_v = caml_copy_string (buf);
2120 ret_v = caml_alloc_small (1, 3);
2121 Field (ret_v, 0) = str_v;
2123 break;
2126 else {
2127 int i;
2128 fz_text_span *span;
2130 ensuretext (page);
2131 for (span = page->text; span; span = span->next) {
2132 for (i = 0; i < span->len; ++i) {
2133 fz_bbox *b;
2134 b = &span->text[i].bbox;
2135 if ((x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1)) {
2136 const char *n2 =
2137 span->font && span->font->name
2138 ? span->font->name
2139 : "Span has no font name"
2141 FT_FaceRec *face = span->font->ft_face;
2142 if (face && face->family_name) {
2143 char *s;
2144 char *n1 = face->family_name;
2145 size_t l1 = strlen (n1);
2146 size_t l2 = strlen (n2);
2148 if (l1 != l2 || memcmp (n1, n2, l1)) {
2149 s = malloc (l1 + l2 + 2);
2150 if (s) {
2151 memcpy (s, n2, l2);
2152 s[l2] = '=';
2153 memcpy (s + l2 + 1, n1, l1 + 1);
2154 str_v = caml_copy_string (s);
2155 free (s);
2159 if (str_v == 0) {
2160 str_v = caml_copy_string (n2);
2162 ret_v = caml_alloc_small (1, 2);
2163 Field (ret_v, 0) = str_v;
2164 goto unlock;
2169 unlock:
2170 unlock ("ml_whatsunder");
2172 done:
2173 CAMLreturn (ret_v);
2176 CAMLprim value ml_seltext (value ptr_v, value rect_v)
2178 CAMLparam2 (ptr_v, rect_v);
2179 fz_bbox *b;
2180 struct page *page;
2181 fz_text_span *span;
2182 struct mark first, last;
2183 int i, x0, x1, y0, y1;
2184 struct pagedim *pdim;
2185 char *s = String_val (ptr_v);
2187 if (trylock ("ml_seltext")) {
2188 goto done;
2191 page = parse_pointer ("ml_seltext", s);
2192 ensuretext (page);
2194 pdim = &state.pagedims[page->pdimno];
2196 x0 = Int_val (Field (rect_v, 0)) + pdim->bounds.x0;
2197 y0 = Int_val (Field (rect_v, 1)) + pdim->bounds.y0;
2198 x1 = Int_val (Field (rect_v, 2)) + pdim->bounds.x0;
2199 y1 = Int_val (Field (rect_v, 3)) + pdim->bounds.y0;
2201 if (0) {
2202 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
2203 glColor3ub (128, 128, 128);
2204 glRecti (x0, y0, x1, y1);
2205 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
2208 first.span = NULL;
2209 last.span = NULL;
2211 last.i = first.i = 0;
2212 first.span = page->text;
2213 for (span = page->text; span; span = span->next) {
2214 for (i = 0; i < span->len; ++i) {
2215 b = &span->text[i].bbox;
2216 int selected = 0;
2218 if (x0 >= b->x0 && x0 <= b->x1 && y0 >= b->y0 && y0 <= b->y1) {
2219 first.i = i;
2220 first.span = span;
2221 selected = 1;
2223 if (x1 >= b->x0 && x1 <= b->x1 && y1 >= b->y0 && y1 <= b->y1) {
2224 last.i = i;
2225 last.span = span;
2226 selected = 1;
2228 if (0 && selected) {
2229 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
2230 glColor3ub (128, 128, 128);
2231 glRecti (b->x0, b->y0, b->x1, b->y1);
2232 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
2237 if (y1 < y0 || x1 < x0) {
2238 int swap = 0;
2240 if (first.span == last.span) {
2241 swap = 1;
2243 else {
2244 if (y1 < y0) {
2245 for (span = first.span; span && span != last.span;
2246 span = span->next) {
2247 if (span->eol) {
2248 swap = 1;
2249 break;
2255 if (swap) {
2256 i = first.i;
2257 span = first.span;
2258 first.i = last.i;
2259 first.span = last.span;
2260 last.i = i;
2261 last.span = span;
2265 page->fmark = first;
2266 page->lmark = last;
2268 unlock ("ml_seltext");
2270 done:
2271 CAMLreturn (Val_unit);
2274 static int pipespan (FILE *f, fz_text_span *span, int a, int b)
2276 char buf[4];
2277 int i, len, ret;
2279 for (i = a; i <= b; ++i) {
2280 len = runetochar (buf, &span->text[i].c);
2281 ret = fwrite (buf, len, 1, f);
2283 if (ret != 1) {
2284 fprintf (stderr, "failed to write %d bytes ret=%d: %s\n",
2285 len, ret, strerror (errno));
2286 return -1;
2289 return 0;
2292 CAMLprim value ml_copysel (value command_v, value ptr_v)
2294 CAMLparam1 (ptr_v);
2295 FILE *f;
2296 struct page *page;
2297 fz_text_span *span;
2298 char *s = String_val (ptr_v);
2299 char *command = String_val (command_v);
2301 if (trylock ("ml_copysel")) {
2302 goto done;
2305 page = parse_pointer ("ml_sopysel", s);
2307 if (!page->fmark.span || !page->lmark.span) {
2308 fprintf (stderr, "nothing to copy\n");
2309 goto unlock;
2312 f = popen (command, "w");
2313 if (!f) {
2314 fprintf (stderr, "failed to open sel pipe: %s\n",
2315 strerror (errno));
2316 f = stdout;
2319 for (span = page->fmark.span;
2320 span && span != page->lmark.span->next;
2321 span = span->next) {
2322 int a = span == page->fmark.span ? page->fmark.i : 0;
2323 int b = span == page->lmark.span ? page->lmark.i : span->len - 1;
2324 if (pipespan (f, span, a, b)) {
2325 goto close;
2327 if (span->eol) {
2328 if (putc ('\n', f) == EOF) {
2329 fprintf (stderr, "failed break line on sel pipe: %s\n",
2330 strerror (errno));
2331 goto close;
2335 page->lmark.span = NULL;
2336 page->fmark.span = NULL;
2338 close:
2339 if (f != stdout) {
2340 int ret = pclose (f);
2341 if (ret == -1) {
2342 if (errno != ECHILD) {
2343 fprintf (stderr, "failed to close sel pipe: %s\n",
2344 strerror (errno));
2348 unlock:
2349 unlock ("ml_copysel");
2351 done:
2352 CAMLreturn (Val_unit);
2355 CAMLprim value ml_getpdimrect (value pagedimno_v)
2357 CAMLparam1 (pagedimno_v);
2358 CAMLlocal1 (ret_v);
2359 int pagedimno = Int_val (pagedimno_v);
2360 fz_rect box;
2362 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
2363 if (trylock ("ml_getpdimrect")) {
2364 box = fz_empty_rect;
2366 else {
2367 box = state.pagedims[pagedimno].mediabox;
2368 unlock ("ml_getpdimrect");
2371 Store_double_field (ret_v, 0, box.x0);
2372 Store_double_field (ret_v, 1, box.x1);
2373 Store_double_field (ret_v, 2, box.y0);
2374 Store_double_field (ret_v, 3, box.y1);
2376 CAMLreturn (ret_v);
2379 static double getmaxw (void)
2381 int i;
2382 struct pagedim *p;
2383 double maxw = 0.0;
2385 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
2386 double x0, x1, w;
2388 x0 = MIN (p->mediabox.x0, p->mediabox.x1);
2389 x1 = MAX (p->mediabox.x0, p->mediabox.x1);
2391 w = x1 - x0;
2392 maxw = MAX (w, maxw);
2394 return maxw;
2397 CAMLprim value ml_getmaxw (value unit_v)
2399 CAMLparam1 (unit_v);
2400 CAMLlocal1 (ret_v);
2401 double maxw = 0.0;
2403 if (trylock ("ml_getmaxw")) {
2404 goto done;
2406 maxw = getmaxw ();
2407 unlock ("ml_getmaxw");
2408 done:
2409 ret_v = caml_copy_double (maxw);
2410 CAMLreturn (ret_v);
2413 CAMLprim value ml_zoom_for_height (value winw_v, value winh_v, value dw_v)
2415 CAMLparam3 (winw_v, winh_v, dw_v);
2416 CAMLlocal1 (ret_v);
2417 int i;
2418 double zoom = 1.0;
2419 double maxw = 0.0, maxh = 0.0;
2420 struct pagedim *p;
2421 double winw = Int_val (winw_v);
2422 double winh = Int_val (winh_v);
2423 double dw = Int_val (dw_v);
2424 double pw = 1.0, ph = 1.0, num, den;
2426 if (trylock ("ml_zoom_for_height")) {
2427 goto done;
2430 if (state.proportional) {
2431 maxw = getmaxw ();
2434 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
2435 double x0, x1, y0, y1, w, h, scaledh, scale;
2437 x0 = MIN (p->mediabox.x0, p->mediabox.x1);
2438 x1 = MAX (p->mediabox.x0, p->mediabox.x1);
2439 y0 = MIN (p->mediabox.y0, p->mediabox.y1);
2440 y1 = MAX (p->mediabox.y0, p->mediabox.y1);
2442 w = x1 - x0;
2443 h = y1 - y0;
2445 if (state.proportional) {
2446 scale = w / maxw;
2447 scaledh = h * scale;
2449 else {
2450 scale = 1.0;
2451 scaledh = h;
2454 if (scaledh > maxh) {
2455 maxh = scaledh;
2456 ph = scaledh;
2457 pw = w * scale;
2461 num = (winh * pw) + (ph * dw);
2462 den = ph * winw;
2463 zoom = num / den;
2465 unlock ("ml_zoom_for_height");
2466 done:
2467 ret_v = caml_copy_double (zoom);
2468 CAMLreturn (ret_v);
2471 #include "glfont.c"
2473 CAMLprim value ml_draw_string (value pt_v, value x_v, value y_v, value string_v)
2475 CAMLparam4 (pt_v, x_v, y_v, string_v);
2476 CAMLlocal1 (ret_v);
2477 int pt = Int_val(pt_v);
2478 int x = Int_val (x_v);
2479 int y = Int_val (y_v);
2480 double w;
2482 w = draw_string (state.face, pt, x, y, String_val (string_v));
2483 ret_v = caml_copy_double (w);
2484 CAMLreturn (ret_v);
2487 CAMLprim value ml_measure_string (value pt_v, value string_v)
2489 CAMLparam2 (pt_v, string_v);
2490 CAMLlocal1 (ret_v);
2491 int pt = Int_val (pt_v);
2492 double w;
2494 w = measure_string (state.face, pt, String_val (string_v));
2495 ret_v = caml_copy_double (w);
2496 CAMLreturn (ret_v);
2499 CAMLprim value ml_getpagebox (value opaque_v)
2501 CAMLparam1 (opaque_v);
2502 CAMLlocal1 (ret_v);
2503 fz_bbox bbox;
2504 fz_device *dev;
2505 char *s = String_val (opaque_v);
2506 struct page *page = parse_pointer ("ml_getpagebox", s);
2508 ret_v = caml_alloc_tuple (4);
2509 dev = fz_new_bbox_device (state.ctx, &bbox);
2510 dev->hints |= FZ_IGNORE_SHADE;
2512 switch (page->type) {
2513 case DPDF:
2514 pdf_run_page (state.u.pdf, page->u.pdfpage, dev, pagectm (page), NULL);
2515 break;
2517 case DXPS:
2518 xps_run_page (state.u.xps, page->u.xpspage, dev, pagectm (page), NULL);
2519 break;
2521 default:
2522 bbox = fz_infinite_bbox;
2523 break;
2526 fz_free_device (dev);
2527 Field (ret_v, 0) = Val_int (bbox.x0);
2528 Field (ret_v, 1) = Val_int (bbox.y0);
2529 Field (ret_v, 2) = Val_int (bbox.x1);
2530 Field (ret_v, 3) = Val_int (bbox.y1);
2532 CAMLreturn (ret_v);
2535 CAMLprim value ml_setaalevel (value level_v)
2537 CAMLparam1 (level_v);
2539 state.aalevel = Int_val (level_v);
2540 CAMLreturn (Val_unit);
2543 #if !defined _WIN32 && !defined __APPLE__
2544 #undef pixel
2545 #include <X11/X.h>
2546 #include <X11/Xlib.h>
2547 #include <X11/Xutil.h>
2548 #include <GL/glx.h>
2550 static void set_wm_class (void)
2552 Display *dpy;
2553 Window win;
2554 XClassHint hint;
2555 char *display;
2557 display = getenv ("DISPLAY");
2558 dpy = XOpenDisplay (display);
2559 if (!dpy) {
2560 fprintf (stderr, "XOpenDisplay `%s' failed\n",
2561 display ? display : "null");
2562 return;
2564 hint.res_name = "llpp";
2565 hint.res_class = "llpp";
2566 win = glXGetCurrentDrawable ();
2567 if (win == None) {
2568 fprintf (stderr, "glXGetCurrentDrawable returned None\n");
2569 XCloseDisplay (dpy);
2570 return;
2572 XSetClassHint (dpy, win, &hint);
2573 XCloseDisplay (dpy);
2575 #define HAS_WM_CLASS_HACK
2576 #endif
2578 enum { piunknown, pilinux, piwindows, pwindowsgui, piosx,
2579 pisun, pifreebsd, pidragonflybsd, piopenbsd, pinetbsd,
2580 pimingw, pmingwgui, picygwin };
2582 #define NOZOMBIESPLEASE
2584 #ifdef _WIN32
2585 static int isgui (void)
2587 /* http://www.opensc.ws/c-snippets/12714-c-getprocaddressex.html
2588 and MSDN */
2589 char *p = (char *) GetModuleHandle (NULL);
2590 if (p) {
2591 IMAGE_DOS_HEADER *dh = (IMAGE_DOS_HEADER *) p;
2592 IMAGE_NT_HEADERS *nh = (IMAGE_NT_HEADERS *) (p + dh->e_lfanew);
2594 return nh->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI;
2596 return 0;
2598 #endif
2600 CAMLprim value ml_platform (value unit_v)
2602 CAMLparam1 (unit_v);
2603 int platid = piunknown;
2605 #if defined __linux__
2606 platid = pilinux;
2607 #elif defined __CYGWIN__
2608 #undef NOZOMBIESPLEASE
2609 platid = picygwin;
2610 #elif defined __MINGW32__
2611 #undef NOZOMBIESPLEASE
2612 platid = pimingw + isgui ();
2613 #elif defined _WIN32
2614 #undef NOZOMBIESPLEASE
2615 platid = piwindows + isgui ();
2616 #elif defined __DragonFly__
2617 platid = pidragonflybsd;
2618 #elif defined __FreeBSD__
2619 platid = pifreebsd;
2620 #elif defined __OpenBSD__
2621 platid = piopenbsd;
2622 #elif defined __NetBSD__
2623 platid = pinetbsd;
2624 #elif defined __sun__
2625 platid = pisun;
2626 #elif defined __APPLE__
2627 platid = piosx;
2628 #endif
2629 CAMLreturn (Val_int (platid));
2632 #ifdef NOZOMBIESPLEASE
2633 #include <signal.h>
2634 #endif
2636 CAMLprim value ml_init (value pipe_v, value params_v)
2638 CAMLparam2 (pipe_v, params_v);
2639 CAMLlocal2 (trim_v, fuzz_v);
2640 char *fontpath;
2641 int texcount;
2642 int wmclasshack;
2643 int colorspace;
2644 int mustoresize;
2646 #ifdef _WIN32
2647 state.cr = Handle_val (Field (pipe_v, 0));
2648 state.cw = Handle_val (Field (pipe_v, 1));
2649 #else
2650 state.cr = Int_val (Field (pipe_v, 0));
2651 state.cw = Int_val (Field (pipe_v, 1));
2652 #endif
2653 state.rotate = Int_val (Field (params_v, 0));
2654 state.proportional = Bool_val (Field (params_v, 1));
2655 trim_v = Field (params_v, 2);
2656 texcount = Int_val (Field (params_v, 3));
2657 state.sliceheight = Int_val (Field (params_v, 4));
2658 mustoresize = Int_val (Field (params_v, 5));
2659 state.ctx = fz_new_context (NULL, NULL, mustoresize);
2660 colorspace = Int_val (Field (params_v, 6));
2661 wmclasshack = Bool_val (Field (params_v, 7));
2662 fontpath = String_val (Field (params_v, 8));
2664 state.trimmargins = Bool_val (Field (trim_v, 0));
2665 fuzz_v = Field (trim_v, 1);
2666 state.trimfuzz.x0 = Int_val (Field (fuzz_v, 0));
2667 state.trimfuzz.y0 = Int_val (Field (fuzz_v, 1));
2668 state.trimfuzz.x1 = Int_val (Field (fuzz_v, 2));
2669 state.trimfuzz.y1 = Int_val (Field (fuzz_v, 3));
2671 set_tex_params (colorspace);
2672 #ifdef HAS_WM_CLASS_HACK
2673 if (wmclasshack) {
2674 set_wm_class ();
2676 #else
2677 (void) wmclasshack;
2678 #endif
2680 if (*fontpath) {
2681 state.face = load_font (fontpath);
2683 else {
2684 unsigned int len;
2685 void *base = pdf_find_substitute_font (0, 0, 0, 0, &len);
2687 state.face = load_builtin_font (base, len);
2689 if (!state.face) _exit (1);
2691 realloctexts (texcount);
2693 #ifdef NOZOMBIESPLEASE
2695 struct sigaction sa;
2697 sa.sa_handler = SIG_DFL;
2698 if (sigemptyset (&sa.sa_mask)) {
2699 err (1, "sigemptyset");
2701 sa.sa_flags = SA_RESTART | SA_NOCLDSTOP | SA_NOCLDWAIT;
2702 if (sigaction (SIGCHLD, &sa, NULL)) {
2703 err (1, "sigaction");
2706 #endif
2708 #ifdef _WIN32
2709 InitializeCriticalSection (&critsec);
2710 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
2711 if (state.thread == INVALID_HANDLE_VALUE) {
2712 errx (1, "CreateThread failed: %lx", GetLastError ());
2714 #else
2716 int ret = pthread_create (&state.thread, NULL, mainloop, NULL);
2717 if (ret) {
2718 errx (1, "pthread_create: %s", strerror (ret));
2721 #endif
2723 CAMLreturn (Val_unit);