Mention unzip
[llpp.git] / link.c
blob17d571bea1ab9ab69eee55f9aac8c746b16df18c
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_xref *xref;
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.xref) {
429 pdf_free_xref (state.xref);
430 state.xref = 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.xref = pdf_open_xref (state.ctx, filename);
441 if (pdf_needs_password (state.xref)) {
442 int okay = pdf_authenticate_password (state.xref, password);
443 if (!okay) {
444 errx (1, "invalid password");
447 state.pagecount = pdf_count_pages (state.xref);
450 static void pdfinfo (void)
452 fz_obj *infoobj;
454 printd (state.sock, "info PDF version\t%d.%d",
455 state.xref->version / 10, state.xref->version % 10);
457 infoobj = fz_dict_gets (state.xref->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, "info Pages\t%d", state.pagecount);
477 printd (state.sock, "info Dimensions\t%d", state.pagedimcount);
478 printd (state.sock, "infoend");
481 static int readlen (int fd)
483 ssize_t n;
484 unsigned char p[4];
486 n = recv (fd, p, 4, 0);
487 if (n != 4) {
488 if (!n) errx (1, "EOF while reading length");
489 sockerr (1, "recv %" FMT_ss, n);
492 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
495 static void unlinktile (struct tile *tile)
497 int i;
499 for (i = 0; i < tile->slicecount; ++i) {
500 struct slice *s = &tile->slices[i];
502 if (s->texindex != -1) {
503 if (state.texowners[s->texindex].slice == s) {
504 state.texowners[s->texindex].slice = NULL;
510 static void freepage (struct page *page)
512 if (page->text) {
513 fz_free_text_span (state.ctx, page->text);
515 pdf_free_page (state.ctx, page->drawpage);
516 fz_free_display_list (state.ctx, page->dlist);
517 free (page);
520 static void freetile (struct tile *tile)
522 unlinktile (tile);
523 #ifndef PIGGYBACK
524 fz_drop_pixmap (state.ctx, tile->pixmap);
525 #else
526 if (state.pig) {
527 fz_drop_pixmap (state.ctx, state.pig);
529 state.pig = tile->pixmap;
530 #endif
531 free (tile);
534 #ifdef __ALTIVEC__
535 #include <altivec.h>
537 static int cacheline32bytes;
538 extern char **environ;
540 static void __attribute__ ((constructor)) clcheck (void)
542 char **envp = environ;
543 unsigned long *auxv;
545 while (*envp++);
547 for (auxv = (unsigned long *) envp; *auxv != 0; auxv += 2) {
548 if (*auxv == 19) {
549 cacheline32bytes = auxv[1] == 32;
550 return;
555 static void OPTIMIZE (3) clearpixmap (fz_pixmap *pixmap)
557 if (cacheline32bytes) {
558 intptr_t a1, a2, diff;
559 size_t sizea, i, size = pixmap->w * pixmap->h * pixmap->n;
560 vector unsigned char v = vec_splat_u8 (-1);
561 vector unsigned char *p;
563 a1 = a2 = (intptr_t) pixmap->samples;
564 a2 = (a1 + 31) & ~31;
565 diff = a2 - a1;
566 sizea = size - diff;
567 p = (void *) a2;
569 while (a1 != a2) *(char *) a1++ = 0xff;
570 for (i = 0; i < (sizea & ~31); i += 32) {
571 __asm volatile ("dcbz %0, %1"::"b"(a2),"r"(i));
572 vec_st (v, i, p);
573 vec_st (v, i + 16, p);
575 while (i < sizea) *((char *) a1 + i++) = 0xff;
577 else fz_clear_pixmap_with_color (pixmap, 0xff);
579 #else
580 #define clearpixmap(p) fz_clear_pixmap_with_color (p, 0xff)
581 #endif
583 static fz_matrix trimctm (pdf_page *page, int pindex)
585 fz_matrix ctm;
586 struct pagedim *pdim = &state.pagedims[pindex];
588 if (!pdim->tctmready) {
589 if (state.trimmargins) {
590 fz_rect realbox;
592 ctm = fz_concat (fz_rotate (-pdim->rotate), fz_scale (1, -1));
593 realbox = fz_transform_rect (ctm, pdim->mediabox);
594 ctm = fz_concat (ctm, fz_translate (-realbox.x0, -realbox.y0));
595 ctm = fz_concat (fz_invert_matrix (page->ctm), ctm);
597 else {
598 ctm = fz_identity;
600 pdim->tctm = ctm;
601 pdim->tctmready = 1;
603 return pdim->tctm;
606 static fz_matrix pagectm (struct page *page)
608 return fz_concat (trimctm (page->drawpage, page->pdimno),
609 state.pagedims[page->pdimno].ctm);
612 static void *loadpage (int pageno, int pindex)
614 fz_device *dev;
615 struct page *page = NULL;
617 page = calloc (sizeof (struct page), 1);
618 if (!page) {
619 err (1, "calloc page %d", pageno);
622 page->drawpage = pdf_load_page (state.xref, pageno);
623 page->dlist = fz_new_display_list (state.ctx);
624 dev = fz_new_list_device (state.ctx, page->dlist);
625 pdf_run_page (state.xref, page->drawpage, dev, fz_identity, NULL);
626 fz_free_device (dev);
628 page->pdimno = pindex;
629 page->pageno = pageno;
630 page->gen = state.gen;
632 return page;
635 static struct tile *alloctile (int h)
637 int i;
638 int slicecount;
639 size_t tilesize;
640 struct tile *tile;
642 slicecount = (h + state.sliceheight - 1) / state.sliceheight;
643 tilesize = sizeof (*tile) + ((slicecount - 1) * sizeof (struct slice));
644 tile = calloc (tilesize, 1);
645 if (!tile) {
646 err (1, "can not allocate tile (%" FMT_s " bytes)", tilesize);
648 for (i = 0; i < slicecount; ++i) {
649 int sh = MIN (h, state.sliceheight);
650 tile->slices[i].h = sh;
651 tile->slices[i].texindex = -1;
652 h -= sh;
654 tile->slicecount = slicecount;
655 tile->sliceheight = state.sliceheight;
656 return tile;
659 static struct tile *rendertile (struct page *page, int x, int y, int w, int h)
661 fz_bbox bbox;
662 fz_device *dev;
663 struct tile *tile;
664 struct pagedim *pdim;
666 tile = alloctile (h);
667 pdim = &state.pagedims[page->pdimno];
669 bbox = pdim->bounds;
670 bbox.x0 += x;
671 bbox.y0 += y;
672 bbox.x1 = bbox.x0 + w;
673 bbox.y1 = bbox.y0 + h;
675 if (state.pig) {
676 if (state.pig->w == w
677 && state.pig->h == h
678 && state.pig->colorspace == state.colorspace) {
679 tile->pixmap = state.pig;
680 tile->pixmap->x = bbox.x0;
681 tile->pixmap->y = bbox.y0;
683 else {
684 fz_drop_pixmap (state.ctx, state.pig);
686 state.pig = NULL;
688 if (!tile->pixmap) {
689 tile->pixmap =
690 fz_new_pixmap_with_rect (state.ctx, state.colorspace, bbox);
693 tile->w = w;
694 tile->h = h;
695 clearpixmap (tile->pixmap);
696 dev = fz_new_draw_device (state.ctx, tile->pixmap);
697 fz_execute_display_list (page->dlist, dev, pagectm (page), bbox, NULL);
698 fz_free_device (dev);
700 return tile;
703 static void initpdims (void)
705 int pageno;
706 double start, end;
708 start = now ();
709 for (pageno = 0; pageno < state.pagecount; ++pageno) {
710 int rotate;
711 fz_obj *pageobj;
712 struct pagedim *p;
713 fz_rect mediabox, cropbox;
715 pageobj = state.xref->page_objs[pageno];
717 if (state.trimmargins) {
718 fz_obj *obj;
719 pdf_page *page;
721 page = pdf_load_page (state.xref, pageno);
722 obj = fz_dict_gets (pageobj, "llpp.TrimBox");
723 if (state.trimanew || !obj) {
724 fz_rect rect;
725 fz_bbox bbox;
726 fz_matrix ctm;
727 fz_device *dev;
729 dev = fz_new_bbox_device (state.ctx, &bbox);
730 dev->hints |= FZ_IGNORE_SHADE;
731 ctm = fz_invert_matrix (page->ctm);
732 pdf_run_page (state.xref, page, dev, fz_identity, NULL);
733 fz_free_device (dev);
735 rect.x0 = bbox.x0 + state.trimfuzz.x0;
736 rect.x1 = bbox.x1 + state.trimfuzz.x1;
737 rect.y0 = bbox.y0 + state.trimfuzz.y0;
738 rect.y1 = bbox.y1 + state.trimfuzz.y1;
739 rect = fz_transform_rect (ctm, rect);
740 rect = fz_intersect_rect (rect, page->mediabox);
742 if (fz_is_empty_rect (rect)) {
743 mediabox = page->mediabox;
745 else {
746 mediabox = rect;
749 obj = fz_new_array (state.ctx, 4);
750 fz_array_push (obj, fz_new_real (state.ctx, mediabox.x0));
751 fz_array_push (obj, fz_new_real (state.ctx, mediabox.y0));
752 fz_array_push (obj, fz_new_real (state.ctx, mediabox.x1));
753 fz_array_push (obj, fz_new_real (state.ctx, mediabox.y1));
754 fz_dict_puts (pageobj, "llpp.TrimBox", obj);
756 else {
757 mediabox.x0 = fz_to_real (fz_array_get (obj, 0));
758 mediabox.y0 = fz_to_real (fz_array_get (obj, 1));
759 mediabox.x1 = fz_to_real (fz_array_get (obj, 2));
760 mediabox.y1 = fz_to_real (fz_array_get (obj, 3));
763 rotate = page->rotate;
764 pdf_free_page (state.ctx, page);
766 printd (state.sock, "progress %f Trimming %d",
767 (double) (pageno + 1) / state.pagecount,
768 pageno + 1);
770 else {
771 mediabox = pdf_to_rect (state.ctx, fz_dict_gets (pageobj, "MediaBox"));
772 if (fz_is_empty_rect (mediabox)) {
773 fprintf (stderr, "cannot find page size for page %d\n", pageno+1);
774 mediabox.x0 = 0;
775 mediabox.y0 = 0;
776 mediabox.x1 = 612;
777 mediabox.y1 = 792;
780 cropbox = pdf_to_rect (state.ctx, fz_dict_gets (pageobj, "CropBox"));
781 if (!fz_is_empty_rect (cropbox)) {
782 mediabox = fz_intersect_rect (mediabox, cropbox);
784 rotate = fz_to_int (fz_dict_gets (pageobj, "Rotate"));
787 if (state.pagedimcount == 0
788 || (p = &state.pagedims[state.pagedimcount-1], p->rotate != rotate)
789 || memcmp (&p->mediabox, &mediabox, sizeof (mediabox))) {
790 size_t size;
792 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
793 state.pagedims = realloc (state.pagedims, size);
794 if (!state.pagedims) {
795 err (1, "realloc pagedims to %" FMT_s " (%d elems)",
796 size, state.pagedimcount + 1);
799 p = &state.pagedims[state.pagedimcount++];
800 p->rotate = rotate;
801 p->mediabox = mediabox;
802 p->pageno = pageno;
805 end = now ();
806 if (state.trimmargins) {
807 printd (state.sock, "progress 1 Trimmed %d pages in %f seconds",
808 state.pagecount, end - start);
810 else {
811 printd (state.sock, "vmsg Processed %d pages in %f seconds",
812 state.pagecount, end - start);
814 state.trimanew = 0;
817 static void layout (void)
819 int pindex;
820 fz_rect box;
821 fz_matrix ctm;
822 double zoom, w, maxw = 0;
823 struct pagedim *p = state.pagedims;
825 if (state.proportional) {
826 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
827 box = fz_transform_rect (fz_rotate (p->rotate + state.rotate),
828 p->mediabox);
829 w = box.x1 - box.x0;
830 maxw = MAX (w, maxw);
834 p = state.pagedims;
835 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
836 fz_bbox bbox;
838 ctm = fz_rotate (state.rotate);
839 box = fz_transform_rect (fz_rotate (p->rotate + state.rotate),
840 p->mediabox);
841 w = box.x1 - box.x0;
843 if (state.proportional) {
844 double scale = w / maxw;
845 zoom = (state.w / w) * scale;
847 else {
848 zoom = state.w / w;
851 p->zoomctm = fz_scale (zoom, zoom);
852 ctm = fz_concat (p->zoomctm, ctm);
854 p->pagebox = fz_transform_rect (fz_rotate (p->rotate), p->mediabox);
855 p->pagebox.x1 -= p->pagebox.x0;
856 p->pagebox.y1 -= p->pagebox.y0;
857 p->pagebox.x0 = 0;
858 p->pagebox.y0 = 0;
859 bbox = fz_round_rect (fz_transform_rect (ctm, p->pagebox));
861 p->bounds = bbox;
862 p->left = state.proportional ? ((maxw - w) * zoom) / 2.0 : 0;
863 p->ctm = ctm;
865 ctm = fz_identity;
866 ctm = fz_concat (ctm, fz_translate (0, -p->mediabox.y1));
867 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
868 ctm = fz_concat (ctm, fz_rotate (p->rotate + state.rotate));
869 p->lctm = ctm;
871 p->tctmready = 0;
874 while (p-- != state.pagedims) {
875 int w = p->bounds.x1 - p->bounds.x0;
876 int h = p->bounds.y1 - p->bounds.y0;
878 printd (state.sock, "pdim %d %d %d %d", p->pageno, w, h, p->left);
882 static void recurse_outline (fz_outline *outline, int level)
884 while (outline) {
885 fz_link_dest *dest;
886 int i, top = 0;
887 struct pagedim *pdim = state.pagedims;
889 dest = &outline->dest;
890 for (i = 0; i < state.pagedimcount; ++i) {
891 if (state.pagedims[i].pageno > dest->ld.gotor.page)
892 break;
893 pdim = &state.pagedims[i];
895 if (dest->ld.gotor.flags & fz_link_flag_t_valid) {
896 fz_point p;
897 p.x = 0;
898 p.y = dest->ld.gotor.lt.y;
899 p = fz_transform_point (pdim->lctm, p);
900 top = p.y;
902 if (dest->ld.gotor.page >= 0 && dest->ld.gotor.page < 1<<30) {
903 int h;
904 double y0, y1;
906 y0 = MIN (pdim->bounds.y0, pdim->bounds.y1);
907 y1 = MAX (pdim->bounds.y0, pdim->bounds.y1);
908 h = y1 - y0;
909 printd (state.sock, "o %d %d %d %d %s",
910 level, dest->ld.gotor.page, top, h, outline->title);
912 if (outline->down) {
913 recurse_outline (outline->down, level + 1);
915 outline = outline->next;
919 static void process_outline (void)
921 fz_outline *outline;
923 if (!state.needoutline) return;
925 state.needoutline = 0;
926 outline = pdf_load_outline (state.xref);
927 if (outline) {
928 recurse_outline (outline, 0);
929 fz_free_outline (outline);
933 static int comparespans (const void *l, const void *r)
935 fz_text_span const *const*ls = l;
936 fz_text_span const *const*rs = r;
937 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
940 /* wishful thinking function */
941 static void search (regex_t *re, int pageno, int y, int forward)
943 int i, j;
944 int ret;
945 char *p;
946 char buf[256];
947 fz_matrix ctm;
948 fz_device *tdev;
949 pdf_page *drawpage;
950 fz_text_span *text, *span, **pspan;
951 struct pagedim *pdim, *pdimprev;
952 int stop = 0;
953 int niters = 0;
954 int nspans;
955 double start, end;
957 start = now ();
958 while (pageno >= 0 && pageno < state.pagecount && !stop) {
959 if (niters++ == 5) {
960 niters = 0;
961 if (hasdata (state.sock)) {
962 printd (state.sock,
963 "progress 1 attention requested aborting search at %d",
964 pageno);
965 stop = 1;
967 else {
968 printd (state.sock, "progress %f searching in page %d",
969 (double) (pageno + 1) / state.pagecount,
970 pageno);
973 pdimprev = NULL;
974 for (i = 0; i < state.pagedimcount; ++i) {
975 pdim = &state.pagedims[i];
976 if (pdim->pageno == pageno) {
977 goto found;
979 if (pdim->pageno > pageno) {
980 pdim = pdimprev;
981 goto found;
983 pdimprev = pdim;
985 pdim = pdimprev;
986 found:
988 drawpage = pdf_load_page (state.xref, pageno);
990 text = fz_new_text_span (state.ctx);
991 tdev = fz_new_text_device (state.ctx, text);
992 pdf_run_page (state.xref, drawpage, tdev, fz_identity, NULL);
993 fz_free_device (tdev);
995 nspans = 0;
996 for (span = text; span; span = span->next) {
997 nspans++;
999 pspan = malloc (sizeof (void *) * nspans);
1000 if (!pspan) {
1001 err (1, "malloc span pointers %" FMT_s, sizeof (void *) * nspans);
1003 for (i = 0, span = text; span; span = span->next, ++i) {
1004 pspan[i] = span;
1006 qsort (pspan, nspans, sizeof (fz_text_span *), comparespans);
1008 j = forward ? 0 : nspans - 1;
1009 while (nspans--) {
1010 regmatch_t rm;
1012 span = pspan[j];
1013 j += forward ? 1 : -1;
1014 p = buf;
1015 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
1016 if (forward) {
1017 if (span->text[i].bbox.y0 < y + 1) {
1018 continue;
1021 else {
1022 if (span->text[i].bbox.y0 > y - 1) {
1023 continue;
1026 if (span->text[i].c < 256) {
1027 *p++ = span->text[i].c;
1029 else {
1030 *p++ = '?';
1033 if (p == buf) {
1034 continue;
1036 *p++ = 0;
1038 ret = regexec (re, buf, 1, &rm, 0);
1039 if (ret) {
1040 if (ret != REG_NOMATCH) {
1041 size_t size;
1042 char errbuf[80];
1043 size = regerror (ret, re, errbuf, sizeof (errbuf));
1044 printd (state.sock,
1045 "msg regexec error `%.*s'",
1046 (int) size, errbuf);
1047 fz_free_text_span (state.ctx, text);
1048 pdf_free_page (state.ctx, drawpage);
1049 free (pspan);
1050 return;
1053 else {
1054 fz_bbox *sb, *eb;
1055 fz_point p1, p2, p3, p4;
1057 sb = &span->text[rm.rm_so].bbox;
1058 eb = &span->text[rm.rm_eo - 1].bbox;
1060 p1.x = sb->x0;
1061 p1.y = sb->y0;
1062 p2.x = eb->x1;
1063 p2.y = sb->y0;
1064 p3.x = eb->x1;
1065 p3.y = eb->y1;
1066 p4.x = sb->x0;
1067 p4.y = eb->y1;
1069 trimctm (drawpage, pdim - state.pagedims);
1070 ctm = fz_concat (pdim->tctm, pdim->zoomctm);
1072 p1 = fz_transform_point (ctm, p1);
1073 p2 = fz_transform_point (ctm, p2);
1074 p3 = fz_transform_point (ctm, p3);
1075 p4 = fz_transform_point (ctm, p4);
1077 if (!stop) {
1078 printd (state.sock,
1079 "firstmatch %d %d %f %f %f %f %f %f %f %f",
1080 pageno, 1,
1081 p1.x, p1.y,
1082 p2.x, p2.y,
1083 p3.x, p3.y,
1084 p4.x, p4.y);
1086 printd (state.sock,
1087 "progress 1 found at %d `%.*s' in %f sec",
1088 pageno, (int) (rm.rm_eo - rm.rm_so), &buf[rm.rm_so],
1089 now () - start);
1091 else {
1092 printd (state.sock,
1093 "match %d %d %f %f %f %f %f %f %f %f",
1094 pageno, 2,
1095 p1.x, p1.y,
1096 p2.x, p2.y,
1097 p3.x, p3.y,
1098 p4.x, p4.y);
1100 stop = 1;
1103 if (forward) {
1104 pageno += 1;
1105 y = 0;
1107 else {
1108 pageno -= 1;
1109 y = INT_MAX;
1111 fz_free_text_span (state.ctx, text);
1112 pdf_free_page (state.ctx, drawpage);
1113 free (pspan);
1115 end = now ();
1116 if (!stop) {
1117 printd (state.sock, "progress 1 no matches %f sec", end - start);
1119 printd (state.sock, "clearrects");
1122 static void set_tex_params (int colorspace)
1124 switch (colorspace) {
1125 case 0:
1126 state.texiform = GL_RGBA8;
1127 state.texform = GL_RGBA;
1128 state.texty = GL_UNSIGNED_BYTE;
1129 state.colorspace = fz_device_rgb;
1130 break;
1131 case 1:
1132 state.texiform = GL_RGBA8;
1133 state.texform = GL_BGRA;
1134 state.texty = fz_is_big_endian ()
1135 ? GL_UNSIGNED_INT_8_8_8_8
1136 : GL_UNSIGNED_INT_8_8_8_8_REV;
1137 state.colorspace = fz_device_bgr;
1138 break;
1139 case 2:
1140 state.texiform = GL_LUMINANCE_ALPHA;
1141 state.texform = GL_LUMINANCE_ALPHA;
1142 state.texty = GL_UNSIGNED_BYTE;
1143 state.colorspace = fz_device_gray;
1144 break;
1145 default:
1146 errx (1, "invalid colorspce %d", colorspace);
1150 static void realloctexts (int texcount)
1152 size_t size;
1154 if (texcount == state.texcount) return;
1156 if (texcount < state.texcount) {
1157 glDeleteTextures (state.texcount - texcount,
1158 state.texids + texcount);
1161 size = texcount * sizeof (*state.texids);
1162 state.texids = realloc (state.texids, size);
1163 if (!state.texids) {
1164 err (1, "realloc texids %" FMT_s, size);
1167 size = texcount * sizeof (*state.texowners);
1168 state.texowners = realloc (state.texowners, size);
1169 if (!state.texowners) {
1170 err (1, "realloc texowners %" FMT_s, size);
1172 if (texcount > state.texcount) {
1173 int i;
1175 glGenTextures (texcount - state.texcount,
1176 state.texids + state.texcount);
1177 for (i = state.texcount; i < texcount; ++i) {
1178 state.texowners[i].w = -1;
1179 state.texowners[i].slice = NULL;
1182 state.texcount = texcount;
1183 state.texindex = 0;
1186 static
1187 #ifdef _WIN32
1188 DWORD _stdcall
1189 #else
1190 void *
1191 #endif
1192 mainloop (void *unused)
1194 char *p = NULL;
1195 int len, ret, oldlen = 0;
1197 for (;;) {
1198 len = readlen (state.sock);
1199 if (len == 0) {
1200 errx (1, "readlen returned 0");
1203 if (oldlen < len + 1) {
1204 p = realloc (p, len + 1);
1205 if (!p) {
1206 err (1, "realloc %d failed", len + 1);
1208 oldlen = len + 1;
1210 readdata (state.sock, p, len);
1211 p[len] = 0;
1213 if (!strncmp ("open", p, 4)) {
1214 size_t filenamelen;
1215 char *password;
1216 char *filename = p + 5;
1218 filenamelen = strlen (filename);
1219 password = filename + filenamelen + 1;
1221 openxref (filename, password);
1222 printd (state.sock, "msg Opened %s (press h/F1 to get help)",
1223 filename);
1224 pdfinfo ();
1225 initpdims ();
1226 state.needoutline = 1;
1228 else if (!strncmp ("cs", p, 2)) {
1229 int i, colorspace;
1231 ret = sscanf (p + 2, " %d", &colorspace);
1232 if (ret != 1) {
1233 errx (1, "malformed cs `%.*s' ret=%d", len, p, ret);
1235 lock ("cs");
1236 set_tex_params (colorspace);
1237 for (i = 0; i < state.texcount; ++i) {
1238 state.texowners[i].w = -1;
1239 state.texowners[i].slice = NULL;
1241 unlock ("cs");
1243 else if (!strncmp ("freepage", p, 8)) {
1244 void *ptr;
1246 ret = sscanf (p + 8, " %" FMT_ptr, FMT_ptr_cast (&ptr));
1247 if (ret != 1) {
1248 errx (1, "malformed freepage `%.*s' ret=%d", len, p, ret);
1250 freepage (ptr);
1252 else if (!strncmp ("freetile", p, 8)) {
1253 void *ptr;
1255 ret = sscanf (p + 8, " %" FMT_ptr, FMT_ptr_cast (&ptr));
1256 if (ret != 1) {
1257 errx (1, "malformed freetile `%.*s' ret=%d", len, p, ret);
1259 freetile (ptr);
1261 else if (!strncmp ("search", p, 6)) {
1262 int icase, pageno, y, ret, len2, forward;
1263 char *pattern;
1264 regex_t re;
1266 ret = sscanf (p + 6, " %d %d %d %d,%n",
1267 &icase, &pageno, &y, &forward, &len2);
1268 if (ret != 4) {
1269 errx (1, "malformed search `%s' ret=%d", p, ret);
1272 pattern = p + 6 + len2;
1273 ret = regcomp (&re, pattern,
1274 REG_EXTENDED | (icase ? REG_ICASE : 0));
1275 if (ret) {
1276 char errbuf[80];
1277 size_t size;
1279 size = regerror (ret, &re, errbuf, sizeof (errbuf));
1280 printd (state.sock, "msg regcomp failed `%.*s'",
1281 (int) size, errbuf);
1283 else {
1284 search (&re, pageno, y, forward);
1285 regfree (&re);
1288 else if (!strncmp ("geometry", p, 8)) {
1289 int w, h;
1291 printd (state.sock, "clear");
1292 ret = sscanf (p + 8, " %d %d", &w, &h);
1293 if (ret != 2) {
1294 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
1297 lock ("geometry");
1298 state.h = h;
1299 if (w != state.w) {
1300 int i;
1301 state.w = w;
1302 for (i = 0; i < state.texcount; ++i) {
1303 state.texowners[i].slice = NULL;
1306 layout ();
1307 process_outline ();
1308 state.gen++;
1309 unlock ("geometry");
1310 printd (state.sock, "continue %d", state.pagecount);
1312 else if (!strncmp ("reqlayout", p, 9)) {
1313 int rotate, proportional;
1315 printd (state.sock, "clear");
1316 ret = sscanf (p + 9, " %d %d", &rotate, &proportional);
1317 if (ret != 2) {
1318 errx (1, "bad reqlayout line `%.*s' ret=%d", len, p, ret);
1320 lock ("reqlayout");
1321 state.rotate = rotate;
1322 state.proportional = proportional;
1323 layout ();
1324 unlock ("reqlayout");
1325 printd (state.sock, "continue %d", state.pagecount);
1327 else if (!strncmp ("page", p, 4)) {
1328 double a, b;
1329 struct page *page;
1330 int pageno, pindex, ret;
1332 ret = sscanf (p + 4, " %d %d", &pageno, &pindex);
1333 if (ret != 2) {
1334 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
1337 lock ("page");
1338 a = now ();
1339 page = loadpage (pageno, pindex);
1340 b = now ();
1341 unlock ("page");
1343 printd (state.sock, "page %" FMT_ptr " %f",
1344 FMT_ptr_cast2 (page), b - a);
1346 else if (!strncmp ("tile", p, 4)) {
1347 int x, y, w, h, ret;
1348 struct page *page;
1349 struct tile *tile;
1350 double a, b;
1352 ret = sscanf (p + 4, " %" FMT_ptr " %d %d %d %d",
1353 FMT_ptr_cast (&page), &x, &y, &w, &h);
1354 if (ret != 5) {
1355 errx (1, "bad tile line `%.*s' ret=%d", len, p, ret);
1358 lock ("tile");
1359 a = now ();
1360 tile = rendertile (page, x, y, w, h);
1361 b = now ();
1362 unlock ("tile");
1364 printd (state.sock, "tile %d %d %" FMT_ptr " %u %f",
1365 x, y,
1366 FMT_ptr_cast2 (tile),
1367 tile->w * tile->h * tile->pixmap->n,
1368 b - a);
1370 else if (!strncmp ("settrim", p, 7)) {
1371 int trimmargins;
1372 fz_bbox fuzz;
1374 ret = sscanf (p + 7, " %d %d %d %d %d", &trimmargins,
1375 &fuzz.x0, &fuzz.y0, &fuzz.x1, &fuzz.y1);
1376 if (ret != 5) {
1377 errx (1, "malformed settrim `%.*s' ret=%d", len, p, ret);
1379 printd (state.sock, "clear");
1380 lock ("settrim");
1381 state.trimmargins = trimmargins;
1382 state.needoutline = 1;
1383 if (memcmp (&fuzz, &state.trimfuzz, sizeof (fuzz))) {
1384 state.trimanew = 1;
1385 state.trimfuzz = fuzz;
1387 state.pagedimcount = 0;
1388 free (state.pagedims);
1389 state.pagedims = NULL;
1390 initpdims ();
1391 layout ();
1392 process_outline ();
1393 unlock ("settrim");
1394 printd (state.sock, "continue %d", state.pagecount);
1396 else if (!strncmp ("texcount", p, 8)) {
1397 int n;
1399 ret = sscanf (p + 8, " %d", &n);
1400 if (ret != 1) {
1401 errx (1, "malformed texcount `%.*s' ret=%d", len, p, ret);
1403 realloctexts (n);
1405 else if (!strncmp ("sliceh", p, 6)) {
1406 int h;
1408 ret = sscanf (p + 6, " %d", &h);
1409 if (ret != 1) {
1410 errx (1, "malformed sliceh `%.*s' ret=%d", len, p, ret);
1412 if (h != state.sliceheight) {
1413 int i;
1415 state.sliceheight = h;
1416 for (i = 0; i < state.texcount; ++i) {
1417 state.texowners[i].w = -1;
1418 state.texowners[i].h = -1;
1419 state.texowners[i].slice = NULL;
1423 else if (!strncmp ("interrupt", p, 9)) {
1424 printd (state.sock, "vmsg interrupted");
1426 else if (!strncmp ("quit", p, 4)) {
1427 return 0;
1429 else {
1430 errx (1, "unknown command %.*s", len, p);
1433 return 0;
1436 static void showsel (struct page *page, int ox, int oy)
1438 fz_bbox bbox;
1439 fz_text_span *span;
1440 struct mark first, last;
1442 first = page->fmark;
1443 last = page->lmark;
1445 if (!first.span || !last.span) return;
1447 glEnable (GL_BLEND);
1448 glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA);
1449 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1451 ox -= state.pagedims[page->pdimno].bounds.x0;
1452 oy -= state.pagedims[page->pdimno].bounds.y0;
1453 for (span = first.span; span; span = span->next) {
1454 int i, j, k;
1456 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0;
1458 j = 0;
1459 k = span->len - 1;
1461 if (span == page->fmark.span && span == page->lmark.span) {
1462 j = MIN (first.i, last.i);
1463 k = MAX (first.i, last.i);
1465 else if (span == first.span) {
1466 j = first.i;
1468 else if (span == last.span) {
1469 k = last.i;
1472 for (i = j; i <= k; ++i) {
1473 bbox = fz_union_bbox (bbox, span->text[i].bbox);
1475 lprintf ("%d %d %d %d oy=%d ox=%d\n",
1476 bbox.x0,
1477 bbox.y0,
1478 bbox.x1,
1479 bbox.y1,
1480 oy, ox);
1482 glRecti (bbox.x0 + ox, bbox.y0 + oy, bbox.x1 + ox, bbox.y1 + oy);
1484 if (span == last.span) break;
1486 glDisable (GL_BLEND);
1489 static void highlightlinks (struct page *page, int xoff, int yoff)
1491 fz_link *link;
1492 fz_matrix ctm;
1494 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1495 glEnable (GL_LINE_STIPPLE);
1496 glLineStipple (0.5, 0xcccc);
1498 ctm = fz_concat (pagectm (page), fz_translate (xoff, yoff));
1500 glBegin (GL_QUADS);
1501 for (link = page->drawpage->links; link; link = link->next) {
1502 fz_point p1, p2, p3, p4;
1504 p1.x = link->rect.x0;
1505 p1.y = link->rect.y0;
1507 p2.x = link->rect.x1;
1508 p2.y = link->rect.y0;
1510 p3.x = link->rect.x1;
1511 p3.y = link->rect.y1;
1513 p4.x = link->rect.x0;
1514 p4.y = link->rect.y1;
1516 p1 = fz_transform_point (ctm, p1);
1517 p2 = fz_transform_point (ctm, p2);
1518 p3 = fz_transform_point (ctm, p3);
1519 p4 = fz_transform_point (ctm, p4);
1521 switch (link->dest.kind) {
1522 case FZ_LINK_GOTO: glColor3ub (255, 0, 0); break;
1523 case FZ_LINK_URI: glColor3ub (0, 0, 255); break;
1524 default: glColor3ub (0, 0, 0); break;
1527 glVertex2f (p1.x, p1.y);
1528 glVertex2f (p2.x, p2.y);
1529 glVertex2f (p3.x, p3.y);
1530 glVertex2f (p4.x, p4.y);
1532 glEnd ();
1534 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1535 glDisable (GL_LINE_STIPPLE);
1538 static void uploadslice (struct tile *tile, struct slice *slice)
1540 int offset;
1541 struct slice *slice1;
1543 offset = 0;
1544 for (slice1 = tile->slices; slice != slice1; slice1++) {
1545 offset += slice1->h * tile->w * tile->pixmap->n;
1547 if (slice->texindex != -1 && slice->texindex < state.texcount
1548 && state.texowners[slice->texindex].slice == slice) {
1549 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1551 else {
1552 int subimage = 0;
1553 int texindex = state.texindex++ % state.texcount;
1555 if (state.texowners[texindex].w == tile->w) {
1556 if (state.texowners[texindex].h >= slice->h) {
1557 subimage = 1;
1559 else {
1560 state.texowners[texindex].h = slice->h;
1563 else {
1564 state.texowners[texindex].h = slice->h;
1567 state.texowners[texindex].w = tile->w;
1568 state.texowners[texindex].slice = slice;
1569 slice->texindex = texindex;
1571 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[texindex]);
1572 if (subimage) {
1573 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1577 tile->w,
1578 slice->h,
1579 state.texform,
1580 state.texty,
1581 tile->pixmap->samples+offset
1584 else {
1585 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1587 state.texiform,
1588 tile->w,
1589 slice->h,
1591 state.texform,
1592 state.texty,
1593 tile->pixmap->samples+offset
1599 CAMLprim value ml_drawtile (value args_v, value ptr_v)
1601 CAMLparam2 (args_v, ptr_v);
1602 int dispx = Int_val (Field (args_v, 0));
1603 int dispy = Int_val (Field (args_v, 1));
1604 int dispw = Int_val (Field (args_v, 2));
1605 int disph = Int_val (Field (args_v, 3));
1606 int tilex = Int_val (Field (args_v, 4));
1607 int tiley = Int_val (Field (args_v, 5));
1608 char *s = String_val (ptr_v);
1609 struct tile *tile = parse_pointer ("ml_drawtile", s);
1611 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1613 int slicey, firstslice;
1614 struct slice *slice;
1616 firstslice = tiley / tile->sliceheight;
1617 slice = &tile->slices[firstslice];
1618 slicey = tiley % tile->sliceheight;
1620 while (disph > 0) {
1621 int dh;
1623 dh = slice->h - slicey;
1624 dh = MIN (disph, dh);
1625 uploadslice (tile, slice);
1627 glBegin (GL_QUADS);
1629 glTexCoord2i (tilex, slicey);
1630 glVertex2i (dispx, dispy);
1632 glTexCoord2i (tilex+dispw, slicey);
1633 glVertex2i (dispx+dispw, dispy);
1635 glTexCoord2i (tilex+dispw, slicey+dh);
1636 glVertex2i (dispx+dispw, dispy+dh);
1638 glTexCoord2i (tilex, slicey+dh);
1639 glVertex2i (dispx, dispy+dh);
1641 glEnd ();
1643 dispy += dh;
1644 disph -= dh;
1645 slice++;
1646 ARSERT (!(slice - tile->slices >= tile->slicecount && disph > 0));
1647 slicey = 0;
1650 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1651 CAMLreturn (Val_unit);
1654 CAMLprim value ml_postprocess (value ptr_v, value hlinks_v,
1655 value xoff_v, value yoff_v)
1657 CAMLparam4 (ptr_v, hlinks_v, xoff_v, yoff_v);
1658 int xoff = Int_val (xoff_v);
1659 int yoff = Int_val (yoff_v);
1660 char *s = String_val (ptr_v);
1661 struct page *page = parse_pointer ("ml_postprocess", s);
1663 if (Bool_val (hlinks_v)) highlightlinks (page, xoff, yoff);
1665 if (trylock ("ml_postprocess")) {
1666 goto done;
1668 showsel (page, xoff, yoff);
1669 unlock ("ml_postprocess");
1671 done:
1672 CAMLreturn (Val_unit);
1675 static fz_link *getlink (struct page *page, int x, int y)
1677 fz_point p;
1678 fz_matrix ctm;
1679 fz_link *link;
1681 p.x = x;
1682 p.y = y;
1684 ctm = fz_concat (trimctm (page->drawpage, page->pdimno),
1685 state.pagedims[page->pdimno].ctm);
1686 ctm = fz_invert_matrix (ctm);
1687 p = fz_transform_point (ctm, p);
1689 for (link = page->drawpage->links; link; link = link->next) {
1690 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1691 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1692 return link;
1696 return NULL;
1699 static void droptext (struct page *page)
1701 if (page->text) {
1702 fz_free_text_span (state.ctx, page->text);
1703 page->fmark.i = -1;
1704 page->lmark.i = -1;
1705 page->fmark.span = NULL;
1706 page->lmark.span = NULL;
1707 page->text = NULL;
1711 static void ensuretext (struct page *page)
1713 if (state.gen != page->gen) {
1714 droptext (page);
1715 page->gen = state.gen;
1717 if (!page->text) {
1718 fz_device *tdev;
1720 page->text = fz_new_text_span (state.ctx);
1721 tdev = fz_new_text_device (state.ctx, page->text);
1722 fz_execute_display_list (page->dlist,
1723 tdev,
1724 pagectm (page),
1725 fz_infinite_bbox, NULL);
1726 fz_free_device (tdev);
1730 CAMLprim value ml_whatsunder (value ptr_v, value x_v, value y_v)
1732 CAMLparam3 (ptr_v, x_v, y_v);
1733 CAMLlocal3 (ret_v, tup_v, str_v);
1734 fz_link *link;
1735 struct page *page;
1736 char *s = String_val (ptr_v);
1737 int x = Int_val (x_v), y = Int_val (y_v);
1738 struct pagedim *pdim;
1740 ret_v = Val_int (0);
1741 if (trylock ("ml_whatsunder")) {
1742 goto done;
1745 page = parse_pointer ("ml_whatsunder", s);
1746 pdim = &state.pagedims[page->pdimno];
1747 x += pdim->bounds.x0;
1748 y += pdim->bounds.y0;
1749 link = getlink (page, x, y);
1750 if (link) {
1751 switch (link->dest.kind) {
1752 case FZ_LINK_GOTO:
1754 int pageno;
1755 fz_point p;
1757 pageno = link->dest.ld.gotor.page;
1758 p.x = 0;
1759 p.y = 0;
1761 if (link->dest.ld.gotor.flags & fz_link_flag_t_valid) {
1762 p.y = link->dest.ld.gotor.lt.y;
1763 p = fz_transform_point (pdim->lctm, p);
1765 tup_v = caml_alloc_tuple (2);
1766 ret_v = caml_alloc_small (1, 1);
1767 Field (tup_v, 0) = Val_int (pageno);
1768 Field (tup_v, 1) = Val_int (p.y);
1769 Field (ret_v, 0) = tup_v;
1771 break;
1773 case FZ_LINK_URI:
1774 str_v = caml_copy_string (link->dest.ld.uri.uri);
1775 ret_v = caml_alloc_small (1, 0);
1776 Field (ret_v, 0) = str_v;
1777 break;
1779 default:
1780 printd (state.sock, "msg unhandled link kind %d", link->dest.kind);
1781 break;
1784 else {
1785 int i;
1786 fz_text_span *span;
1788 ensuretext (page);
1789 for (span = page->text; span; span = span->next) {
1790 for (i = 0; i < span->len; ++i) {
1791 fz_bbox *b;
1792 b = &span->text[i].bbox;
1793 if ((x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1)) {
1794 const char *n2 =
1795 span->font && span->font->name
1796 ? span->font->name
1797 : "Span has no font name"
1799 FT_FaceRec *face = span->font->ft_face;
1800 if (face && face->family_name) {
1801 char *s;
1802 char *n1 = face->family_name;
1803 size_t l1 = strlen (n1);
1804 size_t l2 = strlen (n2);
1806 if (l1 != l2 || memcmp (n1, n2, l1)) {
1807 s = malloc (l1 + l2 + 2);
1808 if (s) {
1809 memcpy (s, n2, l2);
1810 s[l2] = '=';
1811 memcpy (s + l2 + 1, n1, l1 + 1);
1812 str_v = caml_copy_string (s);
1813 free (s);
1817 if (str_v == 0) {
1818 str_v = caml_copy_string (n2);
1820 ret_v = caml_alloc_small (1, 2);
1821 Field (ret_v, 0) = str_v;
1822 goto unlock;
1827 unlock:
1828 unlock ("ml_whatsunder");
1830 done:
1831 CAMLreturn (ret_v);
1834 CAMLprim value ml_seltext (value ptr_v, value rect_v)
1836 CAMLparam2 (ptr_v, rect_v);
1837 fz_bbox *b;
1838 struct page *page;
1839 fz_text_span *span;
1840 struct mark first, last;
1841 int i, x0, x1, y0, y1;
1842 struct pagedim *pdim;
1843 char *s = String_val (ptr_v);
1845 if (trylock ("ml_seltext")) {
1846 goto done;
1849 page = parse_pointer ("ml_seltext", s);
1850 ensuretext (page);
1852 pdim = &state.pagedims[page->pdimno];
1854 x0 = Int_val (Field (rect_v, 0)) + pdim->bounds.x0;
1855 y0 = Int_val (Field (rect_v, 1)) + pdim->bounds.y0;
1856 x1 = Int_val (Field (rect_v, 2)) + pdim->bounds.x0;
1857 y1 = Int_val (Field (rect_v, 3)) + pdim->bounds.y0;
1859 if (0) {
1860 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1861 glColor3ub (128, 128, 128);
1862 glRecti (x0, y0, x1, y1);
1863 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1866 first.span = NULL;
1867 last.span = NULL;
1869 last.i = first.i = 0;
1870 first.span = page->text;
1871 for (span = page->text; span; span = span->next) {
1872 for (i = 0; i < span->len; ++i) {
1873 b = &span->text[i].bbox;
1874 int selected = 0;
1876 if (x0 >= b->x0 && x0 <= b->x1 && y0 >= b->y0 && y0 <= b->y1) {
1877 first.i = i;
1878 first.span = span;
1879 selected = 1;
1881 if (x1 >= b->x0 && x1 <= b->x1 && y1 >= b->y0 && y1 <= b->y1) {
1882 last.i = i;
1883 last.span = span;
1884 selected = 1;
1886 if (0 && selected) {
1887 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1888 glColor3ub (128, 128, 128);
1889 glRecti (b->x0, b->y0, b->x1, b->y1);
1890 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1895 if (y1 < y0 || x1 < x0) {
1896 int swap = 0;
1898 if (first.span == last.span) {
1899 swap = 1;
1901 else {
1902 if (y1 < y0) {
1903 for (span = first.span; span && span != last.span;
1904 span = span->next) {
1905 if (span->eol) {
1906 swap = 1;
1907 break;
1913 if (swap) {
1914 i = first.i;
1915 span = first.span;
1916 first.i = last.i;
1917 first.span = last.span;
1918 last.i = i;
1919 last.span = span;
1923 page->fmark = first;
1924 page->lmark = last;
1926 unlock ("ml_seltext");
1928 done:
1929 CAMLreturn (Val_unit);
1932 static int pipespan (FILE *f, fz_text_span *span, int a, int b)
1934 char buf[4];
1935 int i, len, ret;
1937 for (i = a; i <= b; ++i) {
1938 len = runetochar (buf, &span->text[i].c);
1939 ret = fwrite (buf, len, 1, f);
1941 if (ret != 1) {
1942 printd (state.sock, "msg failed to write %d bytes ret=%d: %s",
1943 len, ret, strerror (errno));
1944 return -1;
1947 return 0;
1950 CAMLprim value ml_copysel (value ptr_v)
1952 CAMLparam1 (ptr_v);
1953 FILE *f;
1954 struct page *page;
1955 char *s = String_val (ptr_v);
1957 if (trylock ("ml_copysel")) {
1958 goto done;
1961 if (!*s) {
1962 close:
1963 #ifdef USE_XSEL
1964 if (state.xselpipe) {
1965 int ret = pclose (state.xselpipe);
1966 if (ret) {
1967 printd (state.sock, "msg failed to close xsel pipe `%s'",
1968 strerror (errno));
1970 state.xselpipe = NULL;
1972 #else
1973 printf ("========================================\n");
1974 #endif
1976 else {
1977 fz_text_span *span;
1979 page = parse_pointer ("ml_sopysel", s);
1981 if (!page->fmark.span || !page->lmark.span) {
1982 printd (state.sock, "msg nothing to copy");
1983 goto unlock;
1986 f = stdout;
1987 #ifdef USE_XSEL
1988 if (!state.xselpipe) {
1989 state.xselpipe = popen ("xsel -i", "w");
1990 if (!state.xselpipe) {
1991 printd (state.sock, "msg failed to open xsel pipe `%s'",
1992 strerror (errno));
1994 else {
1995 f = state.xselpipe;
1998 else {
1999 f = state.xselpipe;
2001 #endif
2003 for (span = page->fmark.span;
2004 span && span != page->lmark.span->next;
2005 span = span->next) {
2006 int a = span == page->fmark.span ? page->fmark.i : 0;
2007 int b = span == page->lmark.span ? page->lmark.i : span->len - 1;
2008 if (pipespan (f, span, a, b)) {
2009 goto close;
2011 if (span->eol) {
2012 if (putc ('\n', f) == EOF) {
2013 printd (state.sock,
2014 "msg failed break line on xsel pipe `%s'",
2015 strerror (errno));
2016 goto close;
2020 page->lmark.span = NULL;
2021 page->fmark.span = NULL;
2024 unlock:
2025 unlock ("ml_copysel");
2027 done:
2028 CAMLreturn (Val_unit);
2031 CAMLprim value ml_getpdimrect (value pagedimno_v)
2033 CAMLparam1 (pagedimno_v);
2034 CAMLlocal1 (ret_v);
2035 int pagedimno = Int_val (pagedimno_v);
2036 fz_rect box;
2038 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
2039 if (trylock ("ml_getpdimrect")) {
2040 box = fz_empty_rect;
2042 else {
2043 box = state.pagedims[pagedimno].mediabox;
2044 unlock ("ml_getpdimrect");
2047 Store_double_field (ret_v, 0, box.x0);
2048 Store_double_field (ret_v, 1, box.x1);
2049 Store_double_field (ret_v, 2, box.y0);
2050 Store_double_field (ret_v, 3, box.y1);
2052 CAMLreturn (ret_v);
2055 static double getmaxw (void)
2057 int i;
2058 struct pagedim *p;
2059 double maxw = 0.0;
2061 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
2062 double x0, x1, w;
2064 x0 = MIN (p->mediabox.x0, p->mediabox.x1);
2065 x1 = MAX (p->mediabox.x0, p->mediabox.x1);
2067 w = x1 - x0;
2068 maxw = MAX (w, maxw);
2070 return maxw;
2073 CAMLprim value ml_getmaxw (value unit_v)
2075 CAMLparam1 (unit_v);
2076 CAMLlocal1 (ret_v);
2077 double maxw = 0.0;
2079 if (trylock ("ml_getmaxw")) {
2080 goto done;
2082 maxw = getmaxw ();
2083 unlock ("ml_getmaxw");
2084 done:
2085 ret_v = caml_copy_double (maxw);
2086 CAMLreturn (ret_v);
2089 CAMLprim value ml_zoom_for_height (value winw_v, value winh_v, value dw_v)
2091 CAMLparam3 (winw_v, winh_v, dw_v);
2092 CAMLlocal1 (ret_v);
2093 int i;
2094 double zoom = 1.0;
2095 double maxw = 0.0, maxh = 0.0;
2096 struct pagedim *p;
2097 double winw = Int_val (winw_v);
2098 double winh = Int_val (winh_v);
2099 double dw = Int_val (dw_v);
2100 double pw = 1.0, ph = 1.0, num, den;
2102 if (trylock ("ml_zoom_for_height")) {
2103 goto done;
2106 if (state.proportional) {
2107 maxw = getmaxw ();
2110 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
2111 double x0, x1, y0, y1, w, h, scaledh, scale;
2113 x0 = MIN (p->mediabox.x0, p->mediabox.x1);
2114 x1 = MAX (p->mediabox.x0, p->mediabox.x1);
2115 y0 = MIN (p->mediabox.y0, p->mediabox.y1);
2116 y1 = MAX (p->mediabox.y0, p->mediabox.y1);
2118 w = x1 - x0;
2119 h = y1 - y0;
2121 if (state.proportional) {
2122 scale = w / maxw;
2123 scaledh = h * scale;
2125 else {
2126 scale = 1.0;
2127 scaledh = h;
2130 if (scaledh > maxh) {
2131 maxh = scaledh;
2132 ph = scaledh;
2133 pw = w * scale;
2137 num = (winh * pw) + (ph * dw);
2138 den = ph * winw;
2139 zoom = num / den;
2141 unlock ("ml_zoom_for_height");
2142 done:
2143 ret_v = caml_copy_double (zoom);
2144 CAMLreturn (ret_v);
2147 #include "glfont.c"
2149 CAMLprim value ml_draw_string (value pt_v, value x_v, value y_v, value string_v)
2151 CAMLparam4 (pt_v, x_v, y_v, string_v);
2152 CAMLlocal1 (ret_v);
2153 int pt = Int_val(pt_v);
2154 int x = Int_val (x_v);
2155 int y = Int_val (y_v);
2156 double w;
2158 w = draw_string (state.face, pt, x, y, String_val (string_v));
2159 ret_v = caml_copy_double (w);
2160 CAMLreturn (ret_v);
2163 CAMLprim value ml_measure_string (value pt_v, value string_v)
2165 CAMLparam2 (pt_v, string_v);
2166 CAMLlocal1 (ret_v);
2167 int pt = Int_val (pt_v);
2168 double w;
2170 w = measure_string (state.face, pt, String_val (string_v));
2171 ret_v = caml_copy_double (w);
2172 CAMLreturn (ret_v);
2175 CAMLprim value ml_getpagebox (value opaque_v)
2177 CAMLparam1 (opaque_v);
2178 CAMLlocal1 (ret_v);
2179 fz_bbox bbox;
2180 fz_device *dev;
2181 char *s = String_val (opaque_v);
2182 struct page *page = parse_pointer ("ml_getpagebox", s);
2184 ret_v = caml_alloc_tuple (4);
2185 dev = fz_new_bbox_device (state.ctx, &bbox);
2186 dev->hints |= FZ_IGNORE_SHADE;
2187 pdf_run_page (state.xref, page->drawpage, dev, pagectm (page), NULL);
2188 fz_free_device (dev);
2190 Field (ret_v, 0) = Val_int (bbox.x0);
2191 Field (ret_v, 1) = Val_int (bbox.y0);
2192 Field (ret_v, 2) = Val_int (bbox.x1);
2193 Field (ret_v, 3) = Val_int (bbox.y1);
2195 CAMLreturn (ret_v);
2198 CAMLprim value ml_setaalevel (value level_v)
2200 CAMLparam1 (level_v);
2202 state.aalevel = Int_val (level_v);
2203 CAMLreturn (Val_unit);
2206 #if !defined _WIN32 && !defined __APPLE__
2207 #undef pixel
2208 #include <X11/X.h>
2209 #include <X11/Xlib.h>
2210 #include <X11/Xutil.h>
2211 #include <GL/glx.h>
2213 static void set_wm_class (int hack)
2215 if (hack) {
2216 Display *dpy;
2217 Window win;
2218 XClassHint hint;
2219 char *display;
2221 display = getenv ("DISPLAY");
2222 dpy = XOpenDisplay (display);
2223 if (!dpy) {
2224 fprintf (stderr, "XOpenDisplay `%s' failed\n",
2225 display ? display : "null");
2226 return;
2228 hint.res_name = "llpp";
2229 hint.res_class = "llpp";
2230 win = glXGetCurrentDrawable ();
2231 if (win == None) {
2232 fprintf (stderr, "glXGetCurrentDrawable returned None\n");
2233 XCloseDisplay (dpy);
2234 return;
2236 XSetClassHint (dpy, win, &hint);
2237 XCloseDisplay (dpy);
2240 #else
2241 #define set_wm_class(h) (void) (h)
2242 #endif
2244 enum { piunknown, pilinux, piwindows, piosx,
2245 pisun, pifreebsd, pidragonflybsd,
2246 piopenbsd, pimingw, picygwin };
2248 CAMLprim value ml_platform (value unit_v)
2250 CAMLparam1 (unit_v);
2251 int platid = piunknown;
2253 #if defined __linux__
2254 platid = pilinux;
2255 #elif defined __CYGWIN__
2256 platid = picygwin;
2257 #elif defined __MINGW32__
2258 platid = pimingw;
2259 #elif defined _WIN32
2260 platid = piwindows;
2261 #elif defined __DragonFly__
2262 platid = pidragonflybsd;
2263 #elif defined __FreeBSD__
2264 platid = pifreebsd;
2265 #elif defined __OpenBSD__
2266 platid = piopenbsd;
2267 #elif defined __sun__
2268 platid = pisun;
2269 #elif defined __APPLE__
2270 platid = piosx;
2271 #endif
2272 CAMLreturn (Val_int (platid));
2275 CAMLprim value ml_init (value sock_v, value params_v)
2277 CAMLparam2 (sock_v, params_v);
2278 CAMLlocal2 (trim_v, fuzz_v);
2279 #ifndef _WIN32
2280 int ret;
2281 #endif
2282 char *fontpath;
2283 int texcount;
2284 int wmclasshack;
2285 int colorspace;
2286 int mumemlimit;
2288 state.rotate = Int_val (Field (params_v, 0));
2289 state.proportional = Bool_val (Field (params_v, 1));
2290 trim_v = Field (params_v, 2);
2291 texcount = Int_val (Field (params_v, 3));
2292 state.sliceheight = Int_val (Field (params_v, 4));
2293 mumemlimit = Int_val (Field (params_v, 5));
2294 state.ctx = fz_new_context (&fz_alloc_default, mumemlimit);
2295 colorspace = Int_val (Field (params_v, 6));
2296 wmclasshack = Bool_val (Field (params_v, 7));
2297 fontpath = String_val (Field (params_v, 8));
2299 state.trimmargins = Bool_val (Field (trim_v, 0));
2300 fuzz_v = Field (trim_v, 1);
2301 state.trimfuzz.x0 = Int_val (Field (fuzz_v, 0));
2302 state.trimfuzz.y0 = Int_val (Field (fuzz_v, 1));
2303 state.trimfuzz.x1 = Int_val (Field (fuzz_v, 2));
2304 state.trimfuzz.y1 = Int_val (Field (fuzz_v, 3));
2306 set_tex_params (colorspace);
2307 set_wm_class (wmclasshack);
2309 if (*fontpath) {
2310 state.face = load_font (fontpath);
2312 else {
2313 unsigned int len;
2314 void *base = pdf_find_substitute_font (0, 0, 0, 0, &len);
2316 state.face = load_builtin_font (base, len);
2318 if (!state.face) _exit (1);
2320 fz_accelerate ();
2321 realloctexts (texcount);
2323 #ifdef _WIN32
2324 state.sock = Socket_val (sock_v);
2325 InitializeCriticalSection (&critsec);
2326 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
2327 if (state.thread == INVALID_HANDLE_VALUE) {
2328 errx (1, "CreateThread failed: %lx", GetLastError ());
2330 #else
2331 state.sock = Int_val (sock_v);
2332 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
2333 if (ret) {
2334 errx (1, "pthread_create: %s", strerror (ret));
2336 #endif
2338 CAMLreturn (Val_unit);