From 0030a82dc379f9f54c7976feaefd5bf1cb1f2d01 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Tue, 22 Dec 2015 12:59:23 +0100 Subject: [PATCH] [api-minor] Add support for URLs in the document outline Re: issue 5089. (Note that since there are other outline features that we currently don't support, e.g. bold/italic text and custom colours, I thus think we can keep the referenced issue open.) --- src/core/obj.js | 16 +++++++++++++--- src/display/annotation_layer.js | 16 ++-------------- src/display/api.js | 1 + src/shared/util.js | 21 +++++++++++++++++++++ test/pdfs/.gitignore | 1 + test/pdfs/issue3214.pdf | Bin 0 -> 11139 bytes test/unit/api_spec.js | 28 +++++++++++++++++++++++++--- web/pdf_outline_view.js | 4 ++++ 8 files changed, 67 insertions(+), 20 deletions(-) create mode 100644 test/pdfs/issue3214.pdf diff --git a/src/core/obj.js b/src/core/obj.js index c46597999..98d6d3d6d 100644 --- a/src/core/obj.js +++ b/src/core/obj.js @@ -47,6 +47,7 @@ var shadow = sharedUtil.shadow; var stringToPDFString = sharedUtil.stringToPDFString; var stringToUTF8String = sharedUtil.stringToUTF8String; var warn = sharedUtil.warn; +var isValidUrl = sharedUtil.isValidUrl; var Ref = corePrimitives.Ref; var RefSet = corePrimitives.RefSet; var RefSetCache = corePrimitives.RefSetCache; @@ -146,9 +147,17 @@ var Catalog = (function CatalogClosure() { if (!outlineDict.has('Title')) { error('Invalid outline item'); } - var dest = outlineDict.get('A'); - if (dest) { - dest = dest.get('D'); + var actionDict = outlineDict.get('A'), dest = null, url = null; + if (actionDict) { + var destEntry = actionDict.get('D'); + if (destEntry) { + dest = destEntry; + } else { + var uriEntry = actionDict.get('URI'); + if (isString(uriEntry) && isValidUrl(uriEntry, false)) { + url = uriEntry; + } + } } else if (outlineDict.has('Dest')) { dest = outlineDict.getRaw('Dest'); if (isName(dest)) { @@ -158,6 +167,7 @@ var Catalog = (function CatalogClosure() { var title = outlineDict.get('Title'); var outlineItem = { dest: dest, + url: url, title: stringToPDFString(title), color: outlineDict.get('C') || [0, 0, 0], count: outlineDict.get('Count'), diff --git a/src/display/annotation_layer.js b/src/display/annotation_layer.js index cd4fffd5c..2107012c1 100644 --- a/src/display/annotation_layer.js +++ b/src/display/annotation_layer.js @@ -31,9 +31,7 @@ var AnnotationBorderStyleType = sharedUtil.AnnotationBorderStyleType; var AnnotationType = sharedUtil.AnnotationType; var Util = sharedUtil.Util; -var isExternalLinkTargetSet = sharedUtil.isExternalLinkTargetSet; -var LinkTargetStringMap = sharedUtil.LinkTargetStringMap; -var removeNullCharacters = sharedUtil.removeNullCharacters; +var addLinkAttributes = sharedUtil.addLinkAttributes; var warn = sharedUtil.warn; var CustomStyle = displayDOMUtils.CustomStyle; @@ -233,17 +231,7 @@ var LinkAnnotationElement = (function LinkAnnotationElementClosure() { this.container.className = 'linkAnnotation'; var link = document.createElement('a'); - link.href = link.title = (this.data.url ? - removeNullCharacters(this.data.url) : ''); - - if (this.data.url && isExternalLinkTargetSet()) { - link.target = LinkTargetStringMap[PDFJS.externalLinkTarget]; - } - - // Strip referrer from the URL. - if (this.data.url) { - link.rel = PDFJS.externalLinkRel; - } + addLinkAttributes(link, { url: this.data.url }); if (!this.data.url) { if (this.data.action) { diff --git a/src/display/api.js b/src/display/api.js index 864a80ebe..9a1ad451a 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -714,6 +714,7 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { * italic: boolean, * color: rgb array, * dest: dest obj, + * url: string, * items: array of more items like this * }, * ... diff --git a/src/shared/util.js b/src/shared/util.js index 286968881..23b359227 100644 --- a/src/shared/util.js +++ b/src/shared/util.js @@ -318,6 +318,26 @@ function isValidUrl(url, allowRelative) { } PDFJS.isValidUrl = isValidUrl; +/** + * Adds various attributes (href, title, target, rel) to hyperlinks. + * @param {HTMLLinkElement} link - The link element. + * @param {Object} params - An object with the properties: + * @param {string} params.url - An absolute URL. + */ +function addLinkAttributes(link, params) { + var url = params && params.url; + link.href = link.title = (url ? removeNullCharacters(url) : ''); + + if (url) { + if (isExternalLinkTargetSet()) { + link.target = LinkTargetStringMap[PDFJS.externalLinkTarget]; + } + // Strip referrer from the URL. + link.rel = PDFJS.externalLinkRel; + } +} +PDFJS.addLinkAttributes = addLinkAttributes; + function shadow(obj, prop, value) { Object.defineProperty(obj, prop, { value: value, enumerable: true, @@ -2292,6 +2312,7 @@ exports.isInt = isInt; exports.isNum = isNum; exports.isString = isString; exports.isValidUrl = isValidUrl; +exports.addLinkAttributes = addLinkAttributes; exports.loadJpegStream = loadJpegStream; exports.log2 = log2; exports.readInt8 = readInt8; diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index 23b656a16..528ed8ddd 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -10,6 +10,7 @@ !issue1155r.pdf !issue2391-1.pdf !issue2391-2.pdf +!issue3214.pdf !issue4665.pdf !issue5801.pdf !issue5946.pdf diff --git a/test/pdfs/issue3214.pdf b/test/pdfs/issue3214.pdf new file mode 100644 index 0000000000000000000000000000000000000000..17a4f1445907d04f77960ec9990ac490b04e7206 GIT binary patch literal 11139 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