@open-file-viewer/core 0.1.12 → 0.1.14

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
@@ -735,7 +735,7 @@ function createViewer(options) {
735
735
  if (destroyed || token !== renderToken) {
736
736
  return;
737
737
  }
738
- currentInstance?.destroy();
738
+ destroyPreviewInstance(currentInstance);
739
739
  currentInstance = void 0;
740
740
  viewport.replaceChildren();
741
741
  setLoading(true);
@@ -757,7 +757,7 @@ function createViewer(options) {
757
757
  setError
758
758
  });
759
759
  if (destroyed || token !== renderToken) {
760
- nextInstance.destroy();
760
+ destroyPreviewInstance(nextInstance);
761
761
  return;
762
762
  }
763
763
  currentInstance = nextInstance;
@@ -814,7 +814,7 @@ function createViewer(options) {
814
814
  destroyed = true;
815
815
  renderToken += 1;
816
816
  resizeObserver.destroy();
817
- currentInstance?.destroy();
817
+ destroyPreviewInstance(currentInstance);
818
818
  toolbar?.destroy();
819
819
  theme.destroy();
820
820
  container.replaceChildren();
@@ -825,6 +825,16 @@ function createViewer(options) {
825
825
  }
826
826
  };
827
827
  }
828
+ function destroyPreviewInstance(instance) {
829
+ if (!instance) {
830
+ return;
831
+ }
832
+ try {
833
+ instance.destroy();
834
+ } catch (error) {
835
+ console.error("Failed to destroy file preview instance:", error);
836
+ }
837
+ }
828
838
  function normalizeQueue(options) {
829
839
  if (options.files && options.files.length > 0) {
830
840
  return options.files.map(
@@ -4628,7 +4638,7 @@ async function renderPdfDocumentPreview(options) {
4628
4638
  },
4629
4639
  destroy() {
4630
4640
  options.viewport.classList.remove("ofv-center");
4631
- documentTask?.destroy?.();
4641
+ destroyPdfResource(documentTask);
4632
4642
  if (options.revokeUrlOnDestroy) {
4633
4643
  revokeObjectUrl(options.fileUrl, Boolean(options.isExternal));
4634
4644
  }
@@ -4861,13 +4871,27 @@ async function renderPdfDocumentPreview(options) {
4861
4871
  }
4862
4872
  });
4863
4873
  pageStates.length = 0;
4864
- void pdfDocument.destroy?.();
4874
+ destroyPdfResource(pdfDocument);
4875
+ destroyPdfResource(documentTask);
4865
4876
  if (options.revokeUrlOnDestroy) {
4866
4877
  revokeObjectUrl(options.fileUrl, Boolean(options.isExternal));
4867
4878
  }
4868
4879
  }
4869
4880
  };
4870
4881
  }
