diff --git a/src/display/api.js b/src/display/api.js index 287c1e7cbe452a..48ac3cfa401e86 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -61,9 +61,9 @@ var warn = sharedUtil.warn; var FontFaceObject = displayFontLoader.FontFaceObject; var FontLoader = displayFontLoader.FontLoader; var CanvasGraphics = displayCanvas.CanvasGraphics; -var createScratchCanvas = displayCanvas.createScratchCanvas; var Metadata = displayMetadata.Metadata; var getDefaultSetting = displayDOMUtils.getDefaultSetting; +var DOMCanvasFactory = displayDOMUtils.DOMCanvasFactory; var DEFAULT_RANGE_CHUNK_SIZE = 65536; // 2^16 = 65536 @@ -697,6 +697,9 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { * called each time the rendering is paused. To continue * rendering call the function that is the first argument * to the callback. + * @property {Object} canvasFactory - (optional) The factory that will be used + * when creating canvases. The default value is + * {DOMCanvasFactory}. */ /** @@ -805,6 +808,7 @@ var PDFPageProxy = (function PDFPageProxyClosure() { var renderingIntent = (params.intent === 'print' ? 'print' : 'display'); var renderInteractiveForms = (params.renderInteractiveForms === true ? true : /* Default */ false); + var canvasFactory = params.canvasFactory || new DOMCanvasFactory(); if (!this.intentStates[renderingIntent]) { this.intentStates[renderingIntent] = Object.create(null); @@ -834,7 +838,8 @@ var PDFPageProxy = (function PDFPageProxyClosure() { this.objs, this.commonObjs, intentState.operatorList, - this.pageNumber); + this.pageNumber, + canvasFactory); internalRenderTask.useRequestAnimationFrame = renderingIntent !== 'print'; if (!intentState.renderTasks) { intentState.renderTasks = []; @@ -1727,6 +1732,12 @@ var WorkerTransport = (function WorkerTransportClosure() { return Promise.reject(new Error('Worker was destroyed')); } + if (typeof document === 'undefined') { + // Make sure that this code is not executing in node.js, as + // it's using DOM image, and there is no library to support that. + return Promise.reject(new Error('"document" is not defined.')); + } + var imageUrl = data[0]; var components = data[1]; if (components !== 3 && components !== 1) { @@ -1742,7 +1753,9 @@ var WorkerTransport = (function WorkerTransportClosure() { var size = width * height; var rgbaLength = size * 4; var buf = new Uint8Array(size * components); - var tmpCanvas = createScratchCanvas(width, height); + var tmpCanvas = document.createElement('canvas'); + tmpCanvas.width = width; + tmpCanvas.height = height; var tmpCtx = tmpCanvas.getContext('2d'); tmpCtx.drawImage(img, 0, 0); var data = tmpCtx.getImageData(0, 0, width, height).data; @@ -2030,7 +2043,7 @@ var RenderTask = (function RenderTaskClosure() { var InternalRenderTask = (function InternalRenderTaskClosure() { function InternalRenderTask(callback, params, objs, commonObjs, operatorList, - pageNumber) { + pageNumber, canvasFactory) { this.callback = callback; this.params = params; this.objs = objs; @@ -2038,6 +2051,7 @@ var InternalRenderTask = (function InternalRenderTaskClosure() { this.operatorListIdx = null; this.operatorList = operatorList; this.pageNumber = pageNumber; + this.canvasFactory = canvasFactory; this.running = false; this.graphicsReadyCallback = null; this.graphicsReady = false; @@ -2068,7 +2082,8 @@ var InternalRenderTask = (function InternalRenderTaskClosure() { var params = this.params; this.gfx = new CanvasGraphics(params.canvasContext, this.commonObjs, - this.objs, params.imageLayer); + this.objs, this.canvasFactory, + params.imageLayer); this.gfx.beginDrawing(params.transform, params.viewport, transparency); this.operatorListIdx = 0; diff --git a/src/display/canvas.js b/src/display/canvas.js index 1d3b1c8b6b3376..1537d4112a0e6e 100644 --- a/src/display/canvas.js +++ b/src/display/canvas.js @@ -80,13 +80,6 @@ var IsLittleEndianCached = { } }; -function createScratchCanvas(width, height) { - var canvas = document.createElement('canvas'); - canvas.width = width; - canvas.height = height; - return canvas; -} - function addContextCurrentTransform(ctx) { // If the context doesn't expose a `mozCurrentTransform`, add a JS based one. if (!ctx.mozCurrentTransform) { @@ -204,7 +197,8 @@ function addContextCurrentTransform(ctx) { } var CachedCanvases = (function CachedCanvasesClosure() { - function CachedCanvases() { + function CachedCanvases(canvasFactory) { + this.canvasFactory = canvasFactory; this.cache = Object.create(null); } CachedCanvases.prototype = { @@ -213,12 +207,11 @@ var CachedCanvases = (function CachedCanvasesClosure() { var canvasEntry; if (this.cache[id] !== undefined) { canvasEntry = this.cache[id]; - canvasEntry.canvas.width = width; - canvasEntry.canvas.height = height; + this.canvasFactory.reset(canvasEntry, width, height); // reset canvas transform for emulated mozCurrentTransform, if needed canvasEntry.context.setTransform(1, 0, 0, 1, 0, 0); } else { - var canvas = createScratchCanvas(width, height); + var canvas = this.canvasFactory.create(width, height); var ctx = canvas.getContext('2d'); if (trackTransform) { addContextCurrentTransform(ctx); @@ -230,10 +223,7 @@ var CachedCanvases = (function CachedCanvasesClosure() { clear: function () { for (var id in this.cache) { var canvasEntry = this.cache[id]; - // Zeroing the width and height causes Firefox to release graphics - // resources immediately, which can greatly reduce memory consumption. - canvasEntry.canvas.width = 0; - canvasEntry.canvas.height = 0; + this.canvasFactory.destroy(canvasEntry.canvas); delete this.cache[id]; } } @@ -456,7 +446,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { // Defines the number of steps before checking the execution time var EXECUTION_STEPS = 10; - function CanvasGraphics(canvasCtx, commonObjs, objs, imageLayer) { + function CanvasGraphics(canvasCtx, commonObjs, objs, canvasFactory, + imageLayer) { this.ctx = canvasCtx; this.current = new CanvasExtraState(); this.stateStack = []; @@ -466,6 +457,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { this.xobjs = null; this.commonObjs = commonObjs; this.objs = objs; + this.canvasFactory = canvasFactory; this.imageLayer = imageLayer; this.groupStack = []; this.processingType3 = null; @@ -477,7 +469,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { this.smaskStack = []; this.smaskCounter = 0; this.tempSMask = null; - this.cachedCanvases = new CachedCanvases(); + this.cachedCanvases = new CachedCanvases(this.canvasFactory); if (canvasCtx) { // NOTE: if mozCurrentTransform is polyfilled, then the current state of // the transformation must already be set in canvasCtx._transformMatrix. @@ -1454,7 +1446,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { get isFontSubpixelAAEnabled() { // Checks if anti-aliasing is enabled when scaled text is painted. // On Windows GDI scaled fonts looks bad. - var ctx = document.createElement('canvas').getContext('2d'); + var ctx = this.canvasFactory.create(10, 10).getContext('2d'); ctx.scale(1.5, 1); ctx.fillText('I', 0, 10); var data = ctx.getImageData(0, 0, 10, 10).data; @@ -1700,7 +1692,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var self = this; var canvasGraphicsFactory = { createCanvasGraphics: function (ctx) { - return new CanvasGraphics(ctx, self.commonObjs, self.objs); + return new CanvasGraphics(ctx, self.commonObjs, self.objs, + self.canvasFactory); } }; pattern = new TilingPattern(IR, color, this.ctx, canvasGraphicsFactory, @@ -2320,5 +2313,4 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { })(); exports.CanvasGraphics = CanvasGraphics; -exports.createScratchCanvas = createScratchCanvas; })); diff --git a/src/display/dom_utils.js b/src/display/dom_utils.js index 5507668140cacf..1d4259fc8ae308 100644 --- a/src/display/dom_utils.js +++ b/src/display/dom_utils.js @@ -26,6 +26,7 @@ } }(this, function (exports, sharedUtil) { +var assert = sharedUtil.assert; var removeNullCharacters = sharedUtil.removeNullCharacters; var warn = sharedUtil.warn; var deprecated = sharedUtil.deprecated; @@ -33,6 +34,32 @@ var createValidAbsoluteUrl = sharedUtil.createValidAbsoluteUrl; var DEFAULT_LINK_REL = 'noopener noreferrer nofollow'; +function DOMCanvasFactory() {} +DOMCanvasFactory.prototype = { + create: function DOMCanvasFactory_create(width, height) { + assert(width > 0 && height > 0, 'invalid canvas size'); + var canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + return canvas; + }, + + reset: function DOMCanvasFactory_reset(canvas, width, height) { + assert(canvas, 'canvas is not specified'); + assert(width > 0 && height > 0, 'invalid canvas size'); + canvas.width = width; + canvas.height = height; + }, + + destroy: function DOMCanvasFactory_destroy(canvas) { + assert(canvas, 'canvas is not specified'); + // Zeroing the width and height cause Firefox to release graphics + // resources immediately, which can greatly reduce memory consumption. + canvas.width = 0; + canvas.height = 0; + } +}; + /** * Optimised CSS custom property getter/setter. * @class @@ -248,4 +275,5 @@ exports.LinkTarget = LinkTarget; exports.hasCanvasTypedArrays = hasCanvasTypedArrays; exports.getDefaultSetting = getDefaultSetting; exports.DEFAULT_LINK_REL = DEFAULT_LINK_REL; +exports.DOMCanvasFactory = DOMCanvasFactory; })); diff --git a/src/display/font_loader.js b/src/display/font_loader.js index 316a8b6d24354c..9a70d275c87aaf 100644 --- a/src/display/font_loader.js +++ b/src/display/font_loader.js @@ -216,6 +216,7 @@ if (typeof PDFJSDev === 'undefined' || !PDFJSDev.test('MOZCENTRAL')) { var i, ii; + // The temporary canvas is used to determine if fonts are loaded. var canvas = document.createElement('canvas'); canvas.width = 1; canvas.height = 1; diff --git a/src/display/text_layer.js b/src/display/text_layer.js index aaf597c74fa838..71eb65031c42f9 100644 --- a/src/display/text_layer.js +++ b/src/display/text_layer.js @@ -186,6 +186,7 @@ var renderTextLayer = (function renderTextLayerClosure() { return; } + // The temporary canvas is used to measure text length in the DOM. var canvas = document.createElement('canvas'); if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('FIREFOX || MOZCENTRAL || GENERIC')) { diff --git a/src/display/webgl.js b/src/display/webgl.js index dc2b957614dd2f..92b8c7ff3193cc 100644 --- a/src/display/webgl.js +++ b/src/display/webgl.js @@ -83,6 +83,8 @@ var WebGLUtils = (function WebGLUtilsClosure() { if (currentGL) { return; } + + // The temporary canvas is used in the WebGL context. currentCanvas = document.createElement('canvas'); currentGL = currentCanvas.getContext('webgl', { premultipliedalpha: false });