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..a3732c282e
--- /dev/null
+++ b/src/file/checkbox/checkbox-util.ts
@@ -0,0 +1,44 @@
+//
+//
+//
+//
+//
+//
+//
+//
+
+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 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..8599e60f42
--- /dev/null
+++ b/src/file/checkbox/checkbox.spec.ts
@@ -0,0 +1,211 @@
+import { describe, expect, it } from "vitest";
+import { Formatter } from "@export/formatter";
+import { CheckBox } from ".";
+
+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("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(undefined, {
+ 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..c60690e3af
--- /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(alias?: string, options?: ICheckboxSymbolOptions) {
+ super("w:sdt");
+
+ const properties = new StructuredDocumentTagProperties(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..4ee856eda3 100644
--- a/src/file/table-of-contents/sdt-properties.ts
+++ b/src/file/table-of-contents/sdt-properties.ts
@@ -2,8 +2,10 @@
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 (typeof alias === "string") {
+ this.root.push(new StringValueElement("w:alias", alias));
+ }
}
}