@coding01/docsjs 0.1.3 → 0.1.6
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 +77 -21
- package/README.zh-CN.md +74 -20
- package/dist/{chunk-PRPDJOB7.js → chunk-632UOG2B.js} +448 -102
- package/dist/chunk-632UOG2B.js.map +1 -0
- package/dist/index.cjs +452 -103
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +11 -4
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +449 -102
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +12 -3
- package/dist/react.js.map +1 -1
- package/dist/types-VvdwVF0_.d.cts +44 -0
- package/dist/types-VvdwVF0_.d.ts +44 -0
- package/dist/vue.cjs +438 -100
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.d.cts +1 -1
- package/dist/vue.d.ts +1 -1
- package/dist/vue.js +1 -1
- package/package.json +3 -1
- package/dist/chunk-PRPDJOB7.js.map +0 -1
- package/dist/types-DF14w1ol.d.cts +0 -20
- package/dist/types-DF14w1ol.d.ts +0 -20
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// src/lib/docxHtml.ts
|
|
2
|
+
import JSZip from "jszip";
|
|
3
|
+
|
|
1
4
|
// src/lib/htmlSnapshot.ts
|
|
2
5
|
var SNAPSHOT_SHELL_START = '<!DOCTYPE html><html><head><meta charset="utf-8"/>';
|
|
3
6
|
var SNAPSHOT_SHELL_END = "</head><body></body></html>";
|
|
@@ -16,7 +19,21 @@ function buildHtmlSnapshot(rawHtml) {
|
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
// src/lib/docxHtml.ts
|
|
19
|
-
|
|
22
|
+
function createEmptyFeatureCounts() {
|
|
23
|
+
return {
|
|
24
|
+
hyperlinkCount: 0,
|
|
25
|
+
anchorImageCount: 0,
|
|
26
|
+
chartCount: 0,
|
|
27
|
+
smartArtCount: 0,
|
|
28
|
+
ommlCount: 0,
|
|
29
|
+
tableCount: 0,
|
|
30
|
+
footnoteRefCount: 0,
|
|
31
|
+
endnoteRefCount: 0,
|
|
32
|
+
commentRefCount: 0,
|
|
33
|
+
revisionCount: 0,
|
|
34
|
+
pageBreakCount: 0
|
|
35
|
+
};
|
|
36
|
+
}
|
|
20
37
|
function escapeHtml(text) {
|
|
21
38
|
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
22
39
|
}
|
|
@@ -40,6 +57,9 @@ function getAttr(node, name) {
|
|
|
40
57
|
function emuToPx(emu) {
|
|
41
58
|
return emu * 96 / 914400;
|
|
42
59
|
}
|
|
60
|
+
function twipToPx(twip) {
|
|
61
|
+
return twip * 96 / 1440;
|
|
62
|
+
}
|
|
43
63
|
function parseDrawingSizePx(drawing) {
|
|
44
64
|
const extentNode = queryAllByLocalName(drawing, "extent").find((node) => {
|
|
45
65
|
const parent = node.parentElement;
|
|
@@ -68,9 +88,7 @@ function imageDimensionAttributes(sizePx) {
|
|
|
68
88
|
}
|
|
69
89
|
return attrs.length > 0 ? ` ${attrs.join(" ")}` : "";
|
|
70
90
|
}
|
|
71
|
-
function parseAnchorPositionPx(
|
|
72
|
-
const anchor = directChildrenByLocalName(drawing, "anchor")[0] ?? null;
|
|
73
|
-
if (!anchor) return { leftPx: null, topPx: null };
|
|
91
|
+
function parseAnchorPositionPx(anchor) {
|
|
74
92
|
let leftPx = null;
|
|
75
93
|
let topPx = null;
|
|
76
94
|
const positionH = directChildrenByLocalName(anchor, "positionH")[0] ?? null;
|
|
@@ -85,34 +103,80 @@ function parseAnchorPositionPx(drawing) {
|
|
|
85
103
|
if (Number.isFinite(top)) topPx = emuToPx(top);
|
|
86
104
|
return { leftPx, topPx };
|
|
87
105
|
}
|
|
88
|
-
function parseAnchorWrapMode(
|
|
89
|
-
const anchor = directChildrenByLocalName(drawing, "anchor")[0] ?? null;
|
|
90
|
-
if (!anchor) return null;
|
|
106
|
+
function parseAnchorWrapMode(anchor) {
|
|
91
107
|
if (directChildrenByLocalName(anchor, "wrapSquare")[0]) return "square";
|
|
92
108
|
if (directChildrenByLocalName(anchor, "wrapTight")[0]) return "tight";
|
|
93
109
|
if (directChildrenByLocalName(anchor, "wrapTopAndBottom")[0]) return "topAndBottom";
|
|
94
110
|
if (directChildrenByLocalName(anchor, "wrapNone")[0]) return "none";
|
|
95
111
|
return null;
|
|
96
112
|
}
|
|
97
|
-
function
|
|
98
|
-
|
|
113
|
+
function parseAnchorMeta(drawing) {
|
|
114
|
+
const anchor = directChildrenByLocalName(drawing, "anchor")[0] ?? null;
|
|
115
|
+
if (!anchor) return null;
|
|
116
|
+
const positionH = directChildrenByLocalName(anchor, "positionH")[0] ?? null;
|
|
117
|
+
const positionV = directChildrenByLocalName(anchor, "positionV")[0] ?? null;
|
|
118
|
+
const relativeFromH = getAttr(positionH, "relativeFrom");
|
|
119
|
+
const relativeFromV = getAttr(positionV, "relativeFrom");
|
|
120
|
+
const parseDistPx = (name) => {
|
|
121
|
+
const raw = getAttr(anchor, name);
|
|
122
|
+
const emu = raw ? Number.parseInt(raw, 10) : Number.NaN;
|
|
123
|
+
return Number.isFinite(emu) && emu >= 0 ? emuToPx(emu) : null;
|
|
124
|
+
};
|
|
125
|
+
const rawHeight = getAttr(anchor, "relativeHeight");
|
|
126
|
+
const parsedHeight = rawHeight ? Number.parseInt(rawHeight, 10) : Number.NaN;
|
|
127
|
+
const boolAttr = (name, fallback) => {
|
|
128
|
+
const raw = (getAttr(anchor, name) ?? "").toLowerCase();
|
|
129
|
+
if (raw === "1" || raw === "true" || raw === "on") return true;
|
|
130
|
+
if (raw === "0" || raw === "false" || raw === "off") return false;
|
|
131
|
+
return fallback;
|
|
132
|
+
};
|
|
133
|
+
return {
|
|
134
|
+
position: parseAnchorPositionPx(anchor),
|
|
135
|
+
wrapMode: parseAnchorWrapMode(anchor),
|
|
136
|
+
distTPx: parseDistPx("distT"),
|
|
137
|
+
distBPx: parseDistPx("distB"),
|
|
138
|
+
distLPx: parseDistPx("distL"),
|
|
139
|
+
distRPx: parseDistPx("distR"),
|
|
140
|
+
relativeFromH,
|
|
141
|
+
relativeFromV,
|
|
142
|
+
behindDoc: boolAttr("behindDoc", false),
|
|
143
|
+
allowOverlap: boolAttr("allowOverlap", true),
|
|
144
|
+
layoutInCell: boolAttr("layoutInCell", true),
|
|
145
|
+
relativeHeight: Number.isFinite(parsedHeight) ? parsedHeight : null
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function mergeImageStyle(baseAttrs, anchorMeta) {
|
|
149
|
+
if (!anchorMeta) return baseAttrs;
|
|
150
|
+
const { position, wrapMode } = anchorMeta;
|
|
151
|
+
if (position.leftPx === null && position.topPx === null) return baseAttrs;
|
|
99
152
|
const styleParts = [
|
|
100
153
|
"position:absolute",
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
154
|
+
position.leftPx !== null ? `left:${position.leftPx.toFixed(2)}px` : "",
|
|
155
|
+
position.topPx !== null ? `top:${position.topPx.toFixed(2)}px` : "",
|
|
156
|
+
`z-index:${anchorMeta.behindDoc ? 0 : anchorMeta.relativeHeight ?? 3}`,
|
|
157
|
+
anchorMeta.distTPx !== null ? `margin-top:${anchorMeta.distTPx.toFixed(2)}px` : "",
|
|
158
|
+
anchorMeta.distBPx !== null ? `margin-bottom:${anchorMeta.distBPx.toFixed(2)}px` : "",
|
|
159
|
+
anchorMeta.distLPx !== null ? `margin-left:${anchorMeta.distLPx.toFixed(2)}px` : "",
|
|
160
|
+
anchorMeta.distRPx !== null ? `margin-right:${anchorMeta.distRPx.toFixed(2)}px` : ""
|
|
104
161
|
].filter((x) => x.length > 0);
|
|
105
162
|
if (wrapMode === "topAndBottom") {
|
|
106
|
-
styleParts.push("display:block");
|
|
107
|
-
}
|
|
163
|
+
styleParts.push("display:block", "clear:both");
|
|
164
|
+
}
|
|
165
|
+
const anchorAttrs = [
|
|
166
|
+
`data-word-anchor="1"`,
|
|
167
|
+
wrapMode ? `data-word-wrap="${wrapMode}"` : "",
|
|
168
|
+
anchorMeta.relativeFromH ? `data-word-anchor-relh="${escapeHtml(anchorMeta.relativeFromH)}"` : "",
|
|
169
|
+
anchorMeta.relativeFromV ? `data-word-anchor-relv="${escapeHtml(anchorMeta.relativeFromV)}"` : "",
|
|
170
|
+
anchorMeta.behindDoc ? `data-word-anchor-behind="1"` : `data-word-anchor-behind="0"`,
|
|
171
|
+
anchorMeta.allowOverlap ? `data-word-anchor-overlap="1"` : `data-word-anchor-overlap="0"`,
|
|
172
|
+
anchorMeta.layoutInCell ? `data-word-anchor-layout-cell="1"` : `data-word-anchor-layout-cell="0"`
|
|
173
|
+
].filter((x) => x.length > 0).join(" ");
|
|
108
174
|
if (!baseAttrs.includes("style=")) {
|
|
109
|
-
|
|
110
|
-
return `${baseAttrs} style="${styleParts.join(";")}" data-word-anchor="1"${wrapAttr}`;
|
|
175
|
+
return `${baseAttrs} style="${styleParts.join(";")}" ${anchorAttrs}`;
|
|
111
176
|
}
|
|
112
177
|
return baseAttrs.replace(/style="([^"]*)"/, (_m, styleText) => {
|
|
113
178
|
const merged = [styleText, ...styleParts].filter((x) => x.length > 0).join(";");
|
|
114
|
-
|
|
115
|
-
return `style="${merged}" data-word-anchor="1"${wrapAttr}`;
|
|
179
|
+
return `style="${merged}" ${anchorAttrs}`;
|
|
116
180
|
});
|
|
117
181
|
}
|
|
118
182
|
function parseDocRelsMap(relsXmlText) {
|
|
@@ -138,11 +202,29 @@ function extToMime(ext) {
|
|
|
138
202
|
if (lower === "svg") return "image/svg+xml";
|
|
139
203
|
return "application/octet-stream";
|
|
140
204
|
}
|
|
205
|
+
function normalizeWordPath(relTarget) {
|
|
206
|
+
const normalized = relTarget.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
207
|
+
if (normalized.startsWith("word/")) return normalized;
|
|
208
|
+
if (normalized.startsWith("../")) return `word/${normalized.replace(/^(\.\.\/)+/, "")}`;
|
|
209
|
+
return `word/${normalized}`;
|
|
210
|
+
}
|
|
211
|
+
function resolveHyperlinkHref(relMap, rid, anchor) {
|
|
212
|
+
if (anchor && anchor.trim()) return `#${encodeURIComponent(anchor.trim())}`;
|
|
213
|
+
if (!rid) return null;
|
|
214
|
+
const relTarget = relMap[rid];
|
|
215
|
+
if (!relTarget) return null;
|
|
216
|
+
const trimmed = relTarget.trim();
|
|
217
|
+
if (!trimmed) return null;
|
|
218
|
+
const lower = trimmed.toLowerCase();
|
|
219
|
+
if (lower.startsWith("http://") || lower.startsWith("https://") || lower.startsWith("mailto:") || lower.startsWith("tel:")) {
|
|
220
|
+
return trimmed;
|
|
221
|
+
}
|
|
222
|
+
return trimmed.startsWith("#") ? trimmed : `#${encodeURIComponent(trimmed)}`;
|
|
223
|
+
}
|
|
141
224
|
async function imageRidToDataUrl(zip, relMap, rid) {
|
|
142
225
|
const relTarget = relMap[rid];
|
|
143
226
|
if (!relTarget) return null;
|
|
144
|
-
const
|
|
145
|
-
const path = normalized.startsWith("word/") ? normalized : `word/${normalized}`;
|
|
227
|
+
const path = normalizeWordPath(relTarget);
|
|
146
228
|
const file = zip.file(path);
|
|
147
229
|
if (!file) return null;
|
|
148
230
|
const base64 = await file.async("base64");
|
|
@@ -150,6 +232,55 @@ async function imageRidToDataUrl(zip, relMap, rid) {
|
|
|
150
232
|
const mime = extToMime(ext);
|
|
151
233
|
return `data:${mime};base64,${base64}`;
|
|
152
234
|
}
|
|
235
|
+
async function readXmlByRid(zip, relMap, rid) {
|
|
236
|
+
const relTarget = relMap[rid];
|
|
237
|
+
if (!relTarget) return null;
|
|
238
|
+
const path = normalizeWordPath(relTarget);
|
|
239
|
+
const file = zip.file(path);
|
|
240
|
+
return file ? file.async("string") : null;
|
|
241
|
+
}
|
|
242
|
+
function parseChartType(chartDoc) {
|
|
243
|
+
const known = ["barChart", "lineChart", "pieChart", "areaChart", "scatterChart", "radarChart", "doughnutChart"];
|
|
244
|
+
for (const type of known) {
|
|
245
|
+
if (queryByLocalName(chartDoc, type)) return type.replace(/Chart$/, "");
|
|
246
|
+
}
|
|
247
|
+
return "unknown";
|
|
248
|
+
}
|
|
249
|
+
function parseChartSummary(chartXmlText) {
|
|
250
|
+
const chartDoc = parseXml(chartXmlText);
|
|
251
|
+
const title = queryAllByLocalName(chartDoc, "t").map((n) => (n.textContent ?? "").trim()).find((v) => v.length > 0) ?? "Chart";
|
|
252
|
+
const seriesCount = queryAllByLocalName(chartDoc, "ser").length;
|
|
253
|
+
const pointCount = queryAllByLocalName(chartDoc, "pt").length;
|
|
254
|
+
const type = parseChartType(chartDoc);
|
|
255
|
+
return { title, type, seriesCount, pointCount };
|
|
256
|
+
}
|
|
257
|
+
function extractSmartArtText(diagramXmlText) {
|
|
258
|
+
const diagramDoc = parseXml(diagramXmlText);
|
|
259
|
+
return queryAllByLocalName(diagramDoc, "t").map((n) => (n.textContent ?? "").trim()).filter((v) => v.length > 0).slice(0, 12);
|
|
260
|
+
}
|
|
261
|
+
function ommlNodeToText(node) {
|
|
262
|
+
if (node.localName === "t") return node.textContent ?? "";
|
|
263
|
+
if (node.localName === "f") {
|
|
264
|
+
const num = queryByLocalName(node, "num");
|
|
265
|
+
const den = queryByLocalName(node, "den");
|
|
266
|
+
return `(${num ? ommlNodeToText(num) : "?"})/(${den ? ommlNodeToText(den) : "?"})`;
|
|
267
|
+
}
|
|
268
|
+
if (node.localName === "sSup") {
|
|
269
|
+
const e = queryByLocalName(node, "e");
|
|
270
|
+
const sup = queryByLocalName(node, "sup");
|
|
271
|
+
return `${e ? ommlNodeToText(e) : ""}^(${sup ? ommlNodeToText(sup) : ""})`;
|
|
272
|
+
}
|
|
273
|
+
if (node.localName === "sSub") {
|
|
274
|
+
const e = queryByLocalName(node, "e");
|
|
275
|
+
const sub = queryByLocalName(node, "sub");
|
|
276
|
+
return `${e ? ommlNodeToText(e) : ""}_(${sub ? ommlNodeToText(sub) : ""})`;
|
|
277
|
+
}
|
|
278
|
+
if (node.localName === "rad") {
|
|
279
|
+
const e = queryByLocalName(node, "e");
|
|
280
|
+
return `sqrt(${e ? ommlNodeToText(e) : ""})`;
|
|
281
|
+
}
|
|
282
|
+
return Array.from(node.children).map((child) => ommlNodeToText(child)).join("");
|
|
283
|
+
}
|
|
153
284
|
function runStyleToCss(rPr) {
|
|
154
285
|
if (!rPr) return "";
|
|
155
286
|
const declarations = [];
|
|
@@ -264,48 +395,73 @@ function renderEndnotesSection(usedIds, endnotesMap) {
|
|
|
264
395
|
const items = uniq.map((id) => `<li id="word-endnote-${id}" data-word-endnote-id="${id}">${endnotesMap[id]}</li>`).join("");
|
|
265
396
|
return `<section data-word-endnotes="1"><hr/><ol>${items}</ol></section>`;
|
|
266
397
|
}
|
|
267
|
-
async function paragraphToHtml(zip, relMap, paragraph, paragraphIndex, footnotesMap, usedFootnoteIds, endnotesMap, usedEndnoteIds, commentsMap, usedCommentIds) {
|
|
398
|
+
async function paragraphToHtml(zip, relMap, context, paragraph, paragraphIndex, footnotesMap, usedFootnoteIds, endnotesMap, usedEndnoteIds, commentsMap, usedCommentIds) {
|
|
268
399
|
const tag = paragraphTag(paragraph);
|
|
269
400
|
const alignStyle = paragraphAlignStyle(paragraph);
|
|
270
401
|
const dataAttr = paragraphDataAttr(paragraphIndex);
|
|
271
|
-
const
|
|
272
|
-
if (
|
|
402
|
+
const hasRenderableNode = queryAllByLocalName(paragraph, "r").length > 0 || queryAllByLocalName(paragraph, "oMath").length > 0 || queryAllByLocalName(paragraph, "oMathPara").length > 0;
|
|
403
|
+
if (!hasRenderableNode) {
|
|
273
404
|
return `<${tag}${dataAttr}${alignStyle ? ` style="${alignStyle}"` : ""}><br/></${tag}>`;
|
|
274
405
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
406
|
+
function parseRevisionMeta(node, type) {
|
|
407
|
+
return {
|
|
408
|
+
type,
|
|
409
|
+
id: getAttr(node, "w:id") ?? getAttr(node, "id"),
|
|
410
|
+
author: getAttr(node, "w:author") ?? getAttr(node, "author"),
|
|
411
|
+
date: getAttr(node, "w:date") ?? getAttr(node, "date")
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
function inferRevisionMeta(run, fallback) {
|
|
415
|
+
if (fallback) return fallback;
|
|
416
|
+
let cursor = run;
|
|
417
|
+
while (cursor) {
|
|
418
|
+
if (cursor.localName === "ins") return parseRevisionMeta(cursor, "ins");
|
|
419
|
+
if (cursor.localName === "del") return parseRevisionMeta(cursor, "del");
|
|
420
|
+
if (cursor.localName === "p") break;
|
|
421
|
+
cursor = cursor.parentElement;
|
|
422
|
+
}
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
function revisionMetaAttrs(meta) {
|
|
426
|
+
const attrs = [`data-word-revision="${meta.type}"`];
|
|
427
|
+
if (meta.id) attrs.push(`data-word-revision-id="${escapeHtml(meta.id)}"`);
|
|
428
|
+
if (meta.author) attrs.push(`data-word-revision-author="${escapeHtml(meta.author)}"`);
|
|
429
|
+
if (meta.date) attrs.push(`data-word-revision-date="${escapeHtml(meta.date)}"`);
|
|
430
|
+
return attrs.join(" ");
|
|
279
431
|
}
|
|
280
|
-
|
|
432
|
+
async function runToHtml(run, revisionFallback) {
|
|
433
|
+
const result = [];
|
|
281
434
|
const rPr = queryByLocalName(run, "rPr");
|
|
282
435
|
const css = runStyleToCss(rPr);
|
|
283
436
|
const footnoteRef = queryByLocalName(run, "footnoteReference");
|
|
284
437
|
const footnoteId = getAttr(footnoteRef, "w:id") ?? getAttr(footnoteRef, "id");
|
|
285
438
|
if (footnoteId && footnotesMap[footnoteId]) {
|
|
439
|
+
context.features.footnoteRefCount += 1;
|
|
286
440
|
usedFootnoteIds.push(footnoteId);
|
|
287
|
-
|
|
441
|
+
result.push(
|
|
288
442
|
`<sup data-word-footnote-ref="${footnoteId}"><a href="#word-footnote-${footnoteId}">[${footnoteId}]</a></sup>`
|
|
289
443
|
);
|
|
290
|
-
|
|
444
|
+
return result;
|
|
291
445
|
}
|
|
292
446
|
const endnoteRef = queryByLocalName(run, "endnoteReference");
|
|
293
447
|
const endnoteId = getAttr(endnoteRef, "w:id") ?? getAttr(endnoteRef, "id");
|
|
294
448
|
if (endnoteId && endnotesMap[endnoteId]) {
|
|
449
|
+
context.features.endnoteRefCount += 1;
|
|
295
450
|
usedEndnoteIds.push(endnoteId);
|
|
296
|
-
|
|
451
|
+
result.push(
|
|
297
452
|
`<sup data-word-endnote-ref="${endnoteId}"><a href="#word-endnote-${endnoteId}">[${endnoteId}]</a></sup>`
|
|
298
453
|
);
|
|
299
|
-
|
|
454
|
+
return result;
|
|
300
455
|
}
|
|
301
456
|
const commentRef = queryByLocalName(run, "commentReference");
|
|
302
457
|
const commentId = getAttr(commentRef, "w:id") ?? getAttr(commentRef, "id");
|
|
303
458
|
if (commentId && commentsMap[commentId]) {
|
|
459
|
+
context.features.commentRefCount += 1;
|
|
304
460
|
usedCommentIds.push(commentId);
|
|
305
|
-
|
|
461
|
+
result.push(
|
|
306
462
|
`<sup data-word-comment-ref="${commentId}"><a href="#word-comment-${commentId}">[c${commentId}]</a></sup>`
|
|
307
463
|
);
|
|
308
|
-
|
|
464
|
+
return result;
|
|
309
465
|
}
|
|
310
466
|
const drawing = queryByLocalName(run, "drawing");
|
|
311
467
|
if (drawing) {
|
|
@@ -316,13 +472,38 @@ async function paragraphToHtml(zip, relMap, paragraph, paragraphIndex, footnotes
|
|
|
316
472
|
if (src) {
|
|
317
473
|
const imageSize = parseDrawingSizePx(drawing);
|
|
318
474
|
const dimensionAttrs = imageDimensionAttributes(imageSize);
|
|
319
|
-
const
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
475
|
+
const anchorMeta = parseAnchorMeta(drawing);
|
|
476
|
+
const attrs = mergeImageStyle(dimensionAttrs, anchorMeta);
|
|
477
|
+
if (anchorMeta) context.features.anchorImageCount += 1;
|
|
478
|
+
result.push(`<img src="${src}" alt="word-image"${attrs}/>`);
|
|
479
|
+
return result;
|
|
324
480
|
}
|
|
325
481
|
}
|
|
482
|
+
const chartRef = queryByLocalName(drawing, "chart");
|
|
483
|
+
const chartRid = getAttr(chartRef, "r:id") ?? getAttr(chartRef, "id");
|
|
484
|
+
if (chartRid) {
|
|
485
|
+
const chartXmlText = await readXmlByRid(zip, relMap, chartRid);
|
|
486
|
+
if (chartXmlText) {
|
|
487
|
+
const summary = parseChartSummary(chartXmlText);
|
|
488
|
+
context.features.chartCount += 1;
|
|
489
|
+
result.push(
|
|
490
|
+
`<figure data-word-chart="1" data-word-chart-type="${summary.type}" data-word-chart-series="${summary.seriesCount}" data-word-chart-points="${summary.pointCount}"><figcaption>${escapeHtml(summary.title)}</figcaption><div>Chart(${escapeHtml(summary.type)}): series=${summary.seriesCount}, points=${summary.pointCount}</div></figure>`
|
|
491
|
+
);
|
|
492
|
+
return result;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
const smartArtRef = queryByLocalName(drawing, "relIds");
|
|
496
|
+
const smartArtRid = getAttr(smartArtRef, "r:dm") ?? getAttr(smartArtRef, "dm");
|
|
497
|
+
if (smartArtRid) {
|
|
498
|
+
const diagramXmlText = await readXmlByRid(zip, relMap, smartArtRid);
|
|
499
|
+
const textItems = diagramXmlText ? extractSmartArtText(diagramXmlText) : [];
|
|
500
|
+
context.features.smartArtCount += 1;
|
|
501
|
+
const preview = textItems.length > 0 ? `: ${escapeHtml(textItems.join(" / "))}` : "";
|
|
502
|
+
result.push(
|
|
503
|
+
`<figure data-word-smartart="1" data-word-smartart-items="${textItems.length}"><figcaption>SmartArt fallback${preview}</figcaption></figure>`
|
|
504
|
+
);
|
|
505
|
+
return result;
|
|
506
|
+
}
|
|
326
507
|
}
|
|
327
508
|
const texts = queryAllByLocalName(run, "t").map((t) => t.textContent ?? "").join("");
|
|
328
509
|
const delTexts = queryAllByLocalName(run, "delText").map((t) => t.textContent ?? "").join("");
|
|
@@ -333,40 +514,86 @@ async function paragraphToHtml(zip, relMap, paragraph, paragraphIndex, footnotes
|
|
|
333
514
|
}).length;
|
|
334
515
|
const lineBreakCount = Math.max(0, brNodes.length - pageBreakCount);
|
|
335
516
|
const runText2 = `${escapeHtml(texts || delTexts)}${"<br/>".repeat(lineBreakCount)}`;
|
|
336
|
-
if (
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
517
|
+
if (runText2) {
|
|
518
|
+
const revisionMeta = inferRevisionMeta(run, revisionFallback);
|
|
519
|
+
if (css) {
|
|
520
|
+
const span = `<span style="${css}">${runText2}</span>`;
|
|
521
|
+
if (revisionMeta) {
|
|
522
|
+
context.features.revisionCount += 1;
|
|
523
|
+
const tagName = revisionMeta.type === "ins" ? "ins" : "del";
|
|
524
|
+
result.push(`<${tagName} ${revisionMetaAttrs(revisionMeta)}>${span}</${tagName}>`);
|
|
525
|
+
} else {
|
|
526
|
+
result.push(span);
|
|
527
|
+
}
|
|
528
|
+
} else if (revisionMeta) {
|
|
529
|
+
context.features.revisionCount += 1;
|
|
530
|
+
const tagName = revisionMeta.type === "ins" ? "ins" : "del";
|
|
531
|
+
result.push(`<${tagName} ${revisionMetaAttrs(revisionMeta)}>${runText2}</${tagName}>`);
|
|
532
|
+
} else {
|
|
533
|
+
result.push(runText2);
|
|
347
534
|
}
|
|
348
|
-
if (cursor.localName === "p") break;
|
|
349
|
-
cursor = cursor.parentElement;
|
|
350
535
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
536
|
+
for (let i = 0; i < pageBreakCount; i += 1) {
|
|
537
|
+
context.features.pageBreakCount += 1;
|
|
538
|
+
result.push(`<span data-word-page-break="1" style="display:block;break-before:page"></span>`);
|
|
539
|
+
}
|
|
540
|
+
return result;
|
|
541
|
+
}
|
|
542
|
+
async function nodeToHtml(node, revisionFallback) {
|
|
543
|
+
if (node.localName === "commentRangeStart") {
|
|
544
|
+
const id = getAttr(node, "w:id") ?? getAttr(node, "id");
|
|
545
|
+
return id ? [`<span data-word-comment-range-start="${id}"></span>`] : [];
|
|
546
|
+
}
|
|
547
|
+
if (node.localName === "commentRangeEnd") {
|
|
548
|
+
const id = getAttr(node, "w:id") ?? getAttr(node, "id");
|
|
549
|
+
return id ? [`<span data-word-comment-range-end="${id}"></span>`] : [];
|
|
550
|
+
}
|
|
551
|
+
if (node.localName === "r") {
|
|
552
|
+
return runToHtml(node, revisionFallback);
|
|
553
|
+
}
|
|
554
|
+
if (node.localName === "hyperlink") {
|
|
555
|
+
const rid = getAttr(node, "r:id") ?? getAttr(node, "id");
|
|
556
|
+
const anchor = getAttr(node, "w:anchor") ?? getAttr(node, "anchor");
|
|
557
|
+
const href = resolveHyperlinkHref(relMap, rid, anchor);
|
|
558
|
+
const nested2 = [];
|
|
559
|
+
for (const child of Array.from(node.children)) {
|
|
560
|
+
nested2.push(...await nodeToHtml(child, revisionFallback));
|
|
358
561
|
}
|
|
359
|
-
|
|
360
|
-
if (
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
562
|
+
const content2 = nested2.join("") || escapeHtml(node.textContent ?? "");
|
|
563
|
+
if (!href) return content2 ? [content2] : [];
|
|
564
|
+
context.features.hyperlinkCount += 1;
|
|
565
|
+
return [
|
|
566
|
+
`<a data-word-hyperlink="1" href="${escapeHtml(href)}" rel="noreferrer noopener" target="_blank">${content2}</a>`
|
|
567
|
+
];
|
|
568
|
+
}
|
|
569
|
+
if (node.localName === "oMath" || node.localName === "oMathPara") {
|
|
570
|
+
const linear = ommlNodeToText(node).trim();
|
|
571
|
+
if (!linear) return [];
|
|
572
|
+
context.features.ommlCount += 1;
|
|
573
|
+
return [`<span data-word-omml="1">${escapeHtml(linear)}</span>`];
|
|
574
|
+
}
|
|
575
|
+
if (node.localName === "ins" || node.localName === "del") {
|
|
576
|
+
const scopedMeta = parseRevisionMeta(node, node.localName === "ins" ? "ins" : "del");
|
|
577
|
+
const nested2 = [];
|
|
578
|
+
for (const child of Array.from(node.children)) {
|
|
579
|
+
nested2.push(...await nodeToHtml(child, scopedMeta));
|
|
365
580
|
}
|
|
581
|
+
return nested2;
|
|
366
582
|
}
|
|
367
|
-
|
|
368
|
-
|
|
583
|
+
const nested = [];
|
|
584
|
+
for (const child of Array.from(node.children)) {
|
|
585
|
+
nested.push(...await nodeToHtml(child, revisionFallback));
|
|
369
586
|
}
|
|
587
|
+
return nested;
|
|
588
|
+
}
|
|
589
|
+
const parts = [];
|
|
590
|
+
const renderedPageBreakCount = queryAllByLocalName(paragraph, "lastRenderedPageBreak").length;
|
|
591
|
+
for (let i = 0; i < renderedPageBreakCount; i += 1) {
|
|
592
|
+
context.features.pageBreakCount += 1;
|
|
593
|
+
parts.push(`<span data-word-page-break="1" style="display:block;break-before:page"></span>`);
|
|
594
|
+
}
|
|
595
|
+
for (const child of Array.from(paragraph.children)) {
|
|
596
|
+
parts.push(...await nodeToHtml(child, null));
|
|
370
597
|
}
|
|
371
598
|
const content = parts.join("") || "<br/>";
|
|
372
599
|
return `<${tag}${dataAttr}${alignStyle ? ` style="${alignStyle}"` : ""}>${content}</${tag}>`;
|
|
@@ -396,7 +623,102 @@ function parseTcVMerge(tc) {
|
|
|
396
623
|
const rawVal = (getAttr(vMerge, "w:val") ?? getAttr(vMerge, "val") ?? "continue").toLowerCase();
|
|
397
624
|
return rawVal === "restart" ? "restart" : "continue";
|
|
398
625
|
}
|
|
399
|
-
function
|
|
626
|
+
function parseTblGridWidthsPx(table) {
|
|
627
|
+
const grid = directChildrenByLocalName(table, "tblGrid")[0] ?? null;
|
|
628
|
+
if (!grid) return [];
|
|
629
|
+
return directChildrenByLocalName(grid, "gridCol").map((col) => {
|
|
630
|
+
const raw = getAttr(col, "w:w") ?? getAttr(col, "w");
|
|
631
|
+
const twip = raw ? Number.parseInt(raw, 10) : Number.NaN;
|
|
632
|
+
return Number.isFinite(twip) && twip > 0 ? twipToPx(twip) : 0;
|
|
633
|
+
}).filter((px) => px > 0);
|
|
634
|
+
}
|
|
635
|
+
function borderSizeToPx(size) {
|
|
636
|
+
return size / 6;
|
|
637
|
+
}
|
|
638
|
+
function parseBorderCss(borderNode) {
|
|
639
|
+
if (!borderNode) return null;
|
|
640
|
+
const val = (getAttr(borderNode, "w:val") ?? getAttr(borderNode, "val") ?? "").toLowerCase();
|
|
641
|
+
if (!val || val === "nil" || val === "none") return "none";
|
|
642
|
+
const color = (getAttr(borderNode, "w:color") ?? getAttr(borderNode, "color") ?? "222222").replace(/^#/, "");
|
|
643
|
+
const rawSize = getAttr(borderNode, "w:sz") ?? getAttr(borderNode, "sz");
|
|
644
|
+
const size = rawSize ? Number.parseInt(rawSize, 10) : Number.NaN;
|
|
645
|
+
const px = Number.isFinite(size) && size > 0 ? borderSizeToPx(size) : 1;
|
|
646
|
+
const style = val === "single" ? "solid" : val;
|
|
647
|
+
return `${px.toFixed(2)}px ${style} #${color}`;
|
|
648
|
+
}
|
|
649
|
+
function parseTableStyleProfile(table) {
|
|
650
|
+
const tblPr = directChildrenByLocalName(table, "tblPr")[0] ?? null;
|
|
651
|
+
const tblBorders = tblPr ? directChildrenByLocalName(tblPr, "tblBorders")[0] ?? null : null;
|
|
652
|
+
const layout = tblPr ? directChildrenByLocalName(tblPr, "tblLayout")[0] ?? null : null;
|
|
653
|
+
const spacing = tblPr ? directChildrenByLocalName(tblPr, "tblCellSpacing")[0] ?? null : null;
|
|
654
|
+
const spacingType = (getAttr(spacing, "w:type") ?? getAttr(spacing, "type") ?? "dxa").toLowerCase();
|
|
655
|
+
const spacingRaw = getAttr(spacing, "w:w") ?? getAttr(spacing, "w");
|
|
656
|
+
const spacingVal = spacingRaw ? Number.parseFloat(spacingRaw) : Number.NaN;
|
|
657
|
+
const borderSpacingPx = spacingType === "dxa" && Number.isFinite(spacingVal) && spacingVal > 0 ? twipToPx(spacingVal) : 0;
|
|
658
|
+
const borderCollapse = borderSpacingPx > 0 ? "separate" : "collapse";
|
|
659
|
+
const tableLayout = (getAttr(layout, "w:type") ?? getAttr(layout, "type") ?? "").toLowerCase() === "autofit" ? "auto" : "fixed";
|
|
660
|
+
const top = parseBorderCss(tblBorders ? directChildrenByLocalName(tblBorders, "top")[0] ?? null : null);
|
|
661
|
+
const bottom = parseBorderCss(tblBorders ? directChildrenByLocalName(tblBorders, "bottom")[0] ?? null : null);
|
|
662
|
+
const left = parseBorderCss(tblBorders ? directChildrenByLocalName(tblBorders, "left")[0] ?? null : null);
|
|
663
|
+
const right = parseBorderCss(tblBorders ? directChildrenByLocalName(tblBorders, "right")[0] ?? null : null);
|
|
664
|
+
const insideH = parseBorderCss(tblBorders ? directChildrenByLocalName(tblBorders, "insideH")[0] ?? null : null);
|
|
665
|
+
const insideV = parseBorderCss(tblBorders ? directChildrenByLocalName(tblBorders, "insideV")[0] ?? null : null);
|
|
666
|
+
const borderCss = top ?? right ?? bottom ?? left ?? "1px solid #222";
|
|
667
|
+
return {
|
|
668
|
+
tableLayout,
|
|
669
|
+
borderCollapse,
|
|
670
|
+
borderSpacingPx,
|
|
671
|
+
borderCss,
|
|
672
|
+
insideHCss: insideH,
|
|
673
|
+
insideVCss: insideV
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
function parseTableWidthStyle(table, gridWidthsPx) {
|
|
677
|
+
const tblPr = directChildrenByLocalName(table, "tblPr")[0] ?? null;
|
|
678
|
+
const tblW = tblPr ? directChildrenByLocalName(tblPr, "tblW")[0] ?? null : null;
|
|
679
|
+
const type = (getAttr(tblW, "w:type") ?? getAttr(tblW, "type") ?? "").toLowerCase();
|
|
680
|
+
const rawVal = getAttr(tblW, "w:w") ?? getAttr(tblW, "w");
|
|
681
|
+
const numericVal = rawVal ? Number.parseFloat(rawVal) : Number.NaN;
|
|
682
|
+
if (type === "dxa" && Number.isFinite(numericVal) && numericVal > 0) {
|
|
683
|
+
return `width:${twipToPx(numericVal).toFixed(2)}px`;
|
|
684
|
+
}
|
|
685
|
+
if (type === "pct" && Number.isFinite(numericVal) && numericVal > 0) {
|
|
686
|
+
return `width:${(numericVal / 50).toFixed(2)}%`;
|
|
687
|
+
}
|
|
688
|
+
const gridTotal = gridWidthsPx.reduce((sum, item) => sum + item, 0);
|
|
689
|
+
if (gridTotal > 0) return `width:${gridTotal.toFixed(2)}px;max-width:100%`;
|
|
690
|
+
return "width:100%";
|
|
691
|
+
}
|
|
692
|
+
function parseCellWidthStyle(cell, colCursor, colSpan, gridWidthsPx) {
|
|
693
|
+
const tcPr = directChildrenByLocalName(cell, "tcPr")[0] ?? null;
|
|
694
|
+
const tcW = tcPr ? directChildrenByLocalName(tcPr, "tcW")[0] ?? null : null;
|
|
695
|
+
const type = (getAttr(tcW, "w:type") ?? getAttr(tcW, "type") ?? "").toLowerCase();
|
|
696
|
+
const rawVal = getAttr(tcW, "w:w") ?? getAttr(tcW, "w");
|
|
697
|
+
const numericVal = rawVal ? Number.parseFloat(rawVal) : Number.NaN;
|
|
698
|
+
if (type === "dxa" && Number.isFinite(numericVal) && numericVal > 0) {
|
|
699
|
+
return `width:${twipToPx(numericVal).toFixed(2)}px`;
|
|
700
|
+
}
|
|
701
|
+
if (type === "pct" && Number.isFinite(numericVal) && numericVal > 0) {
|
|
702
|
+
return `width:${(numericVal / 50).toFixed(2)}%`;
|
|
703
|
+
}
|
|
704
|
+
const width = gridWidthsPx.slice(colCursor, colCursor + colSpan).reduce((sum, item) => sum + item, 0);
|
|
705
|
+
if (width > 0) return `width:${width.toFixed(2)}px`;
|
|
706
|
+
return "";
|
|
707
|
+
}
|
|
708
|
+
function parseCellBorderStyle(cell, tableStyle) {
|
|
709
|
+
const tcPr = directChildrenByLocalName(cell, "tcPr")[0] ?? null;
|
|
710
|
+
const tcBorders = tcPr ? directChildrenByLocalName(tcPr, "tcBorders")[0] ?? null : null;
|
|
711
|
+
if (!tcBorders) {
|
|
712
|
+
const fallback = tableStyle.insideHCss ?? tableStyle.insideVCss ?? tableStyle.borderCss;
|
|
713
|
+
return `border:${fallback}`;
|
|
714
|
+
}
|
|
715
|
+
const top = parseBorderCss(directChildrenByLocalName(tcBorders, "top")[0] ?? null) ?? tableStyle.insideHCss ?? tableStyle.borderCss;
|
|
716
|
+
const right = parseBorderCss(directChildrenByLocalName(tcBorders, "right")[0] ?? null) ?? tableStyle.insideVCss ?? tableStyle.borderCss;
|
|
717
|
+
const bottom = parseBorderCss(directChildrenByLocalName(tcBorders, "bottom")[0] ?? null) ?? tableStyle.insideHCss ?? tableStyle.borderCss;
|
|
718
|
+
const left = parseBorderCss(directChildrenByLocalName(tcBorders, "left")[0] ?? null) ?? tableStyle.insideVCss ?? tableStyle.borderCss;
|
|
719
|
+
return `border-top:${top};border-right:${right};border-bottom:${bottom};border-left:${left}`;
|
|
720
|
+
}
|
|
721
|
+
function tableCellHtml(cell, paragraphIndexMap, context) {
|
|
400
722
|
const blocks = [];
|
|
401
723
|
for (const child of Array.from(cell.children)) {
|
|
402
724
|
if (child.localName === "tcPr") continue;
|
|
@@ -406,7 +728,7 @@ function tableCellHtml(cell, paragraphIndexMap) {
|
|
|
406
728
|
continue;
|
|
407
729
|
}
|
|
408
730
|
if (child.localName === "tbl") {
|
|
409
|
-
blocks.push(tableToHtml(child, paragraphIndexMap));
|
|
731
|
+
blocks.push(tableToHtml(child, paragraphIndexMap, context));
|
|
410
732
|
continue;
|
|
411
733
|
}
|
|
412
734
|
}
|
|
@@ -414,8 +736,11 @@ function tableCellHtml(cell, paragraphIndexMap) {
|
|
|
414
736
|
const text = queryAllByLocalName(cell, "t").map((t) => t.textContent ?? "").join("").trim();
|
|
415
737
|
return escapeHtml(text) || "<br/>";
|
|
416
738
|
}
|
|
417
|
-
function tableToHtml(table, paragraphIndexMap) {
|
|
739
|
+
function tableToHtml(table, paragraphIndexMap, context) {
|
|
740
|
+
context.features.tableCount += 1;
|
|
418
741
|
const rows = directChildrenByLocalName(table, "tr");
|
|
742
|
+
const gridWidthsPx = parseTblGridWidthsPx(table);
|
|
743
|
+
const tableStyle = parseTableStyleProfile(table);
|
|
419
744
|
const activeByCol = /* @__PURE__ */ new Map();
|
|
420
745
|
const allOrigins = [];
|
|
421
746
|
let nextOriginId = 1;
|
|
@@ -440,8 +765,10 @@ function tableToHtml(table, paragraphIndexMap) {
|
|
|
440
765
|
while (activeByCol.has(colCursor)) {
|
|
441
766
|
colCursor += 1;
|
|
442
767
|
}
|
|
443
|
-
const html = tableCellHtml(cell, paragraphIndexMap);
|
|
768
|
+
const html = tableCellHtml(cell, paragraphIndexMap, context);
|
|
444
769
|
const attrs = [];
|
|
770
|
+
const widthStyle = parseCellWidthStyle(cell, colCursor, colSpan, gridWidthsPx);
|
|
771
|
+
const borderStyle = parseCellBorderStyle(cell, tableStyle);
|
|
445
772
|
if (vMerge === "restart") {
|
|
446
773
|
const origin = {
|
|
447
774
|
id: `m${nextOriginId}`,
|
|
@@ -459,7 +786,7 @@ function tableToHtml(table, paragraphIndexMap) {
|
|
|
459
786
|
}
|
|
460
787
|
if (colSpan > 1) attrs.push(`colspan="${colSpan}"`);
|
|
461
788
|
emittedCells.push(
|
|
462
|
-
`<td${attrs.length > 0 ? ` ${attrs.join(" ")}` : ""} style="
|
|
789
|
+
`<td${attrs.length > 0 ? ` ${attrs.join(" ")}` : ""} style="${borderStyle};vertical-align:top;${widthStyle}">${html}</td>`
|
|
463
790
|
);
|
|
464
791
|
colCursor += colSpan;
|
|
465
792
|
}
|
|
@@ -478,9 +805,13 @@ function tableToHtml(table, paragraphIndexMap) {
|
|
|
478
805
|
const replacement = origin.rowSpan > 1 ? `rowspan="${origin.rowSpan}"` : "";
|
|
479
806
|
merged = merged.replace(marker, replacement).replace(/\s{2,}/g, " ");
|
|
480
807
|
}
|
|
481
|
-
|
|
808
|
+
const tableWidthStyle = parseTableWidthStyle(table, gridWidthsPx);
|
|
809
|
+
const spacing = tableStyle.borderSpacingPx > 0 ? `border-spacing:${tableStyle.borderSpacingPx.toFixed(2)}px;` : "";
|
|
810
|
+
return `<table style="border-collapse:${tableStyle.borderCollapse};${spacing}table-layout:${tableStyle.tableLayout};${tableWidthStyle};border:${tableStyle.borderCss};">${merged}</table>`;
|
|
482
811
|
}
|
|
483
|
-
async function
|
|
812
|
+
async function parseDocxToHtmlSnapshotWithReport(file) {
|
|
813
|
+
const startedAt = Date.now();
|
|
814
|
+
const context = { features: createEmptyFeatureCounts() };
|
|
484
815
|
const maybeArrayBuffer = file.arrayBuffer;
|
|
485
816
|
const buffer = maybeArrayBuffer ? await maybeArrayBuffer.call(file) : await new Response(file).arrayBuffer();
|
|
486
817
|
const zip = await JSZip.loadAsync(buffer);
|
|
@@ -517,6 +848,7 @@ async function parseDocxToHtmlSnapshot(file) {
|
|
|
517
848
|
await paragraphToHtml(
|
|
518
849
|
zip,
|
|
519
850
|
relMap,
|
|
851
|
+
context,
|
|
520
852
|
child,
|
|
521
853
|
paragraphIndex,
|
|
522
854
|
footnotesMap,
|
|
@@ -530,14 +862,24 @@ async function parseDocxToHtmlSnapshot(file) {
|
|
|
530
862
|
continue;
|
|
531
863
|
}
|
|
532
864
|
if (child.localName === "tbl") {
|
|
533
|
-
blockHtml.push(tableToHtml(child, paragraphIndexMap));
|
|
865
|
+
blockHtml.push(tableToHtml(child, paragraphIndexMap, context));
|
|
534
866
|
continue;
|
|
535
867
|
}
|
|
536
868
|
}
|
|
537
869
|
blockHtml.push(renderFootnotesSection(usedFootnoteIds, footnotesMap));
|
|
538
870
|
blockHtml.push(renderEndnotesSection(usedEndnoteIds, endnotesMap));
|
|
539
871
|
blockHtml.push(renderCommentsSection(usedCommentIds, commentsMap));
|
|
540
|
-
return
|
|
872
|
+
return {
|
|
873
|
+
htmlSnapshot: buildHtmlSnapshot(blockHtml.join("\n")),
|
|
874
|
+
report: {
|
|
875
|
+
elapsedMs: Date.now() - startedAt,
|
|
876
|
+
features: context.features
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
async function parseDocxToHtmlSnapshot(file) {
|
|
881
|
+
const result = await parseDocxToHtmlSnapshotWithReport(file);
|
|
882
|
+
return result.htmlSnapshot;
|
|
541
883
|
}
|
|
542
884
|
|
|
543
885
|
// src/lib/pastePipeline.ts
|
|
@@ -768,7 +1110,7 @@ function createFallbackWordStyleProfile(sourceFileName = "snapshot") {
|
|
|
768
1110
|
paragraphProfiles: []
|
|
769
1111
|
};
|
|
770
1112
|
}
|
|
771
|
-
function
|
|
1113
|
+
function twipToPx2(twip) {
|
|
772
1114
|
return twip / 15;
|
|
773
1115
|
}
|
|
774
1116
|
function getAttr2(node, attr) {
|
|
@@ -812,10 +1154,10 @@ function parsePageGeometry(documentXml) {
|
|
|
812
1154
|
const top = getTwipAttr(pgMar, "w:top") ?? getTwipAttr(pgMar, "top") ?? null;
|
|
813
1155
|
const bottom = getTwipAttr(pgMar, "w:bottom") ?? getTwipAttr(pgMar, "bottom") ?? null;
|
|
814
1156
|
return {
|
|
815
|
-
contentWidthPx: pageW === null ? null :
|
|
816
|
-
pageHeightPx: pageH === null ? null :
|
|
817
|
-
marginTopPx: top === null ? null :
|
|
818
|
-
marginBottomPx: bottom === null ? null :
|
|
1157
|
+
contentWidthPx: pageW === null ? null : twipToPx2(pageW - left - right),
|
|
1158
|
+
pageHeightPx: pageH === null ? null : twipToPx2(pageH),
|
|
1159
|
+
marginTopPx: top === null ? null : twipToPx2(top),
|
|
1160
|
+
marginBottomPx: bottom === null ? null : twipToPx2(bottom)
|
|
819
1161
|
};
|
|
820
1162
|
}
|
|
821
1163
|
function parseHeadingAlignFromDocument(documentXml) {
|
|
@@ -988,15 +1330,15 @@ function parseParagraphProfiles(documentXml, numberingMap) {
|
|
|
988
1330
|
text,
|
|
989
1331
|
isEmpty: text.length === 0,
|
|
990
1332
|
align: parseParagraphAlign(paragraph),
|
|
991
|
-
beforePx: before === null ? null :
|
|
992
|
-
afterPx: after === null ? null :
|
|
1333
|
+
beforePx: before === null ? null : twipToPx2(before),
|
|
1334
|
+
afterPx: after === null ? null : twipToPx2(after),
|
|
993
1335
|
lineHeightRatio: line === null || lineHeightRule !== "auto" ? null : line / 240,
|
|
994
|
-
lineHeightPx: line === null || lineHeightRule === "auto" ? null :
|
|
1336
|
+
lineHeightPx: line === null || lineHeightRule === "auto" ? null : twipToPx2(line),
|
|
995
1337
|
lineHeightRule,
|
|
996
|
-
indentLeftPx: left === null ? null :
|
|
997
|
-
indentRightPx: right === null ? null :
|
|
998
|
-
firstLinePx: firstLine === null ? null :
|
|
999
|
-
hangingPx: hanging === null ? null :
|
|
1338
|
+
indentLeftPx: left === null ? null : twipToPx2(left),
|
|
1339
|
+
indentRightPx: right === null ? null : twipToPx2(right),
|
|
1340
|
+
firstLinePx: firstLine === null ? null : twipToPx2(firstLine),
|
|
1341
|
+
hangingPx: hanging === null ? null : twipToPx2(hanging),
|
|
1000
1342
|
listNumId,
|
|
1001
1343
|
listLevel,
|
|
1002
1344
|
listFormat: listSpec?.numFmt ?? null,
|
|
@@ -1031,19 +1373,19 @@ function parseTableDefaults(stylesXml) {
|
|
|
1031
1373
|
return {
|
|
1032
1374
|
topPx: (() => {
|
|
1033
1375
|
const v = getTwipAttr(top, "w:w") ?? getTwipAttr(top, "w") ?? null;
|
|
1034
|
-
return v === null ? null :
|
|
1376
|
+
return v === null ? null : twipToPx2(v);
|
|
1035
1377
|
})(),
|
|
1036
1378
|
leftPx: (() => {
|
|
1037
1379
|
const v = getTwipAttr(left, "w:w") ?? getTwipAttr(left, "w") ?? null;
|
|
1038
|
-
return v === null ? null :
|
|
1380
|
+
return v === null ? null : twipToPx2(v);
|
|
1039
1381
|
})(),
|
|
1040
1382
|
bottomPx: (() => {
|
|
1041
1383
|
const v = getTwipAttr(bottom, "w:w") ?? getTwipAttr(bottom, "w") ?? null;
|
|
1042
|
-
return v === null ? null :
|
|
1384
|
+
return v === null ? null : twipToPx2(v);
|
|
1043
1385
|
})(),
|
|
1044
1386
|
rightPx: (() => {
|
|
1045
1387
|
const v = getTwipAttr(right, "w:w") ?? getTwipAttr(right, "w") ?? null;
|
|
1046
|
-
return v === null ? null :
|
|
1388
|
+
return v === null ? null : twipToPx2(v);
|
|
1047
1389
|
})()
|
|
1048
1390
|
};
|
|
1049
1391
|
}
|
|
@@ -1141,9 +1483,9 @@ function parseDefaults(stylesXml) {
|
|
|
1141
1483
|
const rawLineRule = (getAttr2(spacing, "w:lineRule") ?? getAttr2(spacing, "lineRule") ?? "auto").toLowerCase();
|
|
1142
1484
|
const bodyLineHeightRule = rawLineRule === "exact" ? "exact" : rawLineRule === "atleast" ? "atLeast" : "auto";
|
|
1143
1485
|
const bodyLineHeightRatio = line === null || bodyLineHeightRule !== "auto" ? null : line / 240;
|
|
1144
|
-
const bodyLineHeightPx = line === null || bodyLineHeightRule === "auto" ? null :
|
|
1486
|
+
const bodyLineHeightPx = line === null || bodyLineHeightRule === "auto" ? null : twipToPx2(line);
|
|
1145
1487
|
const after = getTwipAttr(spacing, "w:after") ?? getTwipAttr(spacing, "after") ?? null;
|
|
1146
|
-
const paragraphAfterPx = after === null ? null :
|
|
1488
|
+
const paragraphAfterPx = after === null ? null : twipToPx2(after);
|
|
1147
1489
|
return { bodyFontPx, bodyLineHeightRatio, bodyLineHeightPx, bodyLineHeightRule, paragraphAfterPx };
|
|
1148
1490
|
}
|
|
1149
1491
|
function parseHeading1Style(stylesXml) {
|
|
@@ -1668,7 +2010,7 @@ function applyWordRenderModel({ doc, styleProfile, showFormattingMarks }) {
|
|
|
1668
2010
|
}
|
|
1669
2011
|
|
|
1670
2012
|
// src/core/DocsWordElement.ts
|
|
1671
|
-
var VERSION = "0.1.
|
|
2013
|
+
var VERSION = "0.1.5";
|
|
1672
2014
|
var MESSAGES = {
|
|
1673
2015
|
zh: {
|
|
1674
2016
|
readClipboard: "\u4ECE\u7CFB\u7EDF\u526A\u8D34\u677F\u8BFB\u53D6",
|
|
@@ -1811,15 +2153,15 @@ var DocsWordElement = class extends HTMLElement {
|
|
|
1811
2153
|
}
|
|
1812
2154
|
async applyDocx(file) {
|
|
1813
2155
|
try {
|
|
1814
|
-
const [
|
|
1815
|
-
|
|
2156
|
+
const [parseResult, profile] = await Promise.all([
|
|
2157
|
+
parseDocxToHtmlSnapshotWithReport(file),
|
|
1816
2158
|
parseDocxStyleProfile(file)
|
|
1817
2159
|
]);
|
|
1818
2160
|
this.styleProfile = profile;
|
|
1819
|
-
this.htmlSnapshot =
|
|
2161
|
+
this.htmlSnapshot = parseResult.htmlSnapshot;
|
|
1820
2162
|
this.renderSnapshot();
|
|
1821
2163
|
this.setHint(MESSAGES[this.locale].loadedWord(profile.sourceFileName));
|
|
1822
|
-
this.emitChange("upload", profile.sourceFileName);
|
|
2164
|
+
this.emitChange("upload", profile.sourceFileName, parseResult.report);
|
|
1823
2165
|
} catch (error) {
|
|
1824
2166
|
this.emitError(error instanceof Error ? error.message : MESSAGES[this.locale].parseFailed);
|
|
1825
2167
|
}
|
|
@@ -1879,8 +2221,10 @@ var DocsWordElement = class extends HTMLElement {
|
|
|
1879
2221
|
renderSnapshot() {
|
|
1880
2222
|
this.frame.srcdoc = this.htmlSnapshot;
|
|
1881
2223
|
}
|
|
1882
|
-
emitChange(source, fileName) {
|
|
1883
|
-
this.dispatchEvent(
|
|
2224
|
+
emitChange(source, fileName, parseReport) {
|
|
2225
|
+
this.dispatchEvent(
|
|
2226
|
+
new CustomEvent("docsjs-change", { detail: { htmlSnapshot: this.htmlSnapshot, source, fileName, parseReport } })
|
|
2227
|
+
);
|
|
1884
2228
|
}
|
|
1885
2229
|
emitError(message) {
|
|
1886
2230
|
this.dispatchEvent(new CustomEvent("docsjs-error", { detail: { message } }));
|
|
@@ -1915,7 +2259,9 @@ function defineDocsWordElement() {
|
|
|
1915
2259
|
}
|
|
1916
2260
|
|
|
1917
2261
|
export {
|
|
2262
|
+
parseDocxToHtmlSnapshotWithReport,
|
|
2263
|
+
parseDocxToHtmlSnapshot,
|
|
1918
2264
|
DocsWordElement,
|
|
1919
2265
|
defineDocsWordElement
|
|
1920
2266
|
};
|
|
1921
|
-
//# sourceMappingURL=chunk-
|
|
2267
|
+
//# sourceMappingURL=chunk-632UOG2B.js.map
|