comparison 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
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 // Copyright (C) 2004-2024 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 /* Document interface */
24
25 /* Callbacks to implement fz_stream and fz_output using Java classes */
26
27 typedef struct
28 {
29 jobject stream;
30 jbyteArray array;
31 jbyte buffer[8192];
32 }
33 SeekableStreamState;
34
35 static int SeekableInputStream_next(fz_context *ctx, fz_stream *stm, size_t max)
36 {
37 SeekableStreamState *state = stm->state;
38 jboolean detach = JNI_FALSE;
39 JNIEnv *env;
40 int n, ch;
41
42 env = jni_attach_thread(&detach);
43 if (env == NULL)
44 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot attach to JVM in SeekableInputStream_next");
45
46 n = (*env)->CallIntMethod(env, state->stream, mid_SeekableInputStream_read, state->array);
47 if ((*env)->ExceptionCheck(env))
48 fz_throw_java_and_detach_thread(ctx, env, detach);
49
50 if (n > 0)
51 {
52 (*env)->GetByteArrayRegion(env, state->array, 0, n, state->buffer);
53 if ((*env)->ExceptionCheck(env))
54 fz_throw_java_and_detach_thread(ctx, env, detach);
55
56 /* update stm->pos so fz_tell knows the current position */
57 stm->rp = (unsigned char *)state->buffer;
58 stm->wp = stm->rp + n;
59 stm->pos += n;
60
61 ch = *stm->rp++;
62 }
63 else if (n < 0)
64 {
65 ch = EOF;
66 }
67 else
68 fz_throw_and_detach_thread(ctx, detach, FZ_ERROR_GENERIC, "no bytes read");
69
70 jni_detach_thread(detach);
71 return ch;
72 }
73
74 static void SeekableInputStream_seek(fz_context *ctx, fz_stream *stm, int64_t offset, int whence)
75 {
76 SeekableStreamState *state = stm->state;
77 jboolean detach = JNI_FALSE;
78 JNIEnv *env;
79 int64_t pos;
80
81 env = jni_attach_thread(&detach);
82 if (env == NULL)
83 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot attach to JVM in SeekableInputStream_seek");
84
85 pos = (*env)->CallLongMethod(env, state->stream, mid_SeekableStream_seek, offset, whence);
86 if ((*env)->ExceptionCheck(env))
87 fz_throw_java_and_detach_thread(ctx, env, detach);
88
89 stm->pos = pos;
90 stm->rp = stm->wp = (unsigned char *)state->buffer;
91
92 jni_detach_thread(detach);
93 }
94
95 static void SeekableInputStream_drop(fz_context *ctx, void *streamState_)
96 {
97 SeekableStreamState *state = streamState_;
98 jboolean detach = JNI_FALSE;
99 JNIEnv *env;
100
101 env = jni_attach_thread(&detach);
102 if (env == NULL)
103 {
104 fz_warn(ctx, "cannot attach to JVM in SeekableInputStream_drop; leaking input stream");
105 return;
106 }
107
108 (*env)->DeleteGlobalRef(env, state->stream);
109 (*env)->DeleteGlobalRef(env, state->array);
110
111 fz_free(ctx, state);
112
113 jni_detach_thread(detach);
114 }
115
116 static void SeekableOutputStream_write(fz_context *ctx, void *streamState_, const void *buffer_, size_t count)
117 {
118 SeekableStreamState *state = streamState_;
119 const jbyte *buffer = buffer_;
120 jboolean detach = JNI_FALSE;
121 JNIEnv *env;
122
123 env = jni_attach_thread(&detach);
124 if (env == NULL)
125 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot attach to JVM in SeekableOutputStream_write");
126
127 while (count > 0)
128 {
129 size_t n = fz_minz(count, sizeof(state->buffer));
130
131 (*env)->SetByteArrayRegion(env, state->array, 0, n, buffer);
132 if ((*env)->ExceptionCheck(env))
133 fz_throw_java_and_detach_thread(ctx, env, detach);
134
135 buffer += n;
136 count -= n;
137
138 (*env)->CallVoidMethod(env, state->stream, mid_SeekableOutputStream_write, state->array, 0, n);
139 if ((*env)->ExceptionCheck(env))
140 fz_throw_java_and_detach_thread(ctx, env, detach);
141 }
142
143 jni_detach_thread(detach);
144 }
145
146 static int64_t SeekableOutputStream_tell(fz_context *ctx, void *streamState_)
147 {
148 SeekableStreamState *state = streamState_;
149 jboolean detach = JNI_FALSE;
150 int64_t pos = 0;
151 JNIEnv *env;
152
153 env = jni_attach_thread(&detach);
154 if (env == NULL)
155 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot attach to JVM in SeekableOutputStream_tell");
156
157 pos = (*env)->CallLongMethod(env, state->stream, mid_SeekableStream_position);
158 if ((*env)->ExceptionCheck(env))
159 fz_throw_java_and_detach_thread(ctx, env, detach);
160
161 jni_detach_thread(detach);
162
163 return pos;
164 }
165
166 static void SeekableOutputStream_truncate(fz_context *ctx, void *streamState_)
167 {
168 SeekableStreamState *state = streamState_;
169 jboolean detach = JNI_FALSE;
170 JNIEnv *env;
171
172 env = jni_attach_thread(&detach);
173 if (env == NULL)
174 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot attach to JVM in SeekableOutputStream_truncate");
175
176 (*env)->CallVoidMethod(env, state->stream, mid_SeekableOutputStream_truncate);
177 if ((*env)->ExceptionCheck(env))
178 fz_throw_java_and_detach_thread(ctx, env, detach);
179
180 jni_detach_thread(detach);
181 }
182
183 static void SeekableOutputStream_seek(fz_context *ctx, void *streamState_, int64_t offset, int whence)
184 {
185 SeekableStreamState *state = streamState_;
186 jboolean detach = JNI_FALSE;
187 JNIEnv *env;
188
189 env = jni_attach_thread(&detach);
190 if (env == NULL)
191 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot attach to JVM in SeekableOutputStream_seek");
192
193 (void) (*env)->CallLongMethod(env, state->stream, mid_SeekableStream_seek, offset, whence);
194 if ((*env)->ExceptionCheck(env))
195 fz_throw_java_and_detach_thread(ctx, env, detach);
196
197 jni_detach_thread(detach);
198 }
199
200 static void SeekableOutputStream_drop(fz_context *ctx, void *streamState_)
201 {
202 SeekableStreamState *state = streamState_;
203 jboolean detach = JNI_FALSE;
204 JNIEnv *env;
205
206 env = jni_attach_thread(&detach);
207 if (env == NULL)
208 {
209 fz_warn(ctx, "cannot attach to JVM in SeekableOutputStream_drop; leaking output stream");
210 return;
211 }
212
213 (*env)->DeleteGlobalRef(env, state->stream);
214 (*env)->DeleteGlobalRef(env, state->array);
215
216 fz_free(ctx, state);
217
218 jni_detach_thread(detach);
219 }
220 JNIEXPORT void JNICALL
221 FUN(Document_finalize)(JNIEnv *env, jobject self)
222 {
223 fz_context *ctx = get_context(env);
224 fz_document *doc = from_Document_safe(env, self);
225 if (!ctx || !doc) return;
226 (*env)->SetLongField(env, self, fid_Document_pointer, 0);
227 fz_drop_document(ctx, doc);
228
229 /* This is a reasonable place to call Memento. */
230 Memento_fin();
231 }
232
233 JNIEXPORT jobject JNICALL
234 FUN(Document_openNativeWithStream)(JNIEnv *env, jclass cls, jstring jmagic, jobject jdocument, jobject jaccelerator)
235 {
236 fz_context *ctx = get_context(env);
237 fz_document *doc = NULL;
238 fz_stream *docstream = NULL;
239 fz_stream *accstream = NULL;
240 jobject jdoc = NULL;
241 jobject jacc = NULL;
242 jbyteArray docarray = NULL;
243 jbyteArray accarray = NULL;
244 SeekableStreamState *docstate = NULL;
245 SeekableStreamState *accstate = NULL;
246 const char *magic = NULL;
247
248 fz_var(jdoc);
249 fz_var(jacc);
250 fz_var(docarray);
251 fz_var(accarray);
252 fz_var(docstream);
253 fz_var(accstream);
254
255 if (!ctx) return NULL;
256 if (jmagic)
257 {
258 magic = (*env)->GetStringUTFChars(env, jmagic, NULL);
259 if (!magic) jni_throw_run(env, "cannot get characters in magic string");
260 }
261 if (jdocument)
262 {
263 jdoc = (*env)->NewGlobalRef(env, jdocument);
264 if (!jdoc)
265 {
266 if (magic) (*env)->ReleaseStringUTFChars(env, jmagic, magic);
267 jni_throw_run(env, "cannot get reference to document stream");
268 }
269 }
270 if (jaccelerator)
271 {
272 jacc = (*env)->NewGlobalRef(env, jaccelerator);
273 if (!jacc)
274 {
275 (*env)->DeleteGlobalRef(env, jdoc);
276 if (magic) (*env)->ReleaseStringUTFChars(env, jmagic, magic);
277 jni_throw_run(env, "cannot get reference to accelerator stream");
278 }
279 }
280
281 docarray = (*env)->NewByteArray(env, sizeof docstate->buffer);
282 if (docarray)
283 docarray = (*env)->NewGlobalRef(env, docarray);
284 if (!docarray)
285 {
286 (*env)->DeleteGlobalRef(env, jacc);
287 (*env)->DeleteGlobalRef(env, jdoc);
288 if (magic) (*env)->ReleaseStringUTFChars(env, jmagic, magic);
289 jni_throw_run(env, "cannot create internal buffer for document stream");
290 }
291
292 if (jacc)
293 {
294 accarray = (*env)->NewByteArray(env, sizeof accstate->buffer);
295 if (accarray)
296 accarray = (*env)->NewGlobalRef(env, accarray);
297 if (!accarray)
298 {
299 (*env)->DeleteGlobalRef(env, docarray);
300 (*env)->DeleteGlobalRef(env, jacc);
301 (*env)->DeleteGlobalRef(env, jdoc);
302 if (magic) (*env)->ReleaseStringUTFChars(env, jmagic, magic);
303 jni_throw_run(env, "cannot create internal buffer for accelerator stream");
304 }
305 }
306
307 fz_try(ctx)
308 {
309 if (jdoc)
310 {
311 /* No exceptions can occur from here to stream owning docstate, so we must not free docstate. */
312 docstate = Memento_label(fz_malloc(ctx, sizeof(SeekableStreamState)), "SeekableStreamState_docstate");
313 docstate->stream = jdoc;
314 docstate->array = docarray;
315
316 /* Ownership transferred to docstate. */
317 jdoc = NULL;
318 docarray = NULL;
319
320 /* Stream takes ownership of docstate. */
321 docstream = fz_new_stream(ctx, docstate, SeekableInputStream_next, SeekableInputStream_drop);
322 docstream->seek = SeekableInputStream_seek;
323 }
324
325 if (jacc)
326 {
327 /* No exceptions can occur from here to stream owning accstate, so we must not free accstate. */
328 accstate = Memento_label(fz_malloc(ctx, sizeof(SeekableStreamState)), "SeekableStreamState_accstate");
329 accstate->stream = jacc;
330 accstate->array = accarray;
331
332 /* Ownership transferred to accstate. */
333 jacc = NULL;
334 accarray = NULL;
335
336 /* Stream takes ownership of accstate. */
337 accstream = fz_new_stream(ctx, accstate, SeekableInputStream_next, SeekableInputStream_drop);
338 accstream->seek = SeekableInputStream_seek;
339 }
340
341 doc = fz_open_accelerated_document_with_stream(ctx, magic, docstream, accstream);
342 }
343 fz_always(ctx)
344 {
345 fz_drop_stream(ctx, accstream);
346 fz_drop_stream(ctx, docstream);
347 if (magic)
348 (*env)->ReleaseStringUTFChars(env, jmagic, magic);
349 }
350 fz_catch(ctx)
351 {
352 (*env)->DeleteGlobalRef(env, accarray);
353 (*env)->DeleteGlobalRef(env, docarray);
354 (*env)->DeleteGlobalRef(env, jacc);
355 (*env)->DeleteGlobalRef(env, jdoc);
356 jni_rethrow(env, ctx);
357 }
358
359 return to_Document_safe_own(ctx, env, doc);
360 }
361
362 JNIEXPORT jobject JNICALL
363 FUN(Document_openNativeWithPath)(JNIEnv *env, jclass cls, jstring jfilename, jstring jaccelerator)
364 {
365 fz_context *ctx = get_context(env);
366 fz_document *doc = NULL;
367 const char *filename = NULL;
368 const char *accelerator = NULL;
369
370 if (!ctx) return NULL;
371 if (jfilename)
372 {
373 filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
374 if (!filename) jni_throw_run(env, "cannot get characters in filename string");
375 }
376 if (jaccelerator)
377 {
378 accelerator = (*env)->GetStringUTFChars(env, jaccelerator, NULL);
379 if (!accelerator) jni_throw_run(env, "cannot get characters in accelerator filename string");
380 }
381
382 fz_try(ctx)
383 doc = fz_open_accelerated_document(ctx, filename, accelerator);
384 fz_always(ctx)
385 {
386 if (accelerator)
387 (*env)->ReleaseStringUTFChars(env, jaccelerator, accelerator);
388 if (filename)
389 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
390 }
391 fz_catch(ctx)
392 jni_rethrow(env, ctx);
393
394 return to_Document_safe_own(ctx, env, doc);
395 }
396
397
398 JNIEXPORT jobject JNICALL
399 FUN(Document_openNativeWithPathAndStream)(JNIEnv *env, jclass cls, jstring jfilename, jobject jaccelerator)
400 {
401 fz_context *ctx = get_context(env);
402 fz_document *doc = NULL;
403 const char *filename = NULL;
404 fz_stream *docstream = NULL;
405 fz_stream *accstream = NULL;
406 jobject jacc = NULL;
407 jbyteArray accarray = NULL;
408 SeekableStreamState *accstate = NULL;
409
410 fz_var(jacc);
411 fz_var(accarray);
412 fz_var(accstream);
413 fz_var(docstream);
414
415 if (!ctx) return NULL;
416 if (jfilename)
417 {
418 filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
419 if (!filename) jni_throw_run(env, "cannot get characters in filename string");
420 }
421 if (jaccelerator)
422 {
423 jacc = (*env)->NewGlobalRef(env, jaccelerator);
424 if (!jacc)
425 {
426 if (jfilename) (*env)->ReleaseStringUTFChars(env, jfilename, filename);
427 jni_throw_run(env, "cannot get reference to accelerator stream");
428 }
429 }
430
431 accarray = (*env)->NewByteArray(env, sizeof accstate->buffer);
432 if (accarray)
433 accarray = (*env)->NewGlobalRef(env, accarray);
434 if (!accarray)
435 {
436 (*env)->DeleteGlobalRef(env, jacc);
437 if (jfilename) (*env)->ReleaseStringUTFChars(env, jfilename, filename);
438 jni_throw_run(env, "cannot get create internal buffer for accelerator stream");
439 }
440
441 fz_try(ctx)
442 {
443 if (filename)
444 docstream = fz_open_file(ctx, filename);
445
446 if (jacc)
447 {
448 /* No exceptions can occur from here to stream owning accstate, so we must not free accstate. */
449 accstate = Memento_label(fz_malloc(ctx, sizeof(SeekableStreamState)), "SeekableStreamState_accstate2");
450 accstate->stream = jacc;
451 accstate->array = accarray;
452
453 /* Ownership transferred to accstate. */
454 jacc = NULL;
455 accarray = NULL;
456
457 /* Stream takes ownership of accstate. */
458 accstream = fz_new_stream(ctx, accstate, SeekableInputStream_next, SeekableInputStream_drop);
459 accstream->seek = SeekableInputStream_seek;
460 }
461
462 doc = fz_open_accelerated_document_with_stream(ctx, filename, docstream, accstream);
463 }
464 fz_always(ctx)
465 {
466 fz_drop_stream(ctx, accstream);
467 fz_drop_stream(ctx, docstream);
468 if (filename) (*env)->ReleaseStringUTFChars(env, jfilename, filename);
469 }
470 fz_catch(ctx)
471 {
472 (*env)->DeleteGlobalRef(env, accarray);
473 (*env)->DeleteGlobalRef(env, jacc);
474 jni_rethrow(env, ctx);
475 }
476
477 return to_Document_safe_own(ctx, env, doc);
478 }
479
480 JNIEXPORT jobject JNICALL
481 FUN(Document_openNativeWithBuffer)(JNIEnv *env, jclass cls, jstring jmagic, jobject jbuffer, jobject jaccelerator)
482 {
483 fz_context *ctx = get_context(env);
484 fz_document *doc = NULL;
485 const char *magic = NULL;
486 fz_stream *docstream = NULL;
487 fz_stream *accstream = NULL;
488 fz_buffer *docbuf = NULL;
489 fz_buffer *accbuf = NULL;
490 jbyte *buffer = NULL;
491 jbyte *accelerator = NULL;
492 int n, m;
493
494 fz_var(docbuf);
495 fz_var(accbuf);
496 fz_var(docstream);
497 fz_var(accstream);
498
499 if (!ctx) return NULL;
500 if (jmagic)
501 {
502 magic = (*env)->GetStringUTFChars(env, jmagic, NULL);
503 if (!magic)
504 jni_throw_run(env, "cannot get characters in magic string");
505 }
506 if (jbuffer)
507 {
508 n = (*env)->GetArrayLength(env, jbuffer);
509
510 buffer = (*env)->GetByteArrayElements(env, jbuffer, NULL);
511 if (!buffer)
512 {
513 if (magic) (*env)->ReleaseStringUTFChars(env, jmagic, magic);
514 jni_throw_run(env, "cannot get document bytes to read");
515 }
516 }
517 if (jaccelerator)
518 {
519 m = (*env)->GetArrayLength(env, jaccelerator);
520
521 accelerator = (*env)->GetByteArrayElements(env, jaccelerator, NULL);
522 if (!accelerator)
523 {
524 if (buffer) (*env)->ReleaseByteArrayElements(env, jbuffer, buffer, 0);
525 if (magic) (*env)->ReleaseStringUTFChars(env, jmagic, magic);
526 jni_throw_run(env, "cannot get accelerator bytes to read");
527 }
528 }
529
530 fz_try(ctx)
531 {
532 if (buffer)
533 {
534 docbuf = fz_new_buffer(ctx, n);
535 fz_append_data(ctx, docbuf, buffer, n);
536 docstream = fz_open_buffer(ctx, docbuf);
537 }
538
539 if (accelerator)
540 {
541 accbuf = fz_new_buffer(ctx, m);
542 fz_append_data(ctx, accbuf, accelerator, m);
543 accstream = fz_open_buffer(ctx, accbuf);
544 }
545
546 doc = fz_open_accelerated_document_with_stream(ctx, magic, docstream, accstream);
547 }
548 fz_always(ctx)
549 {
550 fz_drop_stream(ctx, accstream);
551 fz_drop_buffer(ctx, accbuf);
552 fz_drop_stream(ctx, docstream);
553 fz_drop_buffer(ctx, docbuf);
554 if (accelerator) (*env)->ReleaseByteArrayElements(env, jaccelerator, accelerator, 0);
555 if (buffer) (*env)->ReleaseByteArrayElements(env, jbuffer, buffer, 0);
556 if (magic) (*env)->ReleaseStringUTFChars(env, jmagic, magic);
557 }
558 fz_catch(ctx)
559 {
560 jni_rethrow(env, ctx);
561 }
562
563 return to_Document_safe_own(ctx, env, doc);
564 }
565
566 JNIEXPORT jboolean JNICALL
567 FUN(Document_recognize)(JNIEnv *env, jclass cls, jstring jmagic)
568 {
569 fz_context *ctx = get_context(env);
570 const char *magic = NULL;
571 jboolean recognized = JNI_FALSE;
572
573 if (!ctx) return JNI_FALSE;
574 if (jmagic)
575 {
576 magic = (*env)->GetStringUTFChars(env, jmagic, NULL);
577 if (!magic) return JNI_FALSE;
578 }
579
580 fz_try(ctx)
581 recognized = fz_recognize_document(ctx, magic) != NULL;
582 fz_always(ctx)
583 if (magic) (*env)->ReleaseStringUTFChars(env, jmagic, magic);
584 fz_catch(ctx)
585 jni_rethrow(env, ctx);
586
587 return recognized;
588 }
589
590 JNIEXPORT jboolean JNICALL
591 FUN(Document_supportsAccelerator)(JNIEnv *env, jobject self)
592 {
593 fz_context *ctx = get_context(env);
594 fz_document *doc = from_Document(env, self);
595 jboolean support = JNI_FALSE;
596
597 fz_try(ctx)
598 support = fz_document_supports_accelerator(ctx, doc);
599 fz_catch(ctx)
600 jni_rethrow(env, ctx);
601
602 return support;
603 }
604
605 JNIEXPORT void JNICALL
606 FUN(Document_saveAccelerator)(JNIEnv *env, jobject self, jstring jfilename)
607 {
608 fz_context *ctx = get_context(env);
609 fz_document *doc = from_Document(env, self);
610 const char *filename = "null";
611
612 if (!ctx || !doc) return;
613 if (!jfilename) jni_throw_arg_void(env, "filename must not be null");
614
615 filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
616 if (!filename) return;
617
618 fz_try(ctx)
619 fz_save_accelerator(ctx, doc, filename);
620 fz_always(ctx)
621 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
622 fz_catch(ctx)
623 jni_rethrow_void(env, ctx);
624 }
625
626 JNIEXPORT void JNICALL
627 FUN(Document_outputAccelerator)(JNIEnv *env, jobject self, jobject jstream)
628 {
629 fz_context *ctx = get_context(env);
630 fz_document *doc = from_Document(env, self);
631 SeekableStreamState *state = NULL;
632 jobject stream = NULL;
633 jbyteArray array = NULL;
634 fz_output *out;
635
636 fz_var(state);
637 fz_var(out);
638 fz_var(stream);
639 fz_var(array);
640
641 stream = (*env)->NewGlobalRef(env, jstream);
642 if (!stream)
643 return;
644
645 array = (*env)->NewByteArray(env, sizeof state->buffer);
646 if (array)
647 array = (*env)->NewGlobalRef(env, array);
648 if (!array)
649 {
650 (*env)->DeleteGlobalRef(env, stream);
651 return;
652 }
653
654 fz_try(ctx)
655 {
656 state = Memento_label(fz_malloc(ctx, sizeof(SeekableStreamState)), "SeekableStreamState_outputAccelerator");
657 state->stream = stream;
658 state->array = array;
659
660 out = fz_new_output(ctx, 8192, state, SeekableOutputStream_write, NULL, SeekableOutputStream_drop);
661 out->seek = SeekableOutputStream_seek;
662 out->tell = SeekableOutputStream_tell;
663
664 /* these are now owned by 'out' */
665 state = NULL;
666 stream = NULL;
667 array = NULL;
668
669 fz_output_accelerator(ctx, doc, out);
670 fz_close_output(ctx, out);
671 }
672 fz_always(ctx)
673 {
674 fz_drop_output(ctx, out);
675 }
676 fz_catch(ctx)
677 {
678 (*env)->DeleteGlobalRef(env, stream);
679 (*env)->DeleteGlobalRef(env, array);
680 fz_free(ctx, state);
681 jni_rethrow_void(env, ctx);
682 }
683 }
684
685 JNIEXPORT jboolean JNICALL
686 FUN(Document_needsPassword)(JNIEnv *env, jobject self)
687 {
688 fz_context *ctx = get_context(env);
689 fz_document *doc = from_Document(env, self);
690 int okay = 0;
691
692 if (!ctx || !doc) return JNI_FALSE;
693
694 fz_try(ctx)
695 okay = fz_needs_password(ctx, doc);
696 fz_catch(ctx)
697 jni_rethrow(env, ctx);
698
699 return okay ? JNI_TRUE : JNI_FALSE;
700 }
701
702 JNIEXPORT jboolean JNICALL
703 FUN(Document_authenticatePassword)(JNIEnv *env, jobject self, jstring jpassword)
704 {
705 fz_context *ctx = get_context(env);
706 fz_document *doc = from_Document(env, self);
707 const char *password = NULL;
708 int okay = 0;
709
710 if (!ctx || !doc) return JNI_FALSE;
711
712 if (jpassword)
713 {
714 password = (*env)->GetStringUTFChars(env, jpassword, NULL);
715 if (!password) return JNI_FALSE;
716 }
717
718 fz_try(ctx)
719 okay = fz_authenticate_password(ctx, doc, password);
720 fz_always(ctx)
721 if (password) (*env)->ReleaseStringUTFChars(env, jpassword, password);
722 fz_catch(ctx)
723 jni_rethrow(env, ctx);
724
725 return okay ? JNI_TRUE : JNI_FALSE;
726 }
727
728 JNIEXPORT jint JNICALL
729 FUN(Document_countChapters)(JNIEnv *env, jobject self)
730 {
731 fz_context *ctx = get_context(env);
732 fz_document *doc = from_Document(env, self);
733 int count = 0;
734
735 if (!ctx || !doc) return 0;
736
737 fz_try(ctx)
738 count = fz_count_chapters(ctx, doc);
739 fz_catch(ctx)
740 jni_rethrow(env, ctx);
741
742 return count;
743 }
744
745 JNIEXPORT jint JNICALL
746 FUN(Document_countPages)(JNIEnv *env, jobject self, jint chapter)
747 {
748 fz_context *ctx = get_context(env);
749 fz_document *doc = from_Document(env, self);
750 int count = 0;
751
752 if (!ctx || !doc) return 0;
753
754 fz_try(ctx)
755 count = fz_count_chapter_pages(ctx, doc, chapter);
756 fz_catch(ctx)
757 jni_rethrow(env, ctx);
758
759 return count;
760 }
761
762 JNIEXPORT jboolean JNICALL
763 FUN(Document_isReflowable)(JNIEnv *env, jobject self)
764 {
765 fz_context *ctx = get_context(env);
766 fz_document *doc = from_Document(env, self);
767 int is_reflowable = 0;
768
769 if (!ctx || !doc) return JNI_FALSE;
770
771 fz_try(ctx)
772 is_reflowable = fz_is_document_reflowable(ctx, doc);
773 fz_catch(ctx)
774 jni_rethrow(env, ctx);
775
776 return is_reflowable ? JNI_TRUE : JNI_FALSE;
777 }
778
779 JNIEXPORT void JNICALL
780 FUN(Document_layout)(JNIEnv *env, jobject self, jfloat w, jfloat h, jfloat em)
781 {
782 fz_context *ctx = get_context(env);
783 fz_document *doc = from_Document(env, self);
784
785 if (!ctx || !doc) return;
786
787 fz_try(ctx)
788 fz_layout_document(ctx, doc, w, h, em);
789 fz_catch(ctx)
790 jni_rethrow_void(env, ctx);
791 }
792
793 JNIEXPORT jobject JNICALL
794 FUN(Document_loadPage)(JNIEnv *env, jobject self, jint chapter, jint number)
795 {
796 fz_context *ctx = get_context(env);
797 fz_document *doc = from_Document(env, self);
798 fz_page *page = NULL;
799
800 if (!ctx || !doc) return NULL;
801
802 fz_try(ctx)
803 page = fz_load_chapter_page(ctx, doc, chapter, number);
804 fz_catch(ctx)
805 jni_rethrow(env, ctx);
806
807 return to_Page_safe_own(ctx, env, page);
808 }
809
810 JNIEXPORT jstring JNICALL
811 FUN(Document_getMetaData)(JNIEnv *env, jobject self, jstring jkey)
812 {
813 fz_context *ctx = get_context(env);
814 fz_document *doc = from_Document(env, self);
815 const char *key = NULL;
816 char info[256];
817
818 if (!ctx || !doc) return NULL;
819 if (!jkey) jni_throw_arg(env, "key must not be null");
820
821 key = (*env)->GetStringUTFChars(env, jkey, NULL);
822 if (!key) return 0;
823
824 fz_try(ctx)
825 fz_lookup_metadata(ctx, doc, key, info, sizeof info);
826 fz_always(ctx)
827 if (key)
828 (*env)->ReleaseStringUTFChars(env, jkey, key);
829 fz_catch(ctx)
830 jni_rethrow(env, ctx);
831
832 return (*env)->NewStringUTF(env, info);
833 }
834
835 JNIEXPORT void JNICALL
836 FUN(Document_setMetaData)(JNIEnv *env, jobject self, jstring jkey, jstring jvalue)
837 {
838 fz_context *ctx = get_context(env);
839 fz_document *doc = from_Document(env, self);
840 const char *key = NULL;
841 const char *value = NULL;
842
843 if (!ctx || !doc) return;
844 if (!jkey) jni_throw_arg_void(env, "key must not be null");
845 if (!jvalue) jni_throw_arg_void(env, "value must not be null");
846
847 key = (*env)->GetStringUTFChars(env, jkey, NULL);
848 value = (*env)->GetStringUTFChars(env, jvalue, NULL);
849 if (!key || !value)
850 {
851 if (key)
852 (*env)->ReleaseStringUTFChars(env, jkey, key);
853 return;
854 }
855
856 fz_try(ctx)
857 fz_set_metadata(ctx, doc, key, value);
858 fz_always(ctx)
859 {
860 (*env)->ReleaseStringUTFChars(env, jkey, key);
861 (*env)->ReleaseStringUTFChars(env, jvalue, value);
862 }
863 fz_catch(ctx)
864 jni_rethrow_void(env, ctx);
865 }
866
867 JNIEXPORT jboolean JNICALL
868 FUN(Document_isUnencryptedPDF)(JNIEnv *env, jobject self)
869 {
870 fz_context *ctx = get_context(env);
871 fz_document *doc = from_Document(env, self);
872 pdf_document *idoc = pdf_specifics(ctx, doc);
873 int cryptVer;
874
875 if (!ctx || !doc) return JNI_FALSE;
876 if (!idoc)
877 return JNI_FALSE;
878
879 cryptVer = pdf_crypt_version(ctx, idoc->crypt);
880 return (cryptVer == 0) ? JNI_TRUE : JNI_FALSE;
881 }
882
883 JNIEXPORT jobject JNICALL
884 FUN(Document_loadOutline)(JNIEnv *env, jobject self)
885 {
886 fz_context *ctx = get_context(env);
887 fz_document *doc = from_Document(env, self);
888 fz_outline *outline = NULL;
889 jobject joutline = NULL;
890
891 if (!ctx || !doc) return NULL;
892
893 fz_var(outline);
894
895 fz_try(ctx)
896 {
897 outline = fz_load_outline(ctx, doc);
898 if (outline)
899 {
900 joutline = to_Outline_safe(ctx, env, doc, outline);
901 if (!joutline && !(*env)->ExceptionCheck(env))
902 fz_throw(ctx, FZ_ERROR_GENERIC, "loadOutline failed");
903 }
904 }
905 fz_always(ctx)
906 fz_drop_outline(ctx, outline);
907 fz_catch(ctx)
908 jni_rethrow(env, ctx);
909
910 if ((*env)->ExceptionCheck(env))
911 return NULL;
912
913 return joutline;
914 }
915
916 JNIEXPORT jobject JNICALL
917 FUN(Document_outlineIterator)(JNIEnv *env, jobject self)
918 {
919 fz_context *ctx = get_context(env);
920 fz_document *doc = from_Document(env, self);
921 fz_outline_iterator *iterator = NULL;
922 jobject jiterator = NULL;
923
924 if (!ctx || !doc) return NULL;
925
926 fz_var(iterator);
927
928 fz_try(ctx)
929 {
930 iterator = fz_new_outline_iterator(ctx, doc);
931 if (iterator)
932 {
933 jiterator = to_OutlineIterator_safe(ctx, env, iterator);
934 if (!jiterator || (*env)->ExceptionCheck(env))
935 fz_throw(ctx, FZ_ERROR_GENERIC, "outlineIterator failed");
936 iterator = NULL;
937 }
938 }
939 fz_always(ctx)
940 fz_drop_outline_iterator(ctx, iterator);
941 fz_catch(ctx)
942 jni_rethrow(env, ctx);
943
944 if ((*env)->ExceptionCheck(env))
945 return NULL;
946
947 return jiterator;
948 }
949
950 JNIEXPORT jlong JNICALL
951 FUN(Document_makeBookmark)(JNIEnv *env, jobject self, jint chapter, jint page)
952 {
953 fz_context *ctx = get_context(env);
954 fz_document *doc = from_Document(env, self);
955 fz_bookmark mark = 0;
956
957 fz_try(ctx)
958 mark = fz_make_bookmark(ctx, doc, fz_make_location(chapter, page));
959 fz_catch(ctx)
960 jni_rethrow(env, ctx);
961
962 return mark;
963 }
964
965 JNIEXPORT jobject JNICALL
966 FUN(Document_findBookmark)(JNIEnv *env, jobject self, jlong mark)
967 {
968 fz_context *ctx = get_context(env);
969 fz_document *doc = from_Document(env, self);
970 fz_location loc = { -1, -1 };
971
972 fz_try(ctx)
973 loc = fz_lookup_bookmark(ctx, doc, mark);
974 fz_catch(ctx)
975 jni_rethrow(env, ctx);
976
977 return (*env)->NewObject(env, cls_Location, mid_Location_init, loc.chapter, loc.page, 0, 0);
978 }
979
980 JNIEXPORT jobject JNICALL
981 FUN(Document_resolveLink)(JNIEnv *env, jobject self, jstring juri)
982 {
983 fz_context *ctx = get_context(env);
984 fz_document *doc = from_Document(env, self);
985 fz_location loc = { -1, -1 };
986 float x = 0, y = 0;
987 const char *uri = "";
988
989 if (juri)
990 {
991 uri = (*env)->GetStringUTFChars(env, juri, NULL);
992 if (!uri)
993 return NULL;
994 }
995
996 fz_try(ctx)
997 loc = fz_resolve_link(ctx, doc, uri, &x, &y);
998 fz_always(ctx)
999 if (juri)
1000 (*env)->ReleaseStringUTFChars(env, juri, uri);
1001 fz_catch(ctx)
1002 jni_rethrow(env, ctx);
1003
1004 return (*env)->NewObject(env, cls_Location, mid_Location_init, loc.chapter, loc.page, x, y);
1005 }
1006
1007 JNIEXPORT jboolean JNICALL
1008 FUN(Document_hasPermission)(JNIEnv *env, jobject self, jint permission)
1009 {
1010 fz_context *ctx = get_context(env);
1011 fz_document *doc = from_Document(env, self);
1012 jboolean result = JNI_FALSE;
1013
1014 fz_try(ctx)
1015 result = fz_has_permission(ctx, doc, permission);
1016 fz_catch(ctx)
1017 jni_rethrow(env, ctx);
1018
1019 return result;
1020 }
1021
1022 JNIEXPORT jobject JNICALL
1023 FUN(Document_search)(JNIEnv *env, jobject self, jint chapter, jint page, jstring jneedle)
1024 {
1025 fz_context *ctx = get_context(env);
1026 fz_document *doc = from_Document(env, self);
1027 const char *needle = NULL;
1028 search_state state = { env, NULL, 0 };
1029
1030 if (!ctx || !doc) return NULL;
1031 if (!jneedle) jni_throw_arg(env, "needle must not be null");
1032
1033 needle = (*env)->GetStringUTFChars(env, jneedle, NULL);
1034 if (!needle) return NULL;
1035
1036 state.hits = (*env)->NewObject(env, cls_ArrayList, mid_ArrayList_init);
1037 if (!state.hits || (*env)->ExceptionCheck(env)) return NULL;
1038
1039 fz_try(ctx)
1040 fz_search_chapter_page_number_cb(ctx, doc, chapter, page, needle, hit_callback, &state);
1041 fz_always(ctx)
1042 {
1043 (*env)->ReleaseStringUTFChars(env, jneedle, needle);
1044 }
1045 fz_catch(ctx)
1046 jni_rethrow(env, ctx);
1047
1048 if (state.error)
1049 return NULL;
1050
1051 return (*env)->CallObjectMethod(env, state.hits, mid_ArrayList_toArray);
1052 }
1053
1054 JNIEXPORT jobject JNICALL
1055 FUN(Document_resolveLinkDestination)(JNIEnv *env, jobject self, jstring juri)
1056 {
1057 fz_context *ctx = get_context(env);
1058 fz_document *doc = from_Document(env, self);
1059 const char *uri = "";
1060 fz_link_dest dest;
1061 jobject jdestination;
1062
1063 if (!ctx || !doc) return NULL;
1064
1065 if (juri)
1066 {
1067 uri = (*env)->GetStringUTFChars(env, juri, NULL);
1068 if (!uri)
1069 return NULL;
1070 }
1071
1072 fz_try(ctx)
1073 dest = fz_resolve_link_dest(ctx, doc, uri);
1074 fz_always(ctx)
1075 if (juri)
1076 (*env)->ReleaseStringUTFChars(env, juri, uri);
1077 fz_catch(ctx)
1078 jni_rethrow(env, ctx);
1079
1080 jdestination = (*env)->NewObject(env, cls_LinkDestination, mid_LinkDestination_init,
1081 dest.loc.chapter, dest.loc.page, dest.type, dest.x, dest.y, dest.w, dest.h, dest.zoom);
1082 if (!jdestination || (*env)->ExceptionCheck(env))
1083 return NULL;
1084
1085 return jdestination;
1086 }
1087
1088 JNIEXPORT jstring JNICALL
1089 FUN(Document_formatLinkURI)(JNIEnv *env, jobject self, jobject jdest)
1090 {
1091 fz_context *ctx = get_context(env);
1092 fz_document *doc = from_Document(env, self);
1093 fz_link_dest dest = from_LinkDestination(env, jdest);
1094 char *uri = NULL;
1095 jobject juri;
1096
1097 fz_try(ctx)
1098 uri = fz_format_link_uri(ctx, doc, dest);
1099 fz_catch(ctx)
1100 jni_rethrow(env, ctx);
1101
1102 juri = (*env)->NewStringUTF(env, uri);
1103 fz_free(ctx, uri);
1104 if (juri == NULL || (*env)->ExceptionCheck(env))
1105 return NULL;
1106
1107 return juri;
1108 }
1109
1110 JNIEXPORT jobject JNICALL
1111 FUN(Document_asPDF)(JNIEnv *env, jobject self)
1112 {
1113 fz_context *ctx = get_context(env);
1114 fz_document *doc = from_Document(env, self);
1115 pdf_document *pdf;
1116
1117 fz_try(ctx)
1118 pdf = fz_new_pdf_document_from_fz_document(ctx, doc);
1119 fz_catch(ctx)
1120 jni_rethrow(env, ctx);
1121
1122 if (!pdf)
1123 return NULL;
1124
1125 return to_PDFDocument_safe_own(ctx, env, pdf);
1126 }