diff --git a/docx-core/examples/sdt.rs b/docx-core/examples/sdt.rs new file mode 100644 index 0000000..1bfdf35 --- /dev/null +++ b/docx-core/examples/sdt.rs @@ -0,0 +1,16 @@ +use docx_rs::*; + +pub fn main() -> Result<(), DocxError> { + let path = std::path::Path::new("./output/sdt.docx"); + let file = std::fs::File::create(&path).unwrap(); + let p = Paragraph::new().add_run( + Run::new() + .add_text("Hello") + .fonts(RunFonts::new().ascii("Arial")), + ); + Docx::new() + .add_structured_data_tag(StructuredDataTag::new().add_paragraph(p)) + .build() + .pack(file)?; + Ok(()) +} diff --git a/docx-core/src/documents/document.rs b/docx-core/src/documents/document.rs index 374cd38..0a4c468 100644 --- a/docx-core/src/documents/document.rs +++ b/docx-core/src/documents/document.rs @@ -21,6 +21,7 @@ pub enum DocumentChild { BookmarkEnd(BookmarkEnd), CommentStart(Box), CommentEnd(CommentRangeEnd), + StructuredDataTag(StructuredDataTag), } impl Serialize for DocumentChild { @@ -65,6 +66,12 @@ impl Serialize for DocumentChild { t.serialize_field("data", r)?; t.end() } + DocumentChild::StructuredDataTag(ref r) => { + let mut t = serializer.serialize_struct("StructuredDataTag", 2)?; + t.serialize_field("type", "structuredDataTag")?; + t.serialize_field("data", r)?; + t.end() + } } } } @@ -179,6 +186,14 @@ impl Document { self.section_property = self.section_property.even_footer(h, rid); self } + + pub fn add_structured_data_tag(mut self, t: StructuredDataTag) -> Self { + if t.has_numbering { + self.has_numbering = true + } + self.children.push(DocumentChild::StructuredDataTag(t)); + self + } } impl BuildXML for DocumentChild { @@ -190,6 +205,7 @@ impl BuildXML for DocumentChild { DocumentChild::BookmarkEnd(v) => v.build(), DocumentChild::CommentStart(v) => v.build(), DocumentChild::CommentEnd(v) => v.build(), + DocumentChild::StructuredDataTag(v) => v.build(), } } } diff --git a/docx-core/src/documents/elements/mod.rs b/docx-core/src/documents/elements/mod.rs index 224ba1c..73eb3e6 100644 --- a/docx-core/src/documents/elements/mod.rs +++ b/docx-core/src/documents/elements/mod.rs @@ -23,9 +23,9 @@ mod doc_id; mod doc_var; mod drawing; mod font; +mod footer_reference; mod grid_span; mod header_reference; -mod footer_reference; mod highlight; mod indent; mod indent_level; @@ -62,6 +62,7 @@ mod section; mod section_property; mod shading; mod start; +mod structured_data_tag; mod style; mod sz; mod sz_cs; @@ -120,9 +121,9 @@ pub use doc_id::*; pub use doc_var::*; pub use drawing::*; pub use font::*; +pub use footer_reference::*; pub use grid_span::*; pub use header_reference::*; -pub use footer_reference::*; pub use highlight::*; pub use indent::*; pub use indent_level::*; @@ -159,6 +160,7 @@ pub use section::*; pub use section_property::*; pub use shading::*; pub use start::*; +pub use structured_data_tag::*; pub use style::*; pub use sz::*; pub use sz_cs::*; diff --git a/docx-core/src/documents/elements/structured_data_tag.rs b/docx-core/src/documents/elements/structured_data_tag.rs new file mode 100644 index 0000000..f602188 --- /dev/null +++ b/docx-core/src/documents/elements/structured_data_tag.rs @@ -0,0 +1,118 @@ +use serde::ser::{SerializeStruct, Serializer}; +use serde::Serialize; + +use super::*; +use crate::documents::BuildXML; +// use crate::types::*; +use crate::xml_builder::*; + +#[derive(Serialize, Debug, Clone, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct StructuredDataTag { + pub children: Vec, + pub has_numbering: bool, +} + +impl Default for StructuredDataTag { + fn default() -> Self { + Self { + children: Vec::new(), + has_numbering: false, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum StructuredDataTagChild { + Run(Box), + Paragraph(Box), +} + +impl BuildXML for StructuredDataTagChild { + fn build(&self) -> Vec { + match self { + StructuredDataTagChild::Run(v) => v.build(), + StructuredDataTagChild::Paragraph(v) => v.build(), + } + } +} + +impl Serialize for StructuredDataTagChild { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + StructuredDataTagChild::Run(ref r) => { + let mut t = serializer.serialize_struct("Run", 2)?; + t.serialize_field("type", "run")?; + t.serialize_field("data", r)?; + t.end() + } + StructuredDataTagChild::Paragraph(ref r) => { + let mut t = serializer.serialize_struct("Paragraph", 2)?; + t.serialize_field("type", "paragraph")?; + t.serialize_field("data", r)?; + t.end() + } + } + } +} + +impl StructuredDataTag { + pub fn new() -> Self { + Default::default() + } + + pub fn add_run(mut self, run: Run) -> Self { + self.children + .push(StructuredDataTagChild::Run(Box::new(run))); + self + } + + pub fn add_paragraph(mut self, p: Paragraph) -> Self { + if p.has_numbering { + self.has_numbering = true + } + self.children + .push(StructuredDataTagChild::Paragraph(Box::new(p))); + self + } +} + +impl BuildXML for StructuredDataTag { + fn build(&self) -> Vec { + XMLBuilder::new() + .open_structured_tag() + .open_structured_tag_property() + .close() + .open_structured_tag_content() + .add_children(&self.children) + .close() + .close() + .build() + } +} + +#[cfg(test)] +mod tests { + + use super::*; + #[cfg(test)] + use pretty_assertions::assert_eq; + use std::str; + + #[test] + fn test_sdt() { + let b = StructuredDataTag::new() + .add_run(Run::new().add_text("Hello")) + .build(); + assert_eq!( + str::from_utf8(&b).unwrap(), + r#" + + Hello +"# + ); + } +} diff --git a/docx-core/src/documents/mod.rs b/docx-core/src/documents/mod.rs index 49588e0..217dd5f 100644 --- a/docx-core/src/documents/mod.rs +++ b/docx-core/src/documents/mod.rs @@ -191,6 +191,16 @@ impl Docx { self } + pub fn add_structured_data_tag(mut self, t: StructuredDataTag) -> Docx { + if t.has_numbering { + // If this document has numbering, set numberings.xml to document_rels. + // This is because numberings.xml without numbering cause an error on word online. + self.document_rels.has_numberings = true; + } + self.document = self.document.add_structured_data_tag(t); + self + } + pub fn add_bookmark_start(mut self, id: usize, name: impl Into) -> Docx { self.document = self.document.add_bookmark_start(id, name); self diff --git a/docx-core/src/xml_builder/elements.rs b/docx-core/src/xml_builder/elements.rs index a3cf268..c7389da 100644 --- a/docx-core/src/xml_builder/elements.rs +++ b/docx-core/src/xml_builder/elements.rs @@ -72,6 +72,11 @@ impl XMLBuilder { open!(open_paragraph, "w:p", "w14:paraId"); open!(open_paragraph_property, "w:pPr"); open!(open_doc_defaults, "w:docDefaults"); + + open!(open_structured_tag, "w:sdt"); + open!(open_structured_tag_content, "w:sdtContent"); + open!(open_structured_tag_property, "w:sdtPr"); + // i.e. closed_with_usize!(outline_lvl, "w:outlineLvl"); // i.e.