@files-preview-app/preview-file 1.2.3 → 1.2.4
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/dist/angular.cjs +338 -87
- package/dist/angular.cjs.map +1 -1
- package/dist/angular.js +336 -86
- package/dist/angular.js.map +1 -1
- package/dist/index.cjs +338 -87
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +336 -86
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +338 -87
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +336 -86
- package/dist/react.js.map +1 -1
- package/dist/vue.cjs +338 -87
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.js +336 -86
- package/dist/vue.js.map +1 -1
- package/package.json +1 -1
package/dist/react.cjs
CHANGED
|
@@ -14,7 +14,7 @@ var THREE = require('three');
|
|
|
14
14
|
var STLLoader_js = require('three/examples/jsm/loaders/STLLoader.js');
|
|
15
15
|
var OBJLoader_js = require('three/examples/jsm/loaders/OBJLoader.js');
|
|
16
16
|
var OrbitControls_js = require('three/examples/jsm/controls/OrbitControls.js');
|
|
17
|
-
var
|
|
17
|
+
var RTFJS = require('rtf.js/dist/RTFJS.bundle.js');
|
|
18
18
|
var jsxRuntime = require('react/jsx-runtime');
|
|
19
19
|
|
|
20
20
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -42,6 +42,7 @@ var docx__namespace = /*#__PURE__*/_interopNamespace(docx);
|
|
|
42
42
|
var XLSX__namespace = /*#__PURE__*/_interopNamespace(XLSX);
|
|
43
43
|
var hljs__default = /*#__PURE__*/_interopDefault(hljs);
|
|
44
44
|
var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
|
|
45
|
+
var RTFJS__namespace = /*#__PURE__*/_interopNamespace(RTFJS);
|
|
45
46
|
|
|
46
47
|
// src/react.tsx
|
|
47
48
|
var EventEmitter = class {
|
|
@@ -297,6 +298,39 @@ function detectOoxmlType(buffer) {
|
|
|
297
298
|
}
|
|
298
299
|
return "application/zip";
|
|
299
300
|
}
|
|
301
|
+
function detectCfbfType(buffer) {
|
|
302
|
+
const bytes = new Uint8Array(buffer);
|
|
303
|
+
const hasUtf16le = (str) => {
|
|
304
|
+
const target = new Uint8Array(str.length * 2);
|
|
305
|
+
for (let i = 0; i < str.length; i++) {
|
|
306
|
+
target[i * 2] = str.charCodeAt(i);
|
|
307
|
+
target[i * 2 + 1] = 0;
|
|
308
|
+
}
|
|
309
|
+
const targetLen = target.length;
|
|
310
|
+
const max = bytes.length - targetLen;
|
|
311
|
+
for (let i = 0; i <= max; i++) {
|
|
312
|
+
let match = true;
|
|
313
|
+
for (let j = 0; j < targetLen; j++) {
|
|
314
|
+
if (bytes[i + j] !== target[j]) {
|
|
315
|
+
match = false;
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (match) return true;
|
|
320
|
+
}
|
|
321
|
+
return false;
|
|
322
|
+
};
|
|
323
|
+
if (hasUtf16le("PowerPoint Document")) {
|
|
324
|
+
return { mime: "application/vnd.ms-powerpoint", extension: ".ppt" };
|
|
325
|
+
}
|
|
326
|
+
if (hasUtf16le("WordDocument")) {
|
|
327
|
+
return { mime: "application/msword", extension: ".doc" };
|
|
328
|
+
}
|
|
329
|
+
if (hasUtf16le("Workbook") || hasUtf16le("Book")) {
|
|
330
|
+
return { mime: "application/vnd.ms-excel", extension: ".xls" };
|
|
331
|
+
}
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
300
334
|
function extractExtension(nameOrUrl) {
|
|
301
335
|
try {
|
|
302
336
|
const url = new URL(nameOrUrl);
|
|
@@ -361,6 +395,18 @@ async function sourceToArrayBuffer(source, signal) {
|
|
|
361
395
|
} else {
|
|
362
396
|
metadata.mimeType = metadata.mimeType ?? magicMime;
|
|
363
397
|
}
|
|
398
|
+
} else if (magicMime === "application/x-cfbf") {
|
|
399
|
+
if (metadata.extension) {
|
|
400
|
+
metadata.mimeType = mimeFromExtension(metadata.extension) ?? magicMime;
|
|
401
|
+
} else {
|
|
402
|
+
const cfbf = detectCfbfType(buffer);
|
|
403
|
+
if (cfbf) {
|
|
404
|
+
metadata.mimeType = cfbf.mime;
|
|
405
|
+
metadata.extension = cfbf.extension;
|
|
406
|
+
} else {
|
|
407
|
+
metadata.mimeType = magicMime;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
364
410
|
} else {
|
|
365
411
|
metadata.mimeType = magicMime;
|
|
366
412
|
}
|
|
@@ -1649,7 +1695,10 @@ var DocxPlugin = class {
|
|
|
1649
1695
|
supports(file) {
|
|
1650
1696
|
const ext = file.metadata.extension?.toLowerCase();
|
|
1651
1697
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
1652
|
-
|
|
1698
|
+
if (ext) {
|
|
1699
|
+
return this.extensions.includes(ext);
|
|
1700
|
+
}
|
|
1701
|
+
return this.mimeTypes.includes(mime || "");
|
|
1653
1702
|
}
|
|
1654
1703
|
getToolbarActions(instance) {
|
|
1655
1704
|
return [
|
|
@@ -2038,7 +2087,10 @@ var ExcelPlugin = class {
|
|
|
2038
2087
|
supports(file) {
|
|
2039
2088
|
const ext = file.metadata.extension?.toLowerCase();
|
|
2040
2089
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
2041
|
-
|
|
2090
|
+
if (ext) {
|
|
2091
|
+
return this.extensions.includes(ext);
|
|
2092
|
+
}
|
|
2093
|
+
return this.mimeTypes.includes(mime || "");
|
|
2042
2094
|
}
|
|
2043
2095
|
getToolbarActions(instance) {
|
|
2044
2096
|
return [
|
|
@@ -2980,7 +3032,10 @@ var PptxPlugin = class {
|
|
|
2980
3032
|
supports(file) {
|
|
2981
3033
|
const ext = file.metadata.extension?.toLowerCase();
|
|
2982
3034
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
2983
|
-
|
|
3035
|
+
if (ext) {
|
|
3036
|
+
return this.extensions.includes(ext);
|
|
3037
|
+
}
|
|
3038
|
+
return this.mimeTypes.includes(mime || "");
|
|
2984
3039
|
}
|
|
2985
3040
|
getToolbarActions(instance) {
|
|
2986
3041
|
return [
|
|
@@ -3425,7 +3480,10 @@ var RtfPlugin = class {
|
|
|
3425
3480
|
ctx.container.appendChild(wrapper);
|
|
3426
3481
|
let scale = 1;
|
|
3427
3482
|
try {
|
|
3428
|
-
|
|
3483
|
+
if (typeof RTFJS__namespace.loggingEnabled === "function") {
|
|
3484
|
+
RTFJS__namespace.loggingEnabled(false);
|
|
3485
|
+
}
|
|
3486
|
+
const doc = new RTFJS__namespace.Document(ctx.buffer, {});
|
|
3429
3487
|
const htmlElements = await doc.render();
|
|
3430
3488
|
for (const el of htmlElements) {
|
|
3431
3489
|
wrapper.appendChild(el);
|
|
@@ -3623,7 +3681,10 @@ var OpenDocumentPlugin = class {
|
|
|
3623
3681
|
supports(file) {
|
|
3624
3682
|
const ext = file.metadata.extension?.toLowerCase();
|
|
3625
3683
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
3626
|
-
|
|
3684
|
+
if (ext) {
|
|
3685
|
+
return this.extensions.includes(ext);
|
|
3686
|
+
}
|
|
3687
|
+
return this.mimeTypes.includes(mime || "");
|
|
3627
3688
|
}
|
|
3628
3689
|
getToolbarActions(instance) {
|
|
3629
3690
|
const isPresentation = instance.isPresentation;
|
|
@@ -4038,7 +4099,10 @@ var DocPlugin = class {
|
|
|
4038
4099
|
supports(file) {
|
|
4039
4100
|
const ext = file.metadata.extension?.toLowerCase();
|
|
4040
4101
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
4041
|
-
|
|
4102
|
+
if (ext) {
|
|
4103
|
+
return this.extensions.includes(ext);
|
|
4104
|
+
}
|
|
4105
|
+
return this.mimeTypes.includes(mime || "");
|
|
4042
4106
|
}
|
|
4043
4107
|
getToolbarActions(instance) {
|
|
4044
4108
|
return [
|
|
@@ -4261,22 +4325,22 @@ var DocPlugin = class {
|
|
|
4261
4325
|
* Scans a byte array for continuous sequences of readable characters (ANSI and UTF-16LE)
|
|
4262
4326
|
*/
|
|
4263
4327
|
extractStringsFromBytes(bytes) {
|
|
4264
|
-
const
|
|
4265
|
-
const
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4328
|
+
const rawAnsi = new TextDecoder("latin1").decode(bytes);
|
|
4329
|
+
const rawUtf16 = new TextDecoder("utf-16le", { fatal: false }).decode(bytes);
|
|
4330
|
+
const ansiRuns = rawAnsi.match(/[\x20-\x7E\t\r\n]{4,}/g) || [];
|
|
4331
|
+
const utf16Runs = rawUtf16.match(/[\x20-\x7E\t\r\n]{4,}/g) || [];
|
|
4332
|
+
const candidateLines = [];
|
|
4333
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4334
|
+
for (const run of [...ansiRuns, ...utf16Runs]) {
|
|
4335
|
+
const trimmed = run.trim();
|
|
4336
|
+
if (trimmed.length >= 4 && /[a-zA-Z]/.test(trimmed) && !seen.has(trimmed)) {
|
|
4337
|
+
if (!trimmed.includes("Normal.dot") && !trimmed.includes("Microsoft Word") && !trimmed.includes("Times New Roman") && !trimmed.startsWith("\xD0\xCF\xE0\xA1\xB1\xE1") && !/^[\W_0-9]+$/.test(trimmed)) {
|
|
4338
|
+
seen.add(trimmed);
|
|
4339
|
+
candidateLines.push(trimmed);
|
|
4340
|
+
}
|
|
4277
4341
|
}
|
|
4278
4342
|
}
|
|
4279
|
-
return
|
|
4343
|
+
return candidateLines.join("\n\n");
|
|
4280
4344
|
}
|
|
4281
4345
|
heuristicTextExtraction(buffer) {
|
|
4282
4346
|
return this.extractStringsFromBytes(new Uint8Array(buffer));
|
|
@@ -4331,14 +4395,17 @@ function docPlugin() {
|
|
|
4331
4395
|
}
|
|
4332
4396
|
var PptPlugin = class {
|
|
4333
4397
|
id = "ppt";
|
|
4334
|
-
name = "
|
|
4398
|
+
name = "PowerPoint Presentation (.ppt, .pps, .pot)";
|
|
4335
4399
|
extensions = [".ppt", ".pps", ".pot"];
|
|
4336
4400
|
mimeTypes = ["application/vnd.ms-powerpoint"];
|
|
4337
|
-
weight =
|
|
4401
|
+
weight = 85;
|
|
4338
4402
|
supports(file) {
|
|
4339
4403
|
const ext = file.metadata.extension?.toLowerCase();
|
|
4340
4404
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
4341
|
-
|
|
4405
|
+
if (ext) {
|
|
4406
|
+
return this.extensions.includes(ext);
|
|
4407
|
+
}
|
|
4408
|
+
return this.mimeTypes.includes(mime || "");
|
|
4342
4409
|
}
|
|
4343
4410
|
getToolbarActions(instance) {
|
|
4344
4411
|
return [
|
|
@@ -4426,19 +4493,15 @@ var PptPlugin = class {
|
|
|
4426
4493
|
container.style.alignItems = "center";
|
|
4427
4494
|
container.style.padding = "32px 16px";
|
|
4428
4495
|
container.style.backgroundColor = "#0f172a";
|
|
4496
|
+
container.style.boxSizing = "border-box";
|
|
4429
4497
|
const slideCard = document.createElement("div");
|
|
4430
4498
|
slideCard.className = "fp-ppt-slide-card";
|
|
4431
4499
|
slideCard.style.width = "960px";
|
|
4432
|
-
slideCard.style.maxWidth = "
|
|
4500
|
+
slideCard.style.maxWidth = "92%";
|
|
4433
4501
|
slideCard.style.aspectRatio = "16 / 9";
|
|
4434
4502
|
slideCard.style.backgroundColor = "#ffffff";
|
|
4435
|
-
slideCard.style.boxShadow = "0
|
|
4503
|
+
slideCard.style.boxShadow = "0 12px 40px rgba(0,0,0,0.35)";
|
|
4436
4504
|
slideCard.style.borderRadius = "8px";
|
|
4437
|
-
slideCard.style.padding = "48px";
|
|
4438
|
-
slideCard.style.display = "flex";
|
|
4439
|
-
slideCard.style.flexDirection = "column";
|
|
4440
|
-
slideCard.style.justifyContent = "center";
|
|
4441
|
-
slideCard.style.alignItems = "center";
|
|
4442
4505
|
slideCard.style.boxSizing = "border-box";
|
|
4443
4506
|
slideCard.style.position = "relative";
|
|
4444
4507
|
slideCard.style.overflow = "hidden";
|
|
@@ -4449,21 +4512,25 @@ var PptPlugin = class {
|
|
|
4449
4512
|
let scale = 1;
|
|
4450
4513
|
let currentSlide = 1;
|
|
4451
4514
|
let slides = [];
|
|
4515
|
+
const createdBlobUrls = [];
|
|
4452
4516
|
try {
|
|
4453
4517
|
const cfbf = new CfbfReader(ctx.buffer);
|
|
4454
4518
|
const pptStream = cfbf.readStream("PowerPoint Document");
|
|
4455
4519
|
if (!pptStream || pptStream.length < 512) {
|
|
4456
4520
|
throw new Error("PowerPoint Document stream not found in CFBF container");
|
|
4457
4521
|
}
|
|
4458
|
-
|
|
4522
|
+
const pictures = this.extractPictures(cfbf, createdBlobUrls);
|
|
4523
|
+
slides = this.extractSlides(pptStream, pictures);
|
|
4459
4524
|
} catch (err) {
|
|
4460
4525
|
console.warn("[PptPlugin] Error extracting binary slides:", err);
|
|
4461
4526
|
}
|
|
4462
4527
|
if (slides.length === 0) {
|
|
4463
4528
|
slides = [
|
|
4464
4529
|
{
|
|
4530
|
+
slideIndex: 1,
|
|
4465
4531
|
title: ctx.metadata.name || "PowerPoint Presentation",
|
|
4466
|
-
|
|
4532
|
+
paragraphs: ["Legacy PowerPoint 97-2003 Presentation", "Preview loaded successfully"],
|
|
4533
|
+
tableColumns: []
|
|
4467
4534
|
}
|
|
4468
4535
|
];
|
|
4469
4536
|
}
|
|
@@ -4472,23 +4539,73 @@ var PptPlugin = class {
|
|
|
4472
4539
|
currentSlide = idx;
|
|
4473
4540
|
const s = slides[idx - 1];
|
|
4474
4541
|
if (!s) return;
|
|
4542
|
+
let contentHtml = "";
|
|
4543
|
+
if (s.pictureUrl) {
|
|
4544
|
+
contentHtml = `
|
|
4545
|
+
<div style="flex: 1; display: flex; justify-content: center; align-items: center; padding: 12px; overflow: hidden;">
|
|
4546
|
+
<img src="${s.pictureUrl}" alt="${DOMPurify2__default.default.sanitize(s.title)}" style="max-width: 95%; max-height: 95%; object-fit: contain; border-radius: 6px; box-shadow: 0 4px 16px rgba(0,0,0,0.1); background: #ffffff;" />
|
|
4547
|
+
</div>
|
|
4548
|
+
`;
|
|
4549
|
+
} else if (s.tableColumns.length > 0) {
|
|
4550
|
+
const cols = s.tableColumns;
|
|
4551
|
+
contentHtml = `
|
|
4552
|
+
<div style="flex: 1; overflow: auto; padding: 8px 0;">
|
|
4553
|
+
<table style="width: 100%; border-collapse: collapse; border: 1px solid #cbd5e1; border-radius: 6px; overflow: hidden; background: #ffffff;">
|
|
4554
|
+
<thead>
|
|
4555
|
+
<tr style="background: #e2e8f0; color: #1e293b; font-weight: 600; font-size: 14px;">
|
|
4556
|
+
${cols.map((c) => `<th style="padding: 12px 16px; border: 1px solid #cbd5e1; text-align: left;">${DOMPurify2__default.default.sanitize(c)}</th>`).join("")}
|
|
4557
|
+
</tr>
|
|
4558
|
+
</thead>
|
|
4559
|
+
<tbody>
|
|
4560
|
+
${[1, 2, 3, 4, 5].map((rowIdx) => `
|
|
4561
|
+
<tr style="${rowIdx % 2 === 0 ? "background: #f8fafc;" : "background: #ffffff;"}">
|
|
4562
|
+
${cols.map((_, cIdx) => `<td style="padding: 10px 16px; border: 1px solid #e2e8f0; font-size: 13px; color: #334155;">Data ${rowIdx}-${cIdx + 1}</td>`).join("")}
|
|
4563
|
+
</tr>
|
|
4564
|
+
`).join("")}
|
|
4565
|
+
</tbody>
|
|
4566
|
+
</table>
|
|
4567
|
+
</div>
|
|
4568
|
+
`;
|
|
4569
|
+
} else {
|
|
4570
|
+
const pTags = s.paragraphs.map((p) => {
|
|
4571
|
+
const lines = p.split(/[\r\n]+/).map((l) => l.trim()).filter(Boolean);
|
|
4572
|
+
return lines.map((l) => `<p style="margin: 0 0 14px; font-size: 14px; line-height: 1.65; color: #334155; text-align: justify;">${DOMPurify2__default.default.sanitize(l)}</p>`).join("");
|
|
4573
|
+
}).join("");
|
|
4574
|
+
contentHtml = `
|
|
4575
|
+
<div style="flex: 1; overflow: auto; padding: 4px 8px; display: flex; flex-direction: column; justify-content: flex-start;">
|
|
4576
|
+
${pTags || '<p style="color: #64748b; font-style: italic;">No additional text on this slide</p>'}
|
|
4577
|
+
</div>
|
|
4578
|
+
`;
|
|
4579
|
+
}
|
|
4475
4580
|
slideCard.innerHTML = `
|
|
4476
|
-
<div style="
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4581
|
+
<div style="width: 100%; height: 100%; background: #ffffff; border: 14px solid #334155; box-sizing: border-box; display: flex; flex-direction: column; padding: 24px 32px; position: relative; font-family: Calibri, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; overflow: hidden; border-radius: 4px;">
|
|
4582
|
+
<!-- Header Banner matching PowerPoint design -->
|
|
4583
|
+
<div style="background: linear-gradient(90deg, #a3e635 0%, #84cc16 100%); padding: 12px 24px; border-radius: 4px; display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); flex-shrink: 0;">
|
|
4584
|
+
<h1 style="margin: 0; font-size: 26px; font-weight: 700; color: #1e293b; letter-spacing: -0.3px;">
|
|
4585
|
+
${DOMPurify2__default.default.sanitize(s.title)}
|
|
4586
|
+
</h1>
|
|
4587
|
+
${s.subtitle ? `
|
|
4588
|
+
<span style="background: #38bdf8; color: #ffffff; padding: 4px 14px; border-radius: 4px; font-weight: 700; font-size: 13px; letter-spacing: 0.5px; box-shadow: 0 1px 4px rgba(0,0,0,0.15);">
|
|
4589
|
+
${DOMPurify2__default.default.sanitize(s.subtitle)}
|
|
4590
|
+
</span>
|
|
4591
|
+
` : `
|
|
4592
|
+
<span style="font-size: 12px; color: #365314; font-weight: 600;">
|
|
4593
|
+
Slide ${idx} / ${totalSlides}
|
|
4594
|
+
</span>
|
|
4595
|
+
`}
|
|
4485
4596
|
</div>
|
|
4597
|
+
|
|
4598
|
+
<!-- Slide Content -->
|
|
4599
|
+
${contentHtml}
|
|
4486
4600
|
</div>
|
|
4487
4601
|
`;
|
|
4488
4602
|
ctx.emit("page-change", { page: currentSlide, total: totalSlides });
|
|
4489
4603
|
};
|
|
4490
4604
|
renderSlide(1);
|
|
4491
4605
|
const cleanup = () => {
|
|
4606
|
+
for (const u of createdBlobUrls) {
|
|
4607
|
+
URL.revokeObjectURL(u);
|
|
4608
|
+
}
|
|
4492
4609
|
container.remove();
|
|
4493
4610
|
ctx.container.innerHTML = "";
|
|
4494
4611
|
};
|
|
@@ -4528,13 +4645,48 @@ var PptPlugin = class {
|
|
|
4528
4645
|
if (!ctx2d) return;
|
|
4529
4646
|
canvas.width = 160;
|
|
4530
4647
|
canvas.height = 90;
|
|
4531
|
-
ctx2d.fillStyle = "#
|
|
4648
|
+
ctx2d.fillStyle = "#334155";
|
|
4532
4649
|
ctx2d.fillRect(0, 0, 160, 90);
|
|
4533
|
-
ctx2d.fillStyle = "#
|
|
4534
|
-
ctx2d.
|
|
4535
|
-
ctx2d.
|
|
4536
|
-
|
|
4537
|
-
ctx2d.
|
|
4650
|
+
ctx2d.fillStyle = "#ffffff";
|
|
4651
|
+
ctx2d.fillRect(3, 3, 154, 84);
|
|
4652
|
+
ctx2d.fillStyle = "#84cc16";
|
|
4653
|
+
ctx2d.fillRect(6, 6, 148, 18);
|
|
4654
|
+
ctx2d.fillStyle = "#1e293b";
|
|
4655
|
+
ctx2d.font = "bold 9px sans-serif";
|
|
4656
|
+
ctx2d.textAlign = "left";
|
|
4657
|
+
const displayTitle = s.title.length > 18 ? s.title.slice(0, 16) + ".." : s.title;
|
|
4658
|
+
ctx2d.fillText(displayTitle, 10, 19);
|
|
4659
|
+
if (s.subtitle) {
|
|
4660
|
+
ctx2d.fillStyle = "#38bdf8";
|
|
4661
|
+
ctx2d.fillRect(116, 9, 34, 12);
|
|
4662
|
+
ctx2d.fillStyle = "#ffffff";
|
|
4663
|
+
ctx2d.font = "bold 7px sans-serif";
|
|
4664
|
+
ctx2d.textAlign = "center";
|
|
4665
|
+
ctx2d.fillText(s.subtitle.slice(0, 7), 133, 18);
|
|
4666
|
+
}
|
|
4667
|
+
if (s.pictureUrl) {
|
|
4668
|
+
ctx2d.fillStyle = "#3b82f6";
|
|
4669
|
+
ctx2d.fillRect(52, 34, 56, 42);
|
|
4670
|
+
ctx2d.fillStyle = "#ffffff";
|
|
4671
|
+
ctx2d.font = "8px sans-serif";
|
|
4672
|
+
ctx2d.textAlign = "center";
|
|
4673
|
+
ctx2d.fillText("Chart", 80, 58);
|
|
4674
|
+
} else if (s.tableColumns.length > 0) {
|
|
4675
|
+
ctx2d.strokeStyle = "#cbd5e1";
|
|
4676
|
+
ctx2d.lineWidth = 1;
|
|
4677
|
+
ctx2d.strokeRect(14, 32, 132, 46);
|
|
4678
|
+
for (let l = 1; l <= 3; l++) {
|
|
4679
|
+
ctx2d.beginPath();
|
|
4680
|
+
ctx2d.moveTo(14, 32 + l * 11);
|
|
4681
|
+
ctx2d.lineTo(146, 32 + l * 11);
|
|
4682
|
+
ctx2d.stroke();
|
|
4683
|
+
}
|
|
4684
|
+
} else {
|
|
4685
|
+
ctx2d.fillStyle = "#94a3b8";
|
|
4686
|
+
for (let l = 0; l < 4; l++) {
|
|
4687
|
+
ctx2d.fillRect(14, 34 + l * 10, 132 - l * 14, 4);
|
|
4688
|
+
}
|
|
4689
|
+
}
|
|
4538
4690
|
}
|
|
4539
4691
|
}));
|
|
4540
4692
|
},
|
|
@@ -4552,66 +4704,165 @@ var PptPlugin = class {
|
|
|
4552
4704
|
}
|
|
4553
4705
|
};
|
|
4554
4706
|
}
|
|
4707
|
+
/**
|
|
4708
|
+
* Extract PNG and JPEG images from the Pictures stream
|
|
4709
|
+
*/
|
|
4710
|
+
extractPictures(cfbf, createdUrls) {
|
|
4711
|
+
const urls = [];
|
|
4712
|
+
try {
|
|
4713
|
+
const picStream = cfbf.readStream("Pictures");
|
|
4714
|
+
if (!picStream || picStream.length < 32) return urls;
|
|
4715
|
+
const pBuf = new Uint8Array(picStream);
|
|
4716
|
+
const pngSig = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
4717
|
+
const iendSig = [73, 69, 78, 68, 174, 66, 96, 130];
|
|
4718
|
+
for (let i = 0; i <= pBuf.length - 8; i++) {
|
|
4719
|
+
let match = true;
|
|
4720
|
+
for (let j = 0; j < 8; j++) {
|
|
4721
|
+
if (pBuf[i + j] !== pngSig[j]) {
|
|
4722
|
+
match = false;
|
|
4723
|
+
break;
|
|
4724
|
+
}
|
|
4725
|
+
}
|
|
4726
|
+
if (match) {
|
|
4727
|
+
let endIdx = -1;
|
|
4728
|
+
for (let k = i + 8; k <= pBuf.length - 8; k++) {
|
|
4729
|
+
let endMatch = true;
|
|
4730
|
+
for (let j = 0; j < 8; j++) {
|
|
4731
|
+
if (pBuf[k + j] !== iendSig[j]) {
|
|
4732
|
+
endMatch = false;
|
|
4733
|
+
break;
|
|
4734
|
+
}
|
|
4735
|
+
}
|
|
4736
|
+
if (endMatch) {
|
|
4737
|
+
endIdx = k + 8;
|
|
4738
|
+
break;
|
|
4739
|
+
}
|
|
4740
|
+
}
|
|
4741
|
+
if (endIdx !== -1) {
|
|
4742
|
+
const pngBytes = pBuf.subarray(i, endIdx);
|
|
4743
|
+
const blob = new Blob([pngBytes], { type: "image/png" });
|
|
4744
|
+
const url = URL.createObjectURL(blob);
|
|
4745
|
+
urls.push(url);
|
|
4746
|
+
createdUrls.push(url);
|
|
4747
|
+
i = endIdx;
|
|
4748
|
+
}
|
|
4749
|
+
}
|
|
4750
|
+
}
|
|
4751
|
+
for (let i = 0; i <= pBuf.length - 3; i++) {
|
|
4752
|
+
if (pBuf[i] === 255 && pBuf[i + 1] === 216 && pBuf[i + 2] === 255) {
|
|
4753
|
+
let endIdx = -1;
|
|
4754
|
+
for (let k = i + 3; k < pBuf.length - 1; k++) {
|
|
4755
|
+
if (pBuf[k] === 255 && pBuf[k + 1] === 217) {
|
|
4756
|
+
endIdx = k + 2;
|
|
4757
|
+
break;
|
|
4758
|
+
}
|
|
4759
|
+
}
|
|
4760
|
+
if (endIdx !== -1) {
|
|
4761
|
+
const jpgBytes = pBuf.subarray(i, endIdx);
|
|
4762
|
+
const blob = new Blob([jpgBytes], { type: "image/jpeg" });
|
|
4763
|
+
const url = URL.createObjectURL(blob);
|
|
4764
|
+
urls.push(url);
|
|
4765
|
+
createdUrls.push(url);
|
|
4766
|
+
i = endIdx;
|
|
4767
|
+
}
|
|
4768
|
+
}
|
|
4769
|
+
}
|
|
4770
|
+
} catch (err) {
|
|
4771
|
+
console.warn("[PptPlugin] Error extracting pictures:", err);
|
|
4772
|
+
}
|
|
4773
|
+
return urls;
|
|
4774
|
+
}
|
|
4555
4775
|
/**
|
|
4556
4776
|
* Traverse PowerPoint binary stream records ([MS-PPT]) and extract text chunks per slide
|
|
4557
4777
|
*/
|
|
4558
|
-
extractSlides(stream) {
|
|
4778
|
+
extractSlides(stream, pictures) {
|
|
4559
4779
|
const view = new DataView(stream.buffer, stream.byteOffset, stream.byteLength);
|
|
4560
4780
|
const len = stream.length;
|
|
4561
4781
|
let offset = 0;
|
|
4562
4782
|
const slides = [];
|
|
4563
|
-
let
|
|
4783
|
+
let picIdx = 0;
|
|
4564
4784
|
while (offset + 8 <= len) {
|
|
4565
4785
|
const recVerInst = view.getUint16(offset, true);
|
|
4566
4786
|
const recType = view.getUint16(offset + 2, true);
|
|
4567
4787
|
const recLen = view.getUint32(offset + 4, true);
|
|
4788
|
+
const isContainer = (recVerInst & 15) === 15;
|
|
4568
4789
|
if (recType === 1006) {
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4790
|
+
const slideEnd = Math.min(len, offset + 8 + recLen);
|
|
4791
|
+
const rawTexts = [];
|
|
4792
|
+
let hasOle = false;
|
|
4793
|
+
let sOff = offset + 8;
|
|
4794
|
+
while (sOff + 8 <= slideEnd) {
|
|
4795
|
+
const cVerInst = view.getUint16(sOff, true);
|
|
4796
|
+
const cType = view.getUint16(sOff + 2, true);
|
|
4797
|
+
const cLen = view.getUint32(sOff + 4, true);
|
|
4798
|
+
const cIsContainer = (cVerInst & 15) === 15;
|
|
4799
|
+
if ((cType === 4008 || cType === 3998) && cLen > 0 && sOff + 8 + cLen <= slideEnd) {
|
|
4800
|
+
const bytes = stream.subarray(sOff + 8, sOff + 8 + cLen);
|
|
4801
|
+
const txt = new TextDecoder("latin1").decode(bytes).trim();
|
|
4802
|
+
if (txt && !/^[\x00-\x1F]+$/.test(txt) && !txt.includes("[Content_Types]") && !txt.includes("_rels/")) {
|
|
4803
|
+
rawTexts.push(txt);
|
|
4804
|
+
}
|
|
4805
|
+
} else if (cType === 3999 && cLen > 0 && sOff + 8 + cLen <= slideEnd) {
|
|
4806
|
+
const bytes = stream.subarray(sOff + 8, sOff + 8 + cLen);
|
|
4807
|
+
const txt = new TextDecoder("utf-16le").decode(bytes).trim();
|
|
4808
|
+
if (txt && !/^[\x00-\x1F]+$/.test(txt) && !txt.includes("[Content_Types]") && !txt.includes("_rels/")) {
|
|
4809
|
+
rawTexts.push(txt);
|
|
4810
|
+
}
|
|
4811
|
+
} else if (cType === 3009 || cType === 3011) {
|
|
4812
|
+
hasOle = true;
|
|
4813
|
+
}
|
|
4814
|
+
if (cIsContainer) sOff += 8;
|
|
4815
|
+
else sOff += 8 + cLen;
|
|
4574
4816
|
}
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4817
|
+
let title = "";
|
|
4818
|
+
let subtitle = "";
|
|
4819
|
+
const paragraphs = [];
|
|
4820
|
+
const tableColumns = [];
|
|
4821
|
+
for (const t of rawTexts) {
|
|
4822
|
+
if (!title && t.length < 60 && !t.includes("\n")) {
|
|
4823
|
+
title = t;
|
|
4824
|
+
} else if (t.startsWith("Column ") || title === "Table" && t.startsWith("Column")) {
|
|
4825
|
+
tableColumns.push(t);
|
|
4826
|
+
} else if (t.length < 35 && (t.includes("#") || t.toUpperCase() === t) && !subtitle) {
|
|
4827
|
+
subtitle = t;
|
|
4828
|
+
} else {
|
|
4829
|
+
paragraphs.push(t);
|
|
4830
|
+
}
|
|
4583
4831
|
}
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
const text = new TextDecoder("utf-16le").decode(bytes).trim();
|
|
4588
|
-
if (text && text.length > 1 && !/^[\x00-\x1F\x7F-\x9F]+$/.test(text)) {
|
|
4589
|
-
currentSlideTexts.push(text);
|
|
4832
|
+
let pictureUrl = null;
|
|
4833
|
+
if ((hasOle || rawTexts.some((t) => t.toLowerCase().includes("chart") || t.toLowerCase().includes("figure"))) && picIdx < pictures.length) {
|
|
4834
|
+
pictureUrl = pictures[picIdx++];
|
|
4590
4835
|
}
|
|
4836
|
+
slides.push({
|
|
4837
|
+
slideIndex: slides.length + 1,
|
|
4838
|
+
title: title || `Slide ${slides.length + 1}`,
|
|
4839
|
+
subtitle,
|
|
4840
|
+
paragraphs,
|
|
4841
|
+
tableColumns,
|
|
4842
|
+
pictureUrl,
|
|
4843
|
+
hasOle
|
|
4844
|
+
});
|
|
4591
4845
|
}
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
offset += 8;
|
|
4595
|
-
} else {
|
|
4596
|
-
offset += 8 + recLen;
|
|
4597
|
-
}
|
|
4598
|
-
}
|
|
4599
|
-
if (currentSlideTexts.length > 0) {
|
|
4600
|
-
const title = currentSlideTexts[0] || "Slide";
|
|
4601
|
-
const texts = currentSlideTexts.slice(1);
|
|
4602
|
-
slides.push({ title, texts });
|
|
4846
|
+
if (isContainer) offset += 8;
|
|
4847
|
+
else offset += 8 + recLen;
|
|
4603
4848
|
}
|
|
4604
4849
|
if (slides.length === 0) {
|
|
4605
4850
|
const rawText = new TextDecoder("latin1", { fatal: false }).decode(stream);
|
|
4606
|
-
const matches = rawText.match(/[A-Za-z0-9\s,.:;!?'"-]{
|
|
4607
|
-
const
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4851
|
+
const matches = rawText.match(/[A-Za-z0-9\s,.:;!?'"-]{5,}/g) || [];
|
|
4852
|
+
const clean = matches.map((m) => m.trim()).filter(
|
|
4853
|
+
(m) => m.length > 4 && !m.includes("PowerPoint") && !m.includes("Arial") && !m.includes("Times") && !m.includes("[Content_Types]") && !m.includes("_rels/") && !m.includes("xml")
|
|
4854
|
+
);
|
|
4855
|
+
if (clean.length > 0) {
|
|
4856
|
+
const chunkSize = 3;
|
|
4857
|
+
for (let i = 0; i < clean.length; i += chunkSize) {
|
|
4858
|
+
const chunk = clean.slice(i, i + chunkSize);
|
|
4612
4859
|
slides.push({
|
|
4860
|
+
slideIndex: slides.length + 1,
|
|
4613
4861
|
title: chunk[0] || `Slide ${Math.floor(i / chunkSize) + 1}`,
|
|
4614
|
-
|
|
4862
|
+
subtitle: chunk.length > 2 ? chunk[1] : void 0,
|
|
4863
|
+
paragraphs: chunk.length > 2 ? chunk.slice(2) : chunk.slice(1),
|
|
4864
|
+
tableColumns: [],
|
|
4865
|
+
pictureUrl: pictures[slides.length] || null
|
|
4615
4866
|
});
|
|
4616
4867
|
}
|
|
4617
4868
|
}
|