@stll/folio-core 0.43.0 → 0.45.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 (237) hide show
  1. package/dist/ai-edits/__fixtures__/paragraphs.js +2 -2
  2. package/dist/ai-edits/headless.js +7 -5
  3. package/dist/ai-edits/index.d.ts +2 -2
  4. package/dist/ai-edits/index.js +2 -2
  5. package/dist/ai-edits/snapshot.js +13 -9
  6. package/dist/compare/content-alignment.js +94 -54
  7. package/dist/compare/inline-atoms.js +34 -20
  8. package/dist/compare/style-resources.js +6 -0
  9. package/dist/content-controls/mutateContentControls.js +4 -2
  10. package/dist/display-list/dom/renderDisplayListToDom.js +8 -8
  11. package/dist/document-operations.js +14 -3
  12. package/dist/docx/appVersionNormalization.d.ts +0 -18
  13. package/dist/docx/blockContentParser.js +8 -0
  14. package/dist/docx/blockRangeMarkers.d.ts +36 -0
  15. package/dist/docx/blockRangeMarkers.js +59 -0
  16. package/dist/docx/bookmarkParser.d.ts +2 -20
  17. package/dist/docx/bookmarkParser.js +6 -30
  18. package/dist/docx/borderParser.d.ts +13 -0
  19. package/dist/docx/borderParser.js +71 -0
  20. package/dist/docx/builtInStyles.d.ts +165 -0
  21. package/dist/docx/builtInStyles.js +239 -0
  22. package/dist/docx/commentIdNormalization.d.ts +3 -1
  23. package/dist/docx/commentIdNormalization.js +18 -1
  24. package/dist/docx/commentParser.d.ts +2 -1
  25. package/dist/docx/commentParser.js +80 -42
  26. package/dist/docx/commentReferenceNormalization.d.ts +4 -1
  27. package/dist/docx/commentReferenceNormalization.js +23 -14
  28. package/dist/docx/commentThreadKey.d.ts +18 -0
  29. package/dist/docx/commentThreadKey.js +22 -0
  30. package/dist/docx/danglingRelationshipReferences.d.ts +15 -0
  31. package/dist/docx/danglingRelationshipReferences.js +30 -0
  32. package/dist/docx/defaultParagraphStyle.d.ts +18 -1
  33. package/dist/docx/defaultParagraphStyle.js +23 -1
  34. package/dist/docx/diagramPreview.js +87 -27
  35. package/dist/docx/documentParser.d.ts +2 -1
  36. package/dist/docx/documentParser.js +2 -2
  37. package/dist/docx/drawingUtils.d.ts +8 -1
  38. package/dist/docx/drawingUtils.js +12 -3
  39. package/dist/docx/fieldParser.js +3 -5
  40. package/dist/docx/footnoteParser.d.ts +3 -2
  41. package/dist/docx/footnoteParser.js +19 -4
  42. package/dist/docx/groupDrawingParser.js +4 -4
  43. package/dist/docx/headerFooterRefParser.d.ts +4 -3
  44. package/dist/docx/headerFooterRefParser.js +42 -12
  45. package/dist/docx/headerFooterReferenceNormalization.d.ts +4 -1
  46. package/dist/docx/headerFooterReferenceNormalization.js +5 -1
  47. package/dist/docx/hyperlinkParser.js +13 -17
  48. package/dist/docx/imageParser.d.ts +10 -2
  49. package/dist/docx/imageParser.js +80 -30
  50. package/dist/docx/imageRawXml.d.ts +14 -1
  51. package/dist/docx/imageRawXml.js +35 -11
  52. package/dist/docx/markupRangeMarker.d.ts +15 -0
  53. package/dist/docx/markupRangeMarker.js +44 -0
  54. package/dist/docx/mathToMathml.js +12 -14
  55. package/dist/docx/nonVisualDrawingProps.d.ts +34 -0
  56. package/dist/docx/nonVisualDrawingProps.js +46 -0
  57. package/dist/docx/noteReferenceStyles.d.ts +29 -0
  58. package/dist/docx/noteReferenceStyles.js +70 -0
  59. package/dist/docx/numberingReferenceNormalization.d.ts +4 -1
  60. package/dist/docx/numberingReferenceNormalization.js +20 -1
  61. package/dist/docx/paraIdRangeNormalization.d.ts +0 -19
  62. package/dist/docx/paragraphParser.js +66 -99
  63. package/dist/docx/paragraphPropertySource.js +1 -0
  64. package/dist/docx/paragraphTextBoxEnrichment.js +3 -0
  65. package/dist/docx/paragraphTraversal.d.ts +37 -1
  66. package/dist/docx/paragraphTraversal.js +84 -1
  67. package/dist/docx/parseContext.d.ts +37 -0
  68. package/dist/docx/parseContext.js +67 -0
  69. package/dist/docx/parseWarningMessage.d.ts +6 -0
  70. package/dist/docx/parseWarningMessage.js +44 -0
  71. package/dist/docx/parser.js +83 -29
  72. package/dist/docx/previewBudget.d.ts +64 -0
  73. package/dist/docx/previewBudget.js +88 -0
  74. package/dist/docx/relsParser.d.ts +28 -11
  75. package/dist/docx/relsParser.js +26 -13
  76. package/dist/docx/revisionIdNormalization.js +96 -10
  77. package/dist/docx/rezip.js +80 -40
  78. package/dist/docx/runConsolidator.js +1 -2
  79. package/dist/docx/runParser.d.ts +8 -1
  80. package/dist/docx/runParser.js +30 -48
  81. package/dist/docx/sdtPropertiesPatch.js +24 -18
  82. package/dist/docx/sectionParser.d.ts +2 -1
  83. package/dist/docx/sectionParser.js +21 -65
  84. package/dist/docx/sectionReferenceHistory.js +2 -2
  85. package/dist/docx/selectiveSave.js +6 -6
  86. package/dist/docx/serializer/blockSdtSerializer.js +38 -26
  87. package/dist/docx/serializer/borderSerializer.d.ts +2 -3
  88. package/dist/docx/serializer/borderSerializer.js +13 -12
  89. package/dist/docx/serializer/commentSerializer.d.ts +41 -16
  90. package/dist/docx/serializer/commentSerializer.js +82 -72
  91. package/dist/docx/serializer/documentSerializer.d.ts +1 -5
  92. package/dist/docx/serializer/documentSerializer.js +6 -16
  93. package/dist/docx/serializer/fontTableSerializer.js +6 -6
  94. package/dist/docx/serializer/headerFooterSerializer.js +10 -5
  95. package/dist/docx/serializer/markupRangeAttributes.d.ts +8 -0
  96. package/dist/docx/serializer/markupRangeAttributes.js +24 -0
  97. package/dist/docx/serializer/noteSerializer.js +5 -0
  98. package/dist/docx/serializer/numberingSerializer.js +7 -6
  99. package/dist/docx/serializer/paragraphSerializer.d.ts +1 -5
  100. package/dist/docx/serializer/paragraphSerializer.js +47 -52
  101. package/dist/docx/serializer/partNamespaces.js +2 -2
  102. package/dist/docx/serializer/runSerializer.js +57 -31
  103. package/dist/docx/serializer/sectionPropertiesSerializer.js +11 -10
  104. package/dist/docx/serializer/settingsSerializer.js +4 -3
  105. package/dist/docx/serializer/stylesSerializer.js +6 -6
  106. package/dist/docx/serializer/tableSerializer.js +37 -21
  107. package/dist/docx/serializer/textFormattingSerializer.d.ts +2 -3
  108. package/dist/docx/serializer/textFormattingSerializer.js +29 -28
  109. package/dist/docx/serializer/themeSerializer.js +6 -6
  110. package/dist/docx/serializer/trackedChangeAttributes.js +2 -2
  111. package/dist/docx/serializer/xmlUtils.d.ts +1 -2
  112. package/dist/docx/serializer/xmlUtils.js +1 -13
  113. package/dist/docx/server/boundedArchive.d.ts +12 -0
  114. package/dist/docx/server/boundedArchive.js +20 -1
  115. package/dist/docx/server/build.js +8 -1
  116. package/dist/docx/server/createBilingualDocument.js +10 -18
  117. package/dist/docx/server/extractDocxText.js +3 -4
  118. package/dist/docx/server/validateDocxConformance.js +22 -1
  119. package/dist/docx/shadingParser.d.ts +6 -0
  120. package/dist/docx/shadingParser.js +32 -0
  121. package/dist/docx/shapeParser.js +10 -8
  122. package/dist/docx/styleParser.js +13 -87
  123. package/dist/docx/styleReferenceResolution.d.ts +36 -0
  124. package/dist/docx/styleReferenceResolution.js +51 -0
  125. package/dist/docx/tableLook.d.ts +57 -0
  126. package/dist/docx/tableLook.js +63 -0
  127. package/dist/docx/tableParser.d.ts +7 -9
  128. package/dist/docx/tableParser.js +64 -110
  129. package/dist/docx/textBoxParser.js +11 -6
  130. package/dist/docx/trackedMoveRangeNormalization.d.ts +3 -1
  131. package/dist/docx/trackedMoveRangeNormalization.js +11 -21
  132. package/dist/docx/transitionalSpelling.d.ts +13 -2
  133. package/dist/docx/transitionalSpelling.js +23 -1
  134. package/dist/docx/unzip.d.ts +23 -0
  135. package/dist/docx/unzip.js +32 -22
  136. package/dist/docx/verbatimCapture.js +5 -12
  137. package/dist/docx/vmlImageParser.js +5 -4
  138. package/dist/docx/vmlPreview.d.ts +1 -3
  139. package/dist/docx/vmlPreview.js +2 -30
  140. package/dist/docx/watermarkParser.js +2 -2
  141. package/dist/docx/xmlParser.d.ts +38 -33
  142. package/dist/docx/xmlParser.js +92 -47
  143. package/dist/docx/xmlResourceLimits.d.ts +89 -9
  144. package/dist/docx/xmlResourceLimits.js +105 -24
  145. package/dist/internal/pageBreakRunSourceDescendantIndex.js +2 -1
  146. package/dist/internal/paragraphFormattingSerialization.d.ts +2 -3
  147. package/dist/internal/paragraphFormattingSerialization.js +29 -8
  148. package/dist/layout-bridge/convert/footnoteLayout.js +2 -7
  149. package/dist/layout-engine/index.d.ts +2 -2
  150. package/dist/layout-engine/index.js +2 -2
  151. package/dist/layout-engine/measure/measureBlocks.js +1 -6
  152. package/dist/layout-engine/types.d.ts +8 -2
  153. package/dist/layout-engine/types.js +35 -2
  154. package/dist/layout-painter/renderImage.js +4 -3
  155. package/dist/layout-painter/renderParagraph.js +4 -3
  156. package/dist/managers/autoSaveCodec.js +2 -8
  157. package/dist/markdown/images.js +1 -4
  158. package/dist/markdown/index.js +1 -1
  159. package/dist/markdown/internals.d.ts +6 -1
  160. package/dist/markdown/internals.js +14 -1
  161. package/dist/markdown/renderBlock.js +35 -21
  162. package/dist/markdown/renderParagraph.js +14 -5
  163. package/dist/markdown/renderRuns.js +4 -3
  164. package/dist/markdown/renderTable.js +4 -3
  165. package/dist/markdown/trailers.js +41 -7
  166. package/dist/markdown/types.d.ts +3 -7
  167. package/dist/prosemirror/attrs/index.js +71 -5
  168. package/dist/prosemirror/bookmarkBoundaryAttrs.d.ts +11 -1
  169. package/dist/prosemirror/bookmarkBoundaryAttrs.js +18 -3
  170. package/dist/prosemirror/commands/image.js +1 -0
  171. package/dist/prosemirror/commands/index.d.ts +3 -3
  172. package/dist/prosemirror/commands/index.js +2 -2
  173. package/dist/prosemirror/commands/paragraph.d.ts +3 -3
  174. package/dist/prosemirror/commands/paragraph.js +2 -2
  175. package/dist/prosemirror/commentIdAllocator.js +2 -7
  176. package/dist/prosemirror/conversion/fromProseDoc.js +197 -68
  177. package/dist/prosemirror/conversion/toProseDoc.d.ts +1 -14
  178. package/dist/prosemirror/conversion/toProseDoc.js +458 -335
  179. package/dist/prosemirror/extensions/core/ParagraphExtension.d.ts +14 -1
  180. package/dist/prosemirror/extensions/core/ParagraphExtension.js +11 -6
  181. package/dist/prosemirror/extensions/features/EmptyParagraphFormatExtension.js +3 -3
  182. package/dist/prosemirror/extensions/features/PasteCleanupExtension.d.ts +4 -1
  183. package/dist/prosemirror/extensions/features/PasteCleanupExtension.js +6 -2
  184. package/dist/prosemirror/extensions/features/pastedHeadingStyles.d.ts +7 -0
  185. package/dist/prosemirror/extensions/features/pastedHeadingStyles.js +74 -0
  186. package/dist/prosemirror/extensions/marks/HyperlinkExtension.js +2 -3
  187. package/dist/prosemirror/extensions/marks/markUtils.d.ts +11 -3
  188. package/dist/prosemirror/extensions/marks/markUtils.js +98 -19
  189. package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +7 -3
  190. package/dist/prosemirror/extensions/nodes/ImageExtension.js +6 -1
  191. package/dist/prosemirror/extensions/nodes/ShapeExtension.js +8 -2
  192. package/dist/prosemirror/extensions/nodes/TableExtension.js +15 -1
  193. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +8 -4
  194. package/dist/prosemirror/extensions/types.d.ts +2 -2
  195. package/dist/prosemirror/index.d.ts +3 -3
  196. package/dist/prosemirror/index.js +3 -3
  197. package/dist/prosemirror/insertOperations.d.ts +9 -2
  198. package/dist/prosemirror/insertOperations.js +9 -4
  199. package/dist/prosemirror/paragraphFormattingProvenance.d.ts +162 -0
  200. package/dist/prosemirror/paragraphFormattingProvenance.js +115 -0
  201. package/dist/prosemirror/plugins/documentStyles.d.ts +9 -1
  202. package/dist/prosemirror/plugins/documentStyles.js +11 -1
  203. package/dist/prosemirror/plugins/index.d.ts +2 -2
  204. package/dist/prosemirror/plugins/index.js +2 -2
  205. package/dist/prosemirror/plugins/revisionIds.d.ts +11 -2
  206. package/dist/prosemirror/plugins/revisionIds.js +21 -6
  207. package/dist/prosemirror/runFormattingReconciliation.js +3 -2
  208. package/dist/prosemirror/runStyleFormatting.d.ts +1 -1
  209. package/dist/prosemirror/schema/nodes.d.ts +81 -1
  210. package/dist/prosemirror/styles/resolvedStyleAttrs.js +2 -0
  211. package/dist/prosemirror/styles/styleResolver.d.ts +9 -0
  212. package/dist/prosemirror/styles/styleResolver.js +12 -0
  213. package/dist/style-engine/styleEngine.d.ts +3 -0
  214. package/dist/style-engine/styleEngine.js +3 -0
  215. package/dist/style-sets/extract.js +1 -23
  216. package/dist/style-sets/stellaStyle.js +46 -39
  217. package/dist/style-sets/styleSetNormalization.d.ts +19 -0
  218. package/dist/style-sets/styleSetNormalization.js +99 -0
  219. package/dist/types/content.d.ts +2 -2
  220. package/dist/utils/base64.d.ts +36 -0
  221. package/dist/utils/base64.js +40 -0
  222. package/dist/utils/clipboard.js +2 -1
  223. package/dist/utils/createDocument.js +145 -20
  224. package/dist/utils/headingCollector.d.ts +8 -5
  225. package/dist/utils/headingCollector.js +23 -25
  226. package/dist/utils/tableOfContentsStyle.js +9 -2
  227. package/dist/utils/units.d.ts +10 -1
  228. package/dist/utils/units.js +12 -1
  229. package/dist/utils/urlSecurity.d.ts +8 -2
  230. package/dist/utils/urlSecurity.js +21 -3
  231. package/package.json +2 -2
  232. package/dist/docx/textWhitespace.d.ts +0 -4
  233. package/dist/docx/textWhitespace.js +0 -4
  234. package/dist/layout-bridge/engine/tableWidthUtils.d.ts +0 -6
  235. package/dist/layout-bridge/engine/tableWidthUtils.js +0 -25
  236. package/dist/markdown/headings.d.ts +0 -13
  237. package/dist/markdown/headings.js +0 -20
