From 7a999d1d670ea691c92330aab3201c0568279106 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Wed, 3 Apr 2019 13:48:18 +0200 Subject: [PATCH] [api-minor] Add basic support for PageLayout in the API and the viewer Please see the specification, https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/PDF32000_2008.pdf#G6.2393749, and refer to the inline comments for additional details. --- src/core/obj.js | 21 +++++++++++++++++++++ src/core/worker.js | 4 ++++ src/display/api.js | 12 ++++++++++++ test/unit/api_spec.js | 18 ++++++++++++++++++ web/app.js | 32 ++++++++++++++++++++++++++++++-- 5 files changed, 85 insertions(+), 2 deletions(-) diff --git a/src/core/obj.js b/src/core/obj.js index 2516133587498..434b5831ed56c 100644 --- a/src/core/obj.js +++ b/src/core/obj.js @@ -376,6 +376,27 @@ class Catalog { return pageLabels; } + get pageLayout() { + const obj = this.catDict.get('PageLayout'); + // Purposely use a non-standard default value, rather than 'SinglePage', to + // allow differentiating between `undefined` and /SinglePage since that does + // affect the Scroll mode (continuous/non-continuous) used in Adobe Reader. + let pageLayout = ''; + + if (isName(obj)) { + switch (obj.name) { + case 'SinglePage': + case 'OneColumn': + case 'TwoColumnLeft': + case 'TwoColumnRight': + case 'TwoPageLeft': + case 'TwoPageRight': + pageLayout = obj.name; + } + } + return shadow(this, 'pageLayout', pageLayout); + } + get pageMode() { const obj = this.catDict.get('PageMode'); let pageMode = 'UseNone'; // Default value. diff --git a/src/core/worker.js b/src/core/worker.js index f0eba694f3f78..c1684c0fc8bc5 100644 --- a/src/core/worker.js +++ b/src/core/worker.js @@ -517,6 +517,10 @@ var WorkerMessageHandler = { } ); + handler.on('GetPageLayout', function wphSetupGetPageLayout(data) { + return pdfManager.ensureCatalog('pageLayout'); + }); + handler.on('GetPageMode', function wphSetupGetPageMode(data) { return pdfManager.ensureCatalog('pageMode'); }); diff --git a/src/display/api.js b/src/display/api.js index 188a17bd02ab1..f842490474e17 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -657,6 +657,14 @@ class PDFDocumentProxy { return this._transport.getPageLabels(); } + /** + * @return {Promise} A promise that is resolved with a {string} containing + * the page layout name. + */ + getPageLayout() { + return this._transport.getPageLayout(); + } + /** * @return {Promise} A promise that is resolved with a {string} containing * the page mode name. @@ -2214,6 +2222,10 @@ class WorkerTransport { return this.messageHandler.sendWithPromise('GetPageLabels', null); } + getPageLayout() { + return this.messageHandler.sendWithPromise('GetPageLayout', null); + } + getPageMode() { return this.messageHandler.sendWithPromise('GetPageMode', null); } diff --git a/test/unit/api_spec.js b/test/unit/api_spec.js index 5ecd8ed25d1e6..f0cb6d6b8a267 100644 --- a/test/unit/api_spec.js +++ b/test/unit/api_spec.js @@ -610,6 +610,24 @@ describe('api', function() { }).catch(done.fail); }); + it('gets default page layout', function(done) { + var loadingTask = getDocument(buildGetDocumentParams('tracemonkey.pdf')); + + loadingTask.promise.then(function(pdfDocument) { + return pdfDocument.getPageLayout(); + }).then(function(mode) { + expect(mode).toEqual(''); + + loadingTask.destroy().then(done); + }).catch(done.fail); + }); + it('gets non-default page layout', function(done) { + doc.getPageLayout().then(function(mode) { + expect(mode).toEqual('SinglePage'); + done(); + }).catch(done.fail); + }); + it('gets default page mode', function(done) { var loadingTask = getDocument(buildGetDocumentParams('tracemonkey.pdf')); diff --git a/web/app.js b/web/app.js index feb06389c931a..d000b24698c5d 100644 --- a/web/app.js +++ b/web/app.js @@ -889,6 +889,8 @@ let PDFViewerApplication = { // Since the `setInitialView` call below depends on this being resolved, // fetch it early to avoid delaying initial rendering of the PDF document. + const pageLayoutPromise = pdfDocument.getPageLayout().catch( + function() { /* Avoid breaking initial rendering; ignoring errors. */ }); const pageModePromise = pdfDocument.getPageMode().catch( function() { /* Avoid breaking initial rendering; ignoring errors. */ }); const openActionDestPromise = pdfDocument.getOpenActionDestination().catch( @@ -934,8 +936,8 @@ let PDFViewerApplication = { }).catch(() => { /* Unable to read from storage; ignoring errors. */ }); Promise.all([ - storePromise, pageModePromise, openActionDestPromise, - ]).then(async ([values = {}, pageMode, openActionDest]) => { + storePromise, pageLayoutPromise, pageModePromise, openActionDestPromise, + ]).then(async ([values = {}, pageLayout, pageMode, openActionDest]) => { const viewOnLoad = AppOptions.get('viewOnLoad'); this._initializePdfHistory({ @@ -974,6 +976,9 @@ let PDFViewerApplication = { if (pageMode && sidebarView === SidebarView.UNKNOWN) { sidebarView = apiPageModeToSidebarView(pageMode); } + if (pageLayout && spreadMode === SpreadMode.UNKNOWN) { + spreadMode = apiPageLayoutToSpreadMode(pageLayout); + } this.setInitialView(hash, { rotation, sidebarView, scrollMode, spreadMode, @@ -2434,6 +2439,29 @@ function webViewerKeyDown(evt) { } } +/** + * Converts API PageLayout values to the format used by `PDFViewer`. + * NOTE: This is supported to the extent that the viewer implements the + * necessary Scroll/Spread modes (since SinglePage, TwoPageLeft, + * and TwoPageRight all suggests using non-continuous scrolling). + * @param {string} mode - The API PageLayout value. + * @returns {number} A value from {SpreadMode}. + */ +function apiPageLayoutToSpreadMode(layout) { + switch (layout) { + case 'SinglePage': + case 'OneColumn': + return SpreadMode.NONE; + case 'TwoColumnLeft': + case 'TwoPageLeft': + return SpreadMode.ODD; + case 'TwoColumnRight': + case 'TwoPageRight': + return SpreadMode.EVEN; + } + return SpreadMode.NONE; // Default value. +} + /** * Converts API PageMode values to the format used by `PDFSidebar`. * NOTE: There's also a "FullScreen" parameter which is not possible to support,