diff --git a/docx-core/examples/style.rs b/docx-core/examples/style.rs new file mode 100644 index 0000000..a3de126 --- /dev/null +++ b/docx-core/examples/style.rs @@ -0,0 +1,40 @@ +use docx_rs::*; + +pub fn main() -> Result<(), DocxError> { + let path = std::path::Path::new("./output/examples/style.docx"); + let file = std::fs::File::create(&path).unwrap(); + + let p1 = Paragraph::new() + .add_run(Run::new().add_text("Hello").style("Run1")) + .add_run(Run::new().add_text(" World")) + .style("Heading1") + .page_break_before(true); + + let table = + Table::new(vec![TableRow::new(vec![TableCell::new().add_paragraph( + Paragraph::new().add_run(Run::new().add_text("Hello")), + )])]) + .style("Table1"); + + let style1 = Style::new("Heading1", StyleType::Paragraph) + .name("Heading 1") + .align(AlignmentType::Center); + + let style2 = Style::new("Run1", StyleType::Character) + .name("Run test") + .bold(); + + let style3 = Style::new("Table1", StyleType::Table) + .name("Table test") + .table_align(TableAlignmentType::Center); + + Docx::new() + .add_style(style1) + .add_style(style2) + .add_style(style3) + .add_paragraph(p1) + .add_table(table) + .build() + .pack(file)?; + Ok(()) +} diff --git a/docx-core/examples/table.rs b/docx-core/examples/table.rs index a52289d..f1d04f8 100644 --- a/docx-core/examples/table.rs +++ b/docx-core/examples/table.rs @@ -4,40 +4,9 @@ pub fn main() -> Result<(), DocxError> { let path = std::path::Path::new("./table.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) - .shading(Shading::new().fill("FF0000")), - TableCell::new() - .add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello"))) - .vertical_align(VAlignType::Center) - .vertical_merge(VMergeType::Restart) - .text_direction(TextDirectionType::TbRlV), - ]), - TableRow::new(vec![ - TableCell::new() - .add_paragraph(Paragraph::new()) - .vertical_merge(VMergeType::Restart), - 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]) - .layout(TableLayoutType::Fixed) - .indent(1000); + let table = Table::new(vec![TableRow::new(vec![ + TableCell::new().add_paragraph(Paragraph::new()) + ])]); Docx::new().add_table(table).build().pack(file)?; Ok(()) } diff --git a/docx-core/src/documents/elements/run_property.rs b/docx-core/src/documents/elements/run_property.rs index 2b75902..dfc941d 100644 --- a/docx-core/src/documents/elements/run_property.rs +++ b/docx-core/src/documents/elements/run_property.rs @@ -163,6 +163,7 @@ impl BuildXML for RunProperty { .add_optional_child(&self.del) .add_optional_child(&self.vert_align) .add_optional_child(&self.character_spacing) + .add_optional_child(&self.style) .close() .build() } diff --git a/docx-core/src/documents/elements/style.rs b/docx-core/src/documents/elements/style.rs index cac87b5..6c943ac 100644 --- a/docx-core/src/documents/elements/style.rs +++ b/docx-core/src/documents/elements/style.rs @@ -100,6 +100,16 @@ impl Style { self } + pub fn text_border(mut self, b: TextBorder) -> Self { + self.run_property = self.run_property.text_border(b); + self + } + + pub fn fonts(mut self, f: RunFonts) -> Self { + self.run_property = self.run_property.fonts(f); + self + } + pub fn align(mut self, alignment_type: AlignmentType) -> Self { self.paragraph_property = self.paragraph_property.align(alignment_type); self @@ -138,6 +148,56 @@ impl Style { self } + pub fn table_indent(mut self, v: i32) -> Self { + self.table_property = self.table_property.indent(v); + self + } + + pub fn table_align(mut self, v: TableAlignmentType) -> Self { + self.table_property = self.table_property.align(v); + self + } + + pub fn style(mut self, s: impl Into) -> Self { + self.table_property = self.table_property.style(s); + self + } + + pub fn layout(mut self, t: TableLayoutType) -> Self { + self.table_property = self.table_property.layout(t); + self + } + + pub fn width(mut self, w: usize, t: WidthType) -> Self { + self.table_property = self.table_property.width(w, t); + self + } + + pub fn margins(mut self, margins: TableCellMargins) -> Self { + self.table_property = self.table_property.set_margins(margins); + self + } + + pub fn set_borders(mut self, borders: TableBorders) -> Self { + self.table_property = self.table_property.set_borders(borders); + self + } + + pub fn set_border(mut self, border: TableBorder) -> Self { + self.table_property = self.table_property.set_border(border); + self + } + + pub fn clear_border(mut self, position: TableBorderPosition) -> Self { + self.table_property = self.table_property.clear_border(position); + self + } + + pub fn clear_all_border(mut self) -> Self { + self.table_property = self.table_property.clear_all_border(); + self + } + pub fn table_cell_property(mut self, p: TableCellProperty) -> Self { self.table_cell_property = p; self @@ -153,11 +213,13 @@ impl BuildXML for Style { .add_child(&self.name) .add_child(&self.run_property) .add_child(&self.paragraph_property); + if self.style_type == StyleType::Table { b = b .add_child(&self.table_cell_property) .add_child(&self.table_property); } + if let Some(ref based_on) = self.based_on { b = b.add_child(based_on) } diff --git a/docx-wasm/js/index.ts b/docx-wasm/js/index.ts index b4914f6..f0ab526 100644 --- a/docx-wasm/js/index.ts +++ b/docx-wasm/js/index.ts @@ -1,14 +1,13 @@ import { Paragraph } from "./paragraph"; -import { ParagraphProperty } from "./paragraph-property"; +import { ParagraphProperty, setParagraphProperty } from "./paragraph-property"; import { Insert } from "./insert"; import { Delete } from "./delete"; import { convertHyperlinkType, Hyperlink } from "./hyperlink"; import { DeleteText } from "./delete-text"; -import { Table } from "./table"; +import { setTableProperty, Table } from "./table"; import { TableOfContents } from "./table-of-contents"; import { TableCell, toTextDirectionWasmType } from "./table-cell"; -import { BorderType } from "./border"; -import { Run, RunFonts } from "./run"; +import { convertBorderType, Run, RunFonts, setRunProperty } from "./run"; import { Text } from "./text"; import { Tab } from "./tab"; import { Break } from "./break"; @@ -37,52 +36,6 @@ import { DocGridType, DocxJSON } from "./json"; import * as wasm from "./pkg"; import { Level } from "./level"; -const convertBorderType = (t: BorderType) => { - switch (t) { - case "nil": - return wasm.BorderType.Nil; - case "none": - return wasm.BorderType.None; - case "single": - return wasm.BorderType.Single; - case "thick": - return wasm.BorderType.Thick; - case "double": - return wasm.BorderType.Double; - case "dotted": - return wasm.BorderType.Dotted; - case "dashed": - return wasm.BorderType.Dashed; - case "dotDash": - return wasm.BorderType.DotDash; - case "dotDotDash": - return wasm.BorderType.DotDotDash; - case "triple": - return wasm.BorderType.Triple; - default: - return wasm.BorderType.Single; - } -}; - -const convertWidthType = (t: string) => { - switch (t) { - case "nil": - case "Nil": - return wasm.WidthType.Nil; - case "Pct": - case "pct": - return wasm.WidthType.Pct; - case "DXA": - case "dxa": - return wasm.WidthType.Dxa; - case "Auto": - case "auto": - return wasm.WidthType.Auto; - default: - return wasm.WidthType.Dxa; - } -}; - export class Docx { children: ( | Paragraph @@ -315,63 +268,7 @@ export class Docx { } }); - if (r.property.style) { - run = run.style(r.property.style); - } - - if (typeof r.property.size !== "undefined") { - run = run.size(r.property.size); - } - - if (r.property.color) { - run = run.color(r.property.color); - } - - if (r.property.highlight) { - run = run.highlight(r.property.highlight); - } - - if (r.property.vertAlign) { - if (r.property.vertAlign === "superscript") { - run = run.vert_align(wasm.VertAlignType.SuperScript); - } else if (r.property.vertAlign === "subscript") { - run = run.vert_align(wasm.VertAlignType.SubScript); - } - } - - if (r.property.bold) { - run = run.bold(); - } - - if (r.property.italic) { - run = run.italic(); - } - - if (r.property.strike) { - run = run.strike(); - } - - if (r.property.underline) { - run = run.underline(r.property.underline); - } - - if (r.property.vanish) { - run = run.vanish(); - } - - if (r.property.spacing != null) { - run = run.spacing(r.property.spacing); - } - - if (r.property.textBorder) { - const { borderType, color, space, size } = r.property.textBorder; - run = run.text_border(convertBorderType(borderType), size, space, color); - } - - if (r.property.fonts) { - const fonts = r.property.fonts.buildWasmObject(); - run = run.fonts(fonts); - } + run = setRunProperty(run, r.property) as wasm.Run; return run; } @@ -521,81 +418,12 @@ export class Docx { } }); - switch (p.property.align) { - case "center": { - paragraph = paragraph.align(wasm.AlignmentType.Center); - break; - } - case "right": { - paragraph = paragraph.align(wasm.AlignmentType.Right); - break; - } - case "justified": { - paragraph = paragraph.align(wasm.AlignmentType.Justified); - break; - } - case "left": { - paragraph = paragraph.align(wasm.AlignmentType.Left); - break; - } - case "distribute": { - paragraph = paragraph.align(wasm.AlignmentType.Distribute); - break; - } - case "both": { - paragraph = paragraph.align(wasm.AlignmentType.Both); - break; - } - case "end": { - paragraph = paragraph.align(wasm.AlignmentType.End); - break; - } - } - - if (typeof p.property.indent !== "undefined") { - const { indent } = p.property; - let kind; - switch (p.property.indent.specialIndentKind) { - case "firstLine": { - kind = wasm.SpecialIndentKind.FirstLine; - break; - } - case "hanging": { - kind = wasm.SpecialIndentKind.Hanging; - break; - } - } - paragraph = paragraph.indent(indent.left, kind, indent.specialIndentSize); - } - - if (typeof p.property.numbering !== "undefined") { - const { numbering } = p.property; - paragraph = paragraph.numbering(numbering.id, numbering.level); - } + paragraph = setParagraphProperty(paragraph, p.property); if (typeof p.property.styleId !== "undefined") { paragraph = paragraph.style(p.property.styleId); } - if (p.property.runProperty.bold) { - paragraph = paragraph.bold(); - } - - if (typeof p.property.lineSpacing !== "undefined") { - const spacing = this.buildLineSpacing(p.property); - if (spacing) { - paragraph = paragraph.line_spacing(spacing); - } - } - - if (p.property.runProperty.italic) { - paragraph = paragraph.italic(); - } - - if (p.property.runProperty.size) { - paragraph = paragraph.size(p.property.runProperty.size); - } - if (p.property.runProperty.del) { paragraph = paragraph.delete( p.property.runProperty.del.author, @@ -610,39 +438,6 @@ export class Docx { ); } - if (p.property.runProperty.fonts) { - let f = wasm.createRunFonts(); - if (p.property.runProperty.fonts._ascii) { - f = f.ascii(p.property.runProperty.fonts._ascii); - } - if (p.property.runProperty.fonts._hiAnsi) { - f = f.hi_ansi(p.property.runProperty.fonts._hiAnsi); - } - if (p.property.runProperty.fonts._cs) { - f = f.cs(p.property.runProperty.fonts._cs); - } - if (p.property.runProperty.fonts._eastAsia) { - f = f.east_asia(p.property.runProperty.fonts._eastAsia); - } - paragraph = paragraph.fonts(f); - } - - if (p.property.keepLines) { - paragraph = paragraph.keep_lines(true); - } - - if (p.property.keepNext) { - paragraph = paragraph.keep_next(true); - } - - if (p.property.pageBreakBefore) { - paragraph = paragraph.page_break_before(true); - } - - if (p.property.widowControl) { - paragraph = paragraph.widow_control(true); - } - if (p.property.paragraphPropertyChange) { let change = wasm.createParagraphPropertyChange(); change = change @@ -701,43 +496,14 @@ export class Docx { } table = table.add_row(row); }); + table = table.set_grid(new Uint32Array(t.grid)); - table = table.indent(t.property.indent || 0); - if (t.property.cellMargins) { - const { top, right, bottom, left } = t.property.cellMargins; - table = table - .cell_margin_top(top.val, convertWidthType(top.type)) - .cell_margin_right(right.val, convertWidthType(right.type)) - .cell_margin_bottom(bottom.val, convertWidthType(bottom.type)) - .cell_margin_left(left.val, convertWidthType(left.type)); + if (t.property.styleId) { + table = table.style(t.property.styleId); } - switch (t.property.align) { - case "center": { - table = table.align(wasm.TableAlignmentType.Center); - break; - } - case "right": { - table = table.align(wasm.TableAlignmentType.Right); - break; - } - case "left": { - table = table.align(wasm.TableAlignmentType.Left); - break; - } - } - - switch (t.property.layout) { - case "fixed": { - table = table.layout(wasm.TableLayoutType.Fixed); - break; - } - case "autofit": { - table = table.layout(wasm.TableLayoutType.Autofit); - break; - } - } + table = setTableProperty(table, t.property); return table; } diff --git a/docx-wasm/js/json/styles.ts b/docx-wasm/js/json/styles.ts index f15b24f..ae8df3a 100644 --- a/docx-wasm/js/json/styles.ts +++ b/docx-wasm/js/json/styles.ts @@ -2,11 +2,12 @@ import { RunPropertyJSON } from "./run"; import { ParagraphPropertyJSON } from "./paragraph"; import { TablePropertyJSON } from "./table"; import { TableCellPropertyJSON } from ".."; +import { StyleType } from "../style"; export type StyleJSON = { styleId: string; name: string; - styleType: string; + styleType: StyleType; runProperty: RunPropertyJSON; paragraphProperty: ParagraphPropertyJSON; tableProperty: TablePropertyJSON; diff --git a/docx-wasm/js/paragraph-property.ts b/docx-wasm/js/paragraph-property.ts index 910f1f2..ae6a749 100644 --- a/docx-wasm/js/paragraph-property.ts +++ b/docx-wasm/js/paragraph-property.ts @@ -1,5 +1,7 @@ import { RunProperty, createDefaultRunProperty } from "./run"; +import * as wasm from "./pkg"; + export type AlignmentType = | "center" | "left" @@ -78,6 +80,37 @@ export const createDefaultParagraphProperty = (): ParagraphProperty => { }; }; +export const createParagraphAlignment = ( + align?: AlignmentType | undefined +): wasm.AlignmentType | null => { + switch (align) { + case "center": { + return wasm.AlignmentType.Center; + } + case "right": { + return wasm.AlignmentType.Right; + } + case "justified": { + return wasm.AlignmentType.Justified; + } + case "left": { + return wasm.AlignmentType.Left; + } + case "distribute": { + return wasm.AlignmentType.Distribute; + } + case "both": { + return wasm.AlignmentType.Both; + } + case "end": { + return wasm.AlignmentType.End; + } + default: { + return null; + } + } +}; + export class ParagraphPropertyChange { _author: string = ""; _date: string = ""; @@ -117,3 +150,135 @@ export class ParagraphPropertyChange { return this; } } + +export const buildLineSpacing = ( + p: ParagraphProperty +): wasm.LineSpacing | null => { + const { lineSpacing } = p; + if (lineSpacing == null) return null; + let kind; + switch (lineSpacing._lineRule) { + case "atLeast": { + kind = wasm.LineSpacingType.AtLeast; + break; + } + case "auto": { + kind = wasm.LineSpacingType.Auto; + break; + } + case "exact": { + kind = wasm.LineSpacingType.Exact; + break; + } + } + let spacing = wasm.createLineSpacing(); + if (lineSpacing._before != null) { + spacing = spacing.before(lineSpacing._before); + } + + if (lineSpacing._after != null) { + spacing = spacing.after(lineSpacing._after); + } + + if (lineSpacing._beforeLines != null) { + spacing = spacing.before_lines(lineSpacing._beforeLines); + } + + if (lineSpacing._afterLines != null) { + spacing = spacing.after_lines(lineSpacing._afterLines); + } + + if (lineSpacing._line != null) { + spacing = spacing.line(lineSpacing._line); + } + + if (kind != null) { + spacing = spacing.line_rule(kind); + } + return spacing; +}; + +export const setParagraphProperty = ( + target: T, + property: ParagraphProperty +): T => { + const alignment = createParagraphAlignment(property.align); + if (alignment != null) { + target = target.align(alignment) as T; + } + + if (typeof property.indent !== "undefined") { + const { indent } = property; + let kind; + switch (property.indent.specialIndentKind) { + case "firstLine": { + kind = wasm.SpecialIndentKind.FirstLine; + break; + } + case "hanging": { + kind = wasm.SpecialIndentKind.Hanging; + break; + } + } + target = target.indent(indent.left, kind, indent.specialIndentSize) as T; + } + + if (typeof property.numbering !== "undefined") { + const { numbering } = property; + target = target.numbering(numbering.id, numbering.level) as T; + } + + if (property.runProperty.bold) { + target = target.bold() as T; + } + + if (typeof property.lineSpacing !== "undefined") { + const spacing = buildLineSpacing(property); + if (spacing) { + target = target.line_spacing(spacing) as T; + } + } + + if (property.runProperty.italic) { + target = target.italic() as T; + } + + if (property.runProperty.size) { + target = target.size(property.runProperty.size) as T; + } + + if (property.runProperty.fonts) { + let f = wasm.createRunFonts(); + if (property.runProperty.fonts._ascii) { + f = f.ascii(property.runProperty.fonts._ascii); + } + if (property.runProperty.fonts._hiAnsi) { + f = f.hi_ansi(property.runProperty.fonts._hiAnsi); + } + if (property.runProperty.fonts._cs) { + f = f.cs(property.runProperty.fonts._cs); + } + if (property.runProperty.fonts._eastAsia) { + f = f.east_asia(property.runProperty.fonts._eastAsia); + } + target = target.fonts(f) as T; + } + + if (property.keepLines) { + target = target.keep_lines(true) as T; + } + + if (property.keepNext) { + target = target.keep_next(true) as T; + } + + if (property.pageBreakBefore) { + target = target.page_break_before(true) as T; + } + + if (property.widowControl) { + target = target.widow_control(true) as T; + } + + return target; +}; diff --git a/docx-wasm/js/run.ts b/docx-wasm/js/run.ts index ea20c1f..88df856 100644 --- a/docx-wasm/js/run.ts +++ b/docx-wasm/js/run.ts @@ -46,6 +46,33 @@ export type RunProperty = { del?: RunPropertyDel; }; +export const convertBorderType = (t: BorderType) => { + switch (t) { + case "nil": + return wasm.BorderType.Nil; + case "none": + return wasm.BorderType.None; + case "single": + return wasm.BorderType.Single; + case "thick": + return wasm.BorderType.Thick; + case "double": + return wasm.BorderType.Double; + case "dotted": + return wasm.BorderType.Dotted; + case "dashed": + return wasm.BorderType.Dashed; + case "dotDash": + return wasm.BorderType.DotDash; + case "dotDotDash": + return wasm.BorderType.DotDotDash; + case "triple": + return wasm.BorderType.Triple; + default: + return wasm.BorderType.Single; + } +}; + export const createDefaultRunProperty = (): RunProperty => { return {}; }; @@ -254,3 +281,73 @@ export class Run { return this; } } + +export const setRunProperty = ( + target: T, + property: RunProperty +): T => { + if (property.style && target instanceof wasm.Run) { + target = target.style(property.style) as T; + } + + if (typeof property.size !== "undefined") { + target = target.size(property.size) as T; + } + + if (property.color) { + target = target.color(property.color) as T; + } + + if (property.highlight) { + target = target.highlight(property.highlight) as T; + } + + if (property.vertAlign) { + if (property.vertAlign === "superscript") { + target = target.vert_align(wasm.VertAlignType.SuperScript) as T; + } else if (property.vertAlign === "subscript") { + target = target.vert_align(wasm.VertAlignType.SubScript) as T; + } + } + + if (property.bold) { + target = target.bold() as T; + } + + if (property.italic) { + target = target.italic() as T; + } + + if (property.strike) { + target = target.strike() as T; + } + + if (property.underline) { + target = target.underline(property.underline) as T; + } + + if (property.vanish) { + target = target.vanish() as T; + } + + if (property.spacing != null) { + target = target.spacing(property.spacing) as T; + } + + if (property.textBorder) { + const { borderType, color, space, size } = property.textBorder; + target = target.text_border( + convertBorderType(borderType), + size, + space, + color + ) as T; + } + + if (property.fonts) { + const fonts = property.fonts.buildWasmObject(); + target = target.fonts(fonts) as T; + } + + return target; +}; diff --git a/docx-wasm/js/style.ts b/docx-wasm/js/style.ts index d4f42ff..d9ff625 100644 --- a/docx-wasm/js/style.ts +++ b/docx-wasm/js/style.ts @@ -1,15 +1,31 @@ import * as wasm from "./pkg"; -import { createDefaultTableCellMargins, TableProperty } from "./table"; -import { RunProperty, createDefaultRunProperty } from "./run"; -import { createDefaultParagraphProperty, ParagraphProperty } from "./paragraph-property"; +import { + createDefaultTableCellMargins, + setTableProperty, + TableAlignmentType, + TableLayoutType, + TableProperty, +} from "./table"; +import { + RunProperty, + createDefaultRunProperty, + VertAlignType, + RunFonts, + setRunProperty, +} from "./run"; +import { + AlignmentType, + createDefaultParagraphProperty, + LineSpacing, + ParagraphProperty, + setParagraphProperty, + SpecialIndentKind, +} from "./paragraph-property"; +import { BorderType } from "./border"; +import { WidthType } from "."; -export type StyleType = - | "paragraph" - | "character" - | "numbering" - | "table" - | "unsupported"; +export type StyleType = "paragraph" | "character" | "numbering" | "table"; export class Style { _styleId: string; @@ -49,16 +65,203 @@ export class Style { // return this; // }; + // run property + style(style: string) { + this._runProperty = { ...this._runProperty, style }; + return this; + } + + size(size: number) { + this._runProperty = { ...this._runProperty, size }; + return this; + } + + color(color: string) { + this._runProperty = { ...this._runProperty, color }; + return this; + } + + highlight(color: string) { + this._runProperty = { ...this._runProperty, highlight: color }; + return this; + } + + vertAlign(vertAlign: VertAlignType) { + this._runProperty = { ...this._runProperty, vertAlign }; + return this; + } + + bold() { + this._runProperty = { ...this._runProperty, bold: true }; + return this; + } + + strike() { + this._runProperty = { ...this._runProperty, strike: true }; + return this; + } + + italic() { + this._runProperty = { ...this._runProperty, italic: true }; + return this; + } + + underline(type: string) { + this._runProperty = { ...this._runProperty, underline: type }; + return this; + } + + vanish() { + this._runProperty = { ...this._runProperty, vanish: true }; + return this; + } + + fonts(fonts: RunFonts) { + this._runProperty = { ...this._runProperty, fonts }; + return this; + } + + spacing(spacing: number) { + this._runProperty = { ...this._runProperty, spacing }; + return this; + } + + delete(author: string, date: string) { + this._runProperty = { ...this._runProperty, del: { author, date } }; + return this; + } + + insert(author: string, date: string) { + this._runProperty = { ...this._runProperty, ins: { author, date } }; + return this; + } + + textBorder(type: BorderType, size: number, space: number, color: string) { + this._runProperty = { + ...this._runProperty, + textBorder: { + borderType: type, + size, + space, + color, + }, + }; + return this; + } + // TODO: // paragraphProperty = (n: ParagraphProperty) => { // this._paragraphProperty = n; // return this; // }; - tableProperty = (n: TableProperty) => { - this._tableProperty = n; + // paragraph property + align(type: AlignmentType) { + this._paragraphProperty.align = type; return this; - }; + } + + indent( + left: number, + specialIndentKind?: SpecialIndentKind, + specialIndentSize?: number + ) { + this._paragraphProperty.indent = { + left, + specialIndentKind, + specialIndentSize, + }; + return this; + } + + numbering(id: number, level: number) { + this._paragraphProperty.numbering = { id, level }; + return this; + } + + lineSpacing(spacing: LineSpacing) { + this._paragraphProperty.lineSpacing = spacing; + return this; + } + + keepNext(v: boolean) { + this._paragraphProperty = { ...this._paragraphProperty, keepNext: v }; + return this; + } + + keepLines(v: boolean) { + this._paragraphProperty = { ...this._paragraphProperty, keepLines: v }; + return this; + } + + pageBreakBefore(v: boolean) { + this._paragraphProperty = { + ...this._paragraphProperty, + pageBreakBefore: v, + }; + return this; + } + + widowControl(v: boolean) { + this._paragraphProperty = { ...this._paragraphProperty, widowControl: v }; + return this; + } + + // tableProperty = (n: TableProperty) => { + // this._tableProperty = n; + // return this; + // }; + + // tableProperty + tableIndent(v: number) { + this._tableProperty.indent = v; + return this; + } + + tableAlign(v: TableAlignmentType) { + this._tableProperty.align = v; + return this; + } + + layout(l: TableLayoutType) { + this._tableProperty.layout = l; + return this; + } + + width(w: number) { + this._tableProperty.width = w; + return this; + } + + cellMargins(top: number, right: number, bottom: number, left: number) { + this._tableProperty.cellMargins = { + top: { val: top, type: "dxa" }, + left: { val: left, type: "dxa" }, + bottom: { val: bottom, type: "dxa" }, + right: { val: right, type: "dxa" }, + }; + return this; + } + + cellMarginTop(v: number, t: WidthType) { + this._tableProperty.cellMargins.top = { val: v, type: t }; + return this; + } + + cellMarginLeft(v: number, t: WidthType) { + this._tableProperty.cellMargins.left = { val: v, type: t }; + return this; + } + + cellMarginRight(v: number, t: WidthType) { + this._tableProperty.cellMargins.right = { val: v, type: t }; + return this; + } + + cellMarginBottom(v: number, t: WidthType) { + this._tableProperty.cellMargins.bottom = { val: v, type: t }; + return this; + } buildStyleType = () => { switch (this._styleType) { @@ -86,6 +289,12 @@ export class Style { s = s.based_on(this._basedOn); } + s = setRunProperty(s, this._runProperty); + + s = setParagraphProperty(s, this._paragraphProperty); + + s = setTableProperty(s, this._tableProperty); + return s; }; } diff --git a/docx-wasm/js/table.ts b/docx-wasm/js/table.ts index 17d8881..09808cb 100644 --- a/docx-wasm/js/table.ts +++ b/docx-wasm/js/table.ts @@ -1,3 +1,5 @@ +import * as wasm from "./pkg"; + import { WidthType } from "."; import { TableRow } from "./table-row"; @@ -8,6 +10,7 @@ export type TableProperty = { indent?: number; align?: TableAlignmentType; width?: number; + styleId?: string; cellMargins: { top: { val: number; type: WidthType }; left: { val: number; type: WidthType }; @@ -42,6 +45,11 @@ export class Table { return this; } + style(id: string) { + this.property.styleId = id; + return this; + } + setGrid(grid: number[]) { this.grid = grid; return this; @@ -97,3 +105,78 @@ export class Table { return this; } } + +export const convertWidthType = (t: string) => { + switch (t) { + case "nil": + case "Nil": + return wasm.WidthType.Nil; + case "Pct": + case "pct": + return wasm.WidthType.Pct; + case "DXA": + case "dxa": + return wasm.WidthType.Dxa; + case "Auto": + case "auto": + return wasm.WidthType.Auto; + default: + return wasm.WidthType.Dxa; + } +}; + +export const setTableProperty = ( + target: T, + property: TableProperty +): T => { + if (target instanceof wasm.Table) { + target = target.indent(property.indent ?? 0) as T; + } else if (target instanceof wasm.Style) { + target = target.table_indent(property.indent ?? 0) as T; + } + + if (property.cellMargins) { + const { top, right, bottom, left } = property.cellMargins; + target = target + .cell_margin_top(top.val, convertWidthType(top.type)) + .cell_margin_right(right.val, convertWidthType(right.type)) + .cell_margin_bottom(bottom.val, convertWidthType(bottom.type)) + .cell_margin_left(left.val, convertWidthType(left.type)) as T; + } + + const align = ((): wasm.TableAlignmentType | null => { + switch (property.align) { + case "center": { + return wasm.TableAlignmentType.Center; + } + case "right": { + return wasm.TableAlignmentType.Right; + } + case "left": { + return wasm.TableAlignmentType.Left; + } + default: + return null; + } + })(); + + if (align != null) { + if (target instanceof wasm.Table) { + target = target.align(align) as T; + } else if (target instanceof wasm.Style) { + target = target.table_align(align) as T; + } + } + + switch (property.layout) { + case "fixed": { + target = target.layout(wasm.TableLayoutType.Fixed) as T; + break; + } + case "autofit": { + target = target.layout(wasm.TableLayoutType.Autofit) as T; + break; + } + } + return target; +}; diff --git a/docx-wasm/package.json b/docx-wasm/package.json index 27e4335..0ba1d06 100644 --- a/docx-wasm/package.json +++ b/docx-wasm/package.json @@ -1,6 +1,6 @@ { "name": "docx-wasm", - "version": "0.0.276-rc14", + "version": "0.0.276-rc18", "main": "dist/node/index.js", "browser": "dist/web/index.js", "author": "bokuweb ", diff --git a/docx-wasm/src/style.rs b/docx-wasm/src/style.rs index a41360d..bf88f84 100644 --- a/docx-wasm/src/style.rs +++ b/docx-wasm/src/style.rs @@ -1,4 +1,5 @@ use super::*; +use docx_rs::{BorderType, TextBorder, VertAlignType, WidthType}; use wasm_bindgen::prelude::*; #[wasm_bindgen] @@ -47,6 +48,11 @@ impl Style { self } + pub fn strike(mut self) -> Self { + self.0.run_property = self.0.run_property.strike(); + self + } + pub fn underline(mut self, line_type: &str) -> Self { self.0.run_property = self.0.run_property.underline(line_type); self @@ -57,6 +63,37 @@ impl Style { self } + pub fn fonts(mut self, f: RunFonts) -> Self { + self.0 = self.0.fonts(f.take()); + self + } + + pub fn spacing(mut self, spacing: i32) -> Self { + self.0.run_property = self.0.run_property.spacing(spacing); + self + } + + pub fn vert_align(mut self, a: VertAlignType) -> Self { + self.0.run_property = self.0.run_property.vert_align(a); + self + } + + pub fn text_border( + mut self, + border_type: BorderType, + size: usize, + space: usize, + color: &str, + ) -> Self { + let border = TextBorder::new() + .border_type(border_type) + .size(size) + .space(space) + .color(color); + self.0.run_property = self.0.run_property.text_border(border); + self + } + pub fn align(mut self, alignment_type: docx_rs::AlignmentType) -> Self { self.0.paragraph_property = self.0.paragraph_property.align(alignment_type); self @@ -81,6 +118,39 @@ impl Style { self } + // TODO: For now only numbering supported. + pub fn numbering(mut self, id: usize, level: usize) -> Self { + let id = docx_rs::NumberingId::new(id); + let level = docx_rs::IndentLevel::new(level); + self.0.paragraph_property = self.0.paragraph_property.numbering(id, level); + self + } + + pub fn line_spacing(mut self, spacing: LineSpacing) -> Self { + self.0.paragraph_property = self.0.paragraph_property.line_spacing(spacing.take()); + self + } + + pub fn keep_next(mut self, v: bool) -> Self { + self.0.paragraph_property = self.0.paragraph_property.keep_next(v); + self + } + + pub fn keep_lines(mut self, v: bool) -> Self { + self.0.paragraph_property = self.0.paragraph_property.keep_lines(v); + self + } + + pub fn page_break_before(mut self, v: bool) -> Self { + self.0.paragraph_property = self.0.paragraph_property.page_break_before(v); + self + } + + pub fn widow_control(mut self, v: bool) -> Self { + self.0.paragraph_property = self.0.paragraph_property.widow_control(v); + self + } + // pub fn run_property(mut self, p: docx_rs::RunProperty) -> Self { // self.0.run_property = p; // self @@ -100,6 +170,53 @@ impl Style { self.0.table_cell_property = p; self } + + pub fn table_indent(mut self, v: i32) -> Self { + self.0.table_property = self.0.table_property.indent(v); + self + } + + pub fn table_align(mut self, v: docx_rs::TableAlignmentType) -> Self { + self.0.table_property = self.0.table_property.align(v); + self + } + + pub fn set_cell_margins( + mut self, + top: usize, + right: usize, + bottom: usize, + left: usize, + ) -> Self { + let m = docx_rs::TableCellMargins::new().margin(top, right, bottom, left); + self.0.table_property = self.0.table_property.set_margins(m); + self + } + + pub fn cell_margin_top(mut self, v: usize, t: WidthType) -> Self { + self.0.table_property = self.0.table_property.cell_margin_top(v, t); + self + } + + pub fn cell_margin_right(mut self, v: usize, t: WidthType) -> Self { + self.0.table_property = self.0.table_property.cell_margin_right(v, t); + self + } + + pub fn cell_margin_bottom(mut self, v: usize, t: WidthType) -> Self { + self.0.table_property = self.0.table_property.cell_margin_bottom(v, t); + self + } + + pub fn cell_margin_left(mut self, v: usize, t: WidthType) -> Self { + self.0.table_property = self.0.table_property.cell_margin_left(v, t); + self + } + + pub fn layout(mut self, t: docx_rs::TableLayoutType) -> Self { + self.0.table_property = self.0.table_property.layout(t); + self + } } impl Style { diff --git a/docx-wasm/src/table.rs b/docx-wasm/src/table.rs index 9611a80..8e4ec01 100644 --- a/docx-wasm/src/table.rs +++ b/docx-wasm/src/table.rs @@ -29,6 +29,11 @@ impl Table { self } + pub fn style(mut self, style_id: &str) -> Table { + self.0.property = self.0.property.style(style_id); + self + } + pub fn indent(mut self, v: i32) -> Table { self.0 = self.0.indent(v); self diff --git a/docx-wasm/test/__snapshots__/index.test.js.snap b/docx-wasm/test/__snapshots__/index.test.js.snap index ed6efef..f10fe57 100644 --- a/docx-wasm/test/__snapshots__/index.test.js.snap +++ b/docx-wasm/test/__snapshots__/index.test.js.snap @@ -67137,6 +67137,23 @@ exports[`writer should write ToC with items 2`] = ` " `; +exports[`writer should write align 1`] = ` +" + + + + + +" +`; + +exports[`writer should write align 2`] = ` +" + + Hello world!! +" +`; + exports[`writer should write anchor hyperlink 1`] = ` " @@ -67995,6 +68012,37 @@ exports[`writer should write strike 3`] = ` " `; +exports[`writer should write style 1`] = ` +" + + + + + +" +`; + +exports[`writer should write style 2`] = ` +" + + Hello + + + + +Hello +" +`; + +exports[`writer should write style 3`] = ` +" + + + + +" +`; + exports[`writer should write table layout 1`] = ` " diff --git a/docx-wasm/test/index.test.js b/docx-wasm/test/index.test.js index 76b85cc..53b8e16 100644 --- a/docx-wasm/test/index.test.js +++ b/docx-wasm/test/index.test.js @@ -179,6 +179,21 @@ describe("writer", () => { } }); + test("should write align", () => { + const p = new w.Paragraph() + .addRun(new w.Run().addText("Hello world!!")) + .align("both"); + const buffer = new w.Docx().addParagraph(p).build(); + const z = new Zip(Buffer.from(buffer)); + for (const e of z.getEntries()) { + if (e.entryName.match(/document.xml/)) { + expect(z.readAsText(e)).toMatchSnapshot(); + } + } + writeFileSync("../output/js/align.docx", buffer); + + }); + test("should write strike", () => { const p = new w.Paragraph().addRun( new w.Run().addText("Hello world!!").strike() @@ -821,4 +836,44 @@ describe("writer", () => { } } }); + + test("should write style", () => { + const p = new w.Paragraph() + .addRun(new w.Run().addText("Hello").style("Run")) + .style("Heading1"); + const rStyle = new w.Style("Run", "character").name("Run test").bold(); + const pStyle = new w.Style("Heading1", "paragraph") + .name("Heading 1") + .align("center"); + const tStyle = new w.Style("Table", "table") + .name("Table 1") + .tableAlign("center") + .tableIndent(200); + + const table = new w.Table() + .addRow( + new w.TableRow().addCell( + new w.TableCell().addParagraph( + new w.Paragraph().addRun(new w.Run().addText("Hello")) + ) + ) + ) + .style("Table"); + + const buffer = new w.Docx() + .addStyle(pStyle) + .addStyle(rStyle) + .addStyle(tStyle) + .addParagraph(p) + .addTable(table) + .build(); + + const z = new Zip(Buffer.from(buffer)); + for (const e of z.getEntries()) { + if (e.entryName.match(/document.xml|styles.xml/)) { + expect(z.readAsText(e)).toMatchSnapshot(); + } + } + writeFileSync("../output/js/style.docx", buffer); + }); });