@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,8 +5,12 @@
5
5
  * arrived with is carried around as its original text, and an edit swaps out one element or one
6
6
  * attribute of it. Rebuilding a parsed element instead would rewrite markup nobody touched.
7
7
  *
8
- * An attribute is matched by its local part rather than by its written name, so a document that
9
- * bound the WordprocessingML namespace to a prefix of its own is edited in the spelling it chose.
8
+ * An attribute is read and written as the WordprocessingML attribute of a local name: the one
9
+ * spelled under the `w` prefix, which `RESERVED_PREFIXES` pins to that namespace in every fragment,
10
+ * or failing that an unprefixed one, which is what `wAttr` accepted off a parsed fragment too. An
11
+ * attribute another prefix qualifies belongs to some other vocabulary, so it is neither read as the
12
+ * formatting value nor written over when it happens to share the local name (`x:val` beside
13
+ * `w:val`). The two readers below are named for which of those they answer.
10
14
  */
11
15
  import { type KnownPrefix } from "./names";
12
16
  /** One attribute as it is written: the whole name, prefix included, and the value before escaping */
@@ -26,16 +30,30 @@ export declare function elementXml(name: string, attrs: readonly XmlAttr[], chil
26
30
  * the prefix itself.
27
31
  */
28
32
  export declare function xmlnsAttr(prefix: KnownPrefix): XmlAttr;
33
+ /** The same for the WordprocessingML vocabulary, which is the one almost everything is written in */
34
+ export declare function wLocalName(name: string): string | null;
29
35
  /**
30
- * The attributes with the one of this local name written as `value`, or removed when it is null.
36
+ * The value of the WordprocessingML attribute of this local name, and null for an element that
37
+ * carries none. An attribute of the same local name under another prefix is not it.
38
+ */
39
+ export declare function wAttrValue(attrs: readonly XmlAttr[], localName: string): string | null;
40
+ /**
41
+ * The attributes with the WordprocessingML one of this local name written as `value`, or removed
42
+ * when it is null.
31
43
  *
32
44
  * One already there keeps the slot it sat in and the name it was written under, so an edited
33
45
  * element reads as the original did. One that is not there yet goes on the end under the `w`
34
- * prefix.
46
+ * prefix, and one of the same local name under another prefix stands untouched beside it.
35
47
  */
36
48
  export declare function withAttr(attrs: readonly XmlAttr[], name: string, value: string | null): XmlAttr[];
37
- export declare function withoutAttrs(attrs: readonly XmlAttr[], localNames: readonly string[]): XmlAttr[];
38
- export declare function attrValue(attrs: readonly XmlAttr[], localName: string): string | null;
49
+ /**
50
+ * The attributes without the ones of these local names, in either spelling.
51
+ *
52
+ * The names are read under the WordprocessingML vocabulary unless another prefix is named, which
53
+ * is what reaches the handful of attributes Word writes beside it: `w14:paraId` is a name under
54
+ * `w14` and no more a `w:` attribute than `x:val` is.
55
+ */
56
+ export declare function withoutAttrs(attrs: readonly XmlAttr[], localNames: readonly string[], prefix?: KnownPrefix): XmlAttr[];
39
57
  /**
40
58
  * An opening tag whose attributes are the ones the original wrote.
41
59
  *
@@ -45,8 +63,3 @@ export declare function attrValue(attrs: readonly XmlAttr[], localName: string):
45
63
  export declare function openTagXml(name: string, rawAttrs: string | null): string;
46
64
  /** The same for an element that closes on its own */
47
65
  export declare function emptyTagXml(name: string, rawAttrs: string | null): string;
48
- /**
49
- * The attributes of a parsed element as pairs, for a caller that still holds the element rather
50
- * than its text.
51
- */
52
- export declare function attrPairs(el: Element | null): XmlAttr[];
@@ -1,5 +1,10 @@
1
1
  // src/ooxml/element.ts
2
- import { NAMESPACES, wName } from "./names.js";
2
+ import {
3
+ NAMESPACES,
4
+ qualify,
5
+ W_PREFIX,
6
+ wName
7
+ } from "./names.js";
3
8
  import { escapeXml, localPart } from "./xml.js";
4
9
  function attrsText(attrs) {
5
10
  return attrs.map(([name, value]) => `${name}="${escapeXml(value)}"`).join(" ");
@@ -13,19 +18,36 @@ function elementXml(name, attrs, children = []) {
13
18
  function xmlnsAttr(prefix) {
14
19
  return [`xmlns:${prefix}`, NAMESPACES[prefix]];
15
20
  }
21
+ function localNameUnder(prefix, name) {
22
+ if (!name.includes(":")) return name;
23
+ const local = localPart(name);
24
+ return name === qualify(prefix, local) ? local : null;
25
+ }
26
+ function wLocalName(name) {
27
+ return localNameUnder(W_PREFIX, name);
28
+ }
29
+ function wAttrIndex(attrs, localName) {
30
+ const qualified = attrs.findIndex(([name]) => name === wName(localName));
31
+ if (qualified !== -1) return qualified;
32
+ return attrs.findIndex(([name]) => name === localName);
33
+ }
34
+ function wAttrValue(attrs, localName) {
35
+ const at = wAttrIndex(attrs, localName);
36
+ return at === -1 ? null : attrs[at][1];
37
+ }
16
38
  function withAttr(attrs, name, value) {
17
39
  if (value === null) return withoutAttrs(attrs, [name]);
18
- const at = attrs.findIndex(([attr]) => localPart(attr) === name);
40
+ const at = wAttrIndex(attrs, name);
19
41
  if (at === -1) return [...attrs, [wName(name), value]];
20
42
  return attrs.map(
21
43
  (attr, index) => index === at ? [attr[0], value] : attr
22
44
  );
23
45
  }
24
- function withoutAttrs(attrs, localNames) {
25
- return attrs.filter(([name]) => !localNames.includes(localPart(name)));
26
- }
27
- function attrValue(attrs, localName) {
28
- return attrs.find(([name]) => localPart(name) === localName)?.[1] ?? null;
46
+ function withoutAttrs(attrs, localNames, prefix = W_PREFIX) {
47
+ return attrs.filter(([name]) => {
48
+ const local = localNameUnder(prefix, name);
49
+ return local === null || !localNames.includes(local);
50
+ });
29
51
  }
30
52
  function openTagXml(name, rawAttrs) {
31
53
  return rawAttrs ? `<${name} ${rawAttrs}>` : `<${name}>`;
@@ -33,16 +55,13 @@ function openTagXml(name, rawAttrs) {
33
55
  function emptyTagXml(name, rawAttrs) {
34
56
  return rawAttrs ? `<${name} ${rawAttrs}/>` : `<${name}/>`;
35
57
  }
36
- function attrPairs(el) {
37
- return el ? Array.from(el.attributes, (attr) => [attr.name, attr.value]) : [];
38
- }
39
58
  export {
40
- attrPairs,
41
- attrValue,
42
59
  attrsText,
43
60
  elementXml,
44
61
  emptyTagXml,
45
62
  openTagXml,
63
+ wAttrValue,
64
+ wLocalName,
46
65
  withAttr,
47
66
  withoutAttrs,
48
67
  xmlnsAttr
@@ -8,14 +8,16 @@
8
8
  /**
9
9
  * Why a document could not be opened.
10
10
  *
11
+ * - `no-xml-parser`: the runtime has no XML parser to read the package with. The one code here that is about where the call was made rather than about the file
11
12
  * - `not-a-docx`: the bytes are not a readable zip container, its entry names are not a package's, or an entry does not hold what it says it does
12
13
  * - `too-large`: the package asks to inflate to more than we open
13
14
  * - `missing-part`: the package has no main document part to read
14
15
  * - `missing-body`: the main part carries no `w:body`
15
16
  * - `malformed-xml`: the XML cannot be parsed, declares a DTD, or its markup is inconsistent
17
+ * - `unsupported-conformance`: the package is an ECMA-376 Strict one, and this editor reads Transitional
16
18
  * - `unsupported-content`: the document holds markup we cannot write back out unchanged
17
19
  */
18
- export type DocxImportErrorCode = "not-a-docx" | "too-large" | "missing-part" | "missing-body" | "malformed-xml" | "unsupported-content";
20
+ export type DocxImportErrorCode = "no-xml-parser" | "not-a-docx" | "too-large" | "missing-part" | "missing-body" | "malformed-xml" | "unsupported-conformance" | "unsupported-content";
19
21
  /** Thrown when we hit a document whose content cannot be kept safely. We refuse to open it instead of losing it silently */
20
22
  export declare class DocxImportError extends Error {
21
23
  readonly code: DocxImportErrorCode;
@@ -24,14 +26,13 @@ export declare class DocxImportError extends Error {
24
26
  /**
25
27
  * Why a document could not be written back out.
26
28
  *
27
- * - `missing-numbering-part`: a new list needs a numbering.xml the document does not have
28
- * - `missing-content-types`: a new image needs a [Content_Types].xml the package does not have
29
- * - `unsupported-content`: the document holds a node kind we have no way to serialize
29
+ * - `missing-content-types`: a part the writer adds, be it an image, a list definition or a comment, needs a [Content_Types].xml the package does not have
30
+ * - `unsupported-content`: the document holds something no correct file can be written from: a node kind we have no way to serialize, a preserved block standing in two places, which has one original XML to write, or a paragraph in a list nothing defines
30
31
  * - `lost-original`: a node that only carries its original XML has lost it
31
32
  * - `malformed-xml`: an original XML fragment cannot be read well enough to rewrite
32
33
  * - `invalid-table`: the table grid is inconsistent, e.g. a vertical merge outliving its rows
33
34
  */
34
- export type DocxExportErrorCode = "missing-numbering-part" | "missing-content-types" | "unsupported-content" | "lost-original" | "malformed-xml" | "invalid-table";
35
+ export type DocxExportErrorCode = "missing-content-types" | "unsupported-content" | "lost-original" | "malformed-xml" | "invalid-table";
35
36
  /** Thrown when an edited document cannot be written back out without losing or corrupting content */
36
37
  export declare class DocxExportError extends Error {
37
38
  readonly code: DocxExportErrorCode;
@@ -10,6 +10,10 @@
10
10
  *
11
11
  * An imported image holds on to its whole original `<w:drawing>` XML, so an untouched
12
12
  * image goes back out byte for byte. A resize rewrites nothing but the two extents.
13
+ *
14
+ * `emuToPx` is imported on its own by a builder working in pixels, so nothing here reads
15
+ * `NAMESPACES` at the top level: a top-level read is an impure statement a bundler keeps,
16
+ * and it would hold the naming layer in a bundle that wanted one multiplication.
13
17
  */
14
18
  /**
15
19
  * English Metric Units, the unit every DrawingML length is written in.
@@ -43,7 +43,6 @@ function toImageExtent(value) {
43
43
  const cy = positiveInt(value.cy);
44
44
  return cx === null || cy === null ? null : { cx, cy };
45
45
  }
46
- var PICTURE_URI = NAMESPACES.pic;
47
46
  function childOf(el, name) {
48
47
  return el ? childByLocalName(el, name) : null;
49
48
  }
@@ -63,7 +62,7 @@ function readDrawingPicture(drawing) {
63
62
  const extent = readExtent(childOf(inline, "extent"));
64
63
  if (!extent) return null;
65
64
  const graphicData = childOf(childOf(inline, "graphic"), "graphicData");
66
- if (!graphicData || graphicData.getAttribute("uri") !== PICTURE_URI) {
65
+ if (!graphicData || graphicData.getAttribute("uri") !== NAMESPACES.pic) {
67
66
  return null;
68
67
  }
69
68
  const blip = childOf(
@@ -83,13 +82,11 @@ function withExtent(xml, extent) {
83
82
  const size = `cx="${extent.cx}" cy="${extent.cy}"`;
84
83
  return xml.replace(EXTENT_TAG, (_match, prefix) => `<${prefix ?? ""}extent ${size}/>`).replace(EXT_TAG, (_match, prefix) => `<${prefix ?? ""}ext ${size}/>`);
85
84
  }
86
- var WP_NS = NAMESPACES.wp;
87
- var A_NS = NAMESPACES.a;
88
85
  function imageDrawingXml(image) {
89
86
  const size = `cx="${image.extent.cx}" cy="${image.extent.cy}"`;
90
87
  const name = `Picture ${image.docPrId}`;
91
88
  const descr = image.alt === null ? "" : ` descr="${escapeXml(image.alt)}"`;
92
- return `<w:drawing><wp:inline xmlns:wp="${WP_NS}" distT="0" distB="0" distL="0" distR="0"><wp:extent ${size}/><wp:effectExtent l="0" t="0" r="0" b="0"/><wp:docPr id="${image.docPrId}" name="${name}"${descr}/><wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a="${A_NS}" noChangeAspect="1"/></wp:cNvGraphicFramePr><a:graphic xmlns:a="${A_NS}"><a:graphicData uri="${PICTURE_URI}"><pic:pic xmlns:pic="${PICTURE_URI}"><pic:nvPicPr><pic:cNvPr id="${image.docPrId}" name="${name}"${descr}/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill><a:blip xmlns:r="${R_NS}" r:embed="${escapeXml(image.relId)}"/><a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr><a:xfrm><a:off x="0" y="0"/><a:ext ${size}/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic></wp:inline></w:drawing>`;
89
+ return `<w:drawing><wp:inline xmlns:wp="${NAMESPACES.wp}" distT="0" distB="0" distL="0" distR="0"><wp:extent ${size}/><wp:effectExtent l="0" t="0" r="0" b="0"/><wp:docPr id="${image.docPrId}" name="${name}"${descr}/><wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a="${NAMESPACES.a}" noChangeAspect="1"/></wp:cNvGraphicFramePr><a:graphic xmlns:a="${NAMESPACES.a}"><a:graphicData uri="${NAMESPACES.pic}"><pic:pic xmlns:pic="${NAMESPACES.pic}"><pic:nvPicPr><pic:cNvPr id="${image.docPrId}" name="${name}"${descr}/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill><a:blip xmlns:r="${R_NS}" r:embed="${escapeXml(image.relId)}"/><a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr><a:xfrm><a:off x="0" y="0"/><a:ext ${size}/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic></wp:inline></w:drawing>`;
93
90
  }
94
91
  export {
95
92
  EMU_PER_PX,
@@ -6,6 +6,13 @@
6
6
  * here, and the part a fragment is spliced into declares the same binding, so the prefix is
7
7
  * decided once rather than at every call site.
8
8
  */
9
+ /** The wordprocessing namespace that every element we read lives in */
10
+ export declare const W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
11
+ /**
12
+ * The relationship namespace. It is both where the `r:embed` attributes live and the base
13
+ * every relationship type name is built on
14
+ */
15
+ export declare const R_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
9
16
  /** The prefix every WordprocessingML element and attribute this package writes carries */
10
17
  export declare const W_PREFIX = "w";
11
18
  export declare const NAMESPACES: {
@@ -19,6 +26,8 @@ export declare const NAMESPACES: {
19
26
  readonly pic: "http://schemas.openxmlformats.org/drawingml/2006/picture";
20
27
  };
21
28
  export type KnownPrefix = keyof typeof NAMESPACES;
29
+ /** Whether this is a prefix the package has a namespace for, rather than one a document brought */
30
+ export declare function isKnownPrefix(prefix: string): prefix is KnownPrefix;
22
31
  /** The name `local` goes out under (`qualify("w15", "commentEx")` -> `w15:commentEx`) */
23
32
  export declare function qualify(prefix: KnownPrefix, local: string): string;
24
33
  /** `wName("val")` -> `w:val`. The one place the written WordprocessingML prefix is decided */
@@ -1,5 +1,6 @@
1
1
  // src/ooxml/names.ts
2
- import { R_NS, W_NS } from "./xml.js";
2
+ var W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
3
+ var R_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
3
4
  var W_PREFIX = "w";
4
5
  var NAMESPACES = {
5
6
  w: W_NS,
@@ -11,6 +12,9 @@ var NAMESPACES = {
11
12
  a: "http://schemas.openxmlformats.org/drawingml/2006/main",
12
13
  pic: "http://schemas.openxmlformats.org/drawingml/2006/picture"
13
14
  };
15
+ function isKnownPrefix(prefix) {
16
+ return Object.hasOwn(NAMESPACES, prefix);
17
+ }
14
18
  function qualify(prefix, local) {
15
19
  return `${prefix}:${local}`;
16
20
  }
@@ -22,7 +26,10 @@ function xmlnsDecl(prefix) {
22
26
  }
23
27
  export {
24
28
  NAMESPACES,
29
+ R_NS,
30
+ W_NS,
25
31
  W_PREFIX,
32
+ isKnownPrefix,
26
33
  qualify,
27
34
  wName,
28
35
  xmlnsDecl
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Putting children into the root element of a package part, and leaving every byte around them as
3
+ * the part arrived with.
4
+ *
5
+ * A part is text the writers cut and join, never a tree they build again, so the spots to cut at
6
+ * are found in the text: the root's opening tag past the prolog, and its closing tag counted to
7
+ * by depth. Searching for the closing tag instead would take a `</Relationships>` inside a
8
+ * comment for the end of the part, and miss the root altogether when it is written under a
9
+ * prefix or closes on itself.
10
+ */
11
+ import type { KnownPrefix } from "./names";
12
+ /** One child to put into a part, named by the local part of its tag so the child order can place it */
13
+ export interface PartChild {
14
+ name: string;
15
+ xml: string;
16
+ }
17
+ /**
18
+ * What goes into a part's root. Every field is optional and they compose: the children are
19
+ * replaced first, then the inserts are placed among them, then the prepend and the append go on
20
+ * either end, and last the opening tag is rewritten.
21
+ */
22
+ export interface PartSplice {
23
+ /** The root's local name. Any prefix, a self-closing root, a prolog and comments are all handled */
24
+ root: string;
25
+ /** Children put at the spot `CHILD_ORDER[root]` lays down, ahead of the first child that order puts after them */
26
+ insert?: readonly PartChild[];
27
+ /** Put in as the last children */
28
+ append?: string;
29
+ /** Put in right after the opening tag */
30
+ prepend?: string;
31
+ /** Written in place of every child */
32
+ replaceChildren?: string;
33
+ /**
34
+ * Rewrites the opening tag, handed as it is about to be written: opened when children go into a
35
+ * root that closed on itself, else as it arrived.
36
+ */
37
+ rootTag?: (open: string) => string;
38
+ }
39
+ /**
40
+ * Why the part cannot be spliced around its root, or null when it can. The same refusal
41
+ * `splicePart` throws, so a caller may ask ahead and be answered in the same words.
42
+ */
43
+ export declare function partRootProblem(xml: string, root: string): string | null;
44
+ /**
45
+ * The part with the children of `splice` put into its root, and every byte outside what was put
46
+ * in standing as it arrived. A root that closed on itself is opened to hold them.
47
+ *
48
+ * Throws `malformed-xml` naming the part when the root is not there or never closes.
49
+ */
50
+ export declare function splicePart(xml: string, splice: PartSplice): string;
51
+ /**
52
+ * The prefix the root's name carries, colon included, so a child written beside the ones the part
53
+ * holds is spelled the way the root is. Empty for a root written under the default namespace, and
54
+ * for a part with no root, which the splice that follows refuses.
55
+ */
56
+ export declare function rootPrefixOf(xml: string): string;
57
+ export interface RootDeclarations {
58
+ /** The prefixes the root has to bind, and what to bind each to where it does not yet */
59
+ namespaces: Partial<Record<KnownPrefix, string>>;
60
+ /** The tokens `mc:Ignorable` has to name, added to the ones it names already */
61
+ ignorable?: readonly string[];
62
+ }
63
+ /**
64
+ * The part with its root binding every prefix named and its `mc:Ignorable` naming every token
65
+ * asked for, each declared once: a matching declaration is preserved, and a conflicting binding is refused.
66
+ */
67
+ export declare function ensureRootDeclarations(xml: string, declarations: RootDeclarations): string;
@@ -0,0 +1,176 @@
1
+ // src/ooxml/partSplice.ts
2
+ import { childOrderOf } from "./childOrder.js";
3
+ import { attrsText } from "./element.js";
4
+ import { DocxExportError } from "./errors.js";
5
+ import { parseAttrs, readTag, rootTagAt } from "./tagScan.js";
6
+ import { localPart } from "./xml.js";
7
+ function closingTagAt(xml, open) {
8
+ let depth = 1;
9
+ let at = open.end;
10
+ for (; ; ) {
11
+ const lt = xml.indexOf("<", at);
12
+ if (lt === -1) return null;
13
+ const tag = readTag(xml, lt);
14
+ if (tag === null) return null;
15
+ if (tag.kind === "open") depth += 1;
16
+ if (tag.kind === "close") {
17
+ depth -= 1;
18
+ if (depth === 0) return tag.name === open.name ? lt : null;
19
+ }
20
+ at = tag.end;
21
+ }
22
+ }
23
+ function readRoot(xml, root) {
24
+ const openAt = rootTagAt(xml);
25
+ const open = openAt === -1 ? null : readTag(xml, openAt);
26
+ if (open === null || open.kind === "close" || localPart(open.name) !== root) {
27
+ return { problem: `the ${root} part has no ${root} root element` };
28
+ }
29
+ if (open.kind === "empty") return { root: { openAt, open, closeAt: null } };
30
+ const closeAt = closingTagAt(xml, open);
31
+ if (closeAt === null) {
32
+ return { problem: `the ${root} part has no closing ${root} tag` };
33
+ }
34
+ return { root: { openAt, open, closeAt } };
35
+ }
36
+ function partRootProblem(xml, root) {
37
+ const reading = readRoot(xml, root);
38
+ return "problem" in reading ? reading.problem : null;
39
+ }
40
+ function directChildren(inner) {
41
+ const children = [];
42
+ let depth = 0;
43
+ let at = 0;
44
+ for (; ; ) {
45
+ const lt = inner.indexOf("<", at);
46
+ if (lt === -1) return children;
47
+ const tag = readTag(inner, lt);
48
+ if (tag === null) return children;
49
+ if (depth === 0 && (tag.kind === "open" || tag.kind === "empty")) {
50
+ children.push({ name: localPart(tag.name), at: lt });
51
+ }
52
+ if (tag.kind === "open") depth += 1;
53
+ if (tag.kind === "close") depth -= 1;
54
+ at = tag.end;
55
+ }
56
+ }
57
+ function insertionAt(children, name, order, root, end) {
58
+ const target = order.indexOf(name);
59
+ if (target === -1) {
60
+ throw new Error(
61
+ `${name} is not a child the order of ${root} knows; add it to CHILD_ORDER`
62
+ );
63
+ }
64
+ const after = children.find((child) => order.indexOf(child.name) > target);
65
+ return after === void 0 ? end : after.at;
66
+ }
67
+ function withInserted(inner, inserts, root) {
68
+ if (inserts.length === 0) return inner;
69
+ const order = childOrderOf(root);
70
+ const children = directChildren(inner);
71
+ const placed = inserts.map((child) => ({
72
+ at: insertionAt(children, child.name, order, root, inner.length),
73
+ xml: child.xml,
74
+ rank: order.indexOf(child.name)
75
+ })).sort((a, b) => a.at - b.at || a.rank - b.rank);
76
+ let out = "";
77
+ let from = 0;
78
+ for (const { at, xml } of placed) {
79
+ out += inner.slice(from, at) + xml;
80
+ from = at;
81
+ }
82
+ return out + inner.slice(from);
83
+ }
84
+ function splicePart(xml, splice) {
85
+ const reading = readRoot(xml, splice.root);
86
+ if ("problem" in reading) {
87
+ throw new DocxExportError("malformed-xml", reading.problem);
88
+ }
89
+ const { openAt, open, closeAt } = reading.root;
90
+ const inner = closeAt === null ? "" : xml.slice(open.end, closeAt);
91
+ const children = (splice.prepend ?? "") + withInserted(
92
+ splice.replaceChildren ?? inner,
93
+ splice.insert ?? [],
94
+ splice.root
95
+ ) + (splice.append ?? "");
96
+ const openTag = xml.slice(openAt, open.end);
97
+ const opened = closeAt === null && children !== "" ? `${openTag.slice(0, -2)}>` : openTag;
98
+ const head = xml.slice(0, openAt) + (splice.rootTag?.(opened) ?? opened);
99
+ if (closeAt !== null) return head + children + xml.slice(closeAt);
100
+ if (children === "") return head + xml.slice(open.end);
101
+ return `${head}${children}</${open.name}>${xml.slice(open.end)}`;
102
+ }
103
+ function rootNameOf(xml) {
104
+ const at = rootTagAt(xml);
105
+ const open = at === -1 ? null : readTag(xml, at);
106
+ return open === null || open.kind === "close" ? null : open.name;
107
+ }
108
+ function rootPrefixOf(xml) {
109
+ const name = rootNameOf(xml) ?? "";
110
+ const colon = name.indexOf(":");
111
+ return colon === -1 ? "" : name.slice(0, colon + 1);
112
+ }
113
+ var IGNORABLE = /\smc:Ignorable\s*=\s*(["'])([^"']*)\1/;
114
+ function withDeclarations(openTag, { namespaces, ignorable }) {
115
+ const tag = readTag(openTag, 0);
116
+ const closeLength = tag?.kind === "empty" ? 2 : 1;
117
+ const attrs = tag === null ? null : parseAttrs(openTag.slice(tag.nameEnd, tag.end - closeLength));
118
+ if (tag === null || attrs === null) {
119
+ throw new DocxExportError(
120
+ "malformed-xml",
121
+ "the root element's attributes could not be read"
122
+ );
123
+ }
124
+ const declared = new Map(attrs);
125
+ const additions = [];
126
+ for (const [prefix, namespace] of Object.entries(namespaces)) {
127
+ if (namespace === void 0) continue;
128
+ const name = `xmlns:${prefix}`;
129
+ const existing = declared.get(name);
130
+ if (existing !== void 0 && existing !== namespace) {
131
+ throw new DocxExportError(
132
+ "unsupported-content",
133
+ `the part root binds ${prefix} to a namespace the writer cannot use`
134
+ );
135
+ }
136
+ if (existing === void 0) additions.push([name, namespace]);
137
+ }
138
+ const ignoring = IGNORABLE.exec(openTag);
139
+ let tokens = null;
140
+ if (ignorable !== void 0 && ignorable.length > 0) {
141
+ if (ignoring === null) {
142
+ additions.push(["mc:Ignorable", ignorable.join(" ")]);
143
+ } else {
144
+ const had = (attrs.find(([name]) => name === "mc:Ignorable")?.[1] ?? "").split(/\s+/).filter(Boolean);
145
+ const missing = ignorable.filter((token) => !had.includes(token));
146
+ if (missing.length > 0) tokens = [...had, ...missing];
147
+ }
148
+ }
149
+ let next = openTag;
150
+ if (ignoring !== null && tokens !== null) {
151
+ const rewritten = ` mc:Ignorable=${ignoring[1]}${tokens.join(" ")}${ignoring[1]}`;
152
+ next = next.slice(0, ignoring.index) + rewritten + next.slice(ignoring.index + ignoring[0].length);
153
+ }
154
+ if (additions.length === 0) return next;
155
+ const end = next.length - closeLength;
156
+ return `${next.slice(0, end)} ${attrsText(additions)}${next.slice(end)}`;
157
+ }
158
+ function ensureRootDeclarations(xml, declarations) {
159
+ const root = rootNameOf(xml);
160
+ if (root === null) {
161
+ throw new DocxExportError(
162
+ "malformed-xml",
163
+ "the part has no root element to declare a namespace on"
164
+ );
165
+ }
166
+ return splicePart(xml, {
167
+ root: localPart(root),
168
+ rootTag: (open) => withDeclarations(open, declarations)
169
+ });
170
+ }
171
+ export {
172
+ ensureRootDeclarations,
173
+ partRootProblem,
174
+ rootPrefixOf,
175
+ splicePart
176
+ };
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Handles the XML fragments that carry formatting, such as `<w:rPr>`, `<w:pPr>`, and `<w:tcPr>`.
3
+ *
4
+ * A fragment is never rebuilt: only the one child a job names is sliced out and swapped, so the
5
+ * rest (borders, shading, margins) stays as the original text wrote it. OOXML lays down the order
6
+ * of the children, so the spot to insert a child that was not there is found by that same order.
7
+ */
8
+ import { type XmlAttr } from "./element";
9
+ export interface PropsChild {
10
+ /** The name with its namespace prefix stripped off (e.g. `gridSpan`) */
11
+ name: string;
12
+ /** This child's original XML fragment exactly as it was */
13
+ xml: string;
14
+ /**
15
+ * Whatever stood between the child before this one and this one: line breaks a producer laid
16
+ * out, comments, anything that is not an element. Carried so that rewriting one child does not
17
+ * quietly drop the rest of what the fragment said. Absent rather than empty when nothing did.
18
+ */
19
+ before?: string;
20
+ }
21
+ export interface Props {
22
+ /** The opening tag name exactly as written (e.g. `w:tcPr`) */
23
+ tag: string;
24
+ attrs: string | null;
25
+ children: PropsChild[];
26
+ /** The same, for what stood between the last child and the closing tag */
27
+ tail?: string;
28
+ }
29
+ /**
30
+ * What stood inside a single element, and "" for one that stood empty or cannot be made out.
31
+ *
32
+ * The opening tag is read rather than scanned for, so a `>` inside an attribute value does not
33
+ * pass for the end of it.
34
+ */
35
+ export declare function innerXml(xml: string): string;
36
+ /**
37
+ * Splits a formatting fragment into its opening tag and its list of children.
38
+ * null if its shape cannot be made out (in which case leaving the original untouched is the safe move).
39
+ */
40
+ export declare function parseProps(xml: string): Props | null;
41
+ /**
42
+ * With nothing to say, the formatting fragment itself is not written. A fragment holding only
43
+ * whitespace says nothing; one holding a comment does, so it is written back.
44
+ */
45
+ export declare function renderProps(props: Props): string;
46
+ /**
47
+ * Replaces a single child with new XML.
48
+ *
49
+ * A null `xml` removes that child. A child that was not there goes into the spot `CHILD_ORDER`
50
+ * lays down for this fragment's own tag, so the caller names what it is writing and not where it
51
+ * goes. Naming the element this fragment stands inside picks the right order where one element
52
+ * carries children in a different order under a different parent (`pPr/rPr`).
53
+ */
54
+ export declare function setChild(props: Props, name: string, xml: string | null, parent?: string): Props;
55
+ export declare function propsChild(children: readonly PropsChild[], name: string): PropsChild | undefined;
56
+ /**
57
+ * The opening tag's attributes as pairs, and null when their shape cannot be made out.
58
+ *
59
+ * The pairs are worked out on demand rather than kept on `Props`, because an untouched fragment
60
+ * has to go back out with the spacing, the quoting and the escaping its producer chose. Only a
61
+ * fragment something writes to passes through `withAttrs`, which is where they are written again.
62
+ */
63
+ export declare function attrsOf(props: Props): XmlAttr[] | null;
64
+ /** The fragment with these attributes written in place of the ones its opening tag carried */
65
+ export declare function withAttrs(props: Props, attrs: readonly XmlAttr[]): Props;
66
+ /**
67
+ * One child of a fragment as it was written: the tag, prefix included, so an edited child keeps
68
+ * the spelling the document chose, and its attributes. A child that is not there has neither, so
69
+ * no tag with attributes hanging off it can be made.
70
+ */
71
+ export type ChildElement = {
72
+ tag: string;
73
+ attrs: readonly XmlAttr[];
74
+ } | {
75
+ tag: null;
76
+ attrs: readonly [];
77
+ };
78
+ /**
79
+ * The tag and attributes of one child. A child that is not there reads as no tag and no
80
+ * attributes, and null says its shape cannot be made out, which leaves the caller to back out
81
+ * rather than write over markup it could not read.
82
+ */
83
+ export declare function childElement(props: Props, name: string): ChildElement | null;
84
+ /**
85
+ * Always writes the element, closing it on its own when it holds nothing. Whitespace alone counts
86
+ * as nothing, the same as `renderProps` reads it, so a pretty-printed fragment emptied of its
87
+ * children collapses rather than keeping the line breaks that stood between them.
88
+ */
89
+ export declare function renderElement(props: Props): string;
90
+ /**
91
+ * An element whose children go in the order `CHILD_ORDER` lays down, whatever order they are
92
+ * handed in. This is how a fragment written from scratch follows the same order an edited one
93
+ * is held to, and a child the order does not know is refused the same way.
94
+ */
95
+ export declare function orderedElement(tag: string, attrs: readonly XmlAttr[], children: readonly PropsChild[]): string;
96
+ /**
97
+ * Edits one child down a path of names, parsing only the fragments the path runs through and
98
+ * leaving every other child as the original text wrote it.
99
+ *
100
+ * The callback is handed the child the path names, null for one that is not there, and answers
101
+ * with what it is to become, null to take it away. A container along the path that was not there
102
+ * is written, and one the edit leaves empty is taken away with it. null when a fragment on the
103
+ * path cannot be made out, which leaves the caller to back out.
104
+ */
105
+ export declare function editChild(props: Props, path: readonly [string, ...string[]], edit: (child: Props | null) => Props | null): Props | null;
106
+ /**
107
+ * Reads a single formatting fragment into an element.
108
+ *
109
+ * A fragment carries no namespace declarations, so they are put back on as it is wrapped.
110
+ * null if its shape cannot be made out, in which case the caller leaves the display values alone.
111
+ */
112
+ export declare function parsePropsXml(xml: string): Element | null;