docxmlater 0.31.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 (81) hide show
  1. package/README.md +277 -193
  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 +1311 -26
  13. package/dist/core/DocumentParser.js.map +1 -1
  14. package/dist/elements/Field.d.ts +7 -1
  15. package/dist/elements/Field.d.ts.map +1 -1
  16. package/dist/elements/Field.js +74 -0
  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 +3 -0
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +8 -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.map +1 -1
  79. package/dist/xml/XMLParser.js +7 -1
  80. package/dist/xml/XMLParser.js.map +1 -1
  81. package/package.json +2 -2
package/README.md CHANGED
@@ -1,51 +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-635%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.31.0
12
+ ## Latest Updates - v1.0.0
13
13
 
14
- **ComplexField Support & Critical Bug Fixes!** Major enhancement release:
14
+ **Production Release!** All major features complete:
15
15
 
16
- ### New Features
17
- - **ComplexField Support:** Full implementation of begin/separate/end field structure per ECMA-376
18
- - **TOC Field Generator:** `createTOCField()` with all switches (\o, \h, \z, \u, \n, \t)
19
- - **Advanced Fields:** Foundation for cross-references, indexes, and dynamic content
20
- - **Style Color Fix:** Fixed critical bug where hex colors were corrupted during load/save
16
+ ### What's New in v1.0.0
21
17
 
22
- ### What's New
23
- - `ComplexField` class for TOC, cross-references, and advanced field types
24
- - `createTOCField()` function with customizable options (levels, hyperlinks, styles)
25
- - Fixed style color parsing (colors no longer corrupted to size values)
26
- - New `XMLParser.extractSelfClosingTag()` for accurate XML parsing
27
- - 41 new comprehensive tests (StylesRoundTrip + ComplexField)
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
28
25
 
29
- ### Field Support
30
- ```typescript
31
- import { createTOCField, ComplexField } from 'docxmlater';
32
-
33
- // Create TOC with custom options
34
- const toc = createTOCField({
35
- levels: '1-3',
36
- hyperlinks: true,
37
- omitPageNumbers: false
38
- });
39
-
40
- // Create custom complex field
41
- const field = new ComplexField({
42
- instruction: ' PAGE \\* MERGEFORMAT ',
43
- result: '1',
44
- resultFormatting: { bold: true }
45
- });
46
- ```
47
-
48
- **Test Results:** 635/635 tests passing (100% - up from 596)
26
+ **Test Results:** 1,098/1,098 tests passing (100% - exceeding v1.0 goal by 29%)
49
27
 
50
28
  ## Quick Start
51
29
 
@@ -80,27 +58,27 @@ await doc.save("output.docx");
80
58
 
81
59
  ### Content Creation
82
60
 
83
- | Method | Description | Example |
84
- | -------------------------------- | ---------------------- | -------------------------------- |
85
- | `createParagraph(text?)` | Add paragraph | `doc.createParagraph('Text')` |
86
- | `createTable(rows, cols)` | Add table | `doc.createTable(3, 4)` |
87
- | `addParagraph(para)` | Add existing paragraph | `doc.addParagraph(myPara)` |
88
- | `addTable(table)` | Add existing table | `doc.addTable(myTable)` |
89
- | `addImage(image)` | Add image | `doc.addImage(myImage)` |
90
- | `addTableOfContents(toc?)` | Add TOC | `doc.addTableOfContents()` |
91
- | `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)` |
92
70
  | `insertTableAt(index, table)` | Insert table at position | `doc.insertTableAt(5, table)` |
93
- | `insertTocAt(index, toc)` | Insert TOC at position | `doc.insertTocAt(0, toc)` |
71
+ | `insertTocAt(index, toc)` | Insert TOC at position | `doc.insertTocAt(0, toc)` |
94
72
 
95
73
  ### Content Manipulation
96
74
 
