@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
@@ -141,6 +141,15 @@ function getFormatSwitch(instruction) {
141
141
  return instruction.switches.find((s) => s.switch === "*" || s.switch === "@")?.value;
142
142
  }
143
143
  /**
144
+ * Get the numeric picture switch value (\#), e.g. `0#` in `PAGE \# 0#`.
145
+ *
146
+ * @param instruction - Parsed instruction
147
+ * @returns Picture string or undefined
148
+ */
149
+ function getNumericPictureSwitch(instruction) {
150
+ return instruction.switches.find((s) => s.switch === "#")?.value;
151
+ }
152
+ /**
144
153
  * Check if field has MERGEFORMAT switch (preserve formatting)
145
154
  *
146
155
  * @param instruction - Parsed instruction
@@ -345,16 +354,51 @@ function isTocField(field) {
345
354
  * @returns Formatted page number string
346
355
  */
347
356
  function computePageNumber(pageNumber, instruction, sectionFormat) {
357
+ const picture = instruction ? getNumericPictureSwitch(instruction) : void 0;
358
+ if (picture !== void 0) return applyNumericPicture(pageNumber, picture);
348
359
  const format = instruction ? getFormatSwitch(instruction) : void 0;
349
360
  if (!format || format.toUpperCase() === "MERGEFORMAT") return formatSectionPageNumber(pageNumber, sectionFormat);
361
+ const isLowercaseSwitch = format === format.toLowerCase();
350
362
  switch (format.toUpperCase()) {
351
- case "ROMAN": return toRoman(pageNumber);
352
- case "ALPHABETIC": return toLetter(pageNumber);
363
+ case "ROMAN": {
364
+ const roman = toRoman(pageNumber);
365
+ return isLowercaseSwitch ? roman.toLowerCase() : roman;
366
+ }
367
+ case "ALPHABETIC": {
368
+ const letters = toLetter(pageNumber);
369
+ return isLowercaseSwitch ? letters.toLowerCase() : letters;
370
+ }
353
371
  default: return String(pageNumber);
354
372
  }
355
373
  }
356
374
  const formatSectionPageNumber = (pageNumber, format) => formatOoxmlCounter(pageNumber, format);
357
375
  /**
376
+ * Apply a `\#` numeric picture (ECMA-376 §17.16.4.2) to an integer field
377
+ * value. `0` is a mandatory digit placeholder that zero-pads once the value
378
+ * runs out of digits; `#` is an optional placeholder that is left blank
379
+ * instead; every other picture character (including a thousands separator)
380
+ * is copied through literally. Placeholders line up with the value's digits
381
+ * from the right; digits that reach past the leftmost placeholder are kept,
382
+ * not truncated.
383
+ */
384
+ function applyNumericPicture(value, picture) {
385
+ const negative = value < 0;
386
+ const digits = [...String(Math.trunc(Math.abs(value)))];
387
+ const pictureChars = [...picture];
388
+ const output = Array.from({ length: pictureChars.length });
389
+ let digitIndex = digits.length - 1;
390
+ for (let i = pictureChars.length - 1; i >= 0; i--) {
391
+ const char = pictureChars[i];
392
+ if (char === "0" || char === "#") if (digitIndex >= 0) {
393
+ output[i] = digits[digitIndex];
394
+ digitIndex--;
395
+ } else output[i] = char === "0" ? "0" : "";
396
+ else output[i] = char;
397
+ }
398
+ const overflow = digitIndex >= 0 ? digits.slice(0, digitIndex + 1).join("") : "";
399
+ return (negative ? "-" : "") + overflow + output.join("");
400
+ }
401
+ /**
358
402
  * Convert number to uppercase Roman numerals
359
403
  */
360
404
  function toRoman(num) {
@@ -555,4 +599,4 @@ function getMergeFieldNames(fields) {
555
599
  }).filter((name) => !!name);
556
600
  }
557
601
  //#endregion
558
- export { KNOWN_FIELD_TYPES, collectFields, computePageNumber, createComplexFieldContext, finalizeComplexField, formatDate, getFieldDisplayValue, getFieldsByType, getFormatSwitch, getMergeFieldNames, getMergeFields, getPageNumberFields, hasMergeFormat, isDateTimeField, isDocPropertyField, isHyperlinkField, isKnownFieldType, isMergeField, isPageNumberField, isReferenceField, isTocField, isTotalPagesField, parseFieldInstruction, parseFieldType, parseSimpleField, resetComplexFieldContext };
602
+ export { KNOWN_FIELD_TYPES, collectFields, computePageNumber, createComplexFieldContext, finalizeComplexField, formatDate, getFieldDisplayValue, getFieldsByType, getFormatSwitch, getMergeFieldNames, getMergeFields, getNumericPictureSwitch, getPageNumberFields, hasMergeFormat, isDateTimeField, isDocPropertyField, isHyperlinkField, isKnownFieldType, isMergeField, isPageNumberField, isReferenceField, isTocField, isTotalPagesField, parseFieldInstruction, parseFieldType, parseSimpleField, resetComplexFieldContext };
@@ -1,5 +1,5 @@
1
1
  import { DERIVED_PART_ROOT_ATTRIBUTES, attributeRemainder } from "./attributeRemainder.js";
