Remove tautology
[llpp.git] / link.c
bloba1d75adf90b4c16fa938c443f085d829fac87457
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 #elif defined __GNUC__
28 #define NORETURN __attribute__ ((noreturn))
29 #define UNUSED __attribute__ ((unused))
30 #else
31 #define NORETURN
32 #define UNUSED
33 #endif
35 #ifdef _WIN32
36 static void __declspec (noreturn) sockerr (int exitcode, const char *fmt, ...)
38 va_list ap;
40 va_start (ap, fmt);
41 vfprintf (stderr, fmt, ap);
42 va_end (ap);
43 fprintf (stderr, ": wsaerror 0x%x\n", WSAGetLastError ());
44 _exit (exitcode);
46 #else
47 #define FMT_ss "%zd"
48 #define FMT_s "%zu"
49 #define fionread_arg int
50 #define ioctlsocket ioctl
51 #define sockerr err
52 #include <unistd.h>
53 #endif
55 #include <regex.h>
56 #include <ctype.h>
57 #include <stdarg.h>
58 #include <limits.h>
60 #ifndef _WIN32
61 #include <pthread.h>
62 #include <sys/time.h>
63 #include <sys/types.h>
64 #include <sys/socket.h>
65 #include <sys/ioctl.h>
66 #endif
68 static void NORETURN err (int exitcode, const char *fmt, ...)
70 va_list ap;
71 int savederrno;
73 savederrno = errno;
74 va_start (ap, fmt);
75 vfprintf (stderr, fmt, ap);
76 va_end (ap);
77 fprintf (stderr, ": %s\n", strerror (savederrno));
78 fflush (stderr);
79 _exit (exitcode);
82 static void NORETURN errx (int exitcode, const char *fmt, ...)
84 va_list ap;
86 va_start (ap, fmt);
87 vfprintf (stderr, fmt, ap);
88 va_end (ap);
89 fputc ('\n', stderr);
90 fflush (stderr);
91 _exit (exitcode);
94 #ifdef __APPLE__
95 #include <OpenGL/gl.h>
96 #else
97 #include <GL/gl.h>
98 #endif
100 #ifndef GL_TEXTURE_RECTANGLE_ARB
101 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
102 #endif
104 #include <caml/fail.h>
105 #include <caml/alloc.h>
106 #include <caml/memory.h>
107 #include <caml/unixsupport.h>
109 #include <fitz.h>
110 #include <mupdf.h>
112 #if 0
113 #define lprintf printf
114 #else
115 #define lprintf(...)
116 #endif
118 #ifdef FT_FREETYPE_H
119 #include FT_FREETYPE_H
120 #endif
122 #define ARSERT(cond) for (;;) { \
123 if (!(cond)) { \
124 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
126 break; \
129 struct slice {
130 int texindex;
131 int w, h;
134 struct pagedim {
135 int pageno;
136 int rotate;
137 int left;
138 fz_rect box;
139 fz_bbox bbox;
140 fz_matrix ctm, ctm1;
143 struct page {
144 int pageno;
145 int slicecount;
146 fz_text_span *text;
147 fz_pixmap *pixmap;
148 pdf_page *drawpage;
149 struct pagedim pagedim;
150 struct mark {
151 int i;
152 fz_text_span *span;
153 } fmark, lmark;
154 struct slice slices[];
157 #if !defined _WIN32 && !defined __APPLE__
158 #define USE_XSEL
159 #endif
161 struct {
162 int sock;
163 int sliceheight;
164 struct page *pig;
165 struct pagedim *pagedims;
166 int pagecount;
167 int pagedimcount;
168 pdf_xref *xref;
169 fz_glyph_cache *cache;
170 int w, h;
172 int texindex;
173 int texcount;
174 GLuint *texids;
176 GLenum texform;
177 GLenum texty;
179 struct {
180 int w, h;
181 struct slice *slice;
182 } *texowners;
184 int rotate;
185 int proportional;
186 int needoutline;
188 #ifdef _WIN32
189 HANDLE thread;
190 #else
191 pthread_t thread;
192 #endif
193 FILE *xselpipe;
195 FT_Face face;
196 } state;
198 #ifdef _WIN32
199 static CRITICAL_SECTION critsec;
201 static void lock (void *unused)
203 (void) unused;
204 EnterCriticalSection (&critsec);
207 static void unlock (void *unused)
209 (void) unused;
210 LeaveCriticalSection (&critsec);
213 static int trylock (void *unused)
215 return TryEnterCriticalSection (&critsec) == 0;
217 #else
218 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
220 static void lock (const char *cap)
222 int ret = pthread_mutex_lock (&mutex);
223 if (ret) {
224 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
228 static void unlock (const char *cap)
230 int ret = pthread_mutex_unlock (&mutex);
231 if (ret) {
232 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
236 static int trylock (const char *cap)
238 int ret = pthread_mutex_trylock (&mutex);
240 if (ret && ret != EBUSY) {
241 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
243 return ret == EBUSY;
245 #endif
247 static void *parse_pointer (const char *cap, const char *s)
249 int ret;
250 void *ptr;
252 ret = sscanf (s, "%p", &ptr);
253 if (ret != 1) {
254 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
256 return ptr;
259 static int hasdata (int sock)
261 int ret;
262 fionread_arg avail;
263 ret = ioctlsocket (sock, FIONREAD, &avail);
264 if (ret) sockerr (1, "hasdata: FIONREAD error ret=%d", ret);
265 return avail > 0;
268 static double now (void)
270 struct timeval tv;
272 if (gettimeofday (&tv, NULL)) {
273 err (1, "gettimeofday");
275 return tv.tv_sec + tv.tv_usec*1e-6;
278 static void readdata (int fd, char *p, int size)
280 ssize_t n;
282 n = recv (fd, p, size, 0);
283 if (n - size) {
284 if (!n) errx (1, "EOF while reading");
285 sockerr (1, "recv (req %d, ret " FMT_ss ")", size, n);
289 static void writedata (int fd, char *p, int size)
291 char buf[4];
292 ssize_t n;
294 buf[0] = (size >> 24) & 0xff;
295 buf[1] = (size >> 16) & 0xff;
296 buf[2] = (size >> 8) & 0xff;
297 buf[3] = (size >> 0) & 0xff;
299 n = send (fd, buf, 4, 0);
300 if (n != 4) {
301 if (!n) errx (1, "EOF while writing length");
302 sockerr (1, "send " FMT_ss, n);
305 n = send (fd, p, size, 0);
306 if (n - size) {
307 if (!n) errx (1, "EOF while writing data");
308 sockerr (1, "send (req %d, ret " FMT_ss ")", size, n);
312 static void
313 #ifdef __GNUC__
314 __attribute__ ((format (printf, 2, 3)))
315 #endif
316 printd (int fd, const char *fmt, ...)
318 int size = 200, len;
319 va_list ap;
320 char *buf;
322 buf = malloc (size);
323 for (;;) {
324 if (!buf) err (errno, "malloc for temp buf (%d bytes) failed", size);
326 va_start (ap, fmt);
327 len = vsnprintf (buf, size, fmt, ap);
328 va_end (ap);
330 if (len > -1 && len < size) {
331 writedata (fd, buf, len);
332 break;
335 if (len > -1) {
336 size = len + 1;
338 else {
339 size *= 2;
341 buf = realloc (buf, size);
343 free (buf);
346 static void die (fz_error error)
348 fz_catch (error, "aborting");
349 if (state.xref)
350 pdf_free_xref (state.xref);
351 _exit (1);
354 static void openxref (char *filename, char *password)
356 int i;
357 fz_error error;
359 for (i = 0; i < state.texcount; ++i) {
360 state.texowners[i].slice = NULL;
363 if (state.cache) {
364 fz_free_glyph_cache (state.cache);
367 state.cache = fz_new_glyph_cache ();
368 if (!state.cache) {
369 errx (1, "fz_newglyph_cache failed");
372 if (state.xref) {
373 if (state.xref->store) {
374 pdf_free_store (state.xref->store);
375 state.xref->store = NULL;
377 pdf_free_xref (state.xref);
378 state.xref = NULL;
381 if (state.pagedims) {
382 free (state.pagedims);
383 state.pagedims = NULL;
385 state.pagedimcount = 0;
387 error = pdf_open_xref (&state.xref, filename, password);
388 if (error) {
389 die (error);
392 error = pdf_load_page_tree (state.xref);
393 if (error) {
394 die (error);
397 state.pagecount = pdf_count_pages (state.xref);
400 static void pdfinfo (void)
402 printd (state.sock, "i PDF version %d.%d\n",
403 state.xref->version / 10, state.xref->version % 10);
405 #if _XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L
407 FILE *f;
408 char *buf;
409 size_t size;
410 fz_obj *obj;
412 f = open_memstream (&buf, &size);
413 if (f) {
414 obj = fz_dict_gets (state.xref->trailer, "Info");
415 fz_fprint_obj (f, fz_resolve_indirect (obj), 0);
416 if (fclose (f)) err (1, "fclose on memstream failed");
417 if (size > 64*1024) size = 64*1024;
418 printd (state.sock, "i %.*s", (int) size, buf);
419 free (buf);
421 else {
422 printd (state.sock, "i error opening memstream: %s\n",
423 strerror (errno));
426 #endif
429 static int readlen (int fd)
431 ssize_t n;
432 unsigned char p[4];
434 n = recv (fd, p, 4, 0);
435 if (n != 4) {
436 if (!n) errx (1, "EOF while reading length");
437 sockerr (1, "recv " FMT_ss, n);
440 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
443 static void unlinkpage (struct page *page)
445 int i;
447 for (i = 0; i < page->slicecount; ++i) {
448 struct slice *s = &page->slices[i];
449 if (s->texindex != -1) {
450 if (state.texowners[s->texindex].slice == s) {
451 state.texowners[s->texindex].slice = NULL;
452 ARSERT (state.texowners[s->texindex].w == s->w);
453 ARSERT (state.texowners[s->texindex].h >= s->h);
459 static void freepage (struct page *page)
461 fz_drop_pixmap (page->pixmap);
463 unlinkpage (page);
465 if (page->text) {
466 fz_free_text_span (page->text);
468 if (page->drawpage) {
469 pdf_free_page (page->drawpage);
472 free (page);
475 static void subdivide (struct page *p)
477 int i;
478 int h = p->pixmap->h;
479 int th = MIN (h, state.sliceheight);
481 for (i = 0; i < p->slicecount; ++i) {
482 struct slice *s = &p->slices[i];
483 s->texindex = -1;
484 s->h = MIN (th, h);
485 s->w = p->pixmap->w;
486 h -= th;
490 static int compatpdims (struct pagedim *p1, struct pagedim *p2)
492 return p1->rotate == p2->rotate
493 && !memcmp (&p1->bbox, &p2->bbox, sizeof (p1->bbox))
494 && !memcmp (&p1->ctm, &p2->ctm, sizeof (p1->ctm));
497 #ifdef __ALTIVEC__
498 #include <altivec.h>
500 static int cacheline32bytes;
501 extern char **environ;
503 static void __attribute__ ((constructor)) clcheck (void)
505 char **envp = environ;
506 unsigned long *auxv;
508 while (*envp++);
510 for (auxv = (unsigned long *) envp; *auxv != 0; auxv += 2) {
511 if (*auxv == 19) {
512 cacheline32bytes = auxv[1] == 32;
513 return;
518 static void __attribute__ ((optimize ("O"))) clearpixmap (fz_pixmap *pixmap)
520 if (cacheline32bytes) {
521 intptr_t a1, a2, diff;
522 size_t sizea, i, size = pixmap->w * pixmap->h * pixmap->n;
523 vector unsigned char v = vec_splat_u8 (-1);
524 vector unsigned char *p;
526 a1 = a2 = (intptr_t) pixmap->samples;
527 a2 = (a1 + 31) & ~31;
528 diff = a2 - a1;
529 sizea = size - diff;
530 p = (void *) a2;
532 while (a1 != a2) *(char *) a1++ = 0xff;
533 for (i = 0; i < (sizea & ~31); i += 32) {
534 __asm volatile ("dcbz %0, %1"::"b"(a2),"r"(i));
535 vec_st (v, i, p);
536 vec_st (v, i + 16, p);
538 while (i < sizea) *((char *) a1 + i++) = 0xff;
540 else fz_clear_pixmap_with_color (pixmap, 0xff);
542 #else
543 #define clearpixmap(p) fz_clear_pixmap_with_color (p, 0xff)
544 #endif
546 static void *render (int pageno, int pindex)
548 fz_error error;
549 int slicecount;
550 struct page *page = NULL;
551 double start, end;
552 pdf_page *drawpage;
553 fz_device *idev;
554 struct pagedim *pagedim;
556 start = now ();
557 printd (state.sock, "V rendering %d", pageno);
559 pagedim = &state.pagedims[pindex];
560 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
561 + state.sliceheight - 1) / state.sliceheight;
562 slicecount += slicecount == 0;
564 if (state.pig) {
565 if (compatpdims (&state.pig->pagedim, pagedim)) {
566 page = state.pig;
567 if (page->text) {
568 fz_free_text_span (page->text);
569 page->text = NULL;
571 if (page->drawpage) {
572 pdf_free_page (page->drawpage);
573 page->drawpage = NULL;
576 else {
577 freepage (state.pig);
580 if (!page) {
581 page = calloc (sizeof (*page)
582 + (slicecount * sizeof (struct slice)), 1);
583 if (!page) {
584 err (1, "calloc page %d\n", pageno);
586 page->pixmap = fz_new_pixmap_with_rect (fz_device_rgb, pagedim->bbox);
589 page->slicecount = slicecount;
591 error = pdf_load_page (&drawpage, state.xref, pageno - 1);
592 if (error)
593 die (error);
595 clearpixmap (page->pixmap);
597 idev = fz_new_draw_device (state.cache, page->pixmap);
598 if (!idev)
599 die (fz_throw ("fz_newdrawdevice failed"));
600 error = pdf_run_page (state.xref, drawpage, idev, pagedim->ctm);
601 if (error)
602 die (fz_rethrow (error, "pdf_runpage failed"));
603 fz_free_device (idev);
605 page->drawpage = drawpage;
606 page->pagedim = *pagedim;
607 page->pageno = pageno;
608 subdivide (page);
609 end = now ();
611 pdf_age_store (state.xref->store, 3);
613 printd (state.sock, "V rendering %d took %f sec", pageno, end - start);
614 state.pig = NULL;
615 return page;
618 static void initpdims (void)
620 int pageno;
621 double start, end;
623 start = now ();
624 for (pageno = 0; pageno < state.pagecount; ++pageno) {
625 int rotate;
626 fz_rect box;
627 struct pagedim *p;
628 fz_obj *obj, *pageobj;
630 pageobj = state.xref->page_objs[pageno];
632 obj = fz_dict_gets (pageobj, "CropBox");
633 if (!fz_is_array (obj)) {
634 obj = fz_dict_gets (pageobj, "MediaBox");
635 if (!fz_is_array (obj)) {
636 die (fz_throw ("cannot find page bounds %d (%d Rd)",
637 fz_to_num (pageobj), fz_to_gen (pageobj)));
640 box = pdf_to_rect (obj);
642 obj = fz_dict_gets (pageobj, "Rotate");
643 if (fz_is_int (obj)) {
644 rotate = fz_to_int (obj);
646 else {
647 rotate = 0;
649 rotate += state.rotate;
651 p = &state.pagedims[state.pagedimcount - 1];
652 if ((state.pagedimcount == 0)
653 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
654 size_t size;
656 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
657 state.pagedims = realloc (state.pagedims, size);
658 if (!state.pagedims) {
659 err (1, "realloc pagedims to " FMT_s " (%d elems)",
660 size, state.pagedimcount + 1);
662 p = &state.pagedims[state.pagedimcount++];
663 p->rotate = rotate;
664 p->box = box;
665 p->pageno = pageno;
668 end = now ();
669 printd (state.sock, "T Processed %d pages in %f seconds",
670 state.pagecount, end - start);
673 static void layout (void)
675 int pindex;
676 fz_matrix ctm;
677 fz_rect box, box2;
678 double zoom, w, maxw = 0;
679 struct pagedim *p = state.pagedims;
681 if (state.proportional) {
682 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
683 box.x0 = MIN (p->box.x0, p->box.x1);
684 box.y0 = MIN (p->box.y0, p->box.y1);
685 box.x1 = MAX (p->box.x0, p->box.x1);
686 box.y1 = MAX (p->box.y0, p->box.y1);
688 ctm = fz_identity;
689 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
690 ctm = fz_concat (ctm, fz_rotate (p->rotate));
691 box2 = fz_transform_rect (ctm, box);
692 w = box2.x1 - box2.x0;
693 maxw = MAX (w, maxw);
697 p = state.pagedims;
698 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
699 box.x0 = MIN (p->box.x0, p->box.x1);
700 box.y0 = MIN (p->box.y0, p->box.y1);
701 box.x1 = MAX (p->box.x0, p->box.x1);
702 box.y1 = MAX (p->box.y0, p->box.y1);
704 ctm = fz_identity;
705 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
706 ctm = fz_concat (ctm, fz_rotate (p->rotate));
707 box2 = fz_transform_rect (ctm, box);
708 w = box2.x1 - box2.x0;
710 if (state.proportional) {
711 double scale = w / maxw;
712 zoom = (state.w / w) * scale;
714 else {
715 zoom = state.w / w;
717 ctm = fz_identity;
718 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
719 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
720 memcpy (&p->ctm1, &ctm, sizeof (ctm));
721 ctm = fz_concat (ctm, fz_rotate (p->rotate));
722 p->bbox = fz_round_rect (fz_transform_rect (ctm, box));
723 p->left = state.proportional ? ((maxw - w) * zoom) / 2.0 : 0;
724 memcpy (&p->ctm, &ctm, sizeof (ctm));
727 while (p-- != state.pagedims) {
728 printd (state.sock, "l %d %d %d %d",
729 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0,
730 p->left);
734 static void recurse_outline (pdf_outline *outline, int level)
736 while (outline) {
737 int i;
738 fz_obj *obj;
739 int pageno = -1;
740 int top = 0, h = 0;
742 if (!outline->link) goto next;
744 obj = outline->link->dest;
745 if (fz_is_indirect (obj)) {
746 obj = fz_resolve_indirect (obj);
748 if (fz_is_array (obj)) {
749 fz_obj *obj2;
751 obj2 = fz_array_get (obj, 0);
752 if (fz_is_int (obj2)) {
753 pageno = fz_to_int (obj2);
755 else {
756 pageno = pdf_find_page_number (state.xref, obj2);
759 if (fz_array_len (obj) > 3) {
760 fz_point p;
761 fz_obj *xo, *yo;
763 xo = fz_array_get (obj, 2);
764 yo = fz_array_get (obj, 3);
765 if (!fz_is_null (xo) && !fz_is_null (yo)) {
766 struct pagedim *pagedim = state.pagedims;
768 for (i = 0; i < state.pagedimcount; ++i) {
769 if (state.pagedims[i].pageno > pageno)
770 break;
771 pagedim = &state.pagedims[i];
773 p.x = fz_to_int (xo);
774 p.y = fz_to_int (yo);
775 p = fz_transform_point (pagedim->ctm, p);
776 h = pagedim->bbox.y1 - pagedim->bbox.y0;
777 top = p.y;
781 else {
782 pageno = pdf_find_page_number (state.xref, outline->link->dest);
785 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
786 if (pageno > 0) {
787 printd (state.sock, "o %d %d %d %d %s",
788 level, pageno, top, h, outline->title);
790 next:
791 if (outline->child) {
792 recurse_outline (outline->child, level + 1);
794 outline = outline->next;
798 static void process_outline (void)
800 pdf_outline *outline;
802 if (!state.needoutline) return;
804 state.needoutline = 0;
805 outline = pdf_load_outline (state.xref);
806 if (outline) {
807 recurse_outline (outline, 0);
808 pdf_free_outline (outline);
812 static int comparespans (const void *l, const void *r)
814 fz_text_span const *const*ls = l;
815 fz_text_span const *const*rs = r;
816 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
819 /* wishful thinking function */
820 static void search (regex_t *re, int pageno, int y, int forward)
822 int i, j;
823 int ret;
824 char *p;
825 char buf[256];
826 fz_matrix ctm;
827 fz_error error;
828 fz_device *tdev;
829 pdf_page *drawpage;
830 fz_text_span *text, *span, **pspan;
831 struct pagedim *pdim, *pdimprev;
832 int stop = 0;
833 int niters = 0;
834 int nspans;
835 double start, end;
837 start = now ();
838 while (pageno >= 0 && pageno < state.pagecount && !stop) {
839 if (niters++ == 5) {
840 pdf_age_store (state.xref->store, 3);
841 niters = 0;
842 if (hasdata (state.sock)) {
843 printd (state.sock, "T attention requested aborting search at %d",
844 pageno);
845 stop = 1;
847 else {
848 printd (state.sock, "T searching in page %d", pageno);
851 pdimprev = NULL;
852 for (i = 0; i < state.pagedimcount; ++i) {
853 pdim = &state.pagedims[i];
854 if (pdim->pageno == pageno) {
855 goto found;
857 if (pdim->pageno > pageno) {
858 pdim = pdimprev;
859 goto found;
861 pdimprev = pdim;
863 pdim = pdimprev;
864 found:
866 error = pdf_load_page (&drawpage, state.xref, pageno);
867 if (error)
868 die (error);
870 ctm = fz_rotate (pdim->rotate);
872 text = fz_new_text_span ();
873 tdev = fz_new_text_device (text);
874 error = pdf_run_page (state.xref, drawpage, tdev, pdim->ctm1);
875 if (error) die (error);
876 fz_free_device (tdev);
878 nspans = 0;
879 for (span = text; span; span = span->next) {
880 nspans++;
882 pspan = malloc (sizeof (void *) * nspans);
883 if (!pspan) {
884 err (1, "malloc span pointers " FMT_s, sizeof (void *) * nspans);
886 for (i = 0, span = text; span; span = span->next, ++i) {
887 pspan[i] = span;
889 qsort (pspan, nspans, sizeof (fz_text_span *), comparespans);
891 j = forward ? 0 : nspans - 1;
892 while (nspans--) {
893 regmatch_t rm;
895 span = pspan[j];
896 j += forward ? 1 : -1;
897 p = buf;
898 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
899 if (forward) {
900 if (span->text[i].bbox.y0 < y + 1) {
901 continue;
904 else {
905 if (span->text[i].bbox.y0 > y - 1) {
906 continue;
909 if (span->text[i].c < 256) {
910 *p++ = span->text[i].c;
912 else {
913 *p++ = '?';
916 if (p == buf) {
917 continue;
919 *p++ = 0;
921 ret = regexec (re, buf, 1, &rm, 0);
922 if (ret) {
923 if (ret != REG_NOMATCH) {
924 size_t size;
925 char errbuf[80];
926 size = regerror (ret, re, errbuf, sizeof (errbuf));
927 printd (state.sock,
928 "T regexec error `%.*s'",
929 (int) size, errbuf);
930 fz_free_text_span (text);
931 pdf_free_page (drawpage);
932 free (pspan);
933 return;
936 else {
937 int xoff, yoff;
938 fz_bbox *sb, *eb;
939 fz_point p1, p2, p3, p4;
941 xoff = pdim->left - pdim->bbox.x0;
942 yoff = -pdim->bbox.y0;
944 sb = &span->text[rm.rm_so].bbox;
945 eb = &span->text[rm.rm_eo - 1].bbox;
947 p1.x = sb->x0;
948 p1.y = sb->y0;
949 p2.x = eb->x1;
950 p2.y = sb->y0;
951 p3.x = eb->x1;
952 p3.y = eb->y1;
953 p4.x = sb->x0;
954 p4.y = eb->y1;
956 p1 = fz_transform_point (ctm, p1);
957 p2 = fz_transform_point (ctm, p2);
958 p3 = fz_transform_point (ctm, p3);
959 p4 = fz_transform_point (ctm, p4);
961 if (!stop) {
962 printd (state.sock, "F %d %d %f %f %f %f %f %f %f %f",
963 pageno, 1,
964 p1.x + xoff, p1.y + yoff,
965 p2.x + xoff, p2.y + yoff,
966 p3.x + xoff, p3.y + yoff,
967 p4.x + xoff, p4.y + yoff);
969 printd (state.sock, "T found at %d `%.*s' in %f sec",
970 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
971 now () - start);
973 else {
974 printd (state.sock, "R %d %d %f %f %f %f %f %f %f %f",
975 pageno, 2,
976 p1.x + xoff, p1.y + yoff,
977 p2.x + xoff, p2.y + yoff,
978 p3.x + xoff, p3.y + yoff,
979 p4.x + xoff, p4.y + yoff);
981 stop = 1;
984 if (forward) {
985 pageno += 1;
986 y = 0;
988 else {
989 pageno -= 1;
990 y = INT_MAX;
992 fz_free_text_span (text);
993 pdf_free_page (drawpage);
994 free (pspan);
996 end = now ();
997 if (!stop) {
998 printd (state.sock, "T no matches %f sec", end - start);
1000 printd (state.sock, "D");
1003 static
1004 #ifdef _WIN32
1005 DWORD _stdcall
1006 #else
1007 void *
1008 #endif
1009 mainloop (void *unused)
1011 char *p = NULL;
1012 int len, ret, oldlen = 0;
1014 for (;;) {
1015 len = readlen (state.sock);
1016 if (len == 0) {
1017 errx (1, "readlen returned 0");
1020 if (oldlen < len + 1) {
1021 p = realloc (p, len + 1);
1022 if (!p) {
1023 err (1, "realloc %d failed", len + 1);
1025 oldlen = len + 1;
1027 readdata (state.sock, p, len);
1028 p[len] = 0;
1030 if (!strncmp ("open", p, 4)) {
1031 fz_obj *obj;
1032 size_t filenamelen;
1033 char *password;
1034 char *filename = p + 5;
1036 filenamelen = strlen (filename);
1037 password = filename + filenamelen + 1;
1039 openxref (filename, password);
1040 initpdims ();
1042 obj = fz_dict_gets (state.xref->trailer, "Info");
1043 if (obj) {
1044 char *s;
1046 obj = fz_dict_gets (obj, "Title");
1047 s = pdf_to_utf8 (obj);
1048 if (*s) {
1049 printd (state.sock, "t %s", s);
1051 fz_free (s);
1054 state.needoutline = 1;
1056 else if (!strncmp ("free", p, 4)) {
1057 void *ptr;
1059 ret = sscanf (p + 4, " %p", &ptr);
1060 if (ret != 1) {
1061 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
1063 unlinkpage (ptr);
1064 state.pig = ptr;
1066 else if (!strncmp ("search", p, 6)) {
1067 int icase, pageno, y, ret, len2, forward;
1068 char *pattern;
1069 regex_t re;
1071 ret = sscanf (p + 6, " %d %d %d %d,%n",
1072 &icase, &pageno, &y, &forward, &len2);
1073 if (ret != 4) {
1074 errx (1, "malformed search `%s' ret=%d", p, ret);
1077 pattern = p + 6 + len2;
1078 ret = regcomp (&re, pattern,
1079 REG_EXTENDED | (icase ? REG_ICASE : 0));
1080 if (ret) {
1081 char errbuf[80];
1082 size_t size;
1084 size = regerror (ret, &re, errbuf, sizeof (errbuf));
1085 printd (state.sock, "T regcomp failed `%.*s'",
1086 (int) size, errbuf);
1088 else {
1089 search (&re, pageno, y, forward);
1090 regfree (&re);
1093 else if (!strncmp ("geometry", p, 8)) {
1094 int w, h;
1096 printd (state.sock, "c");
1097 ret = sscanf (p + 8, " %d %d", &w, &h);
1098 if (ret != 2) {
1099 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
1102 lock ("geometry");
1103 state.h = h;
1104 if (w != state.w) {
1105 int i;
1106 state.w = w;
1107 for (i = 0; i < state.texcount; ++i) {
1108 state.texowners[i].slice = NULL;
1111 layout ();
1112 process_outline ();
1113 unlock ("geometry");
1114 printd (state.sock, "C %d", state.pagecount);
1116 else if (!strncmp ("reinit", p, 6)) {
1117 float rotate;
1118 int proportional;
1120 printd (state.sock, "c");
1121 ret = sscanf (p + 6, " %f %d", &rotate, &proportional);
1122 if (ret != 2) {
1123 errx (1, "bad rotate line `%.*s' ret=%d", len, p, ret);
1125 lock ("reinit");
1126 state.rotate = rotate;
1127 state.proportional = proportional;
1128 state.pagedimcount = 0;
1129 free (state.pagedims);
1130 state.pagedims = NULL;
1131 initpdims ();
1132 layout ();
1133 process_outline ();
1134 if (state.pig) {
1135 freepage (state.pig);
1136 state.pig = NULL;
1138 unlock ("reinit");
1139 printd (state.sock, "C %d", state.pagecount);
1141 else if (!strncmp ("render", p, 6)) {
1142 int pageno, pindex, w, h, ret;
1143 struct page *page;
1145 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
1146 if (ret != 4) {
1147 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
1150 lock ("render");
1151 page = render (pageno, pindex);
1152 unlock ("render");
1154 printd (state.sock, "r %d %d %d %d %d %d %p",
1155 pageno,
1156 state.w,
1157 state.h,
1158 state.rotate,
1159 state.proportional,
1160 w * h * 4,
1161 page);
1163 else if (!strncmp ("info", p, 4)) {
1164 pdfinfo ();
1166 else if (!strncmp ("interrupt", p, 9)) {
1167 printd (state.sock, "V interrupted");
1169 else {
1170 errx (1, "unknown command %.*s", len, p);
1173 return 0;
1176 static void showsel (struct page *page, int oy)
1178 int ox;
1179 fz_bbox bbox;
1180 fz_text_span *span;
1181 struct mark first, last;
1183 first = page->fmark;
1184 last = page->lmark;
1186 if (!first.span || !last.span) return;
1188 glEnable (GL_BLEND);
1189 glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA);
1190 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1192 ox = -page->pixmap->x + page->pagedim.left;
1193 oy = -page->pixmap->y + oy;
1194 for (span = first.span; span; span = span->next) {
1195 int i, j, k;
1197 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0;
1199 j = 0;
1200 k = span->len - 1;
1202 if (span == page->fmark.span && span == page->lmark.span) {
1203 j = MIN (first.i, last.i);
1204 k = MAX (first.i, last.i);
1206 else if (span == first.span) {
1207 j = first.i;
1209 else if (span == last.span) {
1210 k = last.i;
1213 for (i = j; i <= k; ++i) {
1214 bbox = fz_union_bbox (bbox, span->text[i].bbox);
1216 lprintf ("%d %d %d %d oy=%d ox=%d\n",
1217 bbox.x0,
1218 bbox.y0,
1219 bbox.x1,
1220 bbox.y1,
1221 oy, ox);
1223 glRecti (bbox.x0 + ox, bbox.y0 + oy, bbox.x1 + ox, bbox.y1 + oy);
1225 if (span == last.span) break;
1227 glDisable (GL_BLEND);
1230 static void highlightlinks (struct page *page, int yoff)
1232 pdf_link *link;
1233 int xoff;
1235 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1236 glEnable (GL_LINE_STIPPLE);
1237 glLineStipple (0.5, 0xcccc);
1239 xoff = page->pagedim.left - page->pixmap->x;
1240 yoff -= page->pixmap->y;
1242 glBegin (GL_QUADS);
1243 for (link = page->drawpage->links; link; link = link->next) {
1244 fz_point p1, p2, p3, p4;
1245 fz_matrix ctm = page->pagedim.ctm;
1247 p1.x = link->rect.x0;
1248 p1.y = link->rect.y0;
1250 p2.x = link->rect.x1;
1251 p2.y = link->rect.y0;
1253 p3.x = link->rect.x1;
1254 p3.y = link->rect.y1;
1256 p4.x = link->rect.x0;
1257 p4.y = link->rect.y1;
1259 p1 = fz_transform_point (ctm, p1);
1260 p2 = fz_transform_point (ctm, p2);
1261 p3 = fz_transform_point (ctm, p3);
1262 p4 = fz_transform_point (ctm, p4);
1264 switch (link->kind) {
1265 case PDF_LINK_GOTO: glColor3ub (255, 0, 0); break;
1266 case PDF_LINK_URI: glColor3ub (0, 0, 255); break;
1267 default: glColor3ub (0, 0, 0); break;
1270 glVertex2f (p1.x + xoff, p1.y + yoff);
1271 glVertex2f (p2.x + xoff, p2.y + yoff);
1272 glVertex2f (p3.x + xoff, p3.y + yoff);
1273 glVertex2f (p4.x + xoff, p4.y + yoff);
1275 glEnd ();
1277 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1278 glDisable (GL_LINE_STIPPLE);
1281 static void upload2 (struct page *page, int slicenum, const char *cap)
1283 int i;
1284 int w;
1285 double start, end;
1286 struct slice *slice = &page->slices[slicenum];
1288 w = page->pixmap->w;
1290 ARSERT (w == slice->w);
1291 if (slice->texindex != -1
1292 && state.texowners[slice->texindex].slice == slice) {
1293 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1295 else {
1296 int subimage = 0;
1297 int index = (state.texindex++ % state.texcount);
1298 size_t offset = 0;
1300 for (i = 0; i < slicenum; ++i) {
1301 offset += w * page->slices[i].h * 4;
1304 if (state.texowners[index].w == slice->w) {
1305 if (state.texowners[index].h >= slice->h ) {
1306 subimage = 1;
1308 else {
1309 state.texowners[index].h = slice->h;
1312 else {
1313 state.texowners[index].h = slice->h;
1316 state.texowners[index].slice = slice;
1317 state.texowners[index].w = slice->w;
1318 slice->texindex = index;
1320 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1321 start = now ();
1322 if (subimage) {
1324 GLenum err = glGetError ();
1325 if (err != GL_NO_ERROR) {
1326 printf ("\033[0;31mERROR1 %d %d %#x\033[0m\n", w, slice->h, err);
1327 abort ();
1330 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1335 slice->h,
1336 state.texform,
1337 state.texty,
1338 page->pixmap->samples + offset
1341 GLenum err = glGetError ();
1342 if (err != GL_NO_ERROR) {
1343 printf ("\033[0;31mERROR %d %d %#x\033[0m\n", w, slice->h, err);
1344 abort ();
1348 else {
1349 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1351 GL_RGBA8,
1353 slice->h,
1355 state.texform,
1356 state.texty,
1357 page->pixmap->samples + offset
1361 end = now ();
1362 (void) start;
1363 (void) end;
1364 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
1365 subimage ? "sub" : "img",
1366 page->pageno, slicenum,
1367 slice->w, slice->h,
1368 state.texids[slice->texindex],
1369 end - start);
1373 #include "glfont.c"
1375 CAMLprim value ml_draw_string (value pt_v, value x_v, value y_v, value string_v)
1377 CAMLparam4 (pt_v, x_v, y_v, string_v);
1378 int pt = Int_val(pt_v);
1379 int x = Int_val (x_v);
1380 int y = Int_val (y_v);
1382 if (!state.face) {
1383 errx (1, "draw string with no face");
1386 glEnable (GL_TEXTURE_2D);
1387 glEnable (GL_BLEND);
1388 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1389 draw_string (state.face, pt, x, y, String_val (string_v));
1390 glDisable (GL_BLEND);
1391 glDisable (GL_TEXTURE_2D);
1393 CAMLreturn (Val_unit);
1396 CAMLprim value ml_draw (value args_v, value ptr_v)
1398 CAMLparam2 (args_v, ptr_v);
1399 int dispy = Int_val (Field (args_v, 0));
1400 int w = Int_val (Field (args_v, 1));
1401 int h = Int_val (Field (args_v, 2));
1402 int py = Int_val (Field (args_v, 3));
1403 int hlinks = Bool_val (Field (args_v, 4));
1404 char *s = String_val (ptr_v);
1405 int ret;
1406 void *ptr;
1407 struct page *page;
1408 int slicenum = 0;
1409 int yoff = dispy - py;
1411 ret = sscanf (s, "%p", &ptr);
1412 if (ret != 1) {
1413 errx (1, "cannot parse pointer `%s'", s);
1415 page = ptr;
1417 w = page->pixmap->w;
1419 ARSERT (h >= 0 && "ml_draw wrong h");
1420 ARSERT (py <= page->pixmap->h && "ml_draw wrong py");
1422 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1424 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
1425 struct slice *slice = &page->slices[slicenum];
1426 if (slice->h > py) {
1427 break;
1429 py -= slice->h;
1432 h = MIN (state.h, h);
1433 while (h) {
1434 int th, left;
1435 struct slice *slice = &page->slices[slicenum];
1437 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
1439 th = MIN (h, slice->h - py);
1440 upload2 (page, slicenum, "upload");
1442 left = page->pagedim.left;
1443 glBegin (GL_QUADS);
1445 glTexCoord2i (0, py);
1446 glVertex2i (left, dispy);
1448 glTexCoord2i (w, py);
1449 glVertex2i (left+w, dispy);
1451 glTexCoord2i (w, py+th);
1452 glVertex2i (left+w, dispy + th);
1454 glTexCoord2i (0, py+th);
1455 glVertex2i (left, dispy + th);
1457 glEnd ();
1459 h -= th;
1460 py = 0;
1461 dispy += th;
1462 slicenum += 1;
1465 showsel (page, yoff);
1466 if (hlinks) highlightlinks (page, yoff);
1467 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1469 CAMLreturn (Val_unit);
1472 static pdf_link *getlink (struct page *page, int x, int y)
1474 fz_point p;
1475 fz_matrix ctm;
1476 pdf_link *link;
1478 p.x = x + page->pixmap->x;
1479 p.y = y + page->pixmap->y;
1481 ctm = fz_invert_matrix (page->pagedim.ctm);
1482 p = fz_transform_point (ctm, p);
1484 for (link = page->drawpage->links; link; link = link->next) {
1485 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1486 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1487 return link;
1491 return NULL;
1494 static void ensuretext (struct page *page)
1496 if (!page->text) {
1497 fz_error error;
1498 fz_device *tdev;
1500 page->text = fz_new_text_span ();
1501 tdev = fz_new_text_device (page->text);
1502 error = pdf_run_page (state.xref, page->drawpage, tdev,
1503 page->pagedim.ctm);
1504 if (error) die (error);
1505 fz_free_device (tdev);
1509 CAMLprim value ml_whatsunder (value ptr_v, value x_v, value y_v)
1511 CAMLparam3 (ptr_v, x_v, y_v);
1512 CAMLlocal3 (ret_v, tup_v, str_v);
1513 pdf_link *link;
1514 struct page *page;
1515 char *s = String_val (ptr_v);
1517 ret_v = Val_int (0);
1518 if (trylock ("ml_whatsunder")) {
1519 goto done;
1522 page = parse_pointer ("ml_whatsunder", s);
1523 link = getlink (page, Int_val (x_v), Int_val (y_v));
1524 if (link) {
1525 switch (link->kind) {
1526 case PDF_LINK_GOTO:
1528 int pageno;
1529 fz_point p;
1530 fz_obj *obj;
1532 pageno = -1;
1533 p.x = 0;
1534 p.y = 0;
1536 if (fz_is_array (link->dest)) {
1537 obj = fz_array_get (link->dest, 0);
1538 if (fz_is_indirect (obj)) {
1539 pageno = pdf_find_page_number (state.xref, obj);
1541 else if (fz_is_int (obj)) {
1542 pageno = fz_to_int (obj);
1545 if (fz_array_len (link->dest) > 3) {
1546 fz_obj *xo, *yo;
1548 xo = fz_array_get (link->dest, 2);
1549 yo = fz_array_get (link->dest, 3);
1550 if (!fz_is_null (xo) && !fz_is_null (yo)) {
1551 p.x = fz_to_int (xo);
1552 p.y = fz_to_int (yo);
1553 p = fz_transform_point (page->pagedim.ctm, p);
1557 else {
1558 pageno = pdf_find_page_number (state.xref, link->dest);
1560 tup_v = caml_alloc_tuple (2);
1561 ret_v = caml_alloc_small (1, 1);
1562 Field (tup_v, 0) = Val_int (pageno);
1563 Field (tup_v, 1) = Val_int (p.y);
1564 Field (ret_v, 0) = tup_v;
1566 break;
1568 case PDF_LINK_URI:
1569 str_v = caml_copy_string (fz_to_str_buf (link->dest));
1570 ret_v = caml_alloc_small (1, 0);
1571 Field (ret_v, 0) = str_v;
1572 break;
1574 default:
1575 printd (state.sock, "T unhandled link kind %d", link->kind);
1576 break;
1579 else {
1580 int i, x, y;
1581 fz_text_span *span;
1583 ensuretext (page);
1584 x = Int_val (x_v) + page->pixmap->x;
1585 y = Int_val (y_v) + page->pixmap->y;
1587 for (span = page->text; span; span = span->next) {
1588 for (i = 0; i < span->len; ++i) {
1589 fz_bbox *b;
1590 b = &span->text[i].bbox;
1591 if ((x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1)) {
1592 const char *n2 =
1593 span->font && span->font->name
1594 ? span->font->name
1595 : "Span has no font name"
1597 #ifdef FT_FREETYPE_H
1598 FT_FaceRec *face = span->font->ft_face;
1599 if (face && face->family_name) {
1600 char *s;
1601 char *n1 = face->family_name;
1602 size_t l1 = strlen (n1);
1603 size_t l2 = strlen (n2);
1605 if (l1 != l2 || memcmp (n1, n2, l1)) {
1606 s = malloc (l1 + l2 + 2);
1607 if (s) {
1608 memcpy (s, n2, l2);
1609 s[l2] = '=';
1610 memcpy (s + l2 + 1, n1, l1 + 1);
1611 str_v = caml_copy_string (s);
1612 free (s);
1616 if (str_v == 0) {
1617 str_v = caml_copy_string (n2);
1619 #else
1620 str_v = caml_copy_string (n2);
1621 #endif
1622 ret_v = caml_alloc_small (1, 2);
1623 Field (ret_v, 0) = str_v;
1624 goto unlock;
1629 unlock:
1630 unlock ("ml_whatsunder");
1632 done:
1633 CAMLreturn (ret_v);
1636 CAMLprim value ml_seltext (value ptr_v, value rect_v, value oy_v)
1638 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1639 fz_bbox *b;
1640 struct page *page;
1641 fz_text_span *span;
1642 struct mark first, last;
1643 int i, x0, x1, y0, y1, oy, left;
1644 char *s = String_val (ptr_v);
1646 if (trylock ("ml_seltext")) {
1647 goto done;
1650 page = parse_pointer ("ml_seltext", s);
1651 ensuretext (page);
1653 oy = Int_val (oy_v);
1654 x0 = Int_val (Field (rect_v, 0));
1655 y0 = Int_val (Field (rect_v, 1));
1656 x1 = Int_val (Field (rect_v, 2));
1657 y1 = Int_val (Field (rect_v, 3));
1659 left = page->pagedim.left;
1660 if (0) {
1661 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1662 glColor3ub (128, 128, 128);
1663 glRecti (x0, y0, x1, y1);
1664 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1667 x0 += page->pixmap->x - left;
1668 y0 += page->pixmap->y - oy;
1669 x1 += page->pixmap->x - left;
1670 y1 += page->pixmap->y - oy;
1672 first.span = NULL;
1673 last.span = NULL;
1675 last.i = first.i = 0;
1676 first.span = page->text;
1677 for (span = page->text; span; span = span->next) {
1678 for (i = 0; i < span->len; ++i) {
1679 b = &span->text[i].bbox;
1680 int selected = 0;
1682 if (x0 >= b->x0 && x0 <= b->x1 && y0 >= b->y0 && y0 <= b->y1) {
1683 first.i = i;
1684 first.span = span;
1685 selected = 1;
1687 if (x1 >= b->x0 && x1 <= b->x1 && y1 >= b->y0 && y1 <= b->y1) {
1688 last.i = i;
1689 last.span = span;
1690 selected = 1;
1692 if (0 && selected) {
1693 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1694 glColor3ub (128, 128, 128);
1695 glRecti (b->x0+left, b->y0, b->x1+left, b->y1);
1696 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1701 if (y1 < y0 || x1 < x0) {
1702 int swap = 0;
1704 if (first.span == last.span) {
1705 swap = 1;
1707 else {
1708 if (y1 < y0) {
1709 for (span = first.span; span && span != last.span;
1710 span = span->next) {
1711 if (span->eol) {
1712 swap = 1;
1713 break;
1719 if (swap) {
1720 i = first.i;
1721 span = first.span;
1722 first.i = last.i;
1723 first.span = last.span;
1724 last.i = i;
1725 last.span = span;
1729 page->fmark = first;
1730 page->lmark = last;
1732 unlock ("ml_seltext");
1734 done:
1735 CAMLreturn (Val_unit);
1738 static int pipespan (FILE *f, fz_text_span *span, int a, int b)
1740 char buf[4];
1741 int i, len, ret;
1743 for (i = a; i <= b; ++i) {
1744 len = runetochar (buf, &span->text[i].c);
1745 ret = fwrite (buf, len, 1, f);
1747 if (ret != 1) {
1748 printd (state.sock, "T failed to write %d bytes ret=%d: %s",
1749 len, ret, strerror (errno));
1750 return -1;
1753 return 0;
1756 CAMLprim value ml_copysel (value ptr_v)
1758 CAMLparam1 (ptr_v);
1759 FILE *f;
1760 struct page *page;
1761 char *s = String_val (ptr_v);
1763 if (trylock ("ml_copysel")) {
1764 goto done;
1767 if (!*s) {
1768 close:
1769 #ifdef USE_XSEL
1770 if (state.xselpipe) {
1771 int ret = pclose (state.xselpipe);
1772 if (ret) {
1773 printd (state.sock, "T failed to close xsel pipe `%s'",
1774 strerror (errno));
1776 state.xselpipe = NULL;
1778 #else
1779 printf ("========================================\n");
1780 #endif
1782 else {
1783 fz_text_span *span;
1785 page = parse_pointer ("ml_sopysel", s);
1787 if (!page->fmark.span || !page->lmark.span) {
1788 printd (state.sock, "T nothing to copy");
1789 goto unlock;
1792 f = stdout;
1793 #ifdef USE_XSEL
1794 if (!state.xselpipe) {
1795 state.xselpipe = popen ("xsel -i", "w");
1796 if (!state.xselpipe) {
1797 printd (state.sock, "T failed to open xsel pipe `%s'",
1798 strerror (errno));
1800 else {
1801 f = state.xselpipe;
1804 else {
1805 f = state.xselpipe;
1807 #endif
1809 for (span = page->fmark.span;
1810 span && span != page->lmark.span->next;
1811 span = span->next) {
1812 int a = span == page->fmark.span ? page->fmark.i : 0;
1813 int b = span == page->lmark.span ? page->lmark.i : span->len - 1;
1814 if (pipespan (f, span, a, b)) {
1815 goto close;
1817 if (span->eol) {
1818 if (putc ('\n', f) == EOF) {
1819 printd (state.sock, "T failed break line on xsel pipe `%s'",
1820 strerror (errno));
1821 goto close;
1825 page->lmark.span = NULL;
1826 page->fmark.span = NULL;
1829 unlock:
1830 unlock ("ml_copysel");
1832 done:
1833 CAMLreturn (Val_unit);
1836 CAMLprim value ml_getpdimrect (value pagedimno_v)
1838 CAMLparam1 (pagedimno_v);
1839 CAMLlocal1 (ret_v);
1840 int pagedimno = Int_val (pagedimno_v);
1841 fz_rect box;
1843 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1844 if (trylock ("ml_getpdimrect")) {
1845 box = fz_empty_rect;
1847 else {
1848 box = state.pagedims[pagedimno].box;
1849 unlock ("ml_getpdimrect");
1852 Store_double_field (ret_v, 0, box.x0);
1853 Store_double_field (ret_v, 1, box.x1);
1854 Store_double_field (ret_v, 2, box.y0);
1855 Store_double_field (ret_v, 3, box.y1);
1857 CAMLreturn (ret_v);
1860 CAMLprim value ml_zoom_for_height (value winw_v, value winh_v, value dw_v)
1862 CAMLparam3 (winw_v, winh_v, dw_v);
1863 CAMLlocal1 (ret_v);
1864 int i;
1865 double zoom = 1.0;
1866 double maxw = 0.0, maxh = 0.0;
1867 struct pagedim *p;
1868 double winw = Int_val (winw_v);
1869 double winh = Int_val (winh_v);
1870 double dw = Int_val (dw_v);
1871 double pw = 1.0, ph = 1.0, num, den;
1873 if (trylock ("ml_zoom_for_height")) {
1874 goto done;
1877 if (state.proportional) {
1878 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
1879 double x0, x1, w;
1881 x0 = MIN (p->box.x0, p->box.x1);
1882 x1 = MAX (p->box.x0, p->box.x1);
1884 w = x1 - x0;
1885 maxw = MAX (w, maxw);
1889 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
1890 double x0, x1, y0, y1, w, h, scaledh, scale;
1892 x0 = MIN (p->box.x0, p->box.x1);
1893 y0 = MIN (p->box.y0, p->box.y1);
1894 x1 = MAX (p->box.x0, p->box.x1);
1895 y1 = MAX (p->box.y0, p->box.y1);
1897 w = x1 - x0;
1898 h = y1 - y0;
1900 if (state.proportional) {
1901 scale = w / maxw;
1902 scaledh = h * scale;
1904 else {
1905 scale = 1.0;
1906 scaledh = h;
1909 if (scaledh > maxh) {
1910 maxh = scaledh;
1911 ph = scaledh;
1912 pw = w * scale;
1916 num = (winh * pw) + (ph * dw);
1917 den = ph * winw;
1918 zoom = num / den;
1920 unlock ("ml_zoom_for_height");
1921 done:
1922 ret_v = caml_copy_double (zoom);
1923 CAMLreturn (ret_v);
1926 CAMLprim value ml_init (value sock_v, value params_v)
1928 CAMLparam2 (sock_v, params_v);
1929 #ifndef _WIN32
1930 int ret;
1931 #endif
1932 char *fontpath;
1934 state.rotate = Int_val (Field (params_v, 0));
1935 state.proportional = Bool_val (Field (params_v, 1));
1936 state.texcount = Int_val (Field (params_v, 2));
1937 state.sliceheight = Int_val (Field (params_v, 3));
1938 fontpath = String_val (Field (params_v, 4));
1939 state.texform = GL_RGBA;
1940 state.texty = GL_UNSIGNED_BYTE;
1942 if (*fontpath) {
1943 state.face = load_font (fontpath);
1945 else {
1946 int code;
1947 unsigned int len;
1948 void *base = pdf_find_builtin_font ("Courier-Bold", &len);
1950 if (!len || !base) errx (1, "failed to find builtin font\n");
1952 code = FT_New_Memory_Face (g_freetype_lib, base, len, 0, &state.face);
1953 if (code) errx (1, "failed to load font bultin font\n");
1955 if (!state.face) _exit (1);
1957 fz_accelerate ();
1958 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1959 if (!state.texids) {
1960 err (1, "calloc texids " FMT_s,
1961 state.texcount * sizeof (*state.texids));
1964 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1965 if (!state.texowners) {
1966 err (1, "calloc texowners " FMT_s,
1967 state.texcount * sizeof (*state.texowners));
1970 glGenTextures (state.texcount, state.texids);
1972 #ifdef _WIN32
1973 state.sock = Socket_val (sock_v);
1974 #else
1975 state.sock = Int_val (sock_v);
1976 #endif
1978 #ifdef _WIN32
1979 InitializeCriticalSection (&critsec);
1980 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
1981 if (state.thread == INVALID_HANDLE_VALUE) {
1982 errx (1, "CreateThread failed: %lx", GetLastError ());
1984 #else
1985 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1986 if (ret) {
1987 errx (1, "pthread_create: %s", strerror (ret));
1989 #endif
1991 CAMLreturn (Val_unit);