diff --git a/crates/edit/src/added.rs b/crates/edit/src/added.rs index a603a37060..250f71a533 100644 --- a/crates/edit/src/added.rs +++ b/crates/edit/src/added.rs @@ -29,6 +29,8 @@ use draw::{GLYPHS_PER_FONT, LaidLine, draw_laid_shaped}; use layout::Piece; #[cfg(test)] pub(crate) use layout::WRAP_CALLS; +#[cfg(test)] +pub(crate) use layout::shaped_width; pub use layout::{PASTE_STEP, first_undrawable, lines, paste_place, paste_rect, text_rect, text_width}; pub use store::{add_content, delete_content, isolate_content, list_added, scan_markers, update_content}; pub use types::{Added, AddedImage, AddedText, Align, CHAR_SPACING_LIMIT, Family, MarkerScan}; diff --git a/crates/edit/src/added/layout.rs b/crates/edit/src/added/layout.rs index 62f24c5c4c..c76db6463a 100644 --- a/crates/edit/src/added/layout.rs +++ b/crates/edit/src/added/layout.rs @@ -107,7 +107,8 @@ fn thai_lines(t: &AddedText, para: &str, width: f64) -> Result, Edit end_row(&mut lines, &mut line); used = 0.0; } - let too_wide = blank(&line) && ink > width; + // Too wide for what is left of the row, counting the spaces that open the paragraph. + let too_wide = blank(&line) && used + ink > width; for (text, advance) in word { if too_wide && !blank(&line) && !blank(&text) && used + advance > width { end_row(&mut lines, &mut line); @@ -319,7 +320,7 @@ pub(super) fn shaped_layout(t: &AddedText, line: &str, rtl: bool) -> Result<(Vec /// The width of `s` in a shaped-text item; the item's font's estimate if it can't be shaped (drawing /// then reports why). -fn shaped_width(t: &AddedText, s: &str, rtl: bool) -> f64 { +pub(crate) fn shaped_width(t: &AddedText, s: &str, rtl: bool) -> f64 { shaped_layout(t, s, rtl).map(|(_, w)| w).unwrap_or_else(|_| latin_width(t, s)) } diff --git a/crates/edit/src/tests.rs b/crates/edit/src/tests.rs index d8ba0a8aeb..02378057b7 100644 --- a/crates/edit/src/tests.rs +++ b/crates/edit/src/tests.rs @@ -5826,6 +5826,19 @@ fn added_thai_text_wraps_between_dictionary_words() { assert!(rows[0].starts_with(" "), "the opening spaces are kept at width {width}: {rows:?}"); assert_eq!(rows.concat().trim_start(), "ประปาสะอาด"); } + // A first word that fits a row alone but not after the opening spaces breaks between clusters + // on the first row rather than running past its end. + let word = "ประปา"; + let word_w = super::added::shaped_width(&t, word, false); + let indent_w = super::added::shaped_width(&t, " ", false); + let width = word_w + indent_w / 2.0; + let indented = AddedText { rect: [0.0, 0.0, width, 400.0], text: format!(" {word}"), ..t.clone() }; + let rows = super::added::lines(&indented); + for row in &rows { + assert!(super::added::shaped_width(&t, row, false) <= width + 0.5, "{row:?} overflows {width}: {rows:?}"); + } + assert!(rows.len() > 1 && rows[0].starts_with(" "), "{rows:?}"); + assert_eq!(rows.concat().trim_start(), word); } #[test]