d0c071a40d
Currently if the zoom level is reset multiple times in a row, i.e. by pressing <kbd>Ctrl</kbd>+<kbd>0</kbd>, the pages can be re-rendered each time even though their size shouldn't change. Whether this happens can depend on the size of the viewer, but documents with pages in landscape mode seem to be very susceptible to this. (An example is: https://wiki.mozilla.org/images/5/55/MobileOpportunity.pdf.) This can also effect documents with pages in portrait mode, when they are displayed in Presentation Mode. The reason for this unnecessary re-rendering is that due to limited numerical precision, the new scale value may change in *only* the last decimal place.
811 lines
25 KiB
JavaScript
811 lines
25 KiB
JavaScript
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
|
/* 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.
|
|
*/
|
|
/*globals watchScroll, PDFPageView, UNKNOWN_SCALE,
|
|
SCROLLBAR_PADDING, VERTICAL_PADDING, MAX_AUTO_SCALE, CSS_UNITS,
|
|
DEFAULT_SCALE, scrollIntoView, getVisibleElements, RenderingStates,
|
|
PDFJS, Promise, TextLayerBuilder, PDFRenderingQueue,
|
|
AnnotationsLayerBuilder, DEFAULT_SCALE_VALUE */
|
|
|
|
'use strict';
|
|
|
|
var PresentationModeState = {
|
|
UNKNOWN: 0,
|
|
NORMAL: 1,
|
|
CHANGING: 2,
|
|
FULLSCREEN: 3,
|
|
};
|
|
|
|
var IGNORE_CURRENT_POSITION_ON_ZOOM = false;
|
|
var DEFAULT_CACHE_SIZE = 10;
|
|
|
|
//#include pdf_rendering_queue.js
|
|
//#include pdf_page_view.js
|
|
//#include text_layer_builder.js
|
|
//#include annotations_layer_builder.js
|
|
|
|
/**
|
|
* @typedef {Object} PDFViewerOptions
|
|
* @property {HTMLDivElement} container - The container for the viewer element.
|
|
* @property {HTMLDivElement} viewer - (optional) The viewer element.
|
|
* @property {IPDFLinkService} linkService - The navigation/linking service.
|
|
* @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.linkService = options.linkService || new SimpleLinkService(this);
|
|
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.updateInProgress = false;
|
|
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;
|
|
}
|
|
|
|
var event = document.createEvent('UIEvents');
|
|
event.initUIEvent('pagechange', true, true, window, 0);
|
|
event.updateInProgress = this.updateInProgress;
|
|
|
|
if (!(0 < val && val <= this.pagesCount)) {
|
|
event.pageNumber = this._currentPageNumber;
|
|
event.previousPageNumber = val;
|
|
this.container.dispatchEvent(event);
|
|
return;
|
|
}
|
|
|
|
event.previousPageNumber = this._currentPageNumber;
|
|
this._currentPageNumber = val;
|
|
event.pageNumber = val;
|
|
this.container.dispatchEvent(event);
|
|
|
|
// Check if the caller is `PDFViewer_update`, to avoid breaking scrolling.
|
|
if (this.updateInProgress) {
|
|
return;
|
|
}
|
|
this.scrollPageIntoView(val);
|
|
},
|
|
|
|
/**
|
|
* @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;
|
|
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);
|
|
},
|
|
|
|
/**
|
|
* @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 () {
|
|
var event = document.createEvent('CustomEvent');
|
|
event.initCustomEvent('pagesloaded', true, true, {
|
|
pagesCount: pagesCount
|
|
});
|
|
self.container.dispatchEvent(event);
|
|
});
|
|
|
|
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 (!PDFJS.disableTextLayer) {
|
|
textLayerFactory = this;
|
|
}
|
|
var pageView = new PDFPageView({
|
|
container: this.viewer,
|
|
id: pageNum,
|
|
scale: scale,
|
|
defaultViewport: viewport.clone(),
|
|
renderingQueue: this.renderingQueue,
|
|
textLayerFactory: textLayerFactory,
|
|
annotationsLayerFactory: 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 (!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();
|
|
}
|
|
});
|
|
|
|
var event = document.createEvent('CustomEvent');
|
|
event.initCustomEvent('pagesinit', true, true, null);
|
|
self.container.dispatchEvent(event);
|
|
|
|
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 () {
|
|
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 event = document.createEvent('UIEvents');
|
|
event.initUIEvent('scalechange', true, true, window, 0);
|
|
event.scale = newScale;
|
|
if (preset) {
|
|
event.presetValue = newValue;
|
|
}
|
|
this.container.dispatchEvent(event);
|
|
},
|
|
|
|
_setScaleUpdatePages: function pdfViewer_setScaleUpdatePages(
|
|
newScale, newValue, noScroll, preset) {
|
|
this._currentScaleValue = newValue;
|
|
|
|
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 && !IGNORE_CURRENT_POSITION_ON_ZOOM &&
|
|
!(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);
|
|
},
|
|
|
|
_setScale: function pdfViewer_setScale(value, noScroll) {
|
|
if (value === 'custom') {
|
|
return;
|
|
}
|
|
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);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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) {
|
|
var pageView = this._pages[pageNumber - 1];
|
|
|
|
if (this.isInPresentationMode) {
|
|
if (this.linkService.page !== pageView.id) {
|
|
// Avoid breaking getVisiblePages in presentation mode.
|
|
this.linkService.page = pageView.id;
|
|
return;
|
|
}
|
|
dest = null;
|
|
// Fixes the case when PDF has different page sizes.
|
|
this._setScale(this.currentScaleValue, true);
|
|
}
|
|
if (!dest) {
|
|
scrollIntoView(pageView.div);
|
|
return;
|
|
}
|
|
|
|
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';
|
|
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 viewerContainer = this.container;
|
|
var hPadding = this.removePageBorders ? 0 : SCROLLBAR_PADDING;
|
|
var vPadding = this.removePageBorders ? 0 : VERTICAL_PADDING;
|
|
|
|
widthScale = (viewerContainer.clientWidth - hPadding) /
|
|
width / CSS_UNITS;
|
|
heightScale = (viewerContainer.clientHeight - vPadding) /
|
|
height / CSS_UNITS;
|
|
scale = Math.min(Math.abs(widthScale), Math.abs(heightScale));
|
|
break;
|
|
default:
|
|
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;
|
|
}
|
|
|
|
this.updateInProgress = true;
|
|
|
|
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.currentPageNumber = currentId;
|
|
}
|
|
|
|
this._updateLocation(firstPage);
|
|
|
|
this.updateInProgress = false;
|
|
|
|
var event = document.createEvent('UIEvents');
|
|
event.initUIEvent('updateviewarea', true, true, window, 0);
|
|
event.location = this._location;
|
|
this.container.dispatchEvent(event);
|
|
},
|
|
|
|
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();
|
|
});
|
|
},
|
|
|
|
/**
|
|
* @param {HTMLDivElement} textLayerDiv
|
|
* @param {number} pageIndex
|
|
* @param {PageViewport} viewport
|
|
* @returns {TextLayerBuilder}
|
|
*/
|
|
createTextLayerBuilder: function (textLayerDiv, pageIndex, viewport) {
|
|
return new TextLayerBuilder({
|
|
textLayerDiv: textLayerDiv,
|
|
pageIndex: pageIndex,
|
|
viewport: viewport,
|
|
findController: this.isInPresentationMode ? null : this.findController
|
|
});
|
|
},
|
|
|
|
/**
|
|
* @param {HTMLDivElement} pageDiv
|
|
* @param {PDFPage} pdfPage
|
|
* @returns {AnnotationsLayerBuilder}
|
|
*/
|
|
createAnnotationsLayerBuilder: function (pageDiv, pdfPage) {
|
|
return new AnnotationsLayerBuilder({
|
|
pageDiv: pageDiv,
|
|
pdfPage: pdfPage,
|
|
linkService: this.linkService
|
|
});
|
|
},
|
|
|
|
setFindController: function (findController) {
|
|
this.findController = findController;
|
|
},
|
|
};
|
|
|
|
return PDFViewer;
|
|
})();
|
|
|
|
var SimpleLinkService = (function SimpleLinkServiceClosure() {
|
|
function SimpleLinkService(pdfViewer) {
|
|
this.pdfViewer = pdfViewer;
|
|
}
|
|
SimpleLinkService.prototype = {
|
|
/**
|
|
* @returns {number}
|
|
*/
|
|
get page() {
|
|
return this.pdfViewer.currentPageNumber;
|
|
},
|
|
/**
|
|
* @param {number} value
|
|
*/
|
|
set page(value) {
|
|
this.pdfViewer.currentPageNumber = value;
|
|
},
|
|
/**
|
|
* @param dest - The PDF destination object.
|
|
*/
|
|
navigateTo: function (dest) {},
|
|
/**
|
|
* @param dest - The PDF destination object.
|
|
* @returns {string} The hyperlink to the PDF object.
|
|
*/
|
|
getDestinationHash: function (dest) {
|
|
return '#';
|
|
},
|
|
/**
|
|
* @param hash - The PDF parameters/hash.
|
|
* @returns {string} The hyperlink to the PDF object.
|
|
*/
|
|
getAnchorUrl: function (hash) {
|
|
return '#';
|
|
},
|
|
/**
|
|
* @param {string} hash
|
|
*/
|
|
setHash: function (hash) {},
|
|
/**
|
|
* @param {string} action
|
|
*/
|
|
executeNamedAction: function (action) {},
|
|
/**
|
|
* @param {number} pageNum - page number.
|
|
* @param {Object} pageRef - reference to the page.
|
|
*/
|
|
cachePageRef: function (pageNum, pageRef) {}
|
|
};
|
|
return SimpleLinkService;
|
|
})();
|