Merge pull request #5552 from yurydelendik/pdfpageview

Creates/refactors PDFPageView class
This commit is contained in:
Brendan Dahl 2015-01-05 11:26:36 -08:00
commit e93cf5ca97
21 changed files with 1098 additions and 937 deletions

View File

@ -0,0 +1,46 @@
<!DOCTYPE html>
<!--
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.
-->
<html dir="ltr" mozdisallowselectionprint moznomarginboxes>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="google" content="notranslate">
<title>PDF.js page viewer using built components</title>
<style>
body {
background-color: #808080;
margin: 0;
padding: 0;
}
</style>
<link rel="stylesheet" href="../../build/components/pdf_viewer.css">
<!-- for legacy browsers -->
<script src="../../build/components/compatibility.js"></script>
<script src="../../build/pdf.js"></script>
<script src="../../build/components/pdf_viewer.js"></script>
</head>
<body tabindex="1">
<div id="pageContainer" class="pdfPage"></div>
<script src="pageviewer.js"></script>
</body>
</html>

View File

@ -0,0 +1,58 @@
/* 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.
*/
'use strict';
if (!PDFJS.PDFViewer || !PDFJS.getDocument) {
alert('Please build the library and components using\n' +
' `node make generic components`');
}
// In cases when the pdf.worker.js is located at the different folder than the
// pdf.js's one, or the pdf.js is executed via eval(), the workerSrc property
// shall be specified.
//
// PDFJS.workerSrc = '../../build/pdf.worker.js';
// Some PDFs need external cmaps.
//
// PDFJS.cMapUrl = '../../external/bcmaps/';
// PDFJS.cMapPacked = true;
var DEFAULT_URL = '../../web/compressed.tracemonkey-pldi-09.pdf';
var PAGE_TO_VIEW = 1;
var SCALE = 1.0;
var container = document.getElementById('pageContainer');
// Loading document.
PDFJS.getDocument(DEFAULT_URL).then(function (pdfDocument) {
// Document loaded, retrieving the page.
return pdfDocument.getPage(PAGE_TO_VIEW).then(function (pdfPage) {
// Creating the page view with default parameters.
var pdfPageView = new PDFJS.PDFPageView({
container: container,
id: PAGE_TO_VIEW,
scale: SCALE,
defaultViewport: pdfPage.getViewport(SCALE),
// We can enable text/annotations layers, if needed
textLayerFactory: new PDFJS.DefaultTextLayerFactory(),
annotationsLayerFactory: new PDFJS.DefaultAnnotationsLayerFactory()
});
// Associates the actual page with the view, and drawing it
pdfPageView.setPdfPage(pdfPage);
return pdfPageView.draw();
});
});

View File

@ -1,43 +0,0 @@
body {
font-family: arial, verdana, sans-serif;
}
/* Allow absolute positioning of the canvas and textLayer in the page. They
will be the same size and will be placed on top of each other. */
.pdfPage {
position: relative;
overflow: visible;
border: 1px solid #000000;
}
.pdfPage > canvas {
position: absolute;
top: 0;
left: 0;
}
/* CSS classes used by TextLayerBuilder to style the text layer divs */
/* This stuff is important! Otherwise when you select the text,
the text in the divs will show up! */
::selection { background:rgba(0,0,255,0.3); }
::-moz-selection { background:rgba(0,0,255,0.3); }
.textLayer {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
color: #000;
font-family: sans-serif;
overflow: hidden;
}
.textLayer > div {
color: transparent;
position: absolute;
line-height: 1;
white-space: pre;
cursor: text;
}

View File

@ -1,26 +0,0 @@
<html>
<head>
<title>Minimal pdf.js text-selection demo</title>
<link href="css/minimal.css" rel="stylesheet" media="screen" />
<!-- you will need to run "node make generic" first before you can use this -->
<script src="../../build/generic/build/pdf.js"></script>
<!-- These files are viewer components that you will need to get text-selection to work -->
<script src="../../web/ui_utils.js"></script>
<script src="../../web/text_layer_builder.js"></script>
<script>
// Specify the main script used to create a new PDF.JS web worker.
// In production, change this to point to the combined `pdf.js` file.
</script>
<script src="js/minimal.js"></script>
</head>
<body>
This is a minimal pdf.js text-selection demo. The existing minimal-example shows you how to render a PDF, but not
how to enable text-selection. This example shows you how to do both. <br /><br />
<div id="pdfContainer">
</div>
</body>
</html>

View File

@ -1,97 +0,0 @@
// Minimal PDF rendering and text-selection example using PDF.js by Vivin Suresh Paliath (http://vivin.net)
// This example uses a built version of PDF.js that contains all modules that it requires.
//
// The problem with understanding text selection was that the text selection code has heavily intertwined
// with viewer.html and viewer.js. I have extracted the parts I need out of viewer.js into a separate file
// which contains the bare minimum required to implement text selection. The key component is TextLayerBuilder,
// which is the object that handles the creation of text-selection divs. I have added this code as an external
// resource.
//
// This demo uses a PDF that only has one page. You can render other pages if you wish, but the focus here is
// just to show you how you can render a PDF with text selection. Hence the code only loads up one page.
//
// The CSS used here is also very important since it sets up the CSS for the text layer divs overlays that
// you actually end up selecting.
//
// NOTE: The original example was changed to remove jQuery usage, re-structure and add more comments.
window.onload = function () {
if (typeof PDFJS === 'undefined') {
alert('Built version of pdf.js is not found\nPlease run `node make generic`');
return;
}
var scale = 1.5; //Set this to whatever you want. This is basically the "zoom" factor for the PDF.
PDFJS.workerSrc = '../../build/generic/build/pdf.worker.js';
function loadPdf(pdfPath) {
var pdf = PDFJS.getDocument(pdfPath);
return pdf.then(renderPdf);
}
function renderPdf(pdf) {
return pdf.getPage(1).then(renderPage);
}
function renderPage(page) {
var viewport = page.getViewport(scale);
// Create and append the 'pdf-page' div to the pdf container.
var pdfPage = document.createElement('div');
pdfPage.className = 'pdfPage';
var pdfContainer = document.getElementById('pdfContainer');
pdfContainer.appendChild(pdfPage);
// Set the canvas height and width to the height and width of the viewport.
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
// The following few lines of code set up scaling on the context, if we are
// on a HiDPI display.
var outputScale = getOutputScale(context);
canvas.width = (Math.floor(viewport.width) * outputScale.sx) | 0;
canvas.height = (Math.floor(viewport.height) * outputScale.sy) | 0;
context._scaleX = outputScale.sx;
context._scaleY = outputScale.sy;
if (outputScale.scaled) {
context.scale(outputScale.sx, outputScale.sy);
}
// The page, canvas and text layer elements will have the same size.
canvas.style.width = Math.floor(viewport.width) + 'px';
canvas.style.height = Math.floor(viewport.height) + 'px';
pdfPage.style.width = canvas.style.width;
pdfPage.style.height = canvas.style.height;
pdfPage.appendChild(canvas);
var textLayerDiv = document.createElement('div');
textLayerDiv.className = 'textLayer';
textLayerDiv.style.width = canvas.style.width;
textLayerDiv.style.height = canvas.style.height;
pdfPage.appendChild(textLayerDiv);
// Painting the canvas...
var renderContext = {
canvasContext: context,
viewport: viewport
};
var renderTask = page.render(renderContext);
// ... and at the same time, getting the text and creating the text layer.
var textLayerPromise = page.getTextContent().then(function (textContent) {
var textLayerBuilder = new TextLayerBuilder({
textLayerDiv: textLayerDiv,
viewport: viewport,
pageIndex: 0
});
textLayerBuilder.setTextContent(textContent);
});
// We might be interested when rendering complete and text layer is built.
return Promise.all([renderTask.promise, textLayerPromise]);
}
loadPdf('pdf/TestDocument.pdf');
};

View File

