feat: Support table cell border (#63)

* feat: Support table cell border

* fix: lint

* fix: lint error
main
bokuweb 2020-04-27 10:41:23 +09:00 committed by GitHub
parent 6e9c10b385
commit 5c6d95aaf7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 344 additions and 41 deletions

View File

@ -0,0 +1,44 @@
use docx_rs::*;
pub fn main() -> Result<(), DocxError> {
let path = std::path::Path::new("./table_border.docx");
let file = std::fs::File::create(&path).unwrap();
let table = Table::new(vec![
TableRow::new(vec![
TableCell::new()
.add_paragraph(Paragraph::new())
.grid_span(2)
.clear_border(BorderPosition::Left)
.clear_border(BorderPosition::Bottom)
.clear_border(BorderPosition::Right),
TableCell::new()
.add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello")))
.vertical_align(VAlignType::Center)
.vertical_merge(VMergeType::Restart),
]),
TableRow::new(vec![
TableCell::new()
.add_paragraph(Paragraph::new())
.vertical_merge(VMergeType::Restart)
.clear_all_border(),
TableCell::new().add_paragraph(Paragraph::new()),
TableCell::new()
.add_paragraph(Paragraph::new())
.vertical_merge(VMergeType::Continue),
]),
TableRow::new(vec![
TableCell::new()
.add_paragraph(Paragraph::new())
.vertical_merge(VMergeType::Continue),
TableCell::new().add_paragraph(Paragraph::new()),
TableCell::new()
.add_paragraph(Paragraph::new())
.vertical_merge(VMergeType::Continue),
]),
])
.set_grid(vec![2000, 2000, 2000])
.indent(1000);
Docx::new().add_table(table).build().pack(file)?;
Ok(())
}

View File

@ -1,7 +1,7 @@
use serde::ser::{SerializeStruct, Serializer}; use serde::ser::{SerializeStruct, Serializer};
use serde::Serialize; use serde::Serialize;
use super::{Paragraph, TableCellProperty}; use super::*;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::types::*; use crate::types::*;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -67,6 +67,21 @@ impl TableCell {
self.property = self.property.width(v, t); self.property = self.property.width(v, t);
self self
} }
pub fn set_border(mut self, border: TableCellBorder) -> Self {
self.property = self.property.set_border(border);
self
}
pub fn clear_border(mut self, position: BorderPosition) -> Self {
self.property = self.property.clear_border(position);
self
}
pub fn clear_all_border(mut self) -> Self {
self.property = self.property.clear_all_border();
self
}
} }
impl Default for TableCell { impl Default for TableCell {
@ -97,7 +112,6 @@ impl BuildXML for TableCell {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::super::*;
use super::*; use super::*;
#[cfg(test)] #[cfg(test)]
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;

View File

@ -16,15 +16,14 @@ use crate::xml_builder::*;
tl2br diagonal border from top left corner to bottom right corner tl2br diagonal border from top left corner to bottom right corner
tr2bl diagonal border from top right corner to bottom left corner tr2bl diagonal border from top right corner to bottom left corner
*/ */
#[derive(Serialize, Debug, Clone, PartialEq)] #[derive(Serialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct TableCellBorder { pub struct TableCellBorder {
pub border_type: BorderType,
pub size: usize,
pub color: String,
position: BorderPosition, position: BorderPosition,
border_type: BorderType,
size: usize,
space: usize, space: usize,
color: String,
} }
impl TableCellBorder { impl TableCellBorder {
@ -42,6 +41,16 @@ impl TableCellBorder {
self.color = color.into(); self.color = color.into();
self self
} }
pub fn size(mut self, size: usize) -> TableCellBorder {
self.size = size;
self
}
pub fn border_type(mut self, border_type: BorderType) -> TableCellBorder {
self.border_type = border_type;
self
}
} }
impl BuildXML for TableCellBorder { impl BuildXML for TableCellBorder {
@ -100,7 +109,7 @@ impl TableCellBorders {
Default::default() Default::default()
} }
pub fn set_border(mut self, border: TableCellBorder) -> Self { pub fn set(mut self, border: TableCellBorder) -> Self {
match border.position { match border.position {
BorderPosition::Top => self.top = Some(border), BorderPosition::Top => self.top = Some(border),
BorderPosition::Left => self.left = Some(border), BorderPosition::Left => self.left = Some(border),
@ -112,17 +121,31 @@ impl TableCellBorders {
self self
} }
pub fn clear_border(mut self, position: BorderPosition) -> Self { pub fn clear(mut self, position: BorderPosition) -> Self {
let nil = TableCellBorder::new(position.clone()).border_type(BorderType::Nil);
match position { match position {
BorderPosition::Top => self.top = None, BorderPosition::Top => self.top = Some(nil),
BorderPosition::Left => self.left = None, BorderPosition::Left => self.left = Some(nil),
BorderPosition::Bottom => self.bottom = None, BorderPosition::Bottom => self.bottom = Some(nil),
BorderPosition::Right => self.right = None, BorderPosition::Right => self.right = Some(nil),
BorderPosition::InsideH => self.inside_h = None, BorderPosition::InsideH => self.inside_h = Some(nil),
BorderPosition::InsideV => self.inside_v = None, BorderPosition::InsideV => self.inside_v = Some(nil),
}; };
self self
} }
pub fn clear_all(mut self) -> Self {
self.top = Some(TableCellBorder::new(BorderPosition::Top).border_type(BorderType::Nil));
self.left = Some(TableCellBorder::new(BorderPosition::Left).border_type(BorderType::Nil));
self.bottom =
Some(TableCellBorder::new(BorderPosition::Bottom).border_type(BorderType::Nil));
self.right = Some(TableCellBorder::new(BorderPosition::Right).border_type(BorderType::Nil));
self.inside_h =
Some(TableCellBorder::new(BorderPosition::InsideH).border_type(BorderType::Nil));
self.inside_v =
Some(TableCellBorder::new(BorderPosition::InsideV).border_type(BorderType::Nil));
self
}
} }
impl BuildXML for TableCellBorders { impl BuildXML for TableCellBorders {

View File

@ -1,6 +1,6 @@
use serde::Serialize; use serde::Serialize;
use super::{GridSpan, TableCellBorders, TableCellWidth, VAlign, VMerge}; use super::*;
use crate::documents::BuildXML; use crate::documents::BuildXML;
use crate::types::*; use crate::types::*;
use crate::xml_builder::*; use crate::xml_builder::*;
@ -39,6 +39,21 @@ impl TableCellProperty {
self.grid_span = Some(GridSpan::new(v)); self.grid_span = Some(GridSpan::new(v));
self self
} }
pub fn set_border(mut self, border: TableCellBorder) -> Self {
self.borders = Some(self.borders.unwrap_or_default().set(border));
self
}
pub fn clear_border(mut self, position: BorderPosition) -> Self {
self.borders = Some(self.borders.unwrap_or_default().clear(position));
self
}
pub fn clear_all_border(mut self) -> Self {
self.borders = Some(self.borders.unwrap_or_default().clear_all());
self
}
} }
impl Default for TableCellProperty { impl Default for TableCellProperty {

View File

@ -29,7 +29,6 @@ mod xml_element;
mod level_override; mod level_override;
use std::io::Cursor; use std::io::Cursor;
use zip;
use crate::documents::*; use crate::documents::*;

View File

@ -1,5 +1,4 @@
use std::io::{Cursor, Read}; use std::io::{Cursor, Read};
use zip;
use super::ReaderError; use super::ReaderError;

View File

@ -1,5 +1,7 @@
use wasm_bindgen::prelude::*;
use serde::Serialize; use serde::Serialize;
#[wasm_bindgen]
#[derive(Debug, Clone, PartialEq, Serialize)] #[derive(Debug, Clone, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub enum BorderPosition { pub enum BorderPosition {

View File

@ -9,6 +9,7 @@ use wasm_bindgen::prelude::*;
#[derive(Copy, Clone, Debug, PartialEq, Serialize)] #[derive(Copy, Clone, Debug, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub enum BorderType { pub enum BorderType {
Nil,
None, None,
Single, Single,
Thick, Thick,
@ -23,6 +24,7 @@ pub enum BorderType {
impl fmt::Display for BorderType { impl fmt::Display for BorderType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { match *self {
BorderType::Nil => write!(f, "nil"),
BorderType::None => write!(f, "none"), BorderType::None => write!(f, "none"),
BorderType::Single => write!(f, "single"), BorderType::Single => write!(f, "single"),
BorderType::Thick => write!(f, "thick"), BorderType::Thick => write!(f, "thick"),

View File

@ -45,7 +45,7 @@ export class Docx {
buildRun(r: Run) { buildRun(r: Run) {
let run = wasm.createRun(); let run = wasm.createRun();
r.children.forEach(child => { r.children.forEach((child) => {
if (child instanceof Text) { if (child instanceof Text) {
run = run.add_text(child.text); run = run.add_text(child.text);
} else if (child instanceof DeleteText) { } else if (child instanceof DeleteText) {
@ -134,7 +134,7 @@ export class Docx {
buildParagraph(p: Paragraph) { buildParagraph(p: Paragraph) {
let paragraph = wasm.createParagraph(); let paragraph = wasm.createParagraph();
p.children.forEach(child => { p.children.forEach((child) => {
if (child instanceof Run) { if (child instanceof Run) {
const run = this.buildRun(child); const run = this.buildRun(child);
paragraph = paragraph.add_run(run); paragraph = paragraph.add_run(run);
@ -205,9 +205,9 @@ export class Docx {
buildTable(t: Table) { buildTable(t: Table) {
let table = wasm.createTable(); let table = wasm.createTable();
t.rows.forEach(r => { t.rows.forEach((r) => {
let row = wasm.createTableRow(); let row = wasm.createTableRow();
r.cells.forEach(c => { r.cells.forEach((c) => {
const cell = this.buildCell(c); const cell = this.buildCell(c);
row = row.add_cell(cell); row = row.add_cell(cell);
}); });
@ -237,7 +237,7 @@ export class Docx {
buildCell(c: TableCell) { buildCell(c: TableCell) {
let cell = wasm.createTableCell(); let cell = wasm.createTableCell();
c.children.forEach(p => { c.children.forEach((p) => {
const paragraph = this.buildParagraph(p); const paragraph = this.buildParagraph(p);
cell = cell.add_paragraph(paragraph); cell = cell.add_paragraph(paragraph);
}); });
@ -270,12 +270,68 @@ export class Docx {
if (typeof c.property.width !== "undefined") { if (typeof c.property.width !== "undefined") {
cell = cell.width(c.property.width); cell = cell.width(c.property.width);
} }
if (typeof c.property.borders !== "undefined") {
if (c.property.borders.top) {
const border = wasm
.createTableCellBorder(wasm.BorderPosition.Top)
.size(c.property.borders.top.size)
.color(c.property.borders.top.color)
.border_type(c.property.borders.top.border_type);
cell = cell.set_border(border);
}
if (c.property.borders.right) {
const border = wasm
.createTableCellBorder(wasm.BorderPosition.Right)
.size(c.property.borders.right.size)
.color(c.property.borders.right.color)
.border_type(c.property.borders.right.border_type);
cell = cell.set_border(border);
}
if (c.property.borders.bottom) {
const border = wasm
.createTableCellBorder(wasm.BorderPosition.Bottom)
.size(c.property.borders.bottom.size)
.color(c.property.borders.bottom.color)
.border_type(c.property.borders.bottom.border_type);
cell = cell.set_border(border);
}
if (c.property.borders.left) {
const border = wasm
.createTableCellBorder(wasm.BorderPosition.Left)
.size(c.property.borders.left.size)
.color(c.property.borders.left.color)
.border_type(c.property.borders.left.border_type);
cell = cell.set_border(border);
}
if (c.property.borders.insideH) {
const border = wasm
.createTableCellBorder(wasm.BorderPosition.InsideH)
.size(c.property.borders.insideH.size)
.color(c.property.borders.insideH.color)
.border_type(c.property.borders.insideH.border_type);
cell = cell.set_border(border);
}
if (c.property.borders.insideV) {
const border = wasm
.createTableCellBorder(wasm.BorderPosition.InsideV)
.size(c.property.borders.insideV.size)
.color(c.property.borders.insideV.color)
.border_type(c.property.borders.insideV.border_type);
cell = cell.set_border(border);
}
}
return cell; return cell;
} }
build() { build() {
let docx = wasm.createDocx(); let docx = wasm.createDocx();
this.children.forEach(child => { this.children.forEach((child) => {
if (child instanceof Paragraph) { if (child instanceof Paragraph) {
let p = this.buildParagraph(child); let p = this.buildParagraph(child);
docx = docx.add_paragraph(p); docx = docx.add_paragraph(p);
@ -285,16 +341,16 @@ export class Docx {
} }
}); });
this.abstractNumberings.forEach(n => { this.abstractNumberings.forEach((n) => {
let num = wasm.createAbstractNumbering(n.id); let num = wasm.createAbstractNumbering(n.id);
n.levels.forEach(l => { n.levels.forEach((l) => {
const level = wasm.createLevel(l.id, l.start, l.format, l.text, l.jc); const level = wasm.createLevel(l.id, l.start, l.format, l.text, l.jc);
num = num.add_level(level); num = num.add_level(level);
}); });
docx = docx.add_abstract_numbering(num); docx = docx.add_abstract_numbering(num);
}); });
this.numberings.forEach(n => { this.numberings.forEach((n) => {
let num = wasm.createNumbering(n.id, n.abstractNumId); let num = wasm.createNumbering(n.id, n.abstractNumId);
docx = docx.add_numbering(num); docx = docx.add_numbering(num);
}); });

View File

@ -0,0 +1,46 @@
export type BorderType =
| "Nil"
| "None"
| "Single"
| "Thick"
| "Double"
| "Dotted"
| "Dashed"
| "DotDash"
| "DotDotDash"
| "Triple";
export type BorderPosition =
| "Left"
| "Right"
| "Top"
| "Bottom"
| "InsideH"
| "InsideV";
export class TableCellBorder {
_border_type: BorderType;
_size = 2;
_color = "000000";
position: BorderPosition;
space = 0;
constructor(position: BorderPosition) {
this.position = position;
}
color(color: string) {
this._color = color;
return this;
}
size(size: number) {
this._size = size;
return this;
}
border_type(border_type: BorderType) {
this._border_type = border_type;
return this;
}
}

View File

@ -0,0 +1,57 @@
import { BorderPosition, TableCellBorder } from "./table-cell-border";
export class TableCellBorders {
top: TableCellBorder | null = new TableCellBorder("Top");
left: TableCellBorder | null = new TableCellBorder("Left");
bottom: TableCellBorder | null = new TableCellBorder("Bottom");
right: TableCellBorder | null = new TableCellBorder("Right");
insideH: TableCellBorder | null = new TableCellBorder("InsideH");
insideV: TableCellBorder | null = new TableCellBorder("InsideV");
set(border: TableCellBorder) {
switch (border.position) {
case "Top":
this.top = border;
case "Left":
this.left = border;
case "Bottom":
this.bottom = border;
case "Right":
this.right = border;
case "InsideH":
this.insideH = border;
case "InsideV":
this.insideV = border;
}
return this;
}
clear(position: BorderPosition) {
let nil = new TableCellBorder(position).border_type("Nil");
switch (position) {
case "Top":
this.top = nil;
case "Left":
this.left = nil;
case "Bottom":
this.bottom = nil;
case "Right":
this.right = nil;
case "InsideH":
this.insideH = nil;
case "InsideV":
this.insideV = nil;
}
return this;
}
clearAll() {
this.top = new TableCellBorder("Top").border_type("Nil");
this.left = new TableCellBorder("Left").border_type("Nil");
this.bottom = new TableCellBorder("Bottom").border_type("Nil");
this.right = new TableCellBorder("Right").border_type("Nil");
this.insideH = new TableCellBorder("InsideH").border_type("Nil");
this.insideV = new TableCellBorder("InsideV").border_type("Nil");
return this;
}
}

View File

@ -1,10 +1,13 @@
import { Paragraph } from "./paragraph"; import { Paragraph } from "./paragraph";
import { TableCellBorders } from "./table-cell-borders";
import { BorderPosition, TableCellBorder } from "./table-cell-border";
export type VMergeType = "restart" | "continue"; export type VMergeType = "restart" | "continue";
export type VAlignType = "top" | "center" | "bottom"; export type VAlignType = "top" | "center" | "bottom";
export type CellProperty = { export type CellProperty = {
borders?: TableCellBorders;
verticalMerge?: VMergeType; verticalMerge?: VMergeType;
verticalAlign?: VAlignType; verticalAlign?: VAlignType;
gridSpan?: number; gridSpan?: number;
@ -39,4 +42,16 @@ export class TableCell {
this.property.width = v; this.property.width = v;
return this; return this;
} }
setBorder(position: BorderPosition, border: TableCellBorder) {
this.property.borders[position] = border;
return this;
}
clearBorder(position: BorderPosition) {
this.property.borders[position] = new TableCellBorder(position).border_type(
"Nil"
);
return this;
}
} }

View File

@ -1,5 +1,4 @@
use super::*; use super::*;
use docx_rs;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
#[wasm_bindgen] #[wasm_bindgen]

View File

@ -1,5 +1,3 @@
use docx_rs;
pub fn create_special_indent( pub fn create_special_indent(
special_indent_kind: Option<docx_rs::SpecialIndentKind>, special_indent_kind: Option<docx_rs::SpecialIndentKind>,
special_indent_size: Option<i32>, special_indent_size: Option<i32>,

View File

@ -1,5 +1,4 @@
use super::*; use super::*;
use docx_rs;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
#[wasm_bindgen] #[wasm_bindgen]

View File

@ -1,5 +1,4 @@
use super::*; use super::*;
use docx_rs;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
#[wasm_bindgen] #[wasm_bindgen]

View File

@ -1,5 +1,4 @@
use super::*; use super::*;
use docx_rs;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
#[wasm_bindgen] #[wasm_bindgen]

View File

@ -1,5 +1,4 @@
use super::*; use super::*;
use docx_rs;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
#[wasm_bindgen] #[wasm_bindgen]

View File

@ -1,5 +1,4 @@
use super::*; use super::*;
use docx_rs;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
#[wasm_bindgen] #[wasm_bindgen]

View File

@ -12,6 +12,7 @@ mod run;
mod table; mod table;
mod table_cell; mod table_cell;
mod table_row; mod table_row;
mod table_cell_border;
pub use abstract_numbering::*; pub use abstract_numbering::*;
pub use adaptors::*; pub use adaptors::*;
@ -27,3 +28,4 @@ pub use run::*;
pub use table::*; pub use table::*;
pub use table_cell::*; pub use table_cell::*;
pub use table_row::*; pub use table_row::*;
pub use table_cell_border::*;

View File

@ -1,4 +1,3 @@
use docx_rs;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
#[wasm_bindgen] #[wasm_bindgen]

View File

@ -1,5 +1,4 @@
use super::*; use super::*;
use docx_rs;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
#[wasm_bindgen] #[wasm_bindgen]

View File

@ -1,4 +1,3 @@
use docx_rs;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
#[allow(non_snake_case)] #[allow(non_snake_case)]

View File

@ -1,4 +1,3 @@
use docx_rs;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
#[wasm_bindgen] #[wasm_bindgen]

View File

@ -1,5 +1,4 @@
use super::*; use super::*;
use docx_rs;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
#[wasm_bindgen] #[wasm_bindgen]

View File

@ -1,5 +1,4 @@
use super::*; use super::*;
use docx_rs;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
#[wasm_bindgen] #[wasm_bindgen]
@ -45,4 +44,19 @@ impl TableCell {
self.0.property = self.0.property.width(v, docx_rs::WidthType::DXA); self.0.property = self.0.property.width(v, docx_rs::WidthType::DXA);
self self
} }
pub fn set_border(mut self, border: TableCellBorder) -> TableCell {
self.0.property = self.0.property.set_border(border.take());
self
}
pub fn clear_border(mut self, position: docx_rs::BorderPosition) -> TableCell {
self.0.property = self.0.property.clear_border(position);
self
}
pub fn clear_all_border(mut self) -> TableCell {
self.0.property = self.0.property.clear_all_border();
self
}
} }

View File

@ -0,0 +1,29 @@
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
#[derive(Debug)]
pub struct TableCellBorder(docx_rs::TableCellBorder);
#[wasm_bindgen(js_name = createTableCellBorder)]
pub fn create_table_cell_border(position: docx_rs::BorderPosition) -> TableCellBorder {
TableCellBorder(docx_rs::TableCellBorder::new(position))
}
impl TableCellBorder {
pub fn take(self) -> docx_rs::TableCellBorder {
self.0
}
}
#[wasm_bindgen]
impl TableCellBorder {
pub fn color(mut self, color: String) -> TableCellBorder {
self.0.color = color;
self
}
pub fn border_type(mut self, border_type: docx_rs::BorderType) -> TableCellBorder {
self.0.border_type = border_type;
self
}
}

View File

@ -1,5 +1,4 @@
use super::*; use super::*;
use docx_rs;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
#[wasm_bindgen] #[wasm_bindgen]