@stll/folio-core 0.47.7 → 0.48.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 (160) hide show
  1. package/dist/controller/fontReadiness.js +3 -3
  2. package/dist/controller/hyphenationReadiness.d.ts +19 -0
  3. package/dist/controller/hyphenationReadiness.js +47 -0
  4. package/dist/controller/layoutPipeline.d.ts +3 -0
  5. package/dist/controller/layoutPipeline.js +59 -17
  6. package/dist/display-list/build/buildDisplayList.js +39 -12
  7. package/dist/display-list/build/pageFurniture.d.ts +7 -1
  8. package/dist/display-list/build/pageFurniture.js +21 -8
  9. package/dist/display-list/build/paragraphPrimitives.js +5 -4
  10. package/dist/display-list/build/tablePrimitives.js +10 -6
  11. package/dist/docx/attributeRemainder.js +10 -2
  12. package/dist/docx/blockContentParser.d.ts +2 -0
  13. package/dist/docx/blockContentParser.js +77 -62
  14. package/dist/docx/commentParser.d.ts +2 -1
  15. package/dist/docx/commentParser.js +57 -45
  16. package/dist/docx/containerChildren.d.ts +21 -8
  17. package/dist/docx/containerChildren.js +39 -9
  18. package/dist/docx/documentParser.d.ts +5 -1
  19. package/dist/docx/documentParser.js +8 -5
  20. package/dist/docx/documentSectionFacts.d.ts +22 -0
  21. package/dist/docx/documentSectionFacts.js +43 -0
  22. package/dist/docx/fieldParser.d.ts +8 -1
  23. package/dist/docx/fieldParser.js +47 -3
  24. package/dist/docx/fontTableParser.js +34 -27
  25. package/dist/docx/footnoteParser.d.ts +3 -2
  26. package/dist/docx/footnoteParser.js +15 -12
  27. package/dist/docx/groupDrawingParser.d.ts +2 -1
  28. package/dist/docx/groupDrawingParser.js +3 -6
  29. package/dist/docx/headerFooterParser.d.ts +7 -2
  30. package/dist/docx/headerFooterParser.js +15 -5
  31. package/dist/docx/hyperlinkParser.d.ts +48 -36
  32. package/dist/docx/hyperlinkParser.js +97 -82
  33. package/dist/docx/numberingParser.js +108 -84
  34. package/dist/docx/paragraphParser.d.ts +5 -1
  35. package/dist/docx/paragraphParser.js +381 -362
  36. package/dist/docx/paragraphProperties.js +123 -109
  37. package/dist/docx/paragraphPropertySource.d.ts +6 -1
  38. package/dist/docx/paragraphPropertySource.js +9 -1
  39. package/dist/docx/paragraphTextBoxEnrichment.d.ts +2 -1
  40. package/dist/docx/paragraphTextBoxEnrichment.js +17 -6
  41. package/dist/docx/parser.d.ts +7 -1
  42. package/dist/docx/parser.js +33 -16
  43. package/dist/docx/previewBudget.d.ts +55 -33
  44. package/dist/docx/previewBudget.js +59 -65
  45. package/dist/docx/rezip.js +41 -58
  46. package/dist/docx/runParser.d.ts +4 -1
  47. package/dist/docx/runParser.js +172 -184
  48. package/dist/docx/sdtProperties.js +87 -84
  49. package/dist/docx/sectionParser.js +169 -160
  50. package/dist/docx/serializer/paragraphSerializer.js +42 -38
  51. package/dist/docx/serializer/settingsSerializer.js +19 -1
  52. package/dist/docx/settingsParser.js +8 -0
  53. package/dist/docx/streamingXmlParser.d.ts +6 -2
  54. package/dist/docx/streamingXmlParser.js +12 -7
  55. package/dist/docx/styleParser.js +34 -8
  56. package/dist/docx/tableParser.d.ts +21 -12
  57. package/dist/docx/tableParser.js +518 -441
  58. package/dist/docx/textBoxParser.d.ts +8 -3
  59. package/dist/docx/textBoxParser.js +3 -3
  60. package/dist/docx/verbatimCapture.d.ts +10 -8
  61. package/dist/docx/verbatimCapture.js +45 -2
  62. package/dist/docx/vmlImageParser.d.ts +15 -13
  63. package/dist/docx/vmlImageParser.js +75 -60
  64. package/dist/docx/vmlPreview.d.ts +3 -3
  65. package/dist/docx/vmlPreview.js +3 -4
  66. package/dist/docx/xmlNamespaceContext.d.ts +9 -0
  67. package/dist/docx/xmlNamespaceContext.js +54 -0
  68. package/dist/docx/xmlParser.d.ts +6 -3
  69. package/dist/docx/xmlParser.js +11 -41
  70. package/dist/docx/xmlResourceLimits.d.ts +2 -1
  71. package/dist/docx/xmlResourceLimits.js +4 -1
  72. package/dist/fonts/headlessMeasure.js +0 -0
  73. package/dist/headless-layout.d.ts +11 -2
  74. package/dist/headless-layout.js +153 -107
  75. package/dist/internal/paragraphFormattingSerialization.js +3 -3
  76. package/dist/layout-bridge/convert/endnoteLayout.d.ts +65 -0
  77. package/dist/layout-bridge/convert/endnoteLayout.js +227 -0
  78. package/dist/layout-bridge/convert/footnoteLayout.d.ts +19 -3
  79. package/dist/layout-bridge/convert/footnoteLayout.js +40 -31
  80. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -7
  81. package/dist/layout-bridge/convert/headerFooterLayout.js +5 -20
  82. package/dist/layout-bridge/convert/paragraphMarkFormatting.d.ts +10 -0
  83. package/dist/layout-bridge/convert/paragraphMarkFormatting.js +59 -0
  84. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +29 -1
  85. package/dist/layout-bridge/convert/toFlowBlocks.js +258 -35
  86. package/dist/layout-bridge/engine/hitTest.js +5 -3
  87. package/dist/layout-bridge/engine/selectionRects.js +5 -3
  88. package/dist/layout-engine/anchorLayoutInCellCompatibility.d.ts +10 -0
  89. package/dist/layout-engine/anchorLayoutInCellCompatibility.js +22 -0
  90. package/dist/layout-engine/index.d.ts +2 -2
  91. package/dist/layout-engine/index.js +70 -11
  92. package/dist/layout-engine/justificationCompatibility.d.ts +1 -1
  93. package/dist/layout-engine/justificationCompatibility.js +9 -5
  94. package/dist/layout-engine/keep-together.d.ts +11 -5
  95. package/dist/layout-engine/keep-together.js +39 -16
  96. package/dist/layout-engine/layoutInstrumentation.d.ts +8 -2
  97. package/dist/layout-engine/layoutInstrumentation.js +8 -1
  98. package/dist/layout-engine/measure/advanceComposition.js +4 -7
  99. package/dist/layout-engine/measure/hyphenationDictionaries.d.ts +74 -0
  100. package/dist/layout-engine/measure/hyphenationDictionaries.js +138 -0
  101. package/dist/layout-engine/measure/hyphenationPreload.d.ts +11 -0
  102. package/dist/layout-engine/measure/hyphenationPreload.js +24 -0
  103. package/dist/layout-engine/measure/lineBreakProvider.d.ts +12 -2
  104. package/dist/layout-engine/measure/lineBreakProvider.js +30 -30
  105. package/dist/layout-engine/measure/lineBreaks.d.ts +6 -1
  106. package/dist/layout-engine/measure/lineBreaks.js +10 -3
  107. package/dist/layout-engine/measure/listMarkerWidth.d.ts +9 -6
  108. package/dist/layout-engine/measure/listMarkerWidth.js +20 -22
  109. package/dist/layout-engine/measure/measureBlocks.js +42 -8
  110. package/dist/layout-engine/measure/measureContainer.js +5 -5
  111. package/dist/layout-engine/measure/measureHelpers.d.ts +15 -3
  112. package/dist/layout-engine/measure/measureHelpers.js +21 -5
  113. package/dist/layout-engine/measure/measureParagraph.js +109 -30
  114. package/dist/layout-engine/measure/measureTypes.d.ts +5 -0
  115. package/dist/layout-engine/measure/tabCalculator.d.ts +2 -0
  116. package/dist/layout-engine/measure/tabCalculator.js +2 -1
  117. package/dist/layout-engine/measure/tableCellFloating.d.ts +2 -0
  118. package/dist/layout-engine/measure/tableCellFloating.js +51 -19
  119. package/dist/layout-engine/measure/tableCellGrid.d.ts +28 -3
  120. package/dist/layout-engine/measure/tableCellGrid.js +45 -5
  121. package/dist/layout-engine/measure/tableFragmentBorderGeometry.js +2 -1
  122. package/dist/layout-engine/noteAreaFlow.d.ts +19 -0
  123. package/dist/layout-engine/noteAreaFlow.js +83 -0
  124. package/dist/layout-engine/paginator.d.ts +10 -1
  125. package/dist/layout-engine/paginator.js +13 -1
  126. package/dist/layout-engine/tableRowBreak.js +3 -1
  127. package/dist/layout-engine/types.d.ts +89 -3
  128. package/dist/layout-engine/types.js +96 -1
  129. package/dist/layout-painter/index.d.ts +3 -0
  130. package/dist/layout-painter/renderPage.js +32 -83
  131. package/dist/layout-painter/renderParagraph.d.ts +6 -1
  132. package/dist/layout-painter/renderParagraph.js +34 -22
  133. package/dist/layout-painter/renderTable.js +26 -13
  134. package/dist/prosemirror/attrs/index.d.ts +9 -9
  135. package/dist/prosemirror/attrs/index.js +43 -11
  136. package/dist/prosemirror/conversion/fromProseDoc.js +8 -1
  137. package/dist/prosemirror/conversion/markInterner.d.ts +21 -0
  138. package/dist/prosemirror/conversion/markInterner.js +90 -0
  139. package/dist/prosemirror/conversion/toProseDoc.js +77 -41
  140. package/dist/prosemirror/extensions/core/DocExtension.js +2 -1
  141. package/dist/prosemirror/extensions/core/ParagraphExtension.js +1 -0
  142. package/dist/prosemirror/extensions/marks/markUtils.d.ts +6 -2
  143. package/dist/prosemirror/extensions/marks/markUtils.js +39 -51
  144. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +2 -1
  145. package/dist/prosemirror/listMarker.d.ts +1 -0
  146. package/dist/prosemirror/listMarker.js +2 -0
  147. package/dist/prosemirror/listRenderingAttrs.d.ts +1 -0
  148. package/dist/prosemirror/listRenderingAttrs.js +3 -0
  149. package/dist/prosemirror/schema/nodes.d.ts +13 -1
  150. package/dist/prosemirror/textBoxHostParagraph.d.ts +13 -0
  151. package/dist/prosemirror/textBoxHostParagraph.js +21 -0
  152. package/dist/prosemirror/validation.js +91 -80
  153. package/dist/utils/createDocument.js +10 -1
  154. package/dist/utils/scriptSegments.d.ts +33 -4
  155. package/dist/utils/scriptSegments.js +39 -6
  156. package/dist/utils/textFormattingMerge.d.ts +17 -3
  157. package/dist/utils/textFormattingMerge.js +18 -12
  158. package/dist/utils/trailingText.d.ts +22 -0
  159. package/dist/utils/trailingText.js +32 -0
  160. package/package.json +2 -2
