@docx4j/core-ts 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/NOTICE +4 -0
  2. package/README.md +32 -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 +509 -141
  25. package/dist/model/content/Comment.d.mts +21 -3
  26. package/dist/model/content/Comment.mjs +195 -19
  27. package/dist/model/content/ContentControl.d.mts +197 -18
  28. package/dist/model/content/ContentControl.mjs +664 -95
  29. package/dist/model/content/Font.d.mts +67 -17
  30. package/dist/model/content/Font.mjs +114 -35
  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.mjs +3 -0
  34. package/dist/model/content/Paragraph.d.mts +189 -13
  35. package/dist/model/content/Paragraph.mjs +870 -74
  36. package/dist/model/content/Range.d.mts +227 -12
  37. package/dist/model/content/Range.mjs +785 -59
  38. package/dist/model/content/ResolvedView.d.mts +104 -0
  39. package/dist/model/content/ResolvedView.mjs +596 -0
  40. package/dist/model/content/Table.d.mts +29 -3
  41. package/dist/model/content/Table.mjs +109 -33
  42. package/dist/model/content/TrackedChange.d.mts +172 -13
  43. package/dist/model/content/TrackedChange.mjs +728 -167
  44. package/dist/model/content/comments.d.mts +20 -6
  45. package/dist/model/content/comments.mjs +67 -57
  46. package/dist/model/content/generalException.d.mts +18 -0
  47. package/dist/model/content/generalException.mjs +24 -0
  48. package/dist/model/content/index.d.mts +8 -3
  49. package/dist/model/content/index.mjs +7 -2
  50. package/dist/model/content/mathText.d.mts +21 -0
  51. package/dist/model/content/mathText.mjs +267 -0
  52. package/dist/model/content/officeText.d.mts +27 -0
  53. package/dist/model/content/officeText.mjs +74 -0
  54. package/dist/model/content/outline.d.mts +5 -0
  55. package/dist/model/content/outline.mjs +13 -0
  56. package/dist/model/content/placeholders.d.mts +110 -0
  57. package/dist/model/content/placeholders.mjs +253 -0
  58. package/dist/model/content/search.d.mts +72 -0
  59. package/dist/model/content/search.mjs +204 -2
  60. package/dist/model/content/spanEdits.d.mts +159 -0
  61. package/dist/model/content/spanEdits.mjs +728 -0
  62. package/dist/model/content/story.d.mts +44 -0
  63. package/dist/model/content/story.mjs +92 -0
  64. package/dist/model/content/tableColumns.d.mts +1 -1
  65. package/dist/model/content/tableColumns.mjs +108 -80
  66. package/dist/model/content/tableRevisions.d.mts +11 -3
  67. package/dist/model/content/tableRevisions.mjs +72 -42
  68. package/dist/model/content/tableRules.d.mts +30 -0
  69. package/dist/model/content/tableRules.mjs +100 -0
  70. package/dist/model/content/textBoxes.d.mts +18 -0
  71. package/dist/model/content/textBoxes.mjs +105 -0
  72. package/dist/model/content/trackedChangeFilter.d.mts +39 -0
  73. package/dist/model/content/trackedChangeFilter.mjs +81 -0
  74. package/dist/model/content/tracking.d.mts +28 -0
  75. package/dist/model/content/tracking.mjs +149 -10
  76. package/dist/model/content/tree.d.mts +171 -22
  77. package/dist/model/content/tree.mjs +329 -53
  78. package/dist/model/customxml/CustomXmlPartCollection.d.mts +8 -0
  79. package/dist/model/customxml/CustomXmlPartCollection.mjs +34 -11
  80. package/dist/model/customxml/XmlMapping.d.mts +1 -1
  81. package/dist/model/customxml/XmlMapping.mjs +11 -0
  82. package/dist/model/customxml/bindings.d.mts +21 -2
  83. package/dist/model/customxml/bindings.mjs +76 -32
  84. package/dist/model/customxml/kinds.d.mts +8 -3
  85. package/dist/model/customxml/kinds.mjs +26 -5
  86. package/dist/model/customxml/picturePlaceholder.d.mts +17 -0
  87. package/dist/model/customxml/picturePlaceholder.mjs +32 -0
  88. package/dist/model/customxml/pictures.d.mts +4 -3
  89. package/dist/model/customxml/pictures.mjs +18 -6
  90. package/dist/model/customxml/xhtml.mjs +8 -2
  91. package/dist/model/index.d.mts +2 -0
  92. package/dist/model/index.mjs +4 -0
  93. package/dist/model/listnumbering/Emulator.d.mts +37 -5
  94. package/dist/model/listnumbering/Emulator.mjs +63 -9
  95. package/dist/model/properties/PropertyResolver.d.mts +60 -1
  96. package/dist/model/properties/PropertyResolver.mjs +115 -2
  97. package/dist/model/properties/catalogue.d.mts +19 -5
  98. package/dist/model/properties/catalogue.mjs +41 -15
  99. package/dist/model/properties/log.d.mts +1 -1
  100. package/dist/model/properties/log.mjs +5 -5
  101. package/dist/model/properties/table.mjs +4 -2
  102. package/dist/model/text/Extractor.d.mts +289 -0
  103. package/dist/model/text/Extractor.mjs +2162 -0
  104. package/dist/model/text/LinkTargets.d.mts +45 -0
  105. package/dist/model/text/LinkTargets.mjs +170 -0
  106. package/dist/model/text/MarkdownExporter.d.mts +140 -0
  107. package/dist/model/text/MarkdownExporter.mjs +255 -0
  108. package/dist/model/text/PlainText.d.mts +29 -0
  109. package/dist/model/text/PlainText.mjs +539 -0
  110. package/dist/model/text/TextContext.d.mts +161 -0
  111. package/dist/model/text/TextContext.mjs +425 -0
  112. package/dist/model/text/TextOptions.d.mts +150 -0
  113. package/dist/model/text/TextOptions.mjs +278 -0
  114. package/dist/model/text/TextResult.d.mts +504 -0
  115. package/dist/model/text/TextResult.mjs +526 -0
  116. package/dist/model/text/Texts.d.mts +99 -0
  117. package/dist/model/text/Texts.mjs +187 -0
  118. package/dist/model/text/WmlToMarkdown.d.mts +120 -0
  119. package/dist/model/text/WmlToMarkdown.mjs +911 -0
  120. package/dist/model/text/commonmark.d.mts +121 -0
  121. package/dist/model/text/commonmark.mjs +655 -0
  122. package/dist/model/text/index.d.mts +7 -0
  123. package/dist/model/text/index.mjs +8 -0
  124. package/dist/model/text/normalize.d.mts +8 -0
  125. package/dist/model/text/normalize.mjs +13 -0
  126. package/dist/model/text/outline.d.mts +4 -0
  127. package/dist/model/text/outline.mjs +71 -0
  128. package/dist/office-js/fragment.d.mts +7 -5
  129. package/dist/office-js/fragment.mjs +63 -14
  130. package/dist/office-js/proxy.d.mts +8 -0
  131. package/dist/office-js/proxy.mjs +29 -0
  132. package/dist/office-js/run.mjs +2 -0
  133. package/dist/office-js/supported.generated.mjs +16 -0
  134. package/dist/office-js/toApiScript.mjs +21 -5
  135. package/dist/opc/Save.d.mts +17 -0
  136. package/dist/opc/Save.mjs +12 -2
  137. package/dist/opc/index.d.mts +3 -3
  138. package/dist/opc/index.mjs +1 -1
  139. package/dist/opc/mce/preprocessor.d.mts +28 -3
  140. package/dist/opc/mce/preprocessor.mjs +4 -4
  141. package/dist/opc/mce/understood.d.mts +30 -6
  142. package/dist/opc/mce/understood.mjs +37 -7
  143. package/dist/packages/OpcPackage.d.mts +13 -0
  144. package/dist/packages/OpcPackage.mjs +1 -0
  145. package/dist/packages/WordprocessingMLPackage.d.mts +45 -8
  146. package/dist/packages/WordprocessingMLPackage.mjs +155 -20
  147. package/dist/parts/DefaultXmlPart.mjs +2 -2
  148. package/dist/parts/Part.d.mts +10 -2
  149. package/dist/parts/XmlPart.d.mts +12 -30
  150. package/dist/parts/XmlPart.mjs +44 -93
  151. package/dist/parts/wml/comments.mjs +30 -0
  152. package/dist/parts/wml/index.mjs +12 -0
  153. package/dist/xml/dom.d.mts +7 -0
  154. package/dist/xml/dom.mjs +18 -0
  155. 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
