M12: spaces alone stay on an XFA text line

A word of spaces alone draws nothing, but after a word whose own
trailing space ran past the box it failed the fit test and opened a
blank line, so the text measured a line taller than it draws. Such
words now always stay on the line, keeping their width.
This commit is contained in:
Eric Kaden
2026-10-19 14:53:28 -04:00
parent b2f40e0442
commit 6dc8ad933b
2 changed files with 9 additions and 2 deletions
+4
View File
@@ -2097,6 +2097,10 @@ fn a_space_at_the_end_of_a_line_does_not_count_when_fitting_it() {
let spans = draw_spans(&one_draw("a. DATE ", w, "left"));
let baselines: std::collections::BTreeSet<i64> = spans.iter().map(|s| (s.baseline * 100.0).round() as i64).collect();
assert_eq!(baselines.len(), 1, "{spans:?}");
// Nor do they add a blank line: the text measures as tall as without them.
let face = text::Face::of(&Font { typeface: "Arial".into(), size: 10.0, ..Font::default() });
let height = |t: &str| text::measure_height(&text::plain(t), w, &Para::default(), &face, &|_| String::new());
assert_eq!(height("a. DATE "), height("a. DATE"));
// Text that is wider than the box still wraps.
let spans = draw_spans(&one_draw("a. DATES ", w, "left"));
let baselines: std::collections::BTreeSet<i64> = spans.iter().map(|s| (s.baseline * 100.0).round() as i64).collect();
+5 -2
View File
@@ -204,9 +204,12 @@ pub fn layout(rich: &RichText, rect: Rect, para: &Para, base: &Face, embed: &dyn
let mut first_in_para = true;
let indent = |first: bool| if first { para.text_indent.max(0.0) } else { 0.0 };
for w in ws {
// A word fits when what it draws does: the spaces after it would end the line.
// A word fits when what it draws does: the spaces after it would end the line. Spaces
// alone draw nothing, so they always stay on the line (keeping their width, so a word
// after them still starts past them).
let ink = ink_width(&w);
let fits = line.width + ink <= avail - indent(line.first) || line.words.is_empty() && ink <= avail - indent(line.first);
let blank = w.text.trim_end_matches(' ').is_empty();
let fits = blank || line.width + ink <= avail - indent(line.first) || line.words.is_empty() && ink <= avail - indent(line.first);
if !fits && !line.words.is_empty() {
let done = std::mem::replace(&mut line, Line { words: Vec::new(), width: 0.0, size: base.size, first: false });
lines.push((done, p.h_align));