dd0fe10a52
With the changes in PR 7289, we no longer dispatch a 'pagechanging' event on load. Since most PDF documents open on the first page, this means that the `previous` and `firstPage` buttons are no longer correctly disabled. To avoid this, this patch moves the code that updates various UI toolbar state into one method, which is then called on document initialization and from the various existing event handling functions.
816 lines
26 KiB
JavaScript
816 lines
26 KiB
JavaScript
/* Copyright 2014 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.
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
(function (root, factory) {
|
|
if (typeof define === 'function' && define.amd) {
|
|
define('pdfjs-web/pdf_viewer', ['exports', 'pdfjs-web/ui_utils',
|
|
'pdfjs-web/pdf_page_view', 'pdfjs-web/pdf_rendering_queue',
|
|
'pdfjs-web/text_layer_builder', 'pdfjs-web/annotation_layer_builder',
|
|
'pdfjs-web/pdf_link_service', 'pdfjs-web/dom_events', 'pdfjs-web/pdfjs'],
|
|
factory);
|
|
} else if (typeof exports !== 'undefined') {
|
|
factory(exports, require('./ui_utils.js'), require('./pdf_page_view.js'),
|
|
require('./pdf_rendering_queue.js'), require('./text_layer_builder.js'),
|
|
require('./annotation_layer_builder.js'),
|
|
require('./pdf_link_service.js'), require('./dom_events.js'),
|
|
require('./pdfjs.js'));
|
|
} else {
|
|
factory((root.pdfjsWebPDFViewer = {}), root.pdfjsWebUIUtils,
|
|
root.pdfjsWebPDFPageView, root.pdfjsWebPDFRenderingQueue,
|
|
root.pdfjsWebTextLayerBuilder, root.pdfjsWebAnnotationLayerBuilder,
|
|
root.pdfjsWebPDFLinkService, root.pdfjsWebDOMEvents, root.pdfjsWebPDFJS);
|
|
}
|
|
}(this, function (exports, uiUtils, pdfPageView, pdfRenderingQueue,
|
|
textLayerBuilder, annotationLayerBuilder, pdfLinkService,
|
|
domEvents, pdfjsLib) {
|
|
|
|
var UNKNOWN_SCALE = uiUtils.UNKNOWN_SCALE;
|
|
var SCROLLBAR_PADDING = uiUtils.SCROLLBAR_PADDING;
|
|
var VERTICAL_PADDING = uiUtils.VERTICAL_PADDING;
|
|
var MAX_AUTO_SCALE = uiUtils.MAX_AUTO_SCALE;
|
|
var CSS_UNITS = uiUtils.CSS_UNITS;
|
|
var DEFAULT_SCALE = uiUtils.DEFAULT_SCALE;
|
|
var DEFAULT_SCALE_VALUE = uiUtils.DEFAULT_SCALE_VALUE;
|
|
var scrollIntoView = uiUtils.scrollIntoView;
|
|
var watchScroll = uiUtils.watchScroll;
|
|
var getVisibleElements = uiUtils.getVisibleElements;
|
|
var PDFPageView = pdfPageView.PDFPageView;
|
|
var RenderingStates = pdfRenderingQueue.RenderingStates;
|
|
var PDFRenderingQueue = pdfRenderingQueue.PDFRenderingQueue;
|
|
var TextLayerBuilder = textLayerBuilder.TextLayerBuilder;
|
|
var AnnotationLayerBuilder = annotationLayerBuilder.AnnotationLayerBuilder;
|
|
var SimpleLinkService = pdfLinkService.SimpleLinkService;
|
|
|
|
var PresentationModeState = {
|
|
UNKNOWN: 0,
|
|
NORMAL: 1,
|
|
CHANGING: 2,
|
|
FULLSCREEN: 3,
|
|
};
|
|
|
|
var DEFAULT_CACHE_SIZE = 10;
|
|
|
|
/**
|
|
* @typedef {Object} PDFViewerOptions
|
|
* @property {HTMLDivElement} container - The container for the viewer element.
|
|
* @property {HTMLDivElement} viewer - (optional) The viewer element.
|
|
* @property {EventBus} eventBus - The application event bus.
|
|
* @property {IPDFLinkService} linkService - The navigation/linking service.
|
|
* @property {DownloadManager} downloadManager - (optional) The download
|
|
* manager component.
|
|
* @property {PDFRenderingQueue} renderingQueue - (optional) The rendering
|
|
* queue object.
|
|
* @property {boolean} removePageBorders - (optional) Removes the border shadow
|
|
* around the pages. The default is false.
|
|
*/
|
|
|
|
/**
|
|
* Simple viewer control to display PDF content/pages.
|
|
* @class
|
|
* @implements {IRenderableView}
|
|
*/
|
|
var PDFViewer = (function pdfViewer() {
|
|
function PDFPageViewBuffer(size) {
|
|
var data = [];
|
|
this.push = function cachePush(view) {
|
|
var i = data.indexOf(view);
|
|
if (i >= 0) {
|
|
data.splice(i, 1);
|
|
}
|
|
data.push(view);
|
|
if (data.length > size) {
|
|
data.shift().destroy();
|
|
}
|
|
};
|
|
this.resize = function (newSize) {
|
|
size = newSize;
|
|
while (data.length > size) {
|
|
data.shift().destroy();
|
|
}
|
|
};
|
|
}
|
|
|
|
function isSameScale(oldScale, newScale) {
|
|
if (newScale === oldScale) {
|
|
return true;
|
|
}
|
|
if (Math.abs(newScale - oldScale) < 1e-15) {
|
|
// Prevent unnecessary re-rendering of all pages when the scale
|
|
// changes only because of limited numerical precision.
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @constructs PDFViewer
|
|
* @param {PDFViewerOptions} options
|
|
*/
|
|
function PDFViewer(options) {
|
|
this.container = options.container;
|
|
this.viewer = options.viewer || options.container.firstElementChild;
|
|
this.eventBus = options.eventBus || domEvents.getGlobalEventBus();
|
|
this.linkService = options.linkService || new SimpleLinkService();
|
|
this.downloadManager = options.downloadManager || null;
|
|
this.removePageBorders = options.removePageBorders || false;
|
|
|
|
this.defaultRenderingQueue = !options.renderingQueue;
|
|
if (this.defaultRenderingQueue) {
|
|
// Custom rendering queue is not specified, using default one
|
|
this.renderingQueue = new PDFRenderingQueue();
|
|
this.renderingQueue.setViewer(this);
|
|
} else {
|
|
this.renderingQueue = options.renderingQueue;
|
|
}
|
|
|
|
this.scroll = watchScroll(this.container, this._scrollUpdate.bind(this));
|
|
this.presentationModeState = PresentationModeState.UNKNOWN;
|
|
this._resetView();
|
|
|
|
if (this.removePageBorders) {
|
|
this.viewer.classList.add('removePageBorders');
|
|
}
|
|
}
|
|
|
|
PDFViewer.prototype = /** @lends PDFViewer.prototype */{
|
|
get pagesCount() {
|
|
return this._pages.length;
|
|
},
|
|
|
|
getPageView: function (index) {
|
|
return this._pages[index];
|
|
},
|
|
|
|
get currentPageNumber() {
|
|
return this._currentPageNumber;
|
|
},
|
|
|
|
set currentPageNumber(val) {
|
|
if (!this.pdfDocument) {
|
|
this._currentPageNumber = val;
|
|
return;
|
|
}
|
|
this._setCurrentPageNumber(val);
|
|
// The intent can be to just reset a scroll position and/or scale.
|
|
this._resetCurrentPageView();
|
|
},
|
|
|
|
_setCurrentPageNumber: function pdfViewer_setCurrentPageNumber(val) {
|
|
if (this._currentPageNumber === val) {
|
|
return;
|
|
}
|
|
var arg;
|
|
if (!(0 < val && val <= this.pagesCount)) {
|
|
arg = {
|
|
source: this,
|
|
pageNumber: this._currentPageNumber,
|
|
previousPageNumber: val
|
|
};
|
|
this.eventBus.dispatch('pagechanging', arg);
|
|
this.eventBus.dispatch('pagechange', arg);
|
|
return;
|
|
}
|
|
|
|
arg = {
|
|
source: this,
|
|
pageNumber: val,
|
|
previousPageNumber: this._currentPageNumber
|
|
};
|
|
this._currentPageNumber = val;
|
|
this.eventBus.dispatch('pagechanging', arg);
|
|
this.eventBus.dispatch('pagechange', arg);
|
|
},
|
|
|
|
/**
|
|
* @returns {number}
|
|
*/
|
|
get currentScale() {
|
|
return this._currentScale !== UNKNOWN_SCALE ? this._currentScale :
|
|
DEFAULT_SCALE;
|
|
},
|
|
|
|
/**
|
|
* @param {number} val - Scale of the pages in percents.
|
|
*/
|
|
set currentScale(val) {
|
|
if (isNaN(val)) {
|
|
throw new Error('Invalid numeric scale');
|
|
}
|
|
if (!this.pdfDocument) {
|
|
this._currentScale = val;
|
|
this._currentScaleValue = val !== UNKNOWN_SCALE ? val.toString() : null;
|
|
return;
|
|
}
|
|
this._setScale(val, false);
|
|
},
|
|
|
|
/**
|
|
* @returns {string}
|
|
*/
|
|
get currentScaleValue() {
|
|
return this._currentScaleValue;
|
|
},
|
|
|
|
/**
|
|
* @param val - The scale of the pages (in percent or predefined value).
|
|
*/
|
|
set currentScaleValue(val) {
|
|
if (!this.pdfDocument) {
|
|
this._currentScale = isNaN(val) ? UNKNOWN_SCALE : val;
|
|
this._currentScaleValue = val.toString();
|
|
return;
|
|
}
|
|
this._setScale(val, false);
|
|
},
|
|
|
|
/**
|
|
* @returns {number}
|
|
*/
|
|
get pagesRotation() {
|
|
return this._pagesRotation;
|
|
},
|
|
|
|
/**
|
|
* @param {number} rotation - The rotation of the pages (0, 90, 180, 270).
|
|
*/
|
|
set pagesRotation(rotation) {
|
|
this._pagesRotation = rotation;
|
|
|
|
for (var i = 0, l = this._pages.length; i < l; i++) {
|
|
var pageView = this._pages[i];
|
|
pageView.update(pageView.scale, rotation);
|
|
}
|
|
|
|
this._setScale(this._currentScaleValue, true);
|
|
|
|
if (this.defaultRenderingQueue) {
|
|
this.update();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @param pdfDocument {PDFDocument}
|
|
*/
|
|
setDocument: function (pdfDocument) {
|
|
if (this.pdfDocument) {
|
|
this._resetView();
|
|
}
|
|
|
|
this.pdfDocument = pdfDocument;
|
|
if (!pdfDocument) {
|
|
return;
|
|
}
|
|
|
|
var pagesCount = pdfDocument.numPages;
|
|
var self = this;
|
|
|
|
var resolvePagesPromise;
|
|
var pagesPromise = new Promise(function (resolve) {
|
|
resolvePagesPromise = resolve;
|
|
});
|
|
this.pagesPromise = pagesPromise;
|
|
pagesPromise.then(function () {
|
|
self.eventBus.dispatch('pagesloaded', {
|
|
source: self,
|
|
pagesCount: pagesCount
|
|
});
|
|
});
|
|
|
|
var isOnePageRenderedResolved = false;
|
|
var resolveOnePageRendered = null;
|
|
var onePageRendered = new Promise(function (resolve) {
|
|
resolveOnePageRendered = resolve;
|
|
});
|
|
this.onePageRendered = onePageRendered;
|
|
|
|
var bindOnAfterAndBeforeDraw = function (pageView) {
|
|
pageView.onBeforeDraw = function pdfViewLoadOnBeforeDraw() {
|
|
// Add the page to the buffer at the start of drawing. That way it can
|
|
// be evicted from the buffer and destroyed even if we pause its
|
|
// rendering.
|
|
self._buffer.push(this);
|
|
};
|
|
// when page is painted, using the image as thumbnail base
|
|
pageView.onAfterDraw = function pdfViewLoadOnAfterDraw() {
|
|
if (!isOnePageRenderedResolved) {
|
|
isOnePageRenderedResolved = true;
|
|
resolveOnePageRendered();
|
|
}
|
|
};
|
|
};
|
|
|
|
var firstPagePromise = pdfDocument.getPage(1);
|
|
this.firstPagePromise = firstPagePromise;
|
|
|
|
// Fetch a single page so we can get a viewport that will be the default
|
|
// viewport for all pages
|
|
return firstPagePromise.then(function(pdfPage) {
|
|
var scale = this.currentScale;
|
|
var viewport = pdfPage.getViewport(scale * CSS_UNITS);
|
|
for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) {
|
|
var textLayerFactory = null;
|
|
if (!pdfjsLib.PDFJS.disableTextLayer) {
|
|
textLayerFactory = this;
|
|
}
|
|
var pageView = new PDFPageView({
|
|
container: this.viewer,
|
|
eventBus: this.eventBus,
|
|
id: pageNum,
|
|
scale: scale,
|
|
defaultViewport: viewport.clone(),
|
|
renderingQueue: this.renderingQueue,
|
|
textLayerFactory: textLayerFactory,
|
|
annotationLayerFactory: this
|
|
});
|
|
bindOnAfterAndBeforeDraw(pageView);
|
|
this._pages.push(pageView);
|
|
}
|
|
|
|
var linkService = this.linkService;
|
|
|
|
// 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 (!pdfjsLib.PDFJS.disableAutoFetch) {
|
|
var getPagesLeft = pagesCount;
|
|
for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) {
|
|
pdfDocument.getPage(pageNum).then(function (pageNum, pdfPage) {
|
|
var pageView = self._pages[pageNum - 1];
|
|
if (!pageView.pdfPage) {
|
|
pageView.setPdfPage(pdfPage);
|
|
}
|
|
linkService.cachePageRef(pageNum, pdfPage.ref);
|
|
getPagesLeft--;
|
|
if (!getPagesLeft) {
|
|
resolvePagesPromise();
|
|
}
|
|
}.bind(null, pageNum));
|
|
}
|
|
} else {
|
|
// XXX: Printing is semi-broken with auto fetch disabled.
|
|
resolvePagesPromise();
|
|
}
|
|
});
|
|
|
|
self.eventBus.dispatch('pagesinit', {source: self});
|
|
|
|
if (this.defaultRenderingQueue) {
|
|
this.update();
|
|
}
|
|
|
|
if (this.findController) {
|
|
this.findController.resolveFirstPage();
|
|
}
|
|
}.bind(this));
|
|
},
|
|
|
|
_resetView: function () {
|
|
this._pages = [];
|
|
this._currentPageNumber = 1;
|
|
this._currentScale = UNKNOWN_SCALE;
|
|
this._currentScaleValue = null;
|
|
this._buffer = new PDFPageViewBuffer(DEFAULT_CACHE_SIZE);
|
|
this._location = null;
|
|
this._pagesRotation = 0;
|
|
this._pagesRequests = [];
|
|
|
|
var container = this.viewer;
|
|
while (container.hasChildNodes()) {
|
|
container.removeChild(container.lastChild);
|
|
}
|
|
},
|
|
|
|
_scrollUpdate: function PDFViewer_scrollUpdate() {
|
|
if (this.pagesCount === 0) {
|
|
return;
|
|
}
|
|
this.update();
|
|
for (var i = 0, ii = this._pages.length; i < ii; i++) {
|
|
this._pages[i].updatePosition();
|
|
}
|
|
},
|
|
|
|
_setScaleDispatchEvent: function pdfViewer_setScaleDispatchEvent(
|
|
newScale, newValue, preset) {
|
|
var arg = {
|
|
source: this,
|
|
scale: newScale,
|
|
presetValue: preset ? newValue : undefined
|
|
};
|
|
this.eventBus.dispatch('scalechanging', arg);
|
|
this.eventBus.dispatch('scalechange', arg);
|
|
},
|
|
|
|
_setScaleUpdatePages: function pdfViewer_setScaleUpdatePages(
|
|
newScale, newValue, noScroll, preset) {
|
|
this._currentScaleValue = newValue.toString();
|
|
|
|
if (isSameScale(this._currentScale, newScale)) {
|
|
if (preset) {
|
|
this._setScaleDispatchEvent(newScale, newValue, true);
|
|
}
|
|
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._currentPageNumber, dest;
|
|
if (this._location && !pdfjsLib.PDFJS.ignoreCurrentPositionOnZoom &&
|
|
!(this.isInPresentationMode || this.isChangingPresentationMode)) {
|
|
page = this._location.pageNumber;
|
|
dest = [null, { name: 'XYZ' }, this._location.left,
|
|
this._location.top, null];
|
|
}
|
|
this.scrollPageIntoView(page, dest);
|
|
}
|
|
|
|
this._setScaleDispatchEvent(newScale, newValue, preset);
|
|
|
|
if (this.defaultRenderingQueue) {
|
|
this.update();
|
|
}
|
|
},
|
|
|
|
_setScale: function pdfViewer_setScale(value, noScroll) {
|
|
var scale = parseFloat(value);
|
|
|
|
if (scale > 0) {
|
|
this._setScaleUpdatePages(scale, value, noScroll, false);
|
|
} else {
|
|
var currentPage = this._pages[this._currentPageNumber - 1];
|
|
if (!currentPage) {
|
|
return;
|
|
}
|
|
var hPadding = (this.isInPresentationMode || this.removePageBorders) ?
|
|
0 : SCROLLBAR_PADDING;
|
|
var vPadding = (this.isInPresentationMode || this.removePageBorders) ?
|
|
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':
|
|
var isLandscape = (currentPage.width > currentPage.height);
|
|
// For pages in landscape mode, fit the page height to the viewer
|
|
// *unless* the page would thus become too wide to fit horizontally.
|
|
var horizontalScale = isLandscape ?
|
|
Math.min(pageHeightScale, pageWidthScale) : pageWidthScale;
|
|
scale = Math.min(MAX_AUTO_SCALE, horizontalScale);
|
|
break;
|
|
default:
|
|
console.error('pdfViewSetScale: \'' + value +
|
|
'\' is an unknown zoom value.');
|
|
return;
|
|
}
|
|
this._setScaleUpdatePages(scale, value, noScroll, true);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Refreshes page view: scrolls to the current page and updates the scale.
|
|
*/
|
|
_resetCurrentPageView: function () {
|
|
if (this.isInPresentationMode) {
|
|
// Fixes the case when PDF has different page sizes.
|
|
this._setScale(this._currentScaleValue, true);
|
|
}
|
|
|
|
var pageView = this._pages[this._currentPageNumber - 1];
|
|
scrollIntoView(pageView.div);
|
|
},
|
|
|
|
/**
|
|
* Scrolls page into view.
|
|
* @param {number} pageNumber
|
|
* @param {Array} dest - (optional) original PDF destination array:
|
|
* <page-ref> </XYZ|FitXXX> <args..>
|
|
*/
|
|
scrollPageIntoView: function PDFViewer_scrollPageIntoView(pageNumber,
|
|
dest) {
|
|
if (!this.pdfDocument) {
|
|
return;
|
|
}
|
|
|
|
if (this.isInPresentationMode || !dest) {
|
|
this._setCurrentPageNumber(pageNumber);
|
|
this._resetCurrentPageView();
|
|
return;
|
|
}
|
|
|
|
var pageView = this._pages[pageNumber - 1];
|
|
var x = 0, y = 0;
|
|
var width = 0, height = 0, widthScale, heightScale;
|
|
var changeOrientation = (pageView.rotation % 180 === 0 ? false : true);
|
|
var pageWidth = (changeOrientation ? pageView.height : pageView.width) /
|
|
pageView.scale / CSS_UNITS;
|
|
var pageHeight = (changeOrientation ? pageView.width : pageView.height) /
|
|
pageView.scale / CSS_UNITS;
|
|
var scale = 0;
|
|
switch (dest[1].name) {
|
|
case 'XYZ':
|
|
x = dest[2];
|
|
y = dest[3];
|
|
scale = dest[4];
|
|
// If x and/or y coordinates are not supplied, default to
|
|
// _top_ left of the page (not the obvious bottom left,
|
|
// since aligning the bottom of the intended page with the
|
|
// top of the window is rarely helpful).
|
|
x = x !== null ? x : 0;
|
|
y = y !== null ? y : pageHeight;
|
|
break;
|
|
case 'Fit':
|
|
case 'FitB':
|
|
scale = 'page-fit';
|
|
break;
|
|
case 'FitH':
|
|
case 'FitBH':
|
|
y = dest[2];
|
|
scale = 'page-width';
|
|
// According to the PDF spec, section 12.3.2.2, a `null` value in the
|
|
// parameter should maintain the position relative to the new page.
|
|
if (y === null && this._location) {
|
|
x = this._location.left;
|
|
y = this._location.top;
|
|
}
|
|
break;
|
|
case 'FitV':
|
|
case 'FitBV':
|
|
x = dest[2];
|
|
width = pageWidth;
|
|
height = pageHeight;
|
|
scale = 'page-height';
|
|
break;
|
|
case 'FitR':
|
|
x = dest[2];
|
|
y = dest[3];
|
|
width = dest[4] - x;
|
|
height = dest[5] - y;
|
|
var hPadding = this.removePageBorders ? 0 : SCROLLBAR_PADDING;
|
|
var vPadding = this.removePageBorders ? 0 : VERTICAL_PADDING;
|
|
|
|
widthScale = (this.container.clientWidth - hPadding) /
|
|
width / CSS_UNITS;
|
|
heightScale = (this.container.clientHeight - vPadding) /
|
|
height / CSS_UNITS;
|
|
scale = Math.min(Math.abs(widthScale), Math.abs(heightScale));
|
|
break;
|
|
default:
|
|
console.error('PDFViewer_scrollPageIntoView: \'' + dest[1].name +
|
|
'\' is not a valid destination type.');
|
|
return;
|
|
}
|
|
|
|
if (scale && scale !== this._currentScale) {
|
|
this.currentScaleValue = scale;
|
|
} else if (this._currentScale === UNKNOWN_SCALE) {
|
|
this.currentScaleValue = DEFAULT_SCALE_VALUE;
|
|
}
|
|
|
|
if (scale === 'page-fit' && !dest[4]) {
|
|
scrollIntoView(pageView.div);
|
|
return;
|
|
}
|
|
|
|
var boundingRect = [
|
|
pageView.viewport.convertToViewportPoint(x, y),
|
|
pageView.viewport.convertToViewportPoint(x + width, y + height)
|
|
];
|
|
var left = Math.min(boundingRect[0][0], boundingRect[1][0]);
|
|
var top = Math.min(boundingRect[0][1], boundingRect[1][1]);
|
|
|
|
scrollIntoView(pageView.div, { left: left, top: top });
|
|
},
|
|
|
|
_updateLocation: function (firstPage) {
|
|
var currentScale = this._currentScale;
|
|
var currentScaleValue = this._currentScaleValue;
|
|
var normalizedScaleValue =
|
|
parseFloat(currentScaleValue) === currentScale ?
|
|
Math.round(currentScale * 10000) / 100 : currentScaleValue;
|
|
|
|
var pageNumber = firstPage.id;
|
|
var pdfOpenParams = '#page=' + pageNumber;
|
|
pdfOpenParams += '&zoom=' + normalizedScaleValue;
|
|
var currentPageView = this._pages[pageNumber - 1];
|
|
var container = this.container;
|
|
var topLeft = currentPageView.getPagePoint(
|
|
(container.scrollLeft - firstPage.x),
|
|
(container.scrollTop - firstPage.y));
|
|
var intLeft = Math.round(topLeft[0]);
|
|
var intTop = Math.round(topLeft[1]);
|
|
pdfOpenParams += ',' + intLeft + ',' + intTop;
|
|
|
|
this._location = {
|
|
pageNumber: pageNumber,
|
|
scale: normalizedScaleValue,
|
|
top: intTop,
|
|
left: intLeft,
|
|
pdfOpenParams: pdfOpenParams
|
|
};
|
|
},
|
|
|
|
update: function PDFViewer_update() {
|
|
var visible = this._getVisiblePages();
|
|
var visiblePages = visible.views;
|
|
if (visiblePages.length === 0) {
|
|
return;
|
|
}
|
|
|
|
var suggestedCacheSize = Math.max(DEFAULT_CACHE_SIZE,
|
|
2 * visiblePages.length + 1);
|
|
this._buffer.resize(suggestedCacheSize);
|
|
|
|
this.renderingQueue.renderHighestPriority(visible);
|
|
|
|
var currentId = this._currentPageNumber;
|
|
var firstPage = visible.first;
|
|
|
|
for (var i = 0, ii = visiblePages.length, stillFullyVisible = false;
|
|
i < ii; ++i) {
|
|
var page = visiblePages[i];
|
|
|
|
if (page.percent < 100) {
|
|
break;
|
|
}
|
|
if (page.id === currentId) {
|
|
stillFullyVisible = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!stillFullyVisible) {
|
|
currentId = visiblePages[0].id;
|
|
}
|
|
|
|
if (!this.isInPresentationMode) {
|
|
this._setCurrentPageNumber(currentId);
|
|
}
|
|
|
|
this._updateLocation(firstPage);
|
|
|
|
this.eventBus.dispatch('updateviewarea', {
|
|
source: this,
|
|
location: this._location
|
|
});
|
|
},
|
|
|
|
containsElement: function (element) {
|
|
return this.container.contains(element);
|
|
},
|
|
|
|
focus: function () {
|
|
this.container.focus();
|
|
},
|
|
|
|
get isInPresentationMode() {
|
|
return this.presentationModeState === PresentationModeState.FULLSCREEN;
|
|
},
|
|
|
|
get isChangingPresentationMode() {
|
|
return this.presentationModeState === PresentationModeState.CHANGING;
|
|
},
|
|
|
|
get isHorizontalScrollbarEnabled() {
|
|
return (this.isInPresentationMode ?
|
|
false : (this.container.scrollWidth > this.container.clientWidth));
|
|
},
|
|
|
|
_getVisiblePages: function () {
|
|
if (!this.isInPresentationMode) {
|
|
return 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._currentPageNumber - 1];
|
|
visible.push({ id: currentPage.id, view: currentPage });
|
|
return { first: currentPage, last: currentPage, views: visible };
|
|
}
|
|
},
|
|
|
|
cleanup: function () {
|
|
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();
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @param {PDFPageView} pageView
|
|
* @returns {PDFPage}
|
|
* @private
|
|
*/
|
|
_ensurePdfPageLoaded: function (pageView) {
|
|
if (pageView.pdfPage) {
|
|
return Promise.resolve(pageView.pdfPage);
|
|
}
|
|
var pageNumber = pageView.id;
|
|
if (this._pagesRequests[pageNumber]) {
|
|
return this._pagesRequests[pageNumber];
|
|
}
|
|
var promise = this.pdfDocument.getPage(pageNumber).then(
|
|
function (pdfPage) {
|
|
pageView.setPdfPage(pdfPage);
|
|
this._pagesRequests[pageNumber] = null;
|
|
return pdfPage;
|
|
}.bind(this));
|
|
this._pagesRequests[pageNumber] = promise;
|
|
return promise;
|
|
},
|
|
|
|
forceRendering: function (currentlyVisiblePages) {
|
|
var visiblePages = currentlyVisiblePages || this._getVisiblePages();
|
|
var pageView = this.renderingQueue.getHighestPriority(visiblePages,
|
|
this._pages,
|
|
this.scroll.down);
|
|
if (pageView) {
|
|
this._ensurePdfPageLoaded(pageView).then(function () {
|
|
this.renderingQueue.renderView(pageView);
|
|
}.bind(this));
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
getPageTextContent: function (pageIndex) {
|
|
return this.pdfDocument.getPage(pageIndex + 1).then(function (page) {
|
|
return page.getTextContent({ normalizeWhitespace: true });
|
|
});
|
|
},
|
|
|
|
/**
|
|
* @param {HTMLDivElement} textLayerDiv
|
|
* @param {number} pageIndex
|
|
* @param {PageViewport} viewport
|
|
* @returns {TextLayerBuilder}
|
|
*/
|
|
createTextLayerBuilder: function (textLayerDiv, pageIndex, viewport) {
|
|
return new TextLayerBuilder({
|
|
textLayerDiv: textLayerDiv,
|
|
eventBus: this.eventBus,
|
|
pageIndex: pageIndex,
|
|
viewport: viewport,
|
|
findController: this.isInPresentationMode ? null : this.findController
|
|
});
|
|
},
|
|
|
|
/**
|
|
* @param {HTMLDivElement} pageDiv
|
|
* @param {PDFPage} pdfPage
|
|
* @returns {AnnotationLayerBuilder}
|
|
*/
|
|
createAnnotationLayerBuilder: function (pageDiv, pdfPage) {
|
|
return new AnnotationLayerBuilder({
|
|
pageDiv: pageDiv,
|
|
pdfPage: pdfPage,
|
|
linkService: this.linkService,
|
|
downloadManager: this.downloadManager
|
|
});
|
|
},
|
|
|
|
setFindController: function (findController) {
|
|
this.findController = findController;
|
|
},
|
|
};
|
|
|
|
return PDFViewer;
|
|
})();
|
|
|
|
exports.PresentationModeState = PresentationModeState;
|
|
exports.PDFViewer = PDFViewer;
|
|
}));
|