@open-file-viewer/core 0.1.6 → 0.1.7
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 +45 -0
- package/dist/index.cjs +943 -54
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -2
- package/dist/index.d.ts +41 -2
- package/dist/index.js +943 -54
- package/dist/index.js.map +1 -1
- package/dist/style.css +218 -33
- package/package.json +7 -1
package/dist/index.cjs
CHANGED
|
@@ -324,7 +324,7 @@ var extensionMimeMap = {
|
|
|
324
324
|
};
|
|
325
325
|
async function normalizeFile(source, fileName, mimeType) {
|
|
326
326
|
if (typeof source === "string") {
|
|
327
|
-
const name2 = fileName || source
|
|
327
|
+
const name2 = fileName || getFileNameFromUrl(source) || "remote-file";
|
|
328
328
|
const extension2 = getExtension(name2);
|
|
329
329
|
return {
|
|
330
330
|
source,
|
|
@@ -369,6 +369,17 @@ async function normalizeFile(source, fileName, mimeType) {
|
|
|
369
369
|
blob
|
|
370
370
|
};
|
|
371
371
|
}
|
|
372
|
+
function getFileNameFromUrl(source) {
|
|
373
|
+
const rawName = source.split(/[?#]/, 1)[0]?.split("/").filter(Boolean).pop() || "";
|
|
374
|
+
if (!rawName) {
|
|
375
|
+
return "";
|
|
376
|
+
}
|
|
377
|
+
try {
|
|
378
|
+
return decodeURIComponent(rawName);
|
|
379
|
+
} catch {
|
|
380
|
+
return rawName;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
372
383
|
function getExtension(name) {
|
|
373
384
|
const clean = name.split("?")[0]?.split("#")[0] || "";
|
|
374
385
|
const index = clean.lastIndexOf(".");
|
|
@@ -5421,7 +5432,13 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
5421
5432
|
const xlsx = await import("xlsx");
|
|
5422
5433
|
let workbook;
|
|
5423
5434
|
try {
|
|
5424
|
-
workbook = extension === "csv" || extension === "tsv" ? xlsx.read(decodeTextBuffer(arrayBuffer), {
|
|
5435
|
+
workbook = extension === "csv" || extension === "tsv" ? xlsx.read(decodeTextBuffer(arrayBuffer), {
|
|
5436
|
+
type: "string",
|
|
5437
|
+
FS: extension === "tsv" ? " " : ",",
|
|
5438
|
+
cellDates: true,
|
|
5439
|
+
cellNF: true,
|
|
5440
|
+
cellStyles: true
|
|
5441
|
+
}) : xlsx.read(arrayBuffer, { type: "array", cellDates: true, cellNF: true, cellStyles: true });
|
|
5425
5442
|
} catch (error) {
|
|
5426
5443
|
if (isLegacyOfficeBinary(extension)) {
|
|
5427
5444
|
renderLegacyOfficeBinary(panel, extension, arrayBuffer, `\u8868\u683C\u89E3\u6790\u5931\u8D25\uFF1A${normalizeOfficeError(error)}`);
|
|
@@ -5449,7 +5466,7 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
5449
5466
|
const heading = document.createElement("h3");
|
|
5450
5467
|
heading.textContent = sheetName;
|
|
5451
5468
|
const sheet = workbook.Sheets[sheetName];
|
|
5452
|
-
const range = xlsx.utils.decode_range(sheet["!ref"] || "A1:A1");
|
|
5469
|
+
const range = trimWorkbookSheetRange(sheet, xlsx.utils.decode_range(sheet["!ref"] || "A1:A1"), xlsx.utils.decode_cell);
|
|
5453
5470
|
const rowCount = range.e.r - range.s.r + 1;
|
|
5454
5471
|
const columnCount = range.e.c - range.s.c + 1;
|
|
5455
5472
|
const formulaRows = collectFormulaRows(sheet, range, xlsx.utils.encode_cell);
|
|
@@ -5459,6 +5476,7 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
5459
5476
|
const tableWrapper = document.createElement("div");
|
|
5460
5477
|
tableWrapper.className = "ofv-table-scroll";
|
|
5461
5478
|
const viewport = createSheetViewport(rowCount, columnCount);
|
|
5479
|
+
const columnSizing = { widths: /* @__PURE__ */ new Map() };
|
|
5462
5480
|
const windowControls = createSheetWindowControls(viewport, () => renderTableWindow());
|
|
5463
5481
|
const renderTableWindow = () => {
|
|
5464
5482
|
tableWrapper.replaceChildren(
|
|
@@ -5468,7 +5486,9 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
5468
5486
|
sheetIndex,
|
|
5469
5487
|
viewport,
|
|
5470
5488
|
xlsx.utils.encode_cell,
|
|
5471
|
-
xlsx.utils.format_cell
|
|
5489
|
+
xlsx.utils.format_cell,
|
|
5490
|
+
columnSizing,
|
|
5491
|
+
renderTableWindow
|
|
5472
5492
|
)
|
|
5473
5493
|
);
|
|
5474
5494
|
windowControls?.update();
|
|
@@ -5578,9 +5598,10 @@ function renderParsedSheets(panel, sheets, emptyMessage) {
|
|
|
5578
5598
|
const tableWrapper = document.createElement("div");
|
|
5579
5599
|
tableWrapper.className = "ofv-table-scroll";
|
|
5580
5600
|
const viewport = createSheetViewport(rowCount, columnCount);
|
|
5601
|
+
const columnSizing = { widths: /* @__PURE__ */ new Map() };
|
|
5581
5602
|
const windowControls = createSheetWindowControls(viewport, () => renderTableWindow());
|
|
5582
5603
|
const renderTableWindow = () => {
|
|
5583
|
-
tableWrapper.replaceChildren(createParsedSheetTable(sheet, sheetIndex, viewport));
|
|
5604
|
+
tableWrapper.replaceChildren(createParsedSheetTable(sheet, sheetIndex, viewport, columnSizing, renderTableWindow));
|
|
5584
5605
|
windowControls?.update();
|
|
5585
5606
|
};
|
|
5586
5607
|
content.append(heading, summary);
|
|
@@ -5807,6 +5828,45 @@ function chartText(element) {
|
|
|
5807
5828
|
function chartStringValues(element) {
|
|
5808
5829
|
return Array.from(element.querySelectorAll("*")).filter((item) => item.localName === "v" || item.localName === "t").map((item) => item.textContent?.trim() || "").filter(Boolean);
|
|
5809
5830
|
}
|
|
5831
|
+
function trimWorkbookSheetRange(sheet, range, decodeCell) {
|
|
5832
|
+
let minRow = Number.POSITIVE_INFINITY;
|
|
5833
|
+
let minColumn = Number.POSITIVE_INFINITY;
|
|
5834
|
+
let maxRow = Number.NEGATIVE_INFINITY;
|
|
5835
|
+
let maxColumn = Number.NEGATIVE_INFINITY;
|
|
5836
|
+
const include = (row, column) => {
|
|
5837
|
+
minRow = Math.min(minRow, row);
|
|
5838
|
+
minColumn = Math.min(minColumn, column);
|
|
5839
|
+
maxRow = Math.max(maxRow, row);
|
|
5840
|
+
maxColumn = Math.max(maxColumn, column);
|
|
5841
|
+
};
|
|
5842
|
+
for (const [address, cell] of Object.entries(sheet)) {
|
|
5843
|
+
if (address.startsWith("!")) {
|
|
5844
|
+
continue;
|
|
5845
|
+
}
|
|
5846
|
+
if (!cell || cell.v == null && !cell.f && !cell.w && !cell.h) {
|
|
5847
|
+
continue;
|
|
5848
|
+
}
|
|
5849
|
+
const decoded = decodeCell(address);
|
|
5850
|
+
include(decoded.r, decoded.c);
|
|
5851
|
+
}
|
|
5852
|
+
for (const merge of sheet["!merges"] || []) {
|
|
5853
|
+
include(merge.s.r, merge.s.c);
|
|
5854
|
+
include(merge.e.r, merge.e.c);
|
|
5855
|
+
}
|
|
5856
|
+
if (!Number.isFinite(minRow) || !Number.isFinite(minColumn) || !Number.isFinite(maxRow) || !Number.isFinite(maxColumn)) {
|
|
5857
|
+
return range;
|
|
5858
|
+
}
|
|
5859
|
+
return {
|
|
5860
|
+
s: {
|
|
5861
|
+
r: Math.max(range.s.r, minRow),
|
|
5862
|
+
c: Math.max(range.s.c, minColumn)
|
|
5863
|
+
},
|
|
5864
|
+
e: {
|
|
5865
|
+
r: Math.min(range.e.r, maxRow),
|
|
5866
|
+
c: Math.min(range.e.c, maxColumn)
|
|
5867
|
+
}
|
|
5868
|
+
};
|
|
5869
|
+
}
|
|
5810
5870
|
function createSheetViewport(rowCount, columnCount) {
|
|
5811
5871
|
return {
|
|
5812
5872
|
rowStart: 0,
|
|
@@ -5869,42 +5929,245 @@ function createWindowButton(label, onClick) {
|
|
|
5869
5929
|
function maxStart(total, size) {
|
|
5870
5930
|
return Math.max(0, total - size);
|
|
5871
5931
|
}
|
|
5872
|
-
function createWorkbookSheetTable(sheet, range, sheetIndex, viewport, encodeCell, formatCell) {
|
|
5932
|
+
function createWorkbookSheetTable(sheet, range, sheetIndex, viewport, encodeCell, formatCell, columnSizing, rerender) {
|
|
5873
5933
|
const table = document.createElement("table");
|
|
5874
5934
|
table.id = `ofv-sheet-${sheetIndex + 1}`;
|
|
5935
|
+
table.className = "ofv-workbook-table";
|
|
5875
5936
|
const rowEnd = Math.min(range.s.r + viewport.rowStart + SHEET_WINDOW_ROWS - 1, range.e.r);
|
|
5876
5937
|
const columnEnd = Math.min(range.s.c + viewport.columnStart + SHEET_WINDOW_COLUMNS - 1, range.e.c);
|
|
5877
|
-
|
|
5938
|
+
const columnStart = range.s.c + viewport.columnStart;
|
|
5939
|
+
const rowStart = range.s.r + viewport.rowStart;
|
|
5940
|
+
const mergePlan = createSheetMergePlan(sheet["!merges"] || [], rowStart, rowEnd, columnStart, columnEnd);
|
|
5941
|
+
const colGroup = document.createElement("colgroup");
|
|
5942
|
+
let tableWidth = 0;
|
|
5943
|
+
for (let columnIndex = columnStart; columnIndex <= columnEnd; columnIndex += 1) {
|
|
5944
|
+
const col = document.createElement("col");
|
|
5945
|
+
const width = columnSizing.widths.get(columnIndex) ?? getSheetColumnWidth(sheet["!cols"]?.[columnIndex]);
|
|
5946
|
+
col.dataset.columnIndex = String(columnIndex);
|
|
5947
|
+
col.style.width = `${width}px`;
|
|
5948
|
+
tableWidth += width;
|
|
5949
|
+
colGroup.append(col);
|
|
5950
|
+
}
|
|
5951
|
+
table.style.width = `${tableWidth}px`;
|
|
5952
|
+
table.append(colGroup);
|
|
5953
|
+
for (let rowIndex = rowStart; rowIndex <= rowEnd; rowIndex += 1) {
|
|
5878
5954
|
const row = document.createElement("tr");
|
|
5879
|
-
|
|
5880
|
-
|
|
5955
|
+
const rowHeight = getSheetRowHeight(sheet["!rows"]?.[rowIndex]);
|
|
5956
|
+
if (rowHeight) {
|
|
5957
|
+
row.style.height = `${rowHeight}px`;
|
|
5958
|
+
}
|
|
5959
|
+
for (let columnIndex = columnStart; columnIndex <= columnEnd; columnIndex += 1) {
|
|
5881
5960
|
const address = encodeCell({ r: rowIndex, c: columnIndex });
|
|
5882
|
-
const
|
|
5961
|
+
const coordinateKey = `${rowIndex}:${columnIndex}`;
|
|
5962
|
+
if (mergePlan.covered.has(coordinateKey)) {
|
|
5963
|
+
continue;
|
|
5964
|
+
}
|
|
5965
|
+
const merge = mergePlan.anchors.get(coordinateKey);
|
|
5966
|
+
const sourceAddress = merge ? encodeCell({ r: merge.sourceRow, c: merge.sourceColumn }) : address;
|
|
5967
|
+
const sourceCell = sheet[sourceAddress];
|
|
5968
|
+
const cell = document.createElement(rowIndex === range.s.r ? "th" : "td");
|
|
5883
5969
|
cell.dataset.cell = address;
|
|
5970
|
+
if (sourceAddress !== address) {
|
|
5971
|
+
cell.dataset.sourceCell = sourceAddress;
|
|
5972
|
+
}
|
|
5973
|
+
if (merge) {
|
|
5974
|
+
cell.classList.add("ofv-cell-merged");
|
|
5975
|
+
if (merge.rowspan > 1) {
|
|
5976
|
+
cell.rowSpan = merge.rowspan;
|
|
5977
|
+
}
|
|
5978
|
+
if (merge.colspan > 1) {
|
|
5979
|
+
cell.colSpan = merge.colspan;
|
|
5980
|
+
}
|
|
5981
|
+
}
|
|
5884
5982
|
const text = sourceCell ? formatCell(sourceCell) : "";
|
|
5885
5983
|
cell.textContent = text;
|
|
5886
5984
|
if (text) {
|
|
5887
5985
|
cell.title = text;
|
|
5888
5986
|
}
|
|
5987
|
+
applyWorkbookCellStyle(cell, sourceCell);
|
|
5889
5988
|
if (sourceCell?.f) {
|
|
5890
5989
|
cell.classList.add("ofv-cell-formula");
|
|
5891
5990
|
cell.title = `=${sourceCell.f}`;
|
|
5892
5991
|
}
|
|
5992
|
+
if (text.includes("\n")) {
|
|
5993
|
+
cell.classList.add("ofv-cell-multiline");
|
|
5994
|
+
}
|
|
5995
|
+
appendColumnResizeHandle(cell, columnIndex, columnSizing);
|
|
5893
5996
|
row.append(cell);
|
|
5894
5997
|
}
|
|
5895
5998
|
table.append(row);
|
|
5896
5999
|
}
|
|
5897
6000
|
return table;
|
|
5898
6001
|
}
|
|
5899
|
-
function
|
|
6002
|
+
function appendColumnResizeHandle(cell, columnIndex, columnSizing) {
|
|
6003
|
+
const handle = document.createElement("span");
|
|
6004
|
+
handle.className = "ofv-column-resize-handle";
|
|
6005
|
+
handle.setAttribute("aria-hidden", "true");
|
|
6006
|
+
handle.addEventListener("pointerdown", (event) => {
|
|
6007
|
+
event.preventDefault();
|
|
6008
|
+
event.stopPropagation();
|
|
6009
|
+
const startX = event.clientX;
|
|
6010
|
+
const startWidth = columnSizing.widths.get(columnIndex) ?? cell.getBoundingClientRect().width;
|
|
6011
|
+
handle.setPointerCapture(event.pointerId);
|
|
6012
|
+
const onMove = (moveEvent) => {
|
|
6013
|
+
const nextWidth = Math.max(48, Math.min(720, Math.round(startWidth + moveEvent.clientX - startX)));
|
|
6014
|
+
columnSizing.widths.set(columnIndex, nextWidth);
|
|
6015
|
+
updateRenderedColumnWidth(cell, columnIndex, nextWidth);
|
|
6016
|
+
};
|
|
6017
|
+
const onEnd = () => {
|
|
6018
|
+
handle.removeEventListener("pointermove", onMove);
|
|
6019
|
+
handle.removeEventListener("pointerup", onEnd);
|
|
6020
|
+
handle.removeEventListener("pointercancel", onEnd);
|
|
6021
|
+
};
|
|
6022
|
+
handle.addEventListener("pointermove", onMove);
|
|
6023
|
+
handle.addEventListener("pointerup", onEnd);
|
|
6024
|
+
handle.addEventListener("pointercancel", onEnd);
|
|
6025
|
+
});
|
|
6026
|
+
cell.append(handle);
|
|
6027
|
+
}
|
|
6028
|
+
function updateRenderedColumnWidth(cell, columnIndex, width) {
|
|
6029
|
+
const table = cell.closest("table");
|
|
6030
|
+
if (!table) {
|
|
6031
|
+
return;
|
|
6032
|
+
}
|
|
6033
|
+
const column = Array.from(table.querySelectorAll("col")).find(
|
|
6034
|
+
(col) => col.dataset.columnIndex === String(columnIndex)
|
|
6035
|
+
);
|
|
6036
|
+
if (column) {
|
|
6037
|
+
column.style.width = `${width}px`;
|
|
6038
|
+
}
|
|
6039
|
+
const tableWidth = Array.from(table.querySelectorAll("col")).reduce((sum, col) => {
|
|
6040
|
+
const parsed = Number.parseFloat(col.style.width);
|
|
6041
|
+
return sum + (Number.isFinite(parsed) ? parsed : 0);
|
|
6042
|
+
}, 0);
|
|
6043
|
+
if (tableWidth > 0) {
|
|
6044
|
+
table.style.width = `${Math.round(tableWidth)}px`;
|
|
6045
|
+
}
|
|
6046
|
+
}
|
|
6047
|
+
function createSheetMergePlan(merges, rowStart, rowEnd, columnStart, columnEnd) {
|
|
6048
|
+
const anchors = /* @__PURE__ */ new Map();
|
|
6049
|
+
const covered = /* @__PURE__ */ new Set();
|
|
6050
|
+
const encode = (row, column) => `${row}:${column}`;
|
|
6051
|
+
for (const merge of merges) {
|
|
6052
|
+
if (merge.e.r < rowStart || merge.s.r > rowEnd || merge.e.c < columnStart || merge.s.c > columnEnd) {
|
|
6053
|
+
continue;
|
|
6054
|
+
}
|
|
6055
|
+
const visibleStartRow = Math.max(merge.s.r, rowStart);
|
|
6056
|
+
const visibleEndRow = Math.min(merge.e.r, rowEnd);
|
|
6057
|
+
const visibleStartColumn = Math.max(merge.s.c, columnStart);
|
|
6058
|
+
const visibleEndColumn = Math.min(merge.e.c, columnEnd);
|
|
6059
|
+
const anchor = encode(visibleStartRow, visibleStartColumn);
|
|
6060
|
+
anchors.set(anchor, {
|
|
6061
|
+
rowspan: visibleEndRow - visibleStartRow + 1,
|
|
6062
|
+
colspan: visibleEndColumn - visibleStartColumn + 1,
|
|
6063
|
+
sourceRow: merge.s.r,
|
|
6064
|
+
sourceColumn: merge.s.c
|
|
6065
|
+
});
|
|
6066
|
+
for (let rowIndex = visibleStartRow; rowIndex <= visibleEndRow; rowIndex += 1) {
|
|
6067
|
+
for (let columnIndex = visibleStartColumn; columnIndex <= visibleEndColumn; columnIndex += 1) {
|
|
6068
|
+
const address = encode(rowIndex, columnIndex);
|
|
6069
|
+
if (address !== anchor) {
|
|
6070
|
+
covered.add(address);
|
|
6071
|
+
}
|
|
6072
|
+
}
|
|
6073
|
+
}
|
|
6074
|
+
}
|
|
6075
|
+
return { anchors, covered };
|
|
6076
|
+
}
|
|
6077
|
+
function getSheetColumnWidth(column) {
|
|
6078
|
+
if (column?.hidden) {
|
|
6079
|
+
return 0;
|
|
6080
|
+
}
|
|
6081
|
+
const width = column?.wpx || (column?.wch ? column.wch * 7 + 5 : void 0) || (column?.width ? column.width * 7 : void 0) || 96;
|
|
6082
|
+
return Math.max(28, Math.min(360, Math.round(width)));
|
|
6083
|
+
}
|
|
6084
|
+
function getSheetRowHeight(row) {
|
|
6085
|
+
if (row?.hidden) {
|
|
6086
|
+
return 0;
|
|
6087
|
+
}
|
|
6088
|
+
const height = row?.hpx || (row?.hpt ? row.hpt * 1.333 : void 0);
|
|
6089
|
+
return height ? Math.max(18, Math.min(260, Math.round(height))) : void 0;
|
|
6090
|
+
}
|
|
6091
|
+
function applyWorkbookCellStyle(cell, sourceCell) {
|
|
6092
|
+
const style = sourceCell?.s;
|
|
6093
|
+
if (!style) {
|
|
6094
|
+
return;
|
|
6095
|
+
}
|
|
6096
|
+
const fill = readWorkbookColor(style.fgColor || style.fill?.fgColor);
|
|
6097
|
+
if (fill && style.patternType !== "none") {
|
|
6098
|
+
cell.style.backgroundColor = fill;
|
|
6099
|
+
}
|
|
6100
|
+
const font = style.font;
|
|
6101
|
+
if (font) {
|
|
6102
|
+
if (font.bold) {
|
|
6103
|
+
cell.style.fontWeight = "700";
|
|
6104
|
+
}
|
|
6105
|
+
if (font.italic) {
|
|
6106
|
+
cell.style.fontStyle = "italic";
|
|
6107
|
+
}
|
|
6108
|
+
if (font.sz) {
|
|
6109
|
+
cell.style.fontSize = `${Math.max(9, Math.min(24, Number(font.sz)))}pt`;
|
|
6110
|
+
}
|
|
6111
|
+
const fontColor = readWorkbookColor(font.color);
|
|
6112
|
+
if (fontColor) {
|
|
6113
|
+
cell.style.color = fontColor;
|
|
6114
|
+
}
|
|
6115
|
+
}
|
|
6116
|
+
const alignment = style.alignment;
|
|
6117
|
+
if (alignment) {
|
|
6118
|
+
const horizontal = normalizeSheetHorizontalAlign(alignment.horizontal);
|
|
6119
|
+
if (horizontal) {
|
|
6120
|
+
cell.style.textAlign = horizontal;
|
|
6121
|
+
}
|
|
6122
|
+
const vertical = normalizeSheetVerticalAlign(alignment.vertical);
|
|
6123
|
+
if (vertical) {
|
|
6124
|
+
cell.style.verticalAlign = vertical;
|
|
6125
|
+
}
|
|
6126
|
+
if (alignment.wrapText) {
|
|
6127
|
+
cell.classList.add("ofv-cell-multiline");
|
|
6128
|
+
}
|
|
6129
|
+
}
|
|
6130
|
+
}
|
|
6131
|
+
function readWorkbookColor(color) {
|
|
6132
|
+
if (!color?.rgb) {
|
|
6133
|
+
return void 0;
|
|
6134
|
+
}
|
|
6135
|
+
const rgb = color.rgb.length === 8 ? color.rgb.slice(2) : color.rgb;
|
|
6136
|
+
return /^[\da-f]{6}$/i.test(rgb) ? `#${rgb}` : void 0;
|
|
6137
|
+
}
|
|
6138
|
+
function normalizeSheetHorizontalAlign(value) {
|
|
6139
|
+
if (value === "center" || value === "right" || value === "left" || value === "justify") {
|
|
6140
|
+
return value;
|
|
6141
|
+
}
|
|
6142
|
+
return void 0;
|
|
6143
|
+
}
|
|
6144
|
+
function normalizeSheetVerticalAlign(value) {
|
|
6145
|
+
if (value === "top" || value === "middle" || value === "bottom") {
|
|
6146
|
+
return value;
|
|
6147
|
+
}
|
|
6148
|
+
return void 0;
|
|
6149
|
+
}
|
|
6150
|
+
function createParsedSheetTable(sheet, sheetIndex, viewport, columnSizing, rerender) {
|
|
5900
6151
|
const table = document.createElement("table");
|
|
5901
6152
|
table.id = `ofv-sheet-${sheetIndex + 1}`;
|
|
5902
6153
|
const formulaMap = new Map(sheet.formulas.map((item) => [item.address, item.formula]));
|
|
5903
6154
|
const rowEnd = Math.min(viewport.rowStart + SHEET_WINDOW_ROWS, sheet.rows.length);
|
|
6155
|
+
const columnEnd = Math.min(viewport.columnStart + SHEET_WINDOW_COLUMNS, viewport.columnCount);
|
|
6156
|
+
const colGroup = document.createElement("colgroup");
|
|
6157
|
+
let tableWidth = 0;
|
|
6158
|
+
for (let columnIndex = viewport.columnStart; columnIndex < columnEnd; columnIndex += 1) {
|
|
6159
|
+
const width = columnSizing.widths.get(columnIndex) ?? 112;
|
|
6160
|
+
const col = document.createElement("col");
|
|
6161
|
+
col.dataset.columnIndex = String(columnIndex);
|
|
6162
|
+
col.style.width = `${width}px`;
|
|
6163
|
+
tableWidth += width;
|
|
6164
|
+
colGroup.append(col);
|
|
6165
|
+
}
|
|
6166
|
+
table.style.width = `${tableWidth}px`;
|
|
6167
|
+
table.append(colGroup);
|
|
5904
6168
|
for (let rowIndex = viewport.rowStart; rowIndex < rowEnd; rowIndex += 1) {
|
|
5905
6169
|
const sourceRow = sheet.rows[rowIndex] || [];
|
|
5906
6170
|
const row = document.createElement("tr");
|
|
5907
|
-
const columnEnd = Math.min(viewport.columnStart + SHEET_WINDOW_COLUMNS, viewport.columnCount);
|
|
5908
6171
|
for (let columnIndex = viewport.columnStart; columnIndex < columnEnd; columnIndex += 1) {
|
|
5909
6172
|
const value = sourceRow[columnIndex] || "";
|
|
5910
6173
|
const cell = document.createElement(rowIndex === 0 ? "th" : "td");
|
|
@@ -5919,6 +6182,10 @@ function createParsedSheetTable(sheet, sheetIndex, viewport) {
|
|
|
5919
6182
|
cell.classList.add("ofv-cell-formula");
|
|
5920
6183
|
cell.title = formula;
|
|
5921
6184
|
}
|
|
6185
|
+
if (value.includes("\n")) {
|
|
6186
|
+
cell.classList.add("ofv-cell-multiline");
|
|
6187
|
+
}
|
|
6188
|
+
appendColumnResizeHandle(cell, columnIndex, columnSizing);
|
|
5922
6189
|
row.append(cell);
|
|
5923
6190
|
}
|
|
5924
6191
|
table.append(row);
|
|
@@ -9979,6 +10246,521 @@ function decodeDrawioDiagram(value) {
|
|
|
9979
10246
|
|
|
9980
10247
|
// src/plugins/cad.ts
|
|
9981
10248
|
var import_pako3 = __toESM(require("pako"), 1);
|
|
10249
|
+
|
|
10250
|
+
// src/plugins/cad-dwg.ts
|
|
10251
|
+
var libreDwgPromise;
|
|
10252
|
+
var defaultLibreDwgWasmBaseUrl = "/vendor/libredwg-web";
|
|
10253
|
+
var minReadableDrawingHeight = 420;
|
|
10254
|
+
var svgNumberPattern = /-?\d*\.?\d+(?:e[-+]?\d+)?/gi;
|
|
10255
|
+
async function renderLibreDwgPreview(ctx, options = {}) {
|
|
10256
|
+
if (ctx.extension !== "dwg" || options.enabled === false) {
|
|
10257
|
+
return void 0;
|
|
10258
|
+
}
|
|
10259
|
+
const shell = document.createElement("div");
|
|
10260
|
+
shell.className = "ofv-dwg-preview";
|
|
10261
|
+
const status = document.createElement("div");
|
|
10262
|
+
status.className = "ofv-dwg-preview-status";
|
|
10263
|
+
status.textContent = "Loading DWG rendering engine...";
|
|
10264
|
+
shell.append(status);
|
|
10265
|
+
ctx.panel.append(shell);
|
|
10266
|
+
try {
|
|
10267
|
+
const { LibreDwg, Dwg_File_Type } = await loadLibreDwg();
|
|
10268
|
+
const libredwg = await LibreDwg.create(options.wasmBaseUrl || defaultLibreDwgWasmBaseUrl);
|
|
10269
|
+
const data = libredwg.dwg_read_data(ctx.arrayBuffer, Dwg_File_Type.DWG);
|
|
10270
|
+
if (!data) {
|
|
10271
|
+
throw new Error("DWG parser did not return drawing data.");
|
|
10272
|
+
}
|
|
10273
|
+
let svg = "";
|
|
10274
|
+
let stats;
|
|
10275
|
+
let thumbnailUrl;
|
|
10276
|
+
try {
|
|
10277
|
+
thumbnailUrl = createDwgThumbnailUrl(readDwgThumbnail(libredwg, data));
|
|
10278
|
+
try {
|
|
10279
|
+
const result = libredwg.convertEx(data);
|
|
10280
|
+
const database = result.database;
|
|
10281
|
+
stats = createDwgPreviewStats(database, result.stats.unknownEntityCount, Boolean(thumbnailUrl));
|
|
10282
|
+
svg = libredwg.dwg_to_svg(database);
|
|
10283
|
+
} catch (error) {
|
|
10284
|
+
if (thumbnailUrl) {
|
|
10285
|
+
const fallbackThumbnailUrl = thumbnailUrl;
|
|
10286
|
+
status.replaceChildren(createDwgThumbnailFallbackStatus(ctx.fileName, error));
|
|
10287
|
+
shell.append(createDwgThumbnailPreview(fallbackThumbnailUrl, ctx.fileName));
|
|
10288
|
+
return {
|
|
10289
|
+
destroy() {
|
|
10290
|
+
URL.revokeObjectURL(fallbackThumbnailUrl);
|
|
10291
|
+
shell.remove();
|
|
10292
|
+
}
|
|
10293
|
+
};
|
|
10294
|
+
}
|
|
10295
|
+
throw error;
|
|
10296
|
+
}
|
|
10297
|
+
} finally {
|
|
10298
|
+
libredwg.dwg_free(data);
|
|
10299
|
+
}
|
|
10300
|
+
if (!svg || !/<svg[\s>]/i.test(svg)) {
|
|
10301
|
+
if (thumbnailUrl) {
|
|
10302
|
+
const fallbackThumbnailUrl = thumbnailUrl;
|
|
10303
|
+
status.replaceChildren(createDwgThumbnailFallbackStatus(ctx.fileName, "DWG parser finished but did not produce SVG output."));
|
|
10304
|
+
shell.append(createDwgThumbnailPreview(fallbackThumbnailUrl, ctx.fileName));
|
|
10305
|
+
return {
|
|
10306
|
+
destroy() {
|
|
10307
|
+
URL.revokeObjectURL(fallbackThumbnailUrl);
|
|
10308
|
+
shell.remove();
|
|
10309
|
+
}
|
|
10310
|
+
};
|
|
10311
|
+
}
|
|
10312
|
+
throw new Error("DWG parser finished but did not produce SVG output.");
|
|
10313
|
+
}
|
|
10314
|
+
const doc = new DOMParser().parseFromString(svg, "image/svg+xml");
|
|
10315
|
+
const svgElement = doc.documentElement;
|
|
10316
|
+
if (!(svgElement instanceof SVGElement) || svgElement.nodeName.toLowerCase() !== "svg" || svgElement.querySelector("parsererror")) {
|
|
10317
|
+
throw new Error("DWG SVG output is invalid.");
|
|
10318
|
+
}
|
|
10319
|
+
svgElement.classList.add("ofv-dwg-preview-svg");
|
|
10320
|
+
svgElement.setAttribute("role", "img");
|
|
10321
|
+
svgElement.setAttribute("aria-label", ctx.fileName);
|
|
10322
|
+
normalizeDwgSvg(svgElement);
|
|
10323
|
+
const reliability = assessDwgSvgReliability(svgElement);
|
|
10324
|
+
status.replaceChildren(createDwgStatusTitle(ctx.fileName, stats, reliability));
|
|
10325
|
+
if (!reliability.isReliable && thumbnailUrl) {
|
|
10326
|
+
shell.append(createDwgThumbnailPreview(thumbnailUrl, ctx.fileName));
|
|
10327
|
+
return {
|
|
10328
|
+
destroy() {
|
|
10329
|
+
URL.revokeObjectURL(thumbnailUrl);
|
|
10330
|
+
shell.remove();
|
|
10331
|
+
}
|
|
10332
|
+
};
|
|
10333
|
+
}
|
|
10334
|
+
const drawing = createDwgDrawingViewport(svgElement);
|
|
10335
|
+
if (thumbnailUrl) {
|
|
10336
|
+
shell.append(createDwgThumbnailPanel(thumbnailUrl));
|
|
10337
|
+
}
|
|
10338
|
+
shell.append(drawing.frame);
|
|
10339
|
+
return {
|
|
10340
|
+
resize() {
|
|
10341
|
+
drawing.update();
|
|
10342
|
+
},
|
|
10343
|
+
canCommand(command) {
|
|
10344
|
+
return command === "zoom-in" || command === "zoom-out" || command === "zoom-reset";
|
|
10345
|
+
},
|
|
10346
|
+
command(command) {
|
|
10347
|
+
if (command === "zoom-in") {
|
|
10348
|
+
drawing.setZoom(drawing.zoom * 1.18);
|
|
10349
|
+
return true;
|
|
10350
|
+
}
|
|
10351
|
+
if (command === "zoom-out") {
|
|
10352
|
+
drawing.setZoom(drawing.zoom / 1.18);
|
|
10353
|
+
return true;
|
|
10354
|
+
}
|
|
10355
|
+
if (command === "zoom-reset") {
|
|
10356
|
+
drawing.setZoom(1);
|
|
10357
|
+
return true;
|
|
10358
|
+
}
|
|
10359
|
+
return false;
|
|
10360
|
+
},
|
|
10361
|
+
destroy() {
|
|
10362
|
+
if (thumbnailUrl) {
|
|
10363
|
+
URL.revokeObjectURL(thumbnailUrl);
|
|
10364
|
+
}
|
|
10365
|
+
shell.remove();
|
|
10366
|
+
}
|
|
10367
|
+
};
|
|
10368
|
+
} catch (error) {
|
|
10369
|
+
shell.remove();
|
|
10370
|
+
console.warn("DWG LibreDWG preview failed, falling back to metadata preview:", error);
|
|
10371
|
+
return void 0;
|
|
10372
|
+
}
|
|
10373
|
+
}
|
|
10374
|
+
function loadLibreDwg() {
|
|
10375
|
+
libreDwgPromise ||= import("@mlightcad/libredwg-web");
|
|
10376
|
+
return libreDwgPromise;
|
|
10377
|
+
}
|
|
10378
|
+
function createDwgStatusTitle(fileName, stats, reliability) {
|
|
10379
|
+
const wrapper = document.createElement("span");
|
|
10380
|
+
const title = document.createElement("strong");
|
|
10381
|
+
const note = document.createElement("small");
|
|
10382
|
+
title.textContent = reliability.isReliable ? `\u5B9E\u9A8C\u6027 DWG \u6A21\u578B\u7A7A\u95F4\u9884\u89C8 \xB7 ${fileName}` : `DWG \u5185\u7F6E\u9884\u89C8\u56FE \xB7 ${fileName}`;
|
|
10383
|
+
note.textContent = [
|
|
10384
|
+
`${stats.entityCount.toLocaleString()} \u4E2A\u5B9E\u4F53`,
|
|
10385
|
+
`${stats.visibleLayerCount}/${stats.layerCount} \u4E2A\u53EF\u89C1\u56FE\u5C42`,
|
|
10386
|
+
`${stats.layoutCount} \u4E2A\u5E03\u5C40`,
|
|
10387
|
+
stats.paperSpaceEntityCount ? `${stats.paperSpaceEntityCount.toLocaleString()} \u4E2A\u56FE\u7EB8\u7A7A\u95F4\u5B9E\u4F53` : "\u6A21\u578B\u7A7A\u95F4\u7EBF\u7A3F",
|
|
10388
|
+
stats.unknownEntityCount ? `${stats.unknownEntityCount} \u4E2A\u672A\u77E5\u5B9E\u4F53` : "\u5B9E\u4F53\u89E3\u6790\u5B8C\u6574",
|
|
10389
|
+
stats.hasThumbnail ? "\u5305\u542B\u5185\u7F6E\u7F29\u7565\u56FE" : "\u65E0\u5185\u7F6E\u7F29\u7565\u56FE"
|
|
10390
|
+
].join(" \xB7 ");
|
|
10391
|
+
const warning = document.createElement("small");
|
|
10392
|
+
warning.textContent = reliability.isReliable ? "\u5F53\u524D\u4E3A LibreDWG WASM \u7EBF\u7A3F\u9884\u89C8\uFF0C\u590D\u6742\u5E03\u5C40/\u6253\u5370\u7A7A\u95F4/\u5B57\u4F53/\u586B\u5145\u4E0E\u4E13\u4E1A CAD \u4ECD\u53EF\u80FD\u5B58\u5728\u5DEE\u5F02\u3002" : `LibreDWG \u7EBF\u7A3F\u68C0\u6D4B\u5230\u5F02\u5E38\u56FE\u5143\uFF0C\u5DF2\u4F18\u5148\u663E\u793A\u6587\u4EF6\u5185\u7F6E\u9884\u89C8\u56FE\u3002${reliability.reason ?? ""}`;
|
|
10393
|
+
wrapper.append(title, note, warning);
|
|
10394
|
+
return wrapper;
|
|
10395
|
+
}
|
|
10396
|
+
function createDwgThumbnailFallbackStatus(fileName, error) {
|
|
10397
|
+
const wrapper = document.createElement("span");
|
|
10398
|
+
const title = document.createElement("strong");
|
|
10399
|
+
title.textContent = `DWG \u5185\u7F6E\u9884\u89C8\u56FE \xB7 ${fileName}`;
|
|
10400
|
+
const note = document.createElement("small");
|
|
10401
|
+
note.textContent = "LibreDWG \u5DF2\u8BFB\u53D6\u5230\u6587\u4EF6\u5185\u7F6E\u7F29\u7565\u56FE\uFF0C\u4F46\u7EBF\u7A3F\u9884\u89C8\u672A\u80FD\u751F\u6210\uFF0C\u5DF2\u81EA\u52A8\u5207\u6362\u4E3A\u7F29\u7565\u56FE\u515C\u5E95\u3002";
|
|
10402
|
+
const detail = document.createElement("small");
|
|
10403
|
+
detail.textContent = error instanceof Error ? error.message : String(error || "\u672A\u77E5\u89E3\u6790\u9519\u8BEF");
|
|
10404
|
+
wrapper.append(title, note, detail);
|
|
10405
|
+
return wrapper;
|
|
10406
|
+
}
|
|
10407
|
+
function createDwgPreviewStats(database, unknownEntityCount, hasThumbnail) {
|
|
10408
|
+
const layers = database.tables.LAYER.entries;
|
|
10409
|
+
return {
|
|
10410
|
+
entityCount: database.entities.length,
|
|
10411
|
+
layerCount: layers.length,
|
|
10412
|
+
layoutCount: database.objects.LAYOUT.length,
|
|
10413
|
+
unknownEntityCount,
|
|
10414
|
+
visibleLayerCount: layers.filter((layer) => !layer.off && !layer.frozen).length,
|
|
10415
|
+
paperSpaceEntityCount: database.entities.filter((entity) => entity.isInPaperSpace).length,
|
|
10416
|
+
hasThumbnail
|
|
10417
|
+
};
|
|
10418
|
+
}
|
|
10419
|
+
function readDwgThumbnail(libredwg, data) {
|
|
10420
|
+
try {
|
|
10421
|
+
const thumbnail = libredwg.dwg_bmp(data);
|
|
10422
|
+
if (!thumbnail?.data?.length) {
|
|
10423
|
+
return void 0;
|
|
10424
|
+
}
|
|
10425
|
+
return thumbnail;
|
|
10426
|
+
} catch {
|
|
10427
|
+
return void 0;
|
|
10428
|
+
}
|
|
10429
|
+
}
|
|
10430
|
+
function createDwgThumbnailUrl(thumbnail) {
|
|
10431
|
+
if (!thumbnail) {
|
|
10432
|
+
return void 0;
|
|
10433
|
+
}
|
|
10434
|
+
if (thumbnail.type === 6) {
|
|
10435
|
+
return URL.createObjectURL(new Blob([toArrayBuffer(thumbnail.data)], { type: "image/png" }));
|
|
10436
|
+
}
|
|
10437
|
+
if (thumbnail.type === 2) {
|
|
10438
|
+
return URL.createObjectURL(new Blob([toArrayBuffer(createBmpFileBytes(thumbnail.data))], { type: "image/bmp" }));
|
|
10439
|
+
}
|
|
10440
|
+
return void 0;
|
|
10441
|
+
}
|
|
10442
|
+
function toArrayBuffer(bytes) {
|
|
10443
|
+
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
10444
|
+
}
|
|
10445
|
+
function createBmpFileBytes(dibBytes) {
|
|
10446
|
+
const view3 = new DataView(dibBytes.buffer, dibBytes.byteOffset, dibBytes.byteLength);
|
|
10447
|
+
const headerSize = view3.getUint32(0, true);
|
|
10448
|
+
const bitCount = view3.getUint16(14, true);
|
|
10449
|
+
const paletteBytes = bitCount <= 8 ? 2 ** bitCount * 4 : 0;
|
|
10450
|
+
const pixelOffset = 14 + headerSize + paletteBytes;
|
|
10451
|
+
const bytes = new Uint8Array(14 + dibBytes.byteLength);
|
|
10452
|
+
bytes[0] = 66;
|
|
10453
|
+
bytes[1] = 77;
|
|
10454
|
+
const fileView = new DataView(bytes.buffer);
|
|
10455
|
+
fileView.setUint32(2, bytes.byteLength, true);
|
|
10456
|
+
fileView.setUint32(10, pixelOffset, true);
|
|
10457
|
+
bytes.set(dibBytes, 14);
|
|
10458
|
+
return bytes;
|
|
10459
|
+
}
|
|
10460
|
+
function createDwgThumbnailPanel(thumbnailUrl) {
|
|
10461
|
+
const panel = document.createElement("figure");
|
|
10462
|
+
panel.className = "ofv-dwg-thumbnail";
|
|
10463
|
+
const image = document.createElement("img");
|
|
10464
|
+
image.src = thumbnailUrl;
|
|
10465
|
+
image.alt = "DWG \u6587\u4EF6\u5185\u7F6E\u7F29\u7565\u56FE";
|
|
10466
|
+
const caption = document.createElement("figcaption");
|
|
10467
|
+
caption.textContent = "\u6587\u4EF6\u5185\u7F6E\u7F29\u7565\u56FE";
|
|
10468
|
+
panel.append(image, caption);
|
|
10469
|
+
return panel;
|
|
10470
|
+
}
|
|
10471
|
+
function createDwgThumbnailPreview(thumbnailUrl, fileName) {
|
|
10472
|
+
const figure = document.createElement("figure");
|
|
10473
|
+
figure.className = "ofv-dwg-thumbnail-preview";
|
|
10474
|
+
const image = document.createElement("img");
|
|
10475
|
+
image.src = thumbnailUrl;
|
|
10476
|
+
image.alt = `${fileName} \u5185\u7F6E\u9884\u89C8\u56FE`;
|
|
10477
|
+
const caption = document.createElement("figcaption");
|
|
10478
|
+
caption.textContent = "DWG \u6587\u4EF6\u5185\u7F6E\u9884\u89C8\u56FE\u3002\u82E5\u9700\u8981\u63A5\u8FD1 CAD \u5E03\u5C40/\u6253\u5370\u7A7A\u95F4\u7684\u9AD8\u4FDD\u771F\u6548\u679C\uFF0C\u8BF7\u4F7F\u7528\u540C\u540D PNG\u3001SVG\u3001PDF \u5BFC\u51FA\u56FE\uFF0C\u6216\u901A\u8FC7 binaryRenderer \u63A5\u5165\u4E13\u4E1A CAD \u6E32\u67D3/\u8F6C\u6362\u670D\u52A1\u3002";
|
|
10479
|
+
figure.append(image, caption);
|
|
10480
|
+
return figure;
|
|
10481
|
+
}
|
|
10482
|
+
function normalizeDwgSvg(svgElement) {
|
|
10483
|
+
svgElement.setAttribute("preserveAspectRatio", "xMidYMid meet");
|
|
10484
|
+
removeInheritedDwgFills(svgElement);
|
|
10485
|
+
focusDwgSvgOnMainDrawing(svgElement);
|
|
10486
|
+
const style = document.createElementNS("http://www.w3.org/2000/svg", "style");
|
|
10487
|
+
style.textContent = `
|
|
10488
|
+
.ofv-dwg-preview-svg { background: #020617; }
|
|
10489
|
+
.ofv-dwg-preview-svg g {
|
|
10490
|
+
fill: none !important;
|
|
10491
|
+
}
|
|
10492
|
+
.ofv-dwg-preview-svg line,
|
|
10493
|
+
.ofv-dwg-preview-svg path,
|
|
10494
|
+
.ofv-dwg-preview-svg polyline,
|
|
10495
|
+
.ofv-dwg-preview-svg polygon,
|
|
10496
|
+
.ofv-dwg-preview-svg circle,
|
|
10497
|
+
.ofv-dwg-preview-svg ellipse,
|
|
10498
|
+
.ofv-dwg-preview-svg rect {
|
|
10499
|
+
fill: none !important;
|
|
10500
|
+
vector-effect: non-scaling-stroke;
|
|
10501
|
+
stroke-width: 0.7px !important;
|
|
10502
|
+
stroke-linecap: round;
|
|
10503
|
+
stroke-linejoin: round;
|
|
10504
|
+
}
|
|
10505
|
+
.ofv-dwg-preview-svg [stroke="rgb(0,255,0)"] {
|
|
10506
|
+
stroke: #34d399 !important;
|
|
10507
|
+
stroke-opacity: 0.58 !important;
|
|
10508
|
+
}
|
|
10509
|
+
.ofv-dwg-preview-svg text {
|
|
10510
|
+
vector-effect: non-scaling-stroke;
|
|
10511
|
+
stroke-width: 0 !important;
|
|
10512
|
+
fill: currentColor !important;
|
|
10513
|
+
}
|
|
10514
|
+
`;
|
|
10515
|
+
svgElement.prepend(style);
|
|
10516
|
+
}
|
|
10517
|
+
function removeInheritedDwgFills(svgElement) {
|
|
10518
|
+
const shapeSelector = "line,path,polyline,polygon,circle,ellipse,rect";
|
|
10519
|
+
for (const group of svgElement.querySelectorAll("g[fill]")) {
|
|
10520
|
+
group.setAttribute("fill", "none");
|
|
10521
|
+
}
|
|
10522
|
+
for (const styledElement of svgElement.querySelectorAll("[style]")) {
|
|
10523
|
+
styledElement.style.fill = "none";
|
|
10524
|
+
}
|
|
10525
|
+
for (const shape of svgElement.querySelectorAll(shapeSelector)) {
|
|
10526
|
+
shape.setAttribute("fill", "none");
|
|
10527
|
+
}
|
|
10528
|
+
}
|
|
10529
|
+
function assessDwgSvgReliability(svgElement) {
|
|
10530
|
+
const bounds = readSvgViewBox(svgElement);
|
|
10531
|
+
if (!bounds) {
|
|
10532
|
+
return { isReliable: true };
|
|
10533
|
+
}
|
|
10534
|
+
const largePathCount = countLargeSvgPaths(svgElement, bounds);
|
|
10535
|
+
const totalPathCount = svgElement.querySelectorAll("path").length;
|
|
10536
|
+
if (largePathCount >= 24 && totalPathCount > 0 && largePathCount / totalPathCount > 0.08) {
|
|
10537
|
+
return {
|
|
10538
|
+
isReliable: false,
|
|
10539
|
+
reason: "\u8BE5\u6587\u4EF6\u5305\u542B\u5927\u91CF\u8D85\u51FA\u4E3B\u4F53\u89C6\u53E3\u7684\u5927\u8DEF\u5F84/\u5757\u53C2\u7167\uFF0C\u7EBF\u7A3F\u6A21\u5F0F\u4F1A\u660E\u663E\u504F\u79BB CAD \u5E03\u5C40\u3002"
|
|
10540
|
+
};
|
|
10541
|
+
}
|
|
10542
|
+
return { isReliable: true };
|
|
10543
|
+
}
|
|
10544
|
+
function countLargeSvgPaths(svgElement, bounds) {
|
|
10545
|
+
let count = 0;
|
|
10546
|
+
const viewportArea = bounds.width * bounds.height;
|
|
10547
|
+
if (!Number.isFinite(viewportArea) || viewportArea <= 0) {
|
|
10548
|
+
return count;
|
|
10549
|
+
}
|
|
10550
|
+
for (const path of svgElement.querySelectorAll("path")) {
|
|
10551
|
+
if (path.closest("defs")) {
|
|
10552
|
+
continue;
|
|
10553
|
+
}
|
|
10554
|
+
const pathBounds = estimatePathBounds(path);
|
|
10555
|
+
if (!pathBounds) {
|
|
10556
|
+
continue;
|
|
10557
|
+
}
|
|
10558
|
+
const pathArea = pathBounds.width * pathBounds.height;
|
|
10559
|
+
const crossesViewport = pathBounds.minX <= bounds.minX + bounds.width * 0.02 || pathBounds.minY <= bounds.minY + bounds.height * 0.02;
|
|
10560
|
+
if (pathArea > viewportArea * 0.28 || crossesViewport && pathArea > viewportArea * 0.12) {
|
|
10561
|
+
count += 1;
|
|
10562
|
+
}
|
|
10563
|
+
}
|
|
10564
|
+
return count;
|
|
10565
|
+
}
|
|
10566
|
+
function estimatePathBounds(path) {
|
|
10567
|
+
const numbers = readNumbers(path.getAttribute("d") ?? "");
|
|
10568
|
+
if (numbers.length < 4) {
|
|
10569
|
+
return void 0;
|
|
10570
|
+
}
|
|
10571
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
10572
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
10573
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
10574
|
+
let maxY = Number.NEGATIVE_INFINITY;
|
|
10575
|
+
for (let index = 0; index + 1 < numbers.length; index += 2) {
|
|
10576
|
+
const x = numbers[index];
|
|
10577
|
+
const y = numbers[index + 1];
|
|
10578
|
+
if (!Number.isFinite(x) || !Number.isFinite(y) || Math.abs(x) > 1e9 || Math.abs(y) > 1e9) {
|
|
10579
|
+
continue;
|
|
10580
|
+
}
|
|
10581
|
+
minX = Math.min(minX, x);
|
|
10582
|
+
minY = Math.min(minY, y);
|
|
10583
|
+
maxX = Math.max(maxX, x);
|
|
10584
|
+
maxY = Math.max(maxY, y);
|
|
10585
|
+
}
|
|
10586
|
+
if (!Number.isFinite(minX) || !Number.isFinite(minY) || !Number.isFinite(maxX) || !Number.isFinite(maxY)) {
|
|
10587
|
+
return void 0;
|
|
10588
|
+
}
|
|
10589
|
+
return { minX, minY, width: maxX - minX, height: maxY - minY };
|
|
10590
|
+
}
|
|
10591
|
+
function focusDwgSvgOnMainDrawing(svgElement) {
|
|
10592
|
+
const originalBounds = readSvgViewBox(svgElement);
|
|
10593
|
+
const mainBounds = estimateMainDrawingBounds(svgElement);
|
|
10594
|
+
if (!originalBounds || !mainBounds || !shouldUseMainDrawingBounds(originalBounds, mainBounds)) {
|
|
10595
|
+
return;
|
|
10596
|
+
}
|
|
10597
|
+
const paddedBounds = padBounds(mainBounds, 0.05);
|
|
10598
|
+
svgElement.dataset.originalViewBox = formatViewBox(originalBounds);
|
|
10599
|
+
svgElement.dataset.focusViewBox = formatViewBox(paddedBounds);
|
|
10600
|
+
svgElement.setAttribute("viewBox", formatViewBox(paddedBounds));
|
|
10601
|
+
}
|
|
10602
|
+
function shouldUseMainDrawingBounds(original, candidate) {
|
|
10603
|
+
const originalAspectRatio = original.width / original.height;
|
|
10604
|
+
const candidateAspectRatio = candidate.width / candidate.height;
|
|
10605
|
+
const originalArea = original.width * original.height;
|
|
10606
|
+
const candidateArea = candidate.width * candidate.height;
|
|
10607
|
+
if (!Number.isFinite(originalArea) || !Number.isFinite(candidateArea) || candidateArea <= 0) {
|
|
10608
|
+
return false;
|
|
10609
|
+
}
|
|
10610
|
+
return originalAspectRatio > 8 || originalAspectRatio < 0.125 || candidateArea / originalArea < 0.55 || Math.abs(Math.log(originalAspectRatio / candidateAspectRatio)) > Math.log(4);
|
|
10611
|
+
}
|
|
10612
|
+
function estimateMainDrawingBounds(svgElement) {
|
|
10613
|
+
const points = [];
|
|
10614
|
+
const addPoint = (x, y) => {
|
|
10615
|
+
if (Number.isFinite(x) && Number.isFinite(y) && Math.abs(x) < 1e9 && Math.abs(y) < 1e9) {
|
|
10616
|
+
points.push([x, y]);
|
|
10617
|
+
}
|
|
10618
|
+
};
|
|
10619
|
+
for (const element of svgElement.querySelectorAll("line,path,polyline,polygon,circle,ellipse,rect,text,use")) {
|
|
10620
|
+
if (element.closest("defs")) {
|
|
10621
|
+
continue;
|
|
10622
|
+
}
|
|
10623
|
+
collectElementPoints(element, addPoint);
|
|
10624
|
+
}
|
|
10625
|
+
if (points.length < 16) {
|
|
10626
|
+
return void 0;
|
|
10627
|
+
}
|
|
10628
|
+
const xs = points.map(([x]) => x).sort((a, b) => a - b);
|
|
10629
|
+
const ys = points.map(([, y]) => y).sort((a, b) => a - b);
|
|
10630
|
+
const minX = quantile(xs, 5e-3);
|
|
10631
|
+
const maxX = quantile(xs, 0.995);
|
|
10632
|
+
const minY = quantile(ys, 5e-3);
|
|
10633
|
+
const maxY = quantile(ys, 0.995);
|
|
10634
|
+
if (!Number.isFinite(minX) || !Number.isFinite(maxX) || !Number.isFinite(minY) || !Number.isFinite(maxY)) {
|
|
10635
|
+
return void 0;
|
|
10636
|
+
}
|
|
10637
|
+
const width = maxX - minX;
|
|
10638
|
+
const height = maxY - minY;
|
|
10639
|
+
if (width <= 0 || height <= 0) {
|
|
10640
|
+
return void 0;
|
|
10641
|
+
}
|
|
10642
|
+
return { minX, minY, width, height };
|
|
10643
|
+
}
|
|
10644
|
+
function collectElementPoints(element, addPoint) {
|
|
10645
|
+
const tagName = element.tagName.toLowerCase();
|
|
10646
|
+
if (tagName === "line") {
|
|
10647
|
+
addPoint(readNumberAttribute(element, "x1"), readNumberAttribute(element, "y1"));
|
|
10648
|
+
addPoint(readNumberAttribute(element, "x2"), readNumberAttribute(element, "y2"));
|
|
10649
|
+
} else if (tagName === "circle") {
|
|
10650
|
+
const cx = readNumberAttribute(element, "cx");
|
|
10651
|
+
const cy = readNumberAttribute(element, "cy");
|
|
10652
|
+
const r = readNumberAttribute(element, "r");
|
|
10653
|
+
addPoint(cx - r, cy - r);
|
|
10654
|
+
addPoint(cx + r, cy + r);
|
|
10655
|
+
} else if (tagName === "ellipse") {
|
|
10656
|
+
const cx = readNumberAttribute(element, "cx");
|
|
10657
|
+
const cy = readNumberAttribute(element, "cy");
|
|
10658
|
+
const rx = readNumberAttribute(element, "rx");
|
|
10659
|
+
const ry = readNumberAttribute(element, "ry");
|
|
10660
|
+
addPoint(cx - rx, cy - ry);
|
|
10661
|
+
addPoint(cx + rx, cy + ry);
|
|
10662
|
+
} else if (tagName === "rect") {
|
|
10663
|
+
const x = readNumberAttribute(element, "x");
|
|
10664
|
+
const y = readNumberAttribute(element, "y");
|
|
10665
|
+
addPoint(x, y);
|
|
10666
|
+
addPoint(x + readNumberAttribute(element, "width"), y + readNumberAttribute(element, "height"));
|
|
10667
|
+
} else if (tagName === "text") {
|
|
10668
|
+
addPoint(readNumberAttribute(element, "x"), readNumberAttribute(element, "y"));
|
|
10669
|
+
} else if (tagName === "path") {
|
|
10670
|
+
collectNumberPairs(element.getAttribute("d"), addPoint);
|
|
10671
|
+
} else if (tagName === "polyline" || tagName === "polygon") {
|
|
10672
|
+
collectNumberPairs(element.getAttribute("points"), addPoint);
|
|
10673
|
+
}
|
|
10674
|
+
collectTranslatePoints(element.getAttribute("transform"), addPoint);
|
|
10675
|
+
}
|
|
10676
|
+
function collectNumberPairs(value, addPoint) {
|
|
10677
|
+
if (!value) {
|
|
10678
|
+
return;
|
|
10679
|
+
}
|
|
10680
|
+
const numbers = readNumbers(value);
|
|
10681
|
+
for (let index = 0; index + 1 < numbers.length; index += 2) {
|
|
10682
|
+
addPoint(numbers[index], numbers[index + 1]);
|
|
10683
|
+
}
|
|
10684
|
+
}
|
|
10685
|
+
function collectTranslatePoints(value, addPoint) {
|
|
10686
|
+
if (!value) {
|
|
10687
|
+
return;
|
|
10688
|
+
}
|
|
10689
|
+
const translatePattern = /translate\(\s*(-?\d*\.?\d+(?:e[-+]?\d+)?)(?:[\s,]+(-?\d*\.?\d+(?:e[-+]?\d+)?))?/gi;
|
|
10690
|
+
for (const match of value.matchAll(translatePattern)) {
|
|
10691
|
+
addPoint(Number(match[1]), Number(match[2] ?? 0));
|
|
10692
|
+
}
|
|
10693
|
+
}
|
|
10694
|
+
function readNumbers(value) {
|
|
10695
|
+
const matches = value.match(svgNumberPattern);
|
|
10696
|
+
return matches ? matches.map((item) => Number(item)).filter(Number.isFinite) : [];
|
|
10697
|
+
}
|
|
10698
|
+
function readNumberAttribute(element, name) {
|
|
10699
|
+
const value = Number(element.getAttribute(name) ?? 0);
|
|
10700
|
+
return Number.isFinite(value) ? value : 0;
|
|
10701
|
+
}
|
|
10702
|
+
function quantile(values, ratio) {
|
|
10703
|
+
const index = Math.max(0, Math.min(values.length - 1, Math.floor((values.length - 1) * ratio)));
|
|
10704
|
+
return values[index];
|
|
10705
|
+
}
|
|
10706
|
+
function padBounds(bounds, ratio) {
|
|
10707
|
+
const paddingX = bounds.width * ratio;
|
|
10708
|
+
const paddingY = bounds.height * ratio;
|
|
10709
|
+
return {
|
|
10710
|
+
minX: bounds.minX - paddingX,
|
|
10711
|
+
minY: bounds.minY - paddingY,
|
|
10712
|
+
width: bounds.width + paddingX * 2,
|
|
10713
|
+
height: bounds.height + paddingY * 2
|
|
10714
|
+
};
|
|
10715
|
+
}
|
|
10716
|
+
function formatViewBox(bounds) {
|
|
10717
|
+
return [bounds.minX, bounds.minY, bounds.width, bounds.height].map((value) => Number(value.toFixed(4))).join(" ");
|
|
10718
|
+
}
|
|
10719
|
+
function createDwgDrawingViewport(svgElement) {
|
|
10720
|
+
const frame = document.createElement("div");
|
|
10721
|
+
frame.className = "ofv-dwg-preview-frame";
|
|
10722
|
+
const importedSvg = document.importNode(svgElement, true);
|
|
10723
|
+
frame.append(importedSvg);
|
|
10724
|
+
const aspectRatio = readSvgAspectRatio(svgElement);
|
|
10725
|
+
let zoom = 1;
|
|
10726
|
+
const update = () => {
|
|
10727
|
+
const frameWidth = Math.max(frame.clientWidth, 1);
|
|
10728
|
+
const readableWidth = aspectRatio ? minReadableDrawingHeight * aspectRatio : frameWidth;
|
|
10729
|
+
const width = Math.max(frameWidth, readableWidth) * zoom;
|
|
10730
|
+
importedSvg.style.width = `${Math.round(width)}px`;
|
|
10731
|
+
importedSvg.style.minWidth = `${Math.round(width)}px`;
|
|
10732
|
+
};
|
|
10733
|
+
const setZoom = (nextZoom) => {
|
|
10734
|
+
zoom = Math.min(Math.max(nextZoom, 0.2), 8);
|
|
10735
|
+
update();
|
|
10736
|
+
};
|
|
10737
|
+
requestAnimationFrame(update);
|
|
10738
|
+
return {
|
|
10739
|
+
frame,
|
|
10740
|
+
get zoom() {
|
|
10741
|
+
return zoom;
|
|
10742
|
+
},
|
|
10743
|
+
setZoom,
|
|
10744
|
+
update
|
|
10745
|
+
};
|
|
10746
|
+
}
|
|
10747
|
+
function readSvgAspectRatio(svgElement) {
|
|
10748
|
+
const viewBox = readSvgViewBox(svgElement);
|
|
10749
|
+
return viewBox ? viewBox.width / viewBox.height : void 0;
|
|
10750
|
+
}
|
|
10751
|
+
function readSvgViewBox(svgElement) {
|
|
10752
|
+
const viewBox = svgElement.getAttribute("viewBox");
|
|
10753
|
+
if (!viewBox) {
|
|
10754
|
+
return void 0;
|
|
10755
|
+
}
|
|
10756
|
+
const [minX, minY, width, height] = viewBox.trim().split(/[\s,]+/).map((value) => Number(value));
|
|
10757
|
+
if (!Number.isFinite(minX) || !Number.isFinite(minY) || !Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
|
|
10758
|
+
return void 0;
|
|
10759
|
+
}
|
|
10760
|
+
return { minX, minY, width, height };
|
|
10761
|
+
}
|
|
10762
|
+
|
|
10763
|
+
// src/plugins/cad.ts
|
|
9982
10764
|
var cadExtensions = /* @__PURE__ */ new Set([
|
|
9983
10765
|
"dxf",
|
|
9984
10766
|
"dwg",
|
|
@@ -10042,7 +10824,7 @@ var cadMimeFormatMap = {
|
|
|
10042
10824
|
"application/vnd.oasis.layout": "oas",
|
|
10043
10825
|
"application/x-oasis-layout": "oas"
|
|
10044
10826
|
};
|
|
10045
|
-
function cadPlugin() {
|
|
10827
|
+
function cadPlugin(options = {}) {
|
|
10046
10828
|
return {
|
|
10047
10829
|
name: "cad",
|
|
10048
10830
|
match(file) {
|
|
@@ -10065,7 +10847,64 @@ function cadPlugin() {
|
|
|
10065
10847
|
return { destroy: () => panel.remove() };
|
|
10066
10848
|
}
|
|
10067
10849
|
if (extension === "dwg" || extension === "dwf") {
|
|
10068
|
-
|
|
10850
|
+
const arrayBuffer = await readArrayBuffer(ctx.file);
|
|
10851
|
+
const bytes = new Uint8Array(arrayBuffer);
|
|
10852
|
+
const enhancedInstance = await options.binaryRenderer?.({
|
|
10853
|
+
panel,
|
|
10854
|
+
fileName: ctx.file.name,
|
|
10855
|
+
extension,
|
|
10856
|
+
arrayBuffer,
|
|
10857
|
+
bytes,
|
|
10858
|
+
preview: ctx
|
|
10859
|
+
});
|
|
10860
|
+
if (enhancedInstance) {
|
|
10861
|
+
return {
|
|
10862
|
+
resize(size) {
|
|
10863
|
+
enhancedInstance.resize?.(size);
|
|
10864
|
+
},
|
|
10865
|
+
canCommand(command) {
|
|
10866
|
+
return enhancedInstance.canCommand?.(command) ?? false;
|
|
10867
|
+
},
|
|
10868
|
+
command(command) {
|
|
10869
|
+
return enhancedInstance.command?.(command);
|
|
10870
|
+
},
|
|
10871
|
+
destroy() {
|
|
10872
|
+
enhancedInstance.destroy();
|
|
10873
|
+
panel.remove();
|
|
10874
|
+
}
|
|
10875
|
+
};
|
|
10876
|
+
}
|
|
10877
|
+
if (extension === "dwg" && options.libreDwg !== false) {
|
|
10878
|
+
const libreDwgInstance = await renderLibreDwgPreview(
|
|
10879
|
+
{
|
|
10880
|
+
panel,
|
|
10881
|
+
fileName: ctx.file.name,
|
|
10882
|
+
extension,
|
|
10883
|
+
arrayBuffer,
|
|
10884
|
+
bytes,
|
|
10885
|
+
preview: ctx
|
|
10886
|
+
},
|
|
10887
|
+
typeof options.libreDwg === "object" ? options.libreDwg : void 0
|
|
10888
|
+
);
|
|
10889
|
+
if (libreDwgInstance) {
|
|
10890
|
+
return {
|
|
10891
|
+
resize(size) {
|
|
10892
|
+
libreDwgInstance.resize?.(size);
|
|
10893
|
+
},
|
|
10894
|
+
canCommand(command) {
|
|
10895
|
+
return libreDwgInstance.canCommand?.(command) ?? false;
|
|
10896
|
+
},
|
|
10897
|
+
command(command) {
|
|
10898
|
+
return libreDwgInstance.command?.(command);
|
|
10899
|
+
},
|
|
10900
|
+
destroy() {
|
|
10901
|
+
libreDwgInstance.destroy();
|
|
10902
|
+
panel.remove();
|
|
10903
|
+
}
|
|
10904
|
+
};
|
|
10905
|
+
}
|
|
10906
|
+
}
|
|
10907
|
+
renderBinaryCad(panel, bytes, extension, ctx.file.name);
|
|
10069
10908
|
return { destroy: () => panel.remove() };
|
|
10070
10909
|
}
|
|
10071
10910
|
if (extension === "gds") {
|
|
@@ -10750,11 +11589,10 @@ function createOasisStructureShapes(cellNames, fallbackCount) {
|
|
|
10750
11589
|
}
|
|
10751
11590
|
return shapes;
|
|
10752
11591
|
}
|
|
10753
|
-
function renderBinaryCad(panel,
|
|
10754
|
-
const bytes = new Uint8Array(arrayBuffer);
|
|
11592
|
+
function renderBinaryCad(panel, bytes, extension, fileName) {
|
|
10755
11593
|
const section = createSection(`${extension.toUpperCase()} \u6587\u4EF6\u9884\u89C8`);
|
|
10756
11594
|
const note = document.createElement("p");
|
|
10757
|
-
note.textContent = extension === "dwg" ? "
|
|
11595
|
+
note.textContent = extension === "dwg" ? "\u5DF2\u8BC6\u522B DWG \u4E13\u6709\u4E8C\u8FDB\u5236\u56FE\u7EB8\u3002\u6838\u5FC3\u63D2\u4EF6\u9ED8\u8BA4\u63D0\u4F9B\u7248\u672C\u3001\u5BB9\u5668\u3001\u7ED3\u6784\u7EBF\u7D22\u548C\u63A5\u5165\u5EFA\u8BAE\uFF1B\u771F\u5B9E\u51E0\u4F55\u6E32\u67D3\u53EF\u901A\u8FC7 cadPlugin({ binaryRenderer }) \u63A5\u5165\u53EF\u9009\u524D\u7AEF\u5F15\u64CE\u6216\u8F6C\u6362\u670D\u52A1\u3002" : "\u5DF2\u8BC6\u522B DWF \u53D1\u5E03\u56FE\u7EB8\u3002\u6838\u5FC3\u63D2\u4EF6\u9ED8\u8BA4\u63D0\u4F9B\u5BB9\u5668\u7EBF\u7D22\u548C\u63A5\u5165\u5EFA\u8BAE\uFF1B\u9AD8\u4FDD\u771F\u9875\u9762\u6E32\u67D3\u53EF\u901A\u8FC7 cadPlugin({ binaryRenderer }) \u63A5\u5165\u4E13\u7528\u89E3\u6790\u5668\u6216\u8F6C\u6362\u670D\u52A1\u3002";
|
|
10758
11596
|
const meta = document.createElement("div");
|
|
10759
11597
|
meta.className = "ofv-cad-summary";
|
|
10760
11598
|
appendMeta5(meta, "\u6587\u4EF6", fileName);
|
|
@@ -10763,33 +11601,41 @@ function renderBinaryCad(panel, arrayBuffer, extension, fileName) {
|
|
|
10763
11601
|
appendMeta5(meta, "\u7B7E\u540D", byteSignature2(bytes));
|
|
10764
11602
|
appendMeta5(meta, "\u7248\u672C", detectCadVersion(bytes, extension));
|
|
10765
11603
|
appendMeta5(meta, "\u5BB9\u5668", detectCadContainer(bytes));
|
|
10766
|
-
const actions = document.createElement("
|
|
11604
|
+
const actions = document.createElement("div");
|
|
10767
11605
|
actions.className = "ofv-cad-conversion";
|
|
11606
|
+
const actionTitle = document.createElement("h4");
|
|
11607
|
+
actionTitle.textContent = extension === "dwg" ? "\u63A8\u8350\u589E\u5F3A\u8DEF\u7EBF" : "\u63A8\u8350\u5904\u7406\u8DEF\u7EBF";
|
|
11608
|
+
const actionList = document.createElement("ol");
|
|
10768
11609
|
const suggestions = extension === "dwg" ? [
|
|
10769
|
-
"\u670D\u52A1\u7AEF\
|
|
10770
|
-
"\
|
|
10771
|
-
"\
|
|
11610
|
+
"\u4EA7\u54C1\u9ED8\u8BA4\u94FE\u8DEF\uFF1A\u670D\u52A1\u7AEF\u5C06 DWG \u8F6C\u4E3A PDF/SVG/DXF\uFF0C\u518D\u590D\u7528\u73B0\u6709 PDF\u3001\u56FE\u50CF\u6216 DXF \u9884\u89C8\u3002",
|
|
11611
|
+
"\u7EAF\u524D\u7AEF\u589E\u5F3A\uFF1A\u901A\u8FC7 binaryRenderer \u63A5\u5165 mlightcad / LibreDWG WASM \u4E00\u7C7B\u5F15\u64CE\uFF0C\u6309\u9700\u52A0\u8F7D worker \u548C\u5B57\u4F53\u8D44\u6E90\u3002",
|
|
11612
|
+
"\u5546\u7528\u9AD8\u4FDD\u771F\uFF1A\u63A5\u5165 ODA Drawings SDK / Web SDK\uFF0C\u9002\u5408\u590D\u6742\u56FE\u5C42\u3001\u5B57\u4F53\u3001\u5916\u90E8\u53C2\u7167\u548C\u5927\u56FE\u7EB8\u3002"
|
|
10772
11613
|
] : [
|
|
10773
11614
|
"\u4F18\u5148\u5728\u670D\u52A1\u7AEF\u8F6C\u6362\u4E3A PDF/SVG\uFF0C\u4FDD\u7559\u56FE\u5C42\u3001\u9875\u9762\u548C\u6807\u6CE8\u4FE1\u606F\u3002",
|
|
10774
|
-
"\u82E5 DWF \u4E3A\u538B\u7F29\u5BB9\u5668\uFF0C\u53EF\
|
|
11615
|
+
"\u82E5 DWF \u4E3A\u538B\u7F29\u5BB9\u5668\uFF0C\u53EF\u901A\u8FC7 binaryRenderer \u8BFB\u53D6 manifest/descriptor \u518D\u8FD8\u539F\u9875\u9762\u8D44\u6E90\u3002",
|
|
10775
11616
|
"\u82E5\u4E1A\u52A1\u53EA\u9700\u4E0B\u8F7D/\u5F52\u6863\uFF0C\u4FDD\u7559\u5F53\u524D\u6587\u4EF6\u5143\u4FE1\u606F\u548C\u8F6C\u6362\u63D0\u793A\u5373\u53EF\u3002"
|
|
10776
11617
|
];
|
|
10777
11618
|
for (const suggestion of suggestions) {
|
|
10778
11619
|
const item = document.createElement("li");
|
|
10779
11620
|
item.textContent = suggestion;
|
|
10780
|
-
|
|
11621
|
+
actionList.append(item);
|
|
10781
11622
|
}
|
|
11623
|
+
actions.append(actionTitle, actionList);
|
|
11624
|
+
const raw = document.createElement("details");
|
|
11625
|
+
raw.className = "ofv-details ofv-cad-raw-preview";
|
|
11626
|
+
const rawSummary = document.createElement("summary");
|
|
11627
|
+
rawSummary.textContent = "\u539F\u59CB\u5B57\u8282\u9884\u89C8";
|
|
10782
11628
|
const preview = document.createElement("pre");
|
|
10783
11629
|
preview.className = "ofv-text-block";
|
|
10784
11630
|
preview.textContent = hexPreview(bytes);
|
|
10785
|
-
|
|
11631
|
+
raw.append(rawSummary, preview);
|
|
11632
|
+
section.append(note, meta, actions, createBinaryCadProbe(bytes, extension), raw);
|
|
10786
11633
|
panel.append(section);
|
|
10787
11634
|
}
|
|
10788
11635
|
function createBinaryCadProbe(bytes, extension) {
|
|
10789
11636
|
const probe = probeBinaryCad(bytes);
|
|
10790
11637
|
const details = document.createElement("details");
|
|
10791
11638
|
details.className = "ofv-details ofv-cad-binary-probe";
|
|
10792
|
-
details.open = true;
|
|
10793
11639
|
const summary = document.createElement("summary");
|
|
10794
11640
|
summary.textContent = "\u4E8C\u8FDB\u5236\u7ED3\u6784\u63A2\u6D4B";
|
|
10795
11641
|
const meta = document.createElement("div");
|
|
@@ -12393,6 +13239,15 @@ function assetPlugin() {
|
|
|
12393
13239
|
const isExternal = Boolean(ctx.file.url);
|
|
12394
13240
|
const extension = resolveFormat(ctx.file, assetMimeFormatMap).toLowerCase();
|
|
12395
13241
|
const bytes = new Uint8Array(await readArrayBuffer(ctx.file).catch(() => new ArrayBuffer(0)));
|
|
13242
|
+
if (isPhotoshopAsset(extension)) {
|
|
13243
|
+
panel.append(await createPhotoshopPreview(bytes));
|
|
13244
|
+
return {
|
|
13245
|
+
destroy() {
|
|
13246
|
+
revokeObjectUrl(url, isExternal);
|
|
13247
|
+
panel.remove();
|
|
13248
|
+
}
|
|
13249
|
+
};
|
|
13250
|
+
}
|
|
12396
13251
|
const section = createSection(assetTitle(extension));
|
|
12397
13252
|
const summary = document.createElement("div");
|
|
12398
13253
|
summary.className = "ofv-asset-summary";
|
|
@@ -12414,9 +13269,6 @@ function assetPlugin() {
|
|
|
12414
13269
|
if (extension === "wasm") {
|
|
12415
13270
|
section.append(createWasmPreview(bytes));
|
|
12416
13271
|
}
|
|
12417
|
-
if (extension === "psd" || extension === "psb") {
|
|
12418
|
-
section.append(createPhotoshopPreview(bytes));
|
|
12419
|
-
}
|
|
12420
13272
|
if (extension === "sqlite" || extension === "sqlite3" || extension === "db") {
|
|
12421
13273
|
section.append(createSqlitePreview(bytes));
|
|
12422
13274
|
}
|
|
@@ -12469,7 +13321,7 @@ function assetGuidance(extension) {
|
|
|
12469
13321
|
return "\u5B57\u4F53\u6587\u4EF6\u5DF2\u8BC6\u522B\uFF0C\u5F53\u524D\u4F1A\u4F7F\u7528 FontFace \u5C55\u793A\u5B57\u5F62\u6837\u5F20\uFF0C\u5E76\u89E3\u6790 sfnt/WOFF \u8868\u76EE\u5F55\u548C name \u5143\u4FE1\u606F\u3002";
|
|
12470
13322
|
}
|
|
12471
13323
|
if (extension === "psd" || extension === "psb") {
|
|
12472
|
-
return "PSD/PSB \u5DF2\u8BC6\u522B\uFF0C\u5F53\u524D\u4F1A\u89E3\u6790\
|
|
13324
|
+
return "PSD/PSB \u5DF2\u8BC6\u522B\uFF0C\u5F53\u524D\u4F1A\u4F18\u5148\u89E3\u6790 Photoshop \u5408\u6210\u56FE\u9884\u89C8\uFF0C\u5E76\u4FDD\u7559\u753B\u5E03\u3001\u901A\u9053\u3001\u4F4D\u6DF1\u548C\u989C\u8272\u6A21\u5F0F\u7B49\u7ED3\u6784\u4FE1\u606F\u3002";
|
|
12473
13325
|
}
|
|
12474
13326
|
if (["ai", "eps", "ps"].includes(extension)) {
|
|
12475
13327
|
return "PostScript/Illustrator \u6587\u4EF6\u5DF2\u8BC6\u522B\uFF0C\u5F53\u524D\u4F1A\u89E3\u6790\u6587\u6863\u5934\u3001BoundingBox \u548C\u5E38\u89C1 DSC \u5143\u4FE1\u606F\uFF1B\u9AD8\u4FDD\u771F\u56FE\u5F62\u53EF\u540E\u7EED\u63A5\u5165 Ghostscript/Illustrator \u8F6C\u6362\u3002";
|
|
@@ -12491,6 +13343,9 @@ function assetGuidance(extension) {
|
|
|
12491
13343
|
function isFontAsset(extension) {
|
|
12492
13344
|
return ["ttf", "otf", "woff", "woff2", "eot"].includes(extension);
|
|
12493
13345
|
}
|
|
13346
|
+
function isPhotoshopAsset(extension) {
|
|
13347
|
+
return extension === "psd" || extension === "psb";
|
|
13348
|
+
}
|
|
12494
13349
|
async function createFontPreview(extension, url, fileName, bytes) {
|
|
12495
13350
|
const preview = document.createElement("div");
|
|
12496
13351
|
preview.className = "ofv-font-preview";
|
|
@@ -13252,12 +14107,9 @@ function wasmExternalKind(kind) {
|
|
|
13252
14107
|
};
|
|
13253
14108
|
return names[kind] || `unknown ${kind}`;
|
|
13254
14109
|
}
|
|
13255
|
-
function createPhotoshopPreview(bytes) {
|
|
14110
|
+
async function createPhotoshopPreview(bytes) {
|
|
13256
14111
|
const preview = document.createElement("div");
|
|
13257
14112
|
preview.className = "ofv-psd-preview";
|
|
13258
|
-
const heading = document.createElement("strong");
|
|
13259
|
-
heading.textContent = "Photoshop \u7ED3\u6784";
|
|
13260
|
-
preview.append(heading);
|
|
13261
14113
|
const header = parsePhotoshopHeader(bytes);
|
|
13262
14114
|
if (!header.valid) {
|
|
13263
14115
|
const error = document.createElement("p");
|
|
@@ -13266,16 +14118,66 @@ function createPhotoshopPreview(bytes) {
|
|
|
13266
14118
|
preview.append(error);
|
|
13267
14119
|
return preview;
|
|
13268
14120
|
}
|
|
13269
|
-
|
|
13270
|
-
summary.className = "ofv-psd-summary";
|
|
13271
|
-
appendMeta(summary, "\u7248\u672C", header.version === 2 ? "PSB \u5927\u6587\u6863" : "PSD");
|
|
13272
|
-
appendMeta(summary, "\u753B\u5E03", `${header.width} x ${header.height}px`);
|
|
13273
|
-
appendMeta(summary, "\u901A\u9053", header.channels ?? "\u672A\u77E5");
|
|
13274
|
-
appendMeta(summary, "\u4F4D\u6DF1", `${header.depth ?? "\u672A\u77E5"} bit`);
|
|
13275
|
-
appendMeta(summary, "\u989C\u8272\u6A21\u5F0F", photoshopColorMode(header.colorMode ?? -1));
|
|
13276
|
-
preview.append(summary);
|
|
14121
|
+
preview.append(await createPhotoshopCompositePreview(bytes, header));
|
|
13277
14122
|
return preview;
|
|
13278
14123
|
}
|
|
14124
|
+
async function createPhotoshopCompositePreview(bytes, header) {
|
|
14125
|
+
const wrapper = document.createElement("div");
|
|
14126
|
+
wrapper.className = "ofv-psd-composite";
|
|
14127
|
+
if (!canUseBrowserCanvas()) {
|
|
14128
|
+
const status = document.createElement("p");
|
|
14129
|
+
status.className = "ofv-psd-error";
|
|
14130
|
+
status.textContent = "\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301 Canvas\uFF0C\u65E0\u6CD5\u751F\u6210 PSD \u5408\u6210\u56FE\u9884\u89C8\u3002";
|
|
14131
|
+
wrapper.append(status);
|
|
14132
|
+
return wrapper;
|
|
14133
|
+
}
|
|
14134
|
+
try {
|
|
14135
|
+
const { readPsd } = await import("ag-psd");
|
|
14136
|
+
const psd = readPsd(toStandaloneArrayBuffer(bytes), {
|
|
14137
|
+
skipLayerImageData: true,
|
|
14138
|
+
skipThumbnail: true,
|
|
14139
|
+
skipLinkedFilesData: true,
|
|
14140
|
+
useImageData: true,
|
|
14141
|
+
throwForMissingFeatures: false,
|
|
14142
|
+
logMissingFeatures: false
|
|
14143
|
+
});
|
|
14144
|
+
const canvas = psd.canvas || createCanvasFromPsdImageData(psd);
|
|
14145
|
+
if (!canvas) {
|
|
14146
|
+
throw new Error("PSD \u6587\u4EF6\u6CA1\u6709\u53EF\u8BFB\u53D6\u7684\u5408\u6210\u56FE\u50CF\u6570\u636E\u3002\u8BF7\u5728 Photoshop \u4E2D\u5F00\u542F\u201C\u6700\u5927\u517C\u5BB9\u6027\u201D\u4FDD\u5B58\uFF0C\u6216\u63A5\u5165\u5916\u90E8\u8F6C\u6362\u670D\u52A1\u3002");
|
|
14147
|
+
}
|
|
14148
|
+
canvas.classList.add("ofv-psd-canvas");
|
|
14149
|
+
canvas.setAttribute("role", "img");
|
|
14150
|
+
canvas.setAttribute("aria-label", `Photoshop composite ${header.width} x ${header.height}`);
|
|
14151
|
+
wrapper.append(canvas);
|
|
14152
|
+
} catch (error) {
|
|
14153
|
+
const status = document.createElement("p");
|
|
14154
|
+
status.className = "ofv-psd-error";
|
|
14155
|
+
status.textContent = `PSD \u5408\u6210\u56FE\u89E3\u6790\u5931\u8D25\uFF1A${error instanceof Error ? error.message : "\u5F53\u524D\u6587\u4EF6\u7279\u6027\u6682\u4E0D\u652F\u6301\u3002"}`;
|
|
14156
|
+
wrapper.append(status);
|
|
14157
|
+
}
|
|
14158
|
+
return wrapper;
|
|
14159
|
+
}
|
|
14160
|
+
function canUseBrowserCanvas() {
|
|
14161
|
+
return typeof document !== "undefined" && typeof HTMLCanvasElement !== "undefined";
|
|
14162
|
+
}
|
|
14163
|
+
function toStandaloneArrayBuffer(bytes) {
|
|
14164
|
+
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
14165
|
+
}
|
|
14166
|
+
function createCanvasFromPsdImageData(psd) {
|
|
14167
|
+
const imageData = psd.imageData;
|
|
14168
|
+
if (!imageData || !imageData.width || !imageData.height || !(imageData.data instanceof Uint8ClampedArray)) {
|
|
14169
|
+
return void 0;
|
|
14170
|
+
}
|
|
14171
|
+
const canvas = document.createElement("canvas");
|
|
14172
|
+
canvas.width = imageData.width;
|
|
14173
|
+
canvas.height = imageData.height;
|
|
14174
|
+
const context = canvas.getContext("2d");
|
|
14175
|
+
if (!context) {
|
|
14176
|
+
return void 0;
|
|
14177
|
+
}
|
|
14178
|
+
context.putImageData(new ImageData(new Uint8ClampedArray(imageData.data), imageData.width, imageData.height), 0, 0);
|
|
14179
|
+
return canvas;
|
|
14180
|
+
}
|
|
13279
14181
|
function parsePhotoshopHeader(bytes) {
|
|
13280
14182
|
if (bytes.length < 26) {
|
|
13281
14183
|
return { valid: false, error: "\u6587\u4EF6\u592A\u77ED\uFF0C\u65E0\u6CD5\u8BFB\u53D6 PSD/PSB \u5934\u4FE1\u606F\u3002" };
|
|
@@ -13304,19 +14206,6 @@ function parsePhotoshopHeader(bytes) {
|
|
|
13304
14206
|
colorMode: view3.getUint16(24, false)
|
|
13305
14207
|
};
|
|
13306
14208
|
}
|
|
13307
|
-
function photoshopColorMode(mode) {
|
|
13308
|
-
const modes = {
|
|
13309
|
-
0: "Bitmap",
|
|
13310
|
-
1: "Grayscale",
|
|
13311
|
-
2: "Indexed",
|
|
13312
|
-
3: "RGB",
|
|
13313
|
-
4: "CMYK",
|
|
13314
|
-
7: "Multichannel",
|
|
13315
|
-
8: "Duotone",
|
|
13316
|
-
9: "Lab"
|
|
13317
|
-
};
|
|
13318
|
-
return modes[mode] ? `${modes[mode]} (${mode})` : `\u672A\u77E5 (${mode})`;
|
|
13319
|
-
}
|
|
13320
14209
|
function createSqlitePreview(bytes) {
|
|
13321
14210
|
const preview = document.createElement("div");
|
|
13322
14211
|
preview.className = "ofv-sqlite-preview";
|