@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
@@ -1,30 +1,42 @@
1
1
  import { type ParagraphAlign } from "../model/format";
2
+ import { type HexColor } from "./simpleTypes";
2
3
  /** The alignment `w:jc` writes down, both for a paragraph and for a table */
3
4
  export declare const ALIGN_BY_JC: Record<string, ParagraphAlign>;
4
5
  /** A color as the document writes it, six hex digits and no `#`. Null for anything else */
5
6
  export declare function normalizeHex(value: string): string | null;
6
7
  export declare function wAttr(el: Element, name: string): string | null;
8
+ /**
9
+ * Whether a boolean property element states on (§17.17.4).
10
+ *
11
+ * An element carrying no `w:val` states on, and one that is not there at all states nothing, which
12
+ * a caller asking "is this on" reads as off. For malformed values, this project uses the same
13
+ * fallback as an absent attribute. That recovery policy is not a schema or Word guarantee.
14
+ */
15
+ export declare function isOnElement(el: Element | null): boolean;
7
16
  /** `<w:b/>` means on, `<w:b w:val="0"/>` means off */
8
17
  export declare function isOn(parent: Element, name: string): boolean;
9
18
  export declare function childValue(parent: Element, name: string): string | null;
10
- export declare function toNumber(value: string | null): number | null;
11
19
  /** Cuts off the messy digits after the decimal point so the same input always yields the same value */
12
20
  export declare function round(value: number): number;
13
21
  /** twip (1/20 of a point) */
14
- export declare function twipsToPt(value: string | null): number | null;
22
+ export declare function twipsToPt(twips: number | null): number | null;
15
23
  /** half-point (w:sz 20 = 10pt) */
16
- export declare function halfPointsToPt(value: string | null): number | null;
17
- /** 1/8 of a point */
18
- export declare function eighthsToPt(value: string | null): number | null;
24
+ export declare function halfPointsToPt(half: number | null): number | null;
25
+ /** The color as CSS spells it. `auto` is not a color to paint with, so it reads as none */
19
26
  export declare function toHexColor(value: string | null): string | null;
20
- /**
21
- * Moves one border side into a CSS value.
22
- *
23
- * A side the document has pinned down as "draw no line here" becomes `none`.
24
- * A kind we do not know, or a side that is not there at all, becomes null, so the caller can pick a default.
25
- * A side that draws a line but writes down no thickness gets the default one: reading it as "no
26
- * line" would take away a line the style laid down.
27
- */
27
+ /** One border side as `CT_Border` records it (§17.3.4) */
28
+ export interface BorderLine {
29
+ /** ST_Border (§17.18.2). `none` states that this side draws nothing */
30
+ val: string;
31
+ /** The thickness, in eighths of a point */
32
+ eighths: number;
33
+ color: HexColor;
34
+ }
35
+ /** The CSS declaration one line is drawn with, and null for a line there is none of */
36
+ export declare function borderLineCss(line: BorderLine | null): string | null;
37
+ /** The line a CSS declaration this module wrote stands for, so no caller reads one back its own way */
38
+ export declare function borderLineOfCss(css: string | null): BorderLine | null;
39
+ /** Moves one border side into a CSS value */
28
40
  export declare function borderCss(el: Element | null): string | null;
29
41
  /** One side inside `w:tblBorders` or `w:tcBorders` */
30
42
  export declare function borderSide(borders: Element | null, side: string): string | null;
@@ -1,5 +1,13 @@
1
1
  // src/ooxml/units.ts
2
2
  import { NO_FILL } from "../model/format.js";
3
+ import {
4
+ EIGHTHS_PER_PT,
5
+ HALF_POINTS_PER_PT,
6
+ ST_EighthPointMeasure,
7
+ ST_HexColor,
8
+ ST_OnOff,
9
+ TWIPS_PER_PT
10
+ } from "./simpleTypes.js";
3
11
  import { childByLocalName, W_NS } from "./xml.js";