@ -0,0 +1,177 @@
/* -*- 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 PDFJS, CustomStyle, mozL10n */
'use strict';
/**
* @typedef {Object} AnnotationsLayerBuilderOptions
* @property {HTMLDivElement} pageDiv
* @property {PDFPage} pdfPage
* @property {IPDFLinkService} linkService
*/
/**
* @class
*/
var AnnotationsLayerBuilder = (function AnnotationsLayerBuilderClosure() {
/**
* @param {AnnotationsLayerBuilderOptions} options
* @constructs AnnotationsLayerBuilder
*/
function AnnotationsLayerBuilder(options) {
this.pageDiv = options.pageDiv;
this.pdfPage = options.pdfPage;
this.linkService = options.linkService;
this.div = null;
}
AnnotationsLayerBuilder.prototype =
/** @lends AnnotationsLayerBuilder.prototype */ {
/**
* @param {PageViewport} viewport
*/
setupAnnotations:
function AnnotationsLayerBuilder_setupAnnotations(viewport) {
function bindLink(link, dest) {
link.href = linkService.getDestinationHash(dest);
link.onclick = function annotationsLayerBuilderLinksOnclick() {
if (dest) {
linkService.navigateTo(dest);
}
return false;
};
if (dest) {
link.className = 'internalLink';
}
}
function bindNamedAction(link, action) {
link.href = linkService.getAnchorUrl('');
link.onclick = function annotationsLayerBuilderNamedActionOnClick() {
linkService.executeNamedAction(action);
return false;
};
link.className = 'internalLink';
}
var linkService = this.linkService;
var pdfPage = this.pdfPage;
var self = this;
pdfPage.getAnnotations().then(function (annotationsData) {
viewport = viewport.clone({ dontFlip: true });
var transform = viewport.transform;
var transformStr = 'matrix(' + transform.join(',') + ')';
var data, element, i, ii;
if (self.div) {
// If an annotationLayer already exists, refresh its children's
// transformation matrices
for (i = 0, ii = annotationsData.length; i < ii; i++) {
data = annotationsData[i];
element = self.div.querySelector(
'[data-annotation-id="' + data.id + '"]');
if (element) {
CustomStyle.setProp('transform', element, transformStr);
}
}
// See PDFPageView.reset()
self.div.removeAttribute('hidden');
} else {
for (i = 0, ii = annotationsData.length; i < ii; i++) {
data = annotationsData[i];
if (!data || !data.hasHtml) {
continue;
}
element = PDFJS.AnnotationUtils.getHtmlElement(data,
pdfPage.commonObjs);
element.setAttribute('data-annotation-id', data.id);
if (typeof mozL10n !== 'undefined') {
mozL10n.translate(element);
}
var rect = data.rect;
var view = pdfPage.view;
rect = PDFJS.Util.normalizeRect([
rect[0],
view[3] - rect[1] + view[1],
rect[2],
view[3] - rect[3] + view[1]
]);
element.style.left = rect[0] + 'px';
element.style.top = rect[1] + 'px';
element.style.position = 'absolute';
CustomStyle.setProp('transform', element, transformStr);
var transformOriginStr = -rect[0] + 'px ' + -rect[1] + 'px';
CustomStyle.setProp('transformOrigin', element, transformOriginStr);
if (data.subtype === 'Link' && !data.url) {
var link = element.getElementsByTagName('a')[0];
if (link) {
if (data.action) {
bindNamedAction(link, data.action);
} else {
bindLink(link, ('dest' in data) ? data.dest : null);
}
}
}
if (!self.div) {
var annotationLayerDiv = document.createElement('div');
annotationLayerDiv.className = 'annotationLayer';
self.pageDiv.appendChild(annotationLayerDiv);
self.div = annotationLayerDiv;
}
self.div.appendChild(element);
}
}
});
},
hide: function () {
if (!this.div) {
return;
}
this.div.setAttribute('hidden', 'true');
}
};
return AnnotationsLayerBuilder;
})();
/**
* @constructor
* @implements IPDFAnnotationsLayerFactory
*/
function DefaultAnnotationsLayerFactory() {}
DefaultAnnotationsLayerFactory.prototype = {
/**
* @param {HTMLDivElement} pageDiv
* @param {PDFPage} pdfPage
* @returns {AnnotationsLayerBuilder}
*/
createAnnotationsLayerBuilder: function (pageDiv, pdfPage) {
return new AnnotationsLayerBuilder({
pageDiv: pageDiv,
pdfPage: pdfPage
});
}
};

View File

