@portone/docx-editor 0.2.0 → 0.3.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 (256) hide show
  1. package/CHANGELOG.md +174 -0
  2. package/CONTRIBUTING.md +9 -1
  3. package/dist/DocxEditor.d.ts +6 -0
  4. package/dist/DocxEditor.js +12 -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 +14 -7
  10. package/dist/docx/commentOnlyChange.js +9 -139
  11. package/dist/docx/comments/constants.d.ts +3 -4
  12. package/dist/docx/comments/constants.js +4 -5
  13. package/dist/docx/comments/grammar.d.ts +86 -0
  14. package/dist/docx/comments/grammar.js +223 -0
  15. package/dist/docx/comments/parts.d.ts +52 -0
  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 +28 -106
  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.d.ts +5 -3
  22. package/dist/docx/comments/reading.js +30 -29
  23. package/dist/docx/comments/writing.d.ts +19 -7
  24. package/dist/docx/comments/writing.js +105 -137
  25. package/dist/docx/documentSettings.d.ts +7 -0
  26. package/dist/docx/documentSettings.js +10 -2
  27. package/dist/docx/exportDocx.d.ts +40 -5
  28. package/dist/docx/exportDocx.js +71 -72
  29. package/dist/docx/exportRefs.d.ts +5 -2
  30. package/dist/docx/exportRefs.js +3 -1
  31. package/dist/docx/fidelity.d.ts +43 -0
  32. package/dist/docx/fidelity.js +64 -0
  33. package/dist/docx/formatting/attrs.d.ts +27 -0
  34. package/dist/docx/formatting/attrs.js +31 -0
  35. package/dist/docx/formatting/context.d.ts +31 -0
  36. package/dist/docx/formatting/context.js +42 -0
  37. package/dist/docx/formatting/direct.d.ts +6 -5
  38. package/dist/docx/formatting/direct.js +52 -81
  39. package/dist/docx/formatting/resolve.d.ts +45 -0
  40. package/dist/docx/formatting/resolve.js +140 -0
  41. package/dist/docx/formatting/runProperties.d.ts +93 -0
  42. package/dist/docx/formatting/runProperties.js +316 -0
  43. package/dist/docx/formatting/styles.js +3 -3
  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 +13 -22
  48. package/dist/docx/identities.d.ts +67 -0
  49. package/dist/docx/identities.js +174 -0
  50. package/dist/docx/importDocx.d.ts +12 -1
  51. package/dist/docx/importDocx.js +72 -79
  52. package/dist/docx/importParagraph.d.ts +1 -1
  53. package/dist/docx/importParagraph.js +4 -3
  54. package/dist/docx/importTable.d.ts +1 -1
  55. package/dist/docx/importTable.js +17 -1
  56. package/dist/docx/invariants.d.ts +33 -0
  57. package/dist/docx/invariants.js +256 -0
  58. package/dist/docx/media.d.ts +6 -4
  59. package/dist/docx/media.js +6 -37
  60. package/dist/docx/newLists.d.ts +20 -0
  61. package/dist/docx/newLists.js +36 -0
  62. package/dist/docx/notes.js +6 -12
  63. package/dist/docx/numberingPlanner.d.ts +8 -0
  64. package/dist/docx/numberingPlanner.js +19 -0
  65. package/dist/docx/packageParts.d.ts +42 -0
  66. package/dist/docx/packageParts.js +135 -0
  67. package/dist/docx/pageGeometry.d.ts +2 -0
  68. package/dist/docx/pageGeometry.js +18 -9
  69. package/dist/docx/paraProps.d.ts +10 -22
  70. package/dist/docx/paraProps.js +75 -99
  71. package/dist/docx/partPlan.d.ts +36 -0
  72. package/dist/docx/partPlan.js +59 -0
  73. package/dist/docx/protectionPolicy.d.ts +127 -0
  74. package/dist/docx/protectionPolicy.js +169 -0
  75. package/dist/docx/relationships.d.ts +1 -1
  76. package/dist/docx/relationships.js +15 -15
  77. package/dist/docx/runProps.d.ts +9 -22
  78. package/dist/docx/runProps.js +15 -168
  79. package/dist/docx/scan.js +20 -52
  80. package/dist/docx/sdt.js +14 -37
  81. package/dist/docx/sdtProps.d.ts +8 -1
  82. package/dist/docx/sdtProps.js +10 -0
  83. package/dist/docx/serializeBlock.d.ts +2 -0
  84. package/dist/docx/serializeBlock.js +9 -7
  85. package/dist/docx/serializeParagraph.d.ts +2 -1
  86. package/dist/docx/serializeParagraph.js +25 -14
  87. package/dist/docx/serializeTable.js +63 -32
  88. package/dist/docx/session.d.ts +42 -14
  89. package/dist/docx/session.js +43 -17
  90. package/dist/docx/storyProjection.d.ts +36 -0
  91. package/dist/docx/storyProjection.js +23 -0
  92. package/dist/docx/tableFormatting/editing.js +130 -185
  93. package/dist/docx/tableFormatting/reading.d.ts +16 -6
  94. package/dist/docx/tableFormatting/reading.js +40 -20
  95. package/dist/docx/tableTemplate.js +37 -5
  96. package/dist/docx/theme.d.ts +0 -6
  97. package/dist/docx/theme.js +0 -8
  98. package/dist/download.d.ts +8 -5
  99. package/dist/download.js +2 -0
  100. package/dist/editor/clipboard/images.js +17 -3
  101. package/dist/editor/clipboard/inlineFormatting.d.ts +7 -0
  102. package/dist/editor/clipboard/inlineFormatting.js +20 -30
  103. package/dist/editor/commands/breakCommands.js +4 -3
  104. package/dist/editor/commands/canRunCommand.d.ts +2 -2
  105. package/dist/editor/commands/canRunCommand.js +1 -1
  106. package/dist/editor/commands/comments/editing.d.ts +6 -2
  107. package/dist/editor/commands/comments/editing.js +21 -36
  108. package/dist/editor/commands/exportQueries.d.ts +15 -0
  109. package/dist/editor/commands/exportQueries.js +14 -0
  110. package/dist/editor/commands/fidelityQueries.d.ts +12 -0
  111. package/dist/editor/commands/fidelityQueries.js +8 -0
  112. package/dist/editor/commands/formatting/editing.d.ts +2 -2
  113. package/dist/editor/commands/formatting/editing.js +26 -108
  114. package/dist/editor/commands/formatting/propertyCommands.d.ts +21 -0
  115. package/dist/editor/commands/formatting/propertyCommands.js +103 -0
  116. package/dist/editor/commands/formatting/shared.d.ts +3 -3
  117. package/dist/editor/commands/formatting/shared.js +5 -2
  118. package/dist/editor/commands/historyCommands.js +2 -1
  119. package/dist/editor/commands/indentCommands.js +5 -4
  120. package/dist/editor/commands/index.d.ts +11 -0
  121. package/dist/editor/commands/index.js +5 -0
  122. package/dist/editor/commands/linkCommands.js +5 -6
  123. package/dist/editor/commands/listCommands.js +8 -10
  124. package/dist/editor/commands/lockCommands.d.ts +7 -1
  125. package/dist/editor/commands/lockCommands.js +35 -37
  126. package/dist/editor/commands/paragraphCommands.js +28 -31
  127. package/dist/editor/commands/spacingCommands.js +1 -1
  128. package/dist/editor/commands/tabCommands.js +4 -3
  129. package/dist/editor/createEditor.d.ts +17 -29
  130. package/dist/editor/createEditor.js +61 -67
  131. package/dist/editor/documentStyles.d.ts +11 -35
  132. package/dist/editor/documentStyles.js +9 -52
  133. package/dist/editor/editorDocument.d.ts +56 -0
  134. package/dist/editor/editorDocument.js +74 -0
  135. package/dist/editor/externalClipboard.js +166 -33
  136. package/dist/editor/insertImage.js +4 -3
  137. package/dist/editor/insertTable.js +4 -3
  138. package/dist/editor/paragraphEdits.d.ts +11 -18
  139. package/dist/editor/paragraphEdits.js +7 -16
  140. package/dist/editor/plugins/displayDerivation.d.ts +47 -0
  141. package/dist/editor/plugins/displayDerivation.js +78 -0
  142. package/dist/editor/plugins/documentProtection.d.ts +3 -3
  143. package/dist/editor/plugins/imagePaste.js +4 -3
  144. package/dist/editor/plugins/keymap.js +18 -3
  145. package/dist/editor/plugins/lockedContent.d.ts +4 -4
  146. package/dist/editor/plugins/lockedContent.js +1 -1
  147. package/dist/editor/plugins/numberingDecorations.d.ts +5 -11
  148. package/dist/editor/plugins/numberingDecorations.js +7 -17
  149. package/dist/editor/plugins/paragraphDisplay.d.ts +7 -0
  150. package/dist/editor/plugins/paragraphDisplay.js +51 -0
  151. package/dist/editor/plugins/tabLayout.js +1 -1
  152. package/dist/editor/plugins/tableDisplay.d.ts +6 -0
  153. package/dist/editor/plugins/tableDisplay.js +16 -0
  154. package/dist/index.d.ts +2 -0
  155. package/dist/model/format.d.ts +30 -8
  156. package/dist/model/format.js +35 -22
  157. package/dist/model/tabStops.d.ts +9 -0
  158. package/dist/model/tabStops.js +18 -0
  159. package/dist/numbering/listTemplate.js +49 -10
  160. package/dist/numbering/parseNumbering.d.ts +14 -1
  161. package/dist/numbering/parseNumbering.js +35 -22
  162. package/dist/numbering/writeNumbering.d.ts +3 -4
  163. package/dist/numbering/writeNumbering.js +14 -25
  164. package/dist/ooxml/childOrder.d.ts +34 -0
  165. package/dist/ooxml/childOrder.js +496 -0
  166. package/dist/ooxml/element.d.ts +65 -0
  167. package/dist/ooxml/element.js +68 -0
  168. package/dist/ooxml/errors.d.ts +3 -2
  169. package/dist/ooxml/fragment.d.ts +53 -0
  170. package/dist/ooxml/fragment.js +76 -0
  171. package/dist/ooxml/image.d.ts +4 -0
  172. package/dist/ooxml/image.js +3 -5
  173. package/dist/ooxml/names.d.ts +27 -0
  174. package/dist/ooxml/names.js +29 -0
  175. package/dist/ooxml/partSplice.d.ts +67 -0
  176. package/dist/ooxml/partSplice.js +169 -0
  177. package/dist/ooxml/precedence.d.ts +31 -0
  178. package/dist/ooxml/precedence.js +51 -0
  179. package/dist/ooxml/props.d.ts +112 -0
  180. package/dist/ooxml/props.js +214 -0
  181. package/dist/ooxml/simpleTypes.d.ts +103 -0
  182. package/dist/ooxml/simpleTypes.js +182 -0
  183. package/dist/ooxml/tabStops.js +8 -27
  184. package/dist/ooxml/tagScan.d.ts +34 -0
  185. package/dist/ooxml/tagScan.js +108 -0
  186. package/dist/ooxml/units.d.ts +25 -13
  187. package/dist/ooxml/units.js +65 -31
  188. package/dist/ooxml/xml.d.ts +52 -0
  189. package/dist/ooxml/xml.js +64 -5
  190. package/dist/page/blockKinds.d.ts +103 -0
  191. package/dist/page/blockKinds.js +11 -0
  192. package/dist/page/kinds/index.d.ts +6 -0
  193. package/dist/page/kinds/index.js +10 -0
  194. package/dist/page/kinds/paragraphKind.d.ts +10 -0
  195. package/dist/page/kinds/paragraphKind.js +71 -0
  196. package/dist/page/kinds/tableKind.d.ts +10 -0
  197. package/dist/page/kinds/tableKind.js +178 -0
  198. package/dist/page/measureBlocks.d.ts +3 -1
  199. package/dist/page/measureBlocks.js +24 -25
  200. package/dist/page/pageDecorations.d.ts +27 -28
  201. package/dist/page/pageDecorations.js +70 -151
  202. package/dist/page/pageLayout.d.ts +10 -62
  203. package/dist/page/pageLayout.js +60 -53
  204. package/dist/page/usePageLayout.d.ts +2 -2
  205. package/dist/page/usePageLayout.js +3 -11
  206. package/dist/schema/attrRoles.d.ts +42 -0
  207. package/dist/schema/attrRoles.js +141 -0
  208. package/dist/schema/displayDerivation.d.ts +82 -0
  209. package/dist/schema/displayDerivation.js +130 -0
  210. package/dist/schema/docxSchema.d.ts +9 -0
  211. package/dist/schema/docxSchema.js +237 -114
  212. package/dist/schema/editGuard.d.ts +99 -0
  213. package/dist/schema/editGuard.js +43 -0
  214. package/dist/schema/guards.d.ts +91 -0
  215. package/dist/schema/guards.js +108 -0
  216. package/dist/schema/index.d.ts +2 -0
  217. package/dist/schema/index.js +2 -0
  218. package/dist/schema/locks.d.ts +17 -77
  219. package/dist/schema/locks.js +26 -70
  220. package/dist/schema/preservedGuards.d.ts +50 -0
  221. package/dist/schema/preservedGuards.js +80 -0
  222. package/dist/schema/protection.d.ts +17 -4
  223. package/dist/schema/protection.js +4 -2
  224. package/dist/schema/sourceEquality.d.ts +19 -0
  225. package/dist/schema/sourceEquality.js +39 -0
  226. package/dist/styles/inlineStyle.js +15 -6
  227. package/dist/table/cellFormatting.d.ts +8 -1
  228. package/dist/table/cellFormatting.js +10 -17
  229. package/dist/table/commands.d.ts +2 -2
  230. package/dist/table/commands.js +20 -18
  231. package/dist/table/format.d.ts +3 -0
  232. package/dist/table/format.js +3 -9
  233. package/dist/table/gridBorders.d.ts +14 -16
  234. package/dist/table/gridBorders.js +3 -41
  235. package/dist/table/index.d.ts +2 -2
  236. package/dist/table/merge.d.ts +4 -8
  237. package/dist/table/merge.js +5 -5
  238. package/package.json +10 -3
  239. package/dist/docx/comments/contentTypes.d.ts +0 -7
  240. package/dist/docx/comments/contentTypes.js +0 -38
  241. package/dist/docx/formatting/effectiveParagraph.d.ts +0 -15
  242. package/dist/docx/formatting/effectiveParagraph.js +0 -81
  243. package/dist/docx/propsXml.d.ts +0 -49
  244. package/dist/docx/propsXml.js +0 -302
  245. package/dist/docx/uniqueControls.d.ts +0 -14
  246. package/dist/docx/uniqueControls.js +0 -62
  247. package/dist/editor/plugins/bookmarkProtection.d.ts +0 -3
  248. package/dist/editor/plugins/bookmarkProtection.js +0 -28
  249. package/dist/editor/plugins/commentReservations.d.ts +0 -5
  250. package/dist/editor/plugins/commentReservations.js +0 -26
  251. package/dist/editor/plugins/noteProtection.d.ts +0 -3
  252. package/dist/editor/plugins/noteProtection.js +0 -25
  253. package/dist/editor/plugins/styledParagraphs.d.ts +0 -15
  254. package/dist/editor/plugins/styledParagraphs.js +0 -65
  255. package/dist/page/tableMeasurements.d.ts +0 -10
  256. package/dist/page/tableMeasurements.js +0 -115
