@open-file-viewer/core 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -0
- package/dist/index.cjs +398 -107
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -2
- package/dist/index.d.ts +24 -2
- package/dist/index.js +398 -107
- package/dist/index.js.map +1 -1
- package/dist/style.css +20 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -553,6 +553,48 @@ function revokeObjectUrl(url, isExternal) {
|
|
|
553
553
|
}
|
|
554
554
|
}
|
|
555
555
|
|
|
556
|
+
// src/messages.ts
|
|
557
|
+
var defaultMessages = {
|
|
558
|
+
"zh-CN": {
|
|
559
|
+
loading: "\u6B63\u5728\u52A0\u8F7D\u9884\u89C8...",
|
|
560
|
+
unsupportedTitle: "\u5F53\u524D\u6587\u4EF6\u6682\u4E0D\u652F\u6301\u5728\u7EBF\u9884\u89C8",
|
|
561
|
+
downloadTitle: "\u5F53\u524D\u6587\u4EF6\u53EF\u4E0B\u8F7D\u540E\u67E5\u770B",
|
|
562
|
+
downloadFile: "\u4E0B\u8F7D\u6587\u4EF6",
|
|
563
|
+
file: "\u6587\u4EF6",
|
|
564
|
+
unnamedFile: "\u672A\u547D\u540D\u6587\u4EF6",
|
|
565
|
+
format: "\u683C\u5F0F",
|
|
566
|
+
unknown: "\u672A\u77E5",
|
|
567
|
+
mime: "MIME",
|
|
568
|
+
undeclared: "\u672A\u58F0\u660E",
|
|
569
|
+
size: "\u5927\u5C0F",
|
|
570
|
+
source: "\u6765\u6E90",
|
|
571
|
+
remoteUrl: "\u8FDC\u7A0B URL",
|
|
572
|
+
localFile: "\u672C\u5730/\u5185\u5B58\u6587\u4EF6"
|
|
573
|
+
},
|
|
574
|
+
"en-US": {
|
|
575
|
+
loading: "Loading preview...",
|
|
576
|
+
unsupportedTitle: "Preview is not available for this file",
|
|
577
|
+
downloadTitle: "This file can be downloaded and opened locally",
|
|
578
|
+
downloadFile: "Download file",
|
|
579
|
+
file: "File",
|
|
580
|
+
unnamedFile: "Untitled file",
|
|
581
|
+
format: "Format",
|
|
582
|
+
unknown: "Unknown",
|
|
583
|
+
mime: "MIME",
|
|
584
|
+
undeclared: "Not declared",
|
|
585
|
+
size: "Size",
|
|
586
|
+
source: "Source",
|
|
587
|
+
remoteUrl: "Remote URL",
|
|
588
|
+
localFile: "Local or in-memory file"
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
function resolveMessages(options) {
|
|
592
|
+
return {
|
|
593
|
+
...defaultMessages[options.locale || "zh-CN"],
|
|
594
|
+
...options.messages
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
|
|
556
598
|
// src/plugins/fallback.ts
|
|
557
599
|
function fallbackPlugin() {
|
|
558
600
|
return {
|
|
@@ -570,12 +612,12 @@ function fallbackPlugin() {
|
|
|
570
612
|
const panel = document.createElement("div");
|
|
571
613
|
panel.className = "ofv-fallback";
|
|
572
614
|
const title = document.createElement("strong");
|
|
573
|
-
title.textContent = ctx.options.fallback === "download" ?
|
|
574
|
-
const meta = createFallbackMeta(ctx.file);
|
|
615
|
+
title.textContent = ctx.options.fallback === "download" ? ctx.options.messages.downloadTitle : ctx.options.messages.unsupportedTitle;
|
|
616
|
+
const meta = createFallbackMeta(ctx.file, ctx.options.messages);
|
|
575
617
|
const download = document.createElement("a");
|
|
576
618
|
download.href = url;
|
|
577
619
|
download.download = ctx.file.name;
|
|
578
|
-
download.textContent =
|
|
620
|
+
download.textContent = ctx.options.messages.downloadFile;
|
|
579
621
|
panel.append(title, meta, download);
|
|
580
622
|
ctx.viewport.classList.add("ofv-center");
|
|
581
623
|
ctx.viewport.append(panel);
|
|
@@ -591,14 +633,14 @@ function fallbackPlugin() {
|
|
|
591
633
|
}
|
|
592
634
|
};
|
|
593
635
|
}
|
|
594
|
-
function createFallbackMeta(file) {
|
|
636
|
+
function createFallbackMeta(file, messages) {
|
|
595
637
|
const meta = document.createElement("dl");
|
|
596
638
|
meta.className = "ofv-fallback-meta";
|
|
597
|
-
appendFallbackMeta(meta,
|
|
598
|
-
appendFallbackMeta(meta,
|
|
599
|
-
appendFallbackMeta(meta,
|
|
600
|
-
appendFallbackMeta(meta,
|
|
601
|
-
appendFallbackMeta(meta,
|
|
639
|
+
appendFallbackMeta(meta, messages.file, file.name || messages.unnamedFile);
|
|
640
|
+
appendFallbackMeta(meta, messages.format, file.extension ? `.${file.extension}` : messages.unknown);
|
|
641
|
+
appendFallbackMeta(meta, messages.mime, file.mimeType || messages.undeclared);
|
|
642
|
+
appendFallbackMeta(meta, messages.size, file.size === void 0 ? messages.unknown : formatFallbackBytes(file.size));
|
|
643
|
+
appendFallbackMeta(meta, messages.source, file.url ? messages.remoteUrl : messages.localFile);
|
|
602
644
|
return meta;
|
|
603
645
|
}
|
|
604
646
|
function appendFallbackMeta(parent, label, value) {
|
|
@@ -656,15 +698,17 @@ function createViewer(options) {
|
|
|
656
698
|
host.append(status, viewport);
|
|
657
699
|
container.replaceChildren(host);
|
|
658
700
|
const normalizedOptions = {
|
|
701
|
+
...options,
|
|
659
702
|
fit: options.fit || "contain",
|
|
660
703
|
fallback: options.fallback || "inline",
|
|
661
|
-
|
|
704
|
+
zoom: normalizeInitialZoom(options.zoom),
|
|
705
|
+
messages: resolveMessages(options)
|
|
662
706
|
};
|
|
663
707
|
let destroyed = false;
|
|
664
708
|
let renderToken = 0;
|
|
665
709
|
const setLoading = (loading) => {
|
|
666
710
|
status.hidden = !loading;
|
|
667
|
-
status.textContent = loading ?
|
|
711
|
+
status.textContent = loading ? normalizedOptions.messages.loading : "";
|
|
668
712
|
};
|
|
669
713
|
const setError = (error) => {
|
|
670
714
|
status.hidden = false;
|
|
@@ -820,6 +864,9 @@ function clampIndex(index, length) {
|
|
|
820
864
|
}
|
|
821
865
|
return Math.min(Math.max(index, 0), length - 1);
|
|
822
866
|
}
|
|
867
|
+
function normalizeInitialZoom(zoom) {
|
|
868
|
+
return typeof zoom === "number" && Number.isFinite(zoom) && zoom > 0 ? zoom : 1;
|
|
869
|
+
}
|
|
823
870
|
function applyTheme(container, theme) {
|
|
824
871
|
const media = window.matchMedia?.("(prefers-color-scheme: dark)");
|
|
825
872
|
const classes = ["ofv-theme-light", "ofv-theme-dark"];
|
|
@@ -1696,6 +1743,98 @@ async function findPlugin(plugins, file) {
|
|
|
1696
1743
|
return fallbackPlugin();
|
|
1697
1744
|
}
|
|
1698
1745
|
|
|
1746
|
+
// src/plugins/utils.ts
|
|
1747
|
+
async function readArrayBuffer(file) {
|
|
1748
|
+
if (file.source instanceof ArrayBuffer) {
|
|
1749
|
+
return file.source;
|
|
1750
|
+
}
|
|
1751
|
+
if (file.blob) {
|
|
1752
|
+
return file.blob.arrayBuffer();
|
|
1753
|
+
}
|
|
1754
|
+
if (typeof file.source === "string") {
|
|
1755
|
+
const response = await fetch(file.source);
|
|
1756
|
+
if (!response.ok) {
|
|
1757
|
+
throw new Error(`Failed to fetch file: ${response.status}`);
|
|
1758
|
+
}
|
|
1759
|
+
return response.arrayBuffer();
|
|
1760
|
+
}
|
|
1761
|
+
throw new Error("Unsupported file source.");
|
|
1762
|
+
}
|
|
1763
|
+
async function readTextFile(file) {
|
|
1764
|
+
const decode = (buffer) => decodeTextBuffer(buffer);
|
|
1765
|
+
if (typeof file.source === "string") {
|
|
1766
|
+
const response = await fetch(file.source);
|
|
1767
|
+
if (!response.ok) {
|
|
1768
|
+
throw new Error(`Failed to fetch text file: ${response.status}`);
|
|
1769
|
+
}
|
|
1770
|
+
return decode(await response.arrayBuffer());
|
|
1771
|
+
}
|
|
1772
|
+
if (file.blob) {
|
|
1773
|
+
return decode(await file.blob.arrayBuffer());
|
|
1774
|
+
}
|
|
1775
|
+
if (file.source instanceof ArrayBuffer) {
|
|
1776
|
+
return decode(file.source);
|
|
1777
|
+
}
|
|
1778
|
+
return String(file.source);
|
|
1779
|
+
}
|
|
1780
|
+
function createPanel(className = "") {
|
|
1781
|
+
const panel = document.createElement("div");
|
|
1782
|
+
panel.className = `ofv-panel ${className}`.trim();
|
|
1783
|
+
return panel;
|
|
1784
|
+
}
|
|
1785
|
+
function getInitialZoom(ctx, min = 0.1, max = 8) {
|
|
1786
|
+
return Math.min(max, Math.max(min, ctx.options.zoom));
|
|
1787
|
+
}
|
|
1788
|
+
function createSection(title) {
|
|
1789
|
+
const section = document.createElement("section");
|
|
1790
|
+
section.className = "ofv-section";
|
|
1791
|
+
const heading = document.createElement("h3");
|
|
1792
|
+
heading.textContent = title;
|
|
1793
|
+
section.append(heading);
|
|
1794
|
+
return section;
|
|
1795
|
+
}
|
|
1796
|
+
function appendMeta(parent, label, value) {
|
|
1797
|
+
const row = document.createElement("div");
|
|
1798
|
+
row.className = "ofv-meta-row";
|
|
1799
|
+
const key = document.createElement("span");
|
|
1800
|
+
key.textContent = label;
|
|
1801
|
+
const content = document.createElement("strong");
|
|
1802
|
+
content.textContent = String(value);
|
|
1803
|
+
row.append(key, content);
|
|
1804
|
+
parent.append(row);
|
|
1805
|
+
}
|
|
1806
|
+
function decodeTextBuffer(buffer) {
|
|
1807
|
+
return decodeTextBytes(new Uint8Array(buffer));
|
|
1808
|
+
}
|
|
1809
|
+
function decodeTextBytes(bytes) {
|
|
1810
|
+
if (bytes.length >= 3 && bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191) {
|
|
1811
|
+
return new TextDecoder("utf-8").decode(bytes.subarray(3));
|
|
1812
|
+
}
|
|
1813
|
+
if (bytes.length >= 2) {
|
|
1814
|
+
if (bytes[0] === 255 && bytes[1] === 254) {
|
|
1815
|
+
return new TextDecoder("utf-16le").decode(bytes.subarray(2));
|
|
1816
|
+
}
|
|
1817
|
+
if (bytes[0] === 254 && bytes[1] === 255) {
|
|
1818
|
+
return new TextDecoder("utf-16be").decode(bytes.subarray(2));
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
try {
|
|
1822
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
1823
|
+
} catch {
|
|
1824
|
+
return decodeWithFallback(bytes, "gb18030") || decodeWithFallback(bytes, "gbk") || new TextDecoder("utf-8").decode(bytes);
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
function decodeWithFallback(bytes, encoding) {
|
|
1828
|
+
try {
|
|
1829
|
+
return new TextDecoder(encoding).decode(bytes);
|
|
1830
|
+
} catch {
|
|
1831
|
+
return void 0;
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
function resolveFormat(file, mimeMap) {
|
|
1835
|
+
return file.extension || mimeMap[file.mimeType] || "";
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1699
1838
|
// src/plugins/image.ts
|
|
1700
1839
|
var imageExtensions = /* @__PURE__ */ new Set([
|
|
1701
1840
|
"jpg",
|
|
@@ -1809,7 +1948,7 @@ function imagePlugin() {
|
|
|
1809
1948
|
canvasSource.setAttribute("role", "img");
|
|
1810
1949
|
canvasSource.setAttribute("aria-label", ctx.file.name);
|
|
1811
1950
|
}
|
|
1812
|
-
let scale =
|
|
1951
|
+
let scale = getInitialZoom(ctx);
|
|
1813
1952
|
let rotation = 0;
|
|
1814
1953
|
let offsetX = 0;
|
|
1815
1954
|
let offsetY = 0;
|
|
@@ -3637,95 +3776,6 @@ function readAiffExtended(bytes, offset) {
|
|
|
3637
3776
|
return mantissa * Math.pow(2, exponent - 16383 - 63);
|
|
3638
3777
|
}
|
|
3639
3778
|
|
|
3640
|
-
// src/plugins/utils.ts
|
|
3641
|
-
async function readArrayBuffer(file) {
|
|
3642
|
-
if (file.source instanceof ArrayBuffer) {
|
|
3643
|
-
return file.source;
|
|
3644
|
-
}
|
|
3645
|
-
if (file.blob) {
|
|
3646
|
-
return file.blob.arrayBuffer();
|
|
3647
|
-
}
|
|
3648
|
-
if (typeof file.source === "string") {
|
|
3649
|
-
const response = await fetch(file.source);
|
|
3650
|
-
if (!response.ok) {
|
|
3651
|
-
throw new Error(`Failed to fetch file: ${response.status}`);
|
|
3652
|
-
}
|
|
3653
|
-
return response.arrayBuffer();
|
|
3654
|
-
}
|
|
3655
|
-
throw new Error("Unsupported file source.");
|
|
3656
|
-
}
|
|
3657
|
-
async function readTextFile(file) {
|
|
3658
|
-
const decode = (buffer) => decodeTextBuffer(buffer);
|
|
3659
|
-
if (typeof file.source === "string") {
|
|
3660
|
-
const response = await fetch(file.source);
|
|
3661
|
-
if (!response.ok) {
|
|
3662
|
-
throw new Error(`Failed to fetch text file: ${response.status}`);
|
|
3663
|
-
}
|
|
3664
|
-
return decode(await response.arrayBuffer());
|
|
3665
|
-
}
|
|
3666
|
-
if (file.blob) {
|
|
3667
|
-
return decode(await file.blob.arrayBuffer());
|
|
3668
|
-
}
|
|
3669
|
-
if (file.source instanceof ArrayBuffer) {
|
|
3670
|
-
return decode(file.source);
|
|
3671
|
-
}
|
|
3672
|
-
return String(file.source);
|
|
3673
|
-
}
|
|
3674
|
-
function createPanel(className = "") {
|
|
3675
|
-
const panel = document.createElement("div");
|
|
3676
|
-
panel.className = `ofv-panel ${className}`.trim();
|
|
3677
|
-
return panel;
|
|
3678
|
-
}
|
|
3679
|
-
function createSection(title) {
|
|
3680
|
-
const section = document.createElement("section");
|
|
3681
|
-
section.className = "ofv-section";
|
|
3682
|
-
const heading = document.createElement("h3");
|
|
3683
|
-
heading.textContent = title;
|
|
3684
|
-
section.append(heading);
|
|
3685
|
-
return section;
|
|
3686
|
-
}
|
|
3687
|
-
function appendMeta(parent, label, value) {
|
|
3688
|
-
const row = document.createElement("div");
|
|
3689
|
-
row.className = "ofv-meta-row";
|
|
3690
|
-
const key = document.createElement("span");
|
|
3691
|
-
key.textContent = label;
|
|
3692
|
-
const content = document.createElement("strong");
|
|
3693
|
-
content.textContent = String(value);
|
|
3694
|
-
row.append(key, content);
|
|
3695
|
-
parent.append(row);
|
|
3696
|
-
}
|
|
3697
|
-
function decodeTextBuffer(buffer) {
|
|
3698
|
-
return decodeTextBytes(new Uint8Array(buffer));
|
|
3699
|
-
}
|
|
3700
|
-
function decodeTextBytes(bytes) {
|
|
3701
|
-
if (bytes.length >= 3 && bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191) {
|
|
3702
|
-
return new TextDecoder("utf-8").decode(bytes.subarray(3));
|
|
3703
|
-
}
|
|
3704
|
-
if (bytes.length >= 2) {
|
|
3705
|
-
if (bytes[0] === 255 && bytes[1] === 254) {
|
|
3706
|
-
return new TextDecoder("utf-16le").decode(bytes.subarray(2));
|
|
3707
|
-
}
|
|
3708
|
-
if (bytes[0] === 254 && bytes[1] === 255) {
|
|
3709
|
-
return new TextDecoder("utf-16be").decode(bytes.subarray(2));
|
|
3710
|
-
}
|
|
3711
|
-
}
|
|
3712
|
-
try {
|
|
3713
|
-
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
3714
|
-
} catch {
|
|
3715
|
-
return decodeWithFallback(bytes, "gb18030") || decodeWithFallback(bytes, "gbk") || new TextDecoder("utf-8").decode(bytes);
|
|
3716
|
-
}
|
|
3717
|
-
}
|
|
3718
|
-
function decodeWithFallback(bytes, encoding) {
|
|
3719
|
-
try {
|
|
3720
|
-
return new TextDecoder(encoding).decode(bytes);
|
|
3721
|
-
} catch {
|
|
3722
|
-
return void 0;
|
|
3723
|
-
}
|
|
3724
|
-
}
|
|
3725
|
-
function resolveFormat(file, mimeMap) {
|
|
3726
|
-
return file.extension || mimeMap[file.mimeType] || "";
|
|
3727
|
-
}
|
|
3728
|
-
|
|
3729
3779
|
// src/plugins/text.ts
|
|
3730
3780
|
var langMap = {
|
|
3731
3781
|
js: "javascript",
|
|
@@ -4194,7 +4244,7 @@ function textPlugin() {
|
|
|
4194
4244
|
};
|
|
4195
4245
|
}
|
|
4196
4246
|
function createTextZoomController(target, cssVariable, ctx) {
|
|
4197
|
-
let zoom =
|
|
4247
|
+
let zoom = getInitialZoom(ctx, 0.5, 3);
|
|
4198
4248
|
const apply = () => {
|
|
4199
4249
|
const normalized = Math.round(zoom * 100) / 100;
|
|
4200
4250
|
target.style.setProperty(cssVariable, String(normalized));
|
|
@@ -4504,6 +4554,7 @@ function pdfPlugin(options = {}) {
|
|
|
4504
4554
|
viewport: ctx.viewport,
|
|
4505
4555
|
size: ctx.size,
|
|
4506
4556
|
fit: ctx.options.fit,
|
|
4557
|
+
zoom: ctx.options.zoom,
|
|
4507
4558
|
toolbar: ctx.toolbar,
|
|
4508
4559
|
encryptedTitle: "PDF \u5DF2\u52A0\u5BC6\uFF0C\u65E0\u6CD5\u5728\u7EBF\u9884\u89C8",
|
|
4509
4560
|
encryptedMessage: "\u8BF7\u4E0B\u8F7D\u540E\u4F7F\u7528\u5BC6\u7801\u6253\u5F00\uFF0C\u6216\u4E0A\u4F20\u89E3\u5BC6\u540E\u7684 PDF \u6587\u4EF6\u3002",
|
|
@@ -4609,7 +4660,7 @@ async function renderPdfDocumentPreview(options) {
|
|
|
4609
4660
|
const pageStates = [];
|
|
4610
4661
|
let observer = null;
|
|
4611
4662
|
let currentSize = options.size;
|
|
4612
|
-
let zoomFactor = 1;
|
|
4663
|
+
let zoomFactor = getInitialZoom({ options: { zoom: options.zoom ?? 1 } }, 0.25, 4);
|
|
4613
4664
|
let rotation = 0;
|
|
4614
4665
|
const updateSummary = () => {
|
|
4615
4666
|
renderPdfSummary(summary, pdfDocument.numPages, pagesMeta, options.fit, zoomFactor);
|
|
@@ -5934,6 +5985,7 @@ async function renderConvertedOfficePreview(panel, ctx, options, conversionConte
|
|
|
5934
5985
|
viewport: panel,
|
|
5935
5986
|
size: ctx.size,
|
|
5936
5987
|
fit: ctx.options.fit,
|
|
5988
|
+
zoom: ctx.options.zoom,
|
|
5937
5989
|
toolbar: ctx.toolbar,
|
|
5938
5990
|
title: "Office \u9AD8\u4FDD\u771F\u8F6C\u6362\u9884\u89C8",
|
|
5939
5991
|
fallbackTitle: "Office \u8F6C\u6362\u540E\u7684 PDF \u65E0\u6CD5\u9884\u89C8",
|
|
@@ -5997,7 +6049,7 @@ function createOfficeZoomController(panel, ctx) {
|
|
|
5997
6049
|
if (!canZoom) {
|
|
5998
6050
|
return void 0;
|
|
5999
6051
|
}
|
|
6000
|
-
let zoom =
|
|
6052
|
+
let zoom = getInitialZoom(ctx, 0.5, 3);
|
|
6001
6053
|
const apply = () => {
|
|
6002
6054
|
panel.style.setProperty("--ofv-office-zoom", String(zoom));
|
|
6003
6055
|
panel.dispatchEvent(new CustomEvent("ofv-office-zoom"));
|
|
@@ -7129,6 +7181,7 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
7129
7181
|
return;
|
|
7130
7182
|
}
|
|
7131
7183
|
const chartPreviews = await readWorkbookCharts(arrayBuffer).catch(() => []);
|
|
7184
|
+
const workbookImages = await readWorkbookSheetImages(arrayBuffer).catch(() => /* @__PURE__ */ new Map());
|
|
7132
7185
|
const tabs = document.createElement("div");
|
|
7133
7186
|
tabs.className = "ofv-tabs";
|
|
7134
7187
|
tabs.setAttribute("role", "tablist");
|
|
@@ -7147,6 +7200,7 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
7147
7200
|
const heading = document.createElement("h3");
|
|
7148
7201
|
heading.textContent = sheetName;
|
|
7149
7202
|
const sheet = workbook.Sheets[sheetName];
|
|
7203
|
+
const sheetImages = workbookImages.get(sheetName) || [];
|
|
7150
7204
|
const range = trimWorkbookSheetRange(sheet, xlsx.utils.decode_range(sheet["!ref"] || "A1:A1"), xlsx.utils.decode_cell);
|
|
7151
7205
|
const rowCount = range.e.r - range.s.r + 1;
|
|
7152
7206
|
const columnCount = range.e.c - range.s.c + 1;
|
|
@@ -7172,7 +7226,8 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
7172
7226
|
xlsx.utils.encode_cell,
|
|
7173
7227
|
xlsx.utils.format_cell,
|
|
7174
7228
|
columnSizing,
|
|
7175
|
-
renderTableWindow
|
|
7229
|
+
renderTableWindow,
|
|
7230
|
+
sheetImages
|
|
7176
7231
|
)
|
|
7177
7232
|
);
|
|
7178
7233
|
windowControls?.update();
|
|
@@ -7242,6 +7297,143 @@ function renderSheetFallback(panel, extension, detail) {
|
|
|
7242
7297
|
section.append(title, meta, support);
|
|
7243
7298
|
panel.append(section);
|
|
7244
7299
|
}
|
|
7300
|
+
async function readWorkbookSheetImages(arrayBuffer) {
|
|
7301
|
+
const zip = await JSZip3.loadAsync(arrayBuffer);
|
|
7302
|
+
const fileNames = Object.keys(zip.files);
|
|
7303
|
+
if (!fileNames.some((name) => /^xl\/drawings\/.+\.xml$/i.test(name)) || !fileNames.some((name) => /^xl\/media\//i.test(name))) {
|
|
7304
|
+
return /* @__PURE__ */ new Map();
|
|
7305
|
+
}
|
|
7306
|
+
const workbookXml = await zip.file("xl/workbook.xml")?.async("text");
|
|
7307
|
+
if (!workbookXml || typeof DOMParser === "undefined") {
|
|
7308
|
+
return /* @__PURE__ */ new Map();
|
|
7309
|
+
}
|
|
7310
|
+
const workbookDoc = parseOfficeXml(workbookXml);
|
|
7311
|
+
if (!workbookDoc) {
|
|
7312
|
+
return /* @__PURE__ */ new Map();
|
|
7313
|
+
}
|
|
7314
|
+
const workbookRels = await readOfficeRelationships(zip, "xl/workbook.xml");
|
|
7315
|
+
const result = /* @__PURE__ */ new Map();
|
|
7316
|
+
const sheetElements = Array.from(workbookDoc.getElementsByTagName("*")).filter((element) => element.localName === "sheet");
|
|
7317
|
+
for (const sheetElement of sheetElements) {
|
|
7318
|
+
const sheetName = sheetElement.getAttribute("name") || "";
|
|
7319
|
+
const relationshipId = getXmlAttribute3(sheetElement, "id");
|
|
7320
|
+
const sheetRel = workbookRels.find((rel) => rel.id === relationshipId && /\/worksheet$/i.test(rel.type));
|
|
7321
|
+
const sheetPath = resolveOfficeRelationshipTarget("xl/workbook.xml", sheetRel?.target);
|
|
7322
|
+
if (!sheetName || !sheetPath) {
|
|
7323
|
+
continue;
|
|
7324
|
+
}
|
|
7325
|
+
const images = await readWorksheetImages(zip, sheetPath);
|
|
7326
|
+
if (images.length > 0) {
|
|
7327
|
+
result.set(sheetName, images);
|
|
7328
|
+
}
|
|
7329
|
+
}
|
|
7330
|
+
return result;
|
|
7331
|
+
}
|
|
7332
|
+
async function readWorksheetImages(zip, sheetPath) {
|
|
7333
|
+
const sheetXml = await zip.file(sheetPath)?.async("text");
|
|
7334
|
+
const sheetDoc = sheetXml ? parseOfficeXml(sheetXml) : void 0;
|
|
7335
|
+
if (!sheetDoc) {
|
|
7336
|
+
return [];
|
|
7337
|
+
}
|
|
7338
|
+
const sheetRels = await readOfficeRelationships(zip, sheetPath);
|
|
7339
|
+
const drawingIds = Array.from(sheetDoc.getElementsByTagName("*")).filter((element) => element.localName === "drawing").map((element) => getXmlAttribute3(element, "id")).filter((id) => Boolean(id));
|
|
7340
|
+
const images = [];
|
|
7341
|
+
for (const drawingId of drawingIds) {
|
|
7342
|
+
const drawingRel = sheetRels.find((rel) => rel.id === drawingId && /\/drawing$/i.test(rel.type));
|
|
7343
|
+
const drawingPath = resolveOfficeRelationshipTarget(sheetPath, drawingRel?.target);
|
|
7344
|
+
if (drawingPath) {
|
|
7345
|
+
images.push(...await readWorksheetDrawingImages(zip, drawingPath));
|
|
7346
|
+
}
|
|
7347
|
+
}
|
|
7348
|
+
return images;
|
|
7349
|
+
}
|
|
7350
|
+
async function readWorksheetDrawingImages(zip, drawingPath) {
|
|
7351
|
+
const drawingXml = await zip.file(drawingPath)?.async("text");
|
|
7352
|
+
const drawingDoc = drawingXml ? parseOfficeXml(drawingXml) : void 0;
|
|
7353
|
+
if (!drawingDoc) {
|
|
7354
|
+
return [];
|
|
7355
|
+
}
|
|
7356
|
+
const drawingRels = await readOfficeRelationships(zip, drawingPath);
|
|
7357
|
+
const anchors = Array.from(drawingDoc.getElementsByTagName("*")).filter(
|
|
7358
|
+
(element) => element.localName === "twoCellAnchor" || element.localName === "oneCellAnchor"
|
|
7359
|
+
);
|
|
7360
|
+
const images = [];
|
|
7361
|
+
for (const anchor of anchors) {
|
|
7362
|
+
const from = Array.from(anchor.children).find((element) => element.localName === "from");
|
|
7363
|
+
const embedId = findDrawingImageRelationshipId(anchor);
|
|
7364
|
+
const mediaRel = drawingRels.find((rel) => rel.id === embedId && /\/image$/i.test(rel.type));
|
|
7365
|
+
const mediaPath = resolveOfficeRelationshipTarget(drawingPath, mediaRel?.target);
|
|
7366
|
+
const mediaFile = mediaPath ? zip.file(mediaPath) : void 0;
|
|
7367
|
+
if (!from || !mediaPath || !mediaFile) {
|
|
7368
|
+
continue;
|
|
7369
|
+
}
|
|
7370
|
+
const mimeType = mimeTypeFromImagePath(mediaPath);
|
|
7371
|
+
images.push({
|
|
7372
|
+
row: readDrawingMarkerIndex(from, "row"),
|
|
7373
|
+
column: readDrawingMarkerIndex(from, "col"),
|
|
7374
|
+
fileName: mediaPath.split("/").pop() || "image",
|
|
7375
|
+
mimeType,
|
|
7376
|
+
dataUrl: `data:${mimeType};base64,${await mediaFile.async("base64")}`,
|
|
7377
|
+
title: readDrawingImageTitle(anchor)
|
|
7378
|
+
});
|
|
7379
|
+
}
|
|
7380
|
+
return images;
|
|
7381
|
+
}
|
|
7382
|
+
function findDrawingImageRelationshipId(anchor) {
|
|
7383
|
+
for (const element of Array.from(anchor.getElementsByTagName("*"))) {
|
|
7384
|
+
if (element.localName === "blip") {
|
|
7385
|
+
return getXmlAttribute3(element, "embed") || getXmlAttribute3(element, "link") || void 0;
|
|
7386
|
+
}
|
|
7387
|
+
}
|
|
7388
|
+
return void 0;
|
|
7389
|
+
}
|
|
7390
|
+
function readDrawingImageTitle(anchor) {
|
|
7391
|
+
const nonVisualProperties = Array.from(anchor.getElementsByTagName("*")).find((element) => element.localName === "cNvPr");
|
|
7392
|
+
return nonVisualProperties?.getAttribute("descr") || nonVisualProperties?.getAttribute("name") || void 0;
|
|
7393
|
+
}
|
|
7394
|
+
function readDrawingMarkerIndex(marker, localName) {
|
|
7395
|
+
const element = Array.from(marker.children).find((child) => child.localName === localName);
|
|
7396
|
+
const value = Number.parseInt(element?.textContent || "0", 10);
|
|
7397
|
+
return Number.isFinite(value) && value >= 0 ? value : 0;
|
|
7398
|
+
}
|
|
7399
|
+
async function readOfficeRelationships(zip, partPath) {
|
|
7400
|
+
const xml = await zip.file(relationshipPathForPart(partPath))?.async("text");
|
|
7401
|
+
const doc = xml ? parseOfficeXml(xml) : void 0;
|
|
7402
|
+
if (!doc) {
|
|
7403
|
+
return [];
|
|
7404
|
+
}
|
|
7405
|
+
return Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "Relationship").map((element) => ({
|
|
7406
|
+
id: element.getAttribute("Id") || "",
|
|
7407
|
+
type: element.getAttribute("Type") || "",
|
|
7408
|
+
target: element.getAttribute("Target") || ""
|
|
7409
|
+
}));
|
|
7410
|
+
}
|
|
7411
|
+
function parseOfficeXml(xml) {
|
|
7412
|
+
const doc = new DOMParser().parseFromString(xml, "application/xml");
|
|
7413
|
+
return doc.querySelector("parsererror") ? void 0 : doc;
|
|
7414
|
+
}
|
|
7415
|
+
function resolveOfficeRelationshipTarget(sourcePath, target) {
|
|
7416
|
+
return resolvePptxRelationshipTarget(sourcePath, target);
|
|
7417
|
+
}
|
|
7418
|
+
function mimeTypeFromImagePath(path) {
|
|
7419
|
+
const extension = path.split(".").pop()?.toLowerCase();
|
|
7420
|
+
switch (extension) {
|
|
7421
|
+
case "jpg":
|
|
7422
|
+
case "jpeg":
|
|
7423
|
+
return "image/jpeg";
|
|
7424
|
+
case "gif":
|
|
7425
|
+
return "image/gif";
|
|
7426
|
+
case "webp":
|
|
7427
|
+
return "image/webp";
|
|
7428
|
+
case "bmp":
|
|
7429
|
+
return "image/bmp";
|
|
7430
|
+
case "svg":
|
|
7431
|
+
return "image/svg+xml";
|
|
7432
|
+
case "png":
|
|
7433
|
+
default:
|
|
7434
|
+
return "image/png";
|
|
7435
|
+
}
|
|
7436
|
+
}
|
|
7245
7437
|
function renderEncryptedOfficeByFileInfo(panel, fileLabel, title) {
|
|
7246
7438
|
const section = createSection(title);
|
|
7247
7439
|
section.classList.add("ofv-encrypted");
|
|
@@ -7619,7 +7811,7 @@ function createWindowButton(label, onClick) {
|
|
|
7619
7811
|
function maxStart(total, size) {
|
|
7620
7812
|
return Math.max(0, total - size);
|
|
7621
7813
|
}
|
|
7622
|
-
function createWorkbookSheetTable(sheet, range, sheetIndex, viewport, encodeCell, formatCell, columnSizing, rerender) {
|
|
7814
|
+
function createWorkbookSheetTable(sheet, range, sheetIndex, viewport, encodeCell, formatCell, columnSizing, rerender, images = []) {
|
|
7623
7815
|
const table = document.createElement("table");
|
|
7624
7816
|
table.id = `ofv-sheet-${sheetIndex + 1}`;
|
|
7625
7817
|
table.className = "ofv-workbook-table";
|
|
@@ -7628,6 +7820,7 @@ function createWorkbookSheetTable(sheet, range, sheetIndex, viewport, encodeCell
|
|
|
7628
7820
|
const columnStart = range.s.c + viewport.columnStart;
|
|
7629
7821
|
const rowStart = range.s.r + viewport.rowStart;
|
|
7630
7822
|
const mergePlan = createSheetMergePlan(sheet["!merges"] || [], rowStart, rowEnd, columnStart, columnEnd);
|
|
7823
|
+
const imagesByCell = groupWorkbookImagesByCell(images);
|
|
7631
7824
|
const colGroup = document.createElement("colgroup");
|
|
7632
7825
|
let tableWidth = 0;
|
|
7633
7826
|
for (let columnIndex = columnStart; columnIndex <= columnEnd; columnIndex += 1) {
|
|
@@ -7682,6 +7875,7 @@ function createWorkbookSheetTable(sheet, range, sheetIndex, viewport, encodeCell
|
|
|
7682
7875
|
if (text.includes("\n")) {
|
|
7683
7876
|
cell.classList.add("ofv-cell-multiline");
|
|
7684
7877
|
}
|
|
7878
|
+
appendWorkbookCellImages(cell, imagesByCell.get(`${rowIndex}:${columnIndex}`), text);
|
|
7685
7879
|
appendColumnResizeHandle(cell, columnIndex, columnSizing);
|
|
7686
7880
|
row.append(cell);
|
|
7687
7881
|
}
|
|
@@ -7764,6 +7958,39 @@ function createSheetMergePlan(merges, rowStart, rowEnd, columnStart, columnEnd)
|
|
|
7764
7958
|
}
|
|
7765
7959
|
return { anchors, covered };
|
|
7766
7960
|
}
|
|
7961
|
+
function groupWorkbookImagesByCell(images) {
|
|
7962
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
7963
|
+
for (const image of images) {
|
|
7964
|
+
const key = `${image.row}:${image.column}`;
|
|
7965
|
+
const items = grouped.get(key) || [];
|
|
7966
|
+
items.push(image);
|
|
7967
|
+
grouped.set(key, items);
|
|
7968
|
+
}
|
|
7969
|
+
return grouped;
|
|
7970
|
+
}
|
|
7971
|
+
function appendWorkbookCellImages(cell, images, text) {
|
|
7972
|
+
if (!images?.length) {
|
|
7973
|
+
return;
|
|
7974
|
+
}
|
|
7975
|
+
if (isWorkbookImagePlaceholderValue(text)) {
|
|
7976
|
+
cell.textContent = "";
|
|
7977
|
+
cell.removeAttribute("title");
|
|
7978
|
+
}
|
|
7979
|
+
cell.classList.add("ofv-cell-image");
|
|
7980
|
+
for (const image of images) {
|
|
7981
|
+
const figure = document.createElement("figure");
|
|
7982
|
+
figure.className = "ofv-workbook-image";
|
|
7983
|
+
const element = document.createElement("img");
|
|
7984
|
+
element.src = image.dataUrl;
|
|
7985
|
+
element.alt = image.title || image.fileName || "Excel embedded image";
|
|
7986
|
+
element.loading = "lazy";
|
|
7987
|
+
figure.append(element);
|
|
7988
|
+
cell.append(figure);
|
|
7989
|
+
}
|
|
7990
|
+
}
|
|
7991
|
+
function isWorkbookImagePlaceholderValue(text) {
|
|
7992
|
+
return /^#(?:VALUE|NAME|REF|N\/A|NULL|NUM|DIV\/0)!?$/i.test(text.trim());
|
|
7993
|
+
}
|
|
7767
7994
|
function getSheetColumnWidth(column) {
|
|
7768
7995
|
if (column?.hidden) {
|
|
7769
7996
|
return 0;
|
|
@@ -8086,6 +8313,69 @@ function normalizePptxLayout(container) {
|
|
|
8086
8313
|
for (const slide of slideCanvases) {
|
|
8087
8314
|
slide.style.backgroundColor = "#FFFFFF";
|
|
8088
8315
|
}
|
|
8316
|
+
normalizePptxMirroredText(container);
|
|
8317
|
+
}
|
|
8318
|
+
function normalizePptxMirroredText(container) {
|
|
8319
|
+
const mirroredContainers = Array.from(container.querySelectorAll("div")).filter((element) => {
|
|
8320
|
+
const text = element.textContent?.trim();
|
|
8321
|
+
if (!text || element.children.length === 0) {
|
|
8322
|
+
return false;
|
|
8323
|
+
}
|
|
8324
|
+
const styleTransform = element.style.transform;
|
|
8325
|
+
return hasPptxMirrorTransform(styleTransform, "x") || hasPptxMirrorTransform(styleTransform, "y");
|
|
8326
|
+
});
|
|
8327
|
+
for (const element of mirroredContainers) {
|
|
8328
|
+
const flipX = hasPptxMirrorTransform(element.style.transform, "x");
|
|
8329
|
+
const flipY = hasPptxMirrorTransform(element.style.transform, "y");
|
|
8330
|
+
const targets = findPptxMirroredTextTargets(element);
|
|
8331
|
+
for (const target of targets) {
|
|
8332
|
+
counterMirrorPptxTextTarget(target, flipX, flipY);
|
|
8333
|
+
}
|
|
8334
|
+
}
|
|
8335
|
+
}
|
|
8336
|
+
function findPptxMirroredTextTargets(element) {
|
|
8337
|
+
const children = Array.from(element.children).filter((child) => child instanceof HTMLElement);
|
|
8338
|
+
const absoluteTextChildren = children.filter((child) => Boolean(child.textContent?.trim()) && child.style.position === "absolute");
|
|
8339
|
+
if (absoluteTextChildren.length > 0) {
|
|
8340
|
+
return absoluteTextChildren;
|
|
8341
|
+
}
|
|
8342
|
+
return children.filter((child) => Boolean(child.textContent?.trim()));
|
|
8343
|
+
}
|
|
8344
|
+
function hasPptxMirrorTransform(transform, axis) {
|
|
8345
|
+
if (!transform) {
|
|
8346
|
+
return false;
|
|
8347
|
+
}
|
|
8348
|
+
if (axis === "x" && /scaleX\(\s*-1\s*\)/i.test(transform)) {
|
|
8349
|
+
return true;
|
|
8350
|
+
}
|
|
8351
|
+
if (axis === "y" && /scaleY\(\s*-1\s*\)/i.test(transform)) {
|
|
8352
|
+
return true;
|
|
8353
|
+
}
|
|
8354
|
+
const matrix = transform.match(/matrix\(\s*([^,\s]+)\s*,\s*([^,\s]+)\s*,\s*([^,\s]+)\s*,\s*([^,\s]+)/i);
|
|
8355
|
+
if (!matrix) {
|
|
8356
|
+
return false;
|
|
8357
|
+
}
|
|
8358
|
+
const xScale = Number(matrix[1]);
|
|
8359
|
+
const yScale = Number(matrix[4]);
|
|
8360
|
+
return axis === "x" ? xScale < 0 : yScale < 0;
|
|
8361
|
+
}
|
|
8362
|
+
function counterMirrorPptxTextTarget(target, flipX, flipY) {
|
|
8363
|
+
const applied = target.dataset.ofvPptxCounterMirror ?? "";
|
|
8364
|
+
const transforms = [];
|
|
8365
|
+
if (flipX && !applied.includes("x")) {
|
|
8366
|
+
transforms.push("scaleX(-1)");
|
|
8367
|
+
}
|
|
8368
|
+
if (flipY && !applied.includes("y")) {
|
|
8369
|
+
transforms.push("scaleY(-1)");
|
|
8370
|
+
}
|
|
8371
|
+
if (transforms.length === 0) {
|
|
8372
|
+
return;
|
|
8373
|
+
}
|
|
8374
|
+
target.style.transform = `${target.style.transform || ""} ${transforms.join(" ")}`.trim();
|
|
8375
|
+
if (!target.style.transformOrigin) {
|
|
8376
|
+
target.style.transformOrigin = "center center";
|
|
8377
|
+
}
|
|
8378
|
+
target.dataset.ofvPptxCounterMirror = `${applied}${flipX ? "x" : ""}${flipY ? "y" : ""}`;
|
|
8089
8379
|
}
|
|
8090
8380
|
function findPptxSlideCanvases(container) {
|
|
8091
8381
|
const slideWrappers = Array.from(container.querySelectorAll("div[data-slide-index]"));
|
|
@@ -18579,7 +18869,7 @@ async function createPostScriptPreview(bytes, url, fileName, size, fit, toolbar)
|
|
|
18579
18869
|
preview.append(summary);
|
|
18580
18870
|
return { element: preview };
|
|
18581
18871
|
}
|
|
18582
|
-
async function createPdfCompatibleAiPreview(bytes, url, fileName, size, fit, toolbar, pdfOffset = 0) {
|
|
18872
|
+
async function createPdfCompatibleAiPreview(bytes, url, fileName, size, fit, toolbar, pdfOffset = 0, zoom = 1) {
|
|
18583
18873
|
const wrapper = document.createElement("div");
|
|
18584
18874
|
wrapper.className = "ofv-ai-pdf-preview";
|
|
18585
18875
|
let pdfUrl = url;
|
|
@@ -18594,6 +18884,7 @@ async function createPdfCompatibleAiPreview(bytes, url, fileName, size, fit, too
|
|
|
18594
18884
|
viewport: wrapper,
|
|
18595
18885
|
size,
|
|
18596
18886
|
fit,
|
|
18887
|
+
zoom,
|
|
18597
18888
|
toolbar,
|
|
18598
18889
|
fallbackTitle: "AI PDF \u517C\u5BB9\u9884\u89C8\u5931\u8D25",
|
|
18599
18890
|
revokeUrlOnDestroy: false
|