docxmlater 10.3.5 → 10.4.0

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 (185) hide show
  1. package/README.md +158 -7
  2. package/dist/core/Document.d.ts +102 -3
  3. package/dist/core/Document.d.ts.map +1 -1
  4. package/dist/core/Document.js +775 -50
  5. package/dist/core/Document.js.map +1 -1
  6. package/dist/core/DocumentContent.d.ts.map +1 -1
  7. package/dist/core/DocumentContent.js +0 -8
  8. package/dist/core/DocumentContent.js.map +1 -1
  9. package/dist/core/DocumentGenerator.d.ts.map +1 -1
  10. package/dist/core/DocumentGenerator.js +9 -5
  11. package/dist/core/DocumentGenerator.js.map +1 -1
  12. package/dist/core/DocumentParser.d.ts.map +1 -1
  13. package/dist/core/DocumentParser.js +588 -102
  14. package/dist/core/DocumentParser.js.map +1 -1
  15. package/dist/core/RelationshipManager.d.ts.map +1 -1
  16. package/dist/core/RelationshipManager.js +4 -3
  17. package/dist/core/RelationshipManager.js.map +1 -1
  18. package/dist/elements/Bookmark.d.ts +7 -0
  19. package/dist/elements/Bookmark.d.ts.map +1 -1
  20. package/dist/elements/Bookmark.js +24 -4
  21. package/dist/elements/Bookmark.js.map +1 -1
  22. package/dist/elements/BookmarkManager.d.ts.map +1 -1
  23. package/dist/elements/BookmarkManager.js +4 -3
  24. package/dist/elements/BookmarkManager.js.map +1 -1
  25. package/dist/elements/CommonTypes.d.ts +2 -2
  26. package/dist/elements/CommonTypes.d.ts.map +1 -1
  27. package/dist/elements/CommonTypes.js +14 -1
  28. package/dist/elements/CommonTypes.js.map +1 -1
  29. package/dist/elements/Field.d.ts +1 -1
  30. package/dist/elements/Field.d.ts.map +1 -1
  31. package/dist/elements/Field.js +1 -1
  32. package/dist/elements/Field.js.map +1 -1
  33. package/dist/elements/Footer.d.ts +2 -0
  34. package/dist/elements/Footer.d.ts.map +1 -1
  35. package/dist/elements/Footer.js +6 -0
  36. package/dist/elements/Footer.js.map +1 -1
  37. package/dist/elements/Header.d.ts +2 -0
  38. package/dist/elements/Header.d.ts.map +1 -1
  39. package/dist/elements/Header.js +6 -0
  40. package/dist/elements/Header.js.map +1 -1
  41. package/dist/elements/Image.d.ts +1 -0
  42. package/dist/elements/Image.d.ts.map +1 -1
  43. package/dist/elements/Image.js +17 -2
  44. package/dist/elements/Image.js.map +1 -1
  45. package/dist/elements/Paragraph.d.ts +81 -1
  46. package/dist/elements/Paragraph.d.ts.map +1 -1
  47. package/dist/elements/Paragraph.js +515 -21
  48. package/dist/elements/Paragraph.js.map +1 -1
  49. package/dist/elements/Revision.d.ts +0 -1
  50. package/dist/elements/Revision.d.ts.map +1 -1
  51. package/dist/elements/Revision.js +0 -12
  52. package/dist/elements/Revision.js.map +1 -1
  53. package/dist/elements/RevisionManager.d.ts +0 -1
  54. package/dist/elements/RevisionManager.d.ts.map +1 -1
  55. package/dist/elements/RevisionManager.js +0 -2
  56. package/dist/elements/RevisionManager.js.map +1 -1
  57. package/dist/elements/Run.d.ts +16 -4
  58. package/dist/elements/Run.d.ts.map +1 -1
  59. package/dist/elements/Run.js +114 -22
  60. package/dist/elements/Run.js.map +1 -1
  61. package/dist/elements/Section.d.ts +7 -1
  62. package/dist/elements/Section.d.ts.map +1 -1
  63. package/dist/elements/Section.js +185 -4
  64. package/dist/elements/Section.js.map +1 -1
  65. package/dist/elements/Shape.js.map +1 -1
  66. package/dist/elements/Table.d.ts +30 -1
  67. package/dist/elements/Table.d.ts.map +1 -1
  68. package/dist/elements/Table.js +357 -40
  69. package/dist/elements/Table.js.map +1 -1
  70. package/dist/elements/TableCell.d.ts +3 -0
  71. package/dist/elements/TableCell.d.ts.map +1 -1
  72. package/dist/elements/TableCell.js +30 -3
  73. package/dist/elements/TableCell.js.map +1 -1
  74. package/dist/elements/TableGridChange.d.ts +0 -1
  75. package/dist/elements/TableGridChange.d.ts.map +1 -1
  76. package/dist/elements/TableGridChange.js +0 -10
  77. package/dist/elements/TableGridChange.js.map +1 -1
  78. package/dist/elements/TableRow.d.ts +4 -0
  79. package/dist/elements/TableRow.d.ts.map +1 -1
  80. package/dist/elements/TableRow.js +31 -3
  81. package/dist/elements/TableRow.js.map +1 -1
  82. package/dist/formatting/AbstractNumbering.d.ts +5 -0
  83. package/dist/formatting/AbstractNumbering.d.ts.map +1 -1
  84. package/dist/formatting/AbstractNumbering.js +22 -0
  85. package/dist/formatting/AbstractNumbering.js.map +1 -1
  86. package/dist/formatting/NumberingLevel.d.ts.map +1 -1
  87. package/dist/formatting/NumberingLevel.js +3 -3
  88. package/dist/formatting/NumberingLevel.js.map +1 -1
  89. package/dist/formatting/Style.d.ts +1 -0
  90. package/dist/formatting/Style.d.ts.map +1 -1
  91. package/dist/formatting/Style.js +25 -59
  92. package/dist/formatting/Style.js.map +1 -1
  93. package/dist/formatting/StylesManager.d.ts +1 -0
  94. package/dist/formatting/StylesManager.d.ts.map +1 -1
  95. package/dist/formatting/StylesManager.js +12 -0
  96. package/dist/formatting/StylesManager.js.map +1 -1
  97. package/dist/helpers/CleanupHelper.js.map +1 -1
  98. package/dist/images/ImageOptimizer.d.ts.map +1 -1
  99. package/dist/images/ImageOptimizer.js +0 -1
  100. package/dist/images/ImageOptimizer.js.map +1 -1
  101. package/dist/index.d.ts +1 -1
  102. package/dist/index.d.ts.map +1 -1
  103. package/dist/index.js.map +1 -1
  104. package/dist/managers/DrawingManager.d.ts.map +1 -1
  105. package/dist/managers/DrawingManager.js +4 -2
  106. package/dist/managers/DrawingManager.js.map +1 -1
  107. package/dist/types/formatting.d.ts +2 -2
  108. package/dist/types/formatting.d.ts.map +1 -1
  109. package/dist/types/formatting.js.map +1 -1
  110. package/dist/utils/ChangelogGenerator.d.ts +2 -2
  111. package/dist/utils/ChangelogGenerator.d.ts.map +1 -1
  112. package/dist/utils/ChangelogGenerator.js +4 -5
  113. package/dist/utils/ChangelogGenerator.js.map +1 -1
  114. package/dist/utils/InMemoryRevisionAcceptor.d.ts.map +1 -1
  115. package/dist/utils/InMemoryRevisionAcceptor.js +0 -1
  116. package/dist/utils/InMemoryRevisionAcceptor.js.map +1 -1
  117. package/dist/utils/RevisionAwareProcessor.d.ts +2 -2
  118. package/dist/utils/RevisionAwareProcessor.d.ts.map +1 -1
  119. package/dist/utils/RevisionAwareProcessor.js +2 -2
  120. package/dist/utils/RevisionAwareProcessor.js.map +1 -1
  121. package/dist/utils/SelectiveRevisionAcceptor.d.ts +0 -2
  122. package/dist/utils/SelectiveRevisionAcceptor.d.ts.map +1 -1
  123. package/dist/utils/SelectiveRevisionAcceptor.js +0 -26
  124. package/dist/utils/SelectiveRevisionAcceptor.js.map +1 -1
  125. package/dist/utils/ShadingResolver.d.ts.map +1 -1
  126. package/dist/utils/ShadingResolver.js.map +1 -1
  127. package/dist/utils/acceptRevisions.js +1 -1
  128. package/dist/utils/acceptRevisions.js.map +1 -1
  129. package/dist/utils/stripTrackedChanges.js +1 -1
  130. package/dist/utils/stripTrackedChanges.js.map +1 -1
  131. package/dist/utils/units.d.ts.map +1 -1
  132. package/dist/utils/units.js +1 -1
  133. package/dist/utils/units.js.map +1 -1
  134. package/dist/validation/RevisionAutoFixer.d.ts +2 -1
  135. package/dist/validation/RevisionAutoFixer.d.ts.map +1 -1
  136. package/dist/validation/RevisionAutoFixer.js.map +1 -1
  137. package/package.json +10 -1
  138. package/src/constants/CLAUDE.md +28 -0
  139. package/src/core/CLAUDE.md +4 -0
  140. package/src/core/Document.ts +1888 -137
  141. package/src/core/DocumentContent.ts +0 -11
  142. package/src/core/DocumentGenerator.ts +11 -12
  143. package/src/core/DocumentParser.ts +620 -139
  144. package/src/core/RelationshipManager.ts +6 -3
  145. package/src/elements/Bookmark.ts +39 -4
  146. package/src/elements/BookmarkManager.ts +4 -3
  147. package/src/elements/CLAUDE.md +18 -2
  148. package/src/elements/CommonTypes.ts +35 -8
  149. package/src/elements/Field.ts +1 -1
  150. package/src/elements/Footer.ts +23 -0
  151. package/src/elements/Header.ts +25 -0
  152. package/src/elements/Image.ts +28 -5
  153. package/src/elements/Paragraph.ts +1069 -41
  154. package/src/elements/Revision.ts +0 -19
  155. package/src/elements/RevisionManager.ts +1 -3
  156. package/src/elements/Run.ts +265 -35
  157. package/src/elements/Section.ts +214 -8
  158. package/src/elements/Shape.ts +1 -1
  159. package/src/elements/Table.ts +850 -61
  160. package/src/elements/TableCell.ts +84 -10
  161. package/src/elements/TableGridChange.ts +2 -16
  162. package/src/elements/TableRow.ts +94 -9
  163. package/src/formatting/AbstractNumbering.ts +42 -1
  164. package/src/formatting/CLAUDE.md +4 -0
  165. package/src/formatting/NumberingLevel.ts +11 -7
  166. package/src/formatting/Style.ts +39 -71
  167. package/src/formatting/StylesManager.ts +36 -0
  168. package/src/helpers/CleanupHelper.ts +1 -1
  169. package/src/images/ImageOptimizer.ts +0 -3
  170. package/src/index.ts +1 -1
  171. package/src/managers/DrawingManager.ts +5 -3
  172. package/src/tracking/CLAUDE.md +30 -0
  173. package/src/types/CLAUDE.md +39 -0
  174. package/src/types/formatting.ts +2 -2
  175. package/src/utils/CLAUDE.md +15 -0
  176. package/src/utils/ChangelogGenerator.ts +4 -5
  177. package/src/utils/InMemoryRevisionAcceptor.ts +0 -9
  178. package/src/utils/RevisionAwareProcessor.ts +2 -3
  179. package/src/utils/SelectiveRevisionAcceptor.ts +0 -39
  180. package/src/utils/ShadingResolver.ts +0 -1
  181. package/src/utils/acceptRevisions.ts +1 -1
  182. package/src/utils/stripTrackedChanges.ts +1 -1
  183. package/src/utils/units.ts +2 -1
  184. package/src/validation/CLAUDE.md +40 -0
  185. package/src/validation/RevisionAutoFixer.ts +2 -1
