diff options
Diffstat (limited to 'source/xps/xps-outline.c')
-rw-r--r-- | source/xps/xps-outline.c | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/source/xps/xps-outline.c b/source/xps/xps-outline.c new file mode 100644 index 00000000..b87460a4 --- /dev/null +++ b/source/xps/xps-outline.c @@ -0,0 +1,152 @@ +#include "mupdf/xps.h" + +/* + * Parse the document structure / outline parts referenced from fixdoc relationships. + */ + +static fz_outline * +xps_lookup_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_lookup_last_outline_at_level(node->down, level + 1, target_level); +} + +static fz_outline * +xps_parse_document_outline(xps_document *doc, fz_xml *root) +{ + fz_xml *node; + fz_outline *head = NULL, *entry, *tail; + int last_level = 1, this_level; + for (node = fz_xml_down(root); node; node = fz_xml_next(node)) + { + if (!strcmp(fz_xml_tag(node), "OutlineEntry")) + { + char *level = fz_xml_att(node, "OutlineLevel"); + char *target = fz_xml_att(node, "OutlineTarget"); + char *description = fz_xml_att(node, "Description"); + if (!target || !description) + continue; + + entry = fz_malloc_struct(doc->ctx, fz_outline); + entry->title = fz_strdup(doc->ctx, description); + entry->dest.kind = FZ_LINK_GOTO; + entry->dest.ld.gotor.flags = 0; + entry->dest.ld.gotor.page = xps_lookup_link_target(doc, target); + entry->down = NULL; + entry->next = NULL; + + this_level = level ? atoi(level) : 1; + + if (!head) + { + head = entry; + } + else + { + tail = xps_lookup_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, fz_xml *root) +{ + fz_xml *node; + if (!strcmp(fz_xml_tag(root), "DocumentStructure")) + { + node = fz_xml_down(root); + if (node && !strcmp(fz_xml_tag(node), "DocumentStructure.Outline")) + { + node = fz_xml_down(node); + if (node && !strcmp(fz_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; + fz_xml *root; + fz_outline *outline; + + part = xps_read_part(doc, fixdoc->outline); + fz_try(doc->ctx) + { + root = fz_parse_xml(doc->ctx, part->data, part->size); + } + fz_always(doc->ctx) + { + xps_free_part(doc, part); + } + fz_catch(doc->ctx) + { + fz_rethrow(doc->ctx); + } + if (!root) + return NULL; + + fz_try(doc->ctx) + { + outline = xps_parse_document_structure(doc, root); + } + fz_always(doc->ctx) + { + fz_free_xml(doc->ctx, root); + } + fz_catch(doc->ctx) + { + fz_rethrow(doc->ctx); + } + + 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) + { + fz_try(doc->ctx) + { + outline = xps_load_document_structure(doc, fixdoc); + } + fz_catch(doc->ctx) + { + /* FIXME: TryLater ? */ + outline = NULL; + } + if (!outline) + continue; + + if (!head) + head = outline; + else + { + while (tail->next) + tail = tail->next; + tail->next = outline; + } + tail = outline; + } + } + return head; +} |