summaryrefslogtreecommitdiff
path: root/xps
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2011-11-14 18:22:13 +0000
committerRobin Watts <robin.watts@artifex.com>2011-11-15 15:20:54 +0000
commit9c0a49060475b2dea1e4c2668bebd1d566113a7b (patch)
tree49e45a691cf105f4266d5c6b7242a4a3256c1200 /xps
parent60c0544742931da63db623ad7a79ba3758704cc1 (diff)
parentfd6def85f22b598d4c278e76138ab7dccbb84c36 (diff)
downloadmupdf-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.h19
-rw-r--r--xps/xps_doc.c97
-rw-r--r--xps/xps_outline.c124
-rw-r--r--xps/xps_xml.c32
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;
}