Convert TrueType fonts to stencil-ready versions by automatically adding bridges to enclosed contours.
Stencilizer is a Python CLI tool that transforms regular fonts into stencil fonts by detecting "islands" (enclosed contours) in glyphs and adding bridges to connect them. This is essential for creating fonts suitable for stencil cutting, where disconnected parts would fall out.
Characters like O, A, B, D, P, R, Q, 4, 6, 8, 9, @ and others often have enclosed contours that need bridges to remain connected during cutting.
- Automatic Island Detection: Intelligently identifies enclosed contours using contour hierarchy analysis
- Smart Bridge Placement: Places bridges optimally based on contour geometry
- Parallel Processing: Leverages multicore CPUs for fast processing of large fonts
- Flexible Configuration: Control bridge width and parallel workers
- Font Coexistence: Output fonts get a "Stenciled" suffix in their internal name table, allowing installation alongside the original font
- Multiple Output Modes:
- Full processing (default)
- Dry-run analysis
- Island listing
- Rich CLI Output: Beautiful console output with progress tracking
- Detailed Logging: Optional file logging for debugging and analysis
- Format Support: Works with TTF and OTF fonts containing TrueType outlines (see Font Format Support)
git clone https://github.com/cosmix/stencilizer.git
cd stencilizer
uv pip install -e .git clone https://github.com/cosmix/stencilizer.git
cd stencilizer
uv pip install -e .
stencilizer Roboto-Regular.ttfThis creates Roboto-Regular-stenciled.ttf in the same directory.
Example output:
Stencilizer v1.0.0
────────────────────────────────────────────
▸ Loading font
Roboto-Regular.ttf (TrueType)
897 glyphs · 2,048 UPM
▸ Analyzing glyphs
42 glyphs with islands
▸ Processing
8 workers (auto) · Ctrl+C to cancel
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:02
✓ Complete in 2.1s
Roboto-Regular-stenciled.ttf (156 KB)
42 glyphs · 38 bridges · 0 errors
45.2ms avg (12.1–98.3ms range)
# Convert with default settings
stencilizer input.ttf
# Specify output path
stencilizer input.ttf -o output.ttf
# Adjust bridge width (30-110% of stroke width)
stencilizer input.ttf --bridge-width 70# List all glyphs with islands
stencilizer input.ttf --list-islands
# Dry run (analyze without modifying)
stencilizer input.ttf --dry-run
# Verbose output
stencilizer input.ttf --verbose
# Quiet mode
stencilizer input.ttf --quiet# Control parallel workers
stencilizer input.ttf --workers 4
# Use all available cores (default)
stencilizer input.ttf# Enable file logging
stencilizer input.ttf --log-file stencilizer.log
# Set log level
stencilizer input.ttf --log-level DEBUGConfiguration is controlled via CLI options (see the usage examples above).
If you are using Stencilizer as a library, create a StencilizerSettings instance and set
values directly before passing it to the processor:
from stencilizer.config import StencilizerSettings
settings = StencilizerSettings()
settings.bridge.width_percent = 70.0
settings.processing.max_workers = 4Stencilizer analyzes each glyph to identify its contour hierarchy:
- Outer contours: Main glyph shapes (counter-clockwise winding)
- Inner contours: Enclosed areas (clockwise winding)
- Islands: Inner contours that are fully enclosed and need bridges
For each island, the algorithm:
- Analyzes stroke geometry between inner and outer contours
- Determines optimal bridge orientation (vertical or horizontal)
- Calculates bridge width based on stroke dimensions
- Places bridges to connect the island to the outer contour
Bridges are added by cutting notches into both the island and outer contour, creating connection points while preserving the overall glyph structure.
Glyphs are processed in parallel using Python's ProcessPoolExecutor, enabling efficient utilization of multi-core systems.
stencilizer Roboto-Regular.ttf \
--bridge-width 80 \
-o Roboto-Stencil.ttf# Check which glyphs have islands
stencilizer Roboto-Regular.ttf --list-islands
# See what would be done
stencilizer Roboto-Regular.ttf --dry-runDry-run output:
▸ Loading font
Roboto-Regular.ttf (TrueType)
897 glyphs · 2,048 UPM
▸ Analyzing (dry run)
Analysis
Glyphs with islands 42
Total islands 67
Estimated bridges 67
Bridge width 60% of stroke
✓ Dry run complete – no changes made
stencilizer Roboto-Regular.ttf \
--log-file processing.log \
--log-level DEBUG \
--verbose- Python 3.11 or higher
- fonttools >= 4.47.0
- pydantic >= 2.5.0
- rich >= 13.7.0
- structlog >= 24.1.0
- typer >= 0.9.0
# Clone repository
git clone https://github.com/cosmix/stencilizer.git
cd stencilizer
# Install with development dependencies
uv pip install -e ".[dev]"# Run all tests
pytest
# Run with coverage
pytest --cov=stencilizer --cov-report=html
# Run specific test modules
pytest tests/unit/test_analyzer.py# Type checking
mypy src/stencilizer tests
# Linting and formatting
ruff check src tests
ruff format src testsEnsure your font file is a valid TTF file (or OTF with TrueType outlines) and not corrupted. See Font Format Support for details on supported formats.
Some fonts may not have enclosed contours. Use --list-islands to check which glyphs have islands.
Adjust the --bridge-width parameter (range: 30-110% of stroke width). Default is 60%.
Enable detailed logging to diagnose issues:
stencilizer input.ttf --log-file debug.log --log-level DEBUGStencilizer supports the following font formats:
| Format | Extension | Outline Type | Status |
|---|---|---|---|
| TrueType | .ttf |
TrueType (glyf table) |
✅ Fully supported |
| OpenType with TrueType outlines | .otf |
TrueType (glyf table) |
✅ Fully supported |
| OpenType with CFF outlines | .otf |
PostScript (CFF table) |
✅ Fully supported |
| OpenType with CFF2 outlines | .otf |
PostScript (CFF2 table) |
❌ Not supported |
| Variable fonts | .ttf/.otf |
Variable (fvar table) |
❌ Not supported |
Most .ttf files use TrueType outlines and will work. For .otf files, the situation is more nuanced:
- OTF with TrueType outlines: Some foundries package TrueType outlines in an OpenType container. These are fully supported.
- OTF with CFF outlines: Traditional PostScript-based OpenType fonts. These are fully supported.
- OTF with CFF2 outlines: Modern variable OpenType fonts use CFF2. These are not yet supported.
If you're unsure about your font's format, try processing it—Stencilizer will report an error if the format is unsupported.
- OpenType CFF2 font support
- Variable font support (fonts with
fvartable)
MIT License - see the LICENSE file for details.