97
- | Method | Description | Returns |
98
- | ------------------------------------- | --------------------------- | --------- |
99
- | `replaceParagraphAt(index, para)` | Replace paragraph | `boolean` |
100
- | `replaceTableAt(index, table)` | Replace table | `boolean` |
101
- | `moveElement(fromIndex, toIndex)` | Move element to new position | `boolean` |
102
- | `swapElements(index1, index2)` | Swap two elements | `boolean` |
103
- | `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` |
104
82
 
105
83
  ### Content Retrieval
106
84
 
@@ -133,20 +111,21 @@ await doc.save("output.docx");
133
111
 
134
112
  ### Style Application
135
113
 
136
- | Method | Description | Returns |
137
- | -------------------------------------- | ------------------------------- | -------------- |
138
- | `applyStyleToAll(styleId, predicate)` | Apply style to matching elements | `number` |
139
- | `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>` |
140
118
 
141
119
  **Example:**
120
+
142
121
  ```typescript
143
122
  // Apply Heading1 to all paragraphs containing "Chapter"
144
- const count = doc.applyStyleToAll('Heading1', (el) => {
145
- return el instanceof Paragraph && el.getText().includes('Chapter');
123
+ const count = doc.applyStyleToAll("Heading1", (el) => {
124
+ return el instanceof Paragraph && el.getText().includes("Chapter");
146
125
  });
147
126
 
148
127
  // Find all Heading1 elements
149
- const headings = doc.findElementsByStyle('Heading1');
128
+ const headings = doc.findElementsByStyle("Heading1");
150
129
  ```
151
130
 
152
131
  ### Document Statistics
@@ -192,7 +171,10 @@ const para2 = Paragraph.create("Hello World");
192
171
  const para3 = Paragraph.create("Centered text", { alignment: "center" });
193
172
 
194
173
  // Create with just formatting
195
- const para4 = Paragraph.create({ alignment: "right", spacing: { before: 240 } });
174
+ const para4 = Paragraph.create({
175
+ alignment: "right",
176
+ spacing: { before: 240 },
177
+ });
196
178
 
197
179
  // Create with style
198
180
  const heading = Paragraph.createWithStyle("Chapter 1", "Heading1");
@@ -211,13 +193,13 @@ doc.addParagraph(heading);
211
193
 
212
194
  #### Paragraph Factory Methods
213
195
 
214
- | Method | Description | Example |
215
- | ----------------------------------------------------- | ------------------------------ | --------------------------------------- |
216
- | `Paragraph.create(text?, formatting?)` | Create detached paragraph | `Paragraph.create('Text')` |
217
- | `Paragraph.create(formatting?)` | Create with formatting only | `Paragraph.create({alignment: 'left'})` |
218
- | `Paragraph.createWithStyle(text, styleId)` | Create with style | `Paragraph.createWithStyle('', 'H1')` |
219
- | `Paragraph.createEmpty()` | Create empty paragraph | `Paragraph.createEmpty()` |
220
- | `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 |
221
203
 
222
204
  #### Paragraph Formatting Methods
223
205
 
@@ -237,54 +219,56 @@ doc.addParagraph(heading);
237
219
 
238
220
  #### Paragraph Manipulation Methods
239
221
 
240
- | Method | Description | Returns |
241
- | ------------------------------------ | --------------------------- | ------------ |
242
- | `insertRunAt(index, run)` | Insert run at position | `this` |
243
- | `removeRunAt(index)` | Remove run at position | `boolean` |
244
- | `replaceRunAt(index, run)` | Replace run at position | `boolean` |
245
- | `findText(text, options?)` | Find text in runs | `number[]` |
246
- | `replaceText(find, replace, options?)` | Replace text in runs | `number` |
247
- | `mergeWith(otherPara)` | Merge another paragraph | `this` |
248
- | `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` |
249
231
 
250
232
  **Example:**
233
+
251
234
  ```typescript
252
- const para = doc.createParagraph('Hello World');
235
+ const para = doc.createParagraph("Hello World");
253
236
 
254
237
  // Find and replace
255
- const indices = para.findText('World'); // [1]
256
- const count = para.replaceText('World', 'Universe', { caseSensitive: true });
238
+ const indices = para.findText("World"); // [1]
239
+ const count = para.replaceText("World", "Universe", { caseSensitive: true });
257
240
 
