@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.cjs
CHANGED
|
@@ -606,6 +606,48 @@ function revokeObjectUrl(url, isExternal) {
|
|
|
606
606
|
}
|
|
607
607
|
}
|
|
608
608
|
|
|
609
|
+
// src/messages.ts
|
|
610
|
+
var defaultMessages = {
|
|
611
|
+
"zh-CN": {
|
|
612
|
+
loading: "\u6B63\u5728\u52A0\u8F7D\u9884\u89C8...",
|
|
613
|
+
unsupportedTitle: "\u5F53\u524D\u6587\u4EF6\u6682\u4E0D\u652F\u6301\u5728\u7EBF\u9884\u89C8",
|
|
614
|
+
downloadTitle: "\u5F53\u524D\u6587\u4EF6\u53EF\u4E0B\u8F7D\u540E\u67E5\u770B",
|
|
615
|
+
downloadFile: "\u4E0B\u8F7D\u6587\u4EF6",
|
|
616
|
+
file: "\u6587\u4EF6",
|
|
617
|
+
unnamedFile: "\u672A\u547D\u540D\u6587\u4EF6",
|
|
618
|
+
format: "\u683C\u5F0F",
|
|
619
|
+
unknown: "\u672A\u77E5",
|
|
620
|
+
mime: "MIME",
|
|
621
|
+
undeclared: "\u672A\u58F0\u660E",
|
|
622
|
+
size: "\u5927\u5C0F",
|
|
623
|
+
source: "\u6765\u6E90",
|
|
624
|
+
remoteUrl: "\u8FDC\u7A0B URL",
|
|
625
|
+
localFile: "\u672C\u5730/\u5185\u5B58\u6587\u4EF6"
|
|
626
|
+
},
|
|
627
|
+
"en-US": {
|
|
628
|
+
loading: "Loading preview...",
|
|
629
|
+
unsupportedTitle: "Preview is not available for this file",
|
|
630
|
+
downloadTitle: "This file can be downloaded and opened locally",
|
|
631
|
+
downloadFile: "Download file",
|
|
632
|
+
file: "File",
|
|
633
|
+
unnamedFile: "Untitled file",
|
|
634
|
+
format: "Format",
|
|
635
|
+
unknown: "Unknown",
|
|
636
|
+
mime: "MIME",
|
|
637
|
+
undeclared: "Not declared",
|
|
638
|
+
size: "Size",
|
|
639
|
+
source: "Source",
|
|
640
|
+
remoteUrl: "Remote URL",
|
|
641
|
+
localFile: "Local or in-memory file"
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
function resolveMessages(options) {
|
|
645
|
+
return {
|
|
646
|
+
...defaultMessages[options.locale || "zh-CN"],
|
|
647
|
+
...options.messages
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
|
|
609
651
|
// src/plugins/fallback.ts
|
|
610
652
|
function fallbackPlugin() {
|
|
611
653
|
return {
|
|
@@ -623,12 +665,12 @@ function fallbackPlugin() {
|
|
|
623
665
|
const panel = document.createElement("div");
|
|
624
666
|
panel.className = "ofv-fallback";
|
|
625
667
|
const title = document.createElement("strong");
|
|
626
|
-
title.textContent = ctx.options.fallback === "download" ?
|
|
627
|
-
const meta = createFallbackMeta(ctx.file);
|
|
668
|
+
title.textContent = ctx.options.fallback === "download" ? ctx.options.messages.downloadTitle : ctx.options.messages.unsupportedTitle;
|
|
669
|
+
const meta = createFallbackMeta(ctx.file, ctx.options.messages);
|
|
628
670
|
const download = document.createElement("a");
|
|
629
671
|
download.href = url;
|
|
630
672
|
download.download = ctx.file.name;
|
|
631
|
-
download.textContent =
|
|
673
|
+
download.textContent = ctx.options.messages.downloadFile;
|
|
632
674
|
panel.append(title, meta, download);
|
|
633
675
|
ctx.viewport.classList.add("ofv-center");
|
|
634
676
|
ctx.viewport.append(panel);
|
|
@@ -644,14 +686,14 @@ function fallbackPlugin() {
|
|
|
644
686
|
}
|
|
645
687
|
};
|
|
646
688
|
}
|
|
647
|
-
function createFallbackMeta(file) {
|
|
689
|
+
function createFallbackMeta(file, messages) {
|
|
648
690
|
const meta = document.createElement("dl");
|
|
649
691
|
meta.className = "ofv-fallback-meta";
|
|
650
|
-
appendFallbackMeta(meta,
|
|
651
|
-
appendFallbackMeta(meta,
|
|
652
|
-
appendFallbackMeta(meta,
|
|
653
|
-
appendFallbackMeta(meta,
|
|
654
|
-
appendFallbackMeta(meta,
|
|
692
|
+
appendFallbackMeta(meta, messages.file, file.name || messages.unnamedFile);
|
|
693
|
+
appendFallbackMeta(meta, messages.format, file.extension ? `.${file.extension}` : messages.unknown);
|
|
694
|
+
appendFallbackMeta(meta, messages.mime, file.mimeType || messages.undeclared);
|
|
695
|
+
appendFallbackMeta(meta, messages.size, file.size === void 0 ? messages.unknown : formatFallbackBytes(file.size));
|
|
696
|
+
appendFallbackMeta(meta, messages.source, file.url ? messages.remoteUrl : messages.localFile);
|
|
655
697
|
return meta;
|
|
656
698
|
}
|
|
657
699
|
function appendFallbackMeta(parent, label, value) {
|
|
@@ -709,15 +751,17 @@ function createViewer(options) {
|
|
|
709
751
|
host.append(status, viewport);
|
|
710
752
|
container.replaceChildren(host);
|
|
711
753
|
const normalizedOptions = {
|
|
754
|
+
...options,
|
|
712
755
|
fit: options.fit || "contain",
|
|
713
756
|
fallback: options.fallback || "inline",
|
|
714
|
-
|
|
757
|
+
zoom: normalizeInitialZoom(options.zoom),
|
|
758
|
+
messages: resolveMessages(options)
|
|
715
759
|
};
|
|
716
760
|
let destroyed = false;
|
|
717
761
|
let renderToken = 0;
|
|
718
762
|
const setLoading = (loading) => {
|
|
719
763
|
status.hidden = !loading;
|
|
720
|
-
status.textContent = loading ?
|
|
764
|
+
status.textContent = loading ? normalizedOptions.messages.loading : "";
|
|
721
765
|
};
|
|
722
766
|
const setError = (error) => {
|
|
723
767
|
status.hidden = false;
|
|
@@ -873,6 +917,9 @@ function clampIndex(index, length) {
|
|
|
873
917
|
}
|
|
874
918
|
return Math.min(Math.max(index, 0), length - 1);
|
|
875
919
|
}
|
|
920
|
+
function normalizeInitialZoom(zoom) {
|
|
921
|
+
return typeof zoom === "number" && Number.isFinite(zoom) && zoom > 0 ? zoom : 1;
|
|
922
|
+
}
|
|
876
923
|
function applyTheme(container, theme) {
|
|
877
924
|
const media = window.matchMedia?.("(prefers-color-scheme: dark)");
|
|
878
925
|
const classes = ["ofv-theme-light", "ofv-theme-dark"];
|
|
@@ -1749,6 +1796,98 @@ async function findPlugin(plugins, file) {
|
|
|
1749
1796
|
return fallbackPlugin();
|
|
1750
1797
|
}
|
|
1751
1798
|
|
|
1799
|
+
// src/plugins/utils.ts
|
|
1800
|
+
async function readArrayBuffer(file) {
|
|
1801
|
+
if (file.source instanceof ArrayBuffer) {
|
|
1802
|
+
return file.source;
|
|
1803
|
+
}
|
|
1804
|
+
if (file.blob) {
|
|
1805
|
+
return file.blob.arrayBuffer();
|
|
1806
|
+
}
|
|
1807
|
+
if (typeof file.source === "string") {
|
|
1808
|
+
const response = await fetch(file.source);
|
|
1809
|
+
if (!response.ok) {
|
|
1810
|
+
throw new Error(`Failed to fetch file: ${response.status}`);
|
|
1811
|
+
}
|
|
1812
|
+
return response.arrayBuffer();
|
|
1813
|
+
}
|
|
1814
|
+
throw new Error("Unsupported file source.");
|
|
1815
|
+
}
|
|
1816
|
+
async function readTextFile(file) {
|
|
1817
|
+
const decode = (buffer) => decodeTextBuffer(buffer);
|
|
1818
|
+
if (typeof file.source === "string") {
|
|
1819
|
+
const response = await fetch(file.source);
|
|
1820
|
+
if (!response.ok) {
|
|
1821
|
+
throw new Error(`Failed to fetch text file: ${response.status}`);
|
|
1822
|
+
}
|
|
1823
|
+
return decode(await response.arrayBuffer());
|
|
1824
|
+
}
|
|
1825
|
+
if (file.blob) {
|
|
1826
|
+
return decode(await file.blob.arrayBuffer());
|
|
1827
|
+
}
|
|
1828
|
+
if (file.source instanceof ArrayBuffer) {
|
|
1829
|
+
return decode(file.source);
|
|
1830
|
+
}
|
|
1831
|
+
return String(file.source);
|
|
1832
|
+
}
|
|
1833
|
+
function createPanel(className = "") {
|
|
1834
|
+
const panel = document.createElement("div");
|
|
1835
|
+
panel.className = `ofv-panel ${className}`.trim();
|
|
1836
|
+
return panel;
|
|
1837
|
+
}
|
|
1838
|
+
function getInitialZoom(ctx, min = 0.1, max = 8) {
|
|
1839
|
+
return Math.min(max, Math.max(min, ctx.options.zoom));
|
|
1840
|
+
}
|
|
1841
|
+
function createSection(title) {
|
|
1842
|
+
const section = document.createElement("section");
|
|
1843
|
+
section.className = "ofv-section";
|
|
1844
|
+
const heading = document.createElement("h3");
|
|
1845
|
+
heading.textContent = title;
|
|
1846
|
+
section.append(heading);
|
|
1847
|
+
return section;
|
|
1848
|
+
}
|
|
1849
|
+
function appendMeta(parent, label, value) {
|
|
1850
|
+
const row = document.createElement("div");
|
|
1851
|
+
row.className = "ofv-meta-row";
|
|
1852
|
+
const key = document.createElement("span");
|
|
1853
|
+
key.textContent = label;
|
|
1854
|
+
const content = document.createElement("strong");
|
|
1855
|
+
content.textContent = String(value);
|
|
1856
|
+
row.append(key, content);
|
|
1857
|
+
parent.append(row);
|
|
1858
|
+
}
|
|
1859
|
+
function decodeTextBuffer(buffer) {
|
|
1860
|
+
return decodeTextBytes(new Uint8Array(buffer));
|
|
1861
|
+
}
|
|
1862
|
+
function decodeTextBytes(bytes) {
|
|
1863
|
+
if (bytes.length >= 3 && bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191) {
|
|
1864
|
+
return new TextDecoder("utf-8").decode(bytes.subarray(3));
|
|
1865
|
+
}
|
|
1866
|
+
if (bytes.length >= 2) {
|
|
1867
|
+
if (bytes[0] === 255 && bytes[1] === 254) {
|
|
1868
|
+
return new TextDecoder("utf-16le").decode(bytes.subarray(2));
|
|
1869
|
+
}
|
|
1870
|
+
if (bytes[0] === 254 && bytes[1] === 255) {
|
|
1871
|
+
return new TextDecoder("utf-16be").decode(bytes.subarray(2));
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
try {
|
|
1875
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
1876
|
+
} catch {
|
|
1877
|
+
return decodeWithFallback(bytes, "gb18030") || decodeWithFallback(bytes, "gbk") || new TextDecoder("utf-8").decode(bytes);
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
function decodeWithFallback(bytes, encoding) {
|
|
1881
|
+
try {
|
|
1882
|
+
return new TextDecoder(encoding).decode(bytes);
|
|
1883
|
+
} catch {
|
|
1884
|
+
return void 0;
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
function resolveFormat(file, mimeMap) {
|
|
1888
|
+
return file.extension || mimeMap[file.mimeType] || "";
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1752
1891
|
// src/plugins/image.ts
|
|
1753
1892
|
var imageExtensions = /* @__PURE__ */ new Set([
|
|
1754
1893
|
"jpg",
|
|
@@ -1862,7 +2001,7 @@ function imagePlugin() {
|
|
|
1862
2001
|
canvasSource.setAttribute("role", "img");
|
|
1863
2002
|
canvasSource.setAttribute("aria-label", ctx.file.name);
|
|
1864
2003
|
}
|
|
1865
|
-
let scale =
|
|
2004
|
+
let scale = getInitialZoom(ctx);
|
|
1866
2005
|
let rotation = 0;
|
|
1867
2006
|
let offsetX = 0;
|
|
1868
2007
|
let offsetY = 0;
|
|
@@ -3690,95 +3829,6 @@ function readAiffExtended(bytes, offset) {
|
|
|
3690
3829
|
return mantissa * Math.pow(2, exponent - 16383 - 63);
|
|
3691
3830
|
}
|
|
3692
3831
|
|
|
3693
|
-
// src/plugins/utils.ts
|
|
3694
|
-
async function readArrayBuffer(file) {
|
|
3695
|
-
if (file.source instanceof ArrayBuffer) {
|
|
3696
|
-
return file.source;
|
|
3697
|
-
}
|
|
3698
|
-
if (file.blob) {
|
|
3699
|
-
return file.blob.arrayBuffer();
|
|
3700
|
-
}
|
|
3701
|
-
if (typeof file.source === "string") {
|
|
3702
|
-
const response = await fetch(file.source);
|
|
3703
|
-
if (!response.ok) {
|
|
3704
|
-
throw new Error(`Failed to fetch file: ${response.status}`);
|
|
3705
|
-
}
|
|
3706
|
-
return response.arrayBuffer();
|
|
3707
|
-
}
|
|
3708
|
-
throw new Error("Unsupported file source.");
|
|
3709
|
-
}
|
|
3710
|
-
async function readTextFile(file) {
|
|
3711
|
-
const decode = (buffer) => decodeTextBuffer(buffer);
|
|
3712
|
-
if (typeof file.source === "string") {
|
|
3713
|
-
const response = await fetch(file.source);
|
|
3714
|
-
if (!response.ok) {
|
|
3715
|
-
throw new Error(`Failed to fetch text file: ${response.status}`);
|
|
3716
|
-
}
|
|
3717
|
-
return decode(await response.arrayBuffer());
|
|
3718
|
-
}
|
|
3719
|
-
if (file.blob) {
|
|
3720
|
-
return decode(await file.blob.arrayBuffer());
|
|
3721
|
-
}
|
|
3722
|
-
if (file.source instanceof ArrayBuffer) {
|
|
3723
|
-
return decode(file.source);
|
|
3724
|
-
}
|
|
3725
|
-
return String(file.source);
|
|
3726
|
-
}
|
|
3727
|
-
function createPanel(className = "") {
|
|
3728
|
-
const panel = document.createElement("div");
|
|
3729
|
-
panel.className = `ofv-panel ${className}`.trim();
|
|
3730
|
-
return panel;
|
|
3731
|
-
}
|
|
3732
|
-
function createSection(title) {
|
|
3733
|
-
const section = document.createElement("section");
|
|
3734
|
-
section.className = "ofv-section";
|
|
3735
|
-
const heading = document.createElement("h3");
|
|
3736
|
-
heading.textContent = title;
|
|
3737
|
-
section.append(heading);
|
|
3738
|
-
return section;
|
|
3739
|
-
}
|
|
3740
|
-
function appendMeta(parent, label, value) {
|
|
3741
|
-
const row = document.createElement("div");
|
|
3742
|
-
row.className = "ofv-meta-row";
|
|
3743
|
-
const key = document.createElement("span");
|
|
3744
|
-
key.textContent = label;
|
|
3745
|
-
const content = document.createElement("strong");
|
|
3746
|
-
content.textContent = String(value);
|
|
3747
|
-
row.append(key, content);
|
|
3748
|
-
parent.append(row);
|
|
3749
|
-
}
|
|
3750
|
-
function decodeTextBuffer(buffer) {
|
|
3751
|
-
return decodeTextBytes(new Uint8Array(buffer));
|
|
3752
|
-
}
|
|
3753
|
-
function decodeTextBytes(bytes) {
|
|
3754
|
-
if (bytes.length >= 3 && bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191) {
|
|
3755
|
-
return new TextDecoder("utf-8").decode(bytes.subarray(3));
|
|
3756
|
-
}
|
|
3757
|
-
if (bytes.length >= 2) {
|
|
3758
|
-
if (bytes[0] === 255 && bytes[1] === 254) {
|
|
3759
|
-
return new TextDecoder("utf-16le").decode(bytes.subarray(2));
|
|
3760
|
-
}
|
|
3761
|
-
if (bytes[0] === 254 && bytes[1] === 255) {
|
|
3762
|
-
return new TextDecoder("utf-16be").decode(bytes.subarray(2));
|
|
3763
|
-
}
|
|
3764
|
-
}
|
|
3765
|
-
try {
|
|
3766
|
-
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
3767
|
-
} catch {
|
|
3768
|
-
return decodeWithFallback(bytes, "gb18030") || decodeWithFallback(bytes, "gbk") || new TextDecoder("utf-8").decode(bytes);
|
|
3769
|
-
}
|
|
3770
|
-
}
|
|
3771
|
-
function decodeWithFallback(bytes, encoding) {
|
|
3772
|
-
try {
|
|
3773
|
-
return new TextDecoder(encoding).decode(bytes);
|
|
3774
|
-
} catch {
|
|
3775
|
-
return void 0;
|
|
3776
|
-
}
|
|
3777
|
-
}
|
|
3778
|
-
function resolveFormat(file, mimeMap) {
|
|
3779
|
-
return file.extension || mimeMap[file.mimeType] || "";
|
|
3780
|
-
}
|
|
3781
|
-
|
|
3782
3832
|
// src/plugins/text.ts
|
|
3783
3833
|
var langMap = {
|
|
3784
3834
|
js: "javascript",
|
|
@@ -4247,7 +4297,7 @@ function textPlugin() {
|
|
|
4247
4297
|
};
|
|
4248
4298
|
}
|
|
4249
4299
|
function createTextZoomController(target, cssVariable, ctx) {
|
|
4250
|
-
let zoom =
|
|
4300
|
+
let zoom = getInitialZoom(ctx, 0.5, 3);
|
|
4251
4301
|
const apply = () => {
|
|
4252
4302
|
const normalized = Math.round(zoom * 100) / 100;
|
|
4253
4303
|
target.style.setProperty(cssVariable, String(normalized));
|
|
@@ -4557,6 +4607,7 @@ function pdfPlugin(options = {}) {
|
|
|
4557
4607
|
viewport: ctx.viewport,
|
|
4558
4608
|
size: ctx.size,
|
|
4559
4609
|
fit: ctx.options.fit,
|
|
4610
|
+
zoom: ctx.options.zoom,
|
|
4560
4611
|
toolbar: ctx.toolbar,
|
|
4561
4612
|
encryptedTitle: "PDF \u5DF2\u52A0\u5BC6\uFF0C\u65E0\u6CD5\u5728\u7EBF\u9884\u89C8",
|
|
4562
4613
|
encryptedMessage: "\u8BF7\u4E0B\u8F7D\u540E\u4F7F\u7528\u5BC6\u7801\u6253\u5F00\uFF0C\u6216\u4E0A\u4F20\u89E3\u5BC6\u540E\u7684 PDF \u6587\u4EF6\u3002",
|
|
@@ -4662,7 +4713,7 @@ async function renderPdfDocumentPreview(options) {
|
|
|
4662
4713
|
const pageStates = [];
|
|
4663
4714
|
let observer = null;
|
|
4664
4715
|
let currentSize = options.size;
|
|
4665
|
-
let zoomFactor = 1;
|
|
4716
|
+
let zoomFactor = getInitialZoom({ options: { zoom: options.zoom ?? 1 } }, 0.25, 4);
|
|
4666
4717
|
let rotation = 0;
|
|
4667
4718
|
const updateSummary = () => {
|
|
4668
4719
|
renderPdfSummary(summary, pdfDocument.numPages, pagesMeta, options.fit, zoomFactor);
|
|
@@ -5987,6 +6038,7 @@ async function renderConvertedOfficePreview(panel, ctx, options, conversionConte
|
|
|
5987
6038
|
viewport: panel,
|
|
5988
6039
|
size: ctx.size,
|
|
5989
6040
|
fit: ctx.options.fit,
|
|
6041
|
+
zoom: ctx.options.zoom,
|
|
5990
6042
|
toolbar: ctx.toolbar,
|
|
5991
6043
|
title: "Office \u9AD8\u4FDD\u771F\u8F6C\u6362\u9884\u89C8",
|
|
5992
6044
|
fallbackTitle: "Office \u8F6C\u6362\u540E\u7684 PDF \u65E0\u6CD5\u9884\u89C8",
|
|
@@ -6050,7 +6102,7 @@ function createOfficeZoomController(panel, ctx) {
|
|
|
6050
6102
|
if (!canZoom) {
|
|
6051
6103
|
return void 0;
|
|
6052
6104
|
}
|
|
6053
|
-
let zoom =
|
|
6105
|
+
let zoom = getInitialZoom(ctx, 0.5, 3);
|
|
6054
6106
|
const apply = () => {
|
|
6055
6107
|
panel.style.setProperty("--ofv-office-zoom", String(zoom));
|
|
6056
6108
|
panel.dispatchEvent(new CustomEvent("ofv-office-zoom"));
|
|
@@ -7182,6 +7234,7 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
7182
7234
|
return;
|
|
7183
7235
|
}
|
|
7184
7236
|
const chartPreviews = await readWorkbookCharts(arrayBuffer).catch(() => []);
|
|
7237
|
+
const workbookImages = await readWorkbookSheetImages(arrayBuffer).catch(() => /* @__PURE__ */ new Map());
|
|
7185
7238
|
const tabs = document.createElement("div");
|
|
7186
7239
|
tabs.className = "ofv-tabs";
|
|
7187
7240
|
tabs.setAttribute("role", "tablist");
|
|
@@ -7200,6 +7253,7 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
7200
7253
|
const heading = document.createElement("h3");
|
|
7201
7254
|
heading.textContent = sheetName;
|
|
7202
7255
|
const sheet = workbook.Sheets[sheetName];
|
|
7256
|
+
const sheetImages = workbookImages.get(sheetName) || [];
|
|
7203
7257
|
const range = trimWorkbookSheetRange(sheet, xlsx.utils.decode_range(sheet["!ref"] || "A1:A1"), xlsx.utils.decode_cell);
|
|
7204
7258
|
const rowCount = range.e.r - range.s.r + 1;
|
|
7205
7259
|
const columnCount = range.e.c - range.s.c + 1;
|
|
@@ -7225,7 +7279,8 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
7225
7279
|
xlsx.utils.encode_cell,
|
|
7226
7280
|
xlsx.utils.format_cell,
|
|
7227
7281
|
columnSizing,
|
|
7228
|
-
renderTableWindow
|
|
7282
|
+
renderTableWindow,
|
|
7283
|
+
sheetImages
|
|
7229
7284
|
)
|
|
7230
7285
|
);
|
|
7231
7286
|
windowControls?.update();
|
|
@@ -7295,6 +7350,143 @@ function renderSheetFallback(panel, extension, detail) {
|
|
|
7295
7350
|
section.append(title, meta, support);
|
|
7296
7351
|
panel.append(section);
|
|
7297
7352
|
}
|
|
7353
|
+
async function readWorkbookSheetImages(arrayBuffer) {
|
|
7354
|
+
const zip = await import_jszip3.default.loadAsync(arrayBuffer);
|
|
7355
|
+
const fileNames = Object.keys(zip.files);
|
|
7356
|
+
if (!fileNames.some((name) => /^xl\/drawings\/.+\.xml$/i.test(name)) || !fileNames.some((name) => /^xl\/media\//i.test(name))) {
|
|
7357
|
+
return /* @__PURE__ */ new Map();
|
|
7358
|
+
}
|
|
7359
|
+
const workbookXml = await zip.file("xl/workbook.xml")?.async("text");
|
|
7360
|
+
if (!workbookXml || typeof DOMParser === "undefined") {
|
|
7361
|
+
return /* @__PURE__ */ new Map();
|
|
7362
|
+
}
|
|
7363
|
+
const workbookDoc = parseOfficeXml(workbookXml);
|
|
7364
|
+
if (!workbookDoc) {
|
|
7365
|
+
return /* @__PURE__ */ new Map();
|
|
7366
|
+
}
|
|
7367
|
+
const workbookRels = await readOfficeRelationships(zip, "xl/workbook.xml");
|
|
7368
|
+
const result = /* @__PURE__ */ new Map();
|
|
7369
|
+
const sheetElements = Array.from(workbookDoc.getElementsByTagName("*")).filter((element) => element.localName === "sheet");
|
|
7370
|
+
for (const sheetElement of sheetElements) {
|
|
7371
|
+
const sheetName = sheetElement.getAttribute("name") || "";
|
|
7372
|
+
const relationshipId = getXmlAttribute3(sheetElement, "id");
|
|
7373
|
+
const sheetRel = workbookRels.find((rel) => rel.id === relationshipId && /\/worksheet$/i.test(rel.type));
|
|
7374
|
+
const sheetPath = resolveOfficeRelationshipTarget("xl/workbook.xml", sheetRel?.target);
|
|
7375
|
+
if (!sheetName || !sheetPath) {
|
|
7376
|
+
continue;
|
|
7377
|
+
}
|
|
7378
|
+
const images = await readWorksheetImages(zip, sheetPath);
|
|
7379
|
+
if (images.length > 0) {
|
|
7380
|
+
result.set(sheetName, images);
|
|
7381
|
+
}
|
|
7382
|
+
}
|
|
7383
|
+
return result;
|
|
7384
|
+
}
|
|
7385
|
+
async function readWorksheetImages(zip, sheetPath) {
|
|
7386
|
+
const sheetXml = await zip.file(sheetPath)?.async("text");
|
|
7387
|
+
const sheetDoc = sheetXml ? parseOfficeXml(sheetXml) : void 0;
|
|
7388
|
+
if (!sheetDoc) {
|
|
7389
|
+
return [];
|
|
7390
|
+
}
|
|
7391
|
+
const sheetRels = await readOfficeRelationships(zip, sheetPath);
|
|
7392
|
+
const drawingIds = Array.from(sheetDoc.getElementsByTagName("*")).filter((element) => element.localName === "drawing").map((element) => getXmlAttribute3(element, "id")).filter((id) => Boolean(id));
|
|
7393
|
+
const images = [];
|
|
7394
|
+
for (const drawingId of drawingIds) {
|
|
7395
|
+
const drawingRel = sheetRels.find((rel) => rel.id === drawingId && /\/drawing$/i.test(rel.type));
|
|
7396
|
+
const drawingPath = resolveOfficeRelationshipTarget(sheetPath, drawingRel?.target);
|
|
7397
|
+
if (drawingPath) {
|
|
7398
|
+
images.push(...await readWorksheetDrawingImages(zip, drawingPath));
|
|
7399
|
+
}
|
|
7400
|
+
}
|
|
7401
|
+
return images;
|
|
7402
|
+
}
|
|
7403
|
+
async function readWorksheetDrawingImages(zip, drawingPath) {
|
|
7404
|
+
const drawingXml = await zip.file(drawingPath)?.async("text");
|
|
7405
|
+
const drawingDoc = drawingXml ? parseOfficeXml(drawingXml) : void 0;
|
|
7406
|
+
if (!drawingDoc) {
|
|
7407
|
+
return [];
|
|
7408
|
+
}
|
|
7409
|
+
const drawingRels = await readOfficeRelationships(zip, drawingPath);
|
|
7410
|
+
const anchors = Array.from(drawingDoc.getElementsByTagName("*")).filter(
|
|
7411
|
+
(element) => element.localName === "twoCellAnchor" || element.localName === "oneCellAnchor"
|
|
7412
|
+
);
|
|
7413
|
+
const images = [];
|
|
7414
|
+
for (const anchor of anchors) {
|
|
7415
|
+
const from = Array.from(anchor.children).find((element) => element.localName === "from");
|
|
7416
|
+
const embedId = findDrawingImageRelationshipId(anchor);
|
|
7417
|
+
const mediaRel = drawingRels.find((rel) => rel.id === embedId && /\/image$/i.test(rel.type));
|
|
7418
|
+
const mediaPath = resolveOfficeRelationshipTarget(drawingPath, mediaRel?.target);
|
|
7419
|
+
const mediaFile = mediaPath ? zip.file(mediaPath) : void 0;
|
|
7420
|
+
if (!from || !mediaPath || !mediaFile) {
|
|
7421
|
+
continue;
|
|
7422
|
+
}
|
|
7423
|
+
const mimeType = mimeTypeFromImagePath(mediaPath);
|
|
7424
|
+
images.push({
|
|
7425
|
+
row: readDrawingMarkerIndex(from, "row"),
|
|
7426
|
+
column: readDrawingMarkerIndex(from, "col"),
|
|
7427
|
+
fileName: mediaPath.split("/").pop() || "image",
|
|
7428
|
+
mimeType,
|
|
7429
|
+
dataUrl: `data:${mimeType};base64,${await mediaFile.async("base64")}`,
|
|
7430
|
+
title: readDrawingImageTitle(anchor)
|
|
7431
|
+
});
|
|
7432
|
+
}
|
|
7433
|
+
return images;
|
|
7434
|
+
}
|
|
7435
|
+
function findDrawingImageRelationshipId(anchor) {
|
|
7436
|
+
for (const element of Array.from(anchor.getElementsByTagName("*"))) {
|
|
7437
|
+
if (element.localName === "blip") {
|
|
7438
|
+
return getXmlAttribute3(element, "embed") || getXmlAttribute3(element, "link") || void 0;
|
|
7439
|
+
}
|
|
7440
|
+
}
|
|
7441
|
+
return void 0;
|
|
7442
|
+
}
|
|
7443
|
+
function readDrawingImageTitle(anchor) {
|
|
7444
|
+
const nonVisualProperties = Array.from(anchor.getElementsByTagName("*")).find((element) => element.localName === "cNvPr");
|
|
7445
|
+
return nonVisualProperties?.getAttribute("descr") || nonVisualProperties?.getAttribute("name") || void 0;
|
|
7446
|
+
}
|
|
7447
|
+
function readDrawingMarkerIndex(marker, localName) {
|
|
7448
|
+
const element = Array.from(marker.children).find((child) => child.localName === localName);
|
|
7449
|
+
const value = Number.parseInt(element?.textContent || "0", 10);
|
|
7450
|
+
return Number.isFinite(value) && value >= 0 ? value : 0;
|
|
7451
|
+
}
|
|
7452
|
+
async function readOfficeRelationships(zip, partPath) {
|
|
7453
|
+
const xml = await zip.file(relationshipPathForPart(partPath))?.async("text");
|
|
7454
|
+
const doc = xml ? parseOfficeXml(xml) : void 0;
|
|
7455
|
+
if (!doc) {
|
|
7456
|
+
return [];
|
|
7457
|
+
}
|
|
7458
|
+
return Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "Relationship").map((element) => ({
|
|
7459
|
+
id: element.getAttribute("Id") || "",
|
|
7460
|
+
type: element.getAttribute("Type") || "",
|
|
7461
|
+
target: element.getAttribute("Target") || ""
|
|
7462
|
+
}));
|
|
7463
|
+
}
|
|
7464
|
+
function parseOfficeXml(xml) {
|
|
7465
|
+
const doc = new DOMParser().parseFromString(xml, "application/xml");
|
|
7466
|
+
return doc.querySelector("parsererror") ? void 0 : doc;
|
|
7467
|
+
}
|
|
7468
|
+
function resolveOfficeRelationshipTarget(sourcePath, target) {
|
|
7469
|
+
return resolvePptxRelationshipTarget(sourcePath, target);
|
|
7470
|
+
}
|
|
7471
|
+
function mimeTypeFromImagePath(path) {
|
|
7472
|
+
const extension = path.split(".").pop()?.toLowerCase();
|
|
7473
|
+
switch (extension) {
|
|
7474
|
+
case "jpg":
|
|
7475
|
+
case "jpeg":
|
|
7476
|
+
return "image/jpeg";
|
|
7477
|
+
case "gif":
|
|
7478
|
+
return "image/gif";
|
|
7479
|
+
case "webp":
|
|
7480
|
+
return "image/webp";
|
|
7481
|
+
case "bmp":
|
|
7482
|
+
return "image/bmp";
|
|
7483
|
+
case "svg":
|
|
7484
|
+
return "image/svg+xml";
|
|
7485
|
+
case "png":
|
|
7486
|
+
default:
|
|
7487
|
+
return "image/png";
|
|
7488
|
+
}
|
|
7489
|
+
}
|
|
7298
7490
|
function renderEncryptedOfficeByFileInfo(panel, fileLabel, title) {
|
|
7299
7491
|
const section = createSection(title);
|
|
7300
7492
|
section.classList.add("ofv-encrypted");
|
|
@@ -7672,7 +7864,7 @@ function createWindowButton(label, onClick) {
|
|
|
7672
7864
|
function maxStart(total, size) {
|
|
7673
7865
|
return Math.max(0, total - size);
|
|
7674
7866
|
}
|
|
7675
|
-
function createWorkbookSheetTable(sheet, range, sheetIndex, viewport, encodeCell, formatCell, columnSizing, rerender) {
|
|
7867
|
+
function createWorkbookSheetTable(sheet, range, sheetIndex, viewport, encodeCell, formatCell, columnSizing, rerender, images = []) {
|
|
7676
7868
|
const table = document.createElement("table");
|
|
7677
7869
|
table.id = `ofv-sheet-${sheetIndex + 1}`;
|
|
7678
7870
|
table.className = "ofv-workbook-table";
|
|
@@ -7681,6 +7873,7 @@ function createWorkbookSheetTable(sheet, range, sheetIndex, viewport, encodeCell
|
|
|
7681
7873
|
const columnStart = range.s.c + viewport.columnStart;
|
|
7682
7874
|
const rowStart = range.s.r + viewport.rowStart;
|
|
7683
7875
|
const mergePlan = createSheetMergePlan(sheet["!merges"] || [], rowStart, rowEnd, columnStart, columnEnd);
|
|
7876
|
+
const imagesByCell = groupWorkbookImagesByCell(images);
|
|
7684
7877
|
const colGroup = document.createElement("colgroup");
|
|
7685
7878
|
let tableWidth = 0;
|
|
7686
7879
|
for (let columnIndex = columnStart; columnIndex <= columnEnd; columnIndex += 1) {
|
|
@@ -7735,6 +7928,7 @@ function createWorkbookSheetTable(sheet, range, sheetIndex, viewport, encodeCell
|
|
|
7735
7928
|
if (text.includes("\n")) {
|
|
7736
7929
|
cell.classList.add("ofv-cell-multiline");
|
|
7737
7930
|
}
|
|
7931
|
+
appendWorkbookCellImages(cell, imagesByCell.get(`${rowIndex}:${columnIndex}`), text);
|
|
7738
7932
|
appendColumnResizeHandle(cell, columnIndex, columnSizing);
|
|
7739
7933
|
row.append(cell);
|
|
7740
7934
|
}
|
|
@@ -7817,6 +8011,39 @@ function createSheetMergePlan(merges, rowStart, rowEnd, columnStart, columnEnd)
|
|
|
7817
8011
|
}
|
|
7818
8012
|
return { anchors, covered };
|
|
7819
8013
|
}
|
|
8014
|
+
function groupWorkbookImagesByCell(images) {
|
|
8015
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
8016
|
+
for (const image of images) {
|
|
8017
|
+
const key = `${image.row}:${image.column}`;
|
|
8018
|
+
const items = grouped.get(key) || [];
|
|
8019
|
+
items.push(image);
|
|
8020
|
+
grouped.set(key, items);
|
|
8021
|
+
}
|
|
8022
|
+
return grouped;
|
|
8023
|
+
}
|
|
8024
|
+
function appendWorkbookCellImages(cell, images, text) {
|
|
8025
|
+
if (!images?.length) {
|
|
8026
|
+
return;
|
|
8027
|
+
}
|
|
8028
|
+
if (isWorkbookImagePlaceholderValue(text)) {
|
|
8029
|
+
cell.textContent = "";
|
|
8030
|
+
cell.removeAttribute("title");
|
|
8031
|
+
}
|
|
8032
|
+
cell.classList.add("ofv-cell-image");
|
|
8033
|
+
for (const image of images) {
|
|
8034
|
+
const figure = document.createElement("figure");
|
|
8035
|
+
figure.className = "ofv-workbook-image";
|
|
8036
|
+
const element = document.createElement("img");
|
|
8037
|
+
element.src = image.dataUrl;
|
|
8038
|
+
element.alt = image.title || image.fileName || "Excel embedded image";
|
|
8039
|
+
element.loading = "lazy";
|
|
8040
|
+
figure.append(element);
|
|
8041
|
+
cell.append(figure);
|
|
8042
|
+
}
|
|
8043
|
+
}
|
|
8044
|
+
function isWorkbookImagePlaceholderValue(text) {
|
|
8045
|
+
return /^#(?:VALUE|NAME|REF|N\/A|NULL|NUM|DIV\/0)!?$/i.test(text.trim());
|
|
8046
|
+
}
|
|
7820
8047
|
function getSheetColumnWidth(column) {
|
|
7821
8048
|
if (column?.hidden) {
|
|
7822
8049
|
return 0;
|
|
@@ -8139,6 +8366,69 @@ function normalizePptxLayout(container) {
|
|
|
8139
8366
|
for (const slide of slideCanvases) {
|
|
8140
8367
|
slide.style.backgroundColor = "#FFFFFF";
|
|
8141
8368
|
}
|
|
8369
|
+
normalizePptxMirroredText(container);
|
|
8370
|
+
}
|
|
8371
|
+
function normalizePptxMirroredText(container) {
|
|
8372
|
+
const mirroredContainers = Array.from(container.querySelectorAll("div")).filter((element) => {
|
|
8373
|
+
const text = element.textContent?.trim();
|
|
8374
|
+
if (!text || element.children.length === 0) {
|
|
8375
|
+
return false;
|
|
8376
|
+
}
|
|
8377
|
+
const styleTransform = element.style.transform;
|
|
8378
|
+
return hasPptxMirrorTransform(styleTransform, "x") || hasPptxMirrorTransform(styleTransform, "y");
|
|
8379
|
+
});
|
|
8380
|
+
for (const element of mirroredContainers) {
|
|
8381
|
+
const flipX = hasPptxMirrorTransform(element.style.transform, "x");
|
|
8382
|
+
const flipY = hasPptxMirrorTransform(element.style.transform, "y");
|
|
8383
|
+
const targets = findPptxMirroredTextTargets(element);
|
|
8384
|
+
for (const target of targets) {
|
|
8385
|
+
counterMirrorPptxTextTarget(target, flipX, flipY);
|
|
8386
|
+
}
|
|
8387
|
+
}
|
|
8388
|
+
}
|
|
8389
|
+
function findPptxMirroredTextTargets(element) {
|
|
8390
|
+
const children = Array.from(element.children).filter((child) => child instanceof HTMLElement);
|
|
8391
|
+
const absoluteTextChildren = children.filter((child) => Boolean(child.textContent?.trim()) && child.style.position === "absolute");
|
|
8392
|
+
if (absoluteTextChildren.length > 0) {
|
|
8393
|
+
return absoluteTextChildren;
|
|
8394
|
+
}
|
|
8395
|
+
return children.filter((child) => Boolean(child.textContent?.trim()));
|
|
8396
|
+
}
|
|
8397
|
+
function hasPptxMirrorTransform(transform, axis) {
|
|
8398
|
+
if (!transform) {
|
|
8399
|
+
return false;
|
|
8400
|
+
}
|
|
8401
|
+
if (axis === "x" && /scaleX\(\s*-1\s*\)/i.test(transform)) {
|
|
8402
|
+
return true;
|
|
8403
|
+
}
|
|
8404
|
+
if (axis === "y" && /scaleY\(\s*-1\s*\)/i.test(transform)) {
|
|
8405
|
+
return true;
|
|
8406
|
+
}
|
|
8407
|
+
const matrix = transform.match(/matrix\(\s*([^,\s]+)\s*,\s*([^,\s]+)\s*,\s*([^,\s]+)\s*,\s*([^,\s]+)/i);
|
|
8408
|
+
if (!matrix) {
|
|
8409
|
+
return false;
|
|
8410
|
+
}
|
|
8411
|
+
const xScale = Number(matrix[1]);
|
|
8412
|
+
const yScale = Number(matrix[4]);
|
|
8413
|
+
return axis === "x" ? xScale < 0 : yScale < 0;
|
|
8414
|
+
}
|
|
8415
|
+
function counterMirrorPptxTextTarget(target, flipX, flipY) {
|
|
8416
|
+
const applied = target.dataset.ofvPptxCounterMirror ?? "";
|
|
8417
|
+
const transforms = [];
|
|
8418
|
+
if (flipX && !applied.includes("x")) {
|
|
8419
|
+
transforms.push("scaleX(-1)");
|
|
8420
|
+
}
|
|
8421
|
+
if (flipY && !applied.includes("y")) {
|
|
8422
|
+
transforms.push("scaleY(-1)");
|
|
8423
|
+
}
|
|
8424
|
+
if (transforms.length === 0) {
|
|
8425
|
+
return;
|
|
8426
|
+
}
|
|
8427
|
+
target.style.transform = `${target.style.transform || ""} ${transforms.join(" ")}`.trim();
|
|
8428
|
+
if (!target.style.transformOrigin) {
|
|
8429
|
+
target.style.transformOrigin = "center center";
|
|
8430
|
+
}
|
|
8431
|
+
target.dataset.ofvPptxCounterMirror = `${applied}${flipX ? "x" : ""}${flipY ? "y" : ""}`;
|
|
8142
8432
|
}
|
|
8143
8433
|
function findPptxSlideCanvases(container) {
|
|
8144
8434
|
const slideWrappers = Array.from(container.querySelectorAll("div[data-slide-index]"));
|
|
@@ -18632,7 +18922,7 @@ async function createPostScriptPreview(bytes, url, fileName, size, fit, toolbar)
|
|
|
18632
18922
|
preview.append(summary);
|
|
18633
18923
|
return { element: preview };
|
|
18634
18924
|
}
|
|
18635
|
-
async function createPdfCompatibleAiPreview(bytes, url, fileName, size, fit, toolbar, pdfOffset = 0) {
|
|
18925
|
+
async function createPdfCompatibleAiPreview(bytes, url, fileName, size, fit, toolbar, pdfOffset = 0, zoom = 1) {
|
|
18636
18926
|
const wrapper = document.createElement("div");
|
|
18637
18927
|
wrapper.className = "ofv-ai-pdf-preview";
|
|
18638
18928
|
let pdfUrl = url;
|
|
@@ -18647,6 +18937,7 @@ async function createPdfCompatibleAiPreview(bytes, url, fileName, size, fit, too
|
|
|
18647
18937
|
viewport: wrapper,
|
|
18648
18938
|
size,
|
|
18649
18939
|
fit,
|
|
18940
|
+
zoom,
|
|
18650
18941
|
toolbar,
|
|
18651
18942
|
fallbackTitle: "AI PDF \u517C\u5BB9\u9884\u89C8\u5931\u8D25",
|
|
18652
18943
|
revokeUrlOnDestroy: false
|