@@ -0,0 +1,256 @@
1
+ // src/docx/invariants.ts
2
+ import { spanCount } from "../model/format.js";
3
+ import {
4
+ attributeByLocalName,
5
+ parseXml,
6
+ W_NS,
7
+ withXmlParser
8
+ } from "../ooxml/xml.js";
9
+ import {
10
+ commentReferencesIn,
11
+ commentsChanged,
12
+ commentsRootProblem,
13
+ extensionsChanged,
14
+ extensionsRootProblem
15
+ } from "./comments.js";
16
+ import {
17
+ commentsExtendedPart,
18
+ commentsPart,
19
+ peoplePart
20
+ } from "./comments/parts.js";
21
+ import { unrecordedAuthors } from "./comments/people.js";
22
+ import { currentCommentBodies } from "./comments/writing.js";
23
+ import { identityProblems } from "./identities.js";
24
+ import { insertedImageSrcs } from "./media.js";
25
+ import { newNumIds, numberingPartOf } from "./newLists.js";
26
+ import { CONTENT_TYPES_PATH } from "./packageParts.js";
27
+ import { lostOriginal } from "./serializeBlock.js";
28
+ import {
29
+ originalBlock,
30
+ sessionOf
31
+ } from "./session.js";
32
+ function markerSourceOf(node, session) {
33
+ if (node.type.name === "rawInline" || node.type.name === "rawBlock") {
34
+ return typeof node.attrs.xml === "string" ? node.attrs.xml : null;
35
+ }
36
+ if (node.type.isInGroup("preserved")) {
37
+ return originalBlock(node, session)?.xml ?? null;
38
+ }
39
+ return null;
40
+ }
41
+ var bookmarkPairs = {
42
+ name: "bookmarkPairs",
43
+ check(doc, session) {
44
+ const problems = [];
45
+ const open = /* @__PURE__ */ new Map();
46
+ const used = /* @__PURE__ */ new Set();
47
+ doc.descendants((node, pos) => {
48
+ const source = markerSourceOf(node, session);
49
+ if (source === null) return true;
50
+ if (!source.includes("bookmark")) return true;
51
+ let root;
52
+ try {
53
+ root = parseXml(
54
+ session.documentPrefix + source + session.documentSuffix
55
+ ).documentElement;
56
+ } catch {
57
+ problems.push({
58
+ code: "malformed-xml",
59
+ message: "preserved bookmark XML could not be parsed",
60
+ pos
61
+ });
62
+ return true;
63
+ }
64
+ for (const marker of Array.from(root.getElementsByTagName("*"))) {
65
+ if (marker.namespaceURI !== W_NS || marker.localName !== "bookmarkStart" && marker.localName !== "bookmarkEnd")
66
+ continue;
67
+ const kind = marker.localName.slice("bookmark".length);
68
+ const value = attributeByLocalName(marker, "id");
69
+ if (value === null || value === "") {
70
+ problems.push({
71
+ code: "malformed-xml",
72
+ message: `a bookmark${kind} has no id`,
73
+ pos
74
+ });
75
+ } else if (kind === "Start") {
76
+ if (used.has(value)) {
77
+ problems.push({
78
+ code: "malformed-xml",
79
+ message: `bookmark ${value} has more than one start marker`,
80
+ pos
81
+ });
82
+ } else {
83
+ open.set(value, pos);
84
+ used.add(value);
85
+ }
86
+ } else if (!open.delete(value)) {
87
+ problems.push({
88
+ code: "malformed-xml",
89
+ message: `bookmark ${value} ends without an earlier start marker`,
90
+ pos
91
+ });
92
+ }
93
+ }
94
+ return true;
95
+ });
96
+ for (const [id, pos] of open) {
97
+ problems.push({
98
+ code: "malformed-xml",
99
+ message: `bookmark ${id} has no end marker`,
100
+ pos
101
+ });
102
+ }
103
+ return problems;
104
+ }
105
+ };
106
+ function mergeReachesPastLastRow(table) {
107
+ let overlong = false;
108
+ table.forEach((row, _offset, at) => {
109
+ row.forEach((cell) => {
110
+ if (at + spanCount(cell.attrs.rowspan) > table.childCount) {
111
+ overlong = true;
112
+ }
113
+ });
114
+ });
115
+ return overlong;
116
+ }
117
+ var tableGrids = {
118
+ name: "tableGrids",
119
+ check(doc) {
120
+ const problems = [];
121
+ doc.descendants((node, pos) => {
122
+ if (node.type.name !== "table") return true;
123
+ if (mergeReachesPastLastRow(node)) {
124
+ problems.push({
125
+ code: "invalid-table",
126
+ message: "a vertical merge in the table reaches past the last row",
127
+ pos
128
+ });
129
+ }
130
+ return true;
131
+ });
132
+ return problems;
133
+ }
134
+ };
135
+ function lostOriginalOf(node, session) {
136
+ if (node.type.name === "rawInline" || node.type.name === "rawBlock") {
137
+ return typeof node.attrs.xml === "string" ? null : "a preserved element has lost its original XML";
138
+ }
139
+ if (node.type.isInGroup("preserved")) {
140
+ return originalBlock(node, session) ? null : lostOriginal(node, session);
141
+ }
142
+ return null;
143
+ }
144
+ var preservedOriginals = {
145
+ name: "preservedOriginals",
146
+ check(doc, session) {
147
+ const problems = [];
148
+ doc.descendants((node, pos) => {
149
+ const message = lostOriginalOf(node, session);
150
+ if (message !== null)
151
+ problems.push({ code: "lost-original", message, pos });
152
+ return true;
153
+ });
154
+ return problems;
155
+ }
156
+ };
157
+ var uniqueIdentities = {
158
+ name: "uniqueIdentities",
159
+ check(doc) {
160
+ return identityProblems(doc);
161
+ }
162
+ };
163
+ var numberingPart = {
164
+ name: "numberingPart",
165
+ check(doc, session) {
166
+ if (numberingPartOf(session) !== null) return [];
167
+ if (newNumIds(doc, session).length === 0) return [];
168
+ return [
169
+ {
170
+ code: "missing-numbering-part",
171
+ message: "cannot add a new list to a document that has no numbering.xml"
172
+ }
173
+ ];
174
+ }
175
+ };
176
+ function addsCommentsPart(doc, session) {
177
+ const bodyChanged = commentsChanged(doc, session);
178
+ const threadChanged = extensionsChanged(doc, session);
179
+ if (!bodyChanged && !threadChanged) return false;
180
+ if (commentsPart.pathIn(session) === null || session.comments.xml === null)
181
+ return true;
182
+ const references = commentReferencesIn(doc);
183
+ if (threadChanged && (references.size > 0 || session.comments.extendedPartPath !== null) && (commentsExtendedPart.pathIn(session) === null || session.comments.extendedXml === null))
184
+ return true;
185
+ return bodyChanged && (peoplePart.pathIn(session) === null || session.comments.people.xml === null) && unrecordedAuthors(
186
+ currentCommentBodies(references).values(),
187
+ session.comments.people
188
+ ).size > 0;
189
+ }
190
+ var mediaContentTypes = {
191
+ name: "mediaContentTypes",
192
+ check(doc, session) {
193
+ if (session.parts.has(CONTENT_TYPES_PATH)) return [];
194
+ const problems = [];
195
+ if (insertedImageSrcs(doc).length > 0) {
196
+ problems.push({
197
+ code: "missing-content-types",
198
+ message: `cannot add an image to a package that has no ${CONTENT_TYPES_PATH}`
199
+ });
200
+ }
201
+ if (addsCommentsPart(doc, session)) {
202
+ problems.push({
203
+ code: "missing-content-types",
204
+ message: `cannot add a part to a package that has no ${CONTENT_TYPES_PATH}`
205
+ });
206
+ }
207
+ return problems;
208
+ }
209
+ };
210
+ var commentPartRoots = {
211
+ name: "commentPartRoots",
212
+ check(doc, session) {
213
+ const problems = [];
214
+ const { xml, extendedXml, extendedPartPath } = session.comments;
215
+ if (xml !== null && commentsChanged(doc, session)) {
216
+ const problem = commentsRootProblem(xml);
217
+ if (problem !== null)
218
+ problems.push({ code: "malformed-xml", message: problem });
219
+ }
220
+ if (extendedXml !== null && extensionsChanged(doc, session) && (commentReferencesIn(doc).size > 0 || extendedPartPath !== null)) {
221
+ const problem = extensionsRootProblem(extendedXml);
222
+ if (problem !== null)
223
+ problems.push({ code: "malformed-xml", message: problem });
224
+ }
225
+ return problems;
226
+ }
227
+ };
228
+ var EXPORT_INVARIANTS = [
229
+ bookmarkPairs,
230
+ tableGrids,
231
+ preservedOriginals,
232
+ uniqueIdentities,
233
+ numberingPart,
234
+ mediaContentTypes,
235
+ commentPartRoots
236
+ ];
237
+ var answered = /* @__PURE__ */ new WeakMap();
238
+ function problemsOf(doc, session) {
239
+ const known = answered.get(doc);
240
+ if (known && known.session === session) return known.problems;
241
+ const problems = EXPORT_INVARIANTS.flatMap(
242
+ (invariant) => invariant.check(doc, session)
243
+ );
244
+ answered.set(doc, { session, problems });
245
+ return problems;
246
+ }
247
+ function exportProblems(doc, session, options) {
248
+ return withXmlParser(
249
+ options?.xmlParser,
250
+ () => problemsOf(doc, sessionOf(session))
251
+ );
252
+ }
253
+ export {
254
+ exportProblems,
255
+ problemsOf
256
+ };
@@ -3,7 +3,7 @@
3
3
  * bytes without blob-URL lifetime management; `MAX_IMAGE_BYTES` bounds the memory tradeoff.
