summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--platform/java/mupdf_native.c465
-rw-r--r--platform/java/mupdf_native.h66
-rw-r--r--platform/java/src/com/artifex/mupdf/fitz/Document.java5
-rw-r--r--platform/java/src/com/artifex/mupdf/fitz/FileStream.java43
-rw-r--r--platform/java/src/com/artifex/mupdf/fitz/PDFDocument.java6
-rw-r--r--platform/java/src/com/artifex/mupdf/fitz/SeekableInputStream.java9
-rw-r--r--platform/java/src/com/artifex/mupdf/fitz/SeekableOutputStream.java9
-rw-r--r--platform/java/src/com/artifex/mupdf/fitz/SeekableStream.java14
8 files changed, 599 insertions, 18 deletions
diff --git a/platform/java/mupdf_native.c b/platform/java/mupdf_native.c
index c61db6b7..915e6b58 100644
--- a/platform/java/mupdf_native.c
+++ b/platform/java/mupdf_native.c
@@ -49,6 +49,9 @@ static inline jlong jlong_cast(const void *p)
return (jlong)(intptr_t)p;
}
+/* Our vm */
+static JavaVM *jvm = NULL;
+
/* All the cached classes/mids/fids we need. */
static jclass cls_Annotation;
@@ -61,30 +64,33 @@ static jclass cls_Document;
static jclass cls_DocumentWriter;
static jclass cls_FloatArray;
static jclass cls_Font;
+static jclass cls_IOException;
static jclass cls_IllegalArgumentException;
static jclass cls_Image;
static jclass cls_IndexOutOfBoundsException;
static jclass cls_IntegerArray;
-static jclass cls_IOException;
static jclass cls_Link;
static jclass cls_Matrix;
static jclass cls_NativeDevice;
static jclass cls_NullPointerException;
static jclass cls_Object;
-static jclass cls_Outline;
static jclass cls_OutOfMemoryError;
-static jclass cls_Page;
-static jclass cls_Path;
-static jclass cls_PathWalker;
+static jclass cls_Outline;
static jclass cls_PDFAnnotation;
static jclass cls_PDFDocument;
-static jclass cls_PDFPage;
static jclass cls_PDFGraftMap;
static jclass cls_PDFObject;
+static jclass cls_PDFPage;
+static jclass cls_Page;
+static jclass cls_Path;
+static jclass cls_PathWalker;
static jclass cls_Pixmap;
static jclass cls_Point;
static jclass cls_Rect;
static jclass cls_RuntimeException;
+static jclass cls_SeekableInputStream;
+static jclass cls_SeekableOutputStream;
+static jclass cls_SeekableStream;
static jclass cls_Shade;
static jclass cls_StrokeState;
static jclass cls_StructuredText;
@@ -101,8 +107,8 @@ static jfieldID fid_ColorSpace_pointer;
static jfieldID fid_Cookie_pointer;
static jfieldID fid_Device_pointer;
static jfieldID fid_DisplayList_pointer;
-static jfieldID fid_Document_pointer;
static jfieldID fid_DocumentWriter_pointer;
+static jfieldID fid_Document_pointer;
static jfieldID fid_Font_pointer;
static jfieldID fid_Image_pointer;
static jfieldID fid_Link_bounds;
@@ -116,14 +122,14 @@ static jfieldID fid_Matrix_e;
static jfieldID fid_Matrix_f;
static jfieldID fid_NativeDevice_nativeInfo;
static jfieldID fid_NativeDevice_nativeResource;
-static jfieldID fid_Page_pointer;
-static jfieldID fid_Path_pointer;
static jfieldID fid_PDFAnnotation_pointer;
static jfieldID fid_PDFDocument_pointer;
-static jfieldID fid_PDFPage_pointer;
static jfieldID fid_PDFGraftMap_pointer;
-static jfieldID fid_PDFObject_pointer;
static jfieldID fid_PDFObject_Null;
+static jfieldID fid_PDFObject_pointer;
+static jfieldID fid_PDFPage_pointer;
+static jfieldID fid_Page_pointer;
+static jfieldID fid_Path_pointer;
static jfieldID fid_Pixmap_pointer;
static jfieldID fid_Point_x;
static jfieldID fid_Point_y;
@@ -174,28 +180,33 @@ static jmethodID mid_Link_init;
static jmethodID mid_Matrix_init;
static jmethodID mid_Object_toString;
static jmethodID mid_Outline_init;
+static jmethodID mid_PDFAnnotation_init;
+static jmethodID mid_PDFDocument_init;
+static jmethodID mid_PDFGraftMap_init;
+static jmethodID mid_PDFObject_init;
+static jmethodID mid_PDFPage_init;
static jmethodID mid_Page_init;
-static jmethodID mid_Path_init;
static jmethodID mid_PathWalker_closePath;
static jmethodID mid_PathWalker_curveTo;
static jmethodID mid_PathWalker_lineTo;
static jmethodID mid_PathWalker_moveTo;
-static jmethodID mid_PDFAnnotation_init;
-static jmethodID mid_PDFDocument_init;
-static jmethodID mid_PDFPage_init;
-static jmethodID mid_PDFGraftMap_init;
-static jmethodID mid_PDFObject_init;
+static jmethodID mid_Path_init;
static jmethodID mid_Pixmap_init;
static jmethodID mid_Point_init;
static jmethodID mid_Rect_init;
+static jmethodID mid_SeekableInputStream_read;
+static jmethodID mid_SeekableOutputStream_write;
+static jmethodID mid_SeekableStream_close;
+static jmethodID mid_SeekableStream_position;
+static jmethodID mid_SeekableStream_seek;
static jmethodID mid_Shade_init;
static jmethodID mid_StrokeState_init;
static jmethodID mid_StructuredText_init;
static jmethodID mid_TextBlock_init;
static jmethodID mid_TextChar_init;
-static jmethodID mid_Text_init;
static jmethodID mid_TextLine_init;
static jmethodID mid_TextWalker_showGlyph;
+static jmethodID mid_Text_init;
#ifdef _WIN32
static DWORD context_key;
@@ -548,6 +559,17 @@ static int find_fids(JNIEnv *env)
fid_Rect_y1 = get_field(&err, env, "y1", "F");
mid_Rect_init = get_method(&err, env, "<init>", "(FFFF)V");
+ cls_SeekableInputStream = get_class(&err, env, PKG"SeekableInputStream");
+ mid_SeekableInputStream_read = get_method(&err, env, "read", "([B)I");
+
+ cls_SeekableOutputStream = get_class(&err, env, PKG"SeekableOutputStream");
+ mid_SeekableOutputStream_write = get_method(&err, env, "write", "([BII)V");
+
+ cls_SeekableStream = get_class(&err, env, PKG"SeekableStream");
+ mid_SeekableStream_close = get_method(&err, env, "close", "()V");
+ mid_SeekableStream_position = get_method(&err, env, "position", "()J");
+ mid_SeekableStream_seek = get_method(&err, env, "seek", "(JI)J");
+
cls_Shade = get_class(&err, env, PKG"Shade");
fid_Shade_pointer = get_field(&err, env, "pointer", "J");
mid_Shade_init = get_method(&err, env, "<init>", "(J)V");
@@ -600,9 +622,49 @@ static int find_fids(JNIEnv *env)
cls_OutOfMemoryError = get_class(&err, env, "java/lang/OutOfMemoryError");
+ /* Get and store the main JVM pointer. We need this in order to get
+ * JNIEnv pointers on callback threads. This is specifically
+ * guaranteed to be safe to store in a static var. */
+
+ int getvmErr = (*env)->GetJavaVM(env, &jvm);
+ if (getvmErr < 0)
+ {
+ LOGE("mupdf_native.c find_fids() GetJavaVM failed with %d", getvmErr);
+ err = 1;
+ }
+
return err;
}
+/* When making callbacks from C to java, we may be called on threads
+ * other than the foreground. As such, we have no JNIEnv. This function
+ * handles getting us the required environment */
+static JNIEnv *jni_attach_thread(fz_context *ctx, int *detach)
+{
+ JNIEnv *env = NULL;
+ int state;
+
+ *detach = 0;
+ state = (*jvm)->GetEnv(jvm, (void*)&env, MY_JNI_VERSION);
+ if (state == JNI_EDETACHED)
+ {
+ *detach = 1;
+ state = (*jvm)->AttachCurrentThread(jvm, (void*)&env, NULL);
+ }
+
+ if (state != JNI_OK)
+ return NULL;
+
+ return env;
+}
+
+static void jni_detach_thread(int detach)
+{
+ if (!detach)
+ return;
+ (*jvm)->DetachCurrentThread(jvm);
+}
+
static void lose_fids(JNIEnv *env)
{
(*env)->DeleteGlobalRef(env, cls_Annotation);
@@ -639,6 +701,9 @@ static void lose_fids(JNIEnv *env)
(*env)->DeleteGlobalRef(env, cls_Point);
(*env)->DeleteGlobalRef(env, cls_Rect);
(*env)->DeleteGlobalRef(env, cls_RuntimeException);
+ (*env)->DeleteGlobalRef(env, cls_SeekableStream);
+ (*env)->DeleteGlobalRef(env, cls_SeekableInputStream);
+ (*env)->DeleteGlobalRef(env, cls_SeekableOutputStream);
(*env)->DeleteGlobalRef(env, cls_Shade);
(*env)->DeleteGlobalRef(env, cls_StrokeState);
(*env)->DeleteGlobalRef(env, cls_StructuredText);
@@ -1865,6 +1930,214 @@ static inline fz_text *from_Text_safe(JNIEnv *env, jobject jobj)
return CAST(fz_text *, (*env)->GetLongField(env, jobj, fid_Text_pointer));
}
+/* Callbacks to implement fz_stream and fz_output using Java classes */
+
+typedef struct
+{
+ jobject stream;
+ jbyteArray array;
+ jbyte buffer[4096];
+}
+SeekableStreamState;
+
+static int call_SeekableInputStream_next(fz_context *ctx, fz_stream *stm, size_t max)
+{
+ SeekableStreamState *state = stm->state;
+ JNIEnv *env;
+ int detach;
+ int n, ch;
+
+ env = jni_attach_thread(ctx, &detach);
+ if (env == NULL)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot attach to JVM in call_SeekableInputStream_next");
+
+ n = (*env)->CallIntMethod(env, state->stream, mid_SeekableInputStream_read, state->array);
+ if ((*env)->ExceptionCheck(env)) {
+ jni_detach_thread(detach);
+ fz_throw_java(ctx, env);
+ }
+
+ if (n > 0)
+ {
+ (*env)->GetByteArrayRegion(env, state->array, 0, n, state->buffer);
+
+ /* 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
+ {
+ jni_detach_thread(detach);
+ fz_throw(ctx, FZ_ERROR_GENERIC, "no bytes read");
+ }
+
+ jni_detach_thread(detach);
+ return ch;
+}
+
+static void call_SeekableOutputStream_write(fz_context *ctx, void *streamState_, const void *buffer_, size_t count)
+{
+ SeekableStreamState *state = streamState_;
+ const jbyte *buffer = buffer_;
+ JNIEnv *env;
+ int detach;
+
+ env = jni_attach_thread(ctx, &detach);
+ if (env == NULL)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot attach to JVM in call_SeekableOutputStream_write");
+
+ while (count > 0)
+ {
+ size_t n = fz_minz(count, sizeof(state->buffer));
+
+ (*env)->SetByteArrayRegion(env, state->array, 0, n, buffer);
+ buffer += n;
+ count -= n;
+
+ (*env)->CallVoidMethod(env, state->stream, mid_SeekableOutputStream_write, state->array, 0, n);
+ if ((*env)->ExceptionCheck(env)) {
+ jni_detach_thread(detach);
+ fz_throw_java(ctx, env);
+ }
+ }
+
+ jni_detach_thread(detach);
+}
+
+static int64_t call_SeekableOutputStream_tell(fz_context *ctx, void *streamState_)
+{
+ SeekableStreamState *state = streamState_;
+ JNIEnv *env;
+ int detach;
+ int64_t pos = 0;
+
+ env = jni_attach_thread(ctx, &detach);
+ if (env == NULL)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot attach to JVM in call_SeekableOutputStream_tell");
+
+ pos = (*env)->CallLongMethod(env, state->stream, mid_SeekableStream_position);
+ if ((*env)->ExceptionCheck(env)) {
+ jni_detach_thread(detach);
+ fz_throw_java(ctx, env);
+ }
+
+ jni_detach_thread(detach);
+
+ return pos;
+}
+
+static void call_SeekableInputStream_seek(fz_context *ctx, fz_stream *stm, int64_t offset, int whence)
+{
+ SeekableStreamState *state = stm->state;
+ JNIEnv *env;
+ int detach;
+ int64_t pos;
+
+ env = jni_attach_thread(ctx, &detach);
+ if (env == NULL)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot attach to JVM in call_SeekableInputStream_seek");
+
+ pos = (*env)->CallLongMethod(env, state->stream, mid_SeekableStream_seek, offset, whence);
+ if ((*env)->ExceptionCheck(env)) {
+ jni_detach_thread(detach);
+ fz_throw_java(ctx, env);
+ }
+
+ stm->pos = pos;
+ stm->rp = stm->wp = (unsigned char *)state->buffer;
+
+ jni_detach_thread(detach);
+}
+
+static void call_SeekableOutputStream_seek(fz_context *ctx, void *streamState_, int64_t offset, int whence)
+{
+ SeekableStreamState *state = streamState_;
+ JNIEnv *env;
+ int detach;
+
+ env = jni_attach_thread(ctx, &detach);
+ if (env == NULL)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot attach to JVM in call_SeekableOutputStream_seek");
+
+ (void) (*env)->CallLongMethod(env, state->stream, mid_SeekableStream_seek, offset, whence);
+ if ((*env)->ExceptionCheck(env)) {
+ jni_detach_thread(detach);
+ fz_throw_java(ctx, env);
+ }
+
+ jni_detach_thread(detach);
+}
+
+static void call_SeekableInputStream_drop(fz_context *ctx, void *streamState_)
+{
+ SeekableStreamState *state = streamState_;
+ JNIEnv *env;
+ int detach;
+
+ env = jni_attach_thread(ctx, &detach);
+ if (env == NULL) {
+ fz_warn(ctx, "cannot attach to JVM in call_SeekableInputStream_drop; leaking input stream");
+ return;
+ }
+
+ (*env)->CallVoidMethod(env, state->stream, mid_SeekableStream_close);
+ if ((*env)->ExceptionCheck(env))
+ fz_warn(ctx, "ignoring java exception in call_SeekableInputStream_drop");
+
+ (*env)->DeleteGlobalRef(env, state->stream);
+ (*env)->DeleteGlobalRef(env, state->array);
+
+ fz_free(ctx, state);
+
+ jni_detach_thread(detach);
+}
+
+static void call_SeekableOutputStream_close(fz_context *ctx, void *streamState_)
+{
+ SeekableStreamState *state = streamState_;
+ JNIEnv *env;
+ int detach;
+
+ env = jni_attach_thread(ctx, &detach);
+ if (env == NULL)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot attach to JVM in call_SeekableOutputStream_close");
+
+ (*env)->CallVoidMethod(env, state->stream, mid_SeekableStream_close);
+ if ((*env)->ExceptionCheck(env)) {
+ jni_detach_thread(detach);
+ fz_throw_java(ctx, env);
+ }
+
+ jni_detach_thread(detach);
+}
+
+static void call_SeekableOutputStream_drop(fz_context *ctx, void *streamState_)
+{
+ SeekableStreamState *state = streamState_;
+ JNIEnv *env;
+ int detach;
+
+ env = jni_attach_thread(ctx, &detach);
+ if (env == NULL) {
+ fz_warn(ctx, "cannot attach to JVM in call_SeekableOutputStream_drop; leaking output stream");
+ return;
+ }
+
+ (*env)->DeleteGlobalRef(env, state->stream);
+ (*env)->DeleteGlobalRef(env, state->array);
+
+ fz_free(ctx, state);
+
+ jni_detach_thread(detach);
+}
+
/*
Devices can either be implemented in C, or in Java.
We therefore have to think about 4 possible call combinations.
@@ -4405,6 +4678,82 @@ FUN(Document_finalize)(JNIEnv *env, jobject self)
}
JNIEXPORT jobject JNICALL
+FUN(Document_openNativeWithStream)(JNIEnv *env, jclass cls, jobject jstream, jstring jmimetype)
+{
+ fz_context *ctx = get_context(env);
+ fz_document *doc = NULL;
+ fz_stream *stm = NULL;
+ jobject stream = NULL;
+ jbyteArray array = NULL;
+ SeekableStreamState *state = NULL;
+ const char *mimetype = NULL;
+
+ fz_var(state);
+ fz_var(stm);
+
+ if (jmimetype)
+ {
+ mimetype = (*env)->GetStringUTFChars(env, jmimetype, NULL);
+ if (!mimetype)
+ return NULL;
+ }
+
+ stream = (*env)->NewGlobalRef(env, jstream);
+ if (!stream)
+ {
+ if (mimetype)
+ (*env)->ReleaseStringUTFChars(env, jmimetype, mimetype);
+ return NULL;
+ }
+
+ array = (*env)->NewByteArray(env, sizeof state->buffer);
+ if (array)
+ array = (*env)->NewGlobalRef(env, array);
+ if (!array)
+ {
+ if (mimetype)
+ (*env)->ReleaseStringUTFChars(env, jmimetype, mimetype);
+ (*env)->DeleteGlobalRef(env, stream);
+ return NULL;
+ }
+
+ fz_try(ctx)
+ {
+ state = fz_malloc(ctx, sizeof(SeekableStreamState));
+ state->stream = stream;
+ state->array = array;
+
+ /* create a stream and open the doc using it */
+ stm = fz_new_stream(ctx, state, call_SeekableInputStream_next, call_SeekableInputStream_drop);
+ stm->state = state;
+ stm->seek = call_SeekableInputStream_seek;
+
+ /* these are now owned by 'stm' */
+ state = NULL;
+ stream = NULL;
+ array = NULL;
+
+ doc = fz_open_document_with_stream(ctx, mimetype, stm);
+ }
+ fz_always(ctx)
+ {
+ if (mimetype)
+ (*env)->ReleaseStringUTFChars(env, jmimetype, mimetype);
+ fz_drop_stream(ctx, stm);
+ }
+ fz_catch(ctx)
+ {
+ if (stream) (*env)->DeleteGlobalRef(env, stream);
+ if (array) (*env)->DeleteGlobalRef(env, array);
+ fz_free(ctx, state);
+ jni_rethrow(env, ctx);
+ return NULL;
+ }
+
+ return to_Document_safe_own(ctx, env, doc);
+}
+
+JNIEXPORT jobject JNICALL
FUN(Document_openNativeWithPath)(JNIEnv *env, jclass cls, jstring jfilename)
{
fz_context *ctx = get_context(env);
@@ -6704,6 +7053,86 @@ FUN(PDFDocument_canBeSavedIncrementally)(JNIEnv *env, jobject self)
}
JNIEXPORT jint JNICALL
+FUN(PDFDocument_nativeSaveWithStream)(JNIEnv *env, jobject self, jobject jstream, jstring joptions)
+{
+ fz_context *ctx = get_context(env);
+ pdf_document *pdf = from_PDFDocument(env, self);
+ SeekableStreamState *state = NULL;
+ jobject stream = NULL;
+ jbyteArray array = NULL;
+ fz_output *out;
+ const char *options = NULL;
+ int errors = 0;
+ pdf_write_options pwo;
+
+ fz_var(state);
+ fz_var(out);
+
+ if (joptions)
+ {
+ options = (*env)->GetStringUTFChars(env, joptions, NULL);
+ if (!options)
+ return 0;
+ }
+
+ stream = (*env)->NewGlobalRef(env, jstream);
+ if (!stream)
+ {
+ if (options)
+ (*env)->ReleaseStringUTFChars(env, joptions, options);
+ return 0;
+ }
+
+ array = (*env)->NewByteArray(env, sizeof state->buffer);
+ if (array)
+ array = (*env)->NewGlobalRef(env, array);
+ if (!array)
+ {
+ if (options)
+ (*env)->ReleaseStringUTFChars(env, joptions, options);
+ (*env)->DeleteGlobalRef(env, stream);
+ return 0;
+ }
+
+ fz_try(ctx)
+ {
+ state = fz_malloc(ctx, sizeof(SeekableStreamState));
+ state->stream = stream;
+ state->array = array;
+
+ out = fz_new_output(ctx, state, call_SeekableOutputStream_write, call_SeekableOutputStream_close, call_SeekableOutputStream_drop);
+ out->seek = call_SeekableOutputStream_seek;
+ out->tell = call_SeekableOutputStream_tell;
+
+ /* these are now owned by 'out' */
+ state = NULL;
+ stream = NULL;
+ array = NULL;
+
+ pdf_parse_write_options(ctx, &pwo, options);
+ pwo.errors = &errors;
+ pdf_write_document(ctx, pdf, out, &pwo);
+ fz_close_output(ctx, out);
+ }
+ fz_always(ctx)
+ {
+ if (options)
+ (*env)->ReleaseStringUTFChars(env, joptions, options);
+ fz_drop_output(ctx, out);
+ }
+ fz_catch(ctx)
+ {
+ if (stream) (*env)->DeleteGlobalRef(env, stream);
+ if (array) (*env)->DeleteGlobalRef(env, array);
+ fz_free(ctx, state);
+ jni_rethrow(env, ctx);
+ return 0;
+ }
+
+ return errors;
+}
+
+JNIEXPORT jint JNICALL
FUN(PDFDocument_save)(JNIEnv *env, jobject self, jstring jfilename, jstring joptions)
{
fz_context *ctx = get_context(env);
diff --git a/platform/java/mupdf_native.h b/platform/java/mupdf_native.h
index 2065f93e..3c7f3570 100644
--- a/platform/java/mupdf_native.h
+++ b/platform/java/mupdf_native.h
@@ -623,6 +623,14 @@ JNIEXPORT jobject JNICALL Java_com_artifex_mupdf_fitz_Document_openNativeWithBuf
/*
* Class: com_artifex_mupdf_fitz_Document
+ * Method: openNativeWithStream
+ * Signature: (Lcom/artifex/mupdf/fitz/SeekableInputStream;Ljava/lang/String;)Lcom/artifex/mupdf/fitz/Document;
+ */
+JNIEXPORT jobject JNICALL Java_com_artifex_mupdf_fitz_Document_openNativeWithStream
+ (JNIEnv *, jclass, jobject, jstring);
+
+/*
+ * Class: com_artifex_mupdf_fitz_Document
* Method: recognize
* Signature: (Ljava/lang/String;)Z
*/
@@ -863,6 +871,17 @@ JNIEXPORT jlong JNICALL Java_com_artifex_mupdf_fitz_DrawDevice_newNative
}
#endif
#endif
+/* Header for class com_artifex_mupdf_fitz_FileStream */
+
+#ifndef _Included_com_artifex_mupdf_fitz_FileStream
+#define _Included_com_artifex_mupdf_fitz_FileStream
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef __cplusplus
+}
+#endif
+#endif
/* Header for class com_artifex_mupdf_fitz_Font */
#ifndef _Included_com_artifex_mupdf_fitz_Font
@@ -1899,6 +1918,14 @@ JNIEXPORT jboolean JNICALL Java_com_artifex_mupdf_fitz_PDFDocument_canBeSavedInc
JNIEXPORT jint JNICALL Java_com_artifex_mupdf_fitz_PDFDocument_save
(JNIEnv *, jobject, jstring, jstring);
+/*
+ * Class: com_artifex_mupdf_fitz_PDFDocument
+ * Method: saveWithStream
+ * Signature: (Lcom/artifex/mupdf/fitz/SeekableOutputStream;Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_com_artifex_mupdf_fitz_PDFDocument_saveWithStream
+ (JNIEnv *, jobject, jobject, jstring);
+
#ifdef __cplusplus
}
#endif
@@ -2845,6 +2872,45 @@ extern "C" {
}
#endif
#endif
+/* Header for class com_artifex_mupdf_fitz_SeekableInputStream */
+
+#ifndef _Included_com_artifex_mupdf_fitz_SeekableInputStream
+#define _Included_com_artifex_mupdf_fitz_SeekableInputStream
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef __cplusplus
+}
+#endif
+#endif
+/* Header for class com_artifex_mupdf_fitz_SeekableOutputStream */
+
+#ifndef _Included_com_artifex_mupdf_fitz_SeekableOutputStream
+#define _Included_com_artifex_mupdf_fitz_SeekableOutputStream
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef __cplusplus
+}
+#endif
+#endif
+/* Header for class com_artifex_mupdf_fitz_SeekableStream */
+
+#ifndef _Included_com_artifex_mupdf_fitz_SeekableStream
+#define _Included_com_artifex_mupdf_fitz_SeekableStream
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef com_artifex_mupdf_fitz_SeekableStream_SEEK_SET
+#define com_artifex_mupdf_fitz_SeekableStream_SEEK_SET 0L
+#undef com_artifex_mupdf_fitz_SeekableStream_SEEK_CUR
+#define com_artifex_mupdf_fitz_SeekableStream_SEEK_CUR 1L
+#undef com_artifex_mupdf_fitz_SeekableStream_SEEK_END
+#define com_artifex_mupdf_fitz_SeekableStream_SEEK_END 2L
+#ifdef __cplusplus
+}
+#endif
+#endif
/* Header for class com_artifex_mupdf_fitz_Separation */
#ifndef _Included_com_artifex_mupdf_fitz_Separation
diff --git a/platform/java/src/com/artifex/mupdf/fitz/Document.java b/platform/java/src/com/artifex/mupdf/fitz/Document.java
index 9b512c89..0da94d87 100644
--- a/platform/java/src/com/artifex/mupdf/fitz/Document.java
+++ b/platform/java/src/com/artifex/mupdf/fitz/Document.java
@@ -27,6 +27,7 @@ public class Document
protected native static Document openNativeWithPath(String filename);
protected native static Document openNativeWithBuffer(byte buffer[], String magic);
+ protected native static Document openNativeWithStream(SeekableInputStream stream, String mimeType);
public static Document openDocument(String filename) {
Document doc = openNativeWithPath(filename);
@@ -38,6 +39,10 @@ public class Document
return openNativeWithBuffer(buffer, magic);
}
+ public static Document openDocument(SeekableInputStream stream, String magic) {
+ return openNativeWithStream(stream, magic);
+ }
+
public static native boolean recognize(String magic);
public native boolean needsPassword();
diff --git a/platform/java/src/com/artifex/mupdf/fitz/FileStream.java b/platform/java/src/com/artifex/mupdf/fitz/FileStream.java
new file mode 100644
index 00000000..3d9ad0d3
--- /dev/null
+++ b/platform/java/src/com/artifex/mupdf/fitz/FileStream.java
@@ -0,0 +1,43 @@
+package com.artifex.mupdf.fitz;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+public class FileStream implements SeekableInputStream, SeekableOutputStream
+{
+ protected RandomAccessFile file;
+
+ public FileStream(String path, String mode) throws IOException {
+ file = new RandomAccessFile(path, mode);
+ }
+
+ public FileStream(File path, String mode) throws IOException {
+ file = new RandomAccessFile(path, mode);
+ }
+
+ public int read(byte[] buf) throws IOException {
+ return file.read(buf);
+ }
+
+ public void write(byte[] buf, int off, int len) throws IOException {
+ file.write(buf, off, len);
+ }
+
+ public long seek(long offset, int whence) throws IOException {
+ switch (whence) {
+ case SEEK_SET: file.seek(offset); break;
+ case SEEK_CUR: file.seek(file.getFilePointer() + offset); break;
+ case SEEK_END: file.seek(file.length() + offset); break;
+ }
+ return file.getFilePointer();
+ }
+
+ public long position() throws IOException {
+ return file.getFilePointer();
+ }
+
+ public void close() throws IOException {
+ file.close();
+ }
+}
diff --git a/platform/java/src/com/artifex/mupdf/fitz/PDFDocument.java b/platform/java/src/com/artifex/mupdf/fitz/PDFDocument.java
index 68835aaa..945294f3 100644
--- a/platform/java/src/com/artifex/mupdf/fitz/PDFDocument.java
+++ b/platform/java/src/com/artifex/mupdf/fitz/PDFDocument.java
@@ -100,5 +100,11 @@ public class PDFDocument extends Document
public native PDFObject addFont(Font font);
public native boolean hasUnsavedChanges();
public native boolean canBeSavedIncrementally();
+
public native int save(String filename, String options);
+
+ protected native int nativeSaveWithStream(SeekableOutputStream stream, String options);
+ public int save(SeekableOutputStream stream, String options) {
+ return nativeSaveWithStream(stream, options);
+ }
}
diff --git a/platform/java/src/com/artifex/mupdf/fitz/SeekableInputStream.java b/platform/java/src/com/artifex/mupdf/fitz/SeekableInputStream.java
new file mode 100644
index 00000000..74b1a185
--- /dev/null
+++ b/platform/java/src/com/artifex/mupdf/fitz/SeekableInputStream.java
@@ -0,0 +1,9 @@
+package com.artifex.mupdf.fitz;
+
+import java.io.IOException;
+
+public interface SeekableInputStream extends SeekableStream
+{
+ /* Behaves like java.io.InputStream.read */
+ int read(byte[] b) throws IOException;
+}
diff --git a/platform/java/src/com/artifex/mupdf/fitz/SeekableOutputStream.java b/platform/java/src/com/artifex/mupdf/fitz/SeekableOutputStream.java
new file mode 100644
index 00000000..d22ba996
--- /dev/null
+++ b/platform/java/src/com/artifex/mupdf/fitz/SeekableOutputStream.java
@@ -0,0 +1,9 @@
+package com.artifex.mupdf.fitz;
+
+import java.io.IOException;
+
+public interface SeekableOutputStream extends SeekableStream
+{
+ /* Behaves like java.io.OutputStream.write */
+ void write(byte[] b, int off, int len) throws IOException;
+}
diff --git a/platform/java/src/com/artifex/mupdf/fitz/SeekableStream.java b/platform/java/src/com/artifex/mupdf/fitz/SeekableStream.java
new file mode 100644
index 00000000..83f79ea2
--- /dev/null
+++ b/platform/java/src/com/artifex/mupdf/fitz/SeekableStream.java
@@ -0,0 +1,14 @@
+package com.artifex.mupdf.fitz;
+
+import java.io.IOException;
+
+public interface SeekableStream
+{
+ int SEEK_SET = 0; /* set file position to offset */
+ int SEEK_CUR = 1; /* set file position to current location plus offset */
+ int SEEK_END = 2; /* set file position to EOF plus offset */
+
+ long seek(long offset, int whence) throws IOException;
+ long position() throws IOException;
+ void close() throws IOException;
+}