diff --git a/demo/demo34.ts b/demo/demo34.ts new file mode 100644 index 0000000000..098e1c2065 --- /dev/null +++ b/demo/demo34.ts @@ -0,0 +1,32 @@ +// Example of how you would create a table with float positions +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { + Document, + Packer, + Paragraph, + RelativeHorizontalPosition, + RelativeVerticalPosition, + TableAnchorType, + WidthType, +} from "../build"; + +const doc = new Document(); + +const table = doc.createTable(2, 2).float({ + horizontalAnchor: TableAnchorType.MARGIN, + verticalAnchor: TableAnchorType.MARGIN, + relativeHorizontalPosition: RelativeHorizontalPosition.RIGHT, + relativeVerticalPosition: RelativeVerticalPosition.BOTTOM, +}); +table.setFixedWidthLayout(); +table.setWidth(WidthType.DXA, 4535); + +table.getCell(0, 0).addContent(new Paragraph("Hello")); +table.getRow(0).mergeCells(0, 1); + +const packer = new Packer(); + +packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/src/file/table/index.ts b/src/file/table/index.ts index dfba175857..5048912098 100644 --- a/src/file/table/index.ts +++ b/src/file/table/index.ts @@ -1,2 +1,3 @@ export * from "./table"; export * from "./table-cell"; +export * from "./table-float-properties"; diff --git a/src/file/table/properties.ts b/src/file/table/properties.ts index 91ef393df3..248895ca42 100644 --- a/src/file/table/properties.ts +++ b/src/file/table/properties.ts @@ -1,6 +1,7 @@ import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; import { WidthType } from "./table-cell"; import { TableCellMargin } from "./table-cell-margin"; +import { TableFloatProperties } from "./table-float-properties"; export class TableProperties extends XmlComponent { private readonly cellMargin: TableCellMargin; @@ -30,6 +31,11 @@ export class TableProperties extends XmlComponent { public get CellMargin(): TableCellMargin { return this.cellMargin; } + + public setTableFloatProperties(tableFloatProperties: TableFloatProperties): TableProperties { + this.root.push(tableFloatProperties); + return this; + } } interface ITableWidth { diff --git a/src/file/table/table-float-properties.spec.ts b/src/file/table/table-float-properties.spec.ts new file mode 100644 index 0000000000..57daea1f1a --- /dev/null +++ b/src/file/table/table-float-properties.spec.ts @@ -0,0 +1,44 @@ +import { expect } from "chai"; + +import { Formatter } from "../../export/formatter"; +import { RelativeHorizontalPosition, RelativeVerticalPosition, TableAnchorType, TableFloatProperties } from "./table-float-properties"; + +describe("Table Float Properties", () => { + describe("#constructor", () => { + it("should construct a TableFloatProperties with all options", () => { + const tfp = new TableFloatProperties({ + horizontalAnchor: TableAnchorType.MARGIN, + verticalAnchor: TableAnchorType.PAGE, + absoluteHorizontalPosition: 10, + relativeHorizontalPosition: RelativeHorizontalPosition.CENTER, + absoluteVerticalPosition: 20, + relativeVerticalPosition: RelativeVerticalPosition.BOTTOM, + bottomFromText: 30, + topFromText: 40, + leftFromText: 50, + rightFromText: 60, + }); + const tree = new Formatter().format(tfp); + expect(tree).to.be.deep.equal(DEFAULT_TFP); + }); + }); +}); + +const DEFAULT_TFP = { + "w:tblpPr": [ + { + _attr: { + "w:horzAnchor": "margin", + "w:vertAnchor": "page", + "w:tblpX": 10, + "w:tblpXSpec": "center", + "w:tblpY": 20, + "w:tblpYSpec": "bottom", + "w:bottomFromText": 30, + "w:topFromText": 40, + "w:leftFromText": 50, + "w:rightFromText": 60, + }, + }, + ], +}; diff --git a/src/file/table/table-float-properties.ts b/src/file/table/table-float-properties.ts new file mode 100644 index 0000000000..ae68df2b28 --- /dev/null +++ b/src/file/table/table-float-properties.ts @@ -0,0 +1,134 @@ +import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; + +export enum TableAnchorType { + MARGIN = "margin", + PAGE = "page", + TEXT = "text", +} + +export enum RelativeHorizontalPosition { + CENTER = "center", + INSIDE = "inside", + LEFT = "left", + OUTSIDE = "outside", + RIGHT = "right", +} + +export enum RelativeVerticalPosition { + CENTER = "center", + INSIDE = "inside", + BOTTOM = "bottom", + OUTSIDE = "outside", + INLINE = "inline", + TOP = "top", +} + +export interface ITableFloatOptions { + /** + * Specifies the horizontal anchor or the base object from which the horizontal positioning in the + * tblpX or tblpXSpec attribute should be determined. + * margin - relative to the vertical edge of the text margin before any text runs (left edge for left-to-right paragraphs) + * page - relative to the vertical edge of the page before any text runs (left edge for left-to-right paragraphs) + * text - relative to the vertical edge of the text margin for the column in which the anchor paragraph is located + * If omitted, the value is assumed to be page. + */ + horizontalAnchor?: TableAnchorType; + + /** + * Specifies an absolute horizontal position for the table, relative to the horizontalAnchor. + * The value is in twentieths of a point. Note that the value can be negative, in which case the + * table is positioned before the anchor object in the direction of horizontal text flow. + * If relativeHorizontalPosition is also specified, then the absoluteHorizontalPosition attribute is ignored. + * If the attribute is omitted, the value is assumed to be zero. + */ + absoluteHorizontalPosition?: number; + + /** + * Specifies a relative horizontal position for the table, relative to the horizontalAnchor attribute. + * This will supersede the absoluteHorizontalPosition attribute. + * Possible values are: + * center - the table should be horizontally centered with respect to the anchor + * inside - the table should be inside of the anchor + * left - the table should be left aligned with respect to the anchor + * outside - the table should be outside of the anchor + * right - the table should be right aligned with respect to the anchor + */ + relativeHorizontalPosition?: RelativeHorizontalPosition; + + /** + * Specifies the vertical anchor or the base object from which the vertical positioning + * in the absoluteVerticalPosition attribute should be determined. Possible values are: + * margin - relative to the horizontal edge of the text margin before any text runs (top edge for top-to-bottom paragraphs) + * page - relative to the horizontal edge of the page before any text runs (top edge for top-to-bottom paragraphs) + * text - relative to the horizontal edge of the text margin for the column in which the anchor paragraph is located + * If omitted, the value is assumed to be page. + */ + verticalAnchor?: TableAnchorType; + + /** + * Specifies an absolute vertical position for the table, relative to the verticalAnchor anchor. + * The value is in twentieths of a point. Note that the value can be negative, in which case the table is + * positioned before the anchor object in the direction of vertical text flow. + * If relativeVerticalPosition is also specified, then the absoluteVerticalPosition attribute is ignored. + * If the attribute is omitted, the value is assumed to be zero. + */ + absoluteVerticalPosition?: number; + + /** + * Specifies a relative vertical position for the table, relative to the verticalAnchor attribute. + * This will supersede the absoluteVerticalPosition attribute. Possible values are: + * center - the table should be vertically centered with respect to the anchor + * inside - the table should be vertically aligned to the edge of the anchor and inside the anchor + * bottom - the table should be vertically aligned to the bottom edge of the anchor + * outside - the table should be vertically aligned to the edge of the anchor and outside the anchor + * inline - the table should be vertically aligned in line with the surrounding text (so as to not allow any text wrapping around it) + * top - the table should be vertically aligned to the top edge of the anchor + */ + relativeVerticalPosition?: RelativeVerticalPosition; + + /** + * Specifies the minimun distance to be maintained between the table and the top of text in the paragraph + * below the table. The value is in twentieths of a point. If omitted, the value is assumed to be zero. + */ + bottomFromText?: number; + + /** + * Specifies the minimun distance to be maintained between the table and the bottom edge of text in the paragraph + * above the table. The value is in twentieths of a point. If omitted, the value is assumed to be zero. + */ + topFromText?: number; + + /** + * Specifies the minimun distance to be maintained between the table and the edge of text in the paragraph + * to the left of the table. The value is in twentieths of a point. If omitted, the value is assumed to be zero. + */ + leftFromText?: number; + + /** + * Specifies the minimun distance to be maintained between the table and the edge of text in the paragraph + * to the right of the table. The value is in twentieths of a point. If omitted, the value is assumed to be zero. + */ + rightFromText?: number; +} + +export class TableFloatOptionsAttributes extends XmlAttributeComponent { + protected xmlKeys = { + horizontalAnchor: "w:horzAnchor", + verticalAnchor: "w:vertAnchor", + absoluteHorizontalPosition: "w:tblpX", + relativeHorizontalPosition: "w:tblpXSpec", + absoluteVerticalPosition: "w:tblpY", + relativeVerticalPosition: "w:tblpYSpec", + bottomFromText: "w:bottomFromText", + topFromText: "w:topFromText", + leftFromText: "w:leftFromText", + rightFromText: "w:rightFromText", + }; +} + +export class TableFloatProperties extends XmlComponent { + constructor(options: ITableFloatOptions) { + super("w:tblpPr"); + this.root.push(new TableFloatOptionsAttributes(options)); + } +} diff --git a/src/file/table/table.spec.ts b/src/file/table/table.spec.ts index 65871655b4..c67e11b9e1 100644 --- a/src/file/table/table.spec.ts +++ b/src/file/table/table.spec.ts @@ -5,6 +5,7 @@ import { Formatter } from "../../export/formatter"; import { Paragraph } from "../paragraph"; import { Table } from "./"; import { WidthType } from "./table-cell"; +import { RelativeHorizontalPosition, RelativeVerticalPosition, TableAnchorType } from "./table-float-properties"; const DEFAULT_TABLE_PROPERTIES = { "w:tblBorders": [ @@ -294,4 +295,49 @@ describe("Table", () => { }); }); }); + + describe("#float", () => { + it("sets the table float properties", () => { + const table = new Table(1, 1).float({ + horizontalAnchor: TableAnchorType.MARGIN, + verticalAnchor: TableAnchorType.PAGE, + absoluteHorizontalPosition: 10, + relativeHorizontalPosition: RelativeHorizontalPosition.CENTER, + absoluteVerticalPosition: 20, + relativeVerticalPosition: RelativeVerticalPosition.BOTTOM, + bottomFromText: 30, + topFromText: 40, + leftFromText: 50, + rightFromText: 60, + }); + const tree = new Formatter().format(table); + expect(tree) + .to.have.property("w:tbl") + .which.is.an("array") + .with.has.length.at.least(1); + expect(tree["w:tbl"][0]).to.deep.equal({ + "w:tblPr": [ + DEFAULT_TABLE_PROPERTIES, + { + "w:tblpPr": [ + { + _attr: { + "w:horzAnchor": "margin", + "w:vertAnchor": "page", + "w:tblpX": 10, + "w:tblpXSpec": "center", + "w:tblpY": 20, + "w:tblpYSpec": "bottom", + "w:bottomFromText": 30, + "w:topFromText": 40, + "w:leftFromText": 50, + "w:rightFromText": 60, + }, + }, + ], + }, + ], + }); + }); + }); }); diff --git a/src/file/table/table.ts b/src/file/table/table.ts index 3220bce537..b9d18567d0 100644 --- a/src/file/table/table.ts +++ b/src/file/table/table.ts @@ -14,6 +14,7 @@ import { IXmlableObject, XmlComponent } from "file/xml-components"; import { Paragraph } from "../paragraph"; import { TableGrid } from "./grid"; import { TableProperties } from "./properties"; +import { ITableFloatOptions, TableFloatProperties } from "./table-float-properties"; export class Table extends XmlComponent { private readonly properties: TableProperties; @@ -84,6 +85,11 @@ export class Table extends XmlComponent { return this; } + public float(tableFloatProperties: ITableFloatOptions): Table { + this.properties.setTableFloatProperties(new TableFloatProperties(tableFloatProperties)); + return this; + } + public get Properties(): TableProperties { return this.properties; }