@@ -1,6 +1,6 @@
1
1
  import { parseHorizontalScalePercent } from "../utils/horizontalScale.js";
2
2
  import { NO_MODELLED_ATTRIBUTES, attributeRemainder } from "./attributeRemainder.js";
3
- import { CAPTURE, dispatchChildren, keptUnless, ownedElsewhere, sequencePositions } from "./containerChildren.js";
3
+ import { CAPTURE, dispatchChildrenWithContext, keptUnless, ownedElsewhere, sequencePositions } from "./containerChildren.js";
4
4
  import { parseDiagramPreview } from "./diagramPreview.js";
5
5
  import { parseFieldState } from "./fieldState.js";
6
6
  import { isGroupDrawing, parseGroupDrawing } from "./groupDrawingParser.js";
@@ -8,6 +8,7 @@ import { parseImage } from "./imageParser.js";
8
8
  import { imageRawXmlFingerprint } from "./imageRawXml.js";
9
9
  import { EmphasisMarkSchema, FontHintSchema, FontThemeSchema, HighlightColorSchema, PositionalTabAlignmentSchema, PositionalTabLeaderSchema, PositionalTabRelativeToSchema, TextEffectSchema, UnderlineStyleSchema, narrowEnum } from "./parserEnums.js";
10
10
  import { preserveRunChild } from "./preservedRunContent.js";
