Skip to content

Commit 0030a82

Browse files
committed
[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.)
1 parent e60fde7 commit 0030a82

File tree

8 files changed

+67
-20
lines changed

8 files changed

+67
-20
lines changed

src/core/obj.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ var shadow = sharedUtil.shadow;
4747
var stringToPDFString = sharedUtil.stringToPDFString;
4848
var stringToUTF8String = sharedUtil.stringToUTF8String;
4949
var warn = sharedUtil.warn;
50+
var isValidUrl = sharedUtil.isValidUrl;
5051
var Ref = corePrimitives.Ref;
5152
var RefSet = corePrimitives.RefSet;
5253
var RefSetCache = corePrimitives.RefSetCache;
@@ -146,9 +147,17 @@ var Catalog = (function CatalogClosure() {
146147
if (!outlineDict.has('Title')) {
147148
error('Invalid outline item');
148149
}
149-
var dest = outlineDict.get('A');
150-
if (dest) {
151-
dest = dest.get('D');
150+
var actionDict = outlineDict.get('A'), dest = null, url = null;
151+
if (actionDict) {
152+
var destEntry = actionDict.get('D');
153+
if (destEntry) {
154+
dest = destEntry;
155+
} else {
156+
var uriEntry = actionDict.get('URI');
157+
if (isString(uriEntry) && isValidUrl(uriEntry, false)) {
158+
url = uriEntry;
159+
}
160+
}
152161
} else if (outlineDict.has('Dest')) {
153162
dest = outlineDict.getRaw('Dest');
154163
if (isName(dest)) {
@@ -158,6 +167,7 @@ var Catalog = (function CatalogClosure() {
158167
var title = outlineDict.get('Title');
159168
var outlineItem = {
160169
dest: dest,
170+
url: url,
161171
title: stringToPDFString(title),
162172
color: outlineDict.get('C') || [0, 0, 0],
163173
count: outlineDict.get('Count'),

src/display/annotation_layer.js

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,7 @@
3131
var AnnotationBorderStyleType = sharedUtil.AnnotationBorderStyleType;
3232
var AnnotationType = sharedUtil.AnnotationType;
3333
var Util = sharedUtil.Util;
34-
var isExternalLinkTargetSet = sharedUtil.isExternalLinkTargetSet;
35-
var LinkTargetStringMap = sharedUtil.LinkTargetStringMap;
36-
var removeNullCharacters = sharedUtil.removeNullCharacters;
34+
var addLinkAttributes = sharedUtil.addLinkAttributes;
3735
var warn = sharedUtil.warn;
3836
var CustomStyle = displayDOMUtils.CustomStyle;
3937

@@ -233,17 +231,7 @@ var LinkAnnotationElement = (function LinkAnnotationElementClosure() {
233231
this.container.className = 'linkAnnotation';
234232

235233
var link = document.createElement('a');
236-
link.href = link.title = (this.data.url ?
237-
removeNullCharacters(this.data.url) : '');
238-
239-
if (this.data.url && isExternalLinkTargetSet()) {
240-
link.target = LinkTargetStringMap[PDFJS.externalLinkTarget];
241-
}
242-
243-
// Strip referrer from the URL.
244-
if (this.data.url) {
245-
link.rel = PDFJS.externalLinkRel;
246-
}
234+
addLinkAttributes(link, { url: this.data.url });
247235

248236
if (!this.data.url) {
249237
if (this.data.action) {

src/display/api.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,7 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() {
714714
* italic: boolean,
715715
* color: rgb array,
716716
* dest: dest obj,
717+
* url: string,
717718
* items: array of more items like this
718719
* },
719720
* ...

src/shared/util.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,26 @@ function isValidUrl(url, allowRelative) {
318318
}
319319
PDFJS.isValidUrl = isValidUrl;
320320

321+
/**
322+
* Adds various attributes (href, title, target, rel) to hyperlinks.
323+
* @param {HTMLLinkElement} link - The link element.
324+
* @param {Object} params - An object with the properties:
325+
* @param {string} params.url - An absolute URL.
326+
*/
327+
function addLinkAttributes(link, params) {
328+
var url = params && params.url;
329+
link.href = link.title = (url ? removeNullCharacters(url) : '');
330+
331+
if (url) {
332+
if (isExternalLinkTargetSet()) {
333+
link.target = LinkTargetStringMap[PDFJS.externalLinkTarget];
334+
}
335+
// Strip referrer from the URL.
336+
link.rel = PDFJS.externalLinkRel;
337+
}
338+
}
339+
PDFJS.addLinkAttributes = addLinkAttributes;
340+
321341
function shadow(obj, prop, value) {
322342
Object.defineProperty(obj, prop, { value: value,
323343
enumerable: true,
@@ -2292,6 +2312,7 @@ exports.isInt = isInt;
22922312
exports.isNum = isNum;
22932313
exports.isString = isString;
22942314
exports.isValidUrl = isValidUrl;
2315+
exports.addLinkAttributes = addLinkAttributes;
22952316
exports.loadJpegStream = loadJpegStream;
22962317
exports.log2 = log2;
22972318
exports.readInt8 = readInt8;

test/pdfs/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
!issue1155r.pdf
1111
!issue2391-1.pdf
1212
!issue2391-2.pdf
13+
!issue3214.pdf
1314
!issue4665.pdf
1415
!issue5801.pdf
1516
!issue5946.pdf

test/pdfs/issue3214.pdf

10.9 KB
Binary file not shown.

test/unit/api_spec.js

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -371,11 +371,33 @@ describe('api', function() {
371371
var promise = doc.getOutline();
372372
waitsForPromiseResolved(promise, function(outline) {
373373
// Two top level entries.
374+
expect(outline instanceof Array).toEqual(true);
374375
expect(outline.length).toEqual(2);
375376
// Make sure some basic attributes are set.
376-
expect(outline[1].title).toEqual('Chapter 1');
377-
expect(outline[1].items.length).toEqual(1);
378-
expect(outline[1].items[0].title).toEqual('Paragraph 1.1');
377+
var outlineItem = outline[1];
378+
expect(outlineItem.title).toEqual('Chapter 1');
379+
expect(outlineItem.dest instanceof Array).toEqual(true);
380+
expect(outlineItem.url).toEqual(null);
381+
382+
expect(outlineItem.items.length).toEqual(1);
383+
expect(outlineItem.items[0].title).toEqual('Paragraph 1.1');
384+
});
385+
});
386+
it('gets outline containing a url', function() {
387+
var pdfUrl = combineUrl(window.location.href, '../pdfs/issue3214.pdf');
388+
var loadingTask = PDFJS.getDocument(pdfUrl);
389+
390+
loadingTask.promise.then(function (pdfDocument) {
391+
pdfDocument.getOutline().then(function (outline) {
392+
expect(outline instanceof Array).toEqual(true);
393+
expect(outline.length).toEqual(5);
394+
395+
var outlineItem = outline[2];
396+
expect(outlineItem.dest).toEqual(null);
397+
expect(outlineItem.url).toEqual('http://google.com');
398+
399+
loadingTask.destroy(); // Cleanup the worker.
400+
});
379401
});
380402
});
381403
it('gets metadata', function() {

web/pdf_outline_view.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ var PDFOutlineView = (function PDFOutlineViewClosure() {
6262
* @private
6363
*/
6464
_bindLink: function PDFOutlineView_bindLink(element, item) {
65+
if (item.url) {
66+
PDFJS.addLinkAttributes(element, { url: item.url });
67+
return;
68+
}
6569
var linkService = this.linkService;
6670
element.href = linkService.getDestinationHash(item.dest);
6771
element.onclick = function goToDestination(e) {

0 commit comments

Comments
 (0)