From ffa08fa00d664898222ba63fd04ffc45703fe634 Mon Sep 17 00:00:00 2001 From: KenM76 Date: Fri, 30 Oct 2026 03:37:21 -0400 Subject: [PATCH] fix(edit): reach one line above the paragraph's top (#3973) A larger range on the first line of a multi-line paragraph rises from the first line's baseline, not from the old box's bottom, so the reach now extends one pitch above the moved box's top. Name the 1.2 em line floor. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9 --- crates/edit/src/place.rs | 9 ++------- crates/edit/src/tests.rs | 6 +++--- crates/edit/src/text_regions/styled.rs | 3 ++- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/crates/edit/src/place.rs b/crates/edit/src/place.rs index 0d2f138a41..b55e71a6ff 100644 --- a/crates/edit/src/place.rs +++ b/crates/edit/src/place.rs @@ -375,7 +375,7 @@ pub(crate) struct Reach { impl Reach { /// The old box and the moved one, grown to `width` either way (alignment may grow a line left - /// or right), down to `rows` lines and up to one `pitch` above its bottom (a larger size); turned text grows by the lines' diagonal every way. + /// or right), down to `rows` lines and up one `pitch` above its top (a larger size on the first line, however many lines the old box had); turned text grows by the lines' diagonal every way. /// Unbounded when a number is not finite. pub(crate) fn bounds(&self) -> [f64; 4] { const UNBOUNDED: [f64; 4] = [f64::NEG_INFINITY, f64::NEG_INFINITY, f64::INFINITY, f64::INFINITY]; @@ -387,12 +387,7 @@ impl Reach { let rows = f64::from(u32::try_from(self.rows).unwrap_or(u32::MAX)); let (width, height) = (self.width.abs(), self.pitch.abs() * rows); let grown = if self.upright { - [ - moved[0].min(moved[2] - width), - moved[1].min(moved[3] - height), - moved[2].max(moved[0] + width), - moved[3].max(moved[1] + self.pitch.abs()), - ] + [moved[0].min(moved[2] - width), moved[1].min(moved[3] - height), moved[2].max(moved[0] + width), moved[3] + self.pitch.abs()] } else { let d = width.hypot(height); [moved[0] - d, moved[1] - d, moved[2] + d, moved[3] + d] diff --git a/crates/edit/src/tests.rs b/crates/edit/src/tests.rs index dead2dcef0..7ee78e882b 100644 --- a/crates/edit/src/tests.rs +++ b/crates/edit/src/tests.rs @@ -4622,13 +4622,13 @@ fn an_ocr_rewrite_goes_on_top_of_paint_over_where_it_lands() { #[test] fn a_rewrites_reach_covers_its_move_width_and_lines() { let reach = |offset, width, rows, upright| crate::Reach { rect: [10.0, 90.0, 30.0, 100.0], offset, width, rows, pitch: 10.0, upright }.bounds(); - assert_eq!(reach([0.0, 0.0], 20.0, 1, true), [10.0, 90.0, 30.0, 100.0]); + assert_eq!(reach([0.0, 0.0], 20.0, 1, true), [10.0, 90.0, 30.0, 110.0]); assert_eq!(reach([5.0, -50.0], 20.0, 1, true), [10.0, 40.0, 35.0, 100.0]); - assert_eq!(reach([0.0, 0.0], 50.0, 3, true), [-20.0, 70.0, 60.0, 100.0]); + assert_eq!(reach([0.0, 0.0], 50.0, 3, true), [-20.0, 70.0, 60.0, 110.0]); assert_eq!(reach([0.0, 0.0], 30.0, 4, false), [-40.0, 40.0, 80.0, 150.0]); assert_eq!(reach([f64::NAN, 0.0], 20.0, 1, true)[0], f64::NEG_INFINITY); let larger = crate::Reach { rect: [10.0, 90.0, 30.0, 100.0], offset: [0.0, 0.0], width: 20.0, rows: 1, pitch: 24.0, upright: true }; - assert_eq!(larger.bounds(), [10.0, 76.0, 30.0, 114.0], "a larger size grows the line up as well as down"); + assert_eq!(larger.bounds(), [10.0, 76.0, 30.0, 124.0], "a larger size grows the line up as well as down"); } /// Revealed OCR text may have to go on top of the page; a page whose marked content nests too deep diff --git a/crates/edit/src/text_regions/styled.rs b/crates/edit/src/text_regions/styled.rs index 7fe7a6a066..943bece7af 100644 --- a/crates/edit/src/text_regions/styled.rs +++ b/crates/edit/src/text_regions/styled.rs @@ -825,7 +825,8 @@ pub(crate) fn rewrite_styled_block( let largest = block.lines.iter().filter_map(|i| lines.get(*i)).map(|l| l.size).chain(style.ranges.iter().filter_map(|r| r.size)).fold(first.size, f64::max); // At least one line of the largest glyphs, so a larger range reaches up as far as it draws. - let pitch = source_leading.max(row_step(style, largest, source_leading)).max(largest * 1.2); + const LINE_EM: f64 = 1.2; + let pitch = source_leading.max(row_step(style, largest, source_leading)).max(largest * LINE_EM); let widest = measured.iter().copied().fold(width, f64::max); // This path lays rows out along user-space baselines, so the paragraph is upright here. let reach = crate::Reach { rect: block.rect, offset: [dx, dy], width: widest, rows: rows.len(), pitch, upright: true };