From da07540f31f5592e34a1441a19502d4c8eeff1ef Mon Sep 17 00:00:00 2001 From: loganj <77709487+g1obalworlds@users.noreply.github.com> Date: Sat, 10 Oct 2026 16:29:27 -0700 Subject: [PATCH] Layout: a drop cap in a font over 1000 pt no longer panics (#389) The drop-cap letter's size was `clamp(rc.size, 1000.0)`, and `f32::clamp` panics when the minimum is above the maximum, which happens as soon as the paragraph's text is larger than 1000 pt (Word allows up to 1638). Layout, PDF export and saving run outside `Session::run`'s panic guard, so the desktop app panicked on every frame, and `wordcraft-cli convert` failed. Use `.min(1000.0).max(rc.size)`, which never panics and keeps the letter at least as large as the text. Co-authored-by: Claude Sonnet 5.5 --- crates/layout/src/para.rs | 3 ++- crates/layout/src/tests.rs | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/crates/layout/src/para.rs b/crates/layout/src/para.rs index 46b9a7e45..9aad1bdfb 100644 --- a/crates/layout/src/para.rs +++ b/crates/layout/src/para.rs @@ -694,7 +694,8 @@ pub fn layout_para(p: &Paragraph, env: &ParaEnv) -> ParaLayout { let lines = rp.drop_cap as f32; // Capital height is ~0.7 em: the letter's cap spans from line 1's caps to line N's baseline. let mut big = (*rc).clone(); - big.size = (((lines - 1.0) * line_h + rc.size * 0.7) / 0.7).clamp(rc.size, 1000.0); + // `clamp` would panic here when the text itself is larger than 1000 pt. + big.size = (((lines - 1.0) * line_h + rc.size * 0.7) / 0.7).min(1000.0).max(rc.size); let before = b.clusters.len(); b.shape(first, 0, &Arc::new(big), None); let w: f32 = b.clusters.get(before..).map(|c| c.iter().map(|c| c.adv).sum()).unwrap_or(0.0); diff --git a/crates/layout/src/tests.rs b/crates/layout/src/tests.rs index 2a509fb31..30a83b1c7 100644 --- a/crates/layout/src/tests.rs +++ b/crates/layout/src/tests.rs @@ -1282,6 +1282,24 @@ fn repeated_header_is_followed_by_the_next_row_even_when_it_overflows() { assert!(pages.iter().all(|p| p.first() == Some(&0)), "{pages:?}"); } +/// A drop-cap paragraph in a font larger than 1000 pt used to panic in layout (`clamp` with min > max). +#[test] +fn drop_cap_with_a_huge_font_lays_out() { + for size in [999.0, 1000.0, 1001.0, 1638.0] { + let cp = wordcraft_doc::CharProps { size: Some(size), ..Default::default() }; + let mut p = wordcraft_doc::Paragraph::with_text("Hello drop cap world", cp); + p.props.drop_cap = Some(3); + let mut d = Document::from_text("x"); + d.body = vec![wordcraft_doc::para_block(p)]; + let l = lay(&d); + let Some(pl) = l.pages[0].items.iter().find_map(|i| if let Placed::Lines { para, .. } = i { Some(para.clone()) } else { None }) else { + panic!("no lines at size {size}") + }; + let (nc, lines, _) = pl.drop_cap.unwrap_or((0, 0, 0.0)); + assert_eq!((nc, lines), (1, 3), "size {size}"); + } +} + #[test] fn drop_cap_indents_its_lines() { let mut p =