@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/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" ? "\u5F53\u524D\u6587\u4EF6\u53EF\u4E0B\u8F7D\u540E\u67E5\u770B" : "\u5F53\u524D\u6587\u4EF6\u6682\u4E0D\u652F\u6301\u5728\u7EBF\u9884\u89C8";
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 = "\u4E0B\u8F7D\u6587\u4EF6";
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, "\u6587\u4EF6", file.name || "\u672A\u547D\u540D\u6587\u4EF6");
651
- appendFallbackMeta(meta, "\u683C\u5F0F", file.extension ? `.${file.extension}` : "\u672A\u77E5");
652
- appendFallbackMeta(meta, "MIME", file.mimeType || "\u672A\u58F0\u660E");
653
- appendFallbackMeta(meta, "\u5927\u5C0F", file.size === void 0 ? "\u672A\u77E5" : formatFallbackBytes(file.size));
654
- appendFallbackMeta(meta, "\u6765\u6E90", file.url ? "\u8FDC\u7A0B URL" : "\u672C\u5730/\u5185\u5B58\u6587\u4EF6");
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
- ...options
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 ? "Loading preview..." : "";
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 = 1;
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 = 1;
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 = 1;
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