@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,911 @@
1
+ import { log } from '../properties/log.mjs';
2
+ import { LinkTargets } from './LinkTargets.mjs';
3
+ import { MdDocument, MdParagraph, MdHeading, MdBlockQuote, MdBulletList, MdOrderedList, MdListItem, MdFencedCodeBlock, MdThematicBreak, MdHtmlInline, MdText, MdCode, MdEmphasis, MdStrongEmphasis, MdLink, MdImage, MdHardLineBreak, MdTableBlock, MdTableHead, MdTableBody, MdTableRow, MdTableCell, MdStrikethrough, MdFootnoteDefinition, MdFootnoteReference, MdInlineMath, MdDisplayMath, } from './commonmark.mjs';
4
+ const TWIPS_PER_LEVEL = 720;
5
+ /** The paragraph styles docx4j's importer writes, which the export reads back (docx4j `ImportStyles`). */
6
+ const QUOTE = 'Quote';
7
+ const INTENSE_QUOTE = 'IntenseQuote';
8
+ const LIST_PARAGRAPH = 'ListParagraph';
9
+ const SOURCE_CODE = 'SourceCode';
10
+ /** Paragraph styles which are code blocks by name, whatever their font. */
11
+ const CODE_STYLES = new Set([SOURCE_CODE, 'Code', 'HTMLPreformatted', 'MacroText']);
12
+ /**
13
+ * Writes the text model's block model (`TextResult`) as a Markdown tree (WD REQ-703): a heading from its
14
+ * outline level, not its style's name; a list from its numbering; bold, italic, struck-through and code text
15
+ * from what a run adds to its paragraph's style (so a heading's inherent bold doesn't become `**markers**`);
16
+ * links from a `w:hyperlink` or a HYPERLINK field; images with the describer's text; notes as `[^n]`; in the
17
+ * `markup` view, CriticMarkup (`{++ ++}`, `{-- --}`, `{>> <<}`). A table of contents is the text model's to
18
+ * leave out (`toc: false`, the default). Each node made for a block carries it as its `tag`, so that
19
+ * `MarkdownExporter.blocks` can say where each block's Markdown begins. docx4j `WmlToMarkdown`.
20
+ */
21
+ export class WmlToMarkdown {
22
+ constructor(result,
23
+ /** For a style's own indent (a block quote's depth); undefined where there are no styles to ask. */
24
+ resolver,
25
+ /** The schemes a link may use beyond `LinkTargets`' defaults (`MarkdownExportOptions.linkSchemes`). */
26
+ linkSchemes = []) {
27
+ this.result = result;
28
+ this.resolver = resolver;
29
+ this.linkSchemes = linkSchemes;
30
+ /** The open lists, the deepest last. */
31
+ this.openLists = [];
32
+ /** The count each list level last showed (`numId/ilvl`), for a parent item reopened after an interruption. */
33
+ this.lastValues = new Map();
34
+ this.openQuoteDepth = 0;
35
+ /** Above 0 while building a GFM header row, whose bold is convention, not markup. */
36
+ this.tableHeaderDepth = 0;
37
+ this.styleIndents = new Map();
38
+ /** The images, in the order written, for their destinations. */
39
+ this.images = [];
40
+ /** Whether a link target was refused (`LinkTargets`): logged once per document. */
41
+ this.linkRefused = false;
42
+ this.textOptions = result.options;
43
+ }
44
+ convert() {
45
+ const document = new MdDocument();
46
+ const blocks = this.result.blocks;
47
+ for (let i = 0; i < blocks.length; i++) {
48
+ const block = blocks[i];
49
+ if (isNote(block)) {
50
+ // a note's blocks are its definition, at the place the text model reads it (after the main story)
51
+ this.closeLists();
52
+ this.closeQuote();
53
+ let last = i;
54
+ while (last + 1 < blocks.length && sameNote(block, blocks[last + 1]))
55
+ last++;
56
+ const definition = tagged(new MdFootnoteDefinition(noteLabel(block.noteType, block.noteNumber, block.noteId)), block);
57
+ this.body(blocks.slice(i, last + 1), definition);
58
+ this.closeLists();
59
+ this.closeQuote();
60
+ trimLeadingMarkerSpace(definition);
61
+ document.appendChild(definition);
62
+ i = last;
63
+ }
64
+ else {
65
+ i = this.block(blocks, i, document);
66
+ }
67
+ }
68
+ this.closeLists();
69
+ this.closeQuote();
70
+ return document;
71
+ }
72
+ // ---------------------------------------------------------------- blocks
73
+ body(blocks, container) {
74
+ for (let i = 0; i < blocks.length; i++)
75
+ i = this.block(blocks, i, container);
76
+ }
77
+ /** Returns the index of the last block consumed (a code block spans several). */
78
+ block(blocks, index, container) {
79
+ const block = blocks[index];
80
+ switch (block.kind) {
81
+ case 'TABLE':
82
+ this.closeLists();
83
+ this.closeQuote();
84
+ this.table(block, container);
85
+ return index;
86
+ case 'CHART':
87
+ this.closeLists();
88
+ this.closeQuote();
89
+ this.chart(block, container);
90
+ return index;
91
+ default:
92
+ return this.paragraph(blocks, index, container);
93
+ }
94
+ }
95
+ paragraph(blocks, index, container) {
96
+ const block = blocks[index];
97
+ const style = block.style;
98
+ // a thematic break: an empty paragraph carrying a bottom border of its own
99
+ if (block.bottomBorder && isEmpty(block)) {
100
+ this.closeLists();
101
+ this.closeQuote();
102
+ container.appendChild(tagged(new MdThematicBreak('---'), block)); // else the renderer picks its own idiom
103
+ return index;
104
+ }
105
+ // display math: a paragraph which is one m:oMathPara
106
+ const displayMath = soleDisplayMath(block);
107
+ if (displayMath !== undefined) {
108
+ this.closeLists();
109
+ this.closeQuote();
110
+ container.appendChild(tagged(new MdDisplayMath(latex(displayMath)), block));
111
+ return index;
112
+ }
113
+ // a code block: a code-styled paragraph, or one whose paragraph style's own font is mono; consecutive ones
114
+ // merged into one fence, one line each
115
+ if (isCodeParagraph(block)) {
116
+ this.closeLists();
117
+ this.closeQuote();
118
+ let literal = '';
119
+ let last = index;
120
+ for (let j = index; j < blocks.length; j++) {
121
+ const b = blocks[j];
122
+ if (b.kind !== 'PARAGRAPH' || !isCodeParagraph(b) || !sameStory(block, b))
123
+ break;
124
+ literal += `${this.codeText(b)}\n`;
125
+ last = j;
126
+ }
127
+ container.appendChild(tagged(new MdFencedCodeBlock(literal), block));
128
+ return last;
129
+ }
130
+ // a heading by its effective outlineLvl (0 to 5 are # to ######); before the numbering, since the built-in
131
+ // Heading styles can carry a legacy numPr
132
+ const outlineLevel = block.outlineLevel;
133
+ if (outlineLevel !== undefined && outlineLevel <= 5) {
134
+ this.closeLists();
135
+ this.closeQuote();
136
+ const heading = tagged(new MdHeading(outlineLevel + 1), block);
137
+ this.inlines(block, heading, true);
138
+ container.appendChild(heading);
139
+ return index;
140
+ }
141
+ // a list paragraph
142
+ const numbering = block.numbering;
143
+ if (numbering !== undefined) {
144
+ this.closeQuote();
145
+ if (numbering.label) {
146
+ // a level with numFmt none, or a lvlText with no %n, is a label ("NOTE", "EXAMPLE"), not a list: it
147
+ // leads the paragraph in bold
148
+ this.closeLists();
149
+ const label = numbering.levelText === undefined ? '' : numbering.levelText.trim();
150
+ const labelled = this.inlineParagraph(block, false);
151
+ if (label.length > 0) {
152
+ if (labelled.firstChild !== undefined)
153
+ labelled.prependChild(new MdText(' '));
154
+ const strong = new MdStrongEmphasis();
155
+ strong.appendChild(new MdText(label));
156
+ labelled.prependChild(strong);
157
+ }
158
+ if (labelled.firstChild !== undefined)
159
+ container.appendChild(labelled);
160
+ return index;
161
+ }
162
+ this.listParagraph(block, numbering, container);
163
+ return index;
164
+ }
165
+ // an unlabelled paragraph inside a list (WD REQ-703): Markdown has no empty paragraphs, so an empty one
166
+ // neither ends the list nor joins it; one indented at least to an open item's text (or in the
167
+ // ListParagraph style) is a follow-on paragraph of the deepest such item; any other ends the lists, and
168
+ // the list goes on at its own count where it resumes (listParagraph)
169
+ if (this.openLists.length > 0 && style !== QUOTE && style !== INTENSE_QUOTE) {
170
+ if (isEmpty(block))
171
+ return index;
172
+ const into = this.followOnItem(block, style);
173
+ if (into !== undefined) {
174
+ into.node.tight = false;
175
+ into.lastItem.appendChild(this.inlineParagraph(block, true));
176
+ return index;
177
+ }
178
+ }
179
+ this.closeLists();
180
+ // a block quote, nested by the indent beyond the quote style's own
181
+ if (style === QUOTE || style === INTENSE_QUOTE) {
182
+ const beyond = value(block.indentLeft) - this.styleIndentLeft(style);
183
+ const depth = 1 + Math.trunc(Math.max(0, beyond) / TWIPS_PER_LEVEL);
184
+ this.appendToQuote(depth, this.inlineParagraph(block, true), container, block);
185
+ return index;
186
+ }
187
+ this.closeQuote();
188
+ const paragraph = this.inlineParagraph(block, true);
189
+ if (paragraph.firstChild !== undefined)
190
+ container.appendChild(paragraph); // Markdown has no empty paragraphs
191
+ return index;
192
+ }
193
+ inlineParagraph(block, withListLabel) {
194
+ const paragraph = tagged(new MdParagraph(), block);
195
+ this.inlines(block, paragraph, withListLabel);
196
+ return paragraph;
197
+ }
198
+ /** A code paragraph's text, unescaped; in `markup`, its revisions marked. */
199
+ codeText(block) {
200
+ let out = '';
201
+ const runs = block.runs;
202
+ let i = 0;
203
+ while (i < runs.length) {
204
+ const revision = runs[i].revision;
205
+ let j = i;
206
+ while (j < runs.length && this.sameMarker(revision, runs[j].revision))
207
+ j++;
208
+ if (revision !== undefined)
209
+ out += this.open(revision);
210
+ for (let k = i; k < j; k++) {
211
+ const r = runs[k];
212
+ if (r.kind === 'TEXT' || r.kind === 'FIELD_CODE')
213
+ out += r.text;
214
+ }
215
+ if (revision !== undefined)
216
+ out += close(revision);
217
+ i = j;
218
+ }
219
+ return out;
220
+ }
221
+ /** A paragraph style's own left indent, in twips: 0 where it has none, or there are no styles to ask. */
222
+ styleIndentLeft(styleId) {
223
+ var _a;
224
+ const cached = this.styleIndents.get(styleId);
225
+ if (cached !== undefined)
226
+ return cached;
227
+ let left = 0;
228
+ if (this.resolver) {
229
+ try {
230
+ const ind = this.resolver.getEffectivePPr(styleId).ind;
231
+ const l = (_a = ind === null || ind === void 0 ? void 0 : ind.left) !== null && _a !== void 0 ? _a : ind === null || ind === void 0 ? void 0 : ind.start;
232
+ if (l !== undefined && l !== null && Number.isFinite(Number(l)))
233
+ left = Math.trunc(Number(l));
234
+ }
235
+ catch {
236
+ // no indent for the style
237
+ }
238
+ }
239
+ this.styleIndents.set(styleId, left);
240
+ return left;
241
+ }
242
+ // ---------------------------------------------------------------- quotes
243
+ appendToQuote(depth, paragraph, container, block) {
244
+ if (this.openQuote === undefined || depth !== this.openQuoteDepth) {
245
+ this.closeQuote();
246
+ const outer = tagged(new MdBlockQuote(), block);
247
+ let inner = outer;
248
+ for (let d = 1; d < depth; d++) {
249
+ const next = new MdBlockQuote();
250
+ inner.appendChild(next);
251
+ inner = next;
252
+ }
253
+ container.appendChild(outer);
254
+ this.openQuote = inner;
255
+ this.openQuoteDepth = depth;
256
+ }
257
+ this.openQuote.appendChild(paragraph);
258
+ }
259
+ closeQuote() {
260
+ this.openQuote = undefined;
261
+ this.openQuoteDepth = 0;
262
+ }
263
+ // ---------------------------------------------------------------- lists
264
+ /**
265
+ * The open list whose last item an unlabelled paragraph continues: the deepest whose item's text the
266
+ * paragraph is indented to (its left indent at least the item's, and not at the margin), or the deepest
267
+ * open list for a ListParagraph-styled paragraph; the lists deeper than it are closed. Undefined where
268
+ * none: the paragraph ends the lists.
269
+ */
270
+ followOnItem(block, style) {
271
+ const indent = value(block.indentLeft);
272
+ const listParagraph = style === LIST_PARAGRAPH;
273
+ for (let k = this.openLists.length - 1; k >= 0; k--) { // deepest first
274
+ const open = this.openLists[k];
275
+ if (open.lastItem !== undefined && (listParagraph || (indent > 0 && indent >= open.itemIndent))) {
276
+ this.openLists.length = k + 1;
277
+ return open;
278
+ }
279
+ }
280
+ return undefined;
281
+ }
282
+ top() {
283
+ return this.openLists[this.openLists.length - 1];
284
+ }
285
+ listParagraph(block, numbering, container) {
286
+ var _a;
287
+ const ilvl = numbering.level;
288
+ // the list: two w:num on one w:abstractNum are one list, counting on from each other
289
+ const numId = (_a = numbering.abstractNumId) !== null && _a !== void 0 ? _a : numbering.numId;
290
+ const count = numbering.value;
291
+ const bullet = numbering.bullet || numbering.format === undefined;
292
+ // close any deeper (or same-level-but-different-list) contexts
293
+ if (this.openLists.length > ilvl + 1)
294
+ this.openLists.length = ilvl + 1;
295
+ if (this.openLists.length === ilvl + 1 && this.top().numId !== numId && ilvl === 0) {
296
+ this.openLists.pop(); // a different top-level list: restart
297
+ }
298
+ if (this.openLists.length === ilvl + 1 && (this.top().node instanceof MdBulletList) !== bullet) {
299
+ this.openLists.pop(); // a level drawn as the other kind (a w:num overriding it)
300
+ }
301
+ const tight = block.contextualSpacing !== false;
302
+ // the list's items show consecutive counts from its start; an item whose count is not the next one (the
303
+ // list resumed after a paragraph that ended it, a w:num sharing the count, a start override) begins a new
304
+ // list at its count
305
+ if (this.openLists.length === ilvl + 1 && count !== undefined && this.top().nextValue !== undefined
306
+ && count !== this.top().nextValue) {
307
+ this.openLists.pop();
308
+ }
309
+ // open the missing levels (a level skipped over takes the paragraph's kind of list); a parent level
310
+ // reopened after an interruption is an empty item showing the parent's last count
311
+ while (this.openLists.length < ilvl + 1) {
312
+ const level = this.openLists.length;
313
+ const listBlock = tagged(createListBlock(numbering, level === ilvl, tight, level === ilvl ? count : this.lastValues.get(`${numId}/${level}`)), block);
314
+ if (this.openLists.length === 0) {
315
+ appendList(container, listBlock);
316
+ }
317
+ else {
318
+ const parent = this.top();
319
+ if (parent.lastItem === undefined) {
320
+ parent.lastItem = tagged(new MdListItem(), block);
321
+ parent.node.appendChild(parent.lastItem);
322
+ }
323
+ appendList(parent.lastItem, listBlock);
324
+ }
325
+ this.openLists.push({ node: listBlock, numId, lastItem: undefined, itemIndent: 0, nextValue: undefined });
326
+ }
327
+ const target = this.top();
328
+ if (!tight)
329
+ target.node.tight = false;
330
+ const item = tagged(new MdListItem(), block);
331
+ item.appendChild(this.inlineParagraph(block, false));
332
+ target.node.appendChild(item);
333
+ target.lastItem = item;
334
+ target.itemIndent = value(block.indentLeft);
335
+ if (target.node instanceof MdOrderedList && count !== undefined) {
336
+ target.nextValue = count + 1;
337
+ this.lastValues.set(`${numId}/${ilvl}`, count);
338
+ }
339
+ }
340
+ closeLists() {
341
+ this.openLists.length = 0;
342
+ }
343
+ // ---------------------------------------------------------------- tables
344
+ /**
345
+ * GFM pipes: the header row is the table's `w:tblHeader` rows, else its first (several `w:tblHeader` rows
346
+ * are one header, their cells' texts ` / ` apart, as plain text's listed format joins them); block content
347
+ * in cells flattens to inline with `<br>`, a nested table's paragraphs included; `w:vMerge` and
348
+ * `w:gridSpan` degrade (top-left wins, empty cells elsewhere), GFM having no spans.
349
+ *
350
+ * The table style's formatting is left out on purpose (docx4j CR-030 decision 4): a header row the style
351
+ * makes bold is not written `**bold**` (its being the GFM header says as much), and GFM has no table
352
+ * styling for the rest.
353
+ */
354
+ table(table, container) {
355
+ const tableBlock = tagged(new MdTableBlock(), table);
356
+ const head = new MdTableHead();
357
+ const body = new MdTableBody();
358
+ tableBlock.appendChild(head);
359
+ const rows = table.rows;
360
+ let headerRows = 0;
361
+ while (headerRows < rows.length && rows[headerRows].header)
362
+ headerRows++;
363
+ if (headerRows === 0 && rows.length > 0)
364
+ headerRows = 1;
365
+ if (headerRows > 0)
366
+ head.appendChild(this.headerRow(rows.slice(0, headerRows)));
367
+ for (let r = headerRows; r < rows.length; r++) {
368
+ const row = new MdTableRow();
369
+ for (const cell of rows[r].cells) {
370
+ row.appendChild(this.tableCell(cell, false));
371
+ // gridSpan: empty cells after it, so that the columns stay aligned
372
+ for (let s = 1; s < cell.gridSpan; s++)
373
+ row.appendChild(new MdTableCell(false));
374
+ }
375
+ body.appendChild(row);
376
+ }
377
+ if (body.firstChild !== undefined)
378
+ tableBlock.appendChild(body);
379
+ container.appendChild(tableBlock);
380
+ }
381
+ /** The header: one row's cells, or several rows' cells by grid column, ` / ` apart. */
382
+ headerRow(rows) {
383
+ const row = new MdTableRow();
384
+ if (rows.length === 1) {
385
+ for (const cell of rows[0].cells) {
386
+ row.appendChild(this.tableCell(cell, true));
387
+ for (let s = 1; s < cell.gridSpan; s++)
388
+ row.appendChild(new MdTableCell(true));
389
+ }
390
+ return row;
391
+ }
392
+ // each grid column's cells, from the top: a cell is in the column it starts in
393
+ let width = 0;
394
+ for (const r of rows) {
395
+ let w = 0;
396
+ for (const c of r.cells)
397
+ w += Math.max(1, c.gridSpan);
398
+ width = Math.max(width, w);
399
+ }
400
+ const columns = [];
401
+ for (let g = 0; g < width; g++)
402
+ columns.push([]);
403
+ for (const r of rows) {
404
+ let g = 0;
405
+ for (const c of r.cells) {
406
+ if (!c.mergeContinue)
407
+ columns[g].push(c);
408
+ g += Math.max(1, c.gridSpan);
409
+ }
410
+ }
411
+ for (const column of columns) {
412
+ const cell = new MdTableCell(true);
413
+ // the column's alignment is its topmost header cell's, as with one header row
414
+ let alignment;
415
+ if (column.length > 0) {
416
+ const first = paragraphsOf(column[0]);
417
+ if (first.length > 0)
418
+ alignment = cellAlignment(first[0]);
419
+ }
420
+ const sink = new InlineSink(cell);
421
+ this.tableHeaderDepth++;
422
+ try {
423
+ let first = true;
424
+ for (const c of column) {
425
+ if (isEmptyCell(c))
426
+ continue;
427
+ if (!first)
428
+ sink.text(' / ', false, false, false);
429
+ first = false;
430
+ this.cellContent(c, cell, sink);
431
+ }
432
+ }
433
+ finally {
434
+ this.tableHeaderDepth--;
435
+ }
436
+ sink.flush();
437
+ cell.alignment = alignment;
438
+ row.appendChild(cell);
439
+ }
440
+ return row;
441
+ }
442
+ tableCell(textCell, header) {
443
+ const cell = new MdTableCell(header);
444
+ // a vMerge continuation: the merge's top cell already holds the content
445
+ if (textCell.mergeContinue)
446
+ return cell;
447
+ const sink = new InlineSink(cell);
448
+ if (header)
449
+ this.tableHeaderDepth++;
450
+ try {
451
+ this.cellContent(textCell, cell, sink);
452
+ }
453
+ finally {
454
+ if (header)
455
+ this.tableHeaderDepth--;
456
+ }
457
+ sink.flush();
458
+ return cell;
459
+ }
460
+ /**
461
+ * A cell's paragraphs, a nested table's included, `<br>` apart (GFM cells are single-line); its empty
462
+ * paragraphs left out, as plain text leaves them out (Word ends every cell with a paragraph, so a nested
463
+ * table or a block-level control comes with an empty one after it).
464
+ *
465
+ * A cell's alignment is its first paragraph's effective `w:jc`, empty or not; GFM aligns a column, from
466
+ * its header cell's (the delimiter row), so a body cell's is not seen.
467
+ */
468
+ cellContent(textCell, cell, sink) {
469
+ let first = true;
470
+ const paragraphs = paragraphsOf(textCell);
471
+ if (cell.alignment === undefined && paragraphs.length > 0)
472
+ cell.alignment = cellAlignment(paragraphs[0]);
473
+ for (const b of paragraphs) {
474
+ if (isEmpty(b))
475
+ continue;
476
+ if (!first)
477
+ sink.node(new MdHtmlInline('<br>'));
478
+ first = false;
479
+ this.runs(b, sink);
480
+ }
481
+ }
482
+ // ---------------------------------------------------------------- charts
483
+ /**
484
+ * A chart: its note (docx4j CR-047: `[chart: clustered column; value axis: Revenue]`), its title and its
485
+ * data as a table, after the paragraph it is in, the categories down and the series across (CR-012 phase
486
+ * 5c fills them in; until then the text model makes no chart block).
487
+ */
488
+ chart(block, container) {
489
+ if (block.chart !== undefined) {
490
+ const note = tagged(new MdParagraph(), block);
491
+ note.appendChild(new MdText(block.chart.note));
492
+ container.appendChild(note);
493
+ }
494
+ if (block.chartTitle !== undefined && block.chartTitle !== '') {
495
+ const title = tagged(new MdParagraph(), block);
496
+ title.appendChild(new MdText(block.chartTitle));
497
+ container.appendChild(title);
498
+ }
499
+ if (block.chartHeader.length === 0)
500
+ return;
501
+ const table = tagged(new MdTableBlock(), block);
502
+ const head = new MdTableHead();
503
+ head.appendChild(chartRow(block.chartHeader, true, block.chartHeader));
504
+ table.appendChild(head);
505
+ if (block.chartRows.length > 0) {
506
+ const body = new MdTableBody();
507
+ for (const r of block.chartRows)
508
+ body.appendChild(chartRow(r, false, block.chartHeader));
509
+ table.appendChild(body);
510
+ }
511
+ container.appendChild(table);
512
+ }
513
+ // ---------------------------------------------------------------- inlines
514
+ inlines(block, parent, withListLabel) {
515
+ const sink = new InlineSink(parent);
516
+ // under list_labels: true, the label the numbering model computes leads a paragraph that isn't written as
517
+ // a list item (a numbered heading's "1.2")
518
+ if (withListLabel && block.listLabel !== undefined && block.listLabel.trim() !== '') {
519
+ sink.text(block.listLabel.replace(/\t/g, ' '), false, false, false);
520
+ }
521
+ this.runs(block, sink);
522
+ sink.flush();
523
+ }
524
+ /** A block's runs, in `markup` each revision's between its CriticMarkup marks (REQ-703, REQ-702). */
525
+ runs(block, sink) {
526
+ const runs = block.runs;
527
+ let i = 0;
528
+ while (i < runs.length) {
529
+ const revision = runs[i].revision;
530
+ let j = i;
531
+ while (j < runs.length && this.sameMarker(revision, runs[j].revision))
532
+ j++;
533
+ if (revision !== undefined)
534
+ this.openMarker(revision, sink);
535
+ this.linked(runs.slice(i, j), sink);
536
+ if (revision !== undefined)
537
+ sink.node(new MdHtmlInline(close(revision)));
538
+ i = j;
539
+ }
540
+ }
541
+ /** Runs, those with the same link target in one link. */
542
+ linked(runs, sink) {
543
+ let i = 0;
544
+ while (i < runs.length) {
545
+ let target = runs[i].link;
546
+ let j = i;
547
+ while (j < runs.length && runs[j].link === target)
548
+ j++;
549
+ if (target !== undefined && !LinkTargets.isSafe(target, this.linkSchemes)) {
550
+ // docx4j CR-056: a script-scheme target, its character references decoded too; the text, with no link (CR-012
551
+ // sections 21 and 21.2)
552
+ if (!this.linkRefused)
553
+ log.warn(LinkTargets.refusedMessage(target));
554
+ this.linkRefused = true;
555
+ target = undefined;
556
+ }
557
+ if (target === undefined) {
558
+ for (let k = i; k < j; k++)
559
+ this.run(runs[k], sink);
560
+ }
561
+ else {
562
+ const link = new MdLink(target, undefined);
563
+ const linkSink = new InlineSink(link);
564
+ for (let k = i; k < j; k++)
565
+ this.run(runs[k], linkSink);
566
+ linkSink.flush();
567
+ sink.node(link);
568
+ }
569
+ i = j;
570
+ }
571
+ }
572
+ run(run, sink) {
573
+ switch (run.kind) {
574
+ case 'TEXT':
575
+ this.text(run, sink);
576
+ return;
577
+ case 'FIELD_CODE':
578
+ case 'LIST_LABEL':
579
+ sink.text(run.text, false, false, false);
580
+ return;
581
+ case 'NOTE_REFERENCE':
582
+ if (this.textOptions.notes && this.textOptions.noteReferences) {
583
+ sink.node(new MdFootnoteReference(noteLabel(run.noteType, run.noteNumber, run.noteId)));
584
+ }
585
+ return;
586
+ case 'COMMENT_REFERENCE': {
587
+ const comment = run.commentId === undefined ? undefined : this.result.comments.get(run.commentId);
588
+ // a reply is written after its parent, never at its own reference (REQ-504)
589
+ if (comment === undefined || (comment.parentId !== undefined && this.result.comments.has(comment.parentId)))
590
+ return;
591
+ this.comment(comment, sink);
592
+ for (const reply of comment.replies)
593
+ this.comment(reply, sink);
594
+ return;
595
+ }
596
+ case 'IMAGE':
597
+ this.image(run, sink);
598
+ return;
599
+ case 'PAGE_MARKER':
600
+ if (this.textOptions.pageMarkers)
601
+ sink.text(`[page ${run.pageNumber}]`, false, false, false);
602
+ return;
603
+ default:
604
+ // a note's own mark (its definition's label carries it), an object (no text)
605
+ }
606
+ }
607
+ text(run, sink) {
608
+ const value = run.text;
609
+ if (value === '')
610
+ return;
611
+ if (run.math) {
612
+ const l = latex(run);
613
+ if (l !== '')
614
+ sink.node(new MdInlineMath(l, false));
615
+ return;
616
+ }
617
+ const bold = run.bold && this.tableHeaderDepth === 0; // a header row's bold is convention, not markup
618
+ const lines = value.split('\n');
619
+ for (let l = 0; l < lines.length; l++) {
620
+ if (l > 0)
621
+ sink.node(new MdHardLineBreak());
622
+ const line = lines[l];
623
+ if (line === '')
624
+ continue;
625
+ if (run.monospace) {
626
+ sink.node(new MdCode(line));
627
+ }
628
+ else {
629
+ sink.text(line, bold, run.italic, run.strike);
630
+ }
631
+ }
632
+ }
633
+ comment(comment, sink) {
634
+ sink.node(new MdHtmlInline('{>>'));
635
+ this.detailText(comment.author, comment.date, sink);
636
+ sink.text(comment.text, false, false, false);
637
+ sink.node(new MdHtmlInline('<<}'));
638
+ }
639
+ /** Runs under one marker: the same type, and the same author and date where the detail shows them. */
640
+ sameMarker(a, b) {
641
+ if (a === undefined || b === undefined)
642
+ return a === b;
643
+ if (a.type !== b.type)
644
+ return false;
645
+ switch (this.textOptions.markupDetail) {
646
+ case 'author': return a.author === b.author;
647
+ case 'author_date': return a.author === b.author && a.date === b.date;
648
+ default: return true;
649
+ }
650
+ }
651
+ /** The marker as one string, for a code block, where nothing is markup. */
652
+ open(revision) {
653
+ return (revision.type === 'INSERTION' ? '{++' : '{--') + this.detail(revision.author, revision.date);
654
+ }
655
+ /** The marker in a paragraph: its braces raw, its author and date text (docx4j's `open(revision, sink)`). */
656
+ openMarker(revision, sink) {
657
+ sink.node(new MdHtmlInline(revision.type === 'INSERTION' ? '{++' : '{--'));
658
+ this.detailText(revision.author, revision.date, sink);
659
+ }
660
+ /**
661
+ * The marker's author and date as text, which the renderer escapes as it does any document text (docx4j CR-056,
662
+ * `30fb41024`; CR-012 section 21.1): only the braces are raw. Before, the detail went out raw with them, so an
663
+ * author of `<script>...</script>` reached a renderer that allows raw HTML as markup.
664
+ */
665
+ detailText(author, date, sink) {
666
+ const d = this.detail(author, date);
667
+ if (d !== '')
668
+ sink.text(d, false, false, false);
669
+ }
670
+ detail(author, date) {
671
+ switch (this.textOptions.markupDetail) {
672
+ case 'author': return `${author !== null && author !== void 0 ? author : ''}: `;
673
+ case 'author_date': return `${author !== null && author !== void 0 ? author : ''}, ${date !== null && date !== void 0 ? date : ''}: `;
674
+ default: return '';
675
+ }
676
+ }
677
+ /**
678
+ * A picture with the describer's text (REQ-703, REQ-514); its destination, which needs its image part's
679
+ * bytes, is the exporter's to fill in (`images`), empty for a picture whose image can't be had.
680
+ */
681
+ image(run, sink) {
682
+ const image = new MdImage('', undefined);
683
+ image.appendChild(new MdText(run.description === undefined ? '' : run.description.text));
684
+ this.images.push({ node: image, run });
685
+ sink.node(image);
686
+ }
687
+ }
688
+ // ---------------------------------------------------------------- helpers
689
+ /** A node made for a block: it begins the block's Markdown (`MarkdownExporter.blocks`). */
690
+ function tagged(node, block) {
691
+ node.tag = block;
692
+ return node;
693
+ }
694
+ /** Accumulates same-formatted text, wrapping it on flush. */
695
+ class InlineSink {
696
+ constructor(parent) {
697
+ this.parent = parent;
698
+ this.buf = '';
699
+ this.bold = false;
700
+ this.italic = false;
701
+ this.strike = false;
702
+ }
703
+ text(s, b, i, st) {
704
+ if ((b !== this.bold || i !== this.italic || st !== this.strike) && this.buf.length > 0)
705
+ this.flush();
706
+ this.bold = b;
707
+ this.italic = i;
708
+ this.strike = st;
709
+ this.buf += s;
710
+ }
711
+ node(n) {
712
+ this.flush();
713
+ this.parent.appendChild(n);
714
+ }
715
+ flush() {
716
+ if (this.buf.length === 0)
717
+ return;
718
+ let n = new MdText(this.buf);
719
+ // strong inside emphasis: the renderer then writes ***both***, rather than switching the inner marker to _
720
+ if (this.bold) {
721
+ const strong = new MdStrongEmphasis();
722
+ strong.appendChild(n);
723
+ n = strong;
724
+ }
725
+ if (this.italic) {
726
+ const em = new MdEmphasis();
727
+ em.appendChild(n);
728
+ n = em;
729
+ }
730
+ if (this.strike) {
731
+ const st = new MdStrikethrough('~~');
732
+ st.appendChild(n);
733
+ n = st;
734
+ }
735
+ this.parent.appendChild(n);
736
+ this.buf = '';
737
+ }
738
+ }
739
+ function isEmpty(block) {
740
+ for (const r of block.runs) {
741
+ if (r.kind === 'TEXT' && r.text !== '')
742
+ return false;
743
+ if (r.kind === 'IMAGE' || r.kind === 'NOTE_REFERENCE')
744
+ return false;
745
+ }
746
+ return true;
747
+ }
748
+ function isEmptyCell(cell) {
749
+ for (const b of cell.blocks) {
750
+ if (b.kind === 'TABLE') {
751
+ for (const r of b.rows)
752
+ for (const c of r.cells)
753
+ if (!isEmptyCell(c))
754
+ return false;
755
+ }
756
+ else if (b.kind === 'PARAGRAPH' && !isEmpty(b)) {
757
+ return false;
758
+ }
759
+ }
760
+ return true;
761
+ }
762
+ /** The paragraph's one run, a display equation with no revision; else undefined. */
763
+ function soleDisplayMath(block) {
764
+ let found;
765
+ for (const r of block.runs) {
766
+ if (r.kind === 'TEXT' && r.text === '')
767
+ continue;
768
+ if (found !== undefined || !r.displayMath || r.revision !== undefined)
769
+ return undefined;
770
+ found = r;
771
+ }
772
+ return found;
773
+ }
774
+ /** An equation's LaTeX: its text without the delimiters. */
775
+ function latex(run) {
776
+ const s = run.text;
777
+ const d = run.displayMath ? 2 : 1;
778
+ return s.length >= 2 * d ? s.substring(d, s.length - d) : s;
779
+ }
780
+ /**
781
+ * A code paragraph: styled as one, or in a named paragraph style whose own font is mono (every run
782
+ * monospaced, none of them beyond the style). Inline code is what a run adds to its paragraph style, so a
783
+ * paragraph whose style is already mono would get no code marks at all; this is the paragraph-level
784
+ * counterpart. A mono document default doesn't count (a document set in Courier New throughout is prose, not
785
+ * one fence), which is why a named style is required.
786
+ */
787
+ function isCodeParagraph(block) {
788
+ const style = block.style;
789
+ if (style === undefined)
790
+ return false;
791
+ if (CODE_STYLES.has(style))
792
+ return true;
793
+ if (!block.monospace)
794
+ return false;
795
+ for (const r of block.runs)
796
+ if (r.kind === 'TEXT' && r.monospace)
797
+ return false;
798
+ return true;
799
+ }
800
+ function sameStory(a, b) {
801
+ return a.story === b.story && a.noteId === b.noteId;
802
+ }
803
+ function value(i) {
804
+ return i !== null && i !== void 0 ? i : 0;
805
+ }
806
+ /**
807
+ * A list block. An ordered one starts at the count given (the item's own, or a reopened parent's last), else
808
+ * (the paragraph's own level at the top) at the level's `w:start`; Markdown numbers the items on from there.
809
+ */
810
+ function createListBlock(numbering, own, tight, count) {
811
+ // a level the numbering definitions don't give is a bullet list
812
+ const bullet = numbering.bullet || numbering.format === undefined;
813
+ let listBlock;
814
+ if (bullet) {
815
+ listBlock = new MdBulletList();
816
+ }
817
+ else {
818
+ const ordered = new MdOrderedList();
819
+ if (count !== undefined) {
820
+ if (count !== 1)
821
+ ordered.markerStartNumber = count;
822
+ }
823
+ else if (own && numbering.level === 0 && numbering.start > 1) {
824
+ ordered.markerStartNumber = numbering.start;
825
+ }
826
+ listBlock = ordered;
827
+ }
828
+ listBlock.tight = tight;
829
+ return listBlock;
830
+ }
831
+ /**
832
+ * Appends a list. Markdown runs two adjacent lists of one kind together, numbering the second on from the
833
+ * first; a list that directly follows another of its kind therefore takes the other delimiter (`1.` or
834
+ * `1)`) or bullet (`-` or `*`), which CommonMark reads as a new list.
835
+ */
836
+ function appendList(parent, list) {
837
+ const previous = parent.lastChild;
838
+ if (list instanceof MdOrderedList && previous instanceof MdOrderedList) {
839
+ list.markerDelimiter = previous.markerDelimiter === ')' ? '.' : ')';
840
+ }
841
+ else if (list instanceof MdBulletList && previous instanceof MdBulletList) {
842
+ list.marker = previous.marker === '*' ? '-' : '*';
843
+ }
844
+ parent.appendChild(list);
845
+ }
846
+ function paragraphsOf(cell) {
847
+ const out = [];
848
+ for (const b of cell.blocks) {
849
+ if (b.kind === 'TABLE') {
850
+ for (const r of b.rows)
851
+ for (const c of r.cells)
852
+ out.push(...paragraphsOf(c));
853
+ }
854
+ else if (b.kind === 'PARAGRAPH') {
855
+ out.push(b);
856
+ }
857
+ }
858
+ return out;
859
+ }
860
+ function cellAlignment(block) {
861
+ switch (block.alignment) {
862
+ case 'center': return 'CENTER';
863
+ case 'right':
864
+ case 'end': return 'RIGHT';
865
+ default: return undefined; // LEFT is GFM's default
866
+ }
867
+ }
868
+ function chartRow(cells, header, headings) {
869
+ const row = new MdTableRow();
870
+ let labels = 0;
871
+ while (labels < headings.length && headings[labels] === '')
872
+ labels++;
873
+ for (let i = 0; i < cells.length; i++) {
874
+ const cell = new MdTableCell(header);
875
+ if (i >= Math.max(labels, 1))
876
+ cell.alignment = 'RIGHT';
877
+ if (cells[i] !== '')
878
+ cell.appendChild(new MdText(cells[i]));
879
+ row.appendChild(cell);
880
+ }
881
+ return row;
882
+ }
883
+ function isNote(block) {
884
+ return block.noteType !== undefined && (block.story === 'FOOTNOTE' || block.story === 'ENDNOTE' || block.story === 'TEXT_BOX');
885
+ }
886
+ function sameNote(a, b) {
887
+ return isNote(b) && a.noteType === b.noteType && a.noteId === b.noteId;
888
+ }
889
+ /**
890
+ * A note's label: a footnote's number (`[^1]`), an endnote's prefixed `e` (`[^e1]`), since the two are
891
+ * numbered apart (WD REQ-503); a note whose reference carries a custom mark has no number, so its `w:id`
892
+ * labels it (`[^f-5]`, `[^e-5]`).
893
+ */
894
+ function noteLabel(type, number, id) {
895
+ const foot = type === 'footnote';
896
+ if (number > 0)
897
+ return foot ? String(number) : `e${number}`;
898
+ return `${foot ? 'f-' : 'e-'}${String(id)}`;
899
+ }
900
+ /** Drops the leading space left behind by the note's own mark. */
901
+ function trimLeadingMarkerSpace(definition) {
902
+ const paragraph = definition.firstChild;
903
+ if (paragraph instanceof MdParagraph && paragraph.firstChild instanceof MdText) {
904
+ const text = paragraph.firstChild;
905
+ if (text.literal.startsWith(' '))
906
+ text.literal = text.literal.substring(1);
907
+ }
908
+ }
909
+ function close(revision) {
910
+ return revision.type === 'INSERTION' ? '++}' : '--}';
911
+ }