{"record":{"id":"9573353fcc4fb188","repo":"subframe7536/maple-font","slug":"no-hhea-table-found","errorCode":null,"errorMessage":"No hhea table found.","messagePattern":"No hhea table found\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"source/py/task/merge_font/utils.py","lineNumber":257,"sourceCode":"\ndef change_line_height(\n    font: TTFont,\n    factor: float = 1.0,\n    metric: tuple[float, float] | None = None,\n    safe_metric: tuple[float, float] | None = None,\n) -> None:\n    \"\"\"\n    Adjust the line height of the font by modifying the hhea and OS/2 table.\n\n    Args:\n        font: The font to modify\n        factor: Scale factor to apply to metrics\n        metric: Tuple of (ascender, descender) for custom metrics\n        safe_metric: Tuple of (safe_ascender, safe_descender) for safe metrics\n    \"\"\"\n\n    if \"hhea\" not in font:\n        raise ValueError(\"No hhea table found.\")\n    if \"OS/2\" not in font:\n        raise ValueError(\"No OS/2 table found.\")\n\n    hhea = font[\"hhea\"]\n    os2 = font[\"OS/2\"]\n\n    if metric:\n        asc, desc = metric\n        safe_asc, safe_desc = safe_metric if safe_metric else (None, None)\n\n        # Maintain original ascender/descender ratio\n        ascender_ratio = asc / (asc - desc)  # type: ignore\n        # Calculate target total height\n        target_total_height = int(round(factor * (asc - desc)))\n\n        # Calculate new metrics\n        new_ascender = int(round(target_total_height * ascender_ratio))\n        new_descender = new_ascender - target_total_height","sourceCodeStart":239,"sourceCodeEnd":275,"githubUrl":"https://github.com/subframe7536/maple-font/blob/c08fda97fef73d68c1755219852150770f6e6578/source/py/task/merge_font/utils.py#L239-L275","documentation":"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.","triggerScenarios":"Calling polish() with any line_height configuration on a TTFont whose table list has no 'hhea' entry.","commonSituations":"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.","solutions":["Use a complete font that contains hhea (regenerate from source rather than heavily stripping tables).","Add a default hhea table via fontTools (newTable('hhea')) with sane ascender/descender before calling change_line_height.","Guard the call with `if 'hhea' in font and 'OS/2' in font` and skip line-height adjustment otherwise."],"exampleFix":"# before\nchange_line_height(font, factor=1.2)\n\n# after\nif \"hhea\" in font:\n    change_line_height(font, factor=1.2)","handlingStrategy":"type-guard","validationCode":"assert \"hhea\" in font, \"font must contain hhea table for line-height adjustment\"","typeGuard":"def can_change_line_height(font) -> bool:\n    return \"hhea\" in font and \"OS/2\" in font","tryCatchPattern":"try:\n    change_line_height(font, factor=factor)\nexcept ValueError as e:\n    if \"hhea\" in str(e):\n        print(\"Skipping line-height: font has no hhea table\")\n    else:\n        raise","preventionTips":["Verify hhea and OS/2 exist before any metric-mutating step.","Subset fonts with --notdef-outline plus default table retention; do not drop hhea.","Test the pipeline on the smallest real input font before batch runs."],"tags":["python","fonts","fonttools","line-height"],"backgroundTag":"missing-font-table","analyzedSha":"c08fda97fef73d68c1755219852150770f6e6578","analyzedAt":"2026-08-28T22:07:29.503Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}