Always use our own err/errx implementations and avoid using exit
[llpp.git] / link.c
blob239117f4ab997a1231698ac78d4b2cea9bdf9991
1 /* lots of code c&p-ed directly from mupdf */
2 #define _BSD_SOURCE
4 #include <errno.h>
5 #include <stdio.h>
6 #include <string.h>
7 #include <stdlib.h>
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 #pragma warning (disable:4244)
22 #pragma warning (disable:4996)
23 #pragma warning (disable:4995)
24 #endif
26 #ifdef _MSC_VER
27 #define NORETURN __declspec (noreturn)
28 #elif defined __GNUC__
29 #define NORETURN __attribute__ ((noreturn))
30 #else
31 #define NORETURN
32 #endif
34 #ifdef _WIN32
35 static void __declspec (noreturn) sockerr (int exitcode, const char *fmt, ...)
37 va_list ap;
39 va_start (ap, fmt);
40 vfprintf (stderr, fmt, ap);
41 va_end (ap);
42 fprintf (stderr, ": wsaerror 0x%x\n", WSAGetLastError ());
43 _exit (exitcode);
45 #else
46 #define FMT_ss "%zd"
47 #define FMT_s "%zu"
48 #define fionread_arg int
49 #define ioctlsocket ioctl
50 #define sockerr err
51 #include <unistd.h>
52 #endif
54 #include <regex.h>
55 #include <ctype.h>
56 #include <stdarg.h>
57 #include <limits.h>
59 #ifndef _WIN32
60 #include <pthread.h>
61 #include <sys/time.h>
62 #include <sys/types.h>
63 #include <sys/socket.h>
64 #include <sys/ioctl.h>
65 #endif
67 static void NORETURN err (int exitcode, const char *fmt, ...)
69 va_list ap;
70 int savederrno;
72 savederrno = errno;
73 va_start (ap, fmt);
74 vfprintf (stderr, fmt, ap);
75 va_end (ap);
76 fprintf (stderr, ": %s\n", strerror (savederrno));
77 fflush (stderr);
78 _exit (exitcode);
81 static void NORETURN errx (int exitcode, const char *fmt, ...)
83 va_list ap;
85 va_start (ap, fmt);
86 vfprintf (stderr, fmt, ap);
87 va_end (ap);
88 fputc ('\n', stderr);
89 fflush (stderr);
90 _exit (exitcode);
93 #ifdef __APPLE__
94 #include <OpenGL/gl.h>
95 #else
96 #include <GL/gl.h>
97 #endif
99 #ifndef GL_TEXTURE_RECTANGLE_ARB
100 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
101 #endif
103 #include <caml/fail.h>
104 #include <caml/alloc.h>
105 #include <caml/memory.h>
106 #include <caml/unixsupport.h>
108 #include <fitz.h>
109 #include <mupdf.h>
111 #if 0
112 #define lprintf printf
113 #else
114 #define lprintf(...)
115 #endif
117 #ifdef FT_FREETYPE_H
118 #include FT_FREETYPE_H
119 #endif
121 #define ARSERT(cond) for (;;) { \
122 if (!(cond)) { \
123 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
125 break; \
128 struct slice {
129 int texindex;
130 int w, h;
133 struct pagedim {
134 int pageno;
135 int rotate;
136 int left;
137 fz_rect box;
138 fz_bbox bbox;
139 fz_matrix ctm, ctm1;
142 struct page {
143 int pageno;
144 int slicecount;
145 fz_text_span *text;
146 fz_pixmap *pixmap;
147 pdf_page *drawpage;
148 struct pagedim pagedim;
149 struct mark {
150 int i;
151 fz_text_span *span;
152 } fmark, lmark;
153 struct slice slices[];
156 #if !defined _WIN32 && !defined __APPLE__
157 #define USE_XSEL
158 #endif
160 struct {
161 int sock;
162 int sliceheight;
163 struct page *pig;
164 struct pagedim *pagedims;
165 int pagecount;
166 int pagedimcount;
167 pdf_xref *xref;
168 fz_glyph_cache *cache;
169 int w, h;
171 int texindex;
172 int texcount;
173 GLuint *texids;
175 GLenum texform;
176 GLenum texty;
178 struct {
179 int w, h;
180 struct slice *slice;
181 } *texowners;
183 int rotate;
184 int proportional;
185 int needoutline;
187 #ifdef _WIN32
188 HANDLE thread;
189 #else
190 pthread_t thread;
191 #endif
192 FILE *xselpipe;
193 } state;
195 #ifdef _WIN32
196 static CRITICAL_SECTION critsec;
198 static void lock (void *unused)
200 (void) unused;
201 EnterCriticalSection (&critsec);
204 static void unlock (void *unused)
206 (void) unused;
207 LeaveCriticalSection (&critsec);
210 static int trylock (void *unused)
212 return TryEnterCriticalSection (&critsec) == 0;
214 #else
215 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
217 static void lock (const char *cap)
219 int ret = pthread_mutex_lock (&mutex);
220 if (ret) {
221 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
225 static void unlock (const char *cap)
227 int ret = pthread_mutex_unlock (&mutex);
228 if (ret) {
229 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
233 static int trylock (const char *cap)
235 int ret = pthread_mutex_trylock (&mutex);
237 if (ret && ret != EBUSY) {
238 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
240 return ret == EBUSY;
242 #endif
244 static void *parse_pointer (const char *cap, const char *s)
246 int ret;
247 void *ptr;
249 ret = sscanf (s, "%p", &ptr);
250 if (ret != 1) {
251 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
253 return ptr;
256 static int hasdata (int sock)
258 int ret;
259 fionread_arg avail;
260 ret = ioctlsocket (sock, FIONREAD, &avail);
261 if (ret) sockerr (1, "hasdata: FIONREAD error ret=%d", ret);
262 return avail > 0;
265 static double now (void)
267 struct timeval tv;
269 if (gettimeofday (&tv, NULL)) {
270 err (1, "gettimeofday");
272 return tv.tv_sec + tv.tv_usec*1e-6;
275 static void readdata (int fd, char *p, int size)
277 ssize_t n;
279 n = recv (fd, p, size, 0);
280 if (n - size) {
281 if (!n) errx (1, "EOF while reading");
282 sockerr (1, "recv (req %d, ret " FMT_ss ")", size, n);
286 static void writedata (int fd, char *p, int size)
288 char buf[4];
289 ssize_t n;
291 buf[0] = (size >> 24) & 0xff;
292 buf[1] = (size >> 16) & 0xff;
293 buf[2] = (size >> 8) & 0xff;
294 buf[3] = (size >> 0) & 0xff;
296 n = send (fd, buf, 4, 0);
297 if (n != 4) {
298 if (!n) errx (1, "EOF while writing length");
299 sockerr (1, "send " FMT_ss, n);
302 n = send (fd, p, size, 0);
303 if (n - size) {
304 if (!n) errx (1, "EOF while writing data");
305 sockerr (1, "send (req %d, ret " FMT_ss ")", size, n);
309 static void
310 #ifdef __GNUC__
311 __attribute__ ((format (printf, 2, 3)))
312 #endif
313 printd (int fd, const char *fmt, ...)
315 int size = 200, len;
316 va_list ap;
317 char *buf;
319 buf = malloc (size);
320 for (;;) {
321 if (!buf) err (errno, "malloc for temp buf (%d bytes) failed", size);
323 va_start (ap, fmt);
324 len = vsnprintf (buf, size, fmt, ap);
325 va_end (ap);
327 if (len > -1 && len < size) {
328 writedata (fd, buf, len);
329 break;
332 if (len > -1) {
333 size = len + 1;
335 else {
336 size *= 2;
338 buf = realloc (buf, size);
340 free (buf);
343 static void die (fz_error error)
345 fz_catch (error, "aborting");
346 if (state.xref)
347 pdf_free_xref (state.xref);
348 _exit (1);
351 static void openxref (char *filename, char *password)
353 int i;
354 fz_error error;
356 for (i = 0; i < state.texcount; ++i) {
357 state.texowners[i].slice = NULL;
360 if (state.cache) {
361 fz_free_glyph_cache (state.cache);
364 state.cache = fz_new_glyph_cache ();
365 if (!state.cache) {
366 errx (1, "fz_newglyph_cache failed");
369 if (state.xref) {
370 if (state.xref->store) {
371 pdf_free_store (state.xref->store);
372 state.xref->store = NULL;
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 error = pdf_open_xref (&state.xref, filename, password);
385 if (error) {
386 die (error);
389 error = pdf_load_page_tree (state.xref);
390 if (error) {
391 die (error);
394 state.pagecount = pdf_count_pages (state.xref);
397 static void pdfinfo (void)
399 printd (state.sock, "i PDF version %d.%d\n",
400 state.xref->version / 10, state.xref->version % 10);
402 #if _XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L
404 FILE *f;
405 char *buf;
406 size_t size;
407 fz_obj *obj;
409 f = open_memstream (&buf, &size);
410 if (f) {
411 obj = fz_dict_gets (state.xref->trailer, "Info");
412 fz_fprint_obj (f, fz_resolve_indirect (obj), 0);
413 if (fclose (f)) err (1, "fclose on memstream failed");
414 if (size > 64*1024) size = 64*1024;
415 printd (state.sock, "i %.*s", (int) size, buf);
416 free (buf);
418 else {
419 printd (state.sock, "i error opening memstream: %s\n",
420 strerror (errno));
423 #endif
426 static int readlen (int fd)
428 ssize_t n;
429 unsigned char p[4];
431 n = recv (fd, p, 4, 0);
432 if (n != 4) {
433 if (!n) errx (1, "EOF while reading length");
434 sockerr (1, "recv " FMT_ss, n);
437 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
440 static void unlinkpage (struct page *page)
442 int i;
444 for (i = 0; i < page->slicecount; ++i) {
445 struct slice *s = &page->slices[i];
446 if (s->texindex != -1) {
447 if (state.texowners[s->texindex].slice == s) {
448 state.texowners[s->texindex].slice = NULL;
449 ARSERT (state.texowners[s->texindex].w == s->w);
450 ARSERT (state.texowners[s->texindex].h >= s->h);
456 static void freepage (struct page *page)
458 fz_drop_pixmap (page->pixmap);
460 unlinkpage (page);
462 if (page->text) {
463 fz_free_text_span (page->text);
465 if (page->drawpage) {
466 pdf_free_page (page->drawpage);
469 free (page);
472 static void subdivide (struct page *p)
474 int i;
475 int h = p->pixmap->h;
476 int th = MIN (h, state.sliceheight);
478 for (i = 0; i < p->slicecount; ++i) {
479 struct slice *s = &p->slices[i];
480 s->texindex = -1;
481 s->h = MIN (th, h);
482 s->w = p->pixmap->w;
483 h -= th;
487 static int compatpdims (struct pagedim *p1, struct pagedim *p2)
489 return p1->rotate == p2->rotate
490 && !memcmp (&p1->bbox, &p2->bbox, sizeof (p1->bbox))
491 && !memcmp (&p1->ctm, &p2->ctm, sizeof (p1->ctm));
494 #ifdef __ALTIVEC__
495 #include <altivec.h>
497 static int cacheline32bytes;
498 extern char **environ;
500 static void __attribute__ ((constructor)) clcheck (void)
502 char **envp = environ;
503 unsigned long *auxv;
505 while (*envp++);
507 for (auxv = (unsigned long *) envp; *auxv != 0; auxv += 2) {
508 if (*auxv == 19) {
509 cacheline32bytes = auxv[1] == 32;
510 return;
515 static void __attribute__ ((optimize ("O"))) clearpixmap (fz_pixmap *pixmap)
517 if (cacheline32bytes) {
518 intptr_t a1, a2, diff;
519 size_t sizea, i, size = pixmap->w * pixmap->h * pixmap->n;
520 vector unsigned char v = vec_splat_u8 (-1);
521 vector unsigned char *p;
523 a1 = a2 = (intptr_t) pixmap->samples;
524 a2 = (a1 + 31) & ~31;
525 diff = a2 - a1;
526 sizea = size - diff;
527 p = (void *) a2;
529 while (a1 != a2) *(char *) a1++ = 0xff;
530 for (i = 0; i < (sizea & ~31); i += 32) {
531 __asm volatile ("dcbz %0, %1"::"b"(a2),"r"(i));
532 vec_st (v, i, p);
533 vec_st (v, i + 16, p);
535 while (i < sizea) *((char *) a1 + i++) = 0xff;
537 else fz_clear_pixmap_with_color (pixmap, 0xff);
539 #else
540 #define clearpixmap(p) fz_clear_pixmap_with_color (p, 0xff)
541 #endif
543 static void *render (int pageno, int pindex)
545 fz_error error;
546 int slicecount;
547 struct page *page = NULL;
548 double start, end;
549 pdf_page *drawpage;
550 fz_device *idev;
551 struct pagedim *pagedim;
553 start = now ();
554 printd (state.sock, "V rendering %d", pageno);
556 pagedim = &state.pagedims[pindex];
557 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
558 + state.sliceheight - 1) / state.sliceheight;
559 slicecount += slicecount == 0;
561 if (state.pig) {
562 if (compatpdims (&state.pig->pagedim, pagedim)) {
563 page = state.pig;
564 if (page->text) {
565 fz_free_text_span (page->text);
566 page->text = NULL;
568 if (page->drawpage) {
569 pdf_free_page (page->drawpage);
570 page->drawpage = NULL;
573 else {
574 freepage (state.pig);
577 if (!page) {
578 page = calloc (sizeof (*page)
579 + (slicecount * sizeof (struct slice)), 1);
580 if (!page) {
581 err (1, "calloc page %d\n", pageno);
583 page->pixmap = fz_new_pixmap_with_rect (fz_device_rgb, pagedim->bbox);
586 page->slicecount = slicecount;
588 error = pdf_load_page (&drawpage, state.xref, pageno - 1);
589 if (error)
590 die (error);
592 clearpixmap (page->pixmap);
594 idev = fz_new_draw_device (state.cache, page->pixmap);
595 if (!idev)
596 die (fz_throw ("fz_newdrawdevice failed"));
597 error = pdf_run_page (state.xref, drawpage, idev, pagedim->ctm);
598 if (error)
599 die (fz_rethrow (error, "pdf_runpage failed"));
600 fz_free_device (idev);
602 page->drawpage = drawpage;
603 page->pagedim = *pagedim;
604 page->pageno = pageno;
605 subdivide (page);
606 end = now ();
608 pdf_age_store (state.xref->store, 3);
610 printd (state.sock, "V rendering %d took %f sec", pageno, end - start);
611 state.pig = NULL;
612 return page;
615 static void initpdims (void)
617 int pageno;
618 double start, end;
620 start = now ();
621 for (pageno = 0; pageno < state.pagecount; ++pageno) {
622 int rotate;
623 fz_rect box;
624 struct pagedim *p;
625 fz_obj *obj, *pageobj;
627 pageobj = state.xref->page_objs[pageno];
629 obj = fz_dict_gets (pageobj, "CropBox");
630 if (!fz_is_array (obj)) {
631 obj = fz_dict_gets (pageobj, "MediaBox");
632 if (!fz_is_array (obj)) {
633 die (fz_throw ("cannot find page bounds %d (%d Rd)",
634 fz_to_num (pageobj), fz_to_gen (pageobj)));
637 box = pdf_to_rect (obj);
639 obj = fz_dict_gets (pageobj, "Rotate");
640 if (fz_is_int (obj)) {
641 rotate = fz_to_int (obj);
643 else {
644 rotate = 0;
646 rotate += state.rotate;
648 p = &state.pagedims[state.pagedimcount - 1];
649 if ((state.pagedimcount == 0)
650 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
651 size_t size;
653 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
654 state.pagedims = realloc (state.pagedims, size);
655 if (!state.pagedims) {
656 err (1, "realloc pagedims to " FMT_s " (%d elems)",
657 size, state.pagedimcount + 1);
659 p = &state.pagedims[state.pagedimcount++];
660 p->rotate = rotate;
661 p->box = box;
662 p->pageno = pageno;
665 end = now ();
666 printd (state.sock, "T Processed %d pages in %f seconds",
667 state.pagecount, end - start);
670 static void layout (void)
672 int pindex;
673 fz_matrix ctm;
674 fz_rect box, box2;
675 double zoom, w, maxw = 0;
676 struct pagedim *p = state.pagedims;
678 if (state.proportional) {
679 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
680 box.x0 = MIN (p->box.x0, p->box.x1);
681 box.y0 = MIN (p->box.y0, p->box.y1);
682 box.x1 = MAX (p->box.x0, p->box.x1);
683 box.y1 = MAX (p->box.y0, p->box.y1);
685 ctm = fz_identity;
686 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
687 ctm = fz_concat (ctm, fz_rotate (p->rotate));
688 box2 = fz_transform_rect (ctm, box);
689 w = box2.x1 - box2.x0;
690 maxw = MAX (w, maxw);
694 p = state.pagedims;
695 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
696 box.x0 = MIN (p->box.x0, p->box.x1);
697 box.y0 = MIN (p->box.y0, p->box.y1);
698 box.x1 = MAX (p->box.x0, p->box.x1);
699 box.y1 = MAX (p->box.y0, p->box.y1);
701 ctm = fz_identity;
702 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
703 ctm = fz_concat (ctm, fz_rotate (p->rotate));
704 box2 = fz_transform_rect (ctm, box);
705 w = box2.x1 - box2.x0;
707 if (state.proportional) {
708 double scale = w / maxw;
709 zoom = (state.w / w) * scale;
711 else {
712 zoom = state.w / w;
714 ctm = fz_identity;
715 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
716 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
717 memcpy (&p->ctm1, &ctm, sizeof (ctm));
718 ctm = fz_concat (ctm, fz_rotate (p->rotate));
719 p->bbox = fz_round_rect (fz_transform_rect (ctm, box));
720 p->left = state.proportional ? ((maxw - w) * zoom) / 2.0 : 0;
721 memcpy (&p->ctm, &ctm, sizeof (ctm));
724 while (p-- != state.pagedims) {
725 printd (state.sock, "l %d %d %d %d",
726 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0,
727 p->left);
731 static void recurse_outline (pdf_outline *outline, int level)
733 while (outline) {
734 int i;
735 fz_obj *obj;
736 int pageno = -1;
737 int top = 0, h = 0;
739 if (!outline->link) goto next;
741 obj = outline->link->dest;
742 if (fz_is_indirect (obj)) {
743 obj = fz_resolve_indirect (obj);
745 if (fz_is_array (obj)) {
746 fz_obj *obj2;
748 obj2 = fz_array_get (obj, 0);
749 if (fz_is_int (obj2)) {
750 pageno = fz_to_int (obj2);
752 else {
753 pageno = pdf_find_page_number (state.xref, obj2);
756 if (fz_array_len (obj) > 3) {
757 fz_point p;
758 fz_obj *xo, *yo;
760 xo = fz_array_get (obj, 2);
761 yo = fz_array_get (obj, 3);
762 if (!fz_is_null (xo) && !fz_is_null (yo)) {
763 struct pagedim *pagedim = state.pagedims;
765 for (i = 0; i < state.pagedimcount; ++i) {
766 if (state.pagedims[i].pageno > pageno)
767 break;
768 pagedim = &state.pagedims[i];
770 p.x = fz_to_int (xo);
771 p.y = fz_to_int (yo);
772 p = fz_transform_point (pagedim->ctm, p);
773 h = pagedim->bbox.y1 - pagedim->bbox.y0;
774 top = p.y;
778 else {
779 pageno = pdf_find_page_number (state.xref, outline->link->dest);
782 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
783 if (pageno > 0) {
784 printd (state.sock, "o %d %d %d %d %s",
785 level, pageno, top, h, outline->title);
787 next:
788 if (outline->child) {
789 recurse_outline (outline->child, level + 1);
791 outline = outline->next;
795 static void process_outline (void)
797 pdf_outline *outline;
799 if (!state.needoutline) return;
801 state.needoutline = 0;
802 outline = pdf_load_outline (state.xref);
803 if (outline) {
804 recurse_outline (outline, 0);
805 pdf_free_outline (outline);
809 static int comparespans (const void *l, const void *r)
811 fz_text_span const *const*ls = l;
812 fz_text_span const *const*rs = r;
813 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
816 /* wishful thinking function */
817 static void search (regex_t *re, int pageno, int y, int forward)
819 int i, j;
820 int ret;
821 char *p;
822 char buf[256];
823 fz_matrix ctm;
824 fz_error error;
825 fz_device *tdev;
826 pdf_page *drawpage;
827 fz_text_span *text, *span, **pspan;
828 struct pagedim *pdim, *pdimprev;
829 int stop = 0;
830 int niters = 0;
831 int nspans;
832 double start, end;
834 start = now ();
835 while (pageno >= 0 && pageno < state.pagecount && !stop) {
836 if (niters++ == 5) {
837 pdf_age_store (state.xref->store, 3);
838 niters = 0;
839 if (hasdata (state.sock)) {
840 printd (state.sock, "T attention requested aborting search at %d",
841 pageno);
842 stop = 1;
844 else {
845 printd (state.sock, "T searching in page %d", pageno);
848 pdimprev = NULL;
849 for (i = 0; i < state.pagedimcount; ++i) {
850 pdim = &state.pagedims[i];
851 if (pdim->pageno == pageno) {
852 goto found;
854 if (pdim->pageno > pageno) {
855 pdim = pdimprev;
856 goto found;
858 pdimprev = pdim;
860 pdim = pdimprev;
861 found:
863 error = pdf_load_page (&drawpage, state.xref, pageno);
864 if (error)
865 die (error);
867 ctm = fz_rotate (pdim->rotate);
869 text = fz_new_text_span ();
870 tdev = fz_new_text_device (text);
871 error = pdf_run_page (state.xref, drawpage, tdev, pdim->ctm1);
872 if (error) die (error);
873 fz_free_device (tdev);
875 nspans = 0;
876 for (span = text; span; span = span->next) {
877 nspans++;
879 pspan = malloc (sizeof (void *) * nspans);
880 if (!pspan) {
881 err (1, "malloc span pointers " FMT_s, sizeof (void *) * nspans);
883 for (i = 0, span = text; span; span = span->next, ++i) {
884 pspan[i] = span;
886 qsort (pspan, nspans, sizeof (fz_text_span *), comparespans);
888 j = forward ? 0 : nspans - 1;
889 while (nspans--) {
890 regmatch_t rm;
892 span = pspan[j];
893 j += forward ? 1 : -1;
894 p = buf;
895 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
896 if (forward) {
897 if (span->text[i].bbox.y0 < y + 1) {
898 continue;
901 else {
902 if (span->text[i].bbox.y0 > y - 1) {
903 continue;
906 if (span->text[i].c < 256) {
907 *p++ = span->text[i].c;
909 else {
910 *p++ = '?';
913 if (p == buf) {
914 continue;
916 *p++ = 0;
918 ret = regexec (re, buf, 1, &rm, 0);
919 if (ret) {
920 if (ret != REG_NOMATCH) {
921 size_t size;
922 char errbuf[80];
923 size = regerror (ret, re, errbuf, sizeof (errbuf));
924 printd (state.sock,
925 "T regexec error `%.*s'",
926 (int) size, errbuf);
927 fz_free_text_span (text);
928 pdf_free_page (drawpage);
929 free (pspan);
930 return;
933 else {
934 int xoff, yoff;
935 fz_bbox *sb, *eb;
936 fz_point p1, p2, p3, p4;
938 xoff = pdim->left - pdim->bbox.x0;
939 yoff = -pdim->bbox.y0;
941 sb = &span->text[rm.rm_so].bbox;
942 eb = &span->text[rm.rm_eo - 1].bbox;
944 p1.x = sb->x0;
945 p1.y = sb->y0;
946 p2.x = eb->x1;
947 p2.y = sb->y0;
948 p3.x = eb->x1;
949 p3.y = eb->y1;
950 p4.x = sb->x0;
951 p4.y = eb->y1;
953 p1 = fz_transform_point (ctm, p1);
954 p2 = fz_transform_point (ctm, p2);
955 p3 = fz_transform_point (ctm, p3);
956 p4 = fz_transform_point (ctm, p4);
958 if (!stop) {
959 printd (state.sock, "F %d %d %f %f %f %f %f %f %f %f",
960 pageno, 1,
961 p1.x + xoff, p1.y + yoff,
962 p2.x + xoff, p2.y + yoff,
963 p3.x + xoff, p3.y + yoff,
964 p4.x + xoff, p4.y + yoff);
966 printd (state.sock, "T found at %d `%.*s' in %f sec",
967 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
968 now () - start);
970 else {
971 printd (state.sock, "R %d %d %f %f %f %f %f %f %f %f",
972 pageno, 2,
973 p1.x + xoff, p1.y + yoff,
974 p2.x + xoff, p2.y + yoff,
975 p3.x + xoff, p3.y + yoff,
976 p4.x + xoff, p4.y + yoff);
978 stop = 1;
981 if (forward) {
982 pageno += 1;
983 y = 0;
985 else {
986 pageno -= 1;
987 y = INT_MAX;
989 fz_free_text_span (text);
990 pdf_free_page (drawpage);
991 free (pspan);
993 end = now ();
994 if (!stop) {
995 printd (state.sock, "T no matches %f sec", end - start);
997 printd (state.sock, "D");
1000 static
1001 #ifdef _WIN32
1002 DWORD _stdcall
1003 #else
1004 void *
1005 #endif
1006 mainloop (void *unused)
1008 char *p = NULL;
1009 int len, ret, oldlen = 0;
1011 for (;;) {
1012 len = readlen (state.sock);
1013 if (len == 0) {
1014 errx (1, "readlen returned 0");
1017 if (oldlen < len + 1) {
1018 p = realloc (p, len + 1);
1019 if (!p) {
1020 err (1, "realloc %d failed", len + 1);
1022 oldlen = len + 1;
1024 readdata (state.sock, p, len);
1025 p[len] = 0;
1027 if (!strncmp ("open", p, 4)) {
1028 fz_obj *obj;
1029 size_t filenamelen;
1030 char *password;
1031 char *filename = p + 5;
1033 filenamelen = strlen (filename);
1034 password = filename + filenamelen + 1;
1036 openxref (filename, password);
1037 initpdims ();
1039 obj = fz_dict_gets (state.xref->trailer, "Info");
1040 if (obj) {
1041 char *s;
1043 obj = fz_dict_gets (obj, "Title");
1044 s = pdf_to_utf8 (obj);
1045 if (*s) {
1046 printd (state.sock, "t %s", s);
1048 fz_free (s);
1051 state.needoutline = 1;
1053 else if (!strncmp ("free", p, 4)) {
1054 void *ptr;
1056 ret = sscanf (p + 4, " %p", &ptr);
1057 if (ret != 1) {
1058 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
1060 unlinkpage (ptr);
1061 state.pig = ptr;
1063 else if (!strncmp ("search", p, 6)) {
1064 int icase, pageno, y, ret, len2, forward;
1065 char *pattern;
1066 regex_t re;
1068 ret = sscanf (p + 6, " %d %d %d %d,%n",
1069 &icase, &pageno, &y, &forward, &len2);
1070 if (ret != 4) {
1071 errx (1, "malformed search `%s' ret=%d", p, ret);
1074 pattern = p + 6 + len2;
1075 ret = regcomp (&re, pattern,
1076 REG_EXTENDED | (icase ? REG_ICASE : 0));
1077 if (ret) {
1078 char errbuf[80];
1079 size_t size;
1081 size = regerror (ret, &re, errbuf, sizeof (errbuf));
1082 printd (state.sock, "T regcomp failed `%.*s'",
1083 (int) size, errbuf);
1085 else {
1086 search (&re, pageno, y, forward);
1087 regfree (&re);
1090 else if (!strncmp ("geometry", p, 8)) {
1091 int w, h;
1093 printd (state.sock, "c");
1094 ret = sscanf (p + 8, " %d %d", &w, &h);
1095 if (ret != 2) {
1096 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
1099 lock ("geometry");
1100 state.h = h;
1101 if (w != state.w) {
1102 int i;
1103 state.w = w;
1104 for (i = 0; i < state.texcount; ++i) {
1105 state.texowners[i].slice = NULL;
1108 layout ();
1109 process_outline ();
1110 unlock ("geometry");
1111 printd (state.sock, "C %d", state.pagecount);
1113 else if (!strncmp ("reinit", p, 6)) {
1114 float rotate;
1115 int proportional;
1117 printd (state.sock, "c");
1118 ret = sscanf (p + 6, " %f %d", &rotate, &proportional);
1119 if (ret != 2) {
1120 errx (1, "bad rotate line `%.*s' ret=%d", len, p, ret);
1122 lock ("reinit");
1123 state.rotate = rotate;
1124 state.proportional = proportional;
1125 state.pagedimcount = 0;
1126 free (state.pagedims);
1127 state.pagedims = NULL;
1128 initpdims ();
1129 layout ();
1130 process_outline ();
1131 if (state.pig) {
1132 freepage (state.pig);
1133 state.pig = NULL;
1135 unlock ("reinit");
1136 printd (state.sock, "C %d", state.pagecount);
1138 else if (!strncmp ("render", p, 6)) {
1139 int pageno, pindex, w, h, ret;
1140 struct page *page;
1142 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
1143 if (ret != 4) {
1144 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
1147 lock ("render");
1148 page = render (pageno, pindex);
1149 unlock ("render");
1151 printd (state.sock, "r %d %d %d %d %d %d %p",
1152 pageno,
1153 state.w,
1154 state.h,
1155 state.rotate,
1156 state.proportional,
1157 w * h * 4,
1158 page);
1160 else if (!strncmp ("info", p, 4)) {
1161 pdfinfo ();
1163 else if (!strncmp ("interrupt", p, 9)) {
1164 printd (state.sock, "V interrupted");
1166 else {
1167 errx (1, "unknown command %.*s", len, p);
1170 return 0;
1173 static void showsel (struct page *page, int oy)
1175 int ox;
1176 fz_bbox bbox;
1177 fz_text_span *span;
1178 struct mark first, last;
1180 first = page->fmark;
1181 last = page->lmark;
1183 if (!first.span || !last.span) return;
1185 glEnable (GL_BLEND);
1186 glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA);
1187 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1189 ox = -page->pixmap->x + page->pagedim.left;
1190 oy = -page->pixmap->y + oy;
1191 for (span = first.span; span; span = span->next) {
1192 int i, j, k;
1194 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0;
1196 j = 0;
1197 k = span->len - 1;
1199 if (span == page->fmark.span && span == page->lmark.span) {
1200 j = MIN (first.i, last.i);
1201 k = MAX (first.i, last.i);
1203 else if (span == first.span) {
1204 j = first.i;
1206 else if (span == last.span) {
1207 k = last.i;
1210 for (i = j; i <= k; ++i) {
1211 bbox = fz_union_bbox (bbox, span->text[i].bbox);
1213 lprintf ("%d %d %d %d oy=%d ox=%d\n",
1214 bbox.x0,
1215 bbox.y0,
1216 bbox.x1,
1217 bbox.y1,
1218 oy, ox);
1220 glRecti (bbox.x0 + ox, bbox.y0 + oy, bbox.x1 + ox, bbox.y1 + oy);
1222 if (span == last.span) break;
1224 glDisable (GL_BLEND);
1227 static void highlightlinks (struct page *page, int yoff)
1229 pdf_link *link;
1230 int xoff;
1232 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1233 glEnable (GL_LINE_STIPPLE);
1234 glLineStipple (0.5, 0xcccc);
1236 xoff = page->pagedim.left - page->pixmap->x;
1237 yoff -= page->pixmap->y;
1239 glBegin (GL_QUADS);
1240 for (link = page->drawpage->links; link; link = link->next) {
1241 fz_point p1, p2, p3, p4;
1242 fz_matrix ctm = page->pagedim.ctm;
1244 p1.x = link->rect.x0;
1245 p1.y = link->rect.y0;
1247 p2.x = link->rect.x1;
1248 p2.y = link->rect.y0;
1250 p3.x = link->rect.x1;
1251 p3.y = link->rect.y1;
1253 p4.x = link->rect.x0;
1254 p4.y = link->rect.y1;
1256 p1 = fz_transform_point (ctm, p1);
1257 p2 = fz_transform_point (ctm, p2);
1258 p3 = fz_transform_point (ctm, p3);
1259 p4 = fz_transform_point (ctm, p4);
1261 switch (link->kind) {
1262 case PDF_LINK_GOTO: glColor3ub (255, 0, 0); break;
1263 case PDF_LINK_URI: glColor3ub (0, 0, 255); break;
1264 default: glColor3ub (0, 0, 0); break;
1267 glVertex2f (p1.x + xoff, p1.y + yoff);
1268 glVertex2f (p2.x + xoff, p2.y + yoff);
1269 glVertex2f (p3.x + xoff, p3.y + yoff);
1270 glVertex2f (p4.x + xoff, p4.y + yoff);
1272 glEnd ();
1274 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1275 glDisable (GL_LINE_STIPPLE);
1278 static void upload2 (struct page *page, int slicenum, const char *cap)
1280 int i;
1281 int w;
1282 double start, end;
1283 struct slice *slice = &page->slices[slicenum];
1285 w = page->pixmap->w;
1287 ARSERT (w == slice->w);
1288 if (slice->texindex != -1
1289 && state.texowners[slice->texindex].slice == slice) {
1290 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1292 else {
1293 int subimage = 0;
1294 int index = (state.texindex++ % state.texcount);
1295 size_t offset = 0;
1297 for (i = 0; i < slicenum; ++i) {
1298 offset += w * page->slices[i].h * 4;
1301 if (state.texowners[index].w == slice->w) {
1302 if (state.texowners[index].h >= slice->h ) {
1303 subimage = 1;
1305 else {
1306 state.texowners[index].h = slice->h;
1309 else {
1310 state.texowners[index].h = slice->h;
1313 state.texowners[index].slice = slice;
1314 state.texowners[index].w = slice->w;
1315 slice->texindex = index;
1317 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1318 start = now ();
1319 if (subimage) {
1321 GLenum err = glGetError ();
1322 if (err != GL_NO_ERROR) {
1323 printf ("\033[0;31mERROR1 %d %d %#x\033[0m\n", w, slice->h, err);
1324 abort ();
1327 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1332 slice->h,
1333 state.texform,
1334 state.texty,
1335 page->pixmap->samples + offset
1338 GLenum err = glGetError ();
1339 if (err != GL_NO_ERROR) {
1340 printf ("\033[0;31mERROR %d %d %#x\033[0m\n", w, slice->h, err);
1341 abort ();
1345 else {
1346 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1348 GL_RGBA8,
1350 slice->h,
1352 state.texform,
1353 state.texty,
1354 page->pixmap->samples + offset
1358 end = now ();
1359 (void) start;
1360 (void) end;
1361 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
1362 subimage ? "sub" : "img",
1363 page->pageno, slicenum,
1364 slice->w, slice->h,
1365 state.texids[slice->texindex],
1366 end - start);
1370 CAMLprim value ml_draw (value args_v, value ptr_v)
1372 CAMLparam2 (args_v, ptr_v);
1373 int dispy = Int_val (Field (args_v, 0));
1374 int w = Int_val (Field (args_v, 1));
1375 int h = Int_val (Field (args_v, 2));
1376 int py = Int_val (Field (args_v, 3));
1377 int hlinks = Bool_val (Field (args_v, 4));
1378 char *s = String_val (ptr_v);
1379 int ret;
1380 void *ptr;
1381 struct page *page;
1382 int slicenum = 0;
1383 int yoff = dispy - py;
1385 ret = sscanf (s, "%p", &ptr);
1386 if (ret != 1) {
1387 errx (1, "cannot parse pointer `%s'", s);
1389 page = ptr;
1391 w = page->pixmap->w;
1393 ARSERT (h >= 0 && "ml_draw wrong h");
1394 ARSERT (py <= page->pixmap->h && "ml_draw wrong py");
1396 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1398 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
1399 struct slice *slice = &page->slices[slicenum];
1400 if (slice->h > py) {
1401 break;
1403 py -= slice->h;
1406 h = MIN (state.h, h);
1407 while (h) {
1408 int th, left;
1409 struct slice *slice = &page->slices[slicenum];
1411 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
1413 th = MIN (h, slice->h - py);
1414 upload2 (page, slicenum, "upload");
1416 left = page->pagedim.left;
1417 glBegin (GL_QUADS);
1419 glTexCoord2i (0, py);
1420 glVertex2i (left, dispy);
1422 glTexCoord2i (w, py);
1423 glVertex2i (left+w, dispy);
1425 glTexCoord2i (w, py+th);
1426 glVertex2i (left+w, dispy + th);
1428 glTexCoord2i (0, py+th);
1429 glVertex2i (left, dispy + th);
1431 glEnd ();
1433 h -= th;
1434 py = 0;
1435 dispy += th;
1436 slicenum += 1;
1439 showsel (page, yoff);
1440 if (hlinks) highlightlinks (page, yoff);
1441 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1443 CAMLreturn (Val_unit);
1446 static pdf_link *getlink (struct page *page, int x, int y)
1448 fz_point p;
1449 fz_matrix ctm;
1450 pdf_link *link;
1452 p.x = x + page->pixmap->x;
1453 p.y = y + page->pixmap->y;
1455 ctm = fz_invert_matrix (page->pagedim.ctm);
1456 p = fz_transform_point (ctm, p);
1458 for (link = page->drawpage->links; link; link = link->next) {
1459 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1460 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1461 return link;
1465 return NULL;
1468 static void ensuretext (struct page *page)
1470 if (!page->text) {
1471 fz_error error;
1472 fz_device *tdev;
1474 page->text = fz_new_text_span ();
1475 tdev = fz_new_text_device (page->text);
1476 error = pdf_run_page (state.xref, page->drawpage, tdev,
1477 page->pagedim.ctm);
1478 if (error) die (error);
1479 fz_free_device (tdev);
1483 CAMLprim value ml_whatsunder (value ptr_v, value x_v, value y_v)
1485 CAMLparam3 (ptr_v, x_v, y_v);
1486 CAMLlocal3 (ret_v, tup_v, str_v);
1487 pdf_link *link;
1488 struct page *page;
1489 char *s = String_val (ptr_v);
1491 ret_v = Val_int (0);
1492 if (trylock ("ml_whatsunder")) {
1493 goto done;
1496 page = parse_pointer ("ml_whatsunder", s);
1497 link = getlink (page, Int_val (x_v), Int_val (y_v));
1498 if (link) {
1499 switch (link->kind) {
1500 case PDF_LINK_GOTO:
1502 int pageno;
1503 fz_point p;
1504 fz_obj *obj;
1506 pageno = -1;
1507 p.x = 0;
1508 p.y = 0;
1510 if (fz_is_array (link->dest)) {
1511 obj = fz_array_get (link->dest, 0);
1512 if (fz_is_indirect (obj)) {
1513 pageno = pdf_find_page_number (state.xref, obj);
1515 else if (fz_is_int (obj)) {
1516 pageno = fz_to_int (obj);
1519 if (fz_array_len (link->dest) > 3) {
1520 fz_obj *xo, *yo;
1522 xo = fz_array_get (link->dest, 2);
1523 yo = fz_array_get (link->dest, 3);
1524 if (!fz_is_null (xo) && !fz_is_null (yo)) {
1525 p.x = fz_to_int (xo);
1526 p.y = fz_to_int (yo);
1527 p = fz_transform_point (page->pagedim.ctm, p);
1531 else {
1532 pageno = pdf_find_page_number (state.xref, link->dest);
1534 tup_v = caml_alloc_tuple (2);
1535 ret_v = caml_alloc_small (1, 1);
1536 Field (tup_v, 0) = Val_int (pageno);
1537 Field (tup_v, 1) = Val_int (p.y);
1538 Field (ret_v, 0) = tup_v;
1540 break;
1542 case PDF_LINK_URI:
1543 str_v = caml_copy_string (fz_to_str_buf (link->dest));
1544 ret_v = caml_alloc_small (1, 0);
1545 Field (ret_v, 0) = str_v;
1546 break;
1548 default:
1549 printd (state.sock, "T unhandled link kind %d", link->kind);
1550 break;
1553 else {
1554 int i, x, y;
1555 fz_text_span *span;
1557 ensuretext (page);
1558 x = Int_val (x_v) + page->pixmap->x;
1559 y = Int_val (y_v) + page->pixmap->y;
1561 for (span = page->text; span; span = span->next) {
1562 for (i = 0; i < span->len; ++i) {
1563 fz_bbox *b;
1564 b = &span->text[i].bbox;
1565 if ((x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1)) {
1566 const char *n2 =
1567 span->font && span->font->name
1568 ? span->font->name
1569 : "Span has no font name"
1571 #ifdef FT_FREETYPE_H
1572 FT_FaceRec *face = span->font->ft_face;
1573 if (face && face->family_name) {
1574 char *s;
1575 char *n1 = face->family_name;
1576 size_t l1 = strlen (n1);
1577 size_t l2 = strlen (n2);
1579 if (l1 != l2 || memcmp (n1, n2, l1)) {
1580 s = malloc (l1 + l2 + 2);
1581 if (s) {
1582 memcpy (s, n2, l2);
1583 s[l2] = '=';
1584 memcpy (s + l2 + 1, n1, l1 + 1);
1585 str_v = caml_copy_string (s);
1586 free (s);
1590 if (str_v == 0) {
1591 str_v = caml_copy_string (n2);
1593 #else
1594 str_v = caml_copy_string (n2);
1595 #endif
1596 ret_v = caml_alloc_small (1, 2);
1597 Field (ret_v, 0) = str_v;
1598 goto unlock;
1603 unlock:
1604 unlock ("ml_whatsunder");
1606 done:
1607 CAMLreturn (ret_v);
1610 CAMLprim value ml_seltext (value ptr_v, value rect_v, value oy_v)
1612 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1613 fz_bbox *b;
1614 struct page *page;
1615 fz_text_span *span;
1616 struct mark first, last;
1617 int i, x0, x1, y0, y1, oy, left;
1618 char *s = String_val (ptr_v);
1620 if (trylock ("ml_seltext")) {
1621 goto done;
1624 page = parse_pointer ("ml_seltext", s);
1625 ensuretext (page);
1627 oy = Int_val (oy_v);
1628 x0 = Int_val (Field (rect_v, 0));
1629 y0 = Int_val (Field (rect_v, 1));
1630 x1 = Int_val (Field (rect_v, 2));
1631 y1 = Int_val (Field (rect_v, 3));
1633 left = page->pagedim.left;
1634 if (0) {
1635 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1636 glColor3ub (128, 128, 128);
1637 glRecti (x0, y0, x1, y1);
1638 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1641 x0 += page->pixmap->x - left;
1642 y0 += page->pixmap->y - oy;
1643 x1 += page->pixmap->x - left;
1644 y1 += page->pixmap->y - oy;
1646 first.span = NULL;
1647 last.span = NULL;
1649 last.i = first.i = 0;
1650 first.span = page->text;
1651 for (span = page->text; span; span = span->next) {
1652 for (i = 0; i < span->len; ++i) {
1653 b = &span->text[i].bbox;
1654 int selected = 0;
1656 if (x0 >= b->x0 && x0 <= b->x1 && y0 >= b->y0 && y0 <= b->y1) {
1657 first.i = i;
1658 first.span = span;
1659 selected = 1;
1661 if (x1 >= b->x0 && x1 <= b->x1 && y1 >= b->y0 && y1 <= b->y1) {
1662 last.i = i;
1663 last.span = span;
1664 selected = 1;
1666 if (0 && selected) {
1667 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1668 glColor3ub (128, 128, 128);
1669 glRecti (b->x0+left, b->y0, b->x1+left, b->y1);
1670 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1675 if (y1 < y0 || x1 < x0) {
1676 int swap = 0;
1678 if (first.span == last.span) {
1679 swap = 1;
1681 else {
1682 if (y1 < y0) {
1683 for (span = first.span; span && span != last.span;
1684 span = span->next) {
1685 if (span->eol) {
1686 swap = 1;
1687 break;
1693 if (swap) {
1694 i = first.i;
1695 span = first.span;
1696 first.i = last.i;
1697 first.span = last.span;
1698 last.i = i;
1699 last.span = span;
1703 page->fmark = first;
1704 page->lmark = last;
1706 unlock ("ml_seltext");
1708 done:
1709 CAMLreturn (Val_unit);
1712 static int pipespan (FILE *f, fz_text_span *span, int a, int b)
1714 char buf[4];
1715 int i, len, ret;
1717 for (i = a; i <= b; ++i) {
1718 len = runetochar (buf, &span->text[i].c);
1719 ret = fwrite (buf, len, 1, f);
1721 if (ret != 1) {
1722 printd (state.sock, "T failed to write %d bytes ret=%d: %s",
1723 len, ret, strerror (errno));
1724 return -1;
1727 return 0;
1730 CAMLprim value ml_copysel (value ptr_v)
1732 CAMLparam1 (ptr_v);
1733 FILE *f;
1734 struct page *page;
1735 char *s = String_val (ptr_v);
1737 if (trylock ("ml_copysel")) {
1738 goto done;
1741 if (!*s) {
1742 close:
1743 #ifdef USE_XSEL
1744 if (state.xselpipe) {
1745 int ret = pclose (state.xselpipe);
1746 if (ret) {
1747 printd (state.sock, "T failed to close xsel pipe `%s'",
1748 strerror (errno));
1750 state.xselpipe = NULL;
1752 #else
1753 printf ("========================================\n");
1754 #endif
1756 else {
1757 fz_text_span *span;
1759 page = parse_pointer ("ml_sopysel", s);
1761 if (!page->fmark.span || !page->lmark.span) {
1762 printd (state.sock, "T nothing to copy");
1763 goto unlock;
1766 f = stdout;
1767 #ifdef USE_XSEL
1768 if (!state.xselpipe) {
1769 state.xselpipe = popen ("xsel -i", "w");
1770 if (!state.xselpipe) {
1771 printd (state.sock, "T failed to open xsel pipe `%s'",
1772 strerror (errno));
1774 else {
1775 f = state.xselpipe;
1778 else {
1779 f = state.xselpipe;
1781 #endif
1783 for (span = page->fmark.span;
1784 span && span != page->lmark.span->next;
1785 span = span->next) {
1786 int a = span == page->fmark.span ? page->fmark.i : 0;
1787 int b = span == page->lmark.span ? page->lmark.i : span->len - 1;
1788 if (pipespan (f, span, a, b)) {
1789 goto close;
1791 if (span->eol) {
1792 if (putc ('\n', f) == EOF) {
1793 printd (state.sock, "T failed break line on xsel pipe `%s'",
1794 strerror (errno));
1795 goto close;
1799 page->lmark.span = NULL;
1800 page->fmark.span = NULL;
1803 unlock:
1804 unlock ("ml_copysel");
1806 done:
1807 CAMLreturn (Val_unit);
1810 CAMLprim value ml_getpdimrect (value pagedimno_v)
1812 CAMLparam1 (pagedimno_v);
1813 CAMLlocal1 (ret_v);
1814 int pagedimno = Int_val (pagedimno_v);
1815 fz_rect box;
1817 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1818 if (trylock ("ml_getpdimrect")) {
1819 box = fz_empty_rect;
1821 else {
1822 box = state.pagedims[pagedimno].box;
1823 unlock ("ml_getpdimrect");
1826 Store_double_field (ret_v, 0, box.x0);
1827 Store_double_field (ret_v, 1, box.x1);
1828 Store_double_field (ret_v, 2, box.y0);
1829 Store_double_field (ret_v, 3, box.y1);
1831 CAMLreturn (ret_v);
1834 CAMLprim value ml_zoom_for_height (value winw_v, value winh_v, value dw_v)
1836 CAMLparam3 (winw_v, winh_v, dw_v);
1837 CAMLlocal1 (ret_v);
1838 int i;
1839 double zoom = 1.0;
1840 double maxw = 0.0, maxh = 0.0;
1841 struct pagedim *p;
1842 double winw = Int_val (winw_v);
1843 double winh = Int_val (winh_v);
1844 double dw = Int_val (dw_v);
1845 double pw = 1.0, ph = 1.0, num, den;
1847 if (trylock ("ml_zoom_for_height")) {
1848 goto done;
1851 if (state.proportional) {
1852 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
1853 double x0, x1, w;
1855 x0 = MIN (p->box.x0, p->box.x1);
1856 x1 = MAX (p->box.x0, p->box.x1);
1858 w = x1 - x0;
1859 maxw = MAX (w, maxw);
1863 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
1864 double x0, x1, y0, y1, w, h, scaledh, scale;
1866 x0 = MIN (p->box.x0, p->box.x1);
1867 y0 = MIN (p->box.y0, p->box.y1);
1868 x1 = MAX (p->box.x0, p->box.x1);
1869 y1 = MAX (p->box.y0, p->box.y1);
1871 w = x1 - x0;
1872 h = y1 - y0;
1874 if (state.proportional) {
1875 scale = w / maxw;
1876 scaledh = h * scale;
1878 else {
1879 scale = 1.0;
1880 scaledh = h;
1883 if (scaledh > maxh) {
1884 maxh = scaledh;
1885 ph = scaledh;
1886 pw = w * scale;
1890 num = (winh * pw) + (ph * dw);
1891 den = ph * winw;
1892 zoom = num / den;
1894 unlock ("ml_zoom_for_height");
1895 done:
1896 ret_v = caml_copy_double (zoom);
1897 CAMLreturn (ret_v);
1900 CAMLprim value ml_init (value sock_v, value params_v)
1902 #ifndef _WIN32
1903 int ret;
1904 #endif
1905 CAMLparam2 (sock_v, params_v);
1907 state.rotate = Int_val (Field (params_v, 0));
1908 state.proportional = Bool_val (Field (params_v, 1));
1909 state.texcount = Int_val (Field (params_v, 2));
1910 state.sliceheight = Int_val (Field (params_v, 3));
1911 state.texform = GL_RGBA;
1912 state.texty = GL_UNSIGNED_BYTE;
1914 fz_accelerate ();
1915 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1916 if (!state.texids) {
1917 err (1, "calloc texids " FMT_s,
1918 state.texcount * sizeof (*state.texids));
1921 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1922 if (!state.texowners) {
1923 err (1, "calloc texowners " FMT_s,
1924 state.texcount * sizeof (*state.texowners));
1927 glGenTextures (state.texcount, state.texids);
1929 #ifdef _WIN32
1930 state.sock = Socket_val (sock_v);
1931 #else
1932 state.sock = Int_val (sock_v);
1933 #endif
1935 #ifdef _WIN32
1936 InitializeCriticalSection (&critsec);
1937 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
1938 if (state.thread == INVALID_HANDLE_VALUE) {
1939 errx (1, "CreateThread failed: %lx", GetLastError ());
1941 #else
1942 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1943 if (ret) {
1944 errx (1, "pthread_create: %s", strerror (ret));
1946 #endif
1948 CAMLreturn (Val_unit);