Spacing tools
These tools help you review and optionally apply consistent sidebearings across a set of glyphs in a Glyphs font.
review_spacingcomputes measurements and returns suggested metrics (no mutation).apply_spacinguses the same logic to apply those suggestions (with a required confirmation gate).
The intent is to make spacing more systematic and repeatable, while keeping you in control through:
- clear per-glyph reporting, and
- conservative safety defaults (skip rules + clamping).
When to use it
Use the spacing tools when you want to:
- review spacing across selected glyphs or named glyph sets,
- compare sidebearing suggestions across masters,
- keep spacing changes conservative with clamps,
- configure repeatable spacing parameters,
- visualize the measurement model with guides.
Spacing tools are review aids and controlled apply tools. They do not replace visual proofing in words and text strings.
What the tools change (and what they don’t)
Changes
When applied, these tools adjust:
- LSB (left sidebearing)
- RSB (right sidebearing)
- optionally width (mainly for tabular figures / fixed-width targets)
Does not change
- Outlines (paths)
- Kerning
- Components (except they may be included in measurement)
- Anchors
Core idea (plain language)
For each glyph layer (glyph + master):
- Choose a vertical measurement band (
yMin..yMax) using a reference glyph. - At regular
ysteps (frequency), cast a horizontal “measurement line” through the glyph. - For each
y, find the first intersection from the left and the last intersection from the right. - Convert those intersections into two numbers:
- how much the glyph intrudes to the left of its “left edge” in that band,
- how much it intrudes to the right of its “right edge” in that band.
- Integrate (sum) these values across the band to get left/right “area” measurements.
- Compare the measured areas to a target area, then compute suggested LSB/RSB changes.
This is designed to approximate how much “white space” a reader perceives around a glyph, not just the extreme sidebearings at one height.
Reference glyphs and the measurement band
Many glyphs need different vertical measurement ranges:
- Lowercase letters typically want a band around the x-height region.
- Uppercase letters may want a taller band.
- Punctuation and symbols can require special handling.
These tools use a reference glyph to define the band:
- Find the reference layer’s bounds in the same master.
- Take
yMin..yMaxfrom those bounds. - Extend it by
over(as a percent of xHeight) above and below.
If a glyph doesn’t overlap enough with the band (coverage ratio), it is skipped to avoid nonsense measurements.
Important safety skips (by default)
The tools skip glyphs/layers when they are likely to be unsafe or misleading to auto-space:
- Empty layers (no paths and no components).
- Combining marks / nonspacing marks (they should typically stay width 0).
- Auto-aligned component layers (
layer.isAligned == true) whenskipAutoAligned=true. - Metrics keys:
- If both left and right metrics keys are present and
respectMetricsKeys=true, the glyph is skipped. - If only one side has a metrics key, the tool will keep that side unchanged and only suggest the other side.
- If both left and right metrics keys are present and
- Insufficient vertical coverage (the glyph doesn’t meaningfully overlap the band).
You’ll see the exact skip reason in each result entry.
Parameters
You can pass parameters as:
defaults(per-call), and/or- font-level or master-level custom parameters (read automatically when present):
- Canonical (recommended):
cx.ap.spacingArea,cx.ap.spacingDepth,cx.ap.spacingOver,cx.ap.spacingFreq - Legacy aliases (read-only compatibility):
gmcpSpacingArea,gmcpSpacingDepth,gmcpSpacingOver,gmcpSpacingFreqparamArea,paramDepth,paramOver,paramFreq
- Canonical (recommended):
Best way to set parameters (recommended)
Use custom parameters inside the .glyphs file for values that should be consistent for everyone using the font:
- good for:
cx.ap.spacingArea,cx.ap.spacingDepth,cx.ap.spacingOver,cx.ap.spacingFreq - benefits: travels with the font, persists across sessions, and can differ per master
Use a JSON config file for values you want to version-control and share as “spacing presets”:
- good for:
rules, plus anydefaultsyou want to keep in sync across a team - benefits: diffable, reviewable, easy to keep multiple presets (e.g. “text”, “display”, “UI”)
In practice, a good split is:
- In the font (custom parameters):
cx.ap.spacingArea/Depth/Over/Freq(recommended) - In a file:
rules+ any per-project defaults likereferenceGlyph,italicMode,minCoverageRatio, tabular settings
Precedence (what wins)
For each numeric parameter (area, depth, over, frequency) the tools resolve values in this exact order:
- Per-call
defaults(if provided) - Master custom parameter (canonical
cx.ap.spacing*) - Master custom parameter (legacy
gmcpSpacing*) - Master custom parameter (legacy
param*) - Font custom parameter (canonical
cx.ap.spacing*) - Font custom parameter (legacy
gmcpSpacing*) - Font custom parameter (legacy
param*) - Internal default
Setting spacing params in Glyphs (UI)
You can set the parameters in Glyphs’ UI:
- Font-level:
File → Font Info… → Font → Custom Parameters - Per-master:
File → Font Info… → Masters → (select a master) → Custom Parameters
Add any of these keys (case-sensitive). Recommended canonical names:
cx.ap.spacingArea(number)cx.ap.spacingDepth(number, percent of xHeight)cx.ap.spacingOver(number, percent of xHeight)cx.ap.spacingFreq(number, y sampling step in units)
Legacy aliases (still read, but not recommended for new work):
gmcpSpacingArea,gmcpSpacingDepth,gmcpSpacingOver,gmcpSpacingFreqparamArea,paramDepth,paramOver,paramFreq
Font-level parameters act as defaults; master-level parameters override them.
HT compatibility mode (legacy param* keys)
If you previously used HTLetterspacer, your masters (or font) may already contain:
paramArea,paramDepth,paramOver,paramFreq
review_spacing / apply_spacing will automatically read these as legacy keys when the canonical cx.ap.spacing* keys are not present.
Migration (recommended): write canonical keys once (so future tools and collaborators see the branded keys in Font Info), then save:
{
"font_index": 0,
"scope": "font",
"params": { "area": 400, "depth": 15, "over": 0, "frequency": 5 }
}
Then call save_font to persist.
Setting spacing params in Glyphs (Macro Panel script)
If you want to write them into the font programmatically, paste this into Glyphs’ Macro Panel:
from GlyphsApp import Glyphs
font = Glyphs.font
if not font:
raise RuntimeError("No active font")
# Font-wide defaults
font.customParameters["cx.ap.spacingArea"] = 400
font.customParameters["cx.ap.spacingDepth"] = 15
font.customParameters["cx.ap.spacingOver"] = 0
font.customParameters["cx.ap.spacingFreq"] = 5
# Optional: override per master
for master in font.masters:
if master.name == "Display":
master.customParameters["cx.ap.spacingArea"] = 440
Setting parameters via MCP (set_spacing_params)
If you prefer not to use the UI or Macro Panel, use the MCP tool set_spacing_params (it does not auto-save).
Font-level defaults:
{
"font_index": 0,
"scope": "font",
"params": { "area": 400, "depth": 15, "over": 0, "frequency": 5 }
}
Override one master:
{
"font_index": 0,
"scope": "master",
"master_id": "MASTER_ID_HERE",
"params": { "area": 440 }
}
Delete a value (unset):
{
"font_index": 0,
"scope": "font",
"params": { "over": null }
}
Preview changes without writing:
{
"font_index": 0,
"scope": "font",
"params": { "area": 420 },
"dry_run": true
}
To persist after setting values, call save_font.
Using a text file (JSON) for defaults + rules
There’s an example config file at:
./spacing-config.example.json
Recommended pattern:
- Keep your preferred
defaults+rulesin a JSON file in your repo. - When calling
review_spacing/apply_spacing, load that JSON in your client and pass:defaults: config.defaultsrules: config.rules
The spacing tools do not currently read rules from the font automatically; passing them explicitly keeps the behavior transparent and reproducible.
defaults fields
All values are optional; omitted fields fall back to internal defaults.
area(number, default400)- Controls the “target white-space area” magnitude.
- Larger values generally produce looser spacing.
depth(number, percent of xHeight, default15)- Limits how far measurements are allowed to reach “into” openings.
- Helps avoid letting deep counters dominate results.
over(number, percent of xHeight, default0)- Extends the measurement band above/below the reference glyph’s bounds.
frequency(number, default5)- Sampling step in font units along the y-axis.
- Smaller values are slower but can be more stable.
referenceGlyph(string, default"x")- Fallback reference when no rule overrides it.
- Special value
"*"means “use the glyph itself as reference”.
factor(number, default1.0)- Global multiplier applied to the target area (usually overridden by rules).
italicMode("deslant"or"none", default"deslant")- When
"deslant", measurements are corrected using masteritalicAngle.
- When
includeComponents(bool, defaulttrue)- Whether to include components in intersection measurement.
respectMetricsKeys(bool, defaulttrue)skipAutoAligned(bool, defaulttrue)minCoverageRatio(number0..1, default0.7)tabularMode(bool, defaultfalse)- If true, and if a width target is available, the tool will keep width fixed by distributing width adjustments across LSB/RSB.
tabularWidth(number or null, defaultnull)- If set, becomes the fixed width target used by
tabularMode.
- If set, becomes the fixed width target used by
includeSamples(bool, defaultfalse)- If true,
review_spacingincludes per-y sample arrays (larger payload).
- If true,
Rules (per-glyph overrides)
The rules parameter is a list of objects. The best matching rule wins.
Each rule may include:
script(string or"*")category(string or"*")subCategory(string or"*")nameRegex(regex string, optional) ornameFilter(substring, optional)referenceGlyph(string;"*"uses the glyph itself)factor(number)
Matching behavior
- A rule matches when all specified fields match.
- More specific rules win over wildcard rules.
- If two rules are equally specific, the later rule in the list wins (so you can append overrides).
Tool reference
review_spacing
Computes a per-glyph spacing report and suggestions.
Inputs
font_index(int, default0)glyph_names(list of strings, optional)- If omitted, the tool uses the currently selected glyphs in Glyphs only if
font_indexis the active font.
- If omitted, the tool uses the currently selected glyphs in Glyphs only if
master_id(string, optional)- If omitted, evaluates all masters.
rules(list, optional)defaults(object, optional)debug(object, optional)- Currently supports
includeSamples.
- Currently supports
Output
okbooleansummarycounts and effective defaultsresultslist of per-layer entries:status:"ok" | "skipped" | "error"reason(when skipped/error)currentmetricsreferenceband infomeasuredareas/extremestargetvaluessuggestedmetricsdeltavs currentwarningslist
Metric types
current.width/lsb/rsb,suggested.width/lsb/rsb, anddelta.width/lsb/rsbare always integers (font units).- Other numeric fields (
measured.*,target.*,reference.*, etc.) may be floats.
apply_spacing
Applies the suggestions computed by the same engine.
Safety gates
- If
confirm=falseanddry_run=false, the tool refuses to run. - Use
dry_run=truefirst to preview.
Inputs
Same as review_spacing, plus:
clamp(object, optional)maxDeltaLSB(default150)maxDeltaRSB(default150)minLSB(default-200)minRSB(default-200)
confirm(bool, defaultfalse)dry_run(bool, defaultfalse)
Output
okbooleansummaryresults(the same analysis results)appliedlist (only when actually applied) with before/after metrics
set_spacing_guides
Adds or clears glyph-level guides (layer.guides) that visualize the spacing model’s geometry:
- the vertical measurement band (
yMin..yMax), and (by default) - the key x′ boundaries used to compute negative space (zone edges, depth clamp, and average whitespace).
This is purely a visualization aid. It:
- writes guides into glyph layers (so they travel with the
.glyphsfile), - does not change metrics,
- does not auto-save.
Inputs
font_index(int, default0)glyph_names(list of strings, optional)- If omitted, uses selected glyphs in Glyphs (active font). If nothing is selected, it falls back to a small “diagnostic set”:
n,H,zero,o,O,period,comma.
- If omitted, uses selected glyphs in Glyphs (active font). If nothing is selected, it falls back to a small “diagnostic set”:
master_scope(string, default"current")"current": only the currently selected master"all": all masters"master": a specific master viamaster_id
master_id(string, required whenmaster_scope="master")mode("add"or"clear", default"add")reference_glyph(string, default"x")- Special value
"*"means “use the glyph itself”.
- Special value
style("band" | "model" | "full", default"model")"band": only the vertical measurement band (two horizontal guides)."model": band + the most didactic x′ boundaries (zone, depth clamp, measured vs target averages)."full":"model"plus extra reference bounds and full extremes (more clutter).
dry_run(bool, defaultfalse)
Output
okbooleansummarycountsresultslist with per-glyph/per-master actions, the computed band, and guide names
Notes
- To see them in the editor, enable
View → Show Guides. - By default,
mode="add"clears previously created spacing guides for the target layers (so it’s idempotent). - In italic masters with
italicMode="deslant", x′ boundary guides are drawn as slanted lines so they represent a constant deslanted-x across the band (i.e. they match the model’s coordinate space).
What the guides mean (didactic)
Band (yMin/yMax)
cx.ap.spacing.band:min/cx.ap.spacing.band:max- The exact vertical region the model samples at
frequencysteps.
- The exact vertical region the model samples at
Zone edges (what counts as “the edge”)
cx.ap.spacing.zone:L/cx.ap.spacing.zone:R- The model’s left/right “reference edges” inside the band (
lExtreme/rExtreme), expressed in x′ space (deslanted if italic).
- The model’s left/right “reference edges” inside the band (
Depth clamp (how far into openings we measure)
cx.ap.spacing.depth:L/cx.ap.spacing.depth:R- The clamp limits derived from
spacingDepth(percent of x-height), starting from the zone edges. - If the glyph has deep counters, the clamp prevents extreme “inward” measurements from dominating.
- The clamp limits derived from
Average whitespace (measured vs target)
cx.ap.spacing.avg.measured:L/cx.ap.spacing.avg.measured:R- Where the current average whitespace sits on each side (area ÷ height).
cx.ap.spacing.avg.target:L/cx.ap.spacing.avg.target:R- Where the model wants the average whitespace to sit, based on
spacingArea(after scaling by UPM/x-height).
- Where the model wants the average whitespace to sit, based on
Only in style="full"
cx.ap.spacing.ref:min/cx.ap.spacing.ref:max- The raw reference bounds without
spacingOver(useful to understand whatoveris doing).
- The raw reference bounds without
cx.ap.spacing.full:L/cx.ap.spacing.full:R- The full extremes across the glyph’s own bounds (used for overshoot compensation in the engine).
Recommended workflow
- Select glyphs you want to space (or pass
glyph_namesexplicitly). - Run
review_spacing:- Check skip reasons.
- Look at the largest
deltaoutliers.
- Tune
rulesand/ordefaults:- pick better reference glyphs for punctuation and symbols
- adjust
factorper class
- Run
apply_spacing(dry_run=true)to confirm clamps and diffs. - Run
apply_spacing(confirm=true)to apply. - Repeat: re-run
review_spacingto confirm deltas are near zero.
Practical examples
Review selected glyphs (active font)
Call:
{
"font_index": 0
}
Review specific glyphs across all masters
{
"font_index": 0,
"glyph_names": ["H", "O", "n", "o", "period", "comma"],
"defaults": {
"referenceGlyph": "n",
"frequency": 5,
"area": 420
}
}
Apply with a conservative clamp (two-step)
Dry run:
{
"font_index": 0,
"glyph_names": ["H", "O", "n", "o"],
"dry_run": true,
"clamp": { "maxDeltaLSB": 80, "maxDeltaRSB": 80 }
}
Apply:
{
"font_index": 0,
"glyph_names": ["H", "O", "n", "o"],
"confirm": true,
"clamp": { "maxDeltaLSB": 80, "maxDeltaRSB": 80 }
}
Limitations and notes
- Measurements rely on
layer.intersectionsBetweenPoints(...), which behaves like the Glyphs measurement tool; unusual outlines, open paths, or complex overlaps can produce sparse or noisy intersections. - Auto-spacing does not replace human review. Use text strings and proofing after applying.
- If your font heavily uses metrics keys or auto-aligned component glyphs, many layers will be skipped by design.
Related reference
Prompt templates (copy/paste)
HTspacer-style spacing pass (review → dry-run → apply)
You are a meticulous spacing assistant for a type designer working in Glyphs.
Rules:
- Never auto-save.
- Never mutate without a dry run first.
- Keep changes conservative (prefer clamping).
- If glyph_names isn't provided, use my current selection in the active font.
Task: Review and (optionally) apply spacing suggestions to improve rhythm and consistency.
1) Call review_spacing:
{"font_index":0}
2) Summarize:
- Top 15 layers by |delta.lsb| + |delta.rsb| (and why they’re outliers)
- Skipped layers grouped by reason (metrics keys, low coverage, etc.)
- Any warnings that suggest a bad measurement band / reference glyph
3) Call apply_spacing (dry run with a conservative clamp):
{"font_index":0,"dry_run":true,"clamp":{"maxDeltaLSB":80,"maxDeltaRSB":80,"minLSB":-50,"minRSB":-50}}
4) If I say “apply”, call apply_spacing again with confirm=true using the same clamp.
5) If I say “save”, call save_font.
Tabular figures spacing (fixed width)
I want tabular figures to keep a fixed width by distributing width changes evenly across LSB/RSB.
First, select your figure glyphs in Glyphs (Font view), then:
1) Call review_spacing:
{"font_index":0,"defaults":{"tabularMode":true,"tabularWidth":600,"referenceGlyph":"zero"}}
2) Call apply_spacing (dry run):
{"font_index":0,"dry_run":true,"defaults":{"tabularMode":true,"tabularWidth":600,"referenceGlyph":"zero"},"clamp":{"maxDeltaLSB":60,"maxDeltaRSB":60,"minLSB":-50,"minRSB":-50}}
3) If I approve, call apply_spacing again with confirm=true using the same args.
Visualize the spacing model with guides (expert)
Paste this into your LLM/client as a single prompt. It is designed to be safe, practical, and typography-oriented.
Role: You are a meticulous spacing assistant for a type designer working in Glyphs.
Rules: Never auto-save. Usedry_runbefore mutating tools. Prefer a small diagnostic glyph set unless the user provides specific glyphs.
Task: Add spacing-model guides so I can visually confirm what the spacing engine is measuring (band, zone edges, depth clamp, and measured-vs-target averages) in the current master, then tell me what to look for.
- Call
set_spacing_guideswith:
{
"font_index": 0,
"master_scope": "current",
"mode": "add",
"style": "model",
"reference_glyph": "x"
}
- Tell me exactly how to visualize them in Glyphs:
- Turn on
View → Show Guides. - Open the diagnostic glyphs (e.g.
n,H,zero,o,O,period,comma) and verify:- The two horizontal guides match the intended measurement band for the current master.
- The x′ guides are meaningful:
zone:L/Rroughly bracket the “main body” of the glyph inside the band.depth:L/Rshow how far into counters/openings the model is allowed to “look”.avg.measured:*vsavg.target:*shows whether the glyph is currently too tight/loose on each side.
- In italics (if any), confirm x′ guides are slanted (they should represent constant deslanted-x across the band).
- If the band looks wrong, help me correct it:
- If it’s too tall/short: suggest adjusting
cx.ap.spacingOver. - If it’s using an unhelpful reference: suggest a better
reference_glyph(e.g.nfor lowercase,Hfor uppercase,zerofor figures) and re-runset_spacing_guides. - If the depth clamp looks wrong: suggest adjusting
cx.ap.spacingDepth(percent of x-height). - If target averages look too tight/loose globally: suggest adjusting
cx.ap.spacingArea.
- When I say “clear guides”, call:
{
"font_index": 0,
"master_scope": "all",
"mode": "clear"
}