258
241
  // Manipulate runs
259
- para.insertRunAt(0, new Run('Start: ', { bold: true }));
260
- 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 }));
261
244
 
262
245
  // Merge paragraphs
263
- const para2 = Paragraph.create(' More text');
264
- para.mergeWith(para2); // Combines runs
246
+ const para2 = Paragraph.create(" More text");
247
+ para.mergeWith(para2); // Combines runs
265
248
  ```
266
249
 
267
250
  ### Run (Text Span) Operations
268
251
 
269
- | Method | Description | Returns |
270
- | ----------------------------- | ---------------------------- | -------- |
271
- | `clone()` | Clone run with formatting | `Run` |
272
- | `insertText(index, text)` | Insert text at position | `this` |
273
- | `appendText(text)` | Append text to end | `this` |
274
- | `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` |
275
258
 
276
259
  **Example:**
260
+
277
261
  ```typescript
278
- const run = new Run('Hello World', { bold: true });
262
+ const run = new Run("Hello World", { bold: true });
279
263
 
280
264
  // Text manipulation
281
- run.insertText(6, 'Beautiful '); // "Hello Beautiful World"
282
- run.appendText('!'); // "Hello Beautiful World!"
283
- 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!"
284
268
 
285
269
  // Clone for reuse
286
270
  const copy = run.clone();
287
- copy.setColor('FF0000'); // Original unchanged
271
+ copy.setColor("FF0000"); // Original unchanged
288
272
  ```
289
273
 
290
274
  ### Table Operations
@@ -304,19 +288,20 @@ copy.setColor('FF0000'); // Original unchanged
304
288
 
305
289
  #### Advanced Table Operations
306
290
 
307
- | Method | Description | Returns |
308
- | ------------------------------------------- | ----------------------------- | ------------- |
309
- | `mergeCells(startRow, startCol, endRow, endCol)` | Merge cells | `this` |
310
- | `splitCell(row, col)` | Remove cell spanning | `this` |
311
- | `moveCell(fromRow, fromCol, toRow, toCol)` | Move cell contents | `this` |
312
- | `swapCells(row1, col1, row2, col2)` | Swap two cells | `this` |
313
- | `setColumnWidth(index, width)` | Set specific column width | `this` |
314
- | `setColumnWidths(widths)` | Set all column widths | `this` |
315
- | `insertRows(startIndex, count)` | Insert multiple rows | `TableRow[]` |
316
- | `removeRows(startIndex, count)` | Remove multiple rows | `boolean` |
317
- | `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` |
318
302
 
319
303
  **Example:**
304
+
320
305
  ```typescript
321
306
  const table = doc.createTable(3, 3);
322
307
 
@@ -330,12 +315,12 @@ table.moveCell(1, 1, 2, 2);
330
315
  table.swapCells(0, 0, 2, 2);
331
316
 
332
317
  // Batch row operations
333
- table.insertRows(1, 3); // Insert 3 rows at position 1
334
- 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
335
320
 
336
321
  // Set column widths
337
- table.setColumnWidth(0, 2000); // First column = 2000 twips
338
- table.setColumnWidths([2000, 3000, 2000]); // All columns
322
+ table.setColumnWidth(0, 2000); // First column = 2000 twips
323
+ table.setColumnWidths([2000, 3000, 2000]); // All columns
339
324
 
340
325
  // Clone table for reuse
341
326
  const tableCopy = table.clone();
@@ -366,27 +351,28 @@ const tableCopy = table.clone();
366
351
 
367
352
  #### Style Manipulation
368
353
 
369
- | Method | Description | Returns |
370
- | ----------------------- | -------------------------------- | --------- |
371
- | `style.clone()` | Clone style | `Style` |
372
- | `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` |
373
358
 
374
359
  **Example:**
