web-doc 0.6.2 → 0.7.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 (188) hide show
  1. package/THIRD_PARTY_NOTICES.md +26 -1
  2. package/dist/adapters/docx-images.d.ts +13 -4
  3. package/dist/adapters/docx-images.js +3 -0
  4. package/dist/adapters/docx-paragraphs.d.ts +65 -0
  5. package/dist/adapters/docx-paragraphs.js +165 -0
  6. package/dist/adapters/docx-prepass.d.ts +17 -0
  7. package/dist/adapters/docx-prepass.js +230 -0
  8. package/dist/adapters/office.d.ts +31 -5
  9. package/dist/adapters/office.js +60 -29
  10. package/dist/adapters/pdf.d.ts +9 -0
  11. package/dist/adapters/pdf.js +10 -0
  12. package/dist/assets/pdfium/pdfium.wasm +0 -0
  13. package/dist/contracts.d.ts +47 -2
  14. package/dist/edit/assets.d.ts +23 -0
  15. package/dist/edit/assets.js +75 -0
  16. package/dist/edit/docx/elements.d.ts +6 -0
  17. package/dist/edit/docx/elements.js +90 -0
  18. package/dist/edit/docx/engine.d.ts +51 -0
  19. package/dist/edit/docx/engine.js +473 -0
  20. package/dist/edit/docx/handlers.d.ts +3 -0
  21. package/dist/edit/docx/handlers.js +15 -0
  22. package/dist/edit/docx/ids.d.ts +45 -0
  23. package/dist/edit/docx/ids.js +101 -0
  24. package/dist/edit/docx/model.d.ts +92 -0
  25. package/dist/edit/docx/model.js +339 -0
  26. package/dist/edit/docx/operations.d.ts +37 -0
  27. package/dist/edit/docx/operations.js +3 -0
  28. package/dist/edit/docx/provider.d.ts +13 -0
  29. package/dist/edit/docx/provider.js +34 -0
  30. package/dist/edit/docx/schemas.d.ts +6 -0
  31. package/dist/edit/docx/schemas.js +192 -0
  32. package/dist/edit/docx/session.d.ts +37 -0
  33. package/dist/edit/docx/session.js +426 -0
  34. package/dist/edit/docx/structure-ops.d.ts +6 -0
  35. package/dist/edit/docx/structure-ops.js +494 -0
  36. package/dist/edit/docx/style.d.ts +45 -0
  37. package/dist/edit/docx/style.js +375 -0
  38. package/dist/edit/docx/table-ops.d.ts +4 -0
  39. package/dist/edit/docx/table-ops.js +219 -0
  40. package/dist/edit/docx/text-ops.d.ts +18 -0
  41. package/dist/edit/docx/text-ops.js +458 -0
  42. package/dist/edit/docx/text.d.ts +34 -0
  43. package/dist/edit/docx/text.js +263 -0
  44. package/dist/edit/docx/types.d.ts +191 -0
  45. package/dist/edit/docx/types.js +1 -0
  46. package/dist/edit/docx/write.d.ts +50 -0
  47. package/dist/edit/docx/write.js +352 -0
  48. package/dist/edit/engine.d.ts +117 -0
  49. package/dist/edit/engine.js +1 -0
  50. package/dist/edit/history.d.ts +53 -0
  51. package/dist/edit/history.js +101 -0
  52. package/dist/edit/ooxml/names.d.ts +20 -0
  53. package/dist/edit/ooxml/names.js +61 -0
  54. package/dist/edit/ooxml/opc.d.ts +50 -0
  55. package/dist/edit/ooxml/opc.js +150 -0
  56. package/dist/edit/ooxml/package.d.ts +82 -0
  57. package/dist/edit/ooxml/package.js +233 -0
  58. package/dist/edit/ooxml/patch.d.ts +51 -0
  59. package/dist/edit/ooxml/patch.js +250 -0
  60. package/dist/edit/ooxml/transaction.d.ts +39 -0
  61. package/dist/edit/ooxml/transaction.js +316 -0
  62. package/dist/edit/ooxml/worker.d.ts +8 -0
  63. package/dist/edit/ooxml/worker.js +12 -0
  64. package/dist/edit/ooxml/writer.d.ts +21 -0
  65. package/dist/edit/ooxml/writer.js +187 -0
  66. package/dist/edit/ooxml/xml.d.ts +74 -0
  67. package/dist/edit/ooxml/xml.js +451 -0
  68. package/dist/edit/ooxml/zip.d.ts +54 -0
  69. package/dist/edit/ooxml/zip.js +280 -0
  70. package/dist/edit/operations.d.ts +19 -0
  71. package/dist/edit/operations.js +137 -0
  72. package/dist/edit/pdf/engine/compact.d.ts +6 -0
  73. package/dist/edit/pdf/engine/compact.js +442 -0
  74. package/dist/edit/pdf/engine/document.d.ts +95 -0
  75. package/dist/edit/pdf/engine/document.js +868 -0
  76. package/dist/edit/pdf/engine/elements.d.ts +45 -0
  77. package/dist/edit/pdf/engine/elements.js +313 -0
  78. package/dist/edit/pdf/engine/existing-text.d.ts +4 -0
  79. package/dist/edit/pdf/engine/existing-text.js +424 -0
  80. package/dist/edit/pdf/engine/fonts.d.ts +78 -0
  81. package/dist/edit/pdf/engine/fonts.js +466 -0
  82. package/dist/edit/pdf/engine/geometry.d.ts +36 -0
  83. package/dist/edit/pdf/engine/geometry.js +97 -0
  84. package/dist/edit/pdf/engine/handler.d.ts +21 -0
  85. package/dist/edit/pdf/engine/handler.js +108 -0
  86. package/dist/edit/pdf/engine/images.d.ts +33 -0
  87. package/dist/edit/pdf/engine/images.js +188 -0
  88. package/dist/edit/pdf/engine/layout.d.ts +23 -0
  89. package/dist/edit/pdf/engine/layout.js +177 -0
  90. package/dist/edit/pdf/engine/operations.d.ts +67 -0
  91. package/dist/edit/pdf/engine/operations.js +3 -0
  92. package/dist/edit/pdf/engine/pages.d.ts +6 -0
  93. package/dist/edit/pdf/engine/pages.js +98 -0
  94. package/dist/edit/pdf/engine/pdfium.d.ts +195 -0
  95. package/dist/edit/pdf/engine/pdfium.js +249 -0
  96. package/dist/edit/pdf/engine/shapes.d.ts +4 -0
  97. package/dist/edit/pdf/engine/shapes.js +183 -0
  98. package/dist/edit/pdf/engine/tables.d.ts +44 -0
  99. package/dist/edit/pdf/engine/tables.js +315 -0
  100. package/dist/edit/pdf/engine/text-box.d.ts +71 -0
  101. package/dist/edit/pdf/engine/text-box.js +317 -0
  102. package/dist/edit/pdf/engine/text-layout.d.ts +28 -0
  103. package/dist/edit/pdf/engine/text-layout.js +67 -0
  104. package/dist/edit/pdf/engine/transform.d.ts +5 -0
  105. package/dist/edit/pdf/engine/transform.js +137 -0
  106. package/dist/edit/pdf/provider.d.ts +35 -0
  107. package/dist/edit/pdf/provider.js +92 -0
  108. package/dist/edit/pdf/range-map.d.ts +14 -0
  109. package/dist/edit/pdf/range-map.js +107 -0
  110. package/dist/edit/pdf/schemas.d.ts +11 -0
  111. package/dist/edit/pdf/schemas.js +291 -0
  112. package/dist/edit/pdf/selection.d.ts +5 -0
  113. package/dist/edit/pdf/selection.js +152 -0
  114. package/dist/edit/pdf/session.d.ts +53 -0
  115. package/dist/edit/pdf/session.js +273 -0
  116. package/dist/edit/pdf/types.d.ts +336 -0
  117. package/dist/edit/pdf/types.js +1 -0
  118. package/dist/edit/pptx/elements.d.ts +74 -0
  119. package/dist/edit/pptx/elements.js +301 -0
  120. package/dist/edit/pptx/engine.d.ts +37 -0
  121. package/dist/edit/pptx/engine.js +466 -0
  122. package/dist/edit/pptx/geometry.d.ts +72 -0
  123. package/dist/edit/pptx/geometry.js +193 -0
  124. package/dist/edit/pptx/handler.d.ts +7 -0
  125. package/dist/edit/pptx/handler.js +91 -0
  126. package/dist/edit/pptx/handlers.d.ts +3 -0
  127. package/dist/edit/pptx/handlers.js +21 -0
  128. package/dist/edit/pptx/image-table-ops.d.ts +5 -0
  129. package/dist/edit/pptx/image-table-ops.js +233 -0
  130. package/dist/edit/pptx/model.d.ts +69 -0
  131. package/dist/edit/pptx/model.js +170 -0
  132. package/dist/edit/pptx/operations.d.ts +50 -0
  133. package/dist/edit/pptx/operations.js +11 -0
  134. package/dist/edit/pptx/provider.d.ts +17 -0
  135. package/dist/edit/pptx/provider.js +40 -0
  136. package/dist/edit/pptx/schemas.d.ts +6 -0
  137. package/dist/edit/pptx/schemas.js +220 -0
  138. package/dist/edit/pptx/session.d.ts +44 -0
  139. package/dist/edit/pptx/session.js +114 -0
  140. package/dist/edit/pptx/shape-ops.d.ts +10 -0
  141. package/dist/edit/pptx/shape-ops.js +486 -0
  142. package/dist/edit/pptx/slide-ops.d.ts +6 -0
  143. package/dist/edit/pptx/slide-ops.js +353 -0
  144. package/dist/edit/pptx/style.d.ts +31 -0
  145. package/dist/edit/pptx/style.js +181 -0
  146. package/dist/edit/pptx/text-ops.d.ts +13 -0
  147. package/dist/edit/pptx/text-ops.js +267 -0
  148. package/dist/edit/pptx/text-write.d.ts +67 -0
  149. package/dist/edit/pptx/text-write.js +293 -0
  150. package/dist/edit/pptx/text.d.ts +41 -0
  151. package/dist/edit/pptx/text.js +99 -0
  152. package/dist/edit/pptx/types.d.ts +232 -0
  153. package/dist/edit/pptx/types.js +1 -0
  154. package/dist/edit/schema.d.ts +11 -0
  155. package/dist/edit/schema.js +275 -0
  156. package/dist/edit/session.d.ts +66 -0
  157. package/dist/edit/session.js +701 -0
  158. package/dist/edit/sessions.d.ts +8 -0
  159. package/dist/edit/sessions.js +1 -0
  160. package/dist/edit/types.d.ts +276 -0
  161. package/dist/edit/types.js +1 -0
  162. package/dist/edit/worker-engine.d.ts +31 -0
  163. package/dist/edit/worker-engine.js +91 -0
  164. package/dist/fonts/THIRD_PARTY_NOTICES.md +3 -0
  165. package/dist/fonts/manifest.json +6 -1
  166. package/dist/fonts/noto-sans-latin-cyrillic.ttf +0 -0
  167. package/dist/fonts.d.ts +2 -0
  168. package/dist/fonts.js +4 -0
  169. package/dist/headless.d.ts +4 -0
  170. package/dist/headless.js +4 -0
  171. package/dist/index.d.ts +5 -0
  172. package/dist/index.js +5 -0
  173. package/dist/limits.js +3 -0
  174. package/dist/ooxml-edit-worker.d.ts +1 -0
  175. package/dist/ooxml-edit-worker.js +8 -0
  176. package/dist/pdf-edit-worker.d.ts +1 -0
  177. package/dist/pdf-edit-worker.js +35 -0
  178. package/dist/spreadsheet-viewport.d.ts +5 -0
  179. package/dist/spreadsheet-viewport.js +11 -0
  180. package/dist/ui.js +7 -0
  181. package/dist/viewer.d.ts +6 -0
  182. package/dist/viewer.js +293 -19
  183. package/dist/viewport.d.ts +13 -0
  184. package/dist/viewport.js +126 -1
  185. package/dist/worker-protocol.d.ts +39 -2
  186. package/dist/workers/ooxml-edit-worker.js +8671 -0
  187. package/dist/workers/pdf-edit-worker.js +10844 -0
  188. package/package.json +3 -3
