parent
1850ac2018
commit
54b7a4d20c
|
@ -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",
|
||||
"<root><item1>Hello</item1><item2> World!</item2></root>",
|
||||
)
|
||||
.build()
|
||||
.pack(file)?;
|
||||
Ok(())
|
||||
}
|
|
@ -44,19 +44,17 @@ mod tests {
|
|||
#[test]
|
||||
fn test_custom_xml() {
|
||||
let c = CustomItem::from_str(
|
||||
r#"<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<ds:datastoreItem ds:itemID="{06AC5857-5C65-A94A-BCEC-37356A209BC3}"
|
||||
xmlns:ds="http://schemas.openxmlformats.org/officeDocument/2006/customXml">
|
||||
<ds:schemaRefs>
|
||||
<ds:schemaRef ds:uri="https://hoge.com"/>
|
||||
</ds:schemaRefs>
|
||||
</ds:datastoreItem>"#,
|
||||
r#"<ds:datastoreItem ds:itemID="{06AC5857-5C65-A94A-BCEC-37356A209BC3}" xmlns:ds="http://schemas.openxmlformats.org/officeDocument/2006/customXml">
|
||||
<ds:schemaRefs>
|
||||
<ds:schemaRef ds:uri="https://hoge.com"></ds:schemaRef></ds:schemaRefs></ds:datastoreItem>"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
c.0.to_string(),
|
||||
"<ds:datastoreItem ds:itemID=\"{06AC5857-5C65-A94A-BCEC-37356A209BC3}\" xmlns:ds=\"http://schemas.openxmlformats.org/officeDocument/2006/customXml\">\n <ds:schemaRefs>\n <ds:schemaRef ds:uri=\"https://hoge.com\">\n </ds:schemaRef>\n\n </ds:schemaRefs>\n\n</ds:datastoreItem>\n"
|
||||
r#"<ds:datastoreItem ds:itemID="{06AC5857-5C65-A94A-BCEC-37356A209BC3}" xmlns:ds="http://schemas.openxmlformats.org/officeDocument/2006/customXml">
|
||||
<ds:schemaRefs>
|
||||
<ds:schemaRef ds:uri="https://hoge.com"></ds:schemaRef></ds:schemaRefs></ds:datastoreItem>"#
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::to_string(&c).unwrap(),
|
||||
|
|
|
@ -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<String>,
|
||||
pub prefix_mappings: Option<String>,
|
||||
pub store_item_id: Option<String>,
|
||||
}
|
||||
|
||||
impl DataBinding {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn xpath(mut self, xpath: impl Into<String>) -> Self {
|
||||
self.xpath = Some(xpath.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn prefix_mappings(mut self, m: impl Into<String>) -> Self {
|
||||
self.prefix_mappings = Some(m.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn store_item_id(mut self, id: impl Into<String>) -> Self {
|
||||
self.store_item_id = Some(id.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl BuildXML for DataBinding {
|
||||
fn build(&self) -> Vec<u8> {
|
||||
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#"<w:dataBinding w:xpath="root/hello" />"#
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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::*;
|
||||
|
|
|
@ -35,6 +35,7 @@ pub enum ParagraphChild {
|
|||
BookmarkEnd(BookmarkEnd),
|
||||
CommentStart(Box<CommentRangeStart>),
|
||||
CommentEnd(CommentRangeEnd),
|
||||
StructuredDataTag(Box<StructuredDataTag>),
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -10,6 +10,7 @@ use crate::xml_builder::*;
|
|||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StructuredDataTag {
|
||||
pub children: Vec<StructuredDataTagChild>,
|
||||
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<u8> {
|
||||
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#"<w:sdt>
|
||||
<w:sdtPr />
|
||||
<w:sdtContent><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r></w:sdtContent>
|
||||
r#"<w:sdt><w:sdtPr><w:rPr /><w:dataBinding w:xpath="root/hello" /></w:sdtPr><w:sdtContent><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r></w:sdtContent>
|
||||
</w:sdt>"#
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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<DataBinding>,
|
||||
}
|
||||
|
||||
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<u8> {
|
||||
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#"<w:sdtPr><w:rPr /></w:sdtPr>"#
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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. <w:r ... >
|
||||
open!(open_run, "w:r");
|
||||
open!(open_run_property, "w:rPr");
|
||||
|
|
|
@ -39,7 +39,7 @@ impl fmt::Display for XmlDocument {
|
|||
|
||||
/// An XML Tag
|
||||
///
|
||||
/// For exammple:
|
||||
/// For example:
|
||||
///
|
||||
/// ```XML
|
||||
/// <foo bar="baz">
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
|
@ -25315,13 +25315,7 @@ exports[`writer should write customItem 6`] = `
|
|||
|
||||
exports[`writer should write customItem 7`] = `
|
||||
"<root xmlns=\\"https://example.com\\">
|
||||
<item name=\\"Cheap Item\\" price=\\"$193.95\\">
|
||||
</item>
|
||||
<item name=\\"Expensive Item\\" price=\\"$931.88\\">
|
||||
</item>
|
||||
|
||||
</root>
|
||||
"
|
||||
<item name=\\"Cheap Item\\" price=\\"$193.95\\"></item><item name=\\"Expensive Item\\" price=\\"$931.88\\"></item></root>"
|
||||
`;
|
||||
|
||||
exports[`writer should write default font 1`] = `
|
||||
|
|
Loading…
Reference in New Issue