subframe7536/maple-font · error · ValueError

No hhea table found.

Error message

No hhea table found.

What it means

change_line_height() rewrites line-height metrics in both the 'hhea' and 'OS/2' tables. It requires the 'hhea' table; if absent it raises this ValueError before touching anything. hhea carries ascender/descender/lineGap used by many text renderers.

Source

Thrown at source/py/task/merge_font/utils.py:257

def change_line_height(
    font: TTFont,
    factor: float = 1.0,
    metric: tuple[float, float] | None = None,
    safe_metric: tuple[float, float] | None = None,
) -> None:
    """
    Adjust the line height of the font by modifying the hhea and OS/2 table.

    Args:
        font: The font to modify
        factor: Scale factor to apply to metrics
        metric: Tuple of (ascender, descender) for custom metrics
        safe_metric: Tuple of (safe_ascender, safe_descender) for safe metrics
    """

    if "hhea" not in font:
        raise ValueError("No hhea table found.")
    if "OS/2" not in font:
        raise ValueError("No OS/2 table found.")

    hhea = font["hhea"]
    os2 = font["OS/2"]

    if metric:
        asc, desc = metric
        safe_asc, safe_desc = safe_metric if safe_metric else (None, None)

        # Maintain original ascender/descender ratio
        ascender_ratio = asc / (asc - desc)  # type: ignore
        # Calculate target total height
        target_total_height = int(round(factor * (asc - desc)))

        # Calculate new metrics
        new_ascender = int(round(target_total_height * ascender_ratio))
        new_descender = new_ascender - target_total_height

View on GitHub (pinned to c08fda97fe)

Solutions

  1. Use a complete font that contains hhea (regenerate from source rather than heavily stripping tables).
  2. Add a default hhea table via fontTools (newTable('hhea')) with sane ascender/descender before calling change_line_height.
  3. Guard the call with `if 'hhea' in font and 'OS/2' in font` and skip line-height adjustment otherwise.

Example fix

# before
change_line_height(font, factor=1.2)

# after
if "hhea" in font:
    change_line_height(font, factor=1.2)
Defensive patterns

Strategy: type-guard

Validate before calling

assert "hhea" in font, "font must contain hhea table for line-height adjustment"

Type guard

def can_change_line_height(font) -> bool:
    return "hhea" in font and "OS/2" in font

Try / catch

try:
    change_line_height(font, factor=factor)
except ValueError as e:
    if "hhea" in str(e):
        print("Skipping line-height: font has no hhea table")
    else:
        raise

Prevention

When it happens

Trigger: Calling polish() with any line_height configuration on a TTFont whose table list has no 'hhea' entry.

Common situations: Subsetting/stripping tools that dropped hhea; non-TTF source formats converted incompletely; minimal icon fonts; passing a partially constructed TTFont object to the function directly.

Related errors


AI-assisted analysis of subframe7536/maple-font@c08fda97fe (2026-08-28). Data as JSON: /api/errors/9573353fcc4fb188. Report an issue: GitHub.