comparison mupdf-source/platform/java/jni/context.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 /* Context interface */
24
25 /* Put the fz_context in thread-local storage */
26
27 #ifdef _WIN32
28 static CRITICAL_SECTION mutexes[FZ_LOCK_MAX];
29 #else
30 static pthread_mutex_t mutexes[FZ_LOCK_MAX];
31 #endif
32
33 static void lock(void *user, int lock)
34 {
35 #ifdef _WIN32
36 EnterCriticalSection(&mutexes[lock]);
37 #else
38 (void)pthread_mutex_lock(&mutexes[lock]);
39 #endif
40 }
41
42 static void unlock(void *user, int lock)
43 {
44 #ifdef _WIN32
45 LeaveCriticalSection(&mutexes[lock]);
46 #else
47 (void)pthread_mutex_unlock(&mutexes[lock]);
48 #endif
49 }
50
51
52 static const fz_locks_context locks =
53 {
54 NULL, /* user */
55 lock,
56 unlock
57 };
58
59 static void fin_base_context(JNIEnv *env)
60 {
61 int i;
62
63 fz_drop_context(base_context);
64 base_context = NULL;
65
66 for (i = 0; i < FZ_LOCK_MAX; i++)
67 #ifdef _WIN32
68 DeleteCriticalSection(&mutexes[i]);
69 #else
70 (void)pthread_mutex_destroy(&mutexes[i]);
71 #endif
72 }
73
74 #ifndef _WIN32
75 static void drop_tls_context(void *arg)
76 {
77 fz_context *ctx = (fz_context *)arg;
78
79 fz_drop_context(ctx);
80 }
81 #endif
82
83 static void log_callback(void *user, const char *message)
84 {
85 jboolean detach = JNI_FALSE;
86 JNIEnv *env = NULL;
87 jobject jcallback;
88 jstring jmessage;
89 jobject jlock;
90 jmethodID mid;
91
92 env = jni_attach_thread(&detach);
93 if (env == NULL)
94 return;
95
96 if (user != NULL)
97 mid = mid_Context_Log_error;
98 else
99 mid = mid_Context_Log_warning;
100
101 jcallback = (*env)->GetStaticObjectField(env, cls_Context, fid_Context_log);
102 if (jcallback)
103 {
104 jlock = (*env)->GetStaticObjectField(env, cls_Context, fid_Context_lock);
105 (*env)->MonitorEnter(env, jlock);
106 jmessage = (*env)->NewStringUTF(env, message);
107 (*env)->CallVoidMethod(env, jcallback, mid, jmessage);
108 (*env)->DeleteLocalRef(env, jmessage);
109 (*env)->MonitorExit(env, jlock);
110 (*env)->DeleteLocalRef(env, jcallback);
111 (*env)->DeleteLocalRef(env, jlock);
112 }
113
114 jni_detach_thread(detach);
115 }
116
117 static int init_base_context(JNIEnv *env)
118 {
119 int i;
120 #ifdef FZ_JAVA_STORE_SIZE
121 size_t fz_store_size = FZ_JAVA_STORE_SIZE;
122 #else
123 size_t fz_store_size = FZ_STORE_DEFAULT;
124 #endif
125 char *env_fz_store_size;
126
127 #ifdef _WIN32
128 /* No destructor on windows. We will leak contexts.
129 * There is no easy way around this, but this page:
130 * http://stackoverflow.com/questions/3241732/is-there-anyway-to-dynamically-free-thread-local-storage-in-the-win32-apis/3245082#3245082
131 * suggests a workaround that we can implement if we
132 * need to. */
133 context_key = TlsAlloc();
134 if (context_key == TLS_OUT_OF_INDEXES)
135 {
136 LOGE("cannot get thread local storage for storing base context");
137 return -1;
138 }
139 #else
140 int ret = pthread_key_create(&context_key, drop_tls_context);
141 if (ret < 0)
142 {
143 LOGE("cannot get thread local storage for storing base context");
144 return -1;
145 }
146 #endif
147
148 for (i = 0; i < FZ_LOCK_MAX; i++)
149 #ifdef _WIN32
150 InitializeCriticalSection(&mutexes[i]);
151 #else
152 (void)pthread_mutex_init(&mutexes[i], NULL);
153 #endif
154
155 env_fz_store_size = getenv("FZ_JAVA_STORE_SIZE");
156 if (env_fz_store_size)
157 fz_store_size = fz_atoz(env_fz_store_size);
158 base_context = fz_new_context(NULL, &locks, fz_store_size);
159
160 if (!base_context)
161 {
162 LOGE("cannot create base context");
163 fin_base_context(env);
164 return -1;
165 }
166
167 fz_set_error_callback(base_context, log_callback, (void *) 1);
168 fz_set_warning_callback(base_context, log_callback, (void *) 0);
169
170 fz_try(base_context)
171 fz_register_document_handlers(base_context);
172 fz_catch(base_context)
173 {
174 fz_report_error(base_context);
175 LOGE("cannot register document handlers");
176 fin_base_context(env);
177 return -1;
178 }
179
180 #ifdef HAVE_ANDROID
181 fz_install_load_system_font_funcs(base_context,
182 load_droid_font,
183 load_droid_cjk_font,
184 load_droid_fallback_font);
185 #endif
186
187 return 0;
188 }
189
190 static fz_context *get_context(JNIEnv *env)
191 {
192 fz_context *ctx = (fz_context *)
193 #ifdef _WIN32
194 TlsGetValue(context_key);
195 if (ctx == NULL && GetLastError() != ERROR_SUCCESS) jni_throw_run(env, "cannot get context");
196 #else
197 pthread_getspecific(context_key);
198 #endif
199
200 if (ctx)
201 return ctx;
202
203 ctx = fz_clone_context(base_context);
204 if (!ctx) jni_throw_oom(env, "failed to clone fz_context");
205
206 #ifdef _WIN32
207 if (TlsSetValue(context_key, ctx) == 0) jni_throw_run(env, "cannot store context");
208 #else
209 if (pthread_setspecific(context_key, ctx) != 0) jni_throw_run(env, "cannot store context");
210 #endif
211 return ctx;
212 }
213
214
215 JNIEXPORT jint JNICALL
216 FUN(Context_initNative)(JNIEnv *env, jclass cls)
217 {
218 if (!check_enums())
219 return -1;
220
221 /* Must init the context before find_finds, because the act of
222 * finding the fids can cause classes to load. This causes
223 * statics to be setup, which can in turn call JNI code, which
224 * requires the context. (For example see ColorSpace) */
225 if (init_base_context(env) < 0)
226 return -1;
227
228 if (find_fids(env) < 0)
229 {
230 fin_base_context(env);
231 return -1;
232 }
233
234 return 0;
235 }
236
237 JNIEXPORT void JNICALL
238 FUN(Context_emptyStore)(JNIEnv *env, jclass cls)
239 {
240 fz_context *ctx = get_context(env);
241 if (!ctx) return;
242
243 fz_empty_store(ctx);
244 }
245
246 JNIEXPORT void JNICALL
247 FUN(Context_enableICC)(JNIEnv *env, jclass cls)
248 {
249 fz_context *ctx = get_context(env);
250 if (!ctx) return;
251
252 fz_enable_icc(ctx);
253 }
254
255 JNIEXPORT void JNICALL
256 FUN(Context_disableICC)(JNIEnv *env, jclass cls)
257 {
258 fz_context *ctx = get_context(env);
259 if (!ctx) return;
260
261 fz_disable_icc(ctx);
262 }
263
264 JNIEXPORT void JNICALL
265 FUN(Context_setAntiAliasLevel)(JNIEnv *env, jclass cls, jint level)
266 {
267 fz_context *ctx = get_context(env);
268 if (!ctx) return;
269
270 fz_set_aa_level(ctx, level);
271 }
272
273 JNIEXPORT jobject JNICALL
274 FUN(Context_getVersion)(JNIEnv *env, jclass cls)
275 {
276 fz_context *ctx = get_context(env);
277 jobject jversion = NULL;
278 jobject jvs = NULL;
279
280 if (!ctx) return NULL;
281
282 jvs = (*env)->NewStringUTF(env, FZ_VERSION);
283 if (!jvs || (*env)->ExceptionCheck(env))
284 return NULL;
285
286 jversion = (*env)->NewObject(env, cls_Context_Version, mid_Context_Version_init);
287 if (!jversion || (*env)->ExceptionCheck(env))
288 return NULL;
289
290 (*env)->SetIntField(env, jversion, fid_Context_Version_major, FZ_VERSION_MAJOR);
291 (*env)->SetIntField(env, jversion, fid_Context_Version_minor, FZ_VERSION_MINOR);
292 (*env)->SetIntField(env, jversion, fid_Context_Version_patch, FZ_VERSION_PATCH);
293 (*env)->SetObjectField(env, jversion, fid_Context_Version_version, jvs);
294
295 return jversion;
296 }
297
298 JNIEXPORT void JNICALL
299 FUN(Context_setUserCSS)(JNIEnv *env, jclass cls, jstring jcss)
300 {
301 fz_context *ctx = get_context(env);
302 const char *css = NULL;
303
304 if (jcss)
305 css = (*env)->GetStringUTFChars(env, jcss, NULL);
306
307 fz_try(ctx)
308 fz_set_user_css(ctx, css);
309 fz_always(ctx)
310 if (jcss)
311 (*env)->ReleaseStringUTFChars(env, jcss, css);
312 fz_catch(ctx)
313 jni_rethrow_void(env, ctx);
314 }
315
316 JNIEXPORT void JNICALL
317 FUN(Context_useDocumentCSS)(JNIEnv *env, jclass cls, jboolean state)
318 {
319 fz_context *ctx = get_context(env);
320
321 fz_try(ctx)
322 fz_set_use_document_css(ctx, state);
323 fz_catch(ctx)
324 jni_rethrow_void(env, ctx);
325 }
326
327 JNIEXPORT jboolean JNICALL
328 FUN(Context_shrinkStore)(JNIEnv *env, jclass cls, jint percent)
329 {
330 fz_context *ctx = get_context(env);
331 int success = 0;
332
333 if (!ctx) return JNI_FALSE;
334 if (percent < 0) jni_throw_arg(env, "percent must not be negative");
335 if (percent > 100) jni_throw_arg(env, "percent must not be more than 100");
336
337 fz_try(ctx)
338 success = fz_shrink_store(ctx, percent);
339 fz_catch(ctx)
340 jni_rethrow(env, ctx);
341
342 return success != 0;
343 }