Revert "Condense"
[llpp.git] / link.c
blob0ebbc1da873545b160e647aaf593bb230dabd8a8
1 /* lots of code c&p-ed directly from mupdf */
2 extern char **environ;
4 #ifdef __clang__
5 #pragma GCC diagnostic error "-Weverything"
6 #pragma GCC diagnostic ignored "-Wpadded"
7 #pragma GCC diagnostic ignored "-Wsign-conversion"
8 #pragma GCC diagnostic ignored "-Wdocumentation-unknown-command"
9 #pragma GCC diagnostic ignored "-Wdocumentation"
10 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
11 #endif
13 #define CAML_NAME_SPACE
14 #define FIXME 0
16 #include <errno.h>
17 #include <stdio.h>
18 #include <ctype.h>
19 #include <string.h>
20 #include <stdlib.h>
21 #include <signal.h>
23 #include <math.h>
24 #include <wchar.h>
25 #include <locale.h>
26 #include <langinfo.h>
28 #include <unistd.h>
29 #include <pthread.h>
30 #include <sys/uio.h>
31 #include <sys/time.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <sys/types.h>
35 #include <sys/ioctl.h>
36 #include <sys/utsname.h>
38 #include <spawn.h>
40 #include <regex.h>
41 #include <stdarg.h>
42 #include <limits.h>
43 #include <inttypes.h>
45 #ifdef __COCOA__
46 #include <CoreFoundation/CoreFoundation.h>
47 #endif
49 #ifdef __APPLE__
50 #include <OpenGL/gl.h>
51 #else
52 #include <GL/gl.h>
53 #endif
55 #pragma GCC diagnostic push
56 #ifdef __clang__
57 #pragma GCC diagnostic ignored "-Wreserved-id-macro"
58 #endif
59 #pragma GCC diagnostic ignored "-Wpedantic"
60 #include <caml/fail.h>
61 #include <caml/alloc.h>
62 #include <caml/memory.h>
63 #include <caml/unixsupport.h>
65 #pragma GCC diagnostic push
66 #pragma GCC diagnostic ignored "-Wfloat-equal"
67 #include <mupdf/fitz.h>
68 #include <mupdf/pdf.h>
69 #pragma GCC diagnostic pop
71 #include <ft2build.h>
72 #include FT_FREETYPE_H
73 #pragma GCC diagnostic pop
75 #include "cutils.h"
77 #define PIGGYBACK
78 #define CACHE_PAGEREFS
80 #ifdef USE_NPOT
81 #define TEXT_TYPE GL_TEXTURE_2D
82 #else
83 #define TEXT_TYPE GL_TEXTURE_RECTANGLE_ARB
84 #endif
86 #if 0
87 #define lprintf printf
88 #else
89 #define lprintf(...)
90 #endif
92 #define ARSERT(cond) for (;;) { \
93 if (!(cond)) { \
94 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
95 } \
96 break; \
99 struct slice {
100 int h;
101 int texindex;
104 struct tile {
105 int w, h;
106 int slicecount;
107 int sliceheight;
108 struct bo *pbo;
109 fz_pixmap *pixmap;
110 struct slice slices[1];
113 struct pagedim {
114 int pageno;
115 int rotate;
116 int left;
117 int tctmready;
118 fz_irect bounds;
119 fz_rect pagebox;
120 fz_rect mediabox;
121 fz_matrix ctm, zoomctm, tctm;
124 struct slink {
125 enum { SLINK, SANNOT } tag;
126 fz_irect bbox;
127 union {
128 fz_link *link;
129 fz_annot *annot;
130 } u;
133 struct annot {
134 fz_irect bbox;
135 fz_annot *annot;
138 struct page {
139 int tgen;
140 int sgen;
141 int agen;
142 int pageno;
143 int pdimno;
144 fz_stext_page *text;
145 fz_page *fzpage;
146 fz_display_list *dlist;
147 fz_link *links;
148 int slinkcount;
149 struct slink *slinks;
150 int annotcount;
151 struct annot *annots;
152 fz_stext_char *fmark, *lmark;
155 enum { FitWidth, FitProportional, FitPage };
157 static struct {
158 int sliceheight;
159 struct pagedim *pagedims;
160 int pagecount;
161 int pagedimcount;
162 fz_document *doc;
163 fz_context *ctx;
164 int w, h;
166 int texindex;
167 int texcount;
168 GLuint *texids;
170 GLenum texiform;
171 GLenum texform;
172 GLenum texty;
174 fz_colorspace *colorspace;
176 struct {
177 int w, h;
178 struct slice *slice;
179 } *texowners;
181 int rotate;
182 int fitmodel;
183 int trimmargins;
184 int needoutline;
185 int gen;
186 int aalevel;
188 int trimanew;
189 fz_irect trimfuzz;
190 fz_pixmap *pig;
192 pthread_t thread;
193 int csock;
194 FT_Face face;
196 char *trimcachepath;
197 int dirty;
199 GLuint stid;
201 int bo_usable;
202 GLuint boid;
204 void (*glBindBufferARB) (GLenum, GLuint);
205 GLboolean (*glUnmapBufferARB) (GLenum);
206 void *(*glMapBufferARB) (GLenum, GLenum);
207 void (*glBufferDataARB) (GLenum, GLsizei, void *, GLenum);
208 void (*glGenBuffersARB) (GLsizei, GLuint *);
209 void (*glDeleteBuffersARB) (GLsizei, GLuint *);
211 GLfloat texcoords[8];
212 GLfloat vertices[16];
214 #ifdef CACHE_PAGEREFS
215 struct {
216 int idx;
217 int count;
218 pdf_obj **objs;
219 pdf_document *pdf;
220 } pdflut;
221 #endif
222 int utf8cs;
223 } state;
225 struct bo {
226 GLuint id;
227 void *ptr;
228 size_t size;
231 #pragma GCC diagnostic ignored "-Wdouble-promotion"
232 static void UNUSED_ATTR debug_rect (const char *cap, fz_rect r)
234 printf ("%s(rect) %.2f,%.2f,%.2f,%.2f\n", cap, r.x0, r.y0, r.x1, r.y1);
237 static void UNUSED_ATTR debug_bbox (const char *cap, fz_irect r)
239 printf ("%s(bbox) %d,%d,%d,%d\n", cap, r.x0, r.y0, r.x1, r.y1);
242 static void UNUSED_ATTR debug_matrix (const char *cap, fz_matrix m)
244 printf ("%s(matrix) %.2f,%.2f,%.2f,%.2f %.2f %.2f\n", cap,
245 m.a, m.b, m.c, m.d, m.e, m.f);
247 #pragma GCC diagnostic error "-Wdouble-promotion"
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 #pragma GCC diagnostic ignored "-Wdouble-promotion"
1226 if (!stop) {
1227 printd ("firstmatch %d %d %f %f %f %f %f %f %f %f",
1228 pageno, 1,
1229 p1.x, p1.y,
1230 p2.x, p2.y,
1231 p3.x, p3.y,
1232 p4.x, p4.y);
1234 printd ("progress 1 found at %d `%.*s' in %f sec",
1235 pageno + 1, (int) (rm.rm_eo - rm.rm_so), &p[rm.rm_so],
1236 now () - start);
1238 else {
1239 printd ("match %d %d %f %f %f %f %f %f %f %f",
1240 pageno, 2,
1241 p1.x, p1.y,
1242 p2.x, p2.y,
1243 p3.x, p3.y,
1244 p4.x, p4.y);
1246 #pragma GCC diagnostic error "-Wdouble-promotion"
1247 free (p);
1248 return 1;
1252 /* wishful thinking function */
1253 static void search (regex_t *re, int pageno, int y, int forward)
1255 fz_device *tdev;
1256 fz_stext_page *text;
1257 struct pagedim *pdim;
1258 int stop = 0, niters = 0;
1259 double start, end;
1260 fz_page *page;
1261 fz_stext_block *block;
1263 start = now ();
1264 while (pageno >= 0 && pageno < state.pagecount && !stop) {
1265 if (niters++ == 5) {
1266 niters = 0;
1267 if (hasdata ()) {
1268 printd ("progress 1 attention requested aborting search at %d",
1269 pageno);
1270 stop = 1;
1272 else {
1273 printd ("progress %f searching in page %d",
1274 (double) (pageno + 1) / state.pagecount,
1275 pageno);
1278 pdim = pdimofpageno (pageno);
1279 text = fz_new_stext_page (state.ctx, &pdim->mediabox);
1280 tdev = fz_new_stext_device (state.ctx, text, 0);
1282 page = fz_load_page (state.ctx, state.doc, pageno);
1284 fz_matrix ctm = pagectm1 (page, pdim);
1285 fz_run_page (state.ctx, page, tdev, &ctm, NULL);
1288 fz_close_device (state.ctx, tdev);
1289 fz_drop_device (state.ctx, tdev);
1291 if (forward) {
1292 for (block = text->first_block; block; block = block->next) {
1293 fz_stext_line *line;
1295 if (block->type != FZ_STEXT_BLOCK_TEXT) continue;
1296 for (line = block->u.t.first_line; line; line = line->next) {
1297 if (line->bbox.y0 < y + 1) continue;
1299 switch (matchline (re, line, stop, pageno, start)) {
1300 case 0: break;
1301 case 1: stop = 1; break;
1302 case -1: stop = 1; goto endloop;
1307 else {
1308 for (block = text->last_block; block; block = block->prev) {
1309 fz_stext_line *line;
1311 if (block->type != FZ_STEXT_BLOCK_TEXT) continue;
1312 for (line = block->u.t.last_line; line; line = line->prev) {
1313 if (line->bbox.y0 < y + 1) continue;
1315 switch (matchline (re, line, stop, pageno, start)) {
1316 case 0: break;
1317 case 1: stop = 1; break;
1318 case -1: stop = 1; goto endloop;
1324 if (forward) {
1325 pageno += 1;
1326 y = 0;
1328 else {
1329 pageno -= 1;
1330 y = INT_MAX;
1332 endloop:
1333 fz_drop_stext_page (state.ctx, text);
1334 fz_drop_page (state.ctx, page);
1336 end = now ();
1337 if (!stop) {
1338 printd ("progress 1 no matches %f sec", end - start);
1340 printd ("clearrects");
1343 static void set_tex_params (int colorspace)
1345 switch (colorspace) {
1346 case 0:
1347 state.texiform = GL_RGBA8;
1348 state.texform = GL_RGBA;
1349 state.texty = GL_UNSIGNED_BYTE;
1350 state.colorspace = fz_device_rgb (state.ctx);
1351 break;
1352 case 1:
1353 state.texiform = GL_RGBA8;
1354 state.texform = GL_BGRA;
1355 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
1356 state.texty = GL_UNSIGNED_INT_8_8_8_8_REV;
1357 #else
1358 state.texty = GL_UNSIGNED_INT_8_8_8_8;
1359 #endif
1360 state.colorspace = fz_device_bgr (state.ctx);
1361 break;
1362 case 2:
1363 state.texiform = GL_LUMINANCE_ALPHA;
1364 state.texform = GL_LUMINANCE_ALPHA;
1365 state.texty = GL_UNSIGNED_BYTE;
1366 state.colorspace = fz_device_gray (state.ctx);
1367 break;
1368 default:
1369 errx (1, "invalid colorspce %d", colorspace);
1373 static void realloctexts (int texcount)
1375 size_t size;
1377 if (texcount == state.texcount) return;
1379 if (texcount < state.texcount) {
1380 glDeleteTextures (state.texcount - texcount,
1381 state.texids + texcount);
1384 size = texcount * (sizeof (*state.texids) + sizeof (*state.texowners));
1385 state.texids = realloc (state.texids, size);
1386 if (!state.texids) {
1387 err (1, "realloc texs %zu", size);
1390 state.texowners = (void *) (state.texids + texcount);
1391 if (texcount > state.texcount) {
1392 glGenTextures (texcount - state.texcount,
1393 state.texids + state.texcount);
1394 for (int i = state.texcount; i < texcount; ++i) {
1395 state.texowners[i].w = -1;
1396 state.texowners[i].slice = NULL;
1399 state.texcount = texcount;
1400 state.texindex = 0;
1403 static char *mbtoutf8 (char *s)
1405 char *p, *r;
1406 wchar_t *tmp;
1407 size_t i, ret, len;
1409 if (state.utf8cs) {
1410 return s;
1413 len = mbstowcs (NULL, s, strlen (s));
1414 if (len == 0) {
1415 return s;
1417 else {
1418 if (len == (size_t) -1) {
1419 printd ("emsg mbtoutf8: mbstowcs: %d:%s", errno, strerror (errno));
1420 return s;
1424 tmp = calloc (len, sizeof (wchar_t));
1425 if (!tmp) {
1426 printd ("emsg mbtoutf8: calloc(%zu, %zu): %d:%s",
1427 len, sizeof (wchar_t), errno, strerror (errno));
1428 return s;
1431 ret = mbstowcs (tmp, s, len);
1432 if (ret == (size_t) -1) {
1433 printd ("emsg mbtoutf8: mbswcs %zu characters failed: %d:%s",
1434 len, errno, strerror (errno));
1435 free (tmp);
1436 return s;
1439 len = 0;
1440 for (i = 0; i < ret; ++i) {
1441 len += fz_runelen (tmp[i]);
1444 p = r = malloc (len + 1);
1445 if (!r) {
1446 printd ("emsg mbtoutf8: malloc(%zu)", len);
1447 free (tmp);
1448 return s;
1451 for (i = 0; i < ret; ++i) {
1452 p += fz_runetochar (p, tmp[i]);
1454 *p = 0;
1455 free (tmp);
1456 return r;
1459 CAMLprim value ml_mbtoutf8 (value s_v)
1461 CAMLparam1 (s_v);
1462 CAMLlocal1 (ret_v);
1463 char *s, *r;
1465 s = String_val (s_v);
1466 r = mbtoutf8 (s);
1467 if (r == s) {
1468 ret_v = s_v;
1470 else {
1471 ret_v = caml_copy_string (r);
1472 free (r);
1474 CAMLreturn (ret_v);
1477 static void * mainloop (void UNUSED_ATTR *unused)
1479 char *p = NULL;
1480 int len, ret, oldlen = 0;
1482 fz_var (p);
1483 fz_var (oldlen);
1484 for (;;) {
1485 len = readlen (state.csock);
1486 if (len == 0) {
1487 errx (1, "readlen returned 0");
1490 if (oldlen < len + 1) {
1491 p = realloc (p, len + 1);
1492 if (!p) {
1493 err (1, "realloc %d failed", len + 1);
1495 oldlen = len + 1;
1497 readdata (state.csock, p, len);
1498 p[len] = 0;
1500 if (!strncmp ("open", p, 4)) {
1501 int off, usedoccss, ok = 0, layouth;
1502 char *password;
1503 char *filename;
1504 char *utf8filename;
1505 size_t filenamelen;
1507 fz_var (ok);
1508 ret = sscanf (p + 5, " %d %d %n", &usedoccss, &layouth, &off);
1509 if (ret != 2) {
1510 errx (1, "malformed open `%.*s' ret=%d", len, p, ret);
1513 filename = p + 5 + off;
1514 filenamelen = strlen (filename);
1515 password = filename + filenamelen + 1;
1517 if (password[strlen (password) + 1]) {
1518 fz_set_user_css (state.ctx, password + strlen (password) + 1);
1521 lock ("open");
1522 fz_set_use_document_css (state.ctx, usedoccss);
1523 fz_try (state.ctx) {
1524 ok = openxref (filename, password, layouth);
1526 fz_catch (state.ctx) {
1527 utf8filename = mbtoutf8 (filename);
1528 printd ("msg Could not open %s", utf8filename);
1529 if (utf8filename != filename) {
1530 free (utf8filename);
1533 if (ok) {
1534 docinfo ();
1535 initpdims ();
1537 unlock ("open");
1539 if (ok) {
1540 utf8filename = mbtoutf8 (filename);
1541 printd ("msg Opened %s (press h/F1 to get help)", utf8filename);
1542 if (utf8filename != filename) {
1543 free (utf8filename);
1545 state.needoutline = 1;
1548 else if (!strncmp ("cs", p, 2)) {
1549 int i, colorspace;
1551 ret = sscanf (p + 2, " %d", &colorspace);
1552 if (ret != 1) {
1553 errx (1, "malformed cs `%.*s' ret=%d", len, p, ret);
1555 lock ("cs");
1556 set_tex_params (colorspace);
1557 for (i = 0; i < state.texcount; ++i) {
1558 state.texowners[i].w = -1;
1559 state.texowners[i].slice = NULL;
1561 unlock ("cs");
1563 else if (!strncmp ("freepage", p, 8)) {
1564 void *ptr;
1566 ret = sscanf (p + 8, " %" SCNxPTR, (uintptr_t *) &ptr);
1567 if (ret != 1) {
1568 errx (1, "malformed freepage `%.*s' ret=%d", len, p, ret);
1570 lock ("freepage");
1571 freepage (ptr);
1572 unlock ("freepage");
1574 else if (!strncmp ("freetile", p, 8)) {
1575 void *ptr;
1577 ret = sscanf (p + 8, " %" SCNxPTR, (uintptr_t *) &ptr);
1578 if (ret != 1) {
1579 errx (1, "malformed freetile `%.*s' ret=%d", len, p, ret);
1581 lock ("freetile");
1582 freetile (ptr);
1583 unlock ("freetile");
1585 else if (!strncmp ("search", p, 6)) {
1586 int icase, pageno, y, len2, forward;
1587 char *pattern;
1588 regex_t re;
1590 ret = sscanf (p + 6, " %d %d %d %d,%n",
1591 &icase, &pageno, &y, &forward, &len2);
1592 if (ret != 4) {
1593 errx (1, "malformed search `%s' ret=%d", p, ret);
1596 pattern = p + 6 + len2;
1597 ret = regcomp (&re, pattern,
1598 REG_EXTENDED | (icase ? REG_ICASE : 0));
1599 if (ret) {
1600 char errbuf[80];
1601 size_t size;
1603 size = regerror (ret, &re, errbuf, sizeof (errbuf));
1604 printd ("msg regcomp failed `%.*s'", (int) size, errbuf);
1606 else {
1607 search (&re, pageno, y, forward);
1608 regfree (&re);
1611 else if (!strncmp ("geometry", p, 8)) {
1612 int w, h, fitmodel;
1614 printd ("clear");
1615 ret = sscanf (p + 8, " %d %d %d", &w, &h, &fitmodel);
1616 if (ret != 3) {
1617 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
1620 lock ("geometry");
1621 state.h = h;
1622 if (w != state.w) {
1623 state.w = w;
1624 for (int i = 0; i < state.texcount; ++i) {
1625 state.texowners[i].slice = NULL;
1628 state.fitmodel = fitmodel;
1629 layout ();
1630 process_outline ();
1632 state.gen++;
1633 unlock ("geometry");
1634 printd ("continue %d", state.pagecount);
1636 else if (!strncmp ("reqlayout", p, 9)) {
1637 char *nameddest;
1638 int rotate, off, h;
1639 int fitmodel;
1640 pdf_document *pdf;
1642 printd ("clear");
1643 ret = sscanf (p + 9, " %d %d %d %n",
1644 &rotate, &fitmodel, &h, &off);
1645 if (ret != 3) {
1646 errx (1, "bad reqlayout line `%.*s' ret=%d", len, p, ret);
1648 lock ("reqlayout");
1649 pdf = pdf_specifics (state.ctx, state.doc);
1650 if (state.rotate != rotate || state.fitmodel != fitmodel) {
1651 state.gen += 1;
1653 state.rotate = rotate;
1654 state.fitmodel = fitmodel;
1655 state.h = h;
1656 layout ();
1657 process_outline ();
1659 nameddest = p + 9 + off;
1660 if (pdf && nameddest && *nameddest) {
1661 fz_point xy;
1662 struct pagedim *pdim;
1663 int pageno = pdf_lookup_anchor (state.ctx, pdf, nameddest,
1664 &xy.x, &xy.y);
1665 pdim = pdimofpageno (pageno);
1666 fz_transform_point (&xy, &pdim->ctm);
1667 printd ("a %d %d %d", pageno, (int) xy.x, (int) xy.y);
1670 state.gen++;
1671 unlock ("reqlayout");
1672 printd ("continue %d", state.pagecount);
1674 else if (!strncmp ("page", p, 4)) {
1675 double a, b;
1676 struct page *page;
1677 int pageno, pindex;
1679 ret = sscanf (p + 4, " %d %d", &pageno, &pindex);
1680 if (ret != 2) {
1681 errx (1, "bad page line `%.*s' ret=%d", len, p, ret);
1684 lock ("page");
1685 a = now ();
1686 page = loadpage (pageno, pindex);
1687 b = now ();
1688 unlock ("page");
1690 printd ("page %" PRIxPTR " %f", (uintptr_t) page, b - a);
1692 else if (!strncmp ("tile", p, 4)) {
1693 int x, y, w, h;
1694 struct page *page;
1695 struct tile *tile;
1696 double a, b;
1697 void *data;
1699 ret = sscanf (p + 4, " %" SCNxPTR " %d %d %d %d %" SCNxPTR,
1700 (uintptr_t *) &page, &x, &y, &w, &h,
1701 (uintptr_t *) &data);
1702 if (ret != 6) {
1703 errx (1, "bad tile line `%.*s' ret=%d", len, p, ret);
1706 lock ("tile");
1707 a = now ();
1708 tile = rendertile (page, x, y, w, h, data);
1709 b = now ();
1710 unlock ("tile");
1712 printd ("tile %d %d %" PRIxPTR " %u %f",
1713 x, y, (uintptr_t) (tile),
1714 tile->w * tile->h * tile->pixmap->n,
1715 b - a);
1717 else if (!strncmp ("trimset", p, 7)) {
1718 fz_irect fuzz;
1719 int trimmargins;
1721 ret = sscanf (p + 7, " %d %d %d %d %d",
1722 &trimmargins, &fuzz.x0, &fuzz.y0, &fuzz.x1, &fuzz.y1);
1723 if (ret != 5) {
1724 errx (1, "malformed trimset `%.*s' ret=%d", len, p, ret);
1726 lock ("trimset");
1727 state.trimmargins = trimmargins;
1728 if (memcmp (&fuzz, &state.trimfuzz, sizeof (fuzz))) {
1729 state.trimanew = 1;
1730 state.trimfuzz = fuzz;
1732 unlock ("trimset");
1734 else if (!strncmp ("settrim", p, 7)) {
1735 fz_irect fuzz;
1736 int trimmargins;
1738 ret = sscanf (p + 7, " %d %d %d %d %d",
1739 &trimmargins, &fuzz.x0, &fuzz.y0, &fuzz.x1, &fuzz.y1);
1740 if (ret != 5) {
1741 errx (1, "malformed settrim `%.*s' ret=%d", len, p, ret);
1743 printd ("clear");
1744 lock ("settrim");
1745 state.trimmargins = trimmargins;
1746 state.needoutline = 1;
1747 if (memcmp (&fuzz, &state.trimfuzz, sizeof (fuzz))) {
1748 state.trimanew = 1;
1749 state.trimfuzz = fuzz;
1751 state.pagedimcount = 0;
1752 free (state.pagedims);
1753 state.pagedims = NULL;
1754 initpdims ();
1755 layout ();
1756 process_outline ();
1757 unlock ("settrim");
1758 printd ("continue %d", state.pagecount);
1760 else if (!strncmp ("sliceh", p, 6)) {
1761 int h;
1763 ret = sscanf (p + 6, " %d", &h);
1764 if (ret != 1) {
1765 errx (1, "malformed sliceh `%.*s' ret=%d", len, p, ret);
1767 if (h != state.sliceheight) {
1768 state.sliceheight = h;
1769 for (int i = 0; i < state.texcount; ++i) {
1770 state.texowners[i].w = -1;
1771 state.texowners[i].h = -1;
1772 state.texowners[i].slice = NULL;
1776 else if (!strncmp ("interrupt", p, 9)) {
1777 printd ("vmsg interrupted");
1779 else {
1780 errx (1, "unknown command %.*s", len, p);
1783 return 0;
1786 CAMLprim value ml_isexternallink (value uri_v)
1788 CAMLparam1 (uri_v);
1789 int ext = fz_is_external_link (state.ctx, String_val (uri_v));
1790 CAMLreturn (Val_bool (ext));
1793 CAMLprim value ml_uritolocation (value uri_v)
1795 CAMLparam1 (uri_v);
1796 CAMLlocal1 (ret_v);
1797 int pageno;
1798 fz_point xy;
1799 struct pagedim *pdim;
1801 pageno = fz_resolve_link (state.ctx, state.doc, String_val (uri_v),
1802 &xy.x, &xy.y);
1803 pdim = pdimofpageno (pageno);
1804 fz_transform_point (&xy, &pdim->ctm);
1805 ret_v = caml_alloc_tuple (3);
1806 Field (ret_v, 0) = Val_int (pageno);
1807 Field (ret_v, 1) = caml_copy_double ((double) xy.x);
1808 Field (ret_v, 2) = caml_copy_double ((double) xy.y);
1809 CAMLreturn (ret_v);
1812 CAMLprim value ml_realloctexts (value texcount_v)
1814 CAMLparam1 (texcount_v);
1815 int ok;
1817 if (trylock (__func__)) {
1818 ok = 0;
1819 goto done;
1821 realloctexts (Int_val (texcount_v));
1822 ok = 1;
1823 unlock (__func__);
1825 done:
1826 CAMLreturn (Val_bool (ok));
1829 static void recti (int x0, int y0, int x1, int y1)
1831 GLfloat *v = state.vertices;
1833 glVertexPointer (2, GL_FLOAT, 0, v);
1834 v[0] = x0; v[1] = y0;
1835 v[2] = x1; v[3] = y0;
1836 v[4] = x0; v[5] = y1;
1837 v[6] = x1; v[7] = y1;
1838 glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
1841 static void showsel (struct page *page, int ox, int oy)
1843 fz_irect bbox;
1844 fz_rect rect;
1845 fz_stext_block *block;
1846 int seen = 0;
1847 unsigned char selcolor[] = {15,15,15,140};
1849 if (!page->fmark || !page->lmark) return;
1851 glEnable (GL_BLEND);
1852 glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA);
1853 glColor4ubv (selcolor);
1855 ox += state.pagedims[page->pdimno].bounds.x0;
1856 oy += state.pagedims[page->pdimno].bounds.y0;
1858 for (block = page->text->first_block; block; block = block->next) {
1859 fz_stext_line *line;
1861 if (block->type != FZ_STEXT_BLOCK_TEXT) continue;
1862 for (line = block->u.t.first_line; line; line = line->next) {
1863 fz_stext_char *ch;
1865 rect = fz_empty_rect;
1866 for (ch = line->first_char; ch; ch = ch->next) {
1867 if (ch == page->fmark) seen = 1;
1868 if (seen) fz_union_rect (&rect, &ch->bbox);
1869 if (ch == page->lmark) {
1870 fz_round_rect (&bbox, &rect);
1871 recti (bbox.x0 + ox, bbox.y0 + oy,
1872 bbox.x1 + ox, bbox.y1 + oy);
1873 goto done;
1876 fz_round_rect (&bbox, &rect);
1877 recti (bbox.x0 + ox, bbox.y0 + oy,
1878 bbox.x1 + ox, bbox.y1 + oy);
1881 done:
1882 glDisable (GL_BLEND);
1885 #pragma GCC diagnostic push
1886 #pragma GCC diagnostic ignored "-Wdouble-promotion"
1887 #pragma GCC diagnostic ignored "-Wconversion"
1888 #include "glfont.c"
1889 #pragma GCC diagnostic pop
1891 static void stipplerect (fz_matrix *m,
1892 fz_point *p1,
1893 fz_point *p2,
1894 fz_point *p3,
1895 fz_point *p4,
1896 GLfloat *texcoords,
1897 GLfloat *vertices)
1899 fz_transform_point (p1, m);
1900 fz_transform_point (p2, m);
1901 fz_transform_point (p3, m);
1902 fz_transform_point (p4, m);
1904 float w, h, s, t;
1906 w = p2->x - p1->x;
1907 h = p2->y - p1->y;
1908 t = hypotf (w, h) * .25f;
1910 w = p3->x - p2->x;
1911 h = p3->y - p2->y;
1912 s = hypotf (w, h) * .25f;
1914 texcoords[0] = 0; vertices[0] = p1->x; vertices[1] = p1->y;
1915 texcoords[1] = t; vertices[2] = p2->x; vertices[3] = p2->y;
1917 texcoords[2] = 0; vertices[4] = p2->x; vertices[5] = p2->y;
1918 texcoords[3] = s; vertices[6] = p3->x; vertices[7] = p3->y;
1920 texcoords[4] = 0; vertices[8] = p3->x; vertices[9] = p3->y;
1921 texcoords[5] = t; vertices[10] = p4->x; vertices[11] = p4->y;
1923 texcoords[6] = 0; vertices[12] = p4->x; vertices[13] = p4->y;
1924 texcoords[7] = s; vertices[14] = p1->x; vertices[15] = p1->y;
1926 glDrawArrays (GL_LINES, 0, 8);
1929 static void solidrect (fz_matrix *m,
1930 fz_point *p1,
1931 fz_point *p2,
1932 fz_point *p3,
1933 fz_point *p4,
1934 GLfloat *vertices)
1936 fz_transform_point (p1, m);
1937 fz_transform_point (p2, m);
1938 fz_transform_point (p3, m);
1939 fz_transform_point (p4, m);
1940 vertices[0] = p1->x; vertices[1] = p1->y;
1941 vertices[2] = p2->x; vertices[3] = p2->y;
1943 vertices[4] = p3->x; vertices[5] = p3->y;
1944 vertices[6] = p4->x; vertices[7] = p4->y;
1945 glDrawArrays (GL_TRIANGLE_FAN, 0, 4);
1948 static void ensurelinks (struct page *page)
1950 if (!page->links)
1951 page->links = fz_load_links (state.ctx, page->fzpage);
1954 static void highlightlinks (struct page *page, int xoff, int yoff)
1956 fz_matrix ctm, tm, pm;
1957 fz_link *link;
1958 GLfloat *texcoords = state.texcoords;
1959 GLfloat *vertices = state.vertices;
1961 ensurelinks (page);
1963 glEnable (GL_TEXTURE_1D);
1964 glEnable (GL_BLEND);
1965 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1966 glBindTexture (GL_TEXTURE_1D, state.stid);
1968 xoff -= state.pagedims[page->pdimno].bounds.x0;
1969 yoff -= state.pagedims[page->pdimno].bounds.y0;
1970 fz_translate (&tm, xoff, yoff);
1971 pm = pagectm (page);
1972 fz_concat (&ctm, &pm, &tm);
1974 glTexCoordPointer (1, GL_FLOAT, 0, texcoords);
1975 glVertexPointer (2, GL_FLOAT, 0, vertices);
1977 for (link = page->links; link; link = link->next) {
1978 fz_point p1, p2, p3, p4;
1980 p1.x = link->rect.x0;
1981 p1.y = link->rect.y0;
1983 p2.x = link->rect.x1;
1984 p2.y = link->rect.y0;
1986 p3.x = link->rect.x1;
1987 p3.y = link->rect.y1;
1989 p4.x = link->rect.x0;
1990 p4.y = link->rect.y1;
1992 /* TODO: different colours for different schemes */
1993 if (fz_is_external_link (state.ctx, link->uri)) glColor3ub (0, 0, 255);
1994 else glColor3ub (255, 0, 0);
1996 stipplerect (&ctm, &p1, &p2, &p3, &p4, texcoords, vertices);
1999 for (int i = 0; i < page->annotcount; ++i) {
2000 fz_point p1, p2, p3, p4;
2001 struct annot *annot = &page->annots[i];
2003 p1.x = annot->bbox.x0;
2004 p1.y = annot->bbox.y0;
2006 p2.x = annot->bbox.x1;
2007 p2.y = annot->bbox.y0;
2009 p3.x = annot->bbox.x1;
2010 p3.y = annot->bbox.y1;
2012 p4.x = annot->bbox.x0;
2013 p4.y = annot->bbox.y1;
2015 glColor3ub (0, 0, 128);
2016 stipplerect (&ctm, &p1, &p2, &p3, &p4, texcoords, vertices);
2019 glDisable (GL_BLEND);
2020 glDisable (GL_TEXTURE_1D);
2023 static int compareslinks (const void *l, const void *r)
2025 struct slink const *ls = l;
2026 struct slink const *rs = r;
2027 if (ls->bbox.y0 == rs->bbox.y0) {
2028 return rs->bbox.x0 - rs->bbox.x0;
2030 return ls->bbox.y0 - rs->bbox.y0;
2033 static void droptext (struct page *page)
2035 if (page->text) {
2036 fz_drop_stext_page (state.ctx, page->text);
2037 page->fmark = NULL;
2038 page->lmark = NULL;
2039 page->text = NULL;
2043 static void dropannots (struct page *page)
2045 if (page->annots) {
2046 free (page->annots);
2047 page->annots = NULL;
2048 page->annotcount = 0;
2052 static void ensureannots (struct page *page)
2054 int i, count = 0;
2055 size_t annotsize = sizeof (*page->annots);
2056 fz_annot *annot;
2058 if (state.gen != page->agen) {
2059 dropannots (page);
2060 page->agen = state.gen;
2062 if (page->annots) return;
2064 for (annot = fz_first_annot (state.ctx, page->fzpage);
2065 annot;
2066 annot = fz_next_annot (state.ctx, annot)) {
2067 count++;
2070 if (count > 0) {
2071 page->annotcount = count;
2072 page->annots = calloc (count, annotsize);
2073 if (!page->annots) {
2074 err (1, "calloc annots %d", count);
2077 for (annot = fz_first_annot (state.ctx, page->fzpage), i = 0;
2078 annot;
2079 annot = fz_next_annot (state.ctx, annot), i++) {
2080 fz_rect rect;
2082 fz_bound_annot (state.ctx, annot, &rect);
2083 page->annots[i].annot = annot;
2084 fz_round_rect (&page->annots[i].bbox, &rect);
2089 static void dropslinks (struct page *page)
2091 if (page->slinks) {
2092 free (page->slinks);
2093 page->slinks = NULL;
2094 page->slinkcount = 0;
2096 if (page->links) {
2097 fz_drop_link (state.ctx, page->links);
2098 page->links = NULL;
2102 static void ensureslinks (struct page *page)
2104 fz_matrix ctm;
2105 int i, count;
2106 size_t slinksize = sizeof (*page->slinks);
2107 fz_link *link;
2109 ensureannots (page);
2110 if (state.gen != page->sgen) {
2111 dropslinks (page);
2112 page->sgen = state.gen;
2114 if (page->slinks) return;
2116 ensurelinks (page);
2117 ctm = pagectm (page);
2119 count = page->annotcount;
2120 for (link = page->links; link; link = link->next) {
2121 count++;
2123 if (count > 0) {
2124 int j;
2126 page->slinkcount = count;
2127 page->slinks = calloc (count, slinksize);
2128 if (!page->slinks) {
2129 err (1, "calloc slinks %d", count);
2132 for (i = 0, link = page->links; link; ++i, link = link->next) {
2133 fz_rect rect;
2135 rect = link->rect;
2136 fz_transform_rect (&rect, &ctm);
2137 page->slinks[i].tag = SLINK;
2138 page->slinks[i].u.link = link;
2139 fz_round_rect (&page->slinks[i].bbox, &rect);
2141 for (j = 0; j < page->annotcount; ++j, ++i) {
2142 fz_rect rect;
2143 fz_bound_annot (state.ctx, page->annots[j].annot, &rect);
2144 fz_transform_rect (&rect, &ctm);
2145 fz_round_rect (&page->slinks[i].bbox, &rect);
2147 page->slinks[i].tag = SANNOT;
2148 page->slinks[i].u.annot = page->annots[j].annot;
2150 qsort (page->slinks, count, slinksize, compareslinks);
2154 static void highlightslinks (struct page *page, int xoff, int yoff,
2155 int noff, char *targ, mlsize_t tlen, int hfsize)
2157 char buf[40];
2158 struct slink *slink;
2159 float x0, y0, x1, y1, w;
2161 ensureslinks (page);
2162 glColor3ub (0xc3, 0xb0, 0x91);
2163 for (int i = 0; i < page->slinkcount; ++i) {
2164 fmt_linkn (buf, i + noff);
2165 if (!tlen || !strncmp (targ, buf, tlen)) {
2166 slink = &page->slinks[i];
2168 x0 = slink->bbox.x0 + xoff - 5;
2169 y1 = slink->bbox.y0 + yoff - 5;
2170 y0 = y1 + 10 + hfsize;
2171 w = measure_string (state.face, hfsize, buf);
2172 x1 = x0 + w + 10;
2173 recti ((int) x0, (int) y0, (int) x1, (int) y1);
2177 glEnable (GL_BLEND);
2178 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2179 glEnable (GL_TEXTURE_2D);
2180 glColor3ub (0, 0, 0);
2181 for (int i = 0; i < page->slinkcount; ++i) {
2182 fmt_linkn (buf, i + noff);
2183 if (!tlen || !strncmp (targ, buf, tlen)) {
2184 slink = &page->slinks[i];
2186 x0 = slink->bbox.x0 + xoff;
2187 y0 = slink->bbox.y0 + yoff + hfsize;
2188 draw_string (state.face, hfsize, x0, y0, buf);
2191 glDisable (GL_TEXTURE_2D);
2192 glDisable (GL_BLEND);
2195 static void uploadslice (struct tile *tile, struct slice *slice)
2197 int offset;
2198 struct slice *slice1;
2199 unsigned char *texdata;
2201 offset = 0;
2202 for (slice1 = tile->slices; slice != slice1; slice1++) {
2203 offset += slice1->h * tile->w * tile->pixmap->n;
2205 if (slice->texindex != -1 && slice->texindex < state.texcount
2206 && state.texowners[slice->texindex].slice == slice) {
2207 glBindTexture (TEXT_TYPE, state.texids[slice->texindex]);
2209 else {
2210 int subimage = 0;
2211 int texindex = state.texindex++ % state.texcount;
2213 if (state.texowners[texindex].w == tile->w) {
2214 if (state.texowners[texindex].h >= slice->h) {
2215 subimage = 1;
2217 else {
2218 state.texowners[texindex].h = slice->h;
2221 else {
2222 state.texowners[texindex].h = slice->h;
2225 state.texowners[texindex].w = tile->w;
2226 state.texowners[texindex].slice = slice;
2227 slice->texindex = texindex;
2229 glBindTexture (TEXT_TYPE, state.texids[texindex]);
2230 #if TEXT_TYPE == GL_TEXTURE_2D
2231 glTexParameteri (TEXT_TYPE, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2232 glTexParameteri (TEXT_TYPE, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2233 glTexParameteri (TEXT_TYPE, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2234 glTexParameteri (TEXT_TYPE, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2235 #endif
2236 if (tile->pbo) {
2237 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, tile->pbo->id);
2238 texdata = 0;
2240 else {
2241 texdata = tile->pixmap->samples;
2243 if (subimage) {
2244 glTexSubImage2D (TEXT_TYPE,
2248 tile->w,
2249 slice->h,
2250 state.texform,
2251 state.texty,
2252 texdata+offset
2255 else {
2256 glTexImage2D (TEXT_TYPE,
2258 state.texiform,
2259 tile->w,
2260 slice->h,
2262 state.texform,
2263 state.texty,
2264 texdata+offset
2267 if (tile->pbo) {
2268 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, 0);
2273 CAMLprim void ml_begintiles (value unit_v)
2275 CAMLparam1 (unit_v);
2276 glEnable (TEXT_TYPE);
2277 glTexCoordPointer (2, GL_FLOAT, 0, state.texcoords);
2278 glVertexPointer (2, GL_FLOAT, 0, state.vertices);
2279 CAMLreturn0;
2282 CAMLprim void ml_endtiles (value unit_v)
2284 CAMLparam1 (unit_v);
2285 glDisable (TEXT_TYPE);
2286 CAMLreturn0;
2289 CAMLprim void ml_drawtile (value args_v, value ptr_v)
2291 CAMLparam2 (args_v, ptr_v);
2292 int dispx = Int_val (Field (args_v, 0));
2293 int dispy = Int_val (Field (args_v, 1));
2294 int dispw = Int_val (Field (args_v, 2));
2295 int disph = Int_val (Field (args_v, 3));
2296 int tilex = Int_val (Field (args_v, 4));
2297 int tiley = Int_val (Field (args_v, 5));
2298 char *s = String_val (ptr_v);
2299 struct tile *tile = parse_pointer (__func__, s);
2300 int slicey, firstslice;
2301 struct slice *slice;
2302 GLfloat *texcoords = state.texcoords;
2303 GLfloat *vertices = state.vertices;
2305 firstslice = tiley / tile->sliceheight;
2306 slice = &tile->slices[firstslice];
2307 slicey = tiley % tile->sliceheight;
2309 while (disph > 0) {
2310 int dh;
2312 dh = slice->h - slicey;
2313 dh = fz_mini (disph, dh);
2314 uploadslice (tile, slice);
2316 texcoords[0] = tilex; texcoords[1] = slicey;
2317 texcoords[2] = tilex+dispw; texcoords[3] = slicey;
2318 texcoords[4] = tilex; texcoords[5] = slicey+dh;
2319 texcoords[6] = tilex+dispw; texcoords[7] = slicey+dh;
2321 vertices[0] = dispx; vertices[1] = dispy;
2322 vertices[2] = dispx+dispw; vertices[3] = dispy;
2323 vertices[4] = dispx; vertices[5] = dispy+dh;
2324 vertices[6] = dispx+dispw; vertices[7] = dispy+dh;
2326 #if TEXT_TYPE == GL_TEXTURE_2D
2327 for (int i = 0; i < 8; ++i) {
2328 texcoords[i] /= ((i & 1) == 0 ? tile->w : slice->h);
2330 #endif
2332 glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
2333 dispy += dh;
2334 disph -= dh;
2335 slice++;
2336 ARSERT (!(slice - tile->slices >= tile->slicecount && disph > 0));
2337 slicey = 0;
2339 CAMLreturn0;
2342 static void drawprect (struct page *page, int xoff, int yoff, value rects_v)
2344 fz_matrix ctm, tm, pm;
2345 fz_point p1, p2, p3, p4;
2346 GLfloat *vertices = state.vertices;
2347 double *v = (double *) rects_v;
2349 xoff -= state.pagedims[page->pdimno].bounds.x0;
2350 yoff -= state.pagedims[page->pdimno].bounds.y0;
2351 fz_translate (&tm, xoff, yoff);
2352 pm = pagectm (page);
2353 fz_concat (&ctm, &pm, &tm);
2355 glEnable (GL_BLEND);
2356 glVertexPointer (2, GL_FLOAT, 0, vertices);
2358 glColor4dv (v);
2359 p1.x = (float) v[4];
2360 p1.y = (float) v[5];
2362 p2.x = (float) v[6];
2363 p2.y = (float) v[5];
2365 p3.x = (float) v[6];
2366 p3.y = (float) v[7];
2368 p4.x = (float) v[4];
2369 p4.y = (float) v[7];
2370 solidrect (&ctm, &p1, &p2, &p3, &p4, vertices);
2371 glDisable (GL_BLEND);
2374 CAMLprim value ml_postprocess (value ptr_v, value hlinks_v,
2375 value xoff_v, value yoff_v,
2376 value li_v)
2378 CAMLparam5 (ptr_v, hlinks_v, xoff_v, yoff_v, li_v);
2379 int xoff = Int_val (xoff_v);
2380 int yoff = Int_val (yoff_v);
2381 int noff = Int_val (Field (li_v, 0));
2382 char *targ = String_val (Field (li_v, 1));
2383 mlsize_t tlen = caml_string_length (Field (li_v, 1));
2384 int hfsize = Int_val (Field (li_v, 2));
2385 char *s = String_val (ptr_v);
2386 int hlmask = Int_val (hlinks_v);
2387 struct page *page = parse_pointer (__func__, s);
2389 if (!page->fzpage) {
2390 /* deal with loadpage failed pages */
2391 goto done;
2394 if (trylock (__func__)) {
2395 noff = -1;
2396 goto done;
2399 ensureannots (page);
2400 if (hlmask & 1) highlightlinks (page, xoff, yoff);
2401 if (hlmask & 2) {
2402 highlightslinks (page, xoff, yoff, noff, targ, tlen, hfsize);
2403 noff = page->slinkcount;
2405 if (page->tgen == state.gen) {
2406 showsel (page, xoff, yoff);
2408 unlock (__func__);
2410 done:
2411 CAMLreturn (Val_int (noff));
2414 CAMLprim void ml_drawprect (value ptr_v, value xoff_v, value yoff_v,
2415 value rects_v)
2417 CAMLparam4 (ptr_v, xoff_v, yoff_v, rects_v);
2418 int xoff = Int_val (xoff_v);
2419 int yoff = Int_val (yoff_v);
2420 char *s = String_val (ptr_v);
2421 struct page *page = parse_pointer (__func__, s);
2423 drawprect (page, xoff, yoff, rects_v);
2424 CAMLreturn0;
2427 static struct annot *getannot (struct page *page, int x, int y)
2429 fz_point p;
2430 fz_matrix ctm;
2431 const fz_matrix *tctm;
2432 pdf_document *pdf = pdf_specifics (state.ctx, state.doc);
2434 if (!page->annots) return NULL;
2436 if (pdf) {
2437 trimctm (pdf_page_from_fz_page (state.ctx, page->fzpage), page->pdimno);
2438 tctm = &state.pagedims[page->pdimno].tctm;
2440 else {
2441 tctm = &fz_identity;
2444 p.x = x;
2445 p.y = y;
2447 fz_concat (&ctm, tctm, &state.pagedims[page->pdimno].ctm);
2448 fz_invert_matrix (&ctm, &ctm);
2449 fz_transform_point (&p, &ctm);
2451 if (pdf) {
2452 for (int i = 0; i < page->annotcount; ++i) {
2453 struct annot *a = &page->annots[i];
2454 fz_rect rect;
2456 fz_bound_annot (state.ctx, a->annot, &rect);
2457 if (p.x >= rect.x0 && p.x <= rect.x1) {
2458 if (p.y >= rect.y0 && p.y <= rect.y1)
2459 return a;
2463 return NULL;
2466 static fz_link *getlink (struct page *page, int x, int y)
2468 fz_point p;
2469 fz_matrix ctm;
2470 fz_link *link;
2472 ensureslinks (page);
2474 p.x = x;
2475 p.y = y;
2477 ctm = pagectm (page);
2478 fz_invert_matrix (&ctm, &ctm);
2479 fz_transform_point (&p, &ctm);
2481 for (link = page->links; link; link = link->next) {
2482 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
2483 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
2484 return link;
2488 return NULL;
2491 static void ensuretext (struct page *page)
2493 if (state.gen != page->tgen) {
2494 droptext (page);
2495 page->tgen = state.gen;
2497 if (!page->text) {
2498 fz_matrix ctm;
2499 fz_device *tdev;
2501 page->text = fz_new_stext_page (state.ctx,
2502 &state.pagedims[page->pdimno].mediabox);
2503 tdev = fz_new_stext_device (state.ctx, page->text, 0);
2504 ctm = pagectm (page);
2505 fz_run_display_list (state.ctx, page->dlist,
2506 tdev, &ctm, &fz_infinite_rect, NULL);
2507 fz_close_device (state.ctx, tdev);
2508 fz_drop_device (state.ctx, tdev);
2512 CAMLprim value ml_find_page_with_links (value start_page_v, value dir_v)
2514 CAMLparam2 (start_page_v, dir_v);
2515 CAMLlocal1 (ret_v);
2516 int i, dir = Int_val (dir_v);
2517 int start_page = Int_val (start_page_v);
2518 int end_page = dir > 0 ? state.pagecount : -1;
2519 pdf_document *pdf;
2521 fz_var (end_page);
2522 ret_v = Val_int (0);
2523 lock (__func__);
2524 pdf = pdf_specifics (state.ctx, state.doc);
2525 for (i = start_page + dir; i != end_page; i += dir) {
2526 int found;
2528 fz_var (found);
2529 if (pdf) {
2530 pdf_page *page = NULL;
2532 fz_var (page);
2533 fz_try (state.ctx) {
2534 page = pdf_load_page (state.ctx, pdf, i);
2535 found = !!page->links || !!page->annots;
2537 fz_catch (state.ctx) {
2538 found = 0;
2540 if (page) {
2541 fz_drop_page (state.ctx, &page->super);
2544 else {
2545 fz_page *page = fz_load_page (state.ctx, state.doc, i);
2546 fz_link *link = fz_load_links (state.ctx, page);
2547 found = !!link;
2548 fz_drop_link (state.ctx, link);
2549 fz_drop_page (state.ctx, page);
2552 if (found) {
2553 ret_v = caml_alloc_small (1, 1);
2554 Field (ret_v, 0) = Val_int (i);
2555 goto unlock;
2558 unlock:
2559 unlock (__func__);
2560 CAMLreturn (ret_v);
2563 enum { dir_first, dir_last };
2564 enum { dir_first_visible, dir_left, dir_right, dir_down, dir_up };
2566 CAMLprim value ml_findlink (value ptr_v, value dir_v)
2568 CAMLparam2 (ptr_v, dir_v);
2569 CAMLlocal2 (ret_v, pos_v);
2570 struct page *page;
2571 int dirtag, i, slinkindex;
2572 struct slink *found = NULL ,*slink;
2573 char *s = String_val (ptr_v);
2575 page = parse_pointer (__func__, s);
2576 ret_v = Val_int (0);
2577 lock (__func__);
2578 ensureslinks (page);
2580 if (Is_block (dir_v)) {
2581 dirtag = Tag_val (dir_v);
2582 switch (dirtag) {
2583 case dir_first_visible:
2585 int x0, y0, dir, first_index, last_index;
2587 pos_v = Field (dir_v, 0);
2588 x0 = Int_val (Field (pos_v, 0));
2589 y0 = Int_val (Field (pos_v, 1));
2590 dir = Int_val (Field (pos_v, 2));
2592 if (dir >= 0) {
2593 dir = 1;
2594 first_index = 0;
2595 last_index = page->slinkcount;
2597 else {
2598 first_index = page->slinkcount - 1;
2599 last_index = -1;
2602 for (i = first_index; i != last_index; i += dir) {
2603 slink = &page->slinks[i];
2604 if (slink->bbox.y0 >= y0 && slink->bbox.x0 >= x0) {
2605 found = slink;
2606 break;
2610 break;
2612 case dir_left:
2613 slinkindex = Int_val (Field (dir_v, 0));
2614 found = &page->slinks[slinkindex];
2615 for (i = slinkindex - 1; i >= 0; --i) {
2616 slink = &page->slinks[i];
2617 if (slink->bbox.x0 < found->bbox.x0) {
2618 found = slink;
2619 break;
2622 break;
2624 case dir_right:
2625 slinkindex = Int_val (Field (dir_v, 0));
2626 found = &page->slinks[slinkindex];
2627 for (i = slinkindex + 1; i < page->slinkcount; ++i) {
2628 slink = &page->slinks[i];
2629 if (slink->bbox.x0 > found->bbox.x0) {
2630 found = slink;
2631 break;
2634 break;
2636 case dir_down:
2637 slinkindex = Int_val (Field (dir_v, 0));
2638 found = &page->slinks[slinkindex];
2639 for (i = slinkindex + 1; i < page->slinkcount; ++i) {
2640 slink = &page->slinks[i];
2641 if (slink->bbox.y0 >= found->bbox.y0) {
2642 found = slink;
2643 break;
2646 break;
2648 case dir_up:
2649 slinkindex = Int_val (Field (dir_v, 0));
2650 found = &page->slinks[slinkindex];
2651 for (i = slinkindex - 1; i >= 0; --i) {
2652 slink = &page->slinks[i];
2653 if (slink->bbox.y0 <= found->bbox.y0) {
2654 found = slink;
2655 break;
2658 break;
2661 else {
2662 dirtag = Int_val (dir_v);
2663 switch (dirtag) {
2664 case dir_first:
2665 found = page->slinks;
2666 break;
2668 case dir_last:
2669 if (page->slinks) {
2670 found = page->slinks + (page->slinkcount - 1);
2672 break;
2675 if (found) {
2676 ret_v = caml_alloc_small (2, 1);
2677 Field (ret_v, 0) = Val_int (found - page->slinks);
2680 unlock (__func__);
2681 CAMLreturn (ret_v);
2684 enum { uuri, utext, uannot };
2686 CAMLprim value ml_getlink (value ptr_v, value n_v)
2688 CAMLparam2 (ptr_v, n_v);
2689 CAMLlocal4 (ret_v, tup_v, str_v, gr_v);
2690 fz_link *link;
2691 struct page *page;
2692 char *s = String_val (ptr_v);
2693 struct slink *slink;
2695 ret_v = Val_int (0);
2696 page = parse_pointer (__func__, s);
2698 lock (__func__);
2699 ensureslinks (page);
2700 slink = &page->slinks[Int_val (n_v)];
2701 if (slink->tag == SLINK) {
2702 link = slink->u.link;
2703 str_v = caml_copy_string (link->uri);
2704 ret_v = caml_alloc_small (1, uuri);
2705 Field (ret_v, 0) = str_v;
2707 else {
2708 ret_v = caml_alloc_small (1, uannot);
2709 tup_v = caml_alloc_tuple (2);
2710 Field (ret_v, 0) = tup_v;
2711 Field (tup_v, 0) = ptr_v;
2712 Field (tup_v, 1) = n_v;
2714 unlock (__func__);
2716 CAMLreturn (ret_v);
2719 CAMLprim value ml_getannotcontents (value ptr_v, value n_v)
2721 CAMLparam2 (ptr_v, n_v);
2722 CAMLlocal1 (ret_v);
2723 pdf_document *pdf;
2724 char *contents = NULL;
2726 lock (__func__);
2727 pdf = pdf_specifics (state.ctx, state.doc);
2728 if (pdf) {
2729 char *s = String_val (ptr_v);
2730 struct page *page;
2731 struct slink *slink;
2733 page = parse_pointer (__func__, s);
2734 slink = &page->slinks[Int_val (n_v)];
2735 contents = pdf_copy_annot_contents (state.ctx,
2736 (pdf_annot *) slink->u.annot);
2738 unlock (__func__);
2739 if (contents) {
2740 ret_v = caml_copy_string (contents);
2741 fz_free (state.ctx, contents);
2743 else {
2744 ret_v = caml_copy_string ("");
2746 CAMLreturn (ret_v);
2749 CAMLprim value ml_getlinkcount (value ptr_v)
2751 CAMLparam1 (ptr_v);
2752 struct page *page;
2753 char *s = String_val (ptr_v);
2755 page = parse_pointer (__func__, s);
2756 CAMLreturn (Val_int (page->slinkcount));
2759 CAMLprim value ml_getlinkrect (value ptr_v, value n_v)
2761 CAMLparam2 (ptr_v, n_v);
2762 CAMLlocal1 (ret_v);
2763 struct page *page;
2764 struct slink *slink;
2765 char *s = String_val (ptr_v);
2767 page = parse_pointer (__func__, s);
2768 ret_v = caml_alloc_tuple (4);
2769 lock (__func__);
2770 ensureslinks (page);
2772 slink = &page->slinks[Int_val (n_v)];
2773 Field (ret_v, 0) = Val_int (slink->bbox.x0);
2774 Field (ret_v, 1) = Val_int (slink->bbox.y0);
2775 Field (ret_v, 2) = Val_int (slink->bbox.x1);
2776 Field (ret_v, 3) = Val_int (slink->bbox.y1);
2777 unlock (__func__);
2778 CAMLreturn (ret_v);
2781 CAMLprim value ml_whatsunder (value ptr_v, value x_v, value y_v)
2783 CAMLparam3 (ptr_v, x_v, y_v);
2784 CAMLlocal4 (ret_v, tup_v, str_v, gr_v);
2785 fz_link *link;
2786 struct annot *annot;
2787 struct page *page;
2788 char *ptr = String_val (ptr_v);
2789 int x = Int_val (x_v), y = Int_val (y_v);
2790 struct pagedim *pdim;
2792 ret_v = Val_int (0);
2793 if (trylock (__func__)) {
2794 goto done;
2797 page = parse_pointer (__func__, ptr);
2798 pdim = &state.pagedims[page->pdimno];
2799 x += pdim->bounds.x0;
2800 y += pdim->bounds.y0;
2803 annot = getannot (page, x, y);
2804 if (annot) {
2805 int i, n = -1;
2807 ensureslinks (page);
2808 for (i = 0; i < page->slinkcount; ++i) {
2809 if (page->slinks[i].tag == SANNOT
2810 && page->slinks[i].u.annot == annot->annot) {
2811 n = i;
2812 break;
2815 ret_v = caml_alloc_small (1, uannot);
2816 tup_v = caml_alloc_tuple (2);
2817 Field (ret_v, 0) = tup_v;
2818 Field (tup_v, 0) = ptr_v;
2819 Field (tup_v, 1) = Val_int (n);
2820 goto unlock;
2824 link = getlink (page, x, y);
2825 if (link) {
2826 str_v = caml_copy_string (link->uri);
2827 ret_v = caml_alloc_small (1, uuri);
2828 Field (ret_v, 0) = str_v;
2830 else {
2831 fz_rect *b;
2832 fz_stext_block *block;
2834 ensuretext (page);
2836 for (block = page->text->first_block; block; block = block->next) {
2837 fz_stext_line *line;
2839 if (block->type != FZ_STEXT_BLOCK_TEXT) continue;
2840 b = &block->bbox;
2841 if (!(x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1))
2842 continue;
2844 for (line = block->u.t.first_line; line; line = line->next) {
2845 fz_stext_char *ch;
2847 b = &line->bbox;
2848 if (!(x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1))
2849 continue;
2851 for (ch = line->first_char; ch; ch = ch->next) {
2852 b = &ch->bbox;
2854 if (x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1) {
2855 const char *n2 = fz_font_name (state.ctx, ch->font);
2856 FT_FaceRec *face = fz_font_ft_face (state.ctx,
2857 ch->font);
2859 if (!n2) n2 = "<unknown font>";
2861 if (face && face->family_name) {
2862 char *s;
2863 char *n1 = face->family_name;
2864 size_t l1 = strlen (n1);
2865 size_t l2 = strlen (n2);
2867 if (l1 != l2 || memcmp (n1, n2, l1)) {
2868 s = malloc (l1 + l2 + 2);
2869 if (s) {
2870 memcpy (s, n2, l2);
2871 s[l2] = '=';
2872 memcpy (s + l2 + 1, n1, l1 + 1);
2873 str_v = caml_copy_string (s);
2874 free (s);
2878 if (str_v == Val_unit) {
2879 str_v = caml_copy_string (n2);
2881 ret_v = caml_alloc_small (1, utext);
2882 Field (ret_v, 0) = str_v;
2883 goto unlock;
2889 unlock:
2890 unlock (__func__);
2892 done:
2893 CAMLreturn (ret_v);
2896 enum { mark_page, mark_block, mark_line, mark_word };
2898 CAMLprim void ml_clearmark (value ptr_v)
2900 CAMLparam1 (ptr_v);
2901 char *s = String_val (ptr_v);
2902 struct page *page;
2904 if (trylock (__func__)) {
2905 goto done;
2908 page = parse_pointer (__func__, s);
2909 page->fmark = NULL;
2910 page->lmark = NULL;
2912 unlock (__func__);
2913 done:
2914 CAMLreturn0;
2917 static int uninteresting (int c)
2919 return c == ' ' || c == '\n' || c == '\t' || c == '\n' || c == '\r'
2920 || ispunct (c);
2923 CAMLprim value ml_markunder (value ptr_v, value x_v, value y_v, value mark_v)
2925 CAMLparam4 (ptr_v, x_v, y_v, mark_v);
2926 CAMLlocal1 (ret_v);
2927 fz_rect *b;
2928 struct page *page;
2929 fz_stext_line *line;
2930 fz_stext_block *block;
2931 struct pagedim *pdim;
2932 int mark = Int_val (mark_v);
2933 char *s = String_val (ptr_v);
2934 int x = Int_val (x_v), y = Int_val (y_v);
2936 ret_v = Val_bool (0);
2937 if (trylock (__func__)) {
2938 goto done;
2941 page = parse_pointer (__func__, s);
2942 pdim = &state.pagedims[page->pdimno];
2944 ensuretext (page);
2946 if (mark == mark_page) {
2947 page->fmark = page->text->first_block->u.t.first_line->first_char;
2948 page->lmark = page->text->last_block->u.t.last_line->last_char;
2949 ret_v = Val_bool (1);
2950 goto unlock;
2953 x += pdim->bounds.x0;
2954 y += pdim->bounds.y0;
2956 for (block = page->text->first_block; block; block = block->next) {
2957 if (block->type != FZ_STEXT_BLOCK_TEXT) continue;
2958 b = &block->bbox;
2959 if (!(x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1))
2960 continue;
2962 if (mark == mark_block) {
2963 page->fmark = block->u.t.first_line->first_char;
2964 page->lmark = block->u.t.last_line->last_char;
2965 ret_v = Val_bool (1);
2966 goto unlock;
2969 for (line = block->u.t.first_line; line; line = line->next) {
2970 fz_stext_char *ch;
2972 b = &line->bbox;
2973 if (!(x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1))
2974 continue;
2976 if (mark == mark_line) {
2977 page->fmark = line->first_char;
2978 page->lmark = line->last_char;
2979 ret_v = Val_bool (1);
2980 goto unlock;
2983 for (ch = line->first_char; ch; ch = ch->next) {
2984 fz_stext_char *ch2, *first = NULL, *last = NULL;
2985 b = &ch->bbox;
2986 if (x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1) {
2987 for (ch2 = line->first_char; ch2 != ch; ch2 = ch2->next) {
2988 if (uninteresting (ch2->c)) first = NULL;
2989 else if (!first) first = ch2;
2991 for (ch2 = ch; ch2; ch2 = ch2->next) {
2992 if (uninteresting (ch2->c)) break;
2993 last = ch2;
2996 page->fmark = first;
2997 page->lmark = last;
2998 ret_v = Val_bool (1);
2999 goto unlock;
3004 unlock:
3005 if (!Bool_val (ret_v)) {
3006 page->fmark = NULL;
3007 page->lmark = NULL;
3009 unlock (__func__);
3011 done:
3012 CAMLreturn (ret_v);
3015 CAMLprim value ml_rectofblock (value ptr_v, value x_v, value y_v)
3017 CAMLparam3 (ptr_v, x_v, y_v);
3018 CAMLlocal2 (ret_v, res_v);
3019 fz_rect *b = NULL;
3020 struct page *page;
3021 struct pagedim *pdim;
3022 fz_stext_block *block;
3023 char *s = String_val (ptr_v);
3024 int x = Int_val (x_v), y = Int_val (y_v);
3026 ret_v = Val_int (0);
3027 if (trylock (__func__)) {
3028 goto done;
3031 page = parse_pointer (__func__, s);
3032 pdim = &state.pagedims[page->pdimno];
3033 x += pdim->bounds.x0;
3034 y += pdim->bounds.y0;
3036 ensuretext (page);
3038 for (block = page->text->first_block; block; block = block->next) {
3039 switch (block->type) {
3040 case FZ_STEXT_BLOCK_TEXT:
3041 b = &block->bbox;
3042 break;
3044 case FZ_STEXT_BLOCK_IMAGE:
3045 b = &block->bbox;
3046 break;
3048 default:
3049 continue;
3052 if (x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1)
3053 break;
3054 b = NULL;
3056 if (b) {
3057 res_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
3058 ret_v = caml_alloc_small (1, 1);
3059 Store_double_field (res_v, 0, (double) b->x0);
3060 Store_double_field (res_v, 1, (double) b->x1);
3061 Store_double_field (res_v, 2, (double) b->y0);
3062 Store_double_field (res_v, 3, (double) b->y1);
3063 Field (ret_v, 0) = res_v;
3065 unlock (__func__);
3067 done:
3068 CAMLreturn (ret_v);
3071 CAMLprim void ml_seltext (value ptr_v, value rect_v)
3073 CAMLparam2 (ptr_v, rect_v);
3074 fz_rect b;
3075 struct page *page;
3076 struct pagedim *pdim;
3077 char *s = String_val (ptr_v);
3078 int x0, x1, y0, y1;
3079 fz_stext_char *ch;
3080 fz_stext_line *line;
3081 fz_stext_block *block;
3082 fz_stext_char *fc, *lc;
3084 if (trylock (__func__)) {
3085 goto done;
3088 page = parse_pointer (__func__, s);
3089 ensuretext (page);
3091 pdim = &state.pagedims[page->pdimno];
3092 x0 = Int_val (Field (rect_v, 0)) + pdim->bounds.x0;
3093 y0 = Int_val (Field (rect_v, 1)) + pdim->bounds.y0;
3094 x1 = Int_val (Field (rect_v, 2)) + pdim->bounds.x0;
3095 y1 = Int_val (Field (rect_v, 3)) + pdim->bounds.y0;
3097 if (y0 > y1) {
3098 int t = y0;
3099 y0 = y1;
3100 y1 = t;
3101 x0 = x1;
3102 x1 = t;
3105 fc = page->fmark;
3106 lc = page->lmark;
3108 for (block = page->text->first_block; block; block = block->next) {
3109 if (block->type != FZ_STEXT_BLOCK_TEXT) continue;
3110 for (line = block->u.t.first_line; line; line = line->next) {
3111 for (ch = line->first_char; ch; ch = ch->next) {
3112 b = ch->bbox;
3113 if (x0 >= b.x0 && x0 <= b.x1 && y0 >= b.y0 && y0 <= b.y1) {
3114 fc = ch;
3116 if (x1 >= b.x0 && x1 <= b.x1 && y1 >= b.y0 && y1 <= b.y1) {
3117 lc = ch;
3122 if (x1 < x0 && fc == lc) {
3123 fz_stext_char *t;
3125 t = fc;
3126 fc = lc;
3127 lc = t;
3130 page->fmark = fc;
3131 page->lmark = lc;
3133 unlock (__func__);
3135 done:
3136 CAMLreturn0;
3139 static int pipechar (FILE *f, fz_stext_char *ch)
3141 char buf[4];
3142 int len;
3143 size_t ret;
3145 len = fz_runetochar (buf, ch->c);
3146 ret = fwrite (buf, len, 1, f);
3147 if (ret != 1) {
3148 printd ("emsg failed to write %d bytes ret=%zu: %d:%s",
3149 len, ret, errno, strerror (errno));
3150 return -1;
3152 return 0;
3155 CAMLprim value ml_spawn (value command_v, value fds_v)
3157 CAMLparam2 (command_v, fds_v);
3158 CAMLlocal2 (l_v, tup_v);
3159 int ret, ret1;
3160 pid_t pid = (pid_t) -1;
3161 char *msg = NULL;
3162 value earg_v = Nothing;
3163 posix_spawnattr_t attr;
3164 posix_spawn_file_actions_t fa;
3165 char *argv[] = { "/bin/sh", "-c", NULL, NULL };
3167 argv[2] = String_val (command_v);
3169 if ((ret = posix_spawn_file_actions_init (&fa)) != 0) {
3170 unix_error (ret, "posix_spawn_file_actions_init", Nothing);
3173 if ((ret = posix_spawnattr_init (&attr)) != 0) {
3174 msg = "posix_spawnattr_init";
3175 goto fail1;
3178 #ifdef POSIX_SPAWN_USEVFORK
3179 if ((ret = posix_spawnattr_setflags (&attr, POSIX_SPAWN_USEVFORK)) != 0) {
3180 msg = "posix_spawnattr_setflags POSIX_SPAWN_USEVFORK";
3181 goto fail;
3183 #endif
3185 for (l_v = fds_v; l_v != Val_int (0); l_v = Field (l_v, 1)) {
3186 int fd1, fd2;
3188 tup_v = Field (l_v, 0);
3189 fd1 = Int_val (Field (tup_v, 0));
3190 fd2 = Int_val (Field (tup_v, 1));
3191 if (fd2 < 0) {
3192 if ((ret = posix_spawn_file_actions_addclose (&fa, fd1)) != 0) {
3193 msg = "posix_spawn_file_actions_addclose";
3194 earg_v = tup_v;
3195 goto fail;
3198 else {
3199 if ((ret = posix_spawn_file_actions_adddup2 (&fa, fd1, fd2)) != 0) {
3200 msg = "posix_spawn_file_actions_adddup2";
3201 earg_v = tup_v;
3202 goto fail;
3207 if ((ret = posix_spawn (&pid, "/bin/sh", &fa, &attr, argv, environ))) {
3208 msg = "posix_spawn";
3209 goto fail;
3212 fail:
3213 if ((ret1 = posix_spawnattr_destroy (&attr)) != 0) {
3214 printd ("emsg posix_spawnattr_destroy: %d:%s", ret1, strerror (ret1));
3217 fail1:
3218 if ((ret1 = posix_spawn_file_actions_destroy (&fa)) != 0) {
3219 printd ("emsg posix_spawn_file_actions_destroy: %d:%s",
3220 ret1, strerror (ret1));
3223 if (msg)
3224 unix_error (ret, msg, earg_v);
3226 CAMLreturn (Val_int (pid));
3229 CAMLprim value ml_hassel (value ptr_v)
3231 CAMLparam1 (ptr_v);
3232 CAMLlocal1 (ret_v);
3233 struct page *page;
3234 char *s = String_val (ptr_v);
3236 ret_v = Val_bool (0);
3237 if (trylock (__func__)) {
3238 goto done;
3241 page = parse_pointer (__func__, s);
3242 ret_v = Val_bool (page->fmark && page->lmark);
3243 unlock (__func__);
3244 done:
3245 CAMLreturn (ret_v);
3248 CAMLprim void ml_copysel (value fd_v, value ptr_v)
3250 CAMLparam2 (fd_v, ptr_v);
3251 FILE *f;
3252 int seen = 0;
3253 struct page *page;
3254 fz_stext_line *line;
3255 fz_stext_block *block;
3256 int fd = Int_val (fd_v);
3257 char *s = String_val (ptr_v);
3259 if (trylock (__func__)) {
3260 goto done;
3263 page = parse_pointer (__func__, s);
3265 if (!page->fmark || !page->lmark) {
3266 printd ("emsg nothing to copy on page %d", page->pageno);
3267 goto unlock;
3270 f = fdopen (fd, "w");
3271 if (!f) {
3272 printd ("emsg failed to fdopen sel pipe (from fd %d): %d:%s",
3273 fd, errno, strerror (errno));
3274 f = stdout;
3277 for (block = page->text->first_block; block; block = block->next) {
3278 if (block->type != FZ_STEXT_BLOCK_TEXT) continue;
3279 for (line = block->u.t.first_line; line; line = line->next) {
3280 fz_stext_char *ch;
3281 for (ch = line->first_char; ch; ch = ch->next) {
3282 if (seen || ch == page->fmark) {
3283 do {
3284 pipechar (f, ch);
3285 if (ch == page->lmark) goto close;
3286 } while ((ch = ch->next));
3287 seen = 1;
3288 break;
3291 if (seen) fputc ('\n', f);
3294 close:
3295 if (f != stdout) {
3296 int ret = fclose (f);
3297 fd = -1;
3298 if (ret == -1) {
3299 if (errno != ECHILD) {
3300 printd ("emsg failed to close sel pipe: %d:%s",
3301 errno, strerror (errno));
3305 unlock:
3306 unlock (__func__);
3308 done:
3309 if (fd >= 0) {
3310 if (close (fd)) {
3311 printd ("emsg failed to close sel pipe: %d:%s",
3312 errno, strerror (errno));
3315 CAMLreturn0;
3318 CAMLprim value ml_getpdimrect (value pagedimno_v)
3320 CAMLparam1 (pagedimno_v);
3321 CAMLlocal1 (ret_v);
3322 int pagedimno = Int_val (pagedimno_v);
3323 fz_rect box;
3325 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
3326 if (trylock (__func__)) {
3327 box = fz_empty_rect;
3329 else {
3330 box = state.pagedims[pagedimno].mediabox;
3331 unlock (__func__);
3334 Store_double_field (ret_v, 0, (double) box.x0);
3335 Store_double_field (ret_v, 1, (double) box.x1);
3336 Store_double_field (ret_v, 2, (double) box.y0);
3337 Store_double_field (ret_v, 3, (double) box.y1);
3339 CAMLreturn (ret_v);
3342 CAMLprim value ml_zoom_for_height (value winw_v, value winh_v,
3343 value dw_v, value cols_v)
3345 CAMLparam4 (winw_v, winh_v, dw_v, cols_v);
3346 CAMLlocal1 (ret_v);
3347 int i;
3348 float zoom = -1.;
3349 float maxh = 0.0;
3350 struct pagedim *p;
3351 float winw = Int_val (winw_v);
3352 float winh = Int_val (winh_v);
3353 float dw = Int_val (dw_v);
3354 float cols = Int_val (cols_v);
3355 float pw = 1.0, ph = 1.0;
3357 if (trylock (__func__)) {
3358 goto done;
3361 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
3362 float w = p->pagebox.x1 / cols;
3363 float h = p->pagebox.y1;
3364 if (h > maxh) {
3365 maxh = h;
3366 ph = h;
3367 if (state.fitmodel != FitProportional) pw = w;
3369 if ((state.fitmodel == FitProportional) && w > pw) pw = w;
3372 zoom = (((winh / ph) * pw) + dw) / winw;
3373 unlock (__func__);
3374 done:
3375 ret_v = caml_copy_double ((double) zoom);
3376 CAMLreturn (ret_v);
3379 CAMLprim value ml_getmaxw (value unit_v)
3381 CAMLparam1 (unit_v);
3382 CAMLlocal1 (ret_v);
3383 int i;
3384 float maxw = -1.;
3385 struct pagedim *p;
3387 if (trylock (__func__)) {
3388 goto done;
3391 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
3392 float w = p->pagebox.x1;
3393 maxw = fz_max (maxw, w);
3396 unlock (__func__);
3397 done:
3398 ret_v = caml_copy_double ((double) maxw);
3399 CAMLreturn (ret_v);
3402 CAMLprim value ml_draw_string (value pt_v, value x_v, value y_v, value string_v)
3404 CAMLparam4 (pt_v, x_v, y_v, string_v);
3405 CAMLlocal1 (ret_v);
3406 int pt = Int_val(pt_v);
3407 int x = Int_val (x_v);
3408 int y = Int_val (y_v);
3409 double w;
3411 w = (double) draw_string (state.face, pt, x, y, String_val (string_v));
3412 ret_v = caml_copy_double (w);
3413 CAMLreturn (ret_v);
3416 CAMLprim value ml_measure_string (value pt_v, value string_v)
3418 CAMLparam2 (pt_v, string_v);
3419 CAMLlocal1 (ret_v);
3420 int pt = Int_val (pt_v);
3421 double w;
3423 w = (double) measure_string (state.face, pt, String_val (string_v));
3424 ret_v = caml_copy_double (w);
3425 CAMLreturn (ret_v);
3428 CAMLprim value ml_getpagebox (value opaque_v)
3430 CAMLparam1 (opaque_v);
3431 CAMLlocal1 (ret_v);
3432 fz_rect rect;
3433 fz_irect bbox;
3434 fz_matrix ctm;
3435 fz_device *dev;
3436 char *s = String_val (opaque_v);
3437 struct page *page = parse_pointer (__func__, s);
3439 ret_v = caml_alloc_tuple (4);
3440 dev = fz_new_bbox_device (state.ctx, &rect);
3442 ctm = pagectm (page);
3443 fz_run_page (state.ctx, page->fzpage, dev, &ctm, NULL);
3445 fz_close_device (state.ctx, dev);
3446 fz_drop_device (state.ctx, dev);
3447 fz_round_rect (&bbox, &rect);
3448 Field (ret_v, 0) = Val_int (bbox.x0);
3449 Field (ret_v, 1) = Val_int (bbox.y0);
3450 Field (ret_v, 2) = Val_int (bbox.x1);
3451 Field (ret_v, 3) = Val_int (bbox.y1);
3453 CAMLreturn (ret_v);
3456 CAMLprim void ml_setaalevel (value level_v)
3458 CAMLparam1 (level_v);
3460 state.aalevel = Int_val (level_v);
3461 CAMLreturn0;
3464 #ifndef __COCOA__
3465 #pragma GCC diagnostic push
3466 #pragma GCC diagnostic ignored "-Wvariadic-macros"
3467 #include <X11/Xlib.h>
3468 #include <X11/cursorfont.h>
3469 #pragma GCC diagnostic pop
3471 #include <GL/glx.h>
3473 static const int shapes[] = {
3474 XC_left_ptr, XC_hand2, XC_exchange, XC_fleur, XC_xterm
3477 #define CURS_COUNT (sizeof (shapes) / sizeof (shapes[0]))
3479 static struct {
3480 Window wid;
3481 Display *dpy;
3482 GLXContext ctx;
3483 XVisualInfo *visual;
3484 Cursor curs[CURS_COUNT];
3485 } glx;
3488 static void initcurs (void)
3490 for (size_t n = 0; n < CURS_COUNT; ++n) {
3491 glx.curs[n] = XCreateFontCursor (glx.dpy, shapes[n]);
3495 CAMLprim value ml_glxinit (value display_v, value wid_v, value screen_v)
3497 CAMLparam3 (display_v, wid_v, screen_v);
3499 glx.dpy = XOpenDisplay (String_val (display_v));
3500 if (!glx.dpy) {
3501 caml_failwith ("XOpenDisplay");
3504 int attribs[] = { GLX_RGBA, GLX_DOUBLEBUFFER, None };
3505 glx.visual = glXChooseVisual (glx.dpy, Int_val (screen_v), attribs);
3506 if (!glx.visual) {
3507 XCloseDisplay (glx.dpy);
3508 caml_failwith ("glXChooseVisual");
3511 initcurs ();
3513 glx.wid = Int_val (wid_v);
3514 CAMLreturn (Val_int (glx.visual->visualid));
3517 CAMLprim void ml_glxcompleteinit (value unit_v)
3519 CAMLparam1 (unit_v);
3521 glx.ctx = glXCreateContext (glx.dpy, glx.visual, NULL, True);
3522 if (!glx.ctx) {
3523 caml_failwith ("glXCreateContext");
3526 XFree (glx.visual);
3527 glx.visual = NULL;
3529 if (!glXMakeCurrent (glx.dpy, glx.wid, glx.ctx)) {
3530 glXDestroyContext (glx.dpy, glx.ctx);
3531 glx.ctx = NULL;
3532 caml_failwith ("glXMakeCurrent");
3534 CAMLreturn0;
3537 CAMLprim void ml_setcursor (value cursor_v)
3539 CAMLparam1 (cursor_v);
3540 size_t cursn = Int_val (cursor_v);
3542 if (cursn >= CURS_COUNT) caml_failwith ("cursor index out of range");
3543 XDefineCursor (glx.dpy, glx.wid, glx.curs[cursn]);
3544 XFlush (glx.dpy);
3545 CAMLreturn0;
3548 CAMLprim void ml_swapb (value unit_v)
3550 CAMLparam1 (unit_v);
3551 glXSwapBuffers (glx.dpy, glx.wid);
3552 CAMLreturn0;
3555 long keysym2ucs (KeySym);
3556 CAMLprim value ml_keysymtoutf8 (value keysym_v)
3558 CAMLparam1 (keysym_v);
3559 CAMLlocal1 (str_v);
3560 KeySym keysym = Int_val (keysym_v);
3561 Rune rune;
3562 int len;
3563 char buf[5];
3565 rune = (Rune) keysym2ucs (keysym);
3566 len = fz_runetochar (buf, rune);
3567 buf[len] = 0;
3568 str_v = caml_copy_string (buf);
3569 CAMLreturn (str_v);
3571 #else
3572 CAMLprim value ml_keysymtoutf8 (value keysym_v)
3574 CAMLparam1 (keysym_v);
3575 CAMLlocal1 (str_v);
3576 long ucs_v = Long_val (keysym_v);
3577 int len;
3578 char buf[5];
3580 len = fz_runetochar (buf, (int) ucs_v);
3581 buf[len] = 0;
3582 str_v = caml_copy_string (buf);
3583 CAMLreturn (str_v);
3585 #endif
3587 enum { piunknown, pilinux, piosx, pisun, pibsd };
3589 CAMLprim value ml_platform (value unit_v)
3591 CAMLparam1 (unit_v);
3592 CAMLlocal2 (tup_v, arr_v);
3593 int platid = piunknown;
3594 struct utsname buf;
3596 #if defined __linux__
3597 platid = pilinux;
3598 #elif defined __DragonFly__ || defined __FreeBSD__
3599 || defined __OpenBSD__ || defined __NetBSD__
3600 platid = pibsd;
3601 #elif defined __sun__
3602 platid = pisun;
3603 #elif defined __APPLE__
3604 platid = piosx;
3605 #endif
3606 if (uname (&buf)) err (1, "uname");
3608 tup_v = caml_alloc_tuple (2);
3610 char const *sar[] = {
3611 buf.sysname,
3612 buf.release,
3613 buf.version,
3614 buf.machine,
3615 NULL
3617 arr_v = caml_copy_string_array (sar);
3619 Field (tup_v, 0) = Val_int (platid);
3620 Field (tup_v, 1) = arr_v;
3621 CAMLreturn (tup_v);
3624 CAMLprim void ml_cloexec (value fd_v)
3626 CAMLparam1 (fd_v);
3627 int fd = Int_val (fd_v);
3629 if (fcntl (fd, F_SETFD, FD_CLOEXEC, 1)) {
3630 uerror ("fcntl", Nothing);
3632 CAMLreturn0;
3635 CAMLprim value ml_getpbo (value w_v, value h_v, value cs_v)
3637 CAMLparam2 (w_v, h_v);
3638 CAMLlocal1 (ret_v);
3639 struct bo *pbo;
3640 int w = Int_val (w_v);
3641 int h = Int_val (h_v);
3642 int cs = Int_val (cs_v);
3644 if (state.bo_usable) {
3645 pbo = calloc (sizeof (*pbo), 1);
3646 if (!pbo) {
3647 err (1, "calloc pbo");
3650 switch (cs) {
3651 case 0:
3652 case 1:
3653 pbo->size = w*h*4;
3654 break;
3655 case 2:
3656 pbo->size = w*h*2;
3657 break;
3658 default:
3659 errx (1, "%s: invalid colorspace %d", __func__, cs);
3662 state.glGenBuffersARB (1, &pbo->id);
3663 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, pbo->id);
3664 state.glBufferDataARB (GL_PIXEL_UNPACK_BUFFER_ARB, (GLsizei) pbo->size,
3665 NULL, GL_STREAM_DRAW);
3666 pbo->ptr = state.glMapBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB,
3667 GL_READ_WRITE);
3668 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, 0);
3669 if (!pbo->ptr) {
3670 printd ("emsg glMapBufferARB failed: %#x", glGetError ());
3671 state.glDeleteBuffersARB (1, &pbo->id);
3672 free (pbo);
3673 ret_v = caml_copy_string ("0");
3675 else {
3676 int res;
3677 char *s;
3679 res = snprintf (NULL, 0, "%" PRIxPTR, (uintptr_t) pbo);
3680 if (res < 0) {
3681 err (1, "snprintf %" PRIxPTR " failed", (uintptr_t) pbo);
3683 s = malloc (res+1);
3684 if (!s) {
3685 err (1, "malloc %d bytes failed", res+1);
3687 res = sprintf (s, "%" PRIxPTR, (uintptr_t) pbo);
3688 if (res < 0) {
3689 err (1, "sprintf %" PRIxPTR " failed", (uintptr_t) pbo);
3691 ret_v = caml_copy_string (s);
3692 free (s);
3695 else {
3696 ret_v = caml_copy_string ("0");
3698 CAMLreturn (ret_v);
3701 CAMLprim void ml_freepbo (value s_v)
3703 CAMLparam1 (s_v);
3704 char *s = String_val (s_v);
3705 struct tile *tile = parse_pointer (__func__, s);
3707 if (tile->pbo) {
3708 state.glDeleteBuffersARB (1, &tile->pbo->id);
3709 tile->pbo->id = -1;
3710 tile->pbo->ptr = NULL;
3711 tile->pbo->size = -1;
3713 CAMLreturn0;
3716 CAMLprim void ml_unmappbo (value s_v)
3718 CAMLparam1 (s_v);
3719 char *s = String_val (s_v);
3720 struct tile *tile = parse_pointer (__func__, s);
3722 if (tile->pbo) {
3723 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, tile->pbo->id);
3724 if (state.glUnmapBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB) == GL_FALSE) {
3725 errx (1, "glUnmapBufferARB failed: %#x\n", glGetError ());
3727 tile->pbo->ptr = NULL;
3728 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, 0);
3730 CAMLreturn0;
3733 static void setuppbo (void)
3735 #ifdef __clang__
3736 #pragma GCC diagnostic ignored "-Wunused-macros"
3737 #endif
3738 #ifdef __COCOA__
3739 static CFBundleRef framework = NULL;
3740 if (framework == NULL)
3741 framework = CFBundleGetBundleWithIdentifier (CFSTR ("com.apple.opengl"));
3742 #define GGPA(n) \
3743 (&state.n = CFBundleGetFunctionPointerForName (framework, CFSTR (#n)))
3744 #else
3745 #define GGPA(n) \
3746 (*(void (**) (void)) &state.n = glXGetProcAddress ((GLubyte *) #n))
3747 #endif
3748 state.bo_usable = GGPA (glBindBufferARB)
3749 && GGPA (glUnmapBufferARB)
3750 && GGPA (glMapBufferARB)
3751 && GGPA (glBufferDataARB)
3752 && GGPA (glGenBuffersARB)
3753 && GGPA (glDeleteBuffersARB);
3754 #undef GGPA
3757 CAMLprim value ml_bo_usable (value unit_v)
3759 CAMLparam1 (unit_v);
3760 CAMLreturn (Val_bool (state.bo_usable));
3763 CAMLprim value ml_unproject (value ptr_v, value x_v, value y_v)
3765 CAMLparam3 (ptr_v, x_v, y_v);
3766 CAMLlocal2 (ret_v, tup_v);
3767 struct page *page;
3768 char *s = String_val (ptr_v);
3769 int x = Int_val (x_v), y = Int_val (y_v);
3770 struct pagedim *pdim;
3771 fz_point p;
3772 fz_matrix ctm;
3774 page = parse_pointer (__func__, s);
3775 pdim = &state.pagedims[page->pdimno];
3777 ret_v = Val_int (0);
3778 if (trylock (__func__)) {
3779 goto done;
3782 if (pdf_specifics (state.ctx, state.doc)) {
3783 trimctm (pdf_page_from_fz_page (state.ctx, page->fzpage), page->pdimno);
3784 ctm = state.pagedims[page->pdimno].tctm;
3786 else {
3787 ctm = fz_identity;
3789 p.x = x + pdim->bounds.x0;
3790 p.y = y + pdim->bounds.y0;
3792 fz_concat (&ctm, &pdim->tctm, &pdim->ctm);
3793 fz_invert_matrix (&ctm, &ctm);
3794 fz_transform_point (&p, &ctm);
3796 tup_v = caml_alloc_tuple (2);
3797 ret_v = caml_alloc_small (1, 1);
3798 Field (tup_v, 0) = Val_int (p.x);
3799 Field (tup_v, 1) = Val_int (p.y);
3800 Field (ret_v, 0) = tup_v;
3802 unlock (__func__);
3803 done:
3804 CAMLreturn (ret_v);
3807 CAMLprim value ml_project (value ptr_v, value pageno_v, value pdimno_v,
3808 value x_v, value y_v)
3810 CAMLparam5 (ptr_v, pageno_v, pdimno_v, x_v, y_v);
3811 CAMLlocal1 (ret_v);
3812 struct page *page;
3813 char *s = String_val (ptr_v);
3814 int pageno = Int_val (pageno_v);
3815 int pdimno = Int_val (pdimno_v);
3816 float x = (float) Double_val (x_v), y = (float) Double_val (y_v);
3817 struct pagedim *pdim;
3818 fz_point p;
3819 fz_matrix ctm;
3821 ret_v = Val_int (0);
3822 lock (__func__);
3824 if (!*s) {
3825 page = loadpage (pageno, pdimno);
3827 else {
3828 page = parse_pointer (__func__, s);
3830 pdim = &state.pagedims[pdimno];
3832 if (pdf_specifics (state.ctx, state.doc)) {
3833 trimctm (pdf_page_from_fz_page (state.ctx, page->fzpage), page->pdimno);
3834 ctm = state.pagedims[page->pdimno].tctm;
3836 else {
3837 ctm = fz_identity;
3839 p.x = x + pdim->bounds.x0;
3840 p.y = y + pdim->bounds.y0;
3842 fz_concat (&ctm, &pdim->tctm, &pdim->ctm);
3843 fz_transform_point (&p, &ctm);
3845 ret_v = caml_alloc_tuple (2);
3846 Field (ret_v, 0) = caml_copy_double ((double) p.x);
3847 Field (ret_v, 1) = caml_copy_double ((double) p.y);
3849 if (!*s) {
3850 freepage (page);
3852 unlock (__func__);
3853 CAMLreturn (ret_v);
3856 CAMLprim void ml_addannot (value ptr_v, value x_v, value y_v,
3857 value contents_v)
3859 CAMLparam4 (ptr_v, x_v, y_v, contents_v);
3860 pdf_document *pdf = pdf_specifics (state.ctx, state.doc);
3862 if (pdf) {
3863 pdf_annot *annot;
3864 struct page *page;
3865 fz_point p;
3866 char *s = String_val (ptr_v);
3868 page = parse_pointer (__func__, s);
3869 annot = pdf_create_annot (state.ctx,
3870 pdf_page_from_fz_page (state.ctx,
3871 page->fzpage),
3872 PDF_ANNOT_TEXT);
3873 p.x = Int_val (x_v);
3874 p.y = Int_val (y_v);
3875 pdf_set_annot_contents (state.ctx, annot, String_val (contents_v));
3876 pdf_set_text_annot_position (state.ctx, annot, p);
3877 state.dirty = 1;
3879 CAMLreturn0;
3882 CAMLprim void ml_delannot (value ptr_v, value n_v)
3884 CAMLparam2 (ptr_v, n_v);
3885 pdf_document *pdf = pdf_specifics (state.ctx, state.doc);
3887 if (pdf) {
3888 struct page *page;
3889 char *s = String_val (ptr_v);
3890 struct slink *slink;
3892 page = parse_pointer (__func__, s);
3893 slink = &page->slinks[Int_val (n_v)];
3894 pdf_delete_annot (state.ctx,
3895 pdf_page_from_fz_page (state.ctx, page->fzpage),
3896 (pdf_annot *) slink->u.annot);
3897 state.dirty = 1;
3899 CAMLreturn0;
3902 CAMLprim void ml_modannot (value ptr_v, value n_v, value str_v)
3904 CAMLparam3 (ptr_v, n_v, str_v);
3905 pdf_document *pdf = pdf_specifics (state.ctx, state.doc);
3907 if (pdf) {
3908 struct page *page;
3909 char *s = String_val (ptr_v);
3910 struct slink *slink;
3912 page = parse_pointer (__func__, s);
3913 slink = &page->slinks[Int_val (n_v)];
3914 pdf_set_annot_contents (state.ctx, (pdf_annot *) slink->u.annot,
3915 String_val (str_v));
3916 state.dirty = 1;
3918 CAMLreturn0;
3921 CAMLprim value ml_hasunsavedchanges (value unit_v)
3923 CAMLparam1 (unit_v);
3924 CAMLreturn (Val_bool (state.dirty));
3927 CAMLprim void ml_savedoc (value path_v)
3929 CAMLparam1 (path_v);
3930 pdf_document *pdf = pdf_specifics (state.ctx, state.doc);
3932 if (pdf) {
3933 pdf_save_document (state.ctx, pdf, String_val (path_v), NULL);
3935 CAMLreturn0;
3938 static void makestippletex (void)
3940 const char pixels[] = "\xff\xff\0\0";
3941 glGenTextures (1, &state.stid);
3942 glBindTexture (GL_TEXTURE_1D, state.stid);
3943 glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
3944 glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
3945 glTexImage1D (
3946 GL_TEXTURE_1D,
3948 GL_ALPHA,
3951 GL_ALPHA,
3952 GL_UNSIGNED_BYTE,
3953 pixels
3957 CAMLprim value ml_fz_version (void)
3959 return caml_copy_string (FZ_VERSION);
3962 CAMLprim value ml_llpp_version (void)
3964 extern char llpp_version[];
3965 return caml_copy_string (llpp_version);
3968 CAMLprim void ml_init (value csock_v, value params_v)
3970 CAMLparam2 (csock_v, params_v);
3971 CAMLlocal2 (trim_v, fuzz_v);
3972 int ret;
3973 int texcount;
3974 char *fontpath;
3975 int colorspace;
3976 int mustoresize;
3977 int haspboext;
3979 state.csock = Int_val (csock_v);
3980 state.rotate = Int_val (Field (params_v, 0));
3981 state.fitmodel = Int_val (Field (params_v, 1));
3982 trim_v = Field (params_v, 2);
3983 texcount = Int_val (Field (params_v, 3));
3984 state.sliceheight = Int_val (Field (params_v, 4));
3985 mustoresize = Int_val (Field (params_v, 5));
3986 colorspace = Int_val (Field (params_v, 6));
3987 fontpath = String_val (Field (params_v, 7));
3989 #ifdef __COCOA__
3990 state.utf8cs = 1;
3991 #else
3992 /* http://www.cl.cam.ac.uk/~mgk25/unicode.html */
3993 if (setlocale (LC_CTYPE, "")) {
3994 const char *cset = nl_langinfo (CODESET);
3995 state.utf8cs = !strcmp (cset, "UTF-8");
3997 else {
3998 printd ("emsg setlocale: %d:%s", errno, strerror (errno));
4000 #endif
4002 if (caml_string_length (Field (params_v, 8)) > 0) {
4003 state.trimcachepath = ystrdup (String_val (Field (params_v, 8)));
4005 if (!state.trimcachepath) {
4006 printd ("emsg failed to strdup trimcachepath: %d:%s",
4007 errno, strerror (errno));
4011 haspboext = Bool_val (Field (params_v, 9));
4013 state.ctx = fz_new_context (NULL, NULL, mustoresize);
4014 fz_register_document_handlers (state.ctx);
4016 state.trimmargins = Bool_val (Field (trim_v, 0));
4017 fuzz_v = Field (trim_v, 1);
4018 state.trimfuzz.x0 = Int_val (Field (fuzz_v, 0));
4019 state.trimfuzz.y0 = Int_val (Field (fuzz_v, 1));
4020 state.trimfuzz.x1 = Int_val (Field (fuzz_v, 2));
4021 state.trimfuzz.y1 = Int_val (Field (fuzz_v, 3));
4023 set_tex_params (colorspace);
4025 if (*fontpath) {
4026 state.face = load_font (fontpath);
4028 else {
4029 int len;
4030 const unsigned char *data;
4032 data = pdf_lookup_substitute_font (state.ctx, 0, 0, 0, 0, &len);
4033 state.face = load_builtin_font (data, len);
4035 if (!state.face) _exit (1);
4037 realloctexts (texcount);
4039 if (haspboext) {
4040 setuppbo ();
4043 makestippletex ();
4045 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
4046 if (ret) {
4047 errx (1, "pthread_create: %s", strerror (ret));
4050 CAMLreturn0;
4053 #if FIXME || !FIXME
4054 static void UNUSED_ATTR NO_OPTIMIZE_ATTR refmacs (void) {}
4055 #endif