11
+ import { standalonePreviewLedger } from "./previewBudget.js";
11
12
  import { parseShading } from "./shadingParser.js";
12
13
  import { parseShapeFromDrawing, shouldPreserveRawShapeDrawing } from "./shapeParser.js";
13
14
  import { isTextBoxDrawing } from "./textBoxParser.js";
@@ -69,7 +70,7 @@ const RUN_PROPERTY_OWNERS = {
69
70
  * The children each owner's sibling record claims.
70
71
  *
71
72
  * Not the decision map: that one is total over the schema and lives in
72
- * {@link parseRunProperties}. This names only the children a given owner reads
73
+ * {@link RUN_PROPERTY_HANDLERS}. This names only the children a given owner reads
73
74
  * somewhere else, so both cannot write the same element.
74
75
  */
75
76
  const OWNED_BY_A_SIBLING_RECORD = {
@@ -102,6 +103,16 @@ const OWNED_BY_A_SIBLING_RECORD = {
102
103
  },
103
104
  standalone: {}
104
105
  };
106
+ /** The last statement of each declared property in `rPr`, by local name. */
107
+ const lastOccurrences = (rPr) => {
108
+ const winner = /* @__PURE__ */ new Map();
109
+ for (const child of getChildElements(rPr)) {
110
+ const namespace = getNamespaceUri(child);
111
+ if (namespace !== void 0 && !WORDPROCESSINGML_NAMESPACE_URIS.has(namespace)) continue;
112
+ winner.set(getLocalName(child.name), child);
113
+ }
114
+ return winner;
115
+ };
105
116
  /**
106
117
  * A property stated twice in one `w:rPr` resolves to the last statement.
107
118
  *
@@ -123,17 +134,18 @@ const OWNED_BY_A_SIBLING_RECORD = {
123
134
  * first and a reader that takes the last then read the same value.
124
135
  *
125
136
  * The name comes off the child rather than from the map key, so the guard
126
- * cannot name a property the handler does not read.
137
+ * cannot name a property the handler does not read. `undefined` is "handled,
138
+ * nothing to capture": a beaten statement is neither read nor written.
127
139
  */
128
- const readLastOccurrenceOnly = (rPr) => {
129
- const winner = /* @__PURE__ */ new Map();
130
- for (const child of getChildElements(rPr)) {
131
- const namespace = getNamespaceUri(child);
132
- if (namespace !== void 0 && !WORDPROCESSINGML_NAMESPACE_URIS.has(namespace)) continue;
133
- winner.set(getLocalName(child.name), child);
134
- }
135
- return (read) => (child) => winner.get(getLocalName(child.name)) === child ? read(child) : void 0;
136
- };
140
+ const readLastOccurrenceOnly = (read) => (child, context) => context.winner.get(getLocalName(child.name)) === child ? read(child, context) : void 0;
141
+ const readToggle = (field) => readLastOccurrenceOnly((child, { formatting }) => {
142
+ formatting[field] = parseBooleanElement(child);
143
+ });
144
+ const readNumericVal = (field) => readLastOccurrenceOnly((child, { formatting }) => {
145
+ const val = parseNumericAttribute(child, "w", "val");
146
+ if (val !== void 0) formatting[field] = val;
147
+ return keptUnless(val !== void 0);
148
+ });
137
149
  /**
138
150
  * `w:vertAlign` `baseline` is the reserved value that means "no vertical
139
151
  * offset", not a third offset beside `superscript` and `subscript`. It is
@@ -225,6 +237,131 @@ const readLanguage = (lang, formatting) => {
225
237
  return true;
226
238
  };
227
239
  /**
240
+ * The `w:rPr` decision map, before the owner's own claims. Built once: every
241
+ * reader takes the record it fills from the walk's context rather than
242
+ * closing over it. See {@link parseRunProperties} for what each disposition
243
+ * means.
244
+ */
245
+ const RUN_PROPERTY_BASE_HANDLERS = {
246
+ ins: CAPTURE,
247
+ del: CAPTURE,
248
+ moveFrom: CAPTURE,
249
+ moveTo: CAPTURE,
250
+ rStyle: readLastOccurrenceOnly((child, { formatting }) => {
251
+ const val = getAttribute(child, "w", "val");
252
+ if (val) formatting.styleId = val;
253
+ return keptUnless(Boolean(val));
254
+ }),
255
+ rFonts: readLastOccurrenceOnly((child, { formatting, theme }) => keptUnless(readFontFamily(child, theme, formatting))),
256
+ b: readToggle("bold"),
257
+ bCs: readToggle("boldCs"),
258
+ i: readToggle("italic"),
259
+ iCs: readToggle("italicCs"),
260
+ caps: readToggle("allCaps"),
261
+ smallCaps: readToggle("smallCaps"),
262
+ strike: readToggle("strike"),
263
+ dstrike: readToggle("doubleStrike"),
264
+ outline: readToggle("outline"),
265
+ shadow: readToggle("shadow"),
266
+ emboss: readToggle("emboss"),
267
+ imprint: readToggle("imprint"),
268
+ noProof: readToggle("noProof"),
269
+ vanish: readToggle("hidden"),
270
+ rtl: readToggle("rtl"),
271
+ cs: readToggle("cs"),
272
+ /** Whether the run follows the section's document grid; no layout slot. */
273
+ snapToGrid: CAPTURE,
274
+ /** Web-view-only hiding, distinct from `w:vanish`; nothing reads it. */
275
+ webHidden: CAPTURE,
276
+ color: readLastOccurrenceOnly((child, { formatting }) => {
277
+ const color = parseColorValue({
278
+ rgb: getAttribute(child, "w", "val"),
279
+ themeColor: getAttribute(child, "w", "themeColor"),
280
+ themeTint: getAttribute(child, "w", "themeTint"),
281
+ themeShade: getAttribute(child, "w", "themeShade"),
282
+ element: child.name ?? "w:color"
283
+ });
284
+ if (Object.keys(color).length === 0) return CAPTURE;
285
+ formatting.color = color;
286
+ }),
287
+ spacing: readNumericVal("spacing"),
288
+ w: readLastOccurrenceOnly((child, { formatting }) => {
289
+ const val = parseHorizontalScalePercent(getAttribute(child, "w", "val"));
290
+ if (val !== void 0) formatting.scale = val;
291
+ return keptUnless(val !== void 0);
292
+ }),
293
+ kern: readNumericVal("kerning"),
294
+ position: readNumericVal("position"),
295
+ sz: readNumericVal("fontSize"),
296
+ szCs: readNumericVal("fontSizeCs"),
297
+ highlight: readLastOccurrenceOnly((child, { formatting }) => {
298
+ const val = narrowEnum(getAttribute(child, "w", "val"), HighlightColorSchema);
299
+ if (val) formatting.highlight = val;
300
+ return keptUnless(Boolean(val));
301
+ }),
302
+ u: readLastOccurrenceOnly((child, { formatting }) => keptUnless(readUnderline(child, formatting))),
303
+ effect: readLastOccurrenceOnly((child, { formatting }) => {
304
+ const val = narrowEnum(getAttribute(child, "w", "val"), TextEffectSchema);
305
+ if (val) formatting.effect = val;
306
+ return keptUnless(Boolean(val));
307
+ }),
308
+ /** A text border around the run; `BorderSpec` is a paragraph/table slot. */
309
+ bdr: CAPTURE,
310
+ shd: readLastOccurrenceOnly((child, { formatting }) => {
311
+ const shading = parseShading(child);
312
+ if (shading) formatting.shading = shading;
313
+ return keptUnless(shading !== void 0);
314
+ }),
315
+ /** Compress the run's text into a fixed width; no layout slot holds it. */
316
+ fitText: CAPTURE,
317
+ vertAlign: readLastOccurrenceOnly((child, { formatting }) => {
318
+ const val = getAttribute(child, "w", "val");
319
+ if (val === "superscript" || val === "subscript" || val === "baseline") {
320
+ formatting.vertAlign = val;
321
+ return;
322
+ }
323
+ return CAPTURE;
324
+ }),
325
+ em: readLastOccurrenceOnly((child, { formatting }) => {
326
+ const val = narrowEnum(getAttribute(child, "w", "val"), EmphasisMarkSchema);
327
+ if (val) formatting.emphasisMark = val;
328
+ return keptUnless(Boolean(val));
329
+ }),
330
+ lang: readLastOccurrenceOnly((child, { formatting }) => keptUnless(readLanguage(child, formatting))),
331
+ /** East Asian two-lines-in-one and horizontal-in-vertical typesetting. */
332
+ eastAsianLayout: CAPTURE,
333
+ /**
334
+ * The run-in heading marker. Under a run the schema declares it and folio
335
+ * models nothing for it; a paragraph mark's record reads it instead (see
336
+ * {@link RUN_PROPERTY_HANDLERS}).
337
+ */
338
+ specVanish: CAPTURE,
339
+ /** The run is part of an equation; folio has no run-level maths slot. */
340
+ oMath: CAPTURE,
341
+ /**
342
+ * The tracked property change. A run's record reads it into
343
+ * `Run.propertyChanges`; a paragraph mark has no such record, so its
344
+ * snapshot is kept whole rather than dropped with the revision.
345
+ */
346
+ rPrChange: CAPTURE
347
+ };
348
+ /** Each owner's total `w:rPr` decision map. */
349
+ const RUN_PROPERTY_HANDLERS = {
350
+ run: {
351
+ ...RUN_PROPERTY_BASE_HANDLERS,
352
+ ...OWNED_BY_A_SIBLING_RECORD.run
353
+ },
354
+ paragraphMark: {
355
+ ...RUN_PROPERTY_BASE_HANDLERS,
356
+ specVanish: readLastOccurrenceOnly(() => void 0),
357
+ ...OWNED_BY_A_SIBLING_RECORD.paragraphMark
358
+ },
359
+ standalone: {
360
+ ...RUN_PROPERTY_BASE_HANDLERS,
361
+ ...OWNED_BY_A_SIBLING_RECORD.standalone
362
+ }
363
+ };
364
+ /**
228
365
  * The one reader of a run property set (`w:rPr`).
229
366
  *
230
367
  * Every owner reads through here — a run, the paragraph mark, a style, a
@@ -253,167 +390,16 @@ const readLanguage = (lang, formatting) => {
253
390
  function parseRunProperties(rPr, theme, owner) {
254
391
  if (!rPr) return;
255
392
  const formatting = {};
256
- const wins = readLastOccurrenceOnly(rPr);
257
- const preserved = dispatchChildren({
393
+ const preserved = dispatchChildrenWithContext({
258
394
  element: rPr,
259
395
  container: "run-properties",
260
- handlers: {
261
- ins: CAPTURE,
262
- del: CAPTURE,
263
- moveFrom: CAPTURE,
264
- moveTo: CAPTURE,
265
- rStyle: wins((child) => {
266
- const val = getAttribute(child, "w", "val");
267
- if (val) formatting.styleId = val;
268
- return keptUnless(Boolean(val));
269
- }),
270
- rFonts: wins((child) => keptUnless(readFontFamily(child, theme, formatting))),
271
- b: wins((child) => {
272
- formatting.bold = parseBooleanElement(child);
273
- }),
274
- bCs: wins((child) => {
275
- formatting.boldCs = parseBooleanElement(child);
276
- }),
277
- i: wins((child) => {
278
- formatting.italic = parseBooleanElement(child);
279
- }),
280
- iCs: wins((child) => {
281
- formatting.italicCs = parseBooleanElement(child);
282
- }),
283
- caps: wins((child) => {
284
- formatting.allCaps = parseBooleanElement(child);
285
- }),
286
- smallCaps: wins((child) => {
287
- formatting.smallCaps = parseBooleanElement(child);
288
- }),
289
- strike: wins((child) => {
290
- formatting.strike = parseBooleanElement(child);
291
- }),
292
- dstrike: wins((child) => {
293
- formatting.doubleStrike = parseBooleanElement(child);
294
- }),
295
- outline: wins((child) => {
296
- formatting.outline = parseBooleanElement(child);
297
- }),
298
- shadow: wins((child) => {
299
- formatting.shadow = parseBooleanElement(child);
300
- }),
301
- emboss: wins((child) => {
302
- formatting.emboss = parseBooleanElement(child);
303
- }),
304
- imprint: wins((child) => {
305
- formatting.imprint = parseBooleanElement(child);
306
- }),
307
- noProof: wins((child) => {
308
- formatting.noProof = parseBooleanElement(child);
309
- }),
310
- vanish: wins((child) => {
311
- formatting.hidden = parseBooleanElement(child);
312
- }),
313
- rtl: wins((child) => {
314
- formatting.rtl = parseBooleanElement(child);
315
- }),
316
- cs: wins((child) => {
317
- formatting.cs = parseBooleanElement(child);
318
- }),
319
- /** Whether the run follows the section's document grid; no layout slot. */
320
- snapToGrid: CAPTURE,
321
- /** Web-view-only hiding, distinct from `w:vanish`; nothing reads it. */
322
- webHidden: CAPTURE,
323
- color: wins((child) => {
324
- const color = parseColorValue({
325
- rgb: getAttribute(child, "w", "val"),
326
- themeColor: getAttribute(child, "w", "themeColor"),
327
- themeTint: getAttribute(child, "w", "themeTint"),
328
- themeShade: getAttribute(child, "w", "themeShade"),
329
- element: child.name ?? "w:color"
330
- });
331
- if (Object.keys(color).length === 0) return CAPTURE;
332
- formatting.color = color;
333
- }),
334
- spacing: wins((child) => {
335
- const val = parseNumericAttribute(child, "w", "val");
336
- if (val !== void 0) formatting.spacing = val;
337
- return keptUnless(val !== void 0);
338
- }),
339
- w: wins((child) => {
340
- const val = parseHorizontalScalePercent(getAttribute(child, "w", "val"));
341
- if (val !== void 0) formatting.scale = val;
342
- return keptUnless(val !== void 0);
343
- }),
344
- kern: wins((child) => {
345
- const val = parseNumericAttribute(child, "w", "val");
346
- if (val !== void 0) formatting.kerning = val;
347
- return keptUnless(val !== void 0);
348
- }),
349
- position: wins((child) => {
350
- const val = parseNumericAttribute(child, "w", "val");
351
- if (val !== void 0) formatting.position = val;
352
- return keptUnless(val !== void 0);
353
- }),
354
- sz: wins((child) => {
355
- const val = parseNumericAttribute(child, "w", "val");
356
- if (val !== void 0) formatting.fontSize = val;
357
- return keptUnless(val !== void 0);
358
- }),
359
- szCs: wins((child) => {
360
- const val = parseNumericAttribute(child, "w", "val");
361
- if (val !== void 0) formatting.fontSizeCs = val;
362
- return keptUnless(val !== void 0);
363
- }),
364
- highlight: wins((child) => {
365
- const val = narrowEnum(getAttribute(child, "w", "val"), HighlightColorSchema);
366
- if (val) formatting.highlight = val;
367
- return keptUnless(Boolean(val));
368
- }),
369
- u: wins((child) => keptUnless(readUnderline(child, formatting))),
370
- effect: wins((child) => {
371
- const val = narrowEnum(getAttribute(child, "w", "val"), TextEffectSchema);
372
- if (val) formatting.effect = val;
373
- return keptUnless(Boolean(val));
374
- }),
375
- /** A text border around the run; `BorderSpec` is a paragraph/table slot. */
376
- bdr: CAPTURE,
377
- shd: wins((child) => {
378
- const shading = parseShading(child);
379
- if (shading) formatting.shading = shading;
380
- return keptUnless(shading !== void 0);
381
- }),
382
- /** Compress the run's text into a fixed width; no layout slot holds it. */
383
- fitText: CAPTURE,
384
- vertAlign: wins((child) => {
385
- const val = getAttribute(child, "w", "val");
386
- if (val === "superscript" || val === "subscript" || val === "baseline") {
387
- formatting.vertAlign = val;
388
- return;
389
- }
390
- return CAPTURE;
391
- }),
392
- em: wins((child) => {
393
- const val = narrowEnum(getAttribute(child, "w", "val"), EmphasisMarkSchema);
394
- if (val) formatting.emphasisMark = val;
395
- return keptUnless(Boolean(val));
396
- }),
397
- lang: wins((child) => keptUnless(readLanguage(child, formatting))),
398
- /** East Asian two-lines-in-one and horizontal-in-vertical typesetting. */
399
- eastAsianLayout: CAPTURE,
400
- /**
401
- * The run-in heading marker. On a paragraph mark the paragraph's record
402
- * reads it (`runInWithNext`); under a run the schema declares it and folio
403
- * models nothing for it.
404
- */
405
- specVanish: owner === RUN_PROPERTY_OWNERS.paragraphMark ? wins(() => void 0) : CAPTURE,
406
- /** The run is part of an equation; folio has no run-level maths slot. */
407
- oMath: CAPTURE,
408
- /**
409
- * The tracked property change. A run's record reads it into
410
- * `Run.propertyChanges`; a paragraph mark has no such record, so its
411
- * snapshot is kept whole rather than dropped with the revision.
412
- */
413
- rPrChange: CAPTURE,
414
- ...OWNED_BY_A_SIBLING_RECORD[owner]
415
- },
416
- capturePosition: sequencePositions("run-properties", rPr)
396
+ handlers: RUN_PROPERTY_HANDLERS[owner],
397
+ capturePosition: sequencePositions("run-properties", rPr),
398
+ context: {
399
+ formatting,
400
+ theme,
401
+ winner: lastOccurrences(rPr)
402
+ }
417
403
  });
