@portone/docx-editor 0.2.1 → 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 (225) hide show
  1. package/CHANGELOG.md +109 -0
  2. package/CONTRIBUTING.md +5 -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 +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 +52 -111
  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 +69 -71
  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 +27 -0
  33. package/dist/docx/formatting/attrs.js +31 -0
  34. package/dist/docx/formatting/context.d.ts +31 -0
  35. package/dist/docx/formatting/context.js +42 -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 +45 -0
  39. package/dist/docx/formatting/resolve.js +140 -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.js +3 -3
  43. package/dist/docx/formatting/tabStops.js +7 -22
  44. package/dist/docx/formatting.d.ts +4 -1
  45. package/dist/docx/formatting.js +4 -1
  46. package/dist/docx/headersFooters.js +6 -13
  47. package/dist/docx/identities.d.ts +67 -0
  48. package/dist/docx/identities.js +174 -0
  49. package/dist/docx/importDocx.d.ts +12 -1
  50. package/dist/docx/importDocx.js +72 -79
  51. package/dist/docx/importParagraph.d.ts +1 -1
  52. package/dist/docx/importParagraph.js +4 -3
  53. package/dist/docx/importTable.d.ts +1 -1
  54. package/dist/docx/importTable.js +17 -1
  55. package/dist/docx/invariants.d.ts +33 -0
  56. package/dist/docx/invariants.js +256 -0
  57. package/dist/docx/media.d.ts +6 -4
  58. package/dist/docx/media.js +6 -37
  59. package/dist/docx/newLists.d.ts +20 -0
  60. package/dist/docx/newLists.js +36 -0
  61. package/dist/docx/notes.js +3 -7
  62. package/dist/docx/numberingPlanner.d.ts +8 -0
  63. package/dist/docx/numberingPlanner.js +19 -0
  64. package/dist/docx/packageParts.d.ts +42 -0
  65. package/dist/docx/packageParts.js +135 -0
  66. package/dist/docx/pageGeometry.d.ts +2 -0
  67. package/dist/docx/pageGeometry.js +18 -9
  68. package/dist/docx/paraProps.d.ts +10 -22
  69. package/dist/docx/paraProps.js +47 -76
  70. package/dist/docx/partPlan.d.ts +36 -0
  71. package/dist/docx/partPlan.js +59 -0
  72. package/dist/docx/protectionPolicy.d.ts +127 -0
  73. package/dist/docx/protectionPolicy.js +169 -0
  74. package/dist/docx/relationships.d.ts +1 -1
  75. package/dist/docx/relationships.js +8 -13
  76. package/dist/docx/runProps.d.ts +9 -22
  77. package/dist/docx/runProps.js +15 -168
  78. package/dist/docx/scan.js +20 -52
  79. package/dist/docx/sdt.js +9 -34
  80. package/dist/docx/sdtProps.d.ts +8 -1
  81. package/dist/docx/sdtProps.js +10 -0
  82. package/dist/docx/serializeBlock.d.ts +2 -0
  83. package/dist/docx/serializeBlock.js +8 -5
  84. package/dist/docx/serializeTable.js +32 -15
  85. package/dist/docx/session.d.ts +42 -14
  86. package/dist/docx/session.js +43 -17
  87. package/dist/docx/tableFormatting/editing.js +108 -139
  88. package/dist/docx/tableFormatting/reading.d.ts +16 -6
  89. package/dist/docx/tableFormatting/reading.js +40 -20
  90. package/dist/docx/tableTemplate.js +18 -7
  91. package/dist/download.d.ts +8 -5
  92. package/dist/download.js +2 -0
  93. package/dist/editor/clipboard/inlineFormatting.js +19 -30
  94. package/dist/editor/commands/comments/editing.d.ts +6 -2
  95. package/dist/editor/commands/comments/editing.js +19 -31
  96. package/dist/editor/commands/exportQueries.d.ts +15 -0
  97. package/dist/editor/commands/exportQueries.js +14 -0
  98. package/dist/editor/commands/fidelityQueries.d.ts +12 -0
  99. package/dist/editor/commands/fidelityQueries.js +8 -0
  100. package/dist/editor/commands/formatting/editing.d.ts +2 -2
  101. package/dist/editor/commands/formatting/editing.js +26 -108
  102. package/dist/editor/commands/formatting/propertyCommands.d.ts +21 -0
  103. package/dist/editor/commands/formatting/propertyCommands.js +103 -0
  104. package/dist/editor/commands/formatting/shared.d.ts +3 -3
  105. package/dist/editor/commands/formatting/shared.js +5 -2
  106. package/dist/editor/commands/indentCommands.js +5 -4
  107. package/dist/editor/commands/index.d.ts +11 -0
  108. package/dist/editor/commands/index.js +5 -0
  109. package/dist/editor/commands/linkCommands.js +5 -6
  110. package/dist/editor/commands/listCommands.js +8 -10
  111. package/dist/editor/commands/lockCommands.d.ts +7 -1
  112. package/dist/editor/commands/lockCommands.js +35 -46
  113. package/dist/editor/commands/paragraphCommands.js +28 -31
  114. package/dist/editor/commands/spacingCommands.js +1 -1
  115. package/dist/editor/createEditor.d.ts +17 -29
  116. package/dist/editor/createEditor.js +58 -58
  117. package/dist/editor/documentStyles.d.ts +11 -35
  118. package/dist/editor/documentStyles.js +9 -52
  119. package/dist/editor/editorDocument.d.ts +56 -0
  120. package/dist/editor/editorDocument.js +74 -0
  121. package/dist/editor/externalClipboard.js +16 -45
  122. package/dist/editor/insertTable.js +4 -3
  123. package/dist/editor/paragraphEdits.d.ts +11 -18
  124. package/dist/editor/paragraphEdits.js +7 -16
  125. package/dist/editor/plugins/displayDerivation.d.ts +47 -0
  126. package/dist/editor/plugins/displayDerivation.js +78 -0
  127. package/dist/editor/plugins/imagePaste.js +4 -3
  128. package/dist/editor/plugins/keymap.js +18 -3
  129. package/dist/editor/plugins/numberingDecorations.d.ts +5 -11
  130. package/dist/editor/plugins/numberingDecorations.js +7 -17
  131. package/dist/editor/plugins/paragraphDisplay.d.ts +7 -0
  132. package/dist/editor/plugins/paragraphDisplay.js +51 -0
  133. package/dist/editor/plugins/tabLayout.js +1 -1
  134. package/dist/editor/plugins/tableDisplay.d.ts +6 -0
  135. package/dist/editor/plugins/tableDisplay.js +16 -0
  136. package/dist/index.d.ts +2 -0
  137. package/dist/model/format.d.ts +30 -8
  138. package/dist/model/format.js +35 -22
  139. package/dist/model/tabStops.d.ts +9 -0
  140. package/dist/model/tabStops.js +18 -0
  141. package/dist/numbering/listTemplate.js +24 -7
  142. package/dist/numbering/parseNumbering.d.ts +14 -1
  143. package/dist/numbering/parseNumbering.js +35 -22
  144. package/dist/numbering/writeNumbering.d.ts +3 -4
  145. package/dist/numbering/writeNumbering.js +14 -25
  146. package/dist/ooxml/childOrder.d.ts +34 -0
  147. package/dist/ooxml/childOrder.js +496 -0
  148. package/dist/ooxml/element.d.ts +24 -11
  149. package/dist/ooxml/element.js +31 -12
  150. package/dist/ooxml/errors.d.ts +3 -2
  151. package/dist/ooxml/image.d.ts +4 -0
  152. package/dist/ooxml/image.js +2 -5
  153. package/dist/ooxml/partSplice.d.ts +67 -0
  154. package/dist/ooxml/partSplice.js +169 -0
  155. package/dist/ooxml/props.d.ts +112 -0
  156. package/dist/{docx/propsXml.js → ooxml/props.js} +72 -200
  157. package/dist/ooxml/simpleTypes.d.ts +103 -0
  158. package/dist/ooxml/simpleTypes.js +182 -0
  159. package/dist/ooxml/tabStops.js +8 -27
  160. package/dist/ooxml/tagScan.d.ts +34 -0
  161. package/dist/ooxml/tagScan.js +108 -0
  162. package/dist/ooxml/units.d.ts +25 -13
  163. package/dist/ooxml/units.js +65 -31
  164. package/dist/ooxml/xml.d.ts +27 -6
  165. package/dist/ooxml/xml.js +37 -5
  166. package/dist/page/blockKinds.d.ts +61 -0
  167. package/dist/page/blockKinds.js +11 -0
  168. package/dist/page/kinds/index.d.ts +6 -0
  169. package/dist/page/kinds/index.js +10 -0
  170. package/dist/page/kinds/paragraphKind.d.ts +10 -0
  171. package/dist/page/kinds/paragraphKind.js +71 -0
  172. package/dist/page/kinds/tableKind.d.ts +10 -0
  173. package/dist/page/kinds/tableKind.js +178 -0
  174. package/dist/page/measureBlocks.d.ts +3 -14
  175. package/dist/page/measureBlocks.js +24 -45
  176. package/dist/page/pageDecorations.d.ts +27 -44
  177. package/dist/page/pageDecorations.js +70 -152
  178. package/dist/page/pageLayout.d.ts +2 -19
  179. package/dist/page/pageLayout.js +36 -6
  180. package/dist/page/usePageLayout.d.ts +2 -17
  181. package/dist/page/usePageLayout.js +3 -39
  182. package/dist/schema/attrRoles.d.ts +25 -18
  183. package/dist/schema/attrRoles.js +102 -67
  184. package/dist/schema/displayDerivation.d.ts +82 -0
  185. package/dist/schema/displayDerivation.js +130 -0
  186. package/dist/schema/docxSchema.d.ts +3 -0
  187. package/dist/schema/docxSchema.js +26 -14
  188. package/dist/schema/editGuard.d.ts +1 -1
  189. package/dist/schema/guards.d.ts +37 -14
  190. package/dist/schema/guards.js +29 -3
  191. package/dist/schema/index.d.ts +2 -0
  192. package/dist/schema/index.js +2 -0
  193. package/dist/schema/locks.d.ts +11 -44
  194. package/dist/schema/locks.js +0 -9
  195. package/dist/schema/preservedGuards.d.ts +26 -6
  196. package/dist/schema/preservedGuards.js +31 -1
  197. package/dist/schema/protection.d.ts +7 -1
  198. package/dist/schema/protection.js +2 -1
  199. package/dist/schema/sourceEquality.d.ts +1 -9
  200. package/dist/schema/sourceEquality.js +1 -27
  201. package/dist/styles/inlineStyle.js +15 -6
  202. package/dist/table/cellFormatting.d.ts +8 -1
  203. package/dist/table/cellFormatting.js +10 -18
  204. package/dist/table/commands.js +20 -18
  205. package/dist/table/format.d.ts +3 -0
  206. package/dist/table/format.js +3 -9
  207. package/dist/table/gridBorders.d.ts +14 -16
  208. package/dist/table/gridBorders.js +3 -41
  209. package/dist/table/merge.d.ts +2 -6
  210. package/dist/table/merge.js +5 -5
  211. package/package.json +7 -3
  212. package/dist/docx/comments/contentTypes.d.ts +0 -7
  213. package/dist/docx/comments/contentTypes.js +0 -38
  214. package/dist/docx/comments/verifying.js +0 -206
  215. package/dist/docx/formatting/effectiveParagraph.d.ts +0 -15
  216. package/dist/docx/formatting/effectiveParagraph.js +0 -81
  217. package/dist/docx/propsXml.d.ts +0 -67
  218. package/dist/docx/uniqueControls.d.ts +0 -14
  219. package/dist/docx/uniqueControls.js +0 -62
  220. package/dist/editor/plugins/commentReservations.d.ts +0 -5
  221. package/dist/editor/plugins/commentReservations.js +0 -26
  222. package/dist/editor/plugins/styledParagraphs.d.ts +0 -15
  223. package/dist/editor/plugins/styledParagraphs.js +0 -65
  224. package/dist/page/tableMeasurements.d.ts +0 -18
  225. package/dist/page/tableMeasurements.js +0 -119
