//! A paragraph's vertical text alignment (`w:textAlignment`, ECMA-376 Part 1 ยง17.3.1.39, values //! from ST_TextAlignment): read and written back as read, in the schema's place after `w:jc`. //! Word sizes a line by it when a list label is larger than the text, so a save that drops it //! moves every line after. Fixtures are hand-written from the spec. use std::io::{Read, Write}; use craft_wordmodel::props::TextAlignment; fn docx(body: &str) -> Vec { let root = r#""#; let doc = format!( r#"{body}"# ); let mut zw = zip::ZipWriter::new(std::io::Cursor::new(Vec::new())); for (name, bytes) in [("_rels/.rels", root.as_bytes()), ("word/document.xml", doc.as_bytes())] { zw.start_file(name, zip::write::SimpleFileOptions::default()).unwrap(); zw.write_all(bytes).unwrap(); } zw.finish().unwrap().into_inner() } fn document_xml(bytes: &[u8]) -> String { let mut z = zip::ZipArchive::new(std::io::Cursor::new(bytes)).unwrap(); let mut s = String::new(); z.by_name("word/document.xml").unwrap().read_to_string(&mut s).unwrap(); s } #[test] fn text_alignment_is_written_back_as_read() { let para = |ppr: &str, text: &str| format!(r#"{ppr}{text}"#); let body = [ para(r#""#, "A"), para(r#""#, "B"), para(r#""#, "C"), para(r#""#, "D"), para(r#""#, "E"), para(r#""#, "F"), para("", "G"), ] .concat(); let d = craft_docx::read(&docx(&body)).unwrap(); let got: Vec> = d.body.iter().filter_map(|b| b.as_para()).map(|p| p.props.text_alignment).collect(); let want = vec![ Some(TextAlignment::Top), Some(TextAlignment::Center), Some(TextAlignment::Baseline), Some(TextAlignment::Bottom), Some(TextAlignment::Auto), None, None, ]; assert_eq!(got, want); let out = craft_docx::write(&d).unwrap(); let xml = document_xml(&out); assert!(xml.contains(r#""#), "{xml}"); for v in ["center", "baseline", "bottom", "auto"] { assert_eq!(xml.matches(&format!(r#""#)).count(), 1, "{v}: {xml}"); } assert_eq!(xml.matches("w:textAlignment").count(), 5, "an unknown value and none write nothing: {xml}"); let back = craft_docx::read(&out).unwrap(); assert_eq!(back.body.iter().filter_map(|b| b.as_para()).map(|p| p.props.text_alignment).collect::>(), want); }