mozilla/pdf.js · error · FormatError

There must be a private dictionary.

Error message

There must be a private dictionary.

What it means

Thrown by CFFCompiler.compilePrivateDicts when iterating Font DICTs (the FDArray of a CIDFont) and encountering one that lacks either a privateDict or a 'Private' name. Every Font DICT must carry a Private DICT for compilation to succeed; its absence means the in-memory CFF object is inconsistent.

Source

Thrown at src/core/cff_parser.js:1752

      const fontDictTracker = new CFFOffsetTracker();
      const fontDictData = this.compileDict(fontDict, fontDictTracker);
      fontDictTrackers.push(fontDictTracker);
      fdArrayIndex.add(fontDictData);
      fontDictTracker.offset(length);
    }
    fdArrayIndex = this.compileIndex(fdArrayIndex, fontDictTrackers);
    return {
      trackers: fontDictTrackers,
      output: fdArrayIndex,
    };
  }

  compilePrivateDicts(dicts, trackers, output) {
    for (let i = 0, ii = dicts.length; i < ii; ++i) {
      const fontDict = dicts[i];
      const privateDict = fontDict.privateDict;
      if (!privateDict || !fontDict.hasName("Private")) {
        throw new FormatError("There must be a private dictionary.");
      }
      const privateDictTracker = new CFFOffsetTracker();
      const privateDictData = this.compileDict(privateDict, privateDictTracker);

      let outputLength = output.length;
      privateDictTracker.offset(outputLength);
      if (!privateDictData.length) {
        // The private dictionary was empty, set the output length to zero to
        // ensure the offset length isn't out of bounds in the eyes of the
        // sanitizer.
        outputLength = 0;
      }

      trackers[i].setEntryLocation(
        "Private",
        [privateDictData.length, outputLength],
        output
      );

View on GitHub (pinned to 5903d58d58)

Solutions

  1. Report to pdf.js with the offending CIDFont — emptyPrivateDictionary should normally prevent this.
  2. Ensure any code that builds/modifies Font DICTs always attaches a Private DICT and sets the 'Private' name.
  3. Repair the source font with fontTools before embedding.

Example fix

// before (internal: Font DICT missing Private)
this.compilePrivateDicts(dicts, trackers, output); // throws

// after (ensure every Font DICT has a Private DICT before compiling)
for (const fd of dicts) {
  if (!fd.privateDict || !fd.hasName('Private')) {
    this.emptyPrivateDictionary(fd); // attach an empty Private DICT
  }
}
this.compilePrivateDicts(dicts, trackers, output);
Defensive patterns

Strategy: validation

Validate before calling

// Ensure every Font DICT has a Private DICT before compiling.
function ensurePrivateDicts(parser, dicts) {
  for (const fd of dicts) {
    if (!fd.privateDict || !fd.hasName('Private')) {
      parser.emptyPrivateDictionary(fd);
    }
  }
}

Type guard

function fontDictHasPrivate(fd) {
  return !!fd.privateDict && fd.hasName('Private');
}

Try / catch

try {
  this.compilePrivateDicts(dicts, trackers, output);
} catch (e) {
  // A Font DICT lacks Private; attach empty ones and retry.
  for (const fd of dicts) {
    if (!fd.privateDict || !fd.hasName('Private')) this.emptyPrivateDictionary(fd);
  }
  this.compilePrivateDicts(dicts, trackers, output);
}

Prevention

When it happens

Trigger: Compiling a CIDFont whose FDArray contains a Font DICT without an attached privateDict or without the 'Private' operator name. Reached during the per-dict loop in compilePrivateDicts, used when re-emitting a sanitized CFF font.

Common situations: A malformed CIDFont where parsePrivateDict fell back to emptyPrivateDictionary but the 'Private' name was later removed; a pdf.js internal bug in FDArray construction; corrupt font data that yields Font DICTs missing Private entries.

Related errors


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