@portone/docx-editor 0.2.1 → 0.4.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 (253) hide show
  1. package/CHANGELOG.md +155 -0
  2. package/CONTRIBUTING.md +5 -1
  3. package/dist/DocxEditor.d.ts +6 -0
  4. package/dist/DocxEditor.js +13 -29
  5. package/dist/core.d.ts +23 -8
  6. package/dist/core.js +9 -4
  7. package/dist/docx/cloning.d.ts +38 -0
  8. package/dist/docx/cloning.js +74 -0
  9. package/dist/docx/commentOnlyChange.d.ts +12 -7
  10. package/dist/docx/commentOnlyChange.js +9 -156
  11. package/dist/docx/comments/constants.d.ts +0 -1
  12. package/dist/docx/comments/constants.js +0 -2
  13. package/dist/docx/comments/grammar.d.ts +10 -4
  14. package/dist/docx/comments/grammar.js +12 -2
  15. package/dist/docx/comments/{verifying.d.ts → parts.d.ts} +26 -18
  16. package/dist/docx/comments/parts.js +279 -0
  17. package/dist/docx/comments/people.d.ts +11 -2
  18. package/dist/docx/comments/people.js +16 -92
  19. package/dist/docx/comments/policy.d.ts +7 -0
  20. package/dist/docx/comments/policy.js +43 -0
  21. package/dist/docx/comments/reading.js +8 -10
  22. package/dist/docx/comments/writing.d.ts +19 -7
  23. package/dist/docx/comments/writing.js +59 -112
  24. package/dist/docx/documentSettings.d.ts +7 -0
  25. package/dist/docx/documentSettings.js +10 -2
  26. package/dist/docx/exportDocx.d.ts +40 -5
  27. package/dist/docx/exportDocx.js +88 -74
  28. package/dist/docx/exportRefs.d.ts +5 -2
  29. package/dist/docx/exportRefs.js +3 -1
  30. package/dist/docx/fidelity.d.ts +43 -0
  31. package/dist/docx/fidelity.js +64 -0
  32. package/dist/docx/formatting/attrs.d.ts +45 -0
  33. package/dist/docx/formatting/attrs.js +86 -0
  34. package/dist/docx/formatting/context.d.ts +39 -0
  35. package/dist/docx/formatting/context.js +60 -0
  36. package/dist/docx/formatting/direct.d.ts +6 -5
  37. package/dist/docx/formatting/direct.js +52 -78
  38. package/dist/docx/formatting/resolve.d.ts +51 -0
  39. package/dist/docx/formatting/resolve.js +160 -0
  40. package/dist/docx/formatting/runProperties.d.ts +93 -0
  41. package/dist/docx/formatting/runProperties.js +316 -0
  42. package/dist/docx/formatting/styles.d.ts +25 -2
  43. package/dist/docx/formatting/styles.js +87 -6
  44. package/dist/docx/formatting/tabStops.js +7 -22
  45. package/dist/docx/formatting.d.ts +4 -1
  46. package/dist/docx/formatting.js +4 -1
  47. package/dist/docx/headersFooters.js +6 -13
  48. package/dist/docx/hyperlink.d.ts +14 -4
  49. package/dist/docx/hyperlink.js +4 -1
  50. package/dist/docx/identities.d.ts +67 -0
  51. package/dist/docx/identities.js +174 -0
  52. package/dist/docx/importDocx.d.ts +12 -1
  53. package/dist/docx/importDocx.js +90 -88
  54. package/dist/docx/importParagraph.d.ts +1 -1
  55. package/dist/docx/importParagraph.js +4 -3
  56. package/dist/docx/importTable.d.ts +2 -2
  57. package/dist/docx/importTable.js +69 -56
  58. package/dist/docx/invariants.d.ts +33 -0
  59. package/dist/docx/invariants.js +269 -0
  60. package/dist/docx/media.d.ts +6 -4
  61. package/dist/docx/media.js +6 -37
  62. package/dist/docx/newLists.d.ts +50 -0
  63. package/dist/docx/newLists.js +59 -0
  64. package/dist/docx/notes.js +3 -7
  65. package/dist/docx/numberingPlanner.d.ts +11 -0
  66. package/dist/docx/numberingPlanner.js +50 -0
  67. package/dist/docx/packageParts.d.ts +42 -0
  68. package/dist/docx/packageParts.js +135 -0
  69. package/dist/docx/pageGeometry.d.ts +2 -0
  70. package/dist/docx/pageGeometry.js +18 -9
  71. package/dist/docx/paraProps.d.ts +10 -22
  72. package/dist/docx/paraProps.js +47 -76
  73. package/dist/docx/partPlan.d.ts +36 -0
  74. package/dist/docx/partPlan.js +59 -0
  75. package/dist/docx/protectionPolicy.d.ts +127 -0
  76. package/dist/docx/protectionPolicy.js +169 -0
  77. package/dist/docx/relationships.d.ts +1 -1
  78. package/dist/docx/relationships.js +8 -13
  79. package/dist/docx/runProps.d.ts +9 -22
  80. package/dist/docx/runProps.js +15 -168
  81. package/dist/docx/scan.js +20 -52
  82. package/dist/docx/sdt.js +9 -34
  83. package/dist/docx/sdtProps.d.ts +8 -1
  84. package/dist/docx/sdtProps.js +10 -0
  85. package/dist/docx/serializeBlock.d.ts +2 -0
  86. package/dist/docx/serializeBlock.js +8 -5
  87. package/dist/docx/serializeTable.js +32 -15
  88. package/dist/docx/session.d.ts +42 -14
  89. package/dist/docx/session.js +53 -17
  90. package/dist/docx/tableFormatting/conditions.d.ts +76 -0
  91. package/dist/docx/tableFormatting/conditions.js +196 -0
  92. package/dist/docx/tableFormatting/editing.d.ts +7 -3
  93. package/dist/docx/tableFormatting/editing.js +111 -143
  94. package/dist/docx/tableFormatting/reading.d.ts +63 -23
  95. package/dist/docx/tableFormatting/reading.js +132 -38
  96. package/dist/docx/tableFormatting.d.ts +1 -0
  97. package/dist/docx/tableFormatting.js +1 -0
  98. package/dist/docx/tableTemplate.js +30 -19
  99. package/dist/download.d.ts +8 -5
  100. package/dist/download.js +2 -0
  101. package/dist/editor/clipboard/inlineFormatting.js +19 -30
  102. package/dist/editor/commands/comments/editing.d.ts +6 -2
  103. package/dist/editor/commands/comments/editing.js +19 -31
  104. package/dist/editor/commands/exportQueries.d.ts +15 -0
  105. package/dist/editor/commands/exportQueries.js +14 -0
  106. package/dist/editor/commands/fidelityQueries.d.ts +12 -0
  107. package/dist/editor/commands/fidelityQueries.js +8 -0
  108. package/dist/editor/commands/formatting/editing.d.ts +2 -2
  109. package/dist/editor/commands/formatting/editing.js +26 -108
  110. package/dist/editor/commands/formatting/propertyCommands.d.ts +21 -0
  111. package/dist/editor/commands/formatting/propertyCommands.js +108 -0
  112. package/dist/editor/commands/formatting/shared.d.ts +3 -3
  113. package/dist/editor/commands/formatting/shared.js +5 -2
  114. package/dist/editor/commands/indentCommands.js +5 -4
  115. package/dist/editor/commands/index.d.ts +11 -0
  116. package/dist/editor/commands/index.js +5 -0
  117. package/dist/editor/commands/linkCommands.js +5 -6
  118. package/dist/editor/commands/listCommands.d.ts +6 -3
  119. package/dist/editor/commands/listCommands.js +73 -30
  120. package/dist/editor/commands/lockCommands.d.ts +7 -1
  121. package/dist/editor/commands/lockCommands.js +35 -46
  122. package/dist/editor/commands/paragraphCommands.js +33 -31
  123. package/dist/editor/commands/spacingCommands.js +1 -1
  124. package/dist/editor/createEditor.d.ts +17 -29
  125. package/dist/editor/createEditor.js +58 -58
  126. package/dist/editor/documentStyles.d.ts +11 -35
  127. package/dist/editor/documentStyles.js +9 -52
  128. package/dist/editor/editorDocument.d.ts +57 -0
  129. package/dist/editor/editorDocument.js +86 -0
  130. package/dist/editor/externalClipboard.d.ts +13 -2
  131. package/dist/editor/externalClipboard.js +76 -75
  132. package/dist/editor/insertTable.js +4 -3
  133. package/dist/editor/paragraphEdits.d.ts +27 -19
  134. package/dist/editor/paragraphEdits.js +18 -19
  135. package/dist/editor/paragraphPlacement.d.ts +11 -0
  136. package/dist/editor/paragraphPlacement.js +18 -0
  137. package/dist/editor/plugins/displayDerivation.d.ts +47 -0
  138. package/dist/editor/plugins/displayDerivation.js +78 -0
  139. package/dist/editor/plugins/imagePaste.js +11 -7
  140. package/dist/editor/plugins/keymap.js +18 -3
  141. package/dist/editor/plugins/numberingDecorations.d.ts +14 -14
  142. package/dist/editor/plugins/numberingDecorations.js +50 -19
  143. package/dist/editor/plugins/paragraphDisplay.d.ts +7 -0
  144. package/dist/editor/plugins/paragraphDisplay.js +57 -0
  145. package/dist/editor/plugins/tabLayout.js +1 -1
  146. package/dist/editor/plugins/tableDisplay.d.ts +6 -0
  147. package/dist/editor/plugins/tableDisplay.js +30 -0
  148. package/dist/index.d.ts +2 -0
  149. package/dist/model/format.d.ts +80 -8
  150. package/dist/model/format.js +100 -22
  151. package/dist/model/tabStops.d.ts +9 -0
  152. package/dist/model/tabStops.js +18 -0
  153. package/dist/numbering/listRegistry.d.ts +37 -0
  154. package/dist/numbering/listRegistry.js +112 -0
  155. package/dist/numbering/listTemplate.d.ts +26 -17
  156. package/dist/numbering/listTemplate.js +88 -25
  157. package/dist/numbering/markers.d.ts +15 -6
  158. package/dist/numbering/markers.js +26 -59
  159. package/dist/numbering/parseNumbering.d.ts +84 -6
  160. package/dist/numbering/parseNumbering.js +131 -44
  161. package/dist/numbering/spellers.d.ts +15 -0
  162. package/dist/numbering/spellers.js +96 -0
  163. package/dist/numbering/writeNumbering.d.ts +8 -7
  164. package/dist/numbering/writeNumbering.js +28 -39
  165. package/dist/ooxml/childOrder.d.ts +34 -0
  166. package/dist/ooxml/childOrder.js +496 -0
  167. package/dist/ooxml/conformance.d.ts +28 -0
  168. package/dist/ooxml/conformance.js +20 -0
  169. package/dist/ooxml/element.d.ts +24 -11
  170. package/dist/ooxml/element.js +31 -12
  171. package/dist/ooxml/errors.d.ts +6 -5
  172. package/dist/ooxml/image.d.ts +4 -0
  173. package/dist/ooxml/image.js +2 -5
  174. package/dist/ooxml/names.d.ts +9 -0
  175. package/dist/ooxml/names.js +8 -1
  176. package/dist/ooxml/partSplice.d.ts +67 -0
  177. package/dist/ooxml/partSplice.js +176 -0
  178. package/dist/ooxml/props.d.ts +112 -0
  179. package/dist/{docx/propsXml.js → ooxml/props.js} +72 -200
  180. package/dist/ooxml/simpleTypes.d.ts +103 -0
  181. package/dist/ooxml/simpleTypes.js +182 -0
  182. package/dist/ooxml/tabStops.js +8 -27
  183. package/dist/ooxml/tagScan.d.ts +34 -0
  184. package/dist/ooxml/tagScan.js +108 -0
  185. package/dist/ooxml/units.d.ts +25 -13
  186. package/dist/ooxml/units.js +65 -31
  187. package/dist/ooxml/xml.d.ts +35 -15
  188. package/dist/ooxml/xml.js +43 -11
  189. package/dist/page/PageGuides.d.ts +1 -1
  190. package/dist/page/PageGuides.js +0 -13
  191. package/dist/page/blockKinds.d.ts +61 -0
  192. package/dist/page/blockKinds.js +11 -0
  193. package/dist/page/kinds/index.d.ts +6 -0
  194. package/dist/page/kinds/index.js +10 -0
  195. package/dist/page/kinds/paragraphKind.d.ts +10 -0
  196. package/dist/page/kinds/paragraphKind.js +71 -0
  197. package/dist/page/kinds/tableKind.d.ts +10 -0
  198. package/dist/page/kinds/tableKind.js +178 -0
  199. package/dist/page/measureBlocks.d.ts +3 -14
  200. package/dist/page/measureBlocks.js +24 -45
  201. package/dist/page/pageDecorations.d.ts +27 -44
  202. package/dist/page/pageDecorations.js +70 -152
  203. package/dist/page/pageLayout.d.ts +2 -29
  204. package/dist/page/pageLayout.js +39 -16
  205. package/dist/page/usePageLayout.d.ts +2 -24
  206. package/dist/page/usePageLayout.js +5 -48
  207. package/dist/schema/attrRoles.d.ts +25 -18
  208. package/dist/schema/attrRoles.js +110 -68
  209. package/dist/schema/displayDerivation.d.ts +82 -0
  210. package/dist/schema/displayDerivation.js +130 -0
  211. package/dist/schema/docxSchema.d.ts +3 -0
  212. package/dist/schema/docxSchema.js +58 -17
  213. package/dist/schema/editGuard.d.ts +1 -1
  214. package/dist/schema/guards.d.ts +37 -14
  215. package/dist/schema/guards.js +29 -3
  216. package/dist/schema/index.d.ts +2 -0
  217. package/dist/schema/index.js +2 -0
  218. package/dist/schema/locks.d.ts +11 -44
  219. package/dist/schema/locks.js +0 -9
  220. package/dist/schema/preservedGuards.d.ts +26 -6
  221. package/dist/schema/preservedGuards.js +31 -1
  222. package/dist/schema/protection.d.ts +7 -1
  223. package/dist/schema/protection.js +2 -1
  224. package/dist/schema/sourceEquality.d.ts +1 -9
  225. package/dist/schema/sourceEquality.js +1 -27
  226. package/dist/styles/classNames.d.ts +13 -2
  227. package/dist/styles/classNames.js +13 -2
  228. package/dist/styles/inlineStyle.js +15 -6
  229. package/dist/styles.css +16 -18
  230. package/dist/table/cellFormatting.d.ts +8 -1
  231. package/dist/table/cellFormatting.js +11 -20
  232. package/dist/table/commands.js +20 -18
  233. package/dist/table/format.d.ts +3 -0
  234. package/dist/table/format.js +3 -9
  235. package/dist/table/gridBorders.d.ts +24 -30
  236. package/dist/table/gridBorders.js +35 -59
  237. package/dist/table/merge.d.ts +2 -6
  238. package/dist/table/merge.js +5 -5
  239. package/package.json +7 -3
  240. package/dist/docx/comments/contentTypes.d.ts +0 -7
  241. package/dist/docx/comments/contentTypes.js +0 -38
  242. package/dist/docx/comments/verifying.js +0 -206
  243. package/dist/docx/formatting/effectiveParagraph.d.ts +0 -15
  244. package/dist/docx/formatting/effectiveParagraph.js +0 -81
  245. package/dist/docx/propsXml.d.ts +0 -67
  246. package/dist/docx/uniqueControls.d.ts +0 -14
  247. package/dist/docx/uniqueControls.js +0 -62
  248. package/dist/editor/plugins/commentReservations.d.ts +0 -5
  249. package/dist/editor/plugins/commentReservations.js +0 -26
  250. package/dist/editor/plugins/styledParagraphs.d.ts +0 -15
  251. package/dist/editor/plugins/styledParagraphs.js +0 -65
  252. package/dist/page/tableMeasurements.d.ts +0 -18
  253. package/dist/page/tableMeasurements.js +0 -119
