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

  1. Accept pdf.js's automatic fallback to a system font (this error is designed to trigger that).
  2. Re-embed or repair the font by re-saving the PDF with Ghostscript or fontTools.
  3. Ensure the source font file is complete before subsetting/embedding in your pipeline.
  4. 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

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


AI-assisted analysis of mozilla/pdf.js@5903d58d58 (2026-08-13). Data as JSON: /api/errors/d042f3bf9e83f511. Report an issue: GitHub.