1 /* lot's of code c&p-ed directly from mupdf */
16 /* fugly as hell and GCC specific but... */
18 #define GL_GLEXT_PROTOTYPES
24 #include <caml/fail.h>
25 #include <caml/alloc.h>
26 #include <caml/memory.h>
27 #include <caml/unixsupport.h>
33 #define lprintf printf
38 #define ARSERT(cond) for (;;) { \
40 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
56 struct pagedim
*pagedim
;
58 struct slice slices
[];
74 struct pagedim
*pagedims
;
99 static pthread_mutex_t mutex
= PTHREAD_MUTEX_INITIALIZER
;
101 static void lock (const char *cap
)
103 int ret
= pthread_mutex_lock (&mutex
);
105 errx (1, "%s: pthread_mutex_lock: %s", cap
, strerror (ret
));
109 static void unlock (const char *cap
)
111 int ret
= pthread_mutex_unlock (&mutex
);
113 errx (1, "%s: pthread_mutex_unlock: %s", cap
, strerror (ret
));
117 static int trylock (const char *cap
)
119 int ret
= pthread_mutex_trylock (&mutex
);
121 if (ret
&& ret
!= EBUSY
) {
122 errx (1, "%s: pthread_mutex_trylock: %s", cap
, strerror (ret
));
127 static void *parse_pointer (const char *cap
, const char *s
)
132 ret
= sscanf (s
, "%p", &ptr
);
134 errx (1, "%s: cannot parse pointer in `%s'", cap
, s
);
139 static int hasdata (int sock
)
146 ret
= poll (&pfd
, 1, 0);
153 return pfd
.revents
& POLLIN
;
156 static double now (void)
160 if (gettimeofday (&tv
, NULL
)) {
161 err (1, "gettimeofday");
163 return tv
.tv_sec
+ tv
.tv_usec
*1e-6;
166 static void readdata (int fd
, char *p
, int size
)
170 n
= read (fd
, p
, size
);
172 err (1, "read (req %d, ret %zd)", size
, n
);
176 static void writedata (int fd
, char *p
, int size
)
181 buf
[0] = (size
>> 24) & 0xff;
182 buf
[1] = (size
>> 16) & 0xff;
183 buf
[2] = (size
>> 8) & 0xff;
184 buf
[3] = (size
>> 0) & 0xff;
186 n
= write (fd
, buf
, 4);
188 err (1, "write %zd", n
);
191 n
= write (fd
, p
, size
);
193 err (1, "write (req %d, ret %zd)", size
, n
);
197 static void __attribute__ ((format (printf
, 2, 3)))
198 printd (int fd
, const char *fmt
, ...)
205 len
= vsnprintf (buf
, sizeof (buf
), fmt
, ap
);
207 writedata (fd
, buf
, len
);
210 static void die (fz_error error
)
212 fz_catch (error
, "aborting");
214 pdf_closexref (state
.xref
);
218 static void openxref (char *filename
)
224 fd
= open (filename
, O_BINARY
| O_RDONLY
, 0666);
226 die (fz_throw ("cannot open file '%s'", filename
));
228 file
= fz_openfile (fd
);
229 state
.xref
= pdf_openxref (file
);
231 die (fz_throw ("cannot open PDF file '%s'", filename
));
232 fz_dropstream (file
);
234 if (pdf_needspassword (state
.xref
)) {
235 die (fz_throw ("password protected"));
238 error
= pdf_loadpagetree (state
.xref
);
240 die (fz_throw ("cannot load page tree"));
243 state
.pagecount
= pdf_getpagecount (state
.xref
);
244 state
.pagetbl
= stat_alloc (state
.pagecount
* sizeof (*state
.pagetbl
));
247 static int readlen (int fd
)
254 err (1, "read %zd", n
);
257 return (p
[0] << 24) | (p
[1] << 16) | (p
[2] << 8) | p
[3];
260 static void freepage (struct page
*page
)
265 fz_droppixmap (page
->pixmap
);
266 for (p
= state
.pages
; p
; p
= p
->prev
) {
267 if (p
->prev
== page
) {
268 p
->prev
= page
->prev
;
272 for (i
= 0; i
< page
->slicecount
; ++i
) {
273 struct slice
*s
= &page
->slices
[i
];
274 if (s
->texindex
!= -1) {
275 if (state
.texowners
[s
->texindex
].slice
== s
) {
276 state
.texowners
[s
->texindex
].slice
= NULL
;
277 ARSERT (state
.texowners
[s
->texindex
].w
== s
->w
);
278 ARSERT (state
.texowners
[s
->texindex
].h
>= s
->h
);
283 fz_freetextspan (page
->text
);
285 if (page
->drawpage
) {
286 pdf_freepage (page
->drawpage
);
292 static void subdivide (struct page
*p
)
295 int h
= p
->pixmap
->h
;
296 int th
= MIN (h
, state
.sliceheight
);
298 for (i
= 0; i
< p
->slicecount
; ++i
) {
299 struct slice
*s
= &p
->slices
[i
];
307 static void *render (int pageno
, int pindex
)
315 fz_displaylist
*list
;
316 fz_device
*idev
, *mdev
;
317 struct pagedim
*pagedim
;
320 /* printd (state.sock, "T rendering %d", pageno); */
321 pdf_flushxref (state
.xref
, 0);
323 pagedim
= &state
.pagedims
[pindex
];
324 slicecount
= (pagedim
->bbox
.y1
- pagedim
->bbox
.y0
325 + state
.sliceheight
- 1) / state
.sliceheight
;
326 slicecount
+= slicecount
== 0;
328 page
= calloc (sizeof (*page
)
329 + (slicecount
* sizeof (struct slice
)), 1);
331 err (1, "calloc page %d\n", pageno
);
333 page
->slicecount
= slicecount
;
334 page
->prev
= state
.pages
;
337 pageobj
= pdf_getpageobject (state
.xref
, pageno
);
339 die (fz_throw ("cannot retrieve info from page %d", pageno
));
341 error
= pdf_loadpage (&drawpage
, state
.xref
, pageobj
);
345 page
->pixmap
= fz_newpixmapwithrect (pdf_devicergb
, pagedim
->bbox
);
348 fz_clearpixmap (page
->pixmap
, 0xFF);
350 list
= fz_newdisplaylist ();
352 die (fz_throw ("fz_newdisplaylist failed"));
354 mdev
= fz_newlistdevice (list
);
355 error
= pdf_runcontentstream (mdev
, fz_identity (), state
.xref
,
360 fz_freedevice (mdev
);
362 idev
= fz_newdrawdevice (state
.cache
, page
->pixmap
);
364 die (fz_throw ("fz_newdrawdevice failed"));
365 fz_executedisplaylist (list
, idev
, pagedim
->ctm
);
366 fz_freedevice (idev
);
368 fz_freedisplaylist (list
);
370 page
->drawpage
= drawpage
;
371 page
->pagedim
= pagedim
;
372 page
->pageno
= pageno
;
376 if (!state
.lotsamemory
) {
377 pdf_agestoreditems (state
.xref
->store
);
378 pdf_evictageditems (state
.xref
->store
);
381 /* printd (state.sock, "T rendering %d took %f sec", pageno, end - start); */
385 static void initpdims (void)
391 for (pageno
= 0; pageno
< state
.pagecount
; ++pageno
) {
395 fz_obj
*obj
, *pageobj
;
397 pageobj
= pdf_getpageobject (state
.xref
, pageno
+ 1);
399 obj
= fz_dictgets (pageobj
, "CropBox");
400 if (!fz_isarray (obj
)) {
401 obj
= fz_dictgets (pageobj
, "MediaBox");
402 if (!fz_isarray (obj
)) {
403 die (fz_throw ("cannot find page bounds %d (%d Rd)",
404 fz_tonum (pageobj
), fz_togen (pageobj
)));
407 box
= pdf_torect (obj
);
409 obj
= fz_dictgets (pageobj
, "Rotate");
410 if (fz_isint (obj
)) {
411 rotate
= fz_toint (obj
);
417 state
.pagetbl
[pageno
] = fz_tonum (state
.xref
->pagerefs
[pageno
]);
418 p
= &state
.pagedims
[state
.pagedimcount
- 1];
419 if ((state
.pagedimcount
== 0)
420 || (p
->rotate
!= rotate
|| memcmp (&p
->box
, &box
, sizeof (box
)))) {
423 size
= (state
.pagedimcount
+ 1) * sizeof (*state
.pagedims
);
424 state
.pagedims
= realloc (state
.pagedims
, size
);
425 if (!state
.pagedims
) {
426 err (1, "realloc pagedims to %zu (%d elems)",
427 size
, state
.pagedimcount
+ 1);
429 p
= &state
.pagedims
[state
.pagedimcount
++];
436 printd (state
.sock
, "T Processed %d pages in %f seconds",
437 state
.pagecount
, end
- start
);
440 static void layout (void)
446 struct pagedim
*p
= state
.pagedims
;
449 for (pindex
= 0; pindex
< state
.pagedimcount
; ++pindex
, ++p
) {
450 box
.x0
= MIN (p
->box
.x0
, p
->box
.x1
);
451 box
.y0
= MIN (p
->box
.y0
, p
->box
.y1
);
452 box
.x1
= MAX (p
->box
.x0
, p
->box
.x1
);
453 box
.y1
= MAX (p
->box
.y0
, p
->box
.y1
);
455 ctm
= fz_identity ();
456 ctm
= fz_concat (ctm
, fz_translate (0, -box
.y1
));
457 ctm
= fz_concat (ctm
, fz_rotate (p
->rotate
));
458 box2
= fz_transformrect (ctm
, box
);
459 w
= box2
.x1
- box2
.x0
;
461 zoom
= (state
.w
/ w
);
462 ctm
= fz_identity ();
463 ctm
= fz_concat (ctm
, fz_translate (0, -box
.y1
));
464 ctm
= fz_concat (ctm
, fz_scale (zoom
, -zoom
));
465 ctm
= fz_concat (ctm
, fz_rotate (p
->rotate
));
466 p
->bbox
= fz_roundrect (fz_transformrect (ctm
, box
));
467 memcpy (&p
->ctm
, &ctm
, sizeof (ctm
));
470 while (p
-- != state
.pagedims
) {
471 printd (state
.sock
, "l %d %d %d",
472 p
->pageno
, p
->bbox
.x1
- p
->bbox
.x0
, p
->bbox
.y1
- p
->bbox
.y0
);
476 static void recurse_outline (pdf_outline
*outline
, int level
)
484 if (!outline
->link
) goto next
;
486 obj
= outline
->link
->dest
;
487 if (fz_isindirect (obj
)) {
488 num
= fz_tonum (obj
);
490 for (i
= 0; i
< state
.pagecount
; ++i
) {
491 if (state
.pagetbl
[i
] == num
) {
497 else if (fz_isarray (obj
)) {
500 obj2
= fz_arrayget (obj
, 0);
501 if (fz_isint (obj2
)) {
502 pageno
= fz_toint (obj2
);
505 num
= fz_tonum (obj2
);
506 for (i
= 0; i
< state
.pagecount
; ++i
) {
507 if (state
.pagetbl
[i
] == num
) {
514 if (fz_arraylen (obj
) > 3) {
516 struct pagedim
*pagedim
= state
.pagedims
;
518 for (i
= 0; i
< state
.pagedimcount
; ++i
) {
519 if (state
.pagedims
[i
].pageno
> pageno
)
521 pagedim
= &state
.pagedims
[i
];
524 p
.x
= fz_toint (fz_arrayget (obj
, 2));
525 p
.y
= fz_toint (fz_arrayget (obj
, 3));
526 p
= fz_transformpoint (pagedim
->ctm
, p
);
531 lprintf ("%*c%s %d\n", level
, ' ', outline
->title
, pageno
);
532 printd (state
.sock
, "o %d %d %d %s",
533 level
, pageno
, top
, outline
->title
);
535 if (outline
->child
) {
536 recurse_outline (outline
->child
, level
+ 1);
538 outline
= outline
->next
;
542 static void process_outline (void)
544 pdf_outline
*outline
;
546 outline
= pdf_loadoutline (state
.xref
);
548 recurse_outline (outline
, 0);
549 pdf_freeoutline (outline
);
553 static int comparespans (const void *l
, const void *r
)
555 fz_textspan
*const*ls
= l
;
556 fz_textspan
*const*rs
= r
;
558 return (*ls
)->text
->bbox
.y0
- (*rs
)->text
->bbox
.y0
;
561 /* wishful thinking function */
562 static void search (regex_t
*re
, int pageno
, int y
, int forward
)
572 fz_textspan
*text
, *span
, **pspan
;
573 struct pagedim
*pdim
, *pdimprev
;
580 while (pageno
>= 0 && pageno
< state
.pagecount
&& !stop
) {
582 if (!state
.lotsamemory
) {
583 pdf_agestoreditems (state
.xref
->store
);
584 pdf_evictageditems (state
.xref
->store
);
587 if (hasdata (state
.sock
)) {
588 printd (state
.sock
, "T attention requested aborting search at %d",
593 printd (state
.sock
, "T searching in page %d", pageno
);
597 for (i
= 0; i
< state
.pagedimcount
; ++i
) {
598 pdim
= &state
.pagedims
[i
];
599 if (pdim
->pageno
== pageno
) {
602 if (pdim
->pageno
> pageno
) {
611 pageobj
= pdf_getpageobject (state
.xref
, pageno
+ 1);
613 die (fz_throw ("cannot retrieve info from page %d", pageno
));
615 error
= pdf_loadpage (&drawpage
, state
.xref
, pageobj
);
619 text
= fz_newtextspan ();
620 tdev
= fz_newtextdevice (text
);
621 error
= pdf_runcontentstream (tdev
, pdim
->ctm
, state
.xref
,
624 if (error
) die (error
);
625 fz_freedevice (tdev
);
628 for (span
= text
; span
; span
= span
->next
) {
631 pspan
= malloc (sizeof (void *) * nspans
);
633 err (1, "malloc span pointers %zu", sizeof (void *) * nspans
);
635 for (i
= 0, span
= text
; span
; span
= span
->next
, ++i
) {
638 qsort (pspan
, nspans
, sizeof (fz_textspan
*), comparespans
);
640 j
= forward
? 0 : nspans
- 1;
645 j
+= forward
? 1 : -1;
647 /* XXX: spans are not sorted "visually" */
648 for (i
= 0; i
< MIN (span
->len
, sizeof (buf
) - 1); ++i
) {
650 if (span
->text
[i
].bbox
.y0
< y
+ 1) {
655 if (span
->text
[i
].bbox
.y0
> y
- 1) {
659 if (span
->text
[i
].c
< 256) {
660 *p
++ = span
->text
[i
].c
;
671 ret
= regexec (re
, buf
, 1, &rm
, 0);
673 if (ret
!= REG_NOMATCH
) {
676 size
= regerror (ret
, re
, errbuf
, sizeof (errbuf
));
678 "T regexec error `%.*s'",
680 fz_freetextspan (text
);
681 pdf_freepage (drawpage
);
689 r
.x0
= span
->text
[rm
.rm_so
].bbox
.x0
- pdim
->bbox
.x0
;
690 r
.y0
= span
->text
[rm
.rm_so
].bbox
.y0
;
691 r
.x1
= span
->text
[rm
.rm_eo
- 1].bbox
.x1
- pdim
->bbox
.x0
;
692 r
.y1
= span
->text
[rm
.rm_eo
- 1].bbox
.y1
;
695 printd (state
.sock
, "F %d %d %f %f %f %f",
701 printd (state
.sock
, "R %d %d %f %f %f %f",
706 printd (state
.sock
, "T found at %d `%.*s' %f in %f sec",
707 pageno
, rm
.rm_eo
- rm
.rm_so
, &buf
[rm
.rm_so
],
708 span
->text
[0].bbox
.y0
- drawpage
->mediabox
.y0
,
721 fz_freetextspan (text
);
722 pdf_freepage (drawpage
);
727 printd (state
.sock
, "T no matches %f sec", end
- start
);
729 printd (state
.sock
, "D");
732 static void *mainloop (void *unused
)
735 int len
, ret
, oldlen
= 0;
738 len
= readlen (state
.sock
);
740 errx (1, "readlen returned 0");
743 if (oldlen
< len
+ 1) {
744 p
= realloc (p
, len
+ 1);
746 err (1, "realloc %d failed", len
+ 1);
750 readdata (state
.sock
, p
, len
);
753 if (!strncmp ("open", p
, 4)) {
755 char *filename
= p
+ 5;
760 obj
= fz_dictgets (state
.xref
->trailer
, "Info");
762 obj
= fz_dictgets (obj
, "Title");
764 printd (state
.sock
, "t %s", pdf_toutf8 (obj
));
768 else if (!strncmp ("free", p
, 4)) {
771 ret
= sscanf (p
+ 4, " %p", &ptr
);
773 errx (1, "malformed free `%.*s' ret=%d", len
, p
, ret
);
778 printd (state
.sock
, "d");
780 else if (!strncmp ("search", p
, 6)) {
781 int icase
, pageno
, y
, ret
, len2
, forward
;
785 ret
= sscanf (p
+ 6, " %d %d %d %d,%n",
786 &icase
, &pageno
, &y
, &forward
, &len2
);
788 errx (1, "malformed search `%s' ret=%d", p
, ret
);
791 pattern
= p
+ 6 + len2
;
792 ret
= regcomp (&re
, pattern
,
793 REG_EXTENDED
| (icase
? REG_ICASE
: 0));
798 size
= regerror (ret
, &re
, errbuf
, sizeof (errbuf
));
799 printd (state
.sock
, "T regcomp failed `%.*s'",
803 search (&re
, pageno
, y
, forward
);
807 else if (!strncmp ("geometry", p
, 8)) {
810 printd (state
.sock
, "c");
811 ret
= sscanf (p
+ 8, " %d %d", &w
, &h
);
813 errx (1, "malformed geometry `%.*s' ret=%d", len
, p
, ret
);
819 for (i
= 0; i
< state
.texcount
; ++i
) {
820 state
.texowners
[i
].slice
= NULL
;
827 printd (state
.sock
, "C %d", state
.pagecount
);
829 else if (!strncmp ("render", p
, 6)) {
830 int pageno
, pindex
, w
, h
, ret
;
833 ret
= sscanf (p
+ 6, " %d %d %d %d", &pageno
, &pindex
, &w
, &h
);
835 errx (1, "bad render line `%.*s' ret=%d", len
, p
, ret
);
838 page
= render (pageno
, pindex
);
839 printd (state
.sock
, "r %d %d %d %p\n",
846 errx (1, "unknown command %.*s", len
, p
);
852 static void upload2 (struct page
*page
, int slicenum
, const char *cap
)
857 struct slice
*slice
= &page
->slices
[slicenum
];
862 ARSERT (w
== slice
->w
);
863 if (slice
->texindex
!= -1
864 && state
.texowners
[slice
->texindex
].slice
== slice
) {
865 glBindTexture (GL_TEXTURE_RECTANGLE_ARB
, state
.texids
[slice
->texindex
]);
869 int index
= (state
.texindex
++ % state
.texcount
);
872 for (i
= 0; i
< slicenum
; ++i
) {
873 offset
+= w
* page
->slices
[i
].h
* 4;
876 if (state
.texowners
[index
].w
== slice
->w
) {
877 if (state
.texowners
[index
].h
>= slice
->h
) {
881 state
.texowners
[index
].h
= slice
->h
;
885 state
.texowners
[index
].h
= slice
->h
;
888 state
.texowners
[index
].slice
= slice
;
889 state
.texowners
[index
].w
= slice
->w
;
890 slice
->texindex
= index
;
892 glBindTexture (GL_TEXTURE_RECTANGLE_ARB
, state
.texids
[slice
->texindex
]);
896 GLenum err
= glGetError ();
897 if (err
!= GL_NO_ERROR
) {
898 printf ("\e[0;31mERROR1 %d %d %#x\e[0m\n", w
, slice
->h
, err
);
902 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB
,
910 page
->pixmap
->samples
+ offset
913 GLenum err
= glGetError ();
914 if (err
!= GL_NO_ERROR
) {
915 printf ("\e[0;31mERROR %d %d %#x\e[0m\n", w
, slice
->h
, err
);
921 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB
,
929 page
->pixmap
->samples
+ offset
934 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
935 subimage
? "sub" : "img",
936 page
->pageno
, slicenum
,
938 state
.texids
[slice
->texindex
],
943 CAMLprim value
ml_draw (value dispy_v
, value w_v
, value h_v
,
944 value py_v
, value ptr_v
)
946 CAMLparam5 (dispy_v
, w_v
, h_v
, py_v
, ptr_v
);
947 int dispy
= Int_val (dispy_v
);
948 int w
= Int_val (w_v
);
949 int h
= Int_val (h_v
);
950 int py
= Int_val (py_v
);
951 char *s
= String_val (ptr_v
);
957 if (trylock ("ml_draw")) {
961 ret
= sscanf (s
, "%p", &ptr
);
963 errx (1, "cannot parse pointer `%s'", s
);
969 ARSERT (h
>= 0 && "ml_draw wrong h");
971 glEnable (GL_TEXTURE_RECTANGLE_ARB
);
972 if (state
.useatifs
) {
973 glEnable (GL_FRAGMENT_SHADER_ATI
);
976 for (slicenum
= 0; slicenum
< page
->slicecount
; ++slicenum
) {
977 struct slice
*slice
= &page
->slices
[slicenum
];
984 h
= MIN (state
.h
, h
);
987 struct slice
*slice
= &page
->slices
[slicenum
];
989 ARSERT (slicenum
< page
->slicecount
&& "ml_draw wrong slicenum");
991 th
= MIN (h
, slice
->h
- py
);
992 upload2 (page
, slicenum
, "upload");
996 glTexCoord2i (0, py
);
997 glVertex2i (0, dispy
);
999 glTexCoord2i (w
, py
);
1000 glVertex2i (w
, dispy
);
1002 glTexCoord2i (w
, py
+th
);
1003 glVertex2i (w
, dispy
+ th
);
1005 glTexCoord2i (0, py
+th
);
1006 glVertex2i (0, dispy
+ th
);
1016 glDisable (GL_TEXTURE_RECTANGLE_ARB
);
1017 if (state
.useatifs
) {
1018 glDisable (GL_FRAGMENT_SHADER_ATI
);
1023 CAMLreturn (Val_unit
);
1026 static pdf_link
*getlink (struct page
*page
, int x
, int y
)
1035 ctm
= fz_invertmatrix (page
->pagedim
->ctm
);
1036 p
= fz_transformpoint (ctm
, p
);
1038 for (link
= page
->drawpage
->links
; link
; link
= link
->next
) {
1039 if (p
.x
>= link
->rect
.x0
&& p
.x
<= link
->rect
.x1
) {
1040 if (p
.y
>= link
->rect
.y0
&& p
.y
<= link
->rect
.y1
) {
1041 if (link
->kind
== PDF_LGOTO
) {
1050 CAMLprim value
ml_checklink (value ptr_v
, value x_v
, value y_v
)
1052 CAMLparam3 (ptr_v
, x_v
, y_v
);
1053 char *s
= String_val (ptr_v
);
1056 if (trylock ("ml_checklink")) {
1060 ret
= NULL
!= getlink (parse_pointer ("ml_checklink", s
),
1061 Int_val (x_v
), Int_val (y_v
));
1062 unlock ("ml_checklink");
1064 CAMLreturn (Val_bool (ret
));
1067 CAMLprim value
ml_getlink (value ptr_v
, value x_v
, value y_v
)
1069 CAMLparam3 (ptr_v
, x_v
, y_v
);
1070 CAMLlocal2 (ret_v
, tup_v
);
1073 char *s
= String_val (ptr_v
);
1075 if (trylock ("ml_getlink")) {
1076 ret_v
= Val_int (0);
1080 page
= parse_pointer ("ml_getlink", s
);
1082 link
= getlink (page
, Int_val (x_v
), Int_val (y_v
));
1089 obj
= fz_arrayget (link
->dest
, 0);
1090 if (fz_isindirect (obj
)) {
1091 pageno
= pdf_findpageobject (state
.xref
, obj
) - 1;
1093 else if (fz_isint (obj
)) {
1094 pageno
= fz_toint (obj
);
1097 if (fz_arraylen (link
->dest
) > 3) {
1098 p
.x
= fz_toint (fz_arrayget (link
->dest
, 2));
1099 p
.y
= fz_toint (fz_arrayget (link
->dest
, 3));
1100 p
= fz_transformpoint (page
->pagedim
->ctm
, p
);
1107 tup_v
= caml_alloc_tuple (2);
1108 ret_v
= caml_alloc_small (1, 1);
1109 Field (tup_v
, 0) = Val_int (pageno
);
1110 Field (tup_v
, 1) = Val_int (p
.y
);
1111 Field (ret_v
, 0) = tup_v
;
1114 ret_v
= Val_int (0);
1116 unlock ("ml_getlink");
1122 CAMLprim value
ml_gettext (value ptr_v
, value rect_v
, value oy_v
, value rectsel_v
)
1124 CAMLparam4 (ptr_v
, rect_v
, oy_v
, rect_v
);
1129 char *s
= String_val (ptr_v
);
1130 int rectsel
= Bool_val (rectsel_v
);
1131 int i
, bx0
, bx1
, by0
, by1
, x0
, x1
, y0
, y1
, oy
;
1133 /* stop GCC from complaining about uninitialized variables */
1134 int rx0
= rx0
, rx1
= rx1
, ry0
= ry0
, ry1
= ry1
;
1136 if (trylock ("ml_gettext")) {
1140 page
= parse_pointer ("ml_gettext", s
);
1142 oy
= Int_val (oy_v
);
1143 p1
.x
= Int_val (Field (rect_v
, 0));
1144 p1
.y
= Int_val (Field (rect_v
, 1));
1145 p2
.x
= Int_val (Field (rect_v
, 2));
1146 p2
.y
= Int_val (Field (rect_v
, 3));
1149 glEnable (GL_BLEND
);
1150 glPolygonMode (GL_FRONT_AND_BACK
, GL_LINE
);
1151 glBlendFunc (GL_DST_ALPHA
, GL_SRC_ALPHA
);
1152 glColor4f (0, 0, 0, 0.2);
1153 glRecti (p1
.x
, p1
.y
, p2
.x
, p2
.y
);
1154 glPolygonMode (GL_FRONT_AND_BACK
, GL_FILL
);
1155 glDisable (GL_BLEND
);
1158 ctm
= page
->pagedim
->ctm
;
1163 page
->text
= fz_newtextspan ();
1164 tdev
= fz_newtextdevice (page
->text
);
1165 error
= pdf_runcontentstream (tdev
, page
->pagedim
->ctm
, state
.xref
,
1166 page
->drawpage
->resources
,
1167 page
->drawpage
->contents
);
1168 if (error
) die (error
);
1169 fz_freedevice (tdev
);
1174 printf ("BBox %f %f %f %f\n", p1
.x
, p1
.y
, p2
.x
, p2
.y
);
1175 p1
.x
+= page
->pixmap
->x
;
1176 p1
.y
+= page
->pixmap
->y
;
1177 p2
.x
+= page
->pixmap
->x
;
1178 p2
.y
+= page
->pixmap
->y
;
1183 printf ("BBox %d %d %d %d %d %d\n", x0
, y0
, x1
, y1
, oy
, page
->pageno
);
1185 for (span
= page
->text
; span
; span
= span
->next
) {
1188 /* fz_debugtextspanxml (span); */
1189 for (i
= 0; i
< span
->len
; ++i
) {
1192 bx0
= span
->text
[i
].bbox
.x0
;
1193 bx1
= span
->text
[i
].bbox
.x1
;
1194 by0
= span
->text
[i
].bbox
.y0
+ oy
;
1195 by1
= span
->text
[i
].bbox
.y1
+ oy
;
1197 if ((bx1
>= x0
&& bx0
<= x1
&& by1
>= y0
&& by0
<= y1
)) {
1199 rx0
= bx0
- page
->pixmap
->x
;
1200 rx1
= bx1
- page
->pixmap
->x
;
1206 c
= span
->text
[i
].c
;
1208 if ((isprint (c
) && !isspace (c
))) {
1210 bx0
-= page
->pixmap
->x
;
1211 bx1
-= page
->pixmap
->x
;
1212 glEnable (GL_BLEND
);
1213 glPolygonMode (GL_FRONT_AND_BACK
, GL_FILL
);
1214 glBlendFunc (GL_DST_ALPHA
, GL_SRC_ALPHA
);
1215 glColor4f (0.5, 0.5, 0.0, 0.6);
1216 glRecti (bx0
, by0
, bx1
, by1
);
1217 glPolygonMode (GL_FRONT_AND_BACK
, GL_FILL
);
1218 glDisable (GL_BLEND
);
1220 if (isprint (c
) || c
==' ') {
1235 glEnable (GL_BLEND
);
1236 glPolygonMode (GL_FRONT_AND_BACK
, GL_FILL
);
1237 glBlendFunc (GL_DST_ALPHA
, GL_SRC_ALPHA
);
1238 glColor4f (0.5, 0.5, 0.0, 0.6);
1239 glRecti (rx0
, ry0
, rx1
, ry1
);
1240 glPolygonMode (GL_FRONT_AND_BACK
, GL_FILL
);
1241 glDisable (GL_BLEND
);
1245 if (seen
&& span
->eol
) {
1246 x0
= page
->pixmap
->x
;
1247 putc ('\n', stdout
);
1250 unlock ("ml_gettext");
1253 CAMLreturn (Val_unit
);
1256 CAMLprim value
ml_getpagewh (value pagedimno_v
)
1258 CAMLparam1 (pagedimno_v
);
1260 int pagedimno
= Int_val (pagedimno_v
);
1262 ret_v
= caml_alloc_small (4 * Double_wosize
, Double_array_tag
);
1263 Store_double_field (ret_v
, 0, state
.pagedims
[pagedimno
].box
.x0
);
1264 Store_double_field (ret_v
, 1, state
.pagedims
[pagedimno
].box
.x1
);
1265 Store_double_field (ret_v
, 2, state
.pagedims
[pagedimno
].box
.y0
);
1266 Store_double_field (ret_v
, 3, state
.pagedims
[pagedimno
].box
.y1
);
1270 static void initgl (void)
1273 if (strstr ((char *) glGetString (GL_EXTENSIONS
),
1274 "GL_ATI_fragment_shader")) {
1275 /* Here, with MESA, rv280, powerpc32: BGRA(rev) is slow while
1276 ABGR is fast, so fix things in the shader */
1277 state
.texform
= GL_ABGR_EXT
;
1278 state
.texty
= GL_UNSIGNED_INT_8_8_8_8
;
1280 glBindFragmentShaderATI (1);
1281 glBeginFragmentShaderATI ();
1283 glSampleMapATI (GL_REG_0_ATI
, GL_TEXTURE0_ARB
, GL_SWIZZLE_STR_ATI
);
1285 glColorFragmentOp1ATI (GL_MOV_ATI
,
1286 GL_REG_1_ATI
, GL_RED_BIT_ATI
, GL_NONE
,
1287 GL_REG_0_ATI
, GL_BLUE
, GL_NONE
);
1288 glColorFragmentOp1ATI (GL_MOV_ATI
,
1289 GL_REG_1_ATI
, GL_BLUE_BIT_ATI
, GL_NONE
,
1290 GL_REG_0_ATI
, GL_RED
, GL_NONE
);
1291 glColorFragmentOp1ATI (
1293 GL_REG_0_ATI
, GL_RED_BIT_ATI
| GL_BLUE_BIT_ATI
, GL_NONE
,
1294 GL_REG_1_ATI
, GL_NONE
, GL_NONE
1297 glEndFragmentShaderATI ();
1301 state
.texform
= GL_BGRA_EXT
;
1302 state
.texty
= GL_UNSIGNED_INT_8_8_8_8_REV
;
1305 state
.texform
= GL_BGRA_EXT
;
1306 state
.texty
= GL_UNSIGNED_INT_8_8_8_8
;
1310 CAMLprim value
ml_init (value sock_v
)
1313 CAMLparam1 (sock_v
);
1315 state
.texcount
= 256;
1316 state
.sliceheight
= 24;
1318 state
.texids
= calloc (state
.texcount
* sizeof (*state
.texids
), 1);
1319 if (!state
.texids
) {
1320 err (1, "calloc texids %zu", state
.texcount
* sizeof (*state
.texids
));
1323 state
.texowners
= calloc (state
.texcount
* sizeof (*state
.texowners
), 1);
1324 if (!state
.texowners
) {
1325 err (1, "calloc texowners %zu",
1326 state
.texcount
* sizeof (*state
.texowners
));
1329 glGenTextures (state
.texcount
, state
.texids
);
1331 state
.sock
= Int_val (sock_v
);
1334 state
.cache
= fz_newglyphcache ();
1336 errx (1, "fz_newglyphcache failed");
1339 ret
= pthread_create (&state
.thread
, NULL
, mainloop
, NULL
);
1341 errx (1, "pthread_create: %s", strerror (errno
));
1344 CAMLreturn (Val_unit
);