@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.js
CHANGED
|
@@ -3275,6 +3275,95 @@ function readAiffExtended(bytes, offset) {
|
|
|
3275
3275
|
return mantissa * Math.pow(2, exponent - 16383 - 63);
|
|
3276
3276
|
}
|
|
3277
3277
|
|
|
3278
|
+
// src/plugins/utils.ts
|
|
3279
|
+
async function readArrayBuffer(file) {
|
|
3280
|
+
if (file.source instanceof ArrayBuffer) {
|
|
3281
|
+
return file.source;
|
|
3282
|
+
}
|
|
3283
|
+
if (file.blob) {
|
|
3284
|
+
return file.blob.arrayBuffer();
|
|
3285
|
+
}
|
|
3286
|
+
if (typeof file.source === "string") {
|
|
3287
|
+
const response = await fetch(file.source);
|
|
3288
|
+
if (!response.ok) {
|
|
3289
|
+
throw new Error(`Failed to fetch file: ${response.status}`);
|
|
3290
|
+
}
|
|
3291
|
+
return response.arrayBuffer();
|
|
3292
|
+
}
|
|
3293
|
+
throw new Error("Unsupported file source.");
|
|
3294
|
+
}
|
|
3295
|
+
async function readTextFile(file) {
|
|
3296
|
+
const decode = (buffer) => decodeTextBuffer(buffer);
|
|
3297
|
+
if (typeof file.source === "string") {
|
|
3298
|
+
const response = await fetch(file.source);
|
|
3299
|
+
if (!response.ok) {
|
|
3300
|
+
throw new Error(`Failed to fetch text file: ${response.status}`);
|
|
3301
|
+
}
|
|
3302
|
+
return decode(await response.arrayBuffer());
|
|
3303
|
+
}
|
|
3304
|
+
if (file.blob) {
|
|
3305
|
+
return decode(await file.blob.arrayBuffer());
|
|
3306
|
+
}
|
|
3307
|
+
if (file.source instanceof ArrayBuffer) {
|
|
3308
|
+
return decode(file.source);
|
|
3309
|
+
}
|
|
3310
|
+
return String(file.source);
|
|
3311
|
+
}
|
|
3312
|
+
function createPanel(className = "") {
|
|
3313
|
+
const panel = document.createElement("div");
|
|
3314
|
+
panel.className = `ofv-panel ${className}`.trim();
|
|
3315
|
+
return panel;
|
|
3316
|
+
}
|
|
3317
|
+
function createSection(title) {
|
|
3318
|
+
const section = document.createElement("section");
|
|
3319
|
+
section.className = "ofv-section";
|
|
3320
|
+
const heading = document.createElement("h3");
|
|
3321
|
+
heading.textContent = title;
|
|
3322
|
+
section.append(heading);
|
|
3323
|
+
return section;
|
|
3324
|
+
}
|
|
3325
|
+
function appendMeta(parent, label, value) {
|
|
3326
|
+
const row = document.createElement("div");
|
|
3327
|
+
row.className = "ofv-meta-row";
|
|
3328
|
+
const key = document.createElement("span");
|
|
3329
|
+
key.textContent = label;
|
|
3330
|
+
const content = document.createElement("strong");
|
|
3331
|
+
content.textContent = String(value);
|
|
3332
|
+
row.append(key, content);
|
|
3333
|
+
parent.append(row);
|
|
3334
|
+
}
|
|
3335
|
+
function decodeTextBuffer(buffer) {
|
|
3336
|
+
return decodeTextBytes(new Uint8Array(buffer));
|
|
3337
|
+
}
|
|
3338
|
+
function decodeTextBytes(bytes) {
|
|
3339
|
+
if (bytes.length >= 3 && bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191) {
|
|
3340
|
+
return new TextDecoder("utf-8").decode(bytes.subarray(3));
|
|
3341
|
+
}
|
|
3342
|
+
if (bytes.length >= 2) {
|
|
3343
|
+
if (bytes[0] === 255 && bytes[1] === 254) {
|
|
3344
|
+
return new TextDecoder("utf-16le").decode(bytes.subarray(2));
|
|
3345
|
+
}
|
|
3346
|
+
if (bytes[0] === 254 && bytes[1] === 255) {
|
|
3347
|
+
return new TextDecoder("utf-16be").decode(bytes.subarray(2));
|
|
3348
|
+
}
|
|
3349
|
+
}
|
|
3350
|
+
try {
|
|
3351
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
3352
|
+
} catch {
|
|
3353
|
+
return decodeWithFallback(bytes, "gb18030") || decodeWithFallback(bytes, "gbk") || new TextDecoder("utf-8").decode(bytes);
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
function decodeWithFallback(bytes, encoding) {
|
|
3357
|
+
try {
|
|
3358
|
+
return new TextDecoder(encoding).decode(bytes);
|
|
3359
|
+
} catch {
|
|
3360
|
+
return void 0;
|
|
3361
|
+
}
|
|
3362
|
+
}
|
|
3363
|
+
function resolveFormat(file, mimeMap) {
|
|
3364
|
+
return file.extension || mimeMap[file.mimeType] || "";
|
|
3365
|
+
}
|
|
3366
|
+
|
|
3278
3367
|
// src/plugins/text.ts
|
|
3279
3368
|
var langMap = {
|
|
3280
3369
|
js: "javascript",
|
|
@@ -3931,13 +4020,13 @@ async function readText(source) {
|
|
|
3931
4020
|
if (!response.ok) {
|
|
3932
4021
|
throw new Error(`Failed to fetch text file: ${response.status}`);
|
|
3933
4022
|
}
|
|
3934
|
-
return response.
|
|
4023
|
+
return decodeTextBuffer(await response.arrayBuffer());
|
|
3935
4024
|
}
|
|
3936
4025
|
if (source instanceof Blob) {
|
|
3937
|
-
return source.
|
|
4026
|
+
return decodeTextBuffer(await source.arrayBuffer());
|
|
3938
4027
|
}
|
|
3939
4028
|
if (source instanceof ArrayBuffer) {
|
|
3940
|
-
return
|
|
4029
|
+
return decodeTextBuffer(source);
|
|
3941
4030
|
}
|
|
3942
4031
|
return String(source);
|
|
3943
4032
|
}
|
|
@@ -4294,68 +4383,6 @@ function configurePdfWorker(pdf, workerSrc) {
|
|
|
4294
4383
|
// src/plugins/epub.ts
|
|
4295
4384
|
import JSZip from "jszip";
|
|
4296
4385
|
import DOMPurify from "dompurify";
|
|
4297
|
-
|
|
4298
|
-
// src/plugins/utils.ts
|
|
4299
|
-
async function readArrayBuffer(file) {
|
|
4300
|
-
if (file.source instanceof ArrayBuffer) {
|
|
4301
|
-
return file.source;
|
|
4302
|
-
}
|
|
4303
|
-
if (file.blob) {
|
|
4304
|
-
return file.blob.arrayBuffer();
|
|
4305
|
-
}
|
|
4306
|
-
if (typeof file.source === "string") {
|
|
4307
|
-
const response = await fetch(file.source);
|
|
4308
|
-
if (!response.ok) {
|
|
4309
|
-
throw new Error(`Failed to fetch file: ${response.status}`);
|
|
4310
|
-
}
|
|
4311
|
-
return response.arrayBuffer();
|
|
4312
|
-
}
|
|
4313
|
-
throw new Error("Unsupported file source.");
|
|
4314
|
-
}
|
|
4315
|
-
async function readTextFile(file) {
|
|
4316
|
-
if (typeof file.source === "string") {
|
|
4317
|
-
const response = await fetch(file.source);
|
|
4318
|
-
if (!response.ok) {
|
|
4319
|
-
throw new Error(`Failed to fetch text file: ${response.status}`);
|
|
4320
|
-
}
|
|
4321
|
-
return response.text();
|
|
4322
|
-
}
|
|
4323
|
-
if (file.blob) {
|
|
4324
|
-
return file.blob.text();
|
|
4325
|
-
}
|
|
4326
|
-
if (file.source instanceof ArrayBuffer) {
|
|
4327
|
-
return new TextDecoder().decode(file.source);
|
|
4328
|
-
}
|
|
4329
|
-
return String(file.source);
|
|
4330
|
-
}
|
|
4331
|
-
function createPanel(className = "") {
|
|
4332
|
-
const panel = document.createElement("div");
|
|
4333
|
-
panel.className = `ofv-panel ${className}`.trim();
|
|
4334
|
-
return panel;
|
|
4335
|
-
}
|
|
4336
|
-
function createSection(title) {
|
|
4337
|
-
const section = document.createElement("section");
|
|
4338
|
-
section.className = "ofv-section";
|
|
4339
|
-
const heading = document.createElement("h3");
|
|
4340
|
-
heading.textContent = title;
|
|
4341
|
-
section.append(heading);
|
|
4342
|
-
return section;
|
|
4343
|
-
}
|
|
4344
|
-
function appendMeta(parent, label, value) {
|
|
4345
|
-
const row = document.createElement("div");
|
|
4346
|
-
row.className = "ofv-meta-row";
|
|
4347
|
-
const key = document.createElement("span");
|
|
4348
|
-
key.textContent = label;
|
|
4349
|
-
const content = document.createElement("strong");
|
|
4350
|
-
content.textContent = String(value);
|
|
4351
|
-
row.append(key, content);
|
|
4352
|
-
parent.append(row);
|
|
4353
|
-
}
|
|
4354
|
-
function resolveFormat(file, mimeMap) {
|
|
4355
|
-
return file.extension || mimeMap[file.mimeType] || "";
|
|
4356
|
-
}
|
|
4357
|
-
|
|
4358
|
-
// src/plugins/epub.ts
|
|
4359
4386
|
var epubMimeTypes = /* @__PURE__ */ new Set(["application/epub+zip", "application/x-epub+zip"]);
|
|
4360
4387
|
function epubPlugin() {
|
|
4361
4388
|
return {
|
|
@@ -4975,8 +5002,15 @@ function officePlugin() {
|
|
|
4975
5002
|
ctx.viewport.append(panel);
|
|
4976
5003
|
const extension = resolveFormat(ctx.file, officeMimeFormatMap);
|
|
4977
5004
|
const arrayBuffer = await readArrayBuffer(ctx.file);
|
|
5005
|
+
const packageFormat = shouldSniffPackagedOffice(extension) ? await detectPackagedOfficeFormat(arrayBuffer) : void 0;
|
|
4978
5006
|
let disposeDocxFit;
|
|
4979
|
-
if (fileIsDocx(extension)) {
|
|
5007
|
+
if (packageFormat === "docx" && !fileIsDocx(extension)) {
|
|
5008
|
+
disposeDocxFit = await renderDocx(panel, arrayBuffer);
|
|
5009
|
+
} else if (packageFormat === "xlsx" && !sheetExtensions.has(extension)) {
|
|
5010
|
+
await renderSheet(panel, arrayBuffer, "xlsx");
|
|
5011
|
+
} else if (packageFormat === "pptx" && !["pptx", "pptm", "ppsx", "ppsm", "potx", "potm"].includes(extension)) {
|
|
5012
|
+
await renderPptx(panel, arrayBuffer);
|
|
5013
|
+
} else if (fileIsDocx(extension)) {
|
|
4980
5014
|
disposeDocxFit = await renderDocx(panel, arrayBuffer);
|
|
4981
5015
|
} else if (extension === "rtf") {
|
|
4982
5016
|
renderPlainDocument(panel, "RTF \u6587\u6863", rtfToText(await readTextFromBuffer(arrayBuffer)));
|
|
@@ -5012,12 +5046,32 @@ function officePlugin() {
|
|
|
5012
5046
|
function fileIsDocx(extension) {
|
|
5013
5047
|
return extension === "docx" || extension === "docm" || extension === "dotx" || extension === "dotm";
|
|
5014
5048
|
}
|
|
5049
|
+
function shouldSniffPackagedOffice(extension) {
|
|
5050
|
+
return isLegacyOfficeBinary(extension) || packagedOfficeCandidates.has(extension) || extension === "";
|
|
5051
|
+
}
|
|
5052
|
+
async function detectPackagedOfficeFormat(arrayBuffer) {
|
|
5053
|
+
try {
|
|
5054
|
+
const zip = await JSZip3.loadAsync(arrayBuffer);
|
|
5055
|
+
const entries = Object.values(zip.files).filter((entry) => !entry.dir);
|
|
5056
|
+
const hasEntry = (path) => entries.some((entry) => entry.name.toLowerCase() === path.toLowerCase());
|
|
5057
|
+
if (hasEntry("word/document.xml")) {
|
|
5058
|
+
return "docx";
|
|
5059
|
+
}
|
|
5060
|
+
if (hasEntry("xl/workbook.xml")) {
|
|
5061
|
+
return "xlsx";
|
|
5062
|
+
}
|
|
5063
|
+
if (hasEntry("ppt/presentation.xml")) {
|
|
5064
|
+
return "pptx";
|
|
5065
|
+
}
|
|
5066
|
+
} catch {
|
|
5067
|
+
return void 0;
|
|
5068
|
+
}
|
|
5069
|
+
return void 0;
|
|
5070
|
+
}
|
|
5015
5071
|
async function renderDocx(panel, arrayBuffer) {
|
|
5016
|
-
const section = createSection("Word \u6587\u6863");
|
|
5017
5072
|
const content = document.createElement("div");
|
|
5018
5073
|
content.className = "ofv-docx-document";
|
|
5019
|
-
|
|
5020
|
-
panel.append(section);
|
|
5074
|
+
panel.append(content);
|
|
5021
5075
|
try {
|
|
5022
5076
|
const docxPreview = await import("docx-preview");
|
|
5023
5077
|
await docxPreview.renderAsync(arrayBuffer, content, content, {
|
|
@@ -5238,7 +5292,7 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
5238
5292
|
const xlsx = await import("xlsx");
|
|
5239
5293
|
let workbook;
|
|
5240
5294
|
try {
|
|
5241
|
-
workbook = xlsx.read(arrayBuffer, { type: "array" });
|
|
5295
|
+
workbook = extension === "csv" || extension === "tsv" ? xlsx.read(decodeTextBuffer(arrayBuffer), { type: "string", FS: extension === "tsv" ? " " : "," }) : xlsx.read(arrayBuffer, { type: "array" });
|
|
5242
5296
|
} catch (error) {
|
|
5243
5297
|
if (isLegacyOfficeBinary(extension)) {
|
|
5244
5298
|
renderLegacyOfficeBinary(panel, extension, arrayBuffer, `\u8868\u683C\u89E3\u6790\u5931\u8D25\uFF1A${normalizeOfficeError(error)}`);
|
|
@@ -5871,10 +5925,30 @@ async function renderPptx(panel, arrayBuffer) {
|
|
|
5871
5925
|
try {
|
|
5872
5926
|
const { PptxViewer } = await import("@aiden0z/pptx-renderer");
|
|
5873
5927
|
await PptxViewer.open(arrayBuffer, container);
|
|
5928
|
+
normalizePptxLayout(container);
|
|
5874
5929
|
} catch {
|
|
5875
5930
|
container.textContent = "PPTX \u6E32\u67D3\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u6587\u4EF6\u662F\u5426\u635F\u574F\u3002";
|
|
5876
5931
|
}
|
|
5877
5932
|
}
|
|
5933
|
+
function normalizePptxLayout(container) {
|
|
5934
|
+
const slideCanvases = findPptxSlideCanvases(container);
|
|
5935
|
+
for (const slide of slideCanvases) {
|
|
5936
|
+
slide.style.backgroundColor = "#FFFFFF";
|
|
5937
|
+
}
|
|
5938
|
+
}
|
|
5939
|
+
function findPptxSlideCanvases(container) {
|
|
5940
|
+
const slideWrappers = Array.from(container.querySelectorAll("div[data-slide-index]"));
|
|
5941
|
+
const candidates = slideWrappers.flatMap(
|
|
5942
|
+
(wrapper) => Array.from(wrapper.querySelectorAll("div")).filter(isPptxSlideCanvas)
|
|
5943
|
+
);
|
|
5944
|
+
if (candidates.length > 0) {
|
|
5945
|
+
return Array.from(new Set(candidates));
|
|
5946
|
+
}
|
|
5947
|
+
return Array.from(container.querySelectorAll("div")).filter(isPptxSlideCanvas);
|
|
5948
|
+
}
|
|
5949
|
+
function isPptxSlideCanvas(element) {
|
|
5950
|
+
return element.style.position === "relative" && parseCssPixelValue(element.style.width) > 0 && parseCssPixelValue(element.style.height) > 0;
|
|
5951
|
+
}
|
|
5878
5952
|
async function renderOdp(panel, arrayBuffer) {
|
|
5879
5953
|
const zip = await JSZip3.loadAsync(arrayBuffer);
|
|
5880
5954
|
const content = zip.file("content.xml");
|
|
@@ -5902,34 +5976,28 @@ async function renderPackagedOfficePreview(panel, arrayBuffer, extension) {
|
|
|
5902
5976
|
const hasEntry = (path) => entries.some((entry) => entry.name.toLowerCase() === path.toLowerCase());
|
|
5903
5977
|
const contentXml = zip.file(/(^|\/)content\.xml$/i)[0];
|
|
5904
5978
|
if (hasEntry("word/document.xml")) {
|
|
5905
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OOXML Word \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 DOCX \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5906
5979
|
await renderDocx(panel, arrayBuffer);
|
|
5907
5980
|
return true;
|
|
5908
5981
|
}
|
|
5909
5982
|
if (hasEntry("xl/workbook.xml")) {
|
|
5910
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OOXML Workbook \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 XLSX \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5911
5983
|
await renderSheet(panel, arrayBuffer, extension);
|
|
5912
5984
|
return true;
|
|
5913
5985
|
}
|
|
5914
5986
|
if (hasEntry("ppt/presentation.xml")) {
|
|
5915
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OOXML Presentation \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 PPTX \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5916
5987
|
await renderPptx(panel, arrayBuffer);
|
|
5917
5988
|
return true;
|
|
5918
5989
|
}
|
|
5919
5990
|
if (contentXml) {
|
|
5920
5991
|
const xml = await contentXml.async("text");
|
|
5921
5992
|
if (/<office:spreadsheet\b|<table:table\b/i.test(xml)) {
|
|
5922
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OpenDocument Spreadsheet \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 ODS \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5923
5993
|
renderParsedSheets(panel, parseFlatOds(xml), `${extension.toUpperCase()} \u6587\u4EF6\u672A\u89E3\u6790\u5230\u8868\u683C\u3002`);
|
|
5924
5994
|
return true;
|
|
5925
5995
|
}
|
|
5926
5996
|
if (/<office:presentation\b|<draw:page\b/i.test(xml)) {
|
|
5927
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OpenDocument Presentation \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 ODP \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5928
5997
|
renderOpenDocumentPresentation(panel, `${extension.toUpperCase()} \u6F14\u793A\u6587\u7A3F`, xml, await extractZipImages(zip, /^Pictures\//));
|
|
5929
5998
|
return true;
|
|
5930
5999
|
}
|
|
5931
6000
|
if (/<office:text\b|<text:p\b/i.test(xml)) {
|
|
5932
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OpenDocument Text \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 ODT \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5933
6001
|
renderOpenDocumentXml(panel, `${extension.toUpperCase()} \u6587\u6863`, xml);
|
|
5934
6002
|
return true;
|
|
5935
6003
|
}
|
|
@@ -5955,14 +6023,6 @@ async function renderPackagedOfficePreview(panel, arrayBuffer, extension) {
|
|
|
5955
6023
|
}
|
|
5956
6024
|
return false;
|
|
5957
6025
|
}
|
|
5958
|
-
function renderOfficePackageNotice(panel, extension, message) {
|
|
5959
|
-
const section = createSection("\u517C\u5BB9\u5305\u8BC6\u522B");
|
|
5960
|
-
const note = document.createElement("p");
|
|
5961
|
-
note.className = "ofv-office-package-note";
|
|
5962
|
-
note.textContent = `.${extension} ${message}`;
|
|
5963
|
-
section.append(note);
|
|
5964
|
-
panel.append(section);
|
|
5965
|
-
}
|
|
5966
6026
|
function renderOfficePackageStructure(panel, extension, entries, message, metadata) {
|
|
5967
6027
|
const section = createSection("Office \u5305\u7ED3\u6784\u9884\u89C8");
|
|
5968
6028
|
const note = document.createElement("p");
|
|
@@ -6610,7 +6670,7 @@ function rtfToText(rtf) {
|
|
|
6610
6670
|
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();
|
|
6611
6671
|
}
|
|
6612
6672
|
async function readTextFromBuffer(arrayBuffer) {
|
|
6613
|
-
return
|
|
6673
|
+
return decodeTextBuffer(arrayBuffer);
|
|
6614
6674
|
}
|
|
6615
6675
|
function mimeTypeFromPath(path) {
|
|
6616
6676
|
const extension = path.split(".").pop()?.toLowerCase();
|
|
@@ -6677,36 +6737,72 @@ function ofdPlugin() {
|
|
|
6677
6737
|
}
|
|
6678
6738
|
};
|
|
6679
6739
|
}
|
|
6680
|
-
const
|
|
6681
|
-
const pages = await readOfdPages(entries,
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6740
|
+
const context = await readOfdContext(entries);
|
|
6741
|
+
const pages = await readOfdPages(entries, context);
|
|
6742
|
+
let zoom = 1;
|
|
6743
|
+
let rotation = 0;
|
|
6744
|
+
const applyZoom = () => {
|
|
6745
|
+
panel.style.setProperty("--ofv-ofd-zoom", formatOfdCssNumber(zoom));
|
|
6746
|
+
ctx.toolbar?.setZoom(zoom);
|
|
6747
|
+
};
|
|
6748
|
+
const applyRotation = () => {
|
|
6749
|
+
const normalizedRotation = (rotation % 360 + 360) % 360;
|
|
6750
|
+
panel.style.setProperty("--ofv-ofd-rotation", `${normalizedRotation}deg`);
|
|
6751
|
+
panel.classList.toggle("is-ofd-rotated-sideways", normalizedRotation === 90 || normalizedRotation === 270);
|
|
6752
|
+
};
|
|
6687
6753
|
if (pages.length > 0) {
|
|
6688
6754
|
const pagesWrap = document.createElement("div");
|
|
6689
6755
|
pagesWrap.className = "ofv-ofd-pages";
|
|
6690
6756
|
for (const page of pages) {
|
|
6691
6757
|
pagesWrap.append(renderOfdPage(page));
|
|
6692
6758
|
}
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
li.textContent = entry.name;
|
|
6704
|
-
ul.append(li);
|
|
6705
|
-
}
|
|
6706
|
-
list.append(ul);
|
|
6707
|
-
panel.append(section, list);
|
|
6759
|
+
panel.append(pagesWrap);
|
|
6760
|
+
applyZoom();
|
|
6761
|
+
applyRotation();
|
|
6762
|
+
}
|
|
6763
|
+
if (pages.length === 0) {
|
|
6764
|
+
const content = document.createElement("pre");
|
|
6765
|
+
content.className = "ofv-text-block";
|
|
6766
|
+
content.textContent = textFragments.slice(0, 300).join("\n") || "\u672A\u63D0\u53D6\u5230\u53EF\u8BFB\u6587\u672C\u3002";
|
|
6767
|
+
panel.append(content);
|
|
6768
|
+
}
|
|
6708
6769
|
return {
|
|
6770
|
+
canCommand(command) {
|
|
6771
|
+
return pages.length > 0 && (command === "zoom-in" || command === "zoom-out" || command === "zoom-reset" || command === "rotate-right" || command === "rotate-left");
|
|
6772
|
+
},
|
|
6773
|
+
command(command) {
|
|
6774
|
+
if (pages.length === 0) {
|
|
6775
|
+
return false;
|
|
6776
|
+
}
|
|
6777
|
+
if (command === "zoom-in") {
|
|
6778
|
+
zoom = Math.min(4, zoom + 0.15);
|
|
6779
|
+
applyZoom();
|
|
6780
|
+
return true;
|
|
6781
|
+
}
|
|
6782
|
+
if (command === "zoom-out") {
|
|
6783
|
+
zoom = Math.max(0.25, zoom - 0.15);
|
|
6784
|
+
applyZoom();
|
|
6785
|
+
return true;
|
|
6786
|
+
}
|
|
6787
|
+
if (command === "zoom-reset") {
|
|
6788
|
+
zoom = 1;
|
|
6789
|
+
applyZoom();
|
|
6790
|
+
return true;
|
|
6791
|
+
}
|
|
6792
|
+
if (command === "rotate-right") {
|
|
6793
|
+
rotation += 90;
|
|
6794
|
+
applyRotation();
|
|
6795
|
+
return true;
|
|
6796
|
+
}
|
|
6797
|
+
if (command === "rotate-left") {
|
|
6798
|
+
rotation -= 90;
|
|
6799
|
+
applyRotation();
|
|
6800
|
+
return true;
|
|
6801
|
+
}
|
|
6802
|
+
return false;
|
|
6803
|
+
},
|
|
6709
6804
|
destroy() {
|
|
6805
|
+
ctx.toolbar?.setZoom(void 0);
|
|
6710
6806
|
panel.remove();
|
|
6711
6807
|
revokeObjectUrl(url, isExternal);
|
|
6712
6808
|
}
|
|
@@ -6714,76 +6810,71 @@ function ofdPlugin() {
|
|
|
6714
6810
|
}
|
|
6715
6811
|
};
|
|
6716
6812
|
}
|
|
6717
|
-
function
|
|
6718
|
-
const summary = document.createElement("div");
|
|
6719
|
-
summary.className = "ofv-ofd-summary";
|
|
6720
|
-
const xmlEntries = entries.filter((entry) => entry.name.endsWith(".xml")).length;
|
|
6721
|
-
const textCount = pages.reduce((count, page) => count + page.texts.length, 0);
|
|
6722
|
-
const pathCount = pages.reduce((count, page) => count + page.paths.length, 0);
|
|
6723
|
-
const lineCount = pages.reduce((count, page) => count + page.lines.length, 0);
|
|
6724
|
-
const imageCount = pages.reduce((count, page) => count + page.images.length, 0);
|
|
6725
|
-
const textLength = pages.reduce((count, page) => count + page.texts.reduce((inner, item) => inner + item.text.length, 0), 0);
|
|
6726
|
-
appendOfdSummary(summary, "\u6587\u4EF6", String(entries.length));
|
|
6727
|
-
appendOfdSummary(summary, "XML", String(xmlEntries));
|
|
6728
|
-
appendOfdSummary(summary, "\u9875\u9762", String(pages.length));
|
|
6729
|
-
appendOfdSummary(summary, "\u6587\u672C", String(textCount));
|
|
6730
|
-
appendOfdSummary(summary, "\u8DEF\u5F84", String(pathCount));
|
|
6731
|
-
appendOfdSummary(summary, "\u7EBF\u6761", String(lineCount));
|
|
6732
|
-
appendOfdSummary(summary, "\u56FE\u7247\u5BF9\u8C61", String(imageCount));
|
|
6733
|
-
appendOfdSummary(summary, "\u56FE\u7247\u8D44\u6E90", String(uniqueOfdImageResources(images)));
|
|
6734
|
-
if (textLength > 0) {
|
|
6735
|
-
appendOfdSummary(summary, "\u6587\u5B57\u957F\u5EA6", String(textLength));
|
|
6736
|
-
}
|
|
6737
|
-
const sizes = formatOfdPageSizes(pages);
|
|
6738
|
-
if (sizes) {
|
|
6739
|
-
appendOfdSummary(summary, "\u9875\u9762\u5C3A\u5BF8", sizes);
|
|
6740
|
-
}
|
|
6741
|
-
return summary;
|
|
6742
|
-
}
|
|
6743
|
-
function appendOfdSummary(parent, label, value) {
|
|
6744
|
-
const item = document.createElement("span");
|
|
6745
|
-
const key = document.createElement("span");
|
|
6746
|
-
key.textContent = label;
|
|
6747
|
-
const content = document.createElement("strong");
|
|
6748
|
-
content.textContent = value;
|
|
6749
|
-
item.append(key, content);
|
|
6750
|
-
parent.append(item);
|
|
6751
|
-
}
|
|
6752
|
-
function uniqueOfdImageResources(images) {
|
|
6753
|
-
return new Set(images.values()).size;
|
|
6754
|
-
}
|
|
6755
|
-
function formatOfdPageSizes(pages) {
|
|
6756
|
-
const counts = /* @__PURE__ */ new Map();
|
|
6757
|
-
for (const page of pages) {
|
|
6758
|
-
const key = `${Math.round(page.width)} x ${Math.round(page.height)}`;
|
|
6759
|
-
counts.set(key, (counts.get(key) || 0) + 1);
|
|
6760
|
-
}
|
|
6761
|
-
return [...counts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 4).map(([size, count]) => count > 1 ? `${size} (${count})` : size).join(", ");
|
|
6762
|
-
}
|
|
6763
|
-
async function readOfdPages(entries, images) {
|
|
6813
|
+
async function readOfdPages(entries, context) {
|
|
6764
6814
|
const pages = [];
|
|
6765
6815
|
const pageEntries = entries.filter((entry) => /(^|\/)Pages\/Page_[^/]+\/Content\.xml$/i.test(entry.name) || /(^|\/)Page_[^/]+\/Content\.xml$/i.test(entry.name)).slice(0, 80);
|
|
6766
6816
|
for (const entry of pageEntries) {
|
|
6767
6817
|
const xml = await entry.async("text");
|
|
6768
|
-
const
|
|
6818
|
+
const templates = await readPageTemplates(xml, context, entries);
|
|
6819
|
+
const page = parseOfdPage(entry.name, xml, context.images, context.fonts, templates, context.pageSize);
|
|
6769
6820
|
if (page.texts.length > 0 || page.paths.length > 0 || page.lines.length > 0 || page.images.length > 0) {
|
|
6770
6821
|
pages.push(page);
|
|
6771
6822
|
}
|
|
6772
6823
|
}
|
|
6773
6824
|
return pages;
|
|
6774
6825
|
}
|
|
6775
|
-
function
|
|
6826
|
+
async function readPageTemplates(xml, context, entries) {
|
|
6827
|
+
const doc = new DOMParser().parseFromString(xml, "application/xml");
|
|
6828
|
+
if (doc.querySelector("parsererror")) {
|
|
6829
|
+
return [];
|
|
6830
|
+
}
|
|
6831
|
+
const templateIds = Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "Template").map((element) => getOfdAttribute(element, "TemplateID") || getOfdAttribute(element, "ID")).filter((id) => Boolean(id));
|
|
6832
|
+
const templates = [];
|
|
6833
|
+
for (const id of templateIds) {
|
|
6834
|
+
const path = context.templates.get(id);
|
|
6835
|
+
const entry = path ? findOfdEntry(entries, path) : void 0;
|
|
6836
|
+
if (entry) {
|
|
6837
|
+
templates.push(await entry.async("text"));
|
|
6838
|
+
}
|
|
6839
|
+
}
|
|
6840
|
+
return templates;
|
|
6841
|
+
}
|
|
6842
|
+
function parseOfdPage(name, xml, images, fonts, templateXmls = [], defaultPageSize) {
|
|
6776
6843
|
const doc = new DOMParser().parseFromString(xml, "application/xml");
|
|
6777
6844
|
if (doc.querySelector("parsererror")) {
|
|
6778
6845
|
return { name, width: 210, height: 297, texts: [], paths: [], lines: [], images: [] };
|
|
6779
6846
|
}
|
|
6780
|
-
const pageSize = parseOfdPageSize(doc);
|
|
6847
|
+
const pageSize = parseOfdPageSize(doc, defaultPageSize);
|
|
6848
|
+
const templatePages = templateXmls.map((templateXml) => {
|
|
6849
|
+
const templateDoc = new DOMParser().parseFromString(templateXml, "application/xml");
|
|
6850
|
+
return templateDoc.querySelector("parsererror") ? emptyOfdPageContent() : parseOfdPageContent(templateDoc, images, fonts);
|
|
6851
|
+
});
|
|
6852
|
+
const pageContent = parseOfdPageContent(doc, images, fonts);
|
|
6853
|
+
const texts = [...templatePages.flatMap((page) => page.texts), ...pageContent.texts];
|
|
6854
|
+
const paths = [...templatePages.flatMap((page) => page.paths), ...pageContent.paths];
|
|
6855
|
+
const lines = [...templatePages.flatMap((page) => page.lines), ...pageContent.lines];
|
|
6856
|
+
const imageObjects = [...templatePages.flatMap((page) => page.images), ...pageContent.images];
|
|
6857
|
+
if (pageSize.explicit) {
|
|
6858
|
+
return { name, width: pageSize.width, height: pageSize.height, texts, paths, lines, images: imageObjects };
|
|
6859
|
+
}
|
|
6860
|
+
const bounds = createOfdBounds(texts, paths, lines, imageObjects);
|
|
6861
|
+
const width = Math.max(pageSize.width, ...bounds.map((item) => item.x + item.width + 12));
|
|
6862
|
+
const height = Math.max(pageSize.height, ...bounds.map((item) => item.y + item.height + 12));
|
|
6863
|
+
return { name, width, height, texts, paths, lines, images: imageObjects };
|
|
6864
|
+
}
|
|
6865
|
+
function parseOfdPageContent(doc, images, fonts) {
|
|
6781
6866
|
const textObjects = Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "TextObject");
|
|
6782
|
-
const texts = textObjects.flatMap((element) => parseOfdTextObject(element));
|
|
6867
|
+
const texts = textObjects.flatMap((element) => parseOfdTextObject(element, fonts));
|
|
6783
6868
|
const paths = Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "PathObject").flatMap((element) => parseOfdPathObject(element));
|
|
6784
6869
|
const lines = Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "LineObject").flatMap((element) => parseOfdLineObject(element));
|
|
6785
6870
|
const imageObjects = Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "ImageObject").flatMap((element) => parseOfdImageObject(element, images));
|
|
6786
|
-
|
|
6871
|
+
return { texts, paths, lines, images: imageObjects };
|
|
6872
|
+
}
|
|
6873
|
+
function emptyOfdPageContent() {
|
|
6874
|
+
return { texts: [], paths: [], lines: [], images: [] };
|
|
6875
|
+
}
|
|
6876
|
+
function createOfdBounds(texts, paths, lines, imageObjects) {
|
|
6877
|
+
return [
|
|
6787
6878
|
...texts.map((item) => ({ x: item.x, y: item.y, width: item.width, height: item.height })),
|
|
6788
6879
|
...paths.map((item) => ({ x: item.x, y: item.y, width: item.width, height: item.height })),
|
|
6789
6880
|
...lines.map((item) => ({
|
|
@@ -6794,38 +6885,63 @@ function parseOfdPage(name, xml, images) {
|
|
|
6794
6885
|
})),
|
|
6795
6886
|
...imageObjects.map((item) => ({ x: item.x, y: item.y, width: item.width, height: item.height }))
|
|
6796
6887
|
];
|
|
6797
|
-
const width = Math.max(pageSize.width, ...bounds.map((item) => item.x + item.width + 12));
|
|
6798
|
-
const height = Math.max(pageSize.height, ...bounds.map((item) => item.y + item.height + 12));
|
|
6799
|
-
return { name, width, height, texts, paths, lines, images: imageObjects };
|
|
6800
6888
|
}
|
|
6801
|
-
function parseOfdTextObject(element) {
|
|
6889
|
+
function parseOfdTextObject(element, fonts) {
|
|
6802
6890
|
const boundary = parseBoundary(getOfdAttribute(element, "Boundary"));
|
|
6803
6891
|
const size = finiteNumber2(getOfdAttribute(element, "Size"), Math.max(4, boundary.height || 5));
|
|
6804
6892
|
const color = parseOfdColor(element, "#111827");
|
|
6805
6893
|
const weight = finiteNumber2(getOfdAttribute(element, "Weight"), 400) >= 600 ? "700" : "400";
|
|
6806
|
-
const
|
|
6894
|
+
const fontFamily = fontStackForOfdFont(fonts.get(getOfdAttribute(element, "Font") || ""));
|
|
6895
|
+
const objectLetterSpacing = getOfdAttribute(element, "DeltaX") ? 0.5 : void 0;
|
|
6807
6896
|
const textCodes = Array.from(element.getElementsByTagName("*")).filter((child) => child.localName === "TextCode");
|
|
6808
6897
|
if (textCodes.length === 0) {
|
|
6809
6898
|
return [];
|
|
6810
6899
|
}
|
|
6811
|
-
return textCodes.
|
|
6900
|
+
return textCodes.flatMap((code) => {
|
|
6812
6901
|
const x = boundary.x + finiteNumber2(getOfdAttribute(code, "X"), 0);
|
|
6813
6902
|
const y = boundary.y + finiteNumber2(getOfdAttribute(code, "Y"), 0);
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
6903
|
+
const text = code.textContent?.trim() || "";
|
|
6904
|
+
const deltaX = parseOfdDeltaX(getOfdAttribute(code, "DeltaX"));
|
|
6905
|
+
const align = deltaX ? "start" : inferOfdTextAlign(text, boundary);
|
|
6906
|
+
const deltaY = getOfdAttribute(code, "DeltaY");
|
|
6907
|
+
if (deltaY && text.length > 1) {
|
|
6908
|
+
const step = parseOfdDeltaStep(deltaY, size);
|
|
6909
|
+
return Array.from(text).map((char, index) => ({
|
|
6910
|
+
text: char,
|
|
6911
|
+
x,
|
|
6912
|
+
y: y + index * step,
|
|
6913
|
+
width: boundary.width,
|
|
6914
|
+
height: boundary.height,
|
|
6915
|
+
size,
|
|
6916
|
+
color,
|
|
6917
|
+
weight,
|
|
6918
|
+
fontFamily,
|
|
6919
|
+
letterSpacing: objectLetterSpacing,
|
|
6920
|
+
vertical: true,
|
|
6921
|
+
align
|
|
6922
|
+
}));
|
|
6923
|
+
}
|
|
6924
|
+
return [
|
|
6925
|
+
{
|
|
6926
|
+
text,
|
|
6927
|
+
x,
|
|
6928
|
+
y,
|
|
6929
|
+
width: boundary.width,
|
|
6930
|
+
height: boundary.height,
|
|
6931
|
+
size,
|
|
6932
|
+
color,
|
|
6933
|
+
weight,
|
|
6934
|
+
fontFamily,
|
|
6935
|
+
letterSpacing: deltaX ? void 0 : objectLetterSpacing,
|
|
6936
|
+
deltaX,
|
|
6937
|
+
align
|
|
6938
|
+
}
|
|
6939
|
+
];
|
|
6825
6940
|
}).filter((item) => item.text);
|
|
6826
6941
|
}
|
|
6827
6942
|
function parseOfdPathObject(element) {
|
|
6828
6943
|
const boundary = parseBoundary(getOfdAttribute(element, "Boundary"));
|
|
6944
|
+
const ctm = parseOfdCtm(getOfdAttribute(element, "CTM"));
|
|
6829
6945
|
const commands = Array.from(element.getElementsByTagName("*")).filter(
|
|
6830
6946
|
(child) => child.localName === "AbbreviatedData" || child.localName === "PathData"
|
|
6831
6947
|
);
|
|
@@ -6840,9 +6956,10 @@ function parseOfdPathObject(element) {
|
|
|
6840
6956
|
y: boundary.y,
|
|
6841
6957
|
width: boundary.width,
|
|
6842
6958
|
height: boundary.height,
|
|
6843
|
-
stroke: parseOfdColor(element, "#
|
|
6959
|
+
stroke: parseOfdColor(element, "#111827", "StrokeColor"),
|
|
6844
6960
|
fill: parseOfdFill(element),
|
|
6845
|
-
strokeWidth: finiteNumber2(getOfdAttribute(element, "LineWidth"), 1)
|
|
6961
|
+
strokeWidth: finiteNumber2(getOfdAttribute(element, "LineWidth"), 1),
|
|
6962
|
+
transform: createOfdPathTransform(boundary.x, boundary.y, ctm)
|
|
6846
6963
|
}
|
|
6847
6964
|
];
|
|
6848
6965
|
}
|
|
@@ -6859,7 +6976,7 @@ function parseOfdLineObject(element) {
|
|
|
6859
6976
|
y1: boundary.y + start.y,
|
|
6860
6977
|
x2: boundary.x + end.x,
|
|
6861
6978
|
y2: boundary.y + end.y,
|
|
6862
|
-
stroke: parseOfdColor(element, "#
|
|
6979
|
+
stroke: parseOfdColor(element, "#111827"),
|
|
6863
6980
|
strokeWidth: finiteNumber2(getOfdAttribute(element, "LineWidth"), 1)
|
|
6864
6981
|
}
|
|
6865
6982
|
];
|
|
@@ -6881,6 +6998,8 @@ function parseOfdImageObject(element, images) {
|
|
|
6881
6998
|
function renderOfdPage(page) {
|
|
6882
6999
|
const figure = document.createElement("figure");
|
|
6883
7000
|
figure.className = "ofv-ofd-page";
|
|
7001
|
+
figure.style.setProperty("--ofv-ofd-page-width", `${formatOfdCssNumber(page.width)}mm`);
|
|
7002
|
+
figure.style.setProperty("--ofv-ofd-page-height", `${formatOfdCssNumber(page.height)}mm`);
|
|
6884
7003
|
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
6885
7004
|
svg.setAttribute("viewBox", `0 0 ${page.width} ${page.height}`);
|
|
6886
7005
|
svg.setAttribute("role", "img");
|
|
@@ -6917,7 +7036,7 @@ function renderOfdPage(page) {
|
|
|
6917
7036
|
for (const item of page.paths) {
|
|
6918
7037
|
const path = document.createElementNS(svg.namespaceURI, "path");
|
|
6919
7038
|
path.setAttribute("d", item.d);
|
|
6920
|
-
path.setAttribute("transform",
|
|
7039
|
+
path.setAttribute("transform", item.transform);
|
|
6921
7040
|
path.setAttribute("fill", item.fill);
|
|
6922
7041
|
path.setAttribute("stroke", item.stroke);
|
|
6923
7042
|
path.setAttribute("stroke-width", String(item.strokeWidth));
|
|
@@ -6936,22 +7055,63 @@ function renderOfdPage(page) {
|
|
|
6936
7055
|
}
|
|
6937
7056
|
for (const item of page.texts) {
|
|
6938
7057
|
const text = document.createElementNS(svg.namespaceURI, "text");
|
|
6939
|
-
text.setAttribute("x", String(item.x));
|
|
6940
|
-
text.setAttribute("y", String(item.y
|
|
7058
|
+
text.setAttribute("x", String(item.align === "end" ? item.x + item.width : item.x));
|
|
7059
|
+
text.setAttribute("y", String(item.y));
|
|
6941
7060
|
text.setAttribute("font-size", String(item.size));
|
|
6942
7061
|
text.setAttribute("fill", item.color);
|
|
6943
7062
|
text.setAttribute("font-weight", item.weight);
|
|
7063
|
+
text.setAttribute("font-family", item.fontFamily);
|
|
6944
7064
|
if (item.letterSpacing !== void 0) {
|
|
6945
7065
|
text.setAttribute("letter-spacing", String(item.letterSpacing));
|
|
6946
7066
|
}
|
|
6947
|
-
|
|
7067
|
+
if (item.deltaX && item.deltaX.length > 0 && item.align !== "end") {
|
|
7068
|
+
const chars = Array.from(item.text);
|
|
7069
|
+
let x = item.x;
|
|
7070
|
+
for (let index = 0; index < chars.length; index += 1) {
|
|
7071
|
+
const span = document.createElementNS(svg.namespaceURI, "tspan");
|
|
7072
|
+
span.setAttribute("x", String(x));
|
|
7073
|
+
span.setAttribute("y", String(item.y));
|
|
7074
|
+
if (index < chars.length - 1) {
|
|
7075
|
+
x += item.deltaX[Math.min(index, item.deltaX.length - 1)] || item.size;
|
|
7076
|
+
}
|
|
7077
|
+
span.textContent = chars[index];
|
|
7078
|
+
text.append(span);
|
|
7079
|
+
}
|
|
7080
|
+
} else {
|
|
7081
|
+
if (item.align === "end") {
|
|
7082
|
+
text.setAttribute("text-anchor", "end");
|
|
7083
|
+
}
|
|
7084
|
+
text.textContent = item.text;
|
|
7085
|
+
}
|
|
6948
7086
|
svg.append(text);
|
|
6949
7087
|
}
|
|
6950
|
-
|
|
6951
|
-
caption.textContent = `${page.name} \xB7 ${page.texts.length} text \xB7 ${page.paths.length} path \xB7 ${page.lines.length} line \xB7 ${page.images.length} image`;
|
|
6952
|
-
figure.append(svg, caption);
|
|
7088
|
+
figure.append(svg);
|
|
6953
7089
|
return figure;
|
|
6954
7090
|
}
|
|
7091
|
+
async function readOfdContext(entries) {
|
|
7092
|
+
const images = await readOfdImages(entries);
|
|
7093
|
+
const fonts = await readOfdFonts(entries);
|
|
7094
|
+
const { templates, pageSize } = await readOfdDocumentInfo(entries);
|
|
7095
|
+
return { images, templates, fonts, pageSize };
|
|
7096
|
+
}
|
|
7097
|
+
async function readOfdFonts(entries) {
|
|
7098
|
+
const fonts = /* @__PURE__ */ new Map();
|
|
7099
|
+
for (const entry of entries.filter((item) => /(?:^|\/)(?:DocumentRes|PublicRes)\.xml$/i.test(item.name))) {
|
|
7100
|
+
const xml = await entry.async("text");
|
|
7101
|
+
const doc = new DOMParser().parseFromString(xml, "application/xml");
|
|
7102
|
+
if (doc.querySelector("parsererror")) {
|
|
7103
|
+
continue;
|
|
7104
|
+
}
|
|
7105
|
+
for (const font of Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "Font")) {
|
|
7106
|
+
const id = getOfdAttribute(font, "ID");
|
|
7107
|
+
const name = getOfdAttribute(font, "FontName") || getOfdAttribute(font, "FamilyName");
|
|
7108
|
+
if (id && name) {
|
|
7109
|
+
fonts.set(id, name.trim());
|
|
7110
|
+
}
|
|
7111
|
+
}
|
|
7112
|
+
}
|
|
7113
|
+
return fonts;
|
|
7114
|
+
}
|
|
6955
7115
|
async function readOfdImages(entries) {
|
|
6956
7116
|
const images = /* @__PURE__ */ new Map();
|
|
6957
7117
|
for (const entry of entries.filter((item) => /\.(?:png|jpe?g|gif|bmp|webp)$/i.test(item.name)).slice(0, 80)) {
|
|
@@ -6966,17 +7126,69 @@ async function readOfdImages(entries) {
|
|
|
6966
7126
|
images.set(entry.name, href);
|
|
6967
7127
|
images.set(entry.name.split("/").pop() || entry.name, href);
|
|
6968
7128
|
}
|
|
7129
|
+
for (const entry of entries.filter((item) => /(?:^|\/)(?:DocumentRes|PublicRes)\.xml$/i.test(item.name))) {
|
|
7130
|
+
const xml = await entry.async("text");
|
|
7131
|
+
const doc = new DOMParser().parseFromString(xml, "application/xml");
|
|
7132
|
+
if (doc.querySelector("parsererror")) {
|
|
7133
|
+
continue;
|
|
7134
|
+
}
|
|
7135
|
+
const baseLoc = getOfdAttribute(doc.documentElement, "BaseLoc") || "";
|
|
7136
|
+
const resourceDir = joinOfdPath(directoryName2(entry.name), baseLoc);
|
|
7137
|
+
for (const media of Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "MultiMedia")) {
|
|
7138
|
+
const id = getOfdAttribute(media, "ID");
|
|
7139
|
+
const mediaFile = findOfdChild(media, "MediaFile")?.textContent?.trim();
|
|
7140
|
+
if (!id || !mediaFile) {
|
|
7141
|
+
continue;
|
|
7142
|
+
}
|
|
7143
|
+
const imageEntry = findOfdEntry(entries, joinOfdPath(resourceDir, mediaFile)) || findOfdEntry(entries, mediaFile);
|
|
7144
|
+
const href = imageEntry ? images.get(imageEntry.name) || images.get(imageEntry.name.split("/").pop() || imageEntry.name) : void 0;
|
|
7145
|
+
if (href) {
|
|
7146
|
+
images.set(id, href);
|
|
7147
|
+
}
|
|
7148
|
+
}
|
|
7149
|
+
}
|
|
6969
7150
|
return images;
|
|
6970
7151
|
}
|
|
6971
|
-
function
|
|
7152
|
+
async function readOfdDocumentInfo(entries) {
|
|
7153
|
+
const templates = /* @__PURE__ */ new Map();
|
|
7154
|
+
let pageSize;
|
|
7155
|
+
for (const entry of entries.filter((item) => /(?:^|\/)Document\.xml$/i.test(item.name))) {
|
|
7156
|
+
const xml = await entry.async("text");
|
|
7157
|
+
const doc = new DOMParser().parseFromString(xml, "application/xml");
|
|
7158
|
+
if (doc.querySelector("parsererror")) {
|
|
7159
|
+
continue;
|
|
7160
|
+
}
|
|
7161
|
+
const documentDir = directoryName2(entry.name);
|
|
7162
|
+
const physicalBox = Array.from(doc.getElementsByTagName("*")).find((element) => element.localName === "PageArea")?.getElementsByTagName("*");
|
|
7163
|
+
const pageAreaBox = physicalBox ? Array.from(physicalBox).find((element) => element.localName === "PhysicalBox") : void 0;
|
|
7164
|
+
if (pageAreaBox?.textContent) {
|
|
7165
|
+
const box = parseBoundary(pageAreaBox.textContent);
|
|
7166
|
+
if (box.width > 0 && box.height > 0) {
|
|
7167
|
+
pageSize = { width: box.width, height: box.height };
|
|
7168
|
+
}
|
|
7169
|
+
}
|
|
7170
|
+
for (const template of Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "TemplatePage")) {
|
|
7171
|
+
const id = getOfdAttribute(template, "ID");
|
|
7172
|
+
const baseLoc = getOfdAttribute(template, "BaseLoc");
|
|
7173
|
+
if (id && baseLoc) {
|
|
7174
|
+
templates.set(id, joinOfdPath(documentDir, baseLoc));
|
|
7175
|
+
}
|
|
7176
|
+
}
|
|
7177
|
+
}
|
|
7178
|
+
return { templates, pageSize };
|
|
7179
|
+
}
|
|
7180
|
+
function parseOfdPageSize(doc, defaultPageSize) {
|
|
7181
|
+
if (defaultPageSize) {
|
|
7182
|
+
return { ...defaultPageSize, explicit: true };
|
|
7183
|
+
}
|
|
6972
7184
|
const physicalBox = Array.from(doc.getElementsByTagName("*")).find((element) => element.localName === "PhysicalBox");
|
|
6973
7185
|
if (physicalBox?.textContent) {
|
|
6974
7186
|
const box = parseBoundary(physicalBox.textContent);
|
|
6975
7187
|
if (box.width > 0 && box.height > 0) {
|
|
6976
|
-
return { width: box.width, height: box.height };
|
|
7188
|
+
return { width: box.width, height: box.height, explicit: true };
|
|
6977
7189
|
}
|
|
6978
7190
|
}
|
|
6979
|
-
return { width: 210, height: 297 };
|
|
7191
|
+
return { width: 210, height: 297, explicit: false };
|
|
6980
7192
|
}
|
|
6981
7193
|
function parseOfdColor(element, fallback, preferredLocalName = "FillColor") {
|
|
6982
7194
|
const colorElement = findOfdChild(element, preferredLocalName) || findOfdChild(element, "StrokeColor") || findOfdChild(element, "FillColor");
|
|
@@ -7007,6 +7219,101 @@ function parsePoint(value, fallback) {
|
|
|
7007
7219
|
function normalizeOfdPathData(value) {
|
|
7008
7220
|
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();
|
|
7009
7221
|
}
|
|
7222
|
+
function parseOfdCtm(value) {
|
|
7223
|
+
const parts = (value || "").trim().split(/\s+/).map((part) => Number(part));
|
|
7224
|
+
if (parts.length !== 6 || parts.some((part) => !Number.isFinite(part))) {
|
|
7225
|
+
return void 0;
|
|
7226
|
+
}
|
|
7227
|
+
return parts;
|
|
7228
|
+
}
|
|
7229
|
+
function createOfdPathTransform(x, y, ctm) {
|
|
7230
|
+
if (!ctm) {
|
|
7231
|
+
return `translate(${x} ${y})`;
|
|
7232
|
+
}
|
|
7233
|
+
const [a, b, c, d, e, f] = ctm;
|
|
7234
|
+
return `translate(${x} ${y}) matrix(${a} ${b} ${c} ${d} ${e} ${f})`;
|
|
7235
|
+
}
|
|
7236
|
+
function parseOfdDeltaStep(value, fallback) {
|
|
7237
|
+
const numbers = value.match(/-?\d+(?:\.\d+)?/g)?.map((part) => Number(part)).filter((part) => Number.isFinite(part)) || [];
|
|
7238
|
+
return numbers.length > 0 ? numbers[numbers.length - 1] : fallback;
|
|
7239
|
+
}
|
|
7240
|
+
function parseOfdDeltaX(value) {
|
|
7241
|
+
if (!value) {
|
|
7242
|
+
return void 0;
|
|
7243
|
+
}
|
|
7244
|
+
const parts = value.match(/[a-z]+|-?\d+(?:\.\d+)?/gi) || [];
|
|
7245
|
+
const deltas = [];
|
|
7246
|
+
for (let index = 0; index < parts.length; index += 1) {
|
|
7247
|
+
const token = parts[index];
|
|
7248
|
+
if (/^g$/i.test(token)) {
|
|
7249
|
+
const count = Number(parts[index + 1]);
|
|
7250
|
+
const step = Number(parts[index + 2]);
|
|
7251
|
+
if (Number.isFinite(count) && Number.isFinite(step)) {
|
|
7252
|
+
deltas.push(...Array.from({ length: Math.max(0, Math.floor(count)) }, () => step));
|
|
7253
|
+
}
|
|
7254
|
+
index += 2;
|
|
7255
|
+
continue;
|
|
7256
|
+
}
|
|
7257
|
+
const numeric = Number(token);
|
|
7258
|
+
if (Number.isFinite(numeric)) {
|
|
7259
|
+
deltas.push(numeric);
|
|
7260
|
+
}
|
|
7261
|
+
}
|
|
7262
|
+
return deltas.length > 0 ? deltas : void 0;
|
|
7263
|
+
}
|
|
7264
|
+
function fontStackForOfdFont(fontName) {
|
|
7265
|
+
const normalized = (fontName || "").trim().toLowerCase();
|
|
7266
|
+
if (normalized.includes("courier")) {
|
|
7267
|
+
return '"Courier New", Courier, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace';
|
|
7268
|
+
}
|
|
7269
|
+
if (normalized.includes("kaiti") || normalized.includes("kai") || normalized.includes("\u6977")) {
|
|
7270
|
+
return '"STKaiti", "Kaiti SC", "KaiTi", "\u6977\u4F53", serif';
|
|
7271
|
+
}
|
|
7272
|
+
if (normalized.includes("simsun") || normalized.includes("simsong") || normalized.includes("song") || normalized.includes("\u5B8B")) {
|
|
7273
|
+
return '"SimSong", "Songti SC", "STSong", SimSun, "\u5B8B\u4F53", serif';
|
|
7274
|
+
}
|
|
7275
|
+
if (normalized.includes("hei") || normalized.includes("\u9ED1")) {
|
|
7276
|
+
return '"PingFang SC", "Microsoft YaHei", SimHei, sans-serif';
|
|
7277
|
+
}
|
|
7278
|
+
return '"SimSong", "Songti SC", "STSong", SimSun, "Noto Serif CJK SC", serif';
|
|
7279
|
+
}
|
|
7280
|
+
function inferOfdTextAlign(text, boundary) {
|
|
7281
|
+
const normalized = text.trim();
|
|
7282
|
+
if (!/^[¥¥]?\d+(?:\.\d+)?%?$/.test(normalized)) {
|
|
7283
|
+
return "start";
|
|
7284
|
+
}
|
|
7285
|
+
if (boundary.x >= 75 || boundary.width <= 30) {
|
|
7286
|
+
return "end";
|
|
7287
|
+
}
|
|
7288
|
+
return "start";
|
|
7289
|
+
}
|
|
7290
|
+
function findOfdEntry(entries, path) {
|
|
7291
|
+
const normalized = normalizeOfdPath(path);
|
|
7292
|
+
return entries.find((entry) => normalizeOfdPath(entry.name) === normalized || normalizeOfdPath(entry.name).endsWith(`/${normalized}`));
|
|
7293
|
+
}
|
|
7294
|
+
function joinOfdPath(...parts) {
|
|
7295
|
+
const joined = parts.filter(Boolean).join("/");
|
|
7296
|
+
const segments = [];
|
|
7297
|
+
for (const segment of joined.split("/")) {
|
|
7298
|
+
if (!segment || segment === ".") {
|
|
7299
|
+
continue;
|
|
7300
|
+
}
|
|
7301
|
+
if (segment === "..") {
|
|
7302
|
+
segments.pop();
|
|
7303
|
+
continue;
|
|
7304
|
+
}
|
|
7305
|
+
segments.push(segment);
|
|
7306
|
+
}
|
|
7307
|
+
return segments.join("/");
|
|
7308
|
+
}
|
|
7309
|
+
function directoryName2(path) {
|
|
7310
|
+
const normalized = normalizeOfdPath(path);
|
|
7311
|
+
const index = normalized.lastIndexOf("/");
|
|
7312
|
+
return index >= 0 ? normalized.slice(0, index) : "";
|
|
7313
|
+
}
|
|
7314
|
+
function normalizeOfdPath(path) {
|
|
7315
|
+
return path.replace(/\\/g, "/").replace(/^\/+/, "").replace(/\/+/g, "/");
|
|
7316
|
+
}
|
|
7010
7317
|
function mimeTypeFromPath2(path) {
|
|
7011
7318
|
const extension = path.split(".").pop()?.toLowerCase();
|
|
7012
7319
|
const map = {
|
|
@@ -7044,6 +7351,9 @@ function finiteNumber2(value, fallback) {
|
|
|
7044
7351
|
const parsed = value === null ? Number.NaN : Number(value);
|
|
7045
7352
|
return Number.isFinite(parsed) ? parsed : fallback;
|
|
7046
7353
|
}
|
|
7354
|
+
function formatOfdCssNumber(value) {
|
|
7355
|
+
return Number.isInteger(value) ? String(value) : value.toFixed(3).replace(/0+$/, "").replace(/\.$/, "");
|
|
7356
|
+
}
|
|
7047
7357
|
function createOfdFallback(fileName, url, detail) {
|
|
7048
7358
|
const fallback = document.createElement("div");
|
|
7049
7359
|
fallback.className = "ofv-fallback";
|