360
+
375
361
  ```typescript
376
362
  // Clone a style
377
- const heading1 = doc.getStyle('Heading1');
363
+ const heading1 = doc.getStyle("Heading1");
378
364
  const customHeading = heading1.clone();
379
- customHeading.setRunFormatting({ color: 'FF0000' });
365
+ customHeading.setRunFormatting({ color: "FF0000" });
380
366
 
381
367
  // Merge styles
382
368
  const baseStyle = Style.createNormalStyle();
383
369
  const overrideStyle = Style.create({
384
- styleId: 'Override',
385
- name: 'Override',
386
- type: 'paragraph',
387
- runFormatting: { bold: true, color: 'FF0000' }
370
+ styleId: "Override",
371
+ name: "Override",
372
+ type: "paragraph",
373
+ runFormatting: { bold: true, color: "FF0000" },
388
374
  });
389
- baseStyle.mergeWith(overrideStyle); // baseStyle now has bold red text
375
+ baseStyle.mergeWith(overrideStyle); // baseStyle now has bold red text
390
376
  ```
391
377
 
392
378
  #### Built-in Styles
@@ -409,90 +395,94 @@ baseStyle.mergeWith(overrideStyle); // baseStyle now has bold red text
409
395
 
410
396
  #### Basic TOC Creation
411
397
 
412
- | Method | Description | Example |
413
- | -------------------------------- | ---------------------------- | ------------------------------------------ |
414
- | `addTableOfContents(toc?)` | Add TOC to document | `doc.addTableOfContents()` |
415
- | `insertTocAt(index, toc)` | Insert TOC at position | `doc.insertTocAt(0, toc)` |
416
- | `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)` |
417
403
 
418
404
  #### TOC Factory Methods
419
405
 
420
- | Method | Description | Example |
421
- | ------------------------------------------- | -------------------------------- | ----------------------------------------------- |
422
- | `TableOfContents.createStandard(title?)` | Standard TOC (3 levels) | `TableOfContents.createStandard()` |
423
- | `TableOfContents.createSimple(title?)` | Simple TOC (2 levels) | `TableOfContents.createSimple()` |
424
- | `TableOfContents.createDetailed(title?)` | Detailed TOC (4 levels) | `TableOfContents.createDetailed()` |
425
- | `TableOfContents.createHyperlinked(title?)` | Hyperlinked TOC | `TableOfContents.createHyperlinked()` |
426
- | `TableOfContents.createWithStyles(styles, opts?)` | TOC with specific styles | `TableOfContents.createWithStyles(['H1','H3'])` |
427
- | `TableOfContents.createFlat(title?, styles?)` | Flat TOC (no indent) | `TableOfContents.createFlat()` |
428
- | `TableOfContents.createNumbered(title?, format?)` | Numbered TOC | `TableOfContents.createNumbered('TOC', 'roman')` |
429
- | `TableOfContents.createWithSpacing(spacing, opts?)` | TOC with custom spacing | `TableOfContents.createWithSpacing(120)` |
430
- | `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')` |
431
417
 
432
418
  #### TOC Configuration Methods
433
419
 
434
- | Method | Description | Values |
435
- | ----------------------------------- | --------------------------------- | ----------------------------------- |
436
- | `setIncludeStyles(styles)` | Select specific heading styles | `['Heading1', 'Heading3']` |
437
- | `setNumbered(numbered, format?)` | Enable/disable numbering | `(true, 'roman')` |
438
- | `setNoIndent(noIndent)` | Remove indentation | `true/false` |
439
- | `setCustomIndents(indents)` | Custom indents per level | `[0, 200, 400]` (twips) |
440
- | `setSpaceBetweenEntries(spacing)` | Spacing between entries | `120` (twips) |
441
- | `setHyperlinkColor(color)` | Hyperlink color | `'0000FF'` (default blue) |
442
- | `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 |
443
429
 
444
430
  #### TOC Properties
445
431
 
446
- | Property | Type | Default | Description |
447
- | -------------------- | ------------------------------ | ------------ | ------------------------------------- |
448
- | `title` | `string` | `'Table of Contents'` | TOC title |
449
- | `levels` | `number` (1-9) | `3` | Heading levels to include |
450
- | `includeStyles` | `string[]` | `undefined` | Specific styles (overrides levels) |
451
- | `showPageNumbers` | `boolean` | `true` | Show page numbers |
452
- | `useHyperlinks` | `boolean` | `false` | Use hyperlinks instead of page #s |
453
- | `numbered` | `boolean` | `false` | Number TOC entries |
454
- | `numberingFormat` | `'decimal'/'roman'/'alpha'` | `'decimal'` | Numbering format |
455
- | `noIndent` | `boolean` | `false` | Remove all indentation |
456
- | `customIndents` | `number[]` | `undefined` | Custom indents in twips |
457
- | `spaceBetweenEntries`| `number` | `0` | Spacing in twips |
458
- | `hyperlinkColor` | `string` | `'0000FF'` | Hyperlink color (hex without #) |
459
- | `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 |
460
446
 
