Substitute a system font when an embedded CFF is truncated

It fixes #7625.

If the Top DICT's Private DICT extends past the end of the font data,
the Local Subrs INDEX is unreachable and every CharString that calls
a subr ends up as a blank glyph. Throw from parsePrivateDict so the
existing catch in translateFont triggers fallbackToSystemFont, then
run getFontSubstitution post-construction so we pick a close local
match instead of the generic fallbackName.
This commit is contained in:
calixteman 2026-05-23 22:59:28 +02:00 committed by Calixte Denizet
parent 143a7244a3
commit adcde1175e
4 changed files with 46 additions and 1 deletions

View File

@ -787,6 +787,12 @@ class CFFParser {
this.emptyPrivateDictionary(parentDict);
return;
}
// The Private DICT extends past the end of the font data, which means
// the embedded font is truncated; abort so the caller can substitute a
// system font instead of rendering blank glyphs (issue 7625).
if (offset + size > this.bytes.length) {
throw new FormatError("CFF Private DICT extends past end of font");
}
const privateDictEnd = offset + size;
const dictData = this.bytes.subarray(offset, privateDictEnd);

View File

@ -4728,7 +4728,35 @@ class PartialEvaluator {
const newProperties = await this.extractDataStructures(dict, properties);
this.extractWidths(dict, descriptor, newProperties);
return new Font(fontName.name, fontFile, newProperties, this.options);
const font = new Font(fontName.name, fontFile, newProperties, this.options);
// The embedded font may have been too corrupt to parse, in which case
// we ended up in the fallback path without a substitution selected.
// Try the substitution map now so text renders in a font close to what
// the document asked for (issue 7625).
if (
font.missingFile &&
!font.systemFontInfo &&
!isType3Font &&
this.options.useSystemFonts
) {
const standardFontName = getStandardFontName(fontName.name);
const substitution = getFontSubstitution(
this.systemFontCache,
this.idFactory,
this.options.standardFontDataUrl,
fontName.name,
standardFontName,
type
);
if (substitution) {
if (substitution.guessFallback) {
substitution.guessFallback = false;
substitution.css += `,${font.fallbackName}`;
}
font.systemFontInfo = substitution;
}
}
return font;
}
static buildFontPaths(font, glyphs, handler, evaluatorOptions) {

View File

@ -0,0 +1 @@
https://github.com/mozilla/pdf.js/files/467169/Er.aestetik.en.loftestang.for.laering.pdf

View File

@ -14310,5 +14310,15 @@
"md5": "39d15f7f810bd89a4e5858df9c75ca4e",
"rounds": 1,
"type": "eq"
},
{
"id": "issue7625",
"file": "pdfs/issue7625.pdf",
"md5": "77ca0a41da767dca31ca45219e2ae202",
"link": true,
"rounds": 1,
"firstPage": 1,
"lastPage": 1,
"type": "eq"
}
]