From 95912acc3261208c7a8d197fc460cb97ffe98fb8 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Fri, 27 Feb 2026 08:26:19 +0900 Subject: [PATCH] contextual visarga --- OTFbuild/calligra_font_tests.odt | Bin 14878 -> 14920 bytes OTFbuild/font_builder.py | 3 ++ OTFbuild/opentype_features.py | 32 ++++++++++++++++++ src/assets/devanagari_variable.tga | 2 +- .../gdx/TerrarumSansBitmap.kt | 6 ++++ work_files/devanagari_variable.psd | 4 +-- 6 files changed, 44 insertions(+), 3 deletions(-) diff --git a/OTFbuild/calligra_font_tests.odt b/OTFbuild/calligra_font_tests.odt index 6e7f6bac9ce2538a8be31df9a667b39ff95839c9..c73d93446b54b7ce367a82b34094819cbbc972be 100644 GIT binary patch delta 7979 zcmZv>Wl$VU)GUk!clQK$3+}eKJ6T+UyX&F}65QPvcXtac?(WVK2oMNv_sRXF?pt;H z Wzb*5_O$LZ?M^BDC&S66~Zz=!zCKw0< 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 delta 7967 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 bNs@3bi2mPuUIobjY6&Dl42ZS@|AGDo@#Zq8 diff --git a/OTFbuild/font_builder.py b/OTFbuild/font_builder.py index 83c0d26..96ba4b5 100644 --- a/OTFbuild/font_builder.py +++ b/OTFbuild/font_builder.py @@ -371,6 +371,9 @@ def build_font(assets_dir, output_path, no_bitmap=False, no_features=False): try: fea_stream = io.StringIO(fea_code) addOpenTypeFeatures(font, fea_stream) + # Obtain raw .fea text for debugging + # with open("debugout_features.fea", "w") as text_file: + # text_file.write(fea_code) print(" Features compiled successfully") except Exception as e: print(f" [WARNING] Feature compilation failed: {e}") diff --git a/OTFbuild/opentype_features.py b/OTFbuild/opentype_features.py index 9a69bf0..988db62 100644 --- a/OTFbuild/opentype_features.py +++ b/OTFbuild/opentype_features.py @@ -856,6 +856,38 @@ def _generate_devanagari(glyphs, has, replacewith_subs=None): feat.append("} psts;") features.append('\n'.join(all_lookups + [''] + feat)) + # --- calt: contextual Visarga --- + # When Visarga (U+0903) is followed by a Devanagari character (mid-word), + # substitute with interword variant (uF010A). calt is applied globally + # across syllable boundaries, unlike psts which is per-syllable. + INTERWORD_VISARGA = 0xF010A + visarga = 0x0903 + if has(visarga) and has(INTERWORD_VISARGA): + # Build class of Devanagari characters that can follow Visarga mid-word: + # PUA consonants (full, half, RA-appended, RA-appended half) + independent vowels + deva_following_cps = ( + list(SC.DEVANAGARI_PRESENTATION_CONSONANTS) + + list(range(0xF0230, 0xF0320)) + # half forms + list(SC.DEVANAGARI_PRESENTATION_CONSONANTS_WITH_RA) + + list(range(0xF0410, 0xF0500)) + # RA-appended half forms + list(range(0x0904, 0x0915)) + # independent vowels + list(range(0x0915, 0x093A)) + # Unicode consonants (before ccmp) + list(range(0x0958, 0x0962)) # nukta consonants + ) + deva_following = [glyph_name(cp) for cp in sorted(set(deva_following_cps)) if has(cp)] + if deva_following: + calt_lines = [] + calt_lines.append(f"lookup InterwordVisarga {{") + calt_lines.append(f" sub {glyph_name(visarga)} by {glyph_name(INTERWORD_VISARGA)};") + calt_lines.append(f"}} InterwordVisarga;") + calt_lines.append("") + calt_lines.append("feature calt {") + calt_lines.append(" script dev2;") + calt_lines.append(f" @devaFollowing = [{' '.join(deva_following)}];") + calt_lines.append(f" sub {glyph_name(visarga)}' lookup InterwordVisarga @devaFollowing;") + calt_lines.append("} calt;") + features.append('\n'.join(calt_lines)) + if not features: return "" return '\n\n'.join(features) diff --git a/src/assets/devanagari_variable.tga b/src/assets/devanagari_variable.tga index 6262823..400d298 100644 --- a/src/assets/devanagari_variable.tga +++ b/src/assets/devanagari_variable.tga @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d0e62cb837710cb1838824703a77cbb92134486d41b159a91e532d499856d05f +oid sha256:d5f248770ad01d237c5c64a1a6ea03f872d5f93e5b0c2d78a3904a9a4652ad08 size 1474578 diff --git a/src/net/torvald/terrarumsansbitmap/gdx/TerrarumSansBitmap.kt b/src/net/torvald/terrarumsansbitmap/gdx/TerrarumSansBitmap.kt index 440684c..05c13b9 100755 --- a/src/net/torvald/terrarumsansbitmap/gdx/TerrarumSansBitmap.kt +++ b/src/net/torvald/terrarumsansbitmap/gdx/TerrarumSansBitmap.kt @@ -1401,6 +1401,10 @@ class TerrarumSansBitmap( else { seq0.add(c.toDevaInternal()) } + // Contextual Visarga: use interword variant when followed by a Devanagari character + else if (c == 0x0903 && (cNext in 0x0904..0x0939 || cNext in 0x0958..0x097F)) { + seq0.add(DEVANAGARI_INTERWORD_VISARGA) + } // re-order Sundanese diacritics else if ((c == 0x1BA1 || c == 0x1BA2) && cNext == 0x1BA5) { seq0.add(cNext); seq0.add(c); i += 1 @@ -2744,6 +2748,8 @@ class TerrarumSansBitmap( private const val DEVANAGARI_EYELASH_RA = 0xF010B private const val DEVANAGARI_RA_SUPER = 0xF010C private const val DEVANAGARI_RA_SUPER_COMPLEX = 0xF010D + private const val DEVANAGARI_INTERWORD_VISARGA = 0xF010A + private const val MARWARI_DD = 0x978 diff --git a/work_files/devanagari_variable.psd b/work_files/devanagari_variable.psd index d19aa46..784bd57 100644 --- a/work_files/devanagari_variable.psd +++ b/work_files/devanagari_variable.psd @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3d0fbfb962df918b2316e2077d90cb0c1e34eace4dace9922370b174fc1d422 -size 1453551 +oid sha256:4201b49f4f4ef1195d295abf0d13297f305625aab867e2624e268bae6760aca4 +size 1453583