docxmlater 0.30.0 → 1.0.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 (82) hide show
  1. package/README.md +277 -180
  2. package/dist/core/Document.d.ts +26 -0
  3. package/dist/core/Document.d.ts.map +1 -1
  4. package/dist/core/Document.js +86 -2
  5. package/dist/core/Document.js.map +1 -1
  6. package/dist/core/DocumentGenerator.d.ts +3 -2
  7. package/dist/core/DocumentGenerator.d.ts.map +1 -1
  8. package/dist/core/DocumentGenerator.js +52 -7
  9. package/dist/core/DocumentGenerator.js.map +1 -1
  10. package/dist/core/DocumentParser.d.ts +18 -0
  11. package/dist/core/DocumentParser.d.ts.map +1 -1
  12. package/dist/core/DocumentParser.js +1317 -32
  13. package/dist/core/DocumentParser.js.map +1 -1
  14. package/dist/elements/Field.d.ts +38 -1
  15. package/dist/elements/Field.d.ts.map +1 -1
  16. package/dist/elements/Field.js +264 -1
  17. package/dist/elements/Field.js.map +1 -1
  18. package/dist/elements/Image.d.ts +94 -0
  19. package/dist/elements/Image.d.ts.map +1 -1
  20. package/dist/elements/Image.js +311 -25
  21. package/dist/elements/Image.js.map +1 -1
  22. package/dist/elements/Paragraph.d.ts +109 -31
  23. package/dist/elements/Paragraph.d.ts.map +1 -1
  24. package/dist/elements/Paragraph.js +274 -0
  25. package/dist/elements/Paragraph.js.map +1 -1
  26. package/dist/elements/Run.d.ts +68 -0
  27. package/dist/elements/Run.d.ts.map +1 -1
  28. package/dist/elements/Run.js +243 -39
  29. package/dist/elements/Run.js.map +1 -1
  30. package/dist/elements/Section.d.ts +16 -0
  31. package/dist/elements/Section.d.ts.map +1 -1
  32. package/dist/elements/Section.js +61 -1
  33. package/dist/elements/Section.js.map +1 -1
  34. package/dist/elements/Shape.d.ts +86 -0
  35. package/dist/elements/Shape.d.ts.map +1 -0
  36. package/dist/elements/Shape.js +461 -0
  37. package/dist/elements/Shape.js.map +1 -0
  38. package/dist/elements/StructuredDocumentTag.d.ts +61 -0
  39. package/dist/elements/StructuredDocumentTag.d.ts.map +1 -1
  40. package/dist/elements/StructuredDocumentTag.js +235 -0
  41. package/dist/elements/StructuredDocumentTag.js.map +1 -1
  42. package/dist/elements/Table.d.ts +33 -0
  43. package/dist/elements/Table.d.ts.map +1 -1
  44. package/dist/elements/Table.js +82 -8
  45. package/dist/elements/Table.js.map +1 -1
  46. package/dist/elements/TableCell.d.ts +17 -1
  47. package/dist/elements/TableCell.d.ts.map +1 -1
  48. package/dist/elements/TableCell.js +61 -8
  49. package/dist/elements/TableCell.js.map +1 -1
  50. package/dist/elements/TableRow.d.ts +29 -0
  51. package/dist/elements/TableRow.d.ts.map +1 -1
  52. package/dist/elements/TableRow.js +106 -0
  53. package/dist/elements/TableRow.js.map +1 -1
  54. package/dist/elements/TextBox.d.ts +77 -0
  55. package/dist/elements/TextBox.d.ts.map +1 -0
  56. package/dist/elements/TextBox.js +440 -0
  57. package/dist/elements/TextBox.js.map +1 -0
  58. package/dist/formatting/Style.d.ts +100 -0
  59. package/dist/formatting/Style.d.ts.map +1 -1
  60. package/dist/formatting/Style.js +396 -1
  61. package/dist/formatting/Style.js.map +1 -1
  62. package/dist/formatting/StylesManager.d.ts +6 -0
  63. package/dist/formatting/StylesManager.d.ts.map +1 -1
  64. package/dist/formatting/StylesManager.js +50 -0
  65. package/dist/formatting/StylesManager.js.map +1 -1
  66. package/dist/index.d.ts +4 -1
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +10 -2
  69. package/dist/index.js.map +1 -1
  70. package/dist/managers/DrawingManager.d.ts +42 -0
  71. package/dist/managers/DrawingManager.d.ts.map +1 -0
  72. package/dist/managers/DrawingManager.js +160 -0
  73. package/dist/managers/DrawingManager.js.map +1 -0
  74. package/dist/xml/XMLBuilder.d.ts +2 -0
  75. package/dist/xml/XMLBuilder.d.ts.map +1 -1
  76. package/dist/xml/XMLBuilder.js +14 -0
  77. package/dist/xml/XMLBuilder.js.map +1 -1
  78. package/dist/xml/XMLParser.d.ts +1 -0
  79. package/dist/xml/XMLParser.d.ts.map +1 -1
  80. package/dist/xml/XMLParser.js +30 -0
  81. package/dist/xml/XMLParser.js.map +1 -1
  82. package/package.json +2 -2
