docxmlater 1.4.3 → 1.6.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.
package/README.md CHANGED
@@ -1,1415 +1,1466 @@
1
- # docXMLater - Professional DOCX Framework
2
-
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-1119%20passing-brightgreen)](https://github.com/ItMeDiaTech/docXMLater)
5
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue)](https://www.typescriptlang.org/)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
-
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 robust test suite.
9
-
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
-
12
- ## Latest Updates - v1.4.3
13
-
14
- **Critical Fix: Header/Footer Parsing & Enhanced Management:**
15
-
16
- ### What's New in v1.4.3
17
-
18
- - **Header/Footer Parsing Fix:** Documents with headers/footers now load and save correctly
19
- - Fixed [Content_Types].xml corruption when loading documents with headers/footers
20
- - Headers and footers are now properly parsed when loading existing documents
21
- - All header/footer XML files are correctly declared in [Content_Types].xml
22
- - **New Header/Footer Management Methods:**
23
- - `removeHeader(type)` - Remove specific header (default/first/even)
24
- - `removeFooter(type)` - Remove specific footer (default/first/even)
25
- - `clearHeaders()` - Remove all headers while preserving footers
26
- - `clearFooters()` - Remove all footers while preserving headers
27
- - **Round-Trip Support:** Full load-modify-save cycle for documents with headers/footers
28
- - **MS Word Compliance:** Follows ECMA-376 specifications for header/footer handling
29
-
30
- ### Previous Updates - v1.3.0
31
-
32
- - **TOC Parsing:** Parse Table of Contents from existing documents with full SDT support
33
- - **TOC Modification:** Modify TOC field instructions (add/remove switches)
34
- - **Complete Feature Set:** All 102 major features implemented
35
- - **Table Styles:** Full support with 12 conditional formatting types
36
- - **Content Controls:** 9 control types supported
37
- - **Field Types:** 11 field types (PAGE, NUMPAGES, DATE, TIME, FILENAME, AUTHOR, TITLE, REF, HYPERLINK, SEQ, TC/XE)
38
- - **Production Ready:** Full ECMA-376 compliance
39
-
40
- **Test Results:** 1,122/1,150 tests passing (97.6% pass rate - 1,122 core features validated)
41
-
42
- ## Quick Start
43
-
44
- ```bash
45
- npm install docxmlater
46
- ```
47
-
48
- ```typescript
49
- import { Document } from "docxmlater";
50
-
51
- // Create document
52
- const doc = Document.create();
53
- doc.createParagraph("Hello World").setStyle("Title");
54
-
55
- // Save document
56
- await doc.save("output.docx");
57
- ```
58
-
59
- ## Complete API Reference
60
-
61
- ### Document Operations
62
-
63
- | Method | Description | Example |
64
- | --------------------------------- | ----------------------- | ------------------------------------------------ |
65
- | `Document.create(options?)` | Create new document | `const doc = Document.create()` |
66
- | `Document.createEmpty()` | Create minimal document | `const doc = Document.createEmpty()` |
67
- | `Document.load(path)` | Load from file | `const doc = await Document.load('file.docx')` |
68
- | `Document.loadFromBuffer(buffer)` | Load from buffer | `const doc = await Document.loadFromBuffer(buf)` |
69
- | `save(path)` | Save to file | `await doc.save('output.docx')` |
70
- | `toBuffer()` | Export as buffer | `const buffer = await doc.toBuffer()` |
71
- | `dispose()` | Clean up resources | `doc.dispose()` |
72
-
73
- ### Content Creation
74
-
75
- | Method | Description | Example |
76
- | -------------------------------- | ------------------------ | -------------------------------- |
77
- | `createParagraph(text?)` | Add paragraph | `doc.createParagraph('Text')` |
78
- | `createTable(rows, cols)` | Add table | `doc.createTable(3, 4)` |
79
- | `addParagraph(para)` | Add existing paragraph | `doc.addParagraph(myPara)` |
80
- | `addTable(table)` | Add existing table | `doc.addTable(myTable)` |
81
- | `addImage(image)` | Add image | `doc.addImage(myImage)` |
82
- | `addTableOfContents(toc?)` | Add TOC | `doc.addTableOfContents()` |
83
- | `insertParagraphAt(index, para)` | Insert at position | `doc.insertParagraphAt(0, para)` |
84
- | `insertTableAt(index, table)` | Insert table at position | `doc.insertTableAt(5, table)` |
85
- | `insertTocAt(index, toc)` | Insert TOC at position | `doc.insertTocAt(0, toc)` |
86
-
87
- ### Content Manipulation
88
-
89
- | Method | Description | Returns |
90
- | --------------------------------- | ---------------------------- | --------- |
91
- | `replaceParagraphAt(index, para)` | Replace paragraph | `boolean` |
92
- | `replaceTableAt(index, table)` | Replace table | `boolean` |
93
- | `moveElement(fromIndex, toIndex)` | Move element to new position | `boolean` |
94
- | `swapElements(index1, index2)` | Swap two elements | `boolean` |
95
- | `removeTocAt(index)` | Remove TOC element | `boolean` |
96
-
97
- ### Content Retrieval
98
-
99
- | Method | Description | Returns |
100
- | ---------------------- | --------------------------------------- | ------------------------------------------ |
101
- | `getParagraphs()` | Get top-level paragraphs | `Paragraph[]` |
102
- | `getAllParagraphs()` | Get all paragraphs (recursive) | `Paragraph[]` |
103
- | `getTables()` | Get top-level tables | `Table[]` |
104
- | `getAllTables()` | Get all tables (recursive) | `Table[]` |
105
- | `getBodyElements()` | Get all body elements | `BodyElement[]` |
106
- | `getParagraphCount()` | Count paragraphs | `number` |
107
- | `getTableCount()` | Count tables | `number` |
108
- | `getHyperlinks()` | Get all links | `Array<{hyperlink, paragraph}>` |
109
- | `getBookmarks()` | Get all bookmarks | `Array<{bookmark, paragraph}>` |
110
- | `getImages()` | Get all images | `Array<{image, relationshipId, filename}>` |
111
-
112
- **Note**: The `getAllParagraphs()` and `getAllTables()` methods recursively search inside tables and SDTs (Structured Document Tags), while the non-prefixed methods only return top-level elements.
113
-
114
- **Example - Recursive Element Access:**
115
-
116
- ```typescript
117
- import { Document, Hyperlink } from 'docxmlater';
118
-
119
- // Load document with complex structure (tables, SDTs, nested content)
120
- const doc = await Document.load('complex.docx');
121
-
122
- // Get only top-level paragraphs (misses nested content)
123
- const topLevel = doc.getParagraphs();
124
- console.log(`Top-level paragraphs: ${topLevel.length}`); // e.g., 37
125
-
126
- // Get ALL paragraphs including those in tables and SDTs
127
- const allParas = doc.getAllParagraphs();
128
- console.log(`All paragraphs: ${allParas.length}`); // e.g., 52
129
-
130
- // Apply formatting to ALL paragraphs (including nested ones)
131
- for (const para of allParas) {
132
- para.setSpaceAfter(120); // Set 6pt spacing after each paragraph
133
- }
134
-
135
- // Get all tables including those inside SDTs
136
- const allTables = doc.getAllTables();
137
- for (const table of allTables) {
138
- table.setWidth(5000).setWidthType('pct'); // Set to 100% width
139
- }
140
-
141
- // Find all hyperlinks in the entire document
142
- let hyperlinkCount = 0;
143
- for (const para of allParas) {
144
- for (const content of para.getContent()) {
145
- if (content instanceof Hyperlink) {
146
- hyperlinkCount++;
147
- content.setFormatting({ color: '0000FF' }); // Make all links blue
148
- }
149
- }
150
- }
151
- console.log(`Updated ${hyperlinkCount} hyperlinks`);
152
- ```
153
-
154
- ### Content Removal
155
-
156
- | Method | Description | Returns |
157
- | ------------------------------ | ------------------ | --------- |
158
- | `removeParagraph(paraOrIndex)` | Remove paragraph | `boolean` |
159
- | `removeTable(tableOrIndex)` | Remove table | `boolean` |
160
- | `clearParagraphs()` | Remove all content | `this` |
161
-
162
- ### Search & Replace
163
-
164
- | Method | Description | Options |
165
- | -------------------------------------- | --------------------- | ------------------------------ |
166
- | `findText(text, options?)` | Find text occurrences | `{caseSensitive?, wholeWord?}` |
167
- | `replaceText(find, replace, options?)` | Replace all text | `{caseSensitive?, wholeWord?}` |
168
- | `updateHyperlinkUrls(urlMap)` | Update hyperlink URLs | `Map<oldUrl, newUrl>` |
169
-
170
- ### Style Application
171
-
172
- | Method | Description | Returns |
173
- | ------------------------------------- | -------------------------------- | ------------------- |
174
- | `applyStyleToAll(styleId, predicate)` | Apply style to matching elements | `number` |
175
- | `findElementsByStyle(styleId)` | Find all elements using a style | `Array<Para\|Cell>` |
176
-
177
- **Example:**
178
-
179
- ```typescript
180
- // Apply Heading1 to all paragraphs containing "Chapter"
181
- const count = doc.applyStyleToAll("Heading1", (el) => {
182
- return el instanceof Paragraph && el.getText().includes("Chapter");
183
- });
184
-
185
- // Find all Heading1 elements
186
- const headings = doc.findElementsByStyle("Heading1");
187
- ```
188
-
189
- ### Document Statistics
190
-
191
- | Method | Description | Returns |
192
- | ----------------------------------- | ------------------- | ------------------------------ |
193
- | `getWordCount()` | Total word count | `number` |
194
- | `getCharacterCount(includeSpaces?)` | Character count | `number` |
195
- | `estimateSize()` | Size estimation | `{totalEstimatedMB, warning?}` |
196
- | `getSizeStats()` | Detailed size stats | `{elements, size, warnings}` |
197
-
198
- ### Text Formatting
199
-
200
- | Property | Values | Example |
201
- | ------------- | -------------------------------- | ----------------------- |
202
- | `bold` | `true/false` | `{bold: true}` |
203
- | `italic` | `true/false` | `{italic: true}` |
204
- | `underline` | `'single'/'double'/'dotted'/etc` | `{underline: 'single'}` |
205
- | `strike` | `true/false` | `{strike: true}` |
206
- | `font` | Font name | `{font: 'Arial'}` |
207
- | `size` | Points | `{size: 12}` |
208
- | `color` | Hex color | `{color: 'FF0000'}` |
209
- | `highlight` | Color name | `{highlight: 'yellow'}` |
210
- | `subscript` | `true/false` | `{subscript: true}` |
211
- | `superscript` | `true/false` | `{superscript: true}` |
212
- | `smallCaps` | `true/false` | `{smallCaps: true}` |
213
- | `allCaps` | `true/false` | `{allCaps: true}` |
214
-
215
- ### Paragraph Operations
216
-
217
- #### Creating Detached Paragraphs
218
-
219
- Create paragraphs independently before adding to a document:
220
-
221
- ```typescript
222
- // Create empty paragraph
223
- const para1 = Paragraph.create();
224
-
225
- // Create with text
226
- const para2 = Paragraph.create("Hello World");
227
-
228
- // Create with text and formatting
229
- const para3 = Paragraph.create("Centered text", { alignment: "center" });
230
-
231
- // Create with just formatting
232
- const para4 = Paragraph.create({
233
- alignment: "right",
234
- spacing: { before: 240 },
235
- });
236
-
237
- // Create with style
238
- const heading = Paragraph.createWithStyle("Chapter 1", "Heading1");
239
-
240
- // Create with both run and paragraph formatting
241
- const important = Paragraph.createFormatted(
242
- "Important Text",
243
- { bold: true, color: "FF0000" },
244
- { alignment: "center" }
245
- );
246
-
247
- // Add to document later
248
- doc.addParagraph(para1);
249
- doc.addParagraph(heading);
250
- ```
251
-
252
- #### Paragraph Factory Methods
253
-
254
- | Method | Description | Example |
255
- | --------------------------------------------------- | --------------------------- | --------------------------------------- |
256
- | `Paragraph.create(text?, formatting?)` | Create detached paragraph | `Paragraph.create('Text')` |
257
- | `Paragraph.create(formatting?)` | Create with formatting only | `Paragraph.create({alignment: 'left'})` |
258
- | `Paragraph.createWithStyle(text, styleId)` | Create with style | `Paragraph.createWithStyle('', 'H1')` |
259
- | `Paragraph.createEmpty()` | Create empty paragraph | `Paragraph.createEmpty()` |
260
- | `Paragraph.createFormatted(text, run?, paragraph?)` | Create with dual formatting | See example above |
261
-
262
- #### Paragraph Formatting Methods
263
-
264
- | Method | Description | Values |
265
- | ------------------------------ | ------------------- | ----------------------------------- |
266
- | `setAlignment(align)` | Text alignment | `'left'/'center'/'right'/'justify'` |
267
- | `setLeftIndent(twips)` | Left indentation | Twips value |
268
- | `setRightIndent(twips)` | Right indentation | Twips value |
269
- | `setFirstLineIndent(twips)` | First line indent | Twips value |
270
- | `setSpaceBefore(twips)` | Space before | Twips value |
271
- | `setSpaceAfter(twips)` | Space after | Twips value |
272
- | `setLineSpacing(twips, rule?)` | Line spacing | Twips + rule |
273
- | `setStyle(styleId)` | Apply style | Style ID |
274
- | `setKeepNext()` | Keep with next | - |
275
- | `setKeepLines()` | Keep lines together | - |
276
- | `setPageBreakBefore()` | Page break before | - |
277
-
278
- #### Paragraph Manipulation Methods
279
-
280
- | Method | Description | Returns |
281
- | -------------------------------------- | ----------------------- | ----------- |
282
- | `insertRunAt(index, run)` | Insert run at position | `this` |
283
- | `removeRunAt(index)` | Remove run at position | `boolean` |
284
- | `replaceRunAt(index, run)` | Replace run at position | `boolean` |
285
- | `findText(text, options?)` | Find text in runs | `number[]` |
286
- | `replaceText(find, replace, options?)` | Replace text in runs | `number` |
287
- | `mergeWith(otherPara)` | Merge another paragraph | `this` |
288
- | `clone()` | Clone paragraph | `Paragraph` |
289
-
290
- **Example:**
291
-
292
- ```typescript
293
- const para = doc.createParagraph("Hello World");
294
-
295
- // Find and replace
296
- const indices = para.findText("World"); // [1]
297
- const count = para.replaceText("World", "Universe", { caseSensitive: true });
298
-
299
- // Manipulate runs
300
- para.insertRunAt(0, new Run("Start: ", { bold: true }));
301
- para.replaceRunAt(1, new Run("HELLO", { allCaps: true }));
302
-
303
- // Merge paragraphs
304
- const para2 = Paragraph.create(" More text");
305
- para.mergeWith(para2); // Combines runs
306
- ```
307
-
308
- ### Run (Text Span) Operations
309
-
310
- | Method | Description | Returns |
311
- | ------------------------------- | ------------------------- | ------- |
312
- | `clone()` | Clone run with formatting | `Run` |
313
- | `insertText(index, text)` | Insert text at position | `this` |
314
- | `appendText(text)` | Append text to end | `this` |
315
- | `replaceText(start, end, text)` | Replace text range | `this` |
316
-
317
- **Example:**
318
-
319
- ```typescript
320
- const run = new Run("Hello World", { bold: true });
321
-
322
- // Text manipulation
323
- run.insertText(6, "Beautiful "); // "Hello Beautiful World"
324
- run.appendText("!"); // "Hello Beautiful World!"
325
- run.replaceText(0, 5, "Hi"); // "Hi Beautiful World!"
326
-
327
- // Clone for reuse
328
- const copy = run.clone();
329
- copy.setColor("FF0000"); // Original unchanged
330
- ```
331
-
332
- ### Table Operations
333
-
334
- | Method | Description | Example |
335
- | ----------------------- | -------------------- | ---------------------------------------- |
336
- | `getRow(index)` | Get table row | `table.getRow(0)` |
337
- | `getCell(row, col)` | Get table cell | `table.getCell(0, 1)` |
338
- | `addRow()` | Add new row | `table.addRow()` |
339
- | `removeRow(index)` | Remove row | `table.removeRow(2)` |
340
- | `insertColumn(index)` | Insert column | `table.insertColumn(1)` |
341
- | `removeColumn(index)` | Remove column | `table.removeColumn(3)` |
342
- | `setWidth(twips)` | Set table width | `table.setWidth(8640)` |
343
- | `setAlignment(align)` | Table alignment | `table.setAlignment('center')` |
344
- | `setAllBorders(border)` | Set all borders | `table.setAllBorders({style: 'single'})` |
345
- | `setBorders(borders)` | Set specific borders | `table.setBorders({top: {...}})` |
346
-
347
- #### Advanced Table Operations
348
-
349
- | Method | Description | Returns |
350
- | ------------------------------------------------ | ------------------------- | ------------ |
351
- | `mergeCells(startRow, startCol, endRow, endCol)` | Merge cells | `this` |
352
- | `splitCell(row, col)` | Remove cell spanning | `this` |
353
- | `moveCell(fromRow, fromCol, toRow, toCol)` | Move cell contents | `this` |
354
- | `swapCells(row1, col1, row2, col2)` | Swap two cells | `this` |
355
- | `setColumnWidth(index, width)` | Set specific column width | `this` |
356
- | `setColumnWidths(widths)` | Set all column widths | `this` |
357
- | `insertRows(startIndex, count)` | Insert multiple rows | `TableRow[]` |
358
- | `removeRows(startIndex, count)` | Remove multiple rows | `boolean` |
359
- | `clone()` | Clone entire table | `Table` |
360
-
361
- **Example:**
362
-
363
- ```typescript
364
- const table = doc.createTable(3, 3);
365
-
366
- // Merge cells horizontally (row 0, columns 0-2)
367
- table.mergeCells(0, 0, 0, 2);
368
-
369
- // Move cell contents
370
- table.moveCell(1, 1, 2, 2);
371
-
372
- // Swap cells
373
- table.swapCells(0, 0, 2, 2);
374
-
375
- // Batch row operations
376
- table.insertRows(1, 3); // Insert 3 rows at position 1
377
- table.removeRows(4, 2); // Remove 2 rows starting at position 4
378
-
379
- // Set column widths
380
- table.setColumnWidth(0, 2000); // First column = 2000 twips
381
- table.setColumnWidths([2000, 3000, 2000]); // All columns
382
-
383
- // Clone table for reuse
384
- const tableCopy = table.clone();
385
- ```
386
-
387
- ### Table Cell Operations
388
-
389
- | Method | Description | Example |
390
- | ----------------------------- | --------------------- | ------------------------------------- |
391
- | `createParagraph(text?)` | Add paragraph to cell | `cell.createParagraph('Text')` |
392
- | `setShading(shading)` | Cell background | `cell.setShading({fill: 'E0E0E0'})` |
393
- | `setVerticalAlignment(align)` | Vertical align | `cell.setVerticalAlignment('center')` |
394
- | `setColumnSpan(cols)` | Merge columns | `cell.setColumnSpan(3)` |
395
- | `setRowSpan(rows)` | Merge rows | `cell.setRowSpan(2)` |
396
- | `setBorders(borders)` | Cell borders | `cell.setBorders({top: {...}})` |
397
- | `setWidth(width, type?)` | Cell width | `cell.setWidth(2000, 'dxa')` |
398
-
399
- ### Style Management
400
-
401
- | Method | Description | Example |
402
- | ----------------------------- | ------------------ | ---------------------------------- |
403
- | `addStyle(style)` | Add custom style | `doc.addStyle(myStyle)` |
404
- | `getStyle(styleId)` | Get style by ID | `doc.getStyle('Heading1')` |
405
- | `hasStyle(styleId)` | Check style exists | `doc.hasStyle('CustomStyle')` |
406
- | `getStyles()` | Get all styles | `doc.getStyles()` |
407
- | `removeStyle(styleId)` | Remove style | `doc.removeStyle('OldStyle')` |
408
- | `updateStyle(styleId, props)` | Update style | `doc.updateStyle('Normal', {...})` |
409
-
410
- #### Style Manipulation
411
-
412
- | Method | Description | Returns |
413
- | ------------------------ | ----------------------------------- | ------- |
414
- | `style.clone()` | Clone style | `Style` |
415
- | `style.mergeWith(other)` | Merge properties from another style | `this` |
416
-
417
- **Example:**
418
-
419
- ```typescript
420
- // Clone a style
421
- const heading1 = doc.getStyle("Heading1");
422
- const customHeading = heading1.clone();
423
- customHeading.setRunFormatting({ color: "FF0000" });
424
-
425
- // Merge styles
426
- const baseStyle = Style.createNormalStyle();
427
- const overrideStyle = Style.create({
428
- styleId: "Override",
429
- name: "Override",
430
- type: "paragraph",
431
- runFormatting: { bold: true, color: "FF0000" },
432
- });
433
- baseStyle.mergeWith(overrideStyle); // baseStyle now has bold red text
434
- ```
435
-
436
- #### Built-in Styles
437
-
438
- - `Normal` - Default paragraph
439
- - `Title` - Document title
440
- - `Subtitle` - Document subtitle
441
- - `Heading1` through `Heading9` - Section headings
442
- - `ListParagraph` - List items
443
-
444
- ### List Management
445
-
446
- | Method | Description | Returns |
447
- | --------------------------------------- | ----------------------- | ------- |
448
- | `createBulletList(levels?, bullets?)` | Create bullet list | `numId` |
449
- | `createNumberedList(levels?, formats?)` | Create numbered list | `numId` |
450
- | `createMultiLevelList()` | Create multi-level list | `numId` |
451
-
452
- ### Table of Contents (TOC)
453
-
454
- #### Basic TOC Creation
455
-
456
- | Method | Description | Example |
457
- | -------------------------- | ---------------------- | -------------------------- |
458
- | `addTableOfContents(toc?)` | Add TOC to document | `doc.addTableOfContents()` |
459
- | `insertTocAt(index, toc)` | Insert TOC at position | `doc.insertTocAt(0, toc)` |
460
- | `removeTocAt(index)` | Remove TOC at position | `doc.removeTocAt(0)` |
461
-
462
- #### TOC Factory Methods
463
-
464
- | Method | Description | Example |
465
- | -------------------------------------------------------- | ------------------------ | ---------------------------------------------------- |
466
- | `TableOfContents.createStandard(title?)` | Standard TOC (3 levels) | `TableOfContents.createStandard()` |
467
- | `TableOfContents.createSimple(title?)` | Simple TOC (2 levels) | `TableOfContents.createSimple()` |
468
- | `TableOfContents.createDetailed(title?)` | Detailed TOC (4 levels) | `TableOfContents.createDetailed()` |
469
- | `TableOfContents.createHyperlinked(title?)` | Hyperlinked TOC | `TableOfContents.createHyperlinked()` |
470
- | `TableOfContents.createNoPageNumbers(opts?)` | TOC without page numbers | `TableOfContents.createNoPageNumbers()` |
471
- | `TableOfContents.createWithStyles(styles, opts?)` | TOC with specific styles | `TableOfContents.createWithStyles(['H1','H3'])` |
472
- | `TableOfContents.createFlat(title?, styles?)` | Flat TOC (no indent) | `TableOfContents.createFlat()` |
473
- | `TableOfContents.createNumbered(title?, format?)` | Numbered TOC | `TableOfContents.createNumbered('TOC', 'roman')` |
474
- | `TableOfContents.createWithSpacing(spacing, opts?)` | TOC with custom spacing | `TableOfContents.createWithSpacing(120)` |
475
- | `TableOfContents.createWithHyperlinkColor(color, opts?)` | Custom hyperlink color | `TableOfContents.createWithHyperlinkColor('FF0000')` |
476
-
477
- **Note:** All TOC elements are automatically wrapped in an SDT (Structured Document Tag) for native Word integration. This enables Word's "Update Table" button and provides better compatibility with Microsoft Word's TOC features.
478
-
479
- #### TOC Configuration Methods
480
-
481
- | Method | Description | Values |
482
- | --------------------------------- | ------------------------------ | -------------------------- |
483
- | `setIncludeStyles(styles)` | Select specific heading styles | `['Heading1', 'Heading3']` |
484
- | `setNumbered(numbered, format?)` | Enable/disable numbering | `(true, 'roman')` |
485
- | `setNoIndent(noIndent)` | Remove indentation | `true/false` |
486
- | `setCustomIndents(indents)` | Custom indents per level | `[0, 200, 400]` (twips) |
487
- | `setSpaceBetweenEntries(spacing)` | Spacing between entries | `120` (twips) |
488
- | `setHyperlinkColor(color)` | Hyperlink color | `'0000FF'` (default blue) |
489
- | `setHideInWebLayout(hide)` | Hide page numbers in web view | `true/false` |
490
- | `configure(options)` | Bulk configuration | See example below |
491
-
492
- #### TOC Properties
493
-
494
- | Property | Type | Default | Description |
495
- | --------------------- | ------------------------------------ | --------------------- | ---------------------------------- |
496
- | `title` | `string` | `'Table of Contents'` | TOC title |
497
- | `levels` | `number` (1-9) | `3` | Heading levels to include |
498
- | `includeStyles` | `string[]` | `undefined` | Specific styles (overrides levels) |
499
- | `showPageNumbers` | `boolean` | `true` | Show page numbers |
500
- | `useHyperlinks` | `boolean` | `false` | Use hyperlinks instead of page #s |
501
- | `hideInWebLayout` | `boolean` | `false` | Hide page numbers in web layout |
502
- | `numbered` | `boolean` | `false` | Number TOC entries |
503
- | `numberingFormat` | `'decimal'/'roman'/'alpha'` | `'decimal'` | Numbering format |
504
- | `noIndent` | `boolean` | `false` | Remove all indentation |
505
- | `customIndents` | `number[]` | `undefined` | Custom indents in twips |
506
- | `spaceBetweenEntries` | `number` | `0` | Spacing in twips |
507
- | `hyperlinkColor` | `string` | `'0000FF'` | Hyperlink color (hex without #) |
508
- | `tabLeader` | `'dot'/'hyphen'/'underscore'/'none'` | `'dot'` | Tab leader character |
509
-
510
- **Example:**
511
-
512
- ```typescript
513
- // Basic TOC
514
- const simpleToc = TableOfContents.createStandard();
515
- doc.addTableOfContents(simpleToc);
516
-
517
- // Select specific styles (e.g., only Heading1 and Heading3)
518
- const customToc = TableOfContents.createWithStyles(["Heading1", "Heading3"]);
519
-
520
- // Flat TOC with no indentation
521
- const flatToc = TableOfContents.createFlat("Contents");
522
-
523
- // Numbered TOC with roman numerals
524
- const numberedToc = TableOfContents.createNumbered(
525
- "Table of Contents",
526
- "roman"
527
- );
528
-
529
- // Custom hyperlink color (red instead of blue)
530
- const coloredToc = TableOfContents.createWithHyperlinkColor("FF0000");
531
-
532
- // Advanced configuration
533
- const toc = TableOfContents.create()
534
- .setIncludeStyles(["Heading1", "Heading2", "Heading3"])
535
- .setNumbered(true, "decimal")
536
- .setSpaceBetweenEntries(120) // 6pt spacing
537
- .setHyperlinkColor("0000FF")
538
- .setNoIndent(false);
539
-
540
- // Or use configure() for bulk settings
541
- toc.configure({
542
- title: "Table of Contents",
543
- includeStyles: ["Heading1", "CustomHeader"],
544
- numbered: true,
545
- numberingFormat: "alpha",
546
- spaceBetweenEntries: 100,
547
- hyperlinkColor: "FF0000",
548
- noIndent: true,
549
- });
550
-
551
- // Insert at specific position
552
- doc.insertTocAt(0, toc);
553
- ```
554
-
555
- ### Image Handling
556
-
557
- | Method | Description | Example |
558
- | --------------------------------------- | ------------------ | -------------------------------- |
559
- | `Image.fromFile(path, width?, height?)` | Load from file | `Image.fromFile('pic.jpg')` |
560
- | `Image.fromBuffer(buffer, ext, w?, h?)` | Load from buffer | `Image.fromBuffer(buf, 'png')` |
561
- | `setWidth(emus, maintainRatio?)` | Set width | `img.setWidth(inchesToEmus(3))` |
562
- | `setHeight(emus, maintainRatio?)` | Set height | `img.setHeight(inchesToEmus(2))` |
563
- | `setSize(width, height)` | Set dimensions | `img.setSize(w, h)` |
564
- | `setRotation(degrees)` | Rotate image | `img.setRotation(90)` |
565
- | `setAltText(text)` | Accessibility text | `img.setAltText('Description')` |
566
-
567
- ### Hyperlinks
568
-
569
- | Method | Description | Example |
570
- | ------------------------------------------------- | ---------------- | ---------------------------------------------------------- |
571
- | `Hyperlink.createExternal(url, text, format?)` | Web link | `Hyperlink.createExternal('https://example.com', 'Click')` |
572
- | `Hyperlink.createEmail(email, text?, format?)` | Email link | `Hyperlink.createEmail('user@example.com')` |
573
- | `Hyperlink.createInternal(anchor, text, format?)` | Internal link | `Hyperlink.createInternal('Section1', 'Go to')` |
574
- | `para.addHyperlink(hyperlink)` | Add to paragraph | `para.addHyperlink(link)` |
575
-
576
- ### Headers & Footers
577
-
578
- | Method | Description | Example |
579
- | ---------------------------- | ------------------------ | -------------------------------- |
580
- | `setHeader(header)` | Set default header | `doc.setHeader(myHeader)` |
581
- | `setFooter(footer)` | Set default footer | `doc.setFooter(myFooter)` |
582
- | `setFirstPageHeader(header)` | First page header | `doc.setFirstPageHeader(header)` |
583
- | `setFirstPageFooter(footer)` | First page footer | `doc.setFirstPageFooter(footer)` |
584
- | `setEvenPageHeader(header)` | Even page header | `doc.setEvenPageHeader(header)` |
585
- | `setEvenPageFooter(footer)` | Even page footer | `doc.setEvenPageFooter(footer)` |
586
- | `removeHeader(type)` | Remove specific header | `doc.removeHeader('default')` |
587
- | `removeFooter(type)` | Remove specific footer | `doc.removeFooter('first')` |
588
- | `clearHeaders()` | Remove all headers | `doc.clearHeaders()` |
589
- | `clearFooters()` | Remove all footers | `doc.clearFooters()` |
590
-
591
- ### Page Setup
592
-
593
- | Method | Description | Example |
594
- | ------------------------------------- | ----------------- | ------------------------------------- |
595
- | `setPageSize(width, height, orient?)` | Page dimensions | `doc.setPageSize(12240, 15840)` |
596
- | `setPageOrientation(orientation)` | Page orientation | `doc.setPageOrientation('landscape')` |
597
- | `setMargins(margins)` | Page margins | `doc.setMargins({top: 1440, ...})` |
598
- | `setLanguage(language)` | Document language | `doc.setLanguage('en-US')` |
599
-
600
- ### Document Properties
601
-
602
- | Method | Description | Properties |
603
- | ---------------------- | ------------ | ------------------------------------- |
604
- | `setProperties(props)` | Set metadata | `{title, subject, creator, keywords}` |
605
- | `getProperties()` | Get metadata | Returns all properties |
606
-
607
- ### Advanced Features
608
-
609
- #### Bookmarks
610
-
611
- | Method | Description |
612
- | ---------------------------------------- | ------------------- |
613
- | `createBookmark(name)` | Create bookmark |
614
- | `createHeadingBookmark(text)` | Auto-named bookmark |
615
- | `getBookmark(name)` | Get by name |
616
- | `hasBookmark(name)` | Check existence |
617
- | `addBookmarkToParagraph(para, bookmark)` | Add to paragraph |
618
-
619
- #### Comments
620
-
621
- | Method | Description |
622
- | ------------------------------------------- | ----------------- |
623
- | `createComment(author, content, initials?)` | Add comment |
624
- | `createReply(parentId, author, content)` | Reply to comment |
625
- | `getComment(id)` | Get by ID |
626
- | `getAllComments()` | Get all top-level |
627
- | `addCommentToParagraph(para, comment)` | Add to paragraph |
628
-
629
- #### Track Changes
630
-
631
- | Method | Description |
632
- | ------------------------------------ | ----------------- |
633
- | `trackInsertion(para, author, text)` | Track insertion |
634
- | `trackDeletion(para, author, text)` | Track deletion |
635
- | `isTrackingChanges()` | Check if tracking |
636
- | `getRevisionStats()` | Get statistics |
637
-
638
- #### Footnotes & Endnotes
639
-
640
- | Method | Description |
641
- | -------------------------- | ---------------- |
642
- | `FootnoteManager.create()` | Manage footnotes |
643
- | `EndnoteManager.create()` | Manage endnotes |
644
-
645
- #### Document Helper Functions
646
-
647
- High-level helper methods for common document formatting tasks:
648
-
649
- | Method | Description |
650
- | ---------------------------------------- | ------------------------------------------------------------------------------------------- |
651
- | `applyCustomFormattingToExistingStyles()`| Modify Heading1, Heading2, Normal styles with Verdana font, specific spacing, single line spacing, wrap Heading2 in tables, right-align "Top of Document" hyperlinks, set all hyperlinks to blue, and hide TOC page numbers |
652
- | `wrapParagraphInTable(para, options?)` | Wrap a paragraph in a 1x1 table with optional shading, margins, and width settings |
653
- | `isParagraphInTable(para)` | Check if a paragraph is inside a table; returns `{inTable: boolean, cell?: TableCell}` |
654
- | `updateAllHyperlinkColors(color)` | Set all hyperlinks in the document to a specific color (e.g., '0000FF' for blue) |
655
- | `removeAllHeadersFooters()` | Remove all headers and footers from the document; returns count of headers/footers removed |
656
-
657
- **Example - Using Helper Functions:**
658
-
659
- ```typescript
660
- import { Document } from 'docxmlater';
661
-
662
- const doc = await Document.load('document.docx');
663
-
664
- // Apply comprehensive formatting to standard styles
665
- const results = doc.applyCustomFormattingToExistingStyles();
666
- console.log(`Modified styles:`, results);
667
- // Output: { heading1: true, heading2: true, normal: true }
668
-
669
- // Wrap a specific paragraph in a table
670
- const para = doc.getParagraphs()[0];
671
- doc.wrapParagraphInTable(para, {
672
- shading: 'BFBFBF', // Gray background
673
- marginLeft: 101, // 5pt margins
674
- marginRight: 101,
675
- tableWidthPercent: 5000 // 100% width
676
- });
677
-
678
- // Check if a paragraph is in a table
679
- const { inTable, cell } = doc.isParagraphInTable(para);
680
- if (inTable && cell) {
681
- console.log('Paragraph is in a table cell');
682
- cell.setShading({ fill: 'FFFF00' }); // Change to yellow
683
- }
684
-
685
- // Set all hyperlinks to blue
686
- doc.updateAllHyperlinkColors('0000FF');
687
-
688
- // Remove all headers and footers
689
- const removedCount = doc.removeAllHeadersFooters();
690
- console.log(`Removed ${removedCount} headers and footers`);
691
-
692
- await doc.save('formatted.docx');
693
- ```
694
-
695
- **Note on `applyCustomFormattingToExistingStyles()`:**
696
-
697
- This helper function applies a comprehensive set of formatting rules:
698
- - **Heading1**: 18pt black bold Verdana, left aligned, 0pt before/12pt after, single line spacing
699
- - **Heading2**: 14pt black bold Verdana, left aligned, 6pt before/after, single line spacing, wrapped in gray tables (100% width)
700
- - **Normal**: 12pt Verdana, left aligned, 3pt before/after, single line spacing
701
- - **All Styles**: Removes italic and underline formatting
702
- - **Hyperlinks**: "Top of the Document" links are right-aligned with 0pt spacing; all hyperlinks set to blue (#0000FF)
703
- - **Empty Paragraphs**: Empty Heading2 paragraphs are skipped (not wrapped in tables)
704
- - **TOC Elements**: All Table of Contents have page numbers hidden (showPageNumbers=false, hideInWebLayout=true with \n and \z switches)
705
-
706
- Per ECMA-376 §17.7.2, direct formatting in document.xml overrides style definitions. This method automatically clears conflicting direct formatting to ensure style changes take effect.
707
-
708
- ### Low-Level Document Parts
709
-
710
- | Method | Description | Example |
711
- | ---------------------------- | --------------------- | ------------------------------------------------- |
712
- | `getPart(partName)` | Get document part | `doc.getPart('word/document.xml')` |
713
- | `setPart(partName, content)` | Set document part | `doc.setPart('custom.xml', data)` |
714
- | `removePart(partName)` | Remove part | `doc.removePart('custom.xml')` |
715
- | `listParts()` | List all parts | `const parts = await doc.listParts()` |
716
- | `partExists(partName)` | Check part exists | `if (await doc.partExists('...'))` |
717
- | `getContentTypes()` | Get content types | `const types = await doc.getContentTypes()` |
718
- | `addContentType(part, type)` | Register content type | `doc.addContentType('.json', 'application/json')` |
719
-
720
- ### Unit Conversion Utilities
721
-
722
- #### Twips Conversions
723
- | Function | Description | Example |
724
- | ------------------------- | ------------------- | --------------------------- |
725
- | `twipsToPoints(twips)` | Twips to points | `twipsToPoints(240)` // 12 |
726
- | `twipsToInches(twips)` | Twips to inches | `twipsToInches(1440)` // 1 |
727
- | `twipsToCm(twips)` | Twips to cm | `twipsToCm(1440)` // 2.54 |
728
- | `twipsToEmus(twips)` | Twips to EMUs | `twipsToEmus(1440)` |
729
-
730
- #### EMUs (English Metric Units) Conversions
731
- | Function | Description | Example |
732
- | --------------------------- | -------------------- | ----------------------------- |
733
- | `emusToTwips(emus)` | EMUs to twips | `emusToTwips(914400)` // 1440 |
734
- | `emusToInches(emus)` | EMUs to inches | `emusToInches(914400)` // 1 |
735
- | `emusToCm(emus)` | EMUs to cm | `emusToCm(914400)` // 2.54 |
736
- | `emusToPoints(emus)` | EMUs to points | `emusToPoints(914400)` // 72 |
737
- | `emusToPixels(emus, dpi?)` | EMUs to pixels | `emusToPixels(914400)` // 96 |
738
-
739
- #### Points Conversions
740
- | Function | Description | Example |
741
- | ------------------------ | ------------------ | -------------------------- |
742
- | `pointsToTwips(points)` | Points to twips | `pointsToTwips(12)` // 240 |
743
- | `pointsToEmus(points)` | Points to EMUs | `pointsToEmus(72)` |
744
- | `pointsToInches(points)` | Points to inches | `pointsToInches(72)` // 1 |
745
- | `pointsToCm(points)` | Points to cm | `pointsToCm(72)` // 2.54 |
746
-
747
- #### Inches Conversions
748
- | Function | Description | Example |
749
- | ----------------------------- | ------------------- | ----------------------------- |
750
- | `inchesToTwips(inches)` | Inches to twips | `inchesToTwips(1)` // 1440 |
751
- | `inchesToEmus(inches)` | Inches to EMUs | `inchesToEmus(1)` // 914400 |
752
- | `inchesToPoints(inches)` | Inches to points | `inchesToPoints(1)` // 72 |
753
- | `inchesToCm(inches)` | Inches to cm | `inchesToCm(1)` // 2.54 |
754
- | `inchesToPixels(inches, dpi)` | Inches to pixels | `inchesToPixels(1, 96)` // 96 |
755
-
756
- #### Centimeters Conversions
757
- | Function | Description | Example |
758
- | ----------------------- | ---------------- | --------------------------- |
759
- | `cmToTwips(cm)` | cm to twips | `cmToTwips(2.54)` // 1440 |
760
- | `cmToEmus(cm)` | cm to EMUs | `cmToEmus(2.54)` // 914400 |
761
- | `cmToInches(cm)` | cm to inches | `cmToInches(2.54)` // 1 |
762
- | `cmToPoints(cm)` | cm to points | `cmToPoints(2.54)` // 72 |
763
- | `cmToPixels(cm, dpi?)` | cm to pixels | `cmToPixels(2.54, 96)` // 96|
764
-
765
- #### Pixels Conversions
766
- | Function | Description | Example |
767
- | ---------------------------- | ------------------- | ------------------------------ |
768
- | `pixelsToEmus(pixels, dpi?)` | Pixels to EMUs | `pixelsToEmus(96)` // 914400 |
769
- | `pixelsToInches(pixels, dpi?)`| Pixels to inches | `pixelsToInches(96, 96)` // 1 |
770
- | `pixelsToTwips(pixels, dpi?)`| Pixels to twips | `pixelsToTwips(96, 96)` // 1440|
771
- | `pixelsToCm(pixels, dpi?)` | Pixels to cm | `pixelsToCm(96, 96)` // 2.54 |
772
- | `pixelsToPoints(pixels, dpi?)`| Pixels to points | `pixelsToPoints(96, 96)` // 72 |
773
-
774
- **Note:** Default DPI is 96 for pixel conversions
775
-
776
- ### ZIP Archive Helper Methods
777
-
778
- #### File Operations
779
- | Method | Description | Example |
780
- | ------------------------------- | ------------------------- | -------------------------------------------- |
781
- | `addFile(path, content)` | Add file to archive | `handler.addFile('doc.xml', xmlContent)` |
782
- | `updateFile(path, content)` | Update existing file | `handler.updateFile('doc.xml', newContent)` |
783
- | `removeFile(path)` | Remove file from archive | `handler.removeFile('old.xml')` |
784
- | `renameFile(oldPath, newPath)` | Rename file | `handler.renameFile('a.xml', 'b.xml')` |
785
- | `copyFile(srcPath, destPath)` | Copy file | `handler.copyFile('a.xml', 'copy-a.xml')` |
786
- | `moveFile(srcPath, destPath)` | Move file | `handler.moveFile('a.xml', 'folder/a.xml')` |
787
-
788
- #### File Retrieval
789
- | Method | Description | Returns |
790
- | ------------------------- | ---------------------- | --------------- |
791
- | `getFile(path)` | Get file object | `ZipFile` |
792
- | `getFileAsString(path)` | Get file as string | `string` |
793
- | `getFileAsBuffer(path)` | Get file as buffer | `Buffer` |
794
- | `hasFile(path)` | Check if file exists | `boolean` |
795
- | `getFilePaths()` | Get all file paths | `string[]` |
796
- | `getAllFiles()` | Get all files | `FileMap` |
797
-
798
- #### Batch Operations
799
- | Method | Description | Returns |
800
- | ------------------------------- | ---------------------------- | -------------- |
801
- | `removeFiles(paths[])` | Remove multiple files | `number` |
802
- | `getFilesByExtension(ext)` | Get files by extension | `ZipFile[]` |
803
- | `getTextFiles()` | Get all text files | `ZipFile[]` |
804
- | `getBinaryFiles()` | Get all binary files | `ZipFile[]` |
805
- | `getMediaFiles()` | Get media files | `ZipFile[]` |
806
-
807
- #### Archive Information
808
- | Method | Description | Returns |
809
- | ------------------ | ------------------------- | ------------------------ |
810
- | `getFileCount()` | Count files in archive | `number` |
811
- | `getTotalSize()` | Get total size in bytes | `number` |
812
- | `getStats()` | Get detailed statistics | `{fileCount, size, ...}` |
813
- | `isEmpty()` | Check if archive is empty | `boolean` |
814
-
815
- #### Import/Export
816
- | Method | Description | Returns |
817
- | -------------------------------- | ------------------------ | -------------------- |
818
- | `exportFile(internal, external)` | Export file from archive | `Promise<void>` |
819
- | `importFile(external, internal)` | Import file to archive | `Promise<void>` |
820
-
821
- ## Common Recipes
822
-
823
- ### Create a Simple Document
824
-
825
- ```typescript
826
- const doc = Document.create();
827
- doc.createParagraph("Title").setStyle("Title");
828
- doc.createParagraph("This is a simple document.");
829
- await doc.save("simple.docx");
830
- ```
831
-
832
- ### Add Formatted Text
833
-
834
- ```typescript
835
- const para = doc.createParagraph();
836
- para.addText("Bold", { bold: true });
837
- para.addText(" and ");
838
- para.addText("Colored", { color: "FF0000" });
839
- ```
840
-
841
- ### Create a Table with Borders
842
-
843
- ```typescript
844
- const table = doc.createTable(3, 3);
845
- table.setAllBorders({ style: "single", size: 8, color: "000000" });
846
- table.getCell(0, 0)?.createParagraph("Header 1");
847
- table.getRow(0)?.getCell(0)?.setShading({ fill: "4472C4" });
848
- ```
849
-
850
- ### Insert an Image
851
-
852
- ```typescript
853
- import { Image, inchesToEmus } from "docxmlater";
854
-
855
- const image = Image.fromFile("./photo.jpg");
856
- image.setWidth(inchesToEmus(4), true); // 4 inches, maintain ratio
857
- doc.addImage(image);
858
- ```
859
-
860
- ### Add a Hyperlink
861
-
862
- ```typescript
863
- const para = doc.createParagraph();
864
- para.addText("Visit ");
865
- para.addHyperlink(
866
- Hyperlink.createExternal("https://example.com", "our website")
867
- );
868
- ```
869
-
870
- ### Search and Replace Text
871
-
872
- ```typescript
873
- // Find all occurrences
874
- const results = doc.findText("old text", { caseSensitive: true });
875
- console.log(`Found ${results.length} occurrences`);
876
-
877
- // Replace all
878
- const count = doc.replaceText("old text", "new text", { wholeWord: true });
879
- console.log(`Replaced ${count} occurrences`);
880
- ```
881
-
882
- ### Load and Modify Existing Document
883
-
884
- ```typescript
885
- const doc = await Document.load("existing.docx");
886
- doc.createParagraph("Added paragraph");
887
-
888
- // Update all hyperlinks
889
- const urlMap = new Map([["https://old-site.com", "https://new-site.com"]]);
890
- doc.updateHyperlinkUrls(urlMap);
891
-
892
- await doc.save("modified.docx");
893
- ```
894
-
895
- ### Create Lists
896
-
897
- ```typescript
898
- // Bullet list
899
- const bulletId = doc.createBulletList(3);
900
- doc.createParagraph("First item").setNumbering(bulletId, 0);
901
- doc.createParagraph("Second item").setNumbering(bulletId, 0);
902
-
903
- // Numbered list
904
- const numberId = doc.createNumberedList(3);
905
- doc.createParagraph("Step 1").setNumbering(numberId, 0);
906
- doc.createParagraph("Step 2").setNumbering(numberId, 0);
907
- ```
908
-
909
- ### Apply Custom Styles
910
-
911
- ```typescript
912
- import { Style } from "docxmlater";
913
-
914
- const customStyle = Style.create({
915
- styleId: "CustomHeading",
916
- name: "Custom Heading",
917
- basedOn: "Normal",
918
- runFormatting: { bold: true, size: 14, color: "2E74B5" },
919
- paragraphFormatting: { alignment: "center", spaceAfter: 240 },
920
- });
921
-
922
- doc.addStyle(customStyle);
923
- doc.createParagraph("Custom Styled Text").setStyle("CustomHeading");
924
- ```
925
-
926
- ### Build Content with Detached Paragraphs
927
-
928
- Create paragraphs independently and add them conditionally:
929
-
930
- ```typescript
931
- import { Paragraph } from "docxmlater";
932
-
933
- // Create reusable paragraph templates
934
- const warningTemplate = Paragraph.createFormatted(
935
- "WARNING: ",
936
- { bold: true, color: "FF6600" },
937
- { spacing: { before: 120, after: 120 } }
938
- );
939
-
940
- // Clone and customize
941
- const warning1 = warningTemplate.clone();
942
- warning1.addText("Please read the documentation before proceeding.");
943
-
944
- // Build content from data
945
- const items = [
946
- { title: "First Item", description: "Description here" },
947
- { title: "Second Item", description: "Another description" },
948
- ];
949
-
950
- items.forEach((item, index) => {
951
- const titlePara = Paragraph.create(`${index + 1}. `);
952
- titlePara.addText(item.title, { bold: true });
953
-
954
- const descPara = Paragraph.create(item.description, {
955
- indentation: { left: 360 },
956
- });
957
-
958
- doc.addParagraph(titlePara);
959
- doc.addParagraph(descPara);
960
- });
961
-
962
- // See examples/advanced/detached-paragraphs.ts for more patterns
963
- ```
964
-
965
- ### Add Headers and Footers
966
-
967
- ```typescript
968
- import { Header, Footer, Field } from "docxmlater";
969
-
970
- // Header with page numbers
971
- const header = Header.create();
972
- header.addParagraph("Document Title").setAlignment("center");
973
-
974
- // Footer with page numbers
975
- const footer = Footer.create();
976
- const footerPara = footer.addParagraph();
977
- footerPara.addText("Page ");
978
- footerPara.addField(Field.create({ type: "PAGE" }));
979
- footerPara.addText(" of ");
980
- footerPara.addField(Field.create({ type: "NUMPAGES" }));
981
-
982
- doc.setHeader(header);
983
- doc.setFooter(footer);
984
- ```
985
-
986
- ### Work with Document Statistics
987
-
988
- ```typescript
989
- // Get word and character counts
990
- console.log("Words:", doc.getWordCount());
991
- console.log("Characters:", doc.getCharacterCount());
992
- console.log("Characters (no spaces):", doc.getCharacterCount(false));
993
-
994
- // Check document size
995
- const size = doc.estimateSize();
996
- if (size.warning) {
997
- console.warn(size.warning);
998
- }
999
- console.log(`Estimated size: ${size.totalEstimatedMB} MB`);
1000
- ```
1001
-
1002
- ### Handle Large Documents Efficiently
1003
-
1004
- ```typescript
1005
- const doc = Document.create({
1006
- maxMemoryUsagePercent: 80,
1007
- maxRssMB: 2048,
1008
- maxImageCount: 50,
1009
- maxTotalImageSizeMB: 100,
1010
- });
1011
-
1012
- // Process document...
1013
-
1014
- // Clean up resources after saving
1015
- await doc.save("large-document.docx");
1016
- doc.dispose(); // Free memory
1017
- ```
1018
-
1019
- ### Direct XML Access (Advanced)
1020
-
1021
- ```typescript
1022
- // Get raw XML
1023
- const documentXml = await doc.getPart("word/document.xml");
1024
- console.log(documentXml?.content);
1025
-
1026
- // Modify raw XML (use with caution)
1027
- await doc.setPart("word/custom.xml", "<custom>data</custom>");
1028
- await doc.addContentType("/word/custom.xml", "application/xml");
1029
-
1030
- // List all parts
1031
- const parts = await doc.listParts();
1032
- console.log("Document contains:", parts.length, "parts");
1033
- ```
1034
-
1035
- ## Features
1036
-
1037
- - **Full OpenXML Compliance** - Follows ECMA-376 standard
1038
- - **TypeScript First** - Complete type definitions
1039
- - **Memory Efficient** - Handles large documents with streaming
1040
- - **Atomic Saves** - Prevents corruption with temp file pattern
1041
- - **Rich Formatting** - Complete text and paragraph formatting
1042
- - **Tables** - Full support with borders, shading, merging
1043
- - **Images** - PNG, JPEG, GIF with sizing and positioning
1044
- - **Hyperlinks** - External, internal, and email links
1045
- - **Styles** - 13 built-in styles + custom style creation
1046
- - **Lists** - Bullets, numbering, multi-level
1047
- - **Headers/Footers** - Different first/even/odd pages
1048
- - **Search & Replace** - With case and whole word options
1049
- - **Document Stats** - Word count, character count, size estimation
1050
- - **Track Changes** - Insertions and deletions with authors
1051
- - **Comments** - With replies and threading
1052
- - **Bookmarks** - For internal navigation
1053
- - **Low-level Access** - Direct ZIP and XML manipulation
1054
-
1055
- ## Performance
1056
-
1057
- - Process 100+ page documents efficiently
1058
- - Atomic save pattern prevents corruption
1059
- - Memory management for large files
1060
- - Lazy loading of document parts
1061
- - Resource cleanup with `dispose()`
1062
-
1063
- ## Testing
1064
-
1065
- ```bash
1066
- npm test # Run all tests
1067
- npm run test:watch # Watch mode
1068
- npm run test:coverage # Coverage report
1069
- ```
1070
-
1071
- **Current:** 1,119 tests passing (97.3% pass rate) | 100% core functionality covered
1072
-
1073
- ## Development
1074
-
1075
- ```bash
1076
- # Install dependencies
1077
- npm install
1078
-
1079
- # Build TypeScript
1080
- npm run build
1081
-
1082
- # Run examples
1083
- npx ts-node examples/simple-document.ts
1084
- ```
1085
-
1086
- ## Project Structure
1087
-
1088
- ```text
1089
- src/
1090
- ├── core/ # Document, Parser, Generator, Validator
1091
- ├── elements/ # Paragraph, Run, Table, Image, Hyperlink
1092
- ├── formatting/ # Style, NumberingManager
1093
- ├── xml/ # XMLBuilder, XMLParser
1094
- ├── zip/ # ZipHandler for DOCX manipulation
1095
- └── utils/ # Validation, Units conversion
1096
-
1097
- examples/
1098
- ├── 01-basic/ # Simple document creation
1099
- ├── 02-text/ # Text formatting examples
1100
- ├── 03-tables/ # Table examples
1101
- ├── 04-styles/ # Style examples
1102
- ├── 05-images/ # Image handling
1103
- ├── 06-complete/ # Full document examples
1104
- └── 07-hyperlinks/ # Link examples
1105
- ```
1106
-
1107
- ## Hierarchy
1108
-
1109
- ```text
1110
- w:document (root)
1111
- └── w:body (body container)
1112
- ├── w:p (paragraph) [1..n]
1113
- │ ├── w:pPr (paragraph properties) [0..1]
1114
- │ │ ├── w:pStyle (style reference)
1115
- │ │ ├── w:jc (justification/alignment)
1116
- │ │ ├── w:ind (indentation)
1117
- │ │ └── w:spacing (spacing before/after)
1118
- │ ├── w:r (run) [1..n]
1119
- │ │ ├── w:rPr (run properties) [0..1]
1120
- │ │ │ ├── w:b (bold)
1121
- │ │ │ ├── w:i (italic)
1122
- │ │ │ ├── w:u (underline)
1123
- │ │ │ ├── w:sz (font size)
1124
- │ │ │ └── w:color (text color)
1125
- │ │ └── w:t (text content) [1]
1126
- │ ├── w:hyperlink (hyperlink) [0..n]
1127
- │ │ └── w:r (run with hyperlink text)
1128
- │ └── w:drawing (embedded image/shape) [0..n]
1129
- ├── w:tbl (table) [1..n]
1130
- │ ├── w:tblPr (table properties)
1131
- │ └── w:tr (table row) [1..n]
1132
- │ └── w:tc (table cell) [1..n]
1133
- │ └── w:p (paragraph in cell)
1134
- └── w:sectPr (section properties) [1] (must be last child of w:body)
1135
- ```
1136
-
1137
- ## Requirements
1138
-
1139
- - Node.js 16+
1140
- - TypeScript 5.0+ (for development)
1141
-
1142
- ## Installation Options
1143
-
1144
- ```bash
1145
- # NPM
1146
- npm install docxmlater
1147
-
1148
- # Yarn
1149
- yarn add docxmlater
1150
-
1151
- # PNPM
1152
- pnpm add docxmlater
1153
- ```
1154
-
1155
- ## Troubleshooting
1156
-
1157
- ### XML Corruption in Text
1158
-
1159
- **Problem**: Text displays with XML tags like `Important Information<w:t xml:space="preserve">1` in Word.
1160
-
1161
- **Cause**: Passing XML-like strings to text methods instead of using the API properly.
1162
-
1163
- ```typescript
1164
- // WRONG - Will display escaped XML as literal text
1165
- paragraph.addText("Important Information<w:t>1</w:t>");
1166
- // Displays as: "Important Information<w:t>1</w:t>"
1167
-
1168
- // CORRECT - Use separate text runs
1169
- paragraph.addText("Important Information");
1170
- paragraph.addText("1");
1171
- // Displays as: "Important Information1"
1172
-
1173
- // Or combine in one call
1174
- paragraph.addText("Important Information 1");
1175
- ```
1176
-
1177
- **Detection**: Use the corruption detection utility to find issues:
1178
-
1179
- ```typescript
1180
- import { detectCorruptionInDocument } from "docxmlater";
1181
-
1182
- const doc = await Document.load("file.docx");
1183
- const report = detectCorruptionInDocument(doc);
1184
-
1185
- if (report.isCorrupted) {
1186
- console.log(report.summary);
1187
- report.locations.forEach((loc) => {
1188
- console.log(`Paragraph ${loc.paragraphIndex}, Run ${loc.runIndex}:`);
1189
- console.log(` Original: ${loc.text}`);
1190
- console.log(` Fixed: ${loc.suggestedFix}`);
1191
- });
1192
- }
1193
- ```
1194
-
1195
- **Auto-Cleaning**: XML patterns are automatically removed by default for defensive data handling:
1196
-
1197
- ```typescript
1198
- // Default behavior - auto-clean enabled
1199
- const run = new Run("Text<w:t>value</w:t>");
1200
- // Result: "Textvalue" (XML tags removed automatically)
1201
-
1202
- // Disable auto-cleaning (for debugging)
1203
- const run = new Run("Text<w:t>value</w:t>", { cleanXmlFromText: false });
1204
- // Result: "Text<w:t>value</w:t>" (XML tags preserved, will display in Word)
1205
- ```
1206
-
1207
- **Why This Happens**: The framework correctly escapes XML special characters per the XML specification. When you pass XML tags as text, they are properly escaped (`<` becomes `&lt;`) and Word displays them as literal text, not as markup.
1208
-
1209
- **The Right Approach**: Use the framework's API methods instead of embedding XML:
1210
-
1211
- - Use `paragraph.addText()` multiple times for separate text runs
1212
- - Use formatting options: `{bold: true}`, `{italic: true}`, etc.
1213
- - Use `paragraph.addHyperlink()` for links
1214
- - Don't pass XML strings to text methods
1215
- - Don't try to embed `<w:t>` or other XML tags in your text
1216
-
1217
- For more details, see the [corruption detection examples](examples/troubleshooting/).
1218
-
1219
- ### Layout Conflicts (Massive Whitespace)
1220
-
1221
- **Problem**: Documents show massive whitespace between paragraphs when opened in Word, even though the XML looks correct.
1222
-
1223
- **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.
1224
-
1225
- **Why This Causes Problems**:
1226
-
1227
- - `pageBreakBefore` tells Word to insert a page break before the paragraph
1228
- - `keepNext` tells Word to keep the paragraph with the next one (no break)
1229
- - `keepLines` tells Word to keep all lines together (no break)
1230
- - The combination creates layout conflicts that manifest as massive whitespace
1231
-
1232
- **Automatic Conflict Resolution** (v0.28.2+):
1233
-
1234
- The framework now automatically prevents these conflicts by **prioritizing keep properties over page breaks**:
1235
-
1236
- ```typescript
1237
- // When setting keepNext or keepLines, pageBreakBefore is automatically cleared
1238
- const para = new Paragraph()
1239
- .addText("Content")
1240
- .setPageBreakBefore(true) // Set to true
1241
- .setKeepNext(true); // Automatically clears pageBreakBefore
1242
-
1243
- // Result: keepNext=true, pageBreakBefore=false (conflict resolved)
1244
- ```
1245
-
1246
- **Why This Priority?**
1247
-
1248
- - Keep properties (`keepNext`/`keepLines`) represent explicit user intent to keep content together
1249
- - Page breaks are often layout hints that may conflict with document flow
1250
- - Removing `pageBreakBefore` eliminates whitespace while preserving the user's intention
1251
-
1252
- **Parsing Documents**:
1253
-
1254
- When loading existing DOCX files with conflicts, they are automatically resolved:
1255
-
1256
- ```typescript
1257
- // Load document with conflicts
1258
- const doc = await Document.load("document-with-conflicts.docx");
1259
-
1260
- // Conflicts are automatically resolved during parsing
1261
- // keepNext/keepLines take priority, pageBreakBefore is removed
1262
- ```
1263
-
1264
- **How It Works**:
1265
-
1266
- 1. When `setKeepNext(true)` is called, `pageBreakBefore` is automatically set to `false`
1267
- 2. When `setKeepLines(true)` is called, `pageBreakBefore` is automatically set to `false`
1268
- 3. When parsing documents, if both properties exist, `pageBreakBefore` is cleared
1269
- 4. Keep properties win because they represent explicit user intent
1270
-
1271
- **Manual Override**:
1272
-
1273
- If you need a page break despite keep properties, set it after:
1274
-
1275
- ```typescript
1276
- const para = new Paragraph()
1277
- .setKeepNext(true) // Set first
1278
- .setPageBreakBefore(true); // Override - you explicitly want this conflict
1279
-
1280
- // But note: This will cause layout issues (whitespace) in Word
1281
- ```
1282
-
1283
- ## Known Limitations
1284
-
1285
- While docXMLater provides comprehensive DOCX manipulation capabilities, there are some features that are not yet fully implemented:
1286
-
1287
- ### 1. Table Row Spanning with vMerge
1288
-
1289
- **Status:** FULLY IMPLEMENTED
1290
-
1291
- **What Works:**
1292
- - Column spanning (horizontal cell merging) is fully supported
1293
- - Row spanning (vertical cell merging) is now fully implemented
1294
- - Both horizontal and vertical merging can be combined
1295
- - Uses Word's proper `vMerge` attribute ('restart' and 'continue')
1296
-
1297
- **Usage:**
1298
- ```typescript
1299
- // Merge cells horizontally (column spanning)
1300
- table.mergeCells(0, 0, 0, 2); // Merge columns 0-2 in row 0
1301
-
1302
- // Merge cells vertically (row spanning)
1303
- table.mergeCells(0, 0, 2, 0); // Merge rows 0-2 in column 0
1304
-
1305
- // Merge both horizontally and vertically (2x2 block)
1306
- table.mergeCells(0, 0, 1, 1); // Merge 2x2 block starting at (0,0)
1307
- ```
1308
-
1309
- ### 2. Structured Document Tags (SDT) Parsing
1310
-
1311
- **Status:** FULLY IMPLEMENTED
1312
-
1313
- **What Works:**
1314
- - Complete SDT parsing from existing documents
1315
- - All 9 control types supported (richText, plainText, comboBox, dropDownList, datePicker, checkbox, picture, buildingBlock, group)
1316
- - SDT properties fully extracted (id, tag, lock, alias, controlType)
1317
- - Nested content parsing (paragraphs, tables, nested SDTs)
1318
- - Preserves element order using XMLParser's `_orderedChildren` metadata
1319
- - Round-trip operations fully supported
1320
-
1321
- **Control Types Supported:**
1322
- - **Rich Text** - Multi-formatted text content
1323
- - **Plain Text** - Simple text with optional multiLine support
1324
- - **Combo Box** - User-editable dropdown with list items
1325
- - **Dropdown List** - Fixed selection from list items
1326
- - **Date Picker** - Date selection with format and calendar type
1327
- - **Checkbox** - Boolean selection with custom checked/unchecked states
1328
- - **Picture** - Image content control
1329
- - **Building Block** - Gallery and category-based content
1330
- - **Group** - Grouping of other controls
1331
-
1332
- **Usage:**
1333
- ```typescript
1334
- // Load documents with SDTs - fully parsed
1335
- const doc = await Document.load('document-with-sdts.docx');
1336
-
1337
- // Access parsed SDT content
1338
- const sdts = doc.getBodyElements().filter(el => el instanceof StructuredDocumentTag);
1339
- for (const sdt of sdts) {
1340
- console.log('ID:', sdt.getId());
1341
- console.log('Tag:', sdt.getTag());
1342
- console.log('Type:', sdt.getControlType());
1343
- console.log('Content:', sdt.getContent());
1344
- }
1345
-
1346
- // Create new SDTs programmatically
1347
- const sdt = new StructuredDocumentTag({
1348
- id: 123456,
1349
- tag: 'MyControl',
1350
- controlType: 'richText',
1351
- alias: 'Rich Text Control'
1352
- });
1353
- sdt.addContent(paragraph);
1354
- ```
1355
-
1356
- All known limitations have been resolved! For feature requests or bug reports, please visit our [GitHub Issues](https://github.com/ItMeDiaTech/docXMLater/issues).
1357
-
1358
- ## Contributing
1359
-
1360
- Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md).
1361
-
1362
- 1. Fork the repository
1363
- 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
1364
- 3. Commit changes (`git commit -m 'Add amazing feature'`)
1365
- 4. Push to branch (`git push origin feature/amazing-feature`)
1366
- 5. Open a Pull Request
1367
-
1368
- ## Recent Updates (v1.3.0)
1369
-
1370
- **Enhanced Document Parsing & Helper Functions:**
1371
-
1372
- - **TOC Parsing** - Parse Table of Contents from existing DOCX files
1373
- - Extract TOC field instructions with all switches (\h, \u, \z, \n, \o, \t)
1374
- - Detect SDT wrappers with `docPartGallery="Table of Contents"`
1375
- - Create `TableOfContentsElement` objects from parsed TOCs
1376
- - Support for modifying TOC field instructions in loaded documents
1377
- - **removeAllHeadersFooters() Helper** - New document helper function
1378
- - Removes all headers and footers from the document
1379
- - Deletes header/footer XML files and relationships
1380
- - Returns count of removed headers/footers
1381
- - **Enhanced Test Suite** - 1,119/1,150 tests passing (97.3% pass rate)
1382
- - **Documentation Updates** - Complete API reference for new helper functions
1383
-
1384
- **Previous Enhancements (v1.2.0):**
1385
- - 5 advanced document helper functions
1386
- - Enhanced document modification capabilities
1387
- - Improved paragraph and table wrapping utilities
1388
-
1389
- ## License
1390
-
1391
- MIT © DiaTech
1392
-
1393
- ## Acknowledgments
1394
-
1395
- - Built with [JSZip](https://stuk.github.io/jszip/) for ZIP handling
1396
- - Follows [ECMA-376](https://www.ecma-international.org/publications-and-standards/standards/ecma-376/) Office Open XML standard
1397
- - Inspired by [python-docx](https://python-docx.readthedocs.io/) and [docx](https://github.com/dolanmiu/docx)
1398
-
1399
- ## Support
1400
-
1401
- - **Documentation**: [Full Docs](https://github.com/ItMeDiaTech/docXMLater/tree/main/docs)
1402
- - **Examples**: [Example Code](https://github.com/ItMeDiaTech/docXMLater/tree/main/examples)
1403
- - **Issues**: [GitHub Issues](https://github.com/ItMeDiaTech/docXMLater/issues)
1404
- - **Discussions**: [GitHub Discussions](https://github.com/ItMeDiaTech/docXMLater/discussions)
1405
-
1406
- ## Quick Links
1407
-
1408
- - [NPM Package](https://www.npmjs.com/package/docxmlater)
1409
- - [GitHub Repository](https://github.com/ItMeDiaTech/docXMLater)
1410
- - [API Reference](https://github.com/ItMeDiaTech/docXMLater/tree/main/docs/api)
1411
- - [Change Log](https://github.com/ItMeDiaTech/docXMLater/blob/main/CHANGELOG.md)
1412
-
1413
- ---
1414
-
1415
- **Ready to create amazing Word documents?** Start with our [examples](https://github.com/ItMeDiaTech/docXMLater/tree/main/examples) or dive into the [API Reference](#complete-api-reference) above!
1
+ # docXMLater - Professional DOCX Framework
2
+
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-1119%20passing-brightgreen)](https://github.com/ItMeDiaTech/docXMLater)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue)](https://www.typescriptlang.org/)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
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 robust test suite.
9
+
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
+
12
+ ## Latest Updates - v1.4.3
13
+
14
+ **Critical Fix: Header/Footer Parsing & Enhanced Management:**
15
+
16
+ ### What's New in v1.4.3
17
+
18
+ - **Header/Footer Parsing Fix:** Documents with headers/footers now load and save correctly
19
+ - Fixed [Content_Types].xml corruption when loading documents with headers/footers
20
+ - Headers and footers are now properly parsed when loading existing documents
21
+ - All header/footer XML files are correctly declared in [Content_Types].xml
22
+ - **New Header/Footer Management Methods:**
23
+ - `removeHeader(type)` - Remove specific header (default/first/even)
24
+ - `removeFooter(type)` - Remove specific footer (default/first/even)
25
+ - `clearHeaders()` - Remove all headers while preserving footers
26
+ - `clearFooters()` - Remove all footers while preserving headers
27
+ - **Round-Trip Support:** Full load-modify-save cycle for documents with headers/footers
28
+ - **MS Word Compliance:** Follows ECMA-376 specifications for header/footer handling
29
+
30
+ ### Previous Updates - v1.3.0
31
+
32
+ - **TOC Parsing:** Parse Table of Contents from existing documents with full SDT support
33
+ - **TOC Modification:** Modify TOC field instructions (add/remove switches)
34
+ - **Complete Feature Set:** All 102 major features implemented
35
+ - **Table Styles:** Full support with 12 conditional formatting types
36
+ - **Content Controls:** 9 control types supported
37
+ - **Field Types:** 11 field types (PAGE, NUMPAGES, DATE, TIME, FILENAME, AUTHOR, TITLE, REF, HYPERLINK, SEQ, TC/XE)
38
+ - **Production Ready:** Full ECMA-376 compliance
39
+
40
+ **Test Results:** 1,122/1,150 tests passing (97.6% pass rate - 1,122 core features validated)
41
+
42
+ ## Quick Start
43
+
44
+ ```bash
45
+ npm install docxmlater
46
+ ```
47
+
48
+ ```typescript
49
+ import { Document } from "docxmlater";
50
+
51
+ // Create document
52
+ const doc = Document.create();
53
+ doc.createParagraph("Hello World").setStyle("Title");
54
+
55
+ // Save document
56
+ await doc.save("output.docx");
57
+ ```
58
+
59
+ ## Complete API Reference
60
+
61
+ ### Document Operations
62
+
63
+ | Method | Description | Example |
64
+ | --------------------------------- | ----------------------- | ------------------------------------------------ |
65
+ | `Document.create(options?)` | Create new document | `const doc = Document.create()` |
66
+ | `Document.createEmpty()` | Create minimal document | `const doc = Document.createEmpty()` |
67
+ | `Document.load(path)` | Load from file | `const doc = await Document.load('file.docx')` |
68
+ | `Document.loadFromBuffer(buffer)` | Load from buffer | `const doc = await Document.loadFromBuffer(buf)` |
69
+ | `save(path)` | Save to file | `await doc.save('output.docx')` |
70
+ | `toBuffer()` | Export as buffer | `const buffer = await doc.toBuffer()` |
71
+ | `dispose()` | Clean up resources | `doc.dispose()` |
72
+
73
+ ### Content Creation
74
+
75
+ | Method | Description | Example |
76
+ | -------------------------------- | ------------------------ | -------------------------------- |
77
+ | `createParagraph(text?)` | Add paragraph | `doc.createParagraph('Text')` |
78
+ | `createTable(rows, cols)` | Add table | `doc.createTable(3, 4)` |
79
+ | `addParagraph(para)` | Add existing paragraph | `doc.addParagraph(myPara)` |
80
+ | `addTable(table)` | Add existing table | `doc.addTable(myTable)` |
81
+ | `addImage(image)` | Add image | `doc.addImage(myImage)` |
82
+ | `addTableOfContents(toc?)` | Add TOC | `doc.addTableOfContents()` |
83
+ | `insertParagraphAt(index, para)` | Insert at position | `doc.insertParagraphAt(0, para)` |
84
+ | `insertTableAt(index, table)` | Insert table at position | `doc.insertTableAt(5, table)` |
85
+ | `insertTocAt(index, toc)` | Insert TOC at position | `doc.insertTocAt(0, toc)` |
86
+
87
+ ### Content Manipulation
88
+
89
+ | Method | Description | Returns |
90
+ | --------------------------------- | ---------------------------- | --------- |
91
+ | `replaceParagraphAt(index, para)` | Replace paragraph | `boolean` |
92
+ | `replaceTableAt(index, table)` | Replace table | `boolean` |
93
+ | `moveElement(fromIndex, toIndex)` | Move element to new position | `boolean` |
94
+ | `swapElements(index1, index2)` | Swap two elements | `boolean` |
95
+ | `removeTocAt(index)` | Remove TOC element | `boolean` |
96
+
97
+ ### Content Retrieval
98
+
99
+ | Method | Description | Returns |
100
+ | ---------------------- | --------------------------------------- | ------------------------------------------ |
101
+ | `getParagraphs()` | Get top-level paragraphs | `Paragraph[]` |
102
+ | `getAllParagraphs()` | Get all paragraphs (recursive) | `Paragraph[]` |
103
+ | `getTables()` | Get top-level tables | `Table[]` |
104
+ | `getAllTables()` | Get all tables (recursive) | `Table[]` |
105
+ | `getBodyElements()` | Get all body elements | `BodyElement[]` |
106
+ | `getParagraphCount()` | Count paragraphs | `number` |
107
+ | `getTableCount()` | Count tables | `number` |
108
+ | `getHyperlinks()` | Get all links | `Array<{hyperlink, paragraph}>` |
109
+ | `getBookmarks()` | Get all bookmarks | `Array<{bookmark, paragraph}>` |
110
+ | `getImages()` | Get all images | `Array<{image, relationshipId, filename}>` |
111
+
112
+ **Note**: The `getAllParagraphs()` and `getAllTables()` methods recursively search inside tables and SDTs (Structured Document Tags), while the non-prefixed methods only return top-level elements.
113
+
114
+ **Example - Recursive Element Access:**
115
+
116
+ ```typescript
117
+ import { Document, Hyperlink } from 'docxmlater';
118
+
119
+ // Load document with complex structure (tables, SDTs, nested content)
120
+ const doc = await Document.load('complex.docx');
121
+
122
+ // Get only top-level paragraphs (misses nested content)
123
+ const topLevel = doc.getParagraphs();
124
+ console.log(`Top-level paragraphs: ${topLevel.length}`); // e.g., 37
125
+
126
+ // Get ALL paragraphs including those in tables and SDTs
127
+ const allParas = doc.getAllParagraphs();
128
+ console.log(`All paragraphs: ${allParas.length}`); // e.g., 52
129
+
130
+ // Apply formatting to ALL paragraphs (including nested ones)
131
+ for (const para of allParas) {
132
+ para.setSpaceAfter(120); // Set 6pt spacing after each paragraph
133
+ }
134
+
135
+ // Get all tables including those inside SDTs
136
+ const allTables = doc.getAllTables();
137
+ for (const table of allTables) {
138
+ table.setWidth(5000).setWidthType('pct'); // Set to 100% width
139
+ }
140
+
141
+ // Find all hyperlinks in the entire document
142
+ let hyperlinkCount = 0;
143
+ for (const para of allParas) {
144
+ for (const content of para.getContent()) {
145
+ if (content instanceof Hyperlink) {
146
+ hyperlinkCount++;
147
+ content.setFormatting({ color: '0000FF' }); // Make all links blue
148
+ }
149
+ }
150
+ }
151
+ console.log(`Updated ${hyperlinkCount} hyperlinks`);
152
+ ```
153
+
154
+ ### Content Removal
155
+
156
+ | Method | Description | Returns |
157
+ | ------------------------------ | ------------------ | --------- |
158
+ | `removeParagraph(paraOrIndex)` | Remove paragraph | `boolean` |
159
+ | `removeTable(tableOrIndex)` | Remove table | `boolean` |
160
+ | `clearParagraphs()` | Remove all content | `this` |
161
+
162
+ ### Search & Replace
163
+
164
+ | Method | Description | Options |
165
+ | -------------------------------------- | --------------------- | ------------------------------ |
166
+ | `findText(text, options?)` | Find text occurrences | `{caseSensitive?, wholeWord?}` |
167
+ | `replaceText(find, replace, options?)` | Replace all text | `{caseSensitive?, wholeWord?}` |
168
+ | `updateHyperlinkUrls(urlMap)` | Update hyperlink URLs | `Map<oldUrl, newUrl>` |
169
+
170
+ ### Style Application
171
+
172
+ | Method | Description | Returns |
173
+ | ------------------------------------- | -------------------------------- | ------------------- |
174
+ | `applyStyleToAll(styleId, predicate)` | Apply style to matching elements | `number` |
175
+ | `findElementsByStyle(styleId)` | Find all elements using a style | `Array<Para\|Cell>` |
176
+
177
+ **Example:**
178
+
179
+ ```typescript
180
+ // Apply Heading1 to all paragraphs containing "Chapter"
181
+ const count = doc.applyStyleToAll("Heading1", (el) => {
182
+ return el instanceof Paragraph && el.getText().includes("Chapter");
183
+ });
184
+
185
+ // Find all Heading1 elements
186
+ const headings = doc.findElementsByStyle("Heading1");
187
+ ```
188
+
189
+ ### Document Statistics
190
+
191
+ | Method | Description | Returns |
192
+ | ----------------------------------- | ------------------- | ------------------------------ |
193
+ | `getWordCount()` | Total word count | `number` |
194
+ | `getCharacterCount(includeSpaces?)` | Character count | `number` |
195
+ | `estimateSize()` | Size estimation | `{totalEstimatedMB, warning?}` |
196
+ | `getSizeStats()` | Detailed size stats | `{elements, size, warnings}` |
197
+
198
+ ### Text Formatting
199
+
200
+ | Property | Values | Example |
201
+ | ------------- | -------------------------------- | ----------------------- |
202
+ | `bold` | `true/false` | `{bold: true}` |
203
+ | `italic` | `true/false` | `{italic: true}` |
204
+ | `underline` | `'single'/'double'/'dotted'/etc` | `{underline: 'single'}` |
205
+ | `strike` | `true/false` | `{strike: true}` |
206
+ | `font` | Font name | `{font: 'Arial'}` |
207
+ | `size` | Points | `{size: 12}` |
208
+ | `color` | Hex color | `{color: 'FF0000'}` |
209
+ | `highlight` | Color name | `{highlight: 'yellow'}` |
210
+ | `subscript` | `true/false` | `{subscript: true}` |
211
+ | `superscript` | `true/false` | `{superscript: true}` |
212
+ | `smallCaps` | `true/false` | `{smallCaps: true}` |
213
+ | `allCaps` | `true/false` | `{allCaps: true}` |
214
+
215
+ ### Paragraph Operations
216
+
217
+ #### Creating Detached Paragraphs
218
+
219
+ Create paragraphs independently before adding to a document:
220
+
221
+ ```typescript
222
+ // Create empty paragraph
223
+ const para1 = Paragraph.create();
224
+
225
+ // Create with text
226
+ const para2 = Paragraph.create("Hello World");
227
+
228
+ // Create with text and formatting
229
+ const para3 = Paragraph.create("Centered text", { alignment: "center" });
230
+
231
+ // Create with just formatting
232
+ const para4 = Paragraph.create({
233
+ alignment: "right",
234
+ spacing: { before: 240 },
235
+ });
236
+
237
+ // Create with style
238
+ const heading = Paragraph.createWithStyle("Chapter 1", "Heading1");
239
+
240
+ // Create with both run and paragraph formatting
241
+ const important = Paragraph.createFormatted(
242
+ "Important Text",
243
+ { bold: true, color: "FF0000" },
244
+ { alignment: "center" }
245
+ );
246
+
247
+ // Add to document later
248
+ doc.addParagraph(para1);
249
+ doc.addParagraph(heading);
250
+ ```
251
+
252
+ #### Paragraph Factory Methods
253
+
254
+ | Method | Description | Example |
255
+ | --------------------------------------------------- | --------------------------- | --------------------------------------- |
256
+ | `Paragraph.create(text?, formatting?)` | Create detached paragraph | `Paragraph.create('Text')` |
257
+ | `Paragraph.create(formatting?)` | Create with formatting only | `Paragraph.create({alignment: 'left'})` |
258
+ | `Paragraph.createWithStyle(text, styleId)` | Create with style | `Paragraph.createWithStyle('', 'H1')` |
259
+ | `Paragraph.createEmpty()` | Create empty paragraph | `Paragraph.createEmpty()` |
260
+ | `Paragraph.createFormatted(text, run?, paragraph?)` | Create with dual formatting | See example above |
261
+
262
+ #### Paragraph Formatting Methods
263
+
264
+ | Method | Description | Values |
265
+ | ------------------------------ | ------------------- | ----------------------------------- |
266
+ | `setAlignment(align)` | Text alignment | `'left'/'center'/'right'/'justify'` |
267
+ | `setLeftIndent(twips)` | Left indentation | Twips value |
268
+ | `setRightIndent(twips)` | Right indentation | Twips value |
269
+ | `setFirstLineIndent(twips)` | First line indent | Twips value |
270
+ | `setSpaceBefore(twips)` | Space before | Twips value |
271
+ | `setSpaceAfter(twips)` | Space after | Twips value |
272
+ | `setLineSpacing(twips, rule?)` | Line spacing | Twips + rule |
273
+ | `setStyle(styleId)` | Apply style | Style ID |
274
+ | `setKeepNext()` | Keep with next | - |
275
+ | `setKeepLines()` | Keep lines together | - |
276
+ | `setPageBreakBefore()` | Page break before | - |
277
+
278
+ #### Paragraph Manipulation Methods
279
+
280
+ | Method | Description | Returns |
281
+ | -------------------------------------- | ----------------------- | ----------- |
282
+ | `insertRunAt(index, run)` | Insert run at position | `this` |
283
+ | `removeRunAt(index)` | Remove run at position | `boolean` |
284
+ | `replaceRunAt(index, run)` | Replace run at position | `boolean` |
285
+ | `findText(text, options?)` | Find text in runs | `number[]` |
286
+ | `replaceText(find, replace, options?)` | Replace text in runs | `number` |
287
+ | `mergeWith(otherPara)` | Merge another paragraph | `this` |
288
+ | `clone()` | Clone paragraph | `Paragraph` |
289
+
290
+ **Example:**
291
+
292
+ ```typescript
293
+ const para = doc.createParagraph("Hello World");
294
+
295
+ // Find and replace
296
+ const indices = para.findText("World"); // [1]
297
+ const count = para.replaceText("World", "Universe", { caseSensitive: true });
298
+
299
+ // Manipulate runs
300
+ para.insertRunAt(0, new Run("Start: ", { bold: true }));
301
+ para.replaceRunAt(1, new Run("HELLO", { allCaps: true }));
302
+
303
+ // Merge paragraphs
304
+ const para2 = Paragraph.create(" More text");
305
+ para.mergeWith(para2); // Combines runs
306
+ ```
307
+
308
+ ### Run (Text Span) Operations
309
+
310
+ | Method | Description | Returns |
311
+ | ------------------------------- | ------------------------- | ------- |
312
+ | `clone()` | Clone run with formatting | `Run` |
313
+ | `insertText(index, text)` | Insert text at position | `this` |
314
+ | `appendText(text)` | Append text to end | `this` |
315
+ | `replaceText(start, end, text)` | Replace text range | `this` |
316
+
317
+ **Example:**
318
+
319
+ ```typescript
320
+ const run = new Run("Hello World", { bold: true });
321
+
322
+ // Text manipulation
323
+ run.insertText(6, "Beautiful "); // "Hello Beautiful World"
324
+ run.appendText("!"); // "Hello Beautiful World!"
325
+ run.replaceText(0, 5, "Hi"); // "Hi Beautiful World!"
326
+
327
+ // Clone for reuse
328
+ const copy = run.clone();
329
+ copy.setColor("FF0000"); // Original unchanged
330
+ ```
331
+
332
+ ### Table Operations
333
+
334
+ | Method | Description | Example |
335
+ | ----------------------- | -------------------- | ---------------------------------------- |
336
+ | `getRow(index)` | Get table row | `table.getRow(0)` |
337
+ | `getCell(row, col)` | Get table cell | `table.getCell(0, 1)` |
338
+ | `addRow()` | Add new row | `table.addRow()` |
339
+ | `removeRow(index)` | Remove row | `table.removeRow(2)` |
340
+ | `insertColumn(index)` | Insert column | `table.insertColumn(1)` |
341
+ | `removeColumn(index)` | Remove column | `table.removeColumn(3)` |
342
+ | `setWidth(twips)` | Set table width | `table.setWidth(8640)` |
343
+ | `setAlignment(align)` | Table alignment | `table.setAlignment('center')` |
344
+ | `setAllBorders(border)` | Set all borders | `table.setAllBorders({style: 'single'})` |
345
+ | `setBorders(borders)` | Set specific borders | `table.setBorders({top: {...}})` |
346
+
347
+ #### Advanced Table Operations
348
+
349
+ | Method | Description | Returns |
350
+ | ------------------------------------------------ | ------------------------- | ------------ |
351
+ | `mergeCells(startRow, startCol, endRow, endCol)` | Merge cells | `this` |
352
+ | `splitCell(row, col)` | Remove cell spanning | `this` |
353
+ | `moveCell(fromRow, fromCol, toRow, toCol)` | Move cell contents | `this` |
354
+ | `swapCells(row1, col1, row2, col2)` | Swap two cells | `this` |
355
+ | `setColumnWidth(index, width)` | Set specific column width | `this` |
356
+ | `setColumnWidths(widths)` | Set all column widths | `this` |
357
+ | `insertRows(startIndex, count)` | Insert multiple rows | `TableRow[]` |
358
+ | `removeRows(startIndex, count)` | Remove multiple rows | `boolean` |
359
+ | `clone()` | Clone entire table | `Table` |
360
+
361
+ **Example:**
362
+
363
+ ```typescript
364
+ const table = doc.createTable(3, 3);
365
+
366
+ // Merge cells horizontally (row 0, columns 0-2)
367
+ table.mergeCells(0, 0, 0, 2);
368
+
369
+ // Move cell contents
370
+ table.moveCell(1, 1, 2, 2);
371
+
372
+ // Swap cells
373
+ table.swapCells(0, 0, 2, 2);
374
+
375
+ // Batch row operations
376
+ table.insertRows(1, 3); // Insert 3 rows at position 1
377
+ table.removeRows(4, 2); // Remove 2 rows starting at position 4
378
+
379
+ // Set column widths
380
+ table.setColumnWidth(0, 2000); // First column = 2000 twips
381
+ table.setColumnWidths([2000, 3000, 2000]); // All columns
382
+
383
+ // Clone table for reuse
384
+ const tableCopy = table.clone();
385
+ ```
386
+
387
+ ### Table Cell Operations
388
+
389
+ | Method | Description | Example |
390
+ | ----------------------------- | --------------------- | ------------------------------------- |
391
+ | `createParagraph(text?)` | Add paragraph to cell | `cell.createParagraph('Text')` |
392
+ | `setShading(shading)` | Cell background | `cell.setShading({fill: 'E0E0E0'})` |
393
+ | `setVerticalAlignment(align)` | Vertical align | `cell.setVerticalAlignment('center')` |
394
+ | `setColumnSpan(cols)` | Merge columns | `cell.setColumnSpan(3)` |
395
+ | `setRowSpan(rows)` | Merge rows | `cell.setRowSpan(2)` |
396
+ | `setBorders(borders)` | Cell borders | `cell.setBorders({top: {...}})` |
397
+ | `setWidth(width, type?)` | Cell width | `cell.setWidth(2000, 'dxa')` |
398
+
399
+ ### Style Management
400
+
401
+ | Method | Description | Example |
402
+ | ----------------------------- | ------------------ | ---------------------------------- |
403
+ | `addStyle(style)` | Add custom style | `doc.addStyle(myStyle)` |
404
+ | `getStyle(styleId)` | Get style by ID | `doc.getStyle('Heading1')` |
405
+ | `hasStyle(styleId)` | Check style exists | `doc.hasStyle('CustomStyle')` |
406
+ | `getStyles()` | Get all styles | `doc.getStyles()` |
407
+ | `removeStyle(styleId)` | Remove style | `doc.removeStyle('OldStyle')` |
408
+ | `updateStyle(styleId, props)` | Update style | `doc.updateStyle('Normal', {...})` |
409
+
410
+ #### Style Manipulation
411
+
412
+ | Method | Description | Returns |
413
+ | ------------------------ | ----------------------------------- | ------- |
414
+ | `style.clone()` | Clone style | `Style` |
415
+ | `style.mergeWith(other)` | Merge properties from another style | `this` |
416
+
417
+ **Example:**
418
+
419
+ ```typescript
420
+ // Clone a style
421
+ const heading1 = doc.getStyle("Heading1");
422
+ const customHeading = heading1.clone();
423
+ customHeading.setRunFormatting({ color: "FF0000" });
424
+
425
+ // Merge styles
426
+ const baseStyle = Style.createNormalStyle();
427
+ const overrideStyle = Style.create({
428
+ styleId: "Override",
429
+ name: "Override",
430
+ type: "paragraph",
431
+ runFormatting: { bold: true, color: "FF0000" },
432
+ });
433
+ baseStyle.mergeWith(overrideStyle); // baseStyle now has bold red text
434
+ ```
435
+
436
+ #### Built-in Styles
437
+
438
+ - `Normal` - Default paragraph
439
+ - `Title` - Document title
440
+ - `Subtitle` - Document subtitle
441
+ - `Heading1` through `Heading9` - Section headings
442
+ - `ListParagraph` - List items
443
+
444
+ ### List Management
445
+
446
+ | Method | Description | Returns |
447
+ | --------------------------------------- | ----------------------- | ------- |
448
+ | `createBulletList(levels?, bullets?)` | Create bullet list | `numId` |
449
+ | `createNumberedList(levels?, formats?)` | Create numbered list | `numId` |
450
+ | `createMultiLevelList()` | Create multi-level list | `numId` |
451
+
452
+ ### Table of Contents (TOC)
453
+
454
+ #### Basic TOC Creation
455
+
456
+ | Method | Description | Example |
457
+ | -------------------------- | ---------------------- | -------------------------- |
458
+ | `addTableOfContents(toc?)` | Add TOC to document | `doc.addTableOfContents()` |
459
+ | `insertTocAt(index, toc)` | Insert TOC at position | `doc.insertTocAt(0, toc)` |
460
+ | `removeTocAt(index)` | Remove TOC at position | `doc.removeTocAt(0)` |
461
+
462
+ #### TOC Factory Methods
463
+
464
+ | Method | Description | Example |
465
+ | -------------------------------------------------------- | ------------------------ | ---------------------------------------------------- |
466
+ | `TableOfContents.createStandard(title?)` | Standard TOC (3 levels) | `TableOfContents.createStandard()` |
467
+ | `TableOfContents.createSimple(title?)` | Simple TOC (2 levels) | `TableOfContents.createSimple()` |
468
+ | `TableOfContents.createDetailed(title?)` | Detailed TOC (4 levels) | `TableOfContents.createDetailed()` |
469
+ | `TableOfContents.createHyperlinked(title?)` | Hyperlinked TOC | `TableOfContents.createHyperlinked()` |
470
+ | `TableOfContents.createNoPageNumbers(opts?)` | TOC without page numbers | `TableOfContents.createNoPageNumbers()` |
471
+ | `TableOfContents.createWithStyles(styles, opts?)` | TOC with specific styles | `TableOfContents.createWithStyles(['H1','H3'])` |
472
+ | `TableOfContents.createFlat(title?, styles?)` | Flat TOC (no indent) | `TableOfContents.createFlat()` |
473
+ | `TableOfContents.createNumbered(title?, format?)` | Numbered TOC | `TableOfContents.createNumbered('TOC', 'roman')` |
474
+ | `TableOfContents.createWithSpacing(spacing, opts?)` | TOC with custom spacing | `TableOfContents.createWithSpacing(120)` |
475
+ | `TableOfContents.createWithHyperlinkColor(color, opts?)` | Custom hyperlink color | `TableOfContents.createWithHyperlinkColor('FF0000')` |
476
+
477
+ **Note:** All TOC elements are automatically wrapped in an SDT (Structured Document Tag) for native Word integration. This enables Word's "Update Table" button and provides better compatibility with Microsoft Word's TOC features.
478
+
479
+ #### TOC Configuration Methods
480
+
481
+ | Method | Description | Values |
482
+ | --------------------------------- | ------------------------------ | -------------------------- |
483
+ | `setIncludeStyles(styles)` | Select specific heading styles | `['Heading1', 'Heading3']` |
484
+ | `setNumbered(numbered, format?)` | Enable/disable numbering | `(true, 'roman')` |
485
+ | `setNoIndent(noIndent)` | Remove indentation | `true/false` |
486
+ | `setCustomIndents(indents)` | Custom indents per level | `[0, 200, 400]` (twips) |
487
+ | `setSpaceBetweenEntries(spacing)` | Spacing between entries | `120` (twips) |
488
+ | `setHyperlinkColor(color)` | Hyperlink color | `'0000FF'` (default blue) |
489
+ | `setHideInWebLayout(hide)` | Hide page numbers in web view | `true/false` |
490
+ | `configure(options)` | Bulk configuration | See example below |
491
+
492
+ #### TOC Properties
493
+
494
+ | Property | Type | Default | Description |
495
+ | --------------------- | ------------------------------------ | --------------------- | ---------------------------------- |
496
+ | `title` | `string` | `'Table of Contents'` | TOC title |
497
+ | `levels` | `number` (1-9) | `3` | Heading levels to include |
498
+ | `includeStyles` | `string[]` | `undefined` | Specific styles (overrides levels) |
499
+ | `showPageNumbers` | `boolean` | `true` | Show page numbers |
500
+ | `useHyperlinks` | `boolean` | `false` | Use hyperlinks instead of page #s |
501
+ | `hideInWebLayout` | `boolean` | `false` | Hide page numbers in web layout |
502
+ | `numbered` | `boolean` | `false` | Number TOC entries |
503
+ | `numberingFormat` | `'decimal'/'roman'/'alpha'` | `'decimal'` | Numbering format |
504
+ | `noIndent` | `boolean` | `false` | Remove all indentation |
505
+ | `customIndents` | `number[]` | `undefined` | Custom indents in twips |
506
+ | `spaceBetweenEntries` | `number` | `0` | Spacing in twips |
507
+ | `hyperlinkColor` | `string` | `'0000FF'` | Hyperlink color (hex without #) |
508
+ | `tabLeader` | `'dot'/'hyphen'/'underscore'/'none'` | `'dot'` | Tab leader character |
509
+
510
+ **Example:**
511
+
512
+ ```typescript
513
+ // Basic TOC
514
+ const simpleToc = TableOfContents.createStandard();
515
+ doc.addTableOfContents(simpleToc);
516
+
517
+ // Select specific styles (e.g., only Heading1 and Heading3)
518
+ const customToc = TableOfContents.createWithStyles(["Heading1", "Heading3"]);
519
+
520
+ // Flat TOC with no indentation
521
+ const flatToc = TableOfContents.createFlat("Contents");
522
+
523
+ // Numbered TOC with roman numerals
524
+ const numberedToc = TableOfContents.createNumbered(
525
+ "Table of Contents",
526
+ "roman"
527
+ );
528
+
529
+ // Custom hyperlink color (red instead of blue)
530
+ const coloredToc = TableOfContents.createWithHyperlinkColor("FF0000");
531
+
532
+ // Advanced configuration
533
+ const toc = TableOfContents.create()
534
+ .setIncludeStyles(["Heading1", "Heading2", "Heading3"])
535
+ .setNumbered(true, "decimal")
536
+ .setSpaceBetweenEntries(120) // 6pt spacing
537
+ .setHyperlinkColor("0000FF")
538
+ .setNoIndent(false);
539
+
540
+ // Or use configure() for bulk settings
541
+ toc.configure({
542
+ title: "Table of Contents",
543
+ includeStyles: ["Heading1", "CustomHeader"],
544
+ numbered: true,
545
+ numberingFormat: "alpha",
546
+ spaceBetweenEntries: 100,
547
+ hyperlinkColor: "FF0000",
548
+ noIndent: true,
549
+ });
550
+
551
+ // Insert at specific position
552
+ doc.insertTocAt(0, toc);
553
+ ```
554
+
555
+ ### Image Handling
556
+
557
+ | Method | Description | Example |
558
+ | --------------------------------------- | ------------------ | -------------------------------- |
559
+ | `Image.fromFile(path, width?, height?)` | Load from file | `Image.fromFile('pic.jpg')` |
560
+ | `Image.fromBuffer(buffer, ext, w?, h?)` | Load from buffer | `Image.fromBuffer(buf, 'png')` |
561
+ | `setWidth(emus, maintainRatio?)` | Set width | `img.setWidth(inchesToEmus(3))` |
562
+ | `setHeight(emus, maintainRatio?)` | Set height | `img.setHeight(inchesToEmus(2))` |
563
+ | `setSize(width, height)` | Set dimensions | `img.setSize(w, h)` |
564
+ | `setRotation(degrees)` | Rotate image | `img.setRotation(90)` |
565
+ | `setAltText(text)` | Accessibility text | `img.setAltText('Description')` |
566
+
567
+ ### Hyperlinks
568
+
569
+ | Method | Description | Example |
570
+ | ------------------------------------------------- | ---------------- | ---------------------------------------------------------- |
571
+ | `Hyperlink.createExternal(url, text, format?)` | Web link | `Hyperlink.createExternal('https://example.com', 'Click')` |
572
+ | `Hyperlink.createEmail(email, text?, format?)` | Email link | `Hyperlink.createEmail('user@example.com')` |
573
+ | `Hyperlink.createInternal(anchor, text, format?)` | Internal link | `Hyperlink.createInternal('Section1', 'Go to')` |
574
+ | `para.addHyperlink(hyperlink)` | Add to paragraph | `para.addHyperlink(link)` |
575
+
576
+ ### Headers & Footers
577
+
578
+ | Method | Description | Example |
579
+ | ---------------------------- | ------------------------ | -------------------------------- |
580
+ | `setHeader(header)` | Set default header | `doc.setHeader(myHeader)` |
581
+ | `setFooter(footer)` | Set default footer | `doc.setFooter(myFooter)` |
582
+ | `setFirstPageHeader(header)` | First page header | `doc.setFirstPageHeader(header)` |
583
+ | `setFirstPageFooter(footer)` | First page footer | `doc.setFirstPageFooter(footer)` |
584
+ | `setEvenPageHeader(header)` | Even page header | `doc.setEvenPageHeader(header)` |
585
+ | `setEvenPageFooter(footer)` | Even page footer | `doc.setEvenPageFooter(footer)` |
586
+ | `removeHeader(type)` | Remove specific header | `doc.removeHeader('default')` |
587
+ | `removeFooter(type)` | Remove specific footer | `doc.removeFooter('first')` |
588
+ | `clearHeaders()` | Remove all headers | `doc.clearHeaders()` |
589
+ | `clearFooters()` | Remove all footers | `doc.clearFooters()` |
590
+
591
+ ### Page Setup
592
+
593
+ | Method | Description | Example |
594
+ | ------------------------------------- | ----------------- | ------------------------------------- |
595
+ | `setPageSize(width, height, orient?)` | Page dimensions | `doc.setPageSize(12240, 15840)` |
596
+ | `setPageOrientation(orientation)` | Page orientation | `doc.setPageOrientation('landscape')` |
597
+ | `setMargins(margins)` | Page margins | `doc.setMargins({top: 1440, ...})` |
598
+ | `setLanguage(language)` | Document language | `doc.setLanguage('en-US')` |
599
+
600
+ ### Document Properties
601
+
602
+ | Method | Description | Properties |
603
+ | ---------------------- | ------------ | ------------------------------------- |
604
+ | `setProperties(props)` | Set metadata | `{title, subject, creator, keywords}` |
605
+ | `getProperties()` | Get metadata | Returns all properties |
606
+
607
+ ### Advanced Features
608
+
609
+ #### Bookmarks
610
+
611
+ | Method | Description |
612
+ | ---------------------------------------- | ------------------- |
613
+ | `createBookmark(name)` | Create bookmark |
614
+ | `createHeadingBookmark(text)` | Auto-named bookmark |
615
+ | `getBookmark(name)` | Get by name |
616
+ | `hasBookmark(name)` | Check existence |
617
+ | `addBookmarkToParagraph(para, bookmark)` | Add to paragraph |
618
+
619
+ #### Comments
620
+
621
+ | Method | Description |
622
+ | ------------------------------------------- | ----------------- |
623
+ | `createComment(author, content, initials?)` | Add comment |
624
+ | `createReply(parentId, author, content)` | Reply to comment |
625
+ | `getComment(id)` | Get by ID |
626
+ | `getAllComments()` | Get all top-level |
627
+ | `addCommentToParagraph(para, comment)` | Add to paragraph |
628
+
629
+ #### Track Changes
630
+
631
+ | Method | Description |
632
+ | ------------------------------------ | ----------------- |
633
+ | `trackInsertion(para, author, text)` | Track insertion |
634
+ | `trackDeletion(para, author, text)` | Track deletion |
635
+ | `isTrackingChanges()` | Check if tracking |
636
+ | `getRevisionStats()` | Get statistics |
637
+
638
+ #### Footnotes & Endnotes
639
+
640
+ | Method | Description |
641
+ | -------------------------- | ---------------- |
642
+ | `FootnoteManager.create()` | Manage footnotes |
643
+ | `EndnoteManager.create()` | Manage endnotes |
644
+
645
+ #### Document Helper Functions
646
+
647
+ High-level helper methods for common document formatting tasks:
648
+
649
+ | Method | Description |
650
+ | ---------------------------------------- | ------------------------------------------------------------------------------------------- |
651
+ | `applyCustomFormattingToExistingStyles(options?)`| Modify Heading1, Heading2, Heading3, Normal, and List Paragraph styles with custom or default formatting (Verdana font, specific spacing, single line spacing). Heading2 paragraphs are wrapped in tables. Accepts optional configuration for full customization. |
652
+ | `applyStylesFromObjects(...styles)` | Helper function that accepts Style objects and applies their formatting. Converts Style objects to configuration format and calls applyCustomFormattingToExistingStyles(). |
653
+ | `hideTableOfContentsPageNumbers()` | Hide page numbers in all TOC elements in the document; returns count of TOCs modified |
654
+ | `wrapParagraphInTable(para, options?)` | Wrap a paragraph in a 1x1 table with optional shading, margins, and width settings |
655
+ | `isParagraphInTable(para)` | Check if a paragraph is inside a table; returns `{inTable: boolean, cell?: TableCell}` |
656
+ | `updateAllHyperlinkColors(color)` | Set all hyperlinks in the document to a specific color (e.g., '0000FF' for blue) |
657
+ | `removeAllHeadersFooters()` | Remove all headers and footers from the document; returns count of headers/footers removed |
658
+
659
+ **Example - Using Helper Functions:**
660
+
661
+ ```typescript
662
+ import { Document } from 'docxmlater';
663
+
664
+ const doc = await Document.load('document.docx');
665
+
666
+ // Apply default formatting to standard styles (Verdana, current spacing)
667
+ const results = doc.applyCustomFormattingToExistingStyles();
668
+ console.log(`Modified styles:`, results);
669
+ // Output: { heading1: true, heading2: true, heading3: true, normal: true, listParagraph: true }
670
+
671
+ // Or apply custom formatting with config objects
672
+ doc.applyCustomFormattingToExistingStyles({
673
+ heading1: {
674
+ run: { font: 'Arial', size: 16, bold: true, color: '000000' },
675
+ paragraph: { spacing: { before: 0, after: 200, line: 240, lineRule: 'auto' } }
676
+ },
677
+ heading2: {
678
+ run: { font: 'Arial', size: 14, bold: true },
679
+ paragraph: { spacing: { before: 100, after: 100 } },
680
+ tableOptions: { shading: '808080', marginLeft: 150, marginRight: 150 }
681
+ }
682
+ });
683
+
684
+ // Alternative: Use Style objects instead of config
685
+ import { Style } from 'docxmlater';
686
+
687
+ const h1 = new Style({
688
+ styleId: 'Heading1',
689
+ name: 'Heading 1',
690
+ type: 'paragraph',
691
+ runFormatting: { font: 'Arial', size: 16, bold: true },
692
+ paragraphFormatting: { spacing: { before: 0, after: 200 } }
693
+ });
694
+
695
+ const h2 = Style.createHeadingStyle(2);
696
+ h2.setRunFormatting({ font: 'Arial', size: 14, bold: true });
697
+ h2.setHeading2TableOptions({ shading: '808080', marginLeft: 150, marginRight: 150 });
698
+
699
+ doc.applyStylesFromObjects(h1, h2);
700
+
701
+ // Hide page numbers in all TOC elements
702
+ const tocCount = doc.hideTableOfContentsPageNumbers();
703
+ console.log(`Hid page numbers in ${tocCount} TOCs`);
704
+
705
+ // Set all hyperlinks to blue
706
+ doc.updateAllHyperlinkColors('0000FF');
707
+
708
+ // Wrap a specific paragraph in a table
709
+ const para = doc.getParagraphs()[0];
710
+ doc.wrapParagraphInTable(para, {
711
+ shading: 'BFBFBF', // Gray background
712
+ marginLeft: 101, // 5pt margins
713
+ marginRight: 101,
714
+ tableWidthPercent: 5000 // 100% width
715
+ });
716
+
717
+ // Check if a paragraph is in a table
718
+ const { inTable, cell } = doc.isParagraphInTable(para);
719
+ if (inTable && cell) {
720
+ console.log('Paragraph is in a table cell');
721
+ cell.setShading({ fill: 'FFFF00' }); // Change to yellow
722
+ }
723
+
724
+ // Remove all headers and footers
725
+ const removedCount = doc.removeAllHeadersFooters();
726
+ console.log(`Removed ${removedCount} headers and footers`);
727
+
728
+ await doc.save('formatted.docx');
729
+ ```
730
+
731
+ **Note on `applyCustomFormattingToExistingStyles(options?)`:**
732
+
733
+ This helper function modifies existing style definitions with custom or default formatting. All parameters are optional.
734
+
735
+ **Default formatting (when no options provided):**
736
+ - **Heading1**: 18pt black bold Verdana, left aligned, 0pt before/12pt after, single line spacing
737
+ - **Heading2**: 14pt black bold Verdana, left aligned, 6pt before/after, single line spacing, wrapped in gray tables (100% width, 0.08" margins)
738
+ - **Heading3**: 12pt black bold Verdana, left aligned, 3pt before/after, single line spacing (no table wrapping)
739
+ - **Normal**: 12pt Verdana, left aligned, 3pt before/after, single line spacing
740
+ - **List Paragraph**: 12pt Verdana, left aligned, 0pt before/3pt after, single line spacing, 0.25" bullet indent/0.50" text indent, contextual spacing enabled
741
+
742
+ **Customization:**
743
+ - Pass custom `ApplyCustomFormattingOptions` to override any style's run formatting (font, size, bold, italic, underline, color) or paragraph formatting (alignment, spacing, indentation, contextual spacing)
744
+ - Heading2 table options are configurable: shading, margins, and table width
745
+ - All styles have italic and underline formatting cleared to ensure style consistency
746
+
747
+ **Heading2 table wrapping behavior:**
748
+ - Empty Heading2 paragraphs are skipped (not wrapped in tables)
749
+ - Heading2 paragraphs already in tables have their cell formatted with provided or default options
750
+ - Heading2 paragraphs not in tables are wrapped in new 1x1 tables
751
+ - Table appearance is fully customizable via `options.heading2.tableOptions`
752
+
753
+ **Note:** Per ECMA-376 §17.7.2, direct formatting in document.xml overrides style definitions. This method automatically clears conflicting direct formatting to ensure style changes take effect.
754
+
755
+ **Related helpers:**
756
+ - Use `hideTableOfContentsPageNumbers()` to hide page numbers in TOCs
757
+ - Use `updateAllHyperlinkColors(color)` to change hyperlink colors
758
+
759
+ ### Low-Level Document Parts
760
+
761
+ | Method | Description | Example |
762
+ | ---------------------------- | --------------------- | ------------------------------------------------- |
763
+ | `getPart(partName)` | Get document part | `doc.getPart('word/document.xml')` |
764
+ | `setPart(partName, content)` | Set document part | `doc.setPart('custom.xml', data)` |
765
+ | `removePart(partName)` | Remove part | `doc.removePart('custom.xml')` |
766
+ | `listParts()` | List all parts | `const parts = await doc.listParts()` |
767
+ | `partExists(partName)` | Check part exists | `if (await doc.partExists('...'))` |
768
+ | `getContentTypes()` | Get content types | `const types = await doc.getContentTypes()` |
769
+ | `addContentType(part, type)` | Register content type | `doc.addContentType('.json', 'application/json')` |
770
+
771
+ ### Unit Conversion Utilities
772
+
773
+ #### Twips Conversions
774
+ | Function | Description | Example |
775
+ | ------------------------- | ------------------- | --------------------------- |
776
+ | `twipsToPoints(twips)` | Twips to points | `twipsToPoints(240)` // 12 |
777
+ | `twipsToInches(twips)` | Twips to inches | `twipsToInches(1440)` // 1 |
778
+ | `twipsToCm(twips)` | Twips to cm | `twipsToCm(1440)` // 2.54 |
779
+ | `twipsToEmus(twips)` | Twips to EMUs | `twipsToEmus(1440)` |
780
+
781
+ #### EMUs (English Metric Units) Conversions
782
+ | Function | Description | Example |
783
+ | --------------------------- | -------------------- | ----------------------------- |
784
+ | `emusToTwips(emus)` | EMUs to twips | `emusToTwips(914400)` // 1440 |
785
+ | `emusToInches(emus)` | EMUs to inches | `emusToInches(914400)` // 1 |
786
+ | `emusToCm(emus)` | EMUs to cm | `emusToCm(914400)` // 2.54 |
787
+ | `emusToPoints(emus)` | EMUs to points | `emusToPoints(914400)` // 72 |
788
+ | `emusToPixels(emus, dpi?)` | EMUs to pixels | `emusToPixels(914400)` // 96 |
789
+
790
+ #### Points Conversions
791
+ | Function | Description | Example |
792
+ | ------------------------ | ------------------ | -------------------------- |
793
+ | `pointsToTwips(points)` | Points to twips | `pointsToTwips(12)` // 240 |
794
+ | `pointsToEmus(points)` | Points to EMUs | `pointsToEmus(72)` |
795
+ | `pointsToInches(points)` | Points to inches | `pointsToInches(72)` // 1 |
796
+ | `pointsToCm(points)` | Points to cm | `pointsToCm(72)` // 2.54 |
797
+
798
+ #### Inches Conversions
799
+ | Function | Description | Example |
800
+ | ----------------------------- | ------------------- | ----------------------------- |
801
+ | `inchesToTwips(inches)` | Inches to twips | `inchesToTwips(1)` // 1440 |
802
+ | `inchesToEmus(inches)` | Inches to EMUs | `inchesToEmus(1)` // 914400 |
803
+ | `inchesToPoints(inches)` | Inches to points | `inchesToPoints(1)` // 72 |
804
+ | `inchesToCm(inches)` | Inches to cm | `inchesToCm(1)` // 2.54 |
805
+ | `inchesToPixels(inches, dpi)` | Inches to pixels | `inchesToPixels(1, 96)` // 96 |
806
+
807
+ #### Centimeters Conversions
808
+ | Function | Description | Example |
809
+ | ----------------------- | ---------------- | --------------------------- |
810
+ | `cmToTwips(cm)` | cm to twips | `cmToTwips(2.54)` // 1440 |
811
+ | `cmToEmus(cm)` | cm to EMUs | `cmToEmus(2.54)` // 914400 |
812
+ | `cmToInches(cm)` | cm to inches | `cmToInches(2.54)` // 1 |
813
+ | `cmToPoints(cm)` | cm to points | `cmToPoints(2.54)` // 72 |
814
+ | `cmToPixels(cm, dpi?)` | cm to pixels | `cmToPixels(2.54, 96)` // 96|
815
+
816
+ #### Pixels Conversions
817
+ | Function | Description | Example |
818
+ | ---------------------------- | ------------------- | ------------------------------ |
819
+ | `pixelsToEmus(pixels, dpi?)` | Pixels to EMUs | `pixelsToEmus(96)` // 914400 |
820
+ | `pixelsToInches(pixels, dpi?)`| Pixels to inches | `pixelsToInches(96, 96)` // 1 |
821
+ | `pixelsToTwips(pixels, dpi?)`| Pixels to twips | `pixelsToTwips(96, 96)` // 1440|
822
+ | `pixelsToCm(pixels, dpi?)` | Pixels to cm | `pixelsToCm(96, 96)` // 2.54 |
823
+ | `pixelsToPoints(pixels, dpi?)`| Pixels to points | `pixelsToPoints(96, 96)` // 72 |
824
+
825
+ **Note:** Default DPI is 96 for pixel conversions
826
+
827
+ ### ZIP Archive Helper Methods
828
+
829
+ #### File Operations
830
+ | Method | Description | Example |
831
+ | ------------------------------- | ------------------------- | -------------------------------------------- |
832
+ | `addFile(path, content)` | Add file to archive | `handler.addFile('doc.xml', xmlContent)` |
833
+ | `updateFile(path, content)` | Update existing file | `handler.updateFile('doc.xml', newContent)` |
834
+ | `removeFile(path)` | Remove file from archive | `handler.removeFile('old.xml')` |
835
+ | `renameFile(oldPath, newPath)` | Rename file | `handler.renameFile('a.xml', 'b.xml')` |
836
+ | `copyFile(srcPath, destPath)` | Copy file | `handler.copyFile('a.xml', 'copy-a.xml')` |
837
+ | `moveFile(srcPath, destPath)` | Move file | `handler.moveFile('a.xml', 'folder/a.xml')` |
838
+
839
+ #### File Retrieval
840
+ | Method | Description | Returns |
841
+ | ------------------------- | ---------------------- | --------------- |
842
+ | `getFile(path)` | Get file object | `ZipFile` |
843
+ | `getFileAsString(path)` | Get file as string | `string` |
844
+ | `getFileAsBuffer(path)` | Get file as buffer | `Buffer` |
845
+ | `hasFile(path)` | Check if file exists | `boolean` |
846
+ | `getFilePaths()` | Get all file paths | `string[]` |
847
+ | `getAllFiles()` | Get all files | `FileMap` |
848
+
849
+ #### Batch Operations
850
+ | Method | Description | Returns |
851
+ | ------------------------------- | ---------------------------- | -------------- |
852
+ | `removeFiles(paths[])` | Remove multiple files | `number` |
853
+ | `getFilesByExtension(ext)` | Get files by extension | `ZipFile[]` |
854
+ | `getTextFiles()` | Get all text files | `ZipFile[]` |
855
+ | `getBinaryFiles()` | Get all binary files | `ZipFile[]` |
856
+ | `getMediaFiles()` | Get media files | `ZipFile[]` |
857
+
858
+ #### Archive Information
859
+ | Method | Description | Returns |
860
+ | ------------------ | ------------------------- | ------------------------ |
861
+ | `getFileCount()` | Count files in archive | `number` |
862
+ | `getTotalSize()` | Get total size in bytes | `number` |
863
+ | `getStats()` | Get detailed statistics | `{fileCount, size, ...}` |
864
+ | `isEmpty()` | Check if archive is empty | `boolean` |
865
+
866
+ #### Import/Export
867
+ | Method | Description | Returns |
868
+ | -------------------------------- | ------------------------ | -------------------- |
869
+ | `exportFile(internal, external)` | Export file from archive | `Promise<void>` |
870
+ | `importFile(external, internal)` | Import file to archive | `Promise<void>` |
871
+
872
+ ## Common Recipes
873
+
874
+ ### Create a Simple Document
875
+
876
+ ```typescript
877
+ const doc = Document.create();
878
+ doc.createParagraph("Title").setStyle("Title");
879
+ doc.createParagraph("This is a simple document.");
880
+ await doc.save("simple.docx");
881
+ ```
882
+
883
+ ### Add Formatted Text
884
+
885
+ ```typescript
886
+ const para = doc.createParagraph();
887
+ para.addText("Bold", { bold: true });
888
+ para.addText(" and ");
889
+ para.addText("Colored", { color: "FF0000" });
890
+ ```
891
+
892
+ ### Create a Table with Borders
893
+
894
+ ```typescript
895
+ const table = doc.createTable(3, 3);
896
+ table.setAllBorders({ style: "single", size: 8, color: "000000" });
897
+ table.getCell(0, 0)?.createParagraph("Header 1");
898
+ table.getRow(0)?.getCell(0)?.setShading({ fill: "4472C4" });
899
+ ```
900
+
901
+ ### Insert an Image
902
+
903
+ ```typescript
904
+ import { Image, inchesToEmus } from "docxmlater";
905
+
906
+ const image = Image.fromFile("./photo.jpg");
907
+ image.setWidth(inchesToEmus(4), true); // 4 inches, maintain ratio
908
+ doc.addImage(image);
909
+ ```
910
+
911
+ ### Add a Hyperlink
912
+
913
+ ```typescript
914
+ const para = doc.createParagraph();
915
+ para.addText("Visit ");
916
+ para.addHyperlink(
917
+ Hyperlink.createExternal("https://example.com", "our website")
918
+ );
919
+ ```
920
+
921
+ ### Search and Replace Text
922
+
923
+ ```typescript
924
+ // Find all occurrences
925
+ const results = doc.findText("old text", { caseSensitive: true });
926
+ console.log(`Found ${results.length} occurrences`);
927
+
928
+ // Replace all
929
+ const count = doc.replaceText("old text", "new text", { wholeWord: true });
930
+ console.log(`Replaced ${count} occurrences`);
931
+ ```
932
+
933
+ ### Load and Modify Existing Document
934
+
935
+ ```typescript
936
+ const doc = await Document.load("existing.docx");
937
+ doc.createParagraph("Added paragraph");
938
+
939
+ // Update all hyperlinks
940
+ const urlMap = new Map([["https://old-site.com", "https://new-site.com"]]);
941
+ doc.updateHyperlinkUrls(urlMap);
942
+
943
+ await doc.save("modified.docx");
944
+ ```
945
+
946
+ ### Create Lists
947
+
948
+ ```typescript
949
+ // Bullet list
950
+ const bulletId = doc.createBulletList(3);
951
+ doc.createParagraph("First item").setNumbering(bulletId, 0);
952
+ doc.createParagraph("Second item").setNumbering(bulletId, 0);
953
+
954
+ // Numbered list
955
+ const numberId = doc.createNumberedList(3);
956
+ doc.createParagraph("Step 1").setNumbering(numberId, 0);
957
+ doc.createParagraph("Step 2").setNumbering(numberId, 0);
958
+ ```
959
+
960
+ ### Apply Custom Styles
961
+
962
+ ```typescript
963
+ import { Style } from "docxmlater";
964
+
965
+ const customStyle = Style.create({
966
+ styleId: "CustomHeading",
967
+ name: "Custom Heading",
968
+ basedOn: "Normal",
969
+ runFormatting: { bold: true, size: 14, color: "2E74B5" },
970
+ paragraphFormatting: { alignment: "center", spaceAfter: 240 },
971
+ });
972
+
973
+ doc.addStyle(customStyle);
974
+ doc.createParagraph("Custom Styled Text").setStyle("CustomHeading");
975
+ ```
976
+
977
+ ### Build Content with Detached Paragraphs
978
+
979
+ Create paragraphs independently and add them conditionally:
980
+
981
+ ```typescript
982
+ import { Paragraph } from "docxmlater";
983
+
984
+ // Create reusable paragraph templates
985
+ const warningTemplate = Paragraph.createFormatted(
986
+ "WARNING: ",
987
+ { bold: true, color: "FF6600" },
988
+ { spacing: { before: 120, after: 120 } }
989
+ );
990
+
991
+ // Clone and customize
992
+ const warning1 = warningTemplate.clone();
993
+ warning1.addText("Please read the documentation before proceeding.");
994
+
995
+ // Build content from data
996
+ const items = [
997
+ { title: "First Item", description: "Description here" },
998
+ { title: "Second Item", description: "Another description" },
999
+ ];
1000
+
1001
+ items.forEach((item, index) => {
1002
+ const titlePara = Paragraph.create(`${index + 1}. `);
1003
+ titlePara.addText(item.title, { bold: true });
1004
+
1005
+ const descPara = Paragraph.create(item.description, {
1006
+ indentation: { left: 360 },
1007
+ });
1008
+
1009
+ doc.addParagraph(titlePara);
1010
+ doc.addParagraph(descPara);
1011
+ });
1012
+
1013
+ // See examples/advanced/detached-paragraphs.ts for more patterns
1014
+ ```
1015
+
1016
+ ### Add Headers and Footers
1017
+
1018
+ ```typescript
1019
+ import { Header, Footer, Field } from "docxmlater";
1020
+
1021
+ // Header with page numbers
1022
+ const header = Header.create();
1023
+ header.addParagraph("Document Title").setAlignment("center");
1024
+
1025
+ // Footer with page numbers
1026
+ const footer = Footer.create();
1027
+ const footerPara = footer.addParagraph();
1028
+ footerPara.addText("Page ");
1029
+ footerPara.addField(Field.create({ type: "PAGE" }));
1030
+ footerPara.addText(" of ");
1031
+ footerPara.addField(Field.create({ type: "NUMPAGES" }));
1032
+
1033
+ doc.setHeader(header);
1034
+ doc.setFooter(footer);
1035
+ ```
1036
+
1037
+ ### Work with Document Statistics
1038
+
1039
+ ```typescript
1040
+ // Get word and character counts
1041
+ console.log("Words:", doc.getWordCount());
1042
+ console.log("Characters:", doc.getCharacterCount());
1043
+ console.log("Characters (no spaces):", doc.getCharacterCount(false));
1044
+
1045
+ // Check document size
1046
+ const size = doc.estimateSize();
1047
+ if (size.warning) {
1048
+ console.warn(size.warning);
1049
+ }
1050
+ console.log(`Estimated size: ${size.totalEstimatedMB} MB`);
1051
+ ```
1052
+
1053
+ ### Handle Large Documents Efficiently
1054
+
1055
+ ```typescript
1056
+ const doc = Document.create({
1057
+ maxMemoryUsagePercent: 80,
1058
+ maxRssMB: 2048,
1059
+ maxImageCount: 50,
1060
+ maxTotalImageSizeMB: 100,
1061
+ });
1062
+
1063
+ // Process document...
1064
+
1065
+ // Clean up resources after saving
1066
+ await doc.save("large-document.docx");
1067
+ doc.dispose(); // Free memory
1068
+ ```
1069
+
1070
+ ### Direct XML Access (Advanced)
1071
+
1072
+ ```typescript
1073
+ // Get raw XML
1074
+ const documentXml = await doc.getPart("word/document.xml");
1075
+ console.log(documentXml?.content);
1076
+
1077
+ // Modify raw XML (use with caution)
1078
+ await doc.setPart("word/custom.xml", "<custom>data</custom>");
1079
+ await doc.addContentType("/word/custom.xml", "application/xml");
1080
+
1081
+ // List all parts
1082
+ const parts = await doc.listParts();
1083
+ console.log("Document contains:", parts.length, "parts");
1084
+ ```
1085
+
1086
+ ## Features
1087
+
1088
+ - **Full OpenXML Compliance** - Follows ECMA-376 standard
1089
+ - **TypeScript First** - Complete type definitions
1090
+ - **Memory Efficient** - Handles large documents with streaming
1091
+ - **Atomic Saves** - Prevents corruption with temp file pattern
1092
+ - **Rich Formatting** - Complete text and paragraph formatting
1093
+ - **Tables** - Full support with borders, shading, merging
1094
+ - **Images** - PNG, JPEG, GIF with sizing and positioning
1095
+ - **Hyperlinks** - External, internal, and email links
1096
+ - **Styles** - 13 built-in styles + custom style creation
1097
+ - **Lists** - Bullets, numbering, multi-level
1098
+ - **Headers/Footers** - Different first/even/odd pages
1099
+ - **Search & Replace** - With case and whole word options
1100
+ - **Document Stats** - Word count, character count, size estimation
1101
+ - **Track Changes** - Insertions and deletions with authors
1102
+ - **Comments** - With replies and threading
1103
+ - **Bookmarks** - For internal navigation
1104
+ - **Low-level Access** - Direct ZIP and XML manipulation
1105
+
1106
+ ## Performance
1107
+
1108
+ - Process 100+ page documents efficiently
1109
+ - Atomic save pattern prevents corruption
1110
+ - Memory management for large files
1111
+ - Lazy loading of document parts
1112
+ - Resource cleanup with `dispose()`
1113
+
1114
+ ## Testing
1115
+
1116
+ ```bash
1117
+ npm test # Run all tests
1118
+ npm run test:watch # Watch mode
1119
+ npm run test:coverage # Coverage report
1120
+ ```
1121
+
1122
+ **Current:** 1,119 tests passing (97.3% pass rate) | 100% core functionality covered
1123
+
1124
+ ## Development
1125
+
1126
+ ```bash
1127
+ # Install dependencies
1128
+ npm install
1129
+
1130
+ # Build TypeScript
1131
+ npm run build
1132
+
1133
+ # Run examples
1134
+ npx ts-node examples/simple-document.ts
1135
+ ```
1136
+
1137
+ ## Project Structure
1138
+
1139
+ ```text
1140
+ src/
1141
+ ├── core/ # Document, Parser, Generator, Validator
1142
+ ├── elements/ # Paragraph, Run, Table, Image, Hyperlink
1143
+ ├── formatting/ # Style, NumberingManager
1144
+ ├── xml/ # XMLBuilder, XMLParser
1145
+ ├── zip/ # ZipHandler for DOCX manipulation
1146
+ └── utils/ # Validation, Units conversion
1147
+
1148
+ examples/
1149
+ ├── 01-basic/ # Simple document creation
1150
+ ├── 02-text/ # Text formatting examples
1151
+ ├── 03-tables/ # Table examples
1152
+ ├── 04-styles/ # Style examples
1153
+ ├── 05-images/ # Image handling
1154
+ ├── 06-complete/ # Full document examples
1155
+ └── 07-hyperlinks/ # Link examples
1156
+ ```
1157
+
1158
+ ## Hierarchy
1159
+
1160
+ ```text
1161
+ w:document (root)
1162
+ └── w:body (body container)
1163
+ ├── w:p (paragraph) [1..n]
1164
+ │ ├── w:pPr (paragraph properties) [0..1]
1165
+ │ │ ├── w:pStyle (style reference)
1166
+ │ │ ├── w:jc (justification/alignment)
1167
+ │ │ ├── w:ind (indentation)
1168
+ │ │ └── w:spacing (spacing before/after)
1169
+ │ ├── w:r (run) [1..n]
1170
+ │ │ ├── w:rPr (run properties) [0..1]
1171
+ │ │ │ ├── w:b (bold)
1172
+ │ │ │ ├── w:i (italic)
1173
+ │ │ │ ├── w:u (underline)
1174
+ │ │ │ ├── w:sz (font size)
1175
+ │ │ │ └── w:color (text color)
1176
+ │ │ └── w:t (text content) [1]
1177
+ │ ├── w:hyperlink (hyperlink) [0..n]
1178
+ │ │ └── w:r (run with hyperlink text)
1179
+ │ └── w:drawing (embedded image/shape) [0..n]
1180
+ ├── w:tbl (table) [1..n]
1181
+ │ ├── w:tblPr (table properties)
1182
+ │ └── w:tr (table row) [1..n]
1183
+ │ └── w:tc (table cell) [1..n]
1184
+ │ └── w:p (paragraph in cell)
1185
+ └── w:sectPr (section properties) [1] (must be last child of w:body)
1186
+ ```
1187
+
1188
+ ## Requirements
1189
+
1190
+ - Node.js 16+
1191
+ - TypeScript 5.0+ (for development)
1192
+
1193
+ ## Installation Options
1194
+
1195
+ ```bash
1196
+ # NPM
1197
+ npm install docxmlater
1198
+
1199
+ # Yarn
1200
+ yarn add docxmlater
1201
+
1202
+ # PNPM
1203
+ pnpm add docxmlater
1204
+ ```
1205
+
1206
+ ## Troubleshooting
1207
+
1208
+ ### XML Corruption in Text
1209
+
1210
+ **Problem**: Text displays with XML tags like `Important Information<w:t xml:space="preserve">1` in Word.
1211
+
1212
+ **Cause**: Passing XML-like strings to text methods instead of using the API properly.
1213
+
1214
+ ```typescript
1215
+ // WRONG - Will display escaped XML as literal text
1216
+ paragraph.addText("Important Information<w:t>1</w:t>");
1217
+ // Displays as: "Important Information<w:t>1</w:t>"
1218
+
1219
+ // CORRECT - Use separate text runs
1220
+ paragraph.addText("Important Information");
1221
+ paragraph.addText("1");
1222
+ // Displays as: "Important Information1"
1223
+
1224
+ // Or combine in one call
1225
+ paragraph.addText("Important Information 1");
1226
+ ```
1227
+
1228
+ **Detection**: Use the corruption detection utility to find issues:
1229
+
1230
+ ```typescript
1231
+ import { detectCorruptionInDocument } from "docxmlater";
1232
+
1233
+ const doc = await Document.load("file.docx");
1234
+ const report = detectCorruptionInDocument(doc);
1235
+
1236
+ if (report.isCorrupted) {
1237
+ console.log(report.summary);
1238
+ report.locations.forEach((loc) => {
1239
+ console.log(`Paragraph ${loc.paragraphIndex}, Run ${loc.runIndex}:`);
1240
+ console.log(` Original: ${loc.text}`);
1241
+ console.log(` Fixed: ${loc.suggestedFix}`);
1242
+ });
1243
+ }
1244
+ ```
1245
+
1246
+ **Auto-Cleaning**: XML patterns are automatically removed by default for defensive data handling:
1247
+
1248
+ ```typescript
1249
+ // Default behavior - auto-clean enabled
1250
+ const run = new Run("Text<w:t>value</w:t>");
1251
+ // Result: "Textvalue" (XML tags removed automatically)
1252
+
1253
+ // Disable auto-cleaning (for debugging)
1254
+ const run = new Run("Text<w:t>value</w:t>", { cleanXmlFromText: false });
1255
+ // Result: "Text<w:t>value</w:t>" (XML tags preserved, will display in Word)
1256
+ ```
1257
+
1258
+ **Why This Happens**: The framework correctly escapes XML special characters per the XML specification. When you pass XML tags as text, they are properly escaped (`<` becomes `&lt;`) and Word displays them as literal text, not as markup.
1259
+
1260
+ **The Right Approach**: Use the framework's API methods instead of embedding XML:
1261
+
1262
+ - Use `paragraph.addText()` multiple times for separate text runs
1263
+ - Use formatting options: `{bold: true}`, `{italic: true}`, etc.
1264
+ - Use `paragraph.addHyperlink()` for links
1265
+ - Don't pass XML strings to text methods
1266
+ - Don't try to embed `<w:t>` or other XML tags in your text
1267
+
1268
+ For more details, see the [corruption detection examples](examples/troubleshooting/).
1269
+
1270
+ ### Layout Conflicts (Massive Whitespace)
1271
+
1272
+ **Problem**: Documents show massive whitespace between paragraphs when opened in Word, even though the XML looks correct.
1273
+
1274
+ **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.
1275
+
1276
+ **Why This Causes Problems**:
1277
+
1278
+ - `pageBreakBefore` tells Word to insert a page break before the paragraph
1279
+ - `keepNext` tells Word to keep the paragraph with the next one (no break)
1280
+ - `keepLines` tells Word to keep all lines together (no break)
1281
+ - The combination creates layout conflicts that manifest as massive whitespace
1282
+
1283
+ **Automatic Conflict Resolution** (v0.28.2+):
1284
+
1285
+ The framework now automatically prevents these conflicts by **prioritizing keep properties over page breaks**:
1286
+
1287
+ ```typescript
1288
+ // When setting keepNext or keepLines, pageBreakBefore is automatically cleared
1289
+ const para = new Paragraph()
1290
+ .addText("Content")
1291
+ .setPageBreakBefore(true) // Set to true
1292
+ .setKeepNext(true); // Automatically clears pageBreakBefore
1293
+
1294
+ // Result: keepNext=true, pageBreakBefore=false (conflict resolved)
1295
+ ```
1296
+
1297
+ **Why This Priority?**
1298
+
1299
+ - Keep properties (`keepNext`/`keepLines`) represent explicit user intent to keep content together
1300
+ - Page breaks are often layout hints that may conflict with document flow
1301
+ - Removing `pageBreakBefore` eliminates whitespace while preserving the user's intention
1302
+
1303
+ **Parsing Documents**:
1304
+
1305
+ When loading existing DOCX files with conflicts, they are automatically resolved:
1306
+
1307
+ ```typescript
1308
+ // Load document with conflicts
1309
+ const doc = await Document.load("document-with-conflicts.docx");
1310
+
1311
+ // Conflicts are automatically resolved during parsing
1312
+ // keepNext/keepLines take priority, pageBreakBefore is removed
1313
+ ```
1314
+
1315
+ **How It Works**:
1316
+
1317
+ 1. When `setKeepNext(true)` is called, `pageBreakBefore` is automatically set to `false`
1318
+ 2. When `setKeepLines(true)` is called, `pageBreakBefore` is automatically set to `false`
1319
+ 3. When parsing documents, if both properties exist, `pageBreakBefore` is cleared
1320
+ 4. Keep properties win because they represent explicit user intent
1321
+
1322
+ **Manual Override**:
1323
+
1324
+ If you need a page break despite keep properties, set it after:
1325
+
1326
+ ```typescript
1327
+ const para = new Paragraph()
1328
+ .setKeepNext(true) // Set first
1329
+ .setPageBreakBefore(true); // Override - you explicitly want this conflict
1330
+
1331
+ // But note: This will cause layout issues (whitespace) in Word
1332
+ ```
1333
+
1334
+ ## Known Limitations
1335
+
1336
+ While docXMLater provides comprehensive DOCX manipulation capabilities, there are some features that are not yet fully implemented:
1337
+
1338
+ ### 1. Table Row Spanning with vMerge
1339
+
1340
+ **Status:** FULLY IMPLEMENTED ✅
1341
+
1342
+ **What Works:**
1343
+ - Column spanning (horizontal cell merging) is fully supported
1344
+ - Row spanning (vertical cell merging) is now fully implemented
1345
+ - Both horizontal and vertical merging can be combined
1346
+ - Uses Word's proper `vMerge` attribute ('restart' and 'continue')
1347
+
1348
+ **Usage:**
1349
+ ```typescript
1350
+ // Merge cells horizontally (column spanning)
1351
+ table.mergeCells(0, 0, 0, 2); // Merge columns 0-2 in row 0
1352
+
1353
+ // Merge cells vertically (row spanning)
1354
+ table.mergeCells(0, 0, 2, 0); // Merge rows 0-2 in column 0
1355
+
1356
+ // Merge both horizontally and vertically (2x2 block)
1357
+ table.mergeCells(0, 0, 1, 1); // Merge 2x2 block starting at (0,0)
1358
+ ```
1359
+
1360
+ ### 2. Structured Document Tags (SDT) Parsing
1361
+
1362
+ **Status:** FULLY IMPLEMENTED
1363
+
1364
+ **What Works:**
1365
+ - Complete SDT parsing from existing documents
1366
+ - All 9 control types supported (richText, plainText, comboBox, dropDownList, datePicker, checkbox, picture, buildingBlock, group)
1367
+ - SDT properties fully extracted (id, tag, lock, alias, controlType)
1368
+ - Nested content parsing (paragraphs, tables, nested SDTs)
1369
+ - Preserves element order using XMLParser's `_orderedChildren` metadata
1370
+ - Round-trip operations fully supported
1371
+
1372
+ **Control Types Supported:**
1373
+ - **Rich Text** - Multi-formatted text content
1374
+ - **Plain Text** - Simple text with optional multiLine support
1375
+ - **Combo Box** - User-editable dropdown with list items
1376
+ - **Dropdown List** - Fixed selection from list items
1377
+ - **Date Picker** - Date selection with format and calendar type
1378
+ - **Checkbox** - Boolean selection with custom checked/unchecked states
1379
+ - **Picture** - Image content control
1380
+ - **Building Block** - Gallery and category-based content
1381
+ - **Group** - Grouping of other controls
1382
+
1383
+ **Usage:**
1384
+ ```typescript
1385
+ // Load documents with SDTs - fully parsed
1386
+ const doc = await Document.load('document-with-sdts.docx');
1387
+
1388
+ // Access parsed SDT content
1389
+ const sdts = doc.getBodyElements().filter(el => el instanceof StructuredDocumentTag);
1390
+ for (const sdt of sdts) {
1391
+ console.log('ID:', sdt.getId());
1392
+ console.log('Tag:', sdt.getTag());
1393
+ console.log('Type:', sdt.getControlType());
1394
+ console.log('Content:', sdt.getContent());
1395
+ }
1396
+
1397
+ // Create new SDTs programmatically
1398
+ const sdt = new StructuredDocumentTag({
1399
+ id: 123456,
1400
+ tag: 'MyControl',
1401
+ controlType: 'richText',
1402
+ alias: 'Rich Text Control'
1403
+ });
1404
+ sdt.addContent(paragraph);
1405
+ ```
1406
+
1407
+ All known limitations have been resolved! For feature requests or bug reports, please visit our [GitHub Issues](https://github.com/ItMeDiaTech/docXMLater/issues).
1408
+
1409
+ ## Contributing
1410
+
1411
+ Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md).
1412
+
1413
+ 1. Fork the repository
1414
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
1415
+ 3. Commit changes (`git commit -m 'Add amazing feature'`)
1416
+ 4. Push to branch (`git push origin feature/amazing-feature`)
1417
+ 5. Open a Pull Request
1418
+
1419
+ ## Recent Updates (v1.3.0)
1420
+
1421
+ **Enhanced Document Parsing & Helper Functions:**
1422
+
1423
+ - **TOC Parsing** - Parse Table of Contents from existing DOCX files
1424
+ - Extract TOC field instructions with all switches (\h, \u, \z, \n, \o, \t)
1425
+ - Detect SDT wrappers with `docPartGallery="Table of Contents"`
1426
+ - Create `TableOfContentsElement` objects from parsed TOCs
1427
+ - Support for modifying TOC field instructions in loaded documents
1428
+ - **removeAllHeadersFooters() Helper** - New document helper function
1429
+ - Removes all headers and footers from the document
1430
+ - Deletes header/footer XML files and relationships
1431
+ - Returns count of removed headers/footers
1432
+ - **Enhanced Test Suite** - 1,119/1,150 tests passing (97.3% pass rate)
1433
+ - **Documentation Updates** - Complete API reference for new helper functions
1434
+
1435
+ **Previous Enhancements (v1.2.0):**
1436
+ - 5 advanced document helper functions
1437
+ - Enhanced document modification capabilities
1438
+ - Improved paragraph and table wrapping utilities
1439
+
1440
+ ## License
1441
+
1442
+ MIT © DiaTech
1443
+
1444
+ ## Acknowledgments
1445
+
1446
+ - Built with [JSZip](https://stuk.github.io/jszip/) for ZIP handling
1447
+ - Follows [ECMA-376](https://www.ecma-international.org/publications-and-standards/standards/ecma-376/) Office Open XML standard
1448
+ - Inspired by [python-docx](https://python-docx.readthedocs.io/) and [docx](https://github.com/dolanmiu/docx)
1449
+
1450
+ ## Support
1451
+
1452
+ - **Documentation**: [Full Docs](https://github.com/ItMeDiaTech/docXMLater/tree/main/docs)
1453
+ - **Examples**: [Example Code](https://github.com/ItMeDiaTech/docXMLater/tree/main/examples)
1454
+ - **Issues**: [GitHub Issues](https://github.com/ItMeDiaTech/docXMLater/issues)
1455
+ - **Discussions**: [GitHub Discussions](https://github.com/ItMeDiaTech/docXMLater/discussions)
1456
+
1457
+ ## Quick Links
1458
+
1459
+ - [NPM Package](https://www.npmjs.com/package/docxmlater)
1460
+ - [GitHub Repository](https://github.com/ItMeDiaTech/docXMLater)
1461
+ - [API Reference](https://github.com/ItMeDiaTech/docXMLater/tree/main/docs/api)
1462
+ - [Change Log](https://github.com/ItMeDiaTech/docXMLater/blob/main/CHANGELOG.md)
1463
+
1464
+ ---
1465
+
1466
+ **Ready to create amazing Word documents?** Start with our [examples](https://github.com/ItMeDiaTech/docXMLater/tree/main/examples) or dive into the [API Reference](#complete-api-reference) above!