/src/mupdf/source/fitz/document.c
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (C) 2004-2023 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 <string.h> |
26 | | |
27 | | enum |
28 | | { |
29 | | FZ_DOCUMENT_HANDLER_MAX = 32 |
30 | | }; |
31 | | |
32 | 14 | #define DEFW (450) |
33 | 14 | #define DEFH (600) |
34 | 14 | #define DEFEM (12) |
35 | | |
36 | | struct fz_document_handler_context |
37 | | { |
38 | | int refs; |
39 | | int count; |
40 | | const fz_document_handler *handler[FZ_DOCUMENT_HANDLER_MAX]; |
41 | | }; |
42 | | |
43 | | void fz_new_document_handler_context(fz_context *ctx) |
44 | 15.6k | { |
45 | 15.6k | ctx->handler = fz_malloc_struct(ctx, fz_document_handler_context); |
46 | 15.6k | ctx->handler->refs = 1; |
47 | 15.6k | } |
48 | | |
49 | | fz_document_handler_context *fz_keep_document_handler_context(fz_context *ctx) |
50 | 0 | { |
51 | 0 | if (!ctx || !ctx->handler) |
52 | 0 | return NULL; |
53 | 0 | return fz_keep_imp(ctx, ctx->handler, &ctx->handler->refs); |
54 | 0 | } |
55 | | |
56 | | void fz_drop_document_handler_context(fz_context *ctx) |
57 | 15.6k | { |
58 | 15.6k | if (!ctx) |
59 | 0 | return; |
60 | | |
61 | 15.6k | if (fz_drop_imp(ctx, ctx->handler, &ctx->handler->refs)) |
62 | 15.6k | { |
63 | 15.6k | fz_free(ctx, ctx->handler); |
64 | 15.6k | ctx->handler = NULL; |
65 | 15.6k | } |
66 | 15.6k | } |
67 | | |
68 | | void fz_register_document_handler(fz_context *ctx, const fz_document_handler *handler) |
69 | 202k | { |
70 | 202k | fz_document_handler_context *dc; |
71 | 202k | int i; |
72 | | |
73 | 202k | if (!handler) |
74 | 0 | return; |
75 | | |
76 | 202k | dc = ctx->handler; |
77 | 202k | if (dc == NULL) |
78 | 0 | fz_throw(ctx, FZ_ERROR_ARGUMENT, "Document handler list not found"); |
79 | | |
80 | 1.41M | for (i = 0; i < dc->count; i++) |
81 | 1.21M | if (dc->handler[i] == handler) |
82 | 0 | return; |
83 | | |
84 | 202k | if (dc->count >= FZ_DOCUMENT_HANDLER_MAX) |
85 | 0 | fz_throw(ctx, FZ_ERROR_LIMIT, "Too many document handlers"); |
86 | | |
87 | 202k | dc->handler[dc->count++] = handler; |
88 | 202k | } |
89 | | |
90 | | const fz_document_handler * |
91 | | fz_recognize_document_stream_content(fz_context *ctx, fz_stream *stream, const char *magic) |
92 | 0 | { |
93 | 0 | return fz_recognize_document_stream_and_dir_content(ctx, stream, NULL, magic); |
94 | 0 | } |
95 | | |
96 | | const fz_document_handler * |
97 | | fz_recognize_document_stream_and_dir_content(fz_context *ctx, fz_stream *stream, fz_archive *dir, const char *magic) |
98 | 15.6k | { |
99 | 15.6k | fz_document_handler_context *dc; |
100 | 15.6k | int i, best_score, best_i; |
101 | 15.6k | const char *ext; |
102 | | |
103 | 15.6k | dc = ctx->handler; |
104 | 15.6k | if (dc->count == 0) |
105 | 0 | fz_throw(ctx, FZ_ERROR_ARGUMENT, "No document handlers registered"); |
106 | | |
107 | 15.6k | ext = strrchr(magic, '.'); |
108 | 15.6k | if (ext) |
109 | 0 | ext = ext + 1; |
110 | 15.6k | else |
111 | 15.6k | ext = magic; |
112 | | |
113 | 15.6k | best_score = 0; |
114 | 15.6k | best_i = -1; |
115 | | |
116 | 15.6k | if ((stream && stream->seek != NULL) || (stream == NULL && dir != NULL)) |
117 | 15.6k | { |
118 | 219k | for (i = 0; i < dc->count; i++) |
119 | 203k | { |
120 | 203k | int score = 0; |
121 | | |
122 | 203k | if (dc->handler[i]->recognize_content) |
123 | 141k | { |
124 | 141k | if (stream) |
125 | 141k | fz_seek(ctx, stream, 0, SEEK_SET); |
126 | 282k | fz_try(ctx) |
127 | 282k | { |
128 | 141k | score = dc->handler[i]->recognize_content(ctx, stream, dir); |
129 | 141k | } |
130 | 282k | fz_catch(ctx) |
131 | 756 | { |
132 | | /* in case of zip errors when recognizing EPUB/XPS/DOCX files */ |
133 | 756 | fz_rethrow_unless(ctx, FZ_ERROR_FORMAT); |
134 | 756 | (void)fz_convert_error(ctx, NULL); /* ugly hack to silence the error message */ |
135 | 756 | score = 0; |
136 | 756 | } |
137 | 141k | } |
138 | 203k | if (best_score < score) |
139 | 12.0k | { |
140 | 12.0k | best_score = score; |
141 | 12.0k | best_i = i; |
142 | 12.0k | } |
143 | 203k | } |
144 | 15.6k | if (stream) |
145 | 15.6k | fz_seek(ctx, stream, 0, SEEK_SET); |
146 | 15.6k | } |
147 | | |
148 | 15.6k | if (best_score < 100) |
149 | 3.61k | { |
150 | 50.5k | for (i = 0; i < dc->count; i++) |
151 | 46.9k | { |
152 | 46.9k | int score = 0; |
153 | 46.9k | const char **entry; |
154 | | |
155 | 46.9k | if (dc->handler[i]->recognize) |
156 | 3.61k | score = dc->handler[i]->recognize(ctx, magic); |
157 | | |
158 | 209k | for (entry = &dc->handler[i]->mimetypes[0]; *entry; entry++) |
159 | 162k | if (!fz_strcasecmp(magic, *entry) && score < 100) |
160 | 0 | { |
161 | 0 | score = 100; |
162 | 0 | break; |
163 | 0 | } |
164 | | |
165 | 46.9k | if (ext) |
166 | 46.9k | { |
167 | 227k | for (entry = &dc->handler[i]->extensions[0]; *entry; entry++) |
168 | 184k | if (!fz_strcasecmp(ext, *entry) && score < 100) |
169 | 3.53k | { |
170 | 3.53k | score = 100; |
171 | 3.53k | break; |
172 | 3.53k | } |
173 | 46.9k | } |
174 | | |
175 | 46.9k | if (best_score < score) |
176 | 3.53k | { |
177 | 3.53k | best_score = score; |
178 | 3.53k | best_i = i; |
179 | 3.53k | } |
180 | 46.9k | } |
181 | 3.61k | } |
182 | | |
183 | 15.6k | if (best_i < 0) |
184 | 76 | return NULL; |
185 | | |
186 | 15.6k | return dc->handler[best_i]; |
187 | 15.6k | } |
188 | | |
189 | | const fz_document_handler *fz_recognize_document_content(fz_context *ctx, const char *filename) |
190 | 0 | { |
191 | 0 | fz_stream *stream = NULL; |
192 | 0 | const fz_document_handler *handler = NULL; |
193 | 0 | fz_archive *zip = NULL; |
194 | |
|
195 | 0 | if (fz_is_directory(ctx, filename)) |
196 | 0 | zip = fz_open_directory(ctx, filename); |
197 | 0 | else |
198 | 0 | stream = fz_open_file(ctx, filename); |
199 | |
|
200 | 0 | fz_try(ctx) |
201 | 0 | handler = fz_recognize_document_stream_and_dir_content(ctx, stream, zip, filename); |
202 | 0 | fz_always(ctx) |
203 | 0 | { |
204 | 0 | fz_drop_stream(ctx, stream); |
205 | 0 | fz_drop_archive(ctx, zip); |
206 | 0 | } |
207 | 0 | fz_catch(ctx) |
208 | 0 | fz_rethrow(ctx); |
209 | | |
210 | 0 | return handler; |
211 | 0 | } |
212 | | |
213 | | const fz_document_handler * |
214 | | fz_recognize_document(fz_context *ctx, const char *magic) |
215 | 0 | { |
216 | 0 | return fz_recognize_document_stream_and_dir_content(ctx, NULL, NULL, magic); |
217 | 0 | } |
218 | | |
219 | | #if FZ_ENABLE_PDF |
220 | | extern fz_document_handler pdf_document_handler; |
221 | | #endif |
222 | | |
223 | | fz_document * |
224 | | fz_open_accelerated_document_with_stream_and_dir(fz_context *ctx, const char *magic, fz_stream *stream, fz_stream *accel, fz_archive *dir) |
225 | 15.6k | { |
226 | 15.6k | const fz_document_handler *handler; |
227 | | |
228 | 15.6k | if (stream == NULL && dir == NULL) |
229 | 0 | fz_throw(ctx, FZ_ERROR_ARGUMENT, "no document to open"); |
230 | 15.6k | if (magic == NULL) |
231 | 0 | fz_throw(ctx, FZ_ERROR_ARGUMENT, "missing file type"); |
232 | | |
233 | 15.6k | handler = fz_recognize_document_stream_and_dir_content(ctx, stream, dir, magic); |
234 | 15.6k | if (!handler) |
235 | 76 | fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "cannot find document handler for file type: '%s'", magic); |
236 | 15.6k | return handler->open(ctx, stream, accel, dir); |
237 | 15.6k | } |
238 | | |
239 | | fz_document * |
240 | | fz_open_accelerated_document_with_stream(fz_context *ctx, const char *magic, fz_stream *stream, fz_stream *accel) |
241 | 15.6k | { |
242 | 15.6k | return fz_open_accelerated_document_with_stream_and_dir(ctx, magic, stream, accel, NULL); |
243 | 15.6k | } |
244 | | |
245 | | fz_document * |
246 | | fz_open_document_with_stream(fz_context *ctx, const char *magic, fz_stream *stream) |
247 | 15.6k | { |
248 | 15.6k | return fz_open_accelerated_document_with_stream(ctx, magic, stream, NULL); |
249 | 15.6k | } |
250 | | |
251 | | fz_document * |
252 | | fz_open_document_with_stream_and_dir(fz_context *ctx, const char *magic, fz_stream *stream, fz_archive *dir) |
253 | 0 | { |
254 | 0 | return fz_open_accelerated_document_with_stream_and_dir(ctx, magic, stream, NULL, dir); |
255 | 0 | } |
256 | | |
257 | | fz_document * |
258 | | fz_open_document_with_buffer(fz_context *ctx, const char *magic, fz_buffer *buffer) |
259 | 76 | { |
260 | 76 | fz_document *doc; |
261 | 76 | fz_stream *stream = fz_open_buffer(ctx, buffer); |
262 | 152 | fz_try(ctx) |
263 | 152 | doc = fz_open_document_with_stream(ctx, magic, stream); |
264 | 152 | fz_always(ctx) |
265 | 76 | fz_drop_stream(ctx, stream); |
266 | 76 | fz_catch(ctx) |
267 | 76 | fz_rethrow(ctx); |
268 | 0 | return doc; |
269 | 76 | } |
270 | | |
271 | | fz_document * |
272 | | fz_open_accelerated_document(fz_context *ctx, const char *filename, const char *accel) |
273 | 0 | { |
274 | 0 | const fz_document_handler *handler; |
275 | 0 | fz_stream *file; |
276 | 0 | fz_stream *afile = NULL; |
277 | 0 | fz_document *doc = NULL; |
278 | 0 | fz_archive *dir = NULL; |
279 | 0 | char dirname[PATH_MAX]; |
280 | |
|
281 | 0 | fz_var(afile); |
282 | |
|
283 | 0 | if (filename == NULL) |
284 | 0 | fz_throw(ctx, FZ_ERROR_ARGUMENT, "no document to open"); |
285 | | |
286 | 0 | handler = fz_recognize_document_content(ctx, filename); |
287 | 0 | if (!handler) |
288 | 0 | fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "cannot find document handler for file: %s", filename); |
289 | | |
290 | 0 | if (fz_is_directory(ctx, filename)) |
291 | 0 | { |
292 | | /* Cannot accelerate directories, currently. */ |
293 | 0 | dir = fz_open_directory(ctx, filename); |
294 | |
|
295 | 0 | fz_try(ctx) |
296 | 0 | doc = fz_open_accelerated_document_with_stream_and_dir(ctx, filename, NULL, NULL, dir); |
297 | 0 | fz_always(ctx) |
298 | 0 | fz_drop_archive(ctx, dir); |
299 | 0 | fz_catch(ctx) |
300 | 0 | fz_rethrow(ctx); |
301 | | |
302 | 0 | return doc; |
303 | 0 | } |
304 | | |
305 | 0 | file = fz_open_file(ctx, filename); |
306 | |
|
307 | 0 | fz_try(ctx) |
308 | 0 | { |
309 | 0 | if (accel) |
310 | 0 | afile = fz_open_file(ctx, accel); |
311 | 0 | if (handler->wants_dir) |
312 | 0 | { |
313 | 0 | fz_dirname(dirname, filename, sizeof dirname); |
314 | 0 | dir = fz_open_directory(ctx, dirname); |
315 | 0 | } |
316 | 0 | doc = handler->open(ctx, file, afile, dir); |
317 | 0 | } |
318 | 0 | fz_always(ctx) |
319 | 0 | { |
320 | 0 | fz_drop_archive(ctx, dir); |
321 | 0 | fz_drop_stream(ctx, afile); |
322 | 0 | fz_drop_stream(ctx, file); |
323 | 0 | } |
324 | 0 | fz_catch(ctx) |
325 | 0 | fz_rethrow(ctx); |
326 | | |
327 | 0 | return doc; |
328 | 0 | } |
329 | | |
330 | | fz_document * |
331 | | fz_open_document(fz_context *ctx, const char *filename) |
332 | 0 | { |
333 | 0 | return fz_open_accelerated_document(ctx, filename, NULL); |
334 | 0 | } |
335 | | |
336 | | void fz_save_accelerator(fz_context *ctx, fz_document *doc, const char *accel) |
337 | 0 | { |
338 | 0 | if (doc == NULL) |
339 | 0 | return; |
340 | 0 | if (doc->output_accelerator == NULL) |
341 | 0 | return; |
342 | | |
343 | 0 | fz_output_accelerator(ctx, doc, fz_new_output_with_path(ctx, accel, 0)); |
344 | 0 | } |
345 | | |
346 | | void fz_output_accelerator(fz_context *ctx, fz_document *doc, fz_output *accel) |
347 | 0 | { |
348 | 0 | if (doc == NULL || accel == NULL) |
349 | 0 | return; |
350 | 0 | if (doc->output_accelerator == NULL) |
351 | 0 | { |
352 | 0 | fz_drop_output(ctx, accel); |
353 | 0 | fz_throw(ctx, FZ_ERROR_ARGUMENT, "Document does not support writing an accelerator"); |
354 | 0 | } |
355 | | |
356 | 0 | doc->output_accelerator(ctx, doc, accel); |
357 | 0 | } |
358 | | |
359 | | int fz_document_supports_accelerator(fz_context *ctx, fz_document *doc) |
360 | 0 | { |
361 | 0 | if (doc == NULL) |
362 | 0 | return 0; |
363 | 0 | return (doc->output_accelerator) != NULL; |
364 | 0 | } |
365 | | |
366 | | void * |
367 | | fz_new_document_of_size(fz_context *ctx, int size) |
368 | 15.5k | { |
369 | 15.5k | fz_document *doc = fz_calloc(ctx, 1, size); |
370 | 15.5k | doc->refs = 1; |
371 | 15.5k | return doc; |
372 | 15.5k | } |
373 | | |
374 | | fz_document * |
375 | | fz_keep_document(fz_context *ctx, fz_document *doc) |
376 | 42.3k | { |
377 | 42.3k | return fz_keep_imp(ctx, doc, &doc->refs); |
378 | 42.3k | } |
379 | | |
380 | | void |
381 | | fz_drop_document(fz_context *ctx, fz_document *doc) |
382 | 58.5k | { |
383 | 58.5k | if (fz_drop_imp(ctx, doc, &doc->refs)) |
384 | 15.5k | { |
385 | 15.5k | if (doc->open) |
386 | 0 | fz_warn(ctx, "There are still open pages in the document!"); |
387 | 15.5k | if (doc->drop_document) |
388 | 15.5k | doc->drop_document(ctx, doc); |
389 | 15.5k | fz_free(ctx, doc); |
390 | 15.5k | } |
391 | 58.5k | } |
392 | | |
393 | | static void |
394 | | fz_ensure_layout(fz_context *ctx, fz_document *doc) |
395 | 108k | { |
396 | 108k | if (doc && doc->layout && !doc->did_layout) |
397 | 14 | { |
398 | 14 | doc->layout(ctx, doc, DEFW, DEFH, DEFEM); |
399 | 14 | doc->did_layout = 1; |
400 | 14 | } |
401 | 108k | } |
402 | | |
403 | | int |
404 | | fz_is_document_reflowable(fz_context *ctx, fz_document *doc) |
405 | 0 | { |
406 | 0 | return doc ? doc->is_reflowable : 0; |
407 | 0 | } |
408 | | |
409 | | fz_bookmark fz_make_bookmark(fz_context *ctx, fz_document *doc, fz_location loc) |
410 | 0 | { |
411 | 0 | if (doc && doc->make_bookmark) |
412 | 0 | return doc->make_bookmark(ctx, doc, loc); |
413 | 0 | return (loc.chapter<<16) + loc.page; |
414 | 0 | } |
415 | | |
416 | | fz_location fz_lookup_bookmark(fz_context *ctx, fz_document *doc, fz_bookmark mark) |
417 | 0 | { |
418 | 0 | if (doc && doc->lookup_bookmark) |
419 | 0 | return doc->lookup_bookmark(ctx, doc, mark); |
420 | 0 | return fz_make_location((mark>>16) & 0xffff, mark & 0xffff); |
421 | 0 | } |
422 | | |
423 | | int |
424 | | fz_needs_password(fz_context *ctx, fz_document *doc) |
425 | 0 | { |
426 | 0 | if (doc && doc->needs_password) |
427 | 0 | return doc->needs_password(ctx, doc); |
428 | 0 | return 0; |
429 | 0 | } |
430 | | |
431 | | int |
432 | | fz_authenticate_password(fz_context *ctx, fz_document *doc, const char *password) |
433 | 0 | { |
434 | 0 | if (doc && doc->authenticate_password) |
435 | 0 | return doc->authenticate_password(ctx, doc, password); |
436 | 0 | return 1; |
437 | 0 | } |
438 | | |
439 | | int |
440 | | fz_has_permission(fz_context *ctx, fz_document *doc, fz_permission p) |
441 | 0 | { |
442 | 0 | if (doc && doc->has_permission) |
443 | 0 | return doc->has_permission(ctx, doc, p); |
444 | 0 | return 1; |
445 | 0 | } |
446 | | |
447 | | fz_outline * |
448 | | fz_load_outline(fz_context *ctx, fz_document *doc) |
449 | 0 | { |
450 | 0 | if (doc == NULL) |
451 | 0 | return NULL; |
452 | 0 | fz_ensure_layout(ctx, doc); |
453 | 0 | if (doc->load_outline) |
454 | 0 | return doc->load_outline(ctx, doc); |
455 | 0 | if (doc->outline_iterator == NULL) |
456 | 0 | return NULL; |
457 | 0 | return fz_load_outline_from_iterator(ctx, doc->outline_iterator(ctx, doc)); |
458 | 0 | } |
459 | | |
460 | | fz_outline_iterator * |
461 | | fz_new_outline_iterator(fz_context *ctx, fz_document *doc) |
462 | 0 | { |
463 | 0 | if (doc == NULL) |
464 | 0 | return NULL; |
465 | 0 | if (doc->outline_iterator) |
466 | 0 | return doc->outline_iterator(ctx, doc); |
467 | 0 | if (doc->load_outline == NULL) |
468 | 0 | return NULL; |
469 | 0 | return fz_outline_iterator_from_outline(ctx, fz_load_outline(ctx, doc)); |
470 | 0 | } |
471 | | |
472 | | fz_link_dest |
473 | | fz_resolve_link_dest(fz_context *ctx, fz_document *doc, const char *uri) |
474 | 0 | { |
475 | 0 | fz_ensure_layout(ctx, doc); |
476 | 0 | if (doc && doc->resolve_link_dest) |
477 | 0 | return doc->resolve_link_dest(ctx, doc, uri); |
478 | 0 | return fz_make_link_dest_none(); |
479 | 0 | } |
480 | | |
481 | | char * |
482 | | fz_format_link_uri(fz_context *ctx, fz_document *doc, fz_link_dest dest) |
483 | 0 | { |
484 | 0 | if (doc && doc->format_link_uri) |
485 | 0 | return doc->format_link_uri(ctx, doc, dest); |
486 | 0 | fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot create internal links for this document type"); |
487 | 0 | } |
488 | | |
489 | | fz_location |
490 | | fz_resolve_link(fz_context *ctx, fz_document *doc, const char *uri, float *xp, float *yp) |
491 | 0 | { |
492 | 0 | fz_link_dest dest = fz_resolve_link_dest(ctx, doc, uri); |
493 | 0 | if (xp) *xp = dest.x; |
494 | 0 | if (yp) *yp = dest.y; |
495 | 0 | return dest.loc; |
496 | 0 | } |
497 | | |
498 | | void |
499 | | fz_layout_document(fz_context *ctx, fz_document *doc, float w, float h, float em) |
500 | 0 | { |
501 | 0 | if (doc && doc->layout) |
502 | 0 | { |
503 | 0 | doc->layout(ctx, doc, w, h, em); |
504 | 0 | doc->did_layout = 1; |
505 | 0 | } |
506 | 0 | } |
507 | | |
508 | | int |
509 | | fz_count_chapters(fz_context *ctx, fz_document *doc) |
510 | 46.0k | { |
511 | 46.0k | fz_ensure_layout(ctx, doc); |
512 | 46.0k | if (doc && doc->count_chapters) |
513 | 0 | return doc->count_chapters(ctx, doc); |
514 | 46.0k | return 1; |
515 | 46.0k | } |
516 | | |
517 | | int |
518 | | fz_count_chapter_pages(fz_context *ctx, fz_document *doc, int chapter) |
519 | 46.0k | { |
520 | 46.0k | fz_ensure_layout(ctx, doc); |
521 | 46.0k | if (doc && doc->count_pages) |
522 | 46.0k | return doc->count_pages(ctx, doc, chapter); |
523 | 0 | return 0; |
524 | 46.0k | } |
525 | | |
526 | | int |
527 | | fz_count_pages(fz_context *ctx, fz_document *doc) |
528 | 29.7k | { |
529 | 29.7k | int i, c, n = 0; |
530 | 29.7k | c = fz_count_chapters(ctx, doc); |
531 | 59.5k | for (i = 0; i < c; ++i) |
532 | 29.7k | n += fz_count_chapter_pages(ctx, doc, i); |
533 | 29.7k | return n; |
534 | 29.7k | } |
535 | | |
536 | | fz_page * |
537 | | fz_load_page(fz_context *ctx, fz_document *doc, int number) |
538 | 16.2k | { |
539 | 16.2k | int i, n = fz_count_chapters(ctx, doc); |
540 | 16.2k | int start = 0; |
541 | 16.2k | for (i = 0; i < n; ++i) |
542 | 16.2k | { |
543 | 16.2k | int m = fz_count_chapter_pages(ctx, doc, i); |
544 | 16.2k | if (number < start + m) |
545 | 16.2k | return fz_load_chapter_page(ctx, doc, i, number - start); |
546 | 0 | start += m; |
547 | 0 | } |
548 | 0 | fz_throw(ctx, FZ_ERROR_ARGUMENT, "invalid page number: %d", number+1); |
549 | 16.2k | } |
550 | | |
551 | | fz_location fz_last_page(fz_context *ctx, fz_document *doc) |
552 | 0 | { |
553 | 0 | int nc = fz_count_chapters(ctx, doc); |
554 | 0 | int np = fz_count_chapter_pages(ctx, doc, nc-1); |
555 | 0 | return fz_make_location(nc-1, np-1); |
556 | 0 | } |
557 | | |
558 | | fz_location fz_next_page(fz_context *ctx, fz_document *doc, fz_location loc) |
559 | 0 | { |
560 | 0 | int nc = fz_count_chapters(ctx, doc); |
561 | 0 | int np = fz_count_chapter_pages(ctx, doc, loc.chapter); |
562 | 0 | if (loc.page + 1 == np) |
563 | 0 | { |
564 | 0 | if (loc.chapter + 1 < nc) |
565 | 0 | { |
566 | 0 | return fz_make_location(loc.chapter + 1, 0); |
567 | 0 | } |
568 | 0 | } |
569 | 0 | else |
570 | 0 | { |
571 | 0 | return fz_make_location(loc.chapter, loc.page + 1); |
572 | 0 | } |
573 | 0 | return loc; |
574 | 0 | } |
575 | | |
576 | | fz_location fz_previous_page(fz_context *ctx, fz_document *doc, fz_location loc) |
577 | 0 | { |
578 | 0 | if (loc.page == 0) |
579 | 0 | { |
580 | 0 | if (loc.chapter > 0) |
581 | 0 | { |
582 | 0 | int np = fz_count_chapter_pages(ctx, doc, loc.chapter - 1); |
583 | 0 | return fz_make_location(loc.chapter - 1, np - 1); |
584 | 0 | } |
585 | 0 | } |
586 | 0 | else |
587 | 0 | { |
588 | 0 | return fz_make_location(loc.chapter, loc.page - 1); |
589 | 0 | } |
590 | 0 | return loc; |
591 | 0 | } |
592 | | |
593 | | fz_location fz_clamp_location(fz_context *ctx, fz_document *doc, fz_location loc) |
594 | 0 | { |
595 | 0 | int nc = fz_count_chapters(ctx, doc); |
596 | 0 | int np; |
597 | 0 | if (loc.chapter < 0) loc.chapter = 0; |
598 | 0 | if (loc.chapter >= nc) loc.chapter = nc - 1; |
599 | 0 | np = fz_count_chapter_pages(ctx, doc, loc.chapter); |
600 | 0 | if (loc.page < 0) loc.page = 0; |
601 | 0 | if (loc.page >= np) loc.page = np - 1; |
602 | 0 | return loc; |
603 | 0 | } |
604 | | |
605 | | fz_location fz_location_from_page_number(fz_context *ctx, fz_document *doc, int number) |
606 | 0 | { |
607 | 0 | int i, m = 0, n = fz_count_chapters(ctx, doc); |
608 | 0 | int start = 0; |
609 | 0 | if (number < 0) |
610 | 0 | number = 0; |
611 | 0 | for (i = 0; i < n; ++i) |
612 | 0 | { |
613 | 0 | m = fz_count_chapter_pages(ctx, doc, i); |
614 | 0 | if (number < start + m) |
615 | 0 | return fz_make_location(i, number - start); |
616 | 0 | start += m; |
617 | 0 | } |
618 | 0 | return fz_make_location(i-1, m-1); |
619 | 0 | } |
620 | | |
621 | | int fz_page_number_from_location(fz_context *ctx, fz_document *doc, fz_location loc) |
622 | 0 | { |
623 | 0 | int i, n, start = 0; |
624 | 0 | n = fz_count_chapters(ctx, doc); |
625 | 0 | for (i = 0; i < n; ++i) |
626 | 0 | { |
627 | 0 | if (i == loc.chapter) |
628 | 0 | return start + loc.page; |
629 | 0 | start += fz_count_chapter_pages(ctx, doc, i); |
630 | 0 | } |
631 | 0 | return -1; |
632 | 0 | } |
633 | | |
634 | | int |
635 | | fz_lookup_metadata(fz_context *ctx, fz_document *doc, const char *key, char *buf, int size) |
636 | 0 | { |
637 | 0 | if (buf && size > 0) |
638 | 0 | buf[0] = 0; |
639 | 0 | if (doc && doc->lookup_metadata) |
640 | 0 | return doc->lookup_metadata(ctx, doc, key, buf, size); |
641 | 0 | return -1; |
642 | 0 | } |
643 | | |
644 | | void |
645 | | fz_set_metadata(fz_context *ctx, fz_document *doc, const char *key, const char *value) |
646 | 0 | { |
647 | 0 | if (doc && doc->set_metadata) |
648 | 0 | doc->set_metadata(ctx, doc, key, value); |
649 | 0 | } |
650 | | |
651 | | fz_colorspace * |
652 | | fz_document_output_intent(fz_context *ctx, fz_document *doc) |
653 | 0 | { |
654 | 0 | if (doc && doc->get_output_intent) |
655 | 0 | return doc->get_output_intent(ctx, doc); |
656 | 0 | return NULL; |
657 | 0 | } |
658 | | |
659 | | fz_page * |
660 | | fz_load_chapter_page(fz_context *ctx, fz_document *doc, int chapter, int number) |
661 | 16.2k | { |
662 | 16.2k | fz_page *page; |
663 | | |
664 | 16.2k | if (doc == NULL) |
665 | 0 | return NULL; |
666 | | |
667 | 16.2k | fz_ensure_layout(ctx, doc); |
668 | | |
669 | | /* Protect modifications to the page list to cope with |
670 | | * destruction of pages on other threads. */ |
671 | 16.2k | fz_lock(ctx, FZ_LOCK_ALLOC); |
672 | 16.2k | for (page = doc->open; page; page = page->next) |
673 | 0 | if (page->chapter == chapter && page->number == number) |
674 | 0 | { |
675 | 0 | fz_keep_page_locked(ctx, page); |
676 | 0 | fz_unlock(ctx, FZ_LOCK_ALLOC); |
677 | 0 | return page; |
678 | 0 | } |
679 | 16.2k | fz_unlock(ctx, FZ_LOCK_ALLOC); |
680 | | |
681 | 16.2k | if (doc->load_page) |
682 | 16.2k | { |
683 | 16.2k | page = doc->load_page(ctx, doc, chapter, number); |
684 | 16.2k | page->chapter = chapter; |
685 | 16.2k | page->number = number; |
686 | | |
687 | | /* Insert new page at the head of the list of open pages. */ |
688 | 16.2k | if (!page->incomplete) |
689 | 14.9k | { |
690 | 14.9k | fz_lock(ctx, FZ_LOCK_ALLOC); |
691 | 14.9k | if ((page->next = doc->open) != NULL) |
692 | 0 | doc->open->prev = &page->next; |
693 | 14.9k | doc->open = page; |
694 | 14.9k | page->prev = &doc->open; |
695 | 14.9k | fz_unlock(ctx, FZ_LOCK_ALLOC); |
696 | 14.9k | } |
697 | 16.2k | return page; |
698 | 16.2k | } |
699 | | |
700 | 0 | return NULL; |
701 | 16.2k | } |
702 | | |
703 | | fz_link * |
704 | | fz_load_links(fz_context *ctx, fz_page *page) |
705 | 0 | { |
706 | 0 | if (page && page->load_links) |
707 | 0 | return page->load_links(ctx, page); |
708 | 0 | return NULL; |
709 | 0 | } |
710 | | |
711 | | fz_rect |
712 | | fz_bound_page(fz_context *ctx, fz_page *page) |
713 | 14.9k | { |
714 | 14.9k | if (page && page->bound_page) |
715 | 14.9k | return page->bound_page(ctx, page, FZ_CROP_BOX); |
716 | 0 | return fz_empty_rect; |
717 | 14.9k | } |
718 | | |
719 | | fz_rect |
720 | | fz_bound_page_box(fz_context *ctx, fz_page *page, fz_box_type box) |
721 | 0 | { |
722 | 0 | if (page && page->bound_page) |
723 | 0 | return page->bound_page(ctx, page, box); |
724 | 0 | return fz_empty_rect; |
725 | 0 | } |
726 | | |
727 | | void |
728 | | fz_run_document_structure(fz_context *ctx, fz_document *doc, fz_device *dev, fz_cookie *cookie) |
729 | 0 | { |
730 | 0 | if (doc && doc->run_structure) |
731 | 0 | { |
732 | 0 | fz_try(ctx) |
733 | 0 | { |
734 | 0 | doc->run_structure(ctx, doc, dev, cookie); |
735 | 0 | } |
736 | 0 | fz_catch(ctx) |
737 | 0 | { |
738 | 0 | dev->close_device = NULL; /* aborted run, don't warn about unclosed device */ |
739 | 0 | fz_rethrow_unless(ctx, FZ_ERROR_ABORT); |
740 | 0 | fz_ignore_error(ctx); |
741 | 0 | } |
742 | 0 | } |
743 | 0 | } |
744 | | |
745 | | void |
746 | | fz_run_page_contents(fz_context *ctx, fz_page *page, fz_device *dev, fz_matrix transform, fz_cookie *cookie) |
747 | 14.9k | { |
748 | 14.9k | if (page && page->run_page_contents) |
749 | 14.9k | { |
750 | 29.9k | fz_try(ctx) |
751 | 29.9k | { |
752 | 14.9k | page->run_page_contents(ctx, page, dev, transform, cookie); |
753 | 14.9k | } |
754 | 29.9k | fz_catch(ctx) |
755 | 186 | { |
756 | 186 | dev->close_device = NULL; /* aborted run, don't warn about unclosed device */ |
757 | 186 | fz_rethrow_unless(ctx, FZ_ERROR_ABORT); |
758 | 186 | fz_ignore_error(ctx); |
759 | 186 | } |
760 | 14.9k | } |
761 | 14.9k | } |
762 | | |
763 | | void |
764 | | fz_run_page_annots(fz_context *ctx, fz_page *page, fz_device *dev, fz_matrix transform, fz_cookie *cookie) |
765 | 14.7k | { |
766 | 14.7k | if (page && page->run_page_annots) |
767 | 12.5k | { |
768 | 25.1k | fz_try(ctx) |
769 | 25.1k | { |
770 | 12.5k | page->run_page_annots(ctx, page, dev, transform, cookie); |
771 | 12.5k | } |
772 | 25.1k | fz_catch(ctx) |
773 | 1 | { |
774 | 1 | dev->close_device = NULL; /* aborted run, don't warn about unclosed device */ |
775 | 1 | fz_rethrow_unless(ctx, FZ_ERROR_ABORT); |
776 | 1 | fz_ignore_error(ctx); |
777 | 1 | } |
778 | 12.5k | } |
779 | 14.7k | } |
780 | | |
781 | | void |
782 | | fz_run_page_widgets(fz_context *ctx, fz_page *page, fz_device *dev, fz_matrix transform, fz_cookie *cookie) |
783 | 14.7k | { |
784 | 14.7k | if (page && page->run_page_widgets) |
785 | 12.5k | { |
786 | 25.1k | fz_try(ctx) |
787 | 25.1k | { |
788 | 12.5k | page->run_page_widgets(ctx, page, dev, transform, cookie); |
789 | 12.5k | } |
790 | 25.1k | fz_catch(ctx) |
791 | 1 | { |
792 | 1 | dev->close_device = NULL; /* aborted run, don't warn about unclosed device */ |
793 | 1 | fz_rethrow_unless(ctx, FZ_ERROR_ABORT); |
794 | 1 | fz_ignore_error(ctx); |
795 | 1 | } |
796 | 12.5k | } |
797 | 14.7k | } |
798 | | |
799 | | void |
800 | | fz_run_page(fz_context *ctx, fz_page *page, fz_device *dev, fz_matrix transform, fz_cookie *cookie) |
801 | 14.9k | { |
802 | 14.9k | fz_run_page_contents(ctx, page, dev, transform, cookie); |
803 | 14.9k | fz_run_page_annots(ctx, page, dev, transform, cookie); |
804 | 14.9k | fz_run_page_widgets(ctx, page, dev, transform, cookie); |
805 | 14.9k | } |
806 | | |
807 | | fz_page * |
808 | | fz_new_page_of_size(fz_context *ctx, int size, fz_document *doc) |
809 | 14.9k | { |
810 | 14.9k | fz_page *page = Memento_label(fz_calloc(ctx, 1, size), "fz_page"); |
811 | 14.9k | page->refs = 1; |
812 | 14.9k | page->doc = fz_keep_document(ctx, doc); |
813 | 14.9k | return page; |
814 | 14.9k | } |
815 | | |
816 | | fz_page * |
817 | | fz_keep_page(fz_context *ctx, fz_page *page) |
818 | 0 | { |
819 | 0 | return fz_keep_imp(ctx, page, &page->refs); |
820 | 0 | } |
821 | | |
822 | | fz_page * |
823 | | fz_keep_page_locked(fz_context *ctx, fz_page *page) |
824 | 0 | { |
825 | 0 | return fz_keep_imp_locked(ctx, page, &page->refs); |
826 | 0 | } |
827 | | |
828 | | void |
829 | | fz_drop_page(fz_context *ctx, fz_page *page) |
830 | 18.6k | { |
831 | 18.6k | if (fz_drop_imp(ctx, page, &page->refs)) |
832 | 14.9k | { |
833 | | /* Remove page from the list of open pages */ |
834 | 14.9k | fz_lock(ctx, FZ_LOCK_ALLOC); |
835 | 14.9k | if (page->next != NULL) |
836 | 0 | page->next->prev = page->prev; |
837 | 14.9k | if (page->prev != NULL) |
838 | 14.9k | *page->prev = page->next; |
839 | 14.9k | fz_unlock(ctx, FZ_LOCK_ALLOC); |
840 | | |
841 | 14.9k | if (page->drop_page) |
842 | 14.9k | page->drop_page(ctx, page); |
843 | | |
844 | 14.9k | fz_drop_document(ctx, page->doc); |
845 | | |
846 | 14.9k | fz_free(ctx, page); |
847 | 14.9k | } |
848 | 18.6k | } |
849 | | |
850 | | fz_transition * |
851 | | fz_page_presentation(fz_context *ctx, fz_page *page, fz_transition *transition, float *duration) |
852 | 0 | { |
853 | 0 | float dummy; |
854 | 0 | if (duration) |
855 | 0 | *duration = 0; |
856 | 0 | else |
857 | 0 | duration = &dummy; |
858 | 0 | if (page && page->page_presentation && page) |
859 | 0 | return page->page_presentation(ctx, page, transition, duration); |
860 | 0 | return NULL; |
861 | 0 | } |
862 | | |
863 | | fz_separations * |
864 | | fz_page_separations(fz_context *ctx, fz_page *page) |
865 | 0 | { |
866 | 0 | if (page && page->separations) |
867 | 0 | return page->separations(ctx, page); |
868 | 0 | return NULL; |
869 | 0 | } |
870 | | |
871 | | int fz_page_uses_overprint(fz_context *ctx, fz_page *page) |
872 | 0 | { |
873 | 0 | if (page && page->overprint) |
874 | 0 | return page->overprint(ctx, page); |
875 | 0 | return 0; |
876 | 0 | } |
877 | | |
878 | | fz_link *fz_create_link(fz_context *ctx, fz_page *page, fz_rect bbox, const char *uri) |
879 | 0 | { |
880 | 0 | if (page == NULL || uri == NULL) |
881 | 0 | return NULL; |
882 | 0 | if (page->create_link == NULL) |
883 | 0 | fz_throw(ctx, FZ_ERROR_ARGUMENT, "This format of document does not support creating links"); |
884 | 0 | return page->create_link(ctx, page, bbox, uri); |
885 | 0 | } |
886 | | |
887 | | void fz_delete_link(fz_context *ctx, fz_page *page, fz_link *link) |
888 | 0 | { |
889 | 0 | if (page == NULL || link == NULL) |
890 | 0 | return; |
891 | 0 | if (page->delete_link == NULL) |
892 | 0 | fz_throw(ctx, FZ_ERROR_ARGUMENT, "This format of document does not support deleting links"); |
893 | 0 | page->delete_link(ctx, page, link); |
894 | 0 | } |
895 | | |
896 | | void fz_set_link_rect(fz_context *ctx, fz_link *link, fz_rect rect) |
897 | 0 | { |
898 | 0 | if (link == NULL) |
899 | 0 | return; |
900 | 0 | if (link->set_rect_fn == NULL) |
901 | 0 | fz_throw(ctx, FZ_ERROR_ARGUMENT, "This format of document does not support updating link bounds"); |
902 | 0 | link->set_rect_fn(ctx, link, rect); |
903 | 0 | } |
904 | | |
905 | | void fz_set_link_uri(fz_context *ctx, fz_link *link, const char *uri) |
906 | 0 | { |
907 | 0 | if (link == NULL) |
908 | 0 | return; |
909 | 0 | if (link->set_uri_fn == NULL) |
910 | 0 | fz_throw(ctx, FZ_ERROR_ARGUMENT, "This format of document does not support updating link uri"); |
911 | 0 | link->set_uri_fn(ctx, link, uri); |
912 | 0 | } |
913 | | |
914 | | void * |
915 | | fz_process_opened_pages(fz_context *ctx, fz_document *doc, fz_process_opened_page_fn *process_opened_page, void *state) |
916 | 1.81k | { |
917 | 1.81k | fz_page *page; |
918 | 1.81k | fz_page *kept = NULL; |
919 | 1.81k | fz_page *dropme = NULL; |
920 | 1.81k | void *ret = NULL; |
921 | | |
922 | 1.81k | fz_var(kept); |
923 | 1.81k | fz_var(dropme); |
924 | 1.81k | fz_var(page); |
925 | 3.63k | fz_try(ctx) |
926 | 3.63k | { |
927 | | /* We can only walk the page list while the alloc lock is taken, so gymnastics are required. */ |
928 | | /* Loop invariant: at any point where we might throw, kept != NULL iff we are unlocked. */ |
929 | 1.81k | fz_lock(ctx, FZ_LOCK_ALLOC); |
930 | 1.81k | for (page = doc->open; ret == NULL && page != NULL; page = page->next) |
931 | 0 | { |
932 | | /* Keep an extra reference to the page so that no other thread can remove it. */ |
933 | 0 | kept = fz_keep_page_locked(ctx, page); |
934 | 0 | fz_unlock(ctx, FZ_LOCK_ALLOC); |
935 | | /* Drop any extra reference we might still have to a previous page. */ |
936 | 0 | fz_drop_page(ctx, dropme); |
937 | 0 | dropme = NULL; |
938 | |
|
939 | 0 | ret = process_opened_page(ctx, page, state); |
940 | | |
941 | | /* We can't drop kept here, because that would give us a race condition with |
942 | | * us taking the lock and hoping that 'page' would still be valid. So remember it |
943 | | * for dropping later. */ |
944 | 0 | dropme = kept; |
945 | 0 | kept = NULL; |
946 | 0 | fz_lock(ctx, FZ_LOCK_ALLOC); |
947 | 0 | } |
948 | | /* unlock (and final drop of dropme) happens in the always. */ |
949 | 1.81k | } |
950 | 3.63k | fz_always(ctx) |
951 | 1.81k | { |
952 | 1.81k | if (kept == NULL) |
953 | 1.81k | fz_unlock(ctx, FZ_LOCK_ALLOC); |
954 | 1.81k | fz_drop_page(ctx, kept); |
955 | 1.81k | fz_drop_page(ctx, dropme); |
956 | 1.81k | } |
957 | 1.81k | fz_catch(ctx) |
958 | 0 | { |
959 | 0 | fz_rethrow(ctx); |
960 | 0 | } |
961 | | |
962 | 1.81k | return ret; |
963 | 1.81k | } |
964 | | |
965 | | const char * |
966 | | fz_page_label(fz_context *ctx, fz_page *page, char *buf, int size) |
967 | 0 | { |
968 | 0 | fz_document *doc = page->doc; |
969 | 0 | if (doc->page_label) |
970 | 0 | doc->page_label(ctx, page->doc, page->chapter, page->number, buf, size); |
971 | 0 | else if (fz_count_chapters(ctx, page->doc) > 1) |
972 | 0 | fz_snprintf(buf, size, "%d/%d", page->chapter + 1, page->number + 1); |
973 | 0 | else |
974 | 0 | fz_snprintf(buf, size, "%d", page->number + 1); |
975 | 0 | return buf; |
976 | 0 | } |
977 | | |
978 | | |
979 | | fz_box_type fz_box_type_from_string(const char *name) |
980 | 0 | { |
981 | 0 | if (!fz_strcasecmp(name, "MediaBox")) |
982 | 0 | return FZ_MEDIA_BOX; |
983 | 0 | if (!fz_strcasecmp(name, "CropBox")) |
984 | 0 | return FZ_CROP_BOX; |
985 | 0 | if (!fz_strcasecmp(name, "BleedBox")) |
986 | 0 | return FZ_BLEED_BOX; |
987 | 0 | if (!fz_strcasecmp(name, "TrimBox")) |
988 | 0 | return FZ_TRIM_BOX; |
989 | 0 | if (!fz_strcasecmp(name, "ArtBox")) |
990 | 0 | return FZ_ART_BOX; |
991 | 0 | return FZ_UNKNOWN_BOX; |
992 | 0 | } |
993 | | |
994 | | const char *fz_string_from_box_type(fz_box_type box) |
995 | 0 | { |
996 | 0 | switch (box) |
997 | 0 | { |
998 | 0 | case FZ_MEDIA_BOX: return "MediaBox"; |
999 | 0 | case FZ_CROP_BOX: return "CropBox"; |
1000 | 0 | case FZ_BLEED_BOX: return "BleedBox"; |
1001 | 0 | case FZ_TRIM_BOX: return "TrimBox"; |
1002 | 0 | case FZ_ART_BOX: return "ArtBox"; |
1003 | 0 | default: return "UnknownBox"; |
1004 | 0 | } |
1005 | 0 | } |