From 61632e489931e90abc5685874d95862e94cb85fb Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Fri, 16 Oct 2026 13:09:02 +0000 Subject: [PATCH] Treat CRLF as a single line break in fonts::wrap Found while testing crates/fonts/src/lib.rs:79 (wrap_with: `text.split(['\n', '\r'])`) Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Sonnet 5.5 Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Sonnet 5.5 --- crates/fonts/src/lib.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/crates/fonts/src/lib.rs b/crates/fonts/src/lib.rs index a1f2ff2779..13edd5c298 100644 --- a/crates/fonts/src/lib.rs +++ b/crates/fonts/src/lib.rs @@ -69,7 +69,7 @@ fn is_wide(c: char) -> bool { ) } -/// Greedy line breaking within `width` points (paragraphs split on newlines; words longer +/// Greedy line breaking within `width` points (paragraphs split on line breaks, where CRLF counts as one; words longer /// than a line are broken by character). pub fn wrap(text: &str, size: f64, width: f64) -> Vec { wrap_with(text, size, width, helvetica_width) @@ -78,7 +78,8 @@ pub fn wrap(text: &str, size: f64, width: f64) -> Vec { /// [`wrap`], measuring with `measure` (for example [`cjk_width`]). pub fn wrap_with(text: &str, size: f64, width: f64, measure: impl Fn(&str, f64) -> f64) -> Vec { let mut lines = Vec::new(); - for para in text.split(['\n', '\r']) { + // A CRLF pair is one break, a lone CR or LF is one break each. + for para in text.replace("\r\n", "\n").split(['\n', '\r']) { let mut line = String::new(); // No word is on the line yet; an empty word (a leading space) still starts it. let mut fresh = true; @@ -277,6 +278,15 @@ mod tests { assert_eq!(broken, wrap(" abcdefghijklmnopqrstuvwxyz", 11.0, 40.0)); } + #[test] + fn crlf_is_one_line_break() { + assert_eq!(wrap("a\r\nb", 12.0, 100.0), ["a", "b"]); + assert_eq!(wrap("a\rb", 12.0, 100.0), ["a", "b"]); + assert_eq!(wrap("a\nb", 12.0, 100.0), ["a", "b"]); + assert_eq!(wrap("a\r\n\r\nb", 12.0, 100.0), ["a", "", "b"]); + assert_eq!(wrap("a\n\rb", 12.0, 100.0), ["a", "", "b"]); + } + #[test] fn win_ansi_encodability_matches_the_encoder() { for c in ['a', 'é', 'ÿ', '€', '—', '™', '\t', '?'] {