Merge pull request #6480 from dotCooCoo/fix/vertical-styles-keep-horizontal-ligatures

Type: vertical-only settings keep horizontal ligatures whole
This commit is contained in:
BFlat
2026-11-11 07:01:45 +07:00
committed by GitHub
2 changed files with 45 additions and 2 deletions
+20 -2
View File
@@ -796,6 +796,23 @@ fn source_run_at(runs: &[(Range<usize>, &CharStyle)], span: &Range<usize>, byte:
source.get(i).filter(|(range, _)| range.contains(&byte)).map(|_| span.start + i)
}
/// Can one glyph draw characters in styles `a` and `b`? Only when the styles are equal, except that
/// horizontal type (`vertical` false) does not read Standard Vertical Roman Alignment or the
/// tate-chu-yoko offsets, so styles that differ only in those are drawn alike there.
fn drawn_alike(a: &CharStyle, b: &CharStyle, vertical: bool) -> bool {
if a == b {
return true;
}
!vertical
&& *a
== CharStyle {
standard_vertical_roman: a.standard_vertical_roman,
tcy_up_down: a.tcy_up_down,
tcy_left_right: a.tcy_left_right,
..b.clone()
}
}
/// Do text runs in styles `a` and `b` shape alike, so that they are shaped as one piece of text?
/// They do when they differ only in paint and stroke, underline and strikethrough, overprint, the
/// character style's name, leading, baseline shift, rotation, Character Alignment, Character
@@ -1275,9 +1292,10 @@ fn shape_segment(text: &str, context: &Range<usize>, seg: &Segment, feats: &OtFe
cluster_starts.dedup();
// A glyph is drawn in one style: where a cluster would take in characters of runs in different
// styles, the runs are shaped apart, as runs that do not shape alike are.
// Except at harakat styled apart only in paint ([`crate::harakat`]).
// Except at harakat styled apart only in paint ([`crate::harakat`]), and, in horizontal type, where
// the runs differ only in settings that vertical type alone reads.
let inside_clusters: Vec<usize> = boundaries()
.filter(|&b| cluster_starts.binary_search(&b).is_err() && run_at(b - 1).1 != run_at(b).1)
.filter(|&b| cluster_starts.binary_search(&b).is_err() && !drawn_alike(run_at(b - 1).1, run_at(b).1, feats.vertical))
.filter(|b| mark_boundaries.binary_search(b).is_err())
.collect();
if !inside_clusters.is_empty() {
+25
View File
@@ -629,6 +629,31 @@ fn an_upright_ligature_takes_its_caret_down_its_cell() {
assert!((at(900) - 0.5).abs() < 1e-9, "{}", at(900));
}
/// Horizontal type does not read Standard Vertical Roman Alignment or the tate-chu-yoko offsets, so
/// setting one of them on the f of "fi" keeps the ligature. Vertical type reads them per glyph, so
/// there the f is shaped apart from the i.
#[test]
fn vertical_only_settings_keep_a_horizontal_ligature_whole() {
let db = db_with(vertical_ligature_caret_font(280));
let marks: [fn(&mut CharStyle); 3] = [|s| s.standard_vertical_roman = true, |s| s.tcy_up_down = 10.0, |s| s.tcy_left_right = 10.0];
for (i, mark) in marks.into_iter().enumerate() {
for vertical in [false, true] {
let mut t = point("fi", VERTICAL_LIGATURE_CARET_FAMILY);
t.vertical = vertical;
let plain = t.runs[0].style.clone();
let mut marked = plain.clone();
mark(&mut marked);
t.runs = vec![
vectorcraft_doc::TextRun { text: "f".into(), style: marked, inline: None },
vectorcraft_doc::TextRun { text: "i".into(), style: plain, inline: None },
];
let l = layout(&db, &t);
let joined = l.glyphs.iter().any(|g| g.len == 2);
assert_eq!(joined, !vertical, "setting {i}, vertical {vertical}: {:?}", l.glyphs);
}
}
}
/// On a path, an upright ligature in vertical type stands with its top toward the path's start, and
/// its cell runs from 0.12 em before the pen to 0.88 em after it, with or without the font's vertical
/// metrics. The inner stop lies at the middle of that cell, and the first character's selection ends