docxmlater 10.0.4 → 10.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/core/Document.d.ts +22 -0
  2. package/dist/core/Document.d.ts.map +1 -1
  3. package/dist/core/Document.js +170 -26
  4. package/dist/core/Document.js.map +1 -1
  5. package/dist/core/DocumentParser.d.ts.map +1 -1
  6. package/dist/core/DocumentParser.js +71 -6
  7. package/dist/core/DocumentParser.js.map +1 -1
  8. package/dist/elements/Hyperlink.d.ts +6 -0
  9. package/dist/elements/Hyperlink.d.ts.map +1 -1
  10. package/dist/elements/Hyperlink.js +23 -0
  11. package/dist/elements/Hyperlink.js.map +1 -1
  12. package/dist/elements/StructuredDocumentTag.d.ts +23 -1
  13. package/dist/elements/StructuredDocumentTag.d.ts.map +1 -1
  14. package/dist/elements/StructuredDocumentTag.js +97 -0
  15. package/dist/elements/StructuredDocumentTag.js.map +1 -1
  16. package/dist/elements/TableCell.d.ts +5 -0
  17. package/dist/elements/TableCell.d.ts.map +1 -1
  18. package/dist/elements/TableCell.js +13 -0
  19. package/dist/elements/TableCell.js.map +1 -1
  20. package/dist/elements/TableRow.d.ts +3 -0
  21. package/dist/elements/TableRow.d.ts.map +1 -1
  22. package/dist/elements/TableRow.js +10 -0
  23. package/dist/elements/TableRow.js.map +1 -1
  24. package/dist/formatting/AbstractNumbering.d.ts +4 -0
  25. package/dist/formatting/AbstractNumbering.d.ts.map +1 -1
  26. package/dist/formatting/AbstractNumbering.js +15 -0
  27. package/dist/formatting/AbstractNumbering.js.map +1 -1
  28. package/dist/formatting/NumberingInstance.d.ts +6 -0
  29. package/dist/formatting/NumberingInstance.d.ts.map +1 -1
  30. package/dist/formatting/NumberingInstance.js +55 -1
  31. package/dist/formatting/NumberingInstance.js.map +1 -1
  32. package/dist/formatting/NumberingLevel.d.ts +4 -1
  33. package/dist/formatting/NumberingLevel.d.ts.map +1 -1
  34. package/dist/formatting/NumberingLevel.js +17 -0
  35. package/dist/formatting/NumberingLevel.js.map +1 -1
  36. package/dist/formatting/Style.d.ts +6 -0
  37. package/dist/formatting/Style.d.ts.map +1 -1
  38. package/dist/formatting/Style.js +20 -0
  39. package/dist/formatting/Style.js.map +1 -1
  40. package/dist/formatting/StylesManager.d.ts +23 -0
  41. package/dist/formatting/StylesManager.d.ts.map +1 -1
  42. package/dist/formatting/StylesManager.js +65 -0
  43. package/dist/formatting/StylesManager.js.map +1 -1
  44. package/dist/index.d.ts +2 -2
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js.map +1 -1
  47. package/dist/tracking/DocumentTrackingContext.d.ts.map +1 -1
  48. package/dist/tracking/DocumentTrackingContext.js +30 -7
  49. package/dist/tracking/DocumentTrackingContext.js.map +1 -1
  50. package/package.json +1 -1
  51. package/src/core/Document.ts +287 -31
  52. package/src/core/DocumentParser.ts +88 -7
  53. package/src/elements/Hyperlink.ts +47 -0
  54. package/src/elements/StructuredDocumentTag.ts +230 -1
  55. package/src/elements/TableCell.ts +36 -1
  56. package/src/elements/TableRow.ts +24 -1
  57. package/src/formatting/AbstractNumbering.ts +31 -0
  58. package/src/formatting/NumberingInstance.ts +88 -1
  59. package/src/formatting/NumberingLevel.ts +37 -3
  60. package/src/formatting/Style.ts +46 -0
  61. package/src/formatting/StylesManager.ts +125 -0
  62. package/src/index.ts +2 -2
  63. package/src/tracking/DocumentTrackingContext.ts +38 -7