package/README.md CHANGED
@@ -1,38 +1,29 @@
1
1
  # docXMLater - Professional DOCX Framework
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/docxmlater.svg)](https://www.npmjs.com/package/docxmlater)
4
- [![Tests](https://img.shields.io/badge/tests-596%20passing-brightgreen)](https://github.com/ItMeDiaTech/docXMLater)
4
+ [![Tests](https://img.shields.io/badge/tests-1098%20passing-brightgreen)](https://github.com/ItMeDiaTech/docXMLater)
5
5
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue)](https://www.typescriptlang.org/)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
8
  A comprehensive, production-ready TypeScript/JavaScript library for creating, reading, and manipulating Microsoft Word (.docx) documents programmatically. Full OpenXML compliance with extensive API coverage and **100% test pass rate**.
9
9
 
10
- I do a lot of professional documentation work. From the solutions that exist out there for working with .docx files and therefore .xml files, they are not amazing. Most of the frameworks that exist that do give you everything you want... charge thousands a year. I decided to make my own framework to interact with these filetypes and focus on ease of usability. All functionality works with helper functions to interact with all aspects of a .docx / xml document.
10
+ Built for professional documentation work, docXMLater provides a complete solution for programmatic DOCX manipulation with an intuitive API and helper functions for all aspects of document creation and modification.
11
11
 
12
- ## Latest Updates - v0.29.0
12
+ ## Latest Updates - v1.0.0
13
13
 
14
- **100% Test Coverage Achieved!** Major bug fix release with 44 critical fixes:
14
+ **Production Release!** All major features complete:
15
15
 
16
- ### Critical Fixes
17
- - **Text Parsing:** Fixed complete text loss during document load/save cycles
18
- - **Hyperlink Support:** Implemented full hyperlink parsing and relationship preservation
19
- - **Table Parsing:** Implemented complete table structure parsing (was returning null)
20
- - **XML Escaping:** Fixed 3 broken escape functions causing corruption with special characters
21
- - **Document Parts API:** Fixed Buffer/string type conversion for text files
22
- - **Conflict Resolution:** Auto-resolve pageBreakBefore + keepNext/keepLines conflicts
23
- - **XML Validation:** Added self-closing tag validation to prevent Word corruption
24
- - **Element Order:** Preserve correct paragraph/table order through load/save cycles
16
+ ### What's New in v1.0.0
25
17
 
26
- ### What's Fixed
27
- - Text preservation through save/load cycles (including Unicode, emoji, special chars)
28
- - Hyperlink parsing with formatting, tooltips, and relationship IDs
29
- - Table parsing and element order preservation
30
- - XML entity escaping in document properties
31
- - Boolean property XML generation (w:b, w:i, w:keepNext, etc.)
32
- - Section default values (columns, type)
33
- - Whitespace preservation with xml:space="preserve"
18
+ - **Complete Feature Set:** All 102 major features implemented
19
+ - **Table Styles:** Full support with 12 conditional formatting types
20
+ - **Content Controls:** 9 control types (rich text, plain text, combo box, dropdown, date picker, checkbox, picture, building block, group)
21
+ - **Field Types:** 11 field types (PAGE, NUMPAGES, DATE, TIME, FILENAME, AUTHOR, TITLE, REF, HYPERLINK, SEQ, TC/XE)
22
+ - **Drawing Elements:** Shapes and textboxes with full positioning
23
+ - **Document Properties:** Core, extended, and custom properties
24
+ - **Production Ready:** Full ECMA-376 compliance, zero regressions
34
25
 
35
- **Test Results:** 596/596 tests passing (100% - up from 92.7%)
26
+ **Test Results:** 1,098/1,098 tests passing (100% - exceeding v1.0 goal by 29%)
36
27
 
37
28
  ## Quick Start
38
29
 
@@ -67,27 +58,27 @@ await doc.save("output.docx");
67
58
 
68
59
  ### Content Creation
69
60
 
70
- | Method | Description | Example |
71
- | -------------------------------- | ---------------------- | -------------------------------- |
72
- | `createParagraph(text?)` | Add paragraph | `doc.createParagraph('Text')` |
73
- | `createTable(rows, cols)` | Add table | `doc.createTable(3, 4)` |
74
- | `addParagraph(para)` | Add existing paragraph | `doc.addParagraph(myPara)` |
75
- | `addTable(table)` | Add existing table | `doc.addTable(myTable)` |
76
- | `addImage(image)` | Add image | `doc.addImage(myImage)` |
77
- | `addTableOfContents(toc?)` | Add TOC | `doc.addTableOfContents()` |
78
- | `insertParagraphAt(index, para)` | Insert at position | `doc.insertParagraphAt(0, para)` |
61
+ | Method | Description | Example |
62
+ | -------------------------------- | ------------------------ | -------------------------------- |
63
+ | `createParagraph(text?)` | Add paragraph | `doc.createParagraph('Text')` |
64
+ | `createTable(rows, cols)` | Add table | `doc.createTable(3, 4)` |
65
+ | `addParagraph(para)` | Add existing paragraph | `doc.addParagraph(myPara)` |
66
+ | `addTable(table)` | Add existing table | `doc.addTable(myTable)` |
67
+ | `addImage(image)` | Add image | `doc.addImage(myImage)` |
68
+ | `addTableOfContents(toc?)` | Add TOC | `doc.addTableOfContents()` |
69
+ | `insertParagraphAt(index, para)` | Insert at position | `doc.insertParagraphAt(0, para)` |
79
70
  | `insertTableAt(index, table)` | Insert table at position | `doc.insertTableAt(5, table)` |
80
- | `insertTocAt(index, toc)` | Insert TOC at position | `doc.insertTocAt(0, toc)` |
71
+ | `insertTocAt(index, toc)` | Insert TOC at position | `doc.insertTocAt(0, toc)` |
81
72
 
82
73
  ### Content Manipulation
83
74
 
84
- | Method | Description | Returns |
85
- | ------------------------------------- | --------------------------- | --------- |
86
- | `replaceParagraphAt(index, para)` | Replace paragraph | `boolean` |
87
- | `replaceTableAt(index, table)` | Replace table | `boolean` |
88
- | `moveElement(fromIndex, toIndex)` | Move element to new position | `boolean` |
89
- | `swapElements(index1, index2)` | Swap two elements | `boolean` |
90
- | `removeTocAt(index)` | Remove TOC element | `boolean` |
75
+ | Method | Description | Returns |
76
+ | --------------------------------- | ---------------------------- | --------- |
77
+ | `replaceParagraphAt(index, para)` | Replace paragraph | `boolean` |
78
+ | `replaceTableAt(index, table)` | Replace table | `boolean` |
79
+ | `moveElement(fromIndex, toIndex)` | Move element to new position | `boolean` |
80
+ | `swapElements(index1, index2)` | Swap two elements | `boolean` |
81
+ | `removeTocAt(index)` | Remove TOC element | `boolean` |
91
82
 
92
83
  ### Content Retrieval
93
84
 
@@ -120,20 +111,21 @@ await doc.save("output.docx");
120
111
 
121
112
  ### Style Application
122
113
 
123
- | Method | Description | Returns |
124
- | -------------------------------------- | ------------------------------- | -------------- |
125
- | `applyStyleToAll(styleId, predicate)` | Apply style to matching elements | `number` |
126
- | `findElementsByStyle(styleId)` | Find all elements using a style | `Array<Para\|Cell>` |
114
+ | Method | Description | Returns |
115
+ | ------------------------------------- | -------------------------------- | ------------------- |
116
+ | `applyStyleToAll(styleId, predicate)` | Apply style to matching elements | `number` |
117
+ | `findElementsByStyle(styleId)` | Find all elements using a style | `Array<Para\|Cell>` |
127
118
 
128
119
  **Example:**
120
+
129
121
  ```typescript
130
122
  // Apply Heading1 to all paragraphs containing "Chapter"
131
- const count = doc.applyStyleToAll('Heading1', (el) => {
132
- return el instanceof Paragraph && el.getText().includes('Chapter');
123
+ const count = doc.applyStyleToAll("Heading1", (el) => {
124
+ return el instanceof Paragraph && el.getText().includes("Chapter");
133
125
  });
134
126
 
135
127
  // Find all Heading1 elements
136
- const headings = doc.findElementsByStyle('Heading1');
128
+ const headings = doc.findElementsByStyle("Heading1");
137
129
  ```
138
130
 
139
131
  ### Document Statistics
@@ -179,7 +171,10 @@ const para2 = Paragraph.create("Hello World");
179
171
  const para3 = Paragraph.create("Centered text", { alignment: "center" });
180
172
 
181
173
  // Create with just formatting
182
- const para4 = Paragraph.create({ alignment: "right", spacing: { before: 240 } });
174
+ const para4 = Paragraph.create({
175
+ alignment: "right",
176
+ spacing: { before: 240 },
177
+ });
183
178
 
184
179
  // Create with style
185
180
  const heading = Paragraph.createWithStyle("Chapter 1", "Heading1");
@@ -198,13 +193,13 @@ doc.addParagraph(heading);
198
193
 
199
194
  #### Paragraph Factory Methods
200
195
 
201
- | Method | Description | Example |
202
- | ----------------------------------------------------- | ------------------------------ | --------------------------------------- |
203
- | `Paragraph.create(text?, formatting?)` | Create detached paragraph | `Paragraph.create('Text')` |
204
- | `Paragraph.create(formatting?)` | Create with formatting only | `Paragraph.create({alignment: 'left'})` |
205
- | `Paragraph.createWithStyle(text, styleId)` | Create with style | `Paragraph.createWithStyle('', 'H1')` |
206
- | `Paragraph.createEmpty()` | Create empty paragraph | `Paragraph.createEmpty()` |
207
- | `Paragraph.createFormatted(text, run?, paragraph?)` | Create with dual formatting | See example above |
196
+ | Method | Description | Example |
197
+ | --------------------------------------------------- | --------------------------- | --------------------------------------- |
198
+ | `Paragraph.create(text?, formatting?)` | Create detached paragraph | `Paragraph.create('Text')` |
199
+ | `Paragraph.create(formatting?)` | Create with formatting only | `Paragraph.create({alignment: 'left'})` |
200
+ | `Paragraph.createWithStyle(text, styleId)` | Create with style | `Paragraph.createWithStyle('', 'H1')` |
201
+ | `Paragraph.createEmpty()` | Create empty paragraph | `Paragraph.createEmpty()` |
202
+ | `Paragraph.createFormatted(text, run?, paragraph?)` | Create with dual formatting | See example above |
208
203
 
209
204
  #### Paragraph Formatting Methods
210
205
 
@@ -224,54 +219,56 @@ doc.addParagraph(heading);
224
219
 
225
220
  #### Paragraph Manipulation Methods
226
221
 
227
- | Method | Description | Returns |
228
- | ------------------------------------ | --------------------------- | ------------ |
229
- | `insertRunAt(index, run)` | Insert run at position | `this` |
230
- | `removeRunAt(index)` | Remove run at position | `boolean` |
231
- | `replaceRunAt(index, run)` | Replace run at position | `boolean` |
232
- | `findText(text, options?)` | Find text in runs | `number[]` |
233
- | `replaceText(find, replace, options?)` | Replace text in runs | `number` |
234
- | `mergeWith(otherPara)` | Merge another paragraph | `this` |
235
- | `clone()` | Clone paragraph | `Paragraph` |
222
+ | Method | Description | Returns |
223
+ | -------------------------------------- | ----------------------- | ----------- |
224
+ | `insertRunAt(index, run)` | Insert run at position | `this` |
225
+ | `removeRunAt(index)` | Remove run at position | `boolean` |
226
+ | `replaceRunAt(index, run)` | Replace run at position | `boolean` |
227
+ | `findText(text, options?)` | Find text in runs | `number[]` |
228
+ | `replaceText(find, replace, options?)` | Replace text in runs | `number` |
229
+ | `mergeWith(otherPara)` | Merge another paragraph | `this` |
230
+ | `clone()` | Clone paragraph | `Paragraph` |
236
231
 
237
232
  **Example:**
233
+
238
234
  ```typescript
239
- const para = doc.createParagraph('Hello World');
235
+ const para = doc.createParagraph("Hello World");
240
236
 
241
237
  // Find and replace
242
- const indices = para.findText('World'); // [1]
243
- const count = para.replaceText('World', 'Universe', { caseSensitive: true });
238
+ const indices = para.findText("World"); // [1]
239
+ const count = para.replaceText("World", "Universe", { caseSensitive: true });
244
240
 
245
241
  // Manipulate runs
246
- para.insertRunAt(0, new Run('Start: ', { bold: true }));
247
- para.replaceRunAt(1, new Run('HELLO', { allCaps: true }));
242
+ para.insertRunAt(0, new Run("Start: ", { bold: true }));
243
+ para.replaceRunAt(1, new Run("HELLO", { allCaps: true }));
248
244
 
249
245
  // Merge paragraphs
250
- const para2 = Paragraph.create(' More text');
251
- para.mergeWith(para2); // Combines runs
246
+ const para2 = Paragraph.create(" More text");
247
+ para.mergeWith(para2); // Combines runs
252
248
  ```
253
249
 
254
250
  ### Run (Text Span) Operations
255
251
 
256
- | Method | Description | Returns |
257
- | ----------------------------- | ---------------------------- | -------- |
258
- | `clone()` | Clone run with formatting | `Run` |
259
- | `insertText(index, text)` | Insert text at position | `this` |
260
- | `appendText(text)` | Append text to end | `this` |
261
- | `replaceText(start, end, text)` | Replace text range | `this` |
252
+ | Method | Description | Returns |
253
+ | ------------------------------- | ------------------------- | ------- |
254
+ | `clone()` | Clone run with formatting | `Run` |
255
+ | `insertText(index, text)` | Insert text at position | `this` |
256
+ | `appendText(text)` | Append text to end | `this` |
257
+ | `replaceText(start, end, text)` | Replace text range | `this` |
262
258
 
263
259
  **Example:**
260
+
264
261
  ```typescript
265
- const run = new Run('Hello World', { bold: true });
262
+ const run = new Run("Hello World", { bold: true });
266
263
 
267
264
  // Text manipulation
268
- run.insertText(6, 'Beautiful '); // "Hello Beautiful World"
269
- run.appendText('!'); // "Hello Beautiful World!"
270
- run.replaceText(0, 5, 'Hi'); // "Hi Beautiful World!"
265
+ run.insertText(6, "Beautiful "); // "Hello Beautiful World"
266
+ run.appendText("!"); // "Hello Beautiful World!"
267
+ run.replaceText(0, 5, "Hi"); // "Hi Beautiful World!"
271
268
 
272
269
  // Clone for reuse
273
270
  const copy = run.clone();
274
- copy.setColor('FF0000'); // Original unchanged
271
+ copy.setColor("FF0000"); // Original unchanged
275
272
  ```
276
273
 
277
274
  ### Table Operations
@@ -291,19 +288,20 @@ copy.setColor('FF0000'); // Original unchanged
291
288
 
292
289
  #### Advanced Table Operations
293
290
 
294
- | Method | Description | Returns |
295
- | ------------------------------------------- | ----------------------------- | ------------- |
296
- | `mergeCells(startRow, startCol, endRow, endCol)` | Merge cells | `this` |
297
- | `splitCell(row, col)` | Remove cell spanning | `this` |
298
- | `moveCell(fromRow, fromCol, toRow, toCol)` | Move cell contents | `this` |
299
- | `swapCells(row1, col1, row2, col2)` | Swap two cells | `this` |
300
- | `setColumnWidth(index, width)` | Set specific column width | `this` |
301
- | `setColumnWidths(widths)` | Set all column widths | `this` |
302
- | `insertRows(startIndex, count)` | Insert multiple rows | `TableRow[]` |
303
- | `removeRows(startIndex, count)` | Remove multiple rows | `boolean` |
304
- | `clone()` | Clone entire table | `Table` |
291
+ | Method | Description | Returns |
292
+ | ------------------------------------------------ | ------------------------- | ------------ |
293
+ | `mergeCells(startRow, startCol, endRow, endCol)` | Merge cells | `this` |
294
+ | `splitCell(row, col)` | Remove cell spanning | `this` |
295
+ | `moveCell(fromRow, fromCol, toRow, toCol)` | Move cell contents | `this` |
296
+ | `swapCells(row1, col1, row2, col2)` | Swap two cells | `this` |
297
+ | `setColumnWidth(index, width)` | Set specific column width | `this` |
298
+ | `setColumnWidths(widths)` | Set all column widths | `this` |
299
+ | `insertRows(startIndex, count)` | Insert multiple rows | `TableRow[]` |
300
+ | `removeRows(startIndex, count)` | Remove multiple rows | `boolean` |
301
+ | `clone()` | Clone entire table | `Table` |
305
302
 
306
303
  **Example:**
304
+
307
305
  ```typescript
308
306
  const table = doc.createTable(3, 3);
309
307
 
@@ -317,12 +315,12 @@ table.moveCell(1, 1, 2, 2);
317
315
  table.swapCells(0, 0, 2, 2);
318
316
 
319
317
  // Batch row operations
320
- table.insertRows(1, 3); // Insert 3 rows at position 1
321
- table.removeRows(4, 2); // Remove 2 rows starting at position 4
318
+ table.insertRows(1, 3); // Insert 3 rows at position 1
319
+ table.removeRows(4, 2); // Remove 2 rows starting at position 4
322
320
 
323
321
  // Set column widths
324
- table.setColumnWidth(0, 2000); // First column = 2000 twips
325
- table.setColumnWidths([2000, 3000, 2000]); // All columns
322
+ table.setColumnWidth(0, 2000); // First column = 2000 twips
323
+ table.setColumnWidths([2000, 3000, 2000]); // All columns
326
324
 
327
325
  // Clone table for reuse
328
326
  const tableCopy = table.clone();
@@ -353,27 +351,28 @@ const tableCopy = table.clone();
353
351
 
354
352
  #### Style Manipulation
355
353
 
356
- | Method | Description | Returns |
357
- | ----------------------- | -------------------------------- | --------- |
358
- | `style.clone()` | Clone style | `Style` |
359
- | `style.mergeWith(other)` | Merge properties from another style | `this` |
354
+ | Method | Description | Returns |
355
+ | ------------------------ | ----------------------------------- | ------- |
356
+ | `style.clone()` | Clone style | `Style` |
357
+ | `style.mergeWith(other)` | Merge properties from another style | `this` |
360
358
 
361
359
  **Example:**
360
+
362
361
  ```typescript
363
362
  // Clone a style
364
- const heading1 = doc.getStyle('Heading1');
363
+ const heading1 = doc.getStyle("Heading1");
365
364
  const customHeading = heading1.clone();
366
- customHeading.setRunFormatting({ color: 'FF0000' });
365
+ customHeading.setRunFormatting({ color: "FF0000" });
367
366
 
368
367
  // Merge styles
369
368
  const baseStyle = Style.createNormalStyle();
370
369
  const overrideStyle = Style.create({
371
- styleId: 'Override',
372
- name: 'Override',
373
- type: 'paragraph',
374
- runFormatting: { bold: true, color: 'FF0000' }
370
+ styleId: "Override",
371
+ name: "Override",
372
+ type: "paragraph",
373
+ runFormatting: { bold: true, color: "FF0000" },
375
374
  });
376
- baseStyle.mergeWith(overrideStyle); // baseStyle now has bold red text
375
+ baseStyle.mergeWith(overrideStyle); // baseStyle now has bold red text
377
376
  ```
378
377
 
379
378
  #### Built-in Styles
@@ -396,90 +395,94 @@ baseStyle.mergeWith(overrideStyle); // baseStyle now has bold red text
396
395
 
397
396
  #### Basic TOC Creation
398
397
 
399
- | Method | Description | Example |
400
- | -------------------------------- | ---------------------------- | ------------------------------------------ |
401
- | `addTableOfContents(toc?)` | Add TOC to document | `doc.addTableOfContents()` |
402
- | `insertTocAt(index, toc)` | Insert TOC at position | `doc.insertTocAt(0, toc)` |
403
- | `removeTocAt(index)` | Remove TOC at position | `doc.removeTocAt(0)` |
398
+ | Method | Description | Example |
399
+ | -------------------------- | ---------------------- | -------------------------- |
400
+ | `addTableOfContents(toc?)` | Add TOC to document | `doc.addTableOfContents()` |
401
+ | `insertTocAt(index, toc)` | Insert TOC at position | `doc.insertTocAt(0, toc)` |
402
+ | `removeTocAt(index)` | Remove TOC at position | `doc.removeTocAt(0)` |
404
403
 
405
404
  #### TOC Factory Methods
406
405
 
407
- | Method | Description | Example |
408
- | ------------------------------------------- | -------------------------------- | ----------------------------------------------- |
409
- | `TableOfContents.createStandard(title?)` | Standard TOC (3 levels) | `TableOfContents.createStandard()` |
410
- | `TableOfContents.createSimple(title?)` | Simple TOC (2 levels) | `TableOfContents.createSimple()` |
411
- | `TableOfContents.createDetailed(title?)` | Detailed TOC (4 levels) | `TableOfContents.createDetailed()` |
412
- | `TableOfContents.createHyperlinked(title?)` | Hyperlinked TOC | `TableOfContents.createHyperlinked()` |
413
- | `TableOfContents.createWithStyles(styles, opts?)` | TOC with specific styles | `TableOfContents.createWithStyles(['H1','H3'])` |
414
- | `TableOfContents.createFlat(title?, styles?)` | Flat TOC (no indent) | `TableOfContents.createFlat()` |
415
- | `TableOfContents.createNumbered(title?, format?)` | Numbered TOC | `TableOfContents.createNumbered('TOC', 'roman')` |
416
- | `TableOfContents.createWithSpacing(spacing, opts?)` | TOC with custom spacing | `TableOfContents.createWithSpacing(120)` |
417
- | `TableOfContents.createWithHyperlinkColor(color, opts?)` | Custom hyperlink color | `TableOfContents.createWithHyperlinkColor('FF0000')` |
406
+ | Method | Description | Example |
407
+ | -------------------------------------------------------- | ------------------------ | ---------------------------------------------------- |
408
+ | `TableOfContents.createStandard(title?)` | Standard TOC (3 levels) | `TableOfContents.createStandard()` |
409
+ | `TableOfContents.createSimple(title?)` | Simple TOC (2 levels) | `TableOfContents.createSimple()` |
410
+ | `TableOfContents.createDetailed(title?)` | Detailed TOC (4 levels) | `TableOfContents.createDetailed()` |
411
+ | `TableOfContents.createHyperlinked(title?)` | Hyperlinked TOC | `TableOfContents.createHyperlinked()` |
412
+ | `TableOfContents.createWithStyles(styles, opts?)` | TOC with specific styles | `TableOfContents.createWithStyles(['H1','H3'])` |
413
+ | `TableOfContents.createFlat(title?, styles?)` | Flat TOC (no indent) | `TableOfContents.createFlat()` |
414
+ | `TableOfContents.createNumbered(title?, format?)` | Numbered TOC | `TableOfContents.createNumbered('TOC', 'roman')` |
415
+ | `TableOfContents.createWithSpacing(spacing, opts?)` | TOC with custom spacing | `TableOfContents.createWithSpacing(120)` |
416
+ | `TableOfContents.createWithHyperlinkColor(color, opts?)` | Custom hyperlink color | `TableOfContents.createWithHyperlinkColor('FF0000')` |
418
417
 
419
418
  #### TOC Configuration Methods
420
419
 
421
- | Method | Description | Values |
422
- | ----------------------------------- | --------------------------------- | ----------------------------------- |
423
- | `setIncludeStyles(styles)` | Select specific heading styles | `['Heading1', 'Heading3']` |
424
- | `setNumbered(numbered, format?)` | Enable/disable numbering | `(true, 'roman')` |
425
- | `setNoIndent(noIndent)` | Remove indentation | `true/false` |
426
- | `setCustomIndents(indents)` | Custom indents per level | `[0, 200, 400]` (twips) |
427
- | `setSpaceBetweenEntries(spacing)` | Spacing between entries | `120` (twips) |
428
- | `setHyperlinkColor(color)` | Hyperlink color | `'0000FF'` (default blue) |
429
- | `configure(options)` | Bulk configuration | See example below |
420
+ | Method | Description | Values |
421
+ | --------------------------------- | ------------------------------ | -------------------------- |
422
+ | `setIncludeStyles(styles)` | Select specific heading styles | `['Heading1', 'Heading3']` |
423
+ | `setNumbered(numbered, format?)` | Enable/disable numbering | `(true, 'roman')` |
424
+ | `setNoIndent(noIndent)` | Remove indentation | `true/false` |
425
+ | `setCustomIndents(indents)` | Custom indents per level | `[0, 200, 400]` (twips) |
426
+ | `setSpaceBetweenEntries(spacing)` | Spacing between entries | `120` (twips) |
427
+ | `setHyperlinkColor(color)` | Hyperlink color | `'0000FF'` (default blue) |
428
+ | `configure(options)` | Bulk configuration | See example below |
430
429
 
431
430
  #### TOC Properties
432
431
 
433
- | Property | Type | Default | Description |
434
- | -------------------- | ------------------------------ | ------------ | ------------------------------------- |
435
- | `title` | `string` | `'Table of Contents'` | TOC title |
436
- | `levels` | `number` (1-9) | `3` | Heading levels to include |
437
- | `includeStyles` | `string[]` | `undefined` | Specific styles (overrides levels) |
438
- | `showPageNumbers` | `boolean` | `true` | Show page numbers |
439
- | `useHyperlinks` | `boolean` | `false` | Use hyperlinks instead of page #s |
440
- | `numbered` | `boolean` | `false` | Number TOC entries |
441
- | `numberingFormat` | `'decimal'/'roman'/'alpha'` | `'decimal'` | Numbering format |
442
- | `noIndent` | `boolean` | `false` | Remove all indentation |
443
- | `customIndents` | `number[]` | `undefined` | Custom indents in twips |
444
- | `spaceBetweenEntries`| `number` | `0` | Spacing in twips |
445
- | `hyperlinkColor` | `string` | `'0000FF'` | Hyperlink color (hex without #) |
446
- | `tabLeader` | `'dot'/'hyphen'/'underscore'/'none'` | `'dot'` | Tab leader character |
432
+ | Property | Type | Default | Description |
433
+ | --------------------- | ------------------------------------ | --------------------- | ---------------------------------- |
434
+ | `title` | `string` | `'Table of Contents'` | TOC title |
435
+ | `levels` | `number` (1-9) | `3` | Heading levels to include |
436
+ | `includeStyles` | `string[]` | `undefined` | Specific styles (overrides levels) |
437
+ | `showPageNumbers` | `boolean` | `true` | Show page numbers |
438
+ | `useHyperlinks` | `boolean` | `false` | Use hyperlinks instead of page #s |
439
+ | `numbered` | `boolean` | `false` | Number TOC entries |
440
+ | `numberingFormat` | `'decimal'/'roman'/'alpha'` | `'decimal'` | Numbering format |
441
+ | `noIndent` | `boolean` | `false` | Remove all indentation |
442
+ | `customIndents` | `number[]` | `undefined` | Custom indents in twips |
443
+ | `spaceBetweenEntries` | `number` | `0` | Spacing in twips |
444
+ | `hyperlinkColor` | `string` | `'0000FF'` | Hyperlink color (hex without #) |
445
+ | `tabLeader` | `'dot'/'hyphen'/'underscore'/'none'` | `'dot'` | Tab leader character |
447
446
 
448
447
  **Example:**
448
+
449
449
  ```typescript
450
450
  // Basic TOC
451
451
  const simpleToc = TableOfContents.createStandard();
452
452
  doc.addTableOfContents(simpleToc);
453
453
 
454
454
  // Select specific styles (e.g., only Heading1 and Heading3)
455
- const customToc = TableOfContents.createWithStyles(['Heading1', 'Heading3']);
455
+ const customToc = TableOfContents.createWithStyles(["Heading1", "Heading3"]);
456
456
 
457
457
  // Flat TOC with no indentation
458
- const flatToc = TableOfContents.createFlat('Contents');
458
+ const flatToc = TableOfContents.createFlat("Contents");
459
459
 
460
460
  // Numbered TOC with roman numerals
461
- const numberedToc = TableOfContents.createNumbered('Table of Contents', 'roman');
461
+ const numberedToc = TableOfContents.createNumbered(
462
+ "Table of Contents",
463
+ "roman"
464
+ );
462
465
 
463
466
  // Custom hyperlink color (red instead of blue)
464
- const coloredToc = TableOfContents.createWithHyperlinkColor('FF0000');
467
+ const coloredToc = TableOfContents.createWithHyperlinkColor("FF0000");
465
468
 
466
469
  // Advanced configuration
467
470
  const toc = TableOfContents.create()
468
- .setIncludeStyles(['Heading1', 'Heading2', 'Heading3'])
469
- .setNumbered(true, 'decimal')
470
- .setSpaceBetweenEntries(120) // 6pt spacing
471
- .setHyperlinkColor('0000FF')
471
+ .setIncludeStyles(["Heading1", "Heading2", "Heading3"])
472
+ .setNumbered(true, "decimal")
473
+ .setSpaceBetweenEntries(120) // 6pt spacing
474
+ .setHyperlinkColor("0000FF")
472
475
  .setNoIndent(false);
473
476
 
474
477
  // Or use configure() for bulk settings
475
478
  toc.configure({
476
- title: 'Table of Contents',
477
- includeStyles: ['Heading1', 'CustomHeader'],
479
+ title: "Table of Contents",
480
+ includeStyles: ["Heading1", "CustomHeader"],
478
481
  numbered: true,
479
- numberingFormat: 'alpha',
482
+ numberingFormat: "alpha",
480
483
  spaceBetweenEntries: 100,
481
- hyperlinkColor: 'FF0000',
482
- noIndent: true
484
+ hyperlinkColor: "FF0000",
485
+ noIndent: true,
483
486
  });
484
487
 
485
488
  // Insert at specific position
@@ -586,13 +589,104 @@ doc.insertTocAt(0, toc);
586
589
 
587
590
  ### Unit Conversion Utilities
588
591
 
589
- | Function | Description | Example |
590
- | ---------------------------- | -------------------- | --------------------------- |
591
- | `inchesToTwips(inches)` | Inches to twips | `inchesToTwips(1)` // 1440 |
592
- | `inchesToEmus(inches)` | Inches to EMUs | `inchesToEmus(1)` // 914400 |
593
- | `cmToTwips(cm)` | Centimeters to twips | `cmToTwips(2.54)` // 1440 |
594
- | `pointsToTwips(points)` | Points to twips | `pointsToTwips(12)` // 240 |
595
- | `pixelsToEmus(pixels, dpi?)` | Pixels to EMUs | `pixelsToEmus(96)` |
592
+ #### Twips Conversions
593
+ | Function | Description | Example |
594
+ | ------------------------- | ------------------- | --------------------------- |
595
+ | `twipsToPoints(twips)` | Twips to points | `twipsToPoints(240)` // 12 |
596
+ | `twipsToInches(twips)` | Twips to inches | `twipsToInches(1440)` // 1 |
597
+ | `twipsToCm(twips)` | Twips to cm | `twipsToCm(1440)` // 2.54 |
598
+ | `twipsToEmus(twips)` | Twips to EMUs | `twipsToEmus(1440)` |
599
+
600
+ #### EMUs (English Metric Units) Conversions
601
+ | Function | Description | Example |
602
+ | --------------------------- | -------------------- | ----------------------------- |
603
+ | `emusToTwips(emus)` | EMUs to twips | `emusToTwips(914400)` // 1440 |
604
+ | `emusToInches(emus)` | EMUs to inches | `emusToInches(914400)` // 1 |
605
+ | `emusToCm(emus)` | EMUs to cm | `emusToCm(914400)` // 2.54 |
606
+ | `emusToPoints(emus)` | EMUs to points | `emusToPoints(914400)` // 72 |
607
+ | `emusToPixels(emus, dpi?)` | EMUs to pixels | `emusToPixels(914400)` // 96 |
608
+
609
+ #### Points Conversions
610
+ | Function | Description | Example |
611
+ | ------------------------ | ------------------ | -------------------------- |
612
+ | `pointsToTwips(points)` | Points to twips | `pointsToTwips(12)` // 240 |
613
+ | `pointsToEmus(points)` | Points to EMUs | `pointsToEmus(72)` |
614
+ | `pointsToInches(points)` | Points to inches | `pointsToInches(72)` // 1 |
615
+ | `pointsToCm(points)` | Points to cm | `pointsToCm(72)` // 2.54 |
616
+
617
+ #### Inches Conversions
618
+ | Function | Description | Example |
619
+ | ----------------------------- | ------------------- | ----------------------------- |
620
+ | `inchesToTwips(inches)` | Inches to twips | `inchesToTwips(1)` // 1440 |
621
+ | `inchesToEmus(inches)` | Inches to EMUs | `inchesToEmus(1)` // 914400 |
622
+ | `inchesToPoints(inches)` | Inches to points | `inchesToPoints(1)` // 72 |
623
+ | `inchesToCm(inches)` | Inches to cm | `inchesToCm(1)` // 2.54 |
624
+ | `inchesToPixels(inches, dpi)` | Inches to pixels | `inchesToPixels(1, 96)` // 96 |
625
+
626
+ #### Centimeters Conversions
627
+ | Function | Description | Example |
628
+ | ----------------------- | ---------------- | --------------------------- |
629
+ | `cmToTwips(cm)` | cm to twips | `cmToTwips(2.54)` // 1440 |
630
+ | `cmToEmus(cm)` | cm to EMUs | `cmToEmus(2.54)` // 914400 |
631
+ | `cmToInches(cm)` | cm to inches | `cmToInches(2.54)` // 1 |
632
+ | `cmToPoints(cm)` | cm to points | `cmToPoints(2.54)` // 72 |
633
+ | `cmToPixels(cm, dpi?)` | cm to pixels | `cmToPixels(2.54, 96)` // 96|
634
+
635
+ #### Pixels Conversions
636
+ | Function | Description | Example |
637
+ | ---------------------------- | ------------------- | ------------------------------ |
638
+ | `pixelsToEmus(pixels, dpi?)` | Pixels to EMUs | `pixelsToEmus(96)` // 914400 |
639
+ | `pixelsToInches(pixels, dpi?)`| Pixels to inches | `pixelsToInches(96, 96)` // 1 |
640
+ | `pixelsToTwips(pixels, dpi?)`| Pixels to twips | `pixelsToTwips(96, 96)` // 1440|
641
+ | `pixelsToCm(pixels, dpi?)` | Pixels to cm | `pixelsToCm(96, 96)` // 2.54 |
642
+ | `pixelsToPoints(pixels, dpi?)`| Pixels to points | `pixelsToPoints(96, 96)` // 72 |
643
+
644
+ **Note:** Default DPI is 96 for pixel conversions
645
+
646
+ ### ZIP Archive Helper Methods
647
+
648
+ #### File Operations
649
+ | Method | Description | Example |
650
+ | ------------------------------- | ------------------------- | -------------------------------------------- |
651
+ | `addFile(path, content)` | Add file to archive | `handler.addFile('doc.xml', xmlContent)` |
652
+ | `updateFile(path, content)` | Update existing file | `handler.updateFile('doc.xml', newContent)` |
653
+ | `removeFile(path)` | Remove file from archive | `handler.removeFile('old.xml')` |
654
+ | `renameFile(oldPath, newPath)` | Rename file | `handler.renameFile('a.xml', 'b.xml')` |
655
+ | `copyFile(srcPath, destPath)` | Copy file | `handler.copyFile('a.xml', 'copy-a.xml')` |
656
+ | `moveFile(srcPath, destPath)` | Move file | `handler.moveFile('a.xml', 'folder/a.xml')` |
657
+
658
+ #### File Retrieval
659
+ | Method | Description | Returns |
660
+ | ------------------------- | ---------------------- | --------------- |
661
+ | `getFile(path)` | Get file object | `ZipFile` |
662
+ | `getFileAsString(path)` | Get file as string | `string` |
663
+ | `getFileAsBuffer(path)` | Get file as buffer | `Buffer` |
664
+ | `hasFile(path)` | Check if file exists | `boolean` |
665
+ | `getFilePaths()` | Get all file paths | `string[]` |
666
+ | `getAllFiles()` | Get all files | `FileMap` |
667
+
668
+ #### Batch Operations
669
+ | Method | Description | Returns |
670
+ | ------------------------------- | ---------------------------- | -------------- |
671
+ | `removeFiles(paths[])` | Remove multiple files | `number` |
672
+ | `getFilesByExtension(ext)` | Get files by extension | `ZipFile[]` |
673
+ | `getTextFiles()` | Get all text files | `ZipFile[]` |
674
+ | `getBinaryFiles()` | Get all binary files | `ZipFile[]` |
675
+ | `getMediaFiles()` | Get media files | `ZipFile[]` |
676
+
677
+ #### Archive Information
678
+ | Method | Description | Returns |
679
+ | ------------------ | ------------------------- | ------------------------ |
680
+ | `getFileCount()` | Count files in archive | `number` |
681
+ | `getTotalSize()` | Get total size in bytes | `number` |
682
+ | `getStats()` | Get detailed statistics | `{fileCount, size, ...}` |
683
+ | `isEmpty()` | Check if archive is empty | `boolean` |
684
+
685
+ #### Import/Export
686
+ | Method | Description | Returns |
687
+ | -------------------------------- | ------------------------ | -------------------- |
688
+ | `exportFile(internal, external)` | Export file from archive | `Promise<void>` |
689
+ | `importFile(external, internal)` | Import file to archive | `Promise<void>` |
596
690
 
597
691
  ## Common Recipes
598
692
 
@@ -984,11 +1078,11 @@ const run = new Run("Text<w:t>value</w:t>", { cleanXmlFromText: false });
984
1078
 
985
1079
  **The Right Approach**: Use the framework's API methods instead of embedding XML:
986
1080
 
987
- - Use `paragraph.addText()` multiple times for separate text runs
988
- - Use formatting options: `{bold: true}`, `{italic: true}`, etc.
989
- - Use `paragraph.addHyperlink()` for links
990
- - Don't pass XML strings to text methods
991
- - Don't try to embed `<w:t>` or other XML tags in your text
1081
+ - Use `paragraph.addText()` multiple times for separate text runs
1082
+ - Use formatting options: `{bold: true}`, `{italic: true}`, etc.
1083
+ - Use `paragraph.addHyperlink()` for links
1084
+ - Don't pass XML strings to text methods
1085
+ - Don't try to embed `<w:t>` or other XML tags in your text
992
1086
 
993
1087
  For more details, see the [corruption detection examples](examples/troubleshooting/).
994
1088
 
@@ -999,6 +1093,7 @@ For more details, see the [corruption detection examples](examples/troubleshooti
999
1093
  **Cause**: The `pageBreakBefore` property conflicting with `keepNext`/`keepLines` properties. When a paragraph has both `pageBreakBefore` and keep properties set to true, Word's layout engine tries to satisfy contradictory constraints (insert break vs. keep together), resulting in massive whitespace as it struggles to resolve the conflict.
1000
1094
 
1001
1095
  **Why This Causes Problems**:
1096
+
1002
1097
  - `pageBreakBefore` tells Word to insert a page break before the paragraph
1003
1098
  - `keepNext` tells Word to keep the paragraph with the next one (no break)
1004
1099
  - `keepLines` tells Word to keep all lines together (no break)
@@ -1012,13 +1107,14 @@ The framework now automatically prevents these conflicts by **prioritizing keep
1012
1107
  // When setting keepNext or keepLines, pageBreakBefore is automatically cleared
1013
1108
  const para = new Paragraph()
1014
1109
  .addText("Content")
1015
- .setPageBreakBefore(true) // Set to true
1016
- .setKeepNext(true); // Automatically clears pageBreakBefore
1110
+ .setPageBreakBefore(true) // Set to true
1111
+ .setKeepNext(true); // Automatically clears pageBreakBefore
1017
1112
 
1018
1113
  // Result: keepNext=true, pageBreakBefore=false (conflict resolved)
1019
1114
  ```
1020
1115
 
1021
1116
  **Why This Priority?**
1117
+
1022
1118
  - Keep properties (`keepNext`/`keepLines`) represent explicit user intent to keep content together
1023
1119
  - Page breaks are often layout hints that may conflict with document flow
1024
1120
  - Removing `pageBreakBefore` eliminates whitespace while preserving the user's intention
@@ -1036,6 +1132,7 @@ const doc = await Document.load("document-with-conflicts.docx");
1036
1132
  ```
1037
1133
 
1038
1134
  **How It Works**:
1135
+
1039
1136
  1. When `setKeepNext(true)` is called, `pageBreakBefore` is automatically set to `false`
1040
1137
  2. When `setKeepLines(true)` is called, `pageBreakBefore` is automatically set to `false`
1041
1138
  3. When parsing documents, if both properties exist, `pageBreakBefore` is cleared
@@ -1047,7 +1144,7 @@ If you need a page break despite keep properties, set it after:
1047
1144
 
1048
1145
  ```typescript
1049
1146
  const para = new Paragraph()
1050
- .setKeepNext(true) // Set first
1147
+ .setKeepNext(true) // Set first
1051
1148
  .setPageBreakBefore(true); // Override - you explicitly want this conflict
1052
1149
 
1053
1150
  // But note: This will cause layout issues (whitespace) in Word
@@ -1067,12 +1164,12 @@ Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md)
1067
1164
 
1068
1165
  **Critical Bug Fix Release:**
1069
1166
 
1070
- - **Fixed Paragraph.getText()** - Now includes hyperlink text content (critical data loss bug)
1071
- - **Added hyperlink integration tests** - 6 new comprehensive test cases
1072
- - **Enhanced test suite** - 474/478 tests passing (98.1% pass rate)
1073
- - **Fixed type safety** - XMLElement handling improvements across test files
1074
- - **Improved StylesManager** - XML corruption detection moved before parser
1075
- - **Hyperlink management** - Proper relationship ID clearing on URL updates
1167
+ - **Fixed Paragraph.getText()** - Now includes hyperlink text content (critical data loss bug)
1168
+ - **Added hyperlink integration tests** - 6 new comprehensive test cases
1169
+ - **Enhanced test suite** - 474/478 tests passing (98.1% pass rate)
1170
+ - **Fixed type safety** - XMLElement handling improvements across test files
1171
+ - **Improved StylesManager** - XML corruption detection moved before parser
1172
+ - **Hyperlink management** - Proper relationship ID clearing on URL updates
1076
1173
 
1077
1174
  **What This Fixes:**
1078
1175
  When using `para.addText('foo') + para.addHyperlink(link)`, the hyperlink text is now properly included in `paragraph.getText()`, preventing silent text loss.