@portone/docx-editor 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (256) hide show
  1. package/CHANGELOG.md +174 -0
  2. package/CONTRIBUTING.md +9 -1
  3. package/dist/DocxEditor.d.ts +6 -0
  4. package/dist/DocxEditor.js +12 -29
  5. package/dist/core.d.ts +23 -8
  6. package/dist/core.js +9 -4
  7. package/dist/docx/cloning.d.ts +38 -0
  8. package/dist/docx/cloning.js +74 -0
  9. package/dist/docx/commentOnlyChange.d.ts +14 -7
  10. package/dist/docx/commentOnlyChange.js +9 -139
  11. package/dist/docx/comments/constants.d.ts +3 -4
  12. package/dist/docx/comments/constants.js +4 -5
  13. package/dist/docx/comments/grammar.d.ts +86 -0
  14. package/dist/docx/comments/grammar.js +223 -0
  15. package/dist/docx/comments/parts.d.ts +52 -0
  16. package/dist/docx/comments/parts.js +279 -0
  17. package/dist/docx/comments/people.d.ts +11 -2
  18. package/dist/docx/comments/people.js +28 -106
  19. package/dist/docx/comments/policy.d.ts +7 -0
  20. package/dist/docx/comments/policy.js +43 -0
  21. package/dist/docx/comments/reading.d.ts +5 -3
  22. package/dist/docx/comments/reading.js +30 -29
  23. package/dist/docx/comments/writing.d.ts +19 -7
  24. package/dist/docx/comments/writing.js +105 -137
  25. package/dist/docx/documentSettings.d.ts +7 -0
  26. package/dist/docx/documentSettings.js +10 -2
  27. package/dist/docx/exportDocx.d.ts +40 -5
  28. package/dist/docx/exportDocx.js +71 -72
  29. package/dist/docx/exportRefs.d.ts +5 -2
  30. package/dist/docx/exportRefs.js +3 -1
  31. package/dist/docx/fidelity.d.ts +43 -0
  32. package/dist/docx/fidelity.js +64 -0
  33. package/dist/docx/formatting/attrs.d.ts +27 -0
  34. package/dist/docx/formatting/attrs.js +31 -0
  35. package/dist/docx/formatting/context.d.ts +31 -0
  36. package/dist/docx/formatting/context.js +42 -0
  37. package/dist/docx/formatting/direct.d.ts +6 -5
  38. package/dist/docx/formatting/direct.js +52 -81
  39. package/dist/docx/formatting/resolve.d.ts +45 -0
  40. package/dist/docx/formatting/resolve.js +140 -0
  41. package/dist/docx/formatting/runProperties.d.ts +93 -0
  42. package/dist/docx/formatting/runProperties.js +316 -0
  43. package/dist/docx/formatting/styles.js +3 -3
  44. package/dist/docx/formatting/tabStops.js +7 -22
  45. package/dist/docx/formatting.d.ts +4 -1
  46. package/dist/docx/formatting.js +4 -1
  47. package/dist/docx/headersFooters.js +13 -22
  48. package/dist/docx/identities.d.ts +67 -0
  49. package/dist/docx/identities.js +174 -0
  50. package/dist/docx/importDocx.d.ts +12 -1
  51. package/dist/docx/importDocx.js +72 -79
  52. package/dist/docx/importParagraph.d.ts +1 -1
  53. package/dist/docx/importParagraph.js +4 -3
  54. package/dist/docx/importTable.d.ts +1 -1
  55. package/dist/docx/importTable.js +17 -1
  56. package/dist/docx/invariants.d.ts +33 -0
  57. package/dist/docx/invariants.js +256 -0
  58. package/dist/docx/media.d.ts +6 -4
  59. package/dist/docx/media.js +6 -37
  60. package/dist/docx/newLists.d.ts +20 -0
  61. package/dist/docx/newLists.js +36 -0
  62. package/dist/docx/notes.js +6 -12
  63. package/dist/docx/numberingPlanner.d.ts +8 -0
  64. package/dist/docx/numberingPlanner.js +19 -0
  65. package/dist/docx/packageParts.d.ts +42 -0
  66. package/dist/docx/packageParts.js +135 -0
  67. package/dist/docx/pageGeometry.d.ts +2 -0
  68. package/dist/docx/pageGeometry.js +18 -9
  69. package/dist/docx/paraProps.d.ts +10 -22
  70. package/dist/docx/paraProps.js +75 -99
  71. package/dist/docx/partPlan.d.ts +36 -0
  72. package/dist/docx/partPlan.js +59 -0
  73. package/dist/docx/protectionPolicy.d.ts +127 -0
  74. package/dist/docx/protectionPolicy.js +169 -0
  75. package/dist/docx/relationships.d.ts +1 -1
  76. package/dist/docx/relationships.js +15 -15
  77. package/dist/docx/runProps.d.ts +9 -22
  78. package/dist/docx/runProps.js +15 -168
  79. package/dist/docx/scan.js +20 -52
  80. package/dist/docx/sdt.js +14 -37
  81. package/dist/docx/sdtProps.d.ts +8 -1
  82. package/dist/docx/sdtProps.js +10 -0
  83. package/dist/docx/serializeBlock.d.ts +2 -0
  84. package/dist/docx/serializeBlock.js +9 -7
  85. package/dist/docx/serializeParagraph.d.ts +2 -1
  86. package/dist/docx/serializeParagraph.js +25 -14
  87. package/dist/docx/serializeTable.js +63 -32
  88. package/dist/docx/session.d.ts +42 -14
  89. package/dist/docx/session.js +43 -17
  90. package/dist/docx/storyProjection.d.ts +36 -0
  91. package/dist/docx/storyProjection.js +23 -0
  92. package/dist/docx/tableFormatting/editing.js +130 -185
  93. package/dist/docx/tableFormatting/reading.d.ts +16 -6
  94. package/dist/docx/tableFormatting/reading.js +40 -20
  95. package/dist/docx/tableTemplate.js +37 -5
  96. package/dist/docx/theme.d.ts +0 -6
  97. package/dist/docx/theme.js +0 -8
  98. package/dist/download.d.ts +8 -5
  99. package/dist/download.js +2 -0
  100. package/dist/editor/clipboard/images.js +17 -3
  101. package/dist/editor/clipboard/inlineFormatting.d.ts +7 -0
  102. package/dist/editor/clipboard/inlineFormatting.js +20 -30
  103. package/dist/editor/commands/breakCommands.js +4 -3
  104. package/dist/editor/commands/canRunCommand.d.ts +2 -2
  105. package/dist/editor/commands/canRunCommand.js +1 -1
  106. package/dist/editor/commands/comments/editing.d.ts +6 -2
  107. package/dist/editor/commands/comments/editing.js +21 -36
  108. package/dist/editor/commands/exportQueries.d.ts +15 -0
  109. package/dist/editor/commands/exportQueries.js +14 -0
  110. package/dist/editor/commands/fidelityQueries.d.ts +12 -0
  111. package/dist/editor/commands/fidelityQueries.js +8 -0
  112. package/dist/editor/commands/formatting/editing.d.ts +2 -2
  113. package/dist/editor/commands/formatting/editing.js +26 -108
  114. package/dist/editor/commands/formatting/propertyCommands.d.ts +21 -0
  115. package/dist/editor/commands/formatting/propertyCommands.js +103 -0
  116. package/dist/editor/commands/formatting/shared.d.ts +3 -3
  117. package/dist/editor/commands/formatting/shared.js +5 -2
  118. package/dist/editor/commands/historyCommands.js +2 -1
  119. package/dist/editor/commands/indentCommands.js +5 -4
  120. package/dist/editor/commands/index.d.ts +11 -0
  121. package/dist/editor/commands/index.js +5 -0
  122. package/dist/editor/commands/linkCommands.js +5 -6
  123. package/dist/editor/commands/listCommands.js +8 -10
  124. package/dist/editor/commands/lockCommands.d.ts +7 -1
  125. package/dist/editor/commands/lockCommands.js +35 -37
  126. package/dist/editor/commands/paragraphCommands.js +28 -31
  127. package/dist/editor/commands/spacingCommands.js +1 -1
  128. package/dist/editor/commands/tabCommands.js +4 -3
  129. package/dist/editor/createEditor.d.ts +17 -29
  130. package/dist/editor/createEditor.js +61 -67
  131. package/dist/editor/documentStyles.d.ts +11 -35
  132. package/dist/editor/documentStyles.js +9 -52
  133. package/dist/editor/editorDocument.d.ts +56 -0
  134. package/dist/editor/editorDocument.js +74 -0
  135. package/dist/editor/externalClipboard.js +166 -33
  136. package/dist/editor/insertImage.js +4 -3
  137. package/dist/editor/insertTable.js +4 -3
  138. package/dist/editor/paragraphEdits.d.ts +11 -18
  139. package/dist/editor/paragraphEdits.js +7 -16
  140. package/dist/editor/plugins/displayDerivation.d.ts +47 -0
  141. package/dist/editor/plugins/displayDerivation.js +78 -0
  142. package/dist/editor/plugins/documentProtection.d.ts +3 -3
  143. package/dist/editor/plugins/imagePaste.js +4 -3
  144. package/dist/editor/plugins/keymap.js +18 -3
  145. package/dist/editor/plugins/lockedContent.d.ts +4 -4
  146. package/dist/editor/plugins/lockedContent.js +1 -1
  147. package/dist/editor/plugins/numberingDecorations.d.ts +5 -11
  148. package/dist/editor/plugins/numberingDecorations.js +7 -17
  149. package/dist/editor/plugins/paragraphDisplay.d.ts +7 -0
  150. package/dist/editor/plugins/paragraphDisplay.js +51 -0
  151. package/dist/editor/plugins/tabLayout.js +1 -1
  152. package/dist/editor/plugins/tableDisplay.d.ts +6 -0
  153. package/dist/editor/plugins/tableDisplay.js +16 -0
  154. package/dist/index.d.ts +2 -0
  155. package/dist/model/format.d.ts +30 -8
  156. package/dist/model/format.js +35 -22
  157. package/dist/model/tabStops.d.ts +9 -0
  158. package/dist/model/tabStops.js +18 -0
  159. package/dist/numbering/listTemplate.js +49 -10
  160. package/dist/numbering/parseNumbering.d.ts +14 -1
  161. package/dist/numbering/parseNumbering.js +35 -22
  162. package/dist/numbering/writeNumbering.d.ts +3 -4
  163. package/dist/numbering/writeNumbering.js +14 -25
  164. package/dist/ooxml/childOrder.d.ts +34 -0
  165. package/dist/ooxml/childOrder.js +496 -0
  166. package/dist/ooxml/element.d.ts +65 -0
  167. package/dist/ooxml/element.js +68 -0
  168. package/dist/ooxml/errors.d.ts +3 -2
  169. package/dist/ooxml/fragment.d.ts +53 -0
  170. package/dist/ooxml/fragment.js +76 -0
  171. package/dist/ooxml/image.d.ts +4 -0
  172. package/dist/ooxml/image.js +3 -5
  173. package/dist/ooxml/names.d.ts +27 -0
  174. package/dist/ooxml/names.js +29 -0
  175. package/dist/ooxml/partSplice.d.ts +67 -0
  176. package/dist/ooxml/partSplice.js +169 -0
  177. package/dist/ooxml/precedence.d.ts +31 -0
  178. package/dist/ooxml/precedence.js +51 -0
  179. package/dist/ooxml/props.d.ts +112 -0
  180. package/dist/ooxml/props.js +214 -0
  181. package/dist/ooxml/simpleTypes.d.ts +103 -0
  182. package/dist/ooxml/simpleTypes.js +182 -0
  183. package/dist/ooxml/tabStops.js +8 -27
  184. package/dist/ooxml/tagScan.d.ts +34 -0
  185. package/dist/ooxml/tagScan.js +108 -0
  186. package/dist/ooxml/units.d.ts +25 -13
  187. package/dist/ooxml/units.js +65 -31
  188. package/dist/ooxml/xml.d.ts +52 -0
  189. package/dist/ooxml/xml.js +64 -5
  190. package/dist/page/blockKinds.d.ts +103 -0
  191. package/dist/page/blockKinds.js +11 -0
  192. package/dist/page/kinds/index.d.ts +6 -0
  193. package/dist/page/kinds/index.js +10 -0
  194. package/dist/page/kinds/paragraphKind.d.ts +10 -0
  195. package/dist/page/kinds/paragraphKind.js +71 -0
  196. package/dist/page/kinds/tableKind.d.ts +10 -0
  197. package/dist/page/kinds/tableKind.js +178 -0
  198. package/dist/page/measureBlocks.d.ts +3 -1
  199. package/dist/page/measureBlocks.js +24 -25
  200. package/dist/page/pageDecorations.d.ts +27 -28
  201. package/dist/page/pageDecorations.js +70 -151
  202. package/dist/page/pageLayout.d.ts +10 -62
  203. package/dist/page/pageLayout.js +60 -53
  204. package/dist/page/usePageLayout.d.ts +2 -2
  205. package/dist/page/usePageLayout.js +3 -11
  206. package/dist/schema/attrRoles.d.ts +42 -0
  207. package/dist/schema/attrRoles.js +141 -0
  208. package/dist/schema/displayDerivation.d.ts +82 -0
  209. package/dist/schema/displayDerivation.js +130 -0
  210. package/dist/schema/docxSchema.d.ts +9 -0
  211. package/dist/schema/docxSchema.js +237 -114
  212. package/dist/schema/editGuard.d.ts +99 -0
  213. package/dist/schema/editGuard.js +43 -0
  214. package/dist/schema/guards.d.ts +91 -0
  215. package/dist/schema/guards.js +108 -0
  216. package/dist/schema/index.d.ts +2 -0
  217. package/dist/schema/index.js +2 -0
  218. package/dist/schema/locks.d.ts +17 -77
  219. package/dist/schema/locks.js +26 -70
  220. package/dist/schema/preservedGuards.d.ts +50 -0
  221. package/dist/schema/preservedGuards.js +80 -0
  222. package/dist/schema/protection.d.ts +17 -4
  223. package/dist/schema/protection.js +4 -2
  224. package/dist/schema/sourceEquality.d.ts +19 -0
  225. package/dist/schema/sourceEquality.js +39 -0
  226. package/dist/styles/inlineStyle.js +15 -6
  227. package/dist/table/cellFormatting.d.ts +8 -1
  228. package/dist/table/cellFormatting.js +10 -17
  229. package/dist/table/commands.d.ts +2 -2
  230. package/dist/table/commands.js +20 -18
  231. package/dist/table/format.d.ts +3 -0
  232. package/dist/table/format.js +3 -9
  233. package/dist/table/gridBorders.d.ts +14 -16
  234. package/dist/table/gridBorders.js +3 -41
  235. package/dist/table/index.d.ts +2 -2
  236. package/dist/table/merge.d.ts +4 -8
  237. package/dist/table/merge.js +5 -5
  238. package/package.json +10 -3
  239. package/dist/docx/comments/contentTypes.d.ts +0 -7
  240. package/dist/docx/comments/contentTypes.js +0 -38
  241. package/dist/docx/formatting/effectiveParagraph.d.ts +0 -15
  242. package/dist/docx/formatting/effectiveParagraph.js +0 -81
  243. package/dist/docx/propsXml.d.ts +0 -49
  244. package/dist/docx/propsXml.js +0 -302
  245. package/dist/docx/uniqueControls.d.ts +0 -14
  246. package/dist/docx/uniqueControls.js +0 -62
  247. package/dist/editor/plugins/bookmarkProtection.d.ts +0 -3
  248. package/dist/editor/plugins/bookmarkProtection.js +0 -28
  249. package/dist/editor/plugins/commentReservations.d.ts +0 -5
  250. package/dist/editor/plugins/commentReservations.js +0 -26
  251. package/dist/editor/plugins/noteProtection.d.ts +0 -3
  252. package/dist/editor/plugins/noteProtection.js +0 -25
  253. package/dist/editor/plugins/styledParagraphs.d.ts +0 -15
  254. package/dist/editor/plugins/styledParagraphs.js +0 -65
  255. package/dist/page/tableMeasurements.d.ts +0 -10
  256. package/dist/page/tableMeasurements.js +0 -115
