Something went wrong. Try again.
A warm humanist programming family in eighteen real faces, with proud operator gestures, controllable alternates, a 620-unit terminal grid,
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184"""Programming-feature inventory and deterministic OpenType lookup organization."""
from __future__ import annotations
import hashlibimport jsonfrom dataclasses import dataclassfrom pathlib import Path
from fontTools.ttLib import TTFont
from font_project import CELL_WIDTH
LIGATURE_GROUPS = { "ss07": { "name": "Arrows and pipes", "description": "Extended arrows, pipelines, and directional operators.", "sample": "<--> |-> |> >=> <*>", }, "ss08": { "name": "Comments and tags", "description": "Comment delimiters, reader marks, and markup or template tags.", "sample": "// /* */ <!-- </> #?@", }, "ss09": { "name": "Operators and pairs", "description": "Ranges, repeated and proof operators, and compact delimiter pairs.", "sample": "... ??= ++ =!= () [] {}", },}
@dataclass(frozen=True, slots=True)class Ligature: feature: str sequence: str shape: str glyph_name: str group: str | None
def _ligature_glyph_name(sequence: str) -> str: """Keep readable names when they fit the legacy 31-character limit.""" readable = "code_liga_" + "_".join( f"{ord(character):04X}" for character in sequence ) if len(readable) <= 31: return readable digest = hashlib.sha256(sequence.encode("utf-8")).hexdigest()[:16] return f"code_liga_{digest}"
def load_ligatures(path: Path) -> list[Ligature]: data = json.loads(path.read_text(encoding="utf-8")) result: list[Ligature] = [] for feature in ("calt", "dlig"): for item in data.get(feature, []): sequence = item["sequence"] if len(sequence) < 2: raise ValueError(f"ligature sequence must have 2+ characters: {sequence!r}") group = item.get("group") if (feature == "dlig" and group not in LIGATURE_GROUPS) or ( feature == "calt" and group is not None ): raise ValueError(f"invalid {feature} ligature group for {sequence!r}: {group!r}") result.append( Ligature( feature=feature, sequence=sequence, shape=item.get("shape", "compose"), glyph_name=_ligature_glyph_name(sequence), group=group, ) ) sequences = [lig.sequence for lig in result] if len(sequences) != len(set(sequences)): raise ValueError("duplicate ligature sequence in manifest") glyph_names = [lig.glyph_name for lig in result] if len(glyph_names) != len(set(glyph_names)): raise ValueError("generated ligature glyph-name collision") supported_shapes = { "compose", "arrow-right", "arrow-left", "arrow-both", "arrow-right-double", "arrow-left-double", "arrow-both-double", "member-arrow", "raised-dot", "separated", "wedge-up", "wedge-down", "pair", } unknown_shapes = sorted({lig.shape for lig in result} - supported_shapes) if unknown_shapes: raise ValueError("unsupported ligature shape(s): " + ", ".join(unknown_shapes)) return result
def feature_source( font: TTFont, ligatures: list[Ligature], code_forms_source: str,) -> str: """Share guarded lookups between dlig and its independently enabled subsets.
Lookup order, not feature-tag order, establishes longest-match precedence. Every longer declared token fences its substrings even when its own group is disabled: a selected group must never nibble an unselected token. """ cmap = font.getBestCmap() inputs: dict[str, list[str]] = {} for ligature in ligatures: try: inputs[ligature.sequence] = [cmap[ord(c)] for c in ligature.sequence] except KeyError as exc: raise ValueError(f"missing input glyph in {ligature.sequence!r}") from exc
def blockers(ligature: Ligature) -> list[str]: contexts: set[str] = set() needle = ligature.sequence for sequence, glyphs in inputs.items(): if len(sequence) <= len(needle): continue start = sequence.find(needle) while start >= 0: end = start + len(needle) contexts.add(" ".join( f"{name}'" if start <= index < end else name for index, name in enumerate(glyphs) )) start = sequence.find(needle, start + 1) return sorted(contexts)
lines = [ "languagesystem DFLT dflt;", "languagesystem latn dflt;", code_forms_source, # Valley's side caron has anchors but no inherited substitution path. "feature ccmp {", " sub [L d l t] uni030C' by uni030C.alt;", "} ccmp;", ] ordered = sorted( ligatures, key=lambda lig: ( lig.feature != "dlig", len(set(lig.sequence)) == 1, -len(lig.sequence), lig.sequence, ), ) lookup_names: dict[str, str] = {} for ligature in ordered: name = "token_" + ligature.glyph_name.removeprefix("code_liga_") lookup_names[ligature.sequence] = name marked = " ".join(f"{glyph}'" for glyph in inputs[ligature.sequence]) lines.append(f"lookup {name} {{") lines.extend(f" ignore sub {context};" for context in blockers(ligature)) if len(set(ligature.sequence)) == 1: neighbor = inputs[ligature.sequence][0] lines.extend([ f" ignore sub {marked} {neighbor};", f" ignore sub {neighbor} {marked};", ]) lines.extend([ f" sub {marked} by {ligature.glyph_name};", f"}} {name};", ])
for tag in ("dlig", "calt", *LIGATURE_GROUPS): lines.append(f"feature {tag} {{") if tag in LIGATURE_GROUPS: lines.extend([ " featureNames {", f' name 3 1 0x0409 "{LIGATURE_GROUPS[tag]["name"]}";', " };", ]) lines.extend( f" lookup {lookup_names[lig.sequence]};" for lig in ordered if lig.feature == tag or lig.group == tag ) lines.append(f"}} {tag};") lines.append("table GDEF {") for ligature in ligatures: carets = " ".join(str(i * CELL_WIDTH) for i in range(1, len(ligature.sequence))) lines.append(f" LigatureCaretByPos {ligature.glyph_name} {carets};") lines.append("} GDEF;") return "\n".join(lines)