@@ -0,0 +1,92 @@
1
+ import type { OoxmlPackage } from "../ooxml/package.js";
2
+ import type { XmlElement, XmlPart } from "../ooxml/xml.js";
3
+ import { DocxStyles } from "./style.js";
4
+ import { type ParagraphText } from "./text.js";
5
+ import type { DocxReadOnlyReason } from "./types.js";
6
+ export interface ParagraphRecord {
7
+ readonly kind: "paragraph";
8
+ /** Eight upper-case hex digits: the `w14:paraId` or the generated id. */
9
+ readonly id: string;
10
+ /** "p:<id>". */
11
+ readonly elementId: string;
12
+ readonly node: XmlElement;
13
+ readonly pPr?: XmlElement;
14
+ /** `w:pPr/w:sectPr`: the paragraph ends a section. */
15
+ readonly sectPr?: XmlElement;
16
+ readonly text: ParagraphText;
17
+ /** The table whose cell holds the paragraph, when any. */
18
+ readonly table?: TableRecord;
19
+ /** The block-level wrapper (`w:sdt`, `w:customXml`) the paragraph sits in, when any. */
20
+ readonly wrapper?: XmlElement;
21
+ readonly readOnlyReason?: DocxReadOnlyReason;
22
+ /** Inline pictures and other objects of the paragraph, in order. */
23
+ readonly inlines: readonly InlineRecord[];
24
+ }
25
+ export interface CellRecord {
26
+ readonly node: XmlElement;
27
+ readonly paragraphs: readonly ParagraphRecord[];
28
+ }
29
+ export interface TableRecord {
30
+ readonly kind: "table";
31
+ /** The id of the table's first paragraph. */
32
+ readonly id: string;
33
+ /** "tbl:<id>". */
34
+ readonly elementId: string;
35
+ readonly node: XmlElement;
36
+ readonly rows: readonly (readonly CellRecord[])[];
37
+ readonly wrapper?: XmlElement;
38
+ }
39
+ export interface InlineRecord {
40
+ readonly kind: "image" | "other";
41
+ /** "img:<paragraph id>.<n>" or "other:<paragraph id>.<n>". */
42
+ readonly elementId: string;
43
+ readonly paragraph: ParagraphRecord;
44
+ /** The `w:drawing`, `w:object`, `w:pict`, math or alternate-content node. */
45
+ readonly node: XmlElement;
46
+ /** The `w:r` holding the node, when it sits in a run. */
47
+ readonly run?: XmlElement;
48
+ /** For an inline picture: `wp:extent` in EMU. */
49
+ readonly extent?: {
50
+ readonly cx: number;
51
+ readonly cy: number;
52
+ };
53
+ }
54
+ export type BlockRecord = ParagraphRecord | TableRecord;
55
+ export type AnyRecord = ParagraphRecord | TableRecord | InlineRecord;
56
+ export declare class DocxModel {
57
+ #private;
58
+ readonly revision: number;
59
+ readonly mainPart: string;
60
+ readonly document: XmlPart;
61
+ readonly body: XmlElement;
62
+ /** The body's own `w:sectPr`, when present. */
63
+ readonly bodySectPr: XmlElement | undefined;
64
+ /** Top-level blocks of the body in order; cell paragraphs hang off their table. */
65
+ readonly blocks: readonly BlockRecord[];
66
+ /** Every listed record in document order: a table before its cell paragraphs, a paragraph before its inlines. */
67
+ readonly records: readonly AnyRecord[];
68
+ readonly byId: ReadonlyMap<string, AnyRecord>;
69
+ /** Id of every `w:p` of the main part, listed or not. */
70
+ readonly paragraphIds: ReadonlyMap<XmlElement, string>;
71
+ /** Ids the engine owns (never written by an edit), in document order. */
72
+ readonly unauthoredIds: readonly string[];
73
+ /** Every paragraph id the document uses, in any story part. */
74
+ readonly takenIds: ReadonlySet<string>;
75
+ readonly styles: DocxStyles;
76
+ private constructor();
77
+ /**
78
+ * Builds the index. `unauthored` carries the ids of the paragraphs that
79
+ * have no `w14:paraId`, in document order, as the engine tracks them
80
+ * between edits; without it the ids are computed from the bytes, as the
81
+ * display pre-pass computes them.
82
+ */
83
+ static load(pkg: OoxmlPackage, signal?: AbortSignal, unauthored?: readonly string[], cachedStyles?: DocxStyles): Promise<DocxModel>;
84
+ /** The unauthored ids as a set, for "is this paragraph's id written by an edit" checks. */
85
+ get unauthoredSet(): ReadonlySet<string>;
86
+ get paragraphs(): readonly ParagraphRecord[];
87
+ }
88
+ /** The text of a table: cells joined by tabs, rows by newlines. */
89
+ export declare function tableText(table: TableRecord): string;
90
+ export declare function cellText(cell: CellRecord): string;
91
+ /** The paragraph a cell paragraph's table starts with, for `tbl:` ids. */
92
+ export declare function tableRows(table: TableRecord): readonly (readonly string[])[];
@@ -0,0 +1,339 @@
1
+ import { ViewerError } from "../../errors.js";
2
+ import { assignParagraphIds, collectIds, newIdState, OFFICE_RELATIONSHIPS, paragraphsOf, STORY_RELATIONSHIP_TYPES, W_NS, } from "./ids.js";
3
+ import { DocxStyles } from "./style.js";
4
+ import { readParagraphText } from "./text.js";
5
+ export class DocxModel {
6
+ revision;
7
+ mainPart;
8
+ document;
9
+ body;
10
+ bodySectPr;
11
+ blocks;
12
+ records;
13
+ byId;
14
+ paragraphIds;
15
+ unauthoredIds;
16
+ takenIds;
17
+ styles;
18
+ constructor(revision, mainPart, document, body,
19
+ /** The body's own `w:sectPr`, when present. */
20
+ bodySectPr,
21
+ /** Top-level blocks of the body in order; cell paragraphs hang off their table. */
22
+ blocks,
23
+ /** Every listed record in document order: a table before its cell paragraphs, a paragraph before its inlines. */
24
+ records, byId,
25
+ /** Id of every `w:p` of the main part, listed or not. */
26
+ paragraphIds,
27
+ /** Ids the engine owns (never written by an edit), in document order. */
28
+ unauthoredIds,
29
+ /** Every paragraph id the document uses, in any story part. */
30
+ takenIds, styles) {
31
+ this.revision = revision;
32
+ this.mainPart = mainPart;
33
+ this.document = document;
34
+ this.body = body;
35
+ this.bodySectPr = bodySectPr;
36
+ this.blocks = blocks;
37
+ this.records = records;
38
+ this.byId = byId;
39
+ this.paragraphIds = paragraphIds;
40
+ this.unauthoredIds = unauthoredIds;
41
+ this.takenIds = takenIds;
42
+ this.styles = styles;
43
+ }
44
+ /**
45
+ * Builds the index. `unauthored` carries the ids of the paragraphs that
46
+ * have no `w14:paraId`, in document order, as the engine tracks them
47
+ * between edits; without it the ids are computed from the bytes, as the
48
+ * display pre-pass computes them.
49
+ */
50
+ static async load(pkg, signal, unauthored, cachedStyles) {
51
+ const root = await pkg.relationships("/", signal);
52
+ const main = root.byType(`${OFFICE_RELATIONSHIPS}officeDocument`)[0]
53
+ ?.targetPart;
54
+ if (!main || !pkg.has(main))
55
+ throw new ViewerError("invalid-file", "The package has no document part");
56
+ const document = await pkg.xml(main, signal);
57
+ const body = document.root.children.find((child) => child.local === "body" && child.namespace === W_NS);
58
+ if (!body)
59
+ throw new ViewerError("invalid-file", "The document part has no body");
60
+ // Ids are numbered across every story part, the main part first.
61
+ const relationships = await pkg.relationships(main, signal);
62
+ const state = newIdState();
63
+ collectIds(document, state);
64
+ for (const type of STORY_RELATIONSHIP_TYPES)
65
+ for (const item of relationships.byType(type))
66
+ if (item.targetPart && pkg.has(item.targetPart))
67
+ collectIds(await pkg.xml(item.targetPart, signal), state);
68
+ const paragraphIds = new Map();
69
+ const unauthoredIds = [];
70
+ if (unauthored) {
71
+ // The engine's ids: a paragraph without `w14:paraId` takes the next
72
+ // one in order; a paragraph that carries one the engine stamped into
73
+ // a shown copy (restored as a base) keeps it and stays unauthored.
74
+ const owned = new Set(unauthored);
75
+ const paragraphs = paragraphsOf(document);
76
+ const attributed = new Set();
77
+ for (const paragraph of paragraphs) {
78
+ const id = document.attribute(paragraph, "w14:paraId");
79
+ if (id)
80
+ attributed.add(id.toUpperCase());
81
+ }
82
+ const queue = unauthored.filter((id) => !attributed.has(id));
83
+ for (const paragraph of paragraphs) {
84
+ const authored = document.attribute(paragraph, "w14:paraId");
85
+ if (authored) {
86
+ const id = authored.toUpperCase();
87
+ paragraphIds.set(paragraph, id);
88
+ if (owned.has(id))
89
+ unauthoredIds.push(id);
90
+ state.taken.add(id);
91
+ continue;
92
+ }
93
+ const id = queue.shift();
94
+ if (id === undefined)
95
+ throw new ViewerError("internal", "The document has more unmarked paragraphs than the session knows");
96
+ paragraphIds.set(paragraph, id);
97
+ unauthoredIds.push(id);
98
+ state.taken.add(id);
99
+ }
100
+ if (queue.length > 0)
101
+ throw new ViewerError("internal", "The session knows more unmarked paragraphs than the document has");
102
+ }
103
+ else
104
+ for (const entry of assignParagraphIds(document, state)) {
105
+ paragraphIds.set(entry.paragraph, entry.id);
106
+ if (!entry.authored)
107
+ unauthoredIds.push(entry.id);
108
+ }
109
+ const styles = cachedStyles ?? (await DocxStyles.load(pkg, main, signal));
110
+ const builder = new Builder(document, paragraphIds);
111
+ let bodySectPr;
112
+ for (const child of body.children) {
113
+ if (child.namespace === W_NS && child.local === "sectPr") {
114
+ bodySectPr = child;
115
+ continue;
116
+ }
117
+ builder.block(child, undefined, undefined);
118
+ }
119
+ return new DocxModel(pkg.revision, main, document, body, bodySectPr, builder.blocks, builder.records, builder.byId, paragraphIds, unauthoredIds, state.taken, styles);
120
+ }
121
+ /** The unauthored ids as a set, for "is this paragraph's id written by an edit" checks. */
122
+ get unauthoredSet() {
123
+ return (this.#unauthoredSet ??= new Set(this.unauthoredIds));
124
+ }
125
+ #unauthoredSet;
126
+ get paragraphs() {
127
+ return this.records.filter((record) => record.kind === "paragraph");
128
+ }
129
+ }
130
+ class Builder {
131
+ part;
132
+ ids;
133
+ blocks = [];
134
+ records = [];
135
+ byId = new Map();
136
+ constructor(part, ids) {
137
+ this.part = part;
138
+ this.ids = ids;
139
+ }
140
+ /** A child of the body or of a cell; wrappers are unwrapped. */
141
+ block(node, table, wrapper, into) {
142
+ if (node.namespace !== W_NS)
143
+ return;
144
+ switch (node.local) {
145
+ case "p": {
146
+ const record = this.paragraph(node, table, wrapper);
147
+ if (into)
148
+ into.push(record);
149
+ else
150
+ this.blocks.push(record);
151
+ return;
152
+ }
153
+ case "tbl":
154
+ // A table inside a cell is left out: its structure is not edited here.
155
+ if (!table) {
156
+ const record = this.table(node, wrapper);
157
+ if (record)
158
+ this.blocks.push(record);
159
+ }
160
+ return;
161
+ case "sdt": {
162
+ const content = node.children.find((child) => child.local === "sdtContent" && child.namespace === W_NS);
163
+ if (content)
164
+ for (const child of content.children)
165
+ this.block(child, table, node, into);
166
+ return;
167
+ }
168
+ case "customXml":
169
+ for (const child of node.children)
170
+ this.block(child, table, node, into);
171
+ return;
172
+ default:
173
+ return;
174
+ }
175
+ }
176
+ paragraph(node, table, wrapper) {
177
+ const id = this.ids.get(node) ?? "00000000";
178
+ const pPr = node.children.find((child) => child.local === "pPr" && child.namespace === W_NS);
179
+ const sectPr = pPr?.children.find((child) => child.local === "sectPr" && child.namespace === W_NS);
180
+ const text = readParagraphText(this.part, node);
181
+ const fieldOnly = text.items.some((item) => item.kind === "field") &&
182
+ !text.items.some((item) => item.kind === "text" && item.text.length > 0);
183
+ const readOnlyReason = text.tracked
184
+ ? "tracked-changes"
185
+ : sectPr
186
+ ? "section-break"
187
+ : fieldOnly
188
+ ? "unsupported-content"
189
+ : undefined;
190
+ const inlines = [];
191
+ const record = {
192
+ kind: "paragraph",
193
+ id,
194
+ elementId: `p:${id}`,
195
+ node,
196
+ ...(pPr ? { pPr } : {}),
197
+ ...(sectPr ? { sectPr } : {}),
198
+ text,
199
+ ...(table ? { table } : {}),
200
+ ...(wrapper ? { wrapper } : {}),
201
+ ...(readOnlyReason ? { readOnlyReason } : {}),
202
+ inlines,
203
+ };
204
+ this.register(record);
205
+ let pictures = 0;
206
+ let others = 0;
207
+ for (const item of text.items) {
208
+ if (item.kind === "picture") {
209
+ const inline = item.child.children.find((child) => child.local === "inline");
210
+ const extentNode = inline?.children.find((child) => child.local === "extent");
211
+ const cx = Number(extentNode ? this.part.attribute(extentNode, "cx") : NaN);
212
+ const cy = Number(extentNode ? this.part.attribute(extentNode, "cy") : NaN);
213
+ const picture = {
214
+ kind: "image",
215
+ elementId: `img:${id}.${pictures}`,
216
+ paragraph: record,
217
+ node: item.child,
218
+ run: item.run,
219
+ ...(Number.isFinite(cx) && Number.isFinite(cy)
220
+ ? { extent: { cx, cy } }
221
+ : {}),
222
+ };
223
+ pictures += 1;
224
+ inlines.push(picture);
225
+ this.register(picture);
226
+ }
227
+ else if (item.kind === "anchor" || item.kind === "object") {
228
+ const other = {
229
+ kind: "other",
230
+ elementId: `other:${id}.${others}`,
231
+ paragraph: record,
232
+ node: item.child,
233
+ ...(item.run === item.child ? {} : { run: item.run }),
234
+ };
235
+ others += 1;
236
+ inlines.push(other);
237
+ this.register(other);
238
+ }
239
+ }
240
+ return record;
241
+ }
242
+ table(node, wrapper) {
243
+ // The table is named after the first paragraph of its first cell, as
244
+ // listed: a nested table at the top of that cell does not count.
245
+ const firstCell = this.cellsOf(this.rowsOf(node)[0] ?? node)[0];
246
+ const first = firstCell ? this.firstParagraphOf(firstCell) : undefined;
247
+ const id = first ? this.ids.get(first) : undefined;
248
+ if (!id)
249
+ return undefined;
250
+ const rows = [];
251
+ const record = {
252
+ kind: "table",
253
+ id,
254
+ elementId: `tbl:${id}`,
255
+ node,
256
+ rows,
257
+ ...(wrapper ? { wrapper } : {}),
258
+ };
259
+ this.register(record);
260
+ for (const row of this.rowsOf(node))
261
+ rows.push(this.cellsOf(row).map((cell) => this.cell(cell, record)));
262
+ return record;
263
+ }
264
+ /** The first `w:p` child of a cell, through block-level wrappers. */
265
+ firstParagraphOf(cell) {
266
+ for (const child of cell.children) {
267
+ if (child.namespace !== W_NS)
268
+ continue;
269
+ if (child.local === "p")
270
+ return child;
271
+ if (child.local === "sdt" ||
272
+ child.local === "sdtContent" ||
273
+ child.local === "customXml") {
274
+ const nested = this.firstParagraphOf(child);
275
+ if (nested)
276
+ return nested;
277
+ }
278
+ }
279
+ return undefined;
280
+ }
281
+ cell(node, table) {
282
+ const paragraphs = [];
283
+ for (const child of node.children)
284
+ this.block(child, table, undefined, paragraphs);
285
+ return { node, paragraphs };
286
+ }
287
+ /** `w:tr` children of a table, through row-level `w:sdt` wrappers. */
288
+ rowsOf(table) {
289
+ const out = [];
290
+ const walk = (node) => {
291
+ for (const child of node.children) {
292
+ if (child.namespace !== W_NS)
293
+ continue;
294
+ if (child.local === "tr")
295
+ out.push(child);
296
+ else if (child.local === "sdt" ||
297
+ child.local === "sdtContent" ||
298
+ child.local === "customXml")
299
+ walk(child);
300
+ }
301
+ };
302
+ walk(table);
303
+ return out;
304
+ }
305
+ cellsOf(row) {
306
+ const out = [];
307
+ const walk = (node) => {
308
+ for (const child of node.children) {
309
+ if (child.namespace !== W_NS)
310
+ continue;
311
+ if (child.local === "tc")
312
+ out.push(child);
313
+ else if (child.local === "sdt" ||
314
+ child.local === "sdtContent" ||
315
+ child.local === "customXml")
316
+ walk(child);
317
+ }
318
+ };
319
+ walk(row);
320
+ return out;
321
+ }
322
+ register(record) {
323
+ this.records.push(record);
324
+ this.byId.set(record.elementId, record);
325
+ }
326
+ }
327
+ /** The text of a table: cells joined by tabs, rows by newlines. */
328
+ export function tableText(table) {
329
+ return table.rows
330
+ .map((row) => row.map((cell) => cellText(cell)).join("\t"))
331
+ .join("\n");
332
+ }
333
+ export function cellText(cell) {
334
+ return cell.paragraphs.map((paragraph) => paragraph.text.text).join("\n");
335
+ }
336
+ /** The paragraph a cell paragraph's table starts with, for `tbl:` ids. */
337
+ export function tableRows(table) {
338
+ return table.rows.map((row) => row.map((cell) => cellText(cell)));
339
+ }
@@ -0,0 +1,37 @@
1
+ import type { ResourceLimits, ViewerWarning } from "../../contracts.js";
2
+ import type { AssetSource } from "../assets.js";
3
+ import type { OoxmlPackage } from "../ooxml/package.js";
4
+ import type { OperationIssue } from "../types.js";
5
+ import type { DocxModel } from "./model.js";
6
+ import type { DocxOperation } from "./types.js";
7
+ export interface DocxOperationContext {
8
+ readonly pkg: OoxmlPackage;
9
+ readonly model: DocxModel;
10
+ readonly limits: ResourceLimits;
11
+ /** Bytes behind `asset:` references. */
12
+ readonly assets: AssetSource;
13
+ /** The batch's state id, for ids an operation derives. */
14
+ readonly stateId: number;
15
+ readonly operationIndex: number;
16
+ /** A paragraph id no paragraph of the document or of this batch uses. */
17
+ freshParagraphId(): string;
18
+ }
19
+ export interface DocxOperationResult {
20
+ readonly createdIds: readonly string[];
21
+ readonly removedIds?: readonly string[];
22
+ readonly warnings: readonly ViewerWarning[];
23
+ /** Paragraphs the operation wrote a `w14:paraId` on, by id. */
24
+ readonly stamped?: readonly string[];
25
+ /** Paragraphs the operation removed from the document, by id, nested ones included. */
26
+ readonly removedParagraphIds?: readonly string[];
27
+ /** The paragraph id the document reflows from. */
28
+ readonly reflowFrom?: string;
29
+ /** Old id → new id, for a table whose first paragraph changed. */
30
+ readonly remappedIds?: Readonly<Record<string, string>>;
31
+ }
32
+ export type Issue = (path: string, code: string, message: string) => void;
33
+ export interface DocxOperationHandler<T extends DocxOperation = DocxOperation> {
34
+ validate(operation: T, context: DocxOperationContext, issue: Issue): Promise<void>;
35
+ apply(operation: T, context: DocxOperationContext): Promise<DocxOperationResult>;
36
+ }
37
+ export declare function issueCollector(operationIndex: number, issues: OperationIssue[]): Issue;
@@ -0,0 +1,3 @@
1
+ export function issueCollector(operationIndex, issues) {
2
+ return (path, code, message) => issues.push({ operationIndex, path, code, message });
3
+ }
@@ -0,0 +1,13 @@
1
+ import type { EditEngineContext } from "../engine.js";
2
+ import { type OoxmlEditProviderOptions } from "../ooxml/worker.js";
3
+ import { WorkerEngineClient } from "../worker-engine.js";
4
+ export type DocxEditProviderOptions = OoxmlEditProviderOptions;
5
+ /**
6
+ * Starts the OOXML edit worker for `original` as a DOCX engine. Loaded
7
+ * lazily by the Office adapter's provider on the first `edit()`.
8
+ */
9
+ export declare function loadDocxEditEngine(original: Uint8Array, context: EditEngineContext, options?: DocxEditProviderOptions): Promise<DocxEditEngineClient>;
10
+ export declare class DocxEditEngineClient extends WorkerEngineClient {
11
+ readonly schemas: import("../types.js").OperationSchemaSet;
12
+ start(original: Uint8Array): Promise<void>;
13
+ }
@@ -0,0 +1,34 @@
1
+ import { WorkerRpcClient } from "../../worker-client.js";
2
+ import { createOoxmlEditWorker, } from "../ooxml/worker.js";
3
+ import { WorkerEngineClient } from "../worker-engine.js";
4
+ import { docxOperationSchemas } from "./schemas.js";
5
+ /**
6
+ * Starts the OOXML edit worker for `original` as a DOCX engine. Loaded
7
+ * lazily by the Office adapter's provider on the first `edit()`.
8
+ */
9
+ export async function loadDocxEditEngine(original, context, options = {}) {
10
+ const worker = createOoxmlEditWorker(options, context);
11
+ const rpc = new WorkerRpcClient(worker);
12
+ const engine = new DocxEditEngineClient(rpc, context);
13
+ try {
14
+ await engine.start(original);
15
+ return engine;
16
+ }
17
+ catch (error) {
18
+ rpc.destroy();
19
+ throw error;
20
+ }
21
+ }
22
+ export class DocxEditEngineClient extends WorkerEngineClient {
23
+ schemas = docxOperationSchemas;
24
+ async start(original) {
25
+ const data = original.slice().buffer;
26
+ const open = {
27
+ data,
28
+ limits: this.context.limits,
29
+ format: "docx",
30
+ ...(this.context.fileName ? { fileName: this.context.fileName } : {}),
31
+ };
32
+ await this.request("edit-open", open, this.context.signal, [data]);
33
+ }
34
+ }
@@ -0,0 +1,6 @@
1
+ import type { OperationSchemaSet } from "../types.js";
2
+ /** Operations with an engine handler; grows as the tasks land. */
3
+ export declare const IMPLEMENTED_OPERATIONS: readonly string[];
4
+ export declare const docxOperationSchemas: OperationSchemaSet;
5
+ /** Every schema, implemented or not, for the conformance test. */
6
+ export declare const docxOperationSchemaDrafts: Readonly<Record<string, Readonly<Record<string, unknown>>>>;