From 982fb9482801711354f15a2ec36101b04bbec78a Mon Sep 17 00:00:00 2001 From: minjaesong Date: Thu, 26 Feb 2026 10:15:23 +0900 Subject: [PATCH] more devanagari obscurities --- OTFbuild/calligra_font_tests.odt | Bin 14125 -> 14430 bytes OTFbuild/opentype_features.py | 102 +++++++++++++++++++++++++++---- 2 files changed, 91 insertions(+), 11 deletions(-) diff --git a/OTFbuild/calligra_font_tests.odt b/OTFbuild/calligra_font_tests.odt index 9e50ebbf829330f4be8d0b795d5f9dfd8be64c9c..09c4e146ed019ea241868e4c6cee1705ad5cef08 100644 GIT binary patch delta 13214 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 VA1BBBU!nxgL~d%Ra<2av{|`NvLYx2q delta 12902 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 j{LlT*CH!jk{}tE&k!+0rCNgA8V& RYA (uF0106) + ra_int = SC.to_deva_internal(0x0930) + ya_int = SC.to_deva_internal(0x092F) + RYA = 0xF0106 + if has(ra_int) and has(0x200D) and has(SC.DEVANAGARI_VIRAMA) and has(ya_int) and has(RYA): + akhn_subs.append( + f" sub {glyph_name(ra_int)} {glyph_name(0x200D)} {glyph_name(SC.DEVANAGARI_VIRAMA)} {glyph_name(ya_int)} by {glyph_name(RYA)}; # RYA" + ) + ka_int = SC.to_deva_internal(0x0915) ssa_int = SC.to_deva_internal(0x0937) ja_int = SC.to_deva_internal(0x091C) @@ -639,6 +649,12 @@ def _generate_devanagari(glyphs, has, replacewith_subs=None): half_subs.append( f" sub {glyph_name(internal)} {glyph_name(SC.DEVANAGARI_VIRAMA)} by {glyph_name(half_form)};" ) + # RYA (uF0106) has a special half form (uF0107), not at +240 + HALF_RYA = 0xF0107 + if has(RYA) and has(SC.DEVANAGARI_VIRAMA) and has(HALF_RYA): + half_subs.append( + f" sub {glyph_name(RYA)} {glyph_name(SC.DEVANAGARI_VIRAMA)} by {glyph_name(HALF_RYA)};" + ) if half_subs: features.append("feature half {\n script dev2;\n" + '\n'.join(half_subs) + "\n} half;") @@ -787,7 +803,7 @@ def _generate_devanagari(glyphs, has, replacewith_subs=None): abvs_lines.append("} abvs;") features.append('\n'.join(abvs_lines)) - # --- psts: I-matra and II-matra length variants --- + # --- psts: I-matra/II-matra length variants + open Ya --- # Must run AFTER abvs because abvs uses uni093F as context for complex # reph substitution. If I-matra were substituted before abvs, those # contextual rules would break. @@ -795,9 +811,15 @@ def _generate_devanagari(glyphs, has, replacewith_subs=None): # HarfBuzz dev2 feature order: init → pres → abvs → blws → psts → haln # psts has F_GLOBAL_MANUAL_JOINERS masking → applied to ALL glyphs in the # syllable, so it works for both pre-base I-matra and post-base II-matra. - psts_code = _generate_psts_matra_variants(glyphs, has, _conjuncts) - if psts_code: - features.append(psts_code) + matra_lookups, matra_body = _generate_psts_matra_variants(glyphs, has, _conjuncts) + ya_lookups, ya_body = _generate_psts_open_ya(glyphs, has) + all_lookups = matra_lookups + ya_lookups + all_body = matra_body + ya_body + if all_body: + feat = ["feature psts {", " script dev2;"] + feat.extend(all_body) + feat.append("} psts;") + features.append('\n'.join(all_lookups + [''] + feat)) if not features: return "" @@ -1012,15 +1034,73 @@ def _generate_psts_matra_variants(glyphs, has, conjuncts): psts_ii_lines = [] if not psts_i_lines and not psts_ii_lines: - return "" + return [], [] - # Assemble the feature block - feat = ["feature psts {", " script dev2;"] - feat.extend(psts_i_lines) - feat.extend(psts_ii_lines) - feat.append("} psts;") + return lines, psts_i_lines + psts_ii_lines - return '\n'.join(lines + [''] + feat) + +def _generate_psts_open_ya(glyphs, has): + """Generate psts rules for open Ya substitution. + + In the bitmap font, Ya (uF015A) uses an "open" variant (uF0108) when it + follows certain half-form consonants. Half-Ya (uF024A) similarly becomes + open-half-Ya (uF0109). + + Returns (lookup_lines, feature_body_lines) like _generate_psts_matra_variants. + """ + OPEN_YA = 0xF0108 + OPEN_HALF_YA = 0xF0109 + YA_INT = 0xF015A # 0x092F.toDevaInternal() + HALF_YA = YA_INT + 240 # 0xF024A + + if not has(OPEN_YA) or not has(YA_INT): + return [], [] + + # Consonants whose half forms trigger open Ya (from Kotlin ligateIndicConsonants): + # 1. Basic consonants: KA, NGA, CHA, TTA, TTH, DD, DDH + open_ya_full = { + SC.to_deva_internal(cp) + for cp in [0x0915, 0x0919, 0x091B, 0x091F, 0x0920, 0x0921, 0x0922] + } + # 2. D.RA + open_ya_full.add(0xF0331) + # 3. Conjuncts in 0xF01B0..0xF01DF + for cp in range(0xF01B0, 0xF01E0): + if has(cp): + open_ya_full.add(cp) + # 4. RA-appended conjuncts in 0xF0390..0xF03BF + for cp in range(0xF0390, 0xF03C0): + if has(cp): + open_ya_full.add(cp) + + # Collect the HALF forms of all these consonants + open_ya_halfs = set() + for cp in open_ya_full: + half = cp + 240 + if has(half): + open_ya_halfs.add(half) + + if not open_ya_halfs: + return [], [] + + lookups = [] + lookups.append(f"lookup OpenYa {{") + lookups.append(f" sub {glyph_name(YA_INT)} by {glyph_name(OPEN_YA)};") + lookups.append(f"}} OpenYa;") + + if has(OPEN_HALF_YA) and has(HALF_YA): + lookups.append(f"lookup OpenHalfYa {{") + lookups.append(f" sub {glyph_name(HALF_YA)} by {glyph_name(OPEN_HALF_YA)};") + lookups.append(f"}} OpenHalfYa;") + + body = [] + half_names = ' '.join(glyph_name(cp) for cp in sorted(open_ya_halfs)) + body.append(f" @openYaHalfs = [{half_names}];") + body.append(f" sub @openYaHalfs {glyph_name(YA_INT)}' lookup OpenYa;") + if has(OPEN_HALF_YA) and has(HALF_YA): + body.append(f" sub @openYaHalfs {glyph_name(HALF_YA)}' lookup OpenHalfYa;") + + return lookups, body def _generate_tamil(glyphs, has):