@@ -238,7 +240,33 @@ body.acceptAll(); // or rejectAll(), or accept()/reject() one at a time
238
240
  `getTrackedChanges()` is also on `Paragraph` and `Range`. Text and searches read the accepted
239
241
  view throughout, so an agent sees the document as it will read once the changes are taken. A moved
240
242
  paragraph is listed as its two halves, and accepting or rejecting either resolves the whole move,
241
- as Word's Review tab does.
243
+ as Word's Review tab does. `accept()` and `reject()` return whether they acted: a change already
244
+ resolved since it was listed, by its own view or another, is left alone and gives `false` (Office
245
+ JS's return nothing).
246
+
247
+ A body is one story. Every story at once - the main document, each footnote, endnote and comment,
248
+ the headers and footers in the order the sections reference them, and the text boxes in each - is
249
+ the package's, as docx4j lists it (`TrackedChanges.in`), with docx4j's kind and the part of each
250
+ change. The listing and the resolve-all verbs take a filter: docx4j's conditions as an object, which
251
+ travels as JSON, or a function over each single change. A group of touching changes is listed whole
252
+ when any of its pieces matches, and resolved only in the pieces that match:
253
+
254
+ ```ts
255
+ for (const change of await pkg.getAllTrackedChanges()) {
256
+ change.kind; // docx4j's: 'RUN_INSERTION', 'PARAGRAPH_MARK_DELETION', 'TABLE_PROPERTIES', ...
257
+ change.part.partName.name; // '/word/document.xml', '/word/footnotes.xml', '/word/header1.xml', ...
258
+ }
259
+ await pkg.rejectAllRevisions({ authors: ['Author B'] });
260
+ await pkg.acceptAllRevisions({ types: ['Deleted'], from: '2026-10-01', parts: ['/word/document.xml'] });
261
+ await pkg.acceptAllRevisions((change) => change.author === 'Claude');
262
+ body.acceptAll({ kinds: ['RUN_DELETION'] }); // one story; a deleted paragraph's text goes, its mark stays
263
+ ```
264
+
265
+ The conditions are `authors`, `types` (Office JS's names or docx4j's `ADDED`, `DELETED`,
266
+ `FORMATTED`), `kinds`, `from` (inclusive) and `to` (exclusive), and `parts` (a part or its name).
267
+ A key or a value the filter does not know throws a `TypeError`, before anything is resolved. With a
268
+ filter, what no change owns (a `w:cellMerge`, a `w:numberingChange`) is left alone; without one it is
269
+ swept, as before. Each verb returns how many listed changes had a matching piece.
242
270
 
243
271
  To record one caller's edits without touching the document's own setting - an agent working in a
244
272
  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
+ };