From b3708b652831202b8fc600ccdac8ee3218966dc9 Mon Sep 17 00:00:00 2001 From: Paul Gardiner Date: Tue, 7 Feb 2012 15:22:55 +0000 Subject: Updated MuPDF Android app from Paul Gardiner. --- android/AndroidManifest.xml | 17 +- android/ReadMe.txt | 3 +- android/jni/Application.mk | 1 + android/jni/mupdf.c | 360 ++++++++++++- android/project.properties | 11 + android/res/anim/fade_in.xml | 5 + android/res/anim/fade_out.xml | 5 + android/res/drawable-hdpi/btn_star_big_off.png | Bin 0 -> 2208 bytes android/res/drawable-hdpi/ic_btn_search.png | Bin 0 -> 2365 bytes android/res/drawable-hdpi/ic_media_next.png | Bin 0 -> 1675 bytes android/res/drawable-hdpi/ic_media_previous.png | Bin 0 -> 1713 bytes android/res/drawable-mdpi/btn_star_big_off.png | Bin 0 -> 1316 bytes android/res/drawable-mdpi/ic_btn_search.png | Bin 0 -> 1390 bytes android/res/drawable-mdpi/ic_media_next.png | Bin 0 -> 1309 bytes android/res/drawable-mdpi/ic_media_previous.png | Bin 0 -> 1295 bytes android/res/drawable/busy.xml | 10 + android/res/drawable/page_num.xml | 10 + android/res/drawable/slider.xml | 10 + android/res/drawable/tile09.jpg | Bin 0 -> 17986 bytes android/res/drawable/tiled_background.xml | 4 + android/res/layout/buttons.xml | 129 +++++ android/res/layout/outline_entry.xml | 25 + android/res/layout/picker_entry.xml | 9 + android/res/values/strings.xml | 10 + .../src/com/artifex/mupdf/ChoosePDFActivity.java | 71 +++ android/src/com/artifex/mupdf/MuPDFActivity.java | 547 +++++++++++++++---- android/src/com/artifex/mupdf/MuPDFCore.java | 77 ++- .../src/com/artifex/mupdf/MuPDFPageAdapter.java | 72 +++ android/src/com/artifex/mupdf/MuPDFPageView.java | 28 + android/src/com/artifex/mupdf/OutlineActivity.java | 29 ++ .../src/com/artifex/mupdf/OutlineActivityData.java | 17 + android/src/com/artifex/mupdf/OutlineAdapter.java | 46 ++ android/src/com/artifex/mupdf/OutlineItem.java | 14 + android/src/com/artifex/mupdf/PageView.java | 323 ++++++++++++ android/src/com/artifex/mupdf/PixmapView.java | 579 --------------------- android/src/com/artifex/mupdf/ReaderView.java | 554 ++++++++++++++++++++ 36 files changed, 2260 insertions(+), 706 deletions(-) create mode 100644 android/project.properties create mode 100644 android/res/anim/fade_in.xml create mode 100644 android/res/anim/fade_out.xml create mode 100644 android/res/drawable-hdpi/btn_star_big_off.png create mode 100644 android/res/drawable-hdpi/ic_btn_search.png create mode 100644 android/res/drawable-hdpi/ic_media_next.png create mode 100644 android/res/drawable-hdpi/ic_media_previous.png create mode 100644 android/res/drawable-mdpi/btn_star_big_off.png create mode 100644 android/res/drawable-mdpi/ic_btn_search.png create mode 100644 android/res/drawable-mdpi/ic_media_next.png create mode 100644 android/res/drawable-mdpi/ic_media_previous.png create mode 100644 android/res/drawable/busy.xml create mode 100644 android/res/drawable/page_num.xml create mode 100644 android/res/drawable/slider.xml create mode 100644 android/res/drawable/tile09.jpg create mode 100644 android/res/drawable/tiled_background.xml create mode 100644 android/res/layout/buttons.xml create mode 100644 android/res/layout/outline_entry.xml create mode 100644 android/res/layout/picker_entry.xml create mode 100644 android/src/com/artifex/mupdf/ChoosePDFActivity.java create mode 100644 android/src/com/artifex/mupdf/MuPDFPageAdapter.java create mode 100644 android/src/com/artifex/mupdf/MuPDFPageView.java create mode 100644 android/src/com/artifex/mupdf/OutlineActivity.java create mode 100644 android/src/com/artifex/mupdf/OutlineActivityData.java create mode 100644 android/src/com/artifex/mupdf/OutlineAdapter.java create mode 100644 android/src/com/artifex/mupdf/OutlineItem.java create mode 100644 android/src/com/artifex/mupdf/PageView.java delete mode 100644 android/src/com/artifex/mupdf/PixmapView.java create mode 100644 android/src/com/artifex/mupdf/ReaderView.java (limited to 'android') diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 1bea0f8a..83c0ad86 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -11,13 +11,20 @@ - + - - + + + + + + + + + + diff --git a/android/ReadMe.txt b/android/ReadMe.txt index 7d111ece..55624ea2 100644 --- a/android/ReadMe.txt +++ b/android/ReadMe.txt @@ -148,7 +148,8 @@ done once). With the emulator running type: adb push ../../MyTests/pdf_reference17.pdf /mnt/sdcard/Download/test.pdf (where obviously ../../MyTests/pdf_reference17.pdf is altered for your -machine). (adb lives in /platform-tools if it's not on your path). +machine, and under Windows, should start c:/ even if invoked from cygwin) (adb lives +in /platform-tools if it's not on your path). 16) With the emulator running (see step 14), execute diff --git a/android/jni/Application.mk b/android/jni/Application.mk index 16de5796..580786f9 100644 --- a/android/jni/Application.mk +++ b/android/jni/Application.mk @@ -1,3 +1,4 @@ # The ARMv7 is significanly faster due to the use of the hardware FPU +APP_PLATFORM=android-8 APP_ABI := armeabi armeabi-v7a APP_OPTIM := debug diff --git a/android/jni/mupdf.c b/android/jni/mupdf.c index 3e471355..4775e072 100644 --- a/android/jni/mupdf.c +++ b/android/jni/mupdf.c @@ -20,6 +20,8 @@ /* Enable to log rendering times (render each frame 100 times and time) */ #undef TIME_DISPLAY_LIST +#define MAX_SEARCH_HITS (500) + /* Globals */ fz_colorspace *colorspace; pdf_document *xref; @@ -30,12 +32,16 @@ float pageHeight = 100; fz_display_list *currentPageList; fz_rect currentMediabox; fz_context *ctx; +int currentPageNumber = -1; +pdf_page *currentPage = NULL; +fz_bbox *hit_bbox = NULL; JNIEXPORT int JNICALL Java_com_artifex_mupdf_MuPDFCore_openFile(JNIEnv * env, jobject thiz, jstring jfilename) { const char *filename; int pages = 0; + int result = 0; filename = (*env)->GetStringUTFChars(env, jfilename, NULL); if (filename == NULL) @@ -66,8 +72,8 @@ Java_com_artifex_mupdf_MuPDFCore_openFile(JNIEnv * env, jobject thiz, jstring jf { fz_throw(ctx, "Cannot open document: '%s'\n", filename); } - pages = pdf_count_pages(xref); - LOGE("Done! %d pages", pages); + LOGE("Done!"); + result = 1; } fz_catch(ctx) { @@ -78,7 +84,15 @@ Java_com_artifex_mupdf_MuPDFCore_openFile(JNIEnv * env, jobject thiz, jstring jf ctx = NULL; } - return pages; + (*env)->ReleaseStringUTFChars(env, jfilename, filename); + + return result; +} + +JNIEXPORT int JNICALL +Java_com_artifex_mupdf_MuPDFCore_countPagesInternal(JNIEnv *env, jobject thiz) +{ + return pdf_count_pages(xref); } JNIEXPORT void JNICALL @@ -88,15 +102,20 @@ Java_com_artifex_mupdf_MuPDFCore_gotoPageInternal(JNIEnv *env, jobject thiz, int fz_matrix ctm; fz_bbox bbox; fz_device *dev = NULL; - pdf_page *currentPage = NULL; fz_var(dev); - fz_var(currentPage); + + if (currentPage != NULL && page != currentPageNumber) + { + pdf_free_page(xref, currentPage); + currentPage = NULL; + } /* In the event of an error, ensure we give a non-empty page */ pageWidth = 100; pageHeight = 100; + currentPageNumber = page; LOGE("Goto page %d...", page); fz_try(ctx) { @@ -113,17 +132,12 @@ Java_com_artifex_mupdf_MuPDFCore_gotoPageInternal(JNIEnv *env, jobject thiz, int bbox = fz_round_rect(fz_transform_rect(ctm, currentMediabox)); pageWidth = bbox.x1-bbox.x0; pageHeight = bbox.y1-bbox.y0; - /* Render to list */ - currentPageList = fz_new_display_list(ctx); - dev = fz_new_list_device(ctx, currentPageList); - pdf_run_page(xref, currentPage, dev, fz_identity, NULL); } fz_catch(ctx) { + currentPageNumber = page; LOGE("cannot make displaylist from page %d", pagenum); } - pdf_free_page(ctx, currentPage); - currentPage = NULL; fz_free_device(dev); dev = NULL; } @@ -184,6 +198,13 @@ Java_com_artifex_mupdf_MuPDFCore_drawPage(JNIEnv *env, jobject thiz, jobject bit fz_try(ctx) { + if (currentPageList == NULL) + { + /* Render to list */ + currentPageList = fz_new_display_list(ctx); + dev = fz_new_list_device(ctx, currentPageList); + pdf_run_page(xref, currentPage, dev, fz_identity, NULL); + } rect.x0 = patchX; rect.y0 = patchY; rect.x1 = patchX + patchW; @@ -191,10 +212,10 @@ Java_com_artifex_mupdf_MuPDFCore_drawPage(JNIEnv *env, jobject thiz, jobject bit pix = fz_new_pixmap_with_rect_and_data(ctx, colorspace, rect, pixels); if (currentPageList == NULL) { - fz_clear_pixmap_with_value(pix, 0xd0); + fz_clear_pixmap_with_value(ctx, pix, 0xd0); break; } - fz_clear_pixmap_with_value(pix, 0xff); + fz_clear_pixmap_with_value(ctx, pix, 0xff); zoom = resolution / 72; ctm = fz_scale(zoom, zoom); @@ -238,11 +259,324 @@ Java_com_artifex_mupdf_MuPDFCore_drawPage(JNIEnv *env, jobject thiz, jobject bit return 1; } +static int +charat(fz_text_span *span, int idx) +{ + int ofs = 0; + while (span) { + if (idx < ofs + span->len) + return span->text[idx - ofs].c; + if (span->eol) { + if (idx == ofs + span->len) + return ' '; + ofs ++; + } + ofs += span->len; + span = span->next; + } + return 0; +} + +static fz_bbox +bboxat(fz_text_span *span, int idx) +{ + int ofs = 0; + while (span) { + if (idx < ofs + span->len) + return span->text[idx - ofs].bbox; + if (span->eol) { + if (idx == ofs + span->len) + return fz_empty_bbox; + ofs ++; + } + ofs += span->len; + span = span->next; + } + return fz_empty_bbox; +} + +static int +textlen(fz_text_span *span) +{ + int len = 0; + while (span) { + len += span->len; + if (span->eol) + len ++; + span = span->next; + } + return len; +} + +static int +match(fz_text_span *span, const char *s, int n) +{ + int start = n, c; + while (*s) { + s += chartorune(&c, (char *)s); + if (c == ' ' && charat(span, n) == ' ') { + while (charat(span, n) == ' ') + n++; + } else { + if (tolower(c) != tolower(charat(span, n))) + return 0; + n++; + } + } + return n - start; +} + +static int +countOutlineItems(fz_outline *outline) +{ + int count = 0; + + while (outline) + { + if (outline->dest.kind == FZ_LINK_GOTO + && outline->dest.ld.gotor.page >= 0 + && outline->title) + count++; + + count += countOutlineItems(outline->down); + outline = outline->next; + } + + return count; +} + +static int +fillInOutlineItems(JNIEnv * env, jclass olClass, jmethodID ctor, jobjectArray arr, int pos, fz_outline *outline, int level) +{ + while (outline) + { + if (outline->dest.kind == FZ_LINK_GOTO) + { + int page = outline->dest.ld.gotor.page; + if (page >= 0 && outline->title) + { + jobject ol; + jstring title = (*env)->NewStringUTF(env, outline->title); + if (title == NULL) return -1; + ol = (*env)->NewObject(env, olClass, ctor, level, title, page); + if (ol == NULL) return -1; + (*env)->SetObjectArrayElement(env, arr, pos, ol); + (*env)->DeleteLocalRef(env, ol); + (*env)->DeleteLocalRef(env, title); + pos++; + } + } + pos = fillInOutlineItems(env, olClass, ctor, arr, pos, outline->down, level+1); + if (pos < 0) return -1; + outline = outline->next; + } + + return pos; +} + +JNIEXPORT jboolean JNICALL +Java_com_artifex_mupdf_MuPDFCore_needsPasswordInternal(JNIEnv * env, jobject thiz) +{ + return pdf_needs_password(xref) ? JNI_TRUE : JNI_FALSE; +} + +JNIEXPORT jboolean JNICALL +Java_com_artifex_mupdf_MuPDFCore_authenticatePasswordInternal(JNIEnv *env, jobject thiz, jstring password) +{ + const char *pw; + int result; + pw = (*env)->GetStringUTFChars(env, password, NULL); + if (pw == NULL) + return JNI_FALSE; + + result = pdf_authenticate_password(xref, (char *)pw); + (*env)->ReleaseStringUTFChars(env, password, pw); + return result; +} + +JNIEXPORT jboolean JNICALL +Java_com_artifex_mupdf_MuPDFCore_hasOutlineInternal(JNIEnv * env, jobject thiz) +{ + fz_outline *outline = pdf_load_outline(xref); + return (outline == NULL) ? JNI_FALSE : JNI_TRUE; +} + +JNIEXPORT jobjectArray JNICALL +Java_com_artifex_mupdf_MuPDFCore_getOutlineInternal(JNIEnv * env, jobject thiz) +{ + jclass olClass; + jmethodID ctor; + jobjectArray arr; + jobject ol; + fz_outline *outline; + int nItems; + + olClass = (*env)->FindClass(env, "com/artifex/mupdf/OutlineItem"); + if (olClass == NULL) return NULL; + ctor = (*env)->GetMethodID(env, olClass, "", "(ILjava/lang/String;I)V"); + if (ctor == NULL) return NULL; + + outline = pdf_load_outline(xref); + nItems = countOutlineItems(outline); + + arr = (*env)->NewObjectArray(env, + nItems, + olClass, + NULL); + if (arr == NULL) return NULL; + + return fillInOutlineItems(env, olClass, ctor, arr, 0, outline, 0) > 0 + ? arr + :NULL; +} + +JNIEXPORT jobjectArray JNICALL +Java_com_artifex_mupdf_MuPDFCore_searchPage(JNIEnv * env, jobject thiz, jstring jtext) +{ + jclass rectClass; + jmethodID ctor; + jobjectArray arr; + jobject rect; + fz_text_span *text = NULL; + fz_device *dev = NULL; + float zoom; + fz_matrix ctm; + int pos; + int len; + int i, n; + int hit_count = 0; + const char *str; + + rectClass = (*env)->FindClass(env, "android/graphics/RectF"); + if (rectClass == NULL) return NULL; + ctor = (*env)->GetMethodID(env, rectClass, "", "(FFFF)V"); + if (ctor == NULL) return NULL; + str = (*env)->GetStringUTFChars(env, jtext, NULL); + if (str == NULL) return NULL; + + fz_var(text); + fz_var(dev); + + fz_try(ctx) + { + if (hit_bbox == NULL) + hit_bbox = fz_malloc_array(ctx, MAX_SEARCH_HITS, sizeof(*hit_bbox)); + + text = fz_new_text_span(ctx); + dev = fz_new_text_device(ctx, text); + zoom = resolution / 72; + ctm = fz_scale(zoom, zoom); + pdf_run_page(xref, currentPage, dev, ctm, NULL); + fz_free_device(dev); + dev = NULL; + + len = textlen(text); + for (pos = 0; pos < len; pos++) + { + fz_bbox rr = fz_empty_bbox; + n = match(text, str, pos); + for (i = 0; i < n; i++) + rr = fz_union_bbox(rr, bboxat(text, pos + i)); + + if (!fz_is_empty_bbox(rr) && hit_count < MAX_SEARCH_HITS) + hit_bbox[hit_count++] = rr; + } + fz_free_text_span(ctx, text); + text = NULL; + } + fz_catch(ctx) + { + jclass cls; + fz_free_device(dev); + fz_free_text_span(ctx, text); + (*env)->ReleaseStringUTFChars(env, jtext, str); + cls = (*env)->FindClass(env, "java/lang/OutOfMemoryError"); + if (cls != NULL) + (*env)->ThrowNew(env, cls, "Out of memory in MuPDFCore_searchPage"); + (*env)->DeleteLocalRef(env, cls); + + return NULL; + } + + (*env)->ReleaseStringUTFChars(env, jtext, str); + + arr = (*env)->NewObjectArray(env, + hit_count, + rectClass, + NULL); + if (arr == NULL) return NULL; + + for (i = 0; i < hit_count; i++) { + rect = (*env)->NewObject(env, rectClass, ctor, + (float) (hit_bbox[i].x0), + (float) (hit_bbox[i].y0), + (float) (hit_bbox[i].x1), + (float) (hit_bbox[i].y1)); + if (rect == NULL) + return NULL; + (*env)->SetObjectArrayElement(env, arr, i, rect); + (*env)->DeleteLocalRef(env, rect); + } + + return arr; +} + JNIEXPORT void JNICALL Java_com_artifex_mupdf_MuPDFCore_destroying(JNIEnv * env, jobject thiz) { + fz_free(ctx, hit_bbox); + hit_bbox = NULL; fz_free_display_list(ctx, currentPageList); currentPageList = NULL; + if (currentPage != NULL) + { + pdf_free_page(xref, currentPage); + currentPage = NULL; + } pdf_close_document(xref); xref = NULL; } + +JNIEXPORT int JNICALL +Java_com_artifex_mupdf_MuPDFCore_getPageLink(JNIEnv * env, jobject thiz, int pageNumber, float x, float y) +{ + fz_matrix ctm; + float zoom; + fz_link *link; + fz_point p; + + Java_com_artifex_mupdf_MuPDFCore_gotoPageInternal(env, thiz, pageNumber); + if (currentPageNumber == -1 || currentPage == NULL) + return -1; + + p.x = x; + p.y = y; + + /* Ultimately we should probably return a pointer to a java structure + * with the link details in, but for now, page number will suffice. + */ + zoom = resolution / 72; + ctm = fz_scale(zoom, zoom); + ctm = fz_invert_matrix(ctm); + + p = fz_transform_point(ctm, p); + + for (link = currentPage->links; link; link = link->next) + { + if (p.x >= link->rect.x0 && p.x <= link->rect.x1) + if (p.y >= link->rect.y0 && p.y <= link->rect.y1) + break; + } + + if (link == NULL) + return -1; + + if (link->dest.kind == FZ_LINK_URI) + { + //gotouri(link->dest.ld.uri.uri); + return -1; + } + else if (link->dest.kind == FZ_LINK_GOTO) + return link->dest.ld.gotor.page; + return -1; +} diff --git a/android/project.properties b/android/project.properties new file mode 100644 index 00000000..ea89160e --- /dev/null +++ b/android/project.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "ant.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-8 diff --git a/android/res/anim/fade_in.xml b/android/res/anim/fade_in.xml new file mode 100644 index 00000000..65bf6edd --- /dev/null +++ b/android/res/anim/fade_in.xml @@ -0,0 +1,5 @@ + + diff --git a/android/res/anim/fade_out.xml b/android/res/anim/fade_out.xml new file mode 100644 index 00000000..efde8c13 --- /dev/null +++ b/android/res/anim/fade_out.xml @@ -0,0 +1,5 @@ + + diff --git a/android/res/drawable-hdpi/btn_star_big_off.png b/android/res/drawable-hdpi/btn_star_big_off.png new file mode 100644 index 00000000..4be0f5df Binary files /dev/null and b/android/res/drawable-hdpi/btn_star_big_off.png differ diff --git a/android/res/drawable-hdpi/ic_btn_search.png b/android/res/drawable-hdpi/ic_btn_search.png new file mode 100644 index 00000000..0a91eabc Binary files /dev/null and b/android/res/drawable-hdpi/ic_btn_search.png differ diff --git a/android/res/drawable-hdpi/ic_media_next.png b/android/res/drawable-hdpi/ic_media_next.png new file mode 100644 index 00000000..2552f4ed Binary files /dev/null and b/android/res/drawable-hdpi/ic_media_next.png differ diff --git a/android/res/drawable-hdpi/ic_media_previous.png b/android/res/drawable-hdpi/ic_media_previous.png new file mode 100644 index 00000000..05eba718 Binary files /dev/null and b/android/res/drawable-hdpi/ic_media_previous.png differ diff --git a/android/res/drawable-mdpi/btn_star_big_off.png b/android/res/drawable-mdpi/btn_star_big_off.png new file mode 100644 index 00000000..7e9342b5 Binary files /dev/null and b/android/res/drawable-mdpi/btn_star_big_off.png differ diff --git a/android/res/drawable-mdpi/ic_btn_search.png b/android/res/drawable-mdpi/ic_btn_search.png new file mode 100644 index 00000000..3f8913e2 Binary files /dev/null and b/android/res/drawable-mdpi/ic_btn_search.png differ diff --git a/android/res/drawable-mdpi/ic_media_next.png b/android/res/drawable-mdpi/ic_media_next.png new file mode 100644 index 00000000..84f38e8f Binary files /dev/null and b/android/res/drawable-mdpi/ic_media_next.png differ diff --git a/android/res/drawable-mdpi/ic_media_previous.png b/android/res/drawable-mdpi/ic_media_previous.png new file mode 100644 index 00000000..1bba5441 Binary files /dev/null and b/android/res/drawable-mdpi/ic_media_previous.png differ diff --git a/android/res/drawable/busy.xml b/android/res/drawable/busy.xml new file mode 100644 index 00000000..f0bc5612 --- /dev/null +++ b/android/res/drawable/busy.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/android/res/drawable/page_num.xml b/android/res/drawable/page_num.xml new file mode 100644 index 00000000..8090fb45 --- /dev/null +++ b/android/res/drawable/page_num.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/android/res/drawable/slider.xml b/android/res/drawable/slider.xml new file mode 100644 index 00000000..dfd65694 --- /dev/null +++ b/android/res/drawable/slider.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/android/res/drawable/tile09.jpg b/android/res/drawable/tile09.jpg new file mode 100644 index 00000000..3fcd274e Binary files /dev/null and b/android/res/drawable/tile09.jpg differ diff --git a/android/res/drawable/tiled_background.xml b/android/res/drawable/tiled_background.xml new file mode 100644 index 00000000..8c5f64c5 --- /dev/null +++ b/android/res/drawable/tiled_background.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/android/res/layout/buttons.xml b/android/res/layout/buttons.xml new file mode 100644 index 00000000..cb5b4075 --- /dev/null +++ b/android/res/layout/buttons.xml @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + +