@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
@@ -1,5 +1,6 @@
1
- import { isElement, typeNameOf, runItemsOf, mcBranchOf } from '@docx4j/generated-objects-ts/builders/wml';
2
- import { UNDERSTOOD_PREFIXES } from '../../opc/mce/understood.mjs';
1
+ import { isElement, typeNameOf, runItemsOf, mcBranchOf, sdtProperty } from '@docx4j/generated-objects-ts/builders/wml';
2
+ import { isMath, mathTextOf } from './mathText.mjs';
3
+ import { NotSupportedError } from '../../office-js/errors.mjs';
3
4
  export { isElement, typeNameOf, walk, find, linkParents, textOf, W_NS } from '@docx4j/generated-objects-ts/builders/wml';
4
5
  /**
5
6
  * Run content that stands in the text and has no text of its own: a `w:drawing` (a picture, a chart, a
@@ -12,13 +13,14 @@ export const OBJECT_ITEMS = new Set(['drawing', 'pict', 'object']);
12
13
  export const ALTERNATE_CONTENT = 'org_docx4j_mce.AlternateContent';
13
14
  /**
14
15
  * The branch of a kept `mc:AlternateContent` the content API reads (docx4j's `McMode.READ`, CR-010
15
- * phase 3): the objects package's `mcBranchOf` over the prefixes load understands
16
- * (`UNDERSTOOD_PREFIXES`), so a reader of the kept element takes the branch the preprocessor would have
17
- * resolved it to. A mutator walks every branch instead (docx4j's `McMode.ALL`). Undefined for an element
18
- * with no branches.
16
+ * phase 3): the objects package's `mcBranchOf`, the first `mc:Choice` whose `Requires` names only namespaces in the
17
+ * understood set, by URI, else the `mc:Fallback`, else nothing (WD REQ-302 to REQ-305, CR-029). So a reader takes the
18
+ * branch the preprocessor resolves the element to. A mutator walks every branch instead (docx4j's `McMode.ALL`). Undefined
19
+ * where nothing is selected, as for an element with no branches. `options` are `mcBranchOf`'s: the text model passes
20
+ * `onNothingSelected` for `mce.nothing_selected`.
19
21
  */
