@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
@@ -0,0 +1,728 @@
1
+ import * as el from '@docx4j/generated-objects-ts/el/org_docx4j_wml';
2
+ import { deepCopy } from '@docx4j/generated-objects-ts';
3
+ import { applyRunOptions, readRunOptions } from '@docx4j/generated-objects-ts/builders/wml';
4
+ import { dropExcluded, linesOf, officeItems, oneParagraphRun } from './officeText.mjs';
5
+ import { assertControlHolds } from './ContentControl.mjs';
6
+ import { createPPr, createParaRPr } from '@docx4j/generated-objects-ts/factory/org_docx4j_wml';
7
+ import { NotSupportedError } from '../../office-js/errors.mjs';
8
+ import { Docx4JException } from '../../opc/exceptions.mjs';
9
+ import { typeNameOf, childrenOf, linkParents, runItemsOf, runOf, paragraphOf, revisionKindOf, objectInSpan, BLOCK_LEVEL_TYPES, ALTERNATE_CONTENT } from './tree.mjs';
10
+ import { joinWithNext, queueNoteRemovals } from './TrackedChange.mjs';
11
+ import { markDeleted, copyRPr, markHolderDeleted } from './tracking.mjs';
12
+ import { storyLayout, storyOf } from './story.mjs';
13
+ import { Font } from './Font.mjs';
14
+ import { GeneralException } from './generalException.mjs';
15
+ /**
16
+ * The extent of a range, refusing one that starts or ends inside a block it doesn't hold whole. Inside a table
17
+ * that is Word's own refusal (check 52: `delete()` and `Replace` on a range from a cell to a paragraph after the
18
+ * table, or from a paragraph into a cell, threw `GeneralException` and changed nothing, tracked or not; D3).
19
+ * Inside a block content control, `crossControls` lets a range end in the control's own paragraphs, one level
20
+ * down, as `delete()` takes one (check 54 cases 18 to 20); anything else there is `NotSupportedError`.
21
+ */
22
+ export function extentOf(span, member, crossControls = false) {
23
+ var _a, _b;
24
+ const story = storyOf(span.paragraph.p).items;
25
+ const up = blocksHolding(span.paragraph.p, story);
26
+ const down = blocksHolding(span.endParagraph.p, story);
27
+ if (((_a = up[up.length - 1]) === null || _a === void 0 ? void 0 : _a.list) !== story || ((_b = down[down.length - 1]) === null || _b === void 0 ? void 0 : _b.list) !== story) {
28
+ throw new Docx4JException("This range's paragraphs are not in the document any more: an edit through another view removed or joined them");
29
+ }
30
+ for (const [i, link] of up.entries()) {
31
+ const j = down.findIndex((other) => other.list === link.list);
32
+ if (j < 0)
33
+ continue;
34
+ const a = link.element;
35
+ const b = down[j].element;
36
+ const extent = { list: link.list, first: link.list.indexOf(a), last: link.list.indexOf(b) };
37
+ // a range starting or ending in a block control's own paragraph, the other end outside the control
38
+ const inside = (k, block) => { var _a; return crossControls && k === 1 && a !== b && CONTROLS.has((_a = typeNameOf(block)) !== null && _a !== void 0 ? _a : ''); };
39
+ if (i > 0 && !startsWhole(span, a)) {
40
+ if (!inside(i, a))
41
+ refusePartial(a, member, 'starts');
42
+ extent.headInside = up[0].list;
43
+ }
44
+ if (j > 0 && !endsWhole(span, b)) {
45
+ if (!inside(j, b) || span.holdsEndMark)
46
+ refusePartial(b, member, 'ends');
47
+ extent.tailInside = down[0].list;
48
+ }
49
+ if (i === 0 || extent.headInside)
50
+ extent.head = span.paragraph;
51
+ if (j === 0 || extent.tailInside)
52
+ extent.tail = span.endParagraph;
53
+ return extent;
54
+ }
55
+ throw new Docx4JException("This range's two ends are not in one story");
56
+ }
57
+ /** The blocks holding a paragraph, innermost first: the paragraph in its list, each block holding that in the list holding it, up to the story's. */
58
+ function blocksHolding(p, story) {
59
+ const out = [];
60
+ let o = p;
61
+ for (let guard = 0; guard < 256; guard++) {
62
+ const parent = o.PARENT;
63
+ if (!parent)
64
+ break;
65
+ const list = childrenOf(parent);
66
+ const element = list === null || list === void 0 ? void 0 : list.find((e) => e.value === o);
67
+ const tn = element ? typeNameOf(element) : undefined;
68
+ if (list && element && tn !== undefined && (BLOCK_LEVEL_TYPES.has(tn) || tn === ALTERNATE_CONTENT)) {
69
+ out.push({ list, element });
70
+ if (list === story)
71
+ break;
72
+ }
73
+ o = parent;
74
+ }
75
+ return out;
76
+ }
77
+ /** The range starts at the start of the block: its first paragraph, as a reader reads it, at offset 0. */
78
+ function startsWhole(span, block) {
79
+ var _a;
80
+ return span.start === 0 && ((_a = storyLayout([block], () => '').paragraphs[0]) === null || _a === void 0 ? void 0 : _a.element.value) === span.paragraph.p;
81
+ }
82
+ /** The range ends at the end of the block: its last paragraph's, as a reader reads it, the mark (or a table's row end) included. */
83
+ function endsWhole(span, block) {
84
+ var _a;
85
+ const paragraphs = storyLayout([block], () => '').paragraphs;
86
+ return span.holdsEndMark && ((_a = paragraphs[paragraphs.length - 1]) === null || _a === void 0 ? void 0 : _a.element.value) === span.endParagraph.p;
87
+ }
88
+ /** The block-level elements a range may end inside, one level down (`extentOf`'s `crossControls`). */
89
+ const CONTROLS = new Set(['org_docx4j_wml.SdtBlock', 'org_docx4j_wml.CTCustomXmlBlock']);
90
+ function refusePartial(block, member, side) {
91
+ if (typeNameOf(block) === 'org_docx4j_wml.Tbl') {
92
+ throw new GeneralException(`Range.${member}: this range ${side} inside a table, which Office JS refuses too (check 52); `
93
+ + 'take the whole table, or edit inside one cell');
94
+ }
95
+ throw new NotSupportedError('Range', member, `this range ${side} inside a content control (or another block) it doesn't hold whole, `
96
+ + "and what Word does with that isn't measured (CR-014 phase 2)");
97
+ }
98
+ // --- delete -------------------------------------------------------------------------------------------
99
+ /**
100
+ * Deletes a range across paragraphs, or one holding its last paragraph's mark, as Word does (check 52, cases 3 to
101
+ * 28; check 54, cases 5 to 20): the start's paragraph loses its text from the start, every block between goes whole
102
+ * (a table too), the end's paragraph loses its text to the end, and the two join in the end paragraph's properties,
103
+ * each run keeping its own formatting. A mark the range holds goes (`dropMark`): its paragraph joins the paragraph
104
+ * after it, in that one's properties (W1, W2; after a whole table the range took, case 5), or, with no paragraph
105
+ * after it, goes itself where its text went with the range (before a table, at a control's end: cases 8 and 12),
106
+ * a cell's last paragraph and a story's last kept (case 10). `keepLastMark` is `Replace`'s, which keeps it.
107
+ *
108
+ * A range ending (or starting) in a block content control's own paragraph, the other end outside it, joins
109
+ * nothing across the control's edge: the texts go, and the start's paragraph takes the end's paragraph properties
110
+ * (cases 18 to 20). A comment whose reference goes with the range goes too (case 27).
111
+ *
112
+ * Tracked, nothing is joined: the text is a `w:del`, every mark in the range is marked deleted but a last one the
113
+ * range doesn't hold, or that can't go (a cell's last), and a table's rows and its cells' marks are deleted, with no
114
+ * `w:pPrChange` (Word's markup in its save after check 52's run). Accepting gives the untracked answer.
115
+ */
116
+ export function deleteSpan(span, extent, keepLastMark) {
117
+ const tracker = span.paragraph.changeTracker;
118
+ return tracker ? deleteTracked(span, extent, tracker, keepLastMark) : deleteUntracked(span, extent, keepLastMark);
119
+ }
120
+ function deleteUntracked(span, extent, keepLastMark) {
121
+ const { list, first, last, head, tail } = extent;
122
+ const takesMark = span.holdsEndMark && !keepLastMark;
123
+ if (head && tail && head.element === tail.element) {
124
+ cut(head, span.start, span.end, { start: span.whole, end: span.whole || span.holdsEndMark });
125
+ if (takesMark)
126
+ dropMark(head);
127
+ return { paragraph: head, offset: span.start };
128
+ }
129
+ // the end first, so that the indexes before it hold
130
+ if (tail) {
131
+ cut(tail, 0, span.end, { start: true, end: span.whole || span.holdsEndMark });
132
+ if (extent.tailInside)
133
+ removeBetween(span.paragraph, extent.tailInside, -1, extent.tailInside.indexOf(tail.element));
134
+ else if (takesMark)
135
+ dropMark(tail);
136
+ }
137
+ else {
138
+ removeBlock(span.paragraph, list, last);
139
+ }
140
+ // the blocks between; a range marker between blocks stays, for the join to bring into the joined paragraph
141
+ removeBetween(span.paragraph, list, first, last);
142
+ if (head) {
143
+ if (extent.headInside)
144
+ removeBetween(span.paragraph, extent.headInside, extent.headInside.indexOf(head.element), extent.headInside.length);
145
+ cut(head, span.start, head.text.length, { start: span.whole, end: true });
146
+ // across a control's edge nothing joins: the start's paragraph takes the end's properties (check 54)
147
+ if (extent.headInside || extent.tailInside)
148
+ takeProperties(head, tail);
149
+ // the head's mark is in the range: it joins what follows, the tail or the paragraph after a block taken whole
150
+ else
151
+ joinForward(head);
152
+ return { paragraph: head, offset: span.start };
153
+ }
154
+ // a range holding one block whole, with no paragraph at either end (a table's whole range), took it as its last
155
+ // above: removed again, the index would take the block after it (docx4j-python's port found it, 2026-10-23)
156
+ if (tail || first !== last)
157
+ removeBlock(span.paragraph, list, first);
158
+ return tail ? { paragraph: tail, offset: 0 } : undefined;
159
+ }
160
+ /** Removes the blocks of a list strictly between two indexes, last first; a range marker stays. */
161
+ function removeBetween(p, list, from, to) {
162
+ for (let i = to - 1; i > from; i--)
163
+ if (!isRangeMarker(list[i]))
164
+ removeBlock(p, list, i);
165
+ }
166
+ /**
167
+ * A paragraph's mark the range took (`deleteUntracked`): the paragraph joins the one after it where there is one
168
+ * (W1, W2); else, its text gone with the range, it goes (check 54: before a table, case 8; a control's last
169
+ * paragraph, case 12), unless it is a cell's last paragraph (case 10, CR-002 section 55), the last in its story or
170
+ * its list's only block, where the mark stays.
171
+ */
172
+ function dropMark(p) {
173
+ if (nextParagraph(p)) {
174
+ join(p);
175
+ return;
176
+ }
177
+ if (p.text !== '' || p.segments({ objects: true }).length > 0 || !removable(p))
178
+ return;
179
+ const list = listOf(p);
180
+ removeBlock(p, list, list.indexOf(p.element));
181
+ }
182
+ /** A paragraph whose mark can go: not a cell's last, not its story's last, not its list's only block. */
183
+ function removable(p) {
184
+ if (p.markText === '\t')
185
+ return false;
186
+ const list = listOf(p);
187
+ const blocks = list.filter((el) => !isRangeMarker(el));
188
+ if (blocks.length <= 1)
189
+ return false;
190
+ return !(list === storyOf(p.p).items && blocks[blocks.length - 1] === p.element);
191
+ }
192
+ /**
193
+ * The start's paragraph across a control's edge from the end's: it takes the end paragraph's properties, its own
194
+ * mark kept in its last run's formatting and its section break kept (check 54 cases 18 and 20: `HEAD18A ` centred
195
+ * as BODY18 was; `BODY20A ` in PLAIN20's properties, its mark italic as its run).
196
+ */
197
+ function takeProperties(head, tail) {
198
+ var _a, _b;
199
+ const sectPr = (_a = head.p.pPr) === null || _a === void 0 ? void 0 : _a.sectPr;
200
+ const pPr = tail.p.pPr ? deepCopy(tail.p.pPr) : createPPr();
201
+ delete pPr.rPr;
202
+ delete pPr.sectPr;
203
+ delete pPr.pPrChange;
204
+ if (sectPr)
205
+ pPr.sectPr = sectPr;
206
+ const runs = head.segments().filter((s) => s.text.length > 0);
207
+ const rPr = copyRPr((_b = runs[runs.length - 1]) === null || _b === void 0 ? void 0 : _b.run.rPr);
208
+ if (rPr)
209
+ pPr.rPr = { ...rPr, TYPE_NAME: 'org_docx4j_wml.ParaRPr' };
210
+ head.p.pPr = pPr;
211
+ linkParents(pPr, head.p);
212
+ }
213
+ /** Marks the blocks of a list strictly between two indexes deleted; a range marker stays as it is. */
214
+ function markBetween(body, list, from, to, tracker) {
215
+ for (const block of list.slice(from + 1, to))
216
+ if (!isRangeMarker(block))
217
+ deleteBlock(body, list, block, tracker);
218
+ }
219
+ /**
220
+ * Removes [from, to) of a paragraph's text, untracked. The notes its references go with are queued to go too
221
+ * (check 52 case 12); a comment whose reference goes goes with it (check 54 case 27), which the paragraph's splice
222
+ * does for every deletion (CR-014 section 12.6).
223
+ */
224
+ function cut(p, from, to, edges) {
225
+ const removed = p.segments({ objects: true })
226
+ .filter((s) => (s.object ? objectInSpan(s.start, from, to, edges) : s.start < to && s.end > from))
227
+ .map((s) => s.item);
228
+ if (to <= from && removed.length === 0)
229
+ return;
230
+ queueNoteRemovals(p.parentBody.package_, removed);
231
+ p.splice(from, to, '', 'before', edges);
232
+ }
233
+ /**
234
+ * Refuses, before anything changes, a tracked deletion of a range holding a whole `w:altChunk` or kept
235
+ * `mc:AlternateContent`: neither has a deleted form, and Accept All would leave it where the untracked deletion
236
+ * takes it, the paragraphs either side unjoined. A content control or custom XML element held whole is deleted as
237
+ * Word deletes one (check 54 cases 15 and 17: `markHolderDeleted`).
238
+ */
239
+ export function assertTrackable(extent, member) {
240
+ var _a, _b, _c;
241
+ for (let i = extent.first; i <= extent.last; i++) {
242
+ if ((i === extent.first && extent.head) || (i === extent.last && extent.tail))
243
+ continue;
244
+ const block = extent.list[i];
245
+ const tn = (_a = typeNameOf(block)) !== null && _a !== void 0 ? _a : '';
246
+ if (tn === 'org_docx4j_wml.P' || tn === 'org_docx4j_wml.Tbl' || CONTROLS.has(tn) || isRangeMarker(block))
247
+ continue;
248
+ throw new NotSupportedError('Range', member, `this range holds a whole ${(_c = (_b = block.name) === null || _b === void 0 ? void 0 : _b.localPart) !== null && _c !== void 0 ? _c : tn}, and there is no way to delete one `
249
+ + 'under change tracking (CR-014 phase 2); delete it untracked, or the paragraphs either side of it');
250
+ }
251
+ }
252
+ function deleteTracked(span, extent, tracker, keepLastMark) {
253
+ const { list, first, last, head, tail } = extent;
254
+ const takesMark = span.holdsEndMark && !keepLastMark;
255
+ const body = span.paragraph.parentBody;
256
+ if (head && tail && head.element === tail.element) {
257
+ head.splice(span.start, span.end, '', 'before', { start: span.whole, end: span.whole || span.holdsEndMark });
258
+ if (takesMark)
259
+ deleteMark(head, tracker);
260
+ return { paragraph: head, offset: span.start };
261
+ }
262
+ // the blocks as they stand now: a control's deletion puts markers into the list, which moves the indexes
263
+ const firstBlock = list[first];
264
+ const lastBlock = list[last];
265
+ const between = list.slice(first + 1, last);
266
+ if (head) {
267
+ head.splice(span.start, head.text.length, '', 'before', { start: span.whole, end: true });
268
+ deleteMark(head, tracker);
269
+ if (extent.headInside)
270
+ markBetween(body, extent.headInside, extent.headInside.indexOf(head.element), extent.headInside.length, tracker);
271
+ }
272
+ else {
273
+ deleteBlock(body, list, firstBlock, tracker);
274
+ }
275
+ for (const block of between)
276
+ if (!isRangeMarker(block))
277
+ deleteBlock(body, list, block, tracker);
278
+ if (tail) {
279
+ if (extent.tailInside)
280
+ markBetween(body, extent.tailInside, -1, extent.tailInside.indexOf(tail.element), tracker);
281
+ tail.splice(0, span.end, '', 'before', { start: true, end: span.whole || span.holdsEndMark });
282
+ if (takesMark)
283
+ deleteMark(tail, tracker);
284
+ }
285
+ else if (head || lastBlock !== firstBlock) {
286
+ // one block held whole was deleted above as the first, and isn't again (a control would take its markers twice)
287
+ deleteBlock(body, list, lastBlock, tracker);
288
+ }
289
+ return head ? { paragraph: head, offset: span.start } : tail ? { paragraph: tail, offset: 0 } : undefined;
290
+ }
291
+ /** A block the range holds whole, deleted under tracking: a control with Word's markers around it, its content within. */
292
+ function deleteBlock(body, list, block, tracker) {
293
+ var _a;
294
+ if (CONTROLS.has((_a = typeNameOf(block)) !== null && _a !== void 0 ? _a : ''))
295
+ markHolderDeleted(tracker, block, list, () => markBlockDeleted(body, block, tracker));
296
+ else
297
+ markBlockDeleted(body, block, tracker);
298
+ }
299
+ /** Removes a block, untracked: its notes and the relationships it held go on save where nothing else holds them. */
300
+ function removeBlock(p, list, index) {
301
+ const [removed] = list.splice(index, 1);
302
+ if (!removed)
303
+ return;
304
+ queueNoteRemovals(p.parentBody.package_, removed);
305
+ p.releaseRelationships(removed);
306
+ }
307
+ /** The paragraph after `p` in its list, past range markers, joined into it where there is one: its content and properties. */
308
+ function joinForward(p) {
309
+ if (nextParagraph(p))
310
+ join(p);
311
+ }
312
+ /**
313
+ * `p` takes the next paragraph's content and properties, which is Word's join (`joinWithNext`, the accept side's).
314
+ * A section break in `p`'s mark goes with the mark, and the relationships its headers and footers held with it.
315
+ */
316
+ function join(p) {
317
+ var _a;
318
+ const sectPr = (_a = p.p.pPr) === null || _a === void 0 ? void 0 : _a.sectPr;
319
+ if (sectPr)
320
+ p.releaseRelationships(sectPr);
321
+ joinWithNext(p);
322
+ }
323
+ function listOf(p) {
324
+ var _a;
325
+ const list = childrenOf(((_a = p.p.PARENT) !== null && _a !== void 0 ? _a : {}));
326
+ return (list === null || list === void 0 ? void 0 : list.includes(p.element)) ? list : p.container;
327
+ }
328
+ /**
329
+ * A paragraph's mark marked deleted, unless it is already, or can't go: a cell's last paragraph keeps its mark, and
330
+ * Word deleted its text alone (check 54 case 11, as CR-002 section 55's cells), and so does a story's last.
331
+ */
332
+ function deleteMark(p, tracker) {
333
+ if (markDeleted(p.p) || p.markText === '\t' || isStoryLast(p))
334
+ return;
335
+ tracker.markParagraphDeleted(p.p);
336
+ }
337
+ function isStoryLast(p) {
338
+ const list = listOf(p);
339
+ if (list !== storyOf(p.p).items)
340
+ return false;
341
+ const blocks = list.filter((el) => !isRangeMarker(el));
342
+ return blocks[blocks.length - 1] === p.element;
343
+ }
344
+ /** The paragraph right after `p` in its list, past range markers, if there is one. */
345
+ function nextParagraph(p) {
346
+ const list = listOf(p);
347
+ let j = list.indexOf(p.element) + 1;
348
+ while (j < list.length && isRangeMarker(list[j]))
349
+ j++;
350
+ return j < list.length && typeNameOf(list[j]) === 'org_docx4j_wml.P';
351
+ }
352
+ /** Everything in a block marked deleted in place: each paragraph's text and mark, each row of its tables (check 52 case 21). */
353
+ function markBlockDeleted(body, block, tracker) {
354
+ body.sub({ content: [block] }).markContentDeleted(tracker);
355
+ }
356
+ /** A marker that stands between two paragraphs without keeping them apart (as `joinWithNext` reads one). */
357
+ function isRangeMarker(el) {
358
+ var _a, _b;
359
+ const name = (_b = (_a = el.name) === null || _a === void 0 ? void 0 : _a.localPart) !== null && _b !== void 0 ? _b : '';
360
+ return name.endsWith('RangeStart') || name.endsWith('RangeEnd') || name === 'bookmarkStart' || name === 'bookmarkEnd'
361
+ || name === 'permStart' || name === 'permEnd' || name === 'proofErr';
362
+ }
363
+ /** The character at a range's start: its run's properties, or its paragraph's mark's where the range starts at the mark. */
364
+ export function firstCharacterOf(span) {
365
+ const p = span.paragraph;
366
+ const seg = p.segments().find((s) => s.start <= span.start && span.start < s.end);
367
+ return { rPr: seg ? seg.run.rPr : markRPrOf(p.p), pPr: p.p.pPr, p: p.p };
368
+ }
369
+ /**
370
+ * The run properties that give text in `into` the look the first replaced character had (check 52, cases 29 to
371
+ * 38): its run's own properties, and each property its paragraph's style gave it that `into`'s doesn't, written
372
+ * direct - a heading's font, size, colour and bold in a body paragraph (case 29), and a body paragraph's
373
+ * `w:b w:val="0"` and automatic colour in a heading (case 30). A property no style sets anywhere and `into`'s does
374
+ * is written as its neutral value where it has one (`NEUTRAL`).
375
+ */
376
+ export function lookOf(first, into) {
377
+ var _a, _b;
378
+ const direct = copyRPr(first.rPr);
379
+ const resolver = (_a = into.parentBody.package_) === null || _a === void 0 ? void 0 : _a.propertyResolverOrUndefined;
380
+ // the same paragraph properties (a range within one paragraph): its own properties give it its look
381
+ if (!resolver || first.pPr === into.p.pPr)
382
+ return direct;
383
+ const was = resolver.getEffectiveRPr(first.rPr, first.pPr, resolver.cellContextOf(first.p));
384
+ const now = resolver.getEffectiveRPr(first.rPr, into.p.pPr, resolver.cellContextOf(into.p));
385
+ const out = (direct !== null && direct !== void 0 ? direct : { TYPE_NAME: 'org_docx4j_wml.RPr' });
386
+ for (const key of new Set([...Object.keys(was), ...Object.keys(now)])) {
387
+ if (SKIPPED.has(key) || same(was[key], now[key]))
388
+ continue;
389
+ const value = was[key] !== undefined ? deepCopy(was[key]) : (_b = NEUTRAL[key]) === null || _b === void 0 ? void 0 : _b.call(NEUTRAL);
390
+ if (value !== undefined)
391
+ out[key] = value;
392
+ }
393
+ const rPr = out;
394
+ return Object.keys(rPr).some((key) => key !== 'TYPE_NAME') ? rPr : undefined;
395
+ }
396
+ const SKIPPED = new Set(['TYPE_NAME', 'PARENT', 'rPrChange', 'rStyle']);
397
+ /** What a property reads as where nothing sets it, for the properties that have one. */
398
+ const NEUTRAL = {
399
+ ...Object.fromEntries(['b', 'bCs', 'i', 'iCs', 'caps', 'smallCaps', 'strike', 'dstrike', 'outline', 'shadow', 'emboss', 'imprint', 'vanish', 'webHidden']
400
+ .map((key) => [key, () => ({ val: false })])),
401
+ color: () => ({ val: 'auto' }),
402
+ u: () => ({ val: 'none' }),
403
+ highlight: () => ({ val: 'none' }),
404
+ vertAlign: () => ({ val: 'baseline' }),
405
+ sz: () => ({ val: 20 }),
406
+ szCs: () => ({ val: 20 }),
407
+ };
408
+ function same(a, b) {
409
+ return JSON.stringify(a, (key, v) => (key === 'PARENT' || key === 'TYPE_NAME' ? undefined : v)) === JSON.stringify(b, (key, v) => (key === 'PARENT' || key === 'TYPE_NAME' ? undefined : v));
410
+ }
411
+ /** The text of an untracked `Replace`, put where the deletion closed up, in the first replaced character's look. */
412
+ export function placeReplacement(landing, first, text) {
413
+ // Office JS's characters written back (CR-002 section 60): the excluded ones dropped, a line break a paragraph break
414
+ const lines = linesOf(dropExcluded(text));
415
+ const joined = lines.join('');
416
+ const p = landing.paragraph;
417
+ if (joined !== '')
418
+ p.insertItemsAt(landing.offset, [runOf(officeItems(joined), lookOf(first, p))]);
419
+ if (lines.length === 1)
420
+ return { first: p, last: p, start: landing.offset, end: landing.offset + joined.length };
421
+ const offsets = [];
422
+ let at = landing.offset;
423
+ for (const line of lines.slice(0, -1))
424
+ offsets.push(at += line.length);
425
+ const broken = breakParagraphAt(p, offsets);
426
+ return { ...broken, start: landing.offset, end: lines[lines.length - 1].length };
427
+ }
428
+ /** A paragraph mark's formatting as run properties: the mark's own revisions left out. */
429
+ function markRPrOf(p) {
430
+ var _a;
431
+ const rPr = (_a = p.pPr) === null || _a === void 0 ? void 0 : _a.rPr;
432
+ if (!rPr)
433
+ return undefined;
434
+ const { ins: _ins, del: _del, moveFrom: _from, moveTo: _to, rPrChange: _change, ...rest } = rPr;
435
+ return Object.keys(rest).some((key) => key !== 'TYPE_NAME') ? { ...rest, TYPE_NAME: 'org_docx4j_wml.RPr' } : undefined;
436
+ }
437
+ // --- insertParagraph ------------------------------------------------------------------------------------
438
+ /**
439
+ * A paragraph of `text` put at `offset` in `p`, as Word puts one where a range across paragraphs starts
440
+ * ('Before') or ends ('After') (check 52, cases 57 to 70): at the paragraph's start it goes before it and at its
441
+ * end after it, in its properties (`Paragraph.insertParagraph`); inside it `p` is split there (`splitParagraph`)
442
+ * and the new paragraph goes between the halves, its mark and its text in the formatting of the run before the
443
+ * split (case 63's italic). Tracked, the new paragraph and the first half's mark are inserted (cases 69 and 70).
444
+ * Returns the new paragraph, and the first half where `p` was split.
445
+ */
446
+ export function insertParagraphAt(p, offset, text) {
447
+ var _a;
448
+ if (offset <= 0)
449
+ return { inserted: p.insertParagraph(text, 'Before') };
450
+ if (offset >= p.text.length)
451
+ return { inserted: p.insertParagraph(text, 'After') };
452
+ const rPr = rPrBefore(p, offset);
453
+ const firstHalf = splitParagraph(p, offset, rPr);
454
+ const pPr = propertiesOf(p, rPr);
455
+ const run = oneParagraphRun(text, copyRPr(rPr)); // CR-002 section 60 E2
456
+ const el = paragraphOf(((_a = run.value.content) === null || _a === void 0 ? void 0 : _a.length) ? [run] : [], pPr);
457
+ return { inserted: p.parentBody.insertElement(el, 'Before', p), firstHalf };
458
+ }
459
+ /** The run properties of the text just before `offset`, which a split there gives its new marks (check 52 case 63). */
460
+ function rPrBefore(p, offset) {
461
+ var _a;
462
+ const before = p.segments().filter((s) => s.start < offset && s.text.length > 0);
463
+ return copyRPr((_a = before[before.length - 1]) === null || _a === void 0 ? void 0 : _a.run.rPr);
464
+ }
465
+ /** A copy of `p`'s properties for a new paragraph: no section break, no tracked change, the mark in `rPr`'s formatting. */
466
+ function propertiesOf(p, rPr) {
467
+ const pPr = p.p.pPr ? deepCopy(p.p.pPr) : undefined;
468
+ if (pPr) {
469
+ delete pPr.rPr;
470
+ delete pPr.sectPr;
471
+ delete pPr.pPrChange;
472
+ }
473
+ if (!rPr)
474
+ return pPr;
475
+ const out = pPr !== null && pPr !== void 0 ? pPr : createPPr();
476
+ out.rPr = { ...deepCopy(rPr), TYPE_NAME: 'org_docx4j_wml.ParaRPr' };
477
+ linkParents(out.rPr, out);
478
+ return out;
479
+ }
480
+ /**
481
+ * Paragraph breaks put into `p` at `offsets` (ascending, in its text), as Word's insertText writes a line feed or
482
+ * carriage return (CR-002 section 60 E1, check 55): at a split (`splitParagraph`), in the formatting of the run before
483
+ * it; at the paragraph's end an empty paragraph after it, at its start one before it, in its properties. Tracked, each
484
+ * new mark is inserted. Returns the first paragraph and the last, which holds what followed the last break.
485
+ */
486
+ export function breakParagraphAt(p, offsets) {
487
+ let tail = p;
488
+ let consumed = 0;
489
+ let first;
490
+ for (const offset of offsets) {
491
+ const at = offset - consumed;
492
+ let head;
493
+ const before = [...tail.segments()].reverse().find((s) => s.end <= at);
494
+ if (at <= 0)
495
+ head = tail.insertParagraph('', 'Before');
496
+ else if (at >= tail.text.length)
497
+ head = splitAtEnd(tail, before === null || before === void 0 ? void 0 : before.run.rPr);
498
+ else
499
+ head = splitParagraph(tail, at, before === null || before === void 0 ? void 0 : before.run.rPr);
500
+ first !== null && first !== void 0 ? first : (first = head);
501
+ consumed = offset;
502
+ }
503
+ return { first: first !== null && first !== void 0 ? first : p, last: tail };
504
+ }
505
+ /**
506
+ * Splits `p` at `offset`, inside its text: what is before it becomes a new paragraph in front of `p`, in a copy of
507
+ * its properties with a mark of its own in `rPr`'s formatting, and `p` keeps the rest with its own mark, its
508
+ * section break and its identity, as Word keeps them (check 52 cases 57 and 63). A link, a revision or a smart tag
509
+ * the offset is inside is split into two of the same; a complex field's result is split, the field running across the
510
+ * two, and a `w:fldSimple` is made a complex field first, as Word makes it (check 57, CR-002 section 62.2 D1); a content
511
+ * control or a kept `mc:AlternateContent` there is refused, since its two halves would be two of it. Tracked, the new
512
+ * mark is inserted, as Word marks the first half's (cases 69 and 70). Returns the first half.
513
+ */
514
+ export function splitParagraph(p, offset, rPr) {
515
+ var _a, _b, _c, _d;
516
+ var _e;
517
+ p.assertWritable();
518
+ // a bound block control holds one paragraph (CR-002 section 61): a split there is refused, Word not measured on one
519
+ assertControlHolds(p.parentBody, (_a = p.p.PARENT) !== null && _a !== void 0 ? _a : p.parentBody.container, listOf(p), [p.element], undefined, ['Range', 'insertParagraph']);
520
+ const length = p.text.length;
521
+ // the character at the split: the holders above it must be ones that can be cut in two
522
+ const seg = p.segments().find((s) => s.start <= offset && offset < s.end);
523
+ if (!seg || offset <= 0 || offset >= length)
524
+ throw new Docx4JException(`A paragraph splits inside its text, not at ${offset} of ${length}`);
525
+ if (seg.alternate || seg.math)
526
+ throw new NotSupportedError('Range', 'insertParagraph', 'the split falls inside an equation or an mc:AlternateContent');
527
+ // every check before anything changes
528
+ for (let o = seg.run.PARENT; o && o !== p.p; o = o.PARENT) {
529
+ const tn = (_b = o.TYPE_NAME) !== null && _b !== void 0 ? _b : '';
530
+ if (!BREAKABLE.has(tn))
531
+ throw new NotSupportedError('Range', 'insertParagraph', `the split falls inside a ${tn.replace('org_docx4j_wml.', '')}, which can't be cut in two`);
532
+ }
533
+ unfoldSimpleFieldsAround(seg.run, p.p);
534
+ p.splitRunsAround(offset, length);
535
+ const at = p.segments().find((s) => s.start >= offset && s.text.length > 0);
536
+ let owner = at.runOwner;
537
+ let index = at.runIndex;
538
+ const content = ((_c = (_e = p.p).content) !== null && _c !== void 0 ? _c : (_e.content = []));
539
+ while (owner !== content) {
540
+ const holder = owner[index].value.PARENT;
541
+ const parent = holder === null || holder === void 0 ? void 0 : holder.PARENT;
542
+ const outer = parent ? runItemsOf(parent) : undefined;
543
+ const element = outer === null || outer === void 0 ? void 0 : outer.find((e) => e.value === holder);
544
+ const key = holder ? Object.keys(holder).find((k) => holder[k] === owner) : undefined;
545
+ if (!holder || !outer || !element || !key)
546
+ throw new Docx4JException('The split could not find the run holder it is inside');
547
+ let next = outer.indexOf(element);
548
+ if (index > 0) {
549
+ const copy = {};
550
+ for (const [k, v] of Object.entries(holder))
551
+ if (k !== key && k !== 'PARENT')
552
+ copy[k] = v;
553
+ if (revisionKindOf(element) !== undefined)
554
+ copy.id = p.nextAnnotationId();
555
+ copy[key] = owner.splice(index);
556
+ linkParents(copy[key], copy);
557
+ outer.splice(next + 1, 0, { name: element.name, value: copy });
558
+ linkParents(outer[next + 1], parent);
559
+ }
560
+ next += index > 0 ? 1 : 0;
561
+ owner = outer;
562
+ index = next;
563
+ }
564
+ const head = content.splice(0, index);
565
+ const el = paragraphOf(head, propertiesOf(p, rPr));
566
+ linkParents(head, el.value);
567
+ const list = listOf(p);
568
+ list.splice(list.indexOf(p.element), 0, el);
569
+ linkParents(el, p.p.PARENT);
570
+ p.parentBody.assignParaId(el.value);
571
+ (_d = p.changeTracker) === null || _d === void 0 ? void 0 : _d.markParagraphInserted(el.value);
572
+ return p.viewOf(el, list);
573
+ }
574
+ /**
575
+ * A paragraph break at the end of `p`'s text, as a paragraph mark makes one (CR-002 section 60 E1): what `p` holds goes
576
+ * into a new paragraph in front of it, in a copy of its properties with a mark in `rPr`'s formatting, and `p` keeps its
577
+ * own mark, its section break and its identity, empty, as `splitParagraph` keeps them. Tracked, the new mark is
578
+ * inserted. Returns the new paragraph.
579
+ */
580
+ function splitAtEnd(p, rPr) {
581
+ var _a, _b, _c;
582
+ var _d;
583
+ p.assertWritable();
584
+ assertControlHolds(p.parentBody, (_a = p.p.PARENT) !== null && _a !== void 0 ? _a : p.parentBody.container, listOf(p), [p.element], undefined, ['Range', 'insertParagraph']);
585
+ const content = ((_b = (_d = p.p).content) !== null && _b !== void 0 ? _b : (_d.content = []));
586
+ const head = content.splice(0, content.length);
587
+ const el = paragraphOf(head, propertiesOf(p, rPr));
588
+ linkParents(head, el.value);
589
+ const list = listOf(p);
590
+ list.splice(list.indexOf(p.element), 0, el);
591
+ linkParents(el, p.p.PARENT);
592
+ p.parentBody.assignParaId(el.value);
593
+ (_c = p.changeTracker) === null || _c === void 0 ? void 0 : _c.markParagraphInserted(el.value);
594
+ return p.viewOf(el, list);
595
+ }
596
+ /**
597
+ * Word's form of a table put after the paragraph a table cell ends with (check 52, cases 86 and 87; CR-002 section 55.6):
598
+ * that paragraph is split at its end, as a paragraph break there splits it (`splitAtEnd`), the table between the halves,
599
+ * so the cell's mark stays last. The paragraph keeps its text and becomes the first half, whose new mark takes the
600
+ * formatting of the text before it and, tracked, is the inserted one; its properties as they were go after the table, in
601
+ * an empty paragraph, unmarked. The paragraph keeps its text, not its mark, so that a view of it (a `Range`) still holds it.
602
+ */
603
+ export function keepCellMarkLast(p, cellContent) {
604
+ var _a;
605
+ p.assertWritable();
606
+ const mark = paragraphOf([], p.p.pPr ? deepCopy(p.p.pPr) : undefined);
607
+ const pPr = propertiesOf(p, rPrBefore(p, p.text.length));
608
+ if (pPr) {
609
+ p.p.pPr = pPr;
610
+ linkParents(pPr, p.p);
611
+ }
612
+ else
613
+ delete p.p.pPr;
614
+ cellContent.push(mark);
615
+ linkParents(mark, p.p.PARENT);
616
+ p.parentBody.assignParaId(mark.value);
617
+ (_a = p.changeTracker) === null || _a === void 0 ? void 0 : _a.markParagraphInserted(p.p);
618
+ }
619
+ /** The run holders a paragraph split may cut in two: each half is a whole one of the same. */
620
+ export const SPLITTABLE = new Set(['org_docx4j_wml.P.Hyperlink', 'org_docx4j_wml.RunIns', 'org_docx4j_wml.RunDel', 'org_docx4j_wml.RunTrackChange',
621
+ 'org_docx4j_wml.CTSmartTagRun', 'org_docx4j_wml.CTCustomXmlRun', 'org_docx4j_wml.P.Dir', 'org_docx4j_wml.P.Bdo']);
622
+ const SIMPLE_FIELD = 'org_docx4j_wml.CTSimpleField';
623
+ /** The run holders a paragraph break may be made inside: those it cuts in two, and a `w:fldSimple`, made a complex field first (D1). */
624
+ export const BREAKABLE = new Set([...SPLITTABLE, SIMPLE_FIELD]);
625
+ /**
626
+ * Every `w:fldSimple` a run is inside, below the paragraph, made a complex field, the innermost first: what Word does
627
+ * before a paragraph break inside a simple field's result (check 57 cases 2 and 4, CR-002 section 62.2 D1). The caller
628
+ * has checked that every other holder there can be cut in two.
629
+ */
630
+ export function unfoldSimpleFieldsAround(run, p) {
631
+ for (;;) {
632
+ let field;
633
+ for (let o = run.PARENT; o && o !== p; o = o.PARENT) {
634
+ if (o.TYPE_NAME === SIMPLE_FIELD) {
635
+ field = o;
636
+ break;
637
+ }
638
+ }
639
+ if (!field)
640
+ return;
641
+ unfoldSimpleField(field);
642
+ }
643
+ }
644
+ /**
645
+ * A `w:fldSimple` written in its place as the complex field Word wrote for check 57's: a run holding the begin
646
+ * `w:fldChar` (the field's `w:fldLock`, `w:dirty` and `w:fldData` on it), a run holding its instruction as a
647
+ * `w:instrText`, a run holding the separator, the field's content, and a run holding the end. The field characters'
648
+ * runs take a copy of the result's first run's properties; Word's result run had none, and one with some isn't measured.
649
+ */
650
+ function unfoldSimpleField(field) {
651
+ var _a, _b, _c;
652
+ const parent = field.PARENT;
653
+ const list = parent ? runItemsOf(parent) : undefined;
654
+ const element = list === null || list === void 0 ? void 0 : list.find((e) => e.value === field);
655
+ if (!parent || !list || !element)
656
+ throw new Docx4JException('The w:fldSimple could not be found in the list that holds it');
657
+ const content = ((_a = field.content) !== null && _a !== void 0 ? _a : []);
658
+ const rPr = (_c = (_b = content.find((e) => typeNameOf(e) === 'org_docx4j_wml.R')) === null || _b === void 0 ? void 0 : _b.value) === null || _c === void 0 ? void 0 : _c.rPr;
659
+ const begin = { TYPE_NAME: 'org_docx4j_wml.FldChar', fldCharType: 'begin' };
660
+ if (field.fldLock !== undefined)
661
+ begin.fldLock = field.fldLock;
662
+ if (field.dirty !== undefined)
663
+ begin.dirty = field.dirty;
664
+ if (field.fldData)
665
+ begin.fldData = field.fldData;
666
+ const instr = { TYPE_NAME: 'org_docx4j_wml.Text', value: field.instr };
667
+ if (/^\s|\s$/.test(field.instr))
668
+ instr.space = 'preserve';
669
+ const runFor = (item) => runOf([item], copyRPr(rPr));
670
+ const pieces = [
671
+ runFor(el.fldChar(begin)),
672
+ runFor(el.instrText(instr)),
673
+ runFor(el.fldChar({ TYPE_NAME: 'org_docx4j_wml.FldChar', fldCharType: 'separate' })),
674
+ ...content.splice(0),
675
+ runFor(el.fldChar({ TYPE_NAME: 'org_docx4j_wml.FldChar', fldCharType: 'end' })),
676
+ ];
677
+ list.splice(list.indexOf(element), 1, ...pieces);
678
+ linkParents(pieces, parent);
679
+ }
680
+ // --- font ---------------------------------------------------------------------------------------------
681
+ /**
682
+ * True where the effective properties already read every option as given, so that nothing is written: Word wrote
683
+ * `w:b` on no heading run (check 52), `w:sz` on none of a 14-point heading's (check 54 case 23), `w:i` on no run
684
+ * italic through its style (case 21). A colour or a name compares case-insensitively.
685
+ */
686
+ function reads(rPr, opts) {
687
+ const read = readRunOptions(rPr);
688
+ const same = (a, b) => (typeof a === 'string' && typeof b === 'string' ? a.toLowerCase() === b.toLowerCase() : a === b);
689
+ return Object.entries(opts).every(([key, value]) => same(read[key], value));
690
+ }
691
+ /**
692
+ * A `font` write over a range across paragraphs (check 52, cases 49 to 56): every run in the range, a table's
693
+ * cells' too, split at the range's ends, and each mark the range holds, as Word formats them. A property goes only
694
+ * where a run or mark doesn't read the value already (`reads`): Word wrote `w:b` on none of a heading's runs, bold
695
+ * through its style, and tracked no change there; nor `w:sz` on a heading's 14-point runs (check 54). Tracked, each
696
+ * run and mark changed has its `w:rPrChange` (case 56: the body paragraph's mark's).
697
+ */
698
+ export function formatSpan(pieces, opts) {
699
+ var _a;
700
+ for (const piece of pieces) {
701
+ const p = piece.paragraph;
702
+ const resolver = (_a = p.parentBody.package_) === null || _a === void 0 ? void 0 : _a.propertyResolverOrUndefined;
703
+ const cell = resolver === null || resolver === void 0 ? void 0 : resolver.cellContextOf(p.p);
704
+ if (piece.start < piece.end)
705
+ p.splitRunsAround(piece.start, piece.end);
706
+ const runs = piece.runs().filter((run) => !reads(resolver ? resolver.getEffectiveRPr(run.value.rPr, p.p.pPr, cell) : run.value.rPr, opts));
707
+ if (runs.length > 0)
708
+ Object.assign(new Font(() => runs.map((run) => run.value), () => p.fontTracking()), opts);
709
+ if (piece.holdsMark && !reads(resolver ? resolver.getEffectiveParagraphMarkRPr(p.p.pPr, cell) : markRPrOf(p.p), opts))
710
+ formatMark(p, opts);
711
+ }
712
+ }
713
+ /** A paragraph mark's direct formatting written; tracked, its `w:rPrChange` first, unless the mark is this author's insertion. */
714
+ function formatMark(p, opts) {
715
+ var _a, _b, _c;
716
+ var _d;
717
+ p.assertWritable();
718
+ const pPr = ((_a = (_d = p.p).pPr) !== null && _a !== void 0 ? _a : (_d.pPr = createPPr()));
719
+ linkParents(pPr, p.p);
720
+ const rPr = ((_b = pPr.rPr) !== null && _b !== void 0 ? _b : (pPr.rPr = createParaRPr()));
721
+ linkParents(rPr, pPr);
722
+ const tracker = p.changeTracker;
723
+ if (tracker && ((_c = rPr.ins) === null || _c === void 0 ? void 0 : _c.author) !== tracker.author)
724
+ tracker.recordMarkRPrChange(rPr);
725
+ applyRunOptions(rPr, opts);
726
+ if (tracker)
727
+ tracker.settleMarkRPrChange(p.p);
728
+ }