After days of debugging
[llpp.git] / link.c
blobfd66f7cdbdf3d742f3e98a4c42c027e28312a575
1 /* lots of code c&p-ed directly from mupdf */
2 #include <errno.h>
3 #include <stdio.h>
4 #include <ctype.h>
5 #include <string.h>
6 #include <stdlib.h>
7 #include <signal.h>
8 #include <wchar.h>
10 #include <unistd.h>
11 #include <pthread.h>
12 #include <sys/time.h>
13 #include <sys/types.h>
14 #include <sys/ioctl.h>
16 #ifdef __CYGWIN__
17 #include <cygwin/socket.h> /* FIONREAD */
18 #else
19 #include <spawn.h>
20 #endif
22 #include <regex.h>
23 #include <stdarg.h>
24 #include <limits.h>
26 #include <GL/gl.h>
28 #include <caml/fail.h>
29 #include <caml/alloc.h>
30 #include <caml/memory.h>
31 #include <caml/unixsupport.h>
33 #include <mupdf/fitz.h>
34 #include <mupdf/cbz.h>
35 #include <mupdf/pdf.h>
36 #include <mupdf/xps.h>
37 #include <mupdf/img.h>
39 #include <ft2build.h>
40 #include FT_FREETYPE_H
42 #ifdef USE_FONTCONFIG
43 #include <fontconfig/fontconfig.h>
44 #endif
46 #define PIGGYBACK
47 #define SUMATRA_LOOKUP
49 #ifndef __USE_GNU
50 extern char **environ;
51 #endif
53 #define MIN(a,b) ((a) < (b) ? (a) : (b))
54 #define MAX(a,b) ((a) > (b) ? (a) : (b))
56 #if defined __GNUC__
57 #define NORETURN_ATTR __attribute__ ((noreturn))
58 #define UNUSED_ATTR __attribute__ ((unused))
59 #if !defined __clang__
60 #define OPTIMIZE_ATTR(n) __attribute__ ((optimize ("O"#n)))
61 #else
62 #define OPTIMIZE_ATTR(n)
63 #endif
64 #define GCC_FMT_ATTR(a, b) __attribute__ ((format (printf, a, b)))
65 #else
66 #define NORETURN_ATTR
67 #define UNUSED_ATTR
68 #define OPTIMIZE_ATTR(n)
69 #define GCC_FMT_ATTR(a, b)
70 #endif
72 #define FMT_s "zu"
74 #define FMT_ptr "p"
75 #define FMT_ptr_cast(p) (p)
76 #define FMT_ptr_cast2(p) (p)
78 static void NORETURN_ATTR GCC_FMT_ATTR (2, 3)
79 err (int exitcode, const char *fmt, ...)
81 va_list ap;
82 int savederrno;
84 savederrno = errno;
85 va_start (ap, fmt);
86 vfprintf (stderr, fmt, ap);
87 va_end (ap);
88 fprintf (stderr, ": %s\n", strerror (savederrno));
89 fflush (stderr);
90 _exit (exitcode);
93 static void NORETURN_ATTR GCC_FMT_ATTR (2, 3)
94 errx (int exitcode, const char *fmt, ...)
96 va_list ap;
98 va_start (ap, fmt);
99 vfprintf (stderr, fmt, ap);
100 va_end (ap);
101 fputc ('\n', stderr);
102 fflush (stderr);
103 _exit (exitcode);
106 #ifndef GL_TEXTURE_RECTANGLE_ARB
107 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
108 #endif
110 #ifndef GL_BGRA
111 #define GL_BGRA 0x80E1
112 #endif
114 #ifndef GL_UNSIGNED_INT_8_8_8_8
115 #define GL_UNSIGNED_INT_8_8_8_8 0x8035
116 #endif
118 #ifndef GL_UNSIGNED_INT_8_8_8_8_REV
119 #define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
120 #endif
122 #if 0
123 #define lprintf printf
124 #else
125 #define lprintf(...)
126 #endif
128 #define ARSERT(cond) for (;;) { \
129 if (!(cond)) { \
130 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
132 break; \
135 struct slice {
136 int h;
137 int texindex;
140 struct tile {
141 int w, h;
142 int slicecount;
143 int sliceheight;
144 struct pbo *pbo;
145 fz_pixmap *pixmap;
146 struct slice slices[1];
149 struct pagedim {
150 int pageno;
151 int rotate;
152 int left;
153 int tctmready;
154 fz_irect bounds;
155 fz_rect pagebox;
156 fz_rect mediabox;
157 fz_matrix ctm, zoomctm, lctm, tctm;
160 struct slink {
161 fz_irect bbox;
162 fz_link *link;
165 enum { DPDF, DXPS, DCBZ, DIMG };
167 struct page {
168 int tgen;
169 int sgen;
170 int type;
171 int pageno;
172 int pdimno;
173 fz_text_page *text;
174 fz_text_sheet *sheet;
175 union {
176 void *ptr;
177 pdf_page *pdfpage;
178 xps_page *xpspage;
179 cbz_page *cbzpage;
180 image_page *imgpage;
181 } u;
182 fz_display_list *dlist;
183 int slinkcount;
184 struct slink *slinks;
185 struct mark {
186 int i;
187 fz_text_span *span;
188 } fmark, lmark;
189 void (*freepage) (void *);
192 struct {
193 int type;
194 int sliceheight;
195 struct pagedim *pagedims;
196 int pagecount;
197 int pagedimcount;
198 union {
199 pdf_document *pdf;
200 xps_document *xps;
201 cbz_document *cbz;
202 image_document *img;
203 } u;
204 fz_context *ctx;
205 int w, h;
207 int texindex;
208 int texcount;
209 GLuint *texids;
211 GLenum texiform;
212 GLenum texform;
213 GLenum texty;
215 fz_colorspace *colorspace;
217 struct {
218 int w, h;
219 struct slice *slice;
220 } *texowners;
222 int rotate;
223 enum { FitWidth, FitProportional, FitPage } fitmodel;
224 int trimmargins;
225 int needoutline;
226 int gen;
227 int aalevel;
229 int trimanew;
230 fz_irect trimfuzz;
231 fz_pixmap *pig;
233 pthread_t thread;
234 int cr, cw;
235 FT_Face face;
237 void (*closedoc) (void);
238 void (*freepage) (void *);
240 char *trimcachepath;
241 int cxack;
243 GLuint stid;
245 int pbo_usable;
246 void (*glBindBufferARB) (GLenum, GLuint);
247 GLboolean (*glUnmapBufferARB) (GLenum);
248 void *(*glMapBufferARB) (GLenum, GLenum);
249 void (*glBufferDataARB) (GLenum, GLsizei, void *, GLenum);
250 void (*glGenBuffersARB) (GLsizei, GLuint *);
251 void (*glDeleteBuffersARB) (GLsizei, GLuint *);
253 GLfloat texcoords[8];
254 GLfloat vertices[16];
256 #ifdef SUMATRA_LOOKUP
257 struct {
258 int count;
259 pdf_obj **objs;
260 pdf_document *pdf;
261 } pdflut;
262 #endif
263 } state;
265 struct pbo {
266 GLuint id;
267 void *ptr;
268 size_t size;
271 static void UNUSED_ATTR debug_rect (const char *cap, fz_rect r)
273 printf ("%s(rect) %.2f,%.2f,%.2f,%.2f\n", cap, r.x0, r.y0, r.x1, r.y1);
276 static void UNUSED_ATTR debug_bbox (const char *cap, fz_irect r)
278 printf ("%s(bbox) %d,%d,%d,%d\n", cap, r.x0, r.y0, r.x1, r.y1);
281 static void UNUSED_ATTR debug_matrix (const char *cap, fz_matrix m)
283 printf ("%s(matrix) %.2f,%.2f,%.2f,%.2f %.2f %.2f\n", cap,
284 m.a, m.b, m.c, m.d, m.e, m.f);
287 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
289 static void lock (const char *cap)
291 int ret = pthread_mutex_lock (&mutex);
292 if (ret) {
293 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
297 static void unlock (const char *cap)
299 int ret = pthread_mutex_unlock (&mutex);
300 if (ret) {
301 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
305 static int trylock (const char *cap)
307 int ret = pthread_mutex_trylock (&mutex);
308 if (ret && ret != EBUSY) {
309 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
311 return ret == EBUSY;
314 static void *parse_pointer (const char *cap, const char *s)
316 int ret;
317 void *ptr;
319 ret = sscanf (s, "%" FMT_ptr, FMT_ptr_cast (&ptr));
320 if (ret != 1) {
321 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
323 return ptr;
326 static double now (void)
328 struct timeval tv;
330 if (gettimeofday (&tv, NULL)) {
331 err (1, "gettimeofday");
333 return tv.tv_sec + tv.tv_usec*1e-6;
336 static int hasdata (void)
338 int ret, avail;
339 ret = ioctl (state.cr, FIONREAD, &avail);
340 if (ret) err (1, "hasdata: FIONREAD error ret=%d", ret);
341 return avail > 0;
344 CAMLprim value ml_hasdata (value fd_v)
346 CAMLparam1 (fd_v);
347 int ret, avail;
349 ret = ioctl (Int_val (fd_v), FIONREAD, &avail);
350 if (ret) uerror ("ioctl (FIONREAD)", Nothing);
351 CAMLreturn (Val_bool (avail > 0));
354 static void readdata (void *p, int size)
356 ssize_t n;
358 again:
359 n = read (state.cr, p, size);
360 if (n < 0) {
361 if (errno == EINTR) goto again;
362 err (1, "read (req %d, ret %zd)", size, n);
364 if (n - size) {
365 if (!n) errx (1, "EOF while reading");
366 errx (1, "read (req %d, ret %zd)", size, n);
370 static void writedata (char *p, int size)
372 ssize_t n;
374 p[0] = (size >> 24) & 0xff;
375 p[1] = (size >> 16) & 0xff;
376 p[2] = (size >> 8) & 0xff;
377 p[3] = (size >> 0) & 0xff;
379 n = write (state.cw, p, size + 4);
380 if (n - size - 4) {
381 if (!n) errx (1, "EOF while writing data");
382 err (1, "write (req %d, ret %zd)", size + 4, n);
386 static int readlen (void)
388 unsigned char p[4];
390 readdata (p, 4);
391 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
394 static void GCC_FMT_ATTR (1, 2) printd (const char *fmt, ...)
396 int size = 200, len;
397 va_list ap;
398 char *buf;
400 buf = malloc (size);
401 for (;;) {
402 if (!buf) err (1, "malloc for temp buf (%d bytes) failed", size);
404 va_start (ap, fmt);
405 len = vsnprintf (buf + 4, size - 4, fmt, ap);
406 va_end (ap);
408 if (len > -1) {
409 if (len < size - 4) {
410 writedata (buf, len);
411 break;
413 else size = len + 5;
415 else {
416 err (1, "vsnprintf for `%s' failed", fmt);
418 buf = realloc (buf, size);
420 free (buf);
423 static void closepdf (void)
425 #ifdef SUMATRA_LOOKUP
426 if (state.pdflut.objs) {
427 int i;
429 for (i = 0; i < state.pdflut.count; ++i) {
430 pdf_drop_obj (state.pdflut.objs[i]);
432 free (state.pdflut.objs);
433 state.pdflut.objs = NULL;
435 #endif
437 if (state.u.pdf) {
438 pdf_close_document (state.u.pdf);
439 state.u.pdf = NULL;
443 static void closexps (void)
445 if (state.u.xps) {
446 xps_close_document (state.u.xps);
447 state.u.xps = NULL;
451 static void closecbz (void)
453 if (state.u.cbz) {
454 cbz_close_document (state.u.cbz);
455 state.u.cbz = NULL;
459 static void closeimg (void)
461 if (state.u.img) {
462 image_close_document (state.u.img);
463 state.u.img = NULL;
467 static void freepdfpage (void *ptr)
469 pdf_free_page (state.u.pdf, ptr);
472 static void freeemptyxxxpage (void *ptr)
474 (void) ptr;
477 static void freexpspage (void *ptr)
479 xps_free_page (state.u.xps, ptr);
482 static void freecbzpage (void *ptr)
484 cbz_free_page (state.u.cbz, ptr);
487 static void freeimgpage (void *ptr)
489 image_free_page (state.u.img, ptr);
492 static void openxref (char *filename, char *password)
494 int i, len;
496 for (i = 0; i < state.texcount; ++i) {
497 state.texowners[i].w = -1;
498 state.texowners[i].slice = NULL;
501 if (state.closedoc) state.closedoc ();
503 len = strlen (filename);
505 state.type = DPDF;
507 int i;
508 struct {
509 char *ext;
510 int type;
511 } tbl[] = {
512 { "pdf", DPDF },
513 { "xps", DXPS },
514 { "zip", DCBZ },
515 { "cbz", DCBZ },
516 { "jpg", DIMG },
517 { "jpeg", DIMG },
518 { "png", DIMG }
521 for (i = 0; i < sizeof (tbl) / sizeof (*tbl); ++i) {
522 int len2 = strlen (tbl[i].ext);
524 if (len2 < len && !strcasecmp (tbl[i].ext, filename + len - len2)) {
525 state.type = tbl[i].type;
526 break;
531 if (state.pagedims) {
532 free (state.pagedims);
533 state.pagedims = NULL;
535 state.pagedimcount = 0;
537 fz_set_aa_level (state.ctx, state.aalevel);
538 switch (state.type) {
539 case DPDF:
540 state.u.pdf = pdf_open_document (state.ctx, filename);
541 if (pdf_needs_password (state.u.pdf)) {
542 int okay = pdf_authenticate_password (state.u.pdf, password);
543 if (!okay) {
544 errx (1, "invalid password");
547 state.pagecount = pdf_count_pages (state.u.pdf);
548 state.closedoc = closepdf;
549 state.freepage = freepdfpage;
550 break;
552 case DXPS:
553 state.u.xps = xps_open_document (state.ctx, filename);
554 state.pagecount = xps_count_pages (state.u.xps);
555 state.closedoc = closexps;
556 state.freepage = freexpspage;
557 break;
559 case DCBZ:
560 state.u.cbz = cbz_open_document (state.ctx, filename);
561 state.pagecount = cbz_count_pages (state.u.cbz);
562 state.closedoc = closecbz;
563 state.freepage = freecbzpage;
564 break;
566 case DIMG:
567 state.u.img = image_open_document (state.ctx, filename);
568 state.pagecount = 1;
569 state.closedoc = closeimg;
570 state.freepage = freeimgpage;
571 break;
575 static void pdfinfo (void)
577 if (state.type == DPDF) {
578 pdf_obj *infoobj;
580 printd ("info PDF version\t%d.%d",
581 state.u.pdf->version / 10, state.u.pdf->version % 10);
583 infoobj = pdf_dict_gets (pdf_trailer (state.u.pdf), "Info");
584 if (infoobj) {
585 int i;
586 char *s;
587 char *items[] = { "Title", "Author", "Creator",
588 "Producer", "CreationDate" };
590 for (i = 0; i < sizeof (items) / sizeof (*items); ++i) {
591 pdf_obj *obj = pdf_dict_gets (infoobj, items[i]);
592 s = pdf_to_utf8 (state.u.pdf, obj);
593 if (*s) {
594 if (i == 0) {
595 printd ("title %s", s);
597 printd ("info %s\t%s", items[i], s);
599 fz_free (state.ctx, s);
602 printd ("infoend");
606 static void unlinktile (struct tile *tile)
608 int i;
610 for (i = 0; i < tile->slicecount; ++i) {
611 struct slice *s = &tile->slices[i];
613 if (s->texindex != -1) {
614 if (state.texowners[s->texindex].slice == s) {
615 state.texowners[s->texindex].slice = NULL;
621 static void freepage (struct page *page)
623 if (page->text) {
624 fz_free_text_page (state.ctx, page->text);
626 if (page->sheet) {
627 fz_free_text_sheet (state.ctx, page->sheet);
629 if (page->slinks) {
630 free (page->slinks);
632 fz_drop_display_list (state.ctx, page->dlist);
633 page->freepage (page->u.ptr);
634 free (page);
637 static void freetile (struct tile *tile)
639 unlinktile (tile);
640 if (!tile->pbo) {
641 #ifndef PIGGYBACK
642 fz_drop_pixmap (state.ctx, tile->pixmap);
643 #else
644 if (state.pig) {
645 fz_drop_pixmap (state.ctx, state.pig);
647 state.pig = tile->pixmap;
648 #endif
650 else {
651 free (tile->pbo);
652 fz_drop_pixmap (state.ctx, tile->pixmap);
654 free (tile);
657 #ifdef __ALTIVEC__
658 #include <stdint.h>
659 #include <altivec.h>
661 static int cacheline32bytes;
663 static void __attribute__ ((constructor)) clcheck (void)
665 char **envp = environ;
666 unsigned long *auxv;
668 while (*envp++);
670 for (auxv = (unsigned long *) envp; *auxv != 0; auxv += 2) {
671 if (*auxv == 19) {
672 cacheline32bytes = auxv[1] == 32;
673 return;
678 static void OPTIMIZE_ATTR (3) clearpixmap (fz_pixmap *pixmap)
680 size_t size = pixmap->w * pixmap->h * pixmap->n;
681 if (cacheline32bytes && size > 32) {
682 intptr_t a1, a2, diff;
683 size_t sizea, i;
684 vector unsigned char v = vec_splat_u8 (-1);
685 vector unsigned char *p;
687 a1 = a2 = (intptr_t) pixmap->samples;
688 a2 = (a1 + 31) & ~31;
689 diff = a2 - a1;
690 sizea = size - diff;
691 p = (void *) a2;
693 while (a1 != a2) *(char *) a1++ = 0xff;
694 for (i = 0; i < (sizea & ~31); i += 32) {
695 __asm volatile ("dcbz %0, %1"::"b"(a2),"r"(i));
696 vec_st (v, i, p);
697 vec_st (v, i + 16, p);
699 while (i < sizea) *((char *) a1 + i++) = 0xff;
701 else fz_clear_pixmap_with_value (state.ctx, pixmap, 0xff);
703 #else
704 #define clearpixmap(p) fz_clear_pixmap_with_value (state.ctx, p, 0xff)
705 #endif
707 static void trimctm (pdf_page *page, int pindex)
709 fz_matrix ctm;
710 struct pagedim *pdim = &state.pagedims[pindex];
712 if (!pdim->tctmready) {
713 if (state.trimmargins) {
714 fz_rect realbox;
715 fz_matrix rm, sm, tm, im, ctm1;
717 fz_rotate (&rm, -pdim->rotate);
718 fz_scale (&sm, 1, -1);
719 fz_concat (&ctm, &rm, &sm);
720 realbox = pdim->mediabox;
721 fz_transform_rect (&realbox, &ctm);
722 fz_translate (&tm, -realbox.x0, -realbox.y0);
723 fz_concat (&ctm1, &ctm, &tm);
724 fz_invert_matrix (&im, &page->ctm);
725 fz_concat (&ctm, &im, &ctm1);
727 else {
728 ctm = fz_identity;
730 pdim->tctm = ctm;
731 pdim->tctmready = 1;
735 static fz_matrix pagectm (struct page *page)
737 fz_matrix ctm, tm;
739 if (page->type == DPDF) {
740 trimctm (page->u.pdfpage, page->pdimno);
741 fz_concat (&ctm,
742 &state.pagedims[page->pdimno].tctm,
743 &state.pagedims[page->pdimno].ctm);
745 else {
746 struct pagedim *pdim = &state.pagedims[page->pdimno];
748 fz_translate (&tm, -pdim->mediabox.x0, -pdim->mediabox.y0);
749 fz_concat (&ctm, &tm, &state.pagedims[page->pdimno].ctm);
751 return ctm;
754 static void *loadpage (int pageno, int pindex)
756 fz_device *dev;
757 struct page *page;
759 page = calloc (sizeof (struct page), 1);
760 if (!page) {
761 err (1, "calloc page %d", pageno);
764 page->dlist = fz_new_display_list (state.ctx);
765 dev = fz_new_list_device (state.ctx, page->dlist);
766 fz_try (state.ctx) {
767 switch (state.type) {
768 case DPDF:
769 page->u.pdfpage = pdf_load_page (state.u.pdf, pageno);
770 pdf_run_page (state.u.pdf, page->u.pdfpage, dev,
771 &fz_identity, NULL);
772 page->freepage = freepdfpage;
773 break;
775 case DXPS:
776 page->u.xpspage = xps_load_page (state.u.xps, pageno);
777 xps_run_page (state.u.xps, page->u.xpspage, dev,
778 &fz_identity, NULL);
779 page->freepage = freexpspage;
780 break;
782 case DCBZ:
783 page->u.cbzpage = cbz_load_page (state.u.cbz, pageno);
784 cbz_run_page (state.u.cbz, page->u.cbzpage, dev,
785 &fz_identity, NULL);
786 page->freepage = freecbzpage;
787 break;
789 case DIMG:
790 page->u.imgpage = image_load_page (state.u.img, pageno);
791 image_run_page (state.u.img, page->u.imgpage, dev,
792 &fz_identity, NULL);
793 page->freepage = freeimgpage;
794 break;
797 fz_catch (state.ctx) {
798 page->u.ptr = NULL;
799 page->freepage = freeemptyxxxpage;
801 fz_free_device (dev);
803 page->pdimno = pindex;
804 page->pageno = pageno;
805 page->sgen = state.gen;
806 page->tgen = state.gen;
807 page->type = state.type;
809 return page;
812 static struct tile *alloctile (int h)
814 int i;
815 int slicecount;
816 size_t tilesize;
817 struct tile *tile;
819 slicecount = (h + state.sliceheight - 1) / state.sliceheight;
820 tilesize = sizeof (*tile) + ((slicecount - 1) * sizeof (struct slice));
821 tile = calloc (tilesize, 1);
822 if (!tile) {
823 err (1, "can not allocate tile (%" FMT_s " bytes)", tilesize);
825 for (i = 0; i < slicecount; ++i) {
826 int sh = MIN (h, state.sliceheight);
827 tile->slices[i].h = sh;
828 tile->slices[i].texindex = -1;
829 h -= sh;
831 tile->slicecount = slicecount;
832 tile->sliceheight = state.sliceheight;
833 return tile;
836 #ifdef OBSCURED_OPT
837 struct obs {
838 int cured;
839 fz_irect b;
842 static void obs_fill_image (fz_device *dev, fz_image *image,
843 const fz_matrix *ctm, float alpha)
845 struct obs *obs = dev->user;
847 if (!obs->cured && fabs (1.0 - alpha) < 1e6) {
848 fz_irect b;
849 fz_rect rect = fz_unit_rect;
851 fz_transform_rect (&rect, ctm);
852 fz_round_rect (&b, &rect);
853 fz_intersect_irect (&b, &obs->b);
854 obs->cured = b.x0 == obs->b.x0
855 && b.x1 == obs->b.x1
856 && b.y0 == obs->b.y0
857 && b.y1 == obs->b.y1;
861 static int obscured (struct page *page, fz_irect bbox)
863 fz_rect rect;
864 fz_matrix ctm;
865 fz_device dev;
866 struct obs obs;
868 memset (&dev, 0, sizeof (dev));
869 memset (&obs, 0, sizeof (obs));
870 dev.hints = 0;
871 dev.flags = 0;
872 dev.user = &obs;
873 dev.ctx = state.ctx;
874 dev.fill_image = obs_fill_image;
875 obs.b = bbox;
876 fz_rect_from_irect (&rect, &bbox);
877 ctm = pagectm (page);
878 fz_run_display_list (page->dlist, &dev, &ctm, &rect, NULL);
879 return obs.cured;
881 #define OBSCURED obscured
882 #else
883 #define OBSCURED(a, b) 0
884 #endif
886 static struct tile *rendertile (struct page *page, int x, int y, int w, int h,
887 struct pbo *pbo)
889 fz_rect rect;
890 fz_irect bbox;
891 fz_matrix ctm;
892 fz_device *dev;
893 struct tile *tile;
894 struct pagedim *pdim;
896 tile = alloctile (h);
897 pdim = &state.pagedims[page->pdimno];
899 bbox = pdim->bounds;
900 bbox.x0 += x;
901 bbox.y0 += y;
902 bbox.x1 = bbox.x0 + w;
903 bbox.y1 = bbox.y0 + h;
905 if (state.pig) {
906 if (state.pig->w == w
907 && state.pig->h == h
908 && state.pig->colorspace == state.colorspace) {
909 tile->pixmap = state.pig;
910 tile->pixmap->x = bbox.x0;
911 tile->pixmap->y = bbox.y0;
913 else {
914 fz_drop_pixmap (state.ctx, state.pig);
916 state.pig = NULL;
918 if (!tile->pixmap) {
919 if (pbo) {
920 tile->pixmap =
921 fz_new_pixmap_with_bbox_and_data (state.ctx, state.colorspace,
922 &bbox, pbo->ptr);
923 tile->pbo = pbo;
925 else {
926 tile->pixmap =
927 fz_new_pixmap_with_bbox (state.ctx, state.colorspace, &bbox);
931 tile->w = w;
932 tile->h = h;
933 if (!page->u.ptr || ((w < 128 && h < 128) || !OBSCURED (page, bbox))) {
934 clearpixmap (tile->pixmap);
936 dev = fz_new_draw_device (state.ctx, tile->pixmap);
937 ctm = pagectm (page);
938 fz_rect_from_irect (&rect, &bbox);
939 fz_run_display_list (page->dlist, dev, &ctm, &rect, NULL);
940 fz_free_device (dev);
942 return tile;
945 #ifdef SUMATRA_LOOKUP
946 /* Following two functions are taken (almost) verbatim from SumatraPDF
947 PdfEngine.cpp
949 static void
950 pdf_load_page_objs_rec(pdf_document *doc, pdf_obj *node, int *page_no, pdf_obj **page_objs)
952 fz_context *ctx = doc->ctx;
954 if (pdf_mark_obj(node))
955 fz_throw(ctx, FZ_ERROR_GENERIC, "cycle in page tree");
957 fz_try(ctx) {
958 pdf_obj *kids = pdf_dict_gets(node, "Kids");
959 int len = pdf_array_len(kids), i;
960 for (i = 0; i < len; i++) {
961 pdf_obj *kid = pdf_array_get(kids, i);
962 char *type = pdf_to_name(pdf_dict_gets(kid, "Type"));
963 if (!strcmp(type, "Page")) {
964 if (*page_no > pdf_count_pages(doc))
965 fz_throw(ctx, FZ_ERROR_GENERIC, "found more /Page objects than anticipated");
966 page_objs[*page_no - 1] = pdf_keep_obj(kid);
967 (*page_no)++;
969 else if (!strcmp(type, "Pages")) {
970 int count = pdf_to_int(pdf_dict_gets(kid, "Count"));
971 if (count > 0)
972 pdf_load_page_objs_rec(doc, kid, page_no, page_objs);
974 else {
975 fz_warn(ctx, "non-page object in page tree (%s)", type);
979 fz_always(ctx) {
980 pdf_unmark_obj(node);
982 fz_catch(ctx) {
983 fz_rethrow(ctx);
987 static void
988 pdf_load_page_objs(pdf_document *doc, pdf_obj **page_objs)
990 pdf_obj *pages = pdf_dict_getp(pdf_trailer(doc), "Root/Pages");
991 int page_no = 1;
992 pdf_load_page_objs_rec(doc, pages, &page_no, page_objs);
994 #endif
996 static void initpdims (void)
998 double start, end;
999 FILE *trimf = NULL;
1000 fz_rect rootmediabox;
1001 int pageno, trim, show;
1002 int trimw = 0, cxcount;
1004 start = now ();
1006 if (state.trimmargins && state.trimcachepath) {
1007 trimf = fopen (state.trimcachepath, "rb");
1008 if (!trimf) {
1009 trimf = fopen (state.trimcachepath, "wb");
1010 trimw = 1;
1014 if (state.trimmargins || state.type == DPDF || !state.cxack)
1015 cxcount = state.pagecount;
1016 else
1017 cxcount = MIN (state.pagecount, 1);
1019 if (state.type == DPDF) {
1020 pdf_obj *obj;
1021 obj = pdf_dict_getp (pdf_trailer (state.u.pdf),
1022 "Root/Pages/MediaBox");
1023 pdf_to_rect (state.ctx, obj, &rootmediabox);
1026 #ifdef SUMATRA_LOOKUP
1027 if (state.type == DPDF
1028 && (!state.pdflut.objs || state.pdflut.pdf != state.u.pdf)) {
1029 state.pdflut.objs = malloc (sizeof (*state.pdflut.objs) * cxcount);
1030 if (!state.pdflut.objs) {
1031 err (1, "malloc pageobjs %zu %d %zu failed",
1032 sizeof (*state.pdflut.objs), cxcount,
1033 sizeof (*state.pdflut.objs) * cxcount);
1035 state.pdflut.count = cxcount;
1036 pdf_load_page_objs (state.u.pdf, state.pdflut.objs);
1037 state.pdflut.pdf = state.u.pdf;
1039 #endif
1041 for (pageno = 0; pageno < cxcount; ++pageno) {
1042 int rotate = 0;
1043 struct pagedim *p;
1044 fz_rect mediabox;
1046 switch (state.type) {
1047 case DPDF: {
1048 pdf_document *pdf = state.u.pdf;
1049 pdf_obj *pageref, *pageobj;
1051 #ifdef SUMATRA_LOOKUP
1052 pageref = state.pdflut.objs[pageno];
1053 #else
1054 pageref = pdf_lookup_page_obj (pdf, pageno);
1055 #endif
1056 pageobj = pdf_resolve_indirect (pageref);
1058 if (state.trimmargins) {
1059 pdf_obj *obj;
1060 pdf_page *page;
1062 fz_try (state.ctx) {
1063 page = pdf_load_page (pdf, pageno);
1064 obj = pdf_dict_gets (pageobj, "llpp.TrimBox");
1065 trim = state.trimanew || !obj;
1066 if (trim) {
1067 fz_rect rect;
1068 fz_matrix ctm;
1069 fz_device *dev;
1071 dev = fz_new_bbox_device (state.ctx, &rect);
1072 dev->hints |= FZ_IGNORE_SHADE;
1073 fz_invert_matrix (&ctm, &page->ctm);
1074 pdf_run_page (pdf, page, dev, &fz_identity, NULL);
1075 fz_free_device (dev);
1077 rect.x0 += state.trimfuzz.x0;
1078 rect.x1 += state.trimfuzz.x1;
1079 rect.y0 += state.trimfuzz.y0;
1080 rect.y1 += state.trimfuzz.y1;
1081 fz_transform_rect (&rect, &ctm);
1082 fz_intersect_rect (&rect, &page->mediabox);
1084 if (fz_is_empty_rect (&rect)) {
1085 mediabox = page->mediabox;
1087 else {
1088 mediabox = rect;
1091 obj = pdf_new_array (pdf, 4);
1092 pdf_array_push (obj, pdf_new_real (pdf, mediabox.x0));
1093 pdf_array_push (obj, pdf_new_real (pdf, mediabox.y0));
1094 pdf_array_push (obj, pdf_new_real (pdf, mediabox.x1));
1095 pdf_array_push (obj, pdf_new_real (pdf, mediabox.y1));
1096 pdf_dict_puts (pageobj, "llpp.TrimBox", obj);
1098 else {
1099 mediabox.x0 = pdf_to_real (pdf_array_get (obj, 0));
1100 mediabox.y0 = pdf_to_real (pdf_array_get (obj, 1));
1101 mediabox.x1 = pdf_to_real (pdf_array_get (obj, 2));
1102 mediabox.y1 = pdf_to_real (pdf_array_get (obj, 3));
1105 rotate = page->rotate;
1106 pdf_free_page (pdf, page);
1108 show = trim ? pageno % 5 == 0 : pageno % 20 == 0;
1109 if (show) {
1110 printd ("progress %f Trimming %d",
1111 (double) (pageno + 1) / state.pagecount,
1112 pageno + 1);
1115 fz_catch (state.ctx) {
1116 fprintf (stderr, "failed to load page %d\n", pageno+1);
1119 else {
1120 int empty = 0;
1121 fz_rect cropbox;
1123 pdf_to_rect (state.ctx, pdf_dict_gets (pageobj, "MediaBox"),
1124 &mediabox);
1125 if (fz_is_empty_rect (&mediabox)) {
1126 mediabox.x0 = 0;
1127 mediabox.y0 = 0;
1128 mediabox.x1 = 612;
1129 mediabox.y1 = 792;
1130 empty = 1;
1133 pdf_to_rect (state.ctx, pdf_dict_gets (pageobj, "CropBox"),
1134 &cropbox);
1135 if (!fz_is_empty_rect (&cropbox)) {
1136 if (empty) {
1137 mediabox = cropbox;
1139 else {
1140 fz_intersect_rect (&mediabox, &cropbox);
1143 else {
1144 if (empty) {
1145 if (fz_is_empty_rect (&rootmediabox)) {
1146 fprintf (stderr,
1147 "cannot find page size for page %d\n",
1148 pageno+1);
1150 else {
1151 mediabox = rootmediabox;
1155 rotate = pdf_to_int (pdf_dict_gets (pageobj, "Rotate"));
1157 break;
1160 case DXPS:
1162 xps_page *page;
1164 fz_try (state.ctx) {
1165 page = xps_load_page (state.u.xps, pageno);
1166 xps_bound_page (state.u.xps, page, &mediabox);
1167 rotate = 0;
1168 if (state.trimmargins) {
1169 fz_rect rect;
1170 fz_device *dev;
1172 dev = fz_new_bbox_device (state.ctx, &rect);
1173 dev->hints |= FZ_IGNORE_SHADE;
1174 xps_run_page (state.u.xps, page, dev,
1175 &fz_identity, NULL);
1176 fz_free_device (dev);
1178 rect.x0 += state.trimfuzz.x0;
1179 rect.x1 += state.trimfuzz.x1;
1180 rect.y0 += state.trimfuzz.y0;
1181 rect.y1 += state.trimfuzz.y1;
1182 fz_intersect_rect (&rect, &mediabox);
1184 if (!fz_is_empty_rect (&rect)) {
1185 mediabox = rect;
1188 xps_free_page (state.u.xps, page);
1189 if (!state.cxack) {
1190 printd ("progress %f loading %d",
1191 (double) (pageno + 1) / state.pagecount,
1192 pageno + 1);
1195 fz_catch (state.ctx) {
1198 break;
1200 case DCBZ:
1202 rotate = 0;
1203 mediabox.x0 = 0;
1204 mediabox.y0 = 0;
1205 mediabox.x1 = 900;
1206 mediabox.y1 = 900;
1207 if (state.trimmargins && trimw) {
1208 cbz_page *page;
1210 fz_try (state.ctx) {
1211 page = cbz_load_page (state.u.cbz, pageno);
1212 cbz_bound_page (state.u.cbz, page, &mediabox);
1213 cbz_free_page (state.u.cbz, page);
1214 printd ("progress %f Trimming %d",
1215 (double) (pageno + 1) / state.pagecount,
1216 pageno + 1);
1218 fz_catch (state.ctx) {
1219 fprintf (stderr, "failed to load page %d\n", pageno+1);
1221 if (trimf) {
1222 int n = fwrite (&mediabox, sizeof (mediabox), 1, trimf);
1223 if (n - 1) {
1224 err (1, "fwrite trim mediabox");
1228 else {
1229 if (trimf) {
1230 int n = fread (&mediabox, sizeof (mediabox), 1, trimf);
1231 if (n - 1) {
1232 err (1, "fread trim mediabox");
1235 else {
1236 if (state.cxack) {
1237 cbz_page *page;
1238 fz_try (state.ctx) {
1239 page = cbz_load_page (state.u.cbz, pageno);
1240 cbz_bound_page (state.u.cbz, page, &mediabox);
1241 cbz_free_page (state.u.cbz, page);
1243 fz_catch (state.ctx) {
1244 fprintf (stderr, "failed to load cbz page\n");
1250 break;
1252 case DIMG:
1254 image_page *page;
1256 rotate = 0;
1257 mediabox.x0 = 0;
1258 mediabox.y0 = 0;
1259 mediabox.x1 = 900;
1260 mediabox.y1 = 900;
1262 fz_try (state.ctx) {
1263 page = image_load_page (state.u.img, pageno);
1264 image_bound_page (state.u.img, page, &mediabox);
1265 image_free_page (state.u.img, page);
1267 fz_catch (state.ctx) {
1268 fprintf (stderr, "failed to load page %d\n", pageno+1);
1271 break;
1273 default:
1274 ARSERT (0 && state.type);
1277 if (state.pagedimcount == 0
1278 || (p = &state.pagedims[state.pagedimcount-1], p->rotate != rotate)
1279 || memcmp (&p->mediabox, &mediabox, sizeof (mediabox))) {
1280 size_t size;
1282 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
1283 state.pagedims = realloc (state.pagedims, size);
1284 if (!state.pagedims) {
1285 err (1, "realloc pagedims to %" FMT_s " (%d elems)",
1286 size, state.pagedimcount + 1);
1289 p = &state.pagedims[state.pagedimcount++];
1290 p->rotate = rotate;
1291 p->mediabox = mediabox;
1292 p->pageno = pageno;
1295 end = now ();
1296 if (state.trimmargins) {
1297 printd ("progress 1 Trimmed %d pages in %f seconds",
1298 state.pagecount, end - start);
1300 else {
1301 printd ("vmsg Processed %d pages in %f seconds",
1302 state.pagecount, end - start);
1304 state.trimanew = 0;
1305 if (trimf) {
1306 if (fclose (trimf)) {
1307 err (1, "fclose");
1312 static void layout (void)
1314 int pindex;
1315 fz_rect box;
1316 fz_matrix ctm, rm;
1317 struct pagedim *p = p;
1318 double zw, w, maxw = 0.0, zoom = zoom;
1320 if (state.pagedimcount == 0) return;
1322 switch (state.fitmodel) {
1323 case FitProportional:
1324 for (pindex = 0; pindex < state.pagedimcount; ++pindex) {
1325 double x0, x1;
1327 p = &state.pagedims[pindex];
1328 fz_rotate (&rm, p->rotate + state.rotate);
1329 box = p->mediabox;
1330 fz_transform_rect (&box, &rm);
1332 x0 = MIN (box.x0, box.x1);
1333 x1 = MAX (box.x0, box.x1);
1335 w = x1 - x0;
1336 maxw = MAX (w, maxw);
1337 zoom = state.w / maxw;
1339 break;
1341 case FitPage:
1342 maxw = state.w;
1343 break;
1345 case FitWidth:
1346 break;
1348 default:
1349 ARSERT (0 && state.fitmodel);
1352 for (pindex = 0; pindex < state.pagedimcount; ++pindex) {
1353 fz_rect rect;
1354 fz_matrix tm, sm;
1356 p = &state.pagedims[pindex];
1357 fz_rotate (&ctm, state.rotate);
1358 fz_rotate (&rm, p->rotate + state.rotate);
1359 box = p->mediabox;
1360 fz_transform_rect (&box, &rm);
1361 w = box.x1 - box.x0;
1362 switch (state.fitmodel) {
1363 case FitProportional:
1364 p->left = ((maxw - w) * zoom) / 2.0;
1365 break;
1366 case FitPage:
1368 double zh, h;
1369 zw = maxw / w;
1370 h = box.y1 - box.y0;
1371 zh = state.h / h;
1372 zoom = MIN (zw, zh);
1373 p->left = (maxw - (w * zoom)) / 2.0;
1375 break;
1376 case FitWidth:
1377 p->left = 0;
1378 zoom = state.w / w;
1379 break;
1382 fz_scale (&p->zoomctm, zoom, zoom);
1383 fz_concat (&ctm, &p->zoomctm, &ctm);
1385 fz_rotate (&rm, p->rotate);
1386 p->pagebox = p->mediabox;
1387 fz_transform_rect (&p->pagebox, &rm);
1388 p->pagebox.x1 -= p->pagebox.x0;
1389 p->pagebox.y1 -= p->pagebox.y0;
1390 p->pagebox.x0 = 0;
1391 p->pagebox.y0 = 0;
1392 rect = p->pagebox;
1393 fz_transform_rect (&rect, &ctm);
1394 fz_round_rect (&p->bounds, &rect);
1395 p->ctm = ctm;
1397 fz_translate (&tm, 0, -p->mediabox.y1);
1398 fz_scale (&sm, zoom, -zoom);
1399 fz_concat (&ctm, &tm, &sm);
1400 fz_concat (&p->lctm, &ctm, &rm);
1402 p->tctmready = 0;
1405 do {
1406 int x0 = MIN (p->bounds.x0, p->bounds.x1);
1407 int y0 = MIN (p->bounds.y0, p->bounds.y1);
1408 int x1 = MAX (p->bounds.x0, p->bounds.x1);
1409 int y1 = MAX (p->bounds.y0, p->bounds.y1);
1410 int w = x1 - x0;
1411 int h = y1 - y0;
1413 printd ("pdim %d %d %d %d", p->pageno, w, h, p->left);
1414 } while (p-- != state.pagedims);
1417 static
1418 struct anchor { int n; int x; int y; int w; int h; }
1419 desttoanchor (fz_link_dest *dest)
1421 int i;
1422 struct anchor a;
1423 struct pagedim *pdim = state.pagedims;
1425 a.n = -1;
1426 a.x = 0;
1427 a.y = 0;
1428 for (i = 0; i < state.pagedimcount; ++i) {
1429 if (state.pagedims[i].pageno > dest->ld.gotor.page)
1430 break;
1431 pdim = &state.pagedims[i];
1433 if (dest->ld.gotor.flags & fz_link_flag_t_valid) {
1434 fz_point p;
1435 if (dest->ld.gotor.flags & fz_link_flag_l_valid)
1436 p.x = dest->ld.gotor.lt.x;
1437 else
1438 p.x = 0.0;
1439 p.y = dest->ld.gotor.lt.y;
1440 fz_transform_point (&p, &pdim->lctm);
1441 a.y = p.y;
1442 a.x = p.x;
1444 if (dest->ld.gotor.page >= 0 && dest->ld.gotor.page < 1<<30) {
1445 double x0, x1, y0, y1;
1447 x0 = MIN (pdim->bounds.x0, pdim->bounds.x1);
1448 x1 = MAX (pdim->bounds.x0, pdim->bounds.x1);
1449 a.w = x1 - x0;
1450 y0 = MIN (pdim->bounds.y0, pdim->bounds.y1);
1451 y1 = MAX (pdim->bounds.y0, pdim->bounds.y1);
1452 a.h = y1 - y0;
1453 a.n = dest->ld.gotor.page;
1455 return a;
1458 static void recurse_outline (fz_outline *outline, int level)
1460 while (outline) {
1461 switch (outline->dest.kind) {
1462 case FZ_LINK_GOTO:
1464 struct anchor a = desttoanchor (&outline->dest);
1466 if (a.n >= 0) {
1467 printd ("o %d %d %d %d %s",
1468 level, a.n, a.y, a.h, outline->title);
1471 break;
1473 case FZ_LINK_URI:
1474 printd ("ou %d %" FMT_s " %s %s", level,
1475 strlen (outline->title), outline->title,
1476 outline->dest.ld.uri.uri);
1477 break;
1479 case FZ_LINK_NONE:
1480 printd ("on %d %s", level, outline->title);
1481 break;
1483 default:
1484 printd ("emsg Unhandled outline kind %d for %s\n",
1485 outline->dest.kind, outline->title);
1486 break;
1488 if (outline->down) {
1489 recurse_outline (outline->down, level + 1);
1491 outline = outline->next;
1495 static void process_outline (void)
1497 fz_outline *outline;
1499 if (!state.needoutline) return;
1501 state.needoutline = 0;
1502 switch (state.type) {
1503 case DPDF:
1504 outline = pdf_load_outline (state.u.pdf);
1505 break;
1506 case DXPS:
1507 outline = xps_load_outline (state.u.xps);
1508 break;
1509 default:
1510 outline = NULL;
1511 break;
1513 if (outline) {
1514 recurse_outline (outline, 0);
1515 fz_free_outline (state.ctx, outline);
1519 static char *strofspan (fz_text_span *span)
1521 char *p;
1522 char utf8[10];
1523 fz_text_char *ch;
1524 size_t size = 0, cap = 80;
1526 p = malloc (cap + 1);
1527 if (!p) return NULL;
1529 for (ch = span->text; ch < span->text + span->len; ++ch) {
1530 int n = fz_runetochar (utf8, ch->c);
1531 if (size + n > cap) {
1532 cap *= 2;
1533 p = realloc (p, cap + 1);
1534 if (!p) return NULL;
1537 memcpy (p + size, utf8, n);
1538 size += n;
1540 p[size] = 0;
1541 return p;
1544 static int matchspan (regex_t *re, fz_text_page *page,
1545 fz_text_span *span, fz_matrix ctm,
1546 int stop, int pageno, double start)
1548 int ret;
1549 char *p;
1550 regmatch_t rm;
1551 int a, b, c;
1552 fz_rect sb, eb;
1553 fz_point p1, p2, p3, p4;
1555 p = strofspan (span);
1556 if (!p) return -1;
1558 ret = regexec (re, p, 1, &rm, 0);
1559 if (ret) {
1560 free (p);
1561 if (ret != REG_NOMATCH) {
1562 size_t size;
1563 char errbuf[80];
1564 size = regerror (ret, re, errbuf, sizeof (errbuf));
1565 printd ("msg regexec error `%.*s'",
1566 (int) size, errbuf);
1567 return -1;
1569 return 0;
1571 else {
1572 int l = span->len;
1574 for (a = 0, c = 0; c < rm.rm_so && a < l; a++) {
1575 c += fz_runelen (span->text[a].c);
1577 for (b = a; c < rm.rm_eo - 1 && b < l; b++) {
1578 c += fz_runelen (span->text[b].c);
1581 if (fz_runelen (span->text[b].c) > 1) {
1582 b = MAX (0, b-1);
1585 fz_text_char_bbox (&sb, span, a);
1586 fz_text_char_bbox (&eb, span, b);
1588 p1.x = sb.x0;
1589 p1.y = sb.y0;
1590 p2.x = eb.x1;
1591 p2.y = sb.y0;
1592 p3.x = eb.x1;
1593 p3.y = eb.y1;
1594 p4.x = sb.x0;
1595 p4.y = eb.y1;
1597 if (!stop) {
1598 printd ("firstmatch %d %d %f %f %f %f %f %f %f %f",
1599 pageno, 1,
1600 p1.x, p1.y,
1601 p2.x, p2.y,
1602 p3.x, p3.y,
1603 p4.x, p4.y);
1605 printd ("progress 1 found at %d `%.*s' in %f sec",
1606 pageno + 1, (int) (rm.rm_eo - rm.rm_so), &p[rm.rm_so],
1607 now () - start);
1609 else {
1610 printd ("match %d %d %f %f %f %f %f %f %f %f",
1611 pageno, 2,
1612 p1.x, p1.y,
1613 p2.x, p2.y,
1614 p3.x, p3.y,
1615 p4.x, p4.y);
1617 free (p);
1618 return 1;
1622 static int compareblocks (const void *l, const void *r)
1624 fz_text_block const *ls = l;
1625 fz_text_block const* rs = r;
1626 return ls->bbox.y0 - rs->bbox.y0;
1629 /* wishful thinking function */
1630 static void search (regex_t *re, int pageno, int y, int forward)
1632 int i, j;
1633 fz_matrix ctm;
1634 fz_device *tdev;
1635 union { void *ptr; pdf_page *pdfpage; xps_page *xpspage; } u;
1636 fz_text_page *text;
1637 fz_text_sheet *sheet;
1638 struct pagedim *pdim, *pdimprev;
1639 int stop = 0, niters = 0;
1640 double start, end;
1642 if (!(state.type == DPDF || state.type == DXPS))
1643 return;
1645 start = now ();
1646 while (pageno >= 0 && pageno < state.pagecount && !stop) {
1647 if (niters++ == 5) {
1648 niters = 0;
1649 if (hasdata ()) {
1650 printd ("progress 1 attention requested aborting search at %d",
1651 pageno);
1652 stop = 1;
1654 else {
1655 printd ("progress %f searching in page %d",
1656 (double) (pageno + 1) / state.pagecount,
1657 pageno);
1660 pdimprev = NULL;
1661 for (i = 0; i < state.pagedimcount; ++i) {
1662 pdim = &state.pagedims[i];
1663 if (pdim->pageno == pageno) {
1664 goto found;
1666 if (pdim->pageno > pageno) {
1667 pdim = pdimprev;
1668 goto found;
1670 pdimprev = pdim;
1672 pdim = pdimprev;
1673 found:
1675 sheet = fz_new_text_sheet (state.ctx);
1676 text = fz_new_text_page (state.ctx);
1677 tdev = fz_new_text_device (state.ctx, sheet, text);
1679 switch (state.type) {
1680 case DPDF:
1681 u.ptr = NULL;
1682 fz_try (state.ctx) {
1683 u.pdfpage = pdf_load_page (state.u.pdf, pageno);
1684 trimctm (u.pdfpage, pdim - state.pagedims);
1685 fz_concat (&ctm, &pdim->tctm, &pdim->zoomctm);
1686 fz_begin_page (tdev, &fz_infinite_rect, &ctm);
1687 pdf_run_page (state.u.pdf, u.pdfpage, tdev, &ctm, NULL);
1688 fz_end_page (tdev);
1690 fz_catch (state.ctx) {
1691 fz_free_device (tdev);
1692 u.ptr = NULL;
1693 goto nextiter;
1695 break;
1697 case DXPS:
1698 u.xpspage = xps_load_page (state.u.xps, pageno);
1699 xps_run_page (state.u.xps, u.xpspage, tdev, &pdim->ctm, NULL);
1700 break;
1702 default:
1703 ARSERT (0 && state.type);
1706 qsort (text->blocks, text->len, sizeof (*text->blocks), compareblocks);
1707 fz_free_device (tdev);
1709 for (j = 0; j < text->len; ++j) {
1710 int k;
1711 fz_page_block *pb;
1712 fz_text_block *block;
1714 pb = &text->blocks[forward ? j : text->len - 1 - j];
1715 if (pb->type != FZ_PAGE_BLOCK_TEXT) continue;
1716 block = pb->u.text;
1718 for (k = 0; k < block->len; ++k) {
1719 fz_text_line *line;
1720 fz_text_span *span;
1722 if (forward) {
1723 line = &block->lines[k];
1724 if (line->bbox.y0 < y + 1) continue;
1726 else {
1727 line = &block->lines[block->len - 1 - k];
1728 if (line->bbox.y0 > y - 1) continue;
1731 for (span = line->first_span; span; span = span->next) {
1732 switch (matchspan (re, text, span, ctm,
1733 stop, pageno, start)) {
1734 case 0: break;
1735 case 1: stop = 1; break;
1736 case -1: stop = 1; goto endloop;
1741 nextiter:
1742 if (forward) {
1743 pageno += 1;
1744 y = 0;
1746 else {
1747 pageno -= 1;
1748 y = INT_MAX;
1750 endloop:
1751 fz_free_text_page (state.ctx, text);
1752 fz_free_text_sheet (state.ctx, sheet);
1753 if (u.ptr) {
1754 state.freepage (u.ptr);
1757 end = now ();
1758 if (!stop) {
1759 printd ("progress 1 no matches %f sec", end - start);
1761 printd ("clearrects");
1764 static void set_tex_params (int colorspace)
1766 union {
1767 unsigned char b;
1768 unsigned int s;
1769 } endianness = {1};
1771 switch (colorspace) {
1772 case 0:
1773 state.texiform = GL_RGBA8;
1774 state.texform = GL_RGBA;
1775 state.texty = GL_UNSIGNED_BYTE;
1776 state.colorspace = fz_device_rgb (state.ctx);
1777 break;
1778 case 1:
1779 state.texiform = GL_RGBA8;
1780 state.texform = GL_BGRA;
1781 state.texty = endianness.s > 1
1782 ? GL_UNSIGNED_INT_8_8_8_8
1783 : GL_UNSIGNED_INT_8_8_8_8_REV;
1784 state.colorspace = fz_device_bgr (state.ctx);
1785 break;
1786 case 2:
1787 state.texiform = GL_LUMINANCE_ALPHA;
1788 state.texform = GL_LUMINANCE_ALPHA;
1789 state.texty = GL_UNSIGNED_BYTE;
1790 state.colorspace = fz_device_gray (state.ctx);
1791 break;
1792 default:
1793 errx (1, "invalid colorspce %d", colorspace);
1797 static void realloctexts (int texcount)
1799 size_t size;
1801 if (texcount == state.texcount) return;
1803 if (texcount < state.texcount) {
1804 glDeleteTextures (state.texcount - texcount,
1805 state.texids + texcount);
1808 size = texcount * sizeof (*state.texids);
1809 state.texids = realloc (state.texids, size);
1810 if (!state.texids) {
1811 err (1, "realloc texids %" FMT_s, size);
1814 size = texcount * sizeof (*state.texowners);
1815 state.texowners = realloc (state.texowners, size);
1816 if (!state.texowners) {
1817 err (1, "realloc texowners %" FMT_s, size);
1819 if (texcount > state.texcount) {
1820 int i;
1822 glGenTextures (texcount - state.texcount,
1823 state.texids + state.texcount);
1824 for (i = state.texcount; i < texcount; ++i) {
1825 state.texowners[i].w = -1;
1826 state.texowners[i].slice = NULL;
1829 state.texcount = texcount;
1830 state.texindex = 0;
1833 static char *mbtoutf8 (char *s)
1835 char *p, *r;
1836 wchar_t *tmp;
1837 size_t i, ret, len;
1839 len = mbstowcs (NULL, s, strlen (s));
1840 if (len == 0) {
1841 return s;
1843 else {
1844 if (len == (size_t) -1) {
1845 return s;
1849 tmp = malloc (len * sizeof (wchar_t));
1850 if (!tmp) {
1851 return s;
1854 ret = mbstowcs (tmp, s, len);
1855 if (ret == (size_t) -1) {
1856 free (tmp);
1857 return s;
1860 len = 0;
1861 for (i = 0; i < ret; ++i) {
1862 len += fz_runelen (tmp[i]);
1865 p = r = malloc (len + 1);
1866 if (!r) {
1867 free (tmp);
1868 return s;
1871 for (i = 0; i < ret; ++i) {
1872 p += fz_runetochar (p, tmp[i]);
1874 *p = 0;
1875 free (tmp);
1876 return r;
1879 CAMLprim value ml_mbtoutf8 (value s_v)
1881 CAMLparam1 (s_v);
1882 CAMLlocal1 (ret_v);
1883 char *s, *r;
1885 s = String_val (s_v);
1886 r = mbtoutf8 (s);
1887 if (r == s) {
1888 ret_v = s_v;
1890 else {
1891 ret_v = caml_copy_string (r);
1892 free (r);
1894 CAMLreturn (ret_v);
1897 static void * mainloop (void *unused)
1899 char *p = NULL;
1900 int len, ret, oldlen = 0;
1902 for (;;) {
1903 len = readlen ();
1904 if (len == 0) {
1905 errx (1, "readlen returned 0");
1908 if (oldlen < len + 1) {
1909 p = realloc (p, len + 1);
1910 if (!p) {
1911 err (1, "realloc %d failed", len + 1);
1913 oldlen = len + 1;
1915 readdata (p, len);
1916 p[len] = 0;
1918 if (!strncmp ("open", p, 4)) {
1919 int wthack, off, ok = 0;
1920 char *password;
1921 char *filename;
1922 char *utf8filename;
1923 size_t filenamelen;
1925 ret = sscanf (p + 5, " %d %d %n", &wthack, &state.cxack, &off);
1926 if (ret != 2) {
1927 errx (1, "malformed open `%.*s' ret=%d", len, p, ret);
1930 filename = p + 5 + off;
1931 filenamelen = strlen (filename);
1932 password = filename + filenamelen + 1;
1934 lock ("open");
1935 fz_try (state.ctx) {
1936 openxref (filename, password);
1937 ok = 1;
1939 fz_catch (state.ctx) {
1940 utf8filename = mbtoutf8 (filename);
1941 printd ("msg Could not open %s", utf8filename);
1943 if (ok) {
1944 pdfinfo ();
1945 initpdims ();
1947 unlock ("open");
1949 if (ok) {
1950 if (!wthack) {
1951 utf8filename = mbtoutf8 (filename);
1952 printd ("msg Opened %s (press h/F1 to get help)",
1953 utf8filename);
1954 if (utf8filename != filename) {
1955 free (utf8filename);
1958 state.needoutline = 1;
1961 else if (!strncmp ("cs", p, 2)) {
1962 int i, colorspace;
1964 ret = sscanf (p + 2, " %d", &colorspace);
1965 if (ret != 1) {
1966 errx (1, "malformed cs `%.*s' ret=%d", len, p, ret);
1968 lock ("cs");
1969 set_tex_params (colorspace);
1970 for (i = 0; i < state.texcount; ++i) {
1971 state.texowners[i].w = -1;
1972 state.texowners[i].slice = NULL;
1974 unlock ("cs");
1976 else if (!strncmp ("freepage", p, 8)) {
1977 void *ptr;
1979 ret = sscanf (p + 8, " %" FMT_ptr, FMT_ptr_cast (&ptr));
1980 if (ret != 1) {
1981 errx (1, "malformed freepage `%.*s' ret=%d", len, p, ret);
1983 freepage (ptr);
1985 else if (!strncmp ("freetile", p, 8)) {
1986 void *ptr;
1988 ret = sscanf (p + 8, " %" FMT_ptr, FMT_ptr_cast (&ptr));
1989 if (ret != 1) {
1990 errx (1, "malformed freetile `%.*s' ret=%d", len, p, ret);
1992 freetile (ptr);
1994 else if (!strncmp ("search", p, 6)) {
1995 int icase, pageno, y, ret, len2, forward;
1996 char *pattern;
1997 regex_t re;
1999 ret = sscanf (p + 6, " %d %d %d %d,%n",
2000 &icase, &pageno, &y, &forward, &len2);
2001 if (ret != 4) {
2002 errx (1, "malformed search `%s' ret=%d", p, ret);
2005 pattern = p + 6 + len2;
2006 ret = regcomp (&re, pattern,
2007 REG_EXTENDED | (icase ? REG_ICASE : 0));
2008 if (ret) {
2009 char errbuf[80];
2010 size_t size;
2012 size = regerror (ret, &re, errbuf, sizeof (errbuf));
2013 printd ("msg regcomp failed `%.*s'", (int) size, errbuf);
2015 else {
2016 search (&re, pageno, y, forward);
2017 regfree (&re);
2020 else if (!strncmp ("geometry", p, 8)) {
2021 int w, h, fitmodel;
2023 printd ("clear");
2024 ret = sscanf (p + 8, " %d %d %d", &w, &h, &fitmodel);
2025 if (ret != 3) {
2026 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
2029 lock ("geometry");
2030 state.h = h;
2031 if (w != state.w) {
2032 int i;
2033 state.w = w;
2034 for (i = 0; i < state.texcount; ++i) {
2035 state.texowners[i].slice = NULL;
2038 state.fitmodel = fitmodel;
2039 layout ();
2040 process_outline ();
2042 state.gen++;
2043 unlock ("geometry");
2044 printd ("continue %d", state.pagecount);
2046 else if (!strncmp ("reqlayout", p, 9)) {
2047 char *nameddest;
2048 int rotate, fitmodel, off, h;
2050 printd ("clear");
2051 ret = sscanf (p + 9, " %d %d %d %n",
2052 &rotate, &fitmodel, &h, &off);
2053 if (ret != 3) {
2054 errx (1, "bad reqlayout line `%.*s' ret=%d", len, p, ret);
2056 lock ("reqlayout");
2057 if (state.rotate != rotate || state.fitmodel != fitmodel) {
2058 state.gen += 1;
2060 state.rotate = rotate;
2061 state.fitmodel = fitmodel;
2062 state.h = h;
2063 layout ();
2064 process_outline ();
2066 nameddest = p + 9 + off;
2067 if (state.type == DPDF && nameddest && *nameddest) {
2068 struct anchor a;
2069 fz_link_dest dest;
2070 pdf_obj *needle, *obj;
2072 needle = pdf_new_string (state.u.pdf, nameddest,
2073 strlen (nameddest));
2074 obj = pdf_lookup_dest (state.u.pdf, needle);
2075 if (obj) {
2076 dest = pdf_parse_link_dest (state.u.pdf, FZ_LINK_GOTO, obj);
2078 a = desttoanchor (&dest);
2079 if (a.n >= 0) {
2080 printd ("a %d %d %d", a.n, a.x, a.y);
2082 else {
2083 printd ("emsg failed to parse destination `%s'\n",
2084 nameddest);
2087 else {
2088 printd ("emsg destination `%s' not found\n",
2089 nameddest);
2091 pdf_drop_obj (needle);
2094 state.gen++;
2095 unlock ("reqlayout");
2096 printd ("continue %d", state.pagecount);
2098 else if (!strncmp ("page", p, 4)) {
2099 double a, b;
2100 struct page *page;
2101 int pageno, pindex, ret;
2103 ret = sscanf (p + 4, " %d %d", &pageno, &pindex);
2104 if (ret != 2) {
2105 errx (1, "bad page line `%.*s' ret=%d", len, p, ret);
2108 lock ("page");
2109 a = now ();
2110 page = loadpage (pageno, pindex);
2111 b = now ();
2112 unlock ("page");
2114 printd ("page %" FMT_ptr " %f", FMT_ptr_cast2 (page), b - a);
2116 else if (!strncmp ("tile", p, 4)) {
2117 int x, y, w, h, ret;
2118 struct page *page;
2119 struct tile *tile;
2120 double a, b;
2121 void *data;
2123 ret = sscanf (p + 4, " %" FMT_ptr " %d %d %d %d %" FMT_ptr,
2124 FMT_ptr_cast (&page), &x, &y, &w, &h, &data);
2125 if (ret != 6) {
2126 errx (1, "bad tile line `%.*s' ret=%d", len, p, ret);
2129 lock ("tile");
2130 a = now ();
2131 tile = rendertile (page, x, y, w, h, data);
2132 b = now ();
2133 unlock ("tile");
2135 printd ("tile %d %d %" FMT_ptr " %u %f",
2136 x, y,
2137 FMT_ptr_cast2 (tile),
2138 tile->w * tile->h * tile->pixmap->n,
2139 b - a);
2141 else if (!strncmp ("settrim", p, 7)) {
2142 fz_irect fuzz;
2143 int trimmargins;
2145 ret = sscanf (p + 7, " %d %d %d %d %d",
2146 &trimmargins, &fuzz.x0, &fuzz.y0, &fuzz.x1, &fuzz.y1);
2147 if (ret != 5) {
2148 errx (1, "malformed settrim `%.*s' ret=%d", len, p, ret);
2150 printd ("clear");
2151 lock ("settrim");
2152 state.trimmargins = trimmargins;
2153 state.needoutline = 1;
2154 if (memcmp (&fuzz, &state.trimfuzz, sizeof (fuzz))) {
2155 state.trimanew = 1;
2156 state.trimfuzz = fuzz;
2158 state.pagedimcount = 0;
2159 free (state.pagedims);
2160 state.pagedims = NULL;
2161 initpdims ();
2162 layout ();
2163 process_outline ();
2164 unlock ("settrim");
2165 printd ("continue %d", state.pagecount);
2167 else if (!strncmp ("sliceh", p, 6)) {
2168 int h;
2170 ret = sscanf (p + 6, " %d", &h);
2171 if (ret != 1) {
2172 errx (1, "malformed sliceh `%.*s' ret=%d", len, p, ret);
2174 if (h != state.sliceheight) {
2175 int i;
2177 state.sliceheight = h;
2178 for (i = 0; i < state.texcount; ++i) {
2179 state.texowners[i].w = -1;
2180 state.texowners[i].h = -1;
2181 state.texowners[i].slice = NULL;
2185 else if (!strncmp ("interrupt", p, 9)) {
2186 printd ("vmsg interrupted");
2188 else {
2189 errx (1, "unknown command %.*s", len, p);
2192 return 0;
2195 CAMLprim value ml_realloctexts (value texcount_v)
2197 CAMLparam1 (texcount_v);
2198 int ok;
2200 if (trylock ("ml_realloctexts")) {
2201 ok = 0;
2202 goto done;
2204 realloctexts (Int_val (texcount_v));
2205 ok = 1;
2206 unlock ("ml_realloctexts");
2208 done:
2209 CAMLreturn (Val_bool (ok));
2212 static void recti (int x0, int y0, int x1, int y1)
2214 GLfloat *v = state.vertices;
2216 glVertexPointer (2, GL_FLOAT, 0, v);
2217 v[0] = x0; v[1] = y0;
2218 v[2] = x1; v[3] = y0;
2219 v[4] = x0; v[5] = y1;
2220 v[6] = x1; v[7] = y1;
2221 glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
2224 static void showsel (struct page *page, int ox, int oy)
2226 int seen = 0;
2227 fz_irect bbox;
2228 fz_rect rect;
2229 fz_text_line *line;
2230 fz_page_block *pageb;
2231 fz_text_block *block;
2232 struct mark first, last;
2233 unsigned char selcolor[] = {15,15,15,140};
2235 first = page->fmark;
2236 last = page->lmark;
2238 if (!first.span || !last.span) return;
2240 glEnable (GL_BLEND);
2241 glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA);
2242 glColor4ubv (selcolor);
2244 ox += state.pagedims[page->pdimno].bounds.x0;
2245 oy += state.pagedims[page->pdimno].bounds.y0;
2246 for (pageb = page->text->blocks;
2247 pageb < page->text->blocks + page->text->len;
2248 ++pageb) {
2249 if (pageb->type != FZ_PAGE_BLOCK_TEXT) continue;
2250 block = pageb->u.text;
2252 for (line = block->lines;
2253 line < block->lines + block->len;
2254 ++line) {
2255 fz_text_span *span;
2256 rect = fz_empty_rect;
2258 for (span = line->first_span; span; span = span->next) {
2259 int i, j, k;
2260 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0;
2262 j = 0;
2263 k = span->len - 1;
2265 if (span == page->fmark.span && span == page->lmark.span) {
2266 seen = 1;
2267 j = MIN (first.i, last.i);
2268 k = MAX (first.i, last.i);
2270 else {
2271 if (span == first.span) {
2272 seen = 1;
2273 j = first.i;
2275 else if (span == last.span) {
2276 seen = 1;
2277 k = last.i;
2281 if (seen) {
2282 for (i = j; i <= k; ++i) {
2283 fz_rect bbox;
2284 fz_union_rect (&rect,
2285 fz_text_char_bbox (&bbox, span, i));
2287 fz_round_rect (&bbox, &rect);
2288 lprintf ("%d %d %d %d oy=%d ox=%d\n",
2289 bbox.x0,
2290 bbox.y0,
2291 bbox.x1,
2292 bbox.y1,
2293 oy, ox);
2295 recti (bbox.x0 + ox, bbox.y0 + oy,
2296 bbox.x1 + ox, bbox.y1 + oy);
2297 if (span == last.span) {
2298 goto done;
2300 rect = fz_empty_rect;
2305 done:
2306 glDisable (GL_BLEND);
2309 #include "glfont.c"
2311 static void highlightlinks (struct page *page, int xoff, int yoff)
2313 fz_matrix ctm, tm, pm;
2314 fz_link *link, *links;
2315 GLfloat *texcoords = state.texcoords;
2316 GLfloat *vertices = state.vertices;
2318 switch (page->type) {
2319 case DPDF:
2320 links = page->u.pdfpage->links;
2321 break;
2323 case DXPS:
2324 links = page->u.xpspage->links;
2325 break;
2327 default:
2328 return;
2331 glEnable (GL_TEXTURE_1D);
2332 glEnable (GL_BLEND);
2333 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2334 glBindTexture (GL_TEXTURE_1D, state.stid);
2336 xoff -= state.pagedims[page->pdimno].bounds.x0;
2337 yoff -= state.pagedims[page->pdimno].bounds.y0;
2338 fz_translate (&tm, xoff, yoff);
2339 pm = pagectm (page);
2340 fz_concat (&ctm, &pm, &tm);
2342 glTexCoordPointer (1, GL_FLOAT, 0, texcoords);
2343 glVertexPointer (2, GL_FLOAT, 0, vertices);
2345 for (link = links; link; link = link->next) {
2346 fz_point p1, p2, p3, p4;
2348 p1.x = link->rect.x0;
2349 p1.y = link->rect.y0;
2351 p2.x = link->rect.x1;
2352 p2.y = link->rect.y0;
2354 p3.x = link->rect.x1;
2355 p3.y = link->rect.y1;
2357 p4.x = link->rect.x0;
2358 p4.y = link->rect.y1;
2360 fz_transform_point (&p1, &ctm);
2361 fz_transform_point (&p2, &ctm);
2362 fz_transform_point (&p3, &ctm);
2363 fz_transform_point (&p4, &ctm);
2365 switch (link->dest.kind) {
2366 case FZ_LINK_GOTO: glColor3ub (255, 0, 0); break;
2367 case FZ_LINK_URI: glColor3ub (0, 0, 255); break;
2368 case FZ_LINK_LAUNCH: glColor3ub (0, 255, 0); break;
2369 default: glColor3ub (0, 0, 0); break;
2373 float w, h, s, t;
2375 w = p2.x - p1.x;
2376 h = p2.y - p1.y;
2377 t = sqrtf (w*w + h*h) * .25f;
2379 w = p3.x - p2.x;
2380 h = p3.y - p2.y;
2381 s = sqrtf (w*w + h*h) * .25f;
2383 texcoords[0] = 0; vertices[0] = p1.x; vertices[1] = p1.y;
2384 texcoords[1] = t; vertices[2] = p2.x; vertices[3] = p2.y;
2386 texcoords[2] = 0; vertices[4] = p2.x; vertices[5] = p2.y;
2387 texcoords[3] = s; vertices[6] = p3.x; vertices[7] = p3.y;
2389 texcoords[4] = 0; vertices[8] = p3.x; vertices[9] = p3.y;
2390 texcoords[5] = t; vertices[10] = p4.x; vertices[11] = p4.y;
2392 texcoords[6] = 0; vertices[12] = p4.x; vertices[13] = p4.y;
2393 texcoords[7] = s; vertices[14] = p1.x; vertices[15] = p1.y;
2395 glDrawArrays (GL_LINES, 0, 8);
2398 glDisable (GL_BLEND);
2399 glDisable (GL_TEXTURE_1D);
2402 static int compareslinks (const void *l, const void *r)
2404 struct slink const *ls = l;
2405 struct slink const *rs = r;
2406 if (ls->bbox.y0 == rs->bbox.y0) {
2407 return rs->bbox.x0 - rs->bbox.x0;
2409 return ls->bbox.y0 - rs->bbox.y0;
2412 static void droptext (struct page *page)
2414 if (page->text) {
2415 fz_free_text_page (state.ctx, page->text);
2416 page->fmark.i = -1;
2417 page->lmark.i = -1;
2418 page->fmark.span = NULL;
2419 page->lmark.span = NULL;
2420 page->text = NULL;
2422 if (page->sheet) {
2423 fz_free_text_sheet (state.ctx, page->sheet);
2424 page->sheet = NULL;
2428 static void dropslinks (struct page *page)
2430 if (page->slinks) {
2431 free (page->slinks);
2432 page->slinks = NULL;
2433 page->slinkcount = 0;
2437 static void ensureslinks (struct page *page)
2439 fz_matrix ctm;
2440 int i, count = 0;
2441 size_t slinksize = sizeof (*page->slinks);
2442 fz_link *link, *links;
2444 if (state.gen != page->sgen) {
2445 dropslinks (page);
2446 page->sgen = state.gen;
2448 if (page->slinks) return;
2450 switch (page->type) {
2451 case DPDF:
2452 links = page->u.pdfpage->links;
2453 trimctm (page->u.pdfpage, page->pdimno);
2454 fz_concat (&ctm,
2455 &state.pagedims[page->pdimno].tctm,
2456 &state.pagedims[page->pdimno].ctm);
2457 break;
2459 case DXPS:
2460 links = page->u.xpspage->links;
2461 ctm = state.pagedims[page->pdimno].ctm;
2462 break;
2464 default:
2465 return;
2468 for (link = links; link; link = link->next) {
2469 count++;
2471 if (count > 0) {
2472 page->slinkcount = count;
2473 page->slinks = calloc (count, slinksize);
2474 if (!page->slinks) {
2475 err (1, "realloc slinks %d", count);
2478 for (i = 0, link = links; link; ++i, link = link->next) {
2479 fz_rect rect;
2481 rect = link->rect;
2482 fz_transform_rect (&rect, &ctm);
2483 page->slinks[i].link = link;
2484 fz_round_rect (&page->slinks[i].bbox, &rect);
2486 qsort (page->slinks, count, slinksize, compareslinks);
2490 /* slightly tweaked fmt_ulong by D.J. Bernstein */
2491 static void fmt_linkn (char *s, unsigned int u)
2493 unsigned int len; unsigned int q;
2494 int zma = 'z' - 'a' + 1;
2495 len = 1; q = u;
2496 while (q > zma - 1) { ++len; q /= zma; }
2497 if (s) {
2498 s += len;
2499 do { *--s = 'a' + (u % zma) - (u < zma && len > 1); u /= zma; } while(u);
2500 /* handles u == 0 */
2502 s[len] = 0;
2505 static void highlightslinks (struct page *page, int xoff, int yoff,
2506 int noff, char *targ, int tlen, int hfsize)
2508 int i;
2509 char buf[40];
2510 struct slink *slink;
2511 double x0, y0, x1, y1, w;
2513 ensureslinks (page);
2514 glColor3ub (0xc3, 0xb0, 0x91);
2515 for (i = 0; i < page->slinkcount; ++i) {
2516 fmt_linkn (buf, i + noff);
2517 if (!tlen || !strncmp (targ, buf, tlen)) {
2518 slink = &page->slinks[i];
2520 x0 = slink->bbox.x0 + xoff - 5;
2521 y1 = slink->bbox.y0 + yoff - 5;
2522 y0 = y1 + 10 + hfsize;
2523 w = measure_string (state.face, hfsize, buf);
2524 x1 = x0 + w + 10;
2525 recti (x0, y0, x1, y1);
2529 glEnable (GL_BLEND);
2530 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2531 glEnable (GL_TEXTURE_2D);
2532 glColor3ub (0, 0, 0);
2533 for (i = 0; i < page->slinkcount; ++i) {
2534 fmt_linkn (buf, i + noff);
2535 if (!tlen || !strncmp (targ, buf, tlen)) {
2536 slink = &page->slinks[i];
2538 x0 = slink->bbox.x0 + xoff;
2539 y0 = slink->bbox.y0 + yoff + hfsize;
2540 draw_string (state.face, hfsize, x0, y0, buf);
2543 glDisable (GL_TEXTURE_2D);
2544 glDisable (GL_BLEND);
2547 static void uploadslice (struct tile *tile, struct slice *slice)
2549 int offset;
2550 struct slice *slice1;
2551 unsigned char *texdata;
2553 offset = 0;
2554 for (slice1 = tile->slices; slice != slice1; slice1++) {
2555 offset += slice1->h * tile->w * tile->pixmap->n;
2557 if (slice->texindex != -1 && slice->texindex < state.texcount
2558 && state.texowners[slice->texindex].slice == slice) {
2559 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
2561 else {
2562 int subimage = 0;
2563 int texindex = state.texindex++ % state.texcount;
2565 if (state.texowners[texindex].w == tile->w) {
2566 if (state.texowners[texindex].h >= slice->h) {
2567 subimage = 1;
2569 else {
2570 state.texowners[texindex].h = slice->h;
2573 else {
2574 state.texowners[texindex].h = slice->h;
2577 state.texowners[texindex].w = tile->w;
2578 state.texowners[texindex].slice = slice;
2579 slice->texindex = texindex;
2581 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[texindex]);
2582 if (tile->pbo) {
2583 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, tile->pbo->id);
2584 texdata = 0;
2586 else {
2587 texdata = tile->pixmap->samples;
2589 if (subimage) {
2590 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
2594 tile->w,
2595 slice->h,
2596 state.texform,
2597 state.texty,
2598 texdata+offset
2601 else {
2602 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
2604 state.texiform,
2605 tile->w,
2606 slice->h,
2608 state.texform,
2609 state.texty,
2610 texdata+offset
2613 if (tile->pbo) {
2614 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, 0);
2619 CAMLprim value ml_begintiles (value unit_v)
2621 CAMLparam1 (unit_v);
2622 glEnable (GL_TEXTURE_RECTANGLE_ARB);
2623 glTexCoordPointer (2, GL_FLOAT, 0, state.texcoords);
2624 glVertexPointer (2, GL_FLOAT, 0, state.vertices);
2625 CAMLreturn (unit_v);
2628 CAMLprim value ml_endtiles (value unit_v)
2630 CAMLparam1 (unit_v);
2631 glDisable (GL_TEXTURE_RECTANGLE_ARB);
2632 CAMLreturn (unit_v);
2635 CAMLprim value ml_drawtile (value args_v, value ptr_v)
2637 CAMLparam2 (args_v, ptr_v);
2638 int dispx = Int_val (Field (args_v, 0));
2639 int dispy = Int_val (Field (args_v, 1));
2640 int dispw = Int_val (Field (args_v, 2));
2641 int disph = Int_val (Field (args_v, 3));
2642 int tilex = Int_val (Field (args_v, 4));
2643 int tiley = Int_val (Field (args_v, 5));
2644 char *s = String_val (ptr_v);
2645 struct tile *tile = parse_pointer ("ml_drawtile", s);
2646 int slicey, firstslice;
2647 struct slice *slice;
2648 GLfloat *texcoords = state.texcoords;
2649 GLfloat *vertices = state.vertices;
2651 firstslice = tiley / tile->sliceheight;
2652 slice = &tile->slices[firstslice];
2653 slicey = tiley % tile->sliceheight;
2655 while (disph > 0) {
2656 int dh;
2658 dh = slice->h - slicey;
2659 dh = MIN (disph, dh);
2660 uploadslice (tile, slice);
2662 texcoords[0] = tilex; texcoords[1] = slicey;
2663 texcoords[2] = tilex+dispw; texcoords[3] = slicey;
2664 texcoords[4] = tilex; texcoords[5] = slicey+dh;
2665 texcoords[6] = tilex+dispw; texcoords[7] = slicey+dh;
2667 vertices[0] = dispx; vertices[1] = dispy;
2668 vertices[2] = dispx+dispw; vertices[3] = dispy;
2669 vertices[4] = dispx; vertices[5] = dispy+dh;
2670 vertices[6] = dispx+dispw; vertices[7] = dispy+dh;
2672 glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
2673 dispy += dh;
2674 disph -= dh;
2675 slice++;
2676 ARSERT (!(slice - tile->slices >= tile->slicecount && disph > 0));
2677 slicey = 0;
2679 CAMLreturn (Val_unit);
2682 CAMLprim value ml_postprocess (value ptr_v, value hlinks_v,
2683 value xoff_v, value yoff_v,
2684 value li_v)
2686 CAMLparam5 (ptr_v, hlinks_v, xoff_v, yoff_v, li_v);
2687 int xoff = Int_val (xoff_v);
2688 int yoff = Int_val (yoff_v);
2689 int noff = Int_val (Field (li_v, 0));
2690 char *targ = String_val (Field (li_v, 1));
2691 int tlen = caml_string_length (Field (li_v, 1));
2692 int hfsize = Int_val (Field (li_v, 2));
2693 char *s = String_val (ptr_v);
2694 int hlmask = Int_val (hlinks_v);
2695 struct page *page = parse_pointer ("ml_postprocess", s);
2697 if (!page->u.ptr) {
2698 /* deal with loadpage failed pages */
2699 goto done;
2702 if (hlmask & 1) highlightlinks (page, xoff, yoff);
2703 if (trylock ("ml_postprocess")) {
2704 noff = 0;
2705 goto done;
2707 if (hlmask & 2) {
2708 highlightslinks (page, xoff, yoff, noff, targ, tlen, hfsize);
2709 noff = page->slinkcount;
2711 if (page->tgen == state.gen) {
2712 showsel (page, xoff, yoff);
2714 unlock ("ml_postprocess");
2716 done:
2717 CAMLreturn (Val_int (noff));
2720 static fz_link *getlink (struct page *page, int x, int y)
2722 fz_point p;
2723 fz_matrix ctm;
2724 const fz_matrix *tctm;
2725 fz_link *link, *links;
2727 switch (page->type) {
2728 case DPDF:
2729 trimctm (page->u.pdfpage, page->pdimno);
2730 tctm = &state.pagedims[page->pdimno].tctm;
2731 links = page->u.pdfpage->links;
2732 break;
2734 case DXPS:
2735 tctm = &fz_identity;
2736 links = page->u.xpspage->links;
2737 break;
2739 default:
2740 return NULL;
2742 p.x = x;
2743 p.y = y;
2745 fz_concat (&ctm, tctm, &state.pagedims[page->pdimno].ctm);
2746 fz_invert_matrix (&ctm, &ctm);
2747 fz_transform_point (&p, &ctm);
2749 for (link = links; link; link = link->next) {
2750 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
2751 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
2752 return link;
2756 return NULL;
2759 static void ensuretext (struct page *page)
2761 if (state.gen != page->tgen) {
2762 droptext (page);
2763 page->tgen = state.gen;
2765 if (!page->text) {
2766 fz_matrix ctm;
2767 fz_device *tdev;
2769 page->text = fz_new_text_page (state.ctx);
2770 page->sheet = fz_new_text_sheet (state.ctx);
2771 tdev = fz_new_text_device (state.ctx, page->sheet, page->text);
2772 ctm = pagectm (page);
2773 fz_begin_page (tdev, &fz_infinite_rect, &ctm);
2774 fz_run_display_list (page->dlist, tdev, &ctm, &fz_infinite_rect, NULL);
2775 qsort (page->text->blocks, page->text->len,
2776 sizeof (*page->text->blocks), compareblocks);
2777 fz_end_page (tdev);
2778 fz_free_device (tdev);
2782 CAMLprim value ml_find_page_with_links (value start_page_v, value dir_v)
2784 CAMLparam2 (start_page_v, dir_v);
2785 CAMLlocal1 (ret_v);
2786 int i, dir = Int_val (dir_v);
2787 int start_page = Int_val (start_page_v);
2788 int end_page = dir > 0 ? state.pagecount : -1;
2790 ret_v = Val_int (0);
2791 if (!(state.type == DPDF || state.type == DXPS)) {
2792 goto done;
2795 lock ("ml_findpage_with_links");
2796 for (i = start_page + dir; i != end_page; i += dir) {
2797 int found;
2799 switch (state.type) {
2800 case DPDF:
2802 pdf_page *page = NULL;
2804 fz_try (state.ctx) {
2805 page = pdf_load_page (state.u.pdf, i);
2806 found = !!page->links;
2808 fz_catch (state.ctx) {
2809 found = 0;
2811 if (page) {
2812 freepdfpage (page);
2815 break;
2816 case DXPS:
2818 xps_page *page = xps_load_page (state.u.xps, i);
2819 found = !!page->links;
2820 freexpspage (page);
2822 break;
2824 default:
2825 ARSERT (0 && "invalid document type");
2828 if (found) {
2829 ret_v = caml_alloc_small (1, 1);
2830 Field (ret_v, 0) = Val_int (i);
2831 goto unlock;
2834 unlock:
2835 unlock ("ml_findpage_with_links");
2837 done:
2838 CAMLreturn (ret_v);
2841 enum { dir_first, dir_last };
2842 enum { dir_first_visible, dir_left, dir_right, dir_down, dir_up };
2844 CAMLprim value ml_findlink (value ptr_v, value dir_v)
2846 CAMLparam2 (ptr_v, dir_v);
2847 CAMLlocal2 (ret_v, pos_v);
2848 struct page *page;
2849 int dirtag, i, slinkindex;
2850 struct slink *found = NULL ,*slink;
2851 char *s = String_val (ptr_v);
2853 page = parse_pointer ("ml_findlink", s);
2854 ret_v = Val_int (0);
2855 if (trylock ("ml_findlink")) {
2856 goto done;
2859 ensureslinks (page);
2861 if (Is_block (dir_v)) {
2862 dirtag = Tag_val (dir_v);
2863 switch (dirtag) {
2864 case dir_first_visible:
2866 int x0, y0, dir, first_index, last_index;
2868 pos_v = Field (dir_v, 0);
2869 x0 = Int_val (Field (pos_v, 0));
2870 y0 = Int_val (Field (pos_v, 1));
2871 dir = Int_val (Field (pos_v, 2));
2873 if (dir >= 0) {
2874 dir = 1;
2875 first_index = 0;
2876 last_index = page->slinkcount;
2878 else {
2879 first_index = page->slinkcount - 1;
2880 last_index = -1;
2883 for (i = first_index; i != last_index; i += dir) {
2884 slink = &page->slinks[i];
2885 if (slink->bbox.y0 >= y0 && slink->bbox.x0 >= x0) {
2886 found = slink;
2887 break;
2891 break;
2893 case dir_left:
2894 slinkindex = Int_val (Field (dir_v, 0));
2895 found = &page->slinks[slinkindex];
2896 for (i = slinkindex - 1; i >= 0; --i) {
2897 slink = &page->slinks[i];
2898 if (slink->bbox.x0 < found->bbox.x0) {
2899 found = slink;
2900 break;
2903 break;
2905 case dir_right:
2906 slinkindex = Int_val (Field (dir_v, 0));
2907 found = &page->slinks[slinkindex];
2908 for (i = slinkindex + 1; i < page->slinkcount; ++i) {
2909 slink = &page->slinks[i];
2910 if (slink->bbox.x0 > found->bbox.x0) {
2911 found = slink;
2912 break;
2915 break;
2917 case dir_down:
2918 slinkindex = Int_val (Field (dir_v, 0));
2919 found = &page->slinks[slinkindex];
2920 for (i = slinkindex + 1; i < page->slinkcount; ++i) {
2921 slink = &page->slinks[i];
2922 if (slink->bbox.y0 >= found->bbox.y0) {
2923 found = slink;
2924 break;
2927 break;
2929 case dir_up:
2930 slinkindex = Int_val (Field (dir_v, 0));
2931 found = &page->slinks[slinkindex];
2932 for (i = slinkindex - 1; i >= 0; --i) {
2933 slink = &page->slinks[i];
2934 if (slink->bbox.y0 <= found->bbox.y0) {
2935 found = slink;
2936 break;
2939 break;
2942 else {
2943 dirtag = Int_val (dir_v);
2944 switch (dirtag) {
2945 case dir_first:
2946 found = page->slinks;
2947 break;
2949 case dir_last:
2950 if (page->slinks) {
2951 found = page->slinks + (page->slinkcount - 1);
2953 break;
2956 if (found) {
2957 ret_v = caml_alloc_small (2, 1);
2958 Field (ret_v, 0) = Val_int (found - page->slinks);
2961 unlock ("ml_findlink");
2962 done:
2963 CAMLreturn (ret_v);
2966 enum { uuri, ugoto, utext, uunexpected,
2967 ulaunch, unamed, uremote, uremotedest };
2969 #define LINKTOVAL \
2971 int pageno; \
2973 switch (link->dest.kind) { \
2974 case FZ_LINK_GOTO: \
2976 fz_point p; \
2978 pageno = link->dest.ld.gotor.page; \
2979 p.x = 0; \
2980 p.y = 0; \
2982 if (link->dest.ld.gotor.flags & fz_link_flag_t_valid) { \
2983 p.y = link->dest.ld.gotor.lt.y; \
2984 fz_transform_point (&p, &pdim->lctm); \
2986 tup_v = caml_alloc_tuple (2); \
2987 ret_v = caml_alloc_small (1, ugoto); \
2988 Field (tup_v, 0) = Val_int (pageno); \
2989 Field (tup_v, 1) = Val_int (p.y); \
2990 Field (ret_v, 0) = tup_v; \
2992 break; \
2994 case FZ_LINK_URI: \
2995 str_v = caml_copy_string (link->dest.ld.uri.uri); \
2996 ret_v = caml_alloc_small (1, uuri); \
2997 Field (ret_v, 0) = str_v; \
2998 break; \
3000 case FZ_LINK_LAUNCH: \
3001 str_v = caml_copy_string (link->dest.ld.launch.file_spec); \
3002 ret_v = caml_alloc_small (1, ulaunch); \
3003 Field (ret_v, 0) = str_v; \
3004 break; \
3006 case FZ_LINK_NAMED: \
3007 str_v = caml_copy_string (link->dest.ld.named.named); \
3008 ret_v = caml_alloc_small (1, unamed); \
3009 Field (ret_v, 0) = str_v; \
3010 break; \
3012 case FZ_LINK_GOTOR: \
3014 int rty; \
3016 str_v = caml_copy_string (link->dest.ld.gotor.file_spec); \
3017 pageno = link->dest.ld.gotor.page; \
3018 if (pageno == -1) { \
3019 gr_v = caml_copy_string (link->dest.ld.gotor.dest); \
3020 rty = uremotedest; \
3022 else { \
3023 gr_v = Val_int (pageno); \
3024 rty = uremote; \
3026 tup_v = caml_alloc_tuple (2); \
3027 ret_v = caml_alloc_small (1, rty); \
3028 Field (tup_v, 0) = str_v; \
3029 Field (tup_v, 1) = gr_v; \
3030 Field (ret_v, 0) = tup_v; \
3032 break; \
3034 default: \
3036 char buf[80]; \
3038 snprintf (buf, sizeof (buf), \
3039 "unhandled link kind %d", link->dest.kind); \
3040 str_v = caml_copy_string (buf); \
3041 ret_v = caml_alloc_small (1, uunexpected); \
3042 Field (ret_v, 0) = str_v; \
3044 break; \
3048 CAMLprim value ml_getlink (value ptr_v, value n_v)
3050 CAMLparam2 (ptr_v, n_v);
3051 CAMLlocal4 (ret_v, tup_v, str_v, gr_v);
3052 fz_link *link;
3053 struct page *page;
3054 struct pagedim *pdim;
3055 char *s = String_val (ptr_v);
3057 ret_v = Val_int (0);
3058 if (trylock ("ml_getlink")) {
3059 goto done;
3062 page = parse_pointer ("ml_getlink", s);
3063 ensureslinks (page);
3064 pdim = &state.pagedims[page->pdimno];
3065 link = page->slinks[Int_val (n_v)].link;
3066 LINKTOVAL;
3068 unlock ("ml_getlink");
3069 done:
3070 CAMLreturn (ret_v);
3073 CAMLprim value ml_getlinkcount (value ptr_v)
3075 CAMLparam1 (ptr_v);
3076 struct page *page;
3077 char *s = String_val (ptr_v);
3079 page = parse_pointer ("ml_getlinkcount", s);
3080 CAMLreturn (Val_int (page->slinkcount));
3083 CAMLprim value ml_getlinkrect (value ptr_v, value n_v)
3085 CAMLparam2 (ptr_v, n_v);
3086 CAMLlocal1 (ret_v);
3087 struct page *page;
3088 struct slink *slink;
3089 char *s = String_val (ptr_v);
3091 page = parse_pointer ("ml_getlinkrect", s);
3092 ret_v = caml_alloc_tuple (4);
3093 if (trylock ("ml_getlinkrect")) {
3094 Field (ret_v, 0) = Val_int (0);
3095 Field (ret_v, 1) = Val_int (0);
3096 Field (ret_v, 2) = Val_int (0);
3097 Field (ret_v, 3) = Val_int (0);
3098 goto done;
3100 ensureslinks (page);
3102 slink = &page->slinks[Int_val (n_v)];
3103 Field (ret_v, 0) = Val_int (slink->bbox.x0);
3104 Field (ret_v, 1) = Val_int (slink->bbox.y0);
3105 Field (ret_v, 2) = Val_int (slink->bbox.x1);
3106 Field (ret_v, 3) = Val_int (slink->bbox.y1);
3107 unlock ("ml_getlinkrect");
3109 done:
3110 CAMLreturn (ret_v);
3113 CAMLprim value ml_whatsunder (value ptr_v, value x_v, value y_v)
3115 CAMLparam3 (ptr_v, x_v, y_v);
3116 CAMLlocal4 (ret_v, tup_v, str_v, gr_v);
3117 fz_link *link;
3118 struct page *page;
3119 char *s = String_val (ptr_v);
3120 int x = Int_val (x_v), y = Int_val (y_v);
3121 struct pagedim *pdim;
3123 ret_v = Val_int (0);
3124 if (trylock ("ml_whatsunder")) {
3125 goto done;
3128 page = parse_pointer ("ml_whatsunder", s);
3129 pdim = &state.pagedims[page->pdimno];
3130 x += pdim->bounds.x0;
3131 y += pdim->bounds.y0;
3132 link = getlink (page, x, y);
3133 if (link) {
3134 LINKTOVAL;
3136 else {
3137 fz_rect *b;
3138 fz_page_block *pageb;
3139 fz_text_block *block;
3141 ensuretext (page);
3142 for (pageb = page->text->blocks;
3143 pageb < page->text->blocks + page->text->len;
3144 ++pageb) {
3145 fz_text_line *line;
3146 if (pageb->type != FZ_PAGE_BLOCK_TEXT) continue;
3147 block = pageb->u.text;
3149 b = &block->bbox;
3150 if (!(x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1))
3151 continue;
3153 for (line = block->lines;
3154 line < block->lines + block->len;
3155 ++line) {
3156 fz_text_span *span;
3158 b = &line->bbox;
3159 if (!(x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1))
3160 continue;
3162 for (span = line->first_span; span; span = span->next) {
3163 int charnum;
3165 b = &span->bbox;
3166 if (!(x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1))
3167 continue;
3169 for (charnum = 0; charnum < span->len; ++charnum) {
3170 fz_rect bbox;
3171 fz_text_char_bbox (&bbox, span, charnum);
3172 b = &bbox;
3174 if (x >= b->x0 && x <= b->x1
3175 && y >= b->y0 && y <= b->y1) {
3176 fz_text_style *style = span->text->style;
3177 const char *n2 =
3178 style->font && style->font->name
3179 ? style->font->name
3180 : "Span has no font name"
3182 FT_FaceRec *face = style->font->ft_face;
3183 if (face && face->family_name) {
3184 char *s;
3185 char *n1 = face->family_name;
3186 size_t l1 = strlen (n1);
3187 size_t l2 = strlen (n2);
3189 if (l1 != l2 || memcmp (n1, n2, l1)) {
3190 s = malloc (l1 + l2 + 2);
3191 if (s) {
3192 memcpy (s, n2, l2);
3193 s[l2] = '=';
3194 memcpy (s + l2 + 1, n1, l1 + 1);
3195 str_v = caml_copy_string (s);
3196 free (s);
3200 if (str_v == 0) {
3201 str_v = caml_copy_string (n2);
3203 ret_v = caml_alloc_small (1, utext);
3204 Field (ret_v, 0) = str_v;
3205 goto unlock;
3212 unlock:
3213 unlock ("ml_whatsunder");
3215 done:
3216 CAMLreturn (ret_v);
3219 enum { mark_page, mark_block, mark_line, mark_word };
3221 static int uninteresting (int c)
3223 return c == ' ' || c == '\n' || c == '\t' || c == '\n' || c == '\r'
3224 || ispunct (c);
3227 CAMLprim value ml_clearmark (value ptr_v)
3229 CAMLparam1 (ptr_v);
3230 char *s = String_val (ptr_v);
3231 struct page *page;
3233 if (trylock ("ml_clearmark")) {
3234 goto done;
3237 page = parse_pointer ("ml_clearmark", s);
3238 page->fmark.span = NULL;
3239 page->lmark.span = NULL;
3240 page->fmark.i = 0;
3241 page->lmark.i = 0;
3243 unlock ("ml_clearmark");
3244 done:
3245 CAMLreturn (Val_unit);
3248 CAMLprim value ml_markunder (value ptr_v, value x_v, value y_v, value mark_v)
3250 CAMLparam4 (ptr_v, x_v, y_v, mark_v);
3251 CAMLlocal1 (ret_v);
3252 fz_rect *b;
3253 struct page *page;
3254 fz_text_line *line;
3255 fz_page_block *pageb;
3256 fz_text_block *block;
3257 struct pagedim *pdim;
3258 int mark = Int_val (mark_v);
3259 char *s = String_val (ptr_v);
3260 int x = Int_val (x_v), y = Int_val (y_v);
3262 ret_v = Val_bool (0);
3263 if (trylock ("ml_markunder")) {
3264 goto done;
3267 page = parse_pointer ("ml_markunder", s);
3268 pdim = &state.pagedims[page->pdimno];
3270 ensuretext (page);
3272 if (mark == mark_page) {
3273 int i;
3274 fz_page_block *pb1 = NULL, *pb2 = NULL;
3276 for (i = 0; i < page->text->len; ++i) {
3277 if (page->text->blocks[i].type == FZ_PAGE_BLOCK_TEXT) {
3278 pb1 = &page->text->blocks[i];
3279 break;
3282 if (!pb1) goto unlock;
3284 for (i = page->text->len - 1; i >= 0; --i) {
3285 if (page->text->blocks[i].type == FZ_PAGE_BLOCK_TEXT) {
3286 pb2 = &page->text->blocks[i];
3287 break;
3290 if (!pb2) goto unlock;
3292 block = pb1->u.text;
3294 page->fmark.i = 0;
3295 page->fmark.span = block->lines->first_span;
3297 block = pb2->u.text;
3298 line = &block->lines[block->len - 1];
3299 page->lmark.i = line->last_span->len - 1;
3300 page->lmark.span = line->last_span;
3301 ret_v = Val_bool (1);
3302 goto unlock;
3305 x += pdim->bounds.x0;
3306 y += pdim->bounds.y0;
3308 for (pageb = page->text->blocks;
3309 pageb < page->text->blocks + page->text->len;
3310 ++pageb) {
3311 if (pageb->type != FZ_PAGE_BLOCK_TEXT) continue;
3312 block = pageb->u.text;
3314 b = &block->bbox;
3315 if (!(x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1))
3316 continue;
3318 if (mark == mark_block) {
3319 page->fmark.i = 0;
3320 page->fmark.span = block->lines->first_span;
3322 line = &block->lines[block->len - 1];
3323 page->lmark.i = line->last_span->len - 1;
3324 page->lmark.span = line->last_span;
3325 ret_v = Val_bool (1);
3326 goto unlock;
3329 for (line = block->lines;
3330 line < block->lines + block->len;
3331 ++line) {
3332 fz_text_span *span;
3334 b = &line->bbox;
3335 if (!(x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1))
3336 continue;
3338 if (mark == mark_line) {
3339 page->fmark.i = 0;
3340 page->fmark.span = line->first_span;
3342 page->lmark.i = line->last_span->len - 1;
3343 page->lmark.span = line->last_span;
3344 ret_v = Val_bool (1);
3345 goto unlock;
3348 for (span = line->first_span; span; span = span->next) {
3349 int charnum;
3351 b = &span->bbox;
3352 if (!(x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1))
3353 continue;
3355 for (charnum = 0; charnum < span->len; ++charnum) {
3356 fz_rect bbox;
3357 fz_text_char_bbox (&bbox, span, charnum);
3358 b = &bbox;
3360 if (x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1) {
3361 /* unicode ftw */
3362 int charnum2, charnum3 = -1, charnum4 = -1;
3364 if (uninteresting (span->text[charnum].c)) goto unlock;
3366 for (charnum2 = charnum; charnum2 >= 0; --charnum2) {
3367 if (uninteresting (span->text[charnum2].c)) {
3368 charnum3 = charnum2 + 1;
3369 break;
3372 if (charnum3 == -1) charnum3 = 0;
3374 for (charnum2 = charnum + 1;
3375 charnum2 < span->len;
3376 ++charnum2) {
3377 if (uninteresting (span->text[charnum2].c)) break;
3378 charnum4 = charnum2;
3380 if (charnum4 == -1) goto unlock;
3382 page->fmark.i = charnum3;
3383 page->fmark.span = span;
3385 page->lmark.i = charnum4;
3386 page->lmark.span = span;
3387 ret_v = Val_bool (1);
3388 goto unlock;
3394 unlock:
3395 if (!Bool_val (ret_v)) {
3396 page->fmark.span = NULL;
3397 page->lmark.span = NULL;
3398 page->fmark.i = 0;
3399 page->lmark.i = 0;
3401 unlock ("ml_markunder");
3403 done:
3404 CAMLreturn (ret_v);
3407 CAMLprim value ml_rectofblock (value ptr_v, value x_v, value y_v)
3409 CAMLparam3 (ptr_v, x_v, y_v);
3410 CAMLlocal2 (ret_v, res_v);
3411 fz_rect *b = NULL;
3412 struct page *page;
3413 fz_page_block *pageb;
3414 struct pagedim *pdim;
3415 char *s = String_val (ptr_v);
3416 int x = Int_val (x_v), y = Int_val (y_v);
3418 ret_v = Val_int (0);
3419 if (trylock ("ml_rectofblock")) {
3420 goto done;
3423 page = parse_pointer ("ml_rectofblock", s);
3424 pdim = &state.pagedims[page->pdimno];
3425 x += pdim->bounds.x0;
3426 y += pdim->bounds.y0;
3428 ensuretext (page);
3430 for (pageb = page->text->blocks;
3431 pageb < page->text->blocks + page->text->len;
3432 ++pageb) {
3433 switch (pageb->type) {
3434 case FZ_PAGE_BLOCK_TEXT:
3435 b = &pageb->u.text->bbox;
3436 break;
3438 case FZ_PAGE_BLOCK_IMAGE:
3439 b = &pageb->u.image->bbox;
3440 break;
3442 default:
3443 continue;
3446 if (x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1)
3447 break;
3448 b = NULL;
3450 if (b) {
3451 res_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
3452 ret_v = caml_alloc_small (1, 1);
3453 Store_double_field (res_v, 0, b->x0);
3454 Store_double_field (res_v, 1, b->x1);
3455 Store_double_field (res_v, 2, b->y0);
3456 Store_double_field (res_v, 3, b->y1);
3457 Field (ret_v, 0) = res_v;
3459 unlock ("ml_rectofblock");
3461 done:
3462 CAMLreturn (ret_v);
3465 CAMLprim value ml_seltext (value ptr_v, value rect_v)
3467 CAMLparam2 (ptr_v, rect_v);
3468 fz_rect b;
3469 struct page *page;
3470 struct pagedim *pdim;
3471 char *s = String_val (ptr_v);
3472 int i, x0, x1, y0, y1, fi, li;
3473 fz_text_line *line;
3474 fz_page_block *pageb;
3475 fz_text_block *block;
3476 fz_text_span *span, *fspan, *lspan;
3478 if (trylock ("ml_seltext")) {
3479 goto done;
3482 page = parse_pointer ("ml_seltext", s);
3483 ensuretext (page);
3485 pdim = &state.pagedims[page->pdimno];
3486 x0 = Int_val (Field (rect_v, 0)) + pdim->bounds.x0;
3487 y0 = Int_val (Field (rect_v, 1)) + pdim->bounds.y0;
3488 x1 = Int_val (Field (rect_v, 2)) + pdim->bounds.x0;
3489 y1 = Int_val (Field (rect_v, 3)) + pdim->bounds.y0;
3491 if (y0 > y1) {
3492 int t = y0;
3493 y0 = y1;
3494 y1 = t;
3495 x0 = x1;
3496 x1 = t;
3499 if (0) {
3500 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
3501 glColor3ub (128, 128, 128);
3502 recti (x0, y0, x1, y1);
3503 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
3506 fi = page->fmark.i;
3507 fspan = page->fmark.span;
3509 li = page->lmark.i;
3510 lspan = page->lmark.span;
3512 for (pageb = page->text->blocks;
3513 pageb < page->text->blocks + page->text->len;
3514 ++pageb) {
3515 if (pageb->type != FZ_PAGE_BLOCK_TEXT) continue;
3516 block = pageb->u.text;
3517 for (line = block->lines;
3518 line < block->lines + block->len;
3519 ++line) {
3520 fz_text_span *span;
3522 for (span = line->first_span; span; span = span->next) {
3523 for (i = 0; i < span->len; ++i) {
3524 int selected = 0;
3526 fz_text_char_bbox (&b, span, i);
3528 if (x0 >= b.x0 && x0 <= b.x1
3529 && y0 >= b.y0 && y0 <= b.y1) {
3530 fspan = span;
3531 fi = i;
3532 selected = 1;
3534 if (x1 >= b.x0 && x1 <= b.x1
3535 && y1 >= b.y0 && y1 <= b.y1) {
3536 lspan = span;
3537 li = i;
3538 selected = 1;
3540 if (0 && selected) {
3541 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
3542 glColor3ub (128, 128, 128);
3543 recti (b.x0, b.y0, b.x1, b.y1);
3544 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
3550 if (x1 < x0 && fspan == lspan) {
3551 i = fi;
3552 span = fspan;
3554 fi = li;
3555 fspan = lspan;
3557 li = i;
3558 lspan = span;
3561 page->fmark.i = fi;
3562 page->fmark.span = fspan;
3564 page->lmark.i = li;
3565 page->lmark.span = lspan;
3567 unlock ("ml_seltext");
3569 done:
3570 CAMLreturn (Val_unit);
3573 static int UNUSED_ATTR pipespan (FILE *f, fz_text_span *span, int a, int b)
3575 char buf[4];
3576 int i, len, ret;
3578 for (i = a; i <= b; ++i) {
3579 len = fz_runetochar (buf, span->text[i].c);
3580 ret = fwrite (buf, len, 1, f);
3582 if (ret != 1) {
3583 fprintf (stderr, "failed to write %d bytes ret=%d: %s\n",
3584 len, ret, strerror (errno));
3585 return -1;
3588 return 0;
3591 #ifdef __CYGWIN__
3592 value ml_popen (value UNUSED_ATTR u1, value UNUSED_ATTR u2)
3594 caml_failwith ("ml_popen not implemented under Cygwin");
3596 #else
3597 CAMLprim value ml_popen (value command_v, value fds_v)
3599 CAMLparam2 (command_v, fds_v);
3600 CAMLlocal2 (l_v, tup_v);
3601 int ret;
3602 char *msg = NULL;
3603 value earg_v = Nothing;
3604 posix_spawnattr_t attr;
3605 posix_spawn_file_actions_t fa;
3606 char *argv[] = { "/bin/sh", "-c", String_val (command_v), NULL };
3608 if ((ret = posix_spawn_file_actions_init (&fa)) != 0) {
3609 unix_error (ret, "posix_spawn_file_actions_init", Nothing);
3612 if ((ret = posix_spawnattr_init (&attr)) != 0) {
3613 msg = "posix_spawnattr_init";
3614 goto fail1;
3617 #ifdef POSIX_SPAWN_USEVFORK
3618 if ((ret = posix_spawnattr_setflags (&attr, POSIX_SPAWN_USEVFORK)) != 0) {
3619 msg = "posix_spawnattr_setflags POSIX_SPAWN_USEVFORK";
3620 goto fail;
3622 #endif
3624 for (l_v = fds_v; l_v != Val_int (0); l_v = Field (l_v, 1)) {
3625 int fd1, fd2;
3627 tup_v = Field (l_v, 0);
3628 fd1 = Int_val (Field (tup_v, 0));
3629 fd2 = Int_val (Field (tup_v, 1));
3630 if (fd2 < 0) {
3631 if ((ret = posix_spawn_file_actions_addclose (&fa, fd1)) != 0) {
3632 msg = "posix_spawn_file_actions_addclose";
3633 earg_v = tup_v;
3634 goto fail;
3637 else {
3638 if ((ret = posix_spawn_file_actions_adddup2 (&fa, fd1, fd2)) != 0) {
3639 msg = "posix_spawn_file_actions_adddup2";
3640 earg_v = tup_v;
3641 goto fail;
3646 if ((ret = posix_spawn (NULL, "/bin/sh", &fa, &attr, argv, environ))) {
3647 msg = "posix_spawn";
3648 goto fail;
3651 fail:
3652 if ((ret = posix_spawnattr_destroy (&attr)) != 0) {
3653 fprintf (stderr, "posix_spawnattr_destroy: %s\n", strerror (ret));
3656 fail1:
3657 if ((ret = posix_spawn_file_actions_destroy (&fa)) != 0) {
3658 fprintf (stderr, "posix_spawn_file_actions_destroy: %s\n",
3659 strerror (ret));
3662 if (msg)
3663 unix_error (ret, msg, earg_v);
3665 CAMLreturn (Val_unit);
3667 #endif
3669 CAMLprim value ml_hassel (value ptr_v)
3671 CAMLparam1 (ptr_v);
3672 CAMLlocal1 (ret_v);
3673 struct page *page;
3674 char *s = String_val (ptr_v);
3676 ret_v = Val_bool (0);
3677 if (trylock ("ml_hassel")) {
3678 goto done;
3681 page = parse_pointer ("ml_hassel", s);
3682 ret_v = Val_bool (page->fmark.span && page->lmark.span);
3683 unlock ("ml_hassel");
3684 done:
3685 CAMLreturn (ret_v);
3688 CAMLprim value ml_copysel (value fd_v, value ptr_v)
3690 CAMLparam2 (fd_v, ptr_v);
3691 FILE *f;
3692 int seen = 0;
3693 struct page *page;
3694 fz_text_line *line;
3695 fz_page_block *pageb;
3696 fz_text_block *block;
3697 int fd = Int_val (fd_v);
3698 char *s = String_val (ptr_v);
3700 if (trylock ("ml_copysel")) {
3701 goto done;
3704 page = parse_pointer ("ml_copysel", s);
3706 if (!page->fmark.span || !page->lmark.span) {
3707 fprintf (stderr, "nothing to copy on page %d\n", page->pageno);
3708 goto unlock;
3711 f = fdopen (fd, "w");
3712 if (!f) {
3713 fprintf (stderr, "failed to fdopen sel pipe (from fd %d): %s\n",
3714 fd, strerror (errno));
3715 f = stdout;
3718 for (pageb = page->text->blocks;
3719 pageb < page->text->blocks + page->text->len;
3720 ++pageb) {
3721 if (pageb->type != FZ_PAGE_BLOCK_TEXT) continue;
3722 block = pageb->u.text;
3723 for (line = block->lines;
3724 line < block->lines + block->len;
3725 ++line) {
3726 fz_text_span *span;
3728 for (span = line->first_span; span; span = span->next) {
3729 int a, b;
3731 seen |= span == page->fmark.span || span == page->lmark.span;
3732 a = span == page->fmark.span ? page->fmark.i : 0;
3733 b = span == page->lmark.span ? page->lmark.i : span->len - 1;
3735 if (seen) {
3736 if (pipespan (f, span, a, b)) {
3737 goto close;
3739 if (span == page->lmark.span) {
3740 goto close;
3742 if (span == line->last_span) {
3743 if (putc ('\n', f) == EOF) {
3744 fprintf (stderr,
3745 "failed break line on sel pipe: %s\n",
3746 strerror (errno));
3747 goto close;
3754 close:
3755 if (f != stdout) {
3756 int ret = fclose (f);
3757 fd = -1;
3758 if (ret == -1) {
3759 if (errno != ECHILD) {
3760 fprintf (stderr, "failed to close sel pipe: %s\n",
3761 strerror (errno));
3765 unlock:
3766 unlock ("ml_copysel");
3768 done:
3769 if (fd >= 0) {
3770 if (close (fd)) {
3771 fprintf (stderr, "failed to close sel pipe: %s\n",
3772 strerror (errno));
3775 CAMLreturn (Val_unit);
3778 CAMLprim value ml_getpdimrect (value pagedimno_v)
3780 CAMLparam1 (pagedimno_v);
3781 CAMLlocal1 (ret_v);
3782 int pagedimno = Int_val (pagedimno_v);
3783 fz_rect box;
3785 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
3786 if (trylock ("ml_getpdimrect")) {
3787 box = fz_empty_rect;
3789 else {
3790 box = state.pagedims[pagedimno].mediabox;
3791 unlock ("ml_getpdimrect");
3794 Store_double_field (ret_v, 0, box.x0);
3795 Store_double_field (ret_v, 1, box.x1);
3796 Store_double_field (ret_v, 2, box.y0);
3797 Store_double_field (ret_v, 3, box.y1);
3799 CAMLreturn (ret_v);
3802 CAMLprim value ml_zoom_for_height (value winw_v, value winh_v,
3803 value dw_v, value cols_v)
3805 CAMLparam3 (winw_v, winh_v, dw_v);
3806 CAMLlocal1 (ret_v);
3807 int i;
3808 double zoom = -1.;
3809 double maxh = 0.0;
3810 struct pagedim *p;
3811 double winw = Int_val (winw_v);
3812 double winh = Int_val (winh_v);
3813 double dw = Int_val (dw_v);
3814 double cols = Int_val (cols_v);
3815 double pw = 1.0, ph = 1.0;
3817 if (trylock ("ml_zoom_for_height")) {
3818 goto done;
3821 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
3822 double w = p->pagebox.x1 / cols;
3823 double h = p->pagebox.y1;
3824 if (h > maxh) {
3825 maxh = h;
3826 ph = h;
3827 if (state.fitmodel != FitProportional) pw = w;
3829 if ((state.fitmodel == FitProportional) && w > pw) pw = w;
3832 zoom = (((winh / ph) * pw) + dw) / winw;
3833 unlock ("ml_zoom_for_height");
3834 done:
3835 ret_v = caml_copy_double (zoom);
3836 CAMLreturn (ret_v);
3839 CAMLprim value ml_draw_string (value pt_v, value x_v, value y_v, value string_v)
3841 CAMLparam4 (pt_v, x_v, y_v, string_v);
3842 CAMLlocal1 (ret_v);
3843 int pt = Int_val(pt_v);
3844 int x = Int_val (x_v);
3845 int y = Int_val (y_v);
3846 double w;
3848 w = draw_string (state.face, pt, x, y, String_val (string_v));
3849 ret_v = caml_copy_double (w);
3850 CAMLreturn (ret_v);
3853 CAMLprim value ml_measure_string (value pt_v, value string_v)
3855 CAMLparam2 (pt_v, string_v);
3856 CAMLlocal1 (ret_v);
3857 int pt = Int_val (pt_v);
3858 double w;
3860 w = measure_string (state.face, pt, String_val (string_v));
3861 ret_v = caml_copy_double (w);
3862 CAMLreturn (ret_v);
3865 CAMLprim value ml_getpagebox (value opaque_v)
3867 CAMLparam1 (opaque_v);
3868 CAMLlocal1 (ret_v);
3869 fz_rect rect;
3870 fz_irect bbox;
3871 fz_matrix ctm;
3872 fz_device *dev;
3873 char *s = String_val (opaque_v);
3874 struct page *page = parse_pointer ("ml_getpagebox", s);
3876 ret_v = caml_alloc_tuple (4);
3877 dev = fz_new_bbox_device (state.ctx, &rect);
3878 dev->hints |= FZ_IGNORE_SHADE;
3880 switch (page->type) {
3881 case DPDF:
3882 ctm = pagectm (page);
3883 pdf_run_page (state.u.pdf, page->u.pdfpage, dev, &ctm, NULL);
3884 break;
3886 case DXPS:
3887 ctm = pagectm (page);
3888 xps_run_page (state.u.xps, page->u.xpspage, dev, &ctm, NULL);
3889 break;
3891 default:
3892 rect = fz_infinite_rect;
3893 break;
3896 fz_free_device (dev);
3897 fz_round_rect (&bbox, &rect);
3898 Field (ret_v, 0) = Val_int (bbox.x0);
3899 Field (ret_v, 1) = Val_int (bbox.y0);
3900 Field (ret_v, 2) = Val_int (bbox.x1);
3901 Field (ret_v, 3) = Val_int (bbox.y1);
3903 CAMLreturn (ret_v);
3906 CAMLprim value ml_setaalevel (value level_v)
3908 CAMLparam1 (level_v);
3910 state.aalevel = Int_val (level_v);
3911 CAMLreturn (Val_unit);
3914 #undef pixel
3915 #include <X11/Xlib.h>
3916 #include <GL/glx.h>
3918 static struct {
3919 Display *dpy;
3920 GLXContext ctx;
3921 XVisualInfo *visual;
3922 GLXDrawable drawable;
3923 } glx;
3925 #include "keysym2ucs.c"
3927 CAMLprim value ml_keysymtoutf8 (value keysym_v)
3929 CAMLparam1 (keysym_v);
3930 CAMLlocal1 (str_v);
3931 KeySym keysym = Int_val (keysym_v);
3932 Rune rune;
3933 int len;
3934 char buf[5];
3936 rune = keysym2ucs (keysym);
3937 len = fz_runetochar (buf, rune);
3938 buf[len] = 0;
3939 str_v = caml_copy_string (buf);
3940 CAMLreturn (str_v);
3943 CAMLprim value ml_glx1 (value unit_v)
3945 CAMLparam1 (unit_v);
3946 CAMLlocal1 (tup_v);
3947 int screen;
3948 int attributes[] = { GLX_RGBA, GLX_DOUBLEBUFFER, None };
3950 glx.dpy = XOpenDisplay (NULL);
3951 if (!glx.dpy) {
3952 caml_failwith ("XOpenDisplay");
3955 screen = DefaultScreen (glx.dpy);
3956 glx.visual = glXChooseVisual (glx.dpy, screen, attributes);
3957 if (!glx.visual) {
3958 XCloseDisplay (glx.dpy);
3959 glx.dpy = NULL;
3960 caml_failwith ("glXChooseVisual");
3963 tup_v = caml_alloc_tuple (2);
3964 Field (tup_v, 0) = Val_int (glx.visual->visualid);
3965 Field (tup_v, 1) = Val_int (glx.visual->depth);
3966 CAMLreturn (tup_v);
3969 CAMLprim value ml_glx2 (value win_v)
3971 CAMLparam1 (win_v);
3972 int wid = Int_val (win_v);
3974 glx.ctx = glXCreateContext (glx.dpy, glx.visual, NULL, True);
3975 XFree (glx.visual);
3976 glx.visual = NULL;
3977 if (!glx.ctx) {
3978 XCloseDisplay (glx.dpy);
3979 glx.dpy = NULL;
3980 caml_failwith ("glXCreateContext");
3983 if (!glXMakeCurrent (glx.dpy, wid, glx.ctx)) {
3984 glXDestroyContext (glx.dpy, glx.ctx);
3985 XCloseDisplay (glx.dpy);
3986 glx.dpy = NULL;
3987 glx.ctx = NULL;
3988 caml_failwith ("glXMakeCurrent");
3990 glx.drawable = wid;
3991 CAMLreturn (Val_unit);
3994 CAMLprim value ml_swapb (value unit_v)
3996 CAMLparam1 (unit_v);
3997 glXSwapBuffers (glx.dpy, glx.drawable);
3998 CAMLreturn (Val_unit);
4001 CAMLprim value ml_glxsync (value unit_v)
4003 CAMLparam1 (unit_v);
4004 if (glx.dpy && glx.ctx) {
4005 #ifdef GLX_DO_WAIT
4006 glXWaitX ();
4007 glXWaitGL ();
4008 #endif
4010 CAMLreturn (Val_unit);
4013 enum { piunknown, pilinux, piosx, pisun, pifreebsd,
4014 pidragonflybsd, piopenbsd, pinetbsd, picygwin };
4016 CAMLprim value ml_platform (value unit_v)
4018 CAMLparam1 (unit_v);
4019 int platid = piunknown;
4021 #if defined __linux__
4022 platid = pilinux;
4023 #elif defined __CYGWIN__
4024 platid = picygwin;
4025 #elif defined __DragonFly__
4026 platid = pidragonflybsd;
4027 #elif defined __FreeBSD__
4028 platid = pifreebsd;
4029 #elif defined __OpenBSD__
4030 platid = piopenbsd;
4031 #elif defined __NetBSD__
4032 platid = pinetbsd;
4033 #elif defined __sun__
4034 platid = pisun;
4035 #elif defined __APPLE__
4036 platid = piosx;
4037 #endif
4038 CAMLreturn (Val_int (platid));
4041 CAMLprim value ml_cloexec (value fd_v)
4043 CAMLparam1 (fd_v);
4044 int fd = Int_val (fd_v);
4046 if (fcntl (fd, F_SETFD, FD_CLOEXEC, 1)) {
4047 uerror ("fcntl", Nothing);
4049 CAMLreturn (Val_unit);
4052 CAMLprim value ml_getpbo (value w_v, value h_v, value cs_v)
4054 CAMLparam2 (w_v, h_v);
4055 CAMLlocal1 (ret_v);
4056 struct pbo *pbo;
4057 int w = Int_val (w_v);
4058 int h = Int_val (h_v);
4059 int cs = Int_val (cs_v);
4061 if (state.pbo_usable) {
4062 pbo = calloc (sizeof (*pbo), 1);
4063 if (!pbo) {
4064 err (1, "calloc pbo");
4067 switch (cs) {
4068 case 0:
4069 case 1:
4070 pbo->size = w*h*4;
4071 break;
4072 case 2:
4073 pbo->size = w*h*2;
4074 break;
4075 default:
4076 errx (1, "ml_getpbo: invalid colorspace %d", cs);
4079 state.glGenBuffersARB (1, &pbo->id);
4080 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, pbo->id);
4081 state.glBufferDataARB (GL_PIXEL_UNPACK_BUFFER_ARB, pbo->size,
4082 NULL, GL_STREAM_DRAW);
4083 pbo->ptr = state.glMapBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB,
4084 GL_READ_WRITE);
4085 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, 0);
4086 if (!pbo->ptr) {
4087 fprintf (stderr, "glMapBufferARB failed: %#x\n", glGetError ());
4088 state.glDeleteBuffersARB (1, &pbo->id);
4089 free (pbo);
4090 ret_v = caml_copy_string ("0");
4092 else {
4093 int res;
4094 char *s;
4096 res = snprintf (NULL, 0, "%" FMT_ptr, pbo);
4097 if (res < 0) {
4098 err (1, "snprintf %" FMT_ptr " failed", pbo);
4100 s = malloc (res+1);
4101 if (!s) {
4102 err (1, "malloc %d bytes failed", res+1);
4104 res = sprintf (s, "%" FMT_ptr, pbo);
4105 if (res < 0) {
4106 err (1, "sprintf %" FMT_ptr " failed", pbo);
4108 ret_v = caml_copy_string (s);
4109 free (s);
4112 else {
4113 ret_v = caml_copy_string ("0");
4115 CAMLreturn (ret_v);
4118 CAMLprim value ml_freepbo (value s_v)
4120 CAMLparam1 (s_v);
4121 char *s = String_val (s_v);
4122 struct tile *tile = parse_pointer ("ml_freepbo", s);
4124 if (tile->pbo) {
4125 state.glDeleteBuffersARB (1, &tile->pbo->id);
4126 tile->pbo->id = -1;
4127 tile->pbo->ptr = NULL;
4128 tile->pbo->size = -1;
4130 CAMLreturn (Val_unit);
4133 CAMLprim value ml_unmappbo (value s_v)
4135 CAMLparam1 (s_v);
4136 char *s = String_val (s_v);
4137 struct tile *tile = parse_pointer ("ml_unmappbo", s);
4139 if (tile->pbo) {
4140 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, tile->pbo->id);
4141 if (state.glUnmapBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB) == GL_FALSE) {
4142 errx (1, "glUnmapBufferARB failed: %#x\n", glGetError ());
4144 tile->pbo->ptr = NULL;
4145 state.glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, 0);
4147 CAMLreturn (Val_unit);
4150 static void setuppbo (void)
4152 #define GGPA(n) (*(void (**) ()) &state.n = glXGetProcAddress ((GLubyte *) #n))
4153 state.pbo_usable = GGPA (glBindBufferARB)
4154 && GGPA (glUnmapBufferARB)
4155 && GGPA (glMapBufferARB)
4156 && GGPA (glBufferDataARB)
4157 && GGPA (glGenBuffersARB)
4158 && GGPA (glDeleteBuffersARB);
4159 #undef GGPA
4162 CAMLprim value ml_pbo_usable (value unit_v)
4164 CAMLparam1 (unit_v);
4165 CAMLreturn (Val_bool (state.pbo_usable));
4168 CAMLprim value ml_unproject (value ptr_v, value x_v, value y_v)
4170 CAMLparam3 (ptr_v, x_v, y_v);
4171 CAMLlocal2 (ret_v, tup_v);
4172 struct page *page;
4173 char *s = String_val (ptr_v);
4174 int x = Int_val (x_v), y = Int_val (y_v);
4175 struct pagedim *pdim;
4176 fz_point p;
4177 fz_matrix ctm;
4179 page = parse_pointer ("ml_unproject", s);
4180 pdim = &state.pagedims[page->pdimno];
4182 ret_v = Val_int (0);
4183 if (trylock ("ml_unproject")) {
4184 goto done;
4187 switch (page->type) {
4188 case DPDF:
4189 trimctm (page->u.pdfpage, page->pdimno);
4190 break;
4192 default:
4193 break;
4195 p.x = x + pdim->bounds.x0;
4196 p.y = y + pdim->bounds.y0;
4198 fz_concat (&ctm, &pdim->tctm, &pdim->ctm);
4199 fz_invert_matrix (&ctm, &ctm);
4200 fz_transform_point (&p, &ctm);
4202 tup_v = caml_alloc_tuple (2);
4203 ret_v = caml_alloc_small (1, 1);
4204 Field (tup_v, 0) = Val_int (p.x);
4205 Field (tup_v, 1) = Val_int (p.y);
4206 Field (ret_v, 0) = tup_v;
4208 unlock ("ml_unproject");
4209 done:
4210 CAMLreturn (ret_v);
4213 static void makestippletex (void)
4215 const char pixels[] = "\xff\xff\0\0";
4216 glGenTextures (1, &state.stid);
4217 glBindTexture (GL_TEXTURE_1D, state.stid);
4218 glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
4219 glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
4220 glTexImage1D (
4221 GL_TEXTURE_1D,
4223 GL_ALPHA,
4226 GL_ALPHA,
4227 GL_UNSIGNED_BYTE,
4228 pixels
4232 CAMLprim value ml_fz_version (value unit_v)
4234 return caml_copy_string (FZ_VERSION);
4237 #ifdef USE_FONTCONFIG
4238 static struct {
4239 int inited;
4240 FcConfig *config;
4241 } fc;
4243 static fz_font *fc_load_system_font_func (fz_context *ctx,
4244 const char *name,
4245 int bold,
4246 int italic,
4247 int needs_exact_metrics)
4249 char *buf;
4250 size_t i, size;
4251 fz_font *font;
4252 FcChar8 *path;
4253 FcResult result;
4254 FcPattern *pat, *pat1;
4256 lprintf ("looking up %s bold:%d italic:%d needs_exact_metrics:%d\n",
4257 name, bold, italic, needs_exact_metrics);
4258 if (!fc.inited) {
4259 fc.inited = 1;
4260 fc.config = FcInitLoadConfigAndFonts ();
4261 if (!fc.config) {
4262 lprintf ("FcInitLoadConfigAndFonts failed\n");
4263 return NULL;
4266 if (!fc.config) return NULL;
4268 size = strlen (name);
4269 if (bold) size += sizeof (":bold") - 1;
4270 if (italic) size += sizeof (":italic") - 1;
4271 size += 1;
4273 buf = malloc (size);
4274 if (!buf) {
4275 err (1, "malloc %zu failed", size);
4278 strcpy (buf, name);
4279 if (bold && italic) {
4280 strcat (buf, ":bold:italic");
4282 else {
4283 if (bold) strcat (buf, ":bold");
4284 if (italic) strcat (buf, ":italic");
4286 for (i = 0; i < size; ++i) {
4287 if (buf[i] == ',' || buf[i] == '-') buf[i] = ':';
4290 lprintf ("fcbuf=%s\n", buf);
4291 pat = FcNameParse ((FcChar8 *) buf);
4292 if (!pat) {
4293 printd ("emsg FcNameParse failed\n");
4294 free (buf);
4295 return NULL;
4298 if (!FcConfigSubstitute (fc.config, pat, FcMatchPattern)) {
4299 printd ("emsg FcConfigSubstitute failed\n");
4300 free (buf);
4301 return NULL;
4303 FcDefaultSubstitute (pat);
4305 pat1 = FcFontMatch (fc.config, pat, &result);
4306 if (!pat1) {
4307 printd ("emsg FcFontMatch failed\n");
4308 FcPatternDestroy (pat);
4309 free (buf);
4310 return NULL;
4313 if (FcPatternGetString (pat1, FC_FILE, 0, &path) != FcResultMatch) {
4314 printd ("emsg FcPatternGetString failed\n");
4315 FcPatternDestroy (pat);
4316 FcPatternDestroy (pat1);
4317 free (buf);
4318 return NULL;
4321 #if 0
4322 printd ("emsg name=%s path=%s\n", name, path);
4323 #endif
4324 font = fz_new_font_from_file (ctx, name, (char *) path, 0, 0);
4325 FcPatternDestroy (pat);
4326 FcPatternDestroy (pat1);
4327 free (buf);
4328 return font;
4330 #endif
4332 CAMLprim value ml_init (value pipe_v, value params_v)
4334 CAMLparam2 (pipe_v, params_v);
4335 CAMLlocal2 (trim_v, fuzz_v);
4336 int ret;
4337 int texcount;
4338 char *fontpath;
4339 int colorspace;
4340 int mustoresize;
4341 int haspboext;
4342 struct sigaction sa;
4344 state.cr = Int_val (Field (pipe_v, 0));
4345 state.cw = Int_val (Field (pipe_v, 1));
4346 state.rotate = Int_val (Field (params_v, 0));
4347 state.fitmodel = Int_val (Field (params_v, 1));
4348 trim_v = Field (params_v, 2);
4349 texcount = Int_val (Field (params_v, 3));
4350 state.sliceheight = Int_val (Field (params_v, 4));
4351 mustoresize = Int_val (Field (params_v, 5));
4352 colorspace = Int_val (Field (params_v, 6));
4353 fontpath = String_val (Field (params_v, 7));
4355 state.trimcachepath = strdup (String_val (Field (params_v, 8)));
4356 if (!state.trimcachepath) {
4357 fprintf (stderr, "failed to strdup trimcachepath: %s\n",
4358 strerror (errno));
4360 haspboext = Bool_val (Field (params_v, 9));
4362 state.ctx = fz_new_context (NULL, NULL, mustoresize);
4364 #ifdef USE_FONTCONFIG
4365 fz_install_load_system_font_funcs (
4366 state.ctx, fc_load_system_font_func, NULL
4368 #endif
4370 state.trimmargins = Bool_val (Field (trim_v, 0));
4371 fuzz_v = Field (trim_v, 1);
4372 state.trimfuzz.x0 = Int_val (Field (fuzz_v, 0));
4373 state.trimfuzz.y0 = Int_val (Field (fuzz_v, 1));
4374 state.trimfuzz.x1 = Int_val (Field (fuzz_v, 2));
4375 state.trimfuzz.y1 = Int_val (Field (fuzz_v, 3));
4377 set_tex_params (colorspace);
4379 if (*fontpath) {
4380 #ifndef USE_FONTCONFIG
4381 state.face = load_font (fontpath);
4382 #else
4383 FcChar8 *path;
4384 FcResult result;
4385 char *buf = fontpath;
4386 FcPattern *pat, *pat1;
4388 fc.inited = 1;
4389 fc.config = FcInitLoadConfigAndFonts ();
4390 if (!fc.config) {
4391 errx (1, "FcInitLoadConfigAndFonts failed");
4394 pat = FcNameParse ((FcChar8 *) buf);
4395 if (!pat) {
4396 errx (1, "FcNameParse failed");
4399 if (!FcConfigSubstitute (fc.config, pat, FcMatchPattern)) {
4400 errx (1, "FcConfigSubstitute failed");
4402 FcDefaultSubstitute (pat);
4404 pat1 = FcFontMatch (fc.config, pat, &result);
4405 if (!pat1) {
4406 errx (1, "FcFontMatch failed");
4409 if (FcPatternGetString (pat1, FC_FILE, 0, &path) != FcResultMatch) {
4410 errx (1, "FcPatternGetString failed");
4413 state.face = load_font ((char *) path);
4414 FcPatternDestroy (pat);
4415 FcPatternDestroy (pat1);
4416 #endif
4418 else {
4419 unsigned int len;
4420 void *base = pdf_lookup_substitute_font (0, 0, 0, 0, &len);
4422 state.face = load_builtin_font (base, len);
4424 if (!state.face) _exit (1);
4426 realloctexts (texcount);
4428 if (haspboext) {
4429 setuppbo ();
4432 makestippletex ();
4434 #ifdef __CYGWIN__
4435 sa.sa_handler = SIG_IGN;
4436 sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
4437 #else
4438 sa.sa_handler = SIG_DFL;
4439 sa.sa_flags = SA_RESTART | SA_NOCLDSTOP | SA_NOCLDWAIT;
4440 #endif
4441 if (sigemptyset (&sa.sa_mask)) {
4442 err (1, "sigemptyset");
4444 if (sigaction (SIGCHLD, &sa, NULL)) {
4445 err (1, "sigaction");
4448 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
4449 if (ret) {
4450 errx (1, "pthread_create: %s", strerror (ret));
4453 CAMLreturn (Val_unit);