diff --git a/crates/pptx/src/write/dml.rs b/crates/pptx/src/write/dml.rs index 1af1d9627..3705cc93a 100644 --- a/crates/pptx/src/write/dml.rs +++ b/crates/pptx/src/write/dml.rs @@ -357,13 +357,16 @@ pub fn effects(w: &mut W, fx: &Effects) { .a("endA", pct(r.end_alpha.clamp(0.0, 1.0))) .a("endPos", pct(r.end_pos.clamp(0.0, 1.0))) .a("dist", emu(r.dist).max(0)) - .o("stPos", r.st_pos) - .o("dir", r.dir) - .o("fadeDir", r.fade_dir) + // Kept from a file as read: held to the schema's ranges so a stray value can't + // make PowerPoint repair the file (ST_PositiveFixedAngle, ST_FixedAngle, + // ST_PositiveFixedPercentage). + .o("stPos", r.st_pos.map(|v| v.clamp(0, 100_000))) + .o("dir", r.dir.map(|v| v.rem_euclid(21_600_000))) + .o("fadeDir", r.fade_dir.map(|v| v.rem_euclid(21_600_000))) .o("sx", r.sx) .o("sy", r.sy) - .o("kx", r.kx) - .o("ky", r.ky) + .o("kx", r.kx.map(|v| v.clamp(-5_399_999, 5_399_999))) + .o("ky", r.ky.map(|v| v.clamp(-5_399_999, 5_399_999))) .o("algn", r.align.as_deref()) .b("rotWithShape", r.rotate_with_shape), ); diff --git a/crates/pptx/tests/it/roundtrip.rs b/crates/pptx/tests/it/roundtrip.rs index 1ed97f546..36330460b 100644 --- a/crates/pptx/tests/it/roundtrip.rs +++ b/crates/pptx/tests/it/roundtrip.rs @@ -2094,3 +2094,26 @@ fn default_durations_match_powerpoint() { assert_eq!(a.4, ms, "{effect}"); } } + +#[test] +fn reflection_angles_and_skews_out_of_range_are_written_inside_the_schema() { + let mut p = Presentation::default(); + let mut slide = new_slide(&mut p, "Reflection ranges"); + let mut s = shape(&mut p, "Reflected", 10.0, 10.0, 200.0, 80.0); + s.effects = Some(deckcraft_model::style::Effects { + reflection: Some(deckcraft_model::style::Reflection { + st_pos: Some(150_000), + dir: Some(-5), + fade_dir: Some(21_600_000), + kx: Some(9_000_000), + ky: Some(-9_000_000), + ..Default::default() + }), + ..Default::default() + }); + slide.shapes.push(s); + p.slides.push(Arc::new(slide)); + + let r = round(&p).slides.last().and_then(|sl| by_name(sl, "Reflected").effects.clone()).and_then(|fx| fx.reflection).expect("reflection"); + assert_eq!((r.st_pos, r.dir, r.fade_dir, r.kx, r.ky), (Some(100_000), Some(21_599_995), Some(0), Some(5_399_999), Some(-5_399_999))); +}