@open-file-viewer/core 0.1.5 → 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 +1793 -181
- 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 +1793 -181
- package/dist/index.js.map +1 -1
- package/dist/style.css +218 -33
- package/package.json +12 -6
- package/LICENSE +0 -21
package/dist/index.cjs
CHANGED
|
@@ -301,6 +301,9 @@ var extensionMimeMap = {
|
|
|
301
301
|
skp: "application/vnd.sketchup.skp",
|
|
302
302
|
sldprt: "application/sldworks",
|
|
303
303
|
sldasm: "application/sldworks",
|
|
304
|
+
gds: "application/vnd.gds",
|
|
305
|
+
oas: "application/vnd.oasis.layout",
|
|
306
|
+
oasis: "application/vnd.oasis.layout",
|
|
304
307
|
ttf: "font/ttf",
|
|
305
308
|
otf: "font/otf",
|
|
306
309
|
woff: "font/woff",
|
|
@@ -321,7 +324,7 @@ var extensionMimeMap = {
|
|
|
321
324
|
};
|
|
322
325
|
async function normalizeFile(source, fileName, mimeType) {
|
|
323
326
|
if (typeof source === "string") {
|
|
324
|
-
const name2 = fileName || source
|
|
327
|
+
const name2 = fileName || getFileNameFromUrl(source) || "remote-file";
|
|
325
328
|
const extension2 = getExtension(name2);
|
|
326
329
|
return {
|
|
327
330
|
source,
|
|
@@ -366,6 +369,17 @@ async function normalizeFile(source, fileName, mimeType) {
|
|
|
366
369
|
blob
|
|
367
370
|
};
|
|
368
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
|
+
}
|
|
369
383
|
function getExtension(name) {
|
|
370
384
|
const clean = name.split("?")[0]?.split("#")[0] || "";
|
|
371
385
|
const index = clean.lastIndexOf(".");
|
|
@@ -1805,6 +1819,7 @@ function imagePlugin() {
|
|
|
1805
1819
|
let dragStartY = 0;
|
|
1806
1820
|
let startOffsetX = 0;
|
|
1807
1821
|
let startOffsetY = 0;
|
|
1822
|
+
let activePointerId = null;
|
|
1808
1823
|
const zoomLabel = document.createElement("span");
|
|
1809
1824
|
zoomLabel.className = "ofv-image-zoom";
|
|
1810
1825
|
const updateTransform = () => {
|
|
@@ -1852,29 +1867,61 @@ function imagePlugin() {
|
|
|
1852
1867
|
if (event.button !== 0) {
|
|
1853
1868
|
return;
|
|
1854
1869
|
}
|
|
1870
|
+
if (activePointerId !== null && activePointerId !== event.pointerId) {
|
|
1871
|
+
finishDrag(activePointerId);
|
|
1872
|
+
}
|
|
1855
1873
|
dragging = true;
|
|
1874
|
+
activePointerId = event.pointerId;
|
|
1856
1875
|
dragStartX = event.clientX;
|
|
1857
1876
|
dragStartY = event.clientY;
|
|
1858
1877
|
startOffsetX = offsetX;
|
|
1859
1878
|
startOffsetY = offsetY;
|
|
1860
1879
|
stage.classList.add("is-dragging");
|
|
1861
|
-
|
|
1880
|
+
try {
|
|
1881
|
+
stage.setPointerCapture(event.pointerId);
|
|
1882
|
+
} catch {
|
|
1883
|
+
}
|
|
1862
1884
|
};
|
|
1863
1885
|
const onPointerMove = (event) => {
|
|
1864
|
-
if (!dragging) {
|
|
1886
|
+
if (!dragging || event.pointerId !== activePointerId) {
|
|
1865
1887
|
return;
|
|
1866
1888
|
}
|
|
1867
1889
|
offsetX = startOffsetX + event.clientX - dragStartX;
|
|
1868
1890
|
offsetY = startOffsetY + event.clientY - dragStartY;
|
|
1869
1891
|
updateTransform();
|
|
1870
1892
|
};
|
|
1871
|
-
const
|
|
1893
|
+
const finishDrag = (pointerId) => {
|
|
1894
|
+
const captureId = pointerId ?? activePointerId;
|
|
1872
1895
|
dragging = false;
|
|
1896
|
+
activePointerId = null;
|
|
1873
1897
|
stage.classList.remove("is-dragging");
|
|
1874
|
-
if (
|
|
1875
|
-
|
|
1898
|
+
if (captureId !== null && captureId !== void 0) {
|
|
1899
|
+
try {
|
|
1900
|
+
if (stage.hasPointerCapture(captureId)) {
|
|
1901
|
+
stage.releasePointerCapture(captureId);
|
|
1902
|
+
}
|
|
1903
|
+
} catch {
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
};
|
|
1907
|
+
const onPointerUp = (event) => {
|
|
1908
|
+
if (event.pointerId === activePointerId) {
|
|
1909
|
+
finishDrag(event.pointerId);
|
|
1910
|
+
}
|
|
1911
|
+
};
|
|
1912
|
+
const onLostPointerCapture = (event) => {
|
|
1913
|
+
if (event.pointerId === activePointerId) {
|
|
1914
|
+
finishDrag(null);
|
|
1915
|
+
}
|
|
1916
|
+
};
|
|
1917
|
+
const onPointerLeave = (event) => {
|
|
1918
|
+
if (event.pointerId === activePointerId && event.buttons === 0) {
|
|
1919
|
+
finishDrag(event.pointerId);
|
|
1876
1920
|
}
|
|
1877
1921
|
};
|
|
1922
|
+
const onWindowBlur = () => {
|
|
1923
|
+
finishDrag();
|
|
1924
|
+
};
|
|
1878
1925
|
const onWheel = (event) => {
|
|
1879
1926
|
if (!event.ctrlKey && !event.metaKey) {
|
|
1880
1927
|
return;
|
|
@@ -1886,8 +1933,11 @@ function imagePlugin() {
|
|
|
1886
1933
|
stage.addEventListener("pointermove", onPointerMove);
|
|
1887
1934
|
stage.addEventListener("pointerup", onPointerUp);
|
|
1888
1935
|
stage.addEventListener("pointercancel", onPointerUp);
|
|
1936
|
+
stage.addEventListener("lostpointercapture", onLostPointerCapture);
|
|
1937
|
+
stage.addEventListener("pointerleave", onPointerLeave);
|
|
1889
1938
|
stage.addEventListener("wheel", onWheel, { passive: false });
|
|
1890
1939
|
image.addEventListener("error", showImageFallback);
|
|
1940
|
+
window.addEventListener("blur", onWindowBlur);
|
|
1891
1941
|
stage.append(image);
|
|
1892
1942
|
wrapper.append(...showInlineControls ? [controls, stage, infoBar] : [stage, infoBar]);
|
|
1893
1943
|
ctx.viewport.append(wrapper);
|
|
@@ -1934,8 +1984,12 @@ function imagePlugin() {
|
|
|
1934
1984
|
stage.removeEventListener("pointermove", onPointerMove);
|
|
1935
1985
|
stage.removeEventListener("pointerup", onPointerUp);
|
|
1936
1986
|
stage.removeEventListener("pointercancel", onPointerUp);
|
|
1987
|
+
stage.removeEventListener("lostpointercapture", onLostPointerCapture);
|
|
1988
|
+
stage.removeEventListener("pointerleave", onPointerLeave);
|
|
1937
1989
|
stage.removeEventListener("wheel", onWheel);
|
|
1938
1990
|
image.removeEventListener("error", showImageFallback);
|
|
1991
|
+
window.removeEventListener("blur", onWindowBlur);
|
|
1992
|
+
finishDrag();
|
|
1939
1993
|
wrapper.remove();
|
|
1940
1994
|
if (convertedBlob) {
|
|
1941
1995
|
URL.revokeObjectURL(url);
|
|
@@ -4084,6 +4138,38 @@ async function readText(source) {
|
|
|
4084
4138
|
return String(source);
|
|
4085
4139
|
}
|
|
4086
4140
|
|
|
4141
|
+
// src/plugins/encrypted.ts
|
|
4142
|
+
function createEncryptedFallback(file, url, copy = {}) {
|
|
4143
|
+
const fallback = document.createElement("div");
|
|
4144
|
+
fallback.className = "ofv-fallback ofv-encrypted";
|
|
4145
|
+
const title = document.createElement("strong");
|
|
4146
|
+
title.textContent = copy.title || "\u6587\u4EF6\u5DF2\u52A0\u5BC6\uFF0C\u65E0\u6CD5\u5728\u7EBF\u9884\u89C8";
|
|
4147
|
+
const message = document.createElement("span");
|
|
4148
|
+
message.textContent = copy.message || "\u8BF7\u4E0B\u8F7D\u540E\u5728\u672C\u5730\u8F93\u5165\u5BC6\u7801\u6253\u5F00\uFF0C\u6216\u4E0A\u4F20\u89E3\u5BC6\u540E\u7684\u6587\u4EF6\u3002";
|
|
4149
|
+
const meta = document.createElement("dl");
|
|
4150
|
+
meta.className = "ofv-fallback-meta ofv-encrypted-meta";
|
|
4151
|
+
appendEncryptedMeta(meta, "\u6587\u4EF6", file.name || "\u672A\u547D\u540D\u6587\u4EF6");
|
|
4152
|
+
appendEncryptedMeta(meta, "\u683C\u5F0F", file.extension ? `.${file.extension}` : file.mimeType || "\u672A\u77E5");
|
|
4153
|
+
const download = document.createElement("a");
|
|
4154
|
+
download.href = url;
|
|
4155
|
+
download.download = file.name;
|
|
4156
|
+
download.textContent = copy.action || "\u4E0B\u8F7D\u6587\u4EF6";
|
|
4157
|
+
fallback.append(title, message, meta, download);
|
|
4158
|
+
return fallback;
|
|
4159
|
+
}
|
|
4160
|
+
function isEncryptedError(error) {
|
|
4161
|
+
const message = error instanceof Error ? error.message : String(error || "");
|
|
4162
|
+
const name = typeof error === "object" && error !== null && "name" in error ? String(error.name) : "";
|
|
4163
|
+
return /\b(password|encrypted|encrypt|protected|decrypt|permission|加密|密码|受保护)\b/i.test(`${name} ${message}`);
|
|
4164
|
+
}
|
|
4165
|
+
function appendEncryptedMeta(parent, label, value) {
|
|
4166
|
+
const key = document.createElement("dt");
|
|
4167
|
+
key.textContent = label;
|
|
4168
|
+
const content = document.createElement("dd");
|
|
4169
|
+
content.textContent = value;
|
|
4170
|
+
parent.append(key, content);
|
|
4171
|
+
}
|
|
4172
|
+
|
|
4087
4173
|
// src/plugins/pdf.ts
|
|
4088
4174
|
function multiplyMatrices(m1, m2) {
|
|
4089
4175
|
return [
|
|
@@ -4125,7 +4211,11 @@ function pdfPlugin(options = {}) {
|
|
|
4125
4211
|
const doc = await documentTask.promise.catch((error) => {
|
|
4126
4212
|
viewer.remove();
|
|
4127
4213
|
ctx.viewport.classList.add("ofv-center");
|
|
4128
|
-
const fallback =
|
|
4214
|
+
const fallback = isEncryptedError(error) ? createEncryptedFallback(ctx.file, url, {
|
|
4215
|
+
title: "PDF \u5DF2\u52A0\u5BC6\uFF0C\u65E0\u6CD5\u5728\u7EBF\u9884\u89C8",
|
|
4216
|
+
message: "\u8BF7\u4E0B\u8F7D\u540E\u4F7F\u7528\u5BC6\u7801\u6253\u5F00\uFF0C\u6216\u4E0A\u4F20\u89E3\u5BC6\u540E\u7684 PDF \u6587\u4EF6\u3002",
|
|
4217
|
+
action: "\u4E0B\u8F7D PDF"
|
|
4218
|
+
}) : createPdfFallback(ctx.file.name, url, normalizePdfError(error));
|
|
4129
4219
|
ctx.viewport.append(fallback);
|
|
4130
4220
|
return void 0;
|
|
4131
4221
|
});
|
|
@@ -4415,9 +4505,6 @@ function normalizePdfError(error) {
|
|
|
4415
4505
|
const message = error instanceof Error ? error.message : String(error || "");
|
|
4416
4506
|
const name = typeof error === "object" && error !== null && "name" in error ? String(error.name) : "";
|
|
4417
4507
|
const lower = `${name} ${message}`.toLowerCase();
|
|
4418
|
-
if (lower.includes("password")) {
|
|
4419
|
-
return "\u8BE5 PDF \u53D7\u5BC6\u7801\u4FDD\u62A4\uFF0C\u5F53\u524D\u65E0\u6CD5\u5728\u6D4F\u89C8\u5668\u5185\u76F4\u63A5\u9884\u89C8\u3002";
|
|
4420
|
-
}
|
|
4421
4508
|
if (lower.includes("invalid") || lower.includes("missing") || lower.includes("corrupt")) {
|
|
4422
4509
|
return "\u8BE5 PDF \u6587\u4EF6\u53EF\u80FD\u5DF2\u635F\u574F\u6216\u683C\u5F0F\u65E0\u6548\u3002";
|
|
4423
4510
|
}
|
|
@@ -5345,7 +5432,13 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
5345
5432
|
const xlsx = await import("xlsx");
|
|
5346
5433
|
let workbook;
|
|
5347
5434
|
try {
|
|
5348
|
-
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 });
|
|
5349
5442
|
} catch (error) {
|
|
5350
5443
|
if (isLegacyOfficeBinary(extension)) {
|
|
5351
5444
|
renderLegacyOfficeBinary(panel, extension, arrayBuffer, `\u8868\u683C\u89E3\u6790\u5931\u8D25\uFF1A${normalizeOfficeError(error)}`);
|
|
@@ -5373,7 +5466,7 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
5373
5466
|
const heading = document.createElement("h3");
|
|
5374
5467
|
heading.textContent = sheetName;
|
|
5375
5468
|
const sheet = workbook.Sheets[sheetName];
|
|
5376
|
-
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);
|
|
5377
5470
|
const rowCount = range.e.r - range.s.r + 1;
|
|
5378
5471
|
const columnCount = range.e.c - range.s.c + 1;
|
|
5379
5472
|
const formulaRows = collectFormulaRows(sheet, range, xlsx.utils.encode_cell);
|
|
@@ -5383,6 +5476,7 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
5383
5476
|
const tableWrapper = document.createElement("div");
|
|
5384
5477
|
tableWrapper.className = "ofv-table-scroll";
|
|
5385
5478
|
const viewport = createSheetViewport(rowCount, columnCount);
|
|
5479
|
+
const columnSizing = { widths: /* @__PURE__ */ new Map() };
|
|
5386
5480
|
const windowControls = createSheetWindowControls(viewport, () => renderTableWindow());
|
|
5387
5481
|
const renderTableWindow = () => {
|
|
5388
5482
|
tableWrapper.replaceChildren(
|
|
@@ -5392,7 +5486,9 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
5392
5486
|
sheetIndex,
|
|
5393
5487
|
viewport,
|
|
5394
5488
|
xlsx.utils.encode_cell,
|
|
5395
|
-
xlsx.utils.format_cell
|
|
5489
|
+
xlsx.utils.format_cell,
|
|
5490
|
+
columnSizing,
|
|
5491
|
+
renderTableWindow
|
|
5396
5492
|
)
|
|
5397
5493
|
);
|
|
5398
5494
|
windowControls?.update();
|
|
@@ -5447,6 +5543,10 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
5447
5543
|
}
|
|
5448
5544
|
}
|
|
5449
5545
|
function renderSheetFallback(panel, extension, detail) {
|
|
5546
|
+
if (isEncryptedText(detail)) {
|
|
5547
|
+
renderEncryptedOfficeByFileInfo(panel, `.${extension || "sheet"}`, "Office \u6587\u4EF6\u5DF2\u52A0\u5BC6\uFF0C\u65E0\u6CD5\u5728\u7EBF\u9884\u89C8");
|
|
5548
|
+
return;
|
|
5549
|
+
}
|
|
5450
5550
|
const section = createSection("\u8868\u683C\u89E3\u6790\u5931\u8D25");
|
|
5451
5551
|
const title = document.createElement("p");
|
|
5452
5552
|
title.textContent = `.${extension || "sheet"} \u6587\u4EF6\u65E0\u6CD5\u89E3\u6790\u4E3A\u53EF\u9884\u89C8\u8868\u683C\u3002`;
|
|
@@ -5457,6 +5557,17 @@ function renderSheetFallback(panel, extension, detail) {
|
|
|
5457
5557
|
section.append(title, meta, support);
|
|
5458
5558
|
panel.append(section);
|
|
5459
5559
|
}
|
|
5560
|
+
function renderEncryptedOfficeByFileInfo(panel, fileLabel, title) {
|
|
5561
|
+
const section = createSection(title);
|
|
5562
|
+
section.classList.add("ofv-encrypted");
|
|
5563
|
+
const message = document.createElement("p");
|
|
5564
|
+
message.textContent = `${fileLabel} \u53EF\u80FD\u5DF2\u52A0\u5BC6\u6216\u53D7\u4FDD\u62A4\u3002\u8BF7\u4E0B\u8F7D\u540E\u4F7F\u7528 Office/WPS \u8F93\u5165\u5BC6\u7801\u6253\u5F00\uFF0C\u6216\u4E0A\u4F20\u89E3\u5BC6\u540E\u7684\u6587\u4EF6\u3002`;
|
|
5565
|
+
section.append(message);
|
|
5566
|
+
panel.append(section);
|
|
5567
|
+
}
|
|
5568
|
+
function isEncryptedText(value) {
|
|
5569
|
+
return /\b(password|encrypted|encrypt|protected|decrypt|permission|加密|密码|受保护)\b/i.test(value);
|
|
5570
|
+
}
|
|
5460
5571
|
function renderFlatOds(panel, xml) {
|
|
5461
5572
|
const sheets = parseFlatOds(xml);
|
|
5462
5573
|
renderParsedSheets(panel, sheets, "FODS \u6587\u4EF6\u672A\u89E3\u6790\u5230\u8868\u683C\u3002");
|
|
@@ -5487,9 +5598,10 @@ function renderParsedSheets(panel, sheets, emptyMessage) {
|
|
|
5487
5598
|
const tableWrapper = document.createElement("div");
|
|
5488
5599
|
tableWrapper.className = "ofv-table-scroll";
|
|
5489
5600
|
const viewport = createSheetViewport(rowCount, columnCount);
|
|
5601
|
+
const columnSizing = { widths: /* @__PURE__ */ new Map() };
|
|
5490
5602
|
const windowControls = createSheetWindowControls(viewport, () => renderTableWindow());
|
|
5491
5603
|
const renderTableWindow = () => {
|
|
5492
|
-
tableWrapper.replaceChildren(createParsedSheetTable(sheet, sheetIndex, viewport));
|
|
5604
|
+
tableWrapper.replaceChildren(createParsedSheetTable(sheet, sheetIndex, viewport, columnSizing, renderTableWindow));
|
|
5493
5605
|
windowControls?.update();
|
|
5494
5606
|
};
|
|
5495
5607
|
content.append(heading, summary);
|
|
@@ -5716,6 +5828,45 @@ function chartText(element) {
|
|
|
5716
5828
|
function chartStringValues(element) {
|
|
5717
5829
|
return Array.from(element.querySelectorAll("*")).filter((item) => item.localName === "v" || item.localName === "t").map((item) => item.textContent?.trim() || "").filter(Boolean);
|
|
5718
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
|
+
}
|
|
5719
5870
|
function createSheetViewport(rowCount, columnCount) {
|
|
5720
5871
|
return {
|
|
5721
5872
|
rowStart: 0,
|
|
@@ -5778,42 +5929,245 @@ function createWindowButton(label, onClick) {
|
|
|
5778
5929
|
function maxStart(total, size) {
|
|
5779
5930
|
return Math.max(0, total - size);
|
|
5780
5931
|
}
|
|
5781
|
-
function createWorkbookSheetTable(sheet, range, sheetIndex, viewport, encodeCell, formatCell) {
|
|
5932
|
+
function createWorkbookSheetTable(sheet, range, sheetIndex, viewport, encodeCell, formatCell, columnSizing, rerender) {
|
|
5782
5933
|
const table = document.createElement("table");
|
|
5783
5934
|
table.id = `ofv-sheet-${sheetIndex + 1}`;
|
|
5935
|
+
table.className = "ofv-workbook-table";
|
|
5784
5936
|
const rowEnd = Math.min(range.s.r + viewport.rowStart + SHEET_WINDOW_ROWS - 1, range.e.r);
|
|
5785
5937
|
const columnEnd = Math.min(range.s.c + viewport.columnStart + SHEET_WINDOW_COLUMNS - 1, range.e.c);
|
|
5786
|
-
|
|
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) {
|
|
5787
5954
|
const row = document.createElement("tr");
|
|
5788
|
-
|
|
5789
|
-
|
|
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) {
|
|
5790
5960
|
const address = encodeCell({ r: rowIndex, c: columnIndex });
|
|
5791
|
-
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");
|
|
5792
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
|
+
}
|
|
5793
5982
|
const text = sourceCell ? formatCell(sourceCell) : "";
|
|
5794
5983
|
cell.textContent = text;
|
|
5795
5984
|
if (text) {
|
|
5796
5985
|
cell.title = text;
|
|
5797
5986
|
}
|
|
5987
|
+
applyWorkbookCellStyle(cell, sourceCell);
|
|
5798
5988
|
if (sourceCell?.f) {
|
|
5799
5989
|
cell.classList.add("ofv-cell-formula");
|
|
5800
5990
|
cell.title = `=${sourceCell.f}`;
|
|
5801
5991
|
}
|
|
5992
|
+
if (text.includes("\n")) {
|
|
5993
|
+
cell.classList.add("ofv-cell-multiline");
|
|
5994
|
+
}
|
|
5995
|
+
appendColumnResizeHandle(cell, columnIndex, columnSizing);
|
|
5802
5996
|
row.append(cell);
|
|
5803
5997
|
}
|
|
5804
5998
|
table.append(row);
|
|
5805
5999
|
}
|
|
5806
6000
|
return table;
|
|
5807
6001
|
}
|
|
5808
|
-
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) {
|
|
5809
6151
|
const table = document.createElement("table");
|
|
5810
6152
|
table.id = `ofv-sheet-${sheetIndex + 1}`;
|
|
5811
6153
|
const formulaMap = new Map(sheet.formulas.map((item) => [item.address, item.formula]));
|
|
5812
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);
|
|
5813
6168
|
for (let rowIndex = viewport.rowStart; rowIndex < rowEnd; rowIndex += 1) {
|
|
5814
6169
|
const sourceRow = sheet.rows[rowIndex] || [];
|
|
5815
6170
|
const row = document.createElement("tr");
|
|
5816
|
-
const columnEnd = Math.min(viewport.columnStart + SHEET_WINDOW_COLUMNS, viewport.columnCount);
|
|
5817
6171
|
for (let columnIndex = viewport.columnStart; columnIndex < columnEnd; columnIndex += 1) {
|
|
5818
6172
|
const value = sourceRow[columnIndex] || "";
|
|
5819
6173
|
const cell = document.createElement(rowIndex === 0 ? "th" : "td");
|
|
@@ -5828,6 +6182,10 @@ function createParsedSheetTable(sheet, sheetIndex, viewport) {
|
|
|
5828
6182
|
cell.classList.add("ofv-cell-formula");
|
|
5829
6183
|
cell.title = formula;
|
|
5830
6184
|
}
|
|
6185
|
+
if (value.includes("\n")) {
|
|
6186
|
+
cell.classList.add("ofv-cell-multiline");
|
|
6187
|
+
}
|
|
6188
|
+
appendColumnResizeHandle(cell, columnIndex, columnSizing);
|
|
5831
6189
|
row.append(cell);
|
|
5832
6190
|
}
|
|
5833
6191
|
table.append(row);
|
|
@@ -6765,7 +7123,7 @@ function ofdPlugin() {
|
|
|
6765
7123
|
try {
|
|
6766
7124
|
zip = await import_jszip4.default.loadAsync(await readArrayBuffer(ctx.file));
|
|
6767
7125
|
} catch (error) {
|
|
6768
|
-
panel.append(
|
|
7126
|
+
panel.append(createOfdFailure(ctx.file, url, error));
|
|
6769
7127
|
return {
|
|
6770
7128
|
destroy() {
|
|
6771
7129
|
panel.remove();
|
|
@@ -6782,7 +7140,7 @@ function ofdPlugin() {
|
|
|
6782
7140
|
textFragments.push(...matches);
|
|
6783
7141
|
}
|
|
6784
7142
|
} catch (error) {
|
|
6785
|
-
panel.append(
|
|
7143
|
+
panel.append(createOfdFailure(ctx.file, url, error));
|
|
6786
7144
|
return {
|
|
6787
7145
|
destroy() {
|
|
6788
7146
|
panel.remove();
|
|
@@ -7407,6 +7765,16 @@ function finiteNumber2(value, fallback) {
|
|
|
7407
7765
|
function formatOfdCssNumber(value) {
|
|
7408
7766
|
return Number.isInteger(value) ? String(value) : value.toFixed(3).replace(/0+$/, "").replace(/\.$/, "");
|
|
7409
7767
|
}
|
|
7768
|
+
function createOfdFailure(file, url, error) {
|
|
7769
|
+
if (isEncryptedError(error)) {
|
|
7770
|
+
return createEncryptedFallback(file, url, {
|
|
7771
|
+
title: "OFD \u6587\u4EF6\u5DF2\u52A0\u5BC6\uFF0C\u65E0\u6CD5\u5728\u7EBF\u9884\u89C8",
|
|
7772
|
+
message: "\u8BF7\u4E0B\u8F7D\u540E\u4F7F\u7528\u672C\u5730 OFD \u9605\u8BFB\u5668\u8F93\u5165\u5BC6\u7801\u6253\u5F00\uFF0C\u6216\u4E0A\u4F20\u89E3\u5BC6\u540E\u7684 OFD \u6587\u4EF6\u3002",
|
|
7773
|
+
action: "\u4E0B\u8F7D OFD"
|
|
7774
|
+
});
|
|
7775
|
+
}
|
|
7776
|
+
return createOfdFallback(file.name, url, normalizeOfdError(error));
|
|
7777
|
+
}
|
|
7410
7778
|
function createOfdFallback(fileName, url, detail) {
|
|
7411
7779
|
const fallback = document.createElement("div");
|
|
7412
7780
|
fallback.className = "ofv-fallback";
|
|
@@ -7475,7 +7843,9 @@ function archivePlugin() {
|
|
|
7475
7843
|
try {
|
|
7476
7844
|
if (ext === "zip") {
|
|
7477
7845
|
try {
|
|
7478
|
-
const zip = await import_jszip5.default.loadAsync(await readArrayBuffer(ctx.file)
|
|
7846
|
+
const zip = await import_jszip5.default.loadAsync(await readArrayBuffer(ctx.file), {
|
|
7847
|
+
decodeFileName: decodeZipFileName
|
|
7848
|
+
});
|
|
7479
7849
|
archiveEntries = Object.values(zip.files).map((entry) => ({
|
|
7480
7850
|
name: entry.name,
|
|
7481
7851
|
unsafeName: entry.unsafeOriginalName,
|
|
@@ -7484,7 +7854,7 @@ function archivePlugin() {
|
|
|
7484
7854
|
read: () => entry.async("arraybuffer")
|
|
7485
7855
|
}));
|
|
7486
7856
|
} catch (zipErr) {
|
|
7487
|
-
if (
|
|
7857
|
+
if (isEncryptedError(zipErr)) {
|
|
7488
7858
|
isEncrypted = true;
|
|
7489
7859
|
} else {
|
|
7490
7860
|
throw zipErr;
|
|
@@ -7517,17 +7887,11 @@ function archivePlugin() {
|
|
|
7517
7887
|
parseError = `\u538B\u7F29\u5305\u89E3\u6790\u5931\u8D25\uFF1A${err.message || err}`;
|
|
7518
7888
|
}
|
|
7519
7889
|
if (isEncrypted) {
|
|
7520
|
-
const fallback =
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
meta.textContent = "\u4E3A\u4E86\u60A8\u7684\u6570\u636E\u5B89\u5168\uFF0C\u672C\u9884\u89C8\u5668\u4E0D\u652F\u6301\u76F4\u63A5\u5728\u7EBF\u89E3\u5BC6\u3002\u8BF7\u4E0B\u8F7D\u6587\u4EF6\u540E\u5728\u672C\u5730\u8F93\u5165\u5BC6\u7801\u89E3\u538B\u3002";
|
|
7526
|
-
const download = document.createElement("a");
|
|
7527
|
-
download.href = url;
|
|
7528
|
-
download.download = ctx.file.name;
|
|
7529
|
-
download.textContent = "\u4E0B\u8F7D\u6587\u4EF6";
|
|
7530
|
-
fallback.append(title, meta, download);
|
|
7890
|
+
const fallback = createEncryptedFallback(ctx.file, url, {
|
|
7891
|
+
title: "\u538B\u7F29\u5305\u5DF2\u52A0\u5BC6\uFF0C\u65E0\u6CD5\u5728\u7EBF\u9884\u89C8",
|
|
7892
|
+
message: "\u8BF7\u4E0B\u8F7D\u540E\u5728\u672C\u5730\u8F93\u5165\u5BC6\u7801\u89E3\u538B\uFF0C\u6216\u4E0A\u4F20\u89E3\u5BC6\u540E\u7684\u538B\u7F29\u5305\u3002",
|
|
7893
|
+
action: "\u4E0B\u8F7D\u538B\u7F29\u5305"
|
|
7894
|
+
});
|
|
7531
7895
|
panel.append(fallback);
|
|
7532
7896
|
ctx.viewport.classList.add("ofv-center");
|
|
7533
7897
|
return {
|
|
@@ -7776,6 +8140,28 @@ async function findSubPreviewPlugin(plugins, file) {
|
|
|
7776
8140
|
}
|
|
7777
8141
|
return fallbackPlugin();
|
|
7778
8142
|
}
|
|
8143
|
+
function decodeZipFileName(bytes) {
|
|
8144
|
+
const data = Array.isArray(bytes) ? Uint8Array.from(bytes.map((value) => value.charCodeAt(0) & 255)) : bytes instanceof Uint8Array ? bytes : Uint8Array.from(bytes);
|
|
8145
|
+
const utf8 = decodeZipNameWith(data, "utf-8", true);
|
|
8146
|
+
if (utf8 && !looksMojibake(utf8)) {
|
|
8147
|
+
return utf8;
|
|
8148
|
+
}
|
|
8149
|
+
const gb18030 = decodeZipNameWith(data, "gb18030", false) || decodeZipNameWith(data, "gbk", false);
|
|
8150
|
+
if (gb18030 && !looksMojibake(gb18030)) {
|
|
8151
|
+
return gb18030;
|
|
8152
|
+
}
|
|
8153
|
+
return utf8 || new TextDecoder("latin1").decode(data);
|
|
8154
|
+
}
|
|
8155
|
+
function decodeZipNameWith(bytes, encoding, fatal) {
|
|
8156
|
+
try {
|
|
8157
|
+
return new TextDecoder(encoding, { fatal }).decode(bytes);
|
|
8158
|
+
} catch {
|
|
8159
|
+
return void 0;
|
|
8160
|
+
}
|
|
8161
|
+
}
|
|
8162
|
+
function looksMojibake(value) {
|
|
8163
|
+
return /[\uFFFDÃÂÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞß]/.test(value);
|
|
8164
|
+
}
|
|
7779
8165
|
function createInlineError(titleText, detailText) {
|
|
7780
8166
|
const fallback = document.createElement("div");
|
|
7781
8167
|
fallback.className = "ofv-fallback";
|
|
@@ -8197,13 +8583,14 @@ function emailPlugin() {
|
|
|
8197
8583
|
} else {
|
|
8198
8584
|
const PostalMime = (await import("postal-mime")).default;
|
|
8199
8585
|
const parser = new PostalMime();
|
|
8200
|
-
let
|
|
8586
|
+
let rawSource = await readArrayBuffer(ctx.file);
|
|
8201
8587
|
if (ext === "mbox") {
|
|
8588
|
+
let rawText = await readTextFile(ctx.file);
|
|
8202
8589
|
const messages = splitMboxMessages(rawText);
|
|
8203
8590
|
mboxSummary = messages.map(summarizeMboxMessage);
|
|
8204
|
-
|
|
8591
|
+
rawSource = messages[0] || rawText;
|
|
8205
8592
|
}
|
|
8206
|
-
const parsed = await parser.parse(
|
|
8593
|
+
const parsed = await parser.parse(rawSource);
|
|
8207
8594
|
const from = parsed.from ? `${parsed.from.name || ""} <${parsed.from.address || ""}>`.trim() : "";
|
|
8208
8595
|
const to = Array.isArray(parsed.to) ? parsed.to.map((t) => `${t.name || ""} <${t.address || ""}>`.trim()).join("; ") : "";
|
|
8209
8596
|
const cc = Array.isArray(parsed.cc) ? parsed.cc.map((c) => `${c.name || ""} <${c.address || ""}>`.trim()).join("; ") : "";
|
|
@@ -8273,13 +8660,17 @@ function emailPlugin() {
|
|
|
8273
8660
|
const sanitizedHtml = sanitizeEmailHtml(html);
|
|
8274
8661
|
const iframe = document.createElement("iframe");
|
|
8275
8662
|
iframe.className = "ofv-email-body-iframe";
|
|
8276
|
-
iframe.setAttribute("sandbox", "allow-popups allow-popups-to-escape-sandbox");
|
|
8663
|
+
iframe.setAttribute("sandbox", "allow-same-origin allow-popups allow-popups-to-escape-sandbox");
|
|
8277
8664
|
iframe.style.cssText = "width: 100%; border: none; background: #fff; min-height: 200px;";
|
|
8278
|
-
|
|
8279
|
-
|
|
8665
|
+
let renderedHtmlBody = false;
|
|
8666
|
+
const renderHtmlBody = () => {
|
|
8667
|
+
if (renderedHtmlBody) {
|
|
8668
|
+
return;
|
|
8669
|
+
}
|
|
8280
8670
|
try {
|
|
8281
8671
|
const idoc = iframe.contentDocument || iframe.contentWindow?.document;
|
|
8282
8672
|
if (idoc) {
|
|
8673
|
+
renderedHtmlBody = true;
|
|
8283
8674
|
idoc.open();
|
|
8284
8675
|
idoc.write(`
|
|
8285
8676
|
<!doctype html>
|
|
@@ -8330,6 +8721,9 @@ function emailPlugin() {
|
|
|
8330
8721
|
console.error("Failed to write html body to email iframe:", err);
|
|
8331
8722
|
}
|
|
8332
8723
|
};
|
|
8724
|
+
iframe.addEventListener("load", renderHtmlBody, { once: true });
|
|
8725
|
+
bodySection.append(iframe);
|
|
8726
|
+
renderHtmlBody();
|
|
8333
8727
|
} else {
|
|
8334
8728
|
const pre = document.createElement("pre");
|
|
8335
8729
|
pre.className = "ofv-text-block";
|
|
@@ -9851,112 +10245,726 @@ function decodeDrawioDiagram(value) {
|
|
|
9851
10245
|
}
|
|
9852
10246
|
|
|
9853
10247
|
// src/plugins/cad.ts
|
|
9854
|
-
var
|
|
9855
|
-
|
|
9856
|
-
|
|
9857
|
-
|
|
9858
|
-
|
|
9859
|
-
|
|
9860
|
-
|
|
9861
|
-
|
|
9862
|
-
"
|
|
9863
|
-
|
|
9864
|
-
|
|
9865
|
-
"
|
|
9866
|
-
"
|
|
9867
|
-
"
|
|
9868
|
-
"
|
|
9869
|
-
"
|
|
9870
|
-
|
|
9871
|
-
|
|
9872
|
-
|
|
9873
|
-
|
|
9874
|
-
|
|
9875
|
-
|
|
9876
|
-
|
|
9877
|
-
|
|
9878
|
-
|
|
9879
|
-
|
|
9880
|
-
|
|
9881
|
-
|
|
9882
|
-
|
|
9883
|
-
|
|
9884
|
-
|
|
9885
|
-
|
|
9886
|
-
|
|
9887
|
-
|
|
9888
|
-
|
|
9889
|
-
|
|
9890
|
-
|
|
9891
|
-
|
|
9892
|
-
|
|
9893
|
-
|
|
9894
|
-
|
|
9895
|
-
|
|
9896
|
-
|
|
9897
|
-
|
|
9898
|
-
|
|
9899
|
-
|
|
9900
|
-
|
|
9901
|
-
|
|
9902
|
-
"model/vnd.3dm": "3dm",
|
|
9903
|
-
"application/vnd.sketchup.skp": "skp",
|
|
9904
|
-
"application/sldworks": "sldprt"
|
|
9905
|
-
};
|
|
9906
|
-
function cadPlugin() {
|
|
9907
|
-
return {
|
|
9908
|
-
name: "cad",
|
|
9909
|
-
match(file) {
|
|
9910
|
-
return cadExtensions.has(file.extension) || cadMimeTypes.has(file.mimeType);
|
|
9911
|
-
},
|
|
9912
|
-
async render(ctx) {
|
|
9913
|
-
const panel = createPanel("ofv-cad");
|
|
9914
|
-
ctx.viewport.append(panel);
|
|
9915
|
-
const extension = resolveFormat(ctx.file, cadMimeFormatMap);
|
|
9916
|
-
if (extension === "step" || extension === "stp") {
|
|
9917
|
-
renderStep(panel, await readTextFile(ctx.file), extension);
|
|
9918
|
-
return { destroy: () => panel.remove() };
|
|
9919
|
-
}
|
|
9920
|
-
if (extension === "iges" || extension === "igs") {
|
|
9921
|
-
renderIges(panel, await readTextFile(ctx.file), extension);
|
|
9922
|
-
return { destroy: () => panel.remove() };
|
|
9923
|
-
}
|
|
9924
|
-
if (extension === "ifc") {
|
|
9925
|
-
renderIfc(panel, await readTextFile(ctx.file));
|
|
9926
|
-
return { destroy: () => panel.remove() };
|
|
9927
|
-
}
|
|
9928
|
-
if (extension === "dwg" || extension === "dwf") {
|
|
9929
|
-
renderBinaryCad(panel, await readArrayBuffer(ctx.file), extension, ctx.file.name);
|
|
9930
|
-
return { destroy: () => panel.remove() };
|
|
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;
|
|
9931
10296
|
}
|
|
9932
|
-
|
|
9933
|
-
|
|
9934
|
-
|
|
9935
|
-
|
|
9936
|
-
|
|
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
|
+
};
|
|
9937
10311
|
}
|
|
9938
|
-
|
|
9939
|
-
|
|
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));
|
|
9940
10327
|
return {
|
|
9941
|
-
canCommand(command) {
|
|
9942
|
-
return viewer.canCommand(command);
|
|
9943
|
-
},
|
|
9944
|
-
command(command) {
|
|
9945
|
-
return viewer.command(command);
|
|
9946
|
-
},
|
|
9947
10328
|
destroy() {
|
|
9948
|
-
|
|
10329
|
+
URL.revokeObjectURL(thumbnailUrl);
|
|
10330
|
+
shell.remove();
|
|
9949
10331
|
}
|
|
9950
10332
|
};
|
|
9951
10333
|
}
|
|
9952
|
-
|
|
9953
|
-
|
|
9954
|
-
|
|
9955
|
-
|
|
9956
|
-
|
|
9957
|
-
|
|
9958
|
-
|
|
9959
|
-
|
|
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
|
|
10764
|
+
var cadExtensions = /* @__PURE__ */ new Set([
|
|
10765
|
+
"dxf",
|
|
10766
|
+
"dwg",
|
|
10767
|
+
"dwf",
|
|
10768
|
+
"step",
|
|
10769
|
+
"stp",
|
|
10770
|
+
"iges",
|
|
10771
|
+
"igs",
|
|
10772
|
+
"ifc",
|
|
10773
|
+
"sat",
|
|
10774
|
+
"sab",
|
|
10775
|
+
"x_t",
|
|
10776
|
+
"x_b",
|
|
10777
|
+
"3dm",
|
|
10778
|
+
"skp",
|
|
10779
|
+
"sldprt",
|
|
10780
|
+
"sldasm",
|
|
10781
|
+
"gds",
|
|
10782
|
+
"oas",
|
|
10783
|
+
"oasis"
|
|
10784
|
+
]);
|
|
10785
|
+
var cadMimeTypes = /* @__PURE__ */ new Set([
|
|
10786
|
+
"application/acad",
|
|
10787
|
+
"application/dxf",
|
|
10788
|
+
"application/x-dxf",
|
|
10789
|
+
"image/vnd.dxf",
|
|
10790
|
+
"model/vnd.dwf",
|
|
10791
|
+
"model/step",
|
|
10792
|
+
"application/step",
|
|
10793
|
+
"application/iges",
|
|
10794
|
+
"application/x-step",
|
|
10795
|
+
"application/sat",
|
|
10796
|
+
"application/sab",
|
|
10797
|
+
"application/x-parasolid",
|
|
10798
|
+
"model/vnd.3dm",
|
|
10799
|
+
"application/vnd.sketchup.skp",
|
|
10800
|
+
"application/sldworks",
|
|
10801
|
+
"application/vnd.gds",
|
|
10802
|
+
"application/x-gdsii",
|
|
10803
|
+
"application/vnd.oasis.layout",
|
|
10804
|
+
"application/x-oasis-layout"
|
|
10805
|
+
]);
|
|
10806
|
+
var cadMimeFormatMap = {
|
|
10807
|
+
"application/acad": "dwg",
|
|
10808
|
+
"application/dxf": "dxf",
|
|
10809
|
+
"application/x-dxf": "dxf",
|
|
10810
|
+
"image/vnd.dxf": "dxf",
|
|
10811
|
+
"model/vnd.dwf": "dwf",
|
|
10812
|
+
"model/step": "step",
|
|
10813
|
+
"application/step": "step",
|
|
10814
|
+
"application/iges": "iges",
|
|
10815
|
+
"application/x-step": "ifc",
|
|
10816
|
+
"application/sat": "sat",
|
|
10817
|
+
"application/sab": "sab",
|
|
10818
|
+
"application/x-parasolid": "x_t",
|
|
10819
|
+
"model/vnd.3dm": "3dm",
|
|
10820
|
+
"application/vnd.sketchup.skp": "skp",
|
|
10821
|
+
"application/sldworks": "sldprt",
|
|
10822
|
+
"application/vnd.gds": "gds",
|
|
10823
|
+
"application/x-gdsii": "gds",
|
|
10824
|
+
"application/vnd.oasis.layout": "oas",
|
|
10825
|
+
"application/x-oasis-layout": "oas"
|
|
10826
|
+
};
|
|
10827
|
+
function cadPlugin(options = {}) {
|
|
10828
|
+
return {
|
|
10829
|
+
name: "cad",
|
|
10830
|
+
match(file) {
|
|
10831
|
+
return cadExtensions.has(file.extension) || cadMimeTypes.has(file.mimeType);
|
|
10832
|
+
},
|
|
10833
|
+
async render(ctx) {
|
|
10834
|
+
const panel = createPanel("ofv-cad");
|
|
10835
|
+
ctx.viewport.append(panel);
|
|
10836
|
+
const extension = resolveFormat(ctx.file, cadMimeFormatMap);
|
|
10837
|
+
if (extension === "step" || extension === "stp") {
|
|
10838
|
+
renderStep(panel, await readTextFile(ctx.file), extension);
|
|
10839
|
+
return { destroy: () => panel.remove() };
|
|
10840
|
+
}
|
|
10841
|
+
if (extension === "iges" || extension === "igs") {
|
|
10842
|
+
renderIges(panel, await readTextFile(ctx.file), extension);
|
|
10843
|
+
return { destroy: () => panel.remove() };
|
|
10844
|
+
}
|
|
10845
|
+
if (extension === "ifc") {
|
|
10846
|
+
renderIfc(panel, await readTextFile(ctx.file));
|
|
10847
|
+
return { destroy: () => panel.remove() };
|
|
10848
|
+
}
|
|
10849
|
+
if (extension === "dwg" || extension === "dwf") {
|
|
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);
|
|
10908
|
+
return { destroy: () => panel.remove() };
|
|
10909
|
+
}
|
|
10910
|
+
if (extension === "gds") {
|
|
10911
|
+
const viewer2 = renderLayoutPreview(panel, parseGdsLayout(new Uint8Array(await readArrayBuffer(ctx.file)), ctx.file.name), ctx);
|
|
10912
|
+
return {
|
|
10913
|
+
canCommand(command) {
|
|
10914
|
+
return viewer2.canCommand(command);
|
|
10915
|
+
},
|
|
10916
|
+
command(command) {
|
|
10917
|
+
return viewer2.command(command);
|
|
10918
|
+
},
|
|
10919
|
+
destroy() {
|
|
10920
|
+
viewer2.destroy();
|
|
10921
|
+
panel.remove();
|
|
10922
|
+
}
|
|
10923
|
+
};
|
|
10924
|
+
}
|
|
10925
|
+
if (extension === "oas" || extension === "oasis") {
|
|
10926
|
+
const viewer2 = renderLayoutPreview(panel, parseOasisLayout(new Uint8Array(await readArrayBuffer(ctx.file)), ctx.file.name), ctx);
|
|
10927
|
+
return {
|
|
10928
|
+
canCommand(command) {
|
|
10929
|
+
return viewer2.canCommand(command);
|
|
10930
|
+
},
|
|
10931
|
+
command(command) {
|
|
10932
|
+
return viewer2.command(command);
|
|
10933
|
+
},
|
|
10934
|
+
destroy() {
|
|
10935
|
+
viewer2.destroy();
|
|
10936
|
+
panel.remove();
|
|
10937
|
+
}
|
|
10938
|
+
};
|
|
10939
|
+
}
|
|
10940
|
+
if (extension !== "dxf") {
|
|
10941
|
+
const section = createSection("CAD \u57FA\u7840\u9884\u89C8");
|
|
10942
|
+
section.append(`.${extension || "cad"} \u5DF2\u8BC6\u522B\u4E3A\u56FE\u7EB8/\u5DE5\u7A0B\u683C\u5F0F\uFF0C\u5F53\u524D\u524D\u7AEF\u63D2\u4EF6\u4F18\u5148\u6E32\u67D3 DXF\u3002\u8BE5\u683C\u5F0F\u5EFA\u8BAE\u63A5\u5165\u670D\u52A1\u7AEF\u8F6C\u6362\u6216 WASM \u4E13\u7528\u5F15\u64CE\u3002`);
|
|
10943
|
+
panel.append(section);
|
|
10944
|
+
return { destroy: () => panel.remove() };
|
|
10945
|
+
}
|
|
10946
|
+
const dxf = await readTextFile(ctx.file);
|
|
10947
|
+
const viewer = renderDxf(panel, dxf, ctx);
|
|
10948
|
+
return {
|
|
10949
|
+
canCommand(command) {
|
|
10950
|
+
return viewer.canCommand(command);
|
|
10951
|
+
},
|
|
10952
|
+
command(command) {
|
|
10953
|
+
return viewer.command(command);
|
|
10954
|
+
},
|
|
10955
|
+
destroy() {
|
|
10956
|
+
panel.remove();
|
|
10957
|
+
}
|
|
10958
|
+
};
|
|
10959
|
+
}
|
|
10960
|
+
};
|
|
10961
|
+
}
|
|
10962
|
+
function renderStep(panel, text, extension) {
|
|
10963
|
+
const records = parseStepRecords(text);
|
|
10964
|
+
const typeCounts = countBy(records.map((record) => record.type));
|
|
10965
|
+
const section = createSection(`${extension.toUpperCase()} \u7ED3\u6784\u9884\u89C8`);
|
|
10966
|
+
const note = document.createElement("p");
|
|
10967
|
+
note.textContent = "\u5F53\u524D\u7248\u672C\u63D0\u53D6 STEP \u6587\u672C\u5B9E\u4F53\u3001\u7C7B\u578B\u7EDF\u8BA1\u548C\u5173\u952E\u51E0\u4F55\u53C2\u6570\u3002\u7CBE\u786E B-Rep/\u66F2\u9762\u6E32\u67D3\u5EFA\u8BAE\u540E\u7EED\u63A5\u5165 CAD \u5185\u6838\u6216\u670D\u52A1\u7AEF\u8F6C\u6362\u3002";
|
|
9960
10968
|
const meta = document.createElement("div");
|
|
9961
10969
|
meta.className = "ofv-cad-summary";
|
|
9962
10970
|
appendMeta5(meta, "\u5B9E\u4F53", records.length);
|
|
@@ -10027,11 +11035,564 @@ function renderIfc(panel, text) {
|
|
|
10027
11035
|
section.append(table);
|
|
10028
11036
|
panel.append(section);
|
|
10029
11037
|
}
|
|
10030
|
-
|
|
10031
|
-
|
|
11038
|
+
var layoutPalette = [
|
|
11039
|
+
"#2563eb",
|
|
11040
|
+
"#dc2626",
|
|
11041
|
+
"#059669",
|
|
11042
|
+
"#7c3aed",
|
|
11043
|
+
"#d97706",
|
|
11044
|
+
"#0891b2",
|
|
11045
|
+
"#be123c",
|
|
11046
|
+
"#4f46e5",
|
|
11047
|
+
"#15803d",
|
|
11048
|
+
"#a16207"
|
|
11049
|
+
];
|
|
11050
|
+
var gdsRecordNames = {
|
|
11051
|
+
0: "HEADER",
|
|
11052
|
+
1: "BGNLIB",
|
|
11053
|
+
2: "LIBNAME",
|
|
11054
|
+
3: "UNITS",
|
|
11055
|
+
4: "ENDLIB",
|
|
11056
|
+
5: "BGNSTR",
|
|
11057
|
+
6: "STRNAME",
|
|
11058
|
+
7: "ENDSTR",
|
|
11059
|
+
8: "BOUNDARY",
|
|
11060
|
+
9: "PATH",
|
|
11061
|
+
10: "SREF",
|
|
11062
|
+
11: "AREF",
|
|
11063
|
+
12: "TEXT",
|
|
11064
|
+
13: "LAYER",
|
|
11065
|
+
14: "DATATYPE",
|
|
11066
|
+
15: "WIDTH",
|
|
11067
|
+
16: "XY",
|
|
11068
|
+
17: "ENDEL",
|
|
11069
|
+
18: "SNAME",
|
|
11070
|
+
22: "TEXTTYPE",
|
|
11071
|
+
25: "STRING",
|
|
11072
|
+
45: "BOX"
|
|
11073
|
+
};
|
|
11074
|
+
var oasisRecordNames = {
|
|
11075
|
+
0: "PAD",
|
|
11076
|
+
1: "START",
|
|
11077
|
+
2: "END",
|
|
11078
|
+
3: "CELLNAME",
|
|
11079
|
+
4: "CELLNAME-REF",
|
|
11080
|
+
5: "TEXTSTRING",
|
|
11081
|
+
6: "TEXTSTRING-REF",
|
|
11082
|
+
7: "PROPNAME",
|
|
11083
|
+
8: "PROPNAME-REF",
|
|
11084
|
+
9: "PROPSTRING",
|
|
11085
|
+
10: "PROPSTRING-REF",
|
|
11086
|
+
11: "LAYERNAME",
|
|
11087
|
+
12: "LAYERNAME-REF",
|
|
11088
|
+
13: "CELL",
|
|
11089
|
+
14: "XYABSOLUTE",
|
|
11090
|
+
15: "XYRELATIVE",
|
|
11091
|
+
16: "PLACEMENT",
|
|
11092
|
+
17: "PLACEMENT",
|
|
11093
|
+
18: "TEXT",
|
|
11094
|
+
19: "RECTANGLE",
|
|
11095
|
+
20: "POLYGON",
|
|
11096
|
+
21: "PATH",
|
|
11097
|
+
22: "TRAPEZOID",
|
|
11098
|
+
23: "TRAPEZOID",
|
|
11099
|
+
24: "TRAPEZOID",
|
|
11100
|
+
25: "CTRAPEZOID",
|
|
11101
|
+
26: "CIRCLE",
|
|
11102
|
+
27: "PROPERTY",
|
|
11103
|
+
28: "PROPERTY",
|
|
11104
|
+
29: "XNAME",
|
|
11105
|
+
30: "XNAME-REF",
|
|
11106
|
+
31: "XELEMENT",
|
|
11107
|
+
32: "XGEOMETRY",
|
|
11108
|
+
33: "CBLOCK"
|
|
11109
|
+
};
|
|
11110
|
+
function renderLayoutPreview(panel, data, ctx) {
|
|
11111
|
+
const section = createSection(`${data.format} \u7248\u56FE\u9884\u89C8`);
|
|
11112
|
+
const summary = document.createElement("div");
|
|
11113
|
+
summary.className = "ofv-cad-summary ofv-layout-summary";
|
|
11114
|
+
appendMeta5(summary, "\u6587\u4EF6", data.fileName);
|
|
11115
|
+
appendMeta5(summary, "\u683C\u5F0F", data.format);
|
|
11116
|
+
if (data.libraryName) {
|
|
11117
|
+
appendMeta5(summary, "\u5E93", data.libraryName);
|
|
11118
|
+
}
|
|
11119
|
+
if (data.version) {
|
|
11120
|
+
appendMeta5(summary, "\u7248\u672C", data.version);
|
|
11121
|
+
}
|
|
11122
|
+
if (data.unit) {
|
|
11123
|
+
appendMeta5(summary, "\u5355\u4F4D", data.unit);
|
|
11124
|
+
}
|
|
11125
|
+
appendMeta5(summary, "Cell", data.cells.length);
|
|
11126
|
+
appendMeta5(summary, "\u51E0\u4F55", data.shapes.length);
|
|
11127
|
+
appendMeta5(summary, "\u5F15\u7528", data.references.length);
|
|
11128
|
+
appendMeta5(summary, "\u6587\u5B57", data.labels.length);
|
|
11129
|
+
for (const [label, value] of data.metadata) {
|
|
11130
|
+
appendMeta5(summary, label, value);
|
|
11131
|
+
}
|
|
11132
|
+
section.append(summary);
|
|
11133
|
+
for (const noteText of [...data.notes, ...data.warnings]) {
|
|
11134
|
+
const note = document.createElement("p");
|
|
11135
|
+
note.className = data.warnings.includes(noteText) ? "ofv-layout-warning" : "ofv-layout-note";
|
|
11136
|
+
note.textContent = noteText;
|
|
11137
|
+
section.append(note);
|
|
11138
|
+
}
|
|
11139
|
+
const bounds = computeLayoutBounds(data.shapes, data.labels, data.references);
|
|
11140
|
+
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
11141
|
+
svg.setAttribute("class", "ofv-svg-stage ofv-layout-stage");
|
|
11142
|
+
let currentViewBox = { x: bounds.minX, y: bounds.minY, width: bounds.width, height: bounds.height };
|
|
11143
|
+
const initialViewBox = { ...currentViewBox };
|
|
11144
|
+
const applyViewBox = () => {
|
|
11145
|
+
svg.setAttribute("viewBox", `${currentViewBox.x} ${currentViewBox.y} ${currentViewBox.width} ${currentViewBox.height}`);
|
|
11146
|
+
};
|
|
11147
|
+
applyViewBox();
|
|
11148
|
+
if (data.shapes.length === 0) {
|
|
11149
|
+
const empty = document.createElementNS(svg.namespaceURI, "text");
|
|
11150
|
+
empty.setAttribute("x", String(bounds.minX + bounds.width * 0.5));
|
|
11151
|
+
empty.setAttribute("y", String(bounds.minY + bounds.height * 0.5));
|
|
11152
|
+
empty.setAttribute("text-anchor", "middle");
|
|
11153
|
+
empty.setAttribute("font-size", String(Math.max(bounds.width, bounds.height) / 34));
|
|
11154
|
+
empty.setAttribute("fill", "currentColor");
|
|
11155
|
+
empty.textContent = "\u5DF2\u8BC6\u522B\u7248\u56FE\u6587\u4EF6\uFF0C\u5F53\u524D\u6587\u4EF6\u672A\u89E3\u6790\u51FA\u53EF\u7ED8\u5236\u51E0\u4F55";
|
|
11156
|
+
svg.append(empty);
|
|
11157
|
+
}
|
|
11158
|
+
const layerIndex = new Map([...data.layers.keys()].sort((a, b) => a.localeCompare(b)).map((layer, index) => [layer, index]));
|
|
11159
|
+
for (const shape of data.shapes.slice(0, 6e3)) {
|
|
11160
|
+
const color = layoutPalette[(layerIndex.get(shape.layer) || 0) % layoutPalette.length];
|
|
11161
|
+
if (shape.kind === "path") {
|
|
11162
|
+
const polyline = document.createElementNS(svg.namespaceURI, "polyline");
|
|
11163
|
+
polyline.setAttribute("points", shape.points.map(([x, y]) => `${x},${-y}`).join(" "));
|
|
11164
|
+
polyline.setAttribute("fill", "none");
|
|
11165
|
+
polyline.setAttribute("stroke", color);
|
|
11166
|
+
polyline.setAttribute("stroke-width", String(Math.max(bounds.stroke, Math.abs(shape.width || 0))));
|
|
11167
|
+
polyline.setAttribute("stroke-linecap", "round");
|
|
11168
|
+
polyline.setAttribute("stroke-linejoin", "round");
|
|
11169
|
+
applyLayer(polyline, shape.layer);
|
|
11170
|
+
svg.append(polyline);
|
|
11171
|
+
continue;
|
|
11172
|
+
}
|
|
11173
|
+
const polygon = document.createElementNS(svg.namespaceURI, "polygon");
|
|
11174
|
+
polygon.setAttribute("points", shape.points.map(([x, y]) => `${x},${-y}`).join(" "));
|
|
11175
|
+
polygon.setAttribute("fill", color);
|
|
11176
|
+
polygon.setAttribute("fill-opacity", "0.18");
|
|
11177
|
+
polygon.setAttribute("stroke", color);
|
|
11178
|
+
polygon.setAttribute("stroke-width", String(bounds.stroke));
|
|
11179
|
+
polygon.setAttribute("vector-effect", "non-scaling-stroke");
|
|
11180
|
+
applyLayer(polygon, shape.layer);
|
|
11181
|
+
svg.append(polygon);
|
|
11182
|
+
}
|
|
11183
|
+
for (const label of data.labels.slice(0, 400)) {
|
|
11184
|
+
const text = document.createElementNS(svg.namespaceURI, "text");
|
|
11185
|
+
text.setAttribute("x", String(label.x));
|
|
11186
|
+
text.setAttribute("y", String(-label.y));
|
|
11187
|
+
text.setAttribute("font-size", String(Math.max(bounds.stroke * 12, Math.max(bounds.width, bounds.height) / 120)));
|
|
11188
|
+
text.setAttribute("fill", "currentColor");
|
|
11189
|
+
text.textContent = label.text;
|
|
11190
|
+
applyLayer(text, label.layer);
|
|
11191
|
+
svg.append(text);
|
|
11192
|
+
}
|
|
11193
|
+
const layers = [...data.layers.keys()].sort((a, b) => a.localeCompare(b));
|
|
11194
|
+
if (layers.length > 0) {
|
|
11195
|
+
section.append(createLayoutLayerControls(svg, layers, data.layers));
|
|
11196
|
+
}
|
|
11197
|
+
section.append(svg);
|
|
11198
|
+
if (data.cells.length > 0) {
|
|
11199
|
+
section.append(createLayoutCellList(data.cells, data.references));
|
|
11200
|
+
}
|
|
11201
|
+
panel.append(section);
|
|
11202
|
+
const updateToolbarZoom = () => ctx.toolbar?.setZoom(initialViewBox.width / currentViewBox.width);
|
|
11203
|
+
updateToolbarZoom();
|
|
11204
|
+
return {
|
|
11205
|
+
canCommand(command) {
|
|
11206
|
+
return command === "zoom-in" || command === "zoom-out" || command === "zoom-reset";
|
|
11207
|
+
},
|
|
11208
|
+
command(command) {
|
|
11209
|
+
if (command === "zoom-in" || command === "zoom-out") {
|
|
11210
|
+
const factor = command === "zoom-in" ? 0.82 : 1.18;
|
|
11211
|
+
const centerX = currentViewBox.x + currentViewBox.width / 2;
|
|
11212
|
+
const centerY = currentViewBox.y + currentViewBox.height / 2;
|
|
11213
|
+
currentViewBox.width *= factor;
|
|
11214
|
+
currentViewBox.height *= factor;
|
|
11215
|
+
currentViewBox.x = centerX - currentViewBox.width / 2;
|
|
11216
|
+
currentViewBox.y = centerY - currentViewBox.height / 2;
|
|
11217
|
+
applyViewBox();
|
|
11218
|
+
updateToolbarZoom();
|
|
11219
|
+
return true;
|
|
11220
|
+
}
|
|
11221
|
+
if (command === "zoom-reset") {
|
|
11222
|
+
currentViewBox = { ...initialViewBox };
|
|
11223
|
+
applyViewBox();
|
|
11224
|
+
updateToolbarZoom();
|
|
11225
|
+
return true;
|
|
11226
|
+
}
|
|
11227
|
+
return false;
|
|
11228
|
+
},
|
|
11229
|
+
destroy() {
|
|
11230
|
+
ctx.toolbar?.setZoom(void 0);
|
|
11231
|
+
}
|
|
11232
|
+
};
|
|
11233
|
+
}
|
|
11234
|
+
function parseGdsLayout(bytes, fileName) {
|
|
11235
|
+
const shapes = [];
|
|
11236
|
+
const labels = [];
|
|
11237
|
+
const references = [];
|
|
11238
|
+
const cells = [];
|
|
11239
|
+
const layers = /* @__PURE__ */ new Map();
|
|
11240
|
+
const recordCounts = /* @__PURE__ */ new Map();
|
|
11241
|
+
const warnings = [];
|
|
11242
|
+
let libraryName = "";
|
|
11243
|
+
let version = "";
|
|
11244
|
+
let unit = "";
|
|
11245
|
+
let offset = 0;
|
|
11246
|
+
let current = {};
|
|
11247
|
+
let currentKind = "";
|
|
11248
|
+
while (offset + 4 <= bytes.length) {
|
|
11249
|
+
const length = readUInt16(bytes, offset);
|
|
11250
|
+
const recordType = bytes[offset + 2];
|
|
11251
|
+
const data = bytes.slice(offset + 4, offset + length);
|
|
11252
|
+
const name = gdsRecordNames[recordType] || `0x${recordType.toString(16).padStart(2, "0")}`;
|
|
11253
|
+
recordCounts.set(name, (recordCounts.get(name) || 0) + 1);
|
|
11254
|
+
if (length < 4 || offset + length > bytes.length) {
|
|
11255
|
+
warnings.push(`GDS \u8BB0\u5F55\u5728 ${offset} \u5B57\u8282\u5904\u957F\u5EA6\u5F02\u5E38\uFF0C\u5DF2\u505C\u6B62\u89E3\u6790\u3002`);
|
|
11256
|
+
break;
|
|
11257
|
+
}
|
|
11258
|
+
if (recordType === 0 && data.length >= 2) {
|
|
11259
|
+
version = String(readUInt16(data, 0));
|
|
11260
|
+
} else if (recordType === 2) {
|
|
11261
|
+
libraryName = readGdsString(data);
|
|
11262
|
+
} else if (recordType === 3 && data.length >= 16) {
|
|
11263
|
+
unit = `${formatGdsReal(data, 0)} / ${formatGdsReal(data, 8)}`;
|
|
11264
|
+
} else if (recordType === 6) {
|
|
11265
|
+
cells.push(readGdsString(data));
|
|
11266
|
+
} else if (recordType === 8 || recordType === 9 || recordType === 45) {
|
|
11267
|
+
currentKind = recordType === 9 ? "path" : recordType === 45 ? "box" : "boundary";
|
|
11268
|
+
current = { kind: currentKind, layer: "0", datatype: "0", points: [] };
|
|
11269
|
+
} else if (recordType === 12) {
|
|
11270
|
+
currentKind = "text";
|
|
11271
|
+
current = { layer: "0", text: "", x: 0, y: 0 };
|
|
11272
|
+
} else if (recordType === 10 || recordType === 11) {
|
|
11273
|
+
currentKind = "reference";
|
|
11274
|
+
current = { cell: "", x: 0, y: 0 };
|
|
11275
|
+
} else if (recordType === 13 && data.length >= 2) {
|
|
11276
|
+
current.layer = String(readInt16(data, 0));
|
|
11277
|
+
} else if ((recordType === 14 || recordType === 22) && data.length >= 2) {
|
|
11278
|
+
current.datatype = String(readInt16(data, 0));
|
|
11279
|
+
} else if (recordType === 15 && data.length >= 4) {
|
|
11280
|
+
current.width = Math.abs(readInt32(data, 0));
|
|
11281
|
+
} else if (recordType === 16) {
|
|
11282
|
+
const points = readGdsPoints(data);
|
|
11283
|
+
if (currentKind === "text" && points[0]) {
|
|
11284
|
+
current.x = points[0][0];
|
|
11285
|
+
current.y = points[0][1];
|
|
11286
|
+
} else if (currentKind === "reference" && points[0]) {
|
|
11287
|
+
current.x = points[0][0];
|
|
11288
|
+
current.y = points[0][1];
|
|
11289
|
+
} else {
|
|
11290
|
+
current.points = points;
|
|
11291
|
+
}
|
|
11292
|
+
} else if (recordType === 18) {
|
|
11293
|
+
current.cell = readGdsString(data);
|
|
11294
|
+
} else if (recordType === 25) {
|
|
11295
|
+
current.text = readGdsString(data);
|
|
11296
|
+
} else if (recordType === 17) {
|
|
11297
|
+
if ((currentKind === "boundary" || currentKind === "path" || currentKind === "box") && current.points && current.points.length > 1) {
|
|
11298
|
+
const shape = {
|
|
11299
|
+
kind: current.kind || "boundary",
|
|
11300
|
+
layer: String(current.layer || "0"),
|
|
11301
|
+
datatype: current.datatype,
|
|
11302
|
+
points: current.points,
|
|
11303
|
+
width: current.width
|
|
11304
|
+
};
|
|
11305
|
+
shapes.push(shape);
|
|
11306
|
+
layers.set(shape.layer, (layers.get(shape.layer) || 0) + 1);
|
|
11307
|
+
} else if (currentKind === "text" && current.text) {
|
|
11308
|
+
const label = {
|
|
11309
|
+
layer: String(current.layer || "0"),
|
|
11310
|
+
text: String(current.text),
|
|
11311
|
+
x: Number(current.x || 0),
|
|
11312
|
+
y: Number(current.y || 0)
|
|
11313
|
+
};
|
|
11314
|
+
labels.push(label);
|
|
11315
|
+
layers.set(label.layer, (layers.get(label.layer) || 0) + 1);
|
|
11316
|
+
} else if (currentKind === "reference" && current.cell) {
|
|
11317
|
+
references.push({ cell: String(current.cell), x: Number(current.x || 0), y: Number(current.y || 0) });
|
|
11318
|
+
}
|
|
11319
|
+
current = {};
|
|
11320
|
+
currentKind = "";
|
|
11321
|
+
}
|
|
11322
|
+
offset += length;
|
|
11323
|
+
}
|
|
11324
|
+
return {
|
|
11325
|
+
format: "GDSII",
|
|
11326
|
+
fileName,
|
|
11327
|
+
libraryName,
|
|
11328
|
+
version: version ? `Stream ${version}` : void 0,
|
|
11329
|
+
unit,
|
|
11330
|
+
cells,
|
|
11331
|
+
shapes,
|
|
11332
|
+
labels,
|
|
11333
|
+
references,
|
|
11334
|
+
layers,
|
|
11335
|
+
metadata: [
|
|
11336
|
+
["\u5927\u5C0F", formatBytes3(bytes.byteLength)],
|
|
11337
|
+
["\u8BB0\u5F55", sumCounts(recordCounts)],
|
|
11338
|
+
["\u8BB0\u5F55\u7C7B\u578B", recordCounts.size]
|
|
11339
|
+
],
|
|
11340
|
+
notes: [
|
|
11341
|
+
`\u5DF2\u4ECE GDSII Stream \u4E2D\u89E3\u6790 ${shapes.length} \u4E2A\u51E0\u4F55\u3001${references.length} \u4E2A cell \u5F15\u7528\u548C ${labels.length} \u6BB5\u6587\u5B57\u3002`
|
|
11342
|
+
],
|
|
11343
|
+
warnings
|
|
11344
|
+
};
|
|
11345
|
+
}
|
|
11346
|
+
function parseOasisLayout(bytes, fileName) {
|
|
11347
|
+
const chunks = extractOasisCblocks(bytes);
|
|
11348
|
+
const expanded = chunks.flatMap((chunk) => [...chunk.bytes]);
|
|
11349
|
+
const cellNames = uniqueHints([...extractAsciiRuns(bytes), ...chunks.flatMap((chunk) => extractAsciiRuns(chunk.bytes))]).filter((item) => /^[A-Za-z_][\w$.-]{1,80}$/.test(item)).filter((item) => !item.startsWith("S_"));
|
|
11350
|
+
const propertyNames = uniqueHints(chunks.flatMap((chunk) => extractAsciiRuns(chunk.bytes)).filter((item) => item.startsWith("S_")));
|
|
11351
|
+
const recordCounts = scanOasisRecordCounts(bytes);
|
|
11352
|
+
const expandedCounts = scanOasisRecordCounts(new Uint8Array(expanded));
|
|
11353
|
+
const layers = /* @__PURE__ */ new Map();
|
|
11354
|
+
const shapes = [];
|
|
11355
|
+
const labels = [];
|
|
11356
|
+
const references = [];
|
|
11357
|
+
for (const name of cellNames) {
|
|
11358
|
+
references.push({ cell: name, x: 0, y: 0 });
|
|
11359
|
+
}
|
|
11360
|
+
const pseudo = createOasisStructureShapes(cellNames, chunks.length || recordCounts.size || 1);
|
|
11361
|
+
for (const shape of pseudo) {
|
|
11362
|
+
shapes.push(shape);
|
|
11363
|
+
layers.set(shape.layer, (layers.get(shape.layer) || 0) + 1);
|
|
11364
|
+
}
|
|
11365
|
+
for (let index = 0; index < cellNames.length; index++) {
|
|
11366
|
+
labels.push({ layer: "cell", text: cellNames[index], x: 12, y: -(18 + index * 18) });
|
|
11367
|
+
}
|
|
11368
|
+
if (cellNames.length > 0) {
|
|
11369
|
+
layers.set("cell", (layers.get("cell") || 0) + cellNames.length);
|
|
11370
|
+
}
|
|
11371
|
+
const version = readOasisVersion(bytes);
|
|
11372
|
+
const cblockText = chunks.length ? `${chunks.length} \u4E2A\uFF0C\u5C55\u5F00 ${formatBytes3(chunks.reduce((sum, chunk) => sum + chunk.bytes.byteLength, 0))}` : "\u672A\u53D1\u73B0";
|
|
11373
|
+
const notes = [
|
|
11374
|
+
"OASIS \u662F\u9AD8\u538B\u7F29\u82AF\u7247\u7248\u56FE\u683C\u5F0F\uFF0C\u5F53\u524D\u7248\u672C\u63D0\u4F9B\u6D4F\u89C8\u5668\u7AEF\u8BC6\u522B\u3001CBLOCK \u89E3\u538B\u3001cell/\u5C5E\u6027\u7ED3\u6784\u548C\u8F7B\u91CF\u7ED3\u6784\u793A\u610F\uFF1B\u5B8C\u6574\u51E0\u4F55\u9AD8\u4FDD\u771F\u6E32\u67D3\u5EFA\u8BAE\u540E\u7EED\u63A5\u5165\u4E13\u7528 OASIS \u89E3\u6790\u5668\u3002"
|
|
11375
|
+
];
|
|
11376
|
+
if (propertyNames.length > 0) {
|
|
11377
|
+
notes.push(`\u8BC6\u522B\u5230\u5C5E\u6027\uFF1A${propertyNames.slice(0, 5).join("\u3001")}`);
|
|
11378
|
+
}
|
|
11379
|
+
return {
|
|
11380
|
+
format: "OASIS",
|
|
11381
|
+
fileName,
|
|
11382
|
+
version,
|
|
11383
|
+
cells: cellNames,
|
|
11384
|
+
shapes,
|
|
11385
|
+
labels,
|
|
11386
|
+
references: [],
|
|
11387
|
+
layers,
|
|
11388
|
+
metadata: [
|
|
11389
|
+
["\u5927\u5C0F", formatBytes3(bytes.byteLength)],
|
|
11390
|
+
["CBLOCK", cblockText],
|
|
11391
|
+
["\u8BB0\u5F55\u7C7B\u578B", recordCounts.size + expandedCounts.size],
|
|
11392
|
+
["\u53EF\u8BFB\u7247\u6BB5", cellNames.length + propertyNames.length]
|
|
11393
|
+
],
|
|
11394
|
+
notes,
|
|
11395
|
+
warnings: cellNames.length === 0 ? ["\u5F53\u524D OASIS \u6587\u4EF6\u672A\u63D0\u53D6\u5230 cell \u540D\u79F0\uFF0C\u53EF\u80FD\u4F7F\u7528\u4E86\u66F4\u590D\u6742\u7684\u7D22\u5F15\u6216\u52A0\u5BC6/\u538B\u7F29\u5E03\u5C40\u3002"] : []
|
|
11396
|
+
};
|
|
11397
|
+
}
|
|
11398
|
+
function computeLayoutBounds(shapes, labels, references) {
|
|
11399
|
+
const xs = [];
|
|
11400
|
+
const ys = [];
|
|
11401
|
+
for (const shape of shapes) {
|
|
11402
|
+
for (const [x, y] of shape.points) {
|
|
11403
|
+
xs.push(x);
|
|
11404
|
+
ys.push(-y);
|
|
11405
|
+
}
|
|
11406
|
+
}
|
|
11407
|
+
for (const label of labels) {
|
|
11408
|
+
xs.push(label.x, label.x + label.text.length * 12);
|
|
11409
|
+
ys.push(-label.y, -label.y - 16);
|
|
11410
|
+
}
|
|
11411
|
+
for (const reference of references) {
|
|
11412
|
+
xs.push(reference.x);
|
|
11413
|
+
ys.push(-reference.y);
|
|
11414
|
+
}
|
|
11415
|
+
const minX = Math.min(...xs, 0);
|
|
11416
|
+
const maxX = Math.max(...xs, 100);
|
|
11417
|
+
const minY = Math.min(...ys, 0);
|
|
11418
|
+
const maxY = Math.max(...ys, 100);
|
|
11419
|
+
const width = Math.max(1, maxX - minX);
|
|
11420
|
+
const height = Math.max(1, maxY - minY);
|
|
11421
|
+
const padding = Math.max(width, height) * 0.06;
|
|
11422
|
+
return {
|
|
11423
|
+
minX: minX - padding,
|
|
11424
|
+
minY: minY - padding,
|
|
11425
|
+
width: width + padding * 2,
|
|
11426
|
+
height: height + padding * 2,
|
|
11427
|
+
stroke: Math.max(width, height) / 900
|
|
11428
|
+
};
|
|
11429
|
+
}
|
|
11430
|
+
function createLayoutLayerControls(svg, layers, counts) {
|
|
11431
|
+
const controls = document.createElement("div");
|
|
11432
|
+
controls.className = "ofv-cad-layers ofv-layout-layers";
|
|
11433
|
+
const title = document.createElement("strong");
|
|
11434
|
+
title.textContent = `\u56FE\u5C42 ${layers.length}`;
|
|
11435
|
+
controls.append(title);
|
|
11436
|
+
for (const layer of layers) {
|
|
11437
|
+
const label = document.createElement("label");
|
|
11438
|
+
const checkbox = document.createElement("input");
|
|
11439
|
+
checkbox.type = "checkbox";
|
|
11440
|
+
checkbox.checked = true;
|
|
11441
|
+
checkbox.addEventListener("change", () => {
|
|
11442
|
+
for (const element of svg.querySelectorAll(`[data-layer="${escapeCssAttribute(layer)}"]`)) {
|
|
11443
|
+
element.style.display = checkbox.checked ? "" : "none";
|
|
11444
|
+
}
|
|
11445
|
+
});
|
|
11446
|
+
const name = document.createElement("span");
|
|
11447
|
+
name.textContent = `${layer} (${counts.get(layer) || 0})`;
|
|
11448
|
+
label.append(checkbox, name);
|
|
11449
|
+
controls.append(label);
|
|
11450
|
+
}
|
|
11451
|
+
return controls;
|
|
11452
|
+
}
|
|
11453
|
+
function createLayoutCellList(cells, references) {
|
|
11454
|
+
const details = document.createElement("details");
|
|
11455
|
+
details.className = "ofv-details ofv-layout-cells";
|
|
11456
|
+
details.open = true;
|
|
11457
|
+
const summary = document.createElement("summary");
|
|
11458
|
+
summary.textContent = `Cell \u7ED3\u6784 ${cells.length}`;
|
|
11459
|
+
const list = document.createElement("ul");
|
|
11460
|
+
const refCounts = countBy(references.map((reference) => reference.cell));
|
|
11461
|
+
for (const cell of cells.slice(0, 120)) {
|
|
11462
|
+
const item = document.createElement("li");
|
|
11463
|
+
const count = refCounts.get(cell) || 0;
|
|
11464
|
+
item.textContent = count > 0 ? `${cell} \xB7 \u5F15\u7528 ${count}` : cell;
|
|
11465
|
+
list.append(item);
|
|
11466
|
+
}
|
|
11467
|
+
details.append(summary, list);
|
|
11468
|
+
return details;
|
|
11469
|
+
}
|
|
11470
|
+
function readUInt16(bytes, offset) {
|
|
11471
|
+
return bytes[offset] << 8 | bytes[offset + 1];
|
|
11472
|
+
}
|
|
11473
|
+
function readInt16(bytes, offset) {
|
|
11474
|
+
const value = readUInt16(bytes, offset);
|
|
11475
|
+
return value & 32768 ? value - 65536 : value;
|
|
11476
|
+
}
|
|
11477
|
+
function readInt32(bytes, offset) {
|
|
11478
|
+
const value = bytes[offset] << 24 | bytes[offset + 1] << 16 | bytes[offset + 2] << 8 | bytes[offset + 3];
|
|
11479
|
+
return value | 0;
|
|
11480
|
+
}
|
|
11481
|
+
function readGdsString(bytes) {
|
|
11482
|
+
return new TextDecoder("ascii").decode(bytes).replace(/\0+$/g, "").trim();
|
|
11483
|
+
}
|
|
11484
|
+
function readGdsPoints(bytes) {
|
|
11485
|
+
const points = [];
|
|
11486
|
+
for (let offset = 0; offset + 7 < bytes.length; offset += 8) {
|
|
11487
|
+
points.push([readInt32(bytes, offset), readInt32(bytes, offset + 4)]);
|
|
11488
|
+
}
|
|
11489
|
+
return points;
|
|
11490
|
+
}
|
|
11491
|
+
function formatGdsReal(bytes, offset) {
|
|
11492
|
+
const value = readGdsReal(bytes, offset);
|
|
11493
|
+
if (!Number.isFinite(value) || value === 0) {
|
|
11494
|
+
return "0";
|
|
11495
|
+
}
|
|
11496
|
+
if (Math.abs(value) < 1e-3 || Math.abs(value) >= 1e4) {
|
|
11497
|
+
return value.toExponential(4);
|
|
11498
|
+
}
|
|
11499
|
+
return String(Number(value.toPrecision(6)));
|
|
11500
|
+
}
|
|
11501
|
+
function readGdsReal(bytes, offset) {
|
|
11502
|
+
const first = bytes[offset];
|
|
11503
|
+
if (!first) {
|
|
11504
|
+
return 0;
|
|
11505
|
+
}
|
|
11506
|
+
const sign = first & 128 ? -1 : 1;
|
|
11507
|
+
const exponent = (first & 127) - 64;
|
|
11508
|
+
let mantissa = 0;
|
|
11509
|
+
for (let index = 1; index < 8; index++) {
|
|
11510
|
+
mantissa = mantissa * 256 + bytes[offset + index];
|
|
11511
|
+
}
|
|
11512
|
+
return sign * (mantissa / Math.pow(2, 56)) * Math.pow(16, exponent);
|
|
11513
|
+
}
|
|
11514
|
+
function sumCounts(counts) {
|
|
11515
|
+
return [...counts.values()].reduce((sum, count) => sum + count, 0);
|
|
11516
|
+
}
|
|
11517
|
+
function extractOasisCblocks(bytes) {
|
|
11518
|
+
const chunks = [];
|
|
11519
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11520
|
+
const limit = Math.min(bytes.length, 25e4);
|
|
11521
|
+
for (let offset = 0; offset < limit; offset++) {
|
|
11522
|
+
try {
|
|
11523
|
+
const inflated = import_pako3.default.inflateRaw(bytes.slice(offset));
|
|
11524
|
+
if (inflated.byteLength < 4) {
|
|
11525
|
+
continue;
|
|
11526
|
+
}
|
|
11527
|
+
const ascii = extractAsciiRuns(inflated);
|
|
11528
|
+
const hasLayoutSignal = ascii.some((item) => item.startsWith("S_") || /TOP|CELL|DIE|SIZE/i.test(item));
|
|
11529
|
+
const hasRecordSignal = inflated.some((byte) => byte >= 13 && byte <= 34);
|
|
11530
|
+
if (!hasLayoutSignal && !hasRecordSignal) {
|
|
11531
|
+
continue;
|
|
11532
|
+
}
|
|
11533
|
+
const signature = `${inflated.byteLength}:${Array.from(inflated.slice(0, 12)).join(",")}`;
|
|
11534
|
+
if (seen.has(signature)) {
|
|
11535
|
+
continue;
|
|
11536
|
+
}
|
|
11537
|
+
seen.add(signature);
|
|
11538
|
+
chunks.push({ offset, bytes: inflated });
|
|
11539
|
+
if (chunks.length >= 12) {
|
|
11540
|
+
break;
|
|
11541
|
+
}
|
|
11542
|
+
} catch {
|
|
11543
|
+
}
|
|
11544
|
+
}
|
|
11545
|
+
return chunks;
|
|
11546
|
+
}
|
|
11547
|
+
function scanOasisRecordCounts(bytes) {
|
|
11548
|
+
const counts = /* @__PURE__ */ new Map();
|
|
11549
|
+
for (const byte of bytes.slice(0, Math.min(bytes.length, 12e3))) {
|
|
11550
|
+
const name = oasisRecordNames[byte];
|
|
11551
|
+
if (name) {
|
|
11552
|
+
counts.set(name, (counts.get(name) || 0) + 1);
|
|
11553
|
+
}
|
|
11554
|
+
}
|
|
11555
|
+
return counts;
|
|
11556
|
+
}
|
|
11557
|
+
function readOasisVersion(bytes) {
|
|
11558
|
+
const magic = "%SEMI-OASIS\r\n";
|
|
11559
|
+
const header = new TextDecoder("ascii").decode(bytes.slice(0, Math.min(bytes.length, 48)));
|
|
11560
|
+
if (!header.startsWith(magic)) {
|
|
11561
|
+
return void 0;
|
|
11562
|
+
}
|
|
11563
|
+
const start = magic.length;
|
|
11564
|
+
if (bytes[start] !== 1) {
|
|
11565
|
+
return "OASIS";
|
|
11566
|
+
}
|
|
11567
|
+
const length = bytes[start + 1];
|
|
11568
|
+
const version = new TextDecoder("ascii").decode(bytes.slice(start + 2, start + 2 + length));
|
|
11569
|
+
return version ? `OASIS ${version}` : "OASIS";
|
|
11570
|
+
}
|
|
11571
|
+
function createOasisStructureShapes(cellNames, fallbackCount) {
|
|
11572
|
+
const rows = Math.max(1, cellNames.length || fallbackCount);
|
|
11573
|
+
const shapes = [];
|
|
11574
|
+
for (let index = 0; index < rows; index++) {
|
|
11575
|
+
const top = -(index * 18);
|
|
11576
|
+
const height = 12;
|
|
11577
|
+
const width = 88 + Math.min((cellNames[index]?.length || 5) * 4, 90);
|
|
11578
|
+
shapes.push({
|
|
11579
|
+
kind: "box",
|
|
11580
|
+
layer: "cell",
|
|
11581
|
+
points: [
|
|
11582
|
+
[0, top],
|
|
11583
|
+
[width, top],
|
|
11584
|
+
[width, top - height],
|
|
11585
|
+
[0, top - height],
|
|
11586
|
+
[0, top]
|
|
11587
|
+
]
|
|
11588
|
+
});
|
|
11589
|
+
}
|
|
11590
|
+
return shapes;
|
|
11591
|
+
}
|
|
11592
|
+
function renderBinaryCad(panel, bytes, extension, fileName) {
|
|
10032
11593
|
const section = createSection(`${extension.toUpperCase()} \u6587\u4EF6\u9884\u89C8`);
|
|
10033
11594
|
const note = document.createElement("p");
|
|
10034
|
-
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";
|
|
10035
11596
|
const meta = document.createElement("div");
|
|
10036
11597
|
meta.className = "ofv-cad-summary";
|
|
10037
11598
|
appendMeta5(meta, "\u6587\u4EF6", fileName);
|
|
@@ -10040,33 +11601,41 @@ function renderBinaryCad(panel, arrayBuffer, extension, fileName) {
|
|
|
10040
11601
|
appendMeta5(meta, "\u7B7E\u540D", byteSignature2(bytes));
|
|
10041
11602
|
appendMeta5(meta, "\u7248\u672C", detectCadVersion(bytes, extension));
|
|
10042
11603
|
appendMeta5(meta, "\u5BB9\u5668", detectCadContainer(bytes));
|
|
10043
|
-
const actions = document.createElement("
|
|
11604
|
+
const actions = document.createElement("div");
|
|
10044
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");
|
|
10045
11609
|
const suggestions = extension === "dwg" ? [
|
|
10046
|
-
"\u670D\u52A1\u7AEF\
|
|
10047
|
-
"\
|
|
10048
|
-
"\
|
|
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"
|
|
10049
11613
|
] : [
|
|
10050
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",
|
|
10051
|
-
"\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",
|
|
10052
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"
|
|
10053
11617
|
];
|
|
10054
11618
|
for (const suggestion of suggestions) {
|
|
10055
11619
|
const item = document.createElement("li");
|
|
10056
11620
|
item.textContent = suggestion;
|
|
10057
|
-
|
|
11621
|
+
actionList.append(item);
|
|
10058
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";
|
|
10059
11628
|
const preview = document.createElement("pre");
|
|
10060
11629
|
preview.className = "ofv-text-block";
|
|
10061
11630
|
preview.textContent = hexPreview(bytes);
|
|
10062
|
-
|
|
11631
|
+
raw.append(rawSummary, preview);
|
|
11632
|
+
section.append(note, meta, actions, createBinaryCadProbe(bytes, extension), raw);
|
|
10063
11633
|
panel.append(section);
|
|
10064
11634
|
}
|
|
10065
11635
|
function createBinaryCadProbe(bytes, extension) {
|
|
10066
11636
|
const probe = probeBinaryCad(bytes);
|
|
10067
11637
|
const details = document.createElement("details");
|
|
10068
11638
|
details.className = "ofv-details ofv-cad-binary-probe";
|
|
10069
|
-
details.open = true;
|
|
10070
11639
|
const summary = document.createElement("summary");
|
|
10071
11640
|
summary.textContent = "\u4E8C\u8FDB\u5236\u7ED3\u6784\u63A2\u6D4B";
|
|
10072
11641
|
const meta = document.createElement("div");
|
|
@@ -11670,6 +13239,15 @@ function assetPlugin() {
|
|
|
11670
13239
|
const isExternal = Boolean(ctx.file.url);
|
|
11671
13240
|
const extension = resolveFormat(ctx.file, assetMimeFormatMap).toLowerCase();
|
|
11672
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
|
+
}
|
|
11673
13251
|
const section = createSection(assetTitle(extension));
|
|
11674
13252
|
const summary = document.createElement("div");
|
|
11675
13253
|
summary.className = "ofv-asset-summary";
|
|
@@ -11691,9 +13269,6 @@ function assetPlugin() {
|
|
|
11691
13269
|
if (extension === "wasm") {
|
|
11692
13270
|
section.append(createWasmPreview(bytes));
|
|
11693
13271
|
}
|
|
11694
|
-
if (extension === "psd" || extension === "psb") {
|
|
11695
|
-
section.append(createPhotoshopPreview(bytes));
|
|
11696
|
-
}
|
|
11697
13272
|
if (extension === "sqlite" || extension === "sqlite3" || extension === "db") {
|
|
11698
13273
|
section.append(createSqlitePreview(bytes));
|
|
11699
13274
|
}
|
|
@@ -11746,7 +13321,7 @@ function assetGuidance(extension) {
|
|
|
11746
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";
|
|
11747
13322
|
}
|
|
11748
13323
|
if (extension === "psd" || extension === "psb") {
|
|
11749
|
-
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";
|
|
11750
13325
|
}
|
|
11751
13326
|
if (["ai", "eps", "ps"].includes(extension)) {
|
|
11752
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";
|
|
@@ -11768,6 +13343,9 @@ function assetGuidance(extension) {
|
|
|
11768
13343
|
function isFontAsset(extension) {
|
|
11769
13344
|
return ["ttf", "otf", "woff", "woff2", "eot"].includes(extension);
|
|
11770
13345
|
}
|
|
13346
|
+
function isPhotoshopAsset(extension) {
|
|
13347
|
+
return extension === "psd" || extension === "psb";
|
|
13348
|
+
}
|
|
11771
13349
|
async function createFontPreview(extension, url, fileName, bytes) {
|
|
11772
13350
|
const preview = document.createElement("div");
|
|
11773
13351
|
preview.className = "ofv-font-preview";
|
|
@@ -12529,12 +14107,9 @@ function wasmExternalKind(kind) {
|
|
|
12529
14107
|
};
|
|
12530
14108
|
return names[kind] || `unknown ${kind}`;
|
|
12531
14109
|
}
|
|
12532
|
-
function createPhotoshopPreview(bytes) {
|
|
14110
|
+
async function createPhotoshopPreview(bytes) {
|
|
12533
14111
|
const preview = document.createElement("div");
|
|
12534
14112
|
preview.className = "ofv-psd-preview";
|
|
12535
|
-
const heading = document.createElement("strong");
|
|
12536
|
-
heading.textContent = "Photoshop \u7ED3\u6784";
|
|
12537
|
-
preview.append(heading);
|
|
12538
14113
|
const header = parsePhotoshopHeader(bytes);
|
|
12539
14114
|
if (!header.valid) {
|
|
12540
14115
|
const error = document.createElement("p");
|
|
@@ -12543,16 +14118,66 @@ function createPhotoshopPreview(bytes) {
|
|
|
12543
14118
|
preview.append(error);
|
|
12544
14119
|
return preview;
|
|
12545
14120
|
}
|
|
12546
|
-
|
|
12547
|
-
summary.className = "ofv-psd-summary";
|
|
12548
|
-
appendMeta(summary, "\u7248\u672C", header.version === 2 ? "PSB \u5927\u6587\u6863" : "PSD");
|
|
12549
|
-
appendMeta(summary, "\u753B\u5E03", `${header.width} x ${header.height}px`);
|
|
12550
|
-
appendMeta(summary, "\u901A\u9053", header.channels ?? "\u672A\u77E5");
|
|
12551
|
-
appendMeta(summary, "\u4F4D\u6DF1", `${header.depth ?? "\u672A\u77E5"} bit`);
|
|
12552
|
-
appendMeta(summary, "\u989C\u8272\u6A21\u5F0F", photoshopColorMode(header.colorMode ?? -1));
|
|
12553
|
-
preview.append(summary);
|
|
14121
|
+
preview.append(await createPhotoshopCompositePreview(bytes, header));
|
|
12554
14122
|
return preview;
|
|
12555
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
|
+
}
|
|
12556
14181
|
function parsePhotoshopHeader(bytes) {
|
|
12557
14182
|
if (bytes.length < 26) {
|
|
12558
14183
|
return { valid: false, error: "\u6587\u4EF6\u592A\u77ED\uFF0C\u65E0\u6CD5\u8BFB\u53D6 PSD/PSB \u5934\u4FE1\u606F\u3002" };
|
|
@@ -12581,19 +14206,6 @@ function parsePhotoshopHeader(bytes) {
|
|
|
12581
14206
|
colorMode: view3.getUint16(24, false)
|
|
12582
14207
|
};
|
|
12583
14208
|
}
|
|
12584
|
-
function photoshopColorMode(mode) {
|
|
12585
|
-
const modes = {
|
|
12586
|
-
0: "Bitmap",
|
|
12587
|
-
1: "Grayscale",
|
|
12588
|
-
2: "Indexed",
|
|
12589
|
-
3: "RGB",
|
|
12590
|
-
4: "CMYK",
|
|
12591
|
-
7: "Multichannel",
|
|
12592
|
-
8: "Duotone",
|
|
12593
|
-
9: "Lab"
|
|
12594
|
-
};
|
|
12595
|
-
return modes[mode] ? `${modes[mode]} (${mode})` : `\u672A\u77E5 (${mode})`;
|
|
12596
|
-
}
|
|
12597
14209
|
function createSqlitePreview(bytes) {
|
|
12598
14210
|
const preview = document.createElement("div");
|
|
12599
14211
|
preview.className = "ofv-sqlite-preview";
|