@@ -1,14 +1,31 @@
1
1
  // src/docx/exportDocx.ts
2
- import { toParagraphFormat } from "../model/format.js";
3
- import { parseNumbering } from "../numbering/parseNumbering.js";
4
- import { addListDefinitions } from "../numbering/writeNumbering.js";
5
2
  import { DocxExportError } from "../ooxml/errors.js";
6
- import { decodeUtf8, encodeUtf8, parseXml, W_NS } from "../ooxml/xml.js";
3
+ import {
4
+ ensureRootDeclarations
5
+ } from "../ooxml/partSplice.js";
6
+ import {
7
+ encodeUtf8,
8
+ parseXml,
9
+ R_NS,
10
+ W_NS,
11
+ withXmlParser
12
+ } from "../ooxml/xml.js";
7
13
  import { sameSource } from "../schema/sourceEquality.js";
8
- import { planCommentParts } from "./comments.js";
14
+ import { commentsPlanner } from "./comments.js";
9
15
  import { repackParts } from "./container.js";
16
+ import {
17
+ fidelityNotesOf
18
+ } from "./fidelity.js";
10
19
  import { hyperlinkRefs } from "./hyperlink.js";
20
+ import { withUniqueIdentities } from "./identities.js";
21
+ import { problemsOf } from "./invariants.js";
11
22
  import { NO_IMAGE_REFS, planImageMedia } from "./media.js";
