diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index 311c8264..d3c361c9 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -3660,6 +3660,25 @@ fn opening_a_file_caps_absurd_effect_sizes() { r.props.glow = glow.clone(); } } + // Inherited styles too: the deck default, a master's body style, a text box's own list + // style, and the theme's effect styles that shape styles point at. + let level = deckcraft_model::text::LevelStyle { + run: deckcraft_model::text::RunProps { + glow: Some(Glow { color: deckcraft_model::style::ColorRef::rgb(deckcraft_color::Rgba::BLACK), radius: huge }), + ..Default::default() + }, + ..Default::default() + }; + doc.default_text_style.set(0, level.clone()); + let m = std::sync::Arc::make_mut(&mut doc.masters[0]); + m.body_style.set(0, level.clone()); + if let Some(fx) = m.theme.format.effects.first_mut() { + fx.glow = Some(Glow { color: deckcraft_model::style::ColorRef::rgb(deckcraft_color::Rgba::BLACK), radius: huge }); + } + let list = crate::shapes_mut(doc, sel).unwrap(); + if let Some(t) = list.iter_mut().find_map(|sh| sh.text.as_mut()) { + t.list_style.set(0, level); + } Ok(()) }) .unwrap(); @@ -3689,6 +3708,16 @@ fn opening_a_file_caps_absurd_effect_sizes() { glows.extend(cells.flat_map(|p| p.runs.iter()).filter_map(|r| r.props.glow.as_ref().map(|g| g.radius))); } }); + let inherited = |ls: &deckcraft_model::text::ListStyle| { + ls.levels.iter().flatten().filter_map(|l| l.run.glow.as_ref().map(|g| g.radius)).collect::>() + }; + glows.extend(inherited(&st.doc.default_text_style)); + let m = &st.doc.masters[0]; + for ls in [&m.title_style, &m.body_style, &m.other_style] { + glows.extend(inherited(ls)); + } + glows.extend(m.theme.format.effects.iter().filter_map(|e| e.glow.as_ref().map(|g| g.radius))); + deckcraft_model::walk(&slide.shapes, &mut |sh, _| glows.extend(sh.text.iter().flat_map(|t| inherited(&t.list_style)))); assert!(!glows.is_empty(), "{format}: the glows were kept"); assert!(glows.iter().all(|g| (0.0..=150.0).contains(g)), "{format}: {glows:?}"); // Capped, the slide renders (checked last: uncapped, this is what ran out of memory). diff --git a/crates/format/src/lib.rs b/crates/format/src/lib.rs index 9a619d5b..fd2a9216 100644 --- a/crates/format/src/lib.rs +++ b/crates/format/src/lib.rs @@ -392,6 +392,13 @@ fn sane_reflection(r: &mut deckcraft_model::style::Reflection) { r.dist = effect_size(r.dist, MAX_REFLECTION); } +/// Run properties a list style hands down to the text that inherits it. +fn sane_list_style(ls: &mut deckcraft_model::text::ListStyle) { + for level in ls.levels.iter_mut().flatten() { + sane_run(&mut level.run); + } +} + fn sane_effects(e: &mut deckcraft_model::Effects) { for s in e.outer_shadow.iter_mut().chain(e.inner_shadow.iter_mut()) { sane_shadow(s); @@ -420,6 +427,7 @@ fn sane_shapes(shapes: &mut [deckcraft_model::Shape], depth: usize) { sane_effects(e); } if let Some(t) = sh.text.as_mut() { + sane_list_style(&mut t.list_style); for para in &mut t.paragraphs { sane_run(&mut para.end_props); for r in &mut para.runs { @@ -438,6 +446,9 @@ fn sane_shapes(shapes: &mut [deckcraft_model::Shape], depth: usize) { sane_shapes(children, depth + 1); } deckcraft_model::ShapeKind::Table(t) => { + for cell in t.rows.iter_mut().flat_map(|r| r.cells.iter_mut()) { + sane_list_style(&mut cell.text.list_style); + } for para in t.rows.iter_mut().flat_map(|r| r.cells.iter_mut()).flat_map(|c| c.text.paragraphs.iter_mut()) { sane_run(&mut para.end_props); for r in &mut para.runs { @@ -459,12 +470,21 @@ pub fn sane_numbers(p: &mut Presentation) { *slide = Arc::new(fixed); } } + // What text and shapes inherit: the deck's default text style, each master's title, body and + // other styles, and the theme's effect styles that shape styles point at. + sane_list_style(&mut p.default_text_style); for m in p.masters.iter_mut().chain(p.notes_master.iter_mut()).chain(p.handout_master.iter_mut()) { let mut fixed = (**m).clone(); sane_shapes(&mut fixed.shapes, 0); for l in &mut fixed.layouts { sane_shapes(&mut l.shapes, 0); } + for ls in [&mut fixed.title_style, &mut fixed.body_style, &mut fixed.other_style] { + sane_list_style(ls); + } + for e in &mut fixed.theme.format.effects { + sane_effects(e); + } if fixed != **m { *m = Arc::new(fixed); }