PPTX: the notes master's own placeholders and colour map are kept on save

This commit is contained in:
Nikita Rakitin
2026-10-14 05:41:01 +03:00
parent 457406a7a5
commit 6b0c1db6ed
2 changed files with 52 additions and 5 deletions
+18 -5
View File
@@ -434,8 +434,8 @@ pub fn export(p: &Presentation) -> Result<Vec<u8>, PptxError> {
master_rids.push((master_id, pres.rels.add("slideMaster", &opc::relative("ppt/presentation.xml", &mname), false)));
}
// Notes master when any slide has notes.
let has_notes = p.slides.iter().any(|s| !s.notes.is_empty());
// Notes master when the deck has one or any slide has notes.
let has_notes = p.notes_master.is_some() || p.slides.iter().any(|s| !s.notes.is_empty());
let notes_master = "ppt/notesMasters/notesMaster1.xml";
let mut notes_master_rid = None;
if has_notes {
@@ -449,9 +449,22 @@ pub fn export(p: &Presentation) -> Result<Vec<u8>, PptxError> {
x.pkg.rels(&tname, &to.rels);
let mut w = W::new();
root_open(&mut w, "p:notesMaster", A::new());
let bg = Background::Ref { idx: 1001, color: deckcraft_model::ColorRef::scheme(deckcraft_color::SchemeSlot::Bg1) };
csld(&mut w, &mut x, &mut no, "", Some(&bg), &notes_master_shapes(p.notes_size));
clr_map(&mut w, &[]);
let nm = p.notes_master.as_deref();
let bg = nm
.and_then(|m| m.background.clone())
.unwrap_or(Background::Ref { idx: 1001, color: deckcraft_model::ColorRef::scheme(deckcraft_color::SchemeSlot::Bg1) });
// The file's own placeholders are kept; PowerPoint's defaults only when it has none.
let own = nm.map(|m| m.shapes.as_slice()).filter(|s| !s.is_empty());
let defaults;
let list = match own {
Some(s) => s,
None => {
defaults = notes_master_shapes(p.notes_size);
defaults.as_slice()
}
};
csld(&mut w, &mut x, &mut no, "", Some(&bg), list);
clr_map(&mut w, nm.map(|m| m.color_map.as_slice()).unwrap_or(&[]));
dml::list_style(&mut w, &mut x, &mut no, "p:notesStyle", &notes_style());
w.close("p:notesMaster");
x.pkg.xml_part(notes_master, CT_NOTES_MASTER, w.finish());
+34
View File
@@ -991,3 +991,37 @@ fn transition_defaults_read_like_powerpoint() {
assert_eq!((t.kind.as_str(), t.option.as_str(), t.duration_ms), (*kind, *option, 2000), "{el}");
}
}
#[test]
fn notes_master_placeholders_and_colour_map_survive_save() {
let mut p = defaults::new_presentation(None);
let mut box1 = shape(&mut p, "Confidential", 100000.0, 200000.0, 3000000.0, 400000.0);
box1.text = Some(TextBody::from_text("Company confidential"));
let map: Vec<(String, String)> = [("bg1", "dk1"), ("tx1", "lt1"), ("bg2", "dk2"), ("tx2", "lt2")]
.iter()
.chain(
[
("accent1", "accent1"),
("accent2", "accent2"),
("accent3", "accent3"),
("accent4", "accent4"),
("accent5", "accent5"),
("accent6", "accent6"),
("hlink", "hlink"),
("folHlink", "folHlink"),
]
.iter(),
)
.map(|(a, b)| (a.to_string(), b.to_string()))
.collect();
let m = deckcraft_model::Master { name: "Notes Master".into(), shapes: vec![box1], color_map: map.clone(), ..Default::default() };
p.notes_master = Some(Arc::new(m));
// No slide has notes: the notes master must still be written.
let q = round(&p);
let nm = q.notes_master.as_deref().expect("notes master kept");
assert_eq!(nm.shapes.len(), 1, "own shapes, not the defaults");
let sh = &nm.shapes[0];
assert_eq!(sh.name, "Confidential");
assert_eq!(sh.text.as_ref().map(|t| t.text()), Some("Company confidential".to_string()));
assert_eq!(nm.color_map, map);
}