2
- import { CAPTURE, dispatchChildren } from "./containerChildren.js";
2
+ import { CAPTURE, dispatchChildrenWithContext } from "./containerChildren.js";
3
3
  import { getAttribute, parseXmlDocument } from "./xmlParser.js";
4
4
  //#region src/docx/fontTableParser.ts
5
5
  const FONT_FAMILIES = [
@@ -22,14 +22,12 @@ const parseFontTable = (xml) => {
22
22
  const root = parseXmlDocument(xml);
23
23
  if (!root) return;
24
24
  const fonts = [];
25
- const preserved = dispatchChildren({
25
+ const preserved = dispatchChildrenWithContext({
26
26
  element: root,
27
27
  container: "w:fonts",
28
28
  capturePosition: () => fonts.length,
29
- handlers: { font: (element) => {
30
- const font = parseFont(element);
31
- if (font) fonts.push(font);
32
- } }
29
+ handlers: FONT_TABLE_HANDLERS,
30
+ context: fonts
33
31
  });
34
32
  const preservedAttributes = attributeRemainder({
35
33
  element: root,
@@ -45,31 +43,18 @@ const parseFontTable = (xml) => {
45
43
  const parseFont = (element) => {
46
44
  const name = getAttribute(element, "w", "name");
47
45
  if (!name) return;
48
- const font = { name };
49
- let modelled = 0;
50
- const read = (key, value) => {
51
- if (value === void 0) return;
52
- font[key] = value;
53
- modelled += 1;
46
+ const walk = {
47
+ font: { name },
48
+ modelled: 0
54
49
  };
55
- const preserved = dispatchChildren({
50
+ const preserved = dispatchChildrenWithContext({
56
51
  element,
57
52
  container: "w:font",
58
- capturePosition: () => modelled,
59
- handlers: {
60
- altName: (child) => read("altName", value(child)),
61
- panose1: (child) => read("panose1", value(child)),
62
- charset: (child) => read("charset", charsetOf(child)),
63
- family: (child) => read("family", narrow(value(child), FONT_FAMILIES)),
64
- notTrueType: CAPTURE,
65
- pitch: (child) => read("pitch", narrow(value(child), FONT_PITCHES)),
66
- sig: (child) => read("sig", signatureOf(child)),
67
- embedRegular: (child) => read("embedRegular", embedOf(child)),
68
- embedBold: (child) => read("embedBold", embedOf(child)),
69
- embedItalic: (child) => read("embedItalic", embedOf(child)),
70
- embedBoldItalic: (child) => read("embedBoldItalic", embedOf(child))
71
- }
53
+ capturePosition: () => walk.modelled,
54
+ handlers: FONT_HANDLERS,
55
+ context: walk
72
56
  });
57
+ const { font } = walk;
73
58
  if (preserved !== void 0) font.preserved = preserved;
74
59
  const preservedAttributes = attributeRemainder({
75
60
  element,
@@ -129,5 +114,27 @@ const signatureOf = (element) => {
129
114
  return Object.keys(signature).length === 0 ? void 0 : signature;
130
115
  };
131
116
  const narrow = (carried, members) => members.find((member) => member === carried);
117
+ const FONT_TABLE_HANDLERS = { font: (element, fonts) => {
118
+ const font = parseFont(element);
119
+ if (font) fonts.push(font);
120
+ } };
121
+ const read = (walk, key, carried) => {
122
+ if (carried === void 0) return;
123
+ walk.font[key] = carried;
124
+ walk.modelled += 1;
125
+ };
126
+ const FONT_HANDLERS = {
127
+ altName: (child, walk) => read(walk, "altName", value(child)),
128
+ panose1: (child, walk) => read(walk, "panose1", value(child)),
129
+ charset: (child, walk) => read(walk, "charset", charsetOf(child)),
130
+ family: (child, walk) => read(walk, "family", narrow(value(child), FONT_FAMILIES)),
131
+ notTrueType: CAPTURE,
132
+ pitch: (child, walk) => read(walk, "pitch", narrow(value(child), FONT_PITCHES)),
133
+ sig: (child, walk) => read(walk, "sig", signatureOf(child)),
134
+ embedRegular: (child, walk) => read(walk, "embedRegular", embedOf(child)),
135
+ embedBold: (child, walk) => read(walk, "embedBold", embedOf(child)),
136
+ embedItalic: (child, walk) => read(walk, "embedItalic", embedOf(child)),
137
+ embedBoldItalic: (child, walk) => read(walk, "embedBoldItalic", embedOf(child))
138
+ };
132
139
  //#endregion
133
140
  export { parseFontTable };
@@ -1,6 +1,7 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
2
  import { NumberingMap } from "./numberingParser.js";
3
3
  import { ParseContext } from "./parseContext.js";
4
+ import { PreviewLedger } from "./previewBudget.js";
4
5
  import { StyleMap } from "./styleParser.js";
5
6
  import { parseEndnoteProperties, parseFootnoteProperties } from "./notePropertiesParser.js";
6
7
  //#region src/docx/footnoteParser.d.ts
@@ -53,7 +54,7 @@ type EndnoteMap = {
53
54
  * @param media - Media files for images
54
55
  * @returns FootnoteMap with all footnotes
55
56
  */
56
- declare function parseFootnotes(footnotesXml: string | null, styles?: StyleMap | null, theme?: document_d_exports.Theme | null, numbering?: NumberingMap | null, rels?: document_d_exports.RelationshipMap | null, media?: Map<string, document_d_exports.MediaFile> | null, context?: ParseContext): FootnoteMap;
57
+ declare function parseFootnotes(footnotesXml: string | null, styles?: StyleMap | null, theme?: document_d_exports.Theme | null, numbering?: NumberingMap | null, rels?: document_d_exports.RelationshipMap | null, media?: Map<string, document_d_exports.MediaFile> | null, context?: ParseContext, previews?: PreviewLedger): FootnoteMap;
57
58
  /**
58
59
  * Parse endnotes.xml
59
60
  *
@@ -65,7 +66,7 @@ declare function parseFootnotes(footnotesXml: string | null, styles?: StyleMap |
65
66
  * @param media - Media files for images
66
67
  * @returns EndnoteMap with all endnotes
67
68
  */
68
- declare function parseEndnotes(endnotesXml: string | null, styles?: StyleMap | null, theme?: document_d_exports.Theme | null, numbering?: NumberingMap | null, rels?: document_d_exports.RelationshipMap | null, media?: Map<string, document_d_exports.MediaFile> | null, context?: ParseContext): EndnoteMap;
69
+ declare function parseEndnotes(endnotesXml: string | null, styles?: StyleMap | null, theme?: document_d_exports.Theme | null, numbering?: NumberingMap | null, rels?: document_d_exports.RelationshipMap | null, media?: Map<string, document_d_exports.MediaFile> | null, context?: ParseContext, previews?: PreviewLedger): EndnoteMap;
69
70
  /**
70
71
  * Get plain text content of a footnote.
71
72
  *
@@ -1,6 +1,7 @@
1
1
  import { blockPlainText } from "./blockPlainText.js";
2
2
  import { parseEndnoteProperties, parseFootnoteProperties } from "./notePropertiesParser.js";
3
3
  import { parseParagraph } from "./paragraphParser.js";
4
+ import { standalonePreviewLedger } from "./previewBudget.js";
4
5
  import { captureSdtSiblingMarkers, parseSdtProperties } from "./sdtProperties.js";
5
6
  import { parseTable } from "./tableParser.js";
6
7
  import { findChildren, findWordprocessingChild, getAttributes, getChildElements, getLocalName, parseXml } from "./xmlParser.js";
@@ -27,13 +28,13 @@ function parseNoteId(element) {
27
28
  const parsed = Number.parseInt(id, 10);
28
29
  return Number.isNaN(parsed) ? 0 : parsed;
29
30
  }
30
- function parseNoteBlockContent(element, styles, theme, numbering, rels, media) {
31
+ function parseNoteBlockContent(element, styles, theme, numbering, rels, media, previews) {
31
32
  const blocks = [];
32
33
  for (const child of getChildElements(element)) {
33
34
  const localName = getLocalName(child.name ?? "");
34
- if (localName === "p") blocks.push(parseParagraph(child, styles, theme, numbering, rels));
35
+ if (localName === "p") blocks.push(parseParagraph(child, styles, theme, numbering, rels, media, { previews }));
35
36
  else if (localName === "tbl") {
36
- const table = parseTable(child, styles, theme, numbering, rels, media);
37
+ const table = parseTable(child, styles, theme, numbering, rels, media, { previews });
37
38
  if (table) blocks.push(table);
38
39
  } else if (localName === "sdt") {
39
40
  const sdtPr = findWordprocessingChild(child, "sdtPr");
@@ -46,7 +47,7 @@ function parseNoteBlockContent(element, styles, theme, numbering, rels, media) {
46
47
  blocks.push({
47
48
  type: "blockSdt",
48
49
  properties,
49
- content: sdtContent ? parseNoteBlockContent(sdtContent, styles, theme, numbering, rels, media) : []
50
+ content: sdtContent ? parseNoteBlockContent(sdtContent, styles, theme, numbering, rels, media, previews) : []
50
51
  });
51
52
  }
52
53
  }
@@ -55,12 +56,12 @@ function parseNoteBlockContent(element, styles, theme, numbering, rels, media) {
55
56
  /**
56
57
  * Parse a single footnote element (w:footnote)
57
58
  */
58
- function parseFootnote(element, styles, theme, numbering, rels, media) {
59
+ function parseFootnote(element, styles, theme, numbering, rels, media, previews) {
59
60
  return {
60
61
  type: "footnote",
61
62
  id: parseNoteId(element),
62
63
  noteType: parseNoteType(getNoteAttribute(element, "type")),
63
- content: parseNoteBlockContent(element, styles, theme, numbering, rels, media)
64
+ content: parseNoteBlockContent(element, styles, theme, numbering, rels, media, previews)
64
65
  };
65
66
  }
66
67
  /**
@@ -74,7 +75,7 @@ function parseFootnote(element, styles, theme, numbering, rels, media) {
74
75
  * @param media - Media files for images
75
76
  * @returns FootnoteMap with all footnotes
76
77
  */
77
- function parseFootnotes(footnotesXml, styles = null, theme = null, numbering = null, rels = null, media = null, context) {
78
+ function parseFootnotes(footnotesXml, styles = null, theme = null, numbering = null, rels = null, media = null, context, previews = standalonePreviewLedger()) {
78
79
  const byId = /* @__PURE__ */ new Map();
79
80
  const footnotes = [];
80
81
  if (!footnotesXml) return createFootnoteMap(byId, footnotes);
@@ -82,13 +83,14 @@ function parseFootnotes(footnotesXml, styles = null, theme = null, numbering = n
82
83
  if (!rootElement) return createFootnoteMap(byId, footnotes);
83
84
  const footnoteElements = findChildren(rootElement, "w", "footnote");
84
85
  for (const fnEl of footnoteElements) {
85
- const footnote = parseFootnote(fnEl, styles, theme, numbering, rels, media);
86
+ const footnote = parseFootnote(fnEl, styles, theme, numbering, rels, media, previews);
86
87
  if (byId.has(footnote.id)) {
87
88
  context?.warn({
88
89
  code: PARSE_WARNING_CODES.duplicateNoteId,
89
90
  element: "w:footnote",
90
91
  at: `w:id ${String(footnote.id)}`
91
92
  });
93
+ previews.release(footnote);
92
94
  continue;
93
95
  }
94
96
  byId.set(footnote.id, footnote);
@@ -123,12 +125,12 @@ function createFootnoteMap(byId, footnotes) {
123
125
  /**
124
126
  * Parse a single endnote element (w:endnote)
125
127
  */
126
- function parseEndnote(element, styles, theme, numbering, rels, media) {
128
+ function parseEndnote(element, styles, theme, numbering, rels, media, previews) {
127
129
  return {
128
130
  type: "endnote",
129
131
  id: parseNoteId(element),
130
132
  noteType: parseNoteType(getNoteAttribute(element, "type")),
131
- content: parseNoteBlockContent(element, styles, theme, numbering, rels, media)
133
+ content: parseNoteBlockContent(element, styles, theme, numbering, rels, media, previews)
132
134
  };
133
135
  }
134
136
  /**
@@ -142,7 +144,7 @@ function parseEndnote(element, styles, theme, numbering, rels, media) {
142
144
  * @param media - Media files for images
143
145
  * @returns EndnoteMap with all endnotes
144
146
  */
145
- function parseEndnotes(endnotesXml, styles = null, theme = null, numbering = null, rels = null, media = null, context) {
147
+ function parseEndnotes(endnotesXml, styles = null, theme = null, numbering = null, rels = null, media = null, context, previews = standalonePreviewLedger()) {
146
148
  const byId = /* @__PURE__ */ new Map();
147
149
  const endnotes = [];
148
150
  if (!endnotesXml) return createEndnoteMap(byId, endnotes);
@@ -150,13 +152,14 @@ function parseEndnotes(endnotesXml, styles = null, theme = null, numbering = nul
150
152
  if (!rootElement) return createEndnoteMap(byId, endnotes);
151
153
  const endnoteElements = findChildren(rootElement, "w", "endnote");
152
154
  for (const enEl of endnoteElements) {
153
- const endnote = parseEndnote(enEl, styles, theme, numbering, rels, media);
155
+ const endnote = parseEndnote(enEl, styles, theme, numbering, rels, media, previews);
154
156
  if (byId.has(endnote.id)) {
155
157
  context?.warn({
156
158
  code: PARSE_WARNING_CODES.duplicateNoteId,
157
159
  element: "w:endnote",
158
160
  at: `w:id ${String(endnote.id)}`
159
161
  });
162
+ previews.release(endnote);
160
163
  continue;
161
164
  }
162
165
  byId.set(endnote.id, endnote);
@@ -1,5 +1,6 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
2
  import { XmlElement } from "./xmlParser.js";
3
+ import { PreviewLedger } from "./previewBudget.js";
3
4
  //#region src/docx/groupDrawingParser.d.ts
4
5
  /**
5
6
  * Whether a `w:drawing` carries a WordprocessingGroup payload. A group this
@@ -8,6 +9,6 @@ import { XmlElement } from "./xmlParser.js";
8
9
  */
9
10
  declare const isGroupDrawing: (drawing: XmlElement) => boolean;
10
11
  /** Parse a WordprocessingGroup drawing into a safe SVG-backed image preview. */
11
- declare const parseGroupDrawing: (drawing: XmlElement, rels?: document_d_exports.RelationshipMap, media?: Map<string, document_d_exports.MediaFile>) => document_d_exports.Image | null;
12
+ declare const parseGroupDrawing: (drawing: XmlElement, previews: PreviewLedger, rels?: document_d_exports.RelationshipMap, media?: Map<string, document_d_exports.MediaFile>) => document_d_exports.Image | null;
12
13
  //#endregion
13
14
  export { isGroupDrawing, parseGroupDrawing };
@@ -1,6 +1,5 @@
1
1
  import { emuToPixels } from "../utils/units.js";
2
2
  import { parseImage, resolveImageData } from "./imageParser.js";
3
- import { PREVIEW_KINDS } from "./previewBudget.js";
4
3
  import { findAllDeep, findChildByLocalName, findChildrenByLocalName, getAttribute, getChildElements, getLocalName, getTextContent, parseNumericAttribute } from "./xmlParser.js";
5
4
  import { escapeXmlAttribute, escapeXmlText } from "@stll/docx-core";
6
5
  //#region src/docx/groupDrawingParser.ts
@@ -176,17 +175,15 @@ const groupElement = (drawing) => {
176
175
  */
177
176
  const isGroupDrawing = (drawing) => groupElement(drawing) !== null;
178
177
  /** Parse a WordprocessingGroup drawing into a safe SVG-backed image preview. */
179
- const parseGroupDrawing = (drawing, rels, media) => {
178
+ const parseGroupDrawing = (drawing, previews, rels, media) => {
180
179
  const group = groupElement(drawing);
181
180
  if (!group) return null;
182
181
  const image = parseImage(drawing, void 0, void 0);
183
182
  if (!image || image.size.width <= 0 || image.size.height <= 0) return null;
184
183
  const svg = createSvg(group, image.size.width, image.size.height, rels, media);
185
184
  if (!svg || svg.length > MAX_SVG_CHARACTERS) return null;
186
- image.src = `${PREVIEW_KINDS.wpGroup.srcPrefix}${encodeURIComponent(svg)}`;
187
- image.mimeType = PREVIEW_KINDS.wpGroup.mimeType;
188
- image.filename = PREVIEW_KINDS.wpGroup.filename;
189
- return image;
185
+ const { rId: _rId, src: _src, mimeType: _mimeType, filename: _filename, ...frame } = image;
186
+ return previews.svgImage("wpGroup", svg, frame);
190
187
  };
191
188
  //#endregion
192
189
  export { isGroupDrawing, parseGroupDrawing };
@@ -1,5 +1,6 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
2
  import { NumberingMap } from "./numberingParser.js";
3
+ import { PreviewLedger } from "./previewBudget.js";
3
4
  import { StyleMap } from "./styleParser.js";
4
5
  import { parseFooterReference, parseFooterReferences, parseHeaderReference, parseHeaderReferences } from "./headerFooterRefParser.js";
5
6
  //#region src/docx/headerFooterParser.d.ts
@@ -28,9 +29,11 @@ type HeaderFooterMap = {
28
29
  * @param numbering - Parsed numbering definitions for lists
29
30
  * @param rels - Relationships for resolving hyperlinks/images
30
31
  * @param media - Media files for images
32
+ * @param previews - The ledger of the package this part belongs to; a part
33
+ * read on its own charges its previews to no package
31
34
  * @returns HeaderFooter object
32
35
  */
33
- declare function parseHeader(headerXml: string, hdrFtrType?: document_d_exports.HeaderFooterType, styles?: StyleMap | null, theme?: document_d_exports.Theme | null, numbering?: NumberingMap | null, rels?: document_d_exports.RelationshipMap | null, media?: Map<string, document_d_exports.MediaFile> | null): document_d_exports.HeaderFooter;
36
+ declare function parseHeader(headerXml: string, hdrFtrType?: document_d_exports.HeaderFooterType, styles?: StyleMap | null, theme?: document_d_exports.Theme | null, numbering?: NumberingMap | null, rels?: document_d_exports.RelationshipMap | null, media?: Map<string, document_d_exports.MediaFile> | null, previews?: PreviewLedger): document_d_exports.HeaderFooter;
34
37
  /**
35
38
  * Parse a footer XML file (word/footer*.xml)
36
39
  *
@@ -41,9 +44,11 @@ declare function parseHeader(headerXml: string, hdrFtrType?: document_d_exports.
41
44
  * @param numbering - Parsed numbering definitions for lists
42
45
  * @param rels - Relationships for resolving hyperlinks/images
43
46
  * @param media - Media files for images
47
+ * @param previews - The ledger of the package this part belongs to; a part
48
+ * read on its own charges its previews to no package
44
49
  * @returns HeaderFooter object
45
50
  */
46
- declare function parseFooter(footerXml: string, hdrFtrType?: document_d_exports.HeaderFooterType, styles?: StyleMap | null, theme?: document_d_exports.Theme | null, numbering?: NumberingMap | null, rels?: document_d_exports.RelationshipMap | null, media?: Map<string, document_d_exports.MediaFile> | null): document_d_exports.HeaderFooter;
51
+ declare function parseFooter(footerXml: string, hdrFtrType?: document_d_exports.HeaderFooterType, styles?: StyleMap | null, theme?: document_d_exports.Theme | null, numbering?: NumberingMap | null, rels?: document_d_exports.RelationshipMap | null, media?: Map<string, document_d_exports.MediaFile> | null, previews?: PreviewLedger): document_d_exports.HeaderFooter;
47
52
  /**
48
53
  * Generic function to parse either header or footer
49
54
  *
@@ -3,6 +3,7 @@ import { blockPlainText } from "./blockPlainText.js";
3
3
  import { parseFooterReference, parseFooterReferences, parseHeaderReference, parseHeaderReferences } from "./headerFooterRefParser.js";
4
4
  import { assignHeaderFooterVerbatimXml } from "./headerFooterVerbatim.js";
5
5
  import { cloneParagraphWithPropertySource } from "./paragraphPropertySource.js";
6
+ import { standalonePreviewLedger } from "./previewBudget.js";
6
7
  import { parseWatermark } from "./watermarkParser.js";
7
8
  import { collectXmlnsDeclarations, parseXml } from "./xmlParser.js";
8
9
  //#region src/docx/headerFooterParser.ts
@@ -16,9 +17,11 @@ import { collectXmlnsDeclarations, parseXml } from "./xmlParser.js";
16
17
  * @param numbering - Parsed numbering definitions for lists
17
18
  * @param rels - Relationships for resolving hyperlinks/images
18
19
  * @param media - Media files for images
20
+ * @param previews - The ledger of the package this part belongs to; a part
21
+ * read on its own charges its previews to no package
19
22
  * @returns HeaderFooter object
20
23
  */
21
- function parseHeader(headerXml, hdrFtrType = "default", styles = null, theme = null, numbering = null, rels = null, media = null) {
24
+ function parseHeader(headerXml, hdrFtrType = "default", styles = null, theme = null, numbering = null, rels = null, media = null, previews = standalonePreviewLedger()) {
22
25
  const result = {
23
26
  type: "header",
24
27
  hdrFtrType,
@@ -35,11 +38,15 @@ function parseHeader(headerXml, hdrFtrType = "default", styles = null, theme = n
35
38
  }
36
39
  result.content = parseBlockContent(rootElement, styles, theme, numbering, rels, media, {
37
40
  inHeaderFooter: true,
38
- rootXmlns: collectXmlnsDeclarations(rootElement)
41
+ rootXmlns: collectXmlnsDeclarations(rootElement),
42
+ previews
39
43
  });
40
44
  if (watermarkResult) {
41
45
  const host = result.content.at(watermarkResult.blockIndex);
42
- if (host?.type === "paragraph") result.content[watermarkResult.blockIndex] = cloneParagraphWithPropertySource(host, { content: [] });
46
+ if (host?.type === "paragraph") {
47
+ previews.release(host.content);
48
+ result.content[watermarkResult.blockIndex] = cloneParagraphWithPropertySource(host, { content: [] });
49
+ }
43
50
  }
44
51
  assignHeaderFooterVerbatimXml(result, headerXml);
45
52
  return result;
@@ -54,9 +61,11 @@ function parseHeader(headerXml, hdrFtrType = "default", styles = null, theme = n
54
61
  * @param numbering - Parsed numbering definitions for lists
55
62
  * @param rels - Relationships for resolving hyperlinks/images
56
63
  * @param media - Media files for images
64
+ * @param previews - The ledger of the package this part belongs to; a part
65
+ * read on its own charges its previews to no package
57
66
  * @returns HeaderFooter object
58
67
  */
59
- function parseFooter(footerXml, hdrFtrType = "default", styles = null, theme = null, numbering = null, rels = null, media = null) {
68
+ function parseFooter(footerXml, hdrFtrType = "default", styles = null, theme = null, numbering = null, rels = null, media = null, previews = standalonePreviewLedger()) {
60
69
  const result = {
61
70
  type: "footer",
62
71
  hdrFtrType,
@@ -67,7 +76,8 @@ function parseFooter(footerXml, hdrFtrType = "default", styles = null, theme = n
67
76
  if (!rootElement) return result;
68
77
  result.content = parseBlockContent(rootElement, styles, theme, numbering, rels, media, {
69
78
  inHeaderFooter: true,
70
- rootXmlns: collectXmlnsDeclarations(rootElement)
79
+ rootXmlns: collectXmlnsDeclarations(rootElement),
80
+ previews
71
81
  });
72
82
  assignHeaderFooterVerbatimXml(result, footerXml);
73
83
  return result;
@@ -1,6 +1,8 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
2
  import { XmlElement } from "./xmlParser.js";
3
+ import { PreviewLedger } from "./previewBudget.js";
3
4
  import { StyleMap } from "./styleParser.js";
5
+ import { ChildReader } from "./containerChildren.js";
4
6
  //#region src/docx/hyperlinkParser.d.ts
5
7
  /**
6
8
  * Parse a hyperlink element (w:hyperlink)
@@ -13,10 +15,19 @@ import { StyleMap } from "./styleParser.js";
13
15
  * @param styles - Style map for resolving run styles
14
16
  * @param theme - Theme for resolving colors/fonts
15
17
  * @param media - Media files map for image data
18
+ * @param previews - The ledger of the package this link belongs to; a link
19
+ * read on its own charges its previews to no package
16
20
  * @returns Parsed Hyperlink object
17
21
  */
18
- declare function parseHyperlink(node: XmlElement, rels: document_d_exports.RelationshipMap | null, styles?: StyleMap | null, theme?: document_d_exports.Theme | null, media?: Map<string, document_d_exports.MediaFile> | null, rootXmlns?: Record<string, string>): document_d_exports.Hyperlink;
19
- /** What {@link hyperlinkChildHandlers} needs to read one child of a link. */
22
+ declare function parseHyperlink(node: XmlElement, rels: document_d_exports.RelationshipMap | null, styles?: StyleMap | null, theme?: document_d_exports.Theme | null, media?: Map<string, document_d_exports.MediaFile> | null, rootXmlns?: Record<string, string>, previews?: PreviewLedger): document_d_exports.Hyperlink;
23
+ /**
24
+ * A `w:hyperlink`'s own attributes as a link with no children yet.
25
+ *
26
+ * The paragraph parser's revision-segmenting walk reads the children itself,
27
+ * so it takes the shell rather than a link whose children it would discard.
28
+ */
29
+ declare function parseHyperlinkShell(node: XmlElement, rels: document_d_exports.RelationshipMap | null): document_d_exports.Hyperlink;
30
+ /** What {@link HYPERLINK_CHILD_HANDLERS} needs to read one child of a link. */
20
31
  type HyperlinkChildContext = {
21
32
  /** Where a parsed or captured child lands, in source order. */
22
33
  push: (child: document_d_exports.Hyperlink["children"][number]) => void;
@@ -24,6 +35,7 @@ type HyperlinkChildContext = {
24
35
  theme: document_d_exports.Theme | null;
25
36
  rels: document_d_exports.RelationshipMap | null;
26
37
  media: Map<string, document_d_exports.MediaFile> | null;
38
+ previews: PreviewLedger;
27
39
  inScopeXmlns: Record<string, string>;
28
40
  };
29
41
  /**
@@ -43,39 +55,39 @@ type HyperlinkChildContext = {
43
55
  * recognising is recognised by all of them rather than by whichever list
44
56
  * somebody remembered to update.
45
57
  */
46
- declare const hyperlinkChildHandlers: (context: HyperlinkChildContext) => {
47
- r: (child: XmlElement) => void;
48
- bookmarkStart: (child: XmlElement) => void;
49
- bookmarkEnd: (child: XmlElement) => void;
50
- bdo: (child: XmlElement) => void;
51
- dir: (child: XmlElement) => void;
52
- customXml: (child: XmlElement) => void;
53
- smartTag: (child: XmlElement) => void;
54
- del: "capture";
55
- ins: "capture";
56
- moveFrom: "capture";
57
- moveTo: "capture";
58
- commentRangeEnd: "capture";
59
- commentRangeStart: "capture";
60
- customXmlDelRangeEnd: "capture";
61
- customXmlDelRangeStart: "capture";
62
- customXmlInsRangeEnd: "capture";
63
- customXmlInsRangeStart: "capture";
64
- customXmlMoveFromRangeEnd: "capture";
65
- customXmlMoveFromRangeStart: "capture";
66
- customXmlMoveToRangeEnd: "capture";
67
- customXmlMoveToRangeStart: "capture";
68
- fldSimple: (child: XmlElement) => void;
69
- hyperlink: (child: XmlElement) => void;
70
- moveFromRangeEnd: "capture";
71
- moveFromRangeStart: "capture";
72
- moveToRangeEnd: "capture";
73
- moveToRangeStart: "capture";
74
- permEnd: "capture";
75
- permStart: "capture";
76
- proofErr: "capture";
77
- sdt: "capture";
78
- subDoc: "capture";
58
+ declare const HYPERLINK_CHILD_HANDLERS: {
59
+ readonly r: (child: XmlElement, { push, styles, theme, rels, media, previews, inScopeXmlns }: HyperlinkChildContext) => void;
60
+ readonly bookmarkStart: (child: XmlElement, { push }: HyperlinkChildContext) => void;
61
+ readonly bookmarkEnd: (child: XmlElement, { push }: HyperlinkChildContext) => void;
62
+ readonly bdo: ChildReader<HyperlinkChildContext>;
63
+ readonly dir: ChildReader<HyperlinkChildContext>;
64
+ readonly customXml: ChildReader<HyperlinkChildContext>;
65
+ readonly smartTag: ChildReader<HyperlinkChildContext>;
66
+ readonly del: "capture";
67
+ readonly ins: "capture";
68
+ readonly moveFrom: "capture";
69
+ readonly moveTo: "capture";
70
+ readonly commentRangeEnd: "capture";
71
+ readonly commentRangeStart: "capture";
72
+ readonly customXmlDelRangeEnd: "capture";
73
+ readonly customXmlDelRangeStart: "capture";
74
+ readonly customXmlInsRangeEnd: "capture";
75
+ readonly customXmlInsRangeStart: "capture";
76
+ readonly customXmlMoveFromRangeEnd: "capture";
77
+ readonly customXmlMoveFromRangeStart: "capture";
78
+ readonly customXmlMoveToRangeEnd: "capture";
79
+ readonly customXmlMoveToRangeStart: "capture";
80
+ readonly fldSimple: (child: XmlElement, { push }: HyperlinkChildContext) => void;
81
+ readonly hyperlink: (child: XmlElement, { push }: HyperlinkChildContext) => void;
82
+ readonly moveFromRangeEnd: "capture";
83
+ readonly moveFromRangeStart: "capture";
84
+ readonly moveToRangeEnd: "capture";
85
+ readonly moveToRangeStart: "capture";
86
+ readonly permEnd: "capture";
87
+ readonly permStart: "capture";
88
+ readonly proofErr: "capture";
89
+ readonly sdt: "capture";
90
+ readonly subDoc: "capture";
79
91
  };
80
92
  /**
81
93
  * Get the display text of a hyperlink
@@ -169,4 +181,4 @@ declare function createExternalHyperlink(url: string, children: document_d_expor
169
181
  target?: string;
170
182
  }): document_d_exports.Hyperlink;
171
183
  //#endregion
172
- export { HyperlinkChildContext, createExternalHyperlink, createInternalHyperlink, getHyperlinkRuns, getHyperlinkText, getHyperlinkUrl, hasContent, hyperlinkChildHandlers, isExternalLink, isInternalLink, parseHyperlink, resolveHyperlinkUrl };
184
+ export { HYPERLINK_CHILD_HANDLERS, HyperlinkChildContext, createExternalHyperlink, createInternalHyperlink, getHyperlinkRuns, getHyperlinkText, getHyperlinkUrl, hasContent, isExternalLink, isInternalLink, parseHyperlink, parseHyperlinkShell, resolveHyperlinkUrl };