From a46dff6ea742288c862d461e22024dd9361bf215 Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Sun, 15 Mar 2026 16:54:58 -0700 Subject: [PATCH 1/2] fix: allow punctuation for gsub context The exclusion list prevents patterns like "sequence!". What we really want to avoid triggering the ligature when the sequence is a part of digit or substring. --- README.md | 5 +++-- main.py | 12 +++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 2065318..4a50aed 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,9 @@ Injects a PNG logo into a TTF font as a ligature. Type a trigger sequence in any ligature-aware tool and the logo renders in place of the text. The -replacement is context aware and will only trigger when the sequence is -surrounded by whitespace. +replacement is context aware and will only trigger when the sequence is not +part of another number or string. Punctuation and whitespace may precede or +follow the sequence. ``` uv run font-lig --font input.ttf --logo cool.png --out output.ttf --sequence "cool" --family-name "Font + Cool" diff --git a/main.py b/main.py index 24757f7..dd07ae3 100644 --- a/main.py +++ b/main.py @@ -283,15 +283,13 @@ def get_or_create_marker_glyph(font: TTFont, base_glyph: str) -> str: return marker -def non_whitespace_glyphs(font: TTFont) -> list[str]: +def alphanumeric_glyphs(font: TTFont) -> list[str]: """ - Return glyph names for all non-whitespace characters in the font. - The ligature fires only when surrounded by whitespace or at a run boundary. - - TODO: Are there other boundaries that may "trick" us? + Return glyph names for all alphanumeric characters in the font. + The ligature is suppressed when preceded/followed by a letter or digit. """ cmap = font.getBestCmap() or {} - return [name for c, name in cmap.items() if not chr(c).isspace()] + return [name for c, name in cmap.items() if chr(c).isalnum()] def make_lig(glyph_name: str, components: list[str]) -> ot.Ligature: @@ -393,7 +391,7 @@ def add_ligature( first_glyph = glyph_seq[0] if context: - exclusion_glyphs = non_whitespace_glyphs(font) + exclusion_glyphs = alphanumeric_glyphs(font) log.debug("context exclusion glyphs: %s", exclusion_glyphs) if exclusion_glyphs: From 6bd8b3d7a38a2d0e2dfc12a6ea608cba9facfbf1 Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Sun, 15 Mar 2026 16:58:00 -0700 Subject: [PATCH 2/2] docs: add example output --- .gitattributes | 1 + README.md | 4 ++++ examples/cool.png | Bin 0 -> 16115 bytes 3 files changed, 5 insertions(+) create mode 100644 .gitattributes create mode 100644 examples/cool.png diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..947f120 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.png binary diff --git a/README.md b/README.md index 4a50aed..90855ff 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,12 @@ follow the sequence. ``` uv run font-lig --font input.ttf --logo cool.png --out output.ttf --sequence "cool" --family-name "Font + Cool" + +echo "Fonts are cool!" ``` +![example](./examples/fonts_are_cool.png) + ## Existing Tools There are lots of ways to make your own font. I didn't see anything obvious and single-purpose to achieve this diff --git a/examples/cool.png b/examples/cool.png new file mode 100644 index 0000000000000000000000000000000000000000..398b5a46cacc5e4564c98f386dc7f2001310a485 GIT binary patch literal 16115 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 literal 0 HcmV?d00001