Refactor PDFViewer to extend an abstract BaseViewer class

This patch introduces an abstract `BaseViewer` class, that the existing `PDFViewer` then extends. *Please note:* This lays the necessary foundation for the next patch.
This commit is contained in:
Jonas Jenwald 2017-08-01 14:11:28 +02:00
parent d7198d3e17
commit 5fa9cca8dd
5 changed files with 138 additions and 78 deletions

View File

@ -17,7 +17,7 @@
import { import {
animationStarted, DEFAULT_SCALE_VALUE, getPDFFileNameFromURL, isValidRotation, animationStarted, DEFAULT_SCALE_VALUE, getPDFFileNameFromURL, isValidRotation,
MAX_SCALE, MIN_SCALE, noContextMenuHandler, normalizeWheelEventDelta, MAX_SCALE, MIN_SCALE, noContextMenuHandler, normalizeWheelEventDelta,
parseQueryString, ProgressBar, RendererType parseQueryString, PresentationModeState, ProgressBar, RendererType
} from './ui_utils'; } from './ui_utils';
import { import {
build, createBlob, getDocument, getFilenameFromUrl, InvalidPDFException, build, createBlob, getDocument, getFilenameFromUrl, InvalidPDFException,
@ -27,7 +27,6 @@ import {
import { CursorTool, PDFCursorTools } from './pdf_cursor_tools'; import { CursorTool, PDFCursorTools } from './pdf_cursor_tools';
import { PDFRenderingQueue, RenderingStates } from './pdf_rendering_queue'; import { PDFRenderingQueue, RenderingStates } from './pdf_rendering_queue';
import { PDFSidebar, SidebarView } from './pdf_sidebar'; import { PDFSidebar, SidebarView } from './pdf_sidebar';
import { PDFViewer, PresentationModeState } from './base_viewer';
import { getGlobalEventBus } from './dom_events'; import { getGlobalEventBus } from './dom_events';
import { OverlayManager } from './overlay_manager'; import { OverlayManager } from './overlay_manager';
import { PasswordPrompt } from './password_prompt'; import { PasswordPrompt } from './password_prompt';
@ -40,6 +39,7 @@ import { PDFLinkService } from './pdf_link_service';
import { PDFOutlineViewer } from './pdf_outline_viewer'; import { PDFOutlineViewer } from './pdf_outline_viewer';
import { PDFPresentationMode } from './pdf_presentation_mode'; import { PDFPresentationMode } from './pdf_presentation_mode';
import { PDFThumbnailViewer } from './pdf_thumbnail_viewer'; import { PDFThumbnailViewer } from './pdf_thumbnail_viewer';
import { PDFViewer } from './pdf_viewer';
import { SecondaryToolbar } from './secondary_toolbar'; import { SecondaryToolbar } from './secondary_toolbar';
import { Toolbar } from './toolbar'; import { Toolbar } from './toolbar';
import { ViewHistory } from './view_history'; import { ViewHistory } from './view_history';

View File

@ -15,9 +15,9 @@
import { createPromiseCapability, PDFJS } from 'pdfjs-lib'; import { createPromiseCapability, PDFJS } from 'pdfjs-lib';
import { import {
CSS_UNITS, DEFAULT_SCALE, DEFAULT_SCALE_VALUE, getVisibleElements, CSS_UNITS, DEFAULT_SCALE, DEFAULT_SCALE_VALUE, isValidRotation,
isValidRotation, MAX_AUTO_SCALE, NullL10n, RendererType, SCROLLBAR_PADDING, MAX_AUTO_SCALE, NullL10n, PresentationModeState, RendererType,
scrollIntoView, UNKNOWN_SCALE, VERTICAL_PADDING, watchScroll SCROLLBAR_PADDING, UNKNOWN_SCALE, VERTICAL_PADDING, watchScroll
} from './ui_utils'; } from './ui_utils';
import { PDFRenderingQueue, RenderingStates } from './pdf_rendering_queue'; import { PDFRenderingQueue, RenderingStates } from './pdf_rendering_queue';
import { AnnotationLayerBuilder } from './annotation_layer_builder'; import { AnnotationLayerBuilder } from './annotation_layer_builder';
@ -26,13 +26,6 @@ import { PDFPageView } from './pdf_page_view';
import { SimpleLinkService } from './pdf_link_service'; import { SimpleLinkService } from './pdf_link_service';
import { TextLayerBuilder } from './text_layer_builder'; import { TextLayerBuilder } from './text_layer_builder';
const PresentationModeState = {
UNKNOWN: 0,
NORMAL: 1,
CHANGING: 2,
FULLSCREEN: 3,
};
const DEFAULT_CACHE_SIZE = 10; const DEFAULT_CACHE_SIZE = 10;
/** /**
@ -60,7 +53,7 @@ const DEFAULT_CACHE_SIZE = 10;
function PDFPageViewBuffer(size) { function PDFPageViewBuffer(size) {
let data = []; let data = [];
this.push = function cachePush(view) { this.push = function(view) {
let i = data.indexOf(view); let i = data.indexOf(view);
if (i >= 0) { if (i >= 0) {
data.splice(i, 1); data.splice(i, 1);
@ -70,7 +63,7 @@ function PDFPageViewBuffer(size) {
data.shift().destroy(); data.shift().destroy();
} }
}; };
this.resize = function (newSize) { this.resize = function(newSize) {
size = newSize; size = newSize;
while (data.length > size) { while (data.length > size) {
data.shift().destroy(); data.shift().destroy();
@ -98,11 +91,16 @@ function isPortraitOrientation(size) {
* Simple viewer control to display PDF content/pages. * Simple viewer control to display PDF content/pages.
* @implements {IRenderableView} * @implements {IRenderableView}
*/ */
class PDFViewer { class BaseViewer {
/** /**
* @param {PDFViewerOptions} options * @param {PDFViewerOptions} options
*/ */
constructor(options) { constructor(options) {
if (this.constructor === BaseViewer) {
throw new Error('Cannot initialize BaseViewer.');
}
this._name = this.constructor.name;
this.container = options.container; this.container = options.container;
this.viewer = options.viewer || options.container.firstElementChild; this.viewer = options.viewer || options.container.firstElementChild;
this.eventBus = options.eventBus || getGlobalEventBus(); this.eventBus = options.eventBus || getGlobalEventBus();
@ -182,7 +180,7 @@ class PDFViewer {
if (!(0 < val && val <= this.pagesCount)) { if (!(0 < val && val <= this.pagesCount)) {
console.error( console.error(
`PDFViewer._setCurrentPageNumber: "${val}" is out of bounds.`); `${this._name}._setCurrentPageNumber: "${val}" is out of bounds.`);
return; return;
} }
@ -305,6 +303,10 @@ class PDFViewer {
} }
} }
get _setDocumentViewerElement() {
throw new Error('Not implemented: _setDocumentViewerElement');
}
/** /**
* @param pdfDocument {PDFDocument} * @param pdfDocument {PDFDocument}
*/ */
@ -364,7 +366,7 @@ class PDFViewer {
textLayerFactory = this; textLayerFactory = this;
} }
let pageView = new PDFPageView({ let pageView = new PDFPageView({
container: this.viewer, container: this._setDocumentViewerElement,
eventBus: this.eventBus, eventBus: this.eventBus,
id: pageNum, id: pageNum,
scale, scale,
@ -437,7 +439,7 @@ class PDFViewer {
} else if (!(labels instanceof Array && } else if (!(labels instanceof Array &&
this.pdfDocument.numPages === labels.length)) { this.pdfDocument.numPages === labels.length)) {
this._pageLabels = null; this._pageLabels = null;
console.error('PDFViewer.setPageLabels: Invalid page labels.'); console.error(`${this._name}.setPageLabels: Invalid page labels.`);
} else { } else {
this._pageLabels = labels; this._pageLabels = labels;
} }
@ -472,6 +474,10 @@ class PDFViewer {
this.update(); this.update();
} }
_scrollIntoView({ pageDiv, pageSpot = null, pageNumber = null, }) {
throw new Error('Not implemented: _scrollIntoView');
}
_setScaleDispatchEvent(newScale, newValue, preset = false) { _setScaleDispatchEvent(newScale, newValue, preset = false) {
let arg = { let arg = {
source: this, source: this,
@ -560,7 +566,7 @@ class PDFViewer {
break; break;
default: default:
console.error( console.error(
`PDFViewer._setScale: "${value}" is an unknown zoom value.`); `${this._name}._setScale: "${value}" is an unknown zoom value.`);
return; return;
} }
this._setScaleUpdatePages(scale, value, noScroll, /* preset = */ true); this._setScaleUpdatePages(scale, value, noScroll, /* preset = */ true);
@ -578,7 +584,7 @@ class PDFViewer {
} }
let pageView = this._pages[this._currentPageNumber - 1]; let pageView = this._pages[this._currentPageNumber - 1];
scrollIntoView(pageView.div); this._scrollIntoView({ pageDiv: pageView.div, });
} }
/** /**
@ -615,7 +621,7 @@ class PDFViewer {
let pageView = this._pages[pageNumber - 1]; let pageView = this._pages[pageNumber - 1];
if (!pageView) { if (!pageView) {
console.error( console.error(
'PDFViewer.scrollPageIntoView: Invalid "pageNumber" parameter.'); `${this._name}.scrollPageIntoView: Invalid "pageNumber" parameter.`);
return; return;
} }
let x = 0, y = 0; let x = 0, y = 0;
@ -675,7 +681,7 @@ class PDFViewer {
scale = Math.min(Math.abs(widthScale), Math.abs(heightScale)); scale = Math.min(Math.abs(widthScale), Math.abs(heightScale));
break; break;
default: default:
console.error(`PDFViewer.scrollPageIntoView: "${dest[1].name}" ` + console.error(`${this._name}.scrollPageIntoView: "${dest[1].name}" ` +
'is not a valid destination type.'); 'is not a valid destination type.');
return; return;
} }
@ -687,7 +693,10 @@ class PDFViewer {
} }
if (scale === 'page-fit' && !dest[4]) { if (scale === 'page-fit' && !dest[4]) {
scrollIntoView(pageView.div); this._scrollIntoView({
pageDiv: pageView.div,
pageNumber,
});
return; return;
} }
@ -705,7 +714,17 @@ class PDFViewer {
left = Math.max(left, 0); left = Math.max(left, 0);
top = Math.max(top, 0); top = Math.max(top, 0);
} }
scrollIntoView(pageView.div, { left, top, }); this._scrollIntoView({
pageDiv: pageView.div,
pageSpot: { left, top, },
pageNumber,
});
}
_resizeBuffer(numVisiblePages) {
let suggestedCacheSize = Math.max(DEFAULT_CACHE_SIZE,
2 * numVisiblePages + 1);
this._buffer.resize(suggestedCacheSize);
} }
_updateLocation(firstPage) { _updateLocation(firstPage) {
@ -738,48 +757,7 @@ class PDFViewer {
} }
update() { update() {
let visible = this._getVisiblePages(); throw new Error('Not implemented: update');
let visiblePages = visible.views;
if (visiblePages.length === 0) {
return;
}
let suggestedCacheSize = Math.max(DEFAULT_CACHE_SIZE,
2 * visiblePages.length + 1);
this._buffer.resize(suggestedCacheSize);
this.renderingQueue.renderHighestPriority(visible);
let currentId = this._currentPageNumber;
let firstPage = visible.first;
let stillFullyVisible = false;
for (let i = 0, ii = visiblePages.length; i < ii; ++i) {
let 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(element) { containsElement(element) {
@ -804,15 +782,7 @@ class PDFViewer {
} }
_getVisiblePages() { _getVisiblePages() {
if (!this.isInPresentationMode) { throw new Error('Not implemented: _getVisiblePages');
return getVisibleElements(this.container, this._pages, true);
}
// The algorithm in getVisibleElements doesn't work in all browsers and
// configurations when presentation mode is active.
let visible = [];
let currentPage = this._pages[this._currentPageNumber - 1];
visible.push({ id: currentPage.id, view: currentPage, });
return { first: currentPage, last: currentPage, views: visible, };
} }
cleanup() { cleanup() {
@ -974,6 +944,5 @@ class PDFViewer {
} }
export { export {
PresentationModeState, BaseViewer,
PDFViewer,
}; };

View File

@ -16,7 +16,7 @@
'use strict'; 'use strict';
var pdfjsLib = require('./pdfjs.js'); var pdfjsLib = require('./pdfjs.js');
var pdfjsWebPDFViewer = require('./base_viewer.js'); var pdfjsWebPDFViewer = require('./pdf_viewer.js');
var pdfjsWebPDFPageView = require('./pdf_page_view.js'); var pdfjsWebPDFPageView = require('./pdf_page_view.js');
var pdfjsWebPDFLinkService = require('./pdf_link_service.js'); var pdfjsWebPDFLinkService = require('./pdf_link_service.js');
var pdfjsWebTextLayerBuilder = require('./text_layer_builder.js'); var pdfjsWebTextLayerBuilder = require('./text_layer_builder.js');

83
web/pdf_viewer.js Normal file
View File

@ -0,0 +1,83 @@
/* 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.
*/
import { getVisibleElements, scrollIntoView } from './ui_utils';
import { BaseViewer } from './base_viewer';
import { shadow } from 'pdfjs-lib';
class PDFViewer extends BaseViewer {
get _setDocumentViewerElement() {
return shadow(this, '_setDocumentViewerElement', this.viewer);
}
_scrollIntoView({ pageDiv, pageSpot = null, }) {
scrollIntoView(pageDiv, pageSpot);
}
_getVisiblePages() {
if (!this.isInPresentationMode) {
return getVisibleElements(this.container, this._pages, true);
}
// The algorithm in getVisibleElements doesn't work in all browsers and
// configurations when presentation mode is active.
let currentPage = this._pages[this._currentPageNumber - 1];
let visible = [{ id: currentPage.id, view: currentPage, }];
return { first: currentPage, last: currentPage, views: visible, };
}
update() {
let visible = this._getVisiblePages();
let visiblePages = visible.views, numVisiblePages = visiblePages.length;
if (numVisiblePages === 0) {
return;
}
this._resizeBuffer(numVisiblePages);
this.renderingQueue.renderHighestPriority(visible);
let currentId = this._currentPageNumber;
let stillFullyVisible = false;
for (let i = 0; i < numVisiblePages; ++i) {
let 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(visible.first);
this.eventBus.dispatch('updateviewarea', {
source: this,
location: this._location,
});
}
}
export {
PDFViewer,
};

View File

@ -25,6 +25,13 @@ const MAX_AUTO_SCALE = 1.25;
const SCROLLBAR_PADDING = 40; const SCROLLBAR_PADDING = 40;
const VERTICAL_PADDING = 5; const VERTICAL_PADDING = 5;
const PresentationModeState = {
UNKNOWN: 0,
NORMAL: 1,
CHANGING: 2,
FULLSCREEN: 3,
};
const RendererType = { const RendererType = {
CANVAS: 'canvas', CANVAS: 'canvas',
SVG: 'svg', SVG: 'svg',
@ -661,6 +668,7 @@ export {
VERTICAL_PADDING, VERTICAL_PADDING,
isValidRotation, isValidRotation,
cloneObj, cloneObj,
PresentationModeState,
RendererType, RendererType,
mozL10n, mozL10n,
NullL10n, NullL10n,