@@ -2,19 +2,16 @@
2
2
  * TableCell - Represents a cell in a table
3
3
  */
4
4
 
5
+ import { deepClone } from '../utils/deepClone';
5
6
  import { formatDateForXml } from '../utils/dateFormatting';
6
7
  import { XMLBuilder, XMLElement } from '../xml/XMLBuilder';
7
8
  import { Paragraph, TextDirection } from './Paragraph';
8
9
  import { Revision } from './Revision';
9
10
  import {
10
11
  BorderStyle as CommonBorderStyle,
11
- BorderDefinition,
12
- FourSidedBorders,
13
12
  CellVerticalAlignment as CommonCellVerticalAlignment,
14
13
  ShadingConfig,
15
- ShadingPattern,
16
14
  buildShadingAttributes,
17
- WidthType,
18
15
  } from './CommonTypes';
19
16
 
20
17
  // ============================================================================
@@ -446,6 +443,36 @@ export class TableCell {
446
443
  return this;
447
444
  }
448
445
 
446
+ /**
447
+ * Sets the cell background color
448
+ *
449
+ * Convenience method that wraps `setShading()` with a clear pattern.
450
+ * Equivalent to `setShading({ fill: color, pattern: 'clear' })`.
451
+ *
452
+ * @param color - Hex color without # (e.g., 'FF0000' for red, 'FFFFFF' for white)
453
+ * @returns This cell for chaining
454
+ *
455
+ * @example
456
+ * ```typescript
457
+ * cell.setBackgroundColor('FFFF00'); // Yellow background
458
+ * cell.setBackgroundColor('F2F2F2'); // Light gray
459
+ * ```
460
+ */
461
+ setBackgroundColor(color: string): this {
462
+ return this.setShading({ fill: color, pattern: 'clear' });
463
+ }
464
+
465
+ /**
466
+ * Gets the cell background color
467
+ *
468
+ * Returns the fill color from the cell's shading, or undefined if not set.
469
+ *
470
+ * @returns Hex color string or undefined
471
+ */
472
+ getBackgroundColor(): string | undefined {
473
+ return this.formatting.shading?.fill;
474
+ }
475
+
449
476
  /**
450
477
  * Sets vertical alignment
451
478
  * @param alignment - Vertical alignment
@@ -1262,6 +1289,46 @@ export class TableCell {
1262
1289
  return table.getFormatting().style;
1263
1290
  }
1264
1291
 
1292
+ /**
1293
+ * Creates a deep clone of this cell
1294
+ *
1295
+ * Copies all formatting, paragraphs, raw nested content, and property change
1296
+ * tracking. The clone is independent of the original — changes to one do not
1297
+ * affect the other. Parent row reference is NOT copied (set by addCell/insertCellAt).
1298
+ *
1299
+ * @returns New TableCell instance with the same content and formatting
1300
+ *
1301
+ * @example
1302
+ * ```typescript
1303
+ * const cell = new TableCell({ width: 2400, widthType: 'dxa' });
1304
+ * cell.createParagraph('Hello');
1305
+ * const copy = cell.clone();
1306
+ * copy.createParagraph('World');
1307
+ * // Original cell still has only "Hello"
1308
+ * ```
1309
+ */
1310
+ clone(): TableCell {
1311
+ const clonedCell = new TableCell(deepClone(this.formatting));
1312
+
1313
+ for (const para of this.paragraphs) {
1314
+ clonedCell.addParagraph(para.clone());
1315
+ }
1316
+
1317
+ for (const item of this.rawNestedContent) {
1318
+ clonedCell.addRawNestedContent(item.position, item.xml, item.type);
1319
+ }
1320
+
1321
+ if (this.tcPrChange) {
1322
+ clonedCell.setTcPrChange(deepClone(this.tcPrChange));
1323
+ }
1324
+
1325
+ if (this.cellRevision) {
1326
+ clonedCell.setCellRevision(this.cellRevision);
1327
+ }
1328
+
1329
+ return clonedCell;
1330
+ }
1331
+
1265
1332
  /**
1266
1333
  * Converts the cell to WordprocessingML XML element
1267
1334
  * @returns XMLElement representing the cell
@@ -1314,7 +1381,8 @@ export class TableCell {
1314
1381
  const borderElements: XMLElement[] = [];
1315
1382
  const borders = this.formatting.borders;
1316
1383
 
1317
- // Ordered per ECMA-376 CT_TcBorders: top, left, bottom, right, insideH, insideV, tl2br, tr2bl
1384
+ // Ordered per ECMA-376 CT_TcBorders (§17.4.67): top, left, bottom, right, tl2br, tr2bl
1385
+ // Note: insideH/insideV are only in CT_TblBorders (table-level), not CT_TcBorders (cell-level)
1318
1386
  if (borders.top) {
1319
1387
  borderElements.push(XMLBuilder.createBorder('top', borders.top));
1320
1388
  }
@@ -1466,8 +1534,12 @@ export class TableCell {
1466
1534
  const prevTcPrChildren: XMLElement[] = [];
1467
1535
  const prev = this.tcPrChange.previousProperties;
1468
1536
  if (prev) {
1469
- // Ordered per CT_TcPr: tcW → gridSpan → hMerge → vMerge → tcBorders → shd →
1470
- // noWraptcMartextDirectiontcFitTextvAlignhideMarkcnfStyle
1537
+ // Ordered per CT_TcPr (ECMA-376 §17.4.70):
1538
+ // cnfStyletcWgridSpanhMergevMergetcBordersshd →
1539
+ // noWrap → tcMar → textDirection → tcFitText → vAlign → hideMark
1540
+ if (prev.cnfStyle) {
1541
+ prevTcPrChildren.push(XMLBuilder.wSelf('cnfStyle', { 'w:val': prev.cnfStyle }));
1542
+ }
1471
1543
  if (prev.width !== undefined) {
1472
1544
  prevTcPrChildren.push(
1473
1545
  XMLBuilder.wSelf('tcW', {
@@ -1491,6 +1563,7 @@ export class TableCell {
1491
1563
  }
1492
1564
  if (prev.borders) {
1493
1565
  const borderElements: XMLElement[] = [];
1566
+ // Ordered per CT_TcBorders (§17.4.67): top, left, bottom, right, tl2br, tr2bl
1494
1567
  if (prev.borders.top)
1495
1568
  borderElements.push(XMLBuilder.createBorder('top', prev.borders.top));
1496
1569
  if (prev.borders.left)
@@ -1499,6 +1572,10 @@ export class TableCell {
1499
1572
  borderElements.push(XMLBuilder.createBorder('bottom', prev.borders.bottom));
1500
1573
  if (prev.borders.right)
1501
1574
  borderElements.push(XMLBuilder.createBorder('right', prev.borders.right));
1575
+ if (prev.borders.tl2br)
1576
+ borderElements.push(XMLBuilder.createBorder('tl2br', prev.borders.tl2br));
1577
+ if (prev.borders.tr2bl)
1578
+ borderElements.push(XMLBuilder.createBorder('tr2bl', prev.borders.tr2bl));
1502
1579
  if (borderElements.length > 0) {
1503
1580
  prevTcPrChildren.push(XMLBuilder.w('tcBorders', undefined, borderElements));
1504
1581
  }
@@ -1550,9 +1627,6 @@ export class TableCell {
1550
1627
  if (prev.hideMark) {
1551
1628
  prevTcPrChildren.push(XMLBuilder.wSelf('hideMark'));
1552
1629
  }
1553
- if (prev.cnfStyle) {
1554
- prevTcPrChildren.push(XMLBuilder.wSelf('cnfStyle', { 'w:val': prev.cnfStyle }));
1555
- }
1556
1630
  }
1557
1631
  const prevTcPr =
1558
1632
  prevTcPrChildren.length > 0
@@ -6,7 +6,6 @@
6
6
  */
