Quit cosmetics
[llpp/slowscroll.git] / link.c
blob8db25ab02c03e475bca11395db13975716bf34d9
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 #ifdef _WIN32
8 #define WIN32_LEAN_AND_MEAN
9 #include <windows.h>
10 #include <winsock2.h>
11 #define fionread_arg long
12 #define ssize_t int
13 #define FMT_ss "%d"
14 #ifdef _WIN64
15 #define FMT_s "%i64u"
16 #else
17 #define FMT_s "%u"
18 #endif
19 #pragma warning (disable:4244)
20 #pragma warning (disable:4996)
21 #pragma warning (disable:4995)
22 #endif
24 #ifdef _MSC_VER
25 #define NORETURN __declspec (noreturn)
26 #define UNUSED
27 #define OPTIMIZE
28 #elif defined __GNUC__
29 #define NORETURN __attribute__ ((noreturn))
30 #define UNUSED __attribute__ ((unused))
31 #define OPTIMIZE(n) __attribute__ ((optimize ("O"#n)))
32 #else
33 #define NORETURN
34 #define UNUSED
35 #define OPTIMIZE
36 #endif
38 #ifdef _WIN32
39 static void __declspec (noreturn) sockerr (int exitcode, const char *fmt, ...)
41 va_list ap;
43 va_start (ap, fmt);
44 vfprintf (stderr, fmt, ap);
45 va_end (ap);
46 fprintf (stderr, ": wsaerror 0x%x\n", WSAGetLastError ());
47 _exit (exitcode);
49 #else
50 #define FMT_ss "%zd"
51 #define FMT_s "%zu"
52 #define fionread_arg int
53 #define ioctlsocket ioctl
54 #define sockerr err
55 #include <unistd.h>
56 #endif
58 #include <regex.h>
59 #include <ctype.h>
60 #include <stdarg.h>
61 #include <limits.h>
63 #ifndef _WIN32
64 #include <pthread.h>
65 #include <sys/time.h>
66 #include <sys/types.h>
67 #include <sys/socket.h>
68 #include <sys/ioctl.h>
69 #endif
71 static void NORETURN err (int exitcode, const char *fmt, ...)
73 va_list ap;
74 int savederrno;
76 savederrno = errno;
77 va_start (ap, fmt);
78 vfprintf (stderr, fmt, ap);
79 va_end (ap);
80 fprintf (stderr, ": %s\n", strerror (savederrno));
81 fflush (stderr);
82 _exit (exitcode);
85 static void NORETURN errx (int exitcode, const char *fmt, ...)
87 va_list ap;
89 va_start (ap, fmt);
90 vfprintf (stderr, fmt, ap);
91 va_end (ap);
92 fputc ('\n', stderr);
93 fflush (stderr);
94 _exit (exitcode);
97 #ifdef __APPLE__
98 #include <OpenGL/gl.h>
99 #else
100 #include <GL/gl.h>
101 #endif
103 #ifndef GL_TEXTURE_RECTANGLE_ARB
104 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
105 #endif
107 #include <caml/fail.h>
108 #include <caml/alloc.h>
109 #include <caml/memory.h>
110 #include <caml/unixsupport.h>
112 #include <fitz.h>
113 #include <mupdf.h>
115 #include FT_FREETYPE_H
117 #if 0
118 #define lprintf printf
119 #else
120 #define lprintf(...)
121 #endif
123 #define ARSERT(cond) for (;;) { \
124 if (!(cond)) { \
125 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
127 break; \
130 struct block {
131 int w;
132 int x;
133 int offset;
134 int texindex;
137 struct slice {
138 int h;
139 struct block *blocks;
142 struct pagedim {
143 int pageno;
144 int rotate;
145 int left;
146 fz_rect box;
147 fz_bbox bbox;
148 fz_matrix ctm, ctm1;
151 struct page {
152 int pageno;
153 int slicecount;
154 int blockcount;
155 fz_text_span *text;
156 fz_pixmap *pixmap;
157 pdf_page *drawpage;
158 struct pagedim pagedim;
159 struct mark {
160 int i;
161 fz_text_span *span;
162 } fmark, lmark;
163 struct slice *slices;
166 #if !defined _WIN32 && !defined __APPLE__
167 #define USE_XSEL
168 #endif
170 struct {
171 int sock;
172 int sliceheight, blockwidth;
173 struct page *pig;
174 struct pagedim *pagedims;
175 int pagecount;
176 int pagedimcount;
177 pdf_xref *xref;
178 fz_context *ctx;
179 fz_glyph_cache *cache;
180 int w, h;
182 int texindex;
183 int texcount;
184 GLuint *texids;
186 GLenum texform;
187 GLenum texty;
189 struct {
190 int w, h;
191 struct block *block;
192 } *texowners;
194 int rotate;
195 int proportional;
196 int needoutline;
198 #ifdef _WIN32
199 HANDLE thread;
200 #else
201 pthread_t thread;
202 #endif
203 FILE *xselpipe;
205 FT_Face face;
206 } state;
208 #ifdef _WIN32
209 static CRITICAL_SECTION critsec;
211 static void lock (void *unused)
213 (void) unused;
214 EnterCriticalSection (&critsec);
217 static void unlock (void *unused)
219 (void) unused;
220 LeaveCriticalSection (&critsec);
223 static int trylock (void *unused)
225 return TryEnterCriticalSection (&critsec) == 0;
227 #else
228 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
230 static void lock (const char *cap)
232 int ret = pthread_mutex_lock (&mutex);
233 if (ret) {
234 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
238 static void unlock (const char *cap)
240 int ret = pthread_mutex_unlock (&mutex);
241 if (ret) {
242 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
246 static int trylock (const char *cap)
248 int ret = pthread_mutex_trylock (&mutex);
250 if (ret && ret != EBUSY) {
251 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
253 return ret == EBUSY;
255 #endif
257 static void *parse_pointer (const char *cap, const char *s)
259 int ret;
260 void *ptr;
262 ret = sscanf (s, "%p", &ptr);
263 if (ret != 1) {
264 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
266 return ptr;
269 static int hasdata (int sock)
271 int ret;
272 fionread_arg avail;
273 ret = ioctlsocket (sock, FIONREAD, &avail);
274 if (ret) sockerr (1, "hasdata: FIONREAD error ret=%d", ret);
275 return avail > 0;
278 static double now (void)
280 struct timeval tv;
282 if (gettimeofday (&tv, NULL)) {
283 err (1, "gettimeofday");
285 return tv.tv_sec + tv.tv_usec*1e-6;
288 static void readdata (int fd, char *p, int size)
290 ssize_t n;
292 n = recv (fd, p, size, 0);
293 if (n - size) {
294 if (!n) errx (1, "EOF while reading");
295 sockerr (1, "recv (req %d, ret " FMT_ss ")", size, n);
299 static void writedata (int fd, char *p, int size)
301 char buf[4];
302 ssize_t n;
304 buf[0] = (size >> 24) & 0xff;
305 buf[1] = (size >> 16) & 0xff;
306 buf[2] = (size >> 8) & 0xff;
307 buf[3] = (size >> 0) & 0xff;
309 n = send (fd, buf, 4, 0);
310 if (n != 4) {
311 if (!n) errx (1, "EOF while writing length");
312 sockerr (1, "send " FMT_ss, n);
315 n = send (fd, p, size, 0);
316 if (n - size) {
317 if (!n) errx (1, "EOF while writing data");
318 sockerr (1, "send (req %d, ret " FMT_ss ")", size, n);
322 static void
323 #ifdef __GNUC__
324 __attribute__ ((format (printf, 2, 3)))
325 #endif
326 printd (int fd, const char *fmt, ...)
328 int size = 200, len;
329 va_list ap;
330 char *buf;
332 buf = malloc (size);
333 for (;;) {
334 if (!buf) err (errno, "malloc for temp buf (%d bytes) failed", size);
336 va_start (ap, fmt);
337 len = vsnprintf (buf, size, fmt, ap);
338 va_end (ap);
340 if (len > -1 && len < size) {
341 writedata (fd, buf, len);
342 break;
345 if (len > -1) {
346 size = len + 1;
348 else {
349 size *= 2;
351 buf = realloc (buf, size);
353 free (buf);
356 static void openxref (char *filename, char *password)
358 int i;
360 for (i = 0; i < state.texcount; ++i) {
361 state.texowners[i].block = NULL;
364 if (state.cache) {
365 fz_free_glyph_cache (state.ctx, state.cache);
368 state.cache = fz_new_glyph_cache (state.ctx);
369 if (!state.cache) {
370 errx (1, "fz_newglyph_cache failed");
373 if (state.xref) {
374 pdf_free_xref (state.xref);
375 state.xref = NULL;
378 if (state.pagedims) {
379 free (state.pagedims);
380 state.pagedims = NULL;
382 state.pagedimcount = 0;
384 state.xref = pdf_open_xref (state.ctx, filename, password);
385 pdf_load_page_tree (state.xref);
387 state.pagecount = pdf_count_pages (state.xref);
390 static void pdfinfo (void)
392 fz_obj *infoobj;
394 printd (state.sock, "i PDF version\t%d.%d",
395 state.xref->version / 10, state.xref->version % 10);
397 infoobj = fz_dict_gets (state.xref->trailer, "Info");
398 if (infoobj) {
399 int i;
400 char *s;
401 char *items[] = { "Title", "Creator", "Producer", "CreationDate" };
403 for (i = 0; i < sizeof (items) / sizeof (*items); ++i) {
404 fz_obj *obj = fz_dict_gets (infoobj, items[i]);
405 s = pdf_to_utf8 (state.ctx, obj);
406 if (*s) {
407 if (i == 0) {
408 printd (state.sock, "t %s", s);
410 printd (state.sock, "i %s\t%s", items[i], s);
412 fz_free (state.ctx, s);
414 printd (state.sock, "ie");
418 static int readlen (int fd)
420 ssize_t n;
421 unsigned char p[4];
423 n = recv (fd, p, 4, 0);
424 if (n != 4) {
425 if (!n) errx (1, "EOF while reading length");
426 sockerr (1, "recv " FMT_ss, n);
429 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
432 static void unlinkpage (struct page *page)
434 int i;
436 for (i = 0; i < page->slicecount; ++i) {
437 int j;
438 struct slice *s = &page->slices[i];
440 for (j = 0; j < page->blockcount; ++j) {
441 struct block *b = &s->blocks[j];
443 if (b->texindex != -1) {
444 if (state.texowners[b->texindex].block == b) {
445 state.texowners[b->texindex].block = NULL;
452 static void freepage (struct page *page)
454 int i;
456 fz_drop_pixmap (state.ctx, page->pixmap);
458 unlinkpage (page);
460 if (page->text) {
461 fz_free_text_span (state.ctx, page->text);
463 if (page->drawpage) {
464 pdf_free_page (state.ctx, page->drawpage);
466 for (i = 0; i < page->slicecount; ++i) {
467 free (page->slices[i].blocks);
469 free (page->slices);
470 free (page);
473 static void subdivide (struct page *p, int blockcount)
475 int i;
476 int w = p->pixmap->w;
477 int h = p->pixmap->h;
478 int offset = 0;
479 int tw = MIN (w, state.blockwidth);
480 int th = MIN (h, state.sliceheight);
482 for (i = 0; i < p->slicecount; ++i) {
483 int j, x, offset1;
484 struct slice *s = &p->slices[i];
486 s->h = MIN (th, h);
488 s->blocks = calloc (sizeof (struct block), p->blockcount);
489 if (!s->blocks) {
490 err (1, "calloc blocks page %d slice %d, %d",
491 p->pageno, i, p->blockcount);
494 w = p->pixmap->w;
495 x = 0;
496 offset1 = offset;
498 for (j = 0; j < blockcount; ++j) {
499 struct block *b = &s->blocks[j];
501 b->texindex = -1;
502 b->w = MIN (tw, w);
503 b->x = x;
504 b->offset = offset1;
505 w -= tw;
506 x += tw;
507 offset1 += tw * 4;
509 offset += s->h * p->pixmap->w * 4;
510 h -= th;
514 static int compatpdims (struct pagedim *p1, struct pagedim *p2)
516 return p1->rotate == p2->rotate
517 && !memcmp (&p1->bbox, &p2->bbox, sizeof (p1->bbox))
518 && !memcmp (&p1->ctm, &p2->ctm, sizeof (p1->ctm));
521 #ifdef __ALTIVEC__
522 #include <altivec.h>
524 static int cacheline32bytes;
525 extern char **environ;
527 static void __attribute__ ((constructor)) clcheck (void)
529 char **envp = environ;
530 unsigned long *auxv;
532 while (*envp++);
534 for (auxv = (unsigned long *) envp; *auxv != 0; auxv += 2) {
535 if (*auxv == 19) {
536 cacheline32bytes = auxv[1] == 32;
537 return;
542 static void OPTIMIZE (3) clearpixmap (fz_pixmap *pixmap)
544 if (cacheline32bytes) {
545 intptr_t a1, a2, diff;
546 size_t sizea, i, size = pixmap->w * pixmap->h * pixmap->n;
547 vector unsigned char v = vec_splat_u8 (-1);
548 vector unsigned char *p;
550 a1 = a2 = (intptr_t) pixmap->samples;
551 a2 = (a1 + 31) & ~31;
552 diff = a2 - a1;
553 sizea = size - diff;
554 p = (void *) a2;
556 while (a1 != a2) *(char *) a1++ = 0xff;
557 for (i = 0; i < (sizea & ~31); i += 32) {
558 __asm volatile ("dcbz %0, %1"::"b"(a2),"r"(i));
559 vec_st (v, i, p);
560 vec_st (v, i + 16, p);
562 while (i < sizea) *((char *) a1 + i++) = 0xff;
564 else fz_clear_pixmap_with_color (pixmap, 0xff);
566 #else
567 #define clearpixmap(p) fz_clear_pixmap_with_color (p, 0xff)
568 #endif
570 static void *render (int pageno, int pindex)
572 fz_device *idev;
573 double start, end;
574 pdf_page *drawpage;
575 struct pagedim *pagedim;
576 struct page *page = NULL;
577 int slicecount, blockcount;
579 start = now ();
580 printd (state.sock, "V rendering %d", pageno);
582 pagedim = &state.pagedims[pindex];
583 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
584 + state.sliceheight - 1) / state.sliceheight;
585 slicecount += slicecount == 0;
587 blockcount = (pagedim->bbox.x1 - pagedim->bbox.x0
588 + state.blockwidth - 1) / state.blockwidth;
589 blockcount += blockcount == 0;
591 if (state.pig) {
592 if (compatpdims (&state.pig->pagedim, pagedim)) {
593 page = state.pig;
594 if (page->text) {
595 fz_free_text_span (state.ctx, page->text);
596 page->text = NULL;
598 if (page->drawpage) {
599 pdf_free_page (state.ctx, page->drawpage);
600 page->drawpage = NULL;
603 else {
604 freepage (state.pig);
607 if (!page) {
608 page = calloc (sizeof (struct page), 1);
609 if (!page) {
610 err (1, "calloc page %d", pageno);
612 page->slices = calloc (sizeof (struct slice), slicecount);
613 if (!page->slices) {
614 err (1, "calloc slices %d %d", pageno, slicecount);
616 page->pixmap = fz_new_pixmap_with_rect (state.ctx, fz_device_rgb,
617 pagedim->bbox);
620 page->slicecount = slicecount;
621 page->blockcount = blockcount;
623 drawpage = pdf_load_page (state.xref, pageno - 1);
625 clearpixmap (page->pixmap);
627 idev = fz_new_draw_device (state.ctx, state.cache, page->pixmap);
628 pdf_run_page (state.xref, drawpage, idev, pagedim->ctm);
629 fz_free_device (idev);
631 page->drawpage = drawpage;
632 page->pagedim = *pagedim;
633 page->pageno = pageno;
634 subdivide (page, blockcount);
635 end = now ();
637 printd (state.sock, "V rendering %d took %f sec", pageno, end - start);
638 state.pig = NULL;
639 return page;
642 static void initpdims (void)
644 int pageno;
645 double start, end;
647 start = now ();
648 for (pageno = 0; pageno < state.pagecount; ++pageno) {
649 int rotate;
650 fz_rect box;
651 struct pagedim *p;
652 fz_obj *obj, *pageobj;
654 pageobj = state.xref->page_objs[pageno];
656 obj = fz_dict_gets (pageobj, "CropBox");
657 if (!fz_is_array (obj)) {
658 obj = fz_dict_gets (pageobj, "MediaBox");
659 if (!fz_is_array (obj)) {
660 fz_throw (state.ctx, "cannot find page bounds %d (%d Rd)",
661 fz_to_num (pageobj), fz_to_gen (pageobj));
664 box = pdf_to_rect (state.ctx, obj);
666 obj = fz_dict_gets (pageobj, "Rotate");
667 if (fz_is_int (obj)) {
668 rotate = fz_to_int (obj);
670 else {
671 rotate = 0;
673 rotate += state.rotate;
675 p = &state.pagedims[state.pagedimcount - 1];
676 if ((state.pagedimcount == 0)
677 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
678 size_t size;
680 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
681 state.pagedims = realloc (state.pagedims, size);
682 if (!state.pagedims) {
683 err (1, "realloc pagedims to " FMT_s " (%d elems)",
684 size, state.pagedimcount + 1);
686 p = &state.pagedims[state.pagedimcount++];
687 p->rotate = rotate;
688 p->box = box;
689 p->pageno = pageno;
692 end = now ();
693 printd (state.sock, "T Processed %d pages in %f seconds",
694 state.pagecount, end - start);
697 static void layout (void)
699 int pindex;
700 fz_matrix ctm;
701 fz_rect box, box2;
702 double zoom, w, maxw = 0;
703 struct pagedim *p = state.pagedims;
705 if (state.proportional) {
706 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
707 box.x0 = MIN (p->box.x0, p->box.x1);
708 box.y0 = MIN (p->box.y0, p->box.y1);
709 box.x1 = MAX (p->box.x0, p->box.x1);
710 box.y1 = MAX (p->box.y0, p->box.y1);
712 ctm = fz_identity;
713 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
714 ctm = fz_concat (ctm, fz_rotate (p->rotate));
715 box2 = fz_transform_rect (ctm, box);
716 w = box2.x1 - box2.x0;
717 maxw = MAX (w, maxw);
721 p = state.pagedims;
722 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
723 box.x0 = MIN (p->box.x0, p->box.x1);
724 box.y0 = MIN (p->box.y0, p->box.y1);
725 box.x1 = MAX (p->box.x0, p->box.x1);
726 box.y1 = MAX (p->box.y0, p->box.y1);
728 ctm = fz_identity;
729 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
730 ctm = fz_concat (ctm, fz_rotate (p->rotate));
731 box2 = fz_transform_rect (ctm, box);
732 w = box2.x1 - box2.x0;
734 if (state.proportional) {
735 double scale = w / maxw;
736 zoom = (state.w / w) * scale;
738 else {
739 zoom = state.w / w;
741 ctm = fz_identity;
742 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
743 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
744 memcpy (&p->ctm1, &ctm, sizeof (ctm));
745 ctm = fz_concat (ctm, fz_rotate (p->rotate));
746 p->bbox = fz_round_rect (fz_transform_rect (ctm, box));
747 p->left = state.proportional ? ((maxw - w) * zoom) / 2.0 : 0;
748 memcpy (&p->ctm, &ctm, sizeof (ctm));
751 while (p-- != state.pagedims) {
752 printd (state.sock, "l %d %d %d %d",
753 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0,
754 p->left);
758 static void recurse_outline (fz_outline *outline, int level)
760 while (outline) {
761 int pageno;
762 int i, top = 0, h;
763 struct pagedim *pagedim = state.pagedims;
765 pageno = outline->page;
766 for (i = 0; i < state.pagedimcount; ++i) {
767 if (state.pagedims[i].pageno > pageno)
768 break;
769 pagedim = &state.pagedims[i];
771 #ifdef HAS_PRECISE_POSITIONING
772 if (!outline->x_is_null && !outline->y_is_null) {
773 fz_point p = fz_transform_point (pagedim->ctm, outline->p);
774 top = p.y;
776 #endif
777 h = pagedim->bbox.y1 - pagedim->bbox.y0;
778 lprintf ("%*c%s %d %d\n", level, ' ', outline->title, pageno, top);
779 if (pageno > 0) {
780 printd (state.sock, "o %d %d %d %d %s",
781 level, pageno, top, h, outline->title);
783 if (outline->down) {
784 recurse_outline (outline->down, level + 1);
786 outline = outline->next;
790 static void process_outline (void)
792 fz_outline *outline;
794 if (!state.needoutline) return;
796 state.needoutline = 0;
797 outline = pdf_load_outline (state.xref);
798 if (outline) {
799 recurse_outline (outline, 0);
800 fz_free_outline (outline);
804 static int comparespans (const void *l, const void *r)
806 fz_text_span const *const*ls = l;
807 fz_text_span const *const*rs = r;
808 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
811 /* wishful thinking function */
812 static void search (regex_t *re, int pageno, int y, int forward)
814 int i, j;
815 int ret;
816 char *p;
817 char buf[256];
818 fz_matrix ctm;
819 fz_device *tdev;
820 pdf_page *drawpage;
821 fz_text_span *text, *span, **pspan;
822 struct pagedim *pdim, *pdimprev;
823 int stop = 0;
824 int niters = 0;
825 int nspans;
826 double start, end;
828 start = now ();
829 while (pageno >= 0 && pageno < state.pagecount && !stop) {
830 if (niters++ == 5) {
831 niters = 0;
832 if (hasdata (state.sock)) {
833 printd (state.sock, "T attention requested aborting search at %d",
834 pageno);
835 stop = 1;
837 else {
838 printd (state.sock, "T searching in page %d", pageno);
841 pdimprev = NULL;
842 for (i = 0; i < state.pagedimcount; ++i) {
843 pdim = &state.pagedims[i];
844 if (pdim->pageno == pageno) {
845 goto found;
847 if (pdim->pageno > pageno) {
848 pdim = pdimprev;
849 goto found;
851 pdimprev = pdim;
853 pdim = pdimprev;
854 found:
856 drawpage = pdf_load_page (state.xref, pageno);
858 ctm = fz_rotate (pdim->rotate);
860 text = fz_new_text_span (state.ctx);
861 tdev = fz_new_text_device (state.ctx, text);
862 pdf_run_page (state.xref, drawpage, tdev, pdim->ctm1);
863 fz_free_device (tdev);
865 nspans = 0;
866 for (span = text; span; span = span->next) {
867 nspans++;
869 pspan = malloc (sizeof (void *) * nspans);
870 if (!pspan) {
871 err (1, "malloc span pointers " FMT_s, sizeof (void *) * nspans);
873 for (i = 0, span = text; span; span = span->next, ++i) {
874 pspan[i] = span;
876 qsort (pspan, nspans, sizeof (fz_text_span *), comparespans);
878 j = forward ? 0 : nspans - 1;
879 while (nspans--) {
880 regmatch_t rm;
882 span = pspan[j];
883 j += forward ? 1 : -1;
884 p = buf;
885 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
886 if (forward) {
887 if (span->text[i].bbox.y0 < y + 1) {
888 continue;
891 else {
892 if (span->text[i].bbox.y0 > y - 1) {
893 continue;
896 if (span->text[i].c < 256) {
897 *p++ = span->text[i].c;
899 else {
900 *p++ = '?';
903 if (p == buf) {
904 continue;
906 *p++ = 0;
908 ret = regexec (re, buf, 1, &rm, 0);
909 if (ret) {
910 if (ret != REG_NOMATCH) {
911 size_t size;
912 char errbuf[80];
913 size = regerror (ret, re, errbuf, sizeof (errbuf));
914 printd (state.sock,
915 "T regexec error `%.*s'",
916 (int) size, errbuf);
917 fz_free_text_span (state.ctx, text);
918 pdf_free_page (state.ctx, drawpage);
919 free (pspan);
920 return;
923 else {
924 int xoff, yoff;
925 fz_bbox *sb, *eb;
926 fz_point p1, p2, p3, p4;
928 xoff = pdim->left - pdim->bbox.x0;
929 yoff = -pdim->bbox.y0;
931 sb = &span->text[rm.rm_so].bbox;
932 eb = &span->text[rm.rm_eo - 1].bbox;
934 p1.x = sb->x0;
935 p1.y = sb->y0;
936 p2.x = eb->x1;
937 p2.y = sb->y0;
938 p3.x = eb->x1;
939 p3.y = eb->y1;
940 p4.x = sb->x0;
941 p4.y = eb->y1;
943 p1 = fz_transform_point (ctm, p1);
944 p2 = fz_transform_point (ctm, p2);
945 p3 = fz_transform_point (ctm, p3);
946 p4 = fz_transform_point (ctm, p4);
948 if (!stop) {
949 printd (state.sock, "F %d %d %f %f %f %f %f %f %f %f",
950 pageno, 1,
951 p1.x + xoff, p1.y + yoff,
952 p2.x + xoff, p2.y + yoff,
953 p3.x + xoff, p3.y + yoff,
954 p4.x + xoff, p4.y + yoff);
956 printd (state.sock, "T found at %d `%.*s' in %f sec",
957 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
958 now () - start);
960 else {
961 printd (state.sock, "R %d %d %f %f %f %f %f %f %f %f",
962 pageno, 2,
963 p1.x + xoff, p1.y + yoff,
964 p2.x + xoff, p2.y + yoff,
965 p3.x + xoff, p3.y + yoff,
966 p4.x + xoff, p4.y + yoff);
968 stop = 1;
971 if (forward) {
972 pageno += 1;
973 y = 0;
975 else {
976 pageno -= 1;
977 y = INT_MAX;
979 fz_free_text_span (state.ctx, text);
980 pdf_free_page (state.ctx, drawpage);
981 free (pspan);
983 end = now ();
984 if (!stop) {
985 printd (state.sock, "T no matches %f sec", end - start);
987 printd (state.sock, "D");
990 static
991 #ifdef _WIN32
992 DWORD _stdcall
993 #else
994 void *
995 #endif
996 mainloop (void *unused)
998 char *p = NULL;
999 int len, ret, oldlen = 0;
1001 for (;;) {
1002 len = readlen (state.sock);
1003 if (len == 0) {
1004 errx (1, "readlen returned 0");
1007 if (oldlen < len + 1) {
1008 p = realloc (p, len + 1);
1009 if (!p) {
1010 err (1, "realloc %d failed", len + 1);
1012 oldlen = len + 1;
1014 readdata (state.sock, p, len);
1015 p[len] = 0;
1017 if (!strncmp ("open", p, 4)) {
1018 size_t filenamelen;
1019 char *password;
1020 char *filename = p + 5;
1022 filenamelen = strlen (filename);
1023 password = filename + filenamelen + 1;
1025 openxref (filename, password);
1026 initpdims ();
1027 pdfinfo ();
1028 state.needoutline = 1;
1030 else if (!strncmp ("free", p, 4)) {
1031 void *ptr;
1033 ret = sscanf (p + 4, " %p", &ptr);
1034 if (ret != 1) {
1035 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
1037 unlinkpage (ptr);
1038 if (state.pig) {
1039 freepage (state.pig);
1041 state.pig = ptr;
1043 else if (!strncmp ("search", p, 6)) {
1044 int icase, pageno, y, ret, len2, forward;
1045 char *pattern;
1046 regex_t re;
1048 ret = sscanf (p + 6, " %d %d %d %d,%n",
1049 &icase, &pageno, &y, &forward, &len2);
1050 if (ret != 4) {
1051 errx (1, "malformed search `%s' ret=%d", p, ret);
1054 pattern = p + 6 + len2;
1055 ret = regcomp (&re, pattern,
1056 REG_EXTENDED | (icase ? REG_ICASE : 0));
1057 if (ret) {
1058 char errbuf[80];
1059 size_t size;
1061 size = regerror (ret, &re, errbuf, sizeof (errbuf));
1062 printd (state.sock, "T regcomp failed `%.*s'",
1063 (int) size, errbuf);
1065 else {
1066 search (&re, pageno, y, forward);
1067 regfree (&re);
1070 else if (!strncmp ("geometry", p, 8)) {
1071 int w, h;
1073 printd (state.sock, "c");
1074 ret = sscanf (p + 8, " %d %d", &w, &h);
1075 if (ret != 2) {
1076 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
1079 lock ("geometry");
1080 state.h = h;
1081 if (w != state.w) {
1082 int i;
1083 state.w = w;
1084 for (i = 0; i < state.texcount; ++i) {
1085 state.texowners[i].block = NULL;
1088 layout ();
1089 process_outline ();
1090 unlock ("geometry");
1091 printd (state.sock, "C %d", state.pagecount);
1093 else if (!strncmp ("reinit", p, 6)) {
1094 float rotate;
1095 int proportional;
1097 printd (state.sock, "c");
1098 ret = sscanf (p + 6, " %f %d", &rotate, &proportional);
1099 if (ret != 2) {
1100 errx (1, "bad rotate line `%.*s' ret=%d", len, p, ret);
1102 lock ("reinit");
1103 state.rotate = rotate;
1104 state.proportional = proportional;
1105 state.pagedimcount = 0;
1106 free (state.pagedims);
1107 state.pagedims = NULL;
1108 initpdims ();
1109 layout ();
1110 process_outline ();
1111 if (state.pig) {
1112 freepage (state.pig);
1113 state.pig = NULL;
1115 unlock ("reinit");
1116 printd (state.sock, "C %d", state.pagecount);
1118 else if (!strncmp ("render", p, 6)) {
1119 int pageno, pindex, w, h, ret;
1120 struct page *page;
1122 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
1123 if (ret != 4) {
1124 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
1127 lock ("render");
1128 page = render (pageno, pindex);
1129 unlock ("render");
1131 printd (state.sock, "r %d %d %d %d %d %d %p",
1132 pageno,
1133 state.w,
1134 state.h,
1135 state.rotate,
1136 state.proportional,
1137 w * h * 4,
1138 page);
1140 else if (!strncmp ("interrupt", p, 9)) {
1141 printd (state.sock, "V interrupted");
1143 else if (!strncmp ("quit", p, 4)) {
1144 return 0;
1146 else {
1147 errx (1, "unknown command %.*s", len, p);
1150 return 0;
1153 static void showsel (struct page *page, int oy)
1155 int ox;
1156 fz_bbox bbox;
1157 fz_text_span *span;
1158 struct mark first, last;
1160 first = page->fmark;
1161 last = page->lmark;
1163 if (!first.span || !last.span) return;
1165 glEnable (GL_BLEND);
1166 glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA);
1167 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1169 ox = -page->pixmap->x + page->pagedim.left;
1170 oy = -page->pixmap->y + oy;
1171 for (span = first.span; span; span = span->next) {
1172 int i, j, k;
1174 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0;
1176 j = 0;
1177 k = span->len - 1;
1179 if (span == page->fmark.span && span == page->lmark.span) {
1180 j = MIN (first.i, last.i);
1181 k = MAX (first.i, last.i);
1183 else if (span == first.span) {
1184 j = first.i;
1186 else if (span == last.span) {
1187 k = last.i;
1190 for (i = j; i <= k; ++i) {
1191 bbox = fz_union_bbox (bbox, span->text[i].bbox);
1193 lprintf ("%d %d %d %d oy=%d ox=%d\n",
1194 bbox.x0,
1195 bbox.y0,
1196 bbox.x1,
1197 bbox.y1,
1198 oy, ox);
1200 glRecti (bbox.x0 + ox, bbox.y0 + oy, bbox.x1 + ox, bbox.y1 + oy);
1202 if (span == last.span) break;
1204 glDisable (GL_BLEND);
1207 static void highlightlinks (struct page *page, int yoff)
1209 pdf_link *link;
1210 int xoff;
1212 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1213 glEnable (GL_LINE_STIPPLE);
1214 glLineStipple (0.5, 0xcccc);
1216 xoff = page->pagedim.left - page->pixmap->x;
1217 yoff -= page->pixmap->y;
1219 glBegin (GL_QUADS);
1220 for (link = page->drawpage->links; link; link = link->next) {
1221 fz_point p1, p2, p3, p4;
1222 fz_matrix ctm = page->pagedim.ctm;
1224 p1.x = link->rect.x0;
1225 p1.y = link->rect.y0;
1227 p2.x = link->rect.x1;
1228 p2.y = link->rect.y0;
1230 p3.x = link->rect.x1;
1231 p3.y = link->rect.y1;
1233 p4.x = link->rect.x0;
1234 p4.y = link->rect.y1;
1236 p1 = fz_transform_point (ctm, p1);
1237 p2 = fz_transform_point (ctm, p2);
1238 p3 = fz_transform_point (ctm, p3);
1239 p4 = fz_transform_point (ctm, p4);
1241 switch (link->kind) {
1242 case PDF_LINK_GOTO: glColor3ub (255, 0, 0); break;
1243 case PDF_LINK_URI: glColor3ub (0, 0, 255); break;
1244 default: glColor3ub (0, 0, 0); break;
1247 glVertex2f (p1.x + xoff, p1.y + yoff);
1248 glVertex2f (p2.x + xoff, p2.y + yoff);
1249 glVertex2f (p3.x + xoff, p3.y + yoff);
1250 glVertex2f (p4.x + xoff, p4.y + yoff);
1252 glEnd ();
1254 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1255 glDisable (GL_LINE_STIPPLE);
1258 static void upload (struct page *page, int slicenum, int blocknum)
1260 double start, end;
1261 struct slice *slice = &page->slices[slicenum];
1262 struct block *block = &slice->blocks[blocknum];
1264 if (block->texindex != -1
1265 && state.texowners[block->texindex].block == block) {
1266 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[block->texindex]);
1268 else {
1269 int subimage = 0;
1270 int index = (state.texindex++ % state.texcount);
1272 if (state.texowners[index].w == block->w) {
1273 if (state.texowners[index].h >= slice->h ) {
1274 subimage = 1;
1276 else {
1277 state.texowners[index].h = slice->h;
1280 else {
1281 state.texowners[index].h = slice->h;
1284 state.texowners[index].w = block->w;
1285 state.texowners[index].block = block;
1286 block->texindex = index;
1288 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[block->texindex]);
1289 glPixelStorei (GL_UNPACK_ROW_LENGTH, page->pixmap->w);
1291 start = now ();
1292 if (0) {
1293 GLenum err = glGetError ();
1294 if (err != GL_NO_ERROR) {
1295 printf ("\033[0;31mERROR1 %d %d %#x\033[0m\n",
1296 block->w, slice->h, err);
1297 abort ();
1300 if (subimage) {
1301 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1305 block->w,
1306 slice->h,
1307 state.texform,
1308 state.texty,
1309 page->pixmap->samples + block->offset
1312 else {
1313 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1315 GL_RGBA8,
1316 block->w,
1317 slice->h,
1319 state.texform,
1320 state.texty,
1321 page->pixmap->samples + block->offset
1324 if (0) {
1325 GLenum err = glGetError ();
1326 if (err != GL_NO_ERROR) {
1327 printf ("\033[0;31mERROR %d %d %#x\033[0m\n",
1328 block->w, slice->h, err);
1329 abort ();
1333 end = now ();
1334 (void) start;
1335 (void) end;
1336 lprintf ("%s[%d] slice=%d,%d(%d,%d) tex=%d,%d offset=%d %f sec\n",
1337 subimage ? "sub" : "img",
1338 page->pageno, slicenum, blocknum,
1339 block->w, slice->h,
1340 block->texindex,
1341 state.texids[block->texindex],
1342 block->offset,
1343 end - start);
1347 CAMLprim value ml_draw (value args_v, value ptr_v)
1349 CAMLparam2 (args_v, ptr_v);
1350 int dispy = Int_val (Field (args_v, 0));
1351 int h = Int_val (Field (args_v, 1));
1352 int py = Int_val (Field (args_v, 2));
1353 int hlinks = Bool_val (Field (args_v, 3));
1354 char *s = String_val (ptr_v);
1355 int ret;
1356 void *ptr;
1357 struct page *page;
1358 int slicenum;
1359 int yoff = dispy - py;
1360 struct slice *slice;
1362 ret = sscanf (s, "%p", &ptr);
1363 if (ret != 1) {
1364 errx (1, "cannot parse pointer `%s'", s);
1366 page = ptr;
1368 ARSERT (h >= 0 && "ml_draw wrong h");
1369 ARSERT (py <= page->pixmap->h && "ml_draw wrong py");
1371 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1373 for (slicenum = 0, slice = page->slices;
1374 slicenum < page->slicecount; ++slicenum, ++slice) {
1375 if (slice->h > py) {
1376 break;
1378 py -= slice->h;
1380 h = MIN (state.h, h);
1382 while (h) {
1383 int th, left, blocknum;
1385 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
1387 th = MIN (h, slice->h - py);
1388 left = page->pagedim.left;
1390 for (blocknum = 0; blocknum < page->blockcount; ++blocknum) {
1391 struct block *block = &slice->blocks[blocknum];
1393 upload (page, slicenum, blocknum);
1394 glBegin (GL_QUADS);
1396 glTexCoord2i (0, py);
1397 glVertex2i (left, dispy);
1399 glTexCoord2i (block->w, py);
1400 glVertex2i (left + block->w, dispy);
1402 glTexCoord2i (block->w, py + th);
1403 glVertex2i (left + block->w, dispy + th);
1405 glTexCoord2i (0, py + th);
1406 glVertex2i (left, dispy + th);
1408 glEnd ();
1409 left += block->w;
1412 h -= th;
1413 py = 0;
1414 dispy += th;
1415 slicenum += 1;
1418 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1420 showsel (page, yoff);
1421 if (hlinks) highlightlinks (page, yoff);
1423 CAMLreturn (Val_unit);
1426 static pdf_link *getlink (struct page *page, int x, int y)
1428 fz_point p;
1429 fz_matrix ctm;
1430 pdf_link *link;
1432 p.x = x + page->pixmap->x;
1433 p.y = y + page->pixmap->y;
1435 ctm = fz_invert_matrix (page->pagedim.ctm);
1436 p = fz_transform_point (ctm, p);
1438 for (link = page->drawpage->links; link; link = link->next) {
1439 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1440 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1441 return link;
1445 return NULL;
1448 static void ensuretext (struct page *page)
1450 if (!page->text) {
1451 fz_device *tdev;
1453 page->text = fz_new_text_span (state.ctx);
1454 tdev = fz_new_text_device (state.ctx, page->text);
1455 pdf_run_page (state.xref, page->drawpage, tdev, page->pagedim.ctm);
1456 fz_free_device (tdev);
1460 CAMLprim value ml_whatsunder (value ptr_v, value x_v, value y_v)
1462 CAMLparam3 (ptr_v, x_v, y_v);
1463 CAMLlocal3 (ret_v, tup_v, str_v);
1464 pdf_link *link;
1465 struct page *page;
1466 char *s = String_val (ptr_v);
1468 ret_v = Val_int (0);
1469 if (trylock ("ml_whatsunder")) {
1470 goto done;
1473 page = parse_pointer ("ml_whatsunder", s);
1474 link = getlink (page, Int_val (x_v), Int_val (y_v));
1475 if (link) {
1476 switch (link->kind) {
1477 case PDF_LINK_GOTO:
1479 int pageno;
1480 fz_point p;
1481 fz_obj *obj;
1483 pageno = -1;
1484 p.x = 0;
1485 p.y = 0;
1487 if (fz_is_array (link->dest)) {
1488 obj = fz_array_get (link->dest, 0);
1489 if (fz_is_indirect (obj)) {
1490 pageno = pdf_find_page_number (state.xref, obj);
1492 else if (fz_is_int (obj)) {
1493 pageno = fz_to_int (obj);
1496 if (fz_array_len (link->dest) > 3) {
1497 fz_obj *xo, *yo;
1499 xo = fz_array_get (link->dest, 2);
1500 yo = fz_array_get (link->dest, 3);
1501 if (!fz_is_null (xo) && !fz_is_null (yo)) {
1502 p.x = fz_to_int (xo);
1503 p.y = fz_to_int (yo);
1504 p = fz_transform_point (page->pagedim.ctm, p);
1508 else {
1509 pageno = pdf_find_page_number (state.xref, link->dest);
1511 tup_v = caml_alloc_tuple (2);
1512 ret_v = caml_alloc_small (1, 1);
1513 Field (tup_v, 0) = Val_int (pageno);
1514 Field (tup_v, 1) = Val_int (p.y);
1515 Field (ret_v, 0) = tup_v;
1517 break;
1519 case PDF_LINK_URI:
1520 str_v = caml_copy_string (fz_to_str_buf (link->dest));
1521 ret_v = caml_alloc_small (1, 0);
1522 Field (ret_v, 0) = str_v;
1523 break;
1525 default:
1526 printd (state.sock, "T unhandled link kind %d", link->kind);
1527 break;
1530 else {
1531 int i, x, y;
1532 fz_text_span *span;
1534 ensuretext (page);
1535 x = Int_val (x_v) + page->pixmap->x;
1536 y = Int_val (y_v) + page->pixmap->y;
1538 for (span = page->text; span; span = span->next) {
1539 for (i = 0; i < span->len; ++i) {
1540 fz_bbox *b;
1541 b = &span->text[i].bbox;
1542 if ((x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1)) {
1543 const char *n2 =
1544 span->font && span->font->name
1545 ? span->font->name
1546 : "Span has no font name"
1548 FT_FaceRec *face = span->font->ft_face;
1549 if (face && face->family_name) {
1550 char *s;
1551 char *n1 = face->family_name;
1552 size_t l1 = strlen (n1);
1553 size_t l2 = strlen (n2);
1555 if (l1 != l2 || memcmp (n1, n2, l1)) {
1556 s = malloc (l1 + l2 + 2);
1557 if (s) {
1558 memcpy (s, n2, l2);
1559 s[l2] = '=';
1560 memcpy (s + l2 + 1, n1, l1 + 1);
1561 str_v = caml_copy_string (s);
1562 free (s);
1566 if (str_v == 0) {
1567 str_v = caml_copy_string (n2);
1569 ret_v = caml_alloc_small (1, 2);
1570 Field (ret_v, 0) = str_v;
1571 goto unlock;
1576 unlock:
1577 unlock ("ml_whatsunder");
1579 done:
1580 CAMLreturn (ret_v);
1583 CAMLprim value ml_seltext (value ptr_v, value rect_v, value oy_v)
1585 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1586 fz_bbox *b;
1587 struct page *page;
1588 fz_text_span *span;
1589 struct mark first, last;
1590 int i, x0, x1, y0, y1, oy, left;
1591 char *s = String_val (ptr_v);
1593 if (trylock ("ml_seltext")) {
1594 goto done;
1597 page = parse_pointer ("ml_seltext", s);
1598 ensuretext (page);
1600 oy = Int_val (oy_v);
1601 x0 = Int_val (Field (rect_v, 0));
1602 y0 = Int_val (Field (rect_v, 1));
1603 x1 = Int_val (Field (rect_v, 2));
1604 y1 = Int_val (Field (rect_v, 3));
1606 left = page->pagedim.left;
1607 if (0) {
1608 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1609 glColor3ub (128, 128, 128);
1610 glRecti (x0, y0, x1, y1);
1611 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1614 x0 += page->pixmap->x - left;
1615 y0 += page->pixmap->y - oy;
1616 x1 += page->pixmap->x - left;
1617 y1 += page->pixmap->y - oy;
1619 first.span = NULL;
1620 last.span = NULL;
1622 last.i = first.i = 0;
1623 first.span = page->text;
1624 for (span = page->text; span; span = span->next) {
1625 for (i = 0; i < span->len; ++i) {
1626 b = &span->text[i].bbox;
1627 int selected = 0;
1629 if (x0 >= b->x0 && x0 <= b->x1 && y0 >= b->y0 && y0 <= b->y1) {
1630 first.i = i;
1631 first.span = span;
1632 selected = 1;
1634 if (x1 >= b->x0 && x1 <= b->x1 && y1 >= b->y0 && y1 <= b->y1) {
1635 last.i = i;
1636 last.span = span;
1637 selected = 1;
1639 if (0 && selected) {
1640 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1641 glColor3ub (128, 128, 128);
1642 glRecti (b->x0+left, b->y0, b->x1+left, b->y1);
1643 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1648 if (y1 < y0 || x1 < x0) {
1649 int swap = 0;
1651 if (first.span == last.span) {
1652 swap = 1;
1654 else {
1655 if (y1 < y0) {
1656 for (span = first.span; span && span != last.span;
1657 span = span->next) {
1658 if (span->eol) {
1659 swap = 1;
1660 break;
1666 if (swap) {
1667 i = first.i;
1668 span = first.span;
1669 first.i = last.i;
1670 first.span = last.span;
1671 last.i = i;
1672 last.span = span;
1676 page->fmark = first;
1677 page->lmark = last;
1679 unlock ("ml_seltext");
1681 done:
1682 CAMLreturn (Val_unit);
1685 static int pipespan (FILE *f, fz_text_span *span, int a, int b)
1687 char buf[4];
1688 int i, len, ret;
1690 for (i = a; i <= b; ++i) {
1691 len = runetochar (buf, &span->text[i].c);
1692 ret = fwrite (buf, len, 1, f);
1694 if (ret != 1) {
1695 printd (state.sock, "T failed to write %d bytes ret=%d: %s",
1696 len, ret, strerror (errno));
1697 return -1;
1700 return 0;
1703 CAMLprim value ml_copysel (value ptr_v)
1705 CAMLparam1 (ptr_v);
1706 FILE *f;
1707 struct page *page;
1708 char *s = String_val (ptr_v);
1710 if (trylock ("ml_copysel")) {
1711 goto done;
1714 if (!*s) {
1715 close:
1716 #ifdef USE_XSEL
1717 if (state.xselpipe) {
1718 int ret = pclose (state.xselpipe);
1719 if (ret) {
1720 printd (state.sock, "T failed to close xsel pipe `%s'",
1721 strerror (errno));
1723 state.xselpipe = NULL;
1725 #else
1726 printf ("========================================\n");
1727 #endif
1729 else {
1730 fz_text_span *span;
1732 page = parse_pointer ("ml_sopysel", s);
1734 if (!page->fmark.span || !page->lmark.span) {
1735 printd (state.sock, "T nothing to copy");
1736 goto unlock;
1739 f = stdout;
1740 #ifdef USE_XSEL
1741 if (!state.xselpipe) {
1742 state.xselpipe = popen ("xsel -i", "w");
1743 if (!state.xselpipe) {
1744 printd (state.sock, "T failed to open xsel pipe `%s'",
1745 strerror (errno));
1747 else {
1748 f = state.xselpipe;
1751 else {
1752 f = state.xselpipe;
1754 #endif
1756 for (span = page->fmark.span;
1757 span && span != page->lmark.span->next;
1758 span = span->next) {
1759 int a = span == page->fmark.span ? page->fmark.i : 0;
1760 int b = span == page->lmark.span ? page->lmark.i : span->len - 1;
1761 if (pipespan (f, span, a, b)) {
1762 goto close;
1764 if (span->eol) {
1765 if (putc ('\n', f) == EOF) {
1766 printd (state.sock, "T failed break line on xsel pipe `%s'",
1767 strerror (errno));
1768 goto close;
1772 page->lmark.span = NULL;
1773 page->fmark.span = NULL;
1776 unlock:
1777 unlock ("ml_copysel");
1779 done:
1780 CAMLreturn (Val_unit);
1783 CAMLprim value ml_getpdimrect (value pagedimno_v)
1785 CAMLparam1 (pagedimno_v);
1786 CAMLlocal1 (ret_v);
1787 int pagedimno = Int_val (pagedimno_v);
1788 fz_rect box;
1790 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1791 if (trylock ("ml_getpdimrect")) {
1792 box = fz_empty_rect;
1794 else {
1795 box = state.pagedims[pagedimno].box;
1796 unlock ("ml_getpdimrect");
1799 Store_double_field (ret_v, 0, box.x0);
1800 Store_double_field (ret_v, 1, box.x1);
1801 Store_double_field (ret_v, 2, box.y0);
1802 Store_double_field (ret_v, 3, box.y1);
1804 CAMLreturn (ret_v);
1807 static double getmaxw (void)
1809 int i;
1810 struct pagedim *p;
1811 double maxw = 0.0;
1813 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
1814 double x0, x1, w;
1816 x0 = MIN (p->box.x0, p->box.x1);
1817 x1 = MAX (p->box.x0, p->box.x1);
1819 w = x1 - x0;
1820 maxw = MAX (w, maxw);
1822 return maxw;
1825 CAMLprim value ml_getmaxw (value unit_v)
1827 CAMLparam1 (unit_v);
1828 CAMLlocal1 (ret_v);
1829 double maxw = 0.0;
1831 if (trylock ("ml_getmaxw")) {
1832 goto done;
1834 maxw = getmaxw ();
1835 unlock ("ml_getmaxw");
1836 done:
1837 ret_v = caml_copy_double (maxw);
1838 CAMLreturn (ret_v);
1841 CAMLprim value ml_zoom_for_height (value winw_v, value winh_v, value dw_v)
1843 CAMLparam3 (winw_v, winh_v, dw_v);
1844 CAMLlocal1 (ret_v);
1845 int i;
1846 double zoom = 1.0;
1847 double maxw = 0.0, maxh = 0.0;
1848 struct pagedim *p;
1849 double winw = Int_val (winw_v);
1850 double winh = Int_val (winh_v);
1851 double dw = Int_val (dw_v);
1852 double pw = 1.0, ph = 1.0, num, den;
1854 if (trylock ("ml_zoom_for_height")) {
1855 goto done;
1858 if (state.proportional) {
1859 maxw = getmaxw ();
1862 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
1863 double x0, x1, y0, y1, w, h, scaledh, scale;
1865 x0 = MIN (p->box.x0, p->box.x1);
1866 y0 = MIN (p->box.y0, p->box.y1);
1867 x1 = MAX (p->box.x0, p->box.x1);
1868 y1 = MAX (p->box.y0, p->box.y1);
1870 w = x1 - x0;
1871 h = y1 - y0;
1873 if (state.proportional) {
1874 scale = w / maxw;
1875 scaledh = h * scale;
1877 else {
1878 scale = 1.0;
1879 scaledh = h;
1882 if (scaledh > maxh) {
1883 maxh = scaledh;
1884 ph = scaledh;
1885 pw = w * scale;
1889 num = (winh * pw) + (ph * dw);
1890 den = ph * winw;
1891 zoom = num / den;
1893 unlock ("ml_zoom_for_height");
1894 done:
1895 ret_v = caml_copy_double (zoom);
1896 CAMLreturn (ret_v);
1899 #include "glfont.c"
1901 CAMLprim value ml_draw_string (value pt_v, value x_v, value y_v, value string_v)
1903 CAMLparam4 (pt_v, x_v, y_v, string_v);
1904 CAMLlocal1 (ret_v);
1905 int pt = Int_val(pt_v);
1906 int x = Int_val (x_v);
1907 int y = Int_val (y_v);
1908 double w;
1910 w = draw_string (state.face, pt, x, y, String_val (string_v));
1911 ret_v = caml_copy_double (w);
1912 CAMLreturn (ret_v);
1915 CAMLprim value ml_measure_string (value pt_v, value string_v)
1917 CAMLparam2 (pt_v, string_v);
1918 CAMLlocal1 (ret_v);
1919 int pt = Int_val (pt_v);
1920 double w;
1922 w = measure_string (state.face, pt, String_val (string_v));
1923 ret_v = caml_copy_double (w);
1924 CAMLreturn (ret_v);
1927 CAMLprim value ml_init (value sock_v, value params_v)
1929 CAMLparam2 (sock_v, params_v);
1930 #ifndef _WIN32
1931 int ret;
1932 #endif
1933 char *fontpath;
1935 state.ctx = fz_new_context (&fz_alloc_default, 256<<20);
1936 state.rotate = Int_val (Field (params_v, 0));
1937 state.proportional = Bool_val (Field (params_v, 1));
1938 state.texcount = Int_val (Field (params_v, 2));
1939 state.sliceheight = Int_val (Field (params_v, 3));
1940 state.blockwidth = Int_val (Field (params_v, 4));
1941 fontpath = String_val (Field (params_v, 5));
1942 state.texform = GL_RGBA;
1943 state.texty = GL_UNSIGNED_BYTE;
1945 if (*fontpath) {
1946 state.face = load_font (fontpath);
1948 else {
1949 unsigned int len;
1950 void *base = pdf_find_substitute_font (0, 0, 0, 0, &len);
1952 state.face = load_builtin_font (base, len);
1954 if (!state.face) _exit (1);
1956 fz_accelerate ();
1957 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1958 if (!state.texids) {
1959 err (1, "calloc texids " FMT_s,
1960 state.texcount * sizeof (*state.texids));
1963 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1964 if (!state.texowners) {
1965 err (1, "calloc texowners " FMT_s,
1966 state.texcount * sizeof (*state.texowners));
1969 glGenTextures (state.texcount, state.texids);
1971 #ifdef _WIN32
1972 state.sock = Socket_val (sock_v);
1973 #else
1974 state.sock = Int_val (sock_v);
1975 #endif
1977 #ifdef _WIN32
1978 InitializeCriticalSection (&critsec);
1979 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
1980 if (state.thread == INVALID_HANDLE_VALUE) {
1981 errx (1, "CreateThread failed: %lx", GetLastError ());
1983 #else
1984 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1985 if (ret) {
1986 errx (1, "pthread_create: %s", strerror (ret));
1988 #endif
1990 CAMLreturn (Val_unit);