@stll/folio-core 0.54.2 → 0.55.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 (199) hide show
  1. package/dist/ai-edits/apply.js +22 -1
  2. package/dist/ai-edits/headless.js +1 -0
  3. package/dist/compare/content-types.d.ts +3 -15
  4. package/dist/controller/canonicalComposition.d.ts +36 -0
  5. package/dist/controller/canonicalComposition.js +168 -0
  6. package/dist/controller/canonicalInput.d.ts +45 -0
  7. package/dist/controller/canonicalInput.js +338 -0
  8. package/dist/controller/canonicalOperations.d.ts +22 -0
  9. package/dist/controller/canonicalOperations.js +85 -0
  10. package/dist/controller/canonicalReview.d.ts +12 -0
  11. package/dist/controller/canonicalReview.js +19 -0
  12. package/dist/controller/canonicalSession.d.ts +149 -0
  13. package/dist/controller/canonicalSession.js +1038 -0
  14. package/dist/controller/canonicalStoryEditor.d.ts +34 -0
  15. package/dist/controller/canonicalStoryEditor.js +82 -0
  16. package/dist/controller/canonicalStructure.d.ts +16 -0
  17. package/dist/controller/canonicalStructure.js +269 -0
  18. package/dist/controller/folioEditor.d.ts +1 -1
  19. package/dist/controller/folioEditor.js +9 -0
  20. package/dist/controller/headerFooterEditorManager.d.ts +4 -0
  21. package/dist/controller/headerFooterEditorManager.js +71 -3
  22. package/dist/controller/hiddenEditorApi.d.ts +35 -0
  23. package/dist/controller/hiddenEditorApi.js +25 -8
  24. package/dist/controller/hiddenEditorManager.d.ts +11 -2
  25. package/dist/controller/hiddenEditorManager.js +456 -30
  26. package/dist/controller/layoutPipeline.d.ts +1 -1
  27. package/dist/controller/layoutPipeline.js +2 -0
  28. package/dist/controller/noteEditorManager.d.ts +4 -0
  29. package/dist/controller/noteEditorManager.js +113 -15
  30. package/dist/docx/attributeRemainder.js +1 -0
  31. package/dist/docx/canonicalSessionInput.d.ts +11 -0
  32. package/dist/docx/canonicalSessionInput.js +27 -0
  33. package/dist/docx/canonicalStoryRepack.d.ts +10 -0
  34. package/dist/docx/canonicalStoryRepack.js +41 -0
  35. package/dist/docx/contentTypeRegistry.d.ts +8 -0
  36. package/dist/docx/contentTypeRegistry.js +64 -0
  37. package/dist/docx/ensureParaIds.d.ts +7 -1
  38. package/dist/docx/ensureParaIds.js +163 -211
  39. package/dist/docx/foldedListNumberFields.d.ts +85 -0
  40. package/dist/docx/foldedListNumberFields.js +267 -0
  41. package/dist/docx/headerFooterVerbatim.d.ts +14 -1
  42. package/dist/docx/headerFooterVerbatim.js +95 -2
  43. package/dist/docx/noteIds.d.ts +5 -0
  44. package/dist/docx/noteIds.js +6 -2
  45. package/dist/docx/noteReferenceMark.d.ts +3 -1
  46. package/dist/docx/noteReferenceMark.js +15 -14
  47. package/dist/docx/numberingParser.d.ts +3 -1
  48. package/dist/docx/numberingParser.js +17 -3
  49. package/dist/docx/packageParts.d.ts +3 -1
  50. package/dist/docx/packageParts.js +65 -16
  51. package/dist/docx/paragraphMarkPropertyPresence.d.ts +9 -0
  52. package/dist/docx/paragraphMarkPropertyPresence.js +21 -0
  53. package/dist/docx/paragraphParser.js +101 -53
  54. package/dist/docx/paragraphPropertySource.d.ts +15 -1
  55. package/dist/docx/paragraphPropertySource.js +76 -22
  56. package/dist/docx/parser.js +2 -1
  57. package/dist/docx/reviewHistoryNamespace.d.ts +4 -0
  58. package/dist/docx/reviewHistoryNamespace.js +4 -0
  59. package/dist/docx/reviewResolutionProvenance.d.ts +25 -0
  60. package/dist/docx/reviewResolutionProvenance.js +153 -0
  61. package/dist/docx/rezip.d.ts +7 -3
  62. package/dist/docx/rezip.js +107 -45
  63. package/dist/docx/runConsolidator.d.ts +2 -58
  64. package/dist/docx/runConsolidator.js +3 -175
  65. package/dist/docx/runParser.d.ts +2 -1
  66. package/dist/docx/runParser.js +156 -116
  67. package/dist/docx/saveDiagnostics.d.ts +11 -0
  68. package/dist/docx/saveDiagnostics.js +0 -0
  69. package/dist/docx/selectiveSave.d.ts +2 -1
  70. package/dist/docx/selectiveSave.js +4 -1
  71. package/dist/docx/selectiveXmlPatch.d.ts +16 -8
  72. package/dist/docx/selectiveXmlPatch.js +74 -2
  73. package/dist/docx/serializer/headerFooterSerializer.d.ts +5 -1
  74. package/dist/docx/serializer/headerFooterSerializer.js +19 -11
  75. package/dist/docx/serializer/noteSerializer.d.ts +2 -2
  76. package/dist/docx/serializer/noteSerializer.js +24 -8
  77. package/dist/docx/serializer/paragraphSerializer.js +41 -4
  78. package/dist/docx/serializer/partNamespaces.js +2 -1
  79. package/dist/docx/serializer/runSerializer.js +3 -1
  80. package/dist/docx/settingsHeaderFooterUpdate.d.ts +5 -0
  81. package/dist/docx/settingsHeaderFooterUpdate.js +90 -0
  82. package/dist/docx/settingsParser.d.ts +1 -2
  83. package/dist/docx/settingsParser.js +5 -6
  84. package/dist/docx/storyBlockReplay.d.ts +11 -0
  85. package/dist/docx/storyBlockReplay.js +367 -0
  86. package/dist/docx/streamingXmlParser.d.ts +22 -1
  87. package/dist/docx/streamingXmlParser.js +49 -3
  88. package/dist/docx/structuralXmlPatch.js +8 -1
  89. package/dist/docx/verbatimCapture.d.ts +3 -1
  90. package/dist/docx/verbatimCapture.js +49 -6
  91. package/dist/i18n/messages/ar.gen.d.ts +5 -0
  92. package/dist/i18n/messages/ar.gen.js +656 -0
  93. package/dist/i18n/messages/cs.gen.d.ts +5 -0
  94. package/dist/i18n/messages/cs.gen.js +656 -0
  95. package/dist/i18n/messages/de.gen.d.ts +5 -0
  96. package/dist/i18n/messages/de.gen.js +656 -0
  97. package/dist/i18n/messages/en.gen.d.ts +5 -0
  98. package/dist/i18n/messages/en.gen.js +656 -0
  99. package/dist/i18n/messages/es.gen.d.ts +5 -0
  100. package/dist/i18n/messages/es.gen.js +656 -0
  101. package/dist/i18n/messages/et.gen.d.ts +5 -0
  102. package/dist/i18n/messages/et.gen.js +656 -0
  103. package/dist/i18n/messages/fr.gen.d.ts +5 -0
  104. package/dist/i18n/messages/fr.gen.js +656 -0
  105. package/dist/i18n/messages/he.gen.d.ts +5 -0
  106. package/dist/i18n/messages/he.gen.js +656 -0
  107. package/dist/i18n/messages/hi.gen.d.ts +5 -0
  108. package/dist/i18n/messages/hi.gen.js +656 -0
  109. package/dist/i18n/messages/hu.gen.d.ts +5 -0
  110. package/dist/i18n/messages/hu.gen.js +656 -0
  111. package/dist/i18n/messages/locales.d.ts +5 -0
  112. package/dist/i18n/messages/locales.js +22 -0
  113. package/dist/i18n/messages/lt.gen.d.ts +5 -0
  114. package/dist/i18n/messages/lt.gen.js +656 -0
  115. package/dist/i18n/messages/lv.gen.d.ts +5 -0
  116. package/dist/i18n/messages/lv.gen.js +656 -0
  117. package/dist/i18n/messages/pl.gen.d.ts +5 -0
  118. package/dist/i18n/messages/pl.gen.js +656 -0
  119. package/dist/i18n/messages/pt-BR.gen.d.ts +5 -0
  120. package/dist/i18n/messages/pt-BR.gen.js +656 -0
  121. package/dist/i18n/messages/sk.gen.d.ts +5 -0
  122. package/dist/i18n/messages/sk.gen.js +656 -0
  123. package/dist/i18n/messages/tr.gen.d.ts +5 -0
  124. package/dist/i18n/messages/tr.gen.js +656 -0
  125. package/dist/i18n/messages/zh-CN.gen.d.ts +5 -0
  126. package/dist/i18n/messages/zh-CN.gen.js +656 -0
  127. package/dist/i18n/messages.d.ts +2 -3
  128. package/dist/i18n/messages.js +1 -19
  129. package/dist/internal/noteMarkerAttrs.d.ts +8 -0
  130. package/dist/internal/noteMarkerAttrs.js +16 -0
  131. package/dist/internal/pageBreakRunSourceDescendantIndex.js +1 -0
  132. package/dist/internal/prosemirrorAttrBoundary.d.ts +21 -0
  133. package/dist/internal/prosemirrorAttrBoundary.js +50 -0
  134. package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
  135. package/dist/layout-bridge/convert/footnoteLayout.js +3 -3
  136. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
  137. package/dist/layout-bridge/convert/headerFooterLayout.js +1 -0
  138. package/dist/layout-bridge/convert/noteReferences.d.ts +1 -3
  139. package/dist/layout-bridge/convert/noteReferences.js +1 -6
  140. package/dist/layout-bridge/convert/paragraphRuns.js +2 -4
  141. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +2 -2
  142. package/dist/layout-bridge/convert/toFlowBlocks.js +2 -2
  143. package/dist/managers/DocumentLoaderManager.d.ts +1 -0
  144. package/dist/managers/DocumentLoaderManager.js +8 -1
  145. package/dist/markdown/renderBlock.js +15 -1
  146. package/dist/markdown/renderParagraph.js +9 -13
  147. package/dist/markdown/renderRuns.js +3 -2
  148. package/dist/markdown/renderTable.js +2 -0
  149. package/dist/markdown/types.d.ts +3 -9
  150. package/dist/prosemirror/attrs/index.d.ts +2 -12
  151. package/dist/prosemirror/attrs/index.js +22 -48
  152. package/dist/prosemirror/bookmarkBoundaryAttrs.d.ts +2 -1
  153. package/dist/prosemirror/canonicalCommands.d.ts +97 -0
  154. package/dist/prosemirror/canonicalCommands.js +28 -0
  155. package/dist/prosemirror/commands/pageBreak.js +15 -1
  156. package/dist/prosemirror/commentReferenceAttrs.d.ts +2 -1
  157. package/dist/prosemirror/conversion/fromProseDoc.js +91 -19
  158. package/dist/prosemirror/conversion/listRenderingDefinition.d.ts +6 -0
  159. package/dist/prosemirror/conversion/listRenderingDefinition.js +97 -0
  160. package/dist/prosemirror/conversion/toProseDoc.d.ts +4 -3
  161. package/dist/prosemirror/conversion/toProseDoc.js +51 -11
  162. package/dist/prosemirror/executeEditorCommand.d.ts +11 -0
  163. package/dist/prosemirror/executeEditorCommand.js +26 -0
  164. package/dist/prosemirror/extensions/StarterKit.js +2 -0
  165. package/dist/prosemirror/extensions/core/ParagraphExtension.js +50 -19
  166. package/dist/prosemirror/extensions/features/ListExtension.js +18 -5
  167. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +5 -2
  168. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +13 -1
  169. package/dist/prosemirror/extensions/marks/TextColorExtension.js +3 -2
  170. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +2 -0
  171. package/dist/prosemirror/extensions/marks/markUtils.js +70 -8
  172. package/dist/prosemirror/extensions/nodes/NoteMarkerExtension.d.ts +6 -0
  173. package/dist/prosemirror/extensions/nodes/NoteMarkerExtension.js +36 -0
  174. package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.js +4 -2
  175. package/dist/prosemirror/listMarker.d.ts +1 -0
  176. package/dist/prosemirror/listMarker.js +16 -7
  177. package/dist/prosemirror/listNumbering.js +26 -5
  178. package/dist/prosemirror/listRenderingAttrs.d.ts +1 -0
  179. package/dist/prosemirror/listRenderingAttrs.js +3 -0
  180. package/dist/prosemirror/markupViewNotes.js +3 -2
  181. package/dist/prosemirror/moveRangeBoundaryAttrs.d.ts +2 -1
  182. package/dist/prosemirror/noteReferenceReview.js +5 -3
  183. package/dist/prosemirror/plugins/suggestionMode.js +50 -2
  184. package/dist/prosemirror/rangeAnchorAttrs.d.ts +2 -1
  185. package/dist/prosemirror/runFormattingFromMarks.d.ts +4 -1
  186. package/dist/prosemirror/runFormattingFromMarks.js +15 -1
  187. package/dist/prosemirror/runFormattingInlineCarriers.d.ts +2 -1
  188. package/dist/prosemirror/runFormattingInlineCarriers.js +4 -1
  189. package/dist/prosemirror/schema/marks.d.ts +2 -0
  190. package/dist/prosemirror/schema/nodes.d.ts +10 -2
  191. package/dist/prosemirror/textBoxAnchorAttrs.d.ts +2 -1
  192. package/dist/prosemirror/textInput.js +5 -1
  193. package/dist/prosemirror/trackedRevisionPath.js +2 -1
  194. package/dist/prosemirror/trackedRunInlineAtoms.d.ts +1 -0
  195. package/dist/prosemirror/trackedRunInlineAtoms.js +1 -0
  196. package/dist/prosemirror/validation.js +4 -0
  197. package/dist/types/content.d.ts +2 -2
  198. package/dist/utils/createDocument.js +2 -2
  199. package/package.json +14 -2