7
7
 
8
8
  import { XMLElement, XMLBuilder } from '../xml/XMLBuilder';
9
- import { formatDateForXml } from '../utils/dateFormatting';
10
9
 
11
10
  /**
12
11
  * Table grid column definition
@@ -86,30 +85,17 @@ export class TableGridChange {
86
85
  return [...this.previousGrid];
87
86
  }
88
87
 
89
- /**
90
- * Formats a date to ISO 8601 format for XML
91
- * Uses formatDateForXml() to strip milliseconds which Word does not accept.
92
- */
93
- private formatDate(date: Date): string {
94
- return formatDateForXml(date);
95
- }
96
-
97
88
  /**
98
89
  * Generates XML for this table grid change
99
90
  * @returns XMLElement representing the table grid change
100
91
  */
101
92
  toXML(): XMLElement {
93
+ // Per ECMA-376 §17.13.5.35, tblGridChange uses CT_Markup (only w:id)
94
+ // NOT CT_TrackChange — w:author and w:date are not valid attributes
102
95
  const attributes: Record<string, string> = {
103
96
  'w:id': this.id.toString(),
104
97
  };
105
98
 
106
- if (this.author) {
107
- attributes['w:author'] = this.author;
108
- }
109
- if (this.date) {
110
- attributes['w:date'] = this.formatDate(this.date);
111
- }
112
-
113
99
  // Build previous grid
114
100
  const gridChildren: XMLElement[] = [];
115
101
  for (const col of this.previousGrid) {
@@ -2,14 +2,14 @@
2
2
  * TableRow - Represents a row in a table
3
3
  */
4
4
 
5
+ import { deepClone } from '../utils/deepClone';
5
6
  import { TableCell } from './TableCell';
6
7
  import { XMLBuilder, XMLElement } from '../xml/XMLBuilder';
7
- import { TableBorder, TableBorders } from './Table';
8
+ import { TableBorders } from './Table';
8
9
  import {
9
10
  BasicShadingPattern,
10
11
  RowJustification as CommonRowJustification,
11
12
  ShadingConfig,
12
- buildShadingAttributes,
13
13
  } from './CommonTypes';
14
14
  import { defaultLogger } from '../utils/logger';
15
15
 
@@ -318,6 +318,22 @@ export class TableRow {
318
318
  return this;
319
319
  }
320
320
 
321
+ /**
322
+ * Sets the height rule independently of the height value.
323
+ * Per ECMA-376 §17.18.33, valid values are "auto", "atLeast", "exact".
324
+ * When undefined, the w:hRule attribute is omitted (defaults to "auto" per spec).
325
+ * @param rule - Height rule, or undefined to omit (use spec default "auto")
326
+ * @returns This row for chaining
327
+ */
328
+ setHeightRule(rule: RowFormatting['heightRule']): this {
329
+ const prevRule = this.formatting.heightRule;
330
+ this.formatting.heightRule = rule;
331
+ if (this.trackingContext?.isEnabled() && prevRule !== rule) {
332
+ this.trackingContext.trackTableChange(this, 'heightRule', prevRule, rule);
333
+ }
334
+ return this;
335
+ }
336
+
321
337
  /**
322
338
  * Clears the row height, allowing Word to auto-size the row based on content
323
339
  * @returns This row for chaining
@@ -859,11 +875,15 @@ export class TableRow {
859
875
  const prevTrPrChildren: XMLElement[] = [];
860
876
  const prev = this.trPrChange.previousProperties;
861
877
  if (prev) {
862
- // Ordered per CT_TrPr: cnfStyle → gridBefore → gridAfter → wBefore → wAfter →
878
+ // Ordered per CT_TrPr (ECMA-376 §17.4.79):
879
+ // cnfStyle → divId → gridBefore → gridAfter → wBefore → wAfter →
863
880
  // cantSplit → trHeight → tblHeader → tblCellSpacing → jc → hidden
864
881
  if (prev.cnfStyle) {
865
882
  prevTrPrChildren.push(XMLBuilder.wSelf('cnfStyle', { 'w:val': prev.cnfStyle }));
866
883
  }
884
+ if (prev.divId !== undefined) {
885
+ prevTrPrChildren.push(XMLBuilder.wSelf('divId', { 'w:val': prev.divId }));
886
+ }
867
887
  if (prev.gridBefore !== undefined) {
868
888
  prevTrPrChildren.push(XMLBuilder.wSelf('gridBefore', { 'w:val': prev.gridBefore }));
869
889
  }
@@ -920,14 +940,10 @@ export class TableRow {
920
940
  }
921
941
 
922
942
  // Build row element
943
+ // Per ECMA-376 §17.4.78 (CT_Row): tblPrEx → trPr → tc*
923
944
  const rowChildren: XMLElement[] = [];
924
945
 
925
- // Add row properties if there are any
926
- if (trPrChildren.length > 0) {
927
- rowChildren.push(XMLBuilder.w('trPr', undefined, trPrChildren));
928
- }
929
-
930
- // Add table property exceptions (tblPrEx) if present
946
+ // Add table property exceptions (tblPrEx) - must come BEFORE trPr per CT_Row
931
947
  if (this.formatting.tablePropertyExceptions) {
932
948
  const tblPrExChildren = this.buildTablePropertyExceptionsXML(
933
949
  this.formatting.tablePropertyExceptions
@@ -937,6 +953,11 @@ export class TableRow {
937
953
  }
938
954
  }
939
955
 
956
+ // Add row properties if there are any
957
+ if (trPrChildren.length > 0) {
958
+ rowChildren.push(XMLBuilder.w('trPr', undefined, trPrChildren));
959
+ }
960
+
940
961
  // Add all cells - each cell is independent
941
962
  // Note: gridSpan (columnSpan) means a single cell spans multiple columns in the grid,
942
963
  // it does NOT mean subsequent cells should be skipped. Each cell in the array
@@ -948,6 +969,70 @@ export class TableRow {
948
969
  return XMLBuilder.w('tr', undefined, rowChildren);
949
970
  }
950
971
 
972
+ /**
973
+ * Gets the text content of all cells as a string array
974
+ *
975
+ * @returns Array of cell text values
976
+ *
977
+ * @example
978
+ * ```typescript
979
+ * const values = row.toArray(); // ['Alice', '30', 'NYC']
980
+ * ```
981
+ */
982
+ toArray(): string[] {
983
+ return this.cells.map((cell) => cell.getText());
984
+ }
985
+
986
+ /**
987
+ * Gets the concatenated text of all cells, separated by a delimiter
988
+ *
989
+ * @param separator - Separator between cell texts (default: tab)
990
+ * @returns Concatenated cell text
991
+ *
992
+ * @example
993
+ * ```typescript
994
+ * console.log(row.getText()); // "Alice\t30\tNYC"
995
+ * console.log(row.getText(', ')); // "Alice, 30, NYC"
996
+ * ```
997
+ */
998
+ getText(separator = '\t'): string {
999
+ return this.cells.map((cell) => cell.getText()).join(separator);
1000
+ }
1001
+
1002
+ /**
1003
+ * Creates a deep clone of this row
1004
+ *
1005
+ * Copies all cells (via TableCell.clone()), formatting, and property change
1006
+ * tracking. The clone is independent of the original. Parent table reference
1007
+ * is NOT copied (set by Table.addRow/insertRow).
1008
+ *
1009
+ * @returns New TableRow instance with the same structure and content
1010
+ *
1011
+ * @example
1012
+ * ```typescript
1013
+ * const row = new TableRow(3);
1014
+ * row.getCell(0)?.createParagraph('Cell A');
1015
+ * row.setHeader(true);
1016
+ *
1017
+ * const copy = row.clone();
1018
+ * copy.getCell(0)?.createParagraph('Modified');
1019
+ * // Original row unchanged
1020
+ * ```
1021
+ */
1022
+ clone(): TableRow {
1023
+ const clonedRow = new TableRow(0, deepClone(this.formatting));
1024
+
1025
+ for (const cell of this.cells) {
1026
+ clonedRow.addCell(cell.clone());
1027
+ }
1028
+
1029
+ if (this.trPrChange) {
1030
+ clonedRow.setTrPrChange(deepClone(this.trPrChange));
1031
+ }
1032
+
1033
+ return clonedRow;
1034
+ }
1035
+
951
1036
  /**
952
1037
  * Creates a new TableRow
953
1038
  * @param cellCount - Number of cells to create
@@ -34,6 +34,9 @@ export interface AbstractNumberingProperties {
34
34
 
35
35
  /** Template code (ECMA-376 §17.9.30) - 8-char hex string identifying the template */
36
36
  tmpl?: string;
37
+
38
+ /** Namespace-scoped unique ID (ECMA-376 §17.9.17) - 8-char hex string */
39
+ nsid?: string;
37
40
  }
38
41
 
39
42
  /**
@@ -50,6 +53,7 @@ export class AbstractNumbering {
50
53
  private numStyleLink?: string;
51
54
  private styleLink?: string;
52
55
  private tmpl?: string;
56
+ private nsid?: string;
53
57
 
54
58
  /**
55
59
  * Creates a new abstract numbering definition
@@ -64,6 +68,7 @@ export class AbstractNumbering {
64
68
  this.name = name;
65
69
  this.levels = new Map();
66
70
  this.multiLevelType = 1; // default multilevel
71
+ this.nsid = AbstractNumbering.generateNsid();
67
72
  } else {
68
73
  // Object constructor: new AbstractNumbering({ abstractNumId, ... })
69
74
  const properties = idOrProps;
@@ -74,6 +79,7 @@ export class AbstractNumbering {
74
79
  this.numStyleLink = properties.numStyleLink;
75
80
  this.styleLink = properties.styleLink;
76
81
  this.tmpl = properties.tmpl;
82
+ this.nsid = properties.nsid ?? AbstractNumbering.generateNsid();
77
83
 
78
84
  if (properties.levels) {
79
85
  properties.levels.forEach((level) => {
@@ -112,6 +118,31 @@ export class AbstractNumbering {
112
118
  return this.abstractNumId;
113
119
  }
114
120
 
121
+ /**
122
+ * Gets the namespace-scoped unique ID (ECMA-376 §17.9.17)
123
+ */
124
+ getNsid(): string | undefined {
125
+ return this.nsid;
126
+ }
127
+
128
+ /**
129
+ * Sets the namespace-scoped unique ID
130
+ */
131
+ setNsid(nsid: string): void {
132
+ this.nsid = nsid;
133
+ }
134
+
135
+ /**
136
+ * Generates a random 8-character uppercase hex nsid value
137
+ */
138
+ private static generateNsid(): string {
139
+ const hex = Math.floor(Math.random() * 0xffffffff)
140
+ .toString(16)
141
+ .toUpperCase()
142
+ .padStart(8, '0');
143
+ return hex;
144
+ }
145
+
115
146
  /**
116
147
  * Gets the name
117
148
  */
@@ -281,7 +312,12 @@ export class AbstractNumbering {
281
312
 
282
313
  // CT_AbstractNum order per ECMA-376: nsid → multiLevelType → tmpl → name → styleLink → numStyleLink → lvl*
283
314
 
284
- // Add multiLevelType
315
+ // 1. nsid (ECMA-376 §17.9.17) — namespace-scoped unique identifier
316
+ if (this.nsid) {
317
+ children.push(XMLBuilder.wSelf('nsid', { 'w:val': this.nsid }));
318
+ }
319
+
320
+ // 2. multiLevelType
285
321
  let multiLevelTypeValue: string;
286
322
  if (this.multiLevelType === 1) {
287
323
  multiLevelTypeValue = 'multilevel';
@@ -549,6 +585,10 @@ export class AbstractNumbering {
549
585
  const tmplMatch = /<w:tmpl[^>]*w:val="([^"]+)"/.exec(xml);
550
586
  const tmpl = tmplMatch?.[1] || undefined;
551
587
 
588
+ // Extract nsid (optional, ECMA-376 §17.9.17) — 8-char hex namespace ID
589
+ const nsidMatch = /<w:nsid[^>]*w:val="([^"]+)"/.exec(xml);
590
+ const nsid = nsidMatch?.[1] || undefined;
591
+
552
592
  // Create abstract numbering
