diff options
author | Robin Watts <robin.watts@artifex.com> | 2011-11-14 18:22:13 +0000 |
---|---|---|
committer | Robin Watts <robin.watts@artifex.com> | 2011-11-15 15:20:54 +0000 |
commit | 9c0a49060475b2dea1e4c2668bebd1d566113a7b (patch) | |
tree | 49e45a691cf105f4266d5c6b7242a4a3256c1200 /xps | |
parent | 60c0544742931da63db623ad7a79ba3758704cc1 (diff) | |
parent | fd6def85f22b598d4c278e76138ab7dccbb84c36 (diff) | |
download | mupdf-9c0a49060475b2dea1e4c2668bebd1d566113a7b.tar.xz |
Merge branch 'master' into context
Mostly redoing the xps_context to xps_document change and adding
contexts to newly written code.
Conflicts:
apps/pdfapp.c
apps/pdfapp.h
apps/x11_main.c
apps/xpsdraw.c
draw/draw_device.c
draw/draw_scale.c
fitz/base_object.c
fitz/fitz.h
pdf/mupdf.h
pdf/pdf_interpret.c
pdf/pdf_outline.c
pdf/pdf_page.c
xps/muxps.h
xps/xps_doc.c
xps/xps_xml.c
Diffstat (limited to 'xps')
-rw-r--r-- | xps/muxps.h | 19 | ||||
-rw-r--r-- | xps/xps_doc.c | 97 | ||||
-rw-r--r-- | xps/xps_outline.c | 124 | ||||
-rw-r--r-- | xps/xps_xml.c | 32 |
4 files changed, 240 insertions, 32 deletions
diff --git a/xps/muxps.h b/xps/muxps.h index 48ad2ae0..18ca528e 100644 --- a/xps/muxps.h +++ b/xps/muxps.h @@ -15,6 +15,8 @@ typedef struct xps_document_s xps_document; #define REL_START_PART \ "http://schemas.microsoft.com/xps/2005/06/fixedrepresentation" +#define REL_DOC_STRUCTURE \ + "http://schemas.microsoft.com/xps/2005/06/documentstructure" #define REL_REQUIRED_RESOURCE \ "http://schemas.microsoft.com/xps/2005/06/required-resource" #define REL_REQUIRED_RESOURCE_RECURSIVE \ @@ -70,22 +72,32 @@ void xps_free_part(xps_document *doc, xps_part *part); typedef struct xps_fixdoc_s xps_fixdoc; typedef struct xps_page_s xps_page; +typedef struct xps_target_s xps_target; struct xps_fixdoc_s { char *name; + char *outline; xps_fixdoc *next; }; struct xps_page_s { char *name; + int number; int width; int height; xml_element *root; xps_page *next; }; +struct xps_target_s +{ + char *name; + int page; + xps_target *next; +}; + int xps_read_page_list(xps_document *doc); void xps_debug_page_list(xps_document *doc); void xps_free_page_list(xps_document *doc); @@ -94,6 +106,10 @@ int xps_count_pages(xps_document *doc); xps_page *xps_load_page(xps_document *doc, int number); void xps_free_page(xps_document *doc, xps_page *page); +fz_outline *xps_load_outline(xps_document *doc); + +int xps_find_link_target(xps_document *doc, char *target_uri); + /* * Images, fonts, and colorspaces. */ @@ -207,6 +223,9 @@ struct xps_document_s xps_fixdoc *last_fixdoc; /* last fixed document */ xps_page *first_page; /* first page of document */ xps_page *last_page; /* last page of document */ + int page_count; + + xps_target *target; /* link targets */ char *base_uri; /* base uri for parsing XML and resolving relative paths */ char *part_uri; /* part uri for parsing metadata relations */ diff --git a/xps/xps_doc.c b/xps/xps_doc.c index dca1868c..6b6c0c08 100644 --- a/xps/xps_doc.c +++ b/xps/xps_doc.c @@ -1,6 +1,20 @@ #include "fitz.h" #include "muxps.h" +static void +xps_rels_for_part(char *buf, char *name, int buflen) +{ + char *p, *basename; + p = strrchr(name, '/'); + basename = p ? p + 1 : name; + fz_strlcpy(buf, name, buflen); + p = strrchr(buf, '/'); + if (p) *p = 0; + fz_strlcat(buf, "/_rels/", buflen); + fz_strlcat(buf, basename, buflen); + fz_strlcat(buf, ".rels", buflen); +} + /* * The FixedDocumentSequence and FixedDocument parts determine * which parts correspond to actual pages, and the page order. @@ -23,7 +37,7 @@ xps_debug_page_list(xps_document *doc) while (page) { - printf("page %s w=%d h=%d\n", page->name, page->width, page->height); + printf("page[%d] %s w=%d h=%d\n", page->number, page->name, page->width, page->height); page = page->next; } } @@ -40,6 +54,7 @@ xps_add_fixed_document(xps_document *doc, char *name) fixdoc = fz_malloc(doc->ctx, sizeof(xps_fixdoc)); fixdoc->name = fz_strdup(doc->ctx, name); + fixdoc->outline = NULL; fixdoc->next = NULL; if (!doc->first_fixdoc) @@ -66,6 +81,7 @@ xps_add_fixed_page(xps_document *doc, char *name, int width, int height) page = fz_malloc(doc->ctx, sizeof(xps_page)); page->name = fz_strdup(doc->ctx, name); + page->number = doc->page_count++; page->width = width; page->height = height; page->root = NULL; @@ -84,6 +100,42 @@ xps_add_fixed_page(xps_document *doc, char *name, int width, int height) } static void +xps_add_link_target(xps_document *doc, char *name) +{ + xps_page *page = doc->last_page; + xps_target *target = fz_malloc(doc->ctx, sizeof *target); + target->name = fz_strdup(doc->ctx, name); + target->page = page->number; + target->next = doc->target; + doc->target = target; +} + +int +xps_find_link_target(xps_document *doc, char *target_uri) +{ + xps_target *target; + char *needle = strrchr(target_uri, '#'); + needle = needle ? needle + 1 : target_uri; + for (target = doc->target; target; target = target->next) + if (!strcmp(target->name, needle)) + return target->page; + return 0; +} + +static void +xps_free_link_targets(xps_document *doc) +{ + xps_target *target = doc->target, *next; + while (target) + { + next = target->next; + fz_free(doc->ctx, target->name); + fz_free(doc->ctx, target); + target = next; + } +} + +static void xps_free_fixed_pages(xps_document *doc) { xps_page *page = doc->first_page; @@ -119,6 +171,7 @@ xps_free_page_list(xps_document *doc) { xps_free_fixed_documents(doc); xps_free_fixed_pages(doc); + xps_free_link_targets(doc); } /* @@ -126,12 +179,10 @@ xps_free_page_list(xps_document *doc) */ static void -xps_parse_metadata_imp(xps_document *doc, xml_element *item) +xps_parse_metadata_imp(xps_document *doc, xml_element *item, xps_fixdoc *fixdoc) { while (item) { - xps_parse_metadata_imp(doc, xml_down(item)); - if (!strcmp(xml_tag(item), "Relationship")) { char *target = xml_att(item, "Target"); @@ -142,6 +193,8 @@ xps_parse_metadata_imp(xps_document *doc, xml_element *item) xps_absolute_path(tgtbuf, doc->base_uri, target, sizeof tgtbuf); if (!strcmp(type, REL_START_PART)) doc->start_part = fz_strdup(doc->ctx, tgtbuf); + if (!strcmp(type, REL_DOC_STRUCTURE) && fixdoc) + fixdoc->outline = fz_strdup(doc->ctx, tgtbuf); } } @@ -171,12 +224,21 @@ xps_parse_metadata_imp(xps_document *doc, xml_element *item) } } + if (!strcmp(xml_tag(item), "LinkTarget")) + { + char *name = xml_att(item, "Name"); + if (name) + xps_add_link_target(doc, name); + } + + xps_parse_metadata_imp(doc, xml_down(item), fixdoc); + item = xml_next(item); } } static int -xps_parse_metadata(xps_document *doc, xps_part *part) +xps_parse_metadata(xps_document *doc, xps_part *part, xps_fixdoc *fixdoc) { xml_element *root; char buf[1024]; @@ -203,7 +265,7 @@ xps_parse_metadata(xps_document *doc, xps_part *part) if (!root) return fz_error_note(-1, "cannot parse metadata part '%s'", part->name); - xps_parse_metadata_imp(doc, root); + xps_parse_metadata_imp(doc, root, fixdoc); xml_free_element(doc->ctx, root); @@ -214,7 +276,7 @@ xps_parse_metadata(xps_document *doc, xps_part *part) } static int -xps_read_and_process_metadata_part(xps_document *doc, char *name) +xps_read_and_process_metadata_part(xps_document *doc, char *name, xps_fixdoc *fixdoc) { xps_part *part; int code; @@ -223,7 +285,7 @@ xps_read_and_process_metadata_part(xps_document *doc, char *name) if (!part) return fz_error_note(-1, "cannot read zip part '%s'", name); - code = xps_parse_metadata(doc, part); + code = xps_parse_metadata(doc, part, fixdoc); if (code) return fz_error_note(code, "cannot process metadata part '%s'", name); @@ -238,20 +300,27 @@ xps_read_page_list(xps_document *doc) xps_fixdoc *fixdoc; int code; - code = xps_read_and_process_metadata_part(doc, "/_rels/.rels"); + code = xps_read_and_process_metadata_part(doc, "/_rels/.rels", NULL); if (code) return fz_error_note(code, "cannot process root relationship part"); if (!doc->start_part) return fz_error_make("cannot find fixed document sequence start part"); - code = xps_read_and_process_metadata_part(doc, doc->start_part); + code = xps_read_and_process_metadata_part(doc, doc->start_part, NULL); if (code) return fz_error_note(code, "cannot process FixedDocumentSequence part"); for (fixdoc = doc->first_fixdoc; fixdoc; fixdoc = fixdoc->next) { - code = xps_read_and_process_metadata_part(doc, fixdoc->name); + char relbuf[1024]; + xps_rels_for_part(relbuf, fixdoc->name, sizeof relbuf); + + code = xps_read_and_process_metadata_part(doc, relbuf, fixdoc); + if (code) + fz_error_handle(code, "cannot process FixedDocument rels part"); + + code = xps_read_and_process_metadata_part(doc, fixdoc->name, fixdoc); if (code) return fz_error_note(code, "cannot process FixedDocument part"); } @@ -262,11 +331,7 @@ xps_read_page_list(xps_document *doc) int xps_count_pages(xps_document *doc) { - xps_page *page; - int n = 0; - for (page = doc->first_page; page; page = page->next) - n ++; - return n; + return doc->page_count; } static int diff --git a/xps/xps_outline.c b/xps/xps_outline.c new file mode 100644 index 00000000..48eb100b --- /dev/null +++ b/xps/xps_outline.c @@ -0,0 +1,124 @@ +#include "fitz.h" +#include "muxps.h" + +/* + * Parse the document structure / outline parts referenced from fixdoc relationships. + */ + +static fz_outline * +xps_find_last_outline_at_level(fz_outline *node, int level, int target_level) +{ + while (node->next) + node = node->next; + if (level == target_level || !node->down) + return node; + return xps_find_last_outline_at_level(node->down, level + 1, target_level); +} + +static fz_outline * +xps_parse_document_outline(xps_document *doc, xml_element *root) +{ + xml_element *node; + fz_outline *head = NULL, *entry, *tail; + int last_level = 1, this_level; + for (node = xml_down(root); node; node = xml_next(node)) + { + if (!strcmp(xml_tag(node), "OutlineEntry")) + { + char *level = xml_att(node, "OutlineLevel"); + char *target = xml_att(node, "OutlineTarget"); + char *description = xml_att(node, "Description"); + if (!target || !description) + continue; + + entry = fz_malloc(doc->ctx, sizeof *entry); + entry->title = fz_strdup(doc->ctx, description); + entry->page = xps_find_link_target(doc, target); + entry->down = NULL; + entry->next = NULL; + + this_level = level ? atoi(level) : 1; + + if (!head) + { + head = entry; + } + else + { + tail = xps_find_last_outline_at_level(head, 1, this_level); + if (this_level > last_level) + tail->down = entry; + else + tail->next = entry; + } + + last_level = this_level; + } + } + return head; +} + +static fz_outline * +xps_parse_document_structure(xps_document *doc, xml_element *root) +{ + xml_element *node; + if (!strcmp(xml_tag(root), "DocumentStructure")) + { + node = xml_down(root); + if (!strcmp(xml_tag(node), "DocumentStructure.Outline")) + { + node = xml_down(node); + if (!strcmp(xml_tag(node), "DocumentOutline")) + return xps_parse_document_outline(doc, node); + } + } + return NULL; +} + +static fz_outline * +xps_load_document_structure(xps_document *doc, xps_fixdoc *fixdoc) +{ + xps_part *part; + xml_element *root; + fz_outline *outline; + + part = xps_read_part(doc, fixdoc->outline); + if (!part) + return NULL; + + root = xml_parse_document(doc->ctx, part->data, part->size); + if (!root) { + fz_error_handle(-1, "cannot parse document structure part '%s'", part->name); + xps_free_part(doc, part); + return NULL; + } + + outline = xps_parse_document_structure(doc, root); + + xml_free_element(doc->ctx, root); + xps_free_part(doc, part); + + return outline; + +} + +fz_outline * +xps_load_outline(xps_document *doc) +{ + xps_fixdoc *fixdoc; + fz_outline *head = NULL, *tail, *outline; + + for (fixdoc = doc->first_fixdoc; fixdoc; fixdoc = fixdoc->next) { + if (fixdoc->outline) { + outline = xps_load_document_structure(doc, fixdoc); + if (outline) { + if (!head) + head = outline; + else + tail->next = outline; + tail = outline; + } + } + } + return head; +} diff --git a/xps/xps_xml.c b/xps/xps_xml.c index 0c3f591b..d92804af 100644 --- a/xps/xps_xml.c +++ b/xps/xps_xml.c @@ -18,7 +18,7 @@ struct element struct parser { struct element *head; - fz_context *doc; + fz_context *ctx; }; static inline void indent(int n) @@ -71,26 +71,26 @@ char *xml_att(struct element *item, const char *name) return NULL; } -static void xml_free_attribute(fz_context *doc, struct attribute *att) +static void xml_free_attribute(fz_context *ctx, struct attribute *att) { while (att) { struct attribute *next = att->next; if (att->value) - fz_free(doc, att->value); - fz_free(doc, att); + fz_free(ctx, att->value); + fz_free(ctx, att); att = next; } } -void xml_free_element(fz_context *doc, struct element *item) +void xml_free_element(fz_context *ctx, struct element *item) { while (item) { struct element *next = item->next; if (item->atts) - xml_free_attribute(doc, item->atts); + xml_free_attribute(ctx, item->atts); if (item->down) - xml_free_element(doc, item->down); - fz_free(doc, item); + xml_free_element(ctx, item->down); + fz_free(ctx, item); item = next; } } @@ -134,7 +134,7 @@ static void xml_emit_open_tag(struct parser *parser, char *a, char *b) { struct element *head, *tail; - head = fz_malloc(parser->doc, sizeof(struct element)); + head = fz_malloc(parser->ctx, sizeof(struct element)); if (b - a > sizeof(head->name)) b = a + sizeof(head->name); memcpy(head->name, a, b - a); @@ -163,7 +163,7 @@ static void xml_emit_att_name(struct parser *parser, char *a, char *b) struct element *head = parser->head; struct attribute *att; - att = fz_malloc(parser->doc, sizeof(struct attribute)); + att = fz_malloc(parser->ctx, sizeof(struct attribute)); if (b - a > sizeof(att->name)) b = a + sizeof(att->name); memcpy(att->name, a, b - a); @@ -181,7 +181,7 @@ static void xml_emit_att_value(struct parser *parser, char *a, char *b) int c; /* entities are all longer than UTFmax so runetochar is safe */ - s = att->value = fz_malloc(parser->doc, b - a + 1); + s = att->value = fz_malloc(parser->ctx, b - a + 1); while (a < b) { if (*a == '&') { a += xml_parse_entity(&c, a); @@ -362,7 +362,7 @@ static char *convert_to_utf8(fz_context *doc, unsigned char *s, int n) } struct element * -xml_parse_document(fz_context *doc, unsigned char *s, int n) +xml_parse_document(fz_context *ctx, unsigned char *s, int n) { struct parser parser; struct element root; @@ -372,18 +372,18 @@ xml_parse_document(fz_context *doc, unsigned char *s, int n) memset(&root, 0, sizeof(root)); parser.head = &root; - parser.doc = doc; + parser.ctx = ctx; - p = convert_to_utf8(doc, s, n); + p = convert_to_utf8(ctx, s, n); error = xml_parse_document_imp(&parser, p); if (error) { - fz_error_handle(-1, error); + fz_throw(ctx, "%s", error); return NULL; } if (p != (char*)s) - fz_free(doc, p); + fz_free(ctx, p); return root.down; } |