@4399ywkf/editor 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{DocView-BPODKQ57.js → DocView-GDN5CZCJ.js} +3 -3
- package/dist/{DocView-BPODKQ57.js.map → DocView-GDN5CZCJ.js.map} +1 -1
- package/dist/{chunk-3GZSG6KH.js → chunk-ZRFOS52C.js} +79 -3
- package/dist/chunk-ZRFOS52C.js.map +1 -0
- package/dist/doc-runtime.d.ts +33 -0
- package/dist/doc-runtime.js +87 -0
- package/dist/doc-runtime.js.map +1 -1
- package/dist/doc.js +1 -1
- package/dist/docx.d.ts +121 -6
- package/dist/docx.js +421 -40
- package/dist/docx.js.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-3GZSG6KH.js.map +0 -1
package/dist/docx.js
CHANGED
|
@@ -2,6 +2,20 @@ import { Node, mergeAttributes, Extension } from '@tiptap/core';
|
|
|
2
2
|
import { unzipSync, zipSync } from 'fflate';
|
|
3
3
|
|
|
4
4
|
// src/docx/index.ts
|
|
5
|
+
|
|
6
|
+
// src/docx/measure.ts
|
|
7
|
+
function measureContentWidth(dom) {
|
|
8
|
+
const el = dom;
|
|
9
|
+
if (!el || typeof globalThis.getComputedStyle !== "function") return void 0;
|
|
10
|
+
const cs = globalThis.getComputedStyle(el);
|
|
11
|
+
const w = el.clientWidth - (Number.parseFloat(cs.paddingLeft) || 0) - (Number.parseFloat(cs.paddingRight) || 0);
|
|
12
|
+
return Number.isFinite(w) && w > 0 ? w : void 0;
|
|
13
|
+
}
|
|
14
|
+
function tableCellMinWidth(editor) {
|
|
15
|
+
const ext = editor.extensionManager?.extensions?.find((e) => e.name === "table");
|
|
16
|
+
const v = ext?.options?.cellMinWidth;
|
|
17
|
+
return typeof v === "number" && v > 0 ? v : void 0;
|
|
18
|
+
}
|
|
5
19
|
var DocxField = Node.create({
|
|
6
20
|
name: "docxField",
|
|
7
21
|
group: "inline",
|
|
@@ -86,12 +100,34 @@ var DocxInk = Node.create({
|
|
|
86
100
|
|
|
87
101
|
// src/docx/parse.ts
|
|
88
102
|
var EMU_PER_PX = 9525;
|
|
103
|
+
var HIGHLIGHT = {
|
|
104
|
+
black: "#000000",
|
|
105
|
+
blue: "#0000FF",
|
|
106
|
+
cyan: "#00FFFF",
|
|
107
|
+
green: "#008000",
|
|
108
|
+
magenta: "#FF00FF",
|
|
109
|
+
red: "#FF0000",
|
|
110
|
+
yellow: "#FFFF00",
|
|
111
|
+
white: "#FFFFFF",
|
|
112
|
+
darkBlue: "#000080",
|
|
113
|
+
darkCyan: "#008080",
|
|
114
|
+
darkGreen: "#006400",
|
|
115
|
+
darkMagenta: "#800080",
|
|
116
|
+
darkRed: "#800000",
|
|
117
|
+
darkYellow: "#808000",
|
|
118
|
+
darkGray: "#808080",
|
|
119
|
+
lightGray: "#C0C0C0"
|
|
120
|
+
};
|
|
89
121
|
var decoder = new TextDecoder("utf-8");
|
|
90
122
|
function parseDocx(entries, opts) {
|
|
91
123
|
const { DOMParser } = opts;
|
|
92
124
|
const recovered = {
|
|
93
125
|
styleFormat: 0,
|
|
94
126
|
directFormat: 0,
|
|
127
|
+
charStyles: 0,
|
|
128
|
+
fontSizes: 0,
|
|
129
|
+
fontFamilies: 0,
|
|
130
|
+
highlights: 0,
|
|
95
131
|
fields: 0,
|
|
96
132
|
ink: 0,
|
|
97
133
|
floatImages: 0,
|
|
@@ -102,6 +138,9 @@ function parseDocx(entries, opts) {
|
|
|
102
138
|
tableCells: 0,
|
|
103
139
|
mergedCells: 0,
|
|
104
140
|
cellShading: 0,
|
|
141
|
+
paraShading: 0,
|
|
142
|
+
paraSpacing: 0,
|
|
143
|
+
paraIndent: 0,
|
|
105
144
|
lists: 0,
|
|
106
145
|
listItems: 0
|
|
107
146
|
};
|
|
@@ -125,6 +164,7 @@ function parseDocx(entries, opts) {
|
|
|
125
164
|
}
|
|
126
165
|
const STYLE_NAME = {};
|
|
127
166
|
const STYLE_FMT = {};
|
|
167
|
+
const STYLE_PPR = {};
|
|
128
168
|
const STYLE_BASE = {};
|
|
129
169
|
const stylesDoc = xml("word/styles.xml");
|
|
130
170
|
if (stylesDoc) {
|
|
@@ -134,9 +174,10 @@ function parseDocx(entries, opts) {
|
|
|
134
174
|
const name = s.getElementsByTagName("w:name")[0]?.getAttribute("w:val");
|
|
135
175
|
if (name) STYLE_NAME[id] = name;
|
|
136
176
|
STYLE_BASE[id] = s.getElementsByTagName("w:basedOn")[0]?.getAttribute("w:val") ?? null;
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
);
|
|
177
|
+
const own = Array.from(s.childNodes).filter((c) => c.nodeType === 1);
|
|
178
|
+
const pPr = own.find((c) => c.nodeName === "w:pPr");
|
|
179
|
+
if (pPr) STYLE_PPR[id] = pPr;
|
|
180
|
+
const rPr = own.find((c) => c.nodeName === "w:rPr");
|
|
140
181
|
if (!rPr) continue;
|
|
141
182
|
STYLE_FMT[id] = readFmt(rPr);
|
|
142
183
|
}
|
|
@@ -146,15 +187,29 @@ function parseDocx(entries, opts) {
|
|
|
146
187
|
const e = rPr.getElementsByTagName(t)[0];
|
|
147
188
|
if (!e) return void 0;
|
|
148
189
|
const v = e.getAttribute("w:val");
|
|
149
|
-
return v
|
|
190
|
+
return v === "0" || v === "false" || v === "none" || v === "off" ? false : true;
|
|
150
191
|
};
|
|
151
192
|
const color = rPr.getElementsByTagName("w:color")[0]?.getAttribute("w:val");
|
|
193
|
+
const szRaw = rPr.getElementsByTagName("w:sz")[0]?.getAttribute("w:val");
|
|
194
|
+
const szNum = szRaw ? Number(szRaw) : Number.NaN;
|
|
195
|
+
const size = Number.isFinite(szNum) && szNum > 0 ? `${szNum / 2}pt` : void 0;
|
|
196
|
+
const rf = rPr.getElementsByTagName("w:rFonts")[0];
|
|
197
|
+
const stack = [rf?.getAttribute("w:ascii"), rf?.getAttribute("w:hAnsi"), rf?.getAttribute("w:eastAsia")].filter((n) => Boolean(n)).filter((n, i, a) => a.indexOf(n) === i).map((n) => /[\s,]/.test(n) ? `"${n}"` : n);
|
|
198
|
+
const font = stack.length ? stack.join(", ") : void 0;
|
|
199
|
+
const hlName = rPr.getElementsByTagName("w:highlight")[0]?.getAttribute("w:val");
|
|
200
|
+
const shdFill = rPr.getElementsByTagName("w:shd")[0]?.getAttribute("w:fill");
|
|
201
|
+
const highlight = hlName && hlName !== "none" ? HIGHLIGHT[hlName] ?? void 0 : shdFill && shdFill !== "auto" && shdFill !== "FFFFFF" ? `#${shdFill}` : void 0;
|
|
202
|
+
const va = rPr.getElementsByTagName("w:vertAlign")[0]?.getAttribute("w:val");
|
|
152
203
|
return {
|
|
153
204
|
b: on("w:b"),
|
|
154
205
|
i: on("w:i"),
|
|
155
206
|
u: on("w:u"),
|
|
156
207
|
strike: on("w:strike"),
|
|
157
|
-
color: color && color !== "auto" ? `#${color}` : void 0
|
|
208
|
+
color: color && color !== "auto" ? `#${color}` : void 0,
|
|
209
|
+
size,
|
|
210
|
+
font,
|
|
211
|
+
highlight,
|
|
212
|
+
vertAlign: va === "superscript" || va === "subscript" ? va : void 0
|
|
158
213
|
};
|
|
159
214
|
}
|
|
160
215
|
function resolveStyleFmt(id, seen = /* @__PURE__ */ new Set()) {
|
|
@@ -172,7 +227,22 @@ function parseDocx(entries, opts) {
|
|
|
172
227
|
if (f.i) marks.push({ type: "italic" });
|
|
173
228
|
if (f.u) marks.push({ type: "underline" });
|
|
174
229
|
if (f.strike) marks.push({ type: "strike" });
|
|
175
|
-
|
|
230
|
+
const ts = {};
|
|
231
|
+
if (f.color) ts.color = f.color;
|
|
232
|
+
if (f.size) {
|
|
233
|
+
ts.fontSize = f.size;
|
|
234
|
+
recovered.fontSizes += 1;
|
|
235
|
+
}
|
|
236
|
+
if (f.font) {
|
|
237
|
+
ts.fontFamily = f.font;
|
|
238
|
+
recovered.fontFamilies += 1;
|
|
239
|
+
}
|
|
240
|
+
if (Object.keys(ts).length) marks.push({ type: "textStyle", attrs: ts });
|
|
241
|
+
if (f.highlight) {
|
|
242
|
+
marks.push({ type: "highlight", attrs: { color: f.highlight } });
|
|
243
|
+
recovered.highlights += 1;
|
|
244
|
+
}
|
|
245
|
+
if (f.vertAlign) marks.push({ type: f.vertAlign });
|
|
176
246
|
return marks;
|
|
177
247
|
}
|
|
178
248
|
const mediaCache = /* @__PURE__ */ new Map();
|
|
@@ -192,6 +262,7 @@ function parseDocx(entries, opts) {
|
|
|
192
262
|
}
|
|
193
263
|
const docXml = xml("word/document.xml");
|
|
194
264
|
if (!docXml) throw new Error("这不是一个有效的 .docx:缺少 word/document.xml");
|
|
265
|
+
const documentXml = docXml;
|
|
195
266
|
const kids = (n) => Array.from(n.childNodes).filter((c) => c.nodeType === 1);
|
|
196
267
|
const first = (n, name) => n.getElementsByTagName(name)[0] ?? null;
|
|
197
268
|
function readDrawing(dr) {
|
|
@@ -264,7 +335,14 @@ function parseDocx(entries, opts) {
|
|
|
264
335
|
const rPr = first(r, "w:rPr");
|
|
265
336
|
const direct = rPr ? readFmt(rPr) : {};
|
|
266
337
|
if (rPr && Object.values(direct).some((v) => v !== void 0)) recovered.directFormat += 1;
|
|
267
|
-
const
|
|
338
|
+
const rStyleId = rPr ? rPr.getElementsByTagName("w:rStyle")[0]?.getAttribute("w:val") ?? null : null;
|
|
339
|
+
const charFmt = rStyleId ? resolveStyleFmt(rStyleId) : {};
|
|
340
|
+
if (rStyleId && Object.values(charFmt).some((v) => v !== void 0)) recovered.charStyles += 1;
|
|
341
|
+
const marks = fmtToMarks({
|
|
342
|
+
...inherited,
|
|
343
|
+
...stripUndefined(charFmt),
|
|
344
|
+
...stripUndefined(direct)
|
|
345
|
+
});
|
|
268
346
|
for (const c of kids(r)) {
|
|
269
347
|
const t = c.nodeName;
|
|
270
348
|
if (t === "w:t") {
|
|
@@ -340,17 +418,63 @@ function parseDocx(entries, opts) {
|
|
|
340
418
|
}
|
|
341
419
|
}
|
|
342
420
|
}
|
|
421
|
+
function paraLayout(pPr, count = true) {
|
|
422
|
+
const attrs = {};
|
|
423
|
+
const jc = first(pPr, "w:jc")?.getAttribute("w:val");
|
|
424
|
+
if (jc) attrs.textAlign = jc === "both" ? "justify" : jc;
|
|
425
|
+
const fill = first(pPr, "w:shd")?.getAttribute("w:fill");
|
|
426
|
+
if (fill && fill !== "auto" && fill !== "FFFFFF") {
|
|
427
|
+
attrs.backgroundColor = `#${fill}`;
|
|
428
|
+
if (count) recovered.paraShading += 1;
|
|
429
|
+
}
|
|
430
|
+
const ind = first(pPr, "w:ind");
|
|
431
|
+
const left = Number(ind?.getAttribute("w:left") ?? ind?.getAttribute("w:start") ?? 0);
|
|
432
|
+
if (left > 0) {
|
|
433
|
+
attrs.indent = Math.min(8, Math.max(1, Math.round(left / 420)));
|
|
434
|
+
if (count) recovered.paraIndent += 1;
|
|
435
|
+
}
|
|
436
|
+
const sp = first(pPr, "w:spacing");
|
|
437
|
+
if (sp) {
|
|
438
|
+
let touched = false;
|
|
439
|
+
const line = Number(sp.getAttribute("w:line") ?? 0);
|
|
440
|
+
if (line > 0) {
|
|
441
|
+
const rule = sp.getAttribute("w:lineRule") ?? "auto";
|
|
442
|
+
attrs.lineHeight = rule === "auto" ? String(Math.round(line / 240 * 100) / 100) : `${line / 20}pt`;
|
|
443
|
+
touched = true;
|
|
444
|
+
}
|
|
445
|
+
const before = sp.getAttribute("w:before");
|
|
446
|
+
if (before != null && sp.getAttribute("w:beforeAutospacing") !== "1") {
|
|
447
|
+
attrs.spaceBefore = `${Number(before) / 20}pt`;
|
|
448
|
+
touched = true;
|
|
449
|
+
}
|
|
450
|
+
const after = sp.getAttribute("w:after");
|
|
451
|
+
if (after != null && sp.getAttribute("w:afterAutospacing") !== "1") {
|
|
452
|
+
attrs.spaceAfter = `${Number(after) / 20}pt`;
|
|
453
|
+
touched = true;
|
|
454
|
+
}
|
|
455
|
+
if (touched && count) recovered.paraSpacing += 1;
|
|
456
|
+
}
|
|
457
|
+
return attrs;
|
|
458
|
+
}
|
|
459
|
+
function resolveStylePara(id, seen = /* @__PURE__ */ new Set()) {
|
|
460
|
+
if (!id || seen.has(id)) return {};
|
|
461
|
+
seen.add(id);
|
|
462
|
+
const pPr = STYLE_PPR[id];
|
|
463
|
+
return {
|
|
464
|
+
...resolveStylePara(STYLE_BASE[id] ?? null, seen),
|
|
465
|
+
...pPr ? paraLayout(pPr, false) : {}
|
|
466
|
+
};
|
|
467
|
+
}
|
|
343
468
|
function paragraph(p) {
|
|
344
469
|
const pPr = first(p, "w:pPr");
|
|
345
470
|
const styleId = pPr ? first(pPr, "w:pStyle")?.getAttribute("w:val") ?? null : null;
|
|
346
471
|
const styleName = styleId ? STYLE_NAME[styleId] ?? styleId : null;
|
|
347
|
-
const jc = pPr ? first(pPr, "w:jc")?.getAttribute("w:val") : null;
|
|
348
472
|
const styleFmt = resolveStyleFmt(styleId);
|
|
349
473
|
if (Object.values(styleFmt).some((v) => v !== void 0)) recovered.styleFormat += 1;
|
|
350
474
|
const out = { inline: [], blocks: [] };
|
|
351
475
|
childrenInto(p, out, styleFmt);
|
|
352
|
-
const attrs = {};
|
|
353
|
-
if (
|
|
476
|
+
const attrs = { ...resolveStylePara(styleId) };
|
|
477
|
+
if (pPr) Object.assign(attrs, paraLayout(pPr));
|
|
354
478
|
const heading = /^heading\s*(\d)$/i.exec(styleName ?? "");
|
|
355
479
|
const blocks = [];
|
|
356
480
|
const hasContent = out.inline.length > 0;
|
|
@@ -367,6 +491,45 @@ function parseDocx(entries, opts) {
|
|
|
367
491
|
return blocks;
|
|
368
492
|
}
|
|
369
493
|
const dxaToPx = (v) => v ? Math.round(Number(v) / 15) : null;
|
|
494
|
+
const sectionTextWidthPx = (() => {
|
|
495
|
+
const sects = documentXml.getElementsByTagName("w:sectPr");
|
|
496
|
+
const sect = sects[sects.length - 1];
|
|
497
|
+
if (!sect) return null;
|
|
498
|
+
const num = (tag, attr) => Number(sect.getElementsByTagName(tag)[0]?.getAttribute(attr) ?? Number.NaN);
|
|
499
|
+
const text2 = num("w:pgSz", "w:w") - num("w:pgMar", "w:left") - num("w:pgMar", "w:right");
|
|
500
|
+
return Number.isFinite(text2) && text2 > 0 ? text2 / 15 : null;
|
|
501
|
+
})();
|
|
502
|
+
function fitColumns(pxWidths) {
|
|
503
|
+
const basis = opts.contentWidthPx;
|
|
504
|
+
const usable = typeof basis === "number" && Number.isFinite(basis) && basis > 0 && sectionTextWidthPx !== null && sectionTextWidthPx > 0;
|
|
505
|
+
const scale = usable ? basis / sectionTextWidthPx : 1;
|
|
506
|
+
let out = pxWidths.map((w) => w * scale);
|
|
507
|
+
const min = opts.minColumnWidthPx ?? 0;
|
|
508
|
+
if (min > 0) {
|
|
509
|
+
const total = out.reduce((a, b) => a + b, 0);
|
|
510
|
+
const pinned = out.map(() => false);
|
|
511
|
+
for (let round = 0; round < out.length; round++) {
|
|
512
|
+
const freeTotal = total - out.reduce((a, w, i) => a + (pinned[i] ? min : 0), 0);
|
|
513
|
+
const freeSum = out.reduce((a, w, i) => a + (pinned[i] ? 0 : w), 0);
|
|
514
|
+
if (freeSum <= 0 || freeTotal <= 0) break;
|
|
515
|
+
const k = freeTotal / freeSum;
|
|
516
|
+
const short = out.map((w, i) => !pinned[i] && w * k < min);
|
|
517
|
+
if (!short.some(Boolean)) {
|
|
518
|
+
out = out.map((w, i) => pinned[i] ? min : w * k);
|
|
519
|
+
break;
|
|
520
|
+
}
|
|
521
|
+
short.forEach((s, i) => {
|
|
522
|
+
if (s) pinned[i] = true;
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
out = out.map((w, i) => pinned[i] ? Math.max(w, min) : w);
|
|
526
|
+
}
|
|
527
|
+
const target = Math.round(out.reduce((a, b) => a + b, 0));
|
|
528
|
+
const rounded = out.map((w) => Math.round(w));
|
|
529
|
+
const drift = target - rounded.reduce((a, b) => a + b, 0);
|
|
530
|
+
if (drift !== 0 && rounded.length) rounded[rounded.length - 1] += drift;
|
|
531
|
+
return rounded.map((w) => Math.max(1, w));
|
|
532
|
+
}
|
|
370
533
|
function readRows(tbl) {
|
|
371
534
|
const rows = [];
|
|
372
535
|
for (const tr of kids(tbl)) {
|
|
@@ -404,7 +567,7 @@ function parseDocx(entries, opts) {
|
|
|
404
567
|
const grid = first(tbl, "w:tblGrid");
|
|
405
568
|
const rawWidths = grid ? kids(grid).filter((g) => g.nodeName === "w:gridCol").map((g) => dxaToPx(g.getAttribute("w:w"))) : [];
|
|
406
569
|
const sane = rawWidths.length > 0 && rawWidths.every((w) => w != null && w >= 20);
|
|
407
|
-
const colWidths = useAbsoluteWidths && sane ? rawWidths : [];
|
|
570
|
+
const colWidths = useAbsoluteWidths && sane ? fitColumns(rawWidths) : [];
|
|
408
571
|
if (rawWidths.length > 0 && colWidths.length === 0) {
|
|
409
572
|
warnings.push(
|
|
410
573
|
`表格列宽未采信(tblW type=${tblWType ?? "未指定"}),改为自动分配 —— gridCol 原值 ${rawWidths.slice(0, 4).join("/")}`
|
|
@@ -543,12 +706,89 @@ function parseDocx(entries, opts) {
|
|
|
543
706
|
if (!body) throw new Error("docx 缺少 w:body");
|
|
544
707
|
const content = blocksOf(body);
|
|
545
708
|
const doc = remapTypes({ type: "doc", content }, opts.prosemirrorNodes, opts.prosemirrorMarks);
|
|
709
|
+
const section = readSection();
|
|
710
|
+
const defaultHeader = section.refs.find((r) => r.kind === "header" && r.type === "default");
|
|
711
|
+
const defaultFooter = section.refs.find((r) => r.kind === "footer" && r.type === "default");
|
|
546
712
|
return {
|
|
547
713
|
doc,
|
|
548
714
|
recovered,
|
|
549
715
|
warnings,
|
|
550
|
-
...opts.cssStyles ? { css: extractCss(stylesDoc) } : {}
|
|
716
|
+
...opts.cssStyles ? { css: extractCss(stylesDoc) } : {},
|
|
717
|
+
header: defaultHeader ? partContent(defaultHeader.path, "w:hdr") : null,
|
|
718
|
+
footer: defaultFooter ? partContent(defaultFooter.path, "w:ftr") : null,
|
|
719
|
+
section
|
|
551
720
|
};
|
|
721
|
+
function partContent(path, root) {
|
|
722
|
+
const d = xml(path);
|
|
723
|
+
const el = d?.getElementsByTagName(root)[0];
|
|
724
|
+
if (!el) return null;
|
|
725
|
+
return remapTypes(
|
|
726
|
+
{ type: "doc", content: blocksOf(el) },
|
|
727
|
+
opts.prosemirrorNodes,
|
|
728
|
+
opts.prosemirrorMarks
|
|
729
|
+
);
|
|
730
|
+
}
|
|
731
|
+
function resolveRelative(fromPart, target) {
|
|
732
|
+
const segs = fromPart.split("/").slice(0, -1);
|
|
733
|
+
for (const s of target.split("/")) {
|
|
734
|
+
if (s === "" || s === ".") continue;
|
|
735
|
+
if (s === "..") segs.pop();
|
|
736
|
+
else segs.push(s);
|
|
737
|
+
}
|
|
738
|
+
return segs.join("/");
|
|
739
|
+
}
|
|
740
|
+
function collectPart(path, into) {
|
|
741
|
+
const bytes = entries.read(path);
|
|
742
|
+
if (!bytes) {
|
|
743
|
+
warnings.push(`页眉/页脚部件缺失,已跳过:${path}`);
|
|
744
|
+
return false;
|
|
745
|
+
}
|
|
746
|
+
into[path] = bytes;
|
|
747
|
+
const relsPath = path.replace(/([^/]+)$/, "_rels/$1.rels");
|
|
748
|
+
const relBytes = entries.read(relsPath);
|
|
749
|
+
if (!relBytes) return true;
|
|
750
|
+
into[relsPath] = relBytes;
|
|
751
|
+
const relsDom = new DOMParser().parseFromString(decoder.decode(relBytes), "text/xml");
|
|
752
|
+
for (const r of Array.from(relsDom.getElementsByTagName("Relationship"))) {
|
|
753
|
+
if (r.getAttribute("TargetMode") === "External") continue;
|
|
754
|
+
const target = r.getAttribute("Target");
|
|
755
|
+
if (!target || /^[a-z]+:\/\//i.test(target)) continue;
|
|
756
|
+
const dep = resolveRelative(path, target);
|
|
757
|
+
const b = entries.read(dep);
|
|
758
|
+
if (b) into[dep] = b;
|
|
759
|
+
}
|
|
760
|
+
return true;
|
|
761
|
+
}
|
|
762
|
+
function readSection() {
|
|
763
|
+
const files = {};
|
|
764
|
+
const refs = [];
|
|
765
|
+
const sects = documentXml.getElementsByTagName("w:sectPr");
|
|
766
|
+
const sect = sects[sects.length - 1];
|
|
767
|
+
if (sect) {
|
|
768
|
+
for (const kind of ["header", "footer"]) {
|
|
769
|
+
for (const ref of Array.from(sect.getElementsByTagName(`w:${kind}Reference`))) {
|
|
770
|
+
const rid = ref.getAttribute("r:id");
|
|
771
|
+
const target = rid ? REL[rid] : void 0;
|
|
772
|
+
if (!rid || !target) continue;
|
|
773
|
+
const path = `word/${target.replace(/^\.\//, "")}`;
|
|
774
|
+
if (!collectPart(path, files)) continue;
|
|
775
|
+
refs.push({ kind, type: ref.getAttribute("w:type") ?? "default", rid, path });
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
return {
|
|
780
|
+
sectPrXml: rawSectPr(),
|
|
781
|
+
refs,
|
|
782
|
+
files,
|
|
783
|
+
settingsXml: text("word/settings.xml")
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
function rawSectPr() {
|
|
787
|
+
const src = text("word/document.xml");
|
|
788
|
+
if (!src) return null;
|
|
789
|
+
const all = src.match(/<w:sectPr\b[^>]*(?:\/>|>[\s\S]*?<\/w:sectPr>)/g);
|
|
790
|
+
return all?.length ? all[all.length - 1] : null;
|
|
791
|
+
}
|
|
552
792
|
function extractCss(sd) {
|
|
553
793
|
if (!sd) return "";
|
|
554
794
|
const defRPr = sd.getElementsByTagName("w:rPrDefault")[0]?.getElementsByTagName("w:rPr")[0];
|
|
@@ -635,6 +875,52 @@ function serializeDocx(doc, opts = {}) {
|
|
|
635
875
|
const mediaExts = /* @__PURE__ */ new Set();
|
|
636
876
|
let relSeq = 0;
|
|
637
877
|
const imageCache = /* @__PURE__ */ new Map();
|
|
878
|
+
const carried = opts.section?.files ?? {};
|
|
879
|
+
for (const [p, b] of Object.entries(carried)) files[p] = b;
|
|
880
|
+
const pageTextWidthDxa = (() => {
|
|
881
|
+
const raw = opts.section?.sectPrXml;
|
|
882
|
+
const attr = (tag, name) => {
|
|
883
|
+
const el = raw ? new RegExp(`<w:${tag}\\b[^>]*>`).exec(raw)?.[0] : null;
|
|
884
|
+
const m = el ? new RegExp(`w:${name}="(\\d+)"`).exec(el) : null;
|
|
885
|
+
return m ? Number(m[1]) : null;
|
|
886
|
+
};
|
|
887
|
+
const w = attr("pgSz", "w");
|
|
888
|
+
const l = attr("pgMar", "left");
|
|
889
|
+
const r = attr("pgMar", "right");
|
|
890
|
+
const text = w !== null && l !== null && r !== null ? w - l - r : null;
|
|
891
|
+
return text !== null && text > 0 ? text : 11906 - 1440 - 1440;
|
|
892
|
+
})();
|
|
893
|
+
function columnsToDxa(px) {
|
|
894
|
+
const basis = opts.contentWidthPx;
|
|
895
|
+
const clamp = (arr) => {
|
|
896
|
+
const total = arr.reduce((a, b) => a + b, 0);
|
|
897
|
+
if (total <= pageTextWidthDxa || total <= 0) return arr;
|
|
898
|
+
const k = pageTextWidthDxa / total;
|
|
899
|
+
return arr.map((w) => Math.max(1, Math.round(w * k)));
|
|
900
|
+
};
|
|
901
|
+
if (typeof basis === "number" && Number.isFinite(basis) && basis > 0) {
|
|
902
|
+
const k = pageTextWidthDxa / basis;
|
|
903
|
+
return clamp(px.map((w) => Math.max(1, Math.round(w * k))));
|
|
904
|
+
}
|
|
905
|
+
return clamp(px.map((w) => Math.max(1, Math.round(w * PX_TO_DXA))));
|
|
906
|
+
}
|
|
907
|
+
const ridMap = /* @__PURE__ */ new Map();
|
|
908
|
+
const hdrFtrOverrides = [];
|
|
909
|
+
for (const ref of opts.section?.refs ?? []) {
|
|
910
|
+
if (!files[ref.path]) continue;
|
|
911
|
+
const id = `rHF${ridMap.size + 1}`;
|
|
912
|
+
ridMap.set(ref.rid, id);
|
|
913
|
+
rels.push(
|
|
914
|
+
`<Relationship Id="${id}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/${ref.kind}" Target="${esc(ref.path.replace(/^word\//, ""))}"/>`
|
|
915
|
+
);
|
|
916
|
+
hdrFtrOverrides.push(
|
|
917
|
+
`<Override PartName="/${esc(ref.path)}" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.${ref.kind}+xml"/>`
|
|
918
|
+
);
|
|
919
|
+
}
|
|
920
|
+
for (const p of Object.keys(carried)) {
|
|
921
|
+
const ext = /\.([a-z0-9]+)$/i.exec(p)?.[1]?.toLowerCase();
|
|
922
|
+
if (ext && ext !== "xml" && ext !== "rels") mediaExts.add(ext);
|
|
923
|
+
}
|
|
638
924
|
function addImage(src) {
|
|
639
925
|
if (imageCache.has(src)) return imageCache.get(src) ?? null;
|
|
640
926
|
const bytes = opts.resolveImage?.(src) ?? null;
|
|
@@ -646,7 +932,9 @@ function serializeDocx(doc, opts = {}) {
|
|
|
646
932
|
const ext = sniffExt(bytes);
|
|
647
933
|
mediaExts.add(ext);
|
|
648
934
|
const id = `rImg${++relSeq}`;
|
|
649
|
-
|
|
935
|
+
let n = relSeq;
|
|
936
|
+
while (files[`word/media/image${n}.${ext}`]) n += 1;
|
|
937
|
+
const name = `image${n}.${ext}`;
|
|
650
938
|
files[`word/media/${name}`] = bytes;
|
|
651
939
|
rels.push(
|
|
652
940
|
`<Relationship Id="${id}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/${name}"/>`
|
|
@@ -673,16 +961,25 @@ function serializeDocx(doc, opts = {}) {
|
|
|
673
961
|
function runProps(marks = []) {
|
|
674
962
|
const p = [];
|
|
675
963
|
const has = (t) => marks.some((m) => m.type === t);
|
|
964
|
+
const ts = marks.find((m) => m.type === "textStyle")?.attrs ?? {};
|
|
965
|
+
const fam = cssFontStack(ts.fontFamily);
|
|
966
|
+
if (fam) {
|
|
967
|
+
p.push(
|
|
968
|
+
`<w:rFonts w:ascii="${esc(fam.ascii)}" w:hAnsi="${esc(fam.ascii)}" w:eastAsia="${esc(fam.eastAsia)}" w:cs="${esc(fam.ascii)}"/>`
|
|
969
|
+
);
|
|
970
|
+
}
|
|
676
971
|
if (has("bold")) p.push("<w:b/>");
|
|
677
972
|
if (has("italic")) p.push("<w:i/>");
|
|
678
|
-
if (has("underline")) p.push('<w:u w:val="single"/>');
|
|
679
973
|
if (has("strike")) p.push("<w:strike/>");
|
|
680
|
-
|
|
681
|
-
if (has("subscript")) p.push('<w:vertAlign w:val="subscript"/>');
|
|
682
|
-
const color = marks.find((m) => m.type === "textStyle")?.attrs?.color;
|
|
974
|
+
const color = ts.color;
|
|
683
975
|
if (color) p.push(`<w:color w:val="${String(color).replace("#", "")}"/>`);
|
|
976
|
+
const halfPt = cssToHalfPoints(ts.fontSize);
|
|
977
|
+
if (halfPt) p.push(`<w:sz w:val="${halfPt}"/><w:szCs w:val="${halfPt}"/>`);
|
|
978
|
+
if (has("underline")) p.push('<w:u w:val="single"/>');
|
|
684
979
|
const hl = marks.find((m) => m.type === "highlight")?.attrs?.color;
|
|
685
980
|
if (hl) p.push(`<w:shd w:val="clear" w:fill="${String(hl).replace("#", "")}"/>`);
|
|
981
|
+
if (has("superscript")) p.push('<w:vertAlign w:val="superscript"/>');
|
|
982
|
+
if (has("subscript")) p.push('<w:vertAlign w:val="subscript"/>');
|
|
686
983
|
return p.length ? `<w:rPr>${p.join("")}</w:rPr>` : "";
|
|
687
984
|
}
|
|
688
985
|
function drawing(relId, wPx, hPx, alt, float) {
|
|
@@ -744,21 +1041,35 @@ function serializeDocx(doc, opts = {}) {
|
|
|
744
1041
|
}
|
|
745
1042
|
return out;
|
|
746
1043
|
}
|
|
747
|
-
function paraProps(node,
|
|
748
|
-
const p = [...
|
|
749
|
-
const align = node.attrs?.textAlign ?? node.attrs?.nodeTextAlign;
|
|
750
|
-
if (align && align !== "left") p.push(`<w:jc w:val="${esc(align)}"/>`);
|
|
1044
|
+
function paraProps(node, lead = [], indentTwips) {
|
|
1045
|
+
const p = [...lead];
|
|
751
1046
|
const bg = node.attrs?.backgroundColor;
|
|
752
1047
|
if (bg) p.push(`<w:shd w:val="clear" w:fill="${String(bg).replace("#", "")}"/>`);
|
|
753
|
-
const
|
|
754
|
-
|
|
1048
|
+
const sp = [];
|
|
1049
|
+
const before = cssToTwips(node.attrs?.spaceBefore);
|
|
1050
|
+
if (before !== null) sp.push(`w:before="${before}"`);
|
|
1051
|
+
const after = cssToTwips(node.attrs?.spaceAfter);
|
|
1052
|
+
if (after !== null) sp.push(`w:after="${after}"`);
|
|
1053
|
+
const lh = String(node.attrs?.lineHeight ?? "").trim();
|
|
1054
|
+
if (lh) {
|
|
1055
|
+
const abs = cssToTwips(lh);
|
|
1056
|
+
if (/^[\d.]+$/.test(lh)) sp.push(`w:line="${Math.round(Number(lh) * 240)}" w:lineRule="auto"`);
|
|
1057
|
+
else if (abs !== null) sp.push(`w:line="${abs}" w:lineRule="atLeast"`);
|
|
1058
|
+
}
|
|
1059
|
+
if (sp.length) p.push(`<w:spacing ${sp.join(" ")}/>`);
|
|
1060
|
+
const ind = indentTwips ?? Number(node.attrs?.indent ?? 0) * 420;
|
|
1061
|
+
if (ind > 0) p.push(`<w:ind w:left="${ind}"/>`);
|
|
1062
|
+
const align = node.attrs?.textAlign ?? node.attrs?.nodeTextAlign;
|
|
1063
|
+
if (align && align !== "left") {
|
|
1064
|
+
p.push(`<w:jc w:val="${esc(align === "justify" ? "both" : align)}"/>`);
|
|
1065
|
+
}
|
|
755
1066
|
const ov = opts.paragraphOverrides?.(node);
|
|
756
1067
|
for (const [k, v] of Object.entries(ov ?? {})) p.push(`<w:${k} w:val="${esc(v)}"/>`);
|
|
757
1068
|
return p.length ? `<w:pPr>${p.join("")}</w:pPr>` : "";
|
|
758
1069
|
}
|
|
759
|
-
function paragraph(node,
|
|
1070
|
+
function paragraph(node, lead = [], indentTwips) {
|
|
760
1071
|
stats.paragraphs += 1;
|
|
761
|
-
return `<w:p>${paraProps(node,
|
|
1072
|
+
return `<w:p>${paraProps(node, lead, indentTwips)}${inlineToRuns(node.content)}</w:p>`;
|
|
762
1073
|
}
|
|
763
1074
|
function heading(node) {
|
|
764
1075
|
stats.headings += 1;
|
|
@@ -778,8 +1089,9 @@ function serializeDocx(doc, opts = {}) {
|
|
|
778
1089
|
list(child, level + 1, out);
|
|
779
1090
|
continue;
|
|
780
1091
|
}
|
|
781
|
-
const
|
|
782
|
-
|
|
1092
|
+
const lead = firstDone ? [] : [`<w:numPr><w:ilvl w:val="${level}"/><w:numId w:val="${numId}"/></w:numPr>`];
|
|
1093
|
+
const indent = firstDone ? (level + 1) * 720 : void 0;
|
|
1094
|
+
out.push(`<w:p>${paraProps(child, lead, indent)}${inlineToRuns(child.content)}</w:p>`);
|
|
783
1095
|
firstDone = true;
|
|
784
1096
|
}
|
|
785
1097
|
if (!firstDone) {
|
|
@@ -814,20 +1126,24 @@ function serializeDocx(doc, opts = {}) {
|
|
|
814
1126
|
c += occupied[r][c];
|
|
815
1127
|
}
|
|
816
1128
|
});
|
|
817
|
-
const
|
|
1129
|
+
const px = [];
|
|
818
1130
|
for (const { cell, colSpan } of grid[0] ?? []) {
|
|
819
1131
|
const cw = cell?.attrs?.colwidth ?? [];
|
|
820
|
-
for (let i = 0; i < colSpan; i++)
|
|
1132
|
+
for (let i = 0; i < colSpan; i++) px.push(cw[i] ?? 100);
|
|
821
1133
|
}
|
|
1134
|
+
const widths = columnsToDxa(px);
|
|
822
1135
|
const gridXml = widths.map((w) => `<w:gridCol w:w="${w}"/>`).join("");
|
|
823
1136
|
const borders = "<w:tblBorders>" + ["top", "left", "bottom", "right", "insideH", "insideV"].map((s) => `<w:${s} w:val="single" w:sz="4" w:color="auto"/>`).join("") + "</w:tblBorders>";
|
|
824
|
-
let xml = `<w:tbl><w:tblPr><w:tblW w:type="dxa" w:w="${widths.reduce((a, b) => a + b, 0)}"/>${borders}
|
|
1137
|
+
let xml = `<w:tbl><w:tblPr><w:tblW w:type="dxa" w:w="${widths.reduce((a, b) => a + b, 0)}"/>${borders}<w:tblLayout w:type="fixed"/></w:tblPr><w:tblGrid>${gridXml}</w:tblGrid>`;
|
|
825
1138
|
grid.forEach((cells, r) => {
|
|
826
1139
|
const isHeader = (rows[r].content ?? []).some((c) => c.type === "tableHeader");
|
|
827
1140
|
xml += "<w:tr>";
|
|
828
1141
|
if (isHeader) xml += "<w:trPr><w:tblHeader/></w:trPr>";
|
|
1142
|
+
let col = 0;
|
|
829
1143
|
for (const { cell, colSpan } of cells) {
|
|
830
|
-
const
|
|
1144
|
+
const tcW = widths.slice(col, col + colSpan).reduce((a, b) => a + b, 0);
|
|
1145
|
+
col += colSpan;
|
|
1146
|
+
const props = [`<w:tcW w:type="dxa" w:w="${tcW || 100}"/>`];
|
|
831
1147
|
if (colSpan > 1) props.push(`<w:gridSpan w:val="${colSpan}"/>`);
|
|
832
1148
|
if (!cell) {
|
|
833
1149
|
props.push("<w:vMerge/>");
|
|
@@ -864,7 +1180,7 @@ function serializeDocx(doc, opts = {}) {
|
|
|
864
1180
|
out.push(table(n));
|
|
865
1181
|
break;
|
|
866
1182
|
case "blockquote":
|
|
867
|
-
for (const c of n.content ?? []) out.push(paragraph(c, [
|
|
1183
|
+
for (const c of n.content ?? []) out.push(paragraph(c, [], 720));
|
|
868
1184
|
break;
|
|
869
1185
|
case "codeBlock":
|
|
870
1186
|
out.push(`<w:p><w:pPr><w:pStyle w:val="Code"/></w:pPr><w:r><w:rPr><w:rFonts w:ascii="Consolas" w:hAnsi="Consolas"/></w:rPr><w:t xml:space="preserve">${esc(collectText(n))}</w:t></w:r></w:p>`);
|
|
@@ -885,9 +1201,23 @@ function serializeDocx(doc, opts = {}) {
|
|
|
885
1201
|
}
|
|
886
1202
|
return out.join("");
|
|
887
1203
|
}
|
|
1204
|
+
function sectPr() {
|
|
1205
|
+
const raw = opts.section?.sectPrXml;
|
|
1206
|
+
const refs = (opts.section?.refs ?? []).filter((r) => ridMap.has(r.rid)).map(
|
|
1207
|
+
(r) => `<w:${r.kind}Reference w:type="${esc(r.type)}" r:id="${ridMap.get(r.rid)}"/>`
|
|
1208
|
+
).join("");
|
|
1209
|
+
if (!raw) {
|
|
1210
|
+
const geometry = '<w:pgSz w:w="11906" w:h="16838"/><w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440"/>';
|
|
1211
|
+
return `<w:sectPr>${refs}${geometry}</w:sectPr>`;
|
|
1212
|
+
}
|
|
1213
|
+
const stripped = raw.replace(/<w:(?:header|footer)Reference\b[^>]*\/>/g, "");
|
|
1214
|
+
const selfClosing = /^<w:sectPr\b([^>]*)\/>$/.exec(stripped);
|
|
1215
|
+
if (selfClosing) return `<w:sectPr${selfClosing[1]}>${refs}</w:sectPr>`;
|
|
1216
|
+
return stripped.replace(/^(<w:sectPr\b[^>]*>)/, `$1${refs}`);
|
|
1217
|
+
}
|
|
888
1218
|
const bodyXml = blocks(doc.content ?? []);
|
|
889
1219
|
files["word/document.xml"] = enc.encode(
|
|
890
|
-
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:document ${NS}><w:body>${bodyXml}
|
|
1220
|
+
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:document ${NS}><w:body>${bodyXml}${sectPr()}</w:body></w:document>`
|
|
891
1221
|
);
|
|
892
1222
|
const headingStyles = Array.from({ length: 9 }, (_, i) => {
|
|
893
1223
|
const lv = i + 1;
|
|
@@ -909,6 +1239,12 @@ function serializeDocx(doc, opts = {}) {
|
|
|
909
1239
|
files["word/numbering.xml"] = enc.encode(
|
|
910
1240
|
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:numbering ${NS}>${abstractNums}${nums}</w:numbering>`
|
|
911
1241
|
);
|
|
1242
|
+
files["word/settings.xml"] = enc.encode(
|
|
1243
|
+
opts.section?.settingsXml ?? `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:settings ${NS}><w:compat><w:compatSetting w:name="compatibilityMode" w:uri="http://schemas.microsoft.com/office/word" w:val="15"/></w:compat></w:settings>`
|
|
1244
|
+
);
|
|
1245
|
+
rels.push(
|
|
1246
|
+
`<Relationship Id="rSettings" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings" Target="settings.xml"/>`
|
|
1247
|
+
);
|
|
912
1248
|
files["word/_rels/document.xml.rels"] = enc.encode(
|
|
913
1249
|
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">${rels.join("")}</Relationships>`
|
|
914
1250
|
);
|
|
@@ -917,10 +1253,33 @@ function serializeDocx(doc, opts = {}) {
|
|
|
917
1253
|
);
|
|
918
1254
|
const defaults = ["rels", "xml", ...mediaExts];
|
|
919
1255
|
files["[Content_Types].xml"] = enc.encode(
|
|
920
|
-
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">` + defaults.map((e) => `<Default Extension="${e}" ContentType="${contentTypeOf(e)}"/>`).join("") + `<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/><Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/><Override PartName="/word/numbering.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml"
|
|
1256
|
+
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">` + defaults.map((e) => `<Default Extension="${e}" ContentType="${contentTypeOf(e)}"/>`).join("") + `<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/><Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/><Override PartName="/word/numbering.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml"/><Override PartName="/word/settings.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml"/>` + hdrFtrOverrides.join("") + `</Types>`
|
|
921
1257
|
);
|
|
922
1258
|
return { files, warnings, stats };
|
|
923
1259
|
}
|
|
1260
|
+
function cssToPoints(v) {
|
|
1261
|
+
const m = /^\s*(-?[\d.]+)\s*(pt|px|in|cm|mm)\s*$/.exec(String(v ?? ""));
|
|
1262
|
+
if (!m) return null;
|
|
1263
|
+
const n = Number(m[1]);
|
|
1264
|
+
if (!Number.isFinite(n)) return null;
|
|
1265
|
+
const perUnit = { pt: 1, px: 0.75, in: 72, cm: 72 / 2.54, mm: 7.2 / 2.54 };
|
|
1266
|
+
return n * perUnit[m[2]];
|
|
1267
|
+
}
|
|
1268
|
+
function cssToTwips(v) {
|
|
1269
|
+
const pt = cssToPoints(v);
|
|
1270
|
+
return pt === null ? null : Math.round(pt * 20);
|
|
1271
|
+
}
|
|
1272
|
+
function cssToHalfPoints(v) {
|
|
1273
|
+
const pt = cssToPoints(v);
|
|
1274
|
+
return pt === null || pt <= 0 ? null : Math.round(pt * 2);
|
|
1275
|
+
}
|
|
1276
|
+
function cssFontStack(v) {
|
|
1277
|
+
const raw = String(v ?? "").trim();
|
|
1278
|
+
if (!raw) return null;
|
|
1279
|
+
const parts = raw.split(",").map((s) => s.trim().replace(/^["']|["']$/g, "")).filter(Boolean);
|
|
1280
|
+
if (!parts.length) return null;
|
|
1281
|
+
return { ascii: parts[0], eastAsia: parts[parts.length - 1] };
|
|
1282
|
+
}
|
|
924
1283
|
function collectText(n) {
|
|
925
1284
|
if (n?.type === "text") return n.text ?? "";
|
|
926
1285
|
return (n?.content ?? []).map(collectText).join("");
|
|
@@ -968,18 +1327,30 @@ var ExportDocx = Extension.create({
|
|
|
968
1327
|
verbose: 0,
|
|
969
1328
|
resolveImage: void 0,
|
|
970
1329
|
styleOverrides: void 0,
|
|
971
|
-
paragraphOverrides: void 0
|
|
1330
|
+
paragraphOverrides: void 0,
|
|
1331
|
+
section: void 0,
|
|
1332
|
+
contentWidthPx: void 0
|
|
972
1333
|
};
|
|
973
1334
|
},
|
|
974
1335
|
addCommands() {
|
|
975
1336
|
return {
|
|
976
1337
|
exportDocx: (override = {}) => ({ editor }) => {
|
|
977
1338
|
const o = { ...this.options, ...override };
|
|
1339
|
+
if (o.section === void 0) {
|
|
1340
|
+
o.section = editor.storage.importDocx?.section ?? void 0;
|
|
1341
|
+
}
|
|
1342
|
+
if (o.contentWidthPx === void 0) {
|
|
1343
|
+
o.contentWidthPx = measureContentWidth(editor.view?.dom);
|
|
1344
|
+
}
|
|
978
1345
|
try {
|
|
979
1346
|
const { bytes, warnings, stats } = docxFromJSON(editor.getJSON(), o);
|
|
980
1347
|
if (o.verbose >= 1) console.info("[exportDocx] 统计", stats);
|
|
981
1348
|
if (o.verbose >= 2) for (const w of warnings) console.warn("[exportDocx]", w);
|
|
982
|
-
o.onCompleteExport?.(deliver(bytes, o.exportType)
|
|
1349
|
+
o.onCompleteExport?.(deliver(bytes, o.exportType), {
|
|
1350
|
+
stats,
|
|
1351
|
+
warnings,
|
|
1352
|
+
bytes: bytes.length
|
|
1353
|
+
});
|
|
983
1354
|
} catch (e) {
|
|
984
1355
|
if (o.verbose >= 1) console.error("[exportDocx] 失败", e);
|
|
985
1356
|
return false;
|
|
@@ -1091,12 +1462,16 @@ var ImportDocx = Extension.create({
|
|
|
1091
1462
|
cssStyles: false,
|
|
1092
1463
|
verbose: 0,
|
|
1093
1464
|
preserveUnsupported: true,
|
|
1094
|
-
injectStyles: true
|
|
1465
|
+
injectStyles: true,
|
|
1466
|
+
tableWidthBasis: "auto"
|
|
1095
1467
|
};
|
|
1096
1468
|
},
|
|
1097
1469
|
addExtensions() {
|
|
1098
1470
|
return this.options.preserveUnsupported === false ? [] : [DocxInk, DocxField];
|
|
1099
1471
|
},
|
|
1472
|
+
addStorage() {
|
|
1473
|
+
return { section: null };
|
|
1474
|
+
},
|
|
1100
1475
|
onCreate() {
|
|
1101
1476
|
if (this.options.injectStyles) injectStyles();
|
|
1102
1477
|
},
|
|
@@ -1118,6 +1493,7 @@ var ImportDocx = Extension.create({
|
|
|
1118
1493
|
warnings: [],
|
|
1119
1494
|
header: null,
|
|
1120
1495
|
footer: null,
|
|
1496
|
+
section: { sectPrXml: null, refs: [], files: {}, settingsXml: null },
|
|
1121
1497
|
footnotes: {},
|
|
1122
1498
|
endnotes: {}
|
|
1123
1499
|
});
|
|
@@ -1128,6 +1504,7 @@ var ImportDocx = Extension.create({
|
|
|
1128
1504
|
if (!opts.resolveMedia && opts.imageUploadConfig) {
|
|
1129
1505
|
uploaded = await uploadImages(files, opts.imageUploadConfig);
|
|
1130
1506
|
}
|
|
1507
|
+
const contentWidthPx = opts.tableWidthBasis === "auto" ? measureContentWidth(editor.view?.dom) : opts.tableWidthBasis ?? void 0;
|
|
1131
1508
|
const result = parseDocx(
|
|
1132
1509
|
{ read: (p) => files[p] ?? null },
|
|
1133
1510
|
{
|
|
@@ -1135,7 +1512,9 @@ var ImportDocx = Extension.create({
|
|
|
1135
1512
|
resolveMedia: opts.resolveMedia ?? (uploaded ? (_b, entry) => uploaded.get(entry) ?? "" : void 0),
|
|
1136
1513
|
prosemirrorNodes: opts.prosemirrorNodes ?? void 0,
|
|
1137
1514
|
prosemirrorMarks: opts.prosemirrorMarks ?? void 0,
|
|
1138
|
-
cssStyles: opts.cssStyles
|
|
1515
|
+
cssStyles: opts.cssStyles,
|
|
1516
|
+
contentWidthPx,
|
|
1517
|
+
minColumnWidthPx: tableCellMinWidth(editor)
|
|
1139
1518
|
}
|
|
1140
1519
|
);
|
|
1141
1520
|
const warned = /* @__PURE__ */ new Set();
|
|
@@ -1153,14 +1532,16 @@ var ImportDocx = Extension.create({
|
|
|
1153
1532
|
if (opts.verbose >= 2) {
|
|
1154
1533
|
for (const w of result.warnings) console.warn("[importDocx]", w);
|
|
1155
1534
|
}
|
|
1535
|
+
this.storage.section = result.section;
|
|
1156
1536
|
finish({
|
|
1157
1537
|
content: result.doc,
|
|
1158
1538
|
setEditorContent: (c) => editor.commands.setContent(c ?? result.doc, { emitUpdate: true }),
|
|
1159
1539
|
recovered: result.recovered,
|
|
1160
1540
|
warnings: result.warnings,
|
|
1161
1541
|
css: result.css,
|
|
1162
|
-
header:
|
|
1163
|
-
footer:
|
|
1542
|
+
header: result.header,
|
|
1543
|
+
footer: result.footer,
|
|
1544
|
+
section: result.section,
|
|
1164
1545
|
footnotes: {},
|
|
1165
1546
|
endnotes: {}
|
|
1166
1547
|
});
|