/src/mupdf/source/pdf/pdf-page.c
Line | Count | Source |
1 | | // Copyright (C) 2004-2026 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 | | #include "pdf-annot-imp.h" |
25 | | #include "pdf-imp.h" |
26 | | |
27 | | #include <stdlib.h> |
28 | | #include <string.h> |
29 | | #include <limits.h> |
30 | | |
31 | | static void pdf_adjust_page_labels(fz_context *ctx, pdf_document *doc, int index, int adjust); |
32 | | |
33 | | void |
34 | | pdf_set_page_tree_cache(fz_context *ctx, pdf_document *doc, int enabled) |
35 | 0 | { |
36 | 0 | if (enabled) |
37 | 0 | { |
38 | 0 | doc->use_page_tree_map = 1; |
39 | 0 | } |
40 | 0 | else |
41 | 0 | { |
42 | 0 | pdf_drop_page_tree_internal(ctx, doc); |
43 | 0 | doc->use_page_tree_map = 0; |
44 | 0 | } |
45 | 0 | } |
46 | | |
47 | | int |
48 | | pdf_count_pages(fz_context *ctx, pdf_document *doc) |
49 | 1.10k | { |
50 | 1.10k | int pages; |
51 | 1.10k | if (doc->is_fdf) |
52 | 0 | return 0; |
53 | | /* FIXME: We should reset linear_page_count to 0 when editing starts |
54 | | * (or when linear loading ends) */ |
55 | 1.10k | if (doc->linear_page_count != 0) |
56 | 0 | pages = doc->linear_page_count; |
57 | 1.10k | else |
58 | 1.10k | pages = pdf_to_int(ctx, pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/Pages/Count")); |
59 | 1.10k | if (pages < 0) |
60 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "Invalid number of pages"); |
61 | | /* set an upper bound to cope with malicious /Count entries; as there |
62 | | * can't possibly be more pages than objects in the file! */ |
63 | 1.10k | if (pages >= pdf_xref_len(ctx, doc)) |
64 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "Invalid number of pages"); |
65 | 1.10k | return pages; |
66 | 1.10k | } |
67 | | |
68 | | int pdf_count_pages_imp(fz_context *ctx, fz_document *doc, int chapter) |
69 | 726 | { |
70 | 726 | return pdf_count_pages(ctx, (pdf_document*)doc); |
71 | 726 | } |
72 | | |
73 | | static int |
74 | | pdf_load_page_tree_imp(fz_context *ctx, pdf_document *doc, pdf_obj *node, int idx, pdf_cycle_list *cycle_up) |
75 | 400 | { |
76 | 400 | pdf_cycle_list cycle; |
77 | 400 | pdf_obj *type = pdf_dict_get(ctx, node, PDF_NAME(Type)); |
78 | 400 | if (pdf_name_eq(ctx, type, PDF_NAME(Pages))) |
79 | 111 | { |
80 | 111 | pdf_obj *kids = pdf_dict_get(ctx, node, PDF_NAME(Kids)); |
81 | 111 | int i, n = pdf_array_len(ctx, kids); |
82 | 111 | if (pdf_cycle(ctx, &cycle, cycle_up, node)) |
83 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "cycle in page tree"); |
84 | 454 | for (i = 0; i < n; ++i) |
85 | 343 | idx = pdf_load_page_tree_imp(ctx, doc, pdf_array_get(ctx, kids, i), idx, &cycle); |
86 | 111 | } |
87 | 289 | else if (pdf_name_eq(ctx, type, PDF_NAME(Page))) |
88 | 283 | { |
89 | 283 | if (idx >= doc->map_page_count) |
90 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "too many kids in page tree"); |
91 | 283 | doc->rev_page_map[idx].page = idx; |
92 | 283 | doc->rev_page_map[idx].object = pdf_to_num(ctx, node); |
93 | 283 | doc->fwd_page_map[idx] = pdf_keep_obj(ctx, node); |
94 | 283 | ++idx; |
95 | 283 | } |
96 | 6 | else |
97 | 6 | { |
98 | 6 | fz_throw(ctx, FZ_ERROR_FORMAT, "non-page object in page tree"); |
99 | 6 | } |
100 | 394 | return idx; |
101 | 400 | } |
102 | | |
103 | | static int |
104 | | cmp_rev_page_map(const void *va, const void *vb) |
105 | 459 | { |
106 | 459 | const pdf_rev_page_map *a = va; |
107 | 459 | const pdf_rev_page_map *b = vb; |
108 | 459 | return a->object - b->object; |
109 | 459 | } |
110 | | |
111 | | void |
112 | | pdf_load_page_tree(fz_context *ctx, pdf_document *doc) |
113 | 0 | { |
114 | | /* Noop now. */ |
115 | 0 | } |
116 | | |
117 | | void |
118 | | pdf_drop_page_tree_internal(fz_context *ctx, pdf_document *doc) |
119 | 160 | { |
120 | 160 | int i; |
121 | 160 | fz_free(ctx, doc->rev_page_map); |
122 | 160 | doc->rev_page_map = NULL; |
123 | 160 | if (doc->fwd_page_map) |
124 | 383 | for (i = 0; i < doc->map_page_count; i++) |
125 | 326 | pdf_drop_obj(ctx, doc->fwd_page_map[i]); |
126 | 160 | fz_free(ctx, doc->fwd_page_map); |
127 | 160 | doc->fwd_page_map = NULL; |
128 | 160 | doc->map_page_count = 0; |
129 | 160 | } |
130 | | |
131 | | static void |
132 | | pdf_load_page_tree_internal(fz_context *ctx, pdf_document *doc) |
133 | 57 | { |
134 | 57 | int in_op = 0; |
135 | | |
136 | 57 | fz_var(in_op); |
137 | | |
138 | | /* Check we're not already loaded. */ |
139 | 57 | if (doc->fwd_page_map != NULL) |
140 | 0 | return; |
141 | | |
142 | | /* At this point we're trusting that only 1 thread should be doing |
143 | | * stuff that hits the document at a time. */ |
144 | 114 | fz_try(ctx) |
145 | 114 | { |
146 | 57 | int idx; |
147 | | |
148 | 57 | doc->map_page_count = pdf_count_pages(ctx, doc); |
149 | 57 | while (1) |
150 | 57 | { |
151 | 57 | doc->rev_page_map = Memento_label(fz_calloc(ctx, doc->map_page_count, sizeof(pdf_rev_page_map)), "pdf_rev_page_map"); |
152 | 57 | doc->fwd_page_map = Memento_label(fz_calloc(ctx, doc->map_page_count, sizeof(pdf_obj *)), "pdf_fwd_page_map"); |
153 | 57 | idx = pdf_load_page_tree_imp(ctx, doc, pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/Pages"), 0, NULL); |
154 | 57 | if (idx < doc->map_page_count) |
155 | 0 | { |
156 | | /* The document claims more pages that it has. Fix that. */ |
157 | 0 | fz_warn(ctx, "Document claims to have %d pages, but only has %d.", doc->map_page_count, idx); |
158 | | /* This put drops the page tree! */ |
159 | 0 | pdf_begin_implicit_operation(ctx, doc); |
160 | 0 | in_op = 1; |
161 | 0 | pdf_dict_putp_drop(ctx, pdf_trailer(ctx, doc), "Root/Pages/Count", pdf_new_int(ctx, idx)); |
162 | 0 | doc->map_page_count = idx; |
163 | 0 | pdf_end_operation(ctx, doc); |
164 | 0 | in_op = 0; |
165 | 0 | continue; |
166 | 0 | } |
167 | 57 | break; |
168 | 57 | } |
169 | 57 | if (doc->map_page_count > 0) |
170 | 51 | qsort(doc->rev_page_map, doc->map_page_count, sizeof *doc->rev_page_map, cmp_rev_page_map); |
171 | 57 | } |
172 | 114 | fz_catch(ctx) |
173 | 6 | { |
174 | 6 | if (in_op) |
175 | 0 | pdf_abandon_operation(ctx, doc); |
176 | 6 | pdf_drop_page_tree_internal(ctx, doc); |
177 | 6 | fz_rethrow(ctx); |
178 | 6 | } |
179 | 57 | } |
180 | | |
181 | | void |
182 | | pdf_drop_page_tree(fz_context *ctx, pdf_document *doc) |
183 | 0 | { |
184 | | /* Historical entry point. Now does nothing. We drop 'just in time'. */ |
185 | 0 | } |
186 | | |
187 | | static pdf_obj * |
188 | | pdf_lookup_page_loc_imp(fz_context *ctx, pdf_document *doc, pdf_obj *node, int *skip, pdf_obj **parentp, int *indexp) |
189 | 58 | { |
190 | 58 | pdf_mark_list mark_list; |
191 | 58 | pdf_obj *kids; |
192 | 58 | pdf_obj *hit = NULL; |
193 | 58 | int i, len; |
194 | | |
195 | 58 | pdf_mark_list_init(ctx, &mark_list); |
196 | | |
197 | 116 | fz_try(ctx) |
198 | 116 | { |
199 | 58 | do |
200 | 114 | { |
201 | 114 | kids = pdf_dict_get(ctx, node, PDF_NAME(Kids)); |
202 | 114 | len = pdf_array_len(ctx, kids); |
203 | | |
204 | 114 | if (len == 0) |
205 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "malformed page tree"); |
206 | | |
207 | 114 | if (pdf_mark_list_push(ctx, &mark_list, node)) |
208 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "cycle in page tree"); |
209 | | |
210 | 278 | for (i = 0; i < len; i++) |
211 | 278 | { |
212 | 278 | pdf_obj *kid = pdf_array_get(ctx, kids, i); |
213 | 278 | pdf_obj *type = pdf_dict_get(ctx, kid, PDF_NAME(Type)); |
214 | 278 | if (type ? pdf_name_eq(ctx, type, PDF_NAME(Pages)) : pdf_dict_get(ctx, kid, PDF_NAME(Kids)) && !pdf_dict_get(ctx, kid, PDF_NAME(MediaBox))) |
215 | 96 | { |
216 | 96 | int count = pdf_dict_get_int(ctx, kid, PDF_NAME(Count)); |
217 | 96 | if (*skip < count) |
218 | 56 | { |
219 | 56 | node = kid; |
220 | 56 | break; |
221 | 56 | } |
222 | 40 | else |
223 | 40 | { |
224 | 40 | *skip -= count; |
225 | 40 | } |
226 | 96 | } |
227 | 182 | else |
228 | 182 | { |
229 | 182 | if (type ? !pdf_name_eq(ctx, type, PDF_NAME(Page)) : !pdf_dict_get(ctx, kid, PDF_NAME(MediaBox))) |
230 | 42 | fz_warn(ctx, "non-page object in page tree"); |
231 | 182 | if (*skip == 0) |
232 | 58 | { |
233 | 58 | if (parentp) *parentp = node; |
234 | 58 | if (indexp) *indexp = i; |
235 | 58 | hit = kid; |
236 | 58 | break; |
237 | 58 | } |
238 | 124 | else |
239 | 124 | { |
240 | 124 | (*skip)--; |
241 | 124 | } |
242 | 182 | } |
243 | 278 | } |
244 | 114 | } |
245 | | /* If i < len && hit != NULL the desired page was found in the |
246 | | Kids array, done. If i < len && hit == NULL the found page tree |
247 | | node contains a Kids array that contains the desired page, loop |
248 | | back to top to extract it. When i == len the Kids array has been |
249 | | exhausted without finding the desired page, give up. |
250 | | */ |
251 | 114 | while (hit == NULL && i < len); |
252 | 58 | } |
253 | 116 | fz_always(ctx) |
254 | 58 | { |
255 | 58 | pdf_mark_list_free(ctx, &mark_list); |
256 | 58 | } |
257 | 58 | fz_catch(ctx) |
258 | 0 | { |
259 | 0 | fz_rethrow(ctx); |
260 | 0 | } |
261 | | |
262 | 58 | return hit; |
263 | 58 | } |
264 | | |
265 | | pdf_obj * |
266 | | pdf_lookup_page_loc(fz_context *ctx, pdf_document *doc, int needle, pdf_obj **parentp, int *indexp) |
267 | 58 | { |
268 | 58 | pdf_obj *root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root)); |
269 | 58 | pdf_obj *node = pdf_dict_get(ctx, root, PDF_NAME(Pages)); |
270 | 58 | int skip = needle; |
271 | 58 | pdf_obj *hit; |
272 | | |
273 | 58 | if (!node) |
274 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "cannot find page tree"); |
275 | | |
276 | 58 | hit = pdf_lookup_page_loc_imp(ctx, doc, node, &skip, parentp, indexp); |
277 | 58 | if (!hit) |
278 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "cannot find page %d in page tree", needle+1); |
279 | 58 | return hit; |
280 | 58 | } |
281 | | |
282 | | pdf_obj * |
283 | | pdf_lookup_page_obj(fz_context *ctx, pdf_document *doc, int needle) |
284 | 326 | { |
285 | 326 | if (doc->fwd_page_map == NULL && doc->use_page_tree_map) |
286 | 57 | { |
287 | 114 | fz_try(ctx) |
288 | 114 | pdf_load_page_tree_internal(ctx, doc); |
289 | 114 | fz_catch(ctx) |
290 | 6 | { |
291 | 6 | doc->use_page_tree_map = 0; |
292 | 6 | fz_rethrow_if(ctx, FZ_ERROR_SYSTEM); |
293 | 6 | fz_report_error(ctx); |
294 | 6 | fz_warn(ctx, "page tree load failed; falling back to slow lookup"); |
295 | 6 | } |
296 | 57 | } |
297 | | |
298 | 326 | if (doc->fwd_page_map) |
299 | 268 | { |
300 | 268 | if (needle < 0 || needle >= doc->map_page_count) |
301 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "cannot find page %d in page tree", needle+1); |
302 | 268 | if (doc->fwd_page_map[needle] != NULL) |
303 | 268 | return doc->fwd_page_map[needle]; |
304 | 268 | } |
305 | | |
306 | 58 | return pdf_lookup_page_loc(ctx, doc, needle, NULL, NULL); |
307 | 326 | } |
308 | | |
309 | | static int |
310 | | pdf_count_pages_before_kid(fz_context *ctx, pdf_document *doc, pdf_obj *parent, int kid_num) |
311 | 0 | { |
312 | 0 | pdf_obj *kids = pdf_dict_get(ctx, parent, PDF_NAME(Kids)); |
313 | 0 | int i, total = 0, len = pdf_array_len(ctx, kids); |
314 | 0 | for (i = 0; i < len; i++) |
315 | 0 | { |
316 | 0 | pdf_obj *kid = pdf_array_get(ctx, kids, i); |
317 | 0 | if (pdf_to_num(ctx, kid) == kid_num) |
318 | 0 | return total; |
319 | 0 | if (pdf_name_eq(ctx, pdf_dict_get(ctx, kid, PDF_NAME(Type)), PDF_NAME(Pages))) |
320 | 0 | { |
321 | 0 | pdf_obj *count = pdf_dict_get(ctx, kid, PDF_NAME(Count)); |
322 | 0 | int n = pdf_to_int(ctx, count); |
323 | 0 | if (!pdf_is_int(ctx, count) || n < 0 || INT_MAX - total <= n) |
324 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "illegal or missing count in pages tree"); |
325 | 0 | total += n; |
326 | 0 | } |
327 | 0 | else |
328 | 0 | total++; |
329 | 0 | } |
330 | 0 | return -1; // the page we're looking for is not in the page tree (it has been deleted) |
331 | 0 | } |
332 | | |
333 | | static int |
334 | | pdf_lookup_page_number_slow(fz_context *ctx, pdf_document *doc, pdf_obj *node) |
335 | 0 | { |
336 | 0 | pdf_mark_list mark_list; |
337 | 0 | int needle = pdf_to_num(ctx, node); |
338 | 0 | int total = 0; |
339 | 0 | int n; |
340 | 0 | pdf_obj *parent; |
341 | |
|
342 | 0 | if (!pdf_name_eq(ctx, pdf_dict_get(ctx, node, PDF_NAME(Type)), PDF_NAME(Page))) |
343 | 0 | { |
344 | 0 | fz_warn(ctx, "invalid page object"); |
345 | 0 | return -1; |
346 | 0 | } |
347 | | |
348 | 0 | pdf_mark_list_init(ctx, &mark_list); |
349 | 0 | parent = pdf_dict_get(ctx, node, PDF_NAME(Parent)); |
350 | 0 | fz_try(ctx) |
351 | 0 | { |
352 | 0 | while (pdf_is_dict(ctx, parent)) |
353 | 0 | { |
354 | 0 | if (pdf_mark_list_push(ctx, &mark_list, parent)) |
355 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "cycle in page tree (parents)"); |
356 | 0 | n = pdf_count_pages_before_kid(ctx, doc, parent, needle); |
357 | | |
358 | | // Page was not found in page tree! |
359 | 0 | if (n < 0) |
360 | 0 | { |
361 | 0 | total = -1; |
362 | 0 | break; |
363 | 0 | } |
364 | 0 | if (INT_MAX - total <= n) |
365 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "illegal or missing count in pages tree"); |
366 | | |
367 | 0 | total += n; |
368 | 0 | needle = pdf_to_num(ctx, parent); |
369 | 0 | parent = pdf_dict_get(ctx, parent, PDF_NAME(Parent)); |
370 | 0 | } |
371 | 0 | } |
372 | 0 | fz_always(ctx) |
373 | 0 | pdf_mark_list_free(ctx, &mark_list); |
374 | 0 | fz_catch(ctx) |
375 | 0 | fz_rethrow(ctx); |
376 | | |
377 | 0 | return total; |
378 | 0 | } |
379 | | |
380 | | static int |
381 | | pdf_lookup_page_number_fast(fz_context *ctx, pdf_document *doc, int needle) |
382 | 0 | { |
383 | 0 | int l = 0; |
384 | 0 | int r = doc->map_page_count - 1; |
385 | 0 | while (l <= r) |
386 | 0 | { |
387 | 0 | int m = (l + r) >> 1; |
388 | 0 | int c = needle - doc->rev_page_map[m].object; |
389 | 0 | if (c < 0) |
390 | 0 | r = m - 1; |
391 | 0 | else if (c > 0) |
392 | 0 | l = m + 1; |
393 | 0 | else |
394 | 0 | return doc->rev_page_map[m].page; |
395 | 0 | } |
396 | 0 | return -1; |
397 | 0 | } |
398 | | |
399 | | int |
400 | | pdf_lookup_page_number(fz_context *ctx, pdf_document *doc, pdf_obj *page) |
401 | 0 | { |
402 | 0 | if (doc->rev_page_map == NULL && doc->use_page_tree_map) |
403 | 0 | { |
404 | 0 | fz_try(ctx) |
405 | 0 | pdf_load_page_tree_internal(ctx, doc); |
406 | 0 | fz_catch(ctx) |
407 | 0 | { |
408 | 0 | doc->use_page_tree_map = 0; |
409 | 0 | fz_report_error(ctx); |
410 | 0 | fz_warn(ctx, "page tree load failed; falling back to slow lookup"); |
411 | 0 | } |
412 | 0 | } |
413 | |
|
414 | 0 | if (doc->rev_page_map) |
415 | 0 | return pdf_lookup_page_number_fast(ctx, doc, pdf_to_num(ctx, page)); |
416 | 0 | else |
417 | 0 | return pdf_lookup_page_number_slow(ctx, doc, page); |
418 | 0 | } |
419 | | |
420 | | static void |
421 | | pdf_flatten_inheritable_page_item(fz_context *ctx, pdf_obj *page, pdf_obj *key) |
422 | 0 | { |
423 | 0 | pdf_obj *val = pdf_dict_get_inheritable(ctx, page, key); |
424 | 0 | if (val) |
425 | 0 | pdf_dict_put(ctx, page, key, val); |
426 | 0 | } |
427 | | |
428 | | void |
429 | | pdf_flatten_inheritable_page_items(fz_context *ctx, pdf_obj *page) |
430 | 0 | { |
431 | 0 | pdf_flatten_inheritable_page_item(ctx, page, PDF_NAME(MediaBox)); |
432 | 0 | pdf_flatten_inheritable_page_item(ctx, page, PDF_NAME(CropBox)); |
433 | 0 | pdf_flatten_inheritable_page_item(ctx, page, PDF_NAME(Rotate)); |
434 | 0 | pdf_flatten_inheritable_page_item(ctx, page, PDF_NAME(Resources)); |
435 | 0 | } |
436 | | |
437 | | /* We need to know whether to install a page-level transparency group */ |
438 | | |
439 | | /* |
440 | | * Object memo flags - allows us to secretly remember "a memo" (a bool) in an |
441 | | * object, and to read back whether there was a memo, and if so, what it was. |
442 | | */ |
443 | | enum |
444 | | { |
445 | | PDF_FLAGS_MEMO_BM = 0, |
446 | | PDF_FLAGS_MEMO_OP = 1 |
447 | | }; |
448 | | |
449 | | static int pdf_resources_use_blending(fz_context *ctx, pdf_obj *rdb, pdf_cycle_list *cycle_up); |
450 | | |
451 | | static int |
452 | | pdf_extgstate_uses_blending(fz_context *ctx, pdf_obj *dict) |
453 | 203 | { |
454 | 203 | pdf_obj *obj = pdf_dict_get(ctx, dict, PDF_NAME(BM)); |
455 | 203 | if (obj && !pdf_name_eq(ctx, obj, PDF_NAME(Normal))) |
456 | 18 | return 1; |
457 | 185 | return 0; |
458 | 203 | } |
459 | | |
460 | | int |
461 | | pdf_pattern_uses_blending(fz_context *ctx, pdf_obj *dict, pdf_cycle_list *cycle_up) |
462 | 1.02k | { |
463 | 1.02k | pdf_obj *obj; |
464 | 1.02k | pdf_cycle_list cycle; |
465 | 1.02k | if (pdf_cycle(ctx, &cycle, cycle_up, dict)) |
466 | 0 | return 0; |
467 | 1.02k | obj = pdf_dict_get(ctx, dict, PDF_NAME(Resources)); |
468 | 1.02k | if (pdf_resources_use_blending(ctx, obj, &cycle)) |
469 | 1.00k | return 1; |
470 | 25 | obj = pdf_dict_get(ctx, dict, PDF_NAME(ExtGState)); |
471 | 25 | return pdf_extgstate_uses_blending(ctx, obj); |
472 | 1.02k | } |
473 | | |
474 | | static int |
475 | | pdf_xobject_uses_blending(fz_context *ctx, pdf_obj *dict, pdf_cycle_list *cycle_up) |
476 | 1.36k | { |
477 | 1.36k | pdf_obj *obj = pdf_dict_get(ctx, dict, PDF_NAME(Resources)); |
478 | 1.36k | pdf_cycle_list cycle; |
479 | 1.36k | if (pdf_cycle(ctx, &cycle, cycle_up, dict)) |
480 | 0 | return 0; |
481 | 1.36k | if (pdf_name_eq(ctx, pdf_dict_getp(ctx, dict, "Group/S"), PDF_NAME(Transparency))) |
482 | 14 | return 1; |
483 | 1.35k | if (pdf_name_eq(ctx, pdf_dict_get(ctx, dict, PDF_NAME(Subtype)), PDF_NAME(Image)) && |
484 | 1.18k | pdf_dict_get(ctx, dict, PDF_NAME(SMask)) != NULL) |
485 | 1.01k | return 1; |
486 | 339 | return pdf_resources_use_blending(ctx, obj, &cycle); |
487 | 1.35k | } |
488 | | |
489 | | static int |
490 | | pdf_resources_use_blending(fz_context *ctx, pdf_obj *rdb, pdf_cycle_list *cycle_up) |
491 | 2.08k | { |
492 | 2.08k | pdf_cycle_list cycle; |
493 | 2.08k | pdf_obj *obj; |
494 | 2.08k | int i, n, useBM = 0; |
495 | | |
496 | 2.08k | if (!rdb) |
497 | 228 | return 0; |
498 | | |
499 | | /* Have we been here before and remembered an answer? */ |
500 | 1.85k | if (pdf_obj_memo(ctx, rdb, PDF_FLAGS_MEMO_BM, &useBM)) |
501 | 120 | return useBM; |
502 | | |
503 | | /* stop on cyclic resource dependencies */ |
504 | 1.73k | if (pdf_cycle(ctx, &cycle, cycle_up, rdb)) |
505 | 0 | return 0; |
506 | | |
507 | 1.73k | obj = pdf_dict_get(ctx, rdb, PDF_NAME(ExtGState)); |
508 | 1.73k | n = pdf_dict_len(ctx, obj); |
509 | 1.89k | for (i = 0; i < n; i++) |
510 | 178 | if (pdf_extgstate_uses_blending(ctx, pdf_dict_get_val(ctx, obj, i))) |
511 | 18 | goto found; |
512 | | |
513 | 1.71k | obj = pdf_dict_get(ctx, rdb, PDF_NAME(Pattern)); |
514 | 1.71k | n = pdf_dict_len(ctx, obj); |
515 | 1.72k | for (i = 0; i < n; i++) |
516 | 33 | if (pdf_pattern_uses_blending(ctx, pdf_dict_get_val(ctx, obj, i), &cycle)) |
517 | 22 | goto found; |
518 | | |
519 | 1.69k | obj = pdf_dict_get(ctx, rdb, PDF_NAME(XObject)); |
520 | 1.69k | n = pdf_dict_len(ctx, obj); |
521 | 1.97k | for (i = 0; i < n; i++) |
522 | 1.36k | if (pdf_xobject_uses_blending(ctx, pdf_dict_get_val(ctx, obj, i), &cycle)) |
523 | 1.08k | goto found; |
524 | 610 | if (0) |
525 | 0 | { |
526 | 1.12k | found: |
527 | 1.12k | useBM = 1; |
528 | 1.12k | } |
529 | | |
530 | 1.73k | pdf_set_obj_memo(ctx, rdb, PDF_FLAGS_MEMO_BM, useBM); |
531 | 1.73k | return useBM; |
532 | 610 | } |
533 | | |
534 | | static int pdf_resources_use_overprint(fz_context *ctx, pdf_obj *rdb, pdf_cycle_list *cycle_up); |
535 | | |
536 | | static int |
537 | | pdf_extgstate_uses_overprint(fz_context *ctx, pdf_obj *dict) |
538 | 2.08k | { |
539 | 2.08k | pdf_obj *obj = pdf_dict_get(ctx, dict, PDF_NAME(OP)); |
540 | 2.08k | if (obj && pdf_to_bool(ctx, obj)) |
541 | 8 | return 1; |
542 | 2.07k | return 0; |
543 | 2.08k | } |
544 | | |
545 | | static int |
546 | | pdf_pattern_uses_overprint(fz_context *ctx, pdf_obj *dict, pdf_cycle_list *cycle_up) |
547 | 1.13k | { |
548 | 1.13k | pdf_obj *obj; |
549 | 1.13k | pdf_cycle_list cycle; |
550 | 1.13k | if (pdf_cycle(ctx, &cycle, cycle_up, dict)) |
551 | 0 | return 0; |
552 | 1.13k | obj = pdf_dict_get(ctx, dict, PDF_NAME(Resources)); |
553 | 1.13k | if (pdf_resources_use_overprint(ctx, obj, &cycle)) |
554 | 0 | return 1; |
555 | 1.13k | obj = pdf_dict_get(ctx, dict, PDF_NAME(ExtGState)); |
556 | 1.13k | return pdf_extgstate_uses_overprint(ctx, obj); |
557 | 1.13k | } |
558 | | |
559 | | static int |
560 | | pdf_xobject_uses_overprint(fz_context *ctx, pdf_obj *dict, pdf_cycle_list *cycle_up) |
561 | 3.63k | { |
562 | 3.63k | pdf_obj *obj = pdf_dict_get(ctx, dict, PDF_NAME(Resources)); |
563 | 3.63k | pdf_cycle_list cycle; |
564 | 3.63k | if (pdf_cycle(ctx, &cycle, cycle_up, dict)) |
565 | 0 | return 0; |
566 | 3.63k | return pdf_resources_use_overprint(ctx, obj, &cycle); |
567 | 3.63k | } |
568 | | |
569 | | static int |
570 | | pdf_resources_use_overprint(fz_context *ctx, pdf_obj *rdb, pdf_cycle_list *cycle_up) |
571 | 5.48k | { |
572 | 5.48k | pdf_cycle_list cycle; |
573 | 5.48k | pdf_obj *obj; |
574 | 5.48k | int i, n, useOP = 0; |
575 | | |
576 | 5.48k | if (!rdb) |
577 | 3.00k | return 0; |
578 | | |
579 | | /* Have we been here before and remembered an answer? */ |
580 | 2.48k | if (pdf_obj_memo(ctx, rdb, PDF_FLAGS_MEMO_OP, &useOP)) |
581 | 272 | return useOP; |
582 | | |
583 | | /* stop on cyclic resource dependencies */ |
584 | 2.21k | if (pdf_cycle(ctx, &cycle, cycle_up, rdb)) |
585 | 0 | return 0; |
586 | | |
587 | 2.21k | obj = pdf_dict_get(ctx, rdb, PDF_NAME(ExtGState)); |
588 | 2.21k | n = pdf_dict_len(ctx, obj); |
589 | 3.15k | for (i = 0; i < n; i++) |
590 | 950 | if (pdf_extgstate_uses_overprint(ctx, pdf_dict_get_val(ctx, obj, i))) |
591 | 8 | goto found; |
592 | | |
593 | 2.20k | obj = pdf_dict_get(ctx, rdb, PDF_NAME(Pattern)); |
594 | 2.20k | n = pdf_dict_len(ctx, obj); |
595 | 3.33k | for (i = 0; i < n; i++) |
596 | 1.13k | if (pdf_pattern_uses_overprint(ctx, pdf_dict_get_val(ctx, obj, i), &cycle)) |
597 | 0 | goto found; |
598 | | |
599 | 2.20k | obj = pdf_dict_get(ctx, rdb, PDF_NAME(XObject)); |
600 | 2.20k | n = pdf_dict_len(ctx, obj); |
601 | 5.83k | for (i = 0; i < n; i++) |
602 | 3.63k | if (pdf_xobject_uses_overprint(ctx, pdf_dict_get_val(ctx, obj, i), &cycle)) |
603 | 0 | goto found; |
604 | 2.20k | if (0) |
605 | 0 | { |
606 | 8 | found: |
607 | 8 | useOP = 1; |
608 | 8 | } |
609 | | |
610 | 2.21k | pdf_set_obj_memo(ctx, rdb, PDF_FLAGS_MEMO_OP, useOP); |
611 | 2.21k | return useOP; |
612 | 2.20k | } |
613 | | |
614 | | fz_transition * |
615 | | pdf_page_presentation(fz_context *ctx, pdf_page *page, fz_transition *transition, float *duration) |
616 | 0 | { |
617 | 0 | pdf_obj *obj, *transdict; |
618 | |
|
619 | 0 | *duration = pdf_dict_get_real(ctx, page->obj, PDF_NAME(Dur)); |
620 | |
|
621 | 0 | transdict = pdf_dict_get(ctx, page->obj, PDF_NAME(Trans)); |
622 | 0 | if (!transdict) |
623 | 0 | return NULL; |
624 | | |
625 | 0 | obj = pdf_dict_get(ctx, transdict, PDF_NAME(D)); |
626 | |
|
627 | 0 | transition->duration = pdf_to_real_default(ctx, obj, 1); |
628 | |
|
629 | 0 | transition->vertical = !pdf_name_eq(ctx, pdf_dict_get(ctx, transdict, PDF_NAME(Dm)), PDF_NAME(H)); |
630 | 0 | transition->outwards = !pdf_name_eq(ctx, pdf_dict_get(ctx, transdict, PDF_NAME(M)), PDF_NAME(I)); |
631 | | /* FIXME: If 'Di' is None, it should be handled differently, but |
632 | | * this only affects Fly, and we don't implement that currently. */ |
633 | 0 | transition->direction = (pdf_dict_get_int(ctx, transdict, PDF_NAME(Di))); |
634 | | /* FIXME: Read SS for Fly when we implement it */ |
635 | | /* FIXME: Read B for Fly when we implement it */ |
636 | |
|
637 | 0 | obj = pdf_dict_get(ctx, transdict, PDF_NAME(S)); |
638 | 0 | if (pdf_name_eq(ctx, obj, PDF_NAME(Split))) |
639 | 0 | transition->type = FZ_TRANSITION_SPLIT; |
640 | 0 | else if (pdf_name_eq(ctx, obj, PDF_NAME(Blinds))) |
641 | 0 | transition->type = FZ_TRANSITION_BLINDS; |
642 | 0 | else if (pdf_name_eq(ctx, obj, PDF_NAME(Box))) |
643 | 0 | transition->type = FZ_TRANSITION_BOX; |
644 | 0 | else if (pdf_name_eq(ctx, obj, PDF_NAME(Wipe))) |
645 | 0 | transition->type = FZ_TRANSITION_WIPE; |
646 | 0 | else if (pdf_name_eq(ctx, obj, PDF_NAME(Dissolve))) |
647 | 0 | transition->type = FZ_TRANSITION_DISSOLVE; |
648 | 0 | else if (pdf_name_eq(ctx, obj, PDF_NAME(Glitter))) |
649 | 0 | transition->type = FZ_TRANSITION_GLITTER; |
650 | 0 | else if (pdf_name_eq(ctx, obj, PDF_NAME(Fly))) |
651 | 0 | transition->type = FZ_TRANSITION_FLY; |
652 | 0 | else if (pdf_name_eq(ctx, obj, PDF_NAME(Push))) |
653 | 0 | transition->type = FZ_TRANSITION_PUSH; |
654 | 0 | else if (pdf_name_eq(ctx, obj, PDF_NAME(Cover))) |
655 | 0 | transition->type = FZ_TRANSITION_COVER; |
656 | 0 | else if (pdf_name_eq(ctx, obj, PDF_NAME(Uncover))) |
657 | 0 | transition->type = FZ_TRANSITION_UNCOVER; |
658 | 0 | else if (pdf_name_eq(ctx, obj, PDF_NAME(Fade))) |
659 | 0 | transition->type = FZ_TRANSITION_FADE; |
660 | 0 | else |
661 | 0 | transition->type = FZ_TRANSITION_NONE; |
662 | |
|
663 | 0 | return transition; |
664 | 0 | } |
665 | | |
666 | | fz_rect |
667 | | pdf_bound_page(fz_context *ctx, pdf_page *page, fz_box_type box) |
668 | 326 | { |
669 | 326 | fz_matrix page_ctm; |
670 | 326 | fz_rect rect; |
671 | 326 | pdf_page_transform_box(ctx, page, &rect, &page_ctm, box); |
672 | 326 | return fz_transform_rect(rect, page_ctm); |
673 | 326 | } |
674 | | |
675 | | static fz_rect |
676 | | pdf_bound_page_imp(fz_context *ctx, fz_page *page, fz_box_type box) |
677 | 326 | { |
678 | 326 | return pdf_bound_page(ctx, (pdf_page*)page, box); |
679 | 326 | } |
680 | | |
681 | | void |
682 | | pdf_set_page_box(fz_context *ctx, pdf_page *page, fz_box_type box, fz_rect rect) |
683 | 0 | { |
684 | 0 | fz_matrix page_ctm, inv_page_ctm; |
685 | 0 | fz_rect page_rect; |
686 | 0 | pdf_page_transform_box(ctx, page, NULL, &page_ctm, box); |
687 | 0 | inv_page_ctm = fz_invert_matrix(page_ctm); |
688 | 0 | page_rect = fz_transform_rect(rect, inv_page_ctm); |
689 | |
|
690 | 0 | switch (box) |
691 | 0 | { |
692 | 0 | case FZ_MEDIA_BOX: |
693 | 0 | pdf_dict_put_rect(ctx, page->obj, PDF_NAME(MediaBox), page_rect); |
694 | 0 | break; |
695 | 0 | case FZ_CROP_BOX: |
696 | 0 | pdf_dict_put_rect(ctx, page->obj, PDF_NAME(CropBox), page_rect); |
697 | 0 | break; |
698 | 0 | case FZ_BLEED_BOX: |
699 | 0 | pdf_dict_put_rect(ctx, page->obj, PDF_NAME(BleedBox), page_rect); |
700 | 0 | break; |
701 | 0 | case FZ_TRIM_BOX: |
702 | 0 | pdf_dict_put_rect(ctx, page->obj, PDF_NAME(TrimBox), page_rect); |
703 | 0 | break; |
704 | 0 | case FZ_ART_BOX: |
705 | 0 | pdf_dict_put_rect(ctx, page->obj, PDF_NAME(ArtBox), page_rect); |
706 | 0 | break; |
707 | 0 | case FZ_UNKNOWN_BOX: |
708 | 0 | fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "unknown page box type: %d", box); |
709 | 0 | } |
710 | 0 | } |
711 | | |
712 | | fz_link * |
713 | | pdf_load_links(fz_context *ctx, pdf_page *page) |
714 | 0 | { |
715 | 0 | return fz_keep_link(ctx, page->links); |
716 | 0 | } |
717 | | |
718 | | static fz_link * |
719 | | pdf_load_links_imp(fz_context *ctx, fz_page *page) |
720 | 0 | { |
721 | 0 | return pdf_load_links(ctx, (pdf_page*)page); |
722 | 0 | } |
723 | | |
724 | | pdf_obj * |
725 | | pdf_page_resources(fz_context *ctx, pdf_page *page) |
726 | 1.86k | { |
727 | 1.86k | return pdf_dict_get_inheritable(ctx, page->obj, PDF_NAME(Resources)); |
728 | 1.86k | } |
729 | | |
730 | | pdf_obj * |
731 | | pdf_page_contents(fz_context *ctx, pdf_page *page) |
732 | 326 | { |
733 | 326 | return pdf_dict_get(ctx, page->obj, PDF_NAME(Contents)); |
734 | 326 | } |
735 | | |
736 | | pdf_obj * |
737 | | pdf_page_group(fz_context *ctx, pdf_page *page) |
738 | 90 | { |
739 | 90 | return pdf_dict_get(ctx, page->obj, PDF_NAME(Group)); |
740 | 90 | } |
741 | | |
742 | | void |
743 | | pdf_page_obj_transform_box(fz_context *ctx, pdf_obj *pageobj, fz_rect *outbox, fz_matrix *page_ctm, fz_box_type box) |
744 | 1.13k | { |
745 | 1.13k | pdf_obj *obj; |
746 | 1.13k | fz_rect usedbox, tempbox, cropbox, mediabox; |
747 | 1.13k | float userunit = 1; |
748 | 1.13k | int rotate; |
749 | | |
750 | 1.13k | if (!outbox) |
751 | 0 | outbox = &tempbox; |
752 | | |
753 | 1.13k | userunit = pdf_dict_get_real_default(ctx, pageobj, PDF_NAME(UserUnit), 1); |
754 | | |
755 | 1.13k | obj = pdf_dict_get_inheritable(ctx, pageobj, PDF_NAME(MediaBox)); |
756 | 1.13k | mediabox = pdf_to_rect(ctx, obj); |
757 | | |
758 | 1.13k | obj = NULL; |
759 | 1.13k | if (box == FZ_ART_BOX) |
760 | 0 | obj = pdf_dict_get_inheritable(ctx, pageobj, PDF_NAME(ArtBox)); |
761 | 1.13k | if (box == FZ_TRIM_BOX) |
762 | 0 | obj = pdf_dict_get_inheritable(ctx, pageobj, PDF_NAME(TrimBox)); |
763 | 1.13k | if (box == FZ_BLEED_BOX) |
764 | 0 | obj = pdf_dict_get_inheritable(ctx, pageobj, PDF_NAME(BleedBox)); |
765 | 1.13k | if (box == FZ_CROP_BOX || !obj) |
766 | 1.13k | obj = pdf_dict_get_inheritable(ctx, pageobj, PDF_NAME(CropBox)); |
767 | 1.13k | if (box == FZ_MEDIA_BOX || !obj) |
768 | 419 | usedbox = mediabox; |
769 | 711 | else |
770 | 711 | { |
771 | | // never use a box larger than fits the paper (mediabox) |
772 | 711 | usedbox = fz_intersect_rect(mediabox, pdf_to_rect(ctx, obj)); |
773 | 711 | } |
774 | | |
775 | 1.13k | if (fz_is_empty_rect(usedbox)) |
776 | 22 | usedbox = fz_make_rect(0, 0, 612, 792); |
777 | 1.13k | usedbox.x0 = fz_min(usedbox.x0, usedbox.x1); |
778 | 1.13k | usedbox.y0 = fz_min(usedbox.y0, usedbox.y1); |
779 | 1.13k | usedbox.x1 = fz_max(usedbox.x0, usedbox.x1); |
780 | 1.13k | usedbox.y1 = fz_max(usedbox.y0, usedbox.y1); |
781 | 1.13k | if (usedbox.x1 - usedbox.x0 < 1 || usedbox.y1 - usedbox.y0 < 1) |
782 | 0 | usedbox = fz_unit_rect; |
783 | | |
784 | 1.13k | *outbox = usedbox; |
785 | | |
786 | | /* Snap page rotation to 0, 90, 180 or 270 */ |
787 | 1.13k | rotate = pdf_dict_get_inheritable_int(ctx, pageobj, PDF_NAME(Rotate)); |
788 | 1.13k | if (rotate < 0) |
789 | 0 | rotate = 360 - ((-rotate) % 360); |
790 | 1.13k | if (rotate >= 360) |
791 | 0 | rotate = rotate % 360; |
792 | 1.13k | rotate = 90*((rotate + 45)/90); |
793 | 1.13k | if (rotate >= 360) |
794 | 0 | rotate = 0; |
795 | | |
796 | | /* Compute transform from fitz' page space (upper left page origin, y descending, 72 dpi) |
797 | | * to PDF user space (arbitrary page origin, y ascending, UserUnit dpi). */ |
798 | | |
799 | | /* Make left-handed and scale by UserUnit */ |
800 | 1.13k | *page_ctm = fz_scale(userunit, -userunit); |
801 | | |
802 | | /* Rotate */ |
803 | 1.13k | *page_ctm = fz_pre_rotate(*page_ctm, -rotate); |
804 | | |
805 | | /* Always use CropBox to set origin to top left */ |
806 | 1.13k | obj = pdf_dict_get_inheritable(ctx, pageobj, PDF_NAME(CropBox)); |
807 | 1.13k | if (!pdf_is_array(ctx, obj)) |
808 | 419 | obj = pdf_dict_get_inheritable(ctx, pageobj, PDF_NAME(MediaBox)); |
809 | 1.13k | cropbox = pdf_to_rect(ctx, obj); |
810 | 1.13k | cropbox = fz_intersect_rect(cropbox, mediabox); |
811 | 1.13k | if (fz_is_empty_rect(cropbox)) |
812 | 22 | cropbox = fz_make_rect(0, 0, 612, 792); |
813 | 1.13k | cropbox.x0 = fz_min(cropbox.x0, cropbox.x1); |
814 | 1.13k | cropbox.y0 = fz_min(cropbox.y0, cropbox.y1); |
815 | 1.13k | cropbox.x1 = fz_max(cropbox.x0, cropbox.x1); |
816 | 1.13k | cropbox.y1 = fz_max(cropbox.y0, cropbox.y1); |
817 | 1.13k | if (cropbox.x1 - cropbox.x0 < 1 || cropbox.y1 - cropbox.y0 < 1) |
818 | 0 | cropbox = fz_unit_rect; |
819 | | |
820 | | /* Translate page origin of CropBox to 0,0 */ |
821 | 1.13k | cropbox = fz_transform_rect(cropbox, *page_ctm); |
822 | 1.13k | *page_ctm = fz_concat(*page_ctm, fz_translate(-cropbox.x0, -cropbox.y0)); |
823 | 1.13k | } |
824 | | |
825 | | void |
826 | | pdf_page_obj_transform(fz_context *ctx, pdf_obj *pageobj, fz_rect *page_cropbox, fz_matrix *page_ctm) |
827 | 0 | { |
828 | 0 | pdf_page_obj_transform_box(ctx, pageobj, page_cropbox, page_ctm, FZ_CROP_BOX); |
829 | 0 | } |
830 | | |
831 | | void |
832 | | pdf_page_transform_box(fz_context *ctx, pdf_page *page, fz_rect *page_cropbox, fz_matrix *page_ctm, fz_box_type box) |
833 | 1.13k | { |
834 | 1.13k | pdf_page_obj_transform_box(ctx, page->obj, page_cropbox, page_ctm, box); |
835 | 1.13k | } |
836 | | |
837 | | void |
838 | | pdf_page_transform(fz_context *ctx, pdf_page *page, fz_rect *cropbox, fz_matrix *ctm) |
839 | 804 | { |
840 | 804 | pdf_page_transform_box(ctx, page, cropbox, ctm, FZ_CROP_BOX); |
841 | 804 | } |
842 | | |
843 | | static void |
844 | | find_seps(fz_context *ctx, fz_separations **seps, pdf_obj *obj, pdf_mark_list *clearme) |
845 | 0 | { |
846 | 0 | int i, n; |
847 | 0 | pdf_obj *nameobj, *cols; |
848 | |
|
849 | 0 | if (!obj) |
850 | 0 | return; |
851 | | |
852 | | // Already seen this ColorSpace... |
853 | 0 | if (pdf_mark_list_push(ctx, clearme, obj)) |
854 | 0 | return; |
855 | | |
856 | 0 | nameobj = pdf_array_get(ctx, obj, 0); |
857 | 0 | if (pdf_name_eq(ctx, nameobj, PDF_NAME(Separation))) |
858 | 0 | { |
859 | 0 | fz_colorspace *cs; |
860 | 0 | const char *name = pdf_array_get_name(ctx, obj, 1); |
861 | | |
862 | | /* Skip 'special' colorants. */ |
863 | 0 | if (!strcmp(name, "Black") || |
864 | 0 | !strcmp(name, "Cyan") || |
865 | 0 | !strcmp(name, "Magenta") || |
866 | 0 | !strcmp(name, "Yellow") || |
867 | 0 | !strcmp(name, "All") || |
868 | 0 | !strcmp(name, "None")) |
869 | 0 | return; |
870 | | |
871 | 0 | n = fz_count_separations(ctx, *seps); |
872 | 0 | for (i = 0; i < n; i++) |
873 | 0 | { |
874 | 0 | if (!strcmp(name, fz_separation_name(ctx, *seps, i))) |
875 | 0 | return; /* Got that one already */ |
876 | 0 | } |
877 | | |
878 | 0 | fz_try(ctx) |
879 | 0 | cs = pdf_load_colorspace(ctx, obj); |
880 | 0 | fz_catch(ctx) |
881 | 0 | { |
882 | 0 | fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); |
883 | 0 | fz_rethrow_if(ctx, FZ_ERROR_SYSTEM); |
884 | 0 | fz_report_error(ctx); |
885 | 0 | return; /* ignore broken colorspace */ |
886 | 0 | } |
887 | 0 | fz_try(ctx) |
888 | 0 | { |
889 | 0 | if (!*seps) |
890 | 0 | *seps = fz_new_separations(ctx, 0); |
891 | 0 | fz_add_separation(ctx, *seps, name, cs, 0); |
892 | 0 | } |
893 | 0 | fz_always(ctx) |
894 | 0 | fz_drop_colorspace(ctx, cs); |
895 | 0 | fz_catch(ctx) |
896 | 0 | fz_rethrow(ctx); |
897 | 0 | } |
898 | 0 | else if (pdf_name_eq(ctx, nameobj, PDF_NAME(Indexed))) |
899 | 0 | { |
900 | 0 | find_seps(ctx, seps, pdf_array_get(ctx, obj, 1), clearme); |
901 | 0 | } |
902 | 0 | else if (pdf_name_eq(ctx, nameobj, PDF_NAME(DeviceN))) |
903 | 0 | { |
904 | | /* If the separation colorants exists for this DeviceN color space |
905 | | * add those prior to our search for DeviceN color */ |
906 | 0 | cols = pdf_dict_get(ctx, pdf_array_get(ctx, obj, 4), PDF_NAME(Colorants)); |
907 | 0 | n = pdf_dict_len(ctx, cols); |
908 | 0 | for (i = 0; i < n; i++) |
909 | 0 | find_seps(ctx, seps, pdf_dict_get_val(ctx, cols, i), clearme); |
910 | 0 | } |
911 | 0 | } |
912 | | |
913 | | static void |
914 | | find_devn(fz_context *ctx, fz_separations **seps, pdf_obj *obj, pdf_mark_list *clearme) |
915 | 0 | { |
916 | 0 | int i, j, n, m; |
917 | 0 | pdf_obj *arr; |
918 | 0 | pdf_obj *nameobj = pdf_array_get(ctx, obj, 0); |
919 | |
|
920 | 0 | if (!obj) |
921 | 0 | return; |
922 | | |
923 | | // Already seen this ColorSpace... |
924 | 0 | if (pdf_mark_list_push(ctx, clearme, obj)) |
925 | 0 | return; |
926 | | |
927 | 0 | if (!pdf_name_eq(ctx, nameobj, PDF_NAME(DeviceN))) |
928 | 0 | return; |
929 | | |
930 | 0 | arr = pdf_array_get(ctx, obj, 1); |
931 | 0 | m = pdf_array_len(ctx, arr); |
932 | 0 | for (j = 0; j < m; j++) |
933 | 0 | { |
934 | 0 | fz_colorspace *cs; |
935 | 0 | const char *name = pdf_array_get_name(ctx, arr, j); |
936 | | |
937 | | /* Skip 'special' colorants. */ |
938 | 0 | if (!strcmp(name, "Black") || |
939 | 0 | !strcmp(name, "Cyan") || |
940 | 0 | !strcmp(name, "Magenta") || |
941 | 0 | !strcmp(name, "Yellow") || |
942 | 0 | !strcmp(name, "All") || |
943 | 0 | !strcmp(name, "None")) |
944 | 0 | continue; |
945 | | |
946 | 0 | n = fz_count_separations(ctx, *seps); |
947 | 0 | for (i = 0; i < n; i++) |
948 | 0 | { |
949 | 0 | if (!strcmp(name, fz_separation_name(ctx, *seps, i))) |
950 | 0 | break; /* Got that one already */ |
951 | 0 | } |
952 | |
|
953 | 0 | if (i == n) |
954 | 0 | { |
955 | 0 | fz_try(ctx) |
956 | 0 | cs = pdf_load_colorspace(ctx, obj); |
957 | 0 | fz_catch(ctx) |
958 | 0 | { |
959 | 0 | fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); |
960 | 0 | fz_rethrow_if(ctx, FZ_ERROR_SYSTEM); |
961 | 0 | fz_report_error(ctx); |
962 | 0 | continue; /* ignore broken colorspace */ |
963 | 0 | } |
964 | 0 | fz_try(ctx) |
965 | 0 | { |
966 | 0 | if (!*seps) |
967 | 0 | *seps = fz_new_separations(ctx, 0); |
968 | 0 | fz_add_separation(ctx, *seps, name, cs, j); |
969 | 0 | } |
970 | 0 | fz_always(ctx) |
971 | 0 | fz_drop_colorspace(ctx, cs); |
972 | 0 | fz_catch(ctx) |
973 | 0 | fz_rethrow(ctx); |
974 | 0 | } |
975 | 0 | } |
976 | 0 | } |
977 | | |
978 | | typedef void (res_finder_fn)(fz_context *ctx, fz_separations **seps, pdf_obj *obj, pdf_mark_list *clearme); |
979 | | |
980 | | static void |
981 | | scan_page_seps(fz_context *ctx, pdf_obj *res, fz_separations **seps, res_finder_fn *fn, pdf_mark_list *clearme) |
982 | 0 | { |
983 | 0 | pdf_obj *dict; |
984 | 0 | pdf_obj *obj; |
985 | 0 | int i, n; |
986 | |
|
987 | 0 | if (!res) |
988 | 0 | return; |
989 | | |
990 | | // Already seen this Resources... |
991 | 0 | if (pdf_mark_list_push(ctx, clearme, res)) |
992 | 0 | return; |
993 | | |
994 | 0 | dict = pdf_dict_get(ctx, res, PDF_NAME(ColorSpace)); |
995 | 0 | n = pdf_dict_len(ctx, dict); |
996 | 0 | for (i = 0; i < n; i++) |
997 | 0 | { |
998 | 0 | obj = pdf_dict_get_val(ctx, dict, i); |
999 | 0 | fn(ctx, seps, obj, clearme); |
1000 | 0 | } |
1001 | |
|
1002 | 0 | dict = pdf_dict_get(ctx, res, PDF_NAME(Shading)); |
1003 | 0 | n = pdf_dict_len(ctx, dict); |
1004 | 0 | for (i = 0; i < n; i++) |
1005 | 0 | { |
1006 | 0 | obj = pdf_dict_get_val(ctx, dict, i); |
1007 | 0 | fn(ctx, seps, pdf_dict_get(ctx, obj, PDF_NAME(ColorSpace)), clearme); |
1008 | 0 | } |
1009 | |
|
1010 | 0 | dict = pdf_dict_get(ctx, res, PDF_NAME(Pattern)); |
1011 | 0 | n = pdf_dict_len(ctx, dict); |
1012 | 0 | for (i = 0; i < n; i++) |
1013 | 0 | { |
1014 | 0 | pdf_obj *obj2; |
1015 | 0 | obj = pdf_dict_get_val(ctx, dict, i); |
1016 | 0 | obj2 = pdf_dict_get(ctx, obj, PDF_NAME(Shading)); |
1017 | 0 | fn(ctx, seps, pdf_dict_get(ctx, obj2, PDF_NAME(ColorSpace)), clearme); |
1018 | 0 | } |
1019 | |
|
1020 | 0 | dict = pdf_dict_get(ctx, res, PDF_NAME(XObject)); |
1021 | 0 | n = pdf_dict_len(ctx, dict); |
1022 | 0 | for (i = 0; i < n; i++) |
1023 | 0 | { |
1024 | 0 | obj = pdf_dict_get_val(ctx, dict, i); |
1025 | | // Already seen this XObject... |
1026 | 0 | if (!pdf_mark_list_push(ctx, clearme, obj)) |
1027 | 0 | { |
1028 | 0 | fn(ctx, seps, pdf_dict_get(ctx, obj, PDF_NAME(ColorSpace)), clearme); |
1029 | | /* Recurse on XObject forms. */ |
1030 | 0 | scan_page_seps(ctx, pdf_dict_get(ctx, obj, PDF_NAME(Resources)), seps, fn, clearme); |
1031 | 0 | } |
1032 | 0 | } |
1033 | 0 | } |
1034 | | |
1035 | | fz_separations * |
1036 | | pdf_page_separations(fz_context *ctx, pdf_page *page) |
1037 | 0 | { |
1038 | 0 | pdf_obj *res = pdf_page_resources(ctx, page); |
1039 | 0 | pdf_mark_list clearme; |
1040 | 0 | fz_separations *seps = NULL; |
1041 | |
|
1042 | 0 | pdf_mark_list_init(ctx, &clearme); |
1043 | 0 | fz_try(ctx) |
1044 | 0 | { |
1045 | | /* Run through and look for separations first. This is |
1046 | | * because separations are simplest to deal with, and |
1047 | | * because DeviceN may be implemented on top of separations. |
1048 | | */ |
1049 | 0 | scan_page_seps(ctx, res, &seps, find_seps, &clearme); |
1050 | 0 | } |
1051 | 0 | fz_always(ctx) |
1052 | 0 | pdf_mark_list_free(ctx, &clearme); |
1053 | 0 | fz_catch(ctx) |
1054 | 0 | { |
1055 | 0 | fz_drop_separations(ctx, seps); |
1056 | 0 | fz_rethrow(ctx); |
1057 | 0 | } |
1058 | | |
1059 | 0 | pdf_mark_list_init(ctx, &clearme); |
1060 | 0 | fz_try(ctx) |
1061 | 0 | { |
1062 | | /* Now run through again, and look for DeviceNs. These may |
1063 | | * have spot colors in that aren't defined in terms of |
1064 | | * separations. */ |
1065 | 0 | scan_page_seps(ctx, res, &seps, find_devn, &clearme); |
1066 | 0 | } |
1067 | 0 | fz_always(ctx) |
1068 | 0 | pdf_mark_list_free(ctx, &clearme); |
1069 | 0 | fz_catch(ctx) |
1070 | 0 | { |
1071 | 0 | fz_drop_separations(ctx, seps); |
1072 | 0 | fz_rethrow(ctx); |
1073 | 0 | } |
1074 | | |
1075 | 0 | return seps; |
1076 | 0 | } |
1077 | | |
1078 | | int |
1079 | | pdf_page_uses_overprint(fz_context *ctx, pdf_page *page) |
1080 | 0 | { |
1081 | 0 | return page ? page->overprint : 0; |
1082 | 0 | } |
1083 | | |
1084 | | static void |
1085 | | pdf_drop_page_imp(fz_context *ctx, fz_page *page_) |
1086 | 326 | { |
1087 | 326 | pdf_page *page = (pdf_page*)page_; |
1088 | 326 | pdf_annot *widget; |
1089 | 326 | pdf_annot *annot; |
1090 | 326 | pdf_link *link; |
1091 | | |
1092 | 326 | link = (pdf_link *) page->links; |
1093 | 326 | while (link) |
1094 | 0 | { |
1095 | 0 | link->page = NULL; |
1096 | 0 | link = (pdf_link *) link->super.next; |
1097 | 0 | } |
1098 | 326 | fz_drop_link(ctx, page->links); |
1099 | 326 | page->links = NULL; |
1100 | | |
1101 | 326 | annot = page->annots; |
1102 | 411 | while (annot) |
1103 | 85 | { |
1104 | 85 | annot->page = NULL; |
1105 | 85 | annot = annot->next; |
1106 | 85 | } |
1107 | 326 | pdf_drop_annots(ctx, page->annots); |
1108 | 326 | page->annots = NULL; |
1109 | | |
1110 | 326 | widget = page->widgets; |
1111 | 686 | while (widget) |
1112 | 360 | { |
1113 | 360 | widget->page = NULL; |
1114 | 360 | widget = widget->next; |
1115 | 360 | } |
1116 | 326 | pdf_drop_widgets(ctx, page->widgets); |
1117 | 326 | page->widgets = NULL; |
1118 | 326 | pdf_drop_obj(ctx, page->obj); |
1119 | 326 | page->obj = NULL; |
1120 | 326 | page->doc = NULL; |
1121 | 326 | } |
1122 | | |
1123 | | static void pdf_run_page_contents_imp(fz_context *ctx, fz_page *page, fz_device *dev, fz_matrix ctm, fz_cookie *cookie) |
1124 | 326 | { |
1125 | 326 | pdf_run_page_contents(ctx, (pdf_page*)page, dev, ctm, cookie); |
1126 | 326 | } |
1127 | | |
1128 | | static void pdf_run_page_annots_imp(fz_context *ctx, fz_page *page, fz_device *dev, fz_matrix ctm, fz_cookie *cookie) |
1129 | 322 | { |
1130 | 322 | pdf_run_page_annots(ctx, (pdf_page*)page, dev, ctm, cookie); |
1131 | 322 | } |
1132 | | |
1133 | | static void pdf_run_page_widgets_imp(fz_context *ctx, fz_page *page, fz_device *dev, fz_matrix ctm, fz_cookie *cookie) |
1134 | 322 | { |
1135 | 322 | pdf_run_page_widgets(ctx, (pdf_page*)page, dev, ctm, cookie); |
1136 | 322 | } |
1137 | | |
1138 | | static fz_transition * pdf_page_presentation_imp(fz_context *ctx, fz_page *page, fz_transition *transition, float *duration) |
1139 | 0 | { |
1140 | 0 | return pdf_page_presentation(ctx, (pdf_page*)page, transition, duration); |
1141 | 0 | } |
1142 | | |
1143 | | static fz_separations * pdf_page_separations_imp(fz_context *ctx, fz_page *page) |
1144 | 0 | { |
1145 | 0 | return pdf_page_separations(ctx, (pdf_page*)page); |
1146 | 0 | } |
1147 | | |
1148 | | static int pdf_page_uses_overprint_imp(fz_context *ctx, fz_page *page) |
1149 | 0 | { |
1150 | 0 | return pdf_page_uses_overprint(ctx, (pdf_page*)page); |
1151 | 0 | } |
1152 | | |
1153 | | static fz_link * pdf_create_link_imp(fz_context *ctx, fz_page *page, fz_rect bbox, const char *uri) |
1154 | 0 | { |
1155 | 0 | return pdf_create_link(ctx, (pdf_page*)page, bbox, uri); |
1156 | 0 | } |
1157 | | |
1158 | | static void pdf_delete_link_imp(fz_context *ctx, fz_page *page, fz_link *link) |
1159 | 0 | { |
1160 | 0 | pdf_delete_link(ctx, (pdf_page*)page, link); |
1161 | 0 | } |
1162 | | |
1163 | | static pdf_page * |
1164 | | pdf_new_page(fz_context *ctx, pdf_document *doc) |
1165 | 326 | { |
1166 | 326 | pdf_page *page = fz_new_derived_page(ctx, pdf_page, (fz_document*) doc); |
1167 | | |
1168 | 326 | page->doc = doc; /* typecast alias for page->super.doc */ |
1169 | | |
1170 | 326 | page->super.drop_page = pdf_drop_page_imp; |
1171 | 326 | page->super.load_links = pdf_load_links_imp; |
1172 | 326 | page->super.bound_page = pdf_bound_page_imp; |
1173 | 326 | page->super.run_page_contents = pdf_run_page_contents_imp; |
1174 | 326 | page->super.run_page_annots = pdf_run_page_annots_imp; |
1175 | 326 | page->super.run_page_widgets = pdf_run_page_widgets_imp; |
1176 | 326 | page->super.page_presentation = pdf_page_presentation_imp; |
1177 | 326 | page->super.separations = pdf_page_separations_imp; |
1178 | 326 | page->super.overprint = pdf_page_uses_overprint_imp; |
1179 | 326 | page->super.create_link = pdf_create_link_imp; |
1180 | 326 | page->super.delete_link = pdf_delete_link_imp; |
1181 | | |
1182 | 326 | page->obj = NULL; |
1183 | | |
1184 | 326 | page->transparency = 0; |
1185 | 326 | page->links = NULL; |
1186 | 326 | page->annots = NULL; |
1187 | 326 | page->annot_tailp = &page->annots; |
1188 | 326 | page->widgets = NULL; |
1189 | 326 | page->widget_tailp = &page->widgets; |
1190 | | |
1191 | 326 | return page; |
1192 | 326 | } |
1193 | | |
1194 | | static void |
1195 | | pdf_load_default_colorspaces_imp(fz_context *ctx, fz_default_colorspaces *default_cs, pdf_obj *obj) |
1196 | 711 | { |
1197 | 711 | pdf_obj *cs_obj; |
1198 | | |
1199 | | /* The spec says to ignore any colors we can't understand */ |
1200 | | |
1201 | 711 | cs_obj = pdf_dict_get(ctx, obj, PDF_NAME(DefaultGray)); |
1202 | 711 | if (cs_obj) |
1203 | 0 | { |
1204 | 0 | fz_try(ctx) |
1205 | 0 | { |
1206 | 0 | fz_colorspace *cs = pdf_load_colorspace(ctx, cs_obj); |
1207 | 0 | fz_set_default_gray(ctx, default_cs, cs); |
1208 | 0 | fz_drop_colorspace(ctx, cs); |
1209 | 0 | } |
1210 | 0 | fz_catch(ctx) |
1211 | 0 | { |
1212 | 0 | fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); |
1213 | 0 | fz_rethrow_if(ctx, FZ_ERROR_SYSTEM); |
1214 | 0 | fz_report_error(ctx); |
1215 | 0 | } |
1216 | 0 | } |
1217 | | |
1218 | 711 | cs_obj = pdf_dict_get(ctx, obj, PDF_NAME(DefaultRGB)); |
1219 | 711 | if (cs_obj) |
1220 | 0 | { |
1221 | 0 | fz_try(ctx) |
1222 | 0 | { |
1223 | 0 | fz_colorspace *cs = pdf_load_colorspace(ctx, cs_obj); |
1224 | 0 | fz_set_default_rgb(ctx, default_cs, cs); |
1225 | 0 | fz_drop_colorspace(ctx, cs); |
1226 | 0 | } |
1227 | 0 | fz_catch(ctx) |
1228 | 0 | { |
1229 | 0 | fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); |
1230 | 0 | fz_rethrow_if(ctx, FZ_ERROR_SYSTEM); |
1231 | 0 | fz_report_error(ctx); |
1232 | 0 | } |
1233 | 0 | } |
1234 | | |
1235 | 711 | cs_obj = pdf_dict_get(ctx, obj, PDF_NAME(DefaultCMYK)); |
1236 | 711 | if (cs_obj) |
1237 | 0 | { |
1238 | 0 | fz_try(ctx) |
1239 | 0 | { |
1240 | 0 | fz_colorspace *cs = pdf_load_colorspace(ctx, cs_obj); |
1241 | 0 | fz_set_default_cmyk(ctx, default_cs, cs); |
1242 | 0 | fz_drop_colorspace(ctx, cs); |
1243 | 0 | } |
1244 | 0 | fz_catch(ctx) |
1245 | 0 | { |
1246 | 0 | fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); |
1247 | 0 | fz_rethrow_if(ctx, FZ_ERROR_SYSTEM); |
1248 | 0 | fz_report_error(ctx); |
1249 | 0 | } |
1250 | 0 | } |
1251 | 711 | } |
1252 | | |
1253 | | fz_default_colorspaces * |
1254 | | pdf_load_default_colorspaces(fz_context *ctx, pdf_document *doc, pdf_page *page) |
1255 | 771 | { |
1256 | 771 | pdf_obj *res; |
1257 | 771 | pdf_obj *obj; |
1258 | 771 | fz_default_colorspaces *default_cs; |
1259 | 771 | fz_colorspace *oi; |
1260 | | |
1261 | 771 | default_cs = fz_new_default_colorspaces(ctx); |
1262 | | |
1263 | 1.54k | fz_try(ctx) |
1264 | 1.54k | { |
1265 | 771 | res = pdf_page_resources(ctx, page); |
1266 | 771 | obj = pdf_dict_get(ctx, res, PDF_NAME(ColorSpace)); |
1267 | 771 | if (obj) |
1268 | 375 | pdf_load_default_colorspaces_imp(ctx, default_cs, obj); |
1269 | | |
1270 | 771 | oi = pdf_document_output_intent(ctx, doc); |
1271 | 771 | if (oi) |
1272 | 1 | fz_set_default_output_intent(ctx, default_cs, oi); |
1273 | 771 | } |
1274 | 1.54k | fz_catch(ctx) |
1275 | 0 | { |
1276 | 0 | if (fz_caught(ctx) != FZ_ERROR_TRYLATER) |
1277 | 0 | { |
1278 | 0 | fz_drop_default_colorspaces(ctx, default_cs); |
1279 | 0 | fz_rethrow(ctx); |
1280 | 0 | } |
1281 | 0 | fz_ignore_error(ctx); |
1282 | 0 | page->super.incomplete = 1; |
1283 | 0 | } |
1284 | | |
1285 | 771 | return default_cs; |
1286 | 771 | } |
1287 | | |
1288 | | fz_default_colorspaces * |
1289 | | pdf_update_default_colorspaces(fz_context *ctx, fz_default_colorspaces *old_cs, pdf_obj *res) |
1290 | 4.69k | { |
1291 | 4.69k | pdf_obj *obj; |
1292 | 4.69k | fz_default_colorspaces *new_cs; |
1293 | | |
1294 | 4.69k | obj = pdf_dict_get(ctx, res, PDF_NAME(ColorSpace)); |
1295 | 4.69k | if (!obj) |
1296 | 4.35k | return fz_keep_default_colorspaces(ctx, old_cs); |
1297 | | |
1298 | 336 | new_cs = fz_clone_default_colorspaces(ctx, old_cs); |
1299 | 672 | fz_try(ctx) |
1300 | 672 | pdf_load_default_colorspaces_imp(ctx, new_cs, obj); |
1301 | 672 | fz_catch(ctx) |
1302 | 0 | { |
1303 | 0 | fz_drop_default_colorspaces(ctx, new_cs); |
1304 | 0 | fz_rethrow(ctx); |
1305 | 0 | } |
1306 | | |
1307 | 336 | return new_cs; |
1308 | 336 | } |
1309 | | |
1310 | | void pdf_nuke_page(fz_context *ctx, pdf_page *page) |
1311 | 0 | { |
1312 | 0 | pdf_nuke_links(ctx, page); |
1313 | 0 | pdf_nuke_annots(ctx, page); |
1314 | 0 | pdf_drop_obj(ctx, page->obj); |
1315 | 0 | page->obj = NULL; |
1316 | 0 | page->super.in_doc = 0; |
1317 | 0 | } |
1318 | | |
1319 | | void pdf_sync_page(fz_context *ctx, pdf_page *page) |
1320 | 0 | { |
1321 | 0 | pdf_sync_links(ctx, page); |
1322 | 0 | pdf_sync_annots(ctx, page); |
1323 | 0 | } |
1324 | | |
1325 | | void pdf_sync_open_pages(fz_context *ctx, pdf_document *doc) |
1326 | 0 | { |
1327 | 0 | fz_page *page, *next; |
1328 | 0 | pdf_page *ppage; |
1329 | 0 | int number; |
1330 | |
|
1331 | 0 | for (page = doc->super.open; page != NULL; page = next) |
1332 | 0 | { |
1333 | 0 | next = page->next; |
1334 | 0 | if (page->doc == NULL) |
1335 | 0 | continue; |
1336 | 0 | ppage = (pdf_page*)page; |
1337 | 0 | number = pdf_lookup_page_number(ctx, doc, ppage->obj); |
1338 | 0 | if (number < 0) |
1339 | 0 | { |
1340 | 0 | pdf_nuke_page(ctx, ppage); |
1341 | 0 | if (next) |
1342 | 0 | next->prev = page->prev; |
1343 | 0 | if (page->prev) |
1344 | 0 | *page->prev = page->next; |
1345 | 0 | } |
1346 | 0 | else |
1347 | 0 | { |
1348 | 0 | pdf_sync_page(ctx, ppage); |
1349 | 0 | page->number = number; |
1350 | 0 | } |
1351 | 0 | } |
1352 | 0 | } |
1353 | | |
1354 | | pdf_page * |
1355 | | pdf_load_page(fz_context *ctx, pdf_document *doc, int number) |
1356 | 0 | { |
1357 | 0 | return (pdf_page*)fz_load_page(ctx, (fz_document*)doc, number); |
1358 | 0 | } |
1359 | | |
1360 | | int |
1361 | | pdf_page_has_transparency(fz_context *ctx, pdf_page *page) |
1362 | 0 | { |
1363 | 0 | return page->transparency; |
1364 | 0 | } |
1365 | | |
1366 | | fz_page * |
1367 | | pdf_load_page_imp(fz_context *ctx, fz_document *doc_, int chapter, int number) |
1368 | 326 | { |
1369 | 326 | pdf_document *doc = (pdf_document*)doc_; |
1370 | 326 | pdf_page *page; |
1371 | 326 | pdf_annot *annot; |
1372 | 326 | pdf_obj *pageobj, *obj; |
1373 | | |
1374 | 326 | if (doc->is_fdf) |
1375 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "FDF documents have no pages"); |
1376 | | |
1377 | 326 | if (chapter != 0) |
1378 | 0 | fz_throw(ctx, FZ_ERROR_ARGUMENT, "invalid chapter number: %d", chapter); |
1379 | | |
1380 | 326 | if (number < 0 || number >= pdf_count_pages(ctx, doc)) |
1381 | 0 | fz_throw(ctx, FZ_ERROR_ARGUMENT, "invalid page number: %d", number); |
1382 | | |
1383 | 326 | if (doc->file_reading_linearly) |
1384 | 0 | { |
1385 | 0 | pageobj = pdf_progressive_advance(ctx, doc, number); |
1386 | 0 | if (pageobj == NULL) |
1387 | 0 | fz_throw(ctx, FZ_ERROR_TRYLATER, "page %d not available yet", number); |
1388 | 0 | } |
1389 | 326 | else |
1390 | 326 | pageobj = pdf_lookup_page_obj(ctx, doc, number); |
1391 | | |
1392 | 326 | page = pdf_new_page(ctx, doc); |
1393 | 326 | page->obj = pdf_keep_obj(ctx, pageobj); |
1394 | | |
1395 | | /* Pre-load annotations and links */ |
1396 | 652 | fz_try(ctx) |
1397 | 652 | { |
1398 | 326 | obj = pdf_dict_get(ctx, pageobj, PDF_NAME(Annots)); |
1399 | 326 | if (obj) |
1400 | 33 | { |
1401 | 33 | fz_rect page_cropbox; |
1402 | 33 | fz_matrix page_ctm; |
1403 | 33 | pdf_page_transform(ctx, page, &page_cropbox, &page_ctm); |
1404 | 33 | page->links = pdf_load_link_annots(ctx, doc, page, obj, number, page_ctm); |
1405 | 33 | pdf_load_annots(ctx, page); |
1406 | 33 | } |
1407 | 326 | } |
1408 | 652 | fz_catch(ctx) |
1409 | 0 | { |
1410 | 0 | if (fz_caught(ctx) != FZ_ERROR_TRYLATER) |
1411 | 0 | { |
1412 | 0 | fz_drop_page(ctx, &page->super); |
1413 | 0 | fz_rethrow(ctx); |
1414 | 0 | } |
1415 | 0 | fz_ignore_error(ctx); |
1416 | 0 | page->super.incomplete = 1; |
1417 | 0 | fz_drop_link(ctx, page->links); |
1418 | 0 | page->links = NULL; |
1419 | 0 | } |
1420 | | |
1421 | | /* Scan for transparency and overprint */ |
1422 | 652 | fz_try(ctx) |
1423 | 652 | { |
1424 | 326 | pdf_obj *resources = pdf_page_resources(ctx, page); |
1425 | 326 | if (pdf_name_eq(ctx, pdf_dict_getp(ctx, pageobj, "Group/S"), PDF_NAME(Transparency))) |
1426 | 1 | page->transparency = 1; |
1427 | 325 | else if (pdf_resources_use_blending(ctx, resources, NULL)) |
1428 | 71 | page->transparency = 1; |
1429 | 326 | if (pdf_resources_use_overprint(ctx, resources, NULL)) |
1430 | 8 | page->overprint = 1; |
1431 | 357 | for (annot = page->annots; annot && !page->transparency; annot = annot->next) |
1432 | 31 | { |
1433 | 62 | fz_try(ctx) |
1434 | 62 | { |
1435 | 31 | pdf_obj *ap; |
1436 | 31 | pdf_obj *res; |
1437 | 31 | pdf_annot_push_local_xref(ctx, annot); |
1438 | 31 | ap = pdf_annot_ap(ctx, annot); |
1439 | 31 | if (!ap) |
1440 | 0 | break; |
1441 | 31 | res = pdf_xobject_resources(ctx, ap); |
1442 | 31 | if (pdf_resources_use_blending(ctx, res, NULL)) |
1443 | 18 | page->transparency = 1; |
1444 | 31 | if (pdf_resources_use_overprint(ctx, res, NULL)) |
1445 | 0 | page->overprint = 1; |
1446 | 31 | } |
1447 | 62 | fz_always(ctx) |
1448 | 31 | pdf_annot_pop_local_xref(ctx, annot); |
1449 | 31 | fz_catch(ctx) |
1450 | 0 | fz_rethrow(ctx); |
1451 | 31 | } |
1452 | 686 | for (annot = page->widgets; annot && !page->transparency; annot = annot->next) |
1453 | 360 | { |
1454 | 720 | fz_try(ctx) |
1455 | 720 | { |
1456 | 360 | pdf_obj *ap; |
1457 | 360 | pdf_obj *res; |
1458 | 360 | pdf_annot_push_local_xref(ctx, annot); |
1459 | 360 | ap = pdf_annot_ap(ctx, annot); |
1460 | 360 | if (!ap) |
1461 | 0 | break; |
1462 | 360 | res = pdf_xobject_resources(ctx, ap); |
1463 | 360 | if (pdf_resources_use_blending(ctx, res, NULL)) |
1464 | 0 | page->transparency = 1; |
1465 | 360 | if (pdf_resources_use_overprint(ctx, res, NULL)) |
1466 | 0 | page->overprint = 1; |
1467 | 360 | } |
1468 | 720 | fz_always(ctx) |
1469 | 360 | pdf_annot_pop_local_xref(ctx, annot); |
1470 | 360 | fz_catch(ctx) |
1471 | 0 | fz_rethrow(ctx); |
1472 | 360 | } |
1473 | 326 | } |
1474 | 652 | fz_catch(ctx) |
1475 | 0 | { |
1476 | 0 | if (fz_caught(ctx) != FZ_ERROR_TRYLATER) |
1477 | 0 | { |
1478 | 0 | fz_drop_page(ctx, &page->super); |
1479 | 0 | fz_rethrow(ctx); |
1480 | 0 | } |
1481 | 0 | fz_ignore_error(ctx); |
1482 | 0 | page->super.incomplete = 1; |
1483 | 0 | } |
1484 | | |
1485 | 326 | return (fz_page*)page; |
1486 | 326 | } |
1487 | | |
1488 | | static pdf_obj * |
1489 | | adjust_count(fz_context *ctx, pdf_obj *parent, void *arg) |
1490 | 0 | { |
1491 | 0 | int adj = (int)(intptr_t)arg; |
1492 | 0 | int count = pdf_dict_get_int(ctx, parent, PDF_NAME(Count)); |
1493 | 0 | pdf_dict_put_int(ctx, parent, PDF_NAME(Count), count + adj); |
1494 | |
|
1495 | 0 | return NULL; |
1496 | 0 | } |
1497 | | |
1498 | | void |
1499 | | pdf_delete_page(fz_context *ctx, pdf_document *doc, int at) |
1500 | 0 | { |
1501 | 0 | pdf_obj *parent, *kids; |
1502 | 0 | int i; |
1503 | |
|
1504 | 0 | pdf_begin_operation(ctx, doc, "Delete page"); |
1505 | 0 | fz_try(ctx) |
1506 | 0 | { |
1507 | 0 | pdf_lookup_page_loc(ctx, doc, at, &parent, &i); |
1508 | 0 | kids = pdf_dict_get(ctx, parent, PDF_NAME(Kids)); |
1509 | 0 | pdf_array_delete(ctx, kids, i); |
1510 | |
|
1511 | 0 | pdf_walk_parent(ctx, parent, NULL, adjust_count, (void *)(intptr_t)-1); |
1512 | | |
1513 | | /* Adjust page labels */ |
1514 | 0 | pdf_adjust_page_labels(ctx, doc, at, -1); |
1515 | 0 | pdf_end_operation(ctx, doc); |
1516 | 0 | } |
1517 | 0 | fz_catch(ctx) |
1518 | 0 | { |
1519 | 0 | pdf_abandon_operation(ctx, doc); |
1520 | 0 | pdf_sync_open_pages(ctx, doc); |
1521 | 0 | fz_rethrow(ctx); |
1522 | 0 | } |
1523 | | |
1524 | 0 | pdf_sync_open_pages(ctx, doc); |
1525 | 0 | } |
1526 | | |
1527 | | void |
1528 | | pdf_delete_page_range(fz_context *ctx, pdf_document *doc, int start, int end) |
1529 | 0 | { |
1530 | 0 | int count = pdf_count_pages(ctx, doc); |
1531 | 0 | if (end < 0) |
1532 | 0 | end = count; |
1533 | 0 | start = fz_clampi(start, 0, count); |
1534 | 0 | end = fz_clampi(end, 0, count); |
1535 | 0 | while (start < end) |
1536 | 0 | { |
1537 | 0 | pdf_delete_page(ctx, doc, start); |
1538 | 0 | end--; |
1539 | 0 | } |
1540 | 0 | } |
1541 | | |
1542 | | pdf_obj * |
1543 | | pdf_add_page(fz_context *ctx, pdf_document *doc, fz_rect mediabox, int rotate, pdf_obj *resources, fz_buffer *contents) |
1544 | 0 | { |
1545 | 0 | pdf_obj *page_obj = NULL; |
1546 | 0 | pdf_obj *page_ref = NULL; |
1547 | |
|
1548 | 0 | fz_var(page_obj); |
1549 | 0 | fz_var(page_ref); |
1550 | |
|
1551 | 0 | pdf_begin_operation(ctx, doc, "Add page"); |
1552 | |
|
1553 | 0 | fz_try(ctx) |
1554 | 0 | { |
1555 | 0 | page_obj = pdf_new_dict(ctx, doc, 5); |
1556 | |
|
1557 | 0 | pdf_dict_put(ctx, page_obj, PDF_NAME(Type), PDF_NAME(Page)); |
1558 | 0 | pdf_dict_put_rect(ctx, page_obj, PDF_NAME(MediaBox), mediabox); |
1559 | 0 | pdf_dict_put_int(ctx, page_obj, PDF_NAME(Rotate), rotate); |
1560 | |
|
1561 | 0 | if (pdf_is_indirect(ctx, resources)) |
1562 | 0 | pdf_dict_put(ctx, page_obj, PDF_NAME(Resources), resources); |
1563 | 0 | else if (pdf_is_dict(ctx, resources)) |
1564 | 0 | pdf_dict_put_drop(ctx, page_obj, PDF_NAME(Resources), pdf_add_object(ctx, doc, resources)); |
1565 | 0 | else |
1566 | 0 | pdf_dict_put_dict(ctx, page_obj, PDF_NAME(Resources), 1); |
1567 | |
|
1568 | 0 | if (contents && contents->len > 0) |
1569 | 0 | pdf_dict_put_drop(ctx, page_obj, PDF_NAME(Contents), pdf_add_stream(ctx, doc, contents, NULL, 0)); |
1570 | 0 | page_ref = pdf_add_object_drop(ctx, doc, page_obj); |
1571 | 0 | pdf_end_operation(ctx, doc); |
1572 | 0 | } |
1573 | 0 | fz_catch(ctx) |
1574 | 0 | { |
1575 | 0 | pdf_drop_obj(ctx, page_obj); |
1576 | 0 | pdf_abandon_operation(ctx, doc); |
1577 | 0 | fz_rethrow(ctx); |
1578 | 0 | } |
1579 | 0 | return page_ref; |
1580 | 0 | } |
1581 | | |
1582 | | void |
1583 | | pdf_insert_page(fz_context *ctx, pdf_document *doc, int at, pdf_obj *page_ref) |
1584 | 0 | { |
1585 | 0 | int count = pdf_count_pages(ctx, doc); |
1586 | 0 | pdf_obj *parent, *kids; |
1587 | 0 | int i; |
1588 | |
|
1589 | 0 | if (at < 0) |
1590 | 0 | at = count; |
1591 | 0 | if (at == INT_MAX) |
1592 | 0 | at = count; |
1593 | 0 | if (at > count) |
1594 | 0 | fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot insert page beyond end of page tree"); |
1595 | | |
1596 | 0 | pdf_begin_operation(ctx, doc, "Insert page"); |
1597 | |
|
1598 | 0 | fz_try(ctx) |
1599 | 0 | { |
1600 | 0 | if (count == 0) |
1601 | 0 | { |
1602 | 0 | pdf_obj *root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root)); |
1603 | 0 | parent = pdf_dict_get(ctx, root, PDF_NAME(Pages)); |
1604 | 0 | if (!parent) |
1605 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "cannot find page tree"); |
1606 | 0 | kids = pdf_dict_get(ctx, parent, PDF_NAME(Kids)); |
1607 | 0 | if (!kids) |
1608 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "malformed page tree"); |
1609 | 0 | pdf_array_insert(ctx, kids, page_ref, 0); |
1610 | 0 | } |
1611 | 0 | else if (at == count) |
1612 | 0 | { |
1613 | | /* append after last page */ |
1614 | 0 | pdf_lookup_page_loc(ctx, doc, count - 1, &parent, &i); |
1615 | 0 | kids = pdf_dict_get(ctx, parent, PDF_NAME(Kids)); |
1616 | 0 | pdf_array_insert(ctx, kids, page_ref, i + 1); |
1617 | 0 | } |
1618 | 0 | else |
1619 | 0 | { |
1620 | | /* insert before found page */ |
1621 | 0 | pdf_lookup_page_loc(ctx, doc, at, &parent, &i); |
1622 | 0 | kids = pdf_dict_get(ctx, parent, PDF_NAME(Kids)); |
1623 | 0 | pdf_array_insert(ctx, kids, page_ref, i); |
1624 | 0 | } |
1625 | | |
1626 | 0 | pdf_dict_put(ctx, page_ref, PDF_NAME(Parent), parent); |
1627 | | |
1628 | | /* Adjust page counts */ |
1629 | 0 | pdf_walk_parent(ctx, parent, NULL, adjust_count, (void *)(intptr_t)1); |
1630 | | |
1631 | | /* Adjust page labels */ |
1632 | 0 | pdf_adjust_page_labels(ctx, doc, at, 1); |
1633 | 0 | pdf_end_operation(ctx, doc); |
1634 | 0 | } |
1635 | 0 | fz_catch(ctx) |
1636 | 0 | { |
1637 | 0 | pdf_abandon_operation(ctx, doc); |
1638 | 0 | pdf_sync_open_pages(ctx, doc); |
1639 | 0 | fz_rethrow(ctx); |
1640 | 0 | } |
1641 | 0 | pdf_sync_open_pages(ctx, doc); |
1642 | 0 | } |
1643 | | |
1644 | | /* |
1645 | | * Page Labels |
1646 | | */ |
1647 | | |
1648 | | struct page_label_range { |
1649 | | int offset; |
1650 | | pdf_obj *label; |
1651 | | int nums_ix; |
1652 | | pdf_obj *nums; |
1653 | | }; |
1654 | | |
1655 | | static void |
1656 | | pdf_lookup_page_label_imp(fz_context *ctx, pdf_obj *node, int index, struct page_label_range *range) |
1657 | 0 | { |
1658 | 0 | pdf_obj *kids = pdf_dict_get(ctx, node, PDF_NAME(Kids)); |
1659 | 0 | pdf_obj *nums = pdf_dict_get(ctx, node, PDF_NAME(Nums)); |
1660 | 0 | int i; |
1661 | |
|
1662 | 0 | if (pdf_is_array(ctx, kids)) |
1663 | 0 | { |
1664 | 0 | for (i = 0; i < pdf_array_len(ctx, kids); ++i) |
1665 | 0 | { |
1666 | 0 | pdf_obj *kid = pdf_array_get(ctx, kids, i); |
1667 | 0 | pdf_lookup_page_label_imp(ctx, kid, index, range); |
1668 | 0 | } |
1669 | 0 | } |
1670 | |
|
1671 | 0 | if (pdf_is_array(ctx, nums)) |
1672 | 0 | { |
1673 | 0 | for (i = 0; i < pdf_array_len(ctx, nums); i += 2) |
1674 | 0 | { |
1675 | 0 | int k = pdf_array_get_int(ctx, nums, i); |
1676 | 0 | if (k <= index) |
1677 | 0 | { |
1678 | 0 | range->offset = k; |
1679 | 0 | range->label = pdf_array_get(ctx, nums, i + 1); |
1680 | 0 | range->nums_ix = i; |
1681 | 0 | range->nums = nums; |
1682 | 0 | } |
1683 | 0 | else |
1684 | 0 | { |
1685 | | /* stop looking if we've already passed the index */ |
1686 | 0 | return; |
1687 | 0 | } |
1688 | 0 | } |
1689 | 0 | } |
1690 | 0 | } |
1691 | | |
1692 | | static struct page_label_range |
1693 | | pdf_lookup_page_label(fz_context *ctx, pdf_document *doc, int index) |
1694 | 0 | { |
1695 | 0 | pdf_obj *root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root)); |
1696 | 0 | pdf_obj *labels = pdf_dict_get(ctx, root, PDF_NAME(PageLabels)); |
1697 | 0 | pdf_obj *nums = pdf_dict_get(ctx, labels, PDF_NAME(Nums)); |
1698 | 0 | struct page_label_range range = { 0, NULL, 0, nums }; |
1699 | 0 | pdf_lookup_page_label_imp(ctx, labels, index, &range); |
1700 | 0 | return range; |
1701 | 0 | } |
1702 | | |
1703 | | static void |
1704 | | pdf_flatten_page_label_tree_imp(fz_context *ctx, pdf_obj *node, pdf_obj *new_nums) |
1705 | 0 | { |
1706 | 0 | pdf_obj *kids = pdf_dict_get(ctx, node, PDF_NAME(Kids)); |
1707 | 0 | pdf_obj *nums = pdf_dict_get(ctx, node, PDF_NAME(Nums)); |
1708 | 0 | int i; |
1709 | |
|
1710 | 0 | if (pdf_is_array(ctx, kids)) |
1711 | 0 | { |
1712 | 0 | for (i = 0; i < pdf_array_len(ctx, kids); ++i) |
1713 | 0 | { |
1714 | 0 | pdf_obj *kid = pdf_array_get(ctx, kids, i); |
1715 | 0 | pdf_flatten_page_label_tree_imp(ctx, kid, new_nums); |
1716 | 0 | } |
1717 | 0 | } |
1718 | |
|
1719 | 0 | if (pdf_is_array(ctx, nums)) |
1720 | 0 | { |
1721 | 0 | for (i = 0; i < pdf_array_len(ctx, nums); i += 2) |
1722 | 0 | { |
1723 | 0 | pdf_array_push(ctx, new_nums, pdf_array_get(ctx, nums, i)); |
1724 | 0 | pdf_array_push(ctx, new_nums, pdf_array_get(ctx, nums, i + 1)); |
1725 | 0 | } |
1726 | 0 | } |
1727 | 0 | } |
1728 | | |
1729 | | static void |
1730 | | pdf_flatten_page_label_tree(fz_context *ctx, pdf_document *doc) |
1731 | 0 | { |
1732 | 0 | pdf_obj *root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root)); |
1733 | 0 | pdf_obj *labels = pdf_dict_get(ctx, root, PDF_NAME(PageLabels)); |
1734 | 0 | pdf_obj *nums = pdf_dict_get(ctx, labels, PDF_NAME(Nums)); |
1735 | | |
1736 | | // Already flat... |
1737 | 0 | if (pdf_is_array(ctx, nums) && pdf_array_len(ctx, nums) >= 2) |
1738 | 0 | return; |
1739 | | |
1740 | 0 | nums = pdf_new_array(ctx, doc, 8); |
1741 | 0 | fz_try(ctx) |
1742 | 0 | { |
1743 | 0 | if (!labels) |
1744 | 0 | labels = pdf_dict_put_dict(ctx, root, PDF_NAME(PageLabels), 1); |
1745 | |
|
1746 | 0 | pdf_flatten_page_label_tree_imp(ctx, labels, nums); |
1747 | |
|
1748 | 0 | pdf_dict_del(ctx, labels, PDF_NAME(Kids)); |
1749 | 0 | pdf_dict_del(ctx, labels, PDF_NAME(Limits)); |
1750 | 0 | pdf_dict_put(ctx, labels, PDF_NAME(Nums), nums); |
1751 | | |
1752 | | /* No Page Label tree found - insert one with default values */ |
1753 | 0 | if (pdf_array_len(ctx, nums) == 0) |
1754 | 0 | { |
1755 | 0 | pdf_obj *obj; |
1756 | 0 | pdf_array_push_int(ctx, nums, 0); |
1757 | 0 | obj = pdf_array_push_dict(ctx, nums, 1); |
1758 | 0 | pdf_dict_put(ctx, obj, PDF_NAME(S), PDF_NAME(D)); |
1759 | 0 | } |
1760 | 0 | } |
1761 | 0 | fz_always(ctx) |
1762 | 0 | pdf_drop_obj(ctx, nums); |
1763 | 0 | fz_catch(ctx) |
1764 | 0 | fz_rethrow(ctx); |
1765 | 0 | } |
1766 | | |
1767 | | static pdf_obj * |
1768 | | pdf_create_page_label(fz_context *ctx, pdf_document *doc, pdf_page_label_style style, const char *prefix, int start) |
1769 | 0 | { |
1770 | 0 | pdf_obj *obj = pdf_new_dict(ctx, doc, 3); |
1771 | 0 | fz_try(ctx) |
1772 | 0 | { |
1773 | 0 | switch (style) |
1774 | 0 | { |
1775 | 0 | default: |
1776 | 0 | case PDF_PAGE_LABEL_NONE: |
1777 | 0 | break; |
1778 | 0 | case PDF_PAGE_LABEL_DECIMAL: |
1779 | 0 | pdf_dict_put(ctx, obj, PDF_NAME(S), PDF_NAME(D)); |
1780 | 0 | break; |
1781 | 0 | case PDF_PAGE_LABEL_ROMAN_UC: |
1782 | 0 | pdf_dict_put(ctx, obj, PDF_NAME(S), PDF_NAME(R)); |
1783 | 0 | break; |
1784 | 0 | case PDF_PAGE_LABEL_ROMAN_LC: |
1785 | 0 | pdf_dict_put(ctx, obj, PDF_NAME(S), PDF_NAME(r)); |
1786 | 0 | break; |
1787 | 0 | case PDF_PAGE_LABEL_ALPHA_UC: |
1788 | 0 | pdf_dict_put(ctx, obj, PDF_NAME(S), PDF_NAME(A)); |
1789 | 0 | break; |
1790 | 0 | case PDF_PAGE_LABEL_ALPHA_LC: |
1791 | 0 | pdf_dict_put(ctx, obj, PDF_NAME(S), PDF_NAME(a)); |
1792 | 0 | break; |
1793 | 0 | } |
1794 | 0 | if (prefix && strlen(prefix) > 0) |
1795 | 0 | pdf_dict_put_text_string(ctx, obj, PDF_NAME(P), prefix); |
1796 | 0 | if (start > 1) |
1797 | 0 | pdf_dict_put_int(ctx, obj, PDF_NAME(St), start); |
1798 | 0 | } |
1799 | 0 | fz_catch(ctx) |
1800 | 0 | { |
1801 | 0 | pdf_drop_obj(ctx, obj); |
1802 | 0 | fz_rethrow(ctx); |
1803 | 0 | } |
1804 | 0 | return obj; |
1805 | 0 | } |
1806 | | |
1807 | | static void |
1808 | | pdf_adjust_page_labels(fz_context *ctx, pdf_document *doc, int index, int adjust) |
1809 | 0 | { |
1810 | 0 | pdf_obj *root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root)); |
1811 | 0 | pdf_obj *labels = pdf_dict_get(ctx, root, PDF_NAME(PageLabels)); |
1812 | | |
1813 | | // Skip the adjustment step if there are no page labels. |
1814 | | // Exception: If we would adjust the label for page 0, we must create one! |
1815 | | // Exception: If the document only has one page! |
1816 | 0 | if (labels || (adjust > 0 && index == 0 && pdf_count_pages(ctx, doc) > 1)) |
1817 | 0 | { |
1818 | 0 | struct page_label_range range; |
1819 | 0 | int i; |
1820 | | |
1821 | | // Ensure we have a flat page label tree with at least one entry. |
1822 | 0 | pdf_flatten_page_label_tree(ctx, doc); |
1823 | | |
1824 | | // Find page label affecting the page that triggered adjustment |
1825 | 0 | range = pdf_lookup_page_label(ctx, doc, index); |
1826 | | |
1827 | | // Shift all page labels on and after the inserted index |
1828 | 0 | if (adjust > 0) |
1829 | 0 | { |
1830 | 0 | if (range.offset == index) |
1831 | 0 | i = range.nums_ix; |
1832 | 0 | else |
1833 | 0 | i = range.nums_ix + 2; |
1834 | 0 | } |
1835 | | |
1836 | | // Shift all page labels after the removed index |
1837 | 0 | else |
1838 | 0 | { |
1839 | 0 | i = range.nums_ix + 2; |
1840 | 0 | } |
1841 | | |
1842 | | |
1843 | | // Increase/decrease the indices in the name tree |
1844 | 0 | for (; i < pdf_array_len(ctx, range.nums); i += 2) |
1845 | 0 | pdf_array_put_int(ctx, range.nums, i, pdf_array_get_int(ctx, range.nums, i) + adjust); |
1846 | | |
1847 | | // TODO: delete page labels that have no effect (zero range) |
1848 | | |
1849 | | // Make sure the number tree always has an entry for page 0 |
1850 | 0 | if (adjust > 0 && index == 0) |
1851 | 0 | { |
1852 | 0 | pdf_array_insert_drop(ctx, range.nums, pdf_new_int(ctx, index), 0); |
1853 | 0 | pdf_array_insert_drop(ctx, range.nums, pdf_create_page_label(ctx, doc, PDF_PAGE_LABEL_DECIMAL, NULL, 1), 1); |
1854 | 0 | } |
1855 | 0 | } |
1856 | 0 | } |
1857 | | |
1858 | | void |
1859 | | pdf_set_page_labels(fz_context *ctx, pdf_document *doc, |
1860 | | int index, |
1861 | | pdf_page_label_style style, const char *prefix, int start) |
1862 | 0 | { |
1863 | 0 | struct page_label_range range; |
1864 | |
|
1865 | 0 | pdf_begin_operation(ctx, doc, "Set page label"); |
1866 | 0 | fz_try(ctx) |
1867 | 0 | { |
1868 | | // Ensure we have a flat page label tree with at least one entry. |
1869 | 0 | pdf_flatten_page_label_tree(ctx, doc); |
1870 | |
|
1871 | 0 | range = pdf_lookup_page_label(ctx, doc, index); |
1872 | |
|
1873 | 0 | if (range.offset == index) |
1874 | 0 | { |
1875 | | // Replace label |
1876 | 0 | pdf_array_put_drop(ctx, range.nums, |
1877 | 0 | range.nums_ix + 1, |
1878 | 0 | pdf_create_page_label(ctx, doc, style, prefix, start)); |
1879 | 0 | } |
1880 | 0 | else |
1881 | 0 | { |
1882 | | // Insert new label |
1883 | 0 | pdf_array_insert_drop(ctx, range.nums, |
1884 | 0 | pdf_new_int(ctx, index), |
1885 | 0 | range.nums_ix + 2); |
1886 | 0 | pdf_array_insert_drop(ctx, range.nums, |
1887 | 0 | pdf_create_page_label(ctx, doc, style, prefix, start), |
1888 | 0 | range.nums_ix + 3); |
1889 | 0 | } |
1890 | 0 | pdf_end_operation(ctx, doc); |
1891 | 0 | } |
1892 | 0 | fz_catch(ctx) |
1893 | 0 | { |
1894 | 0 | pdf_abandon_operation(ctx, doc); |
1895 | 0 | fz_rethrow(ctx); |
1896 | 0 | } |
1897 | 0 | } |
1898 | | |
1899 | | void |
1900 | | pdf_delete_page_labels(fz_context *ctx, pdf_document *doc, int index) |
1901 | 0 | { |
1902 | 0 | struct page_label_range range; |
1903 | |
|
1904 | 0 | if (index == 0) |
1905 | 0 | { |
1906 | 0 | pdf_set_page_labels(ctx, doc, 0, PDF_PAGE_LABEL_DECIMAL, NULL, 1); |
1907 | 0 | return; |
1908 | 0 | } |
1909 | | |
1910 | 0 | pdf_begin_operation(ctx, doc, "Delete page label"); |
1911 | 0 | fz_try(ctx) |
1912 | 0 | { |
1913 | | // Ensure we have a flat page label tree with at least one entry. |
1914 | 0 | pdf_flatten_page_label_tree(ctx, doc); |
1915 | |
|
1916 | 0 | range = pdf_lookup_page_label(ctx, doc, index); |
1917 | |
|
1918 | 0 | if (range.offset == index) |
1919 | 0 | { |
1920 | | // Delete label |
1921 | 0 | pdf_array_delete(ctx, range.nums, range.nums_ix); |
1922 | 0 | pdf_array_delete(ctx, range.nums, range.nums_ix); |
1923 | 0 | } |
1924 | 0 | pdf_end_operation(ctx, doc); |
1925 | 0 | } |
1926 | 0 | fz_catch(ctx) |
1927 | 0 | { |
1928 | 0 | pdf_abandon_operation(ctx, doc); |
1929 | 0 | fz_rethrow(ctx); |
1930 | 0 | } |
1931 | 0 | } |
1932 | | |
1933 | | static const char *roman_uc[3][10] = { |
1934 | | { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" }, |
1935 | | { "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" }, |
1936 | | { "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" }, |
1937 | | }; |
1938 | | |
1939 | | static const char *roman_lc[3][10] = { |
1940 | | { "", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix" }, |
1941 | | { "", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc" }, |
1942 | | { "", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm" }, |
1943 | | }; |
1944 | | |
1945 | | static void pdf_format_roman_page_label(char *buf, int size, int n, const char *sym[3][10], const char *sym_m) |
1946 | 0 | { |
1947 | 0 | int I = n % 10; |
1948 | 0 | int X = (n / 10) % 10; |
1949 | 0 | int C = (n / 100) % 10; |
1950 | 0 | int M = (n / 1000); |
1951 | |
|
1952 | 0 | fz_strlcpy(buf, "", size); |
1953 | 0 | while (M--) |
1954 | 0 | fz_strlcat(buf, sym_m, size); |
1955 | 0 | fz_strlcat(buf, sym[2][C], size); |
1956 | 0 | fz_strlcat(buf, sym[1][X], size); |
1957 | 0 | fz_strlcat(buf, sym[0][I], size); |
1958 | 0 | } |
1959 | | |
1960 | | static void pdf_format_alpha_page_label(char *buf, int size, int n, int alpha) |
1961 | 0 | { |
1962 | 0 | int reps = (n - 1) / 26 + 1; |
1963 | 0 | if (reps > size - 1) |
1964 | 0 | reps = size - 1; |
1965 | 0 | memset(buf, (n - 1) % 26 + alpha, reps); |
1966 | 0 | buf[reps] = '\0'; |
1967 | 0 | } |
1968 | | |
1969 | | static void |
1970 | | pdf_format_page_label(fz_context *ctx, int index, pdf_obj *dict, char *buf, size_t size) |
1971 | 0 | { |
1972 | 0 | pdf_obj *style = pdf_dict_get(ctx, dict, PDF_NAME(S)); |
1973 | 0 | const char *prefix = pdf_dict_get_text_string(ctx, dict, PDF_NAME(P)); |
1974 | 0 | int start = pdf_dict_get_int(ctx, dict, PDF_NAME(St)); |
1975 | 0 | int v; |
1976 | 0 | size_t n; |
1977 | | |
1978 | | // St must be >= 1; default is 1. |
1979 | 0 | if (start < 1) |
1980 | 0 | start = 1; |
1981 | |
|
1982 | 0 | v = start + index; |
1983 | 0 | if (v < 1) |
1984 | 0 | v = 1; |
1985 | | |
1986 | | // Add prefix (optional; may be empty) |
1987 | 0 | fz_strlcpy(buf, prefix, size); |
1988 | 0 | n = strlen(buf); |
1989 | 0 | buf += n; |
1990 | 0 | size -= n; |
1991 | | |
1992 | | // Append number using style (optional) |
1993 | 0 | if (style == PDF_NAME(D)) |
1994 | 0 | fz_snprintf(buf, size, "%d", v); |
1995 | 0 | else if (style == PDF_NAME(R)) |
1996 | 0 | pdf_format_roman_page_label(buf, (int)size, v, roman_uc, "M"); |
1997 | 0 | else if (style == PDF_NAME(r)) |
1998 | 0 | pdf_format_roman_page_label(buf, (int)size, v, roman_lc, "m"); |
1999 | 0 | else if (style == PDF_NAME(A)) |
2000 | 0 | pdf_format_alpha_page_label(buf, (int)size, v, 'A'); |
2001 | 0 | else if (style == PDF_NAME(a)) |
2002 | 0 | pdf_format_alpha_page_label(buf, (int)size, v, 'a'); |
2003 | 0 | } |
2004 | | |
2005 | | void |
2006 | | pdf_page_label(fz_context *ctx, pdf_document *doc, int index, char *buf, size_t size) |
2007 | 0 | { |
2008 | 0 | struct page_label_range range = pdf_lookup_page_label(ctx, doc, index); |
2009 | 0 | if (range.label) |
2010 | 0 | pdf_format_page_label(ctx, index - range.offset, range.label, buf, size); |
2011 | 0 | else |
2012 | 0 | fz_snprintf(buf, size, "%d", index + 1); |
2013 | 0 | } |
2014 | | |
2015 | | void |
2016 | | pdf_page_label_imp(fz_context *ctx, fz_document *doc, int chapter, int page, char *buf, size_t size) |
2017 | 0 | { |
2018 | 0 | pdf_page_label(ctx, pdf_document_from_fz_document(ctx, doc), page, buf, size); |
2019 | 0 | } |
2020 | | |
2021 | | pdf_page * |
2022 | | pdf_keep_page(fz_context *ctx, pdf_page *page) |
2023 | 0 | { |
2024 | 0 | return (pdf_page *) fz_keep_page(ctx, &page->super); |
2025 | 0 | } |
2026 | | |
2027 | | void |
2028 | | pdf_drop_page(fz_context *ctx, pdf_page *page) |
2029 | 0 | { |
2030 | 0 | if (page) |
2031 | 0 | fz_drop_page(ctx, &page->super); |
2032 | 0 | } |