@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,4 +1,5 @@
1
1
  // src/model/format.ts
2
+ import { TAB_LEADERS, TAB_STOP_ALIGNMENTS } from "./tabStops.js";
2
3
  var ALIGNS = ["left", "center", "right", "justify"];
3
4
  var VERTICAL_ALIGNS = ["superscript", "subscript"];
4
5
  var CELL_VERTICAL_ALIGNS = ["top", "center", "bottom"];
@@ -56,6 +57,38 @@ function withWidthNumber(width, value) {
56
57
  if (width.type === "pct") return { type: "pct", fiftieths: value };
57
58
  return width;
58
59
  }
60
+ var RUN_FORMAT_KEYS = [
61
+ "bold",
62
+ "italic",
63
+ "strike",
64
+ "doubleStrike",
65
+ "smallCaps",
66
+ "caps",
67
+ "underline",
68
+ "fontSizePt",
69
+ "characterSpacingPt",
70
+ "fontFamily",
71
+ "color",
72
+ "highlight",
73
+ "background",
74
+ "verticalAlign",
75
+ "lang"
76
+ ];
77
+ var TABLE_STYLE_CONDITIONS = [
78
+ "wholeTable",
79
+ "band1Vert",
80
+ "band2Vert",
81
+ "band1Horz",
82
+ "band2Horz",
83
+ "firstRow",
84
+ "lastRow",
85
+ "firstCol",
86
+ "lastCol",
87
+ "nwCell",
88
+ "neCell",
89
+ "swCell",
90
+ "seCell"
91
+ ];
59
92
  function isRecord(value) {
60
93
  return typeof value === "object" && value !== null && !Array.isArray(value);
61
94
  }
@@ -89,22 +122,6 @@ function toNumberingRef(value) {
89
122
  if (typeof numId !== "number" || typeof ilvl !== "number") return null;
90
123
  return { numId, ilvl };
91
124
  }
