Refactor to avoid uninitialized variable warning
[llpp.git] / link.c
blob1f4a0dd28d5aa3cc2a417930329fdd15e78f4fe5
1 /* lots of code c&p-ed directly from mupdf */
2 #include <errno.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <stdlib.h>
7 #ifdef _WIN32
8 #define WIN32_LEAN_AND_MEAN
9 #include <windows.h>
10 #include <winsock2.h>
11 #define fionread_arg long
12 #define ssize_t int
13 #define FMT_ss "%d"
14 #ifdef _WIN64
15 #define FMT_s "%i64u"
16 #else
17 #define FMT_s "%u"
18 #endif
19 #pragma warning (disable:4244)
20 #pragma warning (disable:4996)
21 #pragma warning (disable:4995)
22 #endif
24 #ifdef _MSC_VER
25 #define NORETURN __declspec (noreturn)
26 #define UNUSED
27 #elif defined __GNUC__
28 #define NORETURN __attribute__ ((noreturn))
29 #define UNUSED __attribute__ ((unused))
30 #else
31 #define NORETURN
32 #define UNUSED
33 #endif
35 #ifdef _WIN32
36 static void __declspec (noreturn) sockerr (int exitcode, const char *fmt, ...)
38 va_list ap;
40 va_start (ap, fmt);
41 vfprintf (stderr, fmt, ap);
42 va_end (ap);
43 fprintf (stderr, ": wsaerror 0x%x\n", WSAGetLastError ());
44 _exit (exitcode);
46 #else
47 #define FMT_ss "%zd"
48 #define FMT_s "%zu"
49 #define fionread_arg int
50 #define ioctlsocket ioctl
51 #define sockerr err
52 #include <unistd.h>
53 #endif
55 #include <regex.h>
56 #include <ctype.h>
57 #include <stdarg.h>
58 #include <limits.h>
60 #ifndef _WIN32
61 #include <pthread.h>
62 #include <sys/time.h>
63 #include <sys/types.h>
64 #include <sys/socket.h>
65 #include <sys/ioctl.h>
66 #endif
68 static void NORETURN err (int exitcode, const char *fmt, ...)
70 va_list ap;
71 int savederrno;
73 savederrno = errno;
74 va_start (ap, fmt);
75 vfprintf (stderr, fmt, ap);
76 va_end (ap);
77 fprintf (stderr, ": %s\n", strerror (savederrno));
78 fflush (stderr);
79 _exit (exitcode);
82 static void NORETURN errx (int exitcode, const char *fmt, ...)
84 va_list ap;
86 va_start (ap, fmt);
87 vfprintf (stderr, fmt, ap);
88 va_end (ap);
89 fputc ('\n', stderr);
90 fflush (stderr);
91 _exit (exitcode);
94 #ifdef __APPLE__
95 #include <OpenGL/gl.h>
96 #else
97 #include <GL/gl.h>
98 #endif
100 #ifndef GL_TEXTURE_RECTANGLE_ARB
101 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
102 #endif
104 #include <caml/fail.h>
105 #include <caml/alloc.h>
106 #include <caml/memory.h>
107 #include <caml/unixsupport.h>
109 #include <fitz.h>
110 #include <mupdf.h>
112 #if 0
113 #define lprintf printf
114 #else
115 #define lprintf(...)
116 #endif
118 #ifdef FT_FREETYPE_H
119 #include FT_FREETYPE_H
120 #endif
122 #define ARSERT(cond) for (;;) { \
123 if (!(cond)) { \
124 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
126 break; \
129 struct block {
130 int w;
131 int x;
132 int offset;
133 int texindex;
136 struct slice {
137 int h;
138 struct block *blocks;
141 struct pagedim {
142 int pageno;
143 int rotate;
144 int left;
145 fz_rect box;
146 fz_bbox bbox;
147 fz_matrix ctm, ctm1;
150 struct page {
151 int pageno;
152 int slicecount;
153 int blockcount;
154 fz_text_span *text;
155 fz_pixmap *pixmap;
156 pdf_page *drawpage;
157 struct pagedim pagedim;
158 struct mark {
159 int i;
160 fz_text_span *span;
161 } fmark, lmark;
162 struct slice *slices;
165 #if !defined _WIN32 && !defined __APPLE__
166 #define USE_XSEL
167 #endif
169 struct {
170 int sock;
171 int sliceheight, blockwidth;
172 struct page *pig;
173 struct pagedim *pagedims;
174 int pagecount;
175 int pagedimcount;
176 pdf_xref *xref;
177 fz_glyph_cache *cache;
178 int w, h;
180 int texindex;
181 int texcount;
182 GLuint *texids;
184 GLenum texform;
185 GLenum texty;
187 struct {
188 int w, h;
189 struct block *block;
190 } *texowners;
192 int rotate;
193 int proportional;
194 int needoutline;
196 #ifdef _WIN32
197 HANDLE thread;
198 #else
199 pthread_t thread;
200 #endif
201 FILE *xselpipe;
203 FT_Face face;
204 } state;
206 #ifdef _WIN32
207 static CRITICAL_SECTION critsec;
209 static void lock (void *unused)
211 (void) unused;
212 EnterCriticalSection (&critsec);
215 static void unlock (void *unused)
217 (void) unused;
218 LeaveCriticalSection (&critsec);
221 static int trylock (void *unused)
223 return TryEnterCriticalSection (&critsec) == 0;
225 #else
226 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
228 static void lock (const char *cap)
230 int ret = pthread_mutex_lock (&mutex);
231 if (ret) {
232 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
236 static void unlock (const char *cap)
238 int ret = pthread_mutex_unlock (&mutex);
239 if (ret) {
240 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
244 static int trylock (const char *cap)
246 int ret = pthread_mutex_trylock (&mutex);
248 if (ret && ret != EBUSY) {
249 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
251 return ret == EBUSY;
253 #endif
255 static void *parse_pointer (const char *cap, const char *s)
257 int ret;
258 void *ptr;
260 ret = sscanf (s, "%p", &ptr);
261 if (ret != 1) {
262 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
264 return ptr;
267 static int hasdata (int sock)
269 int ret;
270 fionread_arg avail;
271 ret = ioctlsocket (sock, FIONREAD, &avail);
272 if (ret) sockerr (1, "hasdata: FIONREAD error ret=%d", ret);
273 return avail > 0;
276 static double now (void)
278 struct timeval tv;
280 if (gettimeofday (&tv, NULL)) {
281 err (1, "gettimeofday");
283 return tv.tv_sec + tv.tv_usec*1e-6;
286 static void readdata (int fd, char *p, int size)
288 ssize_t n;
290 n = recv (fd, p, size, 0);
291 if (n - size) {
292 if (!n) errx (1, "EOF while reading");
293 sockerr (1, "recv (req %d, ret " FMT_ss ")", size, n);
297 static void writedata (int fd, char *p, int size)
299 char buf[4];
300 ssize_t n;
302 buf[0] = (size >> 24) & 0xff;
303 buf[1] = (size >> 16) & 0xff;
304 buf[2] = (size >> 8) & 0xff;
305 buf[3] = (size >> 0) & 0xff;
307 n = send (fd, buf, 4, 0);
308 if (n != 4) {
309 if (!n) errx (1, "EOF while writing length");
310 sockerr (1, "send " FMT_ss, n);
313 n = send (fd, p, size, 0);
314 if (n - size) {
315 if (!n) errx (1, "EOF while writing data");
316 sockerr (1, "send (req %d, ret " FMT_ss ")", size, n);
320 static void
321 #ifdef __GNUC__
322 __attribute__ ((format (printf, 2, 3)))
323 #endif
324 printd (int fd, const char *fmt, ...)
326 int size = 200, len;
327 va_list ap;
328 char *buf;
330 buf = malloc (size);
331 for (;;) {
332 if (!buf) err (errno, "malloc for temp buf (%d bytes) failed", size);
334 va_start (ap, fmt);
335 len = vsnprintf (buf, size, fmt, ap);
336 va_end (ap);
338 if (len > -1 && len < size) {
339 writedata (fd, buf, len);
340 break;
343 if (len > -1) {
344 size = len + 1;
346 else {
347 size *= 2;
349 buf = realloc (buf, size);
351 free (buf);
354 static void die (fz_error error)
356 fz_catch (error, "aborting");
357 if (state.xref)
358 pdf_free_xref (state.xref);
359 _exit (1);
362 static void openxref (char *filename, char *password)
364 int i;
365 fz_error error;
367 for (i = 0; i < state.texcount; ++i) {
368 state.texowners[i].block = NULL;
371 if (state.cache) {
372 fz_free_glyph_cache (state.cache);
375 state.cache = fz_new_glyph_cache ();
376 if (!state.cache) {
377 errx (1, "fz_newglyph_cache failed");
380 if (state.xref) {
381 if (state.xref->store) {
382 pdf_free_store (state.xref->store);
383 state.xref->store = NULL;
385 pdf_free_xref (state.xref);
386 state.xref = NULL;
389 if (state.pagedims) {
390 free (state.pagedims);
391 state.pagedims = NULL;
393 state.pagedimcount = 0;
395 error = pdf_open_xref (&state.xref, filename, password);
396 if (error) {
397 die (error);
400 error = pdf_load_page_tree (state.xref);
401 if (error) {
402 die (error);
405 state.pagecount = pdf_count_pages (state.xref);
408 static void pdfinfo (void)
410 fz_obj *infoobj;
412 printd (state.sock, "i PDF version\t%d.%d",
413 state.xref->version / 10, state.xref->version % 10);
415 infoobj = fz_dict_gets (state.xref->trailer, "Info");
416 if (infoobj) {
417 int i;
418 char *s;
419 char *items[] = { "Title", "Creator", "Producer", "CreationDate" };
421 for (i = 0; i < sizeof (items) / sizeof (*items); ++i) {
422 fz_obj *obj = fz_dict_gets (infoobj, items[i]);
423 s = pdf_to_utf8 (obj);
424 if (*s) {
425 if (i == 0) {
426 printd (state.sock, "t %s", s);
428 printd (state.sock, "i %s\t%s", items[i], s);
430 fz_free (s);
432 printd (state.sock, "ie");
436 static int readlen (int fd)
438 ssize_t n;
439 unsigned char p[4];
441 n = recv (fd, p, 4, 0);
442 if (n != 4) {
443 if (!n) errx (1, "EOF while reading length");
444 sockerr (1, "recv " FMT_ss, n);
447 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
450 static void unlinkpage (struct page *page)
452 int i;
454 for (i = 0; i < page->slicecount; ++i) {
455 int j;
456 struct slice *s = &page->slices[i];
458 for (j = 0; j < page->blockcount; ++j) {
459 struct block *b = &s->blocks[j];
461 if (b->texindex != -1) {
462 if (state.texowners[b->texindex].block == b) {
463 state.texowners[b->texindex].block = NULL;
470 static void freepage (struct page *page)
472 int i;
474 fz_drop_pixmap (page->pixmap);
476 unlinkpage (page);
478 if (page->text) {
479 fz_free_text_span (page->text);
481 if (page->drawpage) {
482 pdf_free_page (page->drawpage);
484 for (i = 0; i < page->slicecount; ++i) {
485 free (page->slices[i].blocks);
487 free (page->slices);
488 free (page);
491 static void subdivide (struct page *p, int blockcount)
493 int i;
494 int w = p->pixmap->w;
495 int h = p->pixmap->h;
496 int offset = 0;
497 int tw = MIN (w, state.blockwidth);
498 int th = MIN (h, state.sliceheight);
500 for (i = 0; i < p->slicecount; ++i) {
501 int j, x, offset1;
502 struct slice *s = &p->slices[i];
504 s->h = MIN (th, h);
506 s->blocks = calloc (sizeof (struct block), p->blockcount);
507 if (!s->blocks) {
508 err (1, "calloc blocks page %d slice %d, %d",
509 p->pageno, i, p->blockcount);
512 w = p->pixmap->w;
513 x = 0;
514 offset1 = offset;
516 for (j = 0; j < blockcount; ++j) {
517 struct block *b = &s->blocks[j];
519 b->texindex = -1;
520 b->w = MIN (tw, w);
521 b->x = x;
522 b->offset = offset1;
523 w -= tw;
524 x += tw;
525 offset1 += tw * 4;
527 offset += s->h * p->pixmap->w * 4;
528 h -= th;
532 static int compatpdims (struct pagedim *p1, struct pagedim *p2)
534 return p1->rotate == p2->rotate
535 && !memcmp (&p1->bbox, &p2->bbox, sizeof (p1->bbox))
536 && !memcmp (&p1->ctm, &p2->ctm, sizeof (p1->ctm));
539 #ifdef __ALTIVEC__
540 #include <altivec.h>
542 static int cacheline32bytes;
543 extern char **environ;
545 static void __attribute__ ((constructor)) clcheck (void)
547 char **envp = environ;
548 unsigned long *auxv;
550 while (*envp++);
552 for (auxv = (unsigned long *) envp; *auxv != 0; auxv += 2) {
553 if (*auxv == 19) {
554 cacheline32bytes = auxv[1] == 32;
555 return;
560 #if __GNUC__ > 4 && __GNUC_MINOR__ > 3
561 #define OPTIMIZE __attribute__ ((optimize ("O3")))
562 #else
563 #define OPTIMIZE
564 #endif
566 static void clearpixmap (fz_pixmap *pixmap)
568 if (cacheline32bytes) {
569 intptr_t a1, a2, diff;
570 size_t sizea, i, size = pixmap->w * pixmap->h * pixmap->n;
571 vector unsigned char v = vec_splat_u8 (-1);
572 vector unsigned char *p;
574 a1 = a2 = (intptr_t) pixmap->samples;
575 a2 = (a1 + 31) & ~31;
576 diff = a2 - a1;
577 sizea = size - diff;
578 p = (void *) a2;
580 while (a1 != a2) *(char *) a1++ = 0xff;
581 for (i = 0; i < (sizea & ~31); i += 32) {
582 __asm volatile ("dcbz %0, %1"::"b"(a2),"r"(i));
583 vec_st (v, i, p);
584 vec_st (v, i + 16, p);
586 while (i < sizea) *((char *) a1 + i++) = 0xff;
588 else fz_clear_pixmap_with_color (pixmap, 0xff);
590 #else
591 #define clearpixmap(p) fz_clear_pixmap_with_color (p, 0xff)
592 #endif
594 static void *render (int pageno, int pindex)
596 fz_error error;
597 fz_device *idev;
598 double start, end;
599 pdf_page *drawpage;
600 struct pagedim *pagedim;
601 struct page *page = NULL;
602 int slicecount, blockcount;
604 start = now ();
605 printd (state.sock, "V rendering %d", pageno);
607 pagedim = &state.pagedims[pindex];
608 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
609 + state.sliceheight - 1) / state.sliceheight;
610 slicecount += slicecount == 0;
612 blockcount = (pagedim->bbox.x1 - pagedim->bbox.x0
613 + state.blockwidth - 1) / state.blockwidth;
614 blockcount += blockcount == 0;
616 if (state.pig) {
617 if (compatpdims (&state.pig->pagedim, pagedim)) {
618 page = state.pig;
619 if (page->text) {
620 fz_free_text_span (page->text);
621 page->text = NULL;
623 if (page->drawpage) {
624 pdf_free_page (page->drawpage);
625 page->drawpage = NULL;
628 else {
629 freepage (state.pig);
632 if (!page) {
633 page = calloc (sizeof (struct page), 1);
634 if (!page) {
635 err (1, "calloc page %d", pageno);
637 page->slices = calloc (sizeof (struct slice), slicecount);
638 if (!page->slices) {
639 err (1, "calloc slices %d %d", pageno, slicecount);
641 page->pixmap = fz_new_pixmap_with_rect (fz_device_rgb, pagedim->bbox);
644 page->slicecount = slicecount;
645 page->blockcount = blockcount;
647 error = pdf_load_page (&drawpage, state.xref, pageno - 1);
648 if (error)
649 die (error);
651 clearpixmap (page->pixmap);
653 idev = fz_new_draw_device (state.cache, page->pixmap);
654 if (!idev)
655 die (fz_throw ("fz_newdrawdevice failed"));
656 error = pdf_run_page (state.xref, drawpage, idev, pagedim->ctm);
657 if (error)
658 die (fz_rethrow (error, "pdf_runpage failed"));
659 fz_free_device (idev);
661 page->drawpage = drawpage;
662 page->pagedim = *pagedim;
663 page->pageno = pageno;
664 subdivide (page, blockcount);
665 end = now ();
667 pdf_age_store (state.xref->store, 3);
669 printd (state.sock, "V rendering %d took %f sec", pageno, end - start);
670 state.pig = NULL;
671 return page;
674 static void initpdims (void)
676 int pageno;
677 double start, end;
679 start = now ();
680 for (pageno = 0; pageno < state.pagecount; ++pageno) {
681 int rotate;
682 fz_rect box;
683 struct pagedim *p;
684 fz_obj *obj, *pageobj;
686 pageobj = state.xref->page_objs[pageno];
688 obj = fz_dict_gets (pageobj, "CropBox");
689 if (!fz_is_array (obj)) {
690 obj = fz_dict_gets (pageobj, "MediaBox");
691 if (!fz_is_array (obj)) {
692 die (fz_throw ("cannot find page bounds %d (%d Rd)",
693 fz_to_num (pageobj), fz_to_gen (pageobj)));
696 box = pdf_to_rect (obj);
698 obj = fz_dict_gets (pageobj, "Rotate");
699 if (fz_is_int (obj)) {
700 rotate = fz_to_int (obj);
702 else {
703 rotate = 0;
705 rotate += state.rotate;
707 p = &state.pagedims[state.pagedimcount - 1];
708 if ((state.pagedimcount == 0)
709 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
710 size_t size;
712 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
713 state.pagedims = realloc (state.pagedims, size);
714 if (!state.pagedims) {
715 err (1, "realloc pagedims to " FMT_s " (%d elems)",
716 size, state.pagedimcount + 1);
718 p = &state.pagedims[state.pagedimcount++];
719 p->rotate = rotate;
720 p->box = box;
721 p->pageno = pageno;
724 end = now ();
725 printd (state.sock, "T Processed %d pages in %f seconds",
726 state.pagecount, end - start);
729 static void layout (void)
731 int pindex;
732 fz_matrix ctm;
733 fz_rect box, box2;
734 double zoom, w, maxw = 0;
735 struct pagedim *p = state.pagedims;
737 if (state.proportional) {
738 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
739 box.x0 = MIN (p->box.x0, p->box.x1);
740 box.y0 = MIN (p->box.y0, p->box.y1);
741 box.x1 = MAX (p->box.x0, p->box.x1);
742 box.y1 = MAX (p->box.y0, p->box.y1);
744 ctm = fz_identity;
745 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
746 ctm = fz_concat (ctm, fz_rotate (p->rotate));
747 box2 = fz_transform_rect (ctm, box);
748 w = box2.x1 - box2.x0;
749 maxw = MAX (w, maxw);
753 p = state.pagedims;
754 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
755 box.x0 = MIN (p->box.x0, p->box.x1);
756 box.y0 = MIN (p->box.y0, p->box.y1);
757 box.x1 = MAX (p->box.x0, p->box.x1);
758 box.y1 = MAX (p->box.y0, p->box.y1);
760 ctm = fz_identity;
761 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
762 ctm = fz_concat (ctm, fz_rotate (p->rotate));
763 box2 = fz_transform_rect (ctm, box);
764 w = box2.x1 - box2.x0;
766 if (state.proportional) {
767 double scale = w / maxw;
768 zoom = (state.w / w) * scale;
770 else {
771 zoom = state.w / w;
773 ctm = fz_identity;
774 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
775 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
776 memcpy (&p->ctm1, &ctm, sizeof (ctm));
777 ctm = fz_concat (ctm, fz_rotate (p->rotate));
778 p->bbox = fz_round_rect (fz_transform_rect (ctm, box));
779 p->left = state.proportional ? ((maxw - w) * zoom) / 2.0 : 0;
780 memcpy (&p->ctm, &ctm, sizeof (ctm));
783 while (p-- != state.pagedims) {
784 printd (state.sock, "l %d %d %d %d",
785 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0,
786 p->left);
790 static void recurse_outline (pdf_outline *outline, int level)
792 while (outline) {
793 int i;
794 fz_obj *obj;
795 int pageno = -1;
796 int top = 0, h = 0;
798 if (!outline->link) goto next;
800 obj = outline->link->dest;
801 if (fz_is_indirect (obj)) {
802 obj = fz_resolve_indirect (obj);
804 if (fz_is_array (obj)) {
805 fz_obj *obj2;
807 obj2 = fz_array_get (obj, 0);
808 if (fz_is_int (obj2)) {
809 pageno = fz_to_int (obj2);
811 else {
812 pageno = pdf_find_page_number (state.xref, obj2);
815 if (fz_array_len (obj) > 3) {
816 fz_point p;
817 fz_obj *xo, *yo;
819 xo = fz_array_get (obj, 2);
820 yo = fz_array_get (obj, 3);
821 if (!fz_is_null (xo) && !fz_is_null (yo)) {
822 struct pagedim *pagedim = state.pagedims;
824 for (i = 0; i < state.pagedimcount; ++i) {
825 if (state.pagedims[i].pageno > pageno)
826 break;
827 pagedim = &state.pagedims[i];
829 p.x = fz_to_int (xo);
830 p.y = fz_to_int (yo);
831 p = fz_transform_point (pagedim->ctm, p);
832 h = pagedim->bbox.y1 - pagedim->bbox.y0;
833 top = p.y;
837 else {
838 pageno = pdf_find_page_number (state.xref, outline->link->dest);
841 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
842 if (pageno > 0) {
843 printd (state.sock, "o %d %d %d %d %s",
844 level, pageno, top, h, outline->title);
846 next:
847 if (outline->child) {
848 recurse_outline (outline->child, level + 1);
850 outline = outline->next;
854 static void process_outline (void)
856 pdf_outline *outline;
858 if (!state.needoutline) return;
860 state.needoutline = 0;
861 outline = pdf_load_outline (state.xref);
862 if (outline) {
863 recurse_outline (outline, 0);
864 pdf_free_outline (outline);
868 static int comparespans (const void *l, const void *r)
870 fz_text_span const *const*ls = l;
871 fz_text_span const *const*rs = r;
872 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
875 /* wishful thinking function */
876 static void search (regex_t *re, int pageno, int y, int forward)
878 int i, j;
879 int ret;
880 char *p;
881 char buf[256];
882 fz_matrix ctm;
883 fz_error error;
884 fz_device *tdev;
885 pdf_page *drawpage;
886 fz_text_span *text, *span, **pspan;
887 struct pagedim *pdim, *pdimprev;
888 int stop = 0;
889 int niters = 0;
890 int nspans;
891 double start, end;
893 start = now ();
894 while (pageno >= 0 && pageno < state.pagecount && !stop) {
895 if (niters++ == 5) {
896 pdf_age_store (state.xref->store, 3);
897 niters = 0;
898 if (hasdata (state.sock)) {
899 printd (state.sock, "T attention requested aborting search at %d",
900 pageno);
901 stop = 1;
903 else {
904 printd (state.sock, "T searching in page %d", pageno);
907 pdimprev = NULL;
908 for (i = 0; i < state.pagedimcount; ++i) {
909 pdim = &state.pagedims[i];
910 if (pdim->pageno == pageno) {
911 goto found;
913 if (pdim->pageno > pageno) {
914 pdim = pdimprev;
915 goto found;
917 pdimprev = pdim;
919 pdim = pdimprev;
920 found:
922 error = pdf_load_page (&drawpage, state.xref, pageno);
923 if (error)
924 die (error);
926 ctm = fz_rotate (pdim->rotate);
928 text = fz_new_text_span ();
929 tdev = fz_new_text_device (text);
930 error = pdf_run_page (state.xref, drawpage, tdev, pdim->ctm1);
931 if (error) die (error);
932 fz_free_device (tdev);
934 nspans = 0;
935 for (span = text; span; span = span->next) {
936 nspans++;
938 pspan = malloc (sizeof (void *) * nspans);
939 if (!pspan) {
940 err (1, "malloc span pointers " FMT_s, sizeof (void *) * nspans);
942 for (i = 0, span = text; span; span = span->next, ++i) {
943 pspan[i] = span;
945 qsort (pspan, nspans, sizeof (fz_text_span *), comparespans);
947 j = forward ? 0 : nspans - 1;
948 while (nspans--) {
949 regmatch_t rm;
951 span = pspan[j];
952 j += forward ? 1 : -1;
953 p = buf;
954 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
955 if (forward) {
956 if (span->text[i].bbox.y0 < y + 1) {
957 continue;
960 else {
961 if (span->text[i].bbox.y0 > y - 1) {
962 continue;
965 if (span->text[i].c < 256) {
966 *p++ = span->text[i].c;
968 else {
969 *p++ = '?';
972 if (p == buf) {
973 continue;
975 *p++ = 0;
977 ret = regexec (re, buf, 1, &rm, 0);
978 if (ret) {
979 if (ret != REG_NOMATCH) {
980 size_t size;
981 char errbuf[80];
982 size = regerror (ret, re, errbuf, sizeof (errbuf));
983 printd (state.sock,
984 "T regexec error `%.*s'",
985 (int) size, errbuf);
986 fz_free_text_span (text);
987 pdf_free_page (drawpage);
988 free (pspan);
989 return;
992 else {
993 int xoff, yoff;
994 fz_bbox *sb, *eb;
995 fz_point p1, p2, p3, p4;
997 xoff = pdim->left - pdim->bbox.x0;
998 yoff = -pdim->bbox.y0;
1000 sb = &span->text[rm.rm_so].bbox;
1001 eb = &span->text[rm.rm_eo - 1].bbox;
1003 p1.x = sb->x0;
1004 p1.y = sb->y0;
1005 p2.x = eb->x1;
1006 p2.y = sb->y0;
1007 p3.x = eb->x1;
1008 p3.y = eb->y1;
1009 p4.x = sb->x0;
1010 p4.y = eb->y1;
1012 p1 = fz_transform_point (ctm, p1);
1013 p2 = fz_transform_point (ctm, p2);
1014 p3 = fz_transform_point (ctm, p3);
1015 p4 = fz_transform_point (ctm, p4);
1017 if (!stop) {
1018 printd (state.sock, "F %d %d %f %f %f %f %f %f %f %f",
1019 pageno, 1,
1020 p1.x + xoff, p1.y + yoff,
1021 p2.x + xoff, p2.y + yoff,
1022 p3.x + xoff, p3.y + yoff,
1023 p4.x + xoff, p4.y + yoff);
1025 printd (state.sock, "T found at %d `%.*s' in %f sec",
1026 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
1027 now () - start);
1029 else {
1030 printd (state.sock, "R %d %d %f %f %f %f %f %f %f %f",
1031 pageno, 2,
1032 p1.x + xoff, p1.y + yoff,
1033 p2.x + xoff, p2.y + yoff,
1034 p3.x + xoff, p3.y + yoff,
1035 p4.x + xoff, p4.y + yoff);
1037 stop = 1;
1040 if (forward) {
1041 pageno += 1;
1042 y = 0;
1044 else {
1045 pageno -= 1;
1046 y = INT_MAX;
1048 fz_free_text_span (text);
1049 pdf_free_page (drawpage);
1050 free (pspan);
1052 end = now ();
1053 if (!stop) {
1054 printd (state.sock, "T no matches %f sec", end - start);
1056 printd (state.sock, "D");
1059 static
1060 #ifdef _WIN32
1061 DWORD _stdcall
1062 #else
1063 void *
1064 #endif
1065 mainloop (void *unused)
1067 char *p = NULL;
1068 int len, ret, oldlen = 0;
1070 for (;;) {
1071 len = readlen (state.sock);
1072 if (len == 0) {
1073 errx (1, "readlen returned 0");
1076 if (oldlen < len + 1) {
1077 p = realloc (p, len + 1);
1078 if (!p) {
1079 err (1, "realloc %d failed", len + 1);
1081 oldlen = len + 1;
1083 readdata (state.sock, p, len);
1084 p[len] = 0;
1086 if (!strncmp ("open", p, 4)) {
1087 size_t filenamelen;
1088 char *password;
1089 char *filename = p + 5;
1091 filenamelen = strlen (filename);
1092 password = filename + filenamelen + 1;
1094 openxref (filename, password);
1095 initpdims ();
1096 pdfinfo ();
1097 state.needoutline = 1;
1099 else if (!strncmp ("free", p, 4)) {
1100 void *ptr;
1102 ret = sscanf (p + 4, " %p", &ptr);
1103 if (ret != 1) {
1104 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
1106 unlinkpage (ptr);
1107 state.pig = ptr;
1109 else if (!strncmp ("search", p, 6)) {
1110 int icase, pageno, y, ret, len2, forward;
1111 char *pattern;
1112 regex_t re;
1114 ret = sscanf (p + 6, " %d %d %d %d,%n",
1115 &icase, &pageno, &y, &forward, &len2);
1116 if (ret != 4) {
1117 errx (1, "malformed search `%s' ret=%d", p, ret);
1120 pattern = p + 6 + len2;
1121 ret = regcomp (&re, pattern,
1122 REG_EXTENDED | (icase ? REG_ICASE : 0));
1123 if (ret) {
1124 char errbuf[80];
1125 size_t size;
1127 size = regerror (ret, &re, errbuf, sizeof (errbuf));
1128 printd (state.sock, "T regcomp failed `%.*s'",
1129 (int) size, errbuf);
1131 else {
1132 search (&re, pageno, y, forward);
1133 regfree (&re);
1136 else if (!strncmp ("geometry", p, 8)) {
1137 int w, h;
1139 printd (state.sock, "c");
1140 ret = sscanf (p + 8, " %d %d", &w, &h);
1141 if (ret != 2) {
1142 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
1145 lock ("geometry");
1146 state.h = h;
1147 if (w != state.w) {
1148 int i;
1149 state.w = w;
1150 for (i = 0; i < state.texcount; ++i) {
1151 state.texowners[i].block = NULL;
1154 layout ();
1155 process_outline ();
1156 unlock ("geometry");
1157 printd (state.sock, "C %d", state.pagecount);
1159 else if (!strncmp ("reinit", p, 6)) {
1160 float rotate;
1161 int proportional;
1163 printd (state.sock, "c");
1164 ret = sscanf (p + 6, " %f %d", &rotate, &proportional);
1165 if (ret != 2) {
1166 errx (1, "bad rotate line `%.*s' ret=%d", len, p, ret);
1168 lock ("reinit");
1169 state.rotate = rotate;
1170 state.proportional = proportional;
1171 state.pagedimcount = 0;
1172 free (state.pagedims);
1173 state.pagedims = NULL;
1174 initpdims ();
1175 layout ();
1176 process_outline ();
1177 if (state.pig) {
1178 freepage (state.pig);
1179 state.pig = NULL;
1181 unlock ("reinit");
1182 printd (state.sock, "C %d", state.pagecount);
1184 else if (!strncmp ("render", p, 6)) {
1185 int pageno, pindex, w, h, ret;
1186 struct page *page;
1188 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
1189 if (ret != 4) {
1190 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
1193 lock ("render");
1194 page = render (pageno, pindex);
1195 unlock ("render");
1197 printd (state.sock, "r %d %d %d %d %d %d %p",
1198 pageno,
1199 state.w,
1200 state.h,
1201 state.rotate,
1202 state.proportional,
1203 w * h * 4,
1204 page);
1206 else if (!strncmp ("interrupt", p, 9)) {
1207 printd (state.sock, "V interrupted");
1209 else {
1210 errx (1, "unknown command %.*s", len, p);
1213 return 0;
1216 static void showsel (struct page *page, int oy)
1218 int ox;
1219 fz_bbox bbox;
1220 fz_text_span *span;
1221 struct mark first, last;
1223 first = page->fmark;
1224 last = page->lmark;
1226 if (!first.span || !last.span) return;
1228 glEnable (GL_BLEND);
1229 glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA);
1230 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1232 ox = -page->pixmap->x + page->pagedim.left;
1233 oy = -page->pixmap->y + oy;
1234 for (span = first.span; span; span = span->next) {
1235 int i, j, k;
1237 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0;
1239 j = 0;
1240 k = span->len - 1;
1242 if (span == page->fmark.span && span == page->lmark.span) {
1243 j = MIN (first.i, last.i);
1244 k = MAX (first.i, last.i);
1246 else if (span == first.span) {
1247 j = first.i;
1249 else if (span == last.span) {
1250 k = last.i;
1253 for (i = j; i <= k; ++i) {
1254 bbox = fz_union_bbox (bbox, span->text[i].bbox);
1256 lprintf ("%d %d %d %d oy=%d ox=%d\n",
1257 bbox.x0,
1258 bbox.y0,
1259 bbox.x1,
1260 bbox.y1,
1261 oy, ox);
1263 glRecti (bbox.x0 + ox, bbox.y0 + oy, bbox.x1 + ox, bbox.y1 + oy);
1265 if (span == last.span) break;
1267 glDisable (GL_BLEND);
1270 static void highlightlinks (struct page *page, int yoff)
1272 pdf_link *link;
1273 int xoff;
1275 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1276 glEnable (GL_LINE_STIPPLE);
1277 glLineStipple (0.5, 0xcccc);
1279 xoff = page->pagedim.left - page->pixmap->x;
1280 yoff -= page->pixmap->y;
1282 glBegin (GL_QUADS);
1283 for (link = page->drawpage->links; link; link = link->next) {
1284 fz_point p1, p2, p3, p4;
1285 fz_matrix ctm = page->pagedim.ctm;
1287 p1.x = link->rect.x0;
1288 p1.y = link->rect.y0;
1290 p2.x = link->rect.x1;
1291 p2.y = link->rect.y0;
1293 p3.x = link->rect.x1;
1294 p3.y = link->rect.y1;
1296 p4.x = link->rect.x0;
1297 p4.y = link->rect.y1;
1299 p1 = fz_transform_point (ctm, p1);
1300 p2 = fz_transform_point (ctm, p2);
1301 p3 = fz_transform_point (ctm, p3);
1302 p4 = fz_transform_point (ctm, p4);
1304 switch (link->kind) {
1305 case PDF_LINK_GOTO: glColor3ub (255, 0, 0); break;
1306 case PDF_LINK_URI: glColor3ub (0, 0, 255); break;
1307 default: glColor3ub (0, 0, 0); break;
1310 glVertex2f (p1.x + xoff, p1.y + yoff);
1311 glVertex2f (p2.x + xoff, p2.y + yoff);
1312 glVertex2f (p3.x + xoff, p3.y + yoff);
1313 glVertex2f (p4.x + xoff, p4.y + yoff);
1315 glEnd ();
1317 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1318 glDisable (GL_LINE_STIPPLE);
1321 static void upload (struct page *page, int slicenum, int blocknum)
1323 double start, end;
1324 struct slice *slice = &page->slices[slicenum];
1325 struct block *block = &slice->blocks[blocknum];
1327 if (block->texindex != -1
1328 && state.texowners[block->texindex].block == block) {
1329 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[block->texindex]);
1331 else {
1332 int subimage = 0;
1333 int index = (state.texindex++ % state.texcount);
1335 if (state.texowners[index].w == block->w) {
1336 if (state.texowners[index].h >= slice->h ) {
1337 subimage = 1;
1339 else {
1340 state.texowners[index].h = slice->h;
1343 else {
1344 state.texowners[index].h = slice->h;
1347 state.texowners[index].w = block->w;
1348 state.texowners[index].block = block;
1349 block->texindex = index;
1351 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[block->texindex]);
1352 glPixelStorei (GL_UNPACK_ROW_LENGTH, page->pixmap->w);
1354 start = now ();
1355 if (0) {
1356 GLenum err = glGetError ();
1357 if (err != GL_NO_ERROR) {
1358 printf ("\033[0;31mERROR1 %d %d %#x\033[0m\n",
1359 block->w, slice->h, err);
1360 abort ();
1363 if (subimage) {
1364 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1368 block->w,
1369 slice->h,
1370 state.texform,
1371 state.texty,
1372 page->pixmap->samples + block->offset
1375 else {
1376 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1378 GL_RGBA8,
1379 block->w,
1380 slice->h,
1382 state.texform,
1383 state.texty,
1384 page->pixmap->samples + block->offset
1387 if (0) {
1388 GLenum err = glGetError ();
1389 if (err != GL_NO_ERROR) {
1390 printf ("\033[0;31mERROR %d %d %#x\033[0m\n",
1391 block->w, slice->h, err);
1392 abort ();
1396 end = now ();
1397 (void) start;
1398 (void) end;
1399 lprintf ("%s[%d] slice=%d,%d(%d,%d) tex=%d,%d offset=%d %f sec\n",
1400 subimage ? "sub" : "img",
1401 page->pageno, slicenum, blocknum,
1402 block->w, slice->h,
1403 block->texindex,
1404 state.texids[block->texindex],
1405 block->offset,
1406 end - start);
1410 CAMLprim value ml_draw (value args_v, value ptr_v)
1412 CAMLparam2 (args_v, ptr_v);
1413 int dispy = Int_val (Field (args_v, 0));
1414 int h = Int_val (Field (args_v, 1));
1415 int py = Int_val (Field (args_v, 2));
1416 int hlinks = Bool_val (Field (args_v, 3));
1417 char *s = String_val (ptr_v);
1418 int ret;
1419 void *ptr;
1420 struct page *page;
1421 int slicenum;
1422 int yoff = dispy - py;
1423 struct slice *slice;
1425 ret = sscanf (s, "%p", &ptr);
1426 if (ret != 1) {
1427 errx (1, "cannot parse pointer `%s'", s);
1429 page = ptr;
1431 ARSERT (h >= 0 && "ml_draw wrong h");
1432 ARSERT (py <= page->pixmap->h && "ml_draw wrong py");
1434 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1436 for (slicenum = 0, slice = page->slices;
1437 slicenum < page->slicecount; ++slicenum, ++slice) {
1438 if (slice->h > py) {
1439 break;
1441 py -= slice->h;
1443 h = MIN (state.h, h);
1445 while (h) {
1446 int th, left, blocknum;
1448 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
1450 th = MIN (h, slice->h - py);
1451 left = page->pagedim.left;
1453 for (blocknum = 0; blocknum < page->blockcount; ++blocknum) {
1454 struct block *block = &slice->blocks[blocknum];
1456 upload (page, slicenum, blocknum);
1457 glBegin (GL_QUADS);
1459 glTexCoord2i (0, py);
1460 glVertex2i (left, dispy);
1462 glTexCoord2i (block->w, py);
1463 glVertex2i (left + block->w, dispy);
1465 glTexCoord2i (block->w, py + th);
1466 glVertex2i (left + block->w, dispy + th);
1468 glTexCoord2i (0, py + th);
1469 glVertex2i (left, dispy + th);
1471 glEnd ();
1472 left += block->w;
1475 h -= th;
1476 py = 0;
1477 dispy += th;
1478 slicenum += 1;
1481 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1483 showsel (page, yoff);
1484 if (hlinks) highlightlinks (page, yoff);
1486 CAMLreturn (Val_unit);
1489 static pdf_link *getlink (struct page *page, int x, int y)
1491 fz_point p;
1492 fz_matrix ctm;
1493 pdf_link *link;
1495 p.x = x + page->pixmap->x;
1496 p.y = y + page->pixmap->y;
1498 ctm = fz_invert_matrix (page->pagedim.ctm);
1499 p = fz_transform_point (ctm, p);
1501 for (link = page->drawpage->links; link; link = link->next) {
1502 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1503 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1504 return link;
1508 return NULL;
1511 static void ensuretext (struct page *page)
1513 if (!page->text) {
1514 fz_error error;
1515 fz_device *tdev;
1517 page->text = fz_new_text_span ();
1518 tdev = fz_new_text_device (page->text);
1519 error = pdf_run_page (state.xref, page->drawpage, tdev,
1520 page->pagedim.ctm);
1521 if (error) die (error);
1522 fz_free_device (tdev);
1526 CAMLprim value ml_whatsunder (value ptr_v, value x_v, value y_v)
1528 CAMLparam3 (ptr_v, x_v, y_v);
1529 CAMLlocal3 (ret_v, tup_v, str_v);
1530 pdf_link *link;
1531 struct page *page;
1532 char *s = String_val (ptr_v);
1534 ret_v = Val_int (0);
1535 if (trylock ("ml_whatsunder")) {
1536 goto done;
1539 page = parse_pointer ("ml_whatsunder", s);
1540 link = getlink (page, Int_val (x_v), Int_val (y_v));
1541 if (link) {
1542 switch (link->kind) {
1543 case PDF_LINK_GOTO:
1545 int pageno;
1546 fz_point p;
1547 fz_obj *obj;
1549 pageno = -1;
1550 p.x = 0;
1551 p.y = 0;
1553 if (fz_is_array (link->dest)) {
1554 obj = fz_array_get (link->dest, 0);
1555 if (fz_is_indirect (obj)) {
1556 pageno = pdf_find_page_number (state.xref, obj);
1558 else if (fz_is_int (obj)) {
1559 pageno = fz_to_int (obj);
1562 if (fz_array_len (link->dest) > 3) {
1563 fz_obj *xo, *yo;
1565 xo = fz_array_get (link->dest, 2);
1566 yo = fz_array_get (link->dest, 3);
1567 if (!fz_is_null (xo) && !fz_is_null (yo)) {
1568 p.x = fz_to_int (xo);
1569 p.y = fz_to_int (yo);
1570 p = fz_transform_point (page->pagedim.ctm, p);
1574 else {
1575 pageno = pdf_find_page_number (state.xref, link->dest);
1577 tup_v = caml_alloc_tuple (2);
1578 ret_v = caml_alloc_small (1, 1);
1579 Field (tup_v, 0) = Val_int (pageno);
1580 Field (tup_v, 1) = Val_int (p.y);
1581 Field (ret_v, 0) = tup_v;
1583 break;
1585 case PDF_LINK_URI:
1586 str_v = caml_copy_string (fz_to_str_buf (link->dest));
1587 ret_v = caml_alloc_small (1, 0);
1588 Field (ret_v, 0) = str_v;
1589 break;
1591 default:
1592 printd (state.sock, "T unhandled link kind %d", link->kind);
1593 break;
1596 else {
1597 int i, x, y;
1598 fz_text_span *span;
1600 ensuretext (page);
1601 x = Int_val (x_v) + page->pixmap->x;
1602 y = Int_val (y_v) + page->pixmap->y;
1604 for (span = page->text; span; span = span->next) {
1605 for (i = 0; i < span->len; ++i) {
1606 fz_bbox *b;
1607 b = &span->text[i].bbox;
1608 if ((x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1)) {
1609 const char *n2 =
1610 span->font && span->font->name
1611 ? span->font->name
1612 : "Span has no font name"
1614 #ifdef FT_FREETYPE_H
1615 FT_FaceRec *face = span->font->ft_face;
1616 if (face && face->family_name) {
1617 char *s;
1618 char *n1 = face->family_name;
1619 size_t l1 = strlen (n1);
1620 size_t l2 = strlen (n2);
1622 if (l1 != l2 || memcmp (n1, n2, l1)) {
1623 s = malloc (l1 + l2 + 2);
1624 if (s) {
1625 memcpy (s, n2, l2);
1626 s[l2] = '=';
1627 memcpy (s + l2 + 1, n1, l1 + 1);
1628 str_v = caml_copy_string (s);
1629 free (s);
1633 if (str_v == 0) {
1634 str_v = caml_copy_string (n2);
1636 #else
1637 str_v = caml_copy_string (n2);
1638 #endif
1639 ret_v = caml_alloc_small (1, 2);
1640 Field (ret_v, 0) = str_v;
1641 goto unlock;
1646 unlock:
1647 unlock ("ml_whatsunder");
1649 done:
1650 CAMLreturn (ret_v);
1653 CAMLprim value ml_seltext (value ptr_v, value rect_v, value oy_v)
1655 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1656 fz_bbox *b;
1657 struct page *page;
1658 fz_text_span *span;
1659 struct mark first, last;
1660 int i, x0, x1, y0, y1, oy, left;
1661 char *s = String_val (ptr_v);
1663 if (trylock ("ml_seltext")) {
1664 goto done;
1667 page = parse_pointer ("ml_seltext", s);
1668 ensuretext (page);
1670 oy = Int_val (oy_v);
1671 x0 = Int_val (Field (rect_v, 0));
1672 y0 = Int_val (Field (rect_v, 1));
1673 x1 = Int_val (Field (rect_v, 2));
1674 y1 = Int_val (Field (rect_v, 3));
1676 left = page->pagedim.left;
1677 if (0) {
1678 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1679 glColor3ub (128, 128, 128);
1680 glRecti (x0, y0, x1, y1);
1681 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1684 x0 += page->pixmap->x - left;
1685 y0 += page->pixmap->y - oy;
1686 x1 += page->pixmap->x - left;
1687 y1 += page->pixmap->y - oy;
1689 first.span = NULL;
1690 last.span = NULL;
1692 last.i = first.i = 0;
1693 first.span = page->text;
1694 for (span = page->text; span; span = span->next) {
1695 for (i = 0; i < span->len; ++i) {
1696 b = &span->text[i].bbox;
1697 int selected = 0;
1699 if (x0 >= b->x0 && x0 <= b->x1 && y0 >= b->y0 && y0 <= b->y1) {
1700 first.i = i;
1701 first.span = span;
1702 selected = 1;
1704 if (x1 >= b->x0 && x1 <= b->x1 && y1 >= b->y0 && y1 <= b->y1) {
1705 last.i = i;
1706 last.span = span;
1707 selected = 1;
1709 if (0 && selected) {
1710 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1711 glColor3ub (128, 128, 128);
1712 glRecti (b->x0+left, b->y0, b->x1+left, b->y1);
1713 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1718 if (y1 < y0 || x1 < x0) {
1719 int swap = 0;
1721 if (first.span == last.span) {
1722 swap = 1;
1724 else {
1725 if (y1 < y0) {
1726 for (span = first.span; span && span != last.span;
1727 span = span->next) {
1728 if (span->eol) {
1729 swap = 1;
1730 break;
1736 if (swap) {
1737 i = first.i;
1738 span = first.span;
1739 first.i = last.i;
1740 first.span = last.span;
1741 last.i = i;
1742 last.span = span;
1746 page->fmark = first;
1747 page->lmark = last;
1749 unlock ("ml_seltext");
1751 done:
1752 CAMLreturn (Val_unit);
1755 static int pipespan (FILE *f, fz_text_span *span, int a, int b)
1757 char buf[4];
1758 int i, len, ret;
1760 for (i = a; i <= b; ++i) {
1761 len = runetochar (buf, &span->text[i].c);
1762 ret = fwrite (buf, len, 1, f);
1764 if (ret != 1) {
1765 printd (state.sock, "T failed to write %d bytes ret=%d: %s",
1766 len, ret, strerror (errno));
1767 return -1;
1770 return 0;
1773 CAMLprim value ml_copysel (value ptr_v)
1775 CAMLparam1 (ptr_v);
1776 FILE *f;
1777 struct page *page;
1778 char *s = String_val (ptr_v);
1780 if (trylock ("ml_copysel")) {
1781 goto done;
1784 if (!*s) {
1785 close:
1786 #ifdef USE_XSEL
1787 if (state.xselpipe) {
1788 int ret = pclose (state.xselpipe);
1789 if (ret) {
1790 printd (state.sock, "T failed to close xsel pipe `%s'",
1791 strerror (errno));
1793 state.xselpipe = NULL;
1795 #else
1796 printf ("========================================\n");
1797 #endif
1799 else {
1800 fz_text_span *span;
1802 page = parse_pointer ("ml_sopysel", s);
1804 if (!page->fmark.span || !page->lmark.span) {
1805 printd (state.sock, "T nothing to copy");
1806 goto unlock;
1809 f = stdout;
1810 #ifdef USE_XSEL
1811 if (!state.xselpipe) {
1812 state.xselpipe = popen ("xsel -i", "w");
1813 if (!state.xselpipe) {
1814 printd (state.sock, "T failed to open xsel pipe `%s'",
1815 strerror (errno));
1817 else {
1818 f = state.xselpipe;
1821 else {
1822 f = state.xselpipe;
1824 #endif
1826 for (span = page->fmark.span;
1827 span && span != page->lmark.span->next;
1828 span = span->next) {
1829 int a = span == page->fmark.span ? page->fmark.i : 0;
1830 int b = span == page->lmark.span ? page->lmark.i : span->len - 1;
1831 if (pipespan (f, span, a, b)) {
1832 goto close;
1834 if (span->eol) {
1835 if (putc ('\n', f) == EOF) {
1836 printd (state.sock, "T failed break line on xsel pipe `%s'",
1837 strerror (errno));
1838 goto close;
1842 page->lmark.span = NULL;
1843 page->fmark.span = NULL;
1846 unlock:
1847 unlock ("ml_copysel");
1849 done:
1850 CAMLreturn (Val_unit);
1853 CAMLprim value ml_getpdimrect (value pagedimno_v)
1855 CAMLparam1 (pagedimno_v);
1856 CAMLlocal1 (ret_v);
1857 int pagedimno = Int_val (pagedimno_v);
1858 fz_rect box;
1860 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1861 if (trylock ("ml_getpdimrect")) {
1862 box = fz_empty_rect;
1864 else {
1865 box = state.pagedims[pagedimno].box;
1866 unlock ("ml_getpdimrect");
1869 Store_double_field (ret_v, 0, box.x0);
1870 Store_double_field (ret_v, 1, box.x1);
1871 Store_double_field (ret_v, 2, box.y0);
1872 Store_double_field (ret_v, 3, box.y1);
1874 CAMLreturn (ret_v);
1877 CAMLprim value ml_zoom_for_height (value winw_v, value winh_v, value dw_v)
1879 CAMLparam3 (winw_v, winh_v, dw_v);
1880 CAMLlocal1 (ret_v);
1881 int i;
1882 double zoom = 1.0;
1883 double maxw = 0.0, maxh = 0.0;
1884 struct pagedim *p;
1885 double winw = Int_val (winw_v);
1886 double winh = Int_val (winh_v);
1887 double dw = Int_val (dw_v);
1888 double pw = 1.0, ph = 1.0, num, den;
1890 if (trylock ("ml_zoom_for_height")) {
1891 goto done;
1894 if (state.proportional) {
1895 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
1896 double x0, x1, w;
1898 x0 = MIN (p->box.x0, p->box.x1);
1899 x1 = MAX (p->box.x0, p->box.x1);
1901 w = x1 - x0;
1902 maxw = MAX (w, maxw);
1906 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
1907 double x0, x1, y0, y1, w, h, scaledh, scale;
1909 x0 = MIN (p->box.x0, p->box.x1);
1910 y0 = MIN (p->box.y0, p->box.y1);
1911 x1 = MAX (p->box.x0, p->box.x1);
1912 y1 = MAX (p->box.y0, p->box.y1);
1914 w = x1 - x0;
1915 h = y1 - y0;
1917 if (state.proportional) {
1918 scale = w / maxw;
1919 scaledh = h * scale;
1921 else {
1922 scale = 1.0;
1923 scaledh = h;
1926 if (scaledh > maxh) {
1927 maxh = scaledh;
1928 ph = scaledh;
1929 pw = w * scale;
1933 num = (winh * pw) + (ph * dw);
1934 den = ph * winw;
1935 zoom = num / den;
1937 unlock ("ml_zoom_for_height");
1938 done:
1939 ret_v = caml_copy_double (zoom);
1940 CAMLreturn (ret_v);
1943 #include "glfont.c"
1945 CAMLprim value ml_draw_string (value pt_v, value x_v, value y_v, value string_v)
1947 CAMLparam4 (pt_v, x_v, y_v, string_v);
1948 int pt = Int_val(pt_v);
1949 int x = Int_val (x_v);
1950 int y = Int_val (y_v);
1951 double w;
1953 w = draw_string (state.face, pt, x, y, String_val (string_v));
1954 CAMLreturn (caml_copy_double (w));
1957 CAMLprim value ml_measure_string (value pt_v, value string_v)
1959 CAMLparam2 (pt_v, string_v);
1960 CAMLlocal1 (ret_v);
1961 int pt = Int_val (pt_v);
1962 double w;
1964 w = measure_string (state.face, pt, String_val (string_v));
1965 CAMLreturn (caml_copy_double (w));
1968 CAMLprim value ml_init (value sock_v, value params_v)
1970 CAMLparam2 (sock_v, params_v);
1971 #ifndef _WIN32
1972 int ret;
1973 #endif
1974 char *fontpath;
1976 state.rotate = Int_val (Field (params_v, 0));
1977 state.proportional = Bool_val (Field (params_v, 1));
1978 state.texcount = Int_val (Field (params_v, 2));
1979 state.sliceheight = Int_val (Field (params_v, 3));
1980 state.blockwidth = Int_val (Field (params_v, 4));
1981 fontpath = String_val (Field (params_v, 5));
1982 state.texform = GL_RGBA;
1983 state.texty = GL_UNSIGNED_BYTE;
1985 if (*fontpath) {
1986 state.face = load_font (fontpath);
1988 else {
1989 unsigned int len;
1990 void *base = pdf_find_substitute_font (0, 0, 0, 0, &len);
1992 state.face = load_builtin_font (base, len);
1994 if (!state.face) _exit (1);
1996 fz_accelerate ();
1997 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1998 if (!state.texids) {
1999 err (1, "calloc texids " FMT_s,
2000 state.texcount * sizeof (*state.texids));
2003 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
2004 if (!state.texowners) {
2005 err (1, "calloc texowners " FMT_s,
2006 state.texcount * sizeof (*state.texowners));
2009 glGenTextures (state.texcount, state.texids);
2011 #ifdef _WIN32
2012 state.sock = Socket_val (sock_v);
2013 #else
2014 state.sock = Int_val (sock_v);
2015 #endif
2017 #ifdef _WIN32
2018 InitializeCriticalSection (&critsec);
2019 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
2020 if (state.thread == INVALID_HANDLE_VALUE) {
2021 errx (1, "CreateThread failed: %lx", GetLastError ());
2023 #else
2024 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
2025 if (ret) {
2026 errx (1, "pthread_create: %s", strerror (ret));
2028 #endif
2030 CAMLreturn (Val_unit);