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/src/core/obj.js b/src/core/obj.js index 82013cb75..652914efa 100644 --- a/src/core/obj.js +++ b/src/core/obj.js @@ -329,6 +329,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 b39692b38..8be7d816d 100644 --- a/src/core/worker.js +++ b/src/core/worker.js @@ -772,6 +772,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) { diff --git a/web/app.js b/web/app.js index f2a6a024d..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); @@ -932,37 +941,39 @@ 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 + + Promise.all([storePromise, pageModePromise]).then( + ([values = {}, pageMode]) => { + // 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); + } + if (pageMode && !this.viewerPrefs['disablePageMode']) { + // Always let the user preference/history take precedence. + sidebarView = sidebarView || apiPageModeToSidebarView(pageMode); + } + 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. @@ -2293,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 }