@docen/docx 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.
- package/LICENSE +21 -0
- package/README.md +156 -0
- package/dist/converters/docx.d.mts +110 -0
- package/dist/converters/docx.mjs +624 -0
- package/dist/converters/html.d.mts +14 -0
- package/dist/converters/html.mjs +18 -0
- package/dist/converters/markdown.d.mts +13 -0
- package/dist/converters/markdown.mjs +18 -0
- package/dist/converters/patch.d.mts +53 -0
- package/dist/converters/patch.mjs +37 -0
- package/dist/converters/prepare.d.mts +51 -0
- package/dist/converters/prepare.mjs +76 -0
- package/dist/core-CFIQVRfx.mjs +88 -0
- package/dist/core-omBKMRtl.d.mts +34 -0
- package/dist/core.d.mts +2 -0
- package/dist/core.mjs +2 -0
- package/dist/editor.d.mts +21 -0
- package/dist/editor.mjs +20 -0
- package/dist/extensions/extensions.d.mts +12 -0
- package/dist/extensions/extensions.mjs +12 -0
- package/dist/extensions/heading.d.mts +2 -0
- package/dist/extensions/heading.mjs +145 -0
- package/dist/extensions/image.d.mts +2 -0
- package/dist/extensions/image.mjs +201 -0
- package/dist/extensions/index.d.mts +2 -0
- package/dist/extensions/index.mjs +2 -0
- package/dist/extensions/paragraph.d.mts +2 -0
- package/dist/extensions/paragraph.mjs +116 -0
- package/dist/extensions/strike.d.mts +2 -0
- package/dist/extensions/strike.mjs +50 -0
- package/dist/extensions/table-cell.d.mts +2 -0
- package/dist/extensions/table-cell.mjs +112 -0
- package/dist/extensions/table-header.d.mts +2 -0
- package/dist/extensions/table-header.mjs +112 -0
- package/dist/extensions/table-row.d.mts +2 -0
- package/dist/extensions/table-row.mjs +84 -0
- package/dist/extensions/table.d.mts +2 -0
- package/dist/extensions/table.mjs +134 -0
- package/dist/extensions/text-style.d.mts +2 -0
- package/dist/extensions/text-style.mjs +134 -0
- package/dist/extensions/tiptap.d.mts +2 -0
- package/dist/extensions/tiptap.mjs +33 -0
- package/dist/extensions/types.d.mts +30 -0
- package/dist/extensions/utils.d.mts +49 -0
- package/dist/extensions/utils.mjs +289 -0
- package/dist/heading-BvqBD2zX.d.mts +8 -0
- package/dist/image-Ge1y6uam.d.mts +47 -0
- package/dist/index.d.mts +213 -0
- package/dist/index.mjs +8 -0
- package/dist/paragraph-fhEXtAN2.d.mts +16 -0
- package/dist/strike-BgWGvjKr.d.mts +33 -0
- package/dist/table-BFkfeRp9.d.mts +9 -0
- package/dist/table-cell-D_FV4D2h.d.mts +8 -0
- package/dist/table-header-KGQ2aEkP.d.mts +8 -0
- package/dist/table-row-kgzYkZlW.d.mts +9 -0
- package/dist/text-style-BHdtXkMb.d.mts +8 -0
- package/dist/tiptap-TErPjuNJ.d.mts +33 -0
- package/package.json +106 -0
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
import { parseDocx, renderDocx } from "../extensions/heading.mjs";
|
|
2
|
+
import { parseDocx as parseDocx$1, renderDocx as renderDocx$1 } from "../extensions/image.mjs";
|
|
3
|
+
import { parseDocx as parseDocx$2, renderDocx as renderDocx$2 } from "../extensions/paragraph.mjs";
|
|
4
|
+
import { parseDocx as parseDocx$3, renderDocx as renderDocx$3 } from "../extensions/strike.mjs";
|
|
5
|
+
import { parseDocx as parseDocx$4, renderDocx as renderDocx$4 } from "../extensions/table.mjs";
|
|
6
|
+
import { parseDocx as parseDocx$5, renderDocx as renderDocx$5 } from "../extensions/table-cell.mjs";
|
|
7
|
+
import { parseDocx as parseDocx$6, renderDocx as renderDocx$6 } from "../extensions/table-header.mjs";
|
|
8
|
+
import { parseDocx as parseDocx$7, renderDocx as renderDocx$7 } from "../extensions/table-row.mjs";
|
|
9
|
+
import { parseDocx as parseDocx$8, renderDocx as renderDocx$8 } from "../extensions/text-style.mjs";
|
|
10
|
+
import { prepareDocument } from "./prepare.mjs";
|
|
11
|
+
import { generateDocument, generateDocumentStream, generateDocumentSync, parseDocument } from "@office-open/docx";
|
|
12
|
+
//#region src/converters/docx.ts
|
|
13
|
+
/** Remove keys with null/undefined values */
|
|
14
|
+
function cleanAttrs(attrs) {
|
|
15
|
+
const result = {};
|
|
16
|
+
for (const [key, value] of Object.entries(attrs)) if (value !== null && value !== void 0) result[key] = value;
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
/** Merge consecutive text nodes with same marks */
|
|
20
|
+
function mergeTextNodes(nodes) {
|
|
21
|
+
const result = [];
|
|
22
|
+
for (const node of nodes) {
|
|
23
|
+
if (node.type === "text" && result.length > 0 && result[result.length - 1].type === "text") {
|
|
24
|
+
const prev = result[result.length - 1];
|
|
25
|
+
if (JSON.stringify(prev.marks) === JSON.stringify(node.marks)) {
|
|
26
|
+
prev.text = (prev.text ?? "") + (node.text ?? "");
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
result.push({ ...node });
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Manages DOCX serialization (Tiptap JSON ↔ DocumentOptions).
|
|
36
|
+
*
|
|
37
|
+
* Each extension provides renderDocx/parseDocx for its own attrs mapping.
|
|
38
|
+
* DocxManager handles tree walking, child assembly, and dispatching.
|
|
39
|
+
*/
|
|
40
|
+
var DocxManager = class {
|
|
41
|
+
compile(json) {
|
|
42
|
+
const children = [];
|
|
43
|
+
if (json.content) for (const node of json.content) {
|
|
44
|
+
const child = this.compileSectionChild(node);
|
|
45
|
+
if (!child) continue;
|
|
46
|
+
if (Array.isArray(child)) children.push(...child);
|
|
47
|
+
else children.push(child);
|
|
48
|
+
}
|
|
49
|
+
return { sections: [{ children }] };
|
|
50
|
+
}
|
|
51
|
+
resolve(docOpts) {
|
|
52
|
+
const sections = docOpts.sections ?? [];
|
|
53
|
+
if (sections.length === 0) return {
|
|
54
|
+
type: "doc",
|
|
55
|
+
content: [{ type: "paragraph" }]
|
|
56
|
+
};
|
|
57
|
+
const children = sections[0].children ?? [];
|
|
58
|
+
const content = [];
|
|
59
|
+
for (const child of children) {
|
|
60
|
+
const node = this.resolveSectionChild(child);
|
|
61
|
+
if (node) content.push(node);
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
type: "doc",
|
|
65
|
+
content: content.length > 0 ? content : [{ type: "paragraph" }]
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
compileSectionChild(node) {
|
|
69
|
+
switch (node.type) {
|
|
70
|
+
case "paragraph": return { paragraph: this.compileParagraphNode(node) };
|
|
71
|
+
case "heading": return { paragraph: this.compileHeadingNode(node) };
|
|
72
|
+
case "blockquote":
|
|
73
|
+
for (const child of node.content ?? []) if (child.type === "paragraph") return { paragraph: this.compileParagraphNode(child) };
|
|
74
|
+
return null;
|
|
75
|
+
case "codeBlock": return { paragraph: this.compileCodeBlock(node) };
|
|
76
|
+
case "horizontalRule": return { paragraph: { thematicBreak: true } };
|
|
77
|
+
case "table": return { table: this.compileTableNode(node) };
|
|
78
|
+
case "image": {
|
|
79
|
+
const imageRun = renderDocx$1(node);
|
|
80
|
+
if (!imageRun) return null;
|
|
81
|
+
return { paragraph: { children: [imageRun] } };
|
|
82
|
+
}
|
|
83
|
+
case "bulletList":
|
|
84
|
+
case "orderedList":
|
|
85
|
+
case "taskList": return this.compileListFromNode(node);
|
|
86
|
+
default: return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
compileParagraphNode(node) {
|
|
90
|
+
const opts = renderDocx$2(node);
|
|
91
|
+
const childList = this.compileInlineContent(node.content);
|
|
92
|
+
if (childList.length > 0) opts.children = childList;
|
|
93
|
+
return this.simplifyParagraph(opts);
|
|
94
|
+
}
|
|
95
|
+
compileHeadingNode(node) {
|
|
96
|
+
const opts = renderDocx(node);
|
|
97
|
+
const childList = this.compileInlineContent(node.content);
|
|
98
|
+
if (childList.length > 0) opts.children = childList;
|
|
99
|
+
return this.simplifyParagraph(opts);
|
|
100
|
+
}
|
|
101
|
+
compileCodeBlock(node) {
|
|
102
|
+
const text = (node.content ?? []).map((c) => c.text ?? "").join("");
|
|
103
|
+
return {
|
|
104
|
+
style: "Code",
|
|
105
|
+
...node.attrs?.language ? { run: { font: "Consolas" } } : {},
|
|
106
|
+
text
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/** Simple text optimization: merge plain runs into text field */
|
|
110
|
+
simplifyParagraph(opts) {
|
|
111
|
+
const children = opts.children;
|
|
112
|
+
if (!children || children.length === 0) return opts;
|
|
113
|
+
if (children.every((c) => typeof c === "object" && c !== null && "text" in c && Object.keys(c).length === 1)) {
|
|
114
|
+
const combined = children.map((c) => c.text).join("");
|
|
115
|
+
delete opts.children;
|
|
116
|
+
if (combined && Object.keys(opts).length === 0) return combined;
|
|
117
|
+
if (combined) opts.text = combined;
|
|
118
|
+
}
|
|
119
|
+
return opts;
|
|
120
|
+
}
|
|
121
|
+
compileTableNode(node) {
|
|
122
|
+
const opts = renderDocx$4(node);
|
|
123
|
+
const colCount = this.getTableColumnCount(node);
|
|
124
|
+
const rows = [];
|
|
125
|
+
let activeSpans = [];
|
|
126
|
+
for (const rowNode of node.content ?? []) {
|
|
127
|
+
if (rowNode.type !== "tableRow") continue;
|
|
128
|
+
const rowOpts = renderDocx$7(rowNode);
|
|
129
|
+
const pmCells = (rowNode.content ?? []).filter((c) => c.type === "tableCell" || c.type === "tableHeader");
|
|
130
|
+
const currentSpans = [...activeSpans].sort((a, b) => a.colStart - b.colStart);
|
|
131
|
+
const newSpans = [];
|
|
132
|
+
const compiledCells = [];
|
|
133
|
+
let colIdx = 0;
|
|
134
|
+
let cellIdx = 0;
|
|
135
|
+
let spanIdx = 0;
|
|
136
|
+
while (spanIdx < currentSpans.length || cellIdx < pmCells.length) {
|
|
137
|
+
const nextSpanCol = spanIdx < currentSpans.length ? currentSpans[spanIdx].colStart : Infinity;
|
|
138
|
+
if (colIdx >= nextSpanCol) {
|
|
139
|
+
const span = currentSpans[spanIdx];
|
|
140
|
+
compiledCells.push({
|
|
141
|
+
verticalMerge: "continue",
|
|
142
|
+
columnSpan: span.colspan,
|
|
143
|
+
children: [{ paragraph: "" }]
|
|
144
|
+
});
|
|
145
|
+
colIdx += span.colspan;
|
|
146
|
+
spanIdx++;
|
|
147
|
+
} else {
|
|
148
|
+
const cell = this.compileTableCellNode(pmCells[cellIdx]);
|
|
149
|
+
const cs = cell.columnSpan ?? 1;
|
|
150
|
+
const rs = cell.rowSpan ?? 1;
|
|
151
|
+
if (rs > 1) {
|
|
152
|
+
delete cell.rowSpan;
|
|
153
|
+
cell.verticalMerge = "restart";
|
|
154
|
+
newSpans.push({
|
|
155
|
+
colStart: colIdx,
|
|
156
|
+
colspan: cs,
|
|
157
|
+
remainingRows: rs - 1
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
compiledCells.push(cell);
|
|
161
|
+
colIdx += cs;
|
|
162
|
+
cellIdx++;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
rowOpts.cells = compiledCells;
|
|
166
|
+
rows.push(rowOpts);
|
|
167
|
+
for (const span of currentSpans) span.remainingRows--;
|
|
168
|
+
activeSpans = [...currentSpans.filter((s) => s.remainingRows > 0), ...newSpans];
|
|
169
|
+
}
|
|
170
|
+
opts.rows = rows;
|
|
171
|
+
if (colCount > 0) {
|
|
172
|
+
const { columnWidths, tableWidth } = this.computeColumnWidths(node, colCount, opts);
|
|
173
|
+
opts.columnWidths = columnWidths;
|
|
174
|
+
if (!opts.width) opts.width = tableWidth;
|
|
175
|
+
if (!opts.layout) opts.layout = "autofit";
|
|
176
|
+
}
|
|
177
|
+
return opts;
|
|
178
|
+
}
|
|
179
|
+
/** Count grid columns from the first table row (summing colspan). */
|
|
180
|
+
getTableColumnCount(tableNode) {
|
|
181
|
+
for (const rowNode of tableNode.content ?? []) {
|
|
182
|
+
if (rowNode.type !== "tableRow") continue;
|
|
183
|
+
let count = 0;
|
|
184
|
+
for (const cell of rowNode.content ?? []) if (cell.type === "tableCell" || cell.type === "tableHeader") count += cell.attrs?.colspan ?? 1;
|
|
185
|
+
return count;
|
|
186
|
+
}
|
|
187
|
+
return 0;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Compute tblGrid column widths (twips) from first-row cell colwidth attrs.
|
|
191
|
+
* Returns columnWidths array and the appropriate tableWidth.
|
|
192
|
+
*/
|
|
193
|
+
computeColumnWidths(tableNode, colCount, _opts) {
|
|
194
|
+
const DEFAULT_CONTENT_TWIPS = 9026;
|
|
195
|
+
const tableColWidths = tableNode.attrs?.columnWidths;
|
|
196
|
+
if (tableColWidths && tableColWidths.length > 0) {
|
|
197
|
+
const filled = tableColWidths.slice(0, colCount);
|
|
198
|
+
while (filled.length < colCount) filled.push(filled[filled.length - 1] ?? Math.floor(DEFAULT_CONTENT_TWIPS / colCount));
|
|
199
|
+
return {
|
|
200
|
+
columnWidths: filled,
|
|
201
|
+
tableWidth: {
|
|
202
|
+
size: filled.reduce((a, b) => a + b, 0),
|
|
203
|
+
type: "dxa"
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
const widths = Array.from({ length: colCount }, () => null);
|
|
208
|
+
for (const rowNode of tableNode.content ?? []) {
|
|
209
|
+
if (rowNode.type !== "tableRow") continue;
|
|
210
|
+
let colIdx = 0;
|
|
211
|
+
for (const cell of rowNode.content ?? []) {
|
|
212
|
+
if (cell.type !== "tableCell" && cell.type !== "tableHeader") continue;
|
|
213
|
+
const colspan = cell.attrs?.colspan ?? 1;
|
|
214
|
+
const colwidth = cell.attrs?.colwidth;
|
|
215
|
+
if (colwidth) for (let i = 0; i < colspan && colIdx + i < colCount; i++) {
|
|
216
|
+
const px = colwidth[i] ?? colwidth[0];
|
|
217
|
+
if (px) widths[colIdx + i] = px * 15;
|
|
218
|
+
}
|
|
219
|
+
colIdx += colspan;
|
|
220
|
+
}
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
if (!widths.some((w) => w !== null)) {
|
|
224
|
+
const equal = Math.floor(DEFAULT_CONTENT_TWIPS / colCount);
|
|
225
|
+
return {
|
|
226
|
+
columnWidths: Array(colCount).fill(equal),
|
|
227
|
+
tableWidth: {
|
|
228
|
+
size: 5e3,
|
|
229
|
+
type: "pct"
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
const explicit = widths.filter((w) => w !== null);
|
|
234
|
+
const avg = Math.floor(explicit.reduce((a, b) => a + b, 0) / explicit.length);
|
|
235
|
+
const filled = widths.map((w) => w ?? avg);
|
|
236
|
+
return {
|
|
237
|
+
columnWidths: filled,
|
|
238
|
+
tableWidth: {
|
|
239
|
+
size: filled.reduce((a, b) => a + b, 0),
|
|
240
|
+
type: "dxa"
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
compileTableCellNode(cellNode) {
|
|
245
|
+
const cellOpts = cellNode.type === "tableHeader" ? renderDocx$6(cellNode) : renderDocx$5(cellNode);
|
|
246
|
+
const cellAlign = cellNode.attrs?.align ?? null;
|
|
247
|
+
const cellChildren = [];
|
|
248
|
+
for (const paraNode of cellNode.content ?? []) {
|
|
249
|
+
const para = this.compileParagraphNode(paraNode);
|
|
250
|
+
const paraObj = typeof para === "string" ? { text: para } : para;
|
|
251
|
+
if (cellAlign && !paraObj.alignment) paraObj.alignment = cellAlign;
|
|
252
|
+
cellChildren.push({ paragraph: paraObj });
|
|
253
|
+
}
|
|
254
|
+
if (cellChildren.length > 0) cellOpts.children = cellChildren;
|
|
255
|
+
return cellOpts;
|
|
256
|
+
}
|
|
257
|
+
compileListFromNode(node) {
|
|
258
|
+
const items = [];
|
|
259
|
+
for (const listItem of node.content ?? []) {
|
|
260
|
+
if (listItem.type !== "listItem" && listItem.type !== "taskItem") continue;
|
|
261
|
+
for (const child of listItem.content ?? []) if (child.type === "paragraph" || child.type === "heading") {
|
|
262
|
+
const para = this.compileParagraphNode(child);
|
|
263
|
+
const paraObj = typeof para === "string" ? { text: para } : para;
|
|
264
|
+
if (node.type === "bulletList") paraObj.bullet = { level: 0 };
|
|
265
|
+
else if (node.type === "orderedList") paraObj.numbering = {
|
|
266
|
+
reference: "default-numbering",
|
|
267
|
+
level: 0
|
|
268
|
+
};
|
|
269
|
+
items.push({ paragraph: paraObj });
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return items.length > 0 ? items : null;
|
|
273
|
+
}
|
|
274
|
+
compileInlineContent(content) {
|
|
275
|
+
if (!content) return [];
|
|
276
|
+
const children = [];
|
|
277
|
+
for (const node of content) switch (node.type) {
|
|
278
|
+
case "text":
|
|
279
|
+
this.compileTextNode(node, children);
|
|
280
|
+
break;
|
|
281
|
+
case "hardBreak":
|
|
282
|
+
children.push({ break: 1 });
|
|
283
|
+
break;
|
|
284
|
+
case "image": {
|
|
285
|
+
const imageRun = renderDocx$1(node);
|
|
286
|
+
if (imageRun) children.push(imageRun);
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return children;
|
|
291
|
+
}
|
|
292
|
+
compileTextNode(node, children) {
|
|
293
|
+
const text = node.text ?? "";
|
|
294
|
+
if (!text) return;
|
|
295
|
+
const marks = node.marks ?? [];
|
|
296
|
+
const runOpts = { text };
|
|
297
|
+
for (const mark of marks) switch (mark.type) {
|
|
298
|
+
case "bold":
|
|
299
|
+
runOpts.bold = true;
|
|
300
|
+
break;
|
|
301
|
+
case "italic":
|
|
302
|
+
runOpts.italic = true;
|
|
303
|
+
break;
|
|
304
|
+
case "underline":
|
|
305
|
+
runOpts.underline = { type: "single" };
|
|
306
|
+
break;
|
|
307
|
+
case "strike": {
|
|
308
|
+
const strikeProps = renderDocx$3(mark.attrs ?? {});
|
|
309
|
+
Object.assign(runOpts, strikeProps);
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
case "subscript":
|
|
313
|
+
runOpts.subScript = true;
|
|
314
|
+
break;
|
|
315
|
+
case "superscript":
|
|
316
|
+
runOpts.superScript = true;
|
|
317
|
+
break;
|
|
318
|
+
case "highlight":
|
|
319
|
+
runOpts.highlight = mark.attrs?.color ?? "yellow";
|
|
320
|
+
break;
|
|
321
|
+
case "code":
|
|
322
|
+
runOpts.font = "Consolas";
|
|
323
|
+
break;
|
|
324
|
+
case "textStyle": {
|
|
325
|
+
const tsProps = renderDocx$8(mark.attrs ?? {});
|
|
326
|
+
Object.assign(runOpts, tsProps);
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
case "link": {
|
|
330
|
+
const href = mark.attrs?.href;
|
|
331
|
+
if (href) {
|
|
332
|
+
const { text: _, ...runWithoutText } = runOpts;
|
|
333
|
+
const linkChildren = [];
|
|
334
|
+
if (text) linkChildren.push({
|
|
335
|
+
...runWithoutText,
|
|
336
|
+
text
|
|
337
|
+
});
|
|
338
|
+
children.push({ hyperlink: {
|
|
339
|
+
link: href.startsWith("#") ? void 0 : href,
|
|
340
|
+
anchor: href.startsWith("#") ? href.slice(1) : void 0,
|
|
341
|
+
children: linkChildren
|
|
342
|
+
} });
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
children.push(runOpts);
|
|
349
|
+
}
|
|
350
|
+
resolveSectionChild(child) {
|
|
351
|
+
if ("paragraph" in child) return this.resolveParagraph(child.paragraph);
|
|
352
|
+
if ("table" in child) return this.resolveTable(child.table);
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
resolveParagraph(opts) {
|
|
356
|
+
const resolved = typeof opts === "string" ? { text: opts } : opts;
|
|
357
|
+
const headingLevel = resolved.heading ? HEADING_LEVEL_MAP[resolved.heading] : void 0;
|
|
358
|
+
const nodeType = headingLevel ? "heading" : "paragraph";
|
|
359
|
+
const attrs = headingLevel ? parseDocx(resolved) : parseDocx$2(resolved);
|
|
360
|
+
if (resolved.bullet) return this.resolveListItem(resolved, "bulletList", attrs);
|
|
361
|
+
if (resolved.numbering) return this.resolveListItem(resolved, "orderedList", attrs);
|
|
362
|
+
const content = this.resolveInlineContent(resolved);
|
|
363
|
+
const cleanAttrsObj = cleanAttrs(attrs);
|
|
364
|
+
const node = { type: nodeType };
|
|
365
|
+
if (Object.keys(cleanAttrsObj).length > 0) node.attrs = cleanAttrsObj;
|
|
366
|
+
if (content.length > 0) node.content = content;
|
|
367
|
+
return node;
|
|
368
|
+
}
|
|
369
|
+
resolveListItem(opts, listType, paraAttrs) {
|
|
370
|
+
const content = this.resolveInlineContent(opts);
|
|
371
|
+
const cleanAttrsObj = cleanAttrs(paraAttrs);
|
|
372
|
+
const paragraphNode = { type: "paragraph" };
|
|
373
|
+
if (Object.keys(cleanAttrsObj).length > 0) paragraphNode.attrs = cleanAttrsObj;
|
|
374
|
+
if (content.length > 0) paragraphNode.content = content;
|
|
375
|
+
return {
|
|
376
|
+
type: listType,
|
|
377
|
+
content: [{
|
|
378
|
+
type: "listItem",
|
|
379
|
+
content: [paragraphNode]
|
|
380
|
+
}]
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
resolveTable(tableOpts) {
|
|
384
|
+
const attrs = parseDocx$4(tableOpts);
|
|
385
|
+
const rows = tableOpts.rows ?? [];
|
|
386
|
+
const content = [];
|
|
387
|
+
let activeSpans = /* @__PURE__ */ new Map();
|
|
388
|
+
for (const row of rows) {
|
|
389
|
+
const rowAttrs = parseDocx$7(row);
|
|
390
|
+
const cells = row.cells ?? [];
|
|
391
|
+
const cellNodes = [];
|
|
392
|
+
const nextActiveSpans = /* @__PURE__ */ new Map();
|
|
393
|
+
let colIdx = 0;
|
|
394
|
+
for (const cell of cells) {
|
|
395
|
+
const cellColspan = cell.columnSpan ?? 1;
|
|
396
|
+
const vMerge = cell.verticalMerge;
|
|
397
|
+
if (vMerge === "continue") {
|
|
398
|
+
const owner = activeSpans.get(colIdx);
|
|
399
|
+
if (owner) {
|
|
400
|
+
const ownerAttrs = owner.attrs ??= {};
|
|
401
|
+
ownerAttrs.rowspan = (ownerAttrs.rowspan ?? 1) + 1;
|
|
402
|
+
for (let c = colIdx; c < colIdx + cellColspan; c++) nextActiveSpans.set(c, owner);
|
|
403
|
+
}
|
|
404
|
+
colIdx += cellColspan;
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
const isHeader = row.tableHeader;
|
|
408
|
+
const cellAttrs = isHeader ? parseDocx$6(cell) : parseDocx$5(cell);
|
|
409
|
+
delete cellAttrs.verticalMerge;
|
|
410
|
+
const cellChildren = cell.children ?? [];
|
|
411
|
+
const cellContent = [];
|
|
412
|
+
for (const cc of cellChildren) {
|
|
413
|
+
const resolved = this.resolveSectionChild(cc);
|
|
414
|
+
if (resolved) cellContent.push(resolved);
|
|
415
|
+
}
|
|
416
|
+
const cellNode = { type: isHeader ? "tableHeader" : "tableCell" };
|
|
417
|
+
if (Object.keys(cellAttrs).length > 0) cellNode.attrs = cleanAttrs(cellAttrs);
|
|
418
|
+
if (cellContent.length > 0) cellNode.content = cellContent;
|
|
419
|
+
if (vMerge === "restart") for (let c = colIdx; c < colIdx + cellColspan; c++) nextActiveSpans.set(c, cellNode);
|
|
420
|
+
cellNodes.push(cellNode);
|
|
421
|
+
colIdx += cellColspan;
|
|
422
|
+
}
|
|
423
|
+
activeSpans = nextActiveSpans;
|
|
424
|
+
const rowNode = { type: "tableRow" };
|
|
425
|
+
if (Object.keys(rowAttrs).length > 0) rowNode.attrs = cleanAttrs(rowAttrs);
|
|
426
|
+
if (cellNodes.length > 0) rowNode.content = cellNodes;
|
|
427
|
+
content.push(rowNode);
|
|
428
|
+
}
|
|
429
|
+
const node = { type: "table" };
|
|
430
|
+
if (Object.keys(attrs).length > 0) node.attrs = cleanAttrs(attrs);
|
|
431
|
+
if (content.length > 0) node.content = content;
|
|
432
|
+
return node;
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Resolve a paragraph's inline content. @office-open collapses a plain-text
|
|
436
|
+
* paragraph (a single run with no properties) to a bare string or a `{ text }`
|
|
437
|
+
* object with no `children` — recover that text here so it round-trips back to
|
|
438
|
+
* a text node instead of being dropped.
|
|
439
|
+
*/
|
|
440
|
+
resolveInlineContent(opts) {
|
|
441
|
+
const content = this.resolveParagraphChildren(opts.children);
|
|
442
|
+
if (content.length === 0 && opts.text) {
|
|
443
|
+
const marks = this.resolveMarks(opts);
|
|
444
|
+
const node = {
|
|
445
|
+
type: "text",
|
|
446
|
+
text: opts.text
|
|
447
|
+
};
|
|
448
|
+
if (marks) node.marks = marks;
|
|
449
|
+
return [node];
|
|
450
|
+
}
|
|
451
|
+
return content;
|
|
452
|
+
}
|
|
453
|
+
resolveParagraphChildren(children) {
|
|
454
|
+
if (!children || children.length === 0) return [];
|
|
455
|
+
const nodes = [];
|
|
456
|
+
for (const child of children) {
|
|
457
|
+
if (typeof child === "string") {
|
|
458
|
+
if (child) nodes.push({
|
|
459
|
+
type: "text",
|
|
460
|
+
text: child
|
|
461
|
+
});
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
if (typeof child === "object" && child !== null) {
|
|
465
|
+
const resolved = this.resolveParagraphChild(child);
|
|
466
|
+
if (resolved) nodes.push(...Array.isArray(resolved) ? resolved : [resolved]);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return nodes;
|
|
470
|
+
}
|
|
471
|
+
resolveParagraphChild(child) {
|
|
472
|
+
if ("text" in child || "children" in child || "break" in child) return this.resolveRun(child);
|
|
473
|
+
if ("image" in child) return this.resolveImage(child.image);
|
|
474
|
+
if ("hyperlink" in child) return this.resolveHyperlink(child.hyperlink);
|
|
475
|
+
if ("pageBreak" in child) return {
|
|
476
|
+
type: "paragraph",
|
|
477
|
+
attrs: { pageBreak: true }
|
|
478
|
+
};
|
|
479
|
+
if ("columnBreak" in child) return { type: "hardBreak" };
|
|
480
|
+
return null;
|
|
481
|
+
}
|
|
482
|
+
resolveRun(opts) {
|
|
483
|
+
const text = opts.text;
|
|
484
|
+
if (text === void 0 && !opts.children) return null;
|
|
485
|
+
if (opts.children && !text) {
|
|
486
|
+
const parts = [];
|
|
487
|
+
for (const c of opts.children) if (typeof c === "string") parts.push(c);
|
|
488
|
+
if (parts.length === 0) return null;
|
|
489
|
+
return {
|
|
490
|
+
type: "text",
|
|
491
|
+
text: parts.join(""),
|
|
492
|
+
marks: this.resolveMarks(opts)
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
return {
|
|
496
|
+
type: "text",
|
|
497
|
+
text: text ?? "",
|
|
498
|
+
marks: this.resolveMarks(opts)
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
resolveMarks(opts) {
|
|
502
|
+
const marks = [];
|
|
503
|
+
if (opts.bold) marks.push({ type: "bold" });
|
|
504
|
+
if (opts.italic) marks.push({ type: "italic" });
|
|
505
|
+
if (opts.underline) marks.push({ type: "underline" });
|
|
506
|
+
if (opts.strike) {
|
|
507
|
+
const strikeAttrs = parseDocx$3(opts);
|
|
508
|
+
marks.push({
|
|
509
|
+
type: "strike",
|
|
510
|
+
attrs: { doubleStrike: strikeAttrs.doubleStrike ?? null }
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
if (opts.subScript) marks.push({ type: "subscript" });
|
|
514
|
+
if (opts.superScript) marks.push({ type: "superscript" });
|
|
515
|
+
if (opts.highlight) marks.push({ type: "highlight" });
|
|
516
|
+
if (opts.font === "Consolas" || opts.font === "monospace") marks.push({ type: "code" });
|
|
517
|
+
const textStyleAttrs = parseDocx$8(opts);
|
|
518
|
+
if (opts.font === "Consolas" || opts.font === "monospace") delete textStyleAttrs.font;
|
|
519
|
+
if (Object.keys(textStyleAttrs).length > 0) marks.push({
|
|
520
|
+
type: "textStyle",
|
|
521
|
+
attrs: textStyleAttrs
|
|
522
|
+
});
|
|
523
|
+
return marks.length > 0 ? marks : void 0;
|
|
524
|
+
}
|
|
525
|
+
resolveImage(imageOpts) {
|
|
526
|
+
const attrs = parseDocx$1(imageOpts);
|
|
527
|
+
const data = imageOpts.data;
|
|
528
|
+
const type = imageOpts.type;
|
|
529
|
+
if (data && type) attrs.src = `data:image/${type};base64,${typeof btoa !== "undefined" ? btoa(String.fromCharCode(...data instanceof ArrayBuffer ? new Uint8Array(data) : data)) : Buffer.from(data).toString("base64")}`;
|
|
530
|
+
return {
|
|
531
|
+
type: "image",
|
|
532
|
+
attrs
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
resolveHyperlink(hyperlink) {
|
|
536
|
+
const href = hyperlink.link ?? (hyperlink.anchor ? `#${hyperlink.anchor}` : "");
|
|
537
|
+
if (!href) return null;
|
|
538
|
+
const content = this.resolveParagraphChildren((hyperlink.children ?? []).map((c) => c));
|
|
539
|
+
if (content.length > 0) {
|
|
540
|
+
const merged = mergeTextNodes(content);
|
|
541
|
+
for (const node of merged) if (node.type === "text") node.marks = [...node.marks ?? [], {
|
|
542
|
+
type: "link",
|
|
543
|
+
attrs: {
|
|
544
|
+
href,
|
|
545
|
+
target: "_blank",
|
|
546
|
+
rel: "noopener noreferrer nofollow",
|
|
547
|
+
class: null,
|
|
548
|
+
title: hyperlink.tooltip ?? null
|
|
549
|
+
}
|
|
550
|
+
}];
|
|
551
|
+
return merged;
|
|
552
|
+
}
|
|
553
|
+
return null;
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
const HEADING_LEVEL_MAP = {
|
|
557
|
+
Heading1: 1,
|
|
558
|
+
Heading2: 2,
|
|
559
|
+
Heading3: 3,
|
|
560
|
+
Heading4: 4,
|
|
561
|
+
Heading5: 5,
|
|
562
|
+
Heading6: 6,
|
|
563
|
+
Title: 1
|
|
564
|
+
};
|
|
565
|
+
const defaultManager = new DocxManager();
|
|
566
|
+
/**
|
|
567
|
+
* Parse a DOCX file into Tiptap JSON (runtime model).
|
|
568
|
+
*
|
|
569
|
+
* Combines @office-open/docx's `parseDocument` (DOCX binary → DocumentOptions)
|
|
570
|
+
* with `DocxManager.resolve` (DocumentOptions → Tiptap JSON).
|
|
571
|
+
*/
|
|
572
|
+
function parseDOCX(data) {
|
|
573
|
+
return defaultManager.resolve(parseDocument(data));
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Generate a DOCX file from Tiptap JSON (runtime model), asynchronously.
|
|
577
|
+
*
|
|
578
|
+
* Pipeline: `prepareDocument` (default: fetch http images, in place) →
|
|
579
|
+
* `DocxManager.compile` → @office-open/docx's `generateDocument`. `packer.type`
|
|
580
|
+
* controls the output format (default: `"nodebuffer"` → Buffer). Non-blocking
|
|
581
|
+
* (fflate Web Workers). With the default `prepare`, the input `json` is mutated
|
|
582
|
+
* in place (http image URLs become embedded data URLs).
|
|
583
|
+
*/
|
|
584
|
+
async function generateDOCX(json, options) {
|
|
585
|
+
const { prepare = true, packer } = options ?? {};
|
|
586
|
+
if (prepare !== false) await prepareDocument(json, prepare === true ? void 0 : prepare);
|
|
587
|
+
return generateDocument(compileDocument(json), packer);
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Generate a DOCX file synchronously — fastest throughput, blocks the event loop.
|
|
591
|
+
*
|
|
592
|
+
* Pipeline: `DocxManager.compile` → `generateDocumentSync`. Does **not** run
|
|
593
|
+
* `prepareDocument` (it is async); call `await prepareDocument(json)` first
|
|
594
|
+
* when http images need embedding.
|
|
595
|
+
*/
|
|
596
|
+
function generateDOCXSync(json, packerOptions) {
|
|
597
|
+
return generateDocumentSync(compileDocument(json), packerOptions);
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Generate a DOCX file as a `ReadableStream<Uint8Array>` — for large documents
|
|
601
|
+
* or streaming HTTP responses.
|
|
602
|
+
*
|
|
603
|
+
* Pipeline: `prepareDocument` (default: fetch http images, in place) →
|
|
604
|
+
* `DocxManager.compile` → `generateDocumentStream`. Async due to preparation.
|
|
605
|
+
*/
|
|
606
|
+
async function generateDOCXStream(json, options) {
|
|
607
|
+
const { prepare = true, packer } = options ?? {};
|
|
608
|
+
if (prepare !== false) await prepareDocument(json, prepare === true ? void 0 : prepare);
|
|
609
|
+
return generateDocumentStream(compileDocument(json), packer);
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Convert DocumentOptions (persistence model) to Tiptap JSON (runtime model).
|
|
613
|
+
*/
|
|
614
|
+
function resolveDocument(docOpts) {
|
|
615
|
+
return defaultManager.resolve(docOpts);
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Convert Tiptap JSON (runtime model) to DocumentOptions (persistence model).
|
|
619
|
+
*/
|
|
620
|
+
function compileDocument(json) {
|
|
621
|
+
return defaultManager.compile(json);
|
|
622
|
+
}
|
|
623
|
+
//#endregion
|
|
624
|
+
export { DocxManager, compileDocument, generateDOCX, generateDOCXStream, generateDOCXSync, parseDOCX, resolveDocument };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { a as JSONContent, i as Extensions } from "../core-omBKMRtl.mjs";
|
|
2
|
+
import { ParseOptions } from "@tiptap/pm/model";
|
|
3
|
+
|
|
4
|
+
//#region src/converters/html.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Parse HTML string to Tiptap JSON.
|
|
7
|
+
*/
|
|
8
|
+
declare function parseHTML(html: string, extensions?: Extensions, options?: ParseOptions): JSONContent;
|
|
9
|
+
/**
|
|
10
|
+
* Generate HTML string from Tiptap JSON.
|
|
11
|
+
*/
|
|
12
|
+
declare function generateHTML(doc: JSONContent, extensions?: Extensions): string;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { generateHTML, parseHTML };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { s as docxExtensions } from "../core-CFIQVRfx.mjs";
|
|
2
|
+
import { generateHTML as generateHTML$1, generateJSON } from "@tiptap/html";
|
|
3
|
+
//#region src/converters/html.ts
|
|
4
|
+
const defaultExtensions = docxExtensions;
|
|
5
|
+
/**
|
|
6
|
+
* Parse HTML string to Tiptap JSON.
|
|
7
|
+
*/
|
|
8
|
+
function parseHTML(html, extensions, options) {
|
|
9
|
+
return generateJSON(html, extensions ?? defaultExtensions, options);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Generate HTML string from Tiptap JSON.
|
|
13
|
+
*/
|
|
14
|
+
function generateHTML(doc, extensions) {
|
|
15
|
+
return generateHTML$1(doc, extensions ?? defaultExtensions);
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
export { generateHTML, parseHTML };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { a as JSONContent } from "../core-omBKMRtl.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/converters/markdown.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Parse Markdown string to Tiptap JSON.
|
|
6
|
+
*/
|
|
7
|
+
declare function parseMarkdown(markdown: string): JSONContent;
|
|
8
|
+
/**
|
|
9
|
+
* Generate Markdown string from Tiptap JSON.
|
|
10
|
+
*/
|
|
11
|
+
declare function generateMarkdown(doc: JSONContent): string;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { generateMarkdown, parseMarkdown };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { s as docxExtensions } from "../core-CFIQVRfx.mjs";
|
|
2
|
+
import { Markdown, MarkdownManager } from "@tiptap/markdown";
|
|
3
|
+
//#region src/converters/markdown.ts
|
|
4
|
+
const markdownManager = new MarkdownManager({ extensions: [...docxExtensions, Markdown] });
|
|
5
|
+
/**
|
|
6
|
+
* Parse Markdown string to Tiptap JSON.
|
|
7
|
+
*/
|
|
8
|
+
function parseMarkdown(markdown) {
|
|
9
|
+
return markdownManager.parse(markdown);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Generate Markdown string from Tiptap JSON.
|
|
13
|
+
*/
|
|
14
|
+
function generateMarkdown(doc) {
|
|
15
|
+
return markdownManager.serialize(doc);
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
export { generateMarkdown, parseMarkdown };
|