4882
+ function destroyPdfResource(resource) {
4883
+ if (!resource || typeof resource !== "object") {
4884
+ return;
4885
+ }
4886
+ const candidate = resource;
4887
+ if (typeof candidate.destroy === "function") {
4888
+ void candidate.destroy();
4889
+ return;
4890
+ }
4891
+ if (typeof candidate.cleanup === "function") {
4892
+ void candidate.cleanup();
4893
+ }
4894
+ }
4871
4895
  function getPdfOutputScale() {
4872
4896
  if (typeof window === "undefined") {
4873
4897
  return 1;
@@ -7158,6 +7182,7 @@ async function renderSheet(panel, arrayBuffer, extension) {
7158
7182
  return;
7159
7183
  }
7160
7184
  const chartPreviews = await readWorkbookCharts(arrayBuffer).catch(() => []);
7185
+ const workbookImages = await readWorkbookSheetImages(arrayBuffer).catch(() => /* @__PURE__ */ new Map());
7161
7186
  const tabs = document.createElement("div");
7162
7187
  tabs.className = "ofv-tabs";
7163
7188
  tabs.setAttribute("role", "tablist");
@@ -7176,6 +7201,7 @@ async function renderSheet(panel, arrayBuffer, extension) {
7176
7201
  const heading = document.createElement("h3");
7177
7202
  heading.textContent = sheetName;
7178
7203
  const sheet = workbook.Sheets[sheetName];
7204
+ const sheetImages = workbookImages.get(sheetName) || [];
7179
7205
  const range = trimWorkbookSheetRange(sheet, xlsx.utils.decode_range(sheet["!ref"] || "A1:A1"), xlsx.utils.decode_cell);
7180
7206
  const rowCount = range.e.r - range.s.r + 1;
7181
7207
  const columnCount = range.e.c - range.s.c + 1;
@@ -7201,7 +7227,8 @@ async function renderSheet(panel, arrayBuffer, extension) {
7201
7227
  xlsx.utils.encode_cell,
7202
7228
  xlsx.utils.format_cell,
7203
7229
  columnSizing,
7204
- renderTableWindow
7230
+ renderTableWindow,
7231
+ sheetImages
7205
7232
  )
7206
7233
  );
7207
7234
  windowControls?.update();
@@ -7271,6 +7298,143 @@ function renderSheetFallback(panel, extension, detail) {
7271
7298
  section.append(title, meta, support);
7272
7299
  panel.append(section);
7273
7300
  }
7301
+ async function readWorkbookSheetImages(arrayBuffer) {
7302
+ const zip = await import_jszip3.default.loadAsync(arrayBuffer);
7303
+ const fileNames = Object.keys(zip.files);
7304
+ if (!fileNames.some((name) => /^xl\/drawings\/.+\.xml$/i.test(name)) || !fileNames.some((name) => /^xl\/media\//i.test(name))) {
7305
+ return /* @__PURE__ */ new Map();
7306
+ }
7307
+ const workbookXml = await zip.file("xl/workbook.xml")?.async("text");
7308
+ if (!workbookXml || typeof DOMParser === "undefined") {
7309
+ return /* @__PURE__ */ new Map();
7310
+ }
7311
+ const workbookDoc = parseOfficeXml(workbookXml);
7312
+ if (!workbookDoc) {
7313
+ return /* @__PURE__ */ new Map();
7314
+ }
7315
+ const workbookRels = await readOfficeRelationships(zip, "xl/workbook.xml");
7316
+ const result = /* @__PURE__ */ new Map();
7317
+ const sheetElements = Array.from(workbookDoc.getElementsByTagName("*")).filter((element) => element.localName === "sheet");
7318
+ for (const sheetElement of sheetElements) {
7319
+ const sheetName = sheetElement.getAttribute("name") || "";
7320
+ const relationshipId = getXmlAttribute3(sheetElement, "id");
7321
+ const sheetRel = workbookRels.find((rel) => rel.id === relationshipId && /\/worksheet$/i.test(rel.type));
7322
+ const sheetPath = resolveOfficeRelationshipTarget("xl/workbook.xml", sheetRel?.target);
7323
+ if (!sheetName || !sheetPath) {
7324
+ continue;
7325
+ }
7326
+ const images = await readWorksheetImages(zip, sheetPath);
7327
+ if (images.length > 0) {
7328
+ result.set(sheetName, images);
7329
+ }
7330
+ }
7331
+ return result;
7332
+ }
7333
+ async function readWorksheetImages(zip, sheetPath) {
7334
+ const sheetXml = await zip.file(sheetPath)?.async("text");
7335
+ const sheetDoc = sheetXml ? parseOfficeXml(sheetXml) : void 0;
7336
+ if (!sheetDoc) {
7337
+ return [];
7338
+ }
7339
+ const sheetRels = await readOfficeRelationships(zip, sheetPath);
7340
+ const drawingIds = Array.from(sheetDoc.getElementsByTagName("*")).filter((element) => element.localName === "drawing").map((element) => getXmlAttribute3(element, "id")).filter((id) => Boolean(id));
7341
+ const images = [];
7342
+ for (const drawingId of drawingIds) {
7343
+ const drawingRel = sheetRels.find((rel) => rel.id === drawingId && /\/drawing$/i.test(rel.type));
7344
+ const drawingPath = resolveOfficeRelationshipTarget(sheetPath, drawingRel?.target);
7345
+ if (drawingPath) {
7346
+ images.push(...await readWorksheetDrawingImages(zip, drawingPath));
7347
+ }
7348
+ }
7349
+ return images;
7350
+ }
7351
+ async function readWorksheetDrawingImages(zip, drawingPath) {
7352
+ const drawingXml = await zip.file(drawingPath)?.async("text");
7353
+ const drawingDoc = drawingXml ? parseOfficeXml(drawingXml) : void 0;
7354
+ if (!drawingDoc) {
7355
+ return [];
7356
+ }
7357
+ const drawingRels = await readOfficeRelationships(zip, drawingPath);
7358
+ const anchors = Array.from(drawingDoc.getElementsByTagName("*")).filter(
7359
+ (element) => element.localName === "twoCellAnchor" || element.localName === "oneCellAnchor"
7360
+ );
7361
+ const images = [];
7362
+ for (const anchor of anchors) {
7363
+ const from = Array.from(anchor.children).find((element) => element.localName === "from");
7364
+ const embedId = findDrawingImageRelationshipId(anchor);
7365
+ const mediaRel = drawingRels.find((rel) => rel.id === embedId && /\/image$/i.test(rel.type));
7366
+ const mediaPath = resolveOfficeRelationshipTarget(drawingPath, mediaRel?.target);
7367
+ const mediaFile = mediaPath ? zip.file(mediaPath) : void 0;
7368
+ if (!from || !mediaPath || !mediaFile) {
7369
+ continue;
7370
+ }
7371
+ const mimeType = mimeTypeFromImagePath(mediaPath);
7372
+ images.push({
7373
+ row: readDrawingMarkerIndex(from, "row"),
7374
+ column: readDrawingMarkerIndex(from, "col"),
7375
+ fileName: mediaPath.split("/").pop() || "image",
7376
+ mimeType,
7377
+ dataUrl: `data:${mimeType};base64,${await mediaFile.async("base64")}`,
7378
+ title: readDrawingImageTitle(anchor)
7379
+ });
7380
+ }
7381
+ return images;
7382
+ }
7383
+ function findDrawingImageRelationshipId(anchor) {
7384
+ for (const element of Array.from(anchor.getElementsByTagName("*"))) {
7385
+ if (element.localName === "blip") {
7386
+ return getXmlAttribute3(element, "embed") || getXmlAttribute3(element, "link") || void 0;
7387
+ }
7388
+ }
7389
+ return void 0;
7390
+ }
7391
+ function readDrawingImageTitle(anchor) {
7392
+ const nonVisualProperties = Array.from(anchor.getElementsByTagName("*")).find((element) => element.localName === "cNvPr");
7393
+ return nonVisualProperties?.getAttribute("descr") || nonVisualProperties?.getAttribute("name") || void 0;
7394
+ }
7395
+ function readDrawingMarkerIndex(marker, localName) {
7396
+ const element = Array.from(marker.children).find((child) => child.localName === localName);
7397
+ const value = Number.parseInt(element?.textContent || "0", 10);
7398
+ return Number.isFinite(value) && value >= 0 ? value : 0;
7399
+ }
7400
+ async function readOfficeRelationships(zip, partPath) {
7401
+ const xml = await zip.file(relationshipPathForPart(partPath))?.async("text");
7402
+ const doc = xml ? parseOfficeXml(xml) : void 0;
7403
+ if (!doc) {
7404
+ return [];
7405
+ }
7406
+ return Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "Relationship").map((element) => ({
7407
+ id: element.getAttribute("Id") || "",
7408
+ type: element.getAttribute("Type") || "",
7409
+ target: element.getAttribute("Target") || ""
7410
+ }));
7411
+ }
7412
+ function parseOfficeXml(xml) {
7413
+ const doc = new DOMParser().parseFromString(xml, "application/xml");
7414
+ return doc.querySelector("parsererror") ? void 0 : doc;
7415
+ }
7416
+ function resolveOfficeRelationshipTarget(sourcePath, target) {
7417
+ return resolvePptxRelationshipTarget(sourcePath, target);
7418
+ }
7419
+ function mimeTypeFromImagePath(path) {
7420
+ const extension = path.split(".").pop()?.toLowerCase();
7421
+ switch (extension) {
7422
+ case "jpg":
7423
+ case "jpeg":
7424
+ return "image/jpeg";
7425
+ case "gif":
7426
+ return "image/gif";
7427
+ case "webp":
7428
+ return "image/webp";
7429
+ case "bmp":
7430
+ return "image/bmp";
7431
+ case "svg":
7432
+ return "image/svg+xml";
7433
+ case "png":
7434
+ default:
7435
+ return "image/png";
7436
+ }
7437
+ }
7274
7438
  function renderEncryptedOfficeByFileInfo(panel, fileLabel, title) {
7275
7439
  const section = createSection(title);
7276
7440
  section.classList.add("ofv-encrypted");
@@ -7648,7 +7812,7 @@ function createWindowButton(label, onClick) {
7648
7812
  function maxStart(total, size) {
7649
7813
  return Math.max(0, total - size);
7650
7814
  }
7651
- function createWorkbookSheetTable(sheet, range, sheetIndex, viewport, encodeCell, formatCell, columnSizing, rerender) {
7815
+ function createWorkbookSheetTable(sheet, range, sheetIndex, viewport, encodeCell, formatCell, columnSizing, rerender, images = []) {
7652
7816
  const table = document.createElement("table");
7653
7817
  table.id = `ofv-sheet-${sheetIndex + 1}`;
7654
7818
  table.className = "ofv-workbook-table";
@@ -7657,6 +7821,7 @@ function createWorkbookSheetTable(sheet, range, sheetIndex, viewport, encodeCell
7657
7821
  const columnStart = range.s.c + viewport.columnStart;
7658
7822
  const rowStart = range.s.r + viewport.rowStart;
7659
7823
  const mergePlan = createSheetMergePlan(sheet["!merges"] || [], rowStart, rowEnd, columnStart, columnEnd);
7824
+ const imagesByCell = groupWorkbookImagesByCell(images);
7660
7825
  const colGroup = document.createElement("colgroup");
7661
7826
  let tableWidth = 0;
7662
7827
  for (let columnIndex = columnStart; columnIndex <= columnEnd; columnIndex += 1) {
@@ -7711,6 +7876,7 @@ function createWorkbookSheetTable(sheet, range, sheetIndex, viewport, encodeCell
7711
7876
  if (text.includes("\n")) {
7712
7877
  cell.classList.add("ofv-cell-multiline");
7713
7878
  }
7879
+ appendWorkbookCellImages(cell, imagesByCell.get(`${rowIndex}:${columnIndex}`), text);
7714
7880
  appendColumnResizeHandle(cell, columnIndex, columnSizing);
7715
7881
  row.append(cell);
7716
7882
  }
@@ -7793,6 +7959,39 @@ function createSheetMergePlan(merges, rowStart, rowEnd, columnStart, columnEnd)
7793
7959
  }
7794
7960
  return { anchors, covered };
7795
7961
  }
7962
+ function groupWorkbookImagesByCell(images) {
7963
+ const grouped = /* @__PURE__ */ new Map();
7964
+ for (const image of images) {
7965
+ const key = `${image.row}:${image.column}`;
7966
+ const items = grouped.get(key) || [];
7967
+ items.push(image);
7968
+ grouped.set(key, items);
7969
+ }
7970
+ return grouped;
7971
+ }
7972
+ function appendWorkbookCellImages(cell, images, text) {
7973
+ if (!images?.length) {
7974
+ return;
7975
+ }
7976
+ if (isWorkbookImagePlaceholderValue(text)) {
7977
+ cell.textContent = "";
7978
+ cell.removeAttribute("title");
7979
+ }
7980
+ cell.classList.add("ofv-cell-image");
7981
+ for (const image of images) {
7982
+ const figure = document.createElement("figure");
7983
+ figure.className = "ofv-workbook-image";
7984
+ const element = document.createElement("img");
7985
+ element.src = image.dataUrl;
7986
+ element.alt = image.title || image.fileName || "Excel embedded image";
7987
+ element.loading = "lazy";
7988
+ figure.append(element);
7989
+ cell.append(figure);
7990
+ }
7991
+ }
7992
+ function isWorkbookImagePlaceholderValue(text) {
7993
+ return /^#(?:VALUE|NAME|REF|N\/A|NULL|NUM|DIV\/0)!?$/i.test(text.trim());
7994
+ }
7796
7995
  function getSheetColumnWidth(column) {
7797
7996
  if (column?.hidden) {
7798
7997
  return 0;
@@ -8115,6 +8314,69 @@ function normalizePptxLayout(container) {
8115
8314
  for (const slide of slideCanvases) {
8116
8315
  slide.style.backgroundColor = "#FFFFFF";
8117
8316
  }
8317
+ normalizePptxMirroredText(container);
8318
+ }
8319
+ function normalizePptxMirroredText(container) {
8320
+ const mirroredContainers = Array.from(container.querySelectorAll("div")).filter((element) => {
8321
+ const text = element.textContent?.trim();
8322
+ if (!text || element.children.length === 0) {
8323
+ return false;
8324
+ }
8325
+ const styleTransform = element.style.transform;
8326
+ return hasPptxMirrorTransform(styleTransform, "x") || hasPptxMirrorTransform(styleTransform, "y");
8327
+ });
8328
+ for (const element of mirroredContainers) {
8329
+ const flipX = hasPptxMirrorTransform(element.style.transform, "x");
8330
+ const flipY = hasPptxMirrorTransform(element.style.transform, "y");
8331
+ const targets = findPptxMirroredTextTargets(element);
8332
+ for (const target of targets) {
8333
+ counterMirrorPptxTextTarget(target, flipX, flipY);
8334
+ }
8335
+ }
8336
+ }
8337
+ function findPptxMirroredTextTargets(element) {
8338
+ const children = Array.from(element.children).filter((child) => child instanceof HTMLElement);
8339
+ const absoluteTextChildren = children.filter((child) => Boolean(child.textContent?.trim()) && child.style.position === "absolute");
8340
+ if (absoluteTextChildren.length > 0) {
8341
+ return absoluteTextChildren;
8342
+ }
8343
+ return children.filter((child) => Boolean(child.textContent?.trim()));
8344
+ }
8345
+ function hasPptxMirrorTransform(transform, axis) {
8346
+ if (!transform) {
8347
+ return false;
8348
+ }
8349
+ if (axis === "x" && /scaleX\(\s*-1\s*\)/i.test(transform)) {
8350
+ return true;
8351
+ }
8352
+ if (axis === "y" && /scaleY\(\s*-1\s*\)/i.test(transform)) {
8353
+ return true;
8354
+ }
8355
+ const matrix = transform.match(/matrix\(\s*([^,\s]+)\s*,\s*([^,\s]+)\s*,\s*([^,\s]+)\s*,\s*([^,\s]+)/i);
8356
+ if (!matrix) {
8357
+ return false;
8358
+ }
8359
+ const xScale = Number(matrix[1]);
8360
+ const yScale = Number(matrix[4]);
8361
+ return axis === "x" ? xScale < 0 : yScale < 0;
8362
+ }
8363
+ function counterMirrorPptxTextTarget(target, flipX, flipY) {
8364
+ const applied = target.dataset.ofvPptxCounterMirror ?? "";
8365
+ const transforms = [];
8366
+ if (flipX && !applied.includes("x")) {
8367
+ transforms.push("scaleX(-1)");
8368
+ }
8369
+ if (flipY && !applied.includes("y")) {
8370
+ transforms.push("scaleY(-1)");
8371
+ }
8372
+ if (transforms.length === 0) {
8373
+ return;
8374
+ }
8375
+ target.style.transform = `${target.style.transform || ""} ${transforms.join(" ")}`.trim();
8376
+ if (!target.style.transformOrigin) {
8377
+ target.style.transformOrigin = "center center";
8378
+ }
8379
+ target.dataset.ofvPptxCounterMirror = `${applied}${flipX ? "x" : ""}${flipY ? "y" : ""}`;
8118
8380
  }
8119
8381
  function findPptxSlideCanvases(container) {
8120
8382
  const slideWrappers = Array.from(container.querySelectorAll("div[data-slide-index]"));