1#include "mupdf/fitz.h"
2#include "xps-imp.h"
3
4#include <stdlib.h>
5#include <math.h>
6
7/*
8 * Parse the document structure / outline parts referenced from fixdoc relationships.
9 */
10
11static fz_outline *
12xps_lookup_last_outline_at_level(fz_context *ctx, xps_document *doc, fz_outline *node, int level, int target_level)
13{
14 while (node->next)
15 node = node->next;
16 if (level == target_level || !node->down)
17 return node;
18 return xps_lookup_last_outline_at_level(ctx, doc, node->down, level + 1, target_level);
19}
20
21static fz_outline *
22xps_parse_document_outline(fz_context *ctx, xps_document *doc, fz_xml *root)
23{
24 fz_xml *node;
25 fz_outline *head = NULL, *entry, *tail;
26 int last_level = 1, this_level;
27 for (node = fz_xml_down(root); node; node = fz_xml_next(node))
28 {
29 if (fz_xml_is_tag(node, "OutlineEntry"))
30 {
31 char *level = fz_xml_att(node, "OutlineLevel");
32 char *target = fz_xml_att(node, "OutlineTarget");
33 char *description = fz_xml_att(node, "Description");
34 if (!target || !description)
35 continue;
36
37 entry = fz_new_outline(ctx);
38 entry->title = fz_strdup(ctx, description);
39 entry->uri = fz_strdup(ctx, target);
40 entry->page = xps_lookup_link_target(ctx, (fz_document*)doc, target, NULL, NULL);
41 entry->down = NULL;
42 entry->next = NULL;
43
44 this_level = level ? atoi(level) : 1;
45
46 if (!head)
47 {
48 head = entry;
49 }
50 else
51 {
52 tail = xps_lookup_last_outline_at_level(ctx, doc, head, 1, this_level);
53 if (this_level > last_level)
54 tail->down = entry;
55 else
56 tail->next = entry;
57 }
58
59 last_level = this_level;
60 }
61 }
62 return head;
63}
64
65static fz_outline *
66xps_parse_document_structure(fz_context *ctx, xps_document *doc, fz_xml *root)
67{
68 fz_xml *node;
69 if (fz_xml_is_tag(root, "DocumentStructure"))
70 {
71 node = fz_xml_down(root);
72 if (node && fz_xml_is_tag(node, "DocumentStructure.Outline"))
73 {
74 node = fz_xml_down(node);
75 if (node && fz_xml_is_tag(node, "DocumentOutline"))
76 return xps_parse_document_outline(ctx, doc, node);
77 }
78 }
79 return NULL;
80}
81
82static fz_outline *
83xps_load_document_structure(fz_context *ctx, xps_document *doc, xps_fixdoc *fixdoc)
84{
85 xps_part *part;
86 fz_xml_doc *xml = NULL;
87 fz_outline *outline = NULL;
88
89 fz_var(xml);
90
91 part = xps_read_part(ctx, doc, fixdoc->outline);
92 fz_try(ctx)
93 {
94 xml = fz_parse_xml(ctx, part->data, 0);
95 outline = xps_parse_document_structure(ctx, doc, fz_xml_root(xml));
96 }
97 fz_always(ctx)
98 {
99 fz_drop_xml(ctx, xml);
100 xps_drop_part(ctx, doc, part);
101 }
102 fz_catch(ctx)
103 {
104 fz_rethrow(ctx);
105 }
106
107 return outline;
108}
109
110fz_outline *
111xps_load_outline(fz_context *ctx, fz_document *doc_)
112{
113 xps_document *doc = (xps_document*)doc_;
114 xps_fixdoc *fixdoc;
115 fz_outline *head = NULL, *tail, *outline = NULL;
116
117 for (fixdoc = doc->first_fixdoc; fixdoc; fixdoc = fixdoc->next)
118 {
119 if (fixdoc->outline)
120 {
121 fz_try(ctx)
122 {
123 outline = xps_load_document_structure(ctx, doc, fixdoc);
124 }
125 fz_catch(ctx)
126 {
127 fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
128 outline = NULL;
129 }
130 if (!outline)
131 continue;
132
133 if (!head)
134 head = outline;
135 else
136 {
137 while (tail->next)
138 tail = tail->next;
139 tail->next = outline;
140 }
141 tail = outline;
142 }
143 }
144 return head;
145}
146