23
+ import { numberingPlanner } from "./numberingPlanner.js";
24
+ import { CONTENT_TYPES_PATH, contentTypeWriter } from "./packageParts.js";
25
+ import {
26
+ assertPartsParse,
27
+ runPartPlanners
28
+ } from "./partPlan.js";
12
29
  import {
13
30
  readRelationships,
14
31
  relationshipWriter,
@@ -19,7 +36,6 @@ import {
19
36
  originalBlock,
20
37
  sessionOf
21
38
  } from "./session.js";
22
- import { withUniqueControls } from "./uniqueControls.js";
23
39
  function blockXml(node, session, refs) {
24
40
  const imported = originalBlock(node, session);
25
41
  if (imported && sameSource(node, imported.node)) return imported.xml;
@@ -27,7 +43,7 @@ function blockXml(node, session, refs) {
27
43
  }
28
44
  function buildDocumentXml(doc, session, refs) {
29
45
  const pieces = [session.documentPrefix];
30
- withUniqueControls(doc).forEach((child) => {
46
+ withUniqueIdentities(doc).forEach((child) => {
31
47
  pieces.push(blockXml(child, session, refs));
32
48
  });
33
49
  pieces.push(session.documentSuffix);
@@ -36,7 +52,7 @@ function buildDocumentXml(doc, session, refs) {
36
52
  function localAttribute(el, name) {
37
53
  return Array.from(el.attributes).find((attribute) => attribute.localName === name)?.value ?? null;
38
54
  }
39
- function assertBookmarkPairs(documentXml) {
55
+ function assertMainPart(documentXml, wroteLinks) {
40
56
  let root;
41
57
  try {
42
58
  root = parseXml(documentXml).documentElement;
@@ -51,6 +67,15 @@ function assertBookmarkPairs(documentXml) {
51
67
  const used = /* @__PURE__ */ new Set();
52
68
  for (const element of root.getElementsByTagName("*")) {
53
69
  if (element.namespaceURI !== W_NS) continue;
70
+ if (wroteLinks && element.localName === "hyperlink") {
71
+ const id2 = element.getAttributeNode("r:id");
72
+ if (id2 && id2.namespaceURI !== R_NS) {
73
+ throw new DocxExportError(
74
+ "unsupported-content",
75
+ "a hyperlink's r:id is shadowed by a different relationship namespace"
76
+ );
77
+ }
78
+ }
54
79
  if (element.localName !== "bookmarkStart" && element.localName !== "bookmarkEnd") {
55
80
  continue;
56
81
  }
@@ -85,80 +110,69 @@ function assertBookmarkPairs(documentXml) {
85
110
  );
86
111
  }
87
112
  }
88
- function collectNumIds(node, into) {
89
- const visit = (candidate) => {
90
- if (candidate.type.name !== "paragraph") return true;
91
- const numId = toParagraphFormat(candidate.attrs.format)?.numbering?.numId;
92
- if (numId !== void 0) into.add(numId);
93
- return false;
94
- };
95
- if (visit(node)) node.descendants(visit);
96
- }
97
- function numIdsIn(node) {
98
- const used = /* @__PURE__ */ new Set();
99
- collectNumIds(node, used);
100
- return used;
113
+ var LINK_MARKUP = { namespaces: { r: R_NS } };
114
+ var PART_PLANNERS = [
115
+ numberingPlanner,
116
+ commentsPlanner
117
+ ];
118
+ function exportDocx(doc, session, options) {
119
+ return exportDocxReport(doc, session, options).bytes;
101
120
  }
102
- function numIdsAtOpen(session) {
103
- const used = /* @__PURE__ */ new Set();
104
- for (const block of session.blocks) collectNumIds(block.node, used);
105
- return used;
121
+ function exportDocxReport(doc, session, options) {
122
+ return reportThrough(PART_PLANNERS, doc, session, options);
106
123
  }
107
- function newNumIds(doc, session) {
108
- const defined = parseNumbering(session.numberingXml).lists;
109
- const atOpen = numIdsAtOpen(session);
110
- return Array.from(numIdsIn(doc)).filter((numId) => !defined.has(numId) && !atOpen.has(numId)).sort((a, b) => a - b);
124
+ function exportThroughPlanners(planners, doc, session, options) {
125
+ return reportThrough(planners, doc, session, options).bytes;
111
126
  }
112
- function newNumberingPart(doc, session) {
113
- const added = newNumIds(doc, session);
114
- if (added.length === 0) return null;
115
- const path = session.numberingPartPath;
116
- const original = path === null ? void 0 : session.parts.get(path);
117
- if (path === null || original === void 0) {
118
- throw new DocxExportError(
119
- "missing-numbering-part",
120
- "cannot add a new list to a document that has no numbering.xml"
127
+ function reportThrough(planners, doc, session, options) {
128
+ return withXmlParser(options?.xmlParser, () => {
129
+ const store = sessionOf(session);
130
+ const problem = problemsOf(doc, store)[0];
131
+ if (problem) throw new DocxExportError(problem.code, problem.message);
132
+ const approximated = [];
133
+ const bytes = writeDocx(
134
+ doc,
135
+ store,
136
+ { add: (note) => approximated.push(note) },
137
+ planners
121
138
  );
122
- }
123
- const { text, hadBom } = decodeUtf8(original);
124
- return {
125
- path,
126
- bytes: encodeUtf8(addListDefinitions(text, added), hadBom)
127
- };
139
+ return {
140
+ bytes,
141
+ notes: [...fidelityNotesOf(doc, store.mainPartPath), ...approximated]
142
+ };
143
+ });
128
144
  }
129
- function exportDocx(doc, session) {
130
- const store = sessionOf(session);
145
+ function writeDocx(doc, store, notes, planners) {
131
146
  const relsPath = relsPathOf(store.mainPartPath);
132
- const relationships = relationshipWriter(
133
- readRelationships(store.parts, relsPath)
134
- );
135
- const media = planImageMedia(doc, store, relationships);
136
- const comments = planCommentParts(
137
- doc,
138
- store,
139
- relationships,
140
- media?.parts.get("[Content_Types].xml")
141
- );
142
- const documentXml = buildDocumentXml(doc, store, {
147
+ const context = {
148
+ relationships: relationshipWriter(readRelationships(store.parts, relsPath)),
149
+ contentTypes: contentTypeWriter(store.parts)
150
+ };
151
+ const media = planImageMedia(doc, store, context);
152
+ const links = hyperlinkRefs(context.relationships);
153
+ const body = buildDocumentXml(doc, store, {
143
154
  images: media?.refs ?? NO_IMAGE_REFS,
144
- links: hyperlinkRefs(relationships)
155
+ links,
156
+ notes
145
157
  });
146
- assertBookmarkPairs(documentXml);
147
- const replacements = /* @__PURE__ */ new Map([
148
- [store.mainPartPath, encodeUtf8(documentXml, store.documentHadBom)]
149
- ]);
150
- const numbering = newNumberingPart(doc, store);
151
- if (numbering) replacements.set(numbering.path, numbering.bytes);
152
- for (const [path, bytes] of media?.parts ?? []) {
153
- replacements.set(path, bytes);
154
- }
155
- for (const [path, bytes] of comments?.parts ?? []) {
156
- replacements.set(path, bytes);
157
- }
158
- const rels = relationships.part(store.parts.get(relsPath));
159
- if (rels) replacements.set(relsPath, rels);
160
- return repackParts(store.parts, replacements);
158
+ const documentXml = links.addedRelId() ? ensureRootDeclarations(body, LINK_MARKUP) : body;
159
+ assertMainPart(documentXml, links.addedRelId());
160
+ const parts = runPartPlanners(planners, doc, store, context, media?.parts);
161
+ const rels = context.relationships.part(store.parts.get(relsPath));
162
+ if (rels) parts.set(relsPath, rels);
163
+ const contentTypes = context.contentTypes.part();
164
+ if (contentTypes) parts.set(CONTENT_TYPES_PATH, contentTypes);
165
+ assertPartsParse(parts, contentTypes ?? store.parts.get(CONTENT_TYPES_PATH));
166
+ return repackParts(
167
+ store.parts,
168
+ new Map([
169
+ [store.mainPartPath, encodeUtf8(documentXml, store.documentHadBom)],
170
+ ...parts
171
+ ])
172
+ );
161
173
  }
162
174
  export {
163
- exportDocx
175
+ exportDocx,
176
+ exportDocxReport,
177
+ exportThroughPlanners
164
178
  };
@@ -1,16 +1,19 @@
1
1
  /**
2
2
  * What the serializers need from the rest of the package while a body is being written: which
3
- * relationship an inserted image ended up on (`docx/media`), and which one each link's address
4
- * lives on (`docx/hyperlink`).
3
+ * relationship an inserted image ended up on (`docx/media`), which one each link's address lives
4
+ * on (`docx/hyperlink`), and where to record an approximation a writer had to make
5
+ * (`docx/fidelity`).
5
6
  *
6
7
  * They travel as one value so that a serializer taking a body apart passes on everything the
7
8
  * pieces of it may need, whatever a later kind of content turns out to require.
8
9
  */
10
+ import { type FidelityCollector } from "./fidelity";
9
11
  import { type LinkRefs } from "./hyperlink";
10
12
  import { type ImageRefs } from "./media";
11
13
  export interface ExportRefs {
12
14
  images: ImageRefs;
13
15
  links: LinkRefs;
16
+ notes: FidelityCollector;
14
17
  }
15
18
  /** What a serializer called on its own, outside an export, has to work with */
16
19
  export declare const NO_EXPORT_REFS: ExportRefs;
@@ -1,9 +1,11 @@
1
1
  // src/docx/exportRefs.ts
2
+ import { NO_FIDELITY_COLLECTOR } from "./fidelity.js";
2
3
  import { NO_LINK_REFS } from "./hyperlink.js";
3
4
  import { NO_IMAGE_REFS } from "./media.js";
4
5
  var NO_EXPORT_REFS = {
5
6
  images: NO_IMAGE_REFS,
6
- links: NO_LINK_REFS
7
+ links: NO_LINK_REFS,
8
+ notes: NO_FIDELITY_COLLECTOR
7
9
  };
8
10
  export {
9
11
  NO_EXPORT_REFS
@@ -0,0 +1,43 @@
1
+ /**
2
+ * What a document holds that this editor could not model, as a list a host application can read.
3
+ *
4
+ * Opening a file quietly hides some markup, stands a placeholder in front of the rest, and a
5
+ * writer approximates a little more on the way back out. None of that shows on the page, so this
6
+ * is where it is said out loud: one note per preserved node, plus whatever a write approximated.
7
+ */
8
+ import type { Node as PMNode } from "prosemirror-model";
9
+ export type FidelitySeverity =
10
+ /** Carried through whole, with nothing of it on screen */
11
+ "hidden"
12
+ /** Carried through whole behind a placeholder that stands in its place and cannot be edited */
13
+ | "placeholder"
14
+ /** Written back as something near what arrived rather than as what arrived */
15
+ | "approximated";
16
+ export type FidelityCode = "preserved-run-content" | "preserved-inline" | "preserved-block" | "range-marker" | "paragraph-demoted" | "table-demoted";
17
+ export interface FidelityNote {
18
+ severity: FidelitySeverity;
19
+ code: FidelityCode;
20
+ /** The package path of the part this is about, or null when the caller holds no session */
21
+ part: string | null;
22
+ /** Which block of the body as it was opened this falls in. null for a node made while editing */
23
+ block: number | null;
24
+ /** Where the node stands in the document. null for an approximation with no node behind it */
25
+ pos: number | null;
26
+ /** The original element, for example `w:bookmarkStart` */
27
+ element: string;
28
+ }
29
+ /** Where a writer records an approximation it had to make while a body was written */
30
+ export interface FidelityCollector {
31
+ add(note: FidelityNote): void;
32
+ }
33
+ /** What a serializer called on its own, outside an export, records its approximations to */
34
+ export declare const NO_FIDELITY_COLLECTOR: FidelityCollector;
35
+ /**
36
+ * One note per preserved node, in document order.
37
+ *
38
+ * The nodes carry the original element and the block they came from themselves, so the list is a
39
+ * projection of the document rather than something an import has to remember alongside it. A
40
+ * document opened and one edited for an hour are read the same way, and a `pos` is enough to take
41
+ * a reader to the node the note is about.
42
+ */
43
+ export declare function fidelityNotesOf(doc: PMNode, part: string | null): FidelityNote[];
@@ -0,0 +1,64 @@
1
+ // src/docx/fidelity.ts
2
+ import { localPart } from "../ooxml/xml.js";
3
+ import { splitBlockKey } from "./session.js";
4
+ var NO_FIDELITY_COLLECTOR = { add: () => {
5
+ } };
6
+ function stringAttr(value) {
7
+ return typeof value === "string" ? value : null;
8
+ }
9
+ function elementNameOf(xml) {
10
+ return /^\s*<\s*([^\s/>]+)/.exec(xml)?.[1] ?? null;
11
+ }
12
+ function elementOf(node) {
13
+ return stringAttr(node.attrs.name) ?? elementNameOf(stringAttr(node.attrs.xml) ?? "");
14
+ }
15
+ function demotedBlock(element) {
16
+ const name = element === null ? null : localPart(element);
17
+ if (name === "tbl") return { severity: "placeholder", code: "table-demoted" };
18
+ if (name === "p") {
19
+ return { severity: "placeholder", code: "paragraph-demoted" };
20
+ }
21
+ return { severity: "placeholder", code: "preserved-block" };
22
+ }
23
+ function preservedBy(node, element) {
24
+ switch (node.type.name) {
25
+ case "docxRaw":
26
+ return demotedBlock(element);
27
+ case "rawBlock":
28
+ return { severity: "placeholder", code: "preserved-block" };
29
+ case "bookmarkBlock":
30
+ return { severity: "hidden", code: "range-marker" };
31
+ case "rawInline":
32
+ return preservedInline(element);
33
+ default:
34
+ return null;
35
+ }
36
+ }
37
+ function preservedInline(element) {
38
+ const name = element === null ? null : localPart(element);
39
+ if (name === "r") return null;
40
+ return name === "bookmarkStart" || name === "bookmarkEnd" ? { severity: "hidden", code: "range-marker" } : { severity: "hidden", code: "preserved-inline" };
41
+ }
42
+ function blockNumberOf(node) {
43
+ const srcId = stringAttr(node.attrs.srcId);
44
+ const key = srcId === null ? null : splitBlockKey(srcId);
45
+ return key?.index ?? null;
46
+ }
47
+ function fidelityNotesOf(doc, part) {
48
+ const notes = [];
49
+ let block = null;
50
+ doc.descendants((node, pos, parent) => {
51
+ if (parent === doc) block = blockNumberOf(node);
52
+ const element = elementOf(node);
53
+ const preserved = preservedBy(node, element);
54
+ if (preserved) {
55
+ notes.push({ ...preserved, part, block, pos, element: element ?? "?" });
56
+ }
57
+ return true;
58
+ });
59
+ return notes;
60
+ }
61
+ export {
62
+ NO_FIDELITY_COLLECTOR,
63
+ fidelityNotesOf
64
+ };
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The derived attrs a paragraph and the run marks inside it carry, worked out from the resolved
3
+ * hierarchy. Import, the plugin that formats the paragraphs an edit built, the paragraph writers
4
+ * and the clipboard reader all take them from here, so a paragraph is drawn the same whichever of
5
+ * them last touched it.
6
+ */
7
+ import { type Mark, type Node as PMNode } from "prosemirror-model";
8
+ import { type ParagraphFormat, type RunFormat } from "../../model/format";
9
+ import type { FormattingContext } from "./context";
10
+ import { type ParagraphPlacement, type ResolvedParagraph } from "./resolve";
11
+ /** The two derived paragraph attrs (`format` and `styleRun` in `schema`) */
12
+ export interface ParagraphAttrs {
13
+ format: ParagraphFormat | null;
14
+ styleRun: RunFormat | null;
15
+ }
16
+ export declare function paragraphAttrsOf(paragraph: ResolvedParagraph): ParagraphAttrs;
17
+ export declare function paragraphAttrsFor(pPr: string | null, context: FormattingContext, placement?: ParagraphPlacement | null): ParagraphAttrs;
18
+ /**
19
+ * The run mark an inline wears under the resolved paragraph, or null when it needs none.
20
+ *
21
+ * A mark already there keeps the formatting it wrote down and has its display values read
22
+ * again, since they were laid over whatever the paragraph wore before. Text carrying no mark
23
+ * gets one holding nothing but the values the paragraph passes down, so it is drawn the way
24
+ * import draws a bare `w:r`; such a mark writes no `w:rPr` and changes nothing on the way out.
25
+ * An inline that is not text and carries no mark is left as it is.
26
+ */
27
+ export declare function runMarkUnder(mark: Mark | null, isText: boolean, paragraph: ResolvedParagraph, context: FormattingContext): Mark | null;
28
+ /**
29
+ * The paragraph with the values the hierarchy gives it, and the text inside it wearing the run
30
+ * mark that goes with them.
31
+ *
32
+ * The style's run values are also laid on the paragraph itself, so that text carrying no run of
33
+ * its own - typed in the editor - is drawn in them (`styleRun` in `schema`).
34
+ * Where the paragraph stands in a table cell, `placement` is the part of the table it belongs to,
35
+ * which the table style dresses on top of everything else it lays down.
36
+ */
37
+ export declare function styledParagraph(node: PMNode, context: FormattingContext, placement?: ParagraphPlacement | null): PMNode;
38
+ /** Table display inputs, refreshed from the same style context as the paragraphs inside it. */
39
+ export declare function tableStyleAttrs(tblPr: Element | null, context: FormattingContext): {
40
+ format: import("../..").TableFormat | null;
41
+ styleInside: import("../tableFormatting").InsideBorders | null;
42
+ styleCellMargins: import("../tableFormatting").CellMargins | null;
43
+ styleConditions: Readonly<Partial<Record<"band1Horz" | "band1Vert" | "band2Horz" | "band2Vert" | "firstCol" | "firstRow" | "lastCol" | "lastRow" | "neCell" | "nwCell" | "seCell" | "swCell" | "wholeTable", import("../tableFormatting").CellStyleFormat>>> | null;
44
+ styleBands: import("../tableFormatting").BandSizes | null;
45
+ };
@@ -0,0 +1,86 @@
1
+ // src/docx/formatting/attrs.ts
2
+ import { Fragment } from "prosemirror-model";
3
+ import {
4
+ toBandSizes,
5
+ toCellMargins,
6
+ toInsideBorders,
7
+ toTableStyleConditions
8
+ } from "../../model/format.js";
9
+ import { docxSchema } from "../../schema/index.js";
10
+ import {
11
+ cellConditionsOf,
12
+ layerBandSizes,
13
+ NO_BAND_SIZES,
14
+ readBandSizes,
15
+ readTableFormat,
16
+ tblStyleIdOf
17
+ } from "../tableFormatting.js";
18
+ import {
19
+ resolveParagraph,
20
+ resolveRun,
21
+ tableStyleFor
22
+ } from "./resolve.js";
23
+ import { layerTableFormat } from "./styles.js";
24
+ function paragraphAttrsOf(paragraph) {
25
+ return { format: paragraph.format, styleRun: paragraph.styleRun };
26
+ }
27
+ function paragraphAttrsFor(pPr, context, placement = null) {
28
+ return paragraphAttrsOf(resolveParagraph(pPr, context, placement));
29
+ }
30
+ function runMarkUnder(mark, isText, paragraph, context) {
31
+ if (mark) {
32
+ const rPr = mark.attrs.rPr;
33
+ const format2 = resolveRun(
34
+ typeof rPr === "string" ? rPr : null,
35
+ paragraph,
36
+ context
37
+ );
38
+ return mark.type.create({ ...mark.attrs, format: format2 });
39
+ }
40
+ if (!isText) return null;
41
+ const format = resolveRun(null, paragraph, context);
42
+ return format === null ? null : docxSchema.marks.run.create({ rPr: null, rAttrs: null, format });
43
+ }
44
+ function styledParagraph(node, context, placement = null) {
45
+ const pPr = node.attrs.pPr;
46
+ const paragraph = resolveParagraph(
47
+ typeof pPr === "string" ? pPr : null,
48
+ context,
49
+ placement
50
+ );
51
+ const inline = node.children.map((child) => {
52
+ const mark = runMarkUnder(
53
+ child.marks.find((entry) => entry.type === docxSchema.marks.run) ?? null,
54
+ child.isText,
55
+ paragraph,
56
+ context
57
+ );
58
+ return mark ? child.mark(mark.addToSet(child.marks)) : child;
59
+ });
60
+ return node.type.create(
61
+ { ...node.attrs, ...paragraphAttrsOf(paragraph) },
62
+ Fragment.fromArray(inline),
63
+ node.marks
64
+ );
65
+ }
66
+ function tableStyleAttrs(tblPr, context) {
67
+ const style = tableStyleFor(tblStyleIdOf(tblPr), context);
68
+ return {
69
+ format: layerTableFormat(style?.table ?? {}, readTableFormat(tblPr)),
70
+ styleInside: toInsideBorders(style?.tableInside),
71
+ styleCellMargins: toCellMargins(style?.tableCellMargins),
72
+ styleConditions: toTableStyleConditions(
73
+ cellConditionsOf(style?.tableConditions ?? {})
74
+ ),
75
+ styleBands: toBandSizes(
76
+ layerBandSizes(style?.tableBands ?? NO_BAND_SIZES, readBandSizes(tblPr))
77
+ )
78
+ };
79
+ }
80
+ export {
81
+ paragraphAttrsFor,
82
+ paragraphAttrsOf,
83
+ runMarkUnder,
84
+ styledParagraph,
85
+ tableStyleAttrs
86
+ };
@@ -0,0 +1,39 @@
1
+ import type { RunFormat } from "../../model/format";
2
+ import { type Numbering, type NumberingOptions } from "../../numbering/parseNumbering";
3
+ import { type CompatSettings } from "../documentSettings";
4
+ import { type ThemeFonts } from "../theme";
5
+ import { type StyleTable } from "./styles";
6
+ import type { ParagraphFormatLayer } from "./tabStops";
7
+ /**
8
+ * Everything a document lays down that the §17.7.2 hierarchy is resolved against.
9
+ *
10
+ * Built once when the document is opened and read by every caller that derives a paragraph's or
11
+ * a run's display values, so that a value is the same whichever path asked for it.
12
+ */
13
+ export interface FormattingContext {
14
+ styles: StyleTable;
15
+ /** The paragraph style a paragraph naming none wears (`w:default="1"`). Null when the document marks none */
16
+ defaultParagraphStyleId: string | null;
17
+ defaultTableStyleId: string | null;
18
+ /** `pPrDefault`, the layer at the base of the hierarchy */
19
+ paragraphDefaults: ParagraphFormatLayer;
20
+ /**
21
+ * `rPrDefault` read in full. It decides what a removed run property falls back to. The default
22
+ * font and size are supplied through CSS variables; other properties enter derived run values.
23
+ */
24
+ runDefaults: RunFormat;
25
+ numbering: Numbering;
26
+ themeFonts: ThemeFonts;
27
+ compat: CompatSettings;
28
+ }
29
+ export declare const NO_FORMATTING: FormattingContext;
30
+ /**
31
+ * How a document's numbering part is read against the rest of what the document lays down.
32
+ *
33
+ * The numbering folder cannot reach styles.xml, so the links a definition follows are handed to it
34
+ * from here, and the two callers that read a numbering part - opening a document and asking an
35
+ * open one for its lists - resolve the same links.
36
+ */
37
+ export declare function numberingOptionsFor(styles: StyleTable, themeFonts: ThemeFonts): NumberingOptions;
38
+ /** The context an opened document resolves against. A document without a styles part lays down nothing */
39
+ export declare function formattingContextOf(styles: Document | null, numberingXml: string | null, themeFonts: ThemeFonts, compat?: CompatSettings): FormattingContext;
@@ -0,0 +1,60 @@
1
+ // src/docx/formatting/context.ts
2
+ import {
3
+ EMPTY_NUMBERING,
4
+ parseNumbering
5
+ } from "../../numbering/parseNumbering.js";
6
+ import { NO_COMPAT } from "../documentSettings.js";
7
+ import { NO_THEME_FONTS } from "../theme.js";
8
+ import {
9
+ readDefaultParagraphFormat,
10
+ readRunDefaults,
11
+ readRunFormat
12
+ } from "./direct.js";
13
+ import {
14
+ defaultParagraphStyleIdOf,
15
+ defaultTableStyleIdOf,
16
+ NO_STYLES,
17
+ numberingStyleLinks,
18
+ readStyles
19
+ } from "./styles.js";
20
+ var NO_FORMATTING = {
21
+ styles: NO_STYLES,
22
+ defaultParagraphStyleId: null,
23
+ defaultTableStyleId: null,
24
+ paragraphDefaults: {},
25
+ runDefaults: {},
26
+ numbering: EMPTY_NUMBERING,
27
+ themeFonts: NO_THEME_FONTS,
28
+ compat: NO_COMPAT
29
+ };
30
+ function numberingOptionsFor(styles, themeFonts) {
31
+ return {
32
+ links: numberingStyleLinks(styles),
33
+ readRun: (rPr) => readRunFormat(rPr, themeFonts)
34
+ };
35
+ }
36
+ function formattingContextOf(styles, numberingXml, themeFonts, compat = NO_COMPAT) {
37
+ const table = styles === null ? NO_STYLES : readStyles(styles, themeFonts);
38
+ const numbering = parseNumbering(
39
+ numberingXml,
40
+ numberingOptionsFor(table, themeFonts)
41
+ );
42
+ if (styles === null) {
43
+ return { ...NO_FORMATTING, numbering, themeFonts, compat };
44
+ }
45
+ return {
46
+ styles: table,
47
+ defaultParagraphStyleId: defaultParagraphStyleIdOf(styles),
48
+ defaultTableStyleId: defaultTableStyleIdOf(styles),
49
+ paragraphDefaults: readDefaultParagraphFormat(styles),
50
+ runDefaults: readRunDefaults(styles, themeFonts),
51
+ numbering,
52
+ themeFonts,
53
+ compat
54
+ };
55
+ }
56
+ export {
57
+ NO_FORMATTING,
58
+ formattingContextOf,
59
+ numberingOptionsFor
60
+ };
@@ -8,11 +8,8 @@
8
8
  import { type DocumentDefaults, type LineSpacing, type RunFormat } from "../../model/format";
9
9
  import { type ThemeFonts } from "../theme";
10
10
  import type { ParagraphFormatLayer } from "./tabStops";
11
- /**
12
- * The font names held in a display value.
13
- * Splits the CSS name list `fontFamilyOf` produced back into individual names.
14
- */
15
- export declare function fontNamesOf(cssNames: string | undefined | null): string[];
11
+ /** `w:spacing/@line` counts an automatic line height in 240ths of a line (§17.3.1.33) */
12
+ export declare const LINE_UNITS_PER_LINE = 240;
16
13
  /**
17
14
  * The line spacing from `w:spacing`.
18
15
  * auto means a multiple in 240ths, and the rest are heights pinned down in twips.
@@ -23,6 +20,10 @@ export declare function readRunFormat(rPr: Element | null, themeFonts?: ThemeFon
23
20
  export declare const NO_DOCUMENT_DEFAULTS: DocumentDefaults;
24
21
  /** Reads the paragraph-property layer at the base of the OOXML hierarchy. */
25
22
  export declare function readDefaultParagraphFormat(styles: Document): ParagraphFormatLayer;
23
+ /** Reads the run-property layer at the base of the OOXML hierarchy (`rPrDefault`) in full */
24
+ export declare function readRunDefaults(styles: Document, themeFonts?: ThemeFonts): RunFormat;
25
+ /** The character style a run's formatting points at (`w:rStyle`). null when it points at none */
26
+ export declare function runStyleIdOf(rPr: Element | null): string | null;
26
27
  /**
27
28
  * Reads the document default font size, font, and line spacing from docDefaults in styles.xml.
28
29
  * What is absent is left as null, and the on-screen fallback is decided by the display layer.