{"record":{"id":"0d75e5ed9fb6502d","repo":"subframe7536/maple-font","slug":"file-name-or-the-font-may-contains-glyphs-that","errorCode":null,"errorMessage":"{file_name or 'The font'} may contains glyphs that width is not in {expect_widths}, which may broke monospace rule.\n{unexpected_glyphs}","messagePattern":"(.+?) may contains glyphs that width is not in (.+?), which may broke monospace rule\\.\n(.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"source/py/utils.py","lineNumber":213,"sourceCode":"# https://github.com/subframe7536/maple-font/issues/314\ndef verify_glyph_width(\n    font: TTFont, expect_widths: list[int], file_name: str | None = None\n):\n    result = []\n    for name in font.getGlyphNames():\n        width, _ = font[\"hmtx\"][name]  # type: ignore\n        if width not in expect_widths:\n            result.append([name, width])\n\n    if result.__len__() == 0:\n        print(f\"✅ Verified glyph width in {file_name}\")\n        return\n\n    unexpected_glyphs = \"\\n\".join(\n        [f\"{item[0]}  =>  {item[1]}\" for item in result[1:20]]\n    )\n\n    raise Exception(\n        f\"{file_name or 'The font'} may contains glyphs that width is not in {expect_widths}, which may broke monospace rule.\\n{unexpected_glyphs}\"\n    )\n\n\ndef archive_fonts(\n    source_file_or_dir_path: str,\n    target_parent_dir_path: str,\n    family_name_compact: str,\n    suffix: str,\n    build_config_path: str,\n) -> tuple[str, str]:\n    \"\"\"\n    Archive folder and return sha1 and file name\n    \"\"\"\n    source_folder_name = path.basename(source_file_or_dir_path)\n\n    zip_name_without_ext = f\"{family_name_compact}-{source_folder_name}{suffix}\"\n","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/subframe7536/maple-font/blob/c08fda97fef73d68c1755219852150770f6e6578/source/py/utils.py#L195-L231","documentation":"verify_glyph_width checks that every glyph advance width in a built font is one of the expected widths for a monospace font. If more than the allowed number of glyphs have widths outside expect_widths, it raises this generic Exception listing the first 20 offending glyphs as \"glyph => width\" lines. It exists because any off-grid advance width breaks the monospace alignment guarantee of the font.","triggerScenarios":"Calling build_mono, build_nf, build_cn, or build_variable_fonts (which all invoke verify_glyph_width) when the source font or a patched font contains glyphs whose advance width is not in expect_widths. This fires after font loading/glyph normalization, when result[1:] is non-empty.","commonSituations":"Editing or adding ligature/glyph glyphs with wrong widths in the source glyphs files; a fontTools version change altering how glyphs are normalized; custom CN glyphs added with proportional widths; forgetting to run the width-normalization step before verification.","solutions":["Inspect the printed unexpected_glyphs list; fix the listed glyphs' advance widths to match expect_widths in the source design files.","Ensure the glyph normalization/width-adjust step runs before verify_glyph_width in the build pipeline.","If new glyphs are intentionally wider (e.g. ligatures), add their expected widths to expect_widths passed to the build function.","Regenerate the font from an untouched upstream source to rule out a corrupted or hand-edited font file."],"exampleFix":"// before: a custom glyph with width 600 while expect_widths=[600] but ligature at 1200 unlisted\n# expect_widths = [600]\n// after\n# expect_widths = [600, 1200]  # or fix the ligature glyph to occupy 2 x 600 cells","handlingStrategy":"validation","validationCode":"from fontTools.ttLib import TTFont\n\ndef assert_monospace_widths(path, expect_widths):\n    font = TTFont(path)\n    hmtx = font[\"hmtx\"]\n    bad = {name: w for name, (w, _) in hmtx.metrics.items() if w not in expect_widths}\n    if bad:\n        preview = dict(list(bad.items())[:20])\n        raise ValueError(f\"glyph widths not in {expect_widths}: {preview}\")","typeGuard":"def has_valid_widths(font, expect_widths) -> bool:\n    return \"hmtx\" in font and all(\n        w in expect_widths for w, _ in font[\"hmtx\"].metrics.values()\n    )","tryCatchPattern":"try:\n    build_nf(...)\nexcept Exception as e:\n    if \"monospace rule\" in str(e):\n        print(\"Off-width glyphs:\\n\", e)  # message lists glyph => width\n    raise","preventionTips":["Run a width-check on the source font before starting the build.","Never hand-edit glyph widths; use the normalization step in the pipeline.","Add intentionally wider glyphs' widths to expect_widths in one central config.","Pin fontTools version so glyph processing stays deterministic."],"tags":["font","monospace","build","validation"],"backgroundTag":"glyph-width-mismatch","analyzedSha":"c08fda97fef73d68c1755219852150770f6e6578","analyzedAt":"2026-08-28T22:07:29.503Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}