4
4
  */
5
5
  import type { Node as PMNode } from "prosemirror-model";
6
- import { type RelationshipWriter } from "./relationships";
6
+ import type { PartPlanContext } from "./partPlan";
7
7
  import type { SessionStore } from "./session";
8
8
  /** The images the body can refer to, as data URLs keyed by relationship id */
9
9
  export type ImageSources = ReadonlyMap<string, string>;
@@ -46,10 +46,12 @@ export interface MediaAdditions {
46
46
  /** The parts to write into the zip alongside the body */
47
47
  parts: ReadonlyMap<string, Uint8Array>;
48
48
  }
49
+ /** The srcs of the images that were not in the document when it was opened, in document order */
50
+ export declare function insertedImageSrcs(doc: PMNode): string[];
49
51
  /**
50
52
  * What the package needs for the images that were inserted during editing: the media parts,
51
- * the relationships pointing at them, and the content types declaring them.
52
- * null when every image in the document came out of the document itself.
53
+ * with the relationships pointing at them and the content types declaring them asked of the
54
+ * context. null when every image in the document came out of the document itself.
53
55
  *
54
56
  * The relationships go through the writer the export shares between its writers
55
57
  * (`docx/relationships`), so the part is written once with everything in it.
@@ -57,4 +59,4 @@ export interface MediaAdditions {
57
59
  * Bytes that are already in the package are not written a second time: an inserted image
58
60
  * whose fingerprint matches one that is already there reuses its relationship.
59
61
  */
