From 24f14d44cb7ebda981bcbb3e1cb83feab801bd24 Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Mon, 12 Jun 2017 16:04:35 -0500 Subject: [PATCH] Preventing from using the same canvas for multiple render() --- src/display/api.js | 20 +++++++++++++++++++- test/unit/api_spec.js | 26 ++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/display/api.js b/src/display/api.js index fcde21d49..729b62764 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -901,7 +901,7 @@ var PDFPageProxy = (function PDFPageProxyClosure() { stats.time('Rendering'); internalRenderTask.initializeGraphics(transparency); internalRenderTask.operatorListChanged(); - }, complete); + }).catch(complete); return renderTask; }, @@ -2103,6 +2103,7 @@ var RenderTask = (function RenderTaskClosure() { * @ignore */ var InternalRenderTask = (function InternalRenderTaskClosure() { + let canvasInRendering = new WeakMap(); function InternalRenderTask(callback, params, objs, commonObjs, operatorList, pageNumber, canvasFactory) { @@ -2125,6 +2126,7 @@ var InternalRenderTask = (function InternalRenderTaskClosure() { this._continueBound = this._continue.bind(this); this._scheduleNextBound = this._scheduleNext.bind(this); this._nextBound = this._next.bind(this); + this._canvas = params.canvasContext.canvas; } InternalRenderTask.prototype = { @@ -2132,6 +2134,16 @@ var InternalRenderTask = (function InternalRenderTaskClosure() { initializeGraphics: function InternalRenderTask_initializeGraphics(transparency) { + if (this._canvas) { + if (canvasInRendering.has(this._canvas)) { + throw new Error( + 'Cannot use the same canvas during multiple render() operations. ' + + 'Use different canvas or ensure previous operations were ' + + 'cancelled or completed.'); + } + canvasInRendering.set(this._canvas, this); + } + if (this.cancelled) { return; } @@ -2163,6 +2175,9 @@ var InternalRenderTask = (function InternalRenderTaskClosure() { cancel: function InternalRenderTask_cancel() { this.running = false; this.cancelled = true; + if (this._canvas) { + canvasInRendering.delete(this._canvas); + } if ((typeof PDFJSDev !== 'undefined' && PDFJSDev.test('PDFJS_NEXT')) || getDefaultSetting('pdfjsNext')) { @@ -2223,6 +2238,9 @@ var InternalRenderTask = (function InternalRenderTaskClosure() { this.running = false; if (this.operatorList.lastChunk) { this.gfx.endDrawing(); + if (this._canvas) { + canvasInRendering.delete(this._canvas); + } this.callback(); } } diff --git a/test/unit/api_spec.js b/test/unit/api_spec.js index f22988e76..d6d3d690c 100644 --- a/test/unit/api_spec.js +++ b/test/unit/api_spec.js @@ -1041,6 +1041,32 @@ describe('api', function() { done(); }); }); + it('multiple render() on the same canvas', function(done) { + if (isNodeJS()) { + pending('TODO: Support Canvas testing in Node.js.'); + } + var viewport = page.getViewport(1); + var canvasAndCtx = CanvasFactory.create(viewport.width, viewport.height); + + var renderTask1 = page.render({ + canvasContext: canvasAndCtx.context, + viewport, + }); + var renderTask2 = page.render({ + canvasContext: canvasAndCtx.context, + viewport, + }); + + Promise.all([ + renderTask1.promise, + renderTask2.promise.then(() => { + done.fail('shall fail rendering'); + }, (reason) => { + /* it fails because we already using this canvas */ + expect(/multiple render\(\)/.test(reason.message)).toEqual(true); + }) + ]).then(done); + }); }); describe('Multiple PDFJS instances', function() { if (isNodeJS()) {