From ff86d6fa67905ffdeaa410b0207a71dedbbd842b Mon Sep 17 00:00:00 2001 From: 14sxlin Date: Fri, 10 Sep 2021 08:42:08 +0800 Subject: [PATCH] add paragraph propertie outlineLvl support (#335) * add support for outlineLvl of ParagraphPropertiy * add outline_lvl method to Paragraph * add outlineLvl example * update test case about json serializer output --- docx-core/examples/outline_lvl.rs | 37 ++++++++++++++++++ docx-core/src/documents/elements/a_graphic.rs | 2 +- .../documents/elements/abstract_numbering.rs | 2 +- docx-core/src/documents/elements/mod.rs | 2 + .../src/documents/elements/outline_lvl.rs | 38 +++++++++++++++++++ docx-core/src/documents/elements/paragraph.rs | 9 ++++- .../documents/elements/paragraph_property.rs | 22 ++++++++++- .../src/documents/elements/table_cell.rs | 2 +- docx-core/src/xml_builder/elements.rs | 2 + 9 files changed, 109 insertions(+), 7 deletions(-) create mode 100644 docx-core/examples/outline_lvl.rs create mode 100644 docx-core/src/documents/elements/outline_lvl.rs diff --git a/docx-core/examples/outline_lvl.rs b/docx-core/examples/outline_lvl.rs new file mode 100644 index 0000000..1791d16 --- /dev/null +++ b/docx-core/examples/outline_lvl.rs @@ -0,0 +1,37 @@ +use docx_rs::*; + +fn main() -> Result<(), DocxError> { + let path = std::path::Path::new("./outlineLvl.docx"); + let file = std::fs::File::create(&path).unwrap(); + + Docx::new() + .add_paragraph( + Paragraph::new() + .add_run(Run::new().add_text("Title1")) + .outline_lvl(1), + ) + .add_paragraph( + Paragraph::new() + .add_run(Run::new().add_text("Title2")) + .outline_lvl(1), + ) + .add_paragraph( + Paragraph::new() + .add_run(Run::new().add_text("Title2-1")) + .outline_lvl(2), + ) + .add_paragraph( + Paragraph::new() + .add_run(Run::new().add_text("Title2-2")) + .outline_lvl(2), + ) + .add_paragraph( + Paragraph::new() + .add_run(Run::new().add_text("Title3")) + .outline_lvl(1), + ) + .build() + .pack(file)?; + + Ok(()) +} diff --git a/docx-core/src/documents/elements/a_graphic.rs b/docx-core/src/documents/elements/a_graphic.rs index 19974f3..bc52cb6 100644 --- a/docx-core/src/documents/elements/a_graphic.rs +++ b/docx-core/src/documents/elements/a_graphic.rs @@ -59,7 +59,7 @@ mod tests { ); assert_eq!( serde_json::to_string(&graphic).unwrap(), - r#"{"children":[{"dataType":"wpShape","children":[{"type":"shape","data":{"children":[{"type":"textbox","data":{"children":[{"children":[{"type":"paragraph","data":{"id":"12345678","children":[{"type":"run","data":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"children":[{"type":"text","data":{"preserveSpace":true,"text":"pattern1"}}]}}],"property":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":null,"lineHeight":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"divId":null},"hasNumbering":false}}],"has_numbering":false}],"hasNumbering":false}}]}}]}]}"#, + r#"{"children":[{"dataType":"wpShape","children":[{"type":"shape","data":{"children":[{"type":"textbox","data":{"children":[{"children":[{"type":"paragraph","data":{"id":"12345678","children":[{"type":"run","data":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"children":[{"type":"text","data":{"preserveSpace":true,"text":"pattern1"}}]}}],"property":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":null,"lineHeight":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"outlineLvl":null,"divId":null},"hasNumbering":false}}],"has_numbering":false}],"hasNumbering":false}}]}}]}]}"#, ); } } diff --git a/docx-core/src/documents/elements/abstract_numbering.rs b/docx-core/src/documents/elements/abstract_numbering.rs index 7f99290..39a8d1c 100644 --- a/docx-core/src/documents/elements/abstract_numbering.rs +++ b/docx-core/src/documents/elements/abstract_numbering.rs @@ -90,7 +90,7 @@ mod tests { .num_style_link("style1"); assert_eq!( serde_json::to_string(&c).unwrap(), - r#"{"id":0,"styleLink":null,"numStyleLink":"style1","levels":[{"level":1,"start":1,"format":"decimal","text":"%4.","jc":"left","paragraphProperty":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":null,"lineHeight":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"divId":null},"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"suffix":"tab","pstyle":null,"levelRestart":null}]}"#, + r#"{"id":0,"styleLink":null,"numStyleLink":"style1","levels":[{"level":1,"start":1,"format":"decimal","text":"%4.","jc":"left","paragraphProperty":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":null,"lineHeight":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"outlineLvl":null,"divId":null},"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"suffix":"tab","pstyle":null,"levelRestart":null}]}"#, ); } } diff --git a/docx-core/src/documents/elements/mod.rs b/docx-core/src/documents/elements/mod.rs index d04fc12..538545b 100644 --- a/docx-core/src/documents/elements/mod.rs +++ b/docx-core/src/documents/elements/mod.rs @@ -43,6 +43,7 @@ mod number_format; mod numbering; mod numbering_id; mod numbering_property; +mod outline_lvl; mod page_margin; mod page_size; mod paragraph; @@ -137,6 +138,7 @@ pub use number_format::*; pub use numbering::*; pub use numbering_id::*; pub use numbering_property::*; +pub use outline_lvl::*; pub use page_margin::*; pub use page_size::*; pub use paragraph::*; diff --git a/docx-core/src/documents/elements/outline_lvl.rs b/docx-core/src/documents/elements/outline_lvl.rs new file mode 100644 index 0000000..e45828d --- /dev/null +++ b/docx-core/src/documents/elements/outline_lvl.rs @@ -0,0 +1,38 @@ +use crate::documents::BuildXML; +use crate::xml_builder::*; + +use serde::Serialize; + +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct OutlineLvl { + pub v: usize, +} + +impl OutlineLvl { + pub fn new(v: usize) -> OutlineLvl { + OutlineLvl { v } + } +} + +impl BuildXML for OutlineLvl { + fn build(&self) -> Vec { + XMLBuilder::new() + .outline_lvl(self.v) + // .close() + .build() + } +} + +#[cfg(test)] +mod tests { + use crate::{BuildXML, OutlineLvl}; + + + #[test] + fn test_outline_lvl_build(){ + let bytes = OutlineLvl::new(1).build(); + assert_eq!(std::str::from_utf8(&bytes).unwrap(),r#""#); + } + +} diff --git a/docx-core/src/documents/elements/paragraph.rs b/docx-core/src/documents/elements/paragraph.rs index a0a0da8..d51e070 100644 --- a/docx-core/src/documents/elements/paragraph.rs +++ b/docx-core/src/documents/elements/paragraph.rs @@ -177,6 +177,11 @@ impl Paragraph { self } + pub fn outline_lvl(mut self, v: usize) -> Self { + self.property = self.property.outline_lvl(v); + self + } + pub fn page_break_before(mut self, v: bool) -> Self { self.property = self.property.page_break_before(v); self @@ -325,7 +330,7 @@ mod tests { let p = Paragraph::new().add_run(run); assert_eq!( serde_json::to_string(&p).unwrap(), - r#"{"id":"12345678","children":[{"type":"run","data":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"children":[{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}]}}],"property":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":null,"lineHeight":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"divId":null},"hasNumbering":false}"#, + r#"{"id":"12345678","children":[{"type":"run","data":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"children":[{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}]}}],"property":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":null,"lineHeight":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"outlineLvl":null,"divId":null},"hasNumbering":false}"#, ); } @@ -336,7 +341,7 @@ mod tests { let p = Paragraph::new().add_insert(ins); assert_eq!( serde_json::to_string(&p).unwrap(), - r#"{"id":"12345678","children":[{"type":"insert","data":{"children":[{"type":"run","data":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"children":[{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}]}}],"author":"unnamed","date":"1970-01-01T00:00:00Z"}}],"property":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":null,"lineHeight":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"divId":null},"hasNumbering":false}"# + r#"{"id":"12345678","children":[{"type":"insert","data":{"children":[{"type":"run","data":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"children":[{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}]}}],"author":"unnamed","date":"1970-01-01T00:00:00Z"}}],"property":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":null,"lineHeight":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"outlineLvl":null,"divId":null},"hasNumbering":false}"# ); } } diff --git a/docx-core/src/documents/elements/paragraph_property.rs b/docx-core/src/documents/elements/paragraph_property.rs index 17584fb..f485dc4 100644 --- a/docx-core/src/documents/elements/paragraph_property.rs +++ b/docx-core/src/documents/elements/paragraph_property.rs @@ -18,6 +18,7 @@ pub struct ParagraphProperty { pub keep_lines: bool, pub page_break_before: bool, pub window_control: bool, + pub outline_lvl: Option, // read only pub(crate) div_id: Option, } @@ -35,6 +36,7 @@ impl Default for ParagraphProperty { keep_lines: false, page_break_before: false, window_control: false, + outline_lvl: None, div_id: None, } } @@ -91,6 +93,11 @@ impl ParagraphProperty { self } + pub fn outline_lvl(mut self, v: usize) -> Self { + self.outline_lvl = Some(OutlineLvl::new(v)); + self + } + pub fn page_break_before(mut self, v: bool) -> Self { self.page_break_before = v; self @@ -130,7 +137,8 @@ impl BuildXML for ParagraphProperty { .add_optional_child(&self.numbering_property) .add_optional_child(&self.alignment) .add_optional_child(&self.indent) - .add_optional_child(&spacing); + .add_optional_child(&spacing) + .add_optional_child(&self.outline_lvl); if self.keep_next { b = b.keep_next() @@ -198,13 +206,23 @@ mod tests { ); } + #[test] + fn test_outline_lvl() { + let props = ParagraphProperty::new(); + let bytes = props.outline_lvl(1).build(); + assert_eq!( + str::from_utf8(&bytes).unwrap(), + r#""# + ) + } + #[test] fn test_indent_json() { let c = ParagraphProperty::new(); let b = c.indent(Some(20), Some(SpecialIndentType::FirstLine(10)), None, None); assert_eq!( serde_json::to_string(&b).unwrap(), - r#"{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":{"start":20,"startChars":null,"end":null,"specialIndent":{"type":"firstLine","val":10},"hangingChars":null,"firstLineChars":null},"lineHeight":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"divId":null}"# + r#"{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":{"start":20,"startChars":null,"end":null,"specialIndent":{"type":"firstLine","val":10},"hangingChars":null,"firstLineChars":null},"lineHeight":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"outlineLvl":null,"divId":null}"# ); } } diff --git a/docx-core/src/documents/elements/table_cell.rs b/docx-core/src/documents/elements/table_cell.rs index a97bea2..95019d5 100644 --- a/docx-core/src/documents/elements/table_cell.rs +++ b/docx-core/src/documents/elements/table_cell.rs @@ -185,7 +185,7 @@ mod tests { .grid_span(2); assert_eq!( serde_json::to_string(&c).unwrap(), - r#"{"children":[{"type":"paragraph","data":{"id":"12345678","children":[{"type":"run","data":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"children":[{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}]}}],"property":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":null,"lineHeight":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"divId":null},"hasNumbering":false}}],"property":{"width":null,"borders":null,"gridSpan":2,"verticalMerge":null,"verticalAlign":null,"textDirection":null,"shading":null},"hasNumbering":false}"#, + r#"{"children":[{"type":"paragraph","data":{"id":"12345678","children":[{"type":"run","data":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"children":[{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}]}}],"property":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":null,"lineHeight":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"outlineLvl":null,"divId":null},"hasNumbering":false}}],"property":{"width":null,"borders":null,"gridSpan":2,"verticalMerge":null,"verticalAlign":null,"textDirection":null,"shading":null},"hasNumbering":false}"#, ); } } diff --git a/docx-core/src/xml_builder/elements.rs b/docx-core/src/xml_builder/elements.rs index 6f7bcad..6318e18 100644 --- a/docx-core/src/xml_builder/elements.rs +++ b/docx-core/src/xml_builder/elements.rs @@ -71,6 +71,8 @@ impl XMLBuilder { open!(open_paragraph, "w:p", "w14:paraId"); open!(open_paragraph_property, "w:pPr"); open!(open_doc_defaults, "w:docDefaults"); + // i.e. + closed_with_usize!(outline_lvl, "w:outlineLvl"); // i.e. closed_with_str!(name, "w:name"); // i.e.