Modern monitors are bigger
[llpp.git] / link.c
blob034f5ec7b98a7745725b19132f4881b274845dec
1 /* lots of code c&p-ed directly from mupdf */
2 #define CAML_NAME_SPACE
3 #define FIXME 0
5 #include <errno.h>
6 #include <stdio.h>
7 #include <ctype.h>
8 #include <string.h>
9 #include <stdlib.h>
10 #include <signal.h>
11 #include <wchar.h>
13 #include <unistd.h>
14 #include <pthread.h>
15 #include <sys/uio.h>
16 #include <sys/time.h>
17 #include <sys/types.h>
18 #include <sys/ioctl.h>
19 #include <sys/utsname.h>
21 #ifdef __CYGWIN__
22 #include <cygwin/socket.h> /* FIONREAD */
23 #else
24 #include <spawn.h>
25 #endif
27 #include <regex.h>
28 #include <stdarg.h>
29 #include <limits.h>
30 #include <inttypes.h>
32 #include <GL/gl.h>
34 #include <caml/fail.h>
35 #include <caml/alloc.h>
36 #include <caml/memory.h>
37 #include <caml/unixsupport.h>
39 #if __GNUC__ < 5 && !defined __clang__
40 /* At least gcc (Gentoo 4.9.3 p1.0, pie-0.6.2) 4.9.3 emits erroneous
41 clobbered diagnostics */
42 #pragma GCC diagnostic ignored "-Wclobbered"
43 #endif
45 #include <mupdf/fitz.h>
46 #include <mupdf/pdf.h>
48 #include <ft2build.h>
49 #include FT_FREETYPE_H
51 #define PIGGYBACK
52 #define CACHE_PAGEREFS
54 #ifndef __USE_GNU
55 extern char **environ;
56 #endif
58 #if defined __GNUC__
59 #define NORETURN_ATTR __attribute__ ((noreturn))
60 #define UNUSED_ATTR __attribute__ ((unused))
61 #if !defined __clang__
62 #define OPTIMIZE_ATTR(n) __attribute__ ((optimize ("O"#n)))
63 #else
64 #define OPTIMIZE_ATTR(n)
65 #endif
66 #define GCC_FMT_ATTR(a, b) __attribute__ ((format (printf, a, b)))
67 #else
68 #define NORETURN_ATTR
69 #define UNUSED_ATTR
70 #define OPTIMIZE_ATTR(n)
71 #define GCC_FMT_ATTR(a, b)
72 #endif
74 #define FMT_s "zu"
76 #define FMT_ptr PRIxPTR
77 #define SCN_ptr SCNxPTR
78 #define FMT_ptr_cast(p) ((uintptr_t) (p))
79 #define SCN_ptr_cast(p) ((uintptr_t *) (p))
81 static void NORETURN_ATTR GCC_FMT_ATTR (2, 3)
82 err (int exitcode, const char *fmt, ...)
84 va_list ap;
85 int savederrno;
87 savederrno = errno;
88 va_start (ap, fmt);
89 vfprintf (stderr, fmt, ap);
90 va_end (ap);
91 fprintf (stderr, ": %s\n", strerror (savederrno));
92 fflush (stderr);
93 _exit (exitcode);
96 static void NORETURN_ATTR GCC_FMT_ATTR (2, 3)
97 errx (int exitcode, const char *fmt, ...)
99 va_list ap;
101 va_start (ap, fmt);
102 vfprintf (stderr, fmt, ap);
103 va_end (ap);
104 fputc ('\n', stderr);
105 fflush (stderr);
106 _exit (exitcode);
109 #ifndef GL_TEXTURE_RECTANGLE_ARB
110 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
111 #endif
113 #ifdef USE_NPOT
114 #define TEXT_TYPE GL_TEXTURE_2D
115 #else
116 #define TEXT_TYPE GL_TEXTURE_RECTANGLE_ARB
117 #endif
119 #ifndef GL_BGRA
120 #define GL_BGRA 0x80E1
121 #endif
123 #ifndef GL_UNSIGNED_INT_8_8_8_8
124 #define GL_UNSIGNED_INT_8_8_8_8 0x8035
125 #endif
127 #ifndef GL_UNSIGNED_INT_8_8_8_8_REV
128 #define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
129 #endif
131 #if 0
132 #define lprintf printf
133 #else
134 #define lprintf(...)
135 #endif
137 #define ARSERT(cond) for (;;) { \
138 if (!(cond)) { \
139 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
141 break; \
144 struct slice {
145 int h;
146 int texindex;
149 struct tile {
150 int w, h;
151 int slicecount;
152 int sliceheight;
153 struct bo *pbo;
154 fz_pixmap *pixmap;
155 struct slice slices[1];
158 struct pagedim {
159 int pageno;
160 int rotate;
161 int left;
162 int tctmready;
163 fz_irect bounds;
164 fz_rect pagebox;
165 fz_rect mediabox;
166 fz_matrix ctm, zoomctm, tctm;
169 struct slink {
170 enum { SLINK, SANNOT } tag;
171 fz_irect bbox;
172 union {
173 fz_link *link;
174 fz_annot *annot;
175 } u;
178 struct annot {
179 fz_irect bbox;
180 fz_annot *annot;
183 struct page {
184 int tgen;
185 int sgen;
186 int agen;
187 int pageno;
188 int pdimno;
189 fz_stext_page *text;
190 fz_stext_sheet *sheet;
191 fz_page *fzpage;
192 fz_display_list *dlist;
193 int slinkcount;
194 struct slink *slinks;
195 int annotcount;
196 struct annot *annots;
197 struct mark {
198 int i;
199 fz_stext_span *span;
200 } fmark, lmark;
203 struct {
204 int sliceheight;
205 struct pagedim *pagedims;
206 int pagecount;
207 int pagedimcount;
208 fz_document *doc;
209 fz_context *ctx;
210 int w, h;
212 int texindex;
213 int texcount;
214 GLuint *texids;
216 GLenum texiform;
217 GLenum texform;
218 GLenum texty;
220 fz_colorspace *colorspace;
222 struct {
223 int w, h;
224 struct slice *slice;
225 } *texowners;
227 int rotate;
228 enum { FitWidth, FitProportional, FitPage } fitmodel;
229 int trimmargins;
230 int needoutline;
231 int gen;
232 int aalevel;
234 int trimanew;
235 fz_irect trimfuzz;
236 fz_pixmap *pig;
238 pthread_t thread;
239 int csock;
240 FT_Face face;
242 char *trimcachepath;
243 int cxack;
244 int dirty;
246 GLuint stid;
248 int bo_usable;
249 GLuint boid;
251 void (*glBindBufferARB) (GLenum, GLuint);
252 GLboolean (*glUnmapBufferARB) (GLenum);
253 void *(*glMapBufferARB) (GLenum, GLenum);
254 void (*glBufferDataARB) (GLenum, GLsizei, void *, GLenum);
255 void (*glGenBuffersARB) (GLsizei, GLuint *);
256 void (*glDeleteBuffersARB) (GLsizei, GLuint *);
258 GLfloat texcoords[8];
259 GLfloat vertices[16];
261 #ifdef CACHE_PAGEREFS
262 struct {
263 int idx;
264 int count;
265 pdf_obj **objs;
266 pdf_document *pdf;
267 } pdflut;
268 #endif
269 } state;
271 struct bo {
272 GLuint id;
273 void *ptr;
274 size_t size;
277 static void UNUSED_ATTR debug_rect (const char *cap, fz_rect r)
279 printf ("%s(rect) %.2f,%.2f,%.2f,%.2f\n", cap, r.x0, r.y0, r.x1, r.y1);
282 static void UNUSED_ATTR debug_bbox (const char *cap, fz_irect r)
284 printf ("%s(bbox) %d,%d,%d,%d\n", cap, r.x0, r.y0, r.x1, r.y1);
287 static void UNUSED_ATTR debug_matrix (const char *cap, fz_matrix m)
289 printf ("%s(matrix) %.2f,%.2f,%.2f,%.2f %.2f %.2f\n", cap,
290 m.a, m.b, m.c, m.d, m.e, m.f);
293 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
295 static void lock (const char *cap)
297 int ret = pthread_mutex_lock (&mutex);
298 if (ret) {
299 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
303 static void unlock (const char *cap)
305 int ret = pthread_mutex_unlock (&mutex);
306 if (ret) {
307 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
311 static int trylock (const char *cap)
313 int ret = pthread_mutex_trylock (&mutex);
314 if (ret && ret != EBUSY) {
315 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
317 return ret == EBUSY;
320 static void *parse_pointer (const char *cap, const char *s)
322 int ret;
323 void *ptr;
325 ret = sscanf (s, "%" SCN_ptr, SCN_ptr_cast (&ptr));
326 if (ret != 1) {
327 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
329 return ptr;
332 static double now (void)
334 struct timeval tv;
336 if (gettimeofday (&tv, NULL)) {
337 err (1, "gettimeofday");
339 return tv.tv_sec + tv.tv_usec*1e-6;
342 static int hasdata (void)
344 int ret, avail;
345 ret = ioctl (state.csock, FIONREAD, &avail);
346 if (ret) err (1, "hasdata: FIONREAD error ret=%d", ret);
347 return avail > 0;
350 CAMLprim value ml_hasdata (value fd_v)
352 CAMLparam1 (fd_v);
353 int ret, avail;
355 ret = ioctl (Int_val (fd_v), FIONREAD, &avail);
356 if (ret) uerror ("ioctl (FIONREAD)", Nothing);
357 CAMLreturn (Val_bool (avail > 0));
360 static void readdata (int fd, void *p, int size)
362 ssize_t n;
364 again:
365 n = read (fd, p, size);
366 if (n - size) {
367 if (n < 0 && errno == EINTR) goto again;
368 if (!n) errx (1, "EOF while reading");
369 errx (1, "read (fd %d, req %d, ret %zd)", fd, size, n);
373 static void writedata (int fd, char *p, int size)
375 ssize_t n;
376 uint32_t size4 = size;
377 struct iovec iov[2] = {
378 { .iov_base = &size4, .iov_len = 4 },
379 { .iov_base = p, .iov_len = size }
382 again:
383 n = writev (fd, iov, 2);
384 if (n < 0 && errno == EINTR) goto again;
385 if (n - size - 4) {
386 if (!n) errx (1, "EOF while writing data");
387 err (1, "writev (fd %d, req %d, ret %zd)", fd, size + 4, n);
391 static int readlen (int fd)
393 /* Type punned unions here. Why? Less code (Adjusted by more comments).
394 https://en.wikipedia.org/wiki/Type_punning */
395 union { uint32_t len; char raw[4]; } buf;
396 readdata (fd, buf.raw, 4);
397 return buf.len;
400 CAMLprim void ml_wcmd (value fd_v, value bytes_v, value len_v)
402 CAMLparam3 (fd_v, bytes_v, len_v);
403 writedata (Int_val (fd_v), &Byte (bytes_v, 0), Int_val (len_v));
404 CAMLreturn0;
407 CAMLprim value ml_rcmd (value fd_v)
409 CAMLparam1 (fd_v);
410 CAMLlocal1 (strdata_v);
411 int fd = Int_val (fd_v);
412 int len = readlen (fd);
413 strdata_v = caml_alloc_string (len);
414 readdata (fd, String_val (strdata_v), len);
415 CAMLreturn (strdata_v);
418 static void GCC_FMT_ATTR (1, 2) printd (const char *fmt, ...)
420 int size = 64, len;
421 va_list ap;
422 char fbuf[size];
423 char *buf = fbuf;
425 for (;;) {
426 va_start (ap, fmt);
427 len = vsnprintf (buf, size, fmt, ap);
428 va_end (ap);
430 if (len > -1) {
431 if (len < size - 4) {
432 writedata (state.csock, buf, len);
433 break;
435 else size = len + 5;
437 else {
438 err (1, "vsnprintf for `%s' failed", fmt);
440 buf = realloc (buf == fbuf ? NULL : buf, size);
441 if (!buf) err (1, "realloc for temp buf (%d bytes) failed", size);
443 if (buf != fbuf) free (buf);
446 static void closedoc (void)
448 #ifdef CACHE_PAGEREFS
449 if (state.pdflut.objs) {
450 for (int i = 0; i < state.pdflut.count; ++i) {
451 pdf_drop_obj (state.ctx, state.pdflut.objs[i]);
453 free (state.pdflut.objs);
454 state.pdflut.objs = NULL;
455 state.pdflut.idx = 0;
457 #endif
458 if (state.doc) {
459 fz_drop_document (state.ctx, state.doc);
460 state.doc = NULL;
464 static int openxref (char *filename, char *password)
466 for (int i = 0; i < state.texcount; ++i) {
467 state.texowners[i].w = -1;
468 state.texowners[i].slice = NULL;
471 closedoc ();
473 state.dirty = 0;
474 if (state.pagedims) {
475 free (state.pagedims);
476 state.pagedims = NULL;
478 state.pagedimcount = 0;
480 fz_set_aa_level (state.ctx, state.aalevel);
481 state.doc = fz_open_document (state.ctx, filename);
482 if (fz_needs_password (state.ctx, state.doc)) {
483 if (password && !*password) {
484 printd ("pass");
485 return 0;
487 else {
488 int ok = fz_authenticate_password (state.ctx, state.doc, password);
489 if (!ok) {
490 printd ("pass fail");
491 return 0;
495 state.pagecount = fz_count_pages (state.ctx, state.doc);
496 return 1;
499 static void pdfinfo (void)
501 struct { char *tag; char *name; } metatbl[] = {
502 { FZ_META_INFO_TITLE, "Title" },
503 { FZ_META_INFO_AUTHOR, "Author" },
504 { FZ_META_FORMAT, "Format" },
505 { FZ_META_ENCRYPTION, "Encryption" },
506 { "info:Creator", "Creator" },
507 { "info:Producer", "Producer" },
508 { "info:CreationDate", "Creation date" },
510 int len = 0;
511 char *buf = NULL;
513 for (size_t i = 0; i < sizeof (metatbl) / sizeof (metatbl[1]); ++i) {
514 int need;
515 again:
516 need = fz_lookup_metadata (state.ctx, state.doc,
517 metatbl[i].tag, buf, len);
518 if (need > 0) {
519 if (need <= len) {
520 printd ("info %s\t%s", metatbl[i].name, buf);
522 else {
523 buf = realloc (buf, need + 1);
524 if (!buf) err (1, "pdfinfo realloc %d", need + 1);
525 len = need + 1;
526 goto again;
530 free (buf);
532 printd ("infoend");
535 static void unlinktile (struct tile *tile)
537 for (int i = 0; i < tile->slicecount; ++i) {
538 struct slice *s = &tile->slices[i];
540 if (s->texindex != -1) {
541 if (state.texowners[s->texindex].slice == s) {
542 state.texowners[s->texindex].slice = NULL;
548 static void freepage (struct page *page)
550 if (!page) return;
551 if (page->text) {
552 fz_drop_stext_page (state.ctx, page->text);
554 if (page->sheet) {
555 fz_drop_stext_sheet (state.ctx, page->sheet);
557 if (page->slinks) {
558 free (page->slinks);
560 fz_drop_display_list (state.ctx, page->dlist);
561 fz_drop_page (state.ctx, page->fzpage);
562 free (page);
565 static void freetile (struct tile *tile)
567 unlinktile (tile);
568 if (!tile->pbo) {
569 #ifndef PIGGYBACK
570 fz_drop_pixmap (state.ctx, tile->pixmap);
571 #else
572 if (state.pig) {
573 fz_drop_pixmap (state.ctx, state.pig);
575 state.pig = tile->pixmap;
576 #endif
578 else {
579 free (tile->pbo);
580 fz_drop_pixmap (state.ctx, tile->pixmap);
582 free (tile);
585 #ifdef __ALTIVEC__
586 #include <stdint.h>
587 #include <altivec.h>
589 static int cacheline32bytes;
591 static void __attribute__ ((constructor)) clcheck (void)
593 char **envp = environ;
594 unsigned long *auxv;
596 while (*envp++);
598 for (auxv = (unsigned long *) envp; *auxv != 0; auxv += 2) {
599 if (*auxv == 19) {
600 cacheline32bytes = auxv[1] == 32;
601 return;
606 static void OPTIMIZE_ATTR (3) clearpixmap (fz_pixmap *pixmap)
608 size_t size = pixmap->w * pixmap->h * pixmap->n;
609 if (cacheline32bytes && size > 32) {
610 intptr_t a1, a2, diff;
611 size_t sizea, i;
612 vector unsigned char v = vec_splat_u8 (-1);
613 vector unsigned char *p;
615 a1 = a2 = (intptr_t) pixmap->samples;
616 a2 = (a1 + 31) & ~31;
617 diff = a2 - a1;
618 sizea = size - diff;
619 p = (void *) a2;
621 while (a1 != a2) *(char *) a1++ = 0xff;
622 for (i = 0; i < (sizea & ~31); i += 32) {
623 __asm volatile ("dcbz %0, %1"::"b"(a2),"r"(i));
624 vec_st (v, i, p);
625 vec_st (v, i + 16, p);
627 while (i < sizea) *((char *) a1 + i++) = 0xff;
629 else fz_clear_pixmap_with_value (state.ctx, pixmap, 0xff);
631 #else
632 #define clearpixmap(p) fz_clear_pixmap_with_value (state.ctx, p, 0xff)
633 #endif
635 static void trimctm (pdf_page *page, int pindex)
637 fz_matrix ctm;
638 struct pagedim *pdim = &state.pagedims[pindex];
640 if (!page) return;
641 if (!pdim->tctmready) {
642 fz_rect realbox, mediabox;
643 fz_matrix rm, sm, tm, im, ctm1, page_ctm;
645 fz_rotate (&rm, -pdim->rotate);
646 fz_scale (&sm, 1, -1);
647 fz_concat (&ctm, &rm, &sm);
648 realbox = pdim->mediabox;
649 fz_transform_rect (&realbox, &ctm);
650 fz_translate (&tm, -realbox.x0, -realbox.y0);
651 fz_concat (&ctm1, &ctm, &tm);
652 pdf_page_transform (state.ctx, page, &mediabox, &page_ctm);
653 fz_invert_matrix (&im, &page_ctm);
654 fz_concat (&ctm, &im, &ctm1);
655 pdim->tctm = ctm;
656 pdim->tctmready = 1;
660 static fz_matrix pagectm1 (fz_page *fzpage, struct pagedim *pdim)
662 fz_matrix ctm, tm;
663 int pdimno = pdim - state.pagedims;
665 if (pdf_specifics (state.ctx, state.doc)) {
666 trimctm (pdf_page_from_fz_page (state.ctx, fzpage), pdimno);
667 fz_concat (&ctm, &pdim->tctm, &pdim->ctm);
669 else {
670 fz_translate (&tm, -pdim->mediabox.x0, -pdim->mediabox.y0);
671 fz_concat (&ctm, &tm, &pdim->ctm);
673 return ctm;
676 static fz_matrix pagectm (struct page *page)
678 return pagectm1 (page->fzpage, &state.pagedims[page->pdimno]);
681 static void *loadpage (int pageno, int pindex)
683 fz_device *dev;
684 struct page *page;
686 page = calloc (sizeof (struct page), 1);
687 if (!page) {
688 err (1, "calloc page %d", pageno);
691 page->dlist = fz_new_display_list (state.ctx, NULL);
692 dev = fz_new_list_device (state.ctx, page->dlist);
693 fz_try (state.ctx) {
694 page->fzpage = fz_load_page (state.ctx, state.doc, pageno);
695 fz_run_page (state.ctx, page->fzpage, dev,
696 &fz_identity, NULL);
698 fz_catch (state.ctx) {
699 page->fzpage = NULL;
701 fz_close_device (state.ctx, dev);
702 fz_drop_device (state.ctx, dev);
704 page->pdimno = pindex;
705 page->pageno = pageno;
706 page->sgen = state.gen;
707 page->agen = state.gen;
708 page->tgen = state.gen;
709 return page;
712 static struct tile *alloctile (int h)
714 int slicecount;
715 size_t tilesize;
716 struct tile *tile;
718 slicecount = (h + state.sliceheight - 1) / state.sliceheight;
719 tilesize = sizeof (*tile) + ((slicecount - 1) * sizeof (struct slice));
720 tile = calloc (tilesize, 1);
721 if (!tile) {
722 err (1, "cannot allocate tile (%" FMT_s " bytes)", tilesize);
724 for (int i = 0; i < slicecount; ++i) {
725 int sh = fz_mini (h, state.sliceheight);
726 tile->slices[i].h = sh;
727 tile->slices[i].texindex = -1;
728 h -= sh;
730 tile->slicecount = slicecount;
731 tile->sliceheight = state.sliceheight;
732 return tile;
735 static struct tile *rendertile (struct page *page, int x, int y, int w, int h,
736 struct bo *pbo)
738 fz_rect rect;
739 fz_irect bbox;
740 fz_matrix ctm;
741 fz_device *dev;
742 struct tile *tile;
743 struct pagedim *pdim;
745 tile = alloctile (h);
746 pdim = &state.pagedims[page->pdimno];
748 bbox = pdim->bounds;
749 bbox.x0 += x;
750 bbox.y0 += y;
751 bbox.x1 = bbox.x0 + w;
752 bbox.y1 = bbox.y0 + h;
754 if (state.pig) {
755 if (state.pig->w == w
756 && state.pig->h == h
757 && state.pig->colorspace == state.colorspace) {
758 tile->pixmap = state.pig;
759 tile->pixmap->x = bbox.x0;
760 tile->pixmap->y = bbox.y0;
762 else {
763 fz_drop_pixmap (state.ctx, state.pig);
765 state.pig = NULL;
767 if (!tile->pixmap) {
768 if (pbo) {
769 tile->pixmap =
770 fz_new_pixmap_with_bbox_and_data (state.ctx, state.colorspace,
771 &bbox, 1, pbo->ptr);
772 tile->pbo = pbo;
774 else {
775 tile->pixmap =
776 fz_new_pixmap_with_bbox (state.ctx, state.colorspace, &bbox, 1);
780 tile->w = w;
781 tile->h = h;
782 clearpixmap (tile->pixmap);
784 dev = fz_new_draw_device (state.ctx, NULL, tile->pixmap);
785 ctm = pagectm (page);
786 fz_rect_from_irect (&rect, &bbox);
787 fz_run_display_list (state.ctx, page->dlist, dev, &ctm, &rect, NULL);
788 fz_close_device (state.ctx, dev);
789 fz_drop_device (state.ctx, dev);
791 return tile;
794 #ifdef CACHE_PAGEREFS
795 /* modified mupdf/source/pdf/pdf-page.c:pdf_lookup_page_loc_imp
796 thanks to Robin Watts */
797 static void
798 pdf_collect_pages(pdf_document *doc, pdf_obj *node)
800 fz_context *ctx = state.ctx; /* doc->ctx; */
801 pdf_obj *kids;
802 int len;
804 if (state.pdflut.idx == state.pagecount) return;
806 kids = pdf_dict_gets (ctx, node, "Kids");
807 len = pdf_array_len (ctx, kids);
809 if (len == 0)
810 fz_throw (ctx, FZ_ERROR_GENERIC, "malformed pages tree");
812 if (pdf_mark_obj (ctx, node))
813 fz_throw (ctx, FZ_ERROR_GENERIC, "cycle in page tree");
814 for (int i = 0; i < len; i++) {
815 pdf_obj *kid = pdf_array_get (ctx, kids, i);
816 char *type = pdf_to_name (ctx, pdf_dict_gets (ctx, kid, "Type"));
817 if (*type
818 ? !strcmp (type, "Pages")
819 : pdf_dict_gets (ctx, kid, "Kids")
820 && !pdf_dict_gets (ctx, kid, "MediaBox")) {
821 pdf_collect_pages (doc, kid);
823 else {
824 if (*type
825 ? strcmp (type, "Page") != 0
826 : !pdf_dict_gets (ctx, kid, "MediaBox"))
827 fz_warn (ctx, "non-page object in page tree (%s)", type);
828 state.pdflut.objs[state.pdflut.idx++] = pdf_keep_obj (ctx, kid);
831 pdf_unmark_obj (ctx, node);
834 static void
835 pdf_load_page_objs (pdf_document *doc)
837 pdf_obj *root = pdf_dict_gets (state.ctx,
838 pdf_trailer (state.ctx, doc), "Root");
839 pdf_obj *node = pdf_dict_gets (state.ctx, root, "Pages");
841 if (!node)
842 fz_throw (state.ctx, FZ_ERROR_GENERIC, "cannot find page tree");
844 state.pdflut.idx = 0;
845 pdf_collect_pages (doc, node);
847 #endif
849 static void initpdims (int wthack)
851 double start, end;
852 FILE *trimf = NULL;
853 fz_rect rootmediabox;
854 int pageno, trim, show;
855 int trimw = 0, cxcount;
856 fz_context *ctx = state.ctx;
857 pdf_document *pdf = pdf_specifics (ctx, state.doc);
859 fz_var (trimw);
860 fz_var (trimf);
861 fz_var (cxcount);
862 start = now ();
864 if (state.trimmargins && state.trimcachepath) {
865 trimf = fopen (state.trimcachepath, "rb");
866 if (!trimf) {
867 trimf = fopen (state.trimcachepath, "wb");
868 trimw = 1;
872 if (state.trimmargins || pdf || !state.cxack)
873 cxcount = state.pagecount;
874 else
875 cxcount = fz_mini (state.pagecount, 1);
877 if (pdf) {
878 pdf_obj *obj;
879 obj = pdf_dict_getp (ctx, pdf_trailer (ctx, pdf),
880 "Root/Pages/MediaBox");
881 pdf_to_rect (ctx, obj, &rootmediabox);
884 #ifdef CACHE_PAGEREFS
885 if (pdf && (!state.pdflut.objs || state.pdflut.pdf != pdf)) {
886 state.pdflut.objs = calloc (sizeof (*state.pdflut.objs), cxcount);
887 if (!state.pdflut.objs) {
888 err (1, "malloc pageobjs %zu %d %zu failed",
889 sizeof (*state.pdflut.objs), cxcount,
890 sizeof (*state.pdflut.objs) * cxcount);
892 state.pdflut.count = cxcount;
893 pdf_load_page_objs (pdf);
894 state.pdflut.pdf = pdf;
896 #endif
898 for (pageno = 0; pageno < cxcount; ++pageno) {
899 int rotate = 0;
900 struct pagedim *p;
901 fz_rect mediabox;
903 fz_var (rotate);
904 if (pdf) {
905 pdf_obj *pageref, *pageobj;
907 #ifdef CACHE_PAGEREFS
908 pageref = state.pdflut.objs[pageno];
909 #else
910 pageref = pdf_lookup_page_obj (ctx, pdf, pageno);
911 #endif
912 pageobj = pdf_resolve_indirect (ctx, pageref);
913 rotate = pdf_to_int (ctx, pdf_dict_gets (ctx, pageobj, "Rotate"));
915 if (state.trimmargins) {
916 pdf_obj *obj;
917 pdf_page *page;
919 fz_try (ctx) {
920 page = pdf_load_page (ctx, pdf, pageno);
921 obj = pdf_dict_gets (ctx, pageobj, "llpp.TrimBox");
922 trim = state.trimanew || !obj;
923 if (trim) {
924 fz_rect rect;
925 fz_device *dev;
926 fz_matrix ctm, page_ctm;
928 dev = fz_new_bbox_device (ctx, &rect);
929 dev->hints |= FZ_IGNORE_SHADE;
930 pdf_page_transform (ctx, page, &mediabox, &page_ctm);
931 fz_invert_matrix (&ctm, &page_ctm);
932 pdf_run_page (ctx, page, dev, &fz_identity, NULL);
933 fz_close_device (ctx, dev);
934 fz_drop_device (ctx, dev);
936 rect.x0 += state.trimfuzz.x0;
937 rect.x1 += state.trimfuzz.x1;
938 rect.y0 += state.trimfuzz.y0;
939 rect.y1 += state.trimfuzz.y1;
940 fz_transform_rect (&rect, &ctm);
941 fz_intersect_rect (&rect, &mediabox);
943 if (!fz_is_empty_rect (&rect)) {
944 mediabox = rect;
947 obj = pdf_new_array (ctx, pdf, 4);
948 pdf_array_push (ctx, obj, pdf_new_real (ctx, pdf,
949 mediabox.x0));
950 pdf_array_push (ctx, obj, pdf_new_real (ctx, pdf,
951 mediabox.y0));
952 pdf_array_push (ctx, obj, pdf_new_real (ctx, pdf,
953 mediabox.x1));
954 pdf_array_push (ctx, obj, pdf_new_real (ctx, pdf,
955 mediabox.y1));
956 pdf_dict_puts (ctx, pageobj, "llpp.TrimBox", obj);
958 else {
959 mediabox.x0 = pdf_to_real (ctx,
960 pdf_array_get (ctx, obj, 0));
961 mediabox.y0 = pdf_to_real (ctx,
962 pdf_array_get (ctx, obj, 1));
963 mediabox.x1 = pdf_to_real (ctx,
964 pdf_array_get (ctx, obj, 2));
965 mediabox.y1 = pdf_to_real (ctx,
966 pdf_array_get (ctx, obj, 3));
969 fz_drop_page (ctx, &page->super);
970 show = trim ? pageno % 5 == 0 : pageno % 20 == 0;
971 if (show) {
972 printd ("progress %f Trimming %d",
973 (double) (pageno + 1) / state.pagecount,
974 pageno + 1);
977 fz_catch (ctx) {
978 fprintf (stderr, "failed to load page %d\n", pageno+1);
981 else {
982 int empty = 0;
983 fz_rect cropbox;
985 pdf_to_rect (ctx,
986 pdf_dict_gets (ctx, pageobj, "MediaBox"),
987 &mediabox);
988 if (fz_is_empty_rect (&mediabox)) {
989 mediabox.x0 = 0;
990 mediabox.y0 = 0;
991 mediabox.x1 = 612;
992 mediabox.y1 = 792;
993 empty = 1;
996 pdf_to_rect (ctx,
997 pdf_dict_gets (ctx, pageobj, "CropBox"),
998 &cropbox);
999 if (!fz_is_empty_rect (&cropbox)) {
1000 if (empty) {
1001 mediabox = cropbox;
1003 else {
1004 fz_intersect_rect (&mediabox, &cropbox);
1007 else {
1008 if (empty) {
1009 if (fz_is_empty_rect (&rootmediabox)) {
1010 fprintf (stderr,
1011 "cannot find page size for page %d\n",
1012 pageno+1);
1014 else {
1015 mediabox = rootmediabox;
1021 else {
1022 if (state.trimmargins && trimw) {
1023 fz_page *page;
1025 fz_try (ctx) {
1026 page = fz_load_page (ctx, state.doc, pageno);
1027 fz_bound_page (ctx, page, &mediabox);
1028 if (state.trimmargins) {
1029 fz_rect rect;
1030 fz_device *dev;
1032 dev = fz_new_bbox_device (ctx, &rect);
1033 dev->hints |= FZ_IGNORE_SHADE;
1034 fz_run_page (ctx, page, dev, &fz_identity, NULL);
1035 fz_close_device (ctx, dev);
1036 fz_drop_device (ctx, dev);
1038 rect.x0 += state.trimfuzz.x0;
1039 rect.x1 += state.trimfuzz.x1;
1040 rect.y0 += state.trimfuzz.y0;
1041 rect.y1 += state.trimfuzz.y1;
1042 fz_intersect_rect (&rect, &mediabox);
1044 if (!fz_is_empty_rect (&rect)) {
1045 mediabox = rect;
1048 fz_drop_page (ctx, page);
1049 if (!state.cxack) {
1050 printd ("progress %f loading %d",
1051 (double) (pageno + 1) / state.pagecount,
1052 pageno + 1);
1055 fz_catch (ctx) {
1057 if (trimf) {
1058 int n = fwrite (&mediabox, sizeof (mediabox), 1, trimf);
1059 if (n - 1) {
1060 err (1, "fwrite trim mediabox");
1064 else {
1065 if (trimf) {
1066 int n = fread (&mediabox, sizeof (mediabox), 1, trimf);
1067 if (n - 1) {
1068 err (1, "fread trim mediabox %d", pageno);
1071 else {
1072 fz_page *page;
1073 fz_try (ctx) {
1074 page = fz_load_page (ctx, state.doc, pageno);
1075 fz_bound_page (ctx, page, &mediabox);
1076 fz_drop_page (ctx, page);
1078 show = !state.trimmargins && pageno % 20 == 0;
1079 if (show) {
1080 printd ("progress %f Gathering dimensions %d",
1081 (double) (pageno) / state.pagecount,
1082 pageno);
1085 fz_catch (ctx) {
1086 fprintf (stderr, "failed to load page %d\n", pageno);
1092 if (state.pagedimcount == 0
1093 || (p = &state.pagedims[state.pagedimcount-1], p->rotate != rotate)
1094 || memcmp (&p->mediabox, &mediabox, sizeof (mediabox))) {
1095 size_t size;
1097 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
1098 state.pagedims = realloc (state.pagedims, size);
1099 if (!state.pagedims) {
1100 err (1, "realloc pagedims to %" FMT_s " (%d elems)",
1101 size, state.pagedimcount + 1);
1104 p = &state.pagedims[state.pagedimcount++];
1105 p->rotate = rotate;
1106 p->mediabox = mediabox;
1107 p->pageno = pageno;
1110 end = now ();
1111 if (!wthack) {
1112 printd ("progress 1 %s %d pages in %f seconds",
1113 state.trimmargins ? "Trimmed" : "Processed",
1114 state.pagecount, end - start);
1116 state.trimanew = 0;
1117 if (trimf) {
1118 if (fclose (trimf)) {
1119 err (1, "fclose");
1124 static void layout (void)
1126 int pindex;
1127 fz_rect box;
1128 fz_matrix ctm, rm;
1129 struct pagedim *p = p;
1130 double zw, w, maxw = 0.0, zoom = zoom;
1132 if (state.pagedimcount == 0) return;
1134 switch (state.fitmodel) {
1135 case FitProportional:
1136 for (pindex = 0; pindex < state.pagedimcount; ++pindex) {
1137 double x0, x1;
1139 p = &state.pagedims[pindex];
1140 fz_rotate (&rm, p->rotate + state.rotate);
1141 box = p->mediabox;
1142 fz_transform_rect (&box, &rm);
1144 x0 = fz_min (box.x0, box.x1);
1145 x1 = fz_max (box.x0, box.x1);
1147 w = x1 - x0;
1148 maxw = fz_max (w, maxw);
1149 zoom = state.w / maxw;
1151 break;
1153 case FitPage:
1154 maxw = state.w;
1155 break;
1157 case FitWidth:
1158 break;
1160 default:
1161 ARSERT (0 && state.fitmodel);
1164 for (pindex = 0; pindex < state.pagedimcount; ++pindex) {
1165 fz_rect rect;
1166 fz_matrix tm, sm;
1168 p = &state.pagedims[pindex];
1169 fz_rotate (&ctm, state.rotate);
1170 fz_rotate (&rm, p->rotate + state.rotate);
1171 box = p->mediabox;
1172 fz_transform_rect (&box, &rm);
1173 w = box.x1 - box.x0;
1174 switch (state.fitmodel) {
1175 case FitProportional:
1176 p->left = ((maxw - w) * zoom) / 2.0;
1177 break;
1178 case FitPage:
1180 double zh, h;
1181 zw = maxw / w;
1182 h = box.y1 - box.y0;
1183 zh = state.h / h;
1184 zoom = fz_min (zw, zh);
1185 p->left = (maxw - (w * zoom)) / 2.0;
1187 break;
1188 case FitWidth:
1189 p->left = 0;
1190 zoom = state.w / w;
1191 break;
1194 fz_scale (&p->zoomctm, zoom, zoom);
1195 fz_concat (&ctm, &p->zoomctm, &ctm);
1197 fz_rotate (&rm, p->rotate);
1198 p->pagebox = p->mediabox;
1199 fz_transform_rect (&p->pagebox, &rm);
1200 p->pagebox.x1 -= p->pagebox.x0;
1201 p->pagebox.y1 -= p->pagebox.y0;
1202 p->pagebox.x0 = 0;
1203 p->pagebox.y0 = 0;
1204 rect = p->pagebox;
1205 fz_transform_rect (&rect, &ctm);
1206 fz_round_rect (&p->bounds, &rect);
1207 p->ctm = ctm;
1209 fz_translate (&tm, 0, -p->mediabox.y1);
1210 fz_scale (&sm, zoom, -zoom);
1211 fz_concat (&ctm, &tm, &sm);
1213 p->tctmready = 0;
1216 do {
1217 int x0 = fz_mini (p->bounds.x0, p->bounds.x1);
1218 int y0 = fz_mini (p->bounds.y0, p->bounds.y1);
1219 int x1 = fz_maxi (p->bounds.x0, p->bounds.x1);
1220 int y1 = fz_maxi (p->bounds.y0, p->bounds.y1);
1221 int boundw = x1 - x0;
1222 int boundh = y1 - y0;
1224 printd ("pdim %d %d %d %d", p->pageno, boundw, boundh, p->left);
1225 } while (p-- != state.pagedims);
1228 struct pagedim *pdimofpageno (int pageno)
1230 struct pagedim *pdim = state.pagedims;
1232 for (int i = 0; i < state.pagedimcount; ++i) {
1233 if (state.pagedims[i].pageno > pageno)
1234 break;
1235 pdim = &state.pagedims[i];
1237 return pdim;
1240 static
1241 struct anchor { int n; int x; int y; int w; int h; }
1242 uritoanchor (const char *uri)
1244 fz_point p;
1245 struct anchor a;
1247 a.n = -1;
1248 a.n = fz_resolve_link (state.ctx, state.doc, uri, &p.x, &p.y);
1249 if (a.n >= 0) {
1250 struct pagedim *pdim = pdimofpageno (a.n);
1251 fz_transform_point (&p, &pdim->ctm);
1252 a.x = p.x;
1253 a.y = p.y;
1254 a.h = fz_maxi (fz_absi (pdim->bounds.y1 - pdim->bounds.y0), 0);
1256 return a;
1259 static void recurse_outline (fz_outline *outline, int level)
1261 while (outline) {
1262 struct anchor a = uritoanchor (outline->uri);
1263 if (a.n >= 0) {
1264 printd ("o %d %d %d %d %s", level, a.n, a.y, a.h, outline->title);
1266 else {
1267 printd ("on %d %s", level, outline->title);
1269 if (outline->down) {
1270 recurse_outline (outline->down, level + 1);
1272 outline = outline->next;
1276 static void process_outline (void)
1278 fz_outline *outline;
1280 if (!state.needoutline || !state.pagedimcount) return;
1282 state.needoutline = 0;
1283 outline = fz_load_outline (state.ctx, state.doc);
1284 if (outline) {
1285 recurse_outline (outline, 0);
1286 fz_drop_outline (state.ctx, outline);
1290 static char *strofspan (fz_stext_span *span)
1292 char *p;
1293 char utf8[10];
1294 fz_stext_char *ch;
1295 size_t size = 0, cap = 80;
1297 p = malloc (cap + 1);
1298 if (!p) return NULL;
1300 for (ch = span->text; ch < span->text + span->len; ++ch) {
1301 int n = fz_runetochar (utf8, ch->c);
1302 if (size + n > cap) {
1303 cap *= 2;
1304 p = realloc (p, cap + 1);
1305 if (!p) return NULL;
1308 memcpy (p + size, utf8, n);
1309 size += n;
1311 p[size] = 0;
1312 return p;
1315 static int matchspan (regex_t *re, fz_stext_span *span,
1316 int stop, int pageno, double start)
1318 int ret;
1319 char *p;
1320 regmatch_t rm;
1321 int a, b, c;
1322 fz_rect sb, eb;
1323 fz_point p1, p2, p3, p4;
1325 p = strofspan (span);
1326 if (!p) return -1;
1328 ret = regexec (re, p, 1, &rm, 0);
1329 if (ret) {
1330 free (p);
1331 if (ret != REG_NOMATCH) {
1332 size_t size;
1333 char errbuf[80];
1334 size = regerror (ret, re, errbuf, sizeof (errbuf));
1335 printd ("msg regexec error `%.*s'",
1336 (int) size, errbuf);
1337 return -1;
1339 return 0;
1341 else {
1342 int l = span->len;
1344 for (a = 0, c = 0; c < rm.rm_so && a < l; a++) {
1345 c += fz_runelen (span->text[a].c);
1347 for (b = a; c < rm.rm_eo - 1 && b < l; b++) {
1348 c += fz_runelen (span->text[b].c);
1351 if (fz_runelen (span->text[b].c) > 1) {
1352 b = fz_maxi (0, b-1);
1355 fz_stext_char_bbox (state.ctx, &sb, span, a);
1356 fz_stext_char_bbox (state.ctx, &eb, span, b);
1358 p1.x = sb.x0;
1359 p1.y = sb.y0;
1360 p2.x = eb.x1;
1361 p2.y = sb.y0;
1362 p3.x = eb.x1;
1363 p3.y = eb.y1;
1364 p4.x = sb.x0;
1365 p4.y = eb.y1;
1367 if (!stop) {
1368 printd ("firstmatch %d %d %f %f %f %f %f %f %f %f",
1369 pageno, 1,
1370 p1.x, p1.y,
1371 p2.x, p2.y,
1372 p3.x, p3.y,
1373 p4.x, p4.y);
1375 printd ("progress 1 found at %d `%.*s' in %f sec",
1376 pageno + 1, (int) (rm.rm_eo - rm.rm_so), &p[rm.rm_so],
1377 now () - start);
1379 else {
1380 printd ("match %d %d %f %f %f %f %f %f %f %f",
1381 pageno, 2,
1382 p1.x, p1.y,
1383 p2.x, p2.y,
1384 p3.x, p3.y,
1385 p4.x, p4.y);
1387 free (p);
1388 return 1;
1392 static int compareblocks (const void *l, const void *r)
1394 fz_stext_block const *ls = l;
1395 fz_stext_block const *rs = r;
1396 return ls->bbox.y0 - rs->bbox.y0;
1399 /* wishful thinking function */
1400 static void search (regex_t *re, int pageno, int y, int forward)
1402 int j;
1403 fz_device *tdev;
1404 fz_stext_page *text;
1405 fz_stext_sheet *sheet;
1406 struct pagedim *pdim;
1407 int stop = 0, niters = 0;
1408 double start, end;
1409 fz_page *page;
1411 start = now ();
1412 while (pageno >= 0 && pageno < state.pagecount && !stop) {
1413 if (niters++ == 5) {
1414 niters = 0;
1415 if (hasdata ()) {
1416 printd ("progress 1 attention requested aborting search at %d",
1417 pageno);
1418 stop = 1;
1420 else {
1421 printd ("progress %f searching in page %d",
1422 (double) (pageno + 1) / state.pagecount,
1423 pageno);
1426 pdim = pdimofpageno (pageno);
1427 sheet = fz_new_stext_sheet (state.ctx);
1428 text = fz_new_stext_page (state.ctx, &pdim->mediabox);
1429 tdev = fz_new_stext_device (state.ctx, sheet, text, 0);
1431 page = fz_load_page (state.ctx, state.doc, pageno);
1433 fz_matrix ctm = pagectm1 (page, pdim);
1434 fz_run_page (state.ctx, page, tdev, &ctm, NULL);
1437 qsort (text->blocks, text->len, sizeof (*text->blocks), compareblocks);
1438 fz_close_device (state.ctx, tdev);
1439 fz_drop_device (state.ctx, tdev);
1441 for (j = 0; j < text->len; ++j) {
1442 int k;
1443 fz_page_block *pb;
1444 fz_stext_block *block;
1446 pb = &text->blocks[forward ? j : text->len - 1 - j];
1447 if (pb->type != FZ_PAGE_BLOCK_TEXT) continue;
1448 block = pb->u.text;
1450 for (k = 0; k < block->len; ++k) {
1451 fz_stext_line *line;
1452 fz_stext_span *span;
1454 if (forward) {
1455 line = &block->lines[k];
1456 if (line->bbox.y0 < y + 1) continue;
1458 else {
1459 line = &block->lines[block->len - 1 - k];
1460 if (line->bbox.y0 > y - 1) continue;
1463 for (span = line->first_span; span; span = span->next) {
1464 switch (matchspan (re, span, stop, pageno, start)) {
1465 case 0: break;
1466 case 1: stop = 1; break;
1467 case -1: stop = 1; goto endloop;
1472 if (forward) {
1473 pageno += 1;
1474 y = 0;
1476 else {
1477 pageno -= 1;
1478 y = INT_MAX;
1480 endloop:
1481 fz_drop_stext_page (state.ctx, text);
1482 fz_drop_stext_sheet (state.ctx, sheet);
1483 fz_drop_page (state.ctx, page);
1485 end = now ();
1486 if (!stop) {
1487 printd ("progress 1 no matches %f sec", end - start);
1489 printd ("clearrects");
1492 static void set_tex_params (int colorspace)
1494 union {
1495 unsigned char b;
1496 unsigned int s;
1497 } endianness = {1};
1499 switch (colorspace) {
1500 case 0:
1501 state.texiform = GL_RGBA8;
1502 state.texform = GL_RGBA;
1503 state.texty = GL_UNSIGNED_BYTE;
1504 state.colorspace = fz_device_rgb (state.ctx);
1505 break;
1506 case 1:
1507 state.texiform = GL_RGBA8;
1508 state.texform = GL_BGRA;
1509 state.texty = endianness.s > 1
1510 ? GL_UNSIGNED_INT_8_8_8_8
1511 : GL_UNSIGNED_INT_8_8_8_8_REV;
1512 state.colorspace = fz_device_bgr (state.ctx);
1513 break;
1514 case 2:
1515 state.texiform = GL_LUMINANCE_ALPHA;
1516 state.texform = GL_LUMINANCE_ALPHA;
1517 state.texty = GL_UNSIGNED_BYTE;
1518 state.colorspace = fz_device_gray (state.ctx);
1519 break;
1520 default:
1521 errx (1, "invalid colorspce %d", colorspace);
1525 static void realloctexts (int texcount)
1527 size_t size;
1529 if (texcount == state.texcount) return;
1531 if (texcount < state.texcount) {
1532 glDeleteTextures (state.texcount - texcount,
1533 state.texids + texcount);
1536 size = texcount * sizeof (*state.texids);
1537 state.texids = realloc (state.texids, size);
1538 if (!state.texids) {
1539 err (1, "realloc texids %" FMT_s, size);
1542 size = texcount * sizeof (*state.texowners);
1543 state.texowners = realloc (state.texowners, size);
1544 if (!state.texowners) {
1545 err (1, "realloc texowners %" FMT_s, size);
1547 if (texcount > state.texcount) {
1548 glGenTextures (texcount - state.texcount,
1549 state.texids + state.texcount);
1550 for (int i = state.texcount; i < texcount; ++i) {
1551 state.texowners[i].w = -1;
1552 state.texowners[i].slice = NULL;
1555 state.texcount = texcount;
1556 state.texindex = 0;
1559 static char *mbtoutf8 (char *s)
1561 char *p, *r;
1562 wchar_t *tmp;
1563 size_t i, ret, len;
1565 len = mbstowcs (NULL, s, strlen (s));
1566 if (len == 0) {
1567 return s;
1569 else {
1570 if (len == (size_t) -1) {
1571 return s;
1575 tmp = malloc (len * sizeof (wchar_t));
1576 if (!tmp) {
1577 return s;
1580 ret = mbstowcs (tmp, s, len);
1581 if (ret == (size_t) -1) {
1582 free (tmp);
1583 return s;
1586 len = 0;
1587 for (i = 0; i < ret; ++i) {
1588 len += fz_runelen (tmp[i]);
1591 p = r = malloc (len + 1);
1592 if (!r) {
1593 free (tmp);
1594 return s;
1597 for (i = 0; i < ret; ++i) {
1598 p += fz_runetochar (p, tmp[i]);
1600 *p = 0;
1601 free (tmp);
1602 return r;
1605 CAMLprim value ml_mbtoutf8 (value s_v)
1607 CAMLparam1 (s_v);
1608 CAMLlocal1 (ret_v);
1609 char *s, *r;
1611 s = String_val (s_v);
1612 r = mbtoutf8 (s);
1613 if (r == s) {
1614 ret_v = s_v;
1616 else {
1617 ret_v = caml_copy_string (r);
1618 free (r);
1620 CAMLreturn (ret_v);
1623 static void * mainloop (void UNUSED_ATTR *unused)
1625 char *p = NULL;
1626 int len, ret, oldlen = 0;
1628 fz_var (p);
1629 fz_var (oldlen);
1630 for (;;) {
1631 len = readlen (state.csock);
1632 if (len == 0) {
1633 errx (1, "readlen returned 0");
1636 if (oldlen < len + 1) {
1637 p = realloc (p, len + 1);
1638 if (!p) {
1639 err (1, "realloc %d failed", len + 1);
1641 oldlen = len + 1;
1643 readdata (state.csock, p, len);
1644 p[len] = 0;
1646 if (!strncmp ("open", p, 4)) {
1647 int wthack, off, usedoccss, ok = 0;
1648 char *password;
1649 char *filename;
1650 char *utf8filename;
1651 size_t filenamelen;
1653 fz_var (ok);
1654 ret = sscanf (p + 5, " %d %d %d %n",
1655 &wthack, &state.cxack, &usedoccss, &off);
1656 if (ret != 3) {
1657 errx (1, "malformed open `%.*s' ret=%d", len, p, ret);
1660 filename = p + 5 + off;
1661 filenamelen = strlen (filename);
1662 password = filename + filenamelen + 1;
1664 if (password[strlen (password) + 1]) {
1665 fz_set_user_css (state.ctx, password + strlen (password) + 1);
1668 lock ("open");
1669 fz_set_use_document_css (state.ctx, usedoccss);
1670 fz_try (state.ctx) {
1671 ok = openxref (filename, password);
1673 fz_catch (state.ctx) {
1674 utf8filename = mbtoutf8 (filename);
1675 printd ("msg Could not open %s", utf8filename);
1677 if (ok) {
1678 pdfinfo ();
1679 initpdims (wthack);
1681 unlock ("open");
1683 if (ok) {
1684 if (!wthack) {
1685 utf8filename = mbtoutf8 (filename);
1686 printd ("msg Opened %s (press h/F1 to get help)",
1687 utf8filename);
1688 if (utf8filename != filename) {
1689 free (utf8filename);
1692 state.needoutline = 1;
1695 else if (!strncmp ("cs", p, 2)) {
1696 int i, colorspace;
1698 ret = sscanf (p + 2, " %d", &colorspace);
1699 if (ret != 1) {
1700 errx (1, "malformed cs `%.*s' ret=%d", len, p, ret);
1702 lock ("cs");
1703 set_tex_params (colorspace);
1704 for (i = 0; i < state.texcount; ++i) {
1705 state.texowners[i].w = -1;
1706 state.texowners[i].slice = NULL;
1708 unlock ("cs");
1710 else if (!strncmp ("freepage", p, 8)) {
1711 void *ptr;
1713 ret = sscanf (p + 8, " %" SCN_ptr, SCN_ptr_cast (&ptr));
1714 if (ret != 1) {
1715 errx (1, "malformed freepage `%.*s' ret=%d", len, p, ret);
1717 freepage (ptr);
1719 else if (!strncmp ("freetile", p, 8)) {
1720 void *ptr;
1722 ret = sscanf (p + 8, " %" SCN_ptr, SCN_ptr_cast (&ptr));
1723 if (ret != 1) {
1724 errx (1, "malformed freetile `%.*s' ret=%d", len, p, ret);
1726 freetile (ptr);
1728 else if (!strncmp ("search", p, 6)) {
1729 int icase, pageno, y, len2, forward;
1730 char *pattern;
1731 regex_t re;
1733 ret = sscanf (p + 6, " %d %d %d %d,%n",
1734 &icase, &pageno, &y, &forward, &len2);
1735 if (ret != 4) {
1736 errx (1, "malformed search `%s' ret=%d", p, ret);
1739 pattern = p + 6 + len2;
1740 ret = regcomp (&re, pattern,
1741 REG_EXTENDED | (icase ? REG_ICASE : 0));
1742 if (ret) {
1743 char errbuf[80];
1744 size_t size;
1746 size = regerror (ret, &re, errbuf, sizeof (errbuf));
1747 printd ("msg regcomp failed `%.*s'", (int) size, errbuf);
1749 else {
1750 search (&re, pageno, y, forward);
1751 regfree (&re);
1754 else if (!strncmp ("geometry", p, 8)) {
1755 int w, h, fitmodel;
1757 printd ("clear");
1758 ret = sscanf (p + 8, " %d %d %d", &w, &h, &fitmodel);
1759 if (ret != 3) {
1760 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
1763 lock ("geometry");
1764 state.h = h;
1765 if (w != state.w) {
1766 state.w = w;
1767 for (int i = 0; i < state.texcount; ++i) {
1768 state.texowners[i].slice = NULL;
1771 state.fitmodel = fitmodel;
1772 layout ();
1773 process_outline ();
1775 state.gen++;
1776 unlock ("geometry");
1777 printd ("continue %d", state.pagecount);
1779 else if (!strncmp ("reqlayout", p, 9)) {
1780 char *nameddest;
1781 int rotate, off, h;
1782 unsigned int fitmodel;
1783 pdf_document *pdf;
1785 printd ("clear");
1786 ret = sscanf (p + 9, " %d %u %d %n",
1787 &rotate, &fitmodel, &h, &off);
1788 if (ret != 3) {
1789 errx (1, "bad reqlayout line `%.*s' ret=%d", len, p, ret);
1791 lock ("reqlayout");
1792 pdf = pdf_specifics (state.ctx, state.doc);
1793 if (state.rotate != rotate || state.fitmodel != fitmodel) {
1794 state.gen += 1;
1796 state.rotate = rotate;
1797 state.fitmodel = fitmodel;
1798 state.h = h;
1799 layout ();
1800 process_outline ();
1802 nameddest = p + 9 + off;
1803 if (pdf && nameddest && *nameddest) {
1804 fz_point xy;
1805 struct pagedim *pdim;
1806 int pageno = pdf_lookup_anchor (state.ctx, pdf, nameddest,
1807 &xy.x, &xy.y);
1808 pdim = pdimofpageno (pageno);
1809 fz_transform_point (&xy, &pdim->ctm);
1810 printd ("a %d %d %d", pageno, (int) xy.x, (int) xy.y);
1813 state.gen++;
1814 unlock ("reqlayout");
1815 printd ("continue %d", state.pagecount);
1817 else if (!strncmp ("page", p, 4)) {
1818 double a, b;
1819 struct page *page;
1820 int pageno, pindex;
1822 ret = sscanf (p + 4, " %d %d", &pageno, &pindex);
1823 if (ret != 2) {
1824 errx (1, "bad page line `%.*s' ret=%d", len, p, ret);
1827 lock ("page");
1828 a = now ();
1829 page = loadpage (pageno, pindex);
1830 b = now ();
1831 unlock ("page");
1833 printd ("page %" FMT_ptr " %f", FMT_ptr_cast (page), b - a);
1835 else if (!strncmp ("tile", p, 4)) {
1836 int x, y, w, h;
1837 struct page *page;
1838 struct tile *tile;
1839 double a, b;
1840 void *data;
1842 ret = sscanf (p + 4, " %" SCN_ptr " %d %d %d %d %" SCN_ptr,
1843 SCN_ptr_cast (&page), &x, &y, &w, &h,
1844 SCN_ptr_cast (&data));
1845 if (ret != 6) {
1846 errx (1, "bad tile line `%.*s' ret=%d", len, p, ret);
1849 lock ("tile");
1850 a = now ();
1851 tile = rendertile (page, x, y, w, h, data);
1852 b = now ();
1853 unlock ("tile");
1855 printd ("tile %d %d %" FMT_ptr " %u %f",
1856 x, y,
1857 FMT_ptr_cast (tile),
1858 tile->w * tile->h * tile->pixmap->n,
1859 b - a);
1861 else if (!strncmp ("trimset", p, 7)) {
1862 fz_irect fuzz;
1863 int trimmargins;
1865 ret = sscanf (p + 7, " %d %d %d %d %d",
1866 &trimmargins, &fuzz.x0, &fuzz.y0, &fuzz.x1, &fuzz.y1);
1867 if (ret != 5) {
1868 errx (1, "malformed trimset `%.*s' ret=%d", len, p, ret);
1870 lock ("trimset");
1871 state.trimmargins = trimmargins;
1872 if (memcmp (&fuzz, &state.trimfuzz, sizeof (fuzz))) {
1873 state.trimanew = 1;
1874 state.trimfuzz = fuzz;
1876 unlock ("trimset");
1878 else if (!strncmp ("settrim", p, 7)) {
1879 fz_irect fuzz;
1880 int trimmargins;
1882 ret = sscanf (p + 7, " %d %d %d %d %d",
1883 &trimmargins, &fuzz.x0, &fuzz.y0, &fuzz.x1, &fuzz.y1);
1884 if (ret != 5) {
1885 errx (1, "malformed settrim `%.*s' ret=%d", len, p, ret);
1887 printd ("clear");
1888 lock ("settrim");
1889 state.trimmargins = trimmargins;
1890 state.needoutline = 1;
1891 if (memcmp (&fuzz, &state.trimfuzz, sizeof (fuzz))) {
1892 state.trimanew = 1;
1893 state.trimfuzz = fuzz;
1895 state.pagedimcount = 0;
1896 free (state.pagedims);
1897 state.pagedims = NULL;
1898 initpdims (0);
1899 layout ();
1900 process_outline ();
1901 unlock ("settrim");
1902 printd ("continue %d", state.pagecount);
1904 else if (!strncmp ("sliceh", p, 6)) {
1905 int h;
1907 ret = sscanf (p + 6, " %d", &h);
1908 if (ret != 1) {
1909 errx (1, "malformed sliceh `%.*s' ret=%d", len, p, ret);
1911 if (h != state.sliceheight) {
1912 state.sliceheight = h;
1913 for (int i = 0; i < state.texcount; ++i) {
1914 state.texowners[i].w = -1;
1915 state.texowners[i].h = -1;
1916 state.texowners[i].slice = NULL;
1920 else if (!strncmp ("interrupt", p, 9)) {
1921 printd ("vmsg interrupted");
1923 else {
1924 errx (1, "unknown command %.*s", len, p);
1927 return 0;
1930 CAMLprim value ml_isexternallink (value uri_v)
1932 CAMLparam1 (uri_v);
1933 int ext = fz_is_external_link (state.ctx, String_val (uri_v));
1934 CAMLreturn (Val_bool (ext));
1937 CAMLprim value ml_uritolocation (value uri_v)
1939 CAMLparam1 (uri_v);
1940 CAMLlocal1 (ret_v);
1941 int pageno;
1942 fz_point xy;
1943 struct pagedim *pdim;
1945 pageno = fz_resolve_link (state.ctx, state.doc, String_val (uri_v),
1946 &xy.x, &xy.y);
1947 pdim = pdimofpageno (pageno);
1948 fz_transform_point (&xy, &pdim->ctm);
1949 ret_v = caml_alloc_tuple (3);
1950 Field (ret_v, 0) = Val_int (pageno);
1951 Field (ret_v, 1) = caml_copy_double (xy.x);
1952 Field (ret_v, 2) = caml_copy_double (xy.y);
1953 CAMLreturn (ret_v);
1956 CAMLprim value ml_realloctexts (value texcount_v)
1958 CAMLparam1 (texcount_v);
1959 int ok;
1961 if (trylock (__func__)) {
1962 ok = 0;
1963 goto done;
1965 realloctexts (Int_val (texcount_v));
1966 ok = 1;
1967 unlock (__func__);
1969 done:
1970 CAMLreturn (Val_bool (ok));
1973 static void recti (int x0, int y0, int x1, int y1)
1975 GLfloat *v = state.vertices;
1977 glVertexPointer (2, GL_FLOAT, 0, v);
1978 v[0] = x0; v[1] = y0;
1979 v[2] = x1; v[3] = y0;
1980 v[4] = x0; v[5] = y1;
1981 v[6] = x1; v[7] = y1;
1982 glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
1985 static void showsel (struct page *page, int ox, int oy)
1987 int seen = 0;
1988 fz_irect bbox;
1989 fz_rect rect;
1990 fz_stext_line *line;
1991 fz_page_block *pageb;
1992 fz_stext_block *block;
1993 struct mark first, last;
1994 unsigned char selcolor[] = {15,15,15,140};
1996 first = page->fmark;
1997 last = page->lmark;
1999 if (!first.span || !last.span) return;
2001 glEnable (GL_BLEND);
2002 glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA);
2003 glColor4ubv (selcolor);
2005 ox += state.pagedims[page->pdimno].bounds.x0;
2006 oy += state.pagedims[page->pdimno].bounds.y0;
2007 for (pageb = page->text->blocks;
2008 pageb < page->text->blocks + page->text->len;
2009 ++pageb) {
2010 if (pageb->type != FZ_PAGE_BLOCK_TEXT) continue;
2011 block = pageb->u.text;
2013 for (line = block->lines;
2014 line < block->lines + block->len;
2015 ++line) {
2016 fz_stext_span *span;
2017 rect = fz_empty_rect;
2019 for (span = line->first_span; span; span = span->next) {
2020 int i, j, k;
2021 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0;
2023 j = 0;
2024 k = span->len - 1;
2026 if (span == page->fmark.span && span == page->lmark.span) {
2027 seen = 1;
2028 j = fz_mini (first.i, last.i);
2029 k = fz_maxi (first.i, last.i);
2031 else {
2032 if (span == first.span) {
2033 seen = 1;
2034 j = first.i;
2036 else if (span == last.span) {
2037 seen = 1;
2038 k = last.i;
2042 if (seen) {
2043 for (i = j; i <= k; ++i) {
2044 fz_rect bbox1;
2045 fz_union_rect (&rect,
2046 fz_stext_char_bbox (state.ctx, &bbox1,
2047 span, i));
2049 fz_round_rect (&bbox, &rect);
2050 lprintf ("%d %d %d %d oy=%d ox=%d\n",
2051 bbox.x0,
2052 bbox.y0,
2053 bbox.x1,
2054 bbox.y1,
2055 oy, ox);
2057 recti (bbox.x0 + ox, bbox.y0 + oy,
2058 bbox.x1 + ox, bbox.y1 + oy);
2059 if (span == last.span) {
2060 goto done;
2062 rect = fz_empty_rect;
2067 done:
2068 glDisable (GL_BLEND);
2071 #include "glfont.c"
2073 static void stipplerect (fz_matrix *m,
2074 fz_point *p1,
2075 fz_point *p2,
2076 fz_point *p3,
2077 fz_point *p4,
2078 GLfloat *texcoords,
2079 GLfloat *vertices)
2081 fz_transform_point (p1, m);
2082 fz_transform_point (p2, m);
2083 fz_transform_point (p3, m);
2084 fz_transform_point (p4, m);
2086 float w, h, s, t;
2088 w = p2->x - p1->x;
2089 h = p2->y - p1->y;
2090 t = hypotf (w, h) * .25f;
2092 w = p3->x - p2->x;
2093 h = p3->y - p2->y;
2094 s = hypotf (w, h) * .25f;
2096 texcoords[0] = 0; vertices[0] = p1->x; vertices[1] = p1->y;
2097 texcoords[1] = t; vertices[2] = p2->x; vertices[3] = p2->y;
2099 texcoords[2] = 0; vertices[4] = p2->x; vertices[5] = p2->y;
2100 texcoords[3] = s; vertices[6] = p3->x; vertices[7] = p3->y;
2102 texcoords[4] = 0; vertices[8] = p3->x; vertices[9] = p3->y;
2103 texcoords[5] = t; vertices[10] = p4->x; vertices[11] = p4->y;
2105 texcoords[6] = 0; vertices[12] = p4->x; vertices[13] = p4->y;
2106 texcoords[7] = s; vertices[14] = p1->x; vertices[15] = p1->y;
2108 glDrawArrays (GL_LINES, 0, 8);
2111 static void solidrect (fz_matrix *m,
2112 fz_point *p1,
2113 fz_point *p2,
2114 fz_point *p3,
2115 fz_point *p4,
2116 GLfloat *vertices)
2118 fz_transform_point (p1, m);
2119 fz_transform_point (p2, m);
2120 fz_transform_point (p3, m);
2121 fz_transform_point (p4, m);
2122 vertices[0] = p1->x; vertices[1] = p1->y;
2123 vertices[2] = p2->x; vertices[3] = p2->y;
2125 vertices[4] = p3->x; vertices[5] = p3->y;
2126 vertices[6] = p4->x; vertices[7] = p4->y;
2127 glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
2130 static void highlightlinks (struct page *page, int xoff, int yoff)
2132 fz_matrix ctm, tm, pm;
2133 fz_link *link, *links;
2134 GLfloat *texcoords = state.texcoords;
2135 GLfloat *vertices = state.vertices;
2137 links = fz_load_links (state.ctx, page->fzpage);
2139 glEnable (GL_TEXTURE_1D);
2140 glEnable (GL_BLEND);
2141 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2142 glBindTexture (GL_TEXTURE_1D, state.stid);
2144 xoff -= state.pagedims[page->pdimno].bounds.x0;
2145 yoff -= state.pagedims[page->pdimno].bounds.y0;
2146 fz_translate (&tm, xoff, yoff);
2147 pm = pagectm (page);
2148 fz_concat (&ctm, &pm, &tm);
2150 glTexCoordPointer (1, GL_FLOAT, 0, texcoords);
2151 glVertexPointer (2, GL_FLOAT, 0, vertices);
2153 for (link = links; link; link = link->next) {
2154 fz_point p1, p2, p3, p4;
2156 p1.x = link->rect.x0;
2157 p1.y = link->rect.y0;
2159 p2.x = link->rect.x1;
2160 p2.y = link->rect.y0;
2162 p3.x = link->rect.x1;
2163 p3.y = link->rect.y1;
2165 p4.x = link->rect.x0;
2166 p4.y = link->rect.y1;
2168 /* TODO: different colours for different schemes */
2169 if (fz_is_external_link (state.ctx, link->uri)) glColor3ub (0, 0, 255);
2170 else glColor3ub (255, 0, 0);
2172 stipplerect (&ctm, &p1, &p2, &p3, &p4, texcoords, vertices);
2175 for (int i = 0; i < page->annotcount; ++i) {
2176 fz_point p1, p2, p3, p4;
2177 struct annot *annot = &page->annots[i];
2179 p1.x = annot->bbox.x0;
2180 p1.y = annot->bbox.y0;
2182 p2.x = annot->bbox.x1;
2183 p2.y = annot->bbox.y0;
2185 p3.x = annot->bbox.x1;
2186 p3.y = annot->bbox.y1;
2188 p4.x = annot->bbox.x0;
2189 p4.y = annot->bbox.y1;
2191 glColor3ub (0, 0, 128);
2192 stipplerect (&ctm, &p1, &p2, &p3, &p4, texcoords, vertices);
2195 glDisable (GL_BLEND);
2196 glDisable (GL_TEXTURE_1D);
2199 static int compareslinks (const void *l, const void *r)
2201 struct slink const *ls = l;
2202 struct slink const *rs = r;
2203 if (ls->bbox.y0 == rs->bbox.y0) {
2204 return rs->bbox.x0 - rs->bbox.x0;
2206 return ls->bbox.y0 - rs->bbox.y0;
2209 static void droptext (struct page *page)
2211 if (page->text) {
2212 fz_drop_stext_page (state.ctx, page->text);
2213 page->fmark.i = -1;
2214 page->lmark.i = -1;
2215 page->fmark.span = NULL;
2216 page->lmark.span = NULL;
2217 page->text = NULL;
2219 if (page->sheet) {
2220 fz_drop_stext_sheet (state.ctx, page->sheet);
2221 page->sheet = NULL;
2225 static void dropannots (struct page *page)
2227 if (page->annots) {
2228 free (page->annots);
2229 page->annots = NULL;
2230 page->annotcount = 0;
2234 static void ensureannots (struct page *page)
2236 int i, count = 0;
2237 size_t annotsize = sizeof (*page->annots);
2238 fz_annot *annot;
2240 if (state.gen != page->agen) {
2241 dropannots (page);
2242 page->agen = state.gen;
2244 if (page->annots) return;
2246 for (annot = fz_first_annot (state.ctx, page->fzpage);
2247 annot;
2248 annot = fz_next_annot (state.ctx, annot)) {
2249 count++;
2252 if (count > 0) {
2253 page->annotcount = count;
2254 page->annots = calloc (count, annotsize);
2255 if (!page->annots) {
2256 err (1, "calloc annots %d", count);
2259 for (annot = fz_first_annot (state.ctx, page->fzpage), i = 0;
2260 annot;
2261 annot = fz_next_annot (state.ctx, annot), i++) {
2262 fz_rect rect;
2264 fz_bound_annot (state.ctx, annot, &rect);
2265 page->annots[i].annot = annot;
2266 fz_round_rect (&page->annots[i].bbox, &rect);
2271 static void dropslinks (struct page *page)
2273 if (page->slinks) {
2274 free (page->slinks);
2275 page->slinks = NULL;
2276 page->slinkcount = 0;
2280 static void ensureslinks (struct page *page)
2282 fz_matrix ctm;
2283 int i, count;
2284 size_t slinksize = sizeof (*page->slinks);
2285 fz_link *link, *links;
2287 ensureannots (page);
2288 if (state.gen != page->sgen) {
2289 dropslinks (page);
2290 page->sgen = state.gen;
2292 if (page->slinks) return;
2294 links = fz_load_links (state.ctx, page->fzpage);
2295 ctm = pagectm (page);
2297 count = page->annotcount;
2298 for (link = links; link; link = link->next) {
2299 count++;
2301 if (count > 0) {
2302 int j;
2304 page->slinkcount = count;
2305 page->slinks = calloc (count, slinksize);
2306 if (!page->slinks) {
2307 err (1, "calloc slinks %d", count);
2310 for (i = 0, link = links; link; ++i, link = link->next) {
2311 fz_rect rect;
2313 rect = link->rect;
2314 fz_transform_rect (&rect, &ctm);
2315 page->slinks[i].tag = SLINK;
2316 page->slinks[i].u.link = link;
2317 fz_round_rect (&page->slinks[i].bbox, &rect);
2319 for (j = 0; j < page->annotcount; ++j, ++i) {
2320 fz_rect rect;
2321 fz_bound_annot (state.ctx, page->annots[j].annot, &rect);
2322 fz_transform_rect (&rect, &ctm);
2323 fz_round_rect (&page->slinks[i].bbox, &rect);
2325 page->slinks[i].tag = SANNOT;
2326 page->slinks[i].u.annot = page->annots[j].annot;
2328 qsort (page->slinks, count, slinksize, compareslinks);
2332 /* slightly tweaked fmt_ulong by D.J. Bernstein */
2333 static void fmt_linkn (char *s, unsigned int u)
2335 unsigned int len; unsigned int q;
2336 unsigned int zma = 'z' - 'a' + 1;
2337 len = 1; q = u;
2338 while (q > zma - 1) { ++len; q /= zma; }
2339 if (s) {
2340 s += len;
2341 do { *--s = 'a' + (u % zma) - (u < zma && len > 1); u /= zma; } while(u);
2342 /* handles u == 0 */
2344 s[len] = 0;
2347 static void highlightslinks (struct page *page, int xoff, int yoff,
2348 int noff, char *targ, int tlen, int hfsize)
2350 char buf[40];
2351 struct slink *slink;
2352 double x0, y0, x1, y1, w;
2354 ensureslinks (page);
2355 glColor3ub (0xc3, 0xb0, 0x91);
2356 for (int i = 0; i < page->slinkcount; ++i) {
2357 fmt_linkn (buf, i + noff);
2358 if (!tlen || !strncmp (targ, buf, tlen)) {
2359 slink = &page->slinks[i];
2361 x0 = slink->bbox.x0 + xoff - 5;
2362 y1 = slink->bbox.y0 + yoff - 5;
2363 y0 = y1 + 10 + hfsize;
2364 w = measure_string (state.face, hfsize, buf);
2365 x1 = x0 + w + 10;
2366 recti (x0, y0, x1, y1);
2370 glEnable (GL_BLEND);
2371 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2372 glEnable (GL_TEXTURE_2D);
2373 glColor3ub (0, 0, 0);
2374 for (int i = 0; i < page->slinkcount; ++i) {
2375 fmt_linkn (buf, i + noff);
2376 if (!tlen || !strncmp (targ, buf, tlen)) {
2377 slink = &page->slinks[i];
2379 x0 = slink->bbox.x0 + xoff;
2380 y0 = slink->bbox.y0 + yoff + hfsize;
2381 draw_string (state.face, hfsize, x0, y0, buf);
2384 glDisable (GL_TEXTURE_2D);
2385 glDisable (GL_BLEND);
2388 static void uploadslice (struct tile *tile, struct slice *slice)
2390 int offset;
2391 struct slice *slice1;
2392 unsigned char *texdata;
2394 offset = 0;
2395 for (slice1 = tile->slices; slice != slice1; slice1++) {
2396 offset += slice1->h * tile->w * tile->pixmap->n;
2398 if (slice->texindex != -1 && slice->texindex < state.texcount
2399 && state.texowners[slice->texindex].slice == slice) {
2400 glBindTexture (TEXT_TYPE, state.texids[slice->texindex]);
2402 else {
2403 int subimage = 0;
2404 int texindex = state.texindex++ % state.texcount;
2406 if (state.texowners[texindex].w == tile->w) {
2407 if (state.texowners[texindex].h >= slice->h) {
2408 subimage = 1;
2410 else {
2411 state.texowners[texindex].h = slice->h;
2414 else {
2415 state.texowners[texindex].h = slice->h;
2418 state.texowners[texindex].w = tile->w;
2419 state.texowners[texindex].slice = slice;
2420 slice->texindex = texindex;
2422 glBindTexture (TEXT_TYPE, state.texids[texindex]);
2423 #if TEXT_TYPE == GL_TEXTURE_2D
2424 glTexParameteri (TEXT_TYPE, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2425 glTexParameteri (TEXT_TYPE, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2426 glTexParameteri (TEXT_TYPE, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2427 glTexParameteri (TEXT_TYPE, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2428 #endif
2429 if (tile->pbo) {
2430 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, tile->pbo->id);
2431 texdata = 0;
2433 else {
2434 texdata = tile->pixmap->samples;
2436 if (subimage) {
2437 glTexSubImage2D (TEXT_TYPE,
2441 tile->w,
2442 slice->h,
2443 state.texform,
2444 state.texty,
2445 texdata+offset
2448 else {
2449 glTexImage2D (TEXT_TYPE,
2451 state.texiform,
2452 tile->w,
2453 slice->h,
2455 state.texform,
2456 state.texty,
2457 texdata+offset
2460 if (tile->pbo) {
2461 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, 0);
2466 CAMLprim value ml_begintiles (value unit_v)
2468 CAMLparam1 (unit_v);
2469 glEnable (TEXT_TYPE);
2470 glTexCoordPointer (2, GL_FLOAT, 0, state.texcoords);
2471 glVertexPointer (2, GL_FLOAT, 0, state.vertices);
2472 CAMLreturn (unit_v);
2475 CAMLprim value ml_endtiles (value unit_v)
2477 CAMLparam1 (unit_v);
2478 glDisable (TEXT_TYPE);
2479 CAMLreturn (unit_v);
2482 CAMLprim void ml_drawtile (value args_v, value ptr_v)
2484 CAMLparam2 (args_v, ptr_v);
2485 int dispx = Int_val (Field (args_v, 0));
2486 int dispy = Int_val (Field (args_v, 1));
2487 int dispw = Int_val (Field (args_v, 2));
2488 int disph = Int_val (Field (args_v, 3));
2489 int tilex = Int_val (Field (args_v, 4));
2490 int tiley = Int_val (Field (args_v, 5));
2491 char *s = String_val (ptr_v);
2492 struct tile *tile = parse_pointer (__func__, s);
2493 int slicey, firstslice;
2494 struct slice *slice;
2495 GLfloat *texcoords = state.texcoords;
2496 GLfloat *vertices = state.vertices;
2498 firstslice = tiley / tile->sliceheight;
2499 slice = &tile->slices[firstslice];
2500 slicey = tiley % tile->sliceheight;
2502 while (disph > 0) {
2503 int dh;
2505 dh = slice->h - slicey;
2506 dh = fz_mini (disph, dh);
2507 uploadslice (tile, slice);
2509 texcoords[0] = tilex; texcoords[1] = slicey;
2510 texcoords[2] = tilex+dispw; texcoords[3] = slicey;
2511 texcoords[4] = tilex; texcoords[5] = slicey+dh;
2512 texcoords[6] = tilex+dispw; texcoords[7] = slicey+dh;
2514 vertices[0] = dispx; vertices[1] = dispy;
2515 vertices[2] = dispx+dispw; vertices[3] = dispy;
2516 vertices[4] = dispx; vertices[5] = dispy+dh;
2517 vertices[6] = dispx+dispw; vertices[7] = dispy+dh;
2519 #if TEXT_TYPE == GL_TEXTURE_2D
2520 for (int i = 0; i < 8; ++i) {
2521 texcoords[i] /= ((i & 1) == 0 ? tile->w : slice->h);
2523 #endif
2525 glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
2526 dispy += dh;
2527 disph -= dh;
2528 slice++;
2529 ARSERT (!(slice - tile->slices >= tile->slicecount && disph > 0));
2530 slicey = 0;
2532 CAMLreturn0;
2535 static void drawprect (struct page *page, int xoff, int yoff, value rects_v)
2537 fz_matrix ctm, tm, pm;
2538 fz_point p1, p2, p3, p4;
2539 GLfloat *vertices = state.vertices;
2540 double *v = (double *) rects_v;
2542 xoff -= state.pagedims[page->pdimno].bounds.x0;
2543 yoff -= state.pagedims[page->pdimno].bounds.y0;
2544 fz_translate (&tm, xoff, yoff);
2545 pm = pagectm (page);
2546 fz_concat (&ctm, &pm, &tm);
2548 glEnable (GL_BLEND);
2549 glVertexPointer (2, GL_FLOAT, 0, vertices);
2551 glColor4dv (v);
2552 p1.x = v[4];
2553 p1.y = v[5];
2555 p2.x = v[6];
2556 p2.y = v[5];
2558 p3.x = v[6];
2559 p3.y = v[7];
2561 p4.x = v[4];
2562 p4.y = v[7];
2563 solidrect (&ctm, &p1, &p2, &p3, &p4, vertices);
2564 glDisable (GL_BLEND);
2567 CAMLprim value ml_postprocess (value ptr_v, value hlinks_v,
2568 value xoff_v, value yoff_v,
2569 value li_v)
2571 CAMLparam5 (ptr_v, hlinks_v, xoff_v, yoff_v, li_v);
2572 int xoff = Int_val (xoff_v);
2573 int yoff = Int_val (yoff_v);
2574 int noff = Int_val (Field (li_v, 0));
2575 char *targ = String_val (Field (li_v, 1));
2576 int tlen = caml_string_length (Field (li_v, 1));
2577 int hfsize = Int_val (Field (li_v, 2));
2578 char *s = String_val (ptr_v);
2579 int hlmask = Int_val (hlinks_v);
2580 struct page *page = parse_pointer (__func__, s);
2582 if (!page->fzpage) {
2583 /* deal with loadpage failed pages */
2584 goto done;
2587 if (trylock (__func__)) {
2588 noff = -1;
2589 goto done;
2592 ensureannots (page);
2593 if (hlmask & 1) highlightlinks (page, xoff, yoff);
2594 if (hlmask & 2) {
2595 highlightslinks (page, xoff, yoff, noff, targ, tlen, hfsize);
2596 noff = page->slinkcount;
2598 if (page->tgen == state.gen) {
2599 showsel (page, xoff, yoff);
2601 unlock (__func__);
2603 done:
2604 CAMLreturn (Val_int (noff));
2607 CAMLprim void ml_drawprect (value ptr_v, value xoff_v, value yoff_v,
2608 value rects_v)
2610 CAMLparam4 (ptr_v, xoff_v, yoff_v, rects_v);
2611 int xoff = Int_val (xoff_v);
2612 int yoff = Int_val (yoff_v);
2613 char *s = String_val (ptr_v);
2614 struct page *page = parse_pointer (__func__, s);
2616 drawprect (page, xoff, yoff, rects_v);
2617 CAMLreturn0;
2620 static struct annot *getannot (struct page *page, int x, int y)
2622 fz_point p;
2623 fz_matrix ctm;
2624 const fz_matrix *tctm;
2625 pdf_document *pdf = pdf_specifics (state.ctx, state.doc);
2627 if (!page->annots) return NULL;
2629 if (pdf) {
2630 trimctm (pdf_page_from_fz_page (state.ctx, page->fzpage), page->pdimno);
2631 tctm = &state.pagedims[page->pdimno].tctm;
2633 else {
2634 tctm = &fz_identity;
2637 p.x = x;
2638 p.y = y;
2640 fz_concat (&ctm, tctm, &state.pagedims[page->pdimno].ctm);
2641 fz_invert_matrix (&ctm, &ctm);
2642 fz_transform_point (&p, &ctm);
2644 if (pdf) {
2645 for (int i = 0; i < page->annotcount; ++i) {
2646 struct annot *a = &page->annots[i];
2647 fz_rect rect;
2649 fz_bound_annot (state.ctx, a->annot, &rect);
2650 if (p.x >= rect.x0 && p.x <= rect.x1) {
2651 if (p.y >= rect.y0 && p.y <= rect.y1)
2652 return a;
2656 return NULL;
2659 static fz_link *getlink (struct page *page, int x, int y)
2661 fz_point p;
2662 fz_matrix ctm;
2663 fz_link *link, *links;
2665 links = fz_load_links (state.ctx, page->fzpage);
2667 p.x = x;
2668 p.y = y;
2670 ctm = pagectm (page);
2671 fz_invert_matrix (&ctm, &ctm);
2672 fz_transform_point (&p, &ctm);
2674 for (link = links; link; link = link->next) {
2675 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
2676 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
2677 return link;
2681 return NULL;
2684 static void ensuretext (struct page *page)
2686 if (state.gen != page->tgen) {
2687 droptext (page);
2688 page->tgen = state.gen;
2690 if (!page->text) {
2691 fz_matrix ctm;
2692 fz_device *tdev;
2694 page->text = fz_new_stext_page (state.ctx,
2695 &state.pagedims[page->pdimno].mediabox);
2696 page->sheet = fz_new_stext_sheet (state.ctx);
2697 tdev = fz_new_stext_device (state.ctx, page->sheet, page->text, 0);
2698 ctm = pagectm (page);
2699 fz_run_display_list (state.ctx, page->dlist,
2700 tdev, &ctm, &fz_infinite_rect, NULL);
2701 qsort (page->text->blocks, page->text->len,
2702 sizeof (*page->text->blocks), compareblocks);
2703 fz_close_device (state.ctx, tdev);
2704 fz_drop_device (state.ctx, tdev);
2708 CAMLprim value ml_find_page_with_links (value start_page_v, value dir_v)
2710 CAMLparam2 (start_page_v, dir_v);
2711 CAMLlocal1 (ret_v);
2712 int i, dir = Int_val (dir_v);
2713 int start_page = Int_val (start_page_v);
2714 int end_page = dir > 0 ? state.pagecount : -1;
2715 pdf_document *pdf;
2717 fz_var (end_page);
2718 ret_v = Val_int (0);
2719 lock (__func__);
2720 pdf = pdf_specifics (state.ctx, state.doc);
2721 for (i = start_page + dir; i != end_page; i += dir) {
2722 int found;
2724 fz_var (found);
2725 if (pdf) {
2726 pdf_page *page = NULL;
2728 fz_var (page);
2729 fz_try (state.ctx) {
2730 page = pdf_load_page (state.ctx, pdf, i);
2731 found = !!page->links || !!page->annots;
2733 fz_catch (state.ctx) {
2734 found = 0;
2736 if (page) {
2737 fz_drop_page (state.ctx, &page->super);
2740 else {
2741 fz_page *page = fz_load_page (state.ctx, state.doc, i);
2742 found = !!fz_load_links (state.ctx, page);
2743 fz_drop_page (state.ctx, page);
2746 if (found) {
2747 ret_v = caml_alloc_small (1, 1);
2748 Field (ret_v, 0) = Val_int (i);
2749 goto unlock;
2752 unlock:
2753 unlock (__func__);
2754 CAMLreturn (ret_v);
2757 enum { dir_first, dir_last };
2758 enum { dir_first_visible, dir_left, dir_right, dir_down, dir_up };
2760 CAMLprim value ml_findlink (value ptr_v, value dir_v)
2762 CAMLparam2 (ptr_v, dir_v);
2763 CAMLlocal2 (ret_v, pos_v);
2764 struct page *page;
2765 int dirtag, i, slinkindex;
2766 struct slink *found = NULL ,*slink;
2767 char *s = String_val (ptr_v);
2769 page = parse_pointer (__func__, s);
2770 ret_v = Val_int (0);
2771 lock (__func__);
2772 ensureslinks (page);
2774 if (Is_block (dir_v)) {
2775 dirtag = Tag_val (dir_v);
2776 switch (dirtag) {
2777 case dir_first_visible:
2779 int x0, y0, dir, first_index, last_index;
2781 pos_v = Field (dir_v, 0);
2782 x0 = Int_val (Field (pos_v, 0));
2783 y0 = Int_val (Field (pos_v, 1));
2784 dir = Int_val (Field (pos_v, 2));
2786 if (dir >= 0) {
2787 dir = 1;
2788 first_index = 0;
2789 last_index = page->slinkcount;
2791 else {
2792 first_index = page->slinkcount - 1;
2793 last_index = -1;
2796 for (i = first_index; i != last_index; i += dir) {
2797 slink = &page->slinks[i];
2798 if (slink->bbox.y0 >= y0 && slink->bbox.x0 >= x0) {
2799 found = slink;
2800 break;
2804 break;
2806 case dir_left:
2807 slinkindex = Int_val (Field (dir_v, 0));
2808 found = &page->slinks[slinkindex];
2809 for (i = slinkindex - 1; i >= 0; --i) {
2810 slink = &page->slinks[i];
2811 if (slink->bbox.x0 < found->bbox.x0) {
2812 found = slink;
2813 break;
2816 break;
2818 case dir_right:
2819 slinkindex = Int_val (Field (dir_v, 0));
2820 found = &page->slinks[slinkindex];
2821 for (i = slinkindex + 1; i < page->slinkcount; ++i) {
2822 slink = &page->slinks[i];
2823 if (slink->bbox.x0 > found->bbox.x0) {
2824 found = slink;
2825 break;
2828 break;
2830 case dir_down:
2831 slinkindex = Int_val (Field (dir_v, 0));
2832 found = &page->slinks[slinkindex];
2833 for (i = slinkindex + 1; i < page->slinkcount; ++i) {
2834 slink = &page->slinks[i];
2835 if (slink->bbox.y0 >= found->bbox.y0) {
2836 found = slink;
2837 break;
2840 break;
2842 case dir_up:
2843 slinkindex = Int_val (Field (dir_v, 0));
2844 found = &page->slinks[slinkindex];
2845 for (i = slinkindex - 1; i >= 0; --i) {
2846 slink = &page->slinks[i];
2847 if (slink->bbox.y0 <= found->bbox.y0) {
2848 found = slink;
2849 break;
2852 break;
2855 else {
2856 dirtag = Int_val (dir_v);
2857 switch (dirtag) {
2858 case dir_first:
2859 found = page->slinks;
2860 break;
2862 case dir_last:
2863 if (page->slinks) {
2864 found = page->slinks + (page->slinkcount - 1);
2866 break;
2869 if (found) {
2870 ret_v = caml_alloc_small (2, 1);
2871 Field (ret_v, 0) = Val_int (found - page->slinks);
2874 unlock (__func__);
2875 CAMLreturn (ret_v);
2878 enum { uuri, utext, uannot };
2880 CAMLprim value ml_getlink (value ptr_v, value n_v)
2882 CAMLparam2 (ptr_v, n_v);
2883 CAMLlocal4 (ret_v, tup_v, str_v, gr_v);
2884 fz_link *link;
2885 struct page *page;
2886 char *s = String_val (ptr_v);
2887 struct slink *slink;
2889 ret_v = Val_int (0);
2890 page = parse_pointer (__func__, s);
2892 lock (__func__);
2893 ensureslinks (page);
2894 slink = &page->slinks[Int_val (n_v)];
2895 if (slink->tag == SLINK) {
2896 link = slink->u.link;
2897 str_v = caml_copy_string (link->uri);
2898 ret_v = caml_alloc_small (1, uuri);
2899 Field (ret_v, 0) = str_v;
2901 else {
2902 ret_v = caml_alloc_small (1, uannot);
2903 tup_v = caml_alloc_tuple (2);
2904 Field (ret_v, 0) = tup_v;
2905 Field (tup_v, 0) = ptr_v;
2906 Field (tup_v, 1) = n_v;
2908 unlock (__func__);
2910 CAMLreturn (ret_v);
2913 CAMLprim value ml_getannotcontents (value ptr_v, value n_v)
2915 CAMLparam2 (ptr_v, n_v);
2916 pdf_document *pdf;
2917 const char *contents = "";
2919 lock (__func__);
2920 pdf = pdf_specifics (state.ctx, state.doc);
2921 if (pdf) {
2922 char *s = String_val (ptr_v);
2923 struct page *page;
2924 struct slink *slink;
2926 page = parse_pointer (__func__, s);
2927 slink = &page->slinks[Int_val (n_v)];
2928 contents = pdf_annot_contents (state.ctx,
2929 (pdf_annot *) slink->u.annot);
2931 unlock (__func__);
2932 CAMLreturn (caml_copy_string (contents));
2935 CAMLprim value ml_getlinkcount (value ptr_v)
2937 CAMLparam1 (ptr_v);
2938 struct page *page;
2939 char *s = String_val (ptr_v);
2941 page = parse_pointer (__func__, s);
2942 CAMLreturn (Val_int (page->slinkcount));
2945 CAMLprim value ml_getlinkrect (value ptr_v, value n_v)
2947 CAMLparam2 (ptr_v, n_v);
2948 CAMLlocal1 (ret_v);
2949 struct page *page;
2950 struct slink *slink;
2951 char *s = String_val (ptr_v);
2953 page = parse_pointer (__func__, s);
2954 ret_v = caml_alloc_tuple (4);
2955 lock (__func__);
2956 ensureslinks (page);
2958 slink = &page->slinks[Int_val (n_v)];
2959 Field (ret_v, 0) = Val_int (slink->bbox.x0);
2960 Field (ret_v, 1) = Val_int (slink->bbox.y0);
2961 Field (ret_v, 2) = Val_int (slink->bbox.x1);
2962 Field (ret_v, 3) = Val_int (slink->bbox.y1);
2963 unlock (__func__);
2964 CAMLreturn (ret_v);
2967 CAMLprim value ml_whatsunder (value ptr_v, value x_v, value y_v)
2969 CAMLparam3 (ptr_v, x_v, y_v);
2970 CAMLlocal4 (ret_v, tup_v, str_v, gr_v);
2971 fz_link *link;
2972 struct annot *annot;
2973 struct page *page;
2974 char *ptr = String_val (ptr_v);
2975 int x = Int_val (x_v), y = Int_val (y_v);
2976 struct pagedim *pdim;
2978 ret_v = Val_int (0);
2979 if (trylock (__func__)) {
2980 goto done;
2983 page = parse_pointer (__func__, ptr);
2984 pdim = &state.pagedims[page->pdimno];
2985 x += pdim->bounds.x0;
2986 y += pdim->bounds.y0;
2989 annot = getannot (page, x, y);
2990 if (annot) {
2991 int i, n = -1;
2993 ensureslinks (page);
2994 for (i = 0; i < page->slinkcount; ++i) {
2995 if (page->slinks[i].tag == SANNOT
2996 && page->slinks[i].u.annot == annot->annot) {
2997 n = i;
2998 break;
3001 ret_v = caml_alloc_small (1, uannot);
3002 tup_v = caml_alloc_tuple (2);
3003 Field (ret_v, 0) = tup_v;
3004 Field (tup_v, 0) = ptr_v;
3005 Field (tup_v, 1) = Val_int (n);
3006 goto unlock;
3010 link = getlink (page, x, y);
3011 if (link) {
3012 str_v = caml_copy_string (link->uri);
3013 ret_v = caml_alloc_small (1, uuri);
3014 Field (ret_v, 0) = str_v;
3016 else {
3017 fz_rect *b;
3018 fz_page_block *pageb;
3019 fz_stext_block *block;
3021 ensuretext (page);
3022 for (pageb = page->text->blocks;
3023 pageb < page->text->blocks + page->text->len;
3024 ++pageb) {
3025 fz_stext_line *line;
3026 if (pageb->type != FZ_PAGE_BLOCK_TEXT) continue;
3027 block = pageb->u.text;
3029 b = &block->bbox;
3030 if (!(x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1))
3031 continue;
3033 for (line = block->lines;
3034 line < block->lines + block->len;
3035 ++line) {
3036 fz_stext_span *span;
3038 b = &line->bbox;
3039 if (!(x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1))
3040 continue;
3042 for (span = line->first_span; span; span = span->next) {
3043 int charnum;
3045 b = &span->bbox;
3046 if (!(x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1))
3047 continue;
3049 for (charnum = 0; charnum < span->len; ++charnum) {
3050 fz_rect bbox;
3051 fz_stext_char_bbox (state.ctx, &bbox, span, charnum);
3052 b = &bbox;
3054 if (x >= b->x0 && x <= b->x1
3055 && y >= b->y0 && y <= b->y1) {
3056 fz_stext_style *style = span->text->style;
3057 const char *n2 =
3058 style->font
3059 ? fz_font_name (state.ctx, style->font)
3060 : "Span has no font name"
3062 FT_FaceRec *face = fz_font_ft_face (state.ctx,
3063 style->font);
3064 if (face && face->family_name) {
3065 char *s;
3066 char *n1 = face->family_name;
3067 size_t l1 = strlen (n1);
3068 size_t l2 = strlen (n2);
3070 if (l1 != l2 || memcmp (n1, n2, l1)) {
3071 s = malloc (l1 + l2 + 2);
3072 if (s) {
3073 memcpy (s, n2, l2);
3074 s[l2] = '=';
3075 memcpy (s + l2 + 1, n1, l1 + 1);
3076 str_v = caml_copy_string (s);
3077 free (s);
3081 if (str_v == Val_unit) {
3082 str_v = caml_copy_string (n2);
3084 ret_v = caml_alloc_small (1, utext);
3085 Field (ret_v, 0) = str_v;
3086 goto unlock;
3093 unlock:
3094 unlock (__func__);
3096 done:
3097 CAMLreturn (ret_v);
3100 enum { mark_page, mark_block, mark_line, mark_word };
3102 static int uninteresting (int c)
3104 return c == ' ' || c == '\n' || c == '\t' || c == '\n' || c == '\r'
3105 || ispunct (c);
3108 CAMLprim void ml_clearmark (value ptr_v)
3110 CAMLparam1 (ptr_v);
3111 char *s = String_val (ptr_v);
3112 struct page *page;
3114 if (trylock (__func__)) {
3115 goto done;
3118 page = parse_pointer (__func__, s);
3119 page->fmark.span = NULL;
3120 page->lmark.span = NULL;
3121 page->fmark.i = 0;
3122 page->lmark.i = 0;
3124 unlock (__func__);
3125 done:
3126 CAMLreturn0;
3129 CAMLprim value ml_markunder (value ptr_v, value x_v, value y_v, value mark_v)
3131 CAMLparam4 (ptr_v, x_v, y_v, mark_v);
3132 CAMLlocal1 (ret_v);
3133 fz_rect *b;
3134 struct page *page;
3135 fz_stext_line *line;
3136 fz_page_block *pageb;
3137 fz_stext_block *block;
3138 struct pagedim *pdim;
3139 int mark = Int_val (mark_v);
3140 char *s = String_val (ptr_v);
3141 int x = Int_val (x_v), y = Int_val (y_v);
3143 ret_v = Val_bool (0);
3144 if (trylock (__func__)) {
3145 goto done;
3148 page = parse_pointer (__func__, s);
3149 pdim = &state.pagedims[page->pdimno];
3151 ensuretext (page);
3153 if (mark == mark_page) {
3154 int i;
3155 fz_page_block *pb1 = NULL, *pb2 = NULL;
3157 for (i = 0; i < page->text->len; ++i) {
3158 if (page->text->blocks[i].type == FZ_PAGE_BLOCK_TEXT) {
3159 pb1 = &page->text->blocks[i];
3160 break;
3163 if (!pb1) goto unlock;
3165 for (i = page->text->len - 1; i >= 0; --i) {
3166 if (page->text->blocks[i].type == FZ_PAGE_BLOCK_TEXT) {
3167 pb2 = &page->text->blocks[i];
3168 break;
3171 if (!pb2) goto unlock;
3173 block = pb1->u.text;
3175 page->fmark.i = 0;
3176 page->fmark.span = block->lines->first_span;
3178 block = pb2->u.text;
3179 line = &block->lines[block->len - 1];
3180 page->lmark.i = line->last_span->len - 1;
3181 page->lmark.span = line->last_span;
3182 ret_v = Val_bool (1);
3183 goto unlock;
3186 x += pdim->bounds.x0;
3187 y += pdim->bounds.y0;
3189 for (pageb = page->text->blocks;
3190 pageb < page->text->blocks + page->text->len;
3191 ++pageb) {
3192 if (pageb->type != FZ_PAGE_BLOCK_TEXT) continue;
3193 block = pageb->u.text;
3195 b = &block->bbox;
3196 if (!(x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1))
3197 continue;
3199 if (mark == mark_block) {
3200 page->fmark.i = 0;
3201 page->fmark.span = block->lines->first_span;
3203 line = &block->lines[block->len - 1];
3204 page->lmark.i = line->last_span->len - 1;
3205 page->lmark.span = line->last_span;
3206 ret_v = Val_bool (1);
3207 goto unlock;
3210 for (line = block->lines;
3211 line < block->lines + block->len;
3212 ++line) {
3213 fz_stext_span *span;
3215 b = &line->bbox;
3216 if (!(x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1))
3217 continue;
3219 if (mark == mark_line) {
3220 page->fmark.i = 0;
3221 page->fmark.span = line->first_span;
3223 page->lmark.i = line->last_span->len - 1;
3224 page->lmark.span = line->last_span;
3225 ret_v = Val_bool (1);
3226 goto unlock;
3229 for (span = line->first_span; span; span = span->next) {
3230 int charnum;
3232 b = &span->bbox;
3233 if (!(x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1))
3234 continue;
3236 for (charnum = 0; charnum < span->len; ++charnum) {
3237 fz_rect bbox;
3238 fz_stext_char_bbox (state.ctx, &bbox, span, charnum);
3239 b = &bbox;
3241 if (x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1) {
3242 /* unicode ftw */
3243 int charnum2, charnum3 = -1, charnum4 = -1;
3245 if (uninteresting (span->text[charnum].c)) goto unlock;
3247 for (charnum2 = charnum; charnum2 >= 0; --charnum2) {
3248 if (uninteresting (span->text[charnum2].c)) {
3249 charnum3 = charnum2 + 1;
3250 break;
3253 if (charnum3 == -1) charnum3 = 0;
3255 charnum4 = charnum;
3256 for (charnum2 = charnum + 1;
3257 charnum2 < span->len;
3258 ++charnum2) {
3259 if (uninteresting (span->text[charnum2].c)) break;
3260 charnum4 = charnum2;
3263 page->fmark.i = charnum3;
3264 page->fmark.span = span;
3266 page->lmark.i = charnum4;
3267 page->lmark.span = span;
3268 ret_v = Val_bool (1);
3269 goto unlock;
3275 unlock:
3276 if (!Bool_val (ret_v)) {
3277 page->fmark.span = NULL;
3278 page->lmark.span = NULL;
3279 page->fmark.i = 0;
3280 page->lmark.i = 0;
3282 unlock (__func__);
3284 done:
3285 CAMLreturn (ret_v);
3288 CAMLprim value ml_rectofblock (value ptr_v, value x_v, value y_v)
3290 CAMLparam3 (ptr_v, x_v, y_v);
3291 CAMLlocal2 (ret_v, res_v);
3292 fz_rect *b = NULL;
3293 struct page *page;
3294 fz_page_block *pageb;
3295 struct pagedim *pdim;
3296 char *s = String_val (ptr_v);
3297 int x = Int_val (x_v), y = Int_val (y_v);
3299 ret_v = Val_int (0);
3300 if (trylock (__func__)) {
3301 goto done;
3304 page = parse_pointer (__func__, s);
3305 pdim = &state.pagedims[page->pdimno];
3306 x += pdim->bounds.x0;
3307 y += pdim->bounds.y0;
3309 ensuretext (page);
3311 for (pageb = page->text->blocks;
3312 pageb < page->text->blocks + page->text->len;
3313 ++pageb) {
3314 switch (pageb->type) {
3315 case FZ_PAGE_BLOCK_TEXT:
3316 b = &pageb->u.text->bbox;
3317 break;
3319 case FZ_PAGE_BLOCK_IMAGE:
3320 b = &pageb->u.image->bbox;
3321 break;
3323 default:
3324 continue;
3327 if (x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1)
3328 break;
3329 b = NULL;
3331 if (b) {
3332 res_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
3333 ret_v = caml_alloc_small (1, 1);
3334 Store_double_field (res_v, 0, b->x0);
3335 Store_double_field (res_v, 1, b->x1);
3336 Store_double_field (res_v, 2, b->y0);
3337 Store_double_field (res_v, 3, b->y1);
3338 Field (ret_v, 0) = res_v;
3340 unlock (__func__);
3342 done:
3343 CAMLreturn (ret_v);
3346 CAMLprim void ml_seltext (value ptr_v, value rect_v)
3348 CAMLparam2 (ptr_v, rect_v);
3349 fz_rect b;
3350 struct page *page;
3351 struct pagedim *pdim;
3352 char *s = String_val (ptr_v);
3353 int i, x0, x1, y0, y1, fi, li;
3354 fz_stext_line *line;
3355 fz_page_block *pageb;
3356 fz_stext_block *block;
3357 fz_stext_span *span, *fspan, *lspan;
3359 if (trylock (__func__)) {
3360 goto done;
3363 page = parse_pointer (__func__, s);
3364 ensuretext (page);
3366 pdim = &state.pagedims[page->pdimno];
3367 x0 = Int_val (Field (rect_v, 0)) + pdim->bounds.x0;
3368 y0 = Int_val (Field (rect_v, 1)) + pdim->bounds.y0;
3369 x1 = Int_val (Field (rect_v, 2)) + pdim->bounds.x0;
3370 y1 = Int_val (Field (rect_v, 3)) + pdim->bounds.y0;
3372 if (y0 > y1) {
3373 int t = y0;
3374 y0 = y1;
3375 y1 = t;
3376 x0 = x1;
3377 x1 = t;
3380 fi = page->fmark.i;
3381 fspan = page->fmark.span;
3383 li = page->lmark.i;
3384 lspan = page->lmark.span;
3386 for (pageb = page->text->blocks;
3387 pageb < page->text->blocks + page->text->len;
3388 ++pageb) {
3389 if (pageb->type != FZ_PAGE_BLOCK_TEXT) continue;
3390 block = pageb->u.text;
3391 for (line = block->lines;
3392 line < block->lines + block->len;
3393 ++line) {
3395 for (span = line->first_span; span; span = span->next) {
3396 for (i = 0; i < span->len; ++i) {
3397 fz_stext_char_bbox (state.ctx, &b, span, i);
3399 if (x0 >= b.x0 && x0 <= b.x1
3400 && y0 >= b.y0 && y0 <= b.y1) {
3401 fspan = span;
3402 fi = i;
3404 if (x1 >= b.x0 && x1 <= b.x1
3405 && y1 >= b.y0 && y1 <= b.y1) {
3406 lspan = span;
3407 li = i;
3413 if (x1 < x0 && fspan == lspan) {
3414 i = fi;
3415 span = fspan;
3417 fi = li;
3418 fspan = lspan;
3420 li = i;
3421 lspan = span;
3424 page->fmark.i = fi;
3425 page->fmark.span = fspan;
3427 page->lmark.i = li;
3428 page->lmark.span = lspan;
3430 unlock (__func__);
3432 done:
3433 CAMLreturn0;
3436 static int UNUSED_ATTR pipespan (FILE *f, fz_stext_span *span, int a, int b)
3438 char buf[4];
3439 int i, len, ret;
3441 for (i = a; i <= b; ++i) {
3442 len = fz_runetochar (buf, span->text[i].c);
3443 ret = fwrite (buf, len, 1, f);
3445 if (ret != 1) {
3446 fprintf (stderr, "failed to write %d bytes ret=%d: %s\n",
3447 len, ret, strerror (errno));
3448 return -1;
3451 return 0;
3454 #ifdef __CYGWIN__
3455 CAMLprim value ml_spawn (value UNUSED_ATTR u1, value UNUSED_ATTR u2)
3457 caml_failwith ("ml_popen not implemented under Cygwin");
3459 #else
3460 CAMLprim value ml_spawn (value command_v, value fds_v)
3462 CAMLparam2 (command_v, fds_v);
3463 CAMLlocal2 (l_v, tup_v);
3464 int ret, ret1;
3465 pid_t pid;
3466 char *msg = NULL;
3467 value earg_v = Nothing;
3468 posix_spawnattr_t attr;
3469 posix_spawn_file_actions_t fa;
3470 char *argv[] = { "/bin/sh", "-c", NULL, NULL };
3472 argv[2] = String_val (command_v);
3474 if ((ret = posix_spawn_file_actions_init (&fa)) != 0) {
3475 unix_error (ret, "posix_spawn_file_actions_init", Nothing);
3478 if ((ret = posix_spawnattr_init (&attr)) != 0) {
3479 msg = "posix_spawnattr_init";
3480 goto fail1;
3483 #ifdef POSIX_SPAWN_USEVFORK
3484 if ((ret = posix_spawnattr_setflags (&attr, POSIX_SPAWN_USEVFORK)) != 0) {
3485 msg = "posix_spawnattr_setflags POSIX_SPAWN_USEVFORK";
3486 goto fail;
3488 #endif
3490 for (l_v = fds_v; l_v != Val_int (0); l_v = Field (l_v, 1)) {
3491 int fd1, fd2;
3493 tup_v = Field (l_v, 0);
3494 fd1 = Int_val (Field (tup_v, 0));
3495 fd2 = Int_val (Field (tup_v, 1));
3496 if (fd2 < 0) {
3497 if ((ret = posix_spawn_file_actions_addclose (&fa, fd1)) != 0) {
3498 msg = "posix_spawn_file_actions_addclose";
3499 earg_v = tup_v;
3500 goto fail;
3503 else {
3504 if ((ret = posix_spawn_file_actions_adddup2 (&fa, fd1, fd2)) != 0) {
3505 msg = "posix_spawn_file_actions_adddup2";
3506 earg_v = tup_v;
3507 goto fail;
3512 if ((ret = posix_spawn (&pid, "/bin/sh", &fa, &attr, argv, environ))) {
3513 msg = "posix_spawn";
3514 goto fail;
3517 fail:
3518 if ((ret1 = posix_spawnattr_destroy (&attr)) != 0) {
3519 fprintf (stderr, "posix_spawnattr_destroy: %s\n", strerror (ret1));
3522 fail1:
3523 if ((ret1 = posix_spawn_file_actions_destroy (&fa)) != 0) {
3524 fprintf (stderr, "posix_spawn_file_actions_destroy: %s\n",
3525 strerror (ret1));
3528 if (msg)
3529 unix_error (ret, msg, earg_v);
3531 CAMLreturn (Val_int (pid));
3533 #endif
3535 CAMLprim value ml_hassel (value ptr_v)
3537 CAMLparam1 (ptr_v);
3538 CAMLlocal1 (ret_v);
3539 struct page *page;
3540 char *s = String_val (ptr_v);
3542 ret_v = Val_bool (0);
3543 if (trylock (__func__)) {
3544 goto done;
3547 page = parse_pointer (__func__, s);
3548 ret_v = Val_bool (page->fmark.span && page->lmark.span);
3549 unlock (__func__);
3550 done:
3551 CAMLreturn (ret_v);
3554 CAMLprim void ml_copysel (value fd_v, value ptr_v)
3556 CAMLparam2 (fd_v, ptr_v);
3557 FILE *f;
3558 int seen = 0;
3559 struct page *page;
3560 fz_stext_line *line;
3561 fz_page_block *pageb;
3562 fz_stext_block *block;
3563 int fd = Int_val (fd_v);
3564 char *s = String_val (ptr_v);
3566 if (trylock (__func__)) {
3567 goto done;
3570 page = parse_pointer (__func__, s);
3572 if (!page->fmark.span || !page->lmark.span) {
3573 fprintf (stderr, "nothing to copy on page %d\n", page->pageno);
3574 goto unlock;
3577 f = fdopen (fd, "w");
3578 if (!f) {
3579 fprintf (stderr, "failed to fdopen sel pipe (from fd %d): %s\n",
3580 fd, strerror (errno));
3581 f = stdout;
3584 for (pageb = page->text->blocks;
3585 pageb < page->text->blocks + page->text->len;
3586 ++pageb) {
3587 if (pageb->type != FZ_PAGE_BLOCK_TEXT) continue;
3588 block = pageb->u.text;
3589 for (line = block->lines;
3590 line < block->lines + block->len;
3591 ++line) {
3592 fz_stext_span *span;
3594 for (span = line->first_span; span; span = span->next) {
3595 int a, b;
3597 seen |= span == page->fmark.span || span == page->lmark.span;
3598 a = span == page->fmark.span ? page->fmark.i : 0;
3599 b = span == page->lmark.span ? page->lmark.i : span->len - 1;
3601 if (seen) {
3602 if (pipespan (f, span, a, b)) {
3603 goto close;
3605 if (span == page->lmark.span) {
3606 goto close;
3608 if (span == line->last_span) {
3609 if (putc ('\n', f) == EOF) {
3610 fprintf (stderr,
3611 "failed break line on sel pipe: %s\n",
3612 strerror (errno));
3613 goto close;
3620 close:
3621 if (f != stdout) {
3622 int ret = fclose (f);
3623 fd = -1;
3624 if (ret == -1) {
3625 if (errno != ECHILD) {
3626 fprintf (stderr, "failed to close sel pipe: %s\n",
3627 strerror (errno));
3631 unlock:
3632 unlock (__func__);
3634 done:
3635 if (fd >= 0) {
3636 if (close (fd)) {
3637 fprintf (stderr, "failed to close sel pipe: %s\n",
3638 strerror (errno));
3641 CAMLreturn0;
3644 CAMLprim value ml_getpdimrect (value pagedimno_v)
3646 CAMLparam1 (pagedimno_v);
3647 CAMLlocal1 (ret_v);
3648 int pagedimno = Int_val (pagedimno_v);
3649 fz_rect box;
3651 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
3652 if (trylock (__func__)) {
3653 box = fz_empty_rect;
3655 else {
3656 box = state.pagedims[pagedimno].mediabox;
3657 unlock (__func__);
3660 Store_double_field (ret_v, 0, box.x0);
3661 Store_double_field (ret_v, 1, box.x1);
3662 Store_double_field (ret_v, 2, box.y0);
3663 Store_double_field (ret_v, 3, box.y1);
3665 CAMLreturn (ret_v);
3668 CAMLprim value ml_zoom_for_height (value winw_v, value winh_v,
3669 value dw_v, value cols_v)
3671 CAMLparam4 (winw_v, winh_v, dw_v, cols_v);
3672 CAMLlocal1 (ret_v);
3673 int i;
3674 double zoom = -1.;
3675 double maxh = 0.0;
3676 struct pagedim *p;
3677 double winw = Int_val (winw_v);
3678 double winh = Int_val (winh_v);
3679 double dw = Int_val (dw_v);
3680 double cols = Int_val (cols_v);
3681 double pw = 1.0, ph = 1.0;
3683 if (trylock (__func__)) {
3684 goto done;
3687 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
3688 double w = p->pagebox.x1 / cols;
3689 double h = p->pagebox.y1;
3690 if (h > maxh) {
3691 maxh = h;
3692 ph = h;
3693 if (state.fitmodel != FitProportional) pw = w;
3695 if ((state.fitmodel == FitProportional) && w > pw) pw = w;
3698 zoom = (((winh / ph) * pw) + dw) / winw;
3699 unlock (__func__);
3700 done:
3701 ret_v = caml_copy_double (zoom);
3702 CAMLreturn (ret_v);
3705 CAMLprim value ml_getmaxw (value unit_v)
3707 CAMLparam1 (unit_v);
3708 CAMLlocal1 (ret_v);
3709 int i;
3710 double maxw = -1.;
3711 struct pagedim *p;
3713 if (trylock (__func__)) {
3714 goto done;
3717 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
3718 double w = p->pagebox.x1;
3719 maxw = fz_max (maxw, w);
3722 unlock (__func__);
3723 done:
3724 ret_v = caml_copy_double (maxw);
3725 CAMLreturn (ret_v);
3728 CAMLprim value ml_draw_string (value pt_v, value x_v, value y_v, value string_v)
3730 CAMLparam4 (pt_v, x_v, y_v, string_v);
3731 CAMLlocal1 (ret_v);
3732 int pt = Int_val(pt_v);
3733 int x = Int_val (x_v);
3734 int y = Int_val (y_v);
3735 double w;
3737 w = draw_string (state.face, pt, x, y, String_val (string_v));
3738 ret_v = caml_copy_double (w);
3739 CAMLreturn (ret_v);
3742 CAMLprim value ml_measure_string (value pt_v, value string_v)
3744 CAMLparam2 (pt_v, string_v);
3745 CAMLlocal1 (ret_v);
3746 int pt = Int_val (pt_v);
3747 double w;
3749 w = measure_string (state.face, pt, String_val (string_v));
3750 ret_v = caml_copy_double (w);
3751 CAMLreturn (ret_v);
3754 CAMLprim value ml_getpagebox (value opaque_v)
3756 CAMLparam1 (opaque_v);
3757 CAMLlocal1 (ret_v);
3758 fz_rect rect;
3759 fz_irect bbox;
3760 fz_matrix ctm;
3761 fz_device *dev;
3762 char *s = String_val (opaque_v);
3763 struct page *page = parse_pointer (__func__, s);
3765 ret_v = caml_alloc_tuple (4);
3766 dev = fz_new_bbox_device (state.ctx, &rect);
3767 dev->hints |= FZ_IGNORE_SHADE;
3769 ctm = pagectm (page);
3770 fz_run_page (state.ctx, page->fzpage, dev, &ctm, NULL);
3772 fz_close_device (state.ctx, dev);
3773 fz_drop_device (state.ctx, dev);
3774 fz_round_rect (&bbox, &rect);
3775 Field (ret_v, 0) = Val_int (bbox.x0);
3776 Field (ret_v, 1) = Val_int (bbox.y0);
3777 Field (ret_v, 2) = Val_int (bbox.x1);
3778 Field (ret_v, 3) = Val_int (bbox.y1);
3780 CAMLreturn (ret_v);
3783 CAMLprim void ml_setaalevel (value level_v)
3785 CAMLparam1 (level_v);
3787 state.aalevel = Int_val (level_v);
3788 CAMLreturn0;
3791 #pragma GCC diagnostic push
3792 #pragma GCC diagnostic ignored "-Wvariadic-macros"
3793 #include <X11/Xlib.h>
3794 #include <X11/cursorfont.h>
3795 #pragma GCC diagnostic pop
3797 #ifdef USE_EGL
3798 #include <EGL/egl.h>
3799 #else
3800 #include <GL/glx.h>
3801 #endif
3803 static const int shapes[] = {
3804 XC_left_ptr, XC_hand2, XC_exchange, XC_fleur, XC_xterm
3807 #define CURS_COUNT (sizeof (shapes) / sizeof (shapes[0]))
3809 static struct {
3810 Window wid;
3811 Display *dpy;
3812 #ifdef USE_EGL
3813 EGLContext ctx;
3814 EGLConfig conf;
3815 EGLSurface win;
3816 EGLDisplay *edpy;
3817 #else
3818 GLXContext ctx;
3819 #endif
3820 XVisualInfo *visual;
3821 Cursor curs[CURS_COUNT];
3822 } glx;
3825 static void initcurs (void)
3827 for (size_t n = 0; n < CURS_COUNT; ++n) {
3828 glx.curs[n] = XCreateFontCursor (glx.dpy, shapes[n]);
3832 CAMLprim void ml_setbgcol (value color_v)
3834 CAMLparam1 (color_v);
3835 XSetWindowBackground (glx.dpy, glx.wid, Int_val (color_v));
3836 CAMLreturn0;
3839 #ifdef USE_EGL
3840 CAMLprim value ml_glxinit (value display_v, value wid_v, value screen_v)
3842 CAMLparam3 (display_v, wid_v, screen_v);
3843 int major, minor;
3844 int num_conf;
3845 EGLint visid;
3846 EGLint attribs[] = {
3847 EGL_DEPTH_SIZE, 24,
3848 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
3849 EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
3850 EGL_NONE
3852 EGLConfig conf;
3854 glx.dpy = XOpenDisplay (String_val (display_v));
3855 if (!glx.dpy) {
3856 caml_failwith ("XOpenDisplay");
3859 eglBindAPI (EGL_OPENGL_API);
3861 glx.edpy = eglGetDisplay (glx.dpy);
3862 if (glx.edpy == EGL_NO_DISPLAY) {
3863 caml_failwith ("eglGetDisplay");
3866 if (!eglInitialize (glx.edpy, &major, &minor)) {
3867 caml_failwith ("eglInitialize");
3870 if (!eglChooseConfig (glx.edpy, attribs, &conf, 1, &num_conf) ||
3871 !num_conf) {
3872 caml_failwith ("eglChooseConfig");
3875 if (!eglGetConfigAttrib (glx.edpy, conf, EGL_NATIVE_VISUAL_ID, &visid)) {
3876 caml_failwith ("eglGetConfigAttrib");
3879 glx.conf = conf;
3880 initcurs ();
3882 glx.wid = Int_val (wid_v);
3883 CAMLreturn (Val_int (visid));
3886 CAMLprim value ml_glxcompleteinit (value unit_v)
3888 CAMLparam1 (unit_v);
3890 glx.ctx = eglCreateContext (glx.edpy, glx.conf, EGL_NO_CONTEXT, NULL);
3891 if (!glx.ctx) {
3892 caml_failwith ("eglCreateContext");
3895 glx.win = eglCreateWindowSurface (glx.edpy, glx.conf,
3896 glx.wid, NULL);
3897 if (glx.win == EGL_NO_SURFACE) {
3898 caml_failwith ("eglCreateWindowSurface");
3901 XFree (glx.visual);
3902 if (!eglMakeCurrent (glx.edpy, glx.win, glx.win, glx.ctx)) {
3903 glx.ctx = NULL;
3904 caml_failwith ("eglMakeCurrent");
3906 CAMLreturn (unit_v);
3908 #else
3909 CAMLprim value ml_glxinit (value display_v, value wid_v, value screen_v)
3911 CAMLparam3 (display_v, wid_v, screen_v);
3913 glx.dpy = XOpenDisplay (String_val (display_v));
3914 if (!glx.dpy) {
3915 caml_failwith ("XOpenDisplay");
3918 int attribs[] = { GLX_RGBA, GLX_DOUBLEBUFFER, None };
3919 glx.visual = glXChooseVisual (glx.dpy, Int_val (screen_v), attribs);
3920 if (!glx.visual) {
3921 XCloseDisplay (glx.dpy);
3922 caml_failwith ("glXChooseVisual");
3925 initcurs ();
3927 glx.wid = Int_val (wid_v);
3928 CAMLreturn (Val_int (glx.visual->visualid));
3931 CAMLprim value ml_glxcompleteinit (value unit_v)
3933 CAMLparam1 (unit_v);
3935 glx.ctx = glXCreateContext (glx.dpy, glx.visual, NULL, True);
3936 if (!glx.ctx) {
3937 caml_failwith ("glXCreateContext");
3940 XFree (glx.visual);
3941 glx.visual = NULL;
3943 if (!glXMakeCurrent (glx.dpy, glx.wid, glx.ctx)) {
3944 glXDestroyContext (glx.dpy, glx.ctx);
3945 glx.ctx = NULL;
3946 caml_failwith ("glXMakeCurrent");
3948 CAMLreturn (unit_v);
3950 #endif
3952 CAMLprim void ml_setcursor (value cursor_v)
3954 CAMLparam1 (cursor_v);
3955 size_t cursn = Int_val (cursor_v);
3957 if (cursn >= CURS_COUNT) caml_failwith ("cursor index out of range");
3958 XDefineCursor (glx.dpy, glx.wid, glx.curs[cursn]);
3959 XFlush (glx.dpy);
3960 CAMLreturn0;
3963 CAMLprim value ml_swapb (value unit_v)
3965 CAMLparam1 (unit_v);
3966 #ifdef USE_EGL
3967 if (!eglSwapBuffers (glx.edpy, glx.win)) {
3968 caml_failwith ("eglSwapBuffers");
3970 #else
3971 glXSwapBuffers (glx.dpy, glx.wid);
3972 #endif
3973 CAMLreturn (unit_v);
3976 #include "keysym2ucs.c"
3978 CAMLprim value ml_keysymtoutf8 (value keysym_v)
3980 CAMLparam1 (keysym_v);
3981 CAMLlocal1 (str_v);
3982 KeySym keysym = Int_val (keysym_v);
3983 Rune rune;
3984 int len;
3985 char buf[5];
3987 rune = keysym2ucs (keysym);
3988 len = fz_runetochar (buf, rune);
3989 buf[len] = 0;
3990 str_v = caml_copy_string (buf);
3991 CAMLreturn (str_v);
3994 enum { piunknown, pilinux, piosx, pisun, pibsd, picygwin };
3996 CAMLprim value ml_platform (value unit_v)
3998 CAMLparam1 (unit_v);
3999 CAMLlocal2 (tup_v, arr_v);
4000 int platid = piunknown;
4001 struct utsname buf;
4003 #if defined __linux__
4004 platid = pilinux;
4005 #elif defined __CYGWIN__
4006 platid = picygwin;
4007 #elif defined __DragonFly__ || defined __FreeBSD__
4008 || defined __OpenBSD__ || defined __NetBSD__
4009 platid = pibsd;
4010 #elif defined __sun__
4011 platid = pisun;
4012 #elif defined __APPLE__
4013 platid = piosx;
4014 #endif
4015 if (uname (&buf)) err (1, "uname");
4017 tup_v = caml_alloc_tuple (2);
4019 char const *sar[] = {
4020 buf.sysname,
4021 buf.release,
4022 buf.version,
4023 buf.machine,
4024 NULL
4026 arr_v = caml_copy_string_array (sar);
4028 Field (tup_v, 0) = Val_int (platid);
4029 Field (tup_v, 1) = arr_v;
4030 CAMLreturn (tup_v);
4033 CAMLprim void ml_cloexec (value fd_v)
4035 CAMLparam1 (fd_v);
4036 int fd = Int_val (fd_v);
4038 if (fcntl (fd, F_SETFD, FD_CLOEXEC, 1)) {
4039 uerror ("fcntl", Nothing);
4041 CAMLreturn0;
4044 CAMLprim value ml_getpbo (value w_v, value h_v, value cs_v)
4046 CAMLparam2 (w_v, h_v);
4047 CAMLlocal1 (ret_v);
4048 struct bo *pbo;
4049 int w = Int_val (w_v);
4050 int h = Int_val (h_v);
4051 int cs = Int_val (cs_v);
4053 if (state.bo_usable) {
4054 pbo = calloc (sizeof (*pbo), 1);
4055 if (!pbo) {
4056 err (1, "calloc pbo");
4059 switch (cs) {
4060 case 0:
4061 case 1:
4062 pbo->size = w*h*4;
4063 break;
4064 case 2:
4065 pbo->size = w*h*2;
4066 break;
4067 default:
4068 errx (1, "%s: invalid colorspace %d", __func__, cs);
4071 state.glGenBuffersARB (1, &pbo->id);
4072 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, pbo->id);
4073 state.glBufferDataARB (GL_PIXEL_UNPACK_BUFFER_ARB, pbo->size,
4074 NULL, GL_STREAM_DRAW);
4075 pbo->ptr = state.glMapBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB,
4076 GL_READ_WRITE);
4077 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, 0);
4078 if (!pbo->ptr) {
4079 fprintf (stderr, "glMapBufferARB failed: %#x\n", glGetError ());
4080 state.glDeleteBuffersARB (1, &pbo->id);
4081 free (pbo);
4082 ret_v = caml_copy_string ("0");
4084 else {
4085 int res;
4086 char *s;
4088 res = snprintf (NULL, 0, "%" FMT_ptr, FMT_ptr_cast (pbo));
4089 if (res < 0) {
4090 err (1, "snprintf %" FMT_ptr " failed", FMT_ptr_cast (pbo));
4092 s = malloc (res+1);
4093 if (!s) {
4094 err (1, "malloc %d bytes failed", res+1);
4096 res = sprintf (s, "%" FMT_ptr, FMT_ptr_cast (pbo));
4097 if (res < 0) {
4098 err (1, "sprintf %" FMT_ptr " failed", FMT_ptr_cast (pbo));
4100 ret_v = caml_copy_string (s);
4101 free (s);
4104 else {
4105 ret_v = caml_copy_string ("0");
4107 CAMLreturn (ret_v);
4110 CAMLprim void ml_freepbo (value s_v)
4112 CAMLparam1 (s_v);
4113 char *s = String_val (s_v);
4114 struct tile *tile = parse_pointer (__func__, s);
4116 if (tile->pbo) {
4117 state.glDeleteBuffersARB (1, &tile->pbo->id);
4118 tile->pbo->id = -1;
4119 tile->pbo->ptr = NULL;
4120 tile->pbo->size = -1;
4122 CAMLreturn0;
4125 CAMLprim void ml_unmappbo (value s_v)
4127 CAMLparam1 (s_v);
4128 char *s = String_val (s_v);
4129 struct tile *tile = parse_pointer (__func__, s);
4131 if (tile->pbo) {
4132 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, tile->pbo->id);
4133 if (state.glUnmapBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB) == GL_FALSE) {
4134 errx (1, "glUnmapBufferARB failed: %#x\n", glGetError ());
4136 tile->pbo->ptr = NULL;
4137 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, 0);
4139 CAMLreturn0;
4142 static void setuppbo (void)
4144 #ifdef USE_EGL
4145 #define GGPA(n) (*(void (**) ()) &state.n = eglGetProcAddress (#n))
4146 #else
4147 #define GGPA(n) (*(void (**) ()) &state.n = glXGetProcAddress ((GLubyte *) #n))
4148 #endif
4149 state.bo_usable = GGPA (glBindBufferARB)
4150 && GGPA (glUnmapBufferARB)
4151 && GGPA (glMapBufferARB)
4152 && GGPA (glBufferDataARB)
4153 && GGPA (glGenBuffersARB)
4154 && GGPA (glDeleteBuffersARB);
4155 #undef GGPA
4158 CAMLprim value ml_bo_usable (value unit_v)
4160 CAMLparam1 (unit_v);
4161 CAMLreturn (Val_bool (state.bo_usable));
4164 CAMLprim value ml_unproject (value ptr_v, value x_v, value y_v)
4166 CAMLparam3 (ptr_v, x_v, y_v);
4167 CAMLlocal2 (ret_v, tup_v);
4168 struct page *page;
4169 char *s = String_val (ptr_v);
4170 int x = Int_val (x_v), y = Int_val (y_v);
4171 struct pagedim *pdim;
4172 fz_point p;
4173 fz_matrix ctm;
4175 page = parse_pointer (__func__, s);
4176 pdim = &state.pagedims[page->pdimno];
4178 ret_v = Val_int (0);
4179 if (trylock (__func__)) {
4180 goto done;
4183 if (pdf_specifics (state.ctx, state.doc)) {
4184 trimctm (pdf_page_from_fz_page (state.ctx, page->fzpage), page->pdimno);
4185 ctm = state.pagedims[page->pdimno].tctm;
4187 else {
4188 ctm = fz_identity;
4190 p.x = x + pdim->bounds.x0;
4191 p.y = y + pdim->bounds.y0;
4193 fz_concat (&ctm, &pdim->tctm, &pdim->ctm);
4194 fz_invert_matrix (&ctm, &ctm);
4195 fz_transform_point (&p, &ctm);
4197 tup_v = caml_alloc_tuple (2);
4198 ret_v = caml_alloc_small (1, 1);
4199 Field (tup_v, 0) = Val_int (p.x);
4200 Field (tup_v, 1) = Val_int (p.y);
4201 Field (ret_v, 0) = tup_v;
4203 unlock (__func__);
4204 done:
4205 CAMLreturn (ret_v);
4208 CAMLprim value ml_project (value ptr_v, value pageno_v, value pdimno_v,
4209 value x_v, value y_v)
4211 CAMLparam5 (ptr_v, pageno_v, pdimno_v, x_v, y_v);
4212 CAMLlocal1 (ret_v);
4213 struct page *page;
4214 char *s = String_val (ptr_v);
4215 int pageno = Int_val (pageno_v);
4216 int pdimno = Int_val (pdimno_v);
4217 double x = Double_val (x_v), y = Double_val (y_v);
4218 struct pagedim *pdim;
4219 fz_point p;
4220 fz_matrix ctm;
4222 ret_v = Val_int (0);
4223 lock (__func__);
4225 if (!*s) {
4226 page = loadpage (pageno, pdimno);
4228 else {
4229 page = parse_pointer (__func__, s);
4231 pdim = &state.pagedims[pdimno];
4233 if (pdf_specifics (state.ctx, state.doc)) {
4234 trimctm (pdf_page_from_fz_page (state.ctx, page->fzpage), page->pdimno);
4235 ctm = state.pagedims[page->pdimno].tctm;
4237 else {
4238 ctm = fz_identity;
4240 p.x = x + pdim->bounds.x0;
4241 p.y = y + pdim->bounds.y0;
4243 fz_concat (&ctm, &pdim->tctm, &pdim->ctm);
4244 fz_transform_point (&p, &ctm);
4246 ret_v = caml_alloc_tuple (2);
4247 Field (ret_v, 0) = caml_copy_double (p.x);
4248 Field (ret_v, 1) = caml_copy_double (p.y);
4250 if (!*s) {
4251 freepage (page);
4253 unlock (__func__);
4254 CAMLreturn (ret_v);
4257 CAMLprim void ml_addannot (value ptr_v, value x_v, value y_v,
4258 value contents_v)
4260 CAMLparam4 (ptr_v, x_v, y_v, contents_v);
4261 pdf_document *pdf = pdf_specifics (state.ctx, state.doc);
4263 if (pdf) {
4264 pdf_annot *annot;
4265 struct page *page;
4266 fz_point p;
4267 char *s = String_val (ptr_v);
4269 page = parse_pointer (__func__, s);
4270 annot = pdf_create_annot (state.ctx,
4271 pdf_page_from_fz_page (state.ctx,
4272 page->fzpage),
4273 PDF_ANNOT_TEXT);
4274 p.x = Int_val (x_v);
4275 p.y = Int_val (y_v);
4276 pdf_set_annot_contents (state.ctx, annot, String_val (contents_v));
4277 pdf_set_text_annot_position (state.ctx, annot, p);
4278 state.dirty = 1;
4280 CAMLreturn0;
4283 CAMLprim void ml_delannot (value ptr_v, value n_v)
4285 CAMLparam2 (ptr_v, n_v);
4286 pdf_document *pdf = pdf_specifics (state.ctx, state.doc);
4288 if (pdf) {
4289 struct page *page;
4290 char *s = String_val (ptr_v);
4291 struct slink *slink;
4293 page = parse_pointer (__func__, s);
4294 slink = &page->slinks[Int_val (n_v)];
4295 pdf_delete_annot (state.ctx,
4296 pdf_page_from_fz_page (state.ctx, page->fzpage),
4297 (pdf_annot *) slink->u.annot);
4298 state.dirty = 1;
4300 CAMLreturn0;
4303 CAMLprim void ml_modannot (value ptr_v, value n_v, value str_v)
4305 CAMLparam3 (ptr_v, n_v, str_v);
4306 pdf_document *pdf = pdf_specifics (state.ctx, state.doc);
4308 if (pdf) {
4309 struct page *page;
4310 char *s = String_val (ptr_v);
4311 struct slink *slink;
4313 page = parse_pointer (__func__, s);
4314 slink = &page->slinks[Int_val (n_v)];
4315 pdf_set_annot_contents (state.ctx, (pdf_annot *) slink->u.annot,
4316 String_val (str_v));
4317 state.dirty = 1;
4319 CAMLreturn0;
4322 CAMLprim value ml_hasunsavedchanges (value unit_v)
4324 CAMLparam1 (unit_v);
4325 CAMLreturn (Val_bool (state.dirty));
4328 CAMLprim void ml_savedoc (value path_v)
4330 CAMLparam1 (path_v);
4331 pdf_document *pdf = pdf_specifics (state.ctx, state.doc);
4333 if (pdf) {
4334 pdf_save_document (state.ctx, pdf, String_val (path_v), NULL);
4336 CAMLreturn0;
4339 static void makestippletex (void)
4341 const char pixels[] = "\xff\xff\0\0";
4342 glGenTextures (1, &state.stid);
4343 glBindTexture (GL_TEXTURE_1D, state.stid);
4344 glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
4345 glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
4346 glTexImage1D (
4347 GL_TEXTURE_1D,
4349 GL_ALPHA,
4352 GL_ALPHA,
4353 GL_UNSIGNED_BYTE,
4354 pixels
4358 CAMLprim value ml_fz_version (value UNUSED_ATTR unit_v)
4360 return caml_copy_string (FZ_VERSION);
4363 CAMLprim void ml_init (value csock_v, value params_v)
4365 CAMLparam2 (csock_v, params_v);
4366 CAMLlocal2 (trim_v, fuzz_v);
4367 int ret;
4368 int texcount;
4369 char *fontpath;
4370 int colorspace;
4371 int mustoresize;
4372 int haspboext;
4374 state.csock = Int_val (csock_v);
4375 state.rotate = Int_val (Field (params_v, 0));
4376 state.fitmodel = Int_val (Field (params_v, 1));
4377 trim_v = Field (params_v, 2);
4378 texcount = Int_val (Field (params_v, 3));
4379 state.sliceheight = Int_val (Field (params_v, 4));
4380 mustoresize = Int_val (Field (params_v, 5));
4381 colorspace = Int_val (Field (params_v, 6));
4382 fontpath = String_val (Field (params_v, 7));
4384 if (caml_string_length (Field (params_v, 8)) > 0) {
4385 state.trimcachepath = strdup (String_val (Field (params_v, 8)));
4387 if (!state.trimcachepath) {
4388 fprintf (stderr, "failed to strdup trimcachepath: %s\n",
4389 strerror (errno));
4393 haspboext = Bool_val (Field (params_v, 9));
4395 state.ctx = fz_new_context (NULL, NULL, mustoresize);
4396 fz_register_document_handlers (state.ctx);
4398 state.trimmargins = Bool_val (Field (trim_v, 0));
4399 fuzz_v = Field (trim_v, 1);
4400 state.trimfuzz.x0 = Int_val (Field (fuzz_v, 0));
4401 state.trimfuzz.y0 = Int_val (Field (fuzz_v, 1));
4402 state.trimfuzz.x1 = Int_val (Field (fuzz_v, 2));
4403 state.trimfuzz.y1 = Int_val (Field (fuzz_v, 3));
4405 set_tex_params (colorspace);
4407 if (*fontpath) {
4408 state.face = load_font (fontpath);
4410 else {
4411 int len;
4412 const char *data = pdf_lookup_substitute_font (state.ctx, 0, 0,
4413 0, 0, &len);
4414 state.face = load_builtin_font (data, len);
4416 if (!state.face) _exit (1);
4418 realloctexts (texcount);
4420 if (haspboext) {
4421 setuppbo ();
4424 makestippletex ();
4426 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
4427 if (ret) {
4428 errx (1, "pthread_create: %s", strerror (ret));
4431 CAMLreturn0;