@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
@@ -5,30 +5,18 @@
5
5
  * even things we do not know about). So instead of building a new fragment we swap out just the one
6
6
  * child. It is the same principle `tableFormatting` applies to `w:tcPr`.
7
7
  *
8
+ * Which children a job swaps, and what it swaps them for, is the run property table's to say
9
+ * (`formatting/runProperties`); this module holds the surgery around it.
10
+ *
8
11
  * The display values produced here are used on screen only. What goes back into the document is
9
12
  * always the rPr string.
10
13
  */
11
14
  import { type RunFormat } from "../model/format";
15
+ import { type EditableRunKey, type RunEdit } from "./formatting";
16
+ export type { RunEdit } from "./formatting";
17
+ export { matchesRunEdit } from "./formatting";
12
18
  /** The character formatting that is toggled on and off */
13
19
  export type RunToggle = "bold" | "italic" | "underline" | "strike";
14
- /** A job that changes one piece of character formatting. A null value means withdrawing the setting */
15
- export type RunEdit = {
16
- kind: "toggle";
17
- toggle: RunToggle;
18
- on: boolean;
19
- } | {
20
- kind: "fontSize";
21
- pt: number | null;
22
- } | {
23
- kind: "fontFamily";
24
- name: string | null;
25
- } | {
26
- kind: "color";
27
- hex: string | null;
28
- } | {
29
- kind: "background";
30
- hex: string | null;
31
- };
32
20
  /** The formatting a run holds. The original XML and the display values derived from that XML form a pair */