418
404
  if (preserved) formatting.preserved = preserved;
419
405
  return formatting;
@@ -570,8 +556,8 @@ const preserveOnlyDrawing = (rawXml) => ({
570
556
  * - anything the model cannot project (a group the rasterizer declines,
571
557
  * a diagram, a shape with unmodeled properties) → preservation-only raw XML.
572
558
  */
573
- function parseDrawingContent(element, rels, media) {
574
- const groupImage = parseGroupDrawing(element, rels ?? void 0, media ?? void 0);
559
+ function parseDrawingContent(element, rels, media, previews) {
560
+ const groupImage = parseGroupDrawing(element, previews, rels ?? void 0, media ?? void 0);
575
561
  if (groupImage) return {
576
562
  type: "drawing",
577
563
  image: groupImage,
@@ -612,7 +598,7 @@ function parseDrawingContent(element, rels, media) {
612
598
  /**
613
599
  * Parse all content within a run element
614
600
  */
615
- function parseRunContents(runElement, rels, media, rootXmlns = {}) {
601
+ function parseRunContents(runElement, rels, media, previews, rootXmlns = {}) {
616
602
  const contents = [];
617
603
  const children = getChildElements(runElement);
618
604
  for (const child of children) switch (getLocalName(child.name)) {
@@ -650,12 +636,12 @@ function parseRunContents(runElement, rels, media, rootXmlns = {}) {
650
636
  contents.push({ type: "noBreakHyphen" });
651
637
  break;
652
638
  case "drawing": {
653
- const drawing = parseDrawingContent(child, rels, media);
639
+ const drawing = parseDrawingContent(child, rels, media, previews);
654
640
  if (drawing) contents.push(drawing);
655
641
  break;
656
642
  }
657
643
  case "pict": {
658
- const vmlDrawing = parseVmlImageContent(child, rels, media, rootXmlns);
644
+ const vmlDrawing = parseVmlImageContent(child, rels, media, previews, rootXmlns);
659
645
  if (vmlDrawing) {
660
646
  contents.push(vmlDrawing);
661
647
  break;
@@ -664,7 +650,7 @@ function parseRunContents(runElement, rels, media, rootXmlns = {}) {
664
650
  break;
665
651
  }
666
652
  case "object": {
667
- const objectPreview = parseVmlImageContent(child, rels, media, rootXmlns);
653
+ const objectPreview = parseVmlImageContent(child, rels, media, previews, rootXmlns);
668
654
  contents.push(objectPreview ?? preserveRunChild(child));
669
655
  break;
670
656
  }
@@ -687,7 +673,7 @@ function parseRunContents(runElement, rels, media, rootXmlns = {}) {
687
673
  const choiceTextBoxDrawing = choiceEl ? getChildElements(choiceEl).find((element) => getLocalName(element.name) === "drawing" && isTextBoxDrawing(element)) : void 0;
688
674
  const groupedChoiceDrawing = choiceEl ? getChildElements(choiceEl).find((element) => getLocalName(element.name) === "drawing" && findAllDeep(element, "wpg", "wgp").length > 0) : void 0;
689
675
  if (groupedChoiceDrawing) {
690
- const groupedDrawing = parseDrawingContent(groupedChoiceDrawing, rels, media);
676
+ const groupedDrawing = parseDrawingContent(groupedChoiceDrawing, rels, media, previews);
691
677
  if (groupedDrawing?.type === "drawing" && groupedDrawing.rawXmlMode === DRAWING_RAW_XML_MODES.PREVIEW_ONLY && groupedDrawing.image.src) {
692
678
  contents.push({
693
679
  ...groupedDrawing,
@@ -697,7 +683,7 @@ function parseRunContents(runElement, rels, media, rootXmlns = {}) {
697
683
  }
698
684
  }
699
685
  const fallbackPict = fallbackEl ? getChildElements(fallbackEl).find((el) => getLocalName(el.name) === "pict") : void 0;
700
- const fallbackVml = fallbackPict && !choiceTextBoxDrawing ? parseVmlImageContent(fallbackPict, rels, media, rootXmlns) : null;
686
+ const fallbackVml = fallbackPict && !choiceTextBoxDrawing ? parseVmlImageContent(fallbackPict, rels, media, previews, rootXmlns) : null;
701
687
  if (fallbackVml?.image.src) {
702
688
  fallbackVml.rawXml = captureVerbatimXml(cloneWithXmlnsDeclarations(child, rootXmlns));
703
689
  contents.push(fallbackVml);
@@ -707,13 +693,13 @@ function parseRunContents(runElement, rels, media, rootXmlns = {}) {
707
693
  if (targetEl) for (const innerChild of getChildElements(targetEl)) {
708
694
  const innerName = getLocalName(innerChild.name);
709
695
  if (innerName === "drawing") {
710
- const innerDrawing = parseDrawingContent(innerChild, rels, media);
696
+ const innerDrawing = parseDrawingContent(innerChild, rels, media, previews);
711
697
  if (innerDrawing) {
712
698
  if (innerDrawing.type === "drawing" && (innerDrawing.rawXml !== void 0 || !innerDrawing.image.src)) innerDrawing.rawXml = captureVerbatimXml(child);
713
699
  contents.push(innerDrawing);
714
700
  }
715
701
  } else if (innerName === "pict") {
716
- const innerVml = parseVmlImageContent(innerChild, rels, media, rootXmlns);
702
+ const innerVml = parseVmlImageContent(innerChild, rels, media, previews, rootXmlns);
717
703
  if (innerVml) {
718
704
  innerVml.rawXml = captureVerbatimXml(cloneWithXmlnsDeclarations(child, rootXmlns));
719
705
  contents.push(innerVml);
@@ -721,7 +707,7 @@ function parseRunContents(runElement, rels, media, rootXmlns = {}) {
721
707
  } else contents.push(...parseRunContents({
722
708
  ...targetEl,
723
709
  elements: [innerChild]
724
- }, rels, media, rootXmlns));
710
+ }, rels, media, previews, rootXmlns));
725
711
  }
726
712
  const hasTextBoxBranch = [choiceEl, fallbackEl].some((branch) => getChildElements(branch).some((element) => getLocalName(element.name) === "drawing" && isTextBoxDrawing(element)));
727
713
  if (contents.length === contentsBeforeAlternate && !hasTextBoxBranch) contents.push(preserveOnlyDrawing(captureVerbatimXml(cloneWithXmlnsDeclarations(child, rootXmlns))));
@@ -742,9 +728,11 @@ function parseRunContents(runElement, rels, media, rootXmlns = {}) {
742
728
  * @param theme - Theme for resolving theme colors/fonts
743
729
  * @param rels - Relationship map for resolving image references
744
730
  * @param media - Media files map for image data
731
+ * @param previews - The ledger of the package this run belongs to; a run read
732
+ * on its own charges its previews to no package
745
733
  * @returns Parsed Run object
746
734
  */
747
- function parseRun(node, _styles, theme, rels = null, media = null, rootXmlns = {}) {
735
+ function parseRun(node, _styles, theme, rels = null, media = null, rootXmlns = {}, previews = standalonePreviewLedger()) {
748
736
  const run = {
749
737
  type: "run",
750
738
  content: []
@@ -756,7 +744,7 @@ function parseRun(node, _styles, theme, rels = null, media = null, rootXmlns = {
756
744
  const propertyChangesResult = parseRunPropertyChanges(rPr, theme, run.formatting);
757
745
  if (propertyChangesResult) run.propertyChanges = propertyChangesResult;
758
746
  }
759
- run.content = parseRunContents(node, rels, media, mergeXmlnsDeclarations(rootXmlns, node));
747
+ run.content = parseRunContents(node, rels, media, previews, mergeXmlnsDeclarations(rootXmlns, node));
760
748
  const remainder = attributeRemainder({
761
749
  element: node,
762
750
  modelled: NO_MODELLED_ATTRIBUTES
@@ -1,4 +1,4 @@
1
- import { CAPTURE, dispatchChildren, keptUnless, sequencePositions } from "./containerChildren.js";
1
+ import { CAPTURE, dispatchChildrenWithContext, keptUnless, sequencePositions } from "./containerChildren.js";
2
2
  import { SdtLockSchema, narrowEnum } from "./parserEnums.js";
3
3
  import { RUN_PROPERTY_OWNERS, parseRunProperties } from "./runParser.js";
4
4
  import { captureVerbatimXml } from "./verbatimCapture.js";
@@ -287,6 +287,88 @@ const captureSdtSiblingMarkers = (sdt) => {
287
287
  after: afterParts.join("")
288
288
  };
289
289
  };
290
+ /** Read a projection off a control-kind element, and keep the element. */
291
+ const kind = (read) => (element, props) => {
292
+ read(props, element);
293
+ return CAPTURE;
294
+ };
295
+ const SDT_PROPERTY_HANDLERS = {
296
+ rPr: CAPTURE,
297
+ alias: (element, props) => {
298
+ const value = getAttributeAnyPrefix(element, "val");
299
+ if (value !== null) props.alias = value;
300
+ return keptUnless(value !== null);
301
+ },
302
+ tag: (element, props) => {
303
+ const value = getAttributeAnyPrefix(element, "val");
304
+ if (value !== null) props.tag = value;
305
+ return keptUnless(value !== null);
306
+ },
307
+ id: (element, props) => {
308
+ const raw = getAttributeAnyPrefix(element, "val");
309
+ const value = raw === null ? NaN : Number.parseInt(raw, 10);
310
+ if (!Number.isNaN(value)) props.id = value;
311
+ return keptUnless(!Number.isNaN(value));
312
+ },
313
+ lock: (element, props) => {
314
+ const value = narrowEnum(getAttributeAnyPrefix(element, "val"), SdtLockSchema);
315
+ if (value !== void 0) props.lock = value;
316
+ return keptUnless(value !== void 0);
317
+ },
318
+ placeholder: (element, props) => {
319
+ const value = getAttributeAnyPrefix(findChild(element, "w", "docPart"), "val");
320
+ if (value !== null) props.placeholder = value;
321
+ return keptUnless(value !== null);
322
+ },
323
+ temporary: CAPTURE,
324
+ showingPlcHdr: (element, props) => {
325
+ props.showingPlaceholder = parseBooleanElement(element, "w");
326
+ },
327
+ dataBinding: CAPTURE,
328
+ label: CAPTURE,
329
+ tabIndex: CAPTURE,
330
+ equation: kind(() => {}),
331
+ comboBox: kind((props, element) => {
332
+ props.sdtType = "comboBox";
333
+ props.listItems = parseListItems(element);
334
+ readLastValue(element, props);
335
+ }),
336
+ date: kind((props, element) => {
337
+ props.sdtType = "date";
338
+ const format = getAttributeAnyPrefix(findChild(element, "w", "dateFormat"), "val");
339
+ if (format !== null) props.dateFormat = format;
340
+ const fullDate = getAttributeAnyPrefix(element, "fullDate");
341
+ if (fullDate !== null) props.dateValueISO = fullDate;
342
+ }),
343
+ docPartObj: kind((props) => {
344
+ props.sdtType = "buildingBlockGallery";
345
+ }),
346
+ docPartList: kind((props) => {
347
+ props.sdtType = "buildingBlockGallery";
348
+ }),
349
+ dropDownList: kind((props, element) => {
350
+ props.sdtType = "dropdown";
351
+ props.listItems = parseListItems(element);
352
+ readLastValue(element, props);
353
+ }),
354
+ picture: kind((props) => {
355
+ props.sdtType = "picture";
356
+ }),
357
+ richText: kind(() => {}),
358
+ text: kind((props) => {
359
+ props.sdtType = "plainText";
360
+ }),
361
+ citation: kind(() => {}),
362
+ group: kind((props) => {
363
+ props.sdtType = "group";
364
+ }),
365
+ bibliography: kind(() => {})
366
+ };
367
+ const SDT_PROPERTY_UNDECLARED = { [CHECKBOX_CHILD]: kind((props, element) => {
368
+ props.sdtType = "checkbox";
369
+ const state = findChild(element, "w14", "checked") ?? findChild(element, "w", "checked");
370
+ props.checked = state === null ? false : parseBooleanElement(state, "w14");
371
+ }) };
290
372
  /**
291
373
  * Parse `<w:sdtPr>` (and optional `<w:sdtEndPr>`) into {@link SdtProperties}.
292
374
  *
@@ -301,92 +383,13 @@ const captureSdtSiblingMarkers = (sdt) => {
301
383
  function parseSdtProperties(sdtPr, sdtEndPr) {
302
384
  const props = { sdtType: "richText" };
303
385
  if (sdtPr) {
304
- /** Read a projection off a control-kind element, and keep the element. */
305
- const kind = (read) => (element) => {
306
- read(element);
307
- return CAPTURE;
308
- };
309
- const preserved = dispatchChildren({
386
+ const preserved = dispatchChildrenWithContext({
310
387
  element: sdtPr,
311
388
  container: "content-control-properties",
312
- handlers: {
313
- rPr: CAPTURE,
314
- alias: (element) => {
315
- const value = getAttributeAnyPrefix(element, "val");
316
- if (value !== null) props.alias = value;
317
- return keptUnless(value !== null);
318
- },
319
- tag: (element) => {
320
- const value = getAttributeAnyPrefix(element, "val");
321
- if (value !== null) props.tag = value;
322
- return keptUnless(value !== null);
323
- },
324
- id: (element) => {
325
- const raw = getAttributeAnyPrefix(element, "val");
326
- const value = raw === null ? NaN : Number.parseInt(raw, 10);
327
- if (!Number.isNaN(value)) props.id = value;
328
- return keptUnless(!Number.isNaN(value));
329
- },
330
- lock: (element) => {
331
- const value = narrowEnum(getAttributeAnyPrefix(element, "val"), SdtLockSchema);
332
- if (value !== void 0) props.lock = value;
333
- return keptUnless(value !== void 0);
334
- },
335
- placeholder: (element) => {
336
- const value = getAttributeAnyPrefix(findChild(element, "w", "docPart"), "val");
337
- if (value !== null) props.placeholder = value;
338
- return keptUnless(value !== null);
339
- },
340
- temporary: CAPTURE,
341
- showingPlcHdr: (element) => {
342
- props.showingPlaceholder = parseBooleanElement(element, "w");
343
- },
344
- dataBinding: CAPTURE,
345
- label: CAPTURE,
346
- tabIndex: CAPTURE,
347
- equation: kind(() => {}),
348
- comboBox: kind((element) => {
349
- props.sdtType = "comboBox";
350
- props.listItems = parseListItems(element);
351
- readLastValue(element, props);
352
- }),
353
- date: kind((element) => {
354
- props.sdtType = "date";
355
- const format = getAttributeAnyPrefix(findChild(element, "w", "dateFormat"), "val");
356
- if (format !== null) props.dateFormat = format;
357
- const fullDate = getAttributeAnyPrefix(element, "fullDate");
358
- if (fullDate !== null) props.dateValueISO = fullDate;
359
- }),
360
- docPartObj: kind(() => {
361
- props.sdtType = "buildingBlockGallery";
362
- }),
363
- docPartList: kind(() => {
364
- props.sdtType = "buildingBlockGallery";
365
- }),
366
- dropDownList: kind((element) => {
367
- props.sdtType = "dropdown";
368
- props.listItems = parseListItems(element);
369
- readLastValue(element, props);
370
- }),
371
- picture: kind(() => {
372
- props.sdtType = "picture";
373
- }),
374
- richText: kind(() => {}),
375
- text: kind(() => {
376
- props.sdtType = "plainText";
377
- }),
378
- citation: kind(() => {}),
379
- group: kind(() => {
380
- props.sdtType = "group";
381
- }),
382
- bibliography: kind(() => {})
383
- },
389
+ handlers: SDT_PROPERTY_HANDLERS,
384
390
  capturePosition: sequencePositions("content-control-properties", sdtPr),
385
- undeclared: { [CHECKBOX_CHILD]: kind((element) => {
386
- props.sdtType = "checkbox";
387
- const state = findChild(element, "w14", "checked") ?? findChild(element, "w", "checked");
388
- props.checked = state === null ? false : parseBooleanElement(state, "w14");
389
- }) }
391
+ undeclared: SDT_PROPERTY_UNDECLARED,
392
+ context: props
390
393
  });
391
394
  if (preserved !== void 0) props.preserved = preserved;
392
395
  }