diff --git a/extensions/chromium/preferences_schema.json b/extensions/chromium/preferences_schema.json index baa3300c4..9bff7d805 100644 --- a/extensions/chromium/preferences_schema.json +++ b/extensions/chromium/preferences_schema.json @@ -89,6 +89,10 @@ ], "default": 0 }, + "disablePageLabels": { + "type": "boolean", + "default": false + }, "disableTelemetry": { "title": "Disable telemetry", "type": "boolean", diff --git a/l10n/en-US/viewer.properties b/l10n/en-US/viewer.properties index 90c09aa99..2775ae19b 100644 --- a/l10n/en-US/viewer.properties +++ b/l10n/en-US/viewer.properties @@ -20,9 +20,13 @@ next_label=Next # LOCALIZATION NOTE (page.title): The tooltip for the pageNumber input. page.title=Page -# LOCALIZATION NOTE (page_of): "{{pageCount}}" will be replaced by a number +# LOCALIZATION NOTE (of_pages): "{{pagesCount}}" will be replaced by a number # representing the total number of pages in the document. -page_of=of {{pageCount}} +of_pages=of {{pagesCount}} +# LOCALIZATION NOTE (page_of_pages): "{{pageNumber}}" and "{{pagesCount}}" +# will be replaced by a number representing the currently visible page, +# respectively a number representing the total number of pages in the document. ++page_of_pages=({{pageNumber}} of {{pagesCount}}) zoom_out.title=Zoom Out zoom_out_label=Zoom Out diff --git a/l10n/sv-SE/viewer.properties b/l10n/sv-SE/viewer.properties index 514c32d4b..9ada2c333 100644 --- a/l10n/sv-SE/viewer.properties +++ b/l10n/sv-SE/viewer.properties @@ -20,9 +20,13 @@ next_label=Nästa # LOCALIZATION NOTE (page.title): The tooltip for the pageNumber input. page.title=Sida -# LOCALIZATION NOTE (page_of): "{{pageCount}}" will be replaced by a number +# LOCALIZATION NOTE (of_pages): "{{pagesCount}}" will be replaced by a number # representing the total number of pages in the document. -page_of=av {{pageCount}} +of_pages=av {{pagesCount}} +# LOCALIZATION NOTE (page_of_pages): "{{pageNumber}}" and "{{pagesCount}}" +# will be replaced by a number representing the currently active visible page, +# respectively a number representing the total number of pages in the document. +page_of_pages=({{pageNumber}} av {{pagesCount}}) zoom_out.title=Zooma ut zoom_out_label=Zooma ut diff --git a/web/app.js b/web/app.js index 73cc29911..a2fbfb884 100644 --- a/web/app.js +++ b/web/app.js @@ -178,10 +178,12 @@ var PDFViewerApplication = { preferencePdfBugEnabled: false, preferenceShowPreviousViewOnLoad: true, preferenceDefaultZoomValue: '', + preferenceDisablePageLabels: false, isViewerEmbedded: (window.parent !== window), url: '', baseUrl: '', externalServices: DefaultExernalServices, + hasPageLabels: false, // called once when the document is loaded initialize: function pdfViewInitialize(appConfig) { @@ -380,6 +382,9 @@ var PDFViewerApplication = { // before the various viewer components are initialized. self.pdfViewer.renderInteractiveForms = value; }), + Preferences.get('disablePageLabels').then(function resolved(value) { + self.preferenceDisablePageLabels = value; + }), // TODO move more preferences and other async stuff here ]).catch(function (reason) { }); @@ -567,6 +572,7 @@ var PDFViewerApplication = { } this.store = null; this.isInitialViewSet = false; + this.hasPageLabels = false; this.pdfSidebar.reset(); this.pdfOutlineViewer.reset(); @@ -879,7 +885,8 @@ var PDFViewerApplication = { this.pageRotation = 0; - this.pdfThumbnailViewer.setDocument(pdfDocument); + var pdfThumbnailViewer = this.pdfThumbnailViewer; + pdfThumbnailViewer.setDocument(pdfDocument); firstPagePromise.then(function(pdfPage) { downloadedPromise.then(function () { @@ -959,6 +966,33 @@ var PDFViewerApplication = { }); }); + pdfDocument.getPageLabels().then(function (labels) { + if (!labels || self.preferenceDisablePageLabels) { + return; + } + var i = 0, numLabels = labels.length; + if (numLabels !== self.pagesCount) { + console.error('The number of Page Labels does not match ' + + 'the number of pages in the document.'); + return; + } + // Ignore page labels that correspond to standard page numbering. + while (i < numLabels && labels[i] === (i + 1).toString()) { + i++; + } + if (i === numLabels) { + return; + } + + pdfViewer.setPageLabels(labels); + pdfThumbnailViewer.setPageLabels(labels); + + self.hasPageLabels = true; + self._updateUIToolbar({ + resetNumPages: true, + }); + }); + pagesPromise.then(function() { if (self.supportsPrinting) { pdfDocument.getJavaScript().then(function(javaScript) { @@ -1186,6 +1220,7 @@ var PDFViewerApplication = { /** * @typedef UpdateUIToolbarParameters * @property {number} pageNumber + * @property {string} pageLabel * @property {string} scaleValue * @property {number} scale * @property {boolean} resetNumPages @@ -1226,11 +1261,25 @@ var PDFViewerApplication = { var pagesCount = this.pagesCount; if (resetNumPages) { - toolbarConfig.numPages.textContent = - mozL10n.get('page_of', { pageCount: pagesCount }, 'of {{pageCount}}'); + if (this.hasPageLabels) { + toolbarConfig.pageNumber.type = 'text'; + } else { + toolbarConfig.pageNumber.type = 'number'; + toolbarConfig.numPages.textContent = mozL10n.get('of_pages', + { pagesCount: pagesCount }, 'of {{pagesCount}}'); + } toolbarConfig.pageNumber.max = pagesCount; } - toolbarConfig.pageNumber.value = pageNumber; + + if (this.hasPageLabels) { + toolbarConfig.pageNumber.value = params.pageLabel || + this.pdfViewer.currentPageLabel; + toolbarConfig.numPages.textContent = mozL10n.get('page_of_pages', + { pageNumber: pageNumber, pagesCount: pagesCount }, + '({{pageNumber}} of {{pagesCount}})'); + } else { + toolbarConfig.pageNumber.value = pageNumber; + } toolbarConfig.previous.disabled = (pageNumber <= 1); toolbarConfig.next.disabled = (pageNumber >= pagesCount); @@ -1495,11 +1544,13 @@ function webViewerInitialized() { }); appConfig.toolbar.pageNumber.addEventListener('change', function() { - PDFViewerApplication.page = (this.value | 0); + var pdfViewer = PDFViewerApplication.pdfViewer; + pdfViewer.currentPageLabel = this.value; // Ensure that the page number input displays the correct value, even if the // value entered by the user was invalid (e.g. a floating point number). - if (this.value !== PDFViewerApplication.page.toString()) { + if (this.value !== pdfViewer.currentPageNumber.toString() && + this.value !== pdfViewer.currentPageLabel) { PDFViewerApplication._updateUIToolbar({}); } }); @@ -1930,6 +1981,7 @@ function webViewerPageChanging(e) { PDFViewerApplication._updateUIToolbar({ pageNumber: page, + pageLabel: e.pageLabel, }); if (PDFViewerApplication.pdfSidebar.isThumbnailViewVisible) { diff --git a/web/default_preferences.json b/web/default_preferences.json index 242c30b12..15371f6b3 100644 --- a/web/default_preferences.json +++ b/web/default_preferences.json @@ -13,5 +13,6 @@ "useOnlyCssZoom": false, "externalLinkTarget": 0, "enhanceTextSelection": false, - "renderInteractiveForms": false + "renderInteractiveForms": false, + "disablePageLabels": false } diff --git a/web/pdf_thumbnail_viewer.js b/web/pdf_thumbnail_viewer.js index aa4181195..8a9dcc663 100644 --- a/web/pdf_thumbnail_viewer.js +++ b/web/pdf_thumbnail_viewer.js @@ -133,6 +133,7 @@ var PDFThumbnailViewer = (function PDFThumbnailViewerClosure() { */ _resetView: function PDFThumbnailViewer_resetView() { this.thumbnails = []; + this._pageLabels = null; this._pagesRotation = 0; this._pagesRequests = []; @@ -179,6 +180,24 @@ var PDFThumbnailViewer = (function PDFThumbnailViewerClosure() { } }, + /** + * @param {Array|null} labels + */ + setPageLabels: function PDFThumbnailViewer_setPageLabels(labels) { + if (!this.pdfDocument) { + return; + } + if (!labels) { + this._pageLabels = null; + } else if (!(labels instanceof Array && + this.pdfDocument.numPages === labels.length)) { + this._pageLabels = null; + console.error('PDFThumbnailViewer_setPageLabels: Invalid page labels.'); + } else { + this._pageLabels = labels; + } + }, + /** * @param {PDFThumbnailView} thumbView * @returns {PDFPage} diff --git a/web/pdf_viewer.js b/web/pdf_viewer.js index 16c3198b7..5f7b97c13 100644 --- a/web/pdf_viewer.js +++ b/web/pdf_viewer.js @@ -211,6 +211,7 @@ var PDFViewer = (function pdfViewer() { var arg = { source: this, pageNumber: val, + pageLabel: this._pageLabels && this._pageLabels[val - 1], }; this._currentPageNumber = val; this.eventBus.dispatch('pagechanging', arg); @@ -221,6 +222,28 @@ var PDFViewer = (function pdfViewer() { } }, + /** + * @returns {string|null} Returns the current page label, + * or `null` if no page labels exist. + */ + get currentPageLabel() { + return this._pageLabels && this._pageLabels[this._currentPageNumber - 1]; + }, + + /** + * @param {string} val - The page label. + */ + set currentPageLabel(val) { + var pageNumber = val | 0; // Fallback page number. + if (this._pageLabels) { + var i = this._pageLabels.indexOf(val); + if (i >= 0) { + pageNumber = i + 1; + } + } + this.currentPageNumber = pageNumber; + }, + /** * @returns {number} */ @@ -414,11 +437,30 @@ var PDFViewer = (function pdfViewer() { }.bind(this)); }, + /** + * @param {Array|null} labels + */ + setPageLabels: function PDFViewer_setPageLabels(labels) { + if (!this.pdfDocument) { + return; + } + if (!labels) { + this._pageLabels = null; + } else if (!(labels instanceof Array && + this.pdfDocument.numPages === labels.length)) { + this._pageLabels = null; + console.error('PDFViewer_setPageLabels: Invalid page labels.'); + } else { + this._pageLabels = labels; + } + }, + _resetView: function () { this._pages = []; this._currentPageNumber = 1; this._currentScale = UNKNOWN_SCALE; this._currentScaleValue = null; + this._pageLabels = null; this._buffer = new PDFPageViewBuffer(DEFAULT_CACHE_SIZE); this._location = null; this._pagesRotation = 0;