@@ -1,6 +1,7 @@
1
1
  import { applyFolioAIEditOperations, previewFolioAIEditOperations } from "./ai-edits/apply.js";
2
2
  import { LINE_SPACING_RULE_VALUES, PARAGRAPH_ALIGNMENT_VALUES } from "./types/documentEnumValues.js";
3
3
  import { TaggedError } from "better-result";
4
+ import { sanitizeXmlCharacters } from "@stll/docx-core";
4
5
  //#region src/document-operations.ts
5
6
  const FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION = 1;
6
7
  /** Direct paragraph-alignment values accepted by the operation contract. */
@@ -138,15 +139,25 @@ const assertAllowedKeys = (value, path, allowedKeys) => {
138
139
  const unexpected = Object.keys(value).find((key) => !allowedKeys.includes(key));
139
140
  if (unexpected !== void 0) invalidBatch(`${path}.${unexpected}`, "unexpected property");
140
141
  };
142
+ /**
143
+ * Every string in a batch passes through here, so this is where a value an
144
+ * agent sent stops being able to corrupt the package it lands in: XML 1.0
145
+ * admits none of the C0 controls but tab, LF and CR, and no escape can carry
146
+ * one into a document. The batch is otherwise held to exactly what it says, so
147
+ * the rule is the narrowest one that keeps the request usable — drop what
148
+ * cannot be written, map an unpaired surrogate to U+FFFD — rather than
149
+ * rejecting a whole edit over a stray control character. The parsed batch is
150
+ * both what folio applies and what the receipt reports, so the two agree.
151
+ */
141
152
  const readString = (value, key, path) => {
142
153
  const candidate = value[key];
143
- if (typeof candidate === "string") return candidate;
154
+ if (typeof candidate === "string") return sanitizeXmlCharacters(candidate);
144
155
  return invalidBatch(`${path}.${key}`, "expected a string");
145
156
  };
