summaryrefslogtreecommitdiff
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
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.
-rw-r--r--apps/pdfapp.c1
-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
8 files changed, 127 insertions, 46 deletions
diff --git a/apps/pdfapp.c b/apps/pdfapp.c
index aa529220..20e67c62 100644
--- a/apps/pdfapp.c
+++ b/apps/pdfapp.c
@@ -375,6 +375,7 @@ static void pdfapp_loadpage_xps(pdfapp_t *app)
fz_free_device(mdev);
app->page_links = page->links;
+ page->links = NULL;
xps_free_page(app->xps, page);
}
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;
+}