comparison mupdf-source/platform/java/jni/documentwriter.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
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 // Copyright (C) 2004-2025 Artifex Software, Inc.
2 //
3 // This file is part of MuPDF.
4 //
5 // MuPDF is free software: you can redistribute it and/or modify it under the
6 // terms of the GNU Affero General Public License as published by the Free
7 // Software Foundation, either version 3 of the License, or (at your option)
8 // any later version.
9 //
10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU Affero General Public License
16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17 //
18 // Alternative licensing terms are available from the licensor.
19 // For commercial licensing, see <https://www.artifex.com/> or contact
20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21 // CA 94129, USA, for further information.
22
23 /* DocumentWriter interface */
24
25 JNIEXPORT void JNICALL
26 FUN(DocumentWriter_finalize)(JNIEnv *env, jobject self)
27 {
28 fz_context *ctx = get_context(env);
29 fz_document_writer *wri = from_DocumentWriter_safe(env, self);
30 jobject ref;
31 if (!ctx || !wri) return;
32 ref = (jobject)(*env)->GetLongField(env, self, fid_DocumentWriter_ocrlistener);
33 if (ref)
34 {
35 (*env)->DeleteGlobalRef(env, ref);
36 (*env)->SetLongField(env, self, fid_DocumentWriter_ocrlistener, 0);
37 }
38 (*env)->SetLongField(env, self, fid_DocumentWriter_pointer, 0);
39 fz_drop_document_writer(ctx, wri);
40 }
41
42 JNIEXPORT jlong JNICALL
43 FUN(DocumentWriter_newNativeDocumentWriter)(JNIEnv *env, jclass cls, jstring jfilename, jstring jformat, jstring joptions)
44 {
45 fz_context *ctx = get_context(env);
46 fz_document_writer *wri = NULL;
47 const char *filename = NULL;
48 const char *format = NULL;
49 const char *options = NULL;
50
51 if (!ctx) return 0;
52 if (!jfilename) jni_throw_arg(env, "filename must not be null");
53
54 filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
55 if (!filename) return 0;
56
57 if (jformat)
58 {
59 format = (*env)->GetStringUTFChars(env, jformat, NULL);
60 if (!format)
61 {
62 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
63 return 0;
64 }
65 }
66 if (joptions)
67 {
68 options = (*env)->GetStringUTFChars(env, joptions, NULL);
69 if (!options)
70 {
71 if (format)
72 (*env)->ReleaseStringUTFChars(env, jformat, format);
73 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
74 return 0;
75 }
76 }
77
78 fz_try(ctx)
79 wri = fz_new_document_writer(ctx, filename, format, options);
80 fz_always(ctx)
81 {
82 if (options)
83 (*env)->ReleaseStringUTFChars(env, joptions, options);
84 if (format)
85 (*env)->ReleaseStringUTFChars(env, jformat, format);
86 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
87 }
88 fz_catch(ctx)
89 jni_rethrow(env, ctx);
90
91 return jlong_cast(wri);
92 }
93
94 JNIEXPORT jlong JNICALL
95 FUN(DocumentWriter_newNativeDocumentWriterWithSeekableOutputStream)(JNIEnv *env, jclass cls, jobject jstream, jstring jformat, jstring joptions)
96 {
97 fz_context *ctx = get_context(env);
98 fz_document_writer *wri = NULL;
99 SeekableStreamState *state = NULL;
100 jobject stream = NULL;
101 const char *format = NULL;
102 const char *options = NULL;
103 jbyteArray array = NULL;
104 fz_output *out;
105
106 if (!ctx) return 0;
107 if (!jstream) jni_throw_arg(env, "output stream must not be null");
108
109 stream = (*env)->NewGlobalRef(env, jstream);
110 if (!stream)
111 return 0;
112
113 array = (*env)->NewByteArray(env, sizeof state->buffer);
114 if (array)
115 array = (*env)->NewGlobalRef(env, array);
116 if (!array)
117 {
118 (*env)->DeleteGlobalRef(env, stream);
119 return 0;
120 }
121
122 if (jformat)
123 {
124 format = (*env)->GetStringUTFChars(env, jformat, NULL);
125 if (!format)
126 return 0;
127 }
128 if (joptions)
129 {
130 options = (*env)->GetStringUTFChars(env, joptions, NULL);
131 if (!options)
132 {
133 if (format)
134 (*env)->ReleaseStringUTFChars(env, jformat, format);
135 return 0;
136 }
137 }
138
139 fz_var(state);
140 fz_var(out);
141 fz_var(stream);
142 fz_var(array);
143
144 fz_try(ctx)
145 {
146 fz_output *out_temp;
147 state = Memento_label(fz_malloc(ctx, sizeof(SeekableStreamState)), "SeekableStreamState_newNativeDocumentWriterWithSeekableOutputStream");
148 state->stream = stream;
149 state->array = array;
150
151 out = fz_new_output(ctx, 8192, state, SeekableOutputStream_write, NULL, SeekableOutputStream_drop);
152 out->seek = SeekableOutputStream_seek;
153 out->tell = SeekableOutputStream_tell;
154
155 /* these are now owned by 'out' */
156 state = NULL;
157 stream = NULL;
158 array = NULL;
159
160 /* out becomes owned by 'wri' as soon as we call, even if it throws. */
161 out_temp = out;
162 out = NULL;
163 wri = fz_new_document_writer_with_output(ctx, out_temp, format, options);
164 }
165 fz_always(ctx)
166 {
167 fz_drop_output(ctx, out);
168 if (options)
169 (*env)->ReleaseStringUTFChars(env, joptions, options);
170 if (format)
171 (*env)->ReleaseStringUTFChars(env, jformat, format);
172 }
173 fz_catch(ctx)
174 jni_rethrow(env, ctx);
175
176 return jlong_cast(wri);
177 }
178
179 JNIEXPORT jlong JNICALL
180 FUN(DocumentWriter_newNativeDocumentWriterWithBuffer)(JNIEnv *env, jclass cls, jobject jbuffer, jstring jformat, jstring joptions)
181 {
182 fz_context *ctx = get_context(env);
183 fz_document_writer *wri = NULL;
184 fz_buffer *buffer = from_Buffer_safe(env, jbuffer);
185 const char *format = NULL;
186 const char *options = NULL;
187
188 if (!ctx) return 0;
189 if (!buffer) jni_throw_arg(env, "output buffer must not be null");
190
191 if (jformat)
192 {
193 format = (*env)->GetStringUTFChars(env, jformat, NULL);
194 if (!format)
195 return 0;
196 }
197 if (joptions)
198 {
199 options = (*env)->GetStringUTFChars(env, joptions, NULL);
200 if (!options)
201 {
202 if (format)
203 (*env)->ReleaseStringUTFChars(env, jformat, format);
204 return 0;
205 }
206 }
207
208 fz_try(ctx)
209 wri = fz_new_document_writer_with_buffer(ctx, buffer, format, options);
210 fz_always(ctx)
211 {
212 if (options)
213 (*env)->ReleaseStringUTFChars(env, joptions, options);
214 if (format)
215 (*env)->ReleaseStringUTFChars(env, jformat, format);
216 }
217 fz_catch(ctx)
218 jni_rethrow(env, ctx);
219
220 return jlong_cast(wri);
221 }
222
223 JNIEXPORT jobject JNICALL
224 FUN(DocumentWriter_beginPage)(JNIEnv *env, jobject self, jobject jmediabox)
225 {
226 fz_context *ctx = get_context(env);
227 fz_document_writer *wri = from_DocumentWriter(env, self);
228 fz_rect mediabox = from_Rect(env, jmediabox);
229 fz_device *device = NULL;
230
231 if (!ctx || !wri) return NULL;
232
233 fz_try(ctx)
234 device = fz_begin_page(ctx, wri, mediabox);
235 fz_catch(ctx)
236 jni_rethrow(env, ctx);
237
238 return to_NativeDevice_safe_own(ctx, env, fz_keep_device(ctx, device));
239 }
240
241 JNIEXPORT void JNICALL
242 FUN(DocumentWriter_endPage)(JNIEnv *env, jobject self)
243 {
244 fz_context *ctx = get_context(env);
245 fz_document_writer *wri = from_DocumentWriter(env, self);
246
247 if (!ctx || !wri) return;
248
249 fz_try(ctx)
250 fz_end_page(ctx, wri);
251 fz_catch(ctx)
252 jni_rethrow_void(env, ctx);
253 }
254
255 JNIEXPORT void JNICALL
256 FUN(DocumentWriter_close)(JNIEnv *env, jobject self)
257 {
258 fz_context *ctx = get_context(env);
259 fz_document_writer *wri = from_DocumentWriter(env, self);
260
261 if (!ctx || !wri) return;
262
263 fz_try(ctx)
264 fz_close_document_writer(ctx, wri);
265 fz_catch(ctx)
266 jni_rethrow_void(env, ctx);
267 }
268
269 static int
270 jni_ocr_progress(fz_context *ctx, void *arg, int page, int percent)
271 {
272 jobject ref = (jobject)arg;
273 jboolean cancel;
274 JNIEnv *env = NULL;
275 jboolean detach = JNI_FALSE;
276
277 if (ref == NULL)
278 return JNI_FALSE;
279
280 env = jni_attach_thread(&detach);
281 if (env == NULL)
282 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot attach to JVM in jni_ocr_progress");
283
284 cancel = (*env)->CallBooleanMethod(env, ref, mid_DocumentWriter_OCRListener_progress, page, percent);
285 if ((*env)->ExceptionCheck(env))
286 cancel = 1;
287
288 jni_detach_thread(detach);
289
290 return !!cancel;
291 }
292
293 JNIEXPORT void JNICALL
294 FUN(DocumentWriter_addOCRListener)(JNIEnv *env, jobject self, jobject jlistener)
295 {
296 fz_context *ctx = get_context(env);
297 fz_document_writer *wri = from_DocumentWriter(env, self);
298 jobject ref;
299
300 if (!ctx || !wri) return;
301
302 /* Delete any old OCRListener if there is one. */
303 ref = (jobject)(*env)->GetLongField(env, self, fid_DocumentWriter_ocrlistener);
304 if (ref != NULL)
305 {
306 (*env)->DeleteGlobalRef(env, ref);
307 (*env)->SetLongField(env, self, fid_DocumentWriter_ocrlistener, 0);
308 }
309
310 /* Take a ref and store it for the callback to use */
311 ref = (*env)->NewGlobalRef(env, jlistener);
312 if (!ref)
313 jni_throw_run_void(env, "cannot take reference to listener");
314 (*env)->SetLongField(env, self, fid_DocumentWriter_ocrlistener, jlong_cast(ref));
315
316 fz_try(ctx)
317 fz_pdfocr_writer_set_progress(ctx, wri, jni_ocr_progress, ref);
318 fz_catch(ctx)
319 {
320 (*env)->DeleteGlobalRef(env, ref);
321 (*env)->SetLongField(env, self, fid_DocumentWriter_ocrlistener, 0);
322 jni_rethrow_void(env, ctx);
323 }
324
325 }