//! A paragraph whose `w:pStyle` names a style `styles.xml` doesn't define is formatted as Normal,
//! as in Word, not with the document defaults alone (#5077). Pandoc writes footers as
//! `` without defining Footer.
use craft_wordmodel::props::LineSpacing;
use craft_wordmodel::{Block, Document, PartKind};
use std::io::Write;
const W: &str = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
const R: &str = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
fn package(normal_id: &str) -> Vec {
let root = r#""#;
let doc = format!(
r#"bodynormal"#
);
let footer = format!(
r#"1"#
);
let styles = format!(
r#""#
);
let rels = r#""#;
let mut zw = zip::ZipWriter::new(std::io::Cursor::new(Vec::new()));
for (name, body) in [
("_rels/.rels", root),
("word/document.xml", doc.as_str()),
("word/_rels/document.xml.rels", rels),
("word/styles.xml", styles.as_str()),
("word/footer1.xml", footer.as_str()),
] {
zw.start_file(name, zip::write::SimpleFileOptions::default()).unwrap();
zw.write_all(body.as_bytes()).unwrap();
}
zw.finish().unwrap().into_inner()
}
fn footer_para(d: &Document) -> &craft_wordmodel::Paragraph {
let part = d.parts.values().find(|p| p.kind == PartKind::Footer).expect("a footer");
match part.blocks.first().map(|b| &**b) {
Some(Block::Para(p)) => p,
_ => panic!("the footer starts with a paragraph"),
}
}
#[test]
fn a_paragraph_naming_an_undefined_style_is_formatted_as_normal() {
// Also when the file's default paragraph style has another id (read as Normal).
for normal_id in ["Normal", "a"] {
let d = craft_docx::read(&package(normal_id)).unwrap();
let Some(Block::Para(body)) = d.body.first().map(|b| &**b) else { panic!("a body paragraph") };
let Some(Block::Para(normal)) = d.body.get(1).map(|b| &**b) else { panic!("a second body paragraph") };
let normal_rp = d.styles.resolve_para(&normal.props);
assert_eq!((normal_rp.space_before, normal_rp.space_after), (1.0, 2.0), "{normal_id}");
for p in [body, footer_para(&d)] {
// The paragraph keeps the style it names, for the round trip.
assert_eq!(p.props.style.as_deref(), Some("Footer"));
let rp = d.styles.resolve_para(&p.props);
assert_eq!((rp.space_before, rp.space_after), (1.0, 2.0), "{normal_id}: Normal's spacing, not the defaults' 10 pt after");
assert_eq!(rp.line_spacing, LineSpacing::Multiple(259.0 / 240.0), "{normal_id}");
assert_eq!(d.styles.resolve_char(Some("Footer"), &Default::default()).size, 13.0, "{normal_id}");
}
}
}
#[test]
fn an_undefined_style_is_written_back_as_it_was_named() {
let d = craft_docx::read(&package("Normal")).unwrap();
let again = craft_docx::read(&craft_docx::write(&d).unwrap()).unwrap();
assert_eq!(footer_para(&again).props.style.as_deref(), Some("Footer"));
assert_eq!(again.styles.resolve_para(&footer_para(&again).props).space_after, 2.0);
}