@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 { NO_MODELLED_ATTRIBUTES, attributeRemainder } from "./attributeRemainder.js";
2
2
  import { parseBorderSpec } from "./borderParser.js";
3
- import { CAPTURE, dispatchChildren, keptUnless, sequencePositions } from "./containerChildren.js";
3
+ import { CAPTURE, dispatchChildrenWithContext, keptUnless, sequencePositions } from "./containerChildren.js";
4
4
  import { parseFooterReference, parseHeaderReference } from "./headerFooterRefParser.js";
5
5
  import { parseEndnoteProperties, parseFootnoteProperties } from "./notePropertiesParser.js";
6
6
  import { NumberFormatSchema, TextDirectionSchema, narrowEnum } from "./parserEnums.js";
@@ -67,6 +67,165 @@ function parseLineNumberRestart(restart) {
67
67
  default: return;
68
68
  }
69
69
  }
70
+ const SECTION_PROPERTY_HANDLERS = {
71
+ headerReference: (child, { headerRefs, context }) => {
72
+ const reference = parseHeaderReference(child, context);
73
+ if (reference) headerRefs.push(reference);
74
+ },
75
+ footerReference: (child, { footerRefs, context }) => {
76
+ const reference = parseFooterReference(child, context);
77
+ if (reference) footerRefs.push(reference);
78
+ },
79
+ footnotePr: (child, { props }) => {
80
+ const footnotePr = parseFootnoteProperties(child);
81
+ if (Object.keys(footnotePr).length > 0) {
82
+ props.footnotePr = footnotePr;
83
+ return;
84
+ }
85
+ return CAPTURE;
86
+ },
87
+ endnotePr: (child, { props }) => {
88
+ const endnotePr = parseEndnoteProperties(child);
89
+ if (Object.keys(endnotePr).length > 0) {
90
+ props.endnotePr = endnotePr;
91
+ return;
92
+ }
93
+ return CAPTURE;
94
+ },
95
+ type: (child, { props }) => {
96
+ const sectionStart = parseSectionStart(getAttribute(child, "w", "val"));
97
+ if (sectionStart) props.sectionStart = sectionStart;
98
+ return keptUnless(sectionStart !== void 0);
99
+ },
100
+ pgSz: (child, { props }) => {
101
+ const width = parseNumericAttribute(child, "w", "w");
102
+ if (width !== void 0) props.pageWidth = Math.min(width, MAX_PAGE_DIMENSION_TWIPS);
103
+ const height = parseNumericAttribute(child, "w", "h");
104
+ if (height !== void 0) props.pageHeight = Math.min(height, MAX_PAGE_DIMENSION_TWIPS);
105
+ const orientation = parseOrientation(getAttribute(child, "w", "orient"));
106
+ if (orientation) props.orientation = orientation;
107
+ return keptUnless(width !== void 0 || height !== void 0 || orientation !== void 0);
108
+ },
109
+ pgMar: (child, { props }) => {
110
+ const margins = [
111
+ ["top", "marginTop"],
112
+ ["bottom", "marginBottom"],
113
+ ["left", "marginLeft"],
114
+ ["right", "marginRight"],
115
+ ["header", "headerDistance"],
116
+ ["footer", "footerDistance"],
117
+ ["gutter", "gutter"]
118
+ ];
119
+ let taken = false;
120
+ for (const [attribute, field] of margins) {
121
+ const value = parseNumericAttribute(child, "w", attribute);
122
+ if (value !== void 0) {
123
+ props[field] = value;
124
+ taken = true;
125
+ }
126
+ }
127
+ return keptUnless(taken);
128
+ },
129
+ paperSrc: (child, { props }) => {
130
+ const first = parseNumericAttribute(child, "w", "first");
131
+ if (first !== void 0) props.paperSrcFirst = first;
132
+ const other = parseNumericAttribute(child, "w", "other");
133
+ if (other !== void 0) props.paperSrcOther = other;
134
+ return keptUnless(first !== void 0 || other !== void 0);
135
+ },
136
+ pgBorders: (child, { props, context }) => keptUnless(readPageBorders(props, child, context)),
137
+ lnNumType: (child, { props }) => {
138
+ const lineNumbers = {};
139
+ for (const [attribute, field] of [
140
+ ["start", "start"],
141
+ ["countBy", "countBy"],
142
+ ["distance", "distance"]
143
+ ]) {
144
+ const value = parseNumericAttribute(child, "w", attribute);
145
+ if (value !== void 0) lineNumbers[field] = value;
146
+ }
147
+ const restart = parseLineNumberRestart(getAttribute(child, "w", "restart"));
148
+ if (restart) lineNumbers.restart = restart;
149
+ if (Object.keys(lineNumbers).length === 0) return CAPTURE;
150
+ props.lineNumbers = lineNumbers;
151
+ },
152
+ pgNumType: (child, { props }) => {
153
+ const pageNumbering = {};
154
+ const format = narrowEnum(getAttribute(child, "w", "fmt"), NumberFormatSchema);
155
+ if (format) pageNumbering.format = format;
156
+ const start = parseNumericAttribute(child, "w", "start");
157
+ if (start !== void 0) pageNumbering.start = start;
158
+ const chapterStyle = parseNumericAttribute(child, "w", "chapStyle");
159
+ if (chapterStyle !== void 0) pageNumbering.chapterStyle = chapterStyle;
160
+ const chapterSeparator = getAttribute(child, "w", "chapSep");
161
+ if (chapterSeparator) pageNumbering.chapterSeparator = chapterSeparator;
162
+ if (Object.keys(pageNumbering).length === 0) return CAPTURE;
163
+ props.pageNumbering = pageNumbering;
164
+ },
165
+ cols: (child, { props }) => keptUnless(readColumns(props, child)),
166
+ formProt: (child, { props, context }) => {
167
+ props.formProtection = parseBooleanElement(child, "w", context);
168
+ },
169
+ vAlign: (child, { props }) => {
170
+ const verticalAlign = parseVerticalAlign(getAttribute(child, "w", "val"));
171
+ if (verticalAlign) props.verticalAlign = verticalAlign;
172
+ return keptUnless(verticalAlign !== void 0);
173
+ },
174
+ noEndnote: (child, { props, context }) => {
175
+ props.noEndnote = parseBooleanElement(child, "w", context);
176
+ },
177
+ titlePg: (child, { props, context }) => {
178
+ props.titlePg = parseBooleanElement(child, "w", context);
179
+ },
180
+ textDirection: (child, { props }) => {
181
+ const textDirection = narrowEnum(getAttribute(child, "w", "val"), TextDirectionSchema);
182
+ if (textDirection) props.textDirection = textDirection;
183
+ return keptUnless(textDirection !== void 0);
184
+ },
185
+ bidi: (child, { props, context }) => {
186
+ props.bidi = parseBooleanElement(child, "w", context);
187
+ },
188
+ rtlGutter: (child, { props, context }) => {
189
+ props.rtlGutter = parseBooleanElement(child, "w", context);
190
+ },
191
+ docGrid: (child, { props }) => {
192
+ const docGrid = {};
193
+ const gridType = getAttribute(child, "w", "type");
194
+ if (gridType === "default" || gridType === "lines" || gridType === "linesAndChars" || gridType === "snapToChars") docGrid.type = gridType;
195
+ const linePitch = parseNumericAttribute(child, "w", "linePitch");
196
+ if (linePitch !== void 0) docGrid.linePitch = linePitch;
197
+ const charSpace = parseNumericAttribute(child, "w", "charSpace");
198
+ if (charSpace !== void 0) docGrid.charSpace = charSpace;
199
+ props.docGrid = docGrid;
200
+ },
201
+ printerSettings: (child, { props }) => {
202
+ const relationshipId = getAttribute(child, "r", "id");
203
+ if (relationshipId) props.printerSettingsRelationshipId = relationshipId;
204
+ return keptUnless(Boolean(relationshipId));
205
+ },
206
+ sectPrChange: (child, { propertyChanges }) => {
207
+ const previousSectPr = findChild(child, "w", "sectPr");
208
+ const change = {
209
+ type: "sectionPropertyChange",
210
+ info: parsePropertyChangeInfo(child)
211
+ };
212
+ if (previousSectPr) change.previousProperties = parseSectionProperties(previousSectPr);
213
+ const previousReferences = parseSectionReferenceHistory(child);
214
+ if (previousReferences !== void 0) change.previousReferences = previousReferences;
215
+ propertyChanges.push(change);
216
+ }
217
+ };
218
+ const SECTION_PROPERTY_UNDECLARED = {
219
+ background: (child, { props, context }) => keptUnless(readBackground(props, child, context)),
220
+ evenAndOddHeaders: (child, { props, context }) => {
221
+ props.evenAndOddHeaders = parseBooleanElement(child, "w", context);
222
+ },
223
+ footnoteColumns: (child, { props }) => {
224
+ const columns = parseNumericAttribute(child, "w", "val");
225
+ if (columns !== void 0) props.footnoteColumns = columns;
226
+ return keptUnless(columns !== void 0);
227
+ }
228
+ };
70
229
  /**
71
230
  * Parse section properties (w:sectPr)
72
231
  *
@@ -79,168 +238,18 @@ function parseSectionProperties(sectPr, context) {
79
238
  const headerRefs = [];
80
239
  const footerRefs = [];
81
240
  const propertyChanges = [];
82
- const preserved = dispatchChildren({
241
+ const preserved = dispatchChildrenWithContext({
83
242
  element: sectPr,
84
243
  container: "section-properties",
85
- handlers: {
86
- headerReference: (child) => {
87
- const reference = parseHeaderReference(child, context);
88
- if (reference) headerRefs.push(reference);
89
- },
90
- footerReference: (child) => {
91
- const reference = parseFooterReference(child, context);
92
- if (reference) footerRefs.push(reference);
93
- },
94
- footnotePr: (child) => {
95
- const footnotePr = parseFootnoteProperties(child);
96
- if (Object.keys(footnotePr).length > 0) {
97
- props.footnotePr = footnotePr;
98
- return;
99
- }
100
- return CAPTURE;
101
- },
102
- endnotePr: (child) => {
103
- const endnotePr = parseEndnoteProperties(child);
104
- if (Object.keys(endnotePr).length > 0) {
105
- props.endnotePr = endnotePr;
106
- return;
107
- }
108
- return CAPTURE;
109
- },
110
- type: (child) => {
111
- const sectionStart = parseSectionStart(getAttribute(child, "w", "val"));
112
- if (sectionStart) props.sectionStart = sectionStart;
113
- return keptUnless(sectionStart !== void 0);
114
- },
115
- pgSz: (child) => {
116
- const width = parseNumericAttribute(child, "w", "w");
117
- if (width !== void 0) props.pageWidth = Math.min(width, MAX_PAGE_DIMENSION_TWIPS);
118
- const height = parseNumericAttribute(child, "w", "h");
119
- if (height !== void 0) props.pageHeight = Math.min(height, MAX_PAGE_DIMENSION_TWIPS);
120
- const orientation = parseOrientation(getAttribute(child, "w", "orient"));
121
- if (orientation) props.orientation = orientation;
122
- return keptUnless(width !== void 0 || height !== void 0 || orientation !== void 0);
123
- },
124
- pgMar: (child) => {
125
- const margins = [
126
- ["top", "marginTop"],
127
- ["bottom", "marginBottom"],
128
- ["left", "marginLeft"],
129
- ["right", "marginRight"],
130
- ["header", "headerDistance"],
131
- ["footer", "footerDistance"],
132
- ["gutter", "gutter"]
133
- ];
134
- let taken = false;
135
- for (const [attribute, field] of margins) {
136
- const value = parseNumericAttribute(child, "w", attribute);
137
- if (value !== void 0) {
138
- props[field] = value;
139
- taken = true;
140
- }
141
- }
142
- return keptUnless(taken);
143
- },
144
- paperSrc: (child) => {
145
- const first = parseNumericAttribute(child, "w", "first");
146
- if (first !== void 0) props.paperSrcFirst = first;
147
- const other = parseNumericAttribute(child, "w", "other");
148
- if (other !== void 0) props.paperSrcOther = other;
149
- return keptUnless(first !== void 0 || other !== void 0);
150
- },
151
- pgBorders: (child) => keptUnless(readPageBorders(props, child, context)),
152
- lnNumType: (child) => {
153
- const lineNumbers = {};
154
- for (const [attribute, field] of [
155
- ["start", "start"],
156
- ["countBy", "countBy"],
157
- ["distance", "distance"]
158
- ]) {
159
- const value = parseNumericAttribute(child, "w", attribute);
160
- if (value !== void 0) lineNumbers[field] = value;
161
- }
162
- const restart = parseLineNumberRestart(getAttribute(child, "w", "restart"));
163
- if (restart) lineNumbers.restart = restart;
164
- if (Object.keys(lineNumbers).length === 0) return CAPTURE;
165
- props.lineNumbers = lineNumbers;
166
- },
167
- pgNumType: (child) => {
168
- const pageNumbering = {};
169
- const format = narrowEnum(getAttribute(child, "w", "fmt"), NumberFormatSchema);
170
- if (format) pageNumbering.format = format;
171
- const start = parseNumericAttribute(child, "w", "start");
172
- if (start !== void 0) pageNumbering.start = start;
173
- const chapterStyle = parseNumericAttribute(child, "w", "chapStyle");
174
- if (chapterStyle !== void 0) pageNumbering.chapterStyle = chapterStyle;
175
- const chapterSeparator = getAttribute(child, "w", "chapSep");
176
- if (chapterSeparator) pageNumbering.chapterSeparator = chapterSeparator;
177
- if (Object.keys(pageNumbering).length === 0) return CAPTURE;
178
- props.pageNumbering = pageNumbering;
179
- },
180
- cols: (child) => keptUnless(readColumns(props, child)),
181
- formProt: (child) => {
182
- props.formProtection = parseBooleanElement(child, "w", context);
183
- },
184
- vAlign: (child) => {
185
- const verticalAlign = parseVerticalAlign(getAttribute(child, "w", "val"));
186
- if (verticalAlign) props.verticalAlign = verticalAlign;
187
- return keptUnless(verticalAlign !== void 0);
188
- },
189
- noEndnote: (child) => {
190
- props.noEndnote = parseBooleanElement(child, "w", context);
191
- },
192
- titlePg: (child) => {
193
- props.titlePg = parseBooleanElement(child, "w", context);
194
- },
195
- textDirection: (child) => {
196
- const textDirection = narrowEnum(getAttribute(child, "w", "val"), TextDirectionSchema);
197
- if (textDirection) props.textDirection = textDirection;
198
- return keptUnless(textDirection !== void 0);
199
- },
200
- bidi: (child) => {
201
- props.bidi = parseBooleanElement(child, "w", context);
202
- },
203
- rtlGutter: (child) => {
204
- props.rtlGutter = parseBooleanElement(child, "w", context);
205
- },
206
- docGrid: (child) => {
207
- const docGrid = {};
208
- const gridType = getAttribute(child, "w", "type");
209
- if (gridType === "default" || gridType === "lines" || gridType === "linesAndChars" || gridType === "snapToChars") docGrid.type = gridType;
210
- const linePitch = parseNumericAttribute(child, "w", "linePitch");
211
- if (linePitch !== void 0) docGrid.linePitch = linePitch;
212
- const charSpace = parseNumericAttribute(child, "w", "charSpace");
213
- if (charSpace !== void 0) docGrid.charSpace = charSpace;
214
- props.docGrid = docGrid;
215
- },
216
- printerSettings: (child) => {
217
- const relationshipId = getAttribute(child, "r", "id");
218
- if (relationshipId) props.printerSettingsRelationshipId = relationshipId;
219
- return keptUnless(Boolean(relationshipId));
220
- },
221
- sectPrChange: (child) => {
222
- const previousSectPr = findChild(child, "w", "sectPr");
223
- const change = {
224
- type: "sectionPropertyChange",
225
- info: parsePropertyChangeInfo(child)
226
- };
227
- if (previousSectPr) change.previousProperties = parseSectionProperties(previousSectPr);
228
- const previousReferences = parseSectionReferenceHistory(child);
229
- if (previousReferences !== void 0) change.previousReferences = previousReferences;
230
- propertyChanges.push(change);
231
- }
232
- },
244
+ handlers: SECTION_PROPERTY_HANDLERS,
233
245
  capturePosition: sequencePositions("section-properties", sectPr),
234
- undeclared: {
235
- background: (child) => keptUnless(readBackground(props, child, context)),
236
- evenAndOddHeaders: (child) => {
237
- props.evenAndOddHeaders = parseBooleanElement(child, "w", context);
238
- },
239
- footnoteColumns: (child) => {
240
- const columns = parseNumericAttribute(child, "w", "val");
241
- if (columns !== void 0) props.footnoteColumns = columns;
242
- return keptUnless(columns !== void 0);
243
- }
246
+ undeclared: SECTION_PROPERTY_UNDECLARED,
247
+ context: {
248
+ props,
249
+ headerRefs,
250
+ footerRefs,
251
+ propertyChanges,
252
+ context
244
253
  }
245
254
  });
246
255
  if (preserved) props.preserved = preserved;
@@ -5,7 +5,7 @@ import { fieldStateAttributes } from "../fieldState.js";
5
5
  import { getParagraphPropertySource, paragraphPropertySourceMatchesEmission } from "../paragraphPropertySource.js";
6
6
  import { DATE_UTC_ATTRIBUTE } from "../trackedChangeInfo.js";
7
7
  import { toTransitionalNamespaceUri } from "../transitionalSpelling.js";
8
- import { captureVerbatimXml, sanitizeCapturedXmlElement } from "../verbatimCapture.js";
8
+ import { captureVerbatimXml, createCapturedXmlSanitizer } from "../verbatimCapture.js";
9
9
  import { NAMESPACES, OOXML_NAMESPACE_SCOPE, cloneElement, getChildElements, getLocalName, parseXml } from "../xmlParser.js";
10
10
  import { markupRangeAttributes, moveBookmarkAttributes, serializeBookmarkMarker } from "./markupRangeAttributes.js";
11
11
  import { serializeRun } from "./runSerializer.js";
@@ -162,37 +162,35 @@ const hasInvalidParagraphMarkProperties = (root) => {
162
162
  }
163
163
  return false;
164
164
  };
165
- const replayableParagraphPropertySourceXml = (sourceXml) => {
166
- return sanitizeCapturedXmlElement(sourceXml, {
167
- allowedLocalNames: PARAGRAPH_PROPERTY_ROOT_NAME,
168
- allowedNamespaceUris: WORDPROCESSINGML_NAMESPACE,
169
- inheritedNamespaceScope: OOXML_NAMESPACE_SCOPE,
170
- requiredNamespaceBindings: PARAGRAPH_APPEND_PREFIXES,
171
- validate: (root) => {
172
- let paragraphMarkProperties = null;
173
- const seenChildren = /* @__PURE__ */ new Set();
174
- let previousChildOrder = -1;
175
- for (const child of getChildElements(root)) {
176
- const localName = getLocalName(child.name);
177
- const isWordprocessingChild = toTransitionalNamespaceUri(child.namespaceUri ?? "") === NAMESPACES.w;
178
- if (isWordprocessingChild) {
179
- if (RESERVED_PARAGRAPH_PROPERTY_CHILDREN.has(localName)) return false;
180
- const childOrder = PARAGRAPH_PROPERTY_CHILD_ORDER.get(localName);
181
- if (childOrder === void 0 || seenChildren.has(localName) || childOrder < previousChildOrder) return false;
182
- seenChildren.add(localName);
183
- previousChildOrder = childOrder;
184
- }
185
- if (localName !== "rPr" || !isWordprocessingChild) {
186
- if (hasDescendantNamed(child, NAMESPACES.w, "rPr")) return false;
187
- continue;
188
- }
189
- if (paragraphMarkProperties !== null || toTransitionalNamespaceUri(resolveNamespaceBinding(child.namespaceScope, "w") ?? "") !== NAMESPACES.w) return false;
190
- paragraphMarkProperties = child;
165
+ const replayableParagraphPropertySourceXml = createCapturedXmlSanitizer({
166
+ allowedLocalNames: PARAGRAPH_PROPERTY_ROOT_NAME,
167
+ allowedNamespaceUris: WORDPROCESSINGML_NAMESPACE,
168
+ inheritedNamespaceScope: OOXML_NAMESPACE_SCOPE,
169
+ requiredNamespaceBindings: PARAGRAPH_APPEND_PREFIXES,
170
+ validate: (root) => {
171
+ let paragraphMarkProperties = null;
172
+ const seenChildren = /* @__PURE__ */ new Set();
173
+ let previousChildOrder = -1;
174
+ for (const child of getChildElements(root)) {
175
+ const localName = getLocalName(child.name);
176
+ const isWordprocessingChild = toTransitionalNamespaceUri(child.namespaceUri ?? "") === NAMESPACES.w;
177
+ if (isWordprocessingChild) {
178
+ if (RESERVED_PARAGRAPH_PROPERTY_CHILDREN.has(localName)) return false;
179
+ const childOrder = PARAGRAPH_PROPERTY_CHILD_ORDER.get(localName);
180
+ if (childOrder === void 0 || seenChildren.has(localName) || childOrder < previousChildOrder) return false;
181
+ seenChildren.add(localName);
182
+ previousChildOrder = childOrder;
191
183
  }
192
- return !(paragraphMarkProperties && hasInvalidParagraphMarkProperties(paragraphMarkProperties) || hasInvalidParagraphPropertyShape(root) || hasInvalidParagraphPropertyRevision(root));
184
+ if (localName !== "rPr" || !isWordprocessingChild) {
185
+ if (hasDescendantNamed(child, NAMESPACES.w, "rPr")) return false;
186
+ continue;
187
+ }
188
+ if (paragraphMarkProperties !== null || toTransitionalNamespaceUri(resolveNamespaceBinding(child.namespaceScope, "w") ?? "") !== NAMESPACES.w) return false;
189
+ paragraphMarkProperties = child;
193
190
  }
194
- });
195
- };
191
+ return !(paragraphMarkProperties && hasInvalidParagraphMarkProperties(paragraphMarkProperties) || hasInvalidParagraphPropertyShape(root) || hasInvalidParagraphPropertyRevision(root));
192
+ }
193
+ });
196
194
  const verifiedParagraphPropertySource = (formatting, source) => {
197
195
  if (!source) return null;
198
196
  const replayableSource = replayableParagraphPropertySourceXml(source.xml);
@@ -458,9 +456,19 @@ function serializeTrackedChange(tag, change) {
458
456
  flushPending();
459
457
  return segments.join("");
460
458
  }
461
- const TAGGED_WRAPPER_PROPERTY_NAMES = {
462
- smartTag: /* @__PURE__ */ new Set(["smartTagPr"]),
463
- customXml: /* @__PURE__ */ new Set(["customXmlPr"])
459
+ /** The properties element each tagged wrapper declares ahead of its content. */
460
+ const TAGGED_WRAPPER_PROPERTIES = {
461
+ smartTag: "smartTagPr",
462
+ customXml: "customXmlPr"
463
+ };
464
+ const taggedWrapperPropertiesSanitizer = (propertiesName) => createCapturedXmlSanitizer({
465
+ allowedLocalNames: /* @__PURE__ */ new Set([propertiesName]),
466
+ allowedNamespaceUris: WORDPROCESSINGML_NAMESPACE,
467
+ inheritedNamespaceScope: OOXML_NAMESPACE_SCOPE
468
+ });
469
+ const TAGGED_WRAPPER_PROPERTIES_SANITIZERS = {
470
+ smartTag: taggedWrapperPropertiesSanitizer(TAGGED_WRAPPER_PROPERTIES.smartTag),
471
+ customXml: taggedWrapperPropertiesSanitizer(TAGGED_WRAPPER_PROPERTIES.customXml)
464
472
  };
465
473
  /**
466
474
  * Emit a `w:smartTag` or a run-level `w:customXml` around content already written.
@@ -477,11 +485,7 @@ const TAGGED_WRAPPER_PROPERTY_NAMES = {
477
485
  */
478
486
  function serializeTaggedWrapper(kind, wrapper, inner) {
479
487
  const uri = wrapper.uri === void 0 ? "" : ` w:uri="${escapeXmlAttribute(wrapper.uri)}"`;
480
- const properties = sanitizeCapturedXmlElement(wrapper.propertiesXml, {
481
- allowedLocalNames: TAGGED_WRAPPER_PROPERTY_NAMES[kind],
482
- allowedNamespaceUris: WORDPROCESSINGML_NAMESPACE,
483
- inheritedNamespaceScope: OOXML_NAMESPACE_SCOPE
484
- }) ?? "";
488
+ const properties = TAGGED_WRAPPER_PROPERTIES_SANITIZERS[kind](wrapper.propertiesXml) ?? "";
485
489
  return `<w:${kind}${uri} w:element="${escapeXmlAttribute(wrapper.element)}">${properties}${inner}</w:${kind}>`;
486
490
  }
487
491
  /** Emit the `<w:commentReference>` run Word places after a comment range end. */
@@ -2,11 +2,29 @@ import { serializePartElement } from "./partNamespaces.js";
2
2
  import { intAttr } from "./xmlUtils.js";
3
3
  import { escapeXmlAttribute, serializeOnOffElement } from "@stll/docx-core";
4
4
  //#region src/docx/serializer/settingsSerializer.ts
5
+ /** `w:compatSetting/@w:uri` the named application compatibility settings share. */
6
+ const COMPAT_SETTING_URI = "http://schemas.microsoft.com/office/word";
7
+ /**
8
+ * Named compatibility settings written on alongside `compatibilityMode` 15 and
9
+ * later. Layout never gates run typography on a compatibility setting and
10
+ * never flips `w:mirrorIndents`, so declaring both on describes how the
11
+ * package is already laid out.
12
+ */
13
+ const MODERN_COMPAT_SETTINGS = ["enableOpenTypeFeatures", "doNotFlipMirrorIndents"];
14
+ const FIRST_MODERN_COMPATIBILITY_MODE = 15;
15
+ const compatSettingXml = (name, val) => `<w:compatSetting w:name="${name}" w:uri="${COMPAT_SETTING_URI}" w:val="${intAttr(val)}"/>`;
16
+ const serializeCompat = (compatibilityMode) => {
17
+ if (compatibilityMode === void 0) return "";
18
+ const settings = [compatSettingXml("compatibilityMode", compatibilityMode)];
19
+ if (compatibilityMode >= FIRST_MODERN_COMPATIBILITY_MODE) for (const name of MODERN_COMPAT_SETTINGS) settings.push(compatSettingXml(name, 1));
20
+ return `<w:compat>${settings.join("")}</w:compat>`;
21
+ };
5
22
  const serializeSettingsXml = (settings) => {
6
23
  const parts = [
7
24
  `<w:defaultTabStop w:val="${intAttr(settings.defaultTabStop)}"/>`,
8
25
  serializeOnOffElement(settings.evenAndOddHeaders, "evenAndOddHeaders"),
9
- serializeOnOffElement(settings.updateFields, "updateFields")
26
+ serializeOnOffElement(settings.updateFields, "updateFields"),
27
+ serializeCompat(settings.compatibilityMode)
10
28
  ];
11
29
  if (settings.themeFontLang) {
12
30
  const attrs = [];
@@ -1,3 +1,4 @@
1
+ import { parseEndnoteProperties } from "./notePropertiesParser.js";
1
2
  import { WORDPROCESSINGML_NAMESPACE_URIS, findChild, findChildByNamespaceUri, findChildren, getAttribute, parseBooleanElement, parseXmlDocument } from "./xmlParser.js";
2
3
  //#region src/docx/settingsParser.ts
3
4
  /** OOXML default per §17.6.13 when `w:defaultTabStop` is absent. */
@@ -61,6 +62,8 @@ function parseSettings(xml) {
61
62
  const wordprocessingCompat = root ? findChildByNamespaceUri(root, WORDPROCESSINGML_NAMESPACE_URIS, "compat") : null;
62
63
  const adjustLineHeightInTable = wordprocessingCompat ? findChildByNamespaceUri(wordprocessingCompat, WORDPROCESSINGML_NAMESPACE_URIS, "adjustLineHeightInTable") : null;
63
64
  if (adjustLineHeightInTable && parseBooleanElement(adjustLineHeightInTable)) settings.adjustLineHeightInTable = true;
65
+ const doNotUseIndentAsNumberingTabStop = wordprocessingCompat ? findChildByNamespaceUri(wordprocessingCompat, WORDPROCESSINGML_NAMESPACE_URIS, "doNotUseIndentAsNumberingTabStop") : null;
66
+ if (doNotUseIndentAsNumberingTabStop && parseBooleanElement(doNotUseIndentAsNumberingTabStop)) settings.doNotUseIndentAsNumberingTabStop = true;
64
67
  const applyBreakingRules = compat ? findChild(compat, "w", "applyBreakingRules") : null;
65
68
  const useLegacyEthiopicAmharicRules = applyBreakingRules !== null && parseBooleanElement(applyBreakingRules);
66
69
  if (noLineBreaksBefore || noLineBreaksAfter || useLegacyEthiopicAmharicRules) settings.lineBreakRules = {
@@ -68,6 +71,11 @@ function parseSettings(xml) {
68
71
  ...noLineBreaksAfter ? { noLineBreaksAfter } : {},
69
72
  ...useLegacyEthiopicAmharicRules ? { useLegacyEthiopicAmharicRules: true } : {}
70
73
  };
74
+ const endnotePr = root ? findChild(root, "w", "endnotePr") : null;
75
+ if (endnotePr) {
76
+ const endnoteProperties = parseEndnoteProperties(endnotePr);
77
+ if (Object.keys(endnoteProperties).length > 0) settings.endnotePr = endnoteProperties;
78
+ }
71
79
  return settings;
72
80
  }
73
81
  function parseCompatibilityMode(compat) {
@@ -1,4 +1,4 @@
1
- import { XmlElement } from "./xmlParser.js";
1
+ import { XmlElement, XmlNamespaceScope } from "./xmlParser.js";
2
2
  //#region src/docx/streamingXmlParser.d.ts
3
3
  type ParseXmlResult = {
4
4
  status: "parsed";
@@ -16,7 +16,11 @@ type AttributeValueSpan = {
16
16
  end: number;
17
17
  };
18
18
  type OpenTagVisitor = (element: XmlElement, attributeValueSpans: ReadonlyMap<string, AttributeValueSpan>) => ReadonlyMap<string, string> | null;
19
- declare const parseStreamingXml: (xml: string) => ParseXmlResult;
19
+ /**
20
+ * @param inheritedNamespaceScope the bindings in scope around `xml`, for a
21
+ * fragment captured out of a part whose root declared them.
22
+ */
23
+ declare const parseStreamingXml: (xml: string, inheritedNamespaceScope?: XmlNamespaceScope) => ParseXmlResult;
20
24
  /** Rewrite selected decimal attribute values while preserving every other source byte. */
21
25
  declare const rewriteStreamingXmlDecimalAttributes: (xml: string, visitOpenTag: OpenTagVisitor) => {
22
26
  status: "rewritten";
@@ -1,4 +1,4 @@
1
- import { attachXmlNamespaceContext } from "./xmlParser.js";
1
+ import { EMPTY_NAMESPACE_SCOPE, attachXmlNamespaceContext } from "./xmlNamespaceContext.js";
2
2
  import { FOLIO_XML_RESOURCE_LIMITS } from "./xmlResourceLimits.js";
3
3
  //#region src/docx/streamingXmlParser.ts
4
4
  const BUILT_IN_ENTITIES = {
@@ -8,7 +8,7 @@ const BUILT_IN_ENTITIES = {
8
8
  lt: "<",
9
9
  quot: "\""
10
10
  };
11
- const parseStreamingXmlInternal = (xml, visitOpenTag) => {
11
+ const parseStreamingXmlInternal = (xml, visitOpenTag, inheritedNamespaceScope = EMPTY_NAMESPACE_SCOPE) => {
12
12
  const root = { elements: [] };
13
13
  const stack = [];
14
14
  const replacements = [];
@@ -39,6 +39,7 @@ const parseStreamingXmlInternal = (xml, visitOpenTag) => {
39
39
  const close = xml.indexOf("?>", open + 2);
40
40
  if (close === -1) return { status: "unsupported" };
41
41
  cursor = close + 2;
42
+ mergeAdjacentText = false;
42
43
  continue;
43
44
  }
44
45
  if (xml.startsWith("<!", open)) return { status: "unsupported" };
@@ -54,8 +55,8 @@ const parseStreamingXmlInternal = (xml, visitOpenTag) => {
54
55
  }
55
56
  const parsedTag = parseOpenTag(xml, open + 1, close, visitOpenTag !== void 0);
56
57
  if (parsedTag.status === "unsupported") return parsedTag;
57
- const parent = stack.at(-1)?.element ?? root;
58
- attachXmlNamespaceContext(parsedTag.element, parent.namespaceScope);
58
+ const parent = stack.at(-1)?.element;
59
+ attachXmlNamespaceContext(parsedTag.element, parent === void 0 ? inheritedNamespaceScope : parent.namespaceScope);
59
60
  const rewritten = visitOpenTag?.(parsedTag.element, parsedTag.attributeValueSpans ?? /* @__PURE__ */ new Map());
60
61
  if (rewritten) for (const [attributeName, value] of rewritten) {
61
62
  const span = parsedTag.attributeValueSpans?.get(attributeName);
@@ -65,7 +66,7 @@ const parseStreamingXmlInternal = (xml, visitOpenTag) => {
65
66
  value
66
67
  });
67
68
  }
68
- appendElement(parent, parsedTag.element);
69
+ appendElement(parent ?? root, parsedTag.element);
69
70
  if (!parsedTag.selfClosing) {
70
71
  if (stack.length >= FOLIO_XML_RESOURCE_LIMITS.maxDepth) return { status: "unsupported" };
71
72
  stack.push({
@@ -83,8 +84,12 @@ const parseStreamingXmlInternal = (xml, visitOpenTag) => {
83
84
  replacements
84
85
  };
85
86
  };
86
- const parseStreamingXml = (xml) => {
87
- const parsed = parseStreamingXmlInternal(xml);
87
+ /**
88
+ * @param inheritedNamespaceScope the bindings in scope around `xml`, for a
89
+ * fragment captured out of a part whose root declared them.
90
+ */
91
+ const parseStreamingXml = (xml, inheritedNamespaceScope = EMPTY_NAMESPACE_SCOPE) => {
92
+ const parsed = parseStreamingXmlInternal(xml, void 0, inheritedNamespaceScope);
88
93
  return parsed.status === "parsed" ? {
89
94
  status: "parsed",
90
95
  value: parsed.value
@@ -407,22 +407,48 @@ function resolveStyleInheritance(style, styleMap, visited = /* @__PURE__ */ new
407
407
  const mergedRPr = mergeStyleTextFormatting(resolvedParent.rPr, style.rPr);
408
408
  if (mergedRPr) resolved.rPr = mergedRPr;
409
409
  if (style.type === "table") {
410
- if (resolvedParent.tblPr || style.tblPr) resolved.tblPr = {
411
- ...resolvedParent.tblPr,
412
- ...style.tblPr
413
- };
410
+ if (resolvedParent.tblPr || style.tblPr) {
411
+ const tblPr = {
412
+ ...resolvedParent.tblPr,
413
+ ...style.tblPr
414
+ };
415
+ const borders = mergeSides(resolvedParent.tblPr?.borders, style.tblPr?.borders);
416
+ if (borders) tblPr.borders = borders;
417
+ const cellMargins = mergeSides(resolvedParent.tblPr?.cellMargins, style.tblPr?.cellMargins);
418
+ if (cellMargins) tblPr.cellMargins = cellMargins;
419
+ resolved.tblPr = tblPr;
420
+ }
414
421
  if (resolvedParent.trPr || style.trPr) resolved.trPr = {
415
422
  ...resolvedParent.trPr,
416
423
  ...style.trPr
417
424
  };
418
- if (resolvedParent.tcPr || style.tcPr) resolved.tcPr = {
419
- ...resolvedParent.tcPr,
420
- ...style.tcPr
421
- };
425
+ if (resolvedParent.tcPr || style.tcPr) {
426
+ const tcPr = {
427
+ ...resolvedParent.tcPr,
428
+ ...style.tcPr
429
+ };
430
+ const borders = mergeSides(resolvedParent.tcPr?.borders, style.tcPr?.borders);
431
+ if (borders) tcPr.borders = borders;
432
+ const margins = mergeSides(resolvedParent.tcPr?.margins, style.tcPr?.margins);
433
+ if (margins) tcPr.margins = margins;
434
+ resolved.tcPr = tcPr;
435
+ }
422
436
  }
423
437
  return resolved;
424
438
  }
425
439
  /**
440
+ * Each child of `w:tblBorders`, `w:tcBorders`, `w:tblCellMar` and `w:tcMar` is
441
+ * its own property, so a style that states one side inherits the others from
442
+ * its `w:basedOn` parent (ECMA-376 §17.7.4.3).
443
+ */
444
+ function mergeSides(parent, child) {
445
+ if (!parent || !child) return child ?? parent;
446
+ return {
447
+ ...parent,
448
+ ...child
449
+ };
450
+ }
451
+ /**
426
452
  * Parse styles.xml content
427
453
  *
428
454
  * @param stylesXml - XML content of styles.xml