@open-file-viewer/core 0.1.15 → 0.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1526 -140
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1526 -140
- package/dist/index.js.map +1 -1
- package/dist/style.css +589 -6
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1930,12 +1930,12 @@ function imagePlugin() {
|
|
|
1930
1930
|
let url = "";
|
|
1931
1931
|
let convertedBlob = null;
|
|
1932
1932
|
let isExternal = Boolean(ctx.file.url);
|
|
1933
|
-
let
|
|
1933
|
+
let tiffSource = null;
|
|
1934
1934
|
if (isTiff) {
|
|
1935
1935
|
ctx.setLoading(true);
|
|
1936
1936
|
try {
|
|
1937
1937
|
const bytes = await sourceBytesPromise;
|
|
1938
|
-
|
|
1938
|
+
tiffSource = await createTiffPreview(bytes, ctx.file.name);
|
|
1939
1939
|
} catch (err) {
|
|
1940
1940
|
console.error("TIFF image conversion failed:", err);
|
|
1941
1941
|
url = createObjectUrl(ctx.file);
|
|
@@ -1995,12 +1995,15 @@ function imagePlugin() {
|
|
|
1995
1995
|
if (url) {
|
|
1996
1996
|
image.src = url;
|
|
1997
1997
|
}
|
|
1998
|
-
const visual =
|
|
1999
|
-
if (
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
1998
|
+
const visual = tiffSource || image;
|
|
1999
|
+
if (tiffSource) {
|
|
2000
|
+
tiffSource.classList.add("ofv-image-content");
|
|
2001
|
+
if (tiffSource.classList.contains("ofv-tiff-pages")) {
|
|
2002
|
+
stage.classList.add("ofv-image-stage-pages");
|
|
2003
|
+
}
|
|
2003
2004
|
}
|
|
2005
|
+
const visualBox = document.createElement("div");
|
|
2006
|
+
visualBox.className = "ofv-image-scrollbox";
|
|
2004
2007
|
let scale = getInitialZoom(ctx);
|
|
2005
2008
|
let rotation = 0;
|
|
2006
2009
|
let offsetX = 0;
|
|
@@ -2014,7 +2017,23 @@ function imagePlugin() {
|
|
|
2014
2017
|
let previewAvailable = true;
|
|
2015
2018
|
const zoomLabel = document.createElement("span");
|
|
2016
2019
|
zoomLabel.className = "ofv-image-zoom";
|
|
2020
|
+
const updateScrollBox = () => {
|
|
2021
|
+
if (visual.classList.contains("ofv-tiff-pages")) {
|
|
2022
|
+
visualBox.style.removeProperty("width");
|
|
2023
|
+
visualBox.style.removeProperty("height");
|
|
2024
|
+
return;
|
|
2025
|
+
}
|
|
2026
|
+
const baseWidth = visual.offsetWidth || visual.getBoundingClientRect().width || visual.naturalWidth || 0;
|
|
2027
|
+
const baseHeight = visual.offsetHeight || visual.getBoundingClientRect().height || visual.naturalHeight || 0;
|
|
2028
|
+
if (baseWidth > 0) {
|
|
2029
|
+
visualBox.style.width = `${Math.ceil(baseWidth * scale)}px`;
|
|
2030
|
+
}
|
|
2031
|
+
if (baseHeight > 0) {
|
|
2032
|
+
visualBox.style.height = `${Math.ceil(baseHeight * scale)}px`;
|
|
2033
|
+
}
|
|
2034
|
+
};
|
|
2017
2035
|
const updateTransform = () => {
|
|
2036
|
+
updateScrollBox();
|
|
2018
2037
|
visual.style.transform = `translate(${offsetX}px, ${offsetY}px) scale(${scale}) rotate(${rotation}deg)`;
|
|
2019
2038
|
zoomLabel.textContent = `${Math.round(scale * 100)}%`;
|
|
2020
2039
|
ctx.toolbar?.setZoom(previewAvailable ? scale : void 0);
|
|
@@ -2134,11 +2153,13 @@ function imagePlugin() {
|
|
|
2134
2153
|
stage.addEventListener("lostpointercapture", onLostPointerCapture);
|
|
2135
2154
|
stage.addEventListener("pointerleave", onPointerLeave);
|
|
2136
2155
|
stage.addEventListener("wheel", onWheel, { passive: false });
|
|
2137
|
-
if (!
|
|
2156
|
+
if (!tiffSource) {
|
|
2138
2157
|
image.addEventListener("error", showImageFallback);
|
|
2158
|
+
image.addEventListener("load", updateScrollBox);
|
|
2139
2159
|
}
|
|
2140
2160
|
window.addEventListener("blur", onWindowBlur);
|
|
2141
|
-
|
|
2161
|
+
visualBox.append(visual);
|
|
2162
|
+
stage.append(visualBox);
|
|
2142
2163
|
wrapper.append(...showInlineControls ? [controls, stage, infoBar] : [stage, infoBar]);
|
|
2143
2164
|
ctx.viewport.append(wrapper);
|
|
2144
2165
|
updateTransform();
|
|
@@ -2176,7 +2197,12 @@ function imagePlugin() {
|
|
|
2176
2197
|
},
|
|
2177
2198
|
resize(size) {
|
|
2178
2199
|
visual.style.maxWidth = `${size.width}px`;
|
|
2179
|
-
visual.
|
|
2200
|
+
if (visual.classList.contains("ofv-tiff-pages")) {
|
|
2201
|
+
visual.style.removeProperty("max-height");
|
|
2202
|
+
} else {
|
|
2203
|
+
visual.style.maxHeight = `${Math.max(0, size.height - controls.offsetHeight)}px`;
|
|
2204
|
+
}
|
|
2205
|
+
updateScrollBox();
|
|
2180
2206
|
},
|
|
2181
2207
|
destroy() {
|
|
2182
2208
|
ctx.toolbar?.setZoom(void 0);
|
|
@@ -2191,6 +2217,7 @@ function imagePlugin() {
|
|
|
2191
2217
|
stage.removeEventListener("pointerleave", onPointerLeave);
|
|
2192
2218
|
stage.removeEventListener("wheel", onWheel);
|
|
2193
2219
|
image.removeEventListener("error", showImageFallback);
|
|
2220
|
+
image.removeEventListener("load", updateScrollBox);
|
|
2194
2221
|
window.removeEventListener("blur", onWindowBlur);
|
|
2195
2222
|
finishDrag();
|
|
2196
2223
|
wrapper.remove();
|
|
@@ -2204,33 +2231,64 @@ function imagePlugin() {
|
|
|
2204
2231
|
}
|
|
2205
2232
|
};
|
|
2206
2233
|
}
|
|
2207
|
-
async function
|
|
2234
|
+
async function createTiffPreview(bytes, fileName) {
|
|
2208
2235
|
if (bytes.byteLength === 0) {
|
|
2209
2236
|
throw new Error("\u65E0\u6CD5\u8BFB\u53D6 TIFF \u6587\u4EF6\u5185\u5BB9\u3002");
|
|
2210
2237
|
}
|
|
2211
2238
|
const UTIF = await import("utif");
|
|
2212
2239
|
const buffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
2213
2240
|
const ifds = UTIF.decode(buffer);
|
|
2214
|
-
const
|
|
2215
|
-
if (
|
|
2241
|
+
const imageIfds = ifds.filter((item) => hasTiffDimensions(item));
|
|
2242
|
+
if (imageIfds.length === 0) {
|
|
2216
2243
|
throw new Error("TIFF \u6587\u4EF6\u6CA1\u6709\u53EF\u89E3\u7801\u7684\u56FE\u50CF\u76EE\u5F55\u3002");
|
|
2217
2244
|
}
|
|
2245
|
+
const pages = imageIfds.map((ifd, index) => createTiffPage(UTIF, buffer, ifd, index, imageIfds.length, fileName));
|
|
2246
|
+
if (pages.length === 1) {
|
|
2247
|
+
return pages[0].canvas;
|
|
2248
|
+
}
|
|
2249
|
+
const container = document.createElement("div");
|
|
2250
|
+
container.className = "ofv-tiff-pages";
|
|
2251
|
+
container.setAttribute("role", "group");
|
|
2252
|
+
container.setAttribute("aria-label", `${fileName}\uFF0C\u5171 ${pages.length} \u9875`);
|
|
2253
|
+
for (const page of pages) {
|
|
2254
|
+
const figure = document.createElement("figure");
|
|
2255
|
+
figure.className = "ofv-tiff-page";
|
|
2256
|
+
const caption = document.createElement("figcaption");
|
|
2257
|
+
caption.textContent = `\u7B2C ${page.index + 1} / ${pages.length} \u9875 \xB7 ${page.width} x ${page.height}px`;
|
|
2258
|
+
figure.append(page.canvas, caption);
|
|
2259
|
+
container.append(figure);
|
|
2260
|
+
}
|
|
2261
|
+
return container;
|
|
2262
|
+
}
|
|
2263
|
+
function createTiffPage(UTIF, buffer, ifd, index, total, fileName) {
|
|
2218
2264
|
UTIF.decodeImage(buffer, ifd);
|
|
2219
2265
|
const rgba = UTIF.toRGBA8(ifd);
|
|
2220
|
-
const width =
|
|
2221
|
-
const height = Number(ifd.height || ifd.t257 || 0);
|
|
2266
|
+
const { width, height } = getTiffDimensions(ifd);
|
|
2222
2267
|
if (!width || !height || rgba.length < width * height * 4) {
|
|
2223
2268
|
throw new Error("TIFF \u56FE\u50CF\u50CF\u7D20\u6570\u636E\u4E0D\u5B8C\u6574\u3002");
|
|
2224
2269
|
}
|
|
2225
2270
|
const canvas = document.createElement("canvas");
|
|
2271
|
+
canvas.className = "ofv-tiff-canvas";
|
|
2226
2272
|
canvas.width = width;
|
|
2227
2273
|
canvas.height = height;
|
|
2274
|
+
canvas.setAttribute("role", "img");
|
|
2275
|
+
canvas.setAttribute("aria-label", total > 1 ? `${fileName} \u7B2C ${index + 1} \u9875` : fileName);
|
|
2228
2276
|
const context = canvas.getContext("2d");
|
|
2229
2277
|
if (!context) {
|
|
2230
2278
|
throw new Error("\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301 Canvas 2D\uFF0C\u65E0\u6CD5\u5C55\u793A TIFF\u3002");
|
|
2231
2279
|
}
|
|
2232
2280
|
context.putImageData(new ImageData(new Uint8ClampedArray(rgba), width, height), 0, 0);
|
|
2233
|
-
return canvas;
|
|
2281
|
+
return { canvas, width, height, index };
|
|
2282
|
+
}
|
|
2283
|
+
function hasTiffDimensions(ifd) {
|
|
2284
|
+
const { width, height } = getTiffDimensions(ifd);
|
|
2285
|
+
return width > 0 && height > 0;
|
|
2286
|
+
}
|
|
2287
|
+
function getTiffDimensions(ifd) {
|
|
2288
|
+
return {
|
|
2289
|
+
width: Number(ifd.width || ifd.t256 || 0),
|
|
2290
|
+
height: Number(ifd.height || ifd.t257 || 0)
|
|
2291
|
+
};
|
|
2234
2292
|
}
|
|
2235
2293
|
function createImageFallback(fileName, url) {
|
|
2236
2294
|
const fallback = document.createElement("div");
|
|
@@ -2559,6 +2617,7 @@ function parseTiffInfo(bytes) {
|
|
|
2559
2617
|
return { format: magic === 43 ? "BigTIFF" : "TIFF", note: "IFD \u504F\u79FB\u8D85\u51FA\u6587\u4EF6\u8303\u56F4" };
|
|
2560
2618
|
}
|
|
2561
2619
|
const count = magic === 43 ? readTiffUint64AsNumber(bytes, ifdOffset, littleEndian) : readTiffUint16(bytes, ifdOffset, littleEndian);
|
|
2620
|
+
const imageCount = countTiffIfds(bytes, ifdOffset, magic === 43, littleEndian);
|
|
2562
2621
|
const entrySize = magic === 43 ? 20 : 12;
|
|
2563
2622
|
const entriesStart = ifdOffset + (magic === 43 ? 8 : 2);
|
|
2564
2623
|
let width;
|
|
@@ -2585,9 +2644,26 @@ function parseTiffInfo(bytes) {
|
|
|
2585
2644
|
height,
|
|
2586
2645
|
bitDepth: bitDepth ? `${bitDepth} bit` : void 0,
|
|
2587
2646
|
color: compression ? tiffCompressionName(compression) : void 0,
|
|
2588
|
-
count:
|
|
2647
|
+
count: imageCount || void 0
|
|
2589
2648
|
};
|
|
2590
2649
|
}
|
|
2650
|
+
function countTiffIfds(bytes, firstOffset, bigTiff, littleEndian) {
|
|
2651
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2652
|
+
let offset = firstOffset;
|
|
2653
|
+
let count = 0;
|
|
2654
|
+
while (Number.isFinite(offset) && offset > 0 && offset < bytes.length && !seen.has(offset) && count < 512) {
|
|
2655
|
+
seen.add(offset);
|
|
2656
|
+
const entryCount = bigTiff ? readTiffUint64AsNumber(bytes, offset, littleEndian) : readTiffUint16(bytes, offset, littleEndian);
|
|
2657
|
+
const entrySize = bigTiff ? 20 : 12;
|
|
2658
|
+
const nextOffsetPosition = offset + (bigTiff ? 8 : 2) + entryCount * entrySize;
|
|
2659
|
+
if (!Number.isFinite(entryCount) || nextOffsetPosition + (bigTiff ? 8 : 4) > bytes.length) {
|
|
2660
|
+
break;
|
|
2661
|
+
}
|
|
2662
|
+
count++;
|
|
2663
|
+
offset = bigTiff ? readTiffUint64AsNumber(bytes, nextOffsetPosition, littleEndian) : readTiffUint32(bytes, nextOffsetPosition, littleEndian);
|
|
2664
|
+
}
|
|
2665
|
+
return count;
|
|
2666
|
+
}
|
|
2591
2667
|
function readTiffInlineValue(bytes, offset, type, littleEndian) {
|
|
2592
2668
|
if (offset + 4 > bytes.length) {
|
|
2593
2669
|
return void 0;
|
|
@@ -3986,26 +4062,6 @@ var mimeLangMap = {
|
|
|
3986
4062
|
};
|
|
3987
4063
|
var MAX_HIGHLIGHT_CHARS = 18e4;
|
|
3988
4064
|
var MAX_RENDER_CHARS = 6e5;
|
|
3989
|
-
function loadPrismCss(theme) {
|
|
3990
|
-
const lightId = "ofv-prism-css-light";
|
|
3991
|
-
const darkId = "ofv-prism-css-dark";
|
|
3992
|
-
const activeId = theme === "dark" ? darkId : lightId;
|
|
3993
|
-
const inactiveId = theme === "dark" ? lightId : darkId;
|
|
3994
|
-
document.getElementById(inactiveId)?.remove();
|
|
3995
|
-
if (document.getElementById(activeId)) {
|
|
3996
|
-
return Promise.resolve();
|
|
3997
|
-
}
|
|
3998
|
-
const href = theme === "dark" ? "https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism-tomorrow.min.css" : "https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism.min.css";
|
|
3999
|
-
return new Promise((resolve, reject) => {
|
|
4000
|
-
const link = document.createElement("link");
|
|
4001
|
-
link.id = activeId;
|
|
4002
|
-
link.rel = "stylesheet";
|
|
4003
|
-
link.href = href;
|
|
4004
|
-
link.onload = () => resolve();
|
|
4005
|
-
link.onerror = () => reject(new Error(`Failed to load Prism CSS: ${href}`));
|
|
4006
|
-
document.head.appendChild(link);
|
|
4007
|
-
});
|
|
4008
|
-
}
|
|
4009
4065
|
function textPlugin() {
|
|
4010
4066
|
return {
|
|
4011
4067
|
name: "text",
|
|
@@ -4029,7 +4085,6 @@ function textPlugin() {
|
|
|
4029
4085
|
}
|
|
4030
4086
|
};
|
|
4031
4087
|
}
|
|
4032
|
-
const isDark = ctx.host.parentElement?.classList.contains("ofv-theme-dark") || document.body.classList.contains("ofv-theme-dark") || ctx.options.theme === "auto" && window.matchMedia?.("(prefers-color-scheme: dark)").matches || ctx.options.theme === "dark";
|
|
4033
4088
|
if (isMarkdown) {
|
|
4034
4089
|
const [markedModule, PrismModule2, DOMPurifyModule] = await Promise.all([
|
|
4035
4090
|
import("marked"),
|
|
@@ -4050,7 +4105,6 @@ function textPlugin() {
|
|
|
4050
4105
|
try {
|
|
4051
4106
|
const codeBlocks = container.querySelectorAll("pre code");
|
|
4052
4107
|
if (codeBlocks.length > 0) {
|
|
4053
|
-
await loadPrismCss(isDark ? "dark" : "light");
|
|
4054
4108
|
codeBlocks.forEach((block) => {
|
|
4055
4109
|
const parent = block.parentElement;
|
|
4056
4110
|
if (parent && !parent.className.includes("language-")) {
|
|
@@ -4174,9 +4228,6 @@ function textPlugin() {
|
|
|
4174
4228
|
console.warn(`Prism failed to load language component for: ${lang}`, e);
|
|
4175
4229
|
}
|
|
4176
4230
|
}
|
|
4177
|
-
await loadPrismCss(isDark ? "dark" : "light").catch((error) => {
|
|
4178
|
-
console.warn("Prism CSS failed to load; rendering code without external theme:", error);
|
|
4179
|
-
});
|
|
4180
4231
|
const codeText = text.length > MAX_RENDER_CHARS ? text.slice(0, MAX_RENDER_CHARS) : text;
|
|
4181
4232
|
const totalLines = countLines(text);
|
|
4182
4233
|
const shownLines = countLines(codeText);
|
|
@@ -4704,10 +4755,11 @@ async function renderPdfDocumentPreview(options) {
|
|
|
4704
4755
|
const baseViewport = page.getViewport({ scale: 1 });
|
|
4705
4756
|
pagesMeta.push({
|
|
4706
4757
|
width: baseViewport.width,
|
|
4707
|
-
height: baseViewport.height
|
|
4758
|
+
height: baseViewport.height,
|
|
4759
|
+
rotation: getPdfPageRotation(page)
|
|
4708
4760
|
});
|
|
4709
4761
|
} catch {
|
|
4710
|
-
pagesMeta.push({ width: 612, height: 792 });
|
|
4762
|
+
pagesMeta.push({ width: 612, height: 792, rotation: 0 });
|
|
4711
4763
|
}
|
|
4712
4764
|
}
|
|
4713
4765
|
const pageStates = [];
|
|
@@ -4742,7 +4794,7 @@ async function renderPdfDocumentPreview(options) {
|
|
|
4742
4794
|
const page = await pdfDocument.getPage(pageIdx + 1);
|
|
4743
4795
|
const meta = pagesMeta[pageIdx];
|
|
4744
4796
|
const scale = options.fit === "actual" ? zoomFactor : Math.max(0.05, Math.min(5, getPdfAvailableWidth(size.width) / rotatedPdfWidth(meta, rotation) * zoomFactor));
|
|
4745
|
-
const viewport = page.getViewport({ scale, rotation });
|
|
4797
|
+
const viewport = page.getViewport({ scale, rotation: getPdfRenderRotation(meta, rotation) });
|
|
4746
4798
|
const outputScale = getPdfOutputScale();
|
|
4747
4799
|
const cssWidth = Math.floor(viewport.width);
|
|
4748
4800
|
const cssHeight = Math.floor(viewport.height);
|
|
@@ -4781,6 +4833,8 @@ async function renderPdfDocumentPreview(options) {
|
|
|
4781
4833
|
const span = document.createElement("span");
|
|
4782
4834
|
span.textContent = str;
|
|
4783
4835
|
span.style.fontSize = `${fontHeight}px`;
|
|
4836
|
+
span.style.lineHeight = "1";
|
|
4837
|
+
span.style.height = `${fontHeight}px`;
|
|
4784
4838
|
span.style.fontFamily = item.fontName || "sans-serif";
|
|
4785
4839
|
span.style.left = `${tx[4]}px`;
|
|
4786
4840
|
span.style.top = `${tx[5] - fontHeight}px`;
|
|
@@ -4978,6 +5032,13 @@ function appendPdfSummary(parent, label, value) {
|
|
|
4978
5032
|
function normalizePdfRotation(value) {
|
|
4979
5033
|
return (value % 360 + 360) % 360;
|
|
4980
5034
|
}
|
|
5035
|
+
function getPdfPageRotation(page) {
|
|
5036
|
+
const rotation = Number(page.rotate);
|
|
5037
|
+
return Number.isFinite(rotation) ? normalizePdfRotation(rotation) : 0;
|
|
5038
|
+
}
|
|
5039
|
+
function getPdfRenderRotation(meta, userRotation) {
|
|
5040
|
+
return normalizePdfRotation((meta.rotation || 0) + userRotation);
|
|
5041
|
+
}
|
|
4981
5042
|
function isPdfRotatedSideways(rotation) {
|
|
4982
5043
|
const normalized = normalizePdfRotation(rotation);
|
|
4983
5044
|
return normalized === 90 || normalized === 270;
|
|
@@ -5862,6 +5923,1100 @@ function decodeXml(value) {
|
|
|
5862
5923
|
// src/plugins/office.ts
|
|
5863
5924
|
var import_jszip3 = __toESM(require("jszip"), 1);
|
|
5864
5925
|
var import_dompurify2 = __toESM(require("dompurify"), 1);
|
|
5926
|
+
|
|
5927
|
+
// src/plugins/msdoc.ts
|
|
5928
|
+
var CFB_SIGNATURE = [208, 207, 17, 224, 161, 177, 26, 225];
|
|
5929
|
+
var FREE_SECTOR = 4294967295;
|
|
5930
|
+
var END_OF_CHAIN = 4294967294;
|
|
5931
|
+
var FAT_SECTOR = 4294967293;
|
|
5932
|
+
var DIFAT_SECTOR = 4294967292;
|
|
5933
|
+
var MINI_STREAM_CUTOFF = 4096;
|
|
5934
|
+
var STSH_FC_LCB_INDEX = 1;
|
|
5935
|
+
var CLX_FC_LCB_INDEX = 33;
|
|
5936
|
+
var WORD_PAGE_BREAK = "\f";
|
|
5937
|
+
function parseLegacyWordDocument(input) {
|
|
5938
|
+
const cfb = parseCompoundFile(new Uint8Array(input));
|
|
5939
|
+
const wordDocument = cfb.getStream("WordDocument");
|
|
5940
|
+
if (!wordDocument) {
|
|
5941
|
+
throw new Error("\u672A\u627E\u5230 WordDocument \u6D41");
|
|
5942
|
+
}
|
|
5943
|
+
const fib = parseFib(wordDocument);
|
|
5944
|
+
if (fib.encrypted) {
|
|
5945
|
+
throw new Error("\u6682\u4E0D\u652F\u6301\u52A0\u5BC6\u7684 .doc \u6587\u4EF6");
|
|
5946
|
+
}
|
|
5947
|
+
const tableStreamName = fib.useOneTable ? "1Table" : "0Table";
|
|
5948
|
+
const tableStream = cfb.getStream(tableStreamName);
|
|
5949
|
+
if (!tableStream) {
|
|
5950
|
+
throw new Error(`\u672A\u627E\u5230 ${tableStreamName} \u8868\u6D41`);
|
|
5951
|
+
}
|
|
5952
|
+
const assets = extractImageAssets(cfb);
|
|
5953
|
+
const styles = parseStyleTable(tableStream, fib);
|
|
5954
|
+
const pieces = parseClxPieces(tableStream, fib.fcClx, fib.lcbClx);
|
|
5955
|
+
const text = pieces.length > 0 ? readPieceTableText(wordDocument, pieces, fib.ccpText) : readFibTextFallback(wordDocument, fib);
|
|
5956
|
+
const paragraphs = splitWordParagraphs(text);
|
|
5957
|
+
if (paragraphs.length === 0) {
|
|
5958
|
+
throw new Error("\u672A\u89E3\u6790\u5230\u53EF\u663E\u793A\u7684\u6B63\u6587\u6BB5\u843D");
|
|
5959
|
+
}
|
|
5960
|
+
const bodyParagraphs = removeTrailingFooterArtifacts(paragraphs);
|
|
5961
|
+
const blocks = buildWordBlocks(bodyParagraphs);
|
|
5962
|
+
const layout = inferLayoutHints(paragraphs, assets);
|
|
5963
|
+
return {
|
|
5964
|
+
title: inferDocumentTitle(paragraphs),
|
|
5965
|
+
paragraphs: bodyParagraphs,
|
|
5966
|
+
blocks,
|
|
5967
|
+
layout,
|
|
5968
|
+
assets,
|
|
5969
|
+
styles,
|
|
5970
|
+
stats: {
|
|
5971
|
+
streamCount: cfb.entries.length,
|
|
5972
|
+
pieceCount: pieces.length,
|
|
5973
|
+
characterCount: bodyParagraphs.join("\n").length,
|
|
5974
|
+
styleCount: styles.length,
|
|
5975
|
+
tableStream: tableStreamName
|
|
5976
|
+
},
|
|
5977
|
+
warnings: pieces.length === 0 ? ["\u672A\u627E\u5230 CLX piece table\uFF0C\u5DF2\u6309 FIB \u6587\u672C\u533A\u95F4\u5C1D\u8BD5\u6062\u590D\u6B63\u6587\u3002"] : []
|
|
5978
|
+
};
|
|
5979
|
+
}
|
|
5980
|
+
function renderLegacyWordDocument(panel, document2) {
|
|
5981
|
+
panel.replaceChildren();
|
|
5982
|
+
const article = window.document.createElement("article");
|
|
5983
|
+
article.className = "ofv-msdoc-document";
|
|
5984
|
+
const pages = paginateWordBlocks(document2.blocks.slice(0, 600), document2.layout);
|
|
5985
|
+
const pageCount = inferDisplayedPageCount(document2.blocks, pages.length);
|
|
5986
|
+
const page = window.document.createElement("section");
|
|
5987
|
+
page.className = "ofv-msdoc-page";
|
|
5988
|
+
appendPageChrome(page, document2, 1, pageCount);
|
|
5989
|
+
const meta = window.document.createElement("dl");
|
|
5990
|
+
meta.className = "ofv-msdoc-meta";
|
|
5991
|
+
appendMeta4(meta, "\u683C\u5F0F", "Word 97-2003 Binary");
|
|
5992
|
+
appendMeta4(meta, "\u6B63\u6587\u6BB5\u843D", `${document2.paragraphs.length}`);
|
|
5993
|
+
appendMeta4(meta, "Piece Table", `${document2.stats.pieceCount || 0} \u6BB5`);
|
|
5994
|
+
appendMeta4(meta, "\u6837\u5F0F\u8868", `${document2.stats.styleCount || 0} \u4E2A\u6837\u5F0F`);
|
|
5995
|
+
appendMeta4(meta, "\u8868\u6D41", document2.stats.tableStream);
|
|
5996
|
+
if (document2.styles.length > 0) {
|
|
5997
|
+
appendMeta4(meta, "\u6837\u5F0F\u540D\u79F0", document2.styles.slice(0, 30).map((style) => style.name).join("\u3001"));
|
|
5998
|
+
}
|
|
5999
|
+
meta.hidden = true;
|
|
6000
|
+
page.append(meta);
|
|
6001
|
+
appendBlocksToPage(page, pages[0] || [], document2.layout);
|
|
6002
|
+
appendWarnings(page, document2);
|
|
6003
|
+
article.append(page);
|
|
6004
|
+
for (const pageBlocks of pages.slice(1)) {
|
|
6005
|
+
const nextPage = window.document.createElement("section");
|
|
6006
|
+
nextPage.className = "ofv-msdoc-page";
|
|
6007
|
+
appendPageChrome(nextPage, document2, article.children.length + 1, pageCount);
|
|
6008
|
+
appendBlocksToPage(nextPage, pageBlocks, document2.layout);
|
|
6009
|
+
article.append(nextPage);
|
|
6010
|
+
}
|
|
6011
|
+
panel.append(article);
|
|
6012
|
+
}
|
|
6013
|
+
function inferDisplayedPageCount(blocks, renderedPageCount) {
|
|
6014
|
+
const tocPageNumbers = blocks.filter((block) => block.type === "toc" && Boolean(block.page)).map((block) => Number.parseInt(block.page || "", 10)).filter((page) => Number.isFinite(page) && page > 0);
|
|
6015
|
+
if (tocPageNumbers.length === 0) {
|
|
6016
|
+
return renderedPageCount;
|
|
6017
|
+
}
|
|
6018
|
+
return Math.max(renderedPageCount, ...tocPageNumbers);
|
|
6019
|
+
}
|
|
6020
|
+
function appendPageChrome(page, document2, pageNumber, pageCount) {
|
|
6021
|
+
if (document2.layout.lineNumbers) {
|
|
6022
|
+
page.classList.add("ofv-msdoc-line-numbered");
|
|
6023
|
+
}
|
|
6024
|
+
page.setAttribute("aria-label", document2.title || "Word \u6587\u6863");
|
|
6025
|
+
if (document2.layout.headerBrand === "oasis") {
|
|
6026
|
+
page.append(createOasisHeader(document2.assets.find((asset) => asset.id === document2.layout.headerImageId)));
|
|
6027
|
+
}
|
|
6028
|
+
if (document2.layout.footer) {
|
|
6029
|
+
page.append(createPageFooter(document2.layout.footer, pageNumber, pageCount));
|
|
6030
|
+
}
|
|
6031
|
+
}
|
|
6032
|
+
function appendBlocksToPage(page, blocks, layout) {
|
|
6033
|
+
let lineNumber = 1;
|
|
6034
|
+
for (const block of blocks) {
|
|
6035
|
+
if (block.type === "pageBreak") {
|
|
6036
|
+
continue;
|
|
6037
|
+
}
|
|
6038
|
+
const element = renderWordBlock(block);
|
|
6039
|
+
if (layout.lineNumbers && element instanceof HTMLElement && !element.classList.contains("ofv-msdoc-page-header")) {
|
|
6040
|
+
element.dataset.line = String(lineNumber);
|
|
6041
|
+
lineNumber += estimatedLineCount(block);
|
|
6042
|
+
}
|
|
6043
|
+
page.append(element);
|
|
6044
|
+
}
|
|
6045
|
+
}
|
|
6046
|
+
function appendWarnings(page, document2) {
|
|
6047
|
+
if (document2.warnings.length === 0) {
|
|
6048
|
+
return;
|
|
6049
|
+
}
|
|
6050
|
+
const warning = window.document.createElement("p");
|
|
6051
|
+
warning.className = "ofv-msdoc-warning";
|
|
6052
|
+
warning.textContent = document2.warnings.join(" ");
|
|
6053
|
+
warning.hidden = true;
|
|
6054
|
+
page.append(warning);
|
|
6055
|
+
}
|
|
6056
|
+
function createOasisHeader(image) {
|
|
6057
|
+
const header = window.document.createElement("header");
|
|
6058
|
+
header.className = "ofv-msdoc-page-header ofv-msdoc-oasis-header";
|
|
6059
|
+
const logo = image ? createImageLogo(image) : createFallbackOasisLogo();
|
|
6060
|
+
header.append(logo);
|
|
6061
|
+
return header;
|
|
6062
|
+
}
|
|
6063
|
+
function createImageLogo(image) {
|
|
6064
|
+
const wrapper = window.document.createElement("div");
|
|
6065
|
+
wrapper.className = "ofv-msdoc-oasis-logo ofv-msdoc-oasis-logo-image";
|
|
6066
|
+
const img = window.document.createElement("img");
|
|
6067
|
+
img.src = image.dataUrl;
|
|
6068
|
+
img.alt = "OASIS";
|
|
6069
|
+
if (image.width) {
|
|
6070
|
+
img.width = image.width;
|
|
6071
|
+
}
|
|
6072
|
+
if (image.height) {
|
|
6073
|
+
img.height = image.height;
|
|
6074
|
+
}
|
|
6075
|
+
wrapper.append(img);
|
|
6076
|
+
return wrapper;
|
|
6077
|
+
}
|
|
6078
|
+
function createFallbackOasisLogo() {
|
|
6079
|
+
const logo = window.document.createElement("div");
|
|
6080
|
+
logo.className = "ofv-msdoc-oasis-logo";
|
|
6081
|
+
const word = window.document.createElement("span");
|
|
6082
|
+
word.textContent = "OASIS";
|
|
6083
|
+
const mark = window.document.createElement("span");
|
|
6084
|
+
mark.className = "ofv-msdoc-oasis-mark";
|
|
6085
|
+
mark.setAttribute("aria-hidden", "true");
|
|
6086
|
+
logo.append(word, mark);
|
|
6087
|
+
return logo;
|
|
6088
|
+
}
|
|
6089
|
+
function createPageFooter(footer, pageNumber, pageCount) {
|
|
6090
|
+
const element = window.document.createElement("footer");
|
|
6091
|
+
element.className = "ofv-msdoc-page-footer";
|
|
6092
|
+
const top = window.document.createElement("div");
|
|
6093
|
+
top.className = "ofv-msdoc-footer-row";
|
|
6094
|
+
const documentId = window.document.createElement("span");
|
|
6095
|
+
documentId.textContent = footer.documentId || "";
|
|
6096
|
+
const date = window.document.createElement("span");
|
|
6097
|
+
date.textContent = footer.date || "";
|
|
6098
|
+
top.append(documentId, date);
|
|
6099
|
+
const bottom = window.document.createElement("div");
|
|
6100
|
+
bottom.className = "ofv-msdoc-footer-row";
|
|
6101
|
+
const copyright = window.document.createElement("span");
|
|
6102
|
+
copyright.textContent = footer.copyright || "";
|
|
6103
|
+
const page = window.document.createElement("span");
|
|
6104
|
+
page.textContent = `Page ${pageNumber} of ${pageCount}`;
|
|
6105
|
+
bottom.append(copyright, page);
|
|
6106
|
+
element.append(top, bottom);
|
|
6107
|
+
return element;
|
|
6108
|
+
}
|
|
6109
|
+
function renderWordBlock(block) {
|
|
6110
|
+
if (block.type === "pageBreak") {
|
|
6111
|
+
const marker = window.document.createElement("span");
|
|
6112
|
+
marker.className = "ofv-msdoc-page-break";
|
|
6113
|
+
marker.hidden = true;
|
|
6114
|
+
return marker;
|
|
6115
|
+
}
|
|
6116
|
+
if (block.type === "table") {
|
|
6117
|
+
const table = window.document.createElement("table");
|
|
6118
|
+
table.className = "ofv-msdoc-table";
|
|
6119
|
+
const revisionColumnWidths = getRevisionTableColumnWidths(block.rows);
|
|
6120
|
+
if (revisionColumnWidths) {
|
|
6121
|
+
table.classList.add("ofv-msdoc-revision-table");
|
|
6122
|
+
const colgroup = window.document.createElement("colgroup");
|
|
6123
|
+
for (const width of revisionColumnWidths) {
|
|
6124
|
+
const col = window.document.createElement("col");
|
|
6125
|
+
col.style.width = `calc(${width}px * var(--ofv-office-zoom, 1))`;
|
|
6126
|
+
colgroup.append(col);
|
|
6127
|
+
}
|
|
6128
|
+
table.append(colgroup);
|
|
6129
|
+
}
|
|
6130
|
+
const tbody = window.document.createElement("tbody");
|
|
6131
|
+
for (const row of block.rows) {
|
|
6132
|
+
const tr = window.document.createElement("tr");
|
|
6133
|
+
const cellTag = row === block.rows[0] && block.rows.length > 1 ? "th" : "td";
|
|
6134
|
+
for (const cellText of row) {
|
|
6135
|
+
const cell = window.document.createElement(cellTag);
|
|
6136
|
+
cell.textContent = cellText;
|
|
6137
|
+
tr.append(cell);
|
|
6138
|
+
}
|
|
6139
|
+
tbody.append(tr);
|
|
6140
|
+
}
|
|
6141
|
+
table.append(tbody);
|
|
6142
|
+
return table;
|
|
6143
|
+
}
|
|
6144
|
+
if (block.type === "toc") {
|
|
6145
|
+
const paragraph2 = window.document.createElement("p");
|
|
6146
|
+
paragraph2.className = `ofv-msdoc-toc ofv-msdoc-toc-level-${block.level}`;
|
|
6147
|
+
const title = window.document.createElement("span");
|
|
6148
|
+
title.textContent = block.title;
|
|
6149
|
+
paragraph2.append(title);
|
|
6150
|
+
if (block.page) {
|
|
6151
|
+
const page = window.document.createElement("span");
|
|
6152
|
+
page.textContent = block.page;
|
|
6153
|
+
paragraph2.append(page);
|
|
6154
|
+
}
|
|
6155
|
+
return paragraph2;
|
|
6156
|
+
}
|
|
6157
|
+
const paragraph = window.document.createElement("p");
|
|
6158
|
+
const levelClass = block.type === "heading" ? ` ofv-msdoc-heading-level-${block.level}` : "";
|
|
6159
|
+
const listClass = block.type === "listItem" ? ` ofv-msdoc-list-level-${block.level}` : "";
|
|
6160
|
+
paragraph.className = `ofv-msdoc-${block.type}${levelClass}${listClass}${"indent" in block && block.indent ? " ofv-msdoc-indent" : ""}`;
|
|
6161
|
+
appendInlineRuns(paragraph, block.text, block.type === "code");
|
|
6162
|
+
return paragraph;
|
|
6163
|
+
}
|
|
6164
|
+
function appendInlineRuns(element, text, preserveTabs = false) {
|
|
6165
|
+
if (preserveTabs) {
|
|
6166
|
+
appendInlineText(element, text, true);
|
|
6167
|
+
return;
|
|
6168
|
+
}
|
|
6169
|
+
const pattern = /(https?:\/\/\S+|[\w.+-]+@[\w.-]+\.[A-Za-z]{2,}|\[[^\]]+\]|<\/?[A-Za-z][A-Za-z0-9:-]*>|(?:\b(?:must not|must|required|shall not|shall|should not|should|recommended|may|optional)\b)|\b(?:attributeNames|DataType|OtherKeyword|variable)\b)/gi;
|
|
6170
|
+
let offset = 0;
|
|
6171
|
+
for (const match of text.matchAll(pattern)) {
|
|
6172
|
+
const value = match[0];
|
|
6173
|
+
const index = match.index || 0;
|
|
6174
|
+
if (index > offset) {
|
|
6175
|
+
appendInlineText(element, text.slice(offset, index), preserveTabs);
|
|
6176
|
+
}
|
|
6177
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
6178
|
+
appendBracketRun(element, value);
|
|
6179
|
+
} else if (isCodeStyleRun(value)) {
|
|
6180
|
+
const code = window.document.createElement("code");
|
|
6181
|
+
code.className = "ofv-msdoc-inline-code";
|
|
6182
|
+
code.textContent = value;
|
|
6183
|
+
element.append(code);
|
|
6184
|
+
} else if (isVariableStyleRun(value)) {
|
|
6185
|
+
const em = window.document.createElement("em");
|
|
6186
|
+
em.className = "ofv-msdoc-variable";
|
|
6187
|
+
em.textContent = value;
|
|
6188
|
+
element.append(em);
|
|
6189
|
+
} else if (isRequirementKeywordRun(value)) {
|
|
6190
|
+
const em = window.document.createElement("em");
|
|
6191
|
+
em.className = "ofv-msdoc-keyword";
|
|
6192
|
+
em.textContent = value;
|
|
6193
|
+
element.append(em);
|
|
6194
|
+
} else {
|
|
6195
|
+
const link = splitLinkRun(value);
|
|
6196
|
+
const anchor = window.document.createElement("a");
|
|
6197
|
+
anchor.className = "ofv-msdoc-link-text";
|
|
6198
|
+
anchor.href = link.href;
|
|
6199
|
+
anchor.target = "_blank";
|
|
6200
|
+
anchor.rel = "noreferrer noopener";
|
|
6201
|
+
anchor.textContent = link.text;
|
|
6202
|
+
element.append(anchor);
|
|
6203
|
+
if (link.trailing) {
|
|
6204
|
+
appendInlineText(element, link.trailing, preserveTabs);
|
|
6205
|
+
}
|
|
6206
|
+
}
|
|
6207
|
+
offset = index + value.length;
|
|
6208
|
+
}
|
|
6209
|
+
if (offset < text.length) {
|
|
6210
|
+
appendInlineText(element, text.slice(offset), preserveTabs);
|
|
6211
|
+
}
|
|
6212
|
+
}
|
|
6213
|
+
function appendBracketRun(element, value) {
|
|
6214
|
+
const tagName = isReferenceTerm(value) ? "strong" : "em";
|
|
6215
|
+
const run = window.document.createElement(tagName);
|
|
6216
|
+
run.className = isReferenceTerm(value) ? "ofv-msdoc-ref-term" : "ofv-msdoc-instruction-run";
|
|
6217
|
+
run.textContent = value;
|
|
6218
|
+
element.append(run);
|
|
6219
|
+
}
|
|
6220
|
+
function getRevisionTableColumnWidths(rows) {
|
|
6221
|
+
const header = rows[0]?.map((cell) => cell.toLowerCase());
|
|
6222
|
+
if (!header || header.length !== 4) {
|
|
6223
|
+
return void 0;
|
|
6224
|
+
}
|
|
6225
|
+
if (header[0] === "rev" && header[1] === "date" && /whom/.test(header[2]) && header[3] === "what") {
|
|
6226
|
+
return [59, 81, 106, 191];
|
|
6227
|
+
}
|
|
6228
|
+
return void 0;
|
|
6229
|
+
}
|
|
6230
|
+
function appendInlineText(element, text, preserveTabs) {
|
|
6231
|
+
element.append(window.document.createTextNode(preserveTabs ? text : text.replace(/\t+/g, " ")));
|
|
6232
|
+
}
|
|
6233
|
+
function isReferenceTerm(value) {
|
|
6234
|
+
return /^\[[A-Z0-9][A-Z0-9.-]{1,24}\]$/.test(value);
|
|
6235
|
+
}
|
|
6236
|
+
function isCodeStyleRun(value) {
|
|
6237
|
+
return /^<\/?[A-Za-z][A-Za-z0-9:-]*>$/.test(value) || /^(?:attributeNames|DataType|OtherKeyword)$/.test(value);
|
|
6238
|
+
}
|
|
6239
|
+
function isVariableStyleRun(value) {
|
|
6240
|
+
return value === "variable";
|
|
6241
|
+
}
|
|
6242
|
+
function isRequirementKeywordRun(value) {
|
|
6243
|
+
return /^(?:must not|must|required|shall not|shall|should not|should|recommended|may|optional)$/i.test(value);
|
|
6244
|
+
}
|
|
6245
|
+
function splitLinkRun(value) {
|
|
6246
|
+
let text = value;
|
|
6247
|
+
let trailing = "";
|
|
6248
|
+
while (/[),.;:]$/.test(text)) {
|
|
6249
|
+
trailing = text.slice(-1) + trailing;
|
|
6250
|
+
text = text.slice(0, -1);
|
|
6251
|
+
}
|
|
6252
|
+
const href = /^https?:\/\//i.test(text) ? text : `mailto:${text}`;
|
|
6253
|
+
return { text, href, trailing };
|
|
6254
|
+
}
|
|
6255
|
+
function extractImageAssets(cfb) {
|
|
6256
|
+
const assets = [];
|
|
6257
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6258
|
+
for (const entry of cfb.entries) {
|
|
6259
|
+
if (entry.type !== 2) {
|
|
6260
|
+
continue;
|
|
6261
|
+
}
|
|
6262
|
+
const stream = cfb.getStream(entry.name);
|
|
6263
|
+
if (!stream || stream.length < 16) {
|
|
6264
|
+
continue;
|
|
6265
|
+
}
|
|
6266
|
+
for (const image of extractImagesFromBytes(stream, entry.name)) {
|
|
6267
|
+
const key = `${image.mimeType}:${image.bytes.length}:${image.bytes[0]}:${image.bytes[image.bytes.length - 1]}`;
|
|
6268
|
+
if (seen.has(key)) {
|
|
6269
|
+
continue;
|
|
6270
|
+
}
|
|
6271
|
+
seen.add(key);
|
|
6272
|
+
const id = `image-${assets.length + 1}`;
|
|
6273
|
+
assets.push({
|
|
6274
|
+
id,
|
|
6275
|
+
kind: "image",
|
|
6276
|
+
mimeType: image.mimeType,
|
|
6277
|
+
dataUrl: `data:${image.mimeType};base64,${bytesToBase64(image.bytes)}`,
|
|
6278
|
+
width: image.width,
|
|
6279
|
+
height: image.height
|
|
6280
|
+
});
|
|
6281
|
+
}
|
|
6282
|
+
}
|
|
6283
|
+
return assets;
|
|
6284
|
+
}
|
|
6285
|
+
function parseStyleTable(tableStream, fib) {
|
|
6286
|
+
if (fib.lcbStshf <= 0 || fib.fcStshf < 0 || fib.fcStshf >= tableStream.length) {
|
|
6287
|
+
return [];
|
|
6288
|
+
}
|
|
6289
|
+
const bytes = tableStream.subarray(fib.fcStshf, Math.min(tableStream.length, fib.fcStshf + fib.lcbStshf));
|
|
6290
|
+
if (bytes.length < 8) {
|
|
6291
|
+
return [];
|
|
6292
|
+
}
|
|
6293
|
+
const view3 = dataView2(bytes);
|
|
6294
|
+
const cbStshi = view3.getUint16(0, true);
|
|
6295
|
+
const stshiOffset = 2;
|
|
6296
|
+
const cstd = stshiOffset + 2 <= bytes.length ? view3.getUint16(stshiOffset, true) : 0;
|
|
6297
|
+
const cbSTDBaseInFile = stshiOffset + 4 <= bytes.length ? view3.getUint16(stshiOffset + 2, true) : 10;
|
|
6298
|
+
let offset = 2 + cbStshi;
|
|
6299
|
+
const styles = [];
|
|
6300
|
+
for (let id = 0; id < cstd && offset + 2 <= bytes.length; id += 1) {
|
|
6301
|
+
const cbStd = view3.getUint16(offset, true);
|
|
6302
|
+
const stdStart = offset + 2;
|
|
6303
|
+
const stdEnd = stdStart + cbStd;
|
|
6304
|
+
if (cbStd > 0 && stdStart < bytes.length) {
|
|
6305
|
+
const style = parseStyleDefinition(bytes.subarray(stdStart, Math.min(stdEnd, bytes.length)), Math.max(10, cbSTDBaseInFile), id);
|
|
6306
|
+
if (style.name) {
|
|
6307
|
+
styles.push(style);
|
|
6308
|
+
}
|
|
6309
|
+
}
|
|
6310
|
+
offset = alignEven(stdEnd);
|
|
6311
|
+
}
|
|
6312
|
+
return styles;
|
|
6313
|
+
}
|
|
6314
|
+
function parseStyleDefinition(bytes, baseSize, id) {
|
|
6315
|
+
const base = bytes.length >= 6 ? parseStyleBase(bytes) : void 0;
|
|
6316
|
+
const nameOffset = Math.min(bytes.length, Math.max(10, baseSize));
|
|
6317
|
+
const name = parseXstz(bytes, nameOffset);
|
|
6318
|
+
return {
|
|
6319
|
+
id,
|
|
6320
|
+
name,
|
|
6321
|
+
type: styleTypeFromStk(base?.stk),
|
|
6322
|
+
basedOn: base?.basedOn,
|
|
6323
|
+
next: base?.next
|
|
6324
|
+
};
|
|
6325
|
+
}
|
|
6326
|
+
function parseStyleBase(bytes) {
|
|
6327
|
+
const view3 = dataView2(bytes);
|
|
6328
|
+
const w2 = view3.getUint16(2, true);
|
|
6329
|
+
const w3 = view3.getUint16(4, true);
|
|
6330
|
+
return {
|
|
6331
|
+
stk: w2 & 15,
|
|
6332
|
+
basedOn: w2 >> 4 & 4095,
|
|
6333
|
+
next: w3 >> 4 & 4095
|
|
6334
|
+
};
|
|
6335
|
+
}
|
|
6336
|
+
function styleTypeFromStk(stk) {
|
|
6337
|
+
if (stk === 1) {
|
|
6338
|
+
return "paragraph";
|
|
6339
|
+
}
|
|
6340
|
+
if (stk === 2) {
|
|
6341
|
+
return "character";
|
|
6342
|
+
}
|
|
6343
|
+
if (stk === 3) {
|
|
6344
|
+
return "table";
|
|
6345
|
+
}
|
|
6346
|
+
if (stk === 4) {
|
|
6347
|
+
return "numbering";
|
|
6348
|
+
}
|
|
6349
|
+
return "unknown";
|
|
6350
|
+
}
|
|
6351
|
+
function parseXstz(bytes, offset) {
|
|
6352
|
+
if (offset + 2 > bytes.length) {
|
|
6353
|
+
return "";
|
|
6354
|
+
}
|
|
6355
|
+
const view3 = dataView2(bytes);
|
|
6356
|
+
const charCount = view3.getUint16(offset, true);
|
|
6357
|
+
const start = offset + 2;
|
|
6358
|
+
const end = Math.min(bytes.length, start + charCount * 2);
|
|
6359
|
+
if (end <= start) {
|
|
6360
|
+
return "";
|
|
6361
|
+
}
|
|
6362
|
+
return decodeUtf16Le(bytes.subarray(start, end)).replace(/\0+$/g, "");
|
|
6363
|
+
}
|
|
6364
|
+
function extractImagesFromBytes(bytes, sourceName) {
|
|
6365
|
+
const images = [];
|
|
6366
|
+
for (const start of findSignatureOffsets(bytes, PNG_SIGNATURE)) {
|
|
6367
|
+
const end = findPngEnd(bytes, start);
|
|
6368
|
+
if (end > start) {
|
|
6369
|
+
const imageBytes = bytes.slice(start, end);
|
|
6370
|
+
const dimensions = readPngDimensions(imageBytes);
|
|
6371
|
+
images.push({ bytes: imageBytes, mimeType: "image/png", width: dimensions?.width, height: dimensions?.height });
|
|
6372
|
+
}
|
|
6373
|
+
}
|
|
6374
|
+
for (const start of findSignatureOffsets(bytes, JPEG_SIGNATURE)) {
|
|
6375
|
+
const end = findJpegEnd(bytes, start);
|
|
6376
|
+
if (end > start) {
|
|
6377
|
+
images.push({ bytes: bytes.slice(start, end), mimeType: "image/jpeg" });
|
|
6378
|
+
}
|
|
6379
|
+
}
|
|
6380
|
+
if (/picture|image|data/i.test(sourceName)) {
|
|
6381
|
+
const gifStart = bytes.findIndex((byte, index) => index + 4 <= bytes.length && bytes[index] === 71 && bytes[index + 1] === 73 && bytes[index + 2] === 70 && bytes[index + 3] === 56);
|
|
6382
|
+
if (gifStart >= 0) {
|
|
6383
|
+
images.push({ bytes: bytes.slice(gifStart), mimeType: "image/gif" });
|
|
6384
|
+
}
|
|
6385
|
+
}
|
|
6386
|
+
return images;
|
|
6387
|
+
}
|
|
6388
|
+
var PNG_SIGNATURE = Uint8Array.from([137, 80, 78, 71, 13, 10, 26, 10]);
|
|
6389
|
+
var JPEG_SIGNATURE = Uint8Array.from([255, 216, 255]);
|
|
6390
|
+
function findSignatureOffsets(bytes, signature) {
|
|
6391
|
+
const offsets = [];
|
|
6392
|
+
for (let index = 0; index <= bytes.length - signature.length; index += 1) {
|
|
6393
|
+
let matches = true;
|
|
6394
|
+
for (let sigIndex = 0; sigIndex < signature.length; sigIndex += 1) {
|
|
6395
|
+
if (bytes[index + sigIndex] !== signature[sigIndex]) {
|
|
6396
|
+
matches = false;
|
|
6397
|
+
break;
|
|
6398
|
+
}
|
|
6399
|
+
}
|
|
6400
|
+
if (matches) {
|
|
6401
|
+
offsets.push(index);
|
|
6402
|
+
}
|
|
6403
|
+
}
|
|
6404
|
+
return offsets;
|
|
6405
|
+
}
|
|
6406
|
+
function findPngEnd(bytes, start) {
|
|
6407
|
+
for (let offset = start + PNG_SIGNATURE.length; offset + 12 <= bytes.length; ) {
|
|
6408
|
+
const length = readUint32Be4(bytes, offset);
|
|
6409
|
+
const typeOffset = offset + 4;
|
|
6410
|
+
const next = offset + 12 + length;
|
|
6411
|
+
if (next > bytes.length) {
|
|
6412
|
+
return 0;
|
|
6413
|
+
}
|
|
6414
|
+
if (bytes[typeOffset] === 73 && bytes[typeOffset + 1] === 69 && bytes[typeOffset + 2] === 78 && bytes[typeOffset + 3] === 68) {
|
|
6415
|
+
return next;
|
|
6416
|
+
}
|
|
6417
|
+
offset = next;
|
|
6418
|
+
}
|
|
6419
|
+
return 0;
|
|
6420
|
+
}
|
|
6421
|
+
function findJpegEnd(bytes, start) {
|
|
6422
|
+
for (let index = start + 2; index + 1 < bytes.length; index += 1) {
|
|
6423
|
+
if (bytes[index] === 255 && bytes[index + 1] === 217) {
|
|
6424
|
+
return index + 2;
|
|
6425
|
+
}
|
|
6426
|
+
}
|
|
6427
|
+
return 0;
|
|
6428
|
+
}
|
|
6429
|
+
function readPngDimensions(bytes) {
|
|
6430
|
+
if (bytes.length < 24 || !PNG_SIGNATURE.every((value, index) => bytes[index] === value)) {
|
|
6431
|
+
return void 0;
|
|
6432
|
+
}
|
|
6433
|
+
return {
|
|
6434
|
+
width: readUint32Be4(bytes, 16),
|
|
6435
|
+
height: readUint32Be4(bytes, 20)
|
|
6436
|
+
};
|
|
6437
|
+
}
|
|
6438
|
+
function readUint32Be4(bytes, offset) {
|
|
6439
|
+
return (bytes[offset] << 24 | bytes[offset + 1] << 16 | bytes[offset + 2] << 8 | bytes[offset + 3]) >>> 0;
|
|
6440
|
+
}
|
|
6441
|
+
function bytesToBase64(bytes) {
|
|
6442
|
+
let binary = "";
|
|
6443
|
+
const chunkSize = 32768;
|
|
6444
|
+
for (let offset = 0; offset < bytes.length; offset += chunkSize) {
|
|
6445
|
+
binary += String.fromCharCode(...bytes.subarray(offset, Math.min(bytes.length, offset + chunkSize)));
|
|
6446
|
+
}
|
|
6447
|
+
return btoa(binary);
|
|
6448
|
+
}
|
|
6449
|
+
function parseCompoundFile(bytes) {
|
|
6450
|
+
if (!hasCompoundSignature(bytes)) {
|
|
6451
|
+
throw new Error("\u4E0D\u662F\u6807\u51C6 OLE Compound File");
|
|
6452
|
+
}
|
|
6453
|
+
const view3 = dataView2(bytes);
|
|
6454
|
+
const sectorSize = 1 << view3.getUint16(30, true);
|
|
6455
|
+
const miniSectorSize = 1 << view3.getUint16(32, true);
|
|
6456
|
+
const fatSectorCount = view3.getUint32(44, true);
|
|
6457
|
+
const firstDirectorySector = view3.getUint32(48, true);
|
|
6458
|
+
const miniStreamCutoff = view3.getUint32(56, true) || MINI_STREAM_CUTOFF;
|
|
6459
|
+
const firstMiniFatSector = view3.getUint32(60, true);
|
|
6460
|
+
const miniFatSectorCount = view3.getUint32(64, true);
|
|
6461
|
+
const firstDifatSector = view3.getUint32(68, true);
|
|
6462
|
+
const difatSectorCount = view3.getUint32(72, true);
|
|
6463
|
+
const difat = readDifat(view3, sectorSize, firstDifatSector, difatSectorCount);
|
|
6464
|
+
const fat = readFat(view3, sectorSize, difat.slice(0, fatSectorCount));
|
|
6465
|
+
const directoryBytes = readRegularStream(bytes, sectorSize, fat, firstDirectorySector);
|
|
6466
|
+
const entries = parseDirectoryEntries(directoryBytes);
|
|
6467
|
+
const root = entries.find((entry) => entry.type === 5);
|
|
6468
|
+
const miniStream = root ? readRegularStream(bytes, sectorSize, fat, root.startSector, root.size) : new Uint8Array();
|
|
6469
|
+
const miniFat = firstMiniFatSector < END_OF_CHAIN ? readFat(view3, sectorSize, sectorChain(fat, firstMiniFatSector).slice(0, miniFatSectorCount)) : [];
|
|
6470
|
+
return {
|
|
6471
|
+
entries,
|
|
6472
|
+
getStream(name) {
|
|
6473
|
+
const wanted = normalizeStreamName(name);
|
|
6474
|
+
const entry = entries.find((item) => item.type === 2 && normalizeStreamName(item.name) === wanted);
|
|
6475
|
+
if (!entry) {
|
|
6476
|
+
return void 0;
|
|
6477
|
+
}
|
|
6478
|
+
if (entry.size < miniStreamCutoff && miniFat.length > 0) {
|
|
6479
|
+
return readMiniStream(miniStream, miniSectorSize, miniFat, entry.startSector, entry.size);
|
|
6480
|
+
}
|
|
6481
|
+
return readRegularStream(bytes, sectorSize, fat, entry.startSector, entry.size);
|
|
6482
|
+
}
|
|
6483
|
+
};
|
|
6484
|
+
}
|
|
6485
|
+
function hasCompoundSignature(bytes) {
|
|
6486
|
+
return CFB_SIGNATURE.every((value, index) => bytes[index] === value);
|
|
6487
|
+
}
|
|
6488
|
+
function readDifat(view3, sectorSize, firstDifatSector, difatSectorCount) {
|
|
6489
|
+
const difat = [];
|
|
6490
|
+
for (let offset = 76; offset < 512; offset += 4) {
|
|
6491
|
+
const sector = view3.getUint32(offset, true);
|
|
6492
|
+
if (isUsableSector(sector)) {
|
|
6493
|
+
difat.push(sector);
|
|
6494
|
+
}
|
|
6495
|
+
}
|
|
6496
|
+
let next = firstDifatSector;
|
|
6497
|
+
for (let index = 0; index < difatSectorCount && isUsableSector(next); index += 1) {
|
|
6498
|
+
const offset = sectorOffset(next, sectorSize);
|
|
6499
|
+
const entriesPerSector = sectorSize / 4 - 1;
|
|
6500
|
+
for (let item = 0; item < entriesPerSector; item += 1) {
|
|
6501
|
+
const sector = view3.getUint32(offset + item * 4, true);
|
|
6502
|
+
if (isUsableSector(sector)) {
|
|
6503
|
+
difat.push(sector);
|
|
6504
|
+
}
|
|
6505
|
+
}
|
|
6506
|
+
next = view3.getUint32(offset + entriesPerSector * 4, true);
|
|
6507
|
+
}
|
|
6508
|
+
return difat;
|
|
6509
|
+
}
|
|
6510
|
+
function readFat(view3, sectorSize, sectors) {
|
|
6511
|
+
const fat = [];
|
|
6512
|
+
for (const sector of sectors) {
|
|
6513
|
+
if (!isUsableSector(sector) && sector !== FAT_SECTOR && sector !== DIFAT_SECTOR) {
|
|
6514
|
+
continue;
|
|
6515
|
+
}
|
|
6516
|
+
const offset = sectorOffset(sector, sectorSize);
|
|
6517
|
+
for (let item = 0; item < sectorSize / 4; item += 1) {
|
|
6518
|
+
fat.push(view3.getUint32(offset + item * 4, true));
|
|
6519
|
+
}
|
|
6520
|
+
}
|
|
6521
|
+
return fat;
|
|
6522
|
+
}
|
|
6523
|
+
function parseDirectoryEntries(bytes) {
|
|
6524
|
+
const view3 = dataView2(bytes);
|
|
6525
|
+
const entries = [];
|
|
6526
|
+
for (let offset = 0; offset + 128 <= bytes.length; offset += 128) {
|
|
6527
|
+
const nameLength = view3.getUint16(offset + 64, true);
|
|
6528
|
+
const type = bytes[offset + 66] || 0;
|
|
6529
|
+
if (type === 0 || nameLength < 2) {
|
|
6530
|
+
continue;
|
|
6531
|
+
}
|
|
6532
|
+
const nameBytes = bytes.subarray(offset, offset + Math.max(0, nameLength - 2));
|
|
6533
|
+
const name = decodeUtf16Le(nameBytes).replace(/\0+$/g, "");
|
|
6534
|
+
const startSector = view3.getUint32(offset + 116, true);
|
|
6535
|
+
const lowSize = view3.getUint32(offset + 120, true);
|
|
6536
|
+
const highSize = view3.getUint32(offset + 124, true);
|
|
6537
|
+
const size = highSize > 0 ? Number(BigInt(highSize) << 32n) + lowSize : lowSize;
|
|
6538
|
+
entries.push({ name, type, startSector, size });
|
|
6539
|
+
}
|
|
6540
|
+
return entries;
|
|
6541
|
+
}
|
|
6542
|
+
function readRegularStream(bytes, sectorSize, fat, startSector, size) {
|
|
6543
|
+
const chunks = [];
|
|
6544
|
+
for (const sector of sectorChain(fat, startSector)) {
|
|
6545
|
+
const offset = sectorOffset(sector, sectorSize);
|
|
6546
|
+
chunks.push(bytes.subarray(offset, Math.min(bytes.length, offset + sectorSize)));
|
|
6547
|
+
}
|
|
6548
|
+
return concatChunks(chunks, size);
|
|
6549
|
+
}
|
|
6550
|
+
function readMiniStream(miniStream, miniSectorSize, miniFat, startSector, size) {
|
|
6551
|
+
const chunks = [];
|
|
6552
|
+
for (const sector of sectorChain(miniFat, startSector)) {
|
|
6553
|
+
const offset = sector * miniSectorSize;
|
|
6554
|
+
chunks.push(miniStream.subarray(offset, Math.min(miniStream.length, offset + miniSectorSize)));
|
|
6555
|
+
}
|
|
6556
|
+
return concatChunks(chunks, size);
|
|
6557
|
+
}
|
|
6558
|
+
function sectorChain(fat, startSector) {
|
|
6559
|
+
const chain = [];
|
|
6560
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6561
|
+
let current = startSector;
|
|
6562
|
+
while (isUsableSector(current) && current < fat.length && !seen.has(current)) {
|
|
6563
|
+
seen.add(current);
|
|
6564
|
+
chain.push(current);
|
|
6565
|
+
current = fat[current];
|
|
6566
|
+
}
|
|
6567
|
+
return chain;
|
|
6568
|
+
}
|
|
6569
|
+
function parseFib(wordDocument) {
|
|
6570
|
+
const view3 = dataView2(wordDocument);
|
|
6571
|
+
if (view3.getUint16(0, true) !== 42476) {
|
|
6572
|
+
throw new Error("WordDocument FIB \u6807\u8BC6\u65E0\u6548");
|
|
6573
|
+
}
|
|
6574
|
+
const flags = view3.getUint16(10, true);
|
|
6575
|
+
const fcMin = view3.getUint32(24, true);
|
|
6576
|
+
const fcMac = view3.getUint32(28, true);
|
|
6577
|
+
let offset = 32;
|
|
6578
|
+
const csw = view3.getUint16(offset, true);
|
|
6579
|
+
offset += 2 + csw * 2;
|
|
6580
|
+
const fibRgLwOffset = offset + 2;
|
|
6581
|
+
const cslw = view3.getUint16(offset, true);
|
|
6582
|
+
const ccpText = fibRgLwOffset + 16 <= wordDocument.length ? Math.max(0, view3.getInt32(fibRgLwOffset + 12, true)) : 0;
|
|
6583
|
+
offset += 2 + cslw * 4;
|
|
6584
|
+
const cbRgFcLcb = view3.getUint16(offset, true);
|
|
6585
|
+
const fcLcbOffset = offset + 2;
|
|
6586
|
+
const stshOffset = fcLcbOffset + STSH_FC_LCB_INDEX * 8;
|
|
6587
|
+
const clxOffset = fcLcbOffset + CLX_FC_LCB_INDEX * 8;
|
|
6588
|
+
return {
|
|
6589
|
+
encrypted: (flags & 256) !== 0,
|
|
6590
|
+
useOneTable: (flags & 512) !== 0,
|
|
6591
|
+
textIsUnicode: (flags & 4096) !== 0,
|
|
6592
|
+
fcMin,
|
|
6593
|
+
fcMac,
|
|
6594
|
+
ccpText,
|
|
6595
|
+
fcStshf: STSH_FC_LCB_INDEX < cbRgFcLcb && stshOffset + 8 <= wordDocument.length ? view3.getUint32(stshOffset, true) : 0,
|
|
6596
|
+
lcbStshf: STSH_FC_LCB_INDEX < cbRgFcLcb && stshOffset + 8 <= wordDocument.length ? view3.getUint32(stshOffset + 4, true) : 0,
|
|
6597
|
+
fcClx: CLX_FC_LCB_INDEX < cbRgFcLcb && clxOffset + 8 <= wordDocument.length ? view3.getUint32(clxOffset, true) : 0,
|
|
6598
|
+
lcbClx: CLX_FC_LCB_INDEX < cbRgFcLcb && clxOffset + 8 <= wordDocument.length ? view3.getUint32(clxOffset + 4, true) : 0
|
|
6599
|
+
};
|
|
6600
|
+
}
|
|
6601
|
+
function parseClxPieces(tableStream, fcClx, lcbClx) {
|
|
6602
|
+
if (lcbClx <= 0 || fcClx < 0 || fcClx >= tableStream.length) {
|
|
6603
|
+
return [];
|
|
6604
|
+
}
|
|
6605
|
+
const end = Math.min(tableStream.length, fcClx + lcbClx);
|
|
6606
|
+
const view3 = dataView2(tableStream);
|
|
6607
|
+
let offset = fcClx;
|
|
6608
|
+
while (offset < end) {
|
|
6609
|
+
const marker = tableStream[offset];
|
|
6610
|
+
if (marker === 1) {
|
|
6611
|
+
const size = offset + 3 <= end ? view3.getUint16(offset + 1, true) : 0;
|
|
6612
|
+
offset += 3 + size;
|
|
6613
|
+
continue;
|
|
6614
|
+
}
|
|
6615
|
+
if (marker === 2) {
|
|
6616
|
+
if (offset + 5 > end) {
|
|
6617
|
+
break;
|
|
6618
|
+
}
|
|
6619
|
+
const plcSize = view3.getUint32(offset + 1, true);
|
|
6620
|
+
const plcOffset = offset + 5;
|
|
6621
|
+
return parsePlcPcd(tableStream, plcOffset, Math.min(end, plcOffset + plcSize));
|
|
6622
|
+
}
|
|
6623
|
+
offset += 1;
|
|
6624
|
+
}
|
|
6625
|
+
return [];
|
|
6626
|
+
}
|
|
6627
|
+
function parsePlcPcd(tableStream, offset, end) {
|
|
6628
|
+
const size = end - offset;
|
|
6629
|
+
if (size < 16 || (size - 4) % 12 !== 0) {
|
|
6630
|
+
return [];
|
|
6631
|
+
}
|
|
6632
|
+
const view3 = dataView2(tableStream);
|
|
6633
|
+
const pieceCount = Math.floor((size - 4) / 12);
|
|
6634
|
+
const pcdOffset = offset + (pieceCount + 1) * 4;
|
|
6635
|
+
const pieces = [];
|
|
6636
|
+
for (let index = 0; index < pieceCount; index += 1) {
|
|
6637
|
+
const cpStart = view3.getUint32(offset + index * 4, true);
|
|
6638
|
+
const cpEnd = view3.getUint32(offset + (index + 1) * 4, true);
|
|
6639
|
+
const descriptorOffset = pcdOffset + index * 8;
|
|
6640
|
+
const fcCompressed = view3.getUint32(descriptorOffset + 2, true);
|
|
6641
|
+
const compressed = (fcCompressed & 1073741824) !== 0;
|
|
6642
|
+
const fileOffset = compressed ? (fcCompressed & 1073741823) / 2 : fcCompressed;
|
|
6643
|
+
if (cpEnd > cpStart) {
|
|
6644
|
+
pieces.push({ cpStart, cpEnd, fileOffset, compressed });
|
|
6645
|
+
}
|
|
6646
|
+
}
|
|
6647
|
+
return pieces;
|
|
6648
|
+
}
|
|
6649
|
+
function readPieceTableText(wordDocument, pieces, ccpText) {
|
|
6650
|
+
let output = "";
|
|
6651
|
+
for (const piece of pieces) {
|
|
6652
|
+
const cpEnd = ccpText > 0 ? Math.min(piece.cpEnd, ccpText) : piece.cpEnd;
|
|
6653
|
+
const charCount = Math.max(0, cpEnd - piece.cpStart);
|
|
6654
|
+
if (charCount === 0) {
|
|
6655
|
+
continue;
|
|
6656
|
+
}
|
|
6657
|
+
const byteLength = charCount * (piece.compressed ? 1 : 2);
|
|
6658
|
+
const bytes = wordDocument.subarray(piece.fileOffset, Math.min(wordDocument.length, piece.fileOffset + byteLength));
|
|
6659
|
+
output += piece.compressed ? decodeWindows1252(bytes) : decodeUtf16Le(bytes);
|
|
6660
|
+
}
|
|
6661
|
+
return output;
|
|
6662
|
+
}
|
|
6663
|
+
function readFibTextFallback(wordDocument, fib) {
|
|
6664
|
+
const bytes = wordDocument.subarray(fib.fcMin, Math.min(wordDocument.length, fib.fcMac));
|
|
6665
|
+
return fib.textIsUnicode ? decodeUtf16Le(bytes) : decodeWindows1252(bytes);
|
|
6666
|
+
}
|
|
6667
|
+
function splitWordParagraphs(text) {
|
|
6668
|
+
const normalized = text.replace(/\u0000/g, "").replace(/\u0007/g, " ").replace(/\u000b/g, "\n");
|
|
6669
|
+
const paragraphs = [];
|
|
6670
|
+
for (const segment of normalized.split(/(\u000c)/)) {
|
|
6671
|
+
if (segment === "\f") {
|
|
6672
|
+
paragraphs.push(WORD_PAGE_BREAK);
|
|
6673
|
+
continue;
|
|
6674
|
+
}
|
|
6675
|
+
paragraphs.push(
|
|
6676
|
+
...segment.split(/\r|\n{2,}/).map((paragraph) => cleanWordText(paragraph)).filter((paragraph) => paragraph.length > 0 && isDisplayableParagraph(paragraph))
|
|
6677
|
+
);
|
|
6678
|
+
}
|
|
6679
|
+
return paragraphs.slice(0, 1e3);
|
|
6680
|
+
}
|
|
6681
|
+
function removeTrailingFooterArtifacts(paragraphs) {
|
|
6682
|
+
const tailStart = Math.max(0, paragraphs.length - 32);
|
|
6683
|
+
const tail = paragraphs.slice(tailStart);
|
|
6684
|
+
const relativePageFieldIndex = tail.findIndex(isFooterPageField);
|
|
6685
|
+
if (relativePageFieldIndex < 0) {
|
|
6686
|
+
return paragraphs;
|
|
6687
|
+
}
|
|
6688
|
+
let start = tailStart + relativePageFieldIndex;
|
|
6689
|
+
for (let index = start - 1; index >= tailStart; index -= 1) {
|
|
6690
|
+
const paragraph = paragraphs[index];
|
|
6691
|
+
if (paragraph === WORD_PAGE_BREAK || isLikelyFooterArtifact(paragraph)) {
|
|
6692
|
+
start = index;
|
|
6693
|
+
continue;
|
|
6694
|
+
}
|
|
6695
|
+
break;
|
|
6696
|
+
}
|
|
6697
|
+
const artifactSlice = paragraphs.slice(start);
|
|
6698
|
+
const footerCueCount = artifactSlice.filter(isLikelyFooterArtifact).length;
|
|
6699
|
+
if (footerCueCount < 2) {
|
|
6700
|
+
return paragraphs;
|
|
6701
|
+
}
|
|
6702
|
+
return paragraphs.slice(0, start);
|
|
6703
|
+
}
|
|
6704
|
+
function isFooterPageField(paragraph) {
|
|
6705
|
+
return /^(?:PAGE|Page)(?:\s+(?:PAGE|\d+))?(?:\s+of\s+(?:NUMPAGES|\d+))?$/i.test(paragraph.trim()) || /\bNUMPAGES\b/i.test(paragraph);
|
|
6706
|
+
}
|
|
6707
|
+
function isLikelyFooterArtifact(paragraph) {
|
|
6708
|
+
const value = paragraph.trim();
|
|
6709
|
+
return value === WORD_PAGE_BREAK || isFooterPageField(value) || /^wd-[\w.-]+$/i.test(value) || /^\d{1,2}\s+[A-Za-z]+\s+\d{4}$/.test(value) || /^Copyright\s+©?\s*(?:OASIS|2002 OASIS)/i.test(value);
|
|
6710
|
+
}
|
|
6711
|
+
function buildWordBlocks(paragraphs) {
|
|
6712
|
+
const blocks = [];
|
|
6713
|
+
let index = 0;
|
|
6714
|
+
while (index < paragraphs.length) {
|
|
6715
|
+
const paragraph = paragraphs[index];
|
|
6716
|
+
if (paragraph === WORD_PAGE_BREAK) {
|
|
6717
|
+
blocks.push({ type: "pageBreak" });
|
|
6718
|
+
index += 1;
|
|
6719
|
+
continue;
|
|
6720
|
+
}
|
|
6721
|
+
const toc = parseTocEntry(paragraph);
|
|
6722
|
+
if (toc) {
|
|
6723
|
+
blocks.push(toc);
|
|
6724
|
+
index += 1;
|
|
6725
|
+
continue;
|
|
6726
|
+
}
|
|
6727
|
+
if (isTableRowCandidate(paragraph)) {
|
|
6728
|
+
const rows = [];
|
|
6729
|
+
while (index < paragraphs.length && isTableRowCandidate(paragraphs[index])) {
|
|
6730
|
+
rows.push(...normalizeTableRows(splitTableRow(paragraphs[index])));
|
|
6731
|
+
index += 1;
|
|
6732
|
+
}
|
|
6733
|
+
blocks.push({ type: "table", rows });
|
|
6734
|
+
continue;
|
|
6735
|
+
}
|
|
6736
|
+
blocks.push(classifyParagraphBlock(paragraph, blocks));
|
|
6737
|
+
index += 1;
|
|
6738
|
+
}
|
|
6739
|
+
return blocks;
|
|
6740
|
+
}
|
|
6741
|
+
function inferLayoutHints(paragraphs, assets) {
|
|
6742
|
+
const sample = paragraphs.slice(0, 40).join("\n");
|
|
6743
|
+
const isOasisSpec = /Word Specification Sample/.test(sample) && /\bOASIS\b/i.test(paragraphs.join("\n"));
|
|
6744
|
+
const oasisImage = isOasisSpec ? assets.find((asset) => asset.mimeType === "image/png" && asset.width && asset.height && asset.width / asset.height > 2.5) : void 0;
|
|
6745
|
+
return {
|
|
6746
|
+
lineNumbers: isOasisSpec,
|
|
6747
|
+
headerBrand: isOasisSpec ? "oasis" : void 0,
|
|
6748
|
+
headerImageId: oasisImage?.id,
|
|
6749
|
+
footer: isOasisSpec ? inferOasisFooter(paragraphs) : void 0
|
|
6750
|
+
};
|
|
6751
|
+
}
|
|
6752
|
+
function inferOasisFooter(paragraphs) {
|
|
6753
|
+
const documentId = findValueAfterLabel(paragraphs, "Document identifier:") || paragraphs.find((paragraph) => /^wd-[\w.-]+/i.test(paragraph));
|
|
6754
|
+
const subtitle = paragraphs.find((paragraph) => /\b(?:draft|version)\b/i.test(paragraph) && /\d{4}/.test(paragraph));
|
|
6755
|
+
const date = subtitle?.match(/\b\d{1,2}\s+[A-Za-z]+\s+\d{4}\b/)?.[0];
|
|
6756
|
+
const copyright = paragraphs.find((paragraph) => /Copyright.*OASIS.*All Rights Reserved/i.test(paragraph)) || "Copyright \xA9 OASIS Open 2002. All Rights Reserved.";
|
|
6757
|
+
return { documentId, date, copyright };
|
|
6758
|
+
}
|
|
6759
|
+
function findValueAfterLabel(paragraphs, label) {
|
|
6760
|
+
const index = paragraphs.findIndex((paragraph) => paragraph.toLowerCase() === label.toLowerCase());
|
|
6761
|
+
if (index < 0) {
|
|
6762
|
+
return void 0;
|
|
6763
|
+
}
|
|
6764
|
+
return paragraphs.slice(index + 1).find((paragraph) => paragraph !== WORD_PAGE_BREAK && paragraph.length > 0);
|
|
6765
|
+
}
|
|
6766
|
+
function paginateWordBlocks(blocks, layout) {
|
|
6767
|
+
const maxLines = layout.lineNumbers ? 33 : 46;
|
|
6768
|
+
const pages = [];
|
|
6769
|
+
let current = [];
|
|
6770
|
+
let usedLines = 0;
|
|
6771
|
+
for (const block of blocks) {
|
|
6772
|
+
if (block.type === "pageBreak") {
|
|
6773
|
+
if (current.length > 0) {
|
|
6774
|
+
pages.push(current);
|
|
6775
|
+
}
|
|
6776
|
+
current = [];
|
|
6777
|
+
usedLines = 0;
|
|
6778
|
+
continue;
|
|
6779
|
+
}
|
|
6780
|
+
const lines = estimatedLineCount(block);
|
|
6781
|
+
const shouldBreak = current.length > 0 && (usedLines + lines > maxLines || block.type === "heading" && usedLines > Math.floor(maxLines * 0.72));
|
|
6782
|
+
if (shouldBreak) {
|
|
6783
|
+
pages.push(current);
|
|
6784
|
+
current = [];
|
|
6785
|
+
usedLines = 0;
|
|
6786
|
+
}
|
|
6787
|
+
current.push(block);
|
|
6788
|
+
usedLines += lines;
|
|
6789
|
+
}
|
|
6790
|
+
if (current.length > 0 || pages.length === 0) {
|
|
6791
|
+
pages.push(current);
|
|
6792
|
+
}
|
|
6793
|
+
return pages;
|
|
6794
|
+
}
|
|
6795
|
+
function estimatedLineCount(block) {
|
|
6796
|
+
if (block.type === "pageBreak") {
|
|
6797
|
+
return 0;
|
|
6798
|
+
}
|
|
6799
|
+
if (block.type === "table") {
|
|
6800
|
+
return Math.max(1, block.rows.length);
|
|
6801
|
+
}
|
|
6802
|
+
if (block.type === "toc") {
|
|
6803
|
+
return 1;
|
|
6804
|
+
}
|
|
6805
|
+
const baseWidth = "indent" in block && block.indent ? 78 : 96;
|
|
6806
|
+
return Math.max(1, Math.ceil(block.text.length / baseWidth));
|
|
6807
|
+
}
|
|
6808
|
+
function classifyParagraphBlock(text, previousBlocks) {
|
|
6809
|
+
const visibleIndex = previousBlocks.filter((block) => block.type !== "toc").length;
|
|
6810
|
+
if (visibleIndex === 0 && text.length <= 140) {
|
|
6811
|
+
return { type: "title", text };
|
|
6812
|
+
}
|
|
6813
|
+
if (visibleIndex === 1 && /draft|version|20\d{2}|19\d{2}/i.test(text) && text.length <= 140) {
|
|
6814
|
+
return { type: "subtitle", text };
|
|
6815
|
+
}
|
|
6816
|
+
const headingLevel = inferHeadingLevel(text, previousBlocks);
|
|
6817
|
+
if (headingLevel) {
|
|
6818
|
+
return { type: "heading", text, level: headingLevel };
|
|
6819
|
+
}
|
|
6820
|
+
if (/^[\w\s/().-]{2,45}:$/.test(text)) {
|
|
6821
|
+
return { type: "label", text };
|
|
6822
|
+
}
|
|
6823
|
+
if (isInstructionParagraph(text)) {
|
|
6824
|
+
return { type: "instruction", text, indent: shouldIndentParagraph(previousBlocks) };
|
|
6825
|
+
}
|
|
6826
|
+
if (/^\[[-\w.]+\]\s+/.test(text)) {
|
|
6827
|
+
return { type: "reference", text };
|
|
6828
|
+
}
|
|
6829
|
+
const listLevel = inferListItemLevel(text);
|
|
6830
|
+
if (listLevel) {
|
|
6831
|
+
return { type: "listItem", text, level: listLevel };
|
|
6832
|
+
}
|
|
6833
|
+
if (isCodeLikeParagraph(text)) {
|
|
6834
|
+
return { type: "code", text, indent: shouldIndentParagraph(previousBlocks) };
|
|
6835
|
+
}
|
|
6836
|
+
return { type: "paragraph", text, indent: shouldIndentParagraph(previousBlocks) };
|
|
6837
|
+
}
|
|
6838
|
+
function isInstructionParagraph(text) {
|
|
6839
|
+
return /^\[[^\]]{8,}\]$/.test(text.trim());
|
|
6840
|
+
}
|
|
6841
|
+
function inferHeadingLevel(text, previousBlocks) {
|
|
6842
|
+
if (text.length > 120) {
|
|
6843
|
+
return void 0;
|
|
6844
|
+
}
|
|
6845
|
+
if (/^table of contents$/i.test(text)) {
|
|
6846
|
+
return 1;
|
|
6847
|
+
}
|
|
6848
|
+
if (/^(?:introduction|word styles|references|appendix\b.*|acknowledgments|revision history|notices)$/i.test(text)) {
|
|
6849
|
+
return 1;
|
|
6850
|
+
}
|
|
6851
|
+
const numbered = text.match(/^([1-9](?:\.\d+)*)\s+.+/);
|
|
6852
|
+
if (numbered) {
|
|
6853
|
+
return Math.min(3, numbered[1].split(".").length);
|
|
6854
|
+
}
|
|
6855
|
+
if (/^(?:terminology|overall style|title page|headings|paragraphs|lists|tables|code examples|character styles|normative)$/i.test(text)) {
|
|
6856
|
+
return 2;
|
|
6857
|
+
}
|
|
6858
|
+
const previousHeading = [...previousBlocks].reverse().find((block) => block.type === "heading");
|
|
6859
|
+
if (previousHeading?.type === "heading" && previousHeading.level === 1 && /^[A-Z][A-Za-z0-9 ()/-]{2,80}$/.test(text)) {
|
|
6860
|
+
return 2;
|
|
6861
|
+
}
|
|
6862
|
+
return void 0;
|
|
6863
|
+
}
|
|
6864
|
+
function shouldIndentParagraph(previousBlocks) {
|
|
6865
|
+
for (let index = previousBlocks.length - 1; index >= 0; index -= 1) {
|
|
6866
|
+
const block = previousBlocks[index];
|
|
6867
|
+
if (block.type === "toc" || block.type === "table") {
|
|
6868
|
+
continue;
|
|
6869
|
+
}
|
|
6870
|
+
if (block.type === "label") {
|
|
6871
|
+
return true;
|
|
6872
|
+
}
|
|
6873
|
+
if ((block.type === "paragraph" || block.type === "code") && block.indent) {
|
|
6874
|
+
return true;
|
|
6875
|
+
}
|
|
6876
|
+
return false;
|
|
6877
|
+
}
|
|
6878
|
+
return false;
|
|
6879
|
+
}
|
|
6880
|
+
function inferListItemLevel(text) {
|
|
6881
|
+
if (/^(?:list bullet|definition term)$/i.test(text)) {
|
|
6882
|
+
return 1;
|
|
6883
|
+
}
|
|
6884
|
+
if (/^(?:list bullet 2|list continue 2|definition for the term\.)$/i.test(text)) {
|
|
6885
|
+
return 2;
|
|
6886
|
+
}
|
|
6887
|
+
return void 0;
|
|
6888
|
+
}
|
|
6889
|
+
function parseTocEntry(text) {
|
|
6890
|
+
const tabCells = splitTableRow(text);
|
|
6891
|
+
if (tabCells.length >= 2 && /^\d{1,3}$/.test(tabCells[tabCells.length - 1] || "")) {
|
|
6892
|
+
const title2 = tabCells.slice(0, -1).join(" ").trim();
|
|
6893
|
+
if (isLikelyTocTitle(title2)) {
|
|
6894
|
+
const number2 = title2.match(/^(\d+(?:\.\d+)*)\b/)?.[1] || "";
|
|
6895
|
+
return { type: "toc", title: title2, page: tabCells[tabCells.length - 1], level: number2.includes(".") ? Math.min(3, number2.split(".").length) : 1 };
|
|
6896
|
+
}
|
|
6897
|
+
}
|
|
6898
|
+
const cleaned = text.replace(/\s+/g, " ").trim();
|
|
6899
|
+
const match = cleaned.match(/^(?:(\d+(?:\.\d+)*)\s+)?(.+?)\s+(\d{1,3})$/);
|
|
6900
|
+
if (!match || cleaned.length > 140) {
|
|
6901
|
+
return void 0;
|
|
6902
|
+
}
|
|
6903
|
+
const number = match[1] || "";
|
|
6904
|
+
const title = `${number ? `${number} ` : ""}${match[2] || ""}`.trim();
|
|
6905
|
+
const page = match[3];
|
|
6906
|
+
if (!title || !page || !/^(?:appendix\b|references\b|introduction\b|[A-Z0-9])/i.test(title)) {
|
|
6907
|
+
return void 0;
|
|
6908
|
+
}
|
|
6909
|
+
if (!isLikelyTocTitle(title)) {
|
|
6910
|
+
return void 0;
|
|
6911
|
+
}
|
|
6912
|
+
const level = number.includes(".") ? Math.min(3, number.split(".").length) : 1;
|
|
6913
|
+
return { type: "toc", title, page, level };
|
|
6914
|
+
}
|
|
6915
|
+
function isLikelyTocTitle(title) {
|
|
6916
|
+
return /^(?:appendix\b|references\b|introduction\b|[1-9](?:\.\d+)*\b|[A-Z][\w\s.-]{2,80}$)/i.test(title);
|
|
6917
|
+
}
|
|
6918
|
+
function isTableRowCandidate(text) {
|
|
6919
|
+
if (/^\[[-\w.]+\]\t+/.test(text)) {
|
|
6920
|
+
return false;
|
|
6921
|
+
}
|
|
6922
|
+
const cells = splitTableRow(text);
|
|
6923
|
+
return cells.length >= 2 && cells.some((cell) => cell.length > 0);
|
|
6924
|
+
}
|
|
6925
|
+
function splitTableRow(text) {
|
|
6926
|
+
return text.split(/\t+/).map((cell) => cell.trim()).filter(Boolean);
|
|
6927
|
+
}
|
|
6928
|
+
function normalizeTableRows(cells) {
|
|
6929
|
+
if (cells.length >= 8) {
|
|
6930
|
+
const columnCount = inferTableColumnCount(cells);
|
|
6931
|
+
if (columnCount > 1 && cells.length % columnCount === 0) {
|
|
6932
|
+
const rows = [];
|
|
6933
|
+
for (let offset = 0; offset < cells.length; offset += columnCount) {
|
|
6934
|
+
rows.push(cells.slice(offset, offset + columnCount));
|
|
6935
|
+
}
|
|
6936
|
+
return rows;
|
|
6937
|
+
}
|
|
6938
|
+
}
|
|
6939
|
+
return [cells];
|
|
6940
|
+
}
|
|
6941
|
+
function inferTableColumnCount(cells) {
|
|
6942
|
+
const header = cells.slice(0, 6).join(" ").toLowerCase();
|
|
6943
|
+
if (/\brev\b/.test(header) && /\bdate\b/.test(header) && /whom|what/.test(header)) {
|
|
6944
|
+
return 4;
|
|
6945
|
+
}
|
|
6946
|
+
for (const candidate of [5, 4, 3, 2]) {
|
|
6947
|
+
if (cells.length % candidate === 0) {
|
|
6948
|
+
return candidate;
|
|
6949
|
+
}
|
|
6950
|
+
}
|
|
6951
|
+
return 0;
|
|
6952
|
+
}
|
|
6953
|
+
function isCodeLikeParagraph(text) {
|
|
6954
|
+
return /^\d{24,}$/.test(text.replace(/\s+/g, "")) || /^GET\s+https?:\/\//i.test(text) || /^<other\s+HTTP\b/i.test(text) || /<\/?[A-Za-z][A-Za-z0-9:-]*(?:\s+[^>@]*)?>/.test(text) || /^\s*(?:<\?xml|function\b|const\b|let\b|var\b|if\s*\(|for\s*\(|while\s*\(|\{|\}|\/\/)/.test(text);
|
|
6955
|
+
}
|
|
6956
|
+
function cleanWordText(value) {
|
|
6957
|
+
return stripWordFieldCodes(value).replace(/[\u0001-\u0006\u0008\u000e-\u001f]/g, "").replace(/ {2,}/g, " ").trim();
|
|
6958
|
+
}
|
|
6959
|
+
function stripWordFieldCodes(value) {
|
|
6960
|
+
return value.replace(/\u0013\s*(?:HYPERLINK|PAGEREF|TOC)\b[^\u0014\u0015]*/gi, "").replace(/[\u0013-\u0015]/g, "").replace(/\bHYPERLINK\s+\\l\s+"[^"]*"\s*/gi, "").replace(/\bHYPERLINK\s+"[^"]*"\s*/gi, "").replace(/\bPAGEREF\s+\S+\s+\\h\s*/gi, "").replace(/\bTOC\s+\\o\s+"[^"]*"\s+\\h\s+\\z\s*/gi, "").replace(/\bREF\s+[_A-Za-z0-9-]+\s+(?:\\[A-Za-z]+\s*)+/gi, "");
|
|
6961
|
+
}
|
|
6962
|
+
function isDisplayableParagraph(value) {
|
|
6963
|
+
if (value.length < 2) {
|
|
6964
|
+
return false;
|
|
6965
|
+
}
|
|
6966
|
+
const letters = [...value].filter((char) => /[\p{L}\p{N}]/u.test(char)).length;
|
|
6967
|
+
return letters >= Math.min(2, value.length);
|
|
6968
|
+
}
|
|
6969
|
+
function inferDocumentTitle(paragraphs) {
|
|
6970
|
+
return paragraphs.find((paragraph) => paragraph.length <= 120) || "Word \u6587\u6863";
|
|
6971
|
+
}
|
|
6972
|
+
function appendMeta4(list, label, value) {
|
|
6973
|
+
const term = window.document.createElement("dt");
|
|
6974
|
+
term.textContent = label;
|
|
6975
|
+
const detail = window.document.createElement("dd");
|
|
6976
|
+
detail.textContent = value;
|
|
6977
|
+
list.append(term, detail);
|
|
6978
|
+
}
|
|
6979
|
+
function concatChunks(chunks, size) {
|
|
6980
|
+
const total = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
6981
|
+
const output = new Uint8Array(size === void 0 ? total : Math.min(total, size));
|
|
6982
|
+
let offset = 0;
|
|
6983
|
+
for (const chunk of chunks) {
|
|
6984
|
+
const slice = chunk.subarray(0, Math.min(chunk.length, output.length - offset));
|
|
6985
|
+
output.set(slice, offset);
|
|
6986
|
+
offset += slice.length;
|
|
6987
|
+
if (offset >= output.length) {
|
|
6988
|
+
break;
|
|
6989
|
+
}
|
|
6990
|
+
}
|
|
6991
|
+
return output;
|
|
6992
|
+
}
|
|
6993
|
+
function alignEven(value) {
|
|
6994
|
+
return value % 2 === 0 ? value : value + 1;
|
|
6995
|
+
}
|
|
6996
|
+
function isUsableSector(sector) {
|
|
6997
|
+
return sector !== FREE_SECTOR && sector !== END_OF_CHAIN && sector !== FAT_SECTOR && sector !== DIFAT_SECTOR;
|
|
6998
|
+
}
|
|
6999
|
+
function sectorOffset(sector, sectorSize) {
|
|
7000
|
+
return (sector + 1) * sectorSize;
|
|
7001
|
+
}
|
|
7002
|
+
function normalizeStreamName(name) {
|
|
7003
|
+
return name.replace(/^\/+/, "").toLowerCase();
|
|
7004
|
+
}
|
|
7005
|
+
function decodeUtf16Le(bytes) {
|
|
7006
|
+
return new TextDecoder("utf-16le").decode(bytes);
|
|
7007
|
+
}
|
|
7008
|
+
function decodeWindows1252(bytes) {
|
|
7009
|
+
try {
|
|
7010
|
+
return new TextDecoder("windows-1252").decode(bytes);
|
|
7011
|
+
} catch {
|
|
7012
|
+
return Array.from(bytes, (byte) => String.fromCharCode(byte)).join("");
|
|
7013
|
+
}
|
|
7014
|
+
}
|
|
7015
|
+
function dataView2(bytes) {
|
|
7016
|
+
return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
7017
|
+
}
|
|
7018
|
+
|
|
7019
|
+
// src/plugins/office.ts
|
|
5865
7020
|
var wordExtensions = /* @__PURE__ */ new Set(["docx", "docm", "doc", "dotx", "dotm", "dot", "rtf", "odt", "fodt", "wps"]);
|
|
5866
7021
|
var sheetExtensions = /* @__PURE__ */ new Set(["xlsx", "xls", "xlsm", "xlsb", "xlt", "xltx", "xltm", "csv", "tsv", "ods", "fods", "numbers", "et"]);
|
|
5867
7022
|
var presentationExtensions = /* @__PURE__ */ new Set(["pptx", "pptm", "ppt", "pps", "ppsx", "ppsm", "potx", "potm", "odp", "fodp", "key", "dps"]);
|
|
@@ -5948,13 +7103,13 @@ function officePlugin(options = {}) {
|
|
|
5948
7103
|
if (conversionContext && await shouldUseOfficeConversion(options, conversionContext)) {
|
|
5949
7104
|
delegatedInstance = await renderConvertedOfficePreview(panel, ctx, options, conversionContext);
|
|
5950
7105
|
} else if (packageFormat === "docx" && !fileIsDocx(extension)) {
|
|
5951
|
-
disposeDocxFit = await renderDocx(panel, arrayBuffer);
|
|
7106
|
+
disposeDocxFit = await renderDocx(panel, arrayBuffer, ctx.options.fit);
|
|
5952
7107
|
} else if (packageFormat === "xlsx" && !sheetExtensions.has(extension)) {
|
|
5953
7108
|
await renderSheet(panel, arrayBuffer, "xlsx");
|
|
5954
7109
|
} else if (packageFormat === "pptx" && !["pptx", "pptm", "ppsx", "ppsm", "potx", "potm"].includes(extension)) {
|
|
5955
7110
|
await renderPptx(panel, arrayBuffer);
|
|
5956
7111
|
} else if (fileIsDocx(extension)) {
|
|
5957
|
-
disposeDocxFit = await renderDocx(panel, arrayBuffer);
|
|
7112
|
+
disposeDocxFit = await renderDocx(panel, arrayBuffer, ctx.options.fit);
|
|
5958
7113
|
} else if (extension === "rtf") {
|
|
5959
7114
|
renderPlainDocument(panel, "RTF \u6587\u6863", rtfToText(await readTextFromBuffer(arrayBuffer)));
|
|
5960
7115
|
} else if (extension === "odt") {
|
|
@@ -5963,7 +7118,7 @@ function officePlugin(options = {}) {
|
|
|
5963
7118
|
renderOpenDocumentXml(panel, "FODT \u6587\u6863", await readTextFromBuffer(arrayBuffer));
|
|
5964
7119
|
} else if (extension === "fods") {
|
|
5965
7120
|
renderFlatOds(panel, await readTextFromBuffer(arrayBuffer));
|
|
5966
|
-
} else if (packagedOfficeCandidates.has(extension) && await renderPackagedOfficePreview(panel, arrayBuffer, extension)) {
|
|
7121
|
+
} else if (packagedOfficeCandidates.has(extension) && await renderPackagedOfficePreview(panel, arrayBuffer, extension, ctx.options.fit)) {
|
|
5967
7122
|
} else if (sheetExtensions.has(extension)) {
|
|
5968
7123
|
await renderSheet(panel, arrayBuffer, extension);
|
|
5969
7124
|
} else if (["pptx", "pptm", "ppsx", "ppsm", "potx", "potm"].includes(extension)) {
|
|
@@ -5972,6 +7127,8 @@ function officePlugin(options = {}) {
|
|
|
5972
7127
|
await renderOdp(panel, arrayBuffer);
|
|
5973
7128
|
} else if (extension === "fodp") {
|
|
5974
7129
|
renderOpenDocumentPresentationXml(panel, await readTextFromBuffer(arrayBuffer));
|
|
7130
|
+
} else if (extension === "doc" || extension === "dot") {
|
|
7131
|
+
renderLegacyWordBinary(panel, extension, arrayBuffer);
|
|
5975
7132
|
} else if (isLegacyOfficeBinary(extension)) {
|
|
5976
7133
|
renderLegacyOfficeBinary(panel, extension, arrayBuffer);
|
|
5977
7134
|
} else {
|
|
@@ -6165,7 +7322,7 @@ async function detectPackagedOfficeFormat(arrayBuffer) {
|
|
|
6165
7322
|
}
|
|
6166
7323
|
return void 0;
|
|
6167
7324
|
}
|
|
6168
|
-
async function renderDocx(panel, arrayBuffer) {
|
|
7325
|
+
async function renderDocx(panel, arrayBuffer, fit) {
|
|
6169
7326
|
const content = document.createElement("div");
|
|
6170
7327
|
content.className = "ofv-docx-document";
|
|
6171
7328
|
const styleContainer = document.createElement("div");
|
|
@@ -6204,7 +7361,7 @@ async function renderDocx(panel, arrayBuffer) {
|
|
|
6204
7361
|
return () => void 0;
|
|
6205
7362
|
}
|
|
6206
7363
|
panel.append(content);
|
|
6207
|
-
disposeFit = fitDocxPages(content);
|
|
7364
|
+
disposeFit = fitDocxPages(content, fit);
|
|
6208
7365
|
return () => {
|
|
6209
7366
|
disposeFit?.();
|
|
6210
7367
|
styleContainer.remove();
|
|
@@ -6802,7 +7959,8 @@ function looksLikeDocxTextboxHeading(value) {
|
|
|
6802
7959
|
return text.length > 0 && text.length <= 12 && !/[0-9@.::]/.test(text);
|
|
6803
7960
|
}
|
|
6804
7961
|
async function normalizeDocxLayout(container, arrayBuffer) {
|
|
6805
|
-
const hints = await readDocxLayoutHints(arrayBuffer);
|
|
7962
|
+
const [hints, charts] = await Promise.all([readDocxLayoutHints(arrayBuffer), readDocxCharts(arrayBuffer)]);
|
|
7963
|
+
repairDocxChartPlaceholders(container, charts);
|
|
6806
7964
|
const pages = container.querySelectorAll("section.ofv-docx");
|
|
6807
7965
|
for (const page of pages) {
|
|
6808
7966
|
repairDocxShapeFills(page);
|
|
@@ -6817,6 +7975,75 @@ async function normalizeDocxLayout(container, arrayBuffer) {
|
|
|
6817
7975
|
}
|
|
6818
7976
|
}
|
|
6819
7977
|
}
|
|
7978
|
+
async function readDocxCharts(arrayBuffer) {
|
|
7979
|
+
try {
|
|
7980
|
+
const zip = await import_jszip3.default.loadAsync(arrayBuffer);
|
|
7981
|
+
const documentXml = await zip.file("word/document.xml")?.async("text");
|
|
7982
|
+
const documentDoc = documentXml ? parseOfficeXml(documentXml) : void 0;
|
|
7983
|
+
if (!documentDoc) {
|
|
7984
|
+
return [];
|
|
7985
|
+
}
|
|
7986
|
+
const relationships = await readOfficeRelationships(zip, "word/document.xml");
|
|
7987
|
+
const chartDrawings = Array.from(documentDoc.getElementsByTagName("*")).filter((element) => element.localName === "inline" || element.localName === "anchor").map((element) => readDocxChartDrawing(element)).filter((item) => Boolean(item?.relationshipId));
|
|
7988
|
+
const charts = [];
|
|
7989
|
+
for (const [index, drawing] of chartDrawings.entries()) {
|
|
7990
|
+
const chartRel = relationships.find((rel) => rel.id === drawing.relationshipId && /\/chart$/i.test(rel.type));
|
|
7991
|
+
const chartPath = resolveOfficeRelationshipTarget("word/document.xml", chartRel?.target);
|
|
7992
|
+
const chartXml = chartPath ? await zip.file(chartPath)?.async("text") : void 0;
|
|
7993
|
+
const chart = chartXml ? parseChartXml(chartXml, chartPath?.split("/").pop() || `chart${index + 1}.xml`) : null;
|
|
7994
|
+
if (chart) {
|
|
7995
|
+
charts.push({ ...chart, widthPt: drawing.widthPt, heightPt: drawing.heightPt });
|
|
7996
|
+
}
|
|
7997
|
+
}
|
|
7998
|
+
return charts;
|
|
7999
|
+
} catch {
|
|
8000
|
+
return [];
|
|
8001
|
+
}
|
|
8002
|
+
}
|
|
8003
|
+
function readDocxChartDrawing(element) {
|
|
8004
|
+
const chart = Array.from(element.getElementsByTagName("*")).find((child) => child.localName === "chart");
|
|
8005
|
+
const relationshipId = chart ? getXmlAttribute3(chart, "id") : "";
|
|
8006
|
+
if (!relationshipId) {
|
|
8007
|
+
return void 0;
|
|
8008
|
+
}
|
|
8009
|
+
const extent = Array.from(element.children).find((child) => child.localName === "extent");
|
|
8010
|
+
return {
|
|
8011
|
+
relationshipId,
|
|
8012
|
+
widthPt: emuToPt(Number(extent?.getAttribute("cx") || 0)),
|
|
8013
|
+
heightPt: emuToPt(Number(extent?.getAttribute("cy") || 0))
|
|
8014
|
+
};
|
|
8015
|
+
}
|
|
8016
|
+
function repairDocxChartPlaceholders(container, charts) {
|
|
8017
|
+
if (charts.length === 0) {
|
|
8018
|
+
return;
|
|
8019
|
+
}
|
|
8020
|
+
const placeholders = Array.from(container.querySelectorAll("section.ofv-docx div")).filter(isDocxChartPlaceholder);
|
|
8021
|
+
if (placeholders.length !== charts.length) {
|
|
8022
|
+
return;
|
|
8023
|
+
}
|
|
8024
|
+
placeholders.forEach((placeholder, index) => {
|
|
8025
|
+
const chart = charts[index];
|
|
8026
|
+
placeholder.classList.add("ofv-docx-chart-preview");
|
|
8027
|
+
placeholder.dataset.ofvDocxChartPreview = "true";
|
|
8028
|
+
if (chart.widthPt > 0) {
|
|
8029
|
+
placeholder.style.width ||= `${formatCssNumber(chart.widthPt)}pt`;
|
|
8030
|
+
}
|
|
8031
|
+
if (chart.heightPt > 0) {
|
|
8032
|
+
placeholder.style.height ||= `${formatCssNumber(chart.heightPt)}pt`;
|
|
8033
|
+
}
|
|
8034
|
+
placeholder.replaceChildren(renderChartSvg(chart));
|
|
8035
|
+
});
|
|
8036
|
+
}
|
|
8037
|
+
function isDocxChartPlaceholder(element) {
|
|
8038
|
+
if (element.dataset.ofvDocxChartPreview === "true" || element.children.length > 0 || normalizePreviewText(element.textContent || "")) {
|
|
8039
|
+
return false;
|
|
8040
|
+
}
|
|
8041
|
+
const display = element.style.display;
|
|
8042
|
+
const position = element.style.position;
|
|
8043
|
+
const width = parseCssPixelValue(element.style.width);
|
|
8044
|
+
const height = parseCssPixelValue(element.style.height);
|
|
8045
|
+
return display === "inline-block" && position === "relative" && width >= 120 && height >= 80;
|
|
8046
|
+
}
|
|
6820
8047
|
function repairDocxHeadingShapeAlignment(page) {
|
|
6821
8048
|
for (const paragraph of page.querySelectorAll("p")) {
|
|
6822
8049
|
const text = normalizePreviewText(paragraph.textContent || "");
|
|
@@ -6967,14 +8194,19 @@ function repairDocxShapeFills(page) {
|
|
|
6967
8194
|
}
|
|
6968
8195
|
}
|
|
6969
8196
|
function repairDocxFloatingPictures(page, hints) {
|
|
6970
|
-
const
|
|
6971
|
-
if (
|
|
8197
|
+
const pageHints = hints.floatingPictures.filter((item) => item.relativeFrom === "column" && item.wrap === "square");
|
|
8198
|
+
if (pageHints.length === 0) {
|
|
6972
8199
|
return;
|
|
6973
8200
|
}
|
|
6974
|
-
const
|
|
6975
|
-
|
|
8201
|
+
const images = Array.from(page.querySelectorAll("img")).filter(
|
|
8202
|
+
(image) => image.closest("[data-ofv-docx-float-repaired='true']") === null
|
|
8203
|
+
);
|
|
8204
|
+
if (images.length === 0 || images.length !== pageHints.length) {
|
|
6976
8205
|
return;
|
|
6977
8206
|
}
|
|
8207
|
+
images.forEach((image, index) => repairDocxFloatingPicture(page, image, pageHints[index]));
|
|
8208
|
+
}
|
|
8209
|
+
function repairDocxFloatingPicture(page, image, hint) {
|
|
6978
8210
|
const wrapper = image.parentElement;
|
|
6979
8211
|
if (!wrapper || wrapper.dataset.ofvDocxFloatRepaired === "true") {
|
|
6980
8212
|
return;
|
|
@@ -7020,7 +8252,7 @@ function parseCssLineHeight(value) {
|
|
|
7020
8252
|
const parsed = Number.parseFloat(trimmed);
|
|
7021
8253
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
7022
8254
|
}
|
|
7023
|
-
function fitDocxPages(container) {
|
|
8255
|
+
function fitDocxPages(container, fit) {
|
|
7024
8256
|
const wrapper = container.querySelector(".ofv-docx-wrapper");
|
|
7025
8257
|
if (!wrapper) {
|
|
7026
8258
|
return () => void 0;
|
|
@@ -7033,6 +8265,7 @@ function fitDocxPages(container) {
|
|
|
7033
8265
|
return;
|
|
7034
8266
|
}
|
|
7035
8267
|
const availableWidth = Math.max(1, container.clientWidth - 48);
|
|
8268
|
+
const availableHeight = container.clientHeight > 0 ? Math.max(1, container.clientHeight - 48) : void 0;
|
|
7036
8269
|
const pageWidth = Math.max(
|
|
7037
8270
|
1,
|
|
7038
8271
|
...frames.map(({ page }) => {
|
|
@@ -7040,14 +8273,24 @@ function fitDocxPages(container) {
|
|
|
7040
8273
|
return page.offsetWidth || rectWidth || parseCssPixelValue(page.style.width) || 794;
|
|
7041
8274
|
})
|
|
7042
8275
|
);
|
|
7043
|
-
const
|
|
8276
|
+
const pageHeight = Math.max(
|
|
8277
|
+
1,
|
|
8278
|
+
...frames.map(({ page }) => page.offsetHeight || page.getBoundingClientRect().height || parseCssPixelValue(page.style.height) || 1123)
|
|
8279
|
+
);
|
|
8280
|
+
const scale = getDocxFitScale(fit, availableWidth, availableHeight, pageWidth, pageHeight);
|
|
7044
8281
|
const userZoom = parseCssPixelValue(panel?.style.getPropertyValue("--ofv-office-zoom") || "1") || 1;
|
|
7045
8282
|
wrapper.style.setProperty("--ofv-docx-scale", formatCssNumber(scale));
|
|
7046
8283
|
wrapper.style.setProperty("--ofv-docx-page-width", `${pageWidth}px`);
|
|
8284
|
+
wrapper.style.width = `${Math.ceil(pageWidth * scale * userZoom + 48)}px`;
|
|
8285
|
+
wrapper.style.maxWidth = "none";
|
|
8286
|
+
wrapper.style.overflow = "visible";
|
|
7047
8287
|
for (const { frame, page } of frames) {
|
|
7048
|
-
const
|
|
7049
|
-
|
|
7050
|
-
|
|
8288
|
+
const framePageHeight = page.offsetHeight || page.getBoundingClientRect().height || parseCssPixelValue(page.style.height);
|
|
8289
|
+
const framePageWidth = page.offsetWidth || page.getBoundingClientRect().width || parseCssPixelValue(page.style.width) || pageWidth;
|
|
8290
|
+
frame.style.width = `${Math.ceil(framePageWidth * scale * userZoom)}px`;
|
|
8291
|
+
frame.style.maxWidth = "none";
|
|
8292
|
+
if (framePageHeight > 0) {
|
|
8293
|
+
frame.style.height = `${Math.ceil(framePageHeight * scale * userZoom)}px`;
|
|
7051
8294
|
}
|
|
7052
8295
|
}
|
|
7053
8296
|
};
|
|
@@ -7072,6 +8315,26 @@ function fitDocxPages(container) {
|
|
|
7072
8315
|
observer.disconnect();
|
|
7073
8316
|
};
|
|
7074
8317
|
}
|
|
8318
|
+
function getDocxFitScale(fit, availableWidth, availableHeight, pageWidth, pageHeight) {
|
|
8319
|
+
const widthScale = availableWidth / pageWidth;
|
|
8320
|
+
const heightScale = availableHeight ? availableHeight / pageHeight : void 0;
|
|
8321
|
+
if (fit === "actual") {
|
|
8322
|
+
return 1;
|
|
8323
|
+
}
|
|
8324
|
+
if (fit === "height") {
|
|
8325
|
+
return Math.max(0.1, heightScale ?? widthScale);
|
|
8326
|
+
}
|
|
8327
|
+
if (fit === "cover") {
|
|
8328
|
+
return Math.max(0.1, Math.max(widthScale, heightScale ?? widthScale));
|
|
8329
|
+
}
|
|
8330
|
+
if (fit === "scale-down") {
|
|
8331
|
+
return Math.min(1, Math.max(0.1, Math.min(widthScale, heightScale ?? widthScale)));
|
|
8332
|
+
}
|
|
8333
|
+
if (fit === "contain") {
|
|
8334
|
+
return Math.max(0.1, Math.min(widthScale, heightScale ?? widthScale));
|
|
8335
|
+
}
|
|
8336
|
+
return Math.max(0.1, widthScale);
|
|
8337
|
+
}
|
|
7075
8338
|
function ensureDocxPageFrames(wrapper) {
|
|
7076
8339
|
const pages = Array.from(wrapper.querySelectorAll("section.ofv-docx"));
|
|
7077
8340
|
return pages.map((page) => {
|
|
@@ -7603,9 +8866,9 @@ function parseChartXml(xml, fallbackName) {
|
|
|
7603
8866
|
return null;
|
|
7604
8867
|
}
|
|
7605
8868
|
const type = detectChartType(doc);
|
|
7606
|
-
const title =
|
|
8869
|
+
const title = readChartTitle(doc, fallbackName);
|
|
7607
8870
|
const seriesElements = Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "ser");
|
|
7608
|
-
const series = seriesElements.map((element, index) => parseChartSeries(element, index)).filter((item) => item.values.length > 0);
|
|
8871
|
+
const series = seriesElements.map((element, index) => ({ ...parseChartSeries(element, index), color: readChartSeriesColor(element) })).filter((item) => item.values.length > 0);
|
|
7609
8872
|
if (series.length === 0) {
|
|
7610
8873
|
return null;
|
|
7611
8874
|
}
|
|
@@ -7614,9 +8877,21 @@ function parseChartXml(xml, fallbackName) {
|
|
|
7614
8877
|
type,
|
|
7615
8878
|
title,
|
|
7616
8879
|
categories: series.find((item) => item.categories.length > 0)?.categories || [],
|
|
7617
|
-
series: series.map((item) => ({ name: item.name, values: item.values }))
|
|
8880
|
+
series: series.map((item) => ({ name: item.name, values: item.values, color: item.color }))
|
|
7618
8881
|
};
|
|
7619
8882
|
}
|
|
8883
|
+
function readChartTitle(doc, fallbackName) {
|
|
8884
|
+
const titleElement = Array.from(doc.getElementsByTagName("*")).find((element) => element.localName === "title");
|
|
8885
|
+
if (!titleElement) {
|
|
8886
|
+
return fallbackName.replace(/\.xml$/i, "");
|
|
8887
|
+
}
|
|
8888
|
+
const explicitTitle = chartText(titleElement);
|
|
8889
|
+
if (explicitTitle) {
|
|
8890
|
+
return explicitTitle;
|
|
8891
|
+
}
|
|
8892
|
+
const language = Array.from(doc.getElementsByTagName("*")).find((element) => element.localName === "lang")?.getAttribute("val") || "";
|
|
8893
|
+
return /^zh\b/i.test(language) ? "\u56FE\u8868\u6807\u9898" : "Chart Title";
|
|
8894
|
+
}
|
|
7620
8895
|
function detectChartType(doc) {
|
|
7621
8896
|
const chartType = Array.from(doc.getElementsByTagName("*")).find(
|
|
7622
8897
|
(element) => element.localName.endsWith("Chart") && element.localName !== "chart"
|
|
@@ -7633,6 +8908,31 @@ function parseChartSeries(element, index) {
|
|
|
7633
8908
|
categories: stringsFromFirst(element, "cat")
|
|
7634
8909
|
};
|
|
7635
8910
|
}
|
|
8911
|
+
function readChartSeriesColor(element) {
|
|
8912
|
+
const shape = Array.from(element?.children || []).find((child) => child.localName === "spPr");
|
|
8913
|
+
const color = Array.from(shape?.getElementsByTagName("*") || []).find(
|
|
8914
|
+
(child) => child.localName === "srgbClr" || child.localName === "schemeClr"
|
|
8915
|
+
);
|
|
8916
|
+
if (!color) {
|
|
8917
|
+
return void 0;
|
|
8918
|
+
}
|
|
8919
|
+
if (color.localName === "srgbClr") {
|
|
8920
|
+
const value = color.getAttribute("val") || "";
|
|
8921
|
+
return /^[\da-f]{6}$/i.test(value) ? `#${value}` : void 0;
|
|
8922
|
+
}
|
|
8923
|
+
return chartSchemeColor(color.getAttribute("val") || "");
|
|
8924
|
+
}
|
|
8925
|
+
function chartSchemeColor(value) {
|
|
8926
|
+
const colors = {
|
|
8927
|
+
accent1: "#156082",
|
|
8928
|
+
accent2: "#e97132",
|
|
8929
|
+
accent3: "#196b24",
|
|
8930
|
+
accent4: "#0f9ed5",
|
|
8931
|
+
accent5: "#a02b93",
|
|
8932
|
+
accent6: "#4ea72e"
|
|
8933
|
+
};
|
|
8934
|
+
return colors[value];
|
|
8935
|
+
}
|
|
7636
8936
|
function renderChartPreviewSection(charts) {
|
|
7637
8937
|
const section = createSection("\u8868\u683C\u56FE\u8868\u9884\u89C8");
|
|
7638
8938
|
const grid = document.createElement("div");
|
|
@@ -7670,17 +8970,39 @@ function renderChartCard(chart) {
|
|
|
7670
8970
|
}
|
|
7671
8971
|
function renderChartSvg(chart) {
|
|
7672
8972
|
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
7673
|
-
svg.setAttribute("viewBox", "0 0 640
|
|
8973
|
+
svg.setAttribute("viewBox", "0 0 640 380");
|
|
7674
8974
|
svg.setAttribute("role", "img");
|
|
7675
8975
|
svg.setAttribute("aria-label", chart.title);
|
|
7676
8976
|
svg.classList.add("ofv-chart-svg");
|
|
7677
8977
|
const allValues = chart.series.flatMap((item) => item.values).filter((value) => Number.isFinite(value));
|
|
7678
8978
|
const max = Math.max(1, ...allValues);
|
|
7679
8979
|
const min = Math.min(0, ...allValues);
|
|
8980
|
+
const axisScale = createChartAxisScale(max, min);
|
|
8981
|
+
const axisMax = axisScale.max;
|
|
8982
|
+
const axisMin = axisScale.min;
|
|
7680
8983
|
const span = max - min || 1;
|
|
7681
|
-
const colors = ["#
|
|
7682
|
-
const plot = { x:
|
|
7683
|
-
|
|
8984
|
+
const colors = ["#156082", "#e97132", "#196b24", "#0f9ed5", "#a02b93", "#4ea72e"];
|
|
8985
|
+
const plot = { x: 74, y: 74, width: 526, height: 214 };
|
|
8986
|
+
const categories = chart.categories.length > 0 ? chart.categories : chart.series[0]?.values.map((_, index) => String(index + 1)) || [];
|
|
8987
|
+
const title = appendSvg(svg, "text", { x: 320, y: 34, class: "ofv-chart-title", "text-anchor": "middle" });
|
|
8988
|
+
title.textContent = chart.title;
|
|
8989
|
+
for (const value of axisScale.ticks) {
|
|
8990
|
+
const y = plot.y + plot.height - (value - axisMin) / (axisMax - axisMin || 1) * plot.height;
|
|
8991
|
+
appendSvg(svg, "line", {
|
|
8992
|
+
x1: plot.x,
|
|
8993
|
+
y1: Number(y.toFixed(1)),
|
|
8994
|
+
x2: plot.x + plot.width,
|
|
8995
|
+
y2: Number(y.toFixed(1)),
|
|
8996
|
+
class: value === 0 ? "ofv-chart-axis" : "ofv-chart-gridline"
|
|
8997
|
+
});
|
|
8998
|
+
const label = appendSvg(svg, "text", {
|
|
8999
|
+
x: plot.x - 12,
|
|
9000
|
+
y: Number((y + 4).toFixed(1)),
|
|
9001
|
+
class: "ofv-chart-label",
|
|
9002
|
+
"text-anchor": "end"
|
|
9003
|
+
});
|
|
9004
|
+
label.textContent = formatChartTick(value);
|
|
9005
|
+
}
|
|
7684
9006
|
appendSvg(svg, "line", {
|
|
7685
9007
|
x1: plot.x,
|
|
7686
9008
|
y1: plot.y + plot.height,
|
|
@@ -7688,28 +9010,47 @@ function renderChartSvg(chart) {
|
|
|
7688
9010
|
y2: plot.y + plot.height,
|
|
7689
9011
|
class: "ofv-chart-axis"
|
|
7690
9012
|
});
|
|
7691
|
-
chart.
|
|
7692
|
-
const
|
|
7693
|
-
const
|
|
7694
|
-
const
|
|
7695
|
-
|
|
7696
|
-
|
|
7697
|
-
|
|
7698
|
-
|
|
7699
|
-
const
|
|
7700
|
-
|
|
7701
|
-
|
|
7702
|
-
|
|
9013
|
+
if (chart.type.includes("bar") || chart.type.includes("col")) {
|
|
9014
|
+
const categoryCount = Math.max(1, categories.length, ...chart.series.map((series) => series.values.length));
|
|
9015
|
+
const groupWidth = plot.width / categoryCount;
|
|
9016
|
+
const clusterWidth = groupWidth * 0.58;
|
|
9017
|
+
const barWidth = Math.max(5, Math.min(28, clusterWidth / Math.max(1, chart.series.length)));
|
|
9018
|
+
const zeroY = plot.y + plot.height - (0 - axisMin) / (axisMax - axisMin || 1) * plot.height;
|
|
9019
|
+
categories.slice(0, categoryCount).forEach((category, index) => {
|
|
9020
|
+
const x = plot.x + groupWidth * (index + 0.5);
|
|
9021
|
+
const label = appendSvg(svg, "text", {
|
|
9022
|
+
x: Number(x.toFixed(1)),
|
|
9023
|
+
y: plot.y + plot.height + 22,
|
|
9024
|
+
class: "ofv-chart-label",
|
|
9025
|
+
"text-anchor": "middle"
|
|
9026
|
+
});
|
|
9027
|
+
label.textContent = truncateChartLabel(category);
|
|
9028
|
+
});
|
|
9029
|
+
chart.series.forEach((series, seriesIndex) => {
|
|
9030
|
+
const color = series.color || colors[seriesIndex % colors.length];
|
|
9031
|
+
series.values.forEach((value, index) => {
|
|
9032
|
+
const groupCenter = plot.x + groupWidth * (index + 0.5);
|
|
9033
|
+
const x = groupCenter - barWidth * chart.series.length / 2 + seriesIndex * barWidth + barWidth * 0.12;
|
|
9034
|
+
const y = plot.y + plot.height - (value - axisMin) / (axisMax - axisMin || 1) * plot.height;
|
|
7703
9035
|
appendSvg(svg, "rect", {
|
|
7704
|
-
x,
|
|
7705
|
-
y: Math.min(
|
|
7706
|
-
width: barWidth,
|
|
7707
|
-
height: Math.max(1, Math.abs(zeroY -
|
|
9036
|
+
x: Number(x.toFixed(1)),
|
|
9037
|
+
y: Number(Math.min(y, zeroY).toFixed(1)),
|
|
9038
|
+
width: Number((barWidth * 0.76).toFixed(1)),
|
|
9039
|
+
height: Number(Math.max(1, Math.abs(zeroY - y)).toFixed(1)),
|
|
7708
9040
|
fill: color,
|
|
7709
9041
|
"data-index": index
|
|
7710
9042
|
});
|
|
7711
9043
|
});
|
|
7712
|
-
}
|
|
9044
|
+
});
|
|
9045
|
+
} else {
|
|
9046
|
+
chart.series.forEach((series, seriesIndex) => {
|
|
9047
|
+
const color = series.color || colors[seriesIndex % colors.length];
|
|
9048
|
+
const step = series.values.length > 1 ? plot.width / (series.values.length - 1) : plot.width;
|
|
9049
|
+
const lineSpan = axisMax - axisMin || span;
|
|
9050
|
+
const points = series.values.map((value, index) => ({
|
|
9051
|
+
x: plot.x + index * step,
|
|
9052
|
+
y: plot.y + plot.height - (value - axisMin) / lineSpan * plot.height
|
|
9053
|
+
}));
|
|
7713
9054
|
appendSvg(svg, "polyline", {
|
|
7714
9055
|
points: points.map((point) => `${point.x.toFixed(1)},${point.y.toFixed(1)}`).join(" "),
|
|
7715
9056
|
fill: "none",
|
|
@@ -7721,11 +9062,49 @@ function renderChartSvg(chart) {
|
|
|
7721
9062
|
for (const point of points.slice(0, 80)) {
|
|
7722
9063
|
appendSvg(svg, "circle", { cx: point.x, cy: point.y, r: 3, fill: color });
|
|
7723
9064
|
}
|
|
7724
|
-
}
|
|
7725
|
-
|
|
7726
|
-
|
|
9065
|
+
});
|
|
9066
|
+
}
|
|
9067
|
+
appendChartLegend(svg, chart, colors, 348);
|
|
7727
9068
|
return svg;
|
|
7728
9069
|
}
|
|
9070
|
+
function appendChartLegend(svg, chart, colors, y) {
|
|
9071
|
+
const itemWidth = 86;
|
|
9072
|
+
const startX = 320 - chart.series.length * itemWidth / 2;
|
|
9073
|
+
chart.series.forEach((series, seriesIndex) => {
|
|
9074
|
+
const color = series.color || colors[seriesIndex % colors.length];
|
|
9075
|
+
appendLegend(svg, series.name, color, startX + seriesIndex * itemWidth, y);
|
|
9076
|
+
});
|
|
9077
|
+
}
|
|
9078
|
+
function createChartAxisScale(max, min) {
|
|
9079
|
+
const axisMin = Math.min(0, min);
|
|
9080
|
+
const positiveMax = Math.max(1, max);
|
|
9081
|
+
const step = niceChartStep((positiveMax - axisMin) / 5);
|
|
9082
|
+
let axisMax = Math.ceil(positiveMax / step) * step;
|
|
9083
|
+
if (axisMax <= positiveMax) {
|
|
9084
|
+
axisMax += step;
|
|
9085
|
+
}
|
|
9086
|
+
const ticks = [];
|
|
9087
|
+
for (let value = axisMin; value <= axisMax + step / 2; value += step) {
|
|
9088
|
+
ticks.push(Number(value.toFixed(6)));
|
|
9089
|
+
}
|
|
9090
|
+
return { min: axisMin, max: axisMax, ticks };
|
|
9091
|
+
}
|
|
9092
|
+
function niceChartStep(rawStep) {
|
|
9093
|
+
if (rawStep <= 0) {
|
|
9094
|
+
return 1;
|
|
9095
|
+
}
|
|
9096
|
+
const magnitude = 10 ** Math.floor(Math.log10(rawStep));
|
|
9097
|
+
const normalized = rawStep / magnitude;
|
|
9098
|
+
const nice = normalized <= 1 ? 1 : normalized <= 2 ? 2 : normalized <= 5 ? 5 : 10;
|
|
9099
|
+
return nice * magnitude;
|
|
9100
|
+
}
|
|
9101
|
+
function formatChartTick(value) {
|
|
9102
|
+
const rounded = Math.abs(value) < 1 ? Number(value.toFixed(1)) : Number(value.toFixed(0));
|
|
9103
|
+
return String(rounded);
|
|
9104
|
+
}
|
|
9105
|
+
function truncateChartLabel(value) {
|
|
9106
|
+
return value.length > 10 ? `${value.slice(0, 10)}...` : value;
|
|
9107
|
+
}
|
|
7729
9108
|
function appendLegend(svg, label, color, x, y) {
|
|
7730
9109
|
appendSvg(svg, "rect", { x, y: y - 10, width: 12, height: 12, rx: 2, fill: color });
|
|
7731
9110
|
const text = appendSvg(svg, "text", { x: x + 18, y, class: "ofv-chart-label" });
|
|
@@ -8459,7 +9838,7 @@ function renderOpenDocumentPresentationXml(panel, xml) {
|
|
|
8459
9838
|
renderPresentationInsight(panel, inspectOpenDocumentPresentation("FODP \u6F14\u793A\u6587\u7A3F", xml, 0));
|
|
8460
9839
|
renderOpenDocumentPresentation(panel, "FODP \u6F14\u793A\u6587\u7A3F", xml, []);
|
|
8461
9840
|
}
|
|
8462
|
-
async function renderPackagedOfficePreview(panel, arrayBuffer, extension) {
|
|
9841
|
+
async function renderPackagedOfficePreview(panel, arrayBuffer, extension, fit) {
|
|
8463
9842
|
let zip;
|
|
8464
9843
|
try {
|
|
8465
9844
|
zip = await import_jszip3.default.loadAsync(arrayBuffer);
|
|
@@ -8470,7 +9849,7 @@ async function renderPackagedOfficePreview(panel, arrayBuffer, extension) {
|
|
|
8470
9849
|
const hasEntry = (path) => entries.some((entry) => entry.name.toLowerCase() === path.toLowerCase());
|
|
8471
9850
|
const contentXml = zip.file(/(^|\/)content\.xml$/i)[0];
|
|
8472
9851
|
if (hasEntry("word/document.xml")) {
|
|
8473
|
-
await renderDocx(panel, arrayBuffer);
|
|
9852
|
+
await renderDocx(panel, arrayBuffer, fit);
|
|
8474
9853
|
return true;
|
|
8475
9854
|
}
|
|
8476
9855
|
if (hasEntry("xl/workbook.xml")) {
|
|
@@ -8905,6 +10284,13 @@ function legacyOfficeFormatLabel(extension) {
|
|
|
8905
10284
|
}
|
|
8906
10285
|
return "PowerPoint Binary File Format";
|
|
8907
10286
|
}
|
|
10287
|
+
function renderLegacyWordBinary(panel, extension, arrayBuffer) {
|
|
10288
|
+
try {
|
|
10289
|
+
renderLegacyWordDocument(panel, parseLegacyWordDocument(arrayBuffer));
|
|
10290
|
+
} catch (error) {
|
|
10291
|
+
renderLegacyOfficeBinary(panel, extension, arrayBuffer, `Word \u4E8C\u8FDB\u5236\u89E3\u6790\u5931\u8D25\uFF1A${normalizeOfficeError(error)}`);
|
|
10292
|
+
}
|
|
10293
|
+
}
|
|
8908
10294
|
function renderLegacyOfficeBinary(panel, extension, arrayBuffer, parseError) {
|
|
8909
10295
|
const fragments = extractLegacyOfficeText(arrayBuffer);
|
|
8910
10296
|
panel.replaceChildren();
|
|
@@ -13480,10 +14866,10 @@ function renderStep(panel, text, extension, ctx) {
|
|
|
13480
14866
|
note.textContent = "\u5F53\u524D\u7248\u672C\u63D0\u53D6 STEP \u6587\u672C\u5B9E\u4F53\u3001\u7C7B\u578B\u7EDF\u8BA1\u548C\u5173\u952E\u51E0\u4F55\u53C2\u6570\u3002\u7CBE\u786E B-Rep/\u66F2\u9762\u6E32\u67D3\u5EFA\u8BAE\u540E\u7EED\u63A5\u5165 CAD \u5185\u6838\u6216\u670D\u52A1\u7AEF\u8F6C\u6362\u3002";
|
|
13481
14867
|
const meta = document.createElement("div");
|
|
13482
14868
|
meta.className = "ofv-cad-summary";
|
|
13483
|
-
|
|
13484
|
-
|
|
13485
|
-
|
|
13486
|
-
|
|
14869
|
+
appendMeta6(meta, "\u5B9E\u4F53", records.length);
|
|
14870
|
+
appendMeta6(meta, "\u7C7B\u578B", typeCounts.size);
|
|
14871
|
+
appendMeta6(meta, "\u70B9", typeCounts.get("CARTESIAN_POINT") || 0);
|
|
14872
|
+
appendMeta6(meta, "\u65B9\u5411", typeCounts.get("DIRECTION") || 0);
|
|
13487
14873
|
const typeList = createCadTypeList(typeCounts);
|
|
13488
14874
|
section.append(note, meta, typeList);
|
|
13489
14875
|
let viewer;
|
|
@@ -13518,10 +14904,10 @@ function renderIges(panel, text, extension, ctx) {
|
|
|
13518
14904
|
note.textContent = "\u5F53\u524D\u7248\u672C\u63D0\u53D6 IGES \u53C2\u6570\u533A\u5B9E\u4F53\u3001\u7C7B\u578B\u7EDF\u8BA1\u548C\u57FA\u7840\u53C2\u6570\u3002\u7CBE\u786E\u66F2\u7EBF/\u66F2\u9762\u6E32\u67D3\u5EFA\u8BAE\u540E\u7EED\u63A5\u5165 CAD \u5185\u6838\u6216\u670D\u52A1\u7AEF\u8F6C\u6362\u3002";
|
|
13519
14905
|
const meta = document.createElement("div");
|
|
13520
14906
|
meta.className = "ofv-cad-summary";
|
|
13521
|
-
|
|
13522
|
-
|
|
13523
|
-
|
|
13524
|
-
|
|
14907
|
+
appendMeta6(meta, "\u5B9E\u4F53", records.length);
|
|
14908
|
+
appendMeta6(meta, "\u7C7B\u578B", typeCounts.size);
|
|
14909
|
+
appendMeta6(meta, "\u70B9\u5B9E\u4F53", typeCounts.get("116") || 0);
|
|
14910
|
+
appendMeta6(meta, "\u7EBF\u5B9E\u4F53", typeCounts.get("110") || 0);
|
|
13525
14911
|
const typeList = createCadTypeList(typeCounts, "\u7C7B\u578B\u53F7\u7EDF\u8BA1");
|
|
13526
14912
|
section.append(note, meta, typeList);
|
|
13527
14913
|
let viewer;
|
|
@@ -13555,13 +14941,13 @@ function renderIfc(panel, text) {
|
|
|
13555
14941
|
note.textContent = "\u5F53\u524D\u7248\u672C\u63D0\u53D6 IFC STEP \u5B9E\u4F53\u3001BIM \u5C42\u7EA7\u548C\u5E38\u89C1\u6784\u4EF6\u7EDF\u8BA1\u3002\u51E0\u4F55\u7F51\u683C\u3001\u6750\u8D28\u548C\u5C5E\u6027\u96C6\u53EF\u540E\u7EED\u63A5\u5165 IfcOpenShell/IFC.js \u589E\u5F3A\u3002";
|
|
13556
14942
|
const meta = document.createElement("div");
|
|
13557
14943
|
meta.className = "ofv-cad-summary";
|
|
13558
|
-
|
|
13559
|
-
|
|
13560
|
-
|
|
13561
|
-
|
|
13562
|
-
|
|
13563
|
-
|
|
13564
|
-
|
|
14944
|
+
appendMeta6(meta, "\u5B9E\u4F53", records.length);
|
|
14945
|
+
appendMeta6(meta, "\u7C7B\u578B", typeCounts.size);
|
|
14946
|
+
appendMeta6(meta, "\u9879\u76EE", typeCounts.get("IFCPROJECT") || 0);
|
|
14947
|
+
appendMeta6(meta, "\u5EFA\u7B51", typeCounts.get("IFCBUILDING") || 0);
|
|
14948
|
+
appendMeta6(meta, "\u697C\u5C42", typeCounts.get("IFCBUILDINGSTOREY") || 0);
|
|
14949
|
+
appendMeta6(meta, "\u7A7A\u95F4", typeCounts.get("IFCSPACE") || 0);
|
|
14950
|
+
appendMeta6(meta, "\u6784\u4EF6", countIfcElements(typeCounts));
|
|
13565
14951
|
section.append(note, meta, createCadTypeList(typeCounts, "IFC \u5B9E\u4F53\u7EDF\u8BA1"));
|
|
13566
14952
|
const hierarchy = createIfcHierarchy(records);
|
|
13567
14953
|
if (hierarchy) {
|
|
@@ -13586,10 +14972,10 @@ function renderAcisSat(panel, text, extension, ctx) {
|
|
|
13586
14972
|
note.textContent = "\u5F53\u524D\u7248\u672C\u4F1A\u5728\u524D\u7AEF\u89E3\u6790 ACIS SAT \u6587\u672C\u5B9E\u4F53\u3001\u7C7B\u578B\u7EDF\u8BA1\u548C\u5E38\u89C1 vertex/straight-curve \u51E0\u4F55\u7EBF\u7D22\uFF1B\u7CBE\u786E\u66F2\u9762\u3001\u62D3\u6251\u548C\u5E03\u5C14\u4F53\u4ECD\u5EFA\u8BAE\u63A5\u5165 CAD \u5185\u6838\u589E\u5F3A\u3002";
|
|
13587
14973
|
const meta = document.createElement("div");
|
|
13588
14974
|
meta.className = "ofv-cad-summary";
|
|
13589
|
-
|
|
13590
|
-
|
|
13591
|
-
|
|
13592
|
-
|
|
14975
|
+
appendMeta6(meta, "\u5B9E\u4F53", records.length);
|
|
14976
|
+
appendMeta6(meta, "\u7C7B\u578B", typeCounts.size);
|
|
14977
|
+
appendMeta6(meta, "\u9876\u70B9", typeCounts.get("vertex") || 0);
|
|
14978
|
+
appendMeta6(meta, "\u76F4\u7EBF", typeCounts.get("straight-curve") || 0);
|
|
13593
14979
|
const typeList = createCadTypeList(typeCounts);
|
|
13594
14980
|
section.append(note, meta, typeList);
|
|
13595
14981
|
let viewer;
|
|
@@ -13626,10 +15012,10 @@ function renderParasolidText(panel, text, extension, ctx) {
|
|
|
13626
15012
|
note.textContent = "\u5F53\u524D\u7248\u672C\u4F1A\u5728\u524D\u7AEF\u89E3\u6790 Parasolid x_t \u6587\u672C\u7247\u6BB5\u3001\u5B9E\u4F53\u7C7B\u578B\u3001\u5750\u6807\u70B9\u548C\u57FA\u7840\u7EBF\u6BB5\u7EBF\u7D22\uFF1B\u5B8C\u6574 B-Rep\u3001\u66F2\u9762\u548C\u88C5\u914D\u5173\u7CFB\u5EFA\u8BAE\u63A5\u5165 Parasolid/HOOPS/ODA \u7B49\u4E13\u4E1A\u5185\u6838\u3002";
|
|
13627
15013
|
const meta = document.createElement("div");
|
|
13628
15014
|
meta.className = "ofv-cad-summary";
|
|
13629
|
-
|
|
13630
|
-
|
|
13631
|
-
|
|
13632
|
-
|
|
15015
|
+
appendMeta6(meta, "\u5B9E\u4F53", records.length);
|
|
15016
|
+
appendMeta6(meta, "\u7C7B\u578B", typeCounts.size);
|
|
15017
|
+
appendMeta6(meta, "\u70B9", typeCounts.get("point") || typeCounts.get("vertex") || 0);
|
|
15018
|
+
appendMeta6(meta, "\u66F2\u7EBF", (typeCounts.get("line") || 0) + (typeCounts.get("curve") || 0));
|
|
13633
15019
|
const typeList = createCadTypeList(typeCounts);
|
|
13634
15020
|
section.append(note, meta, typeList);
|
|
13635
15021
|
let viewer;
|
|
@@ -13737,23 +15123,23 @@ function renderLayoutPreview(panel, data, ctx) {
|
|
|
13737
15123
|
summary.setAttribute("aria-hidden", "true");
|
|
13738
15124
|
summary.style.display = "none";
|
|
13739
15125
|
}
|
|
13740
|
-
|
|
13741
|
-
|
|
15126
|
+
appendMeta6(summary, "\u6587\u4EF6", data.fileName);
|
|
15127
|
+
appendMeta6(summary, "\u683C\u5F0F", data.format);
|
|
13742
15128
|
if (data.libraryName) {
|
|
13743
|
-
|
|
15129
|
+
appendMeta6(summary, "\u5E93", data.libraryName);
|
|
13744
15130
|
}
|
|
13745
15131
|
if (data.version) {
|
|
13746
|
-
|
|
15132
|
+
appendMeta6(summary, "\u7248\u672C", data.version);
|
|
13747
15133
|
}
|
|
13748
15134
|
if (data.unit) {
|
|
13749
|
-
|
|
15135
|
+
appendMeta6(summary, "\u5355\u4F4D", data.unit);
|
|
13750
15136
|
}
|
|
13751
|
-
|
|
13752
|
-
|
|
13753
|
-
|
|
13754
|
-
|
|
15137
|
+
appendMeta6(summary, "Cell", data.cells.length);
|
|
15138
|
+
appendMeta6(summary, "\u51E0\u4F55", data.shapes.length);
|
|
15139
|
+
appendMeta6(summary, "\u5F15\u7528", data.references.length);
|
|
15140
|
+
appendMeta6(summary, "\u6587\u5B57", data.labels.length);
|
|
13755
15141
|
for (const [label, value] of data.metadata) {
|
|
13756
|
-
|
|
15142
|
+
appendMeta6(summary, label, value);
|
|
13757
15143
|
}
|
|
13758
15144
|
section.append(summary);
|
|
13759
15145
|
for (const noteText of [...data.notes, ...data.warnings]) {
|
|
@@ -14359,12 +15745,12 @@ function renderBinaryCad(panel, bytes, extension, fileName) {
|
|
|
14359
15745
|
note.textContent = extension === "dwg" ? "\u5DF2\u8BC6\u522B DWG \u4E13\u6709\u4E8C\u8FDB\u5236\u56FE\u7EB8\u3002\u6838\u5FC3\u63D2\u4EF6\u9ED8\u8BA4\u63D0\u4F9B\u7248\u672C\u3001\u5BB9\u5668\u3001\u7ED3\u6784\u7EBF\u7D22\u548C\u63A5\u5165\u5EFA\u8BAE\uFF1B\u771F\u5B9E\u51E0\u4F55\u6E32\u67D3\u53EF\u901A\u8FC7 cadPlugin({ binaryRenderer }) \u63A5\u5165\u53EF\u9009\u524D\u7AEF\u5F15\u64CE\u6216\u8F6C\u6362\u670D\u52A1\u3002" : "\u5DF2\u8BC6\u522B DWF \u53D1\u5E03\u56FE\u7EB8\u3002\u6838\u5FC3\u63D2\u4EF6\u9ED8\u8BA4\u63D0\u4F9B\u5BB9\u5668\u7EBF\u7D22\u548C\u63A5\u5165\u5EFA\u8BAE\uFF1B\u9AD8\u4FDD\u771F\u9875\u9762\u6E32\u67D3\u53EF\u901A\u8FC7 cadPlugin({ binaryRenderer }) \u63A5\u5165\u4E13\u7528\u89E3\u6790\u5668\u6216\u8F6C\u6362\u670D\u52A1\u3002";
|
|
14360
15746
|
const meta = document.createElement("div");
|
|
14361
15747
|
meta.className = "ofv-cad-summary";
|
|
14362
|
-
|
|
14363
|
-
|
|
14364
|
-
|
|
14365
|
-
|
|
14366
|
-
|
|
14367
|
-
|
|
15748
|
+
appendMeta6(meta, "\u6587\u4EF6", fileName);
|
|
15749
|
+
appendMeta6(meta, "\u683C\u5F0F", extension.toUpperCase());
|
|
15750
|
+
appendMeta6(meta, "\u5927\u5C0F", formatBytes3(bytes.byteLength));
|
|
15751
|
+
appendMeta6(meta, "\u7B7E\u540D", byteSignature2(bytes));
|
|
15752
|
+
appendMeta6(meta, "\u7248\u672C", detectCadVersion(bytes, extension));
|
|
15753
|
+
appendMeta6(meta, "\u5BB9\u5668", detectCadContainer(bytes));
|
|
14368
15754
|
const actions = document.createElement("div");
|
|
14369
15755
|
actions.className = "ofv-cad-conversion";
|
|
14370
15756
|
const actionTitle = document.createElement("h4");
|
|
@@ -14404,12 +15790,12 @@ function createBinaryCadProbe(bytes, extension) {
|
|
|
14404
15790
|
summary.textContent = "\u4E8C\u8FDB\u5236\u7ED3\u6784\u63A2\u6D4B";
|
|
14405
15791
|
const meta = document.createElement("div");
|
|
14406
15792
|
meta.className = "ofv-archive-probe-meta";
|
|
14407
|
-
|
|
14408
|
-
|
|
14409
|
-
|
|
14410
|
-
|
|
14411
|
-
|
|
14412
|
-
|
|
15793
|
+
appendMeta6(meta, "\u53EF\u8BFB\u7247\u6BB5", probe.tokens.length);
|
|
15794
|
+
appendMeta6(meta, "\u5B9E\u4F53\u5173\u952E\u8BCD", formatCadKeywordCounts(probe.entityCounts));
|
|
15795
|
+
appendMeta6(meta, "\u56FE\u5C42\u7EBF\u7D22", String(probe.layerHints.length));
|
|
15796
|
+
appendMeta6(meta, "\u5757/\u5F15\u7528\u7EBF\u7D22", String(probe.blockHints.length));
|
|
15797
|
+
appendMeta6(meta, "\u5916\u90E8\u5F15\u7528", String(probe.externalRefs.length));
|
|
15798
|
+
appendMeta6(meta, "\u89E3\u6790\u7EA7\u522B", extension === "dwg" ? "\u542F\u53D1\u5F0F\u626B\u63CF" : "\u5BB9\u5668/\u6587\u672C\u626B\u63CF");
|
|
14413
15799
|
details.append(summary, meta);
|
|
14414
15800
|
const hints = [...probe.layerHints, ...probe.blockHints, ...probe.externalRefs].slice(0, 18);
|
|
14415
15801
|
if (hints.length > 0) {
|
|
@@ -14942,9 +16328,9 @@ function createUnsupportedCadSection(extension, fileName) {
|
|
|
14942
16328
|
const section = createSection("CAD \u589E\u5F3A\u63A5\u5165\u63D0\u793A");
|
|
14943
16329
|
const meta = document.createElement("div");
|
|
14944
16330
|
meta.className = "ofv-cad-summary";
|
|
14945
|
-
|
|
14946
|
-
|
|
14947
|
-
|
|
16331
|
+
appendMeta6(meta, "\u6587\u4EF6", fileName);
|
|
16332
|
+
appendMeta6(meta, "\u683C\u5F0F", `.${extension || "cad"}`);
|
|
16333
|
+
appendMeta6(meta, "\u5185\u7F6E\u80FD\u529B", unsupportedCadBuiltInLevel(extension));
|
|
14948
16334
|
const note = document.createElement("p");
|
|
14949
16335
|
note.textContent = unsupportedCadGuidance(extension);
|
|
14950
16336
|
const actions = document.createElement("div");
|
|
@@ -15057,7 +16443,7 @@ function countBy(values) {
|
|
|
15057
16443
|
}
|
|
15058
16444
|
return counts;
|
|
15059
16445
|
}
|
|
15060
|
-
function
|
|
16446
|
+
function appendMeta6(parent, label, value) {
|
|
15061
16447
|
const row = document.createElement("div");
|
|
15062
16448
|
row.className = "ofv-meta-row";
|
|
15063
16449
|
const key = document.createElement("span");
|
|
@@ -17091,10 +18477,10 @@ function read255UInt16(bytes, offset) {
|
|
|
17091
18477
|
return { value: code, offset };
|
|
17092
18478
|
}
|
|
17093
18479
|
function decodeEotString(bytes) {
|
|
17094
|
-
const text =
|
|
18480
|
+
const text = decodeUtf16Le2(bytes).replace(/\0+$/g, "").trim();
|
|
17095
18481
|
return text || new TextDecoder("latin1").decode(bytes).replace(/\0+$/g, "").trim();
|
|
17096
18482
|
}
|
|
17097
|
-
function
|
|
18483
|
+
function decodeUtf16Le2(bytes) {
|
|
17098
18484
|
let value = "";
|
|
17099
18485
|
for (let index = 0; index + 1 < bytes.length; index += 2) {
|
|
17100
18486
|
value += String.fromCharCode(bytes[index] | bytes[index + 1] << 8);
|