@portone/docx-editor 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (256) hide show
  1. package/CHANGELOG.md +174 -0
  2. package/CONTRIBUTING.md +9 -1
  3. package/dist/DocxEditor.d.ts +6 -0
  4. package/dist/DocxEditor.js +12 -29
  5. package/dist/core.d.ts +23 -8
  6. package/dist/core.js +9 -4
  7. package/dist/docx/cloning.d.ts +38 -0
  8. package/dist/docx/cloning.js +74 -0
  9. package/dist/docx/commentOnlyChange.d.ts +14 -7
  10. package/dist/docx/commentOnlyChange.js +9 -139
  11. package/dist/docx/comments/constants.d.ts +3 -4
  12. package/dist/docx/comments/constants.js +4 -5
  13. package/dist/docx/comments/grammar.d.ts +86 -0
  14. package/dist/docx/comments/grammar.js +223 -0
  15. package/dist/docx/comments/parts.d.ts +52 -0
  16. package/dist/docx/comments/parts.js +279 -0
  17. package/dist/docx/comments/people.d.ts +11 -2
  18. package/dist/docx/comments/people.js +28 -106
  19. package/dist/docx/comments/policy.d.ts +7 -0
  20. package/dist/docx/comments/policy.js +43 -0
  21. package/dist/docx/comments/reading.d.ts +5 -3
  22. package/dist/docx/comments/reading.js +30 -29
  23. package/dist/docx/comments/writing.d.ts +19 -7
  24. package/dist/docx/comments/writing.js +105 -137
  25. package/dist/docx/documentSettings.d.ts +7 -0
  26. package/dist/docx/documentSettings.js +10 -2
  27. package/dist/docx/exportDocx.d.ts +40 -5
  28. package/dist/docx/exportDocx.js +71 -72
  29. package/dist/docx/exportRefs.d.ts +5 -2
  30. package/dist/docx/exportRefs.js +3 -1
  31. package/dist/docx/fidelity.d.ts +43 -0
  32. package/dist/docx/fidelity.js +64 -0
  33. package/dist/docx/formatting/attrs.d.ts +27 -0
  34. package/dist/docx/formatting/attrs.js +31 -0
  35. package/dist/docx/formatting/context.d.ts +31 -0
  36. package/dist/docx/formatting/context.js +42 -0
  37. package/dist/docx/formatting/direct.d.ts +6 -5
  38. package/dist/docx/formatting/direct.js +52 -81
  39. package/dist/docx/formatting/resolve.d.ts +45 -0
  40. package/dist/docx/formatting/resolve.js +140 -0
  41. package/dist/docx/formatting/runProperties.d.ts +93 -0
  42. package/dist/docx/formatting/runProperties.js +316 -0
  43. package/dist/docx/formatting/styles.js +3 -3
  44. package/dist/docx/formatting/tabStops.js +7 -22
  45. package/dist/docx/formatting.d.ts +4 -1
  46. package/dist/docx/formatting.js +4 -1
  47. package/dist/docx/headersFooters.js +13 -22
  48. package/dist/docx/identities.d.ts +67 -0
  49. package/dist/docx/identities.js +174 -0
  50. package/dist/docx/importDocx.d.ts +12 -1
  51. package/dist/docx/importDocx.js +72 -79
  52. package/dist/docx/importParagraph.d.ts +1 -1
  53. package/dist/docx/importParagraph.js +4 -3
  54. package/dist/docx/importTable.d.ts +1 -1
  55. package/dist/docx/importTable.js +17 -1
  56. package/dist/docx/invariants.d.ts +33 -0
  57. package/dist/docx/invariants.js +256 -0
  58. package/dist/docx/media.d.ts +6 -4
  59. package/dist/docx/media.js +6 -37
  60. package/dist/docx/newLists.d.ts +20 -0
  61. package/dist/docx/newLists.js +36 -0
  62. package/dist/docx/notes.js +6 -12
  63. package/dist/docx/numberingPlanner.d.ts +8 -0
  64. package/dist/docx/numberingPlanner.js +19 -0
  65. package/dist/docx/packageParts.d.ts +42 -0
  66. package/dist/docx/packageParts.js +135 -0
  67. package/dist/docx/pageGeometry.d.ts +2 -0
  68. package/dist/docx/pageGeometry.js +18 -9
  69. package/dist/docx/paraProps.d.ts +10 -22
  70. package/dist/docx/paraProps.js +75 -99
  71. package/dist/docx/partPlan.d.ts +36 -0
  72. package/dist/docx/partPlan.js +59 -0
  73. package/dist/docx/protectionPolicy.d.ts +127 -0
  74. package/dist/docx/protectionPolicy.js +169 -0
  75. package/dist/docx/relationships.d.ts +1 -1
  76. package/dist/docx/relationships.js +15 -15
  77. package/dist/docx/runProps.d.ts +9 -22
  78. package/dist/docx/runProps.js +15 -168
  79. package/dist/docx/scan.js +20 -52
  80. package/dist/docx/sdt.js +14 -37
  81. package/dist/docx/sdtProps.d.ts +8 -1
  82. package/dist/docx/sdtProps.js +10 -0
  83. package/dist/docx/serializeBlock.d.ts +2 -0
  84. package/dist/docx/serializeBlock.js +9 -7
  85. package/dist/docx/serializeParagraph.d.ts +2 -1
  86. package/dist/docx/serializeParagraph.js +25 -14
  87. package/dist/docx/serializeTable.js +63 -32
  88. package/dist/docx/session.d.ts +42 -14
  89. package/dist/docx/session.js +43 -17
  90. package/dist/docx/storyProjection.d.ts +36 -0
  91. package/dist/docx/storyProjection.js +23 -0
  92. package/dist/docx/tableFormatting/editing.js +130 -185
  93. package/dist/docx/tableFormatting/reading.d.ts +16 -6
  94. package/dist/docx/tableFormatting/reading.js +40 -20
  95. package/dist/docx/tableTemplate.js +37 -5
  96. package/dist/docx/theme.d.ts +0 -6
  97. package/dist/docx/theme.js +0 -8
  98. package/dist/download.d.ts +8 -5
  99. package/dist/download.js +2 -0
  100. package/dist/editor/clipboard/images.js +17 -3
  101. package/dist/editor/clipboard/inlineFormatting.d.ts +7 -0
  102. package/dist/editor/clipboard/inlineFormatting.js +20 -30
  103. package/dist/editor/commands/breakCommands.js +4 -3
  104. package/dist/editor/commands/canRunCommand.d.ts +2 -2
  105. package/dist/editor/commands/canRunCommand.js +1 -1
  106. package/dist/editor/commands/comments/editing.d.ts +6 -2
  107. package/dist/editor/commands/comments/editing.js +21 -36
  108. package/dist/editor/commands/exportQueries.d.ts +15 -0
  109. package/dist/editor/commands/exportQueries.js +14 -0
  110. package/dist/editor/commands/fidelityQueries.d.ts +12 -0
  111. package/dist/editor/commands/fidelityQueries.js +8 -0
  112. package/dist/editor/commands/formatting/editing.d.ts +2 -2
  113. package/dist/editor/commands/formatting/editing.js +26 -108
  114. package/dist/editor/commands/formatting/propertyCommands.d.ts +21 -0
  115. package/dist/editor/commands/formatting/propertyCommands.js +103 -0
  116. package/dist/editor/commands/formatting/shared.d.ts +3 -3
  117. package/dist/editor/commands/formatting/shared.js +5 -2
  118. package/dist/editor/commands/historyCommands.js +2 -1
  119. package/dist/editor/commands/indentCommands.js +5 -4
  120. package/dist/editor/commands/index.d.ts +11 -0
  121. package/dist/editor/commands/index.js +5 -0
  122. package/dist/editor/commands/linkCommands.js +5 -6
  123. package/dist/editor/commands/listCommands.js +8 -10
  124. package/dist/editor/commands/lockCommands.d.ts +7 -1
  125. package/dist/editor/commands/lockCommands.js +35 -37
  126. package/dist/editor/commands/paragraphCommands.js +28 -31
  127. package/dist/editor/commands/spacingCommands.js +1 -1
  128. package/dist/editor/commands/tabCommands.js +4 -3
  129. package/dist/editor/createEditor.d.ts +17 -29
  130. package/dist/editor/createEditor.js +61 -67
  131. package/dist/editor/documentStyles.d.ts +11 -35
  132. package/dist/editor/documentStyles.js +9 -52
  133. package/dist/editor/editorDocument.d.ts +56 -0
  134. package/dist/editor/editorDocument.js +74 -0
  135. package/dist/editor/externalClipboard.js +166 -33
  136. package/dist/editor/insertImage.js +4 -3
  137. package/dist/editor/insertTable.js +4 -3
  138. package/dist/editor/paragraphEdits.d.ts +11 -18
  139. package/dist/editor/paragraphEdits.js +7 -16
  140. package/dist/editor/plugins/displayDerivation.d.ts +47 -0
  141. package/dist/editor/plugins/displayDerivation.js +78 -0
  142. package/dist/editor/plugins/documentProtection.d.ts +3 -3
  143. package/dist/editor/plugins/imagePaste.js +4 -3
  144. package/dist/editor/plugins/keymap.js +18 -3
  145. package/dist/editor/plugins/lockedContent.d.ts +4 -4
  146. package/dist/editor/plugins/lockedContent.js +1 -1
  147. package/dist/editor/plugins/numberingDecorations.d.ts +5 -11
  148. package/dist/editor/plugins/numberingDecorations.js +7 -17
  149. package/dist/editor/plugins/paragraphDisplay.d.ts +7 -0
  150. package/dist/editor/plugins/paragraphDisplay.js +51 -0
  151. package/dist/editor/plugins/tabLayout.js +1 -1
  152. package/dist/editor/plugins/tableDisplay.d.ts +6 -0
  153. package/dist/editor/plugins/tableDisplay.js +16 -0
  154. package/dist/index.d.ts +2 -0
  155. package/dist/model/format.d.ts +30 -8
  156. package/dist/model/format.js +35 -22
  157. package/dist/model/tabStops.d.ts +9 -0
  158. package/dist/model/tabStops.js +18 -0
  159. package/dist/numbering/listTemplate.js +49 -10
  160. package/dist/numbering/parseNumbering.d.ts +14 -1
  161. package/dist/numbering/parseNumbering.js +35 -22
  162. package/dist/numbering/writeNumbering.d.ts +3 -4
  163. package/dist/numbering/writeNumbering.js +14 -25
  164. package/dist/ooxml/childOrder.d.ts +34 -0
  165. package/dist/ooxml/childOrder.js +496 -0
  166. package/dist/ooxml/element.d.ts +65 -0
  167. package/dist/ooxml/element.js +68 -0
  168. package/dist/ooxml/errors.d.ts +3 -2
  169. package/dist/ooxml/fragment.d.ts +53 -0
  170. package/dist/ooxml/fragment.js +76 -0
  171. package/dist/ooxml/image.d.ts +4 -0
  172. package/dist/ooxml/image.js +3 -5
  173. package/dist/ooxml/names.d.ts +27 -0
  174. package/dist/ooxml/names.js +29 -0
  175. package/dist/ooxml/partSplice.d.ts +67 -0
  176. package/dist/ooxml/partSplice.js +169 -0
  177. package/dist/ooxml/precedence.d.ts +31 -0
  178. package/dist/ooxml/precedence.js +51 -0
  179. package/dist/ooxml/props.d.ts +112 -0
  180. package/dist/ooxml/props.js +214 -0
  181. package/dist/ooxml/simpleTypes.d.ts +103 -0
  182. package/dist/ooxml/simpleTypes.js +182 -0
  183. package/dist/ooxml/tabStops.js +8 -27
  184. package/dist/ooxml/tagScan.d.ts +34 -0
  185. package/dist/ooxml/tagScan.js +108 -0
  186. package/dist/ooxml/units.d.ts +25 -13
  187. package/dist/ooxml/units.js +65 -31
  188. package/dist/ooxml/xml.d.ts +52 -0
  189. package/dist/ooxml/xml.js +64 -5
  190. package/dist/page/blockKinds.d.ts +103 -0
  191. package/dist/page/blockKinds.js +11 -0
  192. package/dist/page/kinds/index.d.ts +6 -0
  193. package/dist/page/kinds/index.js +10 -0
  194. package/dist/page/kinds/paragraphKind.d.ts +10 -0
  195. package/dist/page/kinds/paragraphKind.js +71 -0
  196. package/dist/page/kinds/tableKind.d.ts +10 -0
  197. package/dist/page/kinds/tableKind.js +178 -0
  198. package/dist/page/measureBlocks.d.ts +3 -1
  199. package/dist/page/measureBlocks.js +24 -25
  200. package/dist/page/pageDecorations.d.ts +27 -28
  201. package/dist/page/pageDecorations.js +70 -151
  202. package/dist/page/pageLayout.d.ts +10 -62
  203. package/dist/page/pageLayout.js +60 -53
  204. package/dist/page/usePageLayout.d.ts +2 -2
  205. package/dist/page/usePageLayout.js +3 -11
  206. package/dist/schema/attrRoles.d.ts +42 -0
  207. package/dist/schema/attrRoles.js +141 -0
  208. package/dist/schema/displayDerivation.d.ts +82 -0
  209. package/dist/schema/displayDerivation.js +130 -0
  210. package/dist/schema/docxSchema.d.ts +9 -0
  211. package/dist/schema/docxSchema.js +237 -114
  212. package/dist/schema/editGuard.d.ts +99 -0
  213. package/dist/schema/editGuard.js +43 -0
  214. package/dist/schema/guards.d.ts +91 -0
  215. package/dist/schema/guards.js +108 -0
  216. package/dist/schema/index.d.ts +2 -0
  217. package/dist/schema/index.js +2 -0
  218. package/dist/schema/locks.d.ts +17 -77
  219. package/dist/schema/locks.js +26 -70
  220. package/dist/schema/preservedGuards.d.ts +50 -0
  221. package/dist/schema/preservedGuards.js +80 -0
  222. package/dist/schema/protection.d.ts +17 -4
  223. package/dist/schema/protection.js +4 -2
  224. package/dist/schema/sourceEquality.d.ts +19 -0
  225. package/dist/schema/sourceEquality.js +39 -0
  226. package/dist/styles/inlineStyle.js +15 -6
  227. package/dist/table/cellFormatting.d.ts +8 -1
  228. package/dist/table/cellFormatting.js +10 -17
  229. package/dist/table/commands.d.ts +2 -2
  230. package/dist/table/commands.js +20 -18
  231. package/dist/table/format.d.ts +3 -0
  232. package/dist/table/format.js +3 -9
  233. package/dist/table/gridBorders.d.ts +14 -16
  234. package/dist/table/gridBorders.js +3 -41
  235. package/dist/table/index.d.ts +2 -2
  236. package/dist/table/merge.d.ts +4 -8
  237. package/dist/table/merge.js +5 -5
  238. package/package.json +10 -3
  239. package/dist/docx/comments/contentTypes.d.ts +0 -7
  240. package/dist/docx/comments/contentTypes.js +0 -38
  241. package/dist/docx/formatting/effectiveParagraph.d.ts +0 -15
  242. package/dist/docx/formatting/effectiveParagraph.js +0 -81
  243. package/dist/docx/propsXml.d.ts +0 -49
  244. package/dist/docx/propsXml.js +0 -302
  245. package/dist/docx/uniqueControls.d.ts +0 -14
  246. package/dist/docx/uniqueControls.js +0 -62
  247. package/dist/editor/plugins/bookmarkProtection.d.ts +0 -3
  248. package/dist/editor/plugins/bookmarkProtection.js +0 -28
  249. package/dist/editor/plugins/commentReservations.d.ts +0 -5
  250. package/dist/editor/plugins/commentReservations.js +0 -26
  251. package/dist/editor/plugins/noteProtection.d.ts +0 -3
  252. package/dist/editor/plugins/noteProtection.js +0 -25
  253. package/dist/editor/plugins/styledParagraphs.d.ts +0 -15
  254. package/dist/editor/plugins/styledParagraphs.js +0 -65
  255. package/dist/page/tableMeasurements.d.ts +0 -10
  256. package/dist/page/tableMeasurements.js +0 -115
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,12 +1,14 @@
1
1
  // src/docx/sdt.ts