@@ -1,14 +1,27 @@
1
1
  // src/docx/exportDocx.ts
2
- import { toParagraphFormat } from "../model/format.js";
3
- import { parseNumbering } from "../numbering/parseNumbering.js";
4
- import { addListDefinitions } from "../numbering/writeNumbering.js";
5
2
  import { DocxExportError } from "../ooxml/errors.js";
6
- import { decodeUtf8, encodeUtf8, parseXml, W_NS } from "../ooxml/xml.js";
3
+ import {
4
+ encodeUtf8,
5
+ parseXml,
6
+ W_NS,
7
+ withXmlParser
8
+ } from "../ooxml/xml.js";
7
9
  import { sameSource } from "../schema/sourceEquality.js";
8
- import { planCommentParts } from "./comments.js";
10
+ import { commentsPlanner } from "./comments.js";
9
11
  import { repackParts } from "./container.js";
12
+ import {
13
+ fidelityNotesOf
14
+ } from "./fidelity.js";
10
15
  import { hyperlinkRefs } from "./hyperlink.js";
16
+ import { withUniqueIdentities } from "./identities.js";
17
+ import { problemsOf } from "./invariants.js";
11
18
  import { NO_IMAGE_REFS, planImageMedia } from "./media.js";
19
+ import { numberingPlanner } from "./numberingPlanner.js";
20
+ import { CONTENT_TYPES_PATH, contentTypeWriter } from "./packageParts.js";
21
+ import {
22
+ assertPartsParse,
23
+ runPartPlanners
24
+ } from "./partPlan.js";
12
25
  import {
13
26
  readRelationships,
14
27
  relationshipWriter,
@@ -19,7 +32,6 @@ import {
19
32
  originalBlock,
20
33
  sessionOf
21
34
  } from "./session.js";
22
- import { withUniqueControls } from "./uniqueControls.js";
23
35
  function blockXml(node, session, refs) {
24
36
  const imported = originalBlock(node, session);
25
37
  if (imported && sameSource(node, imported.node)) return imported.xml;
@@ -27,7 +39,7 @@ function blockXml(node, session, refs) {
27
39
  }
28
40
  function buildDocumentXml(doc, session, refs) {
29
41
  const pieces = [session.documentPrefix];
30
- withUniqueControls(doc).forEach((child) => {
42
+ withUniqueIdentities(doc).forEach((child) => {
31
43
  pieces.push(blockXml(child, session, refs));
32
44
  });
33
45
  pieces.push(session.documentSuffix);
@@ -85,80 +97,66 @@ function assertBookmarkPairs(documentXml) {
85
97
  );
86
98
  }
87
99
  }
88
- function collectNumIds(node, into) {
89
- const visit = (candidate) => {
90
- if (candidate.type.name !== "paragraph") return true;
91
- const numId = toParagraphFormat(candidate.attrs.format)?.numbering?.numId;
92
- if (numId !== void 0) into.add(numId);
93
- return false;
94
- };
95
- if (visit(node)) node.descendants(visit);
100
+ var PART_PLANNERS = [
101
+ numberingPlanner,
102
+ commentsPlanner
103
+ ];
104
+ function exportDocx(doc, session, options) {
105
+ return exportDocxReport(doc, session, options).bytes;
96
106
  }
97
- function numIdsIn(node) {
98
- const used = /* @__PURE__ */ new Set();
99
- collectNumIds(node, used);
100
- return used;
107
+ function exportDocxReport(doc, session, options) {
108
+ return reportThrough(PART_PLANNERS, doc, session, options);
101
109
  }
102
- function numIdsAtOpen(session) {
103
- const used = /* @__PURE__ */ new Set();
104
- for (const block of session.blocks) collectNumIds(block.node, used);
105
- return used;
106
- }
107
- function newNumIds(doc, session) {
108
- const defined = parseNumbering(session.numberingXml).lists;
109
- const atOpen = numIdsAtOpen(session);
110
- return Array.from(numIdsIn(doc)).filter((numId) => !defined.has(numId) && !atOpen.has(numId)).sort((a, b) => a - b);
110
+ function exportThroughPlanners(planners, doc, session, options) {
111
+ return reportThrough(planners, doc, session, options).bytes;
111
112
  }
112
- function newNumberingPart(doc, session) {
113
- const added = newNumIds(doc, session);
114
- if (added.length === 0) return null;
115
- const path = session.numberingPartPath;
116
- const original = path === null ? void 0 : session.parts.get(path);
117
- if (path === null || original === void 0) {
118
- throw new DocxExportError(
119
- "missing-numbering-part",
120
- "cannot add a new list to a document that has no numbering.xml"
113
+ function reportThrough(planners, doc, session, options) {
114
+ return withXmlParser(options?.xmlParser, () => {
115
+ const store = sessionOf(session);
116
+ const problem = problemsOf(doc, store)[0];
117
+ if (problem) throw new DocxExportError(problem.code, problem.message);
118
+ const approximated = [];
119
+ const bytes = writeDocx(
120
+ doc,
121
+ store,
122
+ { add: (note) => approximated.push(note) },
123
+ planners
121
124
  );
122
- }
123
- const { text, hadBom } = decodeUtf8(original);
124
- return {
125
- path,
126
- bytes: encodeUtf8(addListDefinitions(text, added), hadBom)
127
- };
125
+ return {
126
+ bytes,
127
+ notes: [...fidelityNotesOf(doc, store.mainPartPath), ...approximated]
128
+ };
129
+ });
128
130
  }
129
- function exportDocx(doc, session) {
130
- const store = sessionOf(session);
131
+ function writeDocx(doc, store, notes, planners) {
131
132
  const relsPath = relsPathOf(store.mainPartPath);
132
- const relationships = relationshipWriter(
133
- readRelationships(store.parts, relsPath)
134
- );
135
- const media = planImageMedia(doc, store, relationships);
136
- const comments = planCommentParts(
137
- doc,
138
- store,
139
- relationships,
140
- media?.parts.get("[Content_Types].xml")
141
- );
133
+ const context = {
134
+ relationships: relationshipWriter(readRelationships(store.parts, relsPath)),
135
+ contentTypes: contentTypeWriter(store.parts)
136
+ };
137
+ const media = planImageMedia(doc, store, context);
142
138
  const documentXml = buildDocumentXml(doc, store, {
143
139
  images: media?.refs ?? NO_IMAGE_REFS,
144
- links: hyperlinkRefs(relationships)
140
+ links: hyperlinkRefs(context.relationships),
141
+ notes
145
142
  });
146
143
  assertBookmarkPairs(documentXml);
147
- const replacements = /* @__PURE__ */ new Map([
148
- [store.mainPartPath, encodeUtf8(documentXml, store.documentHadBom)]
149
- ]);
150
- const numbering = newNumberingPart(doc, store);
151
- if (numbering) replacements.set(numbering.path, numbering.bytes);
152
- for (const [path, bytes] of media?.parts ?? []) {
153
- replacements.set(path, bytes);
154
- }
155
- for (const [path, bytes] of comments?.parts ?? []) {
156
- replacements.set(path, bytes);
157
- }
158
- const rels = relationships.part(store.parts.get(relsPath));
159
- if (rels) replacements.set(relsPath, rels);
160
- return repackParts(store.parts, replacements);
144
+ const parts = runPartPlanners(planners, doc, store, context, media?.parts);
145
+ const rels = context.relationships.part(store.parts.get(relsPath));
146
+ if (rels) parts.set(relsPath, rels);
147
+ const contentTypes = context.contentTypes.part();
148
+ if (contentTypes) parts.set(CONTENT_TYPES_PATH, contentTypes);
149
+ assertPartsParse(parts, contentTypes ?? store.parts.get(CONTENT_TYPES_PATH));
150
+ return repackParts(
151
+ store.parts,
152
+ new Map([
153
+ [store.mainPartPath, encodeUtf8(documentXml, store.documentHadBom)],
154
+ ...parts
155
+ ])
156
+ );
161
157
  }
162
158
  export {
163
- exportDocx
159
+ exportDocx,
160
+ exportDocxReport,
161
+ exportThroughPlanners
164
162
  };
@@ -1,16 +1,19 @@
1
1
  /**
2
2
  * What the serializers need from the rest of the package while a body is being written: which
3
- * relationship an inserted image ended up on (`docx/media`), and which one each link's address
4
- * lives on (`docx/hyperlink`).
3
+ * relationship an inserted image ended up on (`docx/media`), which one each link's address lives
4
+ * on (`docx/hyperlink`), and where to record an approximation a writer had to make
5
+ * (`docx/fidelity`).
5
6
  *
6
7
  * They travel as one value so that a serializer taking a body apart passes on everything the
7
8
  * pieces of it may need, whatever a later kind of content turns out to require.
8
9
  */
10
+ import { type FidelityCollector } from "./fidelity";
9
11
  import { type LinkRefs } from "./hyperlink";
10
12
  import { type ImageRefs } from "./media";
11
13
  export interface ExportRefs {
12
14
  images: ImageRefs;
13
15
  links: LinkRefs;
16
+ notes: FidelityCollector;
14
17
  }
15
18
  /** What a serializer called on its own, outside an export, has to work with */
16
19
  export declare const NO_EXPORT_REFS: ExportRefs;
@@ -1,9 +1,11 @@
1
1
  // src/docx/exportRefs.ts
2
+ import { NO_FIDELITY_COLLECTOR } from "./fidelity.js";
2
3
  import { NO_LINK_REFS } from "./hyperlink.js";
3
4
  import { NO_IMAGE_REFS } from "./media.js";
4
5
  var NO_EXPORT_REFS = {
5
6
  images: NO_IMAGE_REFS,
6
- links: NO_LINK_REFS
7
+ links: NO_LINK_REFS,
8
+ notes: NO_FIDELITY_COLLECTOR
7
9
  };
8
10
  export {
9
11
  NO_EXPORT_REFS
@@ -0,0 +1,43 @@
1
+ /**
2
+ * What a document holds that this editor could not model, as a list a host application can read.
3
+ *
4
+ * Opening a file quietly hides some markup, stands a placeholder in front of the rest, and a
5
+ * writer approximates a little more on the way back out. None of that shows on the page, so this
6
+ * is where it is said out loud: one note per preserved node, plus whatever a write approximated.
7
+ */
8
+ import type { Node as PMNode } from "prosemirror-model";
9
+ export type FidelitySeverity =
10
+ /** Carried through whole, with nothing of it on screen */
11
+ "hidden"
12
+ /** Carried through whole behind a placeholder that stands in its place and cannot be edited */
13
+ | "placeholder"
14
+ /** Written back as something near what arrived rather than as what arrived */
15
+ | "approximated";
16
+ export type FidelityCode = "preserved-run-content" | "preserved-inline" | "preserved-block" | "range-marker" | "paragraph-demoted" | "table-demoted";
17
+ export interface FidelityNote {
18
+ severity: FidelitySeverity;
19
+ code: FidelityCode;
20
+ /** The package path of the part this is about, or null when the caller holds no session */
21
+ part: string | null;
22
+ /** Which block of the body as it was opened this falls in. null for a node made while editing */
23
+ block: number | null;
24
+ /** Where the node stands in the document. null for an approximation with no node behind it */
25
+ pos: number | null;
26
+ /** The original element, for example `w:bookmarkStart` */
27
+ element: string;
28
+ }
29
+ /** Where a writer records an approximation it had to make while a body was written */
30
+ export interface FidelityCollector {
31
+ add(note: FidelityNote): void;
32
+ }
33
+ /** What a serializer called on its own, outside an export, records its approximations to */
34
+ export declare const NO_FIDELITY_COLLECTOR: FidelityCollector;
35
+ /**
36
+ * One note per preserved node, in document order.
37
+ *
38
+ * The nodes carry the original element and the block they came from themselves, so the list is a
39
+ * projection of the document rather than something an import has to remember alongside it. A
40
+ * document opened and one edited for an hour are read the same way, and a `pos` is enough to take
41
+ * a reader to the node the note is about.
42
+ */
43
+ export declare function fidelityNotesOf(doc: PMNode, part: string | null): FidelityNote[];
@@ -0,0 +1,64 @@
1
+ // src/docx/fidelity.ts
2
+ import { localPart } from "../ooxml/xml.js";
3
+ import { splitBlockKey } from "./session.js";
4
+ var NO_FIDELITY_COLLECTOR = { add: () => {
5
+ } };
6
+ function stringAttr(value) {
7
+ return typeof value === "string" ? value : null;
8
+ }
9
+ function elementNameOf(xml) {
10
+ return /^\s*<\s*([^\s/>]+)/.exec(xml)?.[1] ?? null;
11
+ }
12
+ function elementOf(node) {
13
+ return stringAttr(node.attrs.name) ?? elementNameOf(stringAttr(node.attrs.xml) ?? "");
14
+ }
15
+ function demotedBlock(element) {
16
+ const name = element === null ? null : localPart(element);
17
+ if (name === "tbl") return { severity: "placeholder", code: "table-demoted" };
18
+ if (name === "p") {
19
+ return { severity: "placeholder", code: "paragraph-demoted" };
20
+ }
21
+ return { severity: "placeholder", code: "preserved-block" };
22
+ }
23
+ function preservedBy(node, element) {
24
+ switch (node.type.name) {
25
+ case "docxRaw":
26
+ return demotedBlock(element);
27
+ case "rawBlock":
28
+ return { severity: "placeholder", code: "preserved-block" };
29
+ case "bookmarkBlock":
30
+ return { severity: "hidden", code: "range-marker" };
31
+ case "rawInline":
32
+ return preservedInline(element);
33
+ default:
34
+ return null;
35
+ }
36
+ }
37
+ function preservedInline(element) {
38
+ const name = element === null ? null : localPart(element);
39
+ if (name === "r") return null;
40
+ return name === "bookmarkStart" || name === "bookmarkEnd" ? { severity: "hidden", code: "range-marker" } : { severity: "hidden", code: "preserved-inline" };
41
+ }
42
+ function blockNumberOf(node) {
43
+ const srcId = stringAttr(node.attrs.srcId);
44
+ const key = srcId === null ? null : splitBlockKey(srcId);
45
+ return key?.index ?? null;
46
+ }
47
+ function fidelityNotesOf(doc, part) {
48
+ const notes = [];
49
+ let block = null;
50
+ doc.descendants((node, pos, parent) => {
51
+ if (parent === doc) block = blockNumberOf(node);
52
+ const element = elementOf(node);
53
+ const preserved = preservedBy(node, element);
54
+ if (preserved) {
55
+ notes.push({ ...preserved, part, block, pos, element: element ?? "?" });
56
+ }
57
+ return true;
58
+ });
59
+ return notes;
60
+ }
61
+ export {
62
+ NO_FIDELITY_COLLECTOR,
63
+ fidelityNotesOf
64
+ };
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The derived attrs a paragraph and the run marks inside it carry, worked out from the resolved
3
+ * hierarchy. Import, the plugin that formats the paragraphs an edit built, the paragraph writers
4
+ * and the clipboard reader all take them from here, so a paragraph is drawn the same whichever of
5
+ * them last touched it.
6
+ */
7
+ import type { Mark } from "prosemirror-model";
8
+ import type { ParagraphFormat, RunFormat } from "../../model/format";
9
+ import type { FormattingContext } from "./context";
10
+ import { type ParagraphPlacement, type ResolvedParagraph } from "./resolve";
11
+ /** The two derived paragraph attrs (`format` and `styleRun` in `schema`) */
12
+ export interface ParagraphAttrs {
13
+ format: ParagraphFormat | null;
14
+ styleRun: RunFormat | null;
15
+ }
16
+ export declare function paragraphAttrsOf(paragraph: ResolvedParagraph): ParagraphAttrs;
17
+ export declare function paragraphAttrsFor(pPr: string | null, context: FormattingContext, placement?: ParagraphPlacement | null): ParagraphAttrs;
18
+ /**
19
+ * The run mark an inline wears under the resolved paragraph, or null when it needs none.
20
+ *
21
+ * A mark already there keeps the formatting it wrote down and has its display values read
22
+ * again, since they were laid over whatever the paragraph wore before. Text carrying no mark
23
+ * gets one holding nothing but the values the paragraph passes down, so it is drawn the way
24
+ * import draws a bare `w:r`; such a mark writes no `w:rPr` and changes nothing on the way out.
25
+ * An inline that is not text and carries no mark is left as it is.
26
+ */
27
+ export declare function runMarkUnder(mark: Mark | null, isText: boolean, paragraph: ResolvedParagraph, context: FormattingContext): Mark | null;
@@ -0,0 +1,31 @@
1
+ // src/docx/formatting/attrs.ts
2
+ import { docxSchema } from "../../schema/index.js";
3
+ import {
4
+ resolveParagraph,
5
+ resolveRun
6
+ } from "./resolve.js";
7
+ function paragraphAttrsOf(paragraph) {
8
+ return { format: paragraph.format, styleRun: paragraph.styleRun };
9
+ }
10
+ function paragraphAttrsFor(pPr, context, placement = null) {
11
+ return paragraphAttrsOf(resolveParagraph(pPr, context, placement));
12
+ }
13
+ function runMarkUnder(mark, isText, paragraph, context) {
14
+ if (mark) {
15
+ const rPr = mark.attrs.rPr;
16
+ const format2 = resolveRun(
17
+ typeof rPr === "string" ? rPr : null,
18
+ paragraph,
19
+ context
20
+ );
21
+ return mark.type.create({ ...mark.attrs, format: format2 });
22
+ }
23
+ if (!isText) return null;
24
+ const format = resolveRun(null, paragraph, context);
25
+ return format === null ? null : docxSchema.marks.run.create({ rPr: null, rAttrs: null, format });
26
+ }
27
+ export {
28
+ paragraphAttrsFor,
29
+ paragraphAttrsOf,
30
+ runMarkUnder
31
+ };
@@ -0,0 +1,31 @@
1
+ import type { RunFormat } from "../../model/format";
2
+ import { type Numbering } from "../../numbering/parseNumbering";
3
+ import { type CompatSettings } from "../documentSettings";
4
+ import { type ThemeFonts } from "../theme";
5
+ import { type StyleTable } from "./styles";
6
+ import type { ParagraphFormatLayer } from "./tabStops";
7
+ /**
8
+ * Everything a document lays down that the §17.7.2 hierarchy is resolved against.
9
+ *
10
+ * Built once when the document is opened and read by every caller that derives a paragraph's or
11
+ * a run's display values, so that a value is the same whichever path asked for it.
12
+ */
13
+ export interface FormattingContext {
14
+ styles: StyleTable;
15
+ /** The paragraph style a paragraph naming none wears (`w:default="1"`). Null when the document marks none */
16
+ defaultParagraphStyleId: string | null;
17
+ defaultTableStyleId: string | null;
18
+ /** `pPrDefault`, the layer at the base of the hierarchy */
19
+ paragraphDefaults: ParagraphFormatLayer;
20
+ /**
21
+ * `rPrDefault` read in full. It decides what a removed run property falls back to. The default
22
+ * font and size are supplied through CSS variables; other properties enter derived run values.
23
+ */
24
+ runDefaults: RunFormat;
25
+ numbering: Numbering;
26
+ themeFonts: ThemeFonts;
27
+ compat: CompatSettings;
28
+ }
29
+ export declare const NO_FORMATTING: FormattingContext;
30
+ /** The context an opened document resolves against. A document without a styles part lays down nothing */
31
+ export declare function formattingContextOf(styles: Document | null, numbering: Numbering, themeFonts: ThemeFonts, compat?: CompatSettings): FormattingContext;
@@ -0,0 +1,42 @@
1
+ // src/docx/formatting/context.ts
2
+ import {
3
+ EMPTY_NUMBERING
4
+ } from "../../numbering/parseNumbering.js";
5
+ import { NO_COMPAT } from "../documentSettings.js";
6
+ import { NO_THEME_FONTS } from "../theme.js";
7
+ import { readDefaultParagraphFormat, readRunDefaults } from "./direct.js";
8
+ import {
9
+ defaultParagraphStyleIdOf,
10
+ defaultTableStyleIdOf,
11
+ NO_STYLES,
12
+ readStyles
13
+ } from "./styles.js";
14
+ var NO_FORMATTING = {
15
+ styles: NO_STYLES,
16
+ defaultParagraphStyleId: null,
17
+ defaultTableStyleId: null,
18
+ paragraphDefaults: {},
19
+ runDefaults: {},
20
+ numbering: EMPTY_NUMBERING,
21
+ themeFonts: NO_THEME_FONTS,
22
+ compat: NO_COMPAT
23
+ };
24
+ function formattingContextOf(styles, numbering, themeFonts, compat = NO_COMPAT) {
25
+ if (styles === null) {
26
+ return { ...NO_FORMATTING, numbering, themeFonts, compat };
27
+ }
28
+ return {
29
+ styles: readStyles(styles, themeFonts),
30
+ defaultParagraphStyleId: defaultParagraphStyleIdOf(styles),
31
+ defaultTableStyleId: defaultTableStyleIdOf(styles),
32
+ paragraphDefaults: readDefaultParagraphFormat(styles),
33
+ runDefaults: readRunDefaults(styles, themeFonts),
34
+ numbering,
35
+ themeFonts,
36
+ compat
37
+ };
38
+ }
39
+ export {
40
+ NO_FORMATTING,
41
+ formattingContextOf
42
+ };
@@ -8,11 +8,8 @@
8
8
  import { type DocumentDefaults, type LineSpacing, type RunFormat } from "../../model/format";
9
9
  import { type ThemeFonts } from "../theme";
10
10
  import type { ParagraphFormatLayer } from "./tabStops";
11
- /**
12
- * The font names held in a display value.
13
- * Splits the CSS name list `fontFamilyOf` produced back into individual names.
14
- */
15
- export declare function fontNamesOf(cssNames: string | undefined | null): string[];
11
+ /** `w:spacing/@line` counts an automatic line height in 240ths of a line (§17.3.1.33) */
12
+ export declare const LINE_UNITS_PER_LINE = 240;
16
13
  /**
17
14
  * The line spacing from `w:spacing`.
18
15
  * auto means a multiple in 240ths, and the rest are heights pinned down in twips.
@@ -23,6 +20,10 @@ export declare function readRunFormat(rPr: Element | null, themeFonts?: ThemeFon
23
20
  export declare const NO_DOCUMENT_DEFAULTS: DocumentDefaults;
24
21
  /** Reads the paragraph-property layer at the base of the OOXML hierarchy. */
25
22
  export declare function readDefaultParagraphFormat(styles: Document): ParagraphFormatLayer;
23
+ /** Reads the run-property layer at the base of the OOXML hierarchy (`rPrDefault`) in full */
24
+ export declare function readRunDefaults(styles: Document, themeFonts?: ThemeFonts): RunFormat;
25
+ /** The character style a run's formatting points at (`w:rStyle`). null when it points at none */
26
+ export declare function runStyleIdOf(rPr: Element | null): string | null;
26
27
  /**
27
28
  * Reads the document default font size, font, and line spacing from docDefaults in styles.xml.
28
29
  * What is absent is left as null, and the on-screen fallback is decided by the display layer.