Refactor
[llpp.git] / link.c
blob415a688bd8adc3416a56c6107aeb6f4acb435130
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 #define UNUSED
29 #elif defined __GNUC__
30 #define NORETURN __attribute__ ((noreturn))
31 #define UNUSED __attribute__ ((unused))
32 #else
33 #define NORETURN
34 #define UNUSED
35 #endif
37 #ifdef _WIN32
38 static void __declspec (noreturn) sockerr (int exitcode, const char *fmt, ...)
40 va_list ap;
42 va_start (ap, fmt);
43 vfprintf (stderr, fmt, ap);
44 va_end (ap);
45 fprintf (stderr, ": wsaerror 0x%x\n", WSAGetLastError ());
46 _exit (exitcode);
48 #else
49 #define FMT_ss "%zd"
50 #define FMT_s "%zu"
51 #define fionread_arg int
52 #define ioctlsocket ioctl
53 #define sockerr err
54 #include <unistd.h>
55 #endif
57 #include <regex.h>
58 #include <ctype.h>
59 #include <stdarg.h>
60 #include <limits.h>
62 #ifndef _WIN32
63 #include <pthread.h>
64 #include <sys/time.h>
65 #include <sys/types.h>
66 #include <sys/socket.h>
67 #include <sys/ioctl.h>
68 #endif
70 static void NORETURN err (int exitcode, const char *fmt, ...)
72 va_list ap;
73 int savederrno;
75 savederrno = errno;
76 va_start (ap, fmt);
77 vfprintf (stderr, fmt, ap);
78 va_end (ap);
79 fprintf (stderr, ": %s\n", strerror (savederrno));
80 fflush (stderr);
81 _exit (exitcode);
84 static void NORETURN errx (int exitcode, const char *fmt, ...)
86 va_list ap;
88 va_start (ap, fmt);
89 vfprintf (stderr, fmt, ap);
90 va_end (ap);
91 fputc ('\n', stderr);
92 fflush (stderr);
93 _exit (exitcode);
96 #ifdef __APPLE__
97 #include <OpenGL/gl.h>
98 #else
99 #include <GL/gl.h>
100 #endif
102 #ifndef GL_TEXTURE_RECTANGLE_ARB
103 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
104 #endif
106 #include <caml/fail.h>
107 #include <caml/alloc.h>
108 #include <caml/memory.h>
109 #include <caml/unixsupport.h>
111 #include <fitz.h>
112 #include <mupdf.h>
114 #if 0
115 #define lprintf printf
116 #else
117 #define lprintf(...)
118 #endif
120 #ifdef FT_FREETYPE_H
121 #include FT_FREETYPE_H
122 #endif
124 #define ARSERT(cond) for (;;) { \
125 if (!(cond)) { \
126 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
128 break; \
131 struct slice {
132 int texindex;
133 int w, h;
136 struct pagedim {
137 int pageno;
138 int rotate;
139 int left;
140 fz_rect box;
141 fz_bbox bbox;
142 fz_matrix ctm, ctm1;
145 struct page {
146 int pageno;
147 int slicecount;
148 fz_text_span *text;
149 fz_pixmap *pixmap;
150 pdf_page *drawpage;
151 struct pagedim pagedim;
152 struct mark {
153 int i;
154 fz_text_span *span;
155 } fmark, lmark;
156 struct slice slices[];
159 #if !defined _WIN32 && !defined __APPLE__
160 #define USE_XSEL
161 #endif
163 struct {
164 int sock;
165 int sliceheight;
166 struct page *pig;
167 struct pagedim *pagedims;
168 int pagecount;
169 int pagedimcount;
170 pdf_xref *xref;
171 fz_glyph_cache *cache;
172 int w, h;
174 int texindex;
175 int texcount;
176 GLuint *texids;
178 GLenum texform;
179 GLenum texty;
181 struct {
182 int w, h;
183 struct slice *slice;
184 } *texowners;
186 int rotate;
187 int proportional;
188 int needoutline;
190 #ifdef _WIN32
191 HANDLE thread;
192 #else
193 pthread_t thread;
194 #endif
195 FILE *xselpipe;
197 FT_Face face;
198 } state;
200 #ifdef _WIN32
201 static CRITICAL_SECTION critsec;
203 static void lock (void *unused)
205 (void) unused;
206 EnterCriticalSection (&critsec);
209 static void unlock (void *unused)
211 (void) unused;
212 LeaveCriticalSection (&critsec);
215 static int trylock (void *unused)
217 return TryEnterCriticalSection (&critsec) == 0;
219 #else
220 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
222 static void lock (const char *cap)
224 int ret = pthread_mutex_lock (&mutex);
225 if (ret) {
226 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
230 static void unlock (const char *cap)
232 int ret = pthread_mutex_unlock (&mutex);
233 if (ret) {
234 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
238 static int trylock (const char *cap)
240 int ret = pthread_mutex_trylock (&mutex);
242 if (ret && ret != EBUSY) {
243 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
245 return ret == EBUSY;
247 #endif
249 static void *parse_pointer (const char *cap, const char *s)
251 int ret;
252 void *ptr;
254 ret = sscanf (s, "%p", &ptr);
255 if (ret != 1) {
256 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
258 return ptr;
261 static int hasdata (int sock)
263 int ret;
264 fionread_arg avail;
265 ret = ioctlsocket (sock, FIONREAD, &avail);
266 if (ret) sockerr (1, "hasdata: FIONREAD error ret=%d", ret);
267 return avail > 0;
270 static double now (void)
272 struct timeval tv;
274 if (gettimeofday (&tv, NULL)) {
275 err (1, "gettimeofday");
277 return tv.tv_sec + tv.tv_usec*1e-6;
280 static void readdata (int fd, char *p, int size)
282 ssize_t n;
284 n = recv (fd, p, size, 0);
285 if (n - size) {
286 if (!n) errx (1, "EOF while reading");
287 sockerr (1, "recv (req %d, ret " FMT_ss ")", size, n);
291 static void writedata (int fd, char *p, int size)
293 char buf[4];
294 ssize_t n;
296 buf[0] = (size >> 24) & 0xff;
297 buf[1] = (size >> 16) & 0xff;
298 buf[2] = (size >> 8) & 0xff;
299 buf[3] = (size >> 0) & 0xff;
301 n = send (fd, buf, 4, 0);
302 if (n != 4) {
303 if (!n) errx (1, "EOF while writing length");
304 sockerr (1, "send " FMT_ss, n);
307 n = send (fd, p, size, 0);
308 if (n - size) {
309 if (!n) errx (1, "EOF while writing data");
310 sockerr (1, "send (req %d, ret " FMT_ss ")", size, n);
314 static void
315 #ifdef __GNUC__
316 __attribute__ ((format (printf, 2, 3)))
317 #endif
318 printd (int fd, const char *fmt, ...)
320 int size = 200, len;
321 va_list ap;
322 char *buf;
324 buf = malloc (size);
325 for (;;) {
326 if (!buf) err (errno, "malloc for temp buf (%d bytes) failed", size);
328 va_start (ap, fmt);
329 len = vsnprintf (buf, size, fmt, ap);
330 va_end (ap);
332 if (len > -1 && len < size) {
333 writedata (fd, buf, len);
334 break;
337 if (len > -1) {
338 size = len + 1;
340 else {
341 size *= 2;
343 buf = realloc (buf, size);
345 free (buf);
348 static void die (fz_error error)
350 fz_catch (error, "aborting");
351 if (state.xref)
352 pdf_free_xref (state.xref);
353 _exit (1);
356 static void openxref (char *filename, char *password)
358 int i;
359 fz_error error;
361 for (i = 0; i < state.texcount; ++i) {
362 state.texowners[i].slice = NULL;
365 if (state.cache) {
366 fz_free_glyph_cache (state.cache);
369 state.cache = fz_new_glyph_cache ();
370 if (!state.cache) {
371 errx (1, "fz_newglyph_cache failed");
374 if (state.xref) {
375 if (state.xref->store) {
376 pdf_free_store (state.xref->store);
377 state.xref->store = NULL;
379 pdf_free_xref (state.xref);
380 state.xref = NULL;
383 if (state.pagedims) {
384 free (state.pagedims);
385 state.pagedims = NULL;
387 state.pagedimcount = 0;
389 error = pdf_open_xref (&state.xref, filename, password);
390 if (error) {
391 die (error);
394 error = pdf_load_page_tree (state.xref);
395 if (error) {
396 die (error);
399 state.pagecount = pdf_count_pages (state.xref);
402 static void pdfinfo (void)
404 printd (state.sock, "i PDF version %d.%d\n",
405 state.xref->version / 10, state.xref->version % 10);
407 #if _XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L
409 FILE *f;
410 char *buf;
411 size_t size;
412 fz_obj *obj;
414 f = open_memstream (&buf, &size);
415 if (f) {
416 obj = fz_dict_gets (state.xref->trailer, "Info");
417 fz_fprint_obj (f, fz_resolve_indirect (obj), 0);
418 if (fclose (f)) err (1, "fclose on memstream failed");
419 if (size > 64*1024) size = 64*1024;
420 printd (state.sock, "i %.*s", (int) size, buf);
421 free (buf);
423 else {
424 printd (state.sock, "i error opening memstream: %s\n",
425 strerror (errno));
428 #endif
431 static int readlen (int fd)
433 ssize_t n;
434 unsigned char p[4];
436 n = recv (fd, p, 4, 0);
437 if (n != 4) {
438 if (!n) errx (1, "EOF while reading length");
439 sockerr (1, "recv " FMT_ss, n);
442 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
445 static void unlinkpage (struct page *page)
447 int i;
449 for (i = 0; i < page->slicecount; ++i) {
450 struct slice *s = &page->slices[i];
451 if (s->texindex != -1) {
452 if (state.texowners[s->texindex].slice == s) {
453 state.texowners[s->texindex].slice = NULL;
454 ARSERT (state.texowners[s->texindex].w == s->w);
455 ARSERT (state.texowners[s->texindex].h >= s->h);
461 static void freepage (struct page *page)
463 fz_drop_pixmap (page->pixmap);
465 unlinkpage (page);
467 if (page->text) {
468 fz_free_text_span (page->text);
470 if (page->drawpage) {
471 pdf_free_page (page->drawpage);
474 free (page);
477 static void subdivide (struct page *p)
479 int i;
480 int h = p->pixmap->h;
481 int th = MIN (h, state.sliceheight);
483 for (i = 0; i < p->slicecount; ++i) {
484 struct slice *s = &p->slices[i];
485 s->texindex = -1;
486 s->h = MIN (th, h);
487 s->w = p->pixmap->w;
488 h -= th;
492 static int compatpdims (struct pagedim *p1, struct pagedim *p2)
494 return p1->rotate == p2->rotate
495 && !memcmp (&p1->bbox, &p2->bbox, sizeof (p1->bbox))
496 && !memcmp (&p1->ctm, &p2->ctm, sizeof (p1->ctm));
499 #ifdef __ALTIVEC__
500 #include <altivec.h>
502 static int cacheline32bytes;
503 extern char **environ;
505 static void __attribute__ ((constructor)) clcheck (void)
507 char **envp = environ;
508 unsigned long *auxv;
510 while (*envp++);
512 for (auxv = (unsigned long *) envp; *auxv != 0; auxv += 2) {
513 if (*auxv == 19) {
514 cacheline32bytes = auxv[1] == 32;
515 return;
520 static void __attribute__ ((optimize ("O"))) clearpixmap (fz_pixmap *pixmap)
522 if (cacheline32bytes) {
523 intptr_t a1, a2, diff;
524 size_t sizea, i, size = pixmap->w * pixmap->h * pixmap->n;
525 vector unsigned char v = vec_splat_u8 (-1);
526 vector unsigned char *p;
528 a1 = a2 = (intptr_t) pixmap->samples;
529 a2 = (a1 + 31) & ~31;
530 diff = a2 - a1;
531 sizea = size - diff;
532 p = (void *) a2;
534 while (a1 != a2) *(char *) a1++ = 0xff;
535 for (i = 0; i < (sizea & ~31); i += 32) {
536 __asm volatile ("dcbz %0, %1"::"b"(a2),"r"(i));
537 vec_st (v, i, p);
538 vec_st (v, i + 16, p);
540 while (i < sizea) *((char *) a1 + i++) = 0xff;
542 else fz_clear_pixmap_with_color (pixmap, 0xff);
544 #else
545 #define clearpixmap(p) fz_clear_pixmap_with_color (p, 0xff)
546 #endif
548 static void *render (int pageno, int pindex)
550 fz_error error;
551 int slicecount;
552 struct page *page = NULL;
553 double start, end;
554 pdf_page *drawpage;
555 fz_device *idev;
556 struct pagedim *pagedim;
558 start = now ();
559 printd (state.sock, "V rendering %d", pageno);
561 pagedim = &state.pagedims[pindex];
562 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
563 + state.sliceheight - 1) / state.sliceheight;
564 slicecount += slicecount == 0;
566 if (state.pig) {
567 if (compatpdims (&state.pig->pagedim, pagedim)) {
568 page = state.pig;
569 if (page->text) {
570 fz_free_text_span (page->text);
571 page->text = NULL;
573 if (page->drawpage) {
574 pdf_free_page (page->drawpage);
575 page->drawpage = NULL;
578 else {
579 freepage (state.pig);
582 if (!page) {
583 page = calloc (sizeof (*page)
584 + (slicecount * sizeof (struct slice)), 1);
585 if (!page) {
586 err (1, "calloc page %d\n", pageno);
588 page->pixmap = fz_new_pixmap_with_rect (fz_device_rgb, pagedim->bbox);
591 page->slicecount = slicecount;
593 error = pdf_load_page (&drawpage, state.xref, pageno - 1);
594 if (error)
595 die (error);
597 clearpixmap (page->pixmap);
599 idev = fz_new_draw_device (state.cache, page->pixmap);
600 if (!idev)
601 die (fz_throw ("fz_newdrawdevice failed"));
602 error = pdf_run_page (state.xref, drawpage, idev, pagedim->ctm);
603 if (error)
604 die (fz_rethrow (error, "pdf_runpage failed"));
605 fz_free_device (idev);
607 page->drawpage = drawpage;
608 page->pagedim = *pagedim;
609 page->pageno = pageno;
610 subdivide (page);
611 end = now ();
613 pdf_age_store (state.xref->store, 3);
615 printd (state.sock, "V rendering %d took %f sec", pageno, end - start);
616 state.pig = NULL;
617 return page;
620 static void initpdims (void)
622 int pageno;
623 double start, end;
625 start = now ();
626 for (pageno = 0; pageno < state.pagecount; ++pageno) {
627 int rotate;
628 fz_rect box;
629 struct pagedim *p;
630 fz_obj *obj, *pageobj;
632 pageobj = state.xref->page_objs[pageno];
634 obj = fz_dict_gets (pageobj, "CropBox");
635 if (!fz_is_array (obj)) {
636 obj = fz_dict_gets (pageobj, "MediaBox");
637 if (!fz_is_array (obj)) {
638 die (fz_throw ("cannot find page bounds %d (%d Rd)",
639 fz_to_num (pageobj), fz_to_gen (pageobj)));
642 box = pdf_to_rect (obj);
644 obj = fz_dict_gets (pageobj, "Rotate");
645 if (fz_is_int (obj)) {
646 rotate = fz_to_int (obj);
648 else {
649 rotate = 0;
651 rotate += state.rotate;
653 p = &state.pagedims[state.pagedimcount - 1];
654 if ((state.pagedimcount == 0)
655 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
656 size_t size;
658 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
659 state.pagedims = realloc (state.pagedims, size);
660 if (!state.pagedims) {
661 err (1, "realloc pagedims to " FMT_s " (%d elems)",
662 size, state.pagedimcount + 1);
664 p = &state.pagedims[state.pagedimcount++];
665 p->rotate = rotate;
666 p->box = box;
667 p->pageno = pageno;
670 end = now ();
671 printd (state.sock, "T Processed %d pages in %f seconds",
672 state.pagecount, end - start);
675 static void layout (void)
677 int pindex;
678 fz_matrix ctm;
679 fz_rect box, box2;
680 double zoom, w, maxw = 0;
681 struct pagedim *p = state.pagedims;
683 if (state.proportional) {
684 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
685 box.x0 = MIN (p->box.x0, p->box.x1);
686 box.y0 = MIN (p->box.y0, p->box.y1);
687 box.x1 = MAX (p->box.x0, p->box.x1);
688 box.y1 = MAX (p->box.y0, p->box.y1);
690 ctm = fz_identity;
691 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
692 ctm = fz_concat (ctm, fz_rotate (p->rotate));
693 box2 = fz_transform_rect (ctm, box);
694 w = box2.x1 - box2.x0;
695 maxw = MAX (w, maxw);
699 p = state.pagedims;
700 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
701 box.x0 = MIN (p->box.x0, p->box.x1);
702 box.y0 = MIN (p->box.y0, p->box.y1);
703 box.x1 = MAX (p->box.x0, p->box.x1);
704 box.y1 = MAX (p->box.y0, p->box.y1);
706 ctm = fz_identity;
707 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
708 ctm = fz_concat (ctm, fz_rotate (p->rotate));
709 box2 = fz_transform_rect (ctm, box);
710 w = box2.x1 - box2.x0;
712 if (state.proportional) {
713 double scale = w / maxw;
714 zoom = (state.w / w) * scale;
716 else {
717 zoom = state.w / w;
719 ctm = fz_identity;
720 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
721 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
722 memcpy (&p->ctm1, &ctm, sizeof (ctm));
723 ctm = fz_concat (ctm, fz_rotate (p->rotate));
724 p->bbox = fz_round_rect (fz_transform_rect (ctm, box));
725 p->left = state.proportional ? ((maxw - w) * zoom) / 2.0 : 0;
726 memcpy (&p->ctm, &ctm, sizeof (ctm));
729 while (p-- != state.pagedims) {
730 printd (state.sock, "l %d %d %d %d",
731 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0,
732 p->left);
736 static void recurse_outline (pdf_outline *outline, int level)
738 while (outline) {
739 int i;
740 fz_obj *obj;
741 int pageno = -1;
742 int top = 0, h = 0;
744 if (!outline->link) goto next;
746 obj = outline->link->dest;
747 if (fz_is_indirect (obj)) {
748 obj = fz_resolve_indirect (obj);
750 if (fz_is_array (obj)) {
751 fz_obj *obj2;
753 obj2 = fz_array_get (obj, 0);
754 if (fz_is_int (obj2)) {
755 pageno = fz_to_int (obj2);
757 else {
758 pageno = pdf_find_page_number (state.xref, obj2);
761 if (fz_array_len (obj) > 3) {
762 fz_point p;
763 fz_obj *xo, *yo;
765 xo = fz_array_get (obj, 2);
766 yo = fz_array_get (obj, 3);
767 if (!fz_is_null (xo) && !fz_is_null (yo)) {
768 struct pagedim *pagedim = state.pagedims;
770 for (i = 0; i < state.pagedimcount; ++i) {
771 if (state.pagedims[i].pageno > pageno)
772 break;
773 pagedim = &state.pagedims[i];
775 p.x = fz_to_int (xo);
776 p.y = fz_to_int (yo);
777 p = fz_transform_point (pagedim->ctm, p);
778 h = pagedim->bbox.y1 - pagedim->bbox.y0;
779 top = p.y;
783 else {
784 pageno = pdf_find_page_number (state.xref, outline->link->dest);
787 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
788 if (pageno > 0) {
789 printd (state.sock, "o %d %d %d %d %s",
790 level, pageno, top, h, outline->title);
792 next:
793 if (outline->child) {
794 recurse_outline (outline->child, level + 1);
796 outline = outline->next;
800 static void process_outline (void)
802 pdf_outline *outline;
804 if (!state.needoutline) return;
806 state.needoutline = 0;
807 outline = pdf_load_outline (state.xref);
808 if (outline) {
809 recurse_outline (outline, 0);
810 pdf_free_outline (outline);
814 static int comparespans (const void *l, const void *r)
816 fz_text_span const *const*ls = l;
817 fz_text_span const *const*rs = r;
818 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
821 /* wishful thinking function */
822 static void search (regex_t *re, int pageno, int y, int forward)
824 int i, j;
825 int ret;
826 char *p;
827 char buf[256];
828 fz_matrix ctm;
829 fz_error error;
830 fz_device *tdev;
831 pdf_page *drawpage;
832 fz_text_span *text, *span, **pspan;
833 struct pagedim *pdim, *pdimprev;
834 int stop = 0;
835 int niters = 0;
836 int nspans;
837 double start, end;
839 start = now ();
840 while (pageno >= 0 && pageno < state.pagecount && !stop) {
841 if (niters++ == 5) {
842 pdf_age_store (state.xref->store, 3);
843 niters = 0;
844 if (hasdata (state.sock)) {
845 printd (state.sock, "T attention requested aborting search at %d",
846 pageno);
847 stop = 1;
849 else {
850 printd (state.sock, "T searching in page %d", pageno);
853 pdimprev = NULL;
854 for (i = 0; i < state.pagedimcount; ++i) {
855 pdim = &state.pagedims[i];
856 if (pdim->pageno == pageno) {
857 goto found;
859 if (pdim->pageno > pageno) {
860 pdim = pdimprev;
861 goto found;
863 pdimprev = pdim;
865 pdim = pdimprev;
866 found:
868 error = pdf_load_page (&drawpage, state.xref, pageno);
869 if (error)
870 die (error);
872 ctm = fz_rotate (pdim->rotate);
874 text = fz_new_text_span ();
875 tdev = fz_new_text_device (text);
876 error = pdf_run_page (state.xref, drawpage, tdev, pdim->ctm1);
877 if (error) die (error);
878 fz_free_device (tdev);
880 nspans = 0;
881 for (span = text; span; span = span->next) {
882 nspans++;
884 pspan = malloc (sizeof (void *) * nspans);
885 if (!pspan) {
886 err (1, "malloc span pointers " FMT_s, sizeof (void *) * nspans);
888 for (i = 0, span = text; span; span = span->next, ++i) {
889 pspan[i] = span;
891 qsort (pspan, nspans, sizeof (fz_text_span *), comparespans);
893 j = forward ? 0 : nspans - 1;
894 while (nspans--) {
895 regmatch_t rm;
897 span = pspan[j];
898 j += forward ? 1 : -1;
899 p = buf;
900 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
901 if (forward) {
902 if (span->text[i].bbox.y0 < y + 1) {
903 continue;
906 else {
907 if (span->text[i].bbox.y0 > y - 1) {
908 continue;
911 if (span->text[i].c < 256) {
912 *p++ = span->text[i].c;
914 else {
915 *p++ = '?';
918 if (p == buf) {
919 continue;
921 *p++ = 0;
923 ret = regexec (re, buf, 1, &rm, 0);
924 if (ret) {
925 if (ret != REG_NOMATCH) {
926 size_t size;
927 char errbuf[80];
928 size = regerror (ret, re, errbuf, sizeof (errbuf));
929 printd (state.sock,
930 "T regexec error `%.*s'",
931 (int) size, errbuf);
932 fz_free_text_span (text);
933 pdf_free_page (drawpage);
934 free (pspan);
935 return;
938 else {
939 int xoff, yoff;
940 fz_bbox *sb, *eb;
941 fz_point p1, p2, p3, p4;
943 xoff = pdim->left - pdim->bbox.x0;
944 yoff = -pdim->bbox.y0;
946 sb = &span->text[rm.rm_so].bbox;
947 eb = &span->text[rm.rm_eo - 1].bbox;
949 p1.x = sb->x0;
950 p1.y = sb->y0;
951 p2.x = eb->x1;
952 p2.y = sb->y0;
953 p3.x = eb->x1;
954 p3.y = eb->y1;
955 p4.x = sb->x0;
956 p4.y = eb->y1;
958 p1 = fz_transform_point (ctm, p1);
959 p2 = fz_transform_point (ctm, p2);
960 p3 = fz_transform_point (ctm, p3);
961 p4 = fz_transform_point (ctm, p4);
963 if (!stop) {
964 printd (state.sock, "F %d %d %f %f %f %f %f %f %f %f",
965 pageno, 1,
966 p1.x + xoff, p1.y + yoff,
967 p2.x + xoff, p2.y + yoff,
968 p3.x + xoff, p3.y + yoff,
969 p4.x + xoff, p4.y + yoff);
971 printd (state.sock, "T found at %d `%.*s' in %f sec",
972 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
973 now () - start);
975 else {
976 printd (state.sock, "R %d %d %f %f %f %f %f %f %f %f",
977 pageno, 2,
978 p1.x + xoff, p1.y + yoff,
979 p2.x + xoff, p2.y + yoff,
980 p3.x + xoff, p3.y + yoff,
981 p4.x + xoff, p4.y + yoff);
983 stop = 1;
986 if (forward) {
987 pageno += 1;
988 y = 0;
990 else {
991 pageno -= 1;
992 y = INT_MAX;
994 fz_free_text_span (text);
995 pdf_free_page (drawpage);
996 free (pspan);
998 end = now ();
999 if (!stop) {
1000 printd (state.sock, "T no matches %f sec", end - start);
1002 printd (state.sock, "D");
1005 static
1006 #ifdef _WIN32
1007 DWORD _stdcall
1008 #else
1009 void *
1010 #endif
1011 mainloop (void *unused)
1013 char *p = NULL;
1014 int len, ret, oldlen = 0;
1016 for (;;) {
1017 len = readlen (state.sock);
1018 if (len == 0) {
1019 errx (1, "readlen returned 0");
1022 if (oldlen < len + 1) {
1023 p = realloc (p, len + 1);
1024 if (!p) {
1025 err (1, "realloc %d failed", len + 1);
1027 oldlen = len + 1;
1029 readdata (state.sock, p, len);
1030 p[len] = 0;
1032 if (!strncmp ("open", p, 4)) {
1033 fz_obj *obj;
1034 size_t filenamelen;
1035 char *password;
1036 char *filename = p + 5;
1038 filenamelen = strlen (filename);
1039 password = filename + filenamelen + 1;
1041 openxref (filename, password);
1042 initpdims ();
1044 obj = fz_dict_gets (state.xref->trailer, "Info");
1045 if (obj) {
1046 char *s;
1048 obj = fz_dict_gets (obj, "Title");
1049 s = pdf_to_utf8 (obj);
1050 if (*s) {
1051 printd (state.sock, "t %s", s);
1053 fz_free (s);
1056 state.needoutline = 1;
1058 else if (!strncmp ("free", p, 4)) {
1059 void *ptr;
1061 ret = sscanf (p + 4, " %p", &ptr);
1062 if (ret != 1) {
1063 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
1065 unlinkpage (ptr);
1066 state.pig = ptr;
1068 else if (!strncmp ("search", p, 6)) {
1069 int icase, pageno, y, ret, len2, forward;
1070 char *pattern;
1071 regex_t re;
1073 ret = sscanf (p + 6, " %d %d %d %d,%n",
1074 &icase, &pageno, &y, &forward, &len2);
1075 if (ret != 4) {
1076 errx (1, "malformed search `%s' ret=%d", p, ret);
1079 pattern = p + 6 + len2;
1080 ret = regcomp (&re, pattern,
1081 REG_EXTENDED | (icase ? REG_ICASE : 0));
1082 if (ret) {
1083 char errbuf[80];
1084 size_t size;
1086 size = regerror (ret, &re, errbuf, sizeof (errbuf));
1087 printd (state.sock, "T regcomp failed `%.*s'",
1088 (int) size, errbuf);
1090 else {
1091 search (&re, pageno, y, forward);
1092 regfree (&re);
1095 else if (!strncmp ("geometry", p, 8)) {
1096 int w, h;
1098 printd (state.sock, "c");
1099 ret = sscanf (p + 8, " %d %d", &w, &h);
1100 if (ret != 2) {
1101 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
1104 lock ("geometry");
1105 state.h = h;
1106 if (w != state.w) {
1107 int i;
1108 state.w = w;
1109 for (i = 0; i < state.texcount; ++i) {
1110 state.texowners[i].slice = NULL;
1113 layout ();
1114 process_outline ();
1115 unlock ("geometry");
1116 printd (state.sock, "C %d", state.pagecount);
1118 else if (!strncmp ("reinit", p, 6)) {
1119 float rotate;
1120 int proportional;
1122 printd (state.sock, "c");
1123 ret = sscanf (p + 6, " %f %d", &rotate, &proportional);
1124 if (ret != 2) {
1125 errx (1, "bad rotate line `%.*s' ret=%d", len, p, ret);
1127 lock ("reinit");
1128 state.rotate = rotate;
1129 state.proportional = proportional;
1130 state.pagedimcount = 0;
1131 free (state.pagedims);
1132 state.pagedims = NULL;
1133 initpdims ();
1134 layout ();
1135 process_outline ();
1136 if (state.pig) {
1137 freepage (state.pig);
1138 state.pig = NULL;
1140 unlock ("reinit");
1141 printd (state.sock, "C %d", state.pagecount);
1143 else if (!strncmp ("render", p, 6)) {
1144 int pageno, pindex, w, h, ret;
1145 struct page *page;
1147 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
1148 if (ret != 4) {
1149 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
1152 lock ("render");
1153 page = render (pageno, pindex);
1154 unlock ("render");
1156 printd (state.sock, "r %d %d %d %d %d %d %p",
1157 pageno,
1158 state.w,
1159 state.h,
1160 state.rotate,
1161 state.proportional,
1162 w * h * 4,
1163 page);
1165 else if (!strncmp ("info", p, 4)) {
1166 pdfinfo ();
1168 else if (!strncmp ("interrupt", p, 9)) {
1169 printd (state.sock, "V interrupted");
1171 else {
1172 errx (1, "unknown command %.*s", len, p);
1175 return 0;
1178 static void showsel (struct page *page, int oy)
1180 int ox;
1181 fz_bbox bbox;
1182 fz_text_span *span;
1183 struct mark first, last;
1185 first = page->fmark;
1186 last = page->lmark;
1188 if (!first.span || !last.span) return;
1190 glEnable (GL_BLEND);
1191 glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA);
1192 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1194 ox = -page->pixmap->x + page->pagedim.left;
1195 oy = -page->pixmap->y + oy;
1196 for (span = first.span; span; span = span->next) {
1197 int i, j, k;
1199 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0;
1201 j = 0;
1202 k = span->len - 1;
1204 if (span == page->fmark.span && span == page->lmark.span) {
1205 j = MIN (first.i, last.i);
1206 k = MAX (first.i, last.i);
1208 else if (span == first.span) {
1209 j = first.i;
1211 else if (span == last.span) {
1212 k = last.i;
1215 for (i = j; i <= k; ++i) {
1216 bbox = fz_union_bbox (bbox, span->text[i].bbox);
1218 lprintf ("%d %d %d %d oy=%d ox=%d\n",
1219 bbox.x0,
1220 bbox.y0,
1221 bbox.x1,
1222 bbox.y1,
1223 oy, ox);
1225 glRecti (bbox.x0 + ox, bbox.y0 + oy, bbox.x1 + ox, bbox.y1 + oy);
1227 if (span == last.span) break;
1229 glDisable (GL_BLEND);
1232 static void highlightlinks (struct page *page, int yoff)
1234 pdf_link *link;
1235 int xoff;
1237 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1238 glEnable (GL_LINE_STIPPLE);
1239 glLineStipple (0.5, 0xcccc);
1241 xoff = page->pagedim.left - page->pixmap->x;
1242 yoff -= page->pixmap->y;
1244 glBegin (GL_QUADS);
1245 for (link = page->drawpage->links; link; link = link->next) {
1246 fz_point p1, p2, p3, p4;
1247 fz_matrix ctm = page->pagedim.ctm;
1249 p1.x = link->rect.x0;
1250 p1.y = link->rect.y0;
1252 p2.x = link->rect.x1;
1253 p2.y = link->rect.y0;
1255 p3.x = link->rect.x1;
1256 p3.y = link->rect.y1;
1258 p4.x = link->rect.x0;
1259 p4.y = link->rect.y1;
1261 p1 = fz_transform_point (ctm, p1);
1262 p2 = fz_transform_point (ctm, p2);
1263 p3 = fz_transform_point (ctm, p3);
1264 p4 = fz_transform_point (ctm, p4);
1266 switch (link->kind) {
1267 case PDF_LINK_GOTO: glColor3ub (255, 0, 0); break;
1268 case PDF_LINK_URI: glColor3ub (0, 0, 255); break;
1269 default: glColor3ub (0, 0, 0); break;
1272 glVertex2f (p1.x + xoff, p1.y + yoff);
1273 glVertex2f (p2.x + xoff, p2.y + yoff);
1274 glVertex2f (p3.x + xoff, p3.y + yoff);
1275 glVertex2f (p4.x + xoff, p4.y + yoff);
1277 glEnd ();
1279 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1280 glDisable (GL_LINE_STIPPLE);
1283 static void upload2 (struct page *page, int slicenum, const char *cap)
1285 int i;
1286 int w;
1287 double start, end;
1288 struct slice *slice = &page->slices[slicenum];
1290 w = page->pixmap->w;
1292 ARSERT (w == slice->w);
1293 if (slice->texindex != -1
1294 && state.texowners[slice->texindex].slice == slice) {
1295 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1297 else {
1298 int subimage = 0;
1299 int index = (state.texindex++ % state.texcount);
1300 size_t offset = 0;
1302 for (i = 0; i < slicenum; ++i) {
1303 offset += w * page->slices[i].h * 4;
1306 if (state.texowners[index].w == slice->w) {
1307 if (state.texowners[index].h >= slice->h ) {
1308 subimage = 1;
1310 else {
1311 state.texowners[index].h = slice->h;
1314 else {
1315 state.texowners[index].h = slice->h;
1318 state.texowners[index].slice = slice;
1319 state.texowners[index].w = slice->w;
1320 slice->texindex = index;
1322 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1323 start = now ();
1324 if (subimage) {
1326 GLenum err = glGetError ();
1327 if (err != GL_NO_ERROR) {
1328 printf ("\033[0;31mERROR1 %d %d %#x\033[0m\n", w, slice->h, err);
1329 abort ();
1332 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1337 slice->h,
1338 state.texform,
1339 state.texty,
1340 page->pixmap->samples + offset
1343 GLenum err = glGetError ();
1344 if (err != GL_NO_ERROR) {
1345 printf ("\033[0;31mERROR %d %d %#x\033[0m\n", w, slice->h, err);
1346 abort ();
1350 else {
1351 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1353 GL_RGBA8,
1355 slice->h,
1357 state.texform,
1358 state.texty,
1359 page->pixmap->samples + offset
1363 end = now ();
1364 (void) start;
1365 (void) end;
1366 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
1367 subimage ? "sub" : "img",
1368 page->pageno, slicenum,
1369 slice->w, slice->h,
1370 state.texids[slice->texindex],
1371 end - start);
1375 #include "glfont.c"
1377 CAMLprim value ml_draw_string (value pt_v, value x_v, value y_v, value string_v)
1379 CAMLparam4 (pt_v, x_v, y_v, string_v);
1380 int pt = Int_val(pt_v);
1381 int x = Int_val (x_v);
1382 int y = Int_val (y_v);
1384 if (!state.face) {
1385 errx (1, "draw string with no face");
1388 glEnable (GL_TEXTURE_2D);
1389 glEnable (GL_BLEND);
1390 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1391 draw_string (state.face, pt, x, y, String_val (string_v));
1392 glDisable (GL_BLEND);
1393 glDisable (GL_TEXTURE_2D);
1395 CAMLreturn (Val_unit);
1398 CAMLprim value ml_draw (value args_v, value ptr_v)
1400 CAMLparam2 (args_v, ptr_v);
1401 int dispy = Int_val (Field (args_v, 0));
1402 int w = Int_val (Field (args_v, 1));
1403 int h = Int_val (Field (args_v, 2));
1404 int py = Int_val (Field (args_v, 3));
1405 int hlinks = Bool_val (Field (args_v, 4));
1406 char *s = String_val (ptr_v);
1407 int ret;
1408 void *ptr;
1409 struct page *page;
1410 int slicenum = 0;
1411 int yoff = dispy - py;
1413 ret = sscanf (s, "%p", &ptr);
1414 if (ret != 1) {
1415 errx (1, "cannot parse pointer `%s'", s);
1417 page = ptr;
1419 w = page->pixmap->w;
1421 ARSERT (h >= 0 && "ml_draw wrong h");
1422 ARSERT (py <= page->pixmap->h && "ml_draw wrong py");
1424 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1426 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
1427 struct slice *slice = &page->slices[slicenum];
1428 if (slice->h > py) {
1429 break;
1431 py -= slice->h;
1434 h = MIN (state.h, h);
1435 while (h) {
1436 int th, left;
1437 struct slice *slice = &page->slices[slicenum];
1439 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
1441 th = MIN (h, slice->h - py);
1442 upload2 (page, slicenum, "upload");
1444 left = page->pagedim.left;
1445 glBegin (GL_QUADS);
1447 glTexCoord2i (0, py);
1448 glVertex2i (left, dispy);
1450 glTexCoord2i (w, py);
1451 glVertex2i (left+w, dispy);
1453 glTexCoord2i (w, py+th);
1454 glVertex2i (left+w, dispy + th);
1456 glTexCoord2i (0, py+th);
1457 glVertex2i (left, dispy + th);
1459 glEnd ();
1461 h -= th;
1462 py = 0;
1463 dispy += th;
1464 slicenum += 1;
1467 showsel (page, yoff);
1468 if (hlinks) highlightlinks (page, yoff);
1469 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1471 CAMLreturn (Val_unit);
1474 static pdf_link *getlink (struct page *page, int x, int y)
1476 fz_point p;
1477 fz_matrix ctm;
1478 pdf_link *link;
1480 p.x = x + page->pixmap->x;
1481 p.y = y + page->pixmap->y;
1483 ctm = fz_invert_matrix (page->pagedim.ctm);
1484 p = fz_transform_point (ctm, p);
1486 for (link = page->drawpage->links; link; link = link->next) {
1487 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1488 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1489 return link;
1493 return NULL;
1496 static void ensuretext (struct page *page)
1498 if (!page->text) {
1499 fz_error error;
1500 fz_device *tdev;
1502 page->text = fz_new_text_span ();
1503 tdev = fz_new_text_device (page->text);
1504 error = pdf_run_page (state.xref, page->drawpage, tdev,
1505 page->pagedim.ctm);
1506 if (error) die (error);
1507 fz_free_device (tdev);
1511 CAMLprim value ml_whatsunder (value ptr_v, value x_v, value y_v)
1513 CAMLparam3 (ptr_v, x_v, y_v);
1514 CAMLlocal3 (ret_v, tup_v, str_v);
1515 pdf_link *link;
1516 struct page *page;
1517 char *s = String_val (ptr_v);
1519 ret_v = Val_int (0);
1520 if (trylock ("ml_whatsunder")) {
1521 goto done;
1524 page = parse_pointer ("ml_whatsunder", s);
1525 link = getlink (page, Int_val (x_v), Int_val (y_v));
1526 if (link) {
1527 switch (link->kind) {
1528 case PDF_LINK_GOTO:
1530 int pageno;
1531 fz_point p;
1532 fz_obj *obj;
1534 pageno = -1;
1535 p.x = 0;
1536 p.y = 0;
1538 if (fz_is_array (link->dest)) {
1539 obj = fz_array_get (link->dest, 0);
1540 if (fz_is_indirect (obj)) {
1541 pageno = pdf_find_page_number (state.xref, obj);
1543 else if (fz_is_int (obj)) {
1544 pageno = fz_to_int (obj);
1547 if (fz_array_len (link->dest) > 3) {
1548 fz_obj *xo, *yo;
1550 xo = fz_array_get (link->dest, 2);
1551 yo = fz_array_get (link->dest, 3);
1552 if (!fz_is_null (xo) && !fz_is_null (yo)) {
1553 p.x = fz_to_int (xo);
1554 p.y = fz_to_int (yo);
1555 p = fz_transform_point (page->pagedim.ctm, p);
1559 else {
1560 pageno = pdf_find_page_number (state.xref, link->dest);
1562 tup_v = caml_alloc_tuple (2);
1563 ret_v = caml_alloc_small (1, 1);
1564 Field (tup_v, 0) = Val_int (pageno);
1565 Field (tup_v, 1) = Val_int (p.y);
1566 Field (ret_v, 0) = tup_v;
1568 break;
1570 case PDF_LINK_URI:
1571 str_v = caml_copy_string (fz_to_str_buf (link->dest));
1572 ret_v = caml_alloc_small (1, 0);
1573 Field (ret_v, 0) = str_v;
1574 break;
1576 default:
1577 printd (state.sock, "T unhandled link kind %d", link->kind);
1578 break;
1581 else {
1582 int i, x, y;
1583 fz_text_span *span;
1585 ensuretext (page);
1586 x = Int_val (x_v) + page->pixmap->x;
1587 y = Int_val (y_v) + page->pixmap->y;
1589 for (span = page->text; span; span = span->next) {
1590 for (i = 0; i < span->len; ++i) {
1591 fz_bbox *b;
1592 b = &span->text[i].bbox;
1593 if ((x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1)) {
1594 const char *n2 =
1595 span->font && span->font->name
1596 ? span->font->name
1597 : "Span has no font name"
1599 #ifdef FT_FREETYPE_H
1600 FT_FaceRec *face = span->font->ft_face;
1601 if (face && face->family_name) {
1602 char *s;
1603 char *n1 = face->family_name;
1604 size_t l1 = strlen (n1);
1605 size_t l2 = strlen (n2);
1607 if (l1 != l2 || memcmp (n1, n2, l1)) {
1608 s = malloc (l1 + l2 + 2);
1609 if (s) {
1610 memcpy (s, n2, l2);
1611 s[l2] = '=';
1612 memcpy (s + l2 + 1, n1, l1 + 1);
1613 str_v = caml_copy_string (s);
1614 free (s);
1618 if (str_v == 0) {
1619 str_v = caml_copy_string (n2);
1621 #else
1622 str_v = caml_copy_string (n2);
1623 #endif
1624 ret_v = caml_alloc_small (1, 2);
1625 Field (ret_v, 0) = str_v;
1626 goto unlock;
1631 unlock:
1632 unlock ("ml_whatsunder");
1634 done:
1635 CAMLreturn (ret_v);
1638 CAMLprim value ml_seltext (value ptr_v, value rect_v, value oy_v)
1640 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1641 fz_bbox *b;
1642 struct page *page;
1643 fz_text_span *span;
1644 struct mark first, last;
1645 int i, x0, x1, y0, y1, oy, left;
1646 char *s = String_val (ptr_v);
1648 if (trylock ("ml_seltext")) {
1649 goto done;
1652 page = parse_pointer ("ml_seltext", s);
1653 ensuretext (page);
1655 oy = Int_val (oy_v);
1656 x0 = Int_val (Field (rect_v, 0));
1657 y0 = Int_val (Field (rect_v, 1));
1658 x1 = Int_val (Field (rect_v, 2));
1659 y1 = Int_val (Field (rect_v, 3));
1661 left = page->pagedim.left;
1662 if (0) {
1663 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1664 glColor3ub (128, 128, 128);
1665 glRecti (x0, y0, x1, y1);
1666 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1669 x0 += page->pixmap->x - left;
1670 y0 += page->pixmap->y - oy;
1671 x1 += page->pixmap->x - left;
1672 y1 += page->pixmap->y - oy;
1674 first.span = NULL;
1675 last.span = NULL;
1677 last.i = first.i = 0;
1678 first.span = page->text;
1679 for (span = page->text; span; span = span->next) {
1680 for (i = 0; i < span->len; ++i) {
1681 b = &span->text[i].bbox;
1682 int selected = 0;
1684 if (x0 >= b->x0 && x0 <= b->x1 && y0 >= b->y0 && y0 <= b->y1) {
1685 first.i = i;
1686 first.span = span;
1687 selected = 1;
1689 if (x1 >= b->x0 && x1 <= b->x1 && y1 >= b->y0 && y1 <= b->y1) {
1690 last.i = i;
1691 last.span = span;
1692 selected = 1;
1694 if (0 && selected) {
1695 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1696 glColor3ub (128, 128, 128);
1697 glRecti (b->x0+left, b->y0, b->x1+left, b->y1);
1698 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1703 if (y1 < y0 || x1 < x0) {
1704 int swap = 0;
1706 if (first.span == last.span) {
1707 swap = 1;
1709 else {
1710 if (y1 < y0) {
1711 for (span = first.span; span && span != last.span;
1712 span = span->next) {
1713 if (span->eol) {
1714 swap = 1;
1715 break;
1721 if (swap) {
1722 i = first.i;
1723 span = first.span;
1724 first.i = last.i;
1725 first.span = last.span;
1726 last.i = i;
1727 last.span = span;
1731 page->fmark = first;
1732 page->lmark = last;
1734 unlock ("ml_seltext");
1736 done:
1737 CAMLreturn (Val_unit);
1740 static int pipespan (FILE *f, fz_text_span *span, int a, int b)
1742 char buf[4];
1743 int i, len, ret;
1745 for (i = a; i <= b; ++i) {
1746 len = runetochar (buf, &span->text[i].c);
1747 ret = fwrite (buf, len, 1, f);
1749 if (ret != 1) {
1750 printd (state.sock, "T failed to write %d bytes ret=%d: %s",
1751 len, ret, strerror (errno));
1752 return -1;
1755 return 0;
1758 CAMLprim value ml_copysel (value ptr_v)
1760 CAMLparam1 (ptr_v);
1761 FILE *f;
1762 struct page *page;
1763 char *s = String_val (ptr_v);
1765 if (trylock ("ml_copysel")) {
1766 goto done;
1769 if (!*s) {
1770 close:
1771 #ifdef USE_XSEL
1772 if (state.xselpipe) {
1773 int ret = pclose (state.xselpipe);
1774 if (ret) {
1775 printd (state.sock, "T failed to close xsel pipe `%s'",
1776 strerror (errno));
1778 state.xselpipe = NULL;
1780 #else
1781 printf ("========================================\n");
1782 #endif
1784 else {
1785 fz_text_span *span;
1787 page = parse_pointer ("ml_sopysel", s);
1789 if (!page->fmark.span || !page->lmark.span) {
1790 printd (state.sock, "T nothing to copy");
1791 goto unlock;
1794 f = stdout;
1795 #ifdef USE_XSEL
1796 if (!state.xselpipe) {
1797 state.xselpipe = popen ("xsel -i", "w");
1798 if (!state.xselpipe) {
1799 printd (state.sock, "T failed to open xsel pipe `%s'",
1800 strerror (errno));
1802 else {
1803 f = state.xselpipe;
1806 else {
1807 f = state.xselpipe;
1809 #endif
1811 for (span = page->fmark.span;
1812 span && span != page->lmark.span->next;
1813 span = span->next) {
1814 int a = span == page->fmark.span ? page->fmark.i : 0;
1815 int b = span == page->lmark.span ? page->lmark.i : span->len - 1;
1816 if (pipespan (f, span, a, b)) {
1817 goto close;
1819 if (span->eol) {
1820 if (putc ('\n', f) == EOF) {
1821 printd (state.sock, "T failed break line on xsel pipe `%s'",
1822 strerror (errno));
1823 goto close;
1827 page->lmark.span = NULL;
1828 page->fmark.span = NULL;
1831 unlock:
1832 unlock ("ml_copysel");
1834 done:
1835 CAMLreturn (Val_unit);
1838 CAMLprim value ml_getpdimrect (value pagedimno_v)
1840 CAMLparam1 (pagedimno_v);
1841 CAMLlocal1 (ret_v);
1842 int pagedimno = Int_val (pagedimno_v);
1843 fz_rect box;
1845 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1846 if (trylock ("ml_getpdimrect")) {
1847 box = fz_empty_rect;
1849 else {
1850 box = state.pagedims[pagedimno].box;
1851 unlock ("ml_getpdimrect");
1854 Store_double_field (ret_v, 0, box.x0);
1855 Store_double_field (ret_v, 1, box.x1);
1856 Store_double_field (ret_v, 2, box.y0);
1857 Store_double_field (ret_v, 3, box.y1);
1859 CAMLreturn (ret_v);
1862 CAMLprim value ml_zoom_for_height (value winw_v, value winh_v, value dw_v)
1864 CAMLparam3 (winw_v, winh_v, dw_v);
1865 CAMLlocal1 (ret_v);
1866 int i;
1867 double zoom = 1.0;
1868 double maxw = 0.0, maxh = 0.0;
1869 struct pagedim *p;
1870 double winw = Int_val (winw_v);
1871 double winh = Int_val (winh_v);
1872 double dw = Int_val (dw_v);
1873 double pw = 1.0, ph = 1.0, num, den;
1875 if (trylock ("ml_zoom_for_height")) {
1876 goto done;
1879 if (state.proportional) {
1880 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
1881 double x0, x1, w;
1883 x0 = MIN (p->box.x0, p->box.x1);
1884 x1 = MAX (p->box.x0, p->box.x1);
1886 w = x1 - x0;
1887 maxw = MAX (w, maxw);
1891 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
1892 double x0, x1, y0, y1, w, h, scaledh, scale;
1894 x0 = MIN (p->box.x0, p->box.x1);
1895 y0 = MIN (p->box.y0, p->box.y1);
1896 x1 = MAX (p->box.x0, p->box.x1);
1897 y1 = MAX (p->box.y0, p->box.y1);
1899 w = x1 - x0;
1900 h = y1 - y0;
1902 if (state.proportional) {
1903 scale = w / maxw;
1904 scaledh = h * scale;
1906 else {
1907 scale = 1.0;
1908 scaledh = h;
1911 if (scaledh > maxh) {
1912 maxh = scaledh;
1913 ph = scaledh;
1914 pw = w * scale;
1918 num = (winh * pw) + (ph * dw);
1919 den = ph * winw;
1920 zoom = num / den;
1922 unlock ("ml_zoom_for_height");
1923 done:
1924 ret_v = caml_copy_double (zoom);
1925 CAMLreturn (ret_v);
1928 CAMLprim value ml_init (value sock_v, value params_v)
1930 CAMLparam2 (sock_v, params_v);
1931 #ifndef _WIN32
1932 int ret;
1933 #endif
1934 char *fontpath;
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 fontpath = String_val (Field (params_v, 4));
1941 state.texform = GL_RGBA;
1942 state.texty = GL_UNSIGNED_BYTE;
1944 if (*fontpath) {
1945 state.face = load_font (fontpath);
1947 else {
1948 int code;
1949 unsigned int len;
1950 void *base = pdf_find_builtin_font ("Courier-Bold", &len);
1952 if (!len || !base) errx (1, "failed to find builtin font\n");
1954 code = FT_New_Memory_Face (g_freetype_lib, base, len, 0, &state.face);
1955 if (code) errx (1, "failed to load font bultin font\n");
1957 if (!state.face) _exit (1);
1959 fz_accelerate ();
1960 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1961 if (!state.texids) {
1962 err (1, "calloc texids " FMT_s,
1963 state.texcount * sizeof (*state.texids));
1966 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1967 if (!state.texowners) {
1968 err (1, "calloc texowners " FMT_s,
1969 state.texcount * sizeof (*state.texowners));
1972 glGenTextures (state.texcount, state.texids);
1974 #ifdef _WIN32
1975 state.sock = Socket_val (sock_v);
1976 #else
1977 state.sock = Int_val (sock_v);
1978 #endif
1980 #ifdef _WIN32
1981 InitializeCriticalSection (&critsec);
1982 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
1983 if (state.thread == INVALID_HANDLE_VALUE) {
1984 errx (1, "CreateThread failed: %lx", GetLastError ());
1986 #else
1987 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1988 if (ret) {
1989 errx (1, "pthread_create: %s", strerror (ret));
1991 #endif
1993 CAMLreturn (Val_unit);