mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-30 21:47:00 -06:00
Merge pull request #1435
fix(pptx): read Auto-reverse from animation behaviour nodes
This commit is contained in:
@@ -94,6 +94,13 @@ fn max_dur(e: &El, depth: usize) -> Option<u32> {
|
||||
best
|
||||
}
|
||||
|
||||
/// Does every behaviour time node (`p:cBhvr/p:cTn`) under the effect node `c` carry `autoRev`?
|
||||
fn behaviour_auto_reverses(c: &El) -> bool {
|
||||
let mut behaviours = vec![];
|
||||
c.find_all("cBhvr", &mut behaviours);
|
||||
!behaviours.is_empty() && behaviours.iter().all(|b| b.child("cTn").and_then(|t| t.bool("autoRev")).unwrap_or(false))
|
||||
}
|
||||
|
||||
/// Flatten the timing tree into the Animation Pane list.
|
||||
pub fn animations(imp: &mut Imp, part: &Part, ctx: &IdCtx, timing: &El, shapes: &[Shape]) -> Vec<Animation> {
|
||||
let _ = (imp, part);
|
||||
@@ -144,7 +151,9 @@ pub fn animations(imp: &mut Imp, part: &Part, ctx: &IdCtx, timing: &El, shapes:
|
||||
trigger: f.trigger.and_then(|t| ctx.lookup(t)),
|
||||
preset_id: Some(preset),
|
||||
preset_subtype: Some(sub),
|
||||
auto_reverse: c.bool("autoRev").unwrap_or(false),
|
||||
// PowerPoint's "Auto-reverse" is `autoRev` on the behaviour nodes (`p:cBhvr/p:cTn`); Color Pulse
|
||||
// always writes it there and plays back and forth on its own, so it isn't read as the option.
|
||||
auto_reverse: c.bool("autoRev").unwrap_or(false) || (effect != "colorPulse" && behaviour_auto_reverses(c)),
|
||||
// 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),
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
//! "Auto-reverse" is written by PowerPoint on the behaviour time nodes, not on the effect node.
|
||||
|
||||
use std::io::{Cursor, Read, Write};
|
||||
|
||||
use deckcraft_geom::Xfrm;
|
||||
use deckcraft_model::anim::{AnimClass, Animation};
|
||||
use deckcraft_model::{Shape, ShapeId, defaults};
|
||||
|
||||
fn deck_with(effect: &str, behaviour: &str) -> Vec<bool> {
|
||||
let mut p = defaults::new_presentation(None);
|
||||
let id = ShapeId(p.alloc_id().expect("id"));
|
||||
let shape = Shape { id, name: "Box".into(), xfrm: Some(Xfrm::new(10.0, 10.0, 200.0, 100.0)), ..Default::default() };
|
||||
let sl = std::sync::Arc::make_mut(&mut p.slides[0]);
|
||||
sl.shapes = vec![shape];
|
||||
sl.animations.push(Animation { shape: id, class: AnimClass::Emphasis, effect: effect.into(), duration_ms: 1000, ..Default::default() });
|
||||
let mut z = zip::ZipArchive::new(Cursor::new(deckcraft_pptx::export(&p).expect("export"))).expect("zip");
|
||||
let mut out = zip::ZipWriter::new(Cursor::new(Vec::new()));
|
||||
for i in 0..z.len() {
|
||||
let mut f = z.by_index(i).expect("entry");
|
||||
let name = f.name().to_string();
|
||||
let mut d = vec![];
|
||||
f.read_to_end(&mut d).expect("read");
|
||||
if name == "ppt/slides/slide1.xml" {
|
||||
let xml = String::from_utf8(d).expect("utf8");
|
||||
let at = xml.find(behaviour).expect("behaviour written");
|
||||
let cb = at + xml[at..].find("<p:cTn ").expect("behaviour node");
|
||||
d = format!("{}<p:cTn autoRev=\"1\" {}", &xml[..cb], &xml[cb + "<p:cTn ".len()..]).into_bytes();
|
||||
}
|
||||
out.start_file(name, zip::write::SimpleFileOptions::default()).expect("start");
|
||||
out.write_all(&d).expect("write");
|
||||
}
|
||||
let q = deckcraft_pptx::import(&out.finish().expect("finish").into_inner()).expect("import");
|
||||
q.slides[0].animations.iter().map(|a| a.auto_reverse).collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn grow_shrink_reads_auto_reverse_from_its_behaviour_node() {
|
||||
assert_eq!(deck_with("growShrink", "<p:animScale>"), [true]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn color_pulse_behaviour_auto_reverse_is_not_the_option() {
|
||||
assert_eq!(deck_with("colorPulse", "<p:animClr "), [false]);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
mod anchor;
|
||||
mod anim_auto_reverse;
|
||||
mod chart_embed;
|
||||
mod comments;
|
||||
mod corpus;
|
||||
|
||||
Reference in New Issue
Block a user