From c1022322757366b0204896085378cb5e199de480 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sun, 15 Jan 2017 18:12:24 +0100 Subject: [PATCH] Append the contents of `FileAttachment` annotations to the attachments view of the sidebar, for easier access to the embedded files Other PDF viewers, e.g. Adobe Reader, seem to append `FileAttachment`s to their attachments views. One obvious difference in PDF.js is that we cannot append all the annotations on document load, since that would require parsing *every* page. Despite that, it still seems like a good idea to add `FileAttachment`s, since it's thus possible to access all the various types of attachments from a single place. *Note:* With the previous patch we display a notification when a `FileAttachment` is added to the sidebar, which thus makes appending the contents of these annotations to the sidebar slightly more visible/useful. --- src/display/annotation_layer.js | 18 ++++++++---- src/main_loader.js | 1 + src/pdf.js | 2 ++ test/driver.js | 14 ++++++++- web/pdf_attachment_viewer.js | 52 +++++++++++++++++++++++++++++---- web/pdf_link_service.js | 16 ++++++++++ 6 files changed, 91 insertions(+), 12 deletions(-) diff --git a/src/display/annotation_layer.js b/src/display/annotation_layer.js index d8a5a8909..fded967d5 100644 --- a/src/display/annotation_layer.js +++ b/src/display/annotation_layer.js @@ -29,6 +29,7 @@ var AnnotationBorderStyleType = sharedUtil.AnnotationBorderStyleType; var AnnotationType = sharedUtil.AnnotationType; +var stringToPDFString = sharedUtil.stringToPDFString; var Util = sharedUtil.Util; var addLinkAttributes = displayDOMUtils.addLinkAttributes; var LinkTarget = displayDOMUtils.LinkTarget; @@ -1007,8 +1008,15 @@ var FileAttachmentAnnotationElement = ( function FileAttachmentAnnotationElement(parameters) { AnnotationElement.call(this, parameters, true); - this.filename = getFilenameFromUrl(parameters.data.file.filename); - this.content = parameters.data.file.content; + var file = this.data.file; + this.filename = getFilenameFromUrl(file.filename); + this.content = file.content; + + this.linkService.onFileAttachmentAnnotation({ + id: stringToPDFString(file.filename), + filename: file.filename, + content: file.content, + }); } Util.inherit(FileAttachmentAnnotationElement, AnnotationElement, { @@ -1086,8 +1094,7 @@ var AnnotationLayer = (function AnnotationLayerClosure() { if (!data) { continue; } - - var properties = { + var element = annotationElementFactory.create({ data: data, layer: parameters.div, page: parameters.page, @@ -1097,8 +1104,7 @@ var AnnotationLayer = (function AnnotationLayerClosure() { imageResourcesPath: parameters.imageResourcesPath || getDefaultSetting('imageResourcesPath'), renderInteractiveForms: parameters.renderInteractiveForms || false, - }; - var element = annotationElementFactory.create(properties); + }); if (element.isRenderable) { parameters.div.appendChild(element.render()); } diff --git a/src/main_loader.js b/src/main_loader.js index a1660908d..1de29358f 100644 --- a/src/main_loader.js +++ b/src/main_loader.js @@ -48,6 +48,7 @@ exports.renderTextLayer = displayTextLayer.renderTextLayer; exports.AnnotationLayer = displayAnnotationLayer.AnnotationLayer; exports.CustomStyle = displayDOMUtils.CustomStyle; + exports.createPromiseCapability = sharedUtil.createPromiseCapability; exports.PasswordResponses = sharedUtil.PasswordResponses; exports.InvalidPDFException = sharedUtil.InvalidPDFException; exports.MissingPDFException = sharedUtil.MissingPDFException; diff --git a/src/pdf.js b/src/pdf.js index e70d6f0de..31d127ef3 100644 --- a/src/pdf.js +++ b/src/pdf.js @@ -55,6 +55,8 @@ exports.AnnotationLayer = pdfjsLibs.pdfjsDisplayAnnotationLayer.AnnotationLayer; exports.CustomStyle = pdfjsLibs.pdfjsDisplayDOMUtils.CustomStyle; + exports.createPromiseCapability = + pdfjsLibs.pdfjsSharedUtil.createPromiseCapability; exports.PasswordResponses = pdfjsLibs.pdfjsSharedUtil.PasswordResponses; exports.InvalidPDFException = pdfjsLibs.pdfjsSharedUtil.InvalidPDFException; exports.MissingPDFException = pdfjsLibs.pdfjsSharedUtil.MissingPDFException; diff --git a/test/driver.js b/test/driver.js index 466cd2bed..7136b54e4 100644 --- a/test/driver.js +++ b/test/driver.js @@ -26,6 +26,12 @@ var LinkServiceMock = (function LinkServiceMockClosure() { function LinkServiceMock() {} LinkServiceMock.prototype = { + get page() { + return 0; + }, + + set page(value) {}, + navigateTo: function (dest) {}, getDestinationHash: function (dest) { @@ -36,7 +42,13 @@ var LinkServiceMock = (function LinkServiceMockClosure() { return '#'; }, - executeNamedAction: function (action) {} + setHash: function (hash) {}, + + executeNamedAction: function (action) {}, + + onFileAttachmentAnnotation: function (params) {}, + + cachePageRef: function (pageNum, pageRef) {}, }; return LinkServiceMock; diff --git a/web/pdf_attachment_viewer.js b/web/pdf_attachment_viewer.js index 2f171785e..dbfda4b3d 100644 --- a/web/pdf_attachment_viewer.js +++ b/web/pdf_attachment_viewer.js @@ -51,16 +51,26 @@ var PDFAttachmentViewer = (function PDFAttachmentViewerClosure() { this.container = options.container; this.eventBus = options.eventBus; this.downloadManager = options.downloadManager; + + this._renderedCapability = pdfjsLib.createPromiseCapability(); + this.eventBus.on('fileattachmentannotation', + this._appendAttachment.bind(this)); } PDFAttachmentViewer.prototype = { - reset: function PDFAttachmentViewer_reset() { + reset: function PDFAttachmentViewer_reset(keepRenderedCapability) { this.attachments = null; var container = this.container; while (container.firstChild) { container.removeChild(container.firstChild); } + + if (!keepRenderedCapability) { + // NOTE: The *only* situation in which the `_renderedCapability` should + // not be replaced is when appending file attachment annotations. + this._renderedCapability = pdfjsLib.createPromiseCapability(); + } }, /** @@ -70,8 +80,10 @@ var PDFAttachmentViewer = (function PDFAttachmentViewerClosure() { function PDFAttachmentViewer_dispatchEvent(attachmentsCount) { this.eventBus.dispatch('attachmentsloaded', { source: this, - attachmentsCount: attachmentsCount + attachmentsCount: attachmentsCount, }); + + this._renderedCapability.resolve(); }, /** @@ -89,11 +101,13 @@ var PDFAttachmentViewer = (function PDFAttachmentViewerClosure() { * @param {PDFAttachmentViewerRenderParameters} params */ render: function PDFAttachmentViewer_render(params) { - var attachments = (params && params.attachments) || null; + params = params || {}; + var attachments = params.attachments || null; var attachmentsCount = 0; if (this.attachments) { - this.reset(); + var keepRenderedCapability = params.keepRenderedCapability === true; + this.reset(keepRenderedCapability); } this.attachments = attachments; @@ -120,7 +134,35 @@ var PDFAttachmentViewer = (function PDFAttachmentViewerClosure() { } this._dispatchEvent(attachmentsCount); - } + }, + + /** + * Used to append FileAttachment annotations to the sidebar. + * @private + */ + _appendAttachment: function PDFAttachmentViewer_appendAttachment(item) { + this._renderedCapability.promise.then(function (id, filename, content) { + var attachments = this.attachments; + + if (!attachments) { + attachments = Object.create(null); + } else { + for (var name in attachments) { + if (id === name) { + return; // Ignore the new attachment if it already exists. + } + } + } + attachments[id] = { + filename: filename, + content: content, + }; + this.render({ + attachments: attachments, + keepRenderedCapability: true, + }); + }.bind(this, item.id, item.filename, item.content)); + }, }; return PDFAttachmentViewer; diff --git a/web/pdf_link_service.js b/web/pdf_link_service.js index 7dd7478d0..15ca78b17 100644 --- a/web/pdf_link_service.js +++ b/web/pdf_link_service.js @@ -350,6 +350,18 @@ var PDFLinkService = (function PDFLinkServiceClosure() { }); }, + /** + * @param {Object} params + */ + onFileAttachmentAnnotation: function (params) { + this.eventBus.dispatch('fileattachmentannotation', { + source: this, + id: params.id, + filename: params.filename, + content: params.content, + }); + }, + /** * @param {number} pageNum - page number. * @param {Object} pageRef - reference to the page. @@ -462,6 +474,10 @@ var SimpleLinkService = (function SimpleLinkServiceClosure() { * @param {string} action */ executeNamedAction: function (action) {}, + /** + * @param {Object} params + */ + onFileAttachmentAnnotation: function (params) {}, /** * @param {number} pageNum - page number. * @param {Object} pageRef - reference to the page.