@docx4j/core-ts 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/NOTICE +4 -0
  2. package/README.md +92 -4
  3. package/dist/html/convert.mjs +2 -1
  4. package/dist/html/elements.mjs +2 -1
  5. package/dist/model/color/ColorMath.d.mts +29 -0
  6. package/dist/model/color/ColorMath.mjs +146 -0
  7. package/dist/model/color/ColorTables.d.mts +6 -0
  8. package/dist/model/color/ColorTables.mjs +83 -0
  9. package/dist/model/color/DrawingMLColors.d.mts +52 -0
  10. package/dist/model/color/DrawingMLColors.mjs +374 -0
  11. package/dist/model/color/ResolvedColor.d.mts +36 -0
  12. package/dist/model/color/ResolvedColor.mjs +65 -0
  13. package/dist/model/color/SpreadsheetColors.d.mts +36 -0
  14. package/dist/model/color/SpreadsheetColors.mjs +84 -0
  15. package/dist/model/color/StyleFill.d.mts +28 -0
  16. package/dist/model/color/StyleFill.mjs +21 -0
  17. package/dist/model/color/ThemeColors.d.mts +86 -0
  18. package/dist/model/color/ThemeColors.mjs +304 -0
  19. package/dist/model/color/WordColors.d.mts +71 -0
  20. package/dist/model/color/WordColors.mjs +121 -0
  21. package/dist/model/color/index.d.mts +6 -0
  22. package/dist/model/color/index.mjs +30 -0
  23. package/dist/model/content/Body.d.mts +145 -23
  24. package/dist/model/content/Body.mjs +507 -150
  25. package/dist/model/content/Comment.d.mts +23 -5
  26. package/dist/model/content/Comment.mjs +207 -26
  27. package/dist/model/content/ContentControl.d.mts +198 -19
  28. package/dist/model/content/ContentControl.mjs +672 -99
  29. package/dist/model/content/Font.d.mts +70 -17
  30. package/dist/model/content/Font.mjs +119 -36
  31. package/dist/model/content/InlinePicture.d.mts +11 -0
  32. package/dist/model/content/InlinePicture.mjs +21 -1
  33. package/dist/model/content/List.d.mts +6 -3
  34. package/dist/model/content/List.mjs +18 -8
  35. package/dist/model/content/Paragraph.d.mts +200 -14
  36. package/dist/model/content/Paragraph.mjs +898 -82
  37. package/dist/model/content/Range.d.mts +227 -12
  38. package/dist/model/content/Range.mjs +785 -59
  39. package/dist/model/content/ResolvedView.d.mts +104 -0
  40. package/dist/model/content/ResolvedView.mjs +596 -0
  41. package/dist/model/content/Table.d.mts +29 -3
  42. package/dist/model/content/Table.mjs +135 -49
  43. package/dist/model/content/TrackedChange.d.mts +174 -15
  44. package/dist/model/content/TrackedChange.mjs +734 -170
  45. package/dist/model/content/comments.d.mts +20 -6
  46. package/dist/model/content/comments.mjs +67 -57
  47. package/dist/model/content/generalException.d.mts +18 -0
  48. package/dist/model/content/generalException.mjs +24 -0
  49. package/dist/model/content/index.d.mts +10 -3
  50. package/dist/model/content/index.mjs +10 -2
  51. package/dist/model/content/mathText.d.mts +21 -0
  52. package/dist/model/content/mathText.mjs +267 -0
  53. package/dist/model/content/officeText.d.mts +27 -0
  54. package/dist/model/content/officeText.mjs +74 -0
  55. package/dist/model/content/ooxml.mjs +12 -1
  56. package/dist/model/content/outline.d.mts +5 -0
  57. package/dist/model/content/outline.mjs +13 -0
  58. package/dist/model/content/paraIds.d.mts +5 -0
  59. package/dist/model/content/paraIds.mjs +54 -0
  60. package/dist/model/content/placeholders.d.mts +110 -0
  61. package/dist/model/content/placeholders.mjs +253 -0
  62. package/dist/model/content/search.d.mts +74 -0
  63. package/dist/model/content/search.mjs +255 -9
  64. package/dist/model/content/spanEdits.d.mts +159 -0
  65. package/dist/model/content/spanEdits.mjs +728 -0
  66. package/dist/model/content/story.d.mts +47 -0
  67. package/dist/model/content/story.mjs +95 -0
  68. package/dist/model/content/tableColumns.d.mts +1 -1
  69. package/dist/model/content/tableColumns.mjs +108 -80
  70. package/dist/model/content/tableRevisions.d.mts +11 -3
  71. package/dist/model/content/tableRevisions.mjs +72 -42
  72. package/dist/model/content/tableRules.d.mts +30 -0
  73. package/dist/model/content/tableRules.mjs +102 -0
  74. package/dist/model/content/textBoxes.d.mts +18 -0
  75. package/dist/model/content/textBoxes.mjs +105 -0
  76. package/dist/model/content/trackedChangeFilter.d.mts +39 -0
  77. package/dist/model/content/trackedChangeFilter.mjs +81 -0
  78. package/dist/model/content/tracking.d.mts +63 -6
  79. package/dist/model/content/tracking.mjs +242 -22
  80. package/dist/model/content/tree.d.mts +172 -24
  81. package/dist/model/content/tree.mjs +328 -54
  82. package/dist/model/content/wordRange.d.mts +48 -0
  83. package/dist/model/content/wordRange.mjs +97 -0
  84. package/dist/model/customxml/CustomXmlPartCollection.d.mts +30 -1
  85. package/dist/model/customxml/CustomXmlPartCollection.mjs +81 -12
  86. package/dist/model/customxml/XmlMapping.d.mts +32 -8
  87. package/dist/model/customxml/XmlMapping.mjs +64 -10
  88. package/dist/model/customxml/bindings.d.mts +33 -4
  89. package/dist/model/customxml/bindings.mjs +171 -45
  90. package/dist/model/customxml/index.d.mts +1 -0
  91. package/dist/model/customxml/index.mjs +1 -0
  92. package/dist/model/customxml/kinds.d.mts +8 -3
  93. package/dist/model/customxml/kinds.mjs +26 -5
  94. package/dist/model/customxml/opendope.d.mts +4 -1
  95. package/dist/model/customxml/opendope.mjs +9 -6
  96. package/dist/model/customxml/picturePlaceholder.d.mts +17 -0
  97. package/dist/model/customxml/picturePlaceholder.mjs +32 -0
  98. package/dist/model/customxml/pictures.d.mts +4 -3
  99. package/dist/model/customxml/pictures.mjs +22 -8
  100. package/dist/model/customxml/storeItems.d.mts +57 -0
  101. package/dist/model/customxml/storeItems.mjs +122 -0
  102. package/dist/model/customxml/xhtml.mjs +23 -22
  103. package/dist/model/dateZone.d.mts +25 -0
  104. package/dist/model/dateZone.mjs +57 -0
  105. package/dist/model/index.d.mts +3 -0
  106. package/dist/model/index.mjs +5 -0
  107. package/dist/model/listnumbering/Emulator.d.mts +37 -5
  108. package/dist/model/listnumbering/Emulator.mjs +63 -9
  109. package/dist/model/properties/PropertyResolver.d.mts +74 -2
  110. package/dist/model/properties/PropertyResolver.mjs +133 -5
  111. package/dist/model/properties/catalogue.d.mts +19 -5
  112. package/dist/model/properties/catalogue.mjs +41 -15
  113. package/dist/model/properties/index.d.mts +1 -1
  114. package/dist/model/properties/log.d.mts +1 -1
  115. package/dist/model/properties/log.mjs +5 -5
  116. package/dist/model/properties/styleUtil.d.mts +3 -1
  117. package/dist/model/properties/styleUtil.mjs +20 -24
  118. package/dist/model/properties/table.mjs +4 -2
  119. package/dist/model/text/CaseMapping.d.mts +38 -0
  120. package/dist/model/text/CaseMapping.mjs +86 -0
  121. package/dist/model/text/Extractor.d.mts +349 -0
  122. package/dist/model/text/Extractor.mjs +2406 -0
  123. package/dist/model/text/LinkTargets.d.mts +45 -0
  124. package/dist/model/text/LinkTargets.mjs +170 -0
  125. package/dist/model/text/MarkdownExporter.d.mts +140 -0
  126. package/dist/model/text/MarkdownExporter.mjs +255 -0
  127. package/dist/model/text/PlainText.d.mts +29 -0
  128. package/dist/model/text/PlainText.mjs +542 -0
  129. package/dist/model/text/TextContext.d.mts +256 -0
  130. package/dist/model/text/TextContext.mjs +600 -0
  131. package/dist/model/text/TextOptions.d.mts +233 -0
  132. package/dist/model/text/TextOptions.mjs +419 -0
  133. package/dist/model/text/TextResult.d.mts +621 -0
  134. package/dist/model/text/TextResult.mjs +629 -0
  135. package/dist/model/text/Texts.d.mts +99 -0
  136. package/dist/model/text/Texts.mjs +187 -0
  137. package/dist/model/text/WmlToMarkdown.d.mts +120 -0
  138. package/dist/model/text/WmlToMarkdown.mjs +936 -0
  139. package/dist/model/text/caseMapping.generated.d.mts +7 -0
  140. package/dist/model/text/caseMapping.generated.mjs +214 -0
  141. package/dist/model/text/commonmark.d.mts +121 -0
  142. package/dist/model/text/commonmark.mjs +655 -0
  143. package/dist/model/text/index.d.mts +8 -0
  144. package/dist/model/text/index.mjs +9 -0
  145. package/dist/model/text/normalize.d.mts +8 -0
  146. package/dist/model/text/normalize.mjs +13 -0
  147. package/dist/model/text/outline.d.mts +4 -0
  148. package/dist/model/text/outline.mjs +71 -0
  149. package/dist/model/wordIntegers.d.mts +29 -0
  150. package/dist/model/wordIntegers.mjs +78 -0
  151. package/dist/office-js/document.mjs +15 -1
  152. package/dist/office-js/fragment.d.mts +7 -5
  153. package/dist/office-js/fragment.mjs +63 -14
  154. package/dist/office-js/proxy.d.mts +8 -0
  155. package/dist/office-js/proxy.mjs +29 -0
  156. package/dist/office-js/run.mjs +2 -0
  157. package/dist/office-js/supported.generated.mjs +16 -0
  158. package/dist/office-js/toApiScript.mjs +29 -12
  159. package/dist/opc/DirectoryPartStore.d.mts +6 -2
  160. package/dist/opc/DirectoryPartStore.mjs +11 -4
  161. package/dist/opc/FlatOpcPartStore.d.mts +4 -2
  162. package/dist/opc/FlatOpcPartStore.mjs +11 -8
  163. package/dist/opc/Load.d.mts +32 -0
  164. package/dist/opc/Load.mjs +115 -5
  165. package/dist/opc/PartStore.d.mts +34 -1
  166. package/dist/opc/PartStore.mjs +71 -8
  167. package/dist/opc/Save.d.mts +32 -0
  168. package/dist/opc/Save.mjs +110 -4
  169. package/dist/opc/ZipPartStore.d.mts +4 -2
  170. package/dist/opc/ZipPartStore.mjs +12 -7
  171. package/dist/opc/exceptions.d.mts +140 -0
  172. package/dist/opc/exceptions.mjs +108 -0
  173. package/dist/opc/index.d.mts +8 -5
  174. package/dist/opc/index.mjs +5 -3
  175. package/dist/opc/mce/preprocessor.d.mts +141 -5
  176. package/dist/opc/mce/preprocessor.mjs +119 -18
  177. package/dist/opc/mce/understood.d.mts +3 -11
  178. package/dist/opc/mce/understood.mjs +27 -23
  179. package/dist/opc/repair/breaks.d.mts +23 -0
  180. package/dist/opc/repair/breaks.mjs +288 -0
  181. package/dist/opc/repair/hosts.d.mts +12 -0
  182. package/dist/opc/repair/hosts.mjs +33 -0
  183. package/dist/opc/repair/mainRoot.d.mts +19 -0
  184. package/dist/opc/repair/mainRoot.mjs +168 -0
  185. package/dist/opc/repair/unknownElements.d.mts +8 -0
  186. package/dist/opc/repair/unknownElements.mjs +180 -0
  187. package/dist/opc/repair/wmlNames.generated.d.mts +2 -0
  188. package/dist/opc/repair/wmlNames.generated.mjs +80 -0
  189. package/dist/opc/skipped.d.mts +50 -0
  190. package/dist/opc/skipped.mjs +140 -0
  191. package/dist/packages/OpcPackage.d.mts +110 -4
  192. package/dist/packages/OpcPackage.mjs +123 -7
  193. package/dist/packages/WordprocessingMLPackage.d.mts +45 -8
  194. package/dist/packages/WordprocessingMLPackage.mjs +158 -21
  195. package/dist/packages/index.d.mts +1 -1
  196. package/dist/parts/DefaultXmlPart.mjs +2 -2
  197. package/dist/parts/Part.d.mts +14 -2
  198. package/dist/parts/RelationshipsPart.d.mts +15 -0
  199. package/dist/parts/RelationshipsPart.mjs +66 -1
  200. package/dist/parts/XmlPart.d.mts +63 -32
  201. package/dist/parts/XmlPart.mjs +240 -96
  202. package/dist/parts/wml/comments.mjs +30 -0
  203. package/dist/parts/wml/index.d.mts +12 -1
  204. package/dist/parts/wml/index.mjs +37 -1
  205. package/dist/xml/dom.d.mts +7 -0
  206. package/dist/xml/dom.mjs +18 -0
  207. package/package.json +2 -2
