Mercurial > hgrepos > Python2 > PyMuPDF
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 } |
