summaryrefslogtreecommitdiff
path: root/source/xps/xps-outline.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/xps/xps-outline.c')
-rw-r--r--source/xps/xps-outline.c152
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;
+}