60
- export declare function planImageMedia(doc: PMNode, session: SessionStore, relationships: RelationshipWriter): MediaAdditions | null;
62
+ export declare function planImageMedia(doc: PMNode, session: SessionStore, context: PartPlanContext): MediaAdditions | null;
@@ -5,7 +5,7 @@ import {
5
5
  imageMimeOf,
6
6
  toImageSrc
7
7
  } from "../ooxml/image.js";
8
- import { decodeUtf8, encodeUtf8, R_NS } from "../ooxml/xml.js";
8
+ import { R_NS } from "../ooxml/xml.js";
9
9
  import {
10
10
  directoryOf,
11
11
  readRelationships,
@@ -13,7 +13,6 @@ import {
13
13
  resolveTarget
14
14
  } from "./relationships.js";
15
15
  var IMAGE_REL_TYPE = `${R_NS}/image`;
16
- var CONTENT_TYPES_PATH = "[Content_Types].xml";
17
16
  var EXTENSION_BY_MIME = {
18
17
  "image/png": "png",
19
18
  "image/jpeg": "jpeg",
@@ -114,6 +113,7 @@ function decodeImage(src) {
114
113
  const bytes = fromBase64(imageBase64Of(src));
115
114
  return {
116
115
  bytes,
116
+ mime,
117
117
  extension: EXTENSION_BY_MIME[mime],
118
118
  key: contentKey(bytes)
119
119
  };
@@ -148,42 +148,14 @@ function maxDocPrId(session) {
148
148
  }
149
149
  return max;
150
150
  }
151
- var TYPES_OPEN_TAG = /<(?:[\w.-]+:)?Types\b[^>]*>/;
152
- function withImageContentTypes(parts, extensions) {
153
- const original = parts.get(CONTENT_TYPES_PATH);
154
- if (!original) {
155
- throw new DocxExportError(
156
- "missing-content-types",
157
- `cannot add an image to a package that has no ${CONTENT_TYPES_PATH}`
158
- );
159
- }
160
- const { text, hadBom } = decodeUtf8(original);
161
- const missing = Array.from(extensions).filter(
162
- (extension) => !new RegExp(`<Default[^>]+Extension="${extension}"`, "i").test(text)
163
- );
164
- if (missing.length === 0) return null;
165
- const open = TYPES_OPEN_TAG.exec(text);
166
- if (!open) {
167
- throw new DocxExportError(
168
- "malformed-xml",
169
- `${CONTENT_TYPES_PATH} has no Types element`
170
- );
171
- }
172
- const declarations = missing.map((extension) => {
173
- const mime = MIME_BY_EXTENSION.get(extension);
174
- return `<Default Extension="${extension}" ContentType="${mime}"/>`;
175
- }).join("");
176
- const at = open.index + open[0].length;
177
- return encodeUtf8(text.slice(0, at) + declarations + text.slice(at), hadBom);
178
- }
179
- function planImageMedia(doc, session, relationships) {
151
+ function planImageMedia(doc, session, context) {
180
152
  const srcs = insertedImageSrcs(doc);
181
153
  if (srcs.length === 0) return null;
154
+ const { relationships, contentTypes } = context;
182
155
  const relIdByContent = imageRelIdsByContent(session, relationships.opened);
183
156
  const directory = directoryOf(session.mainPartPath);
184
157
  const relIdBySrc = /* @__PURE__ */ new Map();
185
158
  const parts = /* @__PURE__ */ new Map();
186
- const extensions = /* @__PURE__ */ new Set();
187
159
  for (const src of srcs) {
188
160
  const image = decodeImage(src);
189
161
  const reused = relIdByContent.get(image.key);
@@ -194,20 +166,17 @@ function planImageMedia(doc, session, relationships) {
194
166
  const target = `media/image-${image.key}.${image.extension}`;
195
167
  const relId = relationships.add({ type: IMAGE_REL_TYPE, target });
196
168
  parts.set(directory + target, image.bytes);
197
- extensions.add(image.extension);
169
+ contentTypes.addDefault(image.extension, image.mime);
198
170
  relIdByContent.set(image.key, relId);
199
171
  relIdBySrc.set(src, relId);
200
172
  }
201
- if (extensions.size > 0) {
202
- const contentTypes = withImageContentTypes(session.parts, extensions);
203
- if (contentTypes) parts.set(CONTENT_TYPES_PATH, contentTypes);
204
- }
205
173
  return { refs: imageRefs(relIdBySrc, maxDocPrId(session) + 1), parts };
206
174
  }
207
175
  export {
208
176
  MAX_IMAGE_BYTES,
209
177
  NO_IMAGES,
210
178
  NO_IMAGE_REFS,
179
+ insertedImageSrcs,
211
180
  planImageMedia,
212
181
  readImageSources
213
182
  };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The lists that were started during editing, and the part their definitions are written into.
3
+ *
4
+ * Both the writer and the export invariants ask these questions, so they stand apart from the
5
+ * writer rather than being read off it.
6
+ */
7
+ import type { Node as PMNode } from "prosemirror-model";
8
+ import type { SessionStore } from "./session";
9
+ /**
10
+ * The numbering ids that appeared during editing and have no definition.
11
+ *
12
+ * An id that was already in use without a definition when the document was opened is left alone,
13
+ * because exporting such a document without editing it must not disturb numbering.xml.
14
+ */
15
+ export declare function newNumIds(doc: PMNode, session: SessionStore): number[];
16
+ /** The numbering part as it was opened, which a new definition is spliced into. null when the document has none */
17
+ export declare function numberingPartOf(session: SessionStore): {
18
+ path: string;
19
+ bytes: Uint8Array;
20
+ } | null;
@@ -0,0 +1,36 @@
1
+ // src/docx/newLists.ts
2
+ import { toParagraphFormat } from "../model/format.js";
3
+ import { parseNumbering } from "../numbering/parseNumbering.js";
4
+ function collectNumIds(node, into) {
5
+ const visit = (candidate) => {
6
+ if (candidate.type.name !== "paragraph") return true;
7
+ const numId = toParagraphFormat(candidate.attrs.format)?.numbering?.numId;
8
+ if (numId !== void 0) into.add(numId);
9
+ return false;
10
+ };
11
+ if (visit(node)) node.descendants(visit);
12
+ }
13
+ function numIdsIn(node) {
14
+ const used = /* @__PURE__ */ new Set();
15
+ collectNumIds(node, used);
16
+ return used;
17
+ }
18
+ function numIdsAtOpen(session) {
19
+ const used = /* @__PURE__ */ new Set();
20
+ for (const block of session.blocks) collectNumIds(block.node, used);
21
+ return used;
22
+ }
23
+ function newNumIds(doc, session) {
24
+ const defined = parseNumbering(session.numberingXml).lists;
25
+ const atOpen = numIdsAtOpen(session);
26
+ return Array.from(numIdsIn(doc)).filter((numId) => !defined.has(numId) && !atOpen.has(numId)).sort((a, b) => a - b);
27
+ }
28
+ function numberingPartOf(session) {
29
+ const path = session.numberingPartPath;
30
+ const bytes = path === null ? void 0 : session.parts.get(path);
31
+ return path === null || bytes === void 0 ? null : { path, bytes };
32
+ }
33
+ export {
34
+ newNumIds,
35
+ numberingPartOf
36
+ };
@@ -1,12 +1,13 @@
1
1
  // src/docx/notes.ts
2
2
  import {
3
+ attributeByLocalName,
3
4
  decodeUtf8,
4
5
  elementChildren,
5
6
  parseXml,
6
7
  R_NS,
7
8
  W_NS
8
9
  } from "../ooxml/xml.js";
9
- import { readRelationships, relsPathOf, resolveTarget } from "./relationships.js";
10
+ import { relatedPartPath } from "./packageParts.js";
10
11
  var EMPTY_PART = {
11
12
  partPath: null,
12
13
  ordered: [],
@@ -16,9 +17,6 @@ var NO_NOTES = {
16
17
  footnotes: EMPTY_PART,
17
18
  endnotes: EMPTY_PART
18
19
  };
19
- function attribute(el, localName) {
20
- return Array.from(el.attributes).find((entry) => entry.localName === localName)?.value ?? null;
21
- }
22
20
  function inlineText(node) {
23
21
  if (node.localName === "t") return node.textContent ?? "";
24
22
  if (node.localName === "tab") return " ";
@@ -31,20 +29,16 @@ function noteText(el) {
31
29
  return Array.from(el.getElementsByTagNameNS(W_NS, "p")).map(inlineText).join("\n");
32
30
  }
33
31
  function notePart(parts, mainPartPath, kind) {
34
- const relationshipType = `${R_NS}/${kind}s`;
35
- const relationship = readRelationships(parts, relsPathOf(mainPartPath)).find(
36
- (entry) => entry.type === relationshipType && !entry.external
37
- );
38
- if (!relationship) return EMPTY_PART;
39
- const partPath = resolveTarget(mainPartPath, relationship.target);
32
+ const partPath = relatedPartPath(parts, mainPartPath, `${R_NS}/${kind}s`);
33
+ if (partPath === null) return EMPTY_PART;
40
34
  const bytes = parts.get(partPath);
41
35
  if (!bytes) return { ...EMPTY_PART, partPath };
42
36
  const root = parseXml(decodeUtf8(bytes).text).documentElement;
43
37
  const elements = elementChildren(root).filter((el) => el.localName === kind);
44
38
  const ordered = elements.flatMap((el) => {
45
- const id = attribute(el, "id");
39
+ const id = attributeByLocalName(el, "id");
46
40
  if (id === null) return [];
47
- const type = attribute(el, "type");
41
+ const type = attributeByLocalName(el, "type");
48
42
  const regular = type === null || type === "normal";
49
43
  const label = regular ? "?" : "";
50
44
  return [{ kind, id, label, text: noteText(el), type }];
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The numbering part with the definition of every list started during editing spliced in.
3
+ *
4
+ * The original text is left as is and only the new definitions go in. Nothing is written when no
5
+ * list was started.
6
+ */
7
+ import type { PartPlanner } from "./partPlan";
8
+ export declare const numberingPlanner: PartPlanner;
@@ -0,0 +1,19 @@
1
+ // src/docx/numberingPlanner.ts
2
+ import { addListDefinitions } from "../numbering/writeNumbering.js";
3
+ import { decodeUtf8, encodeUtf8 } from "../ooxml/xml.js";
4
+ import { newNumIds, numberingPartOf } from "./newLists.js";
5
+ var numberingPlanner = {
6
+ name: "numbering",
7
+ plan(doc, session) {
8
+ const added = newNumIds(doc, session);
9
+ const original = numberingPartOf(session);
10
+ if (added.length === 0 || original === null) return null;
11
+ const { text, hadBom } = decodeUtf8(original.bytes);
12
+ return /* @__PURE__ */ new Map([
13
+ [original.path, encodeUtf8(addListDefinitions(text, added), hadBom)]
14
+ ]);
15
+ }
16
+ };
17
+ export {
18
+ numberingPlanner
19
+ };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Where a part sits in the package, asked one way by every reader and writer, and the content
3
+ * types part every part the export adds is declared in.
4
+ *
5
+ * A part beside the body is found through the relationship the main part declares for it, and a
6
+ * part the export adds takes the first name beside the main part that nothing has claimed. Each
7
+ * reader used to find its part with a lookup of its own, so the rule for what counts as the
8
+ * related part lived in five places.
9
+ */
10
+ export declare const CONTENT_TYPES_PATH = "[Content_Types].xml";
11
+ /**
12
+ * Where the part the main part relates under this type sits. null for no such relationship, and
13
+ * for one that points outside the package, which no part of it can stand behind.
14
+ */
15
+ export declare function relatedPartPath(parts: ReadonlyMap<string, Uint8Array>, mainPartPath: string, type: string): string | null;
16
+ /** A part as text, and null for a path there is no part at, so a missing relationship reads as a missing part */
17
+ export declare function readPart(parts: ReadonlyMap<string, Uint8Array>, path: string | null): string | null;
18
+ /** `comments.xml`, then `comments2.xml`, and so on beside the main part: the first name no part of the package has taken */
19
+ export declare function availablePartPath(parts: ReadonlyMap<string, Uint8Array>, mainPartPath: string, stem: string): string;
20
+ /**
21
+ * The content types part as an export leaves it: what the package declared when it was opened,
22
+ * and every declaration the planners asked for on top, written once at the end.
23
+ *
24
+ * Each writer that added a part used to splice its own declaration in and hand the result to the
25
+ * next, so what the part came to hold depended on the order they were called in, and a writer
26
+ * called without the last one's result wrote over its declaration.
27
+ */
28
+ export interface ContentTypeWriter {
29
+ /** Declares the type of the one part at this path */
30
+ addOverride(partPath: string, contentType: string): void;
31
+ /** Declares the type of every part with this extension */
32
+ addDefault(extension: string, contentType: string): void;
33
+ /**
34
+ * The part with everything added, or null when nothing was added or everything asked for was
35
+ * declared already. The additions go right after the opening tag, the defaults ahead of the
36
+ * overrides, whichever order they were asked in.
37
+ */
38
+ part(): Uint8Array | null;
39
+ }
40
+ /** XML parts named by OPC content types, even when their URI has no .xml suffix. */
41
+ export declare function declaredXmlParts(types: Uint8Array, paths: Iterable<string>): ReadonlySet<string>;
42
+ export declare function contentTypeWriter(parts: ReadonlyMap<string, Uint8Array>): ContentTypeWriter;