From 6ff5b274ea251fb8d22c3b1f1b66e74f1c08b1e5 Mon Sep 17 00:00:00 2001 From: MarekSpodBiedry <73122968+MarekSpodBiedry@users.noreply.github.com> Date: Sat, 10 Oct 2026 19:00:06 +0200 Subject: [PATCH] PPTX: animation rewind survives save and reopen (#77) --- crates/pptx/src/read/timing.rs | 2 ++ crates/pptx/src/write/timing.rs | 2 +- crates/pptx/tests/roundtrip.rs | 17 +++++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/crates/pptx/src/read/timing.rs b/crates/pptx/src/read/timing.rs index 500bb485..fcf01953 100644 --- a/crates/pptx/src/read/timing.rs +++ b/crates/pptx/src/read/timing.rs @@ -137,6 +137,8 @@ pub fn animations(imp: &mut Imp, part: &Part, ctx: &IdCtx, timing: &El, _shapes: preset_id: Some(preset), preset_subtype: Some(sub), auto_reverse: c.bool("autoRev").unwrap_or(false), + // PowerPoint's "Rewind when done playing" is `fill="remove"` on the effect node. + rewind: c.attr("fill") == Some("remove"), smooth_start: c.f64("accel").map(|v| v / 100_000.0).unwrap_or(0.0), smooth_end: c.f64("decel").map(|v| v / 100_000.0).unwrap_or(0.0), ..Default::default() diff --git a/crates/pptx/src/write/timing.rs b/crates/pptx/src/write/timing.rs index aa577ca7..09b101dc 100644 --- a/crates/pptx/src/write/timing.rs +++ b/crates/pptx/src/write/timing.rs @@ -445,7 +445,7 @@ fn effect_par(w: &mut W, x: &mut Exp, ids: &mut Ids, e: &Eff, first_in_click: bo .o("accel", accel) .o("decel", decel) .t("autoRev", a.auto_reverse) - .a("fill", "hold") + .a("fill", if a.rewind { "remove" } else { "hold" }) .a("grpId", e.grp) .a("nodeType", node), ); diff --git a/crates/pptx/tests/roundtrip.rs b/crates/pptx/tests/roundtrip.rs index 420a3921..a835ca69 100644 --- a/crates/pptx/tests/roundtrip.rs +++ b/crates/pptx/tests/roundtrip.rs @@ -633,6 +633,23 @@ fn every_animation_effect_survives() { } } +#[test] +fn animation_rewind_survives() { + let mut p = defaults::new_presentation(None); + let mut s = p.slides[0].as_ref().clone(); + let sh = shape(&mut p, "Target", 100.0, 100.0, 100.0, 100.0); + let id = sh.id; + s.shapes.push(sh); + s.animations.push(Animation { shape: id, rewind: true, ..Default::default() }); + s.animations.push(Animation { shape: id, effect: "fly".into(), option: "l".into(), start: AnimStart::WithPrevious, ..Default::default() }); + p.slides[0] = Arc::new(s); + let q = round(&p); + let b = &q.slides[0].animations; + assert_eq!(b.len(), 2); + assert!(b[0].rewind); + assert!(!b[1].rewind); +} + /// Writes bisection variants of the rich deck to `DECKCRAFT_PPTX_BISECT` (manual checks). #[test] fn bisect_variants() {