Replace hand-tuned kerning with HalfKern auto-kerning#21
Conversation
Use behdad/halfkern's SDF-envelope overlap algorithm to compute principled kern values for all Latin glyph pairs, replacing the manually curated ~220-line kern table. Kerns are injected as GPOS PairPos Format 2 (class-based) for compact representation. - Vendor halfkern as a git submodule at scripts/halfkern - Add scripts/autokern.py wrapping HalfKern for the fix_fonts pipeline - Monkey-patch ctypes.CDLL to resolve Nix store library paths (macOS SIP) - Add pycairo, numpy, scipy, scikit-fmm, cairo, freetype to flake.nix - Cover Basic Latin, Latin-1, Latin Extended-A/B, dashes, quotes, ellipsis, and euro sign (~540 candidate codepoints) - Mono fonts (isFixedPitch) are correctly skipped
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 975d0cf32e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| import cairoft as halfkern_cairoft | ||
| import kern_pair as hk |
There was a problem hiding this comment.
Initialize the HalfKern submodule before importing it
This import assumes scripts/halfkern has been populated, but the change only adds a gitlink; a fresh clone using the current README.md:15 command leaves that directory empty until git submodule update --init is run. In that state the first proportional font processed by add_auto_kerning() hits ModuleNotFoundError: cairoft here and make build exits, so the new dependency needs to be fetched as part of the normal build path.
Useful? React with 👍 / 👎.
| (0x0020, 0x007E), # Basic Latin (space through tilde) | ||
| (0x00A0, 0x00FF), # Latin-1 Supplement (À-ÿ, common accented) | ||
| (0x0100, 0x017F), # Latin Extended-A (Ā-ſ) | ||
| (0x0180, 0x024F), # Latin Extended-B (ƀ-ɏ) |
There was a problem hiding this comment.
Expand
_KERN_RANGES beyond U+024F
Because _candidate_codepoints() only accepts U+0020–024F plus a few punctuation blocks, HalfKern never sees any supported glyph above U+024F. That drops kerning for characters the old tables explicitly handled, including Vietnamese extended Latin (Ạ, Ỳ, etc.) and Greek/Cyrillic forms, so proportional text in those scripts will regress to unkerned spacing after this switch.
Useful? React with 👍 / 👎.
Pillow's default layout engine ignores GPOS kern tables. Switch all ImageFont.truetype calls to use Layout.RAQM so the specimen images reflect the actual kerning. Regenerate images.
The autokern module imports from the vendored halfkern submodule. Without submodules: true, the directory is empty in CI and cairoft fails to import.
…ion) The kern feature was appended to the GPOS FeatureList but not registered in any Script/LangSys FeatureIndex, so HarfBuzz and other shapers never discovered it. Now register the kern feature in every script's DefaultLangSys and all LangSysRecords. Regenerate specimen images — kerning differences now visible.
- negative_only=False: allow loosening kerns for cramped pairs - threshold_units 10→5: capture subtler adjustments - min_kern_units -150→-250: trust HalfKern's SDF for tighter pairs - Add max_kern_units=100 cap for positive kerns
The SDF calibration already targets correct inter-glyph spacing. Halving on top of that just makes kerning less effective.
Switch HalfKern reduce mode from "sum" (averaged overlap) to "max" (worst-case closest approach) for more optically even kerning. Also restrict to negative-only kerns — only tightening, no widening.
Re-enable both tightening and loosening kerns (negative_only=False) to let HalfKern adjust spacing in both directions where needed.
…ng range" This reverts commit 5c1c37d.
Instead of dropping positive kerns entirely, halve their values so pairs that need more space get a gentle nudge rather than the full computed widening. Negative (tightening) kerns remain at full strength.
More conservative loosening — keep only 30% of positive kern values instead of 50%, while negative (tightening) kerns remain at full strength.
- Fix PycairoContext scoping bug in cairoft.py (broke on multi-font runs) - Move Nix library resolution into cairoft.py, remove monkey-patches - Add submodule initialization check in autokern.py - Expand kern ranges: Greek, Cyrillic, Vietnamese, Latin Extended C/D/E
Thin glyphs like . , : ; ! have lots of whitespace so HalfKern computes large positive kerns that widen spacing around them. Skip positive kerns when either side is thin punctuation while still allowing tightening (negative) kerns.
Suppress positive kerns around quote characters (' " ' ' " " etc.)
in addition to existing thin punctuation marks.
No description provided.