diff options
author | Robin Watts <robin.watts@artifex.com> | 2012-02-02 16:18:13 +0000 |
---|---|---|
committer | Robin Watts <robin.watts@artifex.com> | 2012-02-02 17:21:20 +0000 |
commit | bb1e3c973526e66024e1a3260255368034cb2008 (patch) | |
tree | 9102d5d22818788e88c6407a0e4a358dfa7011b5 | |
parent | 4ab5924753d32b5eaeb80138c6626057f61b516f (diff) | |
download | mupdf-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.c | 1 | ||||
-rw-r--r-- | xps/muxps.h | 3 | ||||
-rw-r--r-- | xps/xps_doc.c | 65 | ||||
-rw-r--r-- | xps/xps_glyphs.c | 2 | ||||
-rw-r--r-- | xps/xps_image.c | 2 | ||||
-rw-r--r-- | xps/xps_resource.c | 2 | ||||
-rw-r--r-- | xps/xps_tile.c | 1 | ||||
-rw-r--r-- | xps/xps_util.c | 97 |
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; +} |