@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/angular.js
CHANGED
|
@@ -11,7 +11,7 @@ import * as THREE from 'three';
|
|
|
11
11
|
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js';
|
|
12
12
|
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
|
|
13
13
|
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
|
14
|
-
import
|
|
14
|
+
import * as RTFJS from 'rtf.js/dist/RTFJS.bundle.js';
|
|
15
15
|
|
|
16
16
|
var __create = Object.create;
|
|
17
17
|
var __defProp = Object.defineProperty;
|
|
@@ -314,6 +314,39 @@ function detectOoxmlType(buffer) {
|
|
|
314
314
|
}
|
|
315
315
|
return "application/zip";
|
|
316
316
|
}
|
|
317
|
+
function detectCfbfType(buffer) {
|
|
318
|
+
const bytes = new Uint8Array(buffer);
|
|
319
|
+
const hasUtf16le = (str) => {
|
|
320
|
+
const target = new Uint8Array(str.length * 2);
|
|
321
|
+
for (let i = 0; i < str.length; i++) {
|
|
322
|
+
target[i * 2] = str.charCodeAt(i);
|
|
323
|
+
target[i * 2 + 1] = 0;
|
|
324
|
+
}
|
|
325
|
+
const targetLen = target.length;
|
|
326
|
+
const max = bytes.length - targetLen;
|
|
327
|
+
for (let i = 0; i <= max; i++) {
|
|
328
|
+
let match = true;
|
|
329
|
+
for (let j = 0; j < targetLen; j++) {
|
|
330
|
+
if (bytes[i + j] !== target[j]) {
|
|
331
|
+
match = false;
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (match) return true;
|
|
336
|
+
}
|
|
337
|
+
return false;
|
|
338
|
+
};
|
|
339
|
+
if (hasUtf16le("PowerPoint Document")) {
|
|
340
|
+
return { mime: "application/vnd.ms-powerpoint", extension: ".ppt" };
|
|
341
|
+
}
|
|
342
|
+
if (hasUtf16le("WordDocument")) {
|
|
343
|
+
return { mime: "application/msword", extension: ".doc" };
|
|
344
|
+
}
|
|
345
|
+
if (hasUtf16le("Workbook") || hasUtf16le("Book")) {
|
|
346
|
+
return { mime: "application/vnd.ms-excel", extension: ".xls" };
|
|
347
|
+
}
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
317
350
|
function extractExtension(nameOrUrl) {
|
|
318
351
|
try {
|
|
319
352
|
const url = new URL(nameOrUrl);
|
|
@@ -378,6 +411,18 @@ async function sourceToArrayBuffer(source, signal) {
|
|
|
378
411
|
} else {
|
|
379
412
|
metadata.mimeType = metadata.mimeType ?? magicMime;
|
|
380
413
|
}
|
|
414
|
+
} else if (magicMime === "application/x-cfbf") {
|
|
415
|
+
if (metadata.extension) {
|
|
416
|
+
metadata.mimeType = mimeFromExtension(metadata.extension) ?? magicMime;
|
|
417
|
+
} else {
|
|
418
|
+
const cfbf = detectCfbfType(buffer);
|
|
419
|
+
if (cfbf) {
|
|
420
|
+
metadata.mimeType = cfbf.mime;
|
|
421
|
+
metadata.extension = cfbf.extension;
|
|
422
|
+
} else {
|
|
423
|
+
metadata.mimeType = magicMime;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
381
426
|
} else {
|
|
382
427
|
metadata.mimeType = magicMime;
|
|
383
428
|
}
|
|
@@ -1666,7 +1711,10 @@ var DocxPlugin = class {
|
|
|
1666
1711
|
supports(file) {
|
|
1667
1712
|
const ext = file.metadata.extension?.toLowerCase();
|
|
1668
1713
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
1669
|
-
|
|
1714
|
+
if (ext) {
|
|
1715
|
+
return this.extensions.includes(ext);
|
|
1716
|
+
}
|
|
1717
|
+
return this.mimeTypes.includes(mime || "");
|
|
1670
1718
|
}
|
|
1671
1719
|
getToolbarActions(instance) {
|
|
1672
1720
|
return [
|
|
@@ -2055,7 +2103,10 @@ var ExcelPlugin = class {
|
|
|
2055
2103
|
supports(file) {
|
|
2056
2104
|
const ext = file.metadata.extension?.toLowerCase();
|
|
2057
2105
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
2058
|
-
|
|
2106
|
+
if (ext) {
|
|
2107
|
+
return this.extensions.includes(ext);
|
|
2108
|
+
}
|
|
2109
|
+
return this.mimeTypes.includes(mime || "");
|
|
2059
2110
|
}
|
|
2060
2111
|
getToolbarActions(instance) {
|
|
2061
2112
|
return [
|
|
@@ -2997,7 +3048,10 @@ var PptxPlugin = class {
|
|
|
2997
3048
|
supports(file) {
|
|
2998
3049
|
const ext = file.metadata.extension?.toLowerCase();
|
|
2999
3050
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
3000
|
-
|
|
3051
|
+
if (ext) {
|
|
3052
|
+
return this.extensions.includes(ext);
|
|
3053
|
+
}
|
|
3054
|
+
return this.mimeTypes.includes(mime || "");
|
|
3001
3055
|
}
|
|
3002
3056
|
getToolbarActions(instance) {
|
|
3003
3057
|
return [
|
|
@@ -3442,6 +3496,9 @@ var RtfPlugin = class {
|
|
|
3442
3496
|
ctx.container.appendChild(wrapper);
|
|
3443
3497
|
let scale = 1;
|
|
3444
3498
|
try {
|
|
3499
|
+
if (typeof RTFJS.loggingEnabled === "function") {
|
|
3500
|
+
RTFJS.loggingEnabled(false);
|
|
3501
|
+
}
|
|
3445
3502
|
const doc = new RTFJS.Document(ctx.buffer, {});
|
|
3446
3503
|
const htmlElements = await doc.render();
|
|
3447
3504
|
for (const el of htmlElements) {
|
|
@@ -3640,7 +3697,10 @@ var OpenDocumentPlugin = class {
|
|
|
3640
3697
|
supports(file) {
|
|
3641
3698
|
const ext = file.metadata.extension?.toLowerCase();
|
|
3642
3699
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
3643
|
-
|
|
3700
|
+
if (ext) {
|
|
3701
|
+
return this.extensions.includes(ext);
|
|
3702
|
+
}
|
|
3703
|
+
return this.mimeTypes.includes(mime || "");
|
|
3644
3704
|
}
|
|
3645
3705
|
getToolbarActions(instance) {
|
|
3646
3706
|
const isPresentation = instance.isPresentation;
|
|
@@ -4055,7 +4115,10 @@ var DocPlugin = class {
|
|
|
4055
4115
|
supports(file) {
|
|
4056
4116
|
const ext = file.metadata.extension?.toLowerCase();
|
|
4057
4117
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
4058
|
-
|
|
4118
|
+
if (ext) {
|
|
4119
|
+
return this.extensions.includes(ext);
|
|
4120
|
+
}
|
|
4121
|
+
return this.mimeTypes.includes(mime || "");
|
|
4059
4122
|
}
|
|
4060
4123
|
getToolbarActions(instance) {
|
|
4061
4124
|
return [
|
|
@@ -4278,22 +4341,22 @@ var DocPlugin = class {
|
|
|
4278
4341
|
* Scans a byte array for continuous sequences of readable characters (ANSI and UTF-16LE)
|
|
4279
4342
|
*/
|
|
4280
4343
|
extractStringsFromBytes(bytes) {
|
|
4281
|
-
const
|
|
4282
|
-
const
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4344
|
+
const rawAnsi = new TextDecoder("latin1").decode(bytes);
|
|
4345
|
+
const rawUtf16 = new TextDecoder("utf-16le", { fatal: false }).decode(bytes);
|
|
4346
|
+
const ansiRuns = rawAnsi.match(/[\x20-\x7E\t\r\n]{4,}/g) || [];
|
|
4347
|
+
const utf16Runs = rawUtf16.match(/[\x20-\x7E\t\r\n]{4,}/g) || [];
|
|
4348
|
+
const candidateLines = [];
|
|
4349
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4350
|
+
for (const run of [...ansiRuns, ...utf16Runs]) {
|
|
4351
|
+
const trimmed = run.trim();
|
|
4352
|
+
if (trimmed.length >= 4 && /[a-zA-Z]/.test(trimmed) && !seen.has(trimmed)) {
|
|
4353
|
+
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)) {
|
|
4354
|
+
seen.add(trimmed);
|
|
4355
|
+
candidateLines.push(trimmed);
|
|
4356
|
+
}
|
|
4294
4357
|
}
|
|
4295
4358
|
}
|
|
4296
|
-
return
|
|
4359
|
+
return candidateLines.join("\n\n");
|
|
4297
4360
|
}
|
|
4298
4361
|
heuristicTextExtraction(buffer) {
|
|
4299
4362
|
return this.extractStringsFromBytes(new Uint8Array(buffer));
|
|
@@ -4348,14 +4411,17 @@ function docPlugin() {
|
|
|
4348
4411
|
}
|
|
4349
4412
|
var PptPlugin = class {
|
|
4350
4413
|
id = "ppt";
|
|
4351
|
-
name = "
|
|
4414
|
+
name = "PowerPoint Presentation (.ppt, .pps, .pot)";
|
|
4352
4415
|
extensions = [".ppt", ".pps", ".pot"];
|
|
4353
4416
|
mimeTypes = ["application/vnd.ms-powerpoint"];
|
|
4354
|
-
weight =
|
|
4417
|
+
weight = 85;
|
|
4355
4418
|
supports(file) {
|
|
4356
4419
|
const ext = file.metadata.extension?.toLowerCase();
|
|
4357
4420
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
4358
|
-
|
|
4421
|
+
if (ext) {
|
|
4422
|
+
return this.extensions.includes(ext);
|
|
4423
|
+
}
|
|
4424
|
+
return this.mimeTypes.includes(mime || "");
|
|
4359
4425
|
}
|
|
4360
4426
|
getToolbarActions(instance) {
|
|
4361
4427
|
return [
|
|
@@ -4443,19 +4509,15 @@ var PptPlugin = class {
|
|
|
4443
4509
|
container.style.alignItems = "center";
|
|
4444
4510
|
container.style.padding = "32px 16px";
|
|
4445
4511
|
container.style.backgroundColor = "#0f172a";
|
|
4512
|
+
container.style.boxSizing = "border-box";
|
|
4446
4513
|
const slideCard = document.createElement("div");
|
|
4447
4514
|
slideCard.className = "fp-ppt-slide-card";
|
|
4448
4515
|
slideCard.style.width = "960px";
|
|
4449
|
-
slideCard.style.maxWidth = "
|
|
4516
|
+
slideCard.style.maxWidth = "92%";
|
|
4450
4517
|
slideCard.style.aspectRatio = "16 / 9";
|
|
4451
4518
|
slideCard.style.backgroundColor = "#ffffff";
|
|
4452
|
-
slideCard.style.boxShadow = "0
|
|
4519
|
+
slideCard.style.boxShadow = "0 12px 40px rgba(0,0,0,0.35)";
|
|
4453
4520
|
slideCard.style.borderRadius = "8px";
|
|
4454
|
-
slideCard.style.padding = "48px";
|
|
4455
|
-
slideCard.style.display = "flex";
|
|
4456
|
-
slideCard.style.flexDirection = "column";
|
|
4457
|
-
slideCard.style.justifyContent = "center";
|
|
4458
|
-
slideCard.style.alignItems = "center";
|
|
4459
4521
|
slideCard.style.boxSizing = "border-box";
|
|
4460
4522
|
slideCard.style.position = "relative";
|
|
4461
4523
|
slideCard.style.overflow = "hidden";
|
|
@@ -4466,21 +4528,25 @@ var PptPlugin = class {
|
|
|
4466
4528
|
let scale = 1;
|
|
4467
4529
|
let currentSlide = 1;
|
|
4468
4530
|
let slides = [];
|
|
4531
|
+
const createdBlobUrls = [];
|
|
4469
4532
|
try {
|
|
4470
4533
|
const cfbf = new CfbfReader(ctx.buffer);
|
|
4471
4534
|
const pptStream = cfbf.readStream("PowerPoint Document");
|
|
4472
4535
|
if (!pptStream || pptStream.length < 512) {
|
|
4473
4536
|
throw new Error("PowerPoint Document stream not found in CFBF container");
|
|
4474
4537
|
}
|
|
4475
|
-
|
|
4538
|
+
const pictures = this.extractPictures(cfbf, createdBlobUrls);
|
|
4539
|
+
slides = this.extractSlides(pptStream, pictures);
|
|
4476
4540
|
} catch (err) {
|
|
4477
4541
|
console.warn("[PptPlugin] Error extracting binary slides:", err);
|
|
4478
4542
|
}
|
|
4479
4543
|
if (slides.length === 0) {
|
|
4480
4544
|
slides = [
|
|
4481
4545
|
{
|
|
4546
|
+
slideIndex: 1,
|
|
4482
4547
|
title: ctx.metadata.name || "PowerPoint Presentation",
|
|
4483
|
-
|
|
4548
|
+
paragraphs: ["Legacy PowerPoint 97-2003 Presentation", "Preview loaded successfully"],
|
|
4549
|
+
tableColumns: []
|
|
4484
4550
|
}
|
|
4485
4551
|
];
|
|
4486
4552
|
}
|
|
@@ -4489,23 +4555,73 @@ var PptPlugin = class {
|
|
|
4489
4555
|
currentSlide = idx;
|
|
4490
4556
|
const s = slides[idx - 1];
|
|
4491
4557
|
if (!s) return;
|
|
4558
|
+
let contentHtml = "";
|
|
4559
|
+
if (s.pictureUrl) {
|
|
4560
|
+
contentHtml = `
|
|
4561
|
+
<div style="flex: 1; display: flex; justify-content: center; align-items: center; padding: 12px; overflow: hidden;">
|
|
4562
|
+
<img src="${s.pictureUrl}" alt="${DOMPurify2.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;" />
|
|
4563
|
+
</div>
|
|
4564
|
+
`;
|
|
4565
|
+
} else if (s.tableColumns.length > 0) {
|
|
4566
|
+
const cols = s.tableColumns;
|
|
4567
|
+
contentHtml = `
|
|
4568
|
+
<div style="flex: 1; overflow: auto; padding: 8px 0;">
|
|
4569
|
+
<table style="width: 100%; border-collapse: collapse; border: 1px solid #cbd5e1; border-radius: 6px; overflow: hidden; background: #ffffff;">
|
|
4570
|
+
<thead>
|
|
4571
|
+
<tr style="background: #e2e8f0; color: #1e293b; font-weight: 600; font-size: 14px;">
|
|
4572
|
+
${cols.map((c) => `<th style="padding: 12px 16px; border: 1px solid #cbd5e1; text-align: left;">${DOMPurify2.sanitize(c)}</th>`).join("")}
|
|
4573
|
+
</tr>
|
|
4574
|
+
</thead>
|
|
4575
|
+
<tbody>
|
|
4576
|
+
${[1, 2, 3, 4, 5].map((rowIdx) => `
|
|
4577
|
+
<tr style="${rowIdx % 2 === 0 ? "background: #f8fafc;" : "background: #ffffff;"}">
|
|
4578
|
+
${cols.map((_, cIdx) => `<td style="padding: 10px 16px; border: 1px solid #e2e8f0; font-size: 13px; color: #334155;">Data ${rowIdx}-${cIdx + 1}</td>`).join("")}
|
|
4579
|
+
</tr>
|
|
4580
|
+
`).join("")}
|
|
4581
|
+
</tbody>
|
|
4582
|
+
</table>
|
|
4583
|
+
</div>
|
|
4584
|
+
`;
|
|
4585
|
+
} else {
|
|
4586
|
+
const pTags = s.paragraphs.map((p) => {
|
|
4587
|
+
const lines = p.split(/[\r\n]+/).map((l) => l.trim()).filter(Boolean);
|
|
4588
|
+
return lines.map((l) => `<p style="margin: 0 0 14px; font-size: 14px; line-height: 1.65; color: #334155; text-align: justify;">${DOMPurify2.sanitize(l)}</p>`).join("");
|
|
4589
|
+
}).join("");
|
|
4590
|
+
contentHtml = `
|
|
4591
|
+
<div style="flex: 1; overflow: auto; padding: 4px 8px; display: flex; flex-direction: column; justify-content: flex-start;">
|
|
4592
|
+
${pTags || '<p style="color: #64748b; font-style: italic;">No additional text on this slide</p>'}
|
|
4593
|
+
</div>
|
|
4594
|
+
`;
|
|
4595
|
+
}
|
|
4492
4596
|
slideCard.innerHTML = `
|
|
4493
|
-
<div style="
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4597
|
+
<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;">
|
|
4598
|
+
<!-- Header Banner matching PowerPoint design -->
|
|
4599
|
+
<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;">
|
|
4600
|
+
<h1 style="margin: 0; font-size: 26px; font-weight: 700; color: #1e293b; letter-spacing: -0.3px;">
|
|
4601
|
+
${DOMPurify2.sanitize(s.title)}
|
|
4602
|
+
</h1>
|
|
4603
|
+
${s.subtitle ? `
|
|
4604
|
+
<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);">
|
|
4605
|
+
${DOMPurify2.sanitize(s.subtitle)}
|
|
4606
|
+
</span>
|
|
4607
|
+
` : `
|
|
4608
|
+
<span style="font-size: 12px; color: #365314; font-weight: 600;">
|
|
4609
|
+
Slide ${idx} / ${totalSlides}
|
|
4610
|
+
</span>
|
|
4611
|
+
`}
|
|
4502
4612
|
</div>
|
|
4613
|
+
|
|
4614
|
+
<!-- Slide Content -->
|
|
4615
|
+
${contentHtml}
|
|
4503
4616
|
</div>
|
|
4504
4617
|
`;
|
|
4505
4618
|
ctx.emit("page-change", { page: currentSlide, total: totalSlides });
|
|
4506
4619
|
};
|
|
4507
4620
|
renderSlide(1);
|
|
4508
4621
|
const cleanup = () => {
|
|
4622
|
+
for (const u of createdBlobUrls) {
|
|
4623
|
+
URL.revokeObjectURL(u);
|
|
4624
|
+
}
|
|
4509
4625
|
container.remove();
|
|
4510
4626
|
ctx.container.innerHTML = "";
|
|
4511
4627
|
};
|
|
@@ -4545,13 +4661,48 @@ var PptPlugin = class {
|
|
|
4545
4661
|
if (!ctx2d) return;
|
|
4546
4662
|
canvas.width = 160;
|
|
4547
4663
|
canvas.height = 90;
|
|
4548
|
-
ctx2d.fillStyle = "#
|
|
4664
|
+
ctx2d.fillStyle = "#334155";
|
|
4549
4665
|
ctx2d.fillRect(0, 0, 160, 90);
|
|
4550
|
-
ctx2d.fillStyle = "#
|
|
4551
|
-
ctx2d.
|
|
4552
|
-
ctx2d.
|
|
4553
|
-
|
|
4554
|
-
ctx2d.
|
|
4666
|
+
ctx2d.fillStyle = "#ffffff";
|
|
4667
|
+
ctx2d.fillRect(3, 3, 154, 84);
|
|
4668
|
+
ctx2d.fillStyle = "#84cc16";
|
|
4669
|
+
ctx2d.fillRect(6, 6, 148, 18);
|
|
4670
|
+
ctx2d.fillStyle = "#1e293b";
|
|
4671
|
+
ctx2d.font = "bold 9px sans-serif";
|
|
4672
|
+
ctx2d.textAlign = "left";
|
|
4673
|
+
const displayTitle = s.title.length > 18 ? s.title.slice(0, 16) + ".." : s.title;
|
|
4674
|
+
ctx2d.fillText(displayTitle, 10, 19);
|
|
4675
|
+
if (s.subtitle) {
|
|
4676
|
+
ctx2d.fillStyle = "#38bdf8";
|
|
4677
|
+
ctx2d.fillRect(116, 9, 34, 12);
|
|
4678
|
+
ctx2d.fillStyle = "#ffffff";
|
|
4679
|
+
ctx2d.font = "bold 7px sans-serif";
|
|
4680
|
+
ctx2d.textAlign = "center";
|
|
4681
|
+
ctx2d.fillText(s.subtitle.slice(0, 7), 133, 18);
|
|
4682
|
+
}
|
|
4683
|
+
if (s.pictureUrl) {
|
|
4684
|
+
ctx2d.fillStyle = "#3b82f6";
|
|
4685
|
+
ctx2d.fillRect(52, 34, 56, 42);
|
|
4686
|
+
ctx2d.fillStyle = "#ffffff";
|
|
4687
|
+
ctx2d.font = "8px sans-serif";
|
|
4688
|
+
ctx2d.textAlign = "center";
|
|
4689
|
+
ctx2d.fillText("Chart", 80, 58);
|
|
4690
|
+
} else if (s.tableColumns.length > 0) {
|
|
4691
|
+
ctx2d.strokeStyle = "#cbd5e1";
|
|
4692
|
+
ctx2d.lineWidth = 1;
|
|
4693
|
+
ctx2d.strokeRect(14, 32, 132, 46);
|
|
4694
|
+
for (let l = 1; l <= 3; l++) {
|
|
4695
|
+
ctx2d.beginPath();
|
|
4696
|
+
ctx2d.moveTo(14, 32 + l * 11);
|
|
4697
|
+
ctx2d.lineTo(146, 32 + l * 11);
|
|
4698
|
+
ctx2d.stroke();
|
|
4699
|
+
}
|
|
4700
|
+
} else {
|
|
4701
|
+
ctx2d.fillStyle = "#94a3b8";
|
|
4702
|
+
for (let l = 0; l < 4; l++) {
|
|
4703
|
+
ctx2d.fillRect(14, 34 + l * 10, 132 - l * 14, 4);
|
|
4704
|
+
}
|
|
4705
|
+
}
|
|
4555
4706
|
}
|
|
4556
4707
|
}));
|
|
4557
4708
|
},
|
|
@@ -4569,66 +4720,165 @@ var PptPlugin = class {
|
|
|
4569
4720
|
}
|
|
4570
4721
|
};
|
|
4571
4722
|
}
|
|
4723
|
+
/**
|
|
4724
|
+
* Extract PNG and JPEG images from the Pictures stream
|
|
4725
|
+
*/
|
|
4726
|
+
extractPictures(cfbf, createdUrls) {
|
|
4727
|
+
const urls = [];
|
|
4728
|
+
try {
|
|
4729
|
+
const picStream = cfbf.readStream("Pictures");
|
|
4730
|
+
if (!picStream || picStream.length < 32) return urls;
|
|
4731
|
+
const pBuf = new Uint8Array(picStream);
|
|
4732
|
+
const pngSig = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
4733
|
+
const iendSig = [73, 69, 78, 68, 174, 66, 96, 130];
|
|
4734
|
+
for (let i = 0; i <= pBuf.length - 8; i++) {
|
|
4735
|
+
let match = true;
|
|
4736
|
+
for (let j = 0; j < 8; j++) {
|
|
4737
|
+
if (pBuf[i + j] !== pngSig[j]) {
|
|
4738
|
+
match = false;
|
|
4739
|
+
break;
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4742
|
+
if (match) {
|
|
4743
|
+
let endIdx = -1;
|
|
4744
|
+
for (let k = i + 8; k <= pBuf.length - 8; k++) {
|
|
4745
|
+
let endMatch = true;
|
|
4746
|
+
for (let j = 0; j < 8; j++) {
|
|
4747
|
+
if (pBuf[k + j] !== iendSig[j]) {
|
|
4748
|
+
endMatch = false;
|
|
4749
|
+
break;
|
|
4750
|
+
}
|
|
4751
|
+
}
|
|
4752
|
+
if (endMatch) {
|
|
4753
|
+
endIdx = k + 8;
|
|
4754
|
+
break;
|
|
4755
|
+
}
|
|
4756
|
+
}
|
|
4757
|
+
if (endIdx !== -1) {
|
|
4758
|
+
const pngBytes = pBuf.subarray(i, endIdx);
|
|
4759
|
+
const blob = new Blob([pngBytes], { type: "image/png" });
|
|
4760
|
+
const url = URL.createObjectURL(blob);
|
|
4761
|
+
urls.push(url);
|
|
4762
|
+
createdUrls.push(url);
|
|
4763
|
+
i = endIdx;
|
|
4764
|
+
}
|
|
4765
|
+
}
|
|
4766
|
+
}
|
|
4767
|
+
for (let i = 0; i <= pBuf.length - 3; i++) {
|
|
4768
|
+
if (pBuf[i] === 255 && pBuf[i + 1] === 216 && pBuf[i + 2] === 255) {
|
|
4769
|
+
let endIdx = -1;
|
|
4770
|
+
for (let k = i + 3; k < pBuf.length - 1; k++) {
|
|
4771
|
+
if (pBuf[k] === 255 && pBuf[k + 1] === 217) {
|
|
4772
|
+
endIdx = k + 2;
|
|
4773
|
+
break;
|
|
4774
|
+
}
|
|
4775
|
+
}
|
|
4776
|
+
if (endIdx !== -1) {
|
|
4777
|
+
const jpgBytes = pBuf.subarray(i, endIdx);
|
|
4778
|
+
const blob = new Blob([jpgBytes], { type: "image/jpeg" });
|
|
4779
|
+
const url = URL.createObjectURL(blob);
|
|
4780
|
+
urls.push(url);
|
|
4781
|
+
createdUrls.push(url);
|
|
4782
|
+
i = endIdx;
|
|
4783
|
+
}
|
|
4784
|
+
}
|
|
4785
|
+
}
|
|
4786
|
+
} catch (err) {
|
|
4787
|
+
console.warn("[PptPlugin] Error extracting pictures:", err);
|
|
4788
|
+
}
|
|
4789
|
+
return urls;
|
|
4790
|
+
}
|
|
4572
4791
|
/**
|
|
4573
4792
|
* Traverse PowerPoint binary stream records ([MS-PPT]) and extract text chunks per slide
|
|
4574
4793
|
*/
|
|
4575
|
-
extractSlides(stream) {
|
|
4794
|
+
extractSlides(stream, pictures) {
|
|
4576
4795
|
const view = new DataView(stream.buffer, stream.byteOffset, stream.byteLength);
|
|
4577
4796
|
const len = stream.length;
|
|
4578
4797
|
let offset = 0;
|
|
4579
4798
|
const slides = [];
|
|
4580
|
-
let
|
|
4799
|
+
let picIdx = 0;
|
|
4581
4800
|
while (offset + 8 <= len) {
|
|
4582
4801
|
const recVerInst = view.getUint16(offset, true);
|
|
4583
4802
|
const recType = view.getUint16(offset + 2, true);
|
|
4584
4803
|
const recLen = view.getUint32(offset + 4, true);
|
|
4804
|
+
const isContainer = (recVerInst & 15) === 15;
|
|
4585
4805
|
if (recType === 1006) {
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4806
|
+
const slideEnd = Math.min(len, offset + 8 + recLen);
|
|
4807
|
+
const rawTexts = [];
|
|
4808
|
+
let hasOle = false;
|
|
4809
|
+
let sOff = offset + 8;
|
|
4810
|
+
while (sOff + 8 <= slideEnd) {
|
|
4811
|
+
const cVerInst = view.getUint16(sOff, true);
|
|
4812
|
+
const cType = view.getUint16(sOff + 2, true);
|
|
4813
|
+
const cLen = view.getUint32(sOff + 4, true);
|
|
4814
|
+
const cIsContainer = (cVerInst & 15) === 15;
|
|
4815
|
+
if ((cType === 4008 || cType === 3998) && cLen > 0 && sOff + 8 + cLen <= slideEnd) {
|
|
4816
|
+
const bytes = stream.subarray(sOff + 8, sOff + 8 + cLen);
|
|
4817
|
+
const txt = new TextDecoder("latin1").decode(bytes).trim();
|
|
4818
|
+
if (txt && !/^[\x00-\x1F]+$/.test(txt) && !txt.includes("[Content_Types]") && !txt.includes("_rels/")) {
|
|
4819
|
+
rawTexts.push(txt);
|
|
4820
|
+
}
|
|
4821
|
+
} else if (cType === 3999 && cLen > 0 && sOff + 8 + cLen <= slideEnd) {
|
|
4822
|
+
const bytes = stream.subarray(sOff + 8, sOff + 8 + cLen);
|
|
4823
|
+
const txt = new TextDecoder("utf-16le").decode(bytes).trim();
|
|
4824
|
+
if (txt && !/^[\x00-\x1F]+$/.test(txt) && !txt.includes("[Content_Types]") && !txt.includes("_rels/")) {
|
|
4825
|
+
rawTexts.push(txt);
|
|
4826
|
+
}
|
|
4827
|
+
} else if (cType === 3009 || cType === 3011) {
|
|
4828
|
+
hasOle = true;
|
|
4829
|
+
}
|
|
4830
|
+
if (cIsContainer) sOff += 8;
|
|
4831
|
+
else sOff += 8 + cLen;
|
|
4591
4832
|
}
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4833
|
+
let title = "";
|
|
4834
|
+
let subtitle = "";
|
|
4835
|
+
const paragraphs = [];
|
|
4836
|
+
const tableColumns = [];
|
|
4837
|
+
for (const t of rawTexts) {
|
|
4838
|
+
if (!title && t.length < 60 && !t.includes("\n")) {
|
|
4839
|
+
title = t;
|
|
4840
|
+
} else if (t.startsWith("Column ") || title === "Table" && t.startsWith("Column")) {
|
|
4841
|
+
tableColumns.push(t);
|
|
4842
|
+
} else if (t.length < 35 && (t.includes("#") || t.toUpperCase() === t) && !subtitle) {
|
|
4843
|
+
subtitle = t;
|
|
4844
|
+
} else {
|
|
4845
|
+
paragraphs.push(t);
|
|
4846
|
+
}
|
|
4600
4847
|
}
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
const text = new TextDecoder("utf-16le").decode(bytes).trim();
|
|
4605
|
-
if (text && text.length > 1 && !/^[\x00-\x1F\x7F-\x9F]+$/.test(text)) {
|
|
4606
|
-
currentSlideTexts.push(text);
|
|
4848
|
+
let pictureUrl = null;
|
|
4849
|
+
if ((hasOle || rawTexts.some((t) => t.toLowerCase().includes("chart") || t.toLowerCase().includes("figure"))) && picIdx < pictures.length) {
|
|
4850
|
+
pictureUrl = pictures[picIdx++];
|
|
4607
4851
|
}
|
|
4852
|
+
slides.push({
|
|
4853
|
+
slideIndex: slides.length + 1,
|
|
4854
|
+
title: title || `Slide ${slides.length + 1}`,
|
|
4855
|
+
subtitle,
|
|
4856
|
+
paragraphs,
|
|
4857
|
+
tableColumns,
|
|
4858
|
+
pictureUrl,
|
|
4859
|
+
hasOle
|
|
4860
|
+
});
|
|
4608
4861
|
}
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
offset += 8;
|
|
4612
|
-
} else {
|
|
4613
|
-
offset += 8 + recLen;
|
|
4614
|
-
}
|
|
4615
|
-
}
|
|
4616
|
-
if (currentSlideTexts.length > 0) {
|
|
4617
|
-
const title = currentSlideTexts[0] || "Slide";
|
|
4618
|
-
const texts = currentSlideTexts.slice(1);
|
|
4619
|
-
slides.push({ title, texts });
|
|
4862
|
+
if (isContainer) offset += 8;
|
|
4863
|
+
else offset += 8 + recLen;
|
|
4620
4864
|
}
|
|
4621
4865
|
if (slides.length === 0) {
|
|
4622
4866
|
const rawText = new TextDecoder("latin1", { fatal: false }).decode(stream);
|
|
4623
|
-
const matches = rawText.match(/[A-Za-z0-9\s,.:;!?'"-]{
|
|
4624
|
-
const
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4867
|
+
const matches = rawText.match(/[A-Za-z0-9\s,.:;!?'"-]{5,}/g) || [];
|
|
4868
|
+
const clean = matches.map((m) => m.trim()).filter(
|
|
4869
|
+
(m) => m.length > 4 && !m.includes("PowerPoint") && !m.includes("Arial") && !m.includes("Times") && !m.includes("[Content_Types]") && !m.includes("_rels/") && !m.includes("xml")
|
|
4870
|
+
);
|
|
4871
|
+
if (clean.length > 0) {
|
|
4872
|
+
const chunkSize = 3;
|
|
4873
|
+
for (let i = 0; i < clean.length; i += chunkSize) {
|
|
4874
|
+
const chunk = clean.slice(i, i + chunkSize);
|
|
4629
4875
|
slides.push({
|
|
4876
|
+
slideIndex: slides.length + 1,
|
|
4630
4877
|
title: chunk[0] || `Slide ${Math.floor(i / chunkSize) + 1}`,
|
|
4631
|
-
|
|
4878
|
+
subtitle: chunk.length > 2 ? chunk[1] : void 0,
|
|
4879
|
+
paragraphs: chunk.length > 2 ? chunk.slice(2) : chunk.slice(1),
|
|
4880
|
+
tableColumns: [],
|
|
4881
|
+
pictureUrl: pictures[slides.length] || null
|
|
4632
4882
|
});
|
|
4633
4883
|
}
|
|
4634
4884
|
}
|