feat: Add table property

main
bokuweb 2019-11-12 17:32:50 +09:00
parent b8a13d93e9
commit 5378c07a35
14 changed files with 558 additions and 16 deletions

5
.vscode/settings.json vendored 100644
View File

@ -0,0 +1,5 @@
{
"cSpell.words": [
"libreoffice"
]
}

View File

@ -15,6 +15,12 @@ mod run_property_default;
mod style; mod style;
mod sz; mod sz;
mod sz_cs; mod sz_cs;
mod table_borders;
mod table_cell_margins;
mod table_grid;
mod table_indent;
mod table_property;
mod table_width;
mod text; mod text;
pub use based_on::*; pub use based_on::*;
@ -34,4 +40,10 @@ pub use run_property_default::*;
pub use style::*; pub use style::*;
pub use sz::*; pub use sz::*;
pub use sz_cs::*; 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::*; pub use text::*;

View File

@ -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<String>) -> TableBorder {
self.color = color.into();
self
}
}
impl BuildXML for TableBorder {
fn build(&self) -> Vec<u8> {
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<TableBorder>,
left: Option<TableBorder>,
bottom: Option<TableBorder>,
right: Option<TableBorder>,
inside_h: Option<TableBorder>,
inside_v: Option<TableBorder>,
}
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<u8> {
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#"<w:tblBorders><w:top w:val="single" w:sz="2" w:space="0" w:color="000000" /><w:left w:val="single" w:sz="2" w:space="0" w:color="000000" /><w:bottom w:val="single" w:sz="2" w:space="0" w:color="000000" /><w:insideH w:val="single" w:sz="2" w:space="0" w:color="000000" /></w:tblBorders>"#
);
}
#[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#"<w:tblBorders><w:left w:val="single" w:sz="2" w:space="0" w:color="AAAAAA" /><w:bottom w:val="single" w:sz="2" w:space="0" w:color="000000" /><w:insideH w:val="single" w:sz="2" w:space="0" w:color="000000" /></w:tblBorders>"#
);
}
}

View File

@ -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<u8> {
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#"<w:tblCellMar>
<w:top w:w="55" w:type="dxa" />
<w:left w:w="54" w:type="dxa" />
<w:bottom w:w="55" w:type="dxa" />
<w:right w:w="55" w:type="dxa" />
</w:tblCellMar>"#
);
}
#[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#"<w:tblCellMar>
<w:top w:w="10" w:type="dxa" />
<w:left w:w="20" w:type="dxa" />
<w:bottom w:w="30" w:type="dxa" />
<w:right w:w="40" w:type="dxa" />
</w:tblCellMar>"#
);
}
}

View File

@ -0,0 +1,45 @@
use crate::documents::BuildXML;
use crate::types::*;
use crate::xml_builder::*;
#[derive(Debug, Clone)]
pub struct TableGrid {
grid: Vec<usize>,
}
impl TableGrid {
pub fn new(grid: Vec<usize>) -> TableGrid {
TableGrid { grid }
}
}
impl BuildXML for TableGrid {
fn build(&self) -> Vec<u8> {
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#"<w:tblGrid>
<w:gridCol w:w="100" w:type="dxa" />
<w:gridCol w:w="200" w:type="dxa" />
</w:tblGrid>"#
);
}
}

View File

@ -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<u8> {
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#"<w:tblInd w:w="20" w:type="dxa" />"#
);
}
}

View File

@ -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<TableIndent>,
}
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<u8> {
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#"<w:tblPr><w:tblW w:w="9638" w:type="dxa" /><w:jc w:val="left" /><w:tblBorders><w:top w:val="single" w:sz="2" w:space="0" w:color="000000" /><w:left w:val="single" w:sz="2" w:space="0" w:color="000000" /><w:bottom w:val="single" w:sz="2" w:space="0" w:color="000000" /><w:insideH w:val="single" w:sz="2" w:space="0" w:color="000000" /></w:tblBorders><w:tblCellMar>
<w:top w:w="55" w:type="dxa" />
<w:left w:w="54" w:type="dxa" />
<w:bottom w:w="55" w:type="dxa" />
<w:right w:w="55" w:type="dxa" />
</w:tblCellMar></w:tblPr>"#
);
}
}

View File

@ -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<u8> {
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#"<w:tblW w:w="20" w:type="dxa" />"#
);
}
}

View File

@ -1,7 +1,7 @@
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::xml_builder::*; use crate::xml_builder::*;
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct Text { pub struct Text {
text: String, text: String,
preserve_space: bool, preserve_space: bool,

View File

@ -0,0 +1,9 @@
#[derive(Debug, Clone)]
pub enum BorderPosition {
Left,
Right,
Top,
Bottom,
IndideH,
IndideV,
}

View File

@ -2,17 +2,43 @@
// Please see p3813 <xsd:simpleType name="ST_Border"> // Please see p3813 <xsd:simpleType name="ST_Border">
// //
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" "nil"
"none" "none"
"single"
"thick"
"double"
"dotted"
"dashed"
"dotDash"
"dotDotDash"
"triple"
"thinThickSmallGap" "thinThickSmallGap"
"thickThinSmallGap" "thickThinSmallGap"
"thinThickThinSmallGap" "thinThickThinSmallGap"

View File

@ -1,9 +1,13 @@
pub mod alignment_type; pub mod alignment_type;
pub mod border_position;
pub mod border_type;
pub mod special_indent_type; pub mod special_indent_type;
pub mod style_type; pub mod style_type;
pub mod width_type; pub mod width_type;
pub use alignment_type::*; pub use alignment_type::*;
pub use border_position::*;
pub use border_type::*;
pub use special_indent_type::*; pub use special_indent_type::*;
pub use style_type::*; pub use style_type::*;
pub use width_type::*; pub use width_type::*;

View File

@ -106,10 +106,19 @@ impl XMLBuilder {
closed_w_with_type_el!(grid_column, "w:gridCol"); closed_w_with_type_el!(grid_column, "w:gridCol");
closed_w_with_type_el!(table_cell_width, "w:tcW"); closed_w_with_type_el!(table_cell_width, "w:tcW");
// TODO: closed_w_with_type_el!(margin_top, "w:top");
// w:shd closed_w_with_type_el!(margin_left, "w:left");
// w:top/left/bottom/right closed_w_with_type_el!(margin_bottom, "w:bottom");
// w:insideH/insideV 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)] #[cfg(test)]

View File

@ -170,12 +170,14 @@ macro_rules! closed_w_with_type_el {
macro_rules! closed_border_el { macro_rules! closed_border_el {
($name: ident, $el_name: expr) => { ($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 self.writer
.write( .write(
XmlEvent::start_element($el_name) XmlEvent::start_element($el_name)
.attr("w:w", &format!("{}", w)) .attr("w:val", &val.to_string())
.attr("w:type", &t.to_string()), .attr("w:sz", &format!("{}", size))
.attr("w:space", &format!("{}", space))
.attr("w:color", color),
) )
.expect(EXPECT_MESSAGE); .expect(EXPECT_MESSAGE);
self.close() self.close()