Add -c command line option to specify alternate config file location
[llpp.git] / link.c
blob2b1f7d5d691e9aee714b1d954040f2f5c30221bb
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 #include FT_FREETYPE_H
114 #if 0
115 #define lprintf printf
116 #else
117 #define lprintf(...)
118 #endif
120 #define ARSERT(cond) for (;;) { \
121 if (!(cond)) { \
122 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
124 break; \
127 struct block {
128 int w;
129 int x;
130 int offset;
131 int texindex;
134 struct slice {
135 int h;
136 struct block *blocks;
139 struct pagedim {
140 int pageno;
141 int rotate;
142 int left;
143 fz_rect box;
144 fz_bbox bbox;
145 fz_matrix ctm, ctm1;
148 struct page {
149 int pageno;
150 int slicecount;
151 int blockcount;
152 fz_text_span *text;
153 fz_pixmap *pixmap;
154 pdf_page *drawpage;
155 struct pagedim pagedim;
156 struct mark {
157 int i;
158 fz_text_span *span;
159 } fmark, lmark;
160 struct slice *slices;
163 #if !defined _WIN32 && !defined __APPLE__
164 #define USE_XSEL
165 #endif
167 struct {
168 int sock;
169 int sliceheight, blockwidth;
170 struct page *pig;
171 struct pagedim *pagedims;
172 int pagecount;
173 int pagedimcount;
174 pdf_xref *xref;
175 fz_glyph_cache *cache;
176 int w, h;
178 int texindex;
179 int texcount;
180 GLuint *texids;
182 GLenum texform;
183 GLenum texty;
185 struct {
186 int w, h;
187 struct block *block;
188 } *texowners;
190 int rotate;
191 int proportional;
192 int needoutline;
194 #ifdef _WIN32
195 HANDLE thread;
196 #else
197 pthread_t thread;
198 #endif
199 FILE *xselpipe;
201 FT_Face face;
202 } state;
204 #ifdef _WIN32
205 static CRITICAL_SECTION critsec;
207 static void lock (void *unused)
209 (void) unused;
210 EnterCriticalSection (&critsec);
213 static void unlock (void *unused)
215 (void) unused;
216 LeaveCriticalSection (&critsec);
219 static int trylock (void *unused)
221 return TryEnterCriticalSection (&critsec) == 0;
223 #else
224 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
226 static void lock (const char *cap)
228 int ret = pthread_mutex_lock (&mutex);
229 if (ret) {
230 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
234 static void unlock (const char *cap)
236 int ret = pthread_mutex_unlock (&mutex);
237 if (ret) {
238 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
242 static int trylock (const char *cap)
244 int ret = pthread_mutex_trylock (&mutex);
246 if (ret && ret != EBUSY) {
247 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
249 return ret == EBUSY;
251 #endif
253 static void *parse_pointer (const char *cap, const char *s)
255 int ret;
256 void *ptr;
258 ret = sscanf (s, "%p", &ptr);
259 if (ret != 1) {
260 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
262 return ptr;
265 static int hasdata (int sock)
267 int ret;
268 fionread_arg avail;
269 ret = ioctlsocket (sock, FIONREAD, &avail);
270 if (ret) sockerr (1, "hasdata: FIONREAD error ret=%d", ret);
271 return avail > 0;
274 static double now (void)
276 struct timeval tv;
278 if (gettimeofday (&tv, NULL)) {
279 err (1, "gettimeofday");
281 return tv.tv_sec + tv.tv_usec*1e-6;
284 static void readdata (int fd, char *p, int size)
286 ssize_t n;
288 n = recv (fd, p, size, 0);
289 if (n - size) {
290 if (!n) errx (1, "EOF while reading");
291 sockerr (1, "recv (req %d, ret " FMT_ss ")", size, n);
295 static void writedata (int fd, char *p, int size)
297 char buf[4];
298 ssize_t n;
300 buf[0] = (size >> 24) & 0xff;
301 buf[1] = (size >> 16) & 0xff;
302 buf[2] = (size >> 8) & 0xff;
303 buf[3] = (size >> 0) & 0xff;
305 n = send (fd, buf, 4, 0);
306 if (n != 4) {
307 if (!n) errx (1, "EOF while writing length");
308 sockerr (1, "send " FMT_ss, n);
311 n = send (fd, p, size, 0);
312 if (n - size) {
313 if (!n) errx (1, "EOF while writing data");
314 sockerr (1, "send (req %d, ret " FMT_ss ")", size, n);
318 static void
319 #ifdef __GNUC__
320 __attribute__ ((format (printf, 2, 3)))
321 #endif
322 printd (int fd, const char *fmt, ...)
324 int size = 200, len;
325 va_list ap;
326 char *buf;
328 buf = malloc (size);
329 for (;;) {
330 if (!buf) err (errno, "malloc for temp buf (%d bytes) failed", size);
332 va_start (ap, fmt);
333 len = vsnprintf (buf, size, fmt, ap);
334 va_end (ap);
336 if (len > -1 && len < size) {
337 writedata (fd, buf, len);
338 break;
341 if (len > -1) {
342 size = len + 1;
344 else {
345 size *= 2;
347 buf = realloc (buf, size);
349 free (buf);
352 static void die (fz_error error)
354 fz_catch (error, "aborting");
355 if (state.xref)
356 pdf_free_xref (state.xref);
357 _exit (1);
360 static void openxref (char *filename, char *password)
362 int i;
363 fz_error error;
365 for (i = 0; i < state.texcount; ++i) {
366 state.texowners[i].block = NULL;
369 if (state.cache) {
370 fz_free_glyph_cache (state.cache);
373 state.cache = fz_new_glyph_cache ();
374 if (!state.cache) {
375 errx (1, "fz_newglyph_cache failed");
378 if (state.xref) {
379 if (state.xref->store) {
380 pdf_free_store (state.xref->store);
381 state.xref->store = NULL;
383 pdf_free_xref (state.xref);
384 state.xref = NULL;
387 if (state.pagedims) {
388 free (state.pagedims);
389 state.pagedims = NULL;
391 state.pagedimcount = 0;
393 error = pdf_open_xref (&state.xref, filename, password);
394 if (error) {
395 die (error);
398 error = pdf_load_page_tree (state.xref);
399 if (error) {
400 die (error);
403 state.pagecount = pdf_count_pages (state.xref);
406 static void pdfinfo (void)
408 fz_obj *infoobj;
410 printd (state.sock, "i PDF version\t%d.%d",
411 state.xref->version / 10, state.xref->version % 10);
413 infoobj = fz_dict_gets (state.xref->trailer, "Info");
414 if (infoobj) {
415 int i;
416 char *s;
417 char *items[] = { "Title", "Creator", "Producer", "CreationDate" };
419 for (i = 0; i < sizeof (items) / sizeof (*items); ++i) {
420 fz_obj *obj = fz_dict_gets (infoobj, items[i]);
421 s = pdf_to_utf8 (obj);
422 if (*s) {
423 if (i == 0) {
424 printd (state.sock, "t %s", s);
426 printd (state.sock, "i %s\t%s", items[i], s);
428 fz_free (s);
430 printd (state.sock, "ie");
434 static int readlen (int fd)
436 ssize_t n;
437 unsigned char p[4];
439 n = recv (fd, p, 4, 0);
440 if (n != 4) {
441 if (!n) errx (1, "EOF while reading length");
442 sockerr (1, "recv " FMT_ss, n);
445 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
448 static void unlinkpage (struct page *page)
450 int i;
452 for (i = 0; i < page->slicecount; ++i) {
453 int j;
454 struct slice *s = &page->slices[i];
456 for (j = 0; j < page->blockcount; ++j) {
457 struct block *b = &s->blocks[j];
459 if (b->texindex != -1) {
460 if (state.texowners[b->texindex].block == b) {
461 state.texowners[b->texindex].block = NULL;
468 static void freepage (struct page *page)
470 int i;
472 fz_drop_pixmap (page->pixmap);
474 unlinkpage (page);
476 if (page->text) {
477 fz_free_text_span (page->text);
479 if (page->drawpage) {
480 pdf_free_page (page->drawpage);
482 for (i = 0; i < page->slicecount; ++i) {
483 free (page->slices[i].blocks);
485 free (page->slices);
486 free (page);
489 static void subdivide (struct page *p, int blockcount)
491 int i;
492 int w = p->pixmap->w;
493 int h = p->pixmap->h;
494 int offset = 0;
495 int tw = MIN (w, state.blockwidth);
496 int th = MIN (h, state.sliceheight);
498 for (i = 0; i < p->slicecount; ++i) {
499 int j, x, offset1;
500 struct slice *s = &p->slices[i];
502 s->h = MIN (th, h);
504 s->blocks = calloc (sizeof (struct block), p->blockcount);
505 if (!s->blocks) {
506 err (1, "calloc blocks page %d slice %d, %d",
507 p->pageno, i, p->blockcount);
510 w = p->pixmap->w;
511 x = 0;
512 offset1 = offset;
514 for (j = 0; j < blockcount; ++j) {
515 struct block *b = &s->blocks[j];
517 b->texindex = -1;
518 b->w = MIN (tw, w);
519 b->x = x;
520 b->offset = offset1;
521 w -= tw;
522 x += tw;
523 offset1 += tw * 4;
525 offset += s->h * p->pixmap->w * 4;
526 h -= th;
530 static int compatpdims (struct pagedim *p1, struct pagedim *p2)
532 return p1->rotate == p2->rotate
533 && !memcmp (&p1->bbox, &p2->bbox, sizeof (p1->bbox))
534 && !memcmp (&p1->ctm, &p2->ctm, sizeof (p1->ctm));
537 #ifdef __ALTIVEC__
538 #include <altivec.h>
540 static int cacheline32bytes;
541 extern char **environ;
543 static void __attribute__ ((constructor)) clcheck (void)
545 char **envp = environ;
546 unsigned long *auxv;
548 while (*envp++);
550 for (auxv = (unsigned long *) envp; *auxv != 0; auxv += 2) {
551 if (*auxv == 19) {
552 cacheline32bytes = auxv[1] == 32;
553 return;
558 #if __GNUC__ > 4 && __GNUC_MINOR__ > 3
559 #define OPTIMIZE __attribute__ ((optimize ("O3")))
560 #else
561 #define OPTIMIZE
562 #endif
564 static void clearpixmap (fz_pixmap *pixmap)
566 if (cacheline32bytes) {
567 intptr_t a1, a2, diff;
568 size_t sizea, i, size = pixmap->w * pixmap->h * pixmap->n;
569 vector unsigned char v = vec_splat_u8 (-1);
570 vector unsigned char *p;
572 a1 = a2 = (intptr_t) pixmap->samples;
573 a2 = (a1 + 31) & ~31;
574 diff = a2 - a1;
575 sizea = size - diff;
576 p = (void *) a2;
578 while (a1 != a2) *(char *) a1++ = 0xff;
579 for (i = 0; i < (sizea & ~31); i += 32) {
580 __asm volatile ("dcbz %0, %1"::"b"(a2),"r"(i));
581 vec_st (v, i, p);
582 vec_st (v, i + 16, p);
584 while (i < sizea) *((char *) a1 + i++) = 0xff;
586 else fz_clear_pixmap_with_color (pixmap, 0xff);
588 #else
589 #define clearpixmap(p) fz_clear_pixmap_with_color (p, 0xff)
590 #endif
592 static void *render (int pageno, int pindex)
594 fz_error error;
595 fz_device *idev;
596 double start, end;
597 pdf_page *drawpage;
598 struct pagedim *pagedim;
599 struct page *page = NULL;
600 int slicecount, blockcount;
602 start = now ();
603 printd (state.sock, "V rendering %d", pageno);
605 pagedim = &state.pagedims[pindex];
606 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
607 + state.sliceheight - 1) / state.sliceheight;
608 slicecount += slicecount == 0;
610 blockcount = (pagedim->bbox.x1 - pagedim->bbox.x0
611 + state.blockwidth - 1) / state.blockwidth;
612 blockcount += blockcount == 0;
614 if (state.pig) {
615 if (compatpdims (&state.pig->pagedim, pagedim)) {
616 page = state.pig;
617 if (page->text) {
618 fz_free_text_span (page->text);
619 page->text = NULL;
621 if (page->drawpage) {
622 pdf_free_page (page->drawpage);
623 page->drawpage = NULL;
626 else {
627 freepage (state.pig);
630 if (!page) {
631 page = calloc (sizeof (struct page), 1);
632 if (!page) {
633 err (1, "calloc page %d", pageno);
635 page->slices = calloc (sizeof (struct slice), slicecount);
636 if (!page->slices) {
637 err (1, "calloc slices %d %d", pageno, slicecount);
639 page->pixmap = fz_new_pixmap_with_rect (fz_device_rgb, pagedim->bbox);
642 page->slicecount = slicecount;
643 page->blockcount = blockcount;
645 error = pdf_load_page (&drawpage, state.xref, pageno - 1);
646 if (error)
647 die (error);
649 clearpixmap (page->pixmap);
651 idev = fz_new_draw_device (state.cache, page->pixmap);
652 if (!idev)
653 die (fz_throw ("fz_newdrawdevice failed"));
654 error = pdf_run_page (state.xref, drawpage, idev, pagedim->ctm);
655 if (error)
656 die (fz_rethrow (error, "pdf_runpage failed"));
657 fz_free_device (idev);
659 page->drawpage = drawpage;
660 page->pagedim = *pagedim;
661 page->pageno = pageno;
662 subdivide (page, blockcount);
663 end = now ();
665 pdf_age_store (state.xref->store, 3);
667 printd (state.sock, "V rendering %d took %f sec", pageno, end - start);
668 state.pig = NULL;
669 return page;
672 static void initpdims (void)
674 int pageno;
675 double start, end;
677 start = now ();
678 for (pageno = 0; pageno < state.pagecount; ++pageno) {
679 int rotate;
680 fz_rect box;
681 struct pagedim *p;
682 fz_obj *obj, *pageobj;
684 pageobj = state.xref->page_objs[pageno];
686 obj = fz_dict_gets (pageobj, "CropBox");
687 if (!fz_is_array (obj)) {
688 obj = fz_dict_gets (pageobj, "MediaBox");
689 if (!fz_is_array (obj)) {
690 die (fz_throw ("cannot find page bounds %d (%d Rd)",
691 fz_to_num (pageobj), fz_to_gen (pageobj)));
694 box = pdf_to_rect (obj);
696 obj = fz_dict_gets (pageobj, "Rotate");
697 if (fz_is_int (obj)) {
698 rotate = fz_to_int (obj);
700 else {
701 rotate = 0;
703 rotate += state.rotate;
705 p = &state.pagedims[state.pagedimcount - 1];
706 if ((state.pagedimcount == 0)
707 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
708 size_t size;
710 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
711 state.pagedims = realloc (state.pagedims, size);
712 if (!state.pagedims) {
713 err (1, "realloc pagedims to " FMT_s " (%d elems)",
714 size, state.pagedimcount + 1);
716 p = &state.pagedims[state.pagedimcount++];
717 p->rotate = rotate;
718 p->box = box;
719 p->pageno = pageno;
722 end = now ();
723 printd (state.sock, "T Processed %d pages in %f seconds",
724 state.pagecount, end - start);
727 static void layout (void)
729 int pindex;
730 fz_matrix ctm;
731 fz_rect box, box2;
732 double zoom, w, maxw = 0;
733 struct pagedim *p = state.pagedims;
735 if (state.proportional) {
736 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
737 box.x0 = MIN (p->box.x0, p->box.x1);
738 box.y0 = MIN (p->box.y0, p->box.y1);
739 box.x1 = MAX (p->box.x0, p->box.x1);
740 box.y1 = MAX (p->box.y0, p->box.y1);
742 ctm = fz_identity;
743 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
744 ctm = fz_concat (ctm, fz_rotate (p->rotate));
745 box2 = fz_transform_rect (ctm, box);
746 w = box2.x1 - box2.x0;
747 maxw = MAX (w, maxw);
751 p = state.pagedims;
752 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
753 box.x0 = MIN (p->box.x0, p->box.x1);
754 box.y0 = MIN (p->box.y0, p->box.y1);
755 box.x1 = MAX (p->box.x0, p->box.x1);
756 box.y1 = MAX (p->box.y0, p->box.y1);
758 ctm = fz_identity;
759 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
760 ctm = fz_concat (ctm, fz_rotate (p->rotate));
761 box2 = fz_transform_rect (ctm, box);
762 w = box2.x1 - box2.x0;
764 if (state.proportional) {
765 double scale = w / maxw;
766 zoom = (state.w / w) * scale;
768 else {
769 zoom = state.w / w;
771 ctm = fz_identity;
772 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
773 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
774 memcpy (&p->ctm1, &ctm, sizeof (ctm));
775 ctm = fz_concat (ctm, fz_rotate (p->rotate));
776 p->bbox = fz_round_rect (fz_transform_rect (ctm, box));
777 p->left = state.proportional ? ((maxw - w) * zoom) / 2.0 : 0;
778 memcpy (&p->ctm, &ctm, sizeof (ctm));
781 while (p-- != state.pagedims) {
782 printd (state.sock, "l %d %d %d %d",
783 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0,
784 p->left);
788 static void recurse_outline (pdf_outline *outline, int level)
790 while (outline) {
791 int i;
792 fz_obj *obj;
793 int pageno = -1;
794 int top = 0, h = 0;
796 if (!outline->link) goto next;
798 obj = outline->link->dest;
799 if (fz_is_indirect (obj)) {
800 obj = fz_resolve_indirect (obj);
802 if (fz_is_array (obj)) {
803 fz_obj *obj2;
805 obj2 = fz_array_get (obj, 0);
806 if (fz_is_int (obj2)) {
807 pageno = fz_to_int (obj2);
809 else {
810 pageno = pdf_find_page_number (state.xref, obj2);
813 if (fz_array_len (obj) > 3) {
814 fz_point p;
815 fz_obj *xo, *yo;
817 xo = fz_array_get (obj, 2);
818 yo = fz_array_get (obj, 3);
819 if (!fz_is_null (xo) && !fz_is_null (yo)) {
820 struct pagedim *pagedim = state.pagedims;
822 for (i = 0; i < state.pagedimcount; ++i) {
823 if (state.pagedims[i].pageno > pageno)
824 break;
825 pagedim = &state.pagedims[i];
827 p.x = fz_to_int (xo);
828 p.y = fz_to_int (yo);
829 p = fz_transform_point (pagedim->ctm, p);
830 h = pagedim->bbox.y1 - pagedim->bbox.y0;
831 top = p.y;
835 else {
836 pageno = pdf_find_page_number (state.xref, outline->link->dest);
839 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
840 if (pageno > 0) {
841 printd (state.sock, "o %d %d %d %d %s",
842 level, pageno, top, h, outline->title);
844 next:
845 if (outline->child) {
846 recurse_outline (outline->child, level + 1);
848 outline = outline->next;
852 static void process_outline (void)
854 pdf_outline *outline;
856 if (!state.needoutline) return;
858 state.needoutline = 0;
859 outline = pdf_load_outline (state.xref);
860 if (outline) {
861 recurse_outline (outline, 0);
862 pdf_free_outline (outline);
866 static int comparespans (const void *l, const void *r)
868 fz_text_span const *const*ls = l;
869 fz_text_span const *const*rs = r;
870 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
873 /* wishful thinking function */
874 static void search (regex_t *re, int pageno, int y, int forward)
876 int i, j;
877 int ret;
878 char *p;
879 char buf[256];
880 fz_matrix ctm;
881 fz_error error;
882 fz_device *tdev;
883 pdf_page *drawpage;
884 fz_text_span *text, *span, **pspan;
885 struct pagedim *pdim, *pdimprev;
886 int stop = 0;
887 int niters = 0;
888 int nspans;
889 double start, end;
891 start = now ();
892 while (pageno >= 0 && pageno < state.pagecount && !stop) {
893 if (niters++ == 5) {
894 pdf_age_store (state.xref->store, 3);
895 niters = 0;
896 if (hasdata (state.sock)) {
897 printd (state.sock, "T attention requested aborting search at %d",
898 pageno);
899 stop = 1;
901 else {
902 printd (state.sock, "T searching in page %d", pageno);
905 pdimprev = NULL;
906 for (i = 0; i < state.pagedimcount; ++i) {
907 pdim = &state.pagedims[i];
908 if (pdim->pageno == pageno) {
909 goto found;
911 if (pdim->pageno > pageno) {
912 pdim = pdimprev;
913 goto found;
915 pdimprev = pdim;
917 pdim = pdimprev;
918 found:
920 error = pdf_load_page (&drawpage, state.xref, pageno);
921 if (error)
922 die (error);
924 ctm = fz_rotate (pdim->rotate);
926 text = fz_new_text_span ();
927 tdev = fz_new_text_device (text);
928 error = pdf_run_page (state.xref, drawpage, tdev, pdim->ctm1);
929 if (error) die (error);
930 fz_free_device (tdev);
932 nspans = 0;
933 for (span = text; span; span = span->next) {
934 nspans++;
936 pspan = malloc (sizeof (void *) * nspans);
937 if (!pspan) {
938 err (1, "malloc span pointers " FMT_s, sizeof (void *) * nspans);
940 for (i = 0, span = text; span; span = span->next, ++i) {
941 pspan[i] = span;
943 qsort (pspan, nspans, sizeof (fz_text_span *), comparespans);
945 j = forward ? 0 : nspans - 1;
946 while (nspans--) {
947 regmatch_t rm;
949 span = pspan[j];
950 j += forward ? 1 : -1;
951 p = buf;
952 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
953 if (forward) {
954 if (span->text[i].bbox.y0 < y + 1) {
955 continue;
958 else {
959 if (span->text[i].bbox.y0 > y - 1) {
960 continue;
963 if (span->text[i].c < 256) {
964 *p++ = span->text[i].c;
966 else {
967 *p++ = '?';
970 if (p == buf) {
971 continue;
973 *p++ = 0;
975 ret = regexec (re, buf, 1, &rm, 0);
976 if (ret) {
977 if (ret != REG_NOMATCH) {
978 size_t size;
979 char errbuf[80];
980 size = regerror (ret, re, errbuf, sizeof (errbuf));
981 printd (state.sock,
982 "T regexec error `%.*s'",
983 (int) size, errbuf);
984 fz_free_text_span (text);
985 pdf_free_page (drawpage);
986 free (pspan);
987 return;
990 else {
991 int xoff, yoff;
992 fz_bbox *sb, *eb;
993 fz_point p1, p2, p3, p4;
995 xoff = pdim->left - pdim->bbox.x0;
996 yoff = -pdim->bbox.y0;
998 sb = &span->text[rm.rm_so].bbox;
999 eb = &span->text[rm.rm_eo - 1].bbox;
1001 p1.x = sb->x0;
1002 p1.y = sb->y0;
1003 p2.x = eb->x1;
1004 p2.y = sb->y0;
1005 p3.x = eb->x1;
1006 p3.y = eb->y1;
1007 p4.x = sb->x0;
1008 p4.y = eb->y1;
1010 p1 = fz_transform_point (ctm, p1);
1011 p2 = fz_transform_point (ctm, p2);
1012 p3 = fz_transform_point (ctm, p3);
1013 p4 = fz_transform_point (ctm, p4);
1015 if (!stop) {
1016 printd (state.sock, "F %d %d %f %f %f %f %f %f %f %f",
1017 pageno, 1,
1018 p1.x + xoff, p1.y + yoff,
1019 p2.x + xoff, p2.y + yoff,
1020 p3.x + xoff, p3.y + yoff,
1021 p4.x + xoff, p4.y + yoff);
1023 printd (state.sock, "T found at %d `%.*s' in %f sec",
1024 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
1025 now () - start);
1027 else {
1028 printd (state.sock, "R %d %d %f %f %f %f %f %f %f %f",
1029 pageno, 2,
1030 p1.x + xoff, p1.y + yoff,
1031 p2.x + xoff, p2.y + yoff,
1032 p3.x + xoff, p3.y + yoff,
1033 p4.x + xoff, p4.y + yoff);
1035 stop = 1;
1038 if (forward) {
1039 pageno += 1;
1040 y = 0;
1042 else {
1043 pageno -= 1;
1044 y = INT_MAX;
1046 fz_free_text_span (text);
1047 pdf_free_page (drawpage);
1048 free (pspan);
1050 end = now ();
1051 if (!stop) {
1052 printd (state.sock, "T no matches %f sec", end - start);
1054 printd (state.sock, "D");
1057 static
1058 #ifdef _WIN32
1059 DWORD _stdcall
1060 #else
1061 void *
1062 #endif
1063 mainloop (void *unused)
1065 char *p = NULL;
1066 int len, ret, oldlen = 0;
1068 for (;;) {
1069 len = readlen (state.sock);
1070 if (len == 0) {
1071 errx (1, "readlen returned 0");
1074 if (oldlen < len + 1) {
1075 p = realloc (p, len + 1);
1076 if (!p) {
1077 err (1, "realloc %d failed", len + 1);
1079 oldlen = len + 1;
1081 readdata (state.sock, p, len);
1082 p[len] = 0;
1084 if (!strncmp ("open", p, 4)) {
1085 size_t filenamelen;
1086 char *password;
1087 char *filename = p + 5;
1089 filenamelen = strlen (filename);
1090 password = filename + filenamelen + 1;
1092 openxref (filename, password);
1093 initpdims ();
1094 pdfinfo ();
1095 state.needoutline = 1;
1097 else if (!strncmp ("free", p, 4)) {
1098 void *ptr;
1100 ret = sscanf (p + 4, " %p", &ptr);
1101 if (ret != 1) {
1102 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
1104 unlinkpage (ptr);
1105 state.pig = ptr;
1107 else if (!strncmp ("search", p, 6)) {
1108 int icase, pageno, y, ret, len2, forward;
1109 char *pattern;
1110 regex_t re;
1112 ret = sscanf (p + 6, " %d %d %d %d,%n",
1113 &icase, &pageno, &y, &forward, &len2);
1114 if (ret != 4) {
1115 errx (1, "malformed search `%s' ret=%d", p, ret);
1118 pattern = p + 6 + len2;
1119 ret = regcomp (&re, pattern,
1120 REG_EXTENDED | (icase ? REG_ICASE : 0));
1121 if (ret) {
1122 char errbuf[80];
1123 size_t size;
1125 size = regerror (ret, &re, errbuf, sizeof (errbuf));
1126 printd (state.sock, "T regcomp failed `%.*s'",
1127 (int) size, errbuf);
1129 else {
1130 search (&re, pageno, y, forward);
1131 regfree (&re);
1134 else if (!strncmp ("geometry", p, 8)) {
1135 int w, h;
1137 printd (state.sock, "c");
1138 ret = sscanf (p + 8, " %d %d", &w, &h);
1139 if (ret != 2) {
1140 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
1143 lock ("geometry");
1144 state.h = h;
1145 if (w != state.w) {
1146 int i;
1147 state.w = w;
1148 for (i = 0; i < state.texcount; ++i) {
1149 state.texowners[i].block = NULL;
1152 layout ();
1153 process_outline ();
1154 unlock ("geometry");
1155 printd (state.sock, "C %d", state.pagecount);
1157 else if (!strncmp ("reinit", p, 6)) {
1158 float rotate;
1159 int proportional;
1161 printd (state.sock, "c");
1162 ret = sscanf (p + 6, " %f %d", &rotate, &proportional);
1163 if (ret != 2) {
1164 errx (1, "bad rotate line `%.*s' ret=%d", len, p, ret);
1166 lock ("reinit");
1167 state.rotate = rotate;
1168 state.proportional = proportional;
1169 state.pagedimcount = 0;
1170 free (state.pagedims);
1171 state.pagedims = NULL;
1172 initpdims ();
1173 layout ();
1174 process_outline ();
1175 if (state.pig) {
1176 freepage (state.pig);
1177 state.pig = NULL;
1179 unlock ("reinit");
1180 printd (state.sock, "C %d", state.pagecount);
1182 else if (!strncmp ("render", p, 6)) {
1183 int pageno, pindex, w, h, ret;
1184 struct page *page;
1186 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
1187 if (ret != 4) {
1188 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
1191 lock ("render");
1192 page = render (pageno, pindex);
1193 unlock ("render");
1195 printd (state.sock, "r %d %d %d %d %d %d %p",
1196 pageno,
1197 state.w,
1198 state.h,
1199 state.rotate,
1200 state.proportional,
1201 w * h * 4,
1202 page);
1204 else if (!strncmp ("interrupt", p, 9)) {
1205 printd (state.sock, "V interrupted");
1207 else {
1208 errx (1, "unknown command %.*s", len, p);
1211 return 0;
1214 static void showsel (struct page *page, int oy)
1216 int ox;
1217 fz_bbox bbox;
1218 fz_text_span *span;
1219 struct mark first, last;
1221 first = page->fmark;
1222 last = page->lmark;
1224 if (!first.span || !last.span) return;
1226 glEnable (GL_BLEND);
1227 glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA);
1228 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1230 ox = -page->pixmap->x + page->pagedim.left;
1231 oy = -page->pixmap->y + oy;
1232 for (span = first.span; span; span = span->next) {
1233 int i, j, k;
1235 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0;
1237 j = 0;
1238 k = span->len - 1;
1240 if (span == page->fmark.span && span == page->lmark.span) {
1241 j = MIN (first.i, last.i);
1242 k = MAX (first.i, last.i);
1244 else if (span == first.span) {
1245 j = first.i;
1247 else if (span == last.span) {
1248 k = last.i;
1251 for (i = j; i <= k; ++i) {
1252 bbox = fz_union_bbox (bbox, span->text[i].bbox);
1254 lprintf ("%d %d %d %d oy=%d ox=%d\n",
1255 bbox.x0,
1256 bbox.y0,
1257 bbox.x1,
1258 bbox.y1,
1259 oy, ox);
1261 glRecti (bbox.x0 + ox, bbox.y0 + oy, bbox.x1 + ox, bbox.y1 + oy);
1263 if (span == last.span) break;
1265 glDisable (GL_BLEND);
1268 static void highlightlinks (struct page *page, int yoff)
1270 pdf_link *link;
1271 int xoff;
1273 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1274 glEnable (GL_LINE_STIPPLE);
1275 glLineStipple (0.5, 0xcccc);
1277 xoff = page->pagedim.left - page->pixmap->x;
1278 yoff -= page->pixmap->y;
1280 glBegin (GL_QUADS);
1281 for (link = page->drawpage->links; link; link = link->next) {
1282 fz_point p1, p2, p3, p4;
1283 fz_matrix ctm = page->pagedim.ctm;
1285 p1.x = link->rect.x0;
1286 p1.y = link->rect.y0;
1288 p2.x = link->rect.x1;
1289 p2.y = link->rect.y0;
1291 p3.x = link->rect.x1;
1292 p3.y = link->rect.y1;
1294 p4.x = link->rect.x0;
1295 p4.y = link->rect.y1;
1297 p1 = fz_transform_point (ctm, p1);
1298 p2 = fz_transform_point (ctm, p2);
1299 p3 = fz_transform_point (ctm, p3);
1300 p4 = fz_transform_point (ctm, p4);
1302 switch (link->kind) {
1303 case PDF_LINK_GOTO: glColor3ub (255, 0, 0); break;
1304 case PDF_LINK_URI: glColor3ub (0, 0, 255); break;
1305 default: glColor3ub (0, 0, 0); break;
1308 glVertex2f (p1.x + xoff, p1.y + yoff);
1309 glVertex2f (p2.x + xoff, p2.y + yoff);
1310 glVertex2f (p3.x + xoff, p3.y + yoff);
1311 glVertex2f (p4.x + xoff, p4.y + yoff);
1313 glEnd ();
1315 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1316 glDisable (GL_LINE_STIPPLE);
1319 static void upload (struct page *page, int slicenum, int blocknum)
1321 double start, end;
1322 struct slice *slice = &page->slices[slicenum];
1323 struct block *block = &slice->blocks[blocknum];
1325 if (block->texindex != -1
1326 && state.texowners[block->texindex].block == block) {
1327 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[block->texindex]);
1329 else {
1330 int subimage = 0;
1331 int index = (state.texindex++ % state.texcount);
1333 if (state.texowners[index].w == block->w) {
1334 if (state.texowners[index].h >= slice->h ) {
1335 subimage = 1;
1337 else {
1338 state.texowners[index].h = slice->h;
1341 else {
1342 state.texowners[index].h = slice->h;
1345 state.texowners[index].w = block->w;
1346 state.texowners[index].block = block;
1347 block->texindex = index;
1349 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[block->texindex]);
1350 glPixelStorei (GL_UNPACK_ROW_LENGTH, page->pixmap->w);
1352 start = now ();
1353 if (0) {
1354 GLenum err = glGetError ();
1355 if (err != GL_NO_ERROR) {
1356 printf ("\033[0;31mERROR1 %d %d %#x\033[0m\n",
1357 block->w, slice->h, err);
1358 abort ();
1361 if (subimage) {
1362 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1366 block->w,
1367 slice->h,
1368 state.texform,
1369 state.texty,
1370 page->pixmap->samples + block->offset
1373 else {
1374 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1376 GL_RGBA8,
1377 block->w,
1378 slice->h,
1380 state.texform,
1381 state.texty,
1382 page->pixmap->samples + block->offset
1385 if (0) {
1386 GLenum err = glGetError ();
1387 if (err != GL_NO_ERROR) {
1388 printf ("\033[0;31mERROR %d %d %#x\033[0m\n",
1389 block->w, slice->h, err);
1390 abort ();
1394 end = now ();
1395 (void) start;
1396 (void) end;
1397 lprintf ("%s[%d] slice=%d,%d(%d,%d) tex=%d,%d offset=%d %f sec\n",
1398 subimage ? "sub" : "img",
1399 page->pageno, slicenum, blocknum,
1400 block->w, slice->h,
1401 block->texindex,
1402 state.texids[block->texindex],
1403 block->offset,
1404 end - start);
1408 CAMLprim value ml_draw (value args_v, value ptr_v)
1410 CAMLparam2 (args_v, ptr_v);
1411 int dispy = Int_val (Field (args_v, 0));
1412 int h = Int_val (Field (args_v, 1));
1413 int py = Int_val (Field (args_v, 2));
1414 int hlinks = Bool_val (Field (args_v, 3));
1415 char *s = String_val (ptr_v);
1416 int ret;
1417 void *ptr;
1418 struct page *page;
1419 int slicenum;
1420 int yoff = dispy - py;
1421 struct slice *slice;
1423 ret = sscanf (s, "%p", &ptr);
1424 if (ret != 1) {
1425 errx (1, "cannot parse pointer `%s'", s);
1427 page = ptr;
1429 ARSERT (h >= 0 && "ml_draw wrong h");
1430 ARSERT (py <= page->pixmap->h && "ml_draw wrong py");
1432 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1434 for (slicenum = 0, slice = page->slices;
1435 slicenum < page->slicecount; ++slicenum, ++slice) {
1436 if (slice->h > py) {
1437 break;
1439 py -= slice->h;
1441 h = MIN (state.h, h);
1443 while (h) {
1444 int th, left, blocknum;
1446 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
1448 th = MIN (h, slice->h - py);
1449 left = page->pagedim.left;
1451 for (blocknum = 0; blocknum < page->blockcount; ++blocknum) {
1452 struct block *block = &slice->blocks[blocknum];
1454 upload (page, slicenum, blocknum);
1455 glBegin (GL_QUADS);
1457 glTexCoord2i (0, py);
1458 glVertex2i (left, dispy);
1460 glTexCoord2i (block->w, py);
1461 glVertex2i (left + block->w, dispy);
1463 glTexCoord2i (block->w, py + th);
1464 glVertex2i (left + block->w, dispy + th);
1466 glTexCoord2i (0, py + th);
1467 glVertex2i (left, dispy + th);
1469 glEnd ();
1470 left += block->w;
1473 h -= th;
1474 py = 0;
1475 dispy += th;
1476 slicenum += 1;
1479 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1481 showsel (page, yoff);
1482 if (hlinks) highlightlinks (page, yoff);
1484 CAMLreturn (Val_unit);
1487 static pdf_link *getlink (struct page *page, int x, int y)
1489 fz_point p;
1490 fz_matrix ctm;
1491 pdf_link *link;
1493 p.x = x + page->pixmap->x;
1494 p.y = y + page->pixmap->y;
1496 ctm = fz_invert_matrix (page->pagedim.ctm);
1497 p = fz_transform_point (ctm, p);
1499 for (link = page->drawpage->links; link; link = link->next) {
1500 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1501 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1502 return link;
1506 return NULL;
1509 static void ensuretext (struct page *page)
1511 if (!page->text) {
1512 fz_error error;
1513 fz_device *tdev;
1515 page->text = fz_new_text_span ();
1516 tdev = fz_new_text_device (page->text);
1517 error = pdf_run_page (state.xref, page->drawpage, tdev,
1518 page->pagedim.ctm);
1519 if (error) die (error);
1520 fz_free_device (tdev);
1524 CAMLprim value ml_whatsunder (value ptr_v, value x_v, value y_v)
1526 CAMLparam3 (ptr_v, x_v, y_v);
1527 CAMLlocal3 (ret_v, tup_v, str_v);
1528 pdf_link *link;
1529 struct page *page;
1530 char *s = String_val (ptr_v);
1532 ret_v = Val_int (0);
1533 if (trylock ("ml_whatsunder")) {
1534 goto done;
1537 page = parse_pointer ("ml_whatsunder", s);
1538 link = getlink (page, Int_val (x_v), Int_val (y_v));
1539 if (link) {
1540 switch (link->kind) {
1541 case PDF_LINK_GOTO:
1543 int pageno;
1544 fz_point p;
1545 fz_obj *obj;
1547 pageno = -1;
1548 p.x = 0;
1549 p.y = 0;
1551 if (fz_is_array (link->dest)) {
1552 obj = fz_array_get (link->dest, 0);
1553 if (fz_is_indirect (obj)) {
1554 pageno = pdf_find_page_number (state.xref, obj);
1556 else if (fz_is_int (obj)) {
1557 pageno = fz_to_int (obj);
1560 if (fz_array_len (link->dest) > 3) {
1561 fz_obj *xo, *yo;
1563 xo = fz_array_get (link->dest, 2);
1564 yo = fz_array_get (link->dest, 3);
1565 if (!fz_is_null (xo) && !fz_is_null (yo)) {
1566 p.x = fz_to_int (xo);
1567 p.y = fz_to_int (yo);
1568 p = fz_transform_point (page->pagedim.ctm, p);
1572 else {
1573 pageno = pdf_find_page_number (state.xref, link->dest);
1575 tup_v = caml_alloc_tuple (2);
1576 ret_v = caml_alloc_small (1, 1);
1577 Field (tup_v, 0) = Val_int (pageno);
1578 Field (tup_v, 1) = Val_int (p.y);
1579 Field (ret_v, 0) = tup_v;
1581 break;
1583 case PDF_LINK_URI:
1584 str_v = caml_copy_string (fz_to_str_buf (link->dest));
1585 ret_v = caml_alloc_small (1, 0);
1586 Field (ret_v, 0) = str_v;
1587 break;
1589 default:
1590 printd (state.sock, "T unhandled link kind %d", link->kind);
1591 break;
1594 else {
1595 int i, x, y;
1596 fz_text_span *span;
1598 ensuretext (page);
1599 x = Int_val (x_v) + page->pixmap->x;
1600 y = Int_val (y_v) + page->pixmap->y;
1602 for (span = page->text; span; span = span->next) {
1603 for (i = 0; i < span->len; ++i) {
1604 fz_bbox *b;
1605 b = &span->text[i].bbox;
1606 if ((x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1)) {
1607 const char *n2 =
1608 span->font && span->font->name
1609 ? span->font->name
1610 : "Span has no font name"
1612 FT_FaceRec *face = span->font->ft_face;
1613 if (face && face->family_name) {
1614 char *s;
1615 char *n1 = face->family_name;
1616 size_t l1 = strlen (n1);
1617 size_t l2 = strlen (n2);
1619 if (l1 != l2 || memcmp (n1, n2, l1)) {
1620 s = malloc (l1 + l2 + 2);
1621 if (s) {
1622 memcpy (s, n2, l2);
1623 s[l2] = '=';
1624 memcpy (s + l2 + 1, n1, l1 + 1);
1625 str_v = caml_copy_string (s);
1626 free (s);
1630 if (str_v == 0) {
1631 str_v = caml_copy_string (n2);
1633 ret_v = caml_alloc_small (1, 2);
1634 Field (ret_v, 0) = str_v;
1635 goto unlock;
1640 unlock:
1641 unlock ("ml_whatsunder");
1643 done:
1644 CAMLreturn (ret_v);
1647 CAMLprim value ml_seltext (value ptr_v, value rect_v, value oy_v)
1649 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1650 fz_bbox *b;
1651 struct page *page;
1652 fz_text_span *span;
1653 struct mark first, last;
1654 int i, x0, x1, y0, y1, oy, left;
1655 char *s = String_val (ptr_v);
1657 if (trylock ("ml_seltext")) {
1658 goto done;
1661 page = parse_pointer ("ml_seltext", s);
1662 ensuretext (page);
1664 oy = Int_val (oy_v);
1665 x0 = Int_val (Field (rect_v, 0));
1666 y0 = Int_val (Field (rect_v, 1));
1667 x1 = Int_val (Field (rect_v, 2));
1668 y1 = Int_val (Field (rect_v, 3));
1670 left = page->pagedim.left;
1671 if (0) {
1672 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1673 glColor3ub (128, 128, 128);
1674 glRecti (x0, y0, x1, y1);
1675 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1678 x0 += page->pixmap->x - left;
1679 y0 += page->pixmap->y - oy;
1680 x1 += page->pixmap->x - left;
1681 y1 += page->pixmap->y - oy;
1683 first.span = NULL;
1684 last.span = NULL;
1686 last.i = first.i = 0;
1687 first.span = page->text;
1688 for (span = page->text; span; span = span->next) {
1689 for (i = 0; i < span->len; ++i) {
1690 b = &span->text[i].bbox;
1691 int selected = 0;
1693 if (x0 >= b->x0 && x0 <= b->x1 && y0 >= b->y0 && y0 <= b->y1) {
1694 first.i = i;
1695 first.span = span;
1696 selected = 1;
1698 if (x1 >= b->x0 && x1 <= b->x1 && y1 >= b->y0 && y1 <= b->y1) {
1699 last.i = i;
1700 last.span = span;
1701 selected = 1;
1703 if (0 && selected) {
1704 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1705 glColor3ub (128, 128, 128);
1706 glRecti (b->x0+left, b->y0, b->x1+left, b->y1);
1707 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1712 if (y1 < y0 || x1 < x0) {
1713 int swap = 0;
1715 if (first.span == last.span) {
1716 swap = 1;
1718 else {
1719 if (y1 < y0) {
1720 for (span = first.span; span && span != last.span;
1721 span = span->next) {
1722 if (span->eol) {
1723 swap = 1;
1724 break;
1730 if (swap) {
1731 i = first.i;
1732 span = first.span;
1733 first.i = last.i;
1734 first.span = last.span;
1735 last.i = i;
1736 last.span = span;
1740 page->fmark = first;
1741 page->lmark = last;
1743 unlock ("ml_seltext");
1745 done:
1746 CAMLreturn (Val_unit);
1749 static int pipespan (FILE *f, fz_text_span *span, int a, int b)
1751 char buf[4];
1752 int i, len, ret;
1754 for (i = a; i <= b; ++i) {
1755 len = runetochar (buf, &span->text[i].c);
1756 ret = fwrite (buf, len, 1, f);
1758 if (ret != 1) {
1759 printd (state.sock, "T failed to write %d bytes ret=%d: %s",
1760 len, ret, strerror (errno));
1761 return -1;
1764 return 0;
1767 CAMLprim value ml_copysel (value ptr_v)
1769 CAMLparam1 (ptr_v);
1770 FILE *f;
1771 struct page *page;
1772 char *s = String_val (ptr_v);
1774 if (trylock ("ml_copysel")) {
1775 goto done;
1778 if (!*s) {
1779 close:
1780 #ifdef USE_XSEL
1781 if (state.xselpipe) {
1782 int ret = pclose (state.xselpipe);
1783 if (ret) {
1784 printd (state.sock, "T failed to close xsel pipe `%s'",
1785 strerror (errno));
1787 state.xselpipe = NULL;
1789 #else
1790 printf ("========================================\n");
1791 #endif
1793 else {
1794 fz_text_span *span;
1796 page = parse_pointer ("ml_sopysel", s);
1798 if (!page->fmark.span || !page->lmark.span) {
1799 printd (state.sock, "T nothing to copy");
1800 goto unlock;
1803 f = stdout;
1804 #ifdef USE_XSEL
1805 if (!state.xselpipe) {
1806 state.xselpipe = popen ("xsel -i", "w");
1807 if (!state.xselpipe) {
1808 printd (state.sock, "T failed to open xsel pipe `%s'",
1809 strerror (errno));
1811 else {
1812 f = state.xselpipe;
1815 else {
1816 f = state.xselpipe;
1818 #endif
1820 for (span = page->fmark.span;
1821 span && span != page->lmark.span->next;
1822 span = span->next) {
1823 int a = span == page->fmark.span ? page->fmark.i : 0;
1824 int b = span == page->lmark.span ? page->lmark.i : span->len - 1;
1825 if (pipespan (f, span, a, b)) {
1826 goto close;
1828 if (span->eol) {
1829 if (putc ('\n', f) == EOF) {
1830 printd (state.sock, "T failed break line on xsel pipe `%s'",
1831 strerror (errno));
1832 goto close;
1836 page->lmark.span = NULL;
1837 page->fmark.span = NULL;
1840 unlock:
1841 unlock ("ml_copysel");
1843 done:
1844 CAMLreturn (Val_unit);
1847 CAMLprim value ml_getpdimrect (value pagedimno_v)
1849 CAMLparam1 (pagedimno_v);
1850 CAMLlocal1 (ret_v);
1851 int pagedimno = Int_val (pagedimno_v);
1852 fz_rect box;
1854 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1855 if (trylock ("ml_getpdimrect")) {
1856 box = fz_empty_rect;
1858 else {
1859 box = state.pagedims[pagedimno].box;
1860 unlock ("ml_getpdimrect");
1863 Store_double_field (ret_v, 0, box.x0);
1864 Store_double_field (ret_v, 1, box.x1);
1865 Store_double_field (ret_v, 2, box.y0);
1866 Store_double_field (ret_v, 3, box.y1);
1868 CAMLreturn (ret_v);
1871 static double getmaxw (void)
1873 int i;
1874 struct pagedim *p;
1875 double maxw = 0.0;
1877 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
1878 double x0, x1, w;
1880 x0 = MIN (p->box.x0, p->box.x1);
1881 x1 = MAX (p->box.x0, p->box.x1);
1883 w = x1 - x0;
1884 maxw = MAX (w, maxw);
1886 return maxw;
1889 CAMLprim value ml_getmaxw (value unit_v)
1891 CAMLparam1 (unit_v);
1892 CAMLlocal1 (ret_v);
1893 double maxw = 0.0;
1895 if (trylock ("ml_getmaxw")) {
1896 goto done;
1898 maxw = getmaxw ();
1899 unlock ("ml_getmaxw");
1900 done:
1901 ret_v = caml_copy_double (maxw);
1902 CAMLreturn (ret_v);
1905 CAMLprim value ml_zoom_for_height (value winw_v, value winh_v, value dw_v)
1907 CAMLparam3 (winw_v, winh_v, dw_v);
1908 CAMLlocal1 (ret_v);
1909 int i;
1910 double zoom = 1.0;
1911 double maxw = 0.0, maxh = 0.0;
1912 struct pagedim *p;
1913 double winw = Int_val (winw_v);
1914 double winh = Int_val (winh_v);
1915 double dw = Int_val (dw_v);
1916 double pw = 1.0, ph = 1.0, num, den;
1918 if (trylock ("ml_zoom_for_height")) {
1919 goto done;
1922 if (state.proportional) {
1923 maxw = getmaxw ();
1926 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
1927 double x0, x1, y0, y1, w, h, scaledh, scale;
1929 x0 = MIN (p->box.x0, p->box.x1);
1930 y0 = MIN (p->box.y0, p->box.y1);
1931 x1 = MAX (p->box.x0, p->box.x1);
1932 y1 = MAX (p->box.y0, p->box.y1);
1934 w = x1 - x0;
1935 h = y1 - y0;
1937 if (state.proportional) {
1938 scale = w / maxw;
1939 scaledh = h * scale;
1941 else {
1942 scale = 1.0;
1943 scaledh = h;
1946 if (scaledh > maxh) {
1947 maxh = scaledh;
1948 ph = scaledh;
1949 pw = w * scale;
1953 num = (winh * pw) + (ph * dw);
1954 den = ph * winw;
1955 zoom = num / den;
1957 unlock ("ml_zoom_for_height");
1958 done:
1959 ret_v = caml_copy_double (zoom);
1960 CAMLreturn (ret_v);
1963 #include "glfont.c"
1965 CAMLprim value ml_draw_string (value pt_v, value x_v, value y_v, value string_v)
1967 CAMLparam4 (pt_v, x_v, y_v, string_v);
1968 int pt = Int_val(pt_v);
1969 int x = Int_val (x_v);
1970 int y = Int_val (y_v);
1971 double w;
1973 w = draw_string (state.face, pt, x, y, String_val (string_v));
1974 CAMLreturn (caml_copy_double (w));
1977 CAMLprim value ml_measure_string (value pt_v, value string_v)
1979 CAMLparam2 (pt_v, string_v);
1980 CAMLlocal1 (ret_v);
1981 int pt = Int_val (pt_v);
1982 double w;
1984 w = measure_string (state.face, pt, String_val (string_v));
1985 CAMLreturn (caml_copy_double (w));
1988 CAMLprim value ml_init (value sock_v, value params_v)
1990 CAMLparam2 (sock_v, params_v);
1991 #ifndef _WIN32
1992 int ret;
1993 #endif
1994 char *fontpath;
1996 state.rotate = Int_val (Field (params_v, 0));
1997 state.proportional = Bool_val (Field (params_v, 1));
1998 state.texcount = Int_val (Field (params_v, 2));
1999 state.sliceheight = Int_val (Field (params_v, 3));
2000 state.blockwidth = Int_val (Field (params_v, 4));
2001 fontpath = String_val (Field (params_v, 5));
2002 state.texform = GL_RGBA;
2003 state.texty = GL_UNSIGNED_BYTE;
2005 if (*fontpath) {
2006 state.face = load_font (fontpath);
2008 else {
2009 unsigned int len;
2010 void *base = pdf_find_substitute_font (0, 0, 0, 0, &len);
2012 state.face = load_builtin_font (base, len);
2014 if (!state.face) _exit (1);
2016 fz_accelerate ();
2017 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
2018 if (!state.texids) {
2019 err (1, "calloc texids " FMT_s,
2020 state.texcount * sizeof (*state.texids));
2023 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
2024 if (!state.texowners) {
2025 err (1, "calloc texowners " FMT_s,
2026 state.texcount * sizeof (*state.texowners));
2029 glGenTextures (state.texcount, state.texids);
2031 #ifdef _WIN32
2032 state.sock = Socket_val (sock_v);
2033 #else
2034 state.sock = Int_val (sock_v);
2035 #endif
2037 #ifdef _WIN32
2038 InitializeCriticalSection (&critsec);
2039 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
2040 if (state.thread == INVALID_HANDLE_VALUE) {
2041 errx (1, "CreateThread failed: %lx", GetLastError ());
2043 #else
2044 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
2045 if (ret) {
2046 errx (1, "pthread_create: %s", strerror (ret));
2048 #endif
2050 CAMLreturn (Val_unit);