@@ -0,0 +1,135 @@
1
+ // src/docx/packageParts.ts
2
+ import { elementXml } from "../ooxml/element.js";
3
+ import { DocxExportError } from "../ooxml/errors.js";
4
+ import { rootPrefixOf, splicePart } from "../ooxml/partSplice.js";
5
+ import {
6
+ decodeUtf8,
7
+ elementChildren,
8
+ encodeUtf8,
9
+ parseXml
10
+ } from "../ooxml/xml.js";
11
+ import {
12
+ directoryOf,
13
+ readRelationships,
14
+ relsPathOf,
15
+ resolveTarget
16
+ } from "./relationships.js";
17
+ var CONTENT_TYPES_PATH = "[Content_Types].xml";
18
+ function relatedPartPath(parts, mainPartPath, type) {
19
+ const relationship = readRelationships(parts, relsPathOf(mainPartPath)).find(
20
+ (entry) => entry.type === type && !entry.external
21
+ );
22
+ return relationship === void 0 ? null : resolveTarget(mainPartPath, relationship.target);
23
+ }
24
+ function readPart(parts, path) {
25
+ const bytes = path === null ? void 0 : parts.get(path);
26
+ return bytes ? decodeUtf8(bytes).text : null;
27
+ }
28
+ function availablePartPath(parts, mainPartPath, stem) {
29
+ const directory = directoryOf(mainPartPath);
30
+ const taken = new Set(Array.from(parts.keys(), (path) => path.toLowerCase()));
31
+ for (let suffix = 0; ; suffix += 1) {
32
+ const path = `${directory}${stem}${suffix === 0 ? "" : suffix + 1}.xml`;
33
+ if (!taken.has(path.toLowerCase())) return path;
34
+ }
35
+ }
36
+ function declaredIn(text) {
37
+ let root;
38
+ try {
39
+ root = parseXml(text).documentElement;
40
+ } catch (cause) {
41
+ throw new DocxExportError(
42
+ "malformed-xml",
43
+ `${CONTENT_TYPES_PATH} could not be parsed`,
44
+ { cause }
45
+ );
46
+ }
47
+ const defaults = /* @__PURE__ */ new Set();
48
+ const overrides = /* @__PURE__ */ new Set();
49
+ const xmlDefaults = /* @__PURE__ */ new Set();
50
+ const xmlOverrides = /* @__PURE__ */ new Set();
51
+ for (const el of elementChildren(root)) {
52
+ const extension = el.getAttribute("Extension");
53
+ const partName = el.getAttribute("PartName");
54
+ const contentType = (el.getAttribute("ContentType") ?? "").split(";")[0].trim().toLowerCase();
55
+ const xml = contentType === "application/xml" || contentType === "text/xml" || contentType.endsWith("+xml");
56
+ if (el.localName === "Default" && extension !== null) {
57
+ defaults.add(extension.toLowerCase());
58
+ if (xml) xmlDefaults.add(extension.toLowerCase());
59
+ }
60
+ if (el.localName === "Override" && partName !== null) {
61
+ overrides.add(partName.toLowerCase());
62
+ if (xml) xmlOverrides.add(partName.toLowerCase());
63
+ }
64
+ }
65
+ return { defaults, overrides, xmlDefaults, xmlOverrides };
66
+ }
67
+ function declaredXmlParts(types, paths) {
68
+ const declared = declaredIn(decodeUtf8(types).text);
69
+ return new Set(
70
+ Array.from(paths).filter((path) => {
71
+ const name = `/${path}`.toLowerCase();
72
+ if (declared.overrides.has(name)) return declared.xmlOverrides.has(name);
73
+ const extension = path.slice(path.lastIndexOf(".") + 1).toLowerCase();
74
+ return declared.xmlDefaults.has(extension);
75
+ })
76
+ );
77
+ }
78
+ function contentTypeWriter(parts) {
79
+ const overrides = /* @__PURE__ */ new Map();
80
+ const defaults = /* @__PURE__ */ new Map();
81
+ return {
82
+ addOverride: (partPath, contentType) => {
83
+ const key = partPath.toLowerCase();
84
+ if (!overrides.has(key)) overrides.set(key, [partPath, contentType]);
85
+ },
86
+ addDefault: (extension, contentType) => {
87
+ const key = extension.toLowerCase();
88
+ if (!defaults.has(key)) defaults.set(key, [extension, contentType]);
89
+ },
90
+ part: () => {
91
+ if (overrides.size === 0 && defaults.size === 0) return null;
92
+ const original = parts.get(CONTENT_TYPES_PATH);
93
+ if (!original) {
94
+ throw new DocxExportError(
95
+ "missing-content-types",
96
+ `cannot declare a part in a package that has no ${CONTENT_TYPES_PATH}`
97
+ );
98
+ }
99
+ const { text, hadBom } = decodeUtf8(original);
100
+ const declared = declaredIn(text);
101
+ const prefix = rootPrefixOf(text);
102
+ const declarations = [
103
+ ...Array.from(defaults.values()).filter(
104
+ ([extension]) => !declared.defaults.has(extension.toLowerCase())
105
+ ).map(
106
+ ([extension, contentType]) => elementXml(`${prefix}Default`, [
107
+ ["Extension", extension],
108
+ ["ContentType", contentType]
109
+ ])
110
+ ),
111
+ ...Array.from(overrides.values()).filter(
112
+ ([partPath]) => !declared.overrides.has(`/${partPath}`.toLowerCase())
113
+ ).map(
114
+ ([partPath, contentType]) => elementXml(`${prefix}Override`, [
115
+ ["PartName", `/${partPath}`],
116
+ ["ContentType", contentType]
117
+ ])
118
+ )
119
+ ];
120
+ if (declarations.length === 0) return null;
121
+ return encodeUtf8(
122
+ splicePart(text, { root: "Types", prepend: declarations.join("") }),
123
+ hadBom
124
+ );
125
+ }
126
+ };
127
+ }
128
+ export {
129
+ CONTENT_TYPES_PATH,
130
+ availablePartPath,
131
+ contentTypeWriter,
132
+ declaredXmlParts,
133
+ readPart,
134
+ relatedPartPath
135
+ };
@@ -21,6 +21,8 @@ export interface PageGeometry {
21
21
  marginTopTwips: number;
22
22
  marginBottomTwips: number;
23
23
  }
