mozilla/pdf.js · error · FormatError
CFF Private DICT extends past end of font
Error message
CFF Private DICT extends past end of font
What it means
Thrown by CFFParser.parsePrivateDict when the Private DICT's offset+size exceeds the total font byte length, i.e. the embedded CFF font is truncated. Deliberately added (issue 7625) so the caller aborts and substitutes a system font rather than rendering blank glyphs.
Source
Thrown at src/core/cff_parser.js:831
}
const privateOffset = parentDict.getByName("Private");
// make sure the params are formatted correctly
if (!Array.isArray(privateOffset) || privateOffset.length !== 2) {
parentDict.removeByName("Private");
return;
}
const size = privateOffset[0];
const offset = privateOffset[1];
// remove empty dicts or ones that refer to invalid location
if (size === 0 || offset >= this.bytes.length) {
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);
const dict = this.parseDict(dictData);
const privateDict = this.createDict(
CFFPrivateDict,
dict,
parentDict.strings
);
parentDict.privateDict = privateDict;
const blueScale = privateDict.getByName("BlueScale");
const blueShift = privateDict.getByName("BlueShift");
const blueFuzz = privateDict.getByName("BlueFuzz");
const expansionFactor = privateDict.getByName("ExpansionFactor");
if (
blueScale === 0 &&View on GitHub (pinned to 5903d58d58)
Solutions
- Accept pdf.js's automatic fallback to a system font (this error is designed to trigger that).
- Re-embed or repair the font by re-saving the PDF with Ghostscript or fontTools.
- Ensure the source font file is complete before subsetting/embedding in your pipeline.
- If streaming the PDF over range requests, verify the full font stream was fetched before parsing.
Example fix
// No caller-side fix; pdf.js substitutes a fallback font.
// To prevent: validate the CFF before embedding.
try {
await page.render({ canvasContext, viewport }).promise;
} catch (e) {
console.warn('Render aborted; embedded CFF font is truncated:', e);
} Defensive patterns
Strategy: fallback
Validate before calling
null
Type guard
null
Try / catch
try {
await page.render({ canvasContext, viewport }).promise;
} catch (e) {
// Truncated CFF Private DICT; pdf.js substitutes a system font.
console.warn('Truncated embedded CFF font:', e);
} Prevention
- Ensure font streams are fully embedded (not cut off) when generating PDFs.
- Verify range requests fetch the complete font stream before parsing.
- Repair PDFs with Ghostscript to re-embed complete font programs.
When it happens
Trigger: An embedded CFF font declares a Private DICT whose [size, offset] pair points past the end of the available font bytes. Reached only after earlier guards pass (size != 0 and offset < bytes.length), specifically when `offset + size > this.bytes.length`.
Common situations: PDFs with truncated or partially downloaded embedded CFF fonts; font streams cut off by storage limits; corrupt files where the Private DICT pointer is stale; output from faulty font subsetting/embedding tools.
Related errors
- Invalid CFF header
- Unknown charset format
- Unknown encoding format: ${format} in CFF
- parseFDSelect: Unknown format "${format}".
- parseFDSelect: Invalid font data.
AI-assisted analysis of mozilla/pdf.js@5903d58d58 (2026-08-13).
Data as JSON: /api/errors/d042f3bf9e83f511.
Report an issue: GitHub.