diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..463adf4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "cSpell.words": [ + "libreoffice" + ] +} \ No newline at end of file diff --git a/docx-core/src/documents/elements/mod.rs b/docx-core/src/documents/elements/mod.rs index dd2fcd4..e6905c0 100644 --- a/docx-core/src/documents/elements/mod.rs +++ b/docx-core/src/documents/elements/mod.rs @@ -15,6 +15,12 @@ mod run_property_default; mod style; mod sz; mod sz_cs; +mod table_borders; +mod table_cell_margins; +mod table_grid; +mod table_indent; +mod table_property; +mod table_width; mod text; pub use based_on::*; @@ -34,4 +40,10 @@ pub use run_property_default::*; pub use style::*; pub use sz::*; pub use sz_cs::*; +pub use table_borders::*; +pub use table_cell_margins::*; +pub use table_grid::*; +pub use table_indent::*; +pub use table_property::*; +pub use table_width::*; pub use text::*; diff --git a/docx-core/src/documents/elements/table_borders.rs b/docx-core/src/documents/elements/table_borders.rs new file mode 100644 index 0000000..72df3bc --- /dev/null +++ b/docx-core/src/documents/elements/table_borders.rs @@ -0,0 +1,179 @@ +use crate::documents::BuildXML; +use crate::types::*; +use crate::xml_builder::*; + +/* + Please see. L.4.3.2.18 Cell Border Properties + + left – left border + right – right border + top – top border + bottom – bottom border + insideH – inner horizontal borders + insideV – inner vertical borders + tl2br – diagonal border from top left corner to bottom right corner + tr2bl – diagonal border from top right corner to bottom left corner +*/ + +#[derive(Debug, Clone)] +pub struct TableBorder { + position: BorderPosition, + border_type: BorderType, + size: usize, + space: usize, + color: String, +} + +impl TableBorder { + pub fn new(position: BorderPosition) -> TableBorder { + TableBorder { + position, + border_type: BorderType::Single, + size: 2, + space: 0, + color: "000000".to_owned(), + } + } + + pub fn color(mut self, color: impl Into) -> TableBorder { + self.color = color.into(); + self + } +} + +impl BuildXML for TableBorder { + fn build(&self) -> Vec { + let base = XMLBuilder::new(); + let base = match self.position { + BorderPosition::Top => { + base.border_top(self.border_type, self.size, self.space, &self.color) + } + BorderPosition::Left => { + base.border_left(self.border_type, self.size, self.space, &self.color) + } + BorderPosition::Bottom => { + base.border_bottom(self.border_type, self.size, self.space, &self.color) + } + BorderPosition::Right => { + base.border_right(self.border_type, self.size, self.space, &self.color) + } + BorderPosition::IndideH => { + base.border_inside_h(self.border_type, self.size, self.space, &self.color) + } + BorderPosition::IndideV => { + base.border_inside_v(self.border_type, self.size, self.space, &self.color) + } + }; + base.build() + } +} + +// 17.4.39 +// tblBorders (Table Borders Exceptions) +// This element specifies the set of borders for the edges of the parent table row via a set of table-level property +// exceptions, using the six border types defined by its child elements. +// If the cell spacing for any row is non-zero as specified using the tblCellSpacing element (§17.4.44; §17.4.43; +// §17.4.45), then there is no border conflict and the table-level exception border shall be displayed. +// If the cell spacing is zero, then there is a conflict [Example: Between the left border of all cells in the first column +// and the left border of the table-level exceptions. end example], which shall be resolved as follows: +// If there is a cell border, then the cell border shall be displayed +// If there is no cell border, then the table-level exception border shall be displayed +// If this element is omitted, then this table shall have the borders specified by the associated table level borders +// (§17.4.38). +#[derive(Debug, Clone)] +pub struct TableBorders { + top: Option, + left: Option, + bottom: Option, + right: Option, + inside_h: Option, + inside_v: Option, +} + +impl Default for TableBorders { + fn default() -> TableBorders { + TableBorders { + top: Some(TableBorder::new(BorderPosition::Top)), + left: Some(TableBorder::new(BorderPosition::Left)), + bottom: Some(TableBorder::new(BorderPosition::Bottom)), + right: None, + inside_h: Some(TableBorder::new(BorderPosition::IndideH)), + inside_v: None, + } + } +} + +impl TableBorders { + pub fn new() -> TableBorders { + Default::default() + } + + pub fn set_border(mut self, border: TableBorder) -> Self { + match border.position { + BorderPosition::Top => self.top = Some(border), + BorderPosition::Left => self.left = Some(border), + BorderPosition::Bottom => self.bottom = Some(border), + BorderPosition::Right => self.right = Some(border), + BorderPosition::IndideH => self.inside_h = Some(border), + BorderPosition::IndideV => self.inside_v = Some(border), + }; + self + } + + pub fn clear_border(mut self, position: BorderPosition) -> Self { + match position { + BorderPosition::Top => self.top = None, + BorderPosition::Left => self.left = None, + BorderPosition::Bottom => self.bottom = None, + BorderPosition::Right => self.right = None, + BorderPosition::IndideH => self.inside_h = None, + BorderPosition::IndideV => self.inside_v = None, + }; + self + } +} + +impl BuildXML for TableBorders { + fn build(&self) -> Vec { + XMLBuilder::new() + .open_table_borders() + .add_optional_child(&self.top) + .add_optional_child(&self.left) + .add_optional_child(&self.bottom) + .add_optional_child(&self.right) + .add_optional_child(&self.inside_h) + .add_optional_child(&self.inside_v) + .close() + .build() + } +} + +#[cfg(test)] +mod tests { + + use super::*; + #[cfg(test)] + use pretty_assertions::assert_eq; + use std::str; + + #[test] + fn test_table_borders() { + let b = TableBorders::new().build(); + assert_eq!( + str::from_utf8(&b).unwrap(), + r#""# + ); + } + + #[test] + fn test_table_borders_set() { + let b = TableBorders::new() + .set_border(TableBorder::new(BorderPosition::Left).color("AAAAAA")) + .clear_border(BorderPosition::Top) + .build(); + assert_eq!( + str::from_utf8(&b).unwrap(), + r#""# + ); + } +} diff --git a/docx-core/src/documents/elements/table_cell_margins.rs b/docx-core/src/documents/elements/table_cell_margins.rs new file mode 100644 index 0000000..ee2475b --- /dev/null +++ b/docx-core/src/documents/elements/table_cell_margins.rs @@ -0,0 +1,87 @@ +use crate::documents::BuildXML; +use crate::types::*; +use crate::xml_builder::*; + +#[derive(Debug, Clone)] +pub struct TableCellMargins { + top: usize, + left: usize, + bottom: usize, + right: usize, +} + +impl Default for TableCellMargins { + fn default() -> TableCellMargins { + TableCellMargins { + top: 55, + left: 54, + bottom: 55, + right: 55, + } + } +} + +impl TableCellMargins { + pub fn new() -> TableCellMargins { + Default::default() + } + + pub fn margin(self, top: usize, left: usize, bottom: usize, right: usize) -> TableCellMargins { + TableCellMargins { + top, + left, + bottom, + right, + } + } +} + +impl BuildXML for TableCellMargins { + fn build(&self) -> Vec { + XMLBuilder::new() + .open_table_cell_margins() + .margin_top(self.top, WidthType::DXA) + .margin_left(self.left, WidthType::DXA) + .margin_bottom(self.bottom, WidthType::DXA) + .margin_right(self.right, WidthType::DXA) + .close() + .build() + } +} + +#[cfg(test)] +mod tests { + + use super::*; + #[cfg(test)] + use pretty_assertions::assert_eq; + use std::str; + + #[test] + fn test_table_cell_margin() { + let b = TableCellMargins::new().build(); + assert_eq!( + str::from_utf8(&b).unwrap(), + r#" + + + + +"# + ); + } + + #[test] + fn test_table_cell_margin_setter() { + let b = TableCellMargins::new().margin(10, 20, 30, 40).build(); + assert_eq!( + str::from_utf8(&b).unwrap(), + r#" + + + + +"# + ); + } +} diff --git a/docx-core/src/documents/elements/table_grid.rs b/docx-core/src/documents/elements/table_grid.rs new file mode 100644 index 0000000..5a8fae1 --- /dev/null +++ b/docx-core/src/documents/elements/table_grid.rs @@ -0,0 +1,45 @@ +use crate::documents::BuildXML; +use crate::types::*; +use crate::xml_builder::*; + +#[derive(Debug, Clone)] +pub struct TableGrid { + grid: Vec, +} + +impl TableGrid { + pub fn new(grid: Vec) -> TableGrid { + TableGrid { grid } + } +} + +impl BuildXML for TableGrid { + fn build(&self) -> Vec { + let mut base = XMLBuilder::new().open_table_grid(); + for g in &self.grid { + base = base.grid_column(*g, WidthType::DXA); + } + base.close().build() + } +} + +#[cfg(test)] +mod tests { + + use super::*; + #[cfg(test)] + use pretty_assertions::assert_eq; + use std::str; + + #[test] + fn test_table_indent() { + let b = TableGrid::new(vec![100, 200]).build(); + assert_eq!( + str::from_utf8(&b).unwrap(), + r#" + + +"# + ); + } +} diff --git a/docx-core/src/documents/elements/table_indent.rs b/docx-core/src/documents/elements/table_indent.rs new file mode 100644 index 0000000..03ec49e --- /dev/null +++ b/docx-core/src/documents/elements/table_indent.rs @@ -0,0 +1,39 @@ +use crate::documents::BuildXML; +use crate::types::*; +use crate::xml_builder::*; + +#[derive(Debug, Clone)] +pub struct TableIndent { + width: usize, + width_type: WidthType, +} + +impl TableIndent { + pub fn new(width: usize, width_type: WidthType) -> TableIndent { + TableIndent { width, width_type } + } +} + +impl BuildXML for TableIndent { + fn build(&self) -> Vec { + XMLBuilder::new().table_indent(20, WidthType::DXA).build() + } +} + +#[cfg(test)] +mod tests { + + use super::*; + #[cfg(test)] + use pretty_assertions::assert_eq; + use std::str; + + #[test] + fn test_table_indent() { + let b = TableIndent::new(20, WidthType::DXA).build(); + assert_eq!( + str::from_utf8(&b).unwrap(), + r#""# + ); + } +} diff --git a/docx-core/src/documents/elements/table_property.rs b/docx-core/src/documents/elements/table_property.rs new file mode 100644 index 0000000..0144773 --- /dev/null +++ b/docx-core/src/documents/elements/table_property.rs @@ -0,0 +1,84 @@ +use super::{Justification, TableBorders, TableCellMargins, TableIndent, TableWidth}; +use crate::documents::BuildXML; +use crate::types::*; +use crate::xml_builder::*; + +#[derive(Debug)] +pub struct TableProperty { + width: TableWidth, + justification: Justification, + borders: TableBorders, + margins: TableCellMargins, + indent: Option, +} + +impl Default for TableProperty { + fn default() -> Self { + TableProperty { + width: TableWidth::new(9638, WidthType::DXA), + justification: Justification::new("left"), + borders: TableBorders::new(), + margins: TableCellMargins::new(), + indent: None, + } + } +} + +impl TableProperty { + pub fn new() -> TableProperty { + Default::default() + } + + pub fn indent(mut self, v: usize) -> TableProperty { + self.indent = Some(TableIndent::new(v, WidthType::DXA)); + self + } + + pub fn width(mut self, v: usize) -> TableProperty { + self.width = TableWidth::new(v, WidthType::DXA); + self + } + + pub fn justify(mut self, v: &str) -> TableProperty { + self.justification = Justification::new(v); + self + } +} + +impl BuildXML for TableProperty { + fn build(&self) -> Vec { + XMLBuilder::new() + .open_table_property() + .add_child(&self.width) + .add_child(&self.justification) + .add_child(&self.borders) + .add_child(&self.margins) + .add_optional_child(&self.indent) + .close() + .build() + } +} + +#[cfg(test)] +mod tests { + + use super::*; + #[cfg(test)] + use pretty_assertions::assert_eq; + use std::str; + + #[test] + fn test_default() { + let c = TableProperty::new(); + let b = c.build(); + assert_eq!( + str::from_utf8(&b).unwrap(), + r#" + + + + +"# + ); + } +} diff --git a/docx-core/src/documents/elements/table_width.rs b/docx-core/src/documents/elements/table_width.rs new file mode 100644 index 0000000..76d3ed7 --- /dev/null +++ b/docx-core/src/documents/elements/table_width.rs @@ -0,0 +1,41 @@ +use crate::documents::BuildXML; +use crate::types::*; +use crate::xml_builder::*; + +#[derive(Debug, Clone)] +pub struct TableWidth { + width: usize, + width_type: WidthType, +} + +impl TableWidth { + pub fn new(width: usize, width_type: WidthType) -> TableWidth { + TableWidth { width, width_type } + } +} + +impl BuildXML for TableWidth { + fn build(&self) -> Vec { + XMLBuilder::new() + .table_width(self.width, WidthType::DXA) + .build() + } +} + +#[cfg(test)] +mod tests { + + use super::*; + #[cfg(test)] + use pretty_assertions::assert_eq; + use std::str; + + #[test] + fn test_table_width() { + let b = TableWidth::new(20, WidthType::DXA).build(); + assert_eq!( + str::from_utf8(&b).unwrap(), + r#""# + ); + } +} diff --git a/docx-core/src/documents/elements/text.rs b/docx-core/src/documents/elements/text.rs index 2521102..65e0ad6 100644 --- a/docx-core/src/documents/elements/text.rs +++ b/docx-core/src/documents/elements/text.rs @@ -1,7 +1,7 @@ use crate::documents::BuildXML; use crate::xml_builder::*; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Text { text: String, preserve_space: bool, diff --git a/docx-core/src/types/border_position.rs b/docx-core/src/types/border_position.rs new file mode 100644 index 0000000..5174097 --- /dev/null +++ b/docx-core/src/types/border_position.rs @@ -0,0 +1,9 @@ +#[derive(Debug, Clone)] +pub enum BorderPosition { + Left, + Right, + Top, + Bottom, + IndideH, + IndideV, +} diff --git a/docx-core/src/types/border_type.rs b/docx-core/src/types/border_type.rs index 40d9265..e8fd23d 100644 --- a/docx-core/src/types/border_type.rs +++ b/docx-core/src/types/border_type.rs @@ -2,17 +2,43 @@ // Please see p3813 // +use std::fmt; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +#[derive(Copy, Clone, Debug)] +pub enum BorderType { + None, + Single, + Thick, + Double, + Dotted, + Dashed, + DotDash, + DotDotDash, + Triple, +} + +impl fmt::Display for BorderType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + BorderType::None => write!(f, "none"), + BorderType::Single => write!(f, "single"), + BorderType::Thick => write!(f, "thick"), + BorderType::Double => write!(f, "double"), + BorderType::Dotted => write!(f, "dotted"), + BorderType::Dashed => write!(f, "dashed"), + BorderType::DotDash => write!(f, "dotDash"), + BorderType::DotDotDash => write!(f, "dotDotDash"), + BorderType::Triple => write!(f, "triple"), + } + } +} + /* + Unsupported types "nil" "none" -"single" -"thick" -"double" -"dotted" -"dashed" -"dotDash" -"dotDotDash" -"triple" "thinThickSmallGap" "thickThinSmallGap" "thinThickThinSmallGap" diff --git a/docx-core/src/types/mod.rs b/docx-core/src/types/mod.rs index 5798544..3ddca37 100644 --- a/docx-core/src/types/mod.rs +++ b/docx-core/src/types/mod.rs @@ -1,9 +1,13 @@ pub mod alignment_type; +pub mod border_position; +pub mod border_type; pub mod special_indent_type; pub mod style_type; pub mod width_type; pub use alignment_type::*; +pub use border_position::*; +pub use border_type::*; pub use special_indent_type::*; pub use style_type::*; pub use width_type::*; diff --git a/docx-core/src/xml_builder/elements.rs b/docx-core/src/xml_builder/elements.rs index db10969..3657ae4 100644 --- a/docx-core/src/xml_builder/elements.rs +++ b/docx-core/src/xml_builder/elements.rs @@ -106,10 +106,19 @@ impl XMLBuilder { closed_w_with_type_el!(grid_column, "w:gridCol"); closed_w_with_type_el!(table_cell_width, "w:tcW"); - // TODO: - // w:shd - // w:top/left/bottom/right - // w:insideH/insideV + closed_w_with_type_el!(margin_top, "w:top"); + closed_w_with_type_el!(margin_left, "w:left"); + closed_w_with_type_el!(margin_bottom, "w:bottom"); + closed_w_with_type_el!(margin_right, "w:right"); + + closed_border_el!(border_top, "w:top"); + closed_border_el!(border_left, "w:left"); + closed_border_el!(border_bottom, "w:bottom"); + closed_border_el!(border_right, "w:right"); + closed_border_el!(border_inside_h, "w:insideH"); + closed_border_el!(border_inside_v, "w:insideV"); + + closed_el!(shd, "w:shd", "w:fill", "w:val"); } #[cfg(test)] diff --git a/docx-core/src/xml_builder/macros.rs b/docx-core/src/xml_builder/macros.rs index daf211f..e1b6bfe 100644 --- a/docx-core/src/xml_builder/macros.rs +++ b/docx-core/src/xml_builder/macros.rs @@ -170,12 +170,14 @@ macro_rules! closed_w_with_type_el { macro_rules! closed_border_el { ($name: ident, $el_name: expr) => { - pub(crate) fn $name(mut self, size: usize, space: usize, color: &str) -> Self { + pub(crate) fn $name(mut self, val: BorderType, size: usize, space: usize, color: &str) -> Self { self.writer .write( XmlEvent::start_element($el_name) - .attr("w:w", &format!("{}", w)) - .attr("w:type", &t.to_string()), + .attr("w:val", &val.to_string()) + .attr("w:sz", &format!("{}", size)) + .attr("w:space", &format!("{}", space)) + .attr("w:color", color), ) .expect(EXPECT_MESSAGE); self.close()