@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.js
CHANGED
|
@@ -1877,12 +1877,12 @@ function imagePlugin() {
|
|
|
1877
1877
|
let url = "";
|
|
1878
1878
|
let convertedBlob = null;
|
|
1879
1879
|
let isExternal = Boolean(ctx.file.url);
|
|
1880
|
-
let
|
|
1880
|
+
let tiffSource = null;
|
|
1881
1881
|
if (isTiff) {
|
|
1882
1882
|
ctx.setLoading(true);
|
|
1883
1883
|
try {
|
|
1884
1884
|
const bytes = await sourceBytesPromise;
|
|
1885
|
-
|
|
1885
|
+
tiffSource = await createTiffPreview(bytes, ctx.file.name);
|
|
1886
1886
|
} catch (err) {
|
|
1887
1887
|
console.error("TIFF image conversion failed:", err);
|
|
1888
1888
|
url = createObjectUrl(ctx.file);
|
|
@@ -1942,12 +1942,15 @@ function imagePlugin() {
|
|
|
1942
1942
|
if (url) {
|
|
1943
1943
|
image.src = url;
|
|
1944
1944
|
}
|
|
1945
|
-
const visual =
|
|
1946
|
-
if (
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1945
|
+
const visual = tiffSource || image;
|
|
1946
|
+
if (tiffSource) {
|
|
1947
|
+
tiffSource.classList.add("ofv-image-content");
|
|
1948
|
+
if (tiffSource.classList.contains("ofv-tiff-pages")) {
|
|
1949
|
+
stage.classList.add("ofv-image-stage-pages");
|
|
1950
|
+
}
|
|
1950
1951
|
}
|
|
1952
|
+
const visualBox = document.createElement("div");
|
|
1953
|
+
visualBox.className = "ofv-image-scrollbox";
|
|
1951
1954
|
let scale = getInitialZoom(ctx);
|
|
1952
1955
|
let rotation = 0;
|
|
1953
1956
|
let offsetX = 0;
|
|
@@ -1961,7 +1964,23 @@ function imagePlugin() {
|
|
|
1961
1964
|
let previewAvailable = true;
|
|
1962
1965
|
const zoomLabel = document.createElement("span");
|
|
1963
1966
|
zoomLabel.className = "ofv-image-zoom";
|
|
1967
|
+
const updateScrollBox = () => {
|
|
1968
|
+
if (visual.classList.contains("ofv-tiff-pages")) {
|
|
1969
|
+
visualBox.style.removeProperty("width");
|
|
1970
|
+
visualBox.style.removeProperty("height");
|
|
1971
|
+
return;
|
|
1972
|
+
}
|
|
1973
|
+
const baseWidth = visual.offsetWidth || visual.getBoundingClientRect().width || visual.naturalWidth || 0;
|
|
1974
|
+
const baseHeight = visual.offsetHeight || visual.getBoundingClientRect().height || visual.naturalHeight || 0;
|
|
1975
|
+
if (baseWidth > 0) {
|
|
1976
|
+
visualBox.style.width = `${Math.ceil(baseWidth * scale)}px`;
|
|
1977
|
+
}
|
|
1978
|
+
if (baseHeight > 0) {
|
|
1979
|
+
visualBox.style.height = `${Math.ceil(baseHeight * scale)}px`;
|
|
1980
|
+
}
|
|
1981
|
+
};
|
|
1964
1982
|
const updateTransform = () => {
|
|
1983
|
+
updateScrollBox();
|
|
1965
1984
|
visual.style.transform = `translate(${offsetX}px, ${offsetY}px) scale(${scale}) rotate(${rotation}deg)`;
|
|
1966
1985
|
zoomLabel.textContent = `${Math.round(scale * 100)}%`;
|
|
1967
1986
|
ctx.toolbar?.setZoom(previewAvailable ? scale : void 0);
|
|
@@ -2081,11 +2100,13 @@ function imagePlugin() {
|
|
|
2081
2100
|
stage.addEventListener("lostpointercapture", onLostPointerCapture);
|
|
2082
2101
|
stage.addEventListener("pointerleave", onPointerLeave);
|
|
2083
2102
|
stage.addEventListener("wheel", onWheel, { passive: false });
|
|
2084
|
-
if (!
|
|
2103
|
+
if (!tiffSource) {
|
|
2085
2104
|
image.addEventListener("error", showImageFallback);
|
|
2105
|
+
image.addEventListener("load", updateScrollBox);
|
|
2086
2106
|
}
|
|
2087
2107
|
window.addEventListener("blur", onWindowBlur);
|
|
2088
|
-
|
|
2108
|
+
visualBox.append(visual);
|
|
2109
|
+
stage.append(visualBox);
|
|
2089
2110
|
wrapper.append(...showInlineControls ? [controls, stage, infoBar] : [stage, infoBar]);
|
|
2090
2111
|
ctx.viewport.append(wrapper);
|
|
2091
2112
|
updateTransform();
|
|
@@ -2123,7 +2144,12 @@ function imagePlugin() {
|
|
|
2123
2144
|
},
|
|
2124
2145
|
resize(size) {
|
|
2125
2146
|
visual.style.maxWidth = `${size.width}px`;
|
|
2126
|
-
visual.
|
|
2147
|
+
if (visual.classList.contains("ofv-tiff-pages")) {
|
|
2148
|
+
visual.style.removeProperty("max-height");
|
|
2149
|
+
} else {
|
|
2150
|
+
visual.style.maxHeight = `${Math.max(0, size.height - controls.offsetHeight)}px`;
|
|
2151
|
+
}
|
|
2152
|
+
updateScrollBox();
|
|
2127
2153
|
},
|
|
2128
2154
|
destroy() {
|
|
2129
2155
|
ctx.toolbar?.setZoom(void 0);
|
|
@@ -2138,6 +2164,7 @@ function imagePlugin() {
|
|
|
2138
2164
|
stage.removeEventListener("pointerleave", onPointerLeave);
|
|
2139
2165
|
stage.removeEventListener("wheel", onWheel);
|
|
2140
2166
|
image.removeEventListener("error", showImageFallback);
|
|
2167
|
+
image.removeEventListener("load", updateScrollBox);
|
|
2141
2168
|
window.removeEventListener("blur", onWindowBlur);
|
|
2142
2169
|
finishDrag();
|
|
2143
2170
|
wrapper.remove();
|
|
@@ -2151,33 +2178,64 @@ function imagePlugin() {
|
|
|
2151
2178
|
}
|
|
2152
2179
|
};
|
|
2153
2180
|
}
|
|
2154
|
-
async function
|
|
2181
|
+
async function createTiffPreview(bytes, fileName) {
|
|
2155
2182
|
if (bytes.byteLength === 0) {
|
|
2156
2183
|
throw new Error("\u65E0\u6CD5\u8BFB\u53D6 TIFF \u6587\u4EF6\u5185\u5BB9\u3002");
|
|
2157
2184
|
}
|
|
2158
2185
|
const UTIF = await import("utif");
|
|
2159
2186
|
const buffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
2160
2187
|
const ifds = UTIF.decode(buffer);
|
|
2161
|
-
const
|
|
2162
|
-
if (
|
|
2188
|
+
const imageIfds = ifds.filter((item) => hasTiffDimensions(item));
|
|
2189
|
+
if (imageIfds.length === 0) {
|
|
2163
2190
|
throw new Error("TIFF \u6587\u4EF6\u6CA1\u6709\u53EF\u89E3\u7801\u7684\u56FE\u50CF\u76EE\u5F55\u3002");
|
|
2164
2191
|
}
|
|
2192
|
+
const pages = imageIfds.map((ifd, index) => createTiffPage(UTIF, buffer, ifd, index, imageIfds.length, fileName));
|
|
2193
|
+
if (pages.length === 1) {
|
|
2194
|
+
return pages[0].canvas;
|
|
2195
|
+
}
|
|
2196
|
+
const container = document.createElement("div");
|
|
2197
|
+
container.className = "ofv-tiff-pages";
|
|
2198
|
+
container.setAttribute("role", "group");
|
|
2199
|
+
container.setAttribute("aria-label", `${fileName}\uFF0C\u5171 ${pages.length} \u9875`);
|
|
2200
|
+
for (const page of pages) {
|
|
2201
|
+
const figure = document.createElement("figure");
|
|
2202
|
+
figure.className = "ofv-tiff-page";
|
|
2203
|
+
const caption = document.createElement("figcaption");
|
|
2204
|
+
caption.textContent = `\u7B2C ${page.index + 1} / ${pages.length} \u9875 \xB7 ${page.width} x ${page.height}px`;
|
|
2205
|
+
figure.append(page.canvas, caption);
|
|
2206
|
+
container.append(figure);
|
|
2207
|
+
}
|
|
2208
|
+
return container;
|
|
2209
|
+
}
|
|
2210
|
+
function createTiffPage(UTIF, buffer, ifd, index, total, fileName) {
|
|
2165
2211
|
UTIF.decodeImage(buffer, ifd);
|
|
2166
2212
|
const rgba = UTIF.toRGBA8(ifd);
|
|
2167
|
-
const width =
|
|
2168
|
-
const height = Number(ifd.height || ifd.t257 || 0);
|
|
2213
|
+
const { width, height } = getTiffDimensions(ifd);
|
|
2169
2214
|
if (!width || !height || rgba.length < width * height * 4) {
|
|
2170
2215
|
throw new Error("TIFF \u56FE\u50CF\u50CF\u7D20\u6570\u636E\u4E0D\u5B8C\u6574\u3002");
|
|
2171
2216
|
}
|
|
2172
2217
|
const canvas = document.createElement("canvas");
|
|
2218
|
+
canvas.className = "ofv-tiff-canvas";
|
|
2173
2219
|
canvas.width = width;
|
|
2174
2220
|
canvas.height = height;
|
|
2221
|
+
canvas.setAttribute("role", "img");
|
|
2222
|
+
canvas.setAttribute("aria-label", total > 1 ? `${fileName} \u7B2C ${index + 1} \u9875` : fileName);
|
|
2175
2223
|
const context = canvas.getContext("2d");
|
|
2176
2224
|
if (!context) {
|
|
2177
2225
|
throw new Error("\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301 Canvas 2D\uFF0C\u65E0\u6CD5\u5C55\u793A TIFF\u3002");
|
|
2178
2226
|
}
|
|
2179
2227
|
context.putImageData(new ImageData(new Uint8ClampedArray(rgba), width, height), 0, 0);
|
|
2180
|
-
return canvas;
|
|
2228
|
+
return { canvas, width, height, index };
|
|
2229
|
+
}
|
|
2230
|
+
function hasTiffDimensions(ifd) {
|
|
2231
|
+
const { width, height } = getTiffDimensions(ifd);
|
|
2232
|
+
return width > 0 && height > 0;
|
|
2233
|
+
}
|
|
2234
|
+
function getTiffDimensions(ifd) {
|
|
2235
|
+
return {
|
|
2236
|
+
width: Number(ifd.width || ifd.t256 || 0),
|
|
2237
|
+
height: Number(ifd.height || ifd.t257 || 0)
|
|
2238
|
+
};
|
|
2181
2239
|
}
|
|
2182
2240
|
function createImageFallback(fileName, url) {
|
|
2183
2241
|
const fallback = document.createElement("div");
|
|
@@ -2506,6 +2564,7 @@ function parseTiffInfo(bytes) {
|
|
|
2506
2564
|
return { format: magic === 43 ? "BigTIFF" : "TIFF", note: "IFD \u504F\u79FB\u8D85\u51FA\u6587\u4EF6\u8303\u56F4" };
|
|
2507
2565
|
}
|
|
2508
2566
|
const count = magic === 43 ? readTiffUint64AsNumber(bytes, ifdOffset, littleEndian) : readTiffUint16(bytes, ifdOffset, littleEndian);
|
|
2567
|
+
const imageCount = countTiffIfds(bytes, ifdOffset, magic === 43, littleEndian);
|
|
2509
2568
|
const entrySize = magic === 43 ? 20 : 12;
|
|
2510
2569
|
const entriesStart = ifdOffset + (magic === 43 ? 8 : 2);
|
|
2511
2570
|
let width;
|
|
@@ -2532,9 +2591,26 @@ function parseTiffInfo(bytes) {
|
|
|
2532
2591
|
height,
|
|
2533
2592
|
bitDepth: bitDepth ? `${bitDepth} bit` : void 0,
|
|
2534
2593
|
color: compression ? tiffCompressionName(compression) : void 0,
|
|
2535
|
-
count:
|
|
2594
|
+
count: imageCount || void 0
|
|
2536
2595
|
};
|
|
2537
2596
|
}
|
|
2597
|
+
function countTiffIfds(bytes, firstOffset, bigTiff, littleEndian) {
|
|
2598
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2599
|
+
let offset = firstOffset;
|
|
2600
|
+
let count = 0;
|
|
2601
|
+
while (Number.isFinite(offset) && offset > 0 && offset < bytes.length && !seen.has(offset) && count < 512) {
|
|
2602
|
+
seen.add(offset);
|
|
2603
|
+
const entryCount = bigTiff ? readTiffUint64AsNumber(bytes, offset, littleEndian) : readTiffUint16(bytes, offset, littleEndian);
|
|
2604
|
+
const entrySize = bigTiff ? 20 : 12;
|
|
2605
|
+
const nextOffsetPosition = offset + (bigTiff ? 8 : 2) + entryCount * entrySize;
|
|
2606
|
+
if (!Number.isFinite(entryCount) || nextOffsetPosition + (bigTiff ? 8 : 4) > bytes.length) {
|
|
2607
|
+
break;
|
|
2608
|
+
}
|
|
2609
|
+
count++;
|
|
2610
|
+
offset = bigTiff ? readTiffUint64AsNumber(bytes, nextOffsetPosition, littleEndian) : readTiffUint32(bytes, nextOffsetPosition, littleEndian);
|
|
2611
|
+
}
|
|
2612
|
+
return count;
|
|
2613
|
+
}
|
|
2538
2614
|
function readTiffInlineValue(bytes, offset, type, littleEndian) {
|
|
2539
2615
|
if (offset + 4 > bytes.length) {
|
|
2540
2616
|
return void 0;
|
|
@@ -3933,26 +4009,6 @@ var mimeLangMap = {
|
|
|
3933
4009
|
};
|
|
3934
4010
|
var MAX_HIGHLIGHT_CHARS = 18e4;
|
|
3935
4011
|
var MAX_RENDER_CHARS = 6e5;
|
|
3936
|
-
function loadPrismCss(theme) {
|
|
3937
|
-
const lightId = "ofv-prism-css-light";
|
|
3938
|
-
const darkId = "ofv-prism-css-dark";
|
|
3939
|
-
const activeId = theme === "dark" ? darkId : lightId;
|
|
3940
|
-
const inactiveId = theme === "dark" ? lightId : darkId;
|
|
3941
|
-
document.getElementById(inactiveId)?.remove();
|
|
3942
|
-
if (document.getElementById(activeId)) {
|
|
3943
|
-
return Promise.resolve();
|
|
3944
|
-
}
|
|
3945
|
-
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";
|
|
3946
|
-
return new Promise((resolve, reject) => {
|
|
3947
|
-
const link = document.createElement("link");
|
|
3948
|
-
link.id = activeId;
|
|
3949
|
-
link.rel = "stylesheet";
|
|
3950
|
-
link.href = href;
|
|
3951
|
-
link.onload = () => resolve();
|
|
3952
|
-
link.onerror = () => reject(new Error(`Failed to load Prism CSS: ${href}`));
|
|
3953
|
-
document.head.appendChild(link);
|
|
3954
|
-
});
|
|
3955
|
-
}
|
|
3956
4012
|
function textPlugin() {
|
|
3957
4013
|
return {
|
|
3958
4014
|
name: "text",
|
|
@@ -3976,7 +4032,6 @@ function textPlugin() {
|
|
|
3976
4032
|
}
|
|
3977
4033
|
};
|
|
3978
4034
|
}
|
|
3979
|
-
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";
|
|
3980
4035
|
if (isMarkdown) {
|
|
3981
4036
|
const [markedModule, PrismModule2, DOMPurifyModule] = await Promise.all([
|
|
3982
4037
|
import("marked"),
|
|
@@ -3997,7 +4052,6 @@ function textPlugin() {
|
|
|
3997
4052
|
try {
|
|
3998
4053
|
const codeBlocks = container.querySelectorAll("pre code");
|
|
3999
4054
|
if (codeBlocks.length > 0) {
|
|
4000
|
-
await loadPrismCss(isDark ? "dark" : "light");
|
|
4001
4055
|
codeBlocks.forEach((block) => {
|
|
4002
4056
|
const parent = block.parentElement;
|
|
4003
4057
|
if (parent && !parent.className.includes("language-")) {
|
|
@@ -4121,9 +4175,6 @@ function textPlugin() {
|
|
|
4121
4175
|
console.warn(`Prism failed to load language component for: ${lang}`, e);
|
|
4122
4176
|
}
|
|
4123
4177
|
}
|
|
4124
|
-
await loadPrismCss(isDark ? "dark" : "light").catch((error) => {
|
|
4125
|
-
console.warn("Prism CSS failed to load; rendering code without external theme:", error);
|
|
4126
|
-
});
|
|
4127
4178
|
const codeText = text.length > MAX_RENDER_CHARS ? text.slice(0, MAX_RENDER_CHARS) : text;
|
|
4128
4179
|
const totalLines = countLines(text);
|
|
4129
4180
|
const shownLines = countLines(codeText);
|
|
@@ -4651,10 +4702,11 @@ async function renderPdfDocumentPreview(options) {
|
|
|
4651
4702
|
const baseViewport = page.getViewport({ scale: 1 });
|
|
4652
4703
|
pagesMeta.push({
|
|
4653
4704
|
width: baseViewport.width,
|
|
4654
|
-
height: baseViewport.height
|
|
4705
|
+
height: baseViewport.height,
|
|
4706
|
+
rotation: getPdfPageRotation(page)
|
|
4655
4707
|
});
|
|
4656
4708
|
} catch {
|
|
4657
|
-
pagesMeta.push({ width: 612, height: 792 });
|
|
4709
|
+
pagesMeta.push({ width: 612, height: 792, rotation: 0 });
|
|
4658
4710
|
}
|
|
4659
4711
|
}
|
|
4660
4712
|
const pageStates = [];
|
|
@@ -4689,7 +4741,7 @@ async function renderPdfDocumentPreview(options) {
|
|
|
4689
4741
|
const page = await pdfDocument.getPage(pageIdx + 1);
|
|
4690
4742
|
const meta = pagesMeta[pageIdx];
|
|
4691
4743
|
const scale = options.fit === "actual" ? zoomFactor : Math.max(0.05, Math.min(5, getPdfAvailableWidth(size.width) / rotatedPdfWidth(meta, rotation) * zoomFactor));
|
|
4692
|
-
const viewport = page.getViewport({ scale, rotation });
|
|
4744
|
+
const viewport = page.getViewport({ scale, rotation: getPdfRenderRotation(meta, rotation) });
|
|
4693
4745
|
const outputScale = getPdfOutputScale();
|
|
4694
4746
|
const cssWidth = Math.floor(viewport.width);
|
|
4695
4747
|
const cssHeight = Math.floor(viewport.height);
|
|
@@ -4728,6 +4780,8 @@ async function renderPdfDocumentPreview(options) {
|
|
|
4728
4780
|
const span = document.createElement("span");
|
|
4729
4781
|
span.textContent = str;
|
|
4730
4782
|
span.style.fontSize = `${fontHeight}px`;
|
|
4783
|
+
span.style.lineHeight = "1";
|
|
4784
|
+
span.style.height = `${fontHeight}px`;
|
|
4731
4785
|
span.style.fontFamily = item.fontName || "sans-serif";
|
|
4732
4786
|
span.style.left = `${tx[4]}px`;
|
|
4733
4787
|
span.style.top = `${tx[5] - fontHeight}px`;
|
|
@@ -4925,6 +4979,13 @@ function appendPdfSummary(parent, label, value) {
|
|
|
4925
4979
|
function normalizePdfRotation(value) {
|
|
4926
4980
|
return (value % 360 + 360) % 360;
|
|
4927
4981
|
}
|
|
4982
|
+
function getPdfPageRotation(page) {
|
|
4983
|
+
const rotation = Number(page.rotate);
|
|
4984
|
+
return Number.isFinite(rotation) ? normalizePdfRotation(rotation) : 0;
|
|
4985
|
+
}
|
|
4986
|
+
function getPdfRenderRotation(meta, userRotation) {
|
|
4987
|
+
return normalizePdfRotation((meta.rotation || 0) + userRotation);
|
|
4988
|
+
}
|
|
4928
4989
|
function isPdfRotatedSideways(rotation) {
|
|
4929
4990
|
const normalized = normalizePdfRotation(rotation);
|
|
4930
4991
|
return normalized === 90 || normalized === 270;
|
|
@@ -5809,6 +5870,1100 @@ function decodeXml(value) {
|
|
|
5809
5870
|
// src/plugins/office.ts
|
|
5810
5871
|
import JSZip3 from "jszip";
|
|
5811
5872
|
import DOMPurify2 from "dompurify";
|
|
5873
|
+
|
|
5874
|
+
// src/plugins/msdoc.ts
|
|
5875
|
+
var CFB_SIGNATURE = [208, 207, 17, 224, 161, 177, 26, 225];
|
|
5876
|
+
var FREE_SECTOR = 4294967295;
|
|
5877
|
+
var END_OF_CHAIN = 4294967294;
|
|
5878
|
+
var FAT_SECTOR = 4294967293;
|
|
5879
|
+
var DIFAT_SECTOR = 4294967292;
|
|
5880
|
+
var MINI_STREAM_CUTOFF = 4096;
|
|
5881
|
+
var STSH_FC_LCB_INDEX = 1;
|
|
5882
|
+
var CLX_FC_LCB_INDEX = 33;
|
|
5883
|
+
var WORD_PAGE_BREAK = "\f";
|
|
5884
|
+
function parseLegacyWordDocument(input) {
|
|
5885
|
+
const cfb = parseCompoundFile(new Uint8Array(input));
|
|
5886
|
+
const wordDocument = cfb.getStream("WordDocument");
|
|
5887
|
+
if (!wordDocument) {
|
|
5888
|
+
throw new Error("\u672A\u627E\u5230 WordDocument \u6D41");
|
|
5889
|
+
}
|
|
5890
|
+
const fib = parseFib(wordDocument);
|
|
5891
|
+
if (fib.encrypted) {
|
|
5892
|
+
throw new Error("\u6682\u4E0D\u652F\u6301\u52A0\u5BC6\u7684 .doc \u6587\u4EF6");
|
|
5893
|
+
}
|
|
5894
|
+
const tableStreamName = fib.useOneTable ? "1Table" : "0Table";
|
|
5895
|
+
const tableStream = cfb.getStream(tableStreamName);
|
|
5896
|
+
if (!tableStream) {
|
|
5897
|
+
throw new Error(`\u672A\u627E\u5230 ${tableStreamName} \u8868\u6D41`);
|
|
5898
|
+
}
|
|
5899
|
+
const assets = extractImageAssets(cfb);
|
|
5900
|
+
const styles = parseStyleTable(tableStream, fib);
|
|
5901
|
+
const pieces = parseClxPieces(tableStream, fib.fcClx, fib.lcbClx);
|
|
5902
|
+
const text = pieces.length > 0 ? readPieceTableText(wordDocument, pieces, fib.ccpText) : readFibTextFallback(wordDocument, fib);
|
|
5903
|
+
const paragraphs = splitWordParagraphs(text);
|
|
5904
|
+
if (paragraphs.length === 0) {
|
|
5905
|
+
throw new Error("\u672A\u89E3\u6790\u5230\u53EF\u663E\u793A\u7684\u6B63\u6587\u6BB5\u843D");
|
|
5906
|
+
}
|
|
5907
|
+
const bodyParagraphs = removeTrailingFooterArtifacts(paragraphs);
|
|
5908
|
+
const blocks = buildWordBlocks(bodyParagraphs);
|
|
5909
|
+
const layout = inferLayoutHints(paragraphs, assets);
|
|
5910
|
+
return {
|
|
5911
|
+
title: inferDocumentTitle(paragraphs),
|
|
5912
|
+
paragraphs: bodyParagraphs,
|
|
5913
|
+
blocks,
|
|
5914
|
+
layout,
|
|
5915
|
+
assets,
|
|
5916
|
+
styles,
|
|
5917
|
+
stats: {
|
|
5918
|
+
streamCount: cfb.entries.length,
|
|
5919
|
+
pieceCount: pieces.length,
|
|
5920
|
+
characterCount: bodyParagraphs.join("\n").length,
|
|
5921
|
+
styleCount: styles.length,
|
|
5922
|
+
tableStream: tableStreamName
|
|
5923
|
+
},
|
|
5924
|
+
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"] : []
|
|
5925
|
+
};
|
|
5926
|
+
}
|
|
5927
|
+
function renderLegacyWordDocument(panel, document2) {
|
|
5928
|
+
panel.replaceChildren();
|
|
5929
|
+
const article = window.document.createElement("article");
|
|
5930
|
+
article.className = "ofv-msdoc-document";
|
|
5931
|
+
const pages = paginateWordBlocks(document2.blocks.slice(0, 600), document2.layout);
|
|
5932
|
+
const pageCount = inferDisplayedPageCount(document2.blocks, pages.length);
|
|
5933
|
+
const page = window.document.createElement("section");
|
|
5934
|
+
page.className = "ofv-msdoc-page";
|
|
5935
|
+
appendPageChrome(page, document2, 1, pageCount);
|
|
5936
|
+
const meta = window.document.createElement("dl");
|
|
5937
|
+
meta.className = "ofv-msdoc-meta";
|
|
5938
|
+
appendMeta4(meta, "\u683C\u5F0F", "Word 97-2003 Binary");
|
|
5939
|
+
appendMeta4(meta, "\u6B63\u6587\u6BB5\u843D", `${document2.paragraphs.length}`);
|
|
5940
|
+
appendMeta4(meta, "Piece Table", `${document2.stats.pieceCount || 0} \u6BB5`);
|
|
5941
|
+
appendMeta4(meta, "\u6837\u5F0F\u8868", `${document2.stats.styleCount || 0} \u4E2A\u6837\u5F0F`);
|
|
5942
|
+
appendMeta4(meta, "\u8868\u6D41", document2.stats.tableStream);
|
|
5943
|
+
if (document2.styles.length > 0) {
|
|
5944
|
+
appendMeta4(meta, "\u6837\u5F0F\u540D\u79F0", document2.styles.slice(0, 30).map((style) => style.name).join("\u3001"));
|
|
5945
|
+
}
|
|
5946
|
+
meta.hidden = true;
|
|
5947
|
+
page.append(meta);
|
|
5948
|
+
appendBlocksToPage(page, pages[0] || [], document2.layout);
|
|
5949
|
+
appendWarnings(page, document2);
|
|
5950
|
+
article.append(page);
|
|
5951
|
+
for (const pageBlocks of pages.slice(1)) {
|
|
5952
|
+
const nextPage = window.document.createElement("section");
|
|
5953
|
+
nextPage.className = "ofv-msdoc-page";
|
|
5954
|
+
appendPageChrome(nextPage, document2, article.children.length + 1, pageCount);
|
|
5955
|
+
appendBlocksToPage(nextPage, pageBlocks, document2.layout);
|
|
5956
|
+
article.append(nextPage);
|
|
5957
|
+
}
|
|
5958
|
+
panel.append(article);
|
|
5959
|
+
}
|
|
5960
|
+
function inferDisplayedPageCount(blocks, renderedPageCount) {
|
|
5961
|
+
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);
|
|
5962
|
+
if (tocPageNumbers.length === 0) {
|
|
5963
|
+
return renderedPageCount;
|
|
5964
|
+
}
|
|
5965
|
+
return Math.max(renderedPageCount, ...tocPageNumbers);
|
|
5966
|
+
}
|
|
5967
|
+
function appendPageChrome(page, document2, pageNumber, pageCount) {
|
|
5968
|
+
if (document2.layout.lineNumbers) {
|
|
5969
|
+
page.classList.add("ofv-msdoc-line-numbered");
|
|
5970
|
+
}
|
|
5971
|
+
page.setAttribute("aria-label", document2.title || "Word \u6587\u6863");
|
|
5972
|
+
if (document2.layout.headerBrand === "oasis") {
|
|
5973
|
+
page.append(createOasisHeader(document2.assets.find((asset) => asset.id === document2.layout.headerImageId)));
|
|
5974
|
+
}
|
|
5975
|
+
if (document2.layout.footer) {
|
|
5976
|
+
page.append(createPageFooter(document2.layout.footer, pageNumber, pageCount));
|
|
5977
|
+
}
|
|
5978
|
+
}
|
|
5979
|
+
function appendBlocksToPage(page, blocks, layout) {
|
|
5980
|
+
let lineNumber = 1;
|
|
5981
|
+
for (const block of blocks) {
|
|
5982
|
+
if (block.type === "pageBreak") {
|
|
5983
|
+
continue;
|
|
5984
|
+
}
|
|
5985
|
+
const element = renderWordBlock(block);
|
|
5986
|
+
if (layout.lineNumbers && element instanceof HTMLElement && !element.classList.contains("ofv-msdoc-page-header")) {
|
|
5987
|
+
element.dataset.line = String(lineNumber);
|
|
5988
|
+
lineNumber += estimatedLineCount(block);
|
|
5989
|
+
}
|
|
5990
|
+
page.append(element);
|
|
5991
|
+
}
|
|
5992
|
+
}
|
|
5993
|
+
function appendWarnings(page, document2) {
|
|
5994
|
+
if (document2.warnings.length === 0) {
|
|
5995
|
+
return;
|
|
5996
|
+
}
|
|
5997
|
+
const warning = window.document.createElement("p");
|
|
5998
|
+
warning.className = "ofv-msdoc-warning";
|
|
5999
|
+
warning.textContent = document2.warnings.join(" ");
|
|
6000
|
+
warning.hidden = true;
|
|
6001
|
+
page.append(warning);
|
|
6002
|
+
}
|
|
6003
|
+
function createOasisHeader(image) {
|
|
6004
|
+
const header = window.document.createElement("header");
|
|
6005
|
+
header.className = "ofv-msdoc-page-header ofv-msdoc-oasis-header";
|
|
6006
|
+
const logo = image ? createImageLogo(image) : createFallbackOasisLogo();
|
|
6007
|
+
header.append(logo);
|
|
6008
|
+
return header;
|
|
6009
|
+
}
|
|
6010
|
+
function createImageLogo(image) {
|
|
6011
|
+
const wrapper = window.document.createElement("div");
|
|
6012
|
+
wrapper.className = "ofv-msdoc-oasis-logo ofv-msdoc-oasis-logo-image";
|
|
6013
|
+
const img = window.document.createElement("img");
|
|
6014
|
+
img.src = image.dataUrl;
|
|
6015
|
+
img.alt = "OASIS";
|
|
6016
|
+
if (image.width) {
|
|
6017
|
+
img.width = image.width;
|
|
6018
|
+
}
|
|
6019
|
+
if (image.height) {
|
|
6020
|
+
img.height = image.height;
|
|
6021
|
+
}
|
|
6022
|
+
wrapper.append(img);
|
|
6023
|
+
return wrapper;
|
|
6024
|
+
}
|
|
6025
|
+
function createFallbackOasisLogo() {
|
|
6026
|
+
const logo = window.document.createElement("div");
|
|
6027
|
+
logo.className = "ofv-msdoc-oasis-logo";
|
|
6028
|
+
const word = window.document.createElement("span");
|
|
6029
|
+
word.textContent = "OASIS";
|
|
6030
|
+
const mark = window.document.createElement("span");
|
|
6031
|
+
mark.className = "ofv-msdoc-oasis-mark";
|
|
6032
|
+
mark.setAttribute("aria-hidden", "true");
|
|
6033
|
+
logo.append(word, mark);
|
|
6034
|
+
return logo;
|
|
6035
|
+
}
|
|
6036
|
+
function createPageFooter(footer, pageNumber, pageCount) {
|
|
6037
|
+
const element = window.document.createElement("footer");
|
|
6038
|
+
element.className = "ofv-msdoc-page-footer";
|
|
6039
|
+
const top = window.document.createElement("div");
|
|
6040
|
+
top.className = "ofv-msdoc-footer-row";
|
|
6041
|
+
const documentId = window.document.createElement("span");
|
|
6042
|
+
documentId.textContent = footer.documentId || "";
|
|
6043
|
+
const date = window.document.createElement("span");
|
|
6044
|
+
date.textContent = footer.date || "";
|
|
6045
|
+
top.append(documentId, date);
|
|
6046
|
+
const bottom = window.document.createElement("div");
|
|
6047
|
+
bottom.className = "ofv-msdoc-footer-row";
|
|
6048
|
+
const copyright = window.document.createElement("span");
|
|
6049
|
+
copyright.textContent = footer.copyright || "";
|
|
6050
|
+
const page = window.document.createElement("span");
|
|
6051
|
+
page.textContent = `Page ${pageNumber} of ${pageCount}`;
|
|
6052
|
+
bottom.append(copyright, page);
|
|
6053
|
+
element.append(top, bottom);
|
|
6054
|
+
return element;
|
|
6055
|
+
}
|
|
6056
|
+
function renderWordBlock(block) {
|
|
6057
|
+
if (block.type === "pageBreak") {
|
|
6058
|
+
const marker = window.document.createElement("span");
|
|
6059
|
+
marker.className = "ofv-msdoc-page-break";
|
|
6060
|
+
marker.hidden = true;
|
|
6061
|
+
return marker;
|
|
6062
|
+
}
|
|
6063
|
+
if (block.type === "table") {
|
|
6064
|
+
const table = window.document.createElement("table");
|
|
6065
|
+
table.className = "ofv-msdoc-table";
|
|
6066
|
+
const revisionColumnWidths = getRevisionTableColumnWidths(block.rows);
|
|
6067
|
+
if (revisionColumnWidths) {
|
|
6068
|
+
table.classList.add("ofv-msdoc-revision-table");
|
|
6069
|
+
const colgroup = window.document.createElement("colgroup");
|
|
6070
|
+
for (const width of revisionColumnWidths) {
|
|
6071
|
+
const col = window.document.createElement("col");
|
|
6072
|
+
col.style.width = `calc(${width}px * var(--ofv-office-zoom, 1))`;
|
|
6073
|
+
colgroup.append(col);
|
|
6074
|
+
}
|
|
6075
|
+
table.append(colgroup);
|
|
6076
|
+
}
|
|
6077
|
+
const tbody = window.document.createElement("tbody");
|
|
6078
|
+
for (const row of block.rows) {
|
|
6079
|
+
const tr = window.document.createElement("tr");
|
|
6080
|
+
const cellTag = row === block.rows[0] && block.rows.length > 1 ? "th" : "td";
|
|
6081
|
+
for (const cellText of row) {
|
|
6082
|
+
const cell = window.document.createElement(cellTag);
|
|
6083
|
+
cell.textContent = cellText;
|
|
6084
|
+
tr.append(cell);
|
|
6085
|
+
}
|
|
6086
|
+
tbody.append(tr);
|
|
6087
|
+
}
|
|
6088
|
+
table.append(tbody);
|
|
6089
|
+
return table;
|
|
6090
|
+
}
|
|
6091
|
+
if (block.type === "toc") {
|
|
6092
|
+
const paragraph2 = window.document.createElement("p");
|
|
6093
|
+
paragraph2.className = `ofv-msdoc-toc ofv-msdoc-toc-level-${block.level}`;
|
|
6094
|
+
const title = window.document.createElement("span");
|
|
6095
|
+
title.textContent = block.title;
|
|
6096
|
+
paragraph2.append(title);
|
|
6097
|
+
if (block.page) {
|
|
6098
|
+
const page = window.document.createElement("span");
|
|
6099
|
+
page.textContent = block.page;
|
|
6100
|
+
paragraph2.append(page);
|
|
6101
|
+
}
|
|
6102
|
+
return paragraph2;
|
|
6103
|
+
}
|
|
6104
|
+
const paragraph = window.document.createElement("p");
|
|
6105
|
+
const levelClass = block.type === "heading" ? ` ofv-msdoc-heading-level-${block.level}` : "";
|
|
6106
|
+
const listClass = block.type === "listItem" ? ` ofv-msdoc-list-level-${block.level}` : "";
|
|
6107
|
+
paragraph.className = `ofv-msdoc-${block.type}${levelClass}${listClass}${"indent" in block && block.indent ? " ofv-msdoc-indent" : ""}`;
|
|
6108
|
+
appendInlineRuns(paragraph, block.text, block.type === "code");
|
|
6109
|
+
return paragraph;
|
|
6110
|
+
}
|
|
6111
|
+
function appendInlineRuns(element, text, preserveTabs = false) {
|
|
6112
|
+
if (preserveTabs) {
|
|
6113
|
+
appendInlineText(element, text, true);
|
|
6114
|
+
return;
|
|
6115
|
+
}
|
|
6116
|
+
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;
|
|
6117
|
+
let offset = 0;
|
|
6118
|
+
for (const match of text.matchAll(pattern)) {
|
|
6119
|
+
const value = match[0];
|
|
6120
|
+
const index = match.index || 0;
|
|
6121
|
+
if (index > offset) {
|
|
6122
|
+
appendInlineText(element, text.slice(offset, index), preserveTabs);
|
|
6123
|
+
}
|
|
6124
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
6125
|
+
appendBracketRun(element, value);
|
|
6126
|
+
} else if (isCodeStyleRun(value)) {
|
|
6127
|
+
const code = window.document.createElement("code");
|
|
6128
|
+
code.className = "ofv-msdoc-inline-code";
|
|
6129
|
+
code.textContent = value;
|
|
6130
|
+
element.append(code);
|
|
6131
|
+
} else if (isVariableStyleRun(value)) {
|
|
6132
|
+
const em = window.document.createElement("em");
|
|
6133
|
+
em.className = "ofv-msdoc-variable";
|
|
6134
|
+
em.textContent = value;
|
|
6135
|
+
element.append(em);
|
|
6136
|
+
} else if (isRequirementKeywordRun(value)) {
|
|
6137
|
+
const em = window.document.createElement("em");
|
|
6138
|
+
em.className = "ofv-msdoc-keyword";
|
|
6139
|
+
em.textContent = value;
|
|
6140
|
+
element.append(em);
|
|
6141
|
+
} else {
|
|
6142
|
+
const link = splitLinkRun(value);
|
|
6143
|
+
const anchor = window.document.createElement("a");
|
|
6144
|
+
anchor.className = "ofv-msdoc-link-text";
|
|
6145
|
+
anchor.href = link.href;
|
|
6146
|
+
anchor.target = "_blank";
|
|
6147
|
+
anchor.rel = "noreferrer noopener";
|
|
6148
|
+
anchor.textContent = link.text;
|
|
6149
|
+
element.append(anchor);
|
|
6150
|
+
if (link.trailing) {
|
|
6151
|
+
appendInlineText(element, link.trailing, preserveTabs);
|
|
6152
|
+
}
|
|
6153
|
+
}
|
|
6154
|
+
offset = index + value.length;
|
|
6155
|
+
}
|
|
6156
|
+
if (offset < text.length) {
|
|
6157
|
+
appendInlineText(element, text.slice(offset), preserveTabs);
|
|
6158
|
+
}
|
|
6159
|
+
}
|
|
6160
|
+
function appendBracketRun(element, value) {
|
|
6161
|
+
const tagName = isReferenceTerm(value) ? "strong" : "em";
|
|
6162
|
+
const run = window.document.createElement(tagName);
|
|
6163
|
+
run.className = isReferenceTerm(value) ? "ofv-msdoc-ref-term" : "ofv-msdoc-instruction-run";
|
|
6164
|
+
run.textContent = value;
|
|
6165
|
+
element.append(run);
|
|
6166
|
+
}
|
|
6167
|
+
function getRevisionTableColumnWidths(rows) {
|
|
6168
|
+
const header = rows[0]?.map((cell) => cell.toLowerCase());
|
|
6169
|
+
if (!header || header.length !== 4) {
|
|
6170
|
+
return void 0;
|
|
6171
|
+
}
|
|
6172
|
+
if (header[0] === "rev" && header[1] === "date" && /whom/.test(header[2]) && header[3] === "what") {
|
|
6173
|
+
return [59, 81, 106, 191];
|
|
6174
|
+
}
|
|
6175
|
+
return void 0;
|
|
6176
|
+
}
|
|
6177
|
+
function appendInlineText(element, text, preserveTabs) {
|
|
6178
|
+
element.append(window.document.createTextNode(preserveTabs ? text : text.replace(/\t+/g, " ")));
|
|
6179
|
+
}
|
|
6180
|
+
function isReferenceTerm(value) {
|
|
6181
|
+
return /^\[[A-Z0-9][A-Z0-9.-]{1,24}\]$/.test(value);
|
|
6182
|
+
}
|
|
6183
|
+
function isCodeStyleRun(value) {
|
|
6184
|
+
return /^<\/?[A-Za-z][A-Za-z0-9:-]*>$/.test(value) || /^(?:attributeNames|DataType|OtherKeyword)$/.test(value);
|
|
6185
|
+
}
|
|
6186
|
+
function isVariableStyleRun(value) {
|
|
6187
|
+
return value === "variable";
|
|
6188
|
+
}
|
|
6189
|
+
function isRequirementKeywordRun(value) {
|
|
6190
|
+
return /^(?:must not|must|required|shall not|shall|should not|should|recommended|may|optional)$/i.test(value);
|
|
6191
|
+
}
|
|
6192
|
+
function splitLinkRun(value) {
|
|
6193
|
+
let text = value;
|
|
6194
|
+
let trailing = "";
|
|
6195
|
+
while (/[),.;:]$/.test(text)) {
|
|
6196
|
+
trailing = text.slice(-1) + trailing;
|
|
6197
|
+
text = text.slice(0, -1);
|
|
6198
|
+
}
|
|
6199
|
+
const href = /^https?:\/\//i.test(text) ? text : `mailto:${text}`;
|
|
6200
|
+
return { text, href, trailing };
|
|
6201
|
+
}
|
|
6202
|
+
function extractImageAssets(cfb) {
|
|
6203
|
+
const assets = [];
|
|
6204
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6205
|
+
for (const entry of cfb.entries) {
|
|
6206
|
+
if (entry.type !== 2) {
|
|
6207
|
+
continue;
|
|
6208
|
+
}
|
|
6209
|
+
const stream = cfb.getStream(entry.name);
|
|
6210
|
+
if (!stream || stream.length < 16) {
|
|
6211
|
+
continue;
|
|
6212
|
+
}
|
|
6213
|
+
for (const image of extractImagesFromBytes(stream, entry.name)) {
|
|
6214
|
+
const key = `${image.mimeType}:${image.bytes.length}:${image.bytes[0]}:${image.bytes[image.bytes.length - 1]}`;
|
|
6215
|
+
if (seen.has(key)) {
|
|
6216
|
+
continue;
|
|
6217
|
+
}
|
|
6218
|
+
seen.add(key);
|
|
6219
|
+
const id = `image-${assets.length + 1}`;
|
|
6220
|
+
assets.push({
|
|
6221
|
+
id,
|
|
6222
|
+
kind: "image",
|
|
6223
|
+
mimeType: image.mimeType,
|
|
6224
|
+
dataUrl: `data:${image.mimeType};base64,${bytesToBase64(image.bytes)}`,
|
|
6225
|
+
width: image.width,
|
|
6226
|
+
height: image.height
|
|
6227
|
+
});
|
|
6228
|
+
}
|
|
6229
|
+
}
|
|
6230
|
+
return assets;
|
|
6231
|
+
}
|
|
6232
|
+
function parseStyleTable(tableStream, fib) {
|
|
6233
|
+
if (fib.lcbStshf <= 0 || fib.fcStshf < 0 || fib.fcStshf >= tableStream.length) {
|
|
6234
|
+
return [];
|
|
6235
|
+
}
|
|
6236
|
+
const bytes = tableStream.subarray(fib.fcStshf, Math.min(tableStream.length, fib.fcStshf + fib.lcbStshf));
|
|
6237
|
+
if (bytes.length < 8) {
|
|
6238
|
+
return [];
|
|
6239
|
+
}
|
|
6240
|
+
const view3 = dataView2(bytes);
|
|
6241
|
+
const cbStshi = view3.getUint16(0, true);
|
|
6242
|
+
const stshiOffset = 2;
|
|
6243
|
+
const cstd = stshiOffset + 2 <= bytes.length ? view3.getUint16(stshiOffset, true) : 0;
|
|
6244
|
+
const cbSTDBaseInFile = stshiOffset + 4 <= bytes.length ? view3.getUint16(stshiOffset + 2, true) : 10;
|
|
6245
|
+
let offset = 2 + cbStshi;
|
|
6246
|
+
const styles = [];
|
|
6247
|
+
for (let id = 0; id < cstd && offset + 2 <= bytes.length; id += 1) {
|
|
6248
|
+
const cbStd = view3.getUint16(offset, true);
|
|
6249
|
+
const stdStart = offset + 2;
|
|
6250
|
+
const stdEnd = stdStart + cbStd;
|
|
6251
|
+
if (cbStd > 0 && stdStart < bytes.length) {
|
|
6252
|
+
const style = parseStyleDefinition(bytes.subarray(stdStart, Math.min(stdEnd, bytes.length)), Math.max(10, cbSTDBaseInFile), id);
|
|
6253
|
+
if (style.name) {
|
|
6254
|
+
styles.push(style);
|
|
6255
|
+
}
|
|
6256
|
+
}
|
|
6257
|
+
offset = alignEven(stdEnd);
|
|
6258
|
+
}
|
|
6259
|
+
return styles;
|
|
6260
|
+
}
|
|
6261
|
+
function parseStyleDefinition(bytes, baseSize, id) {
|
|
6262
|
+
const base = bytes.length >= 6 ? parseStyleBase(bytes) : void 0;
|
|
6263
|
+
const nameOffset = Math.min(bytes.length, Math.max(10, baseSize));
|
|
6264
|
+
const name = parseXstz(bytes, nameOffset);
|
|
6265
|
+
return {
|
|
6266
|
+
id,
|
|
6267
|
+
name,
|
|
6268
|
+
type: styleTypeFromStk(base?.stk),
|
|
6269
|
+
basedOn: base?.basedOn,
|
|
6270
|
+
next: base?.next
|
|
6271
|
+
};
|
|
6272
|
+
}
|
|
6273
|
+
function parseStyleBase(bytes) {
|
|
6274
|
+
const view3 = dataView2(bytes);
|
|
6275
|
+
const w2 = view3.getUint16(2, true);
|
|
6276
|
+
const w3 = view3.getUint16(4, true);
|
|
6277
|
+
return {
|
|
6278
|
+
stk: w2 & 15,
|
|
6279
|
+
basedOn: w2 >> 4 & 4095,
|
|
6280
|
+
next: w3 >> 4 & 4095
|
|
6281
|
+
};
|
|
6282
|
+
}
|
|
6283
|
+
function styleTypeFromStk(stk) {
|
|
6284
|
+
if (stk === 1) {
|
|
6285
|
+
return "paragraph";
|
|
6286
|
+
}
|
|
6287
|
+
if (stk === 2) {
|
|
6288
|
+
return "character";
|
|
6289
|
+
}
|
|
6290
|
+
if (stk === 3) {
|
|
6291
|
+
return "table";
|
|
6292
|
+
}
|
|
6293
|
+
if (stk === 4) {
|
|
6294
|
+
return "numbering";
|
|
6295
|
+
}
|
|
6296
|
+
return "unknown";
|
|
6297
|
+
}
|
|
6298
|
+
function parseXstz(bytes, offset) {
|
|
6299
|
+
if (offset + 2 > bytes.length) {
|
|
6300
|
+
return "";
|
|
6301
|
+
}
|
|
6302
|
+
const view3 = dataView2(bytes);
|
|
6303
|
+
const charCount = view3.getUint16(offset, true);
|
|
6304
|
+
const start = offset + 2;
|
|
6305
|
+
const end = Math.min(bytes.length, start + charCount * 2);
|
|
6306
|
+
if (end <= start) {
|
|
6307
|
+
return "";
|
|
6308
|
+
}
|
|
6309
|
+
return decodeUtf16Le(bytes.subarray(start, end)).replace(/\0+$/g, "");
|
|
6310
|
+
}
|
|
6311
|
+
function extractImagesFromBytes(bytes, sourceName) {
|
|
6312
|
+
const images = [];
|
|
6313
|
+
for (const start of findSignatureOffsets(bytes, PNG_SIGNATURE)) {
|
|
6314
|
+
const end = findPngEnd(bytes, start);
|
|
6315
|
+
if (end > start) {
|
|
6316
|
+
const imageBytes = bytes.slice(start, end);
|
|
6317
|
+
const dimensions = readPngDimensions(imageBytes);
|
|
6318
|
+
images.push({ bytes: imageBytes, mimeType: "image/png", width: dimensions?.width, height: dimensions?.height });
|
|
6319
|
+
}
|
|
6320
|
+
}
|
|
6321
|
+
for (const start of findSignatureOffsets(bytes, JPEG_SIGNATURE)) {
|
|
6322
|
+
const end = findJpegEnd(bytes, start);
|
|
6323
|
+
if (end > start) {
|
|
6324
|
+
images.push({ bytes: bytes.slice(start, end), mimeType: "image/jpeg" });
|
|
6325
|
+
}
|
|
6326
|
+
}
|
|
6327
|
+
if (/picture|image|data/i.test(sourceName)) {
|
|
6328
|
+
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);
|
|
6329
|
+
if (gifStart >= 0) {
|
|
6330
|
+
images.push({ bytes: bytes.slice(gifStart), mimeType: "image/gif" });
|
|
6331
|
+
}
|
|
6332
|
+
}
|
|
6333
|
+
return images;
|
|
6334
|
+
}
|
|
6335
|
+
var PNG_SIGNATURE = Uint8Array.from([137, 80, 78, 71, 13, 10, 26, 10]);
|
|
6336
|
+
var JPEG_SIGNATURE = Uint8Array.from([255, 216, 255]);
|
|
6337
|
+
function findSignatureOffsets(bytes, signature) {
|
|
6338
|
+
const offsets = [];
|
|
6339
|
+
for (let index = 0; index <= bytes.length - signature.length; index += 1) {
|
|
6340
|
+
let matches = true;
|
|
6341
|
+
for (let sigIndex = 0; sigIndex < signature.length; sigIndex += 1) {
|
|
6342
|
+
if (bytes[index + sigIndex] !== signature[sigIndex]) {
|
|
6343
|
+
matches = false;
|
|
6344
|
+
break;
|
|
6345
|
+
}
|
|
6346
|
+
}
|
|
6347
|
+
if (matches) {
|
|
6348
|
+
offsets.push(index);
|
|
6349
|
+
}
|
|
6350
|
+
}
|
|
6351
|
+
return offsets;
|
|
6352
|
+
}
|
|
6353
|
+
function findPngEnd(bytes, start) {
|
|
6354
|
+
for (let offset = start + PNG_SIGNATURE.length; offset + 12 <= bytes.length; ) {
|
|
6355
|
+
const length = readUint32Be4(bytes, offset);
|
|
6356
|
+
const typeOffset = offset + 4;
|
|
6357
|
+
const next = offset + 12 + length;
|
|
6358
|
+
if (next > bytes.length) {
|
|
6359
|
+
return 0;
|
|
6360
|
+
}
|
|
6361
|
+
if (bytes[typeOffset] === 73 && bytes[typeOffset + 1] === 69 && bytes[typeOffset + 2] === 78 && bytes[typeOffset + 3] === 68) {
|
|
6362
|
+
return next;
|
|
6363
|
+
}
|
|
6364
|
+
offset = next;
|
|
6365
|
+
}
|
|
6366
|
+
return 0;
|
|
6367
|
+
}
|
|
6368
|
+
function findJpegEnd(bytes, start) {
|
|
6369
|
+
for (let index = start + 2; index + 1 < bytes.length; index += 1) {
|
|
6370
|
+
if (bytes[index] === 255 && bytes[index + 1] === 217) {
|
|
6371
|
+
return index + 2;
|
|
6372
|
+
}
|
|
6373
|
+
}
|
|
6374
|
+
return 0;
|
|
6375
|
+
}
|
|
6376
|
+
function readPngDimensions(bytes) {
|
|
6377
|
+
if (bytes.length < 24 || !PNG_SIGNATURE.every((value, index) => bytes[index] === value)) {
|
|
6378
|
+
return void 0;
|
|
6379
|
+
}
|
|
6380
|
+
return {
|
|
6381
|
+
width: readUint32Be4(bytes, 16),
|
|
6382
|
+
height: readUint32Be4(bytes, 20)
|
|
6383
|
+
};
|
|
6384
|
+
}
|
|
6385
|
+
function readUint32Be4(bytes, offset) {
|
|
6386
|
+
return (bytes[offset] << 24 | bytes[offset + 1] << 16 | bytes[offset + 2] << 8 | bytes[offset + 3]) >>> 0;
|
|
6387
|
+
}
|
|
6388
|
+
function bytesToBase64(bytes) {
|
|
6389
|
+
let binary = "";
|
|
6390
|
+
const chunkSize = 32768;
|
|
6391
|
+
for (let offset = 0; offset < bytes.length; offset += chunkSize) {
|
|
6392
|
+
binary += String.fromCharCode(...bytes.subarray(offset, Math.min(bytes.length, offset + chunkSize)));
|
|
6393
|
+
}
|
|
6394
|
+
return btoa(binary);
|
|
6395
|
+
}
|
|
6396
|
+
function parseCompoundFile(bytes) {
|
|
6397
|
+
if (!hasCompoundSignature(bytes)) {
|
|
6398
|
+
throw new Error("\u4E0D\u662F\u6807\u51C6 OLE Compound File");
|
|
6399
|
+
}
|
|
6400
|
+
const view3 = dataView2(bytes);
|
|
6401
|
+
const sectorSize = 1 << view3.getUint16(30, true);
|
|
6402
|
+
const miniSectorSize = 1 << view3.getUint16(32, true);
|
|
6403
|
+
const fatSectorCount = view3.getUint32(44, true);
|
|
6404
|
+
const firstDirectorySector = view3.getUint32(48, true);
|
|
6405
|
+
const miniStreamCutoff = view3.getUint32(56, true) || MINI_STREAM_CUTOFF;
|
|
6406
|
+
const firstMiniFatSector = view3.getUint32(60, true);
|
|
6407
|
+
const miniFatSectorCount = view3.getUint32(64, true);
|
|
6408
|
+
const firstDifatSector = view3.getUint32(68, true);
|
|
6409
|
+
const difatSectorCount = view3.getUint32(72, true);
|
|
6410
|
+
const difat = readDifat(view3, sectorSize, firstDifatSector, difatSectorCount);
|
|
6411
|
+
const fat = readFat(view3, sectorSize, difat.slice(0, fatSectorCount));
|
|
6412
|
+
const directoryBytes = readRegularStream(bytes, sectorSize, fat, firstDirectorySector);
|
|
6413
|
+
const entries = parseDirectoryEntries(directoryBytes);
|
|
6414
|
+
const root = entries.find((entry) => entry.type === 5);
|
|
6415
|
+
const miniStream = root ? readRegularStream(bytes, sectorSize, fat, root.startSector, root.size) : new Uint8Array();
|
|
6416
|
+
const miniFat = firstMiniFatSector < END_OF_CHAIN ? readFat(view3, sectorSize, sectorChain(fat, firstMiniFatSector).slice(0, miniFatSectorCount)) : [];
|
|
6417
|
+
return {
|
|
6418
|
+
entries,
|
|
6419
|
+
getStream(name) {
|
|
6420
|
+
const wanted = normalizeStreamName(name);
|
|
6421
|
+
const entry = entries.find((item) => item.type === 2 && normalizeStreamName(item.name) === wanted);
|
|
6422
|
+
if (!entry) {
|
|
6423
|
+
return void 0;
|
|
6424
|
+
}
|
|
6425
|
+
if (entry.size < miniStreamCutoff && miniFat.length > 0) {
|
|
6426
|
+
return readMiniStream(miniStream, miniSectorSize, miniFat, entry.startSector, entry.size);
|
|
6427
|
+
}
|
|
6428
|
+
return readRegularStream(bytes, sectorSize, fat, entry.startSector, entry.size);
|
|
6429
|
+
}
|
|
6430
|
+
};
|
|
6431
|
+
}
|
|
6432
|
+
function hasCompoundSignature(bytes) {
|
|
6433
|
+
return CFB_SIGNATURE.every((value, index) => bytes[index] === value);
|
|
6434
|
+
}
|
|
6435
|
+
function readDifat(view3, sectorSize, firstDifatSector, difatSectorCount) {
|
|
6436
|
+
const difat = [];
|
|
6437
|
+
for (let offset = 76; offset < 512; offset += 4) {
|
|
6438
|
+
const sector = view3.getUint32(offset, true);
|
|
6439
|
+
if (isUsableSector(sector)) {
|
|
6440
|
+
difat.push(sector);
|
|
6441
|
+
}
|
|
6442
|
+
}
|
|
6443
|
+
let next = firstDifatSector;
|
|
6444
|
+
for (let index = 0; index < difatSectorCount && isUsableSector(next); index += 1) {
|
|
6445
|
+
const offset = sectorOffset(next, sectorSize);
|
|
6446
|
+
const entriesPerSector = sectorSize / 4 - 1;
|
|
6447
|
+
for (let item = 0; item < entriesPerSector; item += 1) {
|
|
6448
|
+
const sector = view3.getUint32(offset + item * 4, true);
|
|
6449
|
+
if (isUsableSector(sector)) {
|
|
6450
|
+
difat.push(sector);
|
|
6451
|
+
}
|
|
6452
|
+
}
|
|
6453
|
+
next = view3.getUint32(offset + entriesPerSector * 4, true);
|
|
6454
|
+
}
|
|
6455
|
+
return difat;
|
|
6456
|
+
}
|
|
6457
|
+
function readFat(view3, sectorSize, sectors) {
|
|
6458
|
+
const fat = [];
|
|
6459
|
+
for (const sector of sectors) {
|
|
6460
|
+
if (!isUsableSector(sector) && sector !== FAT_SECTOR && sector !== DIFAT_SECTOR) {
|
|
6461
|
+
continue;
|
|
6462
|
+
}
|
|
6463
|
+
const offset = sectorOffset(sector, sectorSize);
|
|
6464
|
+
for (let item = 0; item < sectorSize / 4; item += 1) {
|
|
6465
|
+
fat.push(view3.getUint32(offset + item * 4, true));
|
|
6466
|
+
}
|
|
6467
|
+
}
|
|
6468
|
+
return fat;
|
|
6469
|
+
}
|
|
6470
|
+
function parseDirectoryEntries(bytes) {
|
|
6471
|
+
const view3 = dataView2(bytes);
|
|
6472
|
+
const entries = [];
|
|
6473
|
+
for (let offset = 0; offset + 128 <= bytes.length; offset += 128) {
|
|
6474
|
+
const nameLength = view3.getUint16(offset + 64, true);
|
|
6475
|
+
const type = bytes[offset + 66] || 0;
|
|
6476
|
+
if (type === 0 || nameLength < 2) {
|
|
6477
|
+
continue;
|
|
6478
|
+
}
|
|
6479
|
+
const nameBytes = bytes.subarray(offset, offset + Math.max(0, nameLength - 2));
|
|
6480
|
+
const name = decodeUtf16Le(nameBytes).replace(/\0+$/g, "");
|
|
6481
|
+
const startSector = view3.getUint32(offset + 116, true);
|
|
6482
|
+
const lowSize = view3.getUint32(offset + 120, true);
|
|
6483
|
+
const highSize = view3.getUint32(offset + 124, true);
|
|
6484
|
+
const size = highSize > 0 ? Number(BigInt(highSize) << 32n) + lowSize : lowSize;
|
|
6485
|
+
entries.push({ name, type, startSector, size });
|
|
6486
|
+
}
|
|
6487
|
+
return entries;
|
|
6488
|
+
}
|
|
6489
|
+
function readRegularStream(bytes, sectorSize, fat, startSector, size) {
|
|
6490
|
+
const chunks = [];
|
|
6491
|
+
for (const sector of sectorChain(fat, startSector)) {
|
|
6492
|
+
const offset = sectorOffset(sector, sectorSize);
|
|
6493
|
+
chunks.push(bytes.subarray(offset, Math.min(bytes.length, offset + sectorSize)));
|
|
6494
|
+
}
|
|
6495
|
+
return concatChunks(chunks, size);
|
|
6496
|
+
}
|
|
6497
|
+
function readMiniStream(miniStream, miniSectorSize, miniFat, startSector, size) {
|
|
6498
|
+
const chunks = [];
|
|
6499
|
+
for (const sector of sectorChain(miniFat, startSector)) {
|
|
6500
|
+
const offset = sector * miniSectorSize;
|
|
6501
|
+
chunks.push(miniStream.subarray(offset, Math.min(miniStream.length, offset + miniSectorSize)));
|
|
6502
|
+
}
|
|
6503
|
+
return concatChunks(chunks, size);
|
|
6504
|
+
}
|
|
6505
|
+
function sectorChain(fat, startSector) {
|
|
6506
|
+
const chain = [];
|
|
6507
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6508
|
+
let current = startSector;
|
|
6509
|
+
while (isUsableSector(current) && current < fat.length && !seen.has(current)) {
|
|
6510
|
+
seen.add(current);
|
|
6511
|
+
chain.push(current);
|
|
6512
|
+
current = fat[current];
|
|
6513
|
+
}
|
|
6514
|
+
return chain;
|
|
6515
|
+
}
|
|
6516
|
+
function parseFib(wordDocument) {
|
|
6517
|
+
const view3 = dataView2(wordDocument);
|
|
6518
|
+
if (view3.getUint16(0, true) !== 42476) {
|
|
6519
|
+
throw new Error("WordDocument FIB \u6807\u8BC6\u65E0\u6548");
|
|
6520
|
+
}
|
|
6521
|
+
const flags = view3.getUint16(10, true);
|
|
6522
|
+
const fcMin = view3.getUint32(24, true);
|
|
6523
|
+
const fcMac = view3.getUint32(28, true);
|
|
6524
|
+
let offset = 32;
|
|
6525
|
+
const csw = view3.getUint16(offset, true);
|
|
6526
|
+
offset += 2 + csw * 2;
|
|
6527
|
+
const fibRgLwOffset = offset + 2;
|
|
6528
|
+
const cslw = view3.getUint16(offset, true);
|
|
6529
|
+
const ccpText = fibRgLwOffset + 16 <= wordDocument.length ? Math.max(0, view3.getInt32(fibRgLwOffset + 12, true)) : 0;
|
|
6530
|
+
offset += 2 + cslw * 4;
|
|
6531
|
+
const cbRgFcLcb = view3.getUint16(offset, true);
|
|
6532
|
+
const fcLcbOffset = offset + 2;
|
|
6533
|
+
const stshOffset = fcLcbOffset + STSH_FC_LCB_INDEX * 8;
|
|
6534
|
+
const clxOffset = fcLcbOffset + CLX_FC_LCB_INDEX * 8;
|
|
6535
|
+
return {
|
|
6536
|
+
encrypted: (flags & 256) !== 0,
|
|
6537
|
+
useOneTable: (flags & 512) !== 0,
|
|
6538
|
+
textIsUnicode: (flags & 4096) !== 0,
|
|
6539
|
+
fcMin,
|
|
6540
|
+
fcMac,
|
|
6541
|
+
ccpText,
|
|
6542
|
+
fcStshf: STSH_FC_LCB_INDEX < cbRgFcLcb && stshOffset + 8 <= wordDocument.length ? view3.getUint32(stshOffset, true) : 0,
|
|
6543
|
+
lcbStshf: STSH_FC_LCB_INDEX < cbRgFcLcb && stshOffset + 8 <= wordDocument.length ? view3.getUint32(stshOffset + 4, true) : 0,
|
|
6544
|
+
fcClx: CLX_FC_LCB_INDEX < cbRgFcLcb && clxOffset + 8 <= wordDocument.length ? view3.getUint32(clxOffset, true) : 0,
|
|
6545
|
+
lcbClx: CLX_FC_LCB_INDEX < cbRgFcLcb && clxOffset + 8 <= wordDocument.length ? view3.getUint32(clxOffset + 4, true) : 0
|
|
6546
|
+
};
|
|
6547
|
+
}
|
|
6548
|
+
function parseClxPieces(tableStream, fcClx, lcbClx) {
|
|
6549
|
+
if (lcbClx <= 0 || fcClx < 0 || fcClx >= tableStream.length) {
|
|
6550
|
+
return [];
|
|
6551
|
+
}
|
|
6552
|
+
const end = Math.min(tableStream.length, fcClx + lcbClx);
|
|
6553
|
+
const view3 = dataView2(tableStream);
|
|
6554
|
+
let offset = fcClx;
|
|
6555
|
+
while (offset < end) {
|
|
6556
|
+
const marker = tableStream[offset];
|
|
6557
|
+
if (marker === 1) {
|
|
6558
|
+
const size = offset + 3 <= end ? view3.getUint16(offset + 1, true) : 0;
|
|
6559
|
+
offset += 3 + size;
|
|
6560
|
+
continue;
|
|
6561
|
+
}
|
|
6562
|
+
if (marker === 2) {
|
|
6563
|
+
if (offset + 5 > end) {
|
|
6564
|
+
break;
|
|
6565
|
+
}
|
|
6566
|
+
const plcSize = view3.getUint32(offset + 1, true);
|
|
6567
|
+
const plcOffset = offset + 5;
|
|
6568
|
+
return parsePlcPcd(tableStream, plcOffset, Math.min(end, plcOffset + plcSize));
|
|
6569
|
+
}
|
|
6570
|
+
offset += 1;
|
|
6571
|
+
}
|
|
6572
|
+
return [];
|
|
6573
|
+
}
|
|
6574
|
+
function parsePlcPcd(tableStream, offset, end) {
|
|
6575
|
+
const size = end - offset;
|
|
6576
|
+
if (size < 16 || (size - 4) % 12 !== 0) {
|
|
6577
|
+
return [];
|
|
6578
|
+
}
|
|
6579
|
+
const view3 = dataView2(tableStream);
|
|
6580
|
+
const pieceCount = Math.floor((size - 4) / 12);
|
|
6581
|
+
const pcdOffset = offset + (pieceCount + 1) * 4;
|
|
6582
|
+
const pieces = [];
|
|
6583
|
+
for (let index = 0; index < pieceCount; index += 1) {
|
|
6584
|
+
const cpStart = view3.getUint32(offset + index * 4, true);
|
|
6585
|
+
const cpEnd = view3.getUint32(offset + (index + 1) * 4, true);
|
|
6586
|
+
const descriptorOffset = pcdOffset + index * 8;
|
|
6587
|
+
const fcCompressed = view3.getUint32(descriptorOffset + 2, true);
|
|
6588
|
+
const compressed = (fcCompressed & 1073741824) !== 0;
|
|
6589
|
+
const fileOffset = compressed ? (fcCompressed & 1073741823) / 2 : fcCompressed;
|
|
6590
|
+
if (cpEnd > cpStart) {
|
|
6591
|
+
pieces.push({ cpStart, cpEnd, fileOffset, compressed });
|
|
6592
|
+
}
|
|
6593
|
+
}
|
|
6594
|
+
return pieces;
|
|
6595
|
+
}
|
|
6596
|
+
function readPieceTableText(wordDocument, pieces, ccpText) {
|
|
6597
|
+
let output = "";
|
|
6598
|
+
for (const piece of pieces) {
|
|
6599
|
+
const cpEnd = ccpText > 0 ? Math.min(piece.cpEnd, ccpText) : piece.cpEnd;
|
|
6600
|
+
const charCount = Math.max(0, cpEnd - piece.cpStart);
|
|
6601
|
+
if (charCount === 0) {
|
|
6602
|
+
continue;
|
|
6603
|
+
}
|
|
6604
|
+
const byteLength = charCount * (piece.compressed ? 1 : 2);
|
|
6605
|
+
const bytes = wordDocument.subarray(piece.fileOffset, Math.min(wordDocument.length, piece.fileOffset + byteLength));
|
|
6606
|
+
output += piece.compressed ? decodeWindows1252(bytes) : decodeUtf16Le(bytes);
|
|
6607
|
+
}
|
|
6608
|
+
return output;
|
|
6609
|
+
}
|
|
6610
|
+
function readFibTextFallback(wordDocument, fib) {
|
|
6611
|
+
const bytes = wordDocument.subarray(fib.fcMin, Math.min(wordDocument.length, fib.fcMac));
|
|
6612
|
+
return fib.textIsUnicode ? decodeUtf16Le(bytes) : decodeWindows1252(bytes);
|
|
6613
|
+
}
|
|
6614
|
+
function splitWordParagraphs(text) {
|
|
6615
|
+
const normalized = text.replace(/\u0000/g, "").replace(/\u0007/g, " ").replace(/\u000b/g, "\n");
|
|
6616
|
+
const paragraphs = [];
|
|
6617
|
+
for (const segment of normalized.split(/(\u000c)/)) {
|
|
6618
|
+
if (segment === "\f") {
|
|
6619
|
+
paragraphs.push(WORD_PAGE_BREAK);
|
|
6620
|
+
continue;
|
|
6621
|
+
}
|
|
6622
|
+
paragraphs.push(
|
|
6623
|
+
...segment.split(/\r|\n{2,}/).map((paragraph) => cleanWordText(paragraph)).filter((paragraph) => paragraph.length > 0 && isDisplayableParagraph(paragraph))
|
|
6624
|
+
);
|
|
6625
|
+
}
|
|
6626
|
+
return paragraphs.slice(0, 1e3);
|
|
6627
|
+
}
|
|
6628
|
+
function removeTrailingFooterArtifacts(paragraphs) {
|
|
6629
|
+
const tailStart = Math.max(0, paragraphs.length - 32);
|
|
6630
|
+
const tail = paragraphs.slice(tailStart);
|
|
6631
|
+
const relativePageFieldIndex = tail.findIndex(isFooterPageField);
|
|
6632
|
+
if (relativePageFieldIndex < 0) {
|
|
6633
|
+
return paragraphs;
|
|
6634
|
+
}
|
|
6635
|
+
let start = tailStart + relativePageFieldIndex;
|
|
6636
|
+
for (let index = start - 1; index >= tailStart; index -= 1) {
|
|
6637
|
+
const paragraph = paragraphs[index];
|
|
6638
|
+
if (paragraph === WORD_PAGE_BREAK || isLikelyFooterArtifact(paragraph)) {
|
|
6639
|
+
start = index;
|
|
6640
|
+
continue;
|
|
6641
|
+
}
|
|
6642
|
+
break;
|
|
6643
|
+
}
|
|
6644
|
+
const artifactSlice = paragraphs.slice(start);
|
|
6645
|
+
const footerCueCount = artifactSlice.filter(isLikelyFooterArtifact).length;
|
|
6646
|
+
if (footerCueCount < 2) {
|
|
6647
|
+
return paragraphs;
|
|
6648
|
+
}
|
|
6649
|
+
return paragraphs.slice(0, start);
|
|
6650
|
+
}
|
|
6651
|
+
function isFooterPageField(paragraph) {
|
|
6652
|
+
return /^(?:PAGE|Page)(?:\s+(?:PAGE|\d+))?(?:\s+of\s+(?:NUMPAGES|\d+))?$/i.test(paragraph.trim()) || /\bNUMPAGES\b/i.test(paragraph);
|
|
6653
|
+
}
|
|
6654
|
+
function isLikelyFooterArtifact(paragraph) {
|
|
6655
|
+
const value = paragraph.trim();
|
|
6656
|
+
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);
|
|
6657
|
+
}
|
|
6658
|
+
function buildWordBlocks(paragraphs) {
|
|
6659
|
+
const blocks = [];
|
|
6660
|
+
let index = 0;
|
|
6661
|
+
while (index < paragraphs.length) {
|
|
6662
|
+
const paragraph = paragraphs[index];
|
|
6663
|
+
if (paragraph === WORD_PAGE_BREAK) {
|
|
6664
|
+
blocks.push({ type: "pageBreak" });
|
|
6665
|
+
index += 1;
|
|
6666
|
+
continue;
|
|
6667
|
+
}
|
|
6668
|
+
const toc = parseTocEntry(paragraph);
|
|
6669
|
+
if (toc) {
|
|
6670
|
+
blocks.push(toc);
|
|
6671
|
+
index += 1;
|
|
6672
|
+
continue;
|
|
6673
|
+
}
|
|
6674
|
+
if (isTableRowCandidate(paragraph)) {
|
|
6675
|
+
const rows = [];
|
|
6676
|
+
while (index < paragraphs.length && isTableRowCandidate(paragraphs[index])) {
|
|
6677
|
+
rows.push(...normalizeTableRows(splitTableRow(paragraphs[index])));
|
|
6678
|
+
index += 1;
|
|
6679
|
+
}
|
|
6680
|
+
blocks.push({ type: "table", rows });
|
|
6681
|
+
continue;
|
|
6682
|
+
}
|
|
6683
|
+
blocks.push(classifyParagraphBlock(paragraph, blocks));
|
|
6684
|
+
index += 1;
|
|
6685
|
+
}
|
|
6686
|
+
return blocks;
|
|
6687
|
+
}
|
|
6688
|
+
function inferLayoutHints(paragraphs, assets) {
|
|
6689
|
+
const sample = paragraphs.slice(0, 40).join("\n");
|
|
6690
|
+
const isOasisSpec = /Word Specification Sample/.test(sample) && /\bOASIS\b/i.test(paragraphs.join("\n"));
|
|
6691
|
+
const oasisImage = isOasisSpec ? assets.find((asset) => asset.mimeType === "image/png" && asset.width && asset.height && asset.width / asset.height > 2.5) : void 0;
|
|
6692
|
+
return {
|
|
6693
|
+
lineNumbers: isOasisSpec,
|
|
6694
|
+
headerBrand: isOasisSpec ? "oasis" : void 0,
|
|
6695
|
+
headerImageId: oasisImage?.id,
|
|
6696
|
+
footer: isOasisSpec ? inferOasisFooter(paragraphs) : void 0
|
|
6697
|
+
};
|
|
6698
|
+
}
|
|
6699
|
+
function inferOasisFooter(paragraphs) {
|
|
6700
|
+
const documentId = findValueAfterLabel(paragraphs, "Document identifier:") || paragraphs.find((paragraph) => /^wd-[\w.-]+/i.test(paragraph));
|
|
6701
|
+
const subtitle = paragraphs.find((paragraph) => /\b(?:draft|version)\b/i.test(paragraph) && /\d{4}/.test(paragraph));
|
|
6702
|
+
const date = subtitle?.match(/\b\d{1,2}\s+[A-Za-z]+\s+\d{4}\b/)?.[0];
|
|
6703
|
+
const copyright = paragraphs.find((paragraph) => /Copyright.*OASIS.*All Rights Reserved/i.test(paragraph)) || "Copyright \xA9 OASIS Open 2002. All Rights Reserved.";
|
|
6704
|
+
return { documentId, date, copyright };
|
|
6705
|
+
}
|
|
6706
|
+
function findValueAfterLabel(paragraphs, label) {
|
|
6707
|
+
const index = paragraphs.findIndex((paragraph) => paragraph.toLowerCase() === label.toLowerCase());
|
|
6708
|
+
if (index < 0) {
|
|
6709
|
+
return void 0;
|
|
6710
|
+
}
|
|
6711
|
+
return paragraphs.slice(index + 1).find((paragraph) => paragraph !== WORD_PAGE_BREAK && paragraph.length > 0);
|
|
6712
|
+
}
|
|
6713
|
+
function paginateWordBlocks(blocks, layout) {
|
|
6714
|
+
const maxLines = layout.lineNumbers ? 33 : 46;
|
|
6715
|
+
const pages = [];
|
|
6716
|
+
let current = [];
|
|
6717
|
+
let usedLines = 0;
|
|
6718
|
+
for (const block of blocks) {
|
|
6719
|
+
if (block.type === "pageBreak") {
|
|
6720
|
+
if (current.length > 0) {
|
|
6721
|
+
pages.push(current);
|
|
6722
|
+
}
|
|
6723
|
+
current = [];
|
|
6724
|
+
usedLines = 0;
|
|
6725
|
+
continue;
|
|
6726
|
+
}
|
|
6727
|
+
const lines = estimatedLineCount(block);
|
|
6728
|
+
const shouldBreak = current.length > 0 && (usedLines + lines > maxLines || block.type === "heading" && usedLines > Math.floor(maxLines * 0.72));
|
|
6729
|
+
if (shouldBreak) {
|
|
6730
|
+
pages.push(current);
|
|
6731
|
+
current = [];
|
|
6732
|
+
usedLines = 0;
|
|
6733
|
+
}
|
|
6734
|
+
current.push(block);
|
|
6735
|
+
usedLines += lines;
|
|
6736
|
+
}
|
|
6737
|
+
if (current.length > 0 || pages.length === 0) {
|
|
6738
|
+
pages.push(current);
|
|
6739
|
+
}
|
|
6740
|
+
return pages;
|
|
6741
|
+
}
|
|
6742
|
+
function estimatedLineCount(block) {
|
|
6743
|
+
if (block.type === "pageBreak") {
|
|
6744
|
+
return 0;
|
|
6745
|
+
}
|
|
6746
|
+
if (block.type === "table") {
|
|
6747
|
+
return Math.max(1, block.rows.length);
|
|
6748
|
+
}
|
|
6749
|
+
if (block.type === "toc") {
|
|
6750
|
+
return 1;
|
|
6751
|
+
}
|
|
6752
|
+
const baseWidth = "indent" in block && block.indent ? 78 : 96;
|
|
6753
|
+
return Math.max(1, Math.ceil(block.text.length / baseWidth));
|
|
6754
|
+
}
|
|
6755
|
+
function classifyParagraphBlock(text, previousBlocks) {
|
|
6756
|
+
const visibleIndex = previousBlocks.filter((block) => block.type !== "toc").length;
|
|
6757
|
+
if (visibleIndex === 0 && text.length <= 140) {
|
|
6758
|
+
return { type: "title", text };
|
|
6759
|
+
}
|
|
6760
|
+
if (visibleIndex === 1 && /draft|version|20\d{2}|19\d{2}/i.test(text) && text.length <= 140) {
|
|
6761
|
+
return { type: "subtitle", text };
|
|
6762
|
+
}
|
|
6763
|
+
const headingLevel = inferHeadingLevel(text, previousBlocks);
|
|
6764
|
+
if (headingLevel) {
|
|
6765
|
+
return { type: "heading", text, level: headingLevel };
|
|
6766
|
+
}
|
|
6767
|
+
if (/^[\w\s/().-]{2,45}:$/.test(text)) {
|
|
6768
|
+
return { type: "label", text };
|
|
6769
|
+
}
|
|
6770
|
+
if (isInstructionParagraph(text)) {
|
|
6771
|
+
return { type: "instruction", text, indent: shouldIndentParagraph(previousBlocks) };
|
|
6772
|
+
}
|
|
6773
|
+
if (/^\[[-\w.]+\]\s+/.test(text)) {
|
|
6774
|
+
return { type: "reference", text };
|
|
6775
|
+
}
|
|
6776
|
+
const listLevel = inferListItemLevel(text);
|
|
6777
|
+
if (listLevel) {
|
|
6778
|
+
return { type: "listItem", text, level: listLevel };
|
|
6779
|
+
}
|
|
6780
|
+
if (isCodeLikeParagraph(text)) {
|
|
6781
|
+
return { type: "code", text, indent: shouldIndentParagraph(previousBlocks) };
|
|
6782
|
+
}
|
|
6783
|
+
return { type: "paragraph", text, indent: shouldIndentParagraph(previousBlocks) };
|
|
6784
|
+
}
|
|
6785
|
+
function isInstructionParagraph(text) {
|
|
6786
|
+
return /^\[[^\]]{8,}\]$/.test(text.trim());
|
|
6787
|
+
}
|
|
6788
|
+
function inferHeadingLevel(text, previousBlocks) {
|
|
6789
|
+
if (text.length > 120) {
|
|
6790
|
+
return void 0;
|
|
6791
|
+
}
|
|
6792
|
+
if (/^table of contents$/i.test(text)) {
|
|
6793
|
+
return 1;
|
|
6794
|
+
}
|
|
6795
|
+
if (/^(?:introduction|word styles|references|appendix\b.*|acknowledgments|revision history|notices)$/i.test(text)) {
|
|
6796
|
+
return 1;
|
|
6797
|
+
}
|
|
6798
|
+
const numbered = text.match(/^([1-9](?:\.\d+)*)\s+.+/);
|
|
6799
|
+
if (numbered) {
|
|
6800
|
+
return Math.min(3, numbered[1].split(".").length);
|
|
6801
|
+
}
|
|
6802
|
+
if (/^(?:terminology|overall style|title page|headings|paragraphs|lists|tables|code examples|character styles|normative)$/i.test(text)) {
|
|
6803
|
+
return 2;
|
|
6804
|
+
}
|
|
6805
|
+
const previousHeading = [...previousBlocks].reverse().find((block) => block.type === "heading");
|
|
6806
|
+
if (previousHeading?.type === "heading" && previousHeading.level === 1 && /^[A-Z][A-Za-z0-9 ()/-]{2,80}$/.test(text)) {
|
|
6807
|
+
return 2;
|
|
6808
|
+
}
|
|
6809
|
+
return void 0;
|
|
6810
|
+
}
|
|
6811
|
+
function shouldIndentParagraph(previousBlocks) {
|
|
6812
|
+
for (let index = previousBlocks.length - 1; index >= 0; index -= 1) {
|
|
6813
|
+
const block = previousBlocks[index];
|
|
6814
|
+
if (block.type === "toc" || block.type === "table") {
|
|
6815
|
+
continue;
|
|
6816
|
+
}
|
|
6817
|
+
if (block.type === "label") {
|
|
6818
|
+
return true;
|
|
6819
|
+
}
|
|
6820
|
+
if ((block.type === "paragraph" || block.type === "code") && block.indent) {
|
|
6821
|
+
return true;
|
|
6822
|
+
}
|
|
6823
|
+
return false;
|
|
6824
|
+
}
|
|
6825
|
+
return false;
|
|
6826
|
+
}
|
|
6827
|
+
function inferListItemLevel(text) {
|
|
6828
|
+
if (/^(?:list bullet|definition term)$/i.test(text)) {
|
|
6829
|
+
return 1;
|
|
6830
|
+
}
|
|
6831
|
+
if (/^(?:list bullet 2|list continue 2|definition for the term\.)$/i.test(text)) {
|
|
6832
|
+
return 2;
|
|
6833
|
+
}
|
|
6834
|
+
return void 0;
|
|
6835
|
+
}
|
|
6836
|
+
function parseTocEntry(text) {
|
|
6837
|
+
const tabCells = splitTableRow(text);
|
|
6838
|
+
if (tabCells.length >= 2 && /^\d{1,3}$/.test(tabCells[tabCells.length - 1] || "")) {
|
|
6839
|
+
const title2 = tabCells.slice(0, -1).join(" ").trim();
|
|
6840
|
+
if (isLikelyTocTitle(title2)) {
|
|
6841
|
+
const number2 = title2.match(/^(\d+(?:\.\d+)*)\b/)?.[1] || "";
|
|
6842
|
+
return { type: "toc", title: title2, page: tabCells[tabCells.length - 1], level: number2.includes(".") ? Math.min(3, number2.split(".").length) : 1 };
|
|
6843
|
+
}
|
|
6844
|
+
}
|
|
6845
|
+
const cleaned = text.replace(/\s+/g, " ").trim();
|
|
6846
|
+
const match = cleaned.match(/^(?:(\d+(?:\.\d+)*)\s+)?(.+?)\s+(\d{1,3})$/);
|
|
6847
|
+
if (!match || cleaned.length > 140) {
|
|
6848
|
+
return void 0;
|
|
6849
|
+
}
|
|
6850
|
+
const number = match[1] || "";
|
|
6851
|
+
const title = `${number ? `${number} ` : ""}${match[2] || ""}`.trim();
|
|
6852
|
+
const page = match[3];
|
|
6853
|
+
if (!title || !page || !/^(?:appendix\b|references\b|introduction\b|[A-Z0-9])/i.test(title)) {
|
|
6854
|
+
return void 0;
|
|
6855
|
+
}
|
|
6856
|
+
if (!isLikelyTocTitle(title)) {
|
|
6857
|
+
return void 0;
|
|
6858
|
+
}
|
|
6859
|
+
const level = number.includes(".") ? Math.min(3, number.split(".").length) : 1;
|
|
6860
|
+
return { type: "toc", title, page, level };
|
|
6861
|
+
}
|
|
6862
|
+
function isLikelyTocTitle(title) {
|
|
6863
|
+
return /^(?:appendix\b|references\b|introduction\b|[1-9](?:\.\d+)*\b|[A-Z][\w\s.-]{2,80}$)/i.test(title);
|
|
6864
|
+
}
|
|
6865
|
+
function isTableRowCandidate(text) {
|
|
6866
|
+
if (/^\[[-\w.]+\]\t+/.test(text)) {
|
|
6867
|
+
return false;
|
|
6868
|
+
}
|
|
6869
|
+
const cells = splitTableRow(text);
|
|
6870
|
+
return cells.length >= 2 && cells.some((cell) => cell.length > 0);
|
|
6871
|
+
}
|
|
6872
|
+
function splitTableRow(text) {
|
|
6873
|
+
return text.split(/\t+/).map((cell) => cell.trim()).filter(Boolean);
|
|
6874
|
+
}
|
|
6875
|
+
function normalizeTableRows(cells) {
|
|
6876
|
+
if (cells.length >= 8) {
|
|
6877
|
+
const columnCount = inferTableColumnCount(cells);
|
|
6878
|
+
if (columnCount > 1 && cells.length % columnCount === 0) {
|
|
6879
|
+
const rows = [];
|
|
6880
|
+
for (let offset = 0; offset < cells.length; offset += columnCount) {
|
|
6881
|
+
rows.push(cells.slice(offset, offset + columnCount));
|
|
6882
|
+
}
|
|
6883
|
+
return rows;
|
|
6884
|
+
}
|
|
6885
|
+
}
|
|
6886
|
+
return [cells];
|
|
6887
|
+
}
|
|
6888
|
+
function inferTableColumnCount(cells) {
|
|
6889
|
+
const header = cells.slice(0, 6).join(" ").toLowerCase();
|
|
6890
|
+
if (/\brev\b/.test(header) && /\bdate\b/.test(header) && /whom|what/.test(header)) {
|
|
6891
|
+
return 4;
|
|
6892
|
+
}
|
|
6893
|
+
for (const candidate of [5, 4, 3, 2]) {
|
|
6894
|
+
if (cells.length % candidate === 0) {
|
|
6895
|
+
return candidate;
|
|
6896
|
+
}
|
|
6897
|
+
}
|
|
6898
|
+
return 0;
|
|
6899
|
+
}
|
|
6900
|
+
function isCodeLikeParagraph(text) {
|
|
6901
|
+
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);
|
|
6902
|
+
}
|
|
6903
|
+
function cleanWordText(value) {
|
|
6904
|
+
return stripWordFieldCodes(value).replace(/[\u0001-\u0006\u0008\u000e-\u001f]/g, "").replace(/ {2,}/g, " ").trim();
|
|
6905
|
+
}
|
|
6906
|
+
function stripWordFieldCodes(value) {
|
|
6907
|
+
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, "");
|
|
6908
|
+
}
|
|
6909
|
+
function isDisplayableParagraph(value) {
|
|
6910
|
+
if (value.length < 2) {
|
|
6911
|
+
return false;
|
|
6912
|
+
}
|
|
6913
|
+
const letters = [...value].filter((char) => /[\p{L}\p{N}]/u.test(char)).length;
|
|
6914
|
+
return letters >= Math.min(2, value.length);
|
|
6915
|
+
}
|
|
6916
|
+
function inferDocumentTitle(paragraphs) {
|
|
6917
|
+
return paragraphs.find((paragraph) => paragraph.length <= 120) || "Word \u6587\u6863";
|
|
6918
|
+
}
|
|
6919
|
+
function appendMeta4(list, label, value) {
|
|
6920
|
+
const term = window.document.createElement("dt");
|
|
6921
|
+
term.textContent = label;
|
|
6922
|
+
const detail = window.document.createElement("dd");
|
|
6923
|
+
detail.textContent = value;
|
|
6924
|
+
list.append(term, detail);
|
|
6925
|
+
}
|
|
6926
|
+
function concatChunks(chunks, size) {
|
|
6927
|
+
const total = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
6928
|
+
const output = new Uint8Array(size === void 0 ? total : Math.min(total, size));
|
|
6929
|
+
let offset = 0;
|
|
6930
|
+
for (const chunk of chunks) {
|
|
6931
|
+
const slice = chunk.subarray(0, Math.min(chunk.length, output.length - offset));
|
|
6932
|
+
output.set(slice, offset);
|
|
6933
|
+
offset += slice.length;
|
|
6934
|
+
if (offset >= output.length) {
|
|
6935
|
+
break;
|
|
6936
|
+
}
|
|
6937
|
+
}
|
|
6938
|
+
return output;
|
|
6939
|
+
}
|
|
6940
|
+
function alignEven(value) {
|
|
6941
|
+
return value % 2 === 0 ? value : value + 1;
|
|
6942
|
+
}
|
|
6943
|
+
function isUsableSector(sector) {
|
|
6944
|
+
return sector !== FREE_SECTOR && sector !== END_OF_CHAIN && sector !== FAT_SECTOR && sector !== DIFAT_SECTOR;
|
|
6945
|
+
}
|
|
6946
|
+
function sectorOffset(sector, sectorSize) {
|
|
6947
|
+
return (sector + 1) * sectorSize;
|
|
6948
|
+
}
|
|
6949
|
+
function normalizeStreamName(name) {
|
|
6950
|
+
return name.replace(/^\/+/, "").toLowerCase();
|
|
6951
|
+
}
|
|
6952
|
+
function decodeUtf16Le(bytes) {
|
|
6953
|
+
return new TextDecoder("utf-16le").decode(bytes);
|
|
6954
|
+
}
|
|
6955
|
+
function decodeWindows1252(bytes) {
|
|
6956
|
+
try {
|
|
6957
|
+
return new TextDecoder("windows-1252").decode(bytes);
|
|
6958
|
+
} catch {
|
|
6959
|
+
return Array.from(bytes, (byte) => String.fromCharCode(byte)).join("");
|
|
6960
|
+
}
|
|
6961
|
+
}
|
|
6962
|
+
function dataView2(bytes) {
|
|
6963
|
+
return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
6964
|
+
}
|
|
6965
|
+
|
|
6966
|
+
// src/plugins/office.ts
|
|
5812
6967
|
var wordExtensions = /* @__PURE__ */ new Set(["docx", "docm", "doc", "dotx", "dotm", "dot", "rtf", "odt", "fodt", "wps"]);
|
|
5813
6968
|
var sheetExtensions = /* @__PURE__ */ new Set(["xlsx", "xls", "xlsm", "xlsb", "xlt", "xltx", "xltm", "csv", "tsv", "ods", "fods", "numbers", "et"]);
|
|
5814
6969
|
var presentationExtensions = /* @__PURE__ */ new Set(["pptx", "pptm", "ppt", "pps", "ppsx", "ppsm", "potx", "potm", "odp", "fodp", "key", "dps"]);
|
|
@@ -5895,13 +7050,13 @@ function officePlugin(options = {}) {
|
|
|
5895
7050
|
if (conversionContext && await shouldUseOfficeConversion(options, conversionContext)) {
|
|
5896
7051
|
delegatedInstance = await renderConvertedOfficePreview(panel, ctx, options, conversionContext);
|
|
5897
7052
|
} else if (packageFormat === "docx" && !fileIsDocx(extension)) {
|
|
5898
|
-
disposeDocxFit = await renderDocx(panel, arrayBuffer);
|
|
7053
|
+
disposeDocxFit = await renderDocx(panel, arrayBuffer, ctx.options.fit);
|
|
5899
7054
|
} else if (packageFormat === "xlsx" && !sheetExtensions.has(extension)) {
|
|
5900
7055
|
await renderSheet(panel, arrayBuffer, "xlsx");
|
|
5901
7056
|
} else if (packageFormat === "pptx" && !["pptx", "pptm", "ppsx", "ppsm", "potx", "potm"].includes(extension)) {
|
|
5902
7057
|
await renderPptx(panel, arrayBuffer);
|
|
5903
7058
|
} else if (fileIsDocx(extension)) {
|
|
5904
|
-
disposeDocxFit = await renderDocx(panel, arrayBuffer);
|
|
7059
|
+
disposeDocxFit = await renderDocx(panel, arrayBuffer, ctx.options.fit);
|
|
5905
7060
|
} else if (extension === "rtf") {
|
|
5906
7061
|
renderPlainDocument(panel, "RTF \u6587\u6863", rtfToText(await readTextFromBuffer(arrayBuffer)));
|
|
5907
7062
|
} else if (extension === "odt") {
|
|
@@ -5910,7 +7065,7 @@ function officePlugin(options = {}) {
|
|
|
5910
7065
|
renderOpenDocumentXml(panel, "FODT \u6587\u6863", await readTextFromBuffer(arrayBuffer));
|
|
5911
7066
|
} else if (extension === "fods") {
|
|
5912
7067
|
renderFlatOds(panel, await readTextFromBuffer(arrayBuffer));
|
|
5913
|
-
} else if (packagedOfficeCandidates.has(extension) && await renderPackagedOfficePreview(panel, arrayBuffer, extension)) {
|
|
7068
|
+
} else if (packagedOfficeCandidates.has(extension) && await renderPackagedOfficePreview(panel, arrayBuffer, extension, ctx.options.fit)) {
|
|
5914
7069
|
} else if (sheetExtensions.has(extension)) {
|
|
5915
7070
|
await renderSheet(panel, arrayBuffer, extension);
|
|
5916
7071
|
} else if (["pptx", "pptm", "ppsx", "ppsm", "potx", "potm"].includes(extension)) {
|
|
@@ -5919,6 +7074,8 @@ function officePlugin(options = {}) {
|
|
|
5919
7074
|
await renderOdp(panel, arrayBuffer);
|
|
5920
7075
|
} else if (extension === "fodp") {
|
|
5921
7076
|
renderOpenDocumentPresentationXml(panel, await readTextFromBuffer(arrayBuffer));
|
|
7077
|
+
} else if (extension === "doc" || extension === "dot") {
|
|
7078
|
+
renderLegacyWordBinary(panel, extension, arrayBuffer);
|
|
5922
7079
|
} else if (isLegacyOfficeBinary(extension)) {
|
|
5923
7080
|
renderLegacyOfficeBinary(panel, extension, arrayBuffer);
|
|
5924
7081
|
} else {
|
|
@@ -6112,7 +7269,7 @@ async function detectPackagedOfficeFormat(arrayBuffer) {
|
|
|
6112
7269
|
}
|
|
6113
7270
|
return void 0;
|
|
6114
7271
|
}
|
|
6115
|
-
async function renderDocx(panel, arrayBuffer) {
|
|
7272
|
+
async function renderDocx(panel, arrayBuffer, fit) {
|
|
6116
7273
|
const content = document.createElement("div");
|
|
6117
7274
|
content.className = "ofv-docx-document";
|
|
6118
7275
|
const styleContainer = document.createElement("div");
|
|
@@ -6151,7 +7308,7 @@ async function renderDocx(panel, arrayBuffer) {
|
|
|
6151
7308
|
return () => void 0;
|
|
6152
7309
|
}
|
|
6153
7310
|
panel.append(content);
|
|
6154
|
-
disposeFit = fitDocxPages(content);
|
|
7311
|
+
disposeFit = fitDocxPages(content, fit);
|
|
6155
7312
|
return () => {
|
|
6156
7313
|
disposeFit?.();
|
|
6157
7314
|
styleContainer.remove();
|
|
@@ -6749,7 +7906,8 @@ function looksLikeDocxTextboxHeading(value) {
|
|
|
6749
7906
|
return text.length > 0 && text.length <= 12 && !/[0-9@.::]/.test(text);
|
|
6750
7907
|
}
|
|
6751
7908
|
async function normalizeDocxLayout(container, arrayBuffer) {
|
|
6752
|
-
const hints = await readDocxLayoutHints(arrayBuffer);
|
|
7909
|
+
const [hints, charts] = await Promise.all([readDocxLayoutHints(arrayBuffer), readDocxCharts(arrayBuffer)]);
|
|
7910
|
+
repairDocxChartPlaceholders(container, charts);
|
|
6753
7911
|
const pages = container.querySelectorAll("section.ofv-docx");
|
|
6754
7912
|
for (const page of pages) {
|
|
6755
7913
|
repairDocxShapeFills(page);
|
|
@@ -6764,6 +7922,75 @@ async function normalizeDocxLayout(container, arrayBuffer) {
|
|
|
6764
7922
|
}
|
|
6765
7923
|
}
|
|
6766
7924
|
}
|
|
7925
|
+
async function readDocxCharts(arrayBuffer) {
|
|
7926
|
+
try {
|
|
7927
|
+
const zip = await JSZip3.loadAsync(arrayBuffer);
|
|
7928
|
+
const documentXml = await zip.file("word/document.xml")?.async("text");
|
|
7929
|
+
const documentDoc = documentXml ? parseOfficeXml(documentXml) : void 0;
|
|
7930
|
+
if (!documentDoc) {
|
|
7931
|
+
return [];
|
|
7932
|
+
}
|
|
7933
|
+
const relationships = await readOfficeRelationships(zip, "word/document.xml");
|
|
7934
|
+
const chartDrawings = Array.from(documentDoc.getElementsByTagName("*")).filter((element) => element.localName === "inline" || element.localName === "anchor").map((element) => readDocxChartDrawing(element)).filter((item) => Boolean(item?.relationshipId));
|
|
7935
|
+
const charts = [];
|
|
7936
|
+
for (const [index, drawing] of chartDrawings.entries()) {
|
|
7937
|
+
const chartRel = relationships.find((rel) => rel.id === drawing.relationshipId && /\/chart$/i.test(rel.type));
|
|
7938
|
+
const chartPath = resolveOfficeRelationshipTarget("word/document.xml", chartRel?.target);
|
|
7939
|
+
const chartXml = chartPath ? await zip.file(chartPath)?.async("text") : void 0;
|
|
7940
|
+
const chart = chartXml ? parseChartXml(chartXml, chartPath?.split("/").pop() || `chart${index + 1}.xml`) : null;
|
|
7941
|
+
if (chart) {
|
|
7942
|
+
charts.push({ ...chart, widthPt: drawing.widthPt, heightPt: drawing.heightPt });
|
|
7943
|
+
}
|
|
7944
|
+
}
|
|
7945
|
+
return charts;
|
|
7946
|
+
} catch {
|
|
7947
|
+
return [];
|
|
7948
|
+
}
|
|
7949
|
+
}
|
|
7950
|
+
function readDocxChartDrawing(element) {
|
|
7951
|
+
const chart = Array.from(element.getElementsByTagName("*")).find((child) => child.localName === "chart");
|
|
7952
|
+
const relationshipId = chart ? getXmlAttribute3(chart, "id") : "";
|
|
7953
|
+
if (!relationshipId) {
|
|
7954
|
+
return void 0;
|
|
7955
|
+
}
|
|
7956
|
+
const extent = Array.from(element.children).find((child) => child.localName === "extent");
|
|
7957
|
+
return {
|
|
7958
|
+
relationshipId,
|
|
7959
|
+
widthPt: emuToPt(Number(extent?.getAttribute("cx") || 0)),
|
|
7960
|
+
heightPt: emuToPt(Number(extent?.getAttribute("cy") || 0))
|
|
7961
|
+
};
|
|
7962
|
+
}
|
|
7963
|
+
function repairDocxChartPlaceholders(container, charts) {
|
|
7964
|
+
if (charts.length === 0) {
|
|
7965
|
+
return;
|
|
7966
|
+
}
|
|
7967
|
+
const placeholders = Array.from(container.querySelectorAll("section.ofv-docx div")).filter(isDocxChartPlaceholder);
|
|
7968
|
+
if (placeholders.length !== charts.length) {
|
|
7969
|
+
return;
|
|
7970
|
+
}
|
|
7971
|
+
placeholders.forEach((placeholder, index) => {
|
|
7972
|
+
const chart = charts[index];
|
|
7973
|
+
placeholder.classList.add("ofv-docx-chart-preview");
|
|
7974
|
+
placeholder.dataset.ofvDocxChartPreview = "true";
|
|
7975
|
+
if (chart.widthPt > 0) {
|
|
7976
|
+
placeholder.style.width ||= `${formatCssNumber(chart.widthPt)}pt`;
|
|
7977
|
+
}
|
|
7978
|
+
if (chart.heightPt > 0) {
|
|
7979
|
+
placeholder.style.height ||= `${formatCssNumber(chart.heightPt)}pt`;
|
|
7980
|
+
}
|
|
7981
|
+
placeholder.replaceChildren(renderChartSvg(chart));
|
|
7982
|
+
});
|
|
7983
|
+
}
|
|
7984
|
+
function isDocxChartPlaceholder(element) {
|
|
7985
|
+
if (element.dataset.ofvDocxChartPreview === "true" || element.children.length > 0 || normalizePreviewText(element.textContent || "")) {
|
|
7986
|
+
return false;
|
|
7987
|
+
}
|
|
7988
|
+
const display = element.style.display;
|
|
7989
|
+
const position = element.style.position;
|
|
7990
|
+
const width = parseCssPixelValue(element.style.width);
|
|
7991
|
+
const height = parseCssPixelValue(element.style.height);
|
|
7992
|
+
return display === "inline-block" && position === "relative" && width >= 120 && height >= 80;
|
|
7993
|
+
}
|
|
6767
7994
|
function repairDocxHeadingShapeAlignment(page) {
|
|
6768
7995
|
for (const paragraph of page.querySelectorAll("p")) {
|
|
6769
7996
|
const text = normalizePreviewText(paragraph.textContent || "");
|
|
@@ -6914,14 +8141,19 @@ function repairDocxShapeFills(page) {
|
|
|
6914
8141
|
}
|
|
6915
8142
|
}
|
|
6916
8143
|
function repairDocxFloatingPictures(page, hints) {
|
|
6917
|
-
const
|
|
6918
|
-
if (
|
|
8144
|
+
const pageHints = hints.floatingPictures.filter((item) => item.relativeFrom === "column" && item.wrap === "square");
|
|
8145
|
+
if (pageHints.length === 0) {
|
|
6919
8146
|
return;
|
|
6920
8147
|
}
|
|
6921
|
-
const
|
|
6922
|
-
|
|
8148
|
+
const images = Array.from(page.querySelectorAll("img")).filter(
|
|
8149
|
+
(image) => image.closest("[data-ofv-docx-float-repaired='true']") === null
|
|
8150
|
+
);
|
|
8151
|
+
if (images.length === 0 || images.length !== pageHints.length) {
|
|
6923
8152
|
return;
|
|
6924
8153
|
}
|
|
8154
|
+
images.forEach((image, index) => repairDocxFloatingPicture(page, image, pageHints[index]));
|
|
8155
|
+
}
|
|
8156
|
+
function repairDocxFloatingPicture(page, image, hint) {
|
|
6925
8157
|
const wrapper = image.parentElement;
|
|
6926
8158
|
if (!wrapper || wrapper.dataset.ofvDocxFloatRepaired === "true") {
|
|
6927
8159
|
return;
|
|
@@ -6967,7 +8199,7 @@ function parseCssLineHeight(value) {
|
|
|
6967
8199
|
const parsed = Number.parseFloat(trimmed);
|
|
6968
8200
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
6969
8201
|
}
|
|
6970
|
-
function fitDocxPages(container) {
|
|
8202
|
+
function fitDocxPages(container, fit) {
|
|
6971
8203
|
const wrapper = container.querySelector(".ofv-docx-wrapper");
|
|
6972
8204
|
if (!wrapper) {
|
|
6973
8205
|
return () => void 0;
|
|
@@ -6980,6 +8212,7 @@ function fitDocxPages(container) {
|
|
|
6980
8212
|
return;
|
|
6981
8213
|
}
|
|
6982
8214
|
const availableWidth = Math.max(1, container.clientWidth - 48);
|
|
8215
|
+
const availableHeight = container.clientHeight > 0 ? Math.max(1, container.clientHeight - 48) : void 0;
|
|
6983
8216
|
const pageWidth = Math.max(
|
|
6984
8217
|
1,
|
|
6985
8218
|
...frames.map(({ page }) => {
|
|
@@ -6987,14 +8220,24 @@ function fitDocxPages(container) {
|
|
|
6987
8220
|
return page.offsetWidth || rectWidth || parseCssPixelValue(page.style.width) || 794;
|
|
6988
8221
|
})
|
|
6989
8222
|
);
|
|
6990
|
-
const
|
|
8223
|
+
const pageHeight = Math.max(
|
|
8224
|
+
1,
|
|
8225
|
+
...frames.map(({ page }) => page.offsetHeight || page.getBoundingClientRect().height || parseCssPixelValue(page.style.height) || 1123)
|
|
8226
|
+
);
|
|
8227
|
+
const scale = getDocxFitScale(fit, availableWidth, availableHeight, pageWidth, pageHeight);
|
|
6991
8228
|
const userZoom = parseCssPixelValue(panel?.style.getPropertyValue("--ofv-office-zoom") || "1") || 1;
|
|
6992
8229
|
wrapper.style.setProperty("--ofv-docx-scale", formatCssNumber(scale));
|
|
6993
8230
|
wrapper.style.setProperty("--ofv-docx-page-width", `${pageWidth}px`);
|
|
8231
|
+
wrapper.style.width = `${Math.ceil(pageWidth * scale * userZoom + 48)}px`;
|
|
8232
|
+
wrapper.style.maxWidth = "none";
|
|
8233
|
+
wrapper.style.overflow = "visible";
|
|
6994
8234
|
for (const { frame, page } of frames) {
|
|
6995
|
-
const
|
|
6996
|
-
|
|
6997
|
-
|
|
8235
|
+
const framePageHeight = page.offsetHeight || page.getBoundingClientRect().height || parseCssPixelValue(page.style.height);
|
|
8236
|
+
const framePageWidth = page.offsetWidth || page.getBoundingClientRect().width || parseCssPixelValue(page.style.width) || pageWidth;
|
|
8237
|
+
frame.style.width = `${Math.ceil(framePageWidth * scale * userZoom)}px`;
|
|
8238
|
+
frame.style.maxWidth = "none";
|
|
8239
|
+
if (framePageHeight > 0) {
|
|
8240
|
+
frame.style.height = `${Math.ceil(framePageHeight * scale * userZoom)}px`;
|
|
6998
8241
|
}
|
|
6999
8242
|
}
|
|
7000
8243
|
};
|
|
@@ -7019,6 +8262,26 @@ function fitDocxPages(container) {
|
|
|
7019
8262
|
observer.disconnect();
|
|
7020
8263
|
};
|
|
7021
8264
|
}
|
|
8265
|
+
function getDocxFitScale(fit, availableWidth, availableHeight, pageWidth, pageHeight) {
|
|
8266
|
+
const widthScale = availableWidth / pageWidth;
|
|
8267
|
+
const heightScale = availableHeight ? availableHeight / pageHeight : void 0;
|
|
8268
|
+
if (fit === "actual") {
|
|
8269
|
+
return 1;
|
|
8270
|
+
}
|
|
8271
|
+
if (fit === "height") {
|
|
8272
|
+
return Math.max(0.1, heightScale ?? widthScale);
|
|
8273
|
+
}
|
|
8274
|
+
if (fit === "cover") {
|
|
8275
|
+
return Math.max(0.1, Math.max(widthScale, heightScale ?? widthScale));
|
|
8276
|
+
}
|
|
8277
|
+
if (fit === "scale-down") {
|
|
8278
|
+
return Math.min(1, Math.max(0.1, Math.min(widthScale, heightScale ?? widthScale)));
|
|
8279
|
+
}
|
|
8280
|
+
if (fit === "contain") {
|
|
8281
|
+
return Math.max(0.1, Math.min(widthScale, heightScale ?? widthScale));
|
|
8282
|
+
}
|
|
8283
|
+
return Math.max(0.1, widthScale);
|
|
8284
|
+
}
|
|
7022
8285
|
function ensureDocxPageFrames(wrapper) {
|
|
7023
8286
|
const pages = Array.from(wrapper.querySelectorAll("section.ofv-docx"));
|
|
7024
8287
|
return pages.map((page) => {
|
|
@@ -7550,9 +8813,9 @@ function parseChartXml(xml, fallbackName) {
|
|
|
7550
8813
|
return null;
|
|
7551
8814
|
}
|
|
7552
8815
|
const type = detectChartType(doc);
|
|
7553
|
-
const title =
|
|
8816
|
+
const title = readChartTitle(doc, fallbackName);
|
|
7554
8817
|
const seriesElements = Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "ser");
|
|
7555
|
-
const series = seriesElements.map((element, index) => parseChartSeries(element, index)).filter((item) => item.values.length > 0);
|
|
8818
|
+
const series = seriesElements.map((element, index) => ({ ...parseChartSeries(element, index), color: readChartSeriesColor(element) })).filter((item) => item.values.length > 0);
|
|
7556
8819
|
if (series.length === 0) {
|
|
7557
8820
|
return null;
|
|
7558
8821
|
}
|
|
@@ -7561,9 +8824,21 @@ function parseChartXml(xml, fallbackName) {
|
|
|
7561
8824
|
type,
|
|
7562
8825
|
title,
|
|
7563
8826
|
categories: series.find((item) => item.categories.length > 0)?.categories || [],
|
|
7564
|
-
series: series.map((item) => ({ name: item.name, values: item.values }))
|
|
8827
|
+
series: series.map((item) => ({ name: item.name, values: item.values, color: item.color }))
|
|
7565
8828
|
};
|
|
7566
8829
|
}
|
|
8830
|
+
function readChartTitle(doc, fallbackName) {
|
|
8831
|
+
const titleElement = Array.from(doc.getElementsByTagName("*")).find((element) => element.localName === "title");
|
|
8832
|
+
if (!titleElement) {
|
|
8833
|
+
return fallbackName.replace(/\.xml$/i, "");
|
|
8834
|
+
}
|
|
8835
|
+
const explicitTitle = chartText(titleElement);
|
|
8836
|
+
if (explicitTitle) {
|
|
8837
|
+
return explicitTitle;
|
|
8838
|
+
}
|
|
8839
|
+
const language = Array.from(doc.getElementsByTagName("*")).find((element) => element.localName === "lang")?.getAttribute("val") || "";
|
|
8840
|
+
return /^zh\b/i.test(language) ? "\u56FE\u8868\u6807\u9898" : "Chart Title";
|
|
8841
|
+
}
|
|
7567
8842
|
function detectChartType(doc) {
|
|
7568
8843
|
const chartType = Array.from(doc.getElementsByTagName("*")).find(
|
|
7569
8844
|
(element) => element.localName.endsWith("Chart") && element.localName !== "chart"
|
|
@@ -7580,6 +8855,31 @@ function parseChartSeries(element, index) {
|
|
|
7580
8855
|
categories: stringsFromFirst(element, "cat")
|
|
7581
8856
|
};
|
|
7582
8857
|
}
|
|
8858
|
+
function readChartSeriesColor(element) {
|
|
8859
|
+
const shape = Array.from(element?.children || []).find((child) => child.localName === "spPr");
|
|
8860
|
+
const color = Array.from(shape?.getElementsByTagName("*") || []).find(
|
|
8861
|
+
(child) => child.localName === "srgbClr" || child.localName === "schemeClr"
|
|
8862
|
+
);
|
|
8863
|
+
if (!color) {
|
|
8864
|
+
return void 0;
|
|
8865
|
+
}
|
|
8866
|
+
if (color.localName === "srgbClr") {
|
|
8867
|
+
const value = color.getAttribute("val") || "";
|
|
8868
|
+
return /^[\da-f]{6}$/i.test(value) ? `#${value}` : void 0;
|
|
8869
|
+
}
|
|
8870
|
+
return chartSchemeColor(color.getAttribute("val") || "");
|
|
8871
|
+
}
|
|
8872
|
+
function chartSchemeColor(value) {
|
|
8873
|
+
const colors = {
|
|
8874
|
+
accent1: "#156082",
|
|
8875
|
+
accent2: "#e97132",
|
|
8876
|
+
accent3: "#196b24",
|
|
8877
|
+
accent4: "#0f9ed5",
|
|
8878
|
+
accent5: "#a02b93",
|
|
8879
|
+
accent6: "#4ea72e"
|
|
8880
|
+
};
|
|
8881
|
+
return colors[value];
|
|
8882
|
+
}
|
|
7583
8883
|
function renderChartPreviewSection(charts) {
|
|
7584
8884
|
const section = createSection("\u8868\u683C\u56FE\u8868\u9884\u89C8");
|
|
7585
8885
|
const grid = document.createElement("div");
|
|
@@ -7617,17 +8917,39 @@ function renderChartCard(chart) {
|
|
|
7617
8917
|
}
|
|
7618
8918
|
function renderChartSvg(chart) {
|
|
7619
8919
|
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
7620
|
-
svg.setAttribute("viewBox", "0 0 640
|
|
8920
|
+
svg.setAttribute("viewBox", "0 0 640 380");
|
|
7621
8921
|
svg.setAttribute("role", "img");
|
|
7622
8922
|
svg.setAttribute("aria-label", chart.title);
|
|
7623
8923
|
svg.classList.add("ofv-chart-svg");
|
|
7624
8924
|
const allValues = chart.series.flatMap((item) => item.values).filter((value) => Number.isFinite(value));
|
|
7625
8925
|
const max = Math.max(1, ...allValues);
|
|
7626
8926
|
const min = Math.min(0, ...allValues);
|
|
8927
|
+
const axisScale = createChartAxisScale(max, min);
|
|
8928
|
+
const axisMax = axisScale.max;
|
|
8929
|
+
const axisMin = axisScale.min;
|
|
7627
8930
|
const span = max - min || 1;
|
|
7628
|
-
const colors = ["#
|
|
7629
|
-
const plot = { x:
|
|
7630
|
-
|
|
8931
|
+
const colors = ["#156082", "#e97132", "#196b24", "#0f9ed5", "#a02b93", "#4ea72e"];
|
|
8932
|
+
const plot = { x: 74, y: 74, width: 526, height: 214 };
|
|
8933
|
+
const categories = chart.categories.length > 0 ? chart.categories : chart.series[0]?.values.map((_, index) => String(index + 1)) || [];
|
|
8934
|
+
const title = appendSvg(svg, "text", { x: 320, y: 34, class: "ofv-chart-title", "text-anchor": "middle" });
|
|
8935
|
+
title.textContent = chart.title;
|
|
8936
|
+
for (const value of axisScale.ticks) {
|
|
8937
|
+
const y = plot.y + plot.height - (value - axisMin) / (axisMax - axisMin || 1) * plot.height;
|
|
8938
|
+
appendSvg(svg, "line", {
|
|
8939
|
+
x1: plot.x,
|
|
8940
|
+
y1: Number(y.toFixed(1)),
|
|
8941
|
+
x2: plot.x + plot.width,
|
|
8942
|
+
y2: Number(y.toFixed(1)),
|
|
8943
|
+
class: value === 0 ? "ofv-chart-axis" : "ofv-chart-gridline"
|
|
8944
|
+
});
|
|
8945
|
+
const label = appendSvg(svg, "text", {
|
|
8946
|
+
x: plot.x - 12,
|
|
8947
|
+
y: Number((y + 4).toFixed(1)),
|
|
8948
|
+
class: "ofv-chart-label",
|
|
8949
|
+
"text-anchor": "end"
|
|
8950
|
+
});
|
|
8951
|
+
label.textContent = formatChartTick(value);
|
|
8952
|
+
}
|
|
7631
8953
|
appendSvg(svg, "line", {
|
|
7632
8954
|
x1: plot.x,
|
|
7633
8955
|
y1: plot.y + plot.height,
|
|
@@ -7635,28 +8957,47 @@ function renderChartSvg(chart) {
|
|
|
7635
8957
|
y2: plot.y + plot.height,
|
|
7636
8958
|
class: "ofv-chart-axis"
|
|
7637
8959
|
});
|
|
7638
|
-
chart.
|
|
7639
|
-
const
|
|
7640
|
-
const
|
|
7641
|
-
const
|
|
7642
|
-
|
|
7643
|
-
|
|
7644
|
-
|
|
7645
|
-
|
|
7646
|
-
const
|
|
7647
|
-
|
|
7648
|
-
|
|
7649
|
-
|
|
8960
|
+
if (chart.type.includes("bar") || chart.type.includes("col")) {
|
|
8961
|
+
const categoryCount = Math.max(1, categories.length, ...chart.series.map((series) => series.values.length));
|
|
8962
|
+
const groupWidth = plot.width / categoryCount;
|
|
8963
|
+
const clusterWidth = groupWidth * 0.58;
|
|
8964
|
+
const barWidth = Math.max(5, Math.min(28, clusterWidth / Math.max(1, chart.series.length)));
|
|
8965
|
+
const zeroY = plot.y + plot.height - (0 - axisMin) / (axisMax - axisMin || 1) * plot.height;
|
|
8966
|
+
categories.slice(0, categoryCount).forEach((category, index) => {
|
|
8967
|
+
const x = plot.x + groupWidth * (index + 0.5);
|
|
8968
|
+
const label = appendSvg(svg, "text", {
|
|
8969
|
+
x: Number(x.toFixed(1)),
|
|
8970
|
+
y: plot.y + plot.height + 22,
|
|
8971
|
+
class: "ofv-chart-label",
|
|
8972
|
+
"text-anchor": "middle"
|
|
8973
|
+
});
|
|
8974
|
+
label.textContent = truncateChartLabel(category);
|
|
8975
|
+
});
|
|
8976
|
+
chart.series.forEach((series, seriesIndex) => {
|
|
8977
|
+
const color = series.color || colors[seriesIndex % colors.length];
|
|
8978
|
+
series.values.forEach((value, index) => {
|
|
8979
|
+
const groupCenter = plot.x + groupWidth * (index + 0.5);
|
|
8980
|
+
const x = groupCenter - barWidth * chart.series.length / 2 + seriesIndex * barWidth + barWidth * 0.12;
|
|
8981
|
+
const y = plot.y + plot.height - (value - axisMin) / (axisMax - axisMin || 1) * plot.height;
|
|
7650
8982
|
appendSvg(svg, "rect", {
|
|
7651
|
-
x,
|
|
7652
|
-
y: Math.min(
|
|
7653
|
-
width: barWidth,
|
|
7654
|
-
height: Math.max(1, Math.abs(zeroY -
|
|
8983
|
+
x: Number(x.toFixed(1)),
|
|
8984
|
+
y: Number(Math.min(y, zeroY).toFixed(1)),
|
|
8985
|
+
width: Number((barWidth * 0.76).toFixed(1)),
|
|
8986
|
+
height: Number(Math.max(1, Math.abs(zeroY - y)).toFixed(1)),
|
|
7655
8987
|
fill: color,
|
|
7656
8988
|
"data-index": index
|
|
7657
8989
|
});
|
|
7658
8990
|
});
|
|
7659
|
-
}
|
|
8991
|
+
});
|
|
8992
|
+
} else {
|
|
8993
|
+
chart.series.forEach((series, seriesIndex) => {
|
|
8994
|
+
const color = series.color || colors[seriesIndex % colors.length];
|
|
8995
|
+
const step = series.values.length > 1 ? plot.width / (series.values.length - 1) : plot.width;
|
|
8996
|
+
const lineSpan = axisMax - axisMin || span;
|
|
8997
|
+
const points = series.values.map((value, index) => ({
|
|
8998
|
+
x: plot.x + index * step,
|
|
8999
|
+
y: plot.y + plot.height - (value - axisMin) / lineSpan * plot.height
|
|
9000
|
+
}));
|
|
7660
9001
|
appendSvg(svg, "polyline", {
|
|
7661
9002
|
points: points.map((point) => `${point.x.toFixed(1)},${point.y.toFixed(1)}`).join(" "),
|
|
7662
9003
|
fill: "none",
|
|
@@ -7668,11 +9009,49 @@ function renderChartSvg(chart) {
|
|
|
7668
9009
|
for (const point of points.slice(0, 80)) {
|
|
7669
9010
|
appendSvg(svg, "circle", { cx: point.x, cy: point.y, r: 3, fill: color });
|
|
7670
9011
|
}
|
|
7671
|
-
}
|
|
7672
|
-
|
|
7673
|
-
|
|
9012
|
+
});
|
|
9013
|
+
}
|
|
9014
|
+
appendChartLegend(svg, chart, colors, 348);
|
|
7674
9015
|
return svg;
|
|
7675
9016
|
}
|
|
9017
|
+
function appendChartLegend(svg, chart, colors, y) {
|
|
9018
|
+
const itemWidth = 86;
|
|
9019
|
+
const startX = 320 - chart.series.length * itemWidth / 2;
|
|
9020
|
+
chart.series.forEach((series, seriesIndex) => {
|
|
9021
|
+
const color = series.color || colors[seriesIndex % colors.length];
|
|
9022
|
+
appendLegend(svg, series.name, color, startX + seriesIndex * itemWidth, y);
|
|
9023
|
+
});
|
|
9024
|
+
}
|
|
9025
|
+
function createChartAxisScale(max, min) {
|
|
9026
|
+
const axisMin = Math.min(0, min);
|
|
9027
|
+
const positiveMax = Math.max(1, max);
|
|
9028
|
+
const step = niceChartStep((positiveMax - axisMin) / 5);
|
|
9029
|
+
let axisMax = Math.ceil(positiveMax / step) * step;
|
|
9030
|
+
if (axisMax <= positiveMax) {
|
|
9031
|
+
axisMax += step;
|
|
9032
|
+
}
|
|
9033
|
+
const ticks = [];
|
|
9034
|
+
for (let value = axisMin; value <= axisMax + step / 2; value += step) {
|
|
9035
|
+
ticks.push(Number(value.toFixed(6)));
|
|
9036
|
+
}
|
|
9037
|
+
return { min: axisMin, max: axisMax, ticks };
|
|
9038
|
+
}
|
|
9039
|
+
function niceChartStep(rawStep) {
|
|
9040
|
+
if (rawStep <= 0) {
|
|
9041
|
+
return 1;
|
|
9042
|
+
}
|
|
9043
|
+
const magnitude = 10 ** Math.floor(Math.log10(rawStep));
|
|
9044
|
+
const normalized = rawStep / magnitude;
|
|
9045
|
+
const nice = normalized <= 1 ? 1 : normalized <= 2 ? 2 : normalized <= 5 ? 5 : 10;
|
|
9046
|
+
return nice * magnitude;
|
|
9047
|
+
}
|
|
9048
|
+
function formatChartTick(value) {
|
|
9049
|
+
const rounded = Math.abs(value) < 1 ? Number(value.toFixed(1)) : Number(value.toFixed(0));
|
|
9050
|
+
return String(rounded);
|
|
9051
|
+
}
|
|
9052
|
+
function truncateChartLabel(value) {
|
|
9053
|
+
return value.length > 10 ? `${value.slice(0, 10)}...` : value;
|
|
9054
|
+
}
|
|
7676
9055
|
function appendLegend(svg, label, color, x, y) {
|
|
7677
9056
|
appendSvg(svg, "rect", { x, y: y - 10, width: 12, height: 12, rx: 2, fill: color });
|
|
7678
9057
|
const text = appendSvg(svg, "text", { x: x + 18, y, class: "ofv-chart-label" });
|
|
@@ -8406,7 +9785,7 @@ function renderOpenDocumentPresentationXml(panel, xml) {
|
|
|
8406
9785
|
renderPresentationInsight(panel, inspectOpenDocumentPresentation("FODP \u6F14\u793A\u6587\u7A3F", xml, 0));
|
|
8407
9786
|
renderOpenDocumentPresentation(panel, "FODP \u6F14\u793A\u6587\u7A3F", xml, []);
|
|
8408
9787
|
}
|
|
8409
|
-
async function renderPackagedOfficePreview(panel, arrayBuffer, extension) {
|
|
9788
|
+
async function renderPackagedOfficePreview(panel, arrayBuffer, extension, fit) {
|
|
8410
9789
|
let zip;
|
|
8411
9790
|
try {
|
|
8412
9791
|
zip = await JSZip3.loadAsync(arrayBuffer);
|
|
@@ -8417,7 +9796,7 @@ async function renderPackagedOfficePreview(panel, arrayBuffer, extension) {
|
|
|
8417
9796
|
const hasEntry = (path) => entries.some((entry) => entry.name.toLowerCase() === path.toLowerCase());
|
|
8418
9797
|
const contentXml = zip.file(/(^|\/)content\.xml$/i)[0];
|
|
8419
9798
|
if (hasEntry("word/document.xml")) {
|
|
8420
|
-
await renderDocx(panel, arrayBuffer);
|
|
9799
|
+
await renderDocx(panel, arrayBuffer, fit);
|
|
8421
9800
|
return true;
|
|
8422
9801
|
}
|
|
8423
9802
|
if (hasEntry("xl/workbook.xml")) {
|
|
@@ -8852,6 +10231,13 @@ function legacyOfficeFormatLabel(extension) {
|
|
|
8852
10231
|
}
|
|
8853
10232
|
return "PowerPoint Binary File Format";
|
|
8854
10233
|
}
|
|
10234
|
+
function renderLegacyWordBinary(panel, extension, arrayBuffer) {
|
|
10235
|
+
try {
|
|
10236
|
+
renderLegacyWordDocument(panel, parseLegacyWordDocument(arrayBuffer));
|
|
10237
|
+
} catch (error) {
|
|
10238
|
+
renderLegacyOfficeBinary(panel, extension, arrayBuffer, `Word \u4E8C\u8FDB\u5236\u89E3\u6790\u5931\u8D25\uFF1A${normalizeOfficeError(error)}`);
|
|
10239
|
+
}
|
|
10240
|
+
}
|
|
8855
10241
|
function renderLegacyOfficeBinary(panel, extension, arrayBuffer, parseError) {
|
|
8856
10242
|
const fragments = extractLegacyOfficeText(arrayBuffer);
|
|
8857
10243
|
panel.replaceChildren();
|
|
@@ -13427,10 +14813,10 @@ function renderStep(panel, text, extension, ctx) {
|
|
|
13427
14813
|
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";
|
|
13428
14814
|
const meta = document.createElement("div");
|
|
13429
14815
|
meta.className = "ofv-cad-summary";
|
|
13430
|
-
|
|
13431
|
-
|
|
13432
|
-
|
|
13433
|
-
|
|
14816
|
+
appendMeta6(meta, "\u5B9E\u4F53", records.length);
|
|
14817
|
+
appendMeta6(meta, "\u7C7B\u578B", typeCounts.size);
|
|
14818
|
+
appendMeta6(meta, "\u70B9", typeCounts.get("CARTESIAN_POINT") || 0);
|
|
14819
|
+
appendMeta6(meta, "\u65B9\u5411", typeCounts.get("DIRECTION") || 0);
|
|
13434
14820
|
const typeList = createCadTypeList(typeCounts);
|
|
13435
14821
|
section.append(note, meta, typeList);
|
|
13436
14822
|
let viewer;
|
|
@@ -13465,10 +14851,10 @@ function renderIges(panel, text, extension, ctx) {
|
|
|
13465
14851
|
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";
|
|
13466
14852
|
const meta = document.createElement("div");
|
|
13467
14853
|
meta.className = "ofv-cad-summary";
|
|
13468
|
-
|
|
13469
|
-
|
|
13470
|
-
|
|
13471
|
-
|
|
14854
|
+
appendMeta6(meta, "\u5B9E\u4F53", records.length);
|
|
14855
|
+
appendMeta6(meta, "\u7C7B\u578B", typeCounts.size);
|
|
14856
|
+
appendMeta6(meta, "\u70B9\u5B9E\u4F53", typeCounts.get("116") || 0);
|
|
14857
|
+
appendMeta6(meta, "\u7EBF\u5B9E\u4F53", typeCounts.get("110") || 0);
|
|
13472
14858
|
const typeList = createCadTypeList(typeCounts, "\u7C7B\u578B\u53F7\u7EDF\u8BA1");
|
|
13473
14859
|
section.append(note, meta, typeList);
|
|
13474
14860
|
let viewer;
|
|
@@ -13502,13 +14888,13 @@ function renderIfc(panel, text) {
|
|
|
13502
14888
|
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";
|
|
13503
14889
|
const meta = document.createElement("div");
|
|
13504
14890
|
meta.className = "ofv-cad-summary";
|
|
13505
|
-
|
|
13506
|
-
|
|
13507
|
-
|
|
13508
|
-
|
|
13509
|
-
|
|
13510
|
-
|
|
13511
|
-
|
|
14891
|
+
appendMeta6(meta, "\u5B9E\u4F53", records.length);
|
|
14892
|
+
appendMeta6(meta, "\u7C7B\u578B", typeCounts.size);
|
|
14893
|
+
appendMeta6(meta, "\u9879\u76EE", typeCounts.get("IFCPROJECT") || 0);
|
|
14894
|
+
appendMeta6(meta, "\u5EFA\u7B51", typeCounts.get("IFCBUILDING") || 0);
|
|
14895
|
+
appendMeta6(meta, "\u697C\u5C42", typeCounts.get("IFCBUILDINGSTOREY") || 0);
|
|
14896
|
+
appendMeta6(meta, "\u7A7A\u95F4", typeCounts.get("IFCSPACE") || 0);
|
|
14897
|
+
appendMeta6(meta, "\u6784\u4EF6", countIfcElements(typeCounts));
|
|
13512
14898
|
section.append(note, meta, createCadTypeList(typeCounts, "IFC \u5B9E\u4F53\u7EDF\u8BA1"));
|
|
13513
14899
|
const hierarchy = createIfcHierarchy(records);
|
|
13514
14900
|
if (hierarchy) {
|
|
@@ -13533,10 +14919,10 @@ function renderAcisSat(panel, text, extension, ctx) {
|
|
|
13533
14919
|
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";
|
|
13534
14920
|
const meta = document.createElement("div");
|
|
13535
14921
|
meta.className = "ofv-cad-summary";
|
|
13536
|
-
|
|
13537
|
-
|
|
13538
|
-
|
|
13539
|
-
|
|
14922
|
+
appendMeta6(meta, "\u5B9E\u4F53", records.length);
|
|
14923
|
+
appendMeta6(meta, "\u7C7B\u578B", typeCounts.size);
|
|
14924
|
+
appendMeta6(meta, "\u9876\u70B9", typeCounts.get("vertex") || 0);
|
|
14925
|
+
appendMeta6(meta, "\u76F4\u7EBF", typeCounts.get("straight-curve") || 0);
|
|
13540
14926
|
const typeList = createCadTypeList(typeCounts);
|
|
13541
14927
|
section.append(note, meta, typeList);
|
|
13542
14928
|
let viewer;
|
|
@@ -13573,10 +14959,10 @@ function renderParasolidText(panel, text, extension, ctx) {
|
|
|
13573
14959
|
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";
|
|
13574
14960
|
const meta = document.createElement("div");
|
|
13575
14961
|
meta.className = "ofv-cad-summary";
|
|
13576
|
-
|
|
13577
|
-
|
|
13578
|
-
|
|
13579
|
-
|
|
14962
|
+
appendMeta6(meta, "\u5B9E\u4F53", records.length);
|
|
14963
|
+
appendMeta6(meta, "\u7C7B\u578B", typeCounts.size);
|
|
14964
|
+
appendMeta6(meta, "\u70B9", typeCounts.get("point") || typeCounts.get("vertex") || 0);
|
|
14965
|
+
appendMeta6(meta, "\u66F2\u7EBF", (typeCounts.get("line") || 0) + (typeCounts.get("curve") || 0));
|
|
13580
14966
|
const typeList = createCadTypeList(typeCounts);
|
|
13581
14967
|
section.append(note, meta, typeList);
|
|
13582
14968
|
let viewer;
|
|
@@ -13684,23 +15070,23 @@ function renderLayoutPreview(panel, data, ctx) {
|
|
|
13684
15070
|
summary.setAttribute("aria-hidden", "true");
|
|
13685
15071
|
summary.style.display = "none";
|
|
13686
15072
|
}
|
|
13687
|
-
|
|
13688
|
-
|
|
15073
|
+
appendMeta6(summary, "\u6587\u4EF6", data.fileName);
|
|
15074
|
+
appendMeta6(summary, "\u683C\u5F0F", data.format);
|
|
13689
15075
|
if (data.libraryName) {
|
|
13690
|
-
|
|
15076
|
+
appendMeta6(summary, "\u5E93", data.libraryName);
|
|
13691
15077
|
}
|
|
13692
15078
|
if (data.version) {
|
|
13693
|
-
|
|
15079
|
+
appendMeta6(summary, "\u7248\u672C", data.version);
|
|
13694
15080
|
}
|
|
13695
15081
|
if (data.unit) {
|
|
13696
|
-
|
|
15082
|
+
appendMeta6(summary, "\u5355\u4F4D", data.unit);
|
|
13697
15083
|
}
|
|
13698
|
-
|
|
13699
|
-
|
|
13700
|
-
|
|
13701
|
-
|
|
15084
|
+
appendMeta6(summary, "Cell", data.cells.length);
|
|
15085
|
+
appendMeta6(summary, "\u51E0\u4F55", data.shapes.length);
|
|
15086
|
+
appendMeta6(summary, "\u5F15\u7528", data.references.length);
|
|
15087
|
+
appendMeta6(summary, "\u6587\u5B57", data.labels.length);
|
|
13702
15088
|
for (const [label, value] of data.metadata) {
|
|
13703
|
-
|
|
15089
|
+
appendMeta6(summary, label, value);
|
|
13704
15090
|
}
|
|
13705
15091
|
section.append(summary);
|
|
13706
15092
|
for (const noteText of [...data.notes, ...data.warnings]) {
|
|
@@ -14306,12 +15692,12 @@ function renderBinaryCad(panel, bytes, extension, fileName) {
|
|
|
14306
15692
|
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";
|
|
14307
15693
|
const meta = document.createElement("div");
|
|
14308
15694
|
meta.className = "ofv-cad-summary";
|
|
14309
|
-
|
|
14310
|
-
|
|
14311
|
-
|
|
14312
|
-
|
|
14313
|
-
|
|
14314
|
-
|
|
15695
|
+
appendMeta6(meta, "\u6587\u4EF6", fileName);
|
|
15696
|
+
appendMeta6(meta, "\u683C\u5F0F", extension.toUpperCase());
|
|
15697
|
+
appendMeta6(meta, "\u5927\u5C0F", formatBytes3(bytes.byteLength));
|
|
15698
|
+
appendMeta6(meta, "\u7B7E\u540D", byteSignature2(bytes));
|
|
15699
|
+
appendMeta6(meta, "\u7248\u672C", detectCadVersion(bytes, extension));
|
|
15700
|
+
appendMeta6(meta, "\u5BB9\u5668", detectCadContainer(bytes));
|
|
14315
15701
|
const actions = document.createElement("div");
|
|
14316
15702
|
actions.className = "ofv-cad-conversion";
|
|
14317
15703
|
const actionTitle = document.createElement("h4");
|
|
@@ -14351,12 +15737,12 @@ function createBinaryCadProbe(bytes, extension) {
|
|
|
14351
15737
|
summary.textContent = "\u4E8C\u8FDB\u5236\u7ED3\u6784\u63A2\u6D4B";
|
|
14352
15738
|
const meta = document.createElement("div");
|
|
14353
15739
|
meta.className = "ofv-archive-probe-meta";
|
|
14354
|
-
|
|
14355
|
-
|
|
14356
|
-
|
|
14357
|
-
|
|
14358
|
-
|
|
14359
|
-
|
|
15740
|
+
appendMeta6(meta, "\u53EF\u8BFB\u7247\u6BB5", probe.tokens.length);
|
|
15741
|
+
appendMeta6(meta, "\u5B9E\u4F53\u5173\u952E\u8BCD", formatCadKeywordCounts(probe.entityCounts));
|
|
15742
|
+
appendMeta6(meta, "\u56FE\u5C42\u7EBF\u7D22", String(probe.layerHints.length));
|
|
15743
|
+
appendMeta6(meta, "\u5757/\u5F15\u7528\u7EBF\u7D22", String(probe.blockHints.length));
|
|
15744
|
+
appendMeta6(meta, "\u5916\u90E8\u5F15\u7528", String(probe.externalRefs.length));
|
|
15745
|
+
appendMeta6(meta, "\u89E3\u6790\u7EA7\u522B", extension === "dwg" ? "\u542F\u53D1\u5F0F\u626B\u63CF" : "\u5BB9\u5668/\u6587\u672C\u626B\u63CF");
|
|
14360
15746
|
details.append(summary, meta);
|
|
14361
15747
|
const hints = [...probe.layerHints, ...probe.blockHints, ...probe.externalRefs].slice(0, 18);
|
|
14362
15748
|
if (hints.length > 0) {
|
|
@@ -14889,9 +16275,9 @@ function createUnsupportedCadSection(extension, fileName) {
|
|
|
14889
16275
|
const section = createSection("CAD \u589E\u5F3A\u63A5\u5165\u63D0\u793A");
|
|
14890
16276
|
const meta = document.createElement("div");
|
|
14891
16277
|
meta.className = "ofv-cad-summary";
|
|
14892
|
-
|
|
14893
|
-
|
|
14894
|
-
|
|
16278
|
+
appendMeta6(meta, "\u6587\u4EF6", fileName);
|
|
16279
|
+
appendMeta6(meta, "\u683C\u5F0F", `.${extension || "cad"}`);
|
|
16280
|
+
appendMeta6(meta, "\u5185\u7F6E\u80FD\u529B", unsupportedCadBuiltInLevel(extension));
|
|
14895
16281
|
const note = document.createElement("p");
|
|
14896
16282
|
note.textContent = unsupportedCadGuidance(extension);
|
|
14897
16283
|
const actions = document.createElement("div");
|
|
@@ -15004,7 +16390,7 @@ function countBy(values) {
|
|
|
15004
16390
|
}
|
|
15005
16391
|
return counts;
|
|
15006
16392
|
}
|
|
15007
|
-
function
|
|
16393
|
+
function appendMeta6(parent, label, value) {
|
|
15008
16394
|
const row = document.createElement("div");
|
|
15009
16395
|
row.className = "ofv-meta-row";
|
|
15010
16396
|
const key = document.createElement("span");
|
|
@@ -17038,10 +18424,10 @@ function read255UInt16(bytes, offset) {
|
|
|
17038
18424
|
return { value: code, offset };
|
|
17039
18425
|
}
|
|
17040
18426
|
function decodeEotString(bytes) {
|
|
17041
|
-
const text =
|
|
18427
|
+
const text = decodeUtf16Le2(bytes).replace(/\0+$/g, "").trim();
|
|
17042
18428
|
return text || new TextDecoder("latin1").decode(bytes).replace(/\0+$/g, "").trim();
|
|
17043
18429
|
}
|
|
17044
|
-
function
|
|
18430
|
+
function decodeUtf16Le2(bytes) {
|
|
17045
18431
|
let value = "";
|
|
17046
18432
|
for (let index = 0; index + 1 < bytes.length; index += 2) {
|
|
17047
18433
|
value += String.fromCharCode(bytes[index] | bytes[index + 1] << 8);
|