documentation-hub 5.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/.eslintrc.json +43 -0
  2. package/.github/workflows/build.yml +64 -0
  3. package/.github/workflows/ci.yml +39 -0
  4. package/.vscode/extensions.json +3 -0
  5. package/Current.md +97 -0
  6. package/DocHub_Image.png +0 -0
  7. package/README.md +666 -0
  8. package/USER_GUIDE.md +1173 -0
  9. package/Updater.md +311 -0
  10. package/build/256x256.png +0 -0
  11. package/build/512x512.png +0 -0
  12. package/build/app-update.yml +4 -0
  13. package/build/create-icon.js +208 -0
  14. package/build/icon.ico +0 -0
  15. package/build/icon.png +0 -0
  16. package/build/icon_1024x1024.png +0 -0
  17. package/dist/assets/Analytics-BpsG9895.js +1 -0
  18. package/dist/assets/Card-IAZin8kp.js +1 -0
  19. package/dist/assets/CurrentSession-B-rFkHvf.js +12 -0
  20. package/dist/assets/Dashboard-C_5gMb0q.js +1 -0
  21. package/dist/assets/Documents-CqZ25axS.js +1 -0
  22. package/dist/assets/Input-l89xwXBi.js +1 -0
  23. package/dist/assets/Reporting-DqdHJY_a.js +1 -0
  24. package/dist/assets/Search-XNbu5z_3.js +1 -0
  25. package/dist/assets/SessionManager-lH9hZfzH.js +1 -0
  26. package/dist/assets/Sessions-ClZOPYNc.js +1 -0
  27. package/dist/assets/Settings-DUEHGURa.js +11 -0
  28. package/dist/assets/index-8xUe8ptc.js +24 -0
  29. package/dist/assets/index-RYyJqF7O.css +1 -0
  30. package/dist/assets/path-BkOl0AGO.js +1 -0
  31. package/dist/assets/promises-ID_B9S-h.js +1 -0
  32. package/dist/assets/urlHelpers-TvgahX0r.js +1 -0
  33. package/dist/assets/useToast-yRSO1dkm.js +1 -0
  34. package/dist/assets/vendor-charts-RkGK5ROP.js +36 -0
  35. package/dist/assets/vendor-db-l0sNRNKZ.js +1 -0
  36. package/dist/assets/vendor-react-BVZ_anCF.js +4 -0
  37. package/dist/assets/vendor-search-Dw8P0qyA.js +1 -0
  38. package/dist/assets/vendor-ui-BU7NfluV.js +53 -0
  39. package/dist/electron/PowerAutomateApiService-LfW09ZGr.js +147 -0
  40. package/dist/electron/main-CXkNtyv-.js +19789 -0
  41. package/dist/electron/main.js +5 -0
  42. package/dist/electron/preload.js +1 -0
  43. package/dist/icon.png +0 -0
  44. package/dist/index.html +27 -0
  45. package/docs/CODEBASE_ANALYSIS_REPORT.md +309 -0
  46. package/docs/DEBUG_LOGGING_GUIDE.md +244 -0
  47. package/docs/README.md +115 -0
  48. package/docs/TOC_WIRING_GUIDE.md +344 -0
  49. package/docs/analysis/Bullet_Symbol_Bug_Analysis.md +136 -0
  50. package/docs/analysis/DOCXMLATER_ANALYSIS_SUMMARY.txt +169 -0
  51. package/docs/analysis/Document_Processing_Issues_Analysis.md +704 -0
  52. package/docs/analysis/FIELD_PRESERVATION_ANALYSIS.md +1200 -0
  53. package/docs/analysis/INDENTATION_PRESERVE_ANALYSIS.md +181 -0
  54. package/docs/analysis/INDENTATION_PRESERVE_IMPLEMENTATION.md +207 -0
  55. package/docs/analysis/List_Implementation.md +206 -0
  56. package/docs/analysis/List_Implementation_Accuracy_Report.md +366 -0
  57. package/docs/analysis/PROCESSING_OPTIONS_UI_UPDATES.md +220 -0
  58. package/docs/analysis/RefactorStyles.md +852 -0
  59. package/docs/analysis/STYLE_PARAMETER_ENHANCEMENT.md +143 -0
  60. package/docs/analysis/docxmlater-comparison-todo-2025-11-13.md +636 -0
  61. package/docs/analysis/docxmlater-implementation-analysis-2025-11-13.md +340 -0
  62. package/docs/analysis/docxmlater-template_ui-integration-analysis.md +263 -0
  63. package/docs/analysis/github-issues-to-create.md +237 -0
  64. package/docs/api/API_README.md +538 -0
  65. package/docs/api/API_REFERENCE.md +751 -0
  66. package/docs/api/TYPE_DEFINITIONS.md +869 -0
  67. package/docs/architecture/FONT_EMBEDDING_GUIDE.md +318 -0
  68. package/docs/architecture/docxmlater-functions-and-structure.md +726 -0
  69. package/docs/docxmlater-readme.md +1341 -0
  70. package/docs/fixes/EXECUTION_LOG_TEST_BASE.md +573 -0
  71. package/docs/fixes/HYPERLINK_TEXT_SANITIZATION.md +253 -0
  72. package/docs/fixes/README.md +37 -0
  73. package/docs/github-issues/issue-1-body.md +125 -0
  74. package/docs/github-issues/issue-10-body.md +850 -0
  75. package/docs/github-issues/issue-2-body.md +200 -0
  76. package/docs/github-issues/issue-3-body.md +270 -0
  77. package/docs/github-issues/issue-4-body.md +169 -0
  78. package/docs/github-issues/issue-5-body.md +173 -0
  79. package/docs/github-issues/issue-6-body.md +158 -0
  80. package/docs/github-issues/issue-7-body.md +171 -0
  81. package/docs/github-issues/issue-8-body.md +407 -0
  82. package/docs/github-issues/issue-9-body.md +515 -0
  83. package/docs/github-issues/issue-tracker.md +274 -0
  84. package/docs/github-issues/predictive-analysis-2025-10-18.md +2131 -0
  85. package/docs/implementation/List_Framework_Refactor_Plan.md +336 -0
  86. package/docs/implementation/PRIMARY_TEXT_COLOR_FEATURE.md +217 -0
  87. package/docs/implementation/RELEASE_PLAN_v2.1.0.md +362 -0
  88. package/docs/implementation/RefactorStyles.md +588 -0
  89. package/docs/implementation/implement-plan.md +489 -0
  90. package/docs/implementation/missing-helpers-implementation.md +391 -0
  91. package/docs/implementation/refactor-plan.md +520 -0
  92. package/docs/implementation/session-implementation-complete.md +233 -0
  93. package/docs/implementation/session-management-plan.md +250 -0
  94. package/docs/setup-checklist.md +77 -0
  95. package/docs/versions/changelog.md +345 -0
  96. package/electron/customUpdater.ts +656 -0
  97. package/electron/main.ts +2441 -0
  98. package/electron/memoryConfig.ts +187 -0
  99. package/electron/preload.ts +394 -0
  100. package/electron/proxyConfig.ts +340 -0
  101. package/electron/services/BackupService.ts +452 -0
  102. package/electron/services/DictionaryService.ts +402 -0
  103. package/electron/services/LocalDictionaryLookupService.ts +147 -0
  104. package/electron/services/PowerAutomateApiService.ts +231 -0
  105. package/electron/services/SharePointSyncService.ts +474 -0
  106. package/electron/windowsCertStore.ts +427 -0
  107. package/electron/zscalerConfig.ts +381 -0
  108. package/eslint.config.js +92 -0
  109. package/jest.config.js +52 -0
  110. package/package.json +214 -0
  111. package/postcss.config.mjs +6 -0
  112. package/public/icon.png +0 -0
  113. package/publish-release.ps1 +5 -0
  114. package/renovate.json +30 -0
  115. package/src/App.tsx +216 -0
  116. package/src/__mocks__/p-limit.js +12 -0
  117. package/src/__mocks__/styleMock.js +1 -0
  118. package/src/components/common/BugReportButton.tsx +44 -0
  119. package/src/components/common/BugReportDialog.tsx +193 -0
  120. package/src/components/common/Button.tsx +153 -0
  121. package/src/components/common/Card.tsx +86 -0
  122. package/src/components/common/ColorPickerDialog.tsx +177 -0
  123. package/src/components/common/ConfirmDialog.tsx +96 -0
  124. package/src/components/common/DebugConsole.tsx +275 -0
  125. package/src/components/common/EmptyState.tsx +183 -0
  126. package/src/components/common/ErrorBoundary.tsx +98 -0
  127. package/src/components/common/ErrorDetailsDialog.tsx +153 -0
  128. package/src/components/common/ErrorFallback.tsx +218 -0
  129. package/src/components/common/Input.tsx +109 -0
  130. package/src/components/common/Skeleton.tsx +184 -0
  131. package/src/components/common/SplashScreen.tsx +81 -0
  132. package/src/components/common/Toast.tsx +155 -0
  133. package/src/components/common/Tooltip.tsx +79 -0
  134. package/src/components/common/UpdateNotification.tsx +320 -0
  135. package/src/components/comparison/ComparisonWindow.tsx +374 -0
  136. package/src/components/comparison/SideBySideDiff.tsx +486 -0
  137. package/src/components/comparison/index.ts +8 -0
  138. package/src/components/document/DocumentUploader.tsx +288 -0
  139. package/src/components/document/HyperlinkPreview.tsx +430 -0
  140. package/src/components/document/HyperlinkService.md +1484 -0
  141. package/src/components/document/Hyperlink_Technical_Documentation.md +496 -0
  142. package/src/components/document/InlineChangesView.tsx +707 -0
  143. package/src/components/document/ProcessingProgress.tsx +303 -0
  144. package/src/components/document/ProcessingResults.tsx +256 -0
  145. package/src/components/document/TrackedChangesDetail.tsx +530 -0
  146. package/src/components/document/TrackedChangesPanel.tsx +546 -0
  147. package/src/components/document/VirtualDocumentList.tsx +240 -0
  148. package/src/components/editor/DocumentEditor.tsx +723 -0
  149. package/src/components/editor/DocumentEditorModal.tsx +640 -0
  150. package/src/components/editor/EditorQuickActions.tsx +502 -0
  151. package/src/components/editor/EditorToolbar.tsx +312 -0
  152. package/src/components/editor/TableEditor.tsx +926 -0
  153. package/src/components/editor/index.ts +18 -0
  154. package/src/components/layout/Header.tsx +190 -0
  155. package/src/components/layout/Sidebar.tsx +313 -0
  156. package/src/components/layout/TitleBar.tsx +190 -0
  157. package/src/components/navigation/CommandPalette.tsx +233 -0
  158. package/src/components/navigation/KeyboardShortcutsModal.tsx +173 -0
  159. package/src/components/sessions/ChangeItem.tsx +408 -0
  160. package/src/components/sessions/ChangeViewer.tsx +1155 -0
  161. package/src/components/sessions/DocumentComparisonModal.tsx +314 -0
  162. package/src/components/sessions/ProcessingOptions.tsx +297 -0
  163. package/src/components/sessions/ReplacementsTab.tsx +438 -0
  164. package/src/components/sessions/RevisionHandlingOptions.tsx +87 -0
  165. package/src/components/sessions/SessionManager.tsx +188 -0
  166. package/src/components/sessions/StylesEditor.tsx +1335 -0
  167. package/src/components/sessions/TabContainer.tsx +151 -0
  168. package/src/components/sessions/VirtualSessionList.tsx +157 -0
  169. package/src/components/sessions/sessionToProcessorManager.tsx +420 -0
  170. package/src/components/settings/CertificateManager.tsx +410 -0
  171. package/src/components/settings/SegmentedControl.tsx +88 -0
  172. package/src/components/settings/SettingRow.tsx +52 -0
  173. package/src/contexts/GlobalStatsContext.tsx +396 -0
  174. package/src/contexts/SessionContext.tsx +2129 -0
  175. package/src/contexts/ThemeContext.tsx +428 -0
  176. package/src/contexts/UserSettingsContext.tsx +290 -0
  177. package/src/contexts/__tests__/GlobalStatsContext.test.tsx +390 -0
  178. package/src/global.d.ts +273 -0
  179. package/src/hooks/useDocumentQueue.tsx +210 -0
  180. package/src/hooks/useToast.tsx +55 -0
  181. package/src/main.tsx +10 -0
  182. package/src/pages/Analytics.tsx +386 -0
  183. package/src/pages/CurrentSession.tsx +1174 -0
  184. package/src/pages/Dashboard.tsx +319 -0
  185. package/src/pages/Documents.tsx +317 -0
  186. package/src/pages/Projects.tsx +250 -0
  187. package/src/pages/Reporting.tsx +386 -0
  188. package/src/pages/Search.tsx +349 -0
  189. package/src/pages/Sessions.tsx +285 -0
  190. package/src/pages/Settings.tsx +2662 -0
  191. package/src/services/HyperlinkService.ts +1085 -0
  192. package/src/services/document/DocXMLaterProcessor.ts +617 -0
  193. package/src/services/document/DocumentProcessingComparison.ts +856 -0
  194. package/src/services/document/DocumentSnapshotService.ts +575 -0
  195. package/src/services/document/WordDocumentProcessor.ts +10509 -0
  196. package/src/services/document/__tests__/DocXMLaterProcessor.hyperlinks.test.md +311 -0
  197. package/src/services/document/__tests__/WordDocumentProcessor.integration.test.ts +515 -0
  198. package/src/services/document/__tests__/WordDocumentProcessor.test.ts +812 -0
  199. package/src/services/document/blanklines/BlankLineManager.ts +658 -0
  200. package/src/services/document/blanklines/__tests__/paragraphChecks.test.ts +281 -0
  201. package/src/services/document/blanklines/helpers/blankLineInsertion.ts +87 -0
  202. package/src/services/document/blanklines/helpers/blankLineSnapshot.ts +251 -0
  203. package/src/services/document/blanklines/helpers/clearCustom.ts +121 -0
  204. package/src/services/document/blanklines/helpers/contextChecks.ts +117 -0
  205. package/src/services/document/blanklines/helpers/imageChecks.ts +51 -0
  206. package/src/services/document/blanklines/helpers/paragraphChecks.ts +236 -0
  207. package/src/services/document/blanklines/helpers/removeBlanksBetweenListItems.ts +91 -0
  208. package/src/services/document/blanklines/helpers/removeTrailingBlanks.ts +35 -0
  209. package/src/services/document/blanklines/helpers/tableGuards.ts +21 -0
  210. package/src/services/document/blanklines/index.ts +67 -0
  211. package/src/services/document/blanklines/rules/additionRules.ts +337 -0
  212. package/src/services/document/blanklines/rules/indentationRules.ts +317 -0
  213. package/src/services/document/blanklines/rules/removalRules.ts +362 -0
  214. package/src/services/document/blanklines/rules/ruleTypes.ts +92 -0
  215. package/src/services/document/blanklines/types.ts +29 -0
  216. package/src/services/document/helpers/ImageBorderCropper.ts +377 -0
  217. package/src/services/document/helpers/__tests__/whitespace.test.ts +272 -0
  218. package/src/services/document/helpers/whitespace.ts +117 -0
  219. package/src/services/document/list/ListNormalizer.ts +947 -0
  220. package/src/services/document/list/index.ts +45 -0
  221. package/src/services/document/list/list-detection.ts +275 -0
  222. package/src/services/document/list/list-types.ts +162 -0
  223. package/src/services/document/processors/HyperlinkProcessor.ts +370 -0
  224. package/src/services/document/processors/ListProcessor.ts +257 -0
  225. package/src/services/document/processors/StructureProcessor.ts +176 -0
  226. package/src/services/document/processors/StyleProcessor.ts +389 -0
  227. package/src/services/document/processors/TableProcessor.ts +2238 -0
  228. package/src/services/document/processors/__tests__/HyperlinkProcessor.test.ts +314 -0
  229. package/src/services/document/processors/__tests__/ListProcessor.test.ts +291 -0
  230. package/src/services/document/processors/__tests__/StructureProcessor.test.ts +257 -0
  231. package/src/services/document/processors/__tests__/TableProcessor.hlp-tips-bullets.test.ts +459 -0
  232. package/src/services/document/processors/__tests__/TableProcessor.test.ts +1604 -0
  233. package/src/services/document/processors/index.ts +28 -0
  234. package/src/services/document/types/docx-processing.ts +310 -0
  235. package/src/services/editor/EditorActionHandlers.ts +901 -0
  236. package/src/services/editor/index.ts +13 -0
  237. package/src/setupTests.ts +47 -0
  238. package/src/styles/global.css +782 -0
  239. package/src/types/backup.ts +132 -0
  240. package/src/types/dictionary.ts +125 -0
  241. package/src/types/document-processing.ts +331 -0
  242. package/src/types/docxmlater-augments.d.ts +142 -0
  243. package/src/types/editor.ts +280 -0
  244. package/src/types/electron.ts +340 -0
  245. package/src/types/globalStats.ts +155 -0
  246. package/src/types/hyperlink.ts +471 -0
  247. package/src/types/operations.ts +354 -0
  248. package/src/types/session.ts +427 -0
  249. package/src/types/settings.ts +112 -0
  250. package/src/utils/MemoryMonitor.ts +248 -0
  251. package/src/utils/cn.ts +6 -0
  252. package/src/utils/colorConvert.ts +306 -0
  253. package/src/utils/diffUtils.ts +347 -0
  254. package/src/utils/documentUtils.ts +202 -0
  255. package/src/utils/electronGuard.ts +62 -0
  256. package/src/utils/indexedDB.ts +915 -0
  257. package/src/utils/logger.ts +717 -0
  258. package/src/utils/pathSecurity.ts +232 -0
  259. package/src/utils/pathValidator.ts +236 -0
  260. package/src/utils/processingTimeEstimator.ts +153 -0
  261. package/src/utils/safeJsonParse.ts +62 -0
  262. package/src/utils/textSanitizer.ts +162 -0
  263. package/src/utils/urlHelpers.ts +304 -0
  264. package/src/utils/urlPatterns.ts +198 -0
  265. package/src/utils/urlSanitizer.ts +152 -0
  266. package/src/vite-env.d.ts +11 -0
  267. package/tsconfig.electron.json +19 -0
  268. package/tsconfig.json +36 -0
  269. package/tsconfig.node.json +12 -0
  270. package/typedoc.json +45 -0
  271. package/vite.config.ts +152 -0
