diff mupdf-source/platform/java/jni/documentwriter.c @ 3:2c135c81b16c

MERGE: upstream PyMuPDF 1.26.4 with MuPDF 1.26.7
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:44:09 +0200
parents b50eed0cc0ef
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/platform/java/jni/documentwriter.c	Mon Sep 15 11:44:09 2025 +0200
@@ -0,0 +1,325 @@
+// Copyright (C) 2004-2025 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.
+
+/* DocumentWriter interface */
+
+JNIEXPORT void JNICALL
+FUN(DocumentWriter_finalize)(JNIEnv *env, jobject self)
+{
+	fz_context *ctx = get_context(env);
+	fz_document_writer *wri = from_DocumentWriter_safe(env, self);
+	jobject ref;
+	if (!ctx || !wri) return;
+	ref = (jobject)(*env)->GetLongField(env, self, fid_DocumentWriter_ocrlistener);
+	if (ref)
+	{
+		(*env)->DeleteGlobalRef(env, ref);
+		(*env)->SetLongField(env, self, fid_DocumentWriter_ocrlistener, 0);
+	}
+	(*env)->SetLongField(env, self, fid_DocumentWriter_pointer, 0);
+	fz_drop_document_writer(ctx, wri);
+}
+
+JNIEXPORT jlong JNICALL
+FUN(DocumentWriter_newNativeDocumentWriter)(JNIEnv *env, jclass cls, jstring jfilename, jstring jformat, jstring joptions)
+{
+	fz_context *ctx = get_context(env);
+	fz_document_writer *wri = NULL;
+	const char *filename = NULL;
+	const char *format = NULL;
+	const char *options = NULL;
+
+	if (!ctx) return 0;
+	if (!jfilename) jni_throw_arg(env, "filename must not be null");
+
+	filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
+	if (!filename) return 0;
+
+	if (jformat)
+	{
+		format = (*env)->GetStringUTFChars(env, jformat, NULL);
+		if (!format)
+		{
+			(*env)->ReleaseStringUTFChars(env, jfilename, filename);
+			return 0;
+		}
+	}
+	if (joptions)
+	{
+		options = (*env)->GetStringUTFChars(env, joptions, NULL);
+		if (!options)
+		{
+			if (format)
+				(*env)->ReleaseStringUTFChars(env, jformat, format);
+			(*env)->ReleaseStringUTFChars(env, jfilename, filename);
+			return 0;
+		}
+	}
+
+	fz_try(ctx)
+		wri = fz_new_document_writer(ctx, filename, format, options);
+	fz_always(ctx)
+	{
+		if (options)
+			(*env)->ReleaseStringUTFChars(env, joptions, options);
+		if (format)
+			(*env)->ReleaseStringUTFChars(env, jformat, format);
+		(*env)->ReleaseStringUTFChars(env, jfilename, filename);
+	}
+	fz_catch(ctx)
+		jni_rethrow(env, ctx);
+
+	return jlong_cast(wri);
+}
+
+JNIEXPORT jlong JNICALL
+FUN(DocumentWriter_newNativeDocumentWriterWithSeekableOutputStream)(JNIEnv *env, jclass cls, jobject jstream, jstring jformat, jstring joptions)
+{
+	fz_context *ctx = get_context(env);
+	fz_document_writer *wri = NULL;
+	SeekableStreamState *state = NULL;
+	jobject stream = NULL;
+	const char *format = NULL;
+	const char *options = NULL;
+	jbyteArray array = NULL;
+	fz_output *out;
+
+	if (!ctx) return 0;
+	if (!jstream) jni_throw_arg(env, "output stream must not be null");
+
+	stream = (*env)->NewGlobalRef(env, jstream);
+	if (!stream)
+		return 0;
+
+	array = (*env)->NewByteArray(env, sizeof state->buffer);
+	if (array)
+		array = (*env)->NewGlobalRef(env, array);
+	if (!array)
+	{
+		(*env)->DeleteGlobalRef(env, stream);
+		return 0;
+	}
+
+	if (jformat)
+	{
+		format = (*env)->GetStringUTFChars(env, jformat, NULL);
+		if (!format)
+			return 0;
+	}
+	if (joptions)
+	{
+		options = (*env)->GetStringUTFChars(env, joptions, NULL);
+		if (!options)
+		{
+			if (format)
+				(*env)->ReleaseStringUTFChars(env, jformat, format);
+			return 0;
+		}
+	}
+
+	fz_var(state);
+	fz_var(out);
+	fz_var(stream);
+	fz_var(array);
+
+	fz_try(ctx)
+	{
+		fz_output *out_temp;
+		state = Memento_label(fz_malloc(ctx, sizeof(SeekableStreamState)), "SeekableStreamState_newNativeDocumentWriterWithSeekableOutputStream");
+		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;
+
+		/* out becomes owned by 'wri' as soon as we call, even if it throws. */
+		out_temp = out;
+		out = NULL;
+		wri = fz_new_document_writer_with_output(ctx, out_temp, format, options);
+	}
+	fz_always(ctx)
+	{
+		fz_drop_output(ctx, out);
+		if (options)
+			(*env)->ReleaseStringUTFChars(env, joptions, options);
+		if (format)
+			(*env)->ReleaseStringUTFChars(env, jformat, format);
+	}
+	fz_catch(ctx)
+		jni_rethrow(env, ctx);
+
+	return jlong_cast(wri);
+}
+
+JNIEXPORT jlong JNICALL
+FUN(DocumentWriter_newNativeDocumentWriterWithBuffer)(JNIEnv *env, jclass cls, jobject jbuffer, jstring jformat, jstring joptions)
+{
+	fz_context *ctx = get_context(env);
+	fz_document_writer *wri = NULL;
+	fz_buffer *buffer = from_Buffer_safe(env, jbuffer);
+	const char *format = NULL;
+	const char *options = NULL;
+
+	if (!ctx) return 0;
+	if (!buffer) jni_throw_arg(env, "output buffer must not be null");
+
+	if (jformat)
+	{
+		format = (*env)->GetStringUTFChars(env, jformat, NULL);
+		if (!format)
+			return 0;
+	}
+	if (joptions)
+	{
+		options = (*env)->GetStringUTFChars(env, joptions, NULL);
+		if (!options)
+		{
+			if (format)
+				(*env)->ReleaseStringUTFChars(env, jformat, format);
+			return 0;
+		}
+	}
+
+	fz_try(ctx)
+		wri = fz_new_document_writer_with_buffer(ctx, buffer, format, options);
+	fz_always(ctx)
+	{
+		if (options)
+			(*env)->ReleaseStringUTFChars(env, joptions, options);
+		if (format)
+			(*env)->ReleaseStringUTFChars(env, jformat, format);
+	}
+	fz_catch(ctx)
+		jni_rethrow(env, ctx);
+
+	return jlong_cast(wri);
+}
+
+JNIEXPORT jobject JNICALL
+FUN(DocumentWriter_beginPage)(JNIEnv *env, jobject self, jobject jmediabox)
+{
+	fz_context *ctx = get_context(env);
+	fz_document_writer *wri = from_DocumentWriter(env, self);
+	fz_rect mediabox = from_Rect(env, jmediabox);
+	fz_device *device = NULL;
+
+	if (!ctx || !wri) return NULL;
+
+	fz_try(ctx)
+		device = fz_begin_page(ctx, wri, mediabox);
+	fz_catch(ctx)
+		jni_rethrow(env, ctx);
+
+	return to_NativeDevice_safe_own(ctx, env, fz_keep_device(ctx, device));
+}
+
+JNIEXPORT void JNICALL
+FUN(DocumentWriter_endPage)(JNIEnv *env, jobject self)
+{
+	fz_context *ctx = get_context(env);
+	fz_document_writer *wri = from_DocumentWriter(env, self);
+
+	if (!ctx || !wri) return;
+
+	fz_try(ctx)
+		fz_end_page(ctx, wri);
+	fz_catch(ctx)
+		jni_rethrow_void(env, ctx);
+}
+
+JNIEXPORT void JNICALL
+FUN(DocumentWriter_close)(JNIEnv *env, jobject self)
+{
+	fz_context *ctx = get_context(env);
+	fz_document_writer *wri = from_DocumentWriter(env, self);
+
+	if (!ctx || !wri) return;
+
+	fz_try(ctx)
+		fz_close_document_writer(ctx, wri);
+	fz_catch(ctx)
+		jni_rethrow_void(env, ctx);
+}
+
+static int
+jni_ocr_progress(fz_context *ctx, void *arg, int page, int percent)
+{
+	jobject ref = (jobject)arg;
+	jboolean cancel;
+	JNIEnv *env = NULL;
+	jboolean detach = JNI_FALSE;
+
+	if (ref == NULL)
+		return JNI_FALSE;
+
+	env = jni_attach_thread(&detach);
+	if (env == NULL)
+		fz_throw(ctx, FZ_ERROR_GENERIC, "cannot attach to JVM in jni_ocr_progress");
+
+	cancel = (*env)->CallBooleanMethod(env, ref, mid_DocumentWriter_OCRListener_progress, page, percent);
+	if ((*env)->ExceptionCheck(env))
+		cancel = 1;
+
+	jni_detach_thread(detach);
+
+	return !!cancel;
+}
+
+JNIEXPORT void JNICALL
+FUN(DocumentWriter_addOCRListener)(JNIEnv *env, jobject self, jobject jlistener)
+{
+	fz_context *ctx = get_context(env);
+	fz_document_writer *wri = from_DocumentWriter(env, self);
+	jobject ref;
+
+	if (!ctx || !wri) return;
+
+	/* Delete any old OCRListener if there is one. */
+	ref = (jobject)(*env)->GetLongField(env, self, fid_DocumentWriter_ocrlistener);
+	if (ref != NULL)
+	{
+		(*env)->DeleteGlobalRef(env, ref);
+		(*env)->SetLongField(env, self, fid_DocumentWriter_ocrlistener, 0);
+	}
+
+	/* Take a ref and store it for the callback to use */
+	ref = (*env)->NewGlobalRef(env, jlistener);
+	if (!ref)
+		jni_throw_run_void(env, "cannot take reference to listener");
+	(*env)->SetLongField(env, self, fid_DocumentWriter_ocrlistener, jlong_cast(ref));
+
+	fz_try(ctx)
+		fz_pdfocr_writer_set_progress(ctx, wri, jni_ocr_progress, ref);
+	fz_catch(ctx)
+	{
+		(*env)->DeleteGlobalRef(env, ref);
+		(*env)->SetLongField(env, self, fid_DocumentWriter_ocrlistener, 0);
+		jni_rethrow_void(env, ctx);
+	}
+
+}