Save chars
[llpp.git] / link.c
blob9716a58b6cea5ba1b70d46dc3d56dda1141d07d9
1 /* lots of code c&p-ed directly from mupdf */
2 #ifdef __clang__
3 #pragma GCC diagnostic error "-Weverything"
4 #pragma GCC diagnostic ignored "-Wpadded"
5 #pragma GCC diagnostic ignored "-Wsign-conversion"
6 #pragma GCC diagnostic ignored "-Wdocumentation-unknown-command"
7 #pragma GCC diagnostic ignored "-Wdocumentation"
8 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
9 #endif
11 #pragma GCC diagnostic ignored "-Wdouble-promotion"
13 extern char **environ;
15 #define CAML_NAME_SPACE
16 #define FIXME 0
18 #include <errno.h>
19 #include <stdio.h>
20 #include <ctype.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <signal.h>
25 #include <math.h>
26 #include <wchar.h>
27 #include <locale.h>
28 #include <langinfo.h>
30 #include <unistd.h>
31 #include <pthread.h>
32 #include <sys/uio.h>
33 #include <sys/time.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36 #include <sys/types.h>
37 #include <sys/ioctl.h>
38 #include <sys/utsname.h>
40 #include <spawn.h>
42 #include <regex.h>
43 #include <stdarg.h>
44 #include <limits.h>
45 #include <inttypes.h>
47 #ifdef __COCOA__
48 #include <CoreFoundation/CoreFoundation.h>
49 #endif
51 #ifdef __APPLE__
52 #include <OpenGL/gl.h>
53 #else
54 #include <GL/gl.h>
55 #endif
57 #pragma GCC diagnostic push
58 #ifdef __clang__
59 #pragma GCC diagnostic ignored "-Wreserved-id-macro"
60 #endif
61 #pragma GCC diagnostic ignored "-Wpedantic"
62 #include <caml/fail.h>
63 #include <caml/alloc.h>
64 #include <caml/memory.h>
65 #include <caml/unixsupport.h>
67 #pragma GCC diagnostic push
68 #pragma GCC diagnostic ignored "-Wfloat-equal"
69 #include <mupdf/fitz.h>
70 #include <mupdf/pdf.h>
71 #pragma GCC diagnostic pop
73 #include <ft2build.h>
74 #include FT_FREETYPE_H
75 #pragma GCC diagnostic pop
77 #include "cutils.h"
79 #define PIGGYBACK
80 #define CACHE_PAGEREFS
82 #ifdef USE_NPOT
83 #define TEXT_TYPE GL_TEXTURE_2D
84 #else
85 #define TEXT_TYPE GL_TEXTURE_RECTANGLE_ARB
86 #endif
88 #if 0
89 #define lprintf printf
90 #else
91 #define lprintf(...)
92 #endif
94 #define ARSERT(cond) for (;;) { \
95 if (!(cond)) { \
96 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
97 } \
98 break; \
101 struct slice {
102 int h;
103 int texindex;
106 struct tile {
107 int w, h;
108 int slicecount;
109 int sliceheight;
110 struct bo *pbo;
111 fz_pixmap *pixmap;
112 struct slice slices[1];
115 struct pagedim {
116 int pageno;
117 int rotate;
118 int left;
119 int tctmready;
120 fz_irect bounds;
121 fz_rect pagebox;
122 fz_rect mediabox;
123 fz_matrix ctm, zoomctm, tctm;
126 struct slink {
127 enum { SLINK, SANNOT } tag;
128 fz_irect bbox;
129 union {
130 fz_link *link;
131 fz_annot *annot;
132 } u;
135 struct annot {
136 fz_irect bbox;
137 fz_annot *annot;
140 struct page {
141 int tgen;
142 int sgen;
143 int agen;
144 int pageno;
145 int pdimno;
146 fz_stext_page *text;
147 fz_page *fzpage;
148 fz_display_list *dlist;
149 fz_link *links;
150 int slinkcount;
151 struct slink *slinks;
152 int annotcount;
153 struct annot *annots;
154 fz_stext_char *fmark, *lmark;
157 enum { FitWidth, FitProportional, FitPage };
159 static struct {
160 int sliceheight;
161 struct pagedim *pagedims;
162 int pagecount;
163 int pagedimcount;
164 fz_document *doc;
165 fz_context *ctx;
166 int w, h;
168 int texindex;
169 int texcount;
170 GLuint *texids;
172 GLenum texiform;
173 GLenum texform;
174 GLenum texty;
176 fz_colorspace *colorspace;
178 struct {
179 int w, h;
180 struct slice *slice;
181 } *texowners;
183 int rotate;
184 int fitmodel;
185 int trimmargins;
186 int needoutline;
187 int gen;
188 int aalevel;
190 int trimanew;
191 fz_irect trimfuzz;
192 fz_pixmap *pig;
194 pthread_t thread;
195 int csock;
196 FT_Face face;
198 char *trimcachepath;
199 int dirty;
201 GLuint stid;
203 int bo_usable;
204 GLuint boid;
206 void (*glBindBufferARB) (GLenum, GLuint);
207 GLboolean (*glUnmapBufferARB) (GLenum);
208 void *(*glMapBufferARB) (GLenum, GLenum);
209 void (*glBufferDataARB) (GLenum, GLsizei, void *, GLenum);
210 void (*glGenBuffersARB) (GLsizei, GLuint *);
211 void (*glDeleteBuffersARB) (GLsizei, GLuint *);
213 GLfloat texcoords[8];
214 GLfloat vertices[16];
216 #ifdef CACHE_PAGEREFS
217 struct {
218 int idx;
219 int count;
220 pdf_obj **objs;
221 pdf_document *pdf;
222 } pdflut;
223 #endif
224 int utf8cs;
225 } state;
227 struct bo {
228 GLuint id;
229 void *ptr;
230 size_t size;
233 static void UNUSED_ATTR debug_rect (const char *cap, fz_rect r)
235 printf ("%s(rect) %.2f,%.2f,%.2f,%.2f\n", cap, r.x0, r.y0, r.x1, r.y1);
238 static void UNUSED_ATTR debug_bbox (const char *cap, fz_irect r)
240 printf ("%s(bbox) %d,%d,%d,%d\n", cap, r.x0, r.y0, r.x1, r.y1);
243 static void UNUSED_ATTR debug_matrix (const char *cap, fz_matrix m)
245 printf ("%s(matrix) %.2f,%.2f,%.2f,%.2f %.2f %.2f\n", cap,
246 m.a, m.b, m.c, m.d, m.e, m.f);
249 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
251 static void lock (const char *cap)
253 int ret = pthread_mutex_lock (&mutex);
254 if (ret) {
255 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
259 static void unlock (const char *cap)
261 int ret = pthread_mutex_unlock (&mutex);
262 if (ret) {
263 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
267 static int trylock (const char *cap)
269 int ret = pthread_mutex_trylock (&mutex);
270 if (ret && ret != EBUSY) {
271 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
273 return ret == EBUSY;
276 static int hasdata (void)
278 int ret, avail;
279 ret = ioctl (state.csock, FIONREAD, &avail);
280 if (ret) err (1, "hasdata: FIONREAD error ret=%d", ret);
281 return avail > 0;
284 CAMLprim value ml_hasdata (value fd_v)
286 CAMLparam1 (fd_v);
287 int ret, avail;
289 ret = ioctl (Int_val (fd_v), FIONREAD, &avail);
290 if (ret) uerror ("ioctl (FIONREAD)", Nothing);
291 CAMLreturn (Val_bool (avail > 0));
294 static void readdata (int fd, void *p, int size)
296 ssize_t n;
298 again:
299 n = read (fd, p, size);
300 if (n - size) {
301 if (n < 0 && errno == EINTR) goto again;
302 if (!n) errx (1, "EOF while reading");
303 errx (1, "read (fd %d, req %d, ret %zd)", fd, size, n);
307 static void writedata (int fd, char *p, int size)
309 ssize_t n;
310 uint32_t size4 = size;
311 struct iovec iov[2] = {
312 { .iov_base = &size4, .iov_len = 4 },
313 { .iov_base = p, .iov_len = size }
316 again:
317 n = writev (fd, iov, 2);
318 if (n < 0 && errno == EINTR) goto again;
319 if (n - size - 4) {
320 if (!n) errx (1, "EOF while writing data");
321 err (1, "writev (fd %d, req %d, ret %zd)", fd, size + 4, n);
325 static int readlen (int fd)
327 uint32_t u;
328 readdata (fd, &u, 4);
329 return u;
332 CAMLprim void ml_wcmd (value fd_v, value bytes_v, value len_v)
334 CAMLparam3 (fd_v, bytes_v, len_v);
335 writedata (Int_val (fd_v), &Byte (bytes_v, 0), Int_val (len_v));
336 CAMLreturn0;
339 CAMLprim value ml_rcmd (value fd_v)
341 CAMLparam1 (fd_v);
342 CAMLlocal1 (strdata_v);
343 int fd = Int_val (fd_v);
344 int len = readlen (fd);
345 strdata_v = caml_alloc_string (len);
346 readdata (fd, String_val (strdata_v), len);
347 CAMLreturn (strdata_v);
350 static void GCC_FMT_ATTR (1, 2) printd (const char *fmt, ...)
352 char fbuf[64];
353 int size = sizeof (fbuf), len;
354 va_list ap;
355 char *buf = fbuf;
357 for (;;) {
358 va_start (ap, fmt);
359 len = vsnprintf (buf, size, fmt, ap);
360 va_end (ap);
362 if (len > -1) {
363 if (len < size - 4) {
364 writedata (state.csock, buf, len);
365 break;
367 else size = len + 5;
369 else {
370 err (1, "vsnprintf for `%s' failed", fmt);
372 buf = realloc (buf == fbuf ? NULL : buf, size);
373 if (!buf) err (1, "realloc for temp buf (%d bytes) failed", size);
375 if (buf != fbuf) free (buf);
378 static void closedoc (void)
380 #ifdef CACHE_PAGEREFS
381 if (state.pdflut.objs) {
382 for (int i = 0; i < state.pdflut.count; ++i) {
383 pdf_drop_obj (state.ctx, state.pdflut.objs[i]);
385 free (state.pdflut.objs);
386 state.pdflut.objs = NULL;
387 state.pdflut.idx = 0;
389 #endif
390 if (state.doc) {
391 fz_drop_document (state.ctx, state.doc);
392 state.doc = NULL;
396 static int openxref (char *filename, char *password, int layouth)
398 for (int i = 0; i < state.texcount; ++i) {
399 state.texowners[i].w = -1;
400 state.texowners[i].slice = NULL;
403 closedoc ();
405 state.dirty = 0;
406 if (state.pagedims) {
407 free (state.pagedims);
408 state.pagedims = NULL;
410 state.pagedimcount = 0;
412 fz_set_aa_level (state.ctx, state.aalevel);
413 state.doc = fz_open_document (state.ctx, filename);
414 if (fz_needs_password (state.ctx, state.doc)) {
415 if (password && !*password) {
416 printd ("pass");
417 return 0;
419 else {
420 int ok = fz_authenticate_password (state.ctx, state.doc, password);
421 if (!ok) {
422 printd ("pass fail");
423 return 0;
427 if (layouth >= 0)
428 fz_layout_document (state.ctx, state.doc, 460, layouth, 12);
429 state.pagecount = fz_count_pages (state.ctx, state.doc);
430 return 1;
433 static void docinfo (void)
435 struct { char *tag; char *name; } metatbl[] = {
436 { FZ_META_INFO_TITLE, "Title" },
437 { FZ_META_INFO_AUTHOR, "Author" },
438 { FZ_META_FORMAT, "Format" },
439 { FZ_META_ENCRYPTION, "Encryption" },
440 { "info:Creator", "Creator" },
441 { "info:Producer", "Producer" },
442 { "info:CreationDate", "Creation date" },
444 int len = 0;
445 char *buf = NULL;
447 for (size_t i = 0; i < sizeof (metatbl) / sizeof (metatbl[1]); ++i) {
448 int need;
449 again:
450 need = fz_lookup_metadata (state.ctx, state.doc,
451 metatbl[i].tag, buf, len);
452 if (need > 0) {
453 if (need <= len) {
454 printd ("info %s\t%s", metatbl[i].name, buf);
456 else {
457 buf = realloc (buf, need + 1);
458 if (!buf) err (1, "docinfo realloc %d", need + 1);
459 len = need + 1;
460 goto again;
464 free (buf);
466 printd ("infoend");
469 static void unlinktile (struct tile *tile)
471 for (int i = 0; i < tile->slicecount; ++i) {
472 struct slice *s = &tile->slices[i];
474 if (s->texindex != -1) {
475 if (state.texowners[s->texindex].slice == s) {
476 state.texowners[s->texindex].slice = NULL;
482 static void freepage (struct page *page)
484 if (!page) return;
485 if (page->text) {
486 fz_drop_stext_page (state.ctx, page->text);
488 if (page->slinks) {
489 free (page->slinks);
491 fz_drop_display_list (state.ctx, page->dlist);
492 fz_drop_page (state.ctx, page->fzpage);
493 free (page);
496 static void freetile (struct tile *tile)
498 unlinktile (tile);
499 if (!tile->pbo) {
500 #ifndef PIGGYBACK
501 fz_drop_pixmap (state.ctx, tile->pixmap);
502 #else
503 if (state.pig) {
504 fz_drop_pixmap (state.ctx, state.pig);
506 state.pig = tile->pixmap;
507 #endif
509 else {
510 free (tile->pbo);
511 fz_drop_pixmap (state.ctx, tile->pixmap);
513 free (tile);
516 static void trimctm (pdf_page *page, int pindex)
518 fz_matrix ctm;
519 struct pagedim *pdim = &state.pagedims[pindex];
521 if (!page) return;
522 if (!pdim->tctmready) {
523 fz_rect realbox, mediabox;
524 fz_matrix rm, sm, tm, im, ctm1, page_ctm;
526 fz_rotate (&rm, -pdim->rotate);
527 fz_scale (&sm, 1, -1);
528 fz_concat (&ctm, &rm, &sm);
529 realbox = pdim->mediabox;
530 fz_transform_rect (&realbox, &ctm);
531 fz_translate (&tm, -realbox.x0, -realbox.y0);
532 fz_concat (&ctm1, &ctm, &tm);
533 pdf_page_transform (state.ctx, page, &mediabox, &page_ctm);
534 fz_invert_matrix (&im, &page_ctm);
535 fz_concat (&ctm, &im, &ctm1);
536 pdim->tctm = ctm;
537 pdim->tctmready = 1;
541 static fz_matrix pagectm1 (fz_page *fzpage, struct pagedim *pdim)
543 fz_matrix ctm, tm;
544 ptrdiff_t pdimno = pdim - state.pagedims;
546 ARSERT (pdim - state.pagedims < INT_MAX);
547 if (pdf_specifics (state.ctx, state.doc)) {
548 trimctm (pdf_page_from_fz_page (state.ctx, fzpage), (int) pdimno);
549 fz_concat (&ctm, &pdim->tctm, &pdim->ctm);
551 else {
552 fz_translate (&tm, -pdim->mediabox.x0, -pdim->mediabox.y0);
553 fz_concat (&ctm, &tm, &pdim->ctm);
555 return ctm;
558 static fz_matrix pagectm (struct page *page)
560 return pagectm1 (page->fzpage, &state.pagedims[page->pdimno]);
563 static void *loadpage (int pageno, int pindex)
565 fz_device *dev;
566 struct page *page;
568 page = calloc (sizeof (struct page), 1);
569 if (!page) {
570 err (1, "calloc page %d", pageno);
573 page->dlist = fz_new_display_list (state.ctx, NULL);
574 dev = fz_new_list_device (state.ctx, page->dlist);
575 fz_try (state.ctx) {
576 page->fzpage = fz_load_page (state.ctx, state.doc, pageno);
577 fz_run_page (state.ctx, page->fzpage, dev,
578 &fz_identity, NULL);
580 fz_catch (state.ctx) {
581 page->fzpage = NULL;
583 fz_close_device (state.ctx, dev);
584 fz_drop_device (state.ctx, dev);
586 page->pdimno = pindex;
587 page->pageno = pageno;
588 page->sgen = state.gen;
589 page->agen = state.gen;
590 page->tgen = state.gen;
591 return page;
594 static struct tile *alloctile (int h)
596 int slicecount;
597 size_t tilesize;
598 struct tile *tile;
600 slicecount = (h + state.sliceheight - 1) / state.sliceheight;
601 tilesize = sizeof (*tile) + ((slicecount - 1) * sizeof (struct slice));
602 tile = calloc (tilesize, 1);
603 if (!tile) {
604 err (1, "cannot allocate tile (%zu bytes)", tilesize);
606 for (int i = 0; i < slicecount; ++i) {
607 int sh = fz_mini (h, state.sliceheight);
608 tile->slices[i].h = sh;
609 tile->slices[i].texindex = -1;
610 h -= sh;
612 tile->slicecount = slicecount;
613 tile->sliceheight = state.sliceheight;
614 return tile;
617 static struct tile *rendertile (struct page *page, int x, int y, int w, int h,
618 struct bo *pbo)
620 fz_rect rect;
621 fz_irect bbox;
622 fz_matrix ctm;
623 fz_device *dev;
624 struct tile *tile;
625 struct pagedim *pdim;
627 tile = alloctile (h);
628 pdim = &state.pagedims[page->pdimno];
630 bbox = pdim->bounds;
631 bbox.x0 += x;
632 bbox.y0 += y;
633 bbox.x1 = bbox.x0 + w;
634 bbox.y1 = bbox.y0 + h;
636 if (state.pig) {
637 if (state.pig->w == w
638 && state.pig->h == h
639 && state.pig->colorspace == state.colorspace) {
640 tile->pixmap = state.pig;
641 tile->pixmap->x = bbox.x0;
642 tile->pixmap->y = bbox.y0;
644 else {
645 fz_drop_pixmap (state.ctx, state.pig);
647 state.pig = NULL;
649 if (!tile->pixmap) {
650 if (pbo) {
651 tile->pixmap =
652 fz_new_pixmap_with_bbox_and_data (state.ctx, state.colorspace,
653 &bbox, NULL, 1, pbo->ptr);
654 tile->pbo = pbo;
656 else {
657 tile->pixmap =
658 fz_new_pixmap_with_bbox (state.ctx, state.colorspace, &bbox,
659 NULL, 1);
663 tile->w = w;
664 tile->h = h;
665 fz_clear_pixmap_with_value (state.ctx, tile->pixmap, 0xff);
667 dev = fz_new_draw_device (state.ctx, NULL, tile->pixmap);
668 ctm = pagectm (page);
669 fz_rect_from_irect (&rect, &bbox);
670 fz_run_display_list (state.ctx, page->dlist, dev, &ctm, &rect, NULL);
671 fz_close_device (state.ctx, dev);
672 fz_drop_device (state.ctx, dev);
674 return tile;
677 #ifdef CACHE_PAGEREFS
678 /* modified mupdf/source/pdf/pdf-page.c:pdf_lookup_page_loc_imp
679 thanks to Robin Watts */
680 static void
681 pdf_collect_pages(pdf_document *doc, pdf_obj *node)
683 fz_context *ctx = state.ctx; /* doc->ctx; */
684 pdf_obj *kids;
685 int len;
687 if (state.pdflut.idx == state.pagecount) return;
689 kids = pdf_dict_gets (ctx, node, "Kids");
690 len = pdf_array_len (ctx, kids);
692 if (len == 0)
693 fz_throw (ctx, FZ_ERROR_GENERIC, "malformed pages tree");
695 if (pdf_mark_obj (ctx, node))
696 fz_throw (ctx, FZ_ERROR_GENERIC, "cycle in page tree");
697 for (int i = 0; i < len; i++) {
698 pdf_obj *kid = pdf_array_get (ctx, kids, i);
699 const char *type = pdf_to_name (ctx, pdf_dict_gets (ctx, kid, "Type"));
700 if (*type
701 ? !strcmp (type, "Pages")
702 : pdf_dict_gets (ctx, kid, "Kids")
703 && !pdf_dict_gets (ctx, kid, "MediaBox")) {
704 pdf_collect_pages (doc, kid);
706 else {
707 if (*type
708 ? strcmp (type, "Page") != 0
709 : !pdf_dict_gets (ctx, kid, "MediaBox"))
710 fz_warn (ctx, "non-page object in page tree (%s)", type);
711 state.pdflut.objs[state.pdflut.idx++] = pdf_keep_obj (ctx, kid);
714 pdf_unmark_obj (ctx, node);
717 static void
718 pdf_load_page_objs (pdf_document *doc)
720 pdf_obj *root = pdf_dict_gets (state.ctx,
721 pdf_trailer (state.ctx, doc), "Root");
722 pdf_obj *node = pdf_dict_gets (state.ctx, root, "Pages");
724 if (!node)
725 fz_throw (state.ctx, FZ_ERROR_GENERIC, "cannot find page tree");
727 state.pdflut.idx = 0;
728 pdf_collect_pages (doc, node);
730 #endif
732 static void initpdims (void)
734 double start, end;
735 FILE *trimf = NULL;
736 fz_rect rootmediabox = fz_empty_rect;
737 int pageno, trim, show;
738 int trimw = 0, cxcount;
739 fz_context *ctx = state.ctx;
740 pdf_document *pdf = pdf_specifics (ctx, state.doc);
742 fz_var (trimw);
743 fz_var (trimf);
744 fz_var (cxcount);
745 start = now ();
747 if (state.trimmargins && state.trimcachepath) {
748 trimf = fopen (state.trimcachepath, "rb");
749 if (!trimf) {
750 trimf = fopen (state.trimcachepath, "wb");
751 trimw = 1;
755 if (state.trimmargins || pdf)
756 cxcount = state.pagecount;
757 else
758 cxcount = fz_mini (state.pagecount, 1);
760 if (pdf) {
761 pdf_obj *obj;
762 obj = pdf_dict_getp (ctx, pdf_trailer (ctx, pdf),
763 "Root/Pages/MediaBox");
764 pdf_to_rect (ctx, obj, &rootmediabox);
767 #ifdef CACHE_PAGEREFS
768 if (pdf && (!state.pdflut.objs || state.pdflut.pdf != pdf)) {
769 state.pdflut.objs = calloc (sizeof (*state.pdflut.objs), cxcount);
770 if (!state.pdflut.objs) {
771 err (1, "malloc pageobjs %zu %d %zu failed",
772 sizeof (*state.pdflut.objs), cxcount,
773 sizeof (*state.pdflut.objs) * cxcount);
775 state.pdflut.count = cxcount;
776 pdf_load_page_objs (pdf);
777 state.pdflut.pdf = pdf;
779 #endif
781 for (pageno = 0; pageno < cxcount; ++pageno) {
782 int rotate = 0;
783 struct pagedim *p;
784 fz_rect mediabox = fz_empty_rect;
786 fz_var (rotate);
787 if (pdf) {
788 pdf_obj *pageref, *pageobj;
790 #ifdef CACHE_PAGEREFS
791 pageref = state.pdflut.objs[pageno];
792 #else
793 pageref = pdf_lookup_page_obj (ctx, pdf, pageno);
794 #endif
795 pageobj = pdf_resolve_indirect (ctx, pageref);
796 rotate = pdf_to_int (ctx, pdf_dict_gets (ctx, pageobj, "Rotate"));
798 if (state.trimmargins) {
799 pdf_obj *obj;
800 pdf_page *page;
802 fz_try (ctx) {
803 page = pdf_load_page (ctx, pdf, pageno);
804 obj = pdf_dict_gets (ctx, pageobj, "llpp.TrimBox");
805 trim = state.trimanew || !obj;
806 if (trim) {
807 fz_rect rect;
808 fz_device *dev;
809 fz_matrix ctm, page_ctm;
811 dev = fz_new_bbox_device (ctx, &rect);
812 pdf_page_transform (ctx, page, &mediabox, &page_ctm);
813 fz_invert_matrix (&ctm, &page_ctm);
814 pdf_run_page (ctx, page, dev, &fz_identity, NULL);
815 fz_close_device (ctx, dev);
816 fz_drop_device (ctx, dev);
818 rect.x0 += state.trimfuzz.x0;
819 rect.x1 += state.trimfuzz.x1;
820 rect.y0 += state.trimfuzz.y0;
821 rect.y1 += state.trimfuzz.y1;
822 fz_transform_rect (&rect, &ctm);
823 fz_intersect_rect (&rect, &mediabox);
825 if (!fz_is_empty_rect (&rect)) {
826 mediabox = rect;
829 obj = pdf_new_array (ctx, pdf, 4);
830 pdf_array_push (ctx, obj,
831 pdf_new_real (ctx, mediabox.x0));
832 pdf_array_push (ctx, obj,
833 pdf_new_real (ctx, mediabox.y0));
834 pdf_array_push (ctx, obj,
835 pdf_new_real (ctx, mediabox.x1));
836 pdf_array_push (ctx, obj,
837 pdf_new_real (ctx, mediabox.y1));
838 pdf_dict_puts (ctx, pageobj, "llpp.TrimBox", obj);
840 else {
841 mediabox.x0 = pdf_to_real (ctx,
842 pdf_array_get (ctx, obj, 0));
843 mediabox.y0 = pdf_to_real (ctx,
844 pdf_array_get (ctx, obj, 1));
845 mediabox.x1 = pdf_to_real (ctx,
846 pdf_array_get (ctx, obj, 2));
847 mediabox.y1 = pdf_to_real (ctx,
848 pdf_array_get (ctx, obj, 3));
851 fz_drop_page (ctx, &page->super);
852 show = trim ? pageno % 5 == 0 : pageno % 20 == 0;
853 if (show) {
854 printd ("progress %f Trimming %d",
855 (double) (pageno + 1) / state.pagecount,
856 pageno + 1);
859 fz_catch (ctx) {
860 printd ("emsg failed to load page %d", pageno);
863 else {
864 int empty = 0;
865 fz_rect cropbox;
867 pdf_to_rect (ctx,
868 pdf_dict_gets (ctx, pageobj, "MediaBox"),
869 &mediabox);
870 if (fz_is_empty_rect (&mediabox)) {
871 mediabox.x0 = 0;
872 mediabox.y0 = 0;
873 mediabox.x1 = 612;
874 mediabox.y1 = 792;
875 empty = 1;
878 pdf_to_rect (ctx,
879 pdf_dict_gets (ctx, pageobj, "CropBox"),
880 &cropbox);
881 if (!fz_is_empty_rect (&cropbox)) {
882 if (empty) {
883 mediabox = cropbox;
885 else {
886 fz_intersect_rect (&mediabox, &cropbox);
889 else {
890 if (empty) {
891 if (fz_is_empty_rect (&rootmediabox)) {
892 printd ("emsg cannot find page size for page %d",
893 pageno);
895 else {
896 mediabox = rootmediabox;
902 else {
903 if (state.trimmargins && trimw) {
904 fz_page *page;
906 fz_try (ctx) {
907 page = fz_load_page (ctx, state.doc, pageno);
908 fz_bound_page (ctx, page, &mediabox);
909 if (state.trimmargins) {
910 fz_rect rect;
911 fz_device *dev;
913 dev = fz_new_bbox_device (ctx, &rect);
914 fz_run_page (ctx, page, dev, &fz_identity, NULL);
915 fz_close_device (ctx, dev);
916 fz_drop_device (ctx, dev);
918 rect.x0 += state.trimfuzz.x0;
919 rect.x1 += state.trimfuzz.x1;
920 rect.y0 += state.trimfuzz.y0;
921 rect.y1 += state.trimfuzz.y1;
922 fz_intersect_rect (&rect, &mediabox);
924 if (!fz_is_empty_rect (&rect)) {
925 mediabox = rect;
928 fz_drop_page (ctx, page);
930 fz_catch (ctx) {
932 if (trimf) {
933 size_t n = fwrite (&mediabox, sizeof (mediabox), 1, trimf);
934 if (n - 1) {
935 err (1, "fwrite trim mediabox");
939 else {
940 if (trimf) {
941 size_t n = fread (&mediabox, sizeof (mediabox), 1, trimf);
942 if (n - 1) {
943 err (1, "fread trim mediabox %d", pageno);
946 else {
947 fz_page *page;
948 fz_try (ctx) {
949 page = fz_load_page (ctx, state.doc, pageno);
950 fz_bound_page (ctx, page, &mediabox);
951 fz_drop_page (ctx, page);
953 show = !state.trimmargins && pageno % 20 == 0;
954 if (show) {
955 printd ("progress %f Gathering dimensions %d",
956 (double) (pageno) / state.pagecount,
957 pageno);
960 fz_catch (ctx) {
961 printd ("emsg failed to load page %d", pageno);
967 if (state.pagedimcount == 0
968 || ((void) (p = &state.pagedims[state.pagedimcount-1])
969 , p->rotate != rotate)
970 || memcmp (&p->mediabox, &mediabox, sizeof (mediabox))) {
971 size_t size;
973 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
974 state.pagedims = realloc (state.pagedims, size);
975 if (!state.pagedims) {
976 err (1, "realloc pagedims to %zu (%d elems)",
977 size, state.pagedimcount + 1);
980 p = &state.pagedims[state.pagedimcount++];
981 p->rotate = rotate;
982 p->mediabox = mediabox;
983 p->pageno = pageno;
986 end = now ();
987 printd ("progress 1 %s %d pages in %f seconds",
988 state.trimmargins ? "Trimmed" : "Processed",
989 state.pagecount, end - start);
990 state.trimanew = 0;
991 if (trimf) {
992 if (fclose (trimf)) {
993 err (1, "fclose");
998 static void layout (void)
1000 int pindex;
1001 fz_rect box;
1002 fz_matrix ctm, rm;
1003 struct pagedim *p = NULL;
1004 float zw, w, maxw = 0.0, zoom = 1.0;
1006 if (state.pagedimcount == 0) return;
1008 switch (state.fitmodel) {
1009 case FitProportional:
1010 for (pindex = 0; pindex < state.pagedimcount; ++pindex) {
1011 float x0, x1;
1013 p = &state.pagedims[pindex];
1014 fz_rotate (&rm, p->rotate + state.rotate);
1015 box = p->mediabox;
1016 fz_transform_rect (&box, &rm);
1018 x0 = fz_min (box.x0, box.x1);
1019 x1 = fz_max (box.x0, box.x1);
1021 w = x1 - x0;
1022 maxw = fz_max (w, maxw);
1023 zoom = state.w / maxw;
1025 break;
1027 case FitPage:
1028 maxw = state.w;
1029 break;
1031 case FitWidth:
1032 break;
1034 default:
1035 ARSERT (0 && state.fitmodel);
1038 for (pindex = 0; pindex < state.pagedimcount; ++pindex) {
1039 fz_rect rect;
1040 fz_matrix tm, sm;
1042 p = &state.pagedims[pindex];
1043 fz_rotate (&ctm, state.rotate);
1044 fz_rotate (&rm, p->rotate + state.rotate);
1045 box = p->mediabox;
1046 fz_transform_rect (&box, &rm);
1047 w = box.x1 - box.x0;
1048 switch (state.fitmodel) {
1049 case FitProportional:
1050 p->left = (int) (((maxw - w) * zoom) / 2.f);
1051 break;
1052 case FitPage:
1054 float zh, h;
1055 zw = maxw / w;
1056 h = box.y1 - box.y0;
1057 zh = state.h / h;
1058 zoom = fz_min (zw, zh);
1059 p->left = (int) ((maxw - (w * zoom)) / 2.f);
1061 break;
1062 case FitWidth:
1063 p->left = 0;
1064 zoom = state.w / w;
1065 break;
1068 fz_scale (&p->zoomctm, zoom, zoom);
1069 fz_concat (&ctm, &p->zoomctm, &ctm);
1071 fz_rotate (&rm, p->rotate);
1072 p->pagebox = p->mediabox;
1073 fz_transform_rect (&p->pagebox, &rm);
1074 p->pagebox.x1 -= p->pagebox.x0;
1075 p->pagebox.y1 -= p->pagebox.y0;
1076 p->pagebox.x0 = 0;
1077 p->pagebox.y0 = 0;
1078 rect = p->pagebox;
1079 fz_transform_rect (&rect, &ctm);
1080 fz_round_rect (&p->bounds, &rect);
1081 p->ctm = ctm;
1083 fz_translate (&tm, 0, -p->mediabox.y1);
1084 fz_scale (&sm, zoom, -zoom);
1085 fz_concat (&ctm, &tm, &sm);
1087 p->tctmready = 0;
1090 do {
1091 int x0 = fz_mini (p->bounds.x0, p->bounds.x1);
1092 int y0 = fz_mini (p->bounds.y0, p->bounds.y1);
1093 int x1 = fz_maxi (p->bounds.x0, p->bounds.x1);
1094 int y1 = fz_maxi (p->bounds.y0, p->bounds.y1);
1095 int boundw = x1 - x0;
1096 int boundh = y1 - y0;
1098 printd ("pdim %d %d %d %d", p->pageno, boundw, boundh, p->left);
1099 } while (p-- != state.pagedims);
1102 struct pagedim *pdimofpageno (int pageno)
1104 struct pagedim *pdim = state.pagedims;
1106 for (int i = 0; i < state.pagedimcount; ++i) {
1107 if (state.pagedims[i].pageno > pageno)
1108 break;
1109 pdim = &state.pagedims[i];
1111 return pdim;
1114 static void recurse_outline (fz_outline *outline, int level)
1116 while (outline) {
1117 if (outline->page >= 0) {
1118 fz_point p = {.x = outline->x, .y = outline->y};
1119 struct pagedim *pdim = pdimofpageno (outline->page);
1120 int h = fz_maxi (fz_absi (pdim->bounds.y1 - pdim->bounds.y0), 0);
1121 fz_transform_point (&p, &pdim->ctm);
1122 printd ("o %d %d %d %d %s",
1123 level, outline->page, (int) p.y, h, outline->title);
1125 else {
1126 printd ("on %d %s", level, outline->title);
1128 if (outline->down) {
1129 recurse_outline (outline->down, level + 1);
1131 outline = outline->next;
1135 static void process_outline (void)
1137 fz_outline *outline;
1139 if (!state.needoutline || !state.pagedimcount) return;
1141 state.needoutline = 0;
1142 outline = fz_load_outline (state.ctx, state.doc);
1143 if (outline) {
1144 recurse_outline (outline, 0);
1145 fz_drop_outline (state.ctx, outline);
1149 static char *strofline (fz_stext_line *line)
1151 char *p;
1152 char utf8[10];
1153 fz_stext_char *ch;
1154 size_t size = 0, cap = 80;
1156 p = malloc (cap + 1);
1157 if (!p) return NULL;
1159 for (ch = line->first_char; ch; ch = ch->next) {
1160 int n = fz_runetochar (utf8, ch->c);
1161 if (size + n > cap) {
1162 cap *= 2;
1163 p = realloc (p, cap + 1);
1164 if (!p) return NULL;
1167 memcpy (p + size, utf8, n);
1168 size += n;
1170 p[size] = 0;
1171 return p;
1174 static int matchline (regex_t *re, fz_stext_line *line,
1175 int stop, int pageno, double start)
1177 int ret;
1178 char *p;
1179 regmatch_t rm;
1181 p = strofline (line);
1182 if (!p) return -1;
1184 ret = regexec (re, p, 1, &rm, 0);
1185 if (ret) {
1186 free (p);
1187 if (ret != REG_NOMATCH) {
1188 size_t size;
1189 char errbuf[80];
1190 size = regerror (ret, re, errbuf, sizeof (errbuf));
1191 printd ("msg regexec error `%.*s'",
1192 (int) size, errbuf);
1193 return -1;
1195 return 0;
1197 else {
1198 fz_point p1, p2, p3, p4;
1199 fz_rect s = {0,0,0,0}, e;
1200 fz_stext_char *ch;
1201 int o = 0;
1203 for (ch = line->first_char; ch; ch = ch->next) {
1204 o += fz_runelen (ch->c);
1205 if (o > rm.rm_so) {
1206 s = ch->bbox;
1207 break;
1210 for (;ch; ch = ch->next) {
1211 o += fz_runelen (ch->c);
1212 if (o > rm.rm_eo) break;
1214 e = ch->bbox;
1216 p1.x = s.x0;
1217 p1.y = s.y0;
1218 p2.x = e.x1;
1219 p2.y = s.y0;
1220 p3.x = e.x1;
1221 p3.y = e.y1;
1222 p4.x = s.x0;
1223 p4.y = e.y1;
1225 if (!stop) {
1226 printd ("firstmatch %d %d %f %f %f %f %f %f %f %f",
1227 pageno, 1,
1228 p1.x, p1.y,
1229 p2.x, p2.y,
1230 p3.x, p3.y,
1231 p4.x, p4.y);
1233 printd ("progress 1 found at %d `%.*s' in %f sec",
1234 pageno + 1, (int) (rm.rm_eo - rm.rm_so), &p[rm.rm_so],
1235 now () - start);
1237 else {
1238 printd ("match %d %d %f %f %f %f %f %f %f %f",
1239 pageno, 2,
1240 p1.x, p1.y,
1241 p2.x, p2.y,
1242 p3.x, p3.y,
1243 p4.x, p4.y);
1245 free (p);
1246 return 1;
1250 /* wishful thinking function */
1251 static void search (regex_t *re, int pageno, int y, int forward)
1253 fz_device *tdev;
1254 fz_stext_page *text;
1255 struct pagedim *pdim;
1256 int stop = 0, niters = 0;
1257 double start, end;
1258 fz_page *page;
1259 fz_stext_block *block;
1261 start = now ();
1262 while (pageno >= 0 && pageno < state.pagecount && !stop) {
1263 if (niters++ == 5) {
1264 niters = 0;
1265 if (hasdata ()) {
1266 printd ("progress 1 attention requested aborting search at %d",
1267 pageno);
1268 stop = 1;
1270 else {
1271 printd ("progress %f searching in page %d",
1272 (double) (pageno + 1) / state.pagecount,
1273 pageno);
1276 pdim = pdimofpageno (pageno);
1277 text = fz_new_stext_page (state.ctx, &pdim->mediabox);
1278 tdev = fz_new_stext_device (state.ctx, text, 0);
1280 page = fz_load_page (state.ctx, state.doc, pageno);
1282 fz_matrix ctm = pagectm1 (page, pdim);
1283 fz_run_page (state.ctx, page, tdev, &ctm, NULL);
1286 fz_close_device (state.ctx, tdev);
1287 fz_drop_device (state.ctx, tdev);
1289 if (forward) {
1290 for (block = text->first_block; block; block = block->next) {
1291 fz_stext_line *line;
1293 if (block->type != FZ_STEXT_BLOCK_TEXT) continue;
1294 for (line = block->u.t.first_line; line; line = line->next) {
1295 if (line->bbox.y0 < y + 1) continue;
1297 switch (matchline (re, line, stop, pageno, start)) {
1298 case 0: break;
1299 case 1: stop = 1; break;
1300 case -1: stop = 1; goto endloop;
1305 else {
1306 for (block = text->last_block; block; block = block->prev) {
1307 fz_stext_line *line;
1309 if (block->type != FZ_STEXT_BLOCK_TEXT) continue;
1310 for (line = block->u.t.last_line; line; line = line->prev) {
1311 if (line->bbox.y0 < y + 1) continue;
1313 switch (matchline (re, line, stop, pageno, start)) {
1314 case 0: break;
1315 case 1: stop = 1; break;
1316 case -1: stop = 1; goto endloop;
1322 if (forward) {
1323 pageno += 1;
1324 y = 0;
1326 else {
1327 pageno -= 1;
1328 y = INT_MAX;
1330 endloop:
1331 fz_drop_stext_page (state.ctx, text);
1332 fz_drop_page (state.ctx, page);
1334 end = now ();
1335 if (!stop) {
1336 printd ("progress 1 no matches %f sec", end - start);
1338 printd ("clearrects");
1341 static void set_tex_params (int colorspace)
1343 switch (colorspace) {
1344 case 0:
1345 state.texiform = GL_RGBA8;
1346 state.texform = GL_RGBA;
1347 state.texty = GL_UNSIGNED_BYTE;
1348 state.colorspace = fz_device_rgb (state.ctx);
1349 break;
1350 case 1:
1351 state.texiform = GL_RGBA8;
1352 state.texform = GL_BGRA;
1353 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
1354 state.texty = GL_UNSIGNED_INT_8_8_8_8_REV;
1355 #else
1356 state.texty = GL_UNSIGNED_INT_8_8_8_8;
1357 #endif
1358 state.colorspace = fz_device_bgr (state.ctx);
1359 break;
1360 case 2:
1361 state.texiform = GL_LUMINANCE_ALPHA;
1362 state.texform = GL_LUMINANCE_ALPHA;
1363 state.texty = GL_UNSIGNED_BYTE;
1364 state.colorspace = fz_device_gray (state.ctx);
1365 break;
1366 default:
1367 errx (1, "invalid colorspce %d", colorspace);
1371 static void realloctexts (int texcount)
1373 size_t size;
1375 if (texcount == state.texcount) return;
1377 if (texcount < state.texcount) {
1378 glDeleteTextures (state.texcount - texcount,
1379 state.texids + texcount);
1382 size = texcount * (sizeof (*state.texids) + sizeof (*state.texowners));
1383 state.texids = realloc (state.texids, size);
1384 if (!state.texids) {
1385 err (1, "realloc texs %zu", size);
1388 state.texowners = (void *) (state.texids + texcount);
1389 if (texcount > state.texcount) {
1390 glGenTextures (texcount - state.texcount,
1391 state.texids + state.texcount);
1392 for (int i = state.texcount; i < texcount; ++i) {
1393 state.texowners[i].w = -1;
1394 state.texowners[i].slice = NULL;
1397 state.texcount = texcount;
1398 state.texindex = 0;
1401 static char *mbtoutf8 (char *s)
1403 char *p, *r;
1404 wchar_t *tmp;
1405 size_t i, ret, len;
1407 if (state.utf8cs) {
1408 return s;
1411 len = mbstowcs (NULL, s, strlen (s));
1412 if (len == 0) {
1413 return s;
1415 else {
1416 if (len == (size_t) -1) {
1417 printd ("emsg mbtoutf8: mbstowcs: %d:%s", errno, strerror (errno));
1418 return s;
1422 tmp = calloc (len, sizeof (wchar_t));
1423 if (!tmp) {
1424 printd ("emsg mbtoutf8: calloc(%zu, %zu): %d:%s",
1425 len, sizeof (wchar_t), errno, strerror (errno));
1426 return s;
1429 ret = mbstowcs (tmp, s, len);
1430 if (ret == (size_t) -1) {
1431 printd ("emsg mbtoutf8: mbswcs %zu characters failed: %d:%s",
1432 len, errno, strerror (errno));
1433 free (tmp);
1434 return s;
1437 len = 0;
1438 for (i = 0; i < ret; ++i) {
1439 len += fz_runelen (tmp[i]);
1442 p = r = malloc (len + 1);
1443 if (!r) {
1444 printd ("emsg mbtoutf8: malloc(%zu)", len);
1445 free (tmp);
1446 return s;
1449 for (i = 0; i < ret; ++i) {
1450 p += fz_runetochar (p, tmp[i]);
1452 *p = 0;
1453 free (tmp);
1454 return r;
1457 CAMLprim value ml_mbtoutf8 (value s_v)
1459 CAMLparam1 (s_v);
1460 CAMLlocal1 (ret_v);
1461 char *s, *r;
1463 s = String_val (s_v);
1464 r = mbtoutf8 (s);
1465 if (r == s) {
1466 ret_v = s_v;
1468 else {
1469 ret_v = caml_copy_string (r);
1470 free (r);
1472 CAMLreturn (ret_v);
1475 static void * mainloop (void UNUSED_ATTR *unused)
1477 char *p = NULL;
1478 int len, ret, oldlen = 0;
1480 fz_var (p);
1481 fz_var (oldlen);
1482 for (;;) {
1483 len = readlen (state.csock);
1484 if (len == 0) {
1485 errx (1, "readlen returned 0");
1488 if (oldlen < len + 1) {
1489 p = realloc (p, len + 1);
1490 if (!p) {
1491 err (1, "realloc %d failed", len + 1);
1493 oldlen = len + 1;
1495 readdata (state.csock, p, len);
1496 p[len] = 0;
1498 if (!strncmp ("open", p, 4)) {
1499 int off, usedoccss, ok = 0, layouth;
1500 char *password;
1501 char *filename;
1502 char *utf8filename;
1503 size_t filenamelen;
1505 fz_var (ok);
1506 ret = sscanf (p + 5, " %d %d %n", &usedoccss, &layouth, &off);
1507 if (ret != 2) {
1508 errx (1, "malformed open `%.*s' ret=%d", len, p, ret);
1511 filename = p + 5 + off;
1512 filenamelen = strlen (filename);
1513 password = filename + filenamelen + 1;
1515 if (password[strlen (password) + 1]) {
1516 fz_set_user_css (state.ctx, password + strlen (password) + 1);
1519 lock ("open");
1520 fz_set_use_document_css (state.ctx, usedoccss);
1521 fz_try (state.ctx) {
1522 ok = openxref (filename, password, layouth);
1524 fz_catch (state.ctx) {
1525 utf8filename = mbtoutf8 (filename);
1526 printd ("msg Could not open %s", utf8filename);
1527 if (utf8filename != filename) {
1528 free (utf8filename);
1531 if (ok) {
1532 docinfo ();
1533 initpdims ();
1535 unlock ("open");
1537 if (ok) {
1538 utf8filename = mbtoutf8 (filename);
1539 printd ("msg Opened %s (press h/F1 to get help)", utf8filename);
1540 if (utf8filename != filename) {
1541 free (utf8filename);
1543 state.needoutline = 1;
1546 else if (!strncmp ("cs", p, 2)) {
1547 int i, colorspace;
1549 ret = sscanf (p + 2, " %d", &colorspace);
1550 if (ret != 1) {
1551 errx (1, "malformed cs `%.*s' ret=%d", len, p, ret);
1553 lock ("cs");
1554 set_tex_params (colorspace);
1555 for (i = 0; i < state.texcount; ++i) {
1556 state.texowners[i].w = -1;
1557 state.texowners[i].slice = NULL;
1559 unlock ("cs");
1561 else if (!strncmp ("freepage", p, 8)) {
1562 void *ptr;
1564 ret = sscanf (p + 8, " %" SCNxPTR, (uintptr_t *) &ptr);
1565 if (ret != 1) {
1566 errx (1, "malformed freepage `%.*s' ret=%d", len, p, ret);
1568 lock ("freepage");
1569 freepage (ptr);
1570 unlock ("freepage");
1572 else if (!strncmp ("freetile", p, 8)) {
1573 void *ptr;
1575 ret = sscanf (p + 8, " %" SCNxPTR, (uintptr_t *) &ptr);
1576 if (ret != 1) {
1577 errx (1, "malformed freetile `%.*s' ret=%d", len, p, ret);
1579 lock ("freetile");
1580 freetile (ptr);
1581 unlock ("freetile");
1583 else if (!strncmp ("search", p, 6)) {
1584 int icase, pageno, y, len2, forward;
1585 char *pattern;
1586 regex_t re;
1588 ret = sscanf (p + 6, " %d %d %d %d,%n",
1589 &icase, &pageno, &y, &forward, &len2);
1590 if (ret != 4) {
1591 errx (1, "malformed search `%s' ret=%d", p, ret);
1594 pattern = p + 6 + len2;
1595 ret = regcomp (&re, pattern,
1596 REG_EXTENDED | (icase ? REG_ICASE : 0));
1597 if (ret) {
1598 char errbuf[80];
1599 size_t size;
1601 size = regerror (ret, &re, errbuf, sizeof (errbuf));
1602 printd ("msg regcomp failed `%.*s'", (int) size, errbuf);
1604 else {
1605 search (&re, pageno, y, forward);
1606 regfree (&re);
1609 else if (!strncmp ("geometry", p, 8)) {
1610 int w, h, fitmodel;
1612 printd ("clear");
1613 ret = sscanf (p + 8, " %d %d %d", &w, &h, &fitmodel);
1614 if (ret != 3) {
1615 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
1618 lock ("geometry");
1619 state.h = h;
1620 if (w != state.w) {
1621 state.w = w;
1622 for (int i = 0; i < state.texcount; ++i) {
1623 state.texowners[i].slice = NULL;
1626 state.fitmodel = fitmodel;
1627 layout ();
1628 process_outline ();
1630 state.gen++;
1631 unlock ("geometry");
1632 printd ("continue %d", state.pagecount);
1634 else if (!strncmp ("reqlayout", p, 9)) {
1635 char *nameddest;
1636 int rotate, off, h;
1637 int fitmodel;
1638 pdf_document *pdf;
1640 printd ("clear");
1641 ret = sscanf (p + 9, " %d %d %d %n",
1642 &rotate, &fitmodel, &h, &off);
1643 if (ret != 3) {
1644 errx (1, "bad reqlayout line `%.*s' ret=%d", len, p, ret);
1646 lock ("reqlayout");
1647 pdf = pdf_specifics (state.ctx, state.doc);
1648 if (state.rotate != rotate || state.fitmodel != fitmodel) {
1649 state.gen += 1;
1651 state.rotate = rotate;
1652 state.fitmodel = fitmodel;
1653 state.h = h;
1654 layout ();
1655 process_outline ();
1657 nameddest = p + 9 + off;
1658 if (pdf && nameddest && *nameddest) {
1659 fz_point xy;
1660 struct pagedim *pdim;
1661 int pageno = pdf_lookup_anchor (state.ctx, pdf, nameddest,
1662 &xy.x, &xy.y);
1663 pdim = pdimofpageno (pageno);
1664 fz_transform_point (&xy, &pdim->ctm);
1665 printd ("a %d %d %d", pageno, (int) xy.x, (int) xy.y);
1668 state.gen++;
1669 unlock ("reqlayout");
1670 printd ("continue %d", state.pagecount);
1672 else if (!strncmp ("page", p, 4)) {
1673 double a, b;
1674 struct page *page;
1675 int pageno, pindex;
1677 ret = sscanf (p + 4, " %d %d", &pageno, &pindex);
1678 if (ret != 2) {
1679 errx (1, "bad page line `%.*s' ret=%d", len, p, ret);
1682 lock ("page");
1683 a = now ();
1684 page = loadpage (pageno, pindex);
1685 b = now ();
1686 unlock ("page");
1688 printd ("page %" PRIxPTR " %f", (uintptr_t) page, b - a);
1690 else if (!strncmp ("tile", p, 4)) {
1691 int x, y, w, h;
1692 struct page *page;
1693 struct tile *tile;
1694 double a, b;
1695 void *data;
1697 ret = sscanf (p + 4, " %" SCNxPTR " %d %d %d %d %" SCNxPTR,
1698 (uintptr_t *) &page, &x, &y, &w, &h,
1699 (uintptr_t *) &data);
1700 if (ret != 6) {
1701 errx (1, "bad tile line `%.*s' ret=%d", len, p, ret);
1704 lock ("tile");
1705 a = now ();
1706 tile = rendertile (page, x, y, w, h, data);
1707 b = now ();
1708 unlock ("tile");
1710 printd ("tile %d %d %" PRIxPTR " %u %f",
1711 x, y, (uintptr_t) (tile),
1712 tile->w * tile->h * tile->pixmap->n,
1713 b - a);
1715 else if (!strncmp ("trimset", p, 7)) {
1716 fz_irect fuzz;
1717 int trimmargins;
1719 ret = sscanf (p + 7, " %d %d %d %d %d",
1720 &trimmargins, &fuzz.x0, &fuzz.y0, &fuzz.x1, &fuzz.y1);
1721 if (ret != 5) {
1722 errx (1, "malformed trimset `%.*s' ret=%d", len, p, ret);
1724 lock ("trimset");
1725 state.trimmargins = trimmargins;
1726 if (memcmp (&fuzz, &state.trimfuzz, sizeof (fuzz))) {
1727 state.trimanew = 1;
1728 state.trimfuzz = fuzz;
1730 unlock ("trimset");
1732 else if (!strncmp ("settrim", p, 7)) {
1733 fz_irect fuzz;
1734 int trimmargins;
1736 ret = sscanf (p + 7, " %d %d %d %d %d",
1737 &trimmargins, &fuzz.x0, &fuzz.y0, &fuzz.x1, &fuzz.y1);
1738 if (ret != 5) {
1739 errx (1, "malformed settrim `%.*s' ret=%d", len, p, ret);
1741 printd ("clear");
1742 lock ("settrim");
1743 state.trimmargins = trimmargins;
1744 state.needoutline = 1;
1745 if (memcmp (&fuzz, &state.trimfuzz, sizeof (fuzz))) {
1746 state.trimanew = 1;
1747 state.trimfuzz = fuzz;
1749 state.pagedimcount = 0;
1750 free (state.pagedims);
1751 state.pagedims = NULL;
1752 initpdims ();
1753 layout ();
1754 process_outline ();
1755 unlock ("settrim");
1756 printd ("continue %d", state.pagecount);
1758 else if (!strncmp ("sliceh", p, 6)) {
1759 int h;
1761 ret = sscanf (p + 6, " %d", &h);
1762 if (ret != 1) {
1763 errx (1, "malformed sliceh `%.*s' ret=%d", len, p, ret);
1765 if (h != state.sliceheight) {
1766 state.sliceheight = h;
1767 for (int i = 0; i < state.texcount; ++i) {
1768 state.texowners[i].w = -1;
1769 state.texowners[i].h = -1;
1770 state.texowners[i].slice = NULL;
1774 else if (!strncmp ("interrupt", p, 9)) {
1775 printd ("vmsg interrupted");
1777 else {
1778 errx (1, "unknown command %.*s", len, p);
1781 return 0;
1784 CAMLprim value ml_isexternallink (value uri_v)
1786 CAMLparam1 (uri_v);
1787 int ext = fz_is_external_link (state.ctx, String_val (uri_v));
1788 CAMLreturn (Val_bool (ext));
1791 CAMLprim value ml_uritolocation (value uri_v)
1793 CAMLparam1 (uri_v);
1794 CAMLlocal1 (ret_v);
1795 int pageno;
1796 fz_point xy;
1797 struct pagedim *pdim;
1799 pageno = fz_resolve_link (state.ctx, state.doc, String_val (uri_v),
1800 &xy.x, &xy.y);
1801 pdim = pdimofpageno (pageno);
1802 fz_transform_point (&xy, &pdim->ctm);
1803 ret_v = caml_alloc_tuple (3);
1804 Field (ret_v, 0) = Val_int (pageno);
1805 Field (ret_v, 1) = caml_copy_double ((double) xy.x);
1806 Field (ret_v, 2) = caml_copy_double ((double) xy.y);
1807 CAMLreturn (ret_v);
1810 CAMLprim value ml_realloctexts (value texcount_v)
1812 CAMLparam1 (texcount_v);
1813 int ok;
1815 if (trylock (__func__)) {
1816 ok = 0;
1817 goto done;
1819 realloctexts (Int_val (texcount_v));
1820 ok = 1;
1821 unlock (__func__);
1823 done:
1824 CAMLreturn (Val_bool (ok));
1827 static void recti (int x0, int y0, int x1, int y1)
1829 GLfloat *v = state.vertices;
1831 glVertexPointer (2, GL_FLOAT, 0, v);
1832 v[0] = x0; v[1] = y0;
1833 v[2] = x1; v[3] = y0;
1834 v[4] = x0; v[5] = y1;
1835 v[6] = x1; v[7] = y1;
1836 glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
1839 static void showsel (struct page *page, int ox, int oy)
1841 fz_irect bbox;
1842 fz_rect rect;
1843 fz_stext_block *block;
1844 int seen = 0;
1845 unsigned char selcolor[] = {15,15,15,140};
1847 if (!page->fmark || !page->lmark) return;
1849 glEnable (GL_BLEND);
1850 glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA);
1851 glColor4ubv (selcolor);
1853 ox += state.pagedims[page->pdimno].bounds.x0;
1854 oy += state.pagedims[page->pdimno].bounds.y0;
1856 for (block = page->text->first_block; block; block = block->next) {
1857 fz_stext_line *line;
1859 if (block->type != FZ_STEXT_BLOCK_TEXT) continue;
1860 for (line = block->u.t.first_line; line; line = line->next) {
1861 fz_stext_char *ch;
1863 rect = fz_empty_rect;
1864 for (ch = line->first_char; ch; ch = ch->next) {
1865 if (ch == page->fmark) seen = 1;
1866 if (seen) fz_union_rect (&rect, &ch->bbox);
1867 if (ch == page->lmark) {
1868 fz_round_rect (&bbox, &rect);
1869 recti (bbox.x0 + ox, bbox.y0 + oy,
1870 bbox.x1 + ox, bbox.y1 + oy);
1871 goto done;
1874 fz_round_rect (&bbox, &rect);
1875 recti (bbox.x0 + ox, bbox.y0 + oy,
1876 bbox.x1 + ox, bbox.y1 + oy);
1879 done:
1880 glDisable (GL_BLEND);
1883 #pragma GCC diagnostic push
1884 #pragma GCC diagnostic ignored "-Wconversion"
1885 #include "glfont.c"
1886 #pragma GCC diagnostic pop
1888 static void stipplerect (fz_matrix *m,
1889 fz_point *p1,
1890 fz_point *p2,
1891 fz_point *p3,
1892 fz_point *p4,
1893 GLfloat *texcoords,
1894 GLfloat *vertices)
1896 fz_transform_point (p1, m);
1897 fz_transform_point (p2, m);
1898 fz_transform_point (p3, m);
1899 fz_transform_point (p4, m);
1901 float w, h, s, t;
1903 w = p2->x - p1->x;
1904 h = p2->y - p1->y;
1905 t = hypotf (w, h) * .25f;
1907 w = p3->x - p2->x;
1908 h = p3->y - p2->y;
1909 s = hypotf (w, h) * .25f;
1911 texcoords[0] = 0; vertices[0] = p1->x; vertices[1] = p1->y;
1912 texcoords[1] = t; vertices[2] = p2->x; vertices[3] = p2->y;
1914 texcoords[2] = 0; vertices[4] = p2->x; vertices[5] = p2->y;
1915 texcoords[3] = s; vertices[6] = p3->x; vertices[7] = p3->y;
1917 texcoords[4] = 0; vertices[8] = p3->x; vertices[9] = p3->y;
1918 texcoords[5] = t; vertices[10] = p4->x; vertices[11] = p4->y;
1920 texcoords[6] = 0; vertices[12] = p4->x; vertices[13] = p4->y;
1921 texcoords[7] = s; vertices[14] = p1->x; vertices[15] = p1->y;
1923 glDrawArrays (GL_LINES, 0, 8);
1926 static void solidrect (fz_matrix *m,
1927 fz_point *p1,
1928 fz_point *p2,
1929 fz_point *p3,
1930 fz_point *p4,
1931 GLfloat *vertices)
1933 fz_transform_point (p1, m);
1934 fz_transform_point (p2, m);
1935 fz_transform_point (p3, m);
1936 fz_transform_point (p4, m);
1937 vertices[0] = p1->x; vertices[1] = p1->y;
1938 vertices[2] = p2->x; vertices[3] = p2->y;
1940 vertices[4] = p3->x; vertices[5] = p3->y;
1941 vertices[6] = p4->x; vertices[7] = p4->y;
1942 glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
1945 static void ensurelinks (struct page *page)
1947 if (!page->links)
1948 page->links = fz_load_links (state.ctx, page->fzpage);
1951 static void highlightlinks (struct page *page, int xoff, int yoff)
1953 fz_matrix ctm, tm, pm;
1954 fz_link *link;
1955 GLfloat *texcoords = state.texcoords;
1956 GLfloat *vertices = state.vertices;
1958 ensurelinks (page);
1960 glEnable (GL_TEXTURE_1D);
1961 glEnable (GL_BLEND);
1962 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1963 glBindTexture (GL_TEXTURE_1D, state.stid);
1965 xoff -= state.pagedims[page->pdimno].bounds.x0;
1966 yoff -= state.pagedims[page->pdimno].bounds.y0;
1967 fz_translate (&tm, xoff, yoff);
1968 pm = pagectm (page);
1969 fz_concat (&ctm, &pm, &tm);
1971 glTexCoordPointer (1, GL_FLOAT, 0, texcoords);
1972 glVertexPointer (2, GL_FLOAT, 0, vertices);
1974 for (link = page->links; link; link = link->next) {
1975 fz_point p1, p2, p3, p4;
1977 p1.x = link->rect.x0;
1978 p1.y = link->rect.y0;
1980 p2.x = link->rect.x1;
1981 p2.y = link->rect.y0;
1983 p3.x = link->rect.x1;
1984 p3.y = link->rect.y1;
1986 p4.x = link->rect.x0;
1987 p4.y = link->rect.y1;
1989 /* TODO: different colours for different schemes */
1990 if (fz_is_external_link (state.ctx, link->uri)) glColor3ub (0, 0, 255);
1991 else glColor3ub (255, 0, 0);
1993 stipplerect (&ctm, &p1, &p2, &p3, &p4, texcoords, vertices);
1996 for (int i = 0; i < page->annotcount; ++i) {
1997 fz_point p1, p2, p3, p4;
1998 struct annot *annot = &page->annots[i];
2000 p1.x = annot->bbox.x0;
2001 p1.y = annot->bbox.y0;
2003 p2.x = annot->bbox.x1;
2004 p2.y = annot->bbox.y0;
2006 p3.x = annot->bbox.x1;
2007 p3.y = annot->bbox.y1;
2009 p4.x = annot->bbox.x0;
2010 p4.y = annot->bbox.y1;
2012 glColor3ub (0, 0, 128);
2013 stipplerect (&ctm, &p1, &p2, &p3, &p4, texcoords, vertices);
2016 glDisable (GL_BLEND);
2017 glDisable (GL_TEXTURE_1D);
2020 static int compareslinks (const void *l, const void *r)
2022 struct slink const *ls = l;
2023 struct slink const *rs = r;
2024 if (ls->bbox.y0 == rs->bbox.y0) {
2025 return rs->bbox.x0 - rs->bbox.x0;
2027 return ls->bbox.y0 - rs->bbox.y0;
2030 static void droptext (struct page *page)
2032 if (page->text) {
2033 fz_drop_stext_page (state.ctx, page->text);
2034 page->fmark = NULL;
2035 page->lmark = NULL;
2036 page->text = NULL;
2040 static void dropannots (struct page *page)
2042 if (page->annots) {
2043 free (page->annots);
2044 page->annots = NULL;
2045 page->annotcount = 0;
2049 static void ensureannots (struct page *page)
2051 int i, count = 0;
2052 size_t annotsize = sizeof (*page->annots);
2053 fz_annot *annot;
2055 if (state.gen != page->agen) {
2056 dropannots (page);
2057 page->agen = state.gen;
2059 if (page->annots) return;
2061 for (annot = fz_first_annot (state.ctx, page->fzpage);
2062 annot;
2063 annot = fz_next_annot (state.ctx, annot)) {
2064 count++;
2067 if (count > 0) {
2068 page->annotcount = count;
2069 page->annots = calloc (count, annotsize);
2070 if (!page->annots) {
2071 err (1, "calloc annots %d", count);
2074 for (annot = fz_first_annot (state.ctx, page->fzpage), i = 0;
2075 annot;
2076 annot = fz_next_annot (state.ctx, annot), i++) {
2077 fz_rect rect;
2079 fz_bound_annot (state.ctx, annot, &rect);
2080 page->annots[i].annot = annot;
2081 fz_round_rect (&page->annots[i].bbox, &rect);
2086 static void dropslinks (struct page *page)
2088 if (page->slinks) {
2089 free (page->slinks);
2090 page->slinks = NULL;
2091 page->slinkcount = 0;
2093 if (page->links) {
2094 fz_drop_link (state.ctx, page->links);
2095 page->links = NULL;
2099 static void ensureslinks (struct page *page)
2101 fz_matrix ctm;
2102 int i, count;
2103 size_t slinksize = sizeof (*page->slinks);
2104 fz_link *link;
2106 ensureannots (page);
2107 if (state.gen != page->sgen) {
2108 dropslinks (page);
2109 page->sgen = state.gen;
2111 if (page->slinks) return;
2113 ensurelinks (page);
2114 ctm = pagectm (page);
2116 count = page->annotcount;
2117 for (link = page->links; link; link = link->next) {
2118 count++;
2120 if (count > 0) {
2121 int j;
2123 page->slinkcount = count;
2124 page->slinks = calloc (count, slinksize);
2125 if (!page->slinks) {
2126 err (1, "calloc slinks %d", count);
2129 for (i = 0, link = page->links; link; ++i, link = link->next) {
2130 fz_rect rect;
2132 rect = link->rect;
2133 fz_transform_rect (&rect, &ctm);
2134 page->slinks[i].tag = SLINK;
2135 page->slinks[i].u.link = link;
2136 fz_round_rect (&page->slinks[i].bbox, &rect);
2138 for (j = 0; j < page->annotcount; ++j, ++i) {
2139 fz_rect rect;
2140 fz_bound_annot (state.ctx, page->annots[j].annot, &rect);
2141 fz_transform_rect (&rect, &ctm);
2142 fz_round_rect (&page->slinks[i].bbox, &rect);
2144 page->slinks[i].tag = SANNOT;
2145 page->slinks[i].u.annot = page->annots[j].annot;
2147 qsort (page->slinks, count, slinksize, compareslinks);
2151 static void highlightslinks (struct page *page, int xoff, int yoff,
2152 int noff, char *targ, mlsize_t tlen, int hfsize)
2154 char buf[40];
2155 struct slink *slink;
2156 float x0, y0, x1, y1, w;
2158 ensureslinks (page);
2159 glColor3ub (0xc3, 0xb0, 0x91);
2160 for (int i = 0; i < page->slinkcount; ++i) {
2161 fmt_linkn (buf, i + noff);
2162 if (!tlen || !strncmp (targ, buf, tlen)) {
2163 slink = &page->slinks[i];
2165 x0 = slink->bbox.x0 + xoff - 5;
2166 y1 = slink->bbox.y0 + yoff - 5;
2167 y0 = y1 + 10 + hfsize;
2168 w = measure_string (state.face, hfsize, buf);
2169 x1 = x0 + w + 10;
2170 recti ((int) x0, (int) y0, (int) x1, (int) y1);
2174 glEnable (GL_BLEND);
2175 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2176 glEnable (GL_TEXTURE_2D);
2177 glColor3ub (0, 0, 0);
2178 for (int i = 0; i < page->slinkcount; ++i) {
2179 fmt_linkn (buf, i + noff);
2180 if (!tlen || !strncmp (targ, buf, tlen)) {
2181 slink = &page->slinks[i];
2183 x0 = slink->bbox.x0 + xoff;
2184 y0 = slink->bbox.y0 + yoff + hfsize;
2185 draw_string (state.face, hfsize, x0, y0, buf);
2188 glDisable (GL_TEXTURE_2D);
2189 glDisable (GL_BLEND);
2192 static void uploadslice (struct tile *tile, struct slice *slice)
2194 int offset;
2195 struct slice *slice1;
2196 unsigned char *texdata;
2198 offset = 0;
2199 for (slice1 = tile->slices; slice != slice1; slice1++) {
2200 offset += slice1->h * tile->w * tile->pixmap->n;
2202 if (slice->texindex != -1 && slice->texindex < state.texcount
2203 && state.texowners[slice->texindex].slice == slice) {
2204 glBindTexture (TEXT_TYPE, state.texids[slice->texindex]);
2206 else {
2207 int subimage = 0;
2208 int texindex = state.texindex++ % state.texcount;
2210 if (state.texowners[texindex].w == tile->w) {
2211 if (state.texowners[texindex].h >= slice->h) {
2212 subimage = 1;
2214 else {
2215 state.texowners[texindex].h = slice->h;
2218 else {
2219 state.texowners[texindex].h = slice->h;
2222 state.texowners[texindex].w = tile->w;
2223 state.texowners[texindex].slice = slice;
2224 slice->texindex = texindex;
2226 glBindTexture (TEXT_TYPE, state.texids[texindex]);
2227 #if TEXT_TYPE == GL_TEXTURE_2D
2228 glTexParameteri (TEXT_TYPE, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2229 glTexParameteri (TEXT_TYPE, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2230 glTexParameteri (TEXT_TYPE, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2231 glTexParameteri (TEXT_TYPE, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2232 #endif
2233 if (tile->pbo) {
2234 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, tile->pbo->id);
2235 texdata = 0;
2237 else {
2238 texdata = tile->pixmap->samples;
2240 if (subimage) {
2241 glTexSubImage2D (TEXT_TYPE,
2245 tile->w,
2246 slice->h,
2247 state.texform,
2248 state.texty,
2249 texdata+offset
2252 else {
2253 glTexImage2D (TEXT_TYPE,
2255 state.texiform,
2256 tile->w,
2257 slice->h,
2259 state.texform,
2260 state.texty,
2261 texdata+offset
2264 if (tile->pbo) {
2265 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, 0);
2270 CAMLprim void ml_begintiles (value unit_v)
2272 CAMLparam1 (unit_v);
2273 glEnable (TEXT_TYPE);
2274 glTexCoordPointer (2, GL_FLOAT, 0, state.texcoords);
2275 glVertexPointer (2, GL_FLOAT, 0, state.vertices);
2276 CAMLreturn0;
2279 CAMLprim void ml_endtiles (value unit_v)
2281 CAMLparam1 (unit_v);
2282 glDisable (TEXT_TYPE);
2283 CAMLreturn0;
2286 CAMLprim void ml_drawtile (value args_v, value ptr_v)
2288 CAMLparam2 (args_v, ptr_v);
2289 int dispx = Int_val (Field (args_v, 0));
2290 int dispy = Int_val (Field (args_v, 1));
2291 int dispw = Int_val (Field (args_v, 2));
2292 int disph = Int_val (Field (args_v, 3));
2293 int tilex = Int_val (Field (args_v, 4));
2294 int tiley = Int_val (Field (args_v, 5));
2295 char *s = String_val (ptr_v);
2296 struct tile *tile = parse_pointer (__func__, s);
2297 int slicey, firstslice;
2298 struct slice *slice;
2299 GLfloat *texcoords = state.texcoords;
2300 GLfloat *vertices = state.vertices;
2302 firstslice = tiley / tile->sliceheight;
2303 slice = &tile->slices[firstslice];
2304 slicey = tiley % tile->sliceheight;
2306 while (disph > 0) {
2307 int dh;
2309 dh = slice->h - slicey;
2310 dh = fz_mini (disph, dh);
2311 uploadslice (tile, slice);
2313 texcoords[0] = tilex; texcoords[1] = slicey;
2314 texcoords[2] = tilex+dispw; texcoords[3] = slicey;
2315 texcoords[4] = tilex; texcoords[5] = slicey+dh;
2316 texcoords[6] = tilex+dispw; texcoords[7] = slicey+dh;
2318 vertices[0] = dispx; vertices[1] = dispy;
2319 vertices[2] = dispx+dispw; vertices[3] = dispy;
2320 vertices[4] = dispx; vertices[5] = dispy+dh;
2321 vertices[6] = dispx+dispw; vertices[7] = dispy+dh;
2323 #if TEXT_TYPE == GL_TEXTURE_2D
2324 for (int i = 0; i < 8; ++i) {
2325 texcoords[i] /= ((i & 1) == 0 ? tile->w : slice->h);
2327 #endif
2329 glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
2330 dispy += dh;
2331 disph -= dh;
2332 slice++;
2333 ARSERT (!(slice - tile->slices >= tile->slicecount && disph > 0));
2334 slicey = 0;
2336 CAMLreturn0;
2339 static void drawprect (struct page *page, int xoff, int yoff, value rects_v)
2341 fz_matrix ctm, tm, pm;
2342 fz_point p1, p2, p3, p4;
2343 GLfloat *vertices = state.vertices;
2344 double *v = (double *) rects_v;
2346 xoff -= state.pagedims[page->pdimno].bounds.x0;
2347 yoff -= state.pagedims[page->pdimno].bounds.y0;
2348 fz_translate (&tm, xoff, yoff);
2349 pm = pagectm (page);
2350 fz_concat (&ctm, &pm, &tm);
2352 glEnable (GL_BLEND);
2353 glVertexPointer (2, GL_FLOAT, 0, vertices);
2355 glColor4dv (v);
2356 p1.x = (float) v[4];
2357 p1.y = (float) v[5];
2359 p2.x = (float) v[6];
2360 p2.y = (float) v[5];
2362 p3.x = (float) v[6];
2363 p3.y = (float) v[7];
2365 p4.x = (float) v[4];
2366 p4.y = (float) v[7];
2367 solidrect (&ctm, &p1, &p2, &p3, &p4, vertices);
2368 glDisable (GL_BLEND);
2371 CAMLprim value ml_postprocess (value ptr_v, value hlinks_v,
2372 value xoff_v, value yoff_v,
2373 value li_v)
2375 CAMLparam5 (ptr_v, hlinks_v, xoff_v, yoff_v, li_v);
2376 int xoff = Int_val (xoff_v);
2377 int yoff = Int_val (yoff_v);
2378 int noff = Int_val (Field (li_v, 0));
2379 char *targ = String_val (Field (li_v, 1));
2380 mlsize_t tlen = caml_string_length (Field (li_v, 1));
2381 int hfsize = Int_val (Field (li_v, 2));
2382 char *s = String_val (ptr_v);
2383 int hlmask = Int_val (hlinks_v);
2384 struct page *page = parse_pointer (__func__, s);
2386 if (!page->fzpage) {
2387 /* deal with loadpage failed pages */
2388 goto done;
2391 if (trylock (__func__)) {
2392 noff = -1;
2393 goto done;
2396 ensureannots (page);
2397 if (hlmask & 1) highlightlinks (page, xoff, yoff);
2398 if (hlmask & 2) {
2399 highlightslinks (page, xoff, yoff, noff, targ, tlen, hfsize);
2400 noff = page->slinkcount;
2402 if (page->tgen == state.gen) {
2403 showsel (page, xoff, yoff);
2405 unlock (__func__);
2407 done:
2408 CAMLreturn (Val_int (noff));
2411 CAMLprim void ml_drawprect (value ptr_v, value xoff_v, value yoff_v,
2412 value rects_v)
2414 CAMLparam4 (ptr_v, xoff_v, yoff_v, rects_v);
2415 int xoff = Int_val (xoff_v);
2416 int yoff = Int_val (yoff_v);
2417 char *s = String_val (ptr_v);
2418 struct page *page = parse_pointer (__func__, s);
2420 drawprect (page, xoff, yoff, rects_v);
2421 CAMLreturn0;
2424 static struct annot *getannot (struct page *page, int x, int y)
2426 fz_point p;
2427 fz_matrix ctm;
2428 const fz_matrix *tctm;
2429 pdf_document *pdf = pdf_specifics (state.ctx, state.doc);
2431 if (!page->annots) return NULL;
2433 if (pdf) {
2434 trimctm (pdf_page_from_fz_page (state.ctx, page->fzpage), page->pdimno);
2435 tctm = &state.pagedims[page->pdimno].tctm;
2437 else {
2438 tctm = &fz_identity;
2441 p.x = x;
2442 p.y = y;
2444 fz_concat (&ctm, tctm, &state.pagedims[page->pdimno].ctm);
2445 fz_invert_matrix (&ctm, &ctm);
2446 fz_transform_point (&p, &ctm);
2448 if (pdf) {
2449 for (int i = 0; i < page->annotcount; ++i) {
2450 struct annot *a = &page->annots[i];
2451 fz_rect rect;
2453 fz_bound_annot (state.ctx, a->annot, &rect);
2454 if (p.x >= rect.x0 && p.x <= rect.x1) {
2455 if (p.y >= rect.y0 && p.y <= rect.y1)
2456 return a;
2460 return NULL;
2463 static fz_link *getlink (struct page *page, int x, int y)
2465 fz_point p;
2466 fz_matrix ctm;
2467 fz_link *link;
2469 ensureslinks (page);
2471 p.x = x;
2472 p.y = y;
2474 ctm = pagectm (page);
2475 fz_invert_matrix (&ctm, &ctm);
2476 fz_transform_point (&p, &ctm);
2478 for (link = page->links; link; link = link->next) {
2479 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
2480 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
2481 return link;
2485 return NULL;
2488 static void ensuretext (struct page *page)
2490 if (state.gen != page->tgen) {
2491 droptext (page);
2492 page->tgen = state.gen;
2494 if (!page->text) {
2495 fz_matrix ctm;
2496 fz_device *tdev;
2498 page->text = fz_new_stext_page (state.ctx,
2499 &state.pagedims[page->pdimno].mediabox);
2500 tdev = fz_new_stext_device (state.ctx, page->text, 0);
2501 ctm = pagectm (page);
2502 fz_run_display_list (state.ctx, page->dlist,
2503 tdev, &ctm, &fz_infinite_rect, NULL);
2504 fz_close_device (state.ctx, tdev);
2505 fz_drop_device (state.ctx, tdev);
2509 CAMLprim value ml_find_page_with_links (value start_page_v, value dir_v)
2511 CAMLparam2 (start_page_v, dir_v);
2512 CAMLlocal1 (ret_v);
2513 int i, dir = Int_val (dir_v);
2514 int start_page = Int_val (start_page_v);
2515 int end_page = dir > 0 ? state.pagecount : -1;
2516 pdf_document *pdf;
2518 fz_var (end_page);
2519 ret_v = Val_int (0);
2520 lock (__func__);
2521 pdf = pdf_specifics (state.ctx, state.doc);
2522 for (i = start_page + dir; i != end_page; i += dir) {
2523 int found;
2525 fz_var (found);
2526 if (pdf) {
2527 pdf_page *page = NULL;
2529 fz_var (page);
2530 fz_try (state.ctx) {
2531 page = pdf_load_page (state.ctx, pdf, i);
2532 found = !!page->links || !!page->annots;
2534 fz_catch (state.ctx) {
2535 found = 0;
2537 if (page) {
2538 fz_drop_page (state.ctx, &page->super);
2541 else {
2542 fz_page *page = fz_load_page (state.ctx, state.doc, i);
2543 fz_link *link = fz_load_links (state.ctx, page);
2544 found = !!link;
2545 fz_drop_link (state.ctx, link);
2546 fz_drop_page (state.ctx, page);
2549 if (found) {
2550 ret_v = caml_alloc_small (1, 1);
2551 Field (ret_v, 0) = Val_int (i);
2552 goto unlock;
2555 unlock:
2556 unlock (__func__);
2557 CAMLreturn (ret_v);
2560 enum { dir_first, dir_last };
2561 enum { dir_first_visible, dir_left, dir_right, dir_down, dir_up };
2563 CAMLprim value ml_findlink (value ptr_v, value dir_v)
2565 CAMLparam2 (ptr_v, dir_v);
2566 CAMLlocal2 (ret_v, pos_v);
2567 struct page *page;
2568 int dirtag, i, slinkindex;
2569 struct slink *found = NULL ,*slink;
2570 char *s = String_val (ptr_v);
2572 page = parse_pointer (__func__, s);
2573 ret_v = Val_int (0);
2574 lock (__func__);
2575 ensureslinks (page);
2577 if (Is_block (dir_v)) {
2578 dirtag = Tag_val (dir_v);
2579 switch (dirtag) {
2580 case dir_first_visible:
2582 int x0, y0, dir, first_index, last_index;
2584 pos_v = Field (dir_v, 0);
2585 x0 = Int_val (Field (pos_v, 0));
2586 y0 = Int_val (Field (pos_v, 1));
2587 dir = Int_val (Field (pos_v, 2));
2589 if (dir >= 0) {
2590 dir = 1;
2591 first_index = 0;
2592 last_index = page->slinkcount;
2594 else {
2595 first_index = page->slinkcount - 1;
2596 last_index = -1;
2599 for (i = first_index; i != last_index; i += dir) {
2600 slink = &page->slinks[i];
2601 if (slink->bbox.y0 >= y0 && slink->bbox.x0 >= x0) {
2602 found = slink;
2603 break;
2607 break;
2609 case dir_left:
2610 slinkindex = Int_val (Field (dir_v, 0));
2611 found = &page->slinks[slinkindex];
2612 for (i = slinkindex - 1; i >= 0; --i) {
2613 slink = &page->slinks[i];
2614 if (slink->bbox.x0 < found->bbox.x0) {
2615 found = slink;
2616 break;
2619 break;
2621 case dir_right:
2622 slinkindex = Int_val (Field (dir_v, 0));
2623 found = &page->slinks[slinkindex];
2624 for (i = slinkindex + 1; i < page->slinkcount; ++i) {
2625 slink = &page->slinks[i];
2626 if (slink->bbox.x0 > found->bbox.x0) {
2627 found = slink;
2628 break;
2631 break;
2633 case dir_down:
2634 slinkindex = Int_val (Field (dir_v, 0));
2635 found = &page->slinks[slinkindex];
2636 for (i = slinkindex + 1; i < page->slinkcount; ++i) {
2637 slink = &page->slinks[i];
2638 if (slink->bbox.y0 >= found->bbox.y0) {
2639 found = slink;
2640 break;
2643 break;
2645 case dir_up:
2646 slinkindex = Int_val (Field (dir_v, 0));
2647 found = &page->slinks[slinkindex];
2648 for (i = slinkindex - 1; i >= 0; --i) {
2649 slink = &page->slinks[i];
2650 if (slink->bbox.y0 <= found->bbox.y0) {
2651 found = slink;
2652 break;
2655 break;
2658 else {
2659 dirtag = Int_val (dir_v);
2660 switch (dirtag) {
2661 case dir_first:
2662 found = page->slinks;
2663 break;
2665 case dir_last:
2666 if (page->slinks) {
2667 found = page->slinks + (page->slinkcount - 1);
2669 break;
2672 if (found) {
2673 ret_v = caml_alloc_small (2, 1);
2674 Field (ret_v, 0) = Val_int (found - page->slinks);
2677 unlock (__func__);
2678 CAMLreturn (ret_v);
2681 enum { uuri, utext, uannot };
2683 CAMLprim value ml_getlink (value ptr_v, value n_v)
2685 CAMLparam2 (ptr_v, n_v);
2686 CAMLlocal4 (ret_v, tup_v, str_v, gr_v);
2687 fz_link *link;
2688 struct page *page;
2689 char *s = String_val (ptr_v);
2690 struct slink *slink;
2692 ret_v = Val_int (0);
2693 page = parse_pointer (__func__, s);
2695 lock (__func__);
2696 ensureslinks (page);
2697 slink = &page->slinks[Int_val (n_v)];
2698 if (slink->tag == SLINK) {
2699 link = slink->u.link;
2700 str_v = caml_copy_string (link->uri);
2701 ret_v = caml_alloc_small (1, uuri);
2702 Field (ret_v, 0) = str_v;
2704 else {
2705 ret_v = caml_alloc_small (1, uannot);
2706 tup_v = caml_alloc_tuple (2);
2707 Field (ret_v, 0) = tup_v;
2708 Field (tup_v, 0) = ptr_v;
2709 Field (tup_v, 1) = n_v;
2711 unlock (__func__);
2713 CAMLreturn (ret_v);
2716 CAMLprim value ml_getannotcontents (value ptr_v, value n_v)
2718 CAMLparam2 (ptr_v, n_v);
2719 CAMLlocal1 (ret_v);
2720 pdf_document *pdf;
2721 char *contents = NULL;
2723 lock (__func__);
2724 pdf = pdf_specifics (state.ctx, state.doc);
2725 if (pdf) {
2726 char *s = String_val (ptr_v);
2727 struct page *page;
2728 struct slink *slink;
2730 page = parse_pointer (__func__, s);
2731 slink = &page->slinks[Int_val (n_v)];
2732 contents = pdf_copy_annot_contents (state.ctx,
2733 (pdf_annot *) slink->u.annot);
2735 unlock (__func__);
2736 if (contents) {
2737 ret_v = caml_copy_string (contents);
2738 fz_free (state.ctx, contents);
2740 else {
2741 ret_v = caml_copy_string ("");
2743 CAMLreturn (ret_v);
2746 CAMLprim value ml_getlinkcount (value ptr_v)
2748 CAMLparam1 (ptr_v);
2749 struct page *page;
2750 char *s = String_val (ptr_v);
2752 page = parse_pointer (__func__, s);
2753 CAMLreturn (Val_int (page->slinkcount));
2756 CAMLprim value ml_getlinkrect (value ptr_v, value n_v)
2758 CAMLparam2 (ptr_v, n_v);
2759 CAMLlocal1 (ret_v);
2760 struct page *page;
2761 struct slink *slink;
2762 char *s = String_val (ptr_v);
2764 page = parse_pointer (__func__, s);
2765 ret_v = caml_alloc_tuple (4);
2766 lock (__func__);
2767 ensureslinks (page);
2769 slink = &page->slinks[Int_val (n_v)];
2770 Field (ret_v, 0) = Val_int (slink->bbox.x0);
2771 Field (ret_v, 1) = Val_int (slink->bbox.y0);
2772 Field (ret_v, 2) = Val_int (slink->bbox.x1);
2773 Field (ret_v, 3) = Val_int (slink->bbox.y1);
2774 unlock (__func__);
2775 CAMLreturn (ret_v);
2778 CAMLprim value ml_whatsunder (value ptr_v, value x_v, value y_v)
2780 CAMLparam3 (ptr_v, x_v, y_v);
2781 CAMLlocal4 (ret_v, tup_v, str_v, gr_v);
2782 fz_link *link;
2783 struct annot *annot;
2784 struct page *page;
2785 char *ptr = String_val (ptr_v);
2786 int x = Int_val (x_v), y = Int_val (y_v);
2787 struct pagedim *pdim;
2789 ret_v = Val_int (0);
2790 if (trylock (__func__)) {
2791 goto done;
2794 page = parse_pointer (__func__, ptr);
2795 pdim = &state.pagedims[page->pdimno];
2796 x += pdim->bounds.x0;
2797 y += pdim->bounds.y0;
2800 annot = getannot (page, x, y);
2801 if (annot) {
2802 int i, n = -1;
2804 ensureslinks (page);
2805 for (i = 0; i < page->slinkcount; ++i) {
2806 if (page->slinks[i].tag == SANNOT
2807 && page->slinks[i].u.annot == annot->annot) {
2808 n = i;
2809 break;
2812 ret_v = caml_alloc_small (1, uannot);
2813 tup_v = caml_alloc_tuple (2);
2814 Field (ret_v, 0) = tup_v;
2815 Field (tup_v, 0) = ptr_v;
2816 Field (tup_v, 1) = Val_int (n);
2817 goto unlock;
2821 link = getlink (page, x, y);
2822 if (link) {
2823 str_v = caml_copy_string (link->uri);
2824 ret_v = caml_alloc_small (1, uuri);
2825 Field (ret_v, 0) = str_v;
2827 else {
2828 fz_rect *b;
2829 fz_stext_block *block;
2831 ensuretext (page);
2833 for (block = page->text->first_block; block; block = block->next) {
2834 fz_stext_line *line;
2836 if (block->type != FZ_STEXT_BLOCK_TEXT) continue;
2837 b = &block->bbox;
2838 if (!(x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1))
2839 continue;
2841 for (line = block->u.t.first_line; line; line = line->next) {
2842 fz_stext_char *ch;
2844 b = &line->bbox;
2845 if (!(x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1))
2846 continue;
2848 for (ch = line->first_char; ch; ch = ch->next) {
2849 b = &ch->bbox;
2851 if (x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1) {
2852 const char *n2 = fz_font_name (state.ctx, ch->font);
2853 FT_FaceRec *face = fz_font_ft_face (state.ctx,
2854 ch->font);
2856 if (!n2) n2 = "<unknown font>";
2858 if (face && face->family_name) {
2859 char *s;
2860 char *n1 = face->family_name;
2861 size_t l1 = strlen (n1);
2862 size_t l2 = strlen (n2);
2864 if (l1 != l2 || memcmp (n1, n2, l1)) {
2865 s = malloc (l1 + l2 + 2);
2866 if (s) {
2867 memcpy (s, n2, l2);
2868 s[l2] = '=';
2869 memcpy (s + l2 + 1, n1, l1 + 1);
2870 str_v = caml_copy_string (s);
2871 free (s);
2875 if (str_v == Val_unit) {
2876 str_v = caml_copy_string (n2);
2878 ret_v = caml_alloc_small (1, utext);
2879 Field (ret_v, 0) = str_v;
2880 goto unlock;
2886 unlock:
2887 unlock (__func__);
2889 done:
2890 CAMLreturn (ret_v);
2893 enum { mark_page, mark_block, mark_line, mark_word };
2895 CAMLprim void ml_clearmark (value ptr_v)
2897 CAMLparam1 (ptr_v);
2898 char *s = String_val (ptr_v);
2899 struct page *page;
2901 if (trylock (__func__)) {
2902 goto done;
2905 page = parse_pointer (__func__, s);
2906 page->fmark = NULL;
2907 page->lmark = NULL;
2909 unlock (__func__);
2910 done:
2911 CAMLreturn0;
2914 static int uninteresting (int c)
2916 return c == ' ' || c == '\n' || c == '\t' || c == '\n' || c == '\r'
2917 || ispunct (c);
2920 CAMLprim value ml_markunder (value ptr_v, value x_v, value y_v, value mark_v)
2922 CAMLparam4 (ptr_v, x_v, y_v, mark_v);
2923 CAMLlocal1 (ret_v);
2924 fz_rect *b;
2925 struct page *page;
2926 fz_stext_line *line;
2927 fz_stext_block *block;
2928 struct pagedim *pdim;
2929 int mark = Int_val (mark_v);
2930 char *s = String_val (ptr_v);
2931 int x = Int_val (x_v), y = Int_val (y_v);
2933 ret_v = Val_bool (0);
2934 if (trylock (__func__)) {
2935 goto done;
2938 page = parse_pointer (__func__, s);
2939 pdim = &state.pagedims[page->pdimno];
2941 ensuretext (page);
2943 if (mark == mark_page) {
2944 page->fmark = page->text->first_block->u.t.first_line->first_char;
2945 page->lmark = page->text->last_block->u.t.last_line->last_char;
2946 ret_v = Val_bool (1);
2947 goto unlock;
2950 x += pdim->bounds.x0;
2951 y += pdim->bounds.y0;
2953 for (block = page->text->first_block; block; block = block->next) {
2954 if (block->type != FZ_STEXT_BLOCK_TEXT) continue;
2955 b = &block->bbox;
2956 if (!(x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1))
2957 continue;
2959 if (mark == mark_block) {
2960 page->fmark = block->u.t.first_line->first_char;
2961 page->lmark = block->u.t.last_line->last_char;
2962 ret_v = Val_bool (1);
2963 goto unlock;
2966 for (line = block->u.t.first_line; line; line = line->next) {
2967 fz_stext_char *ch;
2969 b = &line->bbox;
2970 if (!(x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1))
2971 continue;
2973 if (mark == mark_line) {
2974 page->fmark = line->first_char;
2975 page->lmark = line->last_char;
2976 ret_v = Val_bool (1);
2977 goto unlock;
2980 for (ch = line->first_char; ch; ch = ch->next) {
2981 fz_stext_char *ch2, *first = NULL, *last = NULL;
2982 b = &ch->bbox;
2983 if (x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1) {
2984 for (ch2 = line->first_char; ch2 != ch; ch2 = ch2->next) {
2985 if (uninteresting (ch2->c)) first = NULL;
2986 else if (!first) first = ch2;
2988 for (ch2 = ch; ch2; ch2 = ch2->next) {
2989 if (uninteresting (ch2->c)) break;
2990 last = ch2;
2993 page->fmark = first;
2994 page->lmark = last;
2995 ret_v = Val_bool (1);
2996 goto unlock;
3001 unlock:
3002 if (!Bool_val (ret_v)) {
3003 page->fmark = NULL;
3004 page->lmark = NULL;
3006 unlock (__func__);
3008 done:
3009 CAMLreturn (ret_v);
3012 CAMLprim value ml_rectofblock (value ptr_v, value x_v, value y_v)
3014 CAMLparam3 (ptr_v, x_v, y_v);
3015 CAMLlocal2 (ret_v, res_v);
3016 fz_rect *b = NULL;
3017 struct page *page;
3018 struct pagedim *pdim;
3019 fz_stext_block *block;
3020 char *s = String_val (ptr_v);
3021 int x = Int_val (x_v), y = Int_val (y_v);
3023 ret_v = Val_int (0);
3024 if (trylock (__func__)) {
3025 goto done;
3028 page = parse_pointer (__func__, s);
3029 pdim = &state.pagedims[page->pdimno];
3030 x += pdim->bounds.x0;
3031 y += pdim->bounds.y0;
3033 ensuretext (page);
3035 for (block = page->text->first_block; block; block = block->next) {
3036 switch (block->type) {
3037 case FZ_STEXT_BLOCK_TEXT:
3038 b = &block->bbox;
3039 break;
3041 case FZ_STEXT_BLOCK_IMAGE:
3042 b = &block->bbox;
3043 break;
3045 default:
3046 continue;
3049 if (x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1)
3050 break;
3051 b = NULL;
3053 if (b) {
3054 res_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
3055 ret_v = caml_alloc_small (1, 1);
3056 Store_double_field (res_v, 0, (double) b->x0);
3057 Store_double_field (res_v, 1, (double) b->x1);
3058 Store_double_field (res_v, 2, (double) b->y0);
3059 Store_double_field (res_v, 3, (double) b->y1);
3060 Field (ret_v, 0) = res_v;
3062 unlock (__func__);
3064 done:
3065 CAMLreturn (ret_v);
3068 CAMLprim void ml_seltext (value ptr_v, value rect_v)
3070 CAMLparam2 (ptr_v, rect_v);
3071 fz_rect b;
3072 struct page *page;
3073 struct pagedim *pdim;
3074 char *s = String_val (ptr_v);
3075 int x0, x1, y0, y1;
3076 fz_stext_char *ch;
3077 fz_stext_line *line;
3078 fz_stext_block *block;
3079 fz_stext_char *fc, *lc;
3081 if (trylock (__func__)) {
3082 goto done;
3085 page = parse_pointer (__func__, s);
3086 ensuretext (page);
3088 pdim = &state.pagedims[page->pdimno];
3089 x0 = Int_val (Field (rect_v, 0)) + pdim->bounds.x0;
3090 y0 = Int_val (Field (rect_v, 1)) + pdim->bounds.y0;
3091 x1 = Int_val (Field (rect_v, 2)) + pdim->bounds.x0;
3092 y1 = Int_val (Field (rect_v, 3)) + pdim->bounds.y0;
3094 if (y0 > y1) {
3095 int t = y0;
3096 y0 = y1;
3097 y1 = t;
3098 x0 = x1;
3099 x1 = t;
3102 fc = page->fmark;
3103 lc = page->lmark;
3105 for (block = page->text->first_block; block; block = block->next) {
3106 if (block->type != FZ_STEXT_BLOCK_TEXT) continue;
3107 for (line = block->u.t.first_line; line; line = line->next) {
3108 for (ch = line->first_char; ch; ch = ch->next) {
3109 b = ch->bbox;
3110 if (x0 >= b.x0 && x0 <= b.x1 && y0 >= b.y0 && y0 <= b.y1) {
3111 fc = ch;
3113 if (x1 >= b.x0 && x1 <= b.x1 && y1 >= b.y0 && y1 <= b.y1) {
3114 lc = ch;
3119 if (x1 < x0 && fc == lc) {
3120 fz_stext_char *t;
3122 t = fc;
3123 fc = lc;
3124 lc = t;
3127 page->fmark = fc;
3128 page->lmark = lc;
3130 unlock (__func__);
3132 done:
3133 CAMLreturn0;
3136 static int pipechar (FILE *f, fz_stext_char *ch)
3138 char buf[4];
3139 int len;
3140 size_t ret;
3142 len = fz_runetochar (buf, ch->c);
3143 ret = fwrite (buf, len, 1, f);
3144 if (ret != 1) {
3145 printd ("emsg failed to write %d bytes ret=%zu: %d:%s",
3146 len, ret, errno, strerror (errno));
3147 return -1;
3149 return 0;
3152 CAMLprim value ml_spawn (value command_v, value fds_v)
3154 CAMLparam2 (command_v, fds_v);
3155 CAMLlocal2 (l_v, tup_v);
3156 int ret, ret1;
3157 pid_t pid = (pid_t) -1;
3158 char *msg = NULL;
3159 value earg_v = Nothing;
3160 posix_spawnattr_t attr;
3161 posix_spawn_file_actions_t fa;
3162 char *argv[] = { "/bin/sh", "-c", NULL, NULL };
3164 argv[2] = String_val (command_v);
3166 if ((ret = posix_spawn_file_actions_init (&fa)) != 0) {
3167 unix_error (ret, "posix_spawn_file_actions_init", Nothing);
3170 if ((ret = posix_spawnattr_init (&attr)) != 0) {
3171 msg = "posix_spawnattr_init";
3172 goto fail1;
3175 #ifdef POSIX_SPAWN_USEVFORK
3176 if ((ret = posix_spawnattr_setflags (&attr, POSIX_SPAWN_USEVFORK)) != 0) {
3177 msg = "posix_spawnattr_setflags POSIX_SPAWN_USEVFORK";
3178 goto fail;
3180 #endif
3182 for (l_v = fds_v; l_v != Val_int (0); l_v = Field (l_v, 1)) {
3183 int fd1, fd2;
3185 tup_v = Field (l_v, 0);
3186 fd1 = Int_val (Field (tup_v, 0));
3187 fd2 = Int_val (Field (tup_v, 1));
3188 if (fd2 < 0) {
3189 if ((ret = posix_spawn_file_actions_addclose (&fa, fd1)) != 0) {
3190 msg = "posix_spawn_file_actions_addclose";
3191 earg_v = tup_v;
3192 goto fail;
3195 else {
3196 if ((ret = posix_spawn_file_actions_adddup2 (&fa, fd1, fd2)) != 0) {
3197 msg = "posix_spawn_file_actions_adddup2";
3198 earg_v = tup_v;
3199 goto fail;
3204 if ((ret = posix_spawn (&pid, "/bin/sh", &fa, &attr, argv, environ))) {
3205 msg = "posix_spawn";
3206 goto fail;
3209 fail:
3210 if ((ret1 = posix_spawnattr_destroy (&attr)) != 0) {
3211 printd ("emsg posix_spawnattr_destroy: %d:%s", ret1, strerror (ret1));
3214 fail1:
3215 if ((ret1 = posix_spawn_file_actions_destroy (&fa)) != 0) {
3216 printd ("emsg posix_spawn_file_actions_destroy: %d:%s",
3217 ret1, strerror (ret1));
3220 if (msg)
3221 unix_error (ret, msg, earg_v);
3223 CAMLreturn (Val_int (pid));
3226 CAMLprim value ml_hassel (value ptr_v)
3228 CAMLparam1 (ptr_v);
3229 CAMLlocal1 (ret_v);
3230 struct page *page;
3231 char *s = String_val (ptr_v);
3233 ret_v = Val_bool (0);
3234 if (trylock (__func__)) {
3235 goto done;
3238 page = parse_pointer (__func__, s);
3239 ret_v = Val_bool (page->fmark && page->lmark);
3240 unlock (__func__);
3241 done:
3242 CAMLreturn (ret_v);
3245 CAMLprim void ml_copysel (value fd_v, value ptr_v)
3247 CAMLparam2 (fd_v, ptr_v);
3248 FILE *f;
3249 int seen = 0;
3250 struct page *page;
3251 fz_stext_line *line;
3252 fz_stext_block *block;
3253 int fd = Int_val (fd_v);
3254 char *s = String_val (ptr_v);
3256 if (trylock (__func__)) {
3257 goto done;
3260 page = parse_pointer (__func__, s);
3262 if (!page->fmark || !page->lmark) {
3263 printd ("emsg nothing to copy on page %d", page->pageno);
3264 goto unlock;
3267 f = fdopen (fd, "w");
3268 if (!f) {
3269 printd ("emsg failed to fdopen sel pipe (from fd %d): %d:%s",
3270 fd, errno, strerror (errno));
3271 f = stdout;
3274 for (block = page->text->first_block; block; block = block->next) {
3275 if (block->type != FZ_STEXT_BLOCK_TEXT) continue;
3276 for (line = block->u.t.first_line; line; line = line->next) {
3277 fz_stext_char *ch;
3278 for (ch = line->first_char; ch; ch = ch->next) {
3279 if (seen || ch == page->fmark) {
3280 do {
3281 pipechar (f, ch);
3282 if (ch == page->lmark) goto close;
3283 } while ((ch = ch->next));
3284 seen = 1;
3285 break;
3288 if (seen) fputc ('\n', f);
3291 close:
3292 if (f != stdout) {
3293 int ret = fclose (f);
3294 fd = -1;
3295 if (ret == -1) {
3296 if (errno != ECHILD) {
3297 printd ("emsg failed to close sel pipe: %d:%s",
3298 errno, strerror (errno));
3302 unlock:
3303 unlock (__func__);
3305 done:
3306 if (fd >= 0) {
3307 if (close (fd)) {
3308 printd ("emsg failed to close sel pipe: %d:%s",
3309 errno, strerror (errno));
3312 CAMLreturn0;
3315 CAMLprim value ml_getpdimrect (value pagedimno_v)
3317 CAMLparam1 (pagedimno_v);
3318 CAMLlocal1 (ret_v);
3319 int pagedimno = Int_val (pagedimno_v);
3320 fz_rect box;
3322 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
3323 if (trylock (__func__)) {
3324 box = fz_empty_rect;
3326 else {
3327 box = state.pagedims[pagedimno].mediabox;
3328 unlock (__func__);
3331 Store_double_field (ret_v, 0, (double) box.x0);
3332 Store_double_field (ret_v, 1, (double) box.x1);
3333 Store_double_field (ret_v, 2, (double) box.y0);
3334 Store_double_field (ret_v, 3, (double) box.y1);
3336 CAMLreturn (ret_v);
3339 CAMLprim value ml_zoom_for_height (value winw_v, value winh_v,
3340 value dw_v, value cols_v)
3342 CAMLparam4 (winw_v, winh_v, dw_v, cols_v);
3343 CAMLlocal1 (ret_v);
3344 int i;
3345 float zoom = -1.;
3346 float maxh = 0.0;
3347 struct pagedim *p;
3348 float winw = Int_val (winw_v);
3349 float winh = Int_val (winh_v);
3350 float dw = Int_val (dw_v);
3351 float cols = Int_val (cols_v);
3352 float pw = 1.0, ph = 1.0;
3354 if (trylock (__func__)) {
3355 goto done;
3358 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
3359 float w = p->pagebox.x1 / cols;
3360 float h = p->pagebox.y1;
3361 if (h > maxh) {
3362 maxh = h;
3363 ph = h;
3364 if (state.fitmodel != FitProportional) pw = w;
3366 if ((state.fitmodel == FitProportional) && w > pw) pw = w;
3369 zoom = (((winh / ph) * pw) + dw) / winw;
3370 unlock (__func__);
3371 done:
3372 ret_v = caml_copy_double ((double) zoom);
3373 CAMLreturn (ret_v);
3376 CAMLprim value ml_getmaxw (value unit_v)
3378 CAMLparam1 (unit_v);
3379 CAMLlocal1 (ret_v);
3380 int i;
3381 float maxw = -1.;
3382 struct pagedim *p;
3384 if (trylock (__func__)) {
3385 goto done;
3388 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
3389 float w = p->pagebox.x1;
3390 maxw = fz_max (maxw, w);
3393 unlock (__func__);
3394 done:
3395 ret_v = caml_copy_double ((double) maxw);
3396 CAMLreturn (ret_v);
3399 CAMLprim value ml_draw_string (value pt_v, value x_v, value y_v, value string_v)
3401 CAMLparam4 (pt_v, x_v, y_v, string_v);
3402 CAMLlocal1 (ret_v);
3403 int pt = Int_val(pt_v);
3404 int x = Int_val (x_v);
3405 int y = Int_val (y_v);
3406 double w;
3408 w = (double) draw_string (state.face, pt, x, y, String_val (string_v));
3409 ret_v = caml_copy_double (w);
3410 CAMLreturn (ret_v);
3413 CAMLprim value ml_measure_string (value pt_v, value string_v)
3415 CAMLparam2 (pt_v, string_v);
3416 CAMLlocal1 (ret_v);
3417 int pt = Int_val (pt_v);
3418 double w;
3420 w = (double) measure_string (state.face, pt, String_val (string_v));
3421 ret_v = caml_copy_double (w);
3422 CAMLreturn (ret_v);
3425 CAMLprim value ml_getpagebox (value opaque_v)
3427 CAMLparam1 (opaque_v);
3428 CAMLlocal1 (ret_v);
3429 fz_rect rect;
3430 fz_irect bbox;
3431 fz_matrix ctm;
3432 fz_device *dev;
3433 char *s = String_val (opaque_v);
3434 struct page *page = parse_pointer (__func__, s);
3436 ret_v = caml_alloc_tuple (4);
3437 dev = fz_new_bbox_device (state.ctx, &rect);
3439 ctm = pagectm (page);
3440 fz_run_page (state.ctx, page->fzpage, dev, &ctm, NULL);
3442 fz_close_device (state.ctx, dev);
3443 fz_drop_device (state.ctx, dev);
3444 fz_round_rect (&bbox, &rect);
3445 Field (ret_v, 0) = Val_int (bbox.x0);
3446 Field (ret_v, 1) = Val_int (bbox.y0);
3447 Field (ret_v, 2) = Val_int (bbox.x1);
3448 Field (ret_v, 3) = Val_int (bbox.y1);
3450 CAMLreturn (ret_v);
3453 CAMLprim void ml_setaalevel (value level_v)
3455 CAMLparam1 (level_v);
3457 state.aalevel = Int_val (level_v);
3458 CAMLreturn0;
3461 #ifndef __COCOA__
3462 #pragma GCC diagnostic push
3463 #pragma GCC diagnostic ignored "-Wvariadic-macros"
3464 #include <X11/Xlib.h>
3465 #include <X11/cursorfont.h>
3466 #pragma GCC diagnostic pop
3468 #include <GL/glx.h>
3470 static const int shapes[] = {
3471 XC_left_ptr, XC_hand2, XC_exchange, XC_fleur, XC_xterm
3474 #define CURS_COUNT (sizeof (shapes) / sizeof (shapes[0]))
3476 static struct {
3477 Window wid;
3478 Display *dpy;
3479 GLXContext ctx;
3480 XVisualInfo *visual;
3481 Cursor curs[CURS_COUNT];
3482 } glx;
3485 static void initcurs (void)
3487 for (size_t n = 0; n < CURS_COUNT; ++n) {
3488 glx.curs[n] = XCreateFontCursor (glx.dpy, shapes[n]);
3492 CAMLprim value ml_glxinit (value display_v, value wid_v, value screen_v)
3494 CAMLparam3 (display_v, wid_v, screen_v);
3496 glx.dpy = XOpenDisplay (String_val (display_v));
3497 if (!glx.dpy) {
3498 caml_failwith ("XOpenDisplay");
3501 int attribs[] = { GLX_RGBA, GLX_DOUBLEBUFFER, None };
3502 glx.visual = glXChooseVisual (glx.dpy, Int_val (screen_v), attribs);
3503 if (!glx.visual) {
3504 XCloseDisplay (glx.dpy);
3505 caml_failwith ("glXChooseVisual");
3508 initcurs ();
3510 glx.wid = Int_val (wid_v);
3511 CAMLreturn (Val_int (glx.visual->visualid));
3514 CAMLprim void ml_glxcompleteinit (value unit_v)
3516 CAMLparam1 (unit_v);
3518 glx.ctx = glXCreateContext (glx.dpy, glx.visual, NULL, True);
3519 if (!glx.ctx) {
3520 caml_failwith ("glXCreateContext");
3523 XFree (glx.visual);
3524 glx.visual = NULL;
3526 if (!glXMakeCurrent (glx.dpy, glx.wid, glx.ctx)) {
3527 glXDestroyContext (glx.dpy, glx.ctx);
3528 glx.ctx = NULL;
3529 caml_failwith ("glXMakeCurrent");
3531 CAMLreturn0;
3534 CAMLprim void ml_setcursor (value cursor_v)
3536 CAMLparam1 (cursor_v);
3537 size_t cursn = Int_val (cursor_v);
3539 if (cursn >= CURS_COUNT) caml_failwith ("cursor index out of range");
3540 XDefineCursor (glx.dpy, glx.wid, glx.curs[cursn]);
3541 XFlush (glx.dpy);
3542 CAMLreturn0;
3545 CAMLprim void ml_swapb (value unit_v)
3547 CAMLparam1 (unit_v);
3548 glXSwapBuffers (glx.dpy, glx.wid);
3549 CAMLreturn0;
3552 long keysym2ucs (KeySym);
3553 CAMLprim value ml_keysymtoutf8 (value keysym_v)
3555 CAMLparam1 (keysym_v);
3556 CAMLlocal1 (str_v);
3557 KeySym keysym = Int_val (keysym_v);
3558 Rune rune;
3559 int len;
3560 char buf[5];
3562 rune = (Rune) keysym2ucs (keysym);
3563 len = fz_runetochar (buf, rune);
3564 buf[len] = 0;
3565 str_v = caml_copy_string (buf);
3566 CAMLreturn (str_v);
3568 #else
3569 CAMLprim value ml_keysymtoutf8 (value keysym_v)
3571 CAMLparam1 (keysym_v);
3572 CAMLlocal1 (str_v);
3573 long ucs_v = Long_val (keysym_v);
3574 int len;
3575 char buf[5];
3577 len = fz_runetochar (buf, (int) ucs_v);
3578 buf[len] = 0;
3579 str_v = caml_copy_string (buf);
3580 CAMLreturn (str_v);
3582 #endif
3584 enum { piunknown, pilinux, piosx, pisun, pibsd };
3586 CAMLprim value ml_platform (value unit_v)
3588 CAMLparam1 (unit_v);
3589 CAMLlocal2 (tup_v, arr_v);
3590 int platid = piunknown;
3591 struct utsname buf;
3593 #if defined __linux__
3594 platid = pilinux;
3595 #elif defined __DragonFly__ || defined __FreeBSD__
3596 || defined __OpenBSD__ || defined __NetBSD__
3597 platid = pibsd;
3598 #elif defined __sun__
3599 platid = pisun;
3600 #elif defined __APPLE__
3601 platid = piosx;
3602 #endif
3603 if (uname (&buf)) err (1, "uname");
3605 tup_v = caml_alloc_tuple (2);
3607 char const *sar[] = {
3608 buf.sysname,
3609 buf.release,
3610 buf.version,
3611 buf.machine,
3612 NULL
3614 arr_v = caml_copy_string_array (sar);
3616 Field (tup_v, 0) = Val_int (platid);
3617 Field (tup_v, 1) = arr_v;
3618 CAMLreturn (tup_v);
3621 CAMLprim void ml_cloexec (value fd_v)
3623 CAMLparam1 (fd_v);
3624 int fd = Int_val (fd_v);
3626 if (fcntl (fd, F_SETFD, FD_CLOEXEC, 1)) {
3627 uerror ("fcntl", Nothing);
3629 CAMLreturn0;
3632 CAMLprim value ml_getpbo (value w_v, value h_v, value cs_v)
3634 CAMLparam2 (w_v, h_v);
3635 CAMLlocal1 (ret_v);
3636 struct bo *pbo;
3637 int w = Int_val (w_v);
3638 int h = Int_val (h_v);
3639 int cs = Int_val (cs_v);
3641 if (state.bo_usable) {
3642 pbo = calloc (sizeof (*pbo), 1);
3643 if (!pbo) {
3644 err (1, "calloc pbo");
3647 switch (cs) {
3648 case 0:
3649 case 1:
3650 pbo->size = w*h*4;
3651 break;
3652 case 2:
3653 pbo->size = w*h*2;
3654 break;
3655 default:
3656 errx (1, "%s: invalid colorspace %d", __func__, cs);
3659 state.glGenBuffersARB (1, &pbo->id);
3660 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, pbo->id);
3661 state.glBufferDataARB (GL_PIXEL_UNPACK_BUFFER_ARB, (GLsizei) pbo->size,
3662 NULL, GL_STREAM_DRAW);
3663 pbo->ptr = state.glMapBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB,
3664 GL_READ_WRITE);
3665 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, 0);
3666 if (!pbo->ptr) {
3667 printd ("emsg glMapBufferARB failed: %#x", glGetError ());
3668 state.glDeleteBuffersARB (1, &pbo->id);
3669 free (pbo);
3670 ret_v = caml_copy_string ("0");
3672 else {
3673 int res;
3674 char *s;
3676 res = snprintf (NULL, 0, "%" PRIxPTR, (uintptr_t) pbo);
3677 if (res < 0) {
3678 err (1, "snprintf %" PRIxPTR " failed", (uintptr_t) pbo);
3680 s = malloc (res+1);
3681 if (!s) {
3682 err (1, "malloc %d bytes failed", res+1);
3684 res = sprintf (s, "%" PRIxPTR, (uintptr_t) pbo);
3685 if (res < 0) {
3686 err (1, "sprintf %" PRIxPTR " failed", (uintptr_t) pbo);
3688 ret_v = caml_copy_string (s);
3689 free (s);
3692 else {
3693 ret_v = caml_copy_string ("0");
3695 CAMLreturn (ret_v);
3698 CAMLprim void ml_freepbo (value s_v)
3700 CAMLparam1 (s_v);
3701 char *s = String_val (s_v);
3702 struct tile *tile = parse_pointer (__func__, s);
3704 if (tile->pbo) {
3705 state.glDeleteBuffersARB (1, &tile->pbo->id);
3706 tile->pbo->id = -1;
3707 tile->pbo->ptr = NULL;
3708 tile->pbo->size = -1;
3710 CAMLreturn0;
3713 CAMLprim void ml_unmappbo (value s_v)
3715 CAMLparam1 (s_v);
3716 char *s = String_val (s_v);
3717 struct tile *tile = parse_pointer (__func__, s);
3719 if (tile->pbo) {
3720 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, tile->pbo->id);
3721 if (state.glUnmapBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB) == GL_FALSE) {
3722 errx (1, "glUnmapBufferARB failed: %#x\n", glGetError ());
3724 tile->pbo->ptr = NULL;
3725 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, 0);
3727 CAMLreturn0;
3730 static void setuppbo (void)
3732 #ifdef __clang__
3733 #pragma GCC diagnostic ignored "-Wunused-macros"
3734 #endif
3735 #ifdef __COCOA__
3736 static CFBundleRef framework = NULL;
3737 if (framework == NULL)
3738 framework = CFBundleGetBundleWithIdentifier (CFSTR ("com.apple.opengl"));
3739 #define GGPA(n) \
3740 (&state.n = CFBundleGetFunctionPointerForName (framework, CFSTR (#n)))
3741 #else
3742 #define GGPA(n) \
3743 (*(void (**) (void)) &state.n = glXGetProcAddress ((GLubyte *) #n))
3744 #endif
3745 state.bo_usable = GGPA (glBindBufferARB)
3746 && GGPA (glUnmapBufferARB)
3747 && GGPA (glMapBufferARB)
3748 && GGPA (glBufferDataARB)
3749 && GGPA (glGenBuffersARB)
3750 && GGPA (glDeleteBuffersARB);
3751 #undef GGPA
3754 CAMLprim value ml_bo_usable (value unit_v)
3756 CAMLparam1 (unit_v);
3757 CAMLreturn (Val_bool (state.bo_usable));
3760 CAMLprim value ml_unproject (value ptr_v, value x_v, value y_v)
3762 CAMLparam3 (ptr_v, x_v, y_v);
3763 CAMLlocal2 (ret_v, tup_v);
3764 struct page *page;
3765 char *s = String_val (ptr_v);
3766 int x = Int_val (x_v), y = Int_val (y_v);
3767 struct pagedim *pdim;
3768 fz_point p;
3769 fz_matrix ctm;
3771 page = parse_pointer (__func__, s);
3772 pdim = &state.pagedims[page->pdimno];
3774 ret_v = Val_int (0);
3775 if (trylock (__func__)) {
3776 goto done;
3779 if (pdf_specifics (state.ctx, state.doc)) {
3780 trimctm (pdf_page_from_fz_page (state.ctx, page->fzpage), page->pdimno);
3781 ctm = state.pagedims[page->pdimno].tctm;
3783 else {
3784 ctm = fz_identity;
3786 p.x = x + pdim->bounds.x0;
3787 p.y = y + pdim->bounds.y0;
3789 fz_concat (&ctm, &pdim->tctm, &pdim->ctm);
3790 fz_invert_matrix (&ctm, &ctm);
3791 fz_transform_point (&p, &ctm);
3793 tup_v = caml_alloc_tuple (2);
3794 ret_v = caml_alloc_small (1, 1);
3795 Field (tup_v, 0) = Val_int (p.x);
3796 Field (tup_v, 1) = Val_int (p.y);
3797 Field (ret_v, 0) = tup_v;
3799 unlock (__func__);
3800 done:
3801 CAMLreturn (ret_v);
3804 CAMLprim value ml_project (value ptr_v, value pageno_v, value pdimno_v,
3805 value x_v, value y_v)
3807 CAMLparam5 (ptr_v, pageno_v, pdimno_v, x_v, y_v);
3808 CAMLlocal1 (ret_v);
3809 struct page *page;
3810 char *s = String_val (ptr_v);
3811 int pageno = Int_val (pageno_v);
3812 int pdimno = Int_val (pdimno_v);
3813 float x = (float) Double_val (x_v), y = (float) Double_val (y_v);
3814 struct pagedim *pdim;
3815 fz_point p;
3816 fz_matrix ctm;
3818 ret_v = Val_int (0);
3819 lock (__func__);
3821 if (!*s) {
3822 page = loadpage (pageno, pdimno);
3824 else {
3825 page = parse_pointer (__func__, s);
3827 pdim = &state.pagedims[pdimno];
3829 if (pdf_specifics (state.ctx, state.doc)) {
3830 trimctm (pdf_page_from_fz_page (state.ctx, page->fzpage), page->pdimno);
3831 ctm = state.pagedims[page->pdimno].tctm;
3833 else {
3834 ctm = fz_identity;
3836 p.x = x + pdim->bounds.x0;
3837 p.y = y + pdim->bounds.y0;
3839 fz_concat (&ctm, &pdim->tctm, &pdim->ctm);
3840 fz_transform_point (&p, &ctm);
3842 ret_v = caml_alloc_tuple (2);
3843 Field (ret_v, 0) = caml_copy_double ((double) p.x);
3844 Field (ret_v, 1) = caml_copy_double ((double) p.y);
3846 if (!*s) {
3847 freepage (page);
3849 unlock (__func__);
3850 CAMLreturn (ret_v);
3853 CAMLprim void ml_addannot (value ptr_v, value x_v, value y_v,
3854 value contents_v)
3856 CAMLparam4 (ptr_v, x_v, y_v, contents_v);
3857 pdf_document *pdf = pdf_specifics (state.ctx, state.doc);
3859 if (pdf) {
3860 pdf_annot *annot;
3861 struct page *page;
3862 fz_point p;
3863 char *s = String_val (ptr_v);
3865 page = parse_pointer (__func__, s);
3866 annot = pdf_create_annot (state.ctx,
3867 pdf_page_from_fz_page (state.ctx,
3868 page->fzpage),
3869 PDF_ANNOT_TEXT);
3870 p.x = Int_val (x_v);
3871 p.y = Int_val (y_v);
3872 pdf_set_annot_contents (state.ctx, annot, String_val (contents_v));
3873 pdf_set_text_annot_position (state.ctx, annot, p);
3874 state.dirty = 1;
3876 CAMLreturn0;
3879 CAMLprim void ml_delannot (value ptr_v, value n_v)
3881 CAMLparam2 (ptr_v, n_v);
3882 pdf_document *pdf = pdf_specifics (state.ctx, state.doc);
3884 if (pdf) {
3885 struct page *page;
3886 char *s = String_val (ptr_v);
3887 struct slink *slink;
3889 page = parse_pointer (__func__, s);
3890 slink = &page->slinks[Int_val (n_v)];
3891 pdf_delete_annot (state.ctx,
3892 pdf_page_from_fz_page (state.ctx, page->fzpage),
3893 (pdf_annot *) slink->u.annot);
3894 state.dirty = 1;
3896 CAMLreturn0;
3899 CAMLprim void ml_modannot (value ptr_v, value n_v, value str_v)
3901 CAMLparam3 (ptr_v, n_v, str_v);
3902 pdf_document *pdf = pdf_specifics (state.ctx, state.doc);
3904 if (pdf) {
3905 struct page *page;
3906 char *s = String_val (ptr_v);
3907 struct slink *slink;
3909 page = parse_pointer (__func__, s);
3910 slink = &page->slinks[Int_val (n_v)];
3911 pdf_set_annot_contents (state.ctx, (pdf_annot *) slink->u.annot,
3912 String_val (str_v));
3913 state.dirty = 1;
3915 CAMLreturn0;
3918 CAMLprim value ml_hasunsavedchanges (value unit_v)
3920 CAMLparam1 (unit_v);
3921 CAMLreturn (Val_bool (state.dirty));
3924 CAMLprim void ml_savedoc (value path_v)
3926 CAMLparam1 (path_v);
3927 pdf_document *pdf = pdf_specifics (state.ctx, state.doc);
3929 if (pdf) {
3930 pdf_save_document (state.ctx, pdf, String_val (path_v), NULL);
3932 CAMLreturn0;
3935 static void makestippletex (void)
3937 const char pixels[] = "\xff\xff\0\0";
3938 glGenTextures (1, &state.stid);
3939 glBindTexture (GL_TEXTURE_1D, state.stid);
3940 glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
3941 glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
3942 glTexImage1D (
3943 GL_TEXTURE_1D,
3945 GL_ALPHA,
3948 GL_ALPHA,
3949 GL_UNSIGNED_BYTE,
3950 pixels
3954 CAMLprim value ml_fz_version (void)
3956 return caml_copy_string (FZ_VERSION);
3959 CAMLprim value ml_llpp_version (void)
3961 extern char llpp_version[];
3962 return caml_copy_string (llpp_version);
3965 CAMLprim void ml_init (value csock_v, value params_v)
3967 CAMLparam2 (csock_v, params_v);
3968 CAMLlocal2 (trim_v, fuzz_v);
3969 int ret;
3970 int texcount;
3971 char *fontpath;
3972 int colorspace;
3973 int mustoresize;
3974 int haspboext;
3976 state.csock = Int_val (csock_v);
3977 state.rotate = Int_val (Field (params_v, 0));
3978 state.fitmodel = Int_val (Field (params_v, 1));
3979 trim_v = Field (params_v, 2);
3980 texcount = Int_val (Field (params_v, 3));
3981 state.sliceheight = Int_val (Field (params_v, 4));
3982 mustoresize = Int_val (Field (params_v, 5));
3983 colorspace = Int_val (Field (params_v, 6));
3984 fontpath = String_val (Field (params_v, 7));
3986 #ifdef __COCOA__
3987 state.utf8cs = 1;
3988 #else
3989 /* http://www.cl.cam.ac.uk/~mgk25/unicode.html */
3990 if (setlocale (LC_CTYPE, "")) {
3991 const char *cset = nl_langinfo (CODESET);
3992 state.utf8cs = !strcmp (cset, "UTF-8");
3994 else {
3995 printd ("emsg setlocale: %d:%s", errno, strerror (errno));
3997 #endif
3999 if (caml_string_length (Field (params_v, 8)) > 0) {
4000 state.trimcachepath = ystrdup (String_val (Field (params_v, 8)));
4002 if (!state.trimcachepath) {
4003 printd ("emsg failed to strdup trimcachepath: %d:%s",
4004 errno, strerror (errno));
4008 haspboext = Bool_val (Field (params_v, 9));
4010 state.ctx = fz_new_context (NULL, NULL, mustoresize);
4011 fz_register_document_handlers (state.ctx);
4013 state.trimmargins = Bool_val (Field (trim_v, 0));
4014 fuzz_v = Field (trim_v, 1);
4015 state.trimfuzz.x0 = Int_val (Field (fuzz_v, 0));
4016 state.trimfuzz.y0 = Int_val (Field (fuzz_v, 1));
4017 state.trimfuzz.x1 = Int_val (Field (fuzz_v, 2));
4018 state.trimfuzz.y1 = Int_val (Field (fuzz_v, 3));
4020 set_tex_params (colorspace);
4022 if (*fontpath) {
4023 state.face = load_font (fontpath);
4025 else {
4026 int len;
4027 const unsigned char *data;
4029 data = pdf_lookup_substitute_font (state.ctx, 0, 0, 0, 0, &len);
4030 state.face = load_builtin_font (data, len);
4032 if (!state.face) _exit (1);
4034 realloctexts (texcount);
4036 if (haspboext) {
4037 setuppbo ();
4040 makestippletex ();
4042 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
4043 if (ret) {
4044 errx (1, "pthread_create: %s", strerror (ret));
4047 CAMLreturn0;
4050 #if FIXME || !FIXME
4051 static void UNUSED_ATTR NO_OPTIMIZE_ATTR refmacs (void) {}
4052 #endif