461
447
  **Example:**
448
+
462
449
  ```typescript
463
450
  // Basic TOC
464
451
  const simpleToc = TableOfContents.createStandard();
465
452
  doc.addTableOfContents(simpleToc);
466
453
 
467
454
  // Select specific styles (e.g., only Heading1 and Heading3)
468
- const customToc = TableOfContents.createWithStyles(['Heading1', 'Heading3']);
455
+ const customToc = TableOfContents.createWithStyles(["Heading1", "Heading3"]);
469
456
 
470
457
  // Flat TOC with no indentation
471
- const flatToc = TableOfContents.createFlat('Contents');
458
+ const flatToc = TableOfContents.createFlat("Contents");
472
459
 
473
460
  // Numbered TOC with roman numerals
474
- const numberedToc = TableOfContents.createNumbered('Table of Contents', 'roman');
461
+ const numberedToc = TableOfContents.createNumbered(
462
+ "Table of Contents",
463
+ "roman"
464
+ );
475
465
 
476
466
  // Custom hyperlink color (red instead of blue)
477
- const coloredToc = TableOfContents.createWithHyperlinkColor('FF0000');
467
+ const coloredToc = TableOfContents.createWithHyperlinkColor("FF0000");
478
468
 
479
469
  // Advanced configuration
480
470
  const toc = TableOfContents.create()
481
- .setIncludeStyles(['Heading1', 'Heading2', 'Heading3'])
482
- .setNumbered(true, 'decimal')
483
- .setSpaceBetweenEntries(120) // 6pt spacing
484
- .setHyperlinkColor('0000FF')
471
+ .setIncludeStyles(["Heading1", "Heading2", "Heading3"])
472
+ .setNumbered(true, "decimal")
473
+ .setSpaceBetweenEntries(120) // 6pt spacing
474
+ .setHyperlinkColor("0000FF")
485
475
  .setNoIndent(false);
486
476
 
487
477
  // Or use configure() for bulk settings
488
478
  toc.configure({
489
- title: 'Table of Contents',
490
- includeStyles: ['Heading1', 'CustomHeader'],
479
+ title: "Table of Contents",
480
+ includeStyles: ["Heading1", "CustomHeader"],
491
481
  numbered: true,
492
- numberingFormat: 'alpha',
482
+ numberingFormat: "alpha",
493
483
  spaceBetweenEntries: 100,
494
- hyperlinkColor: 'FF0000',
495
- noIndent: true
484
+ hyperlinkColor: "FF0000",
485
+ noIndent: true,
496
486
  });
497
487
 
498
488
  // Insert at specific position
@@ -599,13 +589,104 @@ doc.insertTocAt(0, toc);
599
589
 
600
590
  ### Unit Conversion Utilities
601
591
 
602
- | Function | Description | Example |
603
- | ---------------------------- | -------------------- | --------------------------- |
604
- | `inchesToTwips(inches)` | Inches to twips | `inchesToTwips(1)` // 1440 |
605
- | `inchesToEmus(inches)` | Inches to EMUs | `inchesToEmus(1)` // 914400 |
606
- | `cmToTwips(cm)` | Centimeters to twips | `cmToTwips(2.54)` // 1440 |
607
- | `pointsToTwips(points)` | Points to twips | `pointsToTwips(12)` // 240 |
608
- | `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>` |
609
690
 
610
691
  ## Common Recipes
611
692
 
@@ -997,11 +1078,11 @@ const run = new Run("Text<w:t>value</w:t>", { cleanXmlFromText: false });
997
1078
 
998
1079
  **The Right Approach**: Use the framework's API methods instead of embedding XML:
999
1080
 
1000
- - Use `paragraph.addText()` multiple times for separate text runs
1001
- - Use formatting options: `{bold: true}`, `{italic: true}`, etc.
1002
- - Use `paragraph.addHyperlink()` for links
1003
- - Don't pass XML strings to text methods
1004
- - 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
1005
1086
 
1006
1087
  For more details, see the [corruption detection examples](examples/troubleshooting/).
1007
1088
 
@@ -1012,6 +1093,7 @@ For more details, see the [corruption detection examples](examples/troubleshooti
1012
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.
1013
1094
 
1014
1095
  **Why This Causes Problems**:
1096
+
1015
1097
  - `pageBreakBefore` tells Word to insert a page break before the paragraph
1016
1098
  - `keepNext` tells Word to keep the paragraph with the next one (no break)
1017
1099
  - `keepLines` tells Word to keep all lines together (no break)
@@ -1025,13 +1107,14 @@ The framework now automatically prevents these conflicts by **prioritizing keep
1025
1107
  // When setting keepNext or keepLines, pageBreakBefore is automatically cleared
1026
1108
  const para = new Paragraph()
1027
1109
  .addText("Content")
1028
- .setPageBreakBefore(true) // Set to true
1029
- .setKeepNext(true); // Automatically clears pageBreakBefore
1110
+ .setPageBreakBefore(true) // Set to true
1111
+ .setKeepNext(true); // Automatically clears pageBreakBefore
1030
1112
 
1031
1113
  // Result: keepNext=true, pageBreakBefore=false (conflict resolved)
1032
1114
  ```
