/src/mupdf/source/fitz/context.c
Line | Count | Source (jump to first uncovered line) |
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 | | #include "mupdf/fitz.h" |
24 | | |
25 | | #include "context-imp.h" |
26 | | |
27 | | #include <assert.h> |
28 | | #include <string.h> |
29 | | #include <stdio.h> |
30 | | #include <time.h> |
31 | | |
32 | | struct fz_style_context |
33 | | { |
34 | | int refs; |
35 | | char *user_css; |
36 | | int use_document_css; |
37 | | }; |
38 | | |
39 | | static void fz_new_style_context(fz_context *ctx) |
40 | 13.4k | { |
41 | 13.4k | if (ctx) |
42 | 13.4k | { |
43 | 13.4k | ctx->style = fz_malloc_struct(ctx, fz_style_context); |
44 | 13.4k | ctx->style->refs = 1; |
45 | 13.4k | ctx->style->user_css = NULL; |
46 | 13.4k | ctx->style->use_document_css = 1; |
47 | 13.4k | } |
48 | 13.4k | } |
49 | | |
50 | | static fz_style_context *fz_keep_style_context(fz_context *ctx) |
51 | 0 | { |
52 | 0 | if (!ctx) |
53 | 0 | return NULL; |
54 | 0 | return fz_keep_imp(ctx, ctx->style, &ctx->style->refs); |
55 | 0 | } |
56 | | |
57 | | static void fz_drop_style_context(fz_context *ctx) |
58 | 13.4k | { |
59 | 13.4k | if (!ctx) |
60 | 0 | return; |
61 | 13.4k | if (fz_drop_imp(ctx, ctx->style, &ctx->style->refs)) |
62 | 13.4k | { |
63 | 13.4k | fz_free(ctx, ctx->style->user_css); |
64 | 13.4k | fz_free(ctx, ctx->style); |
65 | 13.4k | } |
66 | 13.4k | } |
67 | | |
68 | | void fz_set_use_document_css(fz_context *ctx, int use) |
69 | 0 | { |
70 | 0 | ctx->style->use_document_css = use; |
71 | 0 | } |
72 | | |
73 | | int fz_use_document_css(fz_context *ctx) |
74 | 1.30k | { |
75 | 1.30k | return ctx->style->use_document_css; |
76 | 1.30k | } |
77 | | |
78 | | void fz_set_user_css(fz_context *ctx, const char *user_css) |
79 | 0 | { |
80 | 0 | fz_free(ctx, ctx->style->user_css); |
81 | 0 | ctx->style->user_css = user_css ? fz_strdup(ctx, user_css) : NULL; |
82 | 0 | } |
83 | | |
84 | | const char *fz_user_css(fz_context *ctx) |
85 | 0 | { |
86 | 0 | return ctx->style->user_css; |
87 | 0 | } |
88 | | |
89 | | void fz_load_user_css(fz_context *ctx, const char *filename) |
90 | 0 | { |
91 | 0 | fz_buffer *buf = NULL; |
92 | 0 | fz_var(buf); |
93 | 0 | fz_try(ctx) |
94 | 0 | { |
95 | 0 | buf = fz_read_file(ctx, filename); |
96 | 0 | fz_set_user_css(ctx, fz_string_from_buffer(ctx, buf)); |
97 | 0 | } |
98 | 0 | fz_always(ctx) |
99 | 0 | { |
100 | 0 | fz_drop_buffer(ctx, buf); |
101 | 0 | } |
102 | 0 | fz_catch(ctx) |
103 | 0 | { |
104 | 0 | fz_report_error(ctx); |
105 | 0 | fz_warn(ctx, "cannot read user css file"); |
106 | 0 | } |
107 | 0 | } |
108 | | |
109 | | static void fz_new_tuning_context(fz_context *ctx) |
110 | 13.4k | { |
111 | 13.4k | if (ctx) |
112 | 13.4k | { |
113 | 13.4k | ctx->tuning = fz_malloc_struct(ctx, fz_tuning_context); |
114 | 13.4k | ctx->tuning->refs = 1; |
115 | 13.4k | ctx->tuning->image_decode = fz_default_image_decode; |
116 | 13.4k | ctx->tuning->image_scale = fz_default_image_scale; |
117 | 13.4k | } |
118 | 13.4k | } |
119 | | |
120 | | static fz_tuning_context *fz_keep_tuning_context(fz_context *ctx) |
121 | 0 | { |
122 | 0 | if (!ctx) |
123 | 0 | return NULL; |
124 | 0 | return fz_keep_imp(ctx, ctx->tuning, &ctx->tuning->refs); |
125 | 0 | } |
126 | | |
127 | | static void fz_drop_tuning_context(fz_context *ctx) |
128 | 13.4k | { |
129 | 13.4k | if (!ctx) |
130 | 0 | return; |
131 | 13.4k | if (fz_drop_imp(ctx, ctx->tuning, &ctx->tuning->refs)) |
132 | 13.4k | { |
133 | 13.4k | fz_free(ctx, ctx->tuning); |
134 | 13.4k | } |
135 | 13.4k | } |
136 | | |
137 | | void fz_tune_image_decode(fz_context *ctx, fz_tune_image_decode_fn *image_decode, void *arg) |
138 | 0 | { |
139 | 0 | ctx->tuning->image_decode = image_decode ? image_decode : fz_default_image_decode; |
140 | 0 | ctx->tuning->image_decode_arg = arg; |
141 | 0 | } |
142 | | |
143 | | void fz_tune_image_scale(fz_context *ctx, fz_tune_image_scale_fn *image_scale, void *arg) |
144 | 0 | { |
145 | 0 | ctx->tuning->image_scale = image_scale ? image_scale : fz_default_image_scale; |
146 | 0 | ctx->tuning->image_scale_arg = arg; |
147 | 0 | } |
148 | | |
149 | | static void fz_init_random_context(fz_context *ctx) |
150 | 13.4k | { |
151 | 13.4k | if (!ctx) |
152 | 0 | return; |
153 | | |
154 | 13.4k | ctx->seed48[0] = 0; |
155 | 13.4k | ctx->seed48[1] = 0; |
156 | 13.4k | ctx->seed48[2] = 0; |
157 | 13.4k | ctx->seed48[3] = 0xe66d; |
158 | 13.4k | ctx->seed48[4] = 0xdeec; |
159 | 13.4k | ctx->seed48[5] = 0x5; |
160 | 13.4k | ctx->seed48[6] = 0xb; |
161 | | |
162 | 13.4k | fz_srand48(ctx, (uint32_t)time(NULL)); |
163 | 13.4k | } |
164 | | |
165 | | void |
166 | | fz_drop_context(fz_context *ctx) |
167 | 13.4k | { |
168 | 13.4k | int free_master = 0; |
169 | 13.4k | int call_log = 0; |
170 | | |
171 | 13.4k | if (!ctx) |
172 | 0 | return; |
173 | | |
174 | 13.4k | if (ctx->error.errcode) |
175 | 0 | { |
176 | 0 | fz_flush_warnings(ctx); |
177 | 0 | fz_warn(ctx, "UNHANDLED EXCEPTION!"); |
178 | 0 | fz_report_error(ctx); |
179 | | #ifdef CLUSTER |
180 | | abort(); |
181 | | #endif |
182 | 0 | } |
183 | | |
184 | | |
185 | 13.4k | assert(ctx->master); |
186 | 13.4k | fz_lock(ctx, FZ_LOCK_ALLOC); |
187 | 13.4k | ctx->master->context_count--; |
188 | 13.4k | if (ctx->master->context_count == 0) |
189 | 13.4k | { |
190 | 13.4k | call_log = 1; |
191 | 13.4k | if (ctx->master != ctx) |
192 | 0 | free_master = 1; |
193 | 13.4k | } |
194 | 13.4k | fz_unlock(ctx, FZ_LOCK_ALLOC); |
195 | | |
196 | | /* We call the log with ctx intact, apart from the master |
197 | | * pointer having had the context_count reduced. The |
198 | | * only possible problem here is if fz_log_activity |
199 | | * clones the context, but it really shouldn't be doing |
200 | | * that! */ |
201 | 13.4k | if (call_log) |
202 | 13.4k | fz_log_activity(ctx, FZ_ACTIVITY_SHUTDOWN, NULL); |
203 | 13.4k | if (free_master) |
204 | 0 | ctx->alloc.free(ctx->alloc.user, ctx->master); |
205 | | |
206 | | /* Other finalisation calls go here (in reverse order) */ |
207 | 13.4k | fz_drop_document_handler_context(ctx); |
208 | 13.4k | fz_drop_archive_handler_context(ctx); |
209 | 13.4k | fz_drop_glyph_cache_context(ctx); |
210 | 13.4k | fz_drop_store_context(ctx); |
211 | 13.4k | fz_drop_style_context(ctx); |
212 | 13.4k | fz_drop_tuning_context(ctx); |
213 | 13.4k | fz_drop_colorspace_context(ctx); |
214 | 13.4k | fz_drop_font_context(ctx); |
215 | | |
216 | 13.4k | fz_flush_warnings(ctx); |
217 | | |
218 | 13.4k | assert(ctx->error.top == ctx->error.stack_base); |
219 | | |
220 | | /* Free the context itself */ |
221 | 13.4k | if (ctx->master == ctx && ctx->context_count != 0) |
222 | 0 | { |
223 | | /* Need to delay our freeing until all our children have died. */ |
224 | 0 | ctx->master = NULL; |
225 | 0 | } |
226 | 13.4k | else |
227 | 13.4k | { |
228 | 13.4k | ctx->alloc.free(ctx->alloc.user, ctx); |
229 | 13.4k | } |
230 | 13.4k | } |
231 | | |
232 | | static void |
233 | | fz_init_error_context(fz_context *ctx) |
234 | 13.4k | { |
235 | 13.4k | #define ALIGN(addr, align) ((((intptr_t)(addr)) + (align-1)) & ~(align-1)) |
236 | 13.4k | ctx->error.stack_base = (fz_error_stack_slot *)ALIGN(ctx->error.stack, FZ_JMPBUF_ALIGN); |
237 | 13.4k | ctx->error.top = ctx->error.stack_base; |
238 | 13.4k | ctx->error.errcode = FZ_ERROR_NONE; |
239 | 13.4k | ctx->error.message[0] = 0; |
240 | | |
241 | 13.4k | ctx->warn.message[0] = 0; |
242 | 13.4k | ctx->warn.count = 0; |
243 | 13.4k | } |
244 | | |
245 | | fz_context * |
246 | | fz_new_context_imp(const fz_alloc_context *alloc, const fz_locks_context *locks, size_t max_store, const char *version) |
247 | 13.4k | { |
248 | 13.4k | fz_context *ctx; |
249 | | |
250 | 13.4k | if (strcmp(version, FZ_VERSION)) |
251 | 0 | { |
252 | 0 | fprintf(stderr, "cannot create context: incompatible header (%s) and library (%s) versions\n", version, FZ_VERSION); |
253 | 0 | return NULL; |
254 | 0 | } |
255 | | |
256 | 13.4k | if (!alloc) |
257 | 0 | alloc = &fz_alloc_default; |
258 | | |
259 | 13.4k | if (!locks) |
260 | 13.4k | locks = &fz_locks_default; |
261 | | |
262 | 13.4k | ctx = Memento_label(alloc->malloc(alloc->user, sizeof(fz_context)), "fz_context"); |
263 | 13.4k | if (!ctx) |
264 | 0 | { |
265 | 0 | fprintf(stderr, "cannot create context (phase 1)\n"); |
266 | 0 | return NULL; |
267 | 0 | } |
268 | 13.4k | memset(ctx, 0, sizeof *ctx); |
269 | | |
270 | 13.4k | ctx->user = NULL; |
271 | 13.4k | ctx->alloc = *alloc; |
272 | 13.4k | ctx->locks = *locks; |
273 | | |
274 | | /* We are our own master! */ |
275 | 13.4k | ctx->master = ctx; |
276 | 13.4k | ctx->context_count = 1; |
277 | | |
278 | 13.4k | ctx->error.print = fz_default_error_callback; |
279 | 13.4k | ctx->warn.print = fz_default_warning_callback; |
280 | | |
281 | 13.4k | fz_init_error_context(ctx); |
282 | 13.4k | fz_init_aa_context(ctx); |
283 | 13.4k | fz_init_random_context(ctx); |
284 | | |
285 | | /* Now initialise sections that are shared */ |
286 | 26.9k | fz_try(ctx) |
287 | 26.9k | { |
288 | 13.4k | fz_new_store_context(ctx, max_store); |
289 | 13.4k | fz_new_glyph_cache_context(ctx); |
290 | 13.4k | fz_new_colorspace_context(ctx); |
291 | 13.4k | fz_new_font_context(ctx); |
292 | 13.4k | fz_new_document_handler_context(ctx); |
293 | 13.4k | fz_new_archive_handler_context(ctx); |
294 | 13.4k | fz_new_style_context(ctx); |
295 | 13.4k | fz_new_tuning_context(ctx); |
296 | 13.4k | } |
297 | 26.9k | fz_catch(ctx) |
298 | 0 | { |
299 | 0 | fz_report_error(ctx); |
300 | 0 | fprintf(stderr, "cannot create context (phase 2)\n"); |
301 | 0 | fz_drop_context(ctx); |
302 | 0 | return NULL; |
303 | 0 | } |
304 | 13.4k | return ctx; |
305 | 13.4k | } |
306 | | |
307 | | fz_context * |
308 | | fz_clone_context(fz_context *ctx) |
309 | 0 | { |
310 | 0 | fz_context *new_ctx; |
311 | | |
312 | | /* We cannot safely clone the context without having locking/ |
313 | | * unlocking functions. */ |
314 | 0 | if (ctx == NULL || (ctx->locks.lock == fz_locks_default.lock && ctx->locks.unlock == fz_locks_default.unlock)) |
315 | 0 | return NULL; |
316 | | |
317 | 0 | new_ctx = ctx->alloc.malloc(ctx->alloc.user, sizeof(fz_context)); |
318 | 0 | if (!new_ctx) |
319 | 0 | return NULL; |
320 | | |
321 | 0 | fz_lock(ctx, FZ_LOCK_ALLOC); |
322 | 0 | ctx->master->context_count++; |
323 | 0 | fz_unlock(ctx, FZ_LOCK_ALLOC); |
324 | 0 | new_ctx->master = ctx->master; |
325 | | |
326 | | /* First copy old context, including pointers to shared contexts */ |
327 | 0 | memcpy(new_ctx, ctx, sizeof (fz_context)); |
328 | | |
329 | | /* Reset error context to initial state. */ |
330 | 0 | fz_init_error_context(new_ctx); |
331 | | |
332 | | /* Then keep lock checking happy by keeping shared contexts with new context */ |
333 | 0 | fz_keep_document_handler_context(new_ctx); |
334 | 0 | fz_keep_archive_handler_context(new_ctx); |
335 | 0 | fz_keep_style_context(new_ctx); |
336 | 0 | fz_keep_tuning_context(new_ctx); |
337 | 0 | fz_keep_font_context(new_ctx); |
338 | 0 | fz_keep_colorspace_context(new_ctx); |
339 | 0 | fz_keep_store_context(new_ctx); |
340 | 0 | fz_keep_glyph_cache(new_ctx); |
341 | |
|
342 | 0 | return new_ctx; |
343 | 0 | } |
344 | | |
345 | | void fz_set_user_context(fz_context *ctx, void *user) |
346 | 0 | { |
347 | 0 | if (ctx != NULL) |
348 | 0 | ctx->user = user; |
349 | 0 | } |
350 | | |
351 | | void *fz_user_context(fz_context *ctx) |
352 | 0 | { |
353 | 0 | if (ctx == NULL) |
354 | 0 | return NULL; |
355 | | |
356 | 0 | return ctx->user; |
357 | 0 | } |
358 | | |
359 | | void fz_register_activity_logger(fz_context *ctx, fz_activity_fn *activity, void *opaque) |
360 | 0 | { |
361 | 0 | if (ctx == NULL) |
362 | 0 | return; |
363 | | |
364 | 0 | ctx->activity.activity = activity; |
365 | 0 | ctx->activity.opaque = opaque; |
366 | 0 | } |
367 | | |
368 | | void fz_log_activity(fz_context *ctx, fz_activity_reason reason, void *arg) |
369 | 26.8k | { |
370 | 26.8k | if (ctx == NULL || ctx->activity.activity == NULL) |
371 | 26.8k | return; |
372 | | |
373 | 0 | ctx->activity.activity(ctx, ctx->activity.opaque, reason, arg); |
374 | 0 | } |