diff options
Diffstat (limited to 'Build/source/texk/dvipdfmx/src/pdfdoc.c')
-rw-r--r-- | Build/source/texk/dvipdfmx/src/pdfdoc.c | 453 |
1 files changed, 434 insertions, 19 deletions
diff --git a/Build/source/texk/dvipdfmx/src/pdfdoc.c b/Build/source/texk/dvipdfmx/src/pdfdoc.c index 797f2dd820b..38f33fb5a43 100644 --- a/Build/source/texk/dvipdfmx/src/pdfdoc.c +++ b/Build/source/texk/dvipdfmx/src/pdfdoc.c @@ -1,4 +1,4 @@ -/* $Header: /home/cvsroot/dvipdfmx/src/pdfdoc.c,v 1.57 2008/06/05 06:27:42 chofchof Exp $ +/* $Header: /home/cvsroot/dvipdfmx/src/pdfdoc.c,v 1.65 2008/12/11 16:03:05 matthias Exp $ This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks. @@ -235,6 +235,9 @@ typedef struct pdf_doc struct name_dict *names; + int check_gotos; + struct ht_table gotos; + struct { int outline_open_depth; double annot_grow; @@ -367,9 +370,11 @@ doc_get_page_entry (pdf_doc *p, unsigned long page_no) static void pdf_doc_init_page_tree (pdf_doc *p, double media_width, double media_height); static void pdf_doc_close_page_tree (pdf_doc *p); -static void pdf_doc_init_names (pdf_doc *p); +static void pdf_doc_init_names (pdf_doc *p, int check_gotos); static void pdf_doc_close_names (pdf_doc *p); +static void pdf_doc_add_gotos (pdf_obj *annot_dict); + static void pdf_doc_init_docinfo (pdf_doc *p); static void pdf_doc_close_docinfo (pdf_doc *p); @@ -871,6 +876,264 @@ pdf_doc_close_page_tree (pdf_doc *p) return; } +/* + * From PDFReference15_v6.pdf (p.119 and p.834) + * + * MediaBox rectangle (Required; inheritable) + * + * The media box defines the boundaries of the physical medium on which the + * page is to be printed. It may include any extended area surrounding the + * finished page for bleed, printing marks, or other such purposes. It may + * also include areas close to the edges of the medium that cannot be marked + * because of physical limitations of the output device. Content falling + * outside this boundary can safely be discarded without affecting the + * meaning of the PDF file. + * + * CropBox rectangle (Optional; inheritable) + * + * The crop box defines the region to which the contents of the page are to be + * clipped (cropped) when displayed or printed. Unlike the other boxes, the + * crop box has no defined meaning in terms of physical page geometry or + * intended use; it merely imposes clipping on the page contents. However, + * in the absence of additional information (such as imposition instructions + * specified in a JDF or PJTF job ticket), the crop box will determine how + * the page's contents are to be positioned on the output medium. The default + * value is the page's media box. + * + * BleedBox rectangle (Optional; PDF 1.3) + * + * The bleed box (PDF 1.3) defines the region to which the contents of the + * page should be clipped when output in a production environment. This may + * include any extra "bleed area" needed to accommodate the physical + * limitations of cutting, folding, and trimming equipment. The actual printed + * page may include printing marks that fall outside the bleed box. + * The default value is the page's crop box. + * + * TrimBox rectangle (Optional; PDF 1.3) + * + * The trim box (PDF 1.3) defines the intended dimensions of the finished page + * after trimming. It may be smaller than the media box, to allow for + * production-related content such as printing instructions, cut marks, or + * color bars. The default value is the page's crop box. + * + * ArtBox rectangle (Optional; PDF 1.3) + * + * The art box (PDF 1.3) defines the extent of the page's meaningful content + * (including potential white space) as intended by the page's creator. + * The default value is the page's crop box. + * + * Rotate integer (Optional; inheritable) + * + * The number of degrees by which the page should be rotated clockwise when + * displayed or printed. The value must be a multiple of 90. Default value: 0. + */ + +pdf_obj * +pdf_doc_get_page (pdf_obj *trailer, long *page_p, long *count_p, + pdf_rect *bbox, pdf_obj **resources_p, + pdf_obj **markinfo_p) { + pdf_obj *page_tree = NULL; + pdf_obj *resources = NULL, *box = NULL, *rotate = NULL; + pdf_obj *catalog; + long page_no = *page_p, page_idx; + + if (pdf_lookup_dict(trailer, "Encrypt")) { + WARN("This PDF document is encrypted."); + pdf_release_obj(trailer); + goto error_silent; + } + + catalog = pdf_deref_obj(pdf_lookup_dict(trailer, "Root")); + pdf_release_obj(trailer); + if (!PDF_OBJ_DICTTYPE(catalog)) { + if (catalog) + pdf_release_obj(catalog); + goto error; + } + + if (markinfo_p) + *markinfo_p = pdf_deref_obj(pdf_lookup_dict(catalog, "MarkInfo")); + + page_tree = pdf_deref_obj(pdf_lookup_dict(catalog, "Pages")); + pdf_release_obj(catalog); + + if (!PDF_OBJ_DICTTYPE(page_tree)) + goto error; + + /* + * Negative page numbers are counted from the back. + */ + { + pdf_obj *tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "Count")); + if (!PDF_OBJ_NUMBERTYPE(tmp)) { + if (tmp) + pdf_release_obj(tmp); + goto error; + } + long count = pdf_number_value(tmp); + pdf_release_obj(tmp); + page_idx = page_no + (page_no >= 0 ? -1 : count); + if (page_idx < 0 || page_idx >= count) { + WARN("Page %ld does not exist.", page_no); + goto error_silent; + } + page_no = page_idx+1; + *page_p = page_no; + *count_p = count; + } + + /* + * Seek correct page. Get MediaBox, CropBox and Resources. + * (Note that these entries can be inherited.) + */ + { + pdf_obj *media_box = NULL, *crop_box = NULL, *kids, *tmp; + int depth = PDF_OBJ_MAX_DEPTH; + long kids_length = 1, i = 0; + + while (--depth && i != kids_length) { + if ((tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "MediaBox")))) { + if (media_box) + pdf_release_obj(media_box); + media_box = tmp; + } + + if ((tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "CropBox")))) { + if (crop_box) + pdf_release_obj(crop_box); + crop_box = tmp; + } + + if ((tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "Rotate")))) { + if (rotate) + pdf_release_obj(rotate); + rotate = tmp; + } + + if ((tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "Resources")))) { + if (resources) + pdf_release_obj(resources); + resources = tmp; + } + + kids = pdf_deref_obj(pdf_lookup_dict(page_tree, "Kids")); + if (!kids) + break; + else if (!PDF_OBJ_ARRAYTYPE(kids)) { + pdf_release_obj(kids); + goto error; + } + kids_length = pdf_array_length(kids); + + for (i = 0; i < kids_length; i++) { + long count; + + pdf_release_obj(page_tree); + page_tree = pdf_deref_obj(pdf_get_array(kids, i)); + if (!PDF_OBJ_DICTTYPE(page_tree)) + goto error; + + tmp = pdf_deref_obj(pdf_lookup_dict(page_tree, "Count")); + if (PDF_OBJ_NUMBERTYPE(tmp)) { + /* Pages object */ + count = pdf_number_value(tmp); + pdf_release_obj(tmp); + } else if (!tmp) + /* Page object */ + count = 1; + else { + pdf_release_obj(tmp); + goto error; + } + + if (page_idx < count) + break; + + page_idx -= count; + } + + pdf_release_obj(kids); + } + + if (!depth || kids_length == i) { + if (media_box) + pdf_release_obj(media_box); + if (crop_box) + pdf_release_obj(crop_box); + goto error; + } + + if (crop_box) + box = crop_box; + else + if (!(box = pdf_deref_obj(pdf_lookup_dict(page_tree, "ArtBox"))) && + !(box = pdf_deref_obj(pdf_lookup_dict(page_tree, "TrimBox"))) && + !(box = pdf_deref_obj(pdf_lookup_dict(page_tree, "BleedBox"))) && + media_box) { + box = media_box; + media_box = NULL; + } + if (media_box) + pdf_release_obj(media_box); + } + + if (!PDF_OBJ_ARRAYTYPE(box) || pdf_array_length(box) != 4 || + !PDF_OBJ_DICTTYPE(resources)) + goto error; + + if (PDF_OBJ_NUMBERTYPE(rotate)) { + if (pdf_number_value(rotate)) + WARN("<< /Rotate %d >> found. (Not supported yet)", + (int) pdf_number_value(rotate)); + pdf_release_obj(rotate); + rotate = NULL; + } else if (rotate) + goto error; + + { + int i; + + for (i = 4; --i; ) { + double x; + pdf_obj *tmp = pdf_deref_obj(pdf_get_array(box, i)); + if (!PDF_OBJ_NUMBERTYPE(tmp)) { + pdf_release_obj(tmp); + goto error; + } + x = pdf_number_value(tmp); + switch (i) { + case 0: bbox->llx = x; break; + case 1: bbox->lly = x; break; + case 2: bbox->urx = x; break; + case 3: bbox->ury = x; break; + } + pdf_release_obj(tmp); + } + } + + pdf_release_obj(box); + + if (resources_p) + *resources_p = resources; + else if (resources) + pdf_release_obj(resources); + + return page_tree; + + error: + WARN("Cannot parse document. Broken PDF file?"); + error_silent: + if (box) + pdf_release_obj(box); + if (rotate) + pdf_release_obj(rotate); + if (resources) + pdf_release_obj(resources); + if (page_tree) + pdf_release_obj(page_tree); + + return NULL; +} #ifndef BOOKMARKS_OPEN_DEFAULT #define BOOKMARKS_OPEN_DEFAULT 0 @@ -1118,6 +1381,8 @@ pdf_doc_bookmarks_add (pdf_obj *dict, int is_open) p->outlines.current = item; + pdf_doc_add_gotos(dict); + return; } @@ -1160,7 +1425,7 @@ static const char *name_dict_categories[] = { #define NUM_NAME_CATEGORY (sizeof(name_dict_categories)/sizeof(name_dict_categories[0])) static void -pdf_doc_init_names (pdf_doc *p) +pdf_doc_init_names (pdf_doc *p, int check_gotos) { int i; @@ -1169,11 +1434,19 @@ pdf_doc_init_names (pdf_doc *p) p->names = NEW(NUM_NAME_CATEGORY + 1, struct name_dict); for (i = 0; i < NUM_NAME_CATEGORY; i++) { p->names[i].category = (char *) name_dict_categories[i]; - p->names[i].data = NULL; + p->names[i].data = strcmp(name_dict_categories[i], "Dests") ? + NULL : pdf_new_name_tree(); + /* + * We need a non-null entry for PDF destinations in order to find + * broken links even if no destination is defined in the DVI file. + */ } p->names[NUM_NAME_CATEGORY].category = NULL; p->names[NUM_NAME_CATEGORY].data = NULL; + p->check_gotos = check_gotos; + ht_init_table(&p->gotos); + return; } @@ -1201,6 +1474,119 @@ pdf_doc_add_names (const char *category, } static void +pdf_doc_add_gotos (pdf_obj *annot_dict) +{ + pdf_obj *subtype, *A, *S, *D = NULL, *tmp; + char *dest; + + if (!pdoc.check_gotos) + return; + + /* + * We cannot use pdf_deref_obj because if it return NULL + * we don't know whether a dictionary does not exist + * or has been declared as @foo and not yet defined. (As + * long as @foo is undefined, it is stored as "null" object.) + */ + + /* + * An annotation dictionary coming from an annotation special + * must have a "Subtype". An annotation dictionary coming from + * an outline special has none. + */ + subtype = pdf_lookup_dict(annot_dict, "Subtype"); + if (PDF_OBJ_INDIRECTTYPE(subtype)) + goto indirect; + else if (subtype && !PDF_OBJ_NULLTYPE(subtype)) { + if (!PDF_OBJ_NAMETYPE(subtype)) + goto error; + else if (strcmp(pdf_name_value(subtype), "Link")) + return; + } + + A = pdf_lookup_dict(annot_dict, "A"); + if (PDF_OBJ_DICTTYPE(A)) { + S = pdf_lookup_dict(A, "S"); + if (PDF_OBJ_INDIRECTTYPE(S)) + goto indirect; + else if (!PDF_OBJ_NAMETYPE(S)) + goto error; + else if (strcmp(pdf_name_value(S), "GoTo")) + return; + + D = pdf_lookup_dict(A, "D"); + if (PDF_OBJ_NULLTYPE(D)) + D = NULL; + } else if (PDF_OBJ_INDIRECTTYPE(A)) + goto indirect; + else if (A && !PDF_OBJ_NULLTYPE(A)) + goto error; + + tmp = pdf_lookup_dict(annot_dict, "Dest"); + if (PDF_OBJ_NULLTYPE(tmp)) + tmp = NULL; + + if (tmp) { + if (D) + goto error; + else + D = tmp; + } + + if (PDF_OBJ_STRINGTYPE(D)) + dest = (char *) pdf_string_value(D); +#if 0 + /* Names as destinations are not supported by dvipdfmx */ + else if (PDF_OBJ_NAMETYPE(D)) + dest = pdf_name_value(D); +#endif + else if (PDF_OBJ_ARRAYTYPE(D)) + return; + else if (PDF_OBJ_INDIRECTTYPE(D)) + goto indirect; + else + goto error; + + ht_insert_table(&pdoc.gotos, dest, strlen(dest), NULL+1, NULL); + /* NULL+1 is just some non-null pointer */ + return; + + indirect: + pdoc.check_gotos = 0; + if (verbose) + MESG("\nFound unexpected annotation format." + " Will not remove PDF destinations\n"); + return; + + error: + WARN("Unknown PDF annotation format."); + return; +} + +static void +warn_undef_dests (struct ht_table *dests, struct ht_table *gotos) +{ + struct ht_iter iter; + + if (ht_set_iter(gotos, &iter) < 0) + return; + + do { + int keylen; + char *key = ht_iter_getkey(&iter, &keylen); + if (!ht_lookup_table(dests, key, keylen)) { + char *dest = NEW(keylen+1, char); + memcpy(dest, key, keylen); + dest[keylen] = 0; + WARN("PDF destination \"%s\" not defined.", dest); + RELEASE(dest); + } + } while (ht_iter_next(&iter) >= 0); + + ht_clear_iter(&iter); +} + +static void pdf_doc_close_names (pdf_doc *p) { pdf_obj *tmp; @@ -1208,15 +1594,30 @@ pdf_doc_close_names (pdf_doc *p) for (i = 0; p->names[i].category != NULL; i++) { if (p->names[i].data) { + struct ht_table *data = p->names[i].data; pdf_obj *name_tree; + long count; + + if (!pdoc.check_gotos || strcmp(p->names[i].category, "Dests")) + name_tree = pdf_names_create_tree(data, &count, NULL); + else { + name_tree = pdf_names_create_tree(data, &count, &pdoc.gotos); + + if (verbose && count < data->count) + MESG("\nRemoved %ld unused PDF destinations\n", data->count-count); - name_tree = pdf_names_create_tree(p->names[i].data); - if (!p->root.names) { - p->root.names = pdf_new_dict(); + if (count < pdoc.gotos.count) + warn_undef_dests(data, &pdoc.gotos); + } + + if (name_tree) { + if (!p->root.names) + p->root.names = pdf_new_dict(); + pdf_add_dict(p->root.names, + pdf_new_name(p->names[i].category), + pdf_ref_obj(name_tree)); + pdf_release_obj(name_tree); } - pdf_add_dict(p->root.names, - pdf_new_name(p->names[i].category), pdf_ref_obj(name_tree)); - pdf_release_obj(name_tree); pdf_delete_name_tree(&p->names[i].data); } } @@ -1243,6 +1644,8 @@ pdf_doc_close_names (pdf_doc *p) RELEASE(p->names); p->names = NULL; + ht_clear_table(&p->gotos, NULL); + return; } @@ -1291,6 +1694,8 @@ pdf_doc_add_annot (unsigned page_no, const pdf_rect *rect, pdf_obj *annot_dict) pdf_add_array(page->annots, pdf_ref_obj(annot_dict)); + pdf_doc_add_gotos(annot_dict); + return; } @@ -1338,11 +1743,13 @@ pdf_doc_begin_article (const char *article_id, pdf_obj *article_info) return; } +#if 0 void pdf_doc_end_article (const char *article_id) { return; /* no-op */ } +#endif static pdf_bead * find_bead (pdf_article *article, const char *bead_id) @@ -1872,14 +2279,15 @@ pdf_doc_finish_page (pdf_doc *p) } -static pdf_color bgcolor = { 1, { 1.0 } }; +static pdf_color bgcolor; + void pdf_doc_set_bgcolor (const pdf_color *color) { if (color) - memcpy(&bgcolor, color, sizeof(pdf_color)); + pdf_color_copycolor(&bgcolor, color); else { /* as clear... */ - pdf_color_graycolor(&bgcolor, 1.0); + pdf_color_white(&bgcolor); } } @@ -1908,10 +2316,8 @@ doc_fill_page_background (pdf_doc *p) currentpage->contents = currentpage->background; pdf_dev_gsave(); - //pdf_color_push(); pdf_dev_set_nonstrokingcolor(&bgcolor); pdf_dev_rectfill(r.llx, r.lly, r.urx - r.llx, r.ury - r.lly); - //pdf_color_pop(); pdf_dev_grestore(); currentpage->contents = saved_content; @@ -1972,7 +2378,8 @@ void pdf_open_document (const char *filename, int do_encryption, double media_width, double media_height, - double annot_grow_amount, int bookmark_open_depth) + double annot_grow_amount, int bookmark_open_depth, + int check_gotos) { pdf_doc *p = &pdoc; @@ -1999,9 +2406,11 @@ pdf_open_document (const char *filename, pdf_doc_init_bookmarks(p, bookmark_open_depth); pdf_doc_init_articles (p); - pdf_doc_init_names (p); + pdf_doc_init_names (p, check_gotos); pdf_doc_init_page_tree(p, media_width, media_height); + pdf_doc_set_bgcolor(NULL); + if (do_encryption) { pdf_obj *encrypt = pdf_encrypt_obj(); pdf_set_encrypt(encrypt, pdf_enc_id_array()); @@ -2135,6 +2544,8 @@ pdf_doc_begin_grabbing (const char *ident, struct form_list_node *fnode; xform_info info; + pdf_dev_push_gstate(); + fnode = NEW(1, struct form_list_node); fnode->prev = p->pending_forms; @@ -2185,7 +2596,7 @@ pdf_doc_begin_grabbing (const char *ident, * current font and color to the object stream. */ pdf_dev_reset_fonts(); - pdf_dev_reset_color(); + pdf_dev_reset_color(1); /* force color operators to be added to stream */ return xobj_id; } @@ -2227,9 +2638,11 @@ pdf_doc_end_grabbing (pdf_obj *attrib) if (attrib) pdf_release_obj(attrib); p->pending_forms = fnode->prev; + + pdf_dev_pop_gstate(); - /* Here we do not need pdf_dev_reset_color(). */ pdf_dev_reset_fonts(); + pdf_dev_reset_color(0); RELEASE(fnode); @@ -2293,6 +2706,7 @@ pdf_doc_expand_box (const pdf_rect *rect) breaking_state.dirty = 1; } +#if 0 /* This should be number tree */ void pdf_doc_set_pagelabel (long pg_start, @@ -2325,3 +2739,4 @@ pdf_doc_set_pagelabel (long pg_start, return; } +#endif |