@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.js
CHANGED
|
@@ -271,7 +271,7 @@ var extensionMimeMap = {
|
|
|
271
271
|
};
|
|
272
272
|
async function normalizeFile(source, fileName, mimeType) {
|
|
273
273
|
if (typeof source === "string") {
|
|
274
|
-
const name2 = fileName || source
|
|
274
|
+
const name2 = fileName || getFileNameFromUrl(source) || "remote-file";
|
|
275
275
|
const extension2 = getExtension(name2);
|
|
276
276
|
return {
|
|
277
277
|
source,
|
|
@@ -316,6 +316,17 @@ async function normalizeFile(source, fileName, mimeType) {
|
|
|
316
316
|
blob
|
|
317
317
|
};
|
|
318
318
|
}
|
|
319
|
+
function getFileNameFromUrl(source) {
|
|
320
|
+
const rawName = source.split(/[?#]/, 1)[0]?.split("/").filter(Boolean).pop() || "";
|
|
321
|
+
if (!rawName) {
|
|
322
|
+
return "";
|
|
323
|
+
}
|
|
324
|
+
try {
|
|
325
|
+
return decodeURIComponent(rawName);
|
|
326
|
+
} catch {
|
|
327
|
+
return rawName;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
319
330
|
function getExtension(name) {
|
|
320
331
|
const clean = name.split("?")[0]?.split("#")[0] || "";
|
|
321
332
|
const index = clean.lastIndexOf(".");
|
|
@@ -5368,7 +5379,13 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
5368
5379
|
const xlsx = await import("xlsx");
|
|
5369
5380
|
let workbook;
|
|
5370
5381
|
try {
|
|
5371
|
-
workbook = extension === "csv" || extension === "tsv" ? xlsx.read(decodeTextBuffer(arrayBuffer), {
|
|
5382
|
+
workbook = extension === "csv" || extension === "tsv" ? xlsx.read(decodeTextBuffer(arrayBuffer), {
|
|
5383
|
+
type: "string",
|
|
5384
|
+
FS: extension === "tsv" ? " " : ",",
|
|
5385
|
+
cellDates: true,
|
|
5386
|
+
cellNF: true,
|
|
5387
|
+
cellStyles: true
|
|
5388
|
+
}) : xlsx.read(arrayBuffer, { type: "array", cellDates: true, cellNF: true, cellStyles: true });
|
|
5372
5389
|
} catch (error) {
|
|
5373
5390
|
if (isLegacyOfficeBinary(extension)) {
|
|
5374
5391
|
renderLegacyOfficeBinary(panel, extension, arrayBuffer, `\u8868\u683C\u89E3\u6790\u5931\u8D25\uFF1A${normalizeOfficeError(error)}`);
|
|
@@ -5396,7 +5413,7 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
5396
5413
|
const heading = document.createElement("h3");
|
|
5397
5414
|
heading.textContent = sheetName;
|
|
5398
5415
|
const sheet = workbook.Sheets[sheetName];
|
|
5399
|
-
const range = xlsx.utils.decode_range(sheet["!ref"] || "A1:A1");
|
|
5416
|
+
const range = trimWorkbookSheetRange(sheet, xlsx.utils.decode_range(sheet["!ref"] || "A1:A1"), xlsx.utils.decode_cell);
|
|
5400
5417
|
const rowCount = range.e.r - range.s.r + 1;
|
|
5401
5418
|
const columnCount = range.e.c - range.s.c + 1;
|
|
5402
5419
|
const formulaRows = collectFormulaRows(sheet, range, xlsx.utils.encode_cell);
|
|
@@ -5406,6 +5423,7 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
5406
5423
|
const tableWrapper = document.createElement("div");
|
|
5407
5424
|
tableWrapper.className = "ofv-table-scroll";
|
|
5408
5425
|
const viewport = createSheetViewport(rowCount, columnCount);
|
|
5426
|
+
const columnSizing = { widths: /* @__PURE__ */ new Map() };
|
|
5409
5427
|
const windowControls = createSheetWindowControls(viewport, () => renderTableWindow());
|
|
5410
5428
|
const renderTableWindow = () => {
|
|
5411
5429
|
tableWrapper.replaceChildren(
|
|
@@ -5415,7 +5433,9 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
5415
5433
|
sheetIndex,
|
|
5416
5434
|
viewport,
|
|
5417
5435
|
xlsx.utils.encode_cell,
|
|
5418
|
-
xlsx.utils.format_cell
|
|
5436
|
+
xlsx.utils.format_cell,
|
|
5437
|
+
columnSizing,
|
|
5438
|
+
renderTableWindow
|
|
5419
5439
|
)
|
|
5420
5440
|
);
|
|
5421
5441
|
windowControls?.update();
|
|
@@ -5525,9 +5545,10 @@ function renderParsedSheets(panel, sheets, emptyMessage) {
|
|
|
5525
5545
|
const tableWrapper = document.createElement("div");
|
|
5526
5546
|
tableWrapper.className = "ofv-table-scroll";
|
|
5527
5547
|
const viewport = createSheetViewport(rowCount, columnCount);
|
|
5548
|
+
const columnSizing = { widths: /* @__PURE__ */ new Map() };
|
|
5528
5549
|
const windowControls = createSheetWindowControls(viewport, () => renderTableWindow());
|
|
5529
5550
|
const renderTableWindow = () => {
|
|
5530
|
-
tableWrapper.replaceChildren(createParsedSheetTable(sheet, sheetIndex, viewport));
|
|
5551
|
+
tableWrapper.replaceChildren(createParsedSheetTable(sheet, sheetIndex, viewport, columnSizing, renderTableWindow));
|
|
5531
5552
|
windowControls?.update();
|
|
5532
5553
|
};
|
|
5533
5554
|
content.append(heading, summary);
|
|
@@ -5754,6 +5775,45 @@ function chartText(element) {
|
|
|
5754
5775
|
function chartStringValues(element) {
|
|
5755
5776
|
return Array.from(element.querySelectorAll("*")).filter((item) => item.localName === "v" || item.localName === "t").map((item) => item.textContent?.trim() || "").filter(Boolean);
|
|
5756
5777
|
}
|
|
5778
|
+
function trimWorkbookSheetRange(sheet, range, decodeCell) {
|
|
5779
|
+
let minRow = Number.POSITIVE_INFINITY;
|
|
5780
|
+
let minColumn = Number.POSITIVE_INFINITY;
|
|
5781
|
+
let maxRow = Number.NEGATIVE_INFINITY;
|
|
5782
|
+
let maxColumn = Number.NEGATIVE_INFINITY;
|
|
5783
|
+
const include = (row, column) => {
|
|
5784
|
+
minRow = Math.min(minRow, row);
|
|
5785
|
+
minColumn = Math.min(minColumn, column);
|
|
5786
|
+
maxRow = Math.max(maxRow, row);
|
|
5787
|
+
maxColumn = Math.max(maxColumn, column);
|
|
5788
|
+
};
|
|
5789
|
+
for (const [address, cell] of Object.entries(sheet)) {
|
|
5790
|
+
if (address.startsWith("!")) {
|
|
5791
|
+
continue;
|
|
5792
|
+
}
|
|
5793
|
+
if (!cell || cell.v == null && !cell.f && !cell.w && !cell.h) {
|
|
5794
|
+
continue;
|
|
5795
|
+
}
|
|
5796
|
+
const decoded = decodeCell(address);
|
|
5797
|
+
include(decoded.r, decoded.c);
|
|
5798
|
+
}
|
|
5799
|
+
for (const merge of sheet["!merges"] || []) {
|
|
5800
|
+
include(merge.s.r, merge.s.c);
|
|
5801
|
+
include(merge.e.r, merge.e.c);
|
|
5802
|
+
}
|
|
5803
|
+
if (!Number.isFinite(minRow) || !Number.isFinite(minColumn) || !Number.isFinite(maxRow) || !Number.isFinite(maxColumn)) {
|
|
5804
|
+
return range;
|
|
5805
|
+
}
|
|
5806
|
+
return {
|
|
5807
|
+
s: {
|
|
5808
|
+
r: Math.max(range.s.r, minRow),
|
|
5809
|
+
c: Math.max(range.s.c, minColumn)
|
|
5810
|
+
},
|
|
5811
|
+
e: {
|
|
5812
|
+
r: Math.min(range.e.r, maxRow),
|
|
5813
|
+
c: Math.min(range.e.c, maxColumn)
|
|
5814
|
+
}
|
|
5815
|
+
};
|
|
5816
|
+
}
|
|
5757
5817
|
function createSheetViewport(rowCount, columnCount) {
|
|
5758
5818
|
return {
|
|
5759
5819
|
rowStart: 0,
|
|
@@ -5816,42 +5876,245 @@ function createWindowButton(label, onClick) {
|
|
|
5816
5876
|
function maxStart(total, size) {
|
|
5817
5877
|
return Math.max(0, total - size);
|
|
5818
5878
|
}
|
|
5819
|
-
function createWorkbookSheetTable(sheet, range, sheetIndex, viewport, encodeCell, formatCell) {
|
|
5879
|
+
function createWorkbookSheetTable(sheet, range, sheetIndex, viewport, encodeCell, formatCell, columnSizing, rerender) {
|
|
5820
5880
|
const table = document.createElement("table");
|
|
5821
5881
|
table.id = `ofv-sheet-${sheetIndex + 1}`;
|
|
5882
|
+
table.className = "ofv-workbook-table";
|
|
5822
5883
|
const rowEnd = Math.min(range.s.r + viewport.rowStart + SHEET_WINDOW_ROWS - 1, range.e.r);
|
|
5823
5884
|
const columnEnd = Math.min(range.s.c + viewport.columnStart + SHEET_WINDOW_COLUMNS - 1, range.e.c);
|
|
5824
|
-
|
|
5885
|
+
const columnStart = range.s.c + viewport.columnStart;
|
|
5886
|
+
const rowStart = range.s.r + viewport.rowStart;
|
|
5887
|
+
const mergePlan = createSheetMergePlan(sheet["!merges"] || [], rowStart, rowEnd, columnStart, columnEnd);
|
|
5888
|
+
const colGroup = document.createElement("colgroup");
|
|
5889
|
+
let tableWidth = 0;
|
|
5890
|
+
for (let columnIndex = columnStart; columnIndex <= columnEnd; columnIndex += 1) {
|
|
5891
|
+
const col = document.createElement("col");
|
|
5892
|
+
const width = columnSizing.widths.get(columnIndex) ?? getSheetColumnWidth(sheet["!cols"]?.[columnIndex]);
|
|
5893
|
+
col.dataset.columnIndex = String(columnIndex);
|
|
5894
|
+
col.style.width = `${width}px`;
|
|
5895
|
+
tableWidth += width;
|
|
5896
|
+
colGroup.append(col);
|
|
5897
|
+
}
|
|
5898
|
+
table.style.width = `${tableWidth}px`;
|
|
5899
|
+
table.append(colGroup);
|
|
5900
|
+
for (let rowIndex = rowStart; rowIndex <= rowEnd; rowIndex += 1) {
|
|
5825
5901
|
const row = document.createElement("tr");
|
|
5826
|
-
|
|
5827
|
-
|
|
5902
|
+
const rowHeight = getSheetRowHeight(sheet["!rows"]?.[rowIndex]);
|
|
5903
|
+
if (rowHeight) {
|
|
5904
|
+
row.style.height = `${rowHeight}px`;
|
|
5905
|
+
}
|
|
5906
|
+
for (let columnIndex = columnStart; columnIndex <= columnEnd; columnIndex += 1) {
|
|
5828
5907
|
const address = encodeCell({ r: rowIndex, c: columnIndex });
|
|
5829
|
-
const
|
|
5908
|
+
const coordinateKey = `${rowIndex}:${columnIndex}`;
|
|
5909
|
+
if (mergePlan.covered.has(coordinateKey)) {
|
|
5910
|
+
continue;
|
|
5911
|
+
}
|
|
5912
|
+
const merge = mergePlan.anchors.get(coordinateKey);
|
|
5913
|
+
const sourceAddress = merge ? encodeCell({ r: merge.sourceRow, c: merge.sourceColumn }) : address;
|
|
5914
|
+
const sourceCell = sheet[sourceAddress];
|
|
5915
|
+
const cell = document.createElement(rowIndex === range.s.r ? "th" : "td");
|
|
5830
5916
|
cell.dataset.cell = address;
|
|
5917
|
+
if (sourceAddress !== address) {
|
|
5918
|
+
cell.dataset.sourceCell = sourceAddress;
|
|
5919
|
+
}
|
|
5920
|
+
if (merge) {
|
|
5921
|
+
cell.classList.add("ofv-cell-merged");
|
|
5922
|
+
if (merge.rowspan > 1) {
|
|
5923
|
+
cell.rowSpan = merge.rowspan;
|
|
5924
|
+
}
|
|
5925
|
+
if (merge.colspan > 1) {
|
|
5926
|
+
cell.colSpan = merge.colspan;
|
|
5927
|
+
}
|
|
5928
|
+
}
|
|
5831
5929
|
const text = sourceCell ? formatCell(sourceCell) : "";
|
|
5832
5930
|
cell.textContent = text;
|
|
5833
5931
|
if (text) {
|
|
5834
5932
|
cell.title = text;
|
|
5835
5933
|
}
|
|
5934
|
+
applyWorkbookCellStyle(cell, sourceCell);
|
|
5836
5935
|
if (sourceCell?.f) {
|
|
5837
5936
|
cell.classList.add("ofv-cell-formula");
|
|
5838
5937
|
cell.title = `=${sourceCell.f}`;
|
|
5839
5938
|
}
|
|
5939
|
+
if (text.includes("\n")) {
|
|
5940
|
+
cell.classList.add("ofv-cell-multiline");
|
|
5941
|
+
}
|
|
5942
|
+
appendColumnResizeHandle(cell, columnIndex, columnSizing);
|
|
5840
5943
|
row.append(cell);
|
|
5841
5944
|
}
|
|
5842
5945
|
table.append(row);
|
|
5843
5946
|
}
|
|
5844
5947
|
return table;
|
|
5845
5948
|
}
|
|
5846
|
-
function
|
|
5949
|
+
function appendColumnResizeHandle(cell, columnIndex, columnSizing) {
|
|
5950
|
+
const handle = document.createElement("span");
|
|
5951
|
+
handle.className = "ofv-column-resize-handle";
|
|
5952
|
+
handle.setAttribute("aria-hidden", "true");
|
|
5953
|
+
handle.addEventListener("pointerdown", (event) => {
|
|
5954
|
+
event.preventDefault();
|
|
5955
|
+
event.stopPropagation();
|
|
5956
|
+
const startX = event.clientX;
|
|
5957
|
+
const startWidth = columnSizing.widths.get(columnIndex) ?? cell.getBoundingClientRect().width;
|
|
5958
|
+
handle.setPointerCapture(event.pointerId);
|
|
5959
|
+
const onMove = (moveEvent) => {
|
|
5960
|
+
const nextWidth = Math.max(48, Math.min(720, Math.round(startWidth + moveEvent.clientX - startX)));
|
|
5961
|
+
columnSizing.widths.set(columnIndex, nextWidth);
|
|
5962
|
+
updateRenderedColumnWidth(cell, columnIndex, nextWidth);
|
|
5963
|
+
};
|
|
5964
|
+
const onEnd = () => {
|
|
5965
|
+
handle.removeEventListener("pointermove", onMove);
|
|
5966
|
+
handle.removeEventListener("pointerup", onEnd);
|
|
5967
|
+
handle.removeEventListener("pointercancel", onEnd);
|
|
5968
|
+
};
|
|
5969
|
+
handle.addEventListener("pointermove", onMove);
|
|
5970
|
+
handle.addEventListener("pointerup", onEnd);
|
|
5971
|
+
handle.addEventListener("pointercancel", onEnd);
|
|
5972
|
+
});
|
|
5973
|
+
cell.append(handle);
|
|
5974
|
+
}
|
|
5975
|
+
function updateRenderedColumnWidth(cell, columnIndex, width) {
|
|
5976
|
+
const table = cell.closest("table");
|
|
5977
|
+
if (!table) {
|
|
5978
|
+
return;
|
|
5979
|
+
}
|
|
5980
|
+
const column = Array.from(table.querySelectorAll("col")).find(
|
|
5981
|
+
(col) => col.dataset.columnIndex === String(columnIndex)
|
|
5982
|
+
);
|
|
5983
|
+
if (column) {
|
|
5984
|
+
column.style.width = `${width}px`;
|
|
5985
|
+
}
|
|
5986
|
+
const tableWidth = Array.from(table.querySelectorAll("col")).reduce((sum, col) => {
|
|
5987
|
+
const parsed = Number.parseFloat(col.style.width);
|
|
5988
|
+
return sum + (Number.isFinite(parsed) ? parsed : 0);
|
|
5989
|
+
}, 0);
|
|
5990
|
+
if (tableWidth > 0) {
|
|
5991
|
+
table.style.width = `${Math.round(tableWidth)}px`;
|
|
5992
|
+
}
|
|
5993
|
+
}
|
|
5994
|
+
function createSheetMergePlan(merges, rowStart, rowEnd, columnStart, columnEnd) {
|
|
5995
|
+
const anchors = /* @__PURE__ */ new Map();
|
|
5996
|
+
const covered = /* @__PURE__ */ new Set();
|
|
5997
|
+
const encode = (row, column) => `${row}:${column}`;
|
|
5998
|
+
for (const merge of merges) {
|
|
5999
|
+
if (merge.e.r < rowStart || merge.s.r > rowEnd || merge.e.c < columnStart || merge.s.c > columnEnd) {
|
|
6000
|
+
continue;
|
|
6001
|
+
}
|
|
6002
|
+
const visibleStartRow = Math.max(merge.s.r, rowStart);
|
|
6003
|
+
const visibleEndRow = Math.min(merge.e.r, rowEnd);
|
|
6004
|
+
const visibleStartColumn = Math.max(merge.s.c, columnStart);
|
|
6005
|
+
const visibleEndColumn = Math.min(merge.e.c, columnEnd);
|
|
6006
|
+
const anchor = encode(visibleStartRow, visibleStartColumn);
|
|
6007
|
+
anchors.set(anchor, {
|
|
6008
|
+
rowspan: visibleEndRow - visibleStartRow + 1,
|
|
6009
|
+
colspan: visibleEndColumn - visibleStartColumn + 1,
|
|
6010
|
+
sourceRow: merge.s.r,
|
|
6011
|
+
sourceColumn: merge.s.c
|
|
6012
|
+
});
|
|
6013
|
+
for (let rowIndex = visibleStartRow; rowIndex <= visibleEndRow; rowIndex += 1) {
|
|
6014
|
+
for (let columnIndex = visibleStartColumn; columnIndex <= visibleEndColumn; columnIndex += 1) {
|
|
6015
|
+
const address = encode(rowIndex, columnIndex);
|
|
6016
|
+
if (address !== anchor) {
|
|
6017
|
+
covered.add(address);
|
|
6018
|
+
}
|
|
6019
|
+
}
|
|
6020
|
+
}
|
|
6021
|
+
}
|
|
6022
|
+
return { anchors, covered };
|
|
6023
|
+
}
|
|
6024
|
+
function getSheetColumnWidth(column) {
|
|
6025
|
+
if (column?.hidden) {
|
|
6026
|
+
return 0;
|
|
6027
|
+
}
|
|
6028
|
+
const width = column?.wpx || (column?.wch ? column.wch * 7 + 5 : void 0) || (column?.width ? column.width * 7 : void 0) || 96;
|
|
6029
|
+
return Math.max(28, Math.min(360, Math.round(width)));
|
|
6030
|
+
}
|
|
6031
|
+
function getSheetRowHeight(row) {
|
|
6032
|
+
if (row?.hidden) {
|
|
6033
|
+
return 0;
|
|
6034
|
+
}
|
|
6035
|
+
const height = row?.hpx || (row?.hpt ? row.hpt * 1.333 : void 0);
|
|
6036
|
+
return height ? Math.max(18, Math.min(260, Math.round(height))) : void 0;
|
|
6037
|
+
}
|
|
6038
|
+
function applyWorkbookCellStyle(cell, sourceCell) {
|
|
6039
|
+
const style = sourceCell?.s;
|
|
6040
|
+
if (!style) {
|
|
6041
|
+
return;
|
|
6042
|
+
}
|
|
6043
|
+
const fill = readWorkbookColor(style.fgColor || style.fill?.fgColor);
|
|
6044
|
+
if (fill && style.patternType !== "none") {
|
|
6045
|
+
cell.style.backgroundColor = fill;
|
|
6046
|
+
}
|
|
6047
|
+
const font = style.font;
|
|
6048
|
+
if (font) {
|
|
6049
|
+
if (font.bold) {
|
|
6050
|
+
cell.style.fontWeight = "700";
|
|
6051
|
+
}
|
|
6052
|
+
if (font.italic) {
|
|
6053
|
+
cell.style.fontStyle = "italic";
|
|
6054
|
+
}
|
|
6055
|
+
if (font.sz) {
|
|
6056
|
+
cell.style.fontSize = `${Math.max(9, Math.min(24, Number(font.sz)))}pt`;
|
|
6057
|
+
}
|
|
6058
|
+
const fontColor = readWorkbookColor(font.color);
|
|
6059
|
+
if (fontColor) {
|
|
6060
|
+
cell.style.color = fontColor;
|
|
6061
|
+
}
|
|
6062
|
+
}
|
|
6063
|
+
const alignment = style.alignment;
|
|
6064
|
+
if (alignment) {
|
|
6065
|
+
const horizontal = normalizeSheetHorizontalAlign(alignment.horizontal);
|
|
6066
|
+
if (horizontal) {
|
|
6067
|
+
cell.style.textAlign = horizontal;
|
|
6068
|
+
}
|
|
6069
|
+
const vertical = normalizeSheetVerticalAlign(alignment.vertical);
|
|
6070
|
+
if (vertical) {
|
|
6071
|
+
cell.style.verticalAlign = vertical;
|
|
6072
|
+
}
|
|
6073
|
+
if (alignment.wrapText) {
|
|
6074
|
+
cell.classList.add("ofv-cell-multiline");
|
|
6075
|
+
}
|
|
6076
|
+
}
|
|
6077
|
+
}
|
|
6078
|
+
function readWorkbookColor(color) {
|
|
6079
|
+
if (!color?.rgb) {
|
|
6080
|
+
return void 0;
|
|
6081
|
+
}
|
|
6082
|
+
const rgb = color.rgb.length === 8 ? color.rgb.slice(2) : color.rgb;
|
|
6083
|
+
return /^[\da-f]{6}$/i.test(rgb) ? `#${rgb}` : void 0;
|
|
6084
|
+
}
|
|
6085
|
+
function normalizeSheetHorizontalAlign(value) {
|
|
6086
|
+
if (value === "center" || value === "right" || value === "left" || value === "justify") {
|
|
6087
|
+
return value;
|
|
6088
|
+
}
|
|
6089
|
+
return void 0;
|
|
6090
|
+
}
|
|
6091
|
+
function normalizeSheetVerticalAlign(value) {
|
|
6092
|
+
if (value === "top" || value === "middle" || value === "bottom") {
|
|
6093
|
+
return value;
|
|
6094
|
+
}
|
|
6095
|
+
return void 0;
|
|
6096
|
+
}
|
|
6097
|
+
function createParsedSheetTable(sheet, sheetIndex, viewport, columnSizing, rerender) {
|
|
5847
6098
|
const table = document.createElement("table");
|
|
5848
6099
|
table.id = `ofv-sheet-${sheetIndex + 1}`;
|
|
5849
6100
|
const formulaMap = new Map(sheet.formulas.map((item) => [item.address, item.formula]));
|
|
5850
6101
|
const rowEnd = Math.min(viewport.rowStart + SHEET_WINDOW_ROWS, sheet.rows.length);
|
|
6102
|
+
const columnEnd = Math.min(viewport.columnStart + SHEET_WINDOW_COLUMNS, viewport.columnCount);
|
|
6103
|
+
const colGroup = document.createElement("colgroup");
|
|
6104
|
+
let tableWidth = 0;
|
|
6105
|
+
for (let columnIndex = viewport.columnStart; columnIndex < columnEnd; columnIndex += 1) {
|
|
6106
|
+
const width = columnSizing.widths.get(columnIndex) ?? 112;
|
|
6107
|
+
const col = document.createElement("col");
|
|
6108
|
+
col.dataset.columnIndex = String(columnIndex);
|
|
6109
|
+
col.style.width = `${width}px`;
|
|
6110
|
+
tableWidth += width;
|
|
6111
|
+
colGroup.append(col);
|
|
6112
|
+
}
|
|
6113
|
+
table.style.width = `${tableWidth}px`;
|
|
6114
|
+
table.append(colGroup);
|
|
5851
6115
|
for (let rowIndex = viewport.rowStart; rowIndex < rowEnd; rowIndex += 1) {
|
|
5852
6116
|
const sourceRow = sheet.rows[rowIndex] || [];
|
|
5853
6117
|
const row = document.createElement("tr");
|
|
5854
|
-
const columnEnd = Math.min(viewport.columnStart + SHEET_WINDOW_COLUMNS, viewport.columnCount);
|
|
5855
6118
|
for (let columnIndex = viewport.columnStart; columnIndex < columnEnd; columnIndex += 1) {
|
|
5856
6119
|
const value = sourceRow[columnIndex] || "";
|
|
5857
6120
|
const cell = document.createElement(rowIndex === 0 ? "th" : "td");
|
|
@@ -5866,6 +6129,10 @@ function createParsedSheetTable(sheet, sheetIndex, viewport) {
|
|
|
5866
6129
|
cell.classList.add("ofv-cell-formula");
|
|
5867
6130
|
cell.title = formula;
|
|
5868
6131
|
}
|
|
6132
|
+
if (value.includes("\n")) {
|
|
6133
|
+
cell.classList.add("ofv-cell-multiline");
|
|
6134
|
+
}
|
|
6135
|
+
appendColumnResizeHandle(cell, columnIndex, columnSizing);
|
|
5869
6136
|
row.append(cell);
|
|
5870
6137
|
}
|
|
5871
6138
|
table.append(row);
|
|
@@ -9926,6 +10193,521 @@ function decodeDrawioDiagram(value) {
|
|
|
9926
10193
|
|
|
9927
10194
|
// src/plugins/cad.ts
|
|
9928
10195
|
import pako3 from "pako";
|
|
10196
|
+
|
|
10197
|
+
// src/plugins/cad-dwg.ts
|
|
10198
|
+
var libreDwgPromise;
|
|
10199
|
+
var defaultLibreDwgWasmBaseUrl = "/vendor/libredwg-web";
|
|
10200
|
+
var minReadableDrawingHeight = 420;
|
|
10201
|
+
var svgNumberPattern = /-?\d*\.?\d+(?:e[-+]?\d+)?/gi;
|
|
10202
|
+
async function renderLibreDwgPreview(ctx, options = {}) {
|
|
10203
|
+
if (ctx.extension !== "dwg" || options.enabled === false) {
|
|
10204
|
+
return void 0;
|
|
10205
|
+
}
|
|
10206
|
+
const shell = document.createElement("div");
|
|
10207
|
+
shell.className = "ofv-dwg-preview";
|
|
10208
|
+
const status = document.createElement("div");
|
|
10209
|
+
status.className = "ofv-dwg-preview-status";
|
|
10210
|
+
status.textContent = "Loading DWG rendering engine...";
|
|
10211
|
+
shell.append(status);
|
|
10212
|
+
ctx.panel.append(shell);
|
|
10213
|
+
try {
|
|
10214
|
+
const { LibreDwg, Dwg_File_Type } = await loadLibreDwg();
|
|
10215
|
+
const libredwg = await LibreDwg.create(options.wasmBaseUrl || defaultLibreDwgWasmBaseUrl);
|
|
10216
|
+
const data = libredwg.dwg_read_data(ctx.arrayBuffer, Dwg_File_Type.DWG);
|
|
10217
|
+
if (!data) {
|
|
10218
|
+
throw new Error("DWG parser did not return drawing data.");
|
|
10219
|
+
}
|
|
10220
|
+
let svg = "";
|
|
10221
|
+
let stats;
|
|
10222
|
+
let thumbnailUrl;
|
|
10223
|
+
try {
|
|
10224
|
+
thumbnailUrl = createDwgThumbnailUrl(readDwgThumbnail(libredwg, data));
|
|
10225
|
+
try {
|
|
10226
|
+
const result = libredwg.convertEx(data);
|
|
10227
|
+
const database = result.database;
|
|
10228
|
+
stats = createDwgPreviewStats(database, result.stats.unknownEntityCount, Boolean(thumbnailUrl));
|
|
10229
|
+
svg = libredwg.dwg_to_svg(database);
|
|
10230
|
+
} catch (error) {
|
|
10231
|
+
if (thumbnailUrl) {
|
|
10232
|
+
const fallbackThumbnailUrl = thumbnailUrl;
|
|
10233
|
+
status.replaceChildren(createDwgThumbnailFallbackStatus(ctx.fileName, error));
|
|
10234
|
+
shell.append(createDwgThumbnailPreview(fallbackThumbnailUrl, ctx.fileName));
|
|
10235
|
+
return {
|
|
10236
|
+
destroy() {
|
|
10237
|
+
URL.revokeObjectURL(fallbackThumbnailUrl);
|
|
10238
|
+
shell.remove();
|
|
10239
|
+
}
|
|
10240
|
+
};
|
|
10241
|
+
}
|
|
10242
|
+
throw error;
|
|
10243
|
+
}
|
|
10244
|
+
} finally {
|
|
10245
|
+
libredwg.dwg_free(data);
|
|
10246
|
+
}
|
|
10247
|
+
if (!svg || !/<svg[\s>]/i.test(svg)) {
|
|
10248
|
+
if (thumbnailUrl) {
|
|
10249
|
+
const fallbackThumbnailUrl = thumbnailUrl;
|
|
10250
|
+
status.replaceChildren(createDwgThumbnailFallbackStatus(ctx.fileName, "DWG parser finished but did not produce SVG output."));
|
|
10251
|
+
shell.append(createDwgThumbnailPreview(fallbackThumbnailUrl, ctx.fileName));
|
|
10252
|
+
return {
|
|
10253
|
+
destroy() {
|
|
10254
|
+
URL.revokeObjectURL(fallbackThumbnailUrl);
|
|
10255
|
+
shell.remove();
|
|
10256
|
+
}
|
|
10257
|
+
};
|
|
10258
|
+
}
|
|
10259
|
+
throw new Error("DWG parser finished but did not produce SVG output.");
|
|
10260
|
+
}
|
|
10261
|
+
const doc = new DOMParser().parseFromString(svg, "image/svg+xml");
|
|
10262
|
+
const svgElement = doc.documentElement;
|
|
10263
|
+
if (!(svgElement instanceof SVGElement) || svgElement.nodeName.toLowerCase() !== "svg" || svgElement.querySelector("parsererror")) {
|
|
10264
|
+
throw new Error("DWG SVG output is invalid.");
|
|
10265
|
+
}
|
|
10266
|
+
svgElement.classList.add("ofv-dwg-preview-svg");
|
|
10267
|
+
svgElement.setAttribute("role", "img");
|
|
10268
|
+
svgElement.setAttribute("aria-label", ctx.fileName);
|
|
10269
|
+
normalizeDwgSvg(svgElement);
|
|
10270
|
+
const reliability = assessDwgSvgReliability(svgElement);
|
|
10271
|
+
status.replaceChildren(createDwgStatusTitle(ctx.fileName, stats, reliability));
|
|
10272
|
+
if (!reliability.isReliable && thumbnailUrl) {
|
|
10273
|
+
shell.append(createDwgThumbnailPreview(thumbnailUrl, ctx.fileName));
|
|
10274
|
+
return {
|
|
10275
|
+
destroy() {
|
|
10276
|
+
URL.revokeObjectURL(thumbnailUrl);
|
|
10277
|
+
shell.remove();
|
|
10278
|
+
}
|
|
10279
|
+
};
|
|
10280
|
+
}
|
|
10281
|
+
const drawing = createDwgDrawingViewport(svgElement);
|
|
10282
|
+
if (thumbnailUrl) {
|
|
10283
|
+
shell.append(createDwgThumbnailPanel(thumbnailUrl));
|
|
10284
|
+
}
|
|
10285
|
+
shell.append(drawing.frame);
|
|
10286
|
+
return {
|
|
10287
|
+
resize() {
|
|
10288
|
+
drawing.update();
|
|
10289
|
+
},
|
|
10290
|
+
canCommand(command) {
|
|
10291
|
+
return command === "zoom-in" || command === "zoom-out" || command === "zoom-reset";
|
|
10292
|
+
},
|
|
10293
|
+
command(command) {
|
|
10294
|
+
if (command === "zoom-in") {
|
|
10295
|
+
drawing.setZoom(drawing.zoom * 1.18);
|
|
10296
|
+
return true;
|
|
10297
|
+
}
|
|
10298
|
+
if (command === "zoom-out") {
|
|
10299
|
+
drawing.setZoom(drawing.zoom / 1.18);
|
|
10300
|
+
return true;
|
|
10301
|
+
}
|
|
10302
|
+
if (command === "zoom-reset") {
|
|
10303
|
+
drawing.setZoom(1);
|
|
10304
|
+
return true;
|
|
10305
|
+
}
|
|
10306
|
+
return false;
|
|
10307
|
+
},
|
|
10308
|
+
destroy() {
|
|
10309
|
+
if (thumbnailUrl) {
|
|
10310
|
+
URL.revokeObjectURL(thumbnailUrl);
|
|
10311
|
+
}
|
|
10312
|
+
shell.remove();
|
|
10313
|
+
}
|
|
10314
|
+
};
|
|
10315
|
+
} catch (error) {
|
|
10316
|
+
shell.remove();
|
|
10317
|
+
console.warn("DWG LibreDWG preview failed, falling back to metadata preview:", error);
|
|
10318
|
+
return void 0;
|
|
10319
|
+
}
|
|
10320
|
+
}
|
|
10321
|
+
function loadLibreDwg() {
|
|
10322
|
+
libreDwgPromise ||= import("@mlightcad/libredwg-web");
|
|
10323
|
+
return libreDwgPromise;
|
|
10324
|
+
}
|
|
10325
|
+
function createDwgStatusTitle(fileName, stats, reliability) {
|
|
10326
|
+
const wrapper = document.createElement("span");
|
|
10327
|
+
const title = document.createElement("strong");
|
|
10328
|
+
const note = document.createElement("small");
|
|
10329
|
+
title.textContent = reliability.isReliable ? `\u5B9E\u9A8C\u6027 DWG \u6A21\u578B\u7A7A\u95F4\u9884\u89C8 \xB7 ${fileName}` : `DWG \u5185\u7F6E\u9884\u89C8\u56FE \xB7 ${fileName}`;
|
|
10330
|
+
note.textContent = [
|
|
10331
|
+
`${stats.entityCount.toLocaleString()} \u4E2A\u5B9E\u4F53`,
|
|
10332
|
+
`${stats.visibleLayerCount}/${stats.layerCount} \u4E2A\u53EF\u89C1\u56FE\u5C42`,
|
|
10333
|
+
`${stats.layoutCount} \u4E2A\u5E03\u5C40`,
|
|
10334
|
+
stats.paperSpaceEntityCount ? `${stats.paperSpaceEntityCount.toLocaleString()} \u4E2A\u56FE\u7EB8\u7A7A\u95F4\u5B9E\u4F53` : "\u6A21\u578B\u7A7A\u95F4\u7EBF\u7A3F",
|
|
10335
|
+
stats.unknownEntityCount ? `${stats.unknownEntityCount} \u4E2A\u672A\u77E5\u5B9E\u4F53` : "\u5B9E\u4F53\u89E3\u6790\u5B8C\u6574",
|
|
10336
|
+
stats.hasThumbnail ? "\u5305\u542B\u5185\u7F6E\u7F29\u7565\u56FE" : "\u65E0\u5185\u7F6E\u7F29\u7565\u56FE"
|
|
10337
|
+
].join(" \xB7 ");
|
|
10338
|
+
const warning = document.createElement("small");
|
|
10339
|
+
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 ?? ""}`;
|
|
10340
|
+
wrapper.append(title, note, warning);
|
|
10341
|
+
return wrapper;
|
|
10342
|
+
}
|
|
10343
|
+
function createDwgThumbnailFallbackStatus(fileName, error) {
|
|
10344
|
+
const wrapper = document.createElement("span");
|
|
10345
|
+
const title = document.createElement("strong");
|
|
10346
|
+
title.textContent = `DWG \u5185\u7F6E\u9884\u89C8\u56FE \xB7 ${fileName}`;
|
|
10347
|
+
const note = document.createElement("small");
|
|
10348
|
+
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";
|
|
10349
|
+
const detail = document.createElement("small");
|
|
10350
|
+
detail.textContent = error instanceof Error ? error.message : String(error || "\u672A\u77E5\u89E3\u6790\u9519\u8BEF");
|
|
10351
|
+
wrapper.append(title, note, detail);
|
|
10352
|
+
return wrapper;
|
|
10353
|
+
}
|
|
10354
|
+
function createDwgPreviewStats(database, unknownEntityCount, hasThumbnail) {
|
|
10355
|
+
const layers = database.tables.LAYER.entries;
|
|
10356
|
+
return {
|
|
10357
|
+
entityCount: database.entities.length,
|
|
10358
|
+
layerCount: layers.length,
|
|
10359
|
+
layoutCount: database.objects.LAYOUT.length,
|
|
10360
|
+
unknownEntityCount,
|
|
10361
|
+
visibleLayerCount: layers.filter((layer) => !layer.off && !layer.frozen).length,
|
|
10362
|
+
paperSpaceEntityCount: database.entities.filter((entity) => entity.isInPaperSpace).length,
|
|
10363
|
+
hasThumbnail
|
|
10364
|
+
};
|
|
10365
|
+
}
|
|
10366
|
+
function readDwgThumbnail(libredwg, data) {
|
|
10367
|
+
try {
|
|
10368
|
+
const thumbnail = libredwg.dwg_bmp(data);
|
|
10369
|
+
if (!thumbnail?.data?.length) {
|
|
10370
|
+
return void 0;
|
|
10371
|
+
}
|
|
10372
|
+
return thumbnail;
|
|
10373
|
+
} catch {
|
|
10374
|
+
return void 0;
|
|
10375
|
+
}
|
|
10376
|
+
}
|
|
10377
|
+
function createDwgThumbnailUrl(thumbnail) {
|
|
10378
|
+
if (!thumbnail) {
|
|
10379
|
+
return void 0;
|
|
10380
|
+
}
|
|
10381
|
+
if (thumbnail.type === 6) {
|
|
10382
|
+
return URL.createObjectURL(new Blob([toArrayBuffer(thumbnail.data)], { type: "image/png" }));
|
|
10383
|
+
}
|
|
10384
|
+
if (thumbnail.type === 2) {
|
|
10385
|
+
return URL.createObjectURL(new Blob([toArrayBuffer(createBmpFileBytes(thumbnail.data))], { type: "image/bmp" }));
|
|
10386
|
+
}
|
|
10387
|
+
return void 0;
|
|
10388
|
+
}
|
|
10389
|
+
function toArrayBuffer(bytes) {
|
|
10390
|
+
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
10391
|
+
}
|
|
10392
|
+
function createBmpFileBytes(dibBytes) {
|
|
10393
|
+
const view3 = new DataView(dibBytes.buffer, dibBytes.byteOffset, dibBytes.byteLength);
|
|
10394
|
+
const headerSize = view3.getUint32(0, true);
|
|
10395
|
+
const bitCount = view3.getUint16(14, true);
|
|
10396
|
+
const paletteBytes = bitCount <= 8 ? 2 ** bitCount * 4 : 0;
|
|
10397
|
+
const pixelOffset = 14 + headerSize + paletteBytes;
|
|
10398
|
+
const bytes = new Uint8Array(14 + dibBytes.byteLength);
|
|
10399
|
+
bytes[0] = 66;
|
|
10400
|
+
bytes[1] = 77;
|
|
10401
|
+
const fileView = new DataView(bytes.buffer);
|
|
10402
|
+
fileView.setUint32(2, bytes.byteLength, true);
|
|
10403
|
+
fileView.setUint32(10, pixelOffset, true);
|
|
10404
|
+
bytes.set(dibBytes, 14);
|
|
10405
|
+
return bytes;
|
|
10406
|
+
}
|
|
10407
|
+
function createDwgThumbnailPanel(thumbnailUrl) {
|
|
10408
|
+
const panel = document.createElement("figure");
|
|
10409
|
+
panel.className = "ofv-dwg-thumbnail";
|
|
10410
|
+
const image = document.createElement("img");
|
|
10411
|
+
image.src = thumbnailUrl;
|
|
10412
|
+
image.alt = "DWG \u6587\u4EF6\u5185\u7F6E\u7F29\u7565\u56FE";
|
|
10413
|
+
const caption = document.createElement("figcaption");
|
|
10414
|
+
caption.textContent = "\u6587\u4EF6\u5185\u7F6E\u7F29\u7565\u56FE";
|
|
10415
|
+
panel.append(image, caption);
|
|
10416
|
+
return panel;
|
|
10417
|
+
}
|
|
10418
|
+
function createDwgThumbnailPreview(thumbnailUrl, fileName) {
|
|
10419
|
+
const figure = document.createElement("figure");
|
|
10420
|
+
figure.className = "ofv-dwg-thumbnail-preview";
|
|
10421
|
+
const image = document.createElement("img");
|
|
10422
|
+
image.src = thumbnailUrl;
|
|
10423
|
+
image.alt = `${fileName} \u5185\u7F6E\u9884\u89C8\u56FE`;
|
|
10424
|
+
const caption = document.createElement("figcaption");
|
|
10425
|
+
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";
|
|
10426
|
+
figure.append(image, caption);
|
|
10427
|
+
return figure;
|
|
10428
|
+
}
|
|
10429
|
+
function normalizeDwgSvg(svgElement) {
|
|
10430
|
+
svgElement.setAttribute("preserveAspectRatio", "xMidYMid meet");
|
|
10431
|
+
removeInheritedDwgFills(svgElement);
|
|
10432
|
+
focusDwgSvgOnMainDrawing(svgElement);
|
|
10433
|
+
const style = document.createElementNS("http://www.w3.org/2000/svg", "style");
|
|
10434
|
+
style.textContent = `
|
|
10435
|
+
.ofv-dwg-preview-svg { background: #020617; }
|
|
10436
|
+
.ofv-dwg-preview-svg g {
|
|
10437
|
+
fill: none !important;
|
|
10438
|
+
}
|
|
10439
|
+
.ofv-dwg-preview-svg line,
|
|
10440
|
+
.ofv-dwg-preview-svg path,
|
|
10441
|
+
.ofv-dwg-preview-svg polyline,
|
|
10442
|
+
.ofv-dwg-preview-svg polygon,
|
|
10443
|
+
.ofv-dwg-preview-svg circle,
|
|
10444
|
+
.ofv-dwg-preview-svg ellipse,
|
|
10445
|
+
.ofv-dwg-preview-svg rect {
|
|
10446
|
+
fill: none !important;
|
|
10447
|
+
vector-effect: non-scaling-stroke;
|
|
10448
|
+
stroke-width: 0.7px !important;
|
|
10449
|
+
stroke-linecap: round;
|
|
10450
|
+
stroke-linejoin: round;
|
|
10451
|
+
}
|
|
10452
|
+
.ofv-dwg-preview-svg [stroke="rgb(0,255,0)"] {
|
|
10453
|
+
stroke: #34d399 !important;
|
|
10454
|
+
stroke-opacity: 0.58 !important;
|
|
10455
|
+
}
|
|
10456
|
+
.ofv-dwg-preview-svg text {
|
|
10457
|
+
vector-effect: non-scaling-stroke;
|
|
10458
|
+
stroke-width: 0 !important;
|
|
10459
|
+
fill: currentColor !important;
|
|
10460
|
+
}
|
|
10461
|
+
`;
|
|
10462
|
+
svgElement.prepend(style);
|
|
10463
|
+
}
|
|
10464
|
+
function removeInheritedDwgFills(svgElement) {
|
|
10465
|
+
const shapeSelector = "line,path,polyline,polygon,circle,ellipse,rect";
|
|
10466
|
+
for (const group of svgElement.querySelectorAll("g[fill]")) {
|
|
10467
|
+
group.setAttribute("fill", "none");
|
|
10468
|
+
}
|
|
10469
|
+
for (const styledElement of svgElement.querySelectorAll("[style]")) {
|
|
10470
|
+
styledElement.style.fill = "none";
|
|
10471
|
+
}
|
|
10472
|
+
for (const shape of svgElement.querySelectorAll(shapeSelector)) {
|
|
10473
|
+
shape.setAttribute("fill", "none");
|
|
10474
|
+
}
|
|
10475
|
+
}
|
|
10476
|
+
function assessDwgSvgReliability(svgElement) {
|
|
10477
|
+
const bounds = readSvgViewBox(svgElement);
|
|
10478
|
+
if (!bounds) {
|
|
10479
|
+
return { isReliable: true };
|
|
10480
|
+
}
|
|
10481
|
+
const largePathCount = countLargeSvgPaths(svgElement, bounds);
|
|
10482
|
+
const totalPathCount = svgElement.querySelectorAll("path").length;
|
|
10483
|
+
if (largePathCount >= 24 && totalPathCount > 0 && largePathCount / totalPathCount > 0.08) {
|
|
10484
|
+
return {
|
|
10485
|
+
isReliable: false,
|
|
10486
|
+
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"
|
|
10487
|
+
};
|
|
10488
|
+
}
|
|
10489
|
+
return { isReliable: true };
|
|
10490
|
+
}
|
|
10491
|
+
function countLargeSvgPaths(svgElement, bounds) {
|
|
10492
|
+
let count = 0;
|
|
10493
|
+
const viewportArea = bounds.width * bounds.height;
|
|
10494
|
+
if (!Number.isFinite(viewportArea) || viewportArea <= 0) {
|
|
10495
|
+
return count;
|
|
10496
|
+
}
|
|
10497
|
+
for (const path of svgElement.querySelectorAll("path")) {
|
|
10498
|
+
if (path.closest("defs")) {
|
|
10499
|
+
continue;
|
|
10500
|
+
}
|
|
10501
|
+
const pathBounds = estimatePathBounds(path);
|
|
10502
|
+
if (!pathBounds) {
|
|
10503
|
+
continue;
|
|
10504
|
+
}
|
|
10505
|
+
const pathArea = pathBounds.width * pathBounds.height;
|
|
10506
|
+
const crossesViewport = pathBounds.minX <= bounds.minX + bounds.width * 0.02 || pathBounds.minY <= bounds.minY + bounds.height * 0.02;
|
|
10507
|
+
if (pathArea > viewportArea * 0.28 || crossesViewport && pathArea > viewportArea * 0.12) {
|
|
10508
|
+
count += 1;
|
|
10509
|
+
}
|
|
10510
|
+
}
|
|
10511
|
+
return count;
|
|
10512
|
+
}
|
|
10513
|
+
function estimatePathBounds(path) {
|
|
10514
|
+
const numbers = readNumbers(path.getAttribute("d") ?? "");
|
|
10515
|
+
if (numbers.length < 4) {
|
|
10516
|
+
return void 0;
|
|
10517
|
+
}
|
|
10518
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
10519
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
10520
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
10521
|
+
let maxY = Number.NEGATIVE_INFINITY;
|
|
10522
|
+
for (let index = 0; index + 1 < numbers.length; index += 2) {
|
|
10523
|
+
const x = numbers[index];
|
|
10524
|
+
const y = numbers[index + 1];
|
|
10525
|
+
if (!Number.isFinite(x) || !Number.isFinite(y) || Math.abs(x) > 1e9 || Math.abs(y) > 1e9) {
|
|
10526
|
+
continue;
|
|
10527
|
+
}
|
|
10528
|
+
minX = Math.min(minX, x);
|
|
10529
|
+
minY = Math.min(minY, y);
|
|
10530
|
+
maxX = Math.max(maxX, x);
|
|
10531
|
+
maxY = Math.max(maxY, y);
|
|
10532
|
+
}
|
|
10533
|
+
if (!Number.isFinite(minX) || !Number.isFinite(minY) || !Number.isFinite(maxX) || !Number.isFinite(maxY)) {
|
|
10534
|
+
return void 0;
|
|
10535
|
+
}
|
|
10536
|
+
return { minX, minY, width: maxX - minX, height: maxY - minY };
|
|
10537
|
+
}
|
|
10538
|
+
function focusDwgSvgOnMainDrawing(svgElement) {
|
|
10539
|
+
const originalBounds = readSvgViewBox(svgElement);
|
|
10540
|
+
const mainBounds = estimateMainDrawingBounds(svgElement);
|
|
10541
|
+
if (!originalBounds || !mainBounds || !shouldUseMainDrawingBounds(originalBounds, mainBounds)) {
|
|
10542
|
+
return;
|
|
10543
|
+
}
|
|
10544
|
+
const paddedBounds = padBounds(mainBounds, 0.05);
|
|
10545
|
+
svgElement.dataset.originalViewBox = formatViewBox(originalBounds);
|
|
10546
|
+
svgElement.dataset.focusViewBox = formatViewBox(paddedBounds);
|
|
10547
|
+
svgElement.setAttribute("viewBox", formatViewBox(paddedBounds));
|
|
10548
|
+
}
|
|
10549
|
+
function shouldUseMainDrawingBounds(original, candidate) {
|
|
10550
|
+
const originalAspectRatio = original.width / original.height;
|
|
10551
|
+
const candidateAspectRatio = candidate.width / candidate.height;
|
|
10552
|
+
const originalArea = original.width * original.height;
|
|
10553
|
+
const candidateArea = candidate.width * candidate.height;
|
|
10554
|
+
if (!Number.isFinite(originalArea) || !Number.isFinite(candidateArea) || candidateArea <= 0) {
|
|
10555
|
+
return false;
|
|
10556
|
+
}
|
|
10557
|
+
return originalAspectRatio > 8 || originalAspectRatio < 0.125 || candidateArea / originalArea < 0.55 || Math.abs(Math.log(originalAspectRatio / candidateAspectRatio)) > Math.log(4);
|
|
10558
|
+
}
|
|
10559
|
+
function estimateMainDrawingBounds(svgElement) {
|
|
10560
|
+
const points = [];
|
|
10561
|
+
const addPoint = (x, y) => {
|
|
10562
|
+
if (Number.isFinite(x) && Number.isFinite(y) && Math.abs(x) < 1e9 && Math.abs(y) < 1e9) {
|
|
10563
|
+
points.push([x, y]);
|
|
10564
|
+
}
|
|
10565
|
+
};
|
|
10566
|
+
for (const element of svgElement.querySelectorAll("line,path,polyline,polygon,circle,ellipse,rect,text,use")) {
|
|
10567
|
+
if (element.closest("defs")) {
|
|
10568
|
+
continue;
|
|
10569
|
+
}
|
|
10570
|
+
collectElementPoints(element, addPoint);
|
|
10571
|
+
}
|
|
10572
|
+
if (points.length < 16) {
|
|
10573
|
+
return void 0;
|
|
10574
|
+
}
|
|
10575
|
+
const xs = points.map(([x]) => x).sort((a, b) => a - b);
|
|
10576
|
+
const ys = points.map(([, y]) => y).sort((a, b) => a - b);
|
|
10577
|
+
const minX = quantile(xs, 5e-3);
|
|
10578
|
+
const maxX = quantile(xs, 0.995);
|
|
10579
|
+
const minY = quantile(ys, 5e-3);
|
|
10580
|
+
const maxY = quantile(ys, 0.995);
|
|
10581
|
+
if (!Number.isFinite(minX) || !Number.isFinite(maxX) || !Number.isFinite(minY) || !Number.isFinite(maxY)) {
|
|
10582
|
+
return void 0;
|
|
10583
|
+
}
|
|
10584
|
+
const width = maxX - minX;
|
|
10585
|
+
const height = maxY - minY;
|
|
10586
|
+
if (width <= 0 || height <= 0) {
|
|
10587
|
+
return void 0;
|
|
10588
|
+
}
|
|
10589
|
+
return { minX, minY, width, height };
|
|
10590
|
+
}
|
|
10591
|
+
function collectElementPoints(element, addPoint) {
|
|
10592
|
+
const tagName = element.tagName.toLowerCase();
|
|
10593
|
+
if (tagName === "line") {
|
|
10594
|
+
addPoint(readNumberAttribute(element, "x1"), readNumberAttribute(element, "y1"));
|
|
10595
|
+
addPoint(readNumberAttribute(element, "x2"), readNumberAttribute(element, "y2"));
|
|
10596
|
+
} else if (tagName === "circle") {
|
|
10597
|
+
const cx = readNumberAttribute(element, "cx");
|
|
10598
|
+
const cy = readNumberAttribute(element, "cy");
|
|
10599
|
+
const r = readNumberAttribute(element, "r");
|
|
10600
|
+
addPoint(cx - r, cy - r);
|
|
10601
|
+
addPoint(cx + r, cy + r);
|
|
10602
|
+
} else if (tagName === "ellipse") {
|
|
10603
|
+
const cx = readNumberAttribute(element, "cx");
|
|
10604
|
+
const cy = readNumberAttribute(element, "cy");
|
|
10605
|
+
const rx = readNumberAttribute(element, "rx");
|
|
10606
|
+
const ry = readNumberAttribute(element, "ry");
|
|
10607
|
+
addPoint(cx - rx, cy - ry);
|
|
10608
|
+
addPoint(cx + rx, cy + ry);
|
|
10609
|
+
} else if (tagName === "rect") {
|
|
10610
|
+
const x = readNumberAttribute(element, "x");
|
|
10611
|
+
const y = readNumberAttribute(element, "y");
|
|
10612
|
+
addPoint(x, y);
|
|
10613
|
+
addPoint(x + readNumberAttribute(element, "width"), y + readNumberAttribute(element, "height"));
|
|
10614
|
+
} else if (tagName === "text") {
|
|
10615
|
+
addPoint(readNumberAttribute(element, "x"), readNumberAttribute(element, "y"));
|
|
10616
|
+
} else if (tagName === "path") {
|
|
10617
|
+
collectNumberPairs(element.getAttribute("d"), addPoint);
|
|
10618
|
+
} else if (tagName === "polyline" || tagName === "polygon") {
|
|
10619
|
+
collectNumberPairs(element.getAttribute("points"), addPoint);
|
|
10620
|
+
}
|
|
10621
|
+
collectTranslatePoints(element.getAttribute("transform"), addPoint);
|
|
10622
|
+
}
|
|
10623
|
+
function collectNumberPairs(value, addPoint) {
|
|
10624
|
+
if (!value) {
|
|
10625
|
+
return;
|
|
10626
|
+
}
|
|
10627
|
+
const numbers = readNumbers(value);
|
|
10628
|
+
for (let index = 0; index + 1 < numbers.length; index += 2) {
|
|
10629
|
+
addPoint(numbers[index], numbers[index + 1]);
|
|
10630
|
+
}
|
|
10631
|
+
}
|
|
10632
|
+
function collectTranslatePoints(value, addPoint) {
|
|
10633
|
+
if (!value) {
|
|
10634
|
+
return;
|
|
10635
|
+
}
|
|
10636
|
+
const translatePattern = /translate\(\s*(-?\d*\.?\d+(?:e[-+]?\d+)?)(?:[\s,]+(-?\d*\.?\d+(?:e[-+]?\d+)?))?/gi;
|
|
10637
|
+
for (const match of value.matchAll(translatePattern)) {
|
|
10638
|
+
addPoint(Number(match[1]), Number(match[2] ?? 0));
|
|
10639
|
+
}
|
|
10640
|
+
}
|
|
10641
|
+
function readNumbers(value) {
|
|
10642
|
+
const matches = value.match(svgNumberPattern);
|
|
10643
|
+
return matches ? matches.map((item) => Number(item)).filter(Number.isFinite) : [];
|
|
10644
|
+
}
|
|
10645
|
+
function readNumberAttribute(element, name) {
|
|
10646
|
+
const value = Number(element.getAttribute(name) ?? 0);
|
|
10647
|
+
return Number.isFinite(value) ? value : 0;
|
|
10648
|
+
}
|
|
10649
|
+
function quantile(values, ratio) {
|
|
10650
|
+
const index = Math.max(0, Math.min(values.length - 1, Math.floor((values.length - 1) * ratio)));
|
|
10651
|
+
return values[index];
|
|
10652
|
+
}
|
|
10653
|
+
function padBounds(bounds, ratio) {
|
|
10654
|
+
const paddingX = bounds.width * ratio;
|
|
10655
|
+
const paddingY = bounds.height * ratio;
|
|
10656
|
+
return {
|
|
10657
|
+
minX: bounds.minX - paddingX,
|
|
10658
|
+
minY: bounds.minY - paddingY,
|
|
10659
|
+
width: bounds.width + paddingX * 2,
|
|
10660
|
+
height: bounds.height + paddingY * 2
|
|
10661
|
+
};
|
|
10662
|
+
}
|
|
10663
|
+
function formatViewBox(bounds) {
|
|
10664
|
+
return [bounds.minX, bounds.minY, bounds.width, bounds.height].map((value) => Number(value.toFixed(4))).join(" ");
|
|
10665
|
+
}
|
|
10666
|
+
function createDwgDrawingViewport(svgElement) {
|
|
10667
|
+
const frame = document.createElement("div");
|
|
10668
|
+
frame.className = "ofv-dwg-preview-frame";
|
|
10669
|
+
const importedSvg = document.importNode(svgElement, true);
|
|
10670
|
+
frame.append(importedSvg);
|
|
10671
|
+
const aspectRatio = readSvgAspectRatio(svgElement);
|
|
10672
|
+
let zoom = 1;
|
|
10673
|
+
const update = () => {
|
|
10674
|
+
const frameWidth = Math.max(frame.clientWidth, 1);
|
|
10675
|
+
const readableWidth = aspectRatio ? minReadableDrawingHeight * aspectRatio : frameWidth;
|
|
10676
|
+
const width = Math.max(frameWidth, readableWidth) * zoom;
|
|
10677
|
+
importedSvg.style.width = `${Math.round(width)}px`;
|
|
10678
|
+
importedSvg.style.minWidth = `${Math.round(width)}px`;
|
|
10679
|
+
};
|
|
10680
|
+
const setZoom = (nextZoom) => {
|
|
10681
|
+
zoom = Math.min(Math.max(nextZoom, 0.2), 8);
|
|
10682
|
+
update();
|
|
10683
|
+
};
|
|
10684
|
+
requestAnimationFrame(update);
|
|
10685
|
+
return {
|
|
10686
|
+
frame,
|
|
10687
|
+
get zoom() {
|
|
10688
|
+
return zoom;
|
|
10689
|
+
},
|
|
10690
|
+
setZoom,
|
|
10691
|
+
update
|
|
10692
|
+
};
|
|
10693
|
+
}
|
|
10694
|
+
function readSvgAspectRatio(svgElement) {
|
|
10695
|
+
const viewBox = readSvgViewBox(svgElement);
|
|
10696
|
+
return viewBox ? viewBox.width / viewBox.height : void 0;
|
|
10697
|
+
}
|
|
10698
|
+
function readSvgViewBox(svgElement) {
|
|
10699
|
+
const viewBox = svgElement.getAttribute("viewBox");
|
|
10700
|
+
if (!viewBox) {
|
|
10701
|
+
return void 0;
|
|
10702
|
+
}
|
|
10703
|
+
const [minX, minY, width, height] = viewBox.trim().split(/[\s,]+/).map((value) => Number(value));
|
|
10704
|
+
if (!Number.isFinite(minX) || !Number.isFinite(minY) || !Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
|
|
10705
|
+
return void 0;
|
|
10706
|
+
}
|
|
10707
|
+
return { minX, minY, width, height };
|
|
10708
|
+
}
|
|
10709
|
+
|
|
10710
|
+
// src/plugins/cad.ts
|
|
9929
10711
|
var cadExtensions = /* @__PURE__ */ new Set([
|
|
9930
10712
|
"dxf",
|
|
9931
10713
|
"dwg",
|
|
@@ -9989,7 +10771,7 @@ var cadMimeFormatMap = {
|
|
|
9989
10771
|
"application/vnd.oasis.layout": "oas",
|
|
9990
10772
|
"application/x-oasis-layout": "oas"
|
|
9991
10773
|
};
|
|
9992
|
-
function cadPlugin() {
|
|
10774
|
+
function cadPlugin(options = {}) {
|
|
9993
10775
|
return {
|
|
9994
10776
|
name: "cad",
|
|
9995
10777
|
match(file) {
|
|
@@ -10012,7 +10794,64 @@ function cadPlugin() {
|
|
|
10012
10794
|
return { destroy: () => panel.remove() };
|
|
10013
10795
|
}
|
|
10014
10796
|
if (extension === "dwg" || extension === "dwf") {
|
|
10015
|
-
|
|
10797
|
+
const arrayBuffer = await readArrayBuffer(ctx.file);
|
|
10798
|
+
const bytes = new Uint8Array(arrayBuffer);
|
|
10799
|
+
const enhancedInstance = await options.binaryRenderer?.({
|
|
10800
|
+
panel,
|
|
10801
|
+
fileName: ctx.file.name,
|
|
10802
|
+
extension,
|
|
10803
|
+
arrayBuffer,
|
|
10804
|
+
bytes,
|
|
10805
|
+
preview: ctx
|
|
10806
|
+
});
|
|
10807
|
+
if (enhancedInstance) {
|
|
10808
|
+
return {
|
|
10809
|
+
resize(size) {
|
|
10810
|
+
enhancedInstance.resize?.(size);
|
|
10811
|
+
},
|
|
10812
|
+
canCommand(command) {
|
|
10813
|
+
return enhancedInstance.canCommand?.(command) ?? false;
|
|
10814
|
+
},
|
|
10815
|
+
command(command) {
|
|
10816
|
+
return enhancedInstance.command?.(command);
|
|
10817
|
+
},
|
|
10818
|
+
destroy() {
|
|
10819
|
+
enhancedInstance.destroy();
|
|
10820
|
+
panel.remove();
|
|
10821
|
+
}
|
|
10822
|
+
};
|
|
10823
|
+
}
|
|
10824
|
+
if (extension === "dwg" && options.libreDwg !== false) {
|
|
10825
|
+
const libreDwgInstance = await renderLibreDwgPreview(
|
|
10826
|
+
{
|
|
10827
|
+
panel,
|
|
10828
|
+
fileName: ctx.file.name,
|
|
10829
|
+
extension,
|
|
10830
|
+
arrayBuffer,
|
|
10831
|
+
bytes,
|
|
10832
|
+
preview: ctx
|
|
10833
|
+
},
|
|
10834
|
+
typeof options.libreDwg === "object" ? options.libreDwg : void 0
|
|
10835
|
+
);
|
|
10836
|
+
if (libreDwgInstance) {
|
|
10837
|
+
return {
|
|
10838
|
+
resize(size) {
|
|
10839
|
+
libreDwgInstance.resize?.(size);
|
|
10840
|
+
},
|
|
10841
|
+
canCommand(command) {
|
|
10842
|
+
return libreDwgInstance.canCommand?.(command) ?? false;
|
|
10843
|
+
},
|
|
10844
|
+
command(command) {
|
|
10845
|
+
return libreDwgInstance.command?.(command);
|
|
10846
|
+
},
|
|
10847
|
+
destroy() {
|
|
10848
|
+
libreDwgInstance.destroy();
|
|
10849
|
+
panel.remove();
|
|
10850
|
+
}
|
|
10851
|
+
};
|
|
10852
|
+
}
|
|
10853
|
+
}
|
|
10854
|
+
renderBinaryCad(panel, bytes, extension, ctx.file.name);
|
|
10016
10855
|
return { destroy: () => panel.remove() };
|
|
10017
10856
|
}
|
|
10018
10857
|
if (extension === "gds") {
|
|
@@ -10697,11 +11536,10 @@ function createOasisStructureShapes(cellNames, fallbackCount) {
|
|
|
10697
11536
|
}
|
|
10698
11537
|
return shapes;
|
|
10699
11538
|
}
|
|
10700
|
-
function renderBinaryCad(panel,
|
|
10701
|
-
const bytes = new Uint8Array(arrayBuffer);
|
|
11539
|
+
function renderBinaryCad(panel, bytes, extension, fileName) {
|
|
10702
11540
|
const section = createSection(`${extension.toUpperCase()} \u6587\u4EF6\u9884\u89C8`);
|
|
10703
11541
|
const note = document.createElement("p");
|
|
10704
|
-
note.textContent = extension === "dwg" ? "
|
|
11542
|
+
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";
|
|
10705
11543
|
const meta = document.createElement("div");
|
|
10706
11544
|
meta.className = "ofv-cad-summary";
|
|
10707
11545
|
appendMeta5(meta, "\u6587\u4EF6", fileName);
|
|
@@ -10710,33 +11548,41 @@ function renderBinaryCad(panel, arrayBuffer, extension, fileName) {
|
|
|
10710
11548
|
appendMeta5(meta, "\u7B7E\u540D", byteSignature2(bytes));
|
|
10711
11549
|
appendMeta5(meta, "\u7248\u672C", detectCadVersion(bytes, extension));
|
|
10712
11550
|
appendMeta5(meta, "\u5BB9\u5668", detectCadContainer(bytes));
|
|
10713
|
-
const actions = document.createElement("
|
|
11551
|
+
const actions = document.createElement("div");
|
|
10714
11552
|
actions.className = "ofv-cad-conversion";
|
|
11553
|
+
const actionTitle = document.createElement("h4");
|
|
11554
|
+
actionTitle.textContent = extension === "dwg" ? "\u63A8\u8350\u589E\u5F3A\u8DEF\u7EBF" : "\u63A8\u8350\u5904\u7406\u8DEF\u7EBF";
|
|
11555
|
+
const actionList = document.createElement("ol");
|
|
10715
11556
|
const suggestions = extension === "dwg" ? [
|
|
10716
|
-
"\u670D\u52A1\u7AEF\
|
|
10717
|
-
"\
|
|
10718
|
-
"\
|
|
11557
|
+
"\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",
|
|
11558
|
+
"\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",
|
|
11559
|
+
"\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"
|
|
10719
11560
|
] : [
|
|
10720
11561
|
"\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",
|
|
10721
|
-
"\u82E5 DWF \u4E3A\u538B\u7F29\u5BB9\u5668\uFF0C\u53EF\
|
|
11562
|
+
"\u82E5 DWF \u4E3A\u538B\u7F29\u5BB9\u5668\uFF0C\u53EF\u901A\u8FC7 binaryRenderer \u8BFB\u53D6 manifest/descriptor \u518D\u8FD8\u539F\u9875\u9762\u8D44\u6E90\u3002",
|
|
10722
11563
|
"\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"
|
|
10723
11564
|
];
|
|
10724
11565
|
for (const suggestion of suggestions) {
|
|
10725
11566
|
const item = document.createElement("li");
|
|
10726
11567
|
item.textContent = suggestion;
|
|
10727
|
-
|
|
11568
|
+
actionList.append(item);
|
|
10728
11569
|
}
|
|
11570
|
+
actions.append(actionTitle, actionList);
|
|
11571
|
+
const raw = document.createElement("details");
|
|
11572
|
+
raw.className = "ofv-details ofv-cad-raw-preview";
|
|
11573
|
+
const rawSummary = document.createElement("summary");
|
|
11574
|
+
rawSummary.textContent = "\u539F\u59CB\u5B57\u8282\u9884\u89C8";
|
|
10729
11575
|
const preview = document.createElement("pre");
|
|
10730
11576
|
preview.className = "ofv-text-block";
|
|
10731
11577
|
preview.textContent = hexPreview(bytes);
|
|
10732
|
-
|
|
11578
|
+
raw.append(rawSummary, preview);
|
|
11579
|
+
section.append(note, meta, actions, createBinaryCadProbe(bytes, extension), raw);
|
|
10733
11580
|
panel.append(section);
|
|
10734
11581
|
}
|
|
10735
11582
|
function createBinaryCadProbe(bytes, extension) {
|
|
10736
11583
|
const probe = probeBinaryCad(bytes);
|
|
10737
11584
|
const details = document.createElement("details");
|
|
10738
11585
|
details.className = "ofv-details ofv-cad-binary-probe";
|
|
10739
|
-
details.open = true;
|
|
10740
11586
|
const summary = document.createElement("summary");
|
|
10741
11587
|
summary.textContent = "\u4E8C\u8FDB\u5236\u7ED3\u6784\u63A2\u6D4B";
|
|
10742
11588
|
const meta = document.createElement("div");
|
|
@@ -12340,6 +13186,15 @@ function assetPlugin() {
|
|
|
12340
13186
|
const isExternal = Boolean(ctx.file.url);
|
|
12341
13187
|
const extension = resolveFormat(ctx.file, assetMimeFormatMap).toLowerCase();
|
|
12342
13188
|
const bytes = new Uint8Array(await readArrayBuffer(ctx.file).catch(() => new ArrayBuffer(0)));
|
|
13189
|
+
if (isPhotoshopAsset(extension)) {
|
|
13190
|
+
panel.append(await createPhotoshopPreview(bytes));
|
|
13191
|
+
return {
|
|
13192
|
+
destroy() {
|
|
13193
|
+
revokeObjectUrl(url, isExternal);
|
|
13194
|
+
panel.remove();
|
|
13195
|
+
}
|
|
13196
|
+
};
|
|
13197
|
+
}
|
|
12343
13198
|
const section = createSection(assetTitle(extension));
|
|
12344
13199
|
const summary = document.createElement("div");
|
|
12345
13200
|
summary.className = "ofv-asset-summary";
|
|
@@ -12361,9 +13216,6 @@ function assetPlugin() {
|
|
|
12361
13216
|
if (extension === "wasm") {
|
|
12362
13217
|
section.append(createWasmPreview(bytes));
|
|
12363
13218
|
}
|
|
12364
|
-
if (extension === "psd" || extension === "psb") {
|
|
12365
|
-
section.append(createPhotoshopPreview(bytes));
|
|
12366
|
-
}
|
|
12367
13219
|
if (extension === "sqlite" || extension === "sqlite3" || extension === "db") {
|
|
12368
13220
|
section.append(createSqlitePreview(bytes));
|
|
12369
13221
|
}
|
|
@@ -12416,7 +13268,7 @@ function assetGuidance(extension) {
|
|
|
12416
13268
|
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";
|
|
12417
13269
|
}
|
|
12418
13270
|
if (extension === "psd" || extension === "psb") {
|
|
12419
|
-
return "PSD/PSB \u5DF2\u8BC6\u522B\uFF0C\u5F53\u524D\u4F1A\u89E3\u6790\
|
|
13271
|
+
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";
|
|
12420
13272
|
}
|
|
12421
13273
|
if (["ai", "eps", "ps"].includes(extension)) {
|
|
12422
13274
|
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";
|
|
@@ -12438,6 +13290,9 @@ function assetGuidance(extension) {
|
|
|
12438
13290
|
function isFontAsset(extension) {
|
|
12439
13291
|
return ["ttf", "otf", "woff", "woff2", "eot"].includes(extension);
|
|
12440
13292
|
}
|
|
13293
|
+
function isPhotoshopAsset(extension) {
|
|
13294
|
+
return extension === "psd" || extension === "psb";
|
|
13295
|
+
}
|
|
12441
13296
|
async function createFontPreview(extension, url, fileName, bytes) {
|
|
12442
13297
|
const preview = document.createElement("div");
|
|
12443
13298
|
preview.className = "ofv-font-preview";
|
|
@@ -13199,12 +14054,9 @@ function wasmExternalKind(kind) {
|
|
|
13199
14054
|
};
|
|
13200
14055
|
return names[kind] || `unknown ${kind}`;
|
|
13201
14056
|
}
|
|
13202
|
-
function createPhotoshopPreview(bytes) {
|
|
14057
|
+
async function createPhotoshopPreview(bytes) {
|
|
13203
14058
|
const preview = document.createElement("div");
|
|
13204
14059
|
preview.className = "ofv-psd-preview";
|
|
13205
|
-
const heading = document.createElement("strong");
|
|
13206
|
-
heading.textContent = "Photoshop \u7ED3\u6784";
|
|
13207
|
-
preview.append(heading);
|
|
13208
14060
|
const header = parsePhotoshopHeader(bytes);
|
|
13209
14061
|
if (!header.valid) {
|
|
13210
14062
|
const error = document.createElement("p");
|
|
@@ -13213,16 +14065,66 @@ function createPhotoshopPreview(bytes) {
|
|
|
13213
14065
|
preview.append(error);
|
|
13214
14066
|
return preview;
|
|
13215
14067
|
}
|
|
13216
|
-
|
|
13217
|
-
summary.className = "ofv-psd-summary";
|
|
13218
|
-
appendMeta(summary, "\u7248\u672C", header.version === 2 ? "PSB \u5927\u6587\u6863" : "PSD");
|
|
13219
|
-
appendMeta(summary, "\u753B\u5E03", `${header.width} x ${header.height}px`);
|
|
13220
|
-
appendMeta(summary, "\u901A\u9053", header.channels ?? "\u672A\u77E5");
|
|
13221
|
-
appendMeta(summary, "\u4F4D\u6DF1", `${header.depth ?? "\u672A\u77E5"} bit`);
|
|
13222
|
-
appendMeta(summary, "\u989C\u8272\u6A21\u5F0F", photoshopColorMode(header.colorMode ?? -1));
|
|
13223
|
-
preview.append(summary);
|
|
14068
|
+
preview.append(await createPhotoshopCompositePreview(bytes, header));
|
|
13224
14069
|
return preview;
|
|
13225
14070
|
}
|
|
14071
|
+
async function createPhotoshopCompositePreview(bytes, header) {
|
|
14072
|
+
const wrapper = document.createElement("div");
|
|
14073
|
+
wrapper.className = "ofv-psd-composite";
|
|
14074
|
+
if (!canUseBrowserCanvas()) {
|
|
14075
|
+
const status = document.createElement("p");
|
|
14076
|
+
status.className = "ofv-psd-error";
|
|
14077
|
+
status.textContent = "\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301 Canvas\uFF0C\u65E0\u6CD5\u751F\u6210 PSD \u5408\u6210\u56FE\u9884\u89C8\u3002";
|
|
14078
|
+
wrapper.append(status);
|
|
14079
|
+
return wrapper;
|
|
14080
|
+
}
|
|
14081
|
+
try {
|
|
14082
|
+
const { readPsd } = await import("ag-psd");
|
|
14083
|
+
const psd = readPsd(toStandaloneArrayBuffer(bytes), {
|
|
14084
|
+
skipLayerImageData: true,
|
|
14085
|
+
skipThumbnail: true,
|
|
14086
|
+
skipLinkedFilesData: true,
|
|
14087
|
+
useImageData: true,
|
|
14088
|
+
throwForMissingFeatures: false,
|
|
14089
|
+
logMissingFeatures: false
|
|
14090
|
+
});
|
|
14091
|
+
const canvas = psd.canvas || createCanvasFromPsdImageData(psd);
|
|
14092
|
+
if (!canvas) {
|
|
14093
|
+
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");
|
|
14094
|
+
}
|
|
14095
|
+
canvas.classList.add("ofv-psd-canvas");
|
|
14096
|
+
canvas.setAttribute("role", "img");
|
|
14097
|
+
canvas.setAttribute("aria-label", `Photoshop composite ${header.width} x ${header.height}`);
|
|
14098
|
+
wrapper.append(canvas);
|
|
14099
|
+
} catch (error) {
|
|
14100
|
+
const status = document.createElement("p");
|
|
14101
|
+
status.className = "ofv-psd-error";
|
|
14102
|
+
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"}`;
|
|
14103
|
+
wrapper.append(status);
|
|
14104
|
+
}
|
|
14105
|
+
return wrapper;
|
|
14106
|
+
}
|
|
14107
|
+
function canUseBrowserCanvas() {
|
|
14108
|
+
return typeof document !== "undefined" && typeof HTMLCanvasElement !== "undefined";
|
|
14109
|
+
}
|
|
14110
|
+
function toStandaloneArrayBuffer(bytes) {
|
|
14111
|
+
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
14112
|
+
}
|
|
14113
|
+
function createCanvasFromPsdImageData(psd) {
|
|
14114
|
+
const imageData = psd.imageData;
|
|
14115
|
+
if (!imageData || !imageData.width || !imageData.height || !(imageData.data instanceof Uint8ClampedArray)) {
|
|
14116
|
+
return void 0;
|
|
14117
|
+
}
|
|
14118
|
+
const canvas = document.createElement("canvas");
|
|
14119
|
+
canvas.width = imageData.width;
|
|
14120
|
+
canvas.height = imageData.height;
|
|
14121
|
+
const context = canvas.getContext("2d");
|
|
14122
|
+
if (!context) {
|
|
14123
|
+
return void 0;
|
|
14124
|
+
}
|
|
14125
|
+
context.putImageData(new ImageData(new Uint8ClampedArray(imageData.data), imageData.width, imageData.height), 0, 0);
|
|
14126
|
+
return canvas;
|
|
14127
|
+
}
|
|
13226
14128
|
function parsePhotoshopHeader(bytes) {
|
|
13227
14129
|
if (bytes.length < 26) {
|
|
13228
14130
|
return { valid: false, error: "\u6587\u4EF6\u592A\u77ED\uFF0C\u65E0\u6CD5\u8BFB\u53D6 PSD/PSB \u5934\u4FE1\u606F\u3002" };
|
|
@@ -13251,19 +14153,6 @@ function parsePhotoshopHeader(bytes) {
|
|
|
13251
14153
|
colorMode: view3.getUint16(24, false)
|
|
13252
14154
|
};
|
|
13253
14155
|
}
|
|
13254
|
-
function photoshopColorMode(mode) {
|
|
13255
|
-
const modes = {
|
|
13256
|
-
0: "Bitmap",
|
|
13257
|
-
1: "Grayscale",
|
|
13258
|
-
2: "Indexed",
|
|
13259
|
-
3: "RGB",
|
|
13260
|
-
4: "CMYK",
|
|
13261
|
-
7: "Multichannel",
|
|
13262
|
-
8: "Duotone",
|
|
13263
|
-
9: "Lab"
|
|
13264
|
-
};
|
|
13265
|
-
return modes[mode] ? `${modes[mode]} (${mode})` : `\u672A\u77E5 (${mode})`;
|
|
13266
|
-
}
|
|
13267
14156
|
function createSqlitePreview(bytes) {
|
|
13268
14157
|
const preview = document.createElement("div");
|
|
13269
14158
|
preview.className = "ofv-sqlite-preview";
|