From d4b70ec30664a5ebc10dbdd401197e1d644617b7 Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Sat, 13 May 2023 16:15:15 +0200 Subject: [PATCH] For missing font, use a local font if it exists even if there's no standard substitution If the font foo is missing we just try lo load local(foo) and maybe we'll be lucky. --- src/core/evaluator.js | 39 ++++++++++++++++++---------------- src/core/font_substitutions.js | 28 +++++++++++++++++------- src/core/fonts.js | 6 +++++- src/core/standard_fonts.js | 3 +++ src/display/font_loader.js | 14 +++++++++--- 5 files changed, 60 insertions(+), 30 deletions(-) diff --git a/src/core/evaluator.js b/src/core/evaluator.js index 4baf3ffe2759a..031e051a4ca6a 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -3921,6 +3921,9 @@ class PartialEvaluator { } if (isMonospace) { properties.flags |= FontFlags.FixedPitch; + } else { + // Clear the flag. + properties.flags &= ~FontFlags.FixedPitch; } properties.defaultWidth = defaultWidth; @@ -4197,15 +4200,15 @@ class PartialEvaluator { if (standardFontName) { file = await this.fetchStandardFontData(standardFontName); properties.isInternalFont = !!file; - if (!properties.isInternalFont && this.options.useSystemFonts) { - properties.systemFontInfo = getFontSubstitution( - this.systemFontCache, - this.idFactory, - this.options.standardFontDataUrl, - baseFontName, - standardFontName - ); - } + } + if (!properties.isInternalFont && this.options.useSystemFonts) { + properties.systemFontInfo = getFontSubstitution( + this.systemFontCache, + this.idFactory, + this.options.standardFontDataUrl, + baseFontName, + standardFontName + ); } return this.extractDataStructures(dict, dict, properties).then( newProperties => { @@ -4310,15 +4313,15 @@ class PartialEvaluator { if (standardFontName) { fontFile = await this.fetchStandardFontData(standardFontName); isInternalFont = !!fontFile; - if (!isInternalFont && this.options.useSystemFonts) { - systemFontInfo = getFontSubstitution( - this.systemFontCache, - this.idFactory, - this.options.standardFontDataUrl, - fontName.name, - standardFontName - ); - } + } + if (!isInternalFont && this.options.useSystemFonts) { + systemFontInfo = getFontSubstitution( + this.systemFontCache, + this.idFactory, + this.options.standardFontDataUrl, + fontName.name, + standardFontName + ); } } diff --git a/src/core/font_substitutions.js b/src/core/font_substitutions.js index bc576aa6d1fd3..0908e8775abcd 100644 --- a/src/core/font_substitutions.js +++ b/src/core/font_substitutions.js @@ -224,7 +224,6 @@ const substitutionMap = new Map([ [ "ArialBlack-Italic", { - prepend: ["Arial Black Italic"], local: { alias: "ArialBlack", append: "Italic", @@ -337,6 +336,8 @@ const fontAliases = new Map([["Arial-Black", "ArialBlack"]]); /** * Create the src path to use to load a font (see FontFace). * @param {Array} prepend A list of font names to search first. + * @param {String} appendToPrepended A String to append to the list of fonts in + * prepend. * @param {Array|Object} local A list of font names to search. If an * Object is passed, then local.alias is the name of an other substition font * and local.append is a String to append to the list of fonts in the alias. @@ -344,7 +345,7 @@ const fontAliases = new Map([["Arial-Black", "ArialBlack"]]); * list of fonts will be "FooSubst1 Bold", "FooSubst2 Bold", etc. * @returns an String with the local fonts. */ -function makeLocal(prepend, local) { +function makeLocal(prepend, appendToPrepended, local) { let append = ""; if (!Array.isArray(local)) { // We are getting our list of fonts in the alias and we'll append Bold, @@ -352,9 +353,14 @@ function makeLocal(prepend, local) { append = ` ${local.append}`; local = substitutionMap.get(local.alias).local; } + if (appendToPrepended) { + appendToPrepended = ` ${appendToPrepended}`; + } + let prependedPaths = ""; if (prepend) { - prependedPaths = prepend.map(name => `local(${name})`).join(",") + ","; + prependedPaths = + prepend.map(name => `local(${name}${appendToPrepended})`).join(",") + ","; } return ( prependedPaths + local.map(name => `local(${name}${append})`).join(",") @@ -378,8 +384,8 @@ function makeLocal(prepend, local) { * @param {Object} idFactory The ids factory. * @param {String} localFontPath Path to the fonts directory. * @param {String} baseFontName The font name to be substituted. - * @param {String} standardFontName The standard font name to use if the base - * font is not available. + * @param {String|undefined} standardFontName The standard font name to use + * if the base font is not available. * @returns an Object with the CSS, the loaded name, the src and the style. */ function getFontSubstitution( @@ -437,7 +443,8 @@ function getFontSubstitution( (italic && ITALIC) || NORMAL; substitutionInfo = { - css: `${loadedName},sans-serif`, + css: loadedName, + guessFallback: true, loadedName, src: `local(${baseFontName})`, style, @@ -457,16 +464,20 @@ function getFontSubstitution( // Prepend the fonts to test before the fallback font. let prepend = substitution.prepend; + let appendToPrepended = ""; if (fallback) { // We've a fallback font: this one is a standard font we want to use in case // nothing has been found from the prepend list. - prepend ||= substitutionMap.get(substitution.local.alias).prepend; + if (substitution.local) { + prepend ||= substitutionMap.get(substitution.local.alias).prepend; + appendToPrepended = substitution.local.append; + } substitution = substitutionMap.get(fallback); } const { local, path, ultimate } = substitution; - let src = makeLocal(prepend, local); + let src = makeLocal(prepend, appendToPrepended, local); if (path && localFontPath !== null) { // PDF.js embeds some fonts we can use. src += `,url(${localFontPath}${path})`; @@ -480,6 +491,7 @@ function getFontSubstitution( substitutionInfo = { css: `${loadedName},${ultimate}`, + guessFallback: false, loadedName, src, style, diff --git a/src/core/fonts.js b/src/core/fonts.js index 62502cfa1e093..5fbacdba3e5ee 100644 --- a/src/core/fonts.js +++ b/src/core/fonts.js @@ -986,6 +986,7 @@ class Font { let { type, subtype } = properties; this.type = type; this.subtype = subtype; + this.systemFontInfo = properties.systemFontInfo; const matches = name.match(/^InvalidPDFjsFont_(.*)_\d+$/); this.isInvalidPDFjsFont = !!matches; @@ -999,7 +1000,10 @@ class Font { this.fallbackName = "sans-serif"; } - this.systemFontInfo = properties.systemFontInfo; + if (this.systemFontInfo?.guessFallback) { + this.systemFontInfo.css += `,${this.fallbackName}`; + } + this.differences = properties.differences; this.widths = properties.widths; this.defaultWidth = properties.defaultWidth; diff --git a/src/core/standard_fonts.js b/src/core/standard_fonts.js index 9f025adf31396..e3e835a674a0e 100644 --- a/src/core/standard_fonts.js +++ b/src/core/standard_fonts.js @@ -58,6 +58,9 @@ const getStdFontMap = getLookupTableFactory(function (t) { t["Arial-BoldItalicMT"] = "Helvetica-BoldOblique"; t["Arial-BoldMT"] = "Helvetica-Bold"; t["Arial-ItalicMT"] = "Helvetica-Oblique"; + t["Arial-BoldItalicMT-BoldItalic"] = "Helvetica-BoldOblique"; + t["Arial-BoldMT-Bold"] = "Helvetica-Bold"; + t["Arial-ItalicMT-Italic"] = "Helvetica-Oblique"; t.ArialUnicodeMS = "Helvetica"; t["ArialUnicodeMS-Bold"] = "Helvetica-Bold"; t["ArialUnicodeMS-BoldItalic"] = "Helvetica-BoldOblique"; diff --git a/src/display/font_loader.js b/src/display/font_loader.js index dce51fafb3b6b..08700e2ae53cb 100644 --- a/src/display/font_loader.js +++ b/src/display/font_loader.js @@ -97,9 +97,17 @@ class FontLoader { await fontFace.load(); this.#systemFonts.add(loadedName); } catch { - warn( - `Cannot load system font: ${loadedName} for style ${style.style} and weight ${style.weight}.` - ); + if (info.guessFallback) { + // We're trying to load only one system font. + const match = src.match(/^local\((.*)\)$/); + warn( + `Cannot load system font: ${match?.[1]}, installing it could help to improve PDF rendering.` + ); + } else { + warn( + `Cannot load system font: ${loadedName} for style ${style.style} and weight ${style.weight}.` + ); + } this.removeNativeFontFace(fontFace); } return;