diff --git a/docx-core/src/documents/elements/cell_margins.rs b/docx-core/src/documents/elements/cell_margins.rs new file mode 100644 index 0000000..0e9dbc4 --- /dev/null +++ b/docx-core/src/documents/elements/cell_margins.rs @@ -0,0 +1,109 @@ +use serde::Serialize; + +use crate::documents::BuildXML; +use crate::types::*; +use crate::xml_builder::*; + +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CellMargin { + pub val: usize, + pub width_type: WidthType, +} + +impl CellMargin { + pub fn new(val: usize, t: WidthType) -> Self { + Self { val, width_type: t } + } +} + +impl Default for CellMargin { + fn default() -> CellMargin { + CellMargin { + val: 55, + width_type: WidthType::Dxa, + } + } +} + +#[derive(Debug, Clone, PartialEq, Serialize, Default)] +#[serde(rename_all = "camelCase")] +pub struct CellMargins { + #[serde(skip_serializing_if = "Option::is_none")] + top: Option, + #[serde(skip_serializing_if = "Option::is_none")] + left: Option, + #[serde(skip_serializing_if = "Option::is_none")] + bottom: Option, + #[serde(skip_serializing_if = "Option::is_none")] + right: Option, +} + +impl CellMargins { + pub fn new() -> CellMargins { + Default::default() + } + + pub fn margin_top(mut self, v: usize, t: WidthType) -> Self { + self.top = Some(CellMargin::new(v, t)); + self + } + + pub fn margin_right(mut self, v: usize, t: WidthType) -> Self { + self.right = Some(CellMargin::new(v, t)); + self + } + + pub fn margin_left(mut self, v: usize, t: WidthType) -> Self { + self.left = Some(CellMargin::new(v, t)); + self + } + + pub fn margin_bottom(mut self, v: usize, t: WidthType) -> Self { + self.bottom = Some(CellMargin::new(v, t)); + self + } +} + +impl BuildXML for CellMargins { + fn build(&self) -> Vec { + let mut b = XMLBuilder::new().open_cell_margins(); + + if let Some(ref top) = self.top { + b = b.margin_top(top.val as i32, top.width_type); + } + + if let Some(ref left) = self.left { + b = b.margin_left(left.val as i32, left.width_type); + } + + if let Some(ref bottom) = self.bottom { + b = b.margin_bottom(bottom.val as i32, bottom.width_type); + } + + if let Some(ref right) = self.right { + b = b.margin_right(right.val as i32, right.width_type); + } + b.close().build() + } +} + +#[cfg(test)] +mod tests { + + use super::*; + #[cfg(test)] + use pretty_assertions::assert_eq; + use std::str; + + #[test] + fn test_cell_margin() { + let b = CellMargins::new().margin_top(10, WidthType::Dxa).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 5079967..e1e6903 100644 --- a/docx-core/src/documents/elements/mod.rs +++ b/docx-core/src/documents/elements/mod.rs @@ -9,6 +9,7 @@ mod bookmark_end; mod bookmark_start; mod br; mod caps; +mod cell_margins; mod character_spacing; mod color; mod comment; @@ -139,6 +140,7 @@ pub use bookmark_end::*; pub use bookmark_start::*; pub use br::*; pub use caps::*; +pub use cell_margins::*; pub use character_spacing::*; pub use color::*; pub use comment::*; diff --git a/docx-core/src/documents/elements/table_cell_borders.rs b/docx-core/src/documents/elements/table_cell_borders.rs index 42bc963..e8aa9ec 100644 --- a/docx-core/src/documents/elements/table_cell_borders.rs +++ b/docx-core/src/documents/elements/table_cell_borders.rs @@ -17,6 +17,8 @@ use crate::xml_builder::*; tr2bl – diagonal border from top right corner to bottom left corner */ #[derive(Serialize, Debug, Clone, PartialEq)] +#[cfg_attr(feature = "wasm", derive(ts_rs::TS))] +#[cfg_attr(feature = "wasm", ts(export))] #[serde(rename_all = "camelCase")] pub struct TableCellBorder { pub border_type: BorderType, @@ -99,6 +101,8 @@ impl BuildXML for TableCellBorder { } #[derive(Serialize, Debug, Clone, PartialEq)] +#[cfg_attr(feature = "wasm", derive(ts_rs::TS))] +#[cfg_attr(feature = "wasm", ts(export))] #[serde(rename_all = "camelCase")] pub struct TableCellBorders { top: Option, diff --git a/docx-core/src/documents/elements/table_cell_margins.rs b/docx-core/src/documents/elements/table_cell_margins.rs index bff1090..14a08a8 100644 --- a/docx-core/src/documents/elements/table_cell_margins.rs +++ b/docx-core/src/documents/elements/table_cell_margins.rs @@ -3,28 +3,7 @@ use serde::Serialize; use crate::documents::BuildXML; use crate::types::*; use crate::xml_builder::*; - -#[derive(Debug, Clone, PartialEq, Serialize)] -#[serde(rename_all = "camelCase")] -struct CellMargin { - pub val: usize, - pub width_type: WidthType, -} - -impl CellMargin { - pub fn new(val: usize, t: WidthType) -> Self { - Self { val, width_type: t } - } -} - -impl Default for CellMargin { - fn default() -> CellMargin { - CellMargin { - val: 55, - width_type: WidthType::Dxa, - } - } -} +use crate::CellMargin; #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] diff --git a/docx-core/src/documents/elements/table_cell_property.rs b/docx-core/src/documents/elements/table_cell_property.rs index 0c1f77e..9a7f476 100644 --- a/docx-core/src/documents/elements/table_cell_property.rs +++ b/docx-core/src/documents/elements/table_cell_property.rs @@ -8,7 +8,7 @@ use crate::types::*; use crate::xml_builder::*; #[cfg_attr(feature = "wasm", wasm_bindgen)] -#[derive(Serialize, Debug, Clone, PartialEq)] +#[derive(Serialize, Debug, Clone, PartialEq, Default)] #[serde(rename_all = "camelCase")] pub struct TableCellProperty { width: Option, @@ -18,6 +18,8 @@ pub struct TableCellProperty { vertical_align: Option, text_direction: Option, shading: Option, + #[serde(skip_serializing_if = "Option::is_none")] + margins: Option, } impl TableCellProperty { @@ -74,19 +76,50 @@ impl TableCellProperty { self.borders = Some(self.borders.unwrap_or_default().clear_all()); self } -} -impl Default for TableCellProperty { - fn default() -> Self { - TableCellProperty { - width: None, - borders: None, - grid_span: None, - vertical_merge: None, - vertical_align: None, - text_direction: None, - shading: None, + pub fn margins(mut self, margins: CellMargins) -> Self { + self.margins = Some(margins); + self + } + + pub fn margin_top(mut self, v: usize, t: WidthType) -> Self { + if let Some(margins) = self.margins { + self.margins = Some(margins.margin_top(v, t)); + } else { + let margins = CellMargins::new(); + self.margins = Some(margins.margin_top(v, t)); } + self + } + + pub fn margin_right(mut self, v: usize, t: WidthType) -> Self { + if let Some(margins) = self.margins { + self.margins = Some(margins.margin_right(v, t)); + } else { + let margins = CellMargins::new(); + self.margins = Some(margins.margin_right(v, t)); + } + self + } + + pub fn margin_bottom(mut self, v: usize, t: WidthType) -> Self { + if let Some(margins) = self.margins { + self.margins = Some(margins.margin_bottom(v, t)); + } else { + let margins = CellMargins::new(); + self.margins = Some(margins.margin_bottom(v, t)); + } + self + } + + pub fn margin_left(mut self, v: usize, t: WidthType) -> Self { + if let Some(margins) = self.margins { + self.margins = Some(margins.margin_left(v, t)); + } else { + let margins = CellMargins::new(); + self.margins = Some(margins.margin_left(v, t)); + } + self } } @@ -101,6 +134,7 @@ impl BuildXML for TableCellProperty { .add_optional_child(&self.vertical_align) .add_optional_child(&self.text_direction) .add_optional_child(&self.shading) + .add_optional_child(&self.margins) .close() .build() } diff --git a/docx-core/src/reader/cell_margins.rs b/docx-core/src/reader/cell_margins.rs new file mode 100644 index 0000000..ef27d29 --- /dev/null +++ b/docx-core/src/reader/cell_margins.rs @@ -0,0 +1,54 @@ +use std::io::Read; +use std::str::FromStr; + +use xml::attribute::OwnedAttribute; +use xml::reader::{EventReader, XmlEvent}; + +use super::*; + +impl ElementReader for CellMargins { + fn read(r: &mut EventReader, _: &[OwnedAttribute]) -> Result { + let mut margins = CellMargins::default(); + loop { + let e = r.next(); + match e { + Ok(XmlEvent::StartElement { + attributes, name, .. + }) => { + let e = XMLElement::from_str(&name.local_name).unwrap(); + match e { + XMLElement::Top => { + if let Ok(width) = read_width(&attributes) { + margins = margins.margin_top(width.0 as usize, width.1) + } + } + XMLElement::Right => { + if let Ok(width) = read_width(&attributes) { + margins = margins.margin_right(width.0 as usize, width.1) + } + } + XMLElement::Bottom => { + if let Ok(width) = read_width(&attributes) { + margins = margins.margin_bottom(width.0 as usize, width.1) + } + } + XMLElement::Left => { + if let Ok(width) = read_width(&attributes) { + margins = margins.margin_left(width.0 as usize, width.1) + } + } + _ => {} + } + } + Ok(XmlEvent::EndElement { name, .. }) => { + let e = XMLElement::from_str(&name.local_name).unwrap(); + if e == XMLElement::CellMargins { + return Ok(margins); + } + } + Err(_) => return Err(ReaderError::XMLReadError), + _ => {} + } + } + } +} diff --git a/docx-core/src/reader/mod.rs b/docx-core/src/reader/mod.rs index 69b52ab..b1cfcf5 100644 --- a/docx-core/src/reader/mod.rs +++ b/docx-core/src/reader/mod.rs @@ -3,6 +3,7 @@ mod a_graphic_data; mod attributes; mod bookmark_end; mod bookmark_start; +mod cell_margins; mod comment; mod comment_extended; mod comments; diff --git a/docx-core/src/reader/table_cell_property.rs b/docx-core/src/reader/table_cell_property.rs index bcc8a7c..5799224 100644 --- a/docx-core/src/reader/table_cell_property.rs +++ b/docx-core/src/reader/table_cell_property.rs @@ -67,6 +67,11 @@ impl ElementReader for TableCellProperty { let borders = TableCellBorders::read(r, &attributes)?; property = property.set_borders(borders); } + XMLElement::CellMargins => { + if let Ok(margins) = CellMargins::read(r, &attributes) { + property = property.margins(margins); + } + } _ => {} } } diff --git a/docx-core/src/reader/xml_element.rs b/docx-core/src/reader/xml_element.rs index b222898..e4230cb 100644 --- a/docx-core/src/reader/xml_element.rs +++ b/docx-core/src/reader/xml_element.rs @@ -99,6 +99,7 @@ pub enum XMLElement { TablePropertyChange, TableRowPropertyChange, TableCellPropertyChange, + CellMargins, Top, Right, End, @@ -340,6 +341,7 @@ impl FromStr for XMLElement { "tblPrChange" => Ok(XMLElement::TablePropertyChange), "trPrChange" => Ok(XMLElement::TableRowPropertyChange), "tcPrChange" => Ok(XMLElement::TableCellPropertyChange), + "tcMar" => Ok(XMLElement::CellMargins), "tblGridChange" => Ok(XMLElement::TableGridChange), "gridCol" => Ok(XMLElement::GridCol), "style" => Ok(XMLElement::Style), diff --git a/docx-core/src/types/border_position.rs b/docx-core/src/types/border_position.rs index eb49028..916167b 100644 --- a/docx-core/src/types/border_position.rs +++ b/docx-core/src/types/border_position.rs @@ -14,7 +14,7 @@ pub enum TableBorderPosition { InsideV, } -#[cfg_attr(feature = "wasm", wasm_bindgen)] +#[cfg_attr(feature = "wasm", wasm_bindgen, derive(ts_rs::TS), ts(export))] #[derive(Debug, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub enum TableCellBorderPosition { diff --git a/docx-core/src/types/border_type.rs b/docx-core/src/types/border_type.rs index d3212de..3bf6cad 100644 --- a/docx-core/src/types/border_type.rs +++ b/docx-core/src/types/border_type.rs @@ -9,7 +9,7 @@ use wasm_bindgen::prelude::*; use super::errors; use std::str::FromStr; -#[cfg_attr(feature = "wasm", wasm_bindgen)] +#[cfg_attr(feature = "wasm", wasm_bindgen, derive(ts_rs::TS), ts(export))] #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum BorderType { diff --git a/docx-core/src/xml_builder/elements.rs b/docx-core/src/xml_builder/elements.rs index 27a9684..e863eed 100644 --- a/docx-core/src/xml_builder/elements.rs +++ b/docx-core/src/xml_builder/elements.rs @@ -319,6 +319,7 @@ impl XMLBuilder { open!(open_table_cell_borders, "w:tcBorders"); open!(open_table_borders, "w:tblBorders"); open!(open_table_cell_margins, "w:tblCellMar"); + open!(open_cell_margins, "w:tcMar"); closed!(table_layout, "w:tblLayout", "w:type"); closed_with_str!(table_style, "w:tblStyle"); diff --git a/docx-wasm/js/json/bindings/BorderType.ts b/docx-wasm/js/json/bindings/BorderType.ts index ae262fa..f66c613 100644 --- a/docx-wasm/js/json/bindings/BorderType.ts +++ b/docx-wasm/js/json/bindings/BorderType.ts @@ -1,3 +1,2 @@ -// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type BorderType = "nil" | "none" | "single" | "thick" | "double" | "dotted" | "dashed" | "dotdash" | "dotdotdash" | "triple" | "thinthicksmallgap" | "thickthinsmallgap" | "thinthickthinsmallgap" | "thinthickmediumgap" | "thickthinmediumgap" | "thinthickthinmediumgap" | "thinthicklargegap" | "thickthinlargegap" | "thinthickthinlargegap" | "wave" | "doublewave" | "dashsmallgap" | "dashdotstroked" | "threedemboss" | "threedengrave" | "outset" | "inset" | "apples" | "archedscallops" | "babypacifier" | "babyrattle"; \ No newline at end of file +export type BorderType = "nil" | "none" | "single" | "thick" | "double" | "dotted" | "dashed" | "dotDash" | "dotDotDash" | "triple" | "thinThickSmallGap" | "thickThinSmallGap" | "thinThickThinSmallGap" | "thinThickMediumGap" | "thickThinMediumGap" | "thinThickThinMediumGap" | "thinThickLargeGap" | "thickThinLargeGap" | "thinThickThinLargeGap" | "wave" | "doubleWave" | "dashSmallGap" | "dashDotStroked" | "threeDEmboss" | "threeDEngrave" | "outset" | "inset" | "apples" | "archedScallops" | "babyPacifier" | "babyRattle"; \ No newline at end of file diff --git a/docx-wasm/js/json/bindings/TableCellBorder.ts b/docx-wasm/js/json/bindings/TableCellBorder.ts new file mode 100644 index 0000000..6f187ad --- /dev/null +++ b/docx-wasm/js/json/bindings/TableCellBorder.ts @@ -0,0 +1,4 @@ +import type { BorderType } from "./BorderType"; +import type { TableCellBorderPosition } from "./TableCellBorderPosition"; + +export interface TableCellBorder { borderType: BorderType, size: number, color: string, position: TableCellBorderPosition, space: number, } \ No newline at end of file diff --git a/docx-wasm/js/json/bindings/TableCellBorderPosition.ts b/docx-wasm/js/json/bindings/TableCellBorderPosition.ts new file mode 100644 index 0000000..12141ec --- /dev/null +++ b/docx-wasm/js/json/bindings/TableCellBorderPosition.ts @@ -0,0 +1,2 @@ + +export type TableCellBorderPosition = "left" | "right" | "top" | "bottom" | "insideH" | "insideV" | "tl2Br" | "tr2Bl"; \ No newline at end of file diff --git a/docx-wasm/js/json/bindings/TableCellBorders.ts b/docx-wasm/js/json/bindings/TableCellBorders.ts new file mode 100644 index 0000000..79977c5 --- /dev/null +++ b/docx-wasm/js/json/bindings/TableCellBorders.ts @@ -0,0 +1,3 @@ +import type { TableCellBorder } from "./TableCellBorder"; + +export interface TableCellBorders { top: TableCellBorder | null, left: TableCellBorder | null, bottom: TableCellBorder | null, right: TableCellBorder | null, insideH: TableCellBorder | null, insideV: TableCellBorder | null, tr2Bl: TableCellBorder | null, tl2Br: TableCellBorder | null, } \ No newline at end of file diff --git a/docx-wasm/js/json/table.ts b/docx-wasm/js/json/table.ts index 5b4db3a..87b4832 100644 --- a/docx-wasm/js/json/table.ts +++ b/docx-wasm/js/json/table.ts @@ -4,9 +4,13 @@ import { HeightRule } from "../table-row"; import { TextDirectionType } from "../table-cell"; import { ShadingJSON } from "./shading"; import { TableLayoutType } from "../table"; -import { DeleteJSONData, InsertJSONData } from ".."; +import { DeleteJSONData, InsertJSONData, TableCellBordersJSON } from ".."; import { StructuredTagJSON } from "./structured-data-tag"; +export { TableCellBorder as TableCellBorderJSON } from "./bindings/TableCellBorder"; + +export { TableCellBorders as TableCellBordersJSON } from "./bindings/TableCellBorders"; + export type TableCellChildJSON = ParagraphJSON | TableJSON | StructuredTagJSON; export type WidthType = "dxa" | "auto" | "pct" | "nil"; @@ -20,12 +24,13 @@ export type TableCellPropertyJSON = { width: number; widthType: WidthType; } | null; - borders: any | null; + borders: TableCellBordersJSON | null; gridSpan: number | null; verticalMerge: "restart" | "continue" | null; verticalAlign: "top" | "center" | "bottom" | null; textDirection: TextDirectionType | null; shading: ShadingJSON | null; + margins?: CellMarginsJSON; }; export type TableRowPropertyJSON = { @@ -64,6 +69,13 @@ export type TableCellMarginsJSON = { right: TableCellMarginJSON; }; +export type CellMarginsJSON = { + top: TableCellMarginJSON; + left: TableCellMarginJSON; + bottom: TableCellMarginJSON; + right: TableCellMarginJSON; +}; + export type TablePropertyJSON = { width: { width: number; diff --git a/docx-wasm/js/table-cell.ts b/docx-wasm/js/table-cell.ts index 8e3c4f5..8553bcd 100644 --- a/docx-wasm/js/table-cell.ts +++ b/docx-wasm/js/table-cell.ts @@ -1,5 +1,5 @@ import { Paragraph } from "./paragraph"; -import { Table } from "./table"; +import { Table, convertWidthType } from "./table"; import { Shading } from "./shading"; import { TableCellBorders, PositionKeys } from "./table-cell-borders"; import { TableCellBorderPosition, TableCellBorder } from "./table-cell-border"; @@ -7,6 +7,7 @@ import * as wasm from "./pkg"; import { convertBorderType } from "./run"; import { TableOfContents } from "./table-of-contents"; import { build } from "./builder"; +import { WidthType } from "."; export type VMergeType = "restart" | "continue"; @@ -61,6 +62,12 @@ export type CellProperty = { width?: number; textDirection?: TextDirectionType; shading?: Shading; + margins?: { + top?: { val: number; type: WidthType }; + left?: { val: number; type: WidthType }; + bottom?: { val: number; type: WidthType }; + right?: { val: number; type: WidthType }; + }; }; export class TableCell { @@ -137,6 +144,38 @@ export class TableCell { return this; } + marginTop(v: number, t: WidthType) { + this.property.margins = { + ...this.property.margins, + top: { val: v, type: t }, + }; + return this; + } + + marginLeft(v: number, t: WidthType) { + this.property.margins = { + ...this.property.margins, + left: { val: v, type: t }, + }; + return this; + } + + marginRight(v: number, t: WidthType) { + this.property.margins = { + ...this.property.margins, + right: { val: v, type: t }, + }; + return this; + } + + marginBottom(v: number, t: WidthType) { + this.property.margins = { + ...this.property.margins, + bottom: { val: v, type: t }, + }; + return this; + } + buildCellBorders(cell: wasm.TableCell): wasm.TableCell { if (this.property.borders.top) { const border = wasm @@ -224,6 +263,34 @@ export class TableCell { cell = cell.set_border(border); } + if (this.property.margins?.top) { + cell = cell.margin_top( + this.property.margins?.top.val, + convertWidthType(this.property.margins?.top.type) + ); + } + + if (this.property.margins?.right) { + cell = cell.margin_right( + this.property.margins?.right.val, + convertWidthType(this.property.margins?.right.type) + ); + } + + if (this.property.margins?.bottom) { + cell = cell.margin_bottom( + this.property.margins?.bottom.val, + convertWidthType(this.property.margins?.bottom.type) + ); + } + + if (this.property.margins?.left) { + cell = cell.margin_left( + this.property.margins?.left.val, + convertWidthType(this.property.margins?.left.type) + ); + } + return cell; } diff --git a/docx-wasm/package.json b/docx-wasm/package.json index 632f5cc..1a94058 100644 --- a/docx-wasm/package.json +++ b/docx-wasm/package.json @@ -1,6 +1,6 @@ { "name": "docx-wasm", - "version": "0.4.12-beta9", + "version": "0.4.12-beta15", "main": "dist/node/index.js", "browser": "dist/web/index.js", "author": "bokuweb ", @@ -52,3 +52,4 @@ "types": "dist/web/index.d.ts", "dependencies": {} } + diff --git a/docx-wasm/src/table_cell.rs b/docx-wasm/src/table_cell.rs index aade4f1..1f9792a 100644 --- a/docx-wasm/src/table_cell.rs +++ b/docx-wasm/src/table_cell.rs @@ -1,7 +1,7 @@ use std::str::FromStr; use super::*; -use docx_rs::Shading; +use docx_rs::{Shading, WidthType}; use wasm_bindgen::prelude::*; #[wasm_bindgen] @@ -92,4 +92,24 @@ impl TableCell { self.0.property = self.0.property.clear_all_border(); self } + + pub fn margin_top(mut self, v: usize, t: WidthType) -> Self { + self.0.property = self.0.property.margin_top(v, t); + self + } + + pub fn margin_right(mut self, v: usize, t: WidthType) -> Self { + self.0.property = self.0.property.margin_right(v, t); + self + } + + pub fn margin_bottom(mut self, v: usize, t: WidthType) -> Self { + self.0.property = self.0.property.margin_bottom(v, t); + self + } + + pub fn margin_left(mut self, v: usize, t: WidthType) -> Self { + self.0.property = self.0.property.margin_left(v, t); + self + } }