diff --git a/crates/text/src/layout.rs b/crates/text/src/layout.rs index 05cc4beb..94a0b5f2 100644 --- a/crates/text/src/layout.rs +++ b/crates/text/src/layout.rs @@ -93,12 +93,24 @@ impl BoxGeom { } else { text_rect }; - let inner = Rect::new( - r.x0 + bp.inset_l.unwrap_or(7.2), - r.y0 + bp.inset_t.unwrap_or(3.6), - (r.x1 - bp.inset_r.unwrap_or(7.2)).max(r.x0 + bp.inset_l.unwrap_or(7.2)), - (r.y1 - bp.inset_b.unwrap_or(3.6)).max(r.y0 + bp.inset_t.unwrap_or(3.6)), - ); + // Insets wider than the shape scale down proportionally to fit inside it (#1632). + let (mut ins_l, mut ins_r) = (bp.inset_l.unwrap_or(7.2).max(0.0), bp.inset_r.unwrap_or(7.2).max(0.0)); + let (mut ins_t, mut ins_b) = (bp.inset_t.unwrap_or(3.6).max(0.0), bp.inset_b.unwrap_or(3.6).max(0.0)); + let w = r.width().max(0.0); + let total_x = ins_l + ins_r; + if total_x > w && total_x > 0.0 { + let s = w / total_x; + ins_l *= s; + ins_r *= s; + } + let h = r.height().max(0.0); + let total_y = ins_t + ins_b; + if total_y > h && total_y > 0.0 { + let s = h / total_y; + ins_t *= s; + ins_b *= s; + } + let inner = Rect::new(r.x0 + ins_l, r.y0 + ins_t, (r.x1 - ins_r).max(r.x0 + ins_l), (r.y1 - ins_b).max(r.y0 + ins_t)); // Text that follows a WordArt arch or circle is set on one line per paragraph. let follow = bp.warp.as_deref().is_some_and(deckcraft_geom::warp::is_follow); let wrap = bp.wrap.unwrap_or(true) && !follow && !matches!(bp.autofit, Some(AutoFit::Shape) if shape.text_box && !bp.wrap.unwrap_or(true)); diff --git a/crates/text/src/tests.rs b/crates/text/src/tests.rs index f9f3f310..a5d3b324 100644 --- a/crates/text/src/tests.rs +++ b/crates/text/src/tests.rs @@ -1928,3 +1928,24 @@ fn a_paragraph_with_its_own_start_at_restarts_the_count_there() { // A later paragraph with a new start begins a fresh run. check(&[1, 1, 7, 7], &[1, 2, 7, 8]); } + +/// #1632: when insets are wider than the shape, they scale down proportionally so the text +/// rectangle stays inside the shape bounds and unwrapped text does not project outside it. +#[test] +fn insets_wider_than_the_shape_scale_down_within_the_shape_bounds() { + let (p, mut sh) = setup("Collaboration Hub", 14.17, 14.17); + sh.ph = None; + let bp = &mut sh.text.as_mut().unwrap().body; + bp.wrap = Some(false); + bp.inset_l = Some(127.5); + bp.inset_r = Some(11.33); + bp.inset_t = Some(11.33); + bp.inset_b = Some(11.33); + sh.text.as_mut().unwrap().paragraphs[0].props = deckcraft_model::text::ParaProps { align: Some(Align::Center), ..Default::default() }; + let l = lay(&p, &sh, 14.17, 14.17); + assert_eq!(l.lines.len(), 1); + let line = &l.lines[0]; + let mid = (line.caret_x[0] + *line.caret_x.last().unwrap()) / 2.0; + // The text center must remain inside the 14.17 pt wide shape, not pushed ~127.5 pt to the right. + assert!((0.0..=14.17).contains(&mid), "line center {mid} should be within [0, 14.17]"); +}