document.c 28 KB

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