Convert a handwriting specimen sheet into installable font files (OTF/TTF).
- Print the template, write one character per cell with a dark pen.
- Photograph the page with your phone.
- Upload the photo and download your font.
templates/v1/ Printable template (PDF, PNG, B&W reference)
samples/
input/ Filled template photos for testing
output/v1-synthetic/ Example generated font
src/handwrite_font_maker/ Core Python library
web/ HTTP API server (job processing)
web/ Next.js frontend
tests/ Python test suite
scripts/ Utility scripts (FontForge, Gemini)
supabase/migrations/ Database schema
The template has four ArUco corner markers (DICT_4X4_50). The pipeline:
- Detects markers and computes a homography to rectify the photo
- Extracts each cell using the grid geometry
- Cleans guide lines, thresholds to binary, vectorizes with potrace
- Generates OTF/TTF/SFD fonts with FontForge
Detection is robust to: perspective warp, B&W printing, mobile camera noise, uneven lighting, JPEG compression (quality 30), and partial shadow.
Python 3.11+, numpy, Pillow, opencv-python-headless, reportlab.
System: potrace, fontforge.
python3 -m venv .venv && source .venv/bin/activate
pip install -e '.[test]'handwrite-font-maker generate-template --output templates/v1/template.pdfPrint the PDF at 100% scale (no fit-to-page). Keep all four corner markers visible.
handwrite-font-maker build photo.jpg \
--font-name MyHandwriting \
--family-name "My Handwriting" \
--output-dir output/my-handwritingdocker compose up -d # postgres + python api + next.js
open http://localhost:3000 # web UIOr run services individually:
# Terminal 1: Python API
DATABASE_URL=postgresql://handwrite:handwrite@localhost:5433/handwrite_fonts \
LOCAL_OBJECT_ROOT=/tmp/objects PROCESS_JOBS_INLINE=1 \
.venv/bin/python -m handwrite_font_maker.web.server
# Terminal 2: Next.js
cd web && npm run dev -- -p 3001.venv/bin/pytest -q # Python tests (31 tests)
cd web && npm test # Frontend testsEach build produces: <name>.otf, <name>.ttf, <name>.sfd,
rectified-template.png (debug overlay), and work/manifest.json.
- Vercel:
web/Next.js frontend - Render: Python API from
Dockerfile.api - Supabase: Postgres + Storage (or use local Postgres + file storage)