Track dimensions/pagecount info properly
[llpp.git] / link.c
blob51e93bd7c46fe515b060ae327126a0f3136e005c
1 /* lots of code c&p-ed directly from mupdf */
2 #include <errno.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <stdlib.h>
7 #define PIGGYBACK
9 #ifdef _WIN32
10 #define WIN32_LEAN_AND_MEAN
11 #include <windows.h>
12 #include <winsock2.h>
13 #define fionread_arg long
14 #define ssize_t int
15 #define FMT_ss "d"
16 #ifdef _WIN64
17 #define FMT_s "i64u"
18 #else
19 #define FMT_s "u"
20 #endif
21 #endif
23 #ifdef _MSC_VER
24 #pragma warning (disable:4244)
25 #pragma warning (disable:4996)
26 #pragma warning (disable:4995)
27 #define NORETURN __declspec (noreturn)
28 #define UNUSED
29 #define OPTIMIZE
30 #elif defined __GNUC__
31 #define NORETURN __attribute__ ((noreturn))
32 #define UNUSED __attribute__ ((unused))
33 #define OPTIMIZE(n) __attribute__ ((optimize ("O"#n)))
34 #else
35 #define NORETURN
36 #define UNUSED
37 #define OPTIMIZE
38 #endif
40 #ifdef __MINGW32__
41 /* some versions of MingW have non idempotent %p */
42 #include <inttypes.h>
43 #define FMT_ptr PRIxPTR
44 #define FMT_ptr_cast(p) ((intptr_t *) (p))
45 #define FMT_ptr_cast2(p) ((intptr_t) (p))
46 #else
47 #define FMT_ptr "p"
48 #define FMT_ptr_cast(p) (p)
49 #define FMT_ptr_cast2(p) (p)
50 #endif
52 #ifdef _WIN32
53 static void __declspec (noreturn) sockerr (int exitcode, const char *fmt, ...)
55 va_list ap;
57 va_start (ap, fmt);
58 vfprintf (stderr, fmt, ap);
59 va_end (ap);
60 fprintf (stderr, ": wsaerror 0x%x\n", WSAGetLastError ());
61 _exit (exitcode);
63 #else
64 #define FMT_ss "zd"
65 #define FMT_s "zu"
66 #define fionread_arg int
67 #define ioctlsocket ioctl
68 #define sockerr err
69 #include <unistd.h>
70 #endif
72 #include <regex.h>
73 #include <ctype.h>
74 #include <stdarg.h>
75 #include <limits.h>
77 #ifndef _WIN32
78 #include <pthread.h>
79 #include <sys/time.h>
80 #include <sys/types.h>
81 #include <sys/socket.h>
82 #include <sys/ioctl.h>
83 #endif
85 static void NORETURN err (int exitcode, const char *fmt, ...)
87 va_list ap;
88 int savederrno;
90 savederrno = errno;
91 va_start (ap, fmt);
92 vfprintf (stderr, fmt, ap);
93 va_end (ap);
94 fprintf (stderr, ": %s\n", strerror (savederrno));
95 fflush (stderr);
96 _exit (exitcode);
99 static void NORETURN errx (int exitcode, const char *fmt, ...)
101 va_list ap;
103 va_start (ap, fmt);
104 vfprintf (stderr, fmt, ap);
105 va_end (ap);
106 fputc ('\n', stderr);
107 fflush (stderr);
108 _exit (exitcode);
111 #ifdef __APPLE__
112 #include <OpenGL/gl.h>
113 #else
114 #include <GL/gl.h>
115 #endif
117 #ifndef GL_TEXTURE_RECTANGLE_ARB
118 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
119 #endif
121 #ifndef GL_BGRA
122 #define GL_BGRA 0x80E1
123 #endif
125 #ifndef GL_UNSIGNED_INT_8_8_8_8
126 #define GL_UNSIGNED_INT_8_8_8_8 0x8035
127 #endif
129 #ifndef GL_UNSIGNED_INT_8_8_8_8_REV
130 #define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
131 #endif
133 #include <caml/fail.h>
134 #include <caml/alloc.h>
135 #include <caml/memory.h>
136 #include <caml/unixsupport.h>
138 #include <fitz.h>
139 #include <mupdf.h>
141 #include FT_FREETYPE_H
143 #if 0
144 #define lprintf printf
145 #else
146 #define lprintf(...)
147 #endif
149 #define ARSERT(cond) for (;;) { \
150 if (!(cond)) { \
151 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
153 break; \
156 struct slice {
157 int h;
158 int texindex;
161 struct tile {
162 int x, y, w, h;
163 int slicecount;
164 int sliceheight;
165 fz_pixmap *pixmap;
166 struct slice slices[1];
169 struct pagedim {
170 int pageno;
171 int rotate;
172 int left;
173 int tctmready;
174 fz_bbox bounds;
175 fz_rect pagebox;
176 fz_rect mediabox;
177 fz_matrix ctm, zoomctm, lctm, tctm;
180 struct page {
181 int gen;
182 int pageno;
183 int pdimno;
184 fz_text_span *text;
185 pdf_page *drawpage;
186 fz_display_list *dlist;
187 struct mark {
188 int i;
189 fz_text_span *span;
190 } fmark, lmark;
193 #if !defined _WIN32 && !defined __APPLE__
194 #define USE_XSEL
195 #endif
197 struct {
198 int sock;
199 int sliceheight;
200 struct pagedim *pagedims;
201 int pagecount;
202 int pagedimcount;
203 pdf_document *doc;
204 fz_context *ctx;
205 fz_glyph_cache *cache;
206 int w, h;
208 int texindex;
209 int texcount;
210 GLuint *texids;
212 GLenum texiform;
213 GLenum texform;
214 GLenum texty;
216 fz_colorspace *colorspace;
218 struct {
219 int w, h;
220 struct slice *slice;
221 } *texowners;
223 int rotate;
224 int proportional;
225 int trimmargins;
226 int needoutline;
227 int gen;
228 int aalevel;
230 int trimanew;
231 fz_bbox trimfuzz;
232 fz_pixmap *pig;
234 #ifdef _WIN32
235 HANDLE thread;
236 #else
237 pthread_t thread;
238 #endif
239 FILE *xselpipe;
241 FT_Face face;
242 } state;
244 static void UNUSED debug_rect (const char *cap, fz_rect r)
246 printf ("%s(rect) %.2f,%.2f,%.2f,%.2f\n", cap, r.x0, r.y0, r.x1, r.y1);
249 static void UNUSED debug_bbox (const char *cap, fz_bbox r)
251 printf ("%s(bbox) %d,%d,%d,%d\n", cap, r.x0, r.y0, r.x1, r.y1);
254 static void UNUSED debug_matrix (const char *cap, fz_matrix m)
256 printf ("%s(matrix) %.2f,%.2f,%.2f,%.2f %.2f %.2f\n", cap,
257 m.a, m.b, m.c, m.d, m.e, m.f);
260 #ifdef _WIN32
261 static CRITICAL_SECTION critsec;
263 static void lock (void *unused)
265 (void) unused;
266 EnterCriticalSection (&critsec);
269 static void unlock (void *unused)
271 (void) unused;
272 LeaveCriticalSection (&critsec);
275 static int trylock (void *unused)
277 return TryEnterCriticalSection (&critsec) == 0;
279 #else
280 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
282 static void lock (const char *cap)
284 int ret = pthread_mutex_lock (&mutex);
285 if (ret) {
286 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
290 static void unlock (const char *cap)
292 int ret = pthread_mutex_unlock (&mutex);
293 if (ret) {
294 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
298 static int trylock (const char *cap)
300 int ret = pthread_mutex_trylock (&mutex);
302 if (ret && ret != EBUSY) {
303 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
305 return ret == EBUSY;
307 #endif
309 static void *parse_pointer (const char *cap, const char *s)
311 int ret;
312 void *ptr;
314 ret = sscanf (s, "%" FMT_ptr, FMT_ptr_cast (&ptr));
315 if (ret != 1) {
316 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
318 return ptr;
321 static int hasdata (int sock)
323 int ret;
324 fionread_arg avail;
325 ret = ioctlsocket (sock, FIONREAD, &avail);
326 if (ret) sockerr (1, "hasdata: FIONREAD error ret=%d", ret);
327 return avail > 0;
330 static double now (void)
332 #ifdef _WIN32
333 FILETIME ft;
334 uint64 tmp;
336 GetSystemTimeAsFileTime (&ft);
337 tmp = ft.dwHighDateTime;
338 tmp <<= 32;
339 tmp |= ft.dwLowDateTime;
340 return tmp * 1e-7;
341 #else
342 struct timeval tv;
344 if (gettimeofday (&tv, NULL)) {
345 err (1, "gettimeofday");
347 return tv.tv_sec + tv.tv_usec*1e-6;
348 #endif
351 static void readdata (int fd, char *p, int size)
353 ssize_t n;
355 n = recv (fd, p, size, 0);
356 if (n - size) {
357 if (!n) errx (1, "EOF while reading");
358 sockerr (1, "recv (req %d, ret %" FMT_ss ")", size, n);
362 static void writedata (int fd, char *p, int size)
364 char buf[4];
365 ssize_t n;
367 buf[0] = (size >> 24) & 0xff;
368 buf[1] = (size >> 16) & 0xff;
369 buf[2] = (size >> 8) & 0xff;
370 buf[3] = (size >> 0) & 0xff;
372 n = send (fd, buf, 4, 0);
373 if (n != 4) {
374 if (!n) errx (1, "EOF while writing length");
375 sockerr (1, "send %" FMT_ss, n);
378 n = send (fd, p, size, 0);
379 if (n - size) {
380 if (!n) errx (1, "EOF while writing data");
381 sockerr (1, "send (req %d, ret %" FMT_ss ")", size, n);
385 static void
386 #ifdef __GNUC__
387 __attribute__ ((format (printf, 2, 3)))
388 #endif
389 printd (int fd, const char *fmt, ...)
391 int size = 200, len;
392 va_list ap;
393 char *buf;
395 buf = malloc (size);
396 for (;;) {
397 if (!buf) err (errno, "malloc for temp buf (%d bytes) failed", size);
399 va_start (ap, fmt);
400 len = vsnprintf (buf, size, fmt, ap);
401 va_end (ap);
403 if (len > -1 && len < size) {
404 writedata (fd, buf, len);
405 break;
408 if (len > -1) {
409 size = len + 1;
411 else {
412 size *= 2;
414 buf = realloc (buf, size);
416 free (buf);
419 static void openxref (char *filename, char *password)
421 int i;
423 for (i = 0; i < state.texcount; ++i) {
424 state.texowners[i].w = -1;
425 state.texowners[i].slice = NULL;
428 if (state.doc) {
429 pdf_close_document (state.doc);
430 state.doc = NULL;
433 if (state.pagedims) {
434 free (state.pagedims);
435 state.pagedims = NULL;
437 state.pagedimcount = 0;
439 fz_set_aa_level (state.ctx, state.aalevel);
440 state.doc = pdf_open_document (state.ctx, filename);
441 if (pdf_needs_password (state.doc)) {
442 int okay = pdf_authenticate_password (state.doc, password);
443 if (!okay) {
444 errx (1, "invalid password");
447 state.pagecount = pdf_count_pages (state.doc);
450 static void pdfinfo (void)
452 fz_obj *infoobj;
454 printd (state.sock, "info PDF version\t%d.%d",
455 state.doc->version / 10, state.doc->version % 10);
457 infoobj = fz_dict_gets (state.doc->trailer, "Info");
458 if (infoobj) {
459 int i;
460 char *s;
461 char *items[] = { "Title", "Author", "Creator",
462 "Producer", "CreationDate" };
464 for (i = 0; i < sizeof (items) / sizeof (*items); ++i) {
465 fz_obj *obj = fz_dict_gets (infoobj, items[i]);
466 s = pdf_to_utf8 (state.ctx, obj);
467 if (*s) {
468 if (i == 0) {
469 printd (state.sock, "title %s", s);
471 printd (state.sock, "info %s\t%s", items[i], s);
473 fz_free (state.ctx, s);
476 printd (state.sock, "infoend");
479 static int readlen (int fd)
481 ssize_t n;
482 unsigned char p[4];
484 n = recv (fd, p, 4, 0);
485 if (n != 4) {
486 if (!n) errx (1, "EOF while reading length");
487 sockerr (1, "recv %" FMT_ss, n);
490 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
493 static void unlinktile (struct tile *tile)
495 int i;
497 for (i = 0; i < tile->slicecount; ++i) {
498 struct slice *s = &tile->slices[i];
500 if (s->texindex != -1) {
501 if (state.texowners[s->texindex].slice == s) {
502 state.texowners[s->texindex].slice = NULL;
508 static void freepage (struct page *page)
510 if (page->text) {
511 fz_free_text_span (state.ctx, page->text);
513 pdf_free_page (state.ctx, page->drawpage);
514 fz_free_display_list (state.ctx, page->dlist);
515 free (page);
518 static void freetile (struct tile *tile)
520 unlinktile (tile);
521 #ifndef PIGGYBACK
522 fz_drop_pixmap (state.ctx, tile->pixmap);
523 #else
524 if (state.pig) {
525 fz_drop_pixmap (state.ctx, state.pig);
527 state.pig = tile->pixmap;
528 #endif
529 free (tile);
532 #ifdef __ALTIVEC__
533 #include <altivec.h>
535 static int cacheline32bytes;
536 extern char **environ;
538 static void __attribute__ ((constructor)) clcheck (void)
540 char **envp = environ;
541 unsigned long *auxv;
543 while (*envp++);
545 for (auxv = (unsigned long *) envp; *auxv != 0; auxv += 2) {
546 if (*auxv == 19) {
547 cacheline32bytes = auxv[1] == 32;
548 return;
553 static void OPTIMIZE (3) clearpixmap (fz_pixmap *pixmap)
555 if (cacheline32bytes) {
556 intptr_t a1, a2, diff;
557 size_t sizea, i, size = pixmap->w * pixmap->h * pixmap->n;
558 vector unsigned char v = vec_splat_u8 (-1);
559 vector unsigned char *p;
561 a1 = a2 = (intptr_t) pixmap->samples;
562 a2 = (a1 + 31) & ~31;
563 diff = a2 - a1;
564 sizea = size - diff;
565 p = (void *) a2;
567 while (a1 != a2) *(char *) a1++ = 0xff;
568 for (i = 0; i < (sizea & ~31); i += 32) {
569 __asm volatile ("dcbz %0, %1"::"b"(a2),"r"(i));
570 vec_st (v, i, p);
571 vec_st (v, i + 16, p);
573 while (i < sizea) *((char *) a1 + i++) = 0xff;
575 else fz_clear_pixmap_with_color (pixmap, 0xff);
577 #else
578 #define clearpixmap(p) fz_clear_pixmap_with_color (p, 0xff)
579 #endif
581 static fz_matrix trimctm (pdf_page *page, int pindex)
583 fz_matrix ctm;
584 struct pagedim *pdim = &state.pagedims[pindex];
586 if (!pdim->tctmready) {
587 if (state.trimmargins) {
588 fz_rect realbox;
590 ctm = fz_concat (fz_rotate (-pdim->rotate), fz_scale (1, -1));
591 realbox = fz_transform_rect (ctm, pdim->mediabox);
592 ctm = fz_concat (ctm, fz_translate (-realbox.x0, -realbox.y0));
593 ctm = fz_concat (fz_invert_matrix (page->ctm), ctm);
595 else {
596 ctm = fz_identity;
598 pdim->tctm = ctm;
599 pdim->tctmready = 1;
601 return pdim->tctm;
604 static fz_matrix pagectm (struct page *page)
606 return fz_concat (trimctm (page->drawpage, page->pdimno),
607 state.pagedims[page->pdimno].ctm);
610 static void *loadpage (int pageno, int pindex)
612 fz_device *dev;
613 struct page *page = NULL;
615 page = calloc (sizeof (struct page), 1);
616 if (!page) {
617 err (1, "calloc page %d", pageno);
620 page->drawpage = pdf_load_page (state.doc, pageno);
621 page->dlist = fz_new_display_list (state.ctx);
622 dev = fz_new_list_device (state.ctx, page->dlist);
623 pdf_run_page (state.doc, page->drawpage, dev, fz_identity, NULL);
624 fz_free_device (dev);
626 page->pdimno = pindex;
627 page->pageno = pageno;
628 page->gen = state.gen;
630 return page;
633 static struct tile *alloctile (int h)
635 int i;
636 int slicecount;
637 size_t tilesize;
638 struct tile *tile;
640 slicecount = (h + state.sliceheight - 1) / state.sliceheight;
641 tilesize = sizeof (*tile) + ((slicecount - 1) * sizeof (struct slice));
642 tile = calloc (tilesize, 1);
643 if (!tile) {
644 err (1, "can not allocate tile (%" FMT_s " bytes)", tilesize);
646 for (i = 0; i < slicecount; ++i) {
647 int sh = MIN (h, state.sliceheight);
648 tile->slices[i].h = sh;
649 tile->slices[i].texindex = -1;
650 h -= sh;
652 tile->slicecount = slicecount;
653 tile->sliceheight = state.sliceheight;
654 return tile;
657 static struct tile *rendertile (struct page *page, int x, int y, int w, int h)
659 fz_bbox bbox;
660 fz_device *dev;
661 struct tile *tile;
662 struct pagedim *pdim;
664 tile = alloctile (h);
665 pdim = &state.pagedims[page->pdimno];
667 bbox = pdim->bounds;
668 bbox.x0 += x;
669 bbox.y0 += y;
670 bbox.x1 = bbox.x0 + w;
671 bbox.y1 = bbox.y0 + h;
673 if (state.pig) {
674 if (state.pig->w == w
675 && state.pig->h == h
676 && state.pig->colorspace == state.colorspace) {
677 tile->pixmap = state.pig;
678 tile->pixmap->x = bbox.x0;
679 tile->pixmap->y = bbox.y0;
681 else {
682 fz_drop_pixmap (state.ctx, state.pig);
684 state.pig = NULL;
686 if (!tile->pixmap) {
687 tile->pixmap =
688 fz_new_pixmap_with_rect (state.ctx, state.colorspace, bbox);
691 tile->w = w;
692 tile->h = h;
693 clearpixmap (tile->pixmap);
694 dev = fz_new_draw_device (state.ctx, tile->pixmap);
695 fz_execute_display_list (page->dlist, dev, pagectm (page), bbox, NULL);
696 fz_free_device (dev);
698 return tile;
701 static void initpdims (void)
703 int pageno;
704 double start, end;
706 start = now ();
707 for (pageno = 0; pageno < state.pagecount; ++pageno) {
708 int rotate;
709 fz_obj *pageobj;
710 struct pagedim *p;
711 fz_rect mediabox, cropbox;
713 pageobj = state.doc->page_objs[pageno];
715 if (state.trimmargins) {
716 fz_obj *obj;
717 pdf_page *page;
719 page = pdf_load_page (state.doc, pageno);
720 obj = fz_dict_gets (pageobj, "llpp.TrimBox");
721 if (state.trimanew || !obj) {
722 fz_rect rect;
723 fz_bbox bbox;
724 fz_matrix ctm;
725 fz_device *dev;
727 dev = fz_new_bbox_device (state.ctx, &bbox);
728 dev->hints |= FZ_IGNORE_SHADE;
729 ctm = fz_invert_matrix (page->ctm);
730 pdf_run_page (state.doc, page, dev, fz_identity, NULL);
731 fz_free_device (dev);
733 rect.x0 = bbox.x0 + state.trimfuzz.x0;
734 rect.x1 = bbox.x1 + state.trimfuzz.x1;
735 rect.y0 = bbox.y0 + state.trimfuzz.y0;
736 rect.y1 = bbox.y1 + state.trimfuzz.y1;
737 rect = fz_transform_rect (ctm, rect);
738 rect = fz_intersect_rect (rect, page->mediabox);
740 if (fz_is_empty_rect (rect)) {
741 mediabox = page->mediabox;
743 else {
744 mediabox = rect;
747 obj = fz_new_array (state.ctx, 4);
748 fz_array_push (obj, fz_new_real (state.ctx, mediabox.x0));
749 fz_array_push (obj, fz_new_real (state.ctx, mediabox.y0));
750 fz_array_push (obj, fz_new_real (state.ctx, mediabox.x1));
751 fz_array_push (obj, fz_new_real (state.ctx, mediabox.y1));
752 fz_dict_puts (pageobj, "llpp.TrimBox", obj);
754 else {
755 mediabox.x0 = fz_to_real (fz_array_get (obj, 0));
756 mediabox.y0 = fz_to_real (fz_array_get (obj, 1));
757 mediabox.x1 = fz_to_real (fz_array_get (obj, 2));
758 mediabox.y1 = fz_to_real (fz_array_get (obj, 3));
761 rotate = page->rotate;
762 pdf_free_page (state.ctx, page);
764 printd (state.sock, "progress %f Trimming %d",
765 (double) (pageno + 1) / state.pagecount,
766 pageno + 1);
768 else {
769 mediabox = pdf_to_rect (state.ctx, fz_dict_gets (pageobj, "MediaBox"));
770 if (fz_is_empty_rect (mediabox)) {
771 fprintf (stderr, "cannot find page size for page %d\n", pageno+1);
772 mediabox.x0 = 0;
773 mediabox.y0 = 0;
774 mediabox.x1 = 612;
775 mediabox.y1 = 792;
778 cropbox = pdf_to_rect (state.ctx, fz_dict_gets (pageobj, "CropBox"));
779 if (!fz_is_empty_rect (cropbox)) {
780 mediabox = fz_intersect_rect (mediabox, cropbox);
782 rotate = fz_to_int (fz_dict_gets (pageobj, "Rotate"));
785 if (state.pagedimcount == 0
786 || (p = &state.pagedims[state.pagedimcount-1], p->rotate != rotate)
787 || memcmp (&p->mediabox, &mediabox, sizeof (mediabox))) {
788 size_t size;
790 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
791 state.pagedims = realloc (state.pagedims, size);
792 if (!state.pagedims) {
793 err (1, "realloc pagedims to %" FMT_s " (%d elems)",
794 size, state.pagedimcount + 1);
797 p = &state.pagedims[state.pagedimcount++];
798 p->rotate = rotate;
799 p->mediabox = mediabox;
800 p->pageno = pageno;
803 end = now ();
804 if (state.trimmargins) {
805 printd (state.sock, "progress 1 Trimmed %d pages in %f seconds",
806 state.pagecount, end - start);
808 else {
809 printd (state.sock, "vmsg Processed %d pages in %f seconds",
810 state.pagecount, end - start);
812 state.trimanew = 0;
815 static void layout (void)
817 int pindex;
818 fz_rect box;
819 fz_matrix ctm;
820 double zoom, w, maxw = 0;
821 struct pagedim *p = state.pagedims;
823 if (state.proportional) {
824 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
825 box = fz_transform_rect (fz_rotate (p->rotate + state.rotate),
826 p->mediabox);
827 w = box.x1 - box.x0;
828 maxw = MAX (w, maxw);
832 p = state.pagedims;
833 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
834 fz_bbox bbox;
836 ctm = fz_rotate (state.rotate);
837 box = fz_transform_rect (fz_rotate (p->rotate + state.rotate),
838 p->mediabox);
839 w = box.x1 - box.x0;
841 if (state.proportional) {
842 double scale = w / maxw;
843 zoom = (state.w / w) * scale;
845 else {
846 zoom = state.w / w;
849 p->zoomctm = fz_scale (zoom, zoom);
850 ctm = fz_concat (p->zoomctm, ctm);
852 p->pagebox = fz_transform_rect (fz_rotate (p->rotate), p->mediabox);
853 p->pagebox.x1 -= p->pagebox.x0;
854 p->pagebox.y1 -= p->pagebox.y0;
855 p->pagebox.x0 = 0;
856 p->pagebox.y0 = 0;
857 bbox = fz_round_rect (fz_transform_rect (ctm, p->pagebox));
859 p->bounds = bbox;
860 p->left = state.proportional ? ((maxw - w) * zoom) / 2.0 : 0;
861 p->ctm = ctm;
863 ctm = fz_identity;
864 ctm = fz_concat (ctm, fz_translate (0, -p->mediabox.y1));
865 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
866 ctm = fz_concat (ctm, fz_rotate (p->rotate + state.rotate));
867 p->lctm = ctm;
869 p->tctmready = 0;
872 while (p-- != state.pagedims) {
873 int w = p->bounds.x1 - p->bounds.x0;
874 int h = p->bounds.y1 - p->bounds.y0;
876 printd (state.sock, "pdim %d %d %d %d", p->pageno, w, h, p->left);
880 static void recurse_outline (fz_outline *outline, int level)
882 while (outline) {
883 fz_link_dest *dest;
884 int i, top = 0;
885 struct pagedim *pdim = state.pagedims;
887 dest = &outline->dest;
888 for (i = 0; i < state.pagedimcount; ++i) {
889 if (state.pagedims[i].pageno > dest->ld.gotor.page)
890 break;
891 pdim = &state.pagedims[i];
893 if (dest->ld.gotor.flags & fz_link_flag_t_valid) {
894 fz_point p;
895 p.x = 0;
896 p.y = dest->ld.gotor.lt.y;
897 p = fz_transform_point (pdim->lctm, p);
898 top = p.y;
900 if (dest->ld.gotor.page >= 0 && dest->ld.gotor.page < 1<<30) {
901 int h;
902 double y0, y1;
904 y0 = MIN (pdim->bounds.y0, pdim->bounds.y1);
905 y1 = MAX (pdim->bounds.y0, pdim->bounds.y1);
906 h = y1 - y0;
907 printd (state.sock, "o %d %d %d %d %s",
908 level, dest->ld.gotor.page, top, h, outline->title);
910 if (outline->down) {
911 recurse_outline (outline->down, level + 1);
913 outline = outline->next;
917 static void process_outline (void)
919 fz_outline *outline;
921 if (!state.needoutline) return;
923 state.needoutline = 0;
924 outline = pdf_load_outline (state.doc);
925 if (outline) {
926 recurse_outline (outline, 0);
927 fz_free_outline (outline);
931 static int comparespans (const void *l, const void *r)
933 fz_text_span const *const*ls = l;
934 fz_text_span const *const*rs = r;
935 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
938 /* wishful thinking function */
939 static void search (regex_t *re, int pageno, int y, int forward)
941 int i, j;
942 int ret;
943 char *p;
944 char buf[256];
945 fz_matrix ctm;
946 fz_device *tdev;
947 pdf_page *drawpage;
948 fz_text_span *text, *span, **pspan;
949 struct pagedim *pdim, *pdimprev;
950 int stop = 0;
951 int niters = 0;
952 int nspans;
953 double start, end;
955 start = now ();
956 while (pageno >= 0 && pageno < state.pagecount && !stop) {
957 if (niters++ == 5) {
958 niters = 0;
959 if (hasdata (state.sock)) {
960 printd (state.sock,
961 "progress 1 attention requested aborting search at %d",
962 pageno);
963 stop = 1;
965 else {
966 printd (state.sock, "progress %f searching in page %d",
967 (double) (pageno + 1) / state.pagecount,
968 pageno);
971 pdimprev = NULL;
972 for (i = 0; i < state.pagedimcount; ++i) {
973 pdim = &state.pagedims[i];
974 if (pdim->pageno == pageno) {
975 goto found;
977 if (pdim->pageno > pageno) {
978 pdim = pdimprev;
979 goto found;
981 pdimprev = pdim;
983 pdim = pdimprev;
984 found:
986 drawpage = pdf_load_page (state.doc, pageno);
988 text = fz_new_text_span (state.ctx);
989 tdev = fz_new_text_device (state.ctx, text);
990 pdf_run_page (state.doc, drawpage, tdev, fz_identity, NULL);
991 fz_free_device (tdev);
993 nspans = 0;
994 for (span = text; span; span = span->next) {
995 nspans++;
997 pspan = malloc (sizeof (void *) * nspans);
998 if (!pspan) {
999 err (1, "malloc span pointers %" FMT_s, sizeof (void *) * nspans);
1001 for (i = 0, span = text; span; span = span->next, ++i) {
1002 pspan[i] = span;
1004 qsort (pspan, nspans, sizeof (fz_text_span *), comparespans);
1006 j = forward ? 0 : nspans - 1;
1007 while (nspans--) {
1008 regmatch_t rm;
1010 span = pspan[j];
1011 j += forward ? 1 : -1;
1012 p = buf;
1013 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
1014 if (forward) {
1015 if (span->text[i].bbox.y0 < y + 1) {
1016 continue;
1019 else {
1020 if (span->text[i].bbox.y0 > y - 1) {
1021 continue;
1024 if (span->text[i].c < 256) {
1025 *p++ = span->text[i].c;
1027 else {
1028 *p++ = '?';
1031 if (p == buf) {
1032 continue;
1034 *p++ = 0;
1036 ret = regexec (re, buf, 1, &rm, 0);
1037 if (ret) {
1038 if (ret != REG_NOMATCH) {
1039 size_t size;
1040 char errbuf[80];
1041 size = regerror (ret, re, errbuf, sizeof (errbuf));
1042 printd (state.sock,
1043 "msg regexec error `%.*s'",
1044 (int) size, errbuf);
1045 fz_free_text_span (state.ctx, text);
1046 pdf_free_page (state.ctx, drawpage);
1047 free (pspan);
1048 return;
1051 else {
1052 fz_bbox *sb, *eb;
1053 fz_point p1, p2, p3, p4;
1055 sb = &span->text[rm.rm_so].bbox;
1056 eb = &span->text[rm.rm_eo - 1].bbox;
1058 p1.x = sb->x0;
1059 p1.y = sb->y0;
1060 p2.x = eb->x1;
1061 p2.y = sb->y0;
1062 p3.x = eb->x1;
1063 p3.y = eb->y1;
1064 p4.x = sb->x0;
1065 p4.y = eb->y1;
1067 trimctm (drawpage, pdim - state.pagedims);
1068 ctm = fz_concat (pdim->tctm, pdim->zoomctm);
1070 p1 = fz_transform_point (ctm, p1);
1071 p2 = fz_transform_point (ctm, p2);
1072 p3 = fz_transform_point (ctm, p3);
1073 p4 = fz_transform_point (ctm, p4);
1075 if (!stop) {
1076 printd (state.sock,
1077 "firstmatch %d %d %f %f %f %f %f %f %f %f",
1078 pageno, 1,
1079 p1.x, p1.y,
1080 p2.x, p2.y,
1081 p3.x, p3.y,
1082 p4.x, p4.y);
1084 printd (state.sock,
1085 "progress 1 found at %d `%.*s' in %f sec",
1086 pageno, (int) (rm.rm_eo - rm.rm_so), &buf[rm.rm_so],
1087 now () - start);
1089 else {
1090 printd (state.sock,
1091 "match %d %d %f %f %f %f %f %f %f %f",
1092 pageno, 2,
1093 p1.x, p1.y,
1094 p2.x, p2.y,
1095 p3.x, p3.y,
1096 p4.x, p4.y);
1098 stop = 1;
1101 if (forward) {
1102 pageno += 1;
1103 y = 0;
1105 else {
1106 pageno -= 1;
1107 y = INT_MAX;
1109 fz_free_text_span (state.ctx, text);
1110 pdf_free_page (state.ctx, drawpage);
1111 free (pspan);
1113 end = now ();
1114 if (!stop) {
1115 printd (state.sock, "progress 1 no matches %f sec", end - start);
1117 printd (state.sock, "clearrects");
1120 static void set_tex_params (int colorspace)
1122 switch (colorspace) {
1123 case 0:
1124 state.texiform = GL_RGBA8;
1125 state.texform = GL_RGBA;
1126 state.texty = GL_UNSIGNED_BYTE;
1127 state.colorspace = fz_device_rgb;
1128 break;
1129 case 1:
1130 state.texiform = GL_RGBA8;
1131 state.texform = GL_BGRA;
1132 state.texty = fz_is_big_endian ()
1133 ? GL_UNSIGNED_INT_8_8_8_8
1134 : GL_UNSIGNED_INT_8_8_8_8_REV;
1135 state.colorspace = fz_device_bgr;
1136 break;
1137 case 2:
1138 state.texiform = GL_LUMINANCE_ALPHA;
1139 state.texform = GL_LUMINANCE_ALPHA;
1140 state.texty = GL_UNSIGNED_BYTE;
1141 state.colorspace = fz_device_gray;
1142 break;
1143 default:
1144 errx (1, "invalid colorspce %d", colorspace);
1148 static void realloctexts (int texcount)
1150 size_t size;
1152 if (texcount == state.texcount) return;
1154 if (texcount < state.texcount) {
1155 glDeleteTextures (state.texcount - texcount,
1156 state.texids + texcount);
1159 size = texcount * sizeof (*state.texids);
1160 state.texids = realloc (state.texids, size);
1161 if (!state.texids) {
1162 err (1, "realloc texids %" FMT_s, size);
1165 size = texcount * sizeof (*state.texowners);
1166 state.texowners = realloc (state.texowners, size);
1167 if (!state.texowners) {
1168 err (1, "realloc texowners %" FMT_s, size);
1170 if (texcount > state.texcount) {
1171 int i;
1173 glGenTextures (texcount - state.texcount,
1174 state.texids + state.texcount);
1175 for (i = state.texcount; i < texcount; ++i) {
1176 state.texowners[i].w = -1;
1177 state.texowners[i].slice = NULL;
1180 state.texcount = texcount;
1181 state.texindex = 0;
1184 static
1185 #ifdef _WIN32
1186 DWORD _stdcall
1187 #else
1188 void *
1189 #endif
1190 mainloop (void *unused)
1192 char *p = NULL;
1193 int len, ret, oldlen = 0;
1195 for (;;) {
1196 len = readlen (state.sock);
1197 if (len == 0) {
1198 errx (1, "readlen returned 0");
1201 if (oldlen < len + 1) {
1202 p = realloc (p, len + 1);
1203 if (!p) {
1204 err (1, "realloc %d failed", len + 1);
1206 oldlen = len + 1;
1208 readdata (state.sock, p, len);
1209 p[len] = 0;
1211 if (!strncmp ("open", p, 4)) {
1212 size_t filenamelen;
1213 char *password;
1214 char *filename = p + 5;
1216 filenamelen = strlen (filename);
1217 password = filename + filenamelen + 1;
1219 openxref (filename, password);
1220 printd (state.sock, "msg Opened %s (press h/F1 to get help)",
1221 filename);
1222 pdfinfo ();
1223 initpdims ();
1224 state.needoutline = 1;
1226 else if (!strncmp ("cs", p, 2)) {
1227 int i, colorspace;
1229 ret = sscanf (p + 2, " %d", &colorspace);
1230 if (ret != 1) {
1231 errx (1, "malformed cs `%.*s' ret=%d", len, p, ret);
1233 lock ("cs");
1234 set_tex_params (colorspace);
1235 for (i = 0; i < state.texcount; ++i) {
1236 state.texowners[i].w = -1;
1237 state.texowners[i].slice = NULL;
1239 unlock ("cs");
1241 else if (!strncmp ("freepage", p, 8)) {
1242 void *ptr;
1244 ret = sscanf (p + 8, " %" FMT_ptr, FMT_ptr_cast (&ptr));
1245 if (ret != 1) {
1246 errx (1, "malformed freepage `%.*s' ret=%d", len, p, ret);
1248 freepage (ptr);
1250 else if (!strncmp ("freetile", p, 8)) {
1251 void *ptr;
1253 ret = sscanf (p + 8, " %" FMT_ptr, FMT_ptr_cast (&ptr));
1254 if (ret != 1) {
1255 errx (1, "malformed freetile `%.*s' ret=%d", len, p, ret);
1257 freetile (ptr);
1259 else if (!strncmp ("search", p, 6)) {
1260 int icase, pageno, y, ret, len2, forward;
1261 char *pattern;
1262 regex_t re;
1264 ret = sscanf (p + 6, " %d %d %d %d,%n",
1265 &icase, &pageno, &y, &forward, &len2);
1266 if (ret != 4) {
1267 errx (1, "malformed search `%s' ret=%d", p, ret);
1270 pattern = p + 6 + len2;
1271 ret = regcomp (&re, pattern,
1272 REG_EXTENDED | (icase ? REG_ICASE : 0));
1273 if (ret) {
1274 char errbuf[80];
1275 size_t size;
1277 size = regerror (ret, &re, errbuf, sizeof (errbuf));
1278 printd (state.sock, "msg regcomp failed `%.*s'",
1279 (int) size, errbuf);
1281 else {
1282 search (&re, pageno, y, forward);
1283 regfree (&re);
1286 else if (!strncmp ("geometry", p, 8)) {
1287 int w, h;
1289 printd (state.sock, "clear");
1290 ret = sscanf (p + 8, " %d %d", &w, &h);
1291 if (ret != 2) {
1292 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
1295 lock ("geometry");
1296 state.h = h;
1297 if (w != state.w) {
1298 int i;
1299 state.w = w;
1300 for (i = 0; i < state.texcount; ++i) {
1301 state.texowners[i].slice = NULL;
1304 layout ();
1305 process_outline ();
1306 state.gen++;
1307 unlock ("geometry");
1308 printd (state.sock, "continue %d", state.pagecount);
1310 else if (!strncmp ("reqlayout", p, 9)) {
1311 int rotate, proportional;
1313 printd (state.sock, "clear");
1314 ret = sscanf (p + 9, " %d %d", &rotate, &proportional);
1315 if (ret != 2) {
1316 errx (1, "bad reqlayout line `%.*s' ret=%d", len, p, ret);
1318 lock ("reqlayout");
1319 state.rotate = rotate;
1320 state.proportional = proportional;
1321 layout ();
1322 unlock ("reqlayout");
1323 printd (state.sock, "continue %d", state.pagecount);
1325 else if (!strncmp ("page", p, 4)) {
1326 double a, b;
1327 struct page *page;
1328 int pageno, pindex, ret;
1330 ret = sscanf (p + 4, " %d %d", &pageno, &pindex);
1331 if (ret != 2) {
1332 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
1335 lock ("page");
1336 a = now ();
1337 page = loadpage (pageno, pindex);
1338 b = now ();
1339 unlock ("page");
1341 printd (state.sock, "page %" FMT_ptr " %f",
1342 FMT_ptr_cast2 (page), b - a);
1344 else if (!strncmp ("tile", p, 4)) {
1345 int x, y, w, h, ret;
1346 struct page *page;
1347 struct tile *tile;
1348 double a, b;
1350 ret = sscanf (p + 4, " %" FMT_ptr " %d %d %d %d",
1351 FMT_ptr_cast (&page), &x, &y, &w, &h);
1352 if (ret != 5) {
1353 errx (1, "bad tile line `%.*s' ret=%d", len, p, ret);
1356 lock ("tile");
1357 a = now ();
1358 tile = rendertile (page, x, y, w, h);
1359 b = now ();
1360 unlock ("tile");
1362 printd (state.sock, "tile %d %d %" FMT_ptr " %u %f",
1363 x, y,
1364 FMT_ptr_cast2 (tile),
1365 tile->w * tile->h * tile->pixmap->n,
1366 b - a);
1368 else if (!strncmp ("settrim", p, 7)) {
1369 int trimmargins;
1370 fz_bbox fuzz;
1372 ret = sscanf (p + 7, " %d %d %d %d %d", &trimmargins,
1373 &fuzz.x0, &fuzz.y0, &fuzz.x1, &fuzz.y1);
1374 if (ret != 5) {
1375 errx (1, "malformed settrim `%.*s' ret=%d", len, p, ret);
1377 printd (state.sock, "clear");
1378 lock ("settrim");
1379 state.trimmargins = trimmargins;
1380 state.needoutline = 1;
1381 if (memcmp (&fuzz, &state.trimfuzz, sizeof (fuzz))) {
1382 state.trimanew = 1;
1383 state.trimfuzz = fuzz;
1385 state.pagedimcount = 0;
1386 free (state.pagedims);
1387 state.pagedims = NULL;
1388 initpdims ();
1389 layout ();
1390 process_outline ();
1391 unlock ("settrim");
1392 printd (state.sock, "continue %d", state.pagecount);
1394 else if (!strncmp ("sliceh", p, 6)) {
1395 int h;
1397 ret = sscanf (p + 6, " %d", &h);
1398 if (ret != 1) {
1399 errx (1, "malformed sliceh `%.*s' ret=%d", len, p, ret);
1401 if (h != state.sliceheight) {
1402 int i;
1404 state.sliceheight = h;
1405 for (i = 0; i < state.texcount; ++i) {
1406 state.texowners[i].w = -1;
1407 state.texowners[i].h = -1;
1408 state.texowners[i].slice = NULL;
1412 else if (!strncmp ("interrupt", p, 9)) {
1413 printd (state.sock, "vmsg interrupted");
1415 else if (!strncmp ("quit", p, 4)) {
1416 return 0;
1418 else {
1419 errx (1, "unknown command %.*s", len, p);
1422 return 0;
1425 CAMLprim value ml_realloctexts (value texcount_v)
1427 CAMLparam1 (texcount_v);
1428 int ok;
1430 if (trylock ("ml_realloctexts")) {
1431 ok = 0;
1432 goto done;
1434 realloctexts (Int_val (texcount_v));
1435 ok = 1;
1436 unlock ("ml_realloctexts");
1438 done:
1439 CAMLreturn (Val_bool (ok));
1442 static void showsel (struct page *page, int ox, int oy)
1444 fz_bbox bbox;
1445 fz_text_span *span;
1446 struct mark first, last;
1448 first = page->fmark;
1449 last = page->lmark;
1451 if (!first.span || !last.span) return;
1453 glEnable (GL_BLEND);
1454 glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA);
1455 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1457 ox -= state.pagedims[page->pdimno].bounds.x0;
1458 oy -= state.pagedims[page->pdimno].bounds.y0;
1459 for (span = first.span; span; span = span->next) {
1460 int i, j, k;
1462 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0;
1464 j = 0;
1465 k = span->len - 1;
1467 if (span == page->fmark.span && span == page->lmark.span) {
1468 j = MIN (first.i, last.i);
1469 k = MAX (first.i, last.i);
1471 else if (span == first.span) {
1472 j = first.i;
1474 else if (span == last.span) {
1475 k = last.i;
1478 for (i = j; i <= k; ++i) {
1479 bbox = fz_union_bbox (bbox, span->text[i].bbox);
1481 lprintf ("%d %d %d %d oy=%d ox=%d\n",
1482 bbox.x0,
1483 bbox.y0,
1484 bbox.x1,
1485 bbox.y1,
1486 oy, ox);
1488 glRecti (bbox.x0 + ox, bbox.y0 + oy, bbox.x1 + ox, bbox.y1 + oy);
1490 if (span == last.span) break;
1492 glDisable (GL_BLEND);
1495 static void highlightlinks (struct page *page, int xoff, int yoff)
1497 fz_link *link;
1498 fz_matrix ctm;
1500 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1501 glEnable (GL_LINE_STIPPLE);
1502 glLineStipple (0.5, 0xcccc);
1504 ctm = fz_concat (pagectm (page), fz_translate (xoff, yoff));
1506 glBegin (GL_QUADS);
1507 for (link = page->drawpage->links; link; link = link->next) {
1508 fz_point p1, p2, p3, p4;
1510 p1.x = link->rect.x0;
1511 p1.y = link->rect.y0;
1513 p2.x = link->rect.x1;
1514 p2.y = link->rect.y0;
1516 p3.x = link->rect.x1;
1517 p3.y = link->rect.y1;
1519 p4.x = link->rect.x0;
1520 p4.y = link->rect.y1;
1522 p1 = fz_transform_point (ctm, p1);
1523 p2 = fz_transform_point (ctm, p2);
1524 p3 = fz_transform_point (ctm, p3);
1525 p4 = fz_transform_point (ctm, p4);
1527 switch (link->dest.kind) {
1528 case FZ_LINK_GOTO: glColor3ub (255, 0, 0); break;
1529 case FZ_LINK_URI: glColor3ub (0, 0, 255); break;
1530 default: glColor3ub (0, 0, 0); break;
1533 glVertex2f (p1.x, p1.y);
1534 glVertex2f (p2.x, p2.y);
1535 glVertex2f (p3.x, p3.y);
1536 glVertex2f (p4.x, p4.y);
1538 glEnd ();
1540 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1541 glDisable (GL_LINE_STIPPLE);
1544 static void uploadslice (struct tile *tile, struct slice *slice)
1546 int offset;
1547 struct slice *slice1;
1549 offset = 0;
1550 for (slice1 = tile->slices; slice != slice1; slice1++) {
1551 offset += slice1->h * tile->w * tile->pixmap->n;
1553 if (slice->texindex != -1 && slice->texindex < state.texcount
1554 && state.texowners[slice->texindex].slice == slice) {
1555 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1557 else {
1558 int subimage = 0;
1559 int texindex = state.texindex++ % state.texcount;
1561 if (state.texowners[texindex].w == tile->w) {
1562 if (state.texowners[texindex].h >= slice->h) {
1563 subimage = 1;
1565 else {
1566 state.texowners[texindex].h = slice->h;
1569 else {
1570 state.texowners[texindex].h = slice->h;
1573 state.texowners[texindex].w = tile->w;
1574 state.texowners[texindex].slice = slice;
1575 slice->texindex = texindex;
1577 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[texindex]);
1578 if (subimage) {
1579 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1583 tile->w,
1584 slice->h,
1585 state.texform,
1586 state.texty,
1587 tile->pixmap->samples+offset
1590 else {
1591 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1593 state.texiform,
1594 tile->w,
1595 slice->h,
1597 state.texform,
1598 state.texty,
1599 tile->pixmap->samples+offset
1605 CAMLprim value ml_drawtile (value args_v, value ptr_v)
1607 CAMLparam2 (args_v, ptr_v);
1608 int dispx = Int_val (Field (args_v, 0));
1609 int dispy = Int_val (Field (args_v, 1));
1610 int dispw = Int_val (Field (args_v, 2));
1611 int disph = Int_val (Field (args_v, 3));
1612 int tilex = Int_val (Field (args_v, 4));
1613 int tiley = Int_val (Field (args_v, 5));
1614 char *s = String_val (ptr_v);
1615 struct tile *tile = parse_pointer ("ml_drawtile", s);
1617 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1619 int slicey, firstslice;
1620 struct slice *slice;
1622 firstslice = tiley / tile->sliceheight;
1623 slice = &tile->slices[firstslice];
1624 slicey = tiley % tile->sliceheight;
1626 while (disph > 0) {
1627 int dh;
1629 dh = slice->h - slicey;
1630 dh = MIN (disph, dh);
1631 uploadslice (tile, slice);
1633 glBegin (GL_QUADS);
1635 glTexCoord2i (tilex, slicey);
1636 glVertex2i (dispx, dispy);
1638 glTexCoord2i (tilex+dispw, slicey);
1639 glVertex2i (dispx+dispw, dispy);
1641 glTexCoord2i (tilex+dispw, slicey+dh);
1642 glVertex2i (dispx+dispw, dispy+dh);
1644 glTexCoord2i (tilex, slicey+dh);
1645 glVertex2i (dispx, dispy+dh);
1647 glEnd ();
1649 dispy += dh;
1650 disph -= dh;
1651 slice++;
1652 ARSERT (!(slice - tile->slices >= tile->slicecount && disph > 0));
1653 slicey = 0;
1656 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1657 CAMLreturn (Val_unit);
1660 CAMLprim value ml_postprocess (value ptr_v, value hlinks_v,
1661 value xoff_v, value yoff_v)
1663 CAMLparam4 (ptr_v, hlinks_v, xoff_v, yoff_v);
1664 int xoff = Int_val (xoff_v);
1665 int yoff = Int_val (yoff_v);
1666 char *s = String_val (ptr_v);
1667 struct page *page = parse_pointer ("ml_postprocess", s);
1669 if (Bool_val (hlinks_v)) highlightlinks (page, xoff, yoff);
1671 if (trylock ("ml_postprocess")) {
1672 goto done;
1674 showsel (page, xoff, yoff);
1675 unlock ("ml_postprocess");
1677 done:
1678 CAMLreturn (Val_unit);
1681 static fz_link *getlink (struct page *page, int x, int y)
1683 fz_point p;
1684 fz_matrix ctm;
1685 fz_link *link;
1687 p.x = x;
1688 p.y = y;
1690 ctm = fz_concat (trimctm (page->drawpage, page->pdimno),
1691 state.pagedims[page->pdimno].ctm);
1692 ctm = fz_invert_matrix (ctm);
1693 p = fz_transform_point (ctm, p);
1695 for (link = page->drawpage->links; link; link = link->next) {
1696 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1697 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1698 return link;
1702 return NULL;
1705 static void droptext (struct page *page)
1707 if (page->text) {
1708 fz_free_text_span (state.ctx, page->text);
1709 page->fmark.i = -1;
1710 page->lmark.i = -1;
1711 page->fmark.span = NULL;
1712 page->lmark.span = NULL;
1713 page->text = NULL;
1717 static void ensuretext (struct page *page)
1719 if (state.gen != page->gen) {
1720 droptext (page);
1721 page->gen = state.gen;
1723 if (!page->text) {
1724 fz_device *tdev;
1726 page->text = fz_new_text_span (state.ctx);
1727 tdev = fz_new_text_device (state.ctx, page->text);
1728 fz_execute_display_list (page->dlist,
1729 tdev,
1730 pagectm (page),
1731 fz_infinite_bbox, NULL);
1732 fz_free_device (tdev);
1736 CAMLprim value ml_whatsunder (value ptr_v, value x_v, value y_v)
1738 CAMLparam3 (ptr_v, x_v, y_v);
1739 CAMLlocal3 (ret_v, tup_v, str_v);
1740 fz_link *link;
1741 struct page *page;
1742 char *s = String_val (ptr_v);
1743 int x = Int_val (x_v), y = Int_val (y_v);
1744 struct pagedim *pdim;
1746 ret_v = Val_int (0);
1747 if (trylock ("ml_whatsunder")) {
1748 goto done;
1751 page = parse_pointer ("ml_whatsunder", s);
1752 pdim = &state.pagedims[page->pdimno];
1753 x += pdim->bounds.x0;
1754 y += pdim->bounds.y0;
1755 link = getlink (page, x, y);
1756 if (link) {
1757 switch (link->dest.kind) {
1758 case FZ_LINK_GOTO:
1760 int pageno;
1761 fz_point p;
1763 pageno = link->dest.ld.gotor.page;
1764 p.x = 0;
1765 p.y = 0;
1767 if (link->dest.ld.gotor.flags & fz_link_flag_t_valid) {
1768 p.y = link->dest.ld.gotor.lt.y;
1769 p = fz_transform_point (pdim->lctm, p);
1771 tup_v = caml_alloc_tuple (2);
1772 ret_v = caml_alloc_small (1, 1);
1773 Field (tup_v, 0) = Val_int (pageno);
1774 Field (tup_v, 1) = Val_int (p.y);
1775 Field (ret_v, 0) = tup_v;
1777 break;
1779 case FZ_LINK_URI:
1780 str_v = caml_copy_string (link->dest.ld.uri.uri);
1781 ret_v = caml_alloc_small (1, 0);
1782 Field (ret_v, 0) = str_v;
1783 break;
1785 default:
1786 printd (state.sock, "msg unhandled link kind %d", link->dest.kind);
1787 break;
1790 else {
1791 int i;
1792 fz_text_span *span;
1794 ensuretext (page);
1795 for (span = page->text; span; span = span->next) {
1796 for (i = 0; i < span->len; ++i) {
1797 fz_bbox *b;
1798 b = &span->text[i].bbox;
1799 if ((x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1)) {
1800 const char *n2 =
1801 span->font && span->font->name
1802 ? span->font->name
1803 : "Span has no font name"
1805 FT_FaceRec *face = span->font->ft_face;
1806 if (face && face->family_name) {
1807 char *s;
1808 char *n1 = face->family_name;
1809 size_t l1 = strlen (n1);
1810 size_t l2 = strlen (n2);
1812 if (l1 != l2 || memcmp (n1, n2, l1)) {
1813 s = malloc (l1 + l2 + 2);
1814 if (s) {
1815 memcpy (s, n2, l2);
1816 s[l2] = '=';
1817 memcpy (s + l2 + 1, n1, l1 + 1);
1818 str_v = caml_copy_string (s);
1819 free (s);
1823 if (str_v == 0) {
1824 str_v = caml_copy_string (n2);
1826 ret_v = caml_alloc_small (1, 2);
1827 Field (ret_v, 0) = str_v;
1828 goto unlock;
1833 unlock:
1834 unlock ("ml_whatsunder");
1836 done:
1837 CAMLreturn (ret_v);
1840 CAMLprim value ml_seltext (value ptr_v, value rect_v)
1842 CAMLparam2 (ptr_v, rect_v);
1843 fz_bbox *b;
1844 struct page *page;
1845 fz_text_span *span;
1846 struct mark first, last;
1847 int i, x0, x1, y0, y1;
1848 struct pagedim *pdim;
1849 char *s = String_val (ptr_v);
1851 if (trylock ("ml_seltext")) {
1852 goto done;
1855 page = parse_pointer ("ml_seltext", s);
1856 ensuretext (page);
1858 pdim = &state.pagedims[page->pdimno];
1860 x0 = Int_val (Field (rect_v, 0)) + pdim->bounds.x0;
1861 y0 = Int_val (Field (rect_v, 1)) + pdim->bounds.y0;
1862 x1 = Int_val (Field (rect_v, 2)) + pdim->bounds.x0;
1863 y1 = Int_val (Field (rect_v, 3)) + pdim->bounds.y0;
1865 if (0) {
1866 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1867 glColor3ub (128, 128, 128);
1868 glRecti (x0, y0, x1, y1);
1869 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1872 first.span = NULL;
1873 last.span = NULL;
1875 last.i = first.i = 0;
1876 first.span = page->text;
1877 for (span = page->text; span; span = span->next) {
1878 for (i = 0; i < span->len; ++i) {
1879 b = &span->text[i].bbox;
1880 int selected = 0;
1882 if (x0 >= b->x0 && x0 <= b->x1 && y0 >= b->y0 && y0 <= b->y1) {
1883 first.i = i;
1884 first.span = span;
1885 selected = 1;
1887 if (x1 >= b->x0 && x1 <= b->x1 && y1 >= b->y0 && y1 <= b->y1) {
1888 last.i = i;
1889 last.span = span;
1890 selected = 1;
1892 if (0 && selected) {
1893 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1894 glColor3ub (128, 128, 128);
1895 glRecti (b->x0, b->y0, b->x1, b->y1);
1896 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1901 if (y1 < y0 || x1 < x0) {
1902 int swap = 0;
1904 if (first.span == last.span) {
1905 swap = 1;
1907 else {
1908 if (y1 < y0) {
1909 for (span = first.span; span && span != last.span;
1910 span = span->next) {
1911 if (span->eol) {
1912 swap = 1;
1913 break;
1919 if (swap) {
1920 i = first.i;
1921 span = first.span;
1922 first.i = last.i;
1923 first.span = last.span;
1924 last.i = i;
1925 last.span = span;
1929 page->fmark = first;
1930 page->lmark = last;
1932 unlock ("ml_seltext");
1934 done:
1935 CAMLreturn (Val_unit);
1938 static int pipespan (FILE *f, fz_text_span *span, int a, int b)
1940 char buf[4];
1941 int i, len, ret;
1943 for (i = a; i <= b; ++i) {
1944 len = runetochar (buf, &span->text[i].c);
1945 ret = fwrite (buf, len, 1, f);
1947 if (ret != 1) {
1948 printd (state.sock, "msg failed to write %d bytes ret=%d: %s",
1949 len, ret, strerror (errno));
1950 return -1;
1953 return 0;
1956 CAMLprim value ml_copysel (value ptr_v)
1958 CAMLparam1 (ptr_v);
1959 FILE *f;
1960 struct page *page;
1961 char *s = String_val (ptr_v);
1963 if (trylock ("ml_copysel")) {
1964 goto done;
1967 if (!*s) {
1968 close:
1969 #ifdef USE_XSEL
1970 if (state.xselpipe) {
1971 int ret = pclose (state.xselpipe);
1972 if (ret) {
1973 printd (state.sock, "msg failed to close xsel pipe `%s'",
1974 strerror (errno));
1976 state.xselpipe = NULL;
1978 #else
1979 printf ("========================================\n");
1980 #endif
1982 else {
1983 fz_text_span *span;
1985 page = parse_pointer ("ml_sopysel", s);
1987 if (!page->fmark.span || !page->lmark.span) {
1988 printd (state.sock, "msg nothing to copy");
1989 goto unlock;
1992 f = stdout;
1993 #ifdef USE_XSEL
1994 if (!state.xselpipe) {
1995 state.xselpipe = popen ("xsel -i", "w");
1996 if (!state.xselpipe) {
1997 printd (state.sock, "msg failed to open xsel pipe `%s'",
1998 strerror (errno));
2000 else {
2001 f = state.xselpipe;
2004 else {
2005 f = state.xselpipe;
2007 #endif
2009 for (span = page->fmark.span;
2010 span && span != page->lmark.span->next;
2011 span = span->next) {
2012 int a = span == page->fmark.span ? page->fmark.i : 0;
2013 int b = span == page->lmark.span ? page->lmark.i : span->len - 1;
2014 if (pipespan (f, span, a, b)) {
2015 goto close;
2017 if (span->eol) {
2018 if (putc ('\n', f) == EOF) {
2019 printd (state.sock,
2020 "msg failed break line on xsel pipe `%s'",
2021 strerror (errno));
2022 goto close;
2026 page->lmark.span = NULL;
2027 page->fmark.span = NULL;
2030 unlock:
2031 unlock ("ml_copysel");
2033 done:
2034 CAMLreturn (Val_unit);
2037 CAMLprim value ml_getpdimrect (value pagedimno_v)
2039 CAMLparam1 (pagedimno_v);
2040 CAMLlocal1 (ret_v);
2041 int pagedimno = Int_val (pagedimno_v);
2042 fz_rect box;
2044 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
2045 if (trylock ("ml_getpdimrect")) {
2046 box = fz_empty_rect;
2048 else {
2049 box = state.pagedims[pagedimno].mediabox;
2050 unlock ("ml_getpdimrect");
2053 Store_double_field (ret_v, 0, box.x0);
2054 Store_double_field (ret_v, 1, box.x1);
2055 Store_double_field (ret_v, 2, box.y0);
2056 Store_double_field (ret_v, 3, box.y1);
2058 CAMLreturn (ret_v);
2061 static double getmaxw (void)
2063 int i;
2064 struct pagedim *p;
2065 double maxw = 0.0;
2067 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
2068 double x0, x1, w;
2070 x0 = MIN (p->mediabox.x0, p->mediabox.x1);
2071 x1 = MAX (p->mediabox.x0, p->mediabox.x1);
2073 w = x1 - x0;
2074 maxw = MAX (w, maxw);
2076 return maxw;
2079 CAMLprim value ml_getmaxw (value unit_v)
2081 CAMLparam1 (unit_v);
2082 CAMLlocal1 (ret_v);
2083 double maxw = 0.0;
2085 if (trylock ("ml_getmaxw")) {
2086 goto done;
2088 maxw = getmaxw ();
2089 unlock ("ml_getmaxw");
2090 done:
2091 ret_v = caml_copy_double (maxw);
2092 CAMLreturn (ret_v);
2095 CAMLprim value ml_zoom_for_height (value winw_v, value winh_v, value dw_v)
2097 CAMLparam3 (winw_v, winh_v, dw_v);
2098 CAMLlocal1 (ret_v);
2099 int i;
2100 double zoom = 1.0;
2101 double maxw = 0.0, maxh = 0.0;
2102 struct pagedim *p;
2103 double winw = Int_val (winw_v);
2104 double winh = Int_val (winh_v);
2105 double dw = Int_val (dw_v);
2106 double pw = 1.0, ph = 1.0, num, den;
2108 if (trylock ("ml_zoom_for_height")) {
2109 goto done;
2112 if (state.proportional) {
2113 maxw = getmaxw ();
2116 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
2117 double x0, x1, y0, y1, w, h, scaledh, scale;
2119 x0 = MIN (p->mediabox.x0, p->mediabox.x1);
2120 x1 = MAX (p->mediabox.x0, p->mediabox.x1);
2121 y0 = MIN (p->mediabox.y0, p->mediabox.y1);
2122 y1 = MAX (p->mediabox.y0, p->mediabox.y1);
2124 w = x1 - x0;
2125 h = y1 - y0;
2127 if (state.proportional) {
2128 scale = w / maxw;
2129 scaledh = h * scale;
2131 else {
2132 scale = 1.0;
2133 scaledh = h;
2136 if (scaledh > maxh) {
2137 maxh = scaledh;
2138 ph = scaledh;
2139 pw = w * scale;
2143 num = (winh * pw) + (ph * dw);
2144 den = ph * winw;
2145 zoom = num / den;
2147 unlock ("ml_zoom_for_height");
2148 done:
2149 ret_v = caml_copy_double (zoom);
2150 CAMLreturn (ret_v);
2153 #include "glfont.c"
2155 CAMLprim value ml_draw_string (value pt_v, value x_v, value y_v, value string_v)
2157 CAMLparam4 (pt_v, x_v, y_v, string_v);
2158 CAMLlocal1 (ret_v);
2159 int pt = Int_val(pt_v);
2160 int x = Int_val (x_v);
2161 int y = Int_val (y_v);
2162 double w;
2164 w = draw_string (state.face, pt, x, y, String_val (string_v));
2165 ret_v = caml_copy_double (w);
2166 CAMLreturn (ret_v);
2169 CAMLprim value ml_measure_string (value pt_v, value string_v)
2171 CAMLparam2 (pt_v, string_v);
2172 CAMLlocal1 (ret_v);
2173 int pt = Int_val (pt_v);
2174 double w;
2176 w = measure_string (state.face, pt, String_val (string_v));
2177 ret_v = caml_copy_double (w);
2178 CAMLreturn (ret_v);
2181 CAMLprim value ml_getpagebox (value opaque_v)
2183 CAMLparam1 (opaque_v);
2184 CAMLlocal1 (ret_v);
2185 fz_bbox bbox;
2186 fz_device *dev;
2187 char *s = String_val (opaque_v);
2188 struct page *page = parse_pointer ("ml_getpagebox", s);
2190 ret_v = caml_alloc_tuple (4);
2191 dev = fz_new_bbox_device (state.ctx, &bbox);
2192 dev->hints |= FZ_IGNORE_SHADE;
2193 pdf_run_page (state.doc, page->drawpage, dev, pagectm (page), NULL);
2194 fz_free_device (dev);
2196 Field (ret_v, 0) = Val_int (bbox.x0);
2197 Field (ret_v, 1) = Val_int (bbox.y0);
2198 Field (ret_v, 2) = Val_int (bbox.x1);
2199 Field (ret_v, 3) = Val_int (bbox.y1);
2201 CAMLreturn (ret_v);
2204 CAMLprim value ml_setaalevel (value level_v)
2206 CAMLparam1 (level_v);
2208 state.aalevel = Int_val (level_v);
2209 CAMLreturn (Val_unit);
2212 #if !defined _WIN32 && !defined __APPLE__
2213 #undef pixel
2214 #include <X11/X.h>
2215 #include <X11/Xlib.h>
2216 #include <X11/Xutil.h>
2217 #include <GL/glx.h>
2219 static void set_wm_class (int hack)
2221 if (hack) {
2222 Display *dpy;
2223 Window win;
2224 XClassHint hint;
2225 char *display;
2227 display = getenv ("DISPLAY");
2228 dpy = XOpenDisplay (display);
2229 if (!dpy) {
2230 fprintf (stderr, "XOpenDisplay `%s' failed\n",
2231 display ? display : "null");
2232 return;
2234 hint.res_name = "llpp";
2235 hint.res_class = "llpp";
2236 win = glXGetCurrentDrawable ();
2237 if (win == None) {
2238 fprintf (stderr, "glXGetCurrentDrawable returned None\n");
2239 XCloseDisplay (dpy);
2240 return;
2242 XSetClassHint (dpy, win, &hint);
2243 XCloseDisplay (dpy);
2246 #else
2247 #define set_wm_class(h) (void) (h)
2248 #endif
2250 enum { piunknown, pilinux, piwindows, piosx,
2251 pisun, pifreebsd, pidragonflybsd,
2252 piopenbsd, pimingw, picygwin };
2254 CAMLprim value ml_platform (value unit_v)
2256 CAMLparam1 (unit_v);
2257 int platid = piunknown;
2259 #if defined __linux__
2260 platid = pilinux;
2261 #elif defined __CYGWIN__
2262 platid = picygwin;
2263 #elif defined __MINGW32__
2264 platid = pimingw;
2265 #elif defined _WIN32
2266 platid = piwindows;
2267 #elif defined __DragonFly__
2268 platid = pidragonflybsd;
2269 #elif defined __FreeBSD__
2270 platid = pifreebsd;
2271 #elif defined __OpenBSD__
2272 platid = piopenbsd;
2273 #elif defined __sun__
2274 platid = pisun;
2275 #elif defined __APPLE__
2276 platid = piosx;
2277 #endif
2278 CAMLreturn (Val_int (platid));
2281 CAMLprim value ml_init (value sock_v, value params_v)
2283 CAMLparam2 (sock_v, params_v);
2284 CAMLlocal2 (trim_v, fuzz_v);
2285 #ifndef _WIN32
2286 int ret;
2287 #endif
2288 char *fontpath;
2289 int texcount;
2290 int wmclasshack;
2291 int colorspace;
2292 int mumemlimit;
2294 state.rotate = Int_val (Field (params_v, 0));
2295 state.proportional = Bool_val (Field (params_v, 1));
2296 trim_v = Field (params_v, 2);
2297 texcount = Int_val (Field (params_v, 3));
2298 state.sliceheight = Int_val (Field (params_v, 4));
2299 mumemlimit = Int_val (Field (params_v, 5));
2300 state.ctx = fz_new_context (&fz_alloc_default, mumemlimit);
2301 colorspace = Int_val (Field (params_v, 6));
2302 wmclasshack = Bool_val (Field (params_v, 7));
2303 fontpath = String_val (Field (params_v, 8));
2305 state.trimmargins = Bool_val (Field (trim_v, 0));
2306 fuzz_v = Field (trim_v, 1);
2307 state.trimfuzz.x0 = Int_val (Field (fuzz_v, 0));
2308 state.trimfuzz.y0 = Int_val (Field (fuzz_v, 1));
2309 state.trimfuzz.x1 = Int_val (Field (fuzz_v, 2));
2310 state.trimfuzz.y1 = Int_val (Field (fuzz_v, 3));
2312 set_tex_params (colorspace);
2313 set_wm_class (wmclasshack);
2315 if (*fontpath) {
2316 state.face = load_font (fontpath);
2318 else {
2319 unsigned int len;
2320 void *base = pdf_find_substitute_font (0, 0, 0, 0, &len);
2322 state.face = load_builtin_font (base, len);
2324 if (!state.face) _exit (1);
2326 fz_accelerate ();
2327 realloctexts (texcount);
2329 #ifdef _WIN32
2330 state.sock = Socket_val (sock_v);
2331 InitializeCriticalSection (&critsec);
2332 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
2333 if (state.thread == INVALID_HANDLE_VALUE) {
2334 errx (1, "CreateThread failed: %lx", GetLastError ());
2336 #else
2337 state.sock = Int_val (sock_v);
2338 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
2339 if (ret) {
2340 errx (1, "pthread_create: %s", strerror (ret));
2342 #endif
2344 CAMLreturn (Val_unit);