From 75207dc8ed02ec91bc6bed35574f113b18452aba Mon Sep 17 00:00:00 2001 From: Matej Vrzala Date: Fri, 23 Oct 2026 16:20:35 +0200 Subject: [PATCH] fix(edit): setting a paragraph's width no longer panics on text larger than a page (#4110) rewrite_block clamps a requested paragraph width between the text's size in user space (size * k) and the largest page PDF allows (14 400 pt). f64::clamp panics when its minimum is the larger, so text set above 14 400 pt crashed the app as soon as its paragraph was given a width. The size comes from the file (`20000 Tf`, or `1 Tf` scaled up by `Tm`) or from the request (the automation tools pass `size` through). The lower bound is now capped at 14 400 pt too, so such a paragraph gets the widest width allowed. `f64::min` also turns a NaN size into the cap. Test: a 20000 pt Tf, a 1 pt Tf scaled 20000x by Tm, and a requested size of 20000 pt each take a width of 200 without panicking (all three panicked before). Reported-by: BS.ehlas.cz Co-authored-by: Brandon Thomas --- crates/edit/src/tests.rs | 18 ++++++++++++++++++ crates/edit/src/text.rs | 5 +++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/crates/edit/src/tests.rs b/crates/edit/src/tests.rs index b1c463a6af..aa785024e4 100644 --- a/crates/edit/src/tests.rs +++ b/crates/edit/src/tests.rs @@ -6356,6 +6356,24 @@ fn reading_keeps_dotted_leader_entries_whole_and_in_order() { ); } +/// Text set larger than the widest page PDF allows (14 400 pt) panicked when its paragraph was given +/// a width: the width is clamped between the font size and 14 400 pt, and `f64::clamp` panics when +/// its minimum is the larger. The width is now capped at 14 400 pt whatever the font size: from the +/// file (`Tf`, or a small `Tf` scaled up by `Tm`) or asked for (the automation tools pass it through). +#[test] +fn width_on_a_paragraph_set_larger_than_a_page_is_capped_not_a_panic() { + let wide = text::BlockStyle { width: Some(200.0), ..Default::default() }; + let huge = text::BlockStyle { size: Some(20_000.0), width: Some(200.0), ..Default::default() }; + for (content, style) in [ + ("BT /F2 20000 Tf 72 700 Td (ab) Tj ET", &wide), + ("BT /F2 1 Tf 20000 0 0 20000 72 700 Tm (ab) Tj ET", &wide), + ("BT /F2 12 Tf 72 700 Td (ab) Tj ET", &huge), + ] { + let mut doc = text_page(content); + text::rewrite_block(&mut doc, 0, 0, None, style).unwrap(); + } +} + #[test] fn added_text_is_wrapped_once_per_write() { let mut doc = fixture(); diff --git a/crates/edit/src/text.rs b/crates/edit/src/text.rs index 29ac90abcc..d003935314 100644 --- a/crates/edit/src/text.rs +++ b/crates/edit/src/text.rs @@ -2780,8 +2780,9 @@ fn rewrite_block_from( // A paragraph keeps its width (or takes the one asked for); a single line grows to the right // (to the left when right-to-left), up to the page's margin. let width = match style.width { - // Capped at the largest page PDF allows (14 400 pt). - Some(w) if w.is_finite() => w.clamp(size * k, 14_400.0), + // Capped at the largest page PDF allows (14 400 pt), even for text set larger than that + // (`clamp` panics when its minimum is the larger, or NaN). + Some(w) if w.is_finite() => w.clamp((size * k).min(14_400.0), 14_400.0), Some(_) => return Err(EditError::Invalid("the paragraph's width must be a number".into())), // Turned text: the page's margins are along another axis. A single line grows (below). None if !upright => (frame[2] - frame[0]).max(size * k),