contextual visarga

This commit is contained in:
minjaesong
2026-02-27 08:26:19 +09:00
parent e3a3079fb2
commit 95912acc32
6 changed files with 44 additions and 3 deletions

Binary file not shown.

View File

@@ -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}")

View File

@@ -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)

Binary file not shown.

View File

@@ -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

Binary file not shown.