24
+ /** CSS calls an inch 96 pixels, and a point is a 72nd of one */
25
+ export declare const PX_PER_PT: number;
24
26
  export declare function twipsToPx(twips: number): number;
25
27
  /**
26
28
  * The paper a document that says nothing is drawn on: A4 portrait with the margins this
@@ -1,8 +1,15 @@
1
1
  // src/docx/pageGeometry.ts
2
- import { toNumber, wAttr } from "../ooxml/units.js";
2
+ import {
3
+ ST_SignedTwipsMeasure,
4
+ ST_TwipsMeasure,
5
+ TWIPS_PER_INCH,
6
+ TWIPS_PER_PT
7
+ } from "../ooxml/simpleTypes.js";
8
+ import { wAttr } from "../ooxml/units.js";
3
9
  import { childByLocalName } from "../ooxml/xml.js";
4
- var TWIPS_PER_CM = 1440 / 2.54;
5
- var PX_PER_TWIP = 96 / 1440;
10
+ var TWIPS_PER_CM = TWIPS_PER_INCH / 2.54;
11
+ var PX_PER_PT = 96 / 72;
12
+ var PX_PER_TWIP = PX_PER_PT / TWIPS_PER_PT;
6
13
  function cm(value) {
7
14
  return Math.round(value * TWIPS_PER_CM);
8
15
  }
@@ -34,13 +41,14 @@ function readPageGeometry(sectPr) {
34
41
  if (!sectPr) return A4_PORTRAIT;
35
42
  const pgSz = childByLocalName(sectPr, "pgSz");
36
43
  const pgMar = childByLocalName(sectPr, "pgMar");
37
- const width = pgSz ? readSize(toNumber(wAttr(pgSz, "w"))) : null;
38
- const height = pgSz ? readSize(toNumber(wAttr(pgSz, "h"))) : null;
44
+ const width = pgSz ? readSize(ST_TwipsMeasure.parse(wAttr(pgSz, "w"))) : null;
45
+ const height = pgSz ? readSize(ST_TwipsMeasure.parse(wAttr(pgSz, "h"))) : null;
39
46
  const readable = width !== null && height !== null;
40
- const marginLeft = pgMar ? toNumber(wAttr(pgMar, "left")) : null;
41
- const marginRight = pgMar ? toNumber(wAttr(pgMar, "right")) : null;
42
- const marginTop = pgMar ? toNumber(wAttr(pgMar, "top")) : null;
43
- const marginBottom = pgMar ? toNumber(wAttr(pgMar, "bottom")) : null;
47
+ const margin = (side) => pgMar ? ST_SignedTwipsMeasure.parse(wAttr(pgMar, side)) : null;
48
+ const marginLeft = margin("left");
49
+ const marginRight = margin("right");
50
+ const marginTop = margin("top");
51
+ const marginBottom = margin("bottom");
44
52
  const geometry = {
45
53
  widthTwips: readable ? width : A4_PORTRAIT.widthTwips,
46
54
  heightTwips: readable ? height : A4_PORTRAIT.heightTwips,
@@ -79,6 +87,7 @@ function firstSectPrIn(root) {
79
87
  export {
80
88
  A4_BODY_WIDTH,
81
89
  A4_PORTRAIT,
90
+ PX_PER_PT,
82
91
  bodyHeightTwips,
83
92
  bodyWidth,
84
93
  bodyWidthTwips,
@@ -5,14 +5,12 @@
5
5
  * When one paragraph is changed, the formatting we do not touch, such as tab stops and borders,
6
6
  * must stay as the original text wrote it. So we never rebuild the fragment; we change it one child
7
7
  * at a time, and within a child one attribute at a time.
8
- * The fragment we operated on is read back with the parser used when opening the document to build
9
- * the display values, and the style the paragraph points at is layered in the same order as it was
10
- * then. That way a changed paragraph's display values are the same as they would be after saving
11
- * and reopening.
8
+ * What the edited fragment is drawn with is not decided here: the caller hands the fragment to the
9
+ * resolver (`formatting/resolve`), the same one opening the document uses, so a changed paragraph's
10
+ * display values are the same as they would be after saving and reopening.
12
11
  */
