Lazy rendering

This commit is contained in:
Artur Adib 2011-10-31 16:49:18 -04:00
parent e7d08e3a98
commit 98f3bab65c
3 changed files with 78 additions and 53 deletions

View File

@ -60,7 +60,7 @@ var CanvasGraphics = (function canvasGraphics() {
// if we execute longer then `kExecutionTime`. // if we execute longer then `kExecutionTime`.
var kExecutionTimeCheck = 500; var kExecutionTimeCheck = 500;
function constructor(canvasCtx, objs, textLayer, textScale) { function constructor(canvasCtx, objs, textLayer) {
this.ctx = canvasCtx; this.ctx = canvasCtx;
this.current = new CanvasExtraState(); this.current = new CanvasExtraState();
this.stateStack = []; this.stateStack = [];
@ -70,7 +70,6 @@ var CanvasGraphics = (function canvasGraphics() {
this.ScratchCanvas = ScratchCanvas; this.ScratchCanvas = ScratchCanvas;
this.objs = objs; this.objs = objs;
this.textLayer = textLayer; this.textLayer = textLayer;
this.textScale = textScale;
} }
var LINE_CAP_STYLES = ['butt', 'round', 'square']; var LINE_CAP_STYLES = ['butt', 'round', 'square'];
@ -98,6 +97,10 @@ var CanvasGraphics = (function canvasGraphics() {
} }
this.ctx.scale(cw / mediaBox.width, ch / mediaBox.height); this.ctx.scale(cw / mediaBox.width, ch / mediaBox.height);
this.textDivs = []; this.textDivs = [];
this.textLayerQueue = [];
// Prevent textLayerQueue to be rendered while rendering a new page
if (this.textLayerTimer)
clearTimeout(this.textLayerTimer);
}, },
executeIRQueue: function canvasGraphicsExecuteIRQueue(codeIR, executeIRQueue: function canvasGraphicsExecuteIRQueue(codeIR,
@ -152,18 +155,38 @@ var CanvasGraphics = (function canvasGraphics() {
}, },
endDrawing: function canvasGraphicsEndDrawing() { endDrawing: function canvasGraphicsEndDrawing() {
var self = this;
this.ctx.restore(); this.ctx.restore();
// Text selection-specific var textLayer = self.textLayer;
var textLayer = this.textLayer; if (textLayer) {
var textDivs = this.textDivs; var renderTextLayer = function canvasRenderTextLayer() {
var textDivs = self.textDivs;
for (var i = 0, length = textDivs.length; i < length; ++i) { for (var i = 0, length = textDivs.length; i < length; ++i) {
if (textDivs[i].dataset.textLength>1) { // avoid div by zero if (textDivs[i].dataset.textLength>1) { // avoid div by zero
textLayer.appendChild(textDivs[i]); textLayer.appendChild(textDivs[i]);
// Adjust div width to match canvas text width // Adjust div width (via letterSpacing) to match canvas text
textDivs[i].style.letterSpacing = ((textDivs[i].dataset.canvasWidth - textDivs[i].offsetWidth)/(textDivs[i].dataset.textLength-1)) + 'px'; // Due to the .offsetWidth calls, this is slow
textDivs[i].style.letterSpacing =
((textDivs[i].dataset.canvasWidth
- textDivs[i].offsetWidth)/(textDivs[i].dataset.textLength-1))
+ 'px';
} }
} }
}
var textLayerQueue = self.textLayerQueue;
textLayerQueue.push(renderTextLayer);
// Lazy textLayer rendering (to prevent UI hangs)
// Only render queue if activity has stopped, where "no activity" ==
// "no beginDrawing() calls in the last N ms"
self.textLayerTimer = setTimeout(function renderTextLayerQueue(){
// Render most recent (==most relevant) layers first
for (var i=textLayerQueue.length-1; i>=0; i--) {
textLayerQueue.pop().call();
}
}, 500);
}
}, },
// Graphics state // Graphics state
@ -516,7 +539,7 @@ var CanvasGraphics = (function canvasGraphics() {
var font = current.font; var font = current.font;
var text = {str:'', length:0, canvasWidth:0, spaceWidth:0, geom:{}}; var text = {str:'', length:0, canvasWidth:0, spaceWidth:0, geom:{}};
// Text selection-specific if (textLayer) {
text.spaceWidth = this.current.font.charsToGlyphs(' ')[0].width; text.spaceWidth = this.current.font.charsToGlyphs(' ')[0].width;
if (!text.spaceWidth>0) { if (!text.spaceWidth>0) {
// Hack (space is sometimes not encoded) // Hack (space is sometimes not encoded)
@ -541,6 +564,7 @@ var CanvasGraphics = (function canvasGraphics() {
text.geom.yFactor = tr[1] - bl[1]; text.geom.yFactor = tr[1] - bl[1];
} }
ctx.restore(); ctx.restore();
}
for (var i = 0; i < arrLength; ++i) { for (var i = 0; i < arrLength; ++i) {
var e = arr[i]; var e = arr[i];
@ -548,8 +572,8 @@ var CanvasGraphics = (function canvasGraphics() {
var spacingLength = -e * 0.001 * fontSize * textHScale; var spacingLength = -e * 0.001 * fontSize * textHScale;
current.x += spacingLength; current.x += spacingLength;
// Text selection-specific if (textLayer) {
// Emulate arbitrary spacing via HTML spaces // Emulate precise spacing via HTML spaces
text.canvasWidth += spacingLength; text.canvasWidth += spacingLength;
if (e<0 && text.spaceWidth>0) { // avoid div by zero if (e<0 && text.spaceWidth>0) { // avoid div by zero
var numFakeSpaces = Math.round(-e / text.spaceWidth); var numFakeSpaces = Math.round(-e / text.spaceWidth);
@ -557,10 +581,11 @@ var CanvasGraphics = (function canvasGraphics() {
text.str += '&nbsp;'; text.str += '&nbsp;';
text.length += numFakeSpaces>0 ? 1 : 0; text.length += numFakeSpaces>0 ? 1 : 0;
} }
}
} else if (isString(e)) { } else if (isString(e)) {
var shownText = this.showText(e); var shownText = this.showText(e);
// Text selection-specific if (textLayer) {
if (shownText.chars === ' ') { if (shownText.chars === ' ') {
text.str += '&nbsp;'; text.str += '&nbsp;';
} else { } else {
@ -568,6 +593,7 @@ var CanvasGraphics = (function canvasGraphics() {
} }
text.canvasWidth += shownText.width; text.canvasWidth += shownText.width;
text.length += e.length; text.length += e.length;
}
} else { } else {
malformed('TJ array element ' + e + ' is not string or num'); malformed('TJ array element ' + e + ' is not string or num');
} }

View File

@ -7,7 +7,7 @@ var globalScope = (typeof window === 'undefined') ? this : window;
var ERRORS = 0, WARNINGS = 1, TODOS = 5; var ERRORS = 0, WARNINGS = 1, TODOS = 5;
var verbosity = WARNINGS; var verbosity = WARNINGS;
var useWorker = false; var useWorker = true;
// The global PDFJS object exposes the API // The global PDFJS object exposes the API
// In production, it will be declared outside a global wrapper // In production, it will be declared outside a global wrapper
@ -157,7 +157,7 @@ var Page = (function pagePage() {
IRQueue, fonts) { IRQueue, fonts) {
var self = this; var self = this;
this.IRQueue = IRQueue; this.IRQueue = IRQueue;
var gfx = new CanvasGraphics(this.ctx, this.objs, this.textLayer, this.textScale); var gfx = new CanvasGraphics(this.ctx, this.objs, this.textLayer);
var startTime = Date.now(); var startTime = Date.now();
var displayContinuation = function pageDisplayContinuation() { var displayContinuation = function pageDisplayContinuation() {
@ -306,11 +306,10 @@ var Page = (function pagePage() {
} }
return links; return links;
}, },
startRendering: function(ctx, callback, textLayer, textScale) { startRendering: function(ctx, callback, textLayer) {
this.ctx = ctx; this.ctx = ctx;
this.callback = callback; this.callback = callback;
this.textLayer = textLayer; this.textLayer = textLayer;
this.textScale = textScale;
this.startRenderingTime = Date.now(); this.startRenderingTime = Date.now();
this.pdf.startRendering(this); this.pdf.startRendering(this);

View File

@ -491,7 +491,7 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
ctx.translate(-this.x * scale, -this.y * scale); ctx.translate(-this.x * scale, -this.y * scale);
stats.begin = Date.now(); stats.begin = Date.now();
this.content.startRendering(ctx, this.updateStats, textLayer, scale); this.content.startRendering(ctx, this.updateStats, textLayer);
setupLinks(this.content, this.scale); setupLinks(this.content, this.scale);
div.setAttribute('data-loaded', true); div.setAttribute('data-loaded', true);