From 68350378c0960ec6fef4c6251931d93fd39f4637 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sat, 15 May 2021 18:21:18 +0200 Subject: [PATCH] Handle errors gracefully, in `PartialEvaluator.buildFontPaths`, when glyph path building fails The building of glyph paths, in the `FontRendererFactory`, can fail in various ways for corrupt font data. However, we're currently not attempting to handle any such errors in the evaluator, which means that a single broken glyph *can* prevent an entire page from rendering. To address this we simply have to pass along, and check, the existing `ignoreErrors` option in `PartialEvaluator.buildFontPaths` similar to the rest of the `PartialEvaluator` code. --- src/core/evaluator.js | 59 +++++++++++++++++++++++++++------------ src/core/font_renderer.js | 28 +++++++++++++------ src/shared/util.js | 1 + 3 files changed, 61 insertions(+), 27 deletions(-) diff --git a/src/core/evaluator.js b/src/core/evaluator.js index 59791cb90..43d134f44 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -825,7 +825,7 @@ class PartialEvaluator { loadedName: "g_font_error", font: new ErrorFont(`Type3 font load error: ${reason}`), dict: translated.font, - extraProperties: this.options.fontExtraProperties, + evaluatorOptions: this.options, }); }); }) @@ -850,7 +850,12 @@ class PartialEvaluator { font.disableFontFace || this.options.disableFontFace ) { - PartialEvaluator.buildFontPaths(font, glyphs, this.handler); + PartialEvaluator.buildFontPaths( + font, + glyphs, + this.handler, + this.options + ); } } return glyphs; @@ -1002,7 +1007,7 @@ class PartialEvaluator { loadedName: "g_font_error", font: new ErrorFont(`Font "${fontName}" is not available.`), dict: font, - extraProperties: this.options.fontExtraProperties, + evaluatorOptions: this.options, }); }; @@ -1147,7 +1152,7 @@ class PartialEvaluator { loadedName: font.loadedName, font: translatedFont, dict: font, - extraProperties: this.options.fontExtraProperties, + evaluatorOptions: this.options, }) ); }) @@ -1178,7 +1183,7 @@ class PartialEvaluator { reason instanceof Error ? reason.message : reason ), dict: font, - extraProperties: this.options.fontExtraProperties, + evaluatorOptions: this.options, }) ); }); @@ -3841,16 +3846,30 @@ class PartialEvaluator { ); } - static buildFontPaths(font, glyphs, handler) { + static buildFontPaths(font, glyphs, handler, evaluatorOptions) { function buildPath(fontChar) { - if (font.renderer.hasBuiltPath(fontChar)) { - return; + const glyphName = `${font.loadedName}_path_${fontChar}`; + try { + if (font.renderer.hasBuiltPath(fontChar)) { + return; + } + handler.send("commonobj", [ + glyphName, + "FontPath", + font.renderer.getPathJs(fontChar), + ]); + } catch (reason) { + if (evaluatorOptions.ignoreErrors) { + // Error in the font data -- sending unsupported feature notification + // and allow glyph path building to continue. + handler.send("UnsupportedFeature", { + featureId: UNSUPPORTED_FEATURES.errorFontBuildPath, + }); + warn(`buildFontPaths - ignoring ${glyphName} glyph: "${reason}".`); + return; + } + throw reason; } - handler.send("commonobj", [ - `${font.loadedName}_path_${fontChar}`, - "FontPath", - font.renderer.getPathJs(fontChar), - ]); } for (const glyph of glyphs) { @@ -3877,11 +3896,11 @@ class PartialEvaluator { } class TranslatedFont { - constructor({ loadedName, font, dict, extraProperties = false }) { + constructor({ loadedName, font, dict, evaluatorOptions }) { this.loadedName = loadedName; this.font = font; this.dict = dict; - this._extraProperties = extraProperties; + this._evaluatorOptions = evaluatorOptions || DefaultPartialEvaluatorOptions; this.type3Loaded = null; this.type3Dependencies = font.isType3Font ? new Set() : null; this.sent = false; @@ -3896,7 +3915,7 @@ class TranslatedFont { handler.send("commonobj", [ this.loadedName, "Font", - this.font.exportData(this._extraProperties), + this.font.exportData(this._evaluatorOptions.fontExtraProperties), ]); } @@ -3912,8 +3931,12 @@ class TranslatedFont { // message was received on the worker-thread. // To ensure that all 'FontPath's are available on the main-thread, when // font loading failed, attempt to resend *all* previously parsed glyphs. - const glyphs = this.font.glyphCacheValues; - PartialEvaluator.buildFontPaths(this.font, glyphs, handler); + PartialEvaluator.buildFontPaths( + this.font, + /* glyphs = */ this.font.glyphCacheValues, + handler, + this._evaluatorOptions + ); } loadType3Data(evaluator, resources, task) { diff --git a/src/core/font_renderer.js b/src/core/font_renderer.js index 02c0d5023..9849c8f2a 100644 --- a/src/core/font_renderer.js +++ b/src/core/font_renderer.js @@ -734,14 +734,24 @@ class CompiledFont { } getPathJs(unicode) { - const cmap = lookupCmap(this.cmap, unicode); - let fn = this.compiledGlyphs[cmap.glyphId]; + const { charCode, glyphId } = lookupCmap(this.cmap, unicode); + let fn = this.compiledGlyphs[glyphId]; if (!fn) { - fn = this.compileGlyph(this.glyphs[cmap.glyphId], cmap.glyphId); - this.compiledGlyphs[cmap.glyphId] = fn; + try { + fn = this.compileGlyph(this.glyphs[glyphId], glyphId); + this.compiledGlyphs[glyphId] = fn; + } catch (ex) { + // Avoid attempting to re-compile a corrupt glyph. + this.compiledGlyphs[glyphId] = NOOP; + + if (this.compiledCharCodeToGlyphId[charCode] === undefined) { + this.compiledCharCodeToGlyphId[charCode] = glyphId; + } + throw ex; + } } - if (this.compiledCharCodeToGlyphId[cmap.charCode] === undefined) { - this.compiledCharCodeToGlyphId[cmap.charCode] = cmap.glyphId; + if (this.compiledCharCodeToGlyphId[charCode] === undefined) { + this.compiledCharCodeToGlyphId[charCode] = glyphId; } return fn; } @@ -781,10 +791,10 @@ class CompiledFont { } hasBuiltPath(unicode) { - const cmap = lookupCmap(this.cmap, unicode); + const { charCode, glyphId } = lookupCmap(this.cmap, unicode); return ( - this.compiledGlyphs[cmap.glyphId] !== undefined && - this.compiledCharCodeToGlyphId[cmap.charCode] !== undefined + this.compiledGlyphs[glyphId] !== undefined && + this.compiledCharCodeToGlyphId[charCode] !== undefined ); } } diff --git a/src/shared/util.js b/src/shared/util.js index 21d507c0c..319ed09a0 100644 --- a/src/shared/util.js +++ b/src/shared/util.js @@ -331,6 +331,7 @@ const UNSUPPORTED_FEATURES = { errorOperatorList: "errorOperatorList", errorFontToUnicode: "errorFontToUnicode", errorFontLoadNative: "errorFontLoadNative", + errorFontBuildPath: "errorFontBuildPath", errorFontGetPath: "errorFontGetPath", errorMarkedContent: "errorMarkedContent", };