Partial and flawed solution to rotation problem
[llpp.git] / link.c
blob99d47152ceb0b8d2558a08e3ada2cec9a7c83212
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 #pragma warning (disable:4244)
8 #pragma warning (disable:4996)
9 #pragma warning (disable:4995)
10 #endif
12 #ifdef _MSC_VER
13 #include <errno.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <stdlib.h>
17 static void __declspec (noreturn) err (int exitcode, const char *fmt, ...)
19 va_list ap;
20 int errcode;
22 errcode = errno;
23 va_start (ap, fmt);
24 vfprintf (stderr, fmt, ap);
25 va_end (ap);
26 fprintf (stderr, ": %s\n", strerror (errno));
27 exit (exitcode);
29 static void __declspec (noreturn) errx (int exitcode, const char *fmt, ...)
31 va_list ap;
32 int errcode;
34 errcode = errno;
35 va_start (ap, fmt);
36 vfprintf (stderr, fmt, ap);
37 va_end (ap);
38 fputc ('\n', stderr);
39 exit (exitcode);
41 static void __declspec (noreturn) sockerr (int exitcode, const char *fmt, ...)
43 va_list ap;
44 int errcode;
46 errcode = errno;
47 va_start (ap, fmt);
48 vfprintf (stderr, fmt, ap);
49 va_end (ap);
50 fprintf (stderr, ": wsaerror %lx\n", WSAGetLastError ());
51 exit (exitcode);
53 #else
54 #define _GNU_SOURCE
55 #include <err.h>
56 #define sockerr err
57 #endif
58 #include <regex.h>
59 #include <errno.h>
60 #include <ctype.h>
61 #include <stdio.h>
62 #include <stdarg.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #ifndef _WIN32
66 #include <pthread.h>
67 #include <sys/time.h>
68 #include <sys/types.h>
69 #include <sys/socket.h>
70 #include <sys/select.h>
71 #endif
73 #ifdef __APPLE__
74 #include <OpenGL/gl.h>
75 #else
76 #include <GL/gl.h>
77 #endif
79 #ifndef GL_TEXTURE_RECTANGLE_ARB
80 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
81 #endif
83 #include <caml/fail.h>
84 #include <caml/alloc.h>
85 #include <caml/memory.h>
86 #include <caml/unixsupport.h>
88 #include <fitz.h>
89 #include <mupdf.h>
91 #if 0
92 #define lprintf printf
93 #else
94 #define lprintf(...)
95 #endif
97 #define ARSERT(cond) for (;;) { \
98 if (!(cond)) { \
99 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
101 break; \
104 struct slice {
105 int texindex;
106 int w, h;
109 struct page {
110 int pageno;
111 int slicecount;
112 fz_textspan *text;
113 fz_pixmap *pixmap;
114 pdf_page *drawpage;
115 struct pagedim *pagedim;
116 struct page *prev;
117 struct slice slices[];
120 struct pagedim {
121 int pageno;
122 int rotate;
123 fz_rect box;
124 fz_bbox bbox;
125 fz_matrix ctm, ctm1;
128 struct {
129 int sock;
130 int sliceheight;
131 struct page *pages;
132 struct pagedim *pagedims;
133 int pagecount;
134 int pagedimcount;
135 pdf_xref *xref;
136 fz_glyphcache *cache;
137 int w, h;
139 int texindex;
140 int texcount;
141 GLuint *texids;
143 GLenum texform;
144 GLenum texty;
146 int lotsamemory;
148 int *pagetbl;
149 struct {
150 int w, h;
151 struct slice *slice;
152 } *texowners;
154 int rotate;
156 #ifdef _WIN32
157 HANDLE thread;
158 #else
159 pthread_t thread;
160 #endif
161 } state;
163 #ifdef _WIN32
164 static CRITICAL_SECTION critsec;
166 static void lock (void *unused)
168 (void) unused;
169 EnterCriticalSection (&critsec);
172 static void unlock (void *unused)
174 (void) unused;
175 LeaveCriticalSection (&critsec);
178 static int trylock (void *unused)
180 return TryEnterCriticalSection (&critsec) == 0;
182 #else
183 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
185 static void lock (const char *cap)
187 int ret = pthread_mutex_lock (&mutex);
188 if (ret) {
189 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
193 static void unlock (const char *cap)
195 int ret = pthread_mutex_unlock (&mutex);
196 if (ret) {
197 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
201 static int trylock (const char *cap)
203 int ret = pthread_mutex_trylock (&mutex);
205 if (ret && ret != EBUSY) {
206 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
208 return ret == EBUSY;
210 #endif
212 static void *parse_pointer (const char *cap, const char *s)
214 int ret;
215 void *ptr;
217 ret = sscanf (s, "%p", &ptr);
218 if (ret != 1) {
219 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
221 return ptr;
224 static int hasdata (int sock)
226 int n;
227 fd_set s;
228 struct timeval tv;
229 FD_ZERO (&s);
230 FD_SET (sock, &s);
231 tv.tv_sec = 0;
232 tv.tv_usec = 0;
233 n = select (sock + 1, &s, NULL, NULL, &tv);
234 if (n == 0) return 0;
235 if (n == 1) return 1;
236 sockerr (1, "hasdata: select error ret=%d", n);
239 static double now (void)
241 struct timeval tv;
243 if (gettimeofday (&tv, NULL)) {
244 err (1, "gettimeofday");
246 return tv.tv_sec + tv.tv_usec*1e-6;
249 static void readdata (int fd, char *p, int size)
251 ssize_t n;
253 n = recv (fd, p, size, 0);
254 if (n - size) {
255 if (!n) errx (1, "EOF while reading");
256 sockerr (1, "recv (req %d, ret %zd)", size, n);
260 static void writedata (int fd, char *p, int size)
262 char buf[4];
263 ssize_t n;
265 buf[0] = (size >> 24) & 0xff;
266 buf[1] = (size >> 16) & 0xff;
267 buf[2] = (size >> 8) & 0xff;
268 buf[3] = (size >> 0) & 0xff;
270 n = send (fd, buf, 4, 0);
271 if (n != 4) {
272 if (!n) errx (1, "EOF while writing length");
273 sockerr (1, "send %zd", n);
276 n = send (fd, p, size, 0);
277 if (n - size) {
278 if (!n) errx (1, "EOF while writing data");
279 sockerr (1, "send (req %d, ret %zd)", size, n);
283 static void
284 #ifdef __GNUC__
285 __attribute__ ((format (printf, 2, 3)))
286 #endif
287 printd (int fd, const char *fmt, ...)
289 int len;
290 va_list ap;
291 char buf[200];
293 va_start (ap, fmt);
294 len = vsnprintf (buf, sizeof (buf), fmt, ap);
295 va_end (ap);
296 writedata (fd, buf, len);
299 static void die (fz_error error)
301 fz_catch (error, "aborting");
302 if (state.xref)
303 pdf_closexref (state.xref);
304 exit (1);
307 static void openxref (char *filename)
309 int fd;
310 fz_error error;
311 fz_stream *file;
313 fd = open (filename, O_BINARY | O_RDONLY, 0666);
314 if (fd < 0)
315 die (fz_throw ("cannot open file '%s'", filename));
317 file = fz_openfile (fd);
318 state.xref = pdf_openxref (file);
319 if (!state.xref)
320 die (fz_throw ("cannot open PDF file '%s'", filename));
321 fz_dropstream (file);
323 if (pdf_needspassword (state.xref)) {
324 die (fz_throw ("password protected"));
327 error = pdf_loadpagetree (state.xref);
328 if (error) {
329 die (fz_throw ("cannot load page tree"));
332 state.pagecount = pdf_getpagecount (state.xref);
333 state.pagetbl = stat_alloc (state.pagecount * sizeof (*state.pagetbl));
336 static int readlen (int fd)
338 ssize_t n;
339 char p[4];
341 n = recv (fd, p, 4, 0);
342 if (n != 4) {
343 if (!n) errx (1, "EOF while reading length");
344 sockerr (1, "recv %zd", n);
347 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
350 static void freepage (struct page *page)
352 int i;
353 struct page *p;
355 fz_droppixmap (page->pixmap);
356 for (p = state.pages; p; p = p->prev) {
357 if (p->prev == page) {
358 p->prev = page->prev;
359 break;
362 for (i = 0; i < page->slicecount; ++i) {
363 struct slice *s = &page->slices[i];
364 if (s->texindex != -1) {
365 if (state.texowners[s->texindex].slice == s) {
366 state.texowners[s->texindex].slice = NULL;
367 ARSERT (state.texowners[s->texindex].w == s->w);
368 ARSERT (state.texowners[s->texindex].h >= s->h);
372 if (page->text) {
373 fz_freetextspan (page->text);
375 if (page->drawpage) {
376 pdf_freepage (page->drawpage);
379 free (page);
382 static void subdivide (struct page *p)
384 int i;
385 int h = p->pixmap->h;
386 int th = MIN (h, state.sliceheight);
388 for (i = 0; i < p->slicecount; ++i) {
389 struct slice *s = &p->slices[i];
390 s->texindex = -1;
391 s->h = MIN (th, h);
392 s->w = p->pixmap->w;
393 h -= th;
397 static void *render (int pageno, int pindex)
399 fz_error error;
400 int slicecount;
401 fz_obj *pageobj;
402 struct page *page;
403 double start, end;
404 pdf_page *drawpage;
405 fz_device *idev;
406 struct pagedim *pagedim;
408 start = now ();
409 printd (state.sock, "V rendering %d", pageno);
410 pdf_flushxref (state.xref, 0);
412 pagedim = &state.pagedims[pindex];
413 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
414 + state.sliceheight - 1) / state.sliceheight;
415 slicecount += slicecount == 0;
417 page = calloc (sizeof (*page)
418 + (slicecount * sizeof (struct slice)), 1);
419 if (!page) {
420 err (1, "calloc page %d\n", pageno);
422 page->slicecount = slicecount;
423 page->prev = state.pages;
424 state.pages = page;
426 pageobj = pdf_getpageobject (state.xref, pageno);
427 if (!pageobj)
428 die (fz_throw ("cannot retrieve info from page %d", pageno));
430 error = pdf_loadpage (&drawpage, state.xref, pageobj);
431 if (error)
432 die (error);
434 page->pixmap = fz_newpixmapwithrect (pdf_devicergb, pagedim->bbox);
435 fz_clearpixmap (page->pixmap, 0xFF);
437 idev = fz_newdrawdevice (state.cache, page->pixmap);
438 if (!idev)
439 die (fz_throw ("fz_newdrawdevice failed"));
440 error = pdf_runcontentstream (idev, pagedim->ctm, state.xref,
441 drawpage->resources,
442 drawpage->contents);
443 fz_freedevice (idev);
445 page->drawpage = drawpage;
446 page->pagedim = pagedim;
447 page->pageno = pageno;
448 subdivide (page);
449 end = now ();
451 if (!state.lotsamemory) {
452 pdf_agestoreditems (state.xref->store);
453 pdf_evictageditems (state.xref->store);
456 printd (state.sock, "V rendering %d took %f sec", pageno, end - start);
457 return page;
460 static void initpdims (void)
462 int pageno;
463 double start, end;
465 start = now ();
466 for (pageno = 0; pageno < state.pagecount; ++pageno) {
467 int rotate;
468 fz_rect box;
469 struct pagedim *p;
470 fz_obj *obj, *pageobj;
472 pageobj = pdf_getpageobject (state.xref, pageno + 1);
474 obj = fz_dictgets (pageobj, "CropBox");
475 if (!fz_isarray (obj)) {
476 obj = fz_dictgets (pageobj, "MediaBox");
477 if (!fz_isarray (obj)) {
478 die (fz_throw ("cannot find page bounds %d (%d Rd)",
479 fz_tonum (pageobj), fz_togen (pageobj)));
482 box = pdf_torect (obj);
484 obj = fz_dictgets (pageobj, "Rotate");
485 if (fz_isint (obj)) {
486 rotate = fz_toint (obj);
488 else {
489 rotate = 0;
491 rotate += state.rotate;
493 state.pagetbl[pageno] = fz_tonum (state.xref->pagerefs[pageno]);
494 p = &state.pagedims[state.pagedimcount - 1];
495 if ((state.pagedimcount == 0)
496 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
497 size_t size;
499 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
500 state.pagedims = realloc (state.pagedims, size);
501 if (!state.pagedims) {
502 err (1, "realloc pagedims to %zu (%d elems)",
503 size, state.pagedimcount + 1);
505 p = &state.pagedims[state.pagedimcount++];
506 p->rotate = rotate;
507 p->box = box;
508 p->pageno = pageno;
511 end = now ();
512 printd (state.sock, "T Processed %d pages in %f seconds",
513 state.pagecount, end - start);
516 static void layout (void)
518 int pindex;
519 fz_matrix ctm;
520 fz_rect box, box2;
521 double zoom, w;
522 struct pagedim *p = state.pagedims;
524 pindex = 0;
525 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
526 box.x0 = MIN (p->box.x0, p->box.x1);
527 box.y0 = MIN (p->box.y0, p->box.y1);
528 box.x1 = MAX (p->box.x0, p->box.x1);
529 box.y1 = MAX (p->box.y0, p->box.y1);
531 ctm = fz_identity ();
532 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
533 ctm = fz_concat (ctm, fz_rotate (p->rotate));
534 box2 = fz_transformrect (ctm, box);
535 w = box2.x1 - box2.x0;
537 zoom = (state.w / w);
538 ctm = fz_identity ();
539 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
540 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
541 memcpy (&p->ctm1, &ctm, sizeof (ctm));
542 ctm = fz_concat (ctm, fz_rotate (p->rotate));
543 p->bbox = fz_roundrect (fz_transformrect (ctm, box));
544 memcpy (&p->ctm, &ctm, sizeof (ctm));
547 while (p-- != state.pagedims) {
548 printd (state.sock, "l %d %d %d",
549 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0);
553 static void recurse_outline (pdf_outline *outline, int level)
555 while (outline) {
556 int i, num;
557 fz_obj *obj;
558 int top = 0;
559 int pageno = -1;
561 if (!outline->link) goto next;
563 obj = outline->link->dest;
564 if (fz_isindirect (obj)) {
565 num = fz_tonum (obj);
567 for (i = 0; i < state.pagecount; ++i) {
568 if (state.pagetbl[i] == num) {
569 pageno = i;
570 break;
574 else if (fz_isarray (obj)) {
575 fz_obj *obj2;
577 obj2 = fz_arrayget (obj, 0);
578 if (fz_isint (obj2)) {
579 pageno = fz_toint (obj2);
581 else {
582 num = fz_tonum (obj2);
583 for (i = 0; i < state.pagecount; ++i) {
584 if (state.pagetbl[i] == num) {
585 pageno = i;
586 break;
591 if (fz_arraylen (obj) > 3) {
592 fz_point p;
593 struct pagedim *pagedim = state.pagedims;
595 for (i = 0; i < state.pagedimcount; ++i) {
596 if (state.pagedims[i].pageno > pageno)
597 break;
598 pagedim = &state.pagedims[i];
601 p.x = fz_toint (fz_arrayget (obj, 2));
602 p.y = fz_toint (fz_arrayget (obj, 3));
603 p = fz_transformpoint (pagedim->ctm, p);
604 top = p.y;
608 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
609 printd (state.sock, "o %d %d %d %s",
610 level, pageno, top, outline->title);
611 next:
612 if (outline->child) {
613 recurse_outline (outline->child, level + 1);
615 outline = outline->next;
619 static void process_outline (void)
621 pdf_outline *outline;
623 outline = pdf_loadoutline (state.xref);
624 if (outline) {
625 recurse_outline (outline, 0);
626 pdf_freeoutline (outline);
630 static int comparespans (const void *l, const void *r)
632 fz_textspan const *const*ls = l;
633 fz_textspan const *const*rs = r;
634 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
637 /* wishful thinking function */
638 static void search (regex_t *re, int pageno, int y, int forward)
640 int i, j;
641 int ret;
642 char *p;
643 char buf[256];
644 fz_matrix ctm;
645 fz_error error;
646 fz_obj *pageobj;
647 fz_device *tdev;
648 pdf_page *drawpage;
649 fz_textspan *text, *span, **pspan;
650 struct pagedim *pdim, *pdimprev;
651 int stop = 0;
652 int niters = 0;
653 int nspans;
654 double start, end;
656 start = now ();
657 while (pageno >= 0 && pageno < state.pagecount && !stop) {
658 if (niters++ == 5) {
659 if (!state.lotsamemory) {
660 pdf_agestoreditems (state.xref->store);
661 pdf_evictageditems (state.xref->store);
663 niters = 0;
664 if (hasdata (state.sock)) {
665 printd (state.sock, "T attention requested aborting search at %d",
666 pageno);
667 stop = 1;
669 else {
670 printd (state.sock, "T searching in page %d", pageno);
673 pdimprev = NULL;
674 for (i = 0; i < state.pagedimcount; ++i) {
675 pdim = &state.pagedims[i];
676 if (pdim->pageno == pageno) {
677 goto found;
679 if (pdim->pageno > pageno) {
680 pdim = pdimprev;
681 goto found;
683 pdimprev = pdim;
685 pdim = pdimprev;
686 found:
688 pageobj = pdf_getpageobject (state.xref, pageno + 1);
689 if (!pageobj)
690 die (fz_throw ("cannot retrieve info from page %d", pageno));
692 error = pdf_loadpage (&drawpage, state.xref, pageobj);
693 if (error)
694 die (error);
696 ctm = fz_rotate (pdim->rotate);
698 text = fz_newtextspan ();
699 tdev = fz_newtextdevice (text);
700 error = pdf_runcontentstream (tdev, pdim->ctm1, state.xref,
701 drawpage->resources,
702 drawpage->contents);
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 %zu", 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");
1099 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1101 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
1102 struct slice *slice = &page->slices[slicenum];
1103 if (slice->h > py) {
1104 break;
1106 py -= slice->h;
1109 h = MIN (state.h, h);
1110 while (h) {
1111 int th;
1112 struct slice *slice = &page->slices[slicenum];
1114 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
1116 th = MIN (h, slice->h - py);
1117 upload2 (page, slicenum, "upload");
1119 glBegin (GL_QUADS);
1121 glTexCoord2i (0, py);
1122 glVertex2i (0, dispy);
1124 glTexCoord2i (w, py);
1125 glVertex2i (w, dispy);
1127 glTexCoord2i (w, py+th);
1128 glVertex2i (w, dispy + th);
1130 glTexCoord2i (0, py+th);
1131 glVertex2i (0, dispy + th);
1133 glEnd ();
1135 h -= th;
1136 py = 0;
1137 dispy += th;
1138 slicenum += 1;
1141 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1143 unlock ("ml_draw");
1144 done:
1145 CAMLreturn (Val_unit);
1148 static pdf_link *getlink (struct page *page, int x, int y)
1150 fz_point p;
1151 fz_matrix ctm;
1152 pdf_link *link;
1154 p.x = x + page->pixmap->x;
1155 p.y = y + page->pixmap->y;
1157 ctm = fz_invertmatrix (page->pagedim->ctm);
1158 p = fz_transformpoint (ctm, p);
1160 for (link = page->drawpage->links; link; link = link->next) {
1161 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1162 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1163 if (link->kind == PDF_LGOTO) {
1164 return link;
1169 return NULL;
1172 CAMLprim value ml_highlightlinks (value ptr_v, value yoff_v)
1174 CAMLparam2 (ptr_v, yoff_v);
1175 pdf_link *link;
1176 struct page *page;
1177 int xoff, yoff = Int_val (yoff_v);
1178 const char *s = String_val (ptr_v);
1180 if (trylock ("ml_highlightlinks")) {
1181 goto done;
1184 page = parse_pointer ("ml_highlightlinks", s);
1186 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1187 glEnable (GL_LINE_STIPPLE);
1188 glLineStipple (0.5, 0xcccc);
1189 glColor3ub (255, 0, 0);
1191 xoff = -page->pixmap->x;
1192 yoff -= page->pixmap->y;
1194 glBegin (GL_QUADS);
1195 for (link = page->drawpage->links; link; link = link->next) {
1196 if (link->kind == PDF_LGOTO) {
1197 fz_point p1, p2, p3, p4;
1198 fz_matrix ctm = page->pagedim->ctm;
1200 p1.x = link->rect.x0;
1201 p1.y = link->rect.y0;
1203 p2.x = link->rect.x1;
1204 p2.y = link->rect.y0;
1206 p3.x = link->rect.x1;
1207 p3.y = link->rect.y1;
1209 p4.x = link->rect.x0;
1210 p4.y = link->rect.y1;
1212 p1 = fz_transformpoint (ctm, p1);
1213 p2 = fz_transformpoint (ctm, p2);
1214 p3 = fz_transformpoint (ctm, p3);
1215 p4 = fz_transformpoint (ctm, p4);
1217 glVertex2f (p1.x + xoff, p1.y + yoff);
1218 glVertex2f (p2.x + xoff, p2.y + yoff);
1219 glVertex2f (p3.x + xoff, p3.y + yoff);
1220 glVertex2f (p4.x + xoff, p4.y + yoff);
1223 glEnd ();
1225 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1226 glDisable (GL_LINE_STIPPLE);
1227 unlock ("ml_highlightlinks");
1229 done:
1230 CAMLreturn (Val_unit);
1233 CAMLprim value ml_checklink (value ptr_v, value x_v, value y_v)
1235 CAMLparam3 (ptr_v, x_v, y_v);
1236 char *s = String_val (ptr_v);
1237 int ret;
1239 if (trylock ("ml_checklink")) {
1240 ret = 0;
1242 else {
1243 ret = NULL != getlink (parse_pointer ("ml_checklink", s),
1244 Int_val (x_v), Int_val (y_v));
1245 unlock ("ml_checklink");
1247 CAMLreturn (Val_bool (ret));
1250 CAMLprim value ml_getlink (value ptr_v, value x_v, value y_v)
1252 CAMLparam3 (ptr_v, x_v, y_v);
1253 CAMLlocal2 (ret_v, tup_v);
1254 pdf_link *link;
1255 struct page *page;
1256 char *s = String_val (ptr_v);
1258 if (trylock ("ml_getlink")) {
1259 ret_v = Val_int (0);
1260 goto done;
1263 page = parse_pointer ("ml_getlink", s);
1265 link = getlink (page, Int_val (x_v), Int_val (y_v));
1266 if (link) {
1267 int pageno;
1268 fz_point p;
1269 fz_obj *obj;
1271 pageno = -1;
1272 obj = fz_arrayget (link->dest, 0);
1273 if (fz_isindirect (obj)) {
1274 pageno = pdf_findpageobject (state.xref, obj) - 1;
1276 else if (fz_isint (obj)) {
1277 pageno = fz_toint (obj);
1280 if (fz_arraylen (link->dest) > 3) {
1281 p.x = fz_toint (fz_arrayget (link->dest, 2));
1282 p.y = fz_toint (fz_arrayget (link->dest, 3));
1283 p = fz_transformpoint (page->pagedim->ctm, p);
1285 else {
1286 p.x = 0.0;
1287 p.y = 0.0;
1290 tup_v = caml_alloc_tuple (2);
1291 ret_v = caml_alloc_small (1, 1);
1292 Field (tup_v, 0) = Val_int (pageno);
1293 Field (tup_v, 1) = Val_int (p.y);
1294 Field (ret_v, 0) = tup_v;
1296 else {
1297 ret_v = Val_int (0);
1299 unlock ("ml_getlink");
1301 done:
1302 CAMLreturn (ret_v);
1305 CAMLprim value ml_gettext (value ptr_v, value rect_v, value oy_v, value rectsel_v)
1307 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1308 fz_matrix ctm;
1309 fz_point p1, p2;
1310 struct page *page;
1311 fz_textspan *span;
1312 char *s = String_val (ptr_v);
1313 int rectsel = Bool_val (rectsel_v);
1314 int i, bx0, bx1, by0, by1, x0, x1, y0, y1, oy;
1316 /* stop GCC from complaining about uninitialized variables */
1317 #ifdef __GNUC__
1318 int rx0 = rx0, rx1 = rx1, ry0 = ry0, ry1 = ry1;
1319 #else
1320 int rx0, rx1, ry0, ry1;
1321 #endif
1323 if (trylock ("ml_gettext")) {
1324 goto done;
1327 page = parse_pointer ("ml_gettext", s);
1329 oy = Int_val (oy_v);
1330 p1.x = Int_val (Field (rect_v, 0));
1331 p1.y = Int_val (Field (rect_v, 1));
1332 p2.x = Int_val (Field (rect_v, 2));
1333 p2.y = Int_val (Field (rect_v, 3));
1335 if (0) {
1336 glEnable (GL_BLEND);
1337 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1338 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1339 glColor4f (0.0f, 0.0f, 0.0f, 0.2f);
1340 glRecti (p1.x, p1.y, p2.x, p2.y);
1341 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1342 glDisable (GL_BLEND);
1345 ctm = page->pagedim->ctm;
1346 if (!page->text) {
1347 fz_error error;
1348 fz_device *tdev;
1350 page->text = fz_newtextspan ();
1351 tdev = fz_newtextdevice (page->text);
1352 error = pdf_runcontentstream (tdev, page->pagedim->ctm, state.xref,
1353 page->drawpage->resources,
1354 page->drawpage->contents);
1355 if (error) die (error);
1356 fz_freedevice (tdev);
1359 printf ("\033c");
1361 printf ("BBox %f %f %f %f\n", p1.x, p1.y, p2.x, p2.y);
1362 p1.x += page->pixmap->x;
1363 p1.y += page->pixmap->y;
1364 p2.x += page->pixmap->x;
1365 p2.y += page->pixmap->y;
1366 x0 = p1.x;
1367 y0 = p1.y;
1368 x1 = p2.x;
1369 y1 = p2.y;
1370 printf ("BBox %d %d %d %d %d %d\n", x0, y0, x1, y1, oy, page->pageno);
1372 for (span = page->text; span; span = span->next) {
1373 int seen = 0;
1375 /* fz_debugtextspanxml (span); */
1376 for (i = 0; i < span->len; ++i) {
1377 long c;
1379 bx0 = span->text[i].bbox.x0;
1380 bx1 = span->text[i].bbox.x1;
1381 by0 = span->text[i].bbox.y0 + oy;
1382 by1 = span->text[i].bbox.y1 + oy;
1384 if ((bx1 >= x0 && bx0 <= x1 && by1 >= y0 && by0 <= y1)) {
1385 if (!seen) {
1386 rx0 = bx0 - page->pixmap->x;
1387 rx1 = bx1 - page->pixmap->x;
1388 ry0 = by0;
1389 ry1 = by1;
1392 seen = 1;
1393 c = span->text[i].c;
1394 if (c < 256) {
1395 if ((isprint (c) && !isspace (c))) {
1396 if (!rectsel) {
1397 bx0 -= page->pixmap->x;
1398 bx1 -= page->pixmap->x;
1399 glEnable (GL_BLEND);
1400 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1401 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1402 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1403 glRecti (bx0, by0, bx1, by1);
1404 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1405 glDisable (GL_BLEND);
1407 if (isprint (c) || c ==' ') {
1408 rx1 = bx1;
1409 ry1 = by1;
1412 putc (c, stdout);
1414 else {
1415 putc ('?', stdout);
1420 if (rectsel) {
1421 if (seen) {
1422 glEnable (GL_BLEND);
1423 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1424 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1425 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1426 glRecti (rx0, ry0, rx1, ry1);
1427 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1428 glDisable (GL_BLEND);
1432 if (seen && span->eol) {
1433 x0 = page->pixmap->x;
1434 putc ('\n', stdout);
1437 unlock ("ml_gettext");
1439 done:
1440 CAMLreturn (Val_unit);
1443 CAMLprim value ml_getpagewh (value pagedimno_v)
1445 CAMLparam1 (pagedimno_v);
1446 CAMLlocal1 (ret_v);
1447 int pagedimno = Int_val (pagedimno_v);
1449 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1450 Store_double_field (ret_v, 0, state.pagedims[pagedimno].box.x0);
1451 Store_double_field (ret_v, 1, state.pagedims[pagedimno].box.x1);
1452 Store_double_field (ret_v, 2, state.pagedims[pagedimno].box.y0);
1453 Store_double_field (ret_v, 3, state.pagedims[pagedimno].box.y1);
1454 CAMLreturn (ret_v);
1457 CAMLprim value ml_init (value sock_v)
1459 #ifndef _WIN32
1460 int ret;
1461 #endif
1462 CAMLparam1 (sock_v);
1464 state.texcount = 256;
1465 state.sliceheight = 24;
1466 state.texform = GL_RGBA;
1467 state.texty = GL_UNSIGNED_BYTE;
1469 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1470 if (!state.texids) {
1471 err (1, "calloc texids %zu", state.texcount * sizeof (*state.texids));
1474 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1475 if (!state.texowners) {
1476 err (1, "calloc texowners %zu",
1477 state.texcount * sizeof (*state.texowners));
1480 glGenTextures (state.texcount, state.texids);
1482 #ifdef _WIN32
1483 state.sock = Socket_val (sock_v);
1484 #else
1485 state.sock = Int_val (sock_v);
1486 #endif
1488 state.cache = fz_newglyphcache ();
1489 if (!state.cache) {
1490 errx (1, "fz_newglyphcache failed");
1493 #ifdef _WIN32
1494 InitializeCriticalSection (&critsec);
1495 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
1496 if (state.thread == INVALID_HANDLE_VALUE) {
1497 errx (1, "CreateThread failed: %lx", GetLastError ());
1499 #else
1500 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1501 if (ret) {
1502 errx (1, "pthread_create: %s", strerror (errno));
1504 #endif
1506 CAMLreturn (Val_unit);