Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/platform/java/jni/document.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/platform/java/jni/document.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,1126 @@ +// Copyright (C) 2004-2024 Artifex Software, Inc. +// +// This file is part of MuPDF. +// +// MuPDF is free software: you can redistribute it and/or modify it under the +// terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License +// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> +// +// Alternative licensing terms are available from the licensor. +// For commercial licensing, see <https://www.artifex.com/> or contact +// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, +// CA 94129, USA, for further information. + +/* Document interface */ + +/* Callbacks to implement fz_stream and fz_output using Java classes */ + +typedef struct +{ + jobject stream; + jbyteArray array; + jbyte buffer[8192]; +} +SeekableStreamState; + +static int SeekableInputStream_next(fz_context *ctx, fz_stream *stm, size_t max) +{ + SeekableStreamState *state = stm->state; + jboolean detach = JNI_FALSE; + JNIEnv *env; + int n, ch; + + env = jni_attach_thread(&detach); + if (env == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot attach to JVM in SeekableInputStream_next"); + + n = (*env)->CallIntMethod(env, state->stream, mid_SeekableInputStream_read, state->array); + if ((*env)->ExceptionCheck(env)) + fz_throw_java_and_detach_thread(ctx, env, detach); + + if (n > 0) + { + (*env)->GetByteArrayRegion(env, state->array, 0, n, state->buffer); + if ((*env)->ExceptionCheck(env)) + fz_throw_java_and_detach_thread(ctx, env, detach); + + /* update stm->pos so fz_tell knows the current position */ + stm->rp = (unsigned char *)state->buffer; + stm->wp = stm->rp + n; + stm->pos += n; + + ch = *stm->rp++; + } + else if (n < 0) + { + ch = EOF; + } + else + fz_throw_and_detach_thread(ctx, detach, FZ_ERROR_GENERIC, "no bytes read"); + + jni_detach_thread(detach); + return ch; +} + +static void SeekableInputStream_seek(fz_context *ctx, fz_stream *stm, int64_t offset, int whence) +{ + SeekableStreamState *state = stm->state; + jboolean detach = JNI_FALSE; + JNIEnv *env; + int64_t pos; + + env = jni_attach_thread(&detach); + if (env == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot attach to JVM in SeekableInputStream_seek"); + + pos = (*env)->CallLongMethod(env, state->stream, mid_SeekableStream_seek, offset, whence); + if ((*env)->ExceptionCheck(env)) + fz_throw_java_and_detach_thread(ctx, env, detach); + + stm->pos = pos; + stm->rp = stm->wp = (unsigned char *)state->buffer; + + jni_detach_thread(detach); +} + +static void SeekableInputStream_drop(fz_context *ctx, void *streamState_) +{ + SeekableStreamState *state = streamState_; + jboolean detach = JNI_FALSE; + JNIEnv *env; + + env = jni_attach_thread(&detach); + if (env == NULL) + { + fz_warn(ctx, "cannot attach to JVM in SeekableInputStream_drop; leaking input stream"); + return; + } + + (*env)->DeleteGlobalRef(env, state->stream); + (*env)->DeleteGlobalRef(env, state->array); + + fz_free(ctx, state); + + jni_detach_thread(detach); +} + +static void SeekableOutputStream_write(fz_context *ctx, void *streamState_, const void *buffer_, size_t count) +{ + SeekableStreamState *state = streamState_; + const jbyte *buffer = buffer_; + jboolean detach = JNI_FALSE; + JNIEnv *env; + + env = jni_attach_thread(&detach); + if (env == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot attach to JVM in SeekableOutputStream_write"); + + while (count > 0) + { + size_t n = fz_minz(count, sizeof(state->buffer)); + + (*env)->SetByteArrayRegion(env, state->array, 0, n, buffer); + if ((*env)->ExceptionCheck(env)) + fz_throw_java_and_detach_thread(ctx, env, detach); + + buffer += n; + count -= n; + + (*env)->CallVoidMethod(env, state->stream, mid_SeekableOutputStream_write, state->array, 0, n); + if ((*env)->ExceptionCheck(env)) + fz_throw_java_and_detach_thread(ctx, env, detach); + } + + jni_detach_thread(detach); +} + +static int64_t SeekableOutputStream_tell(fz_context *ctx, void *streamState_) +{ + SeekableStreamState *state = streamState_; + jboolean detach = JNI_FALSE; + int64_t pos = 0; + JNIEnv *env; + + env = jni_attach_thread(&detach); + if (env == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot attach to JVM in SeekableOutputStream_tell"); + + pos = (*env)->CallLongMethod(env, state->stream, mid_SeekableStream_position); + if ((*env)->ExceptionCheck(env)) + fz_throw_java_and_detach_thread(ctx, env, detach); + + jni_detach_thread(detach); + + return pos; +} + +static void SeekableOutputStream_truncate(fz_context *ctx, void *streamState_) +{ + SeekableStreamState *state = streamState_; + jboolean detach = JNI_FALSE; + JNIEnv *env; + + env = jni_attach_thread(&detach); + if (env == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot attach to JVM in SeekableOutputStream_truncate"); + + (*env)->CallVoidMethod(env, state->stream, mid_SeekableOutputStream_truncate); + if ((*env)->ExceptionCheck(env)) + fz_throw_java_and_detach_thread(ctx, env, detach); + + jni_detach_thread(detach); +} + +static void SeekableOutputStream_seek(fz_context *ctx, void *streamState_, int64_t offset, int whence) +{ + SeekableStreamState *state = streamState_; + jboolean detach = JNI_FALSE; + JNIEnv *env; + + env = jni_attach_thread(&detach); + if (env == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot attach to JVM in SeekableOutputStream_seek"); + + (void) (*env)->CallLongMethod(env, state->stream, mid_SeekableStream_seek, offset, whence); + if ((*env)->ExceptionCheck(env)) + fz_throw_java_and_detach_thread(ctx, env, detach); + + jni_detach_thread(detach); +} + +static void SeekableOutputStream_drop(fz_context *ctx, void *streamState_) +{ + SeekableStreamState *state = streamState_; + jboolean detach = JNI_FALSE; + JNIEnv *env; + + env = jni_attach_thread(&detach); + if (env == NULL) + { + fz_warn(ctx, "cannot attach to JVM in SeekableOutputStream_drop; leaking output stream"); + return; + } + + (*env)->DeleteGlobalRef(env, state->stream); + (*env)->DeleteGlobalRef(env, state->array); + + fz_free(ctx, state); + + jni_detach_thread(detach); +} +JNIEXPORT void JNICALL +FUN(Document_finalize)(JNIEnv *env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document_safe(env, self); + if (!ctx || !doc) return; + (*env)->SetLongField(env, self, fid_Document_pointer, 0); + fz_drop_document(ctx, doc); + + /* This is a reasonable place to call Memento. */ + Memento_fin(); +} + +JNIEXPORT jobject JNICALL +FUN(Document_openNativeWithStream)(JNIEnv *env, jclass cls, jstring jmagic, jobject jdocument, jobject jaccelerator) +{ + fz_context *ctx = get_context(env); + fz_document *doc = NULL; + fz_stream *docstream = NULL; + fz_stream *accstream = NULL; + jobject jdoc = NULL; + jobject jacc = NULL; + jbyteArray docarray = NULL; + jbyteArray accarray = NULL; + SeekableStreamState *docstate = NULL; + SeekableStreamState *accstate = NULL; + const char *magic = NULL; + + fz_var(jdoc); + fz_var(jacc); + fz_var(docarray); + fz_var(accarray); + fz_var(docstream); + fz_var(accstream); + + if (!ctx) return NULL; + if (jmagic) + { + magic = (*env)->GetStringUTFChars(env, jmagic, NULL); + if (!magic) jni_throw_run(env, "cannot get characters in magic string"); + } + if (jdocument) + { + jdoc = (*env)->NewGlobalRef(env, jdocument); + if (!jdoc) + { + if (magic) (*env)->ReleaseStringUTFChars(env, jmagic, magic); + jni_throw_run(env, "cannot get reference to document stream"); + } + } + if (jaccelerator) + { + jacc = (*env)->NewGlobalRef(env, jaccelerator); + if (!jacc) + { + (*env)->DeleteGlobalRef(env, jdoc); + if (magic) (*env)->ReleaseStringUTFChars(env, jmagic, magic); + jni_throw_run(env, "cannot get reference to accelerator stream"); + } + } + + docarray = (*env)->NewByteArray(env, sizeof docstate->buffer); + if (docarray) + docarray = (*env)->NewGlobalRef(env, docarray); + if (!docarray) + { + (*env)->DeleteGlobalRef(env, jacc); + (*env)->DeleteGlobalRef(env, jdoc); + if (magic) (*env)->ReleaseStringUTFChars(env, jmagic, magic); + jni_throw_run(env, "cannot create internal buffer for document stream"); + } + + if (jacc) + { + accarray = (*env)->NewByteArray(env, sizeof accstate->buffer); + if (accarray) + accarray = (*env)->NewGlobalRef(env, accarray); + if (!accarray) + { + (*env)->DeleteGlobalRef(env, docarray); + (*env)->DeleteGlobalRef(env, jacc); + (*env)->DeleteGlobalRef(env, jdoc); + if (magic) (*env)->ReleaseStringUTFChars(env, jmagic, magic); + jni_throw_run(env, "cannot create internal buffer for accelerator stream"); + } + } + + fz_try(ctx) + { + if (jdoc) + { + /* No exceptions can occur from here to stream owning docstate, so we must not free docstate. */ + docstate = Memento_label(fz_malloc(ctx, sizeof(SeekableStreamState)), "SeekableStreamState_docstate"); + docstate->stream = jdoc; + docstate->array = docarray; + + /* Ownership transferred to docstate. */ + jdoc = NULL; + docarray = NULL; + + /* Stream takes ownership of docstate. */ + docstream = fz_new_stream(ctx, docstate, SeekableInputStream_next, SeekableInputStream_drop); + docstream->seek = SeekableInputStream_seek; + } + + if (jacc) + { + /* No exceptions can occur from here to stream owning accstate, so we must not free accstate. */ + accstate = Memento_label(fz_malloc(ctx, sizeof(SeekableStreamState)), "SeekableStreamState_accstate"); + accstate->stream = jacc; + accstate->array = accarray; + + /* Ownership transferred to accstate. */ + jacc = NULL; + accarray = NULL; + + /* Stream takes ownership of accstate. */ + accstream = fz_new_stream(ctx, accstate, SeekableInputStream_next, SeekableInputStream_drop); + accstream->seek = SeekableInputStream_seek; + } + + doc = fz_open_accelerated_document_with_stream(ctx, magic, docstream, accstream); + } + fz_always(ctx) + { + fz_drop_stream(ctx, accstream); + fz_drop_stream(ctx, docstream); + if (magic) + (*env)->ReleaseStringUTFChars(env, jmagic, magic); + } + fz_catch(ctx) + { + (*env)->DeleteGlobalRef(env, accarray); + (*env)->DeleteGlobalRef(env, docarray); + (*env)->DeleteGlobalRef(env, jacc); + (*env)->DeleteGlobalRef(env, jdoc); + jni_rethrow(env, ctx); + } + + return to_Document_safe_own(ctx, env, doc); +} + +JNIEXPORT jobject JNICALL +FUN(Document_openNativeWithPath)(JNIEnv *env, jclass cls, jstring jfilename, jstring jaccelerator) +{ + fz_context *ctx = get_context(env); + fz_document *doc = NULL; + const char *filename = NULL; + const char *accelerator = NULL; + + if (!ctx) return NULL; + if (jfilename) + { + filename = (*env)->GetStringUTFChars(env, jfilename, NULL); + if (!filename) jni_throw_run(env, "cannot get characters in filename string"); + } + if (jaccelerator) + { + accelerator = (*env)->GetStringUTFChars(env, jaccelerator, NULL); + if (!accelerator) jni_throw_run(env, "cannot get characters in accelerator filename string"); + } + + fz_try(ctx) + doc = fz_open_accelerated_document(ctx, filename, accelerator); + fz_always(ctx) + { + if (accelerator) + (*env)->ReleaseStringUTFChars(env, jaccelerator, accelerator); + if (filename) + (*env)->ReleaseStringUTFChars(env, jfilename, filename); + } + fz_catch(ctx) + jni_rethrow(env, ctx); + + return to_Document_safe_own(ctx, env, doc); +} + + +JNIEXPORT jobject JNICALL +FUN(Document_openNativeWithPathAndStream)(JNIEnv *env, jclass cls, jstring jfilename, jobject jaccelerator) +{ + fz_context *ctx = get_context(env); + fz_document *doc = NULL; + const char *filename = NULL; + fz_stream *docstream = NULL; + fz_stream *accstream = NULL; + jobject jacc = NULL; + jbyteArray accarray = NULL; + SeekableStreamState *accstate = NULL; + + fz_var(jacc); + fz_var(accarray); + fz_var(accstream); + fz_var(docstream); + + if (!ctx) return NULL; + if (jfilename) + { + filename = (*env)->GetStringUTFChars(env, jfilename, NULL); + if (!filename) jni_throw_run(env, "cannot get characters in filename string"); + } + if (jaccelerator) + { + jacc = (*env)->NewGlobalRef(env, jaccelerator); + if (!jacc) + { + if (jfilename) (*env)->ReleaseStringUTFChars(env, jfilename, filename); + jni_throw_run(env, "cannot get reference to accelerator stream"); + } + } + + accarray = (*env)->NewByteArray(env, sizeof accstate->buffer); + if (accarray) + accarray = (*env)->NewGlobalRef(env, accarray); + if (!accarray) + { + (*env)->DeleteGlobalRef(env, jacc); + if (jfilename) (*env)->ReleaseStringUTFChars(env, jfilename, filename); + jni_throw_run(env, "cannot get create internal buffer for accelerator stream"); + } + + fz_try(ctx) + { + if (filename) + docstream = fz_open_file(ctx, filename); + + if (jacc) + { + /* No exceptions can occur from here to stream owning accstate, so we must not free accstate. */ + accstate = Memento_label(fz_malloc(ctx, sizeof(SeekableStreamState)), "SeekableStreamState_accstate2"); + accstate->stream = jacc; + accstate->array = accarray; + + /* Ownership transferred to accstate. */ + jacc = NULL; + accarray = NULL; + + /* Stream takes ownership of accstate. */ + accstream = fz_new_stream(ctx, accstate, SeekableInputStream_next, SeekableInputStream_drop); + accstream->seek = SeekableInputStream_seek; + } + + doc = fz_open_accelerated_document_with_stream(ctx, filename, docstream, accstream); + } + fz_always(ctx) + { + fz_drop_stream(ctx, accstream); + fz_drop_stream(ctx, docstream); + if (filename) (*env)->ReleaseStringUTFChars(env, jfilename, filename); + } + fz_catch(ctx) + { + (*env)->DeleteGlobalRef(env, accarray); + (*env)->DeleteGlobalRef(env, jacc); + jni_rethrow(env, ctx); + } + + return to_Document_safe_own(ctx, env, doc); +} + +JNIEXPORT jobject JNICALL +FUN(Document_openNativeWithBuffer)(JNIEnv *env, jclass cls, jstring jmagic, jobject jbuffer, jobject jaccelerator) +{ + fz_context *ctx = get_context(env); + fz_document *doc = NULL; + const char *magic = NULL; + fz_stream *docstream = NULL; + fz_stream *accstream = NULL; + fz_buffer *docbuf = NULL; + fz_buffer *accbuf = NULL; + jbyte *buffer = NULL; + jbyte *accelerator = NULL; + int n, m; + + fz_var(docbuf); + fz_var(accbuf); + fz_var(docstream); + fz_var(accstream); + + if (!ctx) return NULL; + if (jmagic) + { + magic = (*env)->GetStringUTFChars(env, jmagic, NULL); + if (!magic) + jni_throw_run(env, "cannot get characters in magic string"); + } + if (jbuffer) + { + n = (*env)->GetArrayLength(env, jbuffer); + + buffer = (*env)->GetByteArrayElements(env, jbuffer, NULL); + if (!buffer) + { + if (magic) (*env)->ReleaseStringUTFChars(env, jmagic, magic); + jni_throw_run(env, "cannot get document bytes to read"); + } + } + if (jaccelerator) + { + m = (*env)->GetArrayLength(env, jaccelerator); + + accelerator = (*env)->GetByteArrayElements(env, jaccelerator, NULL); + if (!accelerator) + { + if (buffer) (*env)->ReleaseByteArrayElements(env, jbuffer, buffer, 0); + if (magic) (*env)->ReleaseStringUTFChars(env, jmagic, magic); + jni_throw_run(env, "cannot get accelerator bytes to read"); + } + } + + fz_try(ctx) + { + if (buffer) + { + docbuf = fz_new_buffer(ctx, n); + fz_append_data(ctx, docbuf, buffer, n); + docstream = fz_open_buffer(ctx, docbuf); + } + + if (accelerator) + { + accbuf = fz_new_buffer(ctx, m); + fz_append_data(ctx, accbuf, accelerator, m); + accstream = fz_open_buffer(ctx, accbuf); + } + + doc = fz_open_accelerated_document_with_stream(ctx, magic, docstream, accstream); + } + fz_always(ctx) + { + fz_drop_stream(ctx, accstream); + fz_drop_buffer(ctx, accbuf); + fz_drop_stream(ctx, docstream); + fz_drop_buffer(ctx, docbuf); + if (accelerator) (*env)->ReleaseByteArrayElements(env, jaccelerator, accelerator, 0); + if (buffer) (*env)->ReleaseByteArrayElements(env, jbuffer, buffer, 0); + if (magic) (*env)->ReleaseStringUTFChars(env, jmagic, magic); + } + fz_catch(ctx) + { + jni_rethrow(env, ctx); + } + + return to_Document_safe_own(ctx, env, doc); +} + +JNIEXPORT jboolean JNICALL +FUN(Document_recognize)(JNIEnv *env, jclass cls, jstring jmagic) +{ + fz_context *ctx = get_context(env); + const char *magic = NULL; + jboolean recognized = JNI_FALSE; + + if (!ctx) return JNI_FALSE; + if (jmagic) + { + magic = (*env)->GetStringUTFChars(env, jmagic, NULL); + if (!magic) return JNI_FALSE; + } + + fz_try(ctx) + recognized = fz_recognize_document(ctx, magic) != NULL; + fz_always(ctx) + if (magic) (*env)->ReleaseStringUTFChars(env, jmagic, magic); + fz_catch(ctx) + jni_rethrow(env, ctx); + + return recognized; +} + +JNIEXPORT jboolean JNICALL +FUN(Document_supportsAccelerator)(JNIEnv *env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + jboolean support = JNI_FALSE; + + fz_try(ctx) + support = fz_document_supports_accelerator(ctx, doc); + fz_catch(ctx) + jni_rethrow(env, ctx); + + return support; +} + +JNIEXPORT void JNICALL +FUN(Document_saveAccelerator)(JNIEnv *env, jobject self, jstring jfilename) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + const char *filename = "null"; + + if (!ctx || !doc) return; + if (!jfilename) jni_throw_arg_void(env, "filename must not be null"); + + filename = (*env)->GetStringUTFChars(env, jfilename, NULL); + if (!filename) return; + + fz_try(ctx) + fz_save_accelerator(ctx, doc, filename); + fz_always(ctx) + (*env)->ReleaseStringUTFChars(env, jfilename, filename); + fz_catch(ctx) + jni_rethrow_void(env, ctx); +} + +JNIEXPORT void JNICALL +FUN(Document_outputAccelerator)(JNIEnv *env, jobject self, jobject jstream) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + SeekableStreamState *state = NULL; + jobject stream = NULL; + jbyteArray array = NULL; + fz_output *out; + + fz_var(state); + fz_var(out); + fz_var(stream); + fz_var(array); + + stream = (*env)->NewGlobalRef(env, jstream); + if (!stream) + return; + + array = (*env)->NewByteArray(env, sizeof state->buffer); + if (array) + array = (*env)->NewGlobalRef(env, array); + if (!array) + { + (*env)->DeleteGlobalRef(env, stream); + return; + } + + fz_try(ctx) + { + state = Memento_label(fz_malloc(ctx, sizeof(SeekableStreamState)), "SeekableStreamState_outputAccelerator"); + state->stream = stream; + state->array = array; + + out = fz_new_output(ctx, 8192, state, SeekableOutputStream_write, NULL, SeekableOutputStream_drop); + out->seek = SeekableOutputStream_seek; + out->tell = SeekableOutputStream_tell; + + /* these are now owned by 'out' */ + state = NULL; + stream = NULL; + array = NULL; + + fz_output_accelerator(ctx, doc, out); + fz_close_output(ctx, out); + } + fz_always(ctx) + { + fz_drop_output(ctx, out); + } + fz_catch(ctx) + { + (*env)->DeleteGlobalRef(env, stream); + (*env)->DeleteGlobalRef(env, array); + fz_free(ctx, state); + jni_rethrow_void(env, ctx); + } +} + +JNIEXPORT jboolean JNICALL +FUN(Document_needsPassword)(JNIEnv *env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + int okay = 0; + + if (!ctx || !doc) return JNI_FALSE; + + fz_try(ctx) + okay = fz_needs_password(ctx, doc); + fz_catch(ctx) + jni_rethrow(env, ctx); + + return okay ? JNI_TRUE : JNI_FALSE; +} + +JNIEXPORT jboolean JNICALL +FUN(Document_authenticatePassword)(JNIEnv *env, jobject self, jstring jpassword) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + const char *password = NULL; + int okay = 0; + + if (!ctx || !doc) return JNI_FALSE; + + if (jpassword) + { + password = (*env)->GetStringUTFChars(env, jpassword, NULL); + if (!password) return JNI_FALSE; + } + + fz_try(ctx) + okay = fz_authenticate_password(ctx, doc, password); + fz_always(ctx) + if (password) (*env)->ReleaseStringUTFChars(env, jpassword, password); + fz_catch(ctx) + jni_rethrow(env, ctx); + + return okay ? JNI_TRUE : JNI_FALSE; +} + +JNIEXPORT jint JNICALL +FUN(Document_countChapters)(JNIEnv *env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + int count = 0; + + if (!ctx || !doc) return 0; + + fz_try(ctx) + count = fz_count_chapters(ctx, doc); + fz_catch(ctx) + jni_rethrow(env, ctx); + + return count; +} + +JNIEXPORT jint JNICALL +FUN(Document_countPages)(JNIEnv *env, jobject self, jint chapter) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + int count = 0; + + if (!ctx || !doc) return 0; + + fz_try(ctx) + count = fz_count_chapter_pages(ctx, doc, chapter); + fz_catch(ctx) + jni_rethrow(env, ctx); + + return count; +} + +JNIEXPORT jboolean JNICALL +FUN(Document_isReflowable)(JNIEnv *env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + int is_reflowable = 0; + + if (!ctx || !doc) return JNI_FALSE; + + fz_try(ctx) + is_reflowable = fz_is_document_reflowable(ctx, doc); + fz_catch(ctx) + jni_rethrow(env, ctx); + + return is_reflowable ? JNI_TRUE : JNI_FALSE; +} + +JNIEXPORT void JNICALL +FUN(Document_layout)(JNIEnv *env, jobject self, jfloat w, jfloat h, jfloat em) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + + if (!ctx || !doc) return; + + fz_try(ctx) + fz_layout_document(ctx, doc, w, h, em); + fz_catch(ctx) + jni_rethrow_void(env, ctx); +} + +JNIEXPORT jobject JNICALL +FUN(Document_loadPage)(JNIEnv *env, jobject self, jint chapter, jint number) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + fz_page *page = NULL; + + if (!ctx || !doc) return NULL; + + fz_try(ctx) + page = fz_load_chapter_page(ctx, doc, chapter, number); + fz_catch(ctx) + jni_rethrow(env, ctx); + + return to_Page_safe_own(ctx, env, page); +} + +JNIEXPORT jstring JNICALL +FUN(Document_getMetaData)(JNIEnv *env, jobject self, jstring jkey) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + const char *key = NULL; + char info[256]; + + if (!ctx || !doc) return NULL; + if (!jkey) jni_throw_arg(env, "key must not be null"); + + key = (*env)->GetStringUTFChars(env, jkey, NULL); + if (!key) return 0; + + fz_try(ctx) + fz_lookup_metadata(ctx, doc, key, info, sizeof info); + fz_always(ctx) + if (key) + (*env)->ReleaseStringUTFChars(env, jkey, key); + fz_catch(ctx) + jni_rethrow(env, ctx); + + return (*env)->NewStringUTF(env, info); +} + +JNIEXPORT void JNICALL +FUN(Document_setMetaData)(JNIEnv *env, jobject self, jstring jkey, jstring jvalue) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + const char *key = NULL; + const char *value = NULL; + + if (!ctx || !doc) return; + if (!jkey) jni_throw_arg_void(env, "key must not be null"); + if (!jvalue) jni_throw_arg_void(env, "value must not be null"); + + key = (*env)->GetStringUTFChars(env, jkey, NULL); + value = (*env)->GetStringUTFChars(env, jvalue, NULL); + if (!key || !value) + { + if (key) + (*env)->ReleaseStringUTFChars(env, jkey, key); + return; + } + + fz_try(ctx) + fz_set_metadata(ctx, doc, key, value); + fz_always(ctx) + { + (*env)->ReleaseStringUTFChars(env, jkey, key); + (*env)->ReleaseStringUTFChars(env, jvalue, value); + } + fz_catch(ctx) + jni_rethrow_void(env, ctx); +} + +JNIEXPORT jboolean JNICALL +FUN(Document_isUnencryptedPDF)(JNIEnv *env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + pdf_document *idoc = pdf_specifics(ctx, doc); + int cryptVer; + + if (!ctx || !doc) return JNI_FALSE; + if (!idoc) + return JNI_FALSE; + + cryptVer = pdf_crypt_version(ctx, idoc->crypt); + return (cryptVer == 0) ? JNI_TRUE : JNI_FALSE; +} + +JNIEXPORT jobject JNICALL +FUN(Document_loadOutline)(JNIEnv *env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + fz_outline *outline = NULL; + jobject joutline = NULL; + + if (!ctx || !doc) return NULL; + + fz_var(outline); + + fz_try(ctx) + { + outline = fz_load_outline(ctx, doc); + if (outline) + { + joutline = to_Outline_safe(ctx, env, doc, outline); + if (!joutline && !(*env)->ExceptionCheck(env)) + fz_throw(ctx, FZ_ERROR_GENERIC, "loadOutline failed"); + } + } + fz_always(ctx) + fz_drop_outline(ctx, outline); + fz_catch(ctx) + jni_rethrow(env, ctx); + + if ((*env)->ExceptionCheck(env)) + return NULL; + + return joutline; +} + +JNIEXPORT jobject JNICALL +FUN(Document_outlineIterator)(JNIEnv *env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + fz_outline_iterator *iterator = NULL; + jobject jiterator = NULL; + + if (!ctx || !doc) return NULL; + + fz_var(iterator); + + fz_try(ctx) + { + iterator = fz_new_outline_iterator(ctx, doc); + if (iterator) + { + jiterator = to_OutlineIterator_safe(ctx, env, iterator); + if (!jiterator || (*env)->ExceptionCheck(env)) + fz_throw(ctx, FZ_ERROR_GENERIC, "outlineIterator failed"); + iterator = NULL; + } + } + fz_always(ctx) + fz_drop_outline_iterator(ctx, iterator); + fz_catch(ctx) + jni_rethrow(env, ctx); + + if ((*env)->ExceptionCheck(env)) + return NULL; + + return jiterator; +} + +JNIEXPORT jlong JNICALL +FUN(Document_makeBookmark)(JNIEnv *env, jobject self, jint chapter, jint page) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + fz_bookmark mark = 0; + + fz_try(ctx) + mark = fz_make_bookmark(ctx, doc, fz_make_location(chapter, page)); + fz_catch(ctx) + jni_rethrow(env, ctx); + + return mark; +} + +JNIEXPORT jobject JNICALL +FUN(Document_findBookmark)(JNIEnv *env, jobject self, jlong mark) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + fz_location loc = { -1, -1 }; + + fz_try(ctx) + loc = fz_lookup_bookmark(ctx, doc, mark); + fz_catch(ctx) + jni_rethrow(env, ctx); + + return (*env)->NewObject(env, cls_Location, mid_Location_init, loc.chapter, loc.page, 0, 0); +} + +JNIEXPORT jobject JNICALL +FUN(Document_resolveLink)(JNIEnv *env, jobject self, jstring juri) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + fz_location loc = { -1, -1 }; + float x = 0, y = 0; + const char *uri = ""; + + if (juri) + { + uri = (*env)->GetStringUTFChars(env, juri, NULL); + if (!uri) + return NULL; + } + + fz_try(ctx) + loc = fz_resolve_link(ctx, doc, uri, &x, &y); + fz_always(ctx) + if (juri) + (*env)->ReleaseStringUTFChars(env, juri, uri); + fz_catch(ctx) + jni_rethrow(env, ctx); + + return (*env)->NewObject(env, cls_Location, mid_Location_init, loc.chapter, loc.page, x, y); +} + +JNIEXPORT jboolean JNICALL +FUN(Document_hasPermission)(JNIEnv *env, jobject self, jint permission) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + jboolean result = JNI_FALSE; + + fz_try(ctx) + result = fz_has_permission(ctx, doc, permission); + fz_catch(ctx) + jni_rethrow(env, ctx); + + return result; +} + +JNIEXPORT jobject JNICALL +FUN(Document_search)(JNIEnv *env, jobject self, jint chapter, jint page, jstring jneedle) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + const char *needle = NULL; + search_state state = { env, NULL, 0 }; + + if (!ctx || !doc) return NULL; + if (!jneedle) jni_throw_arg(env, "needle must not be null"); + + needle = (*env)->GetStringUTFChars(env, jneedle, NULL); + if (!needle) return NULL; + + state.hits = (*env)->NewObject(env, cls_ArrayList, mid_ArrayList_init); + if (!state.hits || (*env)->ExceptionCheck(env)) return NULL; + + fz_try(ctx) + fz_search_chapter_page_number_cb(ctx, doc, chapter, page, needle, hit_callback, &state); + fz_always(ctx) + { + (*env)->ReleaseStringUTFChars(env, jneedle, needle); + } + fz_catch(ctx) + jni_rethrow(env, ctx); + + if (state.error) + return NULL; + + return (*env)->CallObjectMethod(env, state.hits, mid_ArrayList_toArray); +} + +JNIEXPORT jobject JNICALL +FUN(Document_resolveLinkDestination)(JNIEnv *env, jobject self, jstring juri) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + const char *uri = ""; + fz_link_dest dest; + jobject jdestination; + + if (!ctx || !doc) return NULL; + + if (juri) + { + uri = (*env)->GetStringUTFChars(env, juri, NULL); + if (!uri) + return NULL; + } + + fz_try(ctx) + dest = fz_resolve_link_dest(ctx, doc, uri); + fz_always(ctx) + if (juri) + (*env)->ReleaseStringUTFChars(env, juri, uri); + fz_catch(ctx) + jni_rethrow(env, ctx); + + jdestination = (*env)->NewObject(env, cls_LinkDestination, mid_LinkDestination_init, + dest.loc.chapter, dest.loc.page, dest.type, dest.x, dest.y, dest.w, dest.h, dest.zoom); + if (!jdestination || (*env)->ExceptionCheck(env)) + return NULL; + + return jdestination; +} + +JNIEXPORT jstring JNICALL +FUN(Document_formatLinkURI)(JNIEnv *env, jobject self, jobject jdest) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + fz_link_dest dest = from_LinkDestination(env, jdest); + char *uri = NULL; + jobject juri; + + fz_try(ctx) + uri = fz_format_link_uri(ctx, doc, dest); + fz_catch(ctx) + jni_rethrow(env, ctx); + + juri = (*env)->NewStringUTF(env, uri); + fz_free(ctx, uri); + if (juri == NULL || (*env)->ExceptionCheck(env)) + return NULL; + + return juri; +} + +JNIEXPORT jobject JNICALL +FUN(Document_asPDF)(JNIEnv *env, jobject self) +{ + fz_context *ctx = get_context(env); + fz_document *doc = from_Document(env, self); + pdf_document *pdf; + + fz_try(ctx) + pdf = fz_new_pdf_document_from_fz_document(ctx, doc); + fz_catch(ctx) + jni_rethrow(env, ctx); + + if (!pdf) + return NULL; + + return to_PDFDocument_safe_own(ctx, env, pdf); +}
