You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
314 lines
10 KiB
314 lines
10 KiB
/* -*- 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, Cache, DEFAULT_CACHE_SIZE, PageView, UNKNOWN_SCALE, |
|
IGNORE_CURRENT_POSITION_ON_ZOOM, SCROLLBAR_PADDING, VERTICAL_PADDING, |
|
MAX_AUTO_SCALE, getVisibleElements, PresentationMode, |
|
RenderingStates */ |
|
|
|
'use strict'; |
|
|
|
var PDFViewer = (function pdfViewer() { |
|
function PDFViewer(options) { |
|
this.container = options.container; |
|
this.viewer = options.viewer; |
|
this.renderingQueue = options.renderingQueue; |
|
this.linkService = options.linkService; |
|
|
|
this.scroll = watchScroll(this.container, this._scrollUpdate.bind(this)); |
|
this.pages = []; |
|
this.cache = new Cache(DEFAULT_CACHE_SIZE); |
|
this.currentPageNumber = 1; |
|
this.previousPageNumber = 1; |
|
this.updateInProgress = true; |
|
this.resetView(); |
|
} |
|
|
|
PDFViewer.prototype = { |
|
get pagesCount() { |
|
return this.pages.length; |
|
}, |
|
|
|
setCurrentPageNumber: function (val) { |
|
var event = document.createEvent('UIEvents'); |
|
event.initUIEvent('pagechange', true, true, window, 0); |
|
event.updateInProgress = this.updateInProgress; |
|
|
|
if (!(0 < val && val <= this.pagesCount)) { |
|
this.previousPageNumber = val; |
|
event.pageNumber = this.page; |
|
this.container.dispatchEvent(event); |
|
return; |
|
} |
|
|
|
this.pages[val - 1].updateStats(); |
|
this.previousPageNumber = this.currentPageNumber; |
|
this.currentPageNumber = val; |
|
event.pageNumber = val; |
|
this.container.dispatchEvent(event); |
|
}, |
|
|
|
addPage: function (pageNum, scale, viewport) { |
|
var pageView = new PageView(this.viewer, pageNum, scale, viewport, |
|
this.linkService, this.renderingQueue, |
|
this.cache, this); |
|
this.pages.push(pageView); |
|
return pageView; |
|
}, |
|
|
|
resetView: function () { |
|
this.currentScale = UNKNOWN_SCALE; |
|
this.currentScaleValue = null; |
|
this.location = null; |
|
}, |
|
|
|
_scrollUpdate: function () { |
|
if (this.pagesCount === 0) { |
|
return; |
|
} |
|
this.update(); |
|
}, |
|
|
|
_setScaleUpdatePages: function pdfViewer_setScaleUpdatePages( |
|
newScale, newValue, resetAutoSettings, noScroll, preset) { |
|
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.currentPageNumber, dest; |
|
if (this.location && !this.inPresentationMode && |
|
!IGNORE_CURRENT_POSITION_ON_ZOOM) { |
|
page = this.location.pageNumber; |
|
dest = [null, { name: 'XYZ' }, this.location.left, |
|
this.location.top, null]; |
|
} |
|
this.pages[page - 1].scrollIntoView(dest); |
|
} |
|
|
|
var event = document.createEvent('UIEvents'); |
|
event.initUIEvent('scalechange', true, true, window, 0); |
|
event.scale = newScale; |
|
event.resetAutoSettings = resetAutoSettings; |
|
if (preset) { |
|
event.presetValue = newValue; |
|
} |
|
this.container.dispatchEvent(event); |
|
}, |
|
|
|
setScale: function pdfViewer_setScale(value, resetAutoSettings, noScroll) { |
|
if (value === 'custom') { |
|
return; |
|
} |
|
var scale = parseFloat(value); |
|
|
|
if (scale > 0) { |
|
this._setScaleUpdatePages(scale, value, true, noScroll, false); |
|
} else { |
|
var currentPage = this.pages[this.currentPageNumber - 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': |
|
var isLandscape = (currentPage.width > currentPage.height); |
|
var horizontalScale = isLandscape ? pageHeightScale : |
|
pageWidthScale; |
|
scale = Math.min(MAX_AUTO_SCALE, horizontalScale); |
|
break; |
|
default: |
|
console.error('pdfViewSetScale: \'' + value + |
|
'\' is an unknown zoom value.'); |
|
return; |
|
} |
|
this._setScaleUpdatePages(scale, value, resetAutoSettings, noScroll, |
|
true); |
|
} |
|
}, |
|
|
|
updateRotation: function pdfViewRotatePages(rotation) { |
|
for (var i = 0, l = this.pages.length; i < l; i++) { |
|
var page = this.pages[i]; |
|
page.update(page.scale, rotation); |
|
} |
|
|
|
this.setScale(this.currentScaleValue, true, true); |
|
}, |
|
|
|
removeAllPages: function () { |
|
var container = this.viewer; |
|
while (container.hasChildNodes()) { |
|
container.removeChild(container.lastChild); |
|
} |
|
this.pages = []; |
|
}, |
|
|
|
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 |
|
}; |
|
}, |
|
|
|
get inPresentationMode() { |
|
return PresentationMode.active || PresentationMode.switchInProgress; |
|
}, |
|
|
|
update: function () { |
|
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.cache.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 === this.currentPageNumber) { |
|
stillFullyVisible = true; |
|
break; |
|
} |
|
} |
|
|
|
if (!stillFullyVisible) { |
|
currentId = visiblePages[0].id; |
|
} |
|
|
|
if (!PresentationMode.active) { |
|
this.setCurrentPageNumber(currentId); |
|
} |
|
|
|
this.updateLocation(firstPage); |
|
|
|
this.updateInProgress = false; |
|
|
|
var event = document.createEvent('UIEvents'); |
|
event.initUIEvent('updateviewarea', true, true, window, 0); |
|
this.container.dispatchEvent(event); |
|
}, |
|
|
|
containsElement: function (element) { |
|
return this.container.contains(element); |
|
}, |
|
|
|
focus: function () { |
|
this.container.focus(); |
|
}, |
|
|
|
blur: function () { |
|
this.container.blur(); |
|
}, |
|
|
|
get isHorizontalScrollbarEnabled() { |
|
return (PresentationMode.active ? false : |
|
(this.container.scrollWidth > this.container.clientWidth)); |
|
}, |
|
|
|
getVisiblePages: function () { |
|
if (!PresentationMode.active) { |
|
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(); |
|
} |
|
} |
|
}, |
|
|
|
forceRendering: function (currentlyVisiblePages) { |
|
var visiblePages = currentlyVisiblePages || this.getVisiblePages(); |
|
var pageView = this.renderingQueue.getHighestPriority(visiblePages, |
|
this.pages, |
|
this.scroll.down); |
|
if (pageView) { |
|
this.renderingQueue.renderView(pageView, 'page'); |
|
return; |
|
} |
|
}, |
|
}; |
|
|
|
return PDFViewer; |
|
})();
|
|
|