13
- import type { LineSpacing, NumberingRef, ParagraphAlign, ParagraphFormat, RunFormat } from "../model/format";
12
+ import type { LineSpacing, NumberingRef, ParagraphAlign } from "../model/format";
14
13
  import type { LevelIndent } from "../numbering/parseNumbering";
15
- import { type StyleTable } from "./formatting";
16
14
  /** What to do with the indents */
17
15
  export type IndentChange =
18
16
  /** Leaves the indents the paragraph wrote down as they are */
@@ -33,32 +31,22 @@ export interface ListChange {
33
31
  numbering: NumberingRef | null;
34
32
  indent: IndentChange;
35
33
  }
36
- /** The paragraph formatting we operated on, and the display values read back out of it */
34
+ /** The paragraph formatting we operated on. A null `pPr` is a fragment with nothing left in it */
37
35
  export interface ParagraphProps {
38
36
  pPr: string | null;
39
- format: ParagraphFormat | null;
40
- /** The character formatting the style the paragraph now wears lays down (`styleRun` in `schema`) */
41
- styleRun: RunFormat | null;
42
37
  }
43
- /**
44
- * Reads the fragment we operated on back along the same path used when opening the document.
45
- *
46
- * The values of the style the paragraph wears are laid down underneath (the same order as import),
47
- * which for a paragraph pointing at no style of its own is the document's default paragraph style.
48
- */
49
- export declare function readParagraphProps(pPr: string | null, styles?: StyleTable, defaultStyleId?: string | null): ParagraphFormat | null;
50
- export declare function withListNumbering(pPr: string | null, change: ListChange, styles?: StyleTable, defaultStyleId?: string | null): ParagraphProps | null;
38
+ export declare function withListNumbering(pPr: string | null, change: ListChange): ParagraphProps | null;
51
39
  /**
52
40
  * The result of moving the paragraph's own left indent to `leftTwips`.
53
41
  * Everything else the indent recorded - the hanging or first-line indent, the right indent -
54
42
  * stays as it was.
55
43
  */
