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

  1. Inspect the printed unexpected_glyphs list; fix the listed glyphs' advance widths to match expect_widths in the source design files.
  2. Ensure the glyph normalization/width-adjust step runs before verify_glyph_width in the build pipeline.
  3. If new glyphs are intentionally wider (e.g. ligatures), add their expected widths to expect_widths passed to the build function.
  4. 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

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


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