@docen/docx 0.3.4 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{blockquote-DY80QC06.d.mts → blockquote-D-1aSxEn.d.mts} +5 -1
- package/dist/converters/docx.d.mts +40 -66
- package/dist/converters/docx.mjs +251 -666
- package/dist/converters/html.d.mts +1 -1
- package/dist/converters/html.mjs +3 -4
- package/dist/converters/markdown.d.mts +1 -1
- package/dist/converters/markdown.mjs +1 -1
- package/dist/converters/patch.d.mts +1 -1
- package/dist/converters/prepare.d.mts +1 -1
- package/dist/converters/styles.d.mts +58 -14
- package/dist/converters/styles.mjs +124 -16
- package/dist/{core-DC0_-WcE.d.mts → core-BqyLL84S.d.mts} +34 -13
- package/dist/{core-BnF8XhVE.mjs → core-wNNPJiKr.mjs} +357 -25
- package/dist/core.d.mts +1 -1
- package/dist/core.mjs +1 -1
- package/dist/details-DsJhDP5K.d.mts +31 -0
- package/dist/editor.d.mts +1 -1
- package/dist/editor.mjs +1 -1
- package/dist/extensions/blockquote.d.mts +2 -3
- package/dist/extensions/blockquote.mjs +50 -2
- package/dist/extensions/bullet-list.mjs +1 -1
- package/dist/extensions/code-block.d.mts +2 -2
- package/dist/extensions/code-block.mjs +51 -5
- package/dist/extensions/column-break.d.mts +2 -2
- package/dist/extensions/column-break.mjs +2 -2
- package/dist/extensions/details.d.mts +2 -3
- package/dist/extensions/details.mjs +48 -2
- package/dist/extensions/document.mjs +1 -1
- package/dist/extensions/extensions.d.mts +13 -9
- package/dist/extensions/extensions.mjs +7 -3
- package/dist/extensions/formatting-marks.d.mts +1 -1
- package/dist/extensions/formatting-marks.mjs +1 -1
- package/dist/extensions/heading.d.mts +2 -2
- package/dist/extensions/heading.mjs +61 -5
- package/dist/extensions/image.d.mts +2 -2
- package/dist/extensions/image.mjs +23 -4
- package/dist/extensions/index.d.mts +14 -10
- package/dist/extensions/index.mjs +8 -4
- package/dist/extensions/link.d.mts +2 -2
- package/dist/extensions/link.mjs +30 -2
- package/dist/extensions/list-aggregator.d.mts +8 -0
- package/dist/extensions/list-aggregator.mjs +2 -0
- package/dist/extensions/marks.d.mts +2 -0
- package/dist/extensions/marks.mjs +41 -0
- package/dist/extensions/mention.d.mts +2 -3
- package/dist/extensions/mention.mjs +16 -2
- package/dist/extensions/ordered-list.mjs +1 -1
- package/dist/extensions/page-break.d.mts +2 -2
- package/dist/extensions/page-break.mjs +2 -2
- package/dist/extensions/paragraph.mjs +3 -3
- package/dist/extensions/passthrough.d.mts +1 -1
- package/dist/extensions/passthrough.mjs +1 -1
- package/dist/extensions/scroll.d.mts +1 -1
- package/dist/extensions/scroll.mjs +30 -5
- package/dist/extensions/section-break.d.mts +1 -1
- package/dist/extensions/section-break.mjs +1 -1
- package/dist/extensions/strike.d.mts +2 -2
- package/dist/extensions/strike.mjs +5 -24
- package/dist/extensions/tab.d.mts +2 -2
- package/dist/extensions/tab.mjs +2 -2
- package/dist/extensions/table-cell.mjs +2 -2
- package/dist/extensions/table-header.mjs +2 -2
- package/dist/extensions/table-row.mjs +2 -2
- package/dist/extensions/table.d.mts +2 -2
- package/dist/extensions/table.mjs +122 -11
- package/dist/extensions/task-item.d.mts +2 -3
- package/dist/extensions/task-item.mjs +2 -2
- package/dist/extensions/text-style.d.mts +2 -2
- package/dist/extensions/text-style.mjs +27 -28
- package/dist/extensions/toc-field.d.mts +2 -2
- package/dist/extensions/toc-field.mjs +2 -2
- package/dist/extensions/track-change.d.mts +2 -0
- package/dist/extensions/track-change.mjs +2 -0
- package/dist/extensions/types.d.mts +127 -8
- package/dist/extensions/utils.d.mts +2 -2
- package/dist/extensions/utils.mjs +74 -1
- package/dist/extensions/wpg-group.d.mts +2 -2
- package/dist/extensions/wpg-group.mjs +2 -2
- package/dist/extensions/wps-shape.d.mts +2 -2
- package/dist/extensions/wps-shape.mjs +2 -2
- package/dist/heading-Bwpa8iZY.d.mts +24 -0
- package/dist/index.d.mts +29 -11
- package/dist/index.mjs +9 -5
- package/dist/{link-BawPjQZR.d.mts → link-gUqW45mE.d.mts} +3 -1
- package/dist/marks-Dz9Vb22Q.d.mts +10 -0
- package/dist/{mention-BGLzLVYw.d.mts → mention-CkONDrw9.d.mts} +5 -1
- package/dist/{scroll-ZNeThJsJ.d.mts → scroll-BARiZ5Gm.d.mts} +9 -3
- package/dist/strike-Brn9sWFy.d.mts +16 -0
- package/dist/table-CdcjR6HD.d.mts +18 -0
- package/dist/{task-item-B0ntvQ1Y.d.mts → task-item-CCAC4QLi.d.mts} +3 -1
- package/dist/text-style-BzfcbufI.d.mts +4 -0
- package/dist/{utils-BJwDQts7.d.mts → utils-CfwwOowz.d.mts} +25 -1
- package/package.json +3 -3
- package/dist/details-Dd5MqqmR.d.mts +0 -17
- package/dist/extensions/tiptap.d.mts +0 -2
- package/dist/extensions/tiptap.mjs +0 -31
- package/dist/heading-BvqBD2zX.d.mts +0 -8
- package/dist/strike-BgWGvjKr.d.mts +0 -33
- package/dist/table-BFkfeRp9.d.mts +0 -9
- package/dist/text-style-BHdtXkMb.d.mts +0 -8
- package/dist/tiptap-BKqn41uT.d.mts +0 -31
package/dist/converters/docx.mjs
CHANGED
|
@@ -1,30 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { alignmentFromCss } from "../extensions/utils.mjs";
|
|
2
|
+
import { buildTextBlock } from "./styles.mjs";
|
|
3
|
+
import { applyBlockquoteStyle } from "../extensions/blockquote.mjs";
|
|
4
|
+
import { h as docxExtensions } from "../core-wNNPJiKr.mjs";
|
|
3
5
|
import { DETAILS_SUMMARY_STYLE, DETAILS_TAG } from "../extensions/details.mjs";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { createMention, isMention, readMention } from "../extensions/mention.mjs";
|
|
6
|
+
import { createTaskCheckbox } from "../extensions/task-item.mjs";
|
|
7
|
+
import { createMention } from "../extensions/mention.mjs";
|
|
7
8
|
import { ORDERED_REFERENCE_PREFIX, buildOrderedLevels } from "../extensions/ordered-list.mjs";
|
|
8
|
-
import { parseDocx as parseDocx$2, renderDocx as renderDocx$3 } from "../extensions/paragraph.mjs";
|
|
9
|
-
import { parseDocx as parseDocx$3, renderDocx as renderDocx$4 } from "../extensions/strike.mjs";
|
|
10
|
-
import { parseDocx as parseDocx$4, renderDocx as renderDocx$5 } from "../extensions/table.mjs";
|
|
11
|
-
import { parseDocx as parseDocx$5, renderDocx as renderDocx$6 } from "../extensions/table-cell.mjs";
|
|
12
|
-
import { parseDocx as parseDocx$6, renderDocx as renderDocx$7 } from "../extensions/table-header.mjs";
|
|
13
|
-
import { parseDocx as parseDocx$7, renderDocx as renderDocx$8 } from "../extensions/table-row.mjs";
|
|
14
|
-
import { createTaskCheckbox, isTaskCheckbox, readCheckboxState } from "../extensions/task-item.mjs";
|
|
15
|
-
import { parseDocx as parseDocx$8, renderDocx as renderDocx$9 } from "../extensions/text-style.mjs";
|
|
16
9
|
import { prepareDocument } from "./prepare.mjs";
|
|
17
|
-
import {
|
|
10
|
+
import { flattenExtensions, getExtensionField, getSchema } from "@tiptap/core";
|
|
18
11
|
import { emojis, shortcodeToEmoji } from "@tiptap/extension-emoji";
|
|
19
12
|
import { generateDocument, generateDocumentStream, generateDocumentSync, parseDocument } from "@office-open/docx";
|
|
20
|
-
import { encodeBase64 } from "@office-open/core";
|
|
21
13
|
//#region src/converters/docx.ts
|
|
22
|
-
/** Remove keys with null/undefined values */
|
|
23
|
-
function cleanAttrs(attrs) {
|
|
24
|
-
const result = {};
|
|
25
|
-
for (const [key, value] of Object.entries(attrs)) if (value !== null && value !== void 0) result[key] = value;
|
|
26
|
-
return result;
|
|
27
|
-
}
|
|
28
14
|
/** True when two cell-margin sets (w:tcMar / w:tblCellMar) match on every side's
|
|
29
15
|
* size — used to detect a cell that merely echoes the table's default so its
|
|
30
16
|
* redundant tcMar can be dropped for a near-identity round-trip. */
|
|
@@ -37,6 +23,14 @@ function sameCellMargins(a, b) {
|
|
|
37
23
|
"left"
|
|
38
24
|
].every((k) => sz(a, k) === sz(b, k));
|
|
39
25
|
}
|
|
26
|
+
/** True when two single-side borders match on style/size/color — used to detect
|
|
27
|
+
* a cell side that merely echoes the table's insideHorizontal/insideVertical
|
|
28
|
+
* so it can be dropped for a near-identity round-trip (resolveTable pushes
|
|
29
|
+
* those table-level grid lines onto cell sides lacking their own tcBorder). */
|
|
30
|
+
function sameBorder(a, b) {
|
|
31
|
+
if (!a || !b) return false;
|
|
32
|
+
return a.style === b.style && a.size === b.size && a.color === b.color;
|
|
33
|
+
}
|
|
40
34
|
/** Keys round-tripped between DocumentOptions core properties and `doc.attrs.core`. */
|
|
41
35
|
const CORE_PROPERTY_KEYS = [
|
|
42
36
|
"title",
|
|
@@ -55,7 +49,7 @@ const CORE_PROPERTY_KEYS = [
|
|
|
55
49
|
* carries in dedicated attrs (styles/background/core). Excluded from the
|
|
56
50
|
* documentExtras pass-through so they aren't duplicated.
|
|
57
51
|
*/
|
|
58
|
-
const COMPILE_OWNED_KEYS = new Set([
|
|
52
|
+
const COMPILE_OWNED_KEYS = /* @__PURE__ */ new Set([
|
|
59
53
|
"sections",
|
|
60
54
|
"numbering",
|
|
61
55
|
"styles",
|
|
@@ -72,21 +66,6 @@ function extractCoreProperties(docOpts) {
|
|
|
72
66
|
}
|
|
73
67
|
return Object.keys(core).length > 0 ? core : null;
|
|
74
68
|
}
|
|
75
|
-
/** Merge consecutive text nodes with same marks */
|
|
76
|
-
function mergeTextNodes(nodes) {
|
|
77
|
-
const result = [];
|
|
78
|
-
for (const node of nodes) {
|
|
79
|
-
if (node.type === "text" && result.length > 0 && result[result.length - 1].type === "text") {
|
|
80
|
-
const prev = result[result.length - 1];
|
|
81
|
-
if (JSON.stringify(prev.marks) === JSON.stringify(node.marks)) {
|
|
82
|
-
prev.text = (prev.text ?? "") + (node.text ?? "");
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
result.push({ ...node });
|
|
87
|
-
}
|
|
88
|
-
return result;
|
|
89
|
-
}
|
|
90
69
|
/**
|
|
91
70
|
* Manages DOCX serialization (Tiptap JSON ↔ DocumentOptions).
|
|
92
71
|
*
|
|
@@ -97,6 +76,63 @@ var DocxManager = class {
|
|
|
97
76
|
numberingConfigs = [];
|
|
98
77
|
orderedInstanceCounter = 0;
|
|
99
78
|
resolveStyles;
|
|
79
|
+
resolveNumberingLookup;
|
|
80
|
+
markRender = /* @__PURE__ */ new Map();
|
|
81
|
+
markParse = [];
|
|
82
|
+
nodeRender = /* @__PURE__ */ new Map();
|
|
83
|
+
nodeParse = /* @__PURE__ */ new Map();
|
|
84
|
+
blockRules = [];
|
|
85
|
+
inlineRules = [];
|
|
86
|
+
paragraphRules = [];
|
|
87
|
+
aggregatorRules = [];
|
|
88
|
+
resolveCtx;
|
|
89
|
+
constructor(extensions = docxExtensions) {
|
|
90
|
+
const schema = getSchema(extensions);
|
|
91
|
+
for (const ext of flattenExtensions(extensions)) {
|
|
92
|
+
const name = ext.name;
|
|
93
|
+
if (!name) continue;
|
|
94
|
+
if (schema.marks[name]) {
|
|
95
|
+
const render = getExtensionField(ext, "renderDocx");
|
|
96
|
+
const parse = getExtensionField(ext, "parseDocx");
|
|
97
|
+
if (render) this.markRender.set(name, render);
|
|
98
|
+
if (parse) this.markParse.push({
|
|
99
|
+
name,
|
|
100
|
+
parse
|
|
101
|
+
});
|
|
102
|
+
} else if (schema.nodes[name]) {
|
|
103
|
+
const render = getExtensionField(ext, "renderDocx");
|
|
104
|
+
const parse = getExtensionField(ext, "parseDocx");
|
|
105
|
+
if (render) this.nodeRender.set(name, render);
|
|
106
|
+
if (parse) this.nodeParse.set(name, parse);
|
|
107
|
+
const blockRule = getExtensionField(ext, "parseDocxBlock");
|
|
108
|
+
if (blockRule) this.blockRules.push({
|
|
109
|
+
name,
|
|
110
|
+
rule: blockRule
|
|
111
|
+
});
|
|
112
|
+
const paraRule = getExtensionField(ext, "parseDocxParagraph");
|
|
113
|
+
if (paraRule) this.paragraphRules.push({
|
|
114
|
+
name,
|
|
115
|
+
rule: paraRule
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
const inlineRule = getExtensionField(ext, "parseDocxInline");
|
|
119
|
+
if (inlineRule) this.inlineRules.push({
|
|
120
|
+
name,
|
|
121
|
+
rule: inlineRule
|
|
122
|
+
});
|
|
123
|
+
const aggregator = getExtensionField(ext, "parseDocxAggregator");
|
|
124
|
+
if (aggregator) this.aggregatorRules.push({
|
|
125
|
+
name,
|
|
126
|
+
rule: aggregator
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/** Reflective node renderDocx lookup: the node's DOCX opts, or {} when the
|
|
131
|
+
* node type has no renderDocx hook (degrades to a plain paragraph). node.type
|
|
132
|
+
* is optional on JSONContent — an absent type simply misses the map. */
|
|
133
|
+
renderNodeOpts(node) {
|
|
134
|
+
return this.nodeRender.get(node.type ?? "")?.(node) ?? {};
|
|
135
|
+
}
|
|
100
136
|
compile(json) {
|
|
101
137
|
this.numberingConfigs = [];
|
|
102
138
|
this.orderedInstanceCounter = 0;
|
|
@@ -170,7 +206,7 @@ var DocxManager = class {
|
|
|
170
206
|
* Resolve a section's header/footer group (SectionChild[] per slot) into
|
|
171
207
|
* Tiptap JSON slots. Returns null when no slot has content.
|
|
172
208
|
*/
|
|
173
|
-
resolveHeaderFooter(group
|
|
209
|
+
resolveHeaderFooter(group) {
|
|
174
210
|
if (!group) return null;
|
|
175
211
|
const slots = {};
|
|
176
212
|
for (const slot of [
|
|
@@ -179,7 +215,7 @@ var DocxManager = class {
|
|
|
179
215
|
"even"
|
|
180
216
|
]) {
|
|
181
217
|
const children = group[slot];
|
|
182
|
-
if (children?.length) slots[slot] = this.resolveSectionChildren(children
|
|
218
|
+
if (children?.length) slots[slot] = this.resolveSectionChildren(children);
|
|
183
219
|
}
|
|
184
220
|
return Object.keys(slots).length > 0 ? slots : null;
|
|
185
221
|
}
|
|
@@ -190,17 +226,29 @@ var DocxManager = class {
|
|
|
190
226
|
type: "doc",
|
|
191
227
|
content: [{ type: "paragraph" }]
|
|
192
228
|
};
|
|
193
|
-
|
|
229
|
+
this.resolveNumberingLookup = this.buildNumberingLookup(docOpts);
|
|
230
|
+
const styles = this.resolveStyles;
|
|
231
|
+
this.resolveCtx = {
|
|
232
|
+
resolveBlockStream: (children) => this.resolveSectionChildren(children),
|
|
233
|
+
resolveBlock: (child) => this.resolveSectionChild(child),
|
|
234
|
+
resolveInlineContent: (para) => this.resolveInlineContent(para),
|
|
235
|
+
resolveInlineChildren: (children) => this.resolveParagraphChildren(children),
|
|
236
|
+
resolveParagraph: (para) => this.resolveParagraph(para),
|
|
237
|
+
parseNodeAttrs: (type, opts) => this.nodeParse.get(type)?.(opts) ?? {},
|
|
238
|
+
resolveMarks: (opts) => this.resolveMarks(opts),
|
|
239
|
+
styles,
|
|
240
|
+
numberingLookup: this.resolveNumberingLookup
|
|
241
|
+
};
|
|
194
242
|
const content = [];
|
|
195
243
|
const lastIndex = sections.length - 1;
|
|
196
244
|
for (let i = 0; i < sections.length; i++) {
|
|
197
245
|
const section = sections[i];
|
|
198
|
-
const sectionContent = this.resolveSectionChildren(section.children ?? []
|
|
246
|
+
const sectionContent = this.resolveSectionChildren(section.children ?? []);
|
|
199
247
|
if (i < lastIndex) {
|
|
200
248
|
const sectAttrs = {
|
|
201
249
|
sectionProperties: section.properties ?? null,
|
|
202
|
-
sectionHeaders: this.resolveHeaderFooter(section.headers
|
|
203
|
-
sectionFooters: this.resolveHeaderFooter(section.footers
|
|
250
|
+
sectionHeaders: this.resolveHeaderFooter(section.headers),
|
|
251
|
+
sectionFooters: this.resolveHeaderFooter(section.footers)
|
|
204
252
|
};
|
|
205
253
|
const last = sectionContent[sectionContent.length - 1];
|
|
206
254
|
if (last?.type === "paragraph") last.attrs = {
|
|
@@ -226,9 +274,9 @@ var DocxManager = class {
|
|
|
226
274
|
if (core) attrs.core = core;
|
|
227
275
|
const lastSection = sections[lastIndex];
|
|
228
276
|
if (lastSection.properties) attrs.sectionProperties = lastSection.properties;
|
|
229
|
-
const lastHeaders = this.resolveHeaderFooter(lastSection.headers
|
|
277
|
+
const lastHeaders = this.resolveHeaderFooter(lastSection.headers);
|
|
230
278
|
if (lastHeaders) attrs.sectionHeaders = lastHeaders;
|
|
231
|
-
const lastFooters = this.resolveHeaderFooter(lastSection.footers
|
|
279
|
+
const lastFooters = this.resolveHeaderFooter(lastSection.footers);
|
|
232
280
|
if (lastFooters) attrs.sectionFooters = lastFooters;
|
|
233
281
|
const documentExtras = {};
|
|
234
282
|
for (const [k, v] of Object.entries(docOpts)) {
|
|
@@ -255,7 +303,7 @@ var DocxManager = class {
|
|
|
255
303
|
return items.length > 0 ? items : null;
|
|
256
304
|
}
|
|
257
305
|
case "codeBlock": {
|
|
258
|
-
const opts =
|
|
306
|
+
const opts = this.renderNodeOpts(node);
|
|
259
307
|
const childList = this.compileInlineContent(node.content);
|
|
260
308
|
if (childList.length > 0) opts.children = childList;
|
|
261
309
|
return { paragraph: this.simplifyParagraph(opts) };
|
|
@@ -263,7 +311,7 @@ var DocxManager = class {
|
|
|
263
311
|
case "horizontalRule": return { paragraph: { thematicBreak: true } };
|
|
264
312
|
case "table": return { table: this.compileTableNode(node) };
|
|
265
313
|
case "image": {
|
|
266
|
-
const imageRun =
|
|
314
|
+
const imageRun = this.nodeRender.get(node.type)?.(node) ?? null;
|
|
267
315
|
if (!imageRun) return null;
|
|
268
316
|
return { paragraph: { children: [imageRun] } };
|
|
269
317
|
}
|
|
@@ -297,13 +345,13 @@ var DocxManager = class {
|
|
|
297
345
|
}
|
|
298
346
|
}
|
|
299
347
|
compileParagraphNode(node) {
|
|
300
|
-
const opts =
|
|
348
|
+
const opts = this.renderNodeOpts(node);
|
|
301
349
|
const childList = this.compileInlineContent(node.content);
|
|
302
350
|
if (childList.length > 0) opts.children = childList;
|
|
303
351
|
return this.simplifyParagraph(opts);
|
|
304
352
|
}
|
|
305
353
|
compileHeadingNode(node) {
|
|
306
|
-
const opts =
|
|
354
|
+
const opts = this.renderNodeOpts(node);
|
|
307
355
|
const childList = this.compileInlineContent(node.content);
|
|
308
356
|
if (childList.length > 0) opts.children = childList;
|
|
309
357
|
return this.simplifyParagraph(opts);
|
|
@@ -321,14 +369,17 @@ var DocxManager = class {
|
|
|
321
369
|
return opts;
|
|
322
370
|
}
|
|
323
371
|
compileTableNode(node) {
|
|
324
|
-
const opts =
|
|
372
|
+
const opts = this.renderNodeOpts(node);
|
|
325
373
|
const colCount = this.getTableColumnCount(node);
|
|
326
374
|
const tableCellMargins = opts.cellMargin ?? opts.margins ?? null;
|
|
375
|
+
const tableBorders = opts.borders ?? null;
|
|
376
|
+
const insideH = tableBorders?.insideHorizontal ?? null;
|
|
377
|
+
const insideV = tableBorders?.insideVertical ?? null;
|
|
327
378
|
const rows = [];
|
|
328
379
|
let activeSpans = [];
|
|
329
380
|
for (const rowNode of node.content ?? []) {
|
|
330
381
|
if (rowNode.type !== "tableRow") continue;
|
|
331
|
-
const rowOpts =
|
|
382
|
+
const rowOpts = this.nodeRender.get(rowNode.type)?.(rowNode) ?? {};
|
|
332
383
|
const pmCells = (rowNode.content ?? []).filter((c) => c.type === "tableCell" || c.type === "tableHeader");
|
|
333
384
|
const currentSpans = [...activeSpans].sort((a, b) => a.colStart - b.colStart);
|
|
334
385
|
const newSpans = [];
|
|
@@ -343,12 +394,13 @@ var DocxManager = class {
|
|
|
343
394
|
compiledCells.push({
|
|
344
395
|
verticalMerge: "continue",
|
|
345
396
|
columnSpan: span.colspan,
|
|
397
|
+
...span.borders ? { borders: span.borders } : {},
|
|
346
398
|
children: [{ paragraph: "" }]
|
|
347
399
|
});
|
|
348
400
|
colIdx += span.colspan;
|
|
349
401
|
spanIdx++;
|
|
350
402
|
} else {
|
|
351
|
-
const cell = this.compileTableCellNode(pmCells[cellIdx], tableCellMargins);
|
|
403
|
+
const cell = this.compileTableCellNode(pmCells[cellIdx], tableCellMargins, insideH, insideV);
|
|
352
404
|
const cs = cell.columnSpan ?? 1;
|
|
353
405
|
const rs = cell.rowSpan ?? 1;
|
|
354
406
|
if (rs > 1) {
|
|
@@ -357,7 +409,8 @@ var DocxManager = class {
|
|
|
357
409
|
newSpans.push({
|
|
358
410
|
colStart: colIdx,
|
|
359
411
|
colspan: cs,
|
|
360
|
-
remainingRows: rs - 1
|
|
412
|
+
remainingRows: rs - 1,
|
|
413
|
+
borders: cell.borders
|
|
361
414
|
});
|
|
362
415
|
}
|
|
363
416
|
compiledCells.push(cell);
|
|
@@ -444,16 +497,35 @@ var DocxManager = class {
|
|
|
444
497
|
}
|
|
445
498
|
};
|
|
446
499
|
}
|
|
447
|
-
compileTableCellNode(cellNode, tableMargins) {
|
|
448
|
-
const cellOpts =
|
|
500
|
+
compileTableCellNode(cellNode, tableMargins, insideH, insideV) {
|
|
501
|
+
const cellOpts = this.renderNodeOpts(cellNode);
|
|
449
502
|
if (tableMargins && cellOpts.margins && sameCellMargins(cellOpts.margins, tableMargins)) delete cellOpts.margins;
|
|
450
|
-
|
|
503
|
+
if ((insideH || insideV) && cellOpts.borders) {
|
|
504
|
+
const b = cellOpts.borders;
|
|
505
|
+
if (insideH && sameBorder(b.top, insideH)) delete b.top;
|
|
506
|
+
if (insideH && sameBorder(b.bottom, insideH)) delete b.bottom;
|
|
507
|
+
if (insideV && sameBorder(b.left, insideV)) delete b.left;
|
|
508
|
+
if (insideV && sameBorder(b.right, insideV)) delete b.right;
|
|
509
|
+
if (Object.keys(b).length === 0) delete cellOpts.borders;
|
|
510
|
+
}
|
|
511
|
+
const cellAlign = alignmentFromCss(cellNode.attrs?.align ?? null);
|
|
451
512
|
const cellChildren = [];
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
513
|
+
const pushChild = (child) => {
|
|
514
|
+
if (cellAlign && typeof child === "object" && child !== null && "paragraph" in child) {
|
|
515
|
+
const p = child.paragraph;
|
|
516
|
+
if (typeof p === "string") child.paragraph = {
|
|
517
|
+
text: p,
|
|
518
|
+
alignment: cellAlign
|
|
519
|
+
};
|
|
520
|
+
else if (p && typeof p === "object" && !p.alignment) p.alignment = cellAlign;
|
|
521
|
+
}
|
|
522
|
+
cellChildren.push(child);
|
|
523
|
+
};
|
|
524
|
+
for (const childNode of cellNode.content ?? []) {
|
|
525
|
+
const compiled = this.compileSectionChild(childNode);
|
|
526
|
+
if (compiled == null) continue;
|
|
527
|
+
if (Array.isArray(compiled)) compiled.forEach(pushChild);
|
|
528
|
+
else pushChild(compiled);
|
|
457
529
|
}
|
|
458
530
|
if (cellChildren.length > 0) cellOpts.children = cellChildren;
|
|
459
531
|
return cellOpts;
|
|
@@ -580,7 +652,7 @@ var DocxManager = class {
|
|
|
580
652
|
break;
|
|
581
653
|
}
|
|
582
654
|
case "image": {
|
|
583
|
-
const imageRun =
|
|
655
|
+
const imageRun = this.nodeRender.get(node.type)?.(node) ?? null;
|
|
584
656
|
if (imageRun) children.push(imageRun);
|
|
585
657
|
break;
|
|
586
658
|
}
|
|
@@ -628,69 +700,65 @@ var DocxManager = class {
|
|
|
628
700
|
/** Emit a single run for `text` with all inline marks applied. */
|
|
629
701
|
compileTextRun(text, marks, children) {
|
|
630
702
|
const runOpts = { text };
|
|
631
|
-
for (const mark of marks ?? [])
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
runOpts.superScript = true;
|
|
651
|
-
break;
|
|
652
|
-
case "highlight":
|
|
653
|
-
runOpts.highlight = mark.attrs?.color ?? "yellow";
|
|
654
|
-
break;
|
|
655
|
-
case "code":
|
|
656
|
-
runOpts.style = "CodeChar";
|
|
657
|
-
runOpts.font = "Consolas";
|
|
658
|
-
break;
|
|
659
|
-
case "textStyle": {
|
|
660
|
-
const tsProps = renderDocx$9(mark.attrs ?? {});
|
|
661
|
-
Object.assign(runOpts, tsProps);
|
|
662
|
-
break;
|
|
663
|
-
}
|
|
664
|
-
case "link": {
|
|
665
|
-
const href = mark.attrs?.href;
|
|
666
|
-
if (href) {
|
|
667
|
-
const { text: _, ...runWithoutText } = runOpts;
|
|
668
|
-
const linkChildren = [];
|
|
669
|
-
if (text) linkChildren.push({
|
|
670
|
-
...runWithoutText,
|
|
671
|
-
text
|
|
672
|
-
});
|
|
673
|
-
children.push({ hyperlink: {
|
|
674
|
-
link: href.startsWith("#") ? void 0 : href,
|
|
675
|
-
anchor: href.startsWith("#") ? href.slice(1) : void 0,
|
|
676
|
-
children: linkChildren
|
|
677
|
-
} });
|
|
678
|
-
return;
|
|
703
|
+
for (const mark of marks ?? []) {
|
|
704
|
+
switch (mark.type) {
|
|
705
|
+
case "link": {
|
|
706
|
+
const href = mark.attrs?.href;
|
|
707
|
+
if (href) {
|
|
708
|
+
const { text: _, ...runWithoutText } = runOpts;
|
|
709
|
+
const linkChildren = [];
|
|
710
|
+
if (text) linkChildren.push({
|
|
711
|
+
...runWithoutText,
|
|
712
|
+
text
|
|
713
|
+
});
|
|
714
|
+
children.push({ hyperlink: {
|
|
715
|
+
link: href.startsWith("#") ? void 0 : href,
|
|
716
|
+
anchor: href.startsWith("#") ? href.slice(1) : void 0,
|
|
717
|
+
children: linkChildren
|
|
718
|
+
} });
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
break;
|
|
679
722
|
}
|
|
680
|
-
|
|
723
|
+
case "insertion":
|
|
724
|
+
case "deletion":
|
|
725
|
+
children.push(this.compileTrackedChangeRun(mark.type, mark.attrs, text, runOpts));
|
|
726
|
+
return;
|
|
681
727
|
}
|
|
728
|
+
const render = this.markRender.get(mark.type);
|
|
729
|
+
if (render) Object.assign(runOpts, render(mark.attrs ?? {}));
|
|
682
730
|
}
|
|
683
731
|
children.push(runOpts);
|
|
684
732
|
}
|
|
733
|
+
/**
|
|
734
|
+
* Wrap a run back into a w:ins/w:del container — the reverse of
|
|
735
|
+
* resolveTrackedChange. A literal-key ternary (`{insertion: body}` /
|
|
736
|
+
* `{deletion: body}`) lets TS narrow to the ParagraphChild branch without a
|
|
737
|
+
* cast, and `typeof` guards read `attrs` type-safely (no `as number/string`).
|
|
738
|
+
* stringifyDeletedRun emits `<w:delText>` automatically for deletion children.
|
|
739
|
+
*/
|
|
740
|
+
compileTrackedChangeRun(type, attrs, text, runOpts) {
|
|
741
|
+
const { text: _, ...runWithoutText } = runOpts;
|
|
742
|
+
const trackChildren = [];
|
|
743
|
+
if (text) trackChildren.push({
|
|
744
|
+
...runWithoutText,
|
|
745
|
+
text
|
|
746
|
+
});
|
|
747
|
+
const body = {
|
|
748
|
+
id: typeof attrs?.id === "number" ? attrs.id : 0,
|
|
749
|
+
author: typeof attrs?.author === "string" ? attrs.author : "",
|
|
750
|
+
date: typeof attrs?.date === "string" ? attrs.date : "",
|
|
751
|
+
children: trackChildren
|
|
752
|
+
};
|
|
753
|
+
return type === "insertion" ? { insertion: body } : { deletion: body };
|
|
754
|
+
}
|
|
685
755
|
resolveSectionChild(child) {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
if (sdt.properties?.tag === "docen-details") return this.resolveDetailsSdt(sdt);
|
|
691
|
-
return this.resolvePassthrough(child);
|
|
756
|
+
const ctx = this.resolveCtx;
|
|
757
|
+
for (const { rule } of this.blockRules) if (rule.match(child, ctx)) {
|
|
758
|
+
const node = rule.convert(child, ctx);
|
|
759
|
+
if (node) return node;
|
|
692
760
|
}
|
|
693
|
-
if ("
|
|
761
|
+
if ("paragraph" in child) return this.resolveParagraph(child.paragraph);
|
|
694
762
|
return this.resolvePassthrough(child);
|
|
695
763
|
}
|
|
696
764
|
/** Wrap an opaque SectionChild in a passthrough atom (attrs.data = JSON). */
|
|
@@ -700,116 +768,15 @@ var DocxManager = class {
|
|
|
700
768
|
attrs: { data: JSON.stringify(child) }
|
|
701
769
|
};
|
|
702
770
|
}
|
|
703
|
-
/**
|
|
704
|
-
* Resolve a table of contents into an editable `tableOfContents` container:
|
|
705
|
-
* `attrs.options` carries the field switches, `content` is the entry
|
|
706
|
-
* paragraphs. Each entry's inner HYPERLINK field has content-less runs that
|
|
707
|
-
* office-open parses as `null`; resolving the entries through
|
|
708
|
-
* `resolveSectionChild` → `resolveParagraphChildren` drops those nulls, so the
|
|
709
|
-
* TOC no longer reaches the generate path as an opaque blob of nulls (the
|
|
710
|
-
* `stringifyRunInline(null).break` crash). When `entries` is absent/empty
|
|
711
|
-
* (a fresh, unrendered TOC), keep the node valid for `content: "block+"` with
|
|
712
|
-
* a placeholder empty paragraph.
|
|
713
|
-
*/
|
|
714
|
-
resolveToc(toc) {
|
|
715
|
-
const { entries, ...options } = toc;
|
|
716
|
-
const content = [];
|
|
717
|
-
for (const entry of entries ?? []) {
|
|
718
|
-
const node = this.resolveSectionChild(entry);
|
|
719
|
-
if (!node) continue;
|
|
720
|
-
if (Array.isArray(node)) content.push(...node);
|
|
721
|
-
else content.push(node);
|
|
722
|
-
}
|
|
723
|
-
if (content.length === 0) content.push({ type: "paragraph" });
|
|
724
|
-
const node = {
|
|
725
|
-
type: "tocField",
|
|
726
|
-
content
|
|
727
|
-
};
|
|
728
|
-
const cleanOptions = cleanAttrs(options);
|
|
729
|
-
if (Object.keys(cleanOptions).length > 0) node.attrs = { options: cleanOptions };
|
|
730
|
-
return node;
|
|
731
|
-
}
|
|
732
|
-
/**
|
|
733
|
-
* Resolve a details group-SDT: the summary-style paragraph becomes
|
|
734
|
-
* detailsSummary, the remaining blocks fold into detailsContent.
|
|
735
|
-
*/
|
|
736
|
-
resolveDetailsSdt(sdt) {
|
|
737
|
-
const content = [];
|
|
738
|
-
let summary = null;
|
|
739
|
-
for (const child of sdt.children ?? []) {
|
|
740
|
-
if ("paragraph" in child) {
|
|
741
|
-
const para = child.paragraph;
|
|
742
|
-
if (para.style === "DocenDetailsSummary") {
|
|
743
|
-
summary = this.resolveInlineContent(para);
|
|
744
|
-
continue;
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
const node = this.resolveSectionChild(child);
|
|
748
|
-
if (!node) continue;
|
|
749
|
-
if (Array.isArray(node)) content.push(...node);
|
|
750
|
-
else content.push(node);
|
|
751
|
-
}
|
|
752
|
-
const details = {
|
|
753
|
-
type: "details",
|
|
754
|
-
content: []
|
|
755
|
-
};
|
|
756
|
-
if (summary !== null) details.content.push({
|
|
757
|
-
type: "detailsSummary",
|
|
758
|
-
content: summary
|
|
759
|
-
});
|
|
760
|
-
if (content.length > 0) details.content.push({
|
|
761
|
-
type: "detailsContent",
|
|
762
|
-
content
|
|
763
|
-
});
|
|
764
|
-
return details;
|
|
765
|
-
}
|
|
766
771
|
resolveParagraph(opts) {
|
|
767
772
|
const resolved = typeof opts === "string" ? { text: opts } : opts;
|
|
768
773
|
if (resolved.thematicBreak) return { type: "horizontalRule" };
|
|
769
|
-
|
|
770
|
-
const
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
if (headingLevel && attrs.level == null) attrs.level = headingLevel;
|
|
774
|
-
const content = this.resolveInlineContent(resolved);
|
|
775
|
-
const cleanAttrsObj = cleanAttrs(attrs);
|
|
776
|
-
const node = { type: nodeType };
|
|
777
|
-
if (Object.keys(cleanAttrsObj).length > 0) node.attrs = cleanAttrsObj;
|
|
778
|
-
if (content.length > 0) node.content = content;
|
|
779
|
-
return node;
|
|
780
|
-
}
|
|
781
|
-
/** Heading level (1-9) for a paragraph, or undefined when it isn't a heading.
|
|
782
|
-
* DOCX marks a heading several ways, checked in priority order:
|
|
783
|
-
* 1. office-open lifts a HeadingLevel pStyle ("Heading1".."Title") into `heading`.
|
|
784
|
-
* 2. An explicit `outlineLevel` (0-8 → 1-9) — Word's outline/TOC key off this
|
|
785
|
-
* even without a heading pStyle; the Heading1-9 styles carry outlineLvl 0-8.
|
|
786
|
-
* 3. A pStyle that names a heading style: directly ("Heading7", which stays on
|
|
787
|
-
* `style` because office-open's HeadingLevel type caps at 6), by localized
|
|
788
|
-
* NAME ("heading 1"/"标题 1"), or via the `basedOn` chain (a custom style
|
|
789
|
-
* "MyTitle" basedOn="Heading1"). `heading` and `style` carry the same pStyle.
|
|
790
|
-
* `outlineLevel` is read loosely — office-open's public type omits the field
|
|
791
|
-
* even though it round-trips (w:outlineLvl) at runtime. */
|
|
792
|
-
detectHeadingLevel(resolved) {
|
|
793
|
-
if (resolved.heading) {
|
|
794
|
-
const lvl = HEADING_LEVEL_MAP[resolved.heading];
|
|
795
|
-
if (lvl) return lvl;
|
|
796
|
-
}
|
|
797
|
-
const outline = resolved.outlineLevel;
|
|
798
|
-
if (typeof outline === "number" && outline >= 0 && outline <= 8) return outline + 1;
|
|
799
|
-
const styleId = resolved.style;
|
|
800
|
-
if (!styleId || !this.resolveStyles) return void 0;
|
|
801
|
-
const byId = indexParagraphStyles(this.resolveStyles);
|
|
802
|
-
const visited = /* @__PURE__ */ new Set();
|
|
803
|
-
let curId = styleId;
|
|
804
|
-
while (curId && !visited.has(curId)) {
|
|
805
|
-
visited.add(curId);
|
|
806
|
-
if (HEADING_LEVEL_MAP[curId]) return HEADING_LEVEL_MAP[curId];
|
|
807
|
-
const style = byId.get(curId);
|
|
808
|
-
if (!style) break;
|
|
809
|
-
const lvl = headingLevelFromName(style.name);
|
|
810
|
-
if (lvl) return lvl;
|
|
811
|
-
curId = style.basedOn ?? void 0;
|
|
774
|
+
const ctx = this.resolveCtx;
|
|
775
|
+
for (const { rule } of this.paragraphRules) if (rule.match(resolved, ctx)) {
|
|
776
|
+
const node = rule.convert(resolved, ctx);
|
|
777
|
+
if (node) return node;
|
|
812
778
|
}
|
|
779
|
+
return buildTextBlock("paragraph", resolved, ctx);
|
|
813
780
|
}
|
|
814
781
|
/** reference → level-0 format/start, for classifying numbering paragraphs. */
|
|
815
782
|
buildNumberingLookup(docOpts) {
|
|
@@ -825,309 +792,43 @@ var DocxManager = class {
|
|
|
825
792
|
return lookup;
|
|
826
793
|
}
|
|
827
794
|
/**
|
|
828
|
-
* Walk
|
|
829
|
-
*
|
|
830
|
-
*
|
|
795
|
+
* Walk a SectionChild[] block stream — a section's body, a header/footer
|
|
796
|
+
* slot, or a table cell's children (a cell is just another block stream). An
|
|
797
|
+
* aggregator rule (list/blockquote) claims consecutive paragraphs sharing its
|
|
798
|
+
* predicate and rebuilds them as a composite; everything else resolves
|
|
799
|
+
* individually via resolveSectionChild. The manager owns only the generic
|
|
800
|
+
* group-by loop — the predicate (belongs) + builder (build) come from each
|
|
801
|
+
* rule, so a custom composite plugs in by declaring parseDocxAggregator.
|
|
831
802
|
*/
|
|
832
|
-
resolveSectionChildren(children
|
|
803
|
+
resolveSectionChildren(children) {
|
|
804
|
+
const ctx = this.resolveCtx;
|
|
833
805
|
const content = [];
|
|
834
806
|
let i = 0;
|
|
835
807
|
while (i < children.length) {
|
|
836
808
|
const child = children[i];
|
|
837
809
|
const firstPara = "paragraph" in child ? child.paragraph : null;
|
|
838
|
-
if (
|
|
839
|
-
|
|
810
|
+
if (firstPara && typeof firstPara !== "string") {
|
|
811
|
+
const rule = this.aggregatorRules.find((r) => r.rule.belongs(firstPara, ctx));
|
|
812
|
+
if (rule) {
|
|
840
813
|
const group = [];
|
|
841
814
|
while (i < children.length) {
|
|
842
815
|
const member = children[i];
|
|
843
816
|
if (!("paragraph" in member)) break;
|
|
844
817
|
const para = member.paragraph;
|
|
845
|
-
if (typeof para === "string" || !
|
|
818
|
+
if (typeof para === "string" || !rule.rule.belongs(para, ctx)) break;
|
|
846
819
|
group.push(para);
|
|
847
820
|
i++;
|
|
848
821
|
}
|
|
849
|
-
content.push(
|
|
822
|
+
content.push(...rule.rule.build(group, ctx));
|
|
850
823
|
continue;
|
|
851
824
|
}
|
|
852
|
-
const node = this.resolveSectionChild(child);
|
|
853
|
-
if (node) content.push(node);
|
|
854
|
-
i++;
|
|
855
|
-
continue;
|
|
856
|
-
}
|
|
857
|
-
const group = [];
|
|
858
|
-
while (i < children.length) {
|
|
859
|
-
const member = children[i];
|
|
860
|
-
if (!("paragraph" in member)) break;
|
|
861
|
-
const para = member.paragraph;
|
|
862
|
-
if (typeof para === "string") break;
|
|
863
|
-
const info = this.detectList(para, numberingLookup);
|
|
864
|
-
if (!info) break;
|
|
865
|
-
group.push({
|
|
866
|
-
para,
|
|
867
|
-
info
|
|
868
|
-
});
|
|
869
|
-
i++;
|
|
870
825
|
}
|
|
871
|
-
|
|
826
|
+
const node = this.resolveSectionChild(child);
|
|
827
|
+
if (node) content.push(node);
|
|
828
|
+
i++;
|
|
872
829
|
}
|
|
873
830
|
return content;
|
|
874
831
|
}
|
|
875
|
-
/** Classify a paragraph as a list item, or null if it isn't one. */
|
|
876
|
-
detectList(para, lookup) {
|
|
877
|
-
const p = para;
|
|
878
|
-
const numbering = p.numbering;
|
|
879
|
-
const bullet = p.bullet;
|
|
880
|
-
let kind;
|
|
881
|
-
let level;
|
|
882
|
-
let reference;
|
|
883
|
-
let start;
|
|
884
|
-
if (numbering) {
|
|
885
|
-
reference = numbering.reference;
|
|
886
|
-
level = numbering.level ?? 0;
|
|
887
|
-
const cfg = reference ? lookup.get(reference) : void 0;
|
|
888
|
-
if (cfg && cfg.format && cfg.format !== "bullet") {
|
|
889
|
-
kind = "ordered";
|
|
890
|
-
start = cfg.start;
|
|
891
|
-
} else kind = "bullet";
|
|
892
|
-
} else if (bullet) {
|
|
893
|
-
kind = "bullet";
|
|
894
|
-
level = bullet.level ?? 0;
|
|
895
|
-
} else return null;
|
|
896
|
-
const first = p.children?.[0];
|
|
897
|
-
return {
|
|
898
|
-
kind: isTaskCheckbox(first) ? "task" : kind,
|
|
899
|
-
level,
|
|
900
|
-
reference,
|
|
901
|
-
start,
|
|
902
|
-
checked: readCheckboxState(first)
|
|
903
|
-
};
|
|
904
|
-
}
|
|
905
|
-
/**
|
|
906
|
-
* Rebuild nested Tiptap lists from a flat run of list paragraphs. Stack-based:
|
|
907
|
-
* each frame is an active list at a given depth; the `key` (level:type:
|
|
908
|
-
* reference) decides whether a paragraph continues the top list, starts a
|
|
909
|
-
* nested list, or splits off a new sibling list.
|
|
910
|
-
*/
|
|
911
|
-
buildListTree(group) {
|
|
912
|
-
const topLevel = [];
|
|
913
|
-
const stack = [];
|
|
914
|
-
for (const { para, info } of group) {
|
|
915
|
-
const listType = info.kind === "ordered" ? "orderedList" : info.kind === "task" ? "taskList" : "bulletList";
|
|
916
|
-
const itemType = info.kind === "task" ? "taskItem" : "listItem";
|
|
917
|
-
const key = `${info.level}:${listType}:${info.reference ?? ""}`;
|
|
918
|
-
while (stack.length > 0) {
|
|
919
|
-
const top = stack[stack.length - 1];
|
|
920
|
-
if (top.level > info.level || top.level === info.level && top.key !== key) {
|
|
921
|
-
stack.pop();
|
|
922
|
-
continue;
|
|
923
|
-
}
|
|
924
|
-
break;
|
|
925
|
-
}
|
|
926
|
-
const newItem = {
|
|
927
|
-
type: itemType,
|
|
928
|
-
content: [this.resolveListItemParagraph(para, info)]
|
|
929
|
-
};
|
|
930
|
-
if (itemType === "taskItem") newItem.attrs = { checked: info.checked };
|
|
931
|
-
const top = stack[stack.length - 1];
|
|
932
|
-
if (top && top.level === info.level && top.key === key) {
|
|
933
|
-
top.listNode.content.push(newItem);
|
|
934
|
-
top.currentItem = newItem;
|
|
935
|
-
} else {
|
|
936
|
-
const newList = {
|
|
937
|
-
type: listType,
|
|
938
|
-
content: [newItem]
|
|
939
|
-
};
|
|
940
|
-
const listAttrs = {};
|
|
941
|
-
if (listType === "orderedList" && info.level === 0 && typeof info.start === "number" && info.start !== 1) listAttrs.start = info.start;
|
|
942
|
-
if (info.reference) listAttrs.numbering = info.reference;
|
|
943
|
-
if (Object.keys(listAttrs).length > 0) newList.attrs = listAttrs;
|
|
944
|
-
if (top) top.currentItem.content.push(newList);
|
|
945
|
-
else topLevel.push(newList);
|
|
946
|
-
stack.push({
|
|
947
|
-
level: info.level,
|
|
948
|
-
key,
|
|
949
|
-
listNode: newList,
|
|
950
|
-
currentItem: newItem
|
|
951
|
-
});
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
return topLevel;
|
|
955
|
-
}
|
|
956
|
-
/** Classify a paragraph as a blockquote member by its signature. */
|
|
957
|
-
detectBlockquote(para) {
|
|
958
|
-
const p = para;
|
|
959
|
-
const indent = p.indent;
|
|
960
|
-
const border = p.border;
|
|
961
|
-
if (!indent || indent.left !== 720) return false;
|
|
962
|
-
const bl = border?.left;
|
|
963
|
-
if (!bl) return false;
|
|
964
|
-
const sig = BLOCKQUOTE_BORDER;
|
|
965
|
-
return bl.style === sig.style && bl.size === sig.size && bl.space === sig.space && bl.color === sig.color;
|
|
966
|
-
}
|
|
967
|
-
/**
|
|
968
|
-
* Rebuild a blockquote node from a run of signature-carrying paragraphs,
|
|
969
|
-
* stripping the indent/border signature so child paragraphs render clean.
|
|
970
|
-
*/
|
|
971
|
-
buildBlockquote(group) {
|
|
972
|
-
const content = [];
|
|
973
|
-
for (const para of group) {
|
|
974
|
-
const node = this.resolveParagraph(para);
|
|
975
|
-
const attrs = node.attrs;
|
|
976
|
-
if (attrs) {
|
|
977
|
-
if (attrs.indent) {
|
|
978
|
-
const indent = { ...attrs.indent };
|
|
979
|
-
delete indent.left;
|
|
980
|
-
attrs.indent = Object.keys(indent).length > 0 ? indent : void 0;
|
|
981
|
-
}
|
|
982
|
-
if (attrs.border) {
|
|
983
|
-
const border = { ...attrs.border };
|
|
984
|
-
delete border.left;
|
|
985
|
-
attrs.border = Object.keys(border).length > 0 ? border : void 0;
|
|
986
|
-
}
|
|
987
|
-
const cleaned = cleanAttrs(attrs);
|
|
988
|
-
if (Object.keys(cleaned).length > 0) node.attrs = cleaned;
|
|
989
|
-
else delete node.attrs;
|
|
990
|
-
}
|
|
991
|
-
content.push(node);
|
|
992
|
-
}
|
|
993
|
-
return {
|
|
994
|
-
type: "blockquote",
|
|
995
|
-
content
|
|
996
|
-
};
|
|
997
|
-
}
|
|
998
|
-
/**
|
|
999
|
-
* Resolve a list-item paragraph to a Tiptap paragraph/heading node, stripping
|
|
1000
|
-
* the list marker (bullet/numbering) and the leading task checkbox — those
|
|
1001
|
-
* are expressed at the list/item level, not inside the paragraph.
|
|
1002
|
-
*/
|
|
1003
|
-
resolveListItemParagraph(para, info) {
|
|
1004
|
-
const resolved = typeof para === "string" ? { text: para } : para;
|
|
1005
|
-
const headingLevel = this.detectHeadingLevel(resolved);
|
|
1006
|
-
const nodeType = headingLevel ? "heading" : "paragraph";
|
|
1007
|
-
const attrs = headingLevel ? parseDocx(resolved) : parseDocx$2(resolved);
|
|
1008
|
-
if (headingLevel && attrs.level == null) attrs.level = headingLevel;
|
|
1009
|
-
const stripped = info.kind === "task" ? this.stripTaskCheckbox(resolved) : resolved;
|
|
1010
|
-
const content = this.resolveInlineContent(stripped);
|
|
1011
|
-
const node = { type: nodeType };
|
|
1012
|
-
const cleanAttrsObj = cleanAttrs(attrs);
|
|
1013
|
-
if (Object.keys(cleanAttrsObj).length > 0) node.attrs = cleanAttrsObj;
|
|
1014
|
-
if (content.length > 0) node.content = content;
|
|
1015
|
-
return node;
|
|
1016
|
-
}
|
|
1017
|
-
/** Return a copy of `para` with its leading docen-task checkbox SDT removed. */
|
|
1018
|
-
stripTaskCheckbox(para) {
|
|
1019
|
-
const children = para.children;
|
|
1020
|
-
if (Array.isArray(children) && children.length > 0 && isTaskCheckbox(children[0])) return {
|
|
1021
|
-
...para,
|
|
1022
|
-
children: children.slice(1)
|
|
1023
|
-
};
|
|
1024
|
-
return para;
|
|
1025
|
-
}
|
|
1026
|
-
resolveCodeBlock(opts) {
|
|
1027
|
-
const children = opts.children;
|
|
1028
|
-
const content = [];
|
|
1029
|
-
if (children) {
|
|
1030
|
-
for (const child of children) if (typeof child === "string") {
|
|
1031
|
-
if (child) content.push({
|
|
1032
|
-
type: "text",
|
|
1033
|
-
text: child
|
|
1034
|
-
});
|
|
1035
|
-
} else if (typeof child === "object" && child !== null) {
|
|
1036
|
-
if ("break" in child) {
|
|
1037
|
-
const prev = content[content.length - 1];
|
|
1038
|
-
if (prev && prev.type === "text") prev.text = (prev.text ?? "") + "\n";
|
|
1039
|
-
else content.push({
|
|
1040
|
-
type: "text",
|
|
1041
|
-
text: "\n"
|
|
1042
|
-
});
|
|
1043
|
-
} else if ("text" in child) {
|
|
1044
|
-
const marks = this.resolveMarks(child);
|
|
1045
|
-
const textNode = {
|
|
1046
|
-
type: "text",
|
|
1047
|
-
text: child.text
|
|
1048
|
-
};
|
|
1049
|
-
if (marks) textNode.marks = marks;
|
|
1050
|
-
content.push(textNode);
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
} else if (opts.text) content.push({
|
|
1054
|
-
type: "text",
|
|
1055
|
-
text: opts.text
|
|
1056
|
-
});
|
|
1057
|
-
const node = { type: "codeBlock" };
|
|
1058
|
-
if (content.length > 0) node.content = content;
|
|
1059
|
-
return node;
|
|
1060
|
-
}
|
|
1061
|
-
resolveTable(tableOpts) {
|
|
1062
|
-
const attrs = parseDocx$4(tableOpts);
|
|
1063
|
-
const rows = tableOpts.rows ?? [];
|
|
1064
|
-
const content = [];
|
|
1065
|
-
const tableCellMargins = tableOpts.cellMargin ?? tableOpts.margins ?? null;
|
|
1066
|
-
let activeSpans = /* @__PURE__ */ new Map();
|
|
1067
|
-
for (const row of rows) {
|
|
1068
|
-
const rowAttrs = parseDocx$7(row);
|
|
1069
|
-
const cells = row.cells ?? [];
|
|
1070
|
-
const cellNodes = [];
|
|
1071
|
-
const nextActiveSpans = /* @__PURE__ */ new Map();
|
|
1072
|
-
let colIdx = 0;
|
|
1073
|
-
for (const cell of cells) {
|
|
1074
|
-
const cellColspan = cell.columnSpan ?? 1;
|
|
1075
|
-
const vMerge = cell.verticalMerge;
|
|
1076
|
-
if (vMerge === "continue") {
|
|
1077
|
-
const owner = activeSpans.get(colIdx);
|
|
1078
|
-
if (owner) {
|
|
1079
|
-
const ownerAttrs = owner.attrs ??= {};
|
|
1080
|
-
ownerAttrs.rowspan = (ownerAttrs.rowspan ?? 1) + 1;
|
|
1081
|
-
for (let c = colIdx; c < colIdx + cellColspan; c++) nextActiveSpans.set(c, owner);
|
|
1082
|
-
}
|
|
1083
|
-
colIdx += cellColspan;
|
|
1084
|
-
continue;
|
|
1085
|
-
}
|
|
1086
|
-
const isHeader = row.tableHeader;
|
|
1087
|
-
const cellAttrs = isHeader ? parseDocx$6(cell) : parseDocx$5(cell);
|
|
1088
|
-
delete cellAttrs.verticalMerge;
|
|
1089
|
-
if (!cellAttrs.margins && tableCellMargins) cellAttrs.margins = tableCellMargins;
|
|
1090
|
-
const cellChildren = cell.children ?? [];
|
|
1091
|
-
const cellContent = [];
|
|
1092
|
-
for (const cc of cellChildren) {
|
|
1093
|
-
const resolved = this.resolveSectionChild(cc);
|
|
1094
|
-
if (resolved) cellContent.push(resolved);
|
|
1095
|
-
}
|
|
1096
|
-
const cellNode = { type: isHeader ? "tableHeader" : "tableCell" };
|
|
1097
|
-
if (Object.keys(cellAttrs).length > 0) cellNode.attrs = cleanAttrs(cellAttrs);
|
|
1098
|
-
if (cellContent.length > 0) cellNode.content = cellContent;
|
|
1099
|
-
else cellNode.content = [{ type: "paragraph" }];
|
|
1100
|
-
if (vMerge === "restart") for (let c = colIdx; c < colIdx + cellColspan; c++) nextActiveSpans.set(c, cellNode);
|
|
1101
|
-
cellNodes.push(cellNode);
|
|
1102
|
-
colIdx += cellColspan;
|
|
1103
|
-
}
|
|
1104
|
-
const gridAfter = row.gridAfter ?? 0;
|
|
1105
|
-
if (gridAfter > 0) {
|
|
1106
|
-
const trailingType = row.tableHeader ? "tableHeader" : "tableCell";
|
|
1107
|
-
const nilBorders = {
|
|
1108
|
-
top: { style: "nil" },
|
|
1109
|
-
right: { style: "nil" },
|
|
1110
|
-
bottom: { style: "nil" },
|
|
1111
|
-
left: { style: "nil" }
|
|
1112
|
-
};
|
|
1113
|
-
for (let c = 0; c < gridAfter; c++) cellNodes.push({
|
|
1114
|
-
type: trailingType,
|
|
1115
|
-
attrs: { borders: nilBorders },
|
|
1116
|
-
content: [{ type: "paragraph" }]
|
|
1117
|
-
});
|
|
1118
|
-
colIdx += gridAfter;
|
|
1119
|
-
}
|
|
1120
|
-
activeSpans = nextActiveSpans;
|
|
1121
|
-
const rowNode = { type: "tableRow" };
|
|
1122
|
-
if (Object.keys(rowAttrs).length > 0) rowNode.attrs = cleanAttrs(rowAttrs);
|
|
1123
|
-
if (cellNodes.length > 0) rowNode.content = cellNodes;
|
|
1124
|
-
content.push(rowNode);
|
|
1125
|
-
}
|
|
1126
|
-
const node = { type: "table" };
|
|
1127
|
-
if (Object.keys(attrs).length > 0) node.attrs = cleanAttrs(attrs);
|
|
1128
|
-
if (content.length > 0) node.content = content;
|
|
1129
|
-
return node;
|
|
1130
|
-
}
|
|
1131
832
|
/**
|
|
1132
833
|
* Resolve a paragraph's inline content. @office-open collapses a plain-text
|
|
1133
834
|
* paragraph (a single run with no properties) to a bare string or a `{ text }`
|
|
@@ -1166,70 +867,17 @@ var DocxManager = class {
|
|
|
1166
867
|
return nodes;
|
|
1167
868
|
}
|
|
1168
869
|
resolveParagraphChild(child) {
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
type: "wpgGroup",
|
|
1174
|
-
attrs: { wpgGroup: child.wpgGroup }
|
|
1175
|
-
};
|
|
1176
|
-
if ("wpsShape" in child) {
|
|
1177
|
-
const ws = child.wpsShape;
|
|
1178
|
-
const content = [];
|
|
1179
|
-
if (ws?.children) for (const para of ws.children) {
|
|
1180
|
-
if (typeof para !== "object" || para === null) {
|
|
1181
|
-
const node = this.resolveParagraph(para);
|
|
1182
|
-
if (node) content.push(node);
|
|
1183
|
-
continue;
|
|
1184
|
-
}
|
|
1185
|
-
const defRPr = para.run ?? {};
|
|
1186
|
-
const children = Array.isArray(para.children) ? para.children.map((c) => typeof c !== "object" || c === null ? {
|
|
1187
|
-
...defRPr,
|
|
1188
|
-
text: c
|
|
1189
|
-
} : {
|
|
1190
|
-
...defRPr,
|
|
1191
|
-
...c
|
|
1192
|
-
}) : void 0;
|
|
1193
|
-
const node = this.resolveParagraph({
|
|
1194
|
-
...para,
|
|
1195
|
-
run: void 0,
|
|
1196
|
-
...children ? { children } : {}
|
|
1197
|
-
});
|
|
1198
|
-
if (node) content.push(node);
|
|
1199
|
-
}
|
|
1200
|
-
if (content.length === 0) content.push({ type: "paragraph" });
|
|
1201
|
-
const { children: _omit, ...geometry } = ws ?? {};
|
|
1202
|
-
const node = {
|
|
1203
|
-
type: "wpsShape",
|
|
1204
|
-
content
|
|
1205
|
-
};
|
|
1206
|
-
const cleanGeometry = cleanAttrs(geometry);
|
|
1207
|
-
if (Object.keys(cleanGeometry).length > 0) node.attrs = { wpsShape: cleanGeometry };
|
|
1208
|
-
return node;
|
|
870
|
+
const ctx = this.resolveCtx;
|
|
871
|
+
for (const { rule } of this.inlineRules) if (rule.match(child, ctx)) {
|
|
872
|
+
const node = rule.convert(child, ctx);
|
|
873
|
+
if (node) return node;
|
|
1209
874
|
}
|
|
1210
|
-
if ("
|
|
1211
|
-
if ("hyperlink" in child) return this.resolveHyperlink(child.hyperlink);
|
|
1212
|
-
if ("pageBreak" in child) return { type: "pageBreak" };
|
|
1213
|
-
if ("columnBreak" in child) return { type: "columnBreak" };
|
|
875
|
+
if ("text" in child || "children" in child || "break" in child) return this.resolveRun(child);
|
|
1214
876
|
return {
|
|
1215
877
|
type: "inlinePassthrough",
|
|
1216
878
|
attrs: { data: JSON.stringify(child) }
|
|
1217
879
|
};
|
|
1218
880
|
}
|
|
1219
|
-
/** Resolve an inline SDT (mention carrier; other inline SDTs unsupported). */
|
|
1220
|
-
resolveInlineSdt(child) {
|
|
1221
|
-
if (isMention(child)) {
|
|
1222
|
-
const { id, label } = readMention(child);
|
|
1223
|
-
return {
|
|
1224
|
-
type: "mention",
|
|
1225
|
-
attrs: {
|
|
1226
|
-
id,
|
|
1227
|
-
label
|
|
1228
|
-
}
|
|
1229
|
-
};
|
|
1230
|
-
}
|
|
1231
|
-
return null;
|
|
1232
|
-
}
|
|
1233
881
|
resolveRun(opts) {
|
|
1234
882
|
if (opts.break && opts.text === void 0 && !opts.children) return { type: "hardBreak" };
|
|
1235
883
|
const text = opts.text;
|
|
@@ -1251,18 +899,21 @@ var DocxManager = class {
|
|
|
1251
899
|
};
|
|
1252
900
|
for (const c of opts.children) if (typeof c === "string") parts.push(c);
|
|
1253
901
|
else if (c && typeof c === "object") {
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
902
|
+
const ctx = this.resolveCtx;
|
|
903
|
+
let handled = false;
|
|
904
|
+
for (const { rule } of this.inlineRules) if (rule.match(c, ctx)) {
|
|
905
|
+
const node = rule.convert(c, ctx);
|
|
906
|
+
if (node) {
|
|
907
|
+
flushText();
|
|
908
|
+
nodes.push(...Array.isArray(node) ? node : [node]);
|
|
909
|
+
handled = true;
|
|
910
|
+
break;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
if (handled) continue;
|
|
914
|
+
if ("break" in c) {
|
|
1261
915
|
flushText();
|
|
1262
916
|
nodes.push({ type: "hardBreak" });
|
|
1263
|
-
} else if ("tab" in c) {
|
|
1264
|
-
flushText();
|
|
1265
|
-
nodes.push({ type: "tab" });
|
|
1266
917
|
}
|
|
1267
918
|
}
|
|
1268
919
|
flushText();
|
|
@@ -1285,98 +936,32 @@ var DocxManager = class {
|
|
|
1285
936
|
}
|
|
1286
937
|
resolveMarks(opts) {
|
|
1287
938
|
const marks = [];
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
attrs: { doubleStrike: strikeAttrs.doubleStrike ?? null }
|
|
1296
|
-
});
|
|
1297
|
-
}
|
|
1298
|
-
if (opts.subScript) marks.push({ type: "subscript" });
|
|
1299
|
-
if (opts.superScript) marks.push({ type: "superscript" });
|
|
1300
|
-
if (opts.highlight) marks.push({
|
|
1301
|
-
type: "highlight",
|
|
1302
|
-
attrs: { color: opts.highlight }
|
|
1303
|
-
});
|
|
1304
|
-
if (opts.style === "CodeChar") marks.push({ type: "code" });
|
|
1305
|
-
const textStyleAttrs = parseDocx$8(opts);
|
|
1306
|
-
if (opts.style === "CodeChar") {
|
|
1307
|
-
delete textStyleAttrs.font;
|
|
1308
|
-
delete textStyleAttrs.styleId;
|
|
939
|
+
for (const { name, parse } of this.markParse) {
|
|
940
|
+
const attrs = parse(opts);
|
|
941
|
+
if (attrs === null) continue;
|
|
942
|
+
marks.push(Object.keys(attrs).length ? {
|
|
943
|
+
type: name,
|
|
944
|
+
attrs
|
|
945
|
+
} : { type: name });
|
|
1309
946
|
}
|
|
1310
|
-
if (Object.keys(textStyleAttrs).length > 0) marks.push({
|
|
1311
|
-
type: "textStyle",
|
|
1312
|
-
attrs: textStyleAttrs
|
|
1313
|
-
});
|
|
1314
947
|
return marks.length > 0 ? marks : void 0;
|
|
1315
948
|
}
|
|
1316
|
-
resolveImage(imageOpts) {
|
|
1317
|
-
const attrs = parseDocx$1(imageOpts);
|
|
1318
|
-
const data = imageOpts.data;
|
|
1319
|
-
const type = imageOpts.type;
|
|
1320
|
-
if (data && type) attrs.src = `data:image/${type};base64,${encodeBase64(data instanceof ArrayBuffer ? new Uint8Array(data) : data)}`;
|
|
1321
|
-
return {
|
|
1322
|
-
type: "image",
|
|
1323
|
-
attrs
|
|
1324
|
-
};
|
|
1325
|
-
}
|
|
1326
|
-
resolveHyperlink(hyperlink) {
|
|
1327
|
-
const href = hyperlink.link ?? (hyperlink.anchor ? `#${hyperlink.anchor}` : "");
|
|
1328
|
-
if (!href) return null;
|
|
1329
|
-
const content = this.resolveParagraphChildren((hyperlink.children ?? []).map((c) => c));
|
|
1330
|
-
if (content.length > 0) {
|
|
1331
|
-
const merged = mergeTextNodes(content);
|
|
1332
|
-
for (const node of merged) if (node.type === "text") node.marks = [...node.marks ?? [], {
|
|
1333
|
-
type: "link",
|
|
1334
|
-
attrs: {
|
|
1335
|
-
href,
|
|
1336
|
-
target: href.startsWith("#") ? null : "_blank",
|
|
1337
|
-
rel: "noopener noreferrer nofollow",
|
|
1338
|
-
class: null,
|
|
1339
|
-
title: hyperlink.tooltip ?? null
|
|
1340
|
-
}
|
|
1341
|
-
}];
|
|
1342
|
-
return merged;
|
|
1343
|
-
}
|
|
1344
|
-
return null;
|
|
1345
|
-
}
|
|
1346
949
|
};
|
|
1347
|
-
const
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
Heading6: 6,
|
|
1354
|
-
Heading7: 7,
|
|
1355
|
-
Heading8: 8,
|
|
1356
|
-
Heading9: 9,
|
|
1357
|
-
Title: 1
|
|
1358
|
-
};
|
|
1359
|
-
/** Heading level (1-9) from a localized style NAME: "heading 1"/"标题 1" → 1,
|
|
1360
|
-
* "title" → 1. office-open's built-in names are English ("heading 1"), but
|
|
1361
|
-
* zh-CN Word labels the same styles "标题 1"; both map to the same level. */
|
|
1362
|
-
function headingLevelFromName(name) {
|
|
1363
|
-
if (!name) return void 0;
|
|
1364
|
-
const m = /^heading\s+(\d)$/i.exec(name) ?? /^标题\s*(\d)$/.exec(name);
|
|
1365
|
-
if (m) {
|
|
1366
|
-
const lvl = Number(m[1]);
|
|
1367
|
-
if (lvl >= 1 && lvl <= 9) return lvl;
|
|
1368
|
-
}
|
|
1369
|
-
return /^title$/i.test(name) ? 1 : void 0;
|
|
950
|
+
const defaultManager = new DocxManager(docxExtensions);
|
|
951
|
+
/** Resolve a DocxManager for a conversion call: the shared default singleton
|
|
952
|
+
* when no extensions are given, or a fresh instance bound to custom extensions
|
|
953
|
+
* so user-supplied marks/nodes plug into compile/resolve without a fork. */
|
|
954
|
+
function getDocxManager(extensions) {
|
|
955
|
+
return extensions ? new DocxManager(extensions) : defaultManager;
|
|
1370
956
|
}
|
|
1371
|
-
const defaultManager = new DocxManager();
|
|
1372
957
|
/**
|
|
1373
958
|
* Parse a DOCX file into Tiptap JSON (runtime model).
|
|
1374
959
|
*
|
|
1375
960
|
* Combines @office-open/docx's `parseDocument` (DOCX binary → DocumentOptions)
|
|
1376
961
|
* with `DocxManager.resolve` (DocumentOptions → Tiptap JSON).
|
|
1377
962
|
*/
|
|
1378
|
-
function parseDOCX(data) {
|
|
1379
|
-
return
|
|
963
|
+
function parseDOCX(data, extensions) {
|
|
964
|
+
return getDocxManager(extensions).resolve(parseDocument(data));
|
|
1380
965
|
}
|
|
1381
966
|
/**
|
|
1382
967
|
* Merge {@link DocxGenerateOptions.document} into the `DocumentOptions`
|
|
@@ -1407,9 +992,9 @@ function applyDocumentOptions(base, document) {
|
|
|
1407
992
|
* in place (http image URLs become embedded data URLs).
|
|
1408
993
|
*/
|
|
1409
994
|
async function generateDOCX(json, options) {
|
|
1410
|
-
const { prepare = true, packer, document } = options ?? {};
|
|
995
|
+
const { prepare = true, packer, document, extensions } = options ?? {};
|
|
1411
996
|
if (prepare !== false) await prepareDocument(json, prepare === true ? void 0 : prepare);
|
|
1412
|
-
return generateDocument(applyDocumentOptions(compileDocument(json), document), packer);
|
|
997
|
+
return generateDocument(applyDocumentOptions(compileDocument(json, extensions), document), packer);
|
|
1413
998
|
}
|
|
1414
999
|
/**
|
|
1415
1000
|
* Generate a DOCX file synchronously — fastest throughput, blocks the event loop.
|
|
@@ -1419,8 +1004,8 @@ async function generateDOCX(json, options) {
|
|
|
1419
1004
|
* when http images need embedding. `options.document` is still applied.
|
|
1420
1005
|
*/
|
|
1421
1006
|
function generateDOCXSync(json, options) {
|
|
1422
|
-
const { packer, document } = options ?? {};
|
|
1423
|
-
return generateDocumentSync(applyDocumentOptions(compileDocument(json), document), packer);
|
|
1007
|
+
const { packer, document, extensions } = options ?? {};
|
|
1008
|
+
return generateDocumentSync(applyDocumentOptions(compileDocument(json, extensions), document), packer);
|
|
1424
1009
|
}
|
|
1425
1010
|
/**
|
|
1426
1011
|
* Generate a DOCX file as a `ReadableStream<Uint8Array>` — for large documents
|
|
@@ -1430,21 +1015,21 @@ function generateDOCXSync(json, options) {
|
|
|
1430
1015
|
* `DocxManager.compile` → `generateDocumentStream`. Async due to preparation.
|
|
1431
1016
|
*/
|
|
1432
1017
|
async function generateDOCXStream(json, options) {
|
|
1433
|
-
const { prepare = true, packer, document } = options ?? {};
|
|
1018
|
+
const { prepare = true, packer, document, extensions } = options ?? {};
|
|
1434
1019
|
if (prepare !== false) await prepareDocument(json, prepare === true ? void 0 : prepare);
|
|
1435
|
-
return generateDocumentStream(applyDocumentOptions(compileDocument(json), document), packer);
|
|
1020
|
+
return generateDocumentStream(applyDocumentOptions(compileDocument(json, extensions), document), packer);
|
|
1436
1021
|
}
|
|
1437
1022
|
/**
|
|
1438
1023
|
* Convert DocumentOptions (persistence model) to Tiptap JSON (runtime model).
|
|
1439
1024
|
*/
|
|
1440
|
-
function resolveDocument(docOpts) {
|
|
1441
|
-
return
|
|
1025
|
+
function resolveDocument(docOpts, extensions) {
|
|
1026
|
+
return getDocxManager(extensions).resolve(docOpts);
|
|
1442
1027
|
}
|
|
1443
1028
|
/**
|
|
1444
1029
|
* Convert Tiptap JSON (runtime model) to DocumentOptions (persistence model).
|
|
1445
1030
|
*/
|
|
1446
|
-
function compileDocument(json) {
|
|
1447
|
-
return
|
|
1031
|
+
function compileDocument(json, extensions) {
|
|
1032
|
+
return getDocxManager(extensions).compile(json);
|
|
1448
1033
|
}
|
|
1449
1034
|
//#endregion
|
|
1450
1035
|
export { DocxManager, compileDocument, generateDOCX, generateDOCXStream, generateDOCXSync, parseDOCX, resolveDocument };
|