@@ -4,6 +4,7 @@ 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 { registerContentTypeParts } from "./contentTypeRegistry.js";
7
8
  import { readDocumentSectionFacts } from "./documentSectionFacts.js";
8
9
  import { normalizeDrawingIds } from "./drawingIdNormalization.js";
9
10
  import { rebindDrawingImageRelationship } from "./drawingRelationships.js";
@@ -15,6 +16,7 @@ import { parseNumbering } from "./numberingParser.js";
15
16
  import { paragraphNumberingReferenceId as paragraphNumberingReferenceId$1 } from "./numberingReference.js";
16
17
  import { isUnsafePackagePath, reconcilePackageReferences, removeUnsafeEntries } from "./packageParts.js";
17
18
  import { normalizeParaIdRangeInXmlParts } from "./paraIdRangeNormalization.js";
19
+ import { cloneParagraphWithPropertySource } from "./paragraphPropertySource.js";
18
20
  import { BLOCK_TREE_DESCENT, visitBlockTreeRecords } from "./paragraphTraversal.js";
19
21
  import { RELATIONSHIP_TYPES, parseRelationships, resolveRelativePath } from "./relsParser.js";
20
22
  import { removeResolvedHeaderFooterParts } from "./removeHeaderFooterParts.js";
@@ -30,6 +32,8 @@ import { readRootNamespaceBindings, serializePartElement } from "./serializer/pa
30
32
  import { serializeSettingsXml } from "./serializer/settingsSerializer.js";