1033
1115
 
1034
1116
  **Why This Priority?**
1117
+
1035
1118
  - Keep properties (`keepNext`/`keepLines`) represent explicit user intent to keep content together
1036
1119
  - Page breaks are often layout hints that may conflict with document flow
1037
1120
  - Removing `pageBreakBefore` eliminates whitespace while preserving the user's intention
@@ -1049,6 +1132,7 @@ const doc = await Document.load("document-with-conflicts.docx");
1049
1132
  ```
1050
1133
 
1051
1134
  **How It Works**:
1135
+
1052
1136
  1. When `setKeepNext(true)` is called, `pageBreakBefore` is automatically set to `false`
1053
1137
  2. When `setKeepLines(true)` is called, `pageBreakBefore` is automatically set to `false`
1054
1138
  3. When parsing documents, if both properties exist, `pageBreakBefore` is cleared
@@ -1060,7 +1144,7 @@ If you need a page break despite keep properties, set it after:
1060
1144
 
1061
1145
  ```typescript
1062
1146
  const para = new Paragraph()
1063
- .setKeepNext(true) // Set first
1147
+ .setKeepNext(true) // Set first
1064
1148
  .setPageBreakBefore(true); // Override - you explicitly want this conflict
1065
1149
 
1066
1150
  // But note: This will cause layout issues (whitespace) in Word
@@ -1080,12 +1164,12 @@ Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md)
1080
1164
 
1081
1165
  **Critical Bug Fix Release:**
1082
1166
 
1083
- - **Fixed Paragraph.getText()** - Now includes hyperlink text content (critical data loss bug)
1084
- - **Added hyperlink integration tests** - 6 new comprehensive test cases
1085
- - **Enhanced test suite** - 474/478 tests passing (98.1% pass rate)
1086
- - **Fixed type safety** - XMLElement handling improvements across test files
1087
- - **Improved StylesManager** - XML corruption detection moved before parser
1088
- - **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
1089
1173
 
1090
1174
  **What This Fixes:**
1091
1175
  When using `para.addText('foo') + para.addHyperlink(link)`, the hyperlink text is now properly included in `paragraph.getText()`, preventing silent text loss.