mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-13 08:29:14 +00:00
Type: Bunri-Kinshi finds a pair by its clusters in right-to-left justification
Single Word Justification adds no space inside an inseparable pair. A right-to-left line draws the second half's cluster before the first half, with the cluster's glyphs in logical order, so a dash followed by a combining mark puts the mark next to the first dash. The pair is now found from the two neighboring clusters instead of neighboring glyph indices.
This commit is contained in:
@@ -743,12 +743,25 @@ fn is_cjk_gap(glyph: usize, held: &[usize], after: &[Option<usize>], index: usiz
|
||||
}) && !in_pair(glyph, index, after, sg)
|
||||
}
|
||||
|
||||
// Bunri-Kinshi: are glyph `glyph` and the glyph drawn after it the two halves of an inseparable
|
||||
// pair? Justification adds no space between them.
|
||||
// Bunri-Kinshi: are glyph `glyph` and the glyph drawn after it in the two halves of an inseparable
|
||||
// pair? Justification adds no space between them. The halves are neighboring clusters: the
|
||||
// earlier one's last glyph keeps the next, and the later one starts right after it. Right to left,
|
||||
// the clusters are drawn in the other order, each with its glyphs in logical order, so the two
|
||||
// drawn neighbors need not be neighbors in `sg`.
|
||||
fn in_pair(glyph: usize, index: usize, after: &[Option<usize>], sg: &[SGlyph]) -> bool {
|
||||
drawn_after(index, glyph, after).is_some_and(|next| {
|
||||
(next == glyph + 1 && sg.get(glyph).is_some_and(|a| a.keeps_next)) || (glyph == next + 1 && sg.get(next).is_some_and(|b| b.keeps_next))
|
||||
})
|
||||
let Some(next) = drawn_after(index, glyph, after) else {
|
||||
return false;
|
||||
};
|
||||
let (Some(a), Some(b)) = (sg.get(glyph), sg.get(next)) else {
|
||||
return false;
|
||||
};
|
||||
if a.byte == b.byte {
|
||||
return false;
|
||||
}
|
||||
let (first, later) = if a.byte < b.byte { (glyph, b.byte) } else { (next, a.byte) };
|
||||
let byte = sg.get(first).map_or(0, |g| g.byte);
|
||||
let last = first + sg.iter().skip(first + 1).take_while(|g| g.byte == byte).count();
|
||||
sg.get(last).is_some_and(|g| g.keeps_next) && sg.get(last + 1).is_some_and(|g| g.byte == later)
|
||||
}
|
||||
|
||||
// Single Word Justification's Full Justify spreads a line's leftover room over the gaps between
|
||||
|
||||
@@ -2631,6 +2631,26 @@ fn bunri_kinshi_adds_no_justification_space_inside_a_pair() {
|
||||
let (on, a) = desired(true);
|
||||
assert!((on - natural).abs() < 1e-6, "on: the dashes keep their spacing");
|
||||
assert!(a > layout(db(), &point(text, style(20.0))).glyphs[0].advance + 1.0, "on: other letters still take Desired spacing");
|
||||
// Single Word Justification's Full Justify, right to left: the second dash's cluster (the dash
|
||||
// and a combining mark) is drawn before the first dash, and the mark's gap, which opens next
|
||||
// to the first dash, takes no space. The other gaps of the word still do.
|
||||
let text = "אב——\u{301}גד";
|
||||
let (first, second) = ("אב".len(), "אב—".len());
|
||||
let width = |l: &TextLayout, b: usize| l.glyphs.iter().filter(|g| g.byte == b).map(|g| g.advance).sum::<f64>();
|
||||
let natural = layout(db(), &point(text, style(20.0)));
|
||||
assert!(natural.glyphs.iter().filter(|g| g.byte == second).count() > 1, "the dash and its mark are one cluster");
|
||||
let set = |bunri: bool| {
|
||||
let mut t = area(text, style(20.0), frame, Justify::JustifyAll);
|
||||
t.para.bunri_kinshi = bunri;
|
||||
let l = layout(db(), &t);
|
||||
assert!((l.lines[0].x1 - frame.x1).abs() < 1e-6, "{bunri}: {:?}", l.lines[0]);
|
||||
(width(&l, first), width(&l, second))
|
||||
};
|
||||
let (_, off) = set(false);
|
||||
assert!(off > width(&natural, second) + 1.0, "off: Full Justify widens the gap between the dashes");
|
||||
let (dash, on) = set(true);
|
||||
assert!((on - width(&natural, second)).abs() < 1e-6, "on: the pair keeps its spacing right to left");
|
||||
assert!(dash > width(&natural, first) + 1.0, "on: the gap on the pair's other side still widens");
|
||||
}
|
||||
|
||||
/// No Break (Character panel menu) keeps the marked words on one line, where the same text wraps
|
||||
|
||||
Reference in New Issue
Block a user