mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-24 02:16:56 -06:00
Merge #1218: M4: justified text doesn't widen U+200B (@Salah1St)
# Conflicts: # crates/text/src/tests.rs
This commit is contained in:
@@ -257,6 +257,11 @@ impl Item {
|
||||
fn is_space(&self) -> bool {
|
||||
crate::breaks::is_space(self.ch)
|
||||
}
|
||||
/// A space justified text widens: not U+200B, which only marks where a line may break (between
|
||||
/// the words of Thai, Lao, Khmer or Burmese text, written without spaces).
|
||||
fn stretches(&self) -> bool {
|
||||
self.is_space() && self.ch != '\u{200B}'
|
||||
}
|
||||
}
|
||||
|
||||
/// Bidi embedding levels per char, and whether each char's paragraph runs right to left.
|
||||
@@ -587,7 +592,7 @@ pub fn layout(text: &TextData, box_width: Option<f64>, box_height: Option<f64>)
|
||||
items.get(vis_end.wrapping_sub(1)).filter(|it| hang_end && !rtl_line && vis_end > *s + 1 && closes(it.ch)).map_or(0.0, |it| it.adv);
|
||||
let gstart = out.glyphs.len();
|
||||
// Justify: extra space per gap.
|
||||
let gaps = items.get(*s..vis_end).unwrap_or(&[]).iter().filter(|it| it.is_space()).count();
|
||||
let gaps = items.get(*s..vis_end).unwrap_or(&[]).iter().filter(|it| it.stretches()).count();
|
||||
let justify_extra = match (text.align_h, wrap) {
|
||||
(TextAlignH::Justified, Some(maxw)) if !para_end && !break_after(*e) && gaps > 0 => {
|
||||
((maxw - line_indent + hang_l + hang_r - width) / gaps as f64).max(0.0)
|
||||
@@ -603,7 +608,7 @@ pub fn layout(text: &TextData, box_width: Option<f64>, box_height: Option<f64>)
|
||||
.unwrap_or(&[])
|
||||
.iter()
|
||||
.filter(|it| !is_line_break(it.ch))
|
||||
.map(|it| it.adv + if it.is_space() { justify_extra } else { 0.0 })
|
||||
.map(|it| it.adv + if it.stretches() { justify_extra } else { 0.0 })
|
||||
.sum();
|
||||
let mut pen = if rtl_line { -trailing } else { line_indent - hang_l };
|
||||
// List marker (before the line's text; zero-length char range so carets skip it).
|
||||
@@ -698,7 +703,7 @@ pub fn layout(text: &TextData, box_width: Option<f64>, box_height: Option<f64>)
|
||||
});
|
||||
}
|
||||
pen += it.adv;
|
||||
if it.is_space() {
|
||||
if it.stretches() {
|
||||
pen += justify_extra;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,3 +519,43 @@ fn lower_case_writes_final_sigma() {
|
||||
t.runs.push(figcraft_doc::StyleRun { start: 3, end: 4, style: other });
|
||||
assert_eq!(gids(&t).iter().map(|g| g.0).collect::<Vec<_>>(), want.iter().map(|g| g.0).collect::<Vec<_>>(), "split runs");
|
||||
}
|
||||
|
||||
/// Thai is written without spaces between words; U+200B ZERO WIDTH SPACE marks where a line may
|
||||
/// break. ภาษา|ไทย|เขียน|ติดกัน ("Thai is written joined up") ไม่มี|ช่องว่าง|ระหว่าง|คำ ("with no
|
||||
/// spaces between words"), with one real space between the two phrases.
|
||||
#[test]
|
||||
fn justified_lines_dont_widen_zero_width_spaces() {
|
||||
if !have_fonts() {
|
||||
return;
|
||||
}
|
||||
let lay_out = |s: &str, align, w| {
|
||||
let mut t = TextData::new(s);
|
||||
t.style.font_size = 20.0;
|
||||
t.align_h = align;
|
||||
t.auto_resize = figcraft_doc::TextAutoResize::Height;
|
||||
layout(&t, w, None)
|
||||
};
|
||||
let chars_before = |s: &str, pat: &str| s.find(pat).map(|b| s[..b].chars().count()).unwrap();
|
||||
for (s, why) in [
|
||||
("ภาษา\u{200B}ไทย\u{200B}เขียน\u{200B}ติดกัน ไม่มี\u{200B}ช่องว่าง\u{200B}ระหว่าง\u{200B}คำ", "the real space takes the extra room"),
|
||||
("ภาษา\u{200B}ไทย\u{200B}เขียน\u{200B}ติดกัน\u{200B}ไม่มี\u{200B}ช่องว่าง\u{200B}ระหว่าง\u{200B}คำ", "no space to widen: as left-aligned"),
|
||||
] {
|
||||
// A box that ends inside ช่องว่าง: the first line breaks at the U+200B before it.
|
||||
let word = chars_before(s, "ช่องว่าง");
|
||||
let natural = lay_out(s, TextAlignH::Left, None);
|
||||
let (a, b) = (natural.caret(word).0, natural.caret(word + 4).0);
|
||||
assert!(b > a, "{why}: chars need advances");
|
||||
let w = a + (b - a) / 2.0;
|
||||
let left = lay_out(s, TextAlignH::Left, Some(w));
|
||||
let just = lay_out(s, TextAlignH::Justified, Some(w));
|
||||
assert!(just.lines.len() >= 2 && just.lines[0].char_end == word, "{why}: {:?}", just.lines);
|
||||
let space = s.chars().position(|c| c == ' ');
|
||||
let extra = w - left.lines[0].width;
|
||||
assert!(extra > 0.0, "{why}");
|
||||
assert_eq!(left.glyphs.len(), just.glyphs.len());
|
||||
for (l, j) in left.glyphs.iter().zip(&just.glyphs).filter(|(g, _)| g.char_end <= word) {
|
||||
let want = if space.is_some_and(|sp| l.char_start > sp) { extra } else { 0.0 };
|
||||
assert!((j.x - l.x - want).abs() < 1e-6, "{why}: char {} moved {} (want {want})", l.char_start, j.x - l.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user