@@ -4352,6 +4352,7 @@ export class DocumentParser {
4352
4352
  const tooltip = hyperlinkObj["@_w:tooltip"];
4353
4353
  const tgtFrame = hyperlinkObj["@_w:tgtFrame"];
4354
4354
  const history = hyperlinkObj["@_w:history"];
4355
+ const docLocation = hyperlinkObj["@_w:docLocation"];
4355
4356
 
4356
4357
  // Parse runs inside the hyperlink
4357
4358
  const runs = hyperlinkObj["w:r"];
@@ -4470,6 +4471,7 @@ export class DocumentParser {
4470
4471
  relationshipId: finalRelationshipId,
4471
4472
  tgtFrame,
4472
4473
  history,
4474
+ docLocation,
4473
4475
  });
4474
4476
 
4475
4477
  // If we successfully parsed a run with tabs/breaks, use it instead of the default run
@@ -4772,6 +4774,9 @@ export class DocumentParser {
4772
4774
  if (parseOoxmlBoolean(rPrObj["w:i"])) run.setItalic(true);
4773
4775
  if (parseOoxmlBoolean(rPrObj["w:iCs"])) run.setComplexScriptItalic(true);
4774
4776
  if (parseOoxmlBoolean(rPrObj["w:strike"])) run.setStrike(true);
4777
+ if (parseOoxmlBoolean(rPrObj["w:dstrike"])) {
4778
+ (run as any).formatting.dstrike = true;
4779
+ }
4775
4780
  if (parseOoxmlBoolean(rPrObj["w:smallCaps"])) run.setSmallCaps(true);
4776
4781
  if (parseOoxmlBoolean(rPrObj["w:caps"])) run.setAllCaps(true);
4777
4782
 
@@ -6094,14 +6099,14 @@ export class DocumentParser {
6094
6099
  table.setBidiVisual(true);
6095
6100
  }
6096
6101
 
6097
- // Parse table width
6102
+ // Parse table width — always set when w:tblW is present, including w:w="0" w:type="auto"
6103
+ // (auto-sized tables). Skipping w:w="0" would leave the constructor default (9360/dxa),
6104
+ // causing tblPrChange snapshots to capture wrong "previous" width values.
6098
6105
  if (tblPrObj["w:tblW"]) {
6099
- const width = parseInt(tblPrObj["w:tblW"]["@_w:w"] || "0", 10);
6106
+ const width = safeParseInt(tblPrObj["w:tblW"]["@_w:w"], 0);
6100
6107
  const widthType = tblPrObj["w:tblW"]["@_w:type"] || "dxa";
6101
- if (width > 0) {
6102
- table.setWidth(width);
6103
- table.setWidthType(widthType);
6104
- }
6108
+ table.setWidth(width);
6109
+ table.setWidthType(widthType);
6105
6110
  }
6106
6111
 
6107
6112
  // Parse table caption
@@ -6137,6 +6142,12 @@ export class DocumentParser {
6137
6142
  }
6138
6143
  }
6139
6144
 
6145
+ // Parse table indentation (w:tblInd) per ECMA-376 Part 1 §17.4.43
6146
+ if (tblPrObj["w:tblInd"]) {
6147
+ const indentVal = safeParseInt(tblPrObj["w:tblInd"]["@_w:w"], 0);
6148
+ table.setIndent(indentVal);
6149
+ }
6150
+
6140
6151
  // Parse table cell margins (w:tblCellMar) per ECMA-376 Part 1 §17.4.42
6141
6152
  if (tblPrObj["w:tblCellMar"]) {
6142
6153
  const cellMar = tblPrObj["w:tblCellMar"];
@@ -6379,6 +6390,14 @@ export class DocumentParser {
6379
6390
  }
6380
6391
  }
6381
6392
 
6393
+ // Parse divId (w:divId) per ECMA-376 Part 1 §17.4.9
6394
+ if (trPrObj["w:divId"]) {
6395
+ const val = parseInt(trPrObj["w:divId"]["@_w:val"] || "0", 10);
6396
+ if (val > 0) {
6397
+ row.setDivId(val);
6398
+ }
6399
+ }
6400
+
6382
6401
  // Parse table row property change (w:trPrChange) per ECMA-376 Part 1 §17.13.5.38
