@open-file-viewer/core 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.cjs +523 -189
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +523 -189
- package/dist/index.js.map +1 -1
- package/dist/style.css +45 -33
- package/package.json +6 -6
package/dist/index.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, {
|
|
@@ -5036,6 +5090,7 @@ async function renderDocx(panel, arrayBuffer) {
|
|
|
5036
5090
|
experimental: true,
|
|
5037
5091
|
useBase64URL: true
|
|
5038
5092
|
});
|
|
5093
|
+
normalizeDocxLayout(content);
|
|
5039
5094
|
return fitDocxPages(content);
|
|
5040
5095
|
} catch (error) {
|
|
5041
5096
|
content.replaceChildren();
|
|
@@ -5053,6 +5108,29 @@ async function renderDocx(panel, arrayBuffer) {
|
|
|
5053
5108
|
}
|
|
5054
5109
|
return () => void 0;
|
|
5055
5110
|
}
|
|
5111
|
+
function normalizeDocxLayout(container) {
|
|
5112
|
+
const pages = container.querySelectorAll("section.ofv-docx");
|
|
5113
|
+
for (const page of pages) {
|
|
5114
|
+
for (const element of page.querySelectorAll("[style*='line-height']")) {
|
|
5115
|
+
const lineHeight = parseCssLineHeight(element.style.lineHeight);
|
|
5116
|
+
if (lineHeight > 0 && lineHeight < 1) {
|
|
5117
|
+
element.style.lineHeight = "1.2";
|
|
5118
|
+
}
|
|
5119
|
+
}
|
|
5120
|
+
}
|
|
5121
|
+
}
|
|
5122
|
+
function parseCssLineHeight(value) {
|
|
5123
|
+
const trimmed = value.trim();
|
|
5124
|
+
if (!trimmed || trimmed === "normal") {
|
|
5125
|
+
return 0;
|
|
5126
|
+
}
|
|
5127
|
+
if (trimmed.endsWith("%")) {
|
|
5128
|
+
const parsedPercent = Number.parseFloat(trimmed);
|
|
5129
|
+
return Number.isFinite(parsedPercent) ? parsedPercent / 100 : 0;
|
|
5130
|
+
}
|
|
5131
|
+
const parsed = Number.parseFloat(trimmed);
|
|
5132
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
5133
|
+
}
|
|
5056
5134
|
function fitDocxPages(container) {
|
|
5057
5135
|
const wrapper = container.querySelector(".ofv-docx-wrapper");
|
|
5058
5136
|
if (!wrapper) {
|
|
@@ -5214,7 +5292,7 @@ async function renderSheet(panel, arrayBuffer, extension) {
|
|
|
5214
5292
|
const xlsx = await import("xlsx");
|
|
5215
5293
|
let workbook;
|
|
5216
5294
|
try {
|
|
5217
|
-
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" });
|
|
5218
5296
|
} catch (error) {
|
|
5219
5297
|
if (isLegacyOfficeBinary(extension)) {
|
|
5220
5298
|
renderLegacyOfficeBinary(panel, extension, arrayBuffer, `\u8868\u683C\u89E3\u6790\u5931\u8D25\uFF1A${normalizeOfficeError(error)}`);
|
|
@@ -5847,10 +5925,30 @@ async function renderPptx(panel, arrayBuffer) {
|
|
|
5847
5925
|
try {
|
|
5848
5926
|
const { PptxViewer } = await import("@aiden0z/pptx-renderer");
|
|
5849
5927
|
await PptxViewer.open(arrayBuffer, container);
|
|
5928
|
+
normalizePptxLayout(container);
|
|
5850
5929
|
} catch {
|
|
5851
5930
|
container.textContent = "PPTX \u6E32\u67D3\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u6587\u4EF6\u662F\u5426\u635F\u574F\u3002";
|
|
5852
5931
|
}
|
|
5853
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
|
+
}
|
|
5854
5952
|
async function renderOdp(panel, arrayBuffer) {
|
|
5855
5953
|
const zip = await JSZip3.loadAsync(arrayBuffer);
|
|
5856
5954
|
const content = zip.file("content.xml");
|
|
@@ -5878,34 +5976,28 @@ async function renderPackagedOfficePreview(panel, arrayBuffer, extension) {
|
|
|
5878
5976
|
const hasEntry = (path) => entries.some((entry) => entry.name.toLowerCase() === path.toLowerCase());
|
|
5879
5977
|
const contentXml = zip.file(/(^|\/)content\.xml$/i)[0];
|
|
5880
5978
|
if (hasEntry("word/document.xml")) {
|
|
5881
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OOXML Word \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 DOCX \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5882
5979
|
await renderDocx(panel, arrayBuffer);
|
|
5883
5980
|
return true;
|
|
5884
5981
|
}
|
|
5885
5982
|
if (hasEntry("xl/workbook.xml")) {
|
|
5886
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OOXML Workbook \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 XLSX \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5887
5983
|
await renderSheet(panel, arrayBuffer, extension);
|
|
5888
5984
|
return true;
|
|
5889
5985
|
}
|
|
5890
5986
|
if (hasEntry("ppt/presentation.xml")) {
|
|
5891
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OOXML Presentation \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 PPTX \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5892
5987
|
await renderPptx(panel, arrayBuffer);
|
|
5893
5988
|
return true;
|
|
5894
5989
|
}
|
|
5895
5990
|
if (contentXml) {
|
|
5896
5991
|
const xml = await contentXml.async("text");
|
|
5897
5992
|
if (/<office:spreadsheet\b|<table:table\b/i.test(xml)) {
|
|
5898
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OpenDocument Spreadsheet \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 ODS \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5899
5993
|
renderParsedSheets(panel, parseFlatOds(xml), `${extension.toUpperCase()} \u6587\u4EF6\u672A\u89E3\u6790\u5230\u8868\u683C\u3002`);
|
|
5900
5994
|
return true;
|
|
5901
5995
|
}
|
|
5902
5996
|
if (/<office:presentation\b|<draw:page\b/i.test(xml)) {
|
|
5903
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OpenDocument Presentation \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 ODP \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5904
5997
|
renderOpenDocumentPresentation(panel, `${extension.toUpperCase()} \u6F14\u793A\u6587\u7A3F`, xml, await extractZipImages(zip, /^Pictures\//));
|
|
5905
5998
|
return true;
|
|
5906
5999
|
}
|
|
5907
6000
|
if (/<office:text\b|<text:p\b/i.test(xml)) {
|
|
5908
|
-
renderOfficePackageNotice(panel, extension, "\u68C0\u6D4B\u5230 OpenDocument Text \u5305\u7ED3\u6784\uFF0C\u5DF2\u6309 ODT \u517C\u5BB9\u8DEF\u5F84\u9884\u89C8\u3002");
|
|
5909
6001
|
renderOpenDocumentXml(panel, `${extension.toUpperCase()} \u6587\u6863`, xml);
|
|
5910
6002
|
return true;
|
|
5911
6003
|
}
|
|
@@ -5931,14 +6023,6 @@ async function renderPackagedOfficePreview(panel, arrayBuffer, extension) {
|
|
|
5931
6023
|
}
|
|
5932
6024
|
return false;
|
|
5933
6025
|
}
|
|
5934
|
-
function renderOfficePackageNotice(panel, extension, message) {
|
|
5935
|
-
const section = createSection("\u517C\u5BB9\u5305\u8BC6\u522B");
|
|
5936
|
-
const note = document.createElement("p");
|
|
5937
|
-
note.className = "ofv-office-package-note";
|
|
5938
|
-
note.textContent = `.${extension} ${message}`;
|
|
5939
|
-
section.append(note);
|
|
5940
|
-
panel.append(section);
|
|
5941
|
-
}
|
|
5942
6026
|
function renderOfficePackageStructure(panel, extension, entries, message, metadata) {
|
|
5943
6027
|
const section = createSection("Office \u5305\u7ED3\u6784\u9884\u89C8");
|
|
5944
6028
|
const note = document.createElement("p");
|
|
@@ -6586,7 +6670,7 @@ function rtfToText(rtf) {
|
|
|
6586
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();
|
|
6587
6671
|
}
|
|
6588
6672
|
async function readTextFromBuffer(arrayBuffer) {
|
|
6589
|
-
return
|
|
6673
|
+
return decodeTextBuffer(arrayBuffer);
|
|
6590
6674
|
}
|
|
6591
6675
|
function mimeTypeFromPath(path) {
|
|
6592
6676
|
const extension = path.split(".").pop()?.toLowerCase();
|
|
@@ -6653,36 +6737,72 @@ function ofdPlugin() {
|
|
|
6653
6737
|
}
|
|
6654
6738
|
};
|
|
6655
6739
|
}
|
|
6656
|
-
const
|
|
6657
|
-
const pages = await readOfdPages(entries,
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
|
|
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
|
+
};
|
|
6663
6753
|
if (pages.length > 0) {
|
|
6664
6754
|
const pagesWrap = document.createElement("div");
|
|
6665
6755
|
pagesWrap.className = "ofv-ofd-pages";
|
|
6666
6756
|
for (const page of pages) {
|
|
6667
6757
|
pagesWrap.append(renderOfdPage(page));
|
|
6668
6758
|
}
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
li.textContent = entry.name;
|
|
6680
|
-
ul.append(li);
|
|
6681
|
-
}
|
|
6682
|
-
list.append(ul);
|
|
6683
|
-
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
|
+
}
|
|
6684
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
|
+
},
|
|
6685
6804
|
destroy() {
|
|
6805
|
+
ctx.toolbar?.setZoom(void 0);
|
|
6686
6806
|
panel.remove();
|
|
6687
6807
|
revokeObjectUrl(url, isExternal);
|
|
6688
6808
|
}
|
|
@@ -6690,76 +6810,71 @@ function ofdPlugin() {
|
|
|
6690
6810
|
}
|
|
6691
6811
|
};
|
|
6692
6812
|
}
|
|
6693
|
-
function
|
|
6694
|
-
const summary = document.createElement("div");
|
|
6695
|
-
summary.className = "ofv-ofd-summary";
|
|
6696
|
-
const xmlEntries = entries.filter((entry) => entry.name.endsWith(".xml")).length;
|
|
6697
|
-
const textCount = pages.reduce((count, page) => count + page.texts.length, 0);
|
|
6698
|
-
const pathCount = pages.reduce((count, page) => count + page.paths.length, 0);
|
|
6699
|
-
const lineCount = pages.reduce((count, page) => count + page.lines.length, 0);
|
|
6700
|
-
const imageCount = pages.reduce((count, page) => count + page.images.length, 0);
|
|
6701
|
-
const textLength = pages.reduce((count, page) => count + page.texts.reduce((inner, item) => inner + item.text.length, 0), 0);
|
|
6702
|
-
appendOfdSummary(summary, "\u6587\u4EF6", String(entries.length));
|
|
6703
|
-
appendOfdSummary(summary, "XML", String(xmlEntries));
|
|
6704
|
-
appendOfdSummary(summary, "\u9875\u9762", String(pages.length));
|
|
6705
|
-
appendOfdSummary(summary, "\u6587\u672C", String(textCount));
|
|
6706
|
-
appendOfdSummary(summary, "\u8DEF\u5F84", String(pathCount));
|
|
6707
|
-
appendOfdSummary(summary, "\u7EBF\u6761", String(lineCount));
|
|
6708
|
-
appendOfdSummary(summary, "\u56FE\u7247\u5BF9\u8C61", String(imageCount));
|
|
6709
|
-
appendOfdSummary(summary, "\u56FE\u7247\u8D44\u6E90", String(uniqueOfdImageResources(images)));
|
|
6710
|
-
if (textLength > 0) {
|
|
6711
|
-
appendOfdSummary(summary, "\u6587\u5B57\u957F\u5EA6", String(textLength));
|
|
6712
|
-
}
|
|
6713
|
-
const sizes = formatOfdPageSizes(pages);
|
|
6714
|
-
if (sizes) {
|
|
6715
|
-
appendOfdSummary(summary, "\u9875\u9762\u5C3A\u5BF8", sizes);
|
|
6716
|
-
}
|
|
6717
|
-
return summary;
|
|
6718
|
-
}
|
|
6719
|
-
function appendOfdSummary(parent, label, value) {
|
|
6720
|
-
const item = document.createElement("span");
|
|
6721
|
-
const key = document.createElement("span");
|
|
6722
|
-
key.textContent = label;
|
|
6723
|
-
const content = document.createElement("strong");
|
|
6724
|
-
content.textContent = value;
|
|
6725
|
-
item.append(key, content);
|
|
6726
|
-
parent.append(item);
|
|
6727
|
-
}
|
|
6728
|
-
function uniqueOfdImageResources(images) {
|
|
6729
|
-
return new Set(images.values()).size;
|
|
6730
|
-
}
|
|
6731
|
-
function formatOfdPageSizes(pages) {
|
|
6732
|
-
const counts = /* @__PURE__ */ new Map();
|
|
6733
|
-
for (const page of pages) {
|
|
6734
|
-
const key = `${Math.round(page.width)} x ${Math.round(page.height)}`;
|
|
6735
|
-
counts.set(key, (counts.get(key) || 0) + 1);
|
|
6736
|
-
}
|
|
6737
|
-
return [...counts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 4).map(([size, count]) => count > 1 ? `${size} (${count})` : size).join(", ");
|
|
6738
|
-
}
|
|
6739
|
-
async function readOfdPages(entries, images) {
|
|
6813
|
+
async function readOfdPages(entries, context) {
|
|
6740
6814
|
const pages = [];
|
|
6741
6815
|
const pageEntries = entries.filter((entry) => /(^|\/)Pages\/Page_[^/]+\/Content\.xml$/i.test(entry.name) || /(^|\/)Page_[^/]+\/Content\.xml$/i.test(entry.name)).slice(0, 80);
|
|
6742
6816
|
for (const entry of pageEntries) {
|
|
6743
6817
|
const xml = await entry.async("text");
|
|
6744
|
-
const
|
|
6818
|
+
const templates = await readPageTemplates(xml, context, entries);
|
|
6819
|
+
const page = parseOfdPage(entry.name, xml, context.images, context.fonts, templates, context.pageSize);
|
|
6745
6820
|
if (page.texts.length > 0 || page.paths.length > 0 || page.lines.length > 0 || page.images.length > 0) {
|
|
6746
6821
|
pages.push(page);
|
|
6747
6822
|
}
|
|
6748
6823
|
}
|
|
6749
6824
|
return pages;
|
|
6750
6825
|
}
|
|
6751
|
-
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) {
|
|
6752
6843
|
const doc = new DOMParser().parseFromString(xml, "application/xml");
|
|
6753
6844
|
if (doc.querySelector("parsererror")) {
|
|
6754
6845
|
return { name, width: 210, height: 297, texts: [], paths: [], lines: [], images: [] };
|
|
6755
6846
|
}
|
|
6756
|
-
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) {
|
|
6757
6866
|
const textObjects = Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "TextObject");
|
|
6758
|
-
const texts = textObjects.flatMap((element) => parseOfdTextObject(element));
|
|
6867
|
+
const texts = textObjects.flatMap((element) => parseOfdTextObject(element, fonts));
|
|
6759
6868
|
const paths = Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "PathObject").flatMap((element) => parseOfdPathObject(element));
|
|
6760
6869
|
const lines = Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "LineObject").flatMap((element) => parseOfdLineObject(element));
|
|
6761
6870
|
const imageObjects = Array.from(doc.getElementsByTagName("*")).filter((element) => element.localName === "ImageObject").flatMap((element) => parseOfdImageObject(element, images));
|
|
6762
|
-
|
|
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 [
|
|
6763
6878
|
...texts.map((item) => ({ x: item.x, y: item.y, width: item.width, height: item.height })),
|
|
6764
6879
|
...paths.map((item) => ({ x: item.x, y: item.y, width: item.width, height: item.height })),
|
|
6765
6880
|
...lines.map((item) => ({
|
|
@@ -6770,38 +6885,63 @@ function parseOfdPage(name, xml, images) {
|
|
|
6770
6885
|
})),
|
|
6771
6886
|
...imageObjects.map((item) => ({ x: item.x, y: item.y, width: item.width, height: item.height }))
|
|
6772
6887
|
];
|
|
6773
|
-
const width = Math.max(pageSize.width, ...bounds.map((item) => item.x + item.width + 12));
|
|
6774
|
-
const height = Math.max(pageSize.height, ...bounds.map((item) => item.y + item.height + 12));
|
|
6775
|
-
return { name, width, height, texts, paths, lines, images: imageObjects };
|
|
6776
6888
|
}
|
|
6777
|
-
function parseOfdTextObject(element) {
|
|
6889
|
+
function parseOfdTextObject(element, fonts) {
|
|
6778
6890
|
const boundary = parseBoundary(getOfdAttribute(element, "Boundary"));
|
|
6779
6891
|
const size = finiteNumber2(getOfdAttribute(element, "Size"), Math.max(4, boundary.height || 5));
|
|
6780
6892
|
const color = parseOfdColor(element, "#111827");
|
|
6781
6893
|
const weight = finiteNumber2(getOfdAttribute(element, "Weight"), 400) >= 600 ? "700" : "400";
|
|
6782
|
-
const
|
|
6894
|
+
const fontFamily = fontStackForOfdFont(fonts.get(getOfdAttribute(element, "Font") || ""));
|
|
6895
|
+
const objectLetterSpacing = getOfdAttribute(element, "DeltaX") ? 0.5 : void 0;
|
|
6783
6896
|
const textCodes = Array.from(element.getElementsByTagName("*")).filter((child) => child.localName === "TextCode");
|
|
6784
6897
|
if (textCodes.length === 0) {
|
|
6785
6898
|
return [];
|
|
6786
6899
|
}
|
|
6787
|
-
return textCodes.
|
|
6900
|
+
return textCodes.flatMap((code) => {
|
|
6788
6901
|
const x = boundary.x + finiteNumber2(getOfdAttribute(code, "X"), 0);
|
|
6789
6902
|
const y = boundary.y + finiteNumber2(getOfdAttribute(code, "Y"), 0);
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
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
|
+
];
|
|
6801
6940
|
}).filter((item) => item.text);
|
|
6802
6941
|
}
|
|
6803
6942
|
function parseOfdPathObject(element) {
|
|
6804
6943
|
const boundary = parseBoundary(getOfdAttribute(element, "Boundary"));
|
|
6944
|
+
const ctm = parseOfdCtm(getOfdAttribute(element, "CTM"));
|
|
6805
6945
|
const commands = Array.from(element.getElementsByTagName("*")).filter(
|
|
6806
6946
|
(child) => child.localName === "AbbreviatedData" || child.localName === "PathData"
|
|
6807
6947
|
);
|
|
@@ -6816,9 +6956,10 @@ function parseOfdPathObject(element) {
|
|
|
6816
6956
|
y: boundary.y,
|
|
6817
6957
|
width: boundary.width,
|
|
6818
6958
|
height: boundary.height,
|
|
6819
|
-
stroke: parseOfdColor(element, "#
|
|
6959
|
+
stroke: parseOfdColor(element, "#111827", "StrokeColor"),
|
|
6820
6960
|
fill: parseOfdFill(element),
|
|
6821
|
-
strokeWidth: finiteNumber2(getOfdAttribute(element, "LineWidth"), 1)
|
|
6961
|
+
strokeWidth: finiteNumber2(getOfdAttribute(element, "LineWidth"), 1),
|
|
6962
|
+
transform: createOfdPathTransform(boundary.x, boundary.y, ctm)
|
|
6822
6963
|
}
|
|
6823
6964
|
];
|
|
6824
6965
|
}
|
|
@@ -6835,7 +6976,7 @@ function parseOfdLineObject(element) {
|
|
|
6835
6976
|
y1: boundary.y + start.y,
|
|
6836
6977
|
x2: boundary.x + end.x,
|
|
6837
6978
|
y2: boundary.y + end.y,
|
|
6838
|
-
stroke: parseOfdColor(element, "#
|
|
6979
|
+
stroke: parseOfdColor(element, "#111827"),
|
|
6839
6980
|
strokeWidth: finiteNumber2(getOfdAttribute(element, "LineWidth"), 1)
|
|
6840
6981
|
}
|
|
6841
6982
|
];
|
|
@@ -6857,6 +6998,8 @@ function parseOfdImageObject(element, images) {
|
|
|
6857
6998
|
function renderOfdPage(page) {
|
|
6858
6999
|
const figure = document.createElement("figure");
|
|
6859
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`);
|
|
6860
7003
|
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
6861
7004
|
svg.setAttribute("viewBox", `0 0 ${page.width} ${page.height}`);
|
|
6862
7005
|
svg.setAttribute("role", "img");
|
|
@@ -6893,7 +7036,7 @@ function renderOfdPage(page) {
|
|
|
6893
7036
|
for (const item of page.paths) {
|
|
6894
7037
|
const path = document.createElementNS(svg.namespaceURI, "path");
|
|
6895
7038
|
path.setAttribute("d", item.d);
|
|
6896
|
-
path.setAttribute("transform",
|
|
7039
|
+
path.setAttribute("transform", item.transform);
|
|
6897
7040
|
path.setAttribute("fill", item.fill);
|
|
6898
7041
|
path.setAttribute("stroke", item.stroke);
|
|
6899
7042
|
path.setAttribute("stroke-width", String(item.strokeWidth));
|
|
@@ -6912,22 +7055,63 @@ function renderOfdPage(page) {
|
|
|
6912
7055
|
}
|
|
6913
7056
|
for (const item of page.texts) {
|
|
6914
7057
|
const text = document.createElementNS(svg.namespaceURI, "text");
|
|
6915
|
-
text.setAttribute("x", String(item.x));
|
|
6916
|
-
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));
|
|
6917
7060
|
text.setAttribute("font-size", String(item.size));
|
|
6918
7061
|
text.setAttribute("fill", item.color);
|
|
6919
7062
|
text.setAttribute("font-weight", item.weight);
|
|
7063
|
+
text.setAttribute("font-family", item.fontFamily);
|
|
6920
7064
|
if (item.letterSpacing !== void 0) {
|
|
6921
7065
|
text.setAttribute("letter-spacing", String(item.letterSpacing));
|
|
6922
7066
|
}
|
|
6923
|
-
|
|
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
|
+
}
|
|
6924
7086
|
svg.append(text);
|
|
6925
7087
|
}
|
|
6926
|
-
|
|
6927
|
-
caption.textContent = `${page.name} \xB7 ${page.texts.length} text \xB7 ${page.paths.length} path \xB7 ${page.lines.length} line \xB7 ${page.images.length} image`;
|
|
6928
|
-
figure.append(svg, caption);
|
|
7088
|
+
figure.append(svg);
|
|
6929
7089
|
return figure;
|
|
6930
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
|
+
}
|
|
6931
7115
|
async function readOfdImages(entries) {
|
|
6932
7116
|
const images = /* @__PURE__ */ new Map();
|
|
6933
7117
|
for (const entry of entries.filter((item) => /\.(?:png|jpe?g|gif|bmp|webp)$/i.test(item.name)).slice(0, 80)) {
|
|
@@ -6942,17 +7126,69 @@ async function readOfdImages(entries) {
|
|
|
6942
7126
|
images.set(entry.name, href);
|
|
6943
7127
|
images.set(entry.name.split("/").pop() || entry.name, href);
|
|
6944
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
|
+
}
|
|
6945
7150
|
return images;
|
|
6946
7151
|
}
|
|
6947
|
-
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
|
+
}
|
|
6948
7184
|
const physicalBox = Array.from(doc.getElementsByTagName("*")).find((element) => element.localName === "PhysicalBox");
|
|
6949
7185
|
if (physicalBox?.textContent) {
|
|
6950
7186
|
const box = parseBoundary(physicalBox.textContent);
|
|
6951
7187
|
if (box.width > 0 && box.height > 0) {
|
|
6952
|
-
return { width: box.width, height: box.height };
|
|
7188
|
+
return { width: box.width, height: box.height, explicit: true };
|
|
6953
7189
|
}
|
|
6954
7190
|
}
|
|
6955
|
-
return { width: 210, height: 297 };
|
|
7191
|
+
return { width: 210, height: 297, explicit: false };
|
|
6956
7192
|
}
|
|
6957
7193
|
function parseOfdColor(element, fallback, preferredLocalName = "FillColor") {
|
|
6958
7194
|
const colorElement = findOfdChild(element, preferredLocalName) || findOfdChild(element, "StrokeColor") || findOfdChild(element, "FillColor");
|
|
@@ -6983,6 +7219,101 @@ function parsePoint(value, fallback) {
|
|
|
6983
7219
|
function normalizeOfdPathData(value) {
|
|
6984
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();
|
|
6985
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
|
+
}
|
|
6986
7317
|
function mimeTypeFromPath2(path) {
|
|
6987
7318
|
const extension = path.split(".").pop()?.toLowerCase();
|
|
6988
7319
|
const map = {
|
|
@@ -7020,6 +7351,9 @@ function finiteNumber2(value, fallback) {
|
|
|
7020
7351
|
const parsed = value === null ? Number.NaN : Number(value);
|
|
7021
7352
|
return Number.isFinite(parsed) ? parsed : fallback;
|
|
7022
7353
|
}
|
|
7354
|
+
function formatOfdCssNumber(value) {
|
|
7355
|
+
return Number.isInteger(value) ? String(value) : value.toFixed(3).replace(/0+$/, "").replace(/\.$/, "");
|
|
7356
|
+
}
|
|
7023
7357
|
function createOfdFallback(fileName, url, detail) {
|
|
7024
7358
|
const fallback = document.createElement("div");
|
|
7025
7359
|
fallback.className = "ofv-fallback";
|