56
- export declare function withLeftIndent(pPr: string | null, leftTwips: number, styles?: StyleTable, defaultStyleId?: string | null): ParagraphProps | null;
44
+ export declare function withLeftIndent(pPr: string | null, leftTwips: number): ParagraphProps | null;
57
45
  /**
58
46
  * The result of changing the line spacing. The space above and below the paragraph is left as it was.
59
47
  * `auto` states the multiple in 240ths of a line; the other rules pin the height down in twips.
60
48
  */
61
- export declare function withLineSpacing(pPr: string | null, spacing: LineSpacing, styles?: StyleTable, defaultStyleId?: string | null): ParagraphProps | null;
49
+ export declare function withLineSpacing(pPr: string | null, spacing: LineSpacing): ParagraphProps | null;
62
50
  /**
63
51
  * The result of pointing the paragraph at a named style.
64
52
  * A null id takes the pStyle away, which is how the default style is worn.
@@ -66,9 +54,9 @@ export declare function withLineSpacing(pPr: string | null, spacing: LineSpacing
66
54
  * Nothing else in the fragment moves: as in Word, a style is applied by naming it and not by
67
55
  * copying its values into the paragraph, so the direct formatting written here keeps beating it.
68
56
  */
69
- export declare function withParagraphStyle(pPr: string | null, styleId: string | null, styles?: StyleTable, defaultStyleId?: string | null): ParagraphProps | null;
57
+ export declare function withParagraphStyle(pPr: string | null, styleId: string | null): ParagraphProps | null;
70
58
  /**
71
59
  * The result of changing the paragraph alignment.
72
60
  * One alignment is always on, so we provide no way to withdraw the setting (the same as Word).
73
61
  */
