4 pdf_count_pages(pdf_document *doc)
6 if (doc->page_count == 0)
8 pdf_obj *count = pdf_dict_getp(pdf_trailer(doc), "Root/Pages/Count");
9 doc->page_count = pdf_to_int(count);
11 return doc->page_count;
20 pdf_lookup_page_loc_imp(pdf_document *doc, pdf_obj *node, int *skip, pdf_obj **parentp, int *indexp)
22 fz_context *ctx = doc->ctx;
26 pdf_obj *local_stack[LOCAL_STACK_SIZE];
27 pdf_obj **stack = &local_stack[0];
28 int stack_max = LOCAL_STACK_SIZE;
40 kids = pdf_dict_gets(node, "Kids");
41 len = pdf_array_len(kids);
44 fz_throw(ctx, FZ_ERROR_GENERIC, "Malformed pages tree");
46 /* Every node we need to unmark goes into the stack */
47 if (stack_len == stack_max)
49 if (stack == &local_stack[0])
51 stack = fz_malloc_array(ctx, stack_max * 2, sizeof(*stack));
52 memcpy(stack, &local_stack[0], stack_max * sizeof(*stack));
55 stack = fz_resize_array(ctx, stack, stack_max * 2, sizeof(*stack));
58 stack[stack_len++] = node;
60 if (pdf_mark_obj(node))
61 fz_throw(ctx, FZ_ERROR_GENERIC, "cycle in page tree");
63 for (i = 0; i < len; i++)
65 pdf_obj *kid = pdf_array_get(kids, i);
66 char *type = pdf_to_name(pdf_dict_gets(kid, "Type"));
67 if (*type ? !strcmp(type, "Pages") : pdf_dict_gets(kid, "Kids") && !pdf_dict_gets(kid, "MediaBox"))
69 int count = pdf_to_int(pdf_dict_gets(kid, "Count"));
82 if (*type ? strcmp(type, "Page") != 0 : !pdf_dict_gets(kid, "MediaBox"))
83 fz_warn(ctx, "non-page object in page tree (%s)", type);
86 if (parentp) *parentp = node;
87 if (indexp) *indexp = i;
102 for (i = stack_len; i > 0; i--)
103 pdf_unmark_obj(stack[i-1]);
104 if (stack != &local_stack[0])
116 pdf_lookup_page_loc(pdf_document *doc, int needle, pdf_obj **parentp, int *indexp)
118 pdf_obj *root = pdf_dict_gets(pdf_trailer(doc), "Root");
119 pdf_obj *node = pdf_dict_gets(root, "Pages");
124 fz_throw(doc->ctx, FZ_ERROR_GENERIC, "cannot find page tree");
126 hit = pdf_lookup_page_loc_imp(doc, node, &skip, parentp, indexp);
128 fz_throw(doc->ctx, FZ_ERROR_GENERIC, "cannot find page %d in page tree", needle);
133 pdf_lookup_page_obj(pdf_document *doc, int needle)
135 return pdf_lookup_page_loc(doc, needle, NULL, NULL);
139 pdf_count_pages_before_kid(pdf_document *doc, pdf_obj *parent, int kid_num)
141 pdf_obj *kids = pdf_dict_gets(parent, "Kids");
142 int i, total = 0, len = pdf_array_len(kids);
143 for (i = 0; i < len; i++)
145 pdf_obj *kid = pdf_array_get(kids, i);
146 if (pdf_to_num(kid) == kid_num)
148 if (!strcmp(pdf_to_name(pdf_dict_gets(kid, "Type")), "Pages"))
150 pdf_obj *count = pdf_dict_gets(kid, "Count");
151 int n = pdf_to_int(count);
152 if (!pdf_is_int(count) || n < 0)
153 fz_throw(doc->ctx, FZ_ERROR_GENERIC, "illegal or missing count in pages tree");
159 fz_throw(doc->ctx, FZ_ERROR_GENERIC, "kid not found in parent's kids array");
163 pdf_lookup_page_number(pdf_document *doc, pdf_obj *node)
165 fz_context *ctx = doc->ctx;
166 int needle = pdf_to_num(node);
168 pdf_obj *parent, *parent2;
170 if (strcmp(pdf_to_name(pdf_dict_gets(node, "Type")), "Page") != 0)
171 fz_throw(ctx, FZ_ERROR_GENERIC, "invalid page object");
173 parent2 = parent = pdf_dict_gets(node, "Parent");
177 while (pdf_is_dict(parent))
179 if (pdf_mark_obj(parent))
180 fz_throw(ctx, FZ_ERROR_GENERIC, "cycle in page tree (parents)");
181 total += pdf_count_pages_before_kid(doc, parent, needle);
182 needle = pdf_to_num(parent);
183 parent = pdf_dict_gets(parent, "Parent");
188 /* Run back and unmark */
191 pdf_unmark_obj(parent2);
192 if (parent2 == parent)
194 parent2 = pdf_dict_gets(parent2, "Parent");
206 pdf_lookup_inherited_page_item(pdf_document *doc, pdf_obj *node, const char *key)
208 fz_context *ctx = doc->ctx;
209 pdf_obj *node2 = node;
212 /* fz_var(node); Not required as node passed in */
218 val = pdf_dict_gets(node, key);
221 if (pdf_mark_obj(node))
222 fz_throw(ctx, FZ_ERROR_GENERIC, "cycle in page tree (parents)");
223 node = pdf_dict_gets(node, "Parent");
231 pdf_unmark_obj(node2);
234 node2 = pdf_dict_gets(node2, "Parent");
246 /* We need to know whether to install a page-level transparency group */
248 static int pdf_resources_use_blending(pdf_document *doc, pdf_obj *rdb);
251 pdf_extgstate_uses_blending(pdf_document *doc, pdf_obj *dict)
253 pdf_obj *obj = pdf_dict_gets(dict, "BM");
254 if (pdf_is_name(obj) && strcmp(pdf_to_name(obj), "Normal"))
260 pdf_pattern_uses_blending(pdf_document *doc, pdf_obj *dict)
263 obj = pdf_dict_gets(dict, "Resources");
264 if (pdf_resources_use_blending(doc, obj))
266 obj = pdf_dict_gets(dict, "ExtGState");
267 return pdf_extgstate_uses_blending(doc, obj);
271 pdf_xobject_uses_blending(pdf_document *doc, pdf_obj *dict)
273 pdf_obj *obj = pdf_dict_gets(dict, "Resources");
274 if (!strcmp(pdf_to_name(pdf_dict_getp(dict, "Group/S")), "Transparency"))
276 return pdf_resources_use_blending(doc, obj);
280 pdf_resources_use_blending(pdf_document *doc, pdf_obj *rdb)
282 fz_context *ctx = doc->ctx;
289 /* Have we been here before and remembered an answer? */
290 if (pdf_obj_memo(rdb, &useBM))
293 /* stop on cyclic resource dependencies */
294 if (pdf_mark_obj(rdb))
299 obj = pdf_dict_gets(rdb, "ExtGState");
300 n = pdf_dict_len(obj);
301 for (i = 0; i < n; i++)
302 if (pdf_extgstate_uses_blending(doc, pdf_dict_get_val(obj, i)))
305 obj = pdf_dict_gets(rdb, "Pattern");
306 n = pdf_dict_len(obj);
307 for (i = 0; i < n; i++)
308 if (pdf_pattern_uses_blending(doc, pdf_dict_get_val(obj, i)))
311 obj = pdf_dict_gets(rdb, "XObject");
312 n = pdf_dict_len(obj);
313 for (i = 0; i < n; i++)
314 if (pdf_xobject_uses_blending(doc, pdf_dict_get_val(obj, i)))
331 pdf_set_obj_memo(rdb, useBM);
336 pdf_load_transition(pdf_document *doc, pdf_page *page, pdf_obj *transdict)
342 obj = pdf_dict_gets(transdict, "D");
343 page->transition.duration = (obj ? pdf_to_real(obj) : 1);
345 page->transition.vertical = (pdf_to_name(pdf_dict_gets(transdict, "Dm"))[0] != 'H');
346 page->transition.outwards = (pdf_to_name(pdf_dict_gets(transdict, "M"))[0] != 'I');
347 /* FIXME: If 'Di' is None, it should be handled differently, but
348 * this only affects Fly, and we don't implement that currently. */
349 page->transition.direction = (pdf_to_int(pdf_dict_gets(transdict, "Di")));
350 /* FIXME: Read SS for Fly when we implement it */
351 /* FIXME: Read B for Fly when we implement it */
353 name = pdf_to_name(pdf_dict_gets(transdict, "S"));
354 if (!strcmp(name, "Split"))
355 type = FZ_TRANSITION_SPLIT;
356 else if (!strcmp(name, "Blinds"))
357 type = FZ_TRANSITION_BLINDS;
358 else if (!strcmp(name, "Box"))
359 type = FZ_TRANSITION_BOX;
360 else if (!strcmp(name, "Wipe"))
361 type = FZ_TRANSITION_WIPE;
362 else if (!strcmp(name, "Dissolve"))
363 type = FZ_TRANSITION_DISSOLVE;
364 else if (!strcmp(name, "Glitter"))
365 type = FZ_TRANSITION_GLITTER;
366 else if (!strcmp(name, "Fly"))
367 type = FZ_TRANSITION_FLY;
368 else if (!strcmp(name, "Push"))
369 type = FZ_TRANSITION_PUSH;
370 else if (!strcmp(name, "Cover"))
371 type = FZ_TRANSITION_COVER;
372 else if (!strcmp(name, "Uncover"))
373 type = FZ_TRANSITION_UNCOVER;
374 else if (!strcmp(name, "Fade"))
375 type = FZ_TRANSITION_FADE;
377 type = FZ_TRANSITION_NONE;
378 page->transition.type = type;
382 pdf_load_page(pdf_document *doc, int number)
384 fz_context *ctx = doc->ctx;
387 pdf_obj *pageobj, *pageref, *obj;
388 fz_rect mediabox, cropbox, realbox;
392 if (doc->file_reading_linearly)
394 pageref = pdf_progressive_advance(doc, number);
396 fz_throw(doc->ctx, FZ_ERROR_TRYLATER, "page %d not available yet", number);
399 pageref = pdf_lookup_page_obj(doc, number);
400 pageobj = pdf_resolve_indirect(pageref);
402 page = fz_malloc_struct(ctx, pdf_page);
403 page->resources = NULL;
404 page->contents = NULL;
405 page->transparency = 0;
408 page->annot_tailp = &page->annots;
409 page->deleted_annots = NULL;
410 page->tmp_annots = NULL;
411 page->me = pdf_keep_obj(pageobj);
412 page->incomplete = 0;
414 obj = pdf_dict_gets(pageobj, "UserUnit");
415 if (pdf_is_real(obj))
416 userunit = pdf_to_real(obj);
420 pdf_to_rect(ctx, pdf_lookup_inherited_page_item(doc, pageobj, "MediaBox"), &mediabox);
421 if (fz_is_empty_rect(&mediabox))
423 fz_warn(ctx, "cannot find page size for page %d", number + 1);
430 pdf_to_rect(ctx, pdf_lookup_inherited_page_item(doc, pageobj, "CropBox"), &cropbox);
431 if (!fz_is_empty_rect(&cropbox))
432 fz_intersect_rect(&mediabox, &cropbox);
434 page->mediabox.x0 = fz_min(mediabox.x0, mediabox.x1) * userunit;
435 page->mediabox.y0 = fz_min(mediabox.y0, mediabox.y1) * userunit;
436 page->mediabox.x1 = fz_max(mediabox.x0, mediabox.x1) * userunit;
437 page->mediabox.y1 = fz_max(mediabox.y0, mediabox.y1) * userunit;
439 if (page->mediabox.x1 - page->mediabox.x0 < 1 || page->mediabox.y1 - page->mediabox.y0 < 1)
441 fz_warn(ctx, "invalid page size in page %d", number + 1);
442 page->mediabox = fz_unit_rect;
445 page->rotate = pdf_to_int(pdf_lookup_inherited_page_item(doc, pageobj, "Rotate"));
446 /* Snap page->rotate to 0, 90, 180 or 270 */
447 if (page->rotate < 0)
448 page->rotate = 360 - ((-page->rotate) % 360);
449 if (page->rotate >= 360)
450 page->rotate = page->rotate % 360;
451 page->rotate = 90*((page->rotate + 45)/90);
452 if (page->rotate > 360)
455 fz_pre_rotate(fz_scale(&page->ctm, 1, -1), -page->rotate);
456 realbox = page->mediabox;
457 fz_transform_rect(&realbox, &page->ctm);
458 fz_pre_scale(fz_translate(&mat, -realbox.x0, -realbox.y0), userunit, userunit);
459 fz_concat(&page->ctm, &page->ctm, &mat);
463 obj = pdf_dict_gets(pageobj, "Annots");
466 page->links = pdf_load_link_annots(doc, obj, &page->ctm);
467 pdf_load_annots(doc, page, obj);
472 if (fz_caught(ctx) != FZ_ERROR_TRYLATER)
474 page->incomplete |= PDF_PAGE_INCOMPLETE_ANNOTS;
475 fz_drop_link(ctx, page->links);
479 page->duration = pdf_to_real(pdf_dict_gets(pageobj, "Dur"));
481 obj = pdf_dict_gets(pageobj, "Trans");
482 page->transition_present = (obj != NULL);
485 pdf_load_transition(doc, page, obj);
489 page->resources = pdf_lookup_inherited_page_item(doc, pageobj, "Resources");
491 pdf_keep_obj(page->resources);
493 obj = pdf_dict_gets(pageobj, "Contents");
496 page->contents = pdf_keep_obj(obj);
498 if (pdf_resources_use_blending(doc, page->resources))
499 page->transparency = 1;
500 else if (!strcmp(pdf_to_name(pdf_dict_getp(pageobj, "Group/S")), "Transparency"))
501 page->transparency = 1;
503 for (annot = page->annots; annot && !page->transparency; annot = annot->next)
504 if (annot->ap && pdf_resources_use_blending(doc, annot->ap->resources))
505 page->transparency = 1;
509 if (fz_caught(ctx) != FZ_ERROR_TRYLATER)
511 pdf_free_page(doc, page);
512 fz_rethrow_message(ctx, "cannot load page %d contents (%d 0 R)", number + 1, pdf_to_num(pageref));
514 page->incomplete |= PDF_PAGE_INCOMPLETE_CONTENTS;
521 pdf_bound_page(pdf_document *doc, pdf_page *page, fz_rect *bounds)
524 fz_rect mediabox = page->mediabox;
525 fz_transform_rect(&mediabox, fz_rotate(&mtx, page->rotate));
526 bounds->x0 = bounds->y0 = 0;
527 bounds->x1 = mediabox.x1 - mediabox.x0;
528 bounds->y1 = mediabox.y1 - mediabox.y0;
533 pdf_load_links(pdf_document *doc, pdf_page *page)
535 return fz_keep_link(doc->ctx, page->links);
539 pdf_free_page(pdf_document *doc, pdf_page *page)
543 pdf_drop_obj(page->resources);
544 pdf_drop_obj(page->contents);
546 fz_drop_link(doc->ctx, page->links);
548 pdf_free_annot(doc->ctx, page->annots);
549 if (page->deleted_annots)
550 pdf_free_annot(doc->ctx, page->deleted_annots);
551 if (page->tmp_annots)
552 pdf_free_annot(doc->ctx, page->tmp_annots);
553 /* doc->focus, when not NULL, refers to one of
554 * the annotations and must be NULLed when the
555 * annotations are destroyed. doc->focus_obj
556 * keeps track of the actual annotation object. */
558 pdf_drop_obj(page->me);
559 fz_free(doc->ctx, page);
563 pdf_delete_page(pdf_document *doc, int at)
565 pdf_obj *parent, *kids;
568 pdf_lookup_page_loc(doc, at, &parent, &i);
569 kids = pdf_dict_gets(parent, "Kids");
570 pdf_array_delete(kids, i);
574 int count = pdf_to_int(pdf_dict_gets(parent, "Count"));
575 pdf_dict_puts_drop(parent, "Count", pdf_new_int(doc, count - 1));
576 parent = pdf_dict_gets(parent, "Parent");
579 doc->page_count = 0; /* invalidate cached value */
583 pdf_insert_page(pdf_document *doc, pdf_page *page, int at)
585 fz_context *ctx = doc->ctx;
586 int count = pdf_count_pages(doc);
587 pdf_obj *parent, *kids;
591 page_ref = pdf_new_ref(doc, page->me);
597 pdf_obj *root = pdf_dict_gets(pdf_trailer(doc), "Root");
598 parent = pdf_dict_gets(root, "Pages");
600 fz_throw(doc->ctx, FZ_ERROR_GENERIC, "cannot find page tree");
602 kids = pdf_dict_gets(parent, "Kids");
604 fz_throw(doc->ctx, FZ_ERROR_GENERIC, "malformed page tree");
606 pdf_array_insert(kids, page_ref, 0);
608 else if (at >= count)
614 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot insert page beyond end of page tree");
616 /* append after last page */
617 pdf_lookup_page_loc(doc, count - 1, &parent, &i);
618 kids = pdf_dict_gets(parent, "Kids");
619 pdf_array_insert(kids, page_ref, i + 1);
623 /* insert before found page */
624 pdf_lookup_page_loc(doc, at, &parent, &i);
625 kids = pdf_dict_gets(parent, "Kids");
626 pdf_array_insert(kids, page_ref, i);
629 pdf_dict_puts(page->me, "Parent", parent);
631 /* Adjust page counts */
634 int count = pdf_to_int(pdf_dict_gets(parent, "Count"));
635 pdf_dict_puts_drop(parent, "Count", pdf_new_int(doc, count + 1));
636 parent = pdf_dict_gets(parent, "Parent");
642 pdf_drop_obj(page_ref);
649 doc->page_count = 0; /* invalidate cached value */
653 pdf_delete_page_range(pdf_document *doc, int start, int end)
656 pdf_delete_page(doc, start++);
660 pdf_create_page(pdf_document *doc, fz_rect mediabox, int res, int rotate)
662 pdf_page *page = NULL;
665 fz_context *ctx = doc->ctx;
669 page = fz_malloc_struct(ctx, pdf_page);
673 page->resources = NULL;
674 page->contents = NULL;
675 page->transparency = 0;
678 page->me = pageobj = pdf_new_dict(doc, 4);
680 pdf_dict_puts_drop(pageobj, "Type", pdf_new_name(doc, "Page"));
682 page->mediabox.x0 = fz_min(mediabox.x0, mediabox.x1) * userunit;
683 page->mediabox.y0 = fz_min(mediabox.y0, mediabox.y1) * userunit;
684 page->mediabox.x1 = fz_max(mediabox.x0, mediabox.x1) * userunit;
685 page->mediabox.y1 = fz_max(mediabox.y0, mediabox.y1) * userunit;
686 pdf_dict_puts_drop(pageobj, "MediaBox", pdf_new_rect(doc, &page->mediabox));
688 /* Snap page->rotate to 0, 90, 180 or 270 */
689 if (page->rotate < 0)
690 page->rotate = 360 - ((-page->rotate) % 360);
691 if (page->rotate >= 360)
692 page->rotate = page->rotate % 360;
693 page->rotate = 90*((page->rotate + 45)/90);
694 if (page->rotate > 360)
696 pdf_dict_puts_drop(pageobj, "Rotate", pdf_new_int(doc, page->rotate));
698 fz_pre_rotate(fz_scale(&ctm, 1, -1), -page->rotate);
699 realbox = page->mediabox;
700 fz_transform_rect(&realbox, &ctm);
701 fz_pre_scale(fz_translate(&tmp, -realbox.x0, -realbox.y0), userunit, userunit);
702 fz_concat(&ctm, &ctm, &tmp);
704 /* Do not create a Contents, as an empty Contents dict is not
705 * valid. See Bug 694712 */
709 pdf_drop_obj(page->me);
711 fz_rethrow_message(ctx, "Failed to create page");