/web/viewer.js
https://github.com/hashir/pdf.js · JavaScript · 2429 lines · 1779 code · 282 blank · 368 comment · 307 complexity · f80dea653934ba02b6dbb83ed0fc6e29 MD5 · raw file
Large files are truncated click here to view the full file
- /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
- /* Copyright 2012 Mozilla Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- /* globals PDFJS, PDFBug, FirefoxCom, Stats, Cache, PDFFindBar, CustomStyle,
- PDFFindController, ProgressBar, TextLayerBuilder, DownloadManager,
- getFileName, scrollIntoView, getPDFFileNameFromURL, PDFHistory,
- Preferences, SidebarView, ViewHistory, PageView, ThumbnailView, URL,
- noContextMenuHandler, SecondaryToolbar, PasswordPrompt,
- PresentationMode, HandTool, Promise, DocumentProperties,
- DocumentOutlineView, DocumentAttachmentsView */
- 'use strict';
- var DEFAULT_URL = 'compressed.tracemonkey-pldi-09.pdf';
- var DEFAULT_SCALE = 'auto';
- var DEFAULT_SCALE_DELTA = 1.1;
- var UNKNOWN_SCALE = 0;
- var CACHE_SIZE = 20;
- var CSS_UNITS = 96.0 / 72.0;
- var SCROLLBAR_PADDING = 40;
- var VERTICAL_PADDING = 5;
- var MAX_AUTO_SCALE = 1.25;
- var MIN_SCALE = 0.25;
- var MAX_SCALE = 4.0;
- var VIEW_HISTORY_MEMORY = 20;
- var SCALE_SELECT_CONTAINER_PADDING = 8;
- var SCALE_SELECT_PADDING = 22;
- var THUMBNAIL_SCROLL_MARGIN = -19;
- var USE_ONLY_CSS_ZOOM = false;
- var CLEANUP_TIMEOUT = 30000;
- var IGNORE_CURRENT_POSITION_ON_ZOOM = false;
- //#if B2G
- //USE_ONLY_CSS_ZOOM = true;
- //PDFJS.disableTextLayer = true;
- //#endif
- var RenderingStates = {
- INITIAL: 0,
- RUNNING: 1,
- PAUSED: 2,
- FINISHED: 3
- };
- var FindStates = {
- FIND_FOUND: 0,
- FIND_NOTFOUND: 1,
- FIND_WRAPPED: 2,
- FIND_PENDING: 3
- };
- PDFJS.imageResourcesPath = './images/';
- //#if (FIREFOX || MOZCENTRAL || B2G || GENERIC || CHROME)
- //PDFJS.workerSrc = '../build/pdf.worker.js';
- //#endif
- //#if !PRODUCTION
- PDFJS.cMapUrl = '../external/bcmaps/';
- PDFJS.cMapPacked = true;
- //#else
- //PDFJS.cMapUrl = '../web/cmaps/';
- //PDFJS.cMapPacked = true;
- //#endif
- var mozL10n = document.mozL10n || document.webL10n;
- //#include ui_utils.js
- //#include preferences.js
- //#if !(FIREFOX || MOZCENTRAL || B2G)
- //#include mozPrintCallback_polyfill.js
- //#endif
- //#if GENERIC || CHROME
- //#include download_manager.js
- //#endif
- //#if FIREFOX || MOZCENTRAL
- //#include firefoxcom.js
- //#endif
- //#if CHROME
- //#include chromecom.js
- //#endif
- var cache = new Cache(CACHE_SIZE);
- var currentPageNumber = 1;
- //#include view_history.js
- //#include pdf_find_bar.js
- //#include pdf_find_controller.js
- //#include pdf_history.js
- //#include secondary_toolbar.js
- //#include password_prompt.js
- //#include presentation_mode.js
- //#include hand_tool.js
- //#include document_properties.js
- var PDFView = {
- pages: [],
- thumbnails: [],
- currentScale: UNKNOWN_SCALE,
- currentScaleValue: null,
- initialBookmark: document.location.hash.substring(1),
- container: null,
- thumbnailContainer: null,
- initialized: false,
- fellback: false,
- pdfDocument: null,
- sidebarOpen: false,
- pageViewScroll: null,
- thumbnailViewScroll: null,
- pageRotation: 0,
- mouseScrollTimeStamp: 0,
- mouseScrollDelta: 0,
- lastScroll: 0,
- previousPageNumber: 1,
- isViewerEmbedded: (window.parent !== window),
- idleTimeout: null,
- currentPosition: null,
- // called once when the document is loaded
- initialize: function pdfViewInitialize() {
- var self = this;
- var container = this.container = document.getElementById('viewerContainer');
- this.pageViewScroll = {};
- this.watchScroll(container, this.pageViewScroll, updateViewarea);
- var thumbnailContainer = this.thumbnailContainer =
- document.getElementById('thumbnailView');
- this.thumbnailViewScroll = {};
- this.watchScroll(thumbnailContainer, this.thumbnailViewScroll,
- this.renderHighestPriority.bind(this));
- Preferences.initialize();
- PDFFindBar.initialize({
- bar: document.getElementById('findbar'),
- toggleButton: document.getElementById('viewFind'),
- findField: document.getElementById('findInput'),
- highlightAllCheckbox: document.getElementById('findHighlightAll'),
- caseSensitiveCheckbox: document.getElementById('findMatchCase'),
- findMsg: document.getElementById('findMsg'),
- findStatusIcon: document.getElementById('findStatusIcon'),
- findPreviousButton: document.getElementById('findPrevious'),
- findNextButton: document.getElementById('findNext')
- });
- PDFFindController.initialize({
- pdfPageSource: this,
- integratedFind: this.supportsIntegratedFind
- });
- HandTool.initialize({
- container: container,
- toggleHandTool: document.getElementById('toggleHandTool')
- });
- SecondaryToolbar.initialize({
- toolbar: document.getElementById('secondaryToolbar'),
- presentationMode: PresentationMode,
- toggleButton: document.getElementById('secondaryToolbarToggle'),
- presentationModeButton:
- document.getElementById('secondaryPresentationMode'),
- openFile: document.getElementById('secondaryOpenFile'),
- print: document.getElementById('secondaryPrint'),
- download: document.getElementById('secondaryDownload'),
- viewBookmark: document.getElementById('secondaryViewBookmark'),
- firstPage: document.getElementById('firstPage'),
- lastPage: document.getElementById('lastPage'),
- pageRotateCw: document.getElementById('pageRotateCw'),
- pageRotateCcw: document.getElementById('pageRotateCcw'),
- documentProperties: DocumentProperties,
- documentPropertiesButton: document.getElementById('documentProperties')
- });
- PasswordPrompt.initialize({
- overlayContainer: document.getElementById('overlayContainer'),
- passwordField: document.getElementById('password'),
- passwordText: document.getElementById('passwordText'),
- passwordSubmit: document.getElementById('passwordSubmit'),
- passwordCancel: document.getElementById('passwordCancel')
- });
- PresentationMode.initialize({
- container: container,
- secondaryToolbar: SecondaryToolbar,
- firstPage: document.getElementById('contextFirstPage'),
- lastPage: document.getElementById('contextLastPage'),
- pageRotateCw: document.getElementById('contextPageRotateCw'),
- pageRotateCcw: document.getElementById('contextPageRotateCcw')
- });
- DocumentProperties.initialize({
- overlayContainer: document.getElementById('overlayContainer'),
- closeButton: document.getElementById('documentPropertiesClose'),
- fileNameField: document.getElementById('fileNameField'),
- fileSizeField: document.getElementById('fileSizeField'),
- titleField: document.getElementById('titleField'),
- authorField: document.getElementById('authorField'),
- subjectField: document.getElementById('subjectField'),
- keywordsField: document.getElementById('keywordsField'),
- creationDateField: document.getElementById('creationDateField'),
- modificationDateField: document.getElementById('modificationDateField'),
- creatorField: document.getElementById('creatorField'),
- producerField: document.getElementById('producerField'),
- versionField: document.getElementById('versionField'),
- pageCountField: document.getElementById('pageCountField')
- });
- container.addEventListener('scroll', function() {
- self.lastScroll = Date.now();
- }, false);
- var initializedPromise = Promise.all([
- Preferences.get('enableWebGL').then(function resolved(value) {
- PDFJS.disableWebGL = !value;
- }, function rejected(reason) {}),
- Preferences.get('sidebarViewOnLoad').then(function resolved(value) {
- self.preferenceSidebarViewOnLoad = value;
- }, function rejected(reason) {})
- // TODO move more preferences and other async stuff here
- ]);
- return initializedPromise.then(function () {
- PDFView.initialized = true;
- });
- },
- getPage: function pdfViewGetPage(n) {
- return this.pdfDocument.getPage(n);
- },
- // Helper function to keep track whether a div was scrolled up or down and
- // then call a callback.
- watchScroll: function pdfViewWatchScroll(viewAreaElement, state, callback) {
- state.down = true;
- state.lastY = viewAreaElement.scrollTop;
- viewAreaElement.addEventListener('scroll', function webViewerScroll(evt) {
- if (!PDFView.pdfDocument) {
- return;
- }
- var currentY = viewAreaElement.scrollTop;
- var lastY = state.lastY;
- if (currentY > lastY) {
- state.down = true;
- } else if (currentY < lastY) {
- state.down = false;
- }
- // else do nothing and use previous value
- state.lastY = currentY;
- callback();
- }, true);
- },
- _setScaleUpdatePages: function pdfView_setScaleUpdatePages(
- newScale, newValue, resetAutoSettings, noScroll) {
- this.currentScaleValue = newValue;
- if (newScale === this.currentScale) {
- return;
- }
- for (var i = 0, ii = this.pages.length; i < ii; i++) {
- this.pages[i].update(newScale);
- }
- this.currentScale = newScale;
- if (!noScroll) {
- var page = this.page, dest;
- if (this.currentPosition && !IGNORE_CURRENT_POSITION_ON_ZOOM) {
- page = this.currentPosition.page;
- dest = [null, { name: 'XYZ' }, this.currentPosition.left,
- this.currentPosition.top, null];
- }
- this.pages[page - 1].scrollIntoView(dest);
- }
- var event = document.createEvent('UIEvents');
- event.initUIEvent('scalechange', false, false, window, 0);
- event.scale = newScale;
- event.resetAutoSettings = resetAutoSettings;
- window.dispatchEvent(event);
- },
- setScale: function pdfViewSetScale(value, resetAutoSettings, noScroll) {
- if (value === 'custom') {
- return;
- }
- var scale = parseFloat(value);
- if (scale > 0) {
- this._setScaleUpdatePages(scale, value, true, noScroll);
- } else {
- var currentPage = this.pages[this.page - 1];
- if (!currentPage) {
- return;
- }
- var hPadding = PresentationMode.active ? 0 : SCROLLBAR_PADDING;
- var vPadding = PresentationMode.active ? 0 : VERTICAL_PADDING;
- var pageWidthScale = (this.container.clientWidth - hPadding) /
- currentPage.width * currentPage.scale;
- var pageHeightScale = (this.container.clientHeight - vPadding) /
- currentPage.height * currentPage.scale;
- switch (value) {
- case 'page-actual':
- scale = 1;
- break;
- case 'page-width':
- scale = pageWidthScale;
- break;
- case 'page-height':
- scale = pageHeightScale;
- break;
- case 'page-fit':
- scale = Math.min(pageWidthScale, pageHeightScale);
- break;
- case 'auto':
- scale = Math.min(MAX_AUTO_SCALE, pageWidthScale);
- break;
- default:
- console.error('pdfViewSetScale: \'' + value +
- '\' is an unknown zoom value.');
- return;
- }
- this._setScaleUpdatePages(scale, value, resetAutoSettings, noScroll);
- selectScaleOption(value);
- }
- },
- zoomIn: function pdfViewZoomIn(ticks) {
- var newScale = this.currentScale;
- do {
- newScale = (newScale * DEFAULT_SCALE_DELTA).toFixed(2);
- newScale = Math.ceil(newScale * 10) / 10;
- newScale = Math.min(MAX_SCALE, newScale);
- } while (--ticks && newScale < MAX_SCALE);
- this.setScale(newScale, true);
- },
- zoomOut: function pdfViewZoomOut(ticks) {
- var newScale = this.currentScale;
- do {
- newScale = (newScale / DEFAULT_SCALE_DELTA).toFixed(2);
- newScale = Math.floor(newScale * 10) / 10;
- newScale = Math.max(MIN_SCALE, newScale);
- } while (--ticks && newScale > MIN_SCALE);
- this.setScale(newScale, true);
- },
- set page(val) {
- var pages = this.pages;
- var event = document.createEvent('UIEvents');
- event.initUIEvent('pagechange', false, false, window, 0);
- if (!(0 < val && val <= pages.length)) {
- this.previousPageNumber = val;
- event.pageNumber = this.page;
- window.dispatchEvent(event);
- return;
- }
- pages[val - 1].updateStats();
- this.previousPageNumber = currentPageNumber;
- currentPageNumber = val;
- event.pageNumber = val;
- window.dispatchEvent(event);
- // checking if the this.page was called from the updateViewarea function:
- // avoiding the creation of two "set page" method (internal and public)
- if (updateViewarea.inProgress) {
- return;
- }
- // Avoid scrolling the first page during loading
- if (this.loading && val === 1) {
- return;
- }
- pages[val - 1].scrollIntoView();
- },
- get page() {
- return currentPageNumber;
- },
- get supportsPrinting() {
- var canvas = document.createElement('canvas');
- var value = 'mozPrintCallback' in canvas;
- // shadow
- Object.defineProperty(this, 'supportsPrinting', { value: value,
- enumerable: true,
- configurable: true,
- writable: false });
- return value;
- },
- get supportsFullscreen() {
- var doc = document.documentElement;
- var support = doc.requestFullscreen || doc.mozRequestFullScreen ||
- doc.webkitRequestFullScreen || doc.msRequestFullscreen;
- if (document.fullscreenEnabled === false ||
- document.mozFullScreenEnabled === false ||
- document.webkitFullscreenEnabled === false ||
- document.msFullscreenEnabled === false) {
- support = false;
- }
- Object.defineProperty(this, 'supportsFullscreen', { value: support,
- enumerable: true,
- configurable: true,
- writable: false });
- return support;
- },
- get supportsIntegratedFind() {
- var support = false;
- //#if !(FIREFOX || MOZCENTRAL)
- //#else
- // support = FirefoxCom.requestSync('supportsIntegratedFind');
- //#endif
- Object.defineProperty(this, 'supportsIntegratedFind', { value: support,
- enumerable: true,
- configurable: true,
- writable: false });
- return support;
- },
- get supportsDocumentFonts() {
- var support = true;
- //#if !(FIREFOX || MOZCENTRAL)
- //#else
- // support = FirefoxCom.requestSync('supportsDocumentFonts');
- //#endif
- Object.defineProperty(this, 'supportsDocumentFonts', { value: support,
- enumerable: true,
- configurable: true,
- writable: false });
- return support;
- },
- get supportsDocumentColors() {
- var support = true;
- //#if !(FIREFOX || MOZCENTRAL)
- //#else
- // support = FirefoxCom.requestSync('supportsDocumentColors');
- //#endif
- Object.defineProperty(this, 'supportsDocumentColors', { value: support,
- enumerable: true,
- configurable: true,
- writable: false });
- return support;
- },
- get loadingBar() {
- var bar = new ProgressBar('#loadingBar', {});
- Object.defineProperty(this, 'loadingBar', { value: bar,
- enumerable: true,
- configurable: true,
- writable: false });
- return bar;
- },
- get isHorizontalScrollbarEnabled() {
- return (PresentationMode.active ? false :
- (this.container.scrollWidth > this.container.clientWidth));
- },
- //#if (FIREFOX || MOZCENTRAL)
- initPassiveLoading: function pdfViewInitPassiveLoading() {
- var pdfDataRangeTransport = {
- rangeListeners: [],
- progressListeners: [],
- addRangeListener: function PdfDataRangeTransport_addRangeListener(
- listener) {
- this.rangeListeners.push(listener);
- },
- addProgressListener: function PdfDataRangeTransport_addProgressListener(
- listener) {
- this.progressListeners.push(listener);
- },
- onDataRange: function PdfDataRangeTransport_onDataRange(begin, chunk) {
- var listeners = this.rangeListeners;
- for (var i = 0, n = listeners.length; i < n; ++i) {
- listeners[i](begin, chunk);
- }
- },
- onDataProgress: function PdfDataRangeTransport_onDataProgress(loaded) {
- var listeners = this.progressListeners;
- for (var i = 0, n = listeners.length; i < n; ++i) {
- listeners[i](loaded);
- }
- },
- requestDataRange: function PdfDataRangeTransport_requestDataRange(
- begin, end) {
- FirefoxCom.request('requestDataRange', { begin: begin, end: end });
- }
- };
- window.addEventListener('message', function windowMessage(e) {
- if (e.source !== null) {
- // The message MUST originate from Chrome code.
- console.warn('Rejected untrusted message from ' + e.origin);
- return;
- }
- var args = e.data;
- if (typeof args !== 'object' || !('pdfjsLoadAction' in args)) {
- return;
- }
- switch (args.pdfjsLoadAction) {
- case 'supportsRangedLoading':
- PDFView.open(args.pdfUrl, 0, undefined, pdfDataRangeTransport, {
- length: args.length,
- initialData: args.data
- });
- break;
- case 'range':
- pdfDataRangeTransport.onDataRange(args.begin, args.chunk);
- break;
- case 'rangeProgress':
- pdfDataRangeTransport.onDataProgress(args.loaded);
- break;
- case 'progress':
- PDFView.progress(args.loaded / args.total);
- break;
- case 'complete':
- if (!args.data) {
- PDFView.error(mozL10n.get('loading_error', null,
- 'An error occurred while loading the PDF.'), e);
- break;
- }
- PDFView.open(args.data, 0);
- break;
- }
- });
- FirefoxCom.requestSync('initPassiveLoading', null);
- },
- //#endif
- setTitleUsingUrl: function pdfViewSetTitleUsingUrl(url) {
- this.url = url;
- try {
- this.setTitle(decodeURIComponent(getFileName(url)) || url);
- } catch (e) {
- // decodeURIComponent may throw URIError,
- // fall back to using the unprocessed url in that case
- this.setTitle(url);
- }
- },
- setTitle: function pdfViewSetTitle(title) {
- document.title = title;
- //#if B2G
- // document.getElementById('activityTitle').textContent = title;
- //#endif
- },
- close: function pdfViewClose() {
- var errorWrapper = document.getElementById('errorWrapper');
- errorWrapper.setAttribute('hidden', 'true');
- if (!this.pdfDocument) {
- return;
- }
- this.pdfDocument.destroy();
- this.pdfDocument = null;
- var thumbsView = document.getElementById('thumbnailView');
- while (thumbsView.hasChildNodes()) {
- thumbsView.removeChild(thumbsView.lastChild);
- }
- if ('_loadingInterval' in thumbsView) {
- clearInterval(thumbsView._loadingInterval);
- }
- var container = document.getElementById('viewer');
- while (container.hasChildNodes()) {
- container.removeChild(container.lastChild);
- }
- if (typeof PDFBug !== 'undefined') {
- PDFBug.cleanup();
- }
- },
- // TODO(mack): This function signature should really be pdfViewOpen(url, args)
- open: function pdfViewOpen(url, scale, password,
- pdfDataRangeTransport, args) {
- if (this.pdfDocument) {
- // Reload the preferences if a document was previously opened.
- Preferences.reload();
- }
- this.close();
- var parameters = {password: password};
- if (typeof url === 'string') { // URL
- this.setTitleUsingUrl(url);
- parameters.url = url;
- } else if (url && 'byteLength' in url) { // ArrayBuffer
- parameters.data = url;
- }
- if (args) {
- for (var prop in args) {
- parameters[prop] = args[prop];
- }
- }
- var self = this;
- self.loading = true;
- self.downloadComplete = false;
- var passwordNeeded = function passwordNeeded(updatePassword, reason) {
- PasswordPrompt.updatePassword = updatePassword;
- PasswordPrompt.reason = reason;
- PasswordPrompt.show();
- };
- function getDocumentProgress(progressData) {
- self.progress(progressData.loaded / progressData.total);
- }
- PDFJS.getDocument(parameters, pdfDataRangeTransport, passwordNeeded,
- getDocumentProgress).then(
- function getDocumentCallback(pdfDocument) {
- self.load(pdfDocument, scale);
- self.loading = false;
- },
- function getDocumentError(message, exception) {
- var loadingErrorMessage = mozL10n.get('loading_error', null,
- 'An error occurred while loading the PDF.');
- if (exception && exception.name === 'InvalidPDFException') {
- // change error message also for other builds
- loadingErrorMessage = mozL10n.get('invalid_file_error', null,
- 'Invalid or corrupted PDF file.');
- //#if B2G
- // window.alert(loadingErrorMessage);
- // return window.close();
- //#endif
- }
- if (exception && exception.name === 'MissingPDFException') {
- // special message for missing PDF's
- loadingErrorMessage = mozL10n.get('missing_file_error', null,
- 'Missing PDF file.');
- //#if B2G
- // window.alert(loadingErrorMessage);
- // return window.close();
- //#endif
- }
- var moreInfo = {
- message: message
- };
- self.error(loadingErrorMessage, moreInfo);
- self.loading = false;
- }
- );
- },
- download: function pdfViewDownload() {
- function downloadByUrl() {
- downloadManager.downloadUrl(url, filename);
- }
- var url = this.url.split('#')[0];
- var filename = getPDFFileNameFromURL(url);
- var downloadManager = new DownloadManager();
- downloadManager.onerror = function (err) {
- // This error won't really be helpful because it's likely the
- // fallback won't work either (or is already open).
- PDFView.error('PDF failed to download.');
- };
- if (!this.pdfDocument) { // the PDF is not ready yet
- downloadByUrl();
- return;
- }
- if (!this.downloadComplete) { // the PDF is still downloading
- downloadByUrl();
- return;
- }
- this.pdfDocument.getData().then(
- function getDataSuccess(data) {
- var blob = PDFJS.createBlob(data, 'application/pdf');
- downloadManager.download(blob, url, filename);
- },
- downloadByUrl // Error occurred try downloading with just the url.
- ).then(null, downloadByUrl);
- },
- fallback: function pdfViewFallback(featureId) {
- //#if !(FIREFOX || MOZCENTRAL)
- // return;
- //#else
- // // Only trigger the fallback once so we don't spam the user with messages
- // // for one PDF.
- // if (this.fellback)
- // return;
- // this.fellback = true;
- // var url = this.url.split('#')[0];
- // FirefoxCom.request('fallback', { featureId: featureId, url: url },
- // function response(download) {
- // if (!download) {
- // return;
- // }
- // PDFView.download();
- // });
- //#endif
- },
- navigateTo: function pdfViewNavigateTo(dest) {
- var destString = '';
- var self = this;
- var goToDestination = function(destRef) {
- self.pendingRefStr = null;
- // dest array looks like that: <page-ref> </XYZ|FitXXX> <args..>
- var pageNumber = destRef instanceof Object ?
- self.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] :
- (destRef + 1);
- if (pageNumber) {
- if (pageNumber > self.pages.length) {
- pageNumber = self.pages.length;
- }
- var currentPage = self.pages[pageNumber - 1];
- currentPage.scrollIntoView(dest);
- // Update the browsing history.
- PDFHistory.push({ dest: dest, hash: destString, page: pageNumber });
- } else {
- self.pdfDocument.getPageIndex(destRef).then(function (pageIndex) {
- var pageNum = pageIndex + 1;
- self.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] = pageNum;
- goToDestination(destRef);
- });
- }
- };
- this.destinationsPromise.then(function() {
- if (typeof dest === 'string') {
- destString = dest;
- dest = self.destinations[dest];
- }
- if (!(dest instanceof Array)) {
- return; // invalid destination
- }
- goToDestination(dest[0]);
- });
- },
- getDestinationHash: function pdfViewGetDestinationHash(dest) {
- if (typeof dest === 'string') {
- return PDFView.getAnchorUrl('#' + escape(dest));
- }
- if (dest instanceof Array) {
- var destRef = dest[0]; // see navigateTo method for dest format
- var pageNumber = destRef instanceof Object ?
- this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] :
- (destRef + 1);
- if (pageNumber) {
- var pdfOpenParams = PDFView.getAnchorUrl('#page=' + pageNumber);
- var destKind = dest[1];
- if (typeof destKind === 'object' && 'name' in destKind &&
- destKind.name == 'XYZ') {
- var scale = (dest[4] || this.currentScaleValue);
- var scaleNumber = parseFloat(scale);
- if (scaleNumber) {
- scale = scaleNumber * 100;
- }
- pdfOpenParams += '&zoom=' + scale;
- if (dest[2] || dest[3]) {
- pdfOpenParams += ',' + (dest[2] || 0) + ',' + (dest[3] || 0);
- }
- }
- return pdfOpenParams;
- }
- }
- return '';
- },
- /**
- * Prefix the full url on anchor links to make sure that links are resolved
- * relative to the current URL instead of the one defined in <base href>.
- * @param {String} anchor The anchor hash, including the #.
- */
- getAnchorUrl: function getAnchorUrl(anchor) {
- //#if (GENERIC || B2G)
- return anchor;
- //#endif
- //#if (FIREFOX || MOZCENTRAL)
- // return this.url.split('#')[0] + anchor;
- //#endif
- //#if CHROME
- // return location.href.split('#')[0] + anchor;
- //#endif
- },
- /**
- * Show the error box.
- * @param {String} message A message that is human readable.
- * @param {Object} moreInfo (optional) Further information about the error
- * that is more technical. Should have a 'message'
- * and optionally a 'stack' property.
- */
- error: function pdfViewError(message, moreInfo) {
- var moreInfoText = mozL10n.get('error_version_info',
- {version: PDFJS.version || '?', build: PDFJS.build || '?'},
- 'PDF.js v{{version}} (build: {{build}})') + '\n';
- if (moreInfo) {
- moreInfoText +=
- mozL10n.get('error_message', {message: moreInfo.message},
- 'Message: {{message}}');
- if (moreInfo.stack) {
- moreInfoText += '\n' +
- mozL10n.get('error_stack', {stack: moreInfo.stack},
- 'Stack: {{stack}}');
- } else {
- if (moreInfo.filename) {
- moreInfoText += '\n' +
- mozL10n.get('error_file', {file: moreInfo.filename},
- 'File: {{file}}');
- }
- if (moreInfo.lineNumber) {
- moreInfoText += '\n' +
- mozL10n.get('error_line', {line: moreInfo.lineNumber},
- 'Line: {{line}}');
- }
- }
- }
- //#if !(FIREFOX || MOZCENTRAL)
- var errorWrapper = document.getElementById('errorWrapper');
- errorWrapper.removeAttribute('hidden');
- var errorMessage = document.getElementById('errorMessage');
- errorMessage.textContent = message;
- var closeButton = document.getElementById('errorClose');
- closeButton.onclick = function() {
- errorWrapper.setAttribute('hidden', 'true');
- };
- var errorMoreInfo = document.getElementById('errorMoreInfo');
- var moreInfoButton = document.getElementById('errorShowMore');
- var lessInfoButton = document.getElementById('errorShowLess');
- moreInfoButton.onclick = function() {
- errorMoreInfo.removeAttribute('hidden');
- moreInfoButton.setAttribute('hidden', 'true');
- lessInfoButton.removeAttribute('hidden');
- errorMoreInfo.style.height = errorMoreInfo.scrollHeight + 'px';
- };
- lessInfoButton.onclick = function() {
- errorMoreInfo.setAttribute('hidden', 'true');
- moreInfoButton.removeAttribute('hidden');
- lessInfoButton.setAttribute('hidden', 'true');
- };
- moreInfoButton.oncontextmenu = noContextMenuHandler;
- lessInfoButton.oncontextmenu = noContextMenuHandler;
- closeButton.oncontextmenu = noContextMenuHandler;
- moreInfoButton.removeAttribute('hidden');
- lessInfoButton.setAttribute('hidden', 'true');
- errorMoreInfo.value = moreInfoText;
- //#else
- // console.error(message + '\n' + moreInfoText);
- // this.fallback();
- //#endif
- },
- progress: function pdfViewProgress(level) {
- var percent = Math.round(level * 100);
- // When we transition from full request to range requests, it's possible
- // that we discard some of the loaded data. This can cause the loading
- // bar to move backwards. So prevent this by only updating the bar if it
- // increases.
- if (percent > PDFView.loadingBar.percent || isNaN(percent)) {
- PDFView.loadingBar.percent = percent;
- }
- },
- load: function pdfViewLoad(pdfDocument, scale) {
- var self = this;
- var isOnePageRenderedResolved = false;
- var resolveOnePageRendered = null;
- var onePageRendered = new Promise(function (resolve) {
- resolveOnePageRendered = resolve;
- });
- function bindOnAfterDraw(pageView, thumbnailView) {
- // when page is painted, using the image as thumbnail base
- pageView.onAfterDraw = function pdfViewLoadOnAfterDraw() {
- if (!isOnePageRenderedResolved) {
- isOnePageRenderedResolved = true;
- resolveOnePageRendered();
- }
- thumbnailView.setImage(pageView.canvas);
- };
- }
- PDFFindController.reset();
- this.pdfDocument = pdfDocument;
- DocumentProperties.resolveDataAvailable();
- var downloadedPromise = pdfDocument.getDownloadInfo().then(function() {
- self.downloadComplete = true;
- PDFView.loadingBar.hide();
- var outerContainer = document.getElementById('outerContainer');
- outerContainer.classList.remove('loadingInProgress');
- });
- var pagesCount = pdfDocument.numPages;
- var id = pdfDocument.fingerprint;
- document.getElementById('numPages').textContent =
- mozL10n.get('page_of', {pageCount: pagesCount}, 'of {{pageCount}}');
- document.getElementById('pageNumber').max = pagesCount;
- PDFView.documentFingerprint = id;
- var store = PDFView.store = new ViewHistory(id);
- this.pageRotation = 0;
- var pages = this.pages = [];
- var pagesRefMap = this.pagesRefMap = {};
- var thumbnails = this.thumbnails = [];
- var resolvePagesPromise;
- var pagesPromise = new Promise(function (resolve) {
- resolvePagesPromise = resolve;
- });
- this.pagesPromise = pagesPromise;
- var firstPagePromise = pdfDocument.getPage(1);
- var container = document.getElementById('viewer');
- var thumbsView = document.getElementById('thumbnailView');
- // Fetch a single page so we can get a viewport that will be the default
- // viewport for all pages
- firstPagePromise.then(function(pdfPage) {
- var viewport = pdfPage.getViewport((scale || 1.0) * CSS_UNITS);
- for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) {
- var viewportClone = viewport.clone();
- var pageView = new PageView(container, pageNum, scale,
- self.navigateTo.bind(self),
- viewportClone);
- var thumbnailView = new ThumbnailView(thumbsView, pageNum,
- viewportClone);
- bindOnAfterDraw(pageView, thumbnailView);
- pages.push(pageView);
- thumbnails.push(thumbnailView);
- }
- // Fetch all the pages since the viewport is needed before printing
- // starts to create the correct size canvas. Wait until one page is
- // rendered so we don't tie up too many resources early on.
- onePageRendered.then(function () {
- if (!PDFJS.disableAutoFetch) {
- var getPagesLeft = pagesCount;
- for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) {
- pdfDocument.getPage(pageNum).then(function (pageNum, pdfPage) {
- var pageView = pages[pageNum - 1];
- if (!pageView.pdfPage) {
- pageView.setPdfPage(pdfPage);
- }
- var refStr = pdfPage.ref.num + ' ' + pdfPage.ref.gen + ' R';
- pagesRefMap[refStr] = pageNum;
- getPagesLeft--;
- if (!getPagesLeft) {
- resolvePagesPromise();
- }
- }.bind(null, pageNum));
- }
- } else {
- // XXX: Printing is semi-broken with auto fetch disabled.
- resolvePagesPromise();
- }
- });
- downloadedPromise.then(function () {
- var event = document.createEvent('CustomEvent');
- event.initCustomEvent('documentload', true, true, {});
- window.dispatchEvent(event);
- });
- PDFView.loadingBar.setWidth(container);
- PDFFindController.resolveFirstPage();
- // Initialize the browsing history.
- PDFHistory.initialize(self.documentFingerprint);
- });
- // Fetch the necessary preference values.
- var showPreviousViewOnLoad;
- var showPreviousViewOnLoadPromise =
- Preferences.get('showPreviousViewOnLoad').then(function (prefValue) {
- showPreviousViewOnLoad = prefValue;
- });
- var defaultZoomValue;
- var defaultZoomValuePromise =
- Preferences.get('defaultZoomValue').then(function (prefValue) {
- defaultZoomValue = prefValue;
- });
- var storePromise = store.initializedPromise;
- Promise.all([firstPagePromise, storePromise, showPreviousViewOnLoadPromise,
- defaultZoomValuePromise]).then(function resolved() {
- var storedHash = null;
- if (showPreviousViewOnLoad && store.get('exists', false)) {
- var pageNum = store.get('page', '1');
- var zoom = defaultZoomValue || store.get('zoom', PDFView.currentScale);
- var left = store.get('scrollLeft', '0');
- var top = store.get('scrollTop', '0');
- storedHash = 'page=' + pageNum + '&zoom=' + zoom + ',' +
- left + ',' + top;
- } else if (defaultZoomValue) {
- storedHash = 'page=1&zoom=' + defaultZoomValue;
- }
- self.setInitialView(storedHash, scale);
- // Make all navigation keys work on document load,
- // unless the viewer is embedded in a web page.
- if (!self.isViewerEmbedded) {
- self.container.focus();
- //#if (FIREFOX || MOZCENTRAL)
- // self.container.blur();
- //#endif
- }
- }, function rejected(reason) {
- console.error(reason);
- firstPagePromise.then(function () {
- self.setInitialView(null, scale);
- });
- });
- pagesPromise.then(function() {
- if (PDFView.supportsPrinting) {
- pdfDocument.getJavaScript().then(function(javaScript) {
- if (javaScript.length) {
- console.warn('Warning: JavaScript is not supported');
- PDFView.fallback(PDFJS.UNSUPPORTED_FEATURES.javaScript);
- }
- // Hack to support auto printing.
- var regex = /\bprint\s*\(/g;
- for (var i = 0, ii = javaScript.length; i < ii; i++) {
- var js = javaScript[i];
- if (js && regex.test(js)) {
- setTimeout(function() {
- window.print();
- });
- return;
- }
- }
- });
- }
- });
- var destinationsPromise =
- this.destinationsPromise = pdfDocument.getDestinations();
- destinationsPromise.then(function(destinations) {
- self.destinations = destinations;
- });
- // outline depends on destinations and pagesRefMap
- var promises = [pagesPromise, destinationsPromise,
- PDFView.animationStartedPromise];
- Promise.all(promises).then(function() {
- pdfDocument.getOutline().then(function(outline) {
- self.outline = new DocumentOutlineView(outline);
- document.getElementById('viewOutline').disabled = !outline;
- if (outline &&
- self.preferenceSidebarViewOnLoad === SidebarView.OUTLINE) {
- self.switchSidebarView('outline', true);
- }
- });
- pdfDocument.getAttachments().then(function(attachments) {
- self.attachments = new DocumentAttachmentsView(attachments);
- document.getElementById('viewAttachments').disabled = !attachments;
- if (attachments &&
- self.preferenceSidebarViewOnLoad === SidebarView.ATTACHMENTS) {
- self.switchSidebarView('attachments', true);
- }
- });
- });
- if (self.preferenceSidebarViewOnLoad === SidebarView.THUMBS) {
- Promise.all([firstPagePromise, onePageRendered]).then(function () {
- self.switchSidebarView('thumbs', true);
- });
- }
- pdfDocument.getMetadata().then(function(data) {
- var info = data.info, metadata = data.metadata;
- self.documentInfo = info;
- self.metadata = metadata;
- // Provides some basic debug information
- console.log('PDF ' + pdfDocument.fingerprint + ' [' +
- info.PDFFormatVersion + ' ' + (info.Producer || '-').trim() +
- ' / ' + (info.Creator || '-').trim() + ']' +
- ' (PDF.js: ' + (PDFJS.version || '-') +
- (!PDFJS.disableWebGL ? ' [WebGL]' : '') + ')');
- var pdfTitle;
- if (metadata && metadata.has('dc:title')) {
- pdfTitle = metadata.get('dc:title');
- }
- if (!pdfTitle && info && info['Title']) {
- pdfTitle = info['Title'];
- }
- if (pdfTitle) {
- self.setTitle(pdfTitle + ' - ' + document.title);
- }
- if (info.IsAcroFormPresent) {
- console.warn('Warning: AcroForm/XFA is not supported');
- PDFView.fallback(PDFJS.UNSUPPORTED_FEATURES.forms);
- }
- //#if (FIREFOX || MOZCENTRAL)
- // var versionId = String(info.PDFFormatVersion).slice(-1) | 0;
- // var generatorId = 0;
- // var KNOWN_GENERATORS = ["acrobat distiller", "acrobat pdfwritter",
- // "adobe livecycle", "adobe pdf library", "adobe photoshop", "ghostscript",
- // "tcpdf", "cairo", "dvipdfm", "dvips", "pdftex", "pdfkit", "itext",
- // "prince", "quarkxpress", "mac os x", "microsoft", "openoffice", "oracle",
- // "luradocument", "pdf-xchange", "antenna house", "aspose.cells", "fpdf"];
- // var generatorId = 0;
- // if (info.Producer) {
- // KNOWN_GENERATORS.some(function (generator, s, i) {
- // if (generator.indexOf(s) < 0) {
- // return false;
- // }
- // generatorId = i + 1;
- // return true;
- // }.bind(null, info.Producer.toLowerCase()));
- // }
- // var formType = !info.IsAcroFormPresent ? null : info.IsXFAPresent ?
- // 'xfa' : 'acroform';
- // FirefoxCom.request('reportTelemetry', JSON.stringify({
- // type: 'documentInfo',
- // version: versionId,
- // generator: generatorId,
- // formType: formType
- // }));
- //#endif
- });
- },
- setInitialView: function pdfViewSetInitialView(storedHash, scale) {
- // Reset the current scale, as otherwise the page's scale might not get
- // updated if the zoom level stayed the same.
- this.currentScale = 0;
- this.currentScaleValue = null;
- // When opening a new file (when one is already loaded in the viewer):
- // Reset 'currentPageNumber', since otherwise the page's scale will be wrong
- // if 'currentPageNumber' is larger than the number of pages in the file.
- document.getElementById('pageNumber').value = currentPageNumber = 1;
- // Reset the current position when loading a new file,
- // to prevent displaying the wrong position in the document.
- this.currentPosition = null;
- if (PDFHistory.initialDestination) {
- this.navigateTo(PDFHistory.initialDestination);
- PDFHistory.initialDestination = null;
- } else if (this.initialBookmark) {
- this.setHash(this.initialBookmark);
- PDFHistory.push({ hash: this.initialBookmark }, !!this.initialBookmark);
- this.initialBookmark = null;
- } else if (storedHash) {
- this.setHash(storedHash);
- } else if (scale) {
- this.setScale(scale, true);
- this.page = 1;
- }
- if (PDFView.currentScale === UNKNOWN_SCALE) {
- // Scale was not initialized: invalid bookmark or scale was not specified.
- // Setting the default one.
- this.setScale(DEFAULT_SCALE, true);
- }
- },
- renderHighestPriority:
- function pdfViewRenderHighestPriority(currentlyVisiblePages) {
- if (PDFView.idleTimeout) {
- clearTimeout(PDFView.idleTimeout);
- PDFView.idleTimeout = null;
- }
- // Pages have a higher priority than thumbnails, so check them first.
- var visiblePages = currentlyVisiblePages || this.getVisiblePages();
- var pageView = this.getHighestPriority(visiblePages, this.pages,
- this.pageViewScroll.down);
- if (pageView) {
- this.renderView(pageView, 'page');
- return;
- }
- // No pages needed rendering so check thumbnails.
- if (this.sidebarOpen) {
- var visibleThumbs = this.getVisibleThumbs();
- var thumbView = this.getHighestPriority(visibleThumbs,
- this.thumbnails,
- this.thumbnailViewScroll.down);
- if (thumbView) {
- this.renderView(thumbView, 'thumbnail');
- return;
- }
- }
- PDFView.idleTimeout = setTimeout(function () {
- PDFView.cleanup();
- }, CLEANUP_TIMEOUT);
- },
- cleanup: function pdfViewCleanup() {
- for (var i = 0, ii = this.pages.length; i < ii; i++) {
- if (this.pages[i] &&
- this.pages[i].renderingState !== RenderingStates.FINISHED) {
- this.pages[i].reset();
- }
- }
- this.pdfDocument.cleanup();
- },
- getHighestPriority: function pdfViewGetHighestPriority(visible, views,
- scrolledDown) {
- // The state has changed figure out which page has the highest priority to
- // render next (if any).
- // Priority:
- // 1 visible pages
- // 2 if last scrolled down page after the visible pages
- // 2 if last scrolled up page before the visible pages
- var visibleViews = visible.views;
- var numVisible = visibleViews.length;
- if (numVisible === 0) {
- return false;
- }
- for (var i = 0; i < numVisible; ++i) {
- var view = visibleViews[i].view;
- if (!this.isViewFinished(view)) {
- return view;
- }
- }
- // All the visible views have rendered, try to render next/previous pages.
- if (scrolledDown) {
- var nextPageIndex = visible.last.id;
- // ID's start at 1 so no need to add 1.
- if (views[nextPageIndex] && !this.isViewFinished(views[nextPageIndex])) {
- return views[nextPageIndex];
- }
- } else {
- var previousPageIndex = visible.first.id - 2;
- if (views[previousPageIndex] &&
- !this.isViewFinished(views[previousPageIndex])) {
- return views[previousPageIndex];
- }
- }
- // Everything that needs to be rendered has been.
- return false;
- },
- isViewFinished: function pdfViewIsViewFinished(view) {
- return view.renderingState === RenderingStates.FINISHED;
- },
- // Render a page or thumbnail view. This calls the appropriate function based
- // on the views state. If the view is already rendered it will return false.
- renderView: function pdfViewRender(view, type) {
- var state = view.renderingState;
- switch (state) {
- case RenderingStates.FINISHED:
- return false;
- case RenderingStates.PAUSED:
- PDFView.highestPriorityPage = type + view.id;
- view.resume();
- break;
- case RenderingStates.RUNNING:
- PDFView.highestPriorityPage = type + view.id;
- break;
- case RenderingStates.INITIAL:
- PDFView.highestPriorityPage = type + view.id;
- view.draw(this.renderHighestPriority.bind(this));
- break;
- }
- return true;
- },
- setHash: function pdfViewSetHash(hash) {
- if (!hash) {
- return;
- }
- if (hash.indexOf('=') >= 0) {
- var params = PDFView.parseQueryString(hash);
- // borrowing syntax from "Parameters for Opening PDF Files"
- if ('nameddest' in params) {
- PDFHistory.updateNextHashParam(params.nameddest);
- PDFView.navigateTo(params.nameddest);
- return;
- }
- var pageNumber, dest;
- if ('page' in params) {
- pageNumber = (params.page | 0) || 1;
- }
- if ('zoom' in params) {
- var zoomArgs = params.zoom.split(','); // scale,left,top
- // building destination array
- // If the zoom value, it has to get divided by 100. If it is a string,
- // it should stay as it is.
- var zoomArg = zoomArgs[0];
- var zoomArgNumber = parseFloat(zoomArg);
- if (zoomArgNumber) {
- zoomArg = zoomArgNumber / 100;
- }
- dest = [null, {name: 'XYZ'},
- zoomArgs.length > 1 ? (zoomArgs[1] | 0) : null,
- zoomArgs.length > 2 ? (zoomArgs[2] | 0) : null,
- zoomArg];
- }
- if (dest) {
- var currentPage = this.pages[(pageNumber || this.page) - 1];
- currentPage.scrollIntoView(dest);
- } else if (pageNumber) {
- this.page = pageNumber; // simple page
- }
- if ('pagemode' in params) {
- if (params.pagemode === 'thumbs' || params.pagemode === 'bookmarks' ||
- params.pagemode === 'attachments') {
- this.switchSidebarView((params.pagemode === 'bookmarks' ?
- 'outline' : params.pagemode), true);
- } else if (params.pagemode === 'none' && this.sidebarOpen) {
- document.getElementById('sidebarToggle').click();
- }
- }
- } else if (/^\d+$/.test(hash)) { // page number
- this.page = hash;
- } else { // named destination
- PDFHistory.updateNextHashParam(unescape(hash));
- PDFView.navigateTo(unescape(hash));
- }
- },
- switchSidebarView: function pdfViewSwitchSidebarView(view, openSidebar) {
- if (openSidebar && !this.sidebarOpen) {
- document.getElementById('sidebarToggle').click();
- }
- var thumbsView = document.getElementById('thumbnailView');
- var outlineView = document.getElementById('outlineView');
- var attachmentsView = document.getElementById('attachmentsView');
- var thumbsButton = document.getElementById('viewThumbnail');
- var outlineButton = document.getElementById('viewOutline');
- var attachmentsButton = document.getElementById('viewAttachments');
- switch (view) {
- case 'thumbs':
- var wasAnotherViewVisible = thumbsView.classList.contains('hidden');
- thumbsButton.classList.add('toggled');
- outlineButton.classList.remove('toggled');
- attachmentsButton.classList.remove('toggled');
- thumbsView.classList.remove('hidden');
- outlineView.classList.add('hidden');
- attachmentsView.classList.add('hidden');
- PDFView.renderHighestPriority();
- if (wasAnotherViewVisible) {
- // Ensure that the thumbnail of the current page is visible
- // when switching from another view.
- scrollIntoView(document.getElementById('thumbnailContainer' +
- this.page));
- }
- break;
- case 'outline':
- thumbsButton.classList.remove('toggled');
- outlineButton.classList.add('toggled');
- attachmentsButton.classList.remove('toggled');
- thumbsView.classList.add('hidden');
- outlineView.classList.remove('hidden');
- attachmentsView.classList.add('hidden');
- if (outlineButton.getAttribute('disabled')) {
- return;
- }
- break;
- case 'attachments':
- thumbsButton.classList.remove('toggled');
- outlineButton.classList.remove('toggled');
- attachmentsButton.classList.add('toggled');
- thumbsView.classList.add('hidden');
- outlineView.classList.add('hidden');
- attachmentsView.classList.remove('hidden');
- if (attachmentsButton.getAttribute('disabled')) {
- return;
- }
- break;
- }
- },
- getVisiblePages: function pdfViewGetVisiblePages() {
- if (!PresentationMode.active) {
- return this.getVisibleElements(this.container, this.pages, true);
- } else {
- // The algorithm in getVisibleElements doesn't work in all browsers and
- // configurations when presentation mode is active.
- var visible = [];
- var currentPage = this.pages[this.page - 1];
- visible.push({ id: currentPage.id, view: currentPage });
- return { first: currentPage, last: currentPage, views: visible };
- }
- },
- getVisibleThumbs: function pdfViewGetVisibleThumbs() {
- return this.getVisibleElements(this.thumbnailContainer, this.thumbnails);
- },
- // Generic helper to…