553
593
  const abstractNum = new AbstractNumbering({
554
594
  abstractNumId,
@@ -557,6 +597,7 @@ export class AbstractNumbering {
557
597
  numStyleLink,
558
598
  styleLink,
559
599
  tmpl,
600
+ nsid,
560
601
  });
561
602
 
562
603
  // Extract and parse all levels
@@ -12,6 +12,10 @@ Properties: `styleId`, `type`, `name`, `basedOn` (inheritance), `next`, `link`,
12
12
 
13
13
  **Inheritance:** Styles inherit via `basedOn`. Resolution: default → basedOn chain → own formatting.
14
14
 
15
+ **Run properties:** Style.ts delegates rPr generation to `Run.generateRunPropertiesXML()` for full ECMA-376 CT_RPr coverage (38+ elements in correct order).
16
+
17
+ **Conditional formatting:** `tblStylePr` elements follow CT_Style order: pPr → rPr → tblPr → trPr → tcPr.
18
+
15
19
  Built-in styles: `Normal`, `Heading1`-`Heading6`, `Title`, `Subtitle`, `DefaultParagraphFont`, `TableGrid`.
16
20
 
17
21
  ### StylesManager (`StylesManager.ts`)
@@ -444,7 +444,8 @@ export class NumberingLevel {
444
444
  * Generates the WordprocessingML XML for this level
445
445
  */
446
446
  toXML(): XMLElement {
447
- // ECMA-376 CT_Lvl element order: start, numFmt, lvlRestart, pStyle, isLgl, suff, lvlText, lvlJc, pPr, rPr
447
+ // ECMA-376 CT_Lvl element order (§17.9.6):
448
+ // start, numFmt, lvlRestart, pStyle, isLgl, suff, lvlText, lvlPicBulletId, legacy, lvlJc, pPr, rPr
448
449
  const children: XMLElement[] = [];
449
450
 
450
451
  // 1. Start value
@@ -514,20 +515,23 @@ export class NumberingLevel {
514
515
  rPrChildren.push(XMLBuilder.wSelf('iCs'));
515
516
  }
516
517
 
517
- // Underline
518
- if (this.properties.underline) {
519
- rPrChildren.push(XMLBuilder.wSelf('u', { 'w:val': this.properties.underline }));
520
- }
518
+ // CT_RPr element order per ECMA-376 §17.3.2.28:
519
+ // ... b, bCs, i, iCs, ... color (#19), ... sz (#24), szCs (#25), ... u (#27)
521
520
 
522
- // Color
521
+ // Color (#19)
523
522
  if (this.properties.color) {
524
523
  rPrChildren.push(XMLBuilder.wSelf('color', { 'w:val': this.properties.color }));
525
524
  }
526
525
 
527
- // Font size
526
+ // Font size (#24, #25)
528
527
  rPrChildren.push(XMLBuilder.wSelf('sz', { 'w:val': this.properties.fontSize.toString() }));
529
528
  rPrChildren.push(XMLBuilder.wSelf('szCs', { 'w:val': this.properties.fontSize.toString() }));
530
529
 
530
+ // Underline (#27)
531
+ if (this.properties.underline) {
532
+ rPrChildren.push(XMLBuilder.wSelf('u', { 'w:val': this.properties.underline }));
533
+ }
534
+
531
535
  const rPr = XMLBuilder.w('rPr', undefined, rPrChildren);
532
536
  children.push(rPr);
533
537
 
@@ -5,11 +5,10 @@
5
5
 
6
6
  import { XMLBuilder, XMLElement } from '../xml/XMLBuilder';
7
7
  import { ParagraphFormatting } from '../elements/Paragraph';
8
- import { RunFormatting } from '../elements/Run';
9
- import { ShadingConfig, ShadingPattern, buildShadingAttributes } from '../elements/CommonTypes';
8
+ import { Run, RunFormatting } from '../elements/Run';
9
+ import { ShadingConfig, buildShadingAttributes } from '../elements/CommonTypes';
10
10
  import { Heading2TableOptions } from '../types/styleConfig';
11
11
  import { deepClone } from '../utils/deepClone';
12
- import { pointsToHalfPoints } from '../utils/units';
13
12
 
14
13
  /**
15
14
  * Style type
@@ -85,6 +84,8 @@ export interface CellMargins {
85
84
  export interface TableStyleFormatting {
86
85
  /** Table indentation from left margin in twips */
87
86
  indent?: number;
87
+ /** Indent width type per ECMA-376 ST_TblWidth */
88
+ indentType?: string;
88
89
  /** Default cell spacing in twips */
89
90
  cellSpacing?: number;
90
91
  /** Table borders */
@@ -828,11 +829,28 @@ export class Style {
828
829
  );
829
830
  }
830
831
  if (numPrChildren.length > 0) {
831
- // Insert numPr at the beginning of pPr children (per ECMA-376 element order)
832
- if (pPr.children) {
833
- pPr.children.unshift(XMLBuilder.w('numPr', undefined, numPrChildren));
832
+ // Per CT_PPrBase, numPr is at position #7: after keepNext, keepLines, pageBreakBefore,
833
+ // framePr, widowControl — insert after the last of these that's present.
834
+ const numPrEl = XMLBuilder.w('numPr', undefined, numPrChildren);
835
+ if (pPr.children && pPr.children.length > 0) {
836
+ // Find insertion point: after keepNext/keepLines/pageBreakBefore (positions #2-#4)
837
+ // These are the only elements generateParagraphProperties() emits before numPr
838
+ let insertIdx = 0;
839
+ for (let i = 0; i < pPr.children.length; i++) {
840
+ const child = pPr.children[i];
841
+ if (child && typeof child !== 'string') {
842
+ if (
843
+ child.name === 'w:keepNext' ||
844
+ child.name === 'w:keepLines' ||
845
+ child.name === 'w:pageBreakBefore'
846
+ ) {
847
+ insertIdx = i + 1;
848
+ }
849
+ }
850
+ }
851
+ pPr.children.splice(insertIdx, 0, numPrEl);
834
852
  } else {
835
- pPr.children = [XMLBuilder.w('numPr', undefined, numPrChildren)];
853
+ pPr.children = [numPrEl];
836
854
  }
837
855
  }
838
856
  }
@@ -959,63 +977,13 @@ export class Style {
959
977
  }
960
978
 
961
979
  /**
962
- * Generates run properties XML
980
+ * Generates run properties XML.
981
+ * Delegates to Run.generateRunPropertiesXML() for full ECMA-376 CT_RPr coverage
982
+ * with correct element ordering (38+ properties).
963
983
  */
964
984
  private generateRunProperties(formatting: RunFormatting): XMLElement {
965
- const rPrChildren: XMLElement[] = [];
966
-
967
- // Add formatting elements — ordered per ECMA-376 CT_RPr
968
- if (formatting.font) {
969
- rPrChildren.push(
970
- XMLBuilder.wSelf('rFonts', {
971
- 'w:ascii': formatting.font,
972
- 'w:hAnsi': formatting.font,
973
- 'w:cs': formatting.font,
974
- })
975
- );
976
- }
977
- if (formatting.bold) {
978
- rPrChildren.push(XMLBuilder.wSelf('b'));
979
- }
980
- if (formatting.italic) {
981
- rPrChildren.push(XMLBuilder.wSelf('i'));
982
- }
983
- if (formatting.allCaps) {
984
- rPrChildren.push(XMLBuilder.wSelf('caps'));
985
- }
986
- if (formatting.smallCaps) {
987
- rPrChildren.push(XMLBuilder.wSelf('smallCaps'));
988
- }
989
- if (formatting.strike) {
990
- rPrChildren.push(XMLBuilder.wSelf('strike'));
991
- }
992
- if (formatting.dstrike) {
993
- rPrChildren.push(XMLBuilder.wSelf('dstrike'));
994
- }
995
- if (formatting.color) {
996
- rPrChildren.push(XMLBuilder.wSelf('color', { 'w:val': formatting.color }));
997
- }
998
- if (formatting.size !== undefined) {
999
- const halfPoints = pointsToHalfPoints(formatting.size);
1000
- rPrChildren.push(XMLBuilder.wSelf('sz', { 'w:val': halfPoints }));
1001
- rPrChildren.push(XMLBuilder.wSelf('szCs', { 'w:val': halfPoints }));
1002
- }
1003
- if (formatting.highlight) {
1004
- rPrChildren.push(XMLBuilder.wSelf('highlight', { 'w:val': formatting.highlight }));
1005
- }
1006
- if (formatting.underline) {
1007
- const underlineValue =
1008
- typeof formatting.underline === 'string' ? formatting.underline : 'single';
1009
- rPrChildren.push(XMLBuilder.wSelf('u', { 'w:val': underlineValue }));
1010
- }
1011
- if (formatting.subscript) {
1012
- rPrChildren.push(XMLBuilder.wSelf('vertAlign', { 'w:val': 'subscript' }));
1013
- }
1014
- if (formatting.superscript) {
1015
- rPrChildren.push(XMLBuilder.wSelf('vertAlign', { 'w:val': 'superscript' }));
1016
- }
1017
-
1018
- return XMLBuilder.w('rPr', undefined, rPrChildren);
985
+ const rPr = Run.generateRunPropertiesXML(formatting);
986
+ return rPr || XMLBuilder.w('rPr', undefined, []);
1019
987
  }
1020
988
 
1021
989
  /**
@@ -1073,7 +1041,7 @@ export class Style {
1073
1041
  tblPrChildren.push(
1074
1042
  XMLBuilder.wSelf('tblInd', {
1075
1043
  'w:w': formatting.indent,
1076
- 'w:type': 'dxa',
1044
+ 'w:type': formatting.indentType || 'dxa',
1077
1045
  })
1078
1046
  );
1079
1047
  }
@@ -1254,6 +1222,14 @@ export class Style {
1254
1222
  }
1255
1223
  }
1256
1224
 
1225
+ // Add row properties if specified (trPr before tcPr per CT_Style §17.7.6)
1226
+ if (conditional.rowFormatting) {
1227
+ const trPr = this.generateTableRowProperties(conditional.rowFormatting);
1228
+ if (trPr.children && trPr.children.length > 0) {
1229
+ tblStylePrChildren.push(trPr);
1230
+ }
1231
+ }
1232
+
1257
1233
  // Add cell properties if specified
1258
1234
  if (conditional.cellFormatting) {
1259
1235
  const tcPr = this.generateTableCellProperties(conditional.cellFormatting);
@@ -1262,14 +1238,6 @@ export class Style {
1262
1238
  }
1263
1239
  }
1264
1240
 
1265
- // Add row properties if specified
1266
- if (conditional.rowFormatting) {
1267
- const trPr = this.generateTableRowProperties(conditional.rowFormatting);
1268
- if (trPr.children && trPr.children.length > 0) {
1269
- tblStylePrChildren.push(trPr);
1270
- }
1271
- }
1272
-
1273
1241
  return XMLBuilder.w('tblStylePr', { 'w:type': conditional.type }, tblStylePrChildren);
1274
1242
  }
1275
1243