diff --git a/demo/97-table-look.ts b/demo/97-table-look.ts
new file mode 100644
index 00000000000..93e1da07361
--- /dev/null
+++ b/demo/97-table-look.ts
@@ -0,0 +1,112 @@
+// Example of using tableLook to control conditional table formatting
+import { Document, Packer, Paragraph, Table, TableCell, TableRow, TextRun, WidthType } from "docx";
+import * as fs from "fs";
+
+const styles = fs.readFileSync("./demo/assets/custom-styles.xml", "utf-8");
+
+const doc = new Document({
+ externalStyles: styles,
+ sections: [
+ {
+ children: [
+ new Paragraph({
+ children: [new TextRun({ text: "Table 1: Table Look Default Values", bold: true })],
+ }),
+ new Paragraph({ text: "" }),
+ new Table({
+ rows: [
+ new TableRow({
+ children: [
+ new TableCell({ children: [new Paragraph("Header 1")] }),
+ new TableCell({ children: [new Paragraph("Header 2")] }),
+ new TableCell({ children: [new Paragraph("Header 3")] }),
+ ],
+ }),
+ new TableRow({
+ children: [
+ new TableCell({ children: [new Paragraph("Row 1, Col 1")] }),
+ new TableCell({ children: [new Paragraph("Row 1, Col 2")] }),
+ new TableCell({ children: [new Paragraph("Row 1, Col 3")] }),
+ ],
+ }),
+ new TableRow({
+ children: [
+ new TableCell({ children: [new Paragraph("Row 2, Col 1")] }),
+ new TableCell({ children: [new Paragraph("Row 2, Col 2")] }),
+ new TableCell({ children: [new Paragraph("Row 2, Col 3")] }),
+ ],
+ }),
+ new TableRow({
+ children: [
+ new TableCell({ children: [new Paragraph("Row 3, Col 1")] }),
+ new TableCell({ children: [new Paragraph("Row 3, Col 2")] }),
+ new TableCell({ children: [new Paragraph("Row 3, Col 3")] }),
+ ],
+ }),
+ ],
+ width: {
+ size: 100,
+ type: WidthType.PERCENTAGE,
+ },
+ style: "MyCustomTableStyle",
+ }),
+ new Paragraph({ text: "" }),
+ new Paragraph({ text: "" }),
+ new Paragraph({
+ children: [new TextRun({ text: "Table 2: Table Look All Look Values Enabled", bold: true })],
+ }),
+ new Paragraph({ text: "" }),
+ new Table({
+ tableLook: {
+ firstRow: true,
+ lastRow: true,
+ firstColumn: true,
+ lastColumn: true,
+ noHBand: false,
+ noVBand: false,
+ },
+ rows: [
+ new TableRow({
+ children: [
+ new TableCell({ children: [new Paragraph("Header 1")] }),
+ new TableCell({ children: [new Paragraph("Header 2")] }),
+ new TableCell({ children: [new Paragraph("Header 3")] }),
+ ],
+ }),
+ new TableRow({
+ children: [
+ new TableCell({ children: [new Paragraph("Row 1, Col 1")] }),
+ new TableCell({ children: [new Paragraph("Row 1, Col 2")] }),
+ new TableCell({ children: [new Paragraph("Row 1, Col 3")] }),
+ ],
+ }),
+ new TableRow({
+ children: [
+ new TableCell({ children: [new Paragraph("Row 2, Col 1")] }),
+ new TableCell({ children: [new Paragraph("Row 2, Col 2")] }),
+ new TableCell({ children: [new Paragraph("Row 2, Col 3")] }),
+ ],
+ }),
+ new TableRow({
+ children: [
+ new TableCell({ children: [new Paragraph("Row 3, Col 1")] }),
+ new TableCell({ children: [new Paragraph("Row 3, Col 2")] }),
+ new TableCell({ children: [new Paragraph("Row 3, Col 3")] }),
+ ],
+ }),
+ ],
+ width: {
+ size: 100,
+ type: WidthType.PERCENTAGE,
+ },
+ style: "MyCustomTableStyle",
+ }),
+ ],
+ },
+ ],
+});
+
+Packer.toBuffer(doc).then((buffer) => {
+ fs.writeFileSync("97-table-look.docx", buffer);
+ console.log("Document created successfully at 97-table-look.docx");
+});
diff --git a/docs/usage/tables.md b/docs/usage/tables.md
index 6455dd25497..8cfb69ac40f 100644
--- a/docs/usage/tables.md
+++ b/docs/usage/tables.md
@@ -344,6 +344,37 @@ const table = new Table({
});
```
+### Table Look (Conditional Formatting)
+
+Control which conditional formatting from a table style is applied. Table styles can define special formatting for the first row, first column, etc. Use `tableLook` to toggle these formatting options.
+
+```ts
+const table = new Table({
+ style: "GridTable5Dark-Accent3",
+ tableLook: {
+ firstRow: true, // Apply first row formatting
+ lastRow: false, // Don't apply last row formatting
+ firstColumn: true, // Apply first column formatting
+ lastColumn: false, // Don't apply last column formatting
+ noHBand: false, // Apply horizontal banding (row stripes)
+ noVBand: true, // Don't apply vertical banding (column stripes)
+ },
+});
+```
+
+**Note**: When `tableLook` is not specified at all, Word applies row and column banding by default, but does not apply first/last row/column formatting.
+
+#### Options
+
+| Property | Type | Description |
+| ------------ | --------- | -------------------------------------------- |
+| firstRow | `boolean` | Apply special formatting to first row |
+| lastRow | `boolean` | Apply special formatting to last row |
+| firstColumn | `boolean` | Apply special formatting to first column |
+| lastColumn | `boolean` | Apply special formatting to last column |
+| noHBand | `boolean` | Disable horizontal banding (row stripes) |
+| noVBand | `boolean` | Disable vertical banding (column stripes) |
+
## Examples
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/4-basic-table.ts ':include')
diff --git a/src/file/table/table-properties/index.ts b/src/file/table/table-properties/index.ts
index aefcc9e0aa3..d9e48269d5c 100644
--- a/src/file/table/table-properties/index.ts
+++ b/src/file/table/table-properties/index.ts
@@ -2,3 +2,4 @@ export * from "./table-properties";
export * from "./table-float-properties";
export * from "./table-layout";
export * from "./table-borders";
+export * from "./table-look";
diff --git a/src/file/table/table-properties/table-look.spec.ts b/src/file/table/table-properties/table-look.spec.ts
new file mode 100644
index 00000000000..15f312a5ea8
--- /dev/null
+++ b/src/file/table/table-properties/table-look.spec.ts
@@ -0,0 +1,159 @@
+import { describe, expect, it } from "vitest";
+
+import { Formatter } from "@export/formatter";
+
+import { TableLook } from "./table-look";
+
+describe("TableLook", () => {
+ describe("#constructor", () => {
+ it("should create table look with firstRow enabled", () => {
+ const tableLook = new TableLook({
+ firstRow: true,
+ });
+ const tree = new Formatter().format(tableLook);
+ expect(tree).to.deep.equal({
+ "w:tblLook": {
+ _attr: {
+ "w:firstRow": true,
+ },
+ },
+ });
+ });
+
+ it("should create table look with lastRow enabled", () => {
+ const tableLook = new TableLook({
+ lastRow: true,
+ });
+ const tree = new Formatter().format(tableLook);
+ expect(tree).to.deep.equal({
+ "w:tblLook": {
+ _attr: {
+ "w:lastRow": true,
+ },
+ },
+ });
+ });
+
+ it("should create table look with firstColumn enabled", () => {
+ const tableLook = new TableLook({
+ firstColumn: true,
+ });
+ const tree = new Formatter().format(tableLook);
+ expect(tree).to.deep.equal({
+ "w:tblLook": {
+ _attr: {
+ "w:firstColumn": true,
+ },
+ },
+ });
+ });
+
+ it("should create table look with lastColumn enabled", () => {
+ const tableLook = new TableLook({
+ lastColumn: true,
+ });
+ const tree = new Formatter().format(tableLook);
+ expect(tree).to.deep.equal({
+ "w:tblLook": {
+ _attr: {
+ "w:lastColumn": true,
+ },
+ },
+ });
+ });
+
+ it("should create table look with noHBand enabled", () => {
+ const tableLook = new TableLook({
+ noHBand: true,
+ });
+ const tree = new Formatter().format(tableLook);
+ expect(tree).to.deep.equal({
+ "w:tblLook": {
+ _attr: {
+ "w:noHBand": true,
+ },
+ },
+ });
+ });
+
+ it("should create table look with noVBand enabled", () => {
+ const tableLook = new TableLook({
+ noVBand: true,
+ });
+ const tree = new Formatter().format(tableLook);
+ expect(tree).to.deep.equal({
+ "w:tblLook": {
+ _attr: {
+ "w:noVBand": true,
+ },
+ },
+ });
+ });
+
+ it("should create table look with firstRow set to false", () => {
+ const tableLook = new TableLook({
+ firstRow: false,
+ });
+ const tree = new Formatter().format(tableLook);
+ expect(tree).to.deep.equal({
+ "w:tblLook": {
+ _attr: {
+ "w:firstRow": false,
+ },
+ },
+ });
+ });
+
+ it("should create table look with multiple attributes", () => {
+ const tableLook = new TableLook({
+ firstRow: true,
+ firstColumn: true,
+ noVBand: true,
+ });
+ const tree = new Formatter().format(tableLook);
+ expect(tree).to.deep.equal({
+ "w:tblLook": {
+ _attr: {
+ "w:firstRow": true,
+ "w:firstColumn": true,
+ "w:noVBand": true,
+ },
+ },
+ });
+ });
+
+ it("should create table look with all attributes", () => {
+ const tableLook = new TableLook({
+ firstRow: true,
+ lastRow: false,
+ firstColumn: true,
+ lastColumn: false,
+ noHBand: false,
+ noVBand: true,
+ });
+ const tree = new Formatter().format(tableLook);
+ expect(tree).to.deep.equal({
+ "w:tblLook": {
+ _attr: {
+ "w:firstRow": true,
+ "w:lastRow": false,
+ "w:firstColumn": true,
+ "w:lastColumn": false,
+ "w:noHBand": false,
+ "w:noVBand": true,
+ },
+ },
+ });
+ });
+
+ it("should create table look with empty options", () => {
+ const tableLook = new TableLook({});
+ const tree = new Formatter().format(tableLook);
+ expect(tree).to.deep.equal({
+ "w:tblLook": {
+ _attr: {},
+ },
+ });
+ });
+ });
+});
diff --git a/src/file/table/table-properties/table-look.ts b/src/file/table/table-properties/table-look.ts
new file mode 100644
index 00000000000..2dd0bb94159
--- /dev/null
+++ b/src/file/table/table-properties/table-look.ts
@@ -0,0 +1,37 @@
+// http://officeopenxml.com/WPtblLook.php
+//
+//
+//
+//
+//
+//
+//
+//
+import { XmlAttributeComponent, XmlComponent } from "@file/xml-components";
+
+export type ITableLookOptions = {
+ readonly firstRow?: boolean;
+ readonly lastRow?: boolean;
+ readonly firstColumn?: boolean;
+ readonly lastColumn?: boolean;
+ readonly noHBand?: boolean;
+ readonly noVBand?: boolean;
+};
+
+class TableLookAttributes extends XmlAttributeComponent {
+ protected readonly xmlKeys = {
+ firstRow: "w:firstRow",
+ lastRow: "w:lastRow",
+ firstColumn: "w:firstColumn",
+ lastColumn: "w:lastColumn",
+ noHBand: "w:noHBand",
+ noVBand: "w:noVBand",
+ };
+}
+
+export class TableLook extends XmlComponent {
+ public constructor(options: ITableLookOptions) {
+ super("w:tblLook");
+ this.root.push(new TableLookAttributes(options));
+ }
+}
diff --git a/src/file/table/table-properties/table-properties.spec.ts b/src/file/table/table-properties/table-properties.spec.ts
index 02da55821e1..c841e34c211 100644
--- a/src/file/table/table-properties/table-properties.spec.ts
+++ b/src/file/table/table-properties/table-properties.spec.ts
@@ -199,4 +199,60 @@ describe("TableProperties", () => {
});
});
});
+
+ describe("#tableLook", () => {
+ it("adds table look with first row and first column enabled", () => {
+ const tp = new TableProperties({
+ tableLook: {
+ firstRow: true,
+ firstColumn: true,
+ noVBand: true,
+ },
+ });
+ const tree = new Formatter().format(tp);
+ expect(tree).to.deep.equal({
+ "w:tblPr": [
+ {
+ "w:tblLook": {
+ _attr: {
+ "w:firstRow": true,
+ "w:firstColumn": true,
+ "w:noVBand": true,
+ },
+ },
+ },
+ ],
+ });
+ });
+
+ it("adds table look with all attributes", () => {
+ const tp = new TableProperties({
+ tableLook: {
+ firstRow: true,
+ lastRow: false,
+ firstColumn: true,
+ lastColumn: false,
+ noHBand: false,
+ noVBand: true,
+ },
+ });
+ const tree = new Formatter().format(tp);
+ expect(tree).to.deep.equal({
+ "w:tblPr": [
+ {
+ "w:tblLook": {
+ _attr: {
+ "w:firstRow": true,
+ "w:lastRow": false,
+ "w:firstColumn": true,
+ "w:lastColumn": false,
+ "w:noHBand": false,
+ "w:noVBand": true,
+ },
+ },
+ },
+ ],
+ });
+ });
+ });
});
diff --git a/src/file/table/table-properties/table-properties.ts b/src/file/table/table-properties/table-properties.ts
index b0946a1d60b..2329a5580be 100644
--- a/src/file/table/table-properties/table-properties.ts
+++ b/src/file/table/table-properties/table-properties.ts
@@ -31,6 +31,7 @@ import { ITableCellMarginOptions, TableCellMargin, TableCellMarginElementType }
import { ITableFloatOptions, TableFloatProperties } from "./table-float-properties";
import { TableLayout, TableLayoutType } from "./table-layout";
import { ITableCellSpacingProperties, TableCellSpacingElement } from "../table-cell-spacing";
+import { ITableLookOptions, TableLook } from "./table-look";
export type ITablePropertiesOptions = {
readonly width?: ITableWidthProperties;
@@ -43,6 +44,7 @@ export type ITablePropertiesOptions = {
readonly alignment?: (typeof AlignmentType)[keyof typeof AlignmentType];
readonly cellMargin?: ITableCellMarginOptions;
readonly visuallyRightToLeft?: boolean;
+ readonly tableLook?: ITableLookOptions;
readonly cellSpacing?: ITableCellSpacingProperties;
};
@@ -90,6 +92,10 @@ export class TableProperties extends IgnoreIfEmptyXmlComponent {
this.root.push(new TableCellMargin(TableCellMarginElementType.TABLE, options.cellMargin));
}
+ if (options.tableLook) {
+ this.root.push(new TableLook(options.tableLook));
+ }
+
if (options.cellSpacing) {
this.root.push(new TableCellSpacingElement(options.cellSpacing));
}
diff --git a/src/file/table/table.ts b/src/file/table/table.ts
index 37d4bcfbcac..3860a74d02c 100644
--- a/src/file/table/table.ts
+++ b/src/file/table/table.ts
@@ -8,6 +8,7 @@ import { ITableCellSpacingProperties } from "./table-cell-spacing";
import { ITableBordersOptions, ITableFloatOptions, TableProperties } from "./table-properties";
import { ITableCellMarginOptions } from "./table-properties/table-cell-margin";
import { TableLayoutType } from "./table-properties/table-layout";
+import { ITableLookOptions } from "./table-properties/table-look";
import { TableRow } from "./table-row";
import { ITableWidthProperties } from "./table-width";
@@ -33,6 +34,7 @@ export type ITableOptions = {
readonly borders?: ITableBordersOptions;
readonly alignment?: (typeof AlignmentType)[keyof typeof AlignmentType];
readonly visuallyRightToLeft?: boolean;
+ readonly tableLook?: ITableLookOptions;
readonly cellSpacing?: ITableCellSpacingProperties;
};
@@ -50,6 +52,7 @@ export class Table extends FileChild {
borders,
alignment,
visuallyRightToLeft,
+ tableLook,
cellSpacing,
}: ITableOptions) {
super("w:tbl");
@@ -65,6 +68,7 @@ export class Table extends FileChild {
alignment,
cellMargin: margins,
visuallyRightToLeft,
+ tableLook,
cellSpacing,
}),
);