@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,28 +1,5 @@
1
+ import { document_d_exports } from "../types/document.js";
1
2
  //#region src/docx/previewBudget.d.ts
2
- /**
3
- * Every synthetic preview a parse attaches to the model, and the budget each
4
- * one answers to.
5
- *
6
- * A preview is not document content. It is a drawing folio makes up so a shape
7
- * it cannot project still occupies the page, and the package round-trips from
8
- * its preserved XML whether the preview exists or not. So a preview is the one
9
- * thing in the model that may be dropped, and a package that would retain more
10
- * of it than it is worth has it dropped rather than being refused.
11
- *
12
- * Producer and budget have to agree on what a preview looks like. They used to
13
- * agree by coincidence: the VML producer wrote its filename as a literal and
14
- * the budget recognized it as a constant in another file, so renaming one
15
- * would have stopped the other charging for it without anything failing. The
16
- * table below is that agreement written once, and a producer builds its image
17
- * out of the entry the budget matches against.
18
- *
19
- * An entry says what backs the preview, because that decides what there is to
20
- * agree on. A source-backed preview is image data in `src`, recognized by the
21
- * strings its producer stamps on it and charged by the character. A
22
- * descriptor-backed preview is no picture at all, so it carries none of those
23
- * strings: a table that offered them would be offering a cap over nothing and
24
- * a prefix nothing can start with.
25
- */
26
3
  type PreviewKindName = "vmlShape" | "wpGroup" | "smartArt";
