@open-file-viewer/core 0.1.3 → 0.1.5
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/LICENSE +21 -0
- package/dist/index.cjs +523 -189
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +523 -189
- package/dist/index.js.map +1 -1
- package/dist/style.css +45 -33
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -3328,6 +3328,95 @@ function readAiffExtended(bytes, offset) {
|
|
|
3328
3328
|
return mantissa * Math.pow(2, exponent - 16383 - 63);
|
|
3329
3329
|
}
|
|
3330
3330
|
|
|
3331
|
+
// src/plugins/utils.ts
|
|
3332
|
+
async function readArrayBuffer(file) {
|
|
3333
|
+
if (file.source instanceof ArrayBuffer) {
|
|
3334
|
+
return file.source;
|
|
3335
|
+
}
|
|
3336
|
+
if (file.blob) {
|
|
3337
|
+
return file.blob.arrayBuffer();
|
|
3338
|
+
}
|
|
3339
|
+
if (typeof file.source === "string") {
|
|
3340
|
+
const response = await fetch(file.source);
|
|
3341
|
+
if (!response.ok) {
|
|
3342
|
+
throw new Error(`Failed to fetch file: ${response.status}`);
|
|
3343
|
+
}
|
|
3344
|
+
return response.arrayBuffer();
|
|
3345
|
+
}
|
|
3346
|
+
throw new Error("Unsupported file source.");
|
|
3347
|
+
}
|
|
3348
|
+
async function readTextFile(file) {
|
|
3349
|
+
const decode = (buffer) => decodeTextBuffer(buffer);
|
|
3350
|
+
if (typeof file.source === "string") {
|
|
3351
|
+
const response = await fetch(file.source);
|
|
3352
|
+
if (!response.ok) {
|
|
3353
|
+
throw new Error(`Failed to fetch text file: ${response.status}`);
|
|
3354
|
+
}
|
|
3355
|
+
return decode(await response.arrayBuffer());
|
|
3356
|
+
}
|
|
3357
|
+
if (file.blob) {
|
|
3358
|
+
return decode(await file.blob.arrayBuffer());
|
|
3359
|
+
}
|
|
3360
|
+
if (file.source instanceof ArrayBuffer) {
|
|
3361
|
+
return decode(file.source);
|
|
3362
|
+
}
|
|
3363
|
+
return String(file.source);
|
|
3364
|
+
}
|
|
3365
|
+
function createPanel(className = "") {
|
|
3366
|
+
const panel = document.createElement("div");
|
|
3367
|
+
panel.className = `ofv-panel ${className}`.trim();
|
|
3368
|
+
return panel;
|
|
3369
|
+
}
|
|
3370
|
+
function createSection(title) {
|
|
3371
|
+
const section = document.createElement("section");
|
|
3372
|
+
section.className = "ofv-section";
|
|
3373
|
+
const heading = document.createElement("h3");
|
|
3374
|
+
heading.textContent = title;
|
|
3375
|
+
section.append(heading);
|
|
3376
|
+
return section;
|
|
3377
|
+
}
|
|
3378
|
+
function appendMeta(parent, label, value) {
|
|
3379
|
+
const row = document.createElement("div");
|
|
3380
|
+
row.className = "ofv-meta-row";
|
|
3381
|
+
const key = document.createElement("span");
|
|
3382
|
+
key.textContent = label;
|
|
3383
|
+
const content = document.createElement("strong");
|
|
3384
|
+
content.textContent = String(value);
|
|
3385
|
+
row.append(key, content);
|
|
3386
|
+
parent.append(row);
|
|
3387
|
+
}
|
|
3388
|
+
function decodeTextBuffer(buffer) {
|
|
3389
|
+
return decodeTextBytes(new Uint8Array(buffer));
|
|
3390
|
+
}
|
|
3391
|
+
function decodeTextBytes(bytes) {
|
|
3392
|
+
if (bytes.length >= 3 && bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191) {
|
|
3393
|
+
return new TextDecoder("utf-8").decode(bytes.subarray(3));
|
|
3394
|
+
}
|
|
3395
|
+
if (bytes.length >= 2) {
|
|
3396
|
+
if (bytes[0] === 255 && bytes[1] === 254) {
|
|
3397
|
+
return new TextDecoder("utf-16le").decode(bytes.subarray(2));
|
|
3398
|
+
}
|
|
3399
|
+
if (bytes[0] === 254 && bytes[1] === 255) {
|
|
3400
|
+
return new TextDecoder("utf-16be").decode(bytes.subarray(2));
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
try {
|
|
3404
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
3405
|
+
} catch {
|
|
3406
|
+
return decodeWithFallback(bytes, "gb18030") || decodeWithFallback(bytes, "gbk") || new TextDecoder("utf-8").decode(bytes);
|
|
3407
|
+
}
|
|
3408
|
+
}
|
|
3409
|
+
function decodeWithFallback(bytes, encoding) {
|
|
3410
|
+
try {
|
|
3411
|
+
return new TextDecoder(encoding).decode(bytes);
|
|
3412
|
+
} catch {
|
|
3413
|
+
return void 0;
|
|
3414
|
+
}
|
|
3415
|
+
}
|
|
3416
|
+
function resolveFormat(file, mimeMap) {
|
|
3417
|
+
return file.extension || mimeMap[file.mimeType] || "";
|
|
3418
|
+
}
|
|
3419
|
+
|
|
3331
3420
|
// src/plugins/text.ts
|
|
3332
3421
|
var langMap = {
|
|
3333
3422
|
js: "javascript",
|
|
@@ -3984,13 +4073,13 @@ async function readText(source) {
|
|
|
3984
4073
|
if (!response.ok) {
|
|
3985
4074
|
throw new Error(`Failed to fetch text file: ${response.status}`);
|
|
3986
4075
|
}
|
|
3987
|
-
return response.
|
|
4076
|
+
return decodeTextBuffer(await response.arrayBuffer());
|
|
3988
4077
|
}
|
|
3989
4078
|
if (source instanceof Blob) {
|
|
3990
|
-
return source.
|
|
4079
|
+
return decodeTextBuffer(await source.arrayBuffer());
|
|
3991
4080
|
}
|
|
3992
4081
|
if (source instanceof ArrayBuffer) {
|
|
3993
|
-
return
|
|
4082
|
+
return decodeTextBuffer(source);
|
|
3994
4083
|
}
|
|
3995
4084
|
return String(source);
|
|
3996
4085
|
}
|
|
@@ -4347,68 +4436,6 @@ function configurePdfWorker(pdf, workerSrc) {
|
|
|
4347
4436
|
// src/plugins/epub.ts
|
|
4348
4437
|
var import_jszip = __toESM(require("jszip"), 1);
|
|
4349
4438
|
var import_dompurify = __toESM(require("dompurify"), 1);
|
|
4350
|
-
|
|
4351
|
-
// src/plugins/utils.ts
|
|
4352
|
-
async function readArrayBuffer(file) {
|
|
4353
|
-
if (file.source instanceof ArrayBuffer) {
|
|
4354
|
-
return file.source;
|
|
4355
|
-
}
|
|
4356
|
-
if (file.blob) {
|
|
4357
|
-
return file.blob.arrayBuffer();
|
|
4358
|
-
}
|
|
4359
|
-
if (typeof file.source === "string") {
|
|
4360
|
-
const response = await fetch(file.source);
|
|
4361
|
-
if (!response.ok) {
|
|
4362
|
-
throw new Error(`Failed to fetch file: ${response.status}`);
|
|
4363
|
-
}
|
|
4364
|
-
return response.arrayBuffer();
|
|
4365
|
-
}
|
|
4366
|
-
throw new Error("Unsupported file source.");
|
|
4367
|
-
}
|
|
4368
|
-
async function readTextFile(file) {
|
|
4369
|
-
if (typeof file.source === "string") {
|
|
4370
|
-
const response = await fetch(file.source);
|
|
4371
|
-
if (!response.ok) {
|
|
4372
|
-
throw new Error(`Failed to fetch text file: ${response.status}`);
|
|
4373
|
-
}
|
|
4374
|
-
return response.text();
|
|
4375
|
-
}
|
|
4376
|
-
if (file.blob) {
|
|
4377
|
-
return file.blob.text();
|
|
4378
|
-
}
|
|
4379
|
-
if (file.source instanceof ArrayBuffer) {
|
|
4380
|
-
return new TextDecoder().decode(file.source);
|
|
4381
|
-
}
|
|
4382
|
-
return String(file.source);
|
|
4383
|
-
}
|
|
4384
|
-
function createPanel(className = "") {
|
|
4385
|
-
const panel = document.createElement("div");
|
|
4386
|
-
panel.className = `ofv-panel ${className}`.trim();
|
|
4387
|
-
return panel;
|
|
4388
|
-
}
|
|
4389
|
-
function createSection(title) {
|
|
4390
|
-
const section = document.createElement("section");
|
|
4391
|
-
section.className = "ofv-section";
|
|
4392
|
-
const heading = document.createElement("h3");
|
|
4393
|
-
heading.textContent = title;
|
|
4394
|
-
section.append(heading);
|
|
4395
|
-
return section;
|
|
4396
|
-
}
|
|
4397
|
-
function appendMeta(parent, label, value) {
|
|
4398
|
-
const row = document.createElement("div");
|
|
4399
|
-
row.className = "ofv-meta-row";
|
|
4400
|
-
const key = document.createElement("span");
|
|
4401
|
-
key.textContent = label;
|
|
4402
|
-
const content = document.createElement("strong");
|
|
4403
|
-
content.textContent = String(value);
|
|
4404
|
-
row.append(key, content);
|
|
4405
|
-
parent.append(row);
|
|
4406
|
-
}
|
|
4407
|
-
function resolveFormat(file, mimeMap) {
|
|
4408
|
-
return file.extension || mimeMap[file.mimeType] || "";
|
|
4409
|
-
}
|
|
4410
|
-
|
|
4411
|
-
// src/plugins/epub.ts
|
|
4412
4439
|
var epubMimeTypes = /* @__PURE__ */ new Set(["application/epub+zip", "application/x-epub+zip"]);
|
|
4413
4440
|
function epubPlugin() {
|
|
4414
4441
|
return {
|
|
@@ -5028,8 +5055,15 @@ function officePlugin() {
|
|
|
5028
5055
|
ctx.viewport.append(panel);
|
|
5029
5056
|
const extension = resolveFormat(ctx.file, officeMimeFormatMap);
|
|
5030
5057
|
const arrayBuffer = await readArrayBuffer(ctx.file);
|
|
5058
|
+
const packageFormat = shouldSniffPackagedOffice(extension) ? await detectPackagedOfficeFormat(arrayBuffer) : void 0;
|
|
5031
5059
|
let disposeDocxFit;
|
|
5032
|
-
if (fileIsDocx(extension)) {
|
|
5060
|
+
if (packageFormat === "docx" && !fileIsDocx(extension)) {
|
|
5061
|
+
disposeDocxFit = await renderDocx(panel, arrayBuffer);
|
|
5062
|
+
} else if (packageFormat === "xlsx" && !sheetExtensions.has(extension)) {
|
|
5063
|
+
await renderSheet(panel, arrayBuffer, "xlsx");
|
|
5064
|
+
} else if (packageFormat === "pptx" && !["pptx", "pptm", "ppsx", "ppsm", "potx", "potm"].includes(extension)) {
|
|
5065
|
+
await renderPptx(panel, arrayBuffer);
|
|
5066
|
+
} else if (fileIsDocx(extension)) {
|
|
5033
5067
|
disposeDocxFit = await renderDocx(panel, arrayBuffer);
|
|
5034
5068
|
} else if (extension === "rtf") {
|
|
5035
5069
|
renderPlainDocument(panel, "RTF \u6587\u6863", rtfToText(await readTextFromBuffer(arrayBuffer)));
|
|
@@ -5065,12 +5099,32 @@ function officePlugin() {
|
|
|
5065
5099
|
function fileIsDocx(extension) {
|
|
5066
5100
|
return extension === "docx" || extension === "docm" || extension === "dotx" || extension === "dotm";
|
|
5067
5101
|
}
|
|
5102
|
+
function shouldSniffPackagedOffice(extension) {
|
|
5103
|
+
return isLegacyOfficeBinary(extension) || packagedOfficeCandidates.has(extension) || extension === "";
|
|
5104
|
+
}
|
|
5105
|
+
async function detectPackagedOfficeFormat(arrayBuffer) {
|
|
5106
|
+
try {
|
|
5107
|
+
const zip = await import_jszip3.default.loadAsync(arrayBuffer);
|
|
5108
|
+
const entries = Object.values(zip.files).filter((entry) => !entry.dir);
|
|
5109
|
+
const hasEntry = (path) => entries.some((entry) => entry.name.toLowerCase() === path.toLowerCase());
|
|
5110
|
+
if (hasEntry("word/document.xml")) {
|
|
5111
|
+
return "docx";
|
|
5112
|
+
}
|
|
5113
|
+
if (hasEntry("xl/workbook.xml")) {
|
|
5114
|
+
return "xlsx";
|
|
5115
|
+
}
|
|
5116
|
+
if (hasEntry("ppt/presentation.xml")) {
|
|
5117
|
+
return "pptx";
|
|
5118
|
+
}
|
|
5119
|
+
} catch {
|
|
5120
|
+
return void 0;
|
|
5121
|
+
}
|
|
5122
|
+
return void 0;
|
|
5123
|
+
}
|
|
5068
5124
|
async function renderDocx(panel, arrayBuffer) {
|
|
5069
|
-
const section = createSection("Word \u6587\u6863");
|
|
5070
5125
|
const content = document.createElement("div");
|
|
5071
5126
|
content.className = "ofv-docx-document";
|
|
5072
|
-
|
|
5073
|
-
panel.append(section);
|
|
5127
|
+
panel.append(content);
|
|
5074
5128
|
try {
|
|
5075
5129
|
const docxPreview = await import("docx-preview");
|
|
5076
5130
|
await docxPreview.renderAsync(arrayBuffer, content, content, {
|
|
@@ -5089,6 +5143,7 @@ async function renderDocx(panel, arrayBuffer) {
|
|
|
5089
5143
|
experimental: true,
|
|
5090
5144
|
useBase64URL: true
|
|
5091
5145
|
});
|
|
5146
|
+
normalizeDocxLayout(content);
|
|
5092
5147
|
return fitDocxPages(content);
|
|
5093
5148
|
} catch (error) {
|
|
5094
5149
|
content.replaceChildren();
|
|
@@ -5106,6 +5161,29 @@ async function renderDocx(panel, arrayBuffer) {
|
|
|
5106
5161
|
}
|
|
5107
5162
|
return () => void 0;
|
|
5108
5163
|
}
|
|
5164
|
+
function normalizeDocxLayout(container) {
|
|
5165
|
+
const pages = container.querySelectorAll("section.ofv-docx");
|
|
5166
|
+
for (const page of pages) {
|
|
5167
|
+
for (const element of page.querySelectorAll("[style*='line-height']")) {
|
|
5168
|
+
const lineHeight = parseCssLineHeight(element.style.lineHeight);
|
|
5169
|
+
if (lineHeight > 0 && lineHeight < 1) {
|
|
5170
|
+
element.style.lineHeight = "1.2";
|
|
5171
|
+
}
|
|
5172
|
+
}
|
|
5173
|
+
}
|
|
5174
|
+
}
|
|
5175
|
+
function parseCssLineHeight(value) {
|
|
5176
|
+
const trimmed = value.trim();
|
|
5177
|
+
if (!trimmed || trimmed === "normal") {
|
|
5178
|
+
return 0;
|
|
5179
|
+
}
|
|
5180
|
+
if (trimmed.endsWith("%")) {
|
|
5181
|
+
const parsedPercent = Number.parseFloat(trimmed);
|
|
5182
|
+
return Number.isFinite(parsedPercent) ? parsedPercent / 100 : 0;
|
|
5183
|
+
}
|
|
5184
|
+
const parsed = Number.parseFloat(trimmed);
|
|
5185
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
5186
|
+
}
|
|
5109
5187
|
function fitDocxPages(container) {
|
|
5110
5188
|
const wrapper = container.querySelector(".ofv-docx-wrapper");
|
|
5111
5189
|
if (!wrapper) {
|
|
@@ -5267,7 +5345,7 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
5267
5345
|
const xlsx = await import("xlsx");
|
|
5268
5346
|
let workbook;
|
|
5269
5347
|
try {
|
|
5270
|
-
workbook = xlsx.read(arrayBuffer, { type: "array" });
|
|
5348
|
+
workbook = extension === "csv" || extension === "tsv" ? xlsx.read(decodeTextBuffer(arrayBuffer), { type: "string", FS: extension === "tsv" ? " " : "," }) : xlsx.read(arrayBuffer, { type: "array" });
|
|
5271
5349
|
} catch (error) {
|
|
5272
5350
|
if (isLegacyOfficeBinary(extension)) {
|
|
5273
5351
|
renderLegacyOfficeBinary(panel, extension, arrayBuffer, `\u8868\u683C\u89E3\u6790\u5931\u8D25\uFF1A${normalizeOfficeError(error)}`);
|
|
@@ -5900,10 +5978,30 @@ async function renderPptx(panel, arrayBuffer) {
|
|
|
5900
5978
|
try {
|
|
5901
5979
|
const { PptxViewer } = await import("@aiden0z/pptx-renderer");
|
|
5902
5980
|
await PptxViewer.open(arrayBuffer, container);
|
|
5981
|
+
normalizePptxLayout(container);
|
|
5903
5982
|
} catch {
|
|
5904
5983
|
container.textContent = "PPTX \u6E32\u67D3\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u6587\u4EF6\u662F\u5426\u635F\u574F\u3002";
|
|
5905
5984
|
}
|
|
5906
5985
|
}
|
|
5986
|
+
function normalizePptxLayout(container) {
|
|
5987
|
+
const slideCanvases = findPptxSlideCanvases(container);
|
|
5988
|
+
for (const slide of slideCanvases) {
|
|
5989
|
+
slide.style.backgroundColor = "#FFFFFF";
|
|
5990
|
+
}
|
|
5991
|
+
}
|
|
5992
|
+
function findPptxSlideCanvases(container) {
|
|
5993
|
+
const slideWrappers = Array.from(container.querySelectorAll("div[data-slide-index]"));
|
|
5994
|
+
const candidates = slideWrappers.flatMap(
|
|
5995
|
+
(wrapper) => Array.from(wrapper.querySelectorAll("div")).filter(isPptxSlideCanvas)
|
|
5996
|
+
);
|
|
5997
|
+
if (candidates.length > 0) {
|
|
5998
|
+
return Array.from(new Set(candidates));
|
|
5999
|
+
}
|
|
6000
|
+
return Array.from(container.querySelectorAll("div")).filter(isPptxSlideCanvas);
|
|
6001
|
+
}
|
|
6002
|
+
function isPptxSlideCanvas(element) {
|
|
6003
|
+
return element.style.position === "relative" && parseCssPixelValue(element.style.width) > 0 && parseCssPixelValue(element.style.height) > 0;
|
|
6004
|
+
}
|
|
5907
6005
|
async function renderOdp(panel, arrayBuffer) {
|
|
5908
6006
|
const zip = await import_jszip3.default.loadAsync(arrayBuffer);
|
|
5909
6007
|
const content = zip.file("content.xml");
|
|
@@ -5931,34 +6029,28 @@ async function renderPackagedOfficePreview(panel, arrayBuffer, extension) {
|
|
|
5931
6029
|
const hasEntry = (path) => entries.some((entry) => entry.name.toLowerCase() === path.toLowerCase());
|
|
5932
6030
|
const contentXml = zip.file(/(^|\/)content\.xml$/i)[0];
|
|
5933
6031
|
if (hasEntry("word/document.xml")) {
|
|
5934
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OOXML Word \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 DOCX \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5935
6032
|
await renderDocx(panel, arrayBuffer);
|
|
5936
6033
|
return true;
|
|
5937
6034
|
}
|
|
5938
6035
|
if (hasEntry("xl/workbook.xml")) {
|
|
5939
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OOXML Workbook \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 XLSX \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5940
6036
|
await renderSheet(panel, arrayBuffer, extension);
|
|
5941
6037
|
return true;
|
|
5942
6038
|
}
|
|
5943
6039
|
if (hasEntry("ppt/presentation.xml")) {
|
|
5944
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OOXML Presentation \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 PPTX \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5945
6040
|
await renderPptx(panel, arrayBuffer);
|
|
5946
6041
|
return true;
|
|
5947
6042
|
}
|
|
5948
6043
|
if (contentXml) {
|
|
5949
6044
|
const xml = await contentXml.async("text");
|
|
5950
6045
|
if (/<office:spreadsheet\b|<table:table\b/i.test(xml)) {
|
|
5951
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OpenDocument Spreadsheet \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 ODS \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5952
6046
|
renderParsedSheets(panel, parseFlatOds(xml), `${extension.toUpperCase()} \u6587\u4EF6\u672A\u89E3\u6790\u5230\u8868\u683C\u3002`);
|
|
5953
6047
|
return true;
|
|
5954
6048
|
}
|
|
5955
6049
|
if (/<office:presentation\b|<draw:page\b/i.test(xml)) {
|
|
5956
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OpenDocument Presentation \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 ODP \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5957
6050
|
renderOpenDocumentPresentation(panel, `${extension.toUpperCase()} \u6F14\u793A\u6587\u7A3F`, xml, await extractZipImages(zip, /^Pictures\//));
|
|
5958
6051
|
return true;
|
|
5959
6052
|
}
|
|
5960
6053
|
if (/<office:text\b|<text:p\b/i.test(xml)) {
|
|
5961
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OpenDocument Text \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 ODT \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5962
6054
|
renderOpenDocumentXml(panel, `${extension.toUpperCase()} \u6587\u6863`, xml);
|
|
5963
6055
|
return true;
|
|
5964
6056
|
}
|
|
@@ -5984,14 +6076,6 @@ async function renderPackagedOfficePreview(panel, arrayBuffer, extension) {
|
|
|
5984
6076
|
}
|
|
5985
6077
|
return false;
|
|
5986
6078
|
}
|
|
5987
|
-
function renderOfficePackageNotice(panel, extension, message) {
|
|
5988
|
-
const section = createSection("\u517C\u5BB9\u5305\u8BC6\u522B");
|
|
5989
|
-
const note = document.createElement("p");
|
|
5990
|
-
note.className = "ofv-office-package-note";
|
|
5991
|
-
note.textContent = `.${extension} ${message}`;
|
|
5992
|
-
section.append(note);
|
|
5993
|
-
panel.append(section);
|
|
5994
|
-
}
|
|
5995
6079
|
function renderOfficePackageStructure(panel, extension, entries, message, metadata) {
|
|
5996
6080
|
const section = createSection("Office \u5305\u7ED3\u6784\u9884\u89C8");
|
|
5997
6081
|
const note = document.createElement("p");
|
|
@@ -6639,7 +6723,7 @@ function rtfToText(rtf) {
|
|
|
6639
6723
|
return rtf.replace(/\\'[0-9a-fA-F]{2}/g, "").replace(/\\par[d]?/g, "\n").replace(/\\tab/g, " ").replace(/\\[a-zA-Z]+\d* ?/g, "").replace(/[{}]/g, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
6640
6724
|
}
|
|
6641
6725
|
async function readTextFromBuffer(arrayBuffer) {
|
|
6642
|
-
return
|
|
6726
|
+
return decodeTextBuffer(arrayBuffer);
|
|
6643
6727
|
}
|
|
6644
6728
|
function mimeTypeFromPath(path) {
|
|
6645
6729
|
const extension = path.split(".").pop()?.toLowerCase();
|
|
@@ -6706,36 +6790,72 @@ function ofdPlugin() {
|
|
|
6706
6790
|
}
|
|
6707
6791
|
};
|
|
6708
6792
|
}
|
|
6709
|
-
const
|
|
6710
|
-
const pages = await readOfdPages(entries,
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6793
|
+
const context = await readOfdContext(entries);
|
|
6794
|
+
const pages = await readOfdPages(entries, context);
|
|
6795
|
+
let zoom = 1;
|
|
6796
|
+
let rotation = 0;
|
|
6797
|
+
const applyZoom = () => {
|
|
6798
|
+
panel.style.setProperty("--ofv-ofd-zoom", formatOfdCssNumber(zoom));
|
|
6799
|
+
ctx.toolbar?.setZoom(zoom);
|
|
6800
|
+
};
|
|
6801
|
+
const applyRotation = () => {
|
|
6802
|
+
const normalizedRotation = (rotation % 360 + 360) % 360;
|
|
6803
|
+
panel.style.setProperty("--ofv-ofd-rotation", `${normalizedRotation}deg`);
|
|
6804
|
+
panel.classList.toggle("is-ofd-rotated-sideways", normalizedRotation === 90 || normalizedRotation === 270);
|
|
6805
|
+
};
|
|
6716
6806
|
if (pages.length > 0) {
|
|
6717
6807
|
const pagesWrap = document.createElement("div");
|
|
6718
6808
|
pagesWrap.className = "ofv-ofd-pages";
|
|
6719
6809
|
for (const page of pages) {
|
|
6720
6810
|
pagesWrap.append(renderOfdPage(page));
|
|
6721
6811
|
}
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
li.textContent = entry.name;
|
|
6733
|
-
ul.append(li);
|
|
6734
|
-
}
|
|
6735
|
-
list.append(ul);
|
|
6736
|
-
panel.append(section, list);
|
|
6812
|
+
panel.append(pagesWrap);
|
|
6813
|
+
applyZoom();
|
|
6814
|
+
applyRotation();
|
|
6815
|
+
}
|
|
6816
|
+
if (pages.length === 0) {
|
|
6817
|
+
const content = document.createElement("pre");
|
|
6818
|
+
content.className = "ofv-text-block";
|
|
6819
|
+
content.textContent = textFragments.slice(0, 300).join("\n") || "\u672A\u63D0\u53D6\u5230\u53EF\u8BFB\u6587\u672C\u3002";
|
|
6820
|
+
panel.append(content);
|
|
6821
|
+
}
|
|
6737
6822
|
return {
|
|
6823
|
+
canCommand(command) {
|
|
6824
|
+
return pages.length > 0 && (command === "zoom-in" || command === "zoom-out" || command === "zoom-reset" || command === "rotate-right" || command === "rotate-left");
|
|
6825
|
+
},
|
|
6826
|
+
command(command) {
|
|
6827
|
+
if (pages.length === 0) {
|
|
6828
|
+
return false;
|
|
6829
|
+
}
|
|
6830
|
+
if (command === "zoom-in") {
|
|
6831
|
+
zoom = Math.min(4, zoom + 0.15);
|
|
6832
|
+
applyZoom();
|
|
6833
|
+
return true;
|
|
6834
|
+
}
|
|
6835
|
+
if (command === "zoom-out") {
|
|
6836
|
+
zoom = Math.max(0.25, zoom - 0.15);
|
|
6837
|
+
applyZoom();
|
|
6838
|
+
return true;
|
|
6839
|
+
}
|
|
6840
|
+
if (command === "zoom-reset") {
|
|
6841
|
+
zoom = 1;
|
|
6842
|
+
applyZoom();
|
|
6843
|
+
return true;
|
|
6844
|
+
}
|
|
6845
|
+
if (command === "rotate-right") {
|
|
6846
|
+
rotation += 90;
|
|
6847
|
+
applyRotation();
|
|
6848
|
+
return true;
|
|
6849
|
+
}
|
|
6850
|
+
if (command === "rotate-left") {
|
|
6851
|
+
rotation -= 90;
|
|
6852
|
+
applyRotation();
|
|
6853
|
+
return true;
|
|
6854
|
+
}
|
|
6855
|
+
return false;
|
|
6856
|
+
},
|
|
6738
6857
|
destroy() {
|
|
6858
|
+
ctx.toolbar?.setZoom(void 0);
|
|
6739
6859
|
panel.remove();
|
|
6740
6860
|
revokeObjectUrl(url, isExternal);
|
|
6741
6861
|
}
|
|
@@ -6743,76 +6863,71 @@ function ofdPlugin() {
|
|
|
6743
6863
|
}
|
|
6744
6864
|
};
|
|
6745
6865
|
}
|
|
6746
|
-
function
|
|
6747
|
-
const summary = document.createElement("div");
|
|
6748
|
-
summary.className = "ofv-ofd-summary";
|
|
6749
|
-
const xmlEntries = entries.filter((entry) => entry.name.endsWith(".xml")).length;
|
|
6750
|
-
const textCount = pages.reduce((count, page) => count + page.texts.length, 0);
|
|
6751
|
-
const pathCount = pages.reduce((count, page) => count + page.paths.length, 0);
|
|
6752
|
-
const lineCount = pages.reduce((count, page) => count + page.lines.length, 0);
|
|
6753
|
-
const imageCount = pages.reduce((count, page) => count + page.images.length, 0);
|
|
6754
|
-
const textLength = pages.reduce((count, page) => count + page.texts.reduce((inner, item) => inner + item.text.length, 0), 0);
|
|
6755
|
-
appendOfdSummary(summary, "\u6587\u4EF6", String(entries.length));
|
|
6756
|
-
appendOfdSummary(summary, "XML", String(xmlEntries));
|
|
6757
|
-
appendOfdSummary(summary, "\u9875\u9762", String(pages.length));
|
|
6758
|
-
appendOfdSummary(summary, "\u6587\u672C", String(textCount));
|
|
6759
|
-
appendOfdSummary(summary, "\u8DEF\u5F84", String(pathCount));
|
|
6760
|
-
appendOfdSummary(summary, "\u7EBF\u6761", String(lineCount));
|
|
6761
|
-
appendOfdSummary(summary, "\u56FE\u7247\u5BF9\u8C61", String(imageCount));
|
|
6762
|
-
appendOfdSummary(summary, "\u56FE\u7247\u8D44\u6E90", String(uniqueOfdImageResources(images)));
|
|
6763
|
-
if (textLength > 0) {
|
|
6764
|
-
appendOfdSummary(summary, "\u6587\u5B57\u957F\u5EA6", String(textLength));
|
|
6765
|
-
}
|
|
6766
|
-
const sizes = formatOfdPageSizes(pages);
|
|
6767
|
-
if (sizes) {
|
|
6768
|
-
appendOfdSummary(summary, "\u9875\u9762\u5C3A\u5BF8", sizes);
|
|
6769
|
-
}
|
|
6770
|
-
return summary;
|
|
6771
|
-
}
|
|
6772
|
-
function appendOfdSummary(parent, label, value) {
|
|
6773
|
-
const item = document.createElement("span");
|
|
6774
|
-
const key = document.createElement("span");
|
|
6775
|
-
key.textContent = label;
|
|
6776
|
-
const content = document.createElement("strong");
|
|
6777
|
-
content.textContent = value;
|
|
6778
|
-
item.append(key, content);
|
|
6779
|
-
parent.append(item);
|
|
6780
|
-
}
|
|
6781
|
-
function uniqueOfdImageResources(images) {
|
|
6782
|
-
return new Set(images.values()).size;
|
|
6783
|
-
}
|
|
6784
|
-
function formatOfdPageSizes(pages) {
|
|
6785
|
-
const counts = /* @__PURE__ */ new Map();
|
|
6786
|
-
for (const page of pages) {
|
|
6787
|
-
const key = `${Math.round(page.width)} x ${Math.round(page.height)}`;
|
|
6788
|
-
counts.set(key, (counts.get(key) || 0) + 1);
|
|
6789
|
-
}
|
|
6790
|
-
return [...counts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 4).map(([size, count]) => count > 1 ? `${size} (${count})` : size).join(", ");
|
|
6791
|
-
}
|
|
6792
|
-
async function readOfdPages(entries, images) {
|
|
6866
|
+
async function readOfdPages(entries, context) {
|
|
6793
6867
|
const pages = [];
|
|
6794
6868
|
const pageEntries = entries.filter((entry) => /(^|\/)Pages\/Page_[^/]+\/Content\.xml$/i.test(entry.name) || /(^|\/)Page_[^/]+\/Content\.xml$/i.test(entry.name)).slice(0, 80);
|
|
6795
6869
|
for (const entry of pageEntries) {
|
|
6796
6870
|
const xml = await entry.async("text");
|
|
6797
|
-
const
|
|
6871
|
+
const templates = await readPageTemplates(xml, context, entries);
|
|
6872
|
+
const page = parseOfdPage(entry.name, xml, context.images, context.fonts, templates, context.pageSize);
|
|
6798
6873
|
if (page.texts.length > 0 || page.paths.length > 0 || page.lines.length > 0 || page.images.length > 0) {
|
|
6799
6874
|
pages.push(page);
|
|
6800
6875
|
}
|
|
6801
6876
|
}
|
|
6802
6877
|
return pages;
|
|
6803
6878
|
}
|
|
6804
|
-
function
|
|
6879
|
+
async function readPageTemplates(xml, context, entries) {
|
|
6880
|
+
const doc = new DOMParser().parseFromString(xml, "application/xml");
|
|
6881
|
+
if (doc.querySelector("parsererror")) {
|
|
6882
|
+
return [];
|
|
6883
|
+
}
|
|
6884
|
+
const templateIds = Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "Template").map((element) => getOfdAttribute(element, "TemplateID") || getOfdAttribute(element, "ID")).filter((id) => Boolean(id));
|
|
6885
|
+
const templates = [];
|
|
6886
|
+
for (const id of templateIds) {
|
|
6887
|
+
const path = context.templates.get(id);
|
|
6888
|
+
const entry = path ? findOfdEntry(entries, path) : void 0;
|
|
6889
|
+
if (entry) {
|
|
6890
|
+
templates.push(await entry.async("text"));
|
|
6891
|
+
}
|
|
6892
|
+
}
|
|
6893
|
+
return templates;
|
|
6894
|
+
}
|
|
6895
|
+
function parseOfdPage(name, xml, images, fonts, templateXmls = [], defaultPageSize) {
|
|
6805
6896
|
const doc = new DOMParser().parseFromString(xml, "application/xml");
|
|
6806
6897
|
if (doc.querySelector("parsererror")) {
|
|
6807
6898
|
return { name, width: 210, height: 297, texts: [], paths: [], lines: [], images: [] };
|
|
6808
6899
|
}
|
|
6809
|
-
const pageSize = parseOfdPageSize(doc);
|
|
6900
|
+
const pageSize = parseOfdPageSize(doc, defaultPageSize);
|
|
6901
|
+
const templatePages = templateXmls.map((templateXml) => {
|
|
6902
|
+
const templateDoc = new DOMParser().parseFromString(templateXml, "application/xml");
|
|
6903
|
+
return templateDoc.querySelector("parsererror") ? emptyOfdPageContent() : parseOfdPageContent(templateDoc, images, fonts);
|
|
6904
|
+
});
|
|
6905
|
+
const pageContent = parseOfdPageContent(doc, images, fonts);
|
|
6906
|
+
const texts = [...templatePages.flatMap((page) => page.texts), ...pageContent.texts];
|
|
6907
|
+
const paths = [...templatePages.flatMap((page) => page.paths), ...pageContent.paths];
|
|
6908
|
+
const lines = [...templatePages.flatMap((page) => page.lines), ...pageContent.lines];
|
|
6909
|
+
const imageObjects = [...templatePages.flatMap((page) => page.images), ...pageContent.images];
|
|
6910
|
+
if (pageSize.explicit) {
|
|
6911
|
+
return { name, width: pageSize.width, height: pageSize.height, texts, paths, lines, images: imageObjects };
|
|
6912
|
+
}
|
|
6913
|
+
const bounds = createOfdBounds(texts, paths, lines, imageObjects);
|
|
6914
|
+
const width = Math.max(pageSize.width, ...bounds.map((item) => item.x + item.width + 12));
|
|
6915
|
+
const height = Math.max(pageSize.height, ...bounds.map((item) => item.y + item.height + 12));
|
|
6916
|
+
return { name, width, height, texts, paths, lines, images: imageObjects };
|
|
6917
|
+
}
|
|
6918
|
+
function parseOfdPageContent(doc, images, fonts) {
|
|
6810
6919
|
const textObjects = Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "TextObject");
|
|
6811
|
-
const texts = textObjects.flatMap((element) => parseOfdTextObject(element));
|
|
6920
|
+
const texts = textObjects.flatMap((element) => parseOfdTextObject(element, fonts));
|
|
6812
6921
|
const paths = Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "PathObject").flatMap((element) => parseOfdPathObject(element));
|
|
6813
6922
|
const lines = Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "LineObject").flatMap((element) => parseOfdLineObject(element));
|
|
6814
6923
|
const imageObjects = Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "ImageObject").flatMap((element) => parseOfdImageObject(element, images));
|
|
6815
|
-
|
|
6924
|
+
return { texts, paths, lines, images: imageObjects };
|
|
6925
|
+
}
|
|
6926
|
+
function emptyOfdPageContent() {
|
|
6927
|
+
return { texts: [], paths: [], lines: [], images: [] };
|
|
6928
|
+
}
|
|
6929
|
+
function createOfdBounds(texts, paths, lines, imageObjects) {
|
|
6930
|
+
return [
|
|
6816
6931
|
...texts.map((item) => ({ x: item.x, y: item.y, width: item.width, height: item.height })),
|
|
6817
6932
|
...paths.map((item) => ({ x: item.x, y: item.y, width: item.width, height: item.height })),
|
|
6818
6933
|
...lines.map((item) => ({
|
|
@@ -6823,38 +6938,63 @@ function parseOfdPage(name, xml, images) {
|
|
|
6823
6938
|
})),
|
|
6824
6939
|
...imageObjects.map((item) => ({ x: item.x, y: item.y, width: item.width, height: item.height }))
|
|
6825
6940
|
];
|
|
6826
|
-
const width = Math.max(pageSize.width, ...bounds.map((item) => item.x + item.width + 12));
|
|
6827
|
-
const height = Math.max(pageSize.height, ...bounds.map((item) => item.y + item.height + 12));
|
|
6828
|
-
return { name, width, height, texts, paths, lines, images: imageObjects };
|
|
6829
6941
|
}
|
|
6830
|
-
function parseOfdTextObject(element) {
|
|
6942
|
+
function parseOfdTextObject(element, fonts) {
|
|
6831
6943
|
const boundary = parseBoundary(getOfdAttribute(element, "Boundary"));
|
|
6832
6944
|
const size = finiteNumber2(getOfdAttribute(element, "Size"), Math.max(4, boundary.height || 5));
|
|
6833
6945
|
const color = parseOfdColor(element, "#111827");
|
|
6834
6946
|
const weight = finiteNumber2(getOfdAttribute(element, "Weight"), 400) >= 600 ? "700" : "400";
|
|
6835
|
-
const
|
|
6947
|
+
const fontFamily = fontStackForOfdFont(fonts.get(getOfdAttribute(element, "Font") || ""));
|
|
6948
|
+
const objectLetterSpacing = getOfdAttribute(element, "DeltaX") ? 0.5 : void 0;
|
|
6836
6949
|
const textCodes = Array.from(element.getElementsByTagName("*")).filter((child) => child.localName === "TextCode");
|
|
6837
6950
|
if (textCodes.length === 0) {
|
|
6838
6951
|
return [];
|
|
6839
6952
|
}
|
|
6840
|
-
return textCodes.
|
|
6953
|
+
return textCodes.flatMap((code) => {
|
|
6841
6954
|
const x = boundary.x + finiteNumber2(getOfdAttribute(code, "X"), 0);
|
|
6842
6955
|
const y = boundary.y + finiteNumber2(getOfdAttribute(code, "Y"), 0);
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6956
|
+
const text = code.textContent?.trim() || "";
|
|
6957
|
+
const deltaX = parseOfdDeltaX(getOfdAttribute(code, "DeltaX"));
|
|
6958
|
+
const align = deltaX ? "start" : inferOfdTextAlign(text, boundary);
|
|
6959
|
+
const deltaY = getOfdAttribute(code, "DeltaY");
|
|
6960
|
+
if (deltaY && text.length > 1) {
|
|
6961
|
+
const step = parseOfdDeltaStep(deltaY, size);
|
|
6962
|
+
return Array.from(text).map((char, index) => ({
|
|
6963
|
+
text: char,
|
|
6964
|
+
x,
|
|
6965
|
+
y: y + index * step,
|
|
6966
|
+
width: boundary.width,
|
|
6967
|
+
height: boundary.height,
|
|
6968
|
+
size,
|
|
6969
|
+
color,
|
|
6970
|
+
weight,
|
|
6971
|
+
fontFamily,
|
|
6972
|
+
letterSpacing: objectLetterSpacing,
|
|
6973
|
+
vertical: true,
|
|
6974
|
+
align
|
|
6975
|
+
}));
|
|
6976
|
+
}
|
|
6977
|
+
return [
|
|
6978
|
+
{
|
|
6979
|
+
text,
|
|
6980
|
+
x,
|
|
6981
|
+
y,
|
|
6982
|
+
width: boundary.width,
|
|
6983
|
+
height: boundary.height,
|
|
6984
|
+
size,
|
|
6985
|
+
color,
|
|
6986
|
+
weight,
|
|
6987
|
+
fontFamily,
|
|
6988
|
+
letterSpacing: deltaX ? void 0 : objectLetterSpacing,
|
|
6989
|
+
deltaX,
|
|
6990
|
+
align
|
|
6991
|
+
}
|
|
6992
|
+
];
|
|
6854
6993
|
}).filter((item) => item.text);
|
|
6855
6994
|
}
|
|
6856
6995
|
function parseOfdPathObject(element) {
|
|
6857
6996
|
const boundary = parseBoundary(getOfdAttribute(element, "Boundary"));
|
|
6997
|
+
const ctm = parseOfdCtm(getOfdAttribute(element, "CTM"));
|
|
6858
6998
|
const commands = Array.from(element.getElementsByTagName("*")).filter(
|
|
6859
6999
|
(child) => child.localName === "AbbreviatedData" || child.localName === "PathData"
|
|
6860
7000
|
);
|
|
@@ -6869,9 +7009,10 @@ function parseOfdPathObject(element) {
|
|
|
6869
7009
|
y: boundary.y,
|
|
6870
7010
|
width: boundary.width,
|
|
6871
7011
|
height: boundary.height,
|
|
6872
|
-
stroke: parseOfdColor(element, "#
|
|
7012
|
+
stroke: parseOfdColor(element, "#111827", "StrokeColor"),
|
|
6873
7013
|
fill: parseOfdFill(element),
|
|
6874
|
-
strokeWidth: finiteNumber2(getOfdAttribute(element, "LineWidth"), 1)
|
|
7014
|
+
strokeWidth: finiteNumber2(getOfdAttribute(element, "LineWidth"), 1),
|
|
7015
|
+
transform: createOfdPathTransform(boundary.x, boundary.y, ctm)
|
|
6875
7016
|
}
|
|
6876
7017
|
];
|
|
6877
7018
|
}
|
|
@@ -6888,7 +7029,7 @@ function parseOfdLineObject(element) {
|
|
|
6888
7029
|
y1: boundary.y + start.y,
|
|
6889
7030
|
x2: boundary.x + end.x,
|
|
6890
7031
|
y2: boundary.y + end.y,
|
|
6891
|
-
stroke: parseOfdColor(element, "#
|
|
7032
|
+
stroke: parseOfdColor(element, "#111827"),
|
|
6892
7033
|
strokeWidth: finiteNumber2(getOfdAttribute(element, "LineWidth"), 1)
|
|
6893
7034
|
}
|
|
6894
7035
|
];
|
|
@@ -6910,6 +7051,8 @@ function parseOfdImageObject(element, images) {
|
|
|
6910
7051
|
function renderOfdPage(page) {
|
|
6911
7052
|
const figure = document.createElement("figure");
|
|
6912
7053
|
figure.className = "ofv-ofd-page";
|
|
7054
|
+
figure.style.setProperty("--ofv-ofd-page-width", `${formatOfdCssNumber(page.width)}mm`);
|
|
7055
|
+
figure.style.setProperty("--ofv-ofd-page-height", `${formatOfdCssNumber(page.height)}mm`);
|
|
6913
7056
|
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
6914
7057
|
svg.setAttribute("viewBox", `0 0 ${page.width} ${page.height}`);
|
|
6915
7058
|
svg.setAttribute("role", "img");
|
|
@@ -6946,7 +7089,7 @@ function renderOfdPage(page) {
|
|
|
6946
7089
|
for (const item of page.paths) {
|
|
6947
7090
|
const path = document.createElementNS(svg.namespaceURI, "path");
|
|
6948
7091
|
path.setAttribute("d", item.d);
|
|
6949
|
-
path.setAttribute("transform",
|
|
7092
|
+
path.setAttribute("transform", item.transform);
|
|
6950
7093
|
path.setAttribute("fill", item.fill);
|
|
6951
7094
|
path.setAttribute("stroke", item.stroke);
|
|
6952
7095
|
path.setAttribute("stroke-width", String(item.strokeWidth));
|
|
@@ -6965,22 +7108,63 @@ function renderOfdPage(page) {
|
|
|
6965
7108
|
}
|
|
6966
7109
|
for (const item of page.texts) {
|
|
6967
7110
|
const text = document.createElementNS(svg.namespaceURI, "text");
|
|
6968
|
-
text.setAttribute("x", String(item.x));
|
|
6969
|
-
text.setAttribute("y", String(item.y
|
|
7111
|
+
text.setAttribute("x", String(item.align === "end" ? item.x + item.width : item.x));
|
|
7112
|
+
text.setAttribute("y", String(item.y));
|
|
6970
7113
|
text.setAttribute("font-size", String(item.size));
|
|
6971
7114
|
text.setAttribute("fill", item.color);
|
|
6972
7115
|
text.setAttribute("font-weight", item.weight);
|
|
7116
|
+
text.setAttribute("font-family", item.fontFamily);
|
|
6973
7117
|
if (item.letterSpacing !== void 0) {
|
|
6974
7118
|
text.setAttribute("letter-spacing", String(item.letterSpacing));
|
|
6975
7119
|
}
|
|
6976
|
-
|
|
7120
|
+
if (item.deltaX && item.deltaX.length > 0 && item.align !== "end") {
|
|
7121
|
+
const chars = Array.from(item.text);
|
|
7122
|
+
let x = item.x;
|
|
7123
|
+
for (let index = 0; index < chars.length; index += 1) {
|
|
7124
|
+
const span = document.createElementNS(svg.namespaceURI, "tspan");
|
|
7125
|
+
span.setAttribute("x", String(x));
|
|
7126
|
+
span.setAttribute("y", String(item.y));
|
|
7127
|
+
if (index < chars.length - 1) {
|
|
7128
|
+
x += item.deltaX[Math.min(index, item.deltaX.length - 1)] || item.size;
|
|
7129
|
+
}
|
|
7130
|
+
span.textContent = chars[index];
|
|
7131
|
+
text.append(span);
|
|
7132
|
+
}
|
|
7133
|
+
} else {
|
|
7134
|
+
if (item.align === "end") {
|
|
7135
|
+
text.setAttribute("text-anchor", "end");
|
|
7136
|
+
}
|
|
7137
|
+
text.textContent = item.text;
|
|
7138
|
+
}
|
|
6977
7139
|
svg.append(text);
|
|
6978
7140
|
}
|
|
6979
|
-
|
|
6980
|
-
caption.textContent = `${page.name} \xB7 ${page.texts.length} text \xB7 ${page.paths.length} path \xB7 ${page.lines.length} line \xB7 ${page.images.length} image`;
|
|
6981
|
-
figure.append(svg, caption);
|
|
7141
|
+
figure.append(svg);
|
|
6982
7142
|
return figure;
|
|
6983
7143
|
}
|
|
7144
|
+
async function readOfdContext(entries) {
|
|
7145
|
+
const images = await readOfdImages(entries);
|
|
7146
|
+
const fonts = await readOfdFonts(entries);
|
|
7147
|
+
const { templates, pageSize } = await readOfdDocumentInfo(entries);
|
|
7148
|
+
return { images, templates, fonts, pageSize };
|
|
7149
|
+
}
|
|
7150
|
+
async function readOfdFonts(entries) {
|
|
7151
|
+
const fonts = /* @__PURE__ */ new Map();
|
|
7152
|
+
for (const entry of entries.filter((item) => /(?:^|\/)(?:DocumentRes|PublicRes)\.xml$/i.test(item.name))) {
|
|
7153
|
+
const xml = await entry.async("text");
|
|
7154
|
+
const doc = new DOMParser().parseFromString(xml, "application/xml");
|
|
7155
|
+
if (doc.querySelector("parsererror")) {
|
|
7156
|
+
continue;
|
|
7157
|
+
}
|
|
7158
|
+
for (const font of Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "Font")) {
|
|
7159
|
+
const id = getOfdAttribute(font, "ID");
|
|
7160
|
+
const name = getOfdAttribute(font, "FontName") || getOfdAttribute(font, "FamilyName");
|
|
7161
|
+
if (id && name) {
|
|
7162
|
+
fonts.set(id, name.trim());
|
|
7163
|
+
}
|
|
7164
|
+
}
|
|
7165
|
+
}
|
|
7166
|
+
return fonts;
|
|
7167
|
+
}
|
|
6984
7168
|
async function readOfdImages(entries) {
|
|
6985
7169
|
const images = /* @__PURE__ */ new Map();
|
|
6986
7170
|
for (const entry of entries.filter((item) => /\.(?:png|jpe?g|gif|bmp|webp)$/i.test(item.name)).slice(0, 80)) {
|
|
@@ -6995,17 +7179,69 @@ async function readOfdImages(entries) {
|
|
|
6995
7179
|
images.set(entry.name, href);
|
|
6996
7180
|
images.set(entry.name.split("/").pop() || entry.name, href);
|
|
6997
7181
|
}
|
|
7182
|
+
for (const entry of entries.filter((item) => /(?:^|\/)(?:DocumentRes|PublicRes)\.xml$/i.test(item.name))) {
|
|
7183
|
+
const xml = await entry.async("text");
|
|
7184
|
+
const doc = new DOMParser().parseFromString(xml, "application/xml");
|
|
7185
|
+
if (doc.querySelector("parsererror")) {
|
|
7186
|
+
continue;
|
|
7187
|
+
}
|
|
7188
|
+
const baseLoc = getOfdAttribute(doc.documentElement, "BaseLoc") || "";
|
|
7189
|
+
const resourceDir = joinOfdPath(directoryName2(entry.name), baseLoc);
|
|
7190
|
+
for (const media of Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "MultiMedia")) {
|
|
7191
|
+
const id = getOfdAttribute(media, "ID");
|
|
7192
|
+
const mediaFile = findOfdChild(media, "MediaFile")?.textContent?.trim();
|
|
7193
|
+
if (!id || !mediaFile) {
|
|
7194
|
+
continue;
|
|
7195
|
+
}
|
|
7196
|
+
const imageEntry = findOfdEntry(entries, joinOfdPath(resourceDir, mediaFile)) || findOfdEntry(entries, mediaFile);
|
|
7197
|
+
const href = imageEntry ? images.get(imageEntry.name) || images.get(imageEntry.name.split("/").pop() || imageEntry.name) : void 0;
|
|
7198
|
+
if (href) {
|
|
7199
|
+
images.set(id, href);
|
|
7200
|
+
}
|
|
7201
|
+
}
|
|
7202
|
+
}
|
|
6998
7203
|
return images;
|
|
6999
7204
|
}
|
|
7000
|
-
function
|
|
7205
|
+
async function readOfdDocumentInfo(entries) {
|
|
7206
|
+
const templates = /* @__PURE__ */ new Map();
|
|
7207
|
+
let pageSize;
|
|
7208
|
+
for (const entry of entries.filter((item) => /(?:^|\/)Document\.xml$/i.test(item.name))) {
|
|
7209
|
+
const xml = await entry.async("text");
|
|
7210
|
+
const doc = new DOMParser().parseFromString(xml, "application/xml");
|
|
7211
|
+
if (doc.querySelector("parsererror")) {
|
|
7212
|
+
continue;
|
|
7213
|
+
}
|
|
7214
|
+
const documentDir = directoryName2(entry.name);
|
|
7215
|
+
const physicalBox = Array.from(doc.getElementsByTagName("*")).find((element) => element.localName === "PageArea")?.getElementsByTagName("*");
|
|
7216
|
+
const pageAreaBox = physicalBox ? Array.from(physicalBox).find((element) => element.localName === "PhysicalBox") : void 0;
|
|
7217
|
+
if (pageAreaBox?.textContent) {
|
|
7218
|
+
const box = parseBoundary(pageAreaBox.textContent);
|
|
7219
|
+
if (box.width > 0 && box.height > 0) {
|
|
7220
|
+
pageSize = { width: box.width, height: box.height };
|
|
7221
|
+
}
|
|
7222
|
+
}
|
|
7223
|
+
for (const template of Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "TemplatePage")) {
|
|
7224
|
+
const id = getOfdAttribute(template, "ID");
|
|
7225
|
+
const baseLoc = getOfdAttribute(template, "BaseLoc");
|
|
7226
|
+
if (id && baseLoc) {
|
|
7227
|
+
templates.set(id, joinOfdPath(documentDir, baseLoc));
|
|
7228
|
+
}
|
|
7229
|
+
}
|
|
7230
|
+
}
|
|
7231
|
+
return { templates, pageSize };
|
|
7232
|
+
}
|
|
7233
|
+
function parseOfdPageSize(doc, defaultPageSize) {
|
|
7234
|
+
if (defaultPageSize) {
|
|
7235
|
+
return { ...defaultPageSize, explicit: true };
|
|
7236
|
+
}
|
|
7001
7237
|
const physicalBox = Array.from(doc.getElementsByTagName("*")).find((element) => element.localName === "PhysicalBox");
|
|
7002
7238
|
if (physicalBox?.textContent) {
|
|
7003
7239
|
const box = parseBoundary(physicalBox.textContent);
|
|
7004
7240
|
if (box.width > 0 && box.height > 0) {
|
|
7005
|
-
return { width: box.width, height: box.height };
|
|
7241
|
+
return { width: box.width, height: box.height, explicit: true };
|
|
7006
7242
|
}
|
|
7007
7243
|
}
|
|
7008
|
-
return { width: 210, height: 297 };
|
|
7244
|
+
return { width: 210, height: 297, explicit: false };
|
|
7009
7245
|
}
|
|
7010
7246
|
function parseOfdColor(element, fallback, preferredLocalName = "FillColor") {
|
|
7011
7247
|
const colorElement = findOfdChild(element, preferredLocalName) || findOfdChild(element, "StrokeColor") || findOfdChild(element, "FillColor");
|
|
@@ -7036,6 +7272,101 @@ function parsePoint(value, fallback) {
|
|
|
7036
7272
|
function normalizeOfdPathData(value) {
|
|
7037
7273
|
return value.replace(/\bM\s+/gi, "M ").replace(/\bL\s+/gi, "L ").replace(/\bC\s+/gi, "C ").replace(/\bQ\s+/gi, "Q ").replace(/\bA\s+/gi, "A ").replace(/\bB\s+/gi, "C ").replace(/\bZ\b/gi, "Z").replace(/\s+/g, " ").trim();
|
|
7038
7274
|
}
|
|
7275
|
+
function parseOfdCtm(value) {
|
|
7276
|
+
const parts = (value || "").trim().split(/\s+/).map((part) => Number(part));
|
|
7277
|
+
if (parts.length !== 6 || parts.some((part) => !Number.isFinite(part))) {
|
|
7278
|
+
return void 0;
|
|
7279
|
+
}
|
|
7280
|
+
return parts;
|
|
7281
|
+
}
|
|
7282
|
+
function createOfdPathTransform(x, y, ctm) {
|
|
7283
|
+
if (!ctm) {
|
|
7284
|
+
return `translate(${x} ${y})`;
|
|
7285
|
+
}
|
|
7286
|
+
const [a, b, c, d, e, f] = ctm;
|
|
7287
|
+
return `translate(${x} ${y}) matrix(${a} ${b} ${c} ${d} ${e} ${f})`;
|
|
7288
|
+
}
|
|
7289
|
+
function parseOfdDeltaStep(value, fallback) {
|
|
7290
|
+
const numbers = value.match(/-?\d+(?:\.\d+)?/g)?.map((part) => Number(part)).filter((part) => Number.isFinite(part)) || [];
|
|
7291
|
+
return numbers.length > 0 ? numbers[numbers.length - 1] : fallback;
|
|
7292
|
+
}
|
|
7293
|
+
function parseOfdDeltaX(value) {
|
|
7294
|
+
if (!value) {
|
|
7295
|
+
return void 0;
|
|
7296
|
+
}
|
|
7297
|
+
const parts = value.match(/[a-z]+|-?\d+(?:\.\d+)?/gi) || [];
|
|
7298
|
+
const deltas = [];
|
|
7299
|
+
for (let index = 0; index < parts.length; index += 1) {
|
|
7300
|
+
const token = parts[index];
|
|
7301
|
+
if (/^g$/i.test(token)) {
|
|
7302
|
+
const count = Number(parts[index + 1]);
|
|
7303
|
+
const step = Number(parts[index + 2]);
|
|
7304
|
+
if (Number.isFinite(count) && Number.isFinite(step)) {
|
|
7305
|
+
deltas.push(...Array.from({ length: Math.max(0, Math.floor(count)) }, () => step));
|
|
7306
|
+
}
|
|
7307
|
+
index += 2;
|
|
7308
|
+
continue;
|
|
7309
|
+
}
|
|
7310
|
+
const numeric = Number(token);
|
|
7311
|
+
if (Number.isFinite(numeric)) {
|
|
7312
|
+
deltas.push(numeric);
|
|
7313
|
+
}
|
|
7314
|
+
}
|
|
7315
|
+
return deltas.length > 0 ? deltas : void 0;
|
|
7316
|
+
}
|
|
7317
|
+
function fontStackForOfdFont(fontName) {
|
|
7318
|
+
const normalized = (fontName || "").trim().toLowerCase();
|
|
7319
|
+
if (normalized.includes("courier")) {
|
|
7320
|
+
return '"Courier New", Courier, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace';
|
|
7321
|
+
}
|
|
7322
|
+
if (normalized.includes("kaiti") || normalized.includes("kai") || normalized.includes("\u6977")) {
|
|
7323
|
+
return '"STKaiti", "Kaiti SC", "KaiTi", "\u6977\u4F53", serif';
|
|
7324
|
+
}
|
|
7325
|
+
if (normalized.includes("simsun") || normalized.includes("simsong") || normalized.includes("song") || normalized.includes("\u5B8B")) {
|
|
7326
|
+
return '"SimSong", "Songti SC", "STSong", SimSun, "\u5B8B\u4F53", serif';
|
|
7327
|
+
}
|
|
7328
|
+
if (normalized.includes("hei") || normalized.includes("\u9ED1")) {
|
|
7329
|
+
return '"PingFang SC", "Microsoft YaHei", SimHei, sans-serif';
|
|
7330
|
+
}
|
|
7331
|
+
return '"SimSong", "Songti SC", "STSong", SimSun, "Noto Serif CJK SC", serif';
|
|
7332
|
+
}
|
|
7333
|
+
function inferOfdTextAlign(text, boundary) {
|
|
7334
|
+
const normalized = text.trim();
|
|
7335
|
+
if (!/^[¥¥]?\d+(?:\.\d+)?%?$/.test(normalized)) {
|
|
7336
|
+
return "start";
|
|
7337
|
+
}
|
|
7338
|
+
if (boundary.x >= 75 || boundary.width <= 30) {
|
|
7339
|
+
return "end";
|
|
7340
|
+
}
|
|
7341
|
+
return "start";
|
|
7342
|
+
}
|
|
7343
|
+
function findOfdEntry(entries, path) {
|
|
7344
|
+
const normalized = normalizeOfdPath(path);
|
|
7345
|
+
return entries.find((entry) => normalizeOfdPath(entry.name) === normalized || normalizeOfdPath(entry.name).endsWith(`/${normalized}`));
|
|
7346
|
+
}
|
|
7347
|
+
function joinOfdPath(...parts) {
|
|
7348
|
+
const joined = parts.filter(Boolean).join("/");
|
|
7349
|
+
const segments = [];
|
|
7350
|
+
for (const segment of joined.split("/")) {
|
|
7351
|
+
if (!segment || segment === ".") {
|
|
7352
|
+
continue;
|
|
7353
|
+
}
|
|
7354
|
+
if (segment === "..") {
|
|
7355
|
+
segments.pop();
|
|
7356
|
+
continue;
|
|
7357
|
+
}
|
|
7358
|
+
segments.push(segment);
|
|
7359
|
+
}
|
|
7360
|
+
return segments.join("/");
|
|
7361
|
+
}
|
|
7362
|
+
function directoryName2(path) {
|
|
7363
|
+
const normalized = normalizeOfdPath(path);
|
|
7364
|
+
const index = normalized.lastIndexOf("/");
|
|
7365
|
+
return index >= 0 ? normalized.slice(0, index) : "";
|
|
7366
|
+
}
|
|
7367
|
+
function normalizeOfdPath(path) {
|
|
7368
|
+
return path.replace(/\\/g, "/").replace(/^\/+/, "").replace(/\/+/g, "/");
|
|
7369
|
+
}
|
|
7039
7370
|
function mimeTypeFromPath2(path) {
|
|
7040
7371
|
const extension = path.split(".").pop()?.toLowerCase();
|
|
7041
7372
|
const map = {
|
|
@@ -7073,6 +7404,9 @@ function finiteNumber2(value, fallback) {
|
|
|
7073
7404
|
const parsed = value === null ? Number.NaN : Number(value);
|
|
7074
7405
|
return Number.isFinite(parsed) ? parsed : fallback;
|
|
7075
7406
|
}
|
|
7407
|
+
function formatOfdCssNumber(value) {
|
|
7408
|
+
return Number.isInteger(value) ? String(value) : value.toFixed(3).replace(/0+$/, "").replace(/\.$/, "");
|
|
7409
|
+
}
|
|
7076
7410
|
function createOfdFallback(fileName, url, detail) {
|
|
7077
7411
|
const fallback = document.createElement("div");
|
|
7078
7412
|
fallback.className = "ofv-fallback";
|