2
- import { wAttr } from "../ooxml/units.js";
3
- import { attrString, elementChildren, serializeXml } from "../ooxml/xml.js";
2
+ import { elementXml, openTagXml } from "../ooxml/element.js";
3
+ import { wName } from "../ooxml/names.js";
4
4
  import {
5
5
  parseProps,
6
6
  propsChild,
7
- renderProps,
8
- setPropsChild
9
- } 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";
10
12
  var CONTENTS_LOCKED = ["contentLocked", "sdtContentLocked"];
11
13
  var DELETION_LOCKED = ["sdtLocked", "sdtContentLocked"];
12
14
  function lockValue(sdtPr) {
@@ -31,30 +33,18 @@ function readSdtWrapper(el) {
31
33
  const attrs = attrString(el);
32
34
  const val = lockValue(sdtPr);
33
35
  return {
34
- prefix: (attrs ? `<w:sdt ${attrs}>` : "<w:sdt>") + head.map(serializeXml).join(""),
36
+ prefix: openTagXml(wName("sdt"), attrs) + head.map(serializeXml).join(""),
35
37
  content,
36
38
  contentsLocked: val !== null && CONTENTS_LOCKED.includes(val),
37
39
  deletionLocked: val !== null && DELETION_LOCKED.includes(val)
38
40
  };
39
41
  }
40
- var SDT_ORDER = ["sdtPr", "sdtEndPr", "sdtContent"];
41
- var SDT_PR_ORDER = [
42
- "rPr",
43
- "alias",
44
- "tag",
45
- "id",
46
- "lock",
47
- "placeholder",
48
- "showingPlcHdr",
49
- "dataBinding",
50
- "temporary"
51
- ];
52
42
  function newControlId() {
53
43
  return Math.floor(Math.random() * 2147483647);
54
44
  }
55
45
  function renderPrefix(props) {
56
- const open = props.attrs ? `<${props.tag} ${props.attrs}>` : `<${props.tag}>`;
57
- return open + props.children.map((child) => child.xml).join("");
46
+ const inner = props.children.map((child) => (child.before ?? "") + child.xml).join("");
47
+ return openTagXml(props.tag, props.attrs) + inner + (props.tail ?? "");
58
48
  }
59
49
  var NAMES_NOTHING = ["id", "lock"];
60
50
  function namesNothing(prefix) {
@@ -65,32 +55,19 @@ function namesNothing(prefix) {
65
55
  if (!props || sdt.children.length !== 1) return false;
66
56
  return props.children.every((child) => NAMES_NOTHING.includes(child.name));
67
57
  }
68
- function emptyProps(props) {
69
- return props.attrs ? `<${props.tag} ${props.attrs}/>` : `<${props.tag}/>`;
70
- }
71
58
  function editSdtPrefix(prefix, edits) {
72
59
  const sdt = parseProps(`${prefix}</w:sdt>`);
73
60
  const sdtPr = sdt && propsChild(sdt.children, "sdtPr");
74
61
  const props = sdtPr ? parseProps(sdtPr.xml) : null;
75
62
  if (!sdt || !props) return null;
76
- const children = edits.reduce(
77
- (kept, [name, xml]) => setPropsChild(kept, name, xml, SDT_PR_ORDER),
78
- props.children
63
+ const rendered = renderElement(
64
+ edits.reduce((kept, [name, xml]) => setChild(kept, name, xml), props)
79
65
  );
80
- const rendered = renderProps({ ...props, children });
81
- return renderPrefix({
82
- ...sdt,
83
- children: setPropsChild(
84
- sdt.children,
85
- "sdtPr",
86
- rendered === "" ? emptyProps(props) : rendered,
87
- SDT_ORDER
88
- )
89
- });
66
+ return renderPrefix(setChild(sdt, "sdtPr", rendered));
90
67
  }
91
68
  function copiedControlPrefix(prefix, id) {
92
69
  const copied = editSdtPrefix(prefix, [
93
- ["id", `<w:id w:val="${id}"/>`],
70
+ ["id", elementXml(wName("id"), [[wName("val"), `${id}`]])],
94
71
  ["dataBinding", null]
95
72
  ]);
96
73
  return copied ?? 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
  }
@@ -18,13 +20,13 @@ function serializeBlock(node, session, refs = NO_EXPORT_REFS) {
18
20
  if (node.type.name === "paragraph") return serializeParagraph(node, refs);
19
21
  if (node.type.name === "table") return serializeTable(node, refs);
20
22
  if (node.type.name === "rawBlock") return preservedXml(node);
21
- if (node.type.name === "bookmarkBlock") return serializeRaw(node, session);
22
- if (node.type.name === "docxRaw") return serializeRaw(node, session);
23
+ if (node.type.isInGroup("preserved")) return serializeRaw(node, session);
23
24
  throw new DocxExportError(
24
25
  "unsupported-content",
25
26
  `block we cannot serialize: ${node.type.name}`
26
27
  );
27
28
  }
28
29
  export {
30
+ lostOriginal,
29
31
  serializeBlock
30
32
  };
@@ -10,6 +10,7 @@
10
10
  */
11
11
  import type { Node as PMNode } from "prosemirror-model";
12
12
  import { type ExportRefs } from "./exportRefs";
13
- export declare function openTag(name: string, attrs: unknown): string;
13
+ /** The attribute text a node attr holds, which is either what the original wrote or nothing */
14
+ export declare function rawAttrsOf(value: unknown): string | null;
14
15
  export declare function preservedXml(node: PMNode): string;
15
16
  export declare function serializeParagraph(node: PMNode, refs?: ExportRefs): string;
@@ -1,4 +1,5 @@
1
1
  // src/docx/serializeParagraph.ts
2
+ import { elementXml, emptyTagXml, openTagXml } from "../ooxml/element.js";
2
3
  import { DocxExportError } from "../ooxml/errors.js";
3
4
  import {
4
5
  imageDrawingXml,
@@ -6,6 +7,7 @@ import {
6
7
  toImageSrc,
7
8
  withExtent
8
9
  } from "../ooxml/image.js";
10
+ import { wName } from "../ooxml/names.js";
9
11
  import { escapeXml } from "../ooxml/xml.js";
10
12
  import { NO_EXPORT_REFS } from "./exportRefs.js";
11
13
  import { relIdIn, withRelId } from "./hyperlink.js";
@@ -16,11 +18,8 @@ function sameMark(a, b) {
16
18
  function markOf(node, name) {
17
19
  return node.marks.find((mark) => mark.type.name === name) ?? null;
18
20
  }
19
- function openTag(name, attrs) {
20
- return typeof attrs === "string" ? `<${name} ${attrs}>` : `<${name}>`;
21
- }
22
- function emptyTag(name, attrs) {
23
- return typeof attrs === "string" ? `<${name} ${attrs}/>` : `<${name}/>`;
21
+ function rawAttrsOf(value) {
22
+ return typeof value === "string" ? value : null;
24
23
  }
25
24
  function renderImage(node, images) {
26
25
  const extent = toImageExtent(node.attrs.extent);
@@ -51,19 +50,28 @@ function renderInline(node, images) {
51
50
  if (node.isText) {
52
51
  const tab = markOf(node, "tab");
53
52
  return (node.text ?? "").split(" ").flatMap((text, index, pieces) => {
54
- const rendered = text ? [`<w:t xml:space="preserve">${escapeXml(text)}</w:t>`] : [];
55
- return index < pieces.length - 1 ? [...rendered, emptyTag("w:tab", tab?.attrs.tabAttrs)] : rendered;
53
+ const rendered = text ? [
54
+ elementXml(
55
+ wName("t"),
56
+ [["xml:space", "preserve"]],
57
+ [escapeXml(text)]
58
+ )
59
+ ] : [];
60
+ return index < pieces.length - 1 ? [
61
+ ...rendered,
62
+ emptyTagXml(wName("tab"), rawAttrsOf(tab?.attrs.tabAttrs))
63
+ ] : rendered;
56
64
  }).join("");
57
65
  }
58
66
  if (node.type.name === "hardBreak")
59
- return emptyTag("w:br", node.attrs.brAttrs);
67
+ return emptyTagXml(wName("br"), rawAttrsOf(node.attrs.brAttrs));
60
68
  if (node.type.name === "image") return renderImage(node, images);
61
69
  if (node.type.name === "commentReference") {
62
70
  const original = node.attrs.referenceXml;
63
71
  if (typeof original === "string") return original;
64
72
  const id = node.attrs.id;
65
73
  if (typeof id === "string") {
66
- return `<w:commentReference w:id="${escapeXml(id)}"/>`;
74
+ return elementXml(wName("commentReference"), [[wName("id"), id]]);
67
75
  }
68
76
  throw new DocxExportError(
69
77
  "lost-original",
@@ -76,7 +84,7 @@ function renderInline(node, images) {
76
84
  const id = node.attrs.id;
77
85
  const name = node.attrs.kind === "endnote" ? "endnoteReference" : "footnoteReference";
78
86
  if (typeof id === "string") {
79
- return `<w:${name} w:id="${escapeXml(id)}"/>`;
87
+ return elementXml(wName(name), [[wName("id"), id]]);
80
88
  }
81
89
  throw new DocxExportError(
82
90
  "lost-original",
@@ -117,7 +125,10 @@ function addInline(parts, child, images) {
117
125
  );
118
126
  }
119
127
  const name = child.type.name === "commentStart" ? "commentRangeStart" : "commentRangeEnd";
120
- parts.push({ kind: "raw", xml: `<w:${name} w:id="${escapeXml(id)}"/>` });
128
+ parts.push({
129
+ kind: "raw",
130
+ xml: elementXml(wName(name), [[wName("id"), id]])
131
+ });
121
132
  return;
122
133
  }
123
134
  const piece = renderInline(child, images);
@@ -150,7 +161,7 @@ function splitParagraphGroups(paragraph, refs) {
150
161
  }
151
162
  function renderParagraphPart(part) {
152
163
  if (part.kind === "raw") return part.xml;
153
- const open = openTag("w:r", part.mark?.attrs.rAttrs);
164
+ const open = openTagXml(wName("r"), rawAttrsOf(part.mark?.attrs.rAttrs));
154
165
  const rPr = part.mark?.attrs.rPr;
155
166
  return open + (typeof rPr === "string" ? rPr : "") + part.pieces.join("") + "</w:r>";
156
167
  }
@@ -196,13 +207,13 @@ function renderParagraphGroup(group, refs) {
196
207
  return `${prefix}<w:sdtContent>${body}</w:sdtContent></w:sdt>`;
197
208
  }
198
209
  function serializeParagraph(node, refs = NO_EXPORT_REFS) {
199
- const open = openTag("w:p", node.attrs.pAttrs);
210
+ const open = openTagXml(wName("p"), rawAttrsOf(node.attrs.pAttrs));
200
211
  const pPr = node.attrs.pPr;
201
212
  const body = splitParagraphGroups(node, refs).map((group) => renderParagraphGroup(group, refs)).join("");
202
213
  return open + (typeof pPr === "string" ? pPr : "") + body + "</w:p>";
203
214
  }
204
215
  export {
205
- openTag,
206
216
  preservedXml,
217
+ rawAttrsOf,
207
218
  serializeParagraph
208
219
  };
@@ -5,18 +5,21 @@ import {
5
5
  toTableWidth,
6
6
  widthNumber
7
7
  } from "../model/format.js";
8
+ import { elementXml, openTagXml } from "../ooxml/element.js";
8
9
  import { DocxExportError } from "../ooxml/errors.js";
9
- import { NO_EXPORT_REFS } from "./exportRefs.js";
10
+ import { wName } from "../ooxml/names.js";
10
11
  import {
12
+ innerXml,
11
13
  parseProps,
14
+ propsChild,
12
15
  renderProps,
13
- setPropsChild,
14
- TBL_PR_ORDER,
15
- TC_PR_ORDER
16
- } 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";
17
20
  import {
18
- openTag,
19
21
  preservedXml,
22
+ rawAttrsOf,
20
23
  serializeParagraph
21
24
  } from "./serializeParagraph.js";
22
25
  function propsOf(xml, tag) {
@@ -27,44 +30,72 @@ function propsOf(xml, tag) {
27
30
  }
28
31
  return parsed;
29
32
  }
30
- function widthXml(name, width) {
31
- return `<w:${name} w:w="${widthNumber(width) ?? 0}" w:type="${width.type}"/>`;
33
+ function modelled(name, attrs, replacing) {
34
+ const inner = replacing === void 0 ? "" : innerXml(replacing);
35
+ return elementXml(wName(name), attrs, inner === "" ? [] : [inner]);
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
+ }
45
+ function widthXml(name, width, replacing) {
46
+ return modelled(
47
+ name,
48
+ [
49
+ [wName("w"), widthText(width)],
50
+ [wName("type"), width.type]
51
+ ],
52
+ replacing
53
+ );
32
54
  }
33
- function withWidth(props, name, width, order) {
55
+ function withWidth(props, name, width) {
34
56
  if (!width) return props;
35
- return {
36
- ...props,
37
- children: setPropsChild(props.children, name, widthXml(name, width), order)
38
- };
57
+ const replacing = propsChild(props.children, name)?.xml;
58
+ return setChild(props, name, widthXml(name, width, replacing));
39
59
  }
40
60
  function tablePropsXml(table) {
41
61
  const props = propsOf(table.attrs.tblPr, "w:tblPr");
42
62
  const width = toTableWidth(table.attrs.tblW);
43
- return renderProps(withWidth(props, "tblW", width, TBL_PR_ORDER));
63
+ return renderProps(withWidth(props, "tblW", width)) || elementXml(wName("tblPr"), []);
44
64
  }
45
65
  function tableGridXml(table) {
46
66
  const gridCols = toGridCols(table.attrs.gridCols);
47
- if (gridCols.length === 0) return "";
48
- const cols = gridCols.map((w) => `<w:gridCol w:w="${w}"/>`).join("");
49
- return `<w:tblGrid>${cols}</w:tblGrid>`;
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]);
50
80
  }
51
81
  function cellPropsXml(cell, role) {
52
82
  const props = propsOf(cell.attrs.tcPr, "w:tcPr");
53
83
  const colspan = spanCount(cell.attrs.colspan);
54
84
  const rowspan = spanCount(cell.attrs.rowspan);
55
- const gridSpan = colspan > 1 ? `<w:gridSpan w:val="${colspan}"/>` : null;
56
- const vMerge = role === "continue" ? "<w:vMerge/>" : rowspan > 1 ? '<w:vMerge w:val="restart"/>' : null;
57
- let children = setPropsChild(
58
- props.children,
85
+ const gridSpan = colspan > 1 ? modelled(
59
86
  "gridSpan",
60
- gridSpan,
61
- TC_PR_ORDER
62
- );
63
- children = setPropsChild(children, "vMerge", vMerge, TC_PR_ORDER);
87
+ [[wName("val"), `${colspan}`]],
88
+ propsChild(props.children, "gridSpan")?.xml
89
+ ) : null;
90
+ const vMerge = role === "continue" ? elementXml(wName("vMerge"), []) : rowspan > 1 ? modelled(
91
+ "vMerge",
92
+ [[wName("val"), "restart"]],
93
+ propsChild(props.children, "vMerge")?.xml
94
+ ) : null;
95
+ const spanned = setChild(props, "gridSpan", gridSpan);
96
+ const merged = setChild(spanned, "vMerge", vMerge);
64
97
  const width = toTableWidth(cell.attrs.tcW);
65
- return renderProps(
66
- withWidth({ ...props, children }, "tcW", width, TC_PR_ORDER)
67
- );
98
+ return renderProps(withWidth(merged, "tcW", width));
68
99
  }
69
100
  function cellBlockXml(block, refs) {
70
101
  if (block.type.name === "paragraph") return serializeParagraph(block, refs);
@@ -81,8 +112,8 @@ function wrapInSdt(xml, cell, role) {
81
112
  return `${prefix}<w:sdtContent>${xml}</w:sdtContent></w:sdt>`;
82
113
  }
83
114
  function cellXml(cell, role, refs) {
84
- const body = role === "continue" ? "<w:p/>" : cell.children.map((block) => cellBlockXml(block, refs)).join("");
85
- const xml = openTag("w:tc", cell.attrs.tcAttrs) + cellPropsXml(cell, role) + body + "</w:tc>";
115
+ const body = role === "continue" ? elementXml(wName("p"), []) : cell.children.map((block) => cellBlockXml(block, refs)).join("");
116
+ const xml = openTagXml(wName("tc"), rawAttrsOf(cell.attrs.tcAttrs)) + cellPropsXml(cell, role) + body + "</w:tc>";
86
117
  return wrapInSdt(xml, cell, role);
87
118
  }
88
119
  function placeRow(row, covering) {
@@ -116,7 +147,7 @@ function rowXml(row, covering, refs) {
116
147
  const cells = placeRow(row, covering).map((placed) => cellXml(placed.cell, placed.role, refs)).join("");
117
148
  const tblPrEx = row.attrs.tblPrEx;
118
149
  const trPr = row.attrs.trPr;
119
- return openTag("w:tr", row.attrs.trAttrs) + (typeof tblPrEx === "string" ? tblPrEx : "") + (typeof trPr === "string" ? trPr : "") + cells + "</w:tr>";
150
+ return openTagXml(wName("tr"), rawAttrsOf(row.attrs.trAttrs)) + (typeof tblPrEx === "string" ? tblPrEx : "") + (typeof trPr === "string" ? trPr : "") + cells + "</w:tr>";
120
151
  }
121
152
  function serializeTable(table, refs = NO_EXPORT_REFS) {
122
153
  const covering = [];
@@ -127,7 +158,7 @@ function serializeTable(table, refs = NO_EXPORT_REFS) {
127
158
  "a vertical merge in the table reaches past the last row"
128
159
  );
129
160
  }
130
- return openTag("w:tbl", table.attrs.tblAttrs) + tablePropsXml(table) + tableGridXml(table) + rows + "</w:tbl>";
161
+ return openTagXml(wName("tbl"), rawAttrsOf(table.attrs.tblAttrs)) + tablePropsXml(table) + tableGridXml(table) + rows + "</w:tbl>";
131
162
  }
132
163
  export {
133
164
  serializeTable
@@ -9,9 +9,9 @@
9
9
  */
10
10
  import type { Node as PMNode } from "prosemirror-model";
11
11
  import type { DocumentDefaults } from "../model/format";
12
- import { type Numbering } from "../numbering/parseNumbering";
12
+ import { type Numbering, type NumberingOptions } from "../numbering/parseNumbering";
13
13
  import type { ImportedComments } from "./comments";
14
- import type { ParagraphFormatLayer, ParagraphStyleOption, StyleTable } from "./formatting";
14
+ import type { FormattingContext, ParagraphStyleOption } from "./formatting";
15
15
  import type { HeadersFooters } from "./headersFooters";
16
16
  import type { PageGeometry } from "./pageGeometry";
17
17
  export interface ImportedBlock {
@@ -28,9 +28,23 @@ export interface ImportedBlock {
28
28
  export interface DocxSession {
29
29
  readonly kind: "docxSession";
30
30
  }
31
+ /** What names one open document, which a block key is written against */
32
+ export interface SessionIdentity {
33
+ readonly sessionId: string;
34
+ }
35
+ /**
36
+ * A name for one open document.
37
+ *
38
+ * Opening the same bytes twice opens two documents, so this is drawn fresh each time rather than
39
+ * worked out from what was opened. It carries no `:`, which is what lets a block key be read back
40
+ * however many colons the story it names holds.
41
+ */
42
+ export declare function newSessionId(): string;
31
43
  /** What the package's own layers read the open document off */
32
- export declare class SessionStore implements DocxSession {
44
+ export declare class SessionStore implements DocxSession, SessionIdentity {
33
45
  readonly kind = "docxSession";
46
+ /** Which open document this is. Every block key of it names it, so a block of another document is not taken for one of this one */
47
+ readonly sessionId: string;
34
48
  readonly parts: Map<string, Uint8Array>;
35
49
  readonly mainPartPath: string;
36
50
  readonly documentPrefix: string;
@@ -41,19 +55,12 @@ export declare class SessionStore implements DocxSession {
41
55
  readonly defaults: DocumentDefaults;
42
56
  /** The effective automatic tab interval read from settings.xml. Used for display only. */
43
57
  readonly defaultTabStopPt: number;
44
- /** The paragraph properties at the base of the OOXML formatting hierarchy. */
45
- readonly paragraphDefaults: ParagraphFormatLayer;
46
58
  /** The paper this document is written on, read from the first section. Used for display only: the `w:sectPr` itself goes back out in the preserved tail */
47
59
  readonly geometry: PageGeometry;
48
- /** The style chain from styles.xml. Used only to fold the style a paragraph points at into its displayed values */
49
- readonly styles: StyleTable;
60
+ /** Everything the display values of a paragraph or a run are resolved against: the style chain, the defaults, the list definitions */
61
+ readonly formatting: FormattingContext;
50
62
  /** The paragraph styles this document defines, in the order styles.xml lists them. What the style picker offers */
51
63
  readonly paragraphStyles: ParagraphStyleOption[];
52
- /**
53
- * The paragraph style every paragraph with no `w:pStyle` of its own wears (`w:default="1"`,
54
- * usually Normal). null when the document marks none
55
- */
56
- readonly defaultParagraphStyleId: string | null;
57
64
  /** The raw text of numbering.xml. Kept around so list numbers can be drawn on screen. null if there is none */
58
65
  readonly numberingXml: string | null;
59
66
  /** Where numbering.xml sits inside the zip. When a new list is exported it is rewritten at that spot. null if there is none */
@@ -76,6 +83,27 @@ export declare function sessionOf(session: DocxSession): SessionStore;
76
83
  /** Where the body part sits inside the package, which is the one part an export rewrites */
77
84
  export declare function documentPartPath(session: DocxSession): string;
78
85
  /** The list definitions the document carries, which is what a paragraph's numbering points into */
79
- export declare function documentNumbering(session: DocxSession): Numbering;
80
- /** Finds which original block a node came from. undefined for a node newly created during editing */
86
+ export declare function documentNumbering(session: DocxSession, options?: NumberingOptions): Numbering;
87
+ /** The story a block key names when the block stood in the main document body */
88
+ export declare const BODY_STORY_KEY = "body";
89
+ /** A block key read back: which open document the block came from, which story of it, and where in that story */
90
+ export interface BlockKey {
91
+ sessionId: string;
92
+ storyKey: string;
93
+ index: number;
94
+ }
95
+ /** Where a block stands, as the one string a node carries on its `srcId` */
96
+ export declare function blockKey(session: SessionIdentity, storyKey: string, index: number): string;
97
+ /**
98
+ * The three parts of a block key, or null for a string that is not one.
99
+ *
100
+ * A story key holds colons of its own (`comment:4`), so the key is read from both ends rather
101
+ * than split: the first segment is the document, the last is the place in the story, and
102
+ * everything between them is the story itself.
103
+ */
104
+ export declare function splitBlockKey(key: string): BlockKey | null;
105
+ /**
106
+ * Finds which original block a node came from. undefined for a node newly created during editing,
107
+ * and for one that came in from another document, whose original is in that file and not this one.
108
+ */
81
109
  export declare function originalBlock(node: PMNode, session: SessionStore): ImportedBlock | undefined;