146
157
  const readOptionalString = (value, key, path) => {
147
158
  const candidate = value[key];
148
159
  if (candidate === void 0) return;
149
- if (typeof candidate === "string") return candidate;
160
+ if (typeof candidate === "string") return sanitizeXmlCharacters(candidate);
150
161
  return invalidBatch(`${path}.${key}`, "expected a string when provided");
151
162
  };
152
163
  const readOptionalBoolean = (value, key, path) => {
@@ -184,7 +195,7 @@ const readOptionalStringArray = (value, key, path) => {
184
195
  if (candidate === void 0) return;
185
196
  if (!Array.isArray(candidate)) return invalidBatch(`${path}.${key}`, "expected an array when provided");
186
197
  return candidate.map((item, index) => {
187
- if (typeof item === "string") return item;
198
+ if (typeof item === "string") return sanitizeXmlCharacters(item);
188
199
  return invalidBatch(`${path}.${key}[${index}]`, "expected a string");
189
200
  });
190
201
  };
@@ -1,22 +1,4 @@
1
1
  //#region src/docx/appVersionNormalization.d.ts
2
- /**
3
- * Keep the application version a package states about itself in the form the
4
- * schema gives it.
5
- *
6
- * `AppVersion` in the extended-properties part is `XX.YYYY`: a one- or
7
- * two-digit integer, a dot, and four digits. Producers exist that write a
8
- * three-part version there instead, and folio copies `docProps/app.xml`
9
- * through verbatim when it saves a document it did not create — so a package
10
- * can carry a value with two dots in, and a save that copies it out hands a
11
- * consumer a package it refuses to open at all.
12
- *
13
- * {@link appVersionInSchemaForm} is the one mapping, and it is a pure function
14
- * of the value alone: it keeps the leading integer where the value opens with
15
- * one the form allows, and writes the build digits the form requires. Nothing
16
- * else in the part is touched, and a package that carries no extended
17
- * properties keeps carrying none — synthesizing metadata a document never
18
- * stated is a different decision.
19
- */
20
2
  declare const AppVersionSchemaError_base: import("better-result").TaggedErrorClass<"AppVersionSchemaError">;
21
3
  /**
22
4
  * A value reached the package that the schema form does not accept.
@@ -1,3 +1,4 @@
1
+ import { attachPendingRangeMarkers, attachTrailingRangeMarkers, isBlockRangeMarker } from "./blockRangeMarkers.js";
1
2
  import { parseBookmarkEnd, parseBookmarkStart } from "./bookmarkParser.js";
2
3
  import { appendBookmarkMarkerToLastParagraphInBlocks, prependBookmarkMarkersToFirstParagraphInBlocks } from "./bookmarkPlacement.js";
3
4
  import { convertBulletToUnicode } from "./bulletMarkers.js";
@@ -106,6 +107,7 @@ const parseBlockContentWithState = (parent, styles, theme, numbering, rels, medi
106
107
  const content = [];
107
108
  const children = getChildElements(parent);
108
109
  const pendingBookmarkMarkers = [];
110
+ const pendingRangeMarkers = [];
109
111
  for (const child of children) {
110
112
  const localName = getLocalName(child.name ?? "");
111
113
  if (localName === "p") {
@@ -119,6 +121,7 @@ const parseBlockContentWithState = (parent, styles, theme, numbering, rels, medi
119
121
  restartedNumIds: state.restartedNumIds,
120
122
  previousList: state.previousList
121
123
  });
124
+ attachPendingRangeMarkers(paragraph, pendingRangeMarkers);
122
125
  content.push(paragraph);
123
126
  continue;
124
127
  }
@@ -126,6 +129,7 @@ const parseBlockContentWithState = (parent, styles, theme, numbering, rels, medi
126
129
  const table = parseTable(child, styles, theme, numbering, rels, media, state.options);
127
130
  if (!table) continue;
128
131
  if (prependBookmarkMarkersToFirstParagraphInBlocks([table], pendingBookmarkMarkers)) pendingBookmarkMarkers.length = 0;
132
+ attachPendingRangeMarkers(table, pendingRangeMarkers);
129
133
  content.push(table);
130
134
  continue;
131
135
  }
@@ -143,6 +147,7 @@ const parseBlockContentWithState = (parent, styles, theme, numbering, rels, medi
143
147
  content: sdtContent ? parseBlockContentWithState(sdtContent, styles, theme, numbering, rels, media, withContainerXmlns(withContainerXmlns(state, child), sdtContent)) : []
144
148
  };
145
149
  if (prependBookmarkMarkersToFirstParagraphInBlocks(blockSdt.content, pendingBookmarkMarkers)) pendingBookmarkMarkers.length = 0;
150
+ attachPendingRangeMarkers(blockSdt, pendingRangeMarkers);
146
151
  content.push(blockSdt);
147
152
  continue;
148
153
  }
@@ -154,12 +159,15 @@ const parseBlockContentWithState = (parent, styles, theme, numbering, rels, medi
154
159
  if (localName === "bookmarkStart" || localName === "bookmarkEnd") {
155
160
  const marker = parseBookmarkMarker(child, localName);
156
161
  if (!appendBookmarkMarkerToLastParagraphInBlocks(content, marker)) pendingBookmarkMarkers.push(marker);
162
+ continue;
157
163
  }
164
+ if (isBlockRangeMarker(localName)) pendingRangeMarkers.push(captureVerbatimXml(child));
158
165
  }
159
166
  if (pendingBookmarkMarkers.length > 0) content.push({
160
167
  type: "paragraph",
161
168
  content: [...pendingBookmarkMarkers]
162
169
  });
170
+ attachTrailingRangeMarkers(content, pendingRangeMarkers);
163
171
  return content;
164
172
  };
165
173
  /**
@@ -0,0 +1,36 @@
1
+ //#region src/docx/blockRangeMarkers.d.ts
2
+ /**
3
+ * Range markers that stand between two blocks.
4
+ *
5
+ * `w:body`, `w:tc`, a header and an SDT's content all admit
6
+ * `EG_RunLevelElts` and `EG_RangeMarkupElements` beside their paragraphs, and
7
+ * every block container dropped them: a `w:permStart` between two paragraphs
8
+ * is the whole of a document-protection range, so losing it removes the
9
+ * protection from the saved file without a word. folio models none of these,
10
+ * and their position is their meaning, so they are captured verbatim and
11
+ * replayed where they stood — the same treatment `w:sdt`'s sibling markers
12
+ * already get (MS-OE376 §2.5.2.30).
13
+ *
14
+ * Bookmarks are absent from the set on purpose: they are modelled, and the
15
+ * block containers already relocate them into the neighbouring paragraph.
16
+ */
17
+ declare const isBlockRangeMarker: (localName: string) => boolean;
18
+ /** Hand the markers collected so far to the block they stood before. */
19
+ declare const attachPendingRangeMarkers: (block: {
20
+ rawMarkersBefore?: string;
21
+ }, pending: string[]) => void;
22
+ /**
23
+ * Markers after the last block ride on it, since there is no block after them.
24
+ * With no block at all they are dropped: a container holding markers and no
25
+ * content has nothing for them to delimit.
26
+ */
27
+ declare const attachTrailingRangeMarkers: (blocks: readonly {
28
+ rawMarkersAfter?: string;
29
+ }[], pending: string[]) => void;
30
+ /** Wrap a serialized block in the markup that stood around it. */
31
+ declare const withBlockRangeMarkers: (block: {
32
+ rawMarkersBefore?: string;
33
+ rawMarkersAfter?: string;
34
+ }, xml: string) => string;
35
+ //#endregion
36
+ export { attachPendingRangeMarkers, attachTrailingRangeMarkers, isBlockRangeMarker, withBlockRangeMarkers };
@@ -0,0 +1,59 @@
1
+ //#region src/docx/blockRangeMarkers.ts
2
+ /**
3
+ * Range markers that stand between two blocks.
4
+ *
5
+ * `w:body`, `w:tc`, a header and an SDT's content all admit
6
+ * `EG_RunLevelElts` and `EG_RangeMarkupElements` beside their paragraphs, and
7
+ * every block container dropped them: a `w:permStart` between two paragraphs
8
+ * is the whole of a document-protection range, so losing it removes the
9
+ * protection from the saved file without a word. folio models none of these,
10
+ * and their position is their meaning, so they are captured verbatim and
11
+ * replayed where they stood — the same treatment `w:sdt`'s sibling markers
12
+ * already get (MS-OE376 §2.5.2.30).
13
+ *
14
+ * Bookmarks are absent from the set on purpose: they are modelled, and the
15
+ * block containers already relocate them into the neighbouring paragraph.
16
+ */
17
+ const BLOCK_RANGE_MARKER_NAMES = /* @__PURE__ */ new Set([
18
+ "commentRangeEnd",
19
+ "commentRangeStart",
20
+ "customXmlDelRangeEnd",
21
+ "customXmlDelRangeStart",
22
+ "customXmlInsRangeEnd",
23
+ "customXmlInsRangeStart",
24
+ "customXmlMoveFromRangeEnd",
25
+ "customXmlMoveFromRangeStart",
26
+ "customXmlMoveToRangeEnd",
27
+ "customXmlMoveToRangeStart",
28
+ "moveFromRangeEnd",
29
+ "moveFromRangeStart",
30
+ "moveToRangeEnd",
31
+ "moveToRangeStart",
32
+ "permEnd",
33
+ "permStart"
34
+ ]);
35
+ const isBlockRangeMarker = (localName) => BLOCK_RANGE_MARKER_NAMES.has(localName);
36
+ /** Hand the markers collected so far to the block they stood before. */
37
+ const attachPendingRangeMarkers = (block, pending) => {
38
+ if (pending.length === 0) return;
39
+ block.rawMarkersBefore = pending.join("");
40
+ pending.length = 0;
41
+ };
42
+ /**
43
+ * Markers after the last block ride on it, since there is no block after them.
44
+ * With no block at all they are dropped: a container holding markers and no
45
+ * content has nothing for them to delimit.
46
+ */
47
+ const attachTrailingRangeMarkers = (blocks, pending) => {
48
+ const last = blocks.at(-1);
49
+ if (pending.length === 0 || last === void 0) {
50
+ pending.length = 0;
51
+ return;
52
+ }
53
+ last.rawMarkersAfter = pending.join("");
54
+ pending.length = 0;
55
+ };
56
+ /** Wrap a serialized block in the markup that stood around it. */
57
+ const withBlockRangeMarkers = (block, xml) => `${block.rawMarkersBefore ?? ""}${xml}${block.rawMarkersAfter ?? ""}`;
58
+ //#endregion
59
+ export { attachPendingRangeMarkers, attachTrailingRangeMarkers, isBlockRangeMarker, withBlockRangeMarkers };
@@ -1,27 +1,9 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
2
  import { XmlElement } from "./xmlParser.js";
3
3
  //#region src/docx/bookmarkParser.d.ts
4
- /**
5
- * Parse a bookmark start element (w:bookmarkStart)
6
- *
7
- * Extracts:
8
- * - id: Numeric identifier (required, matches with bookmarkEnd)
9
- * - name: Bookmark name (required, used by hyperlinks)
10
- * - colFirst: First column for table bookmarks (optional)
11
- * - colLast: Last column for table bookmarks (optional)
12
- *
13
- * @param node - The w:bookmarkStart XML element
14
- * @returns Parsed BookmarkStart object
15
- */
4
+ /** Parse a bookmark start element (w:bookmarkStart, CT_Bookmark). */
16
5
  declare function parseBookmarkStart(node: XmlElement): document_d_exports.BookmarkStart;
17
- /**
18
- * Parse a bookmark end element (w:bookmarkEnd)
19
- *
20
- * Bookmark ends only contain an ID that matches the corresponding start marker.
21
- *
22
- * @param node - The w:bookmarkEnd XML element
23
- * @returns Parsed BookmarkEnd object
24
- */
6
+ /** Parse a bookmark end element (w:bookmarkEnd, CT_MarkupRange). */
25
7
  declare function parseBookmarkEnd(node: XmlElement): document_d_exports.BookmarkEnd;
26
8
  /**
27
9
  * Bookmark map for quick lookup by ID or name
@@ -1,41 +1,17 @@
1
- import { getAttribute, parseNumericAttribute } from "./xmlParser.js";
1
+ import { parseBookmarkRangeMarker, parseMarkupRangeMarker } from "./markupRangeMarker.js";
2
2
  //#region src/docx/bookmarkParser.ts
3
- /**
4
- * Parse a bookmark start element (w:bookmarkStart)
5
- *
6
- * Extracts:
7
- * - id: Numeric identifier (required, matches with bookmarkEnd)
8
- * - name: Bookmark name (required, used by hyperlinks)
9
- * - colFirst: First column for table bookmarks (optional)
10
- * - colLast: Last column for table bookmarks (optional)
11
- *
12
- * @param node - The w:bookmarkStart XML element
13
- * @returns Parsed BookmarkStart object
14
- */
3
+ /** Parse a bookmark start element (w:bookmarkStart, CT_Bookmark). */
15
4
  function parseBookmarkStart(node) {
16
- const bookmark = {
5
+ return {
17
6
  type: "bookmarkStart",
18
- id: parseNumericAttribute(node, "w", "id") ?? 0,
19
- name: getAttribute(node, "w", "name") ?? ""
7
+ ...parseBookmarkRangeMarker(node)
20
8
  };
21
- const colFirst = parseNumericAttribute(node, "w", "colFirst");
22
- if (colFirst !== void 0) bookmark.colFirst = colFirst;
23
- const colLast = parseNumericAttribute(node, "w", "colLast");
24
- if (colLast !== void 0) bookmark.colLast = colLast;
25
- return bookmark;
26
9
  }
27
- /**
28
- * Parse a bookmark end element (w:bookmarkEnd)
29
- *
30
- * Bookmark ends only contain an ID that matches the corresponding start marker.
31
- *
32
- * @param node - The w:bookmarkEnd XML element
33
- * @returns Parsed BookmarkEnd object
34
- */
10
+ /** Parse a bookmark end element (w:bookmarkEnd, CT_MarkupRange). */
35
11
  function parseBookmarkEnd(node) {
36
12
  return {
37
13
  type: "bookmarkEnd",
38
- id: parseNumericAttribute(node, "w", "id") ?? 0
14
+ ...parseMarkupRangeMarker(node)
39
15
  };
40
16
  }
41
17
  /**
@@ -0,0 +1,13 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ import { ParseContext } from "./parseContext.js";
3
+ import { XmlElement } from "./xmlParser.js";
4
+ //#region src/docx/borderParser.d.ts
5
+ /**
6
+ * `w:val` is `use="required"` on `CT_Border`. An element without it states no
7
+ * style at all, so the border is dropped rather than invented as `none`:
8
+ * `none` is an authored token that cancels an inherited border, and a
9
+ * malformed element is not evidence the author wanted that.
10
+ */
11
+ declare function parseBorderSpec(border: XmlElement | null, context?: ParseContext): document_d_exports.BorderSpec | undefined;
12
+ //#endregion
13
+ export { parseBorderSpec };
@@ -0,0 +1,71 @@
1
+ import { BorderStyleSchema, ThemeColorSlotSchema, narrowEnum } from "./parserEnums.js";
2
+ import { getAttribute, parseNumericAttribute, parseOnOffAttribute } from "./xmlParser.js";
3
+ import { PARSE_WARNING_CODES } from "@stll/docx-core/model";
4
+ //#region src/docx/borderParser.ts
5
+ /**
6
+ * The one reader for `CT_Border`, shared by the paragraph (`w:pBdr`), style,
7
+ * table (`w:tblBorders`/`w:tcBorders`) and page (`w:pgBorders`) tiers.
8
+ *
9
+ * `w:val` is `ST_Border`, whose 193 members include two distinct "no border"
10
+ * tokens: `nil` and `none`. They are not interchangeable downstream, and an
11
+ * explicit one overrides a border inherited from the container, so the member
12
+ * the author wrote is preserved exactly. Members outside the model's known
13
+ * union survive verbatim rather than collapsing to a default, which is how the
14
+ * repo already treats `w:numFmt`, `w:suff` and `w:tab`.
15
+ */
16
+ const parseBorderColor = (border) => {
17
+ const rgb = getAttribute(border, "w", "color");
18
+ const themeColor = getAttribute(border, "w", "themeColor");
19
+ const themeTint = getAttribute(border, "w", "themeTint");
20
+ const themeShade = getAttribute(border, "w", "themeShade");
21
+ if (rgb === null && themeColor === null && themeTint === null && themeShade === null) return;
22
+ const color = {};
23
+ if (rgb === "auto") color.auto = true;
24
+ else if (rgb) color.rgb = rgb;
25
+ const validatedThemeColor = narrowEnum(themeColor, ThemeColorSlotSchema);
26
+ if (validatedThemeColor) color.themeColor = validatedThemeColor;
27
+ if (themeTint) color.themeTint = themeTint;
28
+ if (themeShade) color.themeShade = themeShade;
29
+ return color;
30
+ };
31
+ /**
32
+ * `w:val` is `use="required"` on `CT_Border`. An element without it states no
33
+ * style at all, so the border is dropped rather than invented as `none`:
34
+ * `none` is an authored token that cancels an inherited border, and a
35
+ * malformed element is not evidence the author wanted that.
36
+ */
37
+ function parseBorderSpec(border, context) {
38
+ if (!border) return;
39
+ const rawStyle = getAttribute(border, "w", "val");
40
+ if (!rawStyle) {
41
+ context?.warn({
42
+ code: PARSE_WARNING_CODES.borderWithoutValue,
43
+ element: border.name ?? "border"
44
+ });
45
+ return;
46
+ }
47
+ const spec = { style: narrowEnum(rawStyle, BorderStyleSchema) ?? rawStyle };
48
+ const color = parseBorderColor(border);
49
+ if (color) spec.color = color;
50
+ const size = parseNumericAttribute(border, "w", "sz");
51
+ if (size !== void 0) spec.size = size;
52
+ const space = parseNumericAttribute(border, "w", "space");
53
+ if (space !== void 0) spec.space = space;
54
+ const shadow = parseOnOffAttribute(border, "w", "shadow");
55
+ if (shadow !== void 0) spec.shadow = shadow;
56
+ const frame = parseOnOffAttribute(border, "w", "frame");
57
+ if (frame !== void 0) spec.frame = frame;
58
+ const artRelationshipId = getAttribute(border, "r", "id")?.trim();
59
+ if (artRelationshipId) spec.artRelationshipId = artRelationshipId;
60
+ const topLeftArtRelationshipId = getAttribute(border, "r", "topLeft")?.trim();
61
+ if (topLeftArtRelationshipId) spec.topLeftArtRelationshipId = topLeftArtRelationshipId;
62
+ const topRightArtRelationshipId = getAttribute(border, "r", "topRight")?.trim();
63
+ if (topRightArtRelationshipId) spec.topRightArtRelationshipId = topRightArtRelationshipId;
64
+ const bottomLeftArtRelationshipId = getAttribute(border, "r", "bottomLeft")?.trim();
65
+ if (bottomLeftArtRelationshipId) spec.bottomLeftArtRelationshipId = bottomLeftArtRelationshipId;
66
+ const bottomRightArtRelationshipId = getAttribute(border, "r", "bottomRight")?.trim();
67
+ if (bottomRightArtRelationshipId) spec.bottomRightArtRelationshipId = bottomRightArtRelationshipId;
68
+ return spec;
69
+ }
70
+ //#endregion
71
+ export { parseBorderSpec };
@@ -0,0 +1,165 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ //#region src/docx/builtInStyles.d.ts
3
+ /**
4
+ * The tenth `w:outlineLvl` value. 17.3.1.20: "the val attribute … can be from
5
+ * 0 to 9, where 9 specifically indicates that there is no outline level
6
+ * specifically applied to this paragraph." It is a deliberate "not a heading",
7
+ * not a tenth level. Every range test goes through
8
+ * {@link isHeadingOutlineLevel} so the reserved value keeps one meaning across
9
+ * the codebase.
10
+ *
11
+ * The same clause adds that an omitted element "is assumed to be 9". That
12
+ * default cannot be applied to a *style* definition, because 17.7.1 tells
13
+ * producers not to write a property "already been set by a previous level of
14
+ * the style hierarchy": a document that names a style `heading 1` and omits
15
+ * the level is inheriting the consumer's built-in definition, which carries
16
+ * level 0. An absent level therefore means "unspecified, ask the name", and
17
+ * only a written 9 means body text.
18
+ */
19
+ declare const BODY_TEXT_OUTLINE_LEVEL = 9;
20
+ /** True when an outline level names a heading rather than body text. */
21
+ declare const isHeadingOutlineLevel: (level: number | null | undefined) => level is number;
22
+ /**
23
+ * Compare style names the way producers actually write them. The corpus shows
24
+ * both `heading 1` (Annex L, 94.8%) and `Heading 1` (5.2%), and LibreOffice
25
+ * drops the space entirely (`Heading1`, `IntenseQuote`), so case and
26
+ * whitespace are the tolerance. A name is otherwise matched whole: a style a
27
+ * Czech template calls `Nadpis 1` stays a custom style.
28
+ */
29
+ declare const normalizeStyleName: (name: string) => string;
30
+ /**
31
+ * The `w:name` Word itself writes for each built-in, and therefore the
32
+ * spelling every style table folio authors must use. One owner: a style set and
33
+ * the classifier that reads it cannot drift apart if both name the same
34
+ * constant.
35
+ *
36
+ * Word is not uniformly cased and guessing gets it wrong, so each value is the
37
+ * spelling that dominates Microsoft Word output in the public corpus:
38
+ * `footnote text` 375 against 49 `Footnote Text`, `footer` 1145 against 2,
39
+ * `caption` 390 against 60 — but `Body Text` 424 against 2, `Title` 621
40
+ * against 2, and the auto-generated linked character styles (`Footnote Text
41
+ * Char` 248, `Endnote Text Char` 116) title-cased without exception.
42
+ * {@link normalizeStyleName} makes matching tolerant of all of it; this map is
43
+ * about what folio *writes*.
44
+ */
45
+ declare const BUILT_IN_STYLE_NAME: {
46
+ /** 4,515 Word occurrences against 3 lowercase. */
47
+ readonly normal: "Normal";
48
+ readonly bodyText: "Body Text";
49
+ readonly title: "Title";
50
+ readonly subtitle: "Subtitle";
51
+ readonly quote: "Quote";
52
+ readonly intenseQuote: "Intense Quote";
53
+ readonly listParagraph: "List Paragraph";
54
+ readonly tocHeading: "TOC Heading";
55
+ readonly caption: "caption";
56
+ readonly header: "header";
57
+ readonly footer: "footer";
58
+ readonly footnoteText: "footnote text";
59
+ readonly commentReference: "annotation reference";
60
+ readonly footnoteReference: "footnote reference";
61
+ readonly footnoteTextChar: "Footnote Text Char";
62
+ readonly endnoteText: "endnote text";
63
+ readonly endnoteReference: "endnote reference";
64
+ readonly endnoteTextChar: "Endnote Text Char";
65
+ readonly hyperlink: "Hyperlink";
66
+ readonly defaultParagraphFont: "Default Paragraph Font";
67
+ readonly noList: "No List";
68
+ readonly normalTable: "Normal Table";
69
+ readonly tableGrid: "Table Grid";
70
+ };
71
+ type BuiltInStyleName = (typeof BUILT_IN_STYLE_NAME)[keyof typeof BUILT_IN_STYLE_NAME];
72
+ /**
73
+ * The name of a built-in heading, from its zero-based outline level.
74
+ * Lowercase: 1,066 Word occurrences of `heading 1` against 13 `Heading 1`, and
75
+ * Annex L writes the latent-style exceptions the same way.
76
+ */
77
+ declare const builtInHeadingStyleName: (outlineLevel: number) => string;
78
+ /** The name of a built-in TOC entry style, from its one-based level (`toc 1`). */
79
+ declare const builtInTableOfContentsStyleName: (level: number) => string;
80
+ /**
81
+ * A document's styles indexed by what they *are* rather than by what they are
82
+ * called. Built once per document: the consumers below classify every
83
+ * paragraph, and rebuilding the maps per paragraph would make each of them
84
+ * quadratic.
85
+ */
86
+ type BuiltInStyleIndex = {
87
+ /** The style's effective `w:outlineLvl`, including 9, or undefined. */
88
+ outlineLevelOf: (styleId: string | null | undefined) => number | undefined;
89
+ /** The zero-based level a built-in heading *name* implies, or undefined. */
90
+ headingLevelFromNameOf: (styleId: string | null | undefined) => number | undefined;
91
+ /** The built-in this style is, by name, or undefined for a custom style. */
92
+ builtInNameOf: (styleId: string | null | undefined) => BuiltInStyleName | undefined;
93
+ /**
94
+ * The level an English built-in heading *id* implies, and only when the
95
+ * package defines no style under it. See {@link resolveHeadingLevel} tier 3.
96
+ */
97
+ undefinedBuiltInHeadingLevelOf: (styleId: string | null | undefined) => number | undefined;
98
+ /** The document's style id for a built-in heading level (zero-based). */
99
+ styleIdForHeadingLevel: (level: number) => string | undefined;
100
+ /** The document's style id for a built-in TOC entry level (one-based, `toc 1`). */
101
+ styleIdForTableOfContentsLevel: (level: number) => string | undefined;
102
+ /** The document's style id for a named built-in, e.g. `TOC Heading`. */
103
+ styleIdForBuiltInName: (name: BuiltInStyleName) => string | undefined;
104
+ };
105
+ declare const createBuiltInStyleIndex: (styles: Iterable<document_d_exports.Style>, docDefaults?: document_d_exports.DocDefaults | undefined) => BuiltInStyleIndex;
106
+ /** An index over a document that defines no styles: every lookup misses. */
107
+ declare const EMPTY_BUILT_IN_STYLE_INDEX: BuiltInStyleIndex;
108
+ /**
109
+ * What a consumer knows about a paragraph: its style id and whatever
110
+ * `w:outlineLvl` applies to it. The ProseMirror `outlineLevel` attr already
111
+ * holds direct-else-style resolution, so passing it here agrees with passing
112
+ * direct formatting from the DOCX model.
113
+ */
114
+ type ParagraphOutlineSource = {
115
+ styleId?: string | null | undefined;
116
+ outlineLevel?: number | null | undefined;
117
+ };
118
+ /**
119
+ * The heading level a paragraph carries, zero-based (`heading 1` is 0), or
120
+ * undefined when it is not a heading.
121
+ *
122
+ * Precedence:
123
+ *
124
+ * 1. An effective outline level decides on its own, including
125
+ * {@link BODY_TEXT_OUTLINE_LEVEL}, which means "not a heading". A style
126
+ * named `heading 5` whose outline level is 0 is a level-1 heading; a style
127
+ * named `heading 3` reset to 9 is body text. The format gives the outline
128
+ * level to field calculation (17.3.1.20) and leaves the name to the UI
129
+ * (17.7.4.9), so the level is the one the document asserts.
130
+ * 2. Only when no outline level is set anywhere does the built-in `w:name`
131
+ * decide — the style is then inheriting the consumer's own built-in
132
+ * definition, which supplies the level.
133
+ * 3. Last resort, and only for a `w:pStyle` the package defines no style for:
134
+ * the id itself, read as the English built-in id. 17.7.4.17 makes a style
135
+ * without `w:customStyle` a built-in and lets an application recognise it
136
+ * "if the associated style ID is known", which is the one case where the id
137
+ * is all the information left. `defaultParagraphStyle.ts` keeps the same
138
+ * last tier for `Normal`. A document that defines its heading styles never
139
+ * reaches this, so it cannot override a name or an outline level — and a
140
+ * localized package never writes an English id to begin with.
141
+ *
142
+ * Two consequences of rule 1 are deliberate, not oversights.
143
+ *
144
+ * **An outline level on a style that is not a heading still makes a heading.**
145
+ * The corpus has 680 such occurrences across 161 files, including `Title` at
146
+ * level 0 (42×) and `Subtitle` at level 1 (23×), plus `H1`, `Sub-heading`,
147
+ * `index heading` and a `DSTOC1-1`…`DSTOC8-8` family. Setting the level is how
148
+ * a document asks for a paragraph to be outlined, and Word's navigation pane
149
+ * and a `TOC \u` field both honour it, so folio does not second-guess a
150
+ * document that asked. Suppressing `Title` here would mean folio deciding a
151
+ * document's outline differs from Word's.
152
+ *
153
+ * **An outline level that disagrees with a built-in heading name wins.** 26
154
+ * corpus styles do this (`heading 5` at level 0, `heading 3` at level 1, and
155
+ * so on), all from non-Word producers or hand-authored fixtures. The format
156
+ * gives the level to field calculation (17.3.1.20) and the name to the user
157
+ * interface (17.7.4.9), so the level is the machine-readable claim and the
158
+ * name is a label. This is the one rule below that was not confirmed against
159
+ * Word itself.
160
+ */
161
+ declare const resolveHeadingLevel: (paragraph: ParagraphOutlineSource, index: BuiltInStyleIndex) => number | undefined;
162
+ /** True when the paragraph's style is Word's `Quote` or `Intense Quote`. */
163
+ declare const isQuoteStyle: (styleId: string | null | undefined, index: BuiltInStyleIndex) => boolean;
164
+ //#endregion
165
+ export { BODY_TEXT_OUTLINE_LEVEL, BUILT_IN_STYLE_NAME, BuiltInStyleIndex, EMPTY_BUILT_IN_STYLE_INDEX, ParagraphOutlineSource, builtInHeadingStyleName, builtInTableOfContentsStyleName, createBuiltInStyleIndex, isHeadingOutlineLevel, isQuoteStyle, normalizeStyleName, resolveHeadingLevel };