74
- export declare function withParagraphAlign(pPr: string | null, align: ParagraphAlign, styles?: StyleTable, defaultStyleId?: string | null): ParagraphProps | null;
62
+ export declare function withParagraphAlign(pPr: string | null, align: ParagraphAlign): ParagraphProps | null;
@@ -1,19 +1,23 @@
1
1
  // src/docx/paraProps.ts
2
- import { childByLocalName, escapeXml, localPart } from "../ooxml/xml.js";
3
2
  import {
4
- layerParagraphFormat,
5
- layerRunFormat,
6
- NO_STYLES,
7
- paragraphStyleFormat,
8
- readParagraphFormat
9
- } from "./formatting.js";
3
+ elementXml,
4
+ wAttrValue,
5
+ withoutAttrs
6
+ } from "../ooxml/element.js";
7
+ import { wName } from "../ooxml/names.js";
8
+ import { setAttr } from "../ooxml/precedence.js";
10
9
  import {
11
- P_PR_ORDER,
10
+ childElement,
12
11
  parseProps,
13
- parsePropsXml,
14
12
  renderProps,
15
- setPropsChild
16
- } from "./propsXml.js";
13
+ setChild
14
+ } from "../ooxml/props.js";
15
+ import {
16
+ ST_DecimalNumber,
17
+ ST_SignedTwipsMeasure,
18
+ TWIPS_PER_PT
19
+ } from "../ooxml/simpleTypes.js";
20
+ import { LINE_UNITS_PER_LINE } from "./formatting.js";
17
21
  var LEFT_IND_ATTRS = ["left", "start", "leftChars", "startChars"];
18
22
  var LEFT_TWIPS_IND_ATTRS = ["left", "start"];
19
23
  var HANGING_IND_ATTRS = ["hanging", "hangingChars"];
@@ -22,130 +26,103 @@ var FIRST_LINE_IND_ATTRS = [
22
26
  "firstLine",
23
27
  "firstLineChars"
24
28
  ];
25
- function readParagraphProps(pPr, styles = NO_STYLES, defaultStyleId = null) {
26
- const direct = pPr === null ? null : readParagraphFormat(parsePropsXml(pPr));
27
- const style = paragraphStyleFormat(pPr, styles, defaultStyleId);
28
- return layerParagraphFormat(style?.paragraph ?? {}, direct);
29
- }
30
- function readParagraphStyleRun(pPr, styles = NO_STYLES, defaultStyleId = null) {
31
- const style = paragraphStyleFormat(pPr, styles, defaultStyleId);
32
- return layerRunFormat(style?.run ?? {}, null);
33
- }
34
29
  function numPrXml(ref) {
35
30
  if (!ref) return null;
36
- return `<w:numPr><w:ilvl w:val="${ref.ilvl}"/><w:numId w:val="${ref.numId}"/></w:numPr>`;
37
- }
38
- function keptIndAttrs(ind, dropped) {
39
- if (!ind) return [];
40
- return Array.from(ind.attributes).filter((attr) => !dropped.includes(localPart(attr.name))).map((attr) => [attr.name, attr.value]);
31
+ return elementXml(
32
+ wName("numPr"),
33
+ [],
34
+ [
35
+ elementXml(wName("ilvl"), [[wName("val"), `${ref.ilvl}`]]),
36
+ elementXml(wName("numId"), [[wName("val"), `${ref.numId}`]])
37
+ ]
38
+ );
41
39
  }
42
40
  function levelIndAttrs(indent) {
43
41
  const attrs = [];
44
42
  if (indent.startTwips !== null)
45
- attrs.push(["w:left", `${indent.startTwips}`]);
43
+ attrs.push([wName("left"), `${indent.startTwips}`]);
46
44
  if (indent.hangingTwips !== null) {
47
- attrs.push(["w:hanging", `${indent.hangingTwips}`]);
45
+ attrs.push([wName("hanging"), `${indent.hangingTwips}`]);
48
46
  } else if (indent.firstLineTwips !== null) {
49
- attrs.push(["w:firstLine", `${indent.firstLineTwips}`]);
47
+ attrs.push([wName("firstLine"), `${indent.firstLineTwips}`]);
50
48
  }
51
49
  return attrs;
52
50
  }
53
- function attrsXml(tag, attrs) {
54
- if (attrs.length === 0) return null;
55
- const text = attrs.map(([name, value]) => `${name}="${escapeXml(value)}"`).join(" ");
56
- return `<${tag} ${text}/>`;
57
- }
58
51
  function indXml(attrs) {
59
- return attrsXml("w:ind", attrs);
52
+ return attrs.length === 0 ? null : elementXml(wName("ind"), attrs);
60
53
  }
61
54
  function nextIndXml(ind, change) {
62
55
  if (change.kind === "keep") return void 0;
63
56
  if (change.kind === "clearHanging") {
64
- return indXml(keptIndAttrs(ind, HANGING_IND_ATTRS));
57
+ return indXml(withoutAttrs(ind, HANGING_IND_ATTRS));
65
58
  }
66
59
  const dropped = [...LEFT_IND_ATTRS, ...FIRST_LINE_IND_ATTRS];
67
60
  return indXml([
68
61
  ...levelIndAttrs(change.indent),
69
- ...keptIndAttrs(ind, dropped)
62
+ ...withoutAttrs(ind, dropped)
70
63
  ]);
71
64
  }
