@open-file-viewer/core 0.1.4 → 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 +499 -189
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +499 -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, {
|
|
@@ -5291,7 +5345,7 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
5291
5345
|
const xlsx = await import("xlsx");
|
|
5292
5346
|
let workbook;
|
|
5293
5347
|
try {
|
|
5294
|
-
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" });
|
|
5295
5349
|
} catch (error) {
|
|
5296
5350
|
if (isLegacyOfficeBinary(extension)) {
|
|
5297
5351
|
renderLegacyOfficeBinary(panel, extension, arrayBuffer, `\u8868\u683C\u89E3\u6790\u5931\u8D25\uFF1A${normalizeOfficeError(error)}`);
|
|
@@ -5924,10 +5978,30 @@ async function renderPptx(panel, arrayBuffer) {
|
|
|
5924
5978
|
try {
|
|
5925
5979
|
const { PptxViewer } = await import("@aiden0z/pptx-renderer");
|
|
5926
5980
|
await PptxViewer.open(arrayBuffer, container);
|
|
5981
|
+
normalizePptxLayout(container);
|
|
5927
5982
|
} catch {
|
|
5928
5983
|
container.textContent = "PPTX \u6E32\u67D3\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u6587\u4EF6\u662F\u5426\u635F\u574F\u3002";
|
|
5929
5984
|
}
|
|
5930
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
|
+
}
|
|
5931
6005
|
async function renderOdp(panel, arrayBuffer) {
|
|
5932
6006
|
const zip = await import_jszip3.default.loadAsync(arrayBuffer);
|
|
5933
6007
|
const content = zip.file("content.xml");
|
|
@@ -5955,34 +6029,28 @@ async function renderPackagedOfficePreview(panel, arrayBuffer, extension) {
|
|
|
5955
6029
|
const hasEntry = (path) => entries.some((entry) => entry.name.toLowerCase() === path.toLowerCase());
|
|
5956
6030
|
const contentXml = zip.file(/(^|\/)content\.xml$/i)[0];
|
|
5957
6031
|
if (hasEntry("word/document.xml")) {
|
|
5958
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OOXML Word \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 DOCX \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5959
6032
|
await renderDocx(panel, arrayBuffer);
|
|
5960
6033
|
return true;
|
|
5961
6034
|
}
|
|
5962
6035
|
if (hasEntry("xl/workbook.xml")) {
|
|
5963
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OOXML Workbook \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 XLSX \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5964
6036
|
await renderSheet(panel, arrayBuffer, extension);
|
|
5965
6037
|
return true;
|
|
5966
6038
|
}
|
|
5967
6039
|
if (hasEntry("ppt/presentation.xml")) {
|
|
5968
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OOXML Presentation \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 PPTX \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5969
6040
|
await renderPptx(panel, arrayBuffer);
|
|
5970
6041
|
return true;
|
|
5971
6042
|
}
|
|
5972
6043
|
if (contentXml) {
|
|
5973
6044
|
const xml = await contentXml.async("text");
|
|
5974
6045
|
if (/<office:spreadsheet\b|<table:table\b/i.test(xml)) {
|
|
5975
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OpenDocument Spreadsheet \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 ODS \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5976
6046
|
renderParsedSheets(panel, parseFlatOds(xml), `${extension.toUpperCase()} \u6587\u4EF6\u672A\u89E3\u6790\u5230\u8868\u683C\u3002`);
|
|
5977
6047
|
return true;
|
|
5978
6048
|
}
|
|
5979
6049
|
if (/<office:presentation\b|<draw:page\b/i.test(xml)) {
|
|
5980
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OpenDocument Presentation \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 ODP \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5981
6050
|
renderOpenDocumentPresentation(panel, `${extension.toUpperCase()} \u6F14\u793A\u6587\u7A3F`, xml, await extractZipImages(zip, /^Pictures\//));
|
|
5982
6051
|
return true;
|
|
5983
6052
|
}
|
|
5984
6053
|
if (/<office:text\b|<text:p\b/i.test(xml)) {
|
|
5985
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OpenDocument Text \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 ODT \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5986
6054
|
renderOpenDocumentXml(panel, `${extension.toUpperCase()} \u6587\u6863`, xml);
|
|
5987
6055
|
return true;
|
|
5988
6056
|
}
|
|
@@ -6008,14 +6076,6 @@ async function renderPackagedOfficePreview(panel, arrayBuffer, extension) {
|
|
|
6008
6076
|
}
|
|
6009
6077
|
return false;
|
|
6010
6078
|
}
|
|
6011
|
-
function renderOfficePackageNotice(panel, extension, message) {
|
|
6012
|
-
const section = createSection("\u517C\u5BB9\u5305\u8BC6\u522B");
|
|
6013
|
-
const note = document.createElement("p");
|
|
6014
|
-
note.className = "ofv-office-package-note";
|
|
6015
|
-
note.textContent = `.${extension} ${message}`;
|
|
6016
|
-
section.append(note);
|
|
6017
|
-
panel.append(section);
|
|
6018
|
-
}
|
|
6019
6079
|
function renderOfficePackageStructure(panel, extension, entries, message, metadata) {
|
|
6020
6080
|
const section = createSection("Office \u5305\u7ED3\u6784\u9884\u89C8");
|
|
6021
6081
|
const note = document.createElement("p");
|
|
@@ -6663,7 +6723,7 @@ function rtfToText(rtf) {
|
|
|
6663
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();
|
|
6664
6724
|
}
|
|
6665
6725
|
async function readTextFromBuffer(arrayBuffer) {
|
|
6666
|
-
return
|
|
6726
|
+
return decodeTextBuffer(arrayBuffer);
|
|
6667
6727
|
}
|
|
6668
6728
|
function mimeTypeFromPath(path) {
|
|
6669
6729
|
const extension = path.split(".").pop()?.toLowerCase();
|
|
@@ -6730,36 +6790,72 @@ function ofdPlugin() {
|
|
|
6730
6790
|
}
|
|
6731
6791
|
};
|
|
6732
6792
|
}
|
|
6733
|
-
const
|
|
6734
|
-
const pages = await readOfdPages(entries,
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
|
|
6738
|
-
|
|
6739
|
-
|
|
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
|
+
};
|
|
6740
6806
|
if (pages.length > 0) {
|
|
6741
6807
|
const pagesWrap = document.createElement("div");
|
|
6742
6808
|
pagesWrap.className = "ofv-ofd-pages";
|
|
6743
6809
|
for (const page of pages) {
|
|
6744
6810
|
pagesWrap.append(renderOfdPage(page));
|
|
6745
6811
|
}
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
|
|
6753
|
-
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
li.textContent = entry.name;
|
|
6757
|
-
ul.append(li);
|
|
6758
|
-
}
|
|
6759
|
-
list.append(ul);
|
|
6760
|
-
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
|
+
}
|
|
6761
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
|
+
},
|
|
6762
6857
|
destroy() {
|
|
6858
|
+
ctx.toolbar?.setZoom(void 0);
|
|
6763
6859
|
panel.remove();
|
|
6764
6860
|
revokeObjectUrl(url, isExternal);
|
|
6765
6861
|
}
|
|
@@ -6767,76 +6863,71 @@ function ofdPlugin() {
|
|
|
6767
6863
|
}
|
|
6768
6864
|
};
|
|
6769
6865
|
}
|
|
6770
|
-
function
|
|
6771
|
-
const summary = document.createElement("div");
|
|
6772
|
-
summary.className = "ofv-ofd-summary";
|
|
6773
|
-
const xmlEntries = entries.filter((entry) => entry.name.endsWith(".xml")).length;
|
|
6774
|
-
const textCount = pages.reduce((count, page) => count + page.texts.length, 0);
|
|
6775
|
-
const pathCount = pages.reduce((count, page) => count + page.paths.length, 0);
|
|
6776
|
-
const lineCount = pages.reduce((count, page) => count + page.lines.length, 0);
|
|
6777
|
-
const imageCount = pages.reduce((count, page) => count + page.images.length, 0);
|
|
6778
|
-
const textLength = pages.reduce((count, page) => count + page.texts.reduce((inner, item) => inner + item.text.length, 0), 0);
|
|
6779
|
-
appendOfdSummary(summary, "\u6587\u4EF6", String(entries.length));
|
|
6780
|
-
appendOfdSummary(summary, "XML", String(xmlEntries));
|
|
6781
|
-
appendOfdSummary(summary, "\u9875\u9762", String(pages.length));
|
|
6782
|
-
appendOfdSummary(summary, "\u6587\u672C", String(textCount));
|
|
6783
|
-
appendOfdSummary(summary, "\u8DEF\u5F84", String(pathCount));
|
|
6784
|
-
appendOfdSummary(summary, "\u7EBF\u6761", String(lineCount));
|
|
6785
|
-
appendOfdSummary(summary, "\u56FE\u7247\u5BF9\u8C61", String(imageCount));
|
|
6786
|
-
appendOfdSummary(summary, "\u56FE\u7247\u8D44\u6E90", String(uniqueOfdImageResources(images)));
|
|
6787
|
-
if (textLength > 0) {
|
|
6788
|
-
appendOfdSummary(summary, "\u6587\u5B57\u957F\u5EA6", String(textLength));
|
|
6789
|
-
}
|
|
6790
|
-
const sizes = formatOfdPageSizes(pages);
|
|
6791
|
-
if (sizes) {
|
|
6792
|
-
appendOfdSummary(summary, "\u9875\u9762\u5C3A\u5BF8", sizes);
|
|
6793
|
-
}
|
|
6794
|
-
return summary;
|
|
6795
|
-
}
|
|
6796
|
-
function appendOfdSummary(parent, label, value) {
|
|
6797
|
-
const item = document.createElement("span");
|
|
6798
|
-
const key = document.createElement("span");
|
|
6799
|
-
key.textContent = label;
|
|
6800
|
-
const content = document.createElement("strong");
|
|
6801
|
-
content.textContent = value;
|
|
6802
|
-
item.append(key, content);
|
|
6803
|
-
parent.append(item);
|
|
6804
|
-
}
|
|
6805
|
-
function uniqueOfdImageResources(images) {
|
|
6806
|
-
return new Set(images.values()).size;
|
|
6807
|
-
}
|
|
6808
|
-
function formatOfdPageSizes(pages) {
|
|
6809
|
-
const counts = /* @__PURE__ */ new Map();
|
|
6810
|
-
for (const page of pages) {
|
|
6811
|
-
const key = `${Math.round(page.width)} x ${Math.round(page.height)}`;
|
|
6812
|
-
counts.set(key, (counts.get(key) || 0) + 1);
|
|
6813
|
-
}
|
|
6814
|
-
return [...counts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 4).map(([size, count]) => count > 1 ? `${size} (${count})` : size).join(", ");
|
|
6815
|
-
}
|
|
6816
|
-
async function readOfdPages(entries, images) {
|
|
6866
|
+
async function readOfdPages(entries, context) {
|
|
6817
6867
|
const pages = [];
|
|
6818
6868
|
const pageEntries = entries.filter((entry) => /(^|\/)Pages\/Page_[^/]+\/Content\.xml$/i.test(entry.name) || /(^|\/)Page_[^/]+\/Content\.xml$/i.test(entry.name)).slice(0, 80);
|
|
6819
6869
|
for (const entry of pageEntries) {
|
|
6820
6870
|
const xml = await entry.async("text");
|
|
6821
|
-
const
|
|
6871
|
+
const templates = await readPageTemplates(xml, context, entries);
|
|
6872
|
+
const page = parseOfdPage(entry.name, xml, context.images, context.fonts, templates, context.pageSize);
|
|
6822
6873
|
if (page.texts.length > 0 || page.paths.length > 0 || page.lines.length > 0 || page.images.length > 0) {
|
|
6823
6874
|
pages.push(page);
|
|
6824
6875
|
}
|
|
6825
6876
|
}
|
|
6826
6877
|
return pages;
|
|
6827
6878
|
}
|
|
6828
|
-
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) {
|
|
6829
6896
|
const doc = new DOMParser().parseFromString(xml, "application/xml");
|
|
6830
6897
|
if (doc.querySelector("parsererror")) {
|
|
6831
6898
|
return { name, width: 210, height: 297, texts: [], paths: [], lines: [], images: [] };
|
|
6832
6899
|
}
|
|
6833
|
-
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) {
|
|
6834
6919
|
const textObjects = Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "TextObject");
|
|
6835
|
-
const texts = textObjects.flatMap((element) => parseOfdTextObject(element));
|
|
6920
|
+
const texts = textObjects.flatMap((element) => parseOfdTextObject(element, fonts));
|
|
6836
6921
|
const paths = Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "PathObject").flatMap((element) => parseOfdPathObject(element));
|
|
6837
6922
|
const lines = Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "LineObject").flatMap((element) => parseOfdLineObject(element));
|
|
6838
6923
|
const imageObjects = Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "ImageObject").flatMap((element) => parseOfdImageObject(element, images));
|
|
6839
|
-
|
|
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 [
|
|
6840
6931
|
...texts.map((item) => ({ x: item.x, y: item.y, width: item.width, height: item.height })),
|
|
6841
6932
|
...paths.map((item) => ({ x: item.x, y: item.y, width: item.width, height: item.height })),
|
|
6842
6933
|
...lines.map((item) => ({
|
|
@@ -6847,38 +6938,63 @@ function parseOfdPage(name, xml, images) {
|
|
|
6847
6938
|
})),
|
|
6848
6939
|
...imageObjects.map((item) => ({ x: item.x, y: item.y, width: item.width, height: item.height }))
|
|
6849
6940
|
];
|
|
6850
|
-
const width = Math.max(pageSize.width, ...bounds.map((item) => item.x + item.width + 12));
|
|
6851
|
-
const height = Math.max(pageSize.height, ...bounds.map((item) => item.y + item.height + 12));
|
|
6852
|
-
return { name, width, height, texts, paths, lines, images: imageObjects };
|
|
6853
6941
|
}
|
|
6854
|
-
function parseOfdTextObject(element) {
|
|
6942
|
+
function parseOfdTextObject(element, fonts) {
|
|
6855
6943
|
const boundary = parseBoundary(getOfdAttribute(element, "Boundary"));
|
|
6856
6944
|
const size = finiteNumber2(getOfdAttribute(element, "Size"), Math.max(4, boundary.height || 5));
|
|
6857
6945
|
const color = parseOfdColor(element, "#111827");
|
|
6858
6946
|
const weight = finiteNumber2(getOfdAttribute(element, "Weight"), 400) >= 600 ? "700" : "400";
|
|
6859
|
-
const
|
|
6947
|
+
const fontFamily = fontStackForOfdFont(fonts.get(getOfdAttribute(element, "Font") || ""));
|
|
6948
|
+
const objectLetterSpacing = getOfdAttribute(element, "DeltaX") ? 0.5 : void 0;
|
|
6860
6949
|
const textCodes = Array.from(element.getElementsByTagName("*")).filter((child) => child.localName === "TextCode");
|
|
6861
6950
|
if (textCodes.length === 0) {
|
|
6862
6951
|
return [];
|
|
6863
6952
|
}
|
|
6864
|
-
return textCodes.
|
|
6953
|
+
return textCodes.flatMap((code) => {
|
|
6865
6954
|
const x = boundary.x + finiteNumber2(getOfdAttribute(code, "X"), 0);
|
|
6866
6955
|
const y = boundary.y + finiteNumber2(getOfdAttribute(code, "Y"), 0);
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
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
|
+
];
|
|
6878
6993
|
}).filter((item) => item.text);
|
|
6879
6994
|
}
|
|
6880
6995
|
function parseOfdPathObject(element) {
|
|
6881
6996
|
const boundary = parseBoundary(getOfdAttribute(element, "Boundary"));
|
|
6997
|
+
const ctm = parseOfdCtm(getOfdAttribute(element, "CTM"));
|
|
6882
6998
|
const commands = Array.from(element.getElementsByTagName("*")).filter(
|
|
6883
6999
|
(child) => child.localName === "AbbreviatedData" || child.localName === "PathData"
|
|
6884
7000
|
);
|
|
@@ -6893,9 +7009,10 @@ function parseOfdPathObject(element) {
|
|
|
6893
7009
|
y: boundary.y,
|
|
6894
7010
|
width: boundary.width,
|
|
6895
7011
|
height: boundary.height,
|
|
6896
|
-
stroke: parseOfdColor(element, "#
|
|
7012
|
+
stroke: parseOfdColor(element, "#111827", "StrokeColor"),
|
|
6897
7013
|
fill: parseOfdFill(element),
|
|
6898
|
-
strokeWidth: finiteNumber2(getOfdAttribute(element, "LineWidth"), 1)
|
|
7014
|
+
strokeWidth: finiteNumber2(getOfdAttribute(element, "LineWidth"), 1),
|
|
7015
|
+
transform: createOfdPathTransform(boundary.x, boundary.y, ctm)
|
|
6899
7016
|
}
|
|
6900
7017
|
];
|
|
6901
7018
|
}
|
|
@@ -6912,7 +7029,7 @@ function parseOfdLineObject(element) {
|
|
|
6912
7029
|
y1: boundary.y + start.y,
|
|
6913
7030
|
x2: boundary.x + end.x,
|
|
6914
7031
|
y2: boundary.y + end.y,
|
|
6915
|
-
stroke: parseOfdColor(element, "#
|
|
7032
|
+
stroke: parseOfdColor(element, "#111827"),
|
|
6916
7033
|
strokeWidth: finiteNumber2(getOfdAttribute(element, "LineWidth"), 1)
|
|
6917
7034
|
}
|
|
6918
7035
|
];
|
|
@@ -6934,6 +7051,8 @@ function parseOfdImageObject(element, images) {
|
|
|
6934
7051
|
function renderOfdPage(page) {
|
|
6935
7052
|
const figure = document.createElement("figure");
|
|
6936
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`);
|
|
6937
7056
|
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
6938
7057
|
svg.setAttribute("viewBox", `0 0 ${page.width} ${page.height}`);
|
|
6939
7058
|
svg.setAttribute("role", "img");
|
|
@@ -6970,7 +7089,7 @@ function renderOfdPage(page) {
|
|
|
6970
7089
|
for (const item of page.paths) {
|
|
6971
7090
|
const path = document.createElementNS(svg.namespaceURI, "path");
|
|
6972
7091
|
path.setAttribute("d", item.d);
|
|
6973
|
-
path.setAttribute("transform",
|
|
7092
|
+
path.setAttribute("transform", item.transform);
|
|
6974
7093
|
path.setAttribute("fill", item.fill);
|
|
6975
7094
|
path.setAttribute("stroke", item.stroke);
|
|
6976
7095
|
path.setAttribute("stroke-width", String(item.strokeWidth));
|
|
@@ -6989,22 +7108,63 @@ function renderOfdPage(page) {
|
|
|
6989
7108
|
}
|
|
6990
7109
|
for (const item of page.texts) {
|
|
6991
7110
|
const text = document.createElementNS(svg.namespaceURI, "text");
|
|
6992
|
-
text.setAttribute("x", String(item.x));
|
|
6993
|
-
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));
|
|
6994
7113
|
text.setAttribute("font-size", String(item.size));
|
|
6995
7114
|
text.setAttribute("fill", item.color);
|
|
6996
7115
|
text.setAttribute("font-weight", item.weight);
|
|
7116
|
+
text.setAttribute("font-family", item.fontFamily);
|
|
6997
7117
|
if (item.letterSpacing !== void 0) {
|
|
6998
7118
|
text.setAttribute("letter-spacing", String(item.letterSpacing));
|
|
6999
7119
|
}
|
|
7000
|
-
|
|
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
|
+
}
|
|
7001
7139
|
svg.append(text);
|
|
7002
7140
|
}
|
|
7003
|
-
|
|
7004
|
-
caption.textContent = `${page.name} \xB7 ${page.texts.length} text \xB7 ${page.paths.length} path \xB7 ${page.lines.length} line \xB7 ${page.images.length} image`;
|
|
7005
|
-
figure.append(svg, caption);
|
|
7141
|
+
figure.append(svg);
|
|
7006
7142
|
return figure;
|
|
7007
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
|
+
}
|
|
7008
7168
|
async function readOfdImages(entries) {
|
|
7009
7169
|
const images = /* @__PURE__ */ new Map();
|
|
7010
7170
|
for (const entry of entries.filter((item) => /\.(?:png|jpe?g|gif|bmp|webp)$/i.test(item.name)).slice(0, 80)) {
|
|
@@ -7019,17 +7179,69 @@ async function readOfdImages(entries) {
|
|
|
7019
7179
|
images.set(entry.name, href);
|
|
7020
7180
|
images.set(entry.name.split("/").pop() || entry.name, href);
|
|
7021
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
|
+
}
|
|
7022
7203
|
return images;
|
|
7023
7204
|
}
|
|
7024
|
-
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
|
+
}
|
|
7025
7237
|
const physicalBox = Array.from(doc.getElementsByTagName("*")).find((element) => element.localName === "PhysicalBox");
|
|
7026
7238
|
if (physicalBox?.textContent) {
|
|
7027
7239
|
const box = parseBoundary(physicalBox.textContent);
|
|
7028
7240
|
if (box.width > 0 && box.height > 0) {
|
|
7029
|
-
return { width: box.width, height: box.height };
|
|
7241
|
+
return { width: box.width, height: box.height, explicit: true };
|
|
7030
7242
|
}
|
|
7031
7243
|
}
|
|
7032
|
-
return { width: 210, height: 297 };
|
|
7244
|
+
return { width: 210, height: 297, explicit: false };
|
|
7033
7245
|
}
|
|
7034
7246
|
function parseOfdColor(element, fallback, preferredLocalName = "FillColor") {
|
|
7035
7247
|
const colorElement = findOfdChild(element, preferredLocalName) || findOfdChild(element, "StrokeColor") || findOfdChild(element, "FillColor");
|
|
@@ -7060,6 +7272,101 @@ function parsePoint(value, fallback) {
|
|
|
7060
7272
|
function normalizeOfdPathData(value) {
|
|
7061
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();
|
|
7062
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
|
+
}
|
|
7063
7370
|
function mimeTypeFromPath2(path) {
|
|
7064
7371
|
const extension = path.split(".").pop()?.toLowerCase();
|
|
7065
7372
|
const map = {
|
|
@@ -7097,6 +7404,9 @@ function finiteNumber2(value, fallback) {
|
|
|
7097
7404
|
const parsed = value === null ? Number.NaN : Number(value);
|
|
7098
7405
|
return Number.isFinite(parsed) ? parsed : fallback;
|
|
7099
7406
|
}
|
|
7407
|
+
function formatOfdCssNumber(value) {
|
|
7408
|
+
return Number.isInteger(value) ? String(value) : value.toFixed(3).replace(/0+$/, "").replace(/\.$/, "");
|
|
7409
|
+
}
|
|
7100
7410
|
function createOfdFallback(fileName, url, detail) {
|
|
7101
7411
|
const fallback = document.createElement("div");
|
|
7102
7412
|
fallback.className = "ofv-fallback";
|