diff --git a/docx-core/src/documents/elements/mod.rs b/docx-core/src/documents/elements/mod.rs index 3801271..d2314fa 100644 --- a/docx-core/src/documents/elements/mod.rs +++ b/docx-core/src/documents/elements/mod.rs @@ -80,6 +80,7 @@ mod paragraph_property_change; mod paragraph_property_default; mod paragraph_style; mod pic; +mod positional_tab; mod q_format; mod run; mod run_fonts; @@ -217,6 +218,7 @@ pub use paragraph_property_change::*; pub use paragraph_property_default::*; pub use paragraph_style::*; pub use pic::*; +pub use positional_tab::*; pub use q_format::*; pub use run::*; pub use run_fonts::*; diff --git a/docx-core/src/documents/elements/positional_tab.rs b/docx-core/src/documents/elements/positional_tab.rs new file mode 100644 index 0000000..cd08957 --- /dev/null +++ b/docx-core/src/documents/elements/positional_tab.rs @@ -0,0 +1,51 @@ +use serde::{Deserialize, Serialize}; + +use crate::documents::BuildXML; +use crate::types::*; +use crate::xml_builder::*; + +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] +#[cfg_attr(feature = "wasm", derive(ts_rs::TS))] +#[cfg_attr(feature = "wasm", ts(export))] +pub struct PositionalTab { + pub alignment: PositionalTabAlignmentType, + pub relative_to: PositionalTabRelativeTo, + pub leader: TabLeaderType, +} + +impl PositionalTab { + pub fn new( + alignment: PositionalTabAlignmentType, + relative_to: PositionalTabRelativeTo, + leader: TabLeaderType, + ) -> Self { + Self { + alignment, + relative_to, + leader, + } + } + + pub fn relative_to(mut self, relative_to: PositionalTabRelativeTo) -> Self { + self.relative_to = relative_to; + self + } + + pub fn leader(mut self, leader: TabLeaderType) -> Self { + self.leader = leader; + self + } + + pub fn alignment(mut self, alignment: PositionalTabAlignmentType) -> Self { + self.alignment = alignment; + self + } +} + +impl BuildXML for PositionalTab { + fn build(&self) -> Vec { + let b = XMLBuilder::new(); + b.ptab(self.alignment, self.relative_to, self.leader) + .build() + } +} diff --git a/docx-core/src/documents/elements/run_property.rs b/docx-core/src/documents/elements/run_property.rs index c21d3dc..ac6dc15 100644 --- a/docx-core/src/documents/elements/run_property.rs +++ b/docx-core/src/documents/elements/run_property.rs @@ -49,6 +49,8 @@ pub struct RunProperty { #[serde(skip_serializing_if = "Option::is_none")] pub strike: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub positional_tab: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub shading: Option, } @@ -157,6 +159,11 @@ impl RunProperty { self } + pub fn ptab(mut self, ptab: PositionalTab) -> Self { + self.positional_tab = Some(ptab); + self + } + pub fn shading(mut self, s: Shading) -> Self { self.shading = Some(s); self @@ -187,6 +194,7 @@ impl BuildXML for RunProperty { .add_optional_child(&self.vert_align) .add_optional_child(&self.character_spacing) .add_optional_child(&self.style) + .add_optional_child(&self.positional_tab) .add_optional_child(&self.shading) .close() .build() @@ -270,6 +278,7 @@ mod tests { r#""# ); } + #[test] fn test_character_spacing() { let c = RunProperty::new().spacing(20); @@ -280,6 +289,20 @@ mod tests { ); } + #[test] + fn test_ptab() { + let c = RunProperty::new().ptab(PositionalTab::new( + PositionalTabAlignmentType::Left, + PositionalTabRelativeTo::Margin, + TabLeaderType::None, + )); + let b = c.build(); + assert_eq!( + str::from_utf8(&b).unwrap(), + r#""# + ); + } + #[test] fn test_character_shading() { let c = RunProperty::new().shading( diff --git a/docx-core/src/types/mod.rs b/docx-core/src/types/mod.rs index b5ac028..870f164 100644 --- a/docx-core/src/types/mod.rs +++ b/docx-core/src/types/mod.rs @@ -15,6 +15,8 @@ pub mod level_suffix_type; pub mod line_spacing_type; pub mod page_margin; pub mod page_orientation_type; +pub mod positional_tab_alignment_type; +pub mod positional_tab_relative_to; pub mod relative_from_type; pub mod section_type; pub mod shd_type; @@ -48,6 +50,8 @@ pub use level_suffix_type::*; pub use line_spacing_type::*; pub use page_margin::*; pub use page_orientation_type::*; +pub use positional_tab_alignment_type::*; +pub use positional_tab_relative_to::*; pub use relative_from_type::*; pub use section_type::*; pub use shd_type::*; diff --git a/docx-core/src/types/positional_tab_alignment_type.rs b/docx-core/src/types/positional_tab_alignment_type.rs new file mode 100644 index 0000000..b80a842 --- /dev/null +++ b/docx-core/src/types/positional_tab_alignment_type.rs @@ -0,0 +1,41 @@ +use serde::{Deserialize, Serialize}; + +use std::fmt; +use std::str::FromStr; +#[cfg(feature = "wasm")] +use wasm_bindgen::prelude::*; + +use super::errors; + +#[cfg_attr(feature = "wasm", wasm_bindgen)] +#[cfg_attr(feature = "wasm", derive(ts_rs::TS))] +#[cfg_attr(feature = "wasm", ts(export))] +#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub enum PositionalTabAlignmentType { + Center, + Left, + Right, +} + +impl fmt::Display for PositionalTabAlignmentType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + PositionalTabAlignmentType::Center => write!(f, "center"), + PositionalTabAlignmentType::Left => write!(f, "left"), + PositionalTabAlignmentType::Right => write!(f, "right"), + } + } +} + +impl FromStr for PositionalTabAlignmentType { + type Err = errors::TypeError; + fn from_str(s: &str) -> Result { + match s { + "center" => Ok(PositionalTabAlignmentType::Center), + "right" => Ok(PositionalTabAlignmentType::Right), + "left" => Ok(PositionalTabAlignmentType::Left), + _ => Err(errors::TypeError::Unsupported(s.to_string())), + } + } +} diff --git a/docx-core/src/types/positional_tab_relative_to.rs b/docx-core/src/types/positional_tab_relative_to.rs new file mode 100644 index 0000000..862139e --- /dev/null +++ b/docx-core/src/types/positional_tab_relative_to.rs @@ -0,0 +1,37 @@ +use serde::{Deserialize, Serialize}; + +use std::fmt; +use std::str::FromStr; +#[cfg(feature = "wasm")] +use wasm_bindgen::prelude::*; + +use super::errors; + +#[cfg_attr(feature = "wasm", wasm_bindgen)] +#[cfg_attr(feature = "wasm", derive(ts_rs::TS))] +#[cfg_attr(feature = "wasm", ts(export))] +#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub enum PositionalTabRelativeTo { + Indent, + Margin, +} + +impl fmt::Display for PositionalTabRelativeTo { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + PositionalTabRelativeTo::Indent => write!(f, "indent"), + PositionalTabRelativeTo::Margin => write!(f, "margin"), + } + } +} + +impl FromStr for PositionalTabRelativeTo { + type Err = errors::TypeError; + fn from_str(s: &str) -> Result { + match s { + "indent" => Ok(PositionalTabRelativeTo::Indent), + "margin" => Ok(PositionalTabRelativeTo::Margin), + _ => Err(errors::TypeError::Unsupported(s.to_string())), + } + } +} diff --git a/docx-core/src/xml_builder/elements.rs b/docx-core/src/xml_builder/elements.rs index f923ac6..2de6100 100644 --- a/docx-core/src/xml_builder/elements.rs +++ b/docx-core/src/xml_builder/elements.rs @@ -733,6 +733,27 @@ impl XMLBuilder { self.close() } + pub(crate) fn ptab( + mut self, + alignment: PositionalTabAlignmentType, + relative_to: PositionalTabRelativeTo, + leader: TabLeaderType, + ) -> Self { + let alignment_string = alignment.to_string(); + let relative_to_string = relative_to.to_string(); + let leader_string = leader.to_string(); + + let mut t = XmlEvent::start_element("w:ptab"); + + t = t.attr("w:alignment", &alignment_string); + t = t.attr("w:relativeTo", &relative_to_string); + t = t.attr("w:leader", &leader_string); + + self.writer.write(t).expect(EXPECT_MESSAGE); + + self.close() + } + // FootnoteReference // w:footnoteReference w:id="1" pub(crate) fn footnote_reference(mut self, id: usize) -> Self { @@ -804,6 +825,22 @@ mod tests { ); } + #[test] + fn test_ptab() { + let b = XMLBuilder::new(); + let r = b + .ptab( + PositionalTabAlignmentType::Left, + PositionalTabRelativeTo::Indent, + TabLeaderType::None, + ) + .build(); + assert_eq!( + str::from_utf8(&r).unwrap(), + r#""# + ); + } + #[test] fn test_footnote_reference() { let b = XMLBuilder::new();