Move font related stuff to a struct of their own
[llpp.git] / link.c
blob4d1f5a033b5cab722dc0e8e41ba92e3080286cb6
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 fz_pixmap *pixmap;
165 struct slice slices[1];
168 struct pagedim {
169 int pageno;
170 int rotate;
171 int left;
172 int tctmready;
173 fz_bbox bounds;
174 fz_rect pagebox;
175 fz_rect mediabox;
176 fz_matrix ctm, zoomctm, lctm, tctm;
179 struct page {
180 int gen;
181 int pageno;
182 int pdimno;
183 fz_text_span *text;
184 pdf_page *drawpage;
185 fz_display_list *dlist;
186 struct mark {
187 int i;
188 fz_text_span *span;
189 } fmark, lmark;
192 #if !defined _WIN32 && !defined __APPLE__
193 #define USE_XSEL
194 #endif
196 struct {
197 int sock;
198 int sliceheight;
199 struct pagedim *pagedims;
200 int pagecount;
201 int pagedimcount;
202 pdf_xref *xref;
203 fz_context *ctx;
204 fz_glyph_cache *cache;
205 int w, h;
207 int texindex;
208 int texcount;
209 GLuint *texids;
211 GLenum texiform;
212 GLenum texform;
213 GLenum texty;
215 fz_colorspace *colorspace;
217 struct {
218 int w, h;
219 struct slice *slice;
220 } *texowners;
222 int rotate;
223 int proportional;
224 int trimmargins;
225 int needoutline;
226 int gen;
227 int aalevel;
229 int trimanew;
230 fz_bbox trimfuzz;
231 fz_pixmap *pig;
233 #ifdef _WIN32
234 HANDLE thread;
235 #else
236 pthread_t thread;
237 #endif
238 FILE *xselpipe;
240 FT_Face face;
241 } state;
243 static void UNUSED debug_rect (const char *cap, fz_rect r)
245 printf ("%s(rect) %.2f,%.2f,%.2f,%.2f\n", cap, r.x0, r.y0, r.x1, r.y1);
248 static void UNUSED debug_bbox (const char *cap, fz_bbox r)
250 printf ("%s(bbox) %d,%d,%d,%d\n", cap, r.x0, r.y0, r.x1, r.y1);
253 static void UNUSED debug_matrix (const char *cap, fz_matrix m)
255 printf ("%s(matrix) %.2f,%.2f,%.2f,%.2f %.2f %.2f\n", cap,
256 m.a, m.b, m.c, m.d, m.e, m.f);
259 #ifdef _WIN32
260 static CRITICAL_SECTION critsec;
262 static void lock (void *unused)
264 (void) unused;
265 EnterCriticalSection (&critsec);
268 static void unlock (void *unused)
270 (void) unused;
271 LeaveCriticalSection (&critsec);
274 static int trylock (void *unused)
276 return TryEnterCriticalSection (&critsec) == 0;
278 #else
279 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
281 static void lock (const char *cap)
283 int ret = pthread_mutex_lock (&mutex);
284 if (ret) {
285 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
289 static void unlock (const char *cap)
291 int ret = pthread_mutex_unlock (&mutex);
292 if (ret) {
293 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
297 static int trylock (const char *cap)
299 int ret = pthread_mutex_trylock (&mutex);
301 if (ret && ret != EBUSY) {
302 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
304 return ret == EBUSY;
306 #endif
308 static void *parse_pointer (const char *cap, const char *s)
310 int ret;
311 void *ptr;
313 ret = sscanf (s, "%" FMT_ptr, FMT_ptr_cast (&ptr));
314 if (ret != 1) {
315 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
317 return ptr;
320 static int hasdata (int sock)
322 int ret;
323 fionread_arg avail;
324 ret = ioctlsocket (sock, FIONREAD, &avail);
325 if (ret) sockerr (1, "hasdata: FIONREAD error ret=%d", ret);
326 return avail > 0;
329 static double now (void)
331 #ifdef _WIN32
332 FILETIME ft;
333 uint64 tmp;
335 GetSystemTimeAsFileTime (&ft);
336 tmp = ft.dwHighDateTime;
337 tmp <<= 32;
338 tmp |= ft.dwLowDateTime;
339 return tmp * 1e-7;
340 #else
341 struct timeval tv;
343 if (gettimeofday (&tv, NULL)) {
344 err (1, "gettimeofday");
346 return tv.tv_sec + tv.tv_usec*1e-6;
347 #endif
350 static void readdata (int fd, char *p, int size)
352 ssize_t n;
354 n = recv (fd, p, size, 0);
355 if (n - size) {
356 if (!n) errx (1, "EOF while reading");
357 sockerr (1, "recv (req %d, ret " FMT_ss ")", size, n);
361 static void writedata (int fd, char *p, int size)
363 char buf[4];
364 ssize_t n;
366 buf[0] = (size >> 24) & 0xff;
367 buf[1] = (size >> 16) & 0xff;
368 buf[2] = (size >> 8) & 0xff;
369 buf[3] = (size >> 0) & 0xff;
371 n = send (fd, buf, 4, 0);
372 if (n != 4) {
373 if (!n) errx (1, "EOF while writing length");
374 sockerr (1, "send " FMT_ss, n);
377 n = send (fd, p, size, 0);
378 if (n - size) {
379 if (!n) errx (1, "EOF while writing data");
380 sockerr (1, "send (req %d, ret " FMT_ss ")", size, n);
384 static void
385 #ifdef __GNUC__
386 __attribute__ ((format (printf, 2, 3)))
387 #endif
388 printd (int fd, const char *fmt, ...)
390 int size = 200, len;
391 va_list ap;
392 char *buf;
394 buf = malloc (size);
395 for (;;) {
396 if (!buf) err (errno, "malloc for temp buf (%d bytes) failed", size);
398 va_start (ap, fmt);
399 len = vsnprintf (buf, size, fmt, ap);
400 va_end (ap);
402 if (len > -1 && len < size) {
403 writedata (fd, buf, len);
404 break;
407 if (len > -1) {
408 size = len + 1;
410 else {
411 size *= 2;
413 buf = realloc (buf, size);
415 free (buf);
418 static void openxref (char *filename, char *password)
420 int i;
422 for (i = 0; i < state.texcount; ++i) {
423 state.texowners[i].w = -1;
424 state.texowners[i].slice = NULL;
427 if (state.xref) {
428 pdf_free_xref (state.xref);
429 state.xref = NULL;
432 if (state.pagedims) {
433 free (state.pagedims);
434 state.pagedims = NULL;
436 state.pagedimcount = 0;
438 fz_set_aa_level (state.ctx, state.aalevel);
439 state.xref = pdf_open_xref (state.ctx, filename);
440 if (pdf_needs_password (state.xref)) {
441 int okay = pdf_authenticate_password (state.xref, password);
442 if (!okay) {
443 errx (1, "invalid password");
446 state.pagecount = pdf_count_pages (state.xref);
449 static void pdfinfo (void)
451 fz_obj *infoobj;
453 printd (state.sock, "info PDF version\t%d.%d",
454 state.xref->version / 10, state.xref->version % 10);
456 infoobj = fz_dict_gets (state.xref->trailer, "Info");
457 if (infoobj) {
458 int i;
459 char *s;
460 char *items[] = { "Title", "Author", "Creator",
461 "Producer", "CreationDate" };
463 for (i = 0; i < sizeof (items) / sizeof (*items); ++i) {
464 fz_obj *obj = fz_dict_gets (infoobj, items[i]);
465 s = pdf_to_utf8 (state.ctx, obj);
466 if (*s) {
467 if (i == 0) {
468 printd (state.sock, "title %s", s);
470 printd (state.sock, "info %s\t%s", items[i], s);
472 fz_free (state.ctx, s);
475 printd (state.sock, "info Pages\t%d", state.pagecount);
476 printd (state.sock, "info Dimensions\t%d", state.pagedimcount);
477 printd (state.sock, "infoend");
480 static int readlen (int fd)
482 ssize_t n;
483 unsigned char p[4];
485 n = recv (fd, p, 4, 0);
486 if (n != 4) {
487 if (!n) errx (1, "EOF while reading length");
488 sockerr (1, "recv " FMT_ss, n);
491 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
494 static void unlinktile (struct tile *tile)
496 int i;
498 for (i = 0; i < tile->slicecount; ++i) {
499 struct slice *s = &tile->slices[i];
501 if (s->texindex != -1) {
502 if (state.texowners[s->texindex].slice == s) {
503 state.texowners[s->texindex].slice = NULL;
509 static void freepage (struct page *page)
511 if (page->text) {
512 fz_free_text_span (state.ctx, page->text);
514 pdf_free_page (state.ctx, page->drawpage);
515 fz_free_display_list (state.ctx, page->dlist);
516 free (page);
519 static void freetile (struct tile *tile)
521 unlinktile (tile);
522 #ifndef PIGGYBACK
523 fz_drop_pixmap (state.ctx, tile->pixmap);
524 #else
525 if (state.pig) {
526 fz_drop_pixmap (state.ctx, state.pig);
528 state.pig = tile->pixmap;
529 #endif
530 free (tile);
533 #ifdef __ALTIVEC__
534 #include <altivec.h>
536 static int cacheline32bytes;
537 extern char **environ;
539 static void __attribute__ ((constructor)) clcheck (void)
541 char **envp = environ;
542 unsigned long *auxv;
544 while (*envp++);
546 for (auxv = (unsigned long *) envp; *auxv != 0; auxv += 2) {
547 if (*auxv == 19) {
548 cacheline32bytes = auxv[1] == 32;
549 return;
554 static void OPTIMIZE (3) clearpixmap (fz_pixmap *pixmap)
556 if (cacheline32bytes) {
557 intptr_t a1, a2, diff;
558 size_t sizea, i, size = pixmap->w * pixmap->h * pixmap->n;
559 vector unsigned char v = vec_splat_u8 (-1);
560 vector unsigned char *p;
562 a1 = a2 = (intptr_t) pixmap->samples;
563 a2 = (a1 + 31) & ~31;
564 diff = a2 - a1;
565 sizea = size - diff;
566 p = (void *) a2;
568 while (a1 != a2) *(char *) a1++ = 0xff;
569 for (i = 0; i < (sizea & ~31); i += 32) {
570 __asm volatile ("dcbz %0, %1"::"b"(a2),"r"(i));
571 vec_st (v, i, p);
572 vec_st (v, i + 16, p);
574 while (i < sizea) *((char *) a1 + i++) = 0xff;
576 else fz_clear_pixmap_with_color (pixmap, 0xff);
578 #else
579 #define clearpixmap(p) fz_clear_pixmap_with_color (p, 0xff)
580 #endif
582 static fz_matrix trimctm (pdf_page *page, int pindex)
584 fz_matrix ctm;
585 struct pagedim *pdim = &state.pagedims[pindex];
587 if (!pdim->tctmready) {
588 if (state.trimmargins) {
589 fz_rect realbox;
591 ctm = fz_concat (fz_rotate (-pdim->rotate), fz_scale (1, -1));
592 realbox = fz_transform_rect (ctm, pdim->mediabox);
593 ctm = fz_concat (ctm, fz_translate (-realbox.x0, -realbox.y0));
594 ctm = fz_concat (fz_invert_matrix (page->ctm), ctm);
596 else {
597 ctm = fz_identity;
599 pdim->tctm = ctm;
600 pdim->tctmready = 1;
602 return pdim->tctm;
605 static fz_matrix pagectm (struct page *page)
607 return fz_concat (trimctm (page->drawpage, page->pdimno),
608 state.pagedims[page->pdimno].ctm);
611 static void *loadpage (int pageno, int pindex)
613 fz_device *dev;
614 struct page *page = NULL;
616 page = calloc (sizeof (struct page), 1);
617 if (!page) {
618 err (1, "calloc page %d", pageno);
621 page->drawpage = pdf_load_page (state.xref, pageno);
622 page->dlist = fz_new_display_list (state.ctx);
623 dev = fz_new_list_device (state.ctx, page->dlist);
624 pdf_run_page (state.xref, page->drawpage, dev, fz_identity, NULL);
625 fz_free_device (dev);
627 page->pdimno = pindex;
628 page->pageno = pageno;
629 page->gen = state.gen;
631 return page;
634 static struct tile *alloctile (int h)
636 int i;
637 int slicecount;
638 size_t tilesize;
639 struct tile *tile;
641 slicecount = (h + state.sliceheight - 1) / state.sliceheight;
642 tilesize = sizeof (*tile) + ((slicecount - 1) * sizeof (struct slice));
643 tile = calloc (tilesize, 1);
644 if (!tile) {
645 err (1, "can not allocate tile (" FMT_s " bytes)", tilesize);
647 for (i = 0; i < slicecount; ++i) {
648 int sh = MIN (h, state.sliceheight);
649 tile->slices[i].h = sh;
650 tile->slices[i].texindex = -1;
651 h -= sh;
653 tile->slicecount = slicecount;
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.xref->page_objs[pageno];
715 if (state.trimmargins) {
716 fz_obj *obj;
717 pdf_page *page;
719 page = pdf_load_page (state.xref, 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.xref, 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.xref);
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.xref, pageno);
988 text = fz_new_text_span (state.ctx);
989 tdev = fz_new_text_device (state.ctx, text);
990 pdf_run_page (state.xref, 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
1149 #ifdef _WIN32
1150 DWORD _stdcall
1151 #else
1152 void *
1153 #endif
1154 mainloop (void *unused)
1156 char *p = NULL;
1157 int len, ret, oldlen = 0;
1159 for (;;) {
1160 len = readlen (state.sock);
1161 if (len == 0) {
1162 errx (1, "readlen returned 0");
1165 if (oldlen < len + 1) {
1166 p = realloc (p, len + 1);
1167 if (!p) {
1168 err (1, "realloc %d failed", len + 1);
1170 oldlen = len + 1;
1172 readdata (state.sock, p, len);
1173 p[len] = 0;
1175 if (!strncmp ("open", p, 4)) {
1176 size_t filenamelen;
1177 char *password;
1178 char *filename = p + 5;
1180 filenamelen = strlen (filename);
1181 password = filename + filenamelen + 1;
1183 openxref (filename, password);
1184 printd (state.sock, "msg Opened %s (press h/F1 to get help)",
1185 filename);
1186 initpdims ();
1187 pdfinfo ();
1188 state.needoutline = 1;
1190 else if (!strncmp ("cs", p, 2)) {
1191 int i, colorspace;
1193 ret = sscanf (p + 2, " %d", &colorspace);
1194 if (ret != 1) {
1195 errx (1, "malformed aa `%.*s' ret=%d", len, p, ret);
1197 lock ("cs");
1198 set_tex_params (colorspace);
1199 for (i = 0; i < state.texcount; ++i) {
1200 state.texowners[i].w = -1;
1201 state.texowners[i].slice = NULL;
1203 unlock ("cs");
1205 else if (!strncmp ("freepage", p, 8)) {
1206 void *ptr;
1208 ret = sscanf (p + 8, " %" FMT_ptr, FMT_ptr_cast (&ptr));
1209 if (ret != 1) {
1210 errx (1, "malformed freepage `%.*s' ret=%d", len, p, ret);
1212 freepage (ptr);
1214 else if (!strncmp ("freetile", p, 8)) {
1215 void *ptr;
1217 ret = sscanf (p + 8, " %" FMT_ptr, FMT_ptr_cast (&ptr));
1218 if (ret != 1) {
1219 errx (1, "malformed freetile `%.*s' ret=%d", len, p, ret);
1221 freetile (ptr);
1223 else if (!strncmp ("search", p, 6)) {
1224 int icase, pageno, y, ret, len2, forward;
1225 char *pattern;
1226 regex_t re;
1228 ret = sscanf (p + 6, " %d %d %d %d,%n",
1229 &icase, &pageno, &y, &forward, &len2);
1230 if (ret != 4) {
1231 errx (1, "malformed search `%s' ret=%d", p, ret);
1234 pattern = p + 6 + len2;
1235 ret = regcomp (&re, pattern,
1236 REG_EXTENDED | (icase ? REG_ICASE : 0));
1237 if (ret) {
1238 char errbuf[80];
1239 size_t size;
1241 size = regerror (ret, &re, errbuf, sizeof (errbuf));
1242 printd (state.sock, "msg regcomp failed `%.*s'",
1243 (int) size, errbuf);
1245 else {
1246 search (&re, pageno, y, forward);
1247 regfree (&re);
1250 else if (!strncmp ("geometry", p, 8)) {
1251 int w, h;
1253 printd (state.sock, "clear");
1254 ret = sscanf (p + 8, " %d %d", &w, &h);
1255 if (ret != 2) {
1256 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
1259 lock ("geometry");
1260 state.h = h;
1261 if (w != state.w) {
1262 int i;
1263 state.w = w;
1264 for (i = 0; i < state.texcount; ++i) {
1265 state.texowners[i].slice = NULL;
1268 layout ();
1269 process_outline ();
1270 state.gen++;
1271 unlock ("geometry");
1272 printd (state.sock, "continue %d", state.pagecount);
1274 else if (!strncmp ("reqlayout", p, 9)) {
1275 int rotate, proportional;
1277 printd (state.sock, "clear");
1278 ret = sscanf (p + 9, " %d %d", &rotate, &proportional);
1279 if (ret != 2) {
1280 errx (1, "bad reqlayout line `%.*s' ret=%d", len, p, ret);
1282 lock ("reqlayout");
1283 state.rotate = rotate;
1284 state.proportional = proportional;
1285 layout ();
1286 unlock ("reqlayout");
1287 printd (state.sock, "continue %d", state.pagecount);
1289 else if (!strncmp ("page", p, 4)) {
1290 double a, b;
1291 struct page *page;
1292 int pageno, pindex, ret;
1294 ret = sscanf (p + 4, " %d %d", &pageno, &pindex);
1295 if (ret != 2) {
1296 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
1299 lock ("page");
1300 a = now ();
1301 page = loadpage (pageno, pindex);
1302 b = now ();
1303 unlock ("page");
1305 printd (state.sock, "page %" FMT_ptr " %f",
1306 FMT_ptr_cast2 (page), b - a);
1308 else if (!strncmp ("tile", p, 4)) {
1309 int x, y, w, h, ret;
1310 struct page *page;
1311 struct tile *tile;
1312 double a, b;
1314 ret = sscanf (p + 4, " %" FMT_ptr " %d %d %d %d",
1315 FMT_ptr_cast (&page), &x, &y, &w, &h);
1316 if (ret != 5) {
1317 errx (1, "bad tile line `%.*s' ret=%d", len, p, ret);
1320 lock ("tile");
1321 a = now ();
1322 tile = rendertile (page, x, y, w, h);
1323 b = now ();
1324 unlock ("tile");
1326 printd (state.sock, "tile %d %d %" FMT_ptr " %u %f",
1327 x, y,
1328 FMT_ptr_cast2 (tile),
1329 tile->w * tile->h * tile->pixmap->n,
1330 b - a);
1332 else if (!strncmp ("settrim", p, 7)) {
1333 int trimmargins;
1334 fz_bbox fuzz;
1336 ret = sscanf (p + 7, " %d %d %d %d %d", &trimmargins,
1337 &fuzz.x0, &fuzz.y0, &fuzz.x1, &fuzz.y1);
1338 if (ret != 5) {
1339 errx (1, "malformed settrim `%.*s' ret=%d", len, p, ret);
1341 printd (state.sock, "clear");
1342 lock ("settrim");
1343 state.trimmargins = trimmargins;
1344 state.needoutline = 1;
1345 if (memcmp (&fuzz, &state.trimfuzz, sizeof (fuzz))) {
1346 state.trimanew = 1;
1347 state.trimfuzz = fuzz;
1349 state.pagedimcount = 0;
1350 free (state.pagedims);
1351 state.pagedims = NULL;
1352 initpdims ();
1353 layout ();
1354 process_outline ();
1355 unlock ("settrim");
1356 printd (state.sock, "continue %d", state.pagecount);
1358 else if (!strncmp ("interrupt", p, 9)) {
1359 printd (state.sock, "vmsg interrupted");
1361 else if (!strncmp ("quit", p, 4)) {
1362 return 0;
1364 else {
1365 errx (1, "unknown command %.*s", len, p);
1368 return 0;
1371 static void showsel (struct page *page, int ox, int oy)
1373 fz_bbox bbox;
1374 fz_text_span *span;
1375 struct mark first, last;
1377 first = page->fmark;
1378 last = page->lmark;
1380 if (!first.span || !last.span) return;
1382 glEnable (GL_BLEND);
1383 glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA);
1384 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1386 ox -= state.pagedims[page->pdimno].bounds.x0;
1387 oy -= state.pagedims[page->pdimno].bounds.y0;
1388 for (span = first.span; span; span = span->next) {
1389 int i, j, k;
1391 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0;
1393 j = 0;
1394 k = span->len - 1;
1396 if (span == page->fmark.span && span == page->lmark.span) {
1397 j = MIN (first.i, last.i);
1398 k = MAX (first.i, last.i);
1400 else if (span == first.span) {
1401 j = first.i;
1403 else if (span == last.span) {
1404 k = last.i;
1407 for (i = j; i <= k; ++i) {
1408 bbox = fz_union_bbox (bbox, span->text[i].bbox);
1410 lprintf ("%d %d %d %d oy=%d ox=%d\n",
1411 bbox.x0,
1412 bbox.y0,
1413 bbox.x1,
1414 bbox.y1,
1415 oy, ox);
1417 glRecti (bbox.x0 + ox, bbox.y0 + oy, bbox.x1 + ox, bbox.y1 + oy);
1419 if (span == last.span) break;
1421 glDisable (GL_BLEND);
1424 static void highlightlinks (struct page *page, int xoff, int yoff)
1426 fz_link *link;
1427 fz_matrix ctm;
1429 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1430 glEnable (GL_LINE_STIPPLE);
1431 glLineStipple (0.5, 0xcccc);
1433 ctm = fz_concat (pagectm (page), fz_translate (xoff, yoff));
1435 glBegin (GL_QUADS);
1436 for (link = page->drawpage->links; link; link = link->next) {
1437 fz_point p1, p2, p3, p4;
1439 p1.x = link->rect.x0;
1440 p1.y = link->rect.y0;
1442 p2.x = link->rect.x1;
1443 p2.y = link->rect.y0;
1445 p3.x = link->rect.x1;
1446 p3.y = link->rect.y1;
1448 p4.x = link->rect.x0;
1449 p4.y = link->rect.y1;
1451 p1 = fz_transform_point (ctm, p1);
1452 p2 = fz_transform_point (ctm, p2);
1453 p3 = fz_transform_point (ctm, p3);
1454 p4 = fz_transform_point (ctm, p4);
1456 switch (link->dest.kind) {
1457 case FZ_LINK_GOTO: glColor3ub (255, 0, 0); break;
1458 case FZ_LINK_URI: glColor3ub (0, 0, 255); break;
1459 default: glColor3ub (0, 0, 0); break;
1462 glVertex2f (p1.x, p1.y);
1463 glVertex2f (p2.x, p2.y);
1464 glVertex2f (p3.x, p3.y);
1465 glVertex2f (p4.x, p4.y);
1467 glEnd ();
1469 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1470 glDisable (GL_LINE_STIPPLE);
1473 static void uploadslice (struct tile *tile, struct slice *slice)
1475 int offset;
1476 struct slice *slice1;
1478 offset = 0;
1479 for (slice1 = tile->slices; slice != slice1; slice1++) {
1480 offset += slice1->h * tile->w * tile->pixmap->n;
1482 if (slice->texindex != -1
1483 && state.texowners[slice->texindex].slice == slice) {
1484 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1486 else {
1487 int subimage = 0;
1488 int texindex = state.texindex++ % state.texcount;
1490 if (state.texowners[texindex].w == tile->w) {
1491 if (state.texowners[texindex].h >= slice->h) {
1492 subimage = 1;
1494 else {
1495 state.texowners[texindex].h = slice->h;
1498 else {
1499 state.texowners[texindex].h = slice->h;
1502 state.texowners[texindex].w = tile->w;
1503 state.texowners[texindex].slice = slice;
1504 slice->texindex = texindex;
1506 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[texindex]);
1507 if (subimage) {
1508 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1512 tile->w,
1513 slice->h,
1514 state.texform,
1515 state.texty,
1516 tile->pixmap->samples+offset
1519 else {
1520 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1522 state.texiform,
1523 tile->w,
1524 slice->h,
1526 state.texform,
1527 state.texty,
1528 tile->pixmap->samples+offset
1534 CAMLprim value ml_drawtile (value args_v, value ptr_v)
1536 CAMLparam2 (args_v, ptr_v);
1537 int dispx = Int_val (Field (args_v, 0));
1538 int dispy = Int_val (Field (args_v, 1));
1539 int dispw = Int_val (Field (args_v, 2));
1540 int disph = Int_val (Field (args_v, 3));
1541 int tilex = Int_val (Field (args_v, 4));
1542 int tiley = Int_val (Field (args_v, 5));
1543 char *s = String_val (ptr_v);
1544 struct tile *tile = parse_pointer ("ml_drawtile", s);
1546 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1548 int slicey, firstslice;
1549 struct slice *slice;
1551 firstslice = tiley / state.sliceheight;
1552 slice = &tile->slices[firstslice];
1553 slicey = tiley % state.sliceheight;
1555 while (disph > 0) {
1556 int dh;
1558 dh = slice->h - slicey;
1559 dh = MIN (disph, dh);
1560 uploadslice (tile, slice);
1562 glBegin (GL_QUADS);
1564 glTexCoord2i (tilex, slicey);
1565 glVertex2i (dispx, dispy);
1567 glTexCoord2i (tilex+dispw, slicey);
1568 glVertex2i (dispx+dispw, dispy);
1570 glTexCoord2i (tilex+dispw, slicey+dh);
1571 glVertex2i (dispx+dispw, dispy+dh);
1573 glTexCoord2i (tilex, slicey+dh);
1574 glVertex2i (dispx, dispy+dh);
1576 glEnd ();
1578 dispy += dh;
1579 disph -= dh;
1580 slice++;
1581 ARSERT (!(slice - tile->slices >= tile->slicecount && disph > 0));
1582 slicey = 0;
1585 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1586 CAMLreturn (Val_unit);
1589 CAMLprim value ml_postprocess (value ptr_v, value hlinks_v,
1590 value xoff_v, value yoff_v)
1592 CAMLparam4 (ptr_v, hlinks_v, xoff_v, yoff_v);
1593 int xoff = Int_val (xoff_v);
1594 int yoff = Int_val (yoff_v);
1595 char *s = String_val (ptr_v);
1596 struct page *page = parse_pointer ("ml_postprocess", s);
1598 if (Bool_val (hlinks_v)) highlightlinks (page, xoff, yoff);
1600 if (trylock ("ml_postprocess")) {
1601 goto done;
1603 showsel (page, xoff, yoff);
1604 unlock ("ml_postprocess");
1606 done:
1607 CAMLreturn (Val_unit);
1610 static fz_link *getlink (struct page *page, int x, int y)
1612 fz_point p;
1613 fz_matrix ctm;
1614 fz_link *link;
1616 p.x = x;
1617 p.y = y;
1619 ctm = fz_concat (trimctm (page->drawpage, page->pdimno),
1620 state.pagedims[page->pdimno].ctm);
1621 ctm = fz_invert_matrix (ctm);
1622 p = fz_transform_point (ctm, p);
1624 for (link = page->drawpage->links; link; link = link->next) {
1625 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1626 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1627 return link;
1631 return NULL;
1634 static void droptext (struct page *page)
1636 if (page->text) {
1637 fz_free_text_span (state.ctx, page->text);
1638 page->fmark.i = -1;
1639 page->lmark.i = -1;
1640 page->fmark.span = NULL;
1641 page->lmark.span = NULL;
1642 page->text = NULL;
1646 static void ensuretext (struct page *page)
1648 if (state.gen != page->gen) {
1649 droptext (page);
1650 page->gen = state.gen;
1652 if (!page->text) {
1653 fz_device *tdev;
1655 page->text = fz_new_text_span (state.ctx);
1656 tdev = fz_new_text_device (state.ctx, page->text);
1657 fz_execute_display_list (page->dlist,
1658 tdev,
1659 pagectm (page),
1660 fz_infinite_bbox, NULL);
1661 fz_free_device (tdev);
1665 CAMLprim value ml_whatsunder (value ptr_v, value x_v, value y_v)
1667 CAMLparam3 (ptr_v, x_v, y_v);
1668 CAMLlocal3 (ret_v, tup_v, str_v);
1669 fz_link *link;
1670 struct page *page;
1671 char *s = String_val (ptr_v);
1672 int x = Int_val (x_v), y = Int_val (y_v);
1673 struct pagedim *pdim;
1675 ret_v = Val_int (0);
1676 if (trylock ("ml_whatsunder")) {
1677 goto done;
1680 page = parse_pointer ("ml_whatsunder", s);
1681 pdim = &state.pagedims[page->pdimno];
1682 x += pdim->bounds.x0;
1683 y += pdim->bounds.y0;
1684 link = getlink (page, x, y);
1685 if (link) {
1686 switch (link->dest.kind) {
1687 case FZ_LINK_GOTO:
1689 int pageno;
1690 fz_point p;
1692 pageno = link->dest.ld.gotor.page;
1693 p.x = 0;
1694 p.y = 0;
1696 if (link->dest.ld.gotor.flags & fz_link_flag_t_valid) {
1697 p.y = link->dest.ld.gotor.lt.y;
1698 p = fz_transform_point (pdim->lctm, p);
1700 tup_v = caml_alloc_tuple (2);
1701 ret_v = caml_alloc_small (1, 1);
1702 Field (tup_v, 0) = Val_int (pageno);
1703 Field (tup_v, 1) = Val_int (p.y);
1704 Field (ret_v, 0) = tup_v;
1706 break;
1708 case FZ_LINK_URI:
1709 str_v = caml_copy_string (link->dest.ld.uri.uri);
1710 ret_v = caml_alloc_small (1, 0);
1711 Field (ret_v, 0) = str_v;
1712 break;
1714 default:
1715 printd (state.sock, "msg unhandled link kind %d", link->dest.kind);
1716 break;
1719 else {
1720 int i;
1721 fz_text_span *span;
1723 ensuretext (page);
1724 for (span = page->text; span; span = span->next) {
1725 for (i = 0; i < span->len; ++i) {
1726 fz_bbox *b;
1727 b = &span->text[i].bbox;
1728 if ((x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1)) {
1729 const char *n2 =
1730 span->font && span->font->name
1731 ? span->font->name
1732 : "Span has no font name"
1734 FT_FaceRec *face = span->font->ft_face;
1735 if (face && face->family_name) {
1736 char *s;
1737 char *n1 = face->family_name;
1738 size_t l1 = strlen (n1);
1739 size_t l2 = strlen (n2);
1741 if (l1 != l2 || memcmp (n1, n2, l1)) {
1742 s = malloc (l1 + l2 + 2);
1743 if (s) {
1744 memcpy (s, n2, l2);
1745 s[l2] = '=';
1746 memcpy (s + l2 + 1, n1, l1 + 1);
1747 str_v = caml_copy_string (s);
1748 free (s);
1752 if (str_v == 0) {
1753 str_v = caml_copy_string (n2);
1755 ret_v = caml_alloc_small (1, 2);
1756 Field (ret_v, 0) = str_v;
1757 goto unlock;
1762 unlock:
1763 unlock ("ml_whatsunder");
1765 done:
1766 CAMLreturn (ret_v);
1769 CAMLprim value ml_seltext (value ptr_v, value rect_v)
1771 CAMLparam2 (ptr_v, rect_v);
1772 fz_bbox *b;
1773 struct page *page;
1774 fz_text_span *span;
1775 struct mark first, last;
1776 int i, x0, x1, y0, y1;
1777 struct pagedim *pdim;
1778 char *s = String_val (ptr_v);
1780 if (trylock ("ml_seltext")) {
1781 goto done;
1784 page = parse_pointer ("ml_seltext", s);
1785 ensuretext (page);
1787 pdim = &state.pagedims[page->pdimno];
1789 x0 = Int_val (Field (rect_v, 0)) + pdim->bounds.x0;
1790 y0 = Int_val (Field (rect_v, 1)) + pdim->bounds.y0;
1791 x1 = Int_val (Field (rect_v, 2)) + pdim->bounds.x0;
1792 y1 = Int_val (Field (rect_v, 3)) + pdim->bounds.y0;
1794 if (0) {
1795 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1796 glColor3ub (128, 128, 128);
1797 glRecti (x0, y0, x1, y1);
1798 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1801 first.span = NULL;
1802 last.span = NULL;
1804 last.i = first.i = 0;
1805 first.span = page->text;
1806 for (span = page->text; span; span = span->next) {
1807 for (i = 0; i < span->len; ++i) {
1808 b = &span->text[i].bbox;
1809 int selected = 0;
1811 if (x0 >= b->x0 && x0 <= b->x1 && y0 >= b->y0 && y0 <= b->y1) {
1812 first.i = i;
1813 first.span = span;
1814 selected = 1;
1816 if (x1 >= b->x0 && x1 <= b->x1 && y1 >= b->y0 && y1 <= b->y1) {
1817 last.i = i;
1818 last.span = span;
1819 selected = 1;
1821 if (0 && selected) {
1822 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1823 glColor3ub (128, 128, 128);
1824 glRecti (b->x0, b->y0, b->x1, b->y1);
1825 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1830 if (y1 < y0 || x1 < x0) {
1831 int swap = 0;
1833 if (first.span == last.span) {
1834 swap = 1;
1836 else {
1837 if (y1 < y0) {
1838 for (span = first.span; span && span != last.span;
1839 span = span->next) {
1840 if (span->eol) {
1841 swap = 1;
1842 break;
1848 if (swap) {
1849 i = first.i;
1850 span = first.span;
1851 first.i = last.i;
1852 first.span = last.span;
1853 last.i = i;
1854 last.span = span;
1858 page->fmark = first;
1859 page->lmark = last;
1861 unlock ("ml_seltext");
1863 done:
1864 CAMLreturn (Val_unit);
1867 static int pipespan (FILE *f, fz_text_span *span, int a, int b)
1869 char buf[4];
1870 int i, len, ret;
1872 for (i = a; i <= b; ++i) {
1873 len = runetochar (buf, &span->text[i].c);
1874 ret = fwrite (buf, len, 1, f);
1876 if (ret != 1) {
1877 printd (state.sock, "msg failed to write %d bytes ret=%d: %s",
1878 len, ret, strerror (errno));
1879 return -1;
1882 return 0;
1885 CAMLprim value ml_copysel (value ptr_v)
1887 CAMLparam1 (ptr_v);
1888 FILE *f;
1889 struct page *page;
1890 char *s = String_val (ptr_v);
1892 if (trylock ("ml_copysel")) {
1893 goto done;
1896 if (!*s) {
1897 close:
1898 #ifdef USE_XSEL
1899 if (state.xselpipe) {
1900 int ret = pclose (state.xselpipe);
1901 if (ret) {
1902 printd (state.sock, "msg failed to close xsel pipe `%s'",
1903 strerror (errno));
1905 state.xselpipe = NULL;
1907 #else
1908 printf ("========================================\n");
1909 #endif
1911 else {
1912 fz_text_span *span;
1914 page = parse_pointer ("ml_sopysel", s);
1916 if (!page->fmark.span || !page->lmark.span) {
1917 printd (state.sock, "msg nothing to copy");
1918 goto unlock;
1921 f = stdout;
1922 #ifdef USE_XSEL
1923 if (!state.xselpipe) {
1924 state.xselpipe = popen ("xsel -i", "w");
1925 if (!state.xselpipe) {
1926 printd (state.sock, "msg failed to open xsel pipe `%s'",
1927 strerror (errno));
1929 else {
1930 f = state.xselpipe;
1933 else {
1934 f = state.xselpipe;
1936 #endif
1938 for (span = page->fmark.span;
1939 span && span != page->lmark.span->next;
1940 span = span->next) {
1941 int a = span == page->fmark.span ? page->fmark.i : 0;
1942 int b = span == page->lmark.span ? page->lmark.i : span->len - 1;
1943 if (pipespan (f, span, a, b)) {
1944 goto close;
1946 if (span->eol) {
1947 if (putc ('\n', f) == EOF) {
1948 printd (state.sock,
1949 "msg failed break line on xsel pipe `%s'",
1950 strerror (errno));
1951 goto close;
1955 page->lmark.span = NULL;
1956 page->fmark.span = NULL;
1959 unlock:
1960 unlock ("ml_copysel");
1962 done:
1963 CAMLreturn (Val_unit);
1966 CAMLprim value ml_getpdimrect (value pagedimno_v)
1968 CAMLparam1 (pagedimno_v);
1969 CAMLlocal1 (ret_v);
1970 int pagedimno = Int_val (pagedimno_v);
1971 fz_rect box;
1973 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1974 if (trylock ("ml_getpdimrect")) {
1975 box = fz_empty_rect;
1977 else {
1978 box = state.pagedims[pagedimno].mediabox;
1979 unlock ("ml_getpdimrect");
1982 Store_double_field (ret_v, 0, box.x0);
1983 Store_double_field (ret_v, 1, box.x1);
1984 Store_double_field (ret_v, 2, box.y0);
1985 Store_double_field (ret_v, 3, box.y1);
1987 CAMLreturn (ret_v);
1990 static double getmaxw (void)
1992 int i;
1993 struct pagedim *p;
1994 double maxw = 0.0;
1996 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
1997 double x0, x1, w;
1999 x0 = MIN (p->mediabox.x0, p->mediabox.x1);
2000 x1 = MAX (p->mediabox.x0, p->mediabox.x1);
2002 w = x1 - x0;
2003 maxw = MAX (w, maxw);
2005 return maxw;
2008 CAMLprim value ml_getmaxw (value unit_v)
2010 CAMLparam1 (unit_v);
2011 CAMLlocal1 (ret_v);
2012 double maxw = 0.0;
2014 if (trylock ("ml_getmaxw")) {
2015 goto done;
2017 maxw = getmaxw ();
2018 unlock ("ml_getmaxw");
2019 done:
2020 ret_v = caml_copy_double (maxw);
2021 CAMLreturn (ret_v);
2024 CAMLprim value ml_zoom_for_height (value winw_v, value winh_v, value dw_v)
2026 CAMLparam3 (winw_v, winh_v, dw_v);
2027 CAMLlocal1 (ret_v);
2028 int i;
2029 double zoom = 1.0;
2030 double maxw = 0.0, maxh = 0.0;
2031 struct pagedim *p;
2032 double winw = Int_val (winw_v);
2033 double winh = Int_val (winh_v);
2034 double dw = Int_val (dw_v);
2035 double pw = 1.0, ph = 1.0, num, den;
2037 if (trylock ("ml_zoom_for_height")) {
2038 goto done;
2041 if (state.proportional) {
2042 maxw = getmaxw ();
2045 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
2046 double x0, x1, y0, y1, w, h, scaledh, scale;
2048 x0 = MIN (p->mediabox.x0, p->mediabox.x1);
2049 x1 = MAX (p->mediabox.x0, p->mediabox.x1);
2050 y0 = MIN (p->mediabox.y0, p->mediabox.y1);
2051 y1 = MAX (p->mediabox.y0, p->mediabox.y1);
2053 w = x1 - x0;
2054 h = y1 - y0;
2056 if (state.proportional) {
2057 scale = w / maxw;
2058 scaledh = h * scale;
2060 else {
2061 scale = 1.0;
2062 scaledh = h;
2065 if (scaledh > maxh) {
2066 maxh = scaledh;
2067 ph = scaledh;
2068 pw = w * scale;
2072 num = (winh * pw) + (ph * dw);
2073 den = ph * winw;
2074 zoom = num / den;
2076 unlock ("ml_zoom_for_height");
2077 done:
2078 ret_v = caml_copy_double (zoom);
2079 CAMLreturn (ret_v);
2082 #include "glfont.c"
2084 CAMLprim value ml_draw_string (value pt_v, value x_v, value y_v, value string_v)
2086 CAMLparam4 (pt_v, x_v, y_v, string_v);
2087 CAMLlocal1 (ret_v);
2088 int pt = Int_val(pt_v);
2089 int x = Int_val (x_v);
2090 int y = Int_val (y_v);
2091 double w;
2093 w = draw_string (state.face, pt, x, y, String_val (string_v));
2094 ret_v = caml_copy_double (w);
2095 CAMLreturn (ret_v);
2098 CAMLprim value ml_measure_string (value pt_v, value string_v)
2100 CAMLparam2 (pt_v, string_v);
2101 CAMLlocal1 (ret_v);
2102 int pt = Int_val (pt_v);
2103 double w;
2105 w = measure_string (state.face, pt, String_val (string_v));
2106 ret_v = caml_copy_double (w);
2107 CAMLreturn (ret_v);
2110 CAMLprim value ml_getpagebox (value opaque_v)
2112 CAMLparam1 (opaque_v);
2113 CAMLlocal1 (ret_v);
2114 fz_bbox bbox;
2115 fz_device *dev;
2116 char *s = String_val (opaque_v);
2117 struct page *page = parse_pointer ("ml_getpagebox", s);
2119 ret_v = caml_alloc_tuple (4);
2120 dev = fz_new_bbox_device (state.ctx, &bbox);
2121 dev->hints |= FZ_IGNORE_SHADE;
2122 pdf_run_page (state.xref, page->drawpage, dev, pagectm (page), NULL);
2123 fz_free_device (dev);
2125 Field (ret_v, 0) = Val_int (bbox.x0);
2126 Field (ret_v, 1) = Val_int (bbox.y0);
2127 Field (ret_v, 2) = Val_int (bbox.x1);
2128 Field (ret_v, 3) = Val_int (bbox.y1);
2130 CAMLreturn (ret_v);
2133 CAMLprim value ml_setaalevel (value level_v)
2135 CAMLparam1 (level_v);
2137 state.aalevel = Int_val (level_v);
2138 CAMLreturn (Val_unit);
2141 #if !defined _WIN32 && !defined __APPLE__
2142 #undef pixel
2143 #include <X11/X.h>
2144 #include <X11/Xlib.h>
2145 #include <X11/Xutil.h>
2146 #include <GL/glx.h>
2148 static void set_wm_class (int hack)
2150 if (hack) {
2151 Display *dpy;
2152 Window win;
2153 XClassHint hint;
2154 char *display;
2156 display = getenv ("DISPLAY");
2157 dpy = XOpenDisplay (display);
2158 if (!dpy) {
2159 fprintf (stderr, "XOpenDisplay `%s' failed\n",
2160 display ? display : "null");
2161 return;
2163 hint.res_name = "llpp";
2164 hint.res_class = "llpp";
2165 win = glXGetCurrentDrawable ();
2166 if (win == None) {
2167 fprintf (stderr, "glXGetCurrentDrawable returned None\n");
2168 XCloseDisplay (dpy);
2169 return;
2171 XSetClassHint (dpy, win, &hint);
2172 XCloseDisplay (dpy);
2175 #else
2176 #define set_wm_class(h) (void) (h)
2177 #endif
2179 enum { piunknown, pilinux, piwindows, piosx,
2180 pisun, pifreebsd, pidragonflybsd,
2181 piopenbsd, pimingw, picygwin };
2183 CAMLprim value ml_platform (value unit_v)
2185 CAMLparam1 (unit_v);
2186 int platid = piunknown;
2188 #if defined __linux__
2189 platid = pilinux;
2190 #elif defined __CYGWIN__
2191 platid = picygwin;
2192 #elif defined __MINGW32__
2193 platid = pimingw;
2194 #elif defined _WIN32
2195 platid = piwindows;
2196 #elif defined __DragonFly__
2197 platid = pidragonflybsd;
2198 #elif defined __FreeBSD__
2199 platid = pifreebsd;
2200 #elif defined __OpenBSD__
2201 platid = piopenbsd;
2202 #elif defined __sun__
2203 platid = pisun;
2204 #elif defined __APPLE__
2205 platid = piosx;
2206 #endif
2207 CAMLreturn (Val_int (platid));
2210 CAMLprim value ml_init (value sock_v, value params_v)
2212 CAMLparam2 (sock_v, params_v);
2213 CAMLlocal2 (trim_v, fuzz_v);
2214 #ifndef _WIN32
2215 int ret;
2216 #endif
2217 char *fontpath;
2218 int wmclasshack;
2219 int colorspace;
2221 state.rotate = Int_val (Field (params_v, 0));
2222 state.proportional = Bool_val (Field (params_v, 1));
2223 trim_v = Field (params_v, 2);
2224 state.texcount = Int_val (Field (params_v, 3));
2225 state.sliceheight = Int_val (Field (params_v, 4));
2226 state.ctx = fz_new_context (&fz_alloc_default, Field (params_v, 5));
2227 colorspace = Int_val (Field (params_v, 6));
2228 wmclasshack = Bool_val (Field (params_v, 7));
2229 fontpath = String_val (Field (params_v, 8));
2231 state.trimmargins = Bool_val (Field (trim_v, 0));
2232 fuzz_v = Field (trim_v, 1);
2233 state.trimfuzz.x0 = Int_val (Field (fuzz_v, 0));
2234 state.trimfuzz.y0 = Int_val (Field (fuzz_v, 1));
2235 state.trimfuzz.x1 = Int_val (Field (fuzz_v, 2));
2236 state.trimfuzz.y1 = Int_val (Field (fuzz_v, 3));
2238 set_tex_params (colorspace);
2239 set_wm_class (wmclasshack);
2241 if (*fontpath) {
2242 state.face = load_font (fontpath);
2244 else {
2245 unsigned int len;
2246 void *base = pdf_find_substitute_font (0, 0, 0, 0, &len);
2248 state.face = load_builtin_font (base, len);
2250 if (!state.face) _exit (1);
2252 fz_accelerate ();
2253 state.texids = calloc (state.texcount, sizeof (*state.texids));
2254 if (!state.texids) {
2255 err (1, "calloc texids " FMT_s,
2256 state.texcount * sizeof (*state.texids));
2259 state.texowners = calloc (state.texcount, sizeof (*state.texowners));
2260 if (!state.texowners) {
2261 err (1, "calloc texowners " FMT_s,
2262 state.texcount * sizeof (*state.texowners));
2265 glGenTextures (state.texcount, state.texids);
2267 #ifdef _WIN32
2268 state.sock = Socket_val (sock_v);
2269 #else
2270 state.sock = Int_val (sock_v);
2271 #endif
2273 #ifdef _WIN32
2274 InitializeCriticalSection (&critsec);
2275 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
2276 if (state.thread == INVALID_HANDLE_VALUE) {
2277 errx (1, "CreateThread failed: %lx", GetLastError ());
2279 #else
2280 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
2281 if (ret) {
2282 errx (1, "pthread_create: %s", strerror (ret));
2284 #endif
2286 CAMLreturn (Val_unit);