4
12
  var ALIGN_BY_JC = {
5
13
  left: "left",
@@ -17,39 +25,29 @@ function normalizeHex(value) {
17
25
  function wAttr(el, name) {
18
26
  return el.getAttributeNS(W_NS, name) ?? el.getAttribute(name);
19
27
  }
20
- function isOn(parent, name) {
21
- const el = childByLocalName(parent, name);
28
+ function isOnElement(el) {
22
29
  if (!el) return false;
23
- const value = wAttr(el, "val");
24
- return value === null || value === "1" || value === "true" || value === "on";
30
+ return ST_OnOff.parse(wAttr(el, "val")) ?? true;
31
+ }
32
+ function isOn(parent, name) {
33
+ return isOnElement(childByLocalName(parent, name));
25
34
  }
26
35
  function childValue(parent, name) {
27
36
  const el = childByLocalName(parent, name);
28
37
  return el ? wAttr(el, "val") : null;
29
38
  }
30
- function toNumber(value) {
31
- if (value === null) return null;
32
- const parsed = Number.parseFloat(value);
33
- return Number.isFinite(parsed) ? parsed : null;
34
- }
35
39
  function round(value) {
36
40
  return Math.round(value * 100) / 100;
37
41
  }
38
- function twipsToPt(value) {
39
- const twips = toNumber(value);
40
- return twips === null ? null : round(twips / 20);
41
- }
42
- function halfPointsToPt(value) {
43
- const half = toNumber(value);
44
- return half === null ? null : round(half / 2);
42
+ function twipsToPt(twips) {
43
+ return twips === null ? null : round(twips / TWIPS_PER_PT);
45
44
  }
46
- function eighthsToPt(value) {
47
- const eighths = toNumber(value);
48
- return eighths === null ? null : round(eighths / 8);
45
+ function halfPointsToPt(half) {
46
+ return half === null ? null : round(half / HALF_POINTS_PER_PT);
49
47
  }
50
48
  function toHexColor(value) {
51
- if (value === null || value === "auto") return null;
52
- return /^[0-9a-fA-F]{6}$/.test(value) ? `#${value}` : null;
49
+ const color = ST_HexColor.parse(value);
50
+ return color?.kind === "rgb" ? `#${color.hex}` : null;
53
51
  }
54
52
  var BORDER_STYLE_BY_VAL = {
55
53
  single: "solid",
@@ -78,17 +76,52 @@ var BORDER_STYLE_BY_VAL = {
78
76
  dotDotDash: "dashed",
79
77
  dashDotStroked: "dashed"
80
78
  };
81
- var FALLBACK_BORDER_PT = 0.5;
82
- function borderCss(el) {
79
+ var BORDER_VAL_BY_STYLE = {
80
+ solid: "single",
81
+ double: "double",
82
+ dashed: "dashed",
83
+ dotted: "dotted"
84
+ };
85
+ var FALLBACK_BORDER_EIGHTHS = 4;
86
+ var DEFAULT_BORDER_CSS_COLOR = "#000000";
87
+ var NO_LINE = {
88
+ val: "none",
89
+ eighths: 0,
90
+ color: { kind: "auto" }
91
+ };
92
+ function borderLineOf(el) {
83
93
  if (!el) return null;
84
94
  const val = wAttr(el, "val") ?? "";
85
- if (val === "nil" || val === "none") return "none";
86
- const style = BORDER_STYLE_BY_VAL[val];
95
+ if (val === "nil" || val === "none") return NO_LINE;
96
+ if (!BORDER_STYLE_BY_VAL[val]) return null;
97
+ const written = ST_EighthPointMeasure.parse(wAttr(el, "sz"));
98
+ return {
99
+ val,
100
+ eighths: written === null || written === 0 ? FALLBACK_BORDER_EIGHTHS : written,
101
+ color: ST_HexColor.parse(wAttr(el, "color")) ?? { kind: "auto" }
102
+ };
103
+ }
104
+ function borderLineCss(line) {
105
+ if (!line) return null;
106
+ if (line.val === "none") return "none";
107
+ const style = BORDER_STYLE_BY_VAL[line.val];
87
108
  if (!style) return null;
88
- const written = eighthsToPt(wAttr(el, "sz"));
89
- const widthPt = written === null || written === 0 ? FALLBACK_BORDER_PT : written;
90
- const color = toHexColor(wAttr(el, "color")) ?? "#000000";
91
- return `${widthPt}pt ${style} ${color}`;
109
+ const color = line.color.kind === "rgb" ? `#${line.color.hex}` : DEFAULT_BORDER_CSS_COLOR;
110
+ return `${round(line.eighths / EIGHTHS_PER_PT)}pt ${style} ${color}`;
111
+ }
112
+ var BORDER_CSS = /^(\d+(?:\.\d+)?)pt (solid|double|dashed|dotted) (#[0-9a-f]{6})$/i;
113
+ function borderLineOfCss(css) {
114
+ if (css === null) return null;
115
+ if (css === "none") return NO_LINE;
116
+ const matched = BORDER_CSS.exec(css);
117
+ if (!matched) return null;
118
+ const eighths = Math.round(Number(matched[1]) * EIGHTHS_PER_PT);
119
+ const color = ST_HexColor.parse(matched[3].slice(1).toUpperCase());
120
+ if (eighths <= 0 || color === null) return null;
121
+ return { val: BORDER_VAL_BY_STYLE[matched[2].toLowerCase()], eighths, color };
122
+ }
123
+ function borderCss(el) {
124
+ return borderLineCss(borderLineOf(el));
92
125
  }
93
126
  function borderSide(borders, side) {
94
127
  return borders ? borderCss(childByLocalName(borders, side)) : null;
@@ -101,16 +134,17 @@ function shadingOf(parent) {
101
134
  export {
102
135
  ALIGN_BY_JC,
103
136
  borderCss,
137
+ borderLineCss,
138
+ borderLineOfCss,
104
139
  borderSide,
105
140
  childValue,
106
- eighthsToPt,
107
141
  halfPointsToPt,
108
142
  isOn,
143
+ isOnElement,
109
144
  normalizeHex,
110
145
  round,
111
146
  shadingOf,
112
147
  toHexColor,
113
- toNumber,
114
148
  twipsToPt,
115
149
  wAttr
116
150
  };
@@ -5,6 +5,14 @@ export declare const W_NS = "http://schemas.openxmlformats.org/wordprocessingml/
5
5
  * every relationship type name is built on
6
6
  */
7
7
  export declare const R_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
8
+ /**
9
+ * The prefixes whose meaning the editor depends on, and the namespace each one has to carry.
10
+ *
11
+ * Every element the writer spells out is a `w:` one, and the relationship a link or an image points
12
+ * at is named by `r:id` or `r:embed`, so a fragment that bound either prefix elsewhere would leave
13
+ * the writer's own markup meaning something else.
14
+ */
15
+ export declare const RESERVED_PREFIXES: ReadonlyMap<string, string>;
8
16
  /** The name with its namespace prefix stripped off (`w:ascii` -> `ascii`) */
9
17
  export declare function localPart(name: string): string;
10
18
  /**
@@ -21,11 +29,55 @@ export declare function decodeUtf8(bytes: Uint8Array): {
21
29
  hadBom: boolean;
22
30
  };
23
31
  export declare function encodeUtf8(text: string, withBom: boolean): Uint8Array;
32
+ /**
33
+ * What reading a package part asks of a runtime: an XML string in, a document out.
34
+ *
35
+ * A browser's `DOMParser` is one, and so is a `DOMParser` from jsdom. The package ships none of
36
+ * them.
37
+ *
38
+ * Markup it cannot read may be answered either way a parser answers one: by handing back a
39
+ * document holding a `parsererror` element, or by throwing. Both are read as `malformed-xml`.
40
+ */
41
+ export interface XmlParser {
42
+ parseFromString(source: string, type: "application/xml"): Document;
43
+ }
44
+ /**
45
+ * Runs `work` with every `parseXml` inside it reading through the one parser settled on here.
46
+ *
47
+ * Settling it at the boundary rather than at each read is what lets an entry point turn a runtime
48
+ * holding no parser down before it has read anything, instead of wherever the first part happens
49
+ * to be parsed; it also means one parser serves the whole call rather than a fresh `DOMParser`
50
+ * being built for every part.
51
+ *
52
+ * The public entry points are synchronous, so the scope covers exactly the work one of them does
53
+ * and nothing that runs after it. `undefined` keeps the enclosing scope's parser rather than
54
+ * clearing it, which is what lets an entry point opening a file through another one - the verifier
55
+ * running two imports - hand its own parser down without every inner call having to carry it.
56
+ */
57
+ export declare function withXmlParser<T>(parser: XmlParser | undefined, work: () => T): T;
24
58
  export declare function parseXml(source: string): Document;
59
+ /**
60
+ * Gathers the namespace prefixes used in the fragment and declares them.
61
+ * Only `w` carries real meaning; the rest are placeholders that keep the parser from stopping.
62
+ * All we read are element names and `w:` attributes, so placeholders still let the values be read as they are.
63
+ *
64
+ * An attribute standing at the very start of the string counts too, since a fragment may be an
65
+ * attribute list of its own (`attrString`) rather than an element.
66
+ */
67
+ export declare function namespaceDecls(xml: string): string;
25
68
  export declare function elementChildren(el: Element): Element[];
69
+ /**
70
+ * The value of the attribute with this local name, whatever prefix it was written under.
71
+ *
72
+ * A document is free to bind the WordprocessingML namespace to a prefix of its own, so an
73
+ * attribute is looked up by the name it carries rather than by the spelling a producer chose.
74
+ */
75
+ export declare function attributeByLocalName(el: Element, localName: string): string | null;
26
76
  export declare function childByLocalName(el: Element, name: string): Element | null;
27
77
  /** Turns an opening tag's attributes into an `a="b" c="d"` string */
28
78
  export declare function attrString(el: Element): string | null;
79
+ /** Whether this node is an element */
80
+ export declare function isElement(node: Node): node is Element;
29
81
  /**
30
82
  * Turns a single element back into an XML string.
31
83
  *
package/dist/ooxml/xml.js CHANGED
@@ -2,6 +2,10 @@
2
2
  import { DocxImportError } from "./errors.js";
3
3
  var W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
4
4
  var R_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
5
+ var RESERVED_PREFIXES = /* @__PURE__ */ new Map([
6
+ ["w", W_NS],
7
+ ["r", R_NS]
8
+ ]);
5
9
  function localPart(name) {
6
10
  const colon = name.indexOf(":");
7
11
  return colon === -1 ? name : name.slice(colon + 1);
@@ -32,19 +36,65 @@ function declaresDtd(source) {
32
36
  at = closes;
33
37
  }
34
38
  }
39
+ var scopedParser;
40
+ function resolveParser(parser) {
41
+ const named = parser ?? scopedParser;
42
+ if (named) return named;
43
+ if (typeof DOMParser === "function") return new DOMParser();
44
+ throw new DocxImportError(
45
+ "no-xml-parser",
46
+ "no XML parser: pass `xmlParser` or install a DOMParser global"
47
+ );
48
+ }
49
+ function withXmlParser(parser, work) {
50
+ const enclosing = scopedParser;
51
+ scopedParser = resolveParser(parser);
52
+ try {
53
+ return work();
54
+ } finally {
55
+ scopedParser = enclosing;
56
+ }
57
+ }
35
58
  function parseXml(source) {
36
59
  if (declaresDtd(source)) {
37
60
  throw new DocxImportError("malformed-xml", "the XML declares a DTD");
38
61
  }
39
- const doc = new DOMParser().parseFromString(source, "application/xml");
62
+ let doc;
63
+ try {
64
+ doc = resolveParser(void 0).parseFromString(source, "application/xml");
65
+ } catch (cause) {
66
+ if (cause instanceof DocxImportError) throw cause;
67
+ throw new DocxImportError("malformed-xml", "could not parse the XML", {
68
+ cause
69
+ });
70
+ }
40
71
  if (doc.getElementsByTagName("parsererror").length > 0) {
41
72
  throw new DocxImportError("malformed-xml", "could not parse the XML");
42
73
  }
43
74
  return doc;
44
75
  }
76
+ function namespaceDecls(xml) {
77
+ const prefixes = /* @__PURE__ */ new Set(["w"]);
78
+ for (const [, prefix] of xml.matchAll(/<\/?([A-Za-z_][\w.-]*):/g)) {
79
+ prefixes.add(prefix);
80
+ }
81
+ for (const [, prefix] of xml.matchAll(
82
+ /(?:^|[\s"'])([A-Za-z_][\w.-]*):[\w.-]+=/g
83
+ )) {
84
+ prefixes.add(prefix);
85
+ }
86
+ prefixes.delete("xml");
87
+ prefixes.delete("xmlns");
88
+ return Array.from(prefixes).map(
89
+ (prefix) => `xmlns:${prefix}="${prefix === "w" ? W_NS : `urn:docx-editor:${prefix}`}"`
90
+ ).join(" ");
91
+ }
45
92
  function elementChildren(el) {
46
93
  return Array.from(el.children);
47
94
  }
95
+ function attributeByLocalName(el, localName) {
96
+ return Array.from(el.attributes).find((entry) => entry.localName === localName)?.value ?? null;
97
+ }
48
98
  function childByLocalName(el, name) {
49
99
  return elementChildren(el).find((child) => child.localName === name) ?? null;
50
100
  }
@@ -52,15 +102,19 @@ function attrString(el) {
52
102
  if (el.attributes.length === 0) return null;
53
103
  return Array.from(el.attributes).map((attr) => `${attr.name}="${escapeXml(attr.value)}"`).join(" ");
54
104
  }
105
+ var ELEMENT_NODE = 1;
106
+ var TEXT_NODE = 3;
107
+ var CDATA_SECTION_NODE = 4;
108
+ var COMMENT_NODE = 8;
55
109
  function isElement(node) {
56
- return node.nodeType === Node.ELEMENT_NODE;
110
+ return node.nodeType === ELEMENT_NODE;
57
111
  }
58
112
  function serializeChildNode(node) {
59
113
  if (isElement(node)) return serializeXml(node);
60
- if (node.nodeType === Node.TEXT_NODE || node.nodeType === Node.CDATA_SECTION_NODE) {
114
+ if (node.nodeType === TEXT_NODE || node.nodeType === CDATA_SECTION_NODE) {
61
115
  return escapeXml(node.nodeValue ?? "");
62
116
  }
63
- if (node.nodeType === Node.COMMENT_NODE) {
117
+ if (node.nodeType === COMMENT_NODE) {
64
118
  return `<!--${node.nodeValue ?? ""}-->`;
65
119
  }
66
120
  throw new DocxImportError(
@@ -75,15 +129,20 @@ function serializeXml(el) {
75
129
  return inner ? `${open}>${inner}</${el.nodeName}>` : `${open}/>`;
76
130
  }
77
131
  export {
132
+ RESERVED_PREFIXES,
78
133
  R_NS,
79
134
  W_NS,
80
135
  attrString,
136
+ attributeByLocalName,
81
137
  childByLocalName,
82
138
  decodeUtf8,
83
139
  elementChildren,
84
140
  encodeUtf8,
85
141
  escapeXml,
142
+ isElement,
86
143
  localPart,
144
+ namespaceDecls,
87
145
  parseXml,
88
- serializeXml
146
+ serializeXml,
147
+ withXmlParser
89
148
  };
@@ -0,0 +1,103 @@
1
+ /**
2
+ * What the page engine knows about a block, whatever shape of block it is.
3
+ *
4
+ * A measurer says where a block may be parted and how much of it has to fit on the page it
5
+ * starts on; the layout (`page/pageLayout`) answers with the places it decided to part it at.
6
+ * Neither side names a paragraph or a table.
7
+ *
8
+ * Which measurer a block gets, and what draws the cuts it was given, is the block's kind
9
+ * (`page/kinds`).
10
+ */
11
+ import type { Node as PMNode } from "prosemirror-model";
12
+ import type { Decoration, EditorView } from "prosemirror-view";
13
+ /** A place inside a block where the next page may, or must, start */
14
+ export interface BreakCandidate {
15
+ /**
16
+ * The document position the continued piece starts at: a page `br` for a paragraph, a row for a
17
+ * table. A position maps through an edit, which is why the marks are kept by position and not
18
+ * by ordinal (see `page/pageDecorations`)
19
+ */
20
+ at: number;
21
+ /** The top of the continued piece, measured from the block's natural top with no space in it */
22
+ offset: number;
23
+ /** A forced candidate always cuts; an optional one cuts only when the piece after it would overflow */
24
+ forced: boolean;
25
+ /** Height the continued piece carries onto the next page ahead of its own content (a repeated header) */
26
+ repeatHeight: number;
27
+ }
28
+ /** One body block as drawn on screen */
29
+ export interface MeasuredBlock {
30
+ /** The position where this block starts in the document */
31
+ pos: number;
32
+ /** The height that naturally opens up between the previous block and this one */
33
+ gap: number;
34
+ height: number;
35
+ /** The document records that a new page starts at this block */
36
+ breakBefore: boolean;
37
+ /** The block holds a break it cannot open a space at (inside a cell), so the next block starts a page */
38
+ breakAfter: boolean;
39
+ candidates: readonly BreakCandidate[];
40
+ /** The smallest piece that has to fit on the page the block starts on */
41
+ minFirstPiece: number;
42
+ /** The document asks for this block to stand on the same page as the start of the block after it */
43
+ keepWithNext: boolean;
44
+ }
45
+ /** A page cut the layout decided on: the space opened before the continued piece */
46
+ export interface PageCut {
47
+ at: number;
48
+ height: number;
49
+ }
50
+ /** One block as it stands on the sheet, handed to the kind about to measure it */
51
+ export interface MeasureTarget {
52
+ view: EditorView;
53
+ node: PMNode;
54
+ pos: number;
55
+ dom: HTMLElement;
56
+ /**
57
+ * Sheet coordinates from viewport coordinates: the visual scale taken out, and everything the
58
+ * engine has opened up above this block taken back off
59
+ */
60
+ sheetY(viewportY: number): number;
61
+ /** The block's natural top: pushes and spaces opened above it already taken back off */
62
+ top: number;
63
+ /** The visual scale the sheet is drawn at, which a length read off the screen is divided by */
64
+ scale: number;
65
+ }
66
+ /** What a kind has to say about the block it measured */
67
+ export interface KindMeasure {
68
+ candidates: readonly BreakCandidate[];
69
+ /** The smallest piece that has to fit on the page the block starts on */
70
+ minFirstPiece: number;
71
+ /** The block holds a break it could not open a space at, so the next block starts a page */
72
+ breakAfter: boolean;
73
+ /** Height the engine's own marks add inside this block, taken off its measured bottom */
74
+ appliedHeight: number;
75
+ /**
76
+ * Whether the document asks for this block to stand on the same page as the start of the block
77
+ * after it. A kind whose blocks never do leaves it out
78
+ */
79
+ keepWithNext?: boolean;
80
+ }
81
+ /**
82
+ * Everything the engine does with one shape of breakable block: how one is measured, and how the
83
+ * cuts the layout gave it are drawn.
84
+ *
85
+ * A shape the engine learns later - a footnote area, a paragraph with a floating object hanging
86
+ * out of it - is one more kind rather than another branch in the measurer, in the decorations
87
+ * and in the plugin state at once.
88
+ */
89
+ export interface BlockKind {
90
+ readonly name: string;
91
+ matches(node: PMNode): boolean;
92
+ measure(target: MeasureTarget): KindMeasure;
93
+ /** Whether `at` still names a place this kind can cut at in `doc`; a false drops the cut */
94
+ holdsCut(doc: PMNode, at: number): boolean;
95
+ /**
96
+ * The decorations one block of this kind carries: the cuts the layout gave it, plus whatever it
97
+ * needs before any measurement (a paragraph puts an empty space on every page `br`, so the
98
+ * measurement has an element to read it off).
99
+ */
100
+ decorate(pos: number, node: PMNode, cuts: readonly PageCut[], into: Decoration[]): void;
101
+ }
102
+ /** The first kind whose `matches` answers. The kind registered last has to match every block */
103
+ export declare function blockKindFor(kinds: readonly BlockKind[], node: PMNode): BlockKind;
@@ -0,0 +1,11 @@
1
+ // src/page/blockKinds.ts
2
+ function blockKindFor(kinds, node) {
3
+ const found = kinds.find((kind) => kind.matches(node));
4
+ if (!found) {
5
+ throw new Error(`no block kind matches a ${node.type.name} block`);
6
+ }
7
+ return found;
8
+ }
9
+ export {
10
+ blockKindFor
11
+ };
@@ -0,0 +1,6 @@
1
+ import type { BlockKind } from "../blockKinds";
2
+ /**
3
+ * The block shapes the editor pages, in the order they are asked. A new kind goes ahead of the
4
+ * kinds it is narrower than, and the last one claims every block none of the others did.
5
+ */
6
+ export declare const DEFAULT_BLOCK_KINDS: readonly BlockKind[];
@@ -0,0 +1,10 @@
1
+ // src/page/kinds/index.ts
2
+ import { paragraphKind } from "./paragraphKind.js";
3
+ import { tableKind } from "./tableKind.js";
4
+ var DEFAULT_BLOCK_KINDS = [
5
+ tableKind,
6
+ paragraphKind
7
+ ];
8
+ export {
9
+ DEFAULT_BLOCK_KINDS
10
+ };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The kind every block falls back to: a paragraph, and any other shape no kind ahead of it claims.
3
+ *
4
+ * A page `br` is the one place a paragraph is parted at, and the space opened at it is what the
5
+ * measurement reads the break's place off, so every `br` carries a space of its own before
6
+ * anything is measured. A break standing where no space can be opened - inside a table cell,
7
+ * where it would grow the cell rather than the page - is answered by the whole block instead.
8
+ */
9
+ import type { BlockKind } from "../blockKinds";
10
+ export declare const paragraphKind: BlockKind;
@@ -0,0 +1,71 @@
1
+ // src/page/kinds/paragraphKind.ts
2
+ import { Decoration } from "prosemirror-view";
3
+ import { toParagraphFormat } from "../../model/format.js";
4
+ import { docxSchema, isPageBreak } from "../../schema/index.js";
5
+ import { editorAttributes } from "../../styles/classNames.js";
6
+ var PAGE_BREAK_BR = `br[${editorAttributes.breakType}="page"]`;
7
+ var BREAK_SPACE = `[${editorAttributes.pageBreakSpace}]`;
8
+ function spaceStyle(height) {
9
+ return `display:block;height:${height}px`;
10
+ }
11
+ function isPageBreakNode(node) {
12
+ return node?.type === docxSchema.nodes.hardBreak && isPageBreak(node.attrs.brAttrs);
13
+ }
14
+ function pageBreaksIn(block, blockPos) {
15
+ const found = [];
16
+ if (block.type !== docxSchema.nodes.paragraph) return found;
17
+ block.forEach((child, offset) => {
18
+ if (isPageBreakNode(child)) {
19
+ found.push({ at: blockPos + 1 + offset, size: child.nodeSize });
20
+ }
21
+ });
22
+ return found;
23
+ }
24
+ var paragraphKind = {
25
+ name: "paragraph",
26
+ matches: () => true,
27
+ measure({ node, pos, dom, sheetY, top, scale }) {
28
+ const breaks = pageBreaksIn(node, pos);
29
+ const candidates = [];
30
+ let appliedHeight = 0;
31
+ dom.querySelectorAll(BREAK_SPACE).forEach((space, index) => {
32
+ const box = space.getBoundingClientRect();
33
+ const found = breaks.at(index);
34
+ if (found) {
35
+ candidates.push({
36
+ at: found.at,
37
+ offset: sheetY(box.top) - appliedHeight - top,
38
+ forced: true,
39
+ repeatHeight: 0
40
+ });
41
+ }
42
+ appliedHeight += box.height / scale;
43
+ });
44
+ return {
45
+ candidates,
46
+ minFirstPiece: candidates[0]?.offset ?? dom.getBoundingClientRect().height / scale - appliedHeight,
47
+ // A break with no space of its own sits somewhere the space could not be opened. Those
48
+ // still start a new page, but only after the whole block
49
+ breakAfter: dom.querySelectorAll(PAGE_BREAK_BR).length > candidates.length,
50
+ appliedHeight,
51
+ keepWithNext: toParagraphFormat(node.attrs.format)?.keepNext === true
52
+ };
53
+ },
54
+ holdsCut: (doc, at) => isPageBreakNode(doc.nodeAt(at)),
55
+ decorate(pos, node, cuts, into) {
56
+ const heights = new Map(cuts.map((cut) => [cut.at, cut.height]));
57
+ for (const { at, size } of pageBreaksIn(node, pos)) {
58
+ const height = heights.get(at) ?? 0;
59
+ into.push(
60
+ Decoration.inline(at, at + size, {
61
+ nodeName: "span",
62
+ style: spaceStyle(height),
63
+ [editorAttributes.pageBreakSpace]: `${height}`
64
+ })
65
+ );
66
+ }
67
+ }
68
+ };
69
+ export {
70
+ paragraphKind
71
+ };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The kind of a table: it is parted between rows, and a continued page opens a spacer row and
3
+ * repeats the headers the table opens with.
4
+ *
5
+ * Both sides read the table as it stands on screen without treating the rows this very engine
6
+ * drew as content: the measurement takes their heights back off, and the projection is derived
7
+ * again from the document, so an edit to a header row redraws it with nothing measured anew.
8
+ */
9
+ import type { BlockKind } from "../blockKinds";
10
+ export declare const tableKind: BlockKind;