Add scrolling modes to web viewer

In addition to the default scrolling mode (vertical), this commit adds
horizontal and wrapped scrolling, implemented primarily with CSS.
This commit is contained in:
Ryan Hendrickson 2018-05-14 23:10:32 -04:00
parent 65c8549759
commit 91cbc185da
17 changed files with 665 additions and 27 deletions

View file

@ -15,9 +15,9 @@
import {
CSS_UNITS, DEFAULT_SCALE, DEFAULT_SCALE_VALUE, isPortraitOrientation,
isValidRotation, MAX_AUTO_SCALE, NullL10n, PresentationModeState,
RendererType, SCROLLBAR_PADDING, TextLayerMode, UNKNOWN_SCALE,
VERTICAL_PADDING, watchScroll
isValidRotation, MAX_AUTO_SCALE, moveToEndOfArray, NullL10n,
PresentationModeState, RendererType, SCROLLBAR_PADDING, TextLayerMode,
UNKNOWN_SCALE, VERTICAL_PADDING, watchScroll
} from './ui_utils';
import { PDFRenderingQueue, RenderingStates } from './pdf_rendering_queue';
import { AnnotationLayerBuilder } from './annotation_layer_builder';
@ -29,6 +29,12 @@ import { TextLayerBuilder } from './text_layer_builder';
const DEFAULT_CACHE_SIZE = 10;
const ScrollMode = {
VERTICAL: 0, // The default value.
HORIZONTAL: 1,
WRAPPED: 2,
};
/**
* @typedef {Object} PDFViewerOptions
* @property {HTMLDivElement} container - The container for the viewer element.
@ -61,6 +67,10 @@ const DEFAULT_CACHE_SIZE = 10;
* size in total pixels, i.e. width * height. Use -1 for no limit.
* The default value is 4096 * 4096 (16 mega-pixels).
* @property {IL10n} l10n - Localization service.
* @property {number} scrollMode - (optional) The direction in which the
* document pages should be laid out within the scrolling container. The
* constants from {ScrollMode} should be used. The default value is
* `ScrollMode.VERTICAL`.
*/
function PDFPageViewBuffer(size) {
@ -75,8 +85,24 @@ function PDFPageViewBuffer(size) {
data.shift().destroy();
}
};
this.resize = function(newSize) {
/**
* After calling resize, the size of the buffer will be newSize. The optional
* parameter pagesToKeep is, if present, an array of pages to push to the back
* of the buffer, delaying their destruction. The size of pagesToKeep has no
* impact on the final size of the buffer; if pagesToKeep has length larger
* than newSize, some of those pages will be destroyed anyway.
*/
this.resize = function(newSize, pagesToKeep) {
size = newSize;
if (pagesToKeep) {
const pageIdsToKeep = new Set();
for (let i = 0, iMax = pagesToKeep.length; i < iMax; ++i) {
pageIdsToKeep.add(pagesToKeep[i].id);
}
moveToEndOfArray(data, function(page) {
return pageIdsToKeep.has(page.id);
});
}
while (data.length > size) {
data.shift().destroy();
}
@ -126,6 +152,7 @@ class BaseViewer {
this.useOnlyCssZoom = options.useOnlyCssZoom || false;
this.maxCanvasPixels = options.maxCanvasPixels;
this.l10n = options.l10n || NullL10n;
this.scrollMode = options.scrollMode || ScrollMode.VERTICAL;
this.defaultRenderingQueue = !options.renderingQueue;
if (this.defaultRenderingQueue) {
@ -143,6 +170,7 @@ class BaseViewer {
if (this.removePageBorders) {
this.viewer.classList.add('removePageBorders');
}
this._updateScrollModeClasses();
}
get pagesCount() {
@ -557,6 +585,11 @@ class BaseViewer {
0 : SCROLLBAR_PADDING;
let vPadding = (this.isInPresentationMode || this.removePageBorders) ?
0 : VERTICAL_PADDING;
if (this.scrollMode === ScrollMode.HORIZONTAL) {
const temp = hPadding;
hPadding = vPadding;
vPadding = temp;
}
let pageWidthScale = (this.container.clientWidth - hPadding) /
currentPage.width * currentPage.scale;
let pageHeightScale = (this.container.clientHeight - vPadding) /
@ -733,10 +766,15 @@ class BaseViewer {
});
}
_resizeBuffer(numVisiblePages) {
/**
* visiblePages is optional; if present, it should be an array of pages and in
* practice its length is going to be numVisiblePages, but this is not
* required. The new size of the buffer depends only on numVisiblePages.
*/
_resizeBuffer(numVisiblePages, visiblePages) {
let suggestedCacheSize = Math.max(DEFAULT_CACHE_SIZE,
2 * numVisiblePages + 1);
this._buffer.resize(suggestedCacheSize);
this._buffer.resize(suggestedCacheSize, visiblePages);
}
_updateLocation(firstPage) {
@ -847,9 +885,11 @@ class BaseViewer {
forceRendering(currentlyVisiblePages) {
let visiblePages = currentlyVisiblePages || this._getVisiblePages();
let scrollAhead = this.scrollMode === ScrollMode.HORIZONTAL ?
this.scroll.right : this.scroll.down;
let pageView = this.renderingQueue.getHighestPriority(visiblePages,
this._pages,
this.scroll.down);
scrollAhead);
if (pageView) {
this._ensurePdfPageLoaded(pageView).then(() => {
this.renderingQueue.renderView(pageView);
@ -957,8 +997,32 @@ class BaseViewer {
};
});
}
setScrollMode(mode) {
if (mode !== this.scrollMode) {
this.scrollMode = mode;
this._updateScrollModeClasses();
this.eventBus.dispatch('scrollmodechanged', { mode, });
const pageNumber = this._currentPageNumber;
// Non-numeric scale modes can be sensitive to the scroll orientation.
// Call this before re-scrolling to the current page, to ensure that any
// changes in scale don't move the current page.
if (isNaN(this._currentScaleValue)) {
this._setScale(this._currentScaleValue, this.isInPresentationMode);
}
this.scrollPageIntoView({ pageNumber, });
this.update();
}
}
_updateScrollModeClasses() {
const mode = this.scrollMode, { classList, } = this.viewer;
classList.toggle('scrollHorizontal', mode === ScrollMode.HORIZONTAL);
classList.toggle('scrollWrapped', mode === ScrollMode.WRAPPED);
}
}
export {
BaseViewer,
ScrollMode,
};