diff --git a/demo/90-check-boxes.ts b/demo/90-check-boxes.ts
new file mode 100644
index 0000000000..7c5d4145ed
--- /dev/null
+++ b/demo/90-check-boxes.ts
@@ -0,0 +1,44 @@
+// Simple example to add check boxes to a document
+import * as fs from "fs";
+import { Document, Packer, Paragraph, TextRun, CheckBox } from "docx";
+
+const doc = new Document({
+ sections: [
+ {
+ properties: {},
+ children: [
+ new Paragraph({
+ children: [
+ new TextRun("Hello World"),
+ new TextRun({ break: 1 }),
+ new CheckBox(),
+ new TextRun({ break: 1 }),
+ new CheckBox({ checked: true }),
+ new TextRun({ break: 1 }),
+ new CheckBox({ checked: true, checkedState: { value: "2611" } }),
+ new TextRun({ break: 1 }),
+ new CheckBox({ checked: true, checkedState: { value: "2611", font: "MS Gothic" } }),
+ new TextRun({ break: 1 }),
+ new CheckBox({
+ checked: true,
+ checkedState: { value: "2611", font: "MS Gothic" },
+ uncheckedState: { value: "2610", font: "MS Gothic" },
+ }),
+ new TextRun({ break: 1 }),
+ new CheckBox({
+ checked: true,
+ checkedState: { value: "2611", font: "MS Gothic" },
+ uncheckedState: { value: "2610", font: "MS Gothic" },
+ }),
+ new TextRun({ text: "Are you ok?", break: 1 }),
+ new CheckBox({ checked: true, alias: "Are you ok?" }),
+ ],
+ }),
+ ],
+ },
+ ],
+});
+
+Packer.toBuffer(doc).then((buffer) => {
+ fs.writeFileSync("My Document.docx", buffer);
+});
diff --git a/src/file/checkbox/checkbox-symbol.ts b/src/file/checkbox/checkbox-symbol.ts
new file mode 100644
index 0000000000..7e172c3141
--- /dev/null
+++ b/src/file/checkbox/checkbox-symbol.ts
@@ -0,0 +1,29 @@
+// This represents element type CT_SdtCheckboxSymbol element
+//
+//
+//
+//
+
+import { XmlAttributeComponent, XmlComponent } from "@file/xml-components";
+import { shortHexNumber } from "@util/values";
+
+class CheckboxSymbolAttributes extends XmlAttributeComponent<{
+ readonly val?: string | number | boolean;
+ readonly symbolfont?: string;
+}> {
+ protected readonly xmlKeys = {
+ val: "w14:val",
+ symbolfont: "w14:font",
+ };
+}
+
+export class CheckBoxSymbolElement extends XmlComponent {
+ public constructor(name: string, val: string, font?: string) {
+ super(name);
+ if (font) {
+ this.root.push(new CheckboxSymbolAttributes({ val: shortHexNumber(val), symbolfont: font }));
+ } else {
+ this.root.push(new CheckboxSymbolAttributes({ val }));
+ }
+ }
+}
diff --git a/src/file/checkbox/checkbox-util.spec.ts b/src/file/checkbox/checkbox-util.spec.ts
new file mode 100644
index 0000000000..74fc74b820
--- /dev/null
+++ b/src/file/checkbox/checkbox-util.spec.ts
@@ -0,0 +1,85 @@
+import { describe, expect, it } from "vitest";
+import { Formatter } from "@export/formatter";
+import { CheckBoxUtil } from ".";
+
+describe("CheckBoxUtil", () => {
+ describe("#constructor()", () => {
+ it("should create a CheckBoxUtil with proper root and default values", () => {
+ const checkBoxUtil = new CheckBoxUtil();
+
+ const tree = new Formatter().format(checkBoxUtil);
+
+ expect(tree).to.deep.equal({
+ "w14:checkbox": [
+ {
+ "w14:checked": {
+ _attr: {
+ "w14:val": "0",
+ },
+ },
+ },
+ {
+ "w14:checkedState": {
+ _attr: {
+ "w14:font": "MS Gothic",
+ "w14:val": "2612",
+ },
+ },
+ },
+ {
+ "w14:uncheckedState": {
+ _attr: {
+ "w14:font": "MS Gothic",
+ "w14:val": "2610",
+ },
+ },
+ },
+ ],
+ });
+ });
+
+ it("should create a CheckBoxUtil with proper structure and custom values", () => {
+ const checkBoxUtil = new CheckBoxUtil({
+ checked: true,
+ checkedState: {
+ value: "2713",
+ font: "Segoe UI Symbol",
+ },
+ uncheckedState: {
+ value: "2705",
+ font: "Segoe UI Symbol",
+ },
+ });
+
+ const tree = new Formatter().format(checkBoxUtil);
+
+ expect(tree).to.deep.equal({
+ "w14:checkbox": [
+ {
+ "w14:checked": {
+ _attr: {
+ "w14:val": "1",
+ },
+ },
+ },
+ {
+ "w14:checkedState": {
+ _attr: {
+ "w14:font": "Segoe UI Symbol",
+ "w14:val": "2713",
+ },
+ },
+ },
+ {
+ "w14:uncheckedState": {
+ _attr: {
+ "w14:font": "Segoe UI Symbol",
+ "w14:val": "2705",
+ },
+ },
+ },
+ ],
+ });
+ });
+ });
+});
diff --git a/src/file/checkbox/checkbox-util.ts b/src/file/checkbox/checkbox-util.ts
new file mode 100644
index 0000000000..d6abb2a3b3
--- /dev/null
+++ b/src/file/checkbox/checkbox-util.ts
@@ -0,0 +1,45 @@
+//
+//
+//
+//
+//
+//
+//
+//
+
+import { XmlComponent } from "@file/xml-components";
+import { CheckBoxSymbolElement } from "@file/checkbox/checkbox-symbol";
+
+export interface ICheckboxSymbolProperties {
+ readonly value?: string;
+ readonly font?: string;
+}
+
+export interface ICheckboxSymbolOptions {
+ readonly alias?: string;
+ readonly checked?: boolean;
+ readonly checkedState?: ICheckboxSymbolProperties;
+ readonly uncheckedState?: ICheckboxSymbolProperties;
+}
+
+export class CheckBoxUtil extends XmlComponent {
+ private readonly DEFAULT_UNCHECKED_SYMBOL: string = "2610";
+ private readonly DEFAULT_CHECKED_SYMBOL: string = "2612";
+ private readonly DEFAULT_FONT: string = "MS Gothic";
+ public constructor(options?: ICheckboxSymbolOptions) {
+ super("w14:checkbox");
+
+ const value = options?.checked ? "1" : "0";
+ let symbol: string;
+ let font: string;
+ this.root.push(new CheckBoxSymbolElement("w14:checked", value));
+
+ symbol = options?.checkedState?.value ? options?.checkedState?.value : this.DEFAULT_CHECKED_SYMBOL;
+ font = options?.checkedState?.font ? options?.checkedState?.font : this.DEFAULT_FONT;
+ this.root.push(new CheckBoxSymbolElement("w14:checkedState", symbol, font));
+
+ symbol = options?.uncheckedState?.value ? options?.uncheckedState?.value : this.DEFAULT_UNCHECKED_SYMBOL;
+ font = options?.uncheckedState?.font ? options?.uncheckedState?.font : this.DEFAULT_FONT;
+ this.root.push(new CheckBoxSymbolElement("w14:uncheckedState", symbol, font));
+ }
+}
diff --git a/src/file/checkbox/checkbox.spec.ts b/src/file/checkbox/checkbox.spec.ts
new file mode 100644
index 0000000000..e6553ac459
--- /dev/null
+++ b/src/file/checkbox/checkbox.spec.ts
@@ -0,0 +1,213 @@
+import { describe, expect, it } from "vitest";
+import { Formatter } from "@export/formatter";
+
+import { CheckBox } from "./checkbox";
+
+describe("CheckBox", () => {
+ describe("#constructor()", () => {
+ it("should create a CheckBox with proper root and default values (no alias, no custom state)", () => {
+ const checkBox = new CheckBox();
+
+ const tree = new Formatter().format(checkBox);
+
+ expect(tree).to.deep.equal({
+ "w:sdt": [
+ {
+ "w:sdtPr": [
+ {
+ "w14:checkbox": [
+ {
+ "w14:checked": {
+ _attr: {
+ "w14:val": "0",
+ },
+ },
+ },
+ {
+ "w14:checkedState": {
+ _attr: {
+ "w14:font": "MS Gothic",
+ "w14:val": "2612",
+ },
+ },
+ },
+ {
+ "w14:uncheckedState": {
+ _attr: {
+ "w14:font": "MS Gothic",
+ "w14:val": "2610",
+ },
+ },
+ },
+ ],
+ },
+ ],
+ },
+ {
+ "w:sdtContent": [
+ {
+ "w:r": [
+ {
+ "w:sym": {
+ _attr: {
+ "w:char": "2610",
+ "w:font": "MS Gothic",
+ },
+ },
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ });
+ });
+
+ it.each([
+ ["2713", "Segoe UI Symbol", "2713", "Segoe UI Symbol"],
+ [undefined, undefined, "2612", "MS Gothic"],
+ ])("should create a CheckBox with proper root and custom values", (inputChar, inputFont, actualChar, actualFont) => {
+ const checkBox = new CheckBox({
+ alias: "Custom Checkbox",
+ checked: true,
+ checkedState: {
+ value: inputChar,
+ font: inputFont,
+ },
+ uncheckedState: {
+ value: "2705",
+ font: "Segoe UI Symbol",
+ },
+ });
+
+ const tree = new Formatter().format(checkBox);
+
+ expect(tree).to.deep.equal({
+ "w:sdt": [
+ {
+ "w:sdtPr": [
+ {
+ "w:alias": {
+ _attr: {
+ "w:val": "Custom Checkbox",
+ },
+ },
+ },
+ {
+ "w14:checkbox": [
+ {
+ "w14:checked": {
+ _attr: {
+ "w14:val": "1",
+ },
+ },
+ },
+ {
+ "w14:checkedState": {
+ _attr: {
+ "w14:font": actualFont,
+ "w14:val": actualChar,
+ },
+ },
+ },
+ {
+ "w14:uncheckedState": {
+ _attr: {
+ "w14:font": "Segoe UI Symbol",
+ "w14:val": "2705",
+ },
+ },
+ },
+ ],
+ },
+ ],
+ },
+ {
+ "w:sdtContent": [
+ {
+ "w:r": [
+ {
+ "w:sym": {
+ _attr: {
+ "w:char": actualChar,
+ "w:font": actualFont,
+ },
+ },
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ });
+ });
+
+ it("should create a CheckBox with proper root, custom state, and no alias", () => {
+ const checkBox = new CheckBox({
+ checked: false,
+ checkedState: {
+ value: "2713",
+ font: "Segoe UI Symbol",
+ },
+ uncheckedState: {
+ value: "2705",
+ font: "Segoe UI Symbol",
+ },
+ });
+
+ const tree = new Formatter().format(checkBox);
+
+ expect(tree).to.deep.equal({
+ "w:sdt": [
+ {
+ "w:sdtPr": [
+ {
+ "w14:checkbox": [
+ {
+ "w14:checked": {
+ _attr: {
+ "w14:val": "0",
+ },
+ },
+ },
+ {
+ "w14:checkedState": {
+ _attr: {
+ "w14:font": "Segoe UI Symbol",
+ "w14:val": "2713",
+ },
+ },
+ },
+ {
+ "w14:uncheckedState": {
+ _attr: {
+ "w14:font": "Segoe UI Symbol",
+ "w14:val": "2705",
+ },
+ },
+ },
+ ],
+ },
+ ],
+ },
+ {
+ "w:sdtContent": [
+ {
+ "w:r": [
+ {
+ "w:sym": {
+ _attr: {
+ "w:char": "2705",
+ "w:font": "Segoe UI Symbol",
+ },
+ },
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ });
+ });
+ });
+});
diff --git a/src/file/checkbox/checkbox.ts b/src/file/checkbox/checkbox.ts
new file mode 100644
index 0000000000..9b75ee7f6c
--- /dev/null
+++ b/src/file/checkbox/checkbox.ts
@@ -0,0 +1,43 @@
+import { SymbolRun } from "@file/paragraph/run/symbol-run";
+import { StructuredDocumentTagProperties } from "@file/table-of-contents/sdt-properties";
+import { StructuredDocumentTagContent } from "@file/table-of-contents/sdt-content";
+import { XmlComponent } from "@file/xml-components";
+import { CheckBoxUtil, ICheckboxSymbolOptions } from "./checkbox-util";
+
+export class CheckBox extends XmlComponent {
+ // default values per Microsoft
+ private readonly DEFAULT_UNCHECKED_SYMBOL: string = "2610";
+ private readonly DEFAULT_CHECKED_SYMBOL: string = "2612";
+ private readonly DEFAULT_FONT: string = "MS Gothic";
+ public constructor(options?: ICheckboxSymbolOptions) {
+ super("w:sdt");
+
+ const properties = new StructuredDocumentTagProperties(options?.alias);
+ properties.addChildElement(new CheckBoxUtil(options));
+ this.root.push(properties);
+
+ const content = new StructuredDocumentTagContent();
+ const checkedFont: string | undefined = options?.checkedState?.font;
+ const checkedText: string | undefined = options?.checkedState?.value;
+ const uncheckedFont: string | undefined = options?.uncheckedState?.font;
+ const uncheckedText: string | undefined = options?.uncheckedState?.value;
+ let symbolFont: string;
+ let char: string;
+
+ if (options?.checked) {
+ symbolFont = checkedFont ? checkedFont : this.DEFAULT_FONT;
+ char = checkedText ? checkedText : this.DEFAULT_CHECKED_SYMBOL;
+ } else {
+ symbolFont = uncheckedFont ? uncheckedFont : this.DEFAULT_FONT;
+ char = uncheckedText ? uncheckedText : this.DEFAULT_UNCHECKED_SYMBOL;
+ }
+
+ const initialRenderedChar = new SymbolRun({
+ char: char,
+ symbolfont: symbolFont,
+ });
+
+ content.addChildElement(initialRenderedChar);
+ this.root.push(content);
+ }
+}
diff --git a/src/file/checkbox/index.ts b/src/file/checkbox/index.ts
new file mode 100644
index 0000000000..9af5c31e28
--- /dev/null
+++ b/src/file/checkbox/index.ts
@@ -0,0 +1,3 @@
+export * from "./checkbox-util";
+export * from "./checkbox-symbol";
+export * from "./checkbox";
diff --git a/src/file/index.ts b/src/file/index.ts
index 4bdac87ef9..6cf75e4f24 100644
--- a/src/file/index.ts
+++ b/src/file/index.ts
@@ -17,3 +17,4 @@ export * from "./track-revision";
export * from "./shared";
export * from "./border";
export * from "./vertical-align";
+export * from "./checkbox";
diff --git a/src/file/paragraph/paragraph.ts b/src/file/paragraph/paragraph.ts
index a12b88df6a..feafb1450f 100644
--- a/src/file/paragraph/paragraph.ts
+++ b/src/file/paragraph/paragraph.ts
@@ -6,6 +6,7 @@ import { FileChild } from "@file/file-child";
import { TargetModeType } from "../relationships/relationship/relationship";
import { DeletedTextRun, InsertedTextRun } from "../track-revision";
+import { CheckBox } from "../checkbox";
import { ColumnBreak, PageBreak } from "./formatting/break";
import { Bookmark, ConcreteHyperlink, ExternalHyperlink, InternalHyperlink } from "./links";
import { Math } from "./math";
@@ -33,7 +34,8 @@ export type ParagraphChild =
| Comment
| CommentRangeStart
| CommentRangeEnd
- | CommentReference;
+ | CommentReference
+ | CheckBox;
export interface IParagraphOptions extends IParagraphPropertiesOptions {
readonly text?: string;
diff --git a/src/file/table-of-contents/sdt-properties.ts b/src/file/table-of-contents/sdt-properties.ts
index f1f2c56304..ba69d5e97a 100644
--- a/src/file/table-of-contents/sdt-properties.ts
+++ b/src/file/table-of-contents/sdt-properties.ts
@@ -2,8 +2,11 @@
import { StringValueElement, XmlComponent } from "@file/xml-components";
export class StructuredDocumentTagProperties extends XmlComponent {
- public constructor(alias: string) {
+ public constructor(alias?: string) {
super("w:sdtPr");
- this.root.push(new StringValueElement("w:alias", alias));
+
+ if (alias) {
+ this.root.push(new StringValueElement("w:alias", alias));
+ }
}
}