92
- var TAB_ALIGNMENTS = [
93
- "start",
94
- "center",
95
- "end",
96
- "decimal",
97
- "num",
98
- "bar"
99
- ];
100
- var TAB_LEADERS = [
101
- "none",
102
- "dot",
103
- "hyphen",
104
- "underscore",
105
- "heavy",
106
- "middleDot"
107
- ];
108
125
  function toTabStops(value) {
109
126
  if (!Array.isArray(value)) return null;
110
127
  const stops = [];
@@ -112,7 +129,9 @@ function toTabStops(value) {
112
129
  if (!isRecord(entry) || typeof entry.positionPt !== "number" || !Number.isFinite(entry.positionPt)) {
113
130
  continue;
114
131
  }
115
- const align = TAB_ALIGNMENTS.find((candidate) => candidate === entry.align);
132
+ const align = TAB_STOP_ALIGNMENTS.find(
133
+ (candidate) => candidate === entry.align
134
+ );
116
135
  if (!align) continue;
117
136
  const leader = TAB_LEADERS.find((candidate) => candidate === entry.leader);
118
137
  stops.push({
@@ -150,6 +169,7 @@ function toParagraphFormat(value) {
150
169
  const tabStops = toTabStops(value.tabStops);
151
170
  if (tabStops) format.tabStops = tabStops;
152
171
  if (value.pageBreakBefore === true) format.pageBreakBefore = true;
172
+ if (typeof value.keepNext === "boolean") format.keepNext = value.keepNext;
153
173
  copyBorders(value, format);
154
174
  const background = matching(FILL, value.background);
155
175
  if (background) format.background = background;
@@ -158,13 +178,20 @@ function toParagraphFormat(value) {
158
178
  function toRunFormat(value) {
159
179
  if (!isRecord(value)) return null;
160
180
  const format = {};
161
- if (value.bold === true) format.bold = true;
162
- if (value.italic === true) format.italic = true;
163
- if (value.strike === true) format.strike = true;
164
- if (value.smallCaps === true) format.smallCaps = true;
165
- if (isUnderlineKind(value.underline)) format.underline = value.underline;
181
+ if (typeof value.bold === "boolean") format.bold = value.bold;
182
+ if (typeof value.italic === "boolean") format.italic = value.italic;
183
+ if (typeof value.strike === "boolean") format.strike = value.strike;
184
+ if (typeof value.doubleStrike === "boolean")
185
+ format.doubleStrike = value.doubleStrike;
186
+ if (typeof value.smallCaps === "boolean") format.smallCaps = value.smallCaps;
187
+ if (typeof value.caps === "boolean") format.caps = value.caps;
188
+ if (isUnderlineKind(value.underline) || value.underline === "none") {
189
+ format.underline = value.underline;
190
+ }
166
191
  if (typeof value.fontSizePt === "number")
167
192
  format.fontSizePt = value.fontSizePt;
193
+ if (typeof value.characterSpacingPt === "number")
194
+ format.characterSpacingPt = value.characterSpacingPt;
168
195
  const fontFamily = matching(FONT_FAMILY, value.fontFamily);
169
196
  if (fontFamily) format.fontFamily = fontFamily;
170
197
  const color = matching(HEX_COLOR, value.color);
@@ -249,6 +276,53 @@ function toCellMargins(value) {
249
276
  ];
250
277
  return sides.every((side) => side === null) ? null : margins;
251
278
  }
279
+ function toBandSizes(value) {
280
+ if (!isRecord(value)) return null;
281
+ const size = (side) => typeof side === "number" && Number.isInteger(side) && side > 0 ? side : null;
282
+ const bands = { row: size(value.row), col: size(value.col) };
283
+ return bands.row === null && bands.col === null ? null : bands;
284
+ }
285
+ function toCellStyleBorders(value) {
286
+ if (!isRecord(value)) {
287
+ return { top: null, bottom: null, left: null, right: null };
288
+ }
289
+ return {
290
+ top: matching(BORDER_CSS, value.top) ?? null,
291
+ bottom: matching(BORDER_CSS, value.bottom) ?? null,
292
+ left: matching(BORDER_CSS, value.left) ?? null,
293
+ right: matching(BORDER_CSS, value.right) ?? null
294
+ };
295
+ }
296
+ function toCellStyleFormat(value) {
297
+ if (!isRecord(value)) return null;
298
+ return {
299
+ background: matching(FILL, value.background) ?? null,
300
+ borders: toCellStyleBorders(value.borders),
301
+ inside: toInsideBorders(value.inside) ?? {
302
+ horizontal: null,
303
+ vertical: null
304
+ },
305
+ margins: toCellMargins(value.margins) ?? {
306
+ topPt: null,
307
+ rightPt: null,
308
+ bottomPt: null,
309
+ leftPt: null
310
+ },
311
+ verticalAlign: isCellVerticalAlign(value.verticalAlign) ? value.verticalAlign : null
312
+ };
313
+ }
314
+ function toTableStyleConditions(value) {
315
+ if (!isRecord(value)) return null;
316
+ const conditions = {};
317
+ let found = false;
318
+ for (const type of TABLE_STYLE_CONDITIONS) {
319
+ const format = toCellStyleFormat(value[type]);
320
+ if (format === null) continue;
321
+ conditions[type] = format;
322
+ found = true;
323
+ }
324
+ return found ? conditions : null;
325
+ }
252
326
  function toRowHeight(value) {
253
327
  if (!isRecord(value) || typeof value.pt !== "number") return null;
254
328
  if (value.rule === "exact") return { rule: "exact", pt: value.pt };
@@ -287,9 +361,12 @@ function spanCount(value) {
287
361
  }
288
362
  export {
289
363
  NO_FILL,
364
+ RUN_FORMAT_KEYS,
365
+ TABLE_STYLE_CONDITIONS,
290
366
  isHighlightName,
291
367
  isUnderlineKind,
292
368
  spanCount,
369
+ toBandSizes,
293
370
  toCellFormat,
294
371
  toCellMargins,
295
372
  toColWidth,
@@ -299,6 +376,7 @@ export {
299
376
  toRowFormat,
300
377
  toRunFormat,
301
378
  toTableFormat,
379
+ toTableStyleConditions,
302
380
  toTableWidth,
303
381
  widthNumber,
304
382
  withWidthNumber
@@ -6,6 +6,15 @@ export interface TabStop {
6
6
  align: TabAlignment | "bar";
7
7
  leader?: TabLeader;
8
8
  }
9
+ /**
10
+ * What a stop may be aligned to, and what it may draw the space it covers with.
11
+ *
12
+ * These are the members of the two unions above, listed so that a reader checking a value against
13
+ * them cannot fall out of step with the types. `ooxml/simpleTypes` builds `ST_TabJc` and
14
+ * `ST_TabTlc` on them, adding the spellings the document writes that the model does not keep.
15
+ */
16
+ export declare const TAB_STOP_ALIGNMENTS: readonly TabStop["align"][];
17
+ export declare const TAB_LEADERS: readonly TabLeader[];
9
18
  /** A tab entry before the paragraph-property hierarchy has been resolved. */
10
19
  export type TabStopDirective = TabStop | {
11
20
  positionPt: number;
@@ -1,4 +1,20 @@
1
1
  // src/model/tabStops.ts
2
+ var TAB_STOP_ALIGNMENTS = [
3
+ "start",
4
+ "center",
5
+ "end",
6
+ "decimal",
7
+ "num",
8
+ "bar"
9
+ ];
10
+ var TAB_LEADERS = [
11
+ "none",
12
+ "dot",
13
+ "hyphen",
14
+ "underscore",
15
+ "heavy",
16
+ "middleDot"
17
+ ];
2
18
  function layerTabStopDirectives(base, over) {
3
19
  const stops = /* @__PURE__ */ new Map();
4
20
  for (const stop of base) {
@@ -17,6 +33,8 @@ function mergeTabStops(base, over) {
17
33
  );
18
34
  }
19
35
  export {
36
+ TAB_LEADERS,
37
+ TAB_STOP_ALIGNMENTS,
20
38
  layerTabStopDirectives,
21
39
  mergeTabStops
22
40
  };
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The register of list definitions a document node carries.
3
+ *
4
+ * A list started while editing is registered with the definition it was started with, and the
5
+ * register rides on the document node itself: undo takes a registration back with the edit that
6
+ * made it, and the export writes out what was registered rather than working a definition out for
7
+ * itself. Only what `abstractNumXml` writes in full may be registered, and the value is read back
8
+ * through this file so that nothing else can end up in the register.
9
+ */
10
+ import { type NewList } from "./parseNumbering";
11
+ /** The lists registered on one document, by the number each was given */
12
+ export type NewLists = ReadonlyMap<number, NewList>;
13
+ export declare const NO_NEW_LISTS: NewLists;
14
+ /** The document attr the register travels on */
15
+ export declare const NEW_LISTS_ATTR = "newLists";
16
+ /**
17
+ * What the document node carries for a register.
18
+ *
19
+ * The entries run in the order of the numbers they define so that the same register is always the
20
+ * same value, whichever order the lists were started in, and an empty register is the null a
21
+ * document that never started a list carries: a list left is then a list never started.
22
+ */
23
+ export declare function newListsValue(lists: NewLists): unknown;
24
+ /**
25
+ * The register a document node carries. Empty for a node carrying none, and for one carrying an
26
+ * entry that is not a list definition: a definition the writer cannot read back in full is a
27
+ * definition it must not write half of.
28
+ */
29
+ export declare function newListsOf(value: unknown): NewLists;
30
+ /**
31
+ * The register with the definitions of the lists nothing wears any more taken out.
32
+ *
33
+ * A number goes back into circulation with its definition, so leaving a list is exactly what
34
+ * joining it was not: pressing the list button twice gives back the document that was there
35
+ * before the first press.
36
+ */
37
+ export declare function listsWorn(lists: NewLists, worn: ReadonlySet<number>): NewLists;
@@ -0,0 +1,112 @@
1
+ // src/numbering/listRegistry.ts
2
+ import { ST_SignedTwipsMeasure, ST_TwipsMeasure } from "../ooxml/simpleTypes.js";
3
+ import { MAX_ILVL } from "./listTemplate.js";
4
+ import {
5
+ LEVEL_ALIGNS,
6
+ LEVEL_SUFFIXES
7
+ } from "./parseNumbering.js";
8
+ import { isNumberFormat } from "./spellers.js";
9
+ var NO_NEW_LISTS = /* @__PURE__ */ new Map();
10
+ var NEW_LISTS_ATTR = "newLists";
11
+ function isRecord(value) {
12
+ return typeof value === "object" && value !== null && !Array.isArray(value);
13
+ }
14
+ function toLevelIndent(value) {
15
+ if (!isRecord(value)) return null;
16
+ const { startTwips, endTwips, hangingTwips, firstLineTwips } = value;
17
+ const signed = (slot) => slot === null || typeof slot === "number" && ST_SignedTwipsMeasure.format(slot) !== null;
18
+ const unsigned = (slot) => slot === null || typeof slot === "number" && ST_TwipsMeasure.format(slot) !== null;
19
+ if (!signed(startTwips) || !signed(endTwips) || !unsigned(hangingTwips) || !unsigned(firstLineTwips))
20
+ return null;
21
+ return { startTwips, endTwips, hangingTwips, firstLineTwips };
22
+ }
23
+ function levelValue(ilvl, level) {
24
+ return {
25
+ ilvl,
26
+ format: level.format,
27
+ text: level.text,
28
+ start: level.start,
29
+ indent: level.indent,
30
+ restartAfterLevel: level.restartAfterLevel,
31
+ legal: level.legal,
32
+ suffix: level.suffix,
33
+ align: level.align
34
+ };
35
+ }
36
+ function toLevel(value) {
37
+ if (!isRecord(value)) return null;
38
+ const { ilvl, format, text, start } = value;
39
+ if (typeof ilvl !== "number" || !Number.isSafeInteger(ilvl) || ilvl < 0 || ilvl > MAX_ILVL)
40
+ return null;
41
+ if (typeof format !== "string" || !isNumberFormat(format)) return null;
42
+ if (typeof text !== "string") return null;
43
+ if (typeof start !== "number" || !Number.isSafeInteger(start)) return null;
44
+ const suffix = LEVEL_SUFFIXES.find((known) => known === value.suffix);
45
+ const align = LEVEL_ALIGNS.find((known) => known === value.align);
46
+ if (suffix === void 0 || align === void 0) return null;
47
+ const restartAfterLevel = value.restartAfterLevel;
48
+ if (restartAfterLevel !== null && (typeof restartAfterLevel !== "number" || !Number.isSafeInteger(restartAfterLevel) || restartAfterLevel < -1 || restartAfterLevel >= MAX_ILVL))
49
+ return null;
50
+ if (typeof value.legal !== "boolean") return null;
51
+ if (value.run !== void 0 && value.run !== null) return null;
52
+ if (value.tabStops !== void 0) return null;
53
+ const indent = toLevelIndent(value.indent);
54
+ if (value.indent !== null && indent === null) return null;
55
+ return {
56
+ ilvl,
57
+ level: {
58
+ format,
59
+ text,
60
+ start,
61
+ indent,
62
+ restartAfterLevel,
63
+ legal: value.legal,
64
+ suffix,
65
+ align,
66
+ run: null
67
+ }
68
+ };
69
+ }
70
+ function toList(value) {
71
+ if (!isRecord(value)) return null;
72
+ const { numId, levels } = value;
73
+ if (typeof numId !== "number" || !Number.isSafeInteger(numId) || numId <= 0)
74
+ return null;
75
+ if (!Array.isArray(levels)) return null;
76
+ const read = /* @__PURE__ */ new Map();
77
+ for (const entry of levels) {
78
+ const level = toLevel(entry);
79
+ if (level === null || read.has(level.ilvl)) return null;
80
+ read.set(level.ilvl, level.level);
81
+ }
82
+ return read.size === 0 ? null : { numId, list: { levels: read } };
83
+ }
84
+ function newListsValue(lists) {
85
+ if (lists.size === 0) return null;
86
+ return Array.from(lists).sort(([left], [right]) => left - right).map(([numId, list]) => ({
87
+ numId,
88
+ levels: Array.from(list.levels).sort(([left], [right]) => left - right).map(([ilvl, level]) => levelValue(ilvl, level))
89
+ }));
90
+ }
91
+ function newListsOf(value) {
92
+ if (!Array.isArray(value)) return NO_NEW_LISTS;
93
+ const lists = /* @__PURE__ */ new Map();
94
+ for (const entry of value) {
95
+ const read = toList(entry);
96
+ if (read === null || lists.has(read.numId)) return NO_NEW_LISTS;
97
+ lists.set(read.numId, read.list);
98
+ }
99
+ return lists;
100
+ }
101
+ function listsWorn(lists, worn) {
102
+ const dropped = [...lists.keys()].filter((numId) => !worn.has(numId));
103
+ if (dropped.length === 0) return lists;
104
+ return new Map([...lists].filter(([numId]) => worn.has(numId)));
105
+ }
106
+ export {
107
+ NEW_LISTS_ATTR,
108
+ NO_NEW_LISTS,
109
+ listsWorn,
110
+ newListsOf,
111
+ newListsValue
112
+ };
@@ -1,29 +1,38 @@
1
1
  /**
2
- * The standard shape of a newly started list.
2
+ * A list started while editing: the definition it is started with, the number it takes, and the
3
+ * `<w:abstractNum>` that definition is written out as.
3
4
  *
4
- * The definition of a new list is not kept in the document model.
5
- * So its shape has to be derivable from the list number (numId) alone, and that rule
6
- * lives here. Even numbers are numbered lists, odd numbers are bullet lists.
7
- *
8
- * Drawing the markers on screen and writing the definitions into numbering.xml both look
9
- * at the same template.
5
+ * Nothing here reads anything into the number. The definition is a value the editor registers
6
+ * (`./listRegistry`), the same value the markers are drawn from and the one written into
7
+ * numbering.xml, so a list goes out as what it was started as.
10
8
  */
11
- import type { LevelIndent, Numbering, NumberingList } from "./parseNumbering";
9
+ import type { LevelIndent, NewList, Numbering, NumberingList } from "./parseNumbering";
12
10
  export type ListKind = "numbered" | "bullet";
13
11
  /** A list has nine levels (the count OOXML defines) */
14
12
  export declare const LEVEL_COUNT = 9;
15
13
  export declare const MAX_ILVL: number;
16
14
  export declare function templateIndent(ilvl: number): LevelIndent;
17
- export declare function listKindOf(numId: number): ListKind;
15
+ /** The definition a list of this kind is started with */
16
+ export declare function templateList(kind: ListKind): NewList;
17
+ /**
18
+ * The definition standing behind this number: the one the document wrote down, or the one a list
19
+ * started while editing was registered with. Undefined for a number nothing defines, which is a
20
+ * list neither drawn nor written.
21
+ */
22
+ export declare function listFor(numbering: Numbering, numId: number): NumberingList | undefined;
23
+ /** Reserves safe positive ids, using gaps if incrementing the highest id would lose precision. */
24
+ export declare function numberingIdAllocator(ids: Iterable<number>): () => number;
18
25
  /**
19
- * The list definition for this number.
20
- * A number the document's numbering.xml does not know is read as the standard template
21
- * (which is the case for a list that was just started).
26
+ * A number for a new list, and the numbering that now defines it under that number.
27
+ *
28
+ * `used` names the numbers the document already spends, the ones no definition stands behind
29
+ * included: a number a paragraph already wears would otherwise join that paragraph to the new list.
22
30
  */
23
- export declare function listFor(numbering: Numbering, numId: number): NumberingList;
24
- /** The next number not yet in use. Picks the even or odd one matching the shape */
25
- export declare function nextNumId(used: Iterable<number>, kind: ListKind): number;
26
- /** Writes one template out as a `<w:abstractNum>` definition */
27
- export declare function abstractNumXml(abstractNumId: number, kind: ListKind): string;
31
+ export declare function allocateList(numbering: Numbering, used: Iterable<number>, list: NewList): {
32
+ numId: number;
33
+ numbering: Numbering;
34
+ };
35
+ /** Writes one registered definition out as a `<w:abstractNum>` */
36
+ export declare function abstractNumXml(abstractNumId: number, list: NewList): string;
28
37
  /** The `<w:num>` that ties a list number to a definition */
29
38
  export declare function numXml(numId: number, abstractNumId: number): string;
@@ -1,6 +1,7 @@
1
1
  // src/numbering/listTemplate.ts
2
2
  import { elementXml } from "../ooxml/element.js";
3
3
  import { wName } from "../ooxml/names.js";
4
+ import { orderedElement } from "../ooxml/props.js";
4
5
  var LEVEL_COUNT = 9;
5
6
  var MAX_ILVL = LEVEL_COUNT - 1;
6
7
  var NUMBER_CYCLE = [
@@ -28,7 +29,12 @@ function templateLevel(kind, ilvl) {
28
29
  format: numbered ? cycled(NUMBER_CYCLE, ilvl, "decimal") : "bullet",
29
30
  text: numbered ? `%${ilvl + 1}.` : cycled(BULLET_CYCLE, ilvl, "\u25CF"),
30
31
  start: 1,
31
- indent: templateIndent(ilvl)
32
+ indent: templateIndent(ilvl),
33
+ restartAfterLevel: null,
34
+ legal: false,
35
+ suffix: "tab",
36
+ align: "left",
37
+ run: null
32
38
  };
33
39
  }
34
40
  function templateList(kind) {
@@ -38,19 +44,35 @@ function templateList(kind) {
38
44
  }
39
45
  return { levels };
40
46
  }
41
- var TEMPLATES = {
42
- numbered: templateList("numbered"),
43
- bullet: templateList("bullet")
44
- };
45
- function listKindOf(numId) {
46
- return numId % 2 === 0 ? "numbered" : "bullet";
47
- }
48
47
  function listFor(numbering, numId) {
49
- return numbering.lists.get(numId) ?? TEMPLATES[listKindOf(numId)];
48
+ return numbering.lists.get(numId) ?? numbering.added.get(numId);
49
+ }
50
+ function numberingIdAllocator(ids) {
51
+ const used = new Set(ids);
52
+ let highest = 0;
53
+ for (const id of used) if (id > highest) highest = id;
54
+ let next = Number.isSafeInteger(highest + 1) ? highest + 1 : 1;
55
+ return () => {
56
+ while (used.has(next)) next += 1;
57
+ const id = next;
58
+ used.add(id);
59
+ next = Number.isSafeInteger(id + 1) ? id + 1 : 1;
60
+ return id;
61
+ };
50
62
  }
51
- function nextNumId(used, kind) {
52
- const from = Math.max(0, ...used) + 1;
53
- return listKindOf(from) === kind ? from : from + 1;
63
+ function allocateList(numbering, used, list) {
64
+ const numId = numberingIdAllocator([
65
+ ...numbering.lists.keys(),
66
+ ...numbering.added.keys(),
67
+ ...used
68
+ ])();
69
+ return {
70
+ numId,
71
+ numbering: {
72
+ ...numbering,
73
+ added: new Map([...numbering.added, [numId, list]])
74
+ }
75
+ };
54
76
  }
55
77
  function indXml(indent) {
56
78
  const slots = [
@@ -61,25 +83,65 @@ function indXml(indent) {
61
83
  ];
62
84
  const attrs = slots.filter((slot) => slot[1] !== null).map(([name, twips]) => [wName(name), `${twips}`]);
63
85
  if (attrs.length === 0) return "";
64
- return elementXml(wName("pPr"), [], [elementXml(wName("ind"), attrs)]);
86
+ return orderedElement(
87
+ wName("pPr"),
88
+ [],
89
+ [{ name: "ind", xml: elementXml(wName("ind"), attrs) }]
90
+ );
91
+ }
92
+ function lvlRestartXml(restartAfterLevel) {
93
+ if (restartAfterLevel === null) return null;
94
+ return {
95
+ name: "lvlRestart",
96
+ xml: elementXml(wName("lvlRestart"), [
97
+ [wName("val"), `${restartAfterLevel + 1}`]
98
+ ])
99
+ };
100
+ }
101
+ function isLglXml(legal) {
102
+ return legal ? { name: "isLgl", xml: elementXml(wName("isLgl"), []) } : null;
103
+ }
104
+ function suffXml(suffix) {
105
+ if (suffix === "tab") return null;
106
+ return {
107
+ name: "suff",
108
+ xml: elementXml(wName("suff"), [[wName("val"), suffix]])
109
+ };
65
110
  }
66
111
  function levelXml(ilvl, level) {
67
112
  const ind = indXml(level.indent);
68
- return elementXml(
113
+ const optional = [
114
+ lvlRestartXml(level.restartAfterLevel),
115
+ isLglXml(level.legal),
116
+ suffXml(level.suffix),
117
+ ind === "" ? null : { name: "pPr", xml: ind }
118
+ ];
119
+ return orderedElement(
69
120
  wName("lvl"),
70
121
  [[wName("ilvl"), `${ilvl}`]],
71
122
  [
72
- elementXml(wName("start"), [[wName("val"), `${level.start}`]]),
73
- elementXml(wName("numFmt"), [[wName("val"), level.format]]),
74
- elementXml(wName("lvlText"), [[wName("val"), level.text]]),
75
- elementXml(wName("lvlJc"), [[wName("val"), "left"]]),
76
- ind
123
+ {
124
+ name: "start",
125
+ xml: elementXml(wName("start"), [[wName("val"), `${level.start}`]])
126
+ },
127
+ {
128
+ name: "numFmt",
129
+ xml: elementXml(wName("numFmt"), [[wName("val"), level.format]])
130
+ },
131
+ {
132
+ name: "lvlText",
133
+ xml: elementXml(wName("lvlText"), [[wName("val"), level.text]])
134
+ },
135
+ {
136
+ name: "lvlJc",
137
+ xml: elementXml(wName("lvlJc"), [[wName("val"), level.align]])
138
+ },
139
+ ...optional.filter((child) => child !== null)
77
140
  ]
78
141
  );
79
142
  }
80
- function abstractNumXml(abstractNumId, kind) {
81
- const levels = TEMPLATES[kind].levels;
82
- const body = Array.from(levels.entries()).map(([ilvl, level]) => levelXml(ilvl, level)).join("");
143
+ function abstractNumXml(abstractNumId, list) {
144
+ const body = Array.from(list.levels.entries()).map(([ilvl, level]) => levelXml(ilvl, level)).join("");
83
145
  return elementXml(
84
146
  wName("abstractNum"),
85
147
  [[wName("abstractNumId"), `${abstractNumId}`]],
@@ -97,9 +159,10 @@ export {
97
159
  LEVEL_COUNT,
98
160
  MAX_ILVL,
99
161
  abstractNumXml,
162
+ allocateList,
100
163
  listFor,
101
- listKindOf,
102
- nextNumId,
103
164
  numXml,
104
- templateIndent
165
+ numberingIdAllocator,
166
+ templateIndent,
167
+ templateList
105
168
  };
@@ -4,21 +4,30 @@
4
4
  * A pure computation that never touches the document model: the same paragraphs with the
5
5
  * same numbering definitions always yield the same numbers.
6
6
  */
7
- import type { NumberingRef } from "../model/format";
8
- import { type LevelIndentPt, type Numbering } from "./parseNumbering";
7
+ import type { NumberingRef, RunFormat } from "../model/format";
8
+ import { type LevelAlign, type LevelIndentPt, type LevelSuffix, type Numbering } from "./parseNumbering";
9
9
  /** The number to draw in front of one paragraph */
10
10
  export interface ListMarker {
11
+ /**
12
+ * The number as it is drawn, the space a level asking for one (`w:suff`) included: a separator
13
+ * made of a character belongs to the marker, while the tab every other level asks for is the
14
+ * width the number sits in rather than anything to draw
15
+ */
11
16
  text: string;
12
17
  /**
13
18
  * The indent defined by the level this paragraph belongs to. Used when the paragraph
14
19
  * records none of its own
15
20
  */
16
21
  indent: LevelIndentPt;
22
+ /** Whether the number sits in a width of its own, which is what a tab suffix asks for */
23
+ suffix: LevelSuffix;
24
+ align: LevelAlign;
25
+ /** The formatting the level puts on the number, which dresses the number and nothing else */
26
+ run: RunFormat | null;
17
27
  }
18
28
  /**
19
- * A slot is null for a paragraph that is not a list item, or whose level shape could not
20
- * be found.
21
- * A number the document does not know (a list started fresh while editing) is drawn with
22
- * the standard template's shape.
29
+ * A slot is null for a paragraph that is not a list item, or whose level shape could not be found.
30
+ * A number nothing defines - neither numbering.xml nor a list started while editing - draws no
31
+ * marker, which is what Word does with a `w:numId` no `w:num` answers.
23
32
  */
24
33
  export declare function computeMarkers(paragraphs: readonly (NumberingRef | null)[], numbering: Numbering): (ListMarker | null)[];