subframe7536/maple-font · error · Exception
{file_name or 'The font'} may contains glyphs that width is
Error message
{file_name or 'The font'} may contains glyphs that width is not in {expect_widths}, which may broke monospace rule.
{unexpected_glyphs} What it means
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.
Source
Thrown at source/py/utils.py:213
# https://github.com/subframe7536/maple-font/issues/314
def verify_glyph_width(
font: TTFont, expect_widths: list[int], file_name: str | None = None
):
result = []
for name in font.getGlyphNames():
width, _ = font["hmtx"][name] # type: ignore
if width not in expect_widths:
result.append([name, width])
if result.__len__() == 0:
print(f"✅ Verified glyph width in {file_name}")
return
unexpected_glyphs = "\n".join(
[f"{item[0]} => {item[1]}" for item in result[1:20]]
)
raise Exception(
f"{file_name or 'The font'} may contains glyphs that width is not in {expect_widths}, which may broke monospace rule.\n{unexpected_glyphs}"
)
def archive_fonts(
source_file_or_dir_path: str,
target_parent_dir_path: str,
family_name_compact: str,
suffix: str,
build_config_path: str,
) -> tuple[str, str]:
"""
Archive folder and return sha1 and file name
"""
source_folder_name = path.basename(source_file_or_dir_path)
zip_name_without_ext = f"{family_name_compact}-{source_folder_name}{suffix}"
View on GitHub (pinned to c08fda97fe)
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.
Example fix
// before: a custom glyph with width 600 while expect_widths=[600] but ligature at 1200 unlisted # expect_widths = [600] // after # expect_widths = [600, 1200] # or fix the ligature glyph to occupy 2 x 600 cells
Defensive patterns
Strategy: validation
Validate before calling
from fontTools.ttLib import TTFont
def assert_monospace_widths(path, expect_widths):
font = TTFont(path)
hmtx = font["hmtx"]
bad = {name: w for name, (w, _) in hmtx.metrics.items() if w not in expect_widths}
if bad:
preview = dict(list(bad.items())[:20])
raise ValueError(f"glyph widths not in {expect_widths}: {preview}") Type guard
def has_valid_widths(font, expect_widths) -> bool:
return "hmtx" in font and all(
w in expect_widths for w, _ in font["hmtx"].metrics.values()
) Try / catch
try:
build_nf(...)
except Exception as e:
if "monospace rule" in str(e):
print("Off-width glyphs:\n", e) # message lists glyph => width
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Invalid axe value, range: [{min_value}, {max_value}]
- line_height object must contain 'ascender' and 'descender' f
- Cannot build both `mono` and `propo` glyphs versions simulta
- No hhea table found.
- No OS/2 table found.
AI-assisted analysis of subframe7536/maple-font@c08fda97fe (2026-08-28).
Data as JSON: /api/errors/0d75e5ed9fb6502d.
Report an issue: GitHub.