//! #4696: Word's built-in heading styles have their outline level even when the file's //! `styles.xml` doesn't write `w:outlineLvl` (some producers leave it out). Word for Windows, //! opening such a file, reports `OutlineLevel` 1 and 2 for "heading 1" and "heading 2" (whatever //! their style ids) and body text for a custom style. use craft_wordmodel::Paragraph; use std::io::Write; const W: &str = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"; fn package(styles: &str, body: &str) -> Vec { let root = r#""#; let rels = r#""#; let doc = format!(r#"{body}"#); let styles = format!(r#"{styles}"#); 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())] { zw.start_file(name, zip::write::SimpleFileOptions::default()).unwrap(); zw.write_all(body.as_bytes()).unwrap(); } zw.finish().unwrap().into_inner() } fn para(style: &str, text: &str) -> String { format!(r#"{text}"#) } fn levels(d: &craft_wordmodel::Document) -> Vec> { d.body.iter().filter_map(|b| b.as_para()).map(|p: &Paragraph| d.styles.resolve_para(&p.props).outline_level).collect() } #[test] fn built_in_heading_styles_have_their_outline_level_without_outline_lvl() { let styles = r#" "#; let body = ["Heading1", "Titolo2", "Heading9", "Below", "MyHead", "Heading10", "Fake4", "Heading3", "Normal"] .iter() .map(|s| para(s, "x")) .collect::(); let d = craft_docx::read(&package(styles, &body)).unwrap(); // Heading 1, 2 and 9 by name; a style based on a heading inherits its level; a custom style, // "heading 10", a custom style named "heading 4" (`w:customStyle="on"`) and Normal have none; a // level the file gives is kept. let want = vec![Some(0), Some(1), Some(8), Some(1), None, None, None, Some(5), None]; assert_eq!(levels(&d), want); // The level survives a save and reopen. let again = craft_docx::read(&craft_docx::write(&d).unwrap()).unwrap(); assert_eq!(levels(&again), want); }