From 3e79181aa306536d336d12542a6f3f5fe124bbf4 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Mon, 2 Mar 2026 21:02:55 +0900 Subject: [PATCH] somewhat working in CoreText, not at all in DirectWrite --- OTFbuild/CLAUDE.md | 58 +++++++++++++++++++++++++++++- OTFbuild/font_builder.py | 5 +-- OTFbuild/opentype_features.py | 66 ++++++++++++++++++++++++---------- demo.PNG | Bin 171064 -> 171031 bytes 4 files changed, 108 insertions(+), 21 deletions(-) diff --git a/OTFbuild/CLAUDE.md b/OTFbuild/CLAUDE.md index 2a016b4..be2e38f 100644 --- a/OTFbuild/CLAUDE.md +++ b/OTFbuild/CLAUDE.md @@ -118,7 +118,7 @@ print(f"{name}: advance={w}, has_outlines={has_outlines}") ### OpenType features generated (`opentype_features.py`) -- **ccmp** — replacewith expansions (DFLT); consonant-to-PUA mapping + vowel decompositions (dev2) +- **ccmp** — replacewith expansions (DFLT); consonant-to-PUA mapping + vowel decompositions + anusvara upper (dev2); vowel decompositions (tml2) - **kern** — pair positioning from `keming_machine.py` - **liga** — Latin ligatures (ff, fi, fl, ffi, ffl, st) and Armenian ligatures - **locl** — Bulgarian/Serbian Cyrillic alternates @@ -127,6 +127,7 @@ print(f"{name}: advance={w}, has_outlines={has_outlines}") - **pres** (sund) — Sundanese diacritic combinations - **ljmo, vjmo, tjmo** — Hangul jamo positional variants - **mark** — GPOS mark-to-base diacritics positioning +- **mkmk** — GPOS mark-to-mark diacritics stacking (successive marks shift by H_DIACRITICS) ### Devanagari PUA mapping @@ -145,3 +146,58 @@ Mapping formula: `to_deva_internal(c)` = `c - 0x0915 + 0xF0140` for U+0915-0939. ### Script tag gotcha When a script-specific feature exists in GSUB (e.g. `ccmp` under `dev2`), HarfBuzz uses **only** the script-specific lookups and does **not** fall back to the DFLT script's lookups for that feature. Any substitutions needed for a specific script must be registered under that script's tag. + +### languagesystem and language records + +The `languagesystem` declarations in the preamble control which script/language records are created in the font tables. Key rules: + +- `languagesystem` declarations must be at the **top level** of the feature file, not inside any `feature` block. Putting them inside `feature aalt { }` is invalid feaLib syntax and causes silent compilation failure. +- When a language-specific record exists (e.g. `dev2/MAR` from `languagesystem dev2 MAR;`), features registered under `script dev2;` only populate `dev2/dflt` — they are **not** automatically copied to `dev2/MAR`. The language record inherits only from DFLT, resulting in incomplete feature sets. +- Only declare language-specific records when you have `locl` or other language-differentiated features. Otherwise, use only `languagesystem