@coding01/docsjs 0.1.5 → 0.1.7
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 +57 -28
- package/README.zh-CN.md +56 -27
- package/dist/index.cjs +26 -2265
- package/dist/index.d.ts +44 -3
- package/dist/index.js +60 -75
- package/dist/react.cjs +1 -2264
- package/dist/react.d.ts +3 -2
- package/dist/react.js +1 -41
- package/dist/{types-DF14w1ol.d.cts → types-BgP7Zasj.d.ts} +19 -0
- package/dist/vue.cjs +1 -2267
- package/dist/vue.d.ts +1 -1
- package/dist/vue.js +1 -44
- package/package.json +7 -3
- package/dist/chunk-IBVWD4UO.js +0 -2193
- package/dist/chunk-IBVWD4UO.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -73
- package/dist/index.js.map +0 -1
- package/dist/react.cjs.map +0 -1
- package/dist/react.d.cts +0 -16
- package/dist/react.js.map +0 -1
- package/dist/types-DF14w1ol.d.ts +0 -20
- package/dist/vue.cjs.map +0 -1
- package/dist/vue.d.cts +0 -24
- package/dist/vue.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,1954 +1,44 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
-
|
|
30
|
-
// src/index.ts
|
|
31
|
-
var index_exports = {};
|
|
32
|
-
__export(index_exports, {
|
|
33
|
-
DocsWordElement: () => DocsWordElement,
|
|
34
|
-
calculateFidelityScore: () => calculateFidelityScore,
|
|
35
|
-
collectSemanticStatsFromDocument: () => collectSemanticStatsFromDocument,
|
|
36
|
-
collectSemanticStatsFromHtml: () => collectSemanticStatsFromHtml,
|
|
37
|
-
defineDocsWordElement: () => defineDocsWordElement
|
|
38
|
-
});
|
|
39
|
-
module.exports = __toCommonJS(index_exports);
|
|
40
|
-
|
|
41
|
-
// src/lib/htmlSnapshot.ts
|
|
42
|
-
var SNAPSHOT_SHELL_START = '<!DOCTYPE html><html><head><meta charset="utf-8"/>';
|
|
43
|
-
var SNAPSHOT_SHELL_END = "</head><body></body></html>";
|
|
44
|
-
function buildHtmlSnapshot(rawHtml) {
|
|
45
|
-
if (!rawHtml.trim()) {
|
|
46
|
-
return `${SNAPSHOT_SHELL_START}${SNAPSHOT_SHELL_END}`;
|
|
47
|
-
}
|
|
48
|
-
const hasHtmlTag = /<html[\s>]/i.test(rawHtml);
|
|
49
|
-
if (hasHtmlTag) {
|
|
50
|
-
return rawHtml;
|
|
51
|
-
}
|
|
52
|
-
return `${SNAPSHOT_SHELL_START}${SNAPSHOT_SHELL_END}`.replace(
|
|
53
|
-
"<body></body>",
|
|
54
|
-
`<body>${rawHtml}</body>`
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// src/lib/docxHtml.ts
|
|
59
|
-
var import_jszip = __toESM(require("jszip"), 1);
|
|
60
|
-
function escapeHtml(text) {
|
|
61
|
-
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
62
|
-
}
|
|
63
|
-
function parseXml(xmlText) {
|
|
64
|
-
const parser = new DOMParser();
|
|
65
|
-
return parser.parseFromString(xmlText, "application/xml");
|
|
66
|
-
}
|
|
67
|
-
function queryAllByLocalName(root, localName) {
|
|
68
|
-
return Array.from(root.querySelectorAll("*")).filter((el) => el.localName === localName);
|
|
69
|
-
}
|
|
70
|
-
function queryByLocalName(root, localName) {
|
|
71
|
-
return queryAllByLocalName(root, localName)[0] ?? null;
|
|
72
|
-
}
|
|
73
|
-
function directChildrenByLocalName(node, localName) {
|
|
74
|
-
return Array.from(node.children).filter((child) => child.localName === localName);
|
|
75
|
-
}
|
|
76
|
-
function getAttr(node, name) {
|
|
77
|
-
if (!node) return null;
|
|
78
|
-
return node.getAttribute(name);
|
|
79
|
-
}
|
|
80
|
-
function emuToPx(emu) {
|
|
81
|
-
return emu * 96 / 914400;
|
|
82
|
-
}
|
|
83
|
-
function twipToPx(twip) {
|
|
84
|
-
return twip * 96 / 1440;
|
|
85
|
-
}
|
|
86
|
-
function parseDrawingSizePx(drawing) {
|
|
87
|
-
const extentNode = queryAllByLocalName(drawing, "extent").find((node) => {
|
|
88
|
-
const parent = node.parentElement;
|
|
89
|
-
return parent?.localName === "inline" || parent?.localName === "anchor";
|
|
90
|
-
}) ?? null;
|
|
91
|
-
if (!extentNode) {
|
|
92
|
-
return { widthPx: null, heightPx: null };
|
|
93
|
-
}
|
|
94
|
-
const rawCx = getAttr(extentNode, "cx");
|
|
95
|
-
const rawCy = getAttr(extentNode, "cy");
|
|
96
|
-
const cx = rawCx ? Number.parseInt(rawCx, 10) : Number.NaN;
|
|
97
|
-
const cy = rawCy ? Number.parseInt(rawCy, 10) : Number.NaN;
|
|
98
|
-
const widthPx = Number.isFinite(cx) && cx > 0 ? emuToPx(cx) : null;
|
|
99
|
-
const heightPx = Number.isFinite(cy) && cy > 0 ? emuToPx(cy) : null;
|
|
100
|
-
return { widthPx, heightPx };
|
|
101
|
-
}
|
|
102
|
-
function imageDimensionAttributes(sizePx) {
|
|
103
|
-
const attrs = [];
|
|
104
|
-
if (sizePx.widthPx !== null) attrs.push(`width="${Math.round(sizePx.widthPx)}"`);
|
|
105
|
-
if (sizePx.heightPx !== null) attrs.push(`height="${Math.round(sizePx.heightPx)}"`);
|
|
106
|
-
if (sizePx.widthPx !== null || sizePx.heightPx !== null) {
|
|
107
|
-
const style = ["max-width:100%"];
|
|
108
|
-
if (sizePx.widthPx !== null) style.push(`width:${sizePx.widthPx.toFixed(2)}px`);
|
|
109
|
-
if (sizePx.heightPx !== null) style.push(`height:${sizePx.heightPx.toFixed(2)}px`);
|
|
110
|
-
attrs.push(`style="${style.join(";")}"`);
|
|
111
|
-
}
|
|
112
|
-
return attrs.length > 0 ? ` ${attrs.join(" ")}` : "";
|
|
113
|
-
}
|
|
114
|
-
function parseAnchorPositionPx(anchor) {
|
|
115
|
-
let leftPx = null;
|
|
116
|
-
let topPx = null;
|
|
117
|
-
const positionH = directChildrenByLocalName(anchor, "positionH")[0] ?? null;
|
|
118
|
-
const positionV = directChildrenByLocalName(anchor, "positionV")[0] ?? null;
|
|
119
|
-
const posH = positionH ? directChildrenByLocalName(positionH, "posOffset")[0] ?? null : null;
|
|
120
|
-
const posV = positionV ? directChildrenByLocalName(positionV, "posOffset")[0] ?? null : null;
|
|
121
|
-
const rawLeft = posH?.textContent?.trim() ?? "";
|
|
122
|
-
const rawTop = posV?.textContent?.trim() ?? "";
|
|
123
|
-
const left = rawLeft ? Number.parseFloat(rawLeft) : Number.NaN;
|
|
124
|
-
const top = rawTop ? Number.parseFloat(rawTop) : Number.NaN;
|
|
125
|
-
if (Number.isFinite(left)) leftPx = emuToPx(left);
|
|
126
|
-
if (Number.isFinite(top)) topPx = emuToPx(top);
|
|
127
|
-
return { leftPx, topPx };
|
|
128
|
-
}
|
|
129
|
-
function parseAnchorWrapMode(anchor) {
|
|
130
|
-
if (directChildrenByLocalName(anchor, "wrapSquare")[0]) return "square";
|
|
131
|
-
if (directChildrenByLocalName(anchor, "wrapTight")[0]) return "tight";
|
|
132
|
-
if (directChildrenByLocalName(anchor, "wrapTopAndBottom")[0]) return "topAndBottom";
|
|
133
|
-
if (directChildrenByLocalName(anchor, "wrapNone")[0]) return "none";
|
|
134
|
-
return null;
|
|
135
|
-
}
|
|
136
|
-
function parseAnchorMeta(drawing) {
|
|
137
|
-
const anchor = directChildrenByLocalName(drawing, "anchor")[0] ?? null;
|
|
138
|
-
if (!anchor) return null;
|
|
139
|
-
const positionH = directChildrenByLocalName(anchor, "positionH")[0] ?? null;
|
|
140
|
-
const positionV = directChildrenByLocalName(anchor, "positionV")[0] ?? null;
|
|
141
|
-
const relativeFromH = getAttr(positionH, "relativeFrom");
|
|
142
|
-
const relativeFromV = getAttr(positionV, "relativeFrom");
|
|
143
|
-
const parseDistPx = (name) => {
|
|
144
|
-
const raw = getAttr(anchor, name);
|
|
145
|
-
const emu = raw ? Number.parseInt(raw, 10) : Number.NaN;
|
|
146
|
-
return Number.isFinite(emu) && emu >= 0 ? emuToPx(emu) : null;
|
|
147
|
-
};
|
|
148
|
-
const rawHeight = getAttr(anchor, "relativeHeight");
|
|
149
|
-
const parsedHeight = rawHeight ? Number.parseInt(rawHeight, 10) : Number.NaN;
|
|
150
|
-
const boolAttr = (name, fallback) => {
|
|
151
|
-
const raw = (getAttr(anchor, name) ?? "").toLowerCase();
|
|
152
|
-
if (raw === "1" || raw === "true" || raw === "on") return true;
|
|
153
|
-
if (raw === "0" || raw === "false" || raw === "off") return false;
|
|
154
|
-
return fallback;
|
|
155
|
-
};
|
|
156
|
-
return {
|
|
157
|
-
position: parseAnchorPositionPx(anchor),
|
|
158
|
-
wrapMode: parseAnchorWrapMode(anchor),
|
|
159
|
-
distTPx: parseDistPx("distT"),
|
|
160
|
-
distBPx: parseDistPx("distB"),
|
|
161
|
-
distLPx: parseDistPx("distL"),
|
|
162
|
-
distRPx: parseDistPx("distR"),
|
|
163
|
-
relativeFromH,
|
|
164
|
-
relativeFromV,
|
|
165
|
-
behindDoc: boolAttr("behindDoc", false),
|
|
166
|
-
allowOverlap: boolAttr("allowOverlap", true),
|
|
167
|
-
layoutInCell: boolAttr("layoutInCell", true),
|
|
168
|
-
relativeHeight: Number.isFinite(parsedHeight) ? parsedHeight : null
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
function mergeImageStyle(baseAttrs, anchorMeta) {
|
|
172
|
-
if (!anchorMeta) return baseAttrs;
|
|
173
|
-
const { position, wrapMode } = anchorMeta;
|
|
174
|
-
if (position.leftPx === null && position.topPx === null) return baseAttrs;
|
|
175
|
-
const styleParts = [
|
|
176
|
-
"position:absolute",
|
|
177
|
-
position.leftPx !== null ? `left:${position.leftPx.toFixed(2)}px` : "",
|
|
178
|
-
position.topPx !== null ? `top:${position.topPx.toFixed(2)}px` : "",
|
|
179
|
-
`z-index:${anchorMeta.behindDoc ? 0 : anchorMeta.relativeHeight ?? 3}`,
|
|
180
|
-
anchorMeta.distTPx !== null ? `margin-top:${anchorMeta.distTPx.toFixed(2)}px` : "",
|
|
181
|
-
anchorMeta.distBPx !== null ? `margin-bottom:${anchorMeta.distBPx.toFixed(2)}px` : "",
|
|
182
|
-
anchorMeta.distLPx !== null ? `margin-left:${anchorMeta.distLPx.toFixed(2)}px` : "",
|
|
183
|
-
anchorMeta.distRPx !== null ? `margin-right:${anchorMeta.distRPx.toFixed(2)}px` : ""
|
|
184
|
-
].filter((x) => x.length > 0);
|
|
185
|
-
if (wrapMode === "topAndBottom") {
|
|
186
|
-
styleParts.push("display:block", "clear:both");
|
|
187
|
-
}
|
|
188
|
-
const anchorAttrs = [
|
|
189
|
-
`data-word-anchor="1"`,
|
|
190
|
-
wrapMode ? `data-word-wrap="${wrapMode}"` : "",
|
|
191
|
-
anchorMeta.relativeFromH ? `data-word-anchor-relh="${escapeHtml(anchorMeta.relativeFromH)}"` : "",
|
|
192
|
-
anchorMeta.relativeFromV ? `data-word-anchor-relv="${escapeHtml(anchorMeta.relativeFromV)}"` : "",
|
|
193
|
-
anchorMeta.behindDoc ? `data-word-anchor-behind="1"` : `data-word-anchor-behind="0"`,
|
|
194
|
-
anchorMeta.allowOverlap ? `data-word-anchor-overlap="1"` : `data-word-anchor-overlap="0"`,
|
|
195
|
-
anchorMeta.layoutInCell ? `data-word-anchor-layout-cell="1"` : `data-word-anchor-layout-cell="0"`
|
|
196
|
-
].filter((x) => x.length > 0).join(" ");
|
|
197
|
-
if (!baseAttrs.includes("style=")) {
|
|
198
|
-
return `${baseAttrs} style="${styleParts.join(";")}" ${anchorAttrs}`;
|
|
199
|
-
}
|
|
200
|
-
return baseAttrs.replace(/style="([^"]*)"/, (_m, styleText) => {
|
|
201
|
-
const merged = [styleText, ...styleParts].filter((x) => x.length > 0).join(";");
|
|
202
|
-
return `style="${merged}" ${anchorAttrs}`;
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
function parseDocRelsMap(relsXmlText) {
|
|
206
|
-
if (!relsXmlText) return {};
|
|
207
|
-
const rels = parseXml(relsXmlText);
|
|
208
|
-
const relationNodes = queryAllByLocalName(rels, "Relationship");
|
|
209
|
-
const map = {};
|
|
210
|
-
for (const rel of relationNodes) {
|
|
211
|
-
const id = getAttr(rel, "Id");
|
|
212
|
-
const target = getAttr(rel, "Target");
|
|
213
|
-
if (!id || !target) continue;
|
|
214
|
-
map[id] = target;
|
|
215
|
-
}
|
|
216
|
-
return map;
|
|
217
|
-
}
|
|
218
|
-
function extToMime(ext) {
|
|
219
|
-
const lower = ext.toLowerCase();
|
|
220
|
-
if (lower === "png") return "image/png";
|
|
221
|
-
if (lower === "jpg" || lower === "jpeg") return "image/jpeg";
|
|
222
|
-
if (lower === "gif") return "image/gif";
|
|
223
|
-
if (lower === "webp") return "image/webp";
|
|
224
|
-
if (lower === "bmp") return "image/bmp";
|
|
225
|
-
if (lower === "svg") return "image/svg+xml";
|
|
226
|
-
return "application/octet-stream";
|
|
227
|
-
}
|
|
228
|
-
function normalizeWordPath(relTarget) {
|
|
229
|
-
const normalized = relTarget.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
230
|
-
if (normalized.startsWith("word/")) return normalized;
|
|
231
|
-
if (normalized.startsWith("../")) return `word/${normalized.replace(/^(\.\.\/)+/, "")}`;
|
|
232
|
-
return `word/${normalized}`;
|
|
233
|
-
}
|
|
234
|
-
async function imageRidToDataUrl(zip, relMap, rid) {
|
|
235
|
-
const relTarget = relMap[rid];
|
|
236
|
-
if (!relTarget) return null;
|
|
237
|
-
const path = normalizeWordPath(relTarget);
|
|
238
|
-
const file = zip.file(path);
|
|
239
|
-
if (!file) return null;
|
|
240
|
-
const base64 = await file.async("base64");
|
|
241
|
-
const ext = path.split(".").pop() ?? "bin";
|
|
242
|
-
const mime = extToMime(ext);
|
|
243
|
-
return `data:${mime};base64,${base64}`;
|
|
244
|
-
}
|
|
245
|
-
async function readXmlByRid(zip, relMap, rid) {
|
|
246
|
-
const relTarget = relMap[rid];
|
|
247
|
-
if (!relTarget) return null;
|
|
248
|
-
const path = normalizeWordPath(relTarget);
|
|
249
|
-
const file = zip.file(path);
|
|
250
|
-
return file ? file.async("string") : null;
|
|
251
|
-
}
|
|
252
|
-
function parseChartType(chartDoc) {
|
|
253
|
-
const known = ["barChart", "lineChart", "pieChart", "areaChart", "scatterChart", "radarChart", "doughnutChart"];
|
|
254
|
-
for (const type of known) {
|
|
255
|
-
if (queryByLocalName(chartDoc, type)) return type.replace(/Chart$/, "");
|
|
256
|
-
}
|
|
257
|
-
return "unknown";
|
|
258
|
-
}
|
|
259
|
-
function parseChartSummary(chartXmlText) {
|
|
260
|
-
const chartDoc = parseXml(chartXmlText);
|
|
261
|
-
const title = queryAllByLocalName(chartDoc, "t").map((n) => (n.textContent ?? "").trim()).find((v) => v.length > 0) ?? "Chart";
|
|
262
|
-
const seriesCount = queryAllByLocalName(chartDoc, "ser").length;
|
|
263
|
-
const pointCount = queryAllByLocalName(chartDoc, "pt").length;
|
|
264
|
-
const type = parseChartType(chartDoc);
|
|
265
|
-
return { title, type, seriesCount, pointCount };
|
|
266
|
-
}
|
|
267
|
-
function extractSmartArtText(diagramXmlText) {
|
|
268
|
-
const diagramDoc = parseXml(diagramXmlText);
|
|
269
|
-
return queryAllByLocalName(diagramDoc, "t").map((n) => (n.textContent ?? "").trim()).filter((v) => v.length > 0).slice(0, 12);
|
|
270
|
-
}
|
|
271
|
-
function ommlNodeToText(node) {
|
|
272
|
-
if (node.localName === "t") return node.textContent ?? "";
|
|
273
|
-
if (node.localName === "f") {
|
|
274
|
-
const num = queryByLocalName(node, "num");
|
|
275
|
-
const den = queryByLocalName(node, "den");
|
|
276
|
-
return `(${num ? ommlNodeToText(num) : "?"})/(${den ? ommlNodeToText(den) : "?"})`;
|
|
277
|
-
}
|
|
278
|
-
if (node.localName === "sSup") {
|
|
279
|
-
const e = queryByLocalName(node, "e");
|
|
280
|
-
const sup = queryByLocalName(node, "sup");
|
|
281
|
-
return `${e ? ommlNodeToText(e) : ""}^(${sup ? ommlNodeToText(sup) : ""})`;
|
|
282
|
-
}
|
|
283
|
-
if (node.localName === "sSub") {
|
|
284
|
-
const e = queryByLocalName(node, "e");
|
|
285
|
-
const sub = queryByLocalName(node, "sub");
|
|
286
|
-
return `${e ? ommlNodeToText(e) : ""}_(${sub ? ommlNodeToText(sub) : ""})`;
|
|
287
|
-
}
|
|
288
|
-
if (node.localName === "rad") {
|
|
289
|
-
const e = queryByLocalName(node, "e");
|
|
290
|
-
return `sqrt(${e ? ommlNodeToText(e) : ""})`;
|
|
291
|
-
}
|
|
292
|
-
return Array.from(node.children).map((child) => ommlNodeToText(child)).join("");
|
|
293
|
-
}
|
|
294
|
-
function runStyleToCss(rPr) {
|
|
295
|
-
if (!rPr) return "";
|
|
296
|
-
const declarations = [];
|
|
297
|
-
if (queryByLocalName(rPr, "b")) declarations.push("font-weight:700");
|
|
298
|
-
if (queryByLocalName(rPr, "i")) declarations.push("font-style:italic");
|
|
299
|
-
if (queryByLocalName(rPr, "u")) declarations.push("text-decoration:underline");
|
|
300
|
-
if (queryByLocalName(rPr, "strike")) declarations.push("text-decoration:line-through");
|
|
301
|
-
const color = queryByLocalName(rPr, "color");
|
|
302
|
-
const colorVal = getAttr(color, "w:val") ?? getAttr(color, "val");
|
|
303
|
-
if (colorVal && colorVal.toLowerCase() !== "auto") declarations.push(`color:#${colorVal}`);
|
|
304
|
-
const highlight = queryByLocalName(rPr, "highlight");
|
|
305
|
-
const highlightVal = (getAttr(highlight, "w:val") ?? getAttr(highlight, "val") ?? "").toLowerCase();
|
|
306
|
-
if (highlightVal === "yellow") declarations.push("background-color:#fff200");
|
|
307
|
-
const vertAlign = queryByLocalName(rPr, "vertAlign");
|
|
308
|
-
const vertVal = (getAttr(vertAlign, "w:val") ?? getAttr(vertAlign, "val") ?? "").toLowerCase();
|
|
309
|
-
if (vertVal === "superscript") declarations.push("vertical-align:super;font-size:0.83em");
|
|
310
|
-
if (vertVal === "subscript") declarations.push("vertical-align:sub;font-size:0.83em");
|
|
311
|
-
return declarations.join(";");
|
|
312
|
-
}
|
|
313
|
-
function paragraphTag(paragraph) {
|
|
314
|
-
const pPr = queryByLocalName(paragraph, "pPr");
|
|
315
|
-
const pStyle = pPr ? queryByLocalName(pPr, "pStyle") : null;
|
|
316
|
-
const val = (getAttr(pStyle, "w:val") ?? getAttr(pStyle, "val") ?? "").toLowerCase();
|
|
317
|
-
if (val.includes("heading1") || val === "1" || val === "heading 1") return "h1";
|
|
318
|
-
if (val.includes("heading2") || val === "2" || val === "heading 2") return "h2";
|
|
319
|
-
if (val.includes("heading3") || val === "3" || val === "heading 3") return "h3";
|
|
320
|
-
return "p";
|
|
321
|
-
}
|
|
322
|
-
function paragraphAlignStyle(paragraph) {
|
|
323
|
-
const pPr = queryByLocalName(paragraph, "pPr");
|
|
324
|
-
const jc = pPr ? queryByLocalName(pPr, "jc") : null;
|
|
325
|
-
const align = (getAttr(jc, "w:val") ?? getAttr(jc, "val") ?? "").toLowerCase();
|
|
326
|
-
if (align === "center" || align === "right" || align === "left") {
|
|
327
|
-
return `text-align:${align};`;
|
|
328
|
-
}
|
|
329
|
-
return "";
|
|
330
|
-
}
|
|
331
|
-
function paragraphDataAttr(paragraphIndex) {
|
|
332
|
-
return paragraphIndex === null ? "" : ` data-word-p-index="${paragraphIndex}"`;
|
|
333
|
-
}
|
|
334
|
-
function parseFootnotesMap(footnotesXmlText) {
|
|
335
|
-
if (!footnotesXmlText) return {};
|
|
336
|
-
const footnotesDoc = parseXml(footnotesXmlText);
|
|
337
|
-
const map = {};
|
|
338
|
-
const footnotes = queryAllByLocalName(footnotesDoc, "footnote");
|
|
339
|
-
for (const footnote of footnotes) {
|
|
340
|
-
const idRaw = getAttr(footnote, "w:id") ?? getAttr(footnote, "id");
|
|
341
|
-
const idNum = idRaw ? Number.parseInt(idRaw, 10) : Number.NaN;
|
|
342
|
-
if (!Number.isFinite(idNum) || idNum <= 0) continue;
|
|
343
|
-
const paragraphs = queryAllByLocalName(footnote, "p");
|
|
344
|
-
const text = paragraphs.map((p) => paragraphText(p)).join("<br/>").trim();
|
|
345
|
-
if (!text) continue;
|
|
346
|
-
map[String(idNum)] = text;
|
|
347
|
-
}
|
|
348
|
-
return map;
|
|
349
|
-
}
|
|
350
|
-
function parseCommentsMap(commentsXmlText) {
|
|
351
|
-
if (!commentsXmlText) return {};
|
|
352
|
-
const commentsDoc = parseXml(commentsXmlText);
|
|
353
|
-
const map = {};
|
|
354
|
-
const comments = queryAllByLocalName(commentsDoc, "comment");
|
|
355
|
-
for (const comment of comments) {
|
|
356
|
-
const idRaw = getAttr(comment, "w:id") ?? getAttr(comment, "id");
|
|
357
|
-
if (!idRaw) continue;
|
|
358
|
-
const paragraphs = queryAllByLocalName(comment, "p");
|
|
359
|
-
const text = paragraphs.map((p) => paragraphText(p)).join("<br/>").trim();
|
|
360
|
-
if (!text) continue;
|
|
361
|
-
map[idRaw] = {
|
|
362
|
-
author: getAttr(comment, "w:author") ?? getAttr(comment, "author"),
|
|
363
|
-
date: getAttr(comment, "w:date") ?? getAttr(comment, "date"),
|
|
364
|
-
text
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
return map;
|
|
368
|
-
}
|
|
369
|
-
function parseEndnotesMap(endnotesXmlText) {
|
|
370
|
-
if (!endnotesXmlText) return {};
|
|
371
|
-
const endnotesDoc = parseXml(endnotesXmlText);
|
|
372
|
-
const map = {};
|
|
373
|
-
const endnotes = queryAllByLocalName(endnotesDoc, "endnote");
|
|
374
|
-
for (const endnote of endnotes) {
|
|
375
|
-
const idRaw = getAttr(endnote, "w:id") ?? getAttr(endnote, "id");
|
|
376
|
-
const idNum = idRaw ? Number.parseInt(idRaw, 10) : Number.NaN;
|
|
377
|
-
if (!Number.isFinite(idNum) || idNum <= 0) continue;
|
|
378
|
-
const paragraphs = queryAllByLocalName(endnote, "p");
|
|
379
|
-
const text = paragraphs.map((p) => paragraphText(p)).join("<br/>").trim();
|
|
380
|
-
if (!text) continue;
|
|
381
|
-
map[String(idNum)] = text;
|
|
382
|
-
}
|
|
383
|
-
return map;
|
|
384
|
-
}
|
|
385
|
-
function renderFootnotesSection(usedIds, footnotesMap) {
|
|
386
|
-
const uniq = [...new Set(usedIds)].filter((id) => footnotesMap[id]);
|
|
387
|
-
if (uniq.length === 0) return "";
|
|
388
|
-
const items = uniq.map((id) => `<li id="word-footnote-${id}" data-word-footnote-id="${id}">${footnotesMap[id]}</li>`).join("");
|
|
389
|
-
return `<section data-word-footnotes="1"><hr/><ol>${items}</ol></section>`;
|
|
390
|
-
}
|
|
391
|
-
function renderCommentsSection(usedIds, commentsMap) {
|
|
392
|
-
const uniq = [...new Set(usedIds)].filter((id) => commentsMap[id]);
|
|
393
|
-
if (uniq.length === 0) return "";
|
|
394
|
-
const items = uniq.map((id) => {
|
|
395
|
-
const item = commentsMap[id];
|
|
396
|
-
const meta = [item.author ?? "", item.date ?? ""].filter((x) => x.length > 0).join(" \xB7 ");
|
|
397
|
-
const metaHtml = meta ? `<div data-word-comment-meta="1">${escapeHtml(meta)}</div>` : "";
|
|
398
|
-
return `<li id="word-comment-${id}" data-word-comment-id="${id}">${metaHtml}<div>${item.text}</div></li>`;
|
|
399
|
-
}).join("");
|
|
400
|
-
return `<section data-word-comments="1"><hr/><ol>${items}</ol></section>`;
|
|
401
|
-
}
|
|
402
|
-
function renderEndnotesSection(usedIds, endnotesMap) {
|
|
403
|
-
const uniq = [...new Set(usedIds)].filter((id) => endnotesMap[id]);
|
|
404
|
-
if (uniq.length === 0) return "";
|
|
405
|
-
const items = uniq.map((id) => `<li id="word-endnote-${id}" data-word-endnote-id="${id}">${endnotesMap[id]}</li>`).join("");
|
|
406
|
-
return `<section data-word-endnotes="1"><hr/><ol>${items}</ol></section>`;
|
|
407
|
-
}
|
|
408
|
-
async function paragraphToHtml(zip, relMap, paragraph, paragraphIndex, footnotesMap, usedFootnoteIds, endnotesMap, usedEndnoteIds, commentsMap, usedCommentIds) {
|
|
409
|
-
const tag = paragraphTag(paragraph);
|
|
410
|
-
const alignStyle = paragraphAlignStyle(paragraph);
|
|
411
|
-
const dataAttr = paragraphDataAttr(paragraphIndex);
|
|
412
|
-
const hasRenderableNode = queryAllByLocalName(paragraph, "r").length > 0 || queryAllByLocalName(paragraph, "oMath").length > 0 || queryAllByLocalName(paragraph, "oMathPara").length > 0;
|
|
413
|
-
if (!hasRenderableNode) {
|
|
414
|
-
return `<${tag}${dataAttr}${alignStyle ? ` style="${alignStyle}"` : ""}><br/></${tag}>`;
|
|
415
|
-
}
|
|
416
|
-
function parseRevisionMeta(node, type) {
|
|
417
|
-
return {
|
|
418
|
-
type,
|
|
419
|
-
id: getAttr(node, "w:id") ?? getAttr(node, "id"),
|
|
420
|
-
author: getAttr(node, "w:author") ?? getAttr(node, "author"),
|
|
421
|
-
date: getAttr(node, "w:date") ?? getAttr(node, "date")
|
|
422
|
-
};
|
|
423
|
-
}
|
|
424
|
-
function inferRevisionMeta(run, fallback) {
|
|
425
|
-
if (fallback) return fallback;
|
|
426
|
-
let cursor = run;
|
|
427
|
-
while (cursor) {
|
|
428
|
-
if (cursor.localName === "ins") return parseRevisionMeta(cursor, "ins");
|
|
429
|
-
if (cursor.localName === "del") return parseRevisionMeta(cursor, "del");
|
|
430
|
-
if (cursor.localName === "p") break;
|
|
431
|
-
cursor = cursor.parentElement;
|
|
432
|
-
}
|
|
433
|
-
return null;
|
|
434
|
-
}
|
|
435
|
-
function revisionMetaAttrs(meta) {
|
|
436
|
-
const attrs = [`data-word-revision="${meta.type}"`];
|
|
437
|
-
if (meta.id) attrs.push(`data-word-revision-id="${escapeHtml(meta.id)}"`);
|
|
438
|
-
if (meta.author) attrs.push(`data-word-revision-author="${escapeHtml(meta.author)}"`);
|
|
439
|
-
if (meta.date) attrs.push(`data-word-revision-date="${escapeHtml(meta.date)}"`);
|
|
440
|
-
return attrs.join(" ");
|
|
441
|
-
}
|
|
442
|
-
async function runToHtml(run, revisionFallback) {
|
|
443
|
-
const result = [];
|
|
444
|
-
const rPr = queryByLocalName(run, "rPr");
|
|
445
|
-
const css = runStyleToCss(rPr);
|
|
446
|
-
const footnoteRef = queryByLocalName(run, "footnoteReference");
|
|
447
|
-
const footnoteId = getAttr(footnoteRef, "w:id") ?? getAttr(footnoteRef, "id");
|
|
448
|
-
if (footnoteId && footnotesMap[footnoteId]) {
|
|
449
|
-
usedFootnoteIds.push(footnoteId);
|
|
450
|
-
result.push(
|
|
451
|
-
`<sup data-word-footnote-ref="${footnoteId}"><a href="#word-footnote-${footnoteId}">[${footnoteId}]</a></sup>`
|
|
452
|
-
);
|
|
453
|
-
return result;
|
|
454
|
-
}
|
|
455
|
-
const endnoteRef = queryByLocalName(run, "endnoteReference");
|
|
456
|
-
const endnoteId = getAttr(endnoteRef, "w:id") ?? getAttr(endnoteRef, "id");
|
|
457
|
-
if (endnoteId && endnotesMap[endnoteId]) {
|
|
458
|
-
usedEndnoteIds.push(endnoteId);
|
|
459
|
-
result.push(
|
|
460
|
-
`<sup data-word-endnote-ref="${endnoteId}"><a href="#word-endnote-${endnoteId}">[${endnoteId}]</a></sup>`
|
|
461
|
-
);
|
|
462
|
-
return result;
|
|
463
|
-
}
|
|
464
|
-
const commentRef = queryByLocalName(run, "commentReference");
|
|
465
|
-
const commentId = getAttr(commentRef, "w:id") ?? getAttr(commentRef, "id");
|
|
466
|
-
if (commentId && commentsMap[commentId]) {
|
|
467
|
-
usedCommentIds.push(commentId);
|
|
468
|
-
result.push(
|
|
469
|
-
`<sup data-word-comment-ref="${commentId}"><a href="#word-comment-${commentId}">[c${commentId}]</a></sup>`
|
|
470
|
-
);
|
|
471
|
-
return result;
|
|
472
|
-
}
|
|
473
|
-
const drawing = queryByLocalName(run, "drawing");
|
|
474
|
-
if (drawing) {
|
|
475
|
-
const blip = queryByLocalName(drawing, "blip");
|
|
476
|
-
const rid = getAttr(blip, "r:embed") ?? getAttr(blip, "embed");
|
|
477
|
-
if (rid) {
|
|
478
|
-
const src = await imageRidToDataUrl(zip, relMap, rid);
|
|
479
|
-
if (src) {
|
|
480
|
-
const imageSize = parseDrawingSizePx(drawing);
|
|
481
|
-
const dimensionAttrs = imageDimensionAttributes(imageSize);
|
|
482
|
-
const anchorMeta = parseAnchorMeta(drawing);
|
|
483
|
-
const attrs = mergeImageStyle(dimensionAttrs, anchorMeta);
|
|
484
|
-
result.push(`<img src="${src}" alt="word-image"${attrs}/>`);
|
|
485
|
-
return result;
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
const chartRef = queryByLocalName(drawing, "chart");
|
|
489
|
-
const chartRid = getAttr(chartRef, "r:id") ?? getAttr(chartRef, "id");
|
|
490
|
-
if (chartRid) {
|
|
491
|
-
const chartXmlText = await readXmlByRid(zip, relMap, chartRid);
|
|
492
|
-
if (chartXmlText) {
|
|
493
|
-
const summary = parseChartSummary(chartXmlText);
|
|
494
|
-
result.push(
|
|
495
|
-
`<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>`
|
|
496
|
-
);
|
|
497
|
-
return result;
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
const smartArtRef = queryByLocalName(drawing, "relIds");
|
|
501
|
-
const smartArtRid = getAttr(smartArtRef, "r:dm") ?? getAttr(smartArtRef, "dm");
|
|
502
|
-
if (smartArtRid) {
|
|
503
|
-
const diagramXmlText = await readXmlByRid(zip, relMap, smartArtRid);
|
|
504
|
-
const textItems = diagramXmlText ? extractSmartArtText(diagramXmlText) : [];
|
|
505
|
-
const preview = textItems.length > 0 ? `: ${escapeHtml(textItems.join(" / "))}` : "";
|
|
506
|
-
result.push(
|
|
507
|
-
`<figure data-word-smartart="1" data-word-smartart-items="${textItems.length}"><figcaption>SmartArt fallback${preview}</figcaption></figure>`
|
|
508
|
-
);
|
|
509
|
-
return result;
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
const texts = queryAllByLocalName(run, "t").map((t) => t.textContent ?? "").join("");
|
|
513
|
-
const delTexts = queryAllByLocalName(run, "delText").map((t) => t.textContent ?? "").join("");
|
|
514
|
-
const brNodes = queryAllByLocalName(run, "br");
|
|
515
|
-
const pageBreakCount = brNodes.filter((node) => {
|
|
516
|
-
const type = (getAttr(node, "w:type") ?? getAttr(node, "type") ?? "").toLowerCase();
|
|
517
|
-
return type === "page";
|
|
518
|
-
}).length;
|
|
519
|
-
const lineBreakCount = Math.max(0, brNodes.length - pageBreakCount);
|
|
520
|
-
const runText2 = `${escapeHtml(texts || delTexts)}${"<br/>".repeat(lineBreakCount)}`;
|
|
521
|
-
if (runText2) {
|
|
522
|
-
const revisionMeta = inferRevisionMeta(run, revisionFallback);
|
|
523
|
-
if (css) {
|
|
524
|
-
const span = `<span style="${css}">${runText2}</span>`;
|
|
525
|
-
if (revisionMeta) {
|
|
526
|
-
const tagName = revisionMeta.type === "ins" ? "ins" : "del";
|
|
527
|
-
result.push(`<${tagName} ${revisionMetaAttrs(revisionMeta)}>${span}</${tagName}>`);
|
|
528
|
-
} else {
|
|
529
|
-
result.push(span);
|
|
530
|
-
}
|
|
531
|
-
} else if (revisionMeta) {
|
|
532
|
-
const tagName = revisionMeta.type === "ins" ? "ins" : "del";
|
|
533
|
-
result.push(`<${tagName} ${revisionMetaAttrs(revisionMeta)}>${runText2}</${tagName}>`);
|
|
534
|
-
} else {
|
|
535
|
-
result.push(runText2);
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
for (let i = 0; i < pageBreakCount; i += 1) {
|
|
539
|
-
result.push(`<span data-word-page-break="1" style="display:block;break-before:page"></span>`);
|
|
540
|
-
}
|
|
541
|
-
return result;
|
|
542
|
-
}
|
|
543
|
-
async function nodeToHtml(node, revisionFallback) {
|
|
544
|
-
if (node.localName === "commentRangeStart") {
|
|
545
|
-
const id = getAttr(node, "w:id") ?? getAttr(node, "id");
|
|
546
|
-
return id ? [`<span data-word-comment-range-start="${id}"></span>`] : [];
|
|
547
|
-
}
|
|
548
|
-
if (node.localName === "commentRangeEnd") {
|
|
549
|
-
const id = getAttr(node, "w:id") ?? getAttr(node, "id");
|
|
550
|
-
return id ? [`<span data-word-comment-range-end="${id}"></span>`] : [];
|
|
551
|
-
}
|
|
552
|
-
if (node.localName === "r") {
|
|
553
|
-
return runToHtml(node, revisionFallback);
|
|
554
|
-
}
|
|
555
|
-
if (node.localName === "oMath" || node.localName === "oMathPara") {
|
|
556
|
-
const linear = ommlNodeToText(node).trim();
|
|
557
|
-
if (!linear) return [];
|
|
558
|
-
return [`<span data-word-omml="1">${escapeHtml(linear)}</span>`];
|
|
559
|
-
}
|
|
560
|
-
if (node.localName === "ins" || node.localName === "del") {
|
|
561
|
-
const scopedMeta = parseRevisionMeta(node, node.localName === "ins" ? "ins" : "del");
|
|
562
|
-
const nested2 = [];
|
|
563
|
-
for (const child of Array.from(node.children)) {
|
|
564
|
-
nested2.push(...await nodeToHtml(child, scopedMeta));
|
|
565
|
-
}
|
|
566
|
-
return nested2;
|
|
567
|
-
}
|
|
568
|
-
const nested = [];
|
|
569
|
-
for (const child of Array.from(node.children)) {
|
|
570
|
-
nested.push(...await nodeToHtml(child, revisionFallback));
|
|
571
|
-
}
|
|
572
|
-
return nested;
|
|
573
|
-
}
|
|
574
|
-
const parts = [];
|
|
575
|
-
const renderedPageBreakCount = queryAllByLocalName(paragraph, "lastRenderedPageBreak").length;
|
|
576
|
-
for (let i = 0; i < renderedPageBreakCount; i += 1) {
|
|
577
|
-
parts.push(`<span data-word-page-break="1" style="display:block;break-before:page"></span>`);
|
|
578
|
-
}
|
|
579
|
-
for (const child of Array.from(paragraph.children)) {
|
|
580
|
-
parts.push(...await nodeToHtml(child, null));
|
|
581
|
-
}
|
|
582
|
-
const content = parts.join("") || "<br/>";
|
|
583
|
-
return `<${tag}${dataAttr}${alignStyle ? ` style="${alignStyle}"` : ""}>${content}</${tag}>`;
|
|
584
|
-
}
|
|
585
|
-
function runText(run) {
|
|
586
|
-
const text = queryAllByLocalName(run, "t").map((t) => t.textContent ?? "").join("");
|
|
587
|
-
const delText = queryAllByLocalName(run, "delText").map((t) => t.textContent ?? "").join("");
|
|
588
|
-
const brCount = queryAllByLocalName(run, "br").length;
|
|
589
|
-
return `${escapeHtml(text || delText)}${"<br/>".repeat(brCount)}`;
|
|
590
|
-
}
|
|
591
|
-
function paragraphText(paragraph) {
|
|
592
|
-
const runs = queryAllByLocalName(paragraph, "r");
|
|
593
|
-
const content = runs.map((run) => runText(run)).join("");
|
|
594
|
-
return content || "<br/>";
|
|
595
|
-
}
|
|
596
|
-
function parseTcGridSpan(tc) {
|
|
597
|
-
const tcPr = directChildrenByLocalName(tc, "tcPr")[0] ?? null;
|
|
598
|
-
const gridSpan = tcPr ? directChildrenByLocalName(tcPr, "gridSpan")[0] ?? null : null;
|
|
599
|
-
const rawVal = getAttr(gridSpan, "w:val") ?? getAttr(gridSpan, "val");
|
|
600
|
-
const parsed = rawVal ? Number.parseInt(rawVal, 10) : Number.NaN;
|
|
601
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : 1;
|
|
602
|
-
}
|
|
603
|
-
function parseTcVMerge(tc) {
|
|
604
|
-
const tcPr = directChildrenByLocalName(tc, "tcPr")[0] ?? null;
|
|
605
|
-
const vMerge = tcPr ? directChildrenByLocalName(tcPr, "vMerge")[0] ?? null : null;
|
|
606
|
-
if (!vMerge) return "none";
|
|
607
|
-
const rawVal = (getAttr(vMerge, "w:val") ?? getAttr(vMerge, "val") ?? "continue").toLowerCase();
|
|
608
|
-
return rawVal === "restart" ? "restart" : "continue";
|
|
609
|
-
}
|
|
610
|
-
function parseTblGridWidthsPx(table) {
|
|
611
|
-
const grid = directChildrenByLocalName(table, "tblGrid")[0] ?? null;
|
|
612
|
-
if (!grid) return [];
|
|
613
|
-
return directChildrenByLocalName(grid, "gridCol").map((col) => {
|
|
614
|
-
const raw = getAttr(col, "w:w") ?? getAttr(col, "w");
|
|
615
|
-
const twip = raw ? Number.parseInt(raw, 10) : Number.NaN;
|
|
616
|
-
return Number.isFinite(twip) && twip > 0 ? twipToPx(twip) : 0;
|
|
617
|
-
}).filter((px) => px > 0);
|
|
618
|
-
}
|
|
619
|
-
function borderSizeToPx(size) {
|
|
620
|
-
return size / 6;
|
|
621
|
-
}
|
|
622
|
-
function parseBorderCss(borderNode) {
|
|
623
|
-
if (!borderNode) return null;
|
|
624
|
-
const val = (getAttr(borderNode, "w:val") ?? getAttr(borderNode, "val") ?? "").toLowerCase();
|
|
625
|
-
if (!val || val === "nil" || val === "none") return "none";
|
|
626
|
-
const color = (getAttr(borderNode, "w:color") ?? getAttr(borderNode, "color") ?? "222222").replace(/^#/, "");
|
|
627
|
-
const rawSize = getAttr(borderNode, "w:sz") ?? getAttr(borderNode, "sz");
|
|
628
|
-
const size = rawSize ? Number.parseInt(rawSize, 10) : Number.NaN;
|
|
629
|
-
const px = Number.isFinite(size) && size > 0 ? borderSizeToPx(size) : 1;
|
|
630
|
-
const style = val === "single" ? "solid" : val;
|
|
631
|
-
return `${px.toFixed(2)}px ${style} #${color}`;
|
|
632
|
-
}
|
|
633
|
-
function parseTableStyleProfile(table) {
|
|
634
|
-
const tblPr = directChildrenByLocalName(table, "tblPr")[0] ?? null;
|
|
635
|
-
const tblBorders = tblPr ? directChildrenByLocalName(tblPr, "tblBorders")[0] ?? null : null;
|
|
636
|
-
const layout = tblPr ? directChildrenByLocalName(tblPr, "tblLayout")[0] ?? null : null;
|
|
637
|
-
const spacing = tblPr ? directChildrenByLocalName(tblPr, "tblCellSpacing")[0] ?? null : null;
|
|
638
|
-
const spacingType = (getAttr(spacing, "w:type") ?? getAttr(spacing, "type") ?? "dxa").toLowerCase();
|
|
639
|
-
const spacingRaw = getAttr(spacing, "w:w") ?? getAttr(spacing, "w");
|
|
640
|
-
const spacingVal = spacingRaw ? Number.parseFloat(spacingRaw) : Number.NaN;
|
|
641
|
-
const borderSpacingPx = spacingType === "dxa" && Number.isFinite(spacingVal) && spacingVal > 0 ? twipToPx(spacingVal) : 0;
|
|
642
|
-
const borderCollapse = borderSpacingPx > 0 ? "separate" : "collapse";
|
|
643
|
-
const tableLayout = (getAttr(layout, "w:type") ?? getAttr(layout, "type") ?? "").toLowerCase() === "autofit" ? "auto" : "fixed";
|
|
644
|
-
const top = parseBorderCss(tblBorders ? directChildrenByLocalName(tblBorders, "top")[0] ?? null : null);
|
|
645
|
-
const bottom = parseBorderCss(tblBorders ? directChildrenByLocalName(tblBorders, "bottom")[0] ?? null : null);
|
|
646
|
-
const left = parseBorderCss(tblBorders ? directChildrenByLocalName(tblBorders, "left")[0] ?? null : null);
|
|
647
|
-
const right = parseBorderCss(tblBorders ? directChildrenByLocalName(tblBorders, "right")[0] ?? null : null);
|
|
648
|
-
const insideH = parseBorderCss(tblBorders ? directChildrenByLocalName(tblBorders, "insideH")[0] ?? null : null);
|
|
649
|
-
const insideV = parseBorderCss(tblBorders ? directChildrenByLocalName(tblBorders, "insideV")[0] ?? null : null);
|
|
650
|
-
const borderCss = top ?? right ?? bottom ?? left ?? "1px solid #222";
|
|
651
|
-
return {
|
|
652
|
-
tableLayout,
|
|
653
|
-
borderCollapse,
|
|
654
|
-
borderSpacingPx,
|
|
655
|
-
borderCss,
|
|
656
|
-
insideHCss: insideH,
|
|
657
|
-
insideVCss: insideV
|
|
658
|
-
};
|
|
659
|
-
}
|
|
660
|
-
function parseTableWidthStyle(table, gridWidthsPx) {
|
|
661
|
-
const tblPr = directChildrenByLocalName(table, "tblPr")[0] ?? null;
|
|
662
|
-
const tblW = tblPr ? directChildrenByLocalName(tblPr, "tblW")[0] ?? null : null;
|
|
663
|
-
const type = (getAttr(tblW, "w:type") ?? getAttr(tblW, "type") ?? "").toLowerCase();
|
|
664
|
-
const rawVal = getAttr(tblW, "w:w") ?? getAttr(tblW, "w");
|
|
665
|
-
const numericVal = rawVal ? Number.parseFloat(rawVal) : Number.NaN;
|
|
666
|
-
if (type === "dxa" && Number.isFinite(numericVal) && numericVal > 0) {
|
|
667
|
-
return `width:${twipToPx(numericVal).toFixed(2)}px`;
|
|
668
|
-
}
|
|
669
|
-
if (type === "pct" && Number.isFinite(numericVal) && numericVal > 0) {
|
|
670
|
-
return `width:${(numericVal / 50).toFixed(2)}%`;
|
|
671
|
-
}
|
|
672
|
-
const gridTotal = gridWidthsPx.reduce((sum, item) => sum + item, 0);
|
|
673
|
-
if (gridTotal > 0) return `width:${gridTotal.toFixed(2)}px;max-width:100%`;
|
|
674
|
-
return "width:100%";
|
|
675
|
-
}
|
|
676
|
-
function parseCellWidthStyle(cell, colCursor, colSpan, gridWidthsPx) {
|
|
677
|
-
const tcPr = directChildrenByLocalName(cell, "tcPr")[0] ?? null;
|
|
678
|
-
const tcW = tcPr ? directChildrenByLocalName(tcPr, "tcW")[0] ?? null : null;
|
|
679
|
-
const type = (getAttr(tcW, "w:type") ?? getAttr(tcW, "type") ?? "").toLowerCase();
|
|
680
|
-
const rawVal = getAttr(tcW, "w:w") ?? getAttr(tcW, "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 width = gridWidthsPx.slice(colCursor, colCursor + colSpan).reduce((sum, item) => sum + item, 0);
|
|
689
|
-
if (width > 0) return `width:${width.toFixed(2)}px`;
|
|
690
|
-
return "";
|
|
691
|
-
}
|
|
692
|
-
function parseCellBorderStyle(cell, tableStyle) {
|
|
693
|
-
const tcPr = directChildrenByLocalName(cell, "tcPr")[0] ?? null;
|
|
694
|
-
const tcBorders = tcPr ? directChildrenByLocalName(tcPr, "tcBorders")[0] ?? null : null;
|
|
695
|
-
if (!tcBorders) {
|
|
696
|
-
const fallback = tableStyle.insideHCss ?? tableStyle.insideVCss ?? tableStyle.borderCss;
|
|
697
|
-
return `border:${fallback}`;
|
|
698
|
-
}
|
|
699
|
-
const top = parseBorderCss(directChildrenByLocalName(tcBorders, "top")[0] ?? null) ?? tableStyle.insideHCss ?? tableStyle.borderCss;
|
|
700
|
-
const right = parseBorderCss(directChildrenByLocalName(tcBorders, "right")[0] ?? null) ?? tableStyle.insideVCss ?? tableStyle.borderCss;
|
|
701
|
-
const bottom = parseBorderCss(directChildrenByLocalName(tcBorders, "bottom")[0] ?? null) ?? tableStyle.insideHCss ?? tableStyle.borderCss;
|
|
702
|
-
const left = parseBorderCss(directChildrenByLocalName(tcBorders, "left")[0] ?? null) ?? tableStyle.insideVCss ?? tableStyle.borderCss;
|
|
703
|
-
return `border-top:${top};border-right:${right};border-bottom:${bottom};border-left:${left}`;
|
|
704
|
-
}
|
|
705
|
-
function tableCellHtml(cell, paragraphIndexMap) {
|
|
706
|
-
const blocks = [];
|
|
707
|
-
for (const child of Array.from(cell.children)) {
|
|
708
|
-
if (child.localName === "tcPr") continue;
|
|
709
|
-
if (child.localName === "p") {
|
|
710
|
-
const paragraphIndex = paragraphIndexMap.get(child) ?? null;
|
|
711
|
-
blocks.push(`<p${paragraphDataAttr(paragraphIndex)}>${paragraphText(child)}</p>`);
|
|
712
|
-
continue;
|
|
713
|
-
}
|
|
714
|
-
if (child.localName === "tbl") {
|
|
715
|
-
blocks.push(tableToHtml(child, paragraphIndexMap));
|
|
716
|
-
continue;
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
if (blocks.length > 0) return blocks.join("");
|
|
720
|
-
const text = queryAllByLocalName(cell, "t").map((t) => t.textContent ?? "").join("").trim();
|
|
721
|
-
return escapeHtml(text) || "<br/>";
|
|
722
|
-
}
|
|
723
|
-
function tableToHtml(table, paragraphIndexMap) {
|
|
724
|
-
const rows = directChildrenByLocalName(table, "tr");
|
|
725
|
-
const gridWidthsPx = parseTblGridWidthsPx(table);
|
|
726
|
-
const tableStyle = parseTableStyleProfile(table);
|
|
727
|
-
const activeByCol = /* @__PURE__ */ new Map();
|
|
728
|
-
const allOrigins = [];
|
|
729
|
-
let nextOriginId = 1;
|
|
730
|
-
const htmlRows = rows.map((row, rowIndex) => {
|
|
731
|
-
const directCells = directChildrenByLocalName(row, "tc");
|
|
732
|
-
const continued = /* @__PURE__ */ new Set();
|
|
733
|
-
const emittedCells = [];
|
|
734
|
-
let colCursor = 0;
|
|
735
|
-
for (const cell of directCells) {
|
|
736
|
-
const colSpan = parseTcGridSpan(cell);
|
|
737
|
-
const vMerge = parseTcVMerge(cell);
|
|
738
|
-
if (vMerge === "continue") {
|
|
739
|
-
const activeOrigins = Array.from(new Set(activeByCol.values())).filter((origin2) => !continued.has(origin2)).sort((a, b) => a.startCol - b.startCol);
|
|
740
|
-
const origin = activeOrigins.find((item) => item.startCol >= colCursor) ?? activeOrigins[0] ?? null;
|
|
741
|
-
if (origin) {
|
|
742
|
-
origin.rowSpan += 1;
|
|
743
|
-
continued.add(origin);
|
|
744
|
-
colCursor = origin.startCol + origin.colSpan;
|
|
745
|
-
}
|
|
746
|
-
continue;
|
|
747
|
-
}
|
|
748
|
-
while (activeByCol.has(colCursor)) {
|
|
749
|
-
colCursor += 1;
|
|
750
|
-
}
|
|
751
|
-
const html = tableCellHtml(cell, paragraphIndexMap);
|
|
752
|
-
const attrs = [];
|
|
753
|
-
const widthStyle = parseCellWidthStyle(cell, colCursor, colSpan, gridWidthsPx);
|
|
754
|
-
const borderStyle = parseCellBorderStyle(cell, tableStyle);
|
|
755
|
-
if (vMerge === "restart") {
|
|
756
|
-
const origin = {
|
|
757
|
-
id: `m${nextOriginId}`,
|
|
758
|
-
startCol: colCursor,
|
|
759
|
-
colSpan,
|
|
760
|
-
rowSpan: 1,
|
|
761
|
-
startedRow: rowIndex
|
|
762
|
-
};
|
|
763
|
-
nextOriginId += 1;
|
|
764
|
-
allOrigins.push(origin);
|
|
765
|
-
for (let i = 0; i < colSpan; i += 1) {
|
|
766
|
-
activeByCol.set(colCursor + i, origin);
|
|
767
|
-
}
|
|
768
|
-
attrs.push(`data-word-merge-id="${origin.id}"`);
|
|
769
|
-
}
|
|
770
|
-
if (colSpan > 1) attrs.push(`colspan="${colSpan}"`);
|
|
771
|
-
emittedCells.push(
|
|
772
|
-
`<td${attrs.length > 0 ? ` ${attrs.join(" ")}` : ""} style="${borderStyle};vertical-align:top;${widthStyle}">${html}</td>`
|
|
773
|
-
);
|
|
774
|
-
colCursor += colSpan;
|
|
775
|
-
}
|
|
776
|
-
for (const origin of Array.from(new Set(activeByCol.values()))) {
|
|
777
|
-
if (origin.startedRow < rowIndex && !continued.has(origin)) {
|
|
778
|
-
for (let i = 0; i < origin.colSpan; i += 1) {
|
|
779
|
-
activeByCol.delete(origin.startCol + i);
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
return `<tr>${emittedCells.join("")}</tr>`;
|
|
784
|
-
});
|
|
785
|
-
let merged = htmlRows.join("");
|
|
786
|
-
for (const origin of allOrigins) {
|
|
787
|
-
const marker = `data-word-merge-id="${origin.id}"`;
|
|
788
|
-
const replacement = origin.rowSpan > 1 ? `rowspan="${origin.rowSpan}"` : "";
|
|
789
|
-
merged = merged.replace(marker, replacement).replace(/\s{2,}/g, " ");
|
|
790
|
-
}
|
|
791
|
-
const tableWidthStyle = parseTableWidthStyle(table, gridWidthsPx);
|
|
792
|
-
const spacing = tableStyle.borderSpacingPx > 0 ? `border-spacing:${tableStyle.borderSpacingPx.toFixed(2)}px;` : "";
|
|
793
|
-
return `<table style="border-collapse:${tableStyle.borderCollapse};${spacing}table-layout:${tableStyle.tableLayout};${tableWidthStyle};border:${tableStyle.borderCss};">${merged}</table>`;
|
|
794
|
-
}
|
|
795
|
-
async function parseDocxToHtmlSnapshot(file) {
|
|
796
|
-
const maybeArrayBuffer = file.arrayBuffer;
|
|
797
|
-
const buffer = maybeArrayBuffer ? await maybeArrayBuffer.call(file) : await new Response(file).arrayBuffer();
|
|
798
|
-
const zip = await import_jszip.default.loadAsync(buffer);
|
|
799
|
-
const documentXmlText = await zip.file("word/document.xml")?.async("string");
|
|
800
|
-
if (!documentXmlText) {
|
|
801
|
-
throw new Error("DOCX missing document.xml");
|
|
802
|
-
}
|
|
803
|
-
const relsText = await zip.file("word/_rels/document.xml.rels")?.async("string");
|
|
804
|
-
const footnotesText = await zip.file("word/footnotes.xml")?.async("string");
|
|
805
|
-
const endnotesText = await zip.file("word/endnotes.xml")?.async("string");
|
|
806
|
-
const commentsText = await zip.file("word/comments.xml")?.async("string");
|
|
807
|
-
const relMap = parseDocRelsMap(relsText ?? null);
|
|
808
|
-
const footnotesMap = parseFootnotesMap(footnotesText ?? null);
|
|
809
|
-
const endnotesMap = parseEndnotesMap(endnotesText ?? null);
|
|
810
|
-
const commentsMap = parseCommentsMap(commentsText ?? null);
|
|
811
|
-
const usedFootnoteIds = [];
|
|
812
|
-
const usedEndnoteIds = [];
|
|
813
|
-
const usedCommentIds = [];
|
|
814
|
-
const documentXml = parseXml(documentXmlText);
|
|
815
|
-
const body = queryByLocalName(documentXml, "body");
|
|
816
|
-
if (!body) {
|
|
817
|
-
throw new Error("DOCX missing body");
|
|
818
|
-
}
|
|
819
|
-
const paragraphIndexMap = /* @__PURE__ */ new Map();
|
|
820
|
-
queryAllByLocalName(documentXml, "p").forEach((paragraph, index) => {
|
|
821
|
-
paragraphIndexMap.set(paragraph, index);
|
|
822
|
-
});
|
|
823
|
-
const blockHtml = [];
|
|
824
|
-
for (const child of Array.from(body.children)) {
|
|
825
|
-
if (child.localName === "sectPr") continue;
|
|
826
|
-
if (child.localName === "p") {
|
|
827
|
-
const paragraphIndex = paragraphIndexMap.get(child) ?? null;
|
|
828
|
-
blockHtml.push(
|
|
829
|
-
await paragraphToHtml(
|
|
830
|
-
zip,
|
|
831
|
-
relMap,
|
|
832
|
-
child,
|
|
833
|
-
paragraphIndex,
|
|
834
|
-
footnotesMap,
|
|
835
|
-
usedFootnoteIds,
|
|
836
|
-
endnotesMap,
|
|
837
|
-
usedEndnoteIds,
|
|
838
|
-
commentsMap,
|
|
839
|
-
usedCommentIds
|
|
840
|
-
)
|
|
841
|
-
);
|
|
842
|
-
continue;
|
|
843
|
-
}
|
|
844
|
-
if (child.localName === "tbl") {
|
|
845
|
-
blockHtml.push(tableToHtml(child, paragraphIndexMap));
|
|
846
|
-
continue;
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
blockHtml.push(renderFootnotesSection(usedFootnoteIds, footnotesMap));
|
|
850
|
-
blockHtml.push(renderEndnotesSection(usedEndnoteIds, endnotesMap));
|
|
851
|
-
blockHtml.push(renderCommentsSection(usedCommentIds, commentsMap));
|
|
852
|
-
return buildHtmlSnapshot(blockHtml.join("\n"));
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
// src/lib/pastePipeline.ts
|
|
856
|
-
function escapeAttr(value) {
|
|
857
|
-
return value.replaceAll("&", "&").replaceAll('"', """);
|
|
858
|
-
}
|
|
859
|
-
async function fileToDataUrl(file) {
|
|
860
|
-
const buffer = await new Response(file).arrayBuffer();
|
|
861
|
-
const bytes = new Uint8Array(buffer);
|
|
862
|
-
let binary = "";
|
|
863
|
-
for (const b of bytes) binary += String.fromCharCode(b);
|
|
864
|
-
const base64 = btoa(binary);
|
|
865
|
-
const mime = file.type || "application/octet-stream";
|
|
866
|
-
return `data:${mime};base64,${base64}`;
|
|
867
|
-
}
|
|
868
|
-
function isUnstableImageSrc(src) {
|
|
869
|
-
const normalized = src.trim().toLowerCase();
|
|
870
|
-
return normalized.startsWith("file:") || normalized.startsWith("blob:") || normalized.startsWith("cid:") || normalized.startsWith("mhtml:") || normalized.startsWith("ms-appx:") || normalized.startsWith("ms-appdata:");
|
|
871
|
-
}
|
|
872
|
-
async function replaceUnstableImageSrc(rawHtml, imageFiles) {
|
|
873
|
-
if (!rawHtml.trim() || imageFiles.length === 0) return rawHtml;
|
|
874
|
-
const parser = new DOMParser();
|
|
875
|
-
const doc = parser.parseFromString(rawHtml, "text/html");
|
|
876
|
-
const images = Array.from(doc.querySelectorAll("img"));
|
|
877
|
-
const replacementDataUrls = await Promise.all(imageFiles.map((file) => fileToDataUrl(file)));
|
|
878
|
-
let replacementIndex = 0;
|
|
879
|
-
for (const img of images) {
|
|
880
|
-
const src = img.getAttribute("src") ?? "";
|
|
881
|
-
if (isUnstableImageSrc(src) && replacementIndex < replacementDataUrls.length) {
|
|
882
|
-
img.setAttribute("src", replacementDataUrls[replacementIndex]);
|
|
883
|
-
replacementIndex += 1;
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
return doc.body.innerHTML;
|
|
887
|
-
}
|
|
888
|
-
async function buildImageOnlyHtml(imageFiles) {
|
|
889
|
-
if (imageFiles.length === 0) return "";
|
|
890
|
-
const urls = await Promise.all(imageFiles.map((f) => fileToDataUrl(f)));
|
|
891
|
-
return urls.map((url) => `<p><img src="${escapeAttr(url)}" alt="clipboard-image" /></p>`).join("\n");
|
|
892
|
-
}
|
|
893
|
-
async function extractFromClipboardDataTransfer(dataTransfer) {
|
|
894
|
-
const html = dataTransfer.getData("text/html") || "";
|
|
895
|
-
const text = dataTransfer.getData("text/plain") || "";
|
|
896
|
-
const imageFiles = Array.from(dataTransfer.items).filter((item) => item.kind === "file" && item.type.startsWith("image/")).map((item) => item.getAsFile()).filter((file) => file !== null);
|
|
897
|
-
const hydratedHtml = await replaceUnstableImageSrc(html, imageFiles);
|
|
898
|
-
const finalHtml = hydratedHtml.trim() ? hydratedHtml : await buildImageOnlyHtml(imageFiles);
|
|
899
|
-
return {
|
|
900
|
-
html: finalHtml,
|
|
901
|
-
text,
|
|
902
|
-
imageFiles
|
|
903
|
-
};
|
|
904
|
-
}
|
|
905
|
-
async function extractFromClipboardItems(items) {
|
|
906
|
-
let html = "";
|
|
907
|
-
let text = "";
|
|
908
|
-
const imageFiles = [];
|
|
909
|
-
for (const item of items) {
|
|
910
|
-
if (item.types.includes("text/html") && !html) {
|
|
911
|
-
const blob = await item.getType("text/html");
|
|
912
|
-
html = await blob.text();
|
|
913
|
-
}
|
|
914
|
-
if (item.types.includes("text/plain") && !text) {
|
|
915
|
-
const blob = await item.getType("text/plain");
|
|
916
|
-
text = await blob.text();
|
|
917
|
-
}
|
|
918
|
-
for (const type of item.types) {
|
|
919
|
-
if (!type.startsWith("image/")) continue;
|
|
920
|
-
const blob = await item.getType(type);
|
|
921
|
-
const name = `clipboard-${Date.now()}-${imageFiles.length}.${type.split("/")[1] ?? "bin"}`;
|
|
922
|
-
imageFiles.push(new File([blob], name, { type }));
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
const hydratedHtml = await replaceUnstableImageSrc(html, imageFiles);
|
|
926
|
-
const finalHtml = hydratedHtml.trim() ? hydratedHtml : await buildImageOnlyHtml(imageFiles);
|
|
927
|
-
return {
|
|
928
|
-
html: finalHtml,
|
|
929
|
-
text,
|
|
930
|
-
imageFiles
|
|
931
|
-
};
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
// src/lib/htmlCompat.ts
|
|
935
|
-
function parseStyle(styleText) {
|
|
936
|
-
const map = /* @__PURE__ */ new Map();
|
|
937
|
-
for (const seg of styleText.split(";")) {
|
|
938
|
-
const [rawKey, ...rawValue] = seg.split(":");
|
|
939
|
-
const key = rawKey?.trim().toLowerCase();
|
|
940
|
-
const value = rawValue.join(":").trim();
|
|
941
|
-
if (!key || !value) continue;
|
|
942
|
-
map.set(key, value);
|
|
943
|
-
}
|
|
944
|
-
return map;
|
|
945
|
-
}
|
|
946
|
-
function serializeStyle(styleMap) {
|
|
947
|
-
return Array.from(styleMap.entries()).map(([k, v]) => `${k}: ${v}`).join("; ");
|
|
948
|
-
}
|
|
949
|
-
function applyMsoMappings(styleMap) {
|
|
950
|
-
const mappingPairs = [
|
|
951
|
-
["mso-ansi-font-size", "font-size"],
|
|
952
|
-
["mso-bidi-font-size", "font-size"],
|
|
953
|
-
["mso-hansi-font-size", "font-size"],
|
|
954
|
-
["mso-margin-top-alt", "margin-top"],
|
|
955
|
-
["mso-margin-bottom-alt", "margin-bottom"],
|
|
956
|
-
["mso-margin-left-alt", "margin-left"],
|
|
957
|
-
["mso-margin-right-alt", "margin-right"],
|
|
958
|
-
["mso-line-height-alt", "line-height"]
|
|
959
|
-
];
|
|
960
|
-
for (const [msoKey, cssKey] of mappingPairs) {
|
|
961
|
-
const value = styleMap.get(msoKey);
|
|
962
|
-
if (!value) continue;
|
|
963
|
-
if (!styleMap.has(cssKey)) {
|
|
964
|
-
styleMap.set(cssKey, value);
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
const msoFore = styleMap.get("mso-foreground");
|
|
968
|
-
if (msoFore && !styleMap.has("color")) {
|
|
969
|
-
styleMap.set("color", msoFore);
|
|
970
|
-
}
|
|
971
|
-
if (styleMap.get("mso-table-lspace") && !styleMap.has("margin-left")) {
|
|
972
|
-
styleMap.set("margin-left", "0");
|
|
973
|
-
}
|
|
974
|
-
if (styleMap.get("mso-table-rspace") && !styleMap.has("margin-right")) {
|
|
975
|
-
styleMap.set("margin-right", "0");
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
function applyListFallback(el, styleMap) {
|
|
979
|
-
const className = (el.getAttribute("class") ?? "").toLowerCase();
|
|
980
|
-
const msoList = styleMap.get("mso-list") ?? "";
|
|
981
|
-
const maybeList = className.includes("msolist") || msoList.length > 0;
|
|
982
|
-
if (!maybeList) return;
|
|
983
|
-
if (!styleMap.has("text-indent")) {
|
|
984
|
-
styleMap.set("text-indent", "0");
|
|
985
|
-
}
|
|
986
|
-
const marginLeft = styleMap.get("margin-left");
|
|
987
|
-
if (marginLeft && !styleMap.has("padding-left")) {
|
|
988
|
-
styleMap.set("padding-left", marginLeft);
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
function applyTagSpecificFallback(el, styleMap) {
|
|
992
|
-
const tag = el.tagName.toLowerCase();
|
|
993
|
-
if (tag === "table") {
|
|
994
|
-
if (!styleMap.has("border-collapse")) {
|
|
995
|
-
styleMap.set("border-collapse", "collapse");
|
|
996
|
-
}
|
|
997
|
-
if (!styleMap.has("border-spacing")) {
|
|
998
|
-
styleMap.set("border-spacing", "0");
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
if ((tag === "td" || tag === "th") && !styleMap.has("vertical-align")) {
|
|
1002
|
-
styleMap.set("vertical-align", "top");
|
|
1003
|
-
}
|
|
1004
|
-
if (tag === "p" && !styleMap.has("min-height")) {
|
|
1005
|
-
styleMap.set("min-height", "1em");
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
function normalizeWordEmptyParagraphs(doc) {
|
|
1009
|
-
const paragraphs = Array.from(doc.querySelectorAll("p"));
|
|
1010
|
-
for (const p of paragraphs) {
|
|
1011
|
-
const text = (p.textContent ?? "").replace(/\u00a0/g, " ").trim();
|
|
1012
|
-
const hasVisibleChildren = p.querySelector("img,table,svg,canvas,br") !== null;
|
|
1013
|
-
if (!hasVisibleChildren && text.length === 0) {
|
|
1014
|
-
p.innerHTML = "<br/>";
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
function applyGlobalDocFixes(doc, options) {
|
|
1019
|
-
const body = doc.body;
|
|
1020
|
-
if (!body) return;
|
|
1021
|
-
if (options?.forceBodyFontFamily) {
|
|
1022
|
-
body.style.fontFamily = options.forceBodyFontFamily;
|
|
1023
|
-
}
|
|
1024
|
-
if (options?.forceHeadingFontFamily) {
|
|
1025
|
-
const headings = Array.from(doc.querySelectorAll("h1,h2,h3,h4,h5,h6"));
|
|
1026
|
-
for (const heading of headings) {
|
|
1027
|
-
heading.style.fontFamily = options.forceHeadingFontFamily;
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
function applyWordHtmlCompatibility(doc, options) {
|
|
1032
|
-
const elements = Array.from(doc.querySelectorAll("[style], p, table, td, th, li, div, span"));
|
|
1033
|
-
for (const el of elements) {
|
|
1034
|
-
const htmlEl = el;
|
|
1035
|
-
const rawStyle = htmlEl.getAttribute("style") ?? "";
|
|
1036
|
-
const styleMap = parseStyle(rawStyle);
|
|
1037
|
-
applyMsoMappings(styleMap);
|
|
1038
|
-
applyListFallback(htmlEl, styleMap);
|
|
1039
|
-
applyTagSpecificFallback(htmlEl, styleMap);
|
|
1040
|
-
if (styleMap.size > 0) {
|
|
1041
|
-
htmlEl.setAttribute("style", serializeStyle(styleMap));
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
normalizeWordEmptyParagraphs(doc);
|
|
1045
|
-
applyGlobalDocFixes(doc, options);
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
// src/lib/styleProfile.ts
|
|
1049
|
-
var import_jszip2 = __toESM(require("jszip"), 1);
|
|
1050
|
-
var FALLBACK_PROFILE = {
|
|
1051
|
-
bodyFontPx: 14.6667,
|
|
1052
|
-
bodyLineHeightRatio: 1.158333,
|
|
1053
|
-
bodyLineHeightPx: null,
|
|
1054
|
-
bodyLineHeightRule: "auto",
|
|
1055
|
-
paragraphAfterPx: 10.67,
|
|
1056
|
-
contentWidthPx: 553.73,
|
|
1057
|
-
pageHeightPx: 1122.53,
|
|
1058
|
-
pageMarginTopPx: 96,
|
|
1059
|
-
pageMarginBottomPx: 96,
|
|
1060
|
-
titleFontPx: 32,
|
|
1061
|
-
titleColor: "#0F4761",
|
|
1062
|
-
titleAlign: "center",
|
|
1063
|
-
bodyFontFamily: '"Times New Roman", "Noto Serif SC", serif',
|
|
1064
|
-
titleFontFamily: 'DengXian, "Noto Sans SC", "Microsoft YaHei", sans-serif',
|
|
1065
|
-
discoveredFonts: [],
|
|
1066
|
-
tableCellPaddingTopPx: 0,
|
|
1067
|
-
tableCellPaddingLeftPx: 7.2,
|
|
1068
|
-
tableCellPaddingBottomPx: 0,
|
|
1069
|
-
tableCellPaddingRightPx: 7.2,
|
|
1070
|
-
paragraphProfiles: [],
|
|
1071
|
-
trailingDateText: null,
|
|
1072
|
-
trailingDateAlignedRight: false,
|
|
1073
|
-
trailingDateParagraphIndex: null,
|
|
1074
|
-
trailingEmptyParagraphCountBeforeDate: 0
|
|
1075
|
-
};
|
|
1076
|
-
function createFallbackWordStyleProfile(sourceFileName = "snapshot") {
|
|
1077
|
-
return {
|
|
1078
|
-
sourceFileName,
|
|
1079
|
-
...FALLBACK_PROFILE,
|
|
1080
|
-
paragraphProfiles: []
|
|
1081
|
-
};
|
|
1082
|
-
}
|
|
1083
|
-
function twipToPx2(twip) {
|
|
1084
|
-
return twip / 15;
|
|
1085
|
-
}
|
|
1086
|
-
function getAttr2(node, attr) {
|
|
1087
|
-
if (!node) return null;
|
|
1088
|
-
return node.getAttribute(attr);
|
|
1089
|
-
}
|
|
1090
|
-
function getTwipAttr(node, attr) {
|
|
1091
|
-
const raw = getAttr2(node, attr);
|
|
1092
|
-
if (!raw) return null;
|
|
1093
|
-
const parsed = Number.parseFloat(raw);
|
|
1094
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
1095
|
-
}
|
|
1096
|
-
function queryByLocalName2(root, localName) {
|
|
1097
|
-
const all = Array.from(root.querySelectorAll("*"));
|
|
1098
|
-
return all.find((el) => el.localName === localName) ?? null;
|
|
1099
|
-
}
|
|
1100
|
-
function queryAllByLocalName2(root, localName) {
|
|
1101
|
-
const all = Array.from(root.querySelectorAll("*"));
|
|
1102
|
-
return all.filter((el) => el.localName === localName);
|
|
1103
|
-
}
|
|
1104
|
-
function parseXml2(xmlText) {
|
|
1105
|
-
const parser = new DOMParser();
|
|
1106
|
-
return parser.parseFromString(xmlText, "application/xml");
|
|
1107
|
-
}
|
|
1108
|
-
function parsePageGeometry(documentXml) {
|
|
1109
|
-
const sectPr = queryByLocalName2(documentXml, "sectPr");
|
|
1110
|
-
if (!sectPr) {
|
|
1111
|
-
return {
|
|
1112
|
-
contentWidthPx: null,
|
|
1113
|
-
pageHeightPx: null,
|
|
1114
|
-
marginTopPx: null,
|
|
1115
|
-
marginBottomPx: null
|
|
1116
|
-
};
|
|
1117
|
-
}
|
|
1118
|
-
const pgSz = queryAllByLocalName2(sectPr, "pgSz")[0] ?? null;
|
|
1119
|
-
const pgMar = queryAllByLocalName2(sectPr, "pgMar")[0] ?? null;
|
|
1120
|
-
const pageW = getTwipAttr(pgSz, "w:w") ?? getTwipAttr(pgSz, "w") ?? null;
|
|
1121
|
-
const pageH = getTwipAttr(pgSz, "w:h") ?? getTwipAttr(pgSz, "h") ?? null;
|
|
1122
|
-
const left = getTwipAttr(pgMar, "w:left") ?? getTwipAttr(pgMar, "left") ?? 0;
|
|
1123
|
-
const right = getTwipAttr(pgMar, "w:right") ?? getTwipAttr(pgMar, "right") ?? 0;
|
|
1124
|
-
const top = getTwipAttr(pgMar, "w:top") ?? getTwipAttr(pgMar, "top") ?? null;
|
|
1125
|
-
const bottom = getTwipAttr(pgMar, "w:bottom") ?? getTwipAttr(pgMar, "bottom") ?? null;
|
|
1126
|
-
return {
|
|
1127
|
-
contentWidthPx: pageW === null ? null : twipToPx2(pageW - left - right),
|
|
1128
|
-
pageHeightPx: pageH === null ? null : twipToPx2(pageH),
|
|
1129
|
-
marginTopPx: top === null ? null : twipToPx2(top),
|
|
1130
|
-
marginBottomPx: bottom === null ? null : twipToPx2(bottom)
|
|
1131
|
-
};
|
|
1132
|
-
}
|
|
1133
|
-
function parseHeadingAlignFromDocument(documentXml) {
|
|
1134
|
-
const paragraphs = queryAllByLocalName2(documentXml, "p");
|
|
1135
|
-
for (const paragraph of paragraphs) {
|
|
1136
|
-
const pPr = queryAllByLocalName2(paragraph, "pPr")[0] ?? null;
|
|
1137
|
-
if (!pPr) continue;
|
|
1138
|
-
const pStyle = queryAllByLocalName2(pPr, "pStyle")[0] ?? null;
|
|
1139
|
-
const styleVal = getAttr2(pStyle, "w:val") ?? getAttr2(pStyle, "val") ?? "";
|
|
1140
|
-
const isHeading = styleVal === "1" || styleVal.toLowerCase().includes("heading");
|
|
1141
|
-
if (!isHeading) continue;
|
|
1142
|
-
const jc = queryAllByLocalName2(pPr, "jc")[0] ?? null;
|
|
1143
|
-
const alignRaw = (getAttr2(jc, "w:val") ?? getAttr2(jc, "val") ?? "").toLowerCase();
|
|
1144
|
-
if (alignRaw === "center") return "center";
|
|
1145
|
-
if (alignRaw === "right") return "right";
|
|
1146
|
-
return "left";
|
|
1147
|
-
}
|
|
1148
|
-
return null;
|
|
1149
|
-
}
|
|
1150
|
-
function parseParagraphText(paragraph) {
|
|
1151
|
-
const textNodes = queryAllByLocalName2(paragraph, "t");
|
|
1152
|
-
return textNodes.map((node) => node.textContent ?? "").join("").trim();
|
|
1153
|
-
}
|
|
1154
|
-
function parseParagraphAlign(paragraph) {
|
|
1155
|
-
const pPr = queryAllByLocalName2(paragraph, "pPr")[0] ?? null;
|
|
1156
|
-
const jc = pPr ? queryAllByLocalName2(pPr, "jc")[0] ?? null : null;
|
|
1157
|
-
const alignRaw = (getAttr2(jc, "w:val") ?? getAttr2(jc, "val") ?? "").toLowerCase();
|
|
1158
|
-
if (alignRaw === "center") return "center";
|
|
1159
|
-
if (alignRaw === "right") return "right";
|
|
1160
|
-
return "left";
|
|
1161
|
-
}
|
|
1162
|
-
function parseTrailingDateAnchor(documentXml) {
|
|
1163
|
-
const paragraphs = queryAllByLocalName2(documentXml, "p");
|
|
1164
|
-
if (paragraphs.length === 0) {
|
|
1165
|
-
return {
|
|
1166
|
-
trailingDateText: null,
|
|
1167
|
-
trailingDateAlignedRight: false,
|
|
1168
|
-
trailingDateParagraphIndex: null,
|
|
1169
|
-
trailingEmptyParagraphCountBeforeDate: 0
|
|
1170
|
-
};
|
|
1171
|
-
}
|
|
1172
|
-
let lastNonEmptyIndex = -1;
|
|
1173
|
-
for (let i = paragraphs.length - 1; i >= 0; i -= 1) {
|
|
1174
|
-
if (parseParagraphText(paragraphs[i]).length > 0) {
|
|
1175
|
-
lastNonEmptyIndex = i;
|
|
1176
|
-
break;
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
if (lastNonEmptyIndex < 0) {
|
|
1180
|
-
return {
|
|
1181
|
-
trailingDateText: null,
|
|
1182
|
-
trailingDateAlignedRight: false,
|
|
1183
|
-
trailingDateParagraphIndex: null,
|
|
1184
|
-
trailingEmptyParagraphCountBeforeDate: 0
|
|
1185
|
-
};
|
|
1186
|
-
}
|
|
1187
|
-
const dateParagraph = paragraphs[lastNonEmptyIndex];
|
|
1188
|
-
const dateText = parseParagraphText(dateParagraph);
|
|
1189
|
-
const looksLikeDate = /\d{4}\s*年\s*\d+\s*月\s*\d+\s*日/.test(dateText);
|
|
1190
|
-
const align = parseParagraphAlign(dateParagraph);
|
|
1191
|
-
let trailingEmpty = 0;
|
|
1192
|
-
for (let i = lastNonEmptyIndex - 1; i >= 0; i -= 1) {
|
|
1193
|
-
if (parseParagraphText(paragraphs[i]).length === 0) {
|
|
1194
|
-
trailingEmpty += 1;
|
|
1195
|
-
continue;
|
|
1196
|
-
}
|
|
1197
|
-
break;
|
|
1198
|
-
}
|
|
1199
|
-
return {
|
|
1200
|
-
trailingDateText: looksLikeDate ? dateText : null,
|
|
1201
|
-
trailingDateAlignedRight: looksLikeDate && align === "right",
|
|
1202
|
-
trailingDateParagraphIndex: looksLikeDate ? lastNonEmptyIndex : null,
|
|
1203
|
-
trailingEmptyParagraphCountBeforeDate: looksLikeDate ? trailingEmpty : 0
|
|
1204
|
-
};
|
|
1205
|
-
}
|
|
1206
|
-
function toInt(value) {
|
|
1207
|
-
if (!value) return null;
|
|
1208
|
-
const n = Number.parseInt(value, 10);
|
|
1209
|
-
return Number.isFinite(n) ? n : null;
|
|
1210
|
-
}
|
|
1211
|
-
function parseNumberingMap(numberingXml) {
|
|
1212
|
-
const levelMap = /* @__PURE__ */ new Map();
|
|
1213
|
-
if (!numberingXml) return levelMap;
|
|
1214
|
-
const abstractMap = /* @__PURE__ */ new Map();
|
|
1215
|
-
const abstractNums = queryAllByLocalName2(numberingXml, "abstractNum");
|
|
1216
|
-
for (const abs of abstractNums) {
|
|
1217
|
-
const absId = toInt(getAttr2(abs, "w:abstractNumId") ?? getAttr2(abs, "abstractNumId"));
|
|
1218
|
-
if (absId === null) continue;
|
|
1219
|
-
const lvlNodes = queryAllByLocalName2(abs, "lvl");
|
|
1220
|
-
const lvlMap = /* @__PURE__ */ new Map();
|
|
1221
|
-
for (const lvl of lvlNodes) {
|
|
1222
|
-
const ilvl = toInt(getAttr2(lvl, "w:ilvl") ?? getAttr2(lvl, "ilvl"));
|
|
1223
|
-
if (ilvl === null) continue;
|
|
1224
|
-
const numFmtNode = queryAllByLocalName2(lvl, "numFmt")[0] ?? null;
|
|
1225
|
-
const lvlTextNode = queryAllByLocalName2(lvl, "lvlText")[0] ?? null;
|
|
1226
|
-
lvlMap.set(ilvl, {
|
|
1227
|
-
numFmt: getAttr2(numFmtNode, "w:val") ?? getAttr2(numFmtNode, "val") ?? null,
|
|
1228
|
-
lvlText: getAttr2(lvlTextNode, "w:val") ?? getAttr2(lvlTextNode, "val") ?? null,
|
|
1229
|
-
startAt: toInt(getAttr2(queryAllByLocalName2(lvl, "start")[0] ?? null, "w:val") ?? getAttr2(queryAllByLocalName2(lvl, "start")[0] ?? null, "val")) ?? 1
|
|
1230
|
-
});
|
|
1231
|
-
}
|
|
1232
|
-
abstractMap.set(absId, lvlMap);
|
|
1233
|
-
}
|
|
1234
|
-
const nums = queryAllByLocalName2(numberingXml, "num");
|
|
1235
|
-
for (const num of nums) {
|
|
1236
|
-
const numId = toInt(getAttr2(num, "w:numId") ?? getAttr2(num, "numId"));
|
|
1237
|
-
if (numId === null) continue;
|
|
1238
|
-
const abstractRefNode = queryAllByLocalName2(num, "abstractNumId")[0] ?? null;
|
|
1239
|
-
const absId = toInt(getAttr2(abstractRefNode, "w:val") ?? getAttr2(abstractRefNode, "val"));
|
|
1240
|
-
if (absId === null) continue;
|
|
1241
|
-
const lvlMap = abstractMap.get(absId);
|
|
1242
|
-
if (!lvlMap) continue;
|
|
1243
|
-
for (const [lvl, spec] of lvlMap.entries()) {
|
|
1244
|
-
levelMap.set(`${numId}:${lvl}`, { ...spec });
|
|
1245
|
-
}
|
|
1246
|
-
const lvlOverrides = queryAllByLocalName2(num, "lvlOverride");
|
|
1247
|
-
for (const override of lvlOverrides) {
|
|
1248
|
-
const ilvl = toInt(getAttr2(override, "w:ilvl") ?? getAttr2(override, "ilvl"));
|
|
1249
|
-
if (ilvl === null) continue;
|
|
1250
|
-
const key = `${numId}:${ilvl}`;
|
|
1251
|
-
const base = levelMap.get(key) ?? { numFmt: null, lvlText: null, startAt: 1 };
|
|
1252
|
-
const overrideStart = toInt(
|
|
1253
|
-
getAttr2(queryAllByLocalName2(override, "startOverride")[0] ?? null, "w:val") ?? getAttr2(queryAllByLocalName2(override, "startOverride")[0] ?? null, "val")
|
|
1254
|
-
);
|
|
1255
|
-
const overrideLvl = queryAllByLocalName2(override, "lvl")[0] ?? null;
|
|
1256
|
-
const overrideNumFmtNode = overrideLvl ? queryAllByLocalName2(overrideLvl, "numFmt")[0] ?? null : null;
|
|
1257
|
-
const overrideLvlTextNode = overrideLvl ? queryAllByLocalName2(overrideLvl, "lvlText")[0] ?? null : null;
|
|
1258
|
-
const overrideLvlStart = toInt(
|
|
1259
|
-
getAttr2(queryAllByLocalName2(overrideLvl ?? override, "start")[0] ?? null, "w:val") ?? getAttr2(queryAllByLocalName2(overrideLvl ?? override, "start")[0] ?? null, "val")
|
|
1260
|
-
);
|
|
1261
|
-
levelMap.set(key, {
|
|
1262
|
-
numFmt: getAttr2(overrideNumFmtNode, "w:val") ?? getAttr2(overrideNumFmtNode, "val") ?? base.numFmt,
|
|
1263
|
-
lvlText: getAttr2(overrideLvlTextNode, "w:val") ?? getAttr2(overrideLvlTextNode, "val") ?? base.lvlText,
|
|
1264
|
-
startAt: overrideStart ?? overrideLvlStart ?? base.startAt
|
|
1265
|
-
});
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
return levelMap;
|
|
1269
|
-
}
|
|
1270
|
-
function parseParagraphProfiles(documentXml, numberingMap) {
|
|
1271
|
-
const paragraphs = queryAllByLocalName2(documentXml, "p");
|
|
1272
|
-
return paragraphs.map((paragraph, index) => {
|
|
1273
|
-
const text = parseParagraphText(paragraph);
|
|
1274
|
-
const pPr = queryAllByLocalName2(paragraph, "pPr")[0] ?? null;
|
|
1275
|
-
const spacing = pPr ? queryAllByLocalName2(pPr, "spacing")[0] ?? null : null;
|
|
1276
|
-
const ind = pPr ? queryAllByLocalName2(pPr, "ind")[0] ?? null : null;
|
|
1277
|
-
const numPr = pPr ? queryAllByLocalName2(pPr, "numPr")[0] ?? null : null;
|
|
1278
|
-
const ilvlNode = numPr ? queryAllByLocalName2(numPr, "ilvl")[0] ?? null : null;
|
|
1279
|
-
const numIdNode = numPr ? queryAllByLocalName2(numPr, "numId")[0] ?? null : null;
|
|
1280
|
-
const listLevel = toInt(getAttr2(ilvlNode, "w:val") ?? getAttr2(ilvlNode, "val"));
|
|
1281
|
-
const listNumId = toInt(getAttr2(numIdNode, "w:val") ?? getAttr2(numIdNode, "val"));
|
|
1282
|
-
const listSpec = listNumId !== null && listLevel !== null ? numberingMap.get(`${listNumId}:${listLevel}`) : void 0;
|
|
1283
|
-
const keepNextNode = pPr ? queryAllByLocalName2(pPr, "keepNext")[0] ?? null : null;
|
|
1284
|
-
const keepLinesNode = pPr ? queryAllByLocalName2(pPr, "keepLines")[0] ?? null : null;
|
|
1285
|
-
const pageBreakBeforeNode = pPr ? queryAllByLocalName2(pPr, "pageBreakBefore")[0] ?? null : null;
|
|
1286
|
-
const renderedPageBreakNode = queryAllByLocalName2(paragraph, "lastRenderedPageBreak")[0] ?? null;
|
|
1287
|
-
const sectionBreakNode = pPr ? queryAllByLocalName2(pPr, "sectPr")[0] ?? null : null;
|
|
1288
|
-
const before = getTwipAttr(spacing, "w:before") ?? getTwipAttr(spacing, "before") ?? null;
|
|
1289
|
-
const after = getTwipAttr(spacing, "w:after") ?? getTwipAttr(spacing, "after") ?? null;
|
|
1290
|
-
const line = getTwipAttr(spacing, "w:line") ?? getTwipAttr(spacing, "line") ?? null;
|
|
1291
|
-
const rawLineRule = (getAttr2(spacing, "w:lineRule") ?? getAttr2(spacing, "lineRule") ?? "auto").toLowerCase();
|
|
1292
|
-
const lineHeightRule = line === null ? null : rawLineRule === "exact" ? "exact" : rawLineRule === "atleast" ? "atLeast" : "auto";
|
|
1293
|
-
const left = getTwipAttr(ind, "w:left") ?? getTwipAttr(ind, "left") ?? null;
|
|
1294
|
-
const right = getTwipAttr(ind, "w:right") ?? getTwipAttr(ind, "right") ?? null;
|
|
1295
|
-
const firstLine = getTwipAttr(ind, "w:firstLine") ?? getTwipAttr(ind, "firstLine") ?? null;
|
|
1296
|
-
const hanging = getTwipAttr(ind, "w:hanging") ?? getTwipAttr(ind, "hanging") ?? null;
|
|
1297
|
-
const runs = parseRunProfiles(paragraph);
|
|
1298
|
-
return {
|
|
1299
|
-
index,
|
|
1300
|
-
text,
|
|
1301
|
-
isEmpty: text.length === 0,
|
|
1302
|
-
align: parseParagraphAlign(paragraph),
|
|
1303
|
-
beforePx: before === null ? null : twipToPx2(before),
|
|
1304
|
-
afterPx: after === null ? null : twipToPx2(after),
|
|
1305
|
-
lineHeightRatio: line === null || lineHeightRule !== "auto" ? null : line / 240,
|
|
1306
|
-
lineHeightPx: line === null || lineHeightRule === "auto" ? null : twipToPx2(line),
|
|
1307
|
-
lineHeightRule,
|
|
1308
|
-
indentLeftPx: left === null ? null : twipToPx2(left),
|
|
1309
|
-
indentRightPx: right === null ? null : twipToPx2(right),
|
|
1310
|
-
firstLinePx: firstLine === null ? null : twipToPx2(firstLine),
|
|
1311
|
-
hangingPx: hanging === null ? null : twipToPx2(hanging),
|
|
1312
|
-
listNumId,
|
|
1313
|
-
listLevel,
|
|
1314
|
-
listFormat: listSpec?.numFmt ?? null,
|
|
1315
|
-
listTextPattern: listSpec?.lvlText ?? null,
|
|
1316
|
-
listStartAt: listSpec?.startAt ?? 1,
|
|
1317
|
-
keepNext: keepNextNode !== null && (getAttr2(keepNextNode, "w:val") ?? getAttr2(keepNextNode, "val") ?? "1") !== "0",
|
|
1318
|
-
keepLines: keepLinesNode !== null && (getAttr2(keepLinesNode, "w:val") ?? getAttr2(keepLinesNode, "val") ?? "1") !== "0",
|
|
1319
|
-
pageBreakBefore: renderedPageBreakNode !== null || pageBreakBeforeNode !== null && (getAttr2(pageBreakBeforeNode, "w:val") ?? getAttr2(pageBreakBeforeNode, "val") ?? "1") !== "0",
|
|
1320
|
-
sectionBreakBefore: sectionBreakNode !== null,
|
|
1321
|
-
runs
|
|
1322
|
-
};
|
|
1323
|
-
});
|
|
1324
|
-
}
|
|
1325
|
-
function parseTableDefaults(stylesXml) {
|
|
1326
|
-
const tableStyles = queryAllByLocalName2(stylesXml, "style").filter((style) => {
|
|
1327
|
-
const type = (getAttr2(style, "w:type") ?? getAttr2(style, "type") ?? "").toLowerCase();
|
|
1328
|
-
return type === "table";
|
|
1329
|
-
});
|
|
1330
|
-
const targetStyle = tableStyles.find((style) => {
|
|
1331
|
-
const styleId = (getAttr2(style, "w:styleId") ?? getAttr2(style, "styleId") ?? "").toLowerCase();
|
|
1332
|
-
return styleId === "a1";
|
|
1333
|
-
}) ?? tableStyles[0] ?? null;
|
|
1334
|
-
if (!targetStyle) {
|
|
1335
|
-
return { topPx: null, leftPx: null, bottomPx: null, rightPx: null };
|
|
1336
|
-
}
|
|
1337
|
-
const tblPr = queryAllByLocalName2(targetStyle, "tblPr")[0] ?? null;
|
|
1338
|
-
const tblCellMar = tblPr ? queryAllByLocalName2(tblPr, "tblCellMar")[0] ?? null : null;
|
|
1339
|
-
const top = tblCellMar ? queryAllByLocalName2(tblCellMar, "top")[0] ?? null : null;
|
|
1340
|
-
const left = tblCellMar ? queryAllByLocalName2(tblCellMar, "left")[0] ?? null : null;
|
|
1341
|
-
const bottom = tblCellMar ? queryAllByLocalName2(tblCellMar, "bottom")[0] ?? null : null;
|
|
1342
|
-
const right = tblCellMar ? queryAllByLocalName2(tblCellMar, "right")[0] ?? null : null;
|
|
1343
|
-
return {
|
|
1344
|
-
topPx: (() => {
|
|
1345
|
-
const v = getTwipAttr(top, "w:w") ?? getTwipAttr(top, "w") ?? null;
|
|
1346
|
-
return v === null ? null : twipToPx2(v);
|
|
1347
|
-
})(),
|
|
1348
|
-
leftPx: (() => {
|
|
1349
|
-
const v = getTwipAttr(left, "w:w") ?? getTwipAttr(left, "w") ?? null;
|
|
1350
|
-
return v === null ? null : twipToPx2(v);
|
|
1351
|
-
})(),
|
|
1352
|
-
bottomPx: (() => {
|
|
1353
|
-
const v = getTwipAttr(bottom, "w:w") ?? getTwipAttr(bottom, "w") ?? null;
|
|
1354
|
-
return v === null ? null : twipToPx2(v);
|
|
1355
|
-
})(),
|
|
1356
|
-
rightPx: (() => {
|
|
1357
|
-
const v = getTwipAttr(right, "w:w") ?? getTwipAttr(right, "w") ?? null;
|
|
1358
|
-
return v === null ? null : twipToPx2(v);
|
|
1359
|
-
})()
|
|
1360
|
-
};
|
|
1361
|
-
}
|
|
1362
|
-
function parseRunProfiles(paragraph) {
|
|
1363
|
-
const runNodes = queryAllByLocalName2(paragraph, "r");
|
|
1364
|
-
return runNodes.map((run) => {
|
|
1365
|
-
const rPr = queryAllByLocalName2(run, "rPr")[0] ?? null;
|
|
1366
|
-
const textNodes = queryAllByLocalName2(run, "t");
|
|
1367
|
-
const breakNodes = queryAllByLocalName2(run, "br");
|
|
1368
|
-
let text = textNodes.map((node) => node.textContent ?? "").join("");
|
|
1369
|
-
if (breakNodes.length > 0) {
|
|
1370
|
-
text += "\n".repeat(breakNodes.length);
|
|
1371
|
-
}
|
|
1372
|
-
if (!text) {
|
|
1373
|
-
return null;
|
|
1374
|
-
}
|
|
1375
|
-
const sz = rPr ? queryAllByLocalName2(rPr, "sz")[0] ?? null : null;
|
|
1376
|
-
const halfPoints = getTwipAttr(sz, "w:val") ?? getTwipAttr(sz, "val") ?? null;
|
|
1377
|
-
const fontSizePx = halfPoints === null ? null : halfPoints / 2 * (96 / 72);
|
|
1378
|
-
const colorNode = rPr ? queryAllByLocalName2(rPr, "color")[0] ?? null : null;
|
|
1379
|
-
const colorRaw = getAttr2(colorNode, "w:val") ?? getAttr2(colorNode, "val") ?? null;
|
|
1380
|
-
const color = colorRaw && colorRaw.toLowerCase() !== "auto" ? `#${colorRaw}` : null;
|
|
1381
|
-
const highlightNode = rPr ? queryAllByLocalName2(rPr, "highlight")[0] ?? null : null;
|
|
1382
|
-
const highlightRaw = (getAttr2(highlightNode, "w:val") ?? getAttr2(highlightNode, "val") ?? "").toLowerCase();
|
|
1383
|
-
const highlightMap = {
|
|
1384
|
-
yellow: "#fff59d",
|
|
1385
|
-
green: "#b9f6ca",
|
|
1386
|
-
cyan: "#b2ebf2",
|
|
1387
|
-
magenta: "#f8bbd0",
|
|
1388
|
-
blue: "#bbdefb",
|
|
1389
|
-
red: "#ffcdd2",
|
|
1390
|
-
darkyellow: "#fbc02d",
|
|
1391
|
-
darkgreen: "#66bb6a",
|
|
1392
|
-
darkblue: "#64b5f6",
|
|
1393
|
-
darkred: "#e57373",
|
|
1394
|
-
darkcyan: "#4dd0e1",
|
|
1395
|
-
darkmagenta: "#ba68c8",
|
|
1396
|
-
gray: "#e0e0e0",
|
|
1397
|
-
lightgray: "#f5f5f5"
|
|
1398
|
-
};
|
|
1399
|
-
const highlightColor = highlightRaw && highlightRaw !== "none" ? highlightMap[highlightRaw] ?? null : null;
|
|
1400
|
-
const shdNode = rPr ? queryAllByLocalName2(rPr, "shd")[0] ?? null : null;
|
|
1401
|
-
const shdFill = (getAttr2(shdNode, "w:fill") ?? getAttr2(shdNode, "fill") ?? "").toLowerCase();
|
|
1402
|
-
const shadingColor = shdFill && shdFill !== "auto" ? `#${shdFill}` : null;
|
|
1403
|
-
const spacingNode = rPr ? queryAllByLocalName2(rPr, "spacing")[0] ?? null : null;
|
|
1404
|
-
const spacingVal = getTwipAttr(spacingNode, "w:val") ?? getTwipAttr(spacingNode, "val") ?? null;
|
|
1405
|
-
const charSpacingPx = spacingVal === null ? null : spacingVal / 20 * (96 / 72);
|
|
1406
|
-
const bNode = rPr ? queryAllByLocalName2(rPr, "b")[0] ?? null : null;
|
|
1407
|
-
const iNode = rPr ? queryAllByLocalName2(rPr, "i")[0] ?? null : null;
|
|
1408
|
-
const uNode = rPr ? queryAllByLocalName2(rPr, "u")[0] ?? null : null;
|
|
1409
|
-
const strikeNode = rPr ? queryAllByLocalName2(rPr, "strike")[0] ?? null : null;
|
|
1410
|
-
const shadowNode = rPr ? queryAllByLocalName2(rPr, "shadow")[0] ?? null : null;
|
|
1411
|
-
const vertAlignNode = rPr ? queryAllByLocalName2(rPr, "vertAlign")[0] ?? null : null;
|
|
1412
|
-
const bold = bNode !== null && (getAttr2(bNode, "w:val") ?? getAttr2(bNode, "val") ?? "1") !== "0";
|
|
1413
|
-
const italic = iNode !== null && (getAttr2(iNode, "w:val") ?? getAttr2(iNode, "val") ?? "1") !== "0";
|
|
1414
|
-
const underlineVal = (getAttr2(uNode, "w:val") ?? getAttr2(uNode, "val") ?? "").toLowerCase();
|
|
1415
|
-
const underline = uNode !== null && underlineVal !== "none";
|
|
1416
|
-
const strike = strikeNode !== null && (getAttr2(strikeNode, "w:val") ?? getAttr2(strikeNode, "val") ?? "1") !== "0";
|
|
1417
|
-
const shadow = shadowNode !== null && (getAttr2(shadowNode, "w:val") ?? getAttr2(shadowNode, "val") ?? "1") !== "0";
|
|
1418
|
-
const vertAlign = (getAttr2(vertAlignNode, "w:val") ?? getAttr2(vertAlignNode, "val") ?? "").toLowerCase();
|
|
1419
|
-
const superscript = vertAlign === "superscript";
|
|
1420
|
-
const subscript = vertAlign === "subscript";
|
|
1421
|
-
const rFonts = rPr ? queryAllByLocalName2(rPr, "rFonts")[0] ?? null : null;
|
|
1422
|
-
const fontFamily = getAttr2(rFonts, "w:eastAsia") ?? getAttr2(rFonts, "eastAsia") ?? getAttr2(rFonts, "w:ascii") ?? getAttr2(rFonts, "ascii") ?? getAttr2(rFonts, "w:hAnsi") ?? getAttr2(rFonts, "hAnsi") ?? null;
|
|
1423
|
-
return {
|
|
1424
|
-
text,
|
|
1425
|
-
fontSizePx,
|
|
1426
|
-
color,
|
|
1427
|
-
highlightColor,
|
|
1428
|
-
shadingColor,
|
|
1429
|
-
charSpacingPx,
|
|
1430
|
-
shadow,
|
|
1431
|
-
bold,
|
|
1432
|
-
italic,
|
|
1433
|
-
underline,
|
|
1434
|
-
strike,
|
|
1435
|
-
superscript,
|
|
1436
|
-
subscript,
|
|
1437
|
-
fontFamily
|
|
1438
|
-
};
|
|
1439
|
-
}).filter((run) => run !== null);
|
|
1440
|
-
}
|
|
1441
|
-
function parseDefaults(stylesXml) {
|
|
1442
|
-
const docDefaults = queryByLocalName2(stylesXml, "docDefaults");
|
|
1443
|
-
if (!docDefaults) {
|
|
1444
|
-
return { bodyFontPx: null, bodyLineHeightRatio: null, bodyLineHeightPx: null, bodyLineHeightRule: "auto", paragraphAfterPx: null };
|
|
1445
|
-
}
|
|
1446
|
-
const rPrDefault = queryByLocalName2(docDefaults, "rPrDefault");
|
|
1447
|
-
const sz = rPrDefault ? queryByLocalName2(rPrDefault, "sz") : null;
|
|
1448
|
-
const halfPoints = getTwipAttr(sz, "w:val") ?? getTwipAttr(sz, "val") ?? null;
|
|
1449
|
-
const bodyFontPx = halfPoints === null ? null : halfPoints / 2 * (96 / 72);
|
|
1450
|
-
const pPrDefault = queryByLocalName2(docDefaults, "pPrDefault");
|
|
1451
|
-
const spacing = pPrDefault ? queryByLocalName2(pPrDefault, "spacing") : null;
|
|
1452
|
-
const line = getTwipAttr(spacing, "w:line") ?? getTwipAttr(spacing, "line") ?? null;
|
|
1453
|
-
const rawLineRule = (getAttr2(spacing, "w:lineRule") ?? getAttr2(spacing, "lineRule") ?? "auto").toLowerCase();
|
|
1454
|
-
const bodyLineHeightRule = rawLineRule === "exact" ? "exact" : rawLineRule === "atleast" ? "atLeast" : "auto";
|
|
1455
|
-
const bodyLineHeightRatio = line === null || bodyLineHeightRule !== "auto" ? null : line / 240;
|
|
1456
|
-
const bodyLineHeightPx = line === null || bodyLineHeightRule === "auto" ? null : twipToPx2(line);
|
|
1457
|
-
const after = getTwipAttr(spacing, "w:after") ?? getTwipAttr(spacing, "after") ?? null;
|
|
1458
|
-
const paragraphAfterPx = after === null ? null : twipToPx2(after);
|
|
1459
|
-
return { bodyFontPx, bodyLineHeightRatio, bodyLineHeightPx, bodyLineHeightRule, paragraphAfterPx };
|
|
1460
|
-
}
|
|
1461
|
-
function parseHeading1Style(stylesXml) {
|
|
1462
|
-
const styles = queryAllByLocalName2(stylesXml, "style");
|
|
1463
|
-
const headingStyle = styles.find((style) => {
|
|
1464
|
-
const styleId = (getAttr2(style, "w:styleId") ?? getAttr2(style, "styleId") ?? "").toLowerCase();
|
|
1465
|
-
const nameNode = queryByLocalName2(style, "name");
|
|
1466
|
-
const nameVal = (getAttr2(nameNode, "w:val") ?? getAttr2(nameNode, "val") ?? "").toLowerCase();
|
|
1467
|
-
return styleId === "1" || nameVal === "heading 1" || nameVal === "\u6807\u9898 1";
|
|
1468
|
-
});
|
|
1469
|
-
if (!headingStyle) {
|
|
1470
|
-
return { titleFontPx: null, titleColor: null };
|
|
1471
|
-
}
|
|
1472
|
-
const rPr = queryByLocalName2(headingStyle, "rPr");
|
|
1473
|
-
const sz = rPr ? queryByLocalName2(rPr, "sz") : null;
|
|
1474
|
-
const halfPoints = getTwipAttr(sz, "w:val") ?? getTwipAttr(sz, "val") ?? null;
|
|
1475
|
-
const titleFontPx = halfPoints === null ? null : halfPoints / 2 * (96 / 72);
|
|
1476
|
-
const colorNode = rPr ? queryByLocalName2(rPr, "color") : null;
|
|
1477
|
-
const colorRaw = getAttr2(colorNode, "w:val") ?? getAttr2(colorNode, "val") ?? null;
|
|
1478
|
-
const titleColor = colorRaw ? `#${colorRaw}` : null;
|
|
1479
|
-
return { titleFontPx, titleColor };
|
|
1480
|
-
}
|
|
1481
|
-
function parseFontTableFamilies(fontTableXml) {
|
|
1482
|
-
if (!fontTableXml) return [];
|
|
1483
|
-
const fontNodes = queryAllByLocalName2(fontTableXml, "font");
|
|
1484
|
-
const families = fontNodes.map((node) => getAttr2(node, "w:name") ?? getAttr2(node, "name") ?? "").map((name) => name.trim()).filter((name) => name.length > 0);
|
|
1485
|
-
return [...new Set(families)];
|
|
1486
|
-
}
|
|
1487
|
-
function hasFontLike(families, candidates) {
|
|
1488
|
-
return families.some(
|
|
1489
|
-
(family) => candidates.some((candidate) => family.toLowerCase().includes(candidate.toLowerCase()))
|
|
1490
|
-
);
|
|
1491
|
-
}
|
|
1492
|
-
function inferBodyFontFamily(families) {
|
|
1493
|
-
if (hasFontLike(families, ["times new roman"])) {
|
|
1494
|
-
return '"Times New Roman", "Noto Serif SC", serif';
|
|
1495
|
-
}
|
|
1496
|
-
if (hasFontLike(families, ["dengxian", "\u7B49\u7EBF", "yahei", "hei", "song"])) {
|
|
1497
|
-
return 'DengXian, "Microsoft YaHei", "PingFang SC", "Noto Sans SC", sans-serif';
|
|
1498
|
-
}
|
|
1499
|
-
return FALLBACK_PROFILE.bodyFontFamily;
|
|
1500
|
-
}
|
|
1501
|
-
function inferTitleFontFamily(families) {
|
|
1502
|
-
if (hasFontLike(families, ["dengxian", "\u7B49\u7EBF"])) {
|
|
1503
|
-
return 'DengXian, "Noto Sans SC", "Microsoft YaHei", sans-serif';
|
|
1504
|
-
}
|
|
1505
|
-
if (hasFontLike(families, ["times new roman"])) {
|
|
1506
|
-
return '"Times New Roman", "Noto Serif SC", serif';
|
|
1507
|
-
}
|
|
1508
|
-
return FALLBACK_PROFILE.titleFontFamily;
|
|
1509
|
-
}
|
|
1510
|
-
async function parseDocxStyleProfile(file) {
|
|
1511
|
-
const maybeArrayBuffer = file.arrayBuffer;
|
|
1512
|
-
const buffer = maybeArrayBuffer ? await maybeArrayBuffer.call(file) : await new Response(file).arrayBuffer();
|
|
1513
|
-
const zip = await import_jszip2.default.loadAsync(buffer);
|
|
1514
|
-
const documentXmlText = await zip.file("word/document.xml")?.async("string");
|
|
1515
|
-
const stylesXmlText = await zip.file("word/styles.xml")?.async("string");
|
|
1516
|
-
const fontTableXmlText = await zip.file("word/fontTable.xml")?.async("string");
|
|
1517
|
-
const numberingXmlText = await zip.file("word/numbering.xml")?.async("string");
|
|
1518
|
-
if (!documentXmlText || !stylesXmlText) {
|
|
1519
|
-
throw new Error("DOCX missing document.xml or styles.xml");
|
|
1520
|
-
}
|
|
1521
|
-
const documentXml = parseXml2(documentXmlText);
|
|
1522
|
-
const stylesXml = parseXml2(stylesXmlText);
|
|
1523
|
-
const fontTableXml = fontTableXmlText ? parseXml2(fontTableXmlText) : null;
|
|
1524
|
-
const numberingXml = numberingXmlText ? parseXml2(numberingXmlText) : null;
|
|
1525
|
-
const numberingMap = parseNumberingMap(numberingXml);
|
|
1526
|
-
const defaults = parseDefaults(stylesXml);
|
|
1527
|
-
const heading1 = parseHeading1Style(stylesXml);
|
|
1528
|
-
const tableDefaults = parseTableDefaults(stylesXml);
|
|
1529
|
-
const pageGeometry = parsePageGeometry(documentXml);
|
|
1530
|
-
const titleAlign = parseHeadingAlignFromDocument(documentXml);
|
|
1531
|
-
const trailingDate = parseTrailingDateAnchor(documentXml);
|
|
1532
|
-
const discoveredFonts = parseFontTableFamilies(fontTableXml);
|
|
1533
|
-
const bodyFontFamily = inferBodyFontFamily(discoveredFonts);
|
|
1534
|
-
const titleFontFamily = inferTitleFontFamily(discoveredFonts);
|
|
1535
|
-
const paragraphProfiles = parseParagraphProfiles(documentXml, numberingMap);
|
|
1536
|
-
return {
|
|
1537
|
-
sourceFileName: file.name,
|
|
1538
|
-
bodyFontPx: defaults.bodyFontPx ?? FALLBACK_PROFILE.bodyFontPx,
|
|
1539
|
-
bodyLineHeightRatio: defaults.bodyLineHeightRatio ?? FALLBACK_PROFILE.bodyLineHeightRatio,
|
|
1540
|
-
bodyLineHeightPx: defaults.bodyLineHeightPx ?? FALLBACK_PROFILE.bodyLineHeightPx,
|
|
1541
|
-
bodyLineHeightRule: defaults.bodyLineHeightRule ?? FALLBACK_PROFILE.bodyLineHeightRule,
|
|
1542
|
-
paragraphAfterPx: defaults.paragraphAfterPx ?? FALLBACK_PROFILE.paragraphAfterPx,
|
|
1543
|
-
contentWidthPx: pageGeometry.contentWidthPx ?? FALLBACK_PROFILE.contentWidthPx,
|
|
1544
|
-
pageHeightPx: pageGeometry.pageHeightPx ?? FALLBACK_PROFILE.pageHeightPx,
|
|
1545
|
-
pageMarginTopPx: pageGeometry.marginTopPx ?? FALLBACK_PROFILE.pageMarginTopPx,
|
|
1546
|
-
pageMarginBottomPx: pageGeometry.marginBottomPx ?? FALLBACK_PROFILE.pageMarginBottomPx,
|
|
1547
|
-
titleFontPx: heading1.titleFontPx ?? FALLBACK_PROFILE.titleFontPx,
|
|
1548
|
-
titleColor: heading1.titleColor ?? FALLBACK_PROFILE.titleColor,
|
|
1549
|
-
titleAlign: titleAlign ?? FALLBACK_PROFILE.titleAlign,
|
|
1550
|
-
bodyFontFamily,
|
|
1551
|
-
titleFontFamily,
|
|
1552
|
-
discoveredFonts,
|
|
1553
|
-
tableCellPaddingTopPx: tableDefaults.topPx ?? FALLBACK_PROFILE.tableCellPaddingTopPx,
|
|
1554
|
-
tableCellPaddingLeftPx: tableDefaults.leftPx ?? FALLBACK_PROFILE.tableCellPaddingLeftPx,
|
|
1555
|
-
tableCellPaddingBottomPx: tableDefaults.bottomPx ?? FALLBACK_PROFILE.tableCellPaddingBottomPx,
|
|
1556
|
-
tableCellPaddingRightPx: tableDefaults.rightPx ?? FALLBACK_PROFILE.tableCellPaddingRightPx,
|
|
1557
|
-
paragraphProfiles,
|
|
1558
|
-
trailingDateText: trailingDate.trailingDateText,
|
|
1559
|
-
trailingDateAlignedRight: trailingDate.trailingDateAlignedRight,
|
|
1560
|
-
trailingDateParagraphIndex: trailingDate.trailingDateParagraphIndex,
|
|
1561
|
-
trailingEmptyParagraphCountBeforeDate: trailingDate.trailingEmptyParagraphCountBeforeDate
|
|
1562
|
-
};
|
|
1563
|
-
}
|
|
1564
|
-
|
|
1565
|
-
// src/lib/renderApply.ts
|
|
1566
|
-
function setImportantStyle(el, prop, value) {
|
|
1567
|
-
el.style.setProperty(prop, value, "important");
|
|
1568
|
-
}
|
|
1569
|
-
function escapeHtml2(text) {
|
|
1570
|
-
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
1571
|
-
}
|
|
1572
|
-
function runStyleToCss2(run) {
|
|
1573
|
-
const declarations = [];
|
|
1574
|
-
if (run.fontSizePx !== null) declarations.push(`font-size:${run.fontSizePx.toFixed(2)}px`);
|
|
1575
|
-
if (run.color) declarations.push(`color:${run.color}`);
|
|
1576
|
-
if (run.highlightColor) declarations.push(`background-color:${run.highlightColor}`);
|
|
1577
|
-
if (run.shadingColor) declarations.push(`background-color:${run.shadingColor}`);
|
|
1578
|
-
if (run.charSpacingPx !== null) declarations.push(`letter-spacing:${run.charSpacingPx.toFixed(2)}px`);
|
|
1579
|
-
if (run.shadow) declarations.push("text-shadow:0.5px 0.5px 0 rgba(0,0,0,0.28)");
|
|
1580
|
-
if (run.bold) declarations.push("font-weight:700");
|
|
1581
|
-
if (run.italic) declarations.push("font-style:italic");
|
|
1582
|
-
const textDecorations = [];
|
|
1583
|
-
if (run.underline) textDecorations.push("underline");
|
|
1584
|
-
if (run.strike) textDecorations.push("line-through");
|
|
1585
|
-
if (textDecorations.length > 0) declarations.push(`text-decoration:${textDecorations.join(" ")}`);
|
|
1586
|
-
if (run.superscript) declarations.push("vertical-align:super");
|
|
1587
|
-
if (run.subscript) declarations.push("vertical-align:sub");
|
|
1588
|
-
if (run.superscript || run.subscript) declarations.push("font-size:0.83em");
|
|
1589
|
-
if (run.fontFamily) declarations.push(`font-family:${run.fontFamily}`);
|
|
1590
|
-
return declarations.join(";");
|
|
1591
|
-
}
|
|
1592
|
-
function paragraphToRunHtml(runs) {
|
|
1593
|
-
return runs.map((run) => {
|
|
1594
|
-
const css = runStyleToCss2(run);
|
|
1595
|
-
const parts = run.text.split("\n");
|
|
1596
|
-
const html = parts.map((part) => escapeHtml2(part)).join("<br/>");
|
|
1597
|
-
if (!css) return html;
|
|
1598
|
-
return `<span style="${css}">${html}</span>`;
|
|
1599
|
-
}).join("");
|
|
1600
|
-
}
|
|
1601
|
-
function toLowerLetter(n) {
|
|
1602
|
-
const alphabet = "abcdefghijklmnopqrstuvwxyz";
|
|
1603
|
-
if (n <= 0) return "a";
|
|
1604
|
-
let x = n;
|
|
1605
|
-
let out = "";
|
|
1606
|
-
while (x > 0) {
|
|
1607
|
-
x -= 1;
|
|
1608
|
-
out = alphabet[x % 26] + out;
|
|
1609
|
-
x = Math.floor(x / 26);
|
|
1610
|
-
}
|
|
1611
|
-
return out;
|
|
1612
|
-
}
|
|
1613
|
-
function toRoman(num) {
|
|
1614
|
-
if (num <= 0) return "I";
|
|
1615
|
-
const map = [
|
|
1616
|
-
[1e3, "M"],
|
|
1617
|
-
[900, "CM"],
|
|
1618
|
-
[500, "D"],
|
|
1619
|
-
[400, "CD"],
|
|
1620
|
-
[100, "C"],
|
|
1621
|
-
[90, "XC"],
|
|
1622
|
-
[50, "L"],
|
|
1623
|
-
[40, "XL"],
|
|
1624
|
-
[10, "X"],
|
|
1625
|
-
[9, "IX"],
|
|
1626
|
-
[5, "V"],
|
|
1627
|
-
[4, "IV"],
|
|
1628
|
-
[1, "I"]
|
|
1629
|
-
];
|
|
1630
|
-
let n = num;
|
|
1631
|
-
let result = "";
|
|
1632
|
-
for (const [v, s] of map) {
|
|
1633
|
-
while (n >= v) {
|
|
1634
|
-
result += s;
|
|
1635
|
-
n -= v;
|
|
1636
|
-
}
|
|
1637
|
-
}
|
|
1638
|
-
return result;
|
|
1639
|
-
}
|
|
1640
|
-
function formatListMarker(format, counter) {
|
|
1641
|
-
switch ((format ?? "").toLowerCase()) {
|
|
1642
|
-
case "decimal":
|
|
1643
|
-
return `${counter}.`;
|
|
1644
|
-
case "lowerletter":
|
|
1645
|
-
return `${toLowerLetter(counter)}.`;
|
|
1646
|
-
case "upperletter":
|
|
1647
|
-
return `${toLowerLetter(counter).toUpperCase()}.`;
|
|
1648
|
-
case "lowerroman":
|
|
1649
|
-
return `${toRoman(counter).toLowerCase()}.`;
|
|
1650
|
-
case "upperroman":
|
|
1651
|
-
return `${toRoman(counter)}.`;
|
|
1652
|
-
case "bullet":
|
|
1653
|
-
default:
|
|
1654
|
-
return "\u2022";
|
|
1655
|
-
}
|
|
1656
|
-
}
|
|
1657
|
-
function formatListMarkerByPattern(pattern, currentLevel, countersByLevel, currentFormat) {
|
|
1658
|
-
if (!pattern || pattern.trim().length === 0) {
|
|
1659
|
-
return formatListMarker(currentFormat, countersByLevel[currentLevel] ?? 1);
|
|
1660
|
-
}
|
|
1661
|
-
const replaced = pattern.replace(/%(\d+)/g, (_, g1) => {
|
|
1662
|
-
const level1Based = Number.parseInt(g1, 10);
|
|
1663
|
-
if (!Number.isFinite(level1Based) || level1Based <= 0) return "";
|
|
1664
|
-
const levelIdx = level1Based - 1;
|
|
1665
|
-
const n = countersByLevel[levelIdx] ?? 0;
|
|
1666
|
-
if (n <= 0) return "";
|
|
1667
|
-
if (levelIdx === currentLevel) {
|
|
1668
|
-
return formatListMarker(currentFormat, n).replace(/\.$/, "");
|
|
1669
|
-
}
|
|
1670
|
-
return String(n);
|
|
1671
|
-
});
|
|
1672
|
-
const normalized = replaced.trim();
|
|
1673
|
-
if (!normalized) {
|
|
1674
|
-
return formatListMarker(currentFormat, countersByLevel[currentLevel] ?? 1);
|
|
1675
|
-
}
|
|
1676
|
-
return normalized;
|
|
1677
|
-
}
|
|
1678
|
-
function ensureStyleTag(doc, id) {
|
|
1679
|
-
let styleEl = doc.getElementById(id);
|
|
1680
|
-
if (!styleEl) {
|
|
1681
|
-
styleEl = doc.createElement("style");
|
|
1682
|
-
styleEl.id = id;
|
|
1683
|
-
doc.head.appendChild(styleEl);
|
|
1684
|
-
}
|
|
1685
|
-
return styleEl;
|
|
1686
|
-
}
|
|
1687
|
-
function applyBaseProfileCss(doc, styleProfile) {
|
|
1688
|
-
const styleEl = ensureStyleTag(doc, "__word_style_profile__");
|
|
1689
|
-
const targetWidthPx = styleProfile.contentWidthPx.toFixed(2);
|
|
1690
|
-
const topPaddingPx = styleProfile.pageMarginTopPx.toFixed(2);
|
|
1691
|
-
const bottomPaddingPx = styleProfile.pageMarginBottomPx.toFixed(2);
|
|
1692
|
-
const pageHeightPx = styleProfile.pageHeightPx.toFixed(2);
|
|
1693
|
-
const bodyLineHeightCss = styleProfile.bodyLineHeightRule === "auto" || styleProfile.bodyLineHeightPx === null ? styleProfile.bodyLineHeightRatio.toFixed(6) : `${styleProfile.bodyLineHeightPx.toFixed(2)}px`;
|
|
1694
|
-
styleEl.textContent = `
|
|
1
|
+
"use strict";var le=Object.create;var mt=Object.defineProperty;var ae=Object.getOwnPropertyDescriptor;var se=Object.getOwnPropertyNames;var ue=Object.getPrototypeOf,ce=Object.prototype.hasOwnProperty;var pe=(e,t)=>{for(var n in t)mt(e,n,{get:t[n],enumerable:!0})},$t=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of se(t))!ce.call(e,o)&&o!==n&&mt(e,o,{get:()=>t[o],enumerable:!(r=ae(t,o))||r.enumerable});return e};var Nt=(e,t,n)=>(n=e!=null?le(ue(e)):{},$t(t||!e||!e.__esModule?mt(n,"default",{value:e,enumerable:!0}):n,e)),me=e=>$t(mt({},"__esModule",{value:!0}),e);var Tn={};pe(Tn,{DocsWordElement:()=>ut,calculateFidelityScore:()=>oe,collectSemanticStatsFromDocument:()=>Ct,collectSemanticStatsFromHtml:()=>re,defineDocsWordElement:()=>ne,parseDocxToHtmlSnapshot:()=>Vt,parseDocxToHtmlSnapshotWithReport:()=>st});module.exports=me(Tn);var Lt='<!DOCTYPE html><html><head><meta charset="utf-8"/>',Ht="</head><body></body></html>";function nt(e){return e.trim()?/<html[\s>]/i.test(e)?e:`${Lt}${Ht}`.replace("<body></body>",`<body>${e}</body>`):`${Lt}${Ht}`}var At=Nt(require("jszip"),1);function de(){return{hyperlinkCount:0,anchorImageCount:0,chartCount:0,smartArtCount:0,ommlCount:0,tableCount:0,footnoteRefCount:0,endnoteRefCount:0,commentRefCount:0,revisionCount:0,pageBreakCount:0}}function W(e){return e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""")}function lt(e){return new DOMParser().parseFromString(e,"application/xml")}function Rt(e){return Array.from(e.children??[])}function O(e,t){let n=[],r=Rt(e).reverse();for(;r.length>0;){let o=r.pop();o.localName===t&&n.push(o);let i=o.children;for(let l=i.length-1;l>=0;l-=1)r.push(i[l])}return n}function L(e,t){let n=Rt(e).reverse();for(;n.length>0;){let r=n.pop();if(r.localName===t)return r;let o=r.children;for(let i=o.length-1;i>=0;i-=1)n.push(o[i])}return null}function x(e,t){return Array.from(e.children).filter(n=>n.localName===t)}function c(e,t){return e?e.getAttribute(t):null}function at(e){return e*96/914400}function dt(e){return e*96/1440}function ge(e){let t=O(e,"extent").find(s=>{let u=s.parentElement;return u?.localName==="inline"||u?.localName==="anchor"})??null;if(!t)return{widthPx:null,heightPx:null};let n=c(t,"cx"),r=c(t,"cy"),o=n?Number.parseInt(n,10):Number.NaN,i=r?Number.parseInt(r,10):Number.NaN,l=Number.isFinite(o)&&o>0?at(o):null,a=Number.isFinite(i)&&i>0?at(i):null;return{widthPx:l,heightPx:a}}function fe(e){let t=[];if(e.widthPx!==null&&t.push(`width="${Math.round(e.widthPx)}"`),e.heightPx!==null&&t.push(`height="${Math.round(e.heightPx)}"`),e.widthPx!==null||e.heightPx!==null){let n=["max-width:100%"];e.widthPx!==null&&n.push(`width:${e.widthPx.toFixed(2)}px`),e.heightPx!==null&&n.push(`height:${e.heightPx.toFixed(2)}px`),t.push(`style="${n.join(";")}"`)}return t.length>0?` ${t.join(" ")}`:""}function he(e){let t=null,n=null,r=x(e,"positionH")[0]??null,o=x(e,"positionV")[0]??null,i=r?x(r,"posOffset")[0]??null:null,l=o?x(o,"posOffset")[0]??null:null,a=i?.textContent?.trim()??"",s=l?.textContent?.trim()??"",u=a?Number.parseFloat(a):Number.NaN,f=s?Number.parseFloat(s):Number.NaN;return Number.isFinite(u)&&(t=at(u)),Number.isFinite(f)&&(n=at(f)),{leftPx:t,topPx:n}}function be(e){return x(e,"wrapSquare")[0]?"square":x(e,"wrapTight")[0]?"tight":x(e,"wrapTopAndBottom")[0]?"topAndBottom":x(e,"wrapNone")[0]?"none":null}function xe(e){let t=x(e,"anchor")[0]??null;if(!t)return null;let n=x(t,"positionH")[0]??null,r=x(t,"positionV")[0]??null,o=c(n,"relativeFrom"),i=c(r,"relativeFrom"),l=f=>{let d=c(t,f),h=d?Number.parseInt(d,10):Number.NaN;return Number.isFinite(h)&&h>=0?at(h):null},a=c(t,"relativeHeight"),s=a?Number.parseInt(a,10):Number.NaN,u=(f,d)=>{let h=(c(t,f)??"").toLowerCase();return h==="1"||h==="true"||h==="on"?!0:h==="0"||h==="false"||h==="off"?!1:d};return{position:he(t),wrapMode:be(t),distTPx:l("distT"),distBPx:l("distB"),distLPx:l("distL"),distRPx:l("distR"),relativeFromH:o,relativeFromV:i,behindDoc:u("behindDoc",!1),allowOverlap:u("allowOverlap",!0),layoutInCell:u("layoutInCell",!0),relativeHeight:Number.isFinite(s)?s:null}}function ye(e,t){if(!t)return e;let{position:n,wrapMode:r}=t;if(n.leftPx===null&&n.topPx===null)return e;let o=["position:absolute",n.leftPx!==null?`left:${n.leftPx.toFixed(2)}px`:"",n.topPx!==null?`top:${n.topPx.toFixed(2)}px`:"",`z-index:${t.behindDoc?0:t.relativeHeight??3}`,t.distTPx!==null?`margin-top:${t.distTPx.toFixed(2)}px`:"",t.distBPx!==null?`margin-bottom:${t.distBPx.toFixed(2)}px`:"",t.distLPx!==null?`margin-left:${t.distLPx.toFixed(2)}px`:"",t.distRPx!==null?`margin-right:${t.distRPx.toFixed(2)}px`:""].filter(l=>l.length>0);r==="topAndBottom"&&o.push("display:block","clear:both");let i=['data-word-anchor="1"',r?`data-word-wrap="${r}"`:"",t.relativeFromH?`data-word-anchor-relh="${W(t.relativeFromH)}"`:"",t.relativeFromV?`data-word-anchor-relv="${W(t.relativeFromV)}"`:"",t.behindDoc?'data-word-anchor-behind="1"':'data-word-anchor-behind="0"',t.allowOverlap?'data-word-anchor-overlap="1"':'data-word-anchor-overlap="0"',t.layoutInCell?'data-word-anchor-layout-cell="1"':'data-word-anchor-layout-cell="0"'].filter(l=>l.length>0).join(" ");return e.includes("style=")?e.replace(/style="([^"]*)"/,(l,a)=>`style="${[a,...o].filter(u=>u.length>0).join(";")}" ${i}`):`${e} style="${o.join(";")}" ${i}`}function we(e){if(!e)return{};let t=lt(e),n=O(t,"Relationship"),r={};for(let o of n){let i=c(o,"Id"),l=c(o,"Target");!i||!l||(r[i]=l)}return r}function Pe(e){let t=e.toLowerCase();return t==="png"?"image/png":t==="jpg"||t==="jpeg"?"image/jpeg":t==="gif"?"image/gif":t==="webp"?"image/webp":t==="bmp"?"image/bmp":t==="svg"?"image/svg+xml":"application/octet-stream"}function Et(e){let t=e.replace(/\\/g,"/").replace(/^\/+/,"");return t.startsWith("word/")?t:t.startsWith("../")?`word/${t.replace(/^(\.\.\/)+/,"")}`:`word/${t}`}function Ce(e,t,n){if(n&&n.trim())return`#${encodeURIComponent(n.trim())}`;if(!t)return null;let r=e[t];if(!r)return null;let o=r.trim();if(!o)return null;let i=o.toLowerCase();return i.startsWith("http://")||i.startsWith("https://")||i.startsWith("mailto:")||i.startsWith("tel:")||o.startsWith("#")?o:`#${encodeURIComponent(o)}`}async function ve(e,t,n){let r=t[n];if(!r)return null;let o=Et(r),i=e.file(o);if(!i)return null;let l=await i.async("base64"),a=o.split(".").pop()??"bin";return`data:${Pe(a)};base64,${l}`}async function Tt(e,t,n){let r=t[n];if(!r)return null;let o=Et(r),i=e.file(o);return i?i.async("string"):null}function Se(e){let t=["barChart","lineChart","pieChart","areaChart","scatterChart","radarChart","doughnutChart"];for(let n of t)if(L(e,n))return n.replace(/Chart$/,"");return"unknown"}function Fe(e){let t=lt(e),n=O(t,"t").map(l=>(l.textContent??"").trim()).find(l=>l.length>0)??"Chart",r=O(t,"ser").length,o=O(t,"pt").length,i=Se(t);return{title:n,type:i,seriesCount:r,pointCount:o}}function $e(e){let t=lt(e);return O(t,"t").map(n=>(n.textContent??"").trim()).filter(n=>n.length>0).slice(0,12)}function Z(e){if(e.localName==="t")return e.textContent??"";if(e.localName==="f"){let t=L(e,"num"),n=L(e,"den");return`(${t?Z(t):"?"})/(${n?Z(n):"?"})`}if(e.localName==="sSup"){let t=L(e,"e"),n=L(e,"sup");return`${t?Z(t):""}^(${n?Z(n):""})`}if(e.localName==="sSub"){let t=L(e,"e"),n=L(e,"sub");return`${t?Z(t):""}_(${n?Z(n):""})`}if(e.localName==="rad"){let t=L(e,"e");return`sqrt(${t?Z(t):""})`}return Array.from(e.children).map(t=>Z(t)).join("")}function Ne(e){if(!e)return"";let t=[];L(e,"b")&&t.push("font-weight:700"),L(e,"i")&&t.push("font-style:italic"),L(e,"u")&&t.push("text-decoration:underline"),L(e,"strike")&&t.push("text-decoration:line-through");let n=L(e,"color"),r=c(n,"w:val")??c(n,"val");r&&r.toLowerCase()!=="auto"&&t.push(`color:#${r}`);let o=L(e,"highlight");(c(o,"w:val")??c(o,"val")??"").toLowerCase()==="yellow"&&t.push("background-color:#fff200");let l=L(e,"vertAlign"),a=(c(l,"w:val")??c(l,"val")??"").toLowerCase();return a==="superscript"&&t.push("vertical-align:super;font-size:0.83em"),a==="subscript"&&t.push("vertical-align:sub;font-size:0.83em"),t.join(";")}function Le(e){let t=L(e,"pPr"),n=t?L(t,"pStyle"):null,r=(c(n,"w:val")??c(n,"val")??"").toLowerCase();return r.includes("heading1")||r==="1"||r==="heading 1"?"h1":r.includes("heading2")||r==="2"||r==="heading 2"?"h2":r.includes("heading3")||r==="3"||r==="heading 3"?"h3":"p"}function He(e){let t=L(e,"pPr"),n=t?L(t,"jc"):null,r=(c(n,"w:val")??c(n,"val")??"").toLowerCase();return r==="center"||r==="right"||r==="left"?`text-align:${r};`:""}function Dt(e){return e===null?"":` data-word-p-index="${e}"`}function Mt(e){return O(e,"p").map(n=>Bt(n)).join("<br/>").trim()}function kt(e,t,n){if(!e)return{};let r=lt(e),o={},i=O(r,t);for(let l of i){let a=c(l,"w:id")??c(l,"id");if(!a)continue;if(n.requirePositiveNumericId){let u=Number.parseInt(a,10);if(!Number.isFinite(u)||u<=0)continue}let s=Mt(l);s&&(o[a]=s)}return o}function Te(e){return kt(e,"footnote",{requirePositiveNumericId:!0})}function Ae(e){if(!e)return{};let t=lt(e),n={},r=O(t,"comment");for(let o of r){let i=c(o,"w:id")??c(o,"id");if(!i)continue;let l=Mt(o);l&&(n[i]={author:c(o,"w:author")??c(o,"author"),date:c(o,"w:date")??c(o,"date"),text:l})}return n}function Re(e){return kt(e,"endnote",{requirePositiveNumericId:!0})}function Ee(e,t){let n=[...new Set(e)].filter(o=>t[o]);return n.length===0?"":`<section data-word-footnotes="1"><hr/><ol>${n.map(o=>`<li id="word-footnote-${o}" data-word-footnote-id="${o}">${t[o]}</li>`).join("")}</ol></section>`}function De(e,t){let n=[...new Set(e)].filter(o=>t[o]);return n.length===0?"":`<section data-word-comments="1"><hr/><ol>${n.map(o=>{let i=t[o],l=[i.author??"",i.date??""].filter(s=>s.length>0).join(" \xB7 "),a=l?`<div data-word-comment-meta="1">${W(l)}</div>`:"";return`<li id="word-comment-${o}" data-word-comment-id="${o}">${a}<div>${i.text}</div></li>`}).join("")}</ol></section>`}function Me(e,t){let n=[...new Set(e)].filter(o=>t[o]);return n.length===0?"":`<section data-word-endnotes="1"><hr/><ol>${n.map(o=>`<li id="word-endnote-${o}" data-word-endnote-id="${o}">${t[o]}</li>`).join("")}</ol></section>`}function It(e){let t={text:"",delText:"",lineBreakCount:0,pageBreakCount:0},n=[e];for(;n.length>0;){let r=n.pop(),o=r.localName;o==="t"?t.text+=r.textContent??"":o==="delText"?t.delText+=r.textContent??"":o==="br"&&((c(r,"w:type")??c(r,"type")??"").toLowerCase()==="page"?t.pageBreakCount+=1:t.lineBreakCount+=1);let i=r.children;for(let l=i.length-1;l>=0;l-=1)n.push(i[l])}return t}async function ke(e,t,n,r,o,i,l,a,s,u,f){let d=Le(r),h=He(r),y=Dt(o);if(!(L(r,"r")!==null||L(r,"oMath")!==null||L(r,"oMathPara")!==null))return`<${d}${y}${h?` style="${h}"`:""}><br/></${d}>`;function H(m,T){return{type:T,id:c(m,"w:id")??c(m,"id"),author:c(m,"w:author")??c(m,"author"),date:c(m,"w:date")??c(m,"date")}}function S(m,T){if(T)return T;let w=m;for(;w;){if(w.localName==="ins")return H(w,"ins");if(w.localName==="del")return H(w,"del");if(w.localName==="p")break;w=w.parentElement}return null}function P(m){let T=[`data-word-revision="${m.type}"`];return m.id&&T.push(`data-word-revision-id="${W(m.id)}"`),m.author&&T.push(`data-word-revision-author="${W(m.author)}"`),m.date&&T.push(`data-word-revision-date="${W(m.date)}"`),T.join(" ")}async function $(m,T){let w=[],v=x(m,"rPr")[0]??null,A=Ne(v),D=x(m,"footnoteReference")[0]??null,I=c(D,"w:id")??c(D,"id");if(I&&i[I])return n.features.footnoteRefCount+=1,l.push(I),w.push(`<sup data-word-footnote-ref="${I}"><a href="#word-footnote-${I}">[${I}]</a></sup>`),w;let Y=x(m,"endnoteReference")[0]??null,U=c(Y,"w:id")??c(Y,"id");if(U&&a[U])return n.features.endnoteRefCount+=1,s.push(U),w.push(`<sup data-word-endnote-ref="${U}"><a href="#word-endnote-${U}">[${U}]</a></sup>`),w;let ct=x(m,"commentReference")[0]??null,Q=c(ct,"w:id")??c(ct,"id");if(Q&&u[Q])return n.features.commentRefCount+=1,f.push(Q),w.push(`<sup data-word-comment-ref="${Q}"><a href="#word-comment-${Q}">[c${Q}]</a></sup>`),w;let tt=x(m,"drawing")[0]??null;if(tt){let z=L(tt,"blip"),B=c(z,"r:embed")??c(z,"embed");if(B){let et=await ve(e,t,B);if(et){let q=ge(tt),xt=fe(q),Ft=xe(tt),ie=ye(xt,Ft);return Ft&&(n.features.anchorImageCount+=1),w.push(`<img src="${et}" alt="word-image"${ie}/>`),w}}let ot=L(tt,"chart"),pt=c(ot,"r:id")??c(ot,"id");if(pt){let et=await Tt(e,t,pt);if(et){let q=Fe(et);return n.features.chartCount+=1,w.push(`<figure data-word-chart="1" data-word-chart-type="${q.type}" data-word-chart-series="${q.seriesCount}" data-word-chart-points="${q.pointCount}"><figcaption>${W(q.title)}</figcaption><div>Chart(${W(q.type)}): series=${q.seriesCount}, points=${q.pointCount}</div></figure>`),w}}let vt=L(tt,"relIds"),St=c(vt,"r:dm")??c(vt,"dm");if(St){let et=await Tt(e,t,St),q=et?$e(et):[];n.features.smartArtCount+=1;let xt=q.length>0?`: ${W(q.join(" / "))}`:"";return w.push(`<figure data-word-smartart="1" data-word-smartart-items="${q.length}"><figcaption>SmartArt fallback${xt}</figcaption></figure>`),w}}let rt=It(m),it=`${W(rt.text||rt.delText)}${"<br/>".repeat(rt.lineBreakCount)}`;if(it){let z=S(m,T);if(A){let B=`<span style="${A}">${it}</span>`;if(z){n.features.revisionCount+=1;let ot=z.type==="ins"?"ins":"del";w.push(`<${ot} ${P(z)}>${B}</${ot}>`)}else w.push(B)}else if(z){n.features.revisionCount+=1;let B=z.type==="ins"?"ins":"del";w.push(`<${B} ${P(z)}>${it}</${B}>`)}else w.push(it)}for(let z=0;z<rt.pageBreakCount;z+=1)n.features.pageBreakCount+=1,w.push('<span data-word-page-break="1" style="display:block;break-before:page"></span>');return w}async function F(m,T){if(m.localName==="commentRangeStart"){let v=c(m,"w:id")??c(m,"id");return v?[`<span data-word-comment-range-start="${v}"></span>`]:[]}if(m.localName==="commentRangeEnd"){let v=c(m,"w:id")??c(m,"id");return v?[`<span data-word-comment-range-end="${v}"></span>`]:[]}if(m.localName==="r")return $(m,T);if(m.localName==="hyperlink"){let v=c(m,"r:id")??c(m,"id"),A=c(m,"w:anchor")??c(m,"anchor"),D=Ce(t,v,A),I=[];for(let U of Array.from(m.children))I.push(...await F(U,T));let Y=I.join("")||W(m.textContent??"");return D?(n.features.hyperlinkCount+=1,[`<a data-word-hyperlink="1" href="${W(D)}" rel="noreferrer noopener" target="_blank">${Y}</a>`]):Y?[Y]:[]}if(m.localName==="oMath"||m.localName==="oMathPara"){let v=Z(m).trim();return v?(n.features.ommlCount+=1,[`<span data-word-omml="1">${W(v)}</span>`]):[]}if(m.localName==="ins"||m.localName==="del"){let v=H(m,m.localName==="ins"?"ins":"del"),A=[];for(let D of Array.from(m.children))A.push(...await F(D,v));return A}let w=[];for(let v of Array.from(m.children))w.push(...await F(v,T));return w}let N=[],E=O(r,"lastRenderedPageBreak").length;for(let m=0;m<E;m+=1)n.features.pageBreakCount+=1,N.push('<span data-word-page-break="1" style="display:block;break-before:page"></span>');for(let m of Array.from(r.children))N.push(...await F(m,null));let R=N.join("")||"<br/>";return`<${d}${y}${h?` style="${h}"`:""}>${R}</${d}>`}function Ie(e){let t=It(e),n=t.lineBreakCount+t.pageBreakCount;return`${W(t.text||t.delText)}${"<br/>".repeat(n)}`}function Bt(e){return O(e,"r").map(r=>Ie(r)).join("")||"<br/>"}function Be(e){let t=x(e,"tcPr")[0]??null,n=t?x(t,"gridSpan")[0]??null:null,r=c(n,"w:val")??c(n,"val"),o=r?Number.parseInt(r,10):Number.NaN;return Number.isFinite(o)&&o>0?o:1}function We(e){let t=x(e,"tcPr")[0]??null,n=t?x(t,"vMerge")[0]??null:null;return n?(c(n,"w:val")??c(n,"val")??"continue").toLowerCase()==="restart"?"restart":"continue":"none"}function Ve(e){let t=x(e,"tblGrid")[0]??null;return t?x(t,"gridCol").map(n=>{let r=c(n,"w:w")??c(n,"w"),o=r?Number.parseInt(r,10):Number.NaN;return Number.isFinite(o)&&o>0?dt(o):0}).filter(n=>n>0):[]}function ze(e){return e/6}function G(e){if(!e)return null;let t=(c(e,"w:val")??c(e,"val")??"").toLowerCase();if(!t||t==="nil"||t==="none")return"none";let n=(c(e,"w:color")??c(e,"color")??"222222").replace(/^#/,""),r=c(e,"w:sz")??c(e,"sz"),o=r?Number.parseInt(r,10):Number.NaN,i=Number.isFinite(o)&&o>0?ze(o):1,l=t==="single"?"solid":t;return`${i.toFixed(2)}px ${l} #${n}`}function je(e){let t=x(e,"tblPr")[0]??null,n=t?x(t,"tblBorders")[0]??null:null,r=t?x(t,"tblLayout")[0]??null:null,o=t?x(t,"tblCellSpacing")[0]??null:null,i=(c(o,"w:type")??c(o,"type")??"dxa").toLowerCase(),l=c(o,"w:w")??c(o,"w"),a=l?Number.parseFloat(l):Number.NaN,s=i==="dxa"&&Number.isFinite(a)&&a>0?dt(a):0,u=s>0?"separate":"collapse",f=(c(r,"w:type")??c(r,"type")??"").toLowerCase()==="autofit"?"auto":"fixed",d=G(n?x(n,"top")[0]??null:null),h=G(n?x(n,"bottom")[0]??null:null),y=G(n?x(n,"left")[0]??null:null),C=G(n?x(n,"right")[0]??null:null),H=G(n?x(n,"insideH")[0]??null:null),S=G(n?x(n,"insideV")[0]??null:null);return{tableLayout:f,borderCollapse:u,borderSpacingPx:s,borderCss:d??C??h??y??"1px solid #222",insideHCss:H,insideVCss:S}}function qe(e,t){let n=x(e,"tblPr")[0]??null,r=n?x(n,"tblW")[0]??null:null,o=(c(r,"w:type")??c(r,"type")??"").toLowerCase(),i=c(r,"w:w")??c(r,"w"),l=i?Number.parseFloat(i):Number.NaN;if(o==="dxa"&&Number.isFinite(l)&&l>0)return`width:${dt(l).toFixed(2)}px`;if(o==="pct"&&Number.isFinite(l)&&l>0)return`width:${(l/50).toFixed(2)}%`;let a=t.reduce((s,u)=>s+u,0);return a>0?`width:${a.toFixed(2)}px;max-width:100%`:"width:100%"}function Oe(e,t,n,r){let o=x(e,"tcPr")[0]??null,i=o?x(o,"tcW")[0]??null:null,l=(c(i,"w:type")??c(i,"type")??"").toLowerCase(),a=c(i,"w:w")??c(i,"w"),s=a?Number.parseFloat(a):Number.NaN;if(l==="dxa"&&Number.isFinite(s)&&s>0)return`width:${dt(s).toFixed(2)}px`;if(l==="pct"&&Number.isFinite(s)&&s>0)return`width:${(s/50).toFixed(2)}%`;let u=r.slice(t,t+n).reduce((f,d)=>f+d,0);return u>0?`width:${u.toFixed(2)}px`:""}function _e(e,t){let n=x(e,"tcPr")[0]??null,r=n?x(n,"tcBorders")[0]??null:null;if(!r)return`border:${t.insideHCss??t.insideVCss??t.borderCss}`;let o=G(x(r,"top")[0]??null)??t.insideHCss??t.borderCss,i=G(x(r,"right")[0]??null)??t.insideVCss??t.borderCss,l=G(x(r,"bottom")[0]??null)??t.insideHCss??t.borderCss,a=G(x(r,"left")[0]??null)??t.insideVCss??t.borderCss;return`border-top:${o};border-right:${i};border-bottom:${l};border-left:${a}`}function Xe(e,t,n){let r=[];for(let i of Array.from(e.children))if(i.localName!=="tcPr"){if(i.localName==="p"){let l=t.get(i)??null;r.push(`<p${Dt(l)}>${Bt(i)}</p>`);continue}if(i.localName==="tbl"){r.push(Wt(i,t,n));continue}}if(r.length>0)return r.join("");let o=O(e,"t").map(i=>i.textContent??"").join("").trim();return W(o)||"<br/>"}function Wt(e,t,n){n.features.tableCount+=1;let r=x(e,"tr"),o=Ve(e),i=je(e),l=new Map,a=[],s=1,f=r.map((y,C)=>{let H=x(y,"tc"),S=new Set,P=[],$=0;for(let F of H){let N=Be(F),E=We(F);if(E==="continue"){let v=Array.from(new Set(l.values())).filter(D=>!S.has(D)).sort((D,I)=>D.startCol-I.startCol),A=v.find(D=>D.startCol>=$)??v[0]??null;A&&(A.rowSpan+=1,S.add(A),$=A.startCol+A.colSpan);continue}for(;l.has($);)$+=1;let R=Xe(F,t,n),m=[],T=Oe(F,$,N,o),w=_e(F,i);if(E==="restart"){let v={id:`m${s}`,startCol:$,colSpan:N,rowSpan:1,startedRow:C};s+=1,a.push(v);for(let A=0;A<N;A+=1)l.set($+A,v);m.push(`data-word-merge-id="${v.id}"`)}N>1&&m.push(`colspan="${N}"`),P.push(`<td${m.length>0?` ${m.join(" ")}`:""} style="${w};vertical-align:top;${T}">${R}</td>`),$+=N}for(let F of Array.from(new Set(l.values())))if(F.startedRow<C&&!S.has(F))for(let N=0;N<F.colSpan;N+=1)l.delete(F.startCol+N);return`<tr>${P.join("")}</tr>`}).join("");for(let y of a){let C=`data-word-merge-id="${y.id}"`,H=y.rowSpan>1?`rowspan="${y.rowSpan}"`:"";f=f.replace(C,H).replace(/\s{2,}/g," ")}let d=qe(e,o),h=i.borderSpacingPx>0?`border-spacing:${i.borderSpacingPx.toFixed(2)}px;`:"";return`<table style="border-collapse:${i.borderCollapse};${h}table-layout:${i.tableLayout};${d};border:${i.borderCss};">${f}</table>`}async function st(e){let t=Date.now(),n={features:de()},r=e.arrayBuffer,o=r?await r.call(e):await new Response(e).arrayBuffer(),i=await At.default.loadAsync(o),l=await i.file("word/document.xml")?.async("string");if(!l)throw new Error("DOCX missing document.xml");let a=await i.file("word/_rels/document.xml.rels")?.async("string"),s=await i.file("word/footnotes.xml")?.async("string"),u=await i.file("word/endnotes.xml")?.async("string"),f=await i.file("word/comments.xml")?.async("string"),d=we(a??null),h=Te(s??null),y=Re(u??null),C=Ae(f??null),H=[],S=[],P=[],$=lt(l),F=L($,"body");if(!F)throw new Error("DOCX missing body");let N=new Map;O($,"p").forEach((R,m)=>{N.set(R,m)});let E=[];for(let R of Array.from(F.children))if(R.localName!=="sectPr"){if(R.localName==="p"){let m=N.get(R)??null;E.push(await ke(i,d,n,R,m,h,H,y,S,C,P));continue}if(R.localName==="tbl"){E.push(Wt(R,N,n));continue}}return E.push(Ee(H,h)),E.push(Me(S,y)),E.push(De(P,C)),{htmlSnapshot:nt(E.join(`
|
|
2
|
+
`)),report:{elapsedMs:Date.now()-t,features:n.features}}}async function Vt(e){return(await st(e)).htmlSnapshot}function Ue(e){return e.replaceAll("&","&").replaceAll('"',""")}async function zt(e){let t=await new Response(e).arrayBuffer(),n=new Uint8Array(t),r="";for(let l of n)r+=String.fromCharCode(l);let o=btoa(r);return`data:${e.type||"application/octet-stream"};base64,${o}`}function Ge(e){let t=e.trim().toLowerCase();return t.startsWith("file:")||t.startsWith("blob:")||t.startsWith("cid:")||t.startsWith("mhtml:")||t.startsWith("ms-appx:")||t.startsWith("ms-appdata:")}async function jt(e,t){if(!e.trim()||t.length===0)return e;let r=new DOMParser().parseFromString(e,"text/html"),o=Array.from(r.querySelectorAll("img")),i=await Promise.all(t.map(a=>zt(a))),l=0;for(let a of o){let s=a.getAttribute("src")??"";Ge(s)&&l<i.length&&(a.setAttribute("src",i[l]),l+=1)}return r.body.innerHTML}async function qt(e){return e.length===0?"":(await Promise.all(e.map(n=>zt(n)))).map(n=>`<p><img src="${Ue(n)}" alt="clipboard-image" /></p>`).join(`
|
|
3
|
+
`)}async function Ot(e){let t=e.getData("text/html")||"",n=e.getData("text/plain")||"",r=Array.from(e.items).filter(l=>l.kind==="file"&&l.type.startsWith("image/")).map(l=>l.getAsFile()).filter(l=>l!==null),o=await jt(t,r);return{html:o.trim()?o:await qt(r),text:n,imageFiles:r}}async function _t(e){let t="",n="",r=[];for(let l of e){l.types.includes("text/html")&&!t&&(t=await(await l.getType("text/html")).text()),l.types.includes("text/plain")&&!n&&(n=await(await l.getType("text/plain")).text());for(let a of l.types){if(!a.startsWith("image/"))continue;let s=await l.getType(a),u=`clipboard-${Date.now()}-${r.length}.${a.split("/")[1]??"bin"}`;r.push(new File([s],u,{type:a}))}}let o=await jt(t,r);return{html:o.trim()?o:await qt(r),text:n,imageFiles:r}}function Je(e){let t=new Map;for(let n of e.split(";")){let[r,...o]=n.split(":"),i=r?.trim().toLowerCase(),l=o.join(":").trim();!i||!l||t.set(i,l)}return t}function Ke(e){return Array.from(e.entries()).map(([t,n])=>`${t}: ${n}`).join("; ")}function Ze(e){let t=[["mso-ansi-font-size","font-size"],["mso-bidi-font-size","font-size"],["mso-hansi-font-size","font-size"],["mso-margin-top-alt","margin-top"],["mso-margin-bottom-alt","margin-bottom"],["mso-margin-left-alt","margin-left"],["mso-margin-right-alt","margin-right"],["mso-line-height-alt","line-height"]];for(let[r,o]of t){let i=e.get(r);i&&(e.has(o)||e.set(o,i))}let n=e.get("mso-foreground");n&&!e.has("color")&&e.set("color",n),e.get("mso-table-lspace")&&!e.has("margin-left")&&e.set("margin-left","0"),e.get("mso-table-rspace")&&!e.has("margin-right")&&e.set("margin-right","0")}function Ye(e,t){let n=(e.getAttribute("class")??"").toLowerCase(),r=t.get("mso-list")??"";if(!(n.includes("msolist")||r.length>0))return;t.has("text-indent")||t.set("text-indent","0");let i=t.get("margin-left");i&&!t.has("padding-left")&&t.set("padding-left",i)}function Qe(e,t){let n=e.tagName.toLowerCase();n==="table"&&(t.has("border-collapse")||t.set("border-collapse","collapse"),t.has("border-spacing")||t.set("border-spacing","0")),(n==="td"||n==="th")&&!t.has("vertical-align")&&t.set("vertical-align","top"),n==="p"&&!t.has("min-height")&&t.set("min-height","1em")}function tn(e){let t=Array.from(e.querySelectorAll("p"));for(let n of t){let r=(n.textContent??"").replace(/\u00a0/g," ").trim();!(n.querySelector("img,table,svg,canvas,br")!==null)&&r.length===0&&(n.innerHTML="<br/>")}}function en(e,t){let n=e.body;if(n&&(t?.forceBodyFontFamily&&(n.style.fontFamily=t.forceBodyFontFamily),t?.forceHeadingFontFamily)){let r=Array.from(e.querySelectorAll("h1,h2,h3,h4,h5,h6"));for(let o of r)o.style.fontFamily=t.forceHeadingFontFamily}}function Xt(e,t){let n=Array.from(e.querySelectorAll("[style], p, table, td, th, li, div, span"));for(let r of n){let o=r,i=o.getAttribute("style")??"",l=Je(i);Ze(l),Ye(o,l),Qe(o,l),l.size>0&&o.setAttribute("style",Ke(l))}tn(e),en(e,t)}var Ut=Nt(require("jszip"),1),M={bodyFontPx:14.6667,bodyLineHeightRatio:1.158333,bodyLineHeightPx:null,bodyLineHeightRule:"auto",paragraphAfterPx:10.67,contentWidthPx:553.73,pageHeightPx:1122.53,pageMarginTopPx:96,pageMarginBottomPx:96,titleFontPx:32,titleColor:"#0F4761",titleAlign:"center",bodyFontFamily:'"Times New Roman", "Noto Serif SC", serif',titleFontFamily:'DengXian, "Noto Sans SC", "Microsoft YaHei", sans-serif',discoveredFonts:[],tableCellPaddingTopPx:0,tableCellPaddingLeftPx:7.2,tableCellPaddingBottomPx:0,tableCellPaddingRightPx:7.2,paragraphProfiles:[],trailingDateText:null,trailingDateAlignedRight:!1,trailingDateParagraphIndex:null,trailingEmptyParagraphCountBeforeDate:0};function Gt(e="snapshot"){return{sourceFileName:e,...M,paragraphProfiles:[]}}function k(e){return e/15}function p(e,t){return e?e.getAttribute(t):null}function b(e,t){let n=p(e,t);if(!n)return null;let r=Number.parseFloat(n);return Number.isFinite(r)?r:null}function K(e,t){return Array.from(e.querySelectorAll("*")).find(r=>r.localName===t)??null}function g(e,t){return Array.from(e.querySelectorAll("*")).filter(r=>r.localName===t)}function gt(e){return new DOMParser().parseFromString(e,"application/xml")}function nn(e){let t=K(e,"sectPr");if(!t)return{contentWidthPx:null,pageHeightPx:null,marginTopPx:null,marginBottomPx:null};let n=g(t,"pgSz")[0]??null,r=g(t,"pgMar")[0]??null,o=b(n,"w:w")??b(n,"w")??null,i=b(n,"w:h")??b(n,"h")??null,l=b(r,"w:left")??b(r,"left")??0,a=b(r,"w:right")??b(r,"right")??0,s=b(r,"w:top")??b(r,"top")??null,u=b(r,"w:bottom")??b(r,"bottom")??null;return{contentWidthPx:o===null?null:k(o-l-a),pageHeightPx:i===null?null:k(i),marginTopPx:s===null?null:k(s),marginBottomPx:u===null?null:k(u)}}function rn(e){let t=g(e,"p");for(let n of t){let r=g(n,"pPr")[0]??null;if(!r)continue;let o=g(r,"pStyle")[0]??null,i=p(o,"w:val")??p(o,"val")??"";if(!(i==="1"||i.toLowerCase().includes("heading")))continue;let a=g(r,"jc")[0]??null,s=(p(a,"w:val")??p(a,"val")??"").toLowerCase();return s==="center"?"center":s==="right"?"right":"left"}return null}function ft(e){return g(e,"t").map(n=>n.textContent??"").join("").trim()}function Jt(e){let t=g(e,"pPr")[0]??null,n=t?g(t,"jc")[0]??null:null,r=(p(n,"w:val")??p(n,"val")??"").toLowerCase();return r==="center"?"center":r==="right"?"right":"left"}function on(e){let t=g(e,"p");if(t.length===0)return{trailingDateText:null,trailingDateAlignedRight:!1,trailingDateParagraphIndex:null,trailingEmptyParagraphCountBeforeDate:0};let n=-1;for(let s=t.length-1;s>=0;s-=1)if(ft(t[s]).length>0){n=s;break}if(n<0)return{trailingDateText:null,trailingDateAlignedRight:!1,trailingDateParagraphIndex:null,trailingEmptyParagraphCountBeforeDate:0};let r=t[n],o=ft(r),i=/\d{4}\s*年\s*\d+\s*月\s*\d+\s*日/.test(o),l=Jt(r),a=0;for(let s=n-1;s>=0;s-=1){if(ft(t[s]).length===0){a+=1;continue}break}return{trailingDateText:i?o:null,trailingDateAlignedRight:i&&l==="right",trailingDateParagraphIndex:i?n:null,trailingEmptyParagraphCountBeforeDate:i?a:0}}function J(e){if(!e)return null;let t=Number.parseInt(e,10);return Number.isFinite(t)?t:null}function ln(e){let t=new Map;if(!e)return t;let n=new Map,r=g(e,"abstractNum");for(let i of r){let l=J(p(i,"w:abstractNumId")??p(i,"abstractNumId"));if(l===null)continue;let a=g(i,"lvl"),s=new Map;for(let u of a){let f=J(p(u,"w:ilvl")??p(u,"ilvl"));if(f===null)continue;let d=g(u,"numFmt")[0]??null,h=g(u,"lvlText")[0]??null;s.set(f,{numFmt:p(d,"w:val")??p(d,"val")??null,lvlText:p(h,"w:val")??p(h,"val")??null,startAt:J(p(g(u,"start")[0]??null,"w:val")??p(g(u,"start")[0]??null,"val"))??1})}n.set(l,s)}let o=g(e,"num");for(let i of o){let l=J(p(i,"w:numId")??p(i,"numId"));if(l===null)continue;let a=g(i,"abstractNumId")[0]??null,s=J(p(a,"w:val")??p(a,"val"));if(s===null)continue;let u=n.get(s);if(!u)continue;for(let[d,h]of u.entries())t.set(`${l}:${d}`,{...h});let f=g(i,"lvlOverride");for(let d of f){let h=J(p(d,"w:ilvl")??p(d,"ilvl"));if(h===null)continue;let y=`${l}:${h}`,C=t.get(y)??{numFmt:null,lvlText:null,startAt:1},H=J(p(g(d,"startOverride")[0]??null,"w:val")??p(g(d,"startOverride")[0]??null,"val")),S=g(d,"lvl")[0]??null,P=S?g(S,"numFmt")[0]??null:null,$=S?g(S,"lvlText")[0]??null:null,F=J(p(g(S??d,"start")[0]??null,"w:val")??p(g(S??d,"start")[0]??null,"val"));t.set(y,{numFmt:p(P,"w:val")??p(P,"val")??C.numFmt,lvlText:p($,"w:val")??p($,"val")??C.lvlText,startAt:H??F??C.startAt})}}return t}function an(e,t){return g(e,"p").map((r,o)=>{let i=ft(r),l=g(r,"pPr")[0]??null,a=l?g(l,"spacing")[0]??null:null,s=l?g(l,"ind")[0]??null:null,u=l?g(l,"numPr")[0]??null:null,f=u?g(u,"ilvl")[0]??null:null,d=u?g(u,"numId")[0]??null:null,h=J(p(f,"w:val")??p(f,"val")),y=J(p(d,"w:val")??p(d,"val")),C=y!==null&&h!==null?t.get(`${y}:${h}`):void 0,H=l?g(l,"keepNext")[0]??null:null,S=l?g(l,"keepLines")[0]??null:null,P=l?g(l,"pageBreakBefore")[0]??null:null,$=g(r,"lastRenderedPageBreak")[0]??null,F=l?g(l,"sectPr")[0]??null:null,N=b(a,"w:before")??b(a,"before")??null,E=b(a,"w:after")??b(a,"after")??null,R=b(a,"w:line")??b(a,"line")??null,m=(p(a,"w:lineRule")??p(a,"lineRule")??"auto").toLowerCase(),T=R===null?null:m==="exact"?"exact":m==="atleast"?"atLeast":"auto",w=b(s,"w:left")??b(s,"left")??null,v=b(s,"w:right")??b(s,"right")??null,A=b(s,"w:firstLine")??b(s,"firstLine")??null,D=b(s,"w:hanging")??b(s,"hanging")??null,I=un(r);return{index:o,text:i,isEmpty:i.length===0,align:Jt(r),beforePx:N===null?null:k(N),afterPx:E===null?null:k(E),lineHeightRatio:R===null||T!=="auto"?null:R/240,lineHeightPx:R===null||T==="auto"?null:k(R),lineHeightRule:T,indentLeftPx:w===null?null:k(w),indentRightPx:v===null?null:k(v),firstLinePx:A===null?null:k(A),hangingPx:D===null?null:k(D),listNumId:y,listLevel:h,listFormat:C?.numFmt??null,listTextPattern:C?.lvlText??null,listStartAt:C?.startAt??1,keepNext:H!==null&&(p(H,"w:val")??p(H,"val")??"1")!=="0",keepLines:S!==null&&(p(S,"w:val")??p(S,"val")??"1")!=="0",pageBreakBefore:$!==null||P!==null&&(p(P,"w:val")??p(P,"val")??"1")!=="0",sectionBreakBefore:F!==null,runs:I}})}function sn(e){let t=g(e,"style").filter(u=>(p(u,"w:type")??p(u,"type")??"").toLowerCase()==="table"),n=t.find(u=>(p(u,"w:styleId")??p(u,"styleId")??"").toLowerCase()==="a1")??t[0]??null;if(!n)return{topPx:null,leftPx:null,bottomPx:null,rightPx:null};let r=g(n,"tblPr")[0]??null,o=r?g(r,"tblCellMar")[0]??null:null,i=o?g(o,"top")[0]??null:null,l=o?g(o,"left")[0]??null:null,a=o?g(o,"bottom")[0]??null:null,s=o?g(o,"right")[0]??null:null;return{topPx:(()=>{let u=b(i,"w:w")??b(i,"w")??null;return u===null?null:k(u)})(),leftPx:(()=>{let u=b(l,"w:w")??b(l,"w")??null;return u===null?null:k(u)})(),bottomPx:(()=>{let u=b(a,"w:w")??b(a,"w")??null;return u===null?null:k(u)})(),rightPx:(()=>{let u=b(s,"w:w")??b(s,"w")??null;return u===null?null:k(u)})()}}function un(e){return g(e,"r").map(n=>{let r=g(n,"rPr")[0]??null,o=g(n,"t"),i=g(n,"br"),l=o.map(pt=>pt.textContent??"").join("");if(i.length>0&&(l+=`
|
|
4
|
+
`.repeat(i.length)),!l)return null;let a=r?g(r,"sz")[0]??null:null,s=b(a,"w:val")??b(a,"val")??null,u=s===null?null:s/2*(96/72),f=r?g(r,"color")[0]??null:null,d=p(f,"w:val")??p(f,"val")??null,h=d&&d.toLowerCase()!=="auto"?`#${d}`:null,y=r?g(r,"highlight")[0]??null:null,C=(p(y,"w:val")??p(y,"val")??"").toLowerCase(),S=C&&C!=="none"?{yellow:"#fff59d",green:"#b9f6ca",cyan:"#b2ebf2",magenta:"#f8bbd0",blue:"#bbdefb",red:"#ffcdd2",darkyellow:"#fbc02d",darkgreen:"#66bb6a",darkblue:"#64b5f6",darkred:"#e57373",darkcyan:"#4dd0e1",darkmagenta:"#ba68c8",gray:"#e0e0e0",lightgray:"#f5f5f5"}[C]??null:null,P=r?g(r,"shd")[0]??null:null,$=(p(P,"w:fill")??p(P,"fill")??"").toLowerCase(),F=$&&$!=="auto"?`#${$}`:null,N=r?g(r,"spacing")[0]??null:null,E=b(N,"w:val")??b(N,"val")??null,R=E===null?null:E/20*(96/72),m=r?g(r,"b")[0]??null:null,T=r?g(r,"i")[0]??null:null,w=r?g(r,"u")[0]??null:null,v=r?g(r,"strike")[0]??null:null,A=r?g(r,"shadow")[0]??null:null,D=r?g(r,"vertAlign")[0]??null:null,I=m!==null&&(p(m,"w:val")??p(m,"val")??"1")!=="0",Y=T!==null&&(p(T,"w:val")??p(T,"val")??"1")!=="0",U=(p(w,"w:val")??p(w,"val")??"").toLowerCase(),ct=w!==null&&U!=="none",Q=v!==null&&(p(v,"w:val")??p(v,"val")??"1")!=="0",tt=A!==null&&(p(A,"w:val")??p(A,"val")??"1")!=="0",rt=(p(D,"w:val")??p(D,"val")??"").toLowerCase(),it=rt==="superscript",z=rt==="subscript",B=r?g(r,"rFonts")[0]??null:null,ot=p(B,"w:eastAsia")??p(B,"eastAsia")??p(B,"w:ascii")??p(B,"ascii")??p(B,"w:hAnsi")??p(B,"hAnsi")??null;return{text:l,fontSizePx:u,color:h,highlightColor:S,shadingColor:F,charSpacingPx:R,shadow:tt,bold:I,italic:Y,underline:ct,strike:Q,superscript:it,subscript:z,fontFamily:ot}}).filter(n=>n!==null)}function cn(e){let t=K(e,"docDefaults");if(!t)return{bodyFontPx:null,bodyLineHeightRatio:null,bodyLineHeightPx:null,bodyLineHeightRule:"auto",paragraphAfterPx:null};let n=K(t,"rPrDefault"),r=n?K(n,"sz"):null,o=b(r,"w:val")??b(r,"val")??null,i=o===null?null:o/2*(96/72),l=K(t,"pPrDefault"),a=l?K(l,"spacing"):null,s=b(a,"w:line")??b(a,"line")??null,u=(p(a,"w:lineRule")??p(a,"lineRule")??"auto").toLowerCase(),f=u==="exact"?"exact":u==="atleast"?"atLeast":"auto",d=s===null||f!=="auto"?null:s/240,h=s===null||f==="auto"?null:k(s),y=b(a,"w:after")??b(a,"after")??null,C=y===null?null:k(y);return{bodyFontPx:i,bodyLineHeightRatio:d,bodyLineHeightPx:h,bodyLineHeightRule:f,paragraphAfterPx:C}}function pn(e){let n=g(e,"style").find(f=>{let d=(p(f,"w:styleId")??p(f,"styleId")??"").toLowerCase(),h=K(f,"name"),y=(p(h,"w:val")??p(h,"val")??"").toLowerCase();return d==="1"||y==="heading 1"||y==="\u6807\u9898 1"});if(!n)return{titleFontPx:null,titleColor:null};let r=K(n,"rPr"),o=r?K(r,"sz"):null,i=b(o,"w:val")??b(o,"val")??null,l=i===null?null:i/2*(96/72),a=r?K(r,"color"):null,s=p(a,"w:val")??p(a,"val")??null,u=s?`#${s}`:null;return{titleFontPx:l,titleColor:u}}function mn(e){if(!e)return[];let n=g(e,"font").map(r=>p(r,"w:name")??p(r,"name")??"").map(r=>r.trim()).filter(r=>r.length>0);return[...new Set(n)]}function ht(e,t){return e.some(n=>t.some(r=>n.toLowerCase().includes(r.toLowerCase())))}function dn(e){return ht(e,["times new roman"])?'"Times New Roman", "Noto Serif SC", serif':ht(e,["dengxian","\u7B49\u7EBF","yahei","hei","song"])?'DengXian, "Microsoft YaHei", "PingFang SC", "Noto Sans SC", sans-serif':M.bodyFontFamily}function gn(e){return ht(e,["dengxian","\u7B49\u7EBF"])?'DengXian, "Noto Sans SC", "Microsoft YaHei", sans-serif':ht(e,["times new roman"])?'"Times New Roman", "Noto Serif SC", serif':M.titleFontFamily}async function Kt(e){let t=e.arrayBuffer,n=t?await t.call(e):await new Response(e).arrayBuffer(),r=await Ut.default.loadAsync(n),o=await r.file("word/document.xml")?.async("string"),i=await r.file("word/styles.xml")?.async("string"),l=await r.file("word/fontTable.xml")?.async("string"),a=await r.file("word/numbering.xml")?.async("string");if(!o||!i)throw new Error("DOCX missing document.xml or styles.xml");let s=gt(o),u=gt(i),f=l?gt(l):null,d=a?gt(a):null,h=ln(d),y=cn(u),C=pn(u),H=sn(u),S=nn(s),P=rn(s),$=on(s),F=mn(f),N=dn(F),E=gn(F),R=an(s,h);return{sourceFileName:e.name,bodyFontPx:y.bodyFontPx??M.bodyFontPx,bodyLineHeightRatio:y.bodyLineHeightRatio??M.bodyLineHeightRatio,bodyLineHeightPx:y.bodyLineHeightPx??M.bodyLineHeightPx,bodyLineHeightRule:y.bodyLineHeightRule??M.bodyLineHeightRule,paragraphAfterPx:y.paragraphAfterPx??M.paragraphAfterPx,contentWidthPx:S.contentWidthPx??M.contentWidthPx,pageHeightPx:S.pageHeightPx??M.pageHeightPx,pageMarginTopPx:S.marginTopPx??M.pageMarginTopPx,pageMarginBottomPx:S.marginBottomPx??M.pageMarginBottomPx,titleFontPx:C.titleFontPx??M.titleFontPx,titleColor:C.titleColor??M.titleColor,titleAlign:P??M.titleAlign,bodyFontFamily:N,titleFontFamily:E,discoveredFonts:F,tableCellPaddingTopPx:H.topPx??M.tableCellPaddingTopPx,tableCellPaddingLeftPx:H.leftPx??M.tableCellPaddingLeftPx,tableCellPaddingBottomPx:H.bottomPx??M.tableCellPaddingBottomPx,tableCellPaddingRightPx:H.rightPx??M.tableCellPaddingRightPx,paragraphProfiles:R,trailingDateText:$.trailingDateText,trailingDateAlignedRight:$.trailingDateAlignedRight,trailingDateParagraphIndex:$.trailingDateParagraphIndex,trailingEmptyParagraphCountBeforeDate:$.trailingEmptyParagraphCountBeforeDate}}function V(e,t,n){e.style.setProperty(t,n,"important")}function fn(e){return e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""")}function hn(e){let t=[];e.fontSizePx!==null&&t.push(`font-size:${e.fontSizePx.toFixed(2)}px`),e.color&&t.push(`color:${e.color}`),e.highlightColor&&t.push(`background-color:${e.highlightColor}`),e.shadingColor&&t.push(`background-color:${e.shadingColor}`),e.charSpacingPx!==null&&t.push(`letter-spacing:${e.charSpacingPx.toFixed(2)}px`),e.shadow&&t.push("text-shadow:0.5px 0.5px 0 rgba(0,0,0,0.28)"),e.bold&&t.push("font-weight:700"),e.italic&&t.push("font-style:italic");let n=[];return e.underline&&n.push("underline"),e.strike&&n.push("line-through"),n.length>0&&t.push(`text-decoration:${n.join(" ")}`),e.superscript&&t.push("vertical-align:super"),e.subscript&&t.push("vertical-align:sub"),(e.superscript||e.subscript)&&t.push("font-size:0.83em"),e.fontFamily&&t.push(`font-family:${e.fontFamily}`),t.join(";")}function bn(e){return e.map(t=>{let n=hn(t),o=t.text.split(`
|
|
5
|
+
`).map(i=>fn(i)).join("<br/>");return n?`<span style="${n}">${o}</span>`:o}).join("")}function Zt(e){let t="abcdefghijklmnopqrstuvwxyz";if(e<=0)return"a";let n=e,r="";for(;n>0;)n-=1,r=t[n%26]+r,n=Math.floor(n/26);return r}function Yt(e){if(e<=0)return"I";let t=[[1e3,"M"],[900,"CM"],[500,"D"],[400,"CD"],[100,"C"],[90,"XC"],[50,"L"],[40,"XL"],[10,"X"],[9,"IX"],[5,"V"],[4,"IV"],[1,"I"]],n=e,r="";for(let[o,i]of t)for(;n>=o;)r+=i,n-=o;return r}function yt(e,t){switch((e??"").toLowerCase()){case"decimal":return`${t}.`;case"lowerletter":return`${Zt(t)}.`;case"upperletter":return`${Zt(t).toUpperCase()}.`;case"lowerroman":return`${Yt(t).toLowerCase()}.`;case"upperroman":return`${Yt(t)}.`;default:return"\u2022"}}function xn(e,t,n,r){if(!e||e.trim().length===0)return yt(r,n[t]??1);let i=e.replace(/%(\d+)/g,(l,a)=>{let s=Number.parseInt(a,10);if(!Number.isFinite(s)||s<=0)return"";let u=s-1,f=n[u]??0;return f<=0?"":u===t?yt(r,f).replace(/\.$/,""):String(f)}).trim();return i||yt(r,n[t]??1)}function Qt(e,t){let n=e.getElementById(t);return n||(n=e.createElement("style"),n.id=t,e.head.appendChild(n)),n}function yn(e,t){let n=Qt(e,"__word_style_profile__"),r=t.contentWidthPx.toFixed(2),o=t.pageMarginTopPx.toFixed(2),i=t.pageMarginBottomPx.toFixed(2),l=t.pageHeightPx.toFixed(2),a=t.bodyLineHeightRule==="auto"||t.bodyLineHeightPx===null?t.bodyLineHeightRatio.toFixed(6):`${t.bodyLineHeightPx.toFixed(2)}px`;n.textContent=`
|
|
1695
6
|
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;700&family=Noto+Serif+SC:wght@400;700&display=swap');
|
|
1696
7
|
html, body { box-sizing: border-box; }
|
|
1697
8
|
body {
|
|
1698
|
-
min-height: ${
|
|
1699
|
-
width: ${
|
|
9
|
+
min-height: ${l}px !important;
|
|
10
|
+
width: ${r}px !important;
|
|
1700
11
|
max-width: calc(100% - 24px) !important;
|
|
1701
12
|
margin-left: auto !important;
|
|
1702
13
|
margin-right: auto !important;
|
|
1703
|
-
padding-top: ${
|
|
1704
|
-
padding-bottom: ${
|
|
14
|
+
padding-top: ${o}px !important;
|
|
15
|
+
padding-bottom: ${i}px !important;
|
|
1705
16
|
padding-left: 0 !important;
|
|
1706
17
|
padding-right: 0 !important;
|
|
1707
|
-
font-family: ${
|
|
18
|
+
font-family: ${t.bodyFontFamily} !important;
|
|
1708
19
|
}
|
|
1709
20
|
p {
|
|
1710
|
-
font-size: ${
|
|
1711
|
-
line-height: ${
|
|
1712
|
-
margin-bottom: ${
|
|
21
|
+
font-size: ${t.bodyFontPx.toFixed(4)}px !important;
|
|
22
|
+
line-height: ${a} !important;
|
|
23
|
+
margin-bottom: ${t.paragraphAfterPx.toFixed(2)}px !important;
|
|
1713
24
|
}
|
|
1714
25
|
table { border-collapse: collapse !important; border-spacing: 0 !important; }
|
|
1715
26
|
td, th {
|
|
1716
|
-
padding-top: ${
|
|
1717
|
-
padding-left: ${
|
|
1718
|
-
padding-bottom: ${
|
|
1719
|
-
padding-right: ${
|
|
27
|
+
padding-top: ${t.tableCellPaddingTopPx.toFixed(2)}px !important;
|
|
28
|
+
padding-left: ${t.tableCellPaddingLeftPx.toFixed(2)}px !important;
|
|
29
|
+
padding-bottom: ${t.tableCellPaddingBottomPx.toFixed(2)}px !important;
|
|
30
|
+
padding-right: ${t.tableCellPaddingRightPx.toFixed(2)}px !important;
|
|
1720
31
|
vertical-align: top !important;
|
|
1721
32
|
}
|
|
1722
33
|
h1 {
|
|
1723
|
-
font-size: ${
|
|
1724
|
-
color: ${
|
|
1725
|
-
text-align: ${
|
|
1726
|
-
font-family: ${
|
|
1727
|
-
}
|
|
1728
|
-
`;
|
|
1729
|
-
}
|
|
1730
|
-
function applyInlineLayoutGuards(doc, styleProfile) {
|
|
1731
|
-
const body = doc.body;
|
|
1732
|
-
const targetWidthPx = styleProfile.contentWidthPx.toFixed(2);
|
|
1733
|
-
const topPaddingPx = styleProfile.pageMarginTopPx.toFixed(2);
|
|
1734
|
-
const bottomPaddingPx = styleProfile.pageMarginBottomPx.toFixed(2);
|
|
1735
|
-
const pageHeightPx = styleProfile.pageHeightPx.toFixed(2);
|
|
1736
|
-
setImportantStyle(body, "box-sizing", "border-box");
|
|
1737
|
-
setImportantStyle(body, "min-height", `${pageHeightPx}px`);
|
|
1738
|
-
setImportantStyle(body, "width", `${targetWidthPx}px`);
|
|
1739
|
-
setImportantStyle(body, "max-width", `${targetWidthPx}px`);
|
|
1740
|
-
setImportantStyle(body, "margin-left", "auto");
|
|
1741
|
-
setImportantStyle(body, "margin-right", "auto");
|
|
1742
|
-
setImportantStyle(body, "padding-top", `${topPaddingPx}px`);
|
|
1743
|
-
setImportantStyle(body, "padding-bottom", `${bottomPaddingPx}px`);
|
|
1744
|
-
setImportantStyle(body, "padding-left", "0");
|
|
1745
|
-
setImportantStyle(body, "padding-right", "0");
|
|
1746
|
-
setImportantStyle(body, "font-family", styleProfile.bodyFontFamily);
|
|
1747
|
-
for (const child of Array.from(body.children)) {
|
|
1748
|
-
if (!(child instanceof HTMLElement)) continue;
|
|
1749
|
-
const tag = child.tagName.toLowerCase();
|
|
1750
|
-
if (tag === "script" || tag === "style") continue;
|
|
1751
|
-
setImportantStyle(child, "box-sizing", "border-box");
|
|
1752
|
-
setImportantStyle(child, "max-width", "100%");
|
|
1753
|
-
}
|
|
1754
|
-
for (const img of Array.from(doc.body.querySelectorAll("img"))) {
|
|
1755
|
-
if (!(img instanceof HTMLElement)) continue;
|
|
1756
|
-
setImportantStyle(img, "max-width", "100%");
|
|
1757
|
-
setImportantStyle(img, "height", "auto");
|
|
1758
|
-
}
|
|
1759
|
-
}
|
|
1760
|
-
function normalizeEmptyParagraphMarkers(paragraphs) {
|
|
1761
|
-
for (const p of paragraphs) {
|
|
1762
|
-
const hasVisualContent = (p.textContent ?? "").trim().length > 0 || p.querySelector("img,table,svg,canvas") !== null;
|
|
1763
|
-
if (!hasVisualContent) {
|
|
1764
|
-
p.setAttribute("data-word-empty", "1");
|
|
1765
|
-
if (p.innerHTML.trim().length === 0) {
|
|
1766
|
-
p.innerHTML = "<br/>";
|
|
1767
|
-
}
|
|
1768
|
-
} else {
|
|
1769
|
-
p.removeAttribute("data-word-empty");
|
|
1770
|
-
}
|
|
1771
|
-
}
|
|
1772
|
-
}
|
|
1773
|
-
function hasMeaningfulParagraphAfter(paragraphs, index) {
|
|
1774
|
-
for (let i = index + 1; i < paragraphs.length; i += 1) {
|
|
1775
|
-
const p = paragraphs[i];
|
|
1776
|
-
const hasText = (p.textContent ?? "").trim().length > 0;
|
|
1777
|
-
const hasVisual = p.querySelector("img,table,svg,canvas") !== null;
|
|
1778
|
-
if (hasText || hasVisual) return true;
|
|
1779
|
-
}
|
|
1780
|
-
return false;
|
|
1781
|
-
}
|
|
1782
|
-
function applyParagraphProfiles(doc, styleProfile) {
|
|
1783
|
-
const fallbackParagraphs = Array.from(doc.body.querySelectorAll("p"));
|
|
1784
|
-
fallbackParagraphs.forEach((p) => {
|
|
1785
|
-
p.classList.remove("__word-date-anchor");
|
|
1786
|
-
p.querySelectorAll("span.__word-list-marker").forEach((node) => node.remove());
|
|
1787
|
-
});
|
|
1788
|
-
const resolvedTargets = styleProfile.paragraphProfiles.map((profile, index) => {
|
|
1789
|
-
const byIndex = doc.body.querySelector(`[data-word-p-index="${profile.index}"]`) ?? null;
|
|
1790
|
-
const fallback = fallbackParagraphs[index] ?? null;
|
|
1791
|
-
return {
|
|
1792
|
-
profile,
|
|
1793
|
-
node: byIndex ?? fallback
|
|
1794
|
-
};
|
|
1795
|
-
});
|
|
1796
|
-
if (styleProfile.trailingDateAlignedRight && styleProfile.trailingDateText) {
|
|
1797
|
-
let dateParagraph = null;
|
|
1798
|
-
if (styleProfile.trailingDateParagraphIndex !== null && styleProfile.trailingDateParagraphIndex >= 0) {
|
|
1799
|
-
dateParagraph = doc.body.querySelector(
|
|
1800
|
-
`[data-word-p-index="${styleProfile.trailingDateParagraphIndex}"]`
|
|
1801
|
-
) ?? fallbackParagraphs[styleProfile.trailingDateParagraphIndex] ?? null;
|
|
1802
|
-
} else {
|
|
1803
|
-
dateParagraph = fallbackParagraphs.slice().reverse().find((p) => {
|
|
1804
|
-
const text = (p.textContent ?? "").replace(/\s+/g, "");
|
|
1805
|
-
const target = styleProfile.trailingDateText?.replace(/\s+/g, "") ?? "";
|
|
1806
|
-
return target.length > 0 && text.includes(target);
|
|
1807
|
-
}) ?? null;
|
|
1808
|
-
}
|
|
1809
|
-
const dateIndex = dateParagraph ? fallbackParagraphs.indexOf(dateParagraph) : -1;
|
|
1810
|
-
const hasContentAfterDate = dateIndex >= 0 ? hasMeaningfulParagraphAfter(fallbackParagraphs, dateIndex) : false;
|
|
1811
|
-
if (dateParagraph && !hasContentAfterDate) {
|
|
1812
|
-
let existingEmptyCount = 0;
|
|
1813
|
-
let cursor = dateParagraph.previousElementSibling;
|
|
1814
|
-
while (cursor && cursor.tagName.toLowerCase() === "p" && (cursor.textContent ?? "").trim().length === 0) {
|
|
1815
|
-
existingEmptyCount += 1;
|
|
1816
|
-
cursor = cursor.previousElementSibling;
|
|
1817
|
-
}
|
|
1818
|
-
const needed = Math.max(0, styleProfile.trailingEmptyParagraphCountBeforeDate - existingEmptyCount);
|
|
1819
|
-
for (let i = 0; i < needed; i += 1) {
|
|
1820
|
-
const spacer = doc.createElement("p");
|
|
1821
|
-
spacer.innerHTML = "<br/>";
|
|
1822
|
-
dateParagraph.parentElement?.insertBefore(spacer, dateParagraph);
|
|
1823
|
-
}
|
|
1824
|
-
}
|
|
1825
|
-
}
|
|
1826
|
-
const paragraphs = Array.from(doc.body.querySelectorAll("p"));
|
|
1827
|
-
const listCounters = /* @__PURE__ */ new Map();
|
|
1828
|
-
const orderedTargets = [];
|
|
1829
|
-
for (const target of resolvedTargets) {
|
|
1830
|
-
const para = target.node;
|
|
1831
|
-
const profile = target.profile;
|
|
1832
|
-
if (!para) continue;
|
|
1833
|
-
orderedTargets.push(para);
|
|
1834
|
-
para.removeAttribute("data-word-list");
|
|
1835
|
-
para.style.textAlign = profile.align;
|
|
1836
|
-
if (profile.beforePx !== null) para.style.marginTop = `${profile.beforePx.toFixed(2)}px`;
|
|
1837
|
-
if (profile.afterPx !== null) para.style.marginBottom = `${profile.afterPx.toFixed(2)}px`;
|
|
1838
|
-
if (profile.lineHeightRule === "auto" && profile.lineHeightRatio !== null) {
|
|
1839
|
-
para.style.lineHeight = profile.lineHeightRatio.toFixed(6);
|
|
1840
|
-
} else if ((profile.lineHeightRule === "exact" || profile.lineHeightRule === "atLeast") && profile.lineHeightPx !== null) {
|
|
1841
|
-
para.style.lineHeight = `${profile.lineHeightPx.toFixed(2)}px`;
|
|
34
|
+
font-size: ${t.titleFontPx.toFixed(2)}px !important;
|
|
35
|
+
color: ${t.titleColor} !important;
|
|
36
|
+
text-align: ${t.titleAlign} !important;
|
|
37
|
+
font-family: ${t.titleFontFamily} !important;
|
|
1842
38
|
}
|
|
1843
|
-
|
|
1844
|
-
if (profile.indentRightPx !== null) para.style.marginRight = `${profile.indentRightPx.toFixed(2)}px`;
|
|
1845
|
-
if (profile.firstLinePx !== null) para.style.textIndent = `${profile.firstLinePx.toFixed(2)}px`;
|
|
1846
|
-
if (profile.hangingPx !== null) para.style.textIndent = `${(-profile.hangingPx).toFixed(2)}px`;
|
|
1847
|
-
if (profile.runs.length > 0 && para.querySelector("img,table,svg,canvas") === null) {
|
|
1848
|
-
const currentTextNormalized = (para.textContent ?? "").replace(/\s+/g, "");
|
|
1849
|
-
const runTextNormalized = profile.runs.map((run) => run.text).join("").replace(/\s+/g, "");
|
|
1850
|
-
if (runTextNormalized.length > 0 && currentTextNormalized === runTextNormalized) {
|
|
1851
|
-
para.innerHTML = paragraphToRunHtml(profile.runs);
|
|
1852
|
-
}
|
|
1853
|
-
}
|
|
1854
|
-
if (profile.listNumId !== null && profile.listLevel !== null) {
|
|
1855
|
-
para.setAttribute("data-word-list", "1");
|
|
1856
|
-
if (profile.sectionBreakBefore) {
|
|
1857
|
-
listCounters.set(profile.listNumId, []);
|
|
1858
|
-
}
|
|
1859
|
-
const currentLevel = Math.max(0, profile.listLevel);
|
|
1860
|
-
const levels = listCounters.get(profile.listNumId) ?? [];
|
|
1861
|
-
const prevValue = levels[currentLevel] ?? profile.listStartAt - 1;
|
|
1862
|
-
const nextValue = prevValue + 1;
|
|
1863
|
-
levels[currentLevel] = nextValue;
|
|
1864
|
-
for (let lv = currentLevel + 1; lv < levels.length; lv += 1) {
|
|
1865
|
-
levels[lv] = 0;
|
|
1866
|
-
}
|
|
1867
|
-
listCounters.set(profile.listNumId, levels);
|
|
1868
|
-
const markerText = formatListMarkerByPattern(profile.listTextPattern, currentLevel, levels, profile.listFormat);
|
|
1869
|
-
const plainText = (para.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
1870
|
-
const alreadyHasMarker = plainText.startsWith(markerText);
|
|
1871
|
-
if (!alreadyHasMarker) {
|
|
1872
|
-
const marker = doc.createElement("span");
|
|
1873
|
-
marker.className = "__word-list-marker";
|
|
1874
|
-
marker.setAttribute("data-word-list-marker", "1");
|
|
1875
|
-
marker.textContent = `${markerText} `;
|
|
1876
|
-
marker.style.display = "inline-block";
|
|
1877
|
-
marker.style.minWidth = "1.8em";
|
|
1878
|
-
marker.style.marginLeft = currentLevel > 0 ? `${currentLevel * 1.2}em` : "0";
|
|
1879
|
-
marker.style.color = "inherit";
|
|
1880
|
-
marker.style.fontWeight = "inherit";
|
|
1881
|
-
para.prepend(marker);
|
|
1882
|
-
}
|
|
1883
|
-
}
|
|
1884
|
-
}
|
|
1885
|
-
normalizeEmptyParagraphMarkers(paragraphs);
|
|
1886
|
-
return orderedTargets;
|
|
1887
|
-
}
|
|
1888
|
-
function paragraphHeightPx(paragraph) {
|
|
1889
|
-
const rect = paragraph.getBoundingClientRect();
|
|
1890
|
-
if (rect.height > 0) return rect.height;
|
|
1891
|
-
const lh = Number.parseFloat(getComputedStyle(paragraph).lineHeight || "0");
|
|
1892
|
-
if (Number.isFinite(lh) && lh > 0) return lh;
|
|
1893
|
-
return 16;
|
|
1894
|
-
}
|
|
1895
|
-
function insertPageSpacerBefore(doc, paragraph, heightPx) {
|
|
1896
|
-
if (heightPx <= 0.5) return;
|
|
1897
|
-
const spacer = doc.createElement("div");
|
|
1898
|
-
spacer.dataset.wordPageSpacer = "1";
|
|
1899
|
-
spacer.style.height = `${heightPx.toFixed(2)}px`;
|
|
1900
|
-
spacer.style.width = "100%";
|
|
1901
|
-
spacer.style.pointerEvents = "none";
|
|
1902
|
-
spacer.style.userSelect = "none";
|
|
1903
|
-
paragraph.parentElement?.insertBefore(spacer, paragraph);
|
|
1904
|
-
}
|
|
1905
|
-
function removePaginationSpacers(doc) {
|
|
1906
|
-
doc.querySelectorAll("[data-word-page-spacer='1']").forEach((node) => node.remove());
|
|
1907
|
-
}
|
|
1908
|
-
function estimateGroupHeight(paragraphs, idx, profile, contentHeight) {
|
|
1909
|
-
const currentH = paragraphHeightPx(paragraphs[idx]);
|
|
1910
|
-
if (!profile.keepNext) return currentH;
|
|
1911
|
-
const next = paragraphs[idx + 1];
|
|
1912
|
-
if (!next) return currentH;
|
|
1913
|
-
const nextH = paragraphHeightPx(next);
|
|
1914
|
-
const sum = currentH + nextH;
|
|
1915
|
-
if (sum > contentHeight) return currentH;
|
|
1916
|
-
return sum;
|
|
1917
|
-
}
|
|
1918
|
-
function applyKeepPagination(doc, styleProfile, paragraphs) {
|
|
1919
|
-
removePaginationSpacers(doc);
|
|
1920
|
-
const contentHeight = Math.max(120, styleProfile.pageHeightPx - styleProfile.pageMarginTopPx - styleProfile.pageMarginBottomPx);
|
|
1921
|
-
const count = Math.min(styleProfile.paragraphProfiles.length, paragraphs.length);
|
|
1922
|
-
let used = 0;
|
|
1923
|
-
for (let i = 0; i < count; i += 1) {
|
|
1924
|
-
const p = paragraphs[i];
|
|
1925
|
-
const profile = styleProfile.paragraphProfiles[i];
|
|
1926
|
-
const h = paragraphHeightPx(p);
|
|
1927
|
-
const forceBreak = profile.pageBreakBefore;
|
|
1928
|
-
if (forceBreak && used > 0) {
|
|
1929
|
-
insertPageSpacerBefore(doc, p, contentHeight - used);
|
|
1930
|
-
used = 0;
|
|
1931
|
-
}
|
|
1932
|
-
const groupHeight = estimateGroupHeight(paragraphs, i, profile, contentHeight);
|
|
1933
|
-
if ((profile.keepLines || profile.keepNext) && used > 0 && used + groupHeight > contentHeight) {
|
|
1934
|
-
insertPageSpacerBefore(doc, p, contentHeight - used);
|
|
1935
|
-
used = 0;
|
|
1936
|
-
}
|
|
1937
|
-
if (used > 0 && used + h > contentHeight) {
|
|
1938
|
-
insertPageSpacerBefore(doc, p, contentHeight - used);
|
|
1939
|
-
used = 0;
|
|
1940
|
-
}
|
|
1941
|
-
used += h;
|
|
1942
|
-
if (used >= contentHeight) {
|
|
1943
|
-
used = used % contentHeight;
|
|
1944
|
-
}
|
|
1945
|
-
}
|
|
1946
|
-
}
|
|
1947
|
-
function applyFormattingMarks(doc, showFormattingMarks) {
|
|
1948
|
-
const styleEl = ensureStyleTag(doc, "__word_view_options__");
|
|
1949
|
-
styleEl.textContent = `
|
|
39
|
+
`}function wn(e,t){let n=e.body,r=t.contentWidthPx.toFixed(2),o=t.pageMarginTopPx.toFixed(2),i=t.pageMarginBottomPx.toFixed(2),l=t.pageHeightPx.toFixed(2);V(n,"box-sizing","border-box"),V(n,"min-height",`${l}px`),V(n,"width",`${r}px`),V(n,"max-width",`${r}px`),V(n,"margin-left","auto"),V(n,"margin-right","auto"),V(n,"padding-top",`${o}px`),V(n,"padding-bottom",`${i}px`),V(n,"padding-left","0"),V(n,"padding-right","0"),V(n,"font-family",t.bodyFontFamily);for(let a of Array.from(n.children)){if(!(a instanceof HTMLElement))continue;let s=a.tagName.toLowerCase();s==="script"||s==="style"||(V(a,"box-sizing","border-box"),V(a,"max-width","100%"))}for(let a of Array.from(e.body.querySelectorAll("img")))a instanceof HTMLElement&&(V(a,"max-width","100%"),V(a,"height","auto"))}function te(e){for(let t of e)(t.textContent??"").trim().length>0||t.querySelector("img,table,svg,canvas")!==null?t.removeAttribute("data-word-empty"):(t.setAttribute("data-word-empty","1"),t.innerHTML.trim().length===0&&(t.innerHTML="<br/>"))}function Pn(e,t){for(let n=t+1;n<e.length;n+=1){let r=e[n],o=(r.textContent??"").trim().length>0,i=r.querySelector("img,table,svg,canvas")!==null;if(o||i)return!0}return!1}function Cn(e,t){let n=Array.from(e.body.querySelectorAll("p"));n.forEach(a=>{a.classList.remove("__word-date-anchor"),a.querySelectorAll("span.__word-list-marker").forEach(s=>s.remove())});let r=t.paragraphProfiles.map((a,s)=>{let u=e.body.querySelector(`[data-word-p-index="${a.index}"]`)??null,f=n[s]??null;return{profile:a,node:u??f}});if(t.trailingDateAlignedRight&&t.trailingDateText){let a=null;t.trailingDateParagraphIndex!==null&&t.trailingDateParagraphIndex>=0?a=e.body.querySelector(`[data-word-p-index="${t.trailingDateParagraphIndex}"]`)??n[t.trailingDateParagraphIndex]??null:a=n.slice().reverse().find(f=>{let d=(f.textContent??"").replace(/\s+/g,""),h=t.trailingDateText?.replace(/\s+/g,"")??"";return h.length>0&&d.includes(h)})??null;let s=a?n.indexOf(a):-1,u=s>=0?Pn(n,s):!1;if(a&&!u){let f=0,d=a.previousElementSibling;for(;d&&d.tagName.toLowerCase()==="p"&&(d.textContent??"").trim().length===0;)f+=1,d=d.previousElementSibling;let h=Math.max(0,t.trailingEmptyParagraphCountBeforeDate-f);for(let y=0;y<h;y+=1){let C=e.createElement("p");C.innerHTML="<br/>",a.parentElement?.insertBefore(C,a)}}}let o=Array.from(e.body.querySelectorAll("p")),i=new Map,l=[];for(let a of r){let s=a.node,u=a.profile;if(s){if(l.push(s),s.removeAttribute("data-word-list"),s.style.textAlign=u.align,u.beforePx!==null&&(s.style.marginTop=`${u.beforePx.toFixed(2)}px`),u.afterPx!==null&&(s.style.marginBottom=`${u.afterPx.toFixed(2)}px`),u.lineHeightRule==="auto"&&u.lineHeightRatio!==null?s.style.lineHeight=u.lineHeightRatio.toFixed(6):(u.lineHeightRule==="exact"||u.lineHeightRule==="atLeast")&&u.lineHeightPx!==null&&(s.style.lineHeight=`${u.lineHeightPx.toFixed(2)}px`),u.indentLeftPx!==null&&(s.style.marginLeft=`${u.indentLeftPx.toFixed(2)}px`),u.indentRightPx!==null&&(s.style.marginRight=`${u.indentRightPx.toFixed(2)}px`),u.firstLinePx!==null&&(s.style.textIndent=`${u.firstLinePx.toFixed(2)}px`),u.hangingPx!==null&&(s.style.textIndent=`${(-u.hangingPx).toFixed(2)}px`),u.runs.length>0&&s.querySelector("img,table,svg,canvas")===null){let f=(s.textContent??"").replace(/\s+/g,""),d=u.runs.map(h=>h.text).join("").replace(/\s+/g,"");d.length>0&&f===d&&(s.innerHTML=bn(u.runs))}if(u.listNumId!==null&&u.listLevel!==null){s.setAttribute("data-word-list","1"),u.sectionBreakBefore&&i.set(u.listNumId,[]);let f=Math.max(0,u.listLevel),d=i.get(u.listNumId)??[],y=(d[f]??u.listStartAt-1)+1;d[f]=y;for(let P=f+1;P<d.length;P+=1)d[P]=0;i.set(u.listNumId,d);let C=xn(u.listTextPattern,f,d,u.listFormat);if(!(s.textContent??"").replace(/\s+/g," ").trim().startsWith(C)){let P=e.createElement("span");P.className="__word-list-marker",P.setAttribute("data-word-list-marker","1"),P.textContent=`${C} `,P.style.display="inline-block",P.style.minWidth="1.8em",P.style.marginLeft=f>0?`${f*1.2}em`:"0",P.style.color="inherit",P.style.fontWeight="inherit",s.prepend(P)}}}}return te(o),l}function Pt(e){let t=e.getBoundingClientRect();if(t.height>0)return t.height;let n=Number.parseFloat(getComputedStyle(e).lineHeight||"0");return Number.isFinite(n)&&n>0?n:16}function wt(e,t,n){if(n<=.5)return;let r=e.createElement("div");r.dataset.wordPageSpacer="1",r.style.height=`${n.toFixed(2)}px`,r.style.width="100%",r.style.pointerEvents="none",r.style.userSelect="none",t.parentElement?.insertBefore(r,t)}function vn(e){e.querySelectorAll("[data-word-page-spacer='1']").forEach(t=>t.remove())}function Sn(e,t,n,r){let o=Pt(e[t]);if(!n.keepNext)return o;let i=e[t+1];if(!i)return o;let l=Pt(i),a=o+l;return a>r?o:a}function Fn(e,t,n){vn(e);let r=Math.max(120,t.pageHeightPx-t.pageMarginTopPx-t.pageMarginBottomPx),o=Math.min(t.paragraphProfiles.length,n.length),i=0;for(let l=0;l<o;l+=1){let a=n[l],s=t.paragraphProfiles[l],u=Pt(a);s.pageBreakBefore&&i>0&&(wt(e,a,r-i),i=0);let d=Sn(n,l,s,r);(s.keepLines||s.keepNext)&&i>0&&i+d>r&&(wt(e,a,r-i),i=0),i>0&&i+u>r&&(wt(e,a,r-i),i=0),i+=u,i>=r&&(i=i%r)}}function $n(e,t){let n=Qt(e,"__word_view_options__");n.textContent=`
|
|
1950
40
|
p[data-word-empty="1"]::before { content: "\\00a0"; }
|
|
1951
|
-
${
|
|
41
|
+
${t?`
|
|
1952
42
|
p::after {
|
|
1953
43
|
content: "\u21B5";
|
|
1954
44
|
color: #66aef9;
|
|
@@ -1959,341 +49,12 @@ function applyFormattingMarks(doc, showFormattingMarks) {
|
|
|
1959
49
|
content: "\u21B5";
|
|
1960
50
|
color: #66aef9;
|
|
1961
51
|
}
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
}
|
|
1965
|
-
function applyWordRenderModel({ doc, styleProfile, showFormattingMarks }) {
|
|
1966
|
-
const effectiveProfile = styleProfile ?? createFallbackWordStyleProfile("__default_a4__");
|
|
1967
|
-
applyWordHtmlCompatibility(doc, {
|
|
1968
|
-
forceBodyFontFamily: effectiveProfile.bodyFontFamily,
|
|
1969
|
-
forceHeadingFontFamily: effectiveProfile.titleFontFamily
|
|
1970
|
-
});
|
|
1971
|
-
let paragraphs = Array.from(doc.body.querySelectorAll("p"));
|
|
1972
|
-
normalizeEmptyParagraphMarkers(paragraphs);
|
|
1973
|
-
applyBaseProfileCss(doc, effectiveProfile);
|
|
1974
|
-
applyInlineLayoutGuards(doc, effectiveProfile);
|
|
1975
|
-
if (styleProfile) {
|
|
1976
|
-
paragraphs = applyParagraphProfiles(doc, styleProfile);
|
|
1977
|
-
applyKeepPagination(doc, styleProfile, paragraphs);
|
|
1978
|
-
}
|
|
1979
|
-
applyFormattingMarks(doc, showFormattingMarks);
|
|
1980
|
-
}
|
|
1981
|
-
|
|
1982
|
-
// src/core/DocsWordElement.ts
|
|
1983
|
-
var VERSION = "0.1.2";
|
|
1984
|
-
var MESSAGES = {
|
|
1985
|
-
zh: {
|
|
1986
|
-
readClipboard: "\u4ECE\u7CFB\u7EDF\u526A\u8D34\u677F\u8BFB\u53D6",
|
|
1987
|
-
uploadWord: "\u4E0A\u4F20 Word",
|
|
1988
|
-
clear: "\u6E05\u7A7A",
|
|
1989
|
-
pastePlaceholder: "\u5728\u6B64\u5904\u7C98\u8D34 Word/WPS/Google Docs \u5185\u5BB9\uFF08Ctrl/Cmd+V\uFF09",
|
|
1990
|
-
waitImport: "\u7B49\u5F85\u5185\u5BB9\u5BFC\u5165",
|
|
1991
|
-
loadedHtml: "\u5DF2\u52A0\u8F7D HTML \u5FEB\u7167",
|
|
1992
|
-
cleared: "\u6587\u6863\u5DF2\u6E05\u7A7A",
|
|
1993
|
-
loadedWord: (name) => `\u5DF2\u52A0\u8F7D Word \u6587\u4EF6: ${name}`,
|
|
1994
|
-
importedClipboard: "\u5DF2\u5BFC\u5165\u526A\u8D34\u677F\u5185\u5BB9",
|
|
1995
|
-
noContent: "\u672A\u68C0\u6D4B\u5230\u53EF\u5BFC\u5165\u5185\u5BB9",
|
|
1996
|
-
noClipboardRead: "\u5F53\u524D\u6D4F\u89C8\u5668\u4E0D\u652F\u6301 clipboard.read",
|
|
1997
|
-
parseFailed: "Word \u89E3\u6790\u5931\u8D25",
|
|
1998
|
-
clipboardReadFailed: "\u8BFB\u53D6\u526A\u8D34\u677F\u5931\u8D25",
|
|
1999
|
-
errorPrefix: "\u9519\u8BEF: "
|
|
2000
|
-
},
|
|
2001
|
-
en: {
|
|
2002
|
-
readClipboard: "Read clipboard",
|
|
2003
|
-
uploadWord: "Upload Word",
|
|
2004
|
-
clear: "Clear",
|
|
2005
|
-
pastePlaceholder: "Paste Word/WPS/Google Docs content here (Ctrl/Cmd+V)",
|
|
2006
|
-
waitImport: "Waiting for input",
|
|
2007
|
-
loadedHtml: "HTML snapshot loaded",
|
|
2008
|
-
cleared: "Document cleared",
|
|
2009
|
-
loadedWord: (name) => `Word file loaded: ${name}`,
|
|
2010
|
-
importedClipboard: "Clipboard content imported",
|
|
2011
|
-
noContent: "No importable content detected",
|
|
2012
|
-
noClipboardRead: "navigator.clipboard.read is not supported in this browser",
|
|
2013
|
-
parseFailed: "Word parse failed",
|
|
2014
|
-
clipboardReadFailed: "Failed to read clipboard",
|
|
2015
|
-
errorPrefix: "Error: "
|
|
2016
|
-
}
|
|
2017
|
-
};
|
|
2018
|
-
var BASE_CSS = `
|
|
52
|
+
`:""}
|
|
53
|
+
`}function ee({doc:e,styleProfile:t,showFormattingMarks:n}){let r=t??Gt("__default_a4__");Xt(e,{forceBodyFontFamily:r.bodyFontFamily,forceHeadingFontFamily:r.titleFontFamily});let o=Array.from(e.body.querySelectorAll("p"));te(o),yn(e,r),wn(e,r),t&&(o=Cn(e,t),Fn(e,t,o)),$n(e,n)}var Nn="0.1.5",_={zh:{readClipboard:"\u4ECE\u7CFB\u7EDF\u526A\u8D34\u677F\u8BFB\u53D6",uploadWord:"\u4E0A\u4F20 Word",clear:"\u6E05\u7A7A",pastePlaceholder:"\u5728\u6B64\u5904\u7C98\u8D34 Word/WPS/Google Docs \u5185\u5BB9\uFF08Ctrl/Cmd+V\uFF09",waitImport:"\u7B49\u5F85\u5185\u5BB9\u5BFC\u5165",loadedHtml:"\u5DF2\u52A0\u8F7D HTML \u5FEB\u7167",cleared:"\u6587\u6863\u5DF2\u6E05\u7A7A",loadedWord:e=>`\u5DF2\u52A0\u8F7D Word \u6587\u4EF6: ${e}`,importedClipboard:"\u5DF2\u5BFC\u5165\u526A\u8D34\u677F\u5185\u5BB9",noContent:"\u672A\u68C0\u6D4B\u5230\u53EF\u5BFC\u5165\u5185\u5BB9",noClipboardRead:"\u5F53\u524D\u6D4F\u89C8\u5668\u4E0D\u652F\u6301 clipboard.read",parseFailed:"Word \u89E3\u6790\u5931\u8D25",clipboardReadFailed:"\u8BFB\u53D6\u526A\u8D34\u677F\u5931\u8D25",errorPrefix:"\u9519\u8BEF: "},en:{readClipboard:"Read clipboard",uploadWord:"Upload Word",clear:"Clear",pastePlaceholder:"Paste Word/WPS/Google Docs content here (Ctrl/Cmd+V)",waitImport:"Waiting for input",loadedHtml:"HTML snapshot loaded",cleared:"Document cleared",loadedWord:e=>`Word file loaded: ${e}`,importedClipboard:"Clipboard content imported",noContent:"No importable content detected",noClipboardRead:"navigator.clipboard.read is not supported in this browser",parseFailed:"Word parse failed",clipboardReadFailed:"Failed to read clipboard",errorPrefix:"Error: "}},Ln=`
|
|
2019
54
|
:host{display:block;border:1px solid #d8deea;border-radius:12px;background:#fff;overflow:hidden;font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto}
|
|
2020
55
|
.toolbar{display:flex;gap:8px;flex-wrap:wrap;padding:10px;border-bottom:1px solid #e8edf6;background:#f8faff}
|
|
2021
56
|
button{border:1px solid #c8d2eb;background:#fff;border-radius:9px;padding:6px 10px;cursor:pointer}
|
|
2022
57
|
.paste{width:100%;min-height:40px;border:1px dashed #95acef;border-radius:10px;background:#edf3ff;padding:8px 10px;box-sizing:border-box;resize:vertical}
|
|
2023
58
|
.hint{display:block;padding:0 10px 10px;color:#4b5c82;font-size:12px}
|
|
2024
59
|
iframe{width:100%;min-height:760px;border:0}
|
|
2025
|
-
|
|
2026
|
-
var DocsWordElement = class extends HTMLElement {
|
|
2027
|
-
rootRef;
|
|
2028
|
-
toolbar;
|
|
2029
|
-
btnRead;
|
|
2030
|
-
btnUpload;
|
|
2031
|
-
btnClear;
|
|
2032
|
-
frame;
|
|
2033
|
-
pasteArea;
|
|
2034
|
-
fileInput;
|
|
2035
|
-
hint;
|
|
2036
|
-
htmlSnapshot;
|
|
2037
|
-
styleProfile = null;
|
|
2038
|
-
frameHeight = 0;
|
|
2039
|
-
locale = "zh";
|
|
2040
|
-
constructor() {
|
|
2041
|
-
super();
|
|
2042
|
-
this.rootRef = this.attachShadow({ mode: "open" });
|
|
2043
|
-
this.locale = this.parseLocale(this.getAttribute("lang"));
|
|
2044
|
-
this.htmlSnapshot = buildHtmlSnapshot("<p><br/></p>");
|
|
2045
|
-
const style = document.createElement("style");
|
|
2046
|
-
style.textContent = BASE_CSS;
|
|
2047
|
-
this.toolbar = document.createElement("div");
|
|
2048
|
-
this.toolbar.className = "toolbar";
|
|
2049
|
-
this.btnRead = document.createElement("button");
|
|
2050
|
-
this.btnRead.onclick = () => void this.loadClipboard();
|
|
2051
|
-
this.btnUpload = document.createElement("button");
|
|
2052
|
-
this.btnUpload.onclick = () => this.fileInput.click();
|
|
2053
|
-
this.btnClear = document.createElement("button");
|
|
2054
|
-
this.btnClear.onclick = () => this.clear();
|
|
2055
|
-
this.fileInput = document.createElement("input");
|
|
2056
|
-
this.fileInput.type = "file";
|
|
2057
|
-
this.fileInput.accept = ".docx";
|
|
2058
|
-
this.fileInput.style.display = "none";
|
|
2059
|
-
this.fileInput.onchange = () => void this.onUpload();
|
|
2060
|
-
this.toolbar.append(this.btnRead, this.btnUpload, this.btnClear, this.fileInput);
|
|
2061
|
-
this.pasteArea = document.createElement("textarea");
|
|
2062
|
-
this.pasteArea.className = "paste";
|
|
2063
|
-
this.pasteArea.placeholder = "";
|
|
2064
|
-
this.pasteArea.onpaste = (event) => {
|
|
2065
|
-
event.preventDefault();
|
|
2066
|
-
void this.applyFromClipboardData(event.clipboardData);
|
|
2067
|
-
};
|
|
2068
|
-
this.hint = document.createElement("span");
|
|
2069
|
-
this.hint.className = "hint";
|
|
2070
|
-
this.hint.textContent = "";
|
|
2071
|
-
this.frame = document.createElement("iframe");
|
|
2072
|
-
this.frame.sandbox.add("allow-same-origin", "allow-scripts");
|
|
2073
|
-
this.frame.onload = () => this.onFrameLoad();
|
|
2074
|
-
this.rootRef.append(style, this.toolbar, this.pasteArea, this.hint, this.frame);
|
|
2075
|
-
this.syncLocaleText();
|
|
2076
|
-
this.syncToolbarVisibility();
|
|
2077
|
-
}
|
|
2078
|
-
static get observedAttributes() {
|
|
2079
|
-
return ["lang", "show-toolbar"];
|
|
2080
|
-
}
|
|
2081
|
-
attributeChangedCallback(name, _, newValue) {
|
|
2082
|
-
if (name === "lang") {
|
|
2083
|
-
this.locale = this.parseLocale(newValue);
|
|
2084
|
-
this.syncLocaleText();
|
|
2085
|
-
return;
|
|
2086
|
-
}
|
|
2087
|
-
if (name === "show-toolbar") {
|
|
2088
|
-
this.syncToolbarVisibility();
|
|
2089
|
-
}
|
|
2090
|
-
}
|
|
2091
|
-
connectedCallback() {
|
|
2092
|
-
this.renderSnapshot();
|
|
2093
|
-
this.dispatchEvent(new CustomEvent("docsjs-ready", { detail: { version: VERSION } }));
|
|
2094
|
-
}
|
|
2095
|
-
setSnapshot(rawHtml) {
|
|
2096
|
-
this.loadHtml(rawHtml);
|
|
2097
|
-
}
|
|
2098
|
-
loadHtml(rawHtml) {
|
|
2099
|
-
this.styleProfile = null;
|
|
2100
|
-
this.htmlSnapshot = buildHtmlSnapshot(rawHtml);
|
|
2101
|
-
this.renderSnapshot();
|
|
2102
|
-
this.setHint(MESSAGES[this.locale].loadedHtml);
|
|
2103
|
-
this.emitChange("api");
|
|
2104
|
-
}
|
|
2105
|
-
getSnapshot() {
|
|
2106
|
-
return this.htmlSnapshot;
|
|
2107
|
-
}
|
|
2108
|
-
clear() {
|
|
2109
|
-
this.styleProfile = null;
|
|
2110
|
-
this.htmlSnapshot = buildHtmlSnapshot("<p><br/></p>");
|
|
2111
|
-
this.renderSnapshot();
|
|
2112
|
-
this.setHint(MESSAGES[this.locale].cleared);
|
|
2113
|
-
this.emitChange("clear");
|
|
2114
|
-
}
|
|
2115
|
-
async loadDocx(file) {
|
|
2116
|
-
await this.applyDocx(file);
|
|
2117
|
-
}
|
|
2118
|
-
async onUpload() {
|
|
2119
|
-
const file = this.fileInput.files?.[0];
|
|
2120
|
-
if (!file) return;
|
|
2121
|
-
await this.applyDocx(file);
|
|
2122
|
-
this.fileInput.value = "";
|
|
2123
|
-
}
|
|
2124
|
-
async applyDocx(file) {
|
|
2125
|
-
try {
|
|
2126
|
-
const [snapshot, profile] = await Promise.all([
|
|
2127
|
-
parseDocxToHtmlSnapshot(file),
|
|
2128
|
-
parseDocxStyleProfile(file)
|
|
2129
|
-
]);
|
|
2130
|
-
this.styleProfile = profile;
|
|
2131
|
-
this.htmlSnapshot = snapshot;
|
|
2132
|
-
this.renderSnapshot();
|
|
2133
|
-
this.setHint(MESSAGES[this.locale].loadedWord(profile.sourceFileName));
|
|
2134
|
-
this.emitChange("upload", profile.sourceFileName);
|
|
2135
|
-
} catch (error) {
|
|
2136
|
-
this.emitError(error instanceof Error ? error.message : MESSAGES[this.locale].parseFailed);
|
|
2137
|
-
}
|
|
2138
|
-
}
|
|
2139
|
-
async loadClipboard() {
|
|
2140
|
-
if (!navigator.clipboard?.read) {
|
|
2141
|
-
this.emitError(MESSAGES[this.locale].noClipboardRead);
|
|
2142
|
-
return;
|
|
2143
|
-
}
|
|
2144
|
-
try {
|
|
2145
|
-
const items = await navigator.clipboard.read();
|
|
2146
|
-
const payload = await extractFromClipboardItems(items);
|
|
2147
|
-
this.applyPayload(payload.html, payload.text);
|
|
2148
|
-
} catch (error) {
|
|
2149
|
-
this.emitError(error instanceof Error ? error.message : MESSAGES[this.locale].clipboardReadFailed);
|
|
2150
|
-
}
|
|
2151
|
-
}
|
|
2152
|
-
async applyFromClipboardData(data) {
|
|
2153
|
-
if (!data) return;
|
|
2154
|
-
const payload = await extractFromClipboardDataTransfer(data);
|
|
2155
|
-
this.applyPayload(payload.html, payload.text);
|
|
2156
|
-
}
|
|
2157
|
-
applyPayload(html, text) {
|
|
2158
|
-
this.styleProfile = null;
|
|
2159
|
-
if (html.trim()) {
|
|
2160
|
-
this.htmlSnapshot = buildHtmlSnapshot(html);
|
|
2161
|
-
} else if (text.trim()) {
|
|
2162
|
-
this.htmlSnapshot = buildHtmlSnapshot(`<p>${text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">")}</p>`);
|
|
2163
|
-
} else {
|
|
2164
|
-
this.setHint(MESSAGES[this.locale].noContent);
|
|
2165
|
-
return;
|
|
2166
|
-
}
|
|
2167
|
-
this.renderSnapshot();
|
|
2168
|
-
this.setHint(MESSAGES[this.locale].importedClipboard);
|
|
2169
|
-
this.emitChange("paste");
|
|
2170
|
-
}
|
|
2171
|
-
onFrameLoad() {
|
|
2172
|
-
const doc = this.frame.contentDocument;
|
|
2173
|
-
if (!doc) return;
|
|
2174
|
-
applyWordRenderModel({
|
|
2175
|
-
doc,
|
|
2176
|
-
styleProfile: this.styleProfile,
|
|
2177
|
-
showFormattingMarks: false
|
|
2178
|
-
});
|
|
2179
|
-
this.syncHeight();
|
|
2180
|
-
window.setTimeout(() => this.syncHeight(), 120);
|
|
2181
|
-
}
|
|
2182
|
-
syncHeight() {
|
|
2183
|
-
const doc = this.frame.contentDocument;
|
|
2184
|
-
if (!doc) return;
|
|
2185
|
-
const measured = Math.max(760, doc.body.scrollHeight, doc.documentElement.scrollHeight);
|
|
2186
|
-
const next = measured + 24;
|
|
2187
|
-
if (Math.abs(next - this.frameHeight) < 2) return;
|
|
2188
|
-
this.frameHeight = next;
|
|
2189
|
-
this.frame.style.height = `${next}px`;
|
|
2190
|
-
}
|
|
2191
|
-
renderSnapshot() {
|
|
2192
|
-
this.frame.srcdoc = this.htmlSnapshot;
|
|
2193
|
-
}
|
|
2194
|
-
emitChange(source, fileName) {
|
|
2195
|
-
this.dispatchEvent(new CustomEvent("docsjs-change", { detail: { htmlSnapshot: this.htmlSnapshot, source, fileName } }));
|
|
2196
|
-
}
|
|
2197
|
-
emitError(message) {
|
|
2198
|
-
this.dispatchEvent(new CustomEvent("docsjs-error", { detail: { message } }));
|
|
2199
|
-
this.setHint(`${MESSAGES[this.locale].errorPrefix}${message}`);
|
|
2200
|
-
}
|
|
2201
|
-
setHint(text) {
|
|
2202
|
-
this.hint.textContent = text;
|
|
2203
|
-
}
|
|
2204
|
-
parseLocale(value) {
|
|
2205
|
-
return value?.toLowerCase() === "en" ? "en" : "zh";
|
|
2206
|
-
}
|
|
2207
|
-
syncToolbarVisibility() {
|
|
2208
|
-
const raw = this.getAttribute("show-toolbar");
|
|
2209
|
-
const show = raw === null || raw === "" || raw === "1" || raw.toLowerCase() === "true";
|
|
2210
|
-
this.toolbar.style.display = show ? "flex" : "none";
|
|
2211
|
-
}
|
|
2212
|
-
syncLocaleText() {
|
|
2213
|
-
const t = MESSAGES[this.locale];
|
|
2214
|
-
this.btnRead.textContent = t.readClipboard;
|
|
2215
|
-
this.btnUpload.textContent = t.uploadWord;
|
|
2216
|
-
this.btnClear.textContent = t.clear;
|
|
2217
|
-
this.pasteArea.placeholder = t.pastePlaceholder;
|
|
2218
|
-
if (!this.hint.textContent || this.hint.textContent === MESSAGES.en.waitImport || this.hint.textContent === MESSAGES.zh.waitImport) {
|
|
2219
|
-
this.hint.textContent = t.waitImport;
|
|
2220
|
-
}
|
|
2221
|
-
}
|
|
2222
|
-
};
|
|
2223
|
-
function defineDocsWordElement() {
|
|
2224
|
-
if (!customElements.get("docs-word-editor")) {
|
|
2225
|
-
customElements.define("docs-word-editor", DocsWordElement);
|
|
2226
|
-
}
|
|
2227
|
-
}
|
|
2228
|
-
|
|
2229
|
-
// src/lib/semanticStats.ts
|
|
2230
|
-
function countElements(root, selector) {
|
|
2231
|
-
return root.querySelectorAll(selector).length;
|
|
2232
|
-
}
|
|
2233
|
-
function isListLikeParagraph(p) {
|
|
2234
|
-
if (p.hasAttribute("data-word-list")) return true;
|
|
2235
|
-
if (p.querySelector("span.__word-list-marker")) return true;
|
|
2236
|
-
const style = (p.getAttribute("style") ?? "").toLowerCase();
|
|
2237
|
-
return style.includes("mso-list");
|
|
2238
|
-
}
|
|
2239
|
-
function collectSemanticStatsFromDocument(doc) {
|
|
2240
|
-
const paragraphs = Array.from(doc.querySelectorAll("p"));
|
|
2241
|
-
const listParagraphCount = paragraphs.filter((p) => isListLikeParagraph(p)).length;
|
|
2242
|
-
const textCharCount = (doc.body.textContent ?? "").replace(/\s+/g, "").length;
|
|
2243
|
-
return {
|
|
2244
|
-
paragraphCount: paragraphs.length,
|
|
2245
|
-
headingCount: countElements(doc, "h1,h2,h3,h4,h5,h6"),
|
|
2246
|
-
tableCount: countElements(doc, "table"),
|
|
2247
|
-
tableCellCount: countElements(doc, "td,th"),
|
|
2248
|
-
imageCount: countElements(doc, "img"),
|
|
2249
|
-
anchorImageCount: countElements(doc, 'img[data-word-anchor="1"]'),
|
|
2250
|
-
wrappedImageCount: countElements(doc, "img[data-word-wrap]"),
|
|
2251
|
-
ommlCount: countElements(doc, "[data-word-omml]"),
|
|
2252
|
-
chartCount: countElements(doc, "[data-word-chart]"),
|
|
2253
|
-
smartArtCount: countElements(doc, "[data-word-smartart]"),
|
|
2254
|
-
listParagraphCount,
|
|
2255
|
-
commentRefCount: countElements(doc, "[data-word-comment-ref]"),
|
|
2256
|
-
revisionInsCount: countElements(doc, '[data-word-revision="ins"]'),
|
|
2257
|
-
revisionDelCount: countElements(doc, '[data-word-revision="del"]'),
|
|
2258
|
-
pageBreakCount: countElements(doc, "[data-word-page-break='1']"),
|
|
2259
|
-
pageSpacerCount: countElements(doc, "[data-word-page-spacer='1']"),
|
|
2260
|
-
textCharCount
|
|
2261
|
-
};
|
|
2262
|
-
}
|
|
2263
|
-
function collectSemanticStatsFromHtml(rawHtml) {
|
|
2264
|
-
const parser = new DOMParser();
|
|
2265
|
-
const doc = parser.parseFromString(rawHtml, "text/html");
|
|
2266
|
-
return collectSemanticStatsFromDocument(doc);
|
|
2267
|
-
}
|
|
2268
|
-
|
|
2269
|
-
// src/lib/fidelityScore.ts
|
|
2270
|
-
function ratioScore(actual, expected) {
|
|
2271
|
-
if (expected <= 0 && actual <= 0) return 1;
|
|
2272
|
-
if (expected <= 0 || actual < 0) return 0;
|
|
2273
|
-
const delta = Math.abs(actual - expected);
|
|
2274
|
-
const penalty = delta / expected;
|
|
2275
|
-
return Math.max(0, 1 - penalty);
|
|
2276
|
-
}
|
|
2277
|
-
function clamp01(v) {
|
|
2278
|
-
if (v < 0) return 0;
|
|
2279
|
-
if (v > 1) return 1;
|
|
2280
|
-
return v;
|
|
2281
|
-
}
|
|
2282
|
-
function calculateFidelityScore(expected, actual) {
|
|
2283
|
-
const structure = clamp01(
|
|
2284
|
-
(ratioScore(actual.paragraphCount, expected.paragraphCount) + ratioScore(actual.headingCount, expected.headingCount) + ratioScore(actual.tableCount, expected.tableCount) + ratioScore(actual.tableCellCount, expected.tableCellCount) + ratioScore(actual.imageCount, expected.imageCount) + ratioScore(actual.ommlCount, expected.ommlCount) + ratioScore(actual.chartCount, expected.chartCount) + ratioScore(actual.smartArtCount, expected.smartArtCount) + ratioScore(actual.listParagraphCount, expected.listParagraphCount)) / 9
|
|
2285
|
-
);
|
|
2286
|
-
const styleProxy = clamp01(ratioScore(actual.textCharCount, expected.textCharCount));
|
|
2287
|
-
const pagination = clamp01(ratioScore(actual.pageSpacerCount, expected.pageSpacerCount));
|
|
2288
|
-
const overall = clamp01(structure * 0.6 + styleProxy * 0.25 + pagination * 0.15);
|
|
2289
|
-
return { structure, styleProxy, pagination, overall };
|
|
2290
|
-
}
|
|
2291
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
2292
|
-
0 && (module.exports = {
|
|
2293
|
-
DocsWordElement,
|
|
2294
|
-
calculateFidelityScore,
|
|
2295
|
-
collectSemanticStatsFromDocument,
|
|
2296
|
-
collectSemanticStatsFromHtml,
|
|
2297
|
-
defineDocsWordElement
|
|
2298
|
-
});
|
|
2299
|
-
//# sourceMappingURL=index.cjs.map
|
|
60
|
+
`,ut=class extends HTMLElement{rootRef;toolbar;btnRead;btnUpload;btnClear;frame;pasteArea;fileInput;hint;htmlSnapshot;styleProfile=null;frameHeight=0;locale="zh";constructor(){super(),this.rootRef=this.attachShadow({mode:"open"}),this.locale=this.parseLocale(this.getAttribute("lang")),this.htmlSnapshot=nt("<p><br/></p>");let t=document.createElement("style");t.textContent=Ln,this.toolbar=document.createElement("div"),this.toolbar.className="toolbar",this.btnRead=document.createElement("button"),this.btnRead.onclick=()=>{this.loadClipboard()},this.btnUpload=document.createElement("button"),this.btnUpload.onclick=()=>this.fileInput.click(),this.btnClear=document.createElement("button"),this.btnClear.onclick=()=>this.clear(),this.fileInput=document.createElement("input"),this.fileInput.type="file",this.fileInput.accept=".docx",this.fileInput.style.display="none",this.fileInput.onchange=()=>{this.onUpload()},this.toolbar.append(this.btnRead,this.btnUpload,this.btnClear,this.fileInput),this.pasteArea=document.createElement("textarea"),this.pasteArea.className="paste",this.pasteArea.placeholder="",this.pasteArea.onpaste=n=>{n.preventDefault(),this.applyFromClipboardData(n.clipboardData)},this.hint=document.createElement("span"),this.hint.className="hint",this.hint.textContent="",this.frame=document.createElement("iframe"),this.frame.sandbox.add("allow-same-origin","allow-scripts"),this.frame.onload=()=>this.onFrameLoad(),this.rootRef.append(t,this.toolbar,this.pasteArea,this.hint,this.frame),this.syncLocaleText(),this.syncToolbarVisibility()}static get observedAttributes(){return["lang","show-toolbar"]}attributeChangedCallback(t,n,r){if(t==="lang"){this.locale=this.parseLocale(r),this.syncLocaleText();return}t==="show-toolbar"&&this.syncToolbarVisibility()}connectedCallback(){this.renderSnapshot(),this.dispatchEvent(new CustomEvent("docsjs-ready",{detail:{version:Nn}}))}setSnapshot(t){this.loadHtml(t)}loadHtml(t){this.styleProfile=null,this.htmlSnapshot=nt(t),this.renderSnapshot(),this.setHint(_[this.locale].loadedHtml),this.emitChange("api")}getSnapshot(){return this.htmlSnapshot}clear(){this.styleProfile=null,this.htmlSnapshot=nt("<p><br/></p>"),this.renderSnapshot(),this.setHint(_[this.locale].cleared),this.emitChange("clear")}async loadDocx(t){await this.applyDocx(t)}async onUpload(){let t=this.fileInput.files?.[0];t&&(await this.applyDocx(t),this.fileInput.value="")}async applyDocx(t){try{let[n,r]=await Promise.all([st(t),Kt(t)]);this.styleProfile=r,this.htmlSnapshot=n.htmlSnapshot,this.renderSnapshot(),this.setHint(_[this.locale].loadedWord(r.sourceFileName)),this.emitChange("upload",r.sourceFileName,n.report)}catch(n){this.emitError(n instanceof Error?n.message:_[this.locale].parseFailed)}}async loadClipboard(){if(!navigator.clipboard?.read){this.emitError(_[this.locale].noClipboardRead);return}try{let t=await navigator.clipboard.read(),n=await _t(t);this.applyPayload(n.html,n.text)}catch(t){this.emitError(t instanceof Error?t.message:_[this.locale].clipboardReadFailed)}}async applyFromClipboardData(t){if(!t)return;let n=await Ot(t);this.applyPayload(n.html,n.text)}applyPayload(t,n){if(this.styleProfile=null,t.trim())this.htmlSnapshot=nt(t);else if(n.trim())this.htmlSnapshot=nt(`<p>${n.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">")}</p>`);else{this.setHint(_[this.locale].noContent);return}this.renderSnapshot(),this.setHint(_[this.locale].importedClipboard),this.emitChange("paste")}onFrameLoad(){let t=this.frame.contentDocument;t&&(ee({doc:t,styleProfile:this.styleProfile,showFormattingMarks:!1}),this.syncHeight(),window.setTimeout(()=>this.syncHeight(),120))}syncHeight(){let t=this.frame.contentDocument;if(!t)return;let r=Math.max(760,t.body.scrollHeight,t.documentElement.scrollHeight)+24;Math.abs(r-this.frameHeight)<2||(this.frameHeight=r,this.frame.style.height=`${r}px`)}renderSnapshot(){this.frame.srcdoc=this.htmlSnapshot}emitChange(t,n,r){this.dispatchEvent(new CustomEvent("docsjs-change",{detail:{htmlSnapshot:this.htmlSnapshot,source:t,fileName:n,parseReport:r}}))}emitError(t){this.dispatchEvent(new CustomEvent("docsjs-error",{detail:{message:t}})),this.setHint(`${_[this.locale].errorPrefix}${t}`)}setHint(t){this.hint.textContent=t}parseLocale(t){return t?.toLowerCase()==="en"?"en":"zh"}syncToolbarVisibility(){let t=this.getAttribute("show-toolbar"),n=t===null||t===""||t==="1"||t.toLowerCase()==="true";this.toolbar.style.display=n?"flex":"none"}syncLocaleText(){let t=_[this.locale];this.btnRead.textContent=t.readClipboard,this.btnUpload.textContent=t.uploadWord,this.btnClear.textContent=t.clear,this.pasteArea.placeholder=t.pastePlaceholder,(!this.hint.textContent||this.hint.textContent===_.en.waitImport||this.hint.textContent===_.zh.waitImport)&&(this.hint.textContent=t.waitImport)}};function ne(){customElements.get("docs-word-editor")||customElements.define("docs-word-editor",ut)}function j(e,t){return e.querySelectorAll(t).length}function Hn(e){return e.hasAttribute("data-word-list")||e.querySelector("span.__word-list-marker")?!0:(e.getAttribute("style")??"").toLowerCase().includes("mso-list")}function Ct(e){let t=Array.from(e.querySelectorAll("p")),n=t.filter(o=>Hn(o)).length,r=(e.body.textContent??"").replace(/\s+/g,"").length;return{paragraphCount:t.length,headingCount:j(e,"h1,h2,h3,h4,h5,h6"),tableCount:j(e,"table"),tableCellCount:j(e,"td,th"),imageCount:j(e,"img"),anchorImageCount:j(e,'img[data-word-anchor="1"]'),wrappedImageCount:j(e,"img[data-word-wrap]"),ommlCount:j(e,"[data-word-omml]"),chartCount:j(e,"[data-word-chart]"),smartArtCount:j(e,"[data-word-smartart]"),listParagraphCount:n,commentRefCount:j(e,"[data-word-comment-ref]"),revisionInsCount:j(e,'[data-word-revision="ins"]'),revisionDelCount:j(e,'[data-word-revision="del"]'),pageBreakCount:j(e,"[data-word-page-break='1']"),pageSpacerCount:j(e,"[data-word-page-spacer='1']"),textCharCount:r}}function re(e){let n=new DOMParser().parseFromString(e,"text/html");return Ct(n)}function X(e,t){if(t<=0&&e<=0)return 1;if(t<=0||e<0)return 0;let r=Math.abs(e-t)/t;return Math.max(0,1-r)}function bt(e){return e<0?0:e>1?1:e}function oe(e,t){let n=bt((X(t.paragraphCount,e.paragraphCount)+X(t.headingCount,e.headingCount)+X(t.tableCount,e.tableCount)+X(t.tableCellCount,e.tableCellCount)+X(t.imageCount,e.imageCount)+X(t.ommlCount,e.ommlCount)+X(t.chartCount,e.chartCount)+X(t.smartArtCount,e.smartArtCount)+X(t.listParagraphCount,e.listParagraphCount))/9),r=bt(X(t.textCharCount,e.textCharCount)),o=bt(X(t.pageSpacerCount,e.pageSpacerCount)),i=bt(n*.6+r*.25+o*.15);return{structure:n,styleProxy:r,pagination:o,overall:i}}0&&(module.exports={DocsWordElement,calculateFidelityScore,collectSemanticStatsFromDocument,collectSemanticStatsFromHtml,defineDocsWordElement,parseDocxToHtmlSnapshot,parseDocxToHtmlSnapshotWithReport});
|