27
4
  declare const PREVIEW_KINDS: {
28
5
  /** A VML shape folio renders rather than projects (`v:shape`, `v:rect`, ...). */
@@ -39,9 +16,9 @@ declare const PREVIEW_KINDS: {
39
16
  * The group's shapes, their text and any picture they carry are drawn into
40
17
  * one SVG, so a package with many groups retains as much generated text as a
41
18
  * package of VML shapes does, from a producer bounded per drawing the same
42
- * way. It was missing from this table, which is not a preview with a generous
43
- * cap but a preview with none: the matcher below never named it, so no
44
- * package was ever charged for one.
19
+ * way. It was once missing from this table, which was not a preview with a
20
+ * generous cap but a preview with none: the budget never recognized it, so
21
+ * no package was ever charged for one.
45
22
  */
46
23
  readonly wpGroup: {
47
24
  readonly backing: "source";
@@ -75,11 +52,56 @@ type SourceBackedKindName = { [Name in PreviewKindName]: (typeof PREVIEW_KINDS)[
75
52
  */
76
53
  type PreviewBudgetOverrides = Partial<Record<SourceBackedKindName, number>>;
77
54
  /**
78
- * Charge every generated preview in the model against its kind's allowance and
79
- * drop the `src` of those past it. Dropping leaves the image in place with its
80
- * size and wrap, so the page still reserves the space the drawing occupies,
81
- * and never touches the preserved XML the package saves from.
55
+ * Everything a preview image states except what the ledger stamps on it: its
56
+ * geometry, wrap, position and names. A preview has no relationship behind
57
+ * it, so its frame cannot name one.
58
+ */
59
+ type PreviewFrame = Omit<document_d_exports.Image, "type" | "rId" | "src" | "mimeType" | "filename">;
60
+ /** The one way a producer builds a preview image. */
61
+ type PreviewLedger = {
62
+ /**
63
+ * Build a `kind` preview that draws `svg` over `frame`, registered against
64
+ * the package's allowance for that kind.
65
+ */
66
+ readonly svgImage: (kind: SourceBackedKindName, svg: string, frame: PreviewFrame) => document_d_exports.Image;
67
+ /**
68
+ * Withdraw every preview inside content the parse built and then discarded,
69
+ * so it spends no allowance. Walks only `discarded`.
70
+ */
71
+ readonly release: (discarded: unknown) => void;
72
+ };
73
+ /** One parse's ledger, and the budget that charges it. */
74
+ type PackagePreviewBudget = {
75
+ readonly ledger: PreviewLedger;
76
+ /**
77
+ * Charge every registered preview against its kind's allowance and drop the
78
+ * `src` of those past it. Dropping leaves the image in place with its size
79
+ * and wrap, so the page still reserves the space the drawing occupies, and
80
+ * never touches the preserved XML the package saves from.
81
+ *
82
+ * Previews are charged in the order the parse built them, which is the
83
+ * order it read the parts: the body (a text box's content right after the
84
+ * paragraph that anchors it), then headers and footers in relationship
85
+ * order, footnotes, endnotes, and comments last. Past an allowance, the
86
+ * previews built first keep their `src`.
87
+ */
88
+ readonly enforce: (overrides?: PreviewBudgetOverrides) => void;
89
+ };
90
+ /**
91
+ * One parse's previews and their budget.
92
+ *
93
+ * A parse that builds content and then discards it (a note repeating an id, a
94
+ * separator note, a duplicate comment, a watermark's host paragraph) releases
95
+ * that content, or its previews would spend allowance no retained preview can
96
+ * use. A missed release errs towards dropping a preview, never towards
97
+ * retaining more than the cap; the budget's tests pin that a package's ledger
98
+ * holds exactly the previews its model carries.
99
+ */
100
+ declare const createPackagePreviewBudget: () => PackagePreviewBudget;
101
+ /**
102
+ * A ledger for a read that builds no package: one part parsed on its own,
103
+ * whose previews no package budget charges.
82
104
  */
83
- declare const enforcePackagePreviewBudget: (root: unknown, overrides?: PreviewBudgetOverrides) => void;
105
+ declare const standalonePreviewLedger: () => PreviewLedger;
84
106
  //#endregion
85
- export { PREVIEW_KINDS, PreviewBudgetOverrides, enforcePackagePreviewBudget };
107
+ export { PackagePreviewBudget, PreviewBudgetOverrides, PreviewLedger, createPackagePreviewBudget, standalonePreviewLedger };
@@ -21,9 +21,9 @@ const PREVIEW_KINDS = {
21
21
  * The group's shapes, their text and any picture they carry are drawn into
22
22
  * one SVG, so a package with many groups retains as much generated text as a
23
23
  * package of VML shapes does, from a producer bounded per drawing the same
24
- * way. It was missing from this table, which is not a preview with a generous
25
- * cap but a preview with none: the matcher below never named it, so no
26
- * package was ever charged for one.
24
+ * way. It was once missing from this table, which was not a preview with a
25
+ * generous cap but a preview with none: the budget never recognized it, so
26
+ * no package was ever charged for one.
27
27
  */
28
28
  wpGroup: {
29
29
  backing: "source",
@@ -45,75 +45,69 @@ const PREVIEW_KINDS = {
45
45
  */
46
46
  smartArt: { backing: "descriptor" }
47
47
  };
48
- const KIND_NAMES = Object.keys(PREVIEW_KINDS);
49
48
  /**
50
- * Whether a package charges this kind, narrowing the name to a source-backed
51
- * one: the predicate reads the same field `SourceBackedKindName` selects on.
52
- */
53
- const isSourceBacked = (name) => {
54
- const kind = PREVIEW_KINDS[name];
55
- switch (kind.backing) {
56
- case "source": return true;
57
- case "descriptor": return false;
58
- default: return kind;
59
- }
60
- };
61
- const SOURCE_BACKED_KIND_NAMES = KIND_NAMES.filter(isSourceBacked);
62
- /**
63
- * The kind a model image was generated as, or `undefined` for one the package
64
- * actually carries. A generated preview has no relationship behind it; the
65
- * filename and data-URL prefix name which producer made it.
66
- *
67
- * "No relationship" is read as the one spelling `RelationshipId` leaves: a
68
- * field that is absent or `undefined`. The walk takes an unvalidated object,
69
- * so the check stays, but it no longer has a second spelling to accept.
49
+ * One parse's previews and their budget.
70
50
  *
71
- * Only a source-backed kind can be named this way. A descriptor-backed preview
72
- * has no `src` to match, so no image the model carries can be charged to it.
73
- */
74
- const previewKindOf = (value) => {
75
- if (!("type" in value) || value.type !== "image" || "rId" in value && value.rId !== void 0 || !("src" in value) || typeof value.src !== "string" || !("mimeType" in value) || !("filename" in value)) return;
76
- const { src, mimeType, filename } = value;
77
- return SOURCE_BACKED_KIND_NAMES.find((name) => {
78
- const kind = PREVIEW_KINDS[name];
79
- return mimeType === kind.mimeType && filename === kind.filename && src.startsWith(kind.srcPrefix);
80
- });
81
- };
82
- /**
83
- * Charge every generated preview in the model against its kind's allowance and
84
- * drop the `src` of those past it. Dropping leaves the image in place with its
85
- * size and wrap, so the page still reserves the space the drawing occupies,
86
- * and never touches the preserved XML the package saves from.
51
+ * A parse that builds content and then discards it (a note repeating an id, a
52
+ * separator note, a duplicate comment, a watermark's host paragraph) releases
53
+ * that content, or its previews would spend allowance no retained preview can
54
+ * use. A missed release errs towards dropping a preview, never towards
55
+ * retaining more than the cap; the budget's tests pin that a package's ledger
56
+ * holds exactly the previews its model carries.
87
57
  */
88
- const enforcePackagePreviewBudget = (root, overrides = {}) => {
89
- const remaining = new Map(SOURCE_BACKED_KIND_NAMES.map((name) => [name, Math.max(0, overrides[name] ?? PREVIEW_KINDS[name].maxPackageCharacters)]));
90
- const visited = /* @__PURE__ */ new WeakSet();
91
- const visit = (value) => {
92
- if (value === null || typeof value !== "object" || visited.has(value)) return;
93
- visited.add(value);
94
- if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) return;
95
- if (value instanceof Map) {
96
- for (const child of value.values()) visit(child);
58
+ const createPackagePreviewBudget = () => {
59
+ const entries = [];
60
+ const registered = /* @__PURE__ */ new WeakSet();
61
+ const released = /* @__PURE__ */ new WeakSet();
62
+ const release = (value, seen) => {
63
+ if (value === null || typeof value !== "object" || seen.has(value) || value instanceof ArrayBuffer || ArrayBuffer.isView(value)) return;
64
+ seen.add(value);
65
+ if (registered.has(value)) {
66
+ released.add(value);
97
67
  return;
98
68
  }
99
- if (Array.isArray(value)) {
100
- for (const child of value) visit(child);
101
- return;
102
- }
103
- const kind = previewKindOf(value);
104
- if (kind !== void 0) {
105
- const image = value;
106
- const length = image.src.length;
107
- const left = remaining.get(kind);
108
- if (length <= left) remaining.set(kind, left - length);
109
- else {
110
- remaining.set(kind, 0);
111
- delete image.src;
69
+ for (const child of value instanceof Map ? value.values() : Object.values(value)) release(child, seen);
70
+ };
71
+ return {
72
+ ledger: {
73
+ svgImage: (kind, svg, frame) => {
74
+ const { srcPrefix, mimeType, filename } = PREVIEW_KINDS[kind];
75
+ const image = {
76
+ type: "image",
77
+ src: `${srcPrefix}${encodeURIComponent(svg)}`,
78
+ mimeType,
79
+ filename,
80
+ ...frame
81
+ };
82
+ entries.push({
83
+ kind,
84
+ image
85
+ });
86
+ registered.add(image);
87
+ return image;
88
+ },
89
+ release: (discarded) => {
90
+ release(discarded, /* @__PURE__ */ new WeakSet());
91
+ }
92
+ },
93
+ enforce: (overrides = {}) => {
94
+ const remaining = /* @__PURE__ */ new Map();
95
+ for (const { kind, image } of entries) {
96
+ if (image.src === void 0 || released.has(image)) continue;
97
+ const left = remaining.get(kind) ?? Math.max(0, overrides[kind] ?? PREVIEW_KINDS[kind].maxPackageCharacters);
98
+ if (image.src.length <= left) remaining.set(kind, left - image.src.length);
99
+ else {
100
+ remaining.set(kind, 0);
101
+ delete image.src;
102
+ }
112
103
  }
113
104
  }
114
- for (const child of Object.values(value)) visit(child);
115
105
  };
116
- visit(root);
117
106
  };
107
+ /**
108
+ * A ledger for a read that builds no package: one part parsed on its own,
109
+ * whose previews no package budget charges.
110
+ */
111
+ const standalonePreviewLedger = () => createPackagePreviewBudget().ledger;
118
112
  //#endregion
119
- export { PREVIEW_KINDS, enforcePackagePreviewBudget };
113
+ export { createPackagePreviewBudget, standalonePreviewLedger };
@@ -4,10 +4,10 @@ import { isAllowedExternalWatermarkImageUrl } from "../watermark/index.js";
4
4
  import { appVersionInSchemaForm, normalizeAppVersionInExtendedProperties } from "./appVersionNormalization.js";
5
5
  import { withoutOrphanCommentRanges } from "./commentRangeIntegrity.js";
6
6
  import { applyReplyThreadMarkers } from "./commentReplyMarkers.js";
7
+ import { readDocumentSectionFacts } from "./documentSectionFacts.js";
7
8
  import { normalizeDrawingIds } from "./drawingIdNormalization.js";
8
9
  import { rebindDrawingImageRelationship } from "./drawingRelationships.js";
9
10
  import { parseEndnotes, parseFootnotes } from "./footnoteParser.js";
10
- import { parseHeaderFooterType } from "./headerFooterRefParser.js";
11
11
  import { assertValidFolioDocumentModel } from "./modelValidation.js";
12
12
  import { isNewDataUrlDrawing } from "./newImage.js";
13
13
  import { missingNoteReferenceStyles, noteReferenceNeeds } from "./noteReferenceStyles.js";
@@ -30,8 +30,7 @@ import { readRootNamespaceBindings } from "./serializer/partNamespaces.js";
30
30
  import { serializeSettingsXml } from "./serializer/settingsSerializer.js";
31
31
  import { serializeStyle, serializeStylesXml } from "./serializer/stylesSerializer.js";
32
32
  import { serializeThemeXml } from "./serializer/themeSerializer.js";
33
- import { OFFICE_RELATIONSHIP_NAMESPACE_URIS, WORDPROCESSINGML_NAMESPACE_URIS, findChild, findChildByNamespaceUri, findChildrenByNamespaceUri, getAttribute, getAttributeByNamespaceUri, getChildElements, getLocalName, getNamespaceUri, matchesName, parseXml, parseXmlDocument } from "./xmlParser.js";
34
- import { assertXmlResourceLimits } from "./xmlResourceLimits.js";
33
+ import { WORDPROCESSINGML_NAMESPACE_URIS, findChild, findChildByNamespaceUri, findChildrenByNamespaceUri, getAttribute, getAttributeByNamespaceUri, getChildElements, getLocalName, getNamespaceUri, matchesName, parseXml, parseXmlDocument } from "./xmlParser.js";
35
34
  import { TaggedError, panic } from "better-result";
36
35
  import { escapeXmlAttribute, escapeXmlText, validateDocxPackage } from "@stll/docx-core";
37
36
  import { mintRelationshipId } from "@stll/docx-core/model";
@@ -108,47 +107,6 @@ function findMaxRId(relsXml) {
108
107
  }
109
108
  return maxId;
110
109
  }
111
- const isWordprocessingElement = (element, localName) => getLocalName(element.name) === localName && WORDPROCESSINGML_NAMESPACE_URIS.has(getNamespaceUri(element) ?? "");
112
- const countDocumentSections = (xml) => {
113
- assertXmlResourceLimits({ xml });
114
- let count = 0;
115
- const pending = [{
116
- element: parseXml(xml),
117
- insidePropertyChange: false
118
- }];
119
- while (pending.length > 0) {
120
- const current = pending.pop();
121
- if (!current) break;
122
- const { element, insidePropertyChange } = current;
123
- const isPropertyChange = isWordprocessingElement(element, "sectPrChange");
124
- if (!insidePropertyChange && isWordprocessingElement(element, "sectPr")) count += 1;
125
- for (const child of getChildElements(element)) pending.push({
126
- element: child,
127
- insidePropertyChange: insidePropertyChange || isPropertyChange
128
- });
129
- }
130
- return count;
131
- };
132
- const extractHeaderFooterReferences = (xml) => {
133
- const references = [];
134
- assertXmlResourceLimits({ xml });
135
- const pending = [parseXml(xml)];
136
- while (pending.length > 0) {
137
- const node = pending.pop();
138
- if (!node) break;
139
- const element = getLocalName(node.name);
140
- if ((element === "headerReference" || element === "footerReference") && isWordprocessingElement(node, element)) {
141
- const rId = getAttributeByNamespaceUri(node, OFFICE_RELATIONSHIP_NAMESPACE_URIS, "id");
142
- if (rId) references.push({
143
- element,
144
- type: parseHeaderFooterType(getAttributeByNamespaceUri(node, WORDPROCESSINGML_NAMESPACE_URIS, "type")),
145
- rId
146
- });
147
- }
148
- pending.push(...getChildElements(node));
149
- }
150
- return references;
151
- };
152
110
  const hasParsedHeaderFooterPart = (doc, ref) => {
153
111
  return (ref.element === "headerReference" ? doc.package.headers : doc.package.footers)?.has(ref.rId) ?? false;
154
112
  };
@@ -201,9 +159,10 @@ const assertSectionCarriersMatchModel = ({ doc, serializedSectionCount }) => {
201
159
  const modelSectionCount = carriers.length + (finalSectionProperties === void 0 ? 0 : 1);
202
160
  if (serializedSectionCount !== modelSectionCount) throw new DocxPackageFidelityError(`Full DOCX repack would write ${serializedSectionCount} sections where the editor holds ${modelSectionCount}.`);
203
161
  };
204
- function assertDocumentPackageFidelity({ originalDocumentXml, serializedDocumentXml, doc, sectionEndpointRemoval, sectionReferenceRemovals = [] }) {
205
- const originalSectionCount = countDocumentSections(originalDocumentXml);
206
- const serializedSectionCount = countDocumentSections(serializedDocumentXml);
162
+ function assertDocumentPackageFidelity({ originalDocumentFacts, serializedDocumentXml, doc, sectionEndpointRemoval, sectionReferenceRemovals = [] }) {
163
+ const serializedDocumentFacts = readDocumentSectionFacts(serializedDocumentXml);
164
+ const originalSectionCount = originalDocumentFacts.sectionCount;
165
+ const serializedSectionCount = serializedDocumentFacts.sectionCount;
207
166
  const trackedRemovedSectionCount = sectionEndpointRemoval ? sectionEndpointRemoval.sourceParagraphEndpointCount - sectionEndpointRemoval.expectedParagraphEndpointCount : 0;
208
167
  const exactTrackedSectionRemoval = trackedRemovedSectionCount > 0 && originalSectionCount - trackedRemovedSectionCount === serializedSectionCount && serializedSectionCount < originalSectionCount;
209
168
  if (serializedSectionCount < originalSectionCount && !exactTrackedSectionRemoval) throw new DocxPackageFidelityError("Full DOCX repack would drop section properties. Use selective patching instead.");
@@ -212,14 +171,14 @@ function assertDocumentPackageFidelity({ originalDocumentXml, serializedDocument
212
171
  serializedSectionCount
213
172
  });
214
173
  const serializedReferenceCounts = /* @__PURE__ */ new Map();
215
- for (const reference of extractHeaderFooterReferences(serializedDocumentXml)) incrementReferenceCount(serializedReferenceCounts, headerFooterReferenceKey(reference));
174
+ for (const reference of serializedDocumentFacts.headerFooterReferences) incrementReferenceCount(serializedReferenceCounts, headerFooterReferenceKey(reference));
216
175
  const removedReferenceCounts = /* @__PURE__ */ new Map();
217
176
  if (exactTrackedSectionRemoval && sectionEndpointRemoval) for (const { part, type, relationshipId } of sectionEndpointRemoval.removedReferences) incrementReferenceCount(removedReferenceCounts, `${part}Reference:${type}:${relationshipId}`);
218
177
  const resolvedReferenceCounts = /* @__PURE__ */ new Map();
219
178
  for (const { part, type, relationshipId } of sectionReferenceRemovals) incrementReferenceCount(resolvedReferenceCounts, `${part}Reference:${type}:${relationshipId}`);
220
179
  for (const [key, count] of resolvedReferenceCounts) removedReferenceCounts.set(key, Math.max(count, removedReferenceCounts.get(key) ?? 0));
221
180
  const missingRefs = [];
222
- for (const reference of extractHeaderFooterReferences(originalDocumentXml)) {
181
+ for (const reference of originalDocumentFacts.headerFooterReferences) {
223
182
  if (!hasParsedHeaderFooterPart(doc, reference)) continue;
224
183
  const key = headerFooterReferenceKey(reference);
225
184
  if (consumeReferenceCount(serializedReferenceCounts, key) || consumeReferenceCount(removedReferenceCounts, key)) continue;
@@ -614,6 +573,30 @@ const generateDocxZip = async (zip, compressionLevel) => {
614
573
  compressionOptions: { level: compressionLevel }
615
574
  });
616
575
  };
576
+ /**
577
+ * Section facts of each source package's `word/document.xml`, keyed by the
578
+ * source buffer. Hosts save repeatedly against one baseline buffer, often
579
+ * through a fresh `Document` each time, so the buffer is the identity that
580
+ * survives between saves. The facts are reused only while the part read from
581
+ * it is still the same string, so a buffer rewritten in place is read again.
582
+ */
583
+ const sourceDocumentSectionFacts = /* @__PURE__ */ new WeakMap();
584
+ const originalDocumentPart = (buffer, xml) => {
585
+ if (xml === void 0) return;
586
+ return {
587
+ xml,
588
+ sectionFacts: () => {
589
+ const cached = sourceDocumentSectionFacts.get(buffer);
590
+ if (cached?.xml === xml) return cached.facts;
591
+ const facts = readDocumentSectionFacts(xml);
592
+ sourceDocumentSectionFacts.set(buffer, {
593
+ xml,
594
+ facts
595
+ });
596
+ return facts;
597
+ }
598
+ };
599
+ };
617
600
  const parsedZipSources = /* @__PURE__ */ new WeakMap();
618
601
  const loadParsedZipSource = async (document, buffer) => {
619
602
  const cached = parsedZipSources.get(document);
@@ -624,7 +607,7 @@ const loadParsedZipSource = async (document, buffer) => {
624
607
  const source = {
625
608
  buffer,
626
609
  zip,
627
- documentXml,
610
+ document: originalDocumentPart(buffer, documentXml),
628
611
  corePropertiesXml
629
612
  };
630
613
  parsedZipSources.set(document, source);
@@ -644,7 +627,7 @@ const normalizeExportDrawingIds = ({ package: docxPackage }) => {
644
627
  ...docxPackage.endnotes !== void 0 ? { endnotes: docxPackage.endnotes } : {}
645
628
  });
646
629
  };
647
- const finishRepack = async ({ document, originalZip, outputZip, originalDocumentXml, originalCorePropertiesXml, compressionLevel, updateModifiedDate, modifiedBy, changedNoteParaIds, sectionEndpointRemoval, sectionReferenceRemovals }) => {
630
+ const finishRepack = async ({ document, originalZip, outputZip, originalDocument, originalCorePropertiesXml, compressionLevel, updateModifiedDate, modifiedBy, changedNoteParaIds, sectionEndpointRemoval, sectionReferenceRemovals }) => {
648
631
  await materializeNewHeaderFooterParts(document, outputZip, compressionLevel);
649
632
  await materializeEmbeddedMedia({
650
633
  document,
@@ -657,9 +640,9 @@ const finishRepack = async ({ document, originalZip, outputZip, originalDocument
657
640
  await processNewHyperlinks(parts, outputZip, compressionLevel);
658
641
  assertValidFolioDocumentModel(document, "Cannot repack invalid DOCX document model");
659
642
  applyReplyThreadMarkers(document);
660
- const documentXml = serializeDocument(document, originalDocumentXml === void 0 ? void 0 : readRootNamespaceBindings(originalDocumentXml));
661
- if (originalDocumentXml) assertDocumentPackageFidelity({
662
- originalDocumentXml,
643
+ const documentXml = serializeDocument(document, originalDocument === void 0 ? void 0 : readRootNamespaceBindings(originalDocument.xml));
644
+ if (originalDocument?.xml) assertDocumentPackageFidelity({
645
+ originalDocumentFacts: originalDocument.sectionFacts(),
663
646
  serializedDocumentXml: documentXml,
664
647
  doc: document,
665
648
  ...sectionEndpointRemoval !== void 0 && { sectionEndpointRemoval },
@@ -712,7 +695,7 @@ async function repackDocxWithSectionEndpointRemoval({ document: doc, options, se
712
695
  document: exportDocument,
713
696
  originalZip,
714
697
  outputZip: newZip,
715
- originalDocumentXml,
698
+ originalDocument: originalDocumentPart(doc.originalBuffer, originalDocumentXml),
716
699
  originalCorePropertiesXml,
717
700
  compressionLevel,
718
701
  updateModifiedDate,
@@ -770,7 +753,7 @@ async function repackDocxFromRaw(doc, rawContent, options = {}) {
770
753
  applyReplyThreadMarkers(exportDocument);
771
754
  const documentXml = serializeDocument(exportDocument, rawContent.documentXml ? readRootNamespaceBindings(rawContent.documentXml) : void 0);
772
755
  if (rawContent.documentXml) assertDocumentPackageFidelity({
773
- originalDocumentXml: rawContent.documentXml,
756
+ originalDocumentFacts: readDocumentSectionFacts(rawContent.documentXml),
774
757
  serializedDocumentXml: documentXml,
775
758
  doc: exportDocument
776
759
  });
@@ -1790,7 +1773,7 @@ async function createDocx(doc, properties = {}) {
1790
1773
  document: withoutOrphanCommentRanges(doc),
1791
1774
  originalZip: source.zip,
1792
1775
  outputZip: cloneDocxZip(source.zip),
1793
- originalDocumentXml: source.documentXml,
1776
+ originalDocument: source.document,
1794
1777
  originalCorePropertiesXml: source.corePropertiesXml,
1795
1778
  compressionLevel: 6,
1796
1779
  updateModifiedDate: true
@@ -1801,7 +1784,7 @@ async function createDocx(doc, properties = {}) {
1801
1784
  document: withoutOrphanCommentRanges(doc),
1802
1785
  originalZip: zip,
1803
1786
  outputZip: zip,
1804
- originalDocumentXml: void 0,
1787
+ originalDocument: void 0,
1805
1788
  originalCorePropertiesXml: await zip.file("docProps/core.xml")?.async("text"),
1806
1789
  compressionLevel: 6,
1807
1790
  updateModifiedDate: true
@@ -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
  import { StyleMap } from "./styleParser.js";
4
5
  //#region src/docx/runParser.d.ts
5
6
  /**
@@ -71,9 +72,11 @@ declare function parseRunProperties(rPr: XmlElement | null, theme: document_d_ex
71
72
  * @param theme - Theme for resolving theme colors/fonts
72
73
  * @param rels - Relationship map for resolving image references
73
74
  * @param media - Media files map for image data
75
+ * @param previews - The ledger of the package this run belongs to; a run read
76
+ * on its own charges its previews to no package
74
77
  * @returns Parsed Run object
75
78
  */
76
- declare function parseRun(node: XmlElement, _styles: StyleMap | null, theme: document_d_exports.Theme | null, rels?: document_d_exports.RelationshipMap | null, media?: Map<string, document_d_exports.MediaFile> | null, rootXmlns?: Record<string, string>): document_d_exports.Run;
79
+ declare function parseRun(node: XmlElement, _styles: StyleMap | null, theme: document_d_exports.Theme | null, rels?: document_d_exports.RelationshipMap | null, media?: Map<string, document_d_exports.MediaFile> | null, rootXmlns?: Record<string, string>, previews?: PreviewLedger): document_d_exports.Run;
77
80
  /**
78
81
  * Get plain text from a run
79
82
  *