|
|
@@ -1,972 +0,0 @@
|
|
|
-(function (name, context, definition) {
|
|
|
- if (typeof module != 'undefined' && module.exports) module.exports = definition()
|
|
|
- else if (typeof define == 'function' && define.amd) define(definition)
|
|
|
- else context[name] = definition()
|
|
|
-})('PDFListView', this, function (name, context) {
|
|
|
-
|
|
|
-//#expand __BUNDLE__
|
|
|
-
|
|
|
-function Logger() {
|
|
|
- this.logLevel = Logger.INFO;
|
|
|
- var self = this;
|
|
|
- if (typeof(console) == "object" && typeof(console.log) == "function") {
|
|
|
- this.debug = function() {
|
|
|
- if (self.logLevel <= Logger.DEBUG) {
|
|
|
- console.log.apply(console, arguments);
|
|
|
- }
|
|
|
- };
|
|
|
- this.info = function() {
|
|
|
- if (self.logLevel <= Logger.INFO) {
|
|
|
- console.log.apply(console, arguments);
|
|
|
- }
|
|
|
- };
|
|
|
- this.error = function() {
|
|
|
- if (self.logLevel <= Logger.ERROR) {
|
|
|
- console.log.apply(console, arguments);
|
|
|
- }
|
|
|
- };
|
|
|
- } else {
|
|
|
- this.debug = this.info = this.error = function nop() {};
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-Logger.DEBUG = 0;
|
|
|
-Logger.INFO = 1;
|
|
|
-Logger.ERROR = 2;
|
|
|
-
|
|
|
-var logger = new Logger();
|
|
|
-
|
|
|
-function _flat(arr) {
|
|
|
- var res = arr.reduce(function(a, b) {
|
|
|
- return a.concat(b);
|
|
|
- });
|
|
|
- return res;
|
|
|
-}
|
|
|
-
|
|
|
-function failDumper(err) {
|
|
|
- logger.error(err);
|
|
|
-}
|
|
|
-
|
|
|
-if (typeof(PDFJS) === "undefined") {
|
|
|
- logger.error("PDF.js is not yet loaded.");
|
|
|
-}
|
|
|
-
|
|
|
-// -----------------------------------------------------------------------------
|
|
|
-
|
|
|
-/**
|
|
|
- * Wrapper around the raw PDF.JS document.
|
|
|
- */
|
|
|
-function Document(url, password, onProgress) {
|
|
|
- this.pdfDocument = null;
|
|
|
- this.pages = null;
|
|
|
-
|
|
|
- var parameters = {password: password};
|
|
|
- if (typeof url === 'string') { // URL
|
|
|
- parameters.url = url;
|
|
|
- } else if (url && 'byteLength' in url) { // ArrayBuffer
|
|
|
- parameters.data = url;
|
|
|
- }
|
|
|
-
|
|
|
- this.initialized = new PDFJS.LegacyPromise();
|
|
|
- PDFJS.getDocument(parameters, null, null, onProgress).then(this.loadPages.bind(this), failDumper);
|
|
|
-}
|
|
|
-
|
|
|
-Document.prototype.loadPages = function(pdfDocument) {
|
|
|
- this.pdfDocument = pdfDocument;
|
|
|
- var pagesCount = this.pagesCount = pdfDocument.numPages;
|
|
|
-
|
|
|
- var pagePromises = [];
|
|
|
- for (var i = 1; i <= pagesCount; i++) {
|
|
|
- pagePromises.push(pdfDocument.getPage(i));
|
|
|
- }
|
|
|
-
|
|
|
- this.pageRefMap = pageRefMap = {};
|
|
|
- var pagesPromise = Promise.all(pagePromises);
|
|
|
- var doc = this
|
|
|
- pagesPromise.then(function(promisedPages) {
|
|
|
- doc.pages = promisedPages.map(function(pdfPage, i) {
|
|
|
- var pageRef = pdfPage.ref
|
|
|
- pageRefMap[pageRef.num + ' ' + pageRef.gen] = i;
|
|
|
- return new Page(pdfPage);
|
|
|
- });
|
|
|
- });
|
|
|
-
|
|
|
- var destinationsPromise = pdfDocument.getDestinations();
|
|
|
- destinationsPromise.then(function(destinations) {
|
|
|
- doc.destinations = destinations;
|
|
|
- });
|
|
|
-
|
|
|
- Promise.all([pagesPromise, destinationsPromise]).then(function() {
|
|
|
- doc.initialized.resolve();
|
|
|
- }, failDumper);
|
|
|
-};
|
|
|
-
|
|
|
-/**
|
|
|
- * Handles the rendering. Multiple ListViews can be bound to a RenderController.
|
|
|
- * In that case, the RenderController figures out what's page has the highest
|
|
|
- * priority to render
|
|
|
- */
|
|
|
-function RenderController() {
|
|
|
- this.listViews = [];
|
|
|
- this.renderList = [];
|
|
|
-}
|
|
|
-
|
|
|
-RenderController.prototype = {
|
|
|
- addListView: function(listView) {
|
|
|
- // TODO: assert listView not already in list of this.listView
|
|
|
- this.listViews.push(listView);
|
|
|
- },
|
|
|
-
|
|
|
- updateRenderList: function() {
|
|
|
- this.renderList = _flat(this.listViews.map(function(listView) {
|
|
|
- return listView.getPagesToRender();
|
|
|
- }));
|
|
|
-
|
|
|
- // TODO: Some "highest-priority" sorting algorithm on the renderList.
|
|
|
-
|
|
|
- this.doRender();
|
|
|
- },
|
|
|
-
|
|
|
- pageToRender: function() {
|
|
|
- if (this.renderList.length === 0) return null;
|
|
|
-
|
|
|
- return this.renderList[0];
|
|
|
- },
|
|
|
-
|
|
|
- doRender: function() {
|
|
|
- var pageToRender = this.pageToRender();
|
|
|
-
|
|
|
- if (!pageToRender) return;
|
|
|
-
|
|
|
- pageToRender.render(this);
|
|
|
- },
|
|
|
-
|
|
|
- finishedRendering: function(pageView) {
|
|
|
- var idx = this.renderList.indexOf(pageView);
|
|
|
-
|
|
|
- pageView.callAfterRenderedCallbacks();
|
|
|
-
|
|
|
- // If the finished pageView is in the list of pages to render,
|
|
|
- // then remove it from the list and render start rendering the
|
|
|
- // next page.
|
|
|
- if (idx !== -1) {
|
|
|
- this.renderList.splice(idx, 1);
|
|
|
- this.doRender();
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- onResize: function() {
|
|
|
- var renderAgain = false
|
|
|
- this.listViews.map(function(listView) {
|
|
|
- if (listView.calculateScale()) {
|
|
|
- listView.layout();
|
|
|
- renderAgain = true;
|
|
|
- }
|
|
|
- });
|
|
|
- if (renderAgain) {
|
|
|
- this.updateRenderList();
|
|
|
- }
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-var LAYOUT_SINGLE = 'layout_single';
|
|
|
-var SCALE_MODE_AUTO = 'scale_mode_auto';
|
|
|
-var SCALE_MODE_VALUE = 'scale_mode_value';
|
|
|
-var SCALE_MODE_FIT_WIDTH = 'scale_mode_fit_width';
|
|
|
-var SCALE_MODE_FIT_HEIGHT = 'scale_mode_fit_height';
|
|
|
-
|
|
|
-/**
|
|
|
- * Main view that holds the single pageContainer/pageViews of the pdfDoc.
|
|
|
- */
|
|
|
-function ListView(dom, options) {
|
|
|
- this.dom = dom;
|
|
|
- this.options = options;
|
|
|
-
|
|
|
- this.pageLayout = LAYOUT_SINGLE;
|
|
|
- this.scaleMode = SCALE_MODE_VALUE;
|
|
|
- this.scale = 1.0;
|
|
|
-
|
|
|
- this.pageWidthOffset = 0;
|
|
|
- this.pageHeightOffset = 0;
|
|
|
-
|
|
|
- this.pageViews = [];
|
|
|
- this.containerViews = [];
|
|
|
-}
|
|
|
-
|
|
|
-ListView.prototype = {
|
|
|
- setDocument: function(pdfDoc) {
|
|
|
- this.clearPages();
|
|
|
-
|
|
|
- this.pdfDoc = pdfDoc;
|
|
|
-
|
|
|
- this.assignPagesToContainer();
|
|
|
- this.layout();
|
|
|
- },
|
|
|
-
|
|
|
- clearPages: function() {
|
|
|
- var self = this;
|
|
|
- this.containerViews.map(function(container) {
|
|
|
- self.dom.removeChild(container.dom);
|
|
|
- });
|
|
|
- this.pageViews = [];
|
|
|
- this.containerViews = [];
|
|
|
- },
|
|
|
-
|
|
|
- assignPagesToContainer: function() {
|
|
|
- // TODO: Handle multiple layout types here. For now, assume to have one page
|
|
|
- // per pageContainer.
|
|
|
- this.pdfDoc.pages.map(function(page) {
|
|
|
- var pageView = new PageView(page, this);
|
|
|
-
|
|
|
- // TODO: Switch over to a proper event handler
|
|
|
- var that = this;
|
|
|
- var index = that.pageViews.length;
|
|
|
- pageView.ondblclick = function(e) {
|
|
|
- e.page = index;
|
|
|
- if (that.ondblclick) {
|
|
|
- that.ondblclick.call(that, e);
|
|
|
- }
|
|
|
- }
|
|
|
- this.pageViews.push(pageView);
|
|
|
-
|
|
|
- var container = new PageContainerView(this);
|
|
|
- container.setPageView(pageView, 0);
|
|
|
- this.containerViews.push(container);
|
|
|
-
|
|
|
- this.dom.appendChild(container.dom);
|
|
|
- }, this);
|
|
|
- },
|
|
|
-
|
|
|
- layout: function() {
|
|
|
- this.savePdfPosition();
|
|
|
- this.containerViews.forEach(function(containerView) {
|
|
|
- containerView.layout();
|
|
|
- });
|
|
|
- this.restorePdfPosition();
|
|
|
- },
|
|
|
-
|
|
|
- getScale: function() {
|
|
|
- return this.scale;
|
|
|
- },
|
|
|
-
|
|
|
- getScaleMode: function() {
|
|
|
- return this.scaleMode;
|
|
|
- },
|
|
|
-
|
|
|
- setScaleMode: function(scaleMode, scale) {
|
|
|
- this.scaleMode = scaleMode;
|
|
|
- if (scaleMode == SCALE_MODE_VALUE) {
|
|
|
- this.scale = scale;
|
|
|
- }
|
|
|
- this.calculateScale();
|
|
|
- this.layout();
|
|
|
- },
|
|
|
-
|
|
|
- setScale: function(scale) {
|
|
|
- this.setScaleMode(SCALE_MODE_VALUE, scale);
|
|
|
- },
|
|
|
-
|
|
|
- setToAutoScale: function() {
|
|
|
- this.setScaleMode(SCALE_MODE_AUTO);
|
|
|
- },
|
|
|
-
|
|
|
- setToFitWidth: function() {
|
|
|
- this.setScaleMode(SCALE_MODE_FIT_WIDTH);
|
|
|
- },
|
|
|
-
|
|
|
- setToFitHeight: function() {
|
|
|
- this.setScaleMode(SCALE_MODE_FIT_HEIGHT);
|
|
|
- },
|
|
|
-
|
|
|
- // Calculates the new scale. Returns `true` if the scale changed.
|
|
|
- calculateScale: function() {
|
|
|
- var newScale = this.scale;
|
|
|
- var oldScale = newScale;
|
|
|
- var scaleMode = this.scaleMode;
|
|
|
- if (scaleMode === SCALE_MODE_FIT_WIDTH || scaleMode === SCALE_MODE_AUTO) {
|
|
|
- var clientWidth = this.dom.clientWidth;
|
|
|
- if (clientWidth == 0) {
|
|
|
- logger.debug("LIST VIEW NOT VISIBLE")
|
|
|
- return false;
|
|
|
- }
|
|
|
- var maxNormalWidth = 0;
|
|
|
- this.containerViews.forEach(function(containerView) {
|
|
|
- maxNormalWidth = Math.max(maxNormalWidth, containerView.normalWidth);
|
|
|
- });
|
|
|
- var scale = (clientWidth - this.pageWidthOffset)/maxNormalWidth;
|
|
|
- if (scaleMode === SCALE_MODE_AUTO) {
|
|
|
- scale = Math.min(1.0, scale);
|
|
|
- }
|
|
|
- newScale = scale;
|
|
|
- } else if (scaleMode === SCALE_MODE_FIT_HEIGHT) {
|
|
|
- var clientHeight = this.dom.clientHeight;
|
|
|
- if (clientHeight == 0) {
|
|
|
- logger.debug("LIST VIEW NOT VISIBLE")
|
|
|
- return false;
|
|
|
- }
|
|
|
- var maxNormalHeight = 0;
|
|
|
- this.containerViews.forEach(function(containerView) {
|
|
|
- maxNormalHeight = Math.max(maxNormalHeight, containerView.normalHeight);
|
|
|
- });
|
|
|
- newScale = (clientHeight - this.pageHeightOffset)/maxNormalHeight;
|
|
|
- }
|
|
|
- this.scale = newScale;
|
|
|
- return newScale !== oldScale;
|
|
|
- },
|
|
|
-
|
|
|
- getPagesToRender: function() {
|
|
|
- // Cache these results to avoid dom access.
|
|
|
- this.scrollTop = this.dom.scrollTop;
|
|
|
- this.scrollBottom = this.scrollTop + this.dom.clientHeight;
|
|
|
-
|
|
|
- // TODO: For now, this only returns the visible pages and not
|
|
|
- // +1/-1 one to render in advance.
|
|
|
- return this.pageViews.filter(function(pageView) {
|
|
|
- var isVisible = pageView.isVisible();
|
|
|
-
|
|
|
- if (isVisible && !pageView.isRendered) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- });
|
|
|
- },
|
|
|
-
|
|
|
- navigateTo: function(destRef) {
|
|
|
- var destination = this.pdfDoc.destinations[destRef]
|
|
|
- if (typeof destination !== "object") {
|
|
|
- return;
|
|
|
- }
|
|
|
- logger.debug("NAVIGATING TO", destination);
|
|
|
-
|
|
|
- var pageRef = destination[0];
|
|
|
- var pageNumber = this.pdfDoc.pageRefMap[pageRef.num + ' ' + pageRef.gen];
|
|
|
- if (typeof pageNumber !== "number") {
|
|
|
- return;
|
|
|
- }
|
|
|
- logger.debug("PAGE NUMBER", pageNumber);
|
|
|
-
|
|
|
- var pageView = this.pageViews[pageNumber];
|
|
|
-
|
|
|
- var destinationType = destination[1].name;
|
|
|
- switch(destinationType) {
|
|
|
- case "XYZ":
|
|
|
- var x = destination[2];
|
|
|
- var y = destination[3];
|
|
|
- break;
|
|
|
- default:
|
|
|
- // TODO
|
|
|
- return;
|
|
|
- }
|
|
|
- var position = pageView.getPdfPositionInViewer(x,y);
|
|
|
- this.dom.scrollTop = position.top;
|
|
|
- this.dom.scrollLeft = position.left;
|
|
|
- },
|
|
|
-
|
|
|
- savePdfPosition: function() {
|
|
|
- this.pdfPosition = this.getPdfPosition();
|
|
|
- logger.debug("SAVED PDF POSITION", this.pdfPosition);
|
|
|
- },
|
|
|
-
|
|
|
- restorePdfPosition: function() {
|
|
|
- logger.debug("RESTORING PDF POSITION", this.pdfPosition);
|
|
|
- this.setPdfPosition(this.pdfPosition);
|
|
|
- },
|
|
|
-
|
|
|
- getPdfPosition: function(fromTop) {
|
|
|
- var pdfPosition = null;
|
|
|
- for (var i = 0; i < this.pageViews.length; i++) {
|
|
|
- var pageView = this.pageViews[i];
|
|
|
- var pdfOffset = pageView.getUppermostVisiblePdfOffset();
|
|
|
- if (pdfOffset !== null) {
|
|
|
- if (fromTop) {
|
|
|
- pdfOffset = pageView.normalHeight - pdfOffset;
|
|
|
- }
|
|
|
- pdfPosition = {
|
|
|
- page: i,
|
|
|
- offset: {
|
|
|
- top: pdfOffset,
|
|
|
- left: 0 // TODO
|
|
|
- }
|
|
|
- }
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- return pdfPosition;
|
|
|
- },
|
|
|
-
|
|
|
- setPdfPosition: function(pdfPosition, fromTop) {
|
|
|
- if (typeof pdfPosition !== "undefined" && pdfPosition != null) {
|
|
|
- var offset = pdfPosition.offset;
|
|
|
- var page_index = pdfPosition.page;
|
|
|
- var pageView = this.pageViews[page_index];
|
|
|
- if (fromTop) {
|
|
|
- offset.top = pageView.normalHeight - offset.top;
|
|
|
- }
|
|
|
- var position = pageView.getPdfPositionInViewer(offset.left, offset.top);
|
|
|
- this.dom.scrollTop = position.top;
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- setHighlights: function(highlights, fromTop) {
|
|
|
- for (i = 0; i < highlights.length; i++) {
|
|
|
- var pageIndex = highlights[i].page;
|
|
|
- var pageView = this.pageViews[pageIndex];
|
|
|
- (function(pageView, highlight) {
|
|
|
- pageView.doWhenRendered(function() {
|
|
|
- pageView.addHighlight(highlight.highlight, fromTop);
|
|
|
- });
|
|
|
- })(pageView, highlights[i]);
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- clearHighlights: function() {
|
|
|
- for (var i = 0; i < this.pageViews.length; i++) {
|
|
|
- var pageView = this.pageViews[i].clearHighlights();
|
|
|
- }
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-/*
|
|
|
- * A PageContainerView holds multiple PageViews. E.g. in a two-page layout,
|
|
|
- * every pageContainerView holds two PageViews and is responsible to layout
|
|
|
- * them.
|
|
|
- */
|
|
|
-function PageContainerView(listView) {
|
|
|
- this.listView = listView;
|
|
|
-
|
|
|
- var dom = this.dom = document.createElement('div');
|
|
|
- dom.className = 'plv-page-container page-container';
|
|
|
- this.pages = [];
|
|
|
-}
|
|
|
-
|
|
|
-PageContainerView.prototype = {
|
|
|
- setPageView: function(pageView, idx) {
|
|
|
- // TODO: handle case if there is already a page here
|
|
|
- this.pages[idx] = pageView;
|
|
|
-
|
|
|
- // TODO: handle page idx properly
|
|
|
- this.dom.appendChild(pageView.dom);
|
|
|
- },
|
|
|
-
|
|
|
- removePageView: function(idx) {
|
|
|
- // TODO: check if idx is set on page[]
|
|
|
- this.dom.removeChild(this.pages[idx].dom);
|
|
|
- },
|
|
|
-
|
|
|
- layout: function() {
|
|
|
- var scale = this.listView.scale;
|
|
|
-
|
|
|
- var normalWidth = 0;
|
|
|
- var normalHeight = 0;
|
|
|
-
|
|
|
- this.pages.forEach(function(pageView) {
|
|
|
- pageView.layout();
|
|
|
- normalWidth += pageView.normalWidth;
|
|
|
- normalHeight = Math.max(pageView.normalHeight, normalHeight);
|
|
|
- });
|
|
|
-
|
|
|
- this.normalWidth = normalWidth;
|
|
|
- this.normalHeight = normalHeight;
|
|
|
-
|
|
|
- this.dom.style.width = (normalWidth * scale) + 'px';
|
|
|
- this.dom.style.height = (normalHeight * scale) + 'px';
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-var RenderingStates = {
|
|
|
- INITIAL: 0,
|
|
|
- RUNNING: 1,
|
|
|
- PAUSED: 2,
|
|
|
- FINISHED: 3
|
|
|
-};
|
|
|
-
|
|
|
-var idCounter = 0;
|
|
|
-
|
|
|
-
|
|
|
-/**
|
|
|
- * The view for a single page.
|
|
|
- */
|
|
|
-function PageView(page, listView) {
|
|
|
- this.page = page;
|
|
|
- this.listView = listView;
|
|
|
- this.id = idCounter++;
|
|
|
- this.number = this.page.number;
|
|
|
-
|
|
|
- this.rotation = 0;
|
|
|
-
|
|
|
- this.isRendered = false;
|
|
|
- this.renderState = RenderingStates.INITIAL;
|
|
|
- this.afterRenderCallbacks = [];
|
|
|
-
|
|
|
- var dom = this.dom = document.createElement('div');
|
|
|
- dom.className = "plv-page-view page-view";
|
|
|
- var that = this;
|
|
|
- dom.ondblclick = function(e) {
|
|
|
- var layerX = e.layerX;
|
|
|
- var layerY = e.layerY;
|
|
|
- var element = e.target;
|
|
|
- while (element.offsetParent && element.offsetParent !== dom) {
|
|
|
- layerX = layerX + element.offsetLeft;
|
|
|
- layerY = layerY + element.offsetTop;
|
|
|
- element = element.offsetParent;
|
|
|
- }
|
|
|
-
|
|
|
- var pdfPoint = that.viewport.convertToPdfPoint(layerX, layerY);
|
|
|
- var event = {
|
|
|
- x: pdfPoint[0],
|
|
|
- y: that.normalHeight - pdfPoint[1]
|
|
|
- };
|
|
|
-
|
|
|
- if (that.ondblclick) {
|
|
|
- that.ondblclick.call(that.listView, event)
|
|
|
- }
|
|
|
- }
|
|
|
- this.createNewCanvas();
|
|
|
-}
|
|
|
-
|
|
|
-PageView.prototype = {
|
|
|
- layout: function() {
|
|
|
- var scale = this.listView.scale;
|
|
|
-
|
|
|
- var viewport = this.viewport =
|
|
|
- this.page.pdfPage.getViewport(scale, this.rotation);
|
|
|
-
|
|
|
- this.normalWidth = viewport.width / scale;
|
|
|
- this.normalHeight = viewport.height / scale;
|
|
|
-
|
|
|
- // Only change the width/height property of the canvas if it really
|
|
|
- // changed. Every assignment to the width/height property clears the
|
|
|
- // content of the canvas.
|
|
|
-
|
|
|
- var outputScale = this.getOutputScale();
|
|
|
-
|
|
|
- var scaledWidth = (Math.floor(viewport.width) * outputScale.sx) | 0;
|
|
|
- var scaledHeight = (Math.floor(viewport.height) * outputScale.sy) | 0;
|
|
|
-
|
|
|
- var newWidth = Math.floor(viewport.width);
|
|
|
- var newHeight = Math.floor(viewport.height);
|
|
|
-
|
|
|
- if (this.canvas.width !== newWidth) {
|
|
|
- this.canvas.width = scaledWidth;
|
|
|
- this.canvas.style.width = newWidth + 'px';
|
|
|
- this.resetRenderState();
|
|
|
- }
|
|
|
- if (this.canvas.height !== newHeight) {
|
|
|
- this.canvas.height = scaledHeight;
|
|
|
- this.canvas.style.height = newHeight + 'px';
|
|
|
- this.resetRenderState();
|
|
|
- }
|
|
|
-
|
|
|
- if(outputScale.scaled){
|
|
|
- var ctx = this.getCanvasContext()
|
|
|
- ctx.scale(outputScale.sx, outputScale.sy)
|
|
|
- }
|
|
|
-
|
|
|
- this.width = viewport.width;
|
|
|
- this.height = viewport.height;
|
|
|
- },
|
|
|
-
|
|
|
- isVisible: function() {
|
|
|
- var listView = this.listView;
|
|
|
- var dom = this.dom;
|
|
|
- var offsetTop = dom.offsetTop;
|
|
|
- var offsetBottom = offsetTop + this.height;
|
|
|
-
|
|
|
- return offsetBottom >= listView.scrollTop &&
|
|
|
- offsetTop <= listView.scrollBottom;
|
|
|
- },
|
|
|
-
|
|
|
- resetRenderState: function() {
|
|
|
- this.renderState = RenderingStates.INITIAL;
|
|
|
- this.isRendered = false;
|
|
|
- if (this.textLayerDiv) {
|
|
|
- this.dom.removeChild(this.textLayerDiv);
|
|
|
- delete this.textLayerDiv;
|
|
|
- }
|
|
|
- if (this.annotationsLayerDiv) {
|
|
|
- this.dom.removeChild(this.annotationsLayerDiv);
|
|
|
- delete this.annotationsLayerDiv;
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- render: function(renderController) {
|
|
|
- return this.page.render(this, renderController);
|
|
|
- },
|
|
|
-
|
|
|
- getCanvasContext: function() {
|
|
|
- return this.canvas.getContext('2d');
|
|
|
- },
|
|
|
-
|
|
|
- /**
|
|
|
- * Returns scale factor for the canvas. It makes sense for the HiDPI displays.
|
|
|
- * @return {Object} The object with horizontal (sx) and vertical (sy)
|
|
|
- scales. The scaled property is set to false if scaling is
|
|
|
- not required, true otherwise.
|
|
|
- */
|
|
|
-
|
|
|
- getOutputScale: function(){
|
|
|
- var ctx = this.getCanvasContext()
|
|
|
- var devicePixelRatio = window.devicePixelRatio || 1;
|
|
|
- var backingStoreRatio = ctx.webkitBackingStorePixelRatio ||
|
|
|
- ctx.mozBackingStorePixelRatio ||
|
|
|
- ctx.msBackingStorePixelRatio ||
|
|
|
- ctx.oBackingStorePixelRatio ||
|
|
|
- ctx.backingStorePixelRatio || 1;
|
|
|
- var pixelRatio = devicePixelRatio / backingStoreRatio;
|
|
|
- return {
|
|
|
- sx: pixelRatio,
|
|
|
- sy: pixelRatio,
|
|
|
- scaled: pixelRatio != 1
|
|
|
- };
|
|
|
- },
|
|
|
-
|
|
|
- createNewCanvas: function() {
|
|
|
- if (this.canvas) {
|
|
|
- this.dom.removeChild(this.canvas);
|
|
|
- }
|
|
|
- var canvas = this.canvas = document.createElement('canvas');
|
|
|
- this.dom.appendChild(canvas);
|
|
|
- this.layout();
|
|
|
- },
|
|
|
-
|
|
|
- pdfPositionToPixels: function(x, y) {
|
|
|
- return this.viewport.convertToViewportPoint(x, y);
|
|
|
- },
|
|
|
-
|
|
|
- getCanvasPositionInViewer: function() {
|
|
|
- return {
|
|
|
- left: this.canvas.offsetLeft + this.dom.offsetLeft,
|
|
|
- top: this.canvas.offsetTop + this.dom.offsetTop
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- getPdfPositionInViewer: function(x, y) {
|
|
|
- pageOffset = this.pdfPositionToPixels(x, y);
|
|
|
- canvasOffset = this.getCanvasPositionInViewer();
|
|
|
- return {
|
|
|
- left: canvasOffset.left + pageOffset[0],
|
|
|
- top: canvasOffset.top + pageOffset[1]
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- getUppermostVisibleCanvasOffset: function() {
|
|
|
- var pagePosition = this.getCanvasPositionInViewer();
|
|
|
- var pageHeight = this.canvas.height;
|
|
|
- var viewportTop = this.listView.dom.scrollTop;
|
|
|
- var viewportHeight = this.listView.dom.clientHeight;
|
|
|
- // Check if the top of the page is showing, i.e:
|
|
|
- // _______________
|
|
|
- // | |
|
|
|
- // | ........ |
|
|
|
- // | . . |
|
|
|
- // ----.------.---
|
|
|
- // . .
|
|
|
- // ........
|
|
|
- var topVisible = (pagePosition.top > viewportTop && pagePosition.top < viewportTop + viewportHeight);
|
|
|
- // Check if at least some of the page is showing, i.e:
|
|
|
- // ........ ........
|
|
|
- // ____.______.___ ---.------.---
|
|
|
- // | . . | or | . . |
|
|
|
- // | . . | | . . |
|
|
|
- // | ........ | | . . |
|
|
|
- // --------------- ---.------.---
|
|
|
- // ........
|
|
|
- var someContentVisible = (pagePosition.top < viewportTop && pagePosition.top + pageHeight > viewportTop);
|
|
|
- if (topVisible) {
|
|
|
- return 0;
|
|
|
- } else if (someContentVisible) {
|
|
|
- return viewportTop - pagePosition.top;
|
|
|
- } else {
|
|
|
- return null;
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- getUppermostVisiblePdfOffset: function() {
|
|
|
- var canvasOffset = this.getUppermostVisibleCanvasOffset();
|
|
|
- if (canvasOffset === null) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- var pdfOffset = this.viewport.convertToPdfPoint(0, canvasOffset);
|
|
|
- return pdfOffset[1];
|
|
|
- },
|
|
|
-
|
|
|
- clearHighlights: function() {
|
|
|
- if (this.highlightsLayer) {
|
|
|
- this.highlightsLayer.clearHighlights();
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- addHighlight: function(highlight, fromTop) {
|
|
|
- if (this.highlightsLayer) {
|
|
|
- var top = highlight.top;
|
|
|
- var left = highlight.left;
|
|
|
- var width = highlight.width;
|
|
|
- var height = highlight.height;
|
|
|
- if (fromTop) {
|
|
|
- top = this.normalHeight - top;
|
|
|
- }
|
|
|
- this.highlightsLayer.addHighlight(left, top, width, height);
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- doWhenRendered: function(callback) {
|
|
|
- if (this.isRendered) {
|
|
|
- callback()
|
|
|
- } else {
|
|
|
- this.afterRenderCallbacks.push(callback);
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- callAfterRenderedCallbacks: function () {
|
|
|
- var callbacks = this.afterRenderCallbacks;
|
|
|
- for (var i = 0; i < callbacks.length; i++) {
|
|
|
- callbacks[i]();
|
|
|
- }
|
|
|
- this.afterRenderCallbacks = [];
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-/**
|
|
|
- * An abstraction around the raw page object of PDF.JS, that also handles the
|
|
|
- * rendering logic of (maybe multiple) pageView(s) that are based on this page.
|
|
|
- */
|
|
|
-function Page(pdfPage, number) {
|
|
|
- this.number = number;
|
|
|
- this.pdfPage = pdfPage;
|
|
|
-
|
|
|
- this.renderContextList = {};
|
|
|
-}
|
|
|
-
|
|
|
-Page.prototype = {
|
|
|
- render: function(pageView, renderController) {
|
|
|
- var renderContext;
|
|
|
-
|
|
|
- // FEATURE: If the page was rendered already once, then use the old
|
|
|
- // version as a placeholder until the new version is rendered at the
|
|
|
- // expected quality.
|
|
|
-
|
|
|
- // FEATURE: If the page can be rendered at low quality (thumbnail) and
|
|
|
- // there is already a higher resolution rendering, then use this one
|
|
|
- // instead of rerendering from scratch again.
|
|
|
-
|
|
|
- // PageView is not layouted.
|
|
|
- if (!pageView.viewport) return;
|
|
|
-
|
|
|
- // Nothing todo.
|
|
|
- if (pageView.isRendered) return;
|
|
|
-
|
|
|
- // Not most important page to render ATM.
|
|
|
- if (renderController.pageToRender() !== pageView) return;
|
|
|
-
|
|
|
- var self = this;
|
|
|
- var viewport;
|
|
|
- if (renderContext = this.renderContextList[pageView.id]) {
|
|
|
- viewport = renderContext.viewport;
|
|
|
-
|
|
|
- // TODO: handle rotation
|
|
|
- if (viewport.height !== pageView.viewport.height ||
|
|
|
- viewport.height !== pageView.viewport.height)
|
|
|
- {
|
|
|
- // The viewport changed -> need to rerender.
|
|
|
- renderContext.abandon = true;
|
|
|
- delete self.renderContextList[pageView.id];
|
|
|
- pageView.createNewCanvas();
|
|
|
- self.render(pageView, renderController);
|
|
|
- } else if (renderContext.state === RenderingStates.PAUSED) {
|
|
|
- // There is already a not finished renderState ->
|
|
|
- logger.debug('RESUME', pageView.id);
|
|
|
- renderContext.resume();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (!renderContext) {
|
|
|
- viewport = pageView.viewport;
|
|
|
- // No rendering data yet -> create a new renderContext and start
|
|
|
- // the rendering process.
|
|
|
-
|
|
|
- var textLayer;
|
|
|
- var textLayerBuilder = pageView.listView.options.textLayerBuilder;
|
|
|
- if (textLayerBuilder) {
|
|
|
- var textLayerDiv = pageView.textLayerDiv = document.createElement("div");
|
|
|
- textLayerDiv.className = 'plv-text-layer text-layer';
|
|
|
- pageView.dom.appendChild(textLayerDiv);
|
|
|
- textLayer = new textLayerBuilder(textLayerDiv);
|
|
|
- this.pdfPage.getTextContent().then(
|
|
|
- function(textContent) {
|
|
|
- textLayer.setTextContent(textContent);
|
|
|
- }
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- var annotationsLayerBuilder = pageView.listView.options.annotationsLayerBuilder;
|
|
|
- if (annotationsLayerBuilder) {
|
|
|
- var annotationsLayerDiv = pageView.annotationsLayerDiv = document.createElement("div");
|
|
|
- annotationsLayerDiv.className = 'plv-annotations-layer annotations-layer';
|
|
|
- pageView.dom.appendChild(annotationsLayerDiv);
|
|
|
- var annotationsLayer = new annotationsLayerBuilder(pageView, annotationsLayerDiv);
|
|
|
- this.pdfPage.getAnnotations().then(
|
|
|
- function(annotations) {
|
|
|
- annotationsLayer.setAnnotations(annotations)
|
|
|
- }
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- var highlightsLayerBuilder = pageView.listView.options.highlightsLayerBuilder;
|
|
|
- if (highlightsLayerBuilder) {
|
|
|
- var highlightsLayerDiv = pageView.highlightsLayerDiv = document.createElement("div");
|
|
|
- highlightsLayerDiv.className = 'plv-highlights-layer highlights-layer';
|
|
|
- pageView.dom.appendChild(highlightsLayerDiv);
|
|
|
- pageView.highlightsLayer = new highlightsLayerBuilder(pageView, highlightsLayerDiv);
|
|
|
- }
|
|
|
-
|
|
|
- renderContext = {
|
|
|
- canvasContext: pageView.getCanvasContext(),
|
|
|
- viewport: viewport,
|
|
|
- textLayer: textLayer,
|
|
|
- continueCallback: function pdfViewContinueCallback(cont) {
|
|
|
- if (renderContext.abandon) {
|
|
|
- logger.debug("ABANDON", pageView.id);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (renderController.pageToRender() !== pageView) {
|
|
|
- logger.debug('PAUSE', pageView.id);
|
|
|
- renderContext.state = RenderingStates.PAUSED;
|
|
|
- renderContext.resume = function resumeCallback() {
|
|
|
- renderContext.state = RenderingStates.RUNNING;
|
|
|
- cont();
|
|
|
- };
|
|
|
- return;
|
|
|
- }
|
|
|
- logger.debug('CONT', pageView.id);
|
|
|
- cont();
|
|
|
- }
|
|
|
- };
|
|
|
- this.renderContextList[pageView.id] = renderContext;
|
|
|
-
|
|
|
- logger.debug("BEGIN", pageView.id);
|
|
|
- renderContext.renderPromise = this.pdfPage.render(renderContext).promise;
|
|
|
- renderContext.renderPromise.then(
|
|
|
- function pdfPageRenderCallback() {
|
|
|
- logger.debug('DONE', pageView.id);
|
|
|
- pageView.isRendered = true;
|
|
|
- renderController.finishedRendering(pageView);
|
|
|
- },
|
|
|
- failDumper
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- return renderContext.renderPromise;
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-function PDFListView(mainDiv, options) {
|
|
|
- if (typeof(options) != "object") {
|
|
|
- options = {};
|
|
|
- }
|
|
|
- if (typeof(options.logLevel) != "number") {
|
|
|
- options.logLevel = Logger.INFO;
|
|
|
- }
|
|
|
- logger.logLevel = options.logLevel;
|
|
|
-
|
|
|
- var self = this;
|
|
|
-
|
|
|
- this.listView = new ListView(mainDiv, options);
|
|
|
- this.listView.ondblclick = function(e) {
|
|
|
- if (options.ondblclick) {
|
|
|
- options.ondblclick.call(self, e);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- this.renderController = new RenderController();
|
|
|
- this.renderController.addListView(this.listView);
|
|
|
- this.renderController.updateRenderList();
|
|
|
-
|
|
|
-
|
|
|
- mainDiv.addEventListener('scroll', function() {
|
|
|
- // This will update the list AND start rendering if needed.
|
|
|
- self.renderController.updateRenderList();
|
|
|
- });
|
|
|
-
|
|
|
- window.addEventListener('resize', function() {
|
|
|
- // Check if the scale changed due to the resizing.
|
|
|
- if (self.listView.calculateScale()) {
|
|
|
- // Update the layout and start rendering. Changing the layout
|
|
|
- // of the PageView makes it rendering stop.
|
|
|
- self.listView.layout();
|
|
|
- self.renderController.updateRenderList();
|
|
|
- }
|
|
|
- });
|
|
|
-}
|
|
|
-
|
|
|
-PDFListView.prototype = {
|
|
|
- loadPdf: function(url, onProgress) {
|
|
|
- this.doc = new Document(url, null, onProgress);
|
|
|
- var self = this;
|
|
|
- var promise = this.doc.initialized;
|
|
|
- promise.then(function() {
|
|
|
- logger.debug('LOADED');
|
|
|
- self.listView.setDocument(self.doc);
|
|
|
- self.renderController.updateRenderList();
|
|
|
- }, failDumper);
|
|
|
- return promise;
|
|
|
- },
|
|
|
-
|
|
|
- getScale: function() {
|
|
|
- return this.listView.getScale();
|
|
|
- },
|
|
|
-
|
|
|
- getScaleMode: function() {
|
|
|
- return this.listView.getScaleMode()
|
|
|
- },
|
|
|
-
|
|
|
- setScaleMode: function(scaleMode, scale) {
|
|
|
- this.listView.setScaleMode(scaleMode, scale);
|
|
|
- this.renderController.updateRenderList();
|
|
|
- },
|
|
|
-
|
|
|
- setScale: function(scale) {
|
|
|
- this.listView.setScale(scale);
|
|
|
- this.renderController.updateRenderList();
|
|
|
- },
|
|
|
-
|
|
|
- setToAutoScale: function() {
|
|
|
- this.listView.setToAutoScale();
|
|
|
- this.renderController.updateRenderList();
|
|
|
- },
|
|
|
-
|
|
|
- setToFitWidth: function() {
|
|
|
- this.listView.setToFitWidth();
|
|
|
- this.renderController.updateRenderList();
|
|
|
- },
|
|
|
-
|
|
|
- setToFitHeight: function() {
|
|
|
- this.listView.setToFitHeight();
|
|
|
- this.renderController.updateRenderList();
|
|
|
- },
|
|
|
-
|
|
|
- onResize: function() {
|
|
|
- this.renderController.onResize();
|
|
|
- },
|
|
|
-
|
|
|
- getPdfPosition: function(fromTop) {
|
|
|
- return this.listView.getPdfPosition(fromTop);
|
|
|
- },
|
|
|
-
|
|
|
- setPdfPosition: function(pdfPosition, fromTop) {
|
|
|
- this.listView.setPdfPosition(pdfPosition, fromTop);
|
|
|
- },
|
|
|
-
|
|
|
- setHighlights: function(highlights, fromTop) {
|
|
|
- this.listView.setHighlights(highlights, fromTop);
|
|
|
- },
|
|
|
-
|
|
|
- clearHighlights: function() {
|
|
|
- this.listView.clearHighlights();
|
|
|
- }
|
|
|
-};
|
|
|
-PDFListView.Logger = Logger;
|
|
|
-
|
|
|
-return PDFListView;
|
|
|
-
|
|
|
-});
|
|
|
-
|