Incrementally render by sending the operator list by chunks as they're ready.

This commit is contained in:
Brendan Dahl 2013-07-31 11:17:36 -07:00
parent f7d2a09bf8
commit bf72bc94e2
11 changed files with 641 additions and 789 deletions

View File

@ -16,7 +16,7 @@
*/ */
/* globals Util, isDict, isName, stringToPDFString, TODO, Dict, Stream, /* globals Util, isDict, isName, stringToPDFString, TODO, Dict, Stream,
stringToBytes, PDFJS, isWorker, assert, NotImplementedException, stringToBytes, PDFJS, isWorker, assert, NotImplementedException,
Promise, isArray, ObjectLoader, isValidUrl */ Promise, isArray, ObjectLoader, isValidUrl, OperatorList */
'use strict'; 'use strict';
@ -162,13 +162,7 @@ var Annotation = (function AnnotationClosure() {
var promise = new Promise(); var promise = new Promise();
if (!this.appearance) { if (!this.appearance) {
promise.resolve({ promise.resolve(new OperatorList());
queue: {
fnArray: [],
argsArray: []
},
dependency: {}
});
return promise; return promise;
} }
@ -192,19 +186,11 @@ var Annotation = (function AnnotationClosure() {
var border = data.border; var border = data.border;
resourcesPromise.then(function(resources) { resourcesPromise.then(function(resources) {
var listPromise = evaluator.getOperatorList(this.appearance, resources); var opList = new OperatorList();
listPromise.then(function(appearanceStreamData) { opList.addOp('beginAnnotation', [data.rect, transform, matrix]);
var fnArray = appearanceStreamData.queue.fnArray; evaluator.getOperatorList(this.appearance, resources, opList);
var argsArray = appearanceStreamData.queue.argsArray; opList.addOp('endAnnotation', []);
promise.resolve(opList);
fnArray.unshift('beginAnnotation');
argsArray.unshift([data.rect, transform, matrix]);
fnArray.push('endAnnotation');
argsArray.push([]);
promise.resolve(appearanceStreamData);
});
}.bind(this)); }.bind(this));
return promise; return promise;
@ -284,7 +270,7 @@ var Annotation = (function AnnotationClosure() {
}; };
Annotation.appendToOperatorList = function Annotation_appendToOperatorList( Annotation.appendToOperatorList = function Annotation_appendToOperatorList(
annotations, pageQueue, pdfManager, dependencies, partialEvaluator) { annotations, opList, pdfManager, partialEvaluator) {
function reject(e) { function reject(e) {
annotationsReadyPromise.reject(e); annotationsReadyPromise.reject(e);
@ -296,22 +282,13 @@ var Annotation = (function AnnotationClosure() {
for (var i = 0, n = annotations.length; i < n; ++i) { for (var i = 0, n = annotations.length; i < n; ++i) {
annotationPromises.push(annotations[i].getOperatorList(partialEvaluator)); annotationPromises.push(annotations[i].getOperatorList(partialEvaluator));
} }
Promise.all(annotationPromises).then(function(datas) { Promise.all(annotationPromises).then(function(datas) {
var fnArray = pageQueue.fnArray; opList.addOp('beginAnnotations', []);
var argsArray = pageQueue.argsArray;
fnArray.push('beginAnnotations');
argsArray.push([]);
for (var i = 0, n = datas.length; i < n; ++i) { for (var i = 0, n = datas.length; i < n; ++i) {
var annotationData = datas[i]; var annotOpList = datas[i];
var annotationQueue = annotationData.queue; opList.addOpList(annotOpList);
Util.concatenateToArray(fnArray, annotationQueue.fnArray);
Util.concatenateToArray(argsArray, annotationQueue.argsArray);
Util.extendObj(dependencies, annotationData.dependencies);
} }
fnArray.push('endAnnotations'); opList.addOp('endAnnotations', []);
argsArray.push([]);
annotationsReadyPromise.resolve(); annotationsReadyPromise.resolve();
}, reject); }, reject);
@ -458,8 +435,8 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator) { getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator) {
var promise = new Promise(); var promise = new Promise();
var opList = new OperatorList();
var data = this.data; var data = this.data;
// Even if there is an appearance stream, ignore it. This is the // Even if there is an appearance stream, ignore it. This is the
@ -467,65 +444,44 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
var defaultAppearance = data.defaultAppearance; var defaultAppearance = data.defaultAppearance;
if (!defaultAppearance) { if (!defaultAppearance) {
promise.resolve({ promise.resolve(opList);
queue: {
fnArray: [],
argsArray: []
},
dependency: {}
});
return promise; return promise;
} }
// Include any font resources found in the default appearance // Include any font resources found in the default appearance
var stream = new Stream(stringToBytes(defaultAppearance)); var stream = new Stream(stringToBytes(defaultAppearance));
var listPromise = evaluator.getOperatorList(stream, this.fieldResources); evaluator.getOperatorList(stream, this.fieldResources, opList);
listPromise.then(function(appearanceStreamData) { var appearanceFnArray = opList.fnArray;
var appearanceFnArray = appearanceStreamData.queue.fnArray; var appearanceArgsArray = opList.argsArray;
var appearanceArgsArray = appearanceStreamData.queue.argsArray; var fnArray = [];
var fnArray = []; var argsArray = [];
var argsArray = [];
// TODO(mack): Add support for stroke color // TODO(mack): Add support for stroke color
data.rgb = [0, 0, 0]; data.rgb = [0, 0, 0];
for (var i = 0, n = fnArray.length; i < n; ++i) { // TODO THIS DOESN'T MAKE ANY SENSE SINCE THE fnArray IS EMPTY!
var fnName = appearanceFnArray[i]; for (var i = 0, n = fnArray.length; i < n; ++i) {
var args = appearanceArgsArray[i]; var fnName = appearanceFnArray[i];
if (fnName === 'dependency') { var args = appearanceArgsArray[i];
var dependency = args[i];
if (dependency.indexOf('g_font_') === 0) { if (fnName === 'setFont') {
data.fontRefName = dependency; data.fontRefName = args[0];
} var size = args[1];
fnArray.push(fnName); if (size < 0) {
argsArray.push(args); data.fontDirection = -1;
} else if (fnName === 'setFont') { data.fontSize = -size;
data.fontRefName = args[0]; } else {
var size = args[1]; data.fontDirection = 1;
if (size < 0) { data.fontSize = size;
data.fontDirection = -1;
data.fontSize = -size;
} else {
data.fontDirection = 1;
data.fontSize = size;
}
} else if (fnName === 'setFillRGBColor') {
data.rgb = args;
} else if (fnName === 'setFillGray') {
var rgbValue = args[0] * 255;
data.rgb = [rgbValue, rgbValue, rgbValue];
} }
} else if (fnName === 'setFillRGBColor') {
data.rgb = args;
} else if (fnName === 'setFillGray') {
var rgbValue = args[0] * 255;
data.rgb = [rgbValue, rgbValue, rgbValue];
} }
promise.resolve({ }
queue: { promise.resolve(opList);
fnArray: fnArray,
argsArray: argsArray
},
dependency: {}
});
});
return promise; return promise;
} }
}); });
@ -557,13 +513,7 @@ var TextAnnotation = (function TextAnnotationClosure() {
getOperatorList: function TextAnnotation_getOperatorList(evaluator) { getOperatorList: function TextAnnotation_getOperatorList(evaluator) {
var promise = new Promise(); var promise = new Promise();
promise.resolve({ promise.resolve(new OperatorList());
queue: {
fnArray: [],
argsArray: []
},
dependency: {}
});
return promise; return promise;
}, },

View File

@ -17,7 +17,7 @@
/* globals CanvasGraphics, combineUrl, createScratchCanvas, error, ErrorFont, /* globals CanvasGraphics, combineUrl, createScratchCanvas, error, ErrorFont,
Font, FontLoader, globalScope, info, isArrayBuffer, loadJpegStream, Font, FontLoader, globalScope, info, isArrayBuffer, loadJpegStream,
MessageHandler, PDFJS, PDFObjects, Promise, StatTimer, warn, MessageHandler, PDFJS, PDFObjects, Promise, StatTimer, warn,
WorkerMessageHandler, PasswordResponses */ WorkerMessageHandler, PasswordResponses, Util */
'use strict'; 'use strict';
@ -222,6 +222,7 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
this.objs = new PDFObjects(); this.objs = new PDFObjects();
this.renderInProgress = false; this.renderInProgress = false;
this.cleanupAfterRender = false; this.cleanupAfterRender = false;
this.renderTasks = [];
} }
PDFPageProxy.prototype = { PDFPageProxy.prototype = {
/** /**
@ -289,20 +290,24 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
* rendering call the function that is the * rendering call the function that is the
* first argument to the callback. * first argument to the callback.
* }. * }.
* @return {Promise} A promise that is resolved when the page finishes * @return {RenderTask} An extended promise that is resolved when the page
* rendering. * finishes rendering (see RenderTask).
*/ */
render: function PDFPageProxy_render(params) { render: function PDFPageProxy_render(params) {
this.renderInProgress = true; this.renderInProgress = true;
var promise = new Promise();
var stats = this.stats; var stats = this.stats;
stats.time('Overall'); stats.time('Overall');
// If there is no displayReadyPromise yet, then the operatorList was never // If there is no displayReadyPromise yet, then the operatorList was never
// requested before. Make the request and create the promise. // requested before. Make the request and create the promise.
if (!this.displayReadyPromise) { if (!this.displayReadyPromise) {
this.displayReadyPromise = new Promise(); this.displayReadyPromise = new Promise();
this.destroyed = false; this.destroyed = false;
this.operatorList = {
fnArray: [],
argsArray: [],
lastChunk: false
};
this.stats.time('Page Request'); this.stats.time('Page Request');
this.transport.messageHandler.send('RenderPageRequest', { this.transport.messageHandler.send('RenderPageRequest', {
@ -310,128 +315,48 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
}); });
} }
var internalRenderTask = new InternalRenderTask(complete, params,
this.objs, this.commonObjs,
this.operatorList, this.pageNumber);
this.renderTasks.push(internalRenderTask);
var renderTask = new RenderTask(internalRenderTask);
var self = this; var self = this;
function complete(error) {
self.renderInProgress = false;
if (self.destroyed || self.cleanupAfterRender) {
delete self.displayReadyPromise;
delete self.operatorList;
self.objs.clear();
}
if (error)
promise.reject(error);
else
promise.resolve();
}
var continueCallback = params.continueCallback;
// Once the operatorList and fonts are loaded, do the actual rendering.
this.displayReadyPromise.then( this.displayReadyPromise.then(
function pageDisplayReadyPromise() { function pageDisplayReadyPromise(transparency) {
if (self.destroyed) { if (self.destroyed) {
complete(); complete();
return; return;
} }
stats.time('Rendering');
var gfx = new CanvasGraphics(params.canvasContext, this.commonObjs, internalRenderTask.initalizeGraphics(transparency);
this.objs, params.textLayer, params.imageLayer); internalRenderTask.operatorListChanged();
try { },
this.display(gfx, params.viewport, complete, continueCallback);
} catch (e) {
complete(e);
}
}.bind(this),
function pageDisplayReadPromiseError(reason) { function pageDisplayReadPromiseError(reason) {
complete(reason); complete(reason);
} }
); );
return promise; function complete(error) {
}, var i = self.renderTasks.indexOf(internalRenderTask);
/** if (i >= 0) {
* For internal use only. self.renderTasks.splice(i, 1);
*/ }
startRenderingFromOperatorList:
function PDFPageProxy_startRenderingFromOperatorList(operatorList,
fonts) {
var self = this;
this.operatorList = operatorList;
this.ensureFonts(fonts, if (self.renderTasks.length === 0 &&
function pageStartRenderingFromOperatorListEnsureFonts() { (self.destroyed || self.cleanupAfterRender)) {
self.displayReadyPromise.resolve(); self._destroy();
} }
); if (error) {
}, renderTask.reject(error);
/** } else {
* For internal use only. renderTask.resolve();
*/
ensureFonts: function PDFPageProxy_ensureFonts(fonts, callback) {
this.stats.time('Font Loading');
// Convert the font names to the corresponding font obj.
var fontObjs = [];
for (var i = 0, ii = fonts.length; i < ii; i++) {
var obj = this.commonObjs.getData(fonts[i]);
if (obj.error) {
warn('Error during font loading: ' + obj.error);
continue;
} }
if (!obj.coded) { stats.timeEnd('Rendering');
this.transport.embeddedFontsUsed = true; stats.timeEnd('Overall');
}
fontObjs.push(obj);
} }
// Load all the fonts return renderTask;
FontLoader.bind(
fontObjs,
function pageEnsureFontsFontObjs(fontObjs) {
this.stats.timeEnd('Font Loading');
callback.call(this);
}.bind(this)
);
},
/**
* For internal use only.
*/
display: function PDFPageProxy_display(gfx, viewport, callback,
continueCallback) {
var stats = this.stats;
stats.time('Rendering');
var operatorList = this.operatorList;
gfx.beginDrawing(viewport, operatorList.transparency);
var startIdx = 0;
var length = operatorList.fnArray.length;
var stepper = null;
if (PDFJS.pdfBug && 'StepperManager' in globalScope &&
globalScope['StepperManager'].enabled) {
stepper = globalScope['StepperManager'].create(this.pageNumber - 1);
stepper.init(operatorList);
stepper.nextBreakPoint = stepper.getNextBreakPoint();
}
var continueWrapper;
if (continueCallback)
continueWrapper = function() { continueCallback(next); };
else
continueWrapper = next;
var self = this;
function next() {
startIdx = gfx.executeOperatorList(operatorList, startIdx,
continueWrapper, stepper);
if (startIdx == length) {
gfx.endDrawing();
stats.timeEnd('Rendering');
stats.timeEnd('Overall');
if (callback) callback();
}
}
continueWrapper();
}, },
/** /**
* @return {Promise} That is resolved with the a {string} that is the text * @return {Promise} That is resolved with the a {string} that is the text
@ -466,10 +391,38 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
destroy: function PDFPageProxy_destroy() { destroy: function PDFPageProxy_destroy() {
this.destroyed = true; this.destroyed = true;
if (!this.renderInProgress) { if (this.renderTasks.length === 0) {
delete this.operatorList; this._destroy();
delete this.displayReadyPromise; }
this.objs.clear(); },
/**
* For internal use only. Does the actual cleanup.
*/
_destroy: function PDFPageProxy__destroy() {
delete this.operatorList;
delete this.displayReadyPromise;
this.objs.clear();
},
/**
* For internal use only.
*/
_startRenderPage: function PDFPageProxy_startRenderPage(transparency) {
this.displayReadyPromise.resolve(transparency);
},
/**
* For internal use only.
*/
_renderPageChunk: function PDFPageProxy_renderPageChunk(operatorListChunk) {
// Add the new chunk to the current operator list.
Util.concatenateToArray(this.operatorList.fnArray,
operatorListChunk.fnArray);
Util.concatenateToArray(this.operatorList.argsArray,
operatorListChunk.argsArray);
this.operatorList.lastChunk = operatorListChunk.lastChunk;
// Notify all the rendering tasks there are more operators to be consumed.
for (var i = 0; i < this.renderTasks.length; i++) {
this.renderTasks[i].operatorListChanged();
} }
} }
}; };
@ -644,12 +597,17 @@ var WorkerTransport = (function WorkerTransportClosure() {
promise.resolve(annotations); promise.resolve(annotations);
}, this); }, this);
messageHandler.on('RenderPage', function transportRender(data) { messageHandler.on('StartRenderPage', function transportRender(data) {
var page = this.pageCache[data.pageIndex]; var page = this.pageCache[data.pageIndex];
var depFonts = data.depFonts;
page.stats.timeEnd('Page Request'); page.stats.timeEnd('Page Request');
page.startRenderingFromOperatorList(data.operatorList, depFonts); page._startRenderPage(data.transparency);
}, this);
messageHandler.on('RenderPageChunk', function transportRender(data) {
var page = this.pageCache[data.pageIndex];
page._renderPageChunk(data.operatorList);
}, this); }, this);
messageHandler.on('commonobj', function transportObj(data) { messageHandler.on('commonobj', function transportObj(data) {
@ -662,14 +620,22 @@ var WorkerTransport = (function WorkerTransportClosure() {
case 'Font': case 'Font':
var exportedData = data[2]; var exportedData = data[2];
// At this point, only the font object is created but the font is
// not yet attached to the DOM. This is done in `FontLoader.bind`.
var font; var font;
if ('error' in exportedData) if ('error' in exportedData) {
font = new ErrorFont(exportedData.error); font = new ErrorFont(exportedData.error);
else warn('Error during font loading: ' + font.error);
this.commonObjs.resolve(id, font);
break;
} else {
font = new Font(exportedData); font = new Font(exportedData);
this.commonObjs.resolve(id, font); }
FontLoader.bind(
[font],
function fontReady(fontObjs) {
this.commonObjs.resolve(id, font);
}.bind(this)
);
break; break;
default: default:
error('Got unknown common object type ' + type); error('Got unknown common object type ' + type);
@ -814,3 +780,129 @@ var WorkerTransport = (function WorkerTransportClosure() {
return WorkerTransport; return WorkerTransport;
})(); })();
/**
* RenderTask is basically a promise but adds a cancel function to terminate it.
*/
var RenderTask = (function RenderTaskClosure() {
function RenderTask(internalRenderTask) {
this.internalRenderTask = internalRenderTask;
Promise.call(this);
}
RenderTask.prototype = Object.create(Promise.prototype);
/**
* Cancel the rendering task. If the task is curently rendering it will not be
* cancelled until graphics pauses with a timeout. The promise that this
* object extends will resolved when cancelled.
*/
RenderTask.prototype.cancel = function RenderTask_cancel() {
this.internalRenderTask.cancel();
};
return RenderTask;
})();
var InternalRenderTask = (function InternalRenderTaskClosure() {
function InternalRenderTask(callback, params, objs, commonObjs, operatorList,
pageNumber) {
this.callback = callback;
this.params = params;
this.objs = objs;
this.commonObjs = commonObjs;
this.operatorListIdx = null;
this.operatorList = operatorList;
this.pageNumber = pageNumber;
this.running = false;
this.graphicsReadyCallback = null;
this.graphicsReady = false;
this.cancelled = false;
}
InternalRenderTask.prototype = {
initalizeGraphics:
function InternalRenderTask_initalizeGraphics(transparency) {
if (this.cancelled) {
return;
}
if (PDFJS.pdfBug && 'StepperManager' in globalScope &&
globalScope.StepperManager.enabled) {
this.stepper = globalScope.StepperManager.create(this.pageNumber - 1);
this.stepper.init(this.operatorList);
this.stepper.nextBreakPoint = this.stepper.getNextBreakPoint();
}
var params = this.params;
this.gfx = new CanvasGraphics(params.canvasContext, this.commonObjs,
this.objs, params.textLayer,
params.imageLayer);
this.gfx.beginDrawing(params.viewport, transparency);
this.operatorListIdx = 0;
this.graphicsReady = true;
if (this.graphicsReadyCallback) {
this.graphicsReadyCallback();
}
},
cancel: function InternalRenderTask_cancel() {
this.running = false;
this.cancelled = true;
this.callback();
},
operatorListChanged: function InternalRenderTask_operatorListChanged() {
if (!this.graphicsReady) {
if (!this.graphicsReadyCallback) {
this.graphicsReadyCallback = this._continue.bind(this);
}
return;
}
if (this.stepper) {
this.stepper.updateOperatorList(this.operatorList);
}
if (this.running) {
return;
}
this._continue();
},
_continue: function InternalRenderTask__continue() {
this.running = true;
if (this.cancelled) {
return;
}
if (this.params.continueCallback) {
this.params.continueCallback(this._next.bind(this));
} else {
this._next();
}
},
_next: function InternalRenderTask__next() {
if (this.cancelled) {
return;
}
this.operatorListIdx = this.gfx.executeOperatorList(this.operatorList,
this.operatorListIdx,
this._continue.bind(this),
this.stepper);
if (this.operatorListIdx === this.operatorList.fnArray.length) {
this.running = false;
if (this.operatorList.lastChunk) {
this.gfx.endDrawing();
this.callback();
}
}
}
};
return InternalRenderTask;
})();

View File

@ -670,7 +670,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
this.setFlatness(value); this.setFlatness(value);
break; break;
case 'Font': case 'Font':
this.setFont(state[1], state[2]); this.setFont(value[0], value[1]);
break; break;
case 'CA': case 'CA':
this.current.strokeAlpha = state[1]; this.current.strokeAlpha = state[1];

View File

@ -18,7 +18,8 @@
isArrayBuffer, isDict, isName, isStream, isString, Lexer, isArrayBuffer, isDict, isName, isStream, isString, Lexer,
Linearization, NullStream, PartialEvaluator, shadow, Stream, Linearization, NullStream, PartialEvaluator, shadow, Stream,
StreamsSequenceStream, stringToPDFString, TODO, Util, warn, XRef, StreamsSequenceStream, stringToPDFString, TODO, Util, warn, XRef,
MissingDataException, Promise, Annotation, ObjectLoader */ MissingDataException, Promise, Annotation, ObjectLoader, OperatorList
*/
'use strict'; 'use strict';
@ -183,33 +184,35 @@ var Page = (function PageClosure() {
dataPromises.then(function(data) { dataPromises.then(function(data) {
var contentStream = data[0]; var contentStream = data[0];
partialEvaluator.getOperatorList(contentStream, self.resources).then(
function(data) { var opList = new OperatorList(handler, self.pageIndex);
pageListPromise.resolve(data);
}, handler.send('StartRenderPage', {
reject transparency: partialEvaluator.hasBlendModes(self.resources),
); pageIndex: self.pageIndex
});
partialEvaluator.getOperatorList(contentStream, self.resources, opList);
pageListPromise.resolve(opList);
}); });
var annotationsPromise = pdfManager.ensure(this, 'annotations'); var annotationsPromise = pdfManager.ensure(this, 'annotations');
Promise.all([pageListPromise, annotationsPromise]).then(function(datas) { Promise.all([pageListPromise, annotationsPromise]).then(function(datas) {
var pageData = datas[0]; var pageOpList = datas[0];
var pageQueue = pageData.queue;
var annotations = datas[1]; var annotations = datas[1];
if (annotations.length === 0) { if (annotations.length === 0) {
PartialEvaluator.optimizeQueue(pageQueue); PartialEvaluator.optimizeQueue(pageOpList);
promise.resolve(pageData); pageOpList.flush(true);
promise.resolve(pageOpList);
return; return;
} }
var dependencies = pageData.dependencies;
var annotationsReadyPromise = Annotation.appendToOperatorList( var annotationsReadyPromise = Annotation.appendToOperatorList(
annotations, pageQueue, pdfManager, dependencies, partialEvaluator); annotations, pageOpList, pdfManager, partialEvaluator);
annotationsReadyPromise.then(function () { annotationsReadyPromise.then(function () {
PartialEvaluator.optimizeQueue(pageQueue); PartialEvaluator.optimizeQueue(pageOpList);
pageOpList.flush(true);
promise.resolve(pageData); promise.resolve(pageOpList);
}, reject); }, reject);
}, reject); }, reject);
@ -244,12 +247,9 @@ var Page = (function PageClosure() {
self.pageIndex, 'p' + self.pageIndex + '_', self.pageIndex, 'p' + self.pageIndex + '_',
self.idCounters); self.idCounters);
partialEvaluator.getTextContent( var bidiTexts = partialEvaluator.getTextContent(contentStream,
contentStream, self.resources).then(function(bidiTexts) { self.resources);
textContentPromise.resolve({ textContentPromise.resolve(bidiTexts);
bidiTexts: bidiTexts
});
});
}); });
return textContentPromise; return textContentPromise;

View File

@ -153,24 +153,51 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var TILING_PATTERN = 1, SHADING_PATTERN = 2; var TILING_PATTERN = 1, SHADING_PATTERN = 2;
function createOperatorList(fnArray, argsArray, dependencies) {
return {
queue: {
fnArray: fnArray || [],
argsArray: argsArray || []
},
dependencies: dependencies || {}
};
}
PartialEvaluator.prototype = { PartialEvaluator.prototype = {
hasBlendModes: function PartialEvaluator_hasBlendModes(resources) {
if (!isDict(resources)) {
return false;
}
var nodes = [resources];
while (nodes.length) {
var node = nodes.shift();
// First check the current resources for blend modes.
var graphicStates = node.get('ExtGState');
if (isDict(graphicStates)) {
graphicStates = graphicStates.getAll();
for (var key in graphicStates) {
var graphicState = graphicStates[key];
var bm = graphicState['BM'];
if (isName(bm) && bm.name !== 'Normal') {
return true;
}
}
}
// Descend into the XObjects to look for more resources and blend modes.
var xObjects = node.get('XObject');
if (!isDict(xObjects)) {
continue;
}
xObjects = xObjects.getAll();
for (var key in xObjects) {
var xObject = xObjects[key];
if (!isStream(xObject)) {
continue;
}
var xResources = xObject.dict.get('Resources');
if (isDict(xResources)) {
nodes.push(xResources);
}
}
}
return false;
},
buildFormXObject: function PartialEvaluator_buildFormXObject(resources, buildFormXObject: function PartialEvaluator_buildFormXObject(resources,
xobj, smask) { xobj, smask,
operatorList) {
var self = this; var self = this;
var promise = new Promise();
var fnArray = [];
var argsArray = [];
var matrix = xobj.dict.get('Matrix'); var matrix = xobj.dict.get('Matrix');
var bbox = xobj.dict.get('BBox'); var bbox = xobj.dict.get('BBox');
@ -191,44 +218,22 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
// There is also a group colorspace, but since we put everything in // There is also a group colorspace, but since we put everything in
// RGB I'm not sure we need it. // RGB I'm not sure we need it.
} }
fnArray.push('beginGroup'); operatorList.addOp('beginGroup', [groupOptions]);
argsArray.push([groupOptions]);
} }
fnArray.push('paintFormXObjectBegin'); operatorList.addOp('paintFormXObjectBegin', [matrix, bbox]);
argsArray.push([matrix, bbox]);
// Pass in the current `queue` object. That means the `fnArray` this.getOperatorList(xobj, xobj.dict.get('Resources') || resources,
// and the `argsArray` in this scope is reused and new commands operatorList);
// are added to them. operatorList.addOp('paintFormXObjectEnd', []);
var opListPromise = this.getOperatorList(xobj,
xobj.dict.get('Resources') || resources);
opListPromise.then(function(data) {
var queue = data.queue;
var dependencies = data.dependencies;
Util.prependToArray(queue.fnArray, fnArray);
Util.prependToArray(queue.argsArray, argsArray);
self.insertDependencies(queue, dependencies);
queue.fnArray.push('paintFormXObjectEnd'); if (group) {
queue.argsArray.push([]); operatorList.addOp('endGroup', [groupOptions]);
}
if (group) {
queue.fnArray.push('endGroup');
queue.argsArray.push([groupOptions]);
}
promise.resolve({
queue: queue,
dependencies: dependencies
});
});
return promise;
}, },
buildPaintImageXObject: function PartialEvaluator_buildPaintImageXObject( buildPaintImageXObject: function PartialEvaluator_buildPaintImageXObject(
resources, image, inline) { resources, image, inline, operatorList) {
var self = this; var self = this;
var dict = image.dict; var dict = image.dict;
var w = dict.get('Width', 'W'); var w = dict.get('Width', 'W');
@ -236,14 +241,9 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if (PDFJS.maxImageSize !== -1 && w * h > PDFJS.maxImageSize) { if (PDFJS.maxImageSize !== -1 && w * h > PDFJS.maxImageSize) {
warn('Image exceeded maximum allowed size and was removed.'); warn('Image exceeded maximum allowed size and was removed.');
return null; return;
} }
var dependencies = {};
var retData = {
dependencies: dependencies
};
var imageMask = dict.get('ImageMask', 'IM') || false; var imageMask = dict.get('ImageMask', 'IM') || false;
if (imageMask) { if (imageMask) {
// This depends on a tmpCanvas beeing filled with the // This depends on a tmpCanvas beeing filled with the
@ -259,10 +259,11 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var decode = dict.get('Decode', 'D'); var decode = dict.get('Decode', 'D');
var inverseDecode = !!decode && decode[0] > 0; var inverseDecode = !!decode && decode[0] > 0;
retData.fn = 'paintImageMaskXObject'; operatorList.addOp('paintImageMaskXObject',
retData.args = [PDFImage.createMask(imgArray, width, height, [PDFImage.createMask(imgArray, width, height,
inverseDecode)]; inverseDecode)]
return retData; );
return;
} }
var softMask = dict.get('SMask', 'SM') || false; var softMask = dict.get('SMask', 'SM') || false;
@ -276,64 +277,53 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var imageObj = new PDFImage(this.xref, resources, image, var imageObj = new PDFImage(this.xref, resources, image,
inline, null, null); inline, null, null);
var imgData = imageObj.getImageData(); var imgData = imageObj.getImageData();
retData.fn = 'paintInlineImageXObject'; operatorList.addOp('paintInlineImageXObject', [imgData]);
retData.args = [imgData]; return;
return retData;
} }
// If there is no imageMask, create the PDFImage and a lot // If there is no imageMask, create the PDFImage and a lot
// of image processing can be done here. // of image processing can be done here.
var uniquePrefix = this.uniquePrefix || ''; var uniquePrefix = this.uniquePrefix || '';
var objId = 'img_' + uniquePrefix + (++this.idCounters.obj); var objId = 'img_' + uniquePrefix + (++this.idCounters.obj);
dependencies[objId] = true; operatorList.addDependency(objId);
retData.args = [objId, w, h]; var args = [objId, w, h];
if (!softMask && !mask && image instanceof JpegStream && if (!softMask && !mask && image instanceof JpegStream &&
image.isNativelySupported(this.xref, resources)) { image.isNativelySupported(this.xref, resources)) {
// These JPEGs don't need any more processing so we can just send it. // These JPEGs don't need any more processing so we can just send it.
retData.fn = 'paintJpegXObject'; operatorList.addOp('paintJpegXObject', args);
this.handler.send( this.handler.send(
'obj', [objId, this.pageIndex, 'JpegStream', image.getIR()]); 'obj', [objId, this.pageIndex, 'JpegStream', image.getIR()]);
return retData; return;
} }
retData.fn = 'paintImageXObject';
PDFImage.buildImage(function(imageObj) { PDFImage.buildImage(function(imageObj) {
var imgData = imageObj.getImageData(); var imgData = imageObj.getImageData();
self.handler.send('obj', [objId, self.pageIndex, 'Image', imgData]); self.handler.send('obj', [objId, self.pageIndex, 'Image', imgData]);
}, self.handler, self.xref, resources, image, inline); }, self.handler, self.xref, resources, image, inline);
return retData; operatorList.addOp('paintImageXObject', args);
}, },
handleTilingType: function PartialEvaluator_handleTilingType( handleTilingType: function PartialEvaluator_handleTilingType(
fn, args, resources, pattern, patternDict) { fn, args, resources, pattern, patternDict,
var self = this; operatorList) {
// Create an IR of the pattern code. // Create an IR of the pattern code.
var promise = new Promise(); var tilingOpList = this.getOperatorList(pattern,
var opListPromise = this.getOperatorList(pattern, patternDict.get('Resources') || resources);
patternDict.get('Resources') || resources); // Add the dependencies to the parent operator list so they are resolved
opListPromise.then(function(data) { // before sub operator list is executed synchronously.
var opListData = createOperatorList([], [], data.dependencies); operatorList.addDependencies(tilingOpList.dependencies);
var queue = opListData.queue; operatorList.addOp(fn, TilingPattern.getIR({
fnArray: tilingOpList.fnArray,
// Add the dependencies that are required to execute the argsArray: tilingOpList.argsArray
// operatorList. }, patternDict, args));
self.insertDependencies(queue, data.dependencies);
queue.fnArray.push(fn);
queue.argsArray.push(
TilingPattern.getIR(data.queue, patternDict, args));
promise.resolve(opListData);
});
return promise;
}, },
handleSetFont: function PartialEvaluator_handleSetFont( handleSetFont: function PartialEvaluator_handleSetFont(
resources, fontArgs, font) { resources, fontArgs, fontRef, operatorList) {
var promise = new Promise();
// TODO(mack): Not needed? // TODO(mack): Not needed?
var fontName; var fontName;
if (fontArgs) { if (fontArgs) {
@ -341,69 +331,27 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
fontName = fontArgs[0].name; fontName = fontArgs[0].name;
} }
var self = this; var self = this;
var fontPromise = this.loadFont(fontName, font, this.xref, resources); var font = this.loadFont(fontName, fontRef, this.xref, resources,
fontPromise.then(function(data) { operatorList);
var font = data.font; var loadedName = font.loadedName;
var loadedName = font.loadedName; if (!font.sent) {
if (!font.sent) { var fontData = font.translated.exportData();
var fontData = font.translated.exportData();
self.handler.send('commonobj', [ self.handler.send('commonobj', [
loadedName, loadedName,
'Font', 'Font',
fontData fontData
]); ]);
font.sent = true; font.sent = true;
}
// Ensure the font is ready before the font is set
// and later on used for drawing.
// OPTIMIZE: This should get insert to the operatorList only once per
// page.
var fnArray = [];
var argsArray = [];
var queue = {
fnArray: fnArray,
argsArray: argsArray
};
var dependencies = data.dependencies;
dependencies[loadedName] = true;
self.insertDependencies(queue, dependencies);
if (fontArgs) {
fontArgs[0] = loadedName;
fnArray.push('setFont');
argsArray.push(fontArgs);
}
promise.resolve({
loadedName: loadedName,
queue: queue,
dependencies: dependencies
});
});
return promise;
},
insertDependencies: function PartialEvaluator_insertDependencies(
queue, dependencies) {
var fnArray = queue.fnArray;
var argsArray = queue.argsArray;
var depList = Object.keys(dependencies);
if (depList.length) {
fnArray.push('dependency');
argsArray.push(depList);
} }
return loadedName;
}, },
setGState: function PartialEvaluator_setGState(resources, gState) { setGState: function PartialEvaluator_setGState(resources, gState,
operatorList) {
var self = this; var self = this;
var opListData = createOperatorList();
var queue = opListData.queue;
var fnArray = queue.fnArray;
var argsArray = queue.argsArray;
var dependencies = opListData.dependencies;
// TODO(mack): This should be rewritten so that this function returns // TODO(mack): This should be rewritten so that this function returns
// what should be added to the queue during each iteration // what should be added to the queue during each iteration
function setGStateForKey(gStateObj, key, value) { function setGStateForKey(gStateObj, key, value) {
@ -422,21 +370,12 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
gStateObj.push([key, value]); gStateObj.push([key, value]);
break; break;
case 'Font': case 'Font':
var promise = new Promise(); var loadedName = self.handleSetFont(resources, null, value[0],
self.handleSetFont(resources, null, value[0]).then(function(data) { operatorList);
var gState = ['Font', data.loadedName, value[1]]; operatorList.addDependency(loadedName);
promise.resolve({ gStateObj.push([key, [loadedName, value[1]]]);
gState: gState,
queue: data.queue,
dependencies: data.dependencies
});
});
gStateObj.push(['promise', promise]);
break; break;
case 'BM': case 'BM':
if (!isName(value) || value.name !== 'Normal') {
queue.transparency = true;
}
gStateObj.push([key, value]); gStateObj.push([key, value]);
break; break;
case 'SMask': case 'SMask':
@ -477,47 +416,18 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
setGStateForKey(gStateObj, key, value); setGStateForKey(gStateObj, key, value);
} }
var promises = []; operatorList.addOp('setGState', [gStateObj]);
var indices = [];
for (var i = 0, n = gStateObj.length; i < n; ++i) {
var value = gStateObj[i];
if (value[0] === 'promise') {
promises.push(value[1]);
indices.push(i);
}
}
var promise = new Promise();
Promise.all(promises).then(function(datas) {
for (var i = 0, n = datas.length; i < n; ++i) {
var data = datas[i];
var index = indices[i];
gStateObj[index] = data.gState;
var subQueue = data.queue;
Util.concatenateToArray(fnArray, subQueue.fnArray);
Util.concatenateToArray(argsArray, subQueue.argsArray);
queue.transparency = subQueue.transparency || queue.transparency;
Util.extendObj(dependencies, data.dependencies);
}
fnArray.push('setGState');
argsArray.push([gStateObj]);
promise.resolve(opListData);
});
return promise;
}, },
loadFont: function PartialEvaluator_loadFont(fontName, font, xref, loadFont: function PartialEvaluator_loadFont(fontName, font, xref,
resources) { resources,
function errorFont(promise) { parentOperatorList) {
promise.resolve({
font: { function errorFont() {
translated: new ErrorFont('Font ' + fontName + ' is not available'), return {
loadedName: 'g_font_error' translated: new ErrorFont('Font ' + fontName + ' is not available'),
}, loadedName: 'g_font_error'
dependencies: {} };
});
return promise;
} }
var fontRef; var fontRef;
@ -530,20 +440,19 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
fontRef = fontRes.getRaw(fontName); fontRef = fontRes.getRaw(fontName);
} else { } else {
warn('fontRes not available'); warn('fontRes not available');
return errorFont(new Promise()); return errorFont();
} }
} }
if (this.fontCache.has(fontRef)) { if (this.fontCache.has(fontRef)) {
return this.fontCache.get(fontRef); return this.fontCache.get(fontRef);
} }
var promise = new Promise();
this.fontCache.put(fontRef, promise);
font = xref.fetchIfRef(fontRef); font = xref.fetchIfRef(fontRef);
if (!isDict(font)) { if (!isDict(font)) {
return errorFont(promise); return errorFont();
} }
this.fontCache.put(fontRef, font);
// keep track of each font we translated so the caller can // keep track of each font we translated so the caller can
// load them asynchronously before calling display on a page // load them asynchronously before calling display on a page
@ -562,55 +471,37 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if (font.translated.loadCharProcs) { if (font.translated.loadCharProcs) {
var charProcs = font.get('CharProcs').getAll(); var charProcs = font.get('CharProcs').getAll();
var fontResources = font.get('Resources') || resources; var fontResources = font.get('Resources') || resources;
var opListPromises = [];
var charProcKeys = Object.keys(charProcs); var charProcKeys = Object.keys(charProcs);
var charProcOperatorList = {};
for (var i = 0, n = charProcKeys.length; i < n; ++i) { for (var i = 0, n = charProcKeys.length; i < n; ++i) {
var key = charProcKeys[i]; var key = charProcKeys[i];
var glyphStream = charProcs[key]; var glyphStream = charProcs[key];
opListPromises.push( var operatorList = this.getOperatorList(glyphStream, fontResources);
this.getOperatorList(glyphStream, fontResources)); charProcOperatorList[key] = operatorList.getIR();
} if (!parentOperatorList) {
Promise.all(opListPromises).then(function(datas) { continue;
var charProcOperatorList = {};
var dependencies = {};
for (var i = 0, n = charProcKeys.length; i < n; ++i) {
var key = charProcKeys[i];
var data = datas[i];
charProcOperatorList[key] = data.queue;
Util.extendObj(dependencies, data.dependencies);
} }
font.translated.charProcOperatorList = charProcOperatorList; // Add the dependencies to the parent operator list so they are
font.loaded = true; // resolved before sub operator list is executed synchronously.
promise.resolve({ parentOperatorList.addDependencies(charProcOperatorList.dependencies);
font: font, }
dependencies: dependencies font.translated.charProcOperatorList = charProcOperatorList;
}); font.loaded = true;
}.bind(this));
} else { } else {
font.loaded = true; font.loaded = true;
promise.resolve({
font: font,
dependencies: {}
});
} }
return promise; return font;
}, },
getOperatorList: function PartialEvaluator_getOperatorList(stream, getOperatorList: function PartialEvaluator_getOperatorList(stream,
resources) { resources,
operatorList) {
var self = this; var self = this;
var xref = this.xref; var xref = this.xref;
var handler = this.handler; var handler = this.handler;
var fnArray = []; operatorList = operatorList || new OperatorList();
var argsArray = [];
var queue = {
transparency: false,
fnArray: fnArray,
argsArray: argsArray
};
var dependencies = {};
resources = resources || new Dict(); resources = resources || new Dict();
var xobjs = resources.get('XObject') || new Dict(); var xobjs = resources.get('XObject') || new Dict();
@ -621,6 +512,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var promise = new Promise(); var promise = new Promise();
var args = []; var args = [];
nextOp:
while (true) { while (true) {
var obj = parser.getObj(); var obj = parser.getObj();
@ -677,10 +569,10 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var typeNum = dict.get('PatternType'); var typeNum = dict.get('PatternType');
if (typeNum == TILING_PATTERN) { if (typeNum == TILING_PATTERN) {
var patternPromise = self.handleTilingType( self.handleTilingType(fn, args, resources, pattern, dict,
fn, args, resources, pattern, dict); operatorList);
fn = 'promise'; args = [];
args = [patternPromise]; continue;
} else if (typeNum == SHADING_PATTERN) { } else if (typeNum == SHADING_PATTERN) {
var shading = dict.get('Shading'); var shading = dict.get('Shading');
var matrix = dict.get('Matrix'); var matrix = dict.get('Matrix');
@ -706,37 +598,28 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
); );
if ('Form' == type.name) { if ('Form' == type.name) {
fn = 'promise'; self.buildFormXObject(resources, xobj, null, operatorList);
args = [self.buildFormXObject(resources, xobj)]; args = [];
continue;
} else if ('Image' == type.name) { } else if ('Image' == type.name) {
var data = self.buildPaintImageXObject( self.buildPaintImageXObject(resources, xobj, false,
resources, xobj, false); operatorList);
if (!data) { args = [];
args = []; continue;
continue;
}
Util.extendObj(dependencies, data.dependencies);
self.insertDependencies(queue, data.dependencies);
fn = data.fn;
args = data.args;
} else { } else {
error('Unhandled XObject subtype ' + type.name); error('Unhandled XObject subtype ' + type.name);
} }
} }
} else if (cmd == 'Tf') { // eagerly collect all fonts } else if (cmd == 'Tf') { // eagerly collect all fonts
fn = 'promise'; var loadedName = self.handleSetFont(resources, args, null,
args = [self.handleSetFont(resources, args)]; operatorList);
operatorList.addDependency(loadedName);
fn = 'setFont';
args[0] = loadedName;
} else if (cmd == 'EI') { } else if (cmd == 'EI') {
var data = self.buildPaintImageXObject( self.buildPaintImageXObject(resources, args[0], true, operatorList);
resources, args[0], true); args = [];
if (!data) { continue;
args = [];
continue;
}
Util.extendObj(dependencies, data.dependencies);
self.insertDependencies(queue, data.dependencies);
fn = data.fn;
args = data.args;
} }
switch (fn) { switch (fn) {
@ -768,12 +651,12 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
break; break;
var gState = extGState.get(dictName.name); var gState = extGState.get(dictName.name);
fn = 'promise'; self.setGState(resources, gState, operatorList);
args = [self.setGState(resources, gState)]; args = [];
continue nextOp;
} // switch } // switch
fnArray.push(fn); operatorList.addOp(fn, args);
argsArray.push(args);
args = []; args = [];
parser.saveState(); parser.saveState();
} else if (obj !== null && obj !== undefined) { } else if (obj !== null && obj !== undefined) {
@ -782,163 +665,86 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
} }
} }
var subQueuePromises = []; return operatorList;
for (var i = 0; i < fnArray.length; ++i) {
if (fnArray[i] === 'promise') {
subQueuePromises.push(argsArray[i][0]);
}
}
Promise.all(subQueuePromises).then(function(datas) {
// TODO(mack): Optimize by using repositioning elements
// in original queue rather than creating new queue
for (var i = 0, n = datas.length; i < n; ++i) {
var data = datas[i];
var subQueue = data.queue;
queue.transparency = subQueue.transparency || queue.transparency;
Util.extendObj(dependencies, data.dependencies);
}
var newFnArray = [];
var newArgsArray = [];
var currOffset = 0;
var subQueueIdx = 0;
for (var i = 0, n = fnArray.length; i < n; ++i) {
var offset = i + currOffset;
if (fnArray[i] === 'promise') {
var data = datas[subQueueIdx++];
var subQueue = data.queue;
var subQueueFnArray = subQueue.fnArray;
var subQueueArgsArray = subQueue.argsArray;
for (var j = 0, nn = subQueueFnArray.length; j < nn; ++j) {
newFnArray[offset + j] = subQueueFnArray[j];
newArgsArray[offset + j] = subQueueArgsArray[j];
}
currOffset += subQueueFnArray.length - 1;
} else {
newFnArray[offset] = fnArray[i];
newArgsArray[offset] = argsArray[i];
}
}
promise.resolve({
queue: {
fnArray: newFnArray,
argsArray: newArgsArray,
transparency: queue.transparency
},
dependencies: dependencies
});
});
return promise;
}, },
getTextContent: function PartialEvaluator_getTextContent( getTextContent: function PartialEvaluator_getTextContent(
stream, resources) { stream, resources, state) {
var bidiTexts;
var SPACE_FACTOR = 0.35; var SPACE_FACTOR = 0.35;
var MULTI_SPACE_FACTOR = 1.5; var MULTI_SPACE_FACTOR = 1.5;
if (!state) {
bidiTexts = [];
state = {
bidiTexts: bidiTexts
};
} else {
bidiTexts = state.bidiTexts;
}
var self = this; var self = this;
var xref = this.xref;
var statePromise = new Promise(); function handleSetFont(fontName, fontRef) {
return self.loadFont(fontName, fontRef, xref, resources, null);
function handleSetFont(fontName, fontRef, resources) {
var promise = new Promise();
self.loadFont(fontName, fontRef, self.xref, resources).then(
function(data) {
promise.resolve(data.font.translated);
}
);
return promise;
} }
function getBidiText(str, startLevel, vertical) { resources = xref.fetchIfRef(resources) || new Dict();
if (str) {
return PDFJS.bidi(str, -1, vertical);
}
}
resources = this.xref.fetchIfRef(resources) || new Dict();
// The xobj is parsed iff it's needed, e.g. if there is a `DO` cmd. // The xobj is parsed iff it's needed, e.g. if there is a `DO` cmd.
var xobjs = null; var xobjs = null;
var parser = new Parser(new Lexer(stream), false); var parser = new Parser(new Lexer(stream), false);
var res = resources;
var args = [], obj;
var chunkPromises = []; var chunk = '';
var fontPromise; var font = null;
var args = []; while (!isEOF(obj = parser.getObj())) {
while (true) {
var obj = parser.getObj();
if (isEOF(obj)) {
break;
}
if (isCmd(obj)) { if (isCmd(obj)) {
var cmd = obj.cmd; var cmd = obj.cmd;
switch (cmd) { switch (cmd) {
// TODO: Add support for SAVE/RESTORE and XFORM here. // TODO: Add support for SAVE/RESTORE and XFORM here.
case 'Tf': case 'Tf':
fontPromise = handleSetFont(args[0].name, null, resources); font = handleSetFont(args[0].name).translated;
//.translated;
break; break;
case 'TJ': case 'TJ':
var chunkPromise = new Promise(); var items = args[0];
chunkPromises.push(chunkPromise); for (var j = 0, jj = items.length; j < jj; j++) {
fontPromise.then(function(items, chunkPromise, font) { if (typeof items[j] === 'string') {
var chunk = ''; chunk += fontCharsToUnicode(items[j], font);
for (var j = 0, jj = items.length; j < jj; j++) { } else if (items[j] < 0 && font.spaceWidth > 0) {
if (typeof items[j] === 'string') { var fakeSpaces = -items[j] / font.spaceWidth;
chunk += fontCharsToUnicode(items[j], font); if (fakeSpaces > MULTI_SPACE_FACTOR) {
} else if (items[j] < 0 && font.spaceWidth > 0) { fakeSpaces = Math.round(fakeSpaces);
var fakeSpaces = -items[j] / font.spaceWidth; while (fakeSpaces--) {
if (fakeSpaces > MULTI_SPACE_FACTOR) {
fakeSpaces = Math.round(fakeSpaces);
while (fakeSpaces--) {
chunk += ' ';
}
} else if (fakeSpaces > SPACE_FACTOR) {
chunk += ' '; chunk += ' ';
} }
} else if (fakeSpaces > SPACE_FACTOR) {
chunk += ' ';
} }
} }
chunkPromise.resolve( }
getBidiText(chunk, -1, font.vertical));
}.bind(null, args[0], chunkPromise));
break; break;
case 'Tj': case 'Tj':
var chunkPromise = new Promise(); chunk += fontCharsToUnicode(args[0], font);
chunkPromises.push(chunkPromise);
fontPromise.then(function(charCodes, chunkPromise, font) {
var chunk = fontCharsToUnicode(charCodes, font);
chunkPromise.resolve(
getBidiText(chunk, -1, font.vertical));
}.bind(null, args[0], chunkPromise));
break; break;
case '\'': case '\'':
// For search, adding a extra white space for line breaks // For search, adding a extra white space for line breaks would be
// would be better here, but that causes too much spaces in // better here, but that causes too much spaces in the
// the text-selection divs. // text-selection divs.
var chunkPromise = new Promise(); chunk += fontCharsToUnicode(args[0], font);
chunkPromises.push(chunkPromise);
fontPromise.then(function(charCodes, chunkPromise, font) {
var chunk = fontCharsToUnicode(charCodes, font);
chunkPromise.resolve(
getBidiText(chunk, -1, font.vertical));
}.bind(null, args[0], chunkPromise));
break; break;
case '"': case '"':
// Note comment in "'" // Note comment in "'"
var chunkPromise = new Promise(); chunk += fontCharsToUnicode(args[2], font);
chunkPromises.push(chunkPromise);
fontPromise.then(function(charCodes, chunkPromise, font) {
var chunk = fontCharsToUnicode(charCodes, font);
chunkPromise.resolve(
getBidiText(chunk, -1, font.vertical));
}.bind(null, args[2], chunkPromise));
break; break;
case 'Do': case 'Do':
// Set the chunk such that the following if won't add something
// to the state.
chunk = '';
if (args[0].code) { if (args[0].code) {
break; break;
} }
@ -951,8 +757,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var xobj = xobjs.get(name); var xobj = xobjs.get(name);
if (!xobj) if (!xobj)
break; break;
assertWellFormed(isStream(xobj), assertWellFormed(isStream(xobj), 'XObject should be a stream');
'XObject should be a stream');
var type = xobj.dict.get('Subtype'); var type = xobj.dict.get('Subtype');
assertWellFormed( assertWellFormed(
@ -963,11 +768,11 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if ('Form' !== type.name) if ('Form' !== type.name)
break; break;
var chunkPromise = self.getTextContent( state = this.getTextContent(
xobj, xobj,
xobj.dict.get('Resources') || resources xobj.dict.get('Resources') || resources,
state
); );
chunkPromises.push(chunkPromise);
break; break;
case 'gs': case 'gs':
var dictName = args[0]; var dictName = args[0];
@ -980,37 +785,27 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
for (var i = 0; i < gsState.length; i++) { for (var i = 0; i < gsState.length; i++) {
if (gsState[i] === 'Font') { if (gsState[i] === 'Font') {
fontPromise = handleSetFont( font = handleSetFont(args[0].name).translated;
args[0].name, null, resources);
} }
} }
break; break;
} // switch } // switch
if (chunk !== '') {
var bidiText = PDFJS.bidi(chunk, -1, font.vertical);
bidiTexts.push(bidiText);
chunk = '';
}
args = []; args = [];
parser.saveState();
} else if (obj !== null && obj !== undefined) { } else if (obj !== null && obj !== undefined) {
assertWellFormed(args.length <= 33, 'Too many arguments'); assertWellFormed(args.length <= 33, 'Too many arguments');
args.push(obj); args.push(obj);
} }
} // while } // while
Promise.all(chunkPromises).then(function(datas) { return state;
var bidiTexts = [];
for (var i = 0, n = datas.length; i < n; ++i) {
var bidiText = datas[i];
if (!bidiText) {
continue;
} else if (isArray(bidiText)) {
Util.concatenateToArray(bidiTexts, bidiText);
} else {
bidiTexts.push(bidiText);
}
}
statePromise.resolve(bidiTexts);
});
return statePromise;
}, },
extractDataStructures: function extractDataStructures: function
@ -1639,6 +1434,74 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
return PartialEvaluator; return PartialEvaluator;
})(); })();
var OperatorList = (function OperatorListClosure() {
var CHUNK_SIZE = 100;
function OperatorList(messageHandler, pageIndex) {
this.messageHandler = messageHandler;
this.fnArray = [];
this.argsArray = [];
this.dependencies = {},
this.pageIndex = pageIndex;
}
OperatorList.prototype = {
addOp: function(fn, args) {
this.fnArray.push(fn);
this.argsArray.push(args);
if (this.messageHandler && this.fnArray.length >= CHUNK_SIZE) {
this.flush();
}
},
addDependency: function(dependency) {
if (dependency in this.dependencies) {
return;
}
this.dependencies[dependency] = true;
this.addOp('dependency', [dependency]);
},
addDependencies: function(dependencies) {
for (var key in dependencies) {
this.addDependency(key);
}
},
addOpList: function(opList) {
Util.concatenateToArray(this.fnArray, opList.fnArray);
Util.concatenateToArray(this.argsArray, opList.argsArray);
Util.extendObj(this.dependencies, opList.dependencies);
},
getIR: function() {
return {
fnArray: this.fnArray,
argsArray: this.argsArray
};
},
flush: function(lastChunk) {
PartialEvaluator.optimizeQueue(this);
this.messageHandler.send('RenderPageChunk', {
operatorList: {
fnArray: this.fnArray,
argsArray: this.argsArray,
lastChunk: lastChunk
},
pageIndex: this.pageIndex
});
this.dependencies = [];
this.fnArray = [];
this.argsArray = [];
}
};
return OperatorList;
})();
var EvalState = (function EvalStateClosure() { var EvalState = (function EvalStateClosure() {
function EvalState() { function EvalState() {
// Are soft masks and alpha values shapes or opacities? // Are soft masks and alpha values shapes or opacities?

View File

@ -903,14 +903,14 @@ var StatTimer = (function StatTimerClosure() {
if (!this.enabled) if (!this.enabled)
return; return;
if (name in this.started) if (name in this.started)
throw 'Timer is already running for ' + name; warn('Timer is already running for ' + name);
this.started[name] = Date.now(); this.started[name] = Date.now();
}, },
timeEnd: function StatTimer_timeEnd(name) { timeEnd: function StatTimer_timeEnd(name) {
if (!this.enabled) if (!this.enabled)
return; return;
if (!(name in this.started)) if (!(name in this.started))
throw 'Timer has not been started for ' + name; warn('Timer has not been started for ' + name);
this.times.push({ this.times.push({
'name': name, 'name': name,
'start': this.started[name], 'start': this.started[name],

View File

@ -398,31 +398,11 @@ var WorkerMessageHandler = {
var pageNum = data.pageIndex + 1; var pageNum = data.pageIndex + 1;
var start = Date.now(); var start = Date.now();
// Pre compile the pdf page and fetch the fonts/images. // Pre compile the pdf page and fetch the fonts/images.
page.getOperatorList(handler).then(function(opListData) { page.getOperatorList(handler).then(function(operatorList) {
var operatorList = opListData.queue;
var dependency = Object.keys(opListData.dependencies);
// The following code does quite the same as
// Page.prototype.startRendering, but stops at one point and sends the
// result back to the main thread.
log('page=%d - getOperatorList: time=%dms, len=%d', pageNum, log('page=%d - getOperatorList: time=%dms, len=%d', pageNum,
Date.now() - start, operatorList.fnArray.length); Date.now() - start, operatorList.fnArray.length);
// Filter the dependecies for fonts.
var fonts = {};
for (var i = 0, ii = dependency.length; i < ii; i++) {
var dep = dependency[i];
if (dep.indexOf('g_font_') === 0) {
fonts[dep] = true;
}
}
handler.send('RenderPage', {
pageIndex: data.pageIndex,
operatorList: operatorList,
depFonts: Object.keys(fonts)
});
}, function(e) { }, function(e) {
var minimumStackMessage = var minimumStackMessage =

View File

@ -36,14 +36,11 @@ describe('evaluator', function() {
new XrefMock(), new HandlerMock(), new XrefMock(), new HandlerMock(),
'prefix'); 'prefix');
var stream = new StringStream('qTT'); var stream = new StringStream('qTT');
var promise = evaluator.getOperatorList(stream, new ResourcesMock()); var result = evaluator.getOperatorList(stream, new ResourcesMock());
promise.then(function(data) { expect(!!result.fnArray && !!result.argsArray).toEqual(true);
var result = data.queue; expect(result.fnArray.length).toEqual(1);
expect(!!result.fnArray && !!result.argsArray).toEqual(true); expect(result.fnArray[0]).toEqual('save');
expect(result.fnArray.length).toEqual(1); expect(result.argsArray[0].length).toEqual(0);
expect(result.fnArray[0]).toEqual('save');
expect(result.argsArray[0].length).toEqual(0);
});
}); });
it('should handle one operations', function() { it('should handle one operations', function() {
@ -51,13 +48,10 @@ describe('evaluator', function() {
new XrefMock(), new HandlerMock(), new XrefMock(), new HandlerMock(),
'prefix'); 'prefix');
var stream = new StringStream('Q'); var stream = new StringStream('Q');
var promise = evaluator.getOperatorList(stream, new ResourcesMock()); var result = evaluator.getOperatorList(stream, new ResourcesMock());
promise.then(function(data) { expect(!!result.fnArray && !!result.argsArray).toEqual(true);
var result = data.queue; expect(result.fnArray.length).toEqual(1);
expect(!!result.fnArray && !!result.argsArray).toEqual(true); expect(result.fnArray[0]).toEqual('restore');
expect(result.fnArray.length).toEqual(1);
expect(result.fnArray[0]).toEqual('restore');
});
}); });
it('should handle two glued operations', function() { it('should handle two glued operations', function() {
@ -67,14 +61,11 @@ describe('evaluator', function() {
var resources = new ResourcesMock(); var resources = new ResourcesMock();
resources.Res1 = {}; resources.Res1 = {};
var stream = new StringStream('/Res1 DoQ'); var stream = new StringStream('/Res1 DoQ');
var promise = evaluator.getOperatorList(stream, resources); var result = evaluator.getOperatorList(stream, resources);
promise.then(function(data) { expect(!!result.fnArray && !!result.argsArray).toEqual(true);
var result = data.queue; expect(result.fnArray.length).toEqual(2);
expect(!!result.fnArray && !!result.argsArray).toEqual(true); expect(result.fnArray[0]).toEqual('paintXObject');
expect(result.fnArray.length).toEqual(2); expect(result.fnArray[1]).toEqual('restore');
expect(result.fnArray[0]).toEqual('paintXObject');
expect(result.fnArray[1]).toEqual('restore');
});
}); });
it('should handle tree glued operations', function() { it('should handle tree glued operations', function() {
@ -82,15 +73,12 @@ describe('evaluator', function() {
new XrefMock(), new HandlerMock(), new XrefMock(), new HandlerMock(),
'prefix'); 'prefix');
var stream = new StringStream('qqq'); var stream = new StringStream('qqq');
var promise = evaluator.getOperatorList(stream, new ResourcesMock()); var result = evaluator.getOperatorList(stream, new ResourcesMock());
promise.then(function(data) { expect(!!result.fnArray && !!result.argsArray).toEqual(true);
var result = data.queue; expect(result.fnArray.length).toEqual(3);
expect(!!result.fnArray && !!result.argsArray).toEqual(true); expect(result.fnArray[0]).toEqual('save');
expect(result.fnArray.length).toEqual(3); expect(result.fnArray[1]).toEqual('save');
expect(result.fnArray[0]).toEqual('save'); expect(result.fnArray[2]).toEqual('save');
expect(result.fnArray[1]).toEqual('save');
expect(result.fnArray[2]).toEqual('save');
});
}); });
it('should handle three glued operations #2', function() { it('should handle three glued operations #2', function() {
@ -100,15 +88,12 @@ describe('evaluator', function() {
var resources = new ResourcesMock(); var resources = new ResourcesMock();
resources.Res1 = {}; resources.Res1 = {};
var stream = new StringStream('B*Bf*'); var stream = new StringStream('B*Bf*');
var promise = evaluator.getOperatorList(stream, resources); var result = evaluator.getOperatorList(stream, resources);
promise.then(function(data) { expect(!!result.fnArray && !!result.argsArray).toEqual(true);
var result = data.queue; expect(result.fnArray.length).toEqual(3);
expect(!!result.fnArray && !!result.argsArray).toEqual(true); expect(result.fnArray[0]).toEqual('eoFillStroke');
expect(result.fnArray.length).toEqual(3); expect(result.fnArray[1]).toEqual('fillStroke');
expect(result.fnArray[0]).toEqual('eoFillStroke'); expect(result.fnArray[2]).toEqual('eoFill');
expect(result.fnArray[1]).toEqual('fillStroke');
expect(result.fnArray[2]).toEqual('eoFill');
});
}); });
it('should handle glued operations and operands', function() { it('should handle glued operations and operands', function() {
@ -116,17 +101,14 @@ describe('evaluator', function() {
new XrefMock(), new HandlerMock(), new XrefMock(), new HandlerMock(),
'prefix'); 'prefix');
var stream = new StringStream('q5 Ts'); var stream = new StringStream('q5 Ts');
var promise = evaluator.getOperatorList(stream, new ResourcesMock()); var result = evaluator.getOperatorList(stream, new ResourcesMock());
promise.then(function(data) { expect(!!result.fnArray && !!result.argsArray).toEqual(true);
var result = data.queue; expect(result.fnArray.length).toEqual(2);
expect(!!result.fnArray && !!result.argsArray).toEqual(true); expect(result.fnArray[0]).toEqual('save');
expect(result.fnArray.length).toEqual(2); expect(result.fnArray[1]).toEqual('setTextRise');
expect(result.fnArray[0]).toEqual('save'); expect(result.argsArray.length).toEqual(2);
expect(result.fnArray[1]).toEqual('setTextRise'); expect(result.argsArray[1].length).toEqual(1);
expect(result.argsArray.length).toEqual(2); expect(result.argsArray[1][0]).toEqual(5);
expect(result.argsArray[1].length).toEqual(1);
expect(result.argsArray[1][0]).toEqual(5);
});
}); });
it('should handle glued operations and literals', function() { it('should handle glued operations and literals', function() {
@ -134,21 +116,18 @@ describe('evaluator', function() {
new XrefMock(), new HandlerMock(), new XrefMock(), new HandlerMock(),
'prefix'); 'prefix');
var stream = new StringStream('trueifalserinullq'); var stream = new StringStream('trueifalserinullq');
var promise = evaluator.getOperatorList(stream, new ResourcesMock()); var result = evaluator.getOperatorList(stream, new ResourcesMock());
promise.then(function(data) { expect(!!result.fnArray && !!result.argsArray).toEqual(true);
var result = data.queue; expect(result.fnArray.length).toEqual(3);
expect(!!result.fnArray && !!result.argsArray).toEqual(true); expect(result.fnArray[0]).toEqual('setFlatness');
expect(result.fnArray.length).toEqual(3); expect(result.fnArray[1]).toEqual('setRenderingIntent');
expect(result.fnArray[0]).toEqual('setFlatness'); expect(result.fnArray[2]).toEqual('save');
expect(result.fnArray[1]).toEqual('setRenderingIntent'); expect(result.argsArray.length).toEqual(3);
expect(result.fnArray[2]).toEqual('save'); expect(result.argsArray[0].length).toEqual(1);
expect(result.argsArray.length).toEqual(3); expect(result.argsArray[0][0]).toEqual(true);
expect(result.argsArray[0].length).toEqual(1); expect(result.argsArray[1].length).toEqual(1);
expect(result.argsArray[0][0]).toEqual(true); expect(result.argsArray[1][0]).toEqual(false);
expect(result.argsArray[1].length).toEqual(1); expect(result.argsArray[2].length).toEqual(0);
expect(result.argsArray[1][0]).toEqual(false);
expect(result.argsArray[2].length).toEqual(0);
});
}); });
}); });
@ -159,39 +138,30 @@ describe('evaluator', function() {
'prefix'); 'prefix');
var stream = new StringStream('5 1 d0'); var stream = new StringStream('5 1 d0');
console.log('here!'); console.log('here!');
var promise = evaluator.getOperatorList(stream, new ResourcesMock()); var result = evaluator.getOperatorList(stream, new ResourcesMock());
promise.then(function(data) { expect(result.argsArray[0][0]).toEqual(5);
var result = data.queue; expect(result.argsArray[0][1]).toEqual(1);
expect(result.argsArray[0][0]).toEqual(5); expect(result.fnArray[0]).toEqual('setCharWidth');
expect(result.argsArray[0][1]).toEqual(1);
expect(result.fnArray[0]).toEqual('setCharWidth');
});
}); });
it('should execute if too many arguments', function() { it('should execute if too many arguments', function() {
var evaluator = new PartialEvaluator(new PdfManagerMock(), var evaluator = new PartialEvaluator(new PdfManagerMock(),
new XrefMock(), new HandlerMock(), new XrefMock(), new HandlerMock(),
'prefix'); 'prefix');
var stream = new StringStream('5 1 4 d0'); var stream = new StringStream('5 1 4 d0');
var promise = evaluator.getOperatorList(stream, new ResourcesMock()); var result = evaluator.getOperatorList(stream, new ResourcesMock());
promise.then(function(data) { expect(result.argsArray[0][0]).toEqual(5);
var result = data.queue; expect(result.argsArray[0][1]).toEqual(1);
expect(result.argsArray[0][0]).toEqual(5); expect(result.argsArray[0][2]).toEqual(4);
expect(result.argsArray[0][1]).toEqual(1); expect(result.fnArray[0]).toEqual('setCharWidth');
expect(result.argsArray[0][2]).toEqual(4);
expect(result.fnArray[0]).toEqual('setCharWidth');
});
}); });
it('should skip if too few arguments', function() { it('should skip if too few arguments', function() {
var evaluator = new PartialEvaluator(new PdfManagerMock(), var evaluator = new PartialEvaluator(new PdfManagerMock(),
new XrefMock(), new HandlerMock(), new XrefMock(), new HandlerMock(),
'prefix'); 'prefix');
var stream = new StringStream('5 d0'); var stream = new StringStream('5 d0');
var promise = evaluator.getOperatorList(stream, new ResourcesMock()); var result = evaluator.getOperatorList(stream, new ResourcesMock());
promise.then(function(data) { expect(result.argsArray).toEqual([]);
var result = data.queue; expect(result.fnArray).toEqual([]);
expect(result.argsArray).toEqual([]);
expect(result.fnArray).toEqual([]);
});
}); });
}); });
}); });

View File

@ -15,8 +15,8 @@
<script type="text/javascript" src="../../src/chunked_stream.js"></script> <script type="text/javascript" src="../../src/chunked_stream.js"></script>
<script type="text/javascript" src="../../src/pdf_manager.js"></script> <script type="text/javascript" src="../../src/pdf_manager.js"></script>
<script type="text/javascript" src="../../src/core.js"></script> <script type="text/javascript" src="../../src/core.js"></script>
<script type="text/javascript" src="../../src/api.js"></script>
<script type="text/javascript" src="../../src/util.js"></script> <script type="text/javascript" src="../../src/util.js"></script>
<script type="text/javascript" src="../../src/api.js"></script>
<script type="text/javascript" src="../../src/canvas.js"></script> <script type="text/javascript" src="../../src/canvas.js"></script>
<script type="text/javascript" src="../../src/obj.js"></script> <script type="text/javascript" src="../../src/obj.js"></script>
<script type="text/javascript" src="../../src/annotation.js"></script> <script type="text/javascript" src="../../src/annotation.js"></script>

View File

@ -220,26 +220,26 @@ var StepperManager = (function StepperManagerClosure() {
// The stepper for each page's IRQueue. // The stepper for each page's IRQueue.
var Stepper = (function StepperClosure() { var Stepper = (function StepperClosure() {
// Shorter way to create element and optionally set textContent.
function c(tag, textContent) {
var d = document.createElement(tag);
if (textContent)
d.textContent = textContent;
return d;
}
function Stepper(panel, pageIndex, initialBreakPoints) { function Stepper(panel, pageIndex, initialBreakPoints) {
this.panel = panel; this.panel = panel;
this.len = 0;
this.breakPoint = 0; this.breakPoint = 0;
this.nextBreakPoint = null; this.nextBreakPoint = null;
this.pageIndex = pageIndex; this.pageIndex = pageIndex;
this.breakPoints = initialBreakPoints; this.breakPoints = initialBreakPoints;
this.currentIdx = -1; this.currentIdx = -1;
this.operatorListIdx = 0;
} }
Stepper.prototype = { Stepper.prototype = {
init: function init(IRQueue) { init: function init() {
// Shorter way to create element and optionally set textContent.
function c(tag, textContent) {
var d = document.createElement(tag);
if (textContent)
d.textContent = textContent;
return d;
}
var panel = this.panel; var panel = this.panel;
this.len = IRQueue.fnArray.length;
var content = c('div', 'c=continue, s=step'); var content = c('div', 'c=continue, s=step');
var table = c('table'); var table = c('table');
content.appendChild(table); content.appendChild(table);
@ -250,15 +250,18 @@ var Stepper = (function StepperClosure() {
headerRow.appendChild(c('th', 'Idx')); headerRow.appendChild(c('th', 'Idx'));
headerRow.appendChild(c('th', 'fn')); headerRow.appendChild(c('th', 'fn'));
headerRow.appendChild(c('th', 'args')); headerRow.appendChild(c('th', 'args'));
panel.appendChild(content);
this.table = table;
},
updateOperatorList: function updateOperatorList(operatorList) {
var self = this; var self = this;
for (var i = 0; i < IRQueue.fnArray.length; i++) { for (var i = this.operatorListIdx; i < operatorList.fnArray.length; i++) {
var line = c('tr'); var line = c('tr');
line.className = 'line'; line.className = 'line';
line.dataset.idx = i; line.dataset.idx = i;
table.appendChild(line); this.table.appendChild(line);
var checked = this.breakPoints.indexOf(i) != -1; var checked = this.breakPoints.indexOf(i) != -1;
var args = IRQueue.argsArray[i] ? IRQueue.argsArray[i] : []; var args = operatorList.argsArray[i] ? operatorList.argsArray[i] : [];
var breakCell = c('td'); var breakCell = c('td');
var cbox = c('input'); var cbox = c('input');
@ -278,11 +281,9 @@ var Stepper = (function StepperClosure() {
breakCell.appendChild(cbox); breakCell.appendChild(cbox);
line.appendChild(breakCell); line.appendChild(breakCell);
line.appendChild(c('td', i.toString())); line.appendChild(c('td', i.toString()));
line.appendChild(c('td', IRQueue.fnArray[i])); line.appendChild(c('td', operatorList.fnArray[i]));
line.appendChild(c('td', args.join(', '))); line.appendChild(c('td', args.join(', ')));
} }
panel.appendChild(content);
var self = this;
}, },
getNextBreakPoint: function getNextBreakPoint() { getNextBreakPoint: function getNextBreakPoint() {
this.breakPoints.sort(function(a, b) { return a - b; }); this.breakPoints.sort(function(a, b) { return a - b; });

View File

@ -1553,8 +1553,12 @@ var PageView = function pageView(container, id, scale,
}; };
this.update = function pageViewUpdate(scale, rotation) { this.update = function pageViewUpdate(scale, rotation) {
this.renderingState = RenderingStates.INITIAL; if (this.renderTask) {
this.renderTask.cancel();
this.renderTask = null;
}
this.resume = null; this.resume = null;
this.renderingState = RenderingStates.INITIAL;
if (typeof rotation !== 'undefined') { if (typeof rotation !== 'undefined') {
this.rotation = rotation; this.rotation = rotation;
@ -1818,13 +1822,9 @@ var PageView = function pageView(container, id, scale,
// Rendering area // Rendering area
var self = this; var self = this;
var renderingWasReset = false;
function pageViewDrawCallback(error) { function pageViewDrawCallback(error) {
if (renderingWasReset) {
return;
}
self.renderingState = RenderingStates.FINISHED; self.renderingState = RenderingStates.FINISHED;
self.renderTask = null;
if (self.loadingIconDiv) { if (self.loadingIconDiv) {
div.removeChild(self.loadingIconDiv); div.removeChild(self.loadingIconDiv);
@ -1874,12 +1874,6 @@ var PageView = function pageView(container, id, scale,
viewport: this.viewport, viewport: this.viewport,
textLayer: textLayer, textLayer: textLayer,
continueCallback: function pdfViewcContinueCallback(cont) { continueCallback: function pdfViewcContinueCallback(cont) {
if (self.renderingState === RenderingStates.INITIAL) {
// The page update() was called, we just need to abort any rendering.
renderingWasReset = true;
return;
}
if (PDFView.highestPriorityPage !== 'page' + self.id) { if (PDFView.highestPriorityPage !== 'page' + self.id) {
self.renderingState = RenderingStates.PAUSED; self.renderingState = RenderingStates.PAUSED;
self.resume = function resumeCallback() { self.resume = function resumeCallback() {
@ -1891,7 +1885,9 @@ var PageView = function pageView(container, id, scale,
cont(); cont();
} }
}; };
this.pdfPage.render(renderContext).then( this.renderTask = this.pdfPage.render(renderContext);
this.renderTask.then(
function pdfPageRenderCallback() { function pdfPageRenderCallback() {
pageViewDrawCallback(null); pageViewDrawCallback(null);
}, },