20
- export function selectedBranchOf(value) {
21
- return mcBranchOf(value, { understood: UNDERSTOOD_PREFIXES });
22
+ export function selectedBranchOf(value, options) {
23
+ return mcBranchOf(value, options);
22
24
  }
23
25
  /** True for a kept `mc:AlternateContent`: its element pair, or its value. */
24
26
  export function isAlternateContent(value) {
@@ -52,6 +54,46 @@ function shows(view, kind) {
52
54
  return view === 'accepted' ? kind === 'ins' || kind === 'moveTo' : kind === 'del' || kind === 'moveFrom';
53
55
  }
54
56
  export { runItemsOf };
57
+ /**
58
+ * An item's text in Office JS's text, Word's own character stream (CR-012 phase 4; check 48, Word 365):
59
+ * a `w:br` U+000B, of type page U+000C and column U+000E; a `w:cr` U+000D; a `w:ptab` `0`; the
60
+ * non-breaking and optional hyphens U+001E and U+001F; any `w:sym` `(`, whatever its font or code
61
+ * (check 48b); a footnote or endnote reference U+0002, and so the note's own mark (`w:footnoteRef`,
62
+ * `w:endnoteRef`, not measured). Undefined for an item that adds no text: a comment reference, a
63
+ * field's characters and code, a drawing.
64
+ */
65
+ export function officeItemText(item) {
66
+ var _a;
67
+ const v = item.value;
68
+ switch (item.name.localPart) {
69
+ case 't':
70
+ case 'delText': return String((_a = v.value) !== null && _a !== void 0 ? _a : '');
71
+ case 'tab': return '\t';
72
+ case 'br': return v.type === 'page' ? '\f' : v.type === 'column' ? '\u000E' : '\u000B';
73
+ case 'cr': return '\r';
74
+ case 'ptab': return '0';
75
+ case 'noBreakHyphen': return '\u001E';
76
+ case 'softHyphen': return '\u001F';
77
+ case 'sym': return '(';
78
+ case 'footnoteReference':
79
+ case 'endnoteReference':
80
+ case 'footnoteRef':
81
+ case 'endnoteRef': return '\u0002';
82
+ default: return undefined;
83
+ }
84
+ }
85
+ /** An item a run-level element's selected branch may hold that makes the element one object (CR-012 section 12.2 item 5). */
86
+ function isDrawn(item) {
87
+ return OBJECT_ITEMS.has(item.name.localPart);
88
+ }
89
+ /** The `run` of an equation's segment: it is in no run, and nothing may be written to this one. */
90
+ function standIn(math) {
91
+ return Object.freeze({ TYPE_NAME: 'org_docx4j_wml.R', PARENT: math.value.PARENT });
92
+ }
93
+ /**
94
+ * The item text the revision listings and the text model's plan count (`itemTextOf`): whether a run
95
+ * holds text, in the characters `textOf` gives. Not Office JS's (`officeItemText`).
96
+ */
55
97
  function itemText(item) {
56
98
  var _a, _b;
57
99
  const t = item.name.localPart;
@@ -72,53 +114,103 @@ function itemText(item) {
72
114
  }
73
115
  }
74
116
  /**
75
- * The text segments of a paragraph (or any run holder) in order. The default view is the
76
- * accepted one: the runs of a `w:ins` or `w:moveTo` count, those of a `w:del` or `w:moveFrom`
77
- * do not. `{ view: 'original' }` is the other way round (CR-002 phase F). `{ objects: true }`
78
- * lists the drawn objects too, each a segment of no text where it stands (CR-002 section 48).
117
+ * The text segments of a paragraph (or any run holder) in order, in Office JS's characters
118
+ * (`officeItemText`). The default view is the accepted one: the runs of a `w:ins` or `w:moveTo`
119
+ * count, those of a `w:del` or `w:moveFrom` do not. `{ view: 'original' }` is the other way round
120
+ * (CR-002 phase F). `{ objects: true }` lists the drawn objects too, each a segment of no text where
121
+ * it stands (CR-002 section 48), and the items of hidden runs (`hidden`).
79
122
  *
80
- * A kept `mc:AlternateContent` in a run is one object segment, whatever its branches hold, so that
81
- * removing a span removes every branch with it (CR-010 phase 3). A drawing adds no text, so the
82
- * offsets are a resolved load's. One in a paragraph's content, outside a run, is not listed: Word
83
- * writes none, and the corpus has none (CR-010 section 11.3).
123
+ * A kept `mc:AlternateContent` reads as its selected branch (CR-012 section 12.2, ED-009): in a run,
124
+ * a branch holding run items reads as those items, at the run's offsets, and one holding a drawing,
125
+ * a picture or an object is one object segment, as a drawing is (CR-010 phase 3); among a paragraph's
126
+ * runs, it reads as the runs its branch holds. Either way the segments carry `alternate`, so that an
127
+ * edit lands in that branch. An equation is one segment of its drawn text (`math`).
84
128
  */
85
129
  export function segmentsOf(container, options) {
86
130
  var _a;
87
131
  const view = (_a = options === null || options === void 0 ? void 0 : options.view) !== null && _a !== void 0 ? _a : 'accepted';
88
132
  const objects = (options === null || options === void 0 ? void 0 : options.objects) === true;
133
+ const hiddenRun = options === null || options === void 0 ? void 0 : options.hidden;
134
+ const visit = options === null || options === void 0 ? void 0 : options.visit;
135
+ const office = (options === null || options === void 0 ? void 0 : options.characters) !== 'textOf';
136
+ const charactersOf = office ? officeItemText : itemText;
89
137
  const out = [];
90
138
  let pos = 0;
91
- const visitRuns = (items, revision) => {
139
+ const runContent = (run, runEl, content, runOwner, runIndex, revision, alternate, hidden) => {
140
+ for (let j = 0; j < content.length; j++) {
141
+ const item = content[j];
142
+ const where = { owner: content, index: j, run, runOwner, runIndex };
143
+ if (office && isAlternateContent(item)) {
144
+ const branch = selectedBranchOf(item.value);
145
+ // a branch holding content the model doesn't bind (DOM) is one object, as a drawing is
146
+ if (branch && branch.every(isElement) && !branch.some(isDrawn)) {
147
+ runContent(run, runEl, branch, runOwner, runIndex, revision, { element: item, owner: content, branch, level: 'run' }, hidden);
148
+ continue;
149
+ }
150
+ }
151
+ const text = charactersOf(item);
152
+ if (text === undefined || (hidden && text !== '')) {
153
+ const drawn = OBJECT_ITEMS.has(item.name.localPart) || isAlternateContent(item);
154
+ if (objects && (drawn || (hidden && text !== undefined))) {
155
+ const seg = { item, ...where, text: '', start: pos, end: pos, editable: false, object: true };
156
+ if (hidden)
157
+ seg.hidden = true;
158
+ if (revision)
159
+ seg.revision = revision;
160
+ if (alternate)
161
+ seg.alternate = alternate;
162
+ out.push(seg);
163
+ }
164
+ visit === null || visit === void 0 ? void 0 : visit(item, pos, content, { element: runEl, owner: runOwner });
165
+ continue;
166
+ }
167
+ const seg = { item, ...where, text, start: pos, end: pos + text.length, editable: item.name.localPart === 't' };
168
+ if (revision)
169
+ seg.revision = revision;
170
+ if (alternate)
171
+ seg.alternate = alternate;
172
+ out.push(seg);
173
+ pos += text.length;
174
+ }
175
+ };
176
+ const visitRuns = (items, revision, alternate) => {
92
177
  var _a;
93
178
  if (!items)
94
179
  return;
95
180
  for (let i = 0; i < items.length; i++) {
96
181
  const el = items[i];
182
+ if (!isElement(el))
183
+ continue; // a kept branch's unbound content (DOM)
184
+ visit === null || visit === void 0 ? void 0 : visit(el, pos, items, undefined);
97
185
  const tn = typeNameOf(el);
98
186
  if (tn === 'org_docx4j_wml.R') {
99
187
  const run = el.value;
100
- const content = (_a = run.content) !== null && _a !== void 0 ? _a : [];
101
- for (let j = 0; j < content.length; j++) {
102
- const item = content[j];
103
- const text = itemText(item);
104
- if (text === undefined) {
105
- if (objects && (OBJECT_ITEMS.has(item.name.localPart) || isAlternateContent(item))) {
106
- const seg = { item, owner: content, index: j, run, runOwner: items, runIndex: i, text: '', start: pos, end: pos, editable: false, object: true };
107
- if (revision)
108
- seg.revision = revision;
109
- out.push(seg);
110
- }
111
- continue;
112
- }
113
- const seg = {
114
- item, owner: content, index: j, run, runOwner: items, runIndex: i, text,
115
- start: pos, end: pos + text.length, editable: item.name.localPart === 't',
116
- };
117
- if (revision)
118
- seg.revision = revision;
119
- out.push(seg);
120
- pos += text.length;
121
- }
188
+ runContent(run, el, (_a = run.content) !== null && _a !== void 0 ? _a : [], items, i, revision, alternate, (hiddenRun === null || hiddenRun === void 0 ? void 0 : hiddenRun(run)) === true);
189
+ continue;
190
+ }
191
+ if (!office) {
192
+ // textOf's reader: no branch among the runs, no equation
193
+ }
194
+ else if (isAlternateContent(el)) {
195
+ const branch = selectedBranchOf(el.value);
196
+ if (branch)
197
+ visitRuns(branch, revision, { element: el, owner: items, branch, level: 'paragraph' });
198
+ continue;
199
+ }
200
+ else if (isMath(el)) {
201
+ const text = mathTextOf(el.value, view);
202
+ if (text === '')
203
+ continue;
204
+ const seg = {
205
+ item: el, owner: items, index: i, run: standIn(el), runOwner: items, runIndex: i, text,
206
+ start: pos, end: pos + text.length, editable: false, math: true,
207
+ };
208
+ if (revision)
209
+ seg.revision = revision;
210
+ if (alternate)
211
+ seg.alternate = alternate;
212
+ out.push(seg);
213
+ pos += text.length;
122
214
  continue;
123
215
  }
124
216
  const kind = revisionKindOf(el);
@@ -127,21 +219,60 @@ export function segmentsOf(container, options) {
127
219
  continue;
128
220
  const nested = runItemsOf(el.value);
129
221
  if (nested)
130
- visitRuns(nested, { kind, element: el, value: el.value, items: nested, owner: items });
222
+ visitRuns(nested, { kind, element: el, value: el.value, items: nested, owner: items }, alternate);
131
223
  }
132
224
  else if (tn !== undefined && RUN_HOLDERS.has(tn)) {
133
- visitRuns(runItemsOf(el.value), revision);
225
+ visitRuns(runItemsOf(el.value), revision, alternate);
134
226
  }
135
227
  }
136
228
  };
137
- visitRuns(runItemsOf(container), undefined);
229
+ visitRuns(runItemsOf(container), undefined, undefined);
138
230
  return out;
139
231
  }
140
- /** The text of a paragraph or container in one of the two views (`textOf` is the accepted one). */
232
+ /**
233
+ * Which of a paragraph's runs are hidden, as the text model's rule has it (REQ-507) and Office JS's text
234
+ * leaves them out (check 48b): a run's own `w:vanish`, else its paragraph style's and character style's.
235
+ * Without a resolver only a run's own counts.
236
+ */
237
+ export function hiddenRunsOf(resolver, p) {
238
+ var _a, _b;
239
+ const pStyle = (_b = (_a = p.pPr) === null || _a === void 0 ? void 0 : _a.pStyle) === null || _b === void 0 ? void 0 : _b.val;
240
+ return (run) => {
241
+ var _a, _b, _c, _d;
242
+ if (inShowingPlaceholder(run))
243
+ return true;
244
+ const own = (_a = run.rPr) === null || _a === void 0 ? void 0 : _a.vanish;
245
+ if (own !== undefined && own !== null)
246
+ return own.val !== false;
247
+ return (_d = resolver === null || resolver === void 0 ? void 0 : resolver.isVanishedByStyles(pStyle, (_c = (_b = run.rPr) === null || _b === void 0 ? void 0 : _b.rStyle) === null || _c === void 0 ? void 0 : _c.val)) !== null && _d !== void 0 ? _d : false;
248
+ };
249
+ }
250
+ /**
251
+ * Whether a run is in a content control showing its placeholder (`w:showingPlcHdr`), run-level or block-level, up its
252
+ * `PARENT`s. Office JS reads a placeholder as no text, and finds no text there to keep (check 53 cases 34 and 35, check 58
253
+ * cases 11 and 12; CR-012 section 18), so the readers take its runs as they take hidden ones.
254
+ */
255
+ export function inShowingPlaceholder(run) {
256
+ var _a;
257
+ let o = run.PARENT;
258
+ for (let guard = 0; o && guard < 32; guard++, o = o.PARENT) {
259
+ const v = o;
260
+ if (v.sdtPr === undefined || v.sdtContent === undefined)
261
+ continue;
262
+ const item = sdtProperty(v.sdtPr, 'showingPlcHdr');
263
+ if (item !== undefined && ((_a = item.value) === null || _a === void 0 ? void 0 : _a.val) !== false)
264
+ return true;
265
+ }
266
+ return false;
267
+ }
268
+ /** The text of a paragraph or container in one of the two views, in Office JS's characters (`segmentsOf`). */
141
269
  export function textOfView(container, options) {
142
270
  return segmentsOf(container, options).map((s) => s.text).join('');
143
271
  }
144
- /** The runs of a paragraph, direct and nested in hyperlinks, content controls and insertions, in order. */
272
+ /**
273
+ * The runs of a paragraph, direct and nested in hyperlinks, content controls and insertions, in order,
274
+ * and in the selected branch of a kept `mc:AlternateContent` among them (CR-012 section 12.2).
275
+ */
145
276
  export function runsOf(container, options) {
146
277
  var _a;
147
278
  const view = (_a = options === null || options === void 0 ? void 0 : options.view) !== null && _a !== void 0 ? _a : 'accepted';
@@ -155,6 +286,10 @@ export function runsOf(container, options) {
155
286
  out.push(el);
156
287
  continue;
157
288
  }
289
+ if (isAlternateContent(el)) {
290
+ visit(selectedBranchOf(el.value));
291
+ continue;
292
+ }
158
293
  const kind = revisionKindOf(el);
159
294
  if (kind !== undefined) {
160
295
  if (shows(view, kind))
@@ -167,30 +302,129 @@ export function runsOf(container, options) {
167
302
  visit(runItemsOf(container));
168
303
  return out;
169
304
  }
305
+ /** The TYPE_NAMEs of a kept `mc:AlternateContent`'s branches, whose items are their `any`. */
306
+ export const BRANCH_TYPES = new Set(['org_docx4j_mce.AlternateContent.Choice', 'org_docx4j_mce.AlternateContent.Fallback']);
170
307
  /**
171
308
  * The block-level children of a container: body, header, footer, cell, sdt content, table (rows),
172
309
  * row (cells). A content control of any kind (block, row, cell, run) answers with its
173
- * `sdtContent`'s content, so that a traversal does not have to know the four types.
310
+ * `sdtContent`'s content, so that a traversal does not have to know the four types. A branch of a kept
311
+ * `mc:AlternateContent` (a Choice or the Fallback) answers with its items, so that a locator climbing
312
+ * `PARENT` from a branch's paragraph, row or cell finds the list it is in; the element itself answers
313
+ * in a reader's walk (`'read'`) with its selected branch (CR-013 phase 2), and otherwise not at all.
174
314
  */
175
- export function childrenOf(value) {
315
+ export function childrenOf(value, mode) {
176
316
  const v = value;
177
317
  if (v.sdtContent)
178
318
  return v.sdtContent.content;
179
- return Array.isArray(v.content) ? v.content : undefined;
319
+ if (Array.isArray(v.content))
320
+ return v.content;
321
+ const tn = v.TYPE_NAME;
322
+ if (tn === undefined)
323
+ return undefined;
324
+ if (BRANCH_TYPES.has(tn))
325
+ return v.any;
326
+ if (mode === 'read' && tn === ALTERNATE_CONTENT)
327
+ return selectedBranchOf(v);
328
+ return undefined;
329
+ }
330
+ /** Every branch of a kept `mc:AlternateContent`, the Choices in order, then the Fallback. */
331
+ export function branchesOf(value) {
332
+ var _a, _b;
333
+ const out = [];
334
+ for (const choice of (_a = value.choice) !== null && _a !== void 0 ? _a : [])
335
+ if (choice.any)
336
+ out.push(choice.any);
337
+ if ((_b = value.fallback) === null || _b === void 0 ? void 0 : _b.any)
338
+ out.push(value.fallback.any);
339
+ return out;
340
+ }
341
+ /** How many bulk edits are writing in every branch now (`writingEveryBranch`). */
342
+ let everyBranch = 0;
343
+ /**
344
+ * Runs a bulk edit that reaches every branch of a kept `mc:AlternateContent` (CR-013 phase 3): a
345
+ * `replaceText`, a tracked clear or row deletion, the binding passes. Inside it the views it makes of
346
+ * what is in a branch write, where a caller's view refuses (D4): the edit itself reaches every branch,
347
+ * the selected one and the others alike, as CR-010's rule has mutators do.
348
+ */
349
+ export function writingEveryBranch(edit) {
350
+ everyBranch++;
351
+ try {
352
+ return edit();
353
+ }
354
+ finally {
355
+ everyBranch--;
356
+ }
357
+ }
358
+ /**
359
+ * Refuses a write through a view in a branch of a kept `mc:AlternateContent` at block, row or cell level
360
+ * (CR-013 D4): the view reads the selected branch, and a write would have to reach the element's other
361
+ * branches too, which nothing maps. A bulk edit reaching every branch writes there (`writingEveryBranch`).
362
+ * `className` is Office JS's ('Paragraph', 'Body').
363
+ */
364
+ export function assertOutsideBranch(value, className, what = `this ${className.toLowerCase()}`) {
365
+ if (everyBranch === 0 && inBranch(value)) {
366
+ throw new NotSupportedError(className, 'edit', `${what} is in a branch of an mc:AlternateContent, which is read-only (CR-013 D4): `
367
+ + "a write would have to reach the element's other branches too, which nothing maps yet");
368
+ }
369
+ }
370
+ /**
371
+ * True for a value inside a branch of a kept `mc:AlternateContent` at block, row or cell level: a
372
+ * paragraph, table, row, cell or content control a reader sees in the selected branch (CR-013 phase 2).
373
+ * A view of one is read-only (D4). From `PARENT` links; a run-level element's content is not reached
374
+ * from a paragraph, which is outside it.
375
+ */
376
+ export function inBranch(value) {
377
+ let o = value === null || value === void 0 ? void 0 : value.PARENT;
378
+ for (let guard = 0; o && guard < 256; guard++, o = o.PARENT) {
379
+ const tn = o.TYPE_NAME;
380
+ if (tn !== undefined && BRANCH_TYPES.has(tn))
381
+ return true;
382
+ }
383
+ return false;
384
+ }
385
+ /**
386
+ * True for a value no reader sees: inside a branch of a kept `mc:AlternateContent` other than the one
387
+ * `selectedBranchOf` takes, at any depth (CR-013 phase 3). What a bulk edit writes there it does not
388
+ * count, as resolve-all counts the listed changes: the count is of what a reader sees.
389
+ */
390
+ export function inUnreadBranch(value) {
391
+ let o = value === null || value === void 0 ? void 0 : value.PARENT;
392
+ for (let guard = 0; o && guard < 256; guard++, o = o.PARENT) {
393
+ const tn = o.TYPE_NAME;
394
+ if (tn === undefined || !BRANCH_TYPES.has(tn))
395
+ continue;
396
+ const ac = o.PARENT;
397
+ if (ac && selectedBranchOf(ac) !== o.any)
398
+ return true;
399
+ }
400
+ return false;
401
+ }
402
+ /**
403
+ * The lists a walk takes for a kept `mc:AlternateContent` among blocks, rows or cells: a reader's
404
+ * (`'read'`) its selected branch, a mutator's (`'every'`) every branch, the Choices first.
405
+ */
406
+ export function branchListsOf(value, mode) {
407
+ if (mode === 'every')
408
+ return branchesOf(value);
409
+ const read = selectedBranchOf(value);
410
+ return read ? [read] : [];
180
411
  }
181
412
  /** w:sdt and w:customXml at row level: they hold the rows of a table (docx4j CTSdtRow, CTCustomXmlRow). */
182
413
  const ROW_HOLDERS = new Set(['org_docx4j_wml.CTSdtRow', 'org_docx4j_wml.CTCustomXmlRow']);
183
414
  /** The same at cell level. */
184
415
  const CELL_HOLDERS = new Set(['org_docx4j_wml.CTSdtCell', 'org_docx4j_wml.CTCustomXmlCell']);
185
- /** The rows of a table, in order, descending into row-level content controls (an OpenDoPE repeat). */
186
- export function rowsOf(tbl) {
187
- return located(childrenOf(tbl), 'org_docx4j_wml.Tr', ROW_HOLDERS);
416
+ /**
417
+ * The rows of a table, in order, descending into row-level content controls (an OpenDoPE repeat), and,
418
+ * with a mode, into a row-level `mc:AlternateContent` (`BranchMode`; CR-013 phase 2).
419
+ */
420
+ export function rowsOf(tbl, mode) {
421
+ return located(childrenOf(tbl), 'org_docx4j_wml.Tr', ROW_HOLDERS, mode);
188
422
  }
189
- /** The cells of a row, in order, descending into cell-level content controls. */
190
- export function cellsOf(tr) {
191
- return located(childrenOf(tr), 'org_docx4j_wml.Tc', CELL_HOLDERS);
423
+ /** The cells of a row, in order, descending into cell-level content controls, and with a mode into a cell-level `mc:AlternateContent`. */
424
+ export function cellsOf(tr, mode) {
425
+ return located(childrenOf(tr), 'org_docx4j_wml.Tc', CELL_HOLDERS, mode);
192
426
  }
193
- function located(items, typeName, holders) {
427
+ function located(items, typeName, holders, mode, reading) {
194
428
  const out = [];
195
429
  const visit = (list) => {
196
430
  if (!list)
@@ -201,11 +435,51 @@ function located(items, typeName, holders) {
201
435
  out.push({ element, container: list });
202
436
  else if (tn !== undefined && holders.has(tn) && typeof element.value === 'object' && element.value !== null)
203
437
  visit(childrenOf(element.value));
438
+ else if (tn === ALTERNATE_CONTENT && mode !== undefined) {
439
+ if (reading && element.value === reading.ac)
440
+ visit(reading.branch);
441
+ else
442
+ for (const branch of branchListsOf(element.value, mode))
443
+ visit(branch);
444
+ }
204
445
  }
205
446
  };
206
447
  visit(items);
207
448
  return out;
208
449
  }
450
+ /**
451
+ * A row's cells as laid along the grid, one list per reading of the row (docx4j's `Lister.cellLayoutsOf`,
452
+ * its CR-051 phase 2): the selected branches' first (`cellsOf(tr, 'read')`), then, for each cell-level
453
+ * `mc:AlternateContent` and each of its other branches, the row read with that branch in the element's
454
+ * place. Every branch spans the same grid columns, so every cell is in some list at its grid position:
455
+ * what a pass over the grid's columns walks (`collapseGrid`, the column passes; CR-013 phase 3).
456
+ */
457
+ export function cellLayoutsOf(tr) {
458
+ const out = [cellsOf(tr, 'read')];
459
+ const elements = [];
460
+ const collect = (list) => {
461
+ for (const element of list !== null && list !== void 0 ? list : []) {
462
+ const tn = typeNameOf(element);
463
+ if (tn === ALTERNATE_CONTENT) {
464
+ elements.push(element.value);
465
+ for (const branch of branchesOf(element.value))
466
+ collect(branch);
467
+ }
468
+ else if (tn !== undefined && CELL_HOLDERS.has(tn) && typeof element.value === 'object' && element.value !== null) {
469
+ collect(childrenOf(element.value));
470
+ }
471
+ }
472
+ };
473
+ collect(childrenOf(tr));
474
+ for (const ac of elements) {
475
+ const read = selectedBranchOf(ac);
476
+ for (const branch of branchesOf(ac)) {
477
+ if (branch !== read)
478
+ out.push(located(childrenOf(tr), 'org_docx4j_wml.Tc', CELL_HOLDERS, 'read', { ac, branch }));
479
+ }
480
+ }
481
+ return out;
482
+ }
209
483
  /** Content controls: `w:sdt` at block, row, cell and run level, by TYPE_NAME. */
210
484
  export const SDT_TYPES = new Set([
211
485
  'org_docx4j_wml.SdtBlock', 'org_docx4j_wml.SdtRun', 'org_docx4j_wml.CTSdtRow', 'org_docx4j_wml.CTSdtCell',
@@ -0,0 +1,48 @@
1
+ /** How Word 365 stores an attribute a setter writes, and the range the setter writes within, in the attribute's unit. */
2
+ export interface WordStorage {
3
+ /** The attribute, as the part writes it. */
4
+ attribute: string;
5
+ /** The attribute's unit, and how many make a point. */
6
+ unit: 'twips' | 'half-points' | 'fiftieths of a percent';
7
+ min: number;
8
+ max: number;
9
+ /** What Word reads a value as, once it is stored: the wrap, as CR-066 measured it (the objects package's `wordIntegerReading`). */
10
+ reads(value: number): string;
11
+ /** Measured by CR-066's rounds 5 and 6: every row is, since round 6 (section 65.6). */
12
+ measured: boolean;
13
+ /** The range Word keeps as written, the objects table's (`keptRange`), before the setter's policy narrows it. */
14
+ stored: readonly [number, number];
15
+ /** Why a value Word would keep as written is refused all the same: the range narrowed below Word's storage. */
16
+ why?: string;
17
+ /** The objects table's entry the row reads: `TYPE_NAME`, property, and the rule beside the attribute where one applies. */
18
+ objects: {
19
+ typeName: string;
20
+ property: string;
21
+ rule?: 'auto' | 'exact' | 'atLeast';
22
+ };
23
+ }
24
+ /**
25
+ * The attributes the content API's setters write, by Word 365's storage (the objects table; CR-066 sections 3.1 and 3.4),
26
+ * narrowed where Word's own setter stops short of its storage (check 71b, CR-002 section 65.9, D2): there this package
27
+ * refuses where Word does, and no earlier.
28
+ */
29
+ export declare const WORD_STORAGE: {
30
+ readonly indLeft: WordStorage;
31
+ readonly indRight: WordStorage;
32
+ readonly indFirstLine: WordStorage;
33
+ readonly indHanging: WordStorage;
34
+ readonly lvlIndLeft: WordStorage;
35
+ readonly spacingBefore: WordStorage;
36
+ readonly spacingAfter: WordStorage;
37
+ readonly spacingLineAuto: WordStorage;
38
+ readonly tcWDxa: WordStorage;
39
+ readonly tcWPct: WordStorage;
40
+ readonly sz: WordStorage;
41
+ };
42
+ /**
43
+ * `points` in the attribute's unit, rounded, where Word stores it as written and the setter's policy allows; else
44
+ * `InvalidArgument`, naming the range and what Word would read, or why the policy stops short of Word's storage (CR-002
45
+ * section 65; the policy section 65.9's). `where` is the setter (`Paragraph.leftIndent`). `units` is the value already
46
+ * in the attribute's unit, where the setter computes it otherwise (a percentage width's fiftieths).
47
+ */
48
+ export declare function withinWord(where: string, points: number, storage: WordStorage, units?: number): number;
@@ -0,0 +1,97 @@
1
+ // CR-002 section 65: a measurement setter writes a value only where Word 365 stores it as written. Word keeps each
2
+ // integer attribute in a field of its binary format's width, 16 bits for these, and reads a value past it wrapped, with
3
+ // no error or repair (docx4j CR-065 round 2, CR-066 rounds 5 and 6, run by Jason 2026-10-26): a left indent of 400,000 pt,
4
+ // 8,000,000 twips, opens as 4,608. So the setters refuse such a value, as Office JS refuses an argument it won't take.
5
+ // The width is the attribute's, not its simple type's (CR-066 section 3.1). A file's value is read and kept as written.
6
+ // Since section 65.3 the storage comes from the objects package's table (`WORD_INTEGER_STORAGE`, its CR-016), through
7
+ // `keptRange`, one source for the setters' guards and the out-of-range warnings (CR-017); what each row adds is the
8
+ // setters' own policy, a range narrowed below Word's storage where a value Word keeps is still a caller's mistake.
9
+ import { wordIntegerReading } from '@docx4j/generated-objects-ts/helpers/wml';
10
+ import { keptRange } from '../wordIntegers.mjs';
11
+ import { InvalidArgument } from './generalException.mjs';
12
+ const WML = 'org_docx4j_wml.';
13
+ const WORD_SPACING_LIMIT = "Word's own setter stops at 22 inches, 1584 pt (check 71b); its storage runs to 65535, which its save writes back as -1";
14
+ /**
15
+ * A row from the objects table: the range Word keeps as written there (`keptRange`), narrowed by the setter's policy
16
+ * where `min`, `max` and `why` say so; `reads` through `wordIntegerReading`. A row whose entry the table lacks, or
17
+ * no longer measures, fails here, at load, so a table change announces itself.
18
+ */
19
+ function row(attribute, unit, typeName, property, policy = {}) {
20
+ var _a, _b;
21
+ const range = keptRange(WML + typeName, property, policy.rule);
22
+ if (range === undefined || range === null)
23
+ throw new Error(`wordRange: the objects table has no measured range for ${typeName}.${property}`);
24
+ const name = (p) => (p === 'hanging' ? 'a hanging indent' : 'a first-line indent');
25
+ return {
26
+ attribute, unit, measured: true, stored: range,
27
+ min: (_a = policy.min) !== null && _a !== void 0 ? _a : range[0], max: (_b = policy.max) !== null && _b !== void 0 ? _b : range[1],
28
+ ...(policy.why !== undefined ? { why: policy.why } : {}),
29
+ objects: { typeName: WML + typeName, property, ...(policy.rule !== undefined ? { rule: policy.rule } : {}) },
30
+ reads(value) {
31
+ const r = wordIntegerReading(WML + typeName, property, value, policy.rule);
32
+ if (r === undefined || r.value === null)
33
+ return 'not known';
34
+ if (r.attribute !== undefined && r.attribute !== property)
35
+ return `${name(r.attribute)} of ${r.value}`;
36
+ if (r.rule !== undefined && policy.rule !== undefined)
37
+ return `${r.rule} ${r.value}`;
38
+ return String(r.value);
39
+ },
40
+ };
41
+ }
42
+ /**
43
+ * The attributes the content API's setters write, by Word 365's storage (the objects table; CR-066 sections 3.1 and 3.4),
44
+ * narrowed where Word's own setter stops short of its storage (check 71b, CR-002 section 65.9, D2): there this package
45
+ * refuses where Word does, and no earlier.
46
+ */
47
+ export const WORD_STORAGE = {
48
+ // int16, wraps (w:left measured by round 5, w:right by round 6); Word's own indent setters wrap too, and this package
49
+ // refuses instead (section 65.5, D1)
50
+ indLeft: row('w:ind/@w:left', 'twips', 'PPrBase.Ind', 'left'),
51
+ indRight: row('w:ind/@w:right', 'twips', 'PPrBase.Ind', 'right'),
52
+ // one signed 16-bit field, the binary format's dxaLeft1: a first-line indent as itself, a hanging indent as its negative,
53
+ // so 0 to 32767 and 0 to 32768 (check 71: Word's firstLineIndent setter wrote 1638.4 pt as a hanging indent of 32768;
54
+ // round 6, the file side: a first-line indent of 32768 reads as a hanging indent of 32768)
55
+ indFirstLine: row('w:ind/@w:firstLine', 'twips', 'PPrBase.Ind', 'firstLine'),
56
+ indHanging: row('w:ind/@w:hanging', 'twips', 'PPrBase.Ind', 'hanging'),
57
+ // a list level's text indent, the same int16 field, but Word's own setLevelIndents refuses at 1584 pt either side
58
+ // (check 71b: 1583.95 and -1583.95 written, 1584 and -1584 InvalidArgument), so 31679 twips either way
59
+ lvlIndLeft: row('w:ind/@w:left', 'twips', 'PPrBase.Ind', 'left', { min: -31679, max: 31679, why: "Word's own setLevelIndents stops short of 22 inches, 1584 pt, either way (check 71b)" }),
60
+ // uint16, wraps, and Word's save writes 0xFFFF back as -1; Word's own setters stop at 22 inches, 1584 pt (check 71b: 1584
61
+ // written as 31680, 1584.05 InvalidArgument; a negative refused, the schema's type being unsigned too)
62
+ spacingBefore: row('w:spacing/@w:before', 'twips', 'PPrBase.Spacing', 'before', { max: 31680, why: WORD_SPACING_LIMIT }),
63
+ spacingAfter: row('w:spacing/@w:after', 'twips', 'PPrBase.Spacing', 'after', { max: 31680, why: WORD_SPACING_LIMIT }),
64
+ // Office JS's lineSpacing, in points, is the line as a multiple of 12 pt ("in the Word UI, this value is divided by 12"):
65
+ // Word's own setter writes w:line = 20 × points under w:lineRule="auto" (check 71b: 0.75 pt as w:line="15"
66
+ // w:lineRule="auto", 1584 as 31680), and takes 0.75 to 1584 pt (0.7 and 1584.05 InvalidArgument); int16, wraps
67
+ spacingLineAuto: row('w:spacing/@w:line', 'twips', 'PPrBase.Spacing', 'line', { rule: 'auto', min: 15, max: 31680, why: "Word's own setter takes 0.75 to 1584 pt, 15 to 31680 twips under w:lineRule=\"auto\" (check 71b)" }),
68
+ // int16, wraps, under dxa and under pct; a width is never negative
69
+ tcWDxa: row('w:tcW/@w:w', 'twips', 'TblWidth', 'w', { min: 0, why: "a cell's width is never negative" }),
70
+ tcWPct: row('w:tcW/@w:w', 'fiftieths of a percent', 'TblWidth', 'w', { min: 0, why: "a cell's width is never negative" }),
71
+ // uint16, wraps; Word's own setter takes 1 to 1638 pt, in half-points after rounding (check 71b: 0.9 written as w:sz
72
+ // w:val="2", 1638.05 as 3276, 0.5 and 1638.4 InvalidArgument), where its storage runs to 65535
73
+ sz: row('w:sz/@w:val', 'half-points', 'HpsMeasure', 'val', { min: 2, max: 3276, why: "Word's own setter takes 1 to 1638 pt, 2 to 3276 half-points after rounding (check 71b)" }),
74
+ };
75
+ const PER_POINT = { twips: 20, 'half-points': 2 };
76
+ /**
77
+ * `points` in the attribute's unit, rounded, where Word stores it as written and the setter's policy allows; else
78
+ * `InvalidArgument`, naming the range and what Word would read, or why the policy stops short of Word's storage (CR-002
79
+ * section 65; the policy section 65.9's). `where` is the setter (`Paragraph.leftIndent`). `units` is the value already
80
+ * in the attribute's unit, where the setter computes it otherwise (a percentage width's fiftieths).
81
+ */
82
+ export function withinWord(where, points, storage, units) {
83
+ if (!Number.isFinite(points))
84
+ throw new InvalidArgument(`${where}: ${points} is not a measurement in points`);
85
+ const unit = storage.unit;
86
+ const value = units !== null && units !== void 0 ? units : Math.round(points * PER_POINT[unit]);
87
+ if (value >= storage.min && value <= storage.max)
88
+ return value;
89
+ const inPoints = unit in PER_POINT
90
+ ? `, ${storage.min / PER_POINT[unit]} to ${storage.max / PER_POINT[unit]} pt`
91
+ : '';
92
+ // within Word's storage the value is kept as written, so the refusal is the setter's policy; past it, what Word reads
93
+ const kept = value >= storage.stored[0] && value <= storage.stored[1];
94
+ const reason = kept && storage.why !== undefined ? storage.why : `Word 365 would read it as ${storage.reads(value)}`;
95
+ throw new InvalidArgument(`${where}: ${points} pt is ${value} ${unit} in ${storage.attribute}, outside the range written there `
96
+ + `(${storage.min} to ${storage.max} ${unit}${inPoints}): ${reason} (CR-002 section 65)`);
97
+ }