@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,8 +1,4 @@
1
1
  // src/docx/importTable.ts
2
- import {
3
- toCellMargins,
4
- toInsideBorders
5
- } from "../model/format.js";
6
2
  import { childValue, wAttr } from "../ooxml/units.js";
7
3
  import {
8
4
  attrString,
@@ -13,7 +9,10 @@ import {
13
9
  import { docxSchema } from "../schema/index.js";
14
10
  import {
15
11
  layerTableFormat,
16
- NO_STYLES
12
+ NO_FORMATTING,
13
+ styledParagraph,
14
+ tableStyleAttrs,
15
+ tableStyleFor
17
16
  } from "./formatting.js";
18
17
  import {
19
18
  buildParagraph,
@@ -21,18 +20,22 @@ import {
21
20
  } from "./importParagraph.js";
22
21
  import { readSdtWrapper } from "./sdt.js";
23
22
  import {
24
- cellBorderDefaults,
25
- gridEdgesOf,
23
+ cellConditionsOf,
24
+ cellDefaultsFor,
25
+ layerBandSizes,
26
26
  layerCellMargins,
27
27
  layerInsideBorders,
28
+ NO_BAND_SIZES,
28
29
  NO_CELL_MARGINS,
29
30
  NO_INSIDE_BORDERS,
31
+ readBandSizes,
30
32
  readCellFormat,
31
33
  readCellMarginsOf,
32
34
  readGridCols,
33
35
  readInsideBorders,
34
36
  readRowFormat,
35
37
  readTableFormat,
38
+ readTableLook,
36
39
  readTableWidth,
37
40
  tblStyleIdOf
38
41
  } from "./tableFormatting.js";
@@ -86,9 +89,23 @@ function readRow(el) {
86
89
  }
87
90
  return cells.length > 0 ? { el, tblPrEx, cells } : null;
88
91
  }
92
+ function gridRevisionXml(grid) {
93
+ const revision = childByLocalName(grid, "tblGridChange");
94
+ if (revision === null) return null;
95
+ const declarations = Array.from(grid.attributes).filter(
96
+ (attr) => attr.namespaceURI === "http://www.w3.org/2000/xmlns/" && !revision.hasAttribute(attr.name)
97
+ );
98
+ if (declarations.length === 0) return serializeXml(revision);
99
+ const preserved = revision.ownerDocument.importNode(revision, true);
100
+ for (const attr of declarations) {
101
+ preserved.setAttributeNS(attr.namespaceURI, attr.name, attr.value);
102
+ }
103
+ return serializeXml(preserved);
104
+ }
89
105
  function readTableParts(el) {
90
106
  let tblPr = null;
91
107
  let tblGrid = null;
108
+ let gridChange = null;
92
109
  const rows = [];
93
110
  for (const child of elementChildren(el)) {
94
111
  if (child.localName === "tblPr") {
@@ -97,6 +114,7 @@ function readTableParts(el) {
97
114
  }
98
115
  if (child.localName === "tblGrid") {
99
116
  tblGrid = child;
117
+ gridChange = gridRevisionXml(child);
100
118
  continue;
101
119
  }
102
120
  if (child.localName !== "tr") return null;
@@ -104,7 +122,7 @@ function readTableParts(el) {
104
122
  if (!row) return null;
105
123
  rows.push(row);
106
124
  }
107
- return rows.length > 0 ? { tblPr, tblGrid, rows } : null;
125
+ return rows.length > 0 ? { tblPr, tblGrid, gridChange, rows } : null;
108
126
  }
109
127
  function resolveVerticalMerges(rows, width) {
110
128
  const drafts = [];
@@ -147,37 +165,31 @@ function gridWidthOf(rows) {
147
165
  const first = widths[0];
148
166
  return widths.every((width) => width === first) ? first : null;
149
167
  }
150
- function buildCellBlock(el, sources) {
168
+ function buildCellBlock(el, sources, context, placement) {
151
169
  if (el.localName === "p") {
152
170
  const paragraph = buildParagraph(el, null, sources);
153
- if (paragraph) return paragraph;
171
+ if (paragraph) return styledParagraph(paragraph, context, placement);
154
172
  }
155
173
  return docxSchema.nodes.rawBlock.create({
156
174
  xml: serializeXml(el),
157
175
  name: el.nodeName
158
176
  });
159
177
  }
160
- function cellBorderDefaultsFor(draft, lines) {
161
- return cellBorderDefaults(
162
- gridEdgesOf(
163
- {
164
- top: draft.row,
165
- bottom: draft.row + draft.rowspan,
166
- left: draft.col,
167
- right: draft.col + draft.colspan
168
- },
169
- lines
170
- ),
171
- lines.outer,
172
- lines.inside
173
- );
178
+ function cellRect(draft) {
179
+ return {
180
+ top: draft.row,
181
+ bottom: draft.row + draft.rowspan,
182
+ left: draft.col,
183
+ right: draft.col + draft.colspan
184
+ };
174
185
  }
175
- function buildCell(draft, lines, sources) {
186
+ function buildCell(draft, table, sources, context) {
176
187
  const tcPr = childByLocalName(draft.el, "tcPr");
188
+ const defaults = cellDefaultsFor(cellRect(draft), table.grid, table.sources);
177
189
  const blocks = [];
178
190
  for (const child of elementChildren(draft.el)) {
179
191
  if (child.localName === "tcPr") continue;
180
- blocks.push(buildCellBlock(child, sources));
192
+ blocks.push(buildCellBlock(child, sources, context, defaults.placement));
181
193
  }
182
194
  if (blocks.length === 0) return null;
183
195
  return docxSchema.nodes.tableCell.create(
@@ -188,11 +200,7 @@ function buildCell(draft, lines, sources) {
188
200
  tcAttrs: attrString(draft.el),
189
201
  tcPr: tcPr ? serializeXml(tcPr) : null,
190
202
  tcW: readTableWidth(tcPr, "tcW"),
191
- format: readCellFormat(
192
- tcPr,
193
- cellBorderDefaultsFor(draft, lines),
194
- lines.margins
195
- ),
203
+ format: readCellFormat(tcPr, defaults),
196
204
  sdtPrefix: draft.control?.prefix ?? null,
197
205
  sdtContentsLocked: draft.control?.contentsLocked ?? false,
198
206
  sdtDeletionLocked: draft.control?.deletionLocked ?? false
@@ -200,10 +208,10 @@ function buildCell(draft, lines, sources) {
200
208
  blocks
201
209
  );
202
210
  }
203
- function buildRow(row, drafts, lines, sources) {
211
+ function buildRow(row, drafts, table, sources, context) {
204
212
  const cells = [];
205
213
  for (const draft of drafts) {
206
- const cell = buildCell(draft, lines, sources);
214
+ const cell = buildCell(draft, table, sources, context);
207
215
  if (!cell) return null;
208
216
  cells.push(cell);
209
217
  }
@@ -218,11 +226,7 @@ function buildRow(row, drafts, lines, sources) {
218
226
  cells
219
227
  );
220
228
  }
221
- function tableStyleOf(tblPr, styles, defaultTableStyleId) {
222
- const styleId = tblStyleIdOf(tblPr);
223
- return (styleId !== null ? styles.get(styleId) : void 0) ?? (defaultTableStyleId !== null ? styles.get(defaultTableStyleId) : void 0);
224
- }
225
- function buildTable(el, srcId, sources = NO_IMPORT_SOURCES, styles = NO_STYLES, defaultTableStyleId = null) {
229
+ function buildTable(el, srcId, sources = NO_IMPORT_SOURCES, context = NO_FORMATTING) {
226
230
  const parts = readTableParts(el);
227
231
  if (!parts) return null;
228
232
  const gridCols = readGridCols(parts.tblGrid);
@@ -231,27 +235,38 @@ function buildTable(el, srcId, sources = NO_IMPORT_SOURCES, styles = NO_STYLES,
231
235
  if (gridCols.length > 0 && gridCols.length !== width) return null;
232
236
  const drafts = resolveVerticalMerges(parts.rows, width);
233
237
  if (!drafts) return null;
234
- const style = tableStyleOf(parts.tblPr, styles, defaultTableStyleId);
238
+ const styleId = tblStyleIdOf(parts.tblPr);
239
+ const style = tableStyleFor(styleId, context);
235
240
  const tableFormat = layerTableFormat(
236
241
  style?.table ?? {},
237
242
  readTableFormat(parts.tblPr)
238
243
  );
239
- const lines = {
240
- rows: parts.rows.length,
241
- cols: width,
242
- outer: tableFormat,
243
- inside: layerInsideBorders(
244
- style?.tableInside ?? NO_INSIDE_BORDERS,
245
- readInsideBorders(parts.tblPr)
246
- ),
247
- margins: layerCellMargins(
248
- style?.tableCellMargins ?? NO_CELL_MARGINS,
249
- readCellMarginsOf(parts.tblPr, "tblCellMar")
250
- )
244
+ const bands = layerBandSizes(
245
+ style?.tableBands ?? NO_BAND_SIZES,
246
+ readBandSizes(parts.tblPr)
247
+ );
248
+ const conditions = cellConditionsOf(style?.tableConditions ?? {});
249
+ const table = {
250
+ grid: { rows: parts.rows.length, cols: width },
251
+ sources: {
252
+ outer: tableFormat,
253
+ inside: layerInsideBorders(
254
+ style?.tableInside ?? NO_INSIDE_BORDERS,
255
+ readInsideBorders(parts.tblPr)
256
+ ),
257
+ margins: layerCellMargins(
258
+ style?.tableCellMargins ?? NO_CELL_MARGINS,
259
+ readCellMarginsOf(parts.tblPr, "tblCellMar")
260
+ ),
261
+ look: readTableLook(parts.tblPr),
262
+ bands,
263
+ conditions,
264
+ styleId
265
+ }
251
266
  };
252
267
  const rows = [];
253
268
  for (const [index, row] of parts.rows.entries()) {
254
- const built = buildRow(row, drafts[index], lines, sources);
269
+ const built = buildRow(row, drafts[index], table, sources, context);
255
270
  if (!built) return null;
256
271
  rows.push(built);
257
272
  }
@@ -262,10 +277,8 @@ function buildTable(el, srcId, sources = NO_IMPORT_SOURCES, styles = NO_STYLES,
262
277
  tblPr: parts.tblPr ? serializeXml(parts.tblPr) : null,
263
278
  tblW: readTableWidth(parts.tblPr, "tblW"),
264
279
  gridCols,
265
- format: tableFormat,
266
- // The cells need these again whenever an edit derives their display values afresh
267
- styleInside: toInsideBorders(style?.tableInside),
268
- styleCellMargins: toCellMargins(style?.tableCellMargins)
280
+ gridChange: parts.gridChange,
281
+ ...tableStyleAttrs(parts.tblPr, context)
269
282
  },
270
283
  rows
271
284
  );
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Known export refusals, asked of the document before anything is written.
3
+ *
4
+ * Each invariant reads what the writer would refuse over and reports it with the code the writer
5
+ * would throw, so a screen can ask at edit time what `exportDocx` would say. The list is walked in
6
+ * order and `exportDocx` throws its first entry, which is what keeps the two from disagreeing.
7
+ *
8
+ * The checks inspect model attrs and preserved XML without running the writers. Bookmark
9
+ * fragments and list definitions use the export parser. `assertBookmarkPairs` in `./exportDocx`
10
+ * and `assertPartsParse` in `./partPlan` remain the final checks over the parts as actually written.
11
+ */
12
+ import type { Node as PMNode } from "prosemirror-model";
13
+ import type { DocxExportErrorCode } from "../ooxml/errors";
14
+ import type { ExportOptions } from "./exportDocx";
15
+ import { type DocxSession, type SessionStore } from "./session";
16
+ /** One reason the document cannot be written back, with the code `exportDocx` would throw it under */
17
+ export interface ExportProblem {
18
+ readonly code: DocxExportErrorCode;
19
+ readonly message: string;
20
+ /** Where the problem stands in the document. Absent for a problem of the package or of the session */
21
+ readonly pos?: number;
22
+ }
23
+ /** The problems as the writer sees them, for a caller already inside the writer's parser scope */
24
+ export declare function problemsOf(doc: PMNode, session: SessionStore): readonly ExportProblem[];
25
+ /**
26
+ * Known reasons writing this document back would be refused, in the order `exportDocx` would
27
+ * raise them. An empty list does not rule out failures while writing. Each entry carries the code and message the
28
+ * `DocxExportError` would carry, and the position in the document where there is one.
29
+ *
30
+ * The list definitions are read to tell a new list from one the document already had, so this
31
+ * needs an XML parser the way `exportDocx` does and takes the same option.
32
+ */
33
+ export declare function exportProblems(doc: PMNode, session: DocxSession, options?: ExportOptions): readonly ExportProblem[];
@@ -0,0 +1,269 @@
1
+ // src/docx/invariants.ts
2
+ import { spanCount, toParagraphFormat } 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 { canDefineNewList, newNumIds, startedLists } 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 listDefinitions = {
164
+ name: "listDefinitions",
165
+ check(doc, session) {
166
+ const undefinedIds = new Set(startedLists(doc, session).unregistered);
167
+ if (undefinedIds.size === 0) return [];
168
+ const problems = [];
169
+ doc.descendants((node, pos) => {
170
+ if (node.type.name !== "paragraph") return true;
171
+ const numId = toParagraphFormat(node.attrs.format)?.numbering?.numId;
172
+ if (numId === void 0 || !undefinedIds.delete(numId)) return false;
173
+ problems.push({
174
+ code: "unsupported-content",
175
+ message: `the list numbered ${numId} has no definition to be written`,
176
+ pos
177
+ });
178
+ return false;
179
+ });
180
+ return problems;
181
+ }
182
+ };
183
+ function addsCommentsPart(doc, session) {
184
+ const bodyChanged = commentsChanged(doc, session);
185
+ const threadChanged = extensionsChanged(doc, session);
186
+ if (!bodyChanged && !threadChanged) return false;
187
+ if (commentsPart.pathIn(session) === null || session.comments.xml === null)
188
+ return true;
189
+ const references = commentReferencesIn(doc);
190
+ if (threadChanged && (references.size > 0 || session.comments.extendedPartPath !== null) && (commentsExtendedPart.pathIn(session) === null || session.comments.extendedXml === null))
191
+ return true;
192
+ return bodyChanged && (peoplePart.pathIn(session) === null || session.comments.people.xml === null) && unrecordedAuthors(
193
+ currentCommentBodies(references).values(),
194
+ session.comments.people
195
+ ).size > 0;
196
+ }
197
+ var mediaContentTypes = {
198
+ name: "mediaContentTypes",
199
+ check(doc, session) {
200
+ if (session.parts.has(CONTENT_TYPES_PATH)) return [];
201
+ const problems = [];
202
+ if (insertedImageSrcs(doc).length > 0) {
203
+ problems.push({
204
+ code: "missing-content-types",
205
+ message: `cannot add an image to a package that has no ${CONTENT_TYPES_PATH}`
206
+ });
207
+ }
208
+ if (!canDefineNewList(session) && newNumIds(doc, session).length > 0) {
209
+ problems.push({
210
+ code: "missing-content-types",
211
+ message: `cannot add a part to a package that has no ${CONTENT_TYPES_PATH}`
212
+ });
213
+ }
214
+ if (addsCommentsPart(doc, session)) {
215
+ problems.push({
216
+ code: "missing-content-types",
217
+ message: `cannot add a part to a package that has no ${CONTENT_TYPES_PATH}`
218
+ });
219
+ }
220
+ return problems;
221
+ }
222
+ };
223
+ var commentPartRoots = {
224
+ name: "commentPartRoots",
225
+ check(doc, session) {
226
+ const problems = [];
227
+ const { xml, extendedXml, extendedPartPath } = session.comments;
228
+ if (xml !== null && commentsChanged(doc, session)) {
229
+ const problem = commentsRootProblem(xml);
230
+ if (problem !== null)
231
+ problems.push({ code: "malformed-xml", message: problem });
232
+ }
233
+ if (extendedXml !== null && extensionsChanged(doc, session) && (commentReferencesIn(doc).size > 0 || extendedPartPath !== null)) {
234
+ const problem = extensionsRootProblem(extendedXml);
235
+ if (problem !== null)
236
+ problems.push({ code: "malformed-xml", message: problem });
237
+ }
238
+ return problems;
239
+ }
240
+ };
241
+ var EXPORT_INVARIANTS = [
242
+ bookmarkPairs,
243
+ tableGrids,
244
+ preservedOriginals,
245
+ uniqueIdentities,
246
+ listDefinitions,
247
+ mediaContentTypes,
248
+ commentPartRoots
249
+ ];
250
+ var answered = /* @__PURE__ */ new WeakMap();
251
+ function problemsOf(doc, session) {
252
+ const known = answered.get(doc);
253
+ if (known && known.session === session) return known.problems;
254
+ const problems = EXPORT_INVARIANTS.flatMap(
255
+ (invariant) => invariant.check(doc, session)
256
+ );
257
+ answered.set(doc, { session, problems });
258
+ return problems;
259
+ }
260
+ function exportProblems(doc, session, options) {
261
+ return withXmlParser(
262
+ options?.xmlParser,
263
+ () => problemsOf(doc, sessionOf(session))
264
+ );
265
+ }
266
+ export {
267
+ exportProblems,
268
+ problemsOf
269
+ };
@@ -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
  };