package/NOTICE CHANGED
@@ -10,3 +10,7 @@ resolution utilities follow docx4j's design and names. It depends on @docx4j/gen
10
10
  (https://github.com/plutext/docx4j-generated-objects-ts) for the Office Open XML object model and on
11
11
  @docx4j/jsonix (https://github.com/plutext/jsonix) for XML binding; see their NOTICE files for the
12
12
  material they derive from (ECMA-376, Microsoft Open Specifications, Jsonix).
13
+
14
+ The Markdown writer (src/model/text/commonmark.mts) is a port of the Markdown renderer of
15
+ commonmark-java (https://github.com/commonmark/commonmark-java), Copyright (c) 2015, Atlassian Pty
16
+ Ltd, under the BSD 2-Clause License; its notice and conditions are kept in that file.
package/README.md CHANGED
@@ -45,9 +45,11 @@ const ooxml = await pkg.saveFlatOpc(); // pkg:package, for insertOo
45
45
  A part that is never touched is written back byte for byte; `getContents()` (or a view over
46
46
  it) marks a part for re-marshalling. `mc:AlternateContent` is kept with every branch, as docx4j
47
47
  keeps it: the content API reads the branch Word would (the first `mc:Choice` whose namespaces are
48
- understood, else the `mc:Fallback`), an edit lands in every branch, and a save writes them all
49
- (`{ mcePreprocess: true }` resolves the element when a part is unmarshalled instead, as Word does on
50
- open). Subpaths
48
+ understood, else the `mc:Fallback`) at every level, and a save writes them all (`{ mcePreprocess: true }`
49
+ resolves the element when a part is unmarshalled instead, as Word does on open). Among a paragraph's runs
50
+ the branch read is edited in place and the others kept (CR-012 section 12). At block, row and cell level a
51
+ view in a branch is read-only (`NotSupportedError`), while resolving tracked changes, `replaceText`, a
52
+ tracked deletion, the column edits and the binding passes reach every branch (CR-013). Subpaths
51
53
  `@docx4j/core-ts/opc`, `/parts`, `/packages` and `/model` give the layers separately,
52
54
  `/office-js` the `Word` shim (below), `/html` HTML to WordprocessingML (a converter to an
53
55
  intermediate form and a builder from it, CR-005 section 8; the HTML parser is yours, a browser's
@@ -55,6 +57,66 @@ intermediate form and a builder from it, CR-005 section 8; the HTML parser is yo
55
57
  container; nothing else in the package imports a `node:` builtin, so a browser or add-in bundle
56
58
  never sees one).
57
59
 
60
+ ### Repairs
61
+
62
+ Some packages Word opens only after its repair prompt, and some it won't open at all. `LoadOptions.repair`
63
+ (WD REQ-207, [CR-031](docs/change-requests/CR-031-the-repair-option.md)) says how a load treats them:
64
+
65
+ ```ts
66
+ const pkg = await WordprocessingMLPackage.load(bytes, { repair: 'word' });
67
+ pkg.repaired; // 'none', 'word' or 'own'; a text result's metadata.repaired says the same
68
+ ```
69
+
70
+ | The package | `refuse`, the default | `word` | `all` |
71
+ |---|---|---|---|
72
+ | Two zip entries of one name (OPC part names, without case) | `DuplicateEntryException` | The first of each read, as Word does: `package.duplicate_entry` | As `word` |
73
+ | More than one `officeDocument` relationship | `MultipleMainPartsException` | The last one's target read, as a fresh Word does: `package.multiple_main_parts` | As `word` |
74
+ | A relationships part in another namespace, or in none | `RelationshipsNamespaceException` | Every relationship read by its local name, as Word does: `package.relationships_namespace` | As `word` |
75
+ | An `External` `officeDocument` relationship beside an internal one | `ExternalMainPartException` | Refused: Word refuses it even through its repair | This package's own repair: the internal part read, every `External` main relationship dropped, its URL never fetched: `package.external_main_part` |
76
+ | An `External` `officeDocument` relationship alone | Refused: no main document part (REQ-321) | Refused | Refused |
77
+
78
+ Each warning is on `pkg.warnings` and, in the text model, a warning of the same code. A save then writes the
79
+ repaired package: one entry of each name, one main relationship, the right namespace, no `External` main
80
+ relationship. `repair` doesn't open the save's own gate ([CR-025](docs/change-requests/CR-025-dropped-content-reported.md)):
81
+ a changed part is written without what Word's own save drops too, such as an ignorable element Word doesn't understand,
82
+ each listed on `pkg.lastSave.dropped`; one that would lose anything else Word keeps, or won't open, needs
83
+ `pkg.checkLossOnSave = false`, each item then on `pkg.lastSave.lost`. Each `part.skipped` warning says which (`onSave`). The other shapes
84
+ Word repairs, such as an undeclared `mc:Ignorable` prefix or an element the model has no place for, read the same at
85
+ every value (CR-031 section 2.1.1).
86
+
87
+ ### Saving
88
+
89
+ A save copies every part nothing changed byte for byte, and re-marshals only what was read. Before it writes
90
+ anything, it checks what it would write. It refuses a file Word would refuse or repair, and a save that would lose
91
+ content Word keeps. Each check has a switch on the package, and each records what it let through on `pkg.lastSave`:
92
+
93
+ | A save that would | Throws | To write it anyway | Recorded on `pkg.lastSave` |
94
+ |---|---|---|---|
95
+ | Write a child its container never admits: a run straight in the body, a table in a paragraph, a string in a run ([CR-016](docs/change-requests/CR-016-misplaced-content-refused.md)) | `MisplacedContentException` (`target.misplaced`, each child with its part, path and the fix) | `pkg.checkContentOnSave = false` | `misplaced` |
96
+ | Lose what the read skipped, where Word's save keeps it or Word won't open the file ([CR-025](docs/change-requests/CR-025-dropped-content-reported.md)) | `SaveLossException` (`save.content_lost`, each part) | `pkg.checkLossOnSave = false` | `lost` |
97
+ | Lose what the read skipped, where Word's own save drops it too | Nothing: written without it | | `dropped` |
98
+
99
+ ```ts
100
+ import { SaveLossException } from '@docx4j/core-ts';
101
+
102
+ try {
103
+ await pkg.save();
104
+ } catch (e) {
105
+ if (!(e instanceof SaveLossException)) throw e;
106
+ pkg.checkLossOnSave = false; // accept the loss: e.losses names each part
107
+ await pkg.save();
108
+ pkg.lastSave.lost; // what went, each a part.skipped warning
109
+ }
110
+ ```
111
+
112
+ Only a part that was read is checked, so a package loaded and saved untouched is never refused. What a load found
113
+ is on `pkg.warnings` before any save: content the read skipped (`part.skipped`, its `onSave` saying which row it
114
+ falls in), misplaced children, and integer values past what Word stores (`value.out_of_range`, which a save writes
115
+ as held and lists on `lastSave.outOfRange`). `part.checkContent()` and `pkg.checkContent()` run the first check on
116
+ request, changing nothing. Two more switches change what a save writes: `pkg.normaliseOnSave` (run properties in
117
+ Word's normal form, on by default) and `pkg.writeTimesOnSave` (the core properties' times, off by default).
118
+ `pkg.dateTimeZone` is the time zone every revision's and comment's date is written and read in.
119
+
58
120
  ### Guides
59
121
 
60
122
  | | |
@@ -238,7 +300,33 @@ body.acceptAll(); // or rejectAll(), or accept()/reject() one at a time
238
300
  `getTrackedChanges()` is also on `Paragraph` and `Range`. Text and searches read the accepted
239
301
  view throughout, so an agent sees the document as it will read once the changes are taken. A moved
240
302
  paragraph is listed as its two halves, and accepting or rejecting either resolves the whole move,
241
- as Word's Review tab does.
303
+ as Word's Review tab does. `accept()` and `reject()` return whether they acted: a change already
304
+ resolved since it was listed, by its own view or another, is left alone and gives `false` (Office
305
+ JS's return nothing).
306
+
307
+ A body is one story. Every story at once - the main document, each footnote, endnote and comment,
308
+ the headers and footers in the order the sections reference them, and the text boxes in each - is
309
+ the package's, as docx4j lists it (`TrackedChanges.in`), with docx4j's kind and the part of each
310
+ change. The listing and the resolve-all verbs take a filter: docx4j's conditions as an object, which
311
+ travels as JSON, or a function over each single change. A group of touching changes is listed whole
312
+ when any of its pieces matches, and resolved only in the pieces that match:
313
+
314
+ ```ts
315
+ for (const change of await pkg.getAllTrackedChanges()) {
316
+ change.kind; // docx4j's: 'RUN_INSERTION', 'PARAGRAPH_MARK_DELETION', 'TABLE_PROPERTIES', ...
317
+ change.part.partName.name; // '/word/document.xml', '/word/footnotes.xml', '/word/header1.xml', ...
318
+ }
319
+ await pkg.rejectAllRevisions({ authors: ['Author B'] });
320
+ await pkg.acceptAllRevisions({ types: ['Deleted'], from: '2026-10-01', parts: ['/word/document.xml'] });
321
+ await pkg.acceptAllRevisions((change) => change.author === 'Claude');
322
+ body.acceptAll({ kinds: ['RUN_DELETION'] }); // one story; a deleted paragraph's text goes, its mark stays
323
+ ```
324
+
325
+ The conditions are `authors`, `types` (Office JS's names or docx4j's `ADDED`, `DELETED`,
326
+ `FORMATTED`), `kinds`, `from` (inclusive) and `to` (exclusive), and `parts` (a part or its name).
327
+ A key or a value the filter does not know throws a `TypeError`, before anything is resolved. With a
328
+ filter, what no change owns (a `w:cellMerge`, a `w:numberingChange`) is left alone; without one it is
329
+ swept, as before. Each verb returns how many listed changes had a matching piece.
242
330
 
243
331
  To record one caller's edits without touching the document's own setting - an agent working in a
244
332
  document whose `w:trackRevisions` belongs to its owner - use `withTracking`. Nothing is written
@@ -415,7 +415,8 @@ function cellFrom(el, w) {
415
415
  var _a, _b;
416
416
  const blocks = [];
417
417
  walkBlocks(el, blocks, w);
418
- if (blocks.length === 0) {
418
+ // a cell ends with a paragraph, as Word reads it, after a table too (CR-002 section 55)
419
+ if (blocks.length === 0 || blocks[blocks.length - 1].kind === 'table') {
419
420
  blocks.push({ kind: 'paragraph', style: null, inlines: [] });
420
421
  w.report.paragraphs++;
421
422
  }
@@ -142,7 +142,8 @@ function tableXml(t, resolve) {
142
142
  w += (_a = widths[col + k]) !== null && _a !== void 0 ? _a : widths[0]; return w; };
143
143
  const tcPr = (col, span, merge) => `<w:tcPr><w:tcW w:w="${widthOf(col, span)}" w:type="dxa"/>${span > 1 ? `<w:gridSpan w:val="${span}"/>` : ''}${merge === 'restart' ? '<w:vMerge w:val="restart"/>' : merge === 'continue' ? '<w:vMerge/>' : ''}</w:tcPr>`;
144
144
  const cellXml = (c, col) => {
145
- const blocks = c.blocks.length > 0 ? blocksToXml(c.blocks, resolve) : '<w:p/>';
145
+ const last = c.blocks[c.blocks.length - 1];
146
+ const blocks = last === undefined ? '<w:p/>' : blocksToXml(c.blocks, resolve) + (last.kind === 'table' ? '<w:p/>' : '');
146
147
  return `<w:tc>${tcPr(col, c.colspan, c.rowspan > 1 ? 'restart' : undefined)}${blocks}</w:tc>`;
147
148
  };
148
149
  // A cell spanning rows holds its place in the rows below with a continuation cell, as WordprocessingML writes a vertical merge.
@@ -0,0 +1,29 @@
1
+ /** sRGB (0 to 1) to linear RGB (IEC 61966-2-1). */
2
+ export declare function toLinear(c: number): number;
3
+ /** linear RGB to sRGB, clamped to 0 to 1. */
4
+ export declare function toSrgb(l: number): number;
5
+ export declare function clamp01(v: number): number;
6
+ /** a channel, 0 to 1, as 0 to 255, rounded half up */
7
+ export declare function channel(v: number): number;
8
+ /** a channel, 0 to 1, as 0 to 255, rounded half down (Office's luminance transforms) */
9
+ export declare function channelHalfDown(v: number): number;
10
+ /** [h, s, l], each 0 to 1, from sRGB channels 0 to 1; a grey's hue is 0 */
11
+ export declare function rgbToHsl(r: number, g: number, b: number): [number, number, number];
12
+ /**
13
+ * sRGB channels (0 to 1, clamped) from HSL; the saturation is not clamped, as Office does not clamp it (a
14
+ * saturation above 1 pushes a channel past its range, which is then clamped).
15
+ */
16
+ export declare function hslToRgb(h: number, s: number, l: number): [number, number, number];
17
+ /**
18
+ * sRGB channels (0 to 1, not clamped) from HSL as Foley and van Dam give it: the same colour as {@link hslToRgb}
19
+ * but not the same rounding error, and the one Office's luminance transforms round as.
20
+ */
21
+ export declare function hslToRgbFoley(h: number, s: number, l: number): [number, number, number];
22
+ export declare const HLSMAX = 240;
23
+ /** [h, l, s] on 0 to 240, as Windows' ColorRGBToHLS (KB29240) computes them */
24
+ export declare function rgbToHls240(r: number, g: number, b: number): [number, number, number];
25
+ /**
26
+ * RGB from {@link rgbToHls240}'s values, as Windows' ColorHLSToRGB, except that a grey is rounded rather than
27
+ * truncated, which is what Excel's fills show (docx4j CR-034 section 11, P4).
28
+ */
29
+ export declare function hls240ToRgb(h: number, l: number, s: number): [number, number, number];
@@ -0,0 +1,146 @@
1
+ // The colour arithmetic Office uses, as docx4j CR-034's probes measured it (section 11): sRGB's transfer function,
2
+ // HSL with an unclamped saturation, and Windows' integer HLS for SpreadsheetML tints. docx4j
3
+ // `org.docx4j.model.color.ColorMath`, expression for expression: JS numbers are IEEE doubles as Java's are, so the
4
+ // same expressions in the same order give the same bits, which the luminance transforms' rounding depends on
5
+ // (CR-008 section 3). Java's integer division is `Math.trunc` of the quotient here; nothing is simplified.
6
+ // ------------------------------------------------------------------ sRGB and linear RGB
7
+ /** sRGB (0 to 1) to linear RGB (IEC 61966-2-1). */
8
+ export function toLinear(c) {
9
+ return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
10
+ }
11
+ /** linear RGB to sRGB, clamped to 0 to 1. */
12
+ export function toSrgb(l) {
13
+ const c = clamp01(l);
14
+ return c <= 0.0031308 ? c * 12.92 : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
15
+ }
16
+ export function clamp01(v) {
17
+ return v < 0 ? 0 : v > 1 ? 1 : v;
18
+ }
19
+ /** a channel, 0 to 1, as 0 to 255, rounded half up */
20
+ export function channel(v) {
21
+ return Math.floor(clamp01(v) * 255 + 0.5);
22
+ }
23
+ /** a channel, 0 to 1, as 0 to 255, rounded half down (Office's luminance transforms) */
24
+ export function channelHalfDown(v) {
25
+ // Math.ceil(-0.5) is -0 in JS, 0 in Java's (int) cast; ResolvedColor's clamp makes either 0
26
+ return Math.ceil(clamp01(v) * 255 - 0.5);
27
+ }
28
+ // ------------------------------------------------------------------ HSL
29
+ /** [h, s, l], each 0 to 1, from sRGB channels 0 to 1; a grey's hue is 0 */
30
+ export function rgbToHsl(r, g, b) {
31
+ const max = Math.max(r, Math.max(g, b)), min = Math.min(r, Math.min(g, b));
32
+ const l = (max + min) / 2;
33
+ if (max === min)
34
+ return [0, 0, l];
35
+ const d = max - min;
36
+ const s = l <= 0.5 ? d / (max + min) : d / (2 - max - min);
37
+ let h;
38
+ if (max === r) {
39
+ h = ((g - b) / d) % 6;
40
+ if (h < 0)
41
+ h += 6;
42
+ }
43
+ else if (max === g) {
44
+ h = (b - r) / d + 2;
45
+ }
46
+ else {
47
+ h = (r - g) / d + 4;
48
+ }
49
+ return [h / 6, s, l];
50
+ }
51
+ /**
52
+ * sRGB channels (0 to 1, clamped) from HSL; the saturation is not clamped, as Office does not clamp it (a
53
+ * saturation above 1 pushes a channel past its range, which is then clamped).
54
+ */
55
+ export function hslToRgb(h, s, l) {
56
+ return [hslChannel(0, h, s, l), hslChannel(8, h, s, l), hslChannel(4, h, s, l)];
57
+ }
58
+ /**
59
+ * sRGB channels (0 to 1, not clamped) from HSL as Foley and van Dam give it: the same colour as {@link hslToRgb}
60
+ * but not the same rounding error, and the one Office's luminance transforms round as.
61
+ */
62
+ export function hslToRgbFoley(h, s, l) {
63
+ // keep this form and its order of operations, and don't fold it into hslToRgb: a channel that is exactly half a
64
+ // unit rounds (channelHalfDown) the way this form's floating-point error leans, which is the way Office rounds it
65
+ // (docx4j CR-034 section 11, follow-up: 29 of 30 halves; hslToRgb's error leans otherwise)
66
+ const m2 = l <= 0.5 ? l * (1 + s) : l + s - l * s, m1 = 2 * l - m2;
67
+ return [foley(m1, m2, h + 1 / 3.0), foley(m1, m2, h), foley(m1, m2, h - 1 / 3.0)];
68
+ }
69
+ function foley(m1, m2, h) {
70
+ h -= Math.floor(h);
71
+ if (h < 1 / 6.0)
72
+ return m1 + (m2 - m1) * 6 * h;
73
+ if (h < 1 / 2.0)
74
+ return m2;
75
+ if (h < 2 / 3.0)
76
+ return m1 + (m2 - m1) * (2 / 3.0 - h) * 6;
77
+ return m1;
78
+ }
79
+ function hslChannel(n, h, s, l) {
80
+ let k = (n + h * 12) % 12;
81
+ if (k < 0)
82
+ k += 12;
83
+ const a = s * Math.min(l, 1 - l);
84
+ return clamp01(l - a * Math.max(-1, Math.min(Math.min(k - 3, 9 - k), 1)));
85
+ }
86
+ // ------------------------------------------------------------------ Windows' integer HLS (Excel)
87
+ export const HLSMAX = 240;
88
+ const RGBMAX = 255;
89
+ /** Java's int division. */
90
+ const div = (a, b) => Math.trunc(a / b);
91
+ /** [h, l, s] on 0 to 240, as Windows' ColorRGBToHLS (KB29240) computes them */
92
+ export function rgbToHls240(r, g, b) {
93
+ const max = Math.max(r, Math.max(g, b)), min = Math.min(r, Math.min(g, b));
94
+ const l = div((max + min) * HLSMAX + RGBMAX, 2 * RGBMAX);
95
+ if (max === min)
96
+ return [div(HLSMAX * 2, 3), l, 0];
97
+ const s = l <= div(HLSMAX, 2)
98
+ ? div((max - min) * HLSMAX + div(max + min, 2), max + min)
99
+ : div((max - min) * HLSMAX + div(2 * RGBMAX - max - min, 2), 2 * RGBMAX - max - min);
100
+ const d = max - min;
101
+ const rd = div((max - r) * div(HLSMAX, 6) + div(d, 2), d);
102
+ const gd = div((max - g) * div(HLSMAX, 6) + div(d, 2), d);
103
+ const bd = div((max - b) * div(HLSMAX, 6) + div(d, 2), d);
104
+ let h;
105
+ if (r === max)
106
+ h = bd - gd;
107
+ else if (g === max)
108
+ h = div(HLSMAX, 3) + rd - bd;
109
+ else
110
+ h = div(2 * HLSMAX, 3) + gd - rd;
111
+ if (h < 0)
112
+ h += HLSMAX;
113
+ if (h > HLSMAX)
114
+ h -= HLSMAX;
115
+ return [h, l, s];
116
+ }
117
+ /**
118
+ * RGB from {@link rgbToHls240}'s values, as Windows' ColorHLSToRGB, except that a grey is rounded rather than
119
+ * truncated, which is what Excel's fills show (docx4j CR-034 section 11, P4).
120
+ */
121
+ export function hls240ToRgb(h, l, s) {
122
+ if (s === 0) {
123
+ const v = div(l * RGBMAX + div(HLSMAX, 2), HLSMAX);
124
+ return [v, v, v];
125
+ }
126
+ const m2 = l <= div(HLSMAX, 2) ? div(l * (HLSMAX + s) + div(HLSMAX, 2), HLSMAX) : l + s - div(l * s + div(HLSMAX, 2), HLSMAX);
127
+ const m1 = 2 * l - m2;
128
+ return [
129
+ div(hueToRgb(m1, m2, h + div(HLSMAX, 3)) * RGBMAX + div(HLSMAX, 2), HLSMAX),
130
+ div(hueToRgb(m1, m2, h) * RGBMAX + div(HLSMAX, 2), HLSMAX),
131
+ div(hueToRgb(m1, m2, h - div(HLSMAX, 3)) * RGBMAX + div(HLSMAX, 2), HLSMAX)
132
+ ];
133
+ }
134
+ function hueToRgb(n1, n2, hue) {
135
+ if (hue < 0)
136
+ hue += HLSMAX;
137
+ if (hue > HLSMAX)
138
+ hue -= HLSMAX;
139
+ if (hue < div(HLSMAX, 6))
140
+ return n1 + div((n2 - n1) * hue + div(HLSMAX, 12), div(HLSMAX, 6));
141
+ if (hue < div(HLSMAX, 2))
142
+ return n2;
143
+ if (hue < div(HLSMAX * 2, 3))
144
+ return n1 + div((n2 - n1) * (div(HLSMAX * 2, 3) - hue) + div(HLSMAX, 12), div(HLSMAX, 6));
145
+ return n1;
146
+ }
@@ -0,0 +1,6 @@
1
+ /** `a:prstClr` names to RRGGBB. */
2
+ export declare const PRESET: ReadonlyMap<string, string>;
3
+ /** `a:sysClr` names to Windows' default RRGGBB. */
4
+ export declare const SYSTEM: ReadonlyMap<string, string>;
5
+ /** SpreadsheetML indexed colours 0 to 65. */
6
+ export declare const INDEXED: readonly string[];
@@ -0,0 +1,83 @@
1
+ // The fixed colour tables: DrawingML's preset colours (`a:prstClr`), Windows' default system colours (`a:sysClr`,
2
+ // which Office draws as the machine's colour, not `lastClr`: docx4j CR-034 section 11), and SpreadsheetML's legacy
3
+ // indexed palette. docx4j `org.docx4j.model.color.ColorTables`, copied entry for entry.
4
+ //
5
+ // The preset and system values are Apache POI's `org.apache.poi.sl.usermodel.PresetColor` (trunk ae62bb5116,
6
+ // Apache-2.0), keyed by the names docx4j's schema gives `ST_PresetColorVal` and `ST_SystemColorVal`. The indexed
7
+ // palette is Excel 365's, read from its PDF of CR-034's probe tc-p4-spreadsheet.xlsx (2026-10-12): entries 0 to 63,
8
+ // then 64 and 65, the system foreground and background.
9
+ /** `a:prstClr` names to RRGGBB. */
10
+ export const PRESET = new Map(Object.entries({
11
+ aliceBlue: 'F0F8FF', antiqueWhite: 'FAEBD7', aqua: '00FFFF',
12
+ aquamarine: '7FFFD4', azure: 'F0FFFF', beige: 'F5F5DC',
13
+ bisque: 'FFE4C4', black: '000000', blanchedAlmond: 'FFEBCD',
14
+ blue: '0000FF', blueViolet: '8A2BE2', brown: 'A52A2A',
15
+ burlyWood: 'DEB887', cadetBlue: '5F9EA0', chartreuse: '7FFF00',
16
+ chocolate: 'D2691E', coral: 'FF7F50', cornflowerBlue: '6495ED',
17
+ cornsilk: 'FFF8DC', crimson: 'DC143C', cyan: '00FFFF',
18
+ dkBlue: '00008B', dkCyan: '008B8B', dkGoldenrod: 'B8860B',
19
+ dkGray: 'A9A9A9', dkGreen: '006400', dkKhaki: 'BDB76B',
20
+ dkMagenta: '8B008B', dkOliveGreen: '556B2F', dkOrange: 'FF8C00',
21
+ dkOrchid: '9932CC', dkRed: '8B0000', dkSalmon: 'E9967A',
22
+ dkSeaGreen: '8FBC8B', dkSlateBlue: '483D8B', dkSlateGray: '2F4F4F',
23
+ dkTurquoise: '00CED1', dkViolet: '9400D3', deepPink: 'FF1493',
24
+ deepSkyBlue: '00BFFF', dimGray: '696969', dodgerBlue: '1E90FF',
25
+ firebrick: 'B22222', floralWhite: 'FFFAF0', forestGreen: '228B22',
26
+ fuchsia: 'FF00FF', gainsboro: 'DCDCDC', ghostWhite: 'F8F8FF',
27
+ gold: 'FFD700', goldenrod: 'DAA520', gray: '808080',
28
+ green: '008000', greenYellow: 'ADFF2F', honeydew: 'F0FFF0',
29
+ hotPink: 'FF69B4', indianRed: 'CD5C5C', indigo: '4B0082',
30
+ ivory: 'FFFFF0', khaki: 'F0E68C', lavender: 'E6E6FA',
31
+ lavenderBlush: 'FFF0F5', lawnGreen: '7CFC00', lemonChiffon: 'FFFACD',
32
+ ltBlue: 'ADD8E6', ltCoral: 'F08080', ltCyan: 'E0FFFF',
33
+ ltGoldenrodYellow: 'FAFA78', ltGray: 'D3D3D3', ltGreen: '90EE90',
34
+ ltPink: 'FFB6C1', ltSalmon: 'FFA07A', ltSeaGreen: '20B2AA',
35
+ ltSkyBlue: '87CEFA', ltSlateGray: '778899', ltSteelBlue: 'B0C4DE',
36
+ ltYellow: 'FFFFE0', lime: '00FF00', limeGreen: '32CD32',
37
+ linen: 'FAF0E6', magenta: 'FF00FF', maroon: '800000',
38
+ medAquamarine: '66CDAA', medBlue: '0000CD', medOrchid: 'BA55D3',
39
+ medPurple: '9370DB', medSeaGreen: '3CB371', medSlateBlue: '7B68EE',
40
+ medSpringGreen: '00FA9A', medTurquoise: '48D1CC', medVioletRed: 'C71585',
41
+ midnightBlue: '191970', mintCream: 'F5FFFA', mistyRose: 'FFE4E1',
42
+ moccasin: 'FFE4B5', navajoWhite: 'FFDEAD', navy: '000080',
43
+ oldLace: 'FDF5E6', olive: '808000', oliveDrab: '6B8E23',
44
+ orange: 'FFA500', orangeRed: 'FF4500', orchid: 'DA70D6',
45
+ paleGoldenrod: 'EEE8AA', paleGreen: '98FB98', paleTurquoise: 'AFEEEE',
46
+ paleVioletRed: 'DB7093', papayaWhip: 'FFEFD5', peachPuff: 'FFDAB9',
47
+ peru: 'CD853F', pink: 'FFC0CB', plum: 'DDA0DD',
48
+ powderBlue: 'B0E0E6', purple: '800080', red: 'FF0000',
49
+ rosyBrown: 'BC8F8F', royalBlue: '4169E1', saddleBrown: '8B4513',
50
+ salmon: 'FA8072', sandyBrown: 'F4A460', seaGreen: '2E8B57',
51
+ seaShell: 'FFF5EE', sienna: 'A0522D', silver: 'C0C0C0',
52
+ skyBlue: '87CEEB', slateBlue: '6A5ACD', slateGray: '708090',
53
+ snow: 'FFFAFA', springGreen: '00FF7F', steelBlue: '4682B4',
54
+ tan: 'D2B48C', teal: '008080', thistle: 'D8BFD8',
55
+ tomato: 'FF6347', turquoise: '40E0D0', violet: 'EE82EE',
56
+ wheat: 'F5DEB3', white: 'FFFFFF', whiteSmoke: 'F5F5F5',
57
+ yellow: 'FFFF00', yellowGreen: '9ACD32',
58
+ }));
59
+ /** `a:sysClr` names to Windows' default RRGGBB. */
60
+ export const SYSTEM = new Map(Object.entries({
61
+ scrollBar: 'C8C8C8', background: '000000', activeCaption: '99B4D1',
62
+ inactiveCaption: 'BFCDDB', menu: 'F0F0F0', window: 'FFFFFF',
63
+ windowFrame: '646464', menuText: '000000', windowText: '000000',
64
+ captionText: '000000', activeBorder: 'B4B4B4', inactiveBorder: 'F4F7FC',
65
+ appWorkspace: 'ABABAB', highlight: '3399FF', highlightText: 'FFFFFF',
66
+ btnFace: 'F0F0F0', btnShadow: '696969', grayText: '6D6D6D',
67
+ btnText: '000000', inactiveCaptionText: '000000', btnHighlight: 'E3E3E3',
68
+ '3dDkShadow': '000000', '3dLight': 'E3E3E3', infoText: '000000',
69
+ infoBk: 'FFFFE1', hotLight: '0066CC', gradientActiveCaption: 'B9D1EA',
70
+ gradientInactiveCaption: 'D7E4F2', menuHighlight: '3399FF', menuBar: 'F0F0F0',
71
+ }));
72
+ /** SpreadsheetML indexed colours 0 to 65. */
73
+ export const INDEXED = Object.freeze([
74
+ '000000', 'FFFFFF', 'FF0000', '00FF00', '0000FF', 'FFFF00', 'FF00FF', '00FFFF',
75
+ '000000', 'FFFFFF', 'FF0000', '00FF00', '0000FF', 'FFFF00', 'FF00FF', '00FFFF',
76
+ '800000', '008000', '000080', '808000', '800080', '008080', 'C0C0C0', '808080',
77
+ '9999FF', '993366', 'FFFFCC', 'CCFFFF', '660066', 'FF8080', '0066CC', 'CCCCFF',
78
+ '000080', 'FF00FF', 'FFFF00', '00FFFF', '800080', '800000', '008080', '0000FF',
79
+ '00CCFF', 'CCFFFF', 'CCFFCC', 'FFFF99', '99CCFF', 'FF99CC', 'CC99FF', 'FFCC99',
80
+ '3366FF', '33CCCC', '99CC00', 'FFCC00', 'FF9900', 'FF6600', '666699', '969696',
81
+ '003366', '339966', '003300', '333300', '993300', '993366', '333399', '333333',
82
+ '000000', 'FFFFFF',
83
+ ]);
@@ -0,0 +1,52 @@
1
+ import type * as dml from '@docx4j/generated-objects-ts/modules/org_docx4j_dml';
2
+ import { ResolvedColor } from './ResolvedColor.mjs';
3
+ import { StyleFill } from './StyleFill.mjs';
4
+ import type { ThemeColors } from './ThemeColors.mjs';
5
+ /** hexBinary as the objects model has it at run time (byte numbers), or as its declarations say (hex digits). */
6
+ export declare function bytesOf(v: unknown): number[] | undefined;
7
+ /**
8
+ * DrawingML colours (ISO/IEC 29500-1 section 20.1.2.3) resolved as Office draws them. The six kinds: `srgbClr`,
9
+ * `scrgbClr` (linear percentages), `hslClr`, `sysClr` (the machine's colour by name, not `lastClr`), `prstClr` and
10
+ * `schemeClr` (through {@link ThemeColors}). Their transforms apply in document order, in the spaces Word and
11
+ * PowerPoint use, measured on 403 cases each (docx4j CR-034 section 11, P3):
12
+ * - `tint`, `shade`, `inv`, `red`/`green`/`blue` and their `Off`/`Mod`: linear RGB; `gamma` and `invGamma`: the
13
+ * sRGB transfer function each way;
14
+ * - `hue`, `sat`, `lum` and their `Off`/`Mod`, `comp`: HSL of the sRGB colour, the saturation not clamped (the
15
+ * channels are, afterwards); a colour whose last change is a luminance transform converted back as Foley and van
16
+ * Dam do and rounded half down, as Office rounds it (so `tx1` at `lumMod 50000 lumOff 50000` is 7F7F7F);
17
+ * - `gray`: Rec.709 luma of the sRGB channels;
18
+ * - `alpha`, `alphaOff`, `alphaMod`: the alpha.
19
+ *
20
+ * MS-OI29500 says `lumOff` and `satOff` are not supported by Office; Office 365 applies both as above in every case
21
+ * measured. Known differences from Office: a saturation set on a grey (Office gives a grey's hue about 40 degrees),
22
+ * and some hue, saturation and luminance cases one unit off.
23
+ *
24
+ * An object is known by its `TYPE_NAME` (which the objects package's unmarshal and factories give it), or, wrapped
25
+ * as `{ name, value }`, by its element name; a holder of a colour also by its `srgbClr`, `schemeClr`, ... member.
26
+ * docx4j's `null` is `undefined` here.
27
+ */
28
+ export declare const DrawingMLColors: {
29
+ /**
30
+ * The colour a DrawingML colour element, or an element holding one, resolves to.
31
+ *
32
+ * @param colour one of the six kinds (`CTSchemeColor`, `CTSRgbColor`, ...), or a holder of one: `CTColor` (a
33
+ * scheme slot), `CTSolidColorFillProperties`, `CTStyleMatrixReference`, `CTFontReference`; `{ name, value }`
34
+ * is unwrapped
35
+ * @param theme the theme and colour map for `schemeClr`; may be undefined where there is none
36
+ * @returns undefined where there is no colour, or a `schemeClr` cannot be resolved
37
+ */
38
+ resolve(colour: unknown, theme: ThemeColors | undefined): ResolvedColor | undefined;
39
+ /** A fill: `a:noFill`, `a:solidFill` (its colour resolved), or a gradient, pattern, picture or group fill reported by kind. */
40
+ fill(fillProperties: unknown, theme: ThemeColors | undefined): StyleFill;
41
+ /** A line's fill: none, solid, gradient or pattern. */
42
+ lineFill(ln: dml.CTLineProperties | undefined | null, theme: ThemeColors | undefined): StyleFill;
43
+ /**
44
+ * A shape style's `a:fillRef`: index 0 is no fill, 1 to 999 the theme's `a:fillStyleLst`, 1001 and up its
45
+ * `a:bgFillStyleLst`, with `phClr` the reference's own colour.
46
+ */
47
+ fillReference(ref: dml.CTStyleMatrixReference | undefined | null, theme: ThemeColors | undefined): StyleFill;
48
+ /** A shape style's `a:lnRef`: index 0 is no line, else the theme's `a:lnStyleLst` entry's fill. */
49
+ lineReference(ref: dml.CTStyleMatrixReference | undefined | null, theme: ThemeColors | undefined): StyleFill;
50
+ /** A shape style's `a:fontRef`: its colour, the text's where the text states none. */
51
+ fontReference(ref: dml.CTFontReference | undefined | null, theme: ThemeColors | undefined): ResolvedColor | undefined;
52
+ };