Run git describe in the source directory
[llpp.git] / link.c
blob3ffb1d7667248bd7c68293ee797d38f162765d25
1 /* lots of code c&p-ed directly from mupdf */
2 #include <errno.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <stdlib.h>
7 #ifdef _WIN32
8 #define WIN32_LEAN_AND_MEAN
9 #include <windows.h>
10 #include <winsock2.h>
11 #define fionread_arg long
12 #define ssize_t int
13 #define FMT_ss "%d"
14 #ifdef _WIN64
15 #define FMT_s "%i64u"
16 #else
17 #define FMT_s "%u"
18 #endif
19 #pragma warning (disable:4244)
20 #pragma warning (disable:4996)
21 #pragma warning (disable:4995)
22 #endif
24 #ifdef _MSC_VER
25 #define NORETURN __declspec (noreturn)
26 #define UNUSED
27 #define OPTIMIZE
28 #elif defined __GNUC__
29 #define NORETURN __attribute__ ((noreturn))
30 #define UNUSED __attribute__ ((unused))
31 #define OPTIMIZE(n) __attribute__ ((optimize ("O"#n)))
32 #else
33 #define NORETURN
34 #define UNUSED
35 #define OPTIMIZE
36 #endif
38 #ifdef _WIN32
39 static void __declspec (noreturn) sockerr (int exitcode, const char *fmt, ...)
41 va_list ap;
43 va_start (ap, fmt);
44 vfprintf (stderr, fmt, ap);
45 va_end (ap);
46 fprintf (stderr, ": wsaerror 0x%x\n", WSAGetLastError ());
47 _exit (exitcode);
49 #else
50 #define FMT_ss "%zd"
51 #define FMT_s "%zu"
52 #define fionread_arg int
53 #define ioctlsocket ioctl
54 #define sockerr err
55 #include <unistd.h>
56 #endif
58 #include <regex.h>
59 #include <ctype.h>
60 #include <stdarg.h>
61 #include <limits.h>
63 #ifndef _WIN32
64 #include <pthread.h>
65 #include <sys/time.h>
66 #include <sys/types.h>
67 #include <sys/socket.h>
68 #include <sys/ioctl.h>
69 #endif
71 static void NORETURN err (int exitcode, const char *fmt, ...)
73 va_list ap;
74 int savederrno;
76 savederrno = errno;
77 va_start (ap, fmt);
78 vfprintf (stderr, fmt, ap);
79 va_end (ap);
80 fprintf (stderr, ": %s\n", strerror (savederrno));
81 fflush (stderr);
82 _exit (exitcode);
85 static void NORETURN errx (int exitcode, const char *fmt, ...)
87 va_list ap;
89 va_start (ap, fmt);
90 vfprintf (stderr, fmt, ap);
91 va_end (ap);
92 fputc ('\n', stderr);
93 fflush (stderr);
94 _exit (exitcode);
97 #ifdef __APPLE__
98 #include <OpenGL/gl.h>
99 #else
100 #include <GL/gl.h>
101 #endif
103 #ifndef GL_TEXTURE_RECTANGLE_ARB
104 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
105 #endif
107 #include <caml/fail.h>
108 #include <caml/alloc.h>
109 #include <caml/memory.h>
110 #include <caml/unixsupport.h>
112 #include <fitz.h>
113 #include <mupdf.h>
115 #include FT_FREETYPE_H
117 #if 0
118 #define lprintf printf
119 #else
120 #define lprintf(...)
121 #endif
123 #define ARSERT(cond) for (;;) { \
124 if (!(cond)) { \
125 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
127 break; \
130 struct block {
131 int w;
132 int x;
133 int offset;
134 int texindex;
137 struct slice {
138 int h;
139 struct block *blocks;
142 struct pagedim {
143 int pageno;
144 int rotate;
145 int left;
146 fz_rect box;
147 fz_bbox bbox;
148 fz_matrix ctm, ctm1;
151 struct page {
152 int pageno;
153 int slicecount;
154 int blockcount;
155 fz_text_span *text;
156 fz_pixmap *pixmap;
157 pdf_page *drawpage;
158 struct pagedim pagedim;
159 struct mark {
160 int i;
161 fz_text_span *span;
162 } fmark, lmark;
163 struct slice *slices;
166 #if !defined _WIN32 && !defined __APPLE__
167 #define USE_XSEL
168 #endif
170 struct {
171 int sock;
172 int sliceheight, blockwidth;
173 struct page *pig;
174 struct pagedim *pagedims;
175 int pagecount;
176 int pagedimcount;
177 pdf_xref *xref;
178 fz_glyph_cache *cache;
179 int w, h;
181 int texindex;
182 int texcount;
183 GLuint *texids;
185 GLenum texform;
186 GLenum texty;
188 struct {
189 int w, h;
190 struct block *block;
191 } *texowners;
193 int rotate;
194 int proportional;
195 int needoutline;
197 #ifdef _WIN32
198 HANDLE thread;
199 #else
200 pthread_t thread;
201 #endif
202 FILE *xselpipe;
204 FT_Face face;
205 } state;
207 #ifdef _WIN32
208 static CRITICAL_SECTION critsec;
210 static void lock (void *unused)
212 (void) unused;
213 EnterCriticalSection (&critsec);
216 static void unlock (void *unused)
218 (void) unused;
219 LeaveCriticalSection (&critsec);
222 static int trylock (void *unused)
224 return TryEnterCriticalSection (&critsec) == 0;
226 #else
227 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
229 static void lock (const char *cap)
231 int ret = pthread_mutex_lock (&mutex);
232 if (ret) {
233 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
237 static void unlock (const char *cap)
239 int ret = pthread_mutex_unlock (&mutex);
240 if (ret) {
241 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
245 static int trylock (const char *cap)
247 int ret = pthread_mutex_trylock (&mutex);
249 if (ret && ret != EBUSY) {
250 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
252 return ret == EBUSY;
254 #endif
256 static void *parse_pointer (const char *cap, const char *s)
258 int ret;
259 void *ptr;
261 ret = sscanf (s, "%p", &ptr);
262 if (ret != 1) {
263 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
265 return ptr;
268 static int hasdata (int sock)
270 int ret;
271 fionread_arg avail;
272 ret = ioctlsocket (sock, FIONREAD, &avail);
273 if (ret) sockerr (1, "hasdata: FIONREAD error ret=%d", ret);
274 return avail > 0;
277 static double now (void)
279 struct timeval tv;
281 if (gettimeofday (&tv, NULL)) {
282 err (1, "gettimeofday");
284 return tv.tv_sec + tv.tv_usec*1e-6;
287 static void readdata (int fd, char *p, int size)
289 ssize_t n;
291 n = recv (fd, p, size, 0);
292 if (n - size) {
293 if (!n) errx (1, "EOF while reading");
294 sockerr (1, "recv (req %d, ret " FMT_ss ")", size, n);
298 static void writedata (int fd, char *p, int size)
300 char buf[4];
301 ssize_t n;
303 buf[0] = (size >> 24) & 0xff;
304 buf[1] = (size >> 16) & 0xff;
305 buf[2] = (size >> 8) & 0xff;
306 buf[3] = (size >> 0) & 0xff;
308 n = send (fd, buf, 4, 0);
309 if (n != 4) {
310 if (!n) errx (1, "EOF while writing length");
311 sockerr (1, "send " FMT_ss, n);
314 n = send (fd, p, size, 0);
315 if (n - size) {
316 if (!n) errx (1, "EOF while writing data");
317 sockerr (1, "send (req %d, ret " FMT_ss ")", size, n);
321 static void
322 #ifdef __GNUC__
323 __attribute__ ((format (printf, 2, 3)))
324 #endif
325 printd (int fd, const char *fmt, ...)
327 int size = 200, len;
328 va_list ap;
329 char *buf;
331 buf = malloc (size);
332 for (;;) {
333 if (!buf) err (errno, "malloc for temp buf (%d bytes) failed", size);
335 va_start (ap, fmt);
336 len = vsnprintf (buf, size, fmt, ap);
337 va_end (ap);
339 if (len > -1 && len < size) {
340 writedata (fd, buf, len);
341 break;
344 if (len > -1) {
345 size = len + 1;
347 else {
348 size *= 2;
350 buf = realloc (buf, size);
352 free (buf);
355 static void die (fz_error error)
357 fz_catch (error, "aborting");
358 if (state.xref)
359 pdf_free_xref (state.xref);
360 _exit (1);
363 static void openxref (char *filename, char *password)
365 int i;
366 fz_error error;
368 for (i = 0; i < state.texcount; ++i) {
369 state.texowners[i].block = NULL;
372 if (state.cache) {
373 fz_free_glyph_cache (state.cache);
376 state.cache = fz_new_glyph_cache ();
377 if (!state.cache) {
378 errx (1, "fz_newglyph_cache failed");
381 if (state.xref) {
382 if (state.xref->store) {
383 pdf_free_store (state.xref->store);
384 state.xref->store = NULL;
386 pdf_free_xref (state.xref);
387 state.xref = NULL;
390 if (state.pagedims) {
391 free (state.pagedims);
392 state.pagedims = NULL;
394 state.pagedimcount = 0;
396 error = pdf_open_xref (&state.xref, filename, password);
397 if (error) {
398 die (error);
401 error = pdf_load_page_tree (state.xref);
402 if (error) {
403 die (error);
406 state.pagecount = pdf_count_pages (state.xref);
409 static void pdfinfo (void)
411 fz_obj *infoobj;
413 printd (state.sock, "i PDF version\t%d.%d",
414 state.xref->version / 10, state.xref->version % 10);
416 infoobj = fz_dict_gets (state.xref->trailer, "Info");
417 if (infoobj) {
418 int i;
419 char *s;
420 char *items[] = { "Title", "Creator", "Producer", "CreationDate" };
422 for (i = 0; i < sizeof (items) / sizeof (*items); ++i) {
423 fz_obj *obj = fz_dict_gets (infoobj, items[i]);
424 s = pdf_to_utf8 (obj);
425 if (*s) {
426 if (i == 0) {
427 printd (state.sock, "t %s", s);
429 printd (state.sock, "i %s\t%s", items[i], s);
431 fz_free (s);
433 printd (state.sock, "ie");
437 static int readlen (int fd)
439 ssize_t n;
440 unsigned char p[4];
442 n = recv (fd, p, 4, 0);
443 if (n != 4) {
444 if (!n) errx (1, "EOF while reading length");
445 sockerr (1, "recv " FMT_ss, n);
448 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
451 static void unlinkpage (struct page *page)
453 int i;
455 for (i = 0; i < page->slicecount; ++i) {
456 int j;
457 struct slice *s = &page->slices[i];
459 for (j = 0; j < page->blockcount; ++j) {
460 struct block *b = &s->blocks[j];
462 if (b->texindex != -1) {
463 if (state.texowners[b->texindex].block == b) {
464 state.texowners[b->texindex].block = NULL;
471 static void freepage (struct page *page)
473 int i;
475 fz_drop_pixmap (page->pixmap);
477 unlinkpage (page);
479 if (page->text) {
480 fz_free_text_span (page->text);
482 if (page->drawpage) {
483 pdf_free_page (page->drawpage);
485 for (i = 0; i < page->slicecount; ++i) {
486 free (page->slices[i].blocks);
488 free (page->slices);
489 free (page);
492 static void subdivide (struct page *p, int blockcount)
494 int i;
495 int w = p->pixmap->w;
496 int h = p->pixmap->h;
497 int offset = 0;
498 int tw = MIN (w, state.blockwidth);
499 int th = MIN (h, state.sliceheight);
501 for (i = 0; i < p->slicecount; ++i) {
502 int j, x, offset1;
503 struct slice *s = &p->slices[i];
505 s->h = MIN (th, h);
507 s->blocks = calloc (sizeof (struct block), p->blockcount);
508 if (!s->blocks) {
509 err (1, "calloc blocks page %d slice %d, %d",
510 p->pageno, i, p->blockcount);
513 w = p->pixmap->w;
514 x = 0;
515 offset1 = offset;
517 for (j = 0; j < blockcount; ++j) {
518 struct block *b = &s->blocks[j];
520 b->texindex = -1;
521 b->w = MIN (tw, w);
522 b->x = x;
523 b->offset = offset1;
524 w -= tw;
525 x += tw;
526 offset1 += tw * 4;
528 offset += s->h * p->pixmap->w * 4;
529 h -= th;
533 static int compatpdims (struct pagedim *p1, struct pagedim *p2)
535 return p1->rotate == p2->rotate
536 && !memcmp (&p1->bbox, &p2->bbox, sizeof (p1->bbox))
537 && !memcmp (&p1->ctm, &p2->ctm, sizeof (p1->ctm));
540 #ifdef __ALTIVEC__
541 #include <altivec.h>
543 static int cacheline32bytes;
544 extern char **environ;
546 static void __attribute__ ((constructor)) clcheck (void)
548 char **envp = environ;
549 unsigned long *auxv;
551 while (*envp++);
553 for (auxv = (unsigned long *) envp; *auxv != 0; auxv += 2) {
554 if (*auxv == 19) {
555 cacheline32bytes = auxv[1] == 32;
556 return;
561 static void OPTIMIZE (3) clearpixmap (fz_pixmap *pixmap)
563 if (cacheline32bytes) {
564 intptr_t a1, a2, diff;
565 size_t sizea, i, size = pixmap->w * pixmap->h * pixmap->n;
566 vector unsigned char v = vec_splat_u8 (-1);
567 vector unsigned char *p;
569 a1 = a2 = (intptr_t) pixmap->samples;
570 a2 = (a1 + 31) & ~31;
571 diff = a2 - a1;
572 sizea = size - diff;
573 p = (void *) a2;
575 while (a1 != a2) *(char *) a1++ = 0xff;
576 for (i = 0; i < (sizea & ~31); i += 32) {
577 __asm volatile ("dcbz %0, %1"::"b"(a2),"r"(i));
578 vec_st (v, i, p);
579 vec_st (v, i + 16, p);
581 while (i < sizea) *((char *) a1 + i++) = 0xff;
583 else fz_clear_pixmap_with_color (pixmap, 0xff);
585 #else
586 #define clearpixmap(p) fz_clear_pixmap_with_color (p, 0xff)
587 #endif
589 static void *render (int pageno, int pindex)
591 fz_error error;
592 fz_device *idev;
593 double start, end;
594 pdf_page *drawpage;
595 struct pagedim *pagedim;
596 struct page *page = NULL;
597 int slicecount, blockcount;
599 start = now ();
600 printd (state.sock, "V rendering %d", pageno);
602 pagedim = &state.pagedims[pindex];
603 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
604 + state.sliceheight - 1) / state.sliceheight;
605 slicecount += slicecount == 0;
607 blockcount = (pagedim->bbox.x1 - pagedim->bbox.x0
608 + state.blockwidth - 1) / state.blockwidth;
609 blockcount += blockcount == 0;
611 if (state.pig) {
612 if (compatpdims (&state.pig->pagedim, pagedim)) {
613 page = state.pig;
614 if (page->text) {
615 fz_free_text_span (page->text);
616 page->text = NULL;
618 if (page->drawpage) {
619 pdf_free_page (page->drawpage);
620 page->drawpage = NULL;
623 else {
624 freepage (state.pig);
627 if (!page) {
628 page = calloc (sizeof (struct page), 1);
629 if (!page) {
630 err (1, "calloc page %d", pageno);
632 page->slices = calloc (sizeof (struct slice), slicecount);
633 if (!page->slices) {
634 err (1, "calloc slices %d %d", pageno, slicecount);
636 page->pixmap = fz_new_pixmap_with_rect (fz_device_rgb, pagedim->bbox);
639 page->slicecount = slicecount;
640 page->blockcount = blockcount;
642 error = pdf_load_page (&drawpage, state.xref, pageno - 1);
643 if (error)
644 die (error);
646 clearpixmap (page->pixmap);
648 idev = fz_new_draw_device (state.cache, page->pixmap);
649 if (!idev)
650 die (fz_throw ("fz_newdrawdevice failed"));
651 error = pdf_run_page (state.xref, drawpage, idev, pagedim->ctm);
652 if (error)
653 die (fz_rethrow (error, "pdf_runpage failed"));
654 fz_free_device (idev);
656 page->drawpage = drawpage;
657 page->pagedim = *pagedim;
658 page->pageno = pageno;
659 subdivide (page, blockcount);
660 end = now ();
662 pdf_age_store (state.xref->store, 3);
664 printd (state.sock, "V rendering %d took %f sec", pageno, end - start);
665 state.pig = NULL;
666 return page;
669 static void initpdims (void)
671 int pageno;
672 double start, end;
674 start = now ();
675 for (pageno = 0; pageno < state.pagecount; ++pageno) {
676 int rotate;
677 fz_rect box;
678 struct pagedim *p;
679 fz_obj *obj, *pageobj;
681 pageobj = state.xref->page_objs[pageno];
683 obj = fz_dict_gets (pageobj, "CropBox");
684 if (!fz_is_array (obj)) {
685 obj = fz_dict_gets (pageobj, "MediaBox");
686 if (!fz_is_array (obj)) {
687 die (fz_throw ("cannot find page bounds %d (%d Rd)",
688 fz_to_num (pageobj), fz_to_gen (pageobj)));
691 box = pdf_to_rect (obj);
693 obj = fz_dict_gets (pageobj, "Rotate");
694 if (fz_is_int (obj)) {
695 rotate = fz_to_int (obj);
697 else {
698 rotate = 0;
700 rotate += state.rotate;
702 p = &state.pagedims[state.pagedimcount - 1];
703 if ((state.pagedimcount == 0)
704 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
705 size_t size;
707 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
708 state.pagedims = realloc (state.pagedims, size);
709 if (!state.pagedims) {
710 err (1, "realloc pagedims to " FMT_s " (%d elems)",
711 size, state.pagedimcount + 1);
713 p = &state.pagedims[state.pagedimcount++];
714 p->rotate = rotate;
715 p->box = box;
716 p->pageno = pageno;
719 end = now ();
720 printd (state.sock, "T Processed %d pages in %f seconds",
721 state.pagecount, end - start);
724 static void layout (void)
726 int pindex;
727 fz_matrix ctm;
728 fz_rect box, box2;
729 double zoom, w, maxw = 0;
730 struct pagedim *p = state.pagedims;
732 if (state.proportional) {
733 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
734 box.x0 = MIN (p->box.x0, p->box.x1);
735 box.y0 = MIN (p->box.y0, p->box.y1);
736 box.x1 = MAX (p->box.x0, p->box.x1);
737 box.y1 = MAX (p->box.y0, p->box.y1);
739 ctm = fz_identity;
740 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
741 ctm = fz_concat (ctm, fz_rotate (p->rotate));
742 box2 = fz_transform_rect (ctm, box);
743 w = box2.x1 - box2.x0;
744 maxw = MAX (w, maxw);
748 p = state.pagedims;
749 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
750 box.x0 = MIN (p->box.x0, p->box.x1);
751 box.y0 = MIN (p->box.y0, p->box.y1);
752 box.x1 = MAX (p->box.x0, p->box.x1);
753 box.y1 = MAX (p->box.y0, p->box.y1);
755 ctm = fz_identity;
756 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
757 ctm = fz_concat (ctm, fz_rotate (p->rotate));
758 box2 = fz_transform_rect (ctm, box);
759 w = box2.x1 - box2.x0;
761 if (state.proportional) {
762 double scale = w / maxw;
763 zoom = (state.w / w) * scale;
765 else {
766 zoom = state.w / w;
768 ctm = fz_identity;
769 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
770 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
771 memcpy (&p->ctm1, &ctm, sizeof (ctm));
772 ctm = fz_concat (ctm, fz_rotate (p->rotate));
773 p->bbox = fz_round_rect (fz_transform_rect (ctm, box));
774 p->left = state.proportional ? ((maxw - w) * zoom) / 2.0 : 0;
775 memcpy (&p->ctm, &ctm, sizeof (ctm));
778 while (p-- != state.pagedims) {
779 printd (state.sock, "l %d %d %d %d",
780 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0,
781 p->left);
785 static void recurse_outline (pdf_outline *outline, int level)
787 while (outline) {
788 int i;
789 fz_obj *obj;
790 int pageno = -1;
791 int top = 0, h = 0;
793 if (!outline->link) goto next;
795 obj = outline->link->dest;
796 if (fz_is_indirect (obj)) {
797 obj = fz_resolve_indirect (obj);
799 if (fz_is_array (obj)) {
800 fz_obj *obj2;
802 obj2 = fz_array_get (obj, 0);
803 if (fz_is_int (obj2)) {
804 pageno = fz_to_int (obj2);
806 else {
807 pageno = pdf_find_page_number (state.xref, obj2);
810 if (fz_array_len (obj) > 3) {
811 fz_point p;
812 fz_obj *xo, *yo;
814 xo = fz_array_get (obj, 2);
815 yo = fz_array_get (obj, 3);
816 if (!fz_is_null (xo) && !fz_is_null (yo)) {
817 struct pagedim *pagedim = state.pagedims;
819 for (i = 0; i < state.pagedimcount; ++i) {
820 if (state.pagedims[i].pageno > pageno)
821 break;
822 pagedim = &state.pagedims[i];
824 p.x = fz_to_int (xo);
825 p.y = fz_to_int (yo);
826 p = fz_transform_point (pagedim->ctm, p);
827 h = pagedim->bbox.y1 - pagedim->bbox.y0;
828 top = p.y;
832 else {
833 pageno = pdf_find_page_number (state.xref, outline->link->dest);
836 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
837 if (pageno > 0) {
838 printd (state.sock, "o %d %d %d %d %s",
839 level, pageno, top, h, outline->title);
841 next:
842 if (outline->child) {
843 recurse_outline (outline->child, level + 1);
845 outline = outline->next;
849 static void process_outline (void)
851 pdf_outline *outline;
853 if (!state.needoutline) return;
855 state.needoutline = 0;
856 outline = pdf_load_outline (state.xref);
857 if (outline) {
858 recurse_outline (outline, 0);
859 pdf_free_outline (outline);
863 static int comparespans (const void *l, const void *r)
865 fz_text_span const *const*ls = l;
866 fz_text_span const *const*rs = r;
867 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
870 /* wishful thinking function */
871 static void search (regex_t *re, int pageno, int y, int forward)
873 int i, j;
874 int ret;
875 char *p;
876 char buf[256];
877 fz_matrix ctm;
878 fz_error error;
879 fz_device *tdev;
880 pdf_page *drawpage;
881 fz_text_span *text, *span, **pspan;
882 struct pagedim *pdim, *pdimprev;
883 int stop = 0;
884 int niters = 0;
885 int nspans;
886 double start, end;
888 start = now ();
889 while (pageno >= 0 && pageno < state.pagecount && !stop) {
890 if (niters++ == 5) {
891 pdf_age_store (state.xref->store, 3);
892 niters = 0;
893 if (hasdata (state.sock)) {
894 printd (state.sock, "T attention requested aborting search at %d",
895 pageno);
896 stop = 1;
898 else {
899 printd (state.sock, "T searching in page %d", pageno);
902 pdimprev = NULL;
903 for (i = 0; i < state.pagedimcount; ++i) {
904 pdim = &state.pagedims[i];
905 if (pdim->pageno == pageno) {
906 goto found;
908 if (pdim->pageno > pageno) {
909 pdim = pdimprev;
910 goto found;
912 pdimprev = pdim;
914 pdim = pdimprev;
915 found:
917 error = pdf_load_page (&drawpage, state.xref, pageno);
918 if (error)
919 die (error);
921 ctm = fz_rotate (pdim->rotate);
923 text = fz_new_text_span ();
924 tdev = fz_new_text_device (text);
925 error = pdf_run_page (state.xref, drawpage, tdev, pdim->ctm1);
926 if (error) die (error);
927 fz_free_device (tdev);
929 nspans = 0;
930 for (span = text; span; span = span->next) {
931 nspans++;
933 pspan = malloc (sizeof (void *) * nspans);
934 if (!pspan) {
935 err (1, "malloc span pointers " FMT_s, sizeof (void *) * nspans);
937 for (i = 0, span = text; span; span = span->next, ++i) {
938 pspan[i] = span;
940 qsort (pspan, nspans, sizeof (fz_text_span *), comparespans);
942 j = forward ? 0 : nspans - 1;
943 while (nspans--) {
944 regmatch_t rm;
946 span = pspan[j];
947 j += forward ? 1 : -1;
948 p = buf;
949 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
950 if (forward) {
951 if (span->text[i].bbox.y0 < y + 1) {
952 continue;
955 else {
956 if (span->text[i].bbox.y0 > y - 1) {
957 continue;
960 if (span->text[i].c < 256) {
961 *p++ = span->text[i].c;
963 else {
964 *p++ = '?';
967 if (p == buf) {
968 continue;
970 *p++ = 0;
972 ret = regexec (re, buf, 1, &rm, 0);
973 if (ret) {
974 if (ret != REG_NOMATCH) {
975 size_t size;
976 char errbuf[80];
977 size = regerror (ret, re, errbuf, sizeof (errbuf));
978 printd (state.sock,
979 "T regexec error `%.*s'",
980 (int) size, errbuf);
981 fz_free_text_span (text);
982 pdf_free_page (drawpage);
983 free (pspan);
984 return;
987 else {
988 int xoff, yoff;
989 fz_bbox *sb, *eb;
990 fz_point p1, p2, p3, p4;
992 xoff = pdim->left - pdim->bbox.x0;
993 yoff = -pdim->bbox.y0;
995 sb = &span->text[rm.rm_so].bbox;
996 eb = &span->text[rm.rm_eo - 1].bbox;
998 p1.x = sb->x0;
999 p1.y = sb->y0;
1000 p2.x = eb->x1;
1001 p2.y = sb->y0;
1002 p3.x = eb->x1;
1003 p3.y = eb->y1;
1004 p4.x = sb->x0;
1005 p4.y = eb->y1;
1007 p1 = fz_transform_point (ctm, p1);
1008 p2 = fz_transform_point (ctm, p2);
1009 p3 = fz_transform_point (ctm, p3);
1010 p4 = fz_transform_point (ctm, p4);
1012 if (!stop) {
1013 printd (state.sock, "F %d %d %f %f %f %f %f %f %f %f",
1014 pageno, 1,
1015 p1.x + xoff, p1.y + yoff,
1016 p2.x + xoff, p2.y + yoff,
1017 p3.x + xoff, p3.y + yoff,
1018 p4.x + xoff, p4.y + yoff);
1020 printd (state.sock, "T found at %d `%.*s' in %f sec",
1021 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
1022 now () - start);
1024 else {
1025 printd (state.sock, "R %d %d %f %f %f %f %f %f %f %f",
1026 pageno, 2,
1027 p1.x + xoff, p1.y + yoff,
1028 p2.x + xoff, p2.y + yoff,
1029 p3.x + xoff, p3.y + yoff,
1030 p4.x + xoff, p4.y + yoff);
1032 stop = 1;
1035 if (forward) {
1036 pageno += 1;
1037 y = 0;
1039 else {
1040 pageno -= 1;
1041 y = INT_MAX;
1043 fz_free_text_span (text);
1044 pdf_free_page (drawpage);
1045 free (pspan);
1047 end = now ();
1048 if (!stop) {
1049 printd (state.sock, "T no matches %f sec", end - start);
1051 printd (state.sock, "D");
1054 static
1055 #ifdef _WIN32
1056 DWORD _stdcall
1057 #else
1058 void *
1059 #endif
1060 mainloop (void *unused)
1062 char *p = NULL;
1063 int len, ret, oldlen = 0;
1065 for (;;) {
1066 len = readlen (state.sock);
1067 if (len == 0) {
1068 errx (1, "readlen returned 0");
1071 if (oldlen < len + 1) {
1072 p = realloc (p, len + 1);
1073 if (!p) {
1074 err (1, "realloc %d failed", len + 1);
1076 oldlen = len + 1;
1078 readdata (state.sock, p, len);
1079 p[len] = 0;
1081 if (!strncmp ("open", p, 4)) {
1082 size_t filenamelen;
1083 char *password;
1084 char *filename = p + 5;
1086 filenamelen = strlen (filename);
1087 password = filename + filenamelen + 1;
1089 openxref (filename, password);
1090 initpdims ();
1091 pdfinfo ();
1092 state.needoutline = 1;
1094 else if (!strncmp ("free", p, 4)) {
1095 void *ptr;
1097 ret = sscanf (p + 4, " %p", &ptr);
1098 if (ret != 1) {
1099 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
1101 unlinkpage (ptr);
1102 state.pig = ptr;
1104 else if (!strncmp ("search", p, 6)) {
1105 int icase, pageno, y, ret, len2, forward;
1106 char *pattern;
1107 regex_t re;
1109 ret = sscanf (p + 6, " %d %d %d %d,%n",
1110 &icase, &pageno, &y, &forward, &len2);
1111 if (ret != 4) {
1112 errx (1, "malformed search `%s' ret=%d", p, ret);
1115 pattern = p + 6 + len2;
1116 ret = regcomp (&re, pattern,
1117 REG_EXTENDED | (icase ? REG_ICASE : 0));
1118 if (ret) {
1119 char errbuf[80];
1120 size_t size;
1122 size = regerror (ret, &re, errbuf, sizeof (errbuf));
1123 printd (state.sock, "T regcomp failed `%.*s'",
1124 (int) size, errbuf);
1126 else {
1127 search (&re, pageno, y, forward);
1128 regfree (&re);
1131 else if (!strncmp ("geometry", p, 8)) {
1132 int w, h;
1134 printd (state.sock, "c");
1135 ret = sscanf (p + 8, " %d %d", &w, &h);
1136 if (ret != 2) {
1137 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
1140 lock ("geometry");
1141 state.h = h;
1142 if (w != state.w) {
1143 int i;
1144 state.w = w;
1145 for (i = 0; i < state.texcount; ++i) {
1146 state.texowners[i].block = NULL;
1149 layout ();
1150 process_outline ();
1151 unlock ("geometry");
1152 printd (state.sock, "C %d", state.pagecount);
1154 else if (!strncmp ("reinit", p, 6)) {
1155 float rotate;
1156 int proportional;
1158 printd (state.sock, "c");
1159 ret = sscanf (p + 6, " %f %d", &rotate, &proportional);
1160 if (ret != 2) {
1161 errx (1, "bad rotate line `%.*s' ret=%d", len, p, ret);
1163 lock ("reinit");
1164 state.rotate = rotate;
1165 state.proportional = proportional;
1166 state.pagedimcount = 0;
1167 free (state.pagedims);
1168 state.pagedims = NULL;
1169 initpdims ();
1170 layout ();
1171 process_outline ();
1172 if (state.pig) {
1173 freepage (state.pig);
1174 state.pig = NULL;
1176 unlock ("reinit");
1177 printd (state.sock, "C %d", state.pagecount);
1179 else if (!strncmp ("render", p, 6)) {
1180 int pageno, pindex, w, h, ret;
1181 struct page *page;
1183 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
1184 if (ret != 4) {
1185 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
1188 lock ("render");
1189 page = render (pageno, pindex);
1190 unlock ("render");
1192 printd (state.sock, "r %d %d %d %d %d %d %p",
1193 pageno,
1194 state.w,
1195 state.h,
1196 state.rotate,
1197 state.proportional,
1198 w * h * 4,
1199 page);
1201 else if (!strncmp ("interrupt", p, 9)) {
1202 printd (state.sock, "V interrupted");
1204 else {
1205 errx (1, "unknown command %.*s", len, p);
1208 return 0;
1211 static void showsel (struct page *page, int oy)
1213 int ox;
1214 fz_bbox bbox;
1215 fz_text_span *span;
1216 struct mark first, last;
1218 first = page->fmark;
1219 last = page->lmark;
1221 if (!first.span || !last.span) return;
1223 glEnable (GL_BLEND);
1224 glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA);
1225 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1227 ox = -page->pixmap->x + page->pagedim.left;
1228 oy = -page->pixmap->y + oy;
1229 for (span = first.span; span; span = span->next) {
1230 int i, j, k;
1232 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0;
1234 j = 0;
1235 k = span->len - 1;
1237 if (span == page->fmark.span && span == page->lmark.span) {
1238 j = MIN (first.i, last.i);
1239 k = MAX (first.i, last.i);
1241 else if (span == first.span) {
1242 j = first.i;
1244 else if (span == last.span) {
1245 k = last.i;
1248 for (i = j; i <= k; ++i) {
1249 bbox = fz_union_bbox (bbox, span->text[i].bbox);
1251 lprintf ("%d %d %d %d oy=%d ox=%d\n",
1252 bbox.x0,
1253 bbox.y0,
1254 bbox.x1,
1255 bbox.y1,
1256 oy, ox);
1258 glRecti (bbox.x0 + ox, bbox.y0 + oy, bbox.x1 + ox, bbox.y1 + oy);
1260 if (span == last.span) break;
1262 glDisable (GL_BLEND);
1265 static void highlightlinks (struct page *page, int yoff)
1267 pdf_link *link;
1268 int xoff;
1270 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1271 glEnable (GL_LINE_STIPPLE);
1272 glLineStipple (0.5, 0xcccc);
1274 xoff = page->pagedim.left - page->pixmap->x;
1275 yoff -= page->pixmap->y;
1277 glBegin (GL_QUADS);
1278 for (link = page->drawpage->links; link; link = link->next) {
1279 fz_point p1, p2, p3, p4;
1280 fz_matrix ctm = page->pagedim.ctm;
1282 p1.x = link->rect.x0;
1283 p1.y = link->rect.y0;
1285 p2.x = link->rect.x1;
1286 p2.y = link->rect.y0;
1288 p3.x = link->rect.x1;
1289 p3.y = link->rect.y1;
1291 p4.x = link->rect.x0;
1292 p4.y = link->rect.y1;
1294 p1 = fz_transform_point (ctm, p1);
1295 p2 = fz_transform_point (ctm, p2);
1296 p3 = fz_transform_point (ctm, p3);
1297 p4 = fz_transform_point (ctm, p4);
1299 switch (link->kind) {
1300 case PDF_LINK_GOTO: glColor3ub (255, 0, 0); break;
1301 case PDF_LINK_URI: glColor3ub (0, 0, 255); break;
1302 default: glColor3ub (0, 0, 0); break;
1305 glVertex2f (p1.x + xoff, p1.y + yoff);
1306 glVertex2f (p2.x + xoff, p2.y + yoff);
1307 glVertex2f (p3.x + xoff, p3.y + yoff);
1308 glVertex2f (p4.x + xoff, p4.y + yoff);
1310 glEnd ();
1312 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1313 glDisable (GL_LINE_STIPPLE);
1316 static void upload (struct page *page, int slicenum, int blocknum)
1318 double start, end;
1319 struct slice *slice = &page->slices[slicenum];
1320 struct block *block = &slice->blocks[blocknum];
1322 if (block->texindex != -1
1323 && state.texowners[block->texindex].block == block) {
1324 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[block->texindex]);
1326 else {
1327 int subimage = 0;
1328 int index = (state.texindex++ % state.texcount);
1330 if (state.texowners[index].w == block->w) {
1331 if (state.texowners[index].h >= slice->h ) {
1332 subimage = 1;
1334 else {
1335 state.texowners[index].h = slice->h;
1338 else {
1339 state.texowners[index].h = slice->h;
1342 state.texowners[index].w = block->w;
1343 state.texowners[index].block = block;
1344 block->texindex = index;
1346 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[block->texindex]);
1347 glPixelStorei (GL_UNPACK_ROW_LENGTH, page->pixmap->w);
1349 start = now ();
1350 if (0) {
1351 GLenum err = glGetError ();
1352 if (err != GL_NO_ERROR) {
1353 printf ("\033[0;31mERROR1 %d %d %#x\033[0m\n",
1354 block->w, slice->h, err);
1355 abort ();
1358 if (subimage) {
1359 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1363 block->w,
1364 slice->h,
1365 state.texform,
1366 state.texty,
1367 page->pixmap->samples + block->offset
1370 else {
1371 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1373 GL_RGBA8,
1374 block->w,
1375 slice->h,
1377 state.texform,
1378 state.texty,
1379 page->pixmap->samples + block->offset
1382 if (0) {
1383 GLenum err = glGetError ();
1384 if (err != GL_NO_ERROR) {
1385 printf ("\033[0;31mERROR %d %d %#x\033[0m\n",
1386 block->w, slice->h, err);
1387 abort ();
1391 end = now ();
1392 (void) start;
1393 (void) end;
1394 lprintf ("%s[%d] slice=%d,%d(%d,%d) tex=%d,%d offset=%d %f sec\n",
1395 subimage ? "sub" : "img",
1396 page->pageno, slicenum, blocknum,
1397 block->w, slice->h,
1398 block->texindex,
1399 state.texids[block->texindex],
1400 block->offset,
1401 end - start);
1405 CAMLprim value ml_draw (value args_v, value ptr_v)
1407 CAMLparam2 (args_v, ptr_v);
1408 int dispy = Int_val (Field (args_v, 0));
1409 int h = Int_val (Field (args_v, 1));
1410 int py = Int_val (Field (args_v, 2));
1411 int hlinks = Bool_val (Field (args_v, 3));
1412 char *s = String_val (ptr_v);
1413 int ret;
1414 void *ptr;
1415 struct page *page;
1416 int slicenum;
1417 int yoff = dispy - py;
1418 struct slice *slice;
1420 ret = sscanf (s, "%p", &ptr);
1421 if (ret != 1) {
1422 errx (1, "cannot parse pointer `%s'", s);
1424 page = ptr;
1426 ARSERT (h >= 0 && "ml_draw wrong h");
1427 ARSERT (py <= page->pixmap->h && "ml_draw wrong py");
1429 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1431 for (slicenum = 0, slice = page->slices;
1432 slicenum < page->slicecount; ++slicenum, ++slice) {
1433 if (slice->h > py) {
1434 break;
1436 py -= slice->h;
1438 h = MIN (state.h, h);
1440 while (h) {
1441 int th, left, blocknum;
1443 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
1445 th = MIN (h, slice->h - py);
1446 left = page->pagedim.left;
1448 for (blocknum = 0; blocknum < page->blockcount; ++blocknum) {
1449 struct block *block = &slice->blocks[blocknum];
1451 upload (page, slicenum, blocknum);
1452 glBegin (GL_QUADS);
1454 glTexCoord2i (0, py);
1455 glVertex2i (left, dispy);
1457 glTexCoord2i (block->w, py);
1458 glVertex2i (left + block->w, dispy);
1460 glTexCoord2i (block->w, py + th);
1461 glVertex2i (left + block->w, dispy + th);
1463 glTexCoord2i (0, py + th);
1464 glVertex2i (left, dispy + th);
1466 glEnd ();
1467 left += block->w;
1470 h -= th;
1471 py = 0;
1472 dispy += th;
1473 slicenum += 1;
1476 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1478 showsel (page, yoff);
1479 if (hlinks) highlightlinks (page, yoff);
1481 CAMLreturn (Val_unit);
1484 static pdf_link *getlink (struct page *page, int x, int y)
1486 fz_point p;
1487 fz_matrix ctm;
1488 pdf_link *link;
1490 p.x = x + page->pixmap->x;
1491 p.y = y + page->pixmap->y;
1493 ctm = fz_invert_matrix (page->pagedim.ctm);
1494 p = fz_transform_point (ctm, p);
1496 for (link = page->drawpage->links; link; link = link->next) {
1497 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1498 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1499 return link;
1503 return NULL;
1506 static void ensuretext (struct page *page)
1508 if (!page->text) {
1509 fz_error error;
1510 fz_device *tdev;
1512 page->text = fz_new_text_span ();
1513 tdev = fz_new_text_device (page->text);
1514 error = pdf_run_page (state.xref, page->drawpage, tdev,
1515 page->pagedim.ctm);
1516 if (error) die (error);
1517 fz_free_device (tdev);
1521 CAMLprim value ml_whatsunder (value ptr_v, value x_v, value y_v)
1523 CAMLparam3 (ptr_v, x_v, y_v);
1524 CAMLlocal3 (ret_v, tup_v, str_v);
1525 pdf_link *link;
1526 struct page *page;
1527 char *s = String_val (ptr_v);
1529 ret_v = Val_int (0);
1530 if (trylock ("ml_whatsunder")) {
1531 goto done;
1534 page = parse_pointer ("ml_whatsunder", s);
1535 link = getlink (page, Int_val (x_v), Int_val (y_v));
1536 if (link) {
1537 switch (link->kind) {
1538 case PDF_LINK_GOTO:
1540 int pageno;
1541 fz_point p;
1542 fz_obj *obj;
1544 pageno = -1;
1545 p.x = 0;
1546 p.y = 0;
1548 if (fz_is_array (link->dest)) {
1549 obj = fz_array_get (link->dest, 0);
1550 if (fz_is_indirect (obj)) {
1551 pageno = pdf_find_page_number (state.xref, obj);
1553 else if (fz_is_int (obj)) {
1554 pageno = fz_to_int (obj);
1557 if (fz_array_len (link->dest) > 3) {
1558 fz_obj *xo, *yo;
1560 xo = fz_array_get (link->dest, 2);
1561 yo = fz_array_get (link->dest, 3);
1562 if (!fz_is_null (xo) && !fz_is_null (yo)) {
1563 p.x = fz_to_int (xo);
1564 p.y = fz_to_int (yo);
1565 p = fz_transform_point (page->pagedim.ctm, p);
1569 else {
1570 pageno = pdf_find_page_number (state.xref, link->dest);
1572 tup_v = caml_alloc_tuple (2);
1573 ret_v = caml_alloc_small (1, 1);
1574 Field (tup_v, 0) = Val_int (pageno);
1575 Field (tup_v, 1) = Val_int (p.y);
1576 Field (ret_v, 0) = tup_v;
1578 break;
1580 case PDF_LINK_URI:
1581 str_v = caml_copy_string (fz_to_str_buf (link->dest));
1582 ret_v = caml_alloc_small (1, 0);
1583 Field (ret_v, 0) = str_v;
1584 break;
1586 default:
1587 printd (state.sock, "T unhandled link kind %d", link->kind);
1588 break;
1591 else {
1592 int i, x, y;
1593 fz_text_span *span;
1595 ensuretext (page);
1596 x = Int_val (x_v) + page->pixmap->x;
1597 y = Int_val (y_v) + page->pixmap->y;
1599 for (span = page->text; span; span = span->next) {
1600 for (i = 0; i < span->len; ++i) {
1601 fz_bbox *b;
1602 b = &span->text[i].bbox;
1603 if ((x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1)) {
1604 const char *n2 =
1605 span->font && span->font->name
1606 ? span->font->name
1607 : "Span has no font name"
1609 FT_FaceRec *face = span->font->ft_face;
1610 if (face && face->family_name) {
1611 char *s;
1612 char *n1 = face->family_name;
1613 size_t l1 = strlen (n1);
1614 size_t l2 = strlen (n2);
1616 if (l1 != l2 || memcmp (n1, n2, l1)) {
1617 s = malloc (l1 + l2 + 2);
1618 if (s) {
1619 memcpy (s, n2, l2);
1620 s[l2] = '=';
1621 memcpy (s + l2 + 1, n1, l1 + 1);
1622 str_v = caml_copy_string (s);
1623 free (s);
1627 if (str_v == 0) {
1628 str_v = caml_copy_string (n2);
1630 ret_v = caml_alloc_small (1, 2);
1631 Field (ret_v, 0) = str_v;
1632 goto unlock;
1637 unlock:
1638 unlock ("ml_whatsunder");
1640 done:
1641 CAMLreturn (ret_v);
1644 CAMLprim value ml_seltext (value ptr_v, value rect_v, value oy_v)
1646 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1647 fz_bbox *b;
1648 struct page *page;
1649 fz_text_span *span;
1650 struct mark first, last;
1651 int i, x0, x1, y0, y1, oy, left;
1652 char *s = String_val (ptr_v);
1654 if (trylock ("ml_seltext")) {
1655 goto done;
1658 page = parse_pointer ("ml_seltext", s);
1659 ensuretext (page);
1661 oy = Int_val (oy_v);
1662 x0 = Int_val (Field (rect_v, 0));
1663 y0 = Int_val (Field (rect_v, 1));
1664 x1 = Int_val (Field (rect_v, 2));
1665 y1 = Int_val (Field (rect_v, 3));
1667 left = page->pagedim.left;
1668 if (0) {
1669 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1670 glColor3ub (128, 128, 128);
1671 glRecti (x0, y0, x1, y1);
1672 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1675 x0 += page->pixmap->x - left;
1676 y0 += page->pixmap->y - oy;
1677 x1 += page->pixmap->x - left;
1678 y1 += page->pixmap->y - oy;
1680 first.span = NULL;
1681 last.span = NULL;
1683 last.i = first.i = 0;
1684 first.span = page->text;
1685 for (span = page->text; span; span = span->next) {
1686 for (i = 0; i < span->len; ++i) {
1687 b = &span->text[i].bbox;
1688 int selected = 0;
1690 if (x0 >= b->x0 && x0 <= b->x1 && y0 >= b->y0 && y0 <= b->y1) {
1691 first.i = i;
1692 first.span = span;
1693 selected = 1;
1695 if (x1 >= b->x0 && x1 <= b->x1 && y1 >= b->y0 && y1 <= b->y1) {
1696 last.i = i;
1697 last.span = span;
1698 selected = 1;
1700 if (0 && selected) {
1701 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1702 glColor3ub (128, 128, 128);
1703 glRecti (b->x0+left, b->y0, b->x1+left, b->y1);
1704 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1709 if (y1 < y0 || x1 < x0) {
1710 int swap = 0;
1712 if (first.span == last.span) {
1713 swap = 1;
1715 else {
1716 if (y1 < y0) {
1717 for (span = first.span; span && span != last.span;
1718 span = span->next) {
1719 if (span->eol) {
1720 swap = 1;
1721 break;
1727 if (swap) {
1728 i = first.i;
1729 span = first.span;
1730 first.i = last.i;
1731 first.span = last.span;
1732 last.i = i;
1733 last.span = span;
1737 page->fmark = first;
1738 page->lmark = last;
1740 unlock ("ml_seltext");
1742 done:
1743 CAMLreturn (Val_unit);
1746 static int pipespan (FILE *f, fz_text_span *span, int a, int b)
1748 char buf[4];
1749 int i, len, ret;
1751 for (i = a; i <= b; ++i) {
1752 len = runetochar (buf, &span->text[i].c);
1753 ret = fwrite (buf, len, 1, f);
1755 if (ret != 1) {
1756 printd (state.sock, "T failed to write %d bytes ret=%d: %s",
1757 len, ret, strerror (errno));
1758 return -1;
1761 return 0;
1764 CAMLprim value ml_copysel (value ptr_v)
1766 CAMLparam1 (ptr_v);
1767 FILE *f;
1768 struct page *page;
1769 char *s = String_val (ptr_v);
1771 if (trylock ("ml_copysel")) {
1772 goto done;
1775 if (!*s) {
1776 close:
1777 #ifdef USE_XSEL
1778 if (state.xselpipe) {
1779 int ret = pclose (state.xselpipe);
1780 if (ret) {
1781 printd (state.sock, "T failed to close xsel pipe `%s'",
1782 strerror (errno));
1784 state.xselpipe = NULL;
1786 #else
1787 printf ("========================================\n");
1788 #endif
1790 else {
1791 fz_text_span *span;
1793 page = parse_pointer ("ml_sopysel", s);
1795 if (!page->fmark.span || !page->lmark.span) {
1796 printd (state.sock, "T nothing to copy");
1797 goto unlock;
1800 f = stdout;
1801 #ifdef USE_XSEL
1802 if (!state.xselpipe) {
1803 state.xselpipe = popen ("xsel -i", "w");
1804 if (!state.xselpipe) {
1805 printd (state.sock, "T failed to open xsel pipe `%s'",
1806 strerror (errno));
1808 else {
1809 f = state.xselpipe;
1812 else {
1813 f = state.xselpipe;
1815 #endif
1817 for (span = page->fmark.span;
1818 span && span != page->lmark.span->next;
1819 span = span->next) {
1820 int a = span == page->fmark.span ? page->fmark.i : 0;
1821 int b = span == page->lmark.span ? page->lmark.i : span->len - 1;
1822 if (pipespan (f, span, a, b)) {
1823 goto close;
1825 if (span->eol) {
1826 if (putc ('\n', f) == EOF) {
1827 printd (state.sock, "T failed break line on xsel pipe `%s'",
1828 strerror (errno));
1829 goto close;
1833 page->lmark.span = NULL;
1834 page->fmark.span = NULL;
1837 unlock:
1838 unlock ("ml_copysel");
1840 done:
1841 CAMLreturn (Val_unit);
1844 CAMLprim value ml_getpdimrect (value pagedimno_v)
1846 CAMLparam1 (pagedimno_v);
1847 CAMLlocal1 (ret_v);
1848 int pagedimno = Int_val (pagedimno_v);
1849 fz_rect box;
1851 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1852 if (trylock ("ml_getpdimrect")) {
1853 box = fz_empty_rect;
1855 else {
1856 box = state.pagedims[pagedimno].box;
1857 unlock ("ml_getpdimrect");
1860 Store_double_field (ret_v, 0, box.x0);
1861 Store_double_field (ret_v, 1, box.x1);
1862 Store_double_field (ret_v, 2, box.y0);
1863 Store_double_field (ret_v, 3, box.y1);
1865 CAMLreturn (ret_v);
1868 static double getmaxw (void)
1870 int i;
1871 struct pagedim *p;
1872 double maxw = 0.0;
1874 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
1875 double x0, x1, w;
1877 x0 = MIN (p->box.x0, p->box.x1);
1878 x1 = MAX (p->box.x0, p->box.x1);
1880 w = x1 - x0;
1881 maxw = MAX (w, maxw);
1883 return maxw;
1886 CAMLprim value ml_getmaxw (value unit_v)
1888 CAMLparam1 (unit_v);
1889 CAMLlocal1 (ret_v);
1890 double maxw = 0.0;
1892 if (trylock ("ml_getmaxw")) {
1893 goto done;
1895 maxw = getmaxw ();
1896 unlock ("ml_getmaxw");
1897 done:
1898 ret_v = caml_copy_double (maxw);
1899 CAMLreturn (ret_v);
1902 CAMLprim value ml_zoom_for_height (value winw_v, value winh_v, value dw_v)
1904 CAMLparam3 (winw_v, winh_v, dw_v);
1905 CAMLlocal1 (ret_v);
1906 int i;
1907 double zoom = 1.0;
1908 double maxw = 0.0, maxh = 0.0;
1909 struct pagedim *p;
1910 double winw = Int_val (winw_v);
1911 double winh = Int_val (winh_v);
1912 double dw = Int_val (dw_v);
1913 double pw = 1.0, ph = 1.0, num, den;
1915 if (trylock ("ml_zoom_for_height")) {
1916 goto done;
1919 if (state.proportional) {
1920 maxw = getmaxw ();
1923 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
1924 double x0, x1, y0, y1, w, h, scaledh, scale;
1926 x0 = MIN (p->box.x0, p->box.x1);
1927 y0 = MIN (p->box.y0, p->box.y1);
1928 x1 = MAX (p->box.x0, p->box.x1);
1929 y1 = MAX (p->box.y0, p->box.y1);
1931 w = x1 - x0;
1932 h = y1 - y0;
1934 if (state.proportional) {
1935 scale = w / maxw;
1936 scaledh = h * scale;
1938 else {
1939 scale = 1.0;
1940 scaledh = h;
1943 if (scaledh > maxh) {
1944 maxh = scaledh;
1945 ph = scaledh;
1946 pw = w * scale;
1950 num = (winh * pw) + (ph * dw);
1951 den = ph * winw;
1952 zoom = num / den;
1954 unlock ("ml_zoom_for_height");
1955 done:
1956 ret_v = caml_copy_double (zoom);
1957 CAMLreturn (ret_v);
1960 #include "glfont.c"
1962 CAMLprim value ml_draw_string (value pt_v, value x_v, value y_v, value string_v)
1964 CAMLparam4 (pt_v, x_v, y_v, string_v);
1965 int pt = Int_val(pt_v);
1966 int x = Int_val (x_v);
1967 int y = Int_val (y_v);
1968 double w;
1970 w = draw_string (state.face, pt, x, y, String_val (string_v));
1971 CAMLreturn (caml_copy_double (w));
1974 CAMLprim value ml_measure_string (value pt_v, value string_v)
1976 CAMLparam2 (pt_v, string_v);
1977 CAMLlocal1 (ret_v);
1978 int pt = Int_val (pt_v);
1979 double w;
1981 w = measure_string (state.face, pt, String_val (string_v));
1982 CAMLreturn (caml_copy_double (w));
1985 CAMLprim value ml_init (value sock_v, value params_v)
1987 CAMLparam2 (sock_v, params_v);
1988 #ifndef _WIN32
1989 int ret;
1990 #endif
1991 char *fontpath;
1993 state.rotate = Int_val (Field (params_v, 0));
1994 state.proportional = Bool_val (Field (params_v, 1));
1995 state.texcount = Int_val (Field (params_v, 2));
1996 state.sliceheight = Int_val (Field (params_v, 3));
1997 state.blockwidth = Int_val (Field (params_v, 4));
1998 fontpath = String_val (Field (params_v, 5));
1999 state.texform = GL_RGBA;
2000 state.texty = GL_UNSIGNED_BYTE;
2002 if (*fontpath) {
2003 state.face = load_font (fontpath);
2005 else {
2006 unsigned int len;
2007 void *base = pdf_find_substitute_font (0, 0, 0, 0, &len);
2009 state.face = load_builtin_font (base, len);
2011 if (!state.face) _exit (1);
2013 fz_accelerate ();
2014 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
2015 if (!state.texids) {
2016 err (1, "calloc texids " FMT_s,
2017 state.texcount * sizeof (*state.texids));
2020 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
2021 if (!state.texowners) {
2022 err (1, "calloc texowners " FMT_s,
2023 state.texcount * sizeof (*state.texowners));
2026 glGenTextures (state.texcount, state.texids);
2028 #ifdef _WIN32
2029 state.sock = Socket_val (sock_v);
2030 #else
2031 state.sock = Int_val (sock_v);
2032 #endif
2034 #ifdef _WIN32
2035 InitializeCriticalSection (&critsec);
2036 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
2037 if (state.thread == INVALID_HANDLE_VALUE) {
2038 errx (1, "CreateThread failed: %lx", GetLastError ());
2040 #else
2041 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
2042 if (ret) {
2043 errx (1, "pthread_create: %s", strerror (ret));
2045 #endif
2047 CAMLreturn (Val_unit);