31
33
  import { serializeStyle, serializeStylesXml } from "./serializer/stylesSerializer.js";
32
34
  import { serializeThemeXml } from "./serializer/themeSerializer.js";
35
+ import { updateEvenAndOddHeaders } from "./settingsHeaderFooterUpdate.js";
36
+ import { parseSettings } from "./settingsParser.js";
33
37
  import { hasCanonicalWordprocessingPrefixes } from "./wordprocessingPrefixes.js";
34
38
  import { WORDPROCESSINGML_NAMESPACE_URIS, findChild, findChildByNamespaceUri, findChildrenByNamespaceUri, getAttribute, getAttributeByNamespaceUri, getChildElements, getLocalName, getNamespaceUri, matchesName, parseXml, parseXmlDocument } from "./xmlParser.js";
35
39
  import { TaggedError, panic } from "better-result";
@@ -635,8 +639,8 @@ const normalizeExportDrawingIds = ({ package: docxPackage }) => {
635
639
  ...docxPackage.endnotes !== void 0 ? { endnotes: docxPackage.endnotes } : {}
636
640
  });
637
641
  };
638
- const finishRepack = async ({ document, originalZip, outputZip, originalDocument, originalCorePropertiesXml, compressionLevel, updateModifiedDate, modifiedBy, changedNoteParaIds, sectionEndpointRemoval, sectionReferenceRemovals }) => {
639
- await materializeNewHeaderFooterParts(document, outputZip, compressionLevel);
642
+ const finishRepack = async ({ document, originalZip, outputZip, originalDocument, originalCorePropertiesXml, compressionLevel, updateModifiedDate, modifiedBy, changedNoteParaIds, onDiagnostic, sectionEndpointRemoval, sectionReferenceRemovals }) => {
643
+ document = await materializeNewHeaderFooterParts(document, outputZip, compressionLevel);
640
644
  await materializeEmbeddedMedia({
641
645
  document,
642
646
  zip: outputZip,
@@ -667,7 +671,12 @@ const finishRepack = async ({ document, originalZip, outputZip, originalDocument
667
671
  compressionOptions: { level: compressionLevel }
668
672
  });
669
673
  await rebindWatermarkRelIds(document, outputZip, compressionLevel);
670
- await serializeHeadersFootersToZip(document, outputZip, compressionLevel);
674
+ await serializeHeadersFootersToZip({
675
+ doc: document,
676
+ zip: outputZip,
677
+ compressionLevel,
678
+ onDiagnostic
679
+ });
671
680
  await serializeNotesToZip({
672
681
  doc: document,
673
682
  originalZip,
@@ -677,6 +686,7 @@ const finishRepack = async ({ document, originalZip, outputZip, originalDocument
677
686
  });
678
687
  await serializeNumberingIntoZip(document, originalZip, outputZip, compressionLevel);
679
688
  await serializeAddedStylesIntoZip(document, originalZip, outputZip, compressionLevel);
689
+ await serializeHeaderFooterSettingsIntoZip(document, outputZip, compressionLevel);
680
690
  await serializeCommentsToZip(document, outputZip, compressionLevel);
681
691
  await dropAttachedTemplateReference(outputZip, compressionLevel);
682
692
  if (updateModifiedDate && originalCorePropertiesXml) {
@@ -707,6 +717,7 @@ async function repackDocxWithSectionEndpointRemoval({ document: doc, options, se
707
717
  originalCorePropertiesXml,
708
718
  compressionLevel,
709
719
  updateModifiedDate,
720
+ onDiagnostic: options.onDiagnostic,
710
721
  ...modifiedBy !== void 0 ? { modifiedBy } : {},
711
722
  ...changedNoteParaIds !== void 0 ? { changedNoteParaIds } : {},
712
723
  ...sectionEndpointRemoval !== void 0 ? { sectionEndpointRemoval } : {},
@@ -733,7 +744,7 @@ function repackDocx(doc, options = {}) {
733
744
  */
734
745
  async function repackDocxFromRaw(doc, rawContent, options = {}) {
735
746
  const { compressionLevel = 6, updateModifiedDate = true, modifiedBy, changedNoteParaIds } = options;
736
- const exportDocument = withoutOrphanCommentRanges(doc);
747
+ let exportDocument = withoutOrphanCommentRanges(doc);
737
748
  const newZip = new JSZip();
738
749
  for (const [path, file] of Object.entries(rawContent.originalZip.files)) {
739
750
  if (file.dir) {
@@ -747,7 +758,7 @@ async function repackDocxFromRaw(doc, rawContent, options = {}) {
747
758
  compressionOptions: { level: compressionLevel }
748
759
  });
749
760
  }
750
- await materializeNewHeaderFooterParts(exportDocument, newZip, compressionLevel);
761
+ exportDocument = await materializeNewHeaderFooterParts(exportDocument, newZip, compressionLevel);
751
762
  await materializeEmbeddedMedia({
752
763
  document: exportDocument,
753
764
  zip: newZip,
@@ -770,7 +781,13 @@ async function repackDocxFromRaw(doc, rawContent, options = {}) {
770
781
  compressionOptions: { level: compressionLevel }
771
782
  });
772
783
  await rebindWatermarkRelIds(exportDocument, newZip, compressionLevel);
773
- await serializeHeadersFootersToZip(exportDocument, newZip, compressionLevel);
784
+ await serializeHeadersFootersToZip({
785
+ doc: exportDocument,
786
+ zip: newZip,
787
+ compressionLevel,
788
+ onDiagnostic: options.onDiagnostic
789
+ });
790
+ await serializeHeaderFooterSettingsIntoZip(exportDocument, newZip, compressionLevel);
774
791
  await serializeNotesToZip({
775
792
  doc: exportDocument,
776
793
  originalZip: rawContent.originalZip,
@@ -1115,11 +1132,19 @@ function findMaxHeaderFooterNum(zip, prefix) {
1115
1132
  * relationship.
1116
1133
  */
1117
1134
  async function materializeNewHeaderFooterParts(doc, zip, compressionLevel) {
1118
- if (!hasUnmaterializedHeaderFooter(doc)) return;
1119
- doc.package.relationships ??= /* @__PURE__ */ new Map();
1120
- const rels = doc.package.relationships;
1135
+ if (!doc.package.headers?.size && !doc.package.footers?.size) return doc;
1136
+ doc = {
1137
+ ...doc,
1138
+ package: {
1139
+ ...doc.package,
1140
+ relationships: new Map(doc.package.relationships),
1141
+ ...doc.package.headers === void 0 ? {} : { headers: new Map(doc.package.headers) },
1142
+ ...doc.package.footers === void 0 ? {} : { footers: new Map(doc.package.footers) }
1143
+ }
1144
+ };
1145
+ const rels = doc.package.relationships ?? panic("The export owns its relationship map.");
1121
1146
  const relEntries = [];
1122
- const overrides = [];
1147
+ const contentTypeParts = [];
1123
1148
  let maxHeaderNum = findMaxHeaderFooterNum(zip, "header");
1124
1149
  let maxFooterNum = findMaxHeaderFooterNum(zip, "footer");
1125
1150
  const relsPath = "word/_rels/document.xml.rels";
@@ -1142,8 +1167,18 @@ async function materializeNewHeaderFooterParts(doc, zip, compressionLevel) {
1142
1167
  const materialize = (map, relType, prefix, contentType, isHeader) => {
1143
1168
  if (!map) return;
1144
1169
  for (const rId of [...map.keys()]) {
1145
- const existing = rels.get(rId) ?? zipRels.get(rId);
1146
- if (existing && existing.type === relType && existing.target) continue;
1170
+ const registered = zipRels.get(rId);
1171
+ const existing = registered ?? rels.get(rId);
1172
+ if (existing && existing.type === relType && existing.target) {
1173
+ rels.set(rId, existing);
1174
+ const path = resolveRelativePath(relsPath, existing.target);
1175
+ contentTypeParts.push({
1176
+ partName: `/${path}`,
1177
+ contentType
1178
+ });
1179
+ if (!registered) relEntries.push(`<Relationship Id="${escapeXmlAttribute(rId)}" Type="${relType}" Target="${escapeXmlAttribute(existing.target)}"/>`);
1180
+ continue;
1181
+ }
1147
1182
  let effectiveRId = rId;
1148
1183
  if (existing) {
1149
1184
  effectiveRId = `rId${++maxRId}`;
@@ -1152,6 +1187,12 @@ async function materializeNewHeaderFooterParts(doc, zip, compressionLevel) {
1152
1187
  map.delete(rId);
1153
1188
  map.set(effectiveRId, headerFooter);
1154
1189
  }
1190
+ const body = doc.package.document;
1191
+ doc.package.document = {
1192
+ ...body,
1193
+ content: body.content.map((block) => block.type === "paragraph" && block.sectionProperties !== void 0 ? cloneParagraphWithPropertySource(block, { sectionProperties: structuredClone(block.sectionProperties) }) : block),
1194
+ ...body.finalSectionProperties === void 0 ? {} : { finalSectionProperties: structuredClone(body.finalSectionProperties) }
1195
+ };
1155
1196
  for (const block of doc.package.document.content) if (block.type === "paragraph") remapRefs(isHeader ? block.sectionProperties?.headerReferences : block.sectionProperties?.footerReferences, rId, effectiveRId);
1156
1197
  const finalProps = doc.package.document.finalSectionProperties;
1157
1198
  remapRefs(isHeader ? finalProps?.headerReferences : finalProps?.footerReferences, rId, effectiveRId);
@@ -1163,32 +1204,29 @@ async function materializeNewHeaderFooterParts(doc, zip, compressionLevel) {
1163
1204
  target: filename
1164
1205
  });
1165
1206
  relEntries.push(`<Relationship Id="${escapeXmlAttribute(effectiveRId)}" Type="${relType}" Target="${filename}"/>`);
1166
- overrides.push(`<Override PartName="/word/${filename}" ContentType="${contentType}"/>`);
1207
+ contentTypeParts.push({
1208
+ partName: `/word/${filename}`,
1209
+ contentType
1210
+ });
1167
1211
  }
1168
1212
  };
1169
1213
  materialize(doc.package.headers, RELATIONSHIP_TYPES.header, "header", HEADER_CONTENT_TYPE, true);
1170
1214
  materialize(doc.package.footers, RELATIONSHIP_TYPES.footer, "footer", FOOTER_CONTENT_TYPE, false);
1171
- if (relEntries.length === 0) return;
1172
1215
  const compressionOptions = { level: compressionLevel };
1173
- zip.file(relsPath, relsXml.replace("</Relationships>", `${relEntries.join("")}</Relationships>`), {
1216
+ if (relEntries.length > 0) zip.file(relsPath, relsXml.replace("</Relationships>", `${relEntries.join("")}</Relationships>`), {
1174
1217
  compression: "DEFLATE",
1175
1218
  compressionOptions
1176
1219
  });
1177
1220
  const ctFile = zip.file("[Content_Types].xml");
1178
1221
  if (ctFile) {
1179
- let ctXml = await ctFile.async("text");
1180
- const missing = overrides.filter((override) => {
1181
- const partName = /PartName="(?<partName>[^"]+)"/u.exec(override)?.groups?.["partName"];
1182
- return partName ? !ctXml.includes(`PartName="${partName}"`) : true;
1222
+ const original = await ctFile.async("text");
1223
+ const updated = registerContentTypeParts(original, contentTypeParts);
1224
+ if (updated !== original) zip.file("[Content_Types].xml", updated, {
1225
+ compression: "DEFLATE",
1226
+ compressionOptions
1183
1227
  });
1184
- if (missing.length > 0) {
1185
- ctXml = ctXml.replace("</Types>", `${missing.join("")}</Types>`);
1186
- zip.file("[Content_Types].xml", ctXml, {
1187
- compression: "DEFLATE",
1188
- compressionOptions
1189
- });
1190
- }
1191
1228
  }
1229
+ return doc;
1192
1230
  }
1193
1231
  /**
1194
1232
  * A picture watermark is "model-driven" when its raw VML was cleared (so the
@@ -1299,13 +1337,7 @@ async function rebindWatermarkRelIds(doc, zip, compressionLevel) {
1299
1337
  });
1300
1338
  }
1301
1339
  }
1302
- /**
1303
- * Re-serialize every header/footer the model still owns, keyed by part path.
1304
- *
1305
- * `sourceZip` supplies each part as it stands before the save so the rebuilt
1306
- * root can keep any prefix binding only the source document declared.
1307
- */
1308
- async function collectHeaderFooterUpdates(doc, sourceZip) {
1340
+ async function collectHeaderFooterUpdates(doc, { sourceZip, onDiagnostic }) {
1309
1341
  const updates = /* @__PURE__ */ new Map();
1310
1342
  const rels = doc.package.relationships;
1311
1343
  if (!rels) return updates;
@@ -1327,19 +1359,21 @@ async function collectHeaderFooterUpdates(doc, sourceZip) {
1327
1359
  const bindings = sourceFile ? readRootNamespaceBindings(await sourceFile.async("text")) : /* @__PURE__ */ new Map();
1328
1360
  updates.set(path, serializeHeaderFooter(headerFooter, {
1329
1361
  path,
1330
- bindings
1362
+ bindings,
1363
+ onDiagnostic,
1364
+ originalBuffer: doc.originalBuffer
1331
1365
  }));
1332
1366
  }
1333
1367
  }
1334
1368
  }
1335
1369
  return updates;
1336
1370
  }
1337
- /**
1338
- * Serialize modified headers and footers into the ZIP
1339
- */
1340
- async function serializeHeadersFootersToZip(doc, zip, compressionLevel) {
1371
+ async function serializeHeadersFootersToZip({ doc, zip, compressionLevel, onDiagnostic }) {
1341
1372
  const compressionOptions = { level: compressionLevel };
1342
- for (const [filename, xml] of await collectHeaderFooterUpdates(doc, zip)) zip.file(filename, xml, {
1373
+ for (const [filename, xml] of await collectHeaderFooterUpdates(doc, {
1374
+ sourceZip: zip,
1375
+ onDiagnostic
1376
+ })) zip.file(filename, xml, {
1343
1377
  compression: "DEFLATE",
1344
1378
  compressionOptions
1345
1379
  });
@@ -1350,7 +1384,7 @@ async function serializeNotesToZip({ doc, originalZip, newZip, compressionLevel,
1350
1384
  if (footnotes.length > 0 || originalFootnotes) if (originalFootnotes) await patchNotePartIntoZip({
1351
1385
  conventionalLowerPath: "word/footnotes.xml",
1352
1386
  currentXml: serializeFootnotes(footnotes),
1353
- replacementXml: serializeNewFootnotesPart(footnotes),
1387
+ replacementXml: serializeNewFootnotesPart(footnotes, doc),
1354
1388
  baselineFrom: (xml) => serializeFootnotes(parseFootnotes(xml).getNormalFootnotes()),
1355
1389
  elementName: "footnote",
1356
1390
  changedNoteParaIds,
@@ -1363,7 +1397,7 @@ async function serializeNotesToZip({ doc, originalZip, newZip, compressionLevel,
1363
1397
  newZip,
1364
1398
  partPath: "word/footnotes.xml",
1365
1399
  relationshipType: RELATIONSHIP_TYPES.footnotes,
1366
- serializedPart: serializeNewFootnotesPart(footnotes),
1400
+ serializedPart: serializeNewFootnotesPart(footnotes, doc),
1367
1401
  compressionLevel
1368
1402
  });
1369
1403
  const endnotes = doc.package.endnotes ?? [];
@@ -1371,7 +1405,7 @@ async function serializeNotesToZip({ doc, originalZip, newZip, compressionLevel,
1371
1405
  if (endnotes.length > 0 || originalEndnotes) if (originalEndnotes) await patchNotePartIntoZip({
1372
1406
  conventionalLowerPath: "word/endnotes.xml",
1373
1407
  currentXml: serializeEndnotes(endnotes),
1374
- replacementXml: serializeNewEndnotesPart(endnotes),
1408
+ replacementXml: serializeNewEndnotesPart(endnotes, doc),
1375
1409
  baselineFrom: (xml) => serializeEndnotes(parseEndnotes(xml).getNormalEndnotes()),
1376
1410
  elementName: "endnote",
1377
1411
  changedNoteParaIds,
@@ -1384,7 +1418,7 @@ async function serializeNotesToZip({ doc, originalZip, newZip, compressionLevel,
1384
1418
  newZip,
1385
1419
  partPath: "word/endnotes.xml",
1386
1420
  relationshipType: RELATIONSHIP_TYPES.endnotes,
1387
- serializedPart: serializeNewEndnotesPart(endnotes),
1421
+ serializedPart: serializeNewEndnotesPart(endnotes, doc),
1388
1422
  compressionLevel
1389
1423
  });
1390
1424
  }
@@ -1413,6 +1447,32 @@ async function materializeNewNotePart({ contentType, newZip, partPath, relations
1413
1447
  compressionOptions
1414
1448
  });
1415
1449
  }
1450
+ /** Only this modeled switch changes on story lifecycle edits; preserve all other settings. */
1451
+ async function serializeHeaderFooterSettingsIntoZip(doc, zip, compressionLevel) {
1452
+ if (!doc.package.settings) return;
1453
+ const enabled = doc.package.settings.evenAndOddHeaders;
1454
+ const file = zip.file(SETTINGS_PART);
1455
+ if (!file) {
1456
+ if (enabled === void 0) return;
1457
+ await materializeNewNotePart({
1458
+ contentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml",
1459
+ newZip: zip,
1460
+ partPath: "word/settings.xml",
1461
+ relationshipType: RELATIONSHIP_TYPES.settings,
1462
+ serializedPart: serializeSettingsXml(doc.package.settings),
1463
+ compressionLevel
1464
+ });
1465
+ return;
1466
+ }
1467
+ const xml = await file.async("text");
1468
+ if (parseSettings(xml).evenAndOddHeaders === enabled) return;
1469
+ const patched = updateEvenAndOddHeaders(xml, enabled);
1470
+ if (patched === null) throw new DocxPackageFidelityError("Cannot update malformed header/footer settings");
1471
+ zip.file(file.name, patched, {
1472
+ compression: "DEFLATE",
1473
+ compressionOptions: { level: compressionLevel }
1474
+ });
1475
+ }
1416
1476
  const numberingBaselines = /* @__PURE__ */ new WeakMap();
1417
1477
  async function serializeNumberingIntoZip(doc, originalZip, newZip, compressionLevel) {
1418
1478
  const numbering = doc.package.numbering;
@@ -1807,7 +1867,8 @@ async function createDocx(doc, properties = {}) {
1807
1867
  originalDocument: source.document,
1808
1868
  originalCorePropertiesXml: source.corePropertiesXml,
1809
1869
  compressionLevel: 6,
1810
- updateModifiedDate: true
1870
+ updateModifiedDate: true,
1871
+ onDiagnostic: properties.onDiagnostic
1811
1872
  });
1812
1873
  }
1813
1874
  const zip = await createDocumentSeedZip(doc, properties);
@@ -1818,7 +1879,8 @@ async function createDocx(doc, properties = {}) {
1818
1879
  originalDocument: void 0,
1819
1880
  originalCorePropertiesXml: await zip.file("docProps/core.xml")?.async("text"),
1820
1881
  compressionLevel: 6,
1821
- updateModifiedDate: true
1882
+ updateModifiedDate: true,
1883
+ onDiagnostic: properties.onDiagnostic
1822
1884
  });
1823
1885
  }
1824
1886
  const createDocumentSeedZip = async (doc, properties) => {
@@ -1,62 +1,6 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
+ import { formattingEquals, isTextOnlyRun, runsMergeable } from "@stll/docx-core/ops";
2
3
  //#region src/docx/runConsolidator.d.ts
3
- /**
4
- * Every `TextFormatting` field, named so the comparison below is total.
5
- *
6
- * A field this comparison forgets is a field one of two merged runs loses,
7
- * silently and at parse time. Naming them turns a field added to the model
8
- * without a comparison into a compile error rather than a fidelity defect.
9
- */
10
- type ComparedTextFormattingField = "bold" | "boldCs" | "italic" | "italicCs" | "underline" | "strike" | "doubleStrike" | "vertAlign" | "smallCaps" | "allCaps" | "hidden" | "noProof" | "color" | "highlight" | "shading" | "fontSize" | "fontSizeCs" | "fontFamily" | "language" | "spacing" | "position" | "scale" | "kerning" | "effect" | "emphasisMark" | "emboss" | "imprint" | "outline" | "shadow" | "rtl" | "cs" | "styleId" | "preserved";
11
- type ComparedTextFormatting = document_d_exports.ExhaustiveFields<document_d_exports.TextFormatting, ComparedTextFormattingField>;
12
- /**
13
- * Check if two TextFormatting objects are equivalent
14
- *
15
- * Uses deep comparison of all properties to determine if runs
16
- * can be merged without losing formatting information.
17
- */
18
- declare function formattingEquals(a: ComparedTextFormatting | undefined, b: ComparedTextFormatting | undefined): boolean;
19
- /**
20
- * Check if a run contains only text content
21
- * (runs with special content like images, fields, etc. should not be merged)
22
- */
23
- declare function isTextOnlyRun(run: document_d_exports.Run): boolean;
24
- /**
25
- * Check if a run can be merged with another run
26
- * Runs with breaks, tabs, images, fields, etc. act as merge boundaries
27
- */
28
- declare function canMergeRun(run: document_d_exports.Run): boolean;
29
- /**
30
- * Every `Run` field, named by what a merge does with it.
31
- *
32
- * The merged run is the survivor spread whole, so a field added to the model
33
- * is carried over from one side by default. Naming each one here forces the
34
- * next field to state whether that default is right before it can compile.
35
- */
36
- type MergeDecidedRunField =
37
- /** Identical for every run. */
38
- "type" |
39
- /** Concatenated; see {@link mergeRunContent}. */
40
- "content" |
41
- /** Must be equal, and the survivor keeps it. */
42
- "formatting" |
43
- /** Must be equal, and the survivor keeps it. */
44
- "preservedAttributes" |
45
- /** Refuses the merge outright; see {@link canMergeRun}. */
46
- "propertyChanges";
47
- type MergeDecidedRun = document_d_exports.ExhaustiveFields<document_d_exports.Run, MergeDecidedRunField>;
48
- /**
49
- * May two adjacent runs become one?
50
- *
51
- * Every consolidation site asks this one question, because a merge that any
52
- * one site decides differently is a merge the next parse undoes. A run holds
53
- * three records a merged run can hold only one of: its typed formatting, the
54
- * attributes `w:r` carried that the model has no field for, and the `w:rPr`
55
- * children no reader took a value from. Merging two runs that disagree on any
56
- * of them discards the loser's copy, which is how both runs' `w:rsid*` used to
57
- * vanish at parse time — before the editor, before any gate could see it.
58
- */
59
- declare function runsMergeable(a: MergeDecidedRun, b: MergeDecidedRun): boolean;
60
4
  /**
61
5
  * Consolidate an array of runs by merging consecutive runs with identical formatting
62
6
  *
@@ -93,4 +37,4 @@ declare function getConsolidationStats(originalParagraphs: document_d_exports.Pa
93
37
  reductionPercentage: number;
94
38
  };
95
39
  //#endregion
96
- export { canMergeRun, consolidateParagraph, consolidateParagraphContent, consolidateRuns, countRuns, formattingEquals, getConsolidationStats, isTextOnlyRun, runsMergeable };
40
+ export { consolidateParagraph, consolidateParagraphContent, consolidateRuns, countRuns, formattingEquals, getConsolidationStats, isTextOnlyRun, runsMergeable };
@@ -1,180 +1,8 @@
1
1
  import { cloneParagraphWithPropertySource } from "./paragraphPropertySource.js";
2
2
  import { runHoldsPayload } from "./runPayload.js";
3
+ import { formattingEquals, isTextOnlyRun, mergeRunContent, runsMergeable, runsMergeable as runsMergeable$1 } from "@stll/docx-core/ops";
3
4
  //#region src/docx/runConsolidator.ts
4
5
  /**
5
- * Check if two TextFormatting objects are equivalent
6
- *
7
- * Uses deep comparison of all properties to determine if runs
8
- * can be merged without losing formatting information.
9
- */
10
- function formattingEquals(a, b) {
11
- if (!a && !b) return true;
12
- if (!a || !b) return false;
13
- if (a.bold !== b.bold) return false;
14
- if (a.boldCs !== b.boldCs) return false;
15
- if (a.italic !== b.italic) return false;
16
- if (a.italicCs !== b.italicCs) return false;
17
- if (a.strike !== b.strike) return false;
18
- if (a.doubleStrike !== b.doubleStrike) return false;
19
- if (a.smallCaps !== b.smallCaps) return false;
20
- if (a.allCaps !== b.allCaps) return false;
21
- if (a.hidden !== b.hidden) return false;
22
- if (a.noProof !== b.noProof) return false;
23
- if (a.emboss !== b.emboss) return false;
24
- if (a.imprint !== b.imprint) return false;
25
- if (a.outline !== b.outline) return false;
26
- if (a.shadow !== b.shadow) return false;
27
- if (a.rtl !== b.rtl) return false;
28
- if (a.cs !== b.cs) return false;
29
- if (a.fontSize !== b.fontSize) return false;
30
- if (a.fontSizeCs !== b.fontSizeCs) return false;
31
- if (a.spacing !== b.spacing) return false;
32
- if (a.position !== b.position) return false;
33
- if (a.scale !== b.scale) return false;
34
- if (a.kerning !== b.kerning) return false;
35
- if (a.vertAlign !== b.vertAlign) return false;
36
- if (a.highlight !== b.highlight) return false;
37
- if (a.effect !== b.effect) return false;
38
- if (a.emphasisMark !== b.emphasisMark) return false;
39
- if (a.styleId !== b.styleId) return false;
40
- if (!underlineEquals(a.underline, b.underline)) return false;
41
- if (!colorEquals(a.color, b.color)) return false;
42
- if (!shadingEquals(a.shading, b.shading)) return false;
43
- if (!fontFamilyEquals(a.fontFamily, b.fontFamily)) return false;
44
- if (!languageEquals(a.language, b.language)) return false;
45
- if (!preservedMarkupEquals(a.preserved, b.preserved)) return false;
46
- return true;
47
- }
48
- /**
49
- * Compare two verbatim sinks position by position.
50
- *
51
- * The sink is ordered — by `index`, then by source order within an index — and
52
- * the order is what puts the markup back between the same modelled siblings,
53
- * so equality is by sequence, not by set.
54
- */
55
- function preservedMarkupEquals(a, b) {
56
- const left = a?.children ?? [];
57
- const right = b?.children ?? [];
58
- if (left.length !== right.length) return false;
59
- return left.every(({ index, xml }, position) => {
60
- const other = right[position];
61
- return other !== void 0 && other.index === index && other.xml === xml;
62
- });
63
- }
64
- /**
65
- * Compare two attribute remainders as sets.
66
- *
67
- * An element cannot carry one expanded name twice, so (namespace, name) orders
68
- * a remainder totally and the sorted keys compare exactly. Order is a set
69
- * comparison rather than a sequence one because attribute order in XML says
70
- * nothing: two runs that spelled the same attributes in a different order
71
- * carry the same remainder and may still become one run.
72
- */
73
- function preservedAttributesEqual(a, b) {
74
- const left = canonicalRemainderKeys(a);
75
- const right = canonicalRemainderKeys(b);
76
- return left.length === right.length && left.every((key, index) => key === right[index]);
77
- }
78
- /** `\u0000` cannot appear in an XML name, namespace URI or attribute value. */
79
- const REMAINDER_KEY_SEPARATOR = "\0";
80
- const canonicalRemainderKeys = (attributes) => (attributes ?? []).map(({ namespace, name, value }) => [
81
- namespace ?? "",
82
- name,
83
- value
84
- ].join(REMAINDER_KEY_SEPARATOR)).sort();
85
- function languageEquals(a, b) {
86
- return a?.val === b?.val && a?.eastAsia === b?.eastAsia && a?.bidi === b?.bidi;
87
- }
88
- /**
89
- * Compare underline settings
90
- */
91
- function underlineEquals(a, b) {
92
- if (!a && !b) return true;
93
- if (!a || !b) return false;
94
- if (a.style !== b.style) return false;
95
- return colorEquals(a.color, b.color);
96
- }
97
- /**
98
- * Compare color values
99
- */
100
- function colorEquals(a, b) {
101
- if (!a && !b) return true;
102
- if (!a || !b) return false;
103
- return a.rgb === b.rgb && a.auto === b.auto && a.themeColor === b.themeColor && a.themeTint === b.themeTint && a.themeShade === b.themeShade;
104
- }
105
- /**
106
- * Compare shading properties
107
- */
108
- function shadingEquals(a, b) {
109
- if (!a && !b) return true;
110
- if (!a || !b) return false;
111
- if (a.pattern !== b.pattern) return false;
112
- if (!colorEquals(a.color, b.color)) return false;
113
- if (!colorEquals(a.fill, b.fill)) return false;
114
- return true;
115
- }
116
- /**
117
- * Compare font family settings
118
- */
119
- function fontFamilyEquals(a, b) {
120
- if (!a && !b) return true;
121
- if (!a || !b) return false;
122
- return a.ascii === b.ascii && a.hAnsi === b.hAnsi && a.eastAsia === b.eastAsia && a.cs === b.cs && a.asciiTheme === b.asciiTheme && a.hAnsiTheme === b.hAnsiTheme && a.eastAsiaTheme === b.eastAsiaTheme && a.csTheme === b.csTheme;
123
- }
124
- /**
125
- * Check if a run contains only text content
126
- * (runs with special content like images, fields, etc. should not be merged)
127
- */
128
- function isTextOnlyRun(run) {
129
- return run.content.every((c) => c.type === "text" || c.type === "softHyphen" || c.type === "noBreakHyphen");
130
- }
131
- /**
132
- * Check if run content can be merged (simple text types)
133
- */
134
- function isMergeableContent(content) {
135
- return content.type === "text" || content.type === "softHyphen" || content.type === "noBreakHyphen";
136
- }
137
- /**
138
- * Check if a run can be merged with another run
139
- * Runs with breaks, tabs, images, fields, etc. act as merge boundaries
140
- */
141
- function canMergeRun(run) {
142
- if (run.propertyChanges && run.propertyChanges.length > 0) return false;
143
- if (!runHoldsPayload(run)) return false;
144
- return run.content.every(isMergeableContent);
145
- }
146
- /**
147
- * May two adjacent runs become one?
148
- *
149
- * Every consolidation site asks this one question, because a merge that any
150
- * one site decides differently is a merge the next parse undoes. A run holds
151
- * three records a merged run can hold only one of: its typed formatting, the
152
- * attributes `w:r` carried that the model has no field for, and the `w:rPr`
153
- * children no reader took a value from. Merging two runs that disagree on any
154
- * of them discards the loser's copy, which is how both runs' `w:rsid*` used to
155
- * vanish at parse time — before the editor, before any gate could see it.
156
- */
157
- function runsMergeable(a, b) {
158
- return canMergeRun(a) && canMergeRun(b) && formattingEquals(a.formatting, b.formatting) && preservedAttributesEqual(a.preservedAttributes, b.preservedAttributes);
159
- }
160
- /**
161
- * Merge the content of two runs into a single content array
162
- */
163
- function mergeRunContent(content1, content2) {
164
- const result = [];
165
- for (const c of content1) result.push(c);
166
- const lastText = result.at(-1);
167
- const firstText = content2[0];
168
- if (lastText?.type === "text" && firstText?.type === "text") {
169
- result[result.length - 1] = {
170
- type: "text",
171
- text: lastText.text + firstText.text
172
- };
173
- for (let i = 1; i < content2.length; i++) result.push(content2[i]);
174
- } else for (const c of content2) result.push(c);
175
- return result;
176
- }
177
- /**
178
6
  * Consolidate an array of runs by merging consecutive runs with identical formatting
179
7
  *
180
8
  * @param runs - Array of runs to consolidate
@@ -200,7 +28,7 @@ function consolidateRuns(runs) {
200
28
  };
201
29
  continue;
202
30
  }
203
- if (runsMergeable(current, run)) current = {
31
+ if (runsMergeable$1(current, run)) current = {
204
32
  ...current,
205
33
  content: mergeRunContent(current.content, run.content)
206
34
  };
@@ -276,4 +104,4 @@ function getConsolidationStats(originalParagraphs, consolidatedParagraphs) {
276
104
  };
277
105
  }
278
106
  //#endregion
279
- export { canMergeRun, consolidateParagraph, consolidateParagraphContent, consolidateRuns, countRuns, formattingEquals, getConsolidationStats, isTextOnlyRun, runsMergeable };
107
+ export { consolidateParagraph, consolidateParagraphContent, consolidateRuns, countRuns, formattingEquals, getConsolidationStats, isTextOnlyRun, runsMergeable };
@@ -60,7 +60,8 @@ declare const isBaselineVertAlign: (vertAlign: document_d_exports.TextFormatting
60
60
  * it beat are not kept.
61
61
  *
62
62
  * An element that states nothing at all still yields a record: `undefined` here
63
- * means the owner carried no `w:rPr`, never that the one it carried was empty.
63
+ * means the owner carried no `w:rPr`, or a paragraph-mark carrier contained
64
+ * only metadata owned by the paragraph. An authored empty set remains {}.
64
65
  */
65
66
  declare function parseRunProperties(rPr: XmlElement | null, theme: document_d_exports.Theme | null, owner: RunPropertyOwner): document_d_exports.TextFormatting | undefined;
66
67
  /**