diff --git a/crates/engine/src/cmd/links.rs b/crates/engine/src/cmd/links.rs index 6789feb2c..e7d164f35 100644 --- a/crates/engine/src/cmd/links.rs +++ b/crates/engine/src/cmd/links.rs @@ -1095,7 +1095,6 @@ mod text_link_tests { /// A small Word file: a Title paragraph, then Body paragraphs (`body` is WordprocessingML). fn docx(body: &str) -> Vec { - use std::io::Write; const W: &str = r#"xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main""#; let styles = format!( r#" @@ -1105,13 +1104,7 @@ mod text_link_tests { r#"Head {body}"# ); - let mut w = zip::ZipWriter::new(std::io::Cursor::new(Vec::new())); - let o = zip::write::SimpleFileOptions::default(); - for (n, c) in [("word/document.xml", doc), ("word/styles.xml", styles)] { - w.start_file(n, o).unwrap(); - w.write_all(c.as_bytes()).unwrap(); - } - w.finish().unwrap().into_inner() + crate::cmd::place_text::word_package(&doc, &styles) } fn text_rows(s: &mut Session) -> Vec { diff --git a/crates/engine/src/cmd/place_text.rs b/crates/engine/src/cmd/place_text.rs index f80d3fba8..f994b0ddb 100644 --- a/crates/engine/src/cmd/place_text.rs +++ b/crates/engine/src/cmd/place_text.rs @@ -506,6 +506,29 @@ pub(super) fn place_text( }) } +/// A minimal Word package for tests: the document and its styles, with the content types and +/// relationships that tell a reader where those parts are. +#[cfg(test)] +pub(crate) fn word_package(document: &str, styles: &str) -> Vec { + use std::io::Write; + const CONTENT_TYPES: &str = r#""#; + const ROOT_RELS: &str = r#""#; + const DOCUMENT_RELS: &str = r#""#; + let mut w = zip::ZipWriter::new(std::io::Cursor::new(Vec::new())); + let o = zip::write::SimpleFileOptions::default(); + for (n, c) in [ + ("[Content_Types].xml", CONTENT_TYPES), + ("_rels/.rels", ROOT_RELS), + ("word/_rels/document.xml.rels", DOCUMENT_RELS), + ("word/document.xml", document), + ("word/styles.xml", styles), + ] { + w.start_file(n, o).unwrap(); + w.write_all(c.as_bytes()).unwrap(); + } + w.finish().unwrap().into_inner() +} + #[cfg(test)] mod tests; @@ -710,7 +733,6 @@ mod pdf_page_tests { } fn word_file() -> Vec { - use std::io::Write; const W: &str = r#"xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main""#; let styles = format!( r#" @@ -721,13 +743,7 @@ mod pdf_page_tests { r#"Head Text"# ); - let mut w = zip::ZipWriter::new(std::io::Cursor::new(Vec::new())); - let o = zip::write::SimpleFileOptions::default(); - for (n, c) in [("word/document.xml", doc), ("word/styles.xml", styles)] { - w.start_file(n, o).unwrap(); - w.write_all(c.as_bytes()).unwrap(); - } - w.finish().unwrap().into_inner() + super::word_package(&doc, &styles) } #[test] @@ -770,19 +786,13 @@ mod pdf_page_tests { #[test] fn word_normal_comes_in_as_its_own_style() { // Regression (#722): Normal's formatting (over Word's document defaults) was dropped. - use std::io::Write; const W: &str = r#"xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main""#; let styles = format!( r#" "# ); let doc = format!(r#"Plain"#); - let mut w = zip::ZipWriter::new(std::io::Cursor::new(Vec::new())); - for (n, c) in [("word/document.xml", doc), ("word/styles.xml", styles)] { - w.start_file(n, zip::write::SimpleFileOptions::default()).unwrap(); - w.write_all(c.as_bytes()).unwrap(); - } - let b64 = crate::cmd::base64_encode(&w.finish().unwrap().into_inner()); + let b64 = crate::cmd::base64_encode(&super::word_package(&doc, &styles)); let basic = designcraft_doc::story::BASIC_PARAGRAPH; let placed = |s: &Session, story: &serde_json::Value| { let d = &s.doc().unwrap().doc; diff --git a/crates/engine/src/cmd/place_text/tests.rs b/crates/engine/src/cmd/place_text/tests.rs index 8275e7ab2..cc8fda19a 100644 --- a/crates/engine/src/cmd/place_text/tests.rs +++ b/crates/engine/src/cmd/place_text/tests.rs @@ -311,7 +311,6 @@ fn autoflow_adds_the_pages_odd_and_even_page_breaks_ask_for() { #[test] fn autoflow_sizes_no_frame_by_one_a_start_option_ends_early() { // Regression (#1209): the title frame a start option ends early isn't a frame's worth of text. - use std::io::Write; const W: &str = r#"xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main""#; let para = |style: &str, text: &str| format!(r#"{text}"#); let body = [para("Title", "Front"), para("Chapter", "Chapter one"), para("Body", "lorem ipsum dolor sit amet ".repeat(60).trim())].concat(); @@ -319,16 +318,8 @@ fn autoflow_sizes_no_frame_by_one_a_start_option_ends_early() { .iter() .map(|s| format!(r#""#)) .collect(); - let mut w = zip::ZipWriter::new(std::io::Cursor::new(Vec::new())); - let o = zip::write::SimpleFileOptions::default(); - for (n, c) in [ - ("word/document.xml", format!(r#"{body}"#)), - ("word/styles.xml", format!(r#"{styles}"#)), - ] { - w.start_file(n, o).unwrap(); - w.write_all(c.as_bytes()).unwrap(); - } - let docx = w.finish().unwrap().into_inner(); + let docx = + super::word_package(&format!(r#"{body}"#), &format!(r#"{styles}"#)); for (start, pages) in [("nextOddPage", 5), ("nextEvenPage", 4)] { let mut s = Session::new(); s.execute("file.new", &json!({"pages": 3, "facingPages": true})).unwrap();