documentwriter.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  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. /* DocumentWriter interface */
  23. JNIEXPORT void JNICALL
  24. FUN(DocumentWriter_finalize)(JNIEnv *env, jobject self)
  25. {
  26. fz_context *ctx = get_context(env);
  27. fz_document_writer *wri = from_DocumentWriter_safe(env, self);
  28. jobject ref;
  29. if (!ctx || !wri) return;
  30. ref = (jobject)(*env)->GetLongField(env, self, fid_DocumentWriter_ocrlistener);
  31. if (ref)
  32. {
  33. (*env)->DeleteGlobalRef(env, ref);
  34. (*env)->SetLongField(env, self, fid_DocumentWriter_ocrlistener, 0);
  35. }
  36. (*env)->SetLongField(env, self, fid_DocumentWriter_pointer, 0);
  37. fz_drop_document_writer(ctx, wri);
  38. }
  39. JNIEXPORT jlong JNICALL
  40. FUN(DocumentWriter_newNativeDocumentWriter)(JNIEnv *env, jclass cls, jstring jfilename, jstring jformat, jstring joptions)
  41. {
  42. fz_context *ctx = get_context(env);
  43. fz_document_writer *wri = NULL;
  44. const char *filename = NULL;
  45. const char *format = NULL;
  46. const char *options = NULL;
  47. if (!ctx) return 0;
  48. if (!jfilename) jni_throw_arg(env, "filename must not be null");
  49. filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
  50. if (!filename) return 0;
  51. if (jformat)
  52. {
  53. format = (*env)->GetStringUTFChars(env, jformat, NULL);
  54. if (!format)
  55. {
  56. (*env)->ReleaseStringUTFChars(env, jfilename, filename);
  57. return 0;
  58. }
  59. }
  60. if (joptions)
  61. {
  62. options = (*env)->GetStringUTFChars(env, joptions, NULL);
  63. if (!options)
  64. {
  65. if (format)
  66. (*env)->ReleaseStringUTFChars(env, jformat, format);
  67. (*env)->ReleaseStringUTFChars(env, jfilename, filename);
  68. return 0;
  69. }
  70. }
  71. fz_try(ctx)
  72. wri = fz_new_document_writer(ctx, filename, format, options);
  73. fz_always(ctx)
  74. {
  75. if (options)
  76. (*env)->ReleaseStringUTFChars(env, joptions, options);
  77. if (format)
  78. (*env)->ReleaseStringUTFChars(env, jformat, format);
  79. (*env)->ReleaseStringUTFChars(env, jfilename, filename);
  80. }
  81. fz_catch(ctx)
  82. jni_rethrow(env, ctx);
  83. return jlong_cast(wri);
  84. }
  85. JNIEXPORT jlong JNICALL
  86. FUN(DocumentWriter_newNativeDocumentWriterWithSeekableOutputStream)(JNIEnv *env, jclass cls, jobject jstream, jstring jformat, jstring joptions)
  87. {
  88. fz_context *ctx = get_context(env);
  89. fz_document_writer *wri = NULL;
  90. SeekableStreamState *state = NULL;
  91. jobject stream = NULL;
  92. const char *format = NULL;
  93. const char *options = NULL;
  94. jbyteArray array = NULL;
  95. fz_output *out;
  96. if (!ctx) return 0;
  97. if (!jstream) jni_throw_arg(env, "output stream must not be null");
  98. stream = (*env)->NewGlobalRef(env, jstream);
  99. if (!stream)
  100. return 0;
  101. array = (*env)->NewByteArray(env, sizeof state->buffer);
  102. if (array)
  103. array = (*env)->NewGlobalRef(env, array);
  104. if (!array)
  105. {
  106. (*env)->DeleteGlobalRef(env, stream);
  107. return 0;
  108. }
  109. if (jformat)
  110. {
  111. format = (*env)->GetStringUTFChars(env, jformat, NULL);
  112. if (!format)
  113. return 0;
  114. }
  115. if (joptions)
  116. {
  117. options = (*env)->GetStringUTFChars(env, joptions, NULL);
  118. if (!options)
  119. {
  120. if (format)
  121. (*env)->ReleaseStringUTFChars(env, jformat, format);
  122. return 0;
  123. }
  124. }
  125. fz_var(state);
  126. fz_var(out);
  127. fz_var(stream);
  128. fz_var(array);
  129. fz_try(ctx)
  130. {
  131. fz_output *out_temp;
  132. state = Memento_label(fz_malloc(ctx, sizeof(SeekableStreamState)), "SeekableStreamState_newNativeDocumentWriterWithSeekableOutputStream");
  133. state->stream = stream;
  134. state->array = array;
  135. out = fz_new_output(ctx, 8192, state, SeekableOutputStream_write, NULL, SeekableOutputStream_drop);
  136. out->seek = SeekableOutputStream_seek;
  137. out->tell = SeekableOutputStream_tell;
  138. /* these are now owned by 'out' */
  139. state = NULL;
  140. stream = NULL;
  141. array = NULL;
  142. /* out becomes owned by 'wri' as soon as we call, even if it throws. */
  143. out_temp = out;
  144. out = NULL;
  145. wri = fz_new_document_writer_with_output(ctx, out_temp, format, options);
  146. }
  147. fz_always(ctx)
  148. {
  149. fz_drop_output(ctx, out);
  150. if (options)
  151. (*env)->ReleaseStringUTFChars(env, joptions, options);
  152. if (format)
  153. (*env)->ReleaseStringUTFChars(env, jformat, format);
  154. }
  155. fz_catch(ctx)
  156. jni_rethrow(env, ctx);
  157. return jlong_cast(wri);
  158. }
  159. JNIEXPORT jlong JNICALL
  160. FUN(DocumentWriter_newNativeDocumentWriterWithBuffer)(JNIEnv *env, jclass cls, jobject jbuffer, jstring jformat, jstring joptions)
  161. {
  162. fz_context *ctx = get_context(env);
  163. fz_document_writer *wri = NULL;
  164. fz_buffer *buffer = from_Buffer_safe(env, jbuffer);
  165. const char *format = NULL;
  166. const char *options = NULL;
  167. if (!ctx) return 0;
  168. if (!buffer) jni_throw_arg(env, "output buffer must not be null");
  169. if (jformat)
  170. {
  171. format = (*env)->GetStringUTFChars(env, jformat, NULL);
  172. if (!format)
  173. return 0;
  174. }
  175. if (joptions)
  176. {
  177. options = (*env)->GetStringUTFChars(env, joptions, NULL);
  178. if (!options)
  179. {
  180. if (format)
  181. (*env)->ReleaseStringUTFChars(env, jformat, format);
  182. return 0;
  183. }
  184. }
  185. fz_try(ctx)
  186. wri = fz_new_document_writer_with_buffer(ctx, buffer, format, options);
  187. fz_always(ctx)
  188. {
  189. if (options)
  190. (*env)->ReleaseStringUTFChars(env, joptions, options);
  191. if (format)
  192. (*env)->ReleaseStringUTFChars(env, jformat, format);
  193. }
  194. fz_catch(ctx)
  195. jni_rethrow(env, ctx);
  196. return jlong_cast(wri);
  197. }
  198. JNIEXPORT jobject JNICALL
  199. FUN(DocumentWriter_beginPage)(JNIEnv *env, jobject self, jobject jmediabox)
  200. {
  201. fz_context *ctx = get_context(env);
  202. fz_document_writer *wri = from_DocumentWriter(env, self);
  203. fz_rect mediabox = from_Rect(env, jmediabox);
  204. fz_device *device = NULL;
  205. if (!ctx || !wri) return NULL;
  206. fz_try(ctx)
  207. device = fz_begin_page(ctx, wri, mediabox);
  208. fz_catch(ctx)
  209. jni_rethrow(env, ctx);
  210. return to_NativeDevice_safe_own(ctx, env, fz_keep_device(ctx, device));
  211. }
  212. JNIEXPORT void JNICALL
  213. FUN(DocumentWriter_endPage)(JNIEnv *env, jobject self)
  214. {
  215. fz_context *ctx = get_context(env);
  216. fz_document_writer *wri = from_DocumentWriter(env, self);
  217. if (!ctx || !wri) return;
  218. fz_try(ctx)
  219. fz_end_page(ctx, wri);
  220. fz_catch(ctx)
  221. jni_rethrow_void(env, ctx);
  222. }
  223. JNIEXPORT void JNICALL
  224. FUN(DocumentWriter_close)(JNIEnv *env, jobject self)
  225. {
  226. fz_context *ctx = get_context(env);
  227. fz_document_writer *wri = from_DocumentWriter(env, self);
  228. if (!ctx || !wri) return;
  229. fz_try(ctx)
  230. fz_close_document_writer(ctx, wri);
  231. fz_catch(ctx)
  232. jni_rethrow_void(env, ctx);
  233. }
  234. static int
  235. jni_ocr_progress(fz_context *ctx, void *arg, int page, int percent)
  236. {
  237. jobject ref = (jobject)arg;
  238. jboolean cancel;
  239. JNIEnv *env = NULL;
  240. jboolean detach = JNI_FALSE;
  241. if (ref == NULL)
  242. return JNI_FALSE;
  243. env = jni_attach_thread(&detach);
  244. if (env == NULL)
  245. fz_throw(ctx, FZ_ERROR_GENERIC, "cannot attach to JVM in jni_ocr_progress");
  246. cancel = (*env)->CallBooleanMethod(env, ref, mid_DocumentWriter_OCRListener_progress, page, percent);
  247. if ((*env)->ExceptionCheck(env))
  248. cancel = 1;
  249. jni_detach_thread(detach);
  250. return !!cancel;
  251. }
  252. JNIEXPORT void JNICALL
  253. FUN(DocumentWriter_addOCRListener)(JNIEnv *env, jobject self, jobject jlistener)
  254. {
  255. fz_context *ctx = get_context(env);
  256. fz_document_writer *wri = from_DocumentWriter(env, self);
  257. jobject ref;
  258. if (!ctx || !wri) return;
  259. /* Delete any old OCRListener if there is one. */
  260. ref = (jobject)(*env)->GetLongField(env, self, fid_DocumentWriter_ocrlistener);
  261. if (ref != NULL)
  262. {
  263. (*env)->DeleteGlobalRef(env, ref);
  264. (*env)->SetLongField(env, self, fid_DocumentWriter_ocrlistener, 0);
  265. }
  266. /* Take a ref and store it for the callback to use */
  267. ref = (*env)->NewGlobalRef(env, jlistener);
  268. if (!ref)
  269. jni_throw_run_void(env, "cannot take reference to listener");
  270. (*env)->SetLongField(env, self, fid_DocumentWriter_ocrlistener, jlong_cast(ref));
  271. fz_try(ctx)
  272. fz_pdfocr_writer_set_progress(ctx, wri, jni_ocr_progress, ref);
  273. fz_catch(ctx)
  274. {
  275. (*env)->DeleteGlobalRef(env, ref);
  276. (*env)->SetLongField(env, self, fid_DocumentWriter_ocrlistener, 0);
  277. jni_rethrow_void(env, ctx);
  278. }
  279. }