From 16c5d41c5b45bee7d8e5733e7ea0157470dcaa6d Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Tue, 18 Jul 2017 13:08:02 +0200 Subject: [PATCH 1/3] [api-minor] Add support for PageMode in the API (issue 8657) Please refer to https://wwwimages2.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf#page=82. --- src/core/obj.js | 18 ++++++++++++++++++ src/core/worker.js | 4 ++++ src/display/api.js | 11 +++++++++++ test/unit/api_spec.js | 22 ++++++++++++++++++++++ 4 files changed, 55 insertions(+) diff --git a/src/core/obj.js b/src/core/obj.js index 3581e57aa..d7e67e8ad 100644 --- a/src/core/obj.js +++ b/src/core/obj.js @@ -313,6 +313,24 @@ var Catalog = (function CatalogClosure() { return pageLabels; }, + get pageMode() { + let obj = this.catDict.get('PageMode'); + let pageMode = 'UseNone'; // Default value. + + if (isName(obj)) { + switch (obj.name) { + case 'UseNone': + case 'UseOutlines': + case 'UseThumbs': + case 'FullScreen': + case 'UseOC': + case 'UseAttachments': + pageMode = obj.name; + } + } + return shadow(this, 'pageMode', pageMode); + }, + get attachments() { var xref = this.xref; var attachments = null, nameTreeRef; diff --git a/src/core/worker.js b/src/core/worker.js index f3dcecdd6..fc3ce3c60 100644 --- a/src/core/worker.js +++ b/src/core/worker.js @@ -770,6 +770,10 @@ var WorkerMessageHandler = { } ); + handler.on('GetPageMode', function wphSetupGetPageMode(data) { + return pdfManager.ensureCatalog('pageMode'); + }); + handler.on('GetAttachments', function wphSetupGetAttachments(data) { return pdfManager.ensureCatalog('attachments'); diff --git a/src/display/api.js b/src/display/api.js index b3c6d234d..b2c86e361 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -568,6 +568,13 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { getPageLabels: function PDFDocumentProxy_getPageLabels() { return this.transport.getPageLabels(); }, + /** + * @return {Promise} A promise that is resolved with a {string} containing + * the PageMode name. + */ + getPageMode() { + return this.transport.getPageMode(); + }, /** * @return {Promise} A promise that is resolved with a lookup table for * mapping named attachments to their content. @@ -1939,6 +1946,10 @@ var WorkerTransport = (function WorkerTransportClosure() { return this.messageHandler.sendWithPromise('GetPageLabels', null); }, + getPageMode() { + return this.messageHandler.sendWithPromise('GetPageMode', null); + }, + getAttachments: function WorkerTransport_getAttachments() { return this.messageHandler.sendWithPromise('GetAttachments', null); }, diff --git a/test/unit/api_spec.js b/test/unit/api_spec.js index d6d3d690c..35336a956 100644 --- a/test/unit/api_spec.js +++ b/test/unit/api_spec.js @@ -630,6 +630,28 @@ describe('api', function() { }); }); + it('gets default page mode', function(done) { + var loadingTask = getDocument(buildGetDocumentParams('tracemonkey.pdf')); + + loadingTask.promise.then(function(pdfDocument) { + return pdfDocument.getPageMode(); + }).then(function(mode) { + expect(mode).toEqual('UseNone'); + + loadingTask.destroy().then(done); + }).catch(function (reason) { + done.fail(reason); + }); + }); + it('gets non-default page mode', function(done) { + doc.getPageMode().then(function(mode) { + expect(mode).toEqual('UseOutlines'); + done(); + }).catch(function(reason) { + done.fail(reason); + }); + }); + it('gets non-existent attachments', function(done) { var promise = doc.getAttachments(); promise.then(function (data) { From f7c4ed4bc326be11b8fc74b2fd4f5bb473902a60 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Wed, 19 Jul 2017 16:04:56 +0200 Subject: [PATCH 2/3] Refactor reading from the `ViewHistory` in `PDFViewerApplication.load` --- web/app.js | 53 +++++++++++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/web/app.js b/web/app.js index f2a6a024d..7b8cfc64a 100644 --- a/web/app.js +++ b/web/app.js @@ -932,37 +932,34 @@ let PDFViewerApplication = { bookmark: this.initialBookmark, hash: null, }; - let storedHash = this.viewerPrefs['defaultZoomValue'] ? - ('zoom=' + this.viewerPrefs['defaultZoomValue']) : null; - let sidebarView = this.viewerPrefs['sidebarViewOnLoad']; + let storePromise = store.getMultiple({ + exists: false, + page: '1', + zoom: DEFAULT_SCALE_VALUE, + scrollLeft: '0', + scrollTop: '0', + sidebarView: SidebarView.NONE, + }).catch(() => { /* Unable to read from storage -- ignoring errors. */ }); - new Promise((resolve, reject) => { - if (!this.viewerPrefs['showPreviousViewOnLoad']) { - resolve(); - return; - } - store.getMultiple({ - exists: false, - page: '1', - zoom: DEFAULT_SCALE_VALUE, - scrollLeft: '0', - scrollTop: '0', - sidebarView: SidebarView.NONE, - }).then((values) => { - if (!values.exists) { - resolve(); - return; - } - storedHash = 'page=' + values.page + + storePromise.then((values = {}) => { + // Initialize the default values, from user preferences. + let hash = this.viewerPrefs['defaultZoomValue'] ? + ('zoom=' + this.viewerPrefs['defaultZoomValue']) : null; + let sidebarView = this.viewerPrefs['sidebarViewOnLoad']; + + if (values.exists && this.viewerPrefs['showPreviousViewOnLoad']) { + hash = 'page=' + values.page + '&zoom=' + (this.viewerPrefs['defaultZoomValue'] || values.zoom) + ',' + values.scrollLeft + ',' + values.scrollTop; - sidebarView = this.viewerPrefs['sidebarViewOnLoad'] || - (values.sidebarView | 0); - resolve(); - }).catch(resolve); - }).then(() => { - this.setInitialView(storedHash, { sidebarView, scale, }); - initialParams.hash = storedHash; + sidebarView = sidebarView || (values.sidebarView | 0); + } + return { + hash, + sidebarView, + }; + }).then(({ hash, sidebarView, }) => { + this.setInitialView(hash, { sidebarView, scale, }); + initialParams.hash = hash; // Make all navigation keys work on document load, // unless the viewer is embedded in a web page. From 20d6286cceb936dfcb334ac1c963cf93c0da3637 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Wed, 19 Jul 2017 16:26:17 +0200 Subject: [PATCH 3/3] Add support for, the API property, PageMode in the viewer (issue 8657) Note that the PageMode, as specified in the API, will only be honoured when either: the user hasn't set the `sidebarViewOnLoad` preference to a non-default value, or a non-default `sidebarView` entry doesn't exist in the view history, or the "pagemode" hash parameter is included in the URL. Since this is new functionality, the patch also includes a preference (`disablePageMode`), to make it easy to opt-out of this functionality if the user/implementor so wishes. --- extensions/chromium/preferences_schema.json | 4 ++ web/app.js | 42 ++++++++++++++++++++- web/default_preferences.json | 1 + 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/extensions/chromium/preferences_schema.json b/extensions/chromium/preferences_schema.json index 0371a9cf9..16cf475eb 100644 --- a/extensions/chromium/preferences_schema.json +++ b/extensions/chromium/preferences_schema.json @@ -102,6 +102,10 @@ "type": "boolean", "default": false }, + "disablePageMode": { + "type": "boolean", + "default": false + }, "disableTelemetry": { "title": "Disable telemetry", "type": "boolean", diff --git a/web/app.js b/web/app.js index 7b8cfc64a..7c7ee1fdf 100644 --- a/web/app.js +++ b/web/app.js @@ -141,6 +141,7 @@ let PDFViewerApplication = { pdfBugEnabled: false, showPreviousViewOnLoad: true, defaultZoomValue: '', + disablePageMode: false, disablePageLabels: false, renderer: 'canvas', enhanceTextSelection: false, @@ -255,6 +256,9 @@ let PDFViewerApplication = { preferences.get('renderInteractiveForms').then(function resolved(value) { viewerPrefs['renderInteractiveForms'] = value; }), + preferences.get('disablePageMode').then(function resolved(value) { + viewerPrefs['disablePageMode'] = value; + }), preferences.get('disablePageLabels').then(function resolved(value) { viewerPrefs['disablePageLabels'] = value; }), @@ -883,6 +887,11 @@ let PDFViewerApplication = { }); }); + // Since the `setInitialView` call below depends on this being resolved, + // fetch it early to avoid delaying initial rendering of the PDF document. + let pageModePromise = pdfDocument.getPageMode().catch( + function() { /* Avoid breaking initial rendering; ignoring errors. */ }); + this.toolbar.setPagesCount(pdfDocument.numPages, false); this.secondaryToolbar.setPagesCount(pdfDocument.numPages); @@ -939,9 +948,10 @@ let PDFViewerApplication = { scrollLeft: '0', scrollTop: '0', sidebarView: SidebarView.NONE, - }).catch(() => { /* Unable to read from storage -- ignoring errors. */ }); + }).catch(() => { /* Unable to read from storage; ignoring errors. */ }); - storePromise.then((values = {}) => { + Promise.all([storePromise, pageModePromise]).then( + ([values = {}, pageMode]) => { // Initialize the default values, from user preferences. let hash = this.viewerPrefs['defaultZoomValue'] ? ('zoom=' + this.viewerPrefs['defaultZoomValue']) : null; @@ -953,6 +963,10 @@ let PDFViewerApplication = { ',' + values.scrollLeft + ',' + values.scrollTop; sidebarView = sidebarView || (values.sidebarView | 0); } + if (pageMode && !this.viewerPrefs['disablePageMode']) { + // Always let the user preference/history take precedence. + sidebarView = sidebarView || apiPageModeToSidebarView(pageMode); + } return { hash, sidebarView, @@ -2290,6 +2304,30 @@ function webViewerKeyDown(evt) { } } +/** + * Converts API PageMode values to the format used by `PDFSidebar`. + * NOTE: There's also a "FullScreen" parameter which is not possible to support, + * since the Fullscreen API used in browsers requires that entering + * fullscreen mode only occurs as a result of a user-initiated event. + * @param {string} mode - The API PageMode value. + * @returns {number} A value from {SidebarView}. + */ +function apiPageModeToSidebarView(mode) { + switch (mode) { + case 'UseNone': + return SidebarView.NONE; + case 'UseThumbs': + return SidebarView.THUMBS; + case 'UseOutlines': + return SidebarView.OUTLINE; + case 'UseAttachments': + return SidebarView.ATTACHMENTS; + case 'UseOC': + // Not implemented, since we don't support Optional Content Groups yet. + } + return SidebarView.NONE; // Default value. +} + /* Abstract factory for the print service. */ let PDFPrintServiceFactory = { instance: { diff --git a/web/default_preferences.json b/web/default_preferences.json index 73b315bff..425de07c3 100644 --- a/web/default_preferences.json +++ b/web/default_preferences.json @@ -17,5 +17,6 @@ "renderer": "canvas", "renderInteractiveForms": false, "enablePrintAutoRotate": false, + "disablePageMode": false, "disablePageLabels": false }