WSAGetLastError returns int not long
[llpp.git] / link.c
blob0df3fdd6c200379d52f5311c0ac6d5babcdf334c
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 ssize_t int
7 #define FMT_ss "%d"
8 #ifdef _WIN64
9 #define FMT_s "%i64u"
10 #else
11 #define FMT_s "%u"
12 #endif
13 #pragma warning (disable:4244)
14 #pragma warning (disable:4996)
15 #pragma warning (disable:4995)
16 #endif
18 #ifdef _MSC_VER
19 #include <errno.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdlib.h>
23 static void __declspec (noreturn) err (int exitcode, const char *fmt, ...)
25 va_list ap;
26 int errcode;
28 errcode = errno;
29 va_start (ap, fmt);
30 vfprintf (stderr, fmt, ap);
31 va_end (ap);
32 fprintf (stderr, ": %s\n", strerror (errno));
33 exit (exitcode);
35 static void __declspec (noreturn) errx (int exitcode, const char *fmt, ...)
37 va_list ap;
39 va_start (ap, fmt);
40 vfprintf (stderr, fmt, ap);
41 va_end (ap);
42 fputc ('\n', stderr);
43 exit (exitcode);
45 static void __declspec (noreturn) sockerr (int exitcode, const char *fmt, ...)
47 va_list ap;
49 va_start (ap, fmt);
50 vfprintf (stderr, fmt, ap);
51 va_end (ap);
52 fprintf (stderr, ": wsaerror 0x%x\n", WSAGetLastError ());
53 exit (exitcode);
55 #else
56 #define FMT_ss "%zd"
57 #define FMT_s "%zu"
58 #define _GNU_SOURCE
59 #include <err.h>
60 #define sockerr err
61 #endif
62 #include <regex.h>
63 #include <errno.h>
64 #include <ctype.h>
65 #include <stdio.h>
66 #include <stdarg.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <limits.h>
70 #ifndef _WIN32
71 #include <pthread.h>
72 #include <sys/time.h>
73 #include <sys/types.h>
74 #include <sys/socket.h>
75 #include <sys/select.h>
76 #endif
78 #ifdef __APPLE__
79 #include <OpenGL/gl.h>
80 #else
81 #include <GL/gl.h>
82 #endif
84 #ifndef GL_TEXTURE_RECTANGLE_ARB
85 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
86 #endif
88 #include <caml/fail.h>
89 #include <caml/alloc.h>
90 #include <caml/memory.h>
91 #include <caml/unixsupport.h>
93 #include <fitz.h>
94 #include <mupdf.h>
96 #if 0
97 #define lprintf printf
98 #else
99 #define lprintf(...)
100 #endif
102 #define ARSERT(cond) for (;;) { \
103 if (!(cond)) { \
104 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
106 break; \
109 struct slice {
110 int texindex;
111 int w, h;
114 struct page {
115 int pageno;
116 int slicecount;
117 fz_textspan *text;
118 fz_pixmap *pixmap;
119 pdf_page *drawpage;
120 struct pagedim *pagedim;
121 struct page *prev;
122 struct slice slices[];
125 struct pagedim {
126 int pageno;
127 int rotate;
128 fz_rect box;
129 fz_bbox bbox;
130 fz_matrix ctm, ctm1;
133 struct {
134 int sock;
135 int sliceheight;
136 struct page *pages;
137 struct pagedim *pagedims;
138 int pagecount;
139 int pagedimcount;
140 pdf_xref *xref;
141 fz_glyphcache *cache;
142 int w, h;
144 int texindex;
145 int texcount;
146 GLuint *texids;
148 GLenum texform;
149 GLenum texty;
151 int lotsamemory;
153 int *pagetbl;
154 struct {
155 int w, h;
156 struct slice *slice;
157 } *texowners;
159 int rotate;
161 #ifdef _WIN32
162 HANDLE thread;
163 #else
164 pthread_t thread;
165 #endif
166 } state;
168 #ifdef _WIN32
169 static CRITICAL_SECTION critsec;
171 static void lock (void *unused)
173 (void) unused;
174 EnterCriticalSection (&critsec);
177 static void unlock (void *unused)
179 (void) unused;
180 LeaveCriticalSection (&critsec);
183 static int trylock (void *unused)
185 return TryEnterCriticalSection (&critsec) == 0;
187 #else
188 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
190 static void lock (const char *cap)
192 int ret = pthread_mutex_lock (&mutex);
193 if (ret) {
194 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
198 static void unlock (const char *cap)
200 int ret = pthread_mutex_unlock (&mutex);
201 if (ret) {
202 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
206 static int trylock (const char *cap)
208 int ret = pthread_mutex_trylock (&mutex);
210 if (ret && ret != EBUSY) {
211 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
213 return ret == EBUSY;
215 #endif
217 static void *parse_pointer (const char *cap, const char *s)
219 int ret;
220 void *ptr;
222 ret = sscanf (s, "%p", &ptr);
223 if (ret != 1) {
224 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
226 return ptr;
229 static int hasdata (int sock)
231 int n;
232 fd_set s;
233 struct timeval tv;
234 FD_ZERO (&s);
235 FD_SET (sock, &s);
236 tv.tv_sec = 0;
237 tv.tv_usec = 0;
238 n = select (sock + 1, &s, NULL, NULL, &tv);
239 if (n == 0) return 0;
240 if (n == 1) return 1;
241 sockerr (1, "hasdata: select error ret=%d", n);
244 static double now (void)
246 struct timeval tv;
248 if (gettimeofday (&tv, NULL)) {
249 err (1, "gettimeofday");
251 return tv.tv_sec + tv.tv_usec*1e-6;
254 static void readdata (int fd, char *p, int size)
256 ssize_t n;
258 n = recv (fd, p, size, 0);
259 if (n - size) {
260 if (!n) errx (1, "EOF while reading");
261 sockerr (1, "recv (req %d, ret " FMT_ss ")", size, n);
265 static void writedata (int fd, char *p, int size)
267 char buf[4];
268 ssize_t n;
270 buf[0] = (size >> 24) & 0xff;
271 buf[1] = (size >> 16) & 0xff;
272 buf[2] = (size >> 8) & 0xff;
273 buf[3] = (size >> 0) & 0xff;
275 n = send (fd, buf, 4, 0);
276 if (n != 4) {
277 if (!n) errx (1, "EOF while writing length");
278 sockerr (1, "send " FMT_ss, n);
281 n = send (fd, p, size, 0);
282 if (n - size) {
283 if (!n) errx (1, "EOF while writing data");
284 sockerr (1, "send (req %d, ret " FMT_ss ")", size, n);
288 static void
289 #ifdef __GNUC__
290 __attribute__ ((format (printf, 2, 3)))
291 #endif
292 printd (int fd, const char *fmt, ...)
294 int len;
295 va_list ap;
296 char buf[200];
298 va_start (ap, fmt);
299 len = vsnprintf (buf, sizeof (buf), fmt, ap);
300 va_end (ap);
301 writedata (fd, buf, len);
304 static void die (fz_error error)
306 fz_catch (error, "aborting");
307 if (state.xref)
308 pdf_freexref (state.xref);
309 exit (1);
312 static void openxref (char *filename)
314 int fd;
315 fz_error error;
316 fz_stream *file;
318 fd = open (filename, O_BINARY | O_RDONLY, 0666);
319 if (fd < 0)
320 die (fz_throw ("cannot open file '%s'", filename));
322 file = fz_openfile (fd);
323 error = pdf_openxrefwithstream (&state.xref, file, NULL);
324 if (error)
325 die (fz_rethrow(error, "cannot open document '%s'", filename));
326 fz_dropstream (file);
328 if (pdf_needspassword (state.xref)) {
329 die (fz_throw ("password protected"));
332 error = pdf_loadpagetree (state.xref);
333 if (error) {
334 die (fz_throw ("cannot load page tree"));
337 state.pagecount = pdf_getpagecount (state.xref);
338 state.pagetbl = stat_alloc (state.pagecount * sizeof (*state.pagetbl));
341 static int readlen (int fd)
343 ssize_t n;
344 char p[4];
346 n = recv (fd, p, 4, 0);
347 if (n != 4) {
348 if (!n) errx (1, "EOF while reading length");
349 sockerr (1, "recv " FMT_ss, n);
352 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
355 static void freepage (struct page *page)
357 int i;
358 struct page *p;
360 fz_droppixmap (page->pixmap);
361 for (p = state.pages; p; p = p->prev) {
362 if (p->prev == page) {
363 p->prev = page->prev;
364 break;
367 for (i = 0; i < page->slicecount; ++i) {
368 struct slice *s = &page->slices[i];
369 if (s->texindex != -1) {
370 if (state.texowners[s->texindex].slice == s) {
371 state.texowners[s->texindex].slice = NULL;
372 ARSERT (state.texowners[s->texindex].w == s->w);
373 ARSERT (state.texowners[s->texindex].h >= s->h);
377 if (page->text) {
378 fz_freetextspan (page->text);
380 if (page->drawpage) {
381 pdf_freepage (page->drawpage);
384 free (page);
387 static void subdivide (struct page *p)
389 int i;
390 int h = p->pixmap->h;
391 int th = MIN (h, state.sliceheight);
393 for (i = 0; i < p->slicecount; ++i) {
394 struct slice *s = &p->slices[i];
395 s->texindex = -1;
396 s->h = MIN (th, h);
397 s->w = p->pixmap->w;
398 h -= th;
402 static void *render (int pageno, int pindex)
404 fz_error error;
405 int slicecount;
406 fz_obj *pageobj;
407 struct page *page;
408 double start, end;
409 pdf_page *drawpage;
410 fz_device *idev;
411 struct pagedim *pagedim;
413 start = now ();
414 printd (state.sock, "V rendering %d", pageno);
416 pagedim = &state.pagedims[pindex];
417 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
418 + state.sliceheight - 1) / state.sliceheight;
419 slicecount += slicecount == 0;
421 page = calloc (sizeof (*page)
422 + (slicecount * sizeof (struct slice)), 1);
423 if (!page) {
424 err (1, "calloc page %d\n", pageno);
426 page->slicecount = slicecount;
427 page->prev = state.pages;
428 state.pages = page;
430 pageobj = pdf_getpageobject (state.xref, pageno);
431 if (!pageobj)
432 die (fz_throw ("cannot retrieve info from page %d", pageno));
434 error = pdf_loadpage (&drawpage, state.xref, pageobj);
435 if (error)
436 die (error);
438 page->pixmap = fz_newpixmapwithrect (fz_devicergb, pagedim->bbox);
439 fz_clearpixmap (page->pixmap, 0xFF);
441 idev = fz_newdrawdevice (state.cache, page->pixmap);
442 if (!idev)
443 die (fz_throw ("fz_newdrawdevice failed"));
444 error = pdf_runpage (state.xref, drawpage, idev, pagedim->ctm);
445 fz_freedevice (idev);
447 page->drawpage = drawpage;
448 page->pagedim = pagedim;
449 page->pageno = pageno;
450 subdivide (page);
451 end = now ();
453 if (!state.lotsamemory) {
454 pdf_agestore(state.xref->store, 3);
457 printd (state.sock, "V rendering %d took %f sec", pageno, end - start);
458 return page;
461 static void initpdims (void)
463 int pageno;
464 double start, end;
466 start = now ();
467 for (pageno = 0; pageno < state.pagecount; ++pageno) {
468 int rotate;
469 fz_rect box;
470 struct pagedim *p;
471 fz_obj *obj, *pageobj;
473 pageobj = pdf_getpageobject (state.xref, pageno + 1);
475 obj = fz_dictgets (pageobj, "CropBox");
476 if (!fz_isarray (obj)) {
477 obj = fz_dictgets (pageobj, "MediaBox");
478 if (!fz_isarray (obj)) {
479 die (fz_throw ("cannot find page bounds %d (%d Rd)",
480 fz_tonum (pageobj), fz_togen (pageobj)));
483 box = pdf_torect (obj);
485 obj = fz_dictgets (pageobj, "Rotate");
486 if (fz_isint (obj)) {
487 rotate = fz_toint (obj);
489 else {
490 rotate = 0;
492 rotate += state.rotate;
494 state.pagetbl[pageno] =
495 fz_tonum (pdf_getpageref (state.xref, pageno + 1));
497 p = &state.pagedims[state.pagedimcount - 1];
498 if ((state.pagedimcount == 0)
499 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
500 size_t size;
502 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
503 state.pagedims = realloc (state.pagedims, size);
504 if (!state.pagedims) {
505 err (1, "realloc pagedims to " FMT_s " (%d elems)",
506 size, state.pagedimcount + 1);
508 p = &state.pagedims[state.pagedimcount++];
509 p->rotate = rotate;
510 p->box = box;
511 p->pageno = pageno;
514 end = now ();
515 printd (state.sock, "T Processed %d pages in %f seconds",
516 state.pagecount, end - start);
519 static void layout (void)
521 int pindex;
522 fz_matrix ctm;
523 fz_rect box, box2;
524 double zoom, w;
525 struct pagedim *p = state.pagedims;
527 pindex = 0;
528 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
529 box.x0 = MIN (p->box.x0, p->box.x1);
530 box.y0 = MIN (p->box.y0, p->box.y1);
531 box.x1 = MAX (p->box.x0, p->box.x1);
532 box.y1 = MAX (p->box.y0, p->box.y1);
534 ctm = fz_identity;
535 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
536 ctm = fz_concat (ctm, fz_rotate (p->rotate));
537 box2 = fz_transformrect (ctm, box);
538 w = box2.x1 - box2.x0;
540 zoom = (state.w / w);
541 ctm = fz_identity;
542 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
543 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
544 memcpy (&p->ctm1, &ctm, sizeof (ctm));
545 ctm = fz_concat (ctm, fz_rotate (p->rotate));
546 p->bbox = fz_roundrect (fz_transformrect (ctm, box));
547 memcpy (&p->ctm, &ctm, sizeof (ctm));
550 while (p-- != state.pagedims) {
551 printd (state.sock, "l %d %d %d",
552 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0);
556 static void recurse_outline (pdf_outline *outline, int level)
558 while (outline) {
559 int i, num;
560 fz_obj *obj;
561 int top = 0;
562 int pageno = -1;
564 if (!outline->link) goto next;
566 obj = outline->link->dest;
567 if (fz_isindirect (obj)) {
568 num = fz_tonum (obj);
570 for (i = 0; i < state.pagecount; ++i) {
571 if (state.pagetbl[i] == num) {
572 pageno = i;
573 break;
577 else if (fz_isarray (obj)) {
578 fz_obj *obj2;
580 obj2 = fz_arrayget (obj, 0);
581 if (fz_isint (obj2)) {
582 pageno = fz_toint (obj2);
584 else {
585 num = fz_tonum (obj2);
586 for (i = 0; i < state.pagecount; ++i) {
587 if (state.pagetbl[i] == num) {
588 pageno = i;
589 break;
594 if (fz_arraylen (obj) > 3) {
595 fz_point p;
596 struct pagedim *pagedim = state.pagedims;
598 for (i = 0; i < state.pagedimcount; ++i) {
599 if (state.pagedims[i].pageno > pageno)
600 break;
601 pagedim = &state.pagedims[i];
604 p.x = fz_toint (fz_arrayget (obj, 2));
605 p.y = fz_toint (fz_arrayget (obj, 3));
606 p = fz_transformpoint (pagedim->ctm, p);
607 top = p.y;
611 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
612 printd (state.sock, "o %d %d %d %s",
613 level, pageno, top, outline->title);
614 next:
615 if (outline->child) {
616 recurse_outline (outline->child, level + 1);
618 outline = outline->next;
622 static void process_outline (void)
624 pdf_outline *outline;
626 outline = pdf_loadoutline (state.xref);
627 if (outline) {
628 recurse_outline (outline, 0);
629 pdf_freeoutline (outline);
633 static int comparespans (const void *l, const void *r)
635 fz_textspan const *const*ls = l;
636 fz_textspan const *const*rs = r;
637 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
640 /* wishful thinking function */
641 static void search (regex_t *re, int pageno, int y, int forward)
643 int i, j;
644 int ret;
645 char *p;
646 char buf[256];
647 fz_matrix ctm;
648 fz_error error;
649 fz_obj *pageobj;
650 fz_device *tdev;
651 pdf_page *drawpage;
652 fz_textspan *text, *span, **pspan;
653 struct pagedim *pdim, *pdimprev;
654 int stop = 0;
655 int niters = 0;
656 int nspans;
657 double start, end;
659 start = now ();
660 while (pageno >= 0 && pageno < state.pagecount && !stop) {
661 if (niters++ == 5) {
662 if (!state.lotsamemory) {
663 pdf_agestore(state.xref->store, 3);
665 niters = 0;
666 if (hasdata (state.sock)) {
667 printd (state.sock, "T attention requested aborting search at %d",
668 pageno);
669 stop = 1;
671 else {
672 printd (state.sock, "T searching in page %d", pageno);
675 pdimprev = NULL;
676 for (i = 0; i < state.pagedimcount; ++i) {
677 pdim = &state.pagedims[i];
678 if (pdim->pageno == pageno) {
679 goto found;
681 if (pdim->pageno > pageno) {
682 pdim = pdimprev;
683 goto found;
685 pdimprev = pdim;
687 pdim = pdimprev;
688 found:
690 pageobj = pdf_getpageobject (state.xref, pageno + 1);
691 if (!pageobj)
692 die (fz_throw ("cannot retrieve info from page %d", pageno));
694 error = pdf_loadpage (&drawpage, state.xref, pageobj);
695 if (error)
696 die (error);
698 ctm = fz_rotate (pdim->rotate);
700 text = fz_newtextspan ();
701 tdev = fz_newtextdevice (text);
702 error = pdf_runpage (state.xref, drawpage, tdev, pdim->ctm1);
703 if (error) die (error);
704 fz_freedevice (tdev);
706 nspans = 0;
707 for (span = text; span; span = span->next) {
708 nspans++;
710 pspan = malloc (sizeof (void *) * nspans);
711 if (!pspan) {
712 err (1, "malloc span pointers " FMT_s, sizeof (void *) * nspans);
714 for (i = 0, span = text; span; span = span->next, ++i) {
715 pspan[i] = span;
717 qsort (pspan, nspans, sizeof (fz_textspan *), comparespans);
719 j = forward ? 0 : nspans - 1;
720 while (nspans--) {
721 regmatch_t rm;
723 span = pspan[j];
724 j += forward ? 1 : -1;
725 p = buf;
726 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
727 if (forward) {
728 if (span->text[i].bbox.y0 < y + 1) {
729 continue;
732 else {
733 if (span->text[i].bbox.y0 > y - 1) {
734 continue;
737 if (span->text[i].c < 256) {
738 *p++ = span->text[i].c;
740 else {
741 *p++ = '?';
744 if (p == buf) {
745 continue;
747 *p++ = 0;
749 ret = regexec (re, buf, 1, &rm, 0);
750 if (ret) {
751 if (ret != REG_NOMATCH) {
752 size_t size;
753 char errbuf[80];
754 size = regerror (ret, re, errbuf, sizeof (errbuf));
755 printd (state.sock,
756 "T regexec error `%.*s'",
757 (int) size, errbuf);
758 fz_freetextspan (text);
759 pdf_freepage (drawpage);
760 free (pspan);
761 return;
764 else {
765 int xoff, yoff;
766 fz_bbox *sb, *eb;
767 fz_point p1, p2, p3, p4;
769 xoff = -pdim->bbox.x0;
770 yoff = -pdim->bbox.y0;
772 sb = &span->text[rm.rm_so].bbox;
773 eb = &span->text[rm.rm_eo - 1].bbox;
775 p1.x = sb->x0;
776 p1.y = sb->y0;
777 p2.x = eb->x1;
778 p2.y = sb->y0;
779 p3.x = eb->x1;
780 p3.y = eb->y1;
781 p4.x = sb->x0;
782 p4.y = eb->y1;
784 p1 = fz_transformpoint (ctm, p1);
785 p2 = fz_transformpoint (ctm, p2);
786 p3 = fz_transformpoint (ctm, p3);
787 p4 = fz_transformpoint (ctm, p4);
789 if (!stop) {
790 printd (state.sock, "F %d %d %f %f %f %f %f %f %f %f",
791 pageno, 1,
792 p1.x + xoff, p1.y + yoff,
793 p2.x + xoff, p2.y + yoff,
794 p3.x + xoff, p3.y + yoff,
795 p4.x + xoff, p4.y + yoff);
797 printd (state.sock, "T found at %d `%.*s' in %f sec",
798 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
799 now () - start);
801 else {
802 printd (state.sock, "R %d %d %f %f %f %f %f %f %f %f",
803 pageno, 2,
804 p1.x + xoff, p1.y + yoff,
805 p2.x + xoff, p2.y + yoff,
806 p3.x + xoff, p3.y + yoff,
807 p4.x + xoff, p4.y + yoff);
809 stop = 1;
812 if (forward) {
813 pageno += 1;
814 y = 0;
816 else {
817 pageno -= 1;
818 y = INT_MAX;
820 fz_freetextspan (text);
821 pdf_freepage (drawpage);
822 free (pspan);
824 end = now ();
825 if (!stop) {
826 printd (state.sock, "T no matches %f sec", end - start);
828 printd (state.sock, "D");
831 static
832 #ifdef _WIN32
833 DWORD _stdcall
834 #else
835 void *
836 #endif
837 mainloop (void *unused)
839 char *p = NULL;
840 int len, ret, oldlen = 0;
842 for (;;) {
843 len = readlen (state.sock);
844 if (len == 0) {
845 errx (1, "readlen returned 0");
848 if (oldlen < len + 1) {
849 p = realloc (p, len + 1);
850 if (!p) {
851 err (1, "realloc %d failed", len + 1);
853 oldlen = len + 1;
855 readdata (state.sock, p, len);
856 p[len] = 0;
858 if (!strncmp ("open", p, 4)) {
859 fz_obj *obj;
860 char *filename = p + 5;
862 openxref (filename);
863 initpdims ();
865 obj = fz_dictgets (state.xref->trailer, "Info");
866 if (obj) {
867 obj = fz_dictgets (obj, "Title");
868 if (obj) {
869 printd (state.sock, "t %s", pdf_toutf8 (obj));
873 else if (!strncmp ("free", p, 4)) {
874 void *ptr;
876 ret = sscanf (p + 4, " %p", &ptr);
877 if (ret != 1) {
878 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
880 lock ("free");
881 freepage (ptr);
882 unlock ("free");
883 printd (state.sock, "d");
885 else if (!strncmp ("search", p, 6)) {
886 int icase, pageno, y, ret, len2, forward;
887 char *pattern;
888 regex_t re;
890 ret = sscanf (p + 6, " %d %d %d %d,%n",
891 &icase, &pageno, &y, &forward, &len2);
892 if (ret != 4) {
893 errx (1, "malformed search `%s' ret=%d", p, ret);
896 pattern = p + 6 + len2;
897 ret = regcomp (&re, pattern,
898 REG_EXTENDED | (icase ? REG_ICASE : 0));
899 if (ret) {
900 char errbuf[80];
901 size_t size;
903 size = regerror (ret, &re, errbuf, sizeof (errbuf));
904 printd (state.sock, "T regcomp failed `%.*s'",
905 (int) size, errbuf);
907 else {
908 search (&re, pageno, y, forward);
909 regfree (&re);
912 else if (!strncmp ("geometry", p, 8)) {
913 int w, h;
915 printd (state.sock, "c");
916 ret = sscanf (p + 8, " %d %d", &w, &h);
917 if (ret != 2) {
918 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
920 state.h = h;
921 if (w != state.w) {
922 int i;
923 state.w = w;
924 for (i = 0; i < state.texcount; ++i) {
925 state.texowners[i].slice = NULL;
928 lock ("geometry");
929 layout ();
930 process_outline ();
931 unlock ("geometry");
932 printd (state.sock, "C %d", state.pagecount);
934 else if (!strncmp ("rotate", p, 6)) {
935 float rotate;
937 printd (state.sock, "c");
938 ret = sscanf (p + 6, " %f", &rotate);
939 if (ret != 1) {
940 errx (1, "bad rotate line `%.*s' ret=%d", len, p, ret);
942 state.rotate = rotate;
943 lock ("rotate");
944 state.pagedimcount = 0;
945 free (state.pagedims);
946 state.pagedims = NULL;
947 initpdims ();
948 layout ();
949 process_outline ();
950 unlock ("rotate");
951 printd (state.sock, "C %d", state.pagecount);
953 else if (!strncmp ("render", p, 6)) {
954 int pageno, pindex, w, h, ret;
955 struct page *page;
957 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
958 if (ret != 4) {
959 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
962 page = render (pageno, pindex);
963 printd (state.sock, "r %d %d %d %d %p\n",
964 pageno,
965 state.w,
966 state.h,
967 state.rotate,
968 page);
970 else if (!strncmp ("interrupt", p, 8)) {
971 printd (state.sock, "V interrupted");
973 else {
974 errx (1, "unknown command %.*s", len, p);
977 return 0;
980 static void upload2 (struct page *page, int slicenum, const char *cap)
982 int i;
983 int w, h;
984 double start, end;
985 struct slice *slice = &page->slices[slicenum];
987 w = page->pixmap->w;
988 h = page->pixmap->h;
990 ARSERT (w == slice->w);
991 if (slice->texindex != -1
992 && state.texowners[slice->texindex].slice == slice) {
993 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
995 else {
996 int subimage = 0;
997 int index = (state.texindex++ % state.texcount);
998 size_t offset = 0;
1000 for (i = 0; i < slicenum; ++i) {
1001 offset += w * page->slices[i].h * 4;
1004 if (state.texowners[index].w == slice->w) {
1005 if (state.texowners[index].h >= slice->h ) {
1006 subimage = 1;
1008 else {
1009 state.texowners[index].h = slice->h;
1012 else {
1013 state.texowners[index].h = slice->h;
1016 state.texowners[index].slice = slice;
1017 state.texowners[index].w = slice->w;
1018 slice->texindex = index;
1020 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1021 start = now ();
1022 if (subimage) {
1024 GLenum err = glGetError ();
1025 if (err != GL_NO_ERROR) {
1026 printf ("\033[0;31mERROR1 %d %d %#x\033[0m\n", w, slice->h, err);
1027 abort ();
1030 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1035 slice->h,
1036 state.texform,
1037 state.texty,
1038 page->pixmap->samples + offset
1041 GLenum err = glGetError ();
1042 if (err != GL_NO_ERROR) {
1043 printf ("\033[0;31mERROR %d %d %#x\033[0m\n", w, slice->h, err);
1044 abort ();
1048 else {
1049 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1051 GL_RGBA8,
1053 slice->h,
1055 state.texform,
1056 state.texty,
1057 page->pixmap->samples + offset
1061 end = now ();
1062 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
1063 subimage ? "sub" : "img",
1064 page->pageno, slicenum,
1065 slice->w, slice->h,
1066 state.texids[slice->texindex],
1067 end - start);
1071 CAMLprim value ml_draw (value dispy_v, value w_v, value h_v,
1072 value py_v, value ptr_v)
1074 CAMLparam5 (dispy_v, w_v, h_v, py_v, ptr_v);
1075 int dispy = Int_val (dispy_v);
1076 int w = Int_val (w_v);
1077 int h = Int_val (h_v);
1078 int py = Int_val (py_v);
1079 char *s = String_val (ptr_v);
1080 int ret;
1081 void *ptr;
1082 struct page *page;
1083 int slicenum = 0;
1085 if (trylock ("ml_draw")) {
1086 goto done;
1089 ret = sscanf (s, "%p", &ptr);
1090 if (ret != 1) {
1091 errx (1, "cannot parse pointer `%s'", s);
1093 page = ptr;
1095 w = page->pixmap->w;
1097 ARSERT (h >= 0 && "ml_draw wrong h");
1098 ARSERT (py <= page->pixmap->h && "ml_draw wrong py");
1100 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1102 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
1103 struct slice *slice = &page->slices[slicenum];
1104 if (slice->h > py) {
1105 break;
1107 py -= slice->h;
1110 h = MIN (state.h, h);
1111 while (h) {
1112 int th;
1113 struct slice *slice = &page->slices[slicenum];
1115 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
1117 th = MIN (h, slice->h - py);
1118 upload2 (page, slicenum, "upload");
1120 glBegin (GL_QUADS);
1122 glTexCoord2i (0, py);
1123 glVertex2i (0, dispy);
1125 glTexCoord2i (w, py);
1126 glVertex2i (w, dispy);
1128 glTexCoord2i (w, py+th);
1129 glVertex2i (w, dispy + th);
1131 glTexCoord2i (0, py+th);
1132 glVertex2i (0, dispy + th);
1134 glEnd ();
1136 h -= th;
1137 py = 0;
1138 dispy += th;
1139 slicenum += 1;
1142 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1144 unlock ("ml_draw");
1145 done:
1146 CAMLreturn (Val_unit);
1149 static pdf_link *getlink (struct page *page, int x, int y)
1151 fz_point p;
1152 fz_matrix ctm;
1153 pdf_link *link;
1155 p.x = x + page->pixmap->x;
1156 p.y = y + page->pixmap->y;
1158 ctm = fz_invertmatrix (page->pagedim->ctm);
1159 p = fz_transformpoint (ctm, p);
1161 for (link = page->drawpage->links; link; link = link->next) {
1162 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1163 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1164 return link;
1168 return NULL;
1171 CAMLprim value ml_highlightlinks (value ptr_v, value yoff_v)
1173 CAMLparam2 (ptr_v, yoff_v);
1174 pdf_link *link;
1175 struct page *page;
1176 int xoff, yoff = Int_val (yoff_v);
1177 const char *s = String_val (ptr_v);
1179 if (trylock ("ml_highlightlinks")) {
1180 goto done;
1183 page = parse_pointer ("ml_highlightlinks", s);
1185 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1186 glEnable (GL_LINE_STIPPLE);
1187 glLineStipple (0.5, 0xcccc);
1189 xoff = -page->pixmap->x;
1190 yoff -= page->pixmap->y;
1192 glBegin (GL_QUADS);
1193 for (link = page->drawpage->links; link; link = link->next) {
1194 fz_point p1, p2, p3, p4;
1195 fz_matrix ctm = page->pagedim->ctm;
1197 p1.x = link->rect.x0;
1198 p1.y = link->rect.y0;
1200 p2.x = link->rect.x1;
1201 p2.y = link->rect.y0;
1203 p3.x = link->rect.x1;
1204 p3.y = link->rect.y1;
1206 p4.x = link->rect.x0;
1207 p4.y = link->rect.y1;
1209 p1 = fz_transformpoint (ctm, p1);
1210 p2 = fz_transformpoint (ctm, p2);
1211 p3 = fz_transformpoint (ctm, p3);
1212 p4 = fz_transformpoint (ctm, p4);
1214 switch (link->kind) {
1215 case PDF_LGOTO: glColor3ub (255, 0, 0); break;
1216 case PDF_LURI: glColor3ub (0, 0, 255); break;
1217 default: glColor3ub (0, 0, 0); break;
1220 glVertex2f (p1.x + xoff, p1.y + yoff);
1221 glVertex2f (p2.x + xoff, p2.y + yoff);
1222 glVertex2f (p3.x + xoff, p3.y + yoff);
1223 glVertex2f (p4.x + xoff, p4.y + yoff);
1225 glEnd ();
1227 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1228 glDisable (GL_LINE_STIPPLE);
1229 unlock ("ml_highlightlinks");
1231 done:
1232 CAMLreturn (Val_unit);
1235 CAMLprim value ml_getlink (value ptr_v, value x_v, value y_v)
1237 CAMLparam3 (ptr_v, x_v, y_v);
1238 CAMLlocal2 (ret_v, tup_v);
1239 pdf_link *link;
1240 struct page *page;
1241 char *s = String_val (ptr_v);
1243 ret_v = Val_int (0);
1244 if (trylock ("ml_getlink")) {
1245 ret_v = Val_int (0);
1246 goto done;
1249 page = parse_pointer ("ml_getlink", s);
1251 link = getlink (page, Int_val (x_v), Int_val (y_v));
1252 if (link) {
1253 switch (link->kind) {
1254 case PDF_LGOTO:
1256 int pageno;
1257 fz_point p;
1258 fz_obj *obj;
1260 pageno = -1;
1261 obj = fz_arrayget (link->dest, 0);
1262 if (fz_isindirect (obj)) {
1263 pageno = pdf_findpageobject (state.xref, obj) - 1;
1265 else if (fz_isint (obj)) {
1266 pageno = fz_toint (obj);
1269 if (fz_arraylen (link->dest) > 3) {
1270 p.x = fz_toint (fz_arrayget (link->dest, 2));
1271 p.y = fz_toint (fz_arrayget (link->dest, 3));
1272 p = fz_transformpoint (page->pagedim->ctm, p);
1274 else {
1275 p.x = 0.0;
1276 p.y = 0.0;
1279 tup_v = caml_alloc_tuple (2);
1280 ret_v = caml_alloc_small (1, 1);
1281 Field (tup_v, 0) = Val_int (pageno);
1282 Field (tup_v, 1) = Val_int (p.y);
1283 Field (ret_v, 0) = tup_v;
1285 break;
1287 case PDF_LURI:
1288 ret_v = caml_alloc_small (1, 0);
1289 Field (ret_v, 0) = caml_copy_string (fz_tostrbuf (link->dest));
1290 break;
1292 default:
1293 printd (state.sock, "T unhandled link kind %d", link->kind);
1294 break;
1297 unlock ("ml_getlink");
1299 done:
1300 CAMLreturn (ret_v);
1303 CAMLprim value ml_gettext (value ptr_v, value rect_v, value oy_v, value rectsel_v)
1305 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1306 fz_matrix ctm;
1307 fz_point p1, p2;
1308 struct page *page;
1309 fz_textspan *span;
1310 char *s = String_val (ptr_v);
1311 int rectsel = Bool_val (rectsel_v);
1312 int i, bx0, bx1, by0, by1, x0, x1, y0, y1, oy;
1314 /* stop GCC from complaining about uninitialized variables */
1315 #ifdef __GNUC__
1316 int rx0 = rx0, rx1 = rx1, ry0 = ry0, ry1 = ry1;
1317 #else
1318 int rx0, rx1, ry0, ry1;
1319 #endif
1321 if (trylock ("ml_gettext")) {
1322 goto done;
1325 page = parse_pointer ("ml_gettext", s);
1327 oy = Int_val (oy_v);
1328 p1.x = Int_val (Field (rect_v, 0));
1329 p1.y = Int_val (Field (rect_v, 1));
1330 p2.x = Int_val (Field (rect_v, 2));
1331 p2.y = Int_val (Field (rect_v, 3));
1333 if (0) {
1334 glEnable (GL_BLEND);
1335 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1336 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1337 glColor4f (0.0f, 0.0f, 0.0f, 0.2f);
1338 glRecti (p1.x, p1.y, p2.x, p2.y);
1339 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1340 glDisable (GL_BLEND);
1343 ctm = page->pagedim->ctm;
1344 if (!page->text) {
1345 fz_error error;
1346 fz_device *tdev;
1348 page->text = fz_newtextspan ();
1349 tdev = fz_newtextdevice (page->text);
1350 error = pdf_runpage (state.xref, page->drawpage, tdev,
1351 page->pagedim->ctm);
1352 if (error) die (error);
1353 fz_freedevice (tdev);
1356 printf ("\033c");
1358 printf ("BBox %f %f %f %f\n", p1.x, p1.y, p2.x, p2.y);
1359 p1.x += page->pixmap->x;
1360 p1.y += page->pixmap->y;
1361 p2.x += page->pixmap->x;
1362 p2.y += page->pixmap->y;
1363 x0 = p1.x;
1364 y0 = p1.y;
1365 x1 = p2.x;
1366 y1 = p2.y;
1367 printf ("BBox %d %d %d %d %d %d\n", x0, y0, x1, y1, oy, page->pageno);
1369 for (span = page->text; span; span = span->next) {
1370 int seen = 0;
1372 /* fz_debugtextspanxml (span); */
1373 for (i = 0; i < span->len; ++i) {
1374 long c;
1376 bx0 = span->text[i].bbox.x0;
1377 bx1 = span->text[i].bbox.x1;
1378 by0 = span->text[i].bbox.y0 + oy;
1379 by1 = span->text[i].bbox.y1 + oy;
1381 if ((bx1 >= x0 && bx0 <= x1 && by1 >= y0 && by0 <= y1)) {
1382 if (!seen) {
1383 rx0 = bx0 - page->pixmap->x;
1384 rx1 = bx1 - page->pixmap->x;
1385 ry0 = by0;
1386 ry1 = by1;
1389 seen = 1;
1390 c = span->text[i].c;
1391 if (c < 256) {
1392 if ((isprint (c) && !isspace (c))) {
1393 if (!rectsel) {
1394 bx0 -= page->pixmap->x;
1395 bx1 -= page->pixmap->x;
1396 glEnable (GL_BLEND);
1397 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1398 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1399 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1400 glRecti (bx0, by0, bx1, by1);
1401 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1402 glDisable (GL_BLEND);
1404 if (isprint (c) || c ==' ') {
1405 rx1 = bx1;
1406 ry1 = by1;
1409 putc (c, stdout);
1411 else {
1412 putc ('?', stdout);
1417 if (rectsel) {
1418 if (seen) {
1419 glEnable (GL_BLEND);
1420 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1421 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1422 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1423 glRecti (rx0, ry0, rx1, ry1);
1424 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1425 glDisable (GL_BLEND);
1429 if (seen && span->eol) {
1430 x0 = page->pixmap->x;
1431 putc ('\n', stdout);
1434 unlock ("ml_gettext");
1436 done:
1437 CAMLreturn (Val_unit);
1440 CAMLprim value ml_getpagewh (value pagedimno_v)
1442 CAMLparam1 (pagedimno_v);
1443 CAMLlocal1 (ret_v);
1444 int pagedimno = Int_val (pagedimno_v);
1446 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1447 Store_double_field (ret_v, 0, state.pagedims[pagedimno].box.x0);
1448 Store_double_field (ret_v, 1, state.pagedims[pagedimno].box.x1);
1449 Store_double_field (ret_v, 2, state.pagedims[pagedimno].box.y0);
1450 Store_double_field (ret_v, 3, state.pagedims[pagedimno].box.y1);
1451 CAMLreturn (ret_v);
1454 CAMLprim value ml_init (value sock_v)
1456 #ifndef _WIN32
1457 int ret;
1458 #endif
1459 CAMLparam1 (sock_v);
1461 state.texcount = 256;
1462 state.sliceheight = 24;
1463 state.texform = GL_RGBA;
1464 state.texty = GL_UNSIGNED_BYTE;
1466 fz_accelerate ();
1467 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1468 if (!state.texids) {
1469 err (1, "calloc texids " FMT_s,
1470 state.texcount * sizeof (*state.texids));
1473 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1474 if (!state.texowners) {
1475 err (1, "calloc texowners " FMT_s,
1476 state.texcount * sizeof (*state.texowners));
1479 glGenTextures (state.texcount, state.texids);
1481 #ifdef _WIN32
1482 state.sock = Socket_val (sock_v);
1483 #else
1484 state.sock = Int_val (sock_v);
1485 #endif
1487 state.cache = fz_newglyphcache ();
1488 if (!state.cache) {
1489 errx (1, "fz_newglyphcache failed");
1492 #ifdef _WIN32
1493 InitializeCriticalSection (&critsec);
1494 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
1495 if (state.thread == INVALID_HANDLE_VALUE) {
1496 errx (1, "CreateThread failed: %lx", GetLastError ());
1498 #else
1499 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1500 if (ret) {
1501 errx (1, "pthread_create: %s", strerror (errno));
1503 #endif
1505 CAMLreturn (Val_unit);