mozilla/pdf.js · error · FormatError
Required "hhea" table is not found
Error message
Required "hhea" table is not found
What it means
Thrown when the font's table directory lacks an 'hhea' (Horizontal Header) table. The hhea table is mandatory — it carries ascent, descent, lineGap, maxAdvanceWidth, and the numberOfHMetrics used by the hmtx table. Without it, horizontal metrics cannot be parsed and the font's vertical layout is undefined.
Source
Thrown at src/core/fonts.js:2987
tables.loca,
tables.glyf,
numGlyphs,
isGlyphLocationsLong,
hintsValid,
dupFirstEntry,
maxSizeOfInstructions
);
missingGlyphs = glyphsInfo.missingGlyphs;
// Some fonts have incorrect maxSizeOfInstructions values, so we use
// the computed value instead.
if (version >= 0x00010000 && tables.maxp.length >= 32) {
tables.maxp.data[26] = glyphsInfo.maxSizeOfInstructions >> 8;
tables.maxp.data[27] = glyphsInfo.maxSizeOfInstructions & 255;
}
}
if (!tables.hhea) {
throw new FormatError('Required "hhea" table is not found');
}
// Sanitizer reduces the glyph advanceWidth to the maxAdvanceWidth
// Sometimes it's 0. That needs to be fixed
if (tables.hhea.data[10] === 0 && tables.hhea.data[11] === 0) {
tables.hhea.data[10] = 0xff;
tables.hhea.data[11] = 0xff;
}
// Extract some more font properties from the OpenType head and
// hhea tables; yMin and descent value are always negative.
const metricsOverride = {
unitsPerEm: int16(tables.head.data[18], tables.head.data[19]),
yMax: signedInt16(tables.head.data[42], tables.head.data[43]),
yMin: signedInt16(tables.head.data[38], tables.head.data[39]),
ascent: signedInt16(tables.hhea.data[4], tables.hhea.data[5]),
descent: signedInt16(tables.hhea.data[6], tables.hhea.data[7]),
lineGap: signedInt16(tables.hhea.data[8], tables.hhea.data[9]),View on GitHub (pinned to 5903d58d58)
Solutions
- Replace the font with a complete copy that includes the hhea table.
- Rebuild the font with fonttools to regenerate the hhea table from glyph metrics.
- Validate with ots-sanitize — it flags missing hhea as a critical error.
- If subsetting fonts, use a reliable tool (pyftsubset) that preserves mandatory tables.
Example fix
# rebuild font to ensure hhea is present and valid
from fontTools import ttLib
font = ttLib.TTFont('broken.ttf')
hhea = font['hhea'] # fontTools rebuilds on save if missing/corrupt
font.save('fixed.ttf')
ots-sanitize fixed.ttf Defensive patterns
Strategy: validation
Validate before calling
// Server-side: verify font has hhea table
// from fontTools import ttLib
// font = ttLib.TTFont('font.ttf')
// assert 'hhea' in font, 'Font missing hhea table'
Try / catch
try {
await page.render(renderParams).promise;
} catch (err) {
if (err.message?.includes('Required "hhea" table is not found')) {
console.error('Font missing hhea table; replace embedded font.');
} else { throw err; }
} Prevention
- Ensure all fonts include the mandatory hhea table before embedding.
- Rebuild fonts with fonttools to regenerate missing horizontal metrics tables.
- Use pyftsubset for subsetting — it preserves mandatory tables including hhea.
When it happens
Trigger: After the TrueType glyph location sanitization block (or after the CFF path rejoins), the check if (!tables.hhea) at line 2986 throws. Note that sanitizeMetrics was already called with tables.hhea earlier (it handles a null hhea gracefully), but subsequent code at line 2992 reads tables.hhea.data directly.
Common situations: A corrupt font missing the hhea table due to truncation or stripping. Fonts produced by defective subsetting tools that dropped hhea. Non-standard fonts that only provide vertical metrics (vhea) without horizontal. Damaged embedded font streams in PDFs.
Related errors
- Required "maxp" table is not found
- Required "head" table is not found
- unsupported cmap: ${format}
- Unicode ranges Bits > 123 are reserved for internal usage
- Required "loca" table is not found
AI-assisted analysis of mozilla/pdf.js@5903d58d58 (2026-08-13).
Data as JSON: /api/errors/eb56679e5d8ea26e.
Report an issue: GitHub.