M7: review follow-up, opening spaces are not a Thai row of their own

A Thai Add text paragraph that opens with spaces held a row of only those
spaces. When the first word did not fit after them, that row was ended
(trimmed to an empty row) and the opening spaces were lost. A row of only
spaces now counts as empty when deciding to end it, so the first word stays
on the first row after the spaces, or breaks between clusters there.

Reported by the CodeRabbit review on #3492; the new case fails without the
fix (at 16 pt the rows were "", "ป", "ระ", ...).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018cECVMvRnc9mHNFfVp7nXd
This commit is contained in:
DewandClaude Opus 5.5 authored and Rutthawit Chuennuawapan committed 2026-11-03 20:24:05 +07:00
1 parent d2723b64fa
commit fb6830c4dd
2 files changed
+14 -4

No files matched your search

+4 -3
View File
@@ -102,13 +102,14 @@ fn thai_lines(t: &AddedText, para: &str, width: f64) -> Result<Vec<String>, Edit
for word in words {
// The word's own width: the spaces after it may hang past the row's end.
let ink: f64 = word.iter().filter(|(s, _)| !blank(s)).map(|(_, w)| w).sum();
if !line.is_empty() && used + ink > width {
// A row holding only the spaces that open the paragraph has nothing to end yet.
if !blank(&line) && used + ink > width {
end_row(&mut lines, &mut line);
used = 0.0;
}
let too_wide = line.is_empty() && ink > width;
let too_wide = blank(&line) && ink > width;
for (text, advance) in word {
if too_wide && !line.is_empty() && !blank(&text) && used + advance > width {
if too_wide && !blank(&line) && !blank(&text) && used + advance > width {
end_row(&mut lines, &mut line);
used = 0.0;
}
+10 -1
View File
@@ -5815,8 +5815,17 @@ fn added_thai_text_wraps_between_dictionary_words() {
let rows = super::added::lines(&spaced);
assert!(rows.iter().all(|r| !r.starts_with(' ') && !r.ends_with(' ')), "{rows:?}");
assert_eq!(rows.concat(), "น้ำประปาสะอาดปลอดภัย");
let narrow = AddedText { rect: [0.0, 0.0, 16.0, 200.0], text: "กิ่กิ่กิ่".into(), ..t };
let narrow = AddedText { rect: [0.0, 0.0, 16.0, 200.0], text: "กิ่กิ่กิ่".into(), ..t.clone() };
assert_eq!(super::added::lines(&narrow), ["กิ่", "กิ่", "กิ่"]);
// Spaces that open a paragraph are kept, but they are not a row of their own: a first word
// too wide to fit after them stays on the first row (or breaks between clusters there).
for width in [16.0, 40.0, 60.0, 90.0] {
let indented = AddedText { rect: [0.0, 0.0, width, 400.0], text: " ประปาสะอาด".into(), ..t.clone() };
let rows = super::added::lines(&indented);
assert!(rows.iter().all(|r| !r.trim().is_empty()), "an empty row at width {width}: {rows:?}");
assert!(rows[0].starts_with(" "), "the opening spaces are kept at width {width}: {rows:?}");
assert_eq!(rows.concat().trim_start(), "ประปาสะอาด");
}
}
#[test]