diff --git a/docx-core/examples/data_binding.rs b/docx-core/examples/data_binding.rs
new file mode 100644
index 0000000..842db3c
--- /dev/null
+++ b/docx-core/examples/data_binding.rs
@@ -0,0 +1,23 @@
+use docx_rs::*;
+
+pub fn main() -> Result<(), DocxError> {
+ let path = std::path::Path::new("./output/data_binding.docx");
+ let file = std::fs::File::create(&path).unwrap();
+ Docx::new()
+ .add_paragraph(
+ Paragraph::new()
+ .add_structured_data_tag(
+ StructuredDataTag::new().data_binding(DataBinding::new().xpath("/root/item1")),
+ )
+ .add_structured_data_tag(
+ StructuredDataTag::new().data_binding(DataBinding::new().xpath("/root/item2")),
+ ),
+ )
+ .add_custom_item(
+ "06AC5857-5C65-A94A-BCEC-37356A209BC3",
+ "Hello World!",
+ )
+ .build()
+ .pack(file)?;
+ Ok(())
+}
diff --git a/docx-core/src/documents/custom_item.rs b/docx-core/src/documents/custom_item.rs
index f56fb87..91580a0 100644
--- a/docx-core/src/documents/custom_item.rs
+++ b/docx-core/src/documents/custom_item.rs
@@ -44,19 +44,17 @@ mod tests {
#[test]
fn test_custom_xml() {
let c = CustomItem::from_str(
- r#"
-
-
-
-
- "#,
+ r#"
+
+"#,
)
.unwrap();
assert_eq!(
c.0.to_string(),
- "\n \n \n \n\n \n\n\n"
+ r#"
+
+"#
);
assert_eq!(
serde_json::to_string(&c).unwrap(),
diff --git a/docx-core/src/documents/elements/data_binding.rs b/docx-core/src/documents/elements/data_binding.rs
new file mode 100644
index 0000000..82ab173
--- /dev/null
+++ b/docx-core/src/documents/elements/data_binding.rs
@@ -0,0 +1,62 @@
+use serde::Serialize;
+
+use crate::documents::*;
+use crate::xml_builder::*;
+
+#[derive(Serialize, Debug, Clone, PartialEq, Default)]
+pub struct DataBinding {
+ pub xpath: Option,
+ pub prefix_mappings: Option,
+ pub store_item_id: Option,
+}
+
+impl DataBinding {
+ pub fn new() -> Self {
+ Default::default()
+ }
+
+ pub fn xpath(mut self, xpath: impl Into) -> Self {
+ self.xpath = Some(xpath.into());
+ self
+ }
+
+ pub fn prefix_mappings(mut self, m: impl Into) -> Self {
+ self.prefix_mappings = Some(m.into());
+ self
+ }
+
+ pub fn store_item_id(mut self, id: impl Into) -> Self {
+ self.store_item_id = Some(id.into());
+ self
+ }
+}
+
+impl BuildXML for DataBinding {
+ fn build(&self) -> Vec {
+ XMLBuilder::new()
+ .data_binding(
+ self.xpath.as_ref(),
+ self.prefix_mappings.as_ref(),
+ self.store_item_id.as_ref(),
+ )
+ .build()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+
+ use super::*;
+ #[cfg(test)]
+ use pretty_assertions::assert_eq;
+ use std::str;
+
+ #[test]
+ fn test_delete_default() {
+ let b = DataBinding::new().xpath("root/hello").build();
+ assert_eq!(
+ str::from_utf8(&b).unwrap(),
+ r#""#
+ );
+ }
+}
diff --git a/docx-core/src/documents/elements/mod.rs b/docx-core/src/documents/elements/mod.rs
index df9cb1f..87d0378 100644
--- a/docx-core/src/documents/elements/mod.rs
+++ b/docx-core/src/documents/elements/mod.rs
@@ -13,6 +13,7 @@ mod comment;
mod comment_extended;
mod comment_range_end;
mod comment_range_start;
+mod data_binding;
mod default_tab_stop;
mod delete;
mod delete_text;
@@ -65,6 +66,7 @@ mod section_property;
mod shading;
mod start;
mod structured_data_tag;
+mod structured_data_tag_property;
mod style;
mod sz;
mod sz_cs;
@@ -114,6 +116,7 @@ pub use comment::*;
pub use comment_extended::*;
pub use comment_range_end::*;
pub use comment_range_start::*;
+pub use data_binding::*;
pub use default_tab_stop::*;
pub use delete::*;
pub use delete_text::*;
@@ -166,6 +169,7 @@ pub use section_property::*;
pub use shading::*;
pub use start::*;
pub use structured_data_tag::*;
+pub use structured_data_tag_property::*;
pub use style::*;
pub use sz::*;
pub use sz_cs::*;
diff --git a/docx-core/src/documents/elements/paragraph.rs b/docx-core/src/documents/elements/paragraph.rs
index 01439ed..41137e3 100644
--- a/docx-core/src/documents/elements/paragraph.rs
+++ b/docx-core/src/documents/elements/paragraph.rs
@@ -35,6 +35,7 @@ pub enum ParagraphChild {
BookmarkEnd(BookmarkEnd),
CommentStart(Box),
CommentEnd(CommentRangeEnd),
+ StructuredDataTag(Box),
}
impl BuildXML for ParagraphChild {
@@ -47,6 +48,7 @@ impl BuildXML for ParagraphChild {
ParagraphChild::BookmarkEnd(v) => v.build(),
ParagraphChild::CommentStart(v) => v.build(),
ParagraphChild::CommentEnd(v) => v.build(),
+ ParagraphChild::StructuredDataTag(v) => v.build(),
}
}
}
@@ -99,6 +101,12 @@ impl Serialize for ParagraphChild {
t.serialize_field("data", r)?;
t.end()
}
+ ParagraphChild::StructuredDataTag(ref r) => {
+ let mut t = serializer.serialize_struct("StructuredDataTag", 2)?;
+ t.serialize_field("type", "structuredDataTag")?;
+ t.serialize_field("data", r)?;
+ t.end()
+ }
}
}
}
@@ -122,6 +130,12 @@ impl Paragraph {
self
}
+ pub fn add_structured_data_tag(mut self, t: StructuredDataTag) -> Self {
+ self.children
+ .push(ParagraphChild::StructuredDataTag(Box::new(t)));
+ self
+ }
+
pub fn add_insert(mut self, insert: Insert) -> Paragraph {
self.children.push(ParagraphChild::Insert(insert));
self
diff --git a/docx-core/src/documents/elements/structured_data_tag.rs b/docx-core/src/documents/elements/structured_data_tag.rs
index f602188..84478a0 100644
--- a/docx-core/src/documents/elements/structured_data_tag.rs
+++ b/docx-core/src/documents/elements/structured_data_tag.rs
@@ -10,6 +10,7 @@ use crate::xml_builder::*;
#[serde(rename_all = "camelCase")]
pub struct StructuredDataTag {
pub children: Vec,
+ pub property: StructuredDataTagProperty,
pub has_numbering: bool,
}
@@ -17,6 +18,7 @@ impl Default for StructuredDataTag {
fn default() -> Self {
Self {
children: Vec::new(),
+ property: StructuredDataTagProperty::new(),
has_numbering: false,
}
}
@@ -78,14 +80,18 @@ impl StructuredDataTag {
.push(StructuredDataTagChild::Paragraph(Box::new(p)));
self
}
+
+ pub fn data_binding(mut self, d: DataBinding) -> Self {
+ self.property = self.property.data_binding(d);
+ self
+ }
}
impl BuildXML for StructuredDataTag {
fn build(&self) -> Vec {
XMLBuilder::new()
.open_structured_tag()
- .open_structured_tag_property()
- .close()
+ .add_child(&self.property)
.open_structured_tag_content()
.add_children(&self.children)
.close()
@@ -105,13 +111,12 @@ mod tests {
#[test]
fn test_sdt() {
let b = StructuredDataTag::new()
+ .data_binding(DataBinding::new().xpath("root/hello"))
.add_run(Run::new().add_text("Hello"))
.build();
assert_eq!(
str::from_utf8(&b).unwrap(),
- r#"
-
- Hello
+ r#"Hello
"#
);
}
diff --git a/docx-core/src/documents/elements/structured_data_tag_property.rs b/docx-core/src/documents/elements/structured_data_tag_property.rs
new file mode 100644
index 0000000..8541e1c
--- /dev/null
+++ b/docx-core/src/documents/elements/structured_data_tag_property.rs
@@ -0,0 +1,62 @@
+use serde::Serialize;
+
+use super::*;
+use crate::documents::BuildXML;
+use crate::xml_builder::*;
+
+#[derive(Serialize, Debug, Clone, PartialEq)]
+#[serde(rename_all = "camelCase")]
+pub struct StructuredDataTagProperty {
+ pub run_property: RunProperty,
+ pub data_binding: Option,
+}
+
+impl Default for StructuredDataTagProperty {
+ fn default() -> Self {
+ StructuredDataTagProperty {
+ run_property: RunProperty::new(),
+ data_binding: None,
+ }
+ }
+}
+
+impl StructuredDataTagProperty {
+ pub fn new() -> Self {
+ Default::default()
+ }
+
+ pub fn data_binding(mut self, d: DataBinding) -> Self {
+ self.data_binding = Some(d);
+ self
+ }
+}
+
+impl BuildXML for StructuredDataTagProperty {
+ fn build(&self) -> Vec {
+ XMLBuilder::new()
+ .open_structured_tag_property()
+ .add_child(&self.run_property)
+ .add_optional_child(&self.data_binding)
+ .close()
+ .build()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+
+ use super::*;
+ #[cfg(test)]
+ use pretty_assertions::assert_eq;
+ use std::str;
+
+ #[test]
+ fn test_default() {
+ let c = StructuredDataTagProperty::new();
+ let b = c.build();
+ assert_eq!(
+ str::from_utf8(&b).unwrap(),
+ r#""#
+ );
+ }
+}
diff --git a/docx-core/src/xml_builder/elements.rs b/docx-core/src/xml_builder/elements.rs
index 882b97d..83c4a83 100644
--- a/docx-core/src/xml_builder/elements.rs
+++ b/docx-core/src/xml_builder/elements.rs
@@ -61,6 +61,27 @@ impl XMLBuilder {
self.writer.write(text).expect(EXPECT_MESSAGE);
self.close()
}
+
+ pub(crate) fn data_binding(
+ mut self,
+ xpath: Option<&String>,
+ prefix_mappings: Option<&String>,
+ store_item_id: Option<&String>,
+ ) -> Self {
+ let mut e = XmlEvent::start_element("w:dataBinding");
+ if let Some(xpath) = xpath {
+ e = e.attr("w:xpath", xpath);
+ }
+ if let Some(prefix_mappings) = prefix_mappings {
+ e = e.attr("w:prefixMappings", prefix_mappings);
+ }
+ if let Some(store_item_id) = store_item_id {
+ e = e.attr("w:storeItemID", store_item_id);
+ }
+ self.writer.write(e).expect(EXPECT_MESSAGE);
+ self.close()
+ }
+
// i.e.
open!(open_run, "w:r");
open!(open_run_property, "w:rPr");
diff --git a/docx-core/src/xml_json/mod.rs b/docx-core/src/xml_json/mod.rs
index 2a54cf4..350de50 100644
--- a/docx-core/src/xml_json/mod.rs
+++ b/docx-core/src/xml_json/mod.rs
@@ -39,7 +39,7 @@ impl fmt::Display for XmlDocument {
/// An XML Tag
///
-/// For exammple:
+/// For example:
///
/// ```XML
///
@@ -60,12 +60,12 @@ pub struct XmlData {
}
// Generate indentation
-fn indent(size: usize) -> String {
- const INDENT: &str = " ";
- (0..size)
- .map(|_| INDENT)
- .fold(String::with_capacity(size * INDENT.len()), |r, s| r + s)
-}
+// fn indent(size: usize) -> String {
+// const INDENT: &str = " ";
+// (0..size)
+// .map(|_| INDENT)
+// .fold(String::with_capacity(size * INDENT.len()), |r, s| r + s)
+// }
// Get the attributes as a string
fn attributes_to_string(attributes: &[(String, String)]) -> String {
@@ -88,22 +88,20 @@ fn format(data: &XmlData, depth: usize) -> String {
sub
};
- let indt = indent(depth);
+ // let indt = indent(depth);
let fmt_data = if let Some(ref d) = data.data {
- format!("\n{}{}", indent(depth + 1), d)
+ format!("\n{}", d)
} else {
"".to_string()
};
format!(
- "{}<{}{}>{}{}\n{}{}>\n",
- indt,
+ "<{}{}>{}{}{}>",
data.name,
attributes_to_string(&data.attributes),
fmt_data,
sub,
- indt,
data.name
)
}
diff --git a/docx-wasm/test/__snapshots__/index.test.js.snap b/docx-wasm/test/__snapshots__/index.test.js.snap
index 6d27071..65a32e4 100644
--- a/docx-wasm/test/__snapshots__/index.test.js.snap
+++ b/docx-wasm/test/__snapshots__/index.test.js.snap
@@ -25315,13 +25315,7 @@ exports[`writer should write customItem 6`] = `
exports[`writer should write customItem 7`] = `
"
- -
-
- -
-
-
-
-"
+ "
`;
exports[`writer should write default font 1`] = `