@open-file-viewer/core 0.1.13 → 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 +240 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +240 -2
- package/dist/index.js.map +1 -1
- package/dist/style.css +20 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -7182,6 +7182,7 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
7182
7182
|
return;
|
|
7183
7183
|
}
|
|
7184
7184
|
const chartPreviews = await readWorkbookCharts(arrayBuffer).catch(() => []);
|
|
7185
|
+
const workbookImages = await readWorkbookSheetImages(arrayBuffer).catch(() => /* @__PURE__ */ new Map());
|
|
7185
7186
|
const tabs = document.createElement("div");
|
|
7186
7187
|
tabs.className = "ofv-tabs";
|
|
7187
7188
|
tabs.setAttribute("role", "tablist");
|
|
@@ -7200,6 +7201,7 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
7200
7201
|
const heading = document.createElement("h3");
|
|
7201
7202
|
heading.textContent = sheetName;
|
|
7202
7203
|
const sheet = workbook.Sheets[sheetName];
|
|
7204
|
+
const sheetImages = workbookImages.get(sheetName) || [];
|
|
7203
7205
|
const range = trimWorkbookSheetRange(sheet, xlsx.utils.decode_range(sheet["!ref"] || "A1:A1"), xlsx.utils.decode_cell);
|
|
7204
7206
|
const rowCount = range.e.r - range.s.r + 1;
|
|
7205
7207
|
const columnCount = range.e.c - range.s.c + 1;
|
|
@@ -7225,7 +7227,8 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
7225
7227
|
xlsx.utils.encode_cell,
|
|
7226
7228
|
xlsx.utils.format_cell,
|
|
7227
7229
|
columnSizing,
|
|
7228
|
-
renderTableWindow
|
|
7230
|
+
renderTableWindow,
|
|
7231
|
+
sheetImages
|
|
7229
7232
|
)
|
|
7230
7233
|
);
|
|
7231
7234
|
windowControls?.update();
|
|
@@ -7295,6 +7298,143 @@ function renderSheetFallback(panel, extension, detail) {
|
|
|
7295
7298
|
section.append(title, meta, support);
|
|
7296
7299
|
panel.append(section);
|
|
7297
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
|
+
}
|
|
7298
7438
|
function renderEncryptedOfficeByFileInfo(panel, fileLabel, title) {
|
|
7299
7439
|
const section = createSection(title);
|
|
7300
7440
|
section.classList.add("ofv-encrypted");
|
|
@@ -7672,7 +7812,7 @@ function createWindowButton(label, onClick) {
|
|
|
7672
7812
|
function maxStart(total, size) {
|
|
7673
7813
|
return Math.max(0, total - size);
|
|
7674
7814
|
}
|
|
7675
|
-
function createWorkbookSheetTable(sheet, range, sheetIndex, viewport, encodeCell, formatCell, columnSizing, rerender) {
|
|
7815
|
+
function createWorkbookSheetTable(sheet, range, sheetIndex, viewport, encodeCell, formatCell, columnSizing, rerender, images = []) {
|
|
7676
7816
|
const table = document.createElement("table");
|
|
7677
7817
|
table.id = `ofv-sheet-${sheetIndex + 1}`;
|
|
7678
7818
|
table.className = "ofv-workbook-table";
|
|
@@ -7681,6 +7821,7 @@ function createWorkbookSheetTable(sheet, range, sheetIndex, viewport, encodeCell
|
|
|
7681
7821
|
const columnStart = range.s.c + viewport.columnStart;
|
|
7682
7822
|
const rowStart = range.s.r + viewport.rowStart;
|
|
7683
7823
|
const mergePlan = createSheetMergePlan(sheet["!merges"] || [], rowStart, rowEnd, columnStart, columnEnd);
|
|
7824
|
+
const imagesByCell = groupWorkbookImagesByCell(images);
|
|
7684
7825
|
const colGroup = document.createElement("colgroup");
|
|
7685
7826
|
let tableWidth = 0;
|
|
7686
7827
|
for (let columnIndex = columnStart; columnIndex <= columnEnd; columnIndex += 1) {
|
|
@@ -7735,6 +7876,7 @@ function createWorkbookSheetTable(sheet, range, sheetIndex, viewport, encodeCell
|
|
|
7735
7876
|
if (text.includes("\n")) {
|
|
7736
7877
|
cell.classList.add("ofv-cell-multiline");
|
|
7737
7878
|
}
|
|
7879
|
+
appendWorkbookCellImages(cell, imagesByCell.get(`${rowIndex}:${columnIndex}`), text);
|
|
7738
7880
|
appendColumnResizeHandle(cell, columnIndex, columnSizing);
|
|
7739
7881
|
row.append(cell);
|
|
7740
7882
|
}
|
|
@@ -7817,6 +7959,39 @@ function createSheetMergePlan(merges, rowStart, rowEnd, columnStart, columnEnd)
|
|
|
7817
7959
|
}
|
|
7818
7960
|
return { anchors, covered };
|
|
7819
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
|
+
}
|
|
7820
7995
|
function getSheetColumnWidth(column) {
|
|
7821
7996
|
if (column?.hidden) {
|
|
7822
7997
|
return 0;
|
|
@@ -8139,6 +8314,69 @@ function normalizePptxLayout(container) {
|
|
|
8139
8314
|
for (const slide of slideCanvases) {
|
|
8140
8315
|
slide.style.backgroundColor = "#FFFFFF";
|
|
8141
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" : ""}`;
|
|
8142
8380
|
}
|
|
8143
8381
|
function findPptxSlideCanvases(container) {
|
|
8144
8382
|
const slideWrappers = Array.from(container.querySelectorAll("div[data-slide-index]"));
|