72
- function leftIndAttrs(ind, leftTwips) {
73
- const original = ind ? Array.from(ind.attributes) : [];
74
- const slot = original.find(
75
- (attr) => LEFT_TWIPS_IND_ATTRS.includes(localPart(attr.name))
76
- )?.name;
77
- const moved = original.flatMap((attr) => {
78
- if (!LEFT_IND_ATTRS.includes(localPart(attr.name))) {
79
- return [[attr.name, attr.value]];
80
- }
81
- return attr.name === slot && leftTwips > 0 ? [[attr.name, `${leftTwips}`]] : [];
82
- });
83
- return slot === void 0 && leftTwips > 0 ? [["w:left", `${leftTwips}`], ...moved] : moved;
65
+ function leftIndAttrs(original, leftTwips) {
66
+ const slot = LEFT_TWIPS_IND_ATTRS.find(
67
+ (name) => wAttrValue(original, name) !== null
68
+ );
69
+ const written = leftTwips > 0 ? `${leftTwips}` : null;
70
+ const kept = withoutAttrs(
71
+ original,
72
+ LEFT_IND_ATTRS.filter((name) => name !== slot)
73
+ );
74
+ if (slot !== void 0) return setAttr(kept, "ind", slot, written);
75
+ return written === null ? kept : [[wName("left"), written], ...kept];
84
76
  }
85
77
  var EMPTY_P_PR = { tag: "w:pPr", attrs: null, children: [] };
86
- function editParagraphProps(pPr, styles, defaultStyleId, plan) {
78
+ function editParagraphProps(pPr, plan) {
87
79
  const props = pPr === null ? EMPTY_P_PR : parseProps(pPr);
88
- const element = pPr === null ? null : parsePropsXml(pPr);
89
- if (!props || pPr !== null && !element) return null;
90
- const children = plan(element).reduce(
91
- (kept, [name, xml]) => setPropsChild(kept, name, xml, P_PR_ORDER),
92
- props.children
80
+ if (!props) return null;
81
+ const edits = plan(props);
82
+ if (!edits) return null;
83
+ const rendered = renderProps(
84
+ edits.reduce((kept, [name, xml]) => setChild(kept, name, xml), props)
93
85
  );
94
- const rendered = renderProps({ ...props, children });
95
- const next = rendered === "" ? null : rendered;
96
- return {
97
- pPr: next,
98
- format: readParagraphProps(next, styles, defaultStyleId),
99
- styleRun: readParagraphStyleRun(next, styles, defaultStyleId)
100
- };
86
+ return { pPr: rendered === "" ? null : rendered };
101
87
  }
102
- function withListNumbering(pPr, change, styles = NO_STYLES, defaultStyleId = null) {
103
- return editParagraphProps(pPr, styles, defaultStyleId, (element) => {
104
- const ind = nextIndXml(
105
- element ? childByLocalName(element, "ind") : null,
106
- change.indent
107
- );
88
+ function withListNumbering(pPr, change) {
89
+ return editParagraphProps(pPr, (props) => {
90
+ const current = childElement(props, "ind");
91
+ if (!current) return null;
92
+ const ind = nextIndXml(current.attrs, change.indent);
108
93
  const numPr = ["numPr", numPrXml(change.numbering)];
109
94
  return ind === void 0 ? [numPr] : [numPr, ["ind", ind]];
110
95
  });
111
96
  }
112
- function withLeftIndent(pPr, leftTwips, styles = NO_STYLES, defaultStyleId = null) {
113
- return editParagraphProps(pPr, styles, defaultStyleId, (element) => {
114
- const ind = element ? childByLocalName(element, "ind") : null;
115
- return [["ind", indXml(leftIndAttrs(ind, leftTwips))]];
97
+ function withLeftIndent(pPr, leftTwips) {
98
+ return editParagraphProps(pPr, (props) => {
99
+ const ind = childElement(props, "ind");
100
+ if (!ind) return null;
101
+ return [["ind", indXml(leftIndAttrs(ind.attrs, leftTwips))]];
116
102
  });
117
103
  }
118
104
  function spacingAttrs(spacing, edits) {
119
- const original = spacing ? Array.from(spacing.attributes) : [];
120
- const editOf = (name) => edits.find(([edited]) => edited === localPart(name));
121
- const changed = original.map((attr) => [
122
- attr.name,
123
- editOf(attr.name)?.[1] ?? attr.value
124
- ]);
125
- const added = edits.filter(
126
- ([name]) => !original.some((attr) => localPart(attr.name) === name)
105
+ return edits.reduce(
106
+ (attrs, [name, value]) => setAttr(attrs, "spacing", name, value),
107
+ spacing
127
108
  );
128
- return [
129
- ...changed,
130
- ...added.map(([name, value]) => [`w:${name}`, value])
131
- ];
132
109
  }
133
- function withLineSpacing(pPr, spacing, styles = NO_STYLES, defaultStyleId = null) {
134
- const line = spacing.rule === "auto" ? Math.round(spacing.lines * 240) : Math.round(spacing.pt * 20);
135
- return editParagraphProps(pPr, styles, defaultStyleId, (element) => {
136
- const current = element ? childByLocalName(element, "spacing") : null;
137
- const attrs = spacingAttrs(current, [
138
- ["line", `${line}`],
110
+ function withLineSpacing(pPr, spacing) {
111
+ const line = spacing.rule === "auto" ? ST_DecimalNumber.format(Math.round(spacing.lines * LINE_UNITS_PER_LINE)) : ST_SignedTwipsMeasure.format(Math.round(spacing.pt * TWIPS_PER_PT));
112
+ if (line === null) return null;
113
+ return editParagraphProps(pPr, (props) => {
114
+ const current = childElement(props, "spacing");
115
+ if (!current) return null;
116
+ const attrs = spacingAttrs(current.attrs, [
117
+ ["line", line],
139
118
  ["lineRule", spacing.rule]
140
119
  ]);
141
- return [["spacing", attrsXml("w:spacing", attrs)]];
120
+ return [["spacing", elementXml(wName("spacing"), attrs)]];
142
121
  });
143
122
  }
144
- function withParagraphStyle(pPr, styleId, styles = NO_STYLES, defaultStyleId = null) {
145
- const pStyle = styleId === null ? null : `<w:pStyle w:val="${escapeXml(styleId)}"/>`;
146
- return editParagraphProps(pPr, styles, defaultStyleId, () => [
147
- ["pStyle", pStyle]
148
- ]);
123
+ function withParagraphStyle(pPr, styleId) {
124
+ const pStyle = styleId === null ? null : elementXml(wName("pStyle"), [[wName("val"), styleId]]);
125
+ return editParagraphProps(pPr, () => [["pStyle", pStyle]]);
149
126
  }
150
127
  var JC_BY_ALIGN = {
151
128
  left: "left",
@@ -153,12 +130,11 @@ var JC_BY_ALIGN = {
153
130
  right: "right",
154
131
  justify: "both"
155
132
  };
156
- function withParagraphAlign(pPr, align, styles = NO_STYLES, defaultStyleId = null) {
157
- const jc = `<w:jc w:val="${JC_BY_ALIGN[align]}"/>`;
158
- return editParagraphProps(pPr, styles, defaultStyleId, () => [["jc", jc]]);
133
+ function withParagraphAlign(pPr, align) {
134
+ const jc = elementXml(wName("jc"), [[wName("val"), JC_BY_ALIGN[align]]]);
135
+ return editParagraphProps(pPr, () => [["jc", jc]]);
159
136
  }
160
137
  export {
161
- readParagraphProps,
162
138
  withLeftIndent,
163
139
  withLineSpacing,
164
140
  withListNumbering,
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The contract every part an export writes beside the body is written under.
3
+ *
4
+ * A planner answers with the parts it rewrites, keyed by path, and declares what a part it adds
5
+ * needs through the two writers the context carries: a relationship from the main part and a
6
+ * content type. Those two parts are written once at the end from everything every planner asked
7
+ * for, so no planner writes either of them itself and none can write over what another declared.
8
+ */
9
+ import type { Node as PMNode } from "prosemirror-model";
10
+ import { type ContentTypeWriter } from "./packageParts";
11
+ import { type RelationshipWriter } from "./relationships";
12
+ import type { SessionStore } from "./session";
13
+ export interface PartPlanContext {
14
+ readonly relationships: RelationshipWriter;
15
+ readonly contentTypes: ContentTypeWriter;
16
+ }
17
+ export interface PartPlanner {
18
+ readonly name: string;
19
+ /** The parts to write, keyed by path, and null when the document gives this planner nothing to write */
20
+ plan(doc: PMNode, session: SessionStore, context: PartPlanContext): ReadonlyMap<string, Uint8Array> | null;
21
+ }
22
+ /**
23
+ * Every part the planners write, folded into one map in planner order.
24
+ *
25
+ * The body and context-owned parts are reserved. Earlier writes include media planned before
26
+ * the body, so no later planner can overwrite those bytes either. Part names are compared
27
+ * without regard to case, as package part names are.
28
+ */
29
+ export declare function runPartPlanners(planners: readonly PartPlanner[], doc: PMNode, session: SessionStore, context: PartPlanContext, prior?: ReadonlyMap<string, Uint8Array>): Map<string, Uint8Array>;
30
+ /**
31
+ * Every rewritten XML part has to read back as XML, and a refusal names the part.
32
+ *
33
+ * The parts are spliced as text, so this is where a splice that cut in the wrong place shows up:
34
+ * before the package is repacked rather than when somebody opens it.
35
+ */
36
+ export declare function assertPartsParse(replacements: ReadonlyMap<string, Uint8Array>, contentTypes?: Uint8Array): void;
@@ -0,0 +1,59 @@
1
+ // src/docx/partPlan.ts
2
+ import { DocxExportError } from "../ooxml/errors.js";
3
+ import { decodeUtf8, parseXml } from "../ooxml/xml.js";
4
+ import {
5
+ CONTENT_TYPES_PATH,
6
+ declaredXmlParts
7
+ } from "./packageParts.js";
8
+ import { relsPathOf } from "./relationships.js";
9
+ function runPartPlanners(planners, doc, session, context, prior = /* @__PURE__ */ new Map()) {
10
+ const owned = new Set(
11
+ [
12
+ CONTENT_TYPES_PATH,
13
+ relsPathOf(session.mainPartPath),
14
+ session.mainPartPath
15
+ ].map((path) => path.toLowerCase())
16
+ );
17
+ const parts = new Map(prior);
18
+ const written = new Set(
19
+ Array.from(prior.keys(), (path) => path.toLowerCase())
20
+ );
21
+ for (const planner of planners) {
22
+ for (const [path, bytes] of planner.plan(doc, session, context) ?? []) {
23
+ if (owned.has(path.toLowerCase())) {
24
+ throw new Error(
25
+ `the ${planner.name} planner wrote ${path}, which another export writer owns`
26
+ );
27
+ }
28
+ if (written.has(path.toLowerCase())) {
29
+ throw new Error(
30
+ `the ${planner.name} planner wrote ${path}, which an earlier writer wrote already`
31
+ );
32
+ }
33
+ parts.set(path, bytes);
34
+ written.add(path.toLowerCase());
35
+ }
36
+ }
37
+ return parts;
38
+ }
39
+ var XML_PART = /\.(?:xml|rels)$/i;
40
+ function assertPartsParse(replacements, contentTypes) {
41
+ const paths = [...replacements.keys()];
42
+ const declared = contentTypes && paths.some((path) => !XML_PART.test(path)) ? declaredXmlParts(contentTypes, paths) : /* @__PURE__ */ new Set();
43
+ for (const [path, bytes] of replacements) {
44
+ if (!XML_PART.test(path) && !declared.has(path)) continue;
45
+ try {
46
+ parseXml(decodeUtf8(bytes).text);
47
+ } catch (cause) {
48
+ throw new DocxExportError(
49
+ "malformed-xml",
50
+ `${path} as written could not be parsed`,
51
+ { cause }
52
+ );
53
+ }
54
+ }
55
+ }
56
+ export {
57
+ assertPartsParse,
58
+ runPartPlanners
59
+ };