6383
6402
  if (trPrObj["w:trPrChange"]) {
6384
6403
  const changeObj = trPrObj["w:trPrChange"];
@@ -6531,6 +6550,10 @@ export class DocumentParser {
6531
6550
  borders.left = this.parseBorderElement(bordersObj["w:left"]);
6532
6551
  if (bordersObj["w:right"])
6533
6552
  borders.right = this.parseBorderElement(bordersObj["w:right"]);
6553
+ if (bordersObj["w:tl2br"])
6554
+ borders.tl2br = this.parseBorderElement(bordersObj["w:tl2br"]);
6555
+ if (bordersObj["w:tr2bl"])
6556
+ borders.tr2bl = this.parseBorderElement(bordersObj["w:tr2bl"]);
6534
6557
 
6535
6558
  if (Object.keys(borders).length > 0) {
6536
6559
  cell.setBorders(borders);
@@ -6605,6 +6628,14 @@ export class DocumentParser {
6605
6628
  cell.setHideMark(true);
6606
6629
  }
6607
6630
 
6631
+ // Parse headers (w:headers) per ECMA-376 Part 1 §17.4.26
6632
+ if (tcPr["w:headers"]) {
6633
+ const headersVal = tcPr["w:headers"]["@_w:val"];
6634
+ if (headersVal) {
6635
+ cell.setHeaders(headersVal);
6636
+ }
6637
+ }
6638
+
6608
6639
  // Parse fit text (w:tcFitText) per ECMA-376 Part 1 §17.4.68
6609
6640
  if (tcPr["w:tcFitText"]) {
6610
6641
  cell.setFitText(true);
@@ -7035,6 +7066,46 @@ export class DocumentParser {
7035
7066
  };
7036
7067
  } else if (sdtPr["w:group"]) {
7037
7068
  properties.controlType = "group";
7069
+ } else if (sdtPr["w:citation"]) {
7070
+ properties.controlType = "citation";
7071
+ } else if (sdtPr["w:bibliography"]) {
7072
+ properties.controlType = "bibliography";
7073
+ } else if (sdtPr["w:equation"]) {
7074
+ properties.controlType = "equation";
7075
+ } else if (sdtPr["w:docPartList"]) {
7076
+ properties.controlType = "docPartList";
7077
+ const docPartList = sdtPr["w:docPartList"];
7078
+ properties.buildingBlock = {
7079
+ gallery: docPartList?.["w:docPartGallery"]?.["@_w:val"],
7080
+ category: docPartList?.["w:docPartCategory"]?.["@_w:val"],
7081
+ isList: true,
7082
+ };
7083
+ }
7084
+
7085
+ // Parse placeholder (w:placeholder/w:docPart)
7086
+ const placeholderElement = sdtPr["w:placeholder"];
7087
+ if (placeholderElement) {
7088
+ const docPartVal = placeholderElement?.["w:docPart"]?.["@_w:val"];
7089
+ if (docPartVal) {
7090
+ properties.placeholder = { docPart: docPartVal };
7091
+ }
7092
+ }
7093
+
7094
+ // Parse data binding (w:dataBinding)
7095
+ const dataBindingElement = sdtPr["w:dataBinding"];
7096
+ if (dataBindingElement) {
7097
+ properties.dataBinding = {
7098
+ xpath: dataBindingElement["@_w:xpath"] || "",
7099
+ prefixMappings: dataBindingElement["@_w:prefixMappings"],
7100
+ storeItemId: dataBindingElement["@_w:storeItemID"],
7101
+ };
7102
+ }
7103
+
7104
+ // Parse showing placeholder flag (w:showingPlcHdr)
7105
+ const showingPlcHdr = sdtPr["w:showingPlcHdr"];
7106
+ if (showingPlcHdr) {
7107
+ const val = showingPlcHdr["@_w:val"];
7108
+ properties.showingPlcHdr = val === "1" || val === "true" || val === true;
7038
7109
  }
7039
7110
  }
7040
7111
 
@@ -8602,6 +8673,14 @@ export class DocumentParser {
8602
8673
  const personal =
8603
8674
  styleXml.includes("<w:personal/>") || styleXml.includes("<w:personal ");
8604
8675
 
8676
+ // personalCompose - Style for composing new messages
8677
+ const personalCompose =
8678
+ styleXml.includes("<w:personalCompose/>") || styleXml.includes("<w:personalCompose ");
8679
+
8680
+ // personalReply - Style for replying to messages
8681
+ const personalReply =
8682
+ styleXml.includes("<w:personalReply/>") || styleXml.includes("<w:personalReply ");
8683
+
8605
8684
  // autoRedefine - Update style from formatting
8606
8685
  const autoRedefine =
8607
8686
  styleXml.includes("<w:autoRedefine/>") ||
@@ -8673,6 +8752,8 @@ export class DocumentParser {
8673
8752
  unhideWhenUsed: unhideWhenUsed || undefined,
8674
8753
  locked: locked || undefined,
8675
8754
  personal: personal || undefined,
8755
+ personalCompose: personalCompose || undefined,
8756
+ personalReply: personalReply || undefined,
8676
8757
  autoRedefine: autoRedefine || undefined,
8677
8758
  uiPriority,
8678
8759
  link,
@@ -10046,7 +10127,7 @@ export class DocumentParser {
10046
10127
  if (propsObj["w:tcBorders"]) {
10047
10128
  const borders: any = {};
10048
10129
  const bordersObj = propsObj["w:tcBorders"];
10049
- for (const side of ['top', 'bottom', 'left', 'right']) {
10130
+ for (const side of ['top', 'bottom', 'left', 'right', 'tl2br', 'tr2bl']) {
10050
10131
  if (bordersObj[`w:${side}`]) {
10051
10132
  borders[side] = this.parseBorderElement(bordersObj[`w:${side}`]);
10052
10133
  }
@@ -76,6 +76,8 @@ export interface HyperlinkProperties {
76
76
  tgtFrame?: string;
77
77
  /** History tracking attribute */
78
78
  history?: string;
79
+ /** Document location for within-document navigation in external files (ECMA-376 §17.16.22) */
80
+ docLocation?: string;
79
81
  }
80
82
 
81
83
  /**
@@ -95,6 +97,8 @@ export class Hyperlink {
95
97
  private tgtFrame?: string;
96
98
  /** History tracking attribute */
97
99
  private history?: string;
100
+ /** Document location for within-document navigation in external files */
101
+ private docLocation?: string;
98
102
  /** Tracking context for automatic change tracking */
99
103
  private trackingContext?: import('../tracking/TrackingContext').TrackingContext;
100
104
  /** Parent paragraph reference for automatic tracking */
@@ -115,6 +119,7 @@ export class Hyperlink {
115
119
  this.relationshipId = properties.relationshipId;
116
120
  this.tgtFrame = properties.tgtFrame;
117
121
  this.history = properties.history;
122
+ this.docLocation = properties.docLocation;
118
123
  this._isEmpty = properties.isEmpty ?? false;
119
124
 
120
125
  // VALIDATION: Warn about hybrid links (url + anchor)
@@ -263,6 +268,40 @@ export class Hyperlink {
263
268
  return this.history;
264
269
  }
265
270
 
271
+ /**
272
+ * Sets the target frame attribute
273
+ * @param tgtFrame Target frame (e.g., "_blank" for new window)
274
+ */
275
+ setTgtFrame(tgtFrame: string | undefined): this {
276
+ this.tgtFrame = tgtFrame;
277
+ return this;
278
+ }
279
+
280
+ /**
281
+ * Sets the history tracking attribute
282
+ * @param history History value (e.g., "1" to add to history)
283
+ */
284
+ setHistory(history: string | undefined): this {
285
+ this.history = history;
286
+ return this;
287
+ }
288
+
289
+ /**
290
+ * Gets the document location attribute (ECMA-376 §17.16.22)
291
+ */
292
+ getDocLocation(): string | undefined {
293
+ return this.docLocation;
294
+ }
295
+
296
+ /**
297
+ * Sets the document location for within-document navigation in external files
298
+ * @param docLocation Location string
299
+ */
300
+ setDocLocation(docLocation: string | undefined): this {
301
+ this.docLocation = docLocation;
302
+ return this;
303
+ }
304
+
266
305
  /**
267
306
  * Gets the display text
268
307
  *
@@ -983,6 +1022,9 @@ export class Hyperlink {
983
1022
  tooltip: this.tooltip,
984
1023
  relationshipId: this.relationshipId,
985
1024
  formatting: { ...this.formatting },
1025
+ tgtFrame: this.tgtFrame,
1026
+ history: this.history,
1027
+ docLocation: this.docLocation,
986
1028
  });
987
1029
 
988
1030
  // Copy the run with its formatting
@@ -1053,6 +1095,11 @@ export class Hyperlink {
1053
1095
  attributes["w:history"] = this.history;
1054
1096
  }
1055
1097
 
1098
+ // Document location attribute (ECMA-376 §17.16.22)
1099
+ if (this.docLocation) {
1100
+ attributes["w:docLocation"] = this.docLocation;
1101
+ }
1102
+
1056
1103
  // Empty/invisible hyperlinks have no children (self-closing element)
1057
1104
  if (this._isEmpty) {
1058
1105
  return {
@@ -31,7 +31,11 @@ export type ContentControlType =
31
31
  | "checkbox"
32
32
  | "picture"
33
33
  | "buildingBlock"
34
- | "group";
34
+ | "group"
35
+ | "citation"
36
+ | "bibliography"
37
+ | "docPartList"
38
+ | "equation";
35
39
 
36
40
  /**
37
41
  * List item for combo box or dropdown
@@ -112,6 +116,28 @@ export interface BuildingBlockProperties {
112
116
  gallery?: string;
113
117
  /** Building block category */
114
118
  category?: string;
119
+ /** Whether to use docPartList (true) or docPartObj (false, default) */
120
+ isList?: boolean;
121
+ }
122
+
123
+ /**
124
+ * Placeholder properties for SDT
125
+ */
126
+ export interface SDTPlaceholder {
127
+ /** Name of the document part to use as placeholder */
128
+ docPart: string;
129
+ }
130
+
131
+ /**
132
+ * Data binding properties for SDT
133
+ */
134
+ export interface SDTDataBinding {
135
+ /** XPath expression for the data binding */
136
+ xpath: string;
137
+ /** Namespace prefix mappings */
138
+ prefixMappings?: string;
139
+ /** Custom XML data store item ID */
140
+ storeItemId?: string;
115
141
  }
116
142
 
117
143
  /**
@@ -142,6 +168,12 @@ export interface SDTProperties {
142
168
  checkbox?: CheckboxProperties;
143
169
  /** Building block properties */
144
170
  buildingBlock?: BuildingBlockProperties;
171
+ /** Placeholder (w:placeholder) */
172
+ placeholder?: SDTPlaceholder;
173
+ /** Data binding (w:dataBinding) */
174
+ dataBinding?: SDTDataBinding;
175
+ /** Whether SDT is currently showing placeholder content */
176
+ showingPlcHdr?: boolean;
145
177
  }
146
178
 
147
179
  /**
@@ -490,6 +522,38 @@ export class StructuredDocumentTag {
490
522
  );
491
523
  }
492
524
 
525
+ // Add placeholder
526
+ if (this.properties.placeholder) {
527
+ sdtPrChildren.push(
528
+ XMLBuilder.w("placeholder", {}, [
529
+ XMLBuilder.wSelf("docPart", { "w:val": this.properties.placeholder.docPart }),
530
+ ])
531
+ );
532
+ }
533
+
534
+ // Add showing placeholder flag
535
+ if (this.properties.showingPlcHdr !== undefined) {
536
+ sdtPrChildren.push(
537
+ XMLBuilder.wSelf("showingPlcHdr", {
538
+ "w:val": this.properties.showingPlcHdr ? "true" : "false",
539
+ })
540
+ );
541
+ }
542
+
543
+ // Add data binding
544
+ if (this.properties.dataBinding) {
545
+ const dbAttrs: Record<string, string> = {
546
+ "w:xpath": this.properties.dataBinding.xpath,
547
+ };
548
+ if (this.properties.dataBinding.prefixMappings) {
549
+ dbAttrs["w:prefixMappings"] = this.properties.dataBinding.prefixMappings;
550
+ }
551
+ if (this.properties.dataBinding.storeItemId) {
552
+ dbAttrs["w:storeItemID"] = this.properties.dataBinding.storeItemId;
553
+ }
554
+ sdtPrChildren.push(XMLBuilder.wSelf("dataBinding", dbAttrs));
555
+ }
556
+
493
557
  // Add control type-specific XML
494
558
  if (this.properties.controlType) {
495
559
  switch (this.properties.controlType) {
@@ -643,6 +707,39 @@ export class StructuredDocumentTag {
643
707
  case "group":
644
708
  sdtPrChildren.push(XMLBuilder.wSelf("group", {}));
645
709
  break;
710
+
711
+ case "citation":
712
+ sdtPrChildren.push(XMLBuilder.wSelf("citation", {}));
713
+ break;
714
+
715
+ case "bibliography":
716
+ sdtPrChildren.push(XMLBuilder.wSelf("bibliography", {}));
717
+ break;
718
+
719
+ case "equation":
720
+ sdtPrChildren.push(XMLBuilder.wSelf("equation", {}));
721
+ break;
722
+
723
+ case "docPartList":
724
+ if (this.properties.buildingBlock) {
725
+ const dplChildren: XMLElement[] = [];
726
+ if (this.properties.buildingBlock.gallery) {
727
+ dplChildren.push(
728
+ XMLBuilder.wSelf("docPartGallery", {
729
+ "w:val": this.properties.buildingBlock.gallery,
730
+ })
731
+ );
732
+ }
733
+ if (this.properties.buildingBlock.category) {
734
+ dplChildren.push(
735
+ XMLBuilder.wSelf("docPartCategory", {
736
+ "w:val": this.properties.buildingBlock.category,
737
+ })
738
+ );
739
+ }
740
+ sdtPrChildren.push(XMLBuilder.w("docPartList", {}, dplChildren));
741
+ }
742
+ break;
646
743
  }
647
744
  }
648
745
 
@@ -975,4 +1072,136 @@ export class StructuredDocumentTag {
975
1072
  const lock = this.properties.lock;
976
1073
  return lock !== "contentLocked" && lock !== "sdtContentLocked";
977
1074
  }
1075
+
1076
+ /**
1077
+ * Get the placeholder configuration
1078
+ */
1079
+ getPlaceholder(): SDTPlaceholder | undefined {
1080
+ return this.properties.placeholder;
1081
+ }
1082
+
1083
+ /**
1084
+ * Set the placeholder (w:placeholder/w:docPart)
1085
+ * @param docPart - Name of the document part to use as placeholder
1086
+ */
1087
+ setPlaceholder(docPart: string): this {
1088
+ this.properties.placeholder = { docPart };
1089
+ return this;
1090
+ }
1091
+
1092
+ /**
1093
+ * Get the data binding configuration
1094
+ */
1095
+ getDataBinding(): SDTDataBinding | undefined {
1096
+ return this.properties.dataBinding;
1097
+ }
1098
+
1099
+ /**
1100
+ * Set the data binding (w:dataBinding)
1101
+ * @param xpath - XPath expression
1102
+ * @param prefixMappings - Namespace prefix mappings
1103
+ * @param storeItemId - Custom XML data store item ID
1104
+ */
1105
+ setDataBinding(xpath: string, prefixMappings?: string, storeItemId?: string): this {
1106
+ this.properties.dataBinding = { xpath, prefixMappings, storeItemId };
1107
+ return this;
1108
+ }
1109
+
1110
+ /**
1111
+ * Get whether the SDT is currently showing placeholder content
1112
+ */
1113
+ isShowingPlaceholder(): boolean {
1114
+ return this.properties.showingPlcHdr === true;
1115
+ }
1116
+
1117
+ /**
1118
+ * Set whether the SDT is showing placeholder content
1119
+ * @param val - Whether placeholder is showing
1120
+ */
1121
+ setShowingPlaceholder(val: boolean): this {
1122
+ this.properties.showingPlcHdr = val;
1123
+ return this;
1124
+ }
1125
+
1126
+ /**
1127
+ * Create a citation content control
1128
+ * @param content - Initial content
1129
+ * @param properties - Additional SDT properties
1130
+ */
1131
+ static createCitation(
1132
+ content: SDTContent[] = [],
1133
+ properties: Partial<SDTProperties> = {}
1134
+ ): StructuredDocumentTag {
1135
+ return new StructuredDocumentTag(
1136
+ {
1137
+ id: Date.now() % 1000000000,
1138
+ controlType: "citation",
1139
+ ...properties,
1140
+ },
1141
+ content
1142
+ );
1143
+ }
1144
+
1145
+ /**
1146
+ * Create a bibliography content control
1147
+ * @param content - Initial content
1148
+ * @param properties - Additional SDT properties
1149
+ */
1150
+ static createBibliography(
1151
+ content: SDTContent[] = [],
1152
+ properties: Partial<SDTProperties> = {}
1153
+ ): StructuredDocumentTag {
1154
+ return new StructuredDocumentTag(
1155
+ {
1156
+ id: Date.now() % 1000000000,
1157
+ controlType: "bibliography",
1158
+ ...properties,
1159
+ },
1160
+ content
1161
+ );
1162
+ }
1163
+
1164
+ /**
1165
+ * Create a document part list content control
1166
+ * Uses w:docPartList instead of w:docPartObj
1167
+ * @param gallery - Building block gallery name
1168
+ * @param category - Building block category
1169
+ * @param content - Initial content
1170
+ * @param properties - Additional SDT properties
1171
+ */
1172
+ static createDocPartList(
1173
+ gallery: string,
1174
+ category: string,
1175
+ content: SDTContent[] = [],
1176
+ properties: Partial<SDTProperties> = {}
1177
+ ): StructuredDocumentTag {
1178
+ return new StructuredDocumentTag(
1179
+ {
1180
+ id: Date.now() % 1000000000,
1181
+ controlType: "docPartList",
1182
+ buildingBlock: { gallery, category, isList: true },
1183
+ ...properties,
1184
+ },
1185
+ content
1186
+ );
1187
+ }
1188
+
1189
+ /**
1190
+ * Create an equation content control
1191
+ * @param content - Initial content
1192
+ * @param properties - Additional SDT properties
1193
+ */
1194
+ static createEquation(
1195
+ content: SDTContent[] = [],
1196
+ properties: Partial<SDTProperties> = {}
1197
+ ): StructuredDocumentTag {
1198
+ return new StructuredDocumentTag(
1199
+ {
1200
+ id: Date.now() % 1000000000,
1201
+ controlType: "equation",
1202
+ ...properties,
1203
+ },
1204
+ content
1205
+ );
1206
+ }
978
1207
  }
@@ -46,6 +46,10 @@ export interface CellBorders {
46
46
  bottom?: CellBorder;
47
47
  left?: CellBorder;
48
48
  right?: CellBorder;
49
+ /** Diagonal border from top-left to bottom-right per ECMA-376 Part 1 §17.4.84 */
50
+ tl2br?: CellBorder;
51
+ /** Diagonal border from top-right to bottom-left per ECMA-376 Part 1 §17.4.85 */
52
+ tr2bl?: CellBorder;
49
53
  }
50
54
 
51
55
  /**
@@ -102,6 +106,8 @@ export interface CellFormatting {
102
106
  cnfStyle?: string; // Conditional formatting style (14-char binary string)
103
107
  vMerge?: VerticalMerge; // Vertical cell merge
104
108
  hMerge?: 'restart' | 'continue'; // Legacy horizontal merge (w:hMerge) per ECMA-376 Part 1 §17.4.22
109
+ /** Cell headers attribute for accessibility per ECMA-376 Part 1 §17.4.26 */
110
+ headers?: string;
105
111
  }
106
112
 
107
113
  /**
@@ -947,6 +953,25 @@ export class TableCell {
947
953
  return this.formatting.hMerge;
948
954
  }
949
955
 
956
+ /**
957
+ * Sets the cell headers attribute for accessibility
958
+ * Links data cells to header cells per ECMA-376 Part 1 §17.4.26
959
+ * @param headers - Space-separated list of header cell IDs
960
+ * @returns This cell for chaining
961
+ */
962
+ setHeaders(headers: string): this {
963
+ this.formatting.headers = headers;
964
+ return this;
965
+ }
966
+
967
+ /**
968
+ * Gets the cell headers attribute
969
+ * @returns Headers string or undefined
970
+ */
971
+ getHeaders(): string | undefined {
972
+ return this.formatting.headers;
973
+ }
974
+
950
975
  /**
951
976
  * Gets the cell margins
952
977
  * @returns Margins object with top, right, bottom, left or undefined
@@ -1306,7 +1331,7 @@ export class TableCell {
1306
1331
  const borderElements: XMLElement[] = [];
1307
1332
  const borders = this.formatting.borders;
1308
1333
 
1309
- // Ordered per ECMA-376 CT_TcBorders: top, left, bottom, right
1334
+ // Ordered per ECMA-376 CT_TcBorders: top, left, bottom, right, insideH, insideV, tl2br, tr2bl
1310
1335
  if (borders.top) {
1311
1336
  borderElements.push(XMLBuilder.createBorder("top", borders.top));
1312
1337
  }
@@ -1319,6 +1344,12 @@ export class TableCell {
1319
1344
  if (borders.right) {
1320
1345
  borderElements.push(XMLBuilder.createBorder("right", borders.right));
1321
1346
  }
1347
+ if (borders.tl2br) {
1348
+ borderElements.push(XMLBuilder.createBorder("tl2br", borders.tl2br));
1349
+ }
1350
+ if (borders.tr2bl) {
1351
+ borderElements.push(XMLBuilder.createBorder("tr2bl", borders.tr2bl));
1352
+ }
1322
1353
 
1323
1354
  if (borderElements.length > 0) {
1324
1355
  tcPrChildren.push(XMLBuilder.w("tcBorders", undefined, borderElements));
@@ -1409,6 +1440,10 @@ export class TableCell {
1409
1440
  tcPrChildren.push(XMLBuilder.wSelf("hideMark"));
1410
1441
  }
1411
1442
 
1443
+ // Note: w:headers (cell headers for accessibility) is defined in ECMA-376 Part 1 §17.4.26
1444
+ // but is NOT included in the Transitional schema and fails OOXML validation.
1445
+ // The property is preserved in memory for reading but not generated in XML.
1446
+
1412
1447
  // Add cell revision markers (w:cellIns, w:cellDel, w:cellMerge) per ECMA-376 Part 1 §17.13.5.4-5.6
1413
1448
  if (this.cellRevision) {
1414
1449
  const revType = this.cellRevision.getType();
@@ -74,6 +74,7 @@ export interface RowFormatting {
74
74
  cellSpacing?: number; // Row-level cell spacing override in twips
75
75
  cellSpacingType?: string; // Cell spacing type (dxa, pct)
76
76
  cnfStyle?: string; // Conditional formatting bitmask (per ECMA-376 §17.3.1.8)
77
+ divId?: number; // HTML div association (per ECMA-376 §17.4.9)
77
78
  }
78
79
 
79
80
  /**
@@ -544,6 +545,25 @@ export class TableRow {
544
545
  return this;
545
546
  }
546
547
 
548
+ /**
549
+ * Sets the HTML div ID for web round-trip
550
+ * Per ECMA-376 Part 1 §17.4.9
551
+ * @param id - Div ID number
552
+ * @returns This row for chaining
553
+ */
554
+ setDivId(id: number): this {
555
+ this.formatting.divId = id;
556
+ return this;
557
+ }
558
+
559
+ /**
560
+ * Gets the HTML div ID
561
+ * @returns Div ID or undefined
562
+ */
563
+ getDivId(): number | undefined {
564
+ return this.formatting.divId;
565
+ }
566
+
547
567
  /**
548
568
  * Gets table property exceptions
549
569
  * @returns Table property exceptions or undefined
@@ -735,7 +755,10 @@ export class TableRow {
735
755
  trPrChildren.push(XMLBuilder.wSelf('cnfStyle', { 'w:val': this.formatting.cnfStyle }));
736
756
  }
737
757
 
738
- // 2. (divId not supported)
758
+ // 2. divId
759
+ if (this.formatting.divId !== undefined) {
760
+ trPrChildren.push(XMLBuilder.wSelf('divId', { 'w:val': this.formatting.divId }));
761
+ }
739
762
 
740
763
  // 3. gridBefore
741
764
  if (this.formatting.gridBefore !== undefined) {