diff --git a/src/display/api.js b/src/display/api.js index 6e71f8cc7..029c3ac59 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -1884,6 +1884,8 @@ var WorkerTransport = (function WorkerTransportClosure() { var font = new FontFaceObject(exportedData, { isEvalSupported: params.isEvalSupported, disableFontFace: params.disableFontFace, + ignoreErrors: params.ignoreErrors, + onUnsupportedFeature: this._onUnsupportedFeature.bind(this), fontRegistry, }); var fontReady = (fontObjs) => { @@ -1986,15 +1988,7 @@ var WorkerTransport = (function WorkerTransportClosure() { } }, this); - messageHandler.on('UnsupportedFeature', function(data) { - if (this.destroyed) { - return; // Ignore any pending requests if the worker was terminated. - } - let loadingTask = this.loadingTask; - if (loadingTask.onUnsupportedFeature) { - loadingTask.onUnsupportedFeature(data.featureId); - } - }, this); + messageHandler.on('UnsupportedFeature', this._onUnsupportedFeature, this); messageHandler.on('JpegDecode', function(data) { if (this.destroyed) { @@ -2060,6 +2054,16 @@ var WorkerTransport = (function WorkerTransportClosure() { }, this); }, + _onUnsupportedFeature({ featureId, }) { + if (this.destroyed) { + return; // Ignore any pending requests if the worker was terminated. + } + let loadingTask = this.loadingTask; + if (loadingTask.onUnsupportedFeature) { + loadingTask.onUnsupportedFeature(featureId); + } + }, + getData: function WorkerTransport_getData() { return this.messageHandler.sendWithPromise('GetData', null); }, @@ -2455,30 +2459,34 @@ var InternalRenderTask = (function InternalRenderTaskClosure() { _scheduleNext: function InternalRenderTask__scheduleNext() { if (this.useRequestAnimationFrame && typeof window !== 'undefined') { - window.requestAnimationFrame(this._nextBound); + window.requestAnimationFrame(() => { + this._nextBound().catch(this.callback); + }); } else { Promise.resolve().then(this._nextBound).catch(this.callback); } }, _next: function InternalRenderTask__next() { - if (this.cancelled) { - return; - } - this.operatorListIdx = this.gfx.executeOperatorList(this.operatorList, - this.operatorListIdx, - this._continueBound, - this.stepper); - if (this.operatorListIdx === this.operatorList.argsArray.length) { - this.running = false; - if (this.operatorList.lastChunk) { - this.gfx.endDrawing(); - if (this._canvas) { - canvasInRendering.delete(this._canvas); - } - this.callback(); + return new Promise(() => { + if (this.cancelled) { + return; } - } + this.operatorListIdx = this.gfx.executeOperatorList(this.operatorList, + this.operatorListIdx, + this._continueBound, + this.stepper); + if (this.operatorListIdx === this.operatorList.argsArray.length) { + this.running = false; + if (this.operatorList.lastChunk) { + this.gfx.endDrawing(); + if (this._canvas) { + canvasInRendering.delete(this._canvas); + } + this.callback(); + } + } + }); }, }; diff --git a/src/display/font_loader.js b/src/display/font_loader.js index 904712bf6..cb7b26f49 100644 --- a/src/display/font_loader.js +++ b/src/display/font_loader.js @@ -14,7 +14,8 @@ */ import { - assert, bytesToString, isEvalSupported, shadow, string32, warn + assert, bytesToString, isEvalSupported, shadow, string32, + UNSUPPORTED_FEATURES, warn } from '../shared/util'; function FontLoader(docId) { @@ -338,6 +339,8 @@ var IsEvalSupportedCached = { var FontFaceObject = (function FontFaceObjectClosure() { function FontFaceObject(translatedData, { isEvalSupported = true, disableFontFace = false, + ignoreErrors = false, + onUnsupportedFeature = null, fontRegistry = null, }) { this.compiledGlyphs = Object.create(null); // importing translated data @@ -346,6 +349,8 @@ var FontFaceObject = (function FontFaceObjectClosure() { } this.isEvalSupported = isEvalSupported !== false; this.disableFontFace = disableFontFace === true; + this.ignoreErrors = ignoreErrors === true; + this._onUnsupportedFeature = onUnsupportedFeature; this.fontRegistry = fontRegistry; } FontFaceObject.prototype = { @@ -385,45 +390,56 @@ var FontFaceObject = (function FontFaceObjectClosure() { return rule; }, - getPathGenerator: - function FontFaceObject_getPathGenerator(objs, character) { - if (!(character in this.compiledGlyphs)) { - var cmds = objs.get(this.loadedName + '_path_' + character); - var current, i, len; - - // If we can, compile cmds into JS for MAXIMUM SPEED - if (this.isEvalSupported && IsEvalSupportedCached.value) { - var args, js = ''; - for (i = 0, len = cmds.length; i < len; i++) { - current = cmds[i]; - - if (current.args !== undefined) { - args = current.args.join(','); - } else { - args = ''; - } - - js += 'c.' + current.cmd + '(' + args + ');\n'; - } - // eslint-disable-next-line no-new-func - this.compiledGlyphs[character] = new Function('c', 'size', js); - } else { - // But fall back on using Function.prototype.apply() if we're - // blocked from using eval() for whatever reason (like CSP policies) - this.compiledGlyphs[character] = function(c, size) { - for (i = 0, len = cmds.length; i < len; i++) { - current = cmds[i]; - - if (current.cmd === 'scale') { - current.args = [size, -size]; - } - - c[current.cmd].apply(c, current.args); - } - }; - } + getPathGenerator(objs, character) { + if (this.compiledGlyphs[character] !== undefined) { + return this.compiledGlyphs[character]; } - return this.compiledGlyphs[character]; + + let cmds, current; + try { + cmds = objs.get(this.loadedName + '_path_' + character); + } catch (ex) { + if (!this.ignoreErrors) { + throw ex; + } + if (this._onUnsupportedFeature) { + this._onUnsupportedFeature({ featureId: UNSUPPORTED_FEATURES.font, }); + } + warn(`getPathGenerator - ignoring character: "${ex}".`); + + return this.compiledGlyphs[character] = function(c, size) { + // No-op function, to allow rendering to continue. + }; + } + + // If we can, compile cmds into JS for MAXIMUM SPEED... + if (this.isEvalSupported && IsEvalSupportedCached.value) { + let args, js = ''; + for (let i = 0, ii = cmds.length; i < ii; i++) { + current = cmds[i]; + + if (current.args !== undefined) { + args = current.args.join(','); + } else { + args = ''; + } + js += 'c.' + current.cmd + '(' + args + ');\n'; + } + // eslint-disable-next-line no-new-func + return this.compiledGlyphs[character] = new Function('c', 'size', js); + } + // ... but fall back on using Function.prototype.apply() if we're + // blocked from using eval() for whatever reason (like CSP policies). + return this.compiledGlyphs[character] = function(c, size) { + for (let i = 0, ii = cmds.length; i < ii; i++) { + current = cmds[i]; + + if (current.cmd === 'scale') { + current.args = [size, -size]; + } + c[current.cmd].apply(c, current.args); + } + }; }, }; diff --git a/test/pdfs/issue4244.pdf.link b/test/pdfs/issue4244.pdf.link new file mode 100644 index 000000000..05767bbfe --- /dev/null +++ b/test/pdfs/issue4244.pdf.link @@ -0,0 +1 @@ +https://web.archive.org/web/20180613082417/https://tcpdf.org/files/examples/example_026.pdf diff --git a/test/test_manifest.json b/test/test_manifest.json index 6b8142274..99213a1a7 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -2267,6 +2267,13 @@ "rounds": 1, "type": "eq" }, + { "id": "issue4244", + "file": "pdfs/issue4244.pdf", + "md5": "26845274a32a537182ced1fd693a38b2", + "rounds": 1, + "link": true, + "type": "eq" + }, { "id": "preistabelle", "file": "pdfs/preistabelle.pdf", "md5": "d2f0b2086160d4f3d325c79a5dc1fb4d",