//! Speaker notes keep their run and paragraph formatting: a bold heading, indented points with
//! custom bullets, plain lines.
use std::io::{Cursor, Read, Write};
use std::sync::Arc;
use deckcraft_model::defaults;
use deckcraft_model::text::{Bullet, ParaProps, TextBody};
const NOTES: &str = "ppt/notesSlides/notesSlide1.xml";
/// A notes slide written the way PowerPoint writes one (Slovak text): a bold heading, a plain
/// line, two level-2 points with a hanging indent and a Wingdings square bullet (one mixing a bold
/// label and plain text), a level-2 paragraph with buNone, and a closing plain line.
const NOTES_XML: &str = r#"
Metodický prehľadŽiaci pracujú vo dvojiciach.Cieľ: vysvetliť cyklusReflexiaBez odrážkyPomôcky: počítač"#;
/// A deck with one notes slide, its notes replaced by [`NOTES_XML`].
fn deck() -> Vec {
let mut p = defaults::new_presentation(None);
Arc::make_mut(&mut p.slides[0]).notes = TextBody::from_text("placeholder");
let bytes = deckcraft_pptx::export(&p).expect("export");
let mut src = zip::ZipArchive::new(Cursor::new(bytes)).expect("zip");
let mut out = zip::ZipWriter::new(Cursor::new(Vec::new()));
let mut found = false;
for i in 0..src.len() {
let mut f = src.by_index(i).expect("entry");
let name = f.name().to_string();
let mut data = vec![];
f.read_to_end(&mut data).expect("read");
if name == NOTES {
data = NOTES_XML.as_bytes().to_vec();
found = true;
}
out.start_file(name, zip::write::SimpleFileOptions::default()).expect("start");
out.write_all(&data).expect("write");
}
assert!(found, "the export has no {NOTES}");
out.finish().expect("finish").into_inner()
}
fn assert_formatted(notes: &TextBody) {
assert_eq!(notes.text(), "Metodický prehľad\nŽiaci pracujú vo dvojiciach.\nCieľ: vysvetliť cyklus\nReflexia\nBez odrážky\nPomôcky: počítač");
let [head, plain, goal, reflect, unbulleted, closing] = notes.paragraphs.as_slice() else { panic!("six paragraphs: {notes:?}") };
assert_eq!(head.runs[0].props.bold, Some(true), "bold heading");
for para in [head, plain, closing] {
assert_eq!((para.level, ¶.props), (0, &ParaProps::default()), "{:?}", para.text());
}
assert_eq!(plain.runs[0].props.bold, None);
for para in [goal, reflect] {
assert_eq!(para.level, 1, "level 2");
assert_eq!(para.props.margin_left, Some(22.5), "marL 285750 EMU");
assert_eq!(para.props.indent, Some(-22.5), "hanging indent");
assert_eq!(para.props.bullet, Some(Bullet::Char { char: "§".into() }));
assert_eq!(para.props.bullet_font.as_deref(), Some("Wingdings"));
}
assert_eq!((goal.runs[0].text.as_str(), goal.runs[0].props.bold), ("Cieľ: ", Some(true)), "bold label");
assert_eq!((goal.runs[1].text.as_str(), goal.runs[1].props.bold), ("vysvetliť cyklus", None), "then plain");
assert_eq!(reflect.runs[0].props.italic, Some(true));
assert_eq!((unbulleted.level, unbulleted.props.bullet.clone(), unbulleted.props.margin_left), (1, Some(Bullet::None), Some(22.5)));
}
#[test]
fn notes_import_keeps_bold_levels_and_indents() {
let p = deckcraft_pptx::import(&deck()).expect("import");
assert_formatted(&p.slides[0].notes);
}
#[test]
fn notes_formatting_survives_a_round_trip() {
let p = deckcraft_pptx::import(&deck()).expect("import");
let again = deckcraft_pptx::import(&deckcraft_pptx::export(&p).expect("export")).expect("reimport");
assert_formatted(&again.slides[0].notes);
// And the written XML says so, for PowerPoint.
let bytes = deckcraft_pptx::export(&again).expect("export");
let mut z = zip::ZipArchive::new(Cursor::new(bytes)).expect("zip");
let mut xml = String::new();
z.by_name(NOTES).expect("notes part").read_to_string(&mut xml).expect("utf-8");
assert!(xml.contains(r#"b="1""#), "{xml}");
assert!(xml.contains(r#"lvl="1""#) && xml.contains(r#"marL="285750""#) && xml.contains(r#"indent="-285750""#), "{xml}");
assert!(xml.contains(r#""), "{xml}");
}