1#include "mupdf/fitz.h"
2#include "mupdf/pdf.h"
3
4#include <stdlib.h>
5#include <string.h>
6#include <limits.h>
7
8int
9pdf_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
18static int
19pdf_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
52static int
53cmp_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
60void
61pdf_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
72void
73pdf_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
80enum
81{
82 LOCAL_STACK_SIZE = 16
83};
84
85static pdf_obj *
86pdf_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
188pdf_obj *
189pdf_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
205pdf_obj *
206pdf_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
211static int
212pdf_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
235static int
236pdf_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
277static int
278pdf_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
296int
297pdf_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*/
311int
312pdf_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
340static void
341pdf_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*/
354void
355pdf_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 */
369enum
370{
371 PDF_FLAGS_MEMO_BM = 0,
372 PDF_FLAGS_MEMO_OP = 1
373};
374
375static int pdf_resources_use_blending(fz_context *ctx, pdf_obj *rdb);
376
377static int
378pdf_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
386static int
387pdf_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
397static int
398pdf_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
406static int
407pdf_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 {
444found:
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
461static int pdf_resources_use_overprint(fz_context *ctx, pdf_obj *rdb);
462
463static int
464pdf_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
472static int
473pdf_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
483static int
484pdf_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
490static int
491pdf_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 {
528found:
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
545fz_transition *
546pdf_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*/
605fz_rect
606pdf_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
614fz_link *
615pdf_load_links(fz_context *ctx, pdf_page *page)
616{
617 return fz_keep_link(ctx, page->links);
618}
619
620pdf_obj *
621pdf_page_resources(fz_context *ctx, pdf_page *page)
622{
623 return pdf_dict_get_inheritable(ctx, page->obj, PDF_NAME(Resources));
624}
625
626pdf_obj *
627pdf_page_contents(fz_context *ctx, pdf_page *page)
628{
629 return pdf_dict_get(ctx, page->obj, PDF_NAME(Contents));
630}
631
632pdf_obj *
633pdf_page_group(fz_context *ctx, pdf_page *page)
634{
635 return pdf_dict_get(ctx, page->obj, PDF_NAME(Group));
636}
637
638void
639pdf_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
699void
700pdf_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
705static void
706find_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
775static void
776find_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
831typedef void (res_finder_fn)(fz_context *ctx, fz_separations **seps, pdf_obj *obj, pdf_obj *clearme);
832
833static void
834scan_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*/
876fz_separations *
877pdf_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
929int
930pdf_page_uses_overprint(fz_context *ctx, pdf_page *page)
931{
932 return page ? page->overprint : 0;
933}
934
935static void
936pdf_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
947static pdf_page *
948pdf_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
976static void
977pdf_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
1023fz_default_colorspaces *
1024pdf_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*/
1060fz_default_colorspaces *
1061pdf_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*/
1092pdf_page *
1093pdf_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*/
1175void
1176pdf_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*/
1205void
1206pdf_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*/
1245pdf_obj *
1246pdf_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*/
1285void
1286pdf_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