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