summaryrefslogtreecommitdiff
path: root/xps
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2012-02-02 16:18:13 +0000
committerRobin Watts <robin.watts@artifex.com>2012-02-02 17:21:20 +0000
commitbb1e3c973526e66024e1a3260255368034cb2008 (patch)
tree9102d5d22818788e88c6407a0e4a358dfa7011b5 /xps
parent4ab5924753d32b5eaeb80138c6626057f61b516f (diff)
downloadmupdf-bb1e3c973526e66024e1a3260255368034cb2008.tar.xz
Support remote links in XPS documents.
Update xps path handling to cope with URLs. Fix premature freeing of links. Spot remote URLs and use appropriate link type.
Diffstat (limited to 'xps')
-rw-r--r--xps/muxps.h3
-rw-r--r--xps/xps_doc.c65
-rw-r--r--xps/xps_glyphs.c2
-rw-r--r--xps/xps_image.c2
-rw-r--r--xps/xps_resource.c2
-rw-r--r--xps/xps_tile.c1
-rw-r--r--xps/xps_util.c97
7 files changed, 126 insertions, 46 deletions
diff --git a/xps/muxps.h b/xps/muxps.h
index 2f87edc6..2e5c4206 100644
--- a/xps/muxps.h
+++ b/xps/muxps.h
@@ -32,7 +32,8 @@ typedef struct xps_document_s xps_document;
*/
int xps_strcasecmp(char *a, char *b);
-void xps_absolute_path(char *output, char *base_uri, char *path, int output_size);
+void xps_resolve_url(char *output, char *base_uri, char *path, int output_size);
+int xps_url_is_remote(char *path);
/*
* XML document model
diff --git a/xps/xps_doc.c b/xps/xps_doc.c
index d1943fc0..0bc7e443 100644
--- a/xps/xps_doc.c
+++ b/xps/xps_doc.c
@@ -90,32 +90,42 @@ xps_add_link(xps_document *doc, fz_rect area, char *base_uri, char *target_uri)
len = 2 + (base_uri ? strlen(base_uri) : 0) +
(target_uri ? strlen(target_uri) : 0);
buffer = fz_malloc(doc->ctx, len);
- xps_absolute_path(buffer, base_uri, target_uri, len);
- uri = buffer;
-
- /* FIXME: This won't work for remote docs */
- /* Skip until we find the fragment marker */
- while (*uri && *uri != '#')
- uri++;
- if (*uri == '#')
- uri++;
-
- for (target = doc->target; target; target = target->next)
- if (!strcmp(target->name, uri))
- break;
+ xps_resolve_url(buffer, base_uri, target_uri, len);
+ if (xps_url_is_remote(buffer))
+ {
+ dest.kind = FZ_LINK_URI;
+ dest.ld.uri.is_map = 0;
+ dest.ld.uri.uri = buffer;
+ buffer = NULL;
+ }
+ else
+ {
+ uri = buffer;
- if (target == NULL)
- break;
+ /* FIXME: This won't work for remote docs */
+ /* Skip until we find the fragment marker */
+ while (*uri && *uri != '#')
+ uri++;
+ if (*uri == '#')
+ uri++;
- dest.kind = FZ_LINK_GOTO;
- dest.ld.gotor.flags = fz_link_flag_l_valid | fz_link_flag_t_valid | fz_link_flag_r_valid | fz_link_flag_b_valid;
- dest.ld.gotor.lt.x = area.x0;
- dest.ld.gotor.lt.y = area.y0;
- dest.ld.gotor.rb.x = area.x1;
- dest.ld.gotor.rb.y = area.y1;
- dest.ld.gotor.page = target->page;
- dest.ld.gotor.file_spec = NULL;
- dest.ld.gotor.new_window = 0;
+ for (target = doc->target; target; target = target->next)
+ if (!strcmp(target->name, uri))
+ break;
+
+ if (target == NULL)
+ break;
+
+ dest.kind = FZ_LINK_GOTO;
+ dest.ld.gotor.flags = 0;
+ dest.ld.gotor.lt.x = 0;
+ dest.ld.gotor.lt.y = 0;
+ dest.ld.gotor.rb.x = 0;
+ dest.ld.gotor.rb.y = 0;
+ dest.ld.gotor.page = target->page;
+ dest.ld.gotor.file_spec = NULL;
+ dest.ld.gotor.new_window = 0;
+ }
link = fz_new_link(doc->ctx, area, dest);
link->next = doc->current_page->links;
@@ -256,7 +266,7 @@ xps_parse_metadata_imp(xps_document *doc, xml_element *item, xps_fixdoc *fixdoc)
if (target && type)
{
char tgtbuf[1024];
- xps_absolute_path(tgtbuf, doc->base_uri, target, sizeof tgtbuf);
+ xps_resolve_url(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)
@@ -270,7 +280,7 @@ xps_parse_metadata_imp(xps_document *doc, xml_element *item, xps_fixdoc *fixdoc)
if (source)
{
char srcbuf[1024];
- xps_absolute_path(srcbuf, doc->base_uri, source, sizeof srcbuf);
+ xps_resolve_url(srcbuf, doc->base_uri, source, sizeof srcbuf);
xps_add_fixed_document(doc, srcbuf);
}
}
@@ -285,7 +295,7 @@ xps_parse_metadata_imp(xps_document *doc, xml_element *item, xps_fixdoc *fixdoc)
if (source)
{
char srcbuf[1024];
- xps_absolute_path(srcbuf, doc->base_uri, source, sizeof srcbuf);
+ xps_resolve_url(srcbuf, doc->base_uri, source, sizeof srcbuf);
xps_add_fixed_page(doc, srcbuf, width, height);
}
}
@@ -421,7 +431,6 @@ xps_load_page(xps_document *doc, int number)
doc->current_page = page;
if (!page->root)
xps_load_fixed_page(doc, page);
- page->links_resolved = 1;
return page;
}
n ++;
diff --git a/xps/xps_glyphs.c b/xps/xps_glyphs.c
index 0cc4af5d..c394dd18 100644
--- a/xps/xps_glyphs.c
+++ b/xps/xps_glyphs.c
@@ -473,7 +473,7 @@ xps_parse_glyphs(xps_document *doc, fz_matrix ctm,
* Find and load the font resource
*/
- xps_absolute_path(partname, base_uri, font_uri_att, sizeof partname);
+ xps_resolve_url(partname, base_uri, font_uri_att, sizeof partname);
subfont = strrchr(partname, '#');
if (subfont)
{
diff --git a/xps/xps_image.c b/xps/xps_image.c
index 2c159d14..10d3baee 100644
--- a/xps/xps_image.c
+++ b/xps/xps_image.c
@@ -83,7 +83,7 @@ xps_find_image_brush_source_part(xps_document *doc, char *base_uri, xml_element
if (!image_name)
fz_throw(doc->ctx, "cannot find image source");
- xps_absolute_path(partname, base_uri, image_name, sizeof partname);
+ xps_resolve_url(partname, base_uri, image_name, sizeof partname);
return xps_read_part(doc, partname);
}
diff --git a/xps/xps_resource.c b/xps/xps_resource.c
index cfac562d..dcf3717d 100644
--- a/xps/xps_resource.c
+++ b/xps/xps_resource.c
@@ -63,7 +63,7 @@ xps_parse_remote_resource_dictionary(xps_document *doc, char *base_uri, char *so
char *s;
/* External resource dictionaries MUST NOT reference other resource dictionaries */
- xps_absolute_path(part_name, base_uri, source_att, sizeof part_name);
+ xps_resolve_url(part_name, base_uri, source_att, sizeof part_name);
part = xps_read_part(doc, part_name);
xml = xml_parse_document(doc->ctx, part->data, part->size);
xps_free_part(doc, part);
diff --git a/xps/xps_tile.c b/xps/xps_tile.c
index fe4d2350..e87d76f7 100644
--- a/xps/xps_tile.c
+++ b/xps/xps_tile.c
@@ -375,4 +375,5 @@ xps_run_page(xps_document *doc, xps_page *page, fz_device *dev, fz_matrix ctm, f
xps_parse_fixed_page(doc, ctm, page);
doc->cookie = NULL;
doc->dev = NULL;
+ page->links_resolved = 1;
}
diff --git a/xps/xps_util.c b/xps/xps_util.c
index 6af4da91..5a3b1fbf 100644
--- a/xps/xps_util.c
+++ b/xps/xps_util.c
@@ -20,15 +20,77 @@ xps_strcasecmp(char *a, char *b)
return xps_tolower(*a) - xps_tolower(*b);
}
+/* A URL is defined as consisting of a:
+ * SCHEME (e.g. http:)
+ * AUTHORITY (username, password, hostname, port, eg //test:passwd@mupdf.com:999)
+ * PATH (e.g. /download)
+ * QUERY (e.g. ?view=page)
+ * FRAGMENT (e.g. #fred) (not strictly part of the URL)
+ */
+static char *
+skip_scheme(char *path)
+{
+ char *p = path;
+
+ /* Skip over: alpha *(alpha | digit | "+" | "-" | ".") looking for : */
+ if (*p >= 'a' && *p <= 'z')
+ {}
+ else if (*p >= 'A' && *p <= 'Z')
+ {}
+ else
+ return path;
+
+ while (*++p)
+ {
+ if (*p >= 'a' && *p <= 'z')
+ {}
+ else if (*p >= 'A' && *p <= 'Z')
+ {}
+ else if (*p >= '0' && *p <= '9')
+ {}
+ else if (*p == '+')
+ {}
+ else if (*p == '-')
+ {}
+ else if (*p == '.')
+ {}
+ else if (*p == ':')
+ return p+1;
+ else
+ break;
+ }
+ return path;
+}
+
+static char *
+skip_authority(char *path)
+{
+ char *p = path;
+
+ /* Authority section must start with '//' */
+ if (p[0] != '/' || p[1] != '/')
+ return path;
+ p += 2;
+
+ /* Authority is terminated by end of URL, '/' or '?' */
+ while (*p && *p != '/' && *p != '?')
+ p++;
+
+ return p;
+}
+
+
#define SEP(x) ((x)=='/' || (x) == 0)
static char *
xps_clean_path(char *name)
{
- char *p, *q, *dotdot;
+ char *p, *q, *dotdot, *start;
int rooted;
- rooted = name[0] == '/';
+ start = skip_scheme(name);
+ start = skip_authority(start);
+ rooted = start[0] == '/';
/*
* invariants:
@@ -37,7 +99,7 @@ xps_clean_path(char *name)
* dotdot points just past the point where .. cannot backtrack
* any further (no slash).
*/
- p = q = dotdot = name + rooted;
+ p = q = dotdot = start + rooted;
while (*p)
{
if(p[0] == '/') /* null element */
@@ -54,7 +116,7 @@ xps_clean_path(char *name)
}
else if (!rooted) /* /.. is / but ./../ is .. */
{
- if (q != name)
+ if (q != start)
*q++ = '/';
*q++ = '.';
*q++ = '.';
@@ -63,36 +125,43 @@ xps_clean_path(char *name)
}
else /* real path element */
{
- if (q != name+rooted)
+ if (q != start+rooted)
*q++ = '/';
while ((*q = *p) != '/' && *q != 0)
p++, q++;
}
}
- if (q == name) /* empty string is really "." */
+ if (q == start) /* empty string is really "." */
*q++ = '.';
*q = '\0';
return name;
}
-/* RJW: Possible problems here:
- * "/foo" + "../../bar" = "/bar" not "../bar"
- * "http://something/" + ... doesn't work.
- */
void
-xps_absolute_path(char *output, char *base_uri, char *path, int output_size)
+xps_resolve_url(char *output, char *base_uri, char *path, int output_size)
{
- if (path[0] == '/')
+ char *p = skip_authority(skip_scheme(path));
+
+ if (p != path || path[0] == '/')
{
fz_strlcpy(output, path, output_size);
}
else
{
- fz_strlcpy(output, base_uri, output_size);
- fz_strlcat(output, "/", output_size);
+ int len = fz_strlcpy(output, base_uri, output_size);
+ if (len == 0 || output[len-1] != '/')
+ fz_strlcat(output, "/", output_size);
fz_strlcat(output, path, output_size);
}
xps_clean_path(output);
}
+
+int
+xps_url_is_remote(char *path)
+{
+ char *p = skip_authority(skip_scheme(path));
+
+ return p != path;
+}