Glyphs: a consonant's ligature in a cluster with its vowel sign is its glyph

Ka with a nukta and the vowel sign I is shaped as one cluster: the vowel
sign's glyph, then the ligature of Ka and the nukta. The ligature is
neither Ka's default glyph nor an alternate, so the lookup found no
glyph for Ka: text.glyphAlternates reported the vowel sign's glyph and
text.setGlyphAlternate reported Ka's default glyph. When the cluster
holds neither, the lookup now takes the first glyph that is not drawn
for another character of the grapheme.
This commit is contained in:
dotCooCoo
2026-11-05 05:20:03 +07:00
committed by bflatastic
parent 961b28d420
commit 92ecb4a6b0
2 changed files with 39 additions and 3 deletions
+17 -3
View File
@@ -1153,7 +1153,9 @@ fn glyph_target(t: &TextObject, p: &Value, c: &str) -> Result<(char, std::ops::R
/// text; `None` when the character is not laid out (overflowing text). A character that starts a
/// longer grapheme (a letter and its combining marks) is drawn in the grapheme's cluster. Of a
/// cluster of several glyphs (a mark, a vowel sign drawn before the letter), the character's glyph
/// is its default glyph or one of its alternates; `Some(None)` when the cluster holds neither.
/// is its default glyph or one of its alternates, else the first glyph that is not the default glyph
/// or an alternate of another character of the grapheme (a ligature of the letter and a mark);
/// `Some(None)` when the cluster holds none of these.
fn own_glyph(
plain: &str,
lay: &vectorcraft_text::TextLayout,
@@ -1172,8 +1174,20 @@ fn own_glyph(
Some(match cluster[..] {
[one] => Some(one),
_ => {
let (default, listed) = (face.glyph_for(ch), face.alternates(ch));
cluster.into_iter().find(|&g| g == default || listed.iter().any(|a| a.gid == g))
// The glyphs of each character are looked up once: a grapheme can repeat a mark.
let glyphs_of = |c: char| face.alternates(c).iter().map(|a| a.gid).chain([face.glyph_for(c)]).collect::<Vec<u32>>();
let default = face.glyph_for(ch);
let listed = || {
let own = glyphs_of(ch);
cluster.iter().copied().find(|g| own.contains(g))
};
cluster.iter().copied().find(|&g| g == default).or_else(listed).or_else(|| {
let mut others: Vec<char> = plain.get(bytes.end..bytes.start + grapheme).unwrap_or_default().chars().collect();
others.sort_unstable();
others.dedup();
let theirs: Vec<u32> = others.into_iter().flat_map(glyphs_of).collect();
cluster.iter().copied().find(|g| !theirs.contains(g))
})
}
})
}