4 #include <android/log.h>
5 #include <android/bitmap.h>
15 #include "mupdf/fitz.h"
16 #include "mupdf/pdf.h"
18 #define JNI_FN(A) Java_cz_cvut_fel_dce_qrscanner_mupdf_ ## A
19 #define PACKAGENAME "cz/cvut/fel/dce/qrscanner/mupdf"
21 #define LOG_TAG "libmupdf"
22 #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
23 #define LOGT(...) __android_log_print(ANDROID_LOG_INFO,"alert",__VA_ARGS__)
24 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
26 /* Set to 1 to enable debug log traces. */
29 /* Enable to log rendering times (render each frame 100 times and time) */
30 #undef TIME_DISPLAY_LIST
32 #define MAX_SEARCH_HITS (500)
34 #define STRIKE_HEIGHT (0.375f)
35 #define UNDERLINE_HEIGHT (0.075f)
36 #define LINE_THICKNESS (0.07f)
37 #define INK_THICKNESS (4.0f)
38 #define SMALL_FLOAT (0.00001)
49 typedef struct rect_node_s rect_node;
64 rect_node *changed_rects;
65 rect_node *hq_changed_rects;
66 fz_display_list *page_list;
67 fz_display_list *annot_list;
70 typedef struct globals_s globals;
74 fz_colorspace *colorspace;
82 page_cache pages[NUM_CACHE];
84 int alerts_initialised;
85 // fin_lock and fin_lock2 are used during shutdown. The two waiting tasks
86 // show_alert and waitForAlertInternal respectively take these locks while
87 // waiting. During shutdown, the conditions are signaled and then the fin_locks
88 // are taken momentarily to ensure the blocked threads leave the controlled
89 // area of code before the mutexes and condition variables are destroyed.
90 pthread_mutex_t fin_lock;
91 pthread_mutex_t fin_lock2;
92 // alert_lock is the main lock guarding the variables directly below.
93 pthread_mutex_t alert_lock;
94 // Flag indicating if the alert system is active. When not active, both
95 // show_alert and waitForAlertInternal return immediately.
97 // Pointer to the alert struct passed in by show_alert, and valid while
98 // show_alert is blocked.
99 pdf_alert_event *current_alert;
100 // Flag and condition varibles to signal a request is present and a reply
101 // is present, respectively. The condition variables alone are not sufficient
102 // because of the pthreads permit spurious signals.
105 pthread_cond_t alert_request_cond;
106 pthread_cond_t alert_reply_cond;
108 // For the buffer reading mode, we need to implement stream reading, which
109 // needs access to the following.
114 static jfieldID global_fid;
115 static jfieldID buffer_fid;
117 static void drop_changed_rects(fz_context *ctx, rect_node **nodePtr)
119 rect_node *node = *nodePtr;
122 rect_node *tnode = node;
130 static void drop_page_cache(globals *glo, page_cache *pc)
132 fz_context *ctx = glo->ctx;
133 fz_document *doc = glo->doc;
135 LOGI("Drop page %d", pc->number);
136 fz_drop_display_list(ctx, pc->page_list);
137 pc->page_list = NULL;
138 fz_drop_display_list(ctx, pc->annot_list);
139 pc->annot_list = NULL;
140 fz_free_page(doc, pc->page);
142 drop_changed_rects(ctx, &pc->changed_rects);
143 drop_changed_rects(ctx, &pc->hq_changed_rects);
146 static void dump_annotation_display_lists(globals *glo)
148 fz_context *ctx = glo->ctx;
151 for (i = 0; i < NUM_CACHE; i++) {
152 fz_drop_display_list(ctx, glo->pages[i].annot_list);
153 glo->pages[i].annot_list = NULL;
157 static void show_alert(globals *glo, pdf_alert_event *alert)
159 pthread_mutex_lock(&glo->fin_lock2);
160 pthread_mutex_lock(&glo->alert_lock);
162 LOGT("Enter show_alert: %s", alert->title);
163 alert->button_pressed = 0;
165 if (glo->alerts_active)
167 glo->current_alert = alert;
168 glo->alert_request = 1;
169 pthread_cond_signal(&glo->alert_request_cond);
171 while (glo->alerts_active && !glo->alert_reply)
172 pthread_cond_wait(&glo->alert_reply_cond, &glo->alert_lock);
173 glo->alert_reply = 0;
174 glo->current_alert = NULL;
177 LOGT("Exit show_alert");
179 pthread_mutex_unlock(&glo->alert_lock);
180 pthread_mutex_unlock(&glo->fin_lock2);
183 static void event_cb(pdf_doc_event *event, void *data)
185 globals *glo = (globals *)data;
189 case PDF_DOCUMENT_EVENT_ALERT:
190 show_alert(glo, pdf_access_alert_event(event));
195 static void alerts_init(globals *glo)
197 pdf_document *idoc = pdf_specifics(glo->doc);
199 if (!idoc || glo->alerts_initialised)
205 glo->alerts_active = 0;
206 glo->alert_request = 0;
207 glo->alert_reply = 0;
208 pthread_mutex_init(&glo->fin_lock, NULL);
209 pthread_mutex_init(&glo->fin_lock2, NULL);
210 pthread_mutex_init(&glo->alert_lock, NULL);
211 pthread_cond_init(&glo->alert_request_cond, NULL);
212 pthread_cond_init(&glo->alert_reply_cond, NULL);
214 pdf_set_doc_event_callback(idoc, event_cb, glo);
216 glo->alerts_initialised = 1;
219 static void alerts_fin(globals *glo)
221 pdf_document *idoc = pdf_specifics(glo->doc);
222 if (!glo->alerts_initialised)
225 LOGT("Enter alerts_fin");
227 pdf_set_doc_event_callback(idoc, NULL, NULL);
229 // Set alerts_active false and wake up show_alert and waitForAlertInternal,
230 pthread_mutex_lock(&glo->alert_lock);
231 glo->current_alert = NULL;
232 glo->alerts_active = 0;
233 pthread_cond_signal(&glo->alert_request_cond);
234 pthread_cond_signal(&glo->alert_reply_cond);
235 pthread_mutex_unlock(&glo->alert_lock);
237 // Wait for the fin_locks.
238 pthread_mutex_lock(&glo->fin_lock);
239 pthread_mutex_unlock(&glo->fin_lock);
240 pthread_mutex_lock(&glo->fin_lock2);
241 pthread_mutex_unlock(&glo->fin_lock2);
243 pthread_cond_destroy(&glo->alert_reply_cond);
244 pthread_cond_destroy(&glo->alert_request_cond);
245 pthread_mutex_destroy(&glo->alert_lock);
246 pthread_mutex_destroy(&glo->fin_lock2);
247 pthread_mutex_destroy(&glo->fin_lock);
248 LOGT("Exit alerts_fin");
249 glo->alerts_initialised = 0;
252 // Should only be called from the single background AsyncTask thread
253 static globals *get_globals(JNIEnv *env, jobject thiz)
255 globals *glo = (globals *)(intptr_t)((*env)->GetLongField(env, thiz, global_fid));
264 // May be called from any thread, provided the values of glo->env and glo->thiz
266 static globals *get_globals_any_thread(JNIEnv *env, jobject thiz)
268 return (globals *)(intptr_t)((*env)->GetLongField(env, thiz, global_fid));
271 JNIEXPORT jlong JNICALL
272 JNI_FN(MuPDFCore_openFile)(JNIEnv * env, jobject thiz, jstring jfilename)
274 const char *filename;
280 monstartup("libmupdf.so");
283 clazz = (*env)->GetObjectClass(env, thiz);
284 global_fid = (*env)->GetFieldID(env, clazz, "globals", "J");
286 glo = calloc(1, sizeof(*glo));
289 glo->resolution = 160;
290 glo->alerts_initialised = 0;
292 filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
293 if (filename == NULL)
295 LOGE("Failed to get filename");
300 /* 128 MB store for low memory devices. Tweak as necessary. */
301 glo->ctx = ctx = fz_new_context(NULL, NULL, 128 << 20);
304 LOGE("Failed to initialise context");
305 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
310 fz_register_document_handlers(ctx);
315 glo->colorspace = fz_device_rgb(ctx);
317 LOGI("Opening document...");
320 glo->current_path = fz_strdup(ctx, (char *)filename);
321 glo->doc = fz_open_document(ctx, (char *)filename);
326 fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot open document: '%s'", filename);
332 LOGE("Failed: %s", ctx->error->message);
333 fz_close_document(glo->doc);
335 fz_free_context(ctx);
341 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
343 return (jlong)(intptr_t)glo;
346 typedef struct buffer_state_s
353 static int bufferStreamNext(fz_stream *stream, int max)
355 buffer_state *bs = (buffer_state *)stream->state;
356 globals *glo = bs->globals;
357 JNIEnv *env = glo->env;
358 jbyteArray array = (jbyteArray)(void *)((*env)->GetObjectField(env, glo->thiz, buffer_fid));
359 int arrayLength = (*env)->GetArrayLength(env, array);
360 int len = sizeof(bs->buffer);
362 if (stream->pos > arrayLength)
363 stream->pos = arrayLength;
366 if (len + stream->pos > arrayLength)
367 len = arrayLength - stream->pos;
369 (*env)->GetByteArrayRegion(env, array, stream->pos, len, bs->buffer);
370 (*env)->DeleteLocalRef(env, array);
372 stream->rp = bs->buffer;
373 stream->wp = stream->rp + len;
377 return *stream->rp++;
380 static void bufferStreamClose(fz_context *ctx, void *state)
385 static void bufferStreamSeek(fz_stream *stream, int offset, int whence)
387 buffer_state *bs = (buffer_state *)stream->state;
388 globals *glo = bs->globals;
389 JNIEnv *env = glo->env;
390 jbyteArray array = (jbyteArray)(void *)((*env)->GetObjectField(env, glo->thiz, buffer_fid));
391 int arrayLength = (*env)->GetArrayLength(env, array);
393 (*env)->DeleteLocalRef(env, array);
395 if (whence == 0) /* SEEK_SET */
396 stream->pos = offset;
397 else if (whence == 1) /* SEEK_CUR */
398 stream->pos += offset;
399 else if (whence == 2) /* SEEK_END */
400 stream->pos = arrayLength + offset;
402 if (stream->pos > arrayLength)
403 stream->pos = arrayLength;
407 stream->wp = stream->rp;
410 JNIEXPORT jlong JNICALL
411 JNI_FN(MuPDFCore_openBuffer)(JNIEnv * env, jobject thiz, jstring jmagic)
416 fz_stream *stream = NULL;
421 monstartup("libmupdf.so");
424 clazz = (*env)->GetObjectClass(env, thiz);
425 global_fid = (*env)->GetFieldID(env, clazz, "globals", "J");
427 glo = calloc(1, sizeof(*glo));
430 glo->resolution = 160;
431 glo->alerts_initialised = 0;
434 buffer_fid = (*env)->GetFieldID(env, clazz, "fileBuffer", "[B");
436 magic = (*env)->GetStringUTFChars(env, jmagic, NULL);
439 LOGE("Failed to get magic");
444 /* 128 MB store for low memory devices. Tweak as necessary. */
445 glo->ctx = ctx = fz_new_context(NULL, NULL, 128 << 20);
448 LOGE("Failed to initialise context");
449 (*env)->ReleaseStringUTFChars(env, jmagic, magic);
454 fz_register_document_handlers(ctx);
460 bs = fz_malloc_struct(ctx, buffer_state);
462 stream = fz_new_stream(ctx, bs, bufferStreamNext, bufferStreamClose, NULL);
463 stream->seek = bufferStreamSeek;
465 glo->colorspace = fz_device_rgb(ctx);
467 LOGI("Opening document...");
470 glo->current_path = NULL;
471 glo->doc = fz_open_document_with_stream(ctx, magic, stream);
476 fz_throw(ctx, FZ_ERROR_GENERIC, "Cannot open memory document");
486 LOGE("Failed: %s", ctx->error->message);
487 fz_close_document(glo->doc);
489 fz_free_context(ctx);
495 (*env)->ReleaseStringUTFChars(env, jmagic, magic);
497 return (jlong)(intptr_t)glo;
500 JNIEXPORT int JNICALL
501 JNI_FN(MuPDFCore_countPagesInternal)(JNIEnv *env, jobject thiz)
503 globals *glo = get_globals(env, thiz);
504 fz_context *ctx = glo->ctx;
509 count = fz_count_pages(glo->doc);
513 LOGE("exception while counting pages: %s", ctx->error->message);
518 JNIEXPORT jstring JNICALL
519 JNI_FN(MuPDFCore_fileFormatInternal)(JNIEnv * env, jobject thiz)
522 globals *glo = get_globals(env, thiz);
524 fz_meta(glo->doc, FZ_META_FORMAT_INFO, info, sizeof(info));
526 return (*env)->NewStringUTF(env, info);
529 JNIEXPORT jboolean JNICALL
530 JNI_FN(MuPDFCore_isUnencryptedPDFInternal)(JNIEnv * env, jobject thiz)
532 globals *glo = get_globals_any_thread(env, thiz);
536 pdf_document *idoc = pdf_specifics(glo->doc);
538 return JNI_FALSE; // Not a PDF
540 int cryptVer = pdf_crypt_version(idoc);
541 return (cryptVer == 0) ? JNI_TRUE : JNI_FALSE;
545 JNIEXPORT void JNICALL
546 JNI_FN(MuPDFCore_gotoPageInternal)(JNIEnv *env, jobject thiz, int page)
550 int furthest_dist = -1;
555 globals *glo = get_globals(env, thiz);
558 fz_context *ctx = glo->ctx;
560 for (i = 0; i < NUM_CACHE; i++)
562 if (glo->pages[i].page != NULL && glo->pages[i].number == page)
564 /* The page is already cached */
569 if (glo->pages[i].page == NULL)
571 /* cache record unused, and so a good one to use */
573 furthest_dist = INT_MAX;
577 int dist = abs(glo->pages[i].number - page);
579 /* Further away - less likely to be needed again */
580 if (dist > furthest_dist)
582 furthest_dist = dist;
588 glo->current = furthest;
589 pc = &glo->pages[glo->current];
591 drop_page_cache(glo, pc);
593 /* In the event of an error, ensure we give a non-empty page */
598 LOGI("Goto page %d...", page);
602 LOGI("Load page %d", pc->number);
603 pc->page = fz_load_page(glo->doc, pc->number);
604 zoom = glo->resolution / 72;
605 fz_bound_page(glo->doc, pc->page, &pc->media_box);
606 fz_scale(&ctm, zoom, zoom);
607 rect = pc->media_box;
608 fz_round_rect(&bbox, fz_transform_rect(&rect, &ctm));
609 pc->width = bbox.x1-bbox.x0;
610 pc->height = bbox.y1-bbox.y0;
614 LOGE("cannot make displaylist from page %d", pc->number);
618 JNIEXPORT float JNICALL
619 JNI_FN(MuPDFCore_getPageWidth)(JNIEnv *env, jobject thiz)
621 globals *glo = get_globals(env, thiz);
622 LOGI("PageWidth=%d", glo->pages[glo->current].width);
623 return glo->pages[glo->current].width;
626 JNIEXPORT float JNICALL
627 JNI_FN(MuPDFCore_getPageHeight)(JNIEnv *env, jobject thiz)
629 globals *glo = get_globals(env, thiz);
630 LOGI("PageHeight=%d", glo->pages[glo->current].height);
631 return glo->pages[glo->current].height;
634 JNIEXPORT jboolean JNICALL
635 JNI_FN(MuPDFCore_javascriptSupported)(JNIEnv *env, jobject thiz)
637 globals *glo = get_globals(env, thiz);
638 pdf_document *idoc = pdf_specifics(glo->doc);
639 return pdf_js_supported(idoc);
642 static void update_changed_rects(globals *glo, page_cache *pc, pdf_document *idoc)
646 pdf_update_page(idoc, (pdf_page *)pc->page);
647 while ((annot = (fz_annot *)pdf_poll_changed_annot(idoc, (pdf_page *)pc->page)) != NULL)
649 /* FIXME: We bound the annot twice here */
650 rect_node *node = fz_malloc_struct(glo->ctx, rect_node);
651 fz_bound_annot(glo->doc, annot, &node->rect);
652 node->next = pc->changed_rects;
653 pc->changed_rects = node;
655 node = fz_malloc_struct(glo->ctx, rect_node);
656 fz_bound_annot(glo->doc, annot, &node->rect);
657 node->next = pc->hq_changed_rects;
658 pc->hq_changed_rects = node;
662 JNIEXPORT jboolean JNICALL
663 JNI_FN(MuPDFCore_drawPage)(JNIEnv *env, jobject thiz, jobject bitmap,
664 int pageW, int pageH, int patchX, int patchY, int patchW, int patchH, jlong cookiePtr)
666 AndroidBitmapInfo info;
669 fz_device *dev = NULL;
674 fz_pixmap *pix = NULL;
675 float xscale, yscale;
676 globals *glo = get_globals(env, thiz);
677 fz_context *ctx = glo->ctx;
678 fz_document *doc = glo->doc;
679 page_cache *pc = &glo->pages[glo->current];
680 int hq = (patchW < pageW || patchH < pageH);
682 fz_cookie *cookie = (fz_cookie *)(intptr_t)cookiePtr;
684 if (pc->page == NULL)
690 LOGI("In native method\n");
691 if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) {
692 LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
696 LOGI("Checking format\n");
697 if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
698 LOGE("Bitmap format is not RGBA_8888 !");
702 LOGI("locking pixels\n");
703 if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) {
704 LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
708 /* Call mupdf to render display list to screen */
709 LOGI("Rendering page(%d)=%dx%d patch=[%d,%d,%d,%d]",
710 pc->number, pageW, pageH, patchX, patchY, patchW, patchH);
715 pdf_document *idoc = pdf_specifics(doc);
719 /* Update the changed-rects for both hq patch and main bitmap */
720 update_changed_rects(glo, pc, idoc);
722 /* Then drop the changed-rects for the bitmap we're about to
723 render because we are rendering the entire area */
724 drop_changed_rects(ctx, hq ? &pc->hq_changed_rects : &pc->changed_rects);
727 if (pc->page_list == NULL)
730 pc->page_list = fz_new_display_list(ctx);
731 dev = fz_new_list_device(ctx, pc->page_list);
732 fz_run_page_contents(doc, pc->page, dev, &fz_identity, cookie);
735 if (cookie != NULL && cookie->abort)
737 fz_drop_display_list(ctx, pc->page_list);
738 pc->page_list = NULL;
739 fz_throw(ctx, FZ_ERROR_GENERIC, "Render aborted");
742 if (pc->annot_list == NULL)
745 pc->annot_list = fz_new_display_list(ctx);
746 dev = fz_new_list_device(ctx, pc->annot_list);
747 for (annot = fz_first_annot(doc, pc->page); annot; annot = fz_next_annot(doc, annot))
748 fz_run_annot(doc, pc->page, annot, dev, &fz_identity, cookie);
751 if (cookie != NULL && cookie->abort)
753 fz_drop_display_list(ctx, pc->annot_list);
754 pc->annot_list = NULL;
755 fz_throw(ctx, FZ_ERROR_GENERIC, "Render aborted");
760 bbox.x1 = patchX + patchW;
761 bbox.y1 = patchY + patchH;
763 pixbbox.x1 = pixbbox.x0 + info.width;
764 /* pixmaps cannot handle right-edge padding, so the bbox must be expanded to
765 * match the pixels data */
766 pix = fz_new_pixmap_with_bbox_and_data(ctx, glo->colorspace, &pixbbox, pixels);
767 if (pc->page_list == NULL && pc->annot_list == NULL)
769 fz_clear_pixmap_with_value(ctx, pix, 0xd0);
772 fz_clear_pixmap_with_value(ctx, pix, 0xff);
774 zoom = glo->resolution / 72;
775 fz_scale(&ctm, zoom, zoom);
776 rect = pc->media_box;
777 fz_round_rect(&bbox, fz_transform_rect(&rect, &ctm));
778 /* Now, adjust ctm so that it would give the correct page width
780 xscale = (float)pageW/(float)(bbox.x1-bbox.x0);
781 yscale = (float)pageH/(float)(bbox.y1-bbox.y0);
782 fz_concat(&ctm, &ctm, fz_scale(&scale, xscale, yscale));
783 rect = pc->media_box;
784 fz_transform_rect(&rect, &ctm);
785 dev = fz_new_draw_device(ctx, pix);
786 #ifdef TIME_DISPLAY_LIST
791 LOGI("Executing display list");
793 for (i=0; i<100;i++) {
796 fz_run_display_list(pc->page_list, dev, &ctm, &rect, cookie);
797 if (cookie != NULL && cookie->abort)
798 fz_throw(ctx, FZ_ERROR_GENERIC, "Render aborted");
801 fz_run_display_list(pc->annot_list, dev, &ctm, &rect, cookie);
802 if (cookie != NULL && cookie->abort)
803 fz_throw(ctx, FZ_ERROR_GENERIC, "Render aborted");
805 #ifdef TIME_DISPLAY_LIST
807 time = clock() - time;
808 LOGI("100 renders in %d (%d per sec)", time, CLOCKS_PER_SEC);
813 fz_drop_pixmap(ctx, pix);
823 LOGE("Render failed");
826 AndroidBitmap_unlockPixels(env, bitmap);
831 static char *widget_type_string(int t)
835 case PDF_WIDGET_TYPE_PUSHBUTTON: return "pushbutton";
836 case PDF_WIDGET_TYPE_CHECKBOX: return "checkbox";
837 case PDF_WIDGET_TYPE_RADIOBUTTON: return "radiobutton";
838 case PDF_WIDGET_TYPE_TEXT: return "text";
839 case PDF_WIDGET_TYPE_LISTBOX: return "listbox";
840 case PDF_WIDGET_TYPE_COMBOBOX: return "combobox";
841 case PDF_WIDGET_TYPE_SIGNATURE: return "signature";
842 default: return "non-widget";
845 JNIEXPORT jboolean JNICALL
846 JNI_FN(MuPDFCore_updatePageInternal)(JNIEnv *env, jobject thiz, jobject bitmap, int page,
847 int pageW, int pageH, int patchX, int patchY, int patchW, int patchH, jlong cookiePtr)
849 AndroidBitmapInfo info;
852 fz_device *dev = NULL;
857 fz_pixmap *pix = NULL;
858 float xscale, yscale;
860 page_cache *pc = NULL;
861 int hq = (patchW < pageW || patchH < pageH);
863 globals *glo = get_globals(env, thiz);
864 fz_context *ctx = glo->ctx;
865 fz_document *doc = glo->doc;
868 fz_cookie *cookie = (fz_cookie *)(intptr_t)cookiePtr;
870 for (i = 0; i < NUM_CACHE; i++)
872 if (glo->pages[i].page != NULL && glo->pages[i].number == page)
881 /* Without a cached page object we cannot perform a partial update so
882 render the entire bitmap instead */
883 JNI_FN(MuPDFCore_gotoPageInternal)(env, thiz, page);
884 return JNI_FN(MuPDFCore_drawPage)(env, thiz, bitmap, pageW, pageH, patchX, patchY, patchW, patchH, (jlong)(intptr_t)cookie);
887 idoc = pdf_specifics(doc);
892 LOGI("In native method\n");
893 if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) {
894 LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
898 LOGI("Checking format\n");
899 if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
900 LOGE("Bitmap format is not RGBA_8888 !");
904 LOGI("locking pixels\n");
905 if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) {
906 LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
910 /* Call mupdf to render display list to screen */
911 LOGI("Rendering page(%d)=%dx%d patch=[%d,%d,%d,%d]",
912 pc->number, pageW, pageH, patchX, patchY, patchW, patchH);
921 /* Update the changed-rects for both hq patch and main bitmap */
922 update_changed_rects(glo, pc, idoc);
925 if (pc->page_list == NULL)
928 pc->page_list = fz_new_display_list(ctx);
929 dev = fz_new_list_device(ctx, pc->page_list);
930 fz_run_page_contents(doc, pc->page, dev, &fz_identity, cookie);
933 if (cookie != NULL && cookie->abort)
935 fz_drop_display_list(ctx, pc->page_list);
936 pc->page_list = NULL;
937 fz_throw(ctx, FZ_ERROR_GENERIC, "Render aborted");
941 if (pc->annot_list == NULL) {
942 pc->annot_list = fz_new_display_list(ctx);
943 dev = fz_new_list_device(ctx, pc->annot_list);
944 for (annot = fz_first_annot(doc, pc->page); annot; annot = fz_next_annot(doc, annot))
945 fz_run_annot(doc, pc->page, annot, dev, &fz_identity, cookie);
948 if (cookie != NULL && cookie->abort)
950 fz_drop_display_list(ctx, pc->annot_list);
951 pc->annot_list = NULL;
952 fz_throw(ctx, FZ_ERROR_GENERIC, "Render aborted");
958 bbox.x1 = patchX + patchW;
959 bbox.y1 = patchY + patchH;
961 pixbbox.x1 = pixbbox.x0 + info.width;
962 /* pixmaps cannot handle right-edge padding, so the bbox must be expanded to
963 * match the pixels data */
964 pix = fz_new_pixmap_with_bbox_and_data(ctx, glo->colorspace, &pixbbox, pixels);
966 zoom = glo->resolution / 72;
967 fz_scale(&ctm, zoom, zoom);
968 rect = pc->media_box;
969 fz_round_rect(&bbox, fz_transform_rect(&rect, &ctm));
970 /* Now, adjust ctm so that it would give the correct page width
972 xscale = (float)pageW/(float)(bbox.x1-bbox.x0);
973 yscale = (float)pageH/(float)(bbox.y1-bbox.y0);
974 fz_concat(&ctm, &ctm, fz_scale(&scale, xscale, yscale));
975 rect = pc->media_box;
976 fz_transform_rect(&rect, &ctm);
978 LOGI("Start partial update");
979 for (crect = hq ? pc->hq_changed_rects : pc->changed_rects; crect; crect = crect->next)
982 fz_rect arect = crect->rect;
983 fz_intersect_rect(fz_transform_rect(&arect, &ctm), &rect);
984 fz_round_rect(&abox, &arect);
986 LOGI("Update rectangle (%d, %d, %d, %d)", abox.x0, abox.y0, abox.x1, abox.y1);
987 if (!fz_is_empty_irect(&abox))
989 LOGI("And it isn't empty");
990 fz_clear_pixmap_rect_with_value(ctx, pix, 0xff, &abox);
991 dev = fz_new_draw_device_with_bbox(ctx, pix, &abox);
993 fz_run_display_list(pc->page_list, dev, &ctm, &arect, cookie);
994 if (cookie != NULL && cookie->abort)
995 fz_throw(ctx, FZ_ERROR_GENERIC, "Render aborted");
998 fz_run_display_list(pc->annot_list, dev, &ctm, &arect, cookie);
999 if (cookie != NULL && cookie->abort)
1000 fz_throw(ctx, FZ_ERROR_GENERIC, "Render aborted");
1002 fz_free_device(dev);
1006 LOGI("End partial update");
1008 /* Drop the changed rects we've just rendered */
1009 drop_changed_rects(ctx, hq ? &pc->hq_changed_rects : &pc->changed_rects);
1015 fz_free_device(dev);
1020 LOGE("Render failed");
1023 fz_drop_pixmap(ctx, pix);
1024 AndroidBitmap_unlockPixels(env, bitmap);
1030 charat(fz_text_page *page, int idx)
1032 fz_char_and_box cab;
1033 return fz_text_char_at(&cab, page, idx)->c;
1037 bboxcharat(fz_text_page *page, int idx)
1039 fz_char_and_box cab;
1040 return fz_text_char_at(&cab, page, idx)->bbox;
1044 textlen(fz_text_page *page)
1049 for (block_num = 0; block_num < page->len; block_num++)
1051 fz_text_block *block;
1054 if (page->blocks[block_num].type != FZ_PAGE_BLOCK_TEXT)
1056 block = page->blocks[block_num].u.text;
1057 for (line = block->lines; line < block->lines + block->len; line++)
1061 for (span = line->first_span; span; span = span->next)
1065 len++; /* pseudo-newline */
1072 countOutlineItems(fz_outline *outline)
1078 if (outline->dest.kind == FZ_LINK_GOTO
1079 && outline->dest.ld.gotor.page >= 0
1083 count += countOutlineItems(outline->down);
1084 outline = outline->next;
1091 fillInOutlineItems(JNIEnv * env, jclass olClass, jmethodID ctor, jobjectArray arr, int pos, fz_outline *outline, int level)
1095 if (outline->dest.kind == FZ_LINK_GOTO)
1097 int page = outline->dest.ld.gotor.page;
1098 if (page >= 0 && outline->title)
1101 jstring title = (*env)->NewStringUTF(env, outline->title);
1102 if (title == NULL) return -1;
1103 ol = (*env)->NewObject(env, olClass, ctor, level, title, page);
1104 if (ol == NULL) return -1;
1105 (*env)->SetObjectArrayElement(env, arr, pos, ol);
1106 (*env)->DeleteLocalRef(env, ol);
1107 (*env)->DeleteLocalRef(env, title);
1111 pos = fillInOutlineItems(env, olClass, ctor, arr, pos, outline->down, level+1);
1112 if (pos < 0) return -1;
1113 outline = outline->next;
1119 JNIEXPORT jboolean JNICALL
1120 JNI_FN(MuPDFCore_needsPasswordInternal)(JNIEnv * env, jobject thiz)
1122 globals *glo = get_globals(env, thiz);
1124 return fz_needs_password(glo->doc) ? JNI_TRUE : JNI_FALSE;
1127 JNIEXPORT jboolean JNICALL
1128 JNI_FN(MuPDFCore_authenticatePasswordInternal)(JNIEnv *env, jobject thiz, jstring password)
1132 globals *glo = get_globals(env, thiz);
1134 pw = (*env)->GetStringUTFChars(env, password, NULL);
1138 result = fz_authenticate_password(glo->doc, (char *)pw);
1139 (*env)->ReleaseStringUTFChars(env, password, pw);
1143 JNIEXPORT jboolean JNICALL
1144 JNI_FN(MuPDFCore_hasOutlineInternal)(JNIEnv * env, jobject thiz)
1146 globals *glo = get_globals(env, thiz);
1147 fz_outline *outline = fz_load_outline(glo->doc);
1149 fz_free_outline(glo->ctx, outline);
1150 return (outline == NULL) ? JNI_FALSE : JNI_TRUE;
1153 JNIEXPORT jobjectArray JNICALL
1154 JNI_FN(MuPDFCore_getOutlineInternal)(JNIEnv * env, jobject thiz)
1160 fz_outline *outline;
1162 globals *glo = get_globals(env, thiz);
1165 olClass = (*env)->FindClass(env, PACKAGENAME "/OutlineItem");
1166 if (olClass == NULL) return NULL;
1167 ctor = (*env)->GetMethodID(env, olClass, "<init>", "(ILjava/lang/String;I)V");
1168 if (ctor == NULL) return NULL;
1170 outline = fz_load_outline(glo->doc);
1171 nItems = countOutlineItems(outline);
1173 arr = (*env)->NewObjectArray(env,
1177 if (arr == NULL) return NULL;
1179 ret = fillInOutlineItems(env, olClass, ctor, arr, 0, outline, 0) > 0
1182 fz_free_outline(glo->ctx, outline);
1186 JNIEXPORT jobjectArray JNICALL
1187 JNI_FN(MuPDFCore_searchPage)(JNIEnv * env, jobject thiz, jstring jtext)
1193 fz_text_sheet *sheet = NULL;
1194 fz_text_page *text = NULL;
1195 fz_device *dev = NULL;
1203 globals *glo = get_globals(env, thiz);
1204 fz_context *ctx = glo->ctx;
1205 fz_document *doc = glo->doc;
1206 page_cache *pc = &glo->pages[glo->current];
1208 rectClass = (*env)->FindClass(env, "android/graphics/RectF");
1209 if (rectClass == NULL) return NULL;
1210 ctor = (*env)->GetMethodID(env, rectClass, "<init>", "(FFFF)V");
1211 if (ctor == NULL) return NULL;
1212 str = (*env)->GetStringUTFChars(env, jtext, NULL);
1213 if (str == NULL) return NULL;
1221 if (glo->hit_bbox == NULL)
1222 glo->hit_bbox = fz_malloc_array(ctx, MAX_SEARCH_HITS, sizeof(*glo->hit_bbox));
1224 zoom = glo->resolution / 72;
1225 fz_scale(&ctm, zoom, zoom);
1226 sheet = fz_new_text_sheet(ctx);
1227 text = fz_new_text_page(ctx);
1228 dev = fz_new_text_device(ctx, sheet, text);
1229 fz_run_page(doc, pc->page, dev, &ctm, NULL);
1230 fz_free_device(dev);
1233 hit_count = fz_search_text_page(ctx, text, str, glo->hit_bbox, MAX_SEARCH_HITS);
1237 fz_free_text_page(ctx, text);
1238 fz_free_text_sheet(ctx, sheet);
1239 fz_free_device(dev);
1244 (*env)->ReleaseStringUTFChars(env, jtext, str);
1245 cls = (*env)->FindClass(env, "java/lang/OutOfMemoryError");
1247 (*env)->ThrowNew(env, cls, "Out of memory in MuPDFCore_searchPage");
1248 (*env)->DeleteLocalRef(env, cls);
1253 (*env)->ReleaseStringUTFChars(env, jtext, str);
1255 arr = (*env)->NewObjectArray(env,
1259 if (arr == NULL) return NULL;
1261 for (i = 0; i < hit_count; i++) {
1262 rect = (*env)->NewObject(env, rectClass, ctor,
1263 (float) (glo->hit_bbox[i].x0),
1264 (float) (glo->hit_bbox[i].y0),
1265 (float) (glo->hit_bbox[i].x1),
1266 (float) (glo->hit_bbox[i].y1));
1269 (*env)->SetObjectArrayElement(env, arr, i, rect);
1270 (*env)->DeleteLocalRef(env, rect);
1276 JNIEXPORT jobjectArray JNICALL
1277 JNI_FN(MuPDFCore_text)(JNIEnv * env, jobject thiz)
1279 jclass textCharClass;
1280 jclass textSpanClass;
1281 jclass textLineClass;
1282 jclass textBlockClass;
1284 jobjectArray barr = NULL;
1285 fz_text_sheet *sheet = NULL;
1286 fz_text_page *text = NULL;
1287 fz_device *dev = NULL;
1290 globals *glo = get_globals(env, thiz);
1291 fz_context *ctx = glo->ctx;
1292 fz_document *doc = glo->doc;
1293 page_cache *pc = &glo->pages[glo->current];
1295 textCharClass = (*env)->FindClass(env, PACKAGENAME "/TextChar");
1296 if (textCharClass == NULL) return NULL;
1297 textSpanClass = (*env)->FindClass(env, "[L" PACKAGENAME "/TextChar;");
1298 if (textSpanClass == NULL) return NULL;
1299 textLineClass = (*env)->FindClass(env, "[[L" PACKAGENAME "/TextChar;");
1300 if (textLineClass == NULL) return NULL;
1301 textBlockClass = (*env)->FindClass(env, "[[[L" PACKAGENAME "/TextChar;");
1302 if (textBlockClass == NULL) return NULL;
1303 ctor = (*env)->GetMethodID(env, textCharClass, "<init>", "(FFFFC)V");
1304 if (ctor == NULL) return NULL;
1314 zoom = glo->resolution / 72;
1315 fz_scale(&ctm, zoom, zoom);
1316 sheet = fz_new_text_sheet(ctx);
1317 text = fz_new_text_page(ctx);
1318 dev = fz_new_text_device(ctx, sheet, text);
1319 fz_run_page(doc, pc->page, dev, &ctm, NULL);
1320 fz_free_device(dev);
1323 barr = (*env)->NewObjectArray(env, text->len, textBlockClass, NULL);
1324 if (barr == NULL) fz_throw(ctx, FZ_ERROR_GENERIC, "NewObjectArray failed");
1326 for (b = 0; b < text->len; b++)
1328 fz_text_block *block;
1331 if (text->blocks[b].type != FZ_PAGE_BLOCK_TEXT)
1333 block = text->blocks[b].u.text;
1334 larr = (*env)->NewObjectArray(env, block->len, textLineClass, NULL);
1335 if (larr == NULL) fz_throw(ctx, FZ_ERROR_GENERIC, "NewObjectArray failed");
1337 for (l = 0; l < block->len; l++)
1339 fz_text_line *line = &block->lines[l];
1344 for (span = line->first_span; span; span = span->next)
1347 sarr = (*env)->NewObjectArray(env, len, textSpanClass, NULL);
1348 if (sarr == NULL) fz_throw(ctx, FZ_ERROR_GENERIC, "NewObjectArray failed");
1350 for (s=0, span = line->first_span; span; s++, span = span->next)
1352 jobjectArray *carr = (*env)->NewObjectArray(env, span->len, textCharClass, NULL);
1353 if (carr == NULL) fz_throw(ctx, FZ_ERROR_GENERIC, "NewObjectArray failed");
1355 for (c = 0; c < span->len; c++)
1357 fz_text_char *ch = &span->text[c];
1359 fz_text_char_bbox(&bbox, span, c);
1360 jobject cobj = (*env)->NewObject(env, textCharClass, ctor, bbox.x0, bbox.y0, bbox.x1, bbox.y1, ch->c);
1361 if (cobj == NULL) fz_throw(ctx, FZ_ERROR_GENERIC, "NewObjectfailed");
1363 (*env)->SetObjectArrayElement(env, carr, c, cobj);
1364 (*env)->DeleteLocalRef(env, cobj);
1367 (*env)->SetObjectArrayElement(env, sarr, s, carr);
1368 (*env)->DeleteLocalRef(env, carr);
1371 (*env)->SetObjectArrayElement(env, larr, l, sarr);
1372 (*env)->DeleteLocalRef(env, sarr);
1375 (*env)->SetObjectArrayElement(env, barr, b, larr);
1376 (*env)->DeleteLocalRef(env, larr);
1381 fz_free_text_page(ctx, text);
1382 fz_free_text_sheet(ctx, sheet);
1383 fz_free_device(dev);
1387 jclass cls = (*env)->FindClass(env, "java/lang/OutOfMemoryError");
1389 (*env)->ThrowNew(env, cls, "Out of memory in MuPDFCore_text");
1390 (*env)->DeleteLocalRef(env, cls);
1398 JNIEXPORT jbyteArray JNICALL
1399 JNI_FN(MuPDFCore_textAsHtml)(JNIEnv * env, jobject thiz)
1401 fz_text_sheet *sheet = NULL;
1402 fz_text_page *text = NULL;
1403 fz_device *dev = NULL;
1405 globals *glo = get_globals(env, thiz);
1406 fz_context *ctx = glo->ctx;
1407 fz_document *doc = glo->doc;
1408 page_cache *pc = &glo->pages[glo->current];
1409 jbyteArray bArray = NULL;
1410 fz_buffer *buf = NULL;
1411 fz_output *out = NULL;
1424 sheet = fz_new_text_sheet(ctx);
1425 text = fz_new_text_page(ctx);
1426 dev = fz_new_text_device(ctx, sheet, text);
1427 fz_run_page(doc, pc->page, dev, &ctm, NULL);
1428 fz_free_device(dev);
1431 fz_analyze_text(ctx, sheet, text);
1433 buf = fz_new_buffer(ctx, 256);
1434 out = fz_new_output_with_buffer(ctx, buf);
1435 fz_printf(out, "<html>\n");
1436 fz_printf(out, "<style>\n");
1437 fz_printf(out, "body{margin:0;}\n");
1438 fz_printf(out, "div.page{background-color:white;}\n");
1439 fz_printf(out, "div.block{margin:0pt;padding:0pt;}\n");
1440 fz_printf(out, "div.metaline{display:table;width:100%%}\n");
1441 fz_printf(out, "div.line{display:table-row;}\n");
1442 fz_printf(out, "div.cell{display:table-cell;padding-left:0.25em;padding-right:0.25em}\n");
1443 //fz_printf(out, "p{margin:0;padding:0;}\n");
1444 fz_printf(out, "</style>\n");
1445 fz_printf(out, "<body style=\"margin:0\"><div style=\"padding:10px\" id=\"content\">");
1446 fz_print_text_page_html(ctx, out, text);
1447 fz_printf(out, "</div></body>\n");
1448 fz_printf(out, "<style>\n");
1449 fz_print_text_sheet(ctx, out, sheet);
1450 fz_printf(out, "</style>\n</html>\n");
1451 fz_close_output(out);
1454 bArray = (*env)->NewByteArray(env, buf->len);
1456 fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to make byteArray");
1457 (*env)->SetByteArrayRegion(env, bArray, 0, buf->len, buf->data);
1462 fz_free_text_page(ctx, text);
1463 fz_free_text_sheet(ctx, sheet);
1464 fz_free_device(dev);
1465 fz_close_output(out);
1466 fz_drop_buffer(ctx, buf);
1470 jclass cls = (*env)->FindClass(env, "java/lang/OutOfMemoryError");
1472 (*env)->ThrowNew(env, cls, "Out of memory in MuPDFCore_textAsHtml");
1473 (*env)->DeleteLocalRef(env, cls);
1481 JNIEXPORT void JNICALL
1482 JNI_FN(MuPDFCore_addMarkupAnnotationInternal)(JNIEnv * env, jobject thiz, jobjectArray points, fz_annot_type type)
1484 globals *glo = get_globals(env, thiz);
1485 fz_context *ctx = glo->ctx;
1486 fz_document *doc = glo->doc;
1487 pdf_document *idoc = pdf_specifics(doc);
1488 page_cache *pc = &glo->pages[glo->current];
1490 jfieldID x_fid, y_fid;
1492 fz_point *pts = NULL;
1496 float line_thickness;
1503 case FZ_ANNOT_HIGHLIGHT:
1508 line_thickness = 1.0;
1511 case FZ_ANNOT_UNDERLINE:
1516 line_thickness = LINE_THICKNESS;
1517 line_height = UNDERLINE_HEIGHT;
1519 case FZ_ANNOT_STRIKEOUT:
1524 line_thickness = LINE_THICKNESS;
1525 line_height = STRIKE_HEIGHT;
1537 float zoom = glo->resolution / 72;
1539 fz_scale(&ctm, zoom, zoom);
1540 pt_cls = (*env)->FindClass(env, "android/graphics/PointF");
1541 if (pt_cls == NULL) fz_throw(ctx, FZ_ERROR_GENERIC, "FindClass");
1542 x_fid = (*env)->GetFieldID(env, pt_cls, "x", "F");
1543 if (x_fid == NULL) fz_throw(ctx, FZ_ERROR_GENERIC, "GetFieldID(x)");
1544 y_fid = (*env)->GetFieldID(env, pt_cls, "y", "F");
1545 if (y_fid == NULL) fz_throw(ctx, FZ_ERROR_GENERIC, "GetFieldID(y)");
1547 n = (*env)->GetArrayLength(env, points);
1549 pts = fz_malloc_array(ctx, n, sizeof(fz_point));
1551 for (i = 0; i < n; i++)
1553 jobject opt = (*env)->GetObjectArrayElement(env, points, i);
1554 pts[i].x = opt ? (*env)->GetFloatField(env, opt, x_fid) : 0.0f;
1555 pts[i].y = opt ? (*env)->GetFloatField(env, opt, y_fid) : 0.0f;
1556 fz_transform_point(&pts[i], &ctm);
1559 annot = (fz_annot *)pdf_create_annot(idoc, (pdf_page *)pc->page, type);
1561 pdf_set_markup_annot_quadpoints(idoc, (pdf_annot *)annot, pts, n);
1562 pdf_set_markup_appearance(idoc, (pdf_annot *)annot, color, alpha, line_thickness, line_height);
1564 dump_annotation_display_lists(glo);
1572 LOGE("addStrikeOutAnnotation: %s failed", ctx->error->message);
1573 jclass cls = (*env)->FindClass(env, "java/lang/OutOfMemoryError");
1575 (*env)->ThrowNew(env, cls, "Out of memory in MuPDFCore_searchPage");
1576 (*env)->DeleteLocalRef(env, cls);
1580 JNIEXPORT void JNICALL
1581 JNI_FN(MuPDFCore_addInkAnnotationInternal)(JNIEnv * env, jobject thiz, jobjectArray arcs)
1583 globals *glo = get_globals(env, thiz);
1584 fz_context *ctx = glo->ctx;
1585 fz_document *doc = glo->doc;
1586 pdf_document *idoc = pdf_specifics(doc);
1587 page_cache *pc = &glo->pages[glo->current];
1589 jfieldID x_fid, y_fid;
1591 fz_point *pts = NULL;
1610 float zoom = glo->resolution / 72;
1612 fz_scale(&ctm, zoom, zoom);
1613 pt_cls = (*env)->FindClass(env, "android/graphics/PointF");
1614 if (pt_cls == NULL) fz_throw(ctx, FZ_ERROR_GENERIC, "FindClass");
1615 x_fid = (*env)->GetFieldID(env, pt_cls, "x", "F");
1616 if (x_fid == NULL) fz_throw(ctx, FZ_ERROR_GENERIC, "GetFieldID(x)");
1617 y_fid = (*env)->GetFieldID(env, pt_cls, "y", "F");
1618 if (y_fid == NULL) fz_throw(ctx, FZ_ERROR_GENERIC, "GetFieldID(y)");
1620 n = (*env)->GetArrayLength(env, arcs);
1622 counts = fz_malloc_array(ctx, n, sizeof(int));
1624 for (i = 0; i < n; i++)
1626 jobjectArray arc = (jobjectArray)(*env)->GetObjectArrayElement(env, arcs, i);
1627 int count = (*env)->GetArrayLength(env, arc);
1633 pts = fz_malloc_array(ctx, total, sizeof(fz_point));
1636 for (i = 0; i < n; i++)
1638 jobjectArray arc = (jobjectArray)(*env)->GetObjectArrayElement(env, arcs, i);
1639 int count = counts[i];
1641 for (j = 0; j < count; j++)
1643 jobject pt = (*env)->GetObjectArrayElement(env, arc, j);
1645 pts[k].x = pt ? (*env)->GetFloatField(env, pt, x_fid) : 0.0f;
1646 pts[k].y = pt ? (*env)->GetFloatField(env, pt, y_fid) : 0.0f;
1647 (*env)->DeleteLocalRef(env, pt);
1648 fz_transform_point(&pts[k], &ctm);
1651 (*env)->DeleteLocalRef(env, arc);
1654 annot = (fz_annot *)pdf_create_annot(idoc, (pdf_page *)pc->page, FZ_ANNOT_INK);
1656 pdf_set_ink_annot_list(idoc, (pdf_annot *)annot, pts, counts, n, color, INK_THICKNESS);
1658 dump_annotation_display_lists(glo);
1663 fz_free(ctx, counts);
1667 LOGE("addInkAnnotation: %s failed", ctx->error->message);
1668 jclass cls = (*env)->FindClass(env, "java/lang/OutOfMemoryError");
1670 (*env)->ThrowNew(env, cls, "Out of memory in MuPDFCore_searchPage");
1671 (*env)->DeleteLocalRef(env, cls);
1675 JNIEXPORT void JNICALL
1676 JNI_FN(MuPDFCore_deleteAnnotationInternal)(JNIEnv * env, jobject thiz, int annot_index)
1678 globals *glo = get_globals(env, thiz);
1679 fz_context *ctx = glo->ctx;
1680 fz_document *doc = glo->doc;
1681 pdf_document *idoc = pdf_specifics(doc);
1682 page_cache *pc = &glo->pages[glo->current];
1691 annot = fz_first_annot(glo->doc, pc->page);
1692 for (i = 0; i < annot_index && annot; i++)
1693 annot = fz_next_annot(glo->doc, annot);
1697 pdf_delete_annot(idoc, (pdf_page *)pc->page, (pdf_annot *)annot);
1698 dump_annotation_display_lists(glo);
1703 LOGE("deleteAnnotationInternal: %s", ctx->error->message);
1707 /* Close the document, at least enough to be able to save over it. This
1708 * may be called again later, so must be idempotent. */
1709 static void close_doc(globals *glo)
1713 fz_free(glo->ctx, glo->hit_bbox);
1714 glo->hit_bbox = NULL;
1716 for (i = 0; i < NUM_CACHE; i++)
1717 drop_page_cache(glo, &glo->pages[i]);
1721 fz_close_document(glo->doc);
1725 JNIEXPORT void JNICALL
1726 JNI_FN(MuPDFCore_destroying)(JNIEnv * env, jobject thiz)
1728 globals *glo = get_globals(env, thiz);
1733 fz_free(glo->ctx, glo->current_path);
1734 glo->current_path = NULL;
1736 fz_free_context(glo->ctx);
1740 LOGI("Destroying dump start");
1741 Memento_listBlocks();
1743 LOGI("Destroying dump end");
1746 // Apparently we should really be writing to whatever path we get
1747 // from calling getFilesDir() in the java part, which supposedly
1748 // gives /sdcard/data/data/com.artifex.MuPDF/gmon.out, but that's
1750 setenv("CPUPROFILE", "/sdcard/gmon.out", 1);
1755 JNIEXPORT jobjectArray JNICALL
1756 JNI_FN(MuPDFCore_getPageLinksInternal)(JNIEnv * env, jobject thiz, int pageNumber)
1758 jclass linkInfoClass;
1759 jclass linkInfoInternalClass;
1760 jclass linkInfoExternalClass;
1761 jclass linkInfoRemoteClass;
1762 jmethodID ctorInternal;
1763 jmethodID ctorExternal;
1764 jmethodID ctorRemote;
1773 globals *glo = get_globals(env, thiz);
1775 linkInfoClass = (*env)->FindClass(env, PACKAGENAME "/LinkInfo");
1776 if (linkInfoClass == NULL) return NULL;
1777 linkInfoInternalClass = (*env)->FindClass(env, PACKAGENAME "/LinkInfoInternal");
1778 if (linkInfoInternalClass == NULL) return NULL;
1779 linkInfoExternalClass = (*env)->FindClass(env, PACKAGENAME "/LinkInfoExternal");
1780 if (linkInfoExternalClass == NULL) return NULL;
1781 linkInfoRemoteClass = (*env)->FindClass(env, PACKAGENAME "/LinkInfoRemote");
1782 if (linkInfoRemoteClass == NULL) return NULL;
1783 ctorInternal = (*env)->GetMethodID(env, linkInfoInternalClass, "<init>", "(FFFFI)V");
1784 if (ctorInternal == NULL) return NULL;
1785 ctorExternal = (*env)->GetMethodID(env, linkInfoExternalClass, "<init>", "(FFFFLjava/lang/String;)V");
1786 if (ctorExternal == NULL) return NULL;
1787 ctorRemote = (*env)->GetMethodID(env, linkInfoRemoteClass, "<init>", "(FFFFLjava/lang/String;IZ)V");
1788 if (ctorRemote == NULL) return NULL;
1790 JNI_FN(MuPDFCore_gotoPageInternal)(env, thiz, pageNumber);
1791 pc = &glo->pages[glo->current];
1792 if (pc->page == NULL || pc->number != pageNumber)
1795 zoom = glo->resolution / 72;
1796 fz_scale(&ctm, zoom, zoom);
1798 list = fz_load_links(glo->doc, pc->page);
1800 for (link = list; link; link = link->next)
1802 switch (link->dest.kind)
1811 arr = (*env)->NewObjectArray(env, count, linkInfoClass, NULL);
1814 fz_drop_link(glo->ctx, list);
1819 for (link = list; link; link = link->next)
1821 fz_rect rect = link->rect;
1822 fz_transform_rect(&rect, &ctm);
1824 switch (link->dest.kind)
1828 linkInfo = (*env)->NewObject(env, linkInfoInternalClass, ctorInternal,
1829 (float)rect.x0, (float)rect.y0, (float)rect.x1, (float)rect.y1,
1830 link->dest.ld.gotor.page);
1836 jstring juri = (*env)->NewStringUTF(env, link->dest.ld.gotor.file_spec);
1837 linkInfo = (*env)->NewObject(env, linkInfoRemoteClass, ctorRemote,
1838 (float)rect.x0, (float)rect.y0, (float)rect.x1, (float)rect.y1,
1839 juri, link->dest.ld.gotor.page, link->dest.ld.gotor.new_window ? JNI_TRUE : JNI_FALSE);
1845 jstring juri = (*env)->NewStringUTF(env, link->dest.ld.uri.uri);
1846 linkInfo = (*env)->NewObject(env, linkInfoExternalClass, ctorExternal,
1847 (float)rect.x0, (float)rect.y0, (float)rect.x1, (float)rect.y1,
1856 if (linkInfo == NULL)
1858 fz_drop_link(glo->ctx, list);
1861 (*env)->SetObjectArrayElement(env, arr, count, linkInfo);
1862 (*env)->DeleteLocalRef(env, linkInfo);
1865 fz_drop_link(glo->ctx, list);
1870 JNIEXPORT jobjectArray JNICALL
1871 JNI_FN(MuPDFCore_getWidgetAreasInternal)(JNIEnv * env, jobject thiz, int pageNumber)
1883 globals *glo = get_globals(env, thiz);
1887 rectFClass = (*env)->FindClass(env, "android/graphics/RectF");
1888 if (rectFClass == NULL) return NULL;
1889 ctor = (*env)->GetMethodID(env, rectFClass, "<init>", "(FFFF)V");
1890 if (ctor == NULL) return NULL;
1892 JNI_FN(MuPDFCore_gotoPageInternal)(env, thiz, pageNumber);
1893 pc = &glo->pages[glo->current];
1894 if (pc->number != pageNumber || pc->page == NULL)
1897 idoc = pdf_specifics(glo->doc);
1901 zoom = glo->resolution / 72;
1902 fz_scale(&ctm, zoom, zoom);
1905 for (widget = pdf_first_widget(idoc, (pdf_page *)pc->page); widget; widget = pdf_next_widget(widget))
1908 arr = (*env)->NewObjectArray(env, count, rectFClass, NULL);
1909 if (arr == NULL) return NULL;
1912 for (widget = pdf_first_widget(idoc, (pdf_page *)pc->page); widget; widget = pdf_next_widget(widget))
1915 pdf_bound_widget(widget, &rect);
1916 fz_transform_rect(&rect, &ctm);
1918 rectF = (*env)->NewObject(env, rectFClass, ctor,
1919 (float)rect.x0, (float)rect.y0, (float)rect.x1, (float)rect.y1);
1920 if (rectF == NULL) return NULL;
1921 (*env)->SetObjectArrayElement(env, arr, count, rectF);
1922 (*env)->DeleteLocalRef(env, rectF);
1930 JNIEXPORT jobjectArray JNICALL
1931 JNI_FN(MuPDFCore_getAnnotationsInternal)(JNIEnv * env, jobject thiz, int pageNumber)
1942 globals *glo = get_globals(env, thiz);
1946 annotClass = (*env)->FindClass(env, PACKAGENAME "/Annotation");
1947 if (annotClass == NULL) return NULL;
1948 ctor = (*env)->GetMethodID(env, annotClass, "<init>", "(FFFFI)V");
1949 if (ctor == NULL) return NULL;
1951 JNI_FN(MuPDFCore_gotoPageInternal)(env, thiz, pageNumber);
1952 pc = &glo->pages[glo->current];
1953 if (pc->number != pageNumber || pc->page == NULL)
1956 zoom = glo->resolution / 72;
1957 fz_scale(&ctm, zoom, zoom);
1960 for (annot = fz_first_annot(glo->doc, pc->page); annot; annot = fz_next_annot(glo->doc, annot))
1963 arr = (*env)->NewObjectArray(env, count, annotClass, NULL);
1964 if (arr == NULL) return NULL;
1967 for (annot = fz_first_annot(glo->doc, pc->page); annot; annot = fz_next_annot(glo->doc, annot))
1970 fz_annot_type type = pdf_annot_type((pdf_annot *)annot);
1971 fz_bound_annot(glo->doc, annot, &rect);
1972 fz_transform_rect(&rect, &ctm);
1974 jannot = (*env)->NewObject(env, annotClass, ctor,
1975 (float)rect.x0, (float)rect.y0, (float)rect.x1, (float)rect.y1, type);
1976 if (jannot == NULL) return NULL;
1977 (*env)->SetObjectArrayElement(env, arr, count, jannot);
1978 (*env)->DeleteLocalRef(env, jannot);
1986 JNIEXPORT int JNICALL
1987 JNI_FN(MuPDFCore_passClickEventInternal)(JNIEnv * env, jobject thiz, int pageNumber, float x, float y)
1989 globals *glo = get_globals(env, thiz);
1990 fz_context *ctx = glo->ctx;
1992 pdf_document *idoc = pdf_specifics(glo->doc);
2002 JNI_FN(MuPDFCore_gotoPageInternal)(env, thiz, pageNumber);
2003 pc = &glo->pages[glo->current];
2004 if (pc->number != pageNumber || pc->page == NULL)
2010 /* Ultimately we should probably return a pointer to a java structure
2011 * with the link details in, but for now, page number will suffice.
2013 zoom = glo->resolution / 72;
2014 fz_scale(&ctm, zoom, zoom);
2015 fz_invert_matrix(&ctm, &ctm);
2017 fz_transform_point(&p, &ctm);
2021 event.etype = PDF_EVENT_TYPE_POINTER;
2022 event.event.pointer.pt = p;
2023 event.event.pointer.ptype = PDF_POINTER_DOWN;
2024 changed = pdf_pass_event(idoc, (pdf_page *)pc->page, &event);
2025 event.event.pointer.ptype = PDF_POINTER_UP;
2026 changed |= pdf_pass_event(idoc, (pdf_page *)pc->page, &event);
2028 dump_annotation_display_lists(glo);
2033 LOGE("passClickEvent: %s", ctx->error->message);
2039 JNIEXPORT jstring JNICALL
2040 JNI_FN(MuPDFCore_getFocusedWidgetTextInternal)(JNIEnv * env, jobject thiz)
2043 globals *glo = get_globals(env, thiz);
2044 fz_context *ctx = glo->ctx;
2048 pdf_document *idoc = pdf_specifics(glo->doc);
2052 pdf_widget *focus = pdf_focused_widget(idoc);
2055 text = pdf_text_widget_text(idoc, focus);
2060 LOGE("getFocusedWidgetText failed: %s", ctx->error->message);
2063 return (*env)->NewStringUTF(env, text);
2066 JNIEXPORT int JNICALL
2067 JNI_FN(MuPDFCore_setFocusedWidgetTextInternal)(JNIEnv * env, jobject thiz, jstring jtext)
2071 globals *glo = get_globals(env, thiz);
2072 fz_context *ctx = glo->ctx;
2074 text = (*env)->GetStringUTFChars(env, jtext, NULL);
2077 LOGE("Failed to get text");
2083 pdf_document *idoc = pdf_specifics(glo->doc);
2087 pdf_widget *focus = pdf_focused_widget(idoc);
2091 result = pdf_text_widget_set_text(idoc, focus, (char *)text);
2092 dump_annotation_display_lists(glo);
2098 LOGE("setFocusedWidgetText failed: %s", ctx->error->message);
2101 (*env)->ReleaseStringUTFChars(env, jtext, text);
2106 JNIEXPORT jobjectArray JNICALL
2107 JNI_FN(MuPDFCore_getFocusedWidgetChoiceOptions)(JNIEnv * env, jobject thiz)
2109 globals *glo = get_globals(env, thiz);
2110 fz_context *ctx = glo->ctx;
2111 pdf_document *idoc = pdf_specifics(glo->doc);
2122 focus = pdf_focused_widget(idoc);
2126 type = pdf_widget_get_type(focus);
2127 if (type != PDF_WIDGET_TYPE_LISTBOX && type != PDF_WIDGET_TYPE_COMBOBOX)
2133 nopts = pdf_choice_widget_options(idoc, focus, NULL);
2134 opts = fz_malloc(ctx, nopts * sizeof(*opts));
2135 (void)pdf_choice_widget_options(idoc, focus, opts);
2140 LOGE("Failed in getFocuseedWidgetChoiceOptions");
2144 stringClass = (*env)->FindClass(env, "java/lang/String");
2146 arr = (*env)->NewObjectArray(env, nopts, stringClass, NULL);
2148 for (i = 0; i < nopts; i++)
2150 jstring s = (*env)->NewStringUTF(env, opts[i]);
2152 (*env)->SetObjectArrayElement(env, arr, i, s);
2154 (*env)->DeleteLocalRef(env, s);
2162 JNIEXPORT jobjectArray JNICALL
2163 JNI_FN(MuPDFCore_getFocusedWidgetChoiceSelected)(JNIEnv * env, jobject thiz)
2165 globals *glo = get_globals(env, thiz);
2166 fz_context *ctx = glo->ctx;
2167 pdf_document *idoc = pdf_specifics(glo->doc);
2178 focus = pdf_focused_widget(idoc);
2182 type = pdf_widget_get_type(focus);
2183 if (type != PDF_WIDGET_TYPE_LISTBOX && type != PDF_WIDGET_TYPE_COMBOBOX)
2189 nsel = pdf_choice_widget_value(idoc, focus, NULL);
2190 sel = fz_malloc(ctx, nsel * sizeof(*sel));
2191 (void)pdf_choice_widget_value(idoc, focus, sel);
2196 LOGE("Failed in getFocuseedWidgetChoiceOptions");
2200 stringClass = (*env)->FindClass(env, "java/lang/String");
2202 arr = (*env)->NewObjectArray(env, nsel, stringClass, NULL);
2204 for (i = 0; i < nsel; i++)
2206 jstring s = (*env)->NewStringUTF(env, sel[i]);
2208 (*env)->SetObjectArrayElement(env, arr, i, s);
2210 (*env)->DeleteLocalRef(env, s);
2218 JNIEXPORT void JNICALL
2219 JNI_FN(MuPDFCore_setFocusedWidgetChoiceSelectedInternal)(JNIEnv * env, jobject thiz, jobjectArray arr)
2221 globals *glo = get_globals(env, thiz);
2222 fz_context *ctx = glo->ctx;
2223 pdf_document *idoc = pdf_specifics(glo->doc);
2228 jstring *objs = NULL;
2233 focus = pdf_focused_widget(idoc);
2237 type = pdf_widget_get_type(focus);
2238 if (type != PDF_WIDGET_TYPE_LISTBOX && type != PDF_WIDGET_TYPE_COMBOBOX)
2241 nsel = (*env)->GetArrayLength(env, arr);
2243 sel = calloc(nsel, sizeof(*sel));
2244 objs = calloc(nsel, sizeof(*objs));
2245 if (objs == NULL || sel == NULL)
2249 LOGE("Failed in setFocusWidgetChoiceSelected");
2253 for (i = 0; i < nsel; i++)
2255 objs[i] = (jstring)(*env)->GetObjectArrayElement(env, arr, i);
2256 sel[i] = (char *)(*env)->GetStringUTFChars(env, objs[i], NULL);
2261 pdf_choice_widget_set_value(idoc, focus, nsel, sel);
2262 dump_annotation_display_lists(glo);
2266 LOGE("Failed in setFocusWidgetChoiceSelected");
2269 for (i = 0; i < nsel; i++)
2270 (*env)->ReleaseStringUTFChars(env, objs[i], sel[i]);
2276 JNIEXPORT int JNICALL
2277 JNI_FN(MuPDFCore_getFocusedWidgetTypeInternal)(JNIEnv * env, jobject thiz)
2279 globals *glo = get_globals(env, thiz);
2280 pdf_document *idoc = pdf_specifics(glo->doc);
2286 focus = pdf_focused_widget(idoc);
2291 switch (pdf_widget_get_type(focus))
2293 case PDF_WIDGET_TYPE_TEXT: return TEXT;
2294 case PDF_WIDGET_TYPE_LISTBOX: return LISTBOX;
2295 case PDF_WIDGET_TYPE_COMBOBOX: return COMBOBOX;
2296 case PDF_WIDGET_TYPE_SIGNATURE: return SIGNATURE;
2302 /* This enum should be kept in line with SignatureState in MuPDFPageView.java */
2305 Signature_NoSupport,
2310 JNIEXPORT int JNICALL
2311 JNI_FN(MuPDFCore_getFocusedWidgetSignatureState)(JNIEnv * env, jobject thiz)
2313 globals *glo = get_globals(env, thiz);
2314 pdf_document *idoc = pdf_specifics(glo->doc);
2318 return Signature_NoSupport;
2320 focus = pdf_focused_widget(idoc);
2323 return Signature_NoSupport;
2325 if (!pdf_signatures_supported())
2326 return Signature_NoSupport;
2328 return pdf_dict_gets(((pdf_annot *)focus)->obj, "V") ? Signature_Signed : Signature_Unsigned;
2331 JNIEXPORT jstring JNICALL
2332 JNI_FN(MuPDFCore_checkFocusedSignatureInternal)(JNIEnv * env, jobject thiz)
2334 globals *glo = get_globals(env, thiz);
2335 pdf_document *idoc = pdf_specifics(glo->doc);
2337 char ebuf[256] = "Failed";
2342 focus = pdf_focused_widget(idoc);
2347 if (pdf_check_signature(idoc, focus, glo->current_path, ebuf, sizeof(ebuf)))
2349 strcpy(ebuf, "Signature is valid");
2353 return (*env)->NewStringUTF(env, ebuf);
2356 JNIEXPORT jboolean JNICALL
2357 JNI_FN(MuPDFCore_signFocusedSignatureInternal)(JNIEnv * env, jobject thiz, jstring jkeyfile, jstring jpassword)
2359 globals *glo = get_globals(env, thiz);
2360 fz_context *ctx = glo->ctx;
2361 pdf_document *idoc = pdf_specifics(glo->doc);
2363 const char *keyfile;
2364 const char *password;
2370 focus = pdf_focused_widget(idoc);
2375 keyfile = (*env)->GetStringUTFChars(env, jkeyfile, NULL);
2376 password = (*env)->GetStringUTFChars(env, jpassword, NULL);
2377 if (keyfile == NULL || password == NULL)
2383 pdf_sign_signature(idoc, focus, keyfile, password);
2384 dump_annotation_display_lists(glo);
2395 JNIEXPORT jobject JNICALL
2396 JNI_FN(MuPDFCore_waitForAlertInternal)(JNIEnv * env, jobject thiz)
2398 globals *glo = get_globals(env, thiz);
2404 pdf_alert_event alert;
2406 LOGT("Enter waitForAlert");
2407 pthread_mutex_lock(&glo->fin_lock);
2408 pthread_mutex_lock(&glo->alert_lock);
2410 while (glo->alerts_active && !glo->alert_request)
2411 pthread_cond_wait(&glo->alert_request_cond, &glo->alert_lock);
2412 glo->alert_request = 0;
2414 alert_present = (glo->alerts_active && glo->current_alert);
2417 alert = *glo->current_alert;
2419 pthread_mutex_unlock(&glo->alert_lock);
2420 pthread_mutex_unlock(&glo->fin_lock);
2421 LOGT("Exit waitForAlert %d", alert_present);
2426 alertClass = (*env)->FindClass(env, PACKAGENAME "/MuPDFAlertInternal");
2427 if (alertClass == NULL)
2430 ctor = (*env)->GetMethodID(env, alertClass, "<init>", "(Ljava/lang/String;IILjava/lang/String;I)V");
2434 title = (*env)->NewStringUTF(env, alert.title);
2438 message = (*env)->NewStringUTF(env, alert.message);
2439 if (message == NULL)
2442 return (*env)->NewObject(env, alertClass, ctor, message, alert.icon_type, alert.button_group_type, title, alert.button_pressed);
2445 JNIEXPORT void JNICALL
2446 JNI_FN(MuPDFCore_replyToAlertInternal)(JNIEnv * env, jobject thiz, jobject alert)
2448 globals *glo = get_globals(env, thiz);
2453 alertClass = (*env)->FindClass(env, PACKAGENAME "/MuPDFAlertInternal");
2454 if (alertClass == NULL)
2457 field = (*env)->GetFieldID(env, alertClass, "buttonPressed", "I");
2461 button_pressed = (*env)->GetIntField(env, alert, field);
2463 LOGT("Enter replyToAlert");
2464 pthread_mutex_lock(&glo->alert_lock);
2466 if (glo->alerts_active && glo->current_alert)
2468 // Fill in button_pressed and signal reply received.
2469 glo->current_alert->button_pressed = button_pressed;
2470 glo->alert_reply = 1;
2471 pthread_cond_signal(&glo->alert_reply_cond);
2474 pthread_mutex_unlock(&glo->alert_lock);
2475 LOGT("Exit replyToAlert");
2478 JNIEXPORT void JNICALL
2479 JNI_FN(MuPDFCore_startAlertsInternal)(JNIEnv * env, jobject thiz)
2481 globals *glo = get_globals(env, thiz);
2483 if (!glo->alerts_initialised)
2486 LOGT("Enter startAlerts");
2487 pthread_mutex_lock(&glo->alert_lock);
2489 glo->alert_reply = 0;
2490 glo->alert_request = 0;
2491 glo->alerts_active = 1;
2492 glo->current_alert = NULL;
2494 pthread_mutex_unlock(&glo->alert_lock);
2495 LOGT("Exit startAlerts");
2498 JNIEXPORT void JNICALL
2499 JNI_FN(MuPDFCore_stopAlertsInternal)(JNIEnv * env, jobject thiz)
2501 globals *glo = get_globals(env, thiz);
2503 if (!glo->alerts_initialised)
2506 LOGT("Enter stopAlerts");
2507 pthread_mutex_lock(&glo->alert_lock);
2509 glo->alert_reply = 0;
2510 glo->alert_request = 0;
2511 glo->alerts_active = 0;
2512 glo->current_alert = NULL;
2513 pthread_cond_signal(&glo->alert_reply_cond);
2514 pthread_cond_signal(&glo->alert_request_cond);
2516 pthread_mutex_unlock(&glo->alert_lock);
2517 LOGT("Exit stopAleerts");
2520 JNIEXPORT jboolean JNICALL
2521 JNI_FN(MuPDFCore_hasChangesInternal)(JNIEnv * env, jobject thiz)
2523 globals *glo = get_globals(env, thiz);
2524 pdf_document *idoc = pdf_specifics(glo->doc);
2526 return (idoc && pdf_has_unsaved_changes(idoc)) ? JNI_TRUE : JNI_FALSE;
2529 static char *tmp_path(char *path)
2532 char *buf = malloc(strlen(path) + 6 + 1);
2537 strcat(buf, "XXXXXX");
2553 JNIEXPORT void JNICALL
2554 JNI_FN(MuPDFCore_saveInternal)(JNIEnv * env, jobject thiz)
2556 globals *glo = get_globals(env, thiz);
2557 fz_context *ctx = glo->ctx;
2559 if (glo->doc && glo->current_path)
2562 fz_write_options opts;
2563 opts.do_incremental = 1;
2566 opts.do_garbage = 0;
2569 tmp = tmp_path(glo->current_path);
2577 FILE *fin = fopen(glo->current_path, "rb");
2578 FILE *fout = fopen(tmp, "wb");
2584 while ((n = fread(buf, 1, sizeof(buf), fin)) > 0)
2585 fwrite(buf, 1, n, fout);
2586 err = (ferror(fin) || ferror(fout));
2596 fz_write_document(glo->doc, tmp, &opts);
2608 rename(tmp, glo->current_path);
2616 JNIEXPORT void JNICALL
2617 JNI_FN(MuPDFCore_dumpMemoryInternal)(JNIEnv * env, jobject thiz)
2619 globals *glo = get_globals(env, thiz);
2620 fz_context *ctx = glo->ctx;
2623 LOGE("dumpMemoryInternal start");
2624 Memento_listNewBlocks();
2626 LOGE("dumpMemoryInternal end");
2630 JNIEXPORT jlong JNICALL
2631 JNI_FN(MuPDFCore_createCookie)(JNIEnv * env, jobject thiz)
2633 globals *glo = get_globals_any_thread(env, thiz);
2636 fz_context *ctx = glo->ctx;
2638 return (jlong) (intptr_t) fz_calloc_no_throw(ctx,1, sizeof(fz_cookie));
2641 JNIEXPORT void JNICALL
2642 JNI_FN(MuPDFCore_destroyCookie)(JNIEnv * env, jobject thiz, jlong cookiePtr)
2644 fz_cookie *cookie = (fz_cookie *) (intptr_t) cookiePtr;
2645 globals *glo = get_globals_any_thread(env, thiz);
2648 fz_context *ctx = glo->ctx;
2650 fz_free(ctx, cookie);
2653 JNIEXPORT void JNICALL
2654 JNI_FN(MuPDFCore_abortCookie)(JNIEnv * env, jobject thiz, jlong cookiePtr)
2656 fz_cookie *cookie = (fz_cookie *) (intptr_t) cookiePtr;