Glyphs 3 plugin that turns a lettering/type scan into a clean first vector outline — drawn the way a type designer would: a minimum of nodes, placed on the extrema, with aligned tangents. Not a faithful auto-trace (potrace already does that); a structurally sound starting point to retouch by hand.
Working end-to-end in Glyphs 3: scan → binarize → trace → reconstruct → oriented contours placed in the layer, validated on real pencil scans. Active work is trace-quality tuning.
brew install autotrace # centerline tracing (line drawings) + ImageMagick
brew install potrace # filled-shape tracing (optional, other mode)
python3 build.py --install # sync core + symlink into Glyphs PluginsRestart Glyphs. Place a scan (a single cropped letter works best) as the layer's background image by dragging it into the edit view, open Filter ▸ Anagone, pick the Pencil mode (default) for pencil outline drawings, optionally tune Smoothing / Detail, hit Trace.
The dialog is deliberately minimal: on a standardized scan (see The perfect scan below) the defaults just work, so most knobs live under Advanced. Binarization is automatic (Otsu) — there is no threshold knob.
| Button | Tracer | Use for |
|---|---|---|
| Pencil | autotrace -centerline |
a thin pencil outline drawing (the line is the letter contour). The everyday mode. |
| Inked | potrace |
a filled / inked shape (solid black letter). Reveals the POTRACE section, hides the centerline-only knobs. |
| Setting | Default | What it does |
|---|---|---|
| Smoothing | 2 | Density / tautness lever. Moving-average-smooths the traced polyline before detection: higher = fewer wiggles and nodes, rounder; 0 = fit the raw pixels (more nodes). The main quality knob. |
| Detail | 8 | Bézier fit tolerance (em). Lower = hug the ink more closely (more nodes); higher = smoother and leaner. |
| Setting | Default | What it does |
|---|---|---|
| Denoise | 1 | Median grain removal before tracing — wipes salt-and-pepper paper grain. Raise for noisy scans, 0 for a clean one. |
| Stroke width (Pencil) | 10 | Max edge separation (em) to merge a too-thick / redrawn line's two traced edges back into one centerline. Keep it near the real pencil width; too wide merges genuine parallel strokes. 0 = off. |
| Merge doubled strokes (Pencil) | on | Enables the merge above. |
| Filter stray diagonals | on | Drops long, thin, diagonal junk contours (construction lines, bad joins) while keeping stems, bars and curves. |
| Setting | Default | What it does |
|---|---|---|
| Close | 80 | Loop closure. Max gap (em) to reassemble two loose arc ends into a closed letter. Raise to close fragmented letters (busy junctions); lower if it bridges things that should stay open. |
| Corner angle | 45° | Turn angle above which a vertex becomes a sharp corner instead of a smoothed tangent. |
| Always close | on | Guarantees every contour is laid down closed (with a cross-letter-chord safeguard). Off = gap-aware (very open arcs stay open). |
| Straight lines | on | Detects straight segments (else everything is rendered as handled curves). |
| Inflections | on | Places a node at inflection points (where a curve changes direction). |
| Setting | Default | What it does |
|---|---|---|
| Alphamax | 1.0 | potrace corner smoothing: 0 = polygonal (hard corners), high = rounded. |
| Turdsize | 2 | Drops speckles smaller than N pixels. |
snap/grid step (always round to the integer em grid — Glyphs rounds too),
flip X/Y (orientation correctness constants), and join gap (a first-pass
stitch; it helps but its value barely matters, so it's fixed per mode: 12 in
Pencil, 0 in Inked). Closure therefore has a single knob: Close.
Anagone assumes a standardized input — the closer your scan is to this, the fewer knobs you ever touch:
- one already-cropped letter per image;
- a single, clean pencil outline — not filled, not hatched, not redrawn/doubled;
- a dark, even line (B/2B, steady pressure) on clean white paper;
- high contrast, even light — no shadow, no gradient, no visible grain;
- loops actually closed where they should be;
- 300–600 dpi, greyscale, a light JPEG.
On such a scan the defaults just work and binarization (Otsu) is automatic.
The algorithmic core is pure Python with no Glyphs and no third-party imports,
so it runs standalone and stays portable to the interpreter inside Glyphs. Only
plugin.py touches the Glyphs API.
anagone/ # pure-Python core (no Glyphs, no deps)
potrace_runner.py # binarize (ImageMagick) -> autotrace/potrace -> polylines
segmentation.py # extrema + corner + inflection key points -> segment fit
schneider.py # Schneider (Graphics Gems) cubic-Bezier fitting
geometry.py # 2D vector ops + polyline helpers
pipeline.py # cleanup/stitch/closure orchestration + Params
glyphspath.py # nodes -> neutral on/off-curve point list
grid.py # UPM snap
Anagone.glyphsFilter/ # the Glyphs bundle (FilterWithDialog)
…/plugin.py # the ONLY Glyphs-API code: reads the bg image, builds GSPaths
Pipeline: background image → binarize (Otsu) → trace → flatten to polylines → clean / stitch / close → Schneider refit → place oriented contours in the layer.
The algorithmic core is pure Python and is unit-tested without Glyphs:
python3 tests/test_pipeline_standalone.py # filters, stitching, closure, params
python3 tests/test_fitting_standalone.py # Schneider fit on O / S / A fixtures
python3 tests/test_plugin_ui_standalone.py # dialog builds (Glyphs API mocked)The one piece that must be checked live in Glyphs is the background-image → em
transform (plugin.py:_bg_affine).
Apache License 2.0 — Copyright 2026 Guillaume Santial. See LICENSE.
potrace and autotrace are separate GPL command-line tools invoked as external
binaries (not linked), so they do not affect Anagone's own license.