33
21
  export interface RunProps {
34
22
  /** The whole `<w:rPr>...</w:rPr>` XML. null for a run with no formatting */
@@ -40,12 +28,11 @@ export declare function readRunProps(rPr: string | null): RunFormat | null;
40
28
  /**
41
29
  * The rPr and display values after changing one piece of character formatting.
42
30
  *
43
- * `pPr` is used only to see whether the paragraph points at a style.
31
+ * `inherited` is everything the layers below the run lay down (`inheritedRunFormat` in
32
+ * `formatting`), which decides whether an off is pinned down or the element simply removed.
44
33
  * null for an rPr whose shape could not be made out, or for a value that cannot be written into
45
34
  * the document, in which case the original is left untouched.
46
35
  */
47
- export declare function editRunProps(current: RunProps, pPr: string | null, edit: RunEdit): RunProps | null;
36
+ export declare function editRunProps<K extends EditableRunKey>(current: RunProps, inherited: RunFormat, edit: RunEdit<K>): RunProps | null;
48
37
  /** Whether one of the toggled formats is on in these display values */
49
38
  export declare function isRunToggleOn(format: RunFormat | null, toggle: RunToggle): boolean;
50
- /** Whether this text is already in the state the job wants. If it already is, we leave it untouched */
51
- export declare function matchesRunEdit(format: RunFormat | null, edit: RunEdit): boolean;
@@ -1,139 +1,20 @@
1
1
  // src/docx/runProps.ts
2
2
  import { toRunFormat } from "../model/format.js";
3
- import {
4
- attrPairs,
5
- attrValue,
6
- elementXml
7
- } from "../ooxml/element.js";
8
- import { wName } from "../ooxml/names.js";
9
- import { setAttr } from "../ooxml/precedence.js";
10
- import { normalizeHex } from "../ooxml/units.js";
11
- import { isEastAsianFontName } from "../styles/fontStack.js";
12
- import { fontNamesOf, readRunFormat } from "./formatting.js";
13
3
  import {
14
4
  parseProps,
15
5
  parsePropsXml,
16
- propsChild,
17
- RUN_PR_ORDER,
18
- renderProps,
19
- setPropsChild
20
- } from "./propsXml.js";
21
- var TOGGLE_CHILDREN = {
22
- bold: ["b", "bCs"],
23
- italic: ["i", "iCs"],
24
- underline: ["u"],
25
- strike: ["strike"]
26
- };
27
- var TOGGLE_ON_VALUE = {
28
- bold: null,
29
- italic: null,
30
- underline: "single",
31
- strike: null
32
- };
33
- var TOGGLE_OFF_VALUE = {
34
- bold: "0",
35
- italic: "0",
36
- underline: "none",
37
- strike: "0"
38
- };
39
- var MAX_FONT_SIZE_PT = 819;
40
- function valXml(name, value) {
41
- return elementXml(wName(name), value === null ? [] : [[wName("val"), value]]);
42
- }
43
- function shadingXml(fill) {
44
- return elementXml(wName("shd"), [
45
- [wName("val"), "clear"],
46
- [wName("color"), "auto"],
47
- [wName("fill"), fill]
48
- ]);
49
- }
50
- function halfPoints(pt) {
51
- if (!Number.isFinite(pt) || pt <= 0 || pt > MAX_FONT_SIZE_PT) return null;
52
- const half = pt * 2;
53
- return Number.isInteger(half) ? half : null;
54
- }
55
- var LATIN_SLOTS = ["ascii", "hAnsi"];
56
- var EAST_ASIAN_SLOTS = [...LATIN_SLOTS, "eastAsia"];
57
- function fontSlots(name, hasComplexScript) {
58
- const slots = isEastAsianFontName(name) ? EAST_ASIAN_SLOTS : LATIN_SLOTS;
59
- return hasComplexScript ? [...slots, "cs"] : slots;
60
- }
61
- function fontName(value) {
62
- const name = value.trim();
63
- return name.length > 0 && !/["';<>&]/.test(name) ? name : null;
64
- }
65
- function rFontsXml(current, name) {
66
- const el = current === null ? null : parsePropsXml(current);
67
- if (current !== null && !el) return null;
68
- const attrs = attrPairs(el);
69
- const slots = fontSlots(name, attrValue(attrs, "cs") !== null);
70
- const kept = slots.reduce(
71
- (rest, slot) => setAttr(rest, "rFonts", slot, null),
72
- attrs
73
- );
74
- return elementXml(wName("rFonts"), [
75
- ...slots.map((slot) => [wName(slot), name]),
76
- ...kept
77
- ]);
78
- }
79
- function hasChild(xml, name) {
80
- if (xml === null) return false;
81
- const props = parseProps(xml);
82
- return props !== null && propsChild(props.children, name) !== void 0;
83
- }
6
+ renderProps
7
+ } from "../ooxml/props.js";
8
+ import {
9
+ EMPTY_RUN_PROPS,
10
+ RUN_PROPERTIES,
11
+ readRunFormat,
12
+ runChildEdits,
13
+ withChildEdits
14
+ } from "./formatting.js";
15
+ import { matchesRunEdit } from "./formatting.js";
84
16
  function propsOf(rPr) {
85
- if (rPr === null) return { tag: "w:rPr", attrs: null, children: [] };
86
- return parseProps(rPr);
87
- }
88
- function offEdit(name, value, inherited) {
89
- return [name, inherited ? valXml(name, value) : null];
90
- }
91
- function childEdits(edit, inherited, rFonts) {
92
- switch (edit.kind) {
93
- case "toggle": {
94
- const names = TOGGLE_CHILDREN[edit.toggle];
95
- if (edit.on) {
96
- const value = TOGGLE_ON_VALUE[edit.toggle];
97
- return names.map((name) => [name, valXml(name, value)]);
98
- }
99
- const off = TOGGLE_OFF_VALUE[edit.toggle];
100
- return names.map((name) => offEdit(name, off, inherited));
101
- }
102
- case "fontSize": {
103
- if (edit.pt === null) {
104
- return [
105
- ["sz", null],
106
- ["szCs", null]
107
- ];
108
- }
109
- const half = halfPoints(edit.pt);
110
- if (half === null) return null;
111
- return [
112
- ["sz", valXml("sz", `${half}`)],
113
- ["szCs", valXml("szCs", `${half}`)]
114
- ];
115
- }
116
- case "fontFamily": {
117
- if (edit.name === null) return [["rFonts", null]];
118
- const name = fontName(edit.name);
119
- if (name === null) return null;
120
- const xml = rFontsXml(rFonts, name);
121
- return xml === null ? null : [["rFonts", xml]];
122
- }
123
- case "color": {
124
- if (edit.hex === null) return [offEdit("color", "auto", inherited)];
125
- const hex = normalizeHex(edit.hex);
126
- return hex === null ? null : [["color", valXml("color", hex)]];
127
- }
128
- case "background": {
129
- const highlight = offEdit("highlight", "none", inherited);
130
- if (edit.hex === null) {
131
- return [["shd", inherited ? shadingXml("auto") : null], highlight];
132
- }
133
- const hex = normalizeHex(edit.hex);
134
- return hex === null ? null : [["shd", shadingXml(hex)], highlight];
135
- }
136
- }
17
+ return rPr === null ? EMPTY_RUN_PROPS : parseProps(rPr);
137
18
  }
138
19
  function readRunProps(rPr) {
139
20
  return rPr === null ? null : readRunFormat(parsePropsXml(rPr));
@@ -154,50 +35,16 @@ function nextProps(rPr, previous) {
154
35
  }
155
36
  return { rPr, format };
156
37
  }
157
- function editRunProps(current, pPr, edit) {
38
+ function editRunProps(current, inherited, edit) {
158
39
  const props = propsOf(current.rPr);
159
40
  if (!props) return null;
160
- const inherited = propsChild(props.children, "rStyle") !== void 0 || hasChild(pPr, "pStyle");
161
- const rFonts = propsChild(props.children, "rFonts")?.xml ?? null;
162
- const edits = childEdits(edit, inherited, rFonts);
41
+ const edits = runChildEdits(edit, { rPr: props, inherited });
163
42
  if (!edits) return null;
164
- const rPr = renderProps(
165
- edits.reduce(
166
- (kept, [name, xml]) => setPropsChild(kept, name, xml, RUN_PR_ORDER),
167
- props
168
- )
169
- );
43
+ const rPr = renderProps(withChildEdits(props, edits));
170
44
  return nextProps(rPr === "" ? null : rPr, current);
171
45
  }
172
46
  function isRunToggleOn(format, toggle) {
173
- if (!format) return false;
174
- if (toggle === "bold") return format.bold === true;
175
- if (toggle === "italic") return format.italic === true;
176
- if (toggle === "strike") return format.strike === true;
177
- return format.underline !== void 0;
178
- }
179
- function matchesRunEdit(format, edit) {
180
- switch (edit.kind) {
181
- case "toggle":
182
- return isRunToggleOn(format, edit.toggle) === edit.on;
183
- case "fontSize":
184
- return (format?.fontSizePt ?? null) === edit.pt;
185
- case "fontFamily": {
186
- const names = fontNamesOf(format?.fontFamily);
187
- if (edit.name === null) return names.length === 0;
188
- return names.length === 1 && names[0] === fontName(edit.name);
189
- }
190
- case "color": {
191
- const current = format?.color ?? null;
192
- if (current === null || edit.hex === null) return current === edit.hex;
193
- return normalizeHex(current) === normalizeHex(edit.hex);
194
- }
195
- case "background": {
196
- if (format?.highlight !== void 0) return false;
197
- const fill = format?.background ? normalizeHex(format.background) : null;
198
- return fill === (edit.hex === null ? null : normalizeHex(edit.hex));
199
- }
200
- }
47
+ return RUN_PROPERTIES[toggle].isOn(format ?? {});
201
48
  }
202
49
  export {
203
50
  editRunProps,
package/dist/docx/scan.js CHANGED
@@ -1,49 +1,19 @@
1
1
  // src/docx/scan.ts
2
2
  import { DocxImportError } from "../ooxml/errors.js";
3
+ import { readTag } from "../ooxml/tagScan.js";
3
4
  import { localPart } from "../ooxml/xml.js";
4
- function skipPast(source, from, marker) {
5
- const at = source.indexOf(marker, from);
6
- if (at === -1) {
7
- throw new DocxImportError("malformed-xml", `no ${marker} to close a tag`);
8
- }
9
- return at + marker.length;
10
- }
11
- function skipNonTag(source, lt) {
12
- if (source.startsWith("<?", lt)) return skipPast(source, lt, "?>");
13
- if (source.startsWith("<!--", lt)) return skipPast(source, lt, "-->");
14
- if (source.startsWith("<![CDATA[", lt)) return skipPast(source, lt, "]]>");
15
- if (source.startsWith("<!", lt)) return skipPast(source, lt, ">");
16
- return null;
17
- }
18
- function readOpenTag(source, lt) {
19
- let i = lt + 1;
20
- const nameStart = i;
21
- while (i < source.length && !" \r\n/>".includes(source[i])) i += 1;
22
- const name = source.slice(nameStart, i);
23
- if (!name)
24
- throw new DocxImportError("malformed-xml", "a tag has an empty name");
25
- let quote = null;
26
- while (i < source.length) {
27
- const ch = source[i];
28
- if (quote !== null) {
29
- if (ch === quote) quote = null;
30
- } else if (ch === '"' || ch === "'") {
31
- quote = ch;
32
- } else if (ch === ">") {
33
- return { name, end: i + 1, selfClosing: source[i - 1] === "/" };
34
- }
35
- i += 1;
5
+ function tagAt(source, lt) {
6
+ const tag = readTag(source, lt);
7
+ if (!tag) {
8
+ throw new DocxImportError(
9
+ "malformed-xml",
10
+ "a tag cannot be read to its end"
11
+ );
36
12
  }
37
- throw new DocxImportError("malformed-xml", "a tag is left unclosed");
38
- }
39
- function readCloseTag(source, lt) {
40
- const gt = source.indexOf(">", lt);
41
- if (gt === -1)
42
- throw new DocxImportError("malformed-xml", "a tag is left unclosed");
43
- return { name: source.slice(lt + 2, gt).trim(), end: gt + 1 };
13
+ return tag;
44
14
  }
45
15
  function isBodyTag(tag, depth) {
46
- return localPart(tag.name) === "body" && depth === 1 && !tag.selfClosing;
16
+ return localPart(tag.name) === "body" && depth === 1 && tag.kind === "open";
47
17
  }
48
18
  function scanBody(source) {
49
19
  const stack = [];
@@ -60,33 +30,31 @@ function scanBody(source) {
60
30
  while (i < source.length) {
61
31
  const lt = source.indexOf("<", i);
62
32
  if (lt === -1) break;
63
- const skipped = skipNonTag(source, lt);
64
- if (skipped !== null) {
65
- i = skipped;
33
+ const tag = tagAt(source, lt);
34
+ if (tag.kind === "other") {
35
+ i = tag.end;
66
36
  continue;
67
37
  }
68
- if (source.startsWith("</", lt)) {
69
- const tag2 = readCloseTag(source, lt);
38
+ if (tag.kind === "close") {
70
39
  const opened = stack.pop();
71
- if (opened !== tag2.name) {
40
+ if (opened !== tag.name) {
72
41
  throw new DocxImportError(
73
42
  "malformed-xml",
74
- `mismatched tags: ${opened} vs ${tag2.name}`
43
+ `mismatched tags: ${opened} vs ${tag.name}`
75
44
  );
76
45
  }
77
- if (atBodyLevel()) takeBlock(tag2.name, tag2.end);
78
- i = tag2.end;
46
+ if (atBodyLevel()) takeBlock(tag.name, tag.end);
47
+ i = tag.end;
79
48
  continue;
80
49
  }
81
- const tag = readOpenTag(source, lt);
82
50
  if (bodyDepth === null && isBodyTag(tag, stack.length)) {
83
51
  stack.push(tag.name);
84
52
  bodyDepth = stack.length;
85
53
  contentStart = tag.end;
86
54
  sliceStart = tag.end;
87
55
  } else {
88
- if (atBodyLevel() && tag.selfClosing) takeBlock(tag.name, tag.end);
89
- if (!tag.selfClosing) stack.push(tag.name);
56
+ if (atBodyLevel() && tag.kind === "empty") takeBlock(tag.name, tag.end);
57
+ if (tag.kind === "open") stack.push(tag.name);
90
58
  }
91
59
  i = tag.end;
92
60
  }
package/dist/docx/sdt.js CHANGED
@@ -1,14 +1,14 @@
1
1
  // src/docx/sdt.ts
2
- import { elementXml, emptyTagXml, openTagXml } from "../ooxml/element.js";
2
+ import { elementXml, openTagXml } from "../ooxml/element.js";
3
3
  import { wName } from "../ooxml/names.js";
4
- import { wAttr } from "../ooxml/units.js";
5
- import { attrString, elementChildren, serializeXml } from "../ooxml/xml.js";
6
4
  import {
7
5
  parseProps,
8
6
  propsChild,
9
- renderProps,
10
- setPropsChild
11
- } from "./propsXml.js";
7
+ renderElement,
8
+ setChild
9
+ } from "../ooxml/props.js";
10
+ import { wAttr } from "../ooxml/units.js";
11
+ import { attrString, elementChildren, serializeXml } from "../ooxml/xml.js";
12
12
  var CONTENTS_LOCKED = ["contentLocked", "sdtContentLocked"];
13
13
  var DELETION_LOCKED = ["sdtLocked", "sdtContentLocked"];
14
14
  function lockValue(sdtPr) {
@@ -39,18 +39,6 @@ function readSdtWrapper(el) {
39
39
  deletionLocked: val !== null && DELETION_LOCKED.includes(val)
40
40
  };
41
41
  }
42
- var SDT_ORDER = ["sdtPr", "sdtEndPr", "sdtContent"];
43
- var SDT_PR_ORDER = [
44
- "rPr",
45
- "alias",
46
- "tag",
47
- "id",
48
- "lock",
49
- "placeholder",
50
- "showingPlcHdr",
51
- "dataBinding",
52
- "temporary"
53
- ];
54
42
  function newControlId() {
55
43
  return Math.floor(Math.random() * 2147483647);
56
44
  }
@@ -67,28 +55,15 @@ function namesNothing(prefix) {
67
55
  if (!props || sdt.children.length !== 1) return false;
68
56
  return props.children.every((child) => NAMES_NOTHING.includes(child.name));
69
57
  }
70
- function emptyProps(props) {
71
- return emptyTagXml(props.tag, props.attrs);
72
- }
73
58
  function editSdtPrefix(prefix, edits) {
74
59
  const sdt = parseProps(`${prefix}</w:sdt>`);
75
60
  const sdtPr = sdt && propsChild(sdt.children, "sdtPr");
76
61
  const props = sdtPr ? parseProps(sdtPr.xml) : null;
77
62
  if (!sdt || !props) return null;
78
- const rendered = renderProps(
79
- edits.reduce(
80
- (kept, [name, xml]) => setPropsChild(kept, name, xml, SDT_PR_ORDER),
81
- props
82
- )
83
- );
84
- return renderPrefix(
85
- setPropsChild(
86
- sdt,
87
- "sdtPr",
88
- rendered === "" ? emptyProps(props) : rendered,
89
- SDT_ORDER
90
- )
63
+ const rendered = renderElement(
64
+ edits.reduce((kept, [name, xml]) => setChild(kept, name, xml), props)
91
65
  );
66
+ return renderPrefix(setChild(sdt, "sdtPr", rendered));
92
67
  }
93
68
  function copiedControlPrefix(prefix, id) {
94
69
  const copied = editSdtPrefix(prefix, [
@@ -1,5 +1,6 @@
1
1
  /**
2
- * Turning the lock of a content control on and off inside the opening XML it goes back out as.
2
+ * Turning the lock of a content control on and off inside the opening XML it goes back out as,
3
+ * and writing the opening of a control that is locked from the start.
3
4
  *
4
5
  * Nothing but the `w:lock` child of the `w:sdtPr` moves. The id Word gave the control, the alias
5
6
  * and tag it goes by, and the `w:dataBinding` that ties it to the file's own XML all stay, which
@@ -10,6 +11,12 @@
10
11
  * A control Word gave nothing but a guard against deletion (`sdtLocked`) is therefore shut by
11
12
  * being given the stricter value, and lifting that lock afterwards opens both clauses.
12
13
  */
14
+ /**
15
+ * The opening of a new control that shuts both clauses, which is what a stretch being locked is
16
+ * wrapped in. It carries its id and the lock and nothing else, so a later lock may take it over
17
+ * (`namesNothing` in `./sdt`).
18
+ */
19
+ export declare function lockedControlPrefix(id: number): string;
13
20
  /**
14
21
  * The opening of the control with its lock shut or lifted.
15
22
  * null for a prefix whose shape cannot be made out, which leaves the caller to decide what to do
@@ -1,11 +1,21 @@
1
1
  // src/docx/sdtProps.ts
2
+ import { elementXml, openTagXml } from "../ooxml/element.js";
3
+ import { wName } from "../ooxml/names.js";
2
4
  import { editSdtPrefix } from "./sdt.js";
3
5
  var LOCK_BOTH_CLAUSES = '<w:lock w:val="sdtContentLocked"/>';
6
+ function lockedControlPrefix(id) {
7
+ return openTagXml(wName("sdt"), null) + elementXml(
8
+ wName("sdtPr"),
9
+ [],
10
+ [elementXml(wName("id"), [[wName("val"), `${id}`]]), LOCK_BOTH_CLAUSES]
11
+ );
12
+ }
4
13
  function withContentLock(sdtPrefix, locked) {
5
14
  return editSdtPrefix(sdtPrefix, [
6
15
  ["lock", locked ? LOCK_BOTH_CLAUSES : null]
7
16
  ]);
8
17
  }
9
18
  export {
19
+ lockedControlPrefix,
10
20
  withContentLock
11
21
  };
@@ -1,4 +1,6 @@
1
1
  import type { Node as PMNode } from "prosemirror-model";
2
2
  import { type ExportRefs } from "./exportRefs";
3
3
  import { type SessionStore } from "./session";
4
+ /** Why there is no original to write, which a block that came in from another document answers differently */
5
+ export declare function lostOriginal(node: PMNode, session: SessionStore): string;
4
6
  export declare function serializeBlock(node: PMNode, session: SessionStore, refs?: ExportRefs): string;
@@ -3,14 +3,16 @@ import { DocxExportError } from "../ooxml/errors.js";
3
3
  import { NO_EXPORT_REFS } from "./exportRefs.js";
4
4
  import { preservedXml, serializeParagraph } from "./serializeParagraph.js";
5
5
  import { serializeTable } from "./serializeTable.js";
6
- import { originalBlock } from "./session.js";
6
+ import { originalBlock, splitBlockKey } from "./session.js";
7
+ function lostOriginal(node, session) {
8
+ const srcId = node.attrs.srcId;
9
+ const key = typeof srcId === "string" ? splitBlockKey(srcId) : null;
10
+ return key === null || key.sessionId === session.sessionId ? "a preserved block has lost its original XML" : `a preserved block comes from another document (${key.sessionId})`;
11
+ }
7
12
  function serializeRaw(node, session) {
8
13
  const imported = originalBlock(node, session);
9
14
  if (!imported) {
10
- throw new DocxExportError(
11
- "lost-original",
12
- "a preserved block has lost its original XML"
13
- );
15
+ throw new DocxExportError("lost-original", lostOriginal(node, session));
14
16
  }
15
17
  return imported.xml;
16
18
  }
@@ -25,5 +27,6 @@ function serializeBlock(node, session, refs = NO_EXPORT_REFS) {
25
27
  );
26
28
  }
27
29
  export {
30
+ lostOriginal,
28
31
  serializeBlock
29
32
  };
@@ -8,16 +8,15 @@ import {
8
8
  import { elementXml, openTagXml } from "../ooxml/element.js";
9
9
  import { DocxExportError } from "../ooxml/errors.js";
10
10
  import { wName } from "../ooxml/names.js";
11
- import { NO_EXPORT_REFS } from "./exportRefs.js";
12
11
  import {
13
12
  innerXml,
14
13
  parseProps,
15
14
  propsChild,
16
15
  renderProps,
17
- setPropsChild,
18
- TBL_PR_ORDER,
19
- TC_PR_ORDER
20
- } from "./propsXml.js";
16
+ setChild
17
+ } from "../ooxml/props.js";
18
+ import { ST_MeasurementOrPercent, ST_TwipsMeasure } from "../ooxml/simpleTypes.js";
19
+ import { NO_EXPORT_REFS } from "./exportRefs.js";
21
20
  import {
22
21
  preservedXml,
23
22
  rawAttrsOf,
@@ -35,31 +34,49 @@ function modelled(name, attrs, replacing) {
35
34
  const inner = replacing === void 0 ? "" : innerXml(replacing);
36
35
  return elementXml(wName(name), attrs, inner === "" ? [] : [inner]);
37
36
  }
37
+ function widthText(width) {
38
+ const value = widthNumber(width);
39
+ if (value === null || !Number.isFinite(value)) return "0";
40
+ return ST_MeasurementOrPercent.format({
41
+ kind: "number",
42
+ value: Math.round(value)
43
+ }) ?? "0";
44
+ }
38
45
  function widthXml(name, width, replacing) {
39
46
  return modelled(
40
47
  name,
41
48
  [
42
- [wName("w"), `${widthNumber(width) ?? 0}`],
49
+ [wName("w"), widthText(width)],
43
50
  [wName("type"), width.type]
44
51
  ],
45
52
  replacing
46
53
  );
47
54
  }
48
- function withWidth(props, name, width, order) {
55
+ function withWidth(props, name, width) {
49
56
  if (!width) return props;
50
57
  const replacing = propsChild(props.children, name)?.xml;
51
- return setPropsChild(props, name, widthXml(name, width, replacing), order);
58
+ return setChild(props, name, widthXml(name, width, replacing));
52
59
  }
53
60
  function tablePropsXml(table) {
54
61
  const props = propsOf(table.attrs.tblPr, "w:tblPr");
55
62
  const width = toTableWidth(table.attrs.tblW);
56
- return renderProps(withWidth(props, "tblW", width, TBL_PR_ORDER));
63
+ return renderProps(withWidth(props, "tblW", width)) || elementXml(wName("tblPr"), []);
57
64
  }
58
65
  function tableGridXml(table) {
59
66
  const gridCols = toGridCols(table.attrs.gridCols);
60
- if (gridCols.length === 0) return "";
61
- const cols = gridCols.map((w) => elementXml(wName("gridCol"), [[wName("w"), `${w}`]])).join("");
62
- return elementXml(wName("tblGrid"), [], [cols]);
67
+ const gridChange = typeof table.attrs.gridChange === "string" ? table.attrs.gridChange : "";
68
+ if (gridCols.length === 0 && gridChange === "") return "";
69
+ const cols = gridCols.map((w) => {
70
+ const width = ST_TwipsMeasure.format(Math.round(w));
71
+ if (width === null) {
72
+ throw new DocxExportError(
73
+ "invalid-table",
74
+ "a grid column has no writable width"
75
+ );
76
+ }
77
+ return elementXml(wName("gridCol"), [[wName("w"), width]]);
78
+ }).join("");
79
+ return elementXml(wName("tblGrid"), [], [cols + gridChange]);
63
80
  }
64
81
  function cellPropsXml(cell, role) {
65
82
  const props = propsOf(cell.attrs.tcPr, "w:tcPr");
@@ -75,10 +92,10 @@ function cellPropsXml(cell, role) {
75
92
  [[wName("val"), "restart"]],
76
93
  propsChild(props.children, "vMerge")?.xml
77
94
  ) : null;
78
- const spanned = setPropsChild(props, "gridSpan", gridSpan, TC_PR_ORDER);
79
- const merged = setPropsChild(spanned, "vMerge", vMerge, TC_PR_ORDER);
95
+ const spanned = setChild(props, "gridSpan", gridSpan);
96
+ const merged = setChild(spanned, "vMerge", vMerge);
80
97
  const width = toTableWidth(cell.attrs.tcW);
81
- return renderProps(withWidth(merged, "tcW", width, TC_PR_ORDER));
98
+ return renderProps(withWidth(merged, "tcW", width));
82
99
  }
83
100
  function cellBlockXml(block, refs) {
84
101
  if (block.type.name === "paragraph") return serializeParagraph(block, refs);