diff options
author | Tor Andersson <tor.andersson@artifex.com> | 2013-06-19 15:29:44 +0200 |
---|---|---|
committer | Tor Andersson <tor.andersson@artifex.com> | 2013-06-20 16:45:35 +0200 |
commit | 0a927854a10e1e6b9770a81e2e1d9f3093631757 (patch) | |
tree | 3d65d820d9fdba2d0d394d99c36290c851b78ca0 /source/pdf/pdf-page.c | |
parent | 1ae8f19179c5f0f8c6352b3c7855465325d5449a (diff) | |
download | mupdf-0a927854a10e1e6b9770a81e2e1d9f3093631757.tar.xz |
Rearrange source files.
Diffstat (limited to 'source/pdf/pdf-page.c')
-rw-r--r-- | source/pdf/pdf-page.c | 489 |
1 files changed, 489 insertions, 0 deletions
diff --git a/source/pdf/pdf-page.c b/source/pdf/pdf-page.c new file mode 100644 index 00000000..8a12b67b --- /dev/null +++ b/source/pdf/pdf-page.c @@ -0,0 +1,489 @@ +#include "mupdf/pdf.h" + +struct info +{ + pdf_obj *resources; + pdf_obj *mediabox; + pdf_obj *cropbox; + pdf_obj *rotate; +}; + +static void +put_marker_bool(fz_context *ctx, pdf_obj *rdb, char *marker, int val) +{ + pdf_obj *tmp; + + tmp = pdf_new_bool(ctx, val); + fz_try(ctx) + { + pdf_dict_puts(rdb, marker, tmp); + } + fz_always(ctx) + { + pdf_drop_obj(tmp); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } +} + +typedef struct pdf_page_load_s pdf_page_load; + +struct pdf_page_load_s +{ + int max; + int pos; + pdf_obj *node; + pdf_obj *kids; + struct info info; +}; + +static void +pdf_load_page_tree_node(pdf_document *xref, pdf_obj *node, struct info info) +{ + pdf_obj *dict, *kids, *count; + pdf_obj *obj; + fz_context *ctx = xref->ctx; + pdf_page_load *stack = NULL; + int stacklen = -1; + int stackmax = 0; + + fz_try(ctx) + { + do + { + if (!node || pdf_obj_mark(node)) + { + /* NULL node, or we've been here before. + * Nothing to do. */ + } + else + { + kids = pdf_dict_gets(node, "Kids"); + count = pdf_dict_gets(node, "Count"); + if (pdf_is_array(kids) && pdf_is_int(count)) + { + /* Push this onto the stack */ + obj = pdf_dict_gets(node, "Resources"); + if (obj) + info.resources = obj; + obj = pdf_dict_gets(node, "MediaBox"); + if (obj) + info.mediabox = obj; + obj = pdf_dict_gets(node, "CropBox"); + if (obj) + info.cropbox = obj; + obj = pdf_dict_gets(node, "Rotate"); + if (obj) + info.rotate = obj; + stacklen++; + if (stacklen == stackmax) + { + stack = fz_resize_array(ctx, stack, stackmax ? stackmax*2 : 10, sizeof(*stack)); + stackmax = stackmax ? stackmax*2 : 10; + } + stack[stacklen].kids = kids; + stack[stacklen].node = node; + stack[stacklen].pos = -1; + stack[stacklen].max = pdf_array_len(kids); + stack[stacklen].info = info; + } + else if ((dict = pdf_to_dict(node)) != NULL) + { + if (info.resources && !pdf_dict_gets(dict, "Resources")) + pdf_dict_puts(dict, "Resources", info.resources); + if (info.mediabox && !pdf_dict_gets(dict, "MediaBox")) + pdf_dict_puts(dict, "MediaBox", info.mediabox); + if (info.cropbox && !pdf_dict_gets(dict, "CropBox")) + pdf_dict_puts(dict, "CropBox", info.cropbox); + if (info.rotate && !pdf_dict_gets(dict, "Rotate")) + pdf_dict_puts(dict, "Rotate", info.rotate); + + if (xref->page_len == xref->page_cap) + { + fz_warn(ctx, "found more pages than expected"); + xref->page_refs = fz_resize_array(ctx, xref->page_refs, xref->page_cap+1, sizeof(pdf_obj*)); + xref->page_objs = fz_resize_array(ctx, xref->page_objs, xref->page_cap+1, sizeof(pdf_obj*)); + xref->page_cap ++; + } + + xref->page_refs[xref->page_len] = pdf_keep_obj(node); + xref->page_objs[xref->page_len] = pdf_keep_obj(dict); + xref->page_len ++; + pdf_obj_unmark(node); + } + } + /* Get the next node */ + if (stacklen < 0) + break; + while (++stack[stacklen].pos == stack[stacklen].max) + { + pdf_obj_unmark(stack[stacklen].node); + stacklen--; + if (stacklen < 0) /* No more to pop! */ + break; + node = stack[stacklen].node; + info = stack[stacklen].info; + pdf_obj_unmark(node); /* Unmark it, cos we're about to mark it again */ + } + if (stacklen >= 0) + node = pdf_array_get(stack[stacklen].kids, stack[stacklen].pos); + } + while (stacklen >= 0); + } + fz_always(ctx) + { + while (stacklen >= 0) + pdf_obj_unmark(stack[stacklen--].node); + fz_free(ctx, stack); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } +} + +static void +pdf_load_page_tree(pdf_document *xref) +{ + fz_context *ctx = xref->ctx; + pdf_obj *catalog; + pdf_obj *pages; + pdf_obj *count; + struct info info; + + if (xref->page_refs) + return; + + catalog = pdf_dict_gets(pdf_trailer(xref), "Root"); + pages = pdf_dict_gets(catalog, "Pages"); + count = pdf_dict_gets(pages, "Count"); + + if (!pdf_is_dict(pages)) + fz_throw(ctx, FZ_ERROR_GENERIC, "missing page tree"); + if (!pdf_is_int(count) || pdf_to_int(count) < 0) + fz_throw(ctx, FZ_ERROR_GENERIC, "missing page count"); + + xref->page_cap = pdf_to_int(count); + xref->page_len = 0; + xref->page_refs = fz_malloc_array(ctx, xref->page_cap, sizeof(pdf_obj*)); + xref->page_objs = fz_malloc_array(ctx, xref->page_cap, sizeof(pdf_obj*)); + + info.resources = NULL; + info.mediabox = NULL; + info.cropbox = NULL; + info.rotate = NULL; + + pdf_load_page_tree_node(xref, pages, info); +} + +int +pdf_count_pages(pdf_document *xref) +{ + pdf_load_page_tree(xref); + return xref->page_len; +} + +int +pdf_lookup_page_number(pdf_document *xref, pdf_obj *page) +{ + int i, num = pdf_to_num(page); + + pdf_load_page_tree(xref); + for (i = 0; i < xref->page_len; i++) + if (num == pdf_to_num(xref->page_refs[i])) + return i; + return -1; +} + +/* We need to know whether to install a page-level transparency group */ + +static int pdf_resources_use_blending(fz_context *ctx, pdf_obj *rdb); + +static int +pdf_extgstate_uses_blending(fz_context *ctx, pdf_obj *dict) +{ + pdf_obj *obj = pdf_dict_gets(dict, "BM"); + if (pdf_is_name(obj) && strcmp(pdf_to_name(obj), "Normal")) + return 1; + return 0; +} + +static int +pdf_pattern_uses_blending(fz_context *ctx, pdf_obj *dict) +{ + pdf_obj *obj; + obj = pdf_dict_gets(dict, "Resources"); + if (pdf_resources_use_blending(ctx, obj)) + return 1; + obj = pdf_dict_gets(dict, "ExtGState"); + return pdf_extgstate_uses_blending(ctx, obj); +} + +static int +pdf_xobject_uses_blending(fz_context *ctx, pdf_obj *dict) +{ + pdf_obj *obj = pdf_dict_gets(dict, "Resources"); + return pdf_resources_use_blending(ctx, obj); +} + +static int +pdf_resources_use_blending(fz_context *ctx, pdf_obj *rdb) +{ + pdf_obj *obj; + int i, n, useBM = 0; + + if (!rdb) + return 0; + + /* Have we been here before and stashed an answer? */ + obj = pdf_dict_gets(rdb, ".useBM"); + if (obj) + return pdf_to_bool(obj); + + /* stop on cyclic resource dependencies */ + if (pdf_obj_mark(rdb)) + return 0; + + fz_try(ctx) + { + obj = pdf_dict_gets(rdb, "ExtGState"); + n = pdf_dict_len(obj); + for (i = 0; i < n; i++) + if (pdf_extgstate_uses_blending(ctx, pdf_dict_get_val(obj, i))) + goto found; + + obj = pdf_dict_gets(rdb, "Pattern"); + n = pdf_dict_len(obj); + for (i = 0; i < n; i++) + if (pdf_pattern_uses_blending(ctx, pdf_dict_get_val(obj, i))) + goto found; + + obj = pdf_dict_gets(rdb, "XObject"); + n = pdf_dict_len(obj); + for (i = 0; i < n; i++) + if (pdf_xobject_uses_blending(ctx, pdf_dict_get_val(obj, i))) + goto found; + if (0) + { +found: + useBM = 1; + } + } + fz_always(ctx) + { + pdf_obj_unmark(rdb); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } + + put_marker_bool(ctx, rdb, ".useBM", useBM); + return useBM; +} + +static void +pdf_load_transition(pdf_document *xref, pdf_page *page, pdf_obj *transdict) +{ + char *name; + pdf_obj *obj; + int type; + + obj = pdf_dict_gets(transdict, "D"); + page->transition.duration = (obj ? pdf_to_real(obj) : 1); + + page->transition.vertical = (pdf_to_name(pdf_dict_gets(transdict, "Dm"))[0] != 'H'); + page->transition.outwards = (pdf_to_name(pdf_dict_gets(transdict, "M"))[0] != 'I'); + /* FIXME: If 'Di' is None, it should be handled differently, but + * this only affects Fly, and we don't implement that currently. */ + page->transition.direction = (pdf_to_int(pdf_dict_gets(transdict, "Di"))); + /* FIXME: Read SS for Fly when we implement it */ + /* FIXME: Read B for Fly when we implement it */ + + name = pdf_to_name(pdf_dict_gets(transdict, "S")); + if (!strcmp(name, "Split")) + type = FZ_TRANSITION_SPLIT; + else if (!strcmp(name, "Blinds")) + type = FZ_TRANSITION_BLINDS; + else if (!strcmp(name, "Box")) + type = FZ_TRANSITION_BOX; + else if (!strcmp(name, "Wipe")) + type = FZ_TRANSITION_WIPE; + else if (!strcmp(name, "Dissolve")) + type = FZ_TRANSITION_DISSOLVE; + else if (!strcmp(name, "Glitter")) + type = FZ_TRANSITION_GLITTER; + else if (!strcmp(name, "Fly")) + type = FZ_TRANSITION_FLY; + else if (!strcmp(name, "Push")) + type = FZ_TRANSITION_PUSH; + else if (!strcmp(name, "Cover")) + type = FZ_TRANSITION_COVER; + else if (!strcmp(name, "Uncover")) + type = FZ_TRANSITION_UNCOVER; + else if (!strcmp(name, "Fade")) + type = FZ_TRANSITION_FADE; + else + type = FZ_TRANSITION_NONE; + page->transition.type = type; +} + +pdf_page * +pdf_load_page(pdf_document *xref, int number) +{ + fz_context *ctx = xref->ctx; + pdf_page *page; + pdf_annot *annot; + pdf_obj *pageobj, *pageref, *obj; + fz_rect mediabox, cropbox, realbox; + float userunit; + fz_matrix mat; + + pdf_load_page_tree(xref); + if (number < 0 || number >= xref->page_len) + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find page %d", number + 1); + + pageobj = xref->page_objs[number]; + pageref = xref->page_refs[number]; + + page = fz_malloc_struct(ctx, pdf_page); + page->resources = NULL; + page->contents = NULL; + page->transparency = 0; + page->links = NULL; + page->annots = NULL; + page->deleted_annots = NULL; + page->tmp_annots = NULL; + page->me = pdf_keep_obj(pageobj); + + obj = pdf_dict_gets(pageobj, "UserUnit"); + if (pdf_is_real(obj)) + userunit = pdf_to_real(obj); + else + userunit = 1; + + pdf_to_rect(ctx, pdf_dict_gets(pageobj, "MediaBox"), &mediabox); + if (fz_is_empty_rect(&mediabox)) + { + fz_warn(ctx, "cannot find page size for page %d", number + 1); + mediabox.x0 = 0; + mediabox.y0 = 0; + mediabox.x1 = 612; + mediabox.y1 = 792; + } + + pdf_to_rect(ctx, pdf_dict_gets(pageobj, "CropBox"), &cropbox); + if (!fz_is_empty_rect(&cropbox)) + fz_intersect_rect(&mediabox, &cropbox); + + page->mediabox.x0 = fz_min(mediabox.x0, mediabox.x1) * userunit; + page->mediabox.y0 = fz_min(mediabox.y0, mediabox.y1) * userunit; + page->mediabox.x1 = fz_max(mediabox.x0, mediabox.x1) * userunit; + page->mediabox.y1 = fz_max(mediabox.y0, mediabox.y1) * userunit; + + if (page->mediabox.x1 - page->mediabox.x0 < 1 || page->mediabox.y1 - page->mediabox.y0 < 1) + { + fz_warn(ctx, "invalid page size in page %d", number + 1); + page->mediabox = fz_unit_rect; + } + + page->rotate = pdf_to_int(pdf_dict_gets(pageobj, "Rotate")); + /* Snap page->rotate to 0, 90, 180 or 270 */ + if (page->rotate < 0) + page->rotate = 360 - ((-page->rotate) % 360); + if (page->rotate >= 360) + page->rotate = page->rotate % 360; + page->rotate = 90*((page->rotate + 45)/90); + if (page->rotate > 360) + page->rotate = 0; + + fz_pre_rotate(fz_scale(&page->ctm, 1, -1), -page->rotate); + realbox = page->mediabox; + fz_transform_rect(&realbox, &page->ctm); + fz_pre_scale(fz_translate(&mat, -realbox.x0, -realbox.y0), userunit, userunit); + fz_concat(&page->ctm, &page->ctm, &mat); + + obj = pdf_dict_gets(pageobj, "Annots"); + if (obj) + { + page->links = pdf_load_link_annots(xref, obj, &page->ctm); + page->annots = pdf_load_annots(xref, obj, page); + } + + page->duration = pdf_to_real(pdf_dict_gets(pageobj, "Dur")); + + obj = pdf_dict_gets(pageobj, "Trans"); + page->transition_present = (obj != NULL); + if (obj) + { + pdf_load_transition(xref, page, obj); + } + + page->resources = pdf_dict_gets(pageobj, "Resources"); + if (page->resources) + pdf_keep_obj(page->resources); + + obj = pdf_dict_gets(pageobj, "Contents"); + fz_try(ctx) + { + page->contents = pdf_keep_obj(obj); + + if (pdf_resources_use_blending(ctx, page->resources)) + page->transparency = 1; + + for (annot = page->annots; annot && !page->transparency; annot = annot->next) + if (annot->ap && pdf_resources_use_blending(ctx, annot->ap->resources)) + page->transparency = 1; + } + fz_catch(ctx) + { + pdf_free_page(xref, page); + fz_rethrow_message(ctx, "cannot load page %d contents (%d 0 R)", number + 1, pdf_to_num(pageref)); + } + + return page; +} + +fz_rect * +pdf_bound_page(pdf_document *xref, pdf_page *page, fz_rect *bounds) +{ + fz_matrix mtx; + fz_rect mediabox = page->mediabox; + fz_transform_rect(&mediabox, fz_rotate(&mtx, page->rotate)); + bounds->x0 = bounds->y0 = 0; + bounds->x1 = mediabox.x1 - mediabox.x0; + bounds->y1 = mediabox.y1 - mediabox.y0; + return bounds; +} + +fz_link * +pdf_load_links(pdf_document *xref, pdf_page *page) +{ + return fz_keep_link(xref->ctx, page->links); +} + +void +pdf_free_page(pdf_document *xref, pdf_page *page) +{ + if (page == NULL) + return; + pdf_drop_obj(page->resources); + pdf_drop_obj(page->contents); + if (page->links) + fz_drop_link(xref->ctx, page->links); + if (page->annots) + pdf_free_annot(xref->ctx, page->annots); + if (page->deleted_annots) + pdf_free_annot(xref->ctx, page->deleted_annots); + if (page->tmp_annots) + pdf_free_annot(xref->ctx, page->tmp_annots); + /* xref->focus, when not NULL, refers to one of + * the annotations and must be NULLed when the + * annotations are destroyed. xref->focus_obj + * keeps track of the actual annotation object. */ + xref->focus = NULL; + pdf_drop_obj(page->me); + fz_free(xref->ctx, page); +} |