1 | #include "mupdf/fitz.h" |
2 | #include "mupdf/pdf.h" |
3 | |
4 | #include <stdlib.h> |
5 | #include <string.h> |
6 | #include <limits.h> |
7 | |
8 | int |
9 | pdf_count_pages(fz_context *ctx, pdf_document *doc) |
10 | { |
11 | /* FIXME: We should reset linear_page_count to 0 when editing starts |
12 | * (or when linear loading ends) */ |
13 | if (doc->linear_page_count != 0) |
14 | return doc->linear_page_count; |
15 | return pdf_to_int(ctx, pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/Pages/Count" )); |
16 | } |
17 | |
18 | static int |
19 | pdf_load_page_tree_imp(fz_context *ctx, pdf_document *doc, pdf_obj *node, int idx) |
20 | { |
21 | pdf_obj *type = pdf_dict_get(ctx, node, PDF_NAME(Type)); |
22 | if (pdf_name_eq(ctx, type, PDF_NAME(Pages))) |
23 | { |
24 | pdf_obj *kids = pdf_dict_get(ctx, node, PDF_NAME(Kids)); |
25 | int i, n = pdf_array_len(ctx, kids); |
26 | |
27 | if (pdf_mark_obj(ctx, node)) |
28 | fz_throw(ctx, FZ_ERROR_GENERIC, "cycle in page tree" ); |
29 | fz_try(ctx) |
30 | for (i = 0; i < n; ++i) |
31 | idx = pdf_load_page_tree_imp(ctx, doc, pdf_array_get(ctx, kids, i), idx); |
32 | fz_always(ctx) |
33 | pdf_unmark_obj(ctx, node); |
34 | fz_catch(ctx) |
35 | fz_rethrow(ctx); |
36 | } |
37 | else if (pdf_name_eq(ctx, type, PDF_NAME(Page))) |
38 | { |
39 | if (idx >= doc->rev_page_count) |
40 | fz_throw(ctx, FZ_ERROR_GENERIC, "too many kids in page tree" ); |
41 | doc->rev_page_map[idx].page = idx; |
42 | doc->rev_page_map[idx].object = pdf_to_num(ctx, node); |
43 | ++idx; |
44 | } |
45 | else |
46 | { |
47 | fz_throw(ctx, FZ_ERROR_GENERIC, "non-page object in page tree" ); |
48 | } |
49 | return idx; |
50 | } |
51 | |
52 | static int |
53 | cmp_rev_page_map(const void *va, const void *vb) |
54 | { |
55 | const pdf_rev_page_map *a = va; |
56 | const pdf_rev_page_map *b = vb; |
57 | return a->object - b->object; |
58 | } |
59 | |
60 | void |
61 | pdf_load_page_tree(fz_context *ctx, pdf_document *doc) |
62 | { |
63 | if (!doc->rev_page_map) |
64 | { |
65 | doc->rev_page_count = pdf_count_pages(ctx, doc); |
66 | doc->rev_page_map = fz_malloc_array(ctx, doc->rev_page_count, pdf_rev_page_map); |
67 | pdf_load_page_tree_imp(ctx, doc, pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/Pages" ), 0); |
68 | qsort(doc->rev_page_map, doc->rev_page_count, sizeof *doc->rev_page_map, cmp_rev_page_map); |
69 | } |
70 | } |
71 | |
72 | void |
73 | pdf_drop_page_tree(fz_context *ctx, pdf_document *doc) |
74 | { |
75 | fz_free(ctx, doc->rev_page_map); |
76 | doc->rev_page_map = NULL; |
77 | doc->rev_page_count = 0; |
78 | } |
79 | |
80 | enum |
81 | { |
82 | LOCAL_STACK_SIZE = 16 |
83 | }; |
84 | |
85 | static pdf_obj * |
86 | pdf_lookup_page_loc_imp(fz_context *ctx, pdf_document *doc, pdf_obj *node, int *skip, pdf_obj **parentp, int *indexp) |
87 | { |
88 | pdf_obj *kids; |
89 | pdf_obj *hit = NULL; |
90 | int i, len; |
91 | pdf_obj *local_stack[LOCAL_STACK_SIZE]; |
92 | pdf_obj **stack = &local_stack[0]; |
93 | int stack_max = LOCAL_STACK_SIZE; |
94 | int stack_len = 0; |
95 | |
96 | fz_var(hit); |
97 | fz_var(stack); |
98 | fz_var(stack_len); |
99 | fz_var(stack_max); |
100 | |
101 | fz_try(ctx) |
102 | { |
103 | do |
104 | { |
105 | kids = pdf_dict_get(ctx, node, PDF_NAME(Kids)); |
106 | len = pdf_array_len(ctx, kids); |
107 | |
108 | if (len == 0) |
109 | fz_throw(ctx, FZ_ERROR_GENERIC, "malformed page tree" ); |
110 | |
111 | /* Every node we need to unmark goes into the stack */ |
112 | if (stack_len == stack_max) |
113 | { |
114 | if (stack == &local_stack[0]) |
115 | { |
116 | stack = fz_malloc_array(ctx, stack_max * 2, pdf_obj*); |
117 | memcpy(stack, &local_stack[0], stack_max * sizeof(*stack)); |
118 | } |
119 | else |
120 | { |
121 | stack = fz_realloc_array(ctx, stack, stack_max * 2, pdf_obj*); |
122 | } |
123 | stack_max *= 2; |
124 | } |
125 | stack[stack_len++] = node; |
126 | |
127 | if (pdf_mark_obj(ctx, node)) |
128 | fz_throw(ctx, FZ_ERROR_GENERIC, "cycle in page tree" ); |
129 | |
130 | for (i = 0; i < len; i++) |
131 | { |
132 | pdf_obj *kid = pdf_array_get(ctx, kids, i); |
133 | pdf_obj *type = pdf_dict_get(ctx, kid, PDF_NAME(Type)); |
134 | 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))) |
135 | { |
136 | int count = pdf_dict_get_int(ctx, kid, PDF_NAME(Count)); |
137 | if (*skip < count) |
138 | { |
139 | node = kid; |
140 | break; |
141 | } |
142 | else |
143 | { |
144 | *skip -= count; |
145 | } |
146 | } |
147 | else |
148 | { |
149 | if (type ? !pdf_name_eq(ctx, type, PDF_NAME(Page)) : !pdf_dict_get(ctx, kid, PDF_NAME(MediaBox))) |
150 | fz_warn(ctx, "non-page object in page tree (%s)" , pdf_to_name(ctx, type)); |
151 | if (*skip == 0) |
152 | { |
153 | if (parentp) *parentp = node; |
154 | if (indexp) *indexp = i; |
155 | hit = kid; |
156 | break; |
157 | } |
158 | else |
159 | { |
160 | (*skip)--; |
161 | } |
162 | } |
163 | } |
164 | } |
165 | /* If i < len && hit != NULL the desired page was found in the |
166 | Kids array, done. If i < len && hit == NULL the found page tree |
167 | node contains a Kids array that contains the desired page, loop |
168 | back to top to extract it. When i == len the Kids array has been |
169 | exhausted without finding the desired page, give up. |
170 | */ |
171 | while (hit == NULL && i < len); |
172 | } |
173 | fz_always(ctx) |
174 | { |
175 | for (i = stack_len; i > 0; i--) |
176 | pdf_unmark_obj(ctx, stack[i-1]); |
177 | if (stack != &local_stack[0]) |
178 | fz_free(ctx, stack); |
179 | } |
180 | fz_catch(ctx) |
181 | { |
182 | fz_rethrow(ctx); |
183 | } |
184 | |
185 | return hit; |
186 | } |
187 | |
188 | pdf_obj * |
189 | pdf_lookup_page_loc(fz_context *ctx, pdf_document *doc, int needle, pdf_obj **parentp, int *indexp) |
190 | { |
191 | pdf_obj *root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root)); |
192 | pdf_obj *node = pdf_dict_get(ctx, root, PDF_NAME(Pages)); |
193 | int skip = needle; |
194 | pdf_obj *hit; |
195 | |
196 | if (!node) |
197 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find page tree" ); |
198 | |
199 | hit = pdf_lookup_page_loc_imp(ctx, doc, node, &skip, parentp, indexp); |
200 | if (!hit) |
201 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find page %d in page tree" , needle+1); |
202 | return hit; |
203 | } |
204 | |
205 | pdf_obj * |
206 | pdf_lookup_page_obj(fz_context *ctx, pdf_document *doc, int needle) |
207 | { |
208 | return pdf_lookup_page_loc(ctx, doc, needle, NULL, NULL); |
209 | } |
210 | |
211 | static int |
212 | pdf_count_pages_before_kid(fz_context *ctx, pdf_document *doc, pdf_obj *parent, int kid_num) |
213 | { |
214 | pdf_obj *kids = pdf_dict_get(ctx, parent, PDF_NAME(Kids)); |
215 | int i, total = 0, len = pdf_array_len(ctx, kids); |
216 | for (i = 0; i < len; i++) |
217 | { |
218 | pdf_obj *kid = pdf_array_get(ctx, kids, i); |
219 | if (pdf_to_num(ctx, kid) == kid_num) |
220 | return total; |
221 | if (pdf_name_eq(ctx, pdf_dict_get(ctx, kid, PDF_NAME(Type)), PDF_NAME(Pages))) |
222 | { |
223 | pdf_obj *count = pdf_dict_get(ctx, kid, PDF_NAME(Count)); |
224 | int n = pdf_to_int(ctx, count); |
225 | if (!pdf_is_int(ctx, count) || n < 0) |
226 | fz_throw(ctx, FZ_ERROR_GENERIC, "illegal or missing count in pages tree" ); |
227 | total += n; |
228 | } |
229 | else |
230 | total++; |
231 | } |
232 | fz_throw(ctx, FZ_ERROR_GENERIC, "kid not found in parent's kids array" ); |
233 | } |
234 | |
235 | static int |
236 | pdf_lookup_page_number_slow(fz_context *ctx, pdf_document *doc, pdf_obj *node) |
237 | { |
238 | int needle = pdf_to_num(ctx, node); |
239 | int total = 0; |
240 | pdf_obj *parent, *parent2; |
241 | |
242 | if (!pdf_name_eq(ctx, pdf_dict_get(ctx, node, PDF_NAME(Type)), PDF_NAME(Page))) |
243 | fz_throw(ctx, FZ_ERROR_GENERIC, "invalid page object" ); |
244 | |
245 | parent2 = parent = pdf_dict_get(ctx, node, PDF_NAME(Parent)); |
246 | fz_var(parent); |
247 | fz_try(ctx) |
248 | { |
249 | while (pdf_is_dict(ctx, parent)) |
250 | { |
251 | if (pdf_mark_obj(ctx, parent)) |
252 | fz_throw(ctx, FZ_ERROR_GENERIC, "cycle in page tree (parents)" ); |
253 | total += pdf_count_pages_before_kid(ctx, doc, parent, needle); |
254 | needle = pdf_to_num(ctx, parent); |
255 | parent = pdf_dict_get(ctx, parent, PDF_NAME(Parent)); |
256 | } |
257 | } |
258 | fz_always(ctx) |
259 | { |
260 | /* Run back and unmark */ |
261 | while (parent2) |
262 | { |
263 | pdf_unmark_obj(ctx, parent2); |
264 | if (parent2 == parent) |
265 | break; |
266 | parent2 = pdf_dict_get(ctx, parent2, PDF_NAME(Parent)); |
267 | } |
268 | } |
269 | fz_catch(ctx) |
270 | { |
271 | fz_rethrow(ctx); |
272 | } |
273 | |
274 | return total; |
275 | } |
276 | |
277 | static int |
278 | pdf_lookup_page_number_fast(fz_context *ctx, pdf_document *doc, int needle) |
279 | { |
280 | int l = 0; |
281 | int r = doc->rev_page_count - 1; |
282 | while (l <= r) |
283 | { |
284 | int m = (l + r) >> 1; |
285 | int c = needle - doc->rev_page_map[m].object; |
286 | if (c < 0) |
287 | r = m - 1; |
288 | else if (c > 0) |
289 | l = m + 1; |
290 | else |
291 | return doc->rev_page_map[m].page; |
292 | } |
293 | return -1; |
294 | } |
295 | |
296 | int |
297 | pdf_lookup_page_number(fz_context *ctx, pdf_document *doc, pdf_obj *page) |
298 | { |
299 | if (doc->rev_page_map) |
300 | return pdf_lookup_page_number_fast(ctx, doc, pdf_to_num(ctx, page)); |
301 | else |
302 | return pdf_lookup_page_number_slow(ctx, doc, page); |
303 | } |
304 | |
305 | /* |
306 | Find the page number of a named destination. |
307 | |
308 | For use with looking up the destination page of a fragment |
309 | identifier in hyperlinks: foo.pdf#bar or foo.pdf#page=5. |
310 | */ |
311 | int |
312 | pdf_lookup_anchor(fz_context *ctx, pdf_document *doc, const char *name, float *xp, float *yp) |
313 | { |
314 | pdf_obj *needle, *dest = NULL; |
315 | char *uri; |
316 | |
317 | if (xp) *xp = 0; |
318 | if (yp) *yp = 0; |
319 | |
320 | needle = pdf_new_string(ctx, name, strlen(name)); |
321 | fz_try(ctx) |
322 | dest = pdf_lookup_dest(ctx, doc, needle); |
323 | fz_always(ctx) |
324 | pdf_drop_obj(ctx, needle); |
325 | fz_catch(ctx) |
326 | fz_rethrow(ctx); |
327 | |
328 | if (dest) |
329 | { |
330 | uri = pdf_parse_link_dest(ctx, doc, dest); |
331 | return pdf_resolve_link(ctx, doc, uri, xp, yp); |
332 | } |
333 | |
334 | if (!strncmp(name, "page=" , 5)) |
335 | return fz_atoi(name + 5) - 1; |
336 | |
337 | return fz_atoi(name) - 1; |
338 | } |
339 | |
340 | static void |
341 | pdf_flatten_inheritable_page_item(fz_context *ctx, pdf_obj *page, pdf_obj *key) |
342 | { |
343 | pdf_obj *val = pdf_dict_get_inheritable(ctx, page, key); |
344 | if (val) |
345 | pdf_dict_put(ctx, page, key, val); |
346 | } |
347 | |
348 | /* |
349 | Make page self sufficient. |
350 | |
351 | Copy any inheritable page keys into the actual page object, removing |
352 | any dependencies on the page tree parents. |
353 | */ |
354 | void |
355 | pdf_flatten_inheritable_page_items(fz_context *ctx, pdf_obj *page) |
356 | { |
357 | pdf_flatten_inheritable_page_item(ctx, page, PDF_NAME(MediaBox)); |
358 | pdf_flatten_inheritable_page_item(ctx, page, PDF_NAME(CropBox)); |
359 | pdf_flatten_inheritable_page_item(ctx, page, PDF_NAME(Rotate)); |
360 | pdf_flatten_inheritable_page_item(ctx, page, PDF_NAME(Resources)); |
361 | } |
362 | |
363 | /* We need to know whether to install a page-level transparency group */ |
364 | |
365 | /* |
366 | * Object memo flags - allows us to secretly remember "a memo" (a bool) in an |
367 | * object, and to read back whether there was a memo, and if so, what it was. |
368 | */ |
369 | enum |
370 | { |
371 | PDF_FLAGS_MEMO_BM = 0, |
372 | PDF_FLAGS_MEMO_OP = 1 |
373 | }; |
374 | |
375 | static int pdf_resources_use_blending(fz_context *ctx, pdf_obj *rdb); |
376 | |
377 | static int |
378 | pdf_extgstate_uses_blending(fz_context *ctx, pdf_obj *dict) |
379 | { |
380 | pdf_obj *obj = pdf_dict_get(ctx, dict, PDF_NAME(BM)); |
381 | if (obj && !pdf_name_eq(ctx, obj, PDF_NAME(Normal))) |
382 | return 1; |
383 | return 0; |
384 | } |
385 | |
386 | static int |
387 | pdf_pattern_uses_blending(fz_context *ctx, pdf_obj *dict) |
388 | { |
389 | pdf_obj *obj; |
390 | obj = pdf_dict_get(ctx, dict, PDF_NAME(Resources)); |
391 | if (pdf_resources_use_blending(ctx, obj)) |
392 | return 1; |
393 | obj = pdf_dict_get(ctx, dict, PDF_NAME(ExtGState)); |
394 | return pdf_extgstate_uses_blending(ctx, obj); |
395 | } |
396 | |
397 | static int |
398 | pdf_xobject_uses_blending(fz_context *ctx, pdf_obj *dict) |
399 | { |
400 | pdf_obj *obj = pdf_dict_get(ctx, dict, PDF_NAME(Resources)); |
401 | if (pdf_name_eq(ctx, pdf_dict_getp(ctx, dict, "Group/S" ), PDF_NAME(Transparency))) |
402 | return 1; |
403 | return pdf_resources_use_blending(ctx, obj); |
404 | } |
405 | |
406 | static int |
407 | pdf_resources_use_blending(fz_context *ctx, pdf_obj *rdb) |
408 | { |
409 | pdf_obj *obj; |
410 | int i, n, useBM = 0; |
411 | |
412 | if (!rdb) |
413 | return 0; |
414 | |
415 | /* Have we been here before and remembered an answer? */ |
416 | if (pdf_obj_memo(ctx, rdb, PDF_FLAGS_MEMO_BM, &useBM)) |
417 | return useBM; |
418 | |
419 | /* stop on cyclic resource dependencies */ |
420 | if (pdf_mark_obj(ctx, rdb)) |
421 | return 0; |
422 | |
423 | fz_try(ctx) |
424 | { |
425 | obj = pdf_dict_get(ctx, rdb, PDF_NAME(ExtGState)); |
426 | n = pdf_dict_len(ctx, obj); |
427 | for (i = 0; i < n; i++) |
428 | if (pdf_extgstate_uses_blending(ctx, pdf_dict_get_val(ctx, obj, i))) |
429 | goto found; |
430 | |
431 | obj = pdf_dict_get(ctx, rdb, PDF_NAME(Pattern)); |
432 | n = pdf_dict_len(ctx, obj); |
433 | for (i = 0; i < n; i++) |
434 | if (pdf_pattern_uses_blending(ctx, pdf_dict_get_val(ctx, obj, i))) |
435 | goto found; |
436 | |
437 | obj = pdf_dict_get(ctx, rdb, PDF_NAME(XObject)); |
438 | n = pdf_dict_len(ctx, obj); |
439 | for (i = 0; i < n; i++) |
440 | if (pdf_xobject_uses_blending(ctx, pdf_dict_get_val(ctx, obj, i))) |
441 | goto found; |
442 | if (0) |
443 | { |
444 | found: |
445 | useBM = 1; |
446 | } |
447 | } |
448 | fz_always(ctx) |
449 | { |
450 | pdf_unmark_obj(ctx, rdb); |
451 | } |
452 | fz_catch(ctx) |
453 | { |
454 | fz_rethrow(ctx); |
455 | } |
456 | |
457 | pdf_set_obj_memo(ctx, rdb, PDF_FLAGS_MEMO_BM, useBM); |
458 | return useBM; |
459 | } |
460 | |
461 | static int pdf_resources_use_overprint(fz_context *ctx, pdf_obj *rdb); |
462 | |
463 | static int |
464 | pdf_extgstate_uses_overprint(fz_context *ctx, pdf_obj *dict) |
465 | { |
466 | pdf_obj *obj = pdf_dict_get(ctx, dict, PDF_NAME(OP)); |
467 | if (obj && pdf_to_bool(ctx, obj)) |
468 | return 1; |
469 | return 0; |
470 | } |
471 | |
472 | static int |
473 | pdf_pattern_uses_overprint(fz_context *ctx, pdf_obj *dict) |
474 | { |
475 | pdf_obj *obj; |
476 | obj = pdf_dict_get(ctx, dict, PDF_NAME(Resources)); |
477 | if (pdf_resources_use_overprint(ctx, obj)) |
478 | return 1; |
479 | obj = pdf_dict_get(ctx, dict, PDF_NAME(ExtGState)); |
480 | return pdf_extgstate_uses_overprint(ctx, obj); |
481 | } |
482 | |
483 | static int |
484 | pdf_xobject_uses_overprint(fz_context *ctx, pdf_obj *dict) |
485 | { |
486 | pdf_obj *obj = pdf_dict_get(ctx, dict, PDF_NAME(Resources)); |
487 | return pdf_resources_use_overprint(ctx, obj); |
488 | } |
489 | |
490 | static int |
491 | pdf_resources_use_overprint(fz_context *ctx, pdf_obj *rdb) |
492 | { |
493 | pdf_obj *obj; |
494 | int i, n, useOP = 0; |
495 | |
496 | if (!rdb) |
497 | return 0; |
498 | |
499 | /* Have we been here before and remembered an answer? */ |
500 | if (pdf_obj_memo(ctx, rdb, PDF_FLAGS_MEMO_OP, &useOP)) |
501 | return useOP; |
502 | |
503 | /* stop on cyclic resource dependencies */ |
504 | if (pdf_mark_obj(ctx, rdb)) |
505 | return 0; |
506 | |
507 | fz_try(ctx) |
508 | { |
509 | obj = pdf_dict_get(ctx, rdb, PDF_NAME(ExtGState)); |
510 | n = pdf_dict_len(ctx, obj); |
511 | for (i = 0; i < n; i++) |
512 | if (pdf_extgstate_uses_overprint(ctx, pdf_dict_get_val(ctx, obj, i))) |
513 | goto found; |
514 | |
515 | obj = pdf_dict_get(ctx, rdb, PDF_NAME(Pattern)); |
516 | n = pdf_dict_len(ctx, obj); |
517 | for (i = 0; i < n; i++) |
518 | if (pdf_pattern_uses_overprint(ctx, pdf_dict_get_val(ctx, obj, i))) |
519 | goto found; |
520 | |
521 | obj = pdf_dict_get(ctx, rdb, PDF_NAME(XObject)); |
522 | n = pdf_dict_len(ctx, obj); |
523 | for (i = 0; i < n; i++) |
524 | if (pdf_xobject_uses_overprint(ctx, pdf_dict_get_val(ctx, obj, i))) |
525 | goto found; |
526 | if (0) |
527 | { |
528 | found: |
529 | useOP = 1; |
530 | } |
531 | } |
532 | fz_always(ctx) |
533 | { |
534 | pdf_unmark_obj(ctx, rdb); |
535 | } |
536 | fz_catch(ctx) |
537 | { |
538 | fz_rethrow(ctx); |
539 | } |
540 | |
541 | pdf_set_obj_memo(ctx, rdb, PDF_FLAGS_MEMO_OP, useOP); |
542 | return useOP; |
543 | } |
544 | |
545 | fz_transition * |
546 | pdf_page_presentation(fz_context *ctx, pdf_page *page, fz_transition *transition, float *duration) |
547 | { |
548 | pdf_obj *obj, *transdict; |
549 | |
550 | *duration = pdf_dict_get_real(ctx, page->obj, PDF_NAME(Dur)); |
551 | |
552 | transdict = pdf_dict_get(ctx, page->obj, PDF_NAME(Trans)); |
553 | if (!transdict) |
554 | return NULL; |
555 | |
556 | obj = pdf_dict_get(ctx, transdict, PDF_NAME(D)); |
557 | |
558 | transition->duration = (obj ? pdf_to_real(ctx, obj) : 1); |
559 | |
560 | transition->vertical = !pdf_name_eq(ctx, pdf_dict_get(ctx, transdict, PDF_NAME(Dm)), PDF_NAME(H)); |
561 | transition->outwards = !pdf_name_eq(ctx, pdf_dict_get(ctx, transdict, PDF_NAME(M)), PDF_NAME(I)); |
562 | /* FIXME: If 'Di' is None, it should be handled differently, but |
563 | * this only affects Fly, and we don't implement that currently. */ |
564 | transition->direction = (pdf_dict_get_int(ctx, transdict, PDF_NAME(Di))); |
565 | /* FIXME: Read SS for Fly when we implement it */ |
566 | /* FIXME: Read B for Fly when we implement it */ |
567 | |
568 | obj = pdf_dict_get(ctx, transdict, PDF_NAME(S)); |
569 | if (pdf_name_eq(ctx, obj, PDF_NAME(Split))) |
570 | transition->type = FZ_TRANSITION_SPLIT; |
571 | else if (pdf_name_eq(ctx, obj, PDF_NAME(Blinds))) |
572 | transition->type = FZ_TRANSITION_BLINDS; |
573 | else if (pdf_name_eq(ctx, obj, PDF_NAME(Box))) |
574 | transition->type = FZ_TRANSITION_BOX; |
575 | else if (pdf_name_eq(ctx, obj, PDF_NAME(Wipe))) |
576 | transition->type = FZ_TRANSITION_WIPE; |
577 | else if (pdf_name_eq(ctx, obj, PDF_NAME(Dissolve))) |
578 | transition->type = FZ_TRANSITION_DISSOLVE; |
579 | else if (pdf_name_eq(ctx, obj, PDF_NAME(Glitter))) |
580 | transition->type = FZ_TRANSITION_GLITTER; |
581 | else if (pdf_name_eq(ctx, obj, PDF_NAME(Fly))) |
582 | transition->type = FZ_TRANSITION_FLY; |
583 | else if (pdf_name_eq(ctx, obj, PDF_NAME(Push))) |
584 | transition->type = FZ_TRANSITION_PUSH; |
585 | else if (pdf_name_eq(ctx, obj, PDF_NAME(Cover))) |
586 | transition->type = FZ_TRANSITION_COVER; |
587 | else if (pdf_name_eq(ctx, obj, PDF_NAME(Uncover))) |
588 | transition->type = FZ_TRANSITION_UNCOVER; |
589 | else if (pdf_name_eq(ctx, obj, PDF_NAME(Fade))) |
590 | transition->type = FZ_TRANSITION_FADE; |
591 | else |
592 | transition->type = FZ_TRANSITION_NONE; |
593 | |
594 | return transition; |
595 | } |
596 | |
597 | /* |
598 | Determine the size of a page. |
599 | |
600 | Determine the page size in user space units, taking page rotation |
601 | into account. The page size is taken to be the crop box if it |
602 | exists (visible area after cropping), otherwise the media box will |
603 | be used (possibly including printing marks). |
604 | */ |
605 | fz_rect |
606 | pdf_bound_page(fz_context *ctx, pdf_page *page) |
607 | { |
608 | fz_matrix page_ctm; |
609 | fz_rect mediabox; |
610 | pdf_page_transform(ctx, page, &mediabox, &page_ctm); |
611 | return fz_transform_rect(mediabox, page_ctm); |
612 | } |
613 | |
614 | fz_link * |
615 | pdf_load_links(fz_context *ctx, pdf_page *page) |
616 | { |
617 | return fz_keep_link(ctx, page->links); |
618 | } |
619 | |
620 | pdf_obj * |
621 | pdf_page_resources(fz_context *ctx, pdf_page *page) |
622 | { |
623 | return pdf_dict_get_inheritable(ctx, page->obj, PDF_NAME(Resources)); |
624 | } |
625 | |
626 | pdf_obj * |
627 | pdf_page_contents(fz_context *ctx, pdf_page *page) |
628 | { |
629 | return pdf_dict_get(ctx, page->obj, PDF_NAME(Contents)); |
630 | } |
631 | |
632 | pdf_obj * |
633 | pdf_page_group(fz_context *ctx, pdf_page *page) |
634 | { |
635 | return pdf_dict_get(ctx, page->obj, PDF_NAME(Group)); |
636 | } |
637 | |
638 | void |
639 | pdf_page_obj_transform(fz_context *ctx, pdf_obj *pageobj, fz_rect *page_mediabox, fz_matrix *page_ctm) |
640 | { |
641 | pdf_obj *obj; |
642 | fz_rect mediabox, cropbox, realbox, pagebox; |
643 | float userunit = 1; |
644 | int rotate; |
645 | |
646 | if (!page_mediabox) |
647 | page_mediabox = &pagebox; |
648 | |
649 | obj = pdf_dict_get(ctx, pageobj, PDF_NAME(UserUnit)); |
650 | if (pdf_is_real(ctx, obj)) |
651 | userunit = pdf_to_real(ctx, obj); |
652 | |
653 | mediabox = pdf_to_rect(ctx, pdf_dict_get_inheritable(ctx, pageobj, PDF_NAME(MediaBox))); |
654 | if (fz_is_empty_rect(mediabox)) |
655 | { |
656 | mediabox.x0 = 0; |
657 | mediabox.y0 = 0; |
658 | mediabox.x1 = 612; |
659 | mediabox.y1 = 792; |
660 | } |
661 | |
662 | cropbox = pdf_to_rect(ctx, pdf_dict_get_inheritable(ctx, pageobj, PDF_NAME(CropBox))); |
663 | if (!fz_is_empty_rect(cropbox)) |
664 | mediabox = fz_intersect_rect(mediabox, cropbox); |
665 | |
666 | page_mediabox->x0 = fz_min(mediabox.x0, mediabox.x1); |
667 | page_mediabox->y0 = fz_min(mediabox.y0, mediabox.y1); |
668 | page_mediabox->x1 = fz_max(mediabox.x0, mediabox.x1); |
669 | page_mediabox->y1 = fz_max(mediabox.y0, mediabox.y1); |
670 | |
671 | if (page_mediabox->x1 - page_mediabox->x0 < 1 || page_mediabox->y1 - page_mediabox->y0 < 1) |
672 | *page_mediabox = fz_unit_rect; |
673 | |
674 | rotate = pdf_to_int(ctx, pdf_dict_get_inheritable(ctx, pageobj, PDF_NAME(Rotate))); |
675 | |
676 | /* Snap page rotation to 0, 90, 180 or 270 */ |
677 | if (rotate < 0) |
678 | rotate = 360 - ((-rotate) % 360); |
679 | if (rotate >= 360) |
680 | rotate = rotate % 360; |
681 | rotate = 90*((rotate + 45)/90); |
682 | if (rotate >= 360) |
683 | rotate = 0; |
684 | |
685 | /* Compute transform from fitz' page space (upper left page origin, y descending, 72 dpi) |
686 | * to PDF user space (arbitrary page origin, y ascending, UserUnit dpi). */ |
687 | |
688 | /* Make left-handed and scale by UserUnit */ |
689 | *page_ctm = fz_scale(userunit, -userunit); |
690 | |
691 | /* Rotate */ |
692 | *page_ctm = fz_pre_rotate(*page_ctm, -rotate); |
693 | |
694 | /* Translate page origin to 0,0 */ |
695 | realbox = fz_transform_rect(*page_mediabox, *page_ctm); |
696 | *page_ctm = fz_concat(*page_ctm, fz_translate(-realbox.x0, -realbox.y0)); |
697 | } |
698 | |
699 | void |
700 | pdf_page_transform(fz_context *ctx, pdf_page *page, fz_rect *page_mediabox, fz_matrix *page_ctm) |
701 | { |
702 | pdf_page_obj_transform(ctx, page->obj, page_mediabox, page_ctm); |
703 | } |
704 | |
705 | static void |
706 | find_seps(fz_context *ctx, fz_separations **seps, pdf_obj *obj, pdf_obj *clearme) |
707 | { |
708 | int i, n; |
709 | pdf_obj *nameobj; |
710 | |
711 | /* Indexed and DeviceN may have cyclic references */ |
712 | if (pdf_is_indirect(ctx, obj)) |
713 | { |
714 | if (pdf_mark_obj(ctx, obj)) |
715 | return; /* already been here */ |
716 | /* remember to clear this colorspace dictionary at the end */ |
717 | pdf_array_push(ctx, clearme, obj); |
718 | } |
719 | |
720 | nameobj = pdf_array_get(ctx, obj, 0); |
721 | if (pdf_name_eq(ctx, nameobj, PDF_NAME(Separation))) |
722 | { |
723 | fz_colorspace *cs; |
724 | const char *name = pdf_to_name(ctx, pdf_array_get(ctx, obj, 1)); |
725 | |
726 | /* Skip 'special' colorants. */ |
727 | if (!strcmp(name, "Black" ) || |
728 | !strcmp(name, "Cyan" ) || |
729 | !strcmp(name, "Magenta" ) || |
730 | !strcmp(name, "Yellow" ) || |
731 | !strcmp(name, "All" ) || |
732 | !strcmp(name, "None" )) |
733 | return; |
734 | |
735 | n = fz_count_separations(ctx, *seps); |
736 | for (i = 0; i < n; i++) |
737 | { |
738 | if (!strcmp(name, fz_separation_name(ctx, *seps, i))) |
739 | return; /* Got that one already */ |
740 | } |
741 | |
742 | fz_try(ctx) |
743 | cs = pdf_load_colorspace(ctx, obj); |
744 | fz_catch(ctx) |
745 | { |
746 | fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); |
747 | return; /* ignore broken colorspace */ |
748 | } |
749 | fz_try(ctx) |
750 | { |
751 | if (!*seps) |
752 | *seps = fz_new_separations(ctx, 0); |
753 | fz_add_separation(ctx, *seps, name, cs, 0); |
754 | } |
755 | fz_always(ctx) |
756 | fz_drop_colorspace(ctx, cs); |
757 | fz_catch(ctx) |
758 | fz_rethrow(ctx); |
759 | } |
760 | else if (pdf_name_eq(ctx, nameobj, PDF_NAME(Indexed))) |
761 | { |
762 | find_seps(ctx, seps, pdf_array_get(ctx, obj, 1), clearme); |
763 | } |
764 | else if (pdf_name_eq(ctx, nameobj, PDF_NAME(DeviceN))) |
765 | { |
766 | /* If the separation colorants exists for this DeviceN color space |
767 | * add those prior to our search for DeviceN color */ |
768 | pdf_obj *cols = pdf_dict_get(ctx, pdf_array_get(ctx, obj, 4), PDF_NAME(Colorants)); |
769 | n = pdf_dict_len(ctx, cols); |
770 | for (i = 0; i < n; i++) |
771 | find_seps(ctx, seps, pdf_dict_get_val(ctx, cols, i), clearme); |
772 | } |
773 | } |
774 | |
775 | static void |
776 | find_devn(fz_context *ctx, fz_separations **seps, pdf_obj *obj, pdf_obj *clearme) |
777 | { |
778 | int i, j, n, m; |
779 | pdf_obj *arr; |
780 | pdf_obj *nameobj = pdf_array_get(ctx, obj, 0); |
781 | |
782 | if (!pdf_name_eq(ctx, nameobj, PDF_NAME(DeviceN))) |
783 | return; |
784 | |
785 | arr = pdf_array_get(ctx, obj, 1); |
786 | m = pdf_array_len(ctx, arr); |
787 | for (j = 0; j < m; j++) |
788 | { |
789 | fz_colorspace *cs; |
790 | const char *name = pdf_to_name(ctx, pdf_array_get(ctx, arr, j)); |
791 | |
792 | /* Skip 'special' colorants. */ |
793 | if (!strcmp(name, "Black" ) || |
794 | !strcmp(name, "Cyan" ) || |
795 | !strcmp(name, "Magenta" ) || |
796 | !strcmp(name, "Yellow" ) || |
797 | !strcmp(name, "All" ) || |
798 | !strcmp(name, "None" )) |
799 | continue; |
800 | |
801 | n = fz_count_separations(ctx, *seps); |
802 | for (i = 0; i < n; i++) |
803 | { |
804 | if (!strcmp(name, fz_separation_name(ctx, *seps, i))) |
805 | break; /* Got that one already */ |
806 | } |
807 | |
808 | if (i == n) |
809 | { |
810 | fz_try(ctx) |
811 | cs = pdf_load_colorspace(ctx, obj); |
812 | fz_catch(ctx) |
813 | { |
814 | fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); |
815 | continue; /* ignore broken colorspace */ |
816 | } |
817 | fz_try(ctx) |
818 | { |
819 | if (!*seps) |
820 | *seps = fz_new_separations(ctx, 0); |
821 | fz_add_separation(ctx, *seps, name, cs, j); |
822 | } |
823 | fz_always(ctx) |
824 | fz_drop_colorspace(ctx, cs); |
825 | fz_catch(ctx) |
826 | fz_rethrow(ctx); |
827 | } |
828 | } |
829 | } |
830 | |
831 | typedef void (res_finder_fn)(fz_context *ctx, fz_separations **seps, pdf_obj *obj, pdf_obj *clearme); |
832 | |
833 | static void |
834 | scan_page_seps(fz_context *ctx, pdf_obj *res, fz_separations **seps, res_finder_fn *fn, pdf_obj *clearme) |
835 | { |
836 | pdf_obj *dict; |
837 | pdf_obj *obj; |
838 | int i, n; |
839 | |
840 | if (pdf_mark_obj(ctx, res)) |
841 | return; /* already been here */ |
842 | |
843 | /* remember to clear this resource dictionary at the end */ |
844 | pdf_array_push(ctx, clearme, res); |
845 | |
846 | dict = pdf_dict_get(ctx, res, PDF_NAME(ColorSpace)); |
847 | n = pdf_dict_len(ctx, dict); |
848 | for (i = 0; i < n; i++) |
849 | { |
850 | obj = pdf_dict_get_val(ctx, dict, i); |
851 | fn(ctx, seps, obj, clearme); |
852 | } |
853 | |
854 | dict = pdf_dict_get(ctx, res, PDF_NAME(Shading)); |
855 | n = pdf_dict_len(ctx, dict); |
856 | for (i = 0; i < n; i++) |
857 | { |
858 | obj = pdf_dict_get_val(ctx, dict, i); |
859 | fn(ctx, seps, pdf_dict_get(ctx, obj, PDF_NAME(ColorSpace)), clearme); |
860 | } |
861 | |
862 | dict = pdf_dict_get(ctx, res, PDF_NAME(XObject)); |
863 | n = pdf_dict_len(ctx, dict); |
864 | for (i = 0; i < n; i++) |
865 | { |
866 | obj = pdf_dict_get_val(ctx, dict, i); |
867 | fn(ctx, seps, pdf_dict_get(ctx, obj, PDF_NAME(ColorSpace)), clearme); |
868 | /* Recurse on XObject forms. */ |
869 | scan_page_seps(ctx, pdf_dict_get(ctx, obj, PDF_NAME(Resources)), seps, fn, clearme); |
870 | } |
871 | } |
872 | |
873 | /* |
874 | Get the separation details for a page. |
875 | */ |
876 | fz_separations * |
877 | pdf_page_separations(fz_context *ctx, pdf_page *page) |
878 | { |
879 | pdf_obj *res = pdf_page_resources(ctx, page); |
880 | pdf_obj *clearme = NULL; |
881 | fz_separations *seps = NULL; |
882 | |
883 | clearme = pdf_new_array(ctx, page->doc, 100); |
884 | fz_try(ctx) |
885 | { |
886 | /* Run through and look for separations first. This is |
887 | * because separations are simplest to deal with, and |
888 | * because DeviceN may be implemented on top of separations. |
889 | */ |
890 | scan_page_seps(ctx, res, &seps, find_seps, clearme); |
891 | } |
892 | fz_always(ctx) |
893 | { |
894 | int i, n = pdf_array_len(ctx, clearme); |
895 | for (i = 0; i < n; ++i) |
896 | pdf_unmark_obj(ctx, pdf_array_get(ctx, clearme, i)); |
897 | pdf_drop_obj(ctx, clearme); |
898 | } |
899 | fz_catch(ctx) |
900 | { |
901 | fz_drop_separations(ctx, seps); |
902 | fz_rethrow(ctx); |
903 | } |
904 | |
905 | clearme = pdf_new_array(ctx, page->doc, 100); |
906 | fz_try(ctx) |
907 | { |
908 | /* Now run through again, and look for DeviceNs. These may |
909 | * have spot colors in that aren't defined in terms of |
910 | * separations. */ |
911 | scan_page_seps(ctx, res, &seps, find_devn, clearme); |
912 | } |
913 | fz_always(ctx) |
914 | { |
915 | int i, n = pdf_array_len(ctx, clearme); |
916 | for (i = 0; i < n; ++i) |
917 | pdf_unmark_obj(ctx, pdf_array_get(ctx, clearme, i)); |
918 | pdf_drop_obj(ctx, clearme); |
919 | } |
920 | fz_catch(ctx) |
921 | { |
922 | fz_drop_separations(ctx, seps); |
923 | fz_rethrow(ctx); |
924 | } |
925 | |
926 | return seps; |
927 | } |
928 | |
929 | int |
930 | pdf_page_uses_overprint(fz_context *ctx, pdf_page *page) |
931 | { |
932 | return page ? page->overprint : 0; |
933 | } |
934 | |
935 | static void |
936 | pdf_drop_page_imp(fz_context *ctx, pdf_page *page) |
937 | { |
938 | fz_drop_link(ctx, page->links); |
939 | pdf_drop_annots(ctx, page->annots); |
940 | pdf_drop_widgets(ctx, page->widgets); |
941 | |
942 | pdf_drop_obj(ctx, page->obj); |
943 | |
944 | fz_drop_document(ctx, &page->doc->super); |
945 | } |
946 | |
947 | static pdf_page * |
948 | pdf_new_page(fz_context *ctx, pdf_document *doc) |
949 | { |
950 | pdf_page *page = fz_new_derived_page(ctx, pdf_page); |
951 | |
952 | page->doc = (pdf_document*) fz_keep_document(ctx, &doc->super); |
953 | |
954 | page->super.drop_page = (fz_page_drop_page_fn*)pdf_drop_page_imp; |
955 | page->super.load_links = (fz_page_load_links_fn*)pdf_load_links; |
956 | page->super.bound_page = (fz_page_bound_page_fn*)pdf_bound_page; |
957 | page->super.run_page_contents = (fz_page_run_page_fn*)pdf_run_page_contents; |
958 | page->super.run_page_annots = (fz_page_run_page_fn*)pdf_run_page_annots; |
959 | page->super.run_page_widgets = (fz_page_run_page_fn*)pdf_run_page_widgets; |
960 | page->super.page_presentation = (fz_page_page_presentation_fn*)pdf_page_presentation; |
961 | page->super.separations = (fz_page_separations_fn *)pdf_page_separations; |
962 | page->super.overprint = (fz_page_uses_overprint_fn *)pdf_page_uses_overprint; |
963 | |
964 | page->obj = NULL; |
965 | |
966 | page->transparency = 0; |
967 | page->links = NULL; |
968 | page->annots = NULL; |
969 | page->annot_tailp = &page->annots; |
970 | page->widgets = NULL; |
971 | page->widget_tailp = &page->widgets; |
972 | |
973 | return page; |
974 | } |
975 | |
976 | static void |
977 | pdf_load_default_colorspaces_imp(fz_context *ctx, fz_default_colorspaces *default_cs, pdf_obj *obj) |
978 | { |
979 | pdf_obj *cs_obj; |
980 | |
981 | /* The spec says to ignore any colors we can't understand */ |
982 | |
983 | cs_obj = pdf_dict_get(ctx, obj, PDF_NAME(DefaultGray)); |
984 | if (cs_obj) |
985 | { |
986 | fz_try(ctx) |
987 | { |
988 | fz_colorspace *cs = pdf_load_colorspace(ctx, cs_obj); |
989 | fz_set_default_gray(ctx, default_cs, cs); |
990 | fz_drop_colorspace(ctx, cs); |
991 | } |
992 | fz_catch(ctx) |
993 | fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); |
994 | } |
995 | |
996 | cs_obj = pdf_dict_get(ctx, obj, PDF_NAME(DefaultRGB)); |
997 | if (cs_obj) |
998 | { |
999 | fz_try(ctx) |
1000 | { |
1001 | fz_colorspace *cs = pdf_load_colorspace(ctx, cs_obj); |
1002 | fz_set_default_rgb(ctx, default_cs, cs); |
1003 | fz_drop_colorspace(ctx, cs); |
1004 | } |
1005 | fz_catch(ctx) |
1006 | fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); |
1007 | } |
1008 | |
1009 | cs_obj = pdf_dict_get(ctx, obj, PDF_NAME(DefaultCMYK)); |
1010 | if (cs_obj) |
1011 | { |
1012 | fz_try(ctx) |
1013 | { |
1014 | fz_colorspace *cs = pdf_load_colorspace(ctx, cs_obj); |
1015 | fz_set_default_cmyk(ctx, default_cs, cs); |
1016 | fz_drop_colorspace(ctx, cs); |
1017 | } |
1018 | fz_catch(ctx) |
1019 | fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); |
1020 | } |
1021 | } |
1022 | |
1023 | fz_default_colorspaces * |
1024 | pdf_load_default_colorspaces(fz_context *ctx, pdf_document *doc, pdf_page *page) |
1025 | { |
1026 | pdf_obj *res; |
1027 | pdf_obj *obj; |
1028 | fz_default_colorspaces *default_cs; |
1029 | fz_colorspace *oi; |
1030 | |
1031 | default_cs = fz_new_default_colorspaces(ctx); |
1032 | |
1033 | fz_try(ctx) |
1034 | { |
1035 | res = pdf_page_resources(ctx, page); |
1036 | obj = pdf_dict_get(ctx, res, PDF_NAME(ColorSpace)); |
1037 | if (obj) |
1038 | pdf_load_default_colorspaces_imp(ctx, default_cs, obj); |
1039 | |
1040 | oi = pdf_document_output_intent(ctx, doc); |
1041 | if (oi) |
1042 | fz_set_default_output_intent(ctx, default_cs, oi); |
1043 | } |
1044 | fz_catch(ctx) |
1045 | { |
1046 | if (fz_caught(ctx) != FZ_ERROR_TRYLATER) |
1047 | { |
1048 | fz_drop_default_colorspaces(ctx, default_cs); |
1049 | fz_rethrow(ctx); |
1050 | } |
1051 | page->super.incomplete = 1; |
1052 | } |
1053 | |
1054 | return default_cs; |
1055 | } |
1056 | |
1057 | /* |
1058 | Update default colorspaces for an xobject. |
1059 | */ |
1060 | fz_default_colorspaces * |
1061 | pdf_update_default_colorspaces(fz_context *ctx, fz_default_colorspaces *old_cs, pdf_obj *res) |
1062 | { |
1063 | pdf_obj *obj; |
1064 | fz_default_colorspaces *new_cs; |
1065 | |
1066 | obj = pdf_dict_get(ctx, res, PDF_NAME(ColorSpace)); |
1067 | if (!obj) |
1068 | return fz_keep_default_colorspaces(ctx, old_cs); |
1069 | |
1070 | new_cs = fz_clone_default_colorspaces(ctx, old_cs); |
1071 | fz_try(ctx) |
1072 | pdf_load_default_colorspaces_imp(ctx, new_cs, obj); |
1073 | fz_catch(ctx) |
1074 | { |
1075 | fz_drop_default_colorspaces(ctx, new_cs); |
1076 | fz_rethrow(ctx); |
1077 | } |
1078 | |
1079 | return new_cs; |
1080 | } |
1081 | |
1082 | /* |
1083 | Load a page and its resources. |
1084 | |
1085 | Locates the page in the PDF document and loads the page and its |
1086 | resources. After pdf_load_page is it possible to retrieve the size |
1087 | of the page using pdf_bound_page, or to render the page using |
1088 | pdf_run_page_*. |
1089 | |
1090 | number: page number, where 0 is the first page of the document. |
1091 | */ |
1092 | pdf_page * |
1093 | pdf_load_page(fz_context *ctx, pdf_document *doc, int number) |
1094 | { |
1095 | pdf_page *page; |
1096 | pdf_annot *annot; |
1097 | pdf_obj *pageobj, *obj; |
1098 | |
1099 | if (doc->file_reading_linearly) |
1100 | { |
1101 | pageobj = pdf_progressive_advance(ctx, doc, number); |
1102 | if (pageobj == NULL) |
1103 | fz_throw(ctx, FZ_ERROR_TRYLATER, "page %d not available yet" , number); |
1104 | } |
1105 | else |
1106 | pageobj = pdf_lookup_page_obj(ctx, doc, number); |
1107 | |
1108 | page = pdf_new_page(ctx, doc); |
1109 | page->obj = pdf_keep_obj(ctx, pageobj); |
1110 | |
1111 | /* Pre-load annotations and links */ |
1112 | fz_try(ctx) |
1113 | { |
1114 | obj = pdf_dict_get(ctx, pageobj, PDF_NAME(Annots)); |
1115 | if (obj) |
1116 | { |
1117 | fz_rect page_mediabox; |
1118 | fz_matrix page_ctm; |
1119 | pdf_page_transform(ctx, page, &page_mediabox, &page_ctm); |
1120 | page->links = pdf_load_link_annots(ctx, doc, obj, number, page_ctm); |
1121 | pdf_load_annots(ctx, page, obj); |
1122 | } |
1123 | } |
1124 | fz_catch(ctx) |
1125 | { |
1126 | if (fz_caught(ctx) != FZ_ERROR_TRYLATER) |
1127 | { |
1128 | fz_drop_page(ctx, &page->super); |
1129 | fz_rethrow(ctx); |
1130 | } |
1131 | page->super.incomplete = 1; |
1132 | fz_drop_link(ctx, page->links); |
1133 | page->links = NULL; |
1134 | } |
1135 | |
1136 | /* Scan for transparency and overprint */ |
1137 | fz_try(ctx) |
1138 | { |
1139 | pdf_obj *resources = pdf_page_resources(ctx, page); |
1140 | if (pdf_name_eq(ctx, pdf_dict_getp(ctx, pageobj, "Group/S" ), PDF_NAME(Transparency))) |
1141 | page->transparency = 1; |
1142 | else if (pdf_resources_use_blending(ctx, resources)) |
1143 | page->transparency = 1; |
1144 | for (annot = page->annots; annot && !page->transparency; annot = annot->next) |
1145 | if (annot->ap && pdf_resources_use_blending(ctx, pdf_xobject_resources(ctx, annot->ap))) |
1146 | page->transparency = 1; |
1147 | if (pdf_resources_use_overprint(ctx, resources)) |
1148 | page->overprint = 1; |
1149 | for (annot = page->annots; annot && !page->overprint; annot = annot->next) |
1150 | if (annot->ap && pdf_resources_use_overprint(ctx, pdf_xobject_resources(ctx, annot->ap))) |
1151 | page->overprint = 1; |
1152 | } |
1153 | fz_catch(ctx) |
1154 | { |
1155 | if (fz_caught(ctx) != FZ_ERROR_TRYLATER) |
1156 | { |
1157 | fz_drop_page(ctx, &page->super); |
1158 | fz_rethrow(ctx); |
1159 | } |
1160 | page->super.incomplete = 1; |
1161 | } |
1162 | |
1163 | return page; |
1164 | } |
1165 | |
1166 | /* |
1167 | Delete a page from the page tree of |
1168 | a document. This does not remove the page contents |
1169 | or resources from the file. |
1170 | |
1171 | doc: The document to operate on. |
1172 | |
1173 | number: The page to remove (numbered from 0) |
1174 | */ |
1175 | void |
1176 | pdf_delete_page(fz_context *ctx, pdf_document *doc, int at) |
1177 | { |
1178 | pdf_obj *parent, *kids; |
1179 | int i; |
1180 | |
1181 | pdf_lookup_page_loc(ctx, doc, at, &parent, &i); |
1182 | kids = pdf_dict_get(ctx, parent, PDF_NAME(Kids)); |
1183 | pdf_array_delete(ctx, kids, i); |
1184 | |
1185 | while (parent) |
1186 | { |
1187 | int count = pdf_dict_get_int(ctx, parent, PDF_NAME(Count)); |
1188 | pdf_dict_put_int(ctx, parent, PDF_NAME(Count), count - 1); |
1189 | parent = pdf_dict_get(ctx, parent, PDF_NAME(Parent)); |
1190 | } |
1191 | } |
1192 | |
1193 | /* |
1194 | Delete a range of pages from the |
1195 | page tree of a document. This does not remove the page |
1196 | contents or resources from the file. |
1197 | |
1198 | doc: The document to operate on. |
1199 | |
1200 | start, end: The range of pages (numbered from 0) |
1201 | (inclusive, exclusive) to remove. If end is negative or |
1202 | greater than the number of pages in the document, it |
1203 | will be taken to be the end of the document. |
1204 | */ |
1205 | void |
1206 | pdf_delete_page_range(fz_context *ctx, pdf_document *doc, int start, int end) |
1207 | { |
1208 | int count = pdf_count_pages(ctx, doc); |
1209 | |
1210 | if (end < 0 || end > count) |
1211 | end = count+1; |
1212 | if (start < 0) |
1213 | start = 0; |
1214 | while (start < end) |
1215 | { |
1216 | pdf_delete_page(ctx, doc, start); |
1217 | end--; |
1218 | } |
1219 | } |
1220 | |
1221 | /* |
1222 | Create a pdf_obj within a document that |
1223 | represents a page, from a previously created resources |
1224 | dictionary and page content stream. This should then be |
1225 | inserted into the document using pdf_insert_page. |
1226 | |
1227 | After this call the page exists within the document |
1228 | structure, but is not actually ever displayed as it is |
1229 | not linked into the PDF page tree. |
1230 | |
1231 | doc: The document to which to add the page. |
1232 | |
1233 | mediabox: The mediabox for the page (should be identical |
1234 | to that used when creating the resources/contents). |
1235 | |
1236 | rotate: 0, 90, 180 or 270. The rotation to use for the |
1237 | page. |
1238 | |
1239 | resources: The resources dictionary for the new page |
1240 | (typically created by pdf_page_write). |
1241 | |
1242 | contents: The page contents for the new page (typically |
1243 | create by pdf_page_write). |
1244 | */ |
1245 | pdf_obj * |
1246 | pdf_add_page(fz_context *ctx, pdf_document *doc, fz_rect mediabox, int rotate, pdf_obj *resources, fz_buffer *contents) |
1247 | { |
1248 | pdf_obj *page_obj = pdf_new_dict(ctx, doc, 5); |
1249 | fz_try(ctx) |
1250 | { |
1251 | pdf_dict_put(ctx, page_obj, PDF_NAME(Type), PDF_NAME(Page)); |
1252 | pdf_dict_put_rect(ctx, page_obj, PDF_NAME(MediaBox), mediabox); |
1253 | pdf_dict_put_int(ctx, page_obj, PDF_NAME(Rotate), rotate); |
1254 | |
1255 | if (pdf_is_indirect(ctx, resources)) |
1256 | pdf_dict_put(ctx, page_obj, PDF_NAME(Resources), resources); |
1257 | else if (pdf_is_dict(ctx, resources)) |
1258 | pdf_dict_put_drop(ctx, page_obj, PDF_NAME(Resources), pdf_add_object(ctx, doc, resources)); |
1259 | else |
1260 | pdf_dict_put_dict(ctx, page_obj, PDF_NAME(Resources), 1); |
1261 | |
1262 | if (contents) |
1263 | pdf_dict_put_drop(ctx, page_obj, PDF_NAME(Contents), pdf_add_stream(ctx, doc, contents, NULL, 0)); |
1264 | } |
1265 | fz_catch(ctx) |
1266 | { |
1267 | pdf_drop_obj(ctx, page_obj); |
1268 | fz_rethrow(ctx); |
1269 | } |
1270 | return pdf_add_object_drop(ctx, doc, page_obj); |
1271 | } |
1272 | |
1273 | /* |
1274 | Insert a page previously created by |
1275 | pdf_add_page into the pages tree of the document. |
1276 | |
1277 | doc: The document to insert into. |
1278 | |
1279 | at: The page number to insert at. 0 inserts at the start. |
1280 | negative numbers, or INT_MAX insert at the end. Otherwise |
1281 | n inserts after page n. |
1282 | |
1283 | page: The page to insert. |
1284 | */ |
1285 | void |
1286 | pdf_insert_page(fz_context *ctx, pdf_document *doc, int at, pdf_obj *page_ref) |
1287 | { |
1288 | int count = pdf_count_pages(ctx, doc); |
1289 | pdf_obj *parent, *kids; |
1290 | int i; |
1291 | |
1292 | if (at < 0) |
1293 | at = count; |
1294 | if (at == INT_MAX) |
1295 | at = count; |
1296 | if (at > count) |
1297 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot insert page beyond end of page tree" ); |
1298 | |
1299 | if (count == 0) |
1300 | { |
1301 | pdf_obj *root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root)); |
1302 | parent = pdf_dict_get(ctx, root, PDF_NAME(Pages)); |
1303 | if (!parent) |
1304 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find page tree" ); |
1305 | kids = pdf_dict_get(ctx, parent, PDF_NAME(Kids)); |
1306 | if (!kids) |
1307 | fz_throw(ctx, FZ_ERROR_GENERIC, "malformed page tree" ); |
1308 | pdf_array_insert(ctx, kids, page_ref, 0); |
1309 | } |
1310 | else if (at == count) |
1311 | { |
1312 | /* append after last page */ |
1313 | pdf_lookup_page_loc(ctx, doc, count - 1, &parent, &i); |
1314 | kids = pdf_dict_get(ctx, parent, PDF_NAME(Kids)); |
1315 | pdf_array_insert(ctx, kids, page_ref, i + 1); |
1316 | } |
1317 | else |
1318 | { |
1319 | /* insert before found page */ |
1320 | pdf_lookup_page_loc(ctx, doc, at, &parent, &i); |
1321 | kids = pdf_dict_get(ctx, parent, PDF_NAME(Kids)); |
1322 | pdf_array_insert(ctx, kids, page_ref, i); |
1323 | } |
1324 | |
1325 | pdf_dict_put(ctx, page_ref, PDF_NAME(Parent), parent); |
1326 | |
1327 | /* Adjust page counts */ |
1328 | while (parent) |
1329 | { |
1330 | count = pdf_dict_get_int(ctx, parent, PDF_NAME(Count)); |
1331 | pdf_dict_put_int(ctx, parent, PDF_NAME(Count), count + 1); |
1332 | parent = pdf_dict_get(ctx, parent, PDF_NAME(Parent)); |
1333 | } |
1334 | } |
1335 | |