//! Run and style properties that must survive repeated DOCX saves. use std::io::{Read, Write}; fn docx(body: &str, styles: &str) -> Vec { let root = r#""#; let rels = r#""#; let document = format!( r#"{body}"# ); let styles = format!(r#"{styles}"#); let mut zip = zip::ZipWriter::new(std::io::Cursor::new(Vec::new())); for (name, xml) in [ ("_rels/.rels", root.to_string()), ("word/document.xml", document), ("word/_rels/document.xml.rels", rels.to_string()), ("word/styles.xml", styles), ] { zip.start_file(name, zip::write::SimpleFileOptions::default()).unwrap(); zip.write_all(xml.as_bytes()).unwrap(); } zip.finish().unwrap().into_inner() } fn part(bytes: &[u8], name: &str) -> String { let mut zip = zip::ZipArchive::new(std::io::Cursor::new(bytes)).unwrap(); let mut xml = String::new(); zip.by_name(name).unwrap().read_to_string(&mut xml).unwrap(); xml } #[test] fn paragraph_adjust_right_indent_inherits_from_style() { let styles = r#""#; let body = r#"off inherited"#; let mut d = craft_docx::read(&docx(body, styles)).unwrap(); let paragraphs: Vec<_> = d.body.iter().filter_map(|b| b.as_para()).collect(); assert_eq!(paragraphs[0].props.adjust_right_indent, Some(false)); assert!(!d.styles.resolve_para(¶graphs[0].props).adjust_right_indent); assert!(d.styles.resolve_para(¶graphs[1].props).adjust_right_indent); for _ in 0..2 { let out = craft_docx::write(&d).unwrap(); assert!(part(&out, "word/document.xml").contains(r#"")); d = craft_docx::read(&out).unwrap(); let paragraphs: Vec<_> = d.body.iter().filter_map(|b| b.as_para()).collect(); assert!(!d.styles.resolve_para(¶graphs[0].props).adjust_right_indent); assert!(d.styles.resolve_para(¶graphs[1].props).adjust_right_indent); assert_eq!(paragraphs[0].props.adjust_right_indent, Some(false)); } } #[test] fn run_and_style_snap_to_grid_and_web_hidden_survive_two_saves() { let styles = r#" "#; let body = r#" inherit direct true override visible in print absent "#; let mut doc = craft_docx::read(&docx(body, styles)).unwrap(); let p = doc.body.iter().find_map(|b| b.as_para()).unwrap(); assert_eq!(p.runs[0].props.snap_to_grid, None); assert_eq!(p.runs[0].props.web_hidden, None); assert_eq!(p.runs[1].props.snap_to_grid, Some(true)); assert_eq!(p.runs[1].props.web_hidden, Some(true)); assert_eq!(p.runs[2].props.snap_to_grid, Some(false)); assert_eq!(p.runs[2].props.web_hidden, Some(false)); assert_eq!(p.runs[3].props.hidden, Some(true)); assert_eq!(p.runs[3].props.web_hidden, None); assert_eq!(p.runs[4].props.snap_to_grid, None); assert_eq!(p.runs[4].props.web_hidden, None); assert!(!doc.styles.resolve_char(None, &p.runs[0].props).hidden, "w:webHidden does not hide Print Layout text"); assert!(doc.styles.resolve_char(None, &p.runs[3].props).hidden, "w:vanish still controls hidden text"); for pass in 0..2 { let out = craft_docx::write(&doc).unwrap(); let style_xml = part(&out, "word/styles.xml"); assert!(style_xml.contains(r#""#), "pass {pass}: {style_xml}"); assert!(style_xml.contains(r#""#), "pass {pass}: {style_xml}"); let document_xml = part(&out, "word/document.xml"); assert!(document_xml.contains(r#""#), "pass {pass}: {document_xml}"); assert!(document_xml.contains(r#""#), "pass {pass}: {document_xml}"); assert!(document_xml.contains(""), "pass {pass}: {document_xml}"); assert!(document_xml.contains(""), "pass {pass}: {document_xml}"); assert!(document_xml.contains("visible in print")); doc = craft_docx::read(&out).unwrap(); } }