Fix handling of outline links where destination is not an array
[llpp.git] / link.c
bloba839c47fcac63065f346e7482b128e5d617c8361
1 /* lot's of code c&p-ed directly from mupdf */
2 #ifdef _WIN32
3 #define WIN32_LEAN_AND_MEAN
4 #include <windows.h>
5 #include <winsock2.h>
6 #define fionread_arg long
7 #define ssize_t int
8 #define FMT_ss "%d"
9 #ifdef _WIN64
10 #define FMT_s "%i64u"
11 #else
12 #define FMT_s "%u"
13 #endif
14 #pragma warning (disable:4244)
15 #pragma warning (disable:4996)
16 #pragma warning (disable:4995)
17 #endif
19 #ifdef _MSC_VER
20 #include <errno.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 static void __declspec (noreturn) err (int exitcode, const char *fmt, ...)
26 va_list ap;
27 int errcode;
29 errcode = errno;
30 va_start (ap, fmt);
31 vfprintf (stderr, fmt, ap);
32 va_end (ap);
33 fprintf (stderr, ": %s\n", strerror (errno));
34 exit (exitcode);
36 static void __declspec (noreturn) errx (int exitcode, const char *fmt, ...)
38 va_list ap;
40 va_start (ap, fmt);
41 vfprintf (stderr, fmt, ap);
42 va_end (ap);
43 fputc ('\n', stderr);
44 exit (exitcode);
46 static void __declspec (noreturn) sockerr (int exitcode, const char *fmt, ...)
48 va_list ap;
50 va_start (ap, fmt);
51 vfprintf (stderr, fmt, ap);
52 va_end (ap);
53 fprintf (stderr, ": wsaerror 0x%x\n", WSAGetLastError ());
54 exit (exitcode);
56 #else
57 #define FMT_ss "%zd"
58 #define FMT_s "%zu"
59 #define fionread_arg int
60 #define ioctlsocket ioctl
61 #define _GNU_SOURCE
62 #include <err.h>
63 #define sockerr err
64 #endif
65 #include <regex.h>
66 #include <errno.h>
67 #include <ctype.h>
68 #include <stdio.h>
69 #include <stdarg.h>
70 #include <stdlib.h>
71 #include <string.h>
72 #include <limits.h>
73 #ifndef _WIN32
74 #include <pthread.h>
75 #include <sys/time.h>
76 #include <sys/types.h>
77 #include <sys/socket.h>
78 #include <sys/ioctl.h>
79 #endif
81 #ifdef __APPLE__
82 #include <OpenGL/gl.h>
83 #else
84 #include <GL/gl.h>
85 #endif
87 #ifndef GL_TEXTURE_RECTANGLE_ARB
88 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
89 #endif
91 #include <caml/fail.h>
92 #include <caml/alloc.h>
93 #include <caml/memory.h>
94 #include <caml/unixsupport.h>
96 #include <fitz.h>
97 #include <mupdf.h>
99 #if 0
100 #define lprintf printf
101 #else
102 #define lprintf(...)
103 #endif
105 #ifdef FT_FREETYPE_H
106 #include FT_FREETYPE_H
107 #endif
109 #define ARSERT(cond) for (;;) { \
110 if (!(cond)) { \
111 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
113 break; \
116 struct slice {
117 int texindex;
118 int w, h;
121 struct pagedim {
122 int pageno;
123 int rotate;
124 fz_rect box;
125 fz_bbox bbox;
126 fz_matrix ctm, ctm1;
129 struct page {
130 int pageno;
131 int slicecount;
132 fz_textspan *text;
133 fz_pixmap *pixmap;
134 pdf_page *drawpage;
135 struct pagedim pagedim;
136 struct page *prev;
137 struct mark {
138 int i;
139 fz_textspan *span;
140 } fmark, lmark;
141 struct slice slices[];
144 #if !defined _WIN32 && !defined __APPLE__
145 #define USE_XSEL
146 #endif
148 struct {
149 int sock;
150 int sliceheight;
151 struct page *pages, *pig;
152 struct pagedim *pagedims;
153 int pagecount;
154 int pagedimcount;
155 pdf_xref *xref;
156 fz_glyphcache *cache;
157 int w, h;
159 int texindex;
160 int texcount;
161 GLuint *texids;
163 GLenum texform;
164 GLenum texty;
166 struct {
167 int w, h;
168 struct slice *slice;
169 } *texowners;
171 int rotate;
172 int needoutline;
174 #ifdef _WIN32
175 HANDLE thread;
176 #else
177 pthread_t thread;
178 #endif
179 FILE *xselpipe;
180 } state;
182 #ifdef _WIN32
183 static CRITICAL_SECTION critsec;
185 static void lock (void *unused)
187 (void) unused;
188 EnterCriticalSection (&critsec);
191 static void unlock (void *unused)
193 (void) unused;
194 LeaveCriticalSection (&critsec);
197 static int trylock (void *unused)
199 return TryEnterCriticalSection (&critsec) == 0;
201 #else
202 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
204 static void lock (const char *cap)
206 int ret = pthread_mutex_lock (&mutex);
207 if (ret) {
208 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
212 static void unlock (const char *cap)
214 int ret = pthread_mutex_unlock (&mutex);
215 if (ret) {
216 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
220 static int trylock (const char *cap)
222 int ret = pthread_mutex_trylock (&mutex);
224 if (ret && ret != EBUSY) {
225 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
227 return ret == EBUSY;
229 #endif
231 static void *parse_pointer (const char *cap, const char *s)
233 int ret;
234 void *ptr;
236 ret = sscanf (s, "%p", &ptr);
237 if (ret != 1) {
238 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
240 return ptr;
243 static int hasdata (int sock)
245 int ret;
246 fionread_arg avail;
247 ret = ioctlsocket (sock, FIONREAD, &avail);
248 if (ret) err (1, "hasdata: FIONREAD error ret=%d", ret);
249 return avail > 0;
252 static double now (void)
254 struct timeval tv;
256 if (gettimeofday (&tv, NULL)) {
257 err (1, "gettimeofday");
259 return tv.tv_sec + tv.tv_usec*1e-6;
262 static void readdata (int fd, char *p, int size)
264 ssize_t n;
266 n = recv (fd, p, size, 0);
267 if (n - size) {
268 if (!n) errx (1, "EOF while reading");
269 sockerr (1, "recv (req %d, ret " FMT_ss ")", size, n);
273 static void writedata (int fd, char *p, int size)
275 char buf[4];
276 ssize_t n;
278 buf[0] = (size >> 24) & 0xff;
279 buf[1] = (size >> 16) & 0xff;
280 buf[2] = (size >> 8) & 0xff;
281 buf[3] = (size >> 0) & 0xff;
283 n = send (fd, buf, 4, 0);
284 if (n != 4) {
285 if (!n) errx (1, "EOF while writing length");
286 sockerr (1, "send " FMT_ss, n);
289 n = send (fd, p, size, 0);
290 if (n - size) {
291 if (!n) errx (1, "EOF while writing data");
292 sockerr (1, "send (req %d, ret " FMT_ss ")", size, n);
296 static void
297 #ifdef __GNUC__
298 __attribute__ ((format (printf, 2, 3)))
299 #endif
300 printd (int fd, const char *fmt, ...)
302 int len;
303 va_list ap;
304 char buf[200];
306 va_start (ap, fmt);
307 len = vsnprintf (buf, sizeof (buf), fmt, ap);
308 va_end (ap);
309 writedata (fd, buf, len);
312 static void die (fz_error error)
314 fz_catch (error, "aborting");
315 if (state.xref)
316 pdf_freexref (state.xref);
317 exit (1);
320 static void openxref (char *filename)
322 int fd;
323 fz_error error;
324 fz_stream *file;
326 fd = open (filename, O_BINARY | O_RDONLY, 0666);
327 if (fd < 0)
328 die (fz_throw ("cannot open file '%s'", filename));
330 file = fz_openfile (fd);
331 error = pdf_openxrefwithstream (&state.xref, file, NULL);
332 if (error)
333 die (fz_rethrow(error, "cannot open document '%s'", filename));
334 fz_close (file);
336 if (pdf_needspassword (state.xref)) {
337 die (fz_throw ("password protected"));
340 error = pdf_loadpagetree (state.xref);
341 if (error) {
342 die (fz_throw ("cannot load page tree"));
345 state.pagecount = pdf_getpagecount (state.xref);
348 static int readlen (int fd)
350 ssize_t n;
351 char p[4];
353 n = recv (fd, p, 4, 0);
354 if (n != 4) {
355 if (!n) errx (1, "EOF while reading length");
356 sockerr (1, "recv " FMT_ss, n);
359 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
362 static void unlinkpage (struct page *page)
364 int i;
365 struct page *p;
367 for (p = state.pages; p; p = p->prev) {
368 if (p->prev == page) {
369 p->prev = page->prev;
370 break;
373 for (i = 0; i < page->slicecount; ++i) {
374 struct slice *s = &page->slices[i];
375 if (s->texindex != -1) {
376 if (state.texowners[s->texindex].slice == s) {
377 state.texowners[s->texindex].slice = NULL;
378 ARSERT (state.texowners[s->texindex].w == s->w);
379 ARSERT (state.texowners[s->texindex].h >= s->h);
385 static void freepage (struct page *page)
387 fz_droppixmap (page->pixmap);
389 unlinkpage (page);
391 if (page->text) {
392 fz_freetextspan (page->text);
394 if (page->drawpage) {
395 pdf_freepage (page->drawpage);
398 free (page);
401 static void subdivide (struct page *p)
403 int i;
404 int h = p->pixmap->h;
405 int th = MIN (h, state.sliceheight);
407 for (i = 0; i < p->slicecount; ++i) {
408 struct slice *s = &p->slices[i];
409 s->texindex = -1;
410 s->h = MIN (th, h);
411 s->w = p->pixmap->w;
412 h -= th;
416 static int compatpdims (struct pagedim *p1, struct pagedim *p2)
418 return p1->rotate == p2->rotate
419 && !memcmp (&p1->bbox, &p2->bbox, sizeof (p1->bbox))
420 && !memcmp (&p1->ctm, &p2->ctm, sizeof (p1->ctm));
423 #ifdef __ALTIVEC__
424 #include <altivec.h>
426 static int cacheline32bytes;
428 static void __attribute__ ((constructor)) clcheck (void)
430 char **envp = environ;
431 unsigned long *auxv;
433 while (*envp++);
435 for (auxv = (unsigned long *) envp; *auxv != 0; auxv += 2) {
436 if (*auxv == 19) {
437 cacheline32bytes = auxv[1] == 32;
438 return;
443 static void __attribute__ ((optimize ("O"))) clearpixmap (fz_pixmap *pixmap)
445 if (cacheline32bytes) {
446 intptr_t a1, a2, diff;
447 size_t sizea, i, size = pixmap->w * pixmap->h * pixmap->n;
448 vector unsigned char v = vec_splat_u8 (-1);
449 vector unsigned char *p;
451 a1 = a2 = (intptr_t) pixmap->samples;
452 a2 = (a1 + 31) & ~31;
453 diff = a2 - a1;
454 sizea = size - diff;
455 p = (void *) a2;
457 while (a1 != a2) *(char *) a1++ = 0xff;
458 for (i = 0; i < (sizea & ~31); i += 32) {
459 __asm volatile ("dcbz %0, %1"::"b"(a2),"r"(i));
460 vec_st (v, i, p);
461 vec_st (v, i + 16, p);
463 while (i < sizea) *((char *) a1 + i++) = 0xff;
465 else fz_clearpixmap (pixmap, 0xff);
467 #else
468 #define clearpixmap(p) fz_clearpixmap (p, 0xff)
469 #endif
471 static void *render (int pageno, int pindex)
473 fz_error error;
474 int slicecount;
475 fz_obj *pageobj;
476 struct page *page = NULL;
477 double start, end;
478 pdf_page *drawpage;
479 fz_device *idev;
480 struct pagedim *pagedim;
482 start = now ();
483 printd (state.sock, "V rendering %d", pageno);
485 pagedim = &state.pagedims[pindex];
486 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
487 + state.sliceheight - 1) / state.sliceheight;
488 slicecount += slicecount == 0;
490 if (state.pig) {
491 if (compatpdims (&state.pig->pagedim, pagedim)) {
492 page = state.pig;
493 if (page->text) {
494 fz_freetextspan (page->text);
495 page->text = NULL;
497 if (page->drawpage) {
498 pdf_freepage (page->drawpage);
499 page->drawpage = NULL;
502 else {
503 freepage (state.pig);
506 if (!page) {
507 page = calloc (sizeof (*page)
508 + (slicecount * sizeof (struct slice)), 1);
509 if (!page) {
510 err (1, "calloc page %d\n", pageno);
512 page->pixmap = fz_newpixmapwithrect (fz_devicergb, pagedim->bbox);
515 page->slicecount = slicecount;
516 page->prev = state.pages;
517 state.pages = page;
519 pageobj = pdf_getpageobject (state.xref, pageno);
520 if (!pageobj)
521 die (fz_throw ("cannot retrieve info from page %d", pageno));
523 error = pdf_loadpage (&drawpage, state.xref, pageobj);
524 if (error)
525 die (error);
527 clearpixmap (page->pixmap);
529 idev = fz_newdrawdevice (state.cache, page->pixmap);
530 if (!idev)
531 die (fz_throw ("fz_newdrawdevice failed"));
532 error = pdf_runpage (state.xref, drawpage, idev, pagedim->ctm);
533 if (error)
534 die (fz_rethrow (error, "pdf_runpage failed"));
535 fz_freedevice (idev);
537 page->drawpage = drawpage;
538 page->pagedim = *pagedim;
539 page->pageno = pageno;
540 subdivide (page);
541 end = now ();
543 pdf_agestore(state.xref->store, 3);
545 printd (state.sock, "V rendering %d took %f sec", pageno, end - start);
546 state.pig = NULL;
547 return page;
550 static void initpdims (void)
552 int pageno;
553 double start, end;
555 start = now ();
556 for (pageno = 0; pageno < state.pagecount; ++pageno) {
557 int rotate;
558 fz_rect box;
559 struct pagedim *p;
560 fz_obj *obj, *pageobj;
562 pageobj = pdf_getpageobject (state.xref, pageno + 1);
564 obj = fz_dictgets (pageobj, "CropBox");
565 if (!fz_isarray (obj)) {
566 obj = fz_dictgets (pageobj, "MediaBox");
567 if (!fz_isarray (obj)) {
568 die (fz_throw ("cannot find page bounds %d (%d Rd)",
569 fz_tonum (pageobj), fz_togen (pageobj)));
572 box = pdf_torect (obj);
574 obj = fz_dictgets (pageobj, "Rotate");
575 if (fz_isint (obj)) {
576 rotate = fz_toint (obj);
578 else {
579 rotate = 0;
581 rotate += state.rotate;
583 p = &state.pagedims[state.pagedimcount - 1];
584 if ((state.pagedimcount == 0)
585 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
586 size_t size;
588 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
589 state.pagedims = realloc (state.pagedims, size);
590 if (!state.pagedims) {
591 err (1, "realloc pagedims to " FMT_s " (%d elems)",
592 size, state.pagedimcount + 1);
594 p = &state.pagedims[state.pagedimcount++];
595 p->rotate = rotate;
596 p->box = box;
597 p->pageno = pageno;
600 end = now ();
601 printd (state.sock, "T Processed %d pages in %f seconds",
602 state.pagecount, end - start);
605 static void layout (void)
607 int pindex;
608 fz_matrix ctm;
609 fz_rect box, box2;
610 double zoom, w;
611 struct pagedim *p = state.pagedims;
613 pindex = 0;
614 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
615 box.x0 = MIN (p->box.x0, p->box.x1);
616 box.y0 = MIN (p->box.y0, p->box.y1);
617 box.x1 = MAX (p->box.x0, p->box.x1);
618 box.y1 = MAX (p->box.y0, p->box.y1);
620 ctm = fz_identity;
621 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
622 ctm = fz_concat (ctm, fz_rotate (p->rotate));
623 box2 = fz_transformrect (ctm, box);
624 w = box2.x1 - box2.x0;
626 zoom = (state.w / w);
627 ctm = fz_identity;
628 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
629 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
630 memcpy (&p->ctm1, &ctm, sizeof (ctm));
631 ctm = fz_concat (ctm, fz_rotate (p->rotate));
632 p->bbox = fz_roundrect (fz_transformrect (ctm, box));
633 memcpy (&p->ctm, &ctm, sizeof (ctm));
636 while (p-- != state.pagedims) {
637 printd (state.sock, "l %d %d %d",
638 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0);
642 static void recurse_outline (pdf_outline *outline, int level)
644 while (outline) {
645 int i;
646 fz_obj *obj;
647 int pageno = -1;
648 int top = 0, h = 0;
650 if (!outline->link) goto next;
652 obj = outline->link->dest;
653 if (fz_isindirect (obj)) {
654 obj = fz_resolveindirect (obj);
656 if (fz_isarray (obj)) {
657 fz_obj *obj2;
659 obj2 = fz_arrayget (obj, 0);
660 if (fz_isint (obj2)) {
661 pageno = fz_toint (obj2);
663 else {
664 pageno = pdf_findpageobject (state.xref, obj2) - 1;
667 if (fz_arraylen (obj) > 3) {
668 fz_point p;
669 fz_obj *xo, *yo;
671 xo = fz_arrayget (obj, 2);
672 yo = fz_arrayget (obj, 3);
673 if (!fz_isnull (xo) && !fz_isnull (yo)) {
674 struct pagedim *pagedim = state.pagedims;
676 for (i = 0; i < state.pagedimcount; ++i) {
677 if (state.pagedims[i].pageno > pageno)
678 break;
679 pagedim = &state.pagedims[i];
681 p.x = fz_toint (xo);
682 p.y = fz_toint (yo);
683 p = fz_transformpoint (pagedim->ctm, p);
684 h = pagedim->bbox.y1 - pagedim->bbox.y0;
685 top = p.y;
689 else {
690 pageno = pdf_findpageobject (state.xref, outline->link->dest) - 1;
693 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
694 printd (state.sock, "o %d %d %d %d %s",
695 level, pageno, top, h, outline->title);
696 next:
697 if (outline->child) {
698 recurse_outline (outline->child, level + 1);
700 outline = outline->next;
704 static void process_outline (void)
706 pdf_outline *outline;
708 if (!state.needoutline) return;
710 state.needoutline = 0;
711 outline = pdf_loadoutline (state.xref);
712 if (outline) {
713 recurse_outline (outline, 0);
714 pdf_freeoutline (outline);
718 static int comparespans (const void *l, const void *r)
720 fz_textspan const *const*ls = l;
721 fz_textspan const *const*rs = r;
722 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
725 /* wishful thinking function */
726 static void search (regex_t *re, int pageno, int y, int forward)
728 int i, j;
729 int ret;
730 char *p;
731 char buf[256];
732 fz_matrix ctm;
733 fz_error error;
734 fz_obj *pageobj;
735 fz_device *tdev;
736 pdf_page *drawpage;
737 fz_textspan *text, *span, **pspan;
738 struct pagedim *pdim, *pdimprev;
739 int stop = 0;
740 int niters = 0;
741 int nspans;
742 double start, end;
744 start = now ();
745 while (pageno >= 0 && pageno < state.pagecount && !stop) {
746 if (niters++ == 5) {
747 pdf_agestore(state.xref->store, 3);
748 niters = 0;
749 if (hasdata (state.sock)) {
750 printd (state.sock, "T attention requested aborting search at %d",
751 pageno);
752 stop = 1;
754 else {
755 printd (state.sock, "T searching in page %d", pageno);
758 pdimprev = NULL;
759 for (i = 0; i < state.pagedimcount; ++i) {
760 pdim = &state.pagedims[i];
761 if (pdim->pageno == pageno) {
762 goto found;
764 if (pdim->pageno > pageno) {
765 pdim = pdimprev;
766 goto found;
768 pdimprev = pdim;
770 pdim = pdimprev;
771 found:
773 pageobj = pdf_getpageobject (state.xref, pageno + 1);
774 if (!pageobj)
775 die (fz_throw ("cannot retrieve info from page %d", pageno));
777 error = pdf_loadpage (&drawpage, state.xref, pageobj);
778 if (error)
779 die (error);
781 ctm = fz_rotate (pdim->rotate);
783 text = fz_newtextspan ();
784 tdev = fz_newtextdevice (text);
785 error = pdf_runpage (state.xref, drawpage, tdev, pdim->ctm1);
786 if (error) die (error);
787 fz_freedevice (tdev);
789 nspans = 0;
790 for (span = text; span; span = span->next) {
791 nspans++;
793 pspan = malloc (sizeof (void *) * nspans);
794 if (!pspan) {
795 err (1, "malloc span pointers " FMT_s, sizeof (void *) * nspans);
797 for (i = 0, span = text; span; span = span->next, ++i) {
798 pspan[i] = span;
800 qsort (pspan, nspans, sizeof (fz_textspan *), comparespans);
802 j = forward ? 0 : nspans - 1;
803 while (nspans--) {
804 regmatch_t rm;
806 span = pspan[j];
807 j += forward ? 1 : -1;
808 p = buf;
809 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
810 if (forward) {
811 if (span->text[i].bbox.y0 < y + 1) {
812 continue;
815 else {
816 if (span->text[i].bbox.y0 > y - 1) {
817 continue;
820 if (span->text[i].c < 256) {
821 *p++ = span->text[i].c;
823 else {
824 *p++ = '?';
827 if (p == buf) {
828 continue;
830 *p++ = 0;
832 ret = regexec (re, buf, 1, &rm, 0);
833 if (ret) {
834 if (ret != REG_NOMATCH) {
835 size_t size;
836 char errbuf[80];
837 size = regerror (ret, re, errbuf, sizeof (errbuf));
838 printd (state.sock,
839 "T regexec error `%.*s'",
840 (int) size, errbuf);
841 fz_freetextspan (text);
842 pdf_freepage (drawpage);
843 free (pspan);
844 return;
847 else {
848 int xoff, yoff;
849 fz_bbox *sb, *eb;
850 fz_point p1, p2, p3, p4;
852 xoff = -pdim->bbox.x0;
853 yoff = -pdim->bbox.y0;
855 sb = &span->text[rm.rm_so].bbox;
856 eb = &span->text[rm.rm_eo - 1].bbox;
858 p1.x = sb->x0;
859 p1.y = sb->y0;
860 p2.x = eb->x1;
861 p2.y = sb->y0;
862 p3.x = eb->x1;
863 p3.y = eb->y1;
864 p4.x = sb->x0;
865 p4.y = eb->y1;
867 p1 = fz_transformpoint (ctm, p1);
868 p2 = fz_transformpoint (ctm, p2);
869 p3 = fz_transformpoint (ctm, p3);
870 p4 = fz_transformpoint (ctm, p4);
872 if (!stop) {
873 printd (state.sock, "F %d %d %f %f %f %f %f %f %f %f",
874 pageno, 1,
875 p1.x + xoff, p1.y + yoff,
876 p2.x + xoff, p2.y + yoff,
877 p3.x + xoff, p3.y + yoff,
878 p4.x + xoff, p4.y + yoff);
880 printd (state.sock, "T found at %d `%.*s' in %f sec",
881 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
882 now () - start);
884 else {
885 printd (state.sock, "R %d %d %f %f %f %f %f %f %f %f",
886 pageno, 2,
887 p1.x + xoff, p1.y + yoff,
888 p2.x + xoff, p2.y + yoff,
889 p3.x + xoff, p3.y + yoff,
890 p4.x + xoff, p4.y + yoff);
892 stop = 1;
895 if (forward) {
896 pageno += 1;
897 y = 0;
899 else {
900 pageno -= 1;
901 y = INT_MAX;
903 fz_freetextspan (text);
904 pdf_freepage (drawpage);
905 free (pspan);
907 end = now ();
908 if (!stop) {
909 printd (state.sock, "T no matches %f sec", end - start);
911 printd (state.sock, "D");
914 static
915 #ifdef _WIN32
916 DWORD _stdcall
917 #else
918 void *
919 #endif
920 mainloop (void *unused)
922 char *p = NULL;
923 int len, ret, oldlen = 0;
925 for (;;) {
926 len = readlen (state.sock);
927 if (len == 0) {
928 errx (1, "readlen returned 0");
931 if (oldlen < len + 1) {
932 p = realloc (p, len + 1);
933 if (!p) {
934 err (1, "realloc %d failed", len + 1);
936 oldlen = len + 1;
938 readdata (state.sock, p, len);
939 p[len] = 0;
941 if (!strncmp ("open", p, 4)) {
942 fz_obj *obj;
943 char *filename = p + 5;
945 openxref (filename);
946 initpdims ();
948 obj = fz_dictgets (state.xref->trailer, "Info");
949 if (obj) {
950 obj = fz_dictgets (obj, "Title");
951 if (obj) {
952 printd (state.sock, "t %s", pdf_toutf8 (obj));
955 state.needoutline = 1;
957 else if (!strncmp ("free", p, 4)) {
958 void *ptr;
960 ret = sscanf (p + 4, " %p", &ptr);
961 if (ret != 1) {
962 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
964 unlinkpage (ptr);
965 state.pig = ptr;
967 else if (!strncmp ("search", p, 6)) {
968 int icase, pageno, y, ret, len2, forward;
969 char *pattern;
970 regex_t re;
972 ret = sscanf (p + 6, " %d %d %d %d,%n",
973 &icase, &pageno, &y, &forward, &len2);
974 if (ret != 4) {
975 errx (1, "malformed search `%s' ret=%d", p, ret);
978 pattern = p + 6 + len2;
979 ret = regcomp (&re, pattern,
980 REG_EXTENDED | (icase ? REG_ICASE : 0));
981 if (ret) {
982 char errbuf[80];
983 size_t size;
985 size = regerror (ret, &re, errbuf, sizeof (errbuf));
986 printd (state.sock, "T regcomp failed `%.*s'",
987 (int) size, errbuf);
989 else {
990 search (&re, pageno, y, forward);
991 regfree (&re);
994 else if (!strncmp ("geometry", p, 8)) {
995 int w, h;
997 printd (state.sock, "c");
998 ret = sscanf (p + 8, " %d %d", &w, &h);
999 if (ret != 2) {
1000 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
1003 lock ("geometry");
1004 state.h = h;
1005 if (w != state.w) {
1006 int i;
1007 state.w = w;
1008 for (i = 0; i < state.texcount; ++i) {
1009 state.texowners[i].slice = NULL;
1012 layout ();
1013 process_outline ();
1014 unlock ("geometry");
1015 printd (state.sock, "C %d", state.pagecount);
1017 else if (!strncmp ("rotate", p, 6)) {
1018 float rotate;
1020 printd (state.sock, "c");
1021 ret = sscanf (p + 6, " %f", &rotate);
1022 if (ret != 1) {
1023 errx (1, "bad rotate line `%.*s' ret=%d", len, p, ret);
1025 lock ("rotate");
1026 state.rotate = rotate;
1027 state.pagedimcount = 0;
1028 free (state.pagedims);
1029 state.pagedims = NULL;
1030 initpdims ();
1031 layout ();
1032 process_outline ();
1033 if (state.pig) {
1034 freepage (state.pig);
1035 state.pig = NULL;
1037 unlock ("rotate");
1038 printd (state.sock, "C %d", state.pagecount);
1040 else if (!strncmp ("render", p, 6)) {
1041 int pageno, pindex, w, h, ret;
1042 struct page *page;
1044 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
1045 if (ret != 4) {
1046 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
1049 lock ("render");
1050 page = render (pageno, pindex);
1051 unlock ("render");
1053 printd (state.sock, "r %d %d %d %d %p",
1054 pageno,
1055 state.w,
1056 state.h,
1057 state.rotate,
1058 page);
1060 else if (!strncmp ("interrupt", p, 9)) {
1061 printd (state.sock, "V interrupted");
1063 else {
1064 errx (1, "unknown command %.*s", len, p);
1067 return 0;
1070 static void showsel (struct page *page, int oy)
1072 int ox;
1073 fz_bbox bbox;
1074 fz_textspan *span;
1075 struct mark first, last;
1077 first = page->fmark;
1078 last = page->lmark;
1080 if (!first.span || !last.span) return;
1082 glEnable (GL_BLEND);
1083 glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA);
1084 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1086 ox = -page->pixmap->x;
1087 oy = -page->pixmap->y + oy;
1088 for (span = first.span; span; span = span->next) {
1089 int i, j, k;
1091 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0;
1093 j = 0;
1094 k = span->len - 1;
1096 if (span == page->fmark.span && span == page->lmark.span) {
1097 j = MIN (first.i, last.i);
1098 k = MAX (first.i, last.i);
1100 else if (span == first.span) {
1101 j = first.i;
1103 else if (span == last.span) {
1104 k = last.i;
1107 for (i = j; i <= k; ++i) {
1108 bbox = fz_unionbbox (bbox, span->text[i].bbox);
1110 lprintf ("%d %d %d %d oy=%d ox=%d\n",
1111 bbox.x0,
1112 bbox.y0,
1113 bbox.x1,
1114 bbox.y1,
1115 oy, ox);
1117 glRecti (bbox.x0 + ox, bbox.y0 + oy, bbox.x1 + ox, bbox.y1 + oy);
1119 if (span == last.span) break;
1121 glDisable (GL_BLEND);
1124 static void highlightlinks (struct page *page, int yoff)
1126 pdf_link *link;
1127 int xoff;
1129 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1130 glEnable (GL_LINE_STIPPLE);
1131 glLineStipple (0.5, 0xcccc);
1133 xoff = -page->pixmap->x;
1134 yoff -= page->pixmap->y;
1136 glBegin (GL_QUADS);
1137 for (link = page->drawpage->links; link; link = link->next) {
1138 fz_point p1, p2, p3, p4;
1139 fz_matrix ctm = page->pagedim.ctm;
1141 p1.x = link->rect.x0;
1142 p1.y = link->rect.y0;
1144 p2.x = link->rect.x1;
1145 p2.y = link->rect.y0;
1147 p3.x = link->rect.x1;
1148 p3.y = link->rect.y1;
1150 p4.x = link->rect.x0;
1151 p4.y = link->rect.y1;
1153 p1 = fz_transformpoint (ctm, p1);
1154 p2 = fz_transformpoint (ctm, p2);
1155 p3 = fz_transformpoint (ctm, p3);
1156 p4 = fz_transformpoint (ctm, p4);
1158 switch (link->kind) {
1159 case PDF_LGOTO: glColor3ub (255, 0, 0); break;
1160 case PDF_LURI: glColor3ub (0, 0, 255); break;
1161 default: glColor3ub (0, 0, 0); break;
1164 glVertex2f (p1.x + xoff, p1.y + yoff);
1165 glVertex2f (p2.x + xoff, p2.y + yoff);
1166 glVertex2f (p3.x + xoff, p3.y + yoff);
1167 glVertex2f (p4.x + xoff, p4.y + yoff);
1169 glEnd ();
1171 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1172 glDisable (GL_LINE_STIPPLE);
1175 static void upload2 (struct page *page, int slicenum, const char *cap)
1177 int i;
1178 int w, h;
1179 double start, end;
1180 struct slice *slice = &page->slices[slicenum];
1182 w = page->pixmap->w;
1183 h = page->pixmap->h;
1185 ARSERT (w == slice->w);
1186 if (slice->texindex != -1
1187 && state.texowners[slice->texindex].slice == slice) {
1188 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1190 else {
1191 int subimage = 0;
1192 int index = (state.texindex++ % state.texcount);
1193 size_t offset = 0;
1195 for (i = 0; i < slicenum; ++i) {
1196 offset += w * page->slices[i].h * 4;
1199 if (state.texowners[index].w == slice->w) {
1200 if (state.texowners[index].h >= slice->h ) {
1201 subimage = 1;
1203 else {
1204 state.texowners[index].h = slice->h;
1207 else {
1208 state.texowners[index].h = slice->h;
1211 state.texowners[index].slice = slice;
1212 state.texowners[index].w = slice->w;
1213 slice->texindex = index;
1215 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1216 start = now ();
1217 if (subimage) {
1219 GLenum err = glGetError ();
1220 if (err != GL_NO_ERROR) {
1221 printf ("\033[0;31mERROR1 %d %d %#x\033[0m\n", w, slice->h, err);
1222 abort ();
1225 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1230 slice->h,
1231 state.texform,
1232 state.texty,
1233 page->pixmap->samples + offset
1236 GLenum err = glGetError ();
1237 if (err != GL_NO_ERROR) {
1238 printf ("\033[0;31mERROR %d %d %#x\033[0m\n", w, slice->h, err);
1239 abort ();
1243 else {
1244 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1246 GL_RGBA8,
1248 slice->h,
1250 state.texform,
1251 state.texty,
1252 page->pixmap->samples + offset
1256 end = now ();
1257 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
1258 subimage ? "sub" : "img",
1259 page->pageno, slicenum,
1260 slice->w, slice->h,
1261 state.texids[slice->texindex],
1262 end - start);
1266 CAMLprim value ml_draw (value args_v, value ptr_v)
1268 CAMLparam2 (args_v, ptr_v);
1269 int dispy = Int_val (Field (args_v, 0));
1270 int w = Int_val (Field (args_v, 1));
1271 int h = Int_val (Field (args_v, 2));
1272 int py = Int_val (Field (args_v, 3));
1273 int hlinks = Bool_val (Field (args_v, 4));
1274 char *s = String_val (ptr_v);
1275 int ret;
1276 void *ptr;
1277 struct page *page;
1278 int slicenum = 0;
1279 int yoff = dispy - py;
1281 ret = sscanf (s, "%p", &ptr);
1282 if (ret != 1) {
1283 errx (1, "cannot parse pointer `%s'", s);
1285 page = ptr;
1287 w = page->pixmap->w;
1289 ARSERT (h >= 0 && "ml_draw wrong h");
1290 ARSERT (py <= page->pixmap->h && "ml_draw wrong py");
1292 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1294 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
1295 struct slice *slice = &page->slices[slicenum];
1296 if (slice->h > py) {
1297 break;
1299 py -= slice->h;
1302 h = MIN (state.h, h);
1303 while (h) {
1304 int th;
1305 struct slice *slice = &page->slices[slicenum];
1307 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
1309 th = MIN (h, slice->h - py);
1310 upload2 (page, slicenum, "upload");
1312 glBegin (GL_QUADS);
1314 glTexCoord2i (0, py);
1315 glVertex2i (0, dispy);
1317 glTexCoord2i (w, py);
1318 glVertex2i (w, dispy);
1320 glTexCoord2i (w, py+th);
1321 glVertex2i (w, dispy + th);
1323 glTexCoord2i (0, py+th);
1324 glVertex2i (0, dispy + th);
1326 glEnd ();
1328 h -= th;
1329 py = 0;
1330 dispy += th;
1331 slicenum += 1;
1334 showsel (page, yoff);
1335 if (hlinks) highlightlinks (page, yoff);
1336 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1338 CAMLreturn (Val_unit);
1341 static pdf_link *getlink (struct page *page, int x, int y)
1343 fz_point p;
1344 fz_matrix ctm;
1345 pdf_link *link;
1347 p.x = x + page->pixmap->x;
1348 p.y = y + page->pixmap->y;
1350 ctm = fz_invertmatrix (page->pagedim.ctm);
1351 p = fz_transformpoint (ctm, p);
1353 for (link = page->drawpage->links; link; link = link->next) {
1354 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1355 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1356 return link;
1360 return NULL;
1363 static void ensuretext (struct page *page)
1365 if (!page->text) {
1366 fz_error error;
1367 fz_device *tdev;
1369 page->text = fz_newtextspan ();
1370 tdev = fz_newtextdevice (page->text);
1371 error = pdf_runpage (state.xref, page->drawpage, tdev,
1372 page->pagedim.ctm);
1373 if (error) die (error);
1374 fz_freedevice (tdev);
1378 CAMLprim value ml_whatsunder (value ptr_v, value x_v, value y_v)
1380 CAMLparam3 (ptr_v, x_v, y_v);
1381 CAMLlocal3 (ret_v, tup_v, str_v);
1382 pdf_link *link;
1383 struct page *page;
1384 char *s = String_val (ptr_v);
1386 ret_v = Val_int (0);
1387 if (trylock ("ml_whatsunder")) {
1388 goto done;
1391 page = parse_pointer ("ml_whatsunder", s);
1392 link = getlink (page, Int_val (x_v), Int_val (y_v));
1393 if (link) {
1394 switch (link->kind) {
1395 case PDF_LGOTO:
1397 int pageno;
1398 fz_point p;
1399 fz_obj *obj;
1401 pageno = -1;
1402 p.x = 0;
1403 p.y = 0;
1405 if (fz_isarray (link->dest)) {
1406 obj = fz_arrayget (link->dest, 0);
1407 if (fz_isindirect (obj)) {
1408 pageno = pdf_findpageobject (state.xref, obj) - 1;
1410 else if (fz_isint (obj)) {
1411 pageno = fz_toint (obj);
1414 if (fz_arraylen (link->dest) > 3) {
1415 fz_obj *xo, *yo;
1417 xo = fz_arrayget (link->dest, 2);
1418 yo = fz_arrayget (link->dest, 3);
1419 if (!fz_isnull (xo) && !fz_isnull (yo)) {
1420 p.x = fz_toint (xo);
1421 p.y = fz_toint (yo);
1422 p = fz_transformpoint (page->pagedim.ctm, p);
1426 else {
1427 pageno = pdf_findpageobject (state.xref, link->dest) - 1;
1429 tup_v = caml_alloc_tuple (2);
1430 ret_v = caml_alloc_small (1, 1);
1431 Field (tup_v, 0) = Val_int (pageno);
1432 Field (tup_v, 1) = Val_int (p.y);
1433 Field (ret_v, 0) = tup_v;
1435 break;
1437 case PDF_LURI:
1438 str_v = caml_copy_string (fz_tostrbuf (link->dest));
1439 ret_v = caml_alloc_small (1, 0);
1440 Field (ret_v, 0) = str_v;
1441 break;
1443 default:
1444 printd (state.sock, "T unhandled link kind %d", link->kind);
1445 break;
1448 else {
1449 int i, x, y;
1450 fz_textspan *span;
1452 ensuretext (page);
1453 x = Int_val (x_v) + page->pixmap->x;
1454 y = Int_val (y_v) + page->pixmap->y;
1456 for (span = page->text; span; span = span->next) {
1457 for (i = 0; i < span->len; ++i) {
1458 fz_bbox *b;
1459 b = &span->text[i].bbox;
1460 if ((x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1)) {
1461 const char *n2 =
1462 span->font && span->font->name
1463 ? span->font->name
1464 : "Span has no font name"
1466 #ifdef FT_FREETYPE_H
1467 FT_FaceRec *face = span->font->ftface;
1468 if (face && face->family_name) {
1469 char *s;
1470 char *n1 = face->family_name;
1471 size_t l1 = strlen (n1);
1472 size_t l2 = strlen (n2);
1474 if (l1 != l2 || memcmp (n1, n2, l1)) {
1475 s = malloc (l1 + l2 + 2);
1476 if (s) {
1477 memcpy (s, n2, l2);
1478 s[l2] = '=';
1479 memcpy (s + l2 + 1, n1, l1 + 1);
1480 str_v = caml_copy_string (s);
1481 free (s);
1485 if (str_v == 0) {
1486 str_v = caml_copy_string (n2);
1488 #else
1489 str_v = caml_copy_string (n2);
1490 #endif
1491 ret_v = caml_alloc_small (1, 2);
1492 Field (ret_v, 0) = str_v;
1493 goto unlock;
1498 unlock:
1499 unlock ("ml_whatsunder");
1501 done:
1502 CAMLreturn (ret_v);
1505 CAMLprim value ml_seltext (value ptr_v, value rect_v, value oy_v)
1507 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1508 fz_bbox *b;
1509 struct page *page;
1510 fz_textspan *span;
1511 struct mark first, last;
1512 int i, x0, x1, y0, y1, oy;
1513 char *s = String_val (ptr_v);
1515 if (trylock ("ml_seltext")) {
1516 goto done;
1519 page = parse_pointer ("ml_seltext", s);
1520 ensuretext (page);
1522 oy = Int_val (oy_v);
1523 x0 = Int_val (Field (rect_v, 0));
1524 y0 = Int_val (Field (rect_v, 1));
1525 x1 = Int_val (Field (rect_v, 2));
1526 y1 = Int_val (Field (rect_v, 3));
1528 if (0) {
1529 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1530 glColor3ub (128, 128, 128);
1531 glRecti (x0, y0, x1, y1);
1532 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1535 x0 += page->pixmap->x;
1536 y0 += page->pixmap->y - oy;
1537 x1 += page->pixmap->x;
1538 y1 += page->pixmap->y - oy;
1540 first.span = NULL;
1541 last.span = NULL;
1543 last.i = first.i = 0;
1544 first.span = page->text;
1545 for (span = page->text; span; span = span->next) {
1546 for (i = 0; i < span->len; ++i) {
1547 b = &span->text[i].bbox;
1548 if (x0 >= b->x0 && x0 <= b->x1 && y0 >= b->y0 && y0 <= b->y1) {
1549 first.i = i;
1550 first.span = span;
1552 if (x1 >= b->x0 && x1 <= b->x1 && y1 >= b->y0 && y1 <= b->y1) {
1553 last.i = i;
1554 last.span = span;
1559 if (y1 < y0 || x1 < x0) {
1560 int swap = 0;
1562 if (first.span == last.span) {
1563 swap = 1;
1565 else {
1566 if (y1 < y0) {
1567 for (span = first.span; span && span != last.span;
1568 span = span->next) {
1569 if (span->eol) {
1570 swap = 1;
1571 break;
1577 if (swap) {
1578 i = first.i;
1579 span = first.span;
1580 first.i = last.i;
1581 first.span = last.span;
1582 last.i = i;
1583 last.span = span;
1587 page->fmark = first;
1588 page->lmark = last;
1590 unlock ("ml_seltext");
1592 done:
1593 CAMLreturn (Val_unit);
1596 static int pipespan (FILE *f, fz_textspan *span, int a, int b)
1598 char buf[4];
1599 int i, len, ret;
1601 for (i = a; i <= b; ++i) {
1602 len = runetochar (buf, &span->text[i].c);
1603 ret = fwrite (buf, len, 1, f);
1605 if (ret != 1) {
1606 printd (state.sock, "T failed to write %d bytes ret=%d: %s",
1607 len, ret, strerror (errno));
1608 return -1;
1611 return 0;
1614 CAMLprim value ml_copysel (value ptr_v)
1616 CAMLparam1 (ptr_v);
1617 FILE *f;
1618 struct page *page;
1619 char *s = String_val (ptr_v);
1621 if (trylock ("ml_copysel")) {
1622 goto done;
1625 if (!*s) {
1626 close:
1627 #ifdef USE_XSEL
1628 if (state.xselpipe) {
1629 int ret = pclose (state.xselpipe);
1630 if (ret) {
1631 printd (state.sock, "T failed to close xsel pipe `%s'",
1632 strerror (errno));
1634 state.xselpipe = NULL;
1636 #else
1637 printf ("========================================\n");
1638 #endif
1640 else {
1641 fz_textspan *span;
1643 page = parse_pointer ("ml_sopysel", s);
1645 if (!page->fmark.span || !page->lmark.span) {
1646 printd (state.sock, "T nothing to copy");
1647 goto unlock;
1650 f = stdout;
1651 #ifdef USE_XSEL
1652 if (!state.xselpipe) {
1653 state.xselpipe = popen ("xsel -i", "w");
1654 if (!state.xselpipe) {
1655 printd (state.sock, "T failed to open xsel pipe `%s'",
1656 strerror (errno));
1658 else {
1659 f = state.xselpipe;
1662 else {
1663 f = state.xselpipe;
1665 #endif
1667 for (span = page->fmark.span;
1668 span && span != page->lmark.span->next;
1669 span = span->next) {
1670 int a = span == page->fmark.span ? page->fmark.i : 0;
1671 int b = span == page->lmark.span ? page->lmark.i : span->len - 1;
1672 if (pipespan (f, span, a, b)) {
1673 goto close;
1675 if (span->eol) {
1676 if (putc ('\n', f) == EOF) {
1677 printd (state.sock, "T failed break line on xsel pipe `%s'",
1678 strerror (errno));
1679 goto close;
1683 page->lmark.span = NULL;
1684 page->fmark.span = NULL;
1687 unlock:
1688 unlock ("ml_copysel");
1690 done:
1691 CAMLreturn (Val_unit);
1694 CAMLprim value ml_getpagewh (value pagedimno_v)
1696 CAMLparam1 (pagedimno_v);
1697 CAMLlocal1 (ret_v);
1698 int pagedimno = Int_val (pagedimno_v);
1700 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1701 Store_double_field (ret_v, 0, state.pagedims[pagedimno].box.x0);
1702 Store_double_field (ret_v, 1, state.pagedims[pagedimno].box.x1);
1703 Store_double_field (ret_v, 2, state.pagedims[pagedimno].box.y0);
1704 Store_double_field (ret_v, 3, state.pagedims[pagedimno].box.y1);
1705 CAMLreturn (ret_v);
1708 CAMLprim value ml_init (value sock_v)
1710 #ifndef _WIN32
1711 int ret;
1712 #endif
1713 CAMLparam1 (sock_v);
1715 state.texcount = 256;
1716 state.sliceheight = 24;
1717 state.texform = GL_RGBA;
1718 state.texty = GL_UNSIGNED_BYTE;
1720 fz_accelerate ();
1721 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1722 if (!state.texids) {
1723 err (1, "calloc texids " FMT_s,
1724 state.texcount * sizeof (*state.texids));
1727 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1728 if (!state.texowners) {
1729 err (1, "calloc texowners " FMT_s,
1730 state.texcount * sizeof (*state.texowners));
1733 glGenTextures (state.texcount, state.texids);
1735 #ifdef _WIN32
1736 state.sock = Socket_val (sock_v);
1737 #else
1738 state.sock = Int_val (sock_v);
1739 #endif
1741 state.cache = fz_newglyphcache ();
1742 if (!state.cache) {
1743 errx (1, "fz_newglyphcache failed");
1746 #ifdef _WIN32
1747 InitializeCriticalSection (&critsec);
1748 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
1749 if (state.thread == INVALID_HANDLE_VALUE) {
1750 errx (1, "CreateThread failed: %lx", GetLastError ());
1752 #else
1753 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1754 if (ret) {
1755 errx (1, "pthread_create: %s", strerror (errno));
1757 #endif
1759 CAMLreturn (Val_unit);