@@ -0,0 +1,1341 @@
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-2073%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 **100% test pass rate**.
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.16.0
13
+
14
+ **Production Release!** All major features complete:
15
+
16
+ ### What's New in v1.16.0
17
+
18
+ - **Complete Feature Set:** All 102 major features implemented across 5 phases
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
25
+
26
+ **Test Results:** 2,073/2,073 tests passing (100% - comprehensive test coverage)
27
+
28
+ ### Implementation Phases - All Complete ✅
29
+
30
+ | Phase | Status | Features |
31
+ | -------------------------------- | ----------- | ----------------------------------------------------- |
32
+ | **Phase 1: Foundation** | ✅ Complete | ZIP handling, XML generation, validation |
33
+ | **Phase 2: Core Elements** | ✅ Complete | Paragraph, Run, text formatting |
34
+ | **Phase 3: Advanced Formatting** | ✅ Complete | Styles, tables, sections, lists |
35
+ | **Phase 4: Rich Content** | ✅ Complete | Images, headers, footers, hyperlinks |
36
+ | **Phase 5: Polish** | ✅ Complete | Track changes, comments, TOC, hyperlink defragmentation |
37
+
38
+ ### Phase 4 & 5 Highlights (v1.13.0 - v1.16.0)
39
+
40
+ #### Phase 4: Rich Content Features
41
+ - **Images**: PNG, JPEG, GIF support with positioning and sizing
42
+ - **Headers & Footers**: Different first/odd/even pages with dynamic fields
43
+ - **Hyperlinks**: External, internal, and email links with full relationship management
44
+ - **Hyperlink Extraction**: Comprehensive API covering main content, tables, headers, and footers
45
+ - **Batch URL Updates**: Efficient bulk hyperlink URL modification with error tracking
46
+
47
+ #### Phase 5: Polish & Advanced Features
48
+ - **Hyperlink Defragmentation** (v1.15.0): Fix fragmented hyperlinks from Google Docs imports
49
+ - **List Formatting Helpers** (v1.14.0): Smart bullet and numbering with proper indentation
50
+ - **Special Character Support** (v1.14.0): Tabs, newlines, non-breaking hyphens serialize correctly
51
+ - **Track Changes**: Insertions and deletions with author attribution
52
+ - **Comments**: Threading and replies for collaboration
53
+ - **Table of Contents**: Multiple TOC styles with customization
54
+ - **XML Corruption Prevention**: Automatic sanitization of XML patterns in text
55
+
56
+ ## Quick Start
57
+
58
+ ```bash
59
+ npm install docxmlater
60
+ ```
61
+
62
+ ```typescript
63
+ import { Document } from "docxmlater";
64
+
65
+ // Create document
66
+ const doc = Document.create();
67
+ doc.createParagraph("Hello World").setStyle("Title");
68
+
69
+ // Save document
70
+ await doc.save("output.docx");
71
+ ```
72
+
73
+ ## Complete API Reference
74
+
75
+ ### Document Operations
76
+
77
+ | Method | Description | Example |
78
+ | --------------------------------- | ----------------------- | ------------------------------------------------ |
79
+ | `Document.create(options?)` | Create new document | `const doc = Document.create()` |
80
+ | `Document.createEmpty()` | Create minimal document | `const doc = Document.createEmpty()` |
81
+ | `Document.load(path)` | Load from file | `const doc = await Document.load('file.docx')` |
82
+ | `Document.loadFromBuffer(buffer)` | Load from buffer | `const doc = await Document.loadFromBuffer(buf)` |
83
+ | `save(path)` | Save to file | `await doc.save('output.docx')` |
84
+ | `toBuffer()` | Export as buffer | `const buffer = await doc.toBuffer()` |
85
+ | `dispose()` | Clean up resources | `doc.dispose()` |
86
+
87
+ ### Content Creation
88
+
89
+ | Method | Description | Example |
90
+ | -------------------------------- | ------------------------ | -------------------------------- |
91
+ | `createParagraph(text?)` | Add paragraph | `doc.createParagraph('Text')` |
92
+ | `createTable(rows, cols)` | Add table | `doc.createTable(3, 4)` |
93
+ | `addParagraph(para)` | Add existing paragraph | `doc.addParagraph(myPara)` |
94
+ | `addTable(table)` | Add existing table | `doc.addTable(myTable)` |
95
+ | `addImage(image)` | Add image | `doc.addImage(myImage)` |
96
+ | `addTableOfContents(toc?)` | Add TOC | `doc.addTableOfContents()` |
97
+ | `insertParagraphAt(index, para)` | Insert at position | `doc.insertParagraphAt(0, para)` |
98
+ | `insertTableAt(index, table)` | Insert table at position | `doc.insertTableAt(5, table)` |
99
+ | `insertTocAt(index, toc)` | Insert TOC at position | `doc.insertTocAt(0, toc)` |
100
+
101
+ ### Content Manipulation
102
+
103
+ | Method | Description | Returns |
104
+ | --------------------------------- | ---------------------------- | --------- |
105
+ | `replaceParagraphAt(index, para)` | Replace paragraph | `boolean` |
106
+ | `replaceTableAt(index, table)` | Replace table | `boolean` |
107
+ | `moveElement(fromIndex, toIndex)` | Move element to new position | `boolean` |
108
+ | `swapElements(index1, index2)` | Swap two elements | `boolean` |
109
+ | `removeTocAt(index)` | Remove TOC element | `boolean` |
110
+
111
+ ### Content Retrieval
112
+
113
+ | Method | Description | Returns |
114
+ | --------------------- | --------------------- | ------------------------------------------ |
115
+ | `getParagraphs()` | Get all paragraphs | `Paragraph[]` |
116
+ | `getTables()` | Get all tables | `Table[]` |
117
+ | `getBodyElements()` | Get all body elements | `BodyElement[]` |
118
+ | `getParagraphCount()` | Count paragraphs | `number` |
119
+ | `getTableCount()` | Count tables | `number` |
120
+ | `getHyperlinks()` | Get all links | `Array<{hyperlink, paragraph}>` |
121
+ | `getBookmarks()` | Get all bookmarks | `Array<{bookmark, paragraph}>` |
122
+ | `getImages()` | Get all images | `Array<{image, relationshipId, filename}>` |
123
+
124
+ ### Content Removal
125
+
126
+ | Method | Description | Returns |
127
+ | ------------------------------ | ------------------ | --------- |
128
+ | `removeParagraph(paraOrIndex)` | Remove paragraph | `boolean` |
129
+ | `removeTable(tableOrIndex)` | Remove table | `boolean` |
130
+ | `clearParagraphs()` | Remove all content | `this` |
131
+
132
+ ### Search & Replace
133
+
134
+ | Method | Description | Options |
135
+ | -------------------------------------- | --------------------- | ------------------------------ |
136
+ | `findText(text, options?)` | Find text occurrences | `{caseSensitive?, wholeWord?}` |
137
+ | `replaceText(find, replace, options?)` | Replace all text | `{caseSensitive?, wholeWord?}` |
138
+ | `updateHyperlinkUrls(urlMap)` | Update hyperlink URLs | `Map<oldUrl, newUrl>` |
139
+
140
+ ### Style Application
141
+
142
+ | Method | Description | Returns |
143
+ | ------------------------------------- | -------------------------------- | ------------------- |
144
+ | `applyStyleToAll(styleId, predicate)` | Apply style to matching elements | `number` |
145
+ | `findElementsByStyle(styleId)` | Find all elements using a style | `Array<Para\|Cell>` |
146
+
147
+ **Example:**
148
+
149
+ ```typescript
150
+ // Apply Heading1 to all paragraphs containing "Chapter"
151
+ const count = doc.applyStyleToAll("Heading1", (el) => {
152
+ return el instanceof Paragraph && el.getText().includes("Chapter");
153
+ });
154
+
155
+ // Find all Heading1 elements
156
+ const headings = doc.findElementsByStyle("Heading1");
157
+ ```
158
+
159
+ ### Document Statistics
160
+
161
+ | Method | Description | Returns |
162
+ | ----------------------------------- | ------------------- | ------------------------------ |
163
+ | `getWordCount()` | Total word count | `number` |
164
+ | `getCharacterCount(includeSpaces?)` | Character count | `number` |
165
+ | `estimateSize()` | Size estimation | `{totalEstimatedMB, warning?}` |
166
+ | `getSizeStats()` | Detailed size stats | `{elements, size, warnings}` |
167
+
168
+ ### Text Formatting
169
+
170
+ | Property | Values | Example |
171
+ | ------------- | -------------------------------- | ----------------------- |
172
+ | `bold` | `true/false` | `{bold: true}` |
173
+ | `italic` | `true/false` | `{italic: true}` |
174
+ | `underline` | `'single'/'double'/'dotted'/etc` | `{underline: 'single'}` |
175
+ | `strike` | `true/false` | `{strike: true}` |
176
+ | `font` | Font name | `{font: 'Arial'}` |
177
+ | `size` | Points | `{size: 12}` |
178
+ | `color` | Hex color | `{color: 'FF0000'}` |
179
+ | `highlight` | Color name | `{highlight: 'yellow'}` |
180
+ | `subscript` | `true/false` | `{subscript: true}` |
181
+ | `superscript` | `true/false` | `{superscript: true}` |
182
+ | `smallCaps` | `true/false` | `{smallCaps: true}` |
183
+ | `allCaps` | `true/false` | `{allCaps: true}` |
184
+
185
+ ### Paragraph Operations
186
+
187
+ #### Creating Detached Paragraphs
188
+
189
+ Create paragraphs independently before adding to a document:
190
+
191
+ ```typescript
192
+ // Create empty paragraph
193
+ const para1 = Paragraph.create();
194
+
195
+ // Create with text
196
+ const para2 = Paragraph.create("Hello World");
197
+
198
+ // Create with text and formatting
199
+ const para3 = Paragraph.create("Centered text", { alignment: "center" });
200
+
201
+ // Create with just formatting
202
+ const para4 = Paragraph.create({
203
+ alignment: "right",
204
+ spacing: { before: 240 },
205
+ });
206
+
207
+ // Create with style
208
+ const heading = Paragraph.createWithStyle("Chapter 1", "Heading1");
209
+
210
+ // Create with both run and paragraph formatting
211
+ const important = Paragraph.createFormatted(
212
+ "Important Text",
213
+ { bold: true, color: "FF0000" },
214
+ { alignment: "center" }
215
+ );
216
+
217
+ // Add to document later
218
+ doc.addParagraph(para1);
219
+ doc.addParagraph(heading);
220
+ ```
221
+
222
+ #### Paragraph Factory Methods
223
+
224
+ | Method | Description | Example |
225
+ | --------------------------------------------------- | --------------------------- | --------------------------------------- |
226
+ | `Paragraph.create(text?, formatting?)` | Create detached paragraph | `Paragraph.create('Text')` |
227
+ | `Paragraph.create(formatting?)` | Create with formatting only | `Paragraph.create({alignment: 'left'})` |
228
+ | `Paragraph.createWithStyle(text, styleId)` | Create with style | `Paragraph.createWithStyle('', 'H1')` |
229
+ | `Paragraph.createEmpty()` | Create empty paragraph | `Paragraph.createEmpty()` |
230
+ | `Paragraph.createFormatted(text, run?, paragraph?)` | Create with dual formatting | See example above |
231
+
232
+ #### Paragraph Formatting Methods
233
+
234
+ | Method | Description | Values |
235
+ | ------------------------------ | ------------------- | ----------------------------------- |
236
+ | `setAlignment(align)` | Text alignment | `'left'/'center'/'right'/'justify'` |
237
+ | `setLeftIndent(twips)` | Left indentation | Twips value |
238
+ | `setRightIndent(twips)` | Right indentation | Twips value |
239
+ | `setFirstLineIndent(twips)` | First line indent | Twips value |
240
+ | `setSpaceBefore(twips)` | Space before | Twips value |
241
+ | `setSpaceAfter(twips)` | Space after | Twips value |
242
+ | `setLineSpacing(twips, rule?)` | Line spacing | Twips + rule |
243
+ | `setStyle(styleId)` | Apply style | Style ID |
244
+ | `setKeepNext()` | Keep with next | - |
245
+ | `setKeepLines()` | Keep lines together | - |
246
+ | `setPageBreakBefore()` | Page break before | - |
247
+
248
+ #### Paragraph Manipulation Methods
249
+
250
+ | Method | Description | Returns |
251
+ | -------------------------------------- | ----------------------- | ----------- |
252
+ | `insertRunAt(index, run)` | Insert run at position | `this` |
253
+ | `removeRunAt(index)` | Remove run at position | `boolean` |
254
+ | `replaceRunAt(index, run)` | Replace run at position | `boolean` |
255
+ | `findText(text, options?)` | Find text in runs | `number[]` |
256
+ | `replaceText(find, replace, options?)` | Replace text in runs | `number` |
257
+ | `mergeWith(otherPara)` | Merge another paragraph | `this` |
258
+ | `clone()` | Clone paragraph | `Paragraph` |
259
+
260
+ **Example:**
261
+
262
+ ```typescript
263
+ const para = doc.createParagraph("Hello World");
264
+
265
+ // Find and replace
266
+ const indices = para.findText("World"); // [1]
267
+ const count = para.replaceText("World", "Universe", { caseSensitive: true });
268
+
269
+ // Manipulate runs
270
+ para.insertRunAt(0, new Run("Start: ", { bold: true }));
271
+ para.replaceRunAt(1, new Run("HELLO", { allCaps: true }));
272
+
273
+ // Merge paragraphs
274
+ const para2 = Paragraph.create(" More text");
275
+ para.mergeWith(para2); // Combines runs
276
+ ```
277
+
278
+ ### Run (Text Span) Operations
279
+
280
+ | Method | Description | Returns |
281
+ | ------------------------------- | ------------------------- | ------- |
282
+ | `clone()` | Clone run with formatting | `Run` |
283
+ | `insertText(index, text)` | Insert text at position | `this` |
284
+ | `appendText(text)` | Append text to end | `this` |
285
+ | `replaceText(start, end, text)` | Replace text range | `this` |
286
+
287
+ **Example:**
288
+
289
+ ```typescript
290
+ const run = new Run("Hello World", { bold: true });
291
+
292
+ // Text manipulation
293
+ run.insertText(6, "Beautiful "); // "Hello Beautiful World"
294
+ run.appendText("!"); // "Hello Beautiful World!"
295
+ run.replaceText(0, 5, "Hi"); // "Hi Beautiful World!"
296
+
297
+ // Clone for reuse
298
+ const copy = run.clone();
299
+ copy.setColor("FF0000"); // Original unchanged
300
+ ```
301
+
302
+ ### Table Operations
303
+
304
+ | Method | Description | Example |
305
+ | ----------------------- | -------------------- | ---------------------------------------- |
306
+ | `getRow(index)` | Get table row | `table.getRow(0)` |
307
+ | `getCell(row, col)` | Get table cell | `table.getCell(0, 1)` |
308
+ | `addRow()` | Add new row | `table.addRow()` |
309
+ | `removeRow(index)` | Remove row | `table.removeRow(2)` |
310
+ | `insertColumn(index)` | Insert column | `table.insertColumn(1)` |
311
+ | `removeColumn(index)` | Remove column | `table.removeColumn(3)` |
312
+ | `setWidth(twips)` | Set table width | `table.setWidth(8640)` |
313
+ | `setAlignment(align)` | Table alignment | `table.setAlignment('center')` |
314
+ | `setAllBorders(border)` | Set all borders | `table.setAllBorders({style: 'single'})` |
315
+ | `setBorders(borders)` | Set specific borders | `table.setBorders({top: {...}})` |
316
+
317
+ #### Advanced Table Operations
318
+
319
+ | Method | Description | Returns |
320
+ | ------------------------------------------------ | ------------------------- | ------------ |
321
+ | `mergeCells(startRow, startCol, endRow, endCol)` | Merge cells | `this` |
322
+ | `splitCell(row, col)` | Remove cell spanning | `this` |
323
+ | `moveCell(fromRow, fromCol, toRow, toCol)` | Move cell contents | `this` |
324
+ | `swapCells(row1, col1, row2, col2)` | Swap two cells | `this` |
325
+ | `setColumnWidth(index, width)` | Set specific column width | `this` |
326
+ | `setColumnWidths(widths)` | Set all column widths | `this` |
327
+ | `insertRows(startIndex, count)` | Insert multiple rows | `TableRow[]` |
328
+ | `removeRows(startIndex, count)` | Remove multiple rows | `boolean` |
329
+ | `clone()` | Clone entire table | `Table` |
330
+
331
+ **Example:**
332
+
333
+ ```typescript
334
+ const table = doc.createTable(3, 3);
335
+
336
+ // Merge cells horizontally (row 0, columns 0-2)
337
+ table.mergeCells(0, 0, 0, 2);
338
+
339
+ // Move cell contents
340
+ table.moveCell(1, 1, 2, 2);
341
+
342
+ // Swap cells
343
+ table.swapCells(0, 0, 2, 2);
344
+
345
+ // Batch row operations
346
+ table.insertRows(1, 3); // Insert 3 rows at position 1
347
+ table.removeRows(4, 2); // Remove 2 rows starting at position 4
348
+
349
+ // Set column widths
350
+ table.setColumnWidth(0, 2000); // First column = 2000 twips
351
+ table.setColumnWidths([2000, 3000, 2000]); // All columns
352
+
353
+ // Clone table for reuse
354
+ const tableCopy = table.clone();
355
+ ```
356
+
357
+ ### Table Cell Operations
358
+
359
+ | Method | Description | Example |
360
+ | ----------------------------- | --------------------- | ------------------------------------- |
361
+ | `createParagraph(text?)` | Add paragraph to cell | `cell.createParagraph('Text')` |
362
+ | `setShading(shading)` | Cell background | `cell.setShading({fill: 'E0E0E0'})` |
363
+ | `setVerticalAlignment(align)` | Vertical align | `cell.setVerticalAlignment('center')` |
364
+ | `setColumnSpan(cols)` | Merge columns | `cell.setColumnSpan(3)` |
365
+ | `setRowSpan(rows)` | Merge rows | `cell.setRowSpan(2)` |
366
+ | `setBorders(borders)` | Cell borders | `cell.setBorders({top: {...}})` |
367
+ | `setWidth(width, type?)` | Cell width | `cell.setWidth(2000, 'dxa')` |
368
+
369
+ ### Style Management
370
+
371
+ | Method | Description | Example |
372
+ | ----------------------------- | ------------------ | ---------------------------------- |
373
+ | `addStyle(style)` | Add custom style | `doc.addStyle(myStyle)` |
374
+ | `getStyle(styleId)` | Get style by ID | `doc.getStyle('Heading1')` |
375
+ | `hasStyle(styleId)` | Check style exists | `doc.hasStyle('CustomStyle')` |
376
+ | `getStyles()` | Get all styles | `doc.getStyles()` |
377
+ | `removeStyle(styleId)` | Remove style | `doc.removeStyle('OldStyle')` |
378
+ | `updateStyle(styleId, props)` | Update style | `doc.updateStyle('Normal', {...})` |
379
+
380
+ #### Style Manipulation
381
+
382
+ | Method | Description | Returns |
383
+ | ------------------------ | ----------------------------------- | ------- |
384
+ | `style.clone()` | Clone style | `Style` |
385
+ | `style.mergeWith(other)` | Merge properties from another style | `this` |
386
+
387
+ **Example:**
388
+
389
+ ```typescript
390
+ // Clone a style
391
+ const heading1 = doc.getStyle("Heading1");
392
+ const customHeading = heading1.clone();
393
+ customHeading.setRunFormatting({ color: "FF0000" });
394
+
395
+ // Merge styles
396
+ const baseStyle = Style.createNormalStyle();
397
+ const overrideStyle = Style.create({
398
+ styleId: "Override",
399
+ name: "Override",
400
+ type: "paragraph",
401
+ runFormatting: { bold: true, color: "FF0000" },
402
+ });
403
+ baseStyle.mergeWith(overrideStyle); // baseStyle now has bold red text
404
+ ```
405
+
406
+ #### Built-in Styles
407
+
408
+ - `Normal` - Default paragraph
409
+ - `Title` - Document title
410
+ - `Subtitle` - Document subtitle
411
+ - `Heading1` through `Heading9` - Section headings
412
+ - `ListParagraph` - List items
413
+
414
+ ### List Management
415
+
416
+ | Method | Description | Returns |
417
+ | --------------------------------------- | ----------------------- | ------- |
418
+ | `createBulletList(levels?, bullets?)` | Create bullet list | `numId` |
419
+ | `createNumberedList(levels?, formats?)` | Create numbered list | `numId` |
420
+ | `createMultiLevelList()` | Create multi-level list | `numId` |
421
+
422
+ ### Table of Contents (TOC)
423
+
424
+ #### Basic TOC Creation
425
+
426
+ | Method | Description | Example |
427
+ | -------------------------- | ---------------------- | -------------------------- |
428
+ | `addTableOfContents(toc?)` | Add TOC to document | `doc.addTableOfContents()` |
429
+ | `insertTocAt(index, toc)` | Insert TOC at position | `doc.insertTocAt(0, toc)` |
430
+ | `removeTocAt(index)` | Remove TOC at position | `doc.removeTocAt(0)` |
431
+
432
+ #### TOC Factory Methods
433
+
434
+ | Method | Description | Example |
435
+ | -------------------------------------------------------- | ------------------------ | ---------------------------------------------------- |
436
+ | `TableOfContents.createStandard(title?)` | Standard TOC (3 levels) | `TableOfContents.createStandard()` |
437
+ | `TableOfContents.createSimple(title?)` | Simple TOC (2 levels) | `TableOfContents.createSimple()` |
438
+ | `TableOfContents.createDetailed(title?)` | Detailed TOC (4 levels) | `TableOfContents.createDetailed()` |
439
+ | `TableOfContents.createHyperlinked(title?)` | Hyperlinked TOC | `TableOfContents.createHyperlinked()` |
440
+ | `TableOfContents.createWithStyles(styles, opts?)` | TOC with specific styles | `TableOfContents.createWithStyles(['H1','H3'])` |
441
+ | `TableOfContents.createFlat(title?, styles?)` | Flat TOC (no indent) | `TableOfContents.createFlat()` |
442
+ | `TableOfContents.createNumbered(title?, format?)` | Numbered TOC | `TableOfContents.createNumbered('TOC', 'roman')` |
443
+ | `TableOfContents.createWithSpacing(spacing, opts?)` | TOC with custom spacing | `TableOfContents.createWithSpacing(120)` |
444
+ | `TableOfContents.createWithHyperlinkColor(color, opts?)` | Custom hyperlink color | `TableOfContents.createWithHyperlinkColor('FF0000')` |
445
+
446
+ #### TOC Configuration Methods
447
+
448
+ | Method | Description | Values |
449
+ | --------------------------------- | ------------------------------ | -------------------------- |
450
+ | `setIncludeStyles(styles)` | Select specific heading styles | `['Heading1', 'Heading3']` |
451
+ | `setNumbered(numbered, format?)` | Enable/disable numbering | `(true, 'roman')` |
452
+ | `setNoIndent(noIndent)` | Remove indentation | `true/false` |
453
+ | `setCustomIndents(indents)` | Custom indents per level | `[0, 200, 400]` (twips) |
454
+ | `setSpaceBetweenEntries(spacing)` | Spacing between entries | `120` (twips) |
455
+ | `setHyperlinkColor(color)` | Hyperlink color | `'0000FF'` (default blue) |
456
+ | `configure(options)` | Bulk configuration | See example below |
457
+
458
+ #### TOC Properties
459
+
460
+ | Property | Type | Default | Description |
461
+ | --------------------- | ------------------------------------ | --------------------- | ---------------------------------- |
462
+ | `title` | `string` | `'Table of Contents'` | TOC title |
463
+ | `levels` | `number` (1-9) | `3` | Heading levels to include |
464
+ | `includeStyles` | `string[]` | `undefined` | Specific styles (overrides levels) |
465
+ | `showPageNumbers` | `boolean` | `true` | Show page numbers |
466
+ | `useHyperlinks` | `boolean` | `false` | Use hyperlinks instead of page #s |
467
+ | `numbered` | `boolean` | `false` | Number TOC entries |
468
+ | `numberingFormat` | `'decimal'/'roman'/'alpha'` | `'decimal'` | Numbering format |
469
+ | `noIndent` | `boolean` | `false` | Remove all indentation |
470
+ | `customIndents` | `number[]` | `undefined` | Custom indents in twips |
471
+ | `spaceBetweenEntries` | `number` | `0` | Spacing in twips |
472
+ | `hyperlinkColor` | `string` | `'0000FF'` | Hyperlink color (hex without #) |
473
+ | `tabLeader` | `'dot'/'hyphen'/'underscore'/'none'` | `'dot'` | Tab leader character |
474
+
475
+ **Example:**
476
+
477
+ ```typescript
478
+ // Basic TOC
479
+ const simpleToc = TableOfContents.createStandard();
480
+ doc.addTableOfContents(simpleToc);
481
+
482
+ // Select specific styles (e.g., only Heading1 and Heading3)
483
+ const customToc = TableOfContents.createWithStyles(["Heading1", "Heading3"]);
484
+
485
+ // Flat TOC with no indentation
486
+ const flatToc = TableOfContents.createFlat("Contents");
487
+
488
+ // Numbered TOC with roman numerals
489
+ const numberedToc = TableOfContents.createNumbered(
490
+ "Table of Contents",
491
+ "roman"
492
+ );
493
+
494
+ // Custom hyperlink color (red instead of blue)
495
+ const coloredToc = TableOfContents.createWithHyperlinkColor("FF0000");
496
+
497
+ // Advanced configuration
498
+ const toc = TableOfContents.create()
499
+ .setIncludeStyles(["Heading1", "Heading2", "Heading3"])
500
+ .setNumbered(true, "decimal")
501
+ .setSpaceBetweenEntries(120) // 6pt spacing
502
+ .setHyperlinkColor("0000FF")
503
+ .setNoIndent(false);
504
+
505
+ // Or use configure() for bulk settings
506
+ toc.configure({
507
+ title: "Table of Contents",
508
+ includeStyles: ["Heading1", "CustomHeader"],
509
+ numbered: true,
510
+ numberingFormat: "alpha",
511
+ spaceBetweenEntries: 100,
512
+ hyperlinkColor: "FF0000",
513
+ noIndent: true,
514
+ });
515
+
516
+ // Insert at specific position
517
+ doc.insertTocAt(0, toc);
518
+ ```
519
+
520
+ ### Image Handling
521
+
522
+ | Method | Description | Example |
523
+ | --------------------------------------- | ------------------ | -------------------------------- |
524
+ | `Image.fromFile(path, width?, height?)` | Load from file | `Image.fromFile('pic.jpg')` |
525
+ | `Image.fromBuffer(buffer, ext, w?, h?)` | Load from buffer | `Image.fromBuffer(buf, 'png')` |
526
+ | `setWidth(emus, maintainRatio?)` | Set width | `img.setWidth(inchesToEmus(3))` |
527
+ | `setHeight(emus, maintainRatio?)` | Set height | `img.setHeight(inchesToEmus(2))` |
528
+ | `setSize(width, height)` | Set dimensions | `img.setSize(w, h)` |
529
+ | `setRotation(degrees)` | Rotate image | `img.setRotation(90)` |
530
+ | `setAltText(text)` | Accessibility text | `img.setAltText('Description')` |
531
+
532
+ ### Hyperlinks
533
+
534
+ #### Creating Hyperlinks
535
+
536
+ | Method | Description | Example |
537
+ | ------------------------------------------------- | ---------------- | ---------------------------------------------------------- |
538
+ | `Hyperlink.createExternal(url, text, format?)` | Web link | `Hyperlink.createExternal('https://example.com', 'Click')` |
539
+ | `Hyperlink.createEmail(email, text?, format?)` | Email link | `Hyperlink.createEmail('user@example.com')` |
540
+ | `Hyperlink.createInternal(anchor, text, format?)` | Internal link | `Hyperlink.createInternal('Section1', 'Go to')` |
541
+ | `para.addHyperlink(hyperlink)` | Add to paragraph | `para.addHyperlink(link)` |
542
+
543
+ #### Hyperlink Defragmentation (v1.15.0+)
544
+
545
+ Fix fragmented hyperlinks caused by Google Docs imports or formatting changes:
546
+
547
+ | Method | Description | Example |
548
+ | ------------------------------------------ | ------------------------------------ | -------------------------------------------------- |
549
+ | `doc.defragmentHyperlinks(options?)` | Merge fragmented hyperlinks by URL | `doc.defragmentHyperlinks({ resetFormatting: true })` |
550
+ | `hyperlink.resetToStandardFormatting()` | Reset to standard blue underlined | `hyperlink.resetToStandardFormatting()` |
551
+
552
+ **Options:**
553
+ - `resetFormatting?: boolean` - Reset hyperlinks to standard style (Calibri, blue, underline)
554
+ - `cleanupRelationships?: boolean` - Remove orphaned relationship entries
555
+
556
+ **Example:**
557
+ ```typescript
558
+ // Load document with fragmented hyperlinks from Google Docs
559
+ const doc = await Document.load('google-docs-export.docx');
560
+
561
+ // Fix fragmented hyperlinks and reset corrupted formatting
562
+ doc.defragmentHyperlinks({
563
+ resetFormatting: true, // Fix Caveat font and other issues
564
+ cleanupRelationships: true // Clean up relationship table
565
+ });
566
+
567
+ // Save cleaned document
568
+ await doc.save('fixed.docx');
569
+ ```
570
+
571
+ ### Headers & Footers
572
+
573
+ | Method | Description | Example |
574
+ | ---------------------------- | ------------------ | -------------------------------- |
575
+ | `setHeader(header)` | Set default header | `doc.setHeader(myHeader)` |
576
+ | `setFooter(footer)` | Set default footer | `doc.setFooter(myFooter)` |
577
+ | `setFirstPageHeader(header)` | First page header | `doc.setFirstPageHeader(header)` |
578
+ | `setFirstPageFooter(footer)` | First page footer | `doc.setFirstPageFooter(footer)` |
579
+ | `setEvenPageHeader(header)` | Even page header | `doc.setEvenPageHeader(header)` |
580
+ | `setEvenPageFooter(footer)` | Even page footer | `doc.setEvenPageFooter(footer)` |
581
+
582
+ ### Page Setup
583
+
584
+ | Method | Description | Example |
585
+ | ------------------------------------- | ----------------- | ------------------------------------- |
586
+ | `setPageSize(width, height, orient?)` | Page dimensions | `doc.setPageSize(12240, 15840)` |
587
+ | `setPageOrientation(orientation)` | Page orientation | `doc.setPageOrientation('landscape')` |
588
+ | `setMargins(margins)` | Page margins | `doc.setMargins({top: 1440, ...})` |
589
+ | `setLanguage(language)` | Document language | `doc.setLanguage('en-US')` |
590
+
591
+ ### Document Properties
592
+
593
+ | Method | Description | Properties |
594
+ | ---------------------- | ------------ | ------------------------------------- |
595
+ | `setProperties(props)` | Set metadata | `{title, subject, creator, keywords}` |
596
+ | `getProperties()` | Get metadata | Returns all properties |
597
+
598
+ ### Advanced Features
599
+
600
+ #### Bookmarks
601
+
602
+ | Method | Description |
603
+ | ---------------------------------------- | ------------------- |
604
+ | `createBookmark(name)` | Create bookmark |
605
+ | `createHeadingBookmark(text)` | Auto-named bookmark |
606
+ | `getBookmark(name)` | Get by name |
607
+ | `hasBookmark(name)` | Check existence |
608
+ | `addBookmarkToParagraph(para, bookmark)` | Add to paragraph |
609
+
610
+ #### Comments
611
+
612
+ | Method | Description |
613
+ | ------------------------------------------- | ----------------- |
614
+ | `createComment(author, content, initials?)` | Add comment |
615
+ | `createReply(parentId, author, content)` | Reply to comment |
616
+ | `getComment(id)` | Get by ID |
617
+ | `getAllComments()` | Get all top-level |
618
+ | `addCommentToParagraph(para, comment)` | Add to paragraph |
619
+
620
+ #### Track Changes
621
+
622
+ | Method | Description |
623
+ | ------------------------------------ | ----------------- |
624
+ | `trackInsertion(para, author, text)` | Track insertion |
625
+ | `trackDeletion(para, author, text)` | Track deletion |
626
+ | `isTrackingChanges()` | Check if tracking |
627
+ | `getRevisionStats()` | Get statistics |
628
+
629
+ #### Footnotes & Endnotes
630
+
631
+ | Method | Description |
632
+ | -------------------------- | ---------------- |
633
+ | `FootnoteManager.create()` | Manage footnotes |
634
+ | `EndnoteManager.create()` | Manage endnotes |
635
+
636
+ ### Low-Level Document Parts
637
+
638
+ | Method | Description | Example |
639
+ | ---------------------------- | --------------------- | ------------------------------------------------- |
640
+ | `getPart(partName)` | Get document part | `doc.getPart('word/document.xml')` |
641
+ | `setPart(partName, content)` | Set document part | `doc.setPart('custom.xml', data)` |
642
+ | `removePart(partName)` | Remove part | `doc.removePart('custom.xml')` |
643
+ | `listParts()` | List all parts | `const parts = await doc.listParts()` |
644
+ | `partExists(partName)` | Check part exists | `if (await doc.partExists('...'))` |
645
+ | `getContentTypes()` | Get content types | `const types = await doc.getContentTypes()` |
646
+ | `addContentType(part, type)` | Register content type | `doc.addContentType('.json', 'application/json')` |
647
+
648
+ ### Unit Conversion Utilities
649
+
650
+ #### Twips Conversions
651
+ | Function | Description | Example |
652
+ | ------------------------- | ------------------- | --------------------------- |
653
+ | `twipsToPoints(twips)` | Twips to points | `twipsToPoints(240)` // 12 |
654
+ | `twipsToInches(twips)` | Twips to inches | `twipsToInches(1440)` // 1 |
655
+ | `twipsToCm(twips)` | Twips to cm | `twipsToCm(1440)` // 2.54 |
656
+ | `twipsToEmus(twips)` | Twips to EMUs | `twipsToEmus(1440)` |
657
+
658
+ #### EMUs (English Metric Units) Conversions
659
+ | Function | Description | Example |
660
+ | --------------------------- | -------------------- | ----------------------------- |
661
+ | `emusToTwips(emus)` | EMUs to twips | `emusToTwips(914400)` // 1440 |
662
+ | `emusToInches(emus)` | EMUs to inches | `emusToInches(914400)` // 1 |
663
+ | `emusToCm(emus)` | EMUs to cm | `emusToCm(914400)` // 2.54 |
664
+ | `emusToPoints(emus)` | EMUs to points | `emusToPoints(914400)` // 72 |
665
+ | `emusToPixels(emus, dpi?)` | EMUs to pixels | `emusToPixels(914400)` // 96 |
666
+
667
+ #### Points Conversions
668
+ | Function | Description | Example |
669
+ | ------------------------ | ------------------ | -------------------------- |
670
+ | `pointsToTwips(points)` | Points to twips | `pointsToTwips(12)` // 240 |
671
+ | `pointsToEmus(points)` | Points to EMUs | `pointsToEmus(72)` |
672
+ | `pointsToInches(points)` | Points to inches | `pointsToInches(72)` // 1 |
673
+ | `pointsToCm(points)` | Points to cm | `pointsToCm(72)` // 2.54 |
674
+
675
+ #### Inches Conversions
676
+ | Function | Description | Example |
677
+ | ----------------------------- | ------------------- | ----------------------------- |
678
+ | `inchesToTwips(inches)` | Inches to twips | `inchesToTwips(1)` // 1440 |
679
+ | `inchesToEmus(inches)` | Inches to EMUs | `inchesToEmus(1)` // 914400 |
680
+ | `inchesToPoints(inches)` | Inches to points | `inchesToPoints(1)` // 72 |
681
+ | `inchesToCm(inches)` | Inches to cm | `inchesToCm(1)` // 2.54 |
682
+ | `inchesToPixels(inches, dpi)` | Inches to pixels | `inchesToPixels(1, 96)` // 96 |
683
+
684
+ #### Centimeters Conversions
685
+ | Function | Description | Example |
686
+ | ----------------------- | ---------------- | --------------------------- |
687
+ | `cmToTwips(cm)` | cm to twips | `cmToTwips(2.54)` // 1440 |
688
+ | `cmToEmus(cm)` | cm to EMUs | `cmToEmus(2.54)` // 914400 |
689
+ | `cmToInches(cm)` | cm to inches | `cmToInches(2.54)` // 1 |
690
+ | `cmToPoints(cm)` | cm to points | `cmToPoints(2.54)` // 72 |
691
+ | `cmToPixels(cm, dpi?)` | cm to pixels | `cmToPixels(2.54, 96)` // 96|
692
+
693
+ #### Pixels Conversions
694
+ | Function | Description | Example |
695
+ | ---------------------------- | ------------------- | ------------------------------ |
696
+ | `pixelsToEmus(pixels, dpi?)` | Pixels to EMUs | `pixelsToEmus(96)` // 914400 |
697
+ | `pixelsToInches(pixels, dpi?)`| Pixels to inches | `pixelsToInches(96, 96)` // 1 |
698
+ | `pixelsToTwips(pixels, dpi?)`| Pixels to twips | `pixelsToTwips(96, 96)` // 1440|
699
+ | `pixelsToCm(pixels, dpi?)` | Pixels to cm | `pixelsToCm(96, 96)` // 2.54 |
700
+ | `pixelsToPoints(pixels, dpi?)`| Pixels to points | `pixelsToPoints(96, 96)` // 72 |
701
+
702
+ **Note:** Default DPI is 96 for pixel conversions
703
+
704
+ ### ZIP Archive Helper Methods
705
+
706
+ #### File Operations
707
+ | Method | Description | Example |
708
+ | ------------------------------- | ------------------------- | -------------------------------------------- |
709
+ | `addFile(path, content)` | Add file to archive | `handler.addFile('doc.xml', xmlContent)` |
710
+ | `updateFile(path, content)` | Update existing file | `handler.updateFile('doc.xml', newContent)` |
711
+ | `removeFile(path)` | Remove file from archive | `handler.removeFile('old.xml')` |
712
+ | `renameFile(oldPath, newPath)` | Rename file | `handler.renameFile('a.xml', 'b.xml')` |
713
+ | `copyFile(srcPath, destPath)` | Copy file | `handler.copyFile('a.xml', 'copy-a.xml')` |
714
+ | `moveFile(srcPath, destPath)` | Move file | `handler.moveFile('a.xml', 'folder/a.xml')` |
715
+
716
+ #### File Retrieval
717
+ | Method | Description | Returns |
718
+ | ------------------------- | ---------------------- | --------------- |
719
+ | `getFile(path)` | Get file object | `ZipFile` |
720
+ | `getFileAsString(path)` | Get file as string | `string` |
721
+ | `getFileAsBuffer(path)` | Get file as buffer | `Buffer` |
722
+ | `hasFile(path)` | Check if file exists | `boolean` |
723
+ | `getFilePaths()` | Get all file paths | `string[]` |
724
+ | `getAllFiles()` | Get all files | `FileMap` |
725
+
726
+ #### Batch Operations
727
+ | Method | Description | Returns |
728
+ | ------------------------------- | ---------------------------- | -------------- |
729
+ | `removeFiles(paths[])` | Remove multiple files | `number` |
730
+ | `getFilesByExtension(ext)` | Get files by extension | `ZipFile[]` |
731
+ | `getTextFiles()` | Get all text files | `ZipFile[]` |
732
+ | `getBinaryFiles()` | Get all binary files | `ZipFile[]` |
733
+ | `getMediaFiles()` | Get media files | `ZipFile[]` |
734
+
735
+ #### Archive Information
736
+ | Method | Description | Returns |
737
+ | ------------------ | ------------------------- | ------------------------ |
738
+ | `getFileCount()` | Count files in archive | `number` |
739
+ | `getTotalSize()` | Get total size in bytes | `number` |
740
+ | `getStats()` | Get detailed statistics | `{fileCount, size, ...}` |
741
+ | `isEmpty()` | Check if archive is empty | `boolean` |
742
+
743
+ #### Import/Export
744
+ | Method | Description | Returns |
745
+ | -------------------------------- | ------------------------ | -------------------- |
746
+ | `exportFile(internal, external)` | Export file from archive | `Promise<void>` |
747
+ | `importFile(external, internal)` | Import file to archive | `Promise<void>` |
748
+
749
+ ## Common Recipes
750
+
751
+ ### Create a Simple Document
752
+
753
+ ```typescript
754
+ const doc = Document.create();
755
+ doc.createParagraph("Title").setStyle("Title");
756
+ doc.createParagraph("This is a simple document.");
757
+ await doc.save("simple.docx");
758
+ ```
759
+
760
+ ### Add Formatted Text
761
+
762
+ ```typescript
763
+ const para = doc.createParagraph();
764
+ para.addText("Bold", { bold: true });
765
+ para.addText(" and ");
766
+ para.addText("Colored", { color: "FF0000" });
767
+ ```
768
+
769
+ ### Create a Table with Borders
770
+
771
+ ```typescript
772
+ const table = doc.createTable(3, 3);
773
+ table.setAllBorders({ style: "single", size: 8, color: "000000" });
774
+ table.getCell(0, 0)?.createParagraph("Header 1");
775
+ table.getRow(0)?.getCell(0)?.setShading({ fill: "4472C4" });
776
+ ```
777
+
778
+ ### Insert an Image
779
+
780
+ ```typescript
781
+ import { Image, inchesToEmus } from "docxmlater";
782
+
783
+ const image = Image.fromFile("./photo.jpg");
784
+ image.setWidth(inchesToEmus(4), true); // 4 inches, maintain ratio
785
+ doc.addImage(image);
786
+ ```
787
+
788
+ ### Add a Hyperlink
789
+
790
+ ```typescript
791
+ const para = doc.createParagraph();
792
+ para.addText("Visit ");
793
+ para.addHyperlink(
794
+ Hyperlink.createExternal("https://example.com", "our website")
795
+ );
796
+ ```
797
+
798
+ ### Search and Replace Text
799
+
800
+ ```typescript
801
+ // Find all occurrences
802
+ const results = doc.findText("old text", { caseSensitive: true });
803
+ console.log(`Found ${results.length} occurrences`);
804
+
805
+ // Replace all
806
+ const count = doc.replaceText("old text", "new text", { wholeWord: true });
807
+ console.log(`Replaced ${count} occurrences`);
808
+ ```
809
+
810
+ ### Load and Modify Existing Document
811
+
812
+ ```typescript
813
+ const doc = await Document.load("existing.docx");
814
+ doc.createParagraph("Added paragraph");
815
+
816
+ // Update all hyperlinks
817
+ const urlMap = new Map([["https://old-site.com", "https://new-site.com"]]);
818
+ doc.updateHyperlinkUrls(urlMap);
819
+
820
+ await doc.save("modified.docx");
821
+ ```
822
+
823
+ ### Create Lists
824
+
825
+ ```typescript
826
+ // Bullet list
827
+ const bulletId = doc.createBulletList(3);
828
+ doc.createParagraph("First item").setNumbering(bulletId, 0);
829
+ doc.createParagraph("Second item").setNumbering(bulletId, 0);
830
+
831
+ // Numbered list
832
+ const numberId = doc.createNumberedList(3);
833
+ doc.createParagraph("Step 1").setNumbering(numberId, 0);
834
+ doc.createParagraph("Step 2").setNumbering(numberId, 0);
835
+ ```
836
+
837
+ ### Apply Custom Styles
838
+
839
+ ```typescript
840
+ import { Style } from "docxmlater";
841
+
842
+ const customStyle = Style.create({
843
+ styleId: "CustomHeading",
844
+ name: "Custom Heading",
845
+ basedOn: "Normal",
846
+ runFormatting: { bold: true, size: 14, color: "2E74B5" },
847
+ paragraphFormatting: { alignment: "center", spaceAfter: 240 },
848
+ });
849
+
850
+ doc.addStyle(customStyle);
851
+ doc.createParagraph("Custom Styled Text").setStyle("CustomHeading");
852
+ ```
853
+
854
+ ### Build Content with Detached Paragraphs
855
+
856
+ Create paragraphs independently and add them conditionally:
857
+
858
+ ```typescript
859
+ import { Paragraph } from "docxmlater";
860
+
861
+ // Create reusable paragraph templates
862
+ const warningTemplate = Paragraph.createFormatted(
863
+ "WARNING: ",
864
+ { bold: true, color: "FF6600" },
865
+ { spacing: { before: 120, after: 120 } }
866
+ );
867
+
868
+ // Clone and customize
869
+ const warning1 = warningTemplate.clone();
870
+ warning1.addText("Please read the documentation before proceeding.");
871
+
872
+ // Build content from data
873
+ const items = [
874
+ { title: "First Item", description: "Description here" },
875
+ { title: "Second Item", description: "Another description" },
876
+ ];
877
+
878
+ items.forEach((item, index) => {
879
+ const titlePara = Paragraph.create(`${index + 1}. `);
880
+ titlePara.addText(item.title, { bold: true });
881
+
882
+ const descPara = Paragraph.create(item.description, {
883
+ indentation: { left: 360 },
884
+ });
885
+
886
+ doc.addParagraph(titlePara);
887
+ doc.addParagraph(descPara);
888
+ });
889
+
890
+ // See examples/advanced/detached-paragraphs.ts for more patterns
891
+ ```
892
+
893
+ ### Add Headers and Footers
894
+
895
+ ```typescript
896
+ import { Header, Footer, Field } from "docxmlater";
897
+
898
+ // Header with page numbers
899
+ const header = Header.create();
900
+ header.addParagraph("Document Title").setAlignment("center");
901
+
902
+ // Footer with page numbers
903
+ const footer = Footer.create();
904
+ const footerPara = footer.addParagraph();
905
+ footerPara.addText("Page ");
906
+ footerPara.addField(Field.create({ type: "PAGE" }));
907
+ footerPara.addText(" of ");
908
+ footerPara.addField(Field.create({ type: "NUMPAGES" }));
909
+
910
+ doc.setHeader(header);
911
+ doc.setFooter(footer);
912
+ ```
913
+
914
+ ### Work with Document Statistics
915
+
916
+ ```typescript
917
+ // Get word and character counts
918
+ console.log("Words:", doc.getWordCount());
919
+ console.log("Characters:", doc.getCharacterCount());
920
+ console.log("Characters (no spaces):", doc.getCharacterCount(false));
921
+
922
+ // Check document size
923
+ const size = doc.estimateSize();
924
+ if (size.warning) {
925
+ console.warn(size.warning);
926
+ }
927
+ console.log(`Estimated size: ${size.totalEstimatedMB} MB`);
928
+ ```
929
+
930
+ ### Handle Large Documents Efficiently
931
+
932
+ ```typescript
933
+ const doc = Document.create({
934
+ maxMemoryUsagePercent: 80,
935
+ maxRssMB: 2048,
936
+ maxImageCount: 50,
937
+ maxTotalImageSizeMB: 100,
938
+ });
939
+
940
+ // Process document...
941
+
942
+ // Clean up resources after saving
943
+ await doc.save("large-document.docx");
944
+ doc.dispose(); // Free memory
945
+ ```
946
+
947
+ ### Direct XML Access (Advanced)
948
+
949
+ ```typescript
950
+ // Get raw XML
951
+ const documentXml = await doc.getPart("word/document.xml");
952
+ console.log(documentXml?.content);
953
+
954
+ // Modify raw XML (use with caution)
955
+ await doc.setPart("word/custom.xml", "<custom>data</custom>");
956
+ await doc.addContentType("/word/custom.xml", "application/xml");
957
+
958
+ // List all parts
959
+ const parts = await doc.listParts();
960
+ console.log("Document contains:", parts.length, "parts");
961
+ ```
962
+
963
+ ### Fix Fragmented Hyperlinks from Google Docs
964
+
965
+ **Problem:** Documents exported from Google Docs often have fragmented hyperlinks where a single URL appears multiple times as separate hyperlinks, sometimes with corrupted fonts (e.g., Caveat instead of Calibri).
966
+
967
+ **Solution:** Use the hyperlink defragmentation API to automatically merge and fix these issues.
968
+
969
+ ```typescript
970
+ import { Document } from "docxmlater";
971
+
972
+ async function fixGoogleDocsHyperlinks(inputPath: string, outputPath: string) {
973
+ // Load document
974
+ const doc = await Document.load(inputPath);
975
+
976
+ // Get hyperlinks before defragmentation
977
+ const hyperlinksBefore = doc.getHyperlinks();
978
+ console.log(`Found ${hyperlinksBefore.length} hyperlinks before defragmentation`);
979
+
980
+ // Defragment hyperlinks and reset formatting
981
+ doc.defragmentHyperlinks({
982
+ resetFormatting: true, // Fix corrupted fonts (Caveat → Calibri)
983
+ cleanupRelationships: true // Remove orphaned relationships
984
+ });
985
+
986
+ // Check results
987
+ const hyperlinksAfter = doc.getHyperlinks();
988
+ console.log(`Reduced to ${hyperlinksAfter.length} hyperlinks after defragmentation`);
989
+ console.log(`Merged ${hyperlinksBefore.length - hyperlinksAfter.length} duplicate hyperlinks`);
990
+
991
+ // Save fixed document
992
+ await doc.save(outputPath);
993
+ doc.dispose();
994
+
995
+ console.log(`✅ Fixed document saved to ${outputPath}`);
996
+ }
997
+
998
+ // Example usage
999
+ await fixGoogleDocsHyperlinks('input.docx', 'output.docx');
1000
+ ```
1001
+
1002
+ **What it fixes:**
1003
+ - Merges hyperlinks with the same URL across paragraphs and tables
1004
+ - Handles non-consecutive fragments (hyperlinks separated by other content)
1005
+ - Resets corrupted fonts to standard Calibri
1006
+ - Applies standard hyperlink style (blue, underlined)
1007
+ - Removes orphaned relationship entries
1008
+
1009
+ **Advanced usage - Manual control:**
1010
+ ```typescript
1011
+ // Get all hyperlinks grouped by URL
1012
+ const hyperlinks = doc.getHyperlinks();
1013
+ const urlGroups = new Map<string, typeof hyperlinks>();
1014
+
1015
+ hyperlinks.forEach(({ hyperlink, paragraph }) => {
1016
+ const url = hyperlink.getUrl();
1017
+ if (!urlGroups.has(url)) {
1018
+ urlGroups.set(url, []);
1019
+ }
1020
+ urlGroups.get(url)!.push({ hyperlink, paragraph });
1021
+ });
1022
+
1023
+ // Identify fragmented hyperlinks (URLs appearing multiple times)
1024
+ urlGroups.forEach((group, url) => {
1025
+ if (group.length > 1) {
1026
+ console.log(`URL "${url}" is fragmented into ${group.length} hyperlinks`);
1027
+
1028
+ // Manually reset formatting for each fragment
1029
+ group.forEach(({ hyperlink }) => {
1030
+ hyperlink.resetToStandardFormatting();
1031
+ });
1032
+ }
1033
+ });
1034
+
1035
+ await doc.save('manually-fixed.docx');
1036
+ ```
1037
+
1038
+ ## Features
1039
+
1040
+ - **Full OpenXML Compliance** - Follows ECMA-376 standard
1041
+ - **TypeScript First** - Complete type definitions
1042
+ - **Memory Efficient** - Handles large documents with streaming
1043
+ - **Atomic Saves** - Prevents corruption with temp file pattern
1044
+ - **Rich Formatting** - Complete text and paragraph formatting
1045
+ - **Tables** - Full support with borders, shading, merging
1046
+ - **Images** - PNG, JPEG, GIF with sizing and positioning
1047
+ - **Hyperlinks** - External, internal, and email links
1048
+ - **Hyperlink Defragmentation** - Fix fragmented hyperlinks from Google Docs imports (v1.15.0+)
1049
+ - **Styles** - 13 built-in styles + custom style creation
1050
+ - **Lists** - Bullets, numbering, multi-level with smart indentation
1051
+ - **Special Characters** - Tabs, newlines, non-breaking hyphens (v1.14.0+)
1052
+ - **Headers/Footers** - Different first/even/odd pages with dynamic fields
1053
+ - **Search & Replace** - With case and whole word options
1054
+ - **Document Stats** - Word count, character count, size estimation
1055
+ - **Track Changes** - Insertions and deletions with authors
1056
+ - **Comments** - With replies and threading
1057
+ - **Bookmarks** - For internal navigation
1058
+ - **Table of Contents** - Multiple TOC styles with customization
1059
+ - **Content Controls** - 9 control types for interactive documents
1060
+ - **Fields** - 11 field types including page numbers and cross-references
1061
+ - **Low-level Access** - Direct ZIP and XML manipulation
1062
+
1063
+ ## Performance
1064
+
1065
+ - Process 100+ page documents efficiently
1066
+ - Atomic save pattern prevents corruption
1067
+ - Memory management for large files
1068
+ - Lazy loading of document parts
1069
+ - Resource cleanup with `dispose()`
1070
+
1071
+ ## Testing
1072
+
1073
+ ```bash
1074
+ npm test # Run all tests
1075
+ npm run test:watch # Watch mode
1076
+ npm run test:coverage # Coverage report
1077
+ ```
1078
+
1079
+ **Current:** 2,073 tests passing | 100% pass rate | 100% core functionality covered
1080
+
1081
+ ## Development
1082
+
1083
+ ```bash
1084
+ # Install dependencies
1085
+ npm install
1086
+
1087
+ # Build TypeScript
1088
+ npm run build
1089
+
1090
+ # Run examples
1091
+ npx ts-node examples/simple-document.ts
1092
+ ```
1093
+
1094
+ ## Project Structure
1095
+
1096
+ ```text
1097
+ src/
1098
+ ├── core/ # Document, Parser, Generator, Validator
1099
+ ├── elements/ # Paragraph, Run, Table, Image, Hyperlink
1100
+ ├── formatting/ # Style, NumberingManager
1101
+ ├── xml/ # XMLBuilder, XMLParser
1102
+ ├── zip/ # ZipHandler for DOCX manipulation
1103
+ └── utils/ # Validation, Units conversion
1104
+
1105
+ examples/
1106
+ ├── 01-basic/ # Simple document creation
1107
+ ├── 02-text/ # Text formatting examples
1108
+ ├── 03-tables/ # Table examples
1109
+ ├── 04-styles/ # Style examples
1110
+ ├── 05-images/ # Image handling
1111
+ ├── 06-complete/ # Full document examples
1112
+ └── 07-hyperlinks/ # Link examples
1113
+ ```
1114
+
1115
+ ## Hierarchy
1116
+
1117
+ ```text
1118
+ w:document (root)
1119
+ └── w:body (body container)
1120
+ ├── w:p (paragraph) [1..n]
1121
+ │ ├── w:pPr (paragraph properties) [0..1]
1122
+ │ │ ├── w:pStyle (style reference)
1123
+ │ │ ├── w:jc (justification/alignment)
1124
+ │ │ ├── w:ind (indentation)
1125
+ │ │ └── w:spacing (spacing before/after)
1126
+ │ ├── w:r (run) [1..n]
1127
+ │ │ ├── w:rPr (run properties) [0..1]
1128
+ │ │ │ ├── w:b (bold)
1129
+ │ │ │ ├── w:i (italic)
1130
+ │ │ │ ├── w:u (underline)
1131
+ │ │ │ ├── w:sz (font size)
1132
+ │ │ │ └── w:color (text color)
1133
+ │ │ └── w:t (text content) [1]
1134
+ │ ├── w:hyperlink (hyperlink) [0..n]
1135
+ │ │ └── w:r (run with hyperlink text)
1136
+ │ └── w:drawing (embedded image/shape) [0..n]
1137
+ ├── w:tbl (table) [1..n]
1138
+ │ ├── w:tblPr (table properties)
1139
+ │ └── w:tr (table row) [1..n]
1140
+ │ └── w:tc (table cell) [1..n]
1141
+ │ └── w:p (paragraph in cell)
1142
+ └── w:sectPr (section properties) [1] (must be last child of w:body)
1143
+ ```
1144
+
1145
+ ## Requirements
1146
+
1147
+ - Node.js 16+
1148
+ - TypeScript 5.0+ (for development)
1149
+
1150
+ ## Installation Options
1151
+
1152
+ ```bash
1153
+ # NPM
1154
+ npm install docxmlater
1155
+
1156
+ # Yarn
1157
+ yarn add docxmlater
1158
+
1159
+ # PNPM
1160
+ pnpm add docxmlater
1161
+ ```
1162
+
1163
+ ## Troubleshooting
1164
+
1165
+ ### XML Corruption in Text
1166
+
1167
+ **Problem**: Text displays with XML tags like `Important Information<w:t xml:space="preserve">1` in Word.
1168
+
1169
+ **Cause**: Passing XML-like strings to text methods instead of using the API properly.
1170
+
1171
+ ```typescript
1172
+ // WRONG - Will display escaped XML as literal text
1173
+ paragraph.addText("Important Information<w:t>1</w:t>");
1174
+ // Displays as: "Important Information<w:t>1</w:t>"
1175
+
1176
+ // CORRECT - Use separate text runs
1177
+ paragraph.addText("Important Information");
1178
+ paragraph.addText("1");
1179
+ // Displays as: "Important Information1"
1180
+
1181
+ // Or combine in one call
1182
+ paragraph.addText("Important Information 1");
1183
+ ```
1184
+
1185
+ **Detection**: Use the corruption detection utility to find issues:
1186
+
1187
+ ```typescript
1188
+ import { detectCorruptionInDocument } from "docxmlater";
1189
+
1190
+ const doc = await Document.load("file.docx");
1191
+ const report = detectCorruptionInDocument(doc);
1192
+
1193
+ if (report.isCorrupted) {
1194
+ console.log(report.summary);
1195
+ report.locations.forEach((loc) => {
1196
+ console.log(`Paragraph ${loc.paragraphIndex}, Run ${loc.runIndex}:`);
1197
+ console.log(` Original: ${loc.text}`);
1198
+ console.log(` Fixed: ${loc.suggestedFix}`);
1199
+ });
1200
+ }
1201
+ ```
1202
+
1203
+ **Auto-Cleaning**: XML patterns are automatically removed by default for defensive data handling:
1204
+
1205
+ ```typescript
1206
+ // Default behavior - auto-clean enabled
1207
+ const run = new Run("Text<w:t>value</w:t>");
1208
+ // Result: "Textvalue" (XML tags removed automatically)
1209
+
1210
+ // Disable auto-cleaning (for debugging)
1211
+ const run = new Run("Text<w:t>value</w:t>", { cleanXmlFromText: false });
1212
+ // Result: "Text<w:t>value</w:t>" (XML tags preserved, will display in Word)
1213
+ ```
1214
+
1215
+ **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.
1216
+
1217
+ **The Right Approach**: Use the framework's API methods instead of embedding XML:
1218
+
1219
+ - Use `paragraph.addText()` multiple times for separate text runs
1220
+ - Use formatting options: `{bold: true}`, `{italic: true}`, etc.
1221
+ - Use `paragraph.addHyperlink()` for links
1222
+ - Don't pass XML strings to text methods
1223
+ - Don't try to embed `<w:t>` or other XML tags in your text
1224
+
1225
+ For more details, see the [corruption detection examples](examples/troubleshooting/).
1226
+
1227
+ ### Layout Conflicts (Massive Whitespace)
1228
+
1229
+ **Problem**: Documents show massive whitespace between paragraphs when opened in Word, even though the XML looks correct.
1230
+
1231
+ **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.
1232
+
1233
+ **Why This Causes Problems**:
1234
+
1235
+ - `pageBreakBefore` tells Word to insert a page break before the paragraph
1236
+ - `keepNext` tells Word to keep the paragraph with the next one (no break)
1237
+ - `keepLines` tells Word to keep all lines together (no break)
1238
+ - The combination creates layout conflicts that manifest as massive whitespace
1239
+
1240
+ **Automatic Conflict Resolution** (v0.28.2+):
1241
+
1242
+ The framework now automatically prevents these conflicts by **prioritizing keep properties over page breaks**:
1243
+
1244
+ ```typescript
1245
+ // When setting keepNext or keepLines, pageBreakBefore is automatically cleared
1246
+ const para = new Paragraph()
1247
+ .addText("Content")
1248
+ .setPageBreakBefore(true) // Set to true
1249
+ .setKeepNext(true); // Automatically clears pageBreakBefore
1250
+
1251
+ // Result: keepNext=true, pageBreakBefore=false (conflict resolved)
1252
+ ```
1253
+
1254
+ **Why This Priority?**
1255
+
1256
+ - Keep properties (`keepNext`/`keepLines`) represent explicit user intent to keep content together
1257
+ - Page breaks are often layout hints that may conflict with document flow
1258
+ - Removing `pageBreakBefore` eliminates whitespace while preserving the user's intention
1259
+
1260
+ **Parsing Documents**:
1261
+
1262
+ When loading existing DOCX files with conflicts, they are automatically resolved:
1263
+
1264
+ ```typescript
1265
+ // Load document with conflicts
1266
+ const doc = await Document.load("document-with-conflicts.docx");
1267
+
1268
+ // Conflicts are automatically resolved during parsing
1269
+ // keepNext/keepLines take priority, pageBreakBefore is removed
1270
+ ```
1271
+
1272
+ **How It Works**:
1273
+
1274
+ 1. When `setKeepNext(true)` is called, `pageBreakBefore` is automatically set to `false`
1275
+ 2. When `setKeepLines(true)` is called, `pageBreakBefore` is automatically set to `false`
1276
+ 3. When parsing documents, if both properties exist, `pageBreakBefore` is cleared
1277
+ 4. Keep properties win because they represent explicit user intent
1278
+
1279
+ **Manual Override**:
1280
+
1281
+ If you need a page break despite keep properties, set it after:
1282
+
1283
+ ```typescript
1284
+ const para = new Paragraph()
1285
+ .setKeepNext(true) // Set first
1286
+ .setPageBreakBefore(true); // Override - you explicitly want this conflict
1287
+
1288
+ // But note: This will cause layout issues (whitespace) in Word
1289
+ ```
1290
+
1291
+ ## Contributing
1292
+
1293
+ Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md).
1294
+
1295
+ 1. Fork the repository
1296
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
1297
+ 3. Commit changes (`git commit -m 'Add amazing feature'`)
1298
+ 4. Push to branch (`git push origin feature/amazing-feature`)
1299
+ 5. Open a Pull Request
1300
+
1301
+ ## Recent Updates (v0.20.1)
1302
+
1303
+ **Critical Bug Fix Release:**
1304
+
1305
+ - **Fixed Paragraph.getText()** - Now includes hyperlink text content (critical data loss bug)
1306
+ - **Added hyperlink integration tests** - 6 new comprehensive test cases
1307
+ - **Enhanced test suite** - 474/478 tests passing (98.1% pass rate)
1308
+ - **Fixed type safety** - XMLElement handling improvements across test files
1309
+ - **Improved StylesManager** - XML corruption detection moved before parser
1310
+ - **Hyperlink management** - Proper relationship ID clearing on URL updates
1311
+
1312
+ **What This Fixes:**
1313
+ When using `para.addText('foo') + para.addHyperlink(link)`, the hyperlink text is now properly included in `paragraph.getText()`, preventing silent text loss.
1314
+
1315
+ ## License
1316
+
1317
+ MIT © DiaTech
1318
+
1319
+ ## Acknowledgments
1320
+
1321
+ - Built with [JSZip](https://stuk.github.io/jszip/) for ZIP handling
1322
+ - Follows [ECMA-376](https://www.ecma-international.org/publications-and-standards/standards/ecma-376/) Office Open XML standard
1323
+ - Inspired by [python-docx](https://python-docx.readthedocs.io/) and [docx](https://github.com/dolanmiu/docx)
1324
+
1325
+ ## Support
1326
+
1327
+ - **Documentation**: [Full Docs](https://github.com/ItMeDiaTech/docXMLater/tree/main/docs)
1328
+ - **Examples**: [Example Code](https://github.com/ItMeDiaTech/docXMLater/tree/main/examples)
1329
+ - **Issues**: [GitHub Issues](https://github.com/ItMeDiaTech/docXMLater/issues)
1330
+ - **Discussions**: [GitHub Discussions](https://github.com/ItMeDiaTech/docXMLater/discussions)
1331
+
1332
+ ## Quick Links
1333
+
1334
+ - [NPM Package](https://www.npmjs.com/package/docxmlater)
1335
+ - [GitHub Repository](https://github.com/ItMeDiaTech/docXMLater)
1336
+ - [API Reference](https://github.com/ItMeDiaTech/docXMLater/tree/main/docs/api)
1337
+ - [Change Log](https://github.com/ItMeDiaTech/docXMLater/blob/main/CHANGELOG.md)
1338
+
1339
+ ---
1340
+
1341
+ **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!