@ -67,19 +67,35 @@ IRenderableView.prototype = {
*/
get renderingState() {},
/**
* @param {function} callback - The draw completion callback.
* @returns {Promise} Resolved on draw completion.
*/
draw: function (callback) {},
draw: function () {},
resume: function () {},
};
/**
* @interface
*/
function ILastScrollSource() {}
ILastScrollSource.prototype = {
function IPDFTextLayerFactory() {}
IPDFTextLayerFactory.prototype = {
/**
* @returns {number}
* @param {HTMLDivElement} textLayerDiv
* @param {number} pageIndex
* @param {PageViewport} viewport
* @returns {TextLayerBuilder}
*/
get lastScroll() {},
createTextLayerBuilder: function (textLayerDiv, pageIndex, viewport) {}
};
/**
* @interface
*/
function IPDFAnnotationsLayerFactory() {}
IPDFAnnotationsLayerFactory.prototype = {
/**
* @param {HTMLDivElement} pageDiv
* @param {PDFPage} pdfPage
* @returns {AnnotationsLayerBuilder}
*/
createAnnotationsLayerBuilder: function (pageDiv, pdfPage) {}
};

View File

@ -1,610 +0,0 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 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 RenderingStates, PDFJS, mozL10n, CustomStyle, getOutputScale, Stats,
CSS_UNITS */
'use strict';
/**
* @constructor
* @param {HTMLDivElement} container - The viewer element.
* @param {number} id - The page unique ID (normally its number).
* @param {number} scale - The page scale display.
* @param {PageViewport} defaultViewport - The page viewport.
* @param {IPDFLinkService} linkService - The navigation/linking service.
* @param {PDFRenderingQueue} renderingQueue - The rendering queue object.
* @param {Cache} cache - The page cache.
* @param {PDFPageSource} pageSource
* @param {PDFViewer} viewer
*
* @implements {IRenderableView}
*/
var PageView = function pageView(container, id, scale, defaultViewport,
linkService, renderingQueue, cache,
pageSource, viewer) {
this.id = id;
this.renderingId = 'page' + id;
this.rotation = 0;
this.scale = scale || 1.0;
this.viewport = defaultViewport;
this.pdfPageRotate = defaultViewport.rotation;
this.hasRestrictedScaling = false;
this.linkService = linkService;
this.renderingQueue = renderingQueue;
this.cache = cache;
this.pageSource = pageSource;
this.viewer = viewer;
this.renderingState = RenderingStates.INITIAL;
this.resume = null;
this.textLayer = null;
this.zoomLayer = null;
this.annotationLayer = null;
var anchor = document.createElement('a');
anchor.name = '' + this.id;
var div = this.el = document.createElement('div');
div.id = 'pageContainer' + this.id;
div.className = 'page';
div.style.width = Math.floor(this.viewport.width) + 'px';
div.style.height = Math.floor(this.viewport.height) + 'px';
container.appendChild(anchor);
container.appendChild(div);
this.setPdfPage = function pageViewSetPdfPage(pdfPage) {
this.pdfPage = pdfPage;
this.pdfPageRotate = pdfPage.rotate;
var totalRotation = (this.rotation + this.pdfPageRotate) % 360;
this.viewport = pdfPage.getViewport(this.scale * CSS_UNITS, totalRotation);
this.stats = pdfPage.stats;
this.reset();
};
this.destroy = function pageViewDestroy() {
this.zoomLayer = null;
this.reset();
if (this.pdfPage) {
this.pdfPage.destroy();
}
};
this.reset = function pageViewReset(keepAnnotations) {
if (this.renderTask) {
this.renderTask.cancel();
}
this.resume = null;
this.renderingState = RenderingStates.INITIAL;
div.style.width = Math.floor(this.viewport.width) + 'px';
div.style.height = Math.floor(this.viewport.height) + 'px';
var childNodes = div.childNodes;
for (var i = div.childNodes.length - 1; i >= 0; i--) {
var node = childNodes[i];
if ((this.zoomLayer && this.zoomLayer === node) ||
(keepAnnotations && this.annotationLayer === node)) {
continue;
}
div.removeChild(node);
}
div.removeAttribute('data-loaded');
if (keepAnnotations) {
if (this.annotationLayer) {
// Hide annotationLayer until all elements are resized
// so they are not displayed on the already-resized page
this.annotationLayer.setAttribute('hidden', 'true');
}
} else {
this.annotationLayer = null;
}
if (this.canvas) {
// Zeroing the width and height causes Firefox to release graphics
// resources immediately, which can greatly reduce memory consumption.
this.canvas.width = 0;
this.canvas.height = 0;
delete this.canvas;
}
this.loadingIconDiv = document.createElement('div');
this.loadingIconDiv.className = 'loadingIcon';
div.appendChild(this.loadingIconDiv);
};
this.update = function pageViewUpdate(scale, rotation) {
this.scale = scale || this.scale;
if (typeof rotation !== 'undefined') {
this.rotation = rotation;
}
var totalRotation = (this.rotation + this.pdfPageRotate) % 360;
this.viewport = this.viewport.clone({
scale: this.scale * CSS_UNITS,
rotation: totalRotation
});
var isScalingRestricted = false;
if (this.canvas && PDFJS.maxCanvasPixels > 0) {
var ctx = this.canvas.getContext('2d');
var outputScale = getOutputScale(ctx);
var pixelsInViewport = this.viewport.width * this.viewport.height;
var maxScale = Math.sqrt(PDFJS.maxCanvasPixels / pixelsInViewport);
if (((Math.floor(this.viewport.width) * outputScale.sx) | 0) *
((Math.floor(this.viewport.height) * outputScale.sy) | 0) >
PDFJS.maxCanvasPixels) {
isScalingRestricted = true;
}
}
if (this.canvas &&
(PDFJS.useOnlyCssZoom ||
(this.hasRestrictedScaling && isScalingRestricted))) {
this.cssTransform(this.canvas, true);
return;
} else if (this.canvas && !this.zoomLayer) {
this.zoomLayer = this.canvas.parentNode;
this.zoomLayer.style.position = 'absolute';
}
if (this.zoomLayer) {
this.cssTransform(this.zoomLayer.firstChild);
}
this.reset(true);
};
this.cssTransform = function pageCssTransform(canvas, redrawAnnotations) {
// Scale canvas, canvas wrapper, and page container.
var width = this.viewport.width;
var height = this.viewport.height;
canvas.style.width = canvas.parentNode.style.width = div.style.width =
Math.floor(width) + 'px';
canvas.style.height = canvas.parentNode.style.height = div.style.height =
Math.floor(height) + 'px';
// The canvas may have been originally rotated, so rotate relative to that.
var relativeRotation = this.viewport.rotation - canvas._viewport.rotation;
var absRotation = Math.abs(relativeRotation);
var scaleX = 1, scaleY = 1;
if (absRotation === 90 || absRotation === 270) {
// Scale x and y because of the rotation.
scaleX = height / width;
scaleY = width / height;
}
var cssTransform = 'rotate(' + relativeRotation + 'deg) ' +
'scale(' + scaleX + ',' + scaleY + ')';
CustomStyle.setProp('transform', canvas, cssTransform);
if (this.textLayer) {
// Rotating the text layer is more complicated since the divs inside the
// the text layer are rotated.
// TODO: This could probably be simplified by drawing the text layer in
// one orientation then rotating overall.
var textLayerViewport = this.textLayer.viewport;
var textRelativeRotation = this.viewport.rotation -
textLayerViewport.rotation;
var textAbsRotation = Math.abs(textRelativeRotation);
var scale = width / textLayerViewport.width;
if (textAbsRotation === 90 || textAbsRotation === 270) {
scale = width / textLayerViewport.height;
}
var textLayerDiv = this.textLayer.textLayerDiv;
var transX, transY;
switch (textAbsRotation) {
case 0:
transX = transY = 0;
break;
case 90:
transX = 0;
transY = '-' + textLayerDiv.style.height;
break;
case 180:
transX = '-' + textLayerDiv.style.width;
transY = '-' + textLayerDiv.style.height;
break;
case 270:
transX = '-' + textLayerDiv.style.width;
transY = 0;
break;
default:
console.error('Bad rotation value.');
break;
}
CustomStyle.setProp('transform', textLayerDiv,
'rotate(' + textAbsRotation + 'deg) ' +
'scale(' + scale + ', ' + scale + ') ' +
'translate(' + transX + ', ' + transY + ')');
CustomStyle.setProp('transformOrigin', textLayerDiv, '0% 0%');
}
if (redrawAnnotations && this.annotationLayer) {
setupAnnotations(div, this.pdfPage, this.viewport);
}
};
Object.defineProperty(this, 'width', {
get: function PageView_getWidth() {
return this.viewport.width;
},
enumerable: true
});
Object.defineProperty(this, 'height', {
get: function PageView_getHeight() {
return this.viewport.height;
},
enumerable: true
});
var self = this;
function setupAnnotations(pageDiv, pdfPage, viewport) {
function bindLink(link, dest) {
link.href = linkService.getDestinationHash(dest);
link.onclick = function pageViewSetupLinksOnclick() {
if (dest) {
linkService.navigateTo(dest);
}
return false;
};
if (dest) {
link.className = 'internalLink';
}
}
function bindNamedAction(link, action) {
link.href = linkService.getAnchorUrl('');
link.onclick = function pageViewSetupNamedActionOnClick() {
linkService.executeNamedAction(action);
return false;
};
link.className = 'internalLink';
}
pdfPage.getAnnotations().then(function(annotationsData) {
viewport = viewport.clone({ dontFlip: true });
var transform = viewport.transform;
var transformStr = 'matrix(' + transform.join(',') + ')';
var data, element, i, ii;
if (self.annotationLayer) {
// If an annotationLayer already exists, refresh its children's
// transformation matrices
for (i = 0, ii = annotationsData.length; i < ii; i++) {
data = annotationsData[i];
element = self.annotationLayer.querySelector(
'[data-annotation-id="' + data.id + '"]');
if (element) {
CustomStyle.setProp('transform', element, transformStr);
}
}
// See this.reset()
self.annotationLayer.removeAttribute('hidden');
} else {
for (i = 0, ii = annotationsData.length; i < ii; i++) {
data = annotationsData[i];
if (!data || !data.hasHtml) {
continue;
}
element = PDFJS.AnnotationUtils.getHtmlElement(data,
pdfPage.commonObjs);
element.setAttribute('data-annotation-id', data.id);
mozL10n.translate(element);
var rect = data.rect;
var view = pdfPage.view;
rect = PDFJS.Util.normalizeRect([
rect[0],
view[3] - rect[1] + view[1],
rect[2],
view[3] - rect[3] + view[1]
]);
element.style.left = rect[0] + 'px';
element.style.top = rect[1] + 'px';
element.style.position = 'absolute';
CustomStyle.setProp('transform', element, transformStr);
var transformOriginStr = -rect[0] + 'px ' + -rect[1] + 'px';
CustomStyle.setProp('transformOrigin', element, transformOriginStr);
if (data.subtype === 'Link' && !data.url) {
var link = element.getElementsByTagName('a')[0];
if (link) {
if (data.action) {
bindNamedAction(link, data.action);
} else {
bindLink(link, ('dest' in data) ? data.dest : null);
}
}
}
if (!self.annotationLayer) {
var annotationLayerDiv = document.createElement('div');
annotationLayerDiv.className = 'annotationLayer';
pageDiv.appendChild(annotationLayerDiv);
self.annotationLayer = annotationLayerDiv;
}
self.annotationLayer.appendChild(element);
}
}
});
}
this.getPagePoint = function pageViewGetPagePoint(x, y) {
return this.viewport.convertToPdfPoint(x, y);
};
this.draw = function pageviewDraw(callback) {
var pdfPage = this.pdfPage;
if (this.pagePdfPromise) {
return;
}
if (!pdfPage) {
var promise = this.pageSource.getPage();
promise.then(function(pdfPage) {
delete this.pagePdfPromise;
this.setPdfPage(pdfPage);
this.draw(callback);
}.bind(this));
this.pagePdfPromise = promise;
return;
}
if (this.renderingState !== RenderingStates.INITIAL) {
console.error('Must be in new state before drawing');
}
this.renderingState = RenderingStates.RUNNING;
var viewport = this.viewport;
// Wrap the canvas so if it has a css transform for highdpi the overflow
// will be hidden in FF.
var canvasWrapper = document.createElement('div');
canvasWrapper.style.width = div.style.width;
canvasWrapper.style.height = div.style.height;
canvasWrapper.classList.add('canvasWrapper');
var canvas = document.createElement('canvas');
canvas.id = 'page' + this.id;
canvasWrapper.appendChild(canvas);
if (this.annotationLayer) {
// annotationLayer needs to stay on top
div.insertBefore(canvasWrapper, this.annotationLayer);
} else {
div.appendChild(canvasWrapper);
}
this.canvas = canvas;
var ctx = canvas.getContext('2d');
var outputScale = getOutputScale(ctx);
if (PDFJS.useOnlyCssZoom) {
var actualSizeViewport = viewport.clone({ scale: CSS_UNITS });
// Use a scale that will make the canvas be the original intended size
// of the page.
outputScale.sx *= actualSizeViewport.width / viewport.width;
outputScale.sy *= actualSizeViewport.height / viewport.height;
outputScale.scaled = true;
}
if (PDFJS.maxCanvasPixels > 0) {
var pixelsInViewport = viewport.width * viewport.height;
var maxScale = Math.sqrt(PDFJS.maxCanvasPixels / pixelsInViewport);
if (outputScale.sx > maxScale || outputScale.sy > maxScale) {
outputScale.sx = maxScale;
outputScale.sy = maxScale;
outputScale.scaled = true;
this.hasRestrictedScaling = true;
} else {
this.hasRestrictedScaling = false;
}
}
canvas.width = (Math.floor(viewport.width) * outputScale.sx) | 0;
canvas.height = (Math.floor(viewport.height) * outputScale.sy) | 0;
canvas.style.width = Math.floor(viewport.width) + 'px';
canvas.style.height = Math.floor(viewport.height) + 'px';
// Add the viewport so it's known what it was originally drawn with.
canvas._viewport = viewport;
var textLayerDiv = null;
var textLayer = null;
if (!PDFJS.disableTextLayer) {
textLayerDiv = document.createElement('div');
textLayerDiv.className = 'textLayer';
textLayerDiv.style.width = canvas.style.width;
textLayerDiv.style.height = canvas.style.height;
if (this.annotationLayer) {
// annotationLayer needs to stay on top
div.insertBefore(textLayerDiv, this.annotationLayer);
} else {
div.appendChild(textLayerDiv);
}
textLayer = this.viewer.createTextLayerBuilder(textLayerDiv, this.id - 1,
this.viewport);
}
this.textLayer = textLayer;
// TODO(mack): use data attributes to store these
ctx._scaleX = outputScale.sx;
ctx._scaleY = outputScale.sy;
if (outputScale.scaled) {
ctx.scale(outputScale.sx, outputScale.sy);
}
// Rendering area
var self = this;
function pageViewDrawCallback(error) {
// The renderTask may have been replaced by a new one, so only remove the
// reference to the renderTask if it matches the one that is triggering
// this callback.
if (renderTask === self.renderTask) {
self.renderTask = null;
}
if (error === 'cancelled') {
return;
}
self.renderingState = RenderingStates.FINISHED;
if (self.loadingIconDiv) {
div.removeChild(self.loadingIconDiv);
delete self.loadingIconDiv;
}
if (self.zoomLayer) {
div.removeChild(self.zoomLayer);
self.zoomLayer = null;
}
self.error = error;
self.stats = pdfPage.stats;
self.updateStats();
if (self.onAfterDraw) {
self.onAfterDraw();
}
var event = document.createEvent('CustomEvent');
event.initCustomEvent('pagerender', true, true, {
pageNumber: pdfPage.pageNumber
});
div.dispatchEvent(event);
callback();
}
var renderContext = {
canvasContext: ctx,
viewport: this.viewport,
// intent: 'default', // === 'display'
continueCallback: function pdfViewcContinueCallback(cont) {
if (!self.renderingQueue.isHighestPriority(self)) {
self.renderingState = RenderingStates.PAUSED;
self.resume = function resumeCallback() {
self.renderingState = RenderingStates.RUNNING;
cont();
};
return;
}
cont();
}
};
var renderTask = this.renderTask = this.pdfPage.render(renderContext);
this.renderTask.promise.then(
function pdfPageRenderCallback() {
pageViewDrawCallback(null);
if (textLayer) {
self.pdfPage.getTextContent().then(
function textContentResolved(textContent) {
textLayer.setTextContent(textContent);
}
);
}
},
function pdfPageRenderError(error) {
pageViewDrawCallback(error);
}
);
setupAnnotations(div, pdfPage, this.viewport);
div.setAttribute('data-loaded', true);
// Add the page to the cache at the start of drawing. That way it can be
// evicted from the cache and destroyed even if we pause its rendering.
cache.push(this);
};
this.beforePrint = function pageViewBeforePrint() {
var pdfPage = this.pdfPage;
var viewport = pdfPage.getViewport(1);
// Use the same hack we use for high dpi displays for printing to get better
// output until bug 811002 is fixed in FF.
var PRINT_OUTPUT_SCALE = 2;
var canvas = document.createElement('canvas');
canvas.width = Math.floor(viewport.width) * PRINT_OUTPUT_SCALE;
canvas.height = Math.floor(viewport.height) * PRINT_OUTPUT_SCALE;
canvas.style.width = (PRINT_OUTPUT_SCALE * viewport.width) + 'pt';
canvas.style.height = (PRINT_OUTPUT_SCALE * viewport.height) + 'pt';
var cssScale = 'scale(' + (1 / PRINT_OUTPUT_SCALE) + ', ' +
(1 / PRINT_OUTPUT_SCALE) + ')';
CustomStyle.setProp('transform' , canvas, cssScale);
CustomStyle.setProp('transformOrigin' , canvas, '0% 0%');
var printContainer = document.getElementById('printContainer');
var canvasWrapper = document.createElement('div');
canvasWrapper.style.width = viewport.width + 'pt';
canvasWrapper.style.height = viewport.height + 'pt';
canvasWrapper.appendChild(canvas);
printContainer.appendChild(canvasWrapper);
canvas.mozPrintCallback = function(obj) {
var ctx = obj.context;
ctx.save();
ctx.fillStyle = 'rgb(255, 255, 255)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore();
ctx.scale(PRINT_OUTPUT_SCALE, PRINT_OUTPUT_SCALE);
var renderContext = {
canvasContext: ctx,
viewport: viewport,
intent: 'print'
};
pdfPage.render(renderContext).promise.then(function() {
// Tell the printEngine that rendering this canvas/page has finished.
obj.done();
}, function(error) {
console.error(error);
// Tell the printEngine that rendering this canvas/page has failed.
// This will make the print proces stop.
if ('abort' in obj) {
obj.abort();
} else {
obj.done();
}
});
};
};
this.updateStats = function pageViewUpdateStats() {
if (!this.stats) {
return;
}
if (PDFJS.pdfBug && Stats.enabled) {
var stats = this.stats;
Stats.add(this.id, stats);
}
};
};

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* globals PDFJS, FirefoxCom, Promise */
/* globals PDFJS, FirefoxCom, Promise, scrollIntoView */
'use strict';
@ -24,6 +24,9 @@ var FindStates = {
FIND_PENDING: 3
};
var FIND_SCROLL_OFFSET_TOP = -50;
var FIND_SCROLL_OFFSET_LEFT = -400;
/**
* Provides "search" or "find" functionality for the PDF.
* This object actually performs the search for a given string.
@ -197,8 +200,6 @@ var PDFFindController = (function PDFFindControllerClosure() {
},
updatePage: function PDFFindController_updatePage(index) {
var page = this.pdfViewer.getPageView(index);
if (this.selected.pageIdx === index) {
// If the page is selected, scroll the page into view, which triggers
// rendering the page, which adds the textLayer. Once the textLayer is
@ -206,6 +207,7 @@ var PDFFindController = (function PDFFindControllerClosure() {
this.pdfViewer.scrollPageIntoView(index + 1);
}
var page = this.pdfViewer.getPageView(index);
if (page.textLayer) {
page.textLayer.updateMatches();
}
@ -309,6 +311,26 @@ var PDFFindController = (function PDFFindControllerClosure() {
}
},
/**
* The method is called back from the text layer when match presentation
* is updated.
* @param {number} pageIndex - page index.
* @param {number} index - match index.
* @param {Array} elements - text layer div elements array.
* @param {number} beginIdx - start index of the div array for the match.
* @param {number} endIdx - end index of the div array for the match.
*/
updateMatchPosition: function PDFFindController_updateMatchPosition(
pageIndex, index, elements, beginIdx, endIdx) {
if (this.selected.matchIdx === index &&
this.selected.pageIdx === pageIndex) {
scrollIntoView(elements[beginIdx], {
top: FIND_SCROLL_OFFSET_TOP,
left: FIND_SCROLL_OFFSET_LEFT
});
}
},
nextPageMatch: function PDFFindController_nextPageMatch() {
if (this.resumePageIdx !== null) {
console.error('There can only be one pending page.');

542
web/pdf_page_view.js Normal file
View File

@ -0,0 +1,542 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 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 RenderingStates, PDFJS, CustomStyle, CSS_UNITS, getOutputScale,
TextLayerBuilder, AnnotationsLayerBuilder, Promise */
'use strict';
var TEXT_LAYER_RENDER_DELAY = 200; // ms
/**
* @typedef {Object} PDFPageViewOptions
* @property {HTMLDivElement} container - The viewer element.
* @property {number} id - The page unique ID (normally its number).
* @property {number} scale - The page scale display.
* @property {PageViewport} defaultViewport - The page viewport.
* @property {PDFRenderingQueue} renderingQueue - The rendering queue object.
* @property {IPDFTextLayerFactory} textLayerFactory
* @property {IPDFAnnotationsLayerFactory} annotationsLayerFactory
*/
/**
* @class
* @implements {IRenderableView}
*/
var PDFPageView = (function PDFPageViewClosure() {
/**
* @constructs PDFPageView
* @param {PDFPageViewOptions} options
*/
function PDFPageView(options) {
var container = options.container;
var id = options.id;
var scale = options.scale;
var defaultViewport = options.defaultViewport;
var renderingQueue = options.renderingQueue;
var textLayerFactory = options.textLayerFactory;
var annotationsLayerFactory = options.annotationsLayerFactory;
this.id = id;
this.renderingId = 'page' + id;
this.rotation = 0;
this.scale = scale || 1.0;
this.viewport = defaultViewport;
this.pdfPageRotate = defaultViewport.rotation;
this.hasRestrictedScaling = false;
this.renderingQueue = renderingQueue;
this.textLayerFactory = textLayerFactory;
this.annotationsLayerFactory = annotationsLayerFactory;
this.renderingState = RenderingStates.INITIAL;
this.resume = null;
this.onBeforeDraw = null;
this.onAfterDraw = null;
this.textLayer = null;
this.zoomLayer = null;
this.annotationLayer = null;
var anchor = document.createElement('a');
anchor.name = '' + this.id;
var div = document.createElement('div');
div.id = 'pageContainer' + this.id;
div.className = 'page';
div.style.width = Math.floor(this.viewport.width) + 'px';
div.style.height = Math.floor(this.viewport.height) + 'px';
this.el = div; // TODO replace 'el' property usage
this.div = div;
container.appendChild(anchor);
container.appendChild(div);
}
PDFPageView.prototype = {
setPdfPage: function PDFPageView_setPdfPage(pdfPage) {
this.pdfPage = pdfPage;
this.pdfPageRotate = pdfPage.rotate;
var totalRotation = (this.rotation + this.pdfPageRotate) % 360;
this.viewport = pdfPage.getViewport(this.scale * CSS_UNITS,
totalRotation);
this.stats = pdfPage.stats;
this.reset();
},
destroy: function PDFPageView_destroy() {
this.zoomLayer = null;
this.reset();
if (this.pdfPage) {
this.pdfPage.destroy();
}
},
reset: function PDFPageView_reset(keepAnnotations) {
if (this.renderTask) {
this.renderTask.cancel();
}
this.resume = null;
this.renderingState = RenderingStates.INITIAL;
var div = this.div;
div.style.width = Math.floor(this.viewport.width) + 'px';
div.style.height = Math.floor(this.viewport.height) + 'px';
var childNodes = div.childNodes;
var currentZoomLayer = this.zoomLayer || null;
var currentAnnotationNode = (keepAnnotations && this.annotationLayer &&
this.annotationLayer.div) || null;
for (var i = div.childNodes.length - 1; i >= 0; i--) {
var node = childNodes[i];
if (currentZoomLayer === node || currentAnnotationNode === node) {
continue;
}
div.removeChild(node);
}
div.removeAttribute('data-loaded');
if (keepAnnotations) {
if (this.annotationLayer) {
// Hide annotationLayer until all elements are resized
// so they are not displayed on the already-resized page
this.annotationLayer.hide();
}
} else {
this.annotationLayer = null;
}
if (this.canvas) {
// Zeroing the width and height causes Firefox to release graphics
// resources immediately, which can greatly reduce memory consumption.
this.canvas.width = 0;
this.canvas.height = 0;
delete this.canvas;
}
this.loadingIconDiv = document.createElement('div');
this.loadingIconDiv.className = 'loadingIcon';
div.appendChild(this.loadingIconDiv);
},
update: function PDFPageView_update(scale, rotation) {
this.scale = scale || this.scale;
if (typeof rotation !== 'undefined') {
this.rotation = rotation;
}
var totalRotation = (this.rotation + this.pdfPageRotate) % 360;
this.viewport = this.viewport.clone({
scale: this.scale * CSS_UNITS,
rotation: totalRotation
});
var isScalingRestricted = false;
if (this.canvas && PDFJS.maxCanvasPixels > 0) {
var ctx = this.canvas.getContext('2d');
var outputScale = getOutputScale(ctx);
var pixelsInViewport = this.viewport.width * this.viewport.height;
var maxScale = Math.sqrt(PDFJS.maxCanvasPixels / pixelsInViewport);
if (((Math.floor(this.viewport.width) * outputScale.sx) | 0) *
((Math.floor(this.viewport.height) * outputScale.sy) | 0) >
PDFJS.maxCanvasPixels) {
isScalingRestricted = true;
}
}
if (this.canvas &&
(PDFJS.useOnlyCssZoom ||
(this.hasRestrictedScaling && isScalingRestricted))) {
this.cssTransform(this.canvas, true);
return;
} else if (this.canvas && !this.zoomLayer) {
this.zoomLayer = this.canvas.parentNode;
this.zoomLayer.style.position = 'absolute';
}
if (this.zoomLayer) {
this.cssTransform(this.zoomLayer.firstChild);
}
this.reset(true);
},
/**
* Called when moved in the parent's container.
*/
updatePosition: function PDFPageView_updatePosition() {
if (this.textLayer) {
this.textLayer.render(TEXT_LAYER_RENDER_DELAY);
}
},
cssTransform: function PDFPageView_transform(canvas, redrawAnnotations) {
// Scale canvas, canvas wrapper, and page container.
var width = this.viewport.width;
var height = this.viewport.height;
var div = this.div;
canvas.style.width = canvas.parentNode.style.width = div.style.width =
Math.floor(width) + 'px';
canvas.style.height = canvas.parentNode.style.height = div.style.height =
Math.floor(height) + 'px';
// The canvas may have been originally rotated, rotate relative to that.
var relativeRotation = this.viewport.rotation - canvas._viewport.rotation;
var absRotation = Math.abs(relativeRotation);
var scaleX = 1, scaleY = 1;
if (absRotation === 90 || absRotation === 270) {
// Scale x and y because of the rotation.
scaleX = height / width;
scaleY = width / height;
}
var cssTransform = 'rotate(' + relativeRotation + 'deg) ' +
'scale(' + scaleX + ',' + scaleY + ')';
CustomStyle.setProp('transform', canvas, cssTransform);
if (this.textLayer) {
// Rotating the text layer is more complicated since the divs inside the
// the text layer are rotated.
// TODO: This could probably be simplified by drawing the text layer in
// one orientation then rotating overall.
var textLayerViewport = this.textLayer.viewport;
var textRelativeRotation = this.viewport.rotation -
textLayerViewport.rotation;
var textAbsRotation = Math.abs(textRelativeRotation);
var scale = width / textLayerViewport.width;
if (textAbsRotation === 90 || textAbsRotation === 270) {
scale = width / textLayerViewport.height;
}
var textLayerDiv = this.textLayer.textLayerDiv;
var transX, transY;
switch (textAbsRotation) {
case 0:
transX = transY = 0;
break;
case 90:
transX = 0;
transY = '-' + textLayerDiv.style.height;
break;
case 180:
transX = '-' + textLayerDiv.style.width;
transY = '-' + textLayerDiv.style.height;
break;
case 270:
transX = '-' + textLayerDiv.style.width;
transY = 0;
break;
default:
console.error('Bad rotation value.');
break;
}
CustomStyle.setProp('transform', textLayerDiv,
'rotate(' + textAbsRotation + 'deg) ' +
'scale(' + scale + ', ' + scale + ') ' +
'translate(' + transX + ', ' + transY + ')');
CustomStyle.setProp('transformOrigin', textLayerDiv, '0% 0%');
}
if (redrawAnnotations && this.annotationLayer) {
this.annotationLayer.setupAnnotations(this.viewport);
}
},
get width() {
return this.viewport.width;
},
get height() {
return this.viewport.height;
},
getPagePoint: function PDFPageView_getPagePoint(x, y) {
return this.viewport.convertToPdfPoint(x, y);
},
draw: function PDFPageView_draw() {
if (this.renderingState !== RenderingStates.INITIAL) {
console.error('Must be in new state before drawing');
}
this.renderingState = RenderingStates.RUNNING;
var pdfPage = this.pdfPage;
var viewport = this.viewport;
var div = this.div;
// Wrap the canvas so if it has a css transform for highdpi the overflow
// will be hidden in FF.
var canvasWrapper = document.createElement('div');
canvasWrapper.style.width = div.style.width;
canvasWrapper.style.height = div.style.height;
canvasWrapper.classList.add('canvasWrapper');
var canvas = document.createElement('canvas');
canvas.id = 'page' + this.id;
canvasWrapper.appendChild(canvas);
if (this.annotationLayer) {
// annotationLayer needs to stay on top
div.insertBefore(canvasWrapper, this.annotationLayer.div);
} else {
div.appendChild(canvasWrapper);
}
this.canvas = canvas;
var ctx = canvas.getContext('2d');
var outputScale = getOutputScale(ctx);
if (PDFJS.useOnlyCssZoom) {
var actualSizeViewport = viewport.clone({ scale: CSS_UNITS });
// Use a scale that will make the canvas be the original intended size
// of the page.
outputScale.sx *= actualSizeViewport.width / viewport.width;
outputScale.sy *= actualSizeViewport.height / viewport.height;
outputScale.scaled = true;
}
if (PDFJS.maxCanvasPixels > 0) {
var pixelsInViewport = viewport.width * viewport.height;
var maxScale = Math.sqrt(PDFJS.maxCanvasPixels / pixelsInViewport);
if (outputScale.sx > maxScale || outputScale.sy > maxScale) {
outputScale.sx = maxScale;
outputScale.sy = maxScale;
outputScale.scaled = true;
this.hasRestrictedScaling = true;
} else {
this.hasRestrictedScaling = false;
}
}
canvas.width = (Math.floor(viewport.width) * outputScale.sx) | 0;
canvas.height = (Math.floor(viewport.height) * outputScale.sy) | 0;
canvas.style.width = Math.floor(viewport.width) + 'px';
canvas.style.height = Math.floor(viewport.height) + 'px';
// Add the viewport so it's known what it was originally drawn with.
canvas._viewport = viewport;
var textLayerDiv = null;
var textLayer = null;
if (this.textLayerFactory) {
textLayerDiv = document.createElement('div');
textLayerDiv.className = 'textLayer';
textLayerDiv.style.width = canvas.style.width;
textLayerDiv.style.height = canvas.style.height;
if (this.annotationLayer) {
// annotationLayer needs to stay on top
div.insertBefore(textLayerDiv, this.annotationLayer.div);
} else {
div.appendChild(textLayerDiv);
}
textLayer = this.textLayerFactory.createTextLayerBuilder(textLayerDiv,
this.id - 1,
this.viewport);
}
this.textLayer = textLayer;
// TODO(mack): use data attributes to store these
ctx._scaleX = outputScale.sx;
ctx._scaleY = outputScale.sy;
if (outputScale.scaled) {
ctx.scale(outputScale.sx, outputScale.sy);
}
var resolveRenderPromise, rejectRenderPromise;
var promise = new Promise(function (resolve, reject) {
resolveRenderPromise = resolve;
rejectRenderPromise = reject;
});
// Rendering area
var self = this;
function pageViewDrawCallback(error) {
// The renderTask may have been replaced by a new one, so only remove
// the reference to the renderTask if it matches the one that is
// triggering this callback.
if (renderTask === self.renderTask) {
self.renderTask = null;
}
if (error === 'cancelled') {
rejectRenderPromise(error);
return;
}
self.renderingState = RenderingStates.FINISHED;
if (self.loadingIconDiv) {
div.removeChild(self.loadingIconDiv);
delete self.loadingIconDiv;
}
if (self.zoomLayer) {
div.removeChild(self.zoomLayer);
self.zoomLayer = null;
}
self.error = error;
self.stats = pdfPage.stats;
if (self.onAfterDraw) {
self.onAfterDraw();
}
var event = document.createEvent('CustomEvent');
event.initCustomEvent('pagerender', true, true, {
pageNumber: pdfPage.pageNumber
});
div.dispatchEvent(event);
if (!error) {
resolveRenderPromise(undefined);
} else {
rejectRenderPromise(error);
}
}
var renderContinueCallback = null;
if (this.renderingQueue) {
renderContinueCallback = function renderContinueCallback(cont) {
if (!self.renderingQueue.isHighestPriority(self)) {
self.renderingState = RenderingStates.PAUSED;
self.resume = function resumeCallback() {
self.renderingState = RenderingStates.RUNNING;
cont();
};
return;
}
cont();
};
}
var renderContext = {
canvasContext: ctx,
viewport: this.viewport,
// intent: 'default', // === 'display'
continueCallback: renderContinueCallback
};
var renderTask = this.renderTask = this.pdfPage.render(renderContext);
this.renderTask.promise.then(
function pdfPageRenderCallback() {
pageViewDrawCallback(null);
if (textLayer) {
self.pdfPage.getTextContent().then(
function textContentResolved(textContent) {
textLayer.setTextContent(textContent);
textLayer.render(TEXT_LAYER_RENDER_DELAY);
}
);
}
},
function pdfPageRenderError(error) {
pageViewDrawCallback(error);
}
);
if (this.annotationsLayerFactory) {
if (!this.annotationLayer) {
this.annotationLayer = this.annotationsLayerFactory.
createAnnotationsLayerBuilder(div, this.pdfPage);
}
this.annotationLayer.setupAnnotations(this.viewport);
}
div.setAttribute('data-loaded', true);
if (self.onBeforeDraw) {
self.onBeforeDraw();
}
return promise;
},
beforePrint: function PDFPageView_beforePrint() {
var pdfPage = this.pdfPage;
var viewport = pdfPage.getViewport(1);
// Use the same hack we use for high dpi displays for printing to get
// better output until bug 811002 is fixed in FF.
var PRINT_OUTPUT_SCALE = 2;
var canvas = document.createElement('canvas');
canvas.width = Math.floor(viewport.width) * PRINT_OUTPUT_SCALE;
canvas.height = Math.floor(viewport.height) * PRINT_OUTPUT_SCALE;
canvas.style.width = (PRINT_OUTPUT_SCALE * viewport.width) + 'pt';
canvas.style.height = (PRINT_OUTPUT_SCALE * viewport.height) + 'pt';
var cssScale = 'scale(' + (1 / PRINT_OUTPUT_SCALE) + ', ' +
(1 / PRINT_OUTPUT_SCALE) + ')';
CustomStyle.setProp('transform' , canvas, cssScale);
CustomStyle.setProp('transformOrigin' , canvas, '0% 0%');
var printContainer = document.getElementById('printContainer');
var canvasWrapper = document.createElement('div');
canvasWrapper.style.width = viewport.width + 'pt';
canvasWrapper.style.height = viewport.height + 'pt';
canvasWrapper.appendChild(canvas);
printContainer.appendChild(canvasWrapper);
canvas.mozPrintCallback = function(obj) {
var ctx = obj.context;
ctx.save();
ctx.fillStyle = 'rgb(255, 255, 255)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore();
ctx.scale(PRINT_OUTPUT_SCALE, PRINT_OUTPUT_SCALE);
var renderContext = {
canvasContext: ctx,
viewport: viewport,
intent: 'print'
};
pdfPage.render(renderContext).promise.then(function() {
// Tell the printEngine that rendering this canvas/page has finished.
obj.done();
}, function(error) {
console.error(error);
// Tell the printEngine that rendering this canvas/page has failed.
// This will make the print proces stop.
if ('abort' in obj) {
obj.abort();
} else {
obj.done();
}
});
};
},
};
return PDFPageView;
})();

View File

@ -165,7 +165,10 @@ var PDFRenderingQueue = (function PDFRenderingQueueClosure() {
break;
case RenderingStates.INITIAL:
this.highestPriorityPage = view.renderingId;
view.draw(this.renderHighestPriority.bind(this));
var continueRendering = function () {
this.renderHighestPriority();
}.bind(this);
view.draw().then(continueRendering, continueRendering);
break;
}
return true;

View File

@ -15,7 +15,9 @@
* limitations under the License.
*/
/*jshint globalstrict: false */
/* globals PDFJS, PDFViewer */
/* globals PDFJS, PDFViewer, PDFPageView, TextLayerBuilder,
DefaultTextLayerFactory, AnnotationsLayerBuilder,
DefaultAnnotationsLayerFactory */
// Initializing PDFJS global object (if still undefined)
if (typeof PDFJS === 'undefined') {
@ -29,4 +31,9 @@ if (typeof PDFJS === 'undefined') {
//#include pdf_viewer.js
PDFJS.PDFViewer = PDFViewer;
PDFJS.PDFPageView = PDFPageView;
PDFJS.TextLayerBuilder = TextLayerBuilder;
PDFJS.DefaultTextLayerFactory = DefaultTextLayerFactory;
PDFJS.AnnotationsLayerBuilder = AnnotationsLayerBuilder;
PDFJS.DefaultAnnotationsLayerFactory = DefaultAnnotationsLayerFactory;
}).call((typeof window === 'undefined') ? this : window);

View File

@ -14,10 +14,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*globals watchScroll, Cache, DEFAULT_CACHE_SIZE, PageView, UNKNOWN_SCALE,
/*globals watchScroll, PDFPageView, UNKNOWN_SCALE,
SCROLLBAR_PADDING, VERTICAL_PADDING, MAX_AUTO_SCALE, CSS_UNITS,
DEFAULT_SCALE, scrollIntoView, getVisibleElements, RenderingStates,
PDFJS, Promise, TextLayerBuilder, PDFRenderingQueue */
PDFJS, Promise, TextLayerBuilder, PDFRenderingQueue,
AnnotationsLayerBuilder */
'use strict';
@ -29,10 +30,12 @@ var PresentationModeState = {
};
var IGNORE_CURRENT_POSITION_ON_ZOOM = false;
var DEFAULT_CACHE_SIZE = 10;
//#include pdf_rendering_queue.js
//#include page_view.js
//#include pdf_page_view.js
//#include text_layer_builder.js
//#include annotations_layer_builder.js
/**
* @typedef {Object} PDFViewerOptions
@ -46,10 +49,29 @@ var IGNORE_CURRENT_POSITION_ON_ZOOM = false;
/**
* Simple viewer control to display PDF content/pages.
* @class
* @implements {ILastScrollSource}
* @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();
}
};
}
/**
* @constructs PDFViewer
* @param {PDFViewerOptions} options
@ -69,7 +91,6 @@ var PDFViewer = (function pdfViewer() {
}
this.scroll = watchScroll(this.container, this._scrollUpdate.bind(this));
this.lastScroll = 0;
this.updateInProgress = false;
this.presentationModeState = PresentationModeState.UNKNOWN;
this._resetView();
@ -105,7 +126,6 @@ var PDFViewer = (function pdfViewer() {
return;
}
this.pages[val - 1].updateStats();
event.previousPageNumber = this._currentPageNumber;
this._currentPageNumber = val;
event.pageNumber = val;
@ -211,7 +231,13 @@ var PDFViewer = (function pdfViewer() {
});
this.onePageRendered = onePageRendered;
var bindOnAfterDraw = function (pageView) {
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) {
@ -235,12 +261,20 @@ var PDFViewer = (function pdfViewer() {
var scale = this._currentScale || 1.0;
var viewport = pdfPage.getViewport(scale * CSS_UNITS);
for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) {
var pageSource = new PDFPageSource(pdfDocument, pageNum);
var pageView = new PageView(this.viewer, pageNum, scale,
viewport.clone(), this.linkService,
this.renderingQueue, this.cache,
pageSource, this);
bindOnAfterDraw(pageView);
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);
}
@ -281,13 +315,14 @@ var PDFViewer = (function pdfViewer() {
},
_resetView: function () {
this.cache = new Cache(DEFAULT_CACHE_SIZE);
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()) {
@ -296,12 +331,13 @@ var PDFViewer = (function pdfViewer() {
},
_scrollUpdate: function () {
this.lastScroll = Date.now();
if (this.pagesCount === 0) {
return;
}
this.update();
for (var i = 0, ii = this.pages.length; i < ii; i++) {
this.pages[i].updatePosition();
}
},
_setScaleUpdatePages: function pdfViewer_setScaleUpdatePages(
@ -398,7 +434,6 @@ var PDFViewer = (function pdfViewer() {
scrollPageIntoView: function PDFViewer_scrollPageIntoView(pageNumber,
dest) {
var pageView = this.pages[pageNumber - 1];
var pageViewDiv = pageView.el;
if (this.presentationModeState ===
PresentationModeState.FULLSCREEN) {
@ -412,7 +447,7 @@ var PDFViewer = (function pdfViewer() {
this._setScale(this.currentScaleValue, true);
}
if (!dest) {
scrollIntoView(pageViewDiv);
scrollIntoView(pageView.div);
return;
}
@ -475,7 +510,7 @@ var PDFViewer = (function pdfViewer() {
}
if (scale === 'page-fit' && !dest[4]) {
scrollIntoView(pageViewDiv);
scrollIntoView(pageView.div);
return;
}
@ -486,7 +521,7 @@ var PDFViewer = (function pdfViewer() {
var left = Math.min(boundingRect[0][0], boundingRect[1][0]);
var top = Math.min(boundingRect[0][1], boundingRect[1][1]);
scrollIntoView(pageViewDiv, { left: left, top: top });
scrollIntoView(pageView.div, { left: left, top: top });
},
_updateLocation: function (firstPage) {
@ -528,7 +563,7 @@ var PDFViewer = (function pdfViewer() {
var suggestedCacheSize = Math.max(DEFAULT_CACHE_SIZE,
2 * visiblePages.length + 1);
this.cache.resize(suggestedCacheSize);
this._buffer.resize(suggestedCacheSize);
this.renderingQueue.renderHighestPriority(visible);
@ -604,13 +639,38 @@ var PDFViewer = (function pdfViewer() {
}
},
/**
* @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.renderingQueue.renderView(pageView);
this._ensurePdfPageLoaded(pageView).then(function () {
this.renderingQueue.renderView(pageView);
}.bind(this));
return true;
}
return false;
@ -623,9 +683,9 @@ var PDFViewer = (function pdfViewer() {
},
/**
* @param textLayerDiv {HTMLDivElement}
* @param pageIndex {number}
* @param viewport {PageViewport}
* @param {HTMLDivElement} textLayerDiv
* @param {number} pageIndex
* @param {PageViewport} viewport
* @returns {TextLayerBuilder}
*/
createTextLayerBuilder: function (textLayerDiv, pageIndex, viewport) {
@ -635,9 +695,20 @@ var PDFViewer = (function pdfViewer() {
textLayerDiv: textLayerDiv,
pageIndex: pageIndex,
viewport: viewport,
lastScrollSource: this,
isViewerInPresentationMode: isViewerInPresentationMode,
findController: this.findController
findController: isViewerInPresentationMode ? 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
});
},
@ -695,31 +766,3 @@ var SimpleLinkService = (function SimpleLinkServiceClosure() {
};
return SimpleLinkService;
})();
/**
* PDFPage object source.
* @class
*/
var PDFPageSource = (function PDFPageSourceClosure() {
/**
* @constructs
* @param {PDFDocument} pdfDocument
* @param {number} pageNumber
* @constructor
*/
function PDFPageSource(pdfDocument, pageNumber) {
this.pdfDocument = pdfDocument;
this.pageNumber = pageNumber;
}
PDFPageSource.prototype = /** @lends PDFPageSource.prototype */ {
/**
* @returns {Promise<PDFPage>}
*/
getPage: function () {
return this.pdfDocument.getPage(this.pageNumber);
}
};
return PDFPageSource;
})();

View File

@ -20,6 +20,7 @@
right: 0;
bottom: 0;
overflow: hidden;
opacity: 0.2;
}
.textLayer > div {
@ -57,3 +58,6 @@
.textLayer .highlight.selected {
background-color: rgb(0, 100, 0);
}
.textLayer ::selection { background: rgb(0,0,255); }
.textLayer ::-moz-selection { background: rgb(0,0,255); }

View File

@ -13,14 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* globals CustomStyle, scrollIntoView, PDFJS */
/* globals CustomStyle, PDFJS */
'use strict';
var FIND_SCROLL_OFFSET_TOP = -50;
var FIND_SCROLL_OFFSET_LEFT = -400;
var MAX_TEXT_DIVS_TO_RENDER = 100000;
var RENDER_DELAY = 200; // ms
var NonWhitespaceRegexp = /\S/;
@ -33,9 +30,6 @@ function isAllWhitespace(str) {
* @property {HTMLDivElement} textLayerDiv - The text layer container.
* @property {number} pageIndex - The page index.
* @property {PageViewport} viewport - The viewport of the text layer.
* @property {ILastScrollSource} lastScrollSource - The object that records when
* last time scroll happened.
* @property {boolean} isViewerInPresentationMode
* @property {PDFFindController} findController
*/
@ -49,13 +43,11 @@ function isAllWhitespace(str) {
var TextLayerBuilder = (function TextLayerBuilderClosure() {
function TextLayerBuilder(options) {
this.textLayerDiv = options.textLayerDiv;
this.layoutDone = false;
this.renderingDone = false;
this.divContentDone = false;
this.pageIdx = options.pageIndex;
this.matches = [];
this.lastScrollSource = options.lastScrollSource || null;
this.viewport = options.viewport;
this.isViewerInPresentationMode = options.isViewerInPresentationMode;
this.textDivs = [];
this.findController = options.findController || null;
}
@ -71,6 +63,7 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
// No point in rendering many divs as it would make the browser
// unusable even after the divs are rendered.
if (textDivsLength > MAX_TEXT_DIVS_TO_RENDER) {
this.renderingDone = true;
return;
}
@ -118,23 +111,29 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
this.updateMatches();
},
setupRenderLayoutTimer:
function TextLayerBuilder_setupRenderLayoutTimer() {
// Schedule renderLayout() if the user has been scrolling,
// otherwise run it right away.
var self = this;
var lastScroll = (this.lastScrollSource === null ?
0 : this.lastScrollSource.lastScroll);
/**
* Renders the text layer.
* @param {number} timeout (optional) if specified, the rendering waits
* for specified amount of ms.
*/
render: function TextLayerBuilder_render(timeout) {
if (!this.divContentDone || this.renderingDone) {
return;
}
if (Date.now() - lastScroll > RENDER_DELAY) { // Render right away
if (this.renderTimer) {
clearTimeout(this.renderTimer);
this.renderTimer = null;
}
if (!timeout) { // Render right away
this.renderLayer();
} else { // Schedule
if (this.renderTimer) {
clearTimeout(this.renderTimer);
}
var self = this;
this.renderTimer = setTimeout(function() {
self.setupRenderLayoutTimer();
}, RENDER_DELAY);
self.renderLayer();
self.renderTimer = null;
}, timeout);
}
},
@ -204,7 +203,6 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
this.appendText(textItems[i], textContent.styles);
}
this.divContentDone = true;
this.setupRenderLayoutTimer();
},
convertMatches: function TextLayerBuilder_convertMatches(matches) {
@ -266,8 +264,9 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
var bidiTexts = this.textContent.items;
var textDivs = this.textDivs;
var prevEnd = null;
var pageIdx = this.pageIdx;
var isSelectedPage = (this.findController === null ?
false : (this.pageIdx === this.findController.selected.pageIdx));
false : (pageIdx === this.findController.selected.pageIdx));
var selectedMatchIdx = (this.findController === null ?
-1 : this.findController.selected.matchIdx);
var highlightAll = (this.findController === null ?
@ -313,10 +312,9 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
var isSelected = (isSelectedPage && i === selectedMatchIdx);
var highlightSuffix = (isSelected ? ' selected' : '');
if (isSelected && !this.isViewerInPresentationMode) {
scrollIntoView(textDivs[begin.divIdx],
{ top: FIND_SCROLL_OFFSET_TOP,
left: FIND_SCROLL_OFFSET_LEFT });
if (this.findController) {
this.findController.updateMatchPosition(pageIdx, i, textDivs,
begin.divIdx, end.divIdx);
}
// Match inside new div.
@ -387,3 +385,24 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
};
return TextLayerBuilder;
})();
/**
* @constructor
* @implements IPDFTextLayerFactory
*/
function DefaultTextLayerFactory() {}
DefaultTextLayerFactory.prototype = {
/**
* @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
});
}
};

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* globals mozL10n, RenderingStates, Promise, scrollIntoView, PDFPageSource,
/* globals mozL10n, RenderingStates, Promise, scrollIntoView,
watchScroll, getVisibleElements */
'use strict';
@ -28,13 +28,11 @@ var THUMBNAIL_SCROLL_MARGIN = -19;
* @param defaultViewport
* @param linkService
* @param renderingQueue
* @param pageSource
*
* @implements {IRenderableView}
*/
var ThumbnailView = function thumbnailView(container, id, defaultViewport,
linkService, renderingQueue,
pageSource) {
linkService, renderingQueue) {
var anchor = document.createElement('a');
anchor.href = linkService.getAnchorUrl('#page=' + id);
anchor.title = mozL10n.get('thumb_page_title', {page: id}, 'Page {{page}}');
@ -80,7 +78,6 @@ var ThumbnailView = function thumbnailView(container, id, defaultViewport,
this.hasImage = false;
this.renderingState = RenderingStates.INITIAL;
this.renderingQueue = renderingQueue;
this.pageSource = pageSource;
this.setPdfPage = function thumbnailViewSetPdfPage(pdfPage) {
this.pdfPage = pdfPage;
@ -142,26 +139,22 @@ var ThumbnailView = function thumbnailView(container, id, defaultViewport,
return !this.hasImage;
};
this.draw = function thumbnailViewDraw(callback) {
if (!this.pdfPage) {
var promise = this.pageSource.getPage(this.id);
promise.then(function(pdfPage) {
this.setPdfPage(pdfPage);
this.draw(callback);
}.bind(this));
return;
}
this.draw = function thumbnailViewDraw() {
if (this.renderingState !== RenderingStates.INITIAL) {
console.error('Must be in new state before drawing');
}
this.renderingState = RenderingStates.RUNNING;
if (this.hasImage) {
callback();
return;
return Promise.resolve(undefined);
}
var resolveRenderPromise, rejectRenderPromise;
var promise = new Promise(function (resolve, reject) {
resolveRenderPromise = resolve;
rejectRenderPromise = reject;
});
var self = this;
var ctx = this.getPageDrawContext();
var drawViewport = this.viewport.clone({ scale: this.scale });
@ -183,14 +176,15 @@ var ThumbnailView = function thumbnailView(container, id, defaultViewport,
this.pdfPage.render(renderContext).promise.then(
function pdfPageRenderCallback() {
self.renderingState = RenderingStates.FINISHED;
callback();
resolveRenderPromise(undefined);
},
function pdfPageRenderError(error) {
self.renderingState = RenderingStates.FINISHED;
callback();
rejectRenderPromise(error);
}
);
this.hasImage = true;
return promise;
};
function getTempCanvas(width, height) {
@ -204,18 +198,14 @@ var ThumbnailView = function thumbnailView(container, id, defaultViewport,
return tempCanvas;
}
this.setImage = function thumbnailViewSetImage(img) {
if (!this.pdfPage) {
var promise = this.pageSource.getPage();
promise.then(function(pdfPage) {
this.setPdfPage(pdfPage);
this.setImage(img);
}.bind(this));
return;
}
this.setImage = function thumbnailViewSetImage(pageView) {
var img = pageView.canvas;
if (this.hasImage || !img) {
return;
}
if (this.pdfPage) {
this.setPdfPage(pageView.pdfPage);
}
this.renderingState = RenderingStates.FINISHED;
var ctx = this.getPageDrawContext();
@ -330,6 +320,7 @@ var PDFThumbnailViewer = (function pdfThumbnailViewer() {
_resetView: function () {
this.thumbnails = [];
this._pagesRotation = 0;
this._pagesRequests = [];
},
setDocument: function (pdfDocument) {
@ -351,15 +342,37 @@ var PDFThumbnailViewer = (function pdfThumbnailViewer() {
var pagesCount = pdfDocument.numPages;
var viewport = firstPage.getViewport(1.0);
for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) {
var pageSource = new PDFPageSource(pdfDocument, pageNum);
var thumbnail = new ThumbnailView(this.container, pageNum,
viewport.clone(), this.linkService,
this.renderingQueue, pageSource);
this.renderingQueue);
this.thumbnails.push(thumbnail);
}
}.bind(this));
},
/**
* @param {PDFPageView} pageView
* @returns {PDFPage}
* @private
*/
_ensurePdfPageLoaded: function (thumbView) {
if (thumbView.pdfPage) {
return Promise.resolve(thumbView.pdfPage);
}
var pageNumber = thumbView.id;
if (this._pagesRequests[pageNumber]) {
return this._pagesRequests[pageNumber];
}
var promise = this.pdfDocument.getPage(pageNumber).then(
function (pdfPage) {
thumbView.setPdfPage(pdfPage);
this._pagesRequests[pageNumber] = null;
return pdfPage;
}.bind(this));
this._pagesRequests[pageNumber] = promise;
return promise;
},
ensureThumbnailVisible:
function PDFThumbnailViewer_ensureThumbnailVisible(page) {
// Ensure that the thumbnail of the current page is visible
@ -373,7 +386,9 @@ var PDFThumbnailViewer = (function pdfThumbnailViewer() {
this.thumbnails,
this.scroll.down);
if (thumbView) {
this.renderingQueue.renderView(thumbView);
this._ensurePdfPageLoaded(thumbView).then(function () {
this.renderingQueue.renderView(thumbView);
}.bind(this));
return true;
}
return false;

View File

@ -22,7 +22,6 @@ var UNKNOWN_SCALE = 0;
var MAX_AUTO_SCALE = 1.25;
var SCROLLBAR_PADDING = 40;
var VERTICAL_PADDING = 5;
var DEFAULT_CACHE_SIZE = 10;
// optimised CSS custom property getter/setter
var CustomStyle = (function CustomStyleClosure() {
@ -349,23 +348,3 @@ var ProgressBar = (function ProgressBarClosure() {
return ProgressBar;
})();
var Cache = function cacheCache(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();
}
};
};

View File

@ -1256,19 +1256,12 @@ html[dir='rtl'] .attachmentsItem > button {
cursor: default;
}
/* TODO: file FF bug to support ::-moz-selection:window-inactive
so we can override the opaque grey background when the window is inactive;
see https://bugzilla.mozilla.org/show_bug.cgi?id=706209 */
::selection { background: rgba(0,0,255,0.3); }
::-moz-selection { background: rgba(0,0,255,0.3); }
.textLayer ::selection { background: rgb(0,0,255); }
.textLayer ::-moz-selection { background: rgb(0,0,255); }
.textLayer {
opacity: 0.2;
}
#errorWrapper {
background: none repeat scroll 0 0 #FF5555;
color: white;

View File

@ -69,8 +69,9 @@ http://sourceforge.net/adobe/cmap/wiki/License/
<script src="download_manager.js"></script>
<script src="view_history.js"></script>
<script src="pdf_rendering_queue.js"></script>
<script src="page_view.js"></script>
<script src="pdf_page_view.js"></script>
<script src="text_layer_builder.js"></script>
<script src="annotations_layer_builder.js"></script>
<script src="pdf_viewer.js"></script>
<script src="thumbnail_view.js"></script>
<script src="document_outline_view.js"></script>

View File

@ -16,7 +16,7 @@
*/
/* globals PDFJS, PDFBug, FirefoxCom, Stats, Cache, ProgressBar,
DownloadManager, getFileName, scrollIntoView, getPDFFileNameFromURL,
PDFHistory, Preferences, SidebarView, ViewHistory, PageView,
PDFHistory, Preferences, SidebarView, ViewHistory, Stats,
PDFThumbnailViewer, URL, noContextMenuHandler, SecondaryToolbar,
PasswordPrompt, PresentationMode, HandTool, Promise,
DocumentProperties, DocumentOutlineView, DocumentAttachmentsView,
@ -1353,7 +1353,6 @@ var PDFViewerApplication = {
rotatePages: function pdfViewRotatePages(delta) {
var pageNumber = this.page;
this.pageRotation = (this.pageRotation + 360 + delta) % 360;
this.pdfViewer.pagesRotation = this.pageRotation;
this.pdfThumbnailViewer.pagesRotation = this.pageRotation;
@ -1731,11 +1730,16 @@ function webViewerInitialized() {
document.addEventListener('DOMContentLoaded', webViewerLoad, true);
document.addEventListener('pagerendered', function (e) {
var pageIndex = e.detail.pageNumber - 1;
var pageNumber = e.detail.pageNumber;
var pageIndex = pageNumber - 1;
var pageView = PDFViewerApplication.pdfViewer.getPageView(pageIndex);
var thumbnailView = PDFViewerApplication.pdfThumbnailViewer.
getThumbnail(pageIndex);
thumbnailView.setImage(pageView.canvas);
thumbnailView.setImage(pageView);
if (PDFJS.pdfBug && Stats.enabled && pageView.stats) {
Stats.add(pageNumber, pageView.stats);
}
//#if (FIREFOX || MOZCENTRAL)
//if (pageView.textLayer && pageView.textLayer.textDivs &&
@ -1769,7 +1773,7 @@ document.addEventListener('pagerendered', function (e) {
// If the page is still visible when it has finished rendering,
// ensure that the page number input loading indicator is hidden.
if ((pageIndex + 1) === PDFViewerApplication.page) {
if (pageNumber === PDFViewerApplication.page) {
var pageNumberInput = document.getElementById('pageNumber');
pageNumberInput.classList.remove(PAGE_NUMBER_LOADING_INDICATOR);
}
@ -1966,6 +1970,14 @@ window.addEventListener('pagechange', function pagechange(evt) {
document.getElementById('firstPage').disabled = (page <= 1);
document.getElementById('lastPage').disabled = (page >= numPages);
// we need to update stats
if (PDFJS.pdfBug && Stats.enabled) {
var pageView = PDFViewerApplication.pdfViewer.getPageView(page - 1);
if (pageView.stats) {
Stats.add(page, pageView.stats);
}
}
// checking if the this.page was called from the updateViewarea function
if (evt.updateInProgress) {
return;