Type: a line holding right-to-left text pushes out

A line with right-to-left text is reordered before it is drawn, and
justification closes the gaps between its glyphs in that order, which
can differ from the gaps the push-in counted. Such a line could be
taken as fitting and then run past its frame. Kinsoku Shori Type now
pushes it out.
This commit is contained in:
dotCooCoo
2026-11-01 08:22:06 -08:00
parent b5cf31950b
commit 932426eaad
2 changed files with 16 additions and 2 deletions
+7 -2
View File
@@ -518,8 +518,8 @@ const PUSH_IN_RUN: usize = 16;
/// before it, makes room for them. Push In First tries this before it pushes out. Push Out First
/// tries it only when pushing out, to the line's last allowed break `pushed_out`, would leave the
/// line short by more than an em of the character before `j`. Push Out Only never pushes in, and
/// neither does a line holding a tab, nor one where the characters taken in reach a space after a
/// character that may not end a line. With `hanging` (Optical Margin Alignment or Roman Hanging
/// neither does a line holding a tab or right-to-left text, nor one where the characters taken in
/// reach a space after a character that may not end a line. With `hanging` (Optical Margin Alignment or Roman Hanging
/// Punctuation), the gaps that hanging punctuation keeps open are not counted.
#[allow(clippy::too_many_arguments)]
fn push_in(
@@ -569,6 +569,11 @@ fn push_in(
while last > i && g[last - 1].is_space() {
last -= 1;
}
// A line holding right-to-left text is reordered before it is drawn, and justification closes
// the gaps between its glyphs in that order: it pushes out.
if g[i..last].iter().any(|h| h.len > 0 && h.level.is_rtl()) {
return None;
}
let punct = |k: usize| g.get(k).is_some_and(optical_punct);
let quote = |k: usize| g.get(k).is_some_and(|h| is_quote(h.ch));
let may_hang = |a: usize| {
+9
View File
@@ -223,4 +223,13 @@ fn kinsoku_shori_type_pushes_a_closing_bracket_in_or_out() {
assert_eq!(end(tabbed, 146.0, Kinsoku::Hard, PushOutOnly), 12);
assert_eq!(end(tabbed, 146.0, Kinsoku::Hard, PushOutFirst), 12);
assert_eq!(end(tabbed, 146.0, Kinsoku::Hard, PushInFirst), 12);
// A line holding right-to-left text is reordered before it is drawn, so the gaps that close
// differ from the ones counted here: it pushes out.
let mixed = "一二三א五六七八九十一二三四五」六";
let mut glyphs = full_width(mixed);
glyphs[3].level = unicode_bidi::Level::rtl();
let mixed_end = |ty: KinsokuShoriType| break_line(mixed, &glyphs, 0, 150.0, &[], 0.0, None, Burasagari::None, Kinsoku::Hard, ty, false).0;
assert_eq!(mixed_end(PushInFirst), 14, "right-to-left text on the line: 五 is pushed out with 」");
assert_eq!(mixed_end(PushOutOnly), 14);
}