@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/vue.js
CHANGED
|
@@ -10,7 +10,7 @@ import * as THREE from 'three';
|
|
|
10
10
|
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js';
|
|
11
11
|
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
|
|
12
12
|
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
|
13
|
-
import
|
|
13
|
+
import * as RTFJS from 'rtf.js/dist/RTFJS.bundle.js';
|
|
14
14
|
|
|
15
15
|
// src/vue.ts
|
|
16
16
|
var EventEmitter = class {
|
|
@@ -266,6 +266,39 @@ function detectOoxmlType(buffer) {
|
|
|
266
266
|
}
|
|
267
267
|
return "application/zip";
|
|
268
268
|
}
|
|
269
|
+
function detectCfbfType(buffer) {
|
|
270
|
+
const bytes = new Uint8Array(buffer);
|
|
271
|
+
const hasUtf16le = (str) => {
|
|
272
|
+
const target = new Uint8Array(str.length * 2);
|
|
273
|
+
for (let i = 0; i < str.length; i++) {
|
|
274
|
+
target[i * 2] = str.charCodeAt(i);
|
|
275
|
+
target[i * 2 + 1] = 0;
|
|
276
|
+
}
|
|
277
|
+
const targetLen = target.length;
|
|
278
|
+
const max = bytes.length - targetLen;
|
|
279
|
+
for (let i = 0; i <= max; i++) {
|
|
280
|
+
let match = true;
|
|
281
|
+
for (let j = 0; j < targetLen; j++) {
|
|
282
|
+
if (bytes[i + j] !== target[j]) {
|
|
283
|
+
match = false;
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (match) return true;
|
|
288
|
+
}
|
|
289
|
+
return false;
|
|
290
|
+
};
|
|
291
|
+
if (hasUtf16le("PowerPoint Document")) {
|
|
292
|
+
return { mime: "application/vnd.ms-powerpoint", extension: ".ppt" };
|
|
293
|
+
}
|
|
294
|
+
if (hasUtf16le("WordDocument")) {
|
|
295
|
+
return { mime: "application/msword", extension: ".doc" };
|
|
296
|
+
}
|
|
297
|
+
if (hasUtf16le("Workbook") || hasUtf16le("Book")) {
|
|
298
|
+
return { mime: "application/vnd.ms-excel", extension: ".xls" };
|
|
299
|
+
}
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
269
302
|
function extractExtension(nameOrUrl) {
|
|
270
303
|
try {
|
|
271
304
|
const url = new URL(nameOrUrl);
|
|
@@ -330,6 +363,18 @@ async function sourceToArrayBuffer(source, signal) {
|
|
|
330
363
|
} else {
|
|
331
364
|
metadata.mimeType = metadata.mimeType ?? magicMime;
|
|
332
365
|
}
|
|
366
|
+
} else if (magicMime === "application/x-cfbf") {
|
|
367
|
+
if (metadata.extension) {
|
|
368
|
+
metadata.mimeType = mimeFromExtension(metadata.extension) ?? magicMime;
|
|
369
|
+
} else {
|
|
370
|
+
const cfbf = detectCfbfType(buffer);
|
|
371
|
+
if (cfbf) {
|
|
372
|
+
metadata.mimeType = cfbf.mime;
|
|
373
|
+
metadata.extension = cfbf.extension;
|
|
374
|
+
} else {
|
|
375
|
+
metadata.mimeType = magicMime;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
333
378
|
} else {
|
|
334
379
|
metadata.mimeType = magicMime;
|
|
335
380
|
}
|
|
@@ -1618,7 +1663,10 @@ var DocxPlugin = class {
|
|
|
1618
1663
|
supports(file) {
|
|
1619
1664
|
const ext = file.metadata.extension?.toLowerCase();
|
|
1620
1665
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
1621
|
-
|
|
1666
|
+
if (ext) {
|
|
1667
|
+
return this.extensions.includes(ext);
|
|
1668
|
+
}
|
|
1669
|
+
return this.mimeTypes.includes(mime || "");
|
|
1622
1670
|
}
|
|
1623
1671
|
getToolbarActions(instance) {
|
|
1624
1672
|
return [
|
|
@@ -2007,7 +2055,10 @@ var ExcelPlugin = class {
|
|
|
2007
2055
|
supports(file) {
|
|
2008
2056
|
const ext = file.metadata.extension?.toLowerCase();
|
|
2009
2057
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
2010
|
-
|
|
2058
|
+
if (ext) {
|
|
2059
|
+
return this.extensions.includes(ext);
|
|
2060
|
+
}
|
|
2061
|
+
return this.mimeTypes.includes(mime || "");
|
|
2011
2062
|
}
|
|
2012
2063
|
getToolbarActions(instance) {
|
|
2013
2064
|
return [
|
|
@@ -2949,7 +3000,10 @@ var PptxPlugin = class {
|
|
|
2949
3000
|
supports(file) {
|
|
2950
3001
|
const ext = file.metadata.extension?.toLowerCase();
|
|
2951
3002
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
2952
|
-
|
|
3003
|
+
if (ext) {
|
|
3004
|
+
return this.extensions.includes(ext);
|
|
3005
|
+
}
|
|
3006
|
+
return this.mimeTypes.includes(mime || "");
|
|
2953
3007
|
}
|
|
2954
3008
|
getToolbarActions(instance) {
|
|
2955
3009
|
return [
|
|
@@ -3394,6 +3448,9 @@ var RtfPlugin = class {
|
|
|
3394
3448
|
ctx.container.appendChild(wrapper);
|
|
3395
3449
|
let scale = 1;
|
|
3396
3450
|
try {
|
|
3451
|
+
if (typeof RTFJS.loggingEnabled === "function") {
|
|
3452
|
+
RTFJS.loggingEnabled(false);
|
|
3453
|
+
}
|
|
3397
3454
|
const doc = new RTFJS.Document(ctx.buffer, {});
|
|
3398
3455
|
const htmlElements = await doc.render();
|
|
3399
3456
|
for (const el of htmlElements) {
|
|
@@ -3592,7 +3649,10 @@ var OpenDocumentPlugin = class {
|
|
|
3592
3649
|
supports(file) {
|
|
3593
3650
|
const ext = file.metadata.extension?.toLowerCase();
|
|
3594
3651
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
3595
|
-
|
|
3652
|
+
if (ext) {
|
|
3653
|
+
return this.extensions.includes(ext);
|
|
3654
|
+
}
|
|
3655
|
+
return this.mimeTypes.includes(mime || "");
|
|
3596
3656
|
}
|
|
3597
3657
|
getToolbarActions(instance) {
|
|
3598
3658
|
const isPresentation = instance.isPresentation;
|
|
@@ -4007,7 +4067,10 @@ var DocPlugin = class {
|
|
|
4007
4067
|
supports(file) {
|
|
4008
4068
|
const ext = file.metadata.extension?.toLowerCase();
|
|
4009
4069
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
4010
|
-
|
|
4070
|
+
if (ext) {
|
|
4071
|
+
return this.extensions.includes(ext);
|
|
4072
|
+
}
|
|
4073
|
+
return this.mimeTypes.includes(mime || "");
|
|
4011
4074
|
}
|
|
4012
4075
|
getToolbarActions(instance) {
|
|
4013
4076
|
return [
|
|
@@ -4230,22 +4293,22 @@ var DocPlugin = class {
|
|
|
4230
4293
|
* Scans a byte array for continuous sequences of readable characters (ANSI and UTF-16LE)
|
|
4231
4294
|
*/
|
|
4232
4295
|
extractStringsFromBytes(bytes) {
|
|
4233
|
-
const
|
|
4234
|
-
const
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4296
|
+
const rawAnsi = new TextDecoder("latin1").decode(bytes);
|
|
4297
|
+
const rawUtf16 = new TextDecoder("utf-16le", { fatal: false }).decode(bytes);
|
|
4298
|
+
const ansiRuns = rawAnsi.match(/[\x20-\x7E\t\r\n]{4,}/g) || [];
|
|
4299
|
+
const utf16Runs = rawUtf16.match(/[\x20-\x7E\t\r\n]{4,}/g) || [];
|
|
4300
|
+
const candidateLines = [];
|
|
4301
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4302
|
+
for (const run of [...ansiRuns, ...utf16Runs]) {
|
|
4303
|
+
const trimmed = run.trim();
|
|
4304
|
+
if (trimmed.length >= 4 && /[a-zA-Z]/.test(trimmed) && !seen.has(trimmed)) {
|
|
4305
|
+
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)) {
|
|
4306
|
+
seen.add(trimmed);
|
|
4307
|
+
candidateLines.push(trimmed);
|
|
4308
|
+
}
|
|
4246
4309
|
}
|
|
4247
4310
|
}
|
|
4248
|
-
return
|
|
4311
|
+
return candidateLines.join("\n\n");
|
|
4249
4312
|
}
|
|
4250
4313
|
heuristicTextExtraction(buffer) {
|
|
4251
4314
|
return this.extractStringsFromBytes(new Uint8Array(buffer));
|
|
@@ -4300,14 +4363,17 @@ function docPlugin() {
|
|
|
4300
4363
|
}
|
|
4301
4364
|
var PptPlugin = class {
|
|
4302
4365
|
id = "ppt";
|
|
4303
|
-
name = "
|
|
4366
|
+
name = "PowerPoint Presentation (.ppt, .pps, .pot)";
|
|
4304
4367
|
extensions = [".ppt", ".pps", ".pot"];
|
|
4305
4368
|
mimeTypes = ["application/vnd.ms-powerpoint"];
|
|
4306
|
-
weight =
|
|
4369
|
+
weight = 85;
|
|
4307
4370
|
supports(file) {
|
|
4308
4371
|
const ext = file.metadata.extension?.toLowerCase();
|
|
4309
4372
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
4310
|
-
|
|
4373
|
+
if (ext) {
|
|
4374
|
+
return this.extensions.includes(ext);
|
|
4375
|
+
}
|
|
4376
|
+
return this.mimeTypes.includes(mime || "");
|
|
4311
4377
|
}
|
|
4312
4378
|
getToolbarActions(instance) {
|
|
4313
4379
|
return [
|
|
@@ -4395,19 +4461,15 @@ var PptPlugin = class {
|
|
|
4395
4461
|
container.style.alignItems = "center";
|
|
4396
4462
|
container.style.padding = "32px 16px";
|
|
4397
4463
|
container.style.backgroundColor = "#0f172a";
|
|
4464
|
+
container.style.boxSizing = "border-box";
|
|
4398
4465
|
const slideCard = document.createElement("div");
|
|
4399
4466
|
slideCard.className = "fp-ppt-slide-card";
|
|
4400
4467
|
slideCard.style.width = "960px";
|
|
4401
|
-
slideCard.style.maxWidth = "
|
|
4468
|
+
slideCard.style.maxWidth = "92%";
|
|
4402
4469
|
slideCard.style.aspectRatio = "16 / 9";
|
|
4403
4470
|
slideCard.style.backgroundColor = "#ffffff";
|
|
4404
|
-
slideCard.style.boxShadow = "0
|
|
4471
|
+
slideCard.style.boxShadow = "0 12px 40px rgba(0,0,0,0.35)";
|
|
4405
4472
|
slideCard.style.borderRadius = "8px";
|
|
4406
|
-
slideCard.style.padding = "48px";
|
|
4407
|
-
slideCard.style.display = "flex";
|
|
4408
|
-
slideCard.style.flexDirection = "column";
|
|
4409
|
-
slideCard.style.justifyContent = "center";
|
|
4410
|
-
slideCard.style.alignItems = "center";
|
|
4411
4473
|
slideCard.style.boxSizing = "border-box";
|
|
4412
4474
|
slideCard.style.position = "relative";
|
|
4413
4475
|
slideCard.style.overflow = "hidden";
|
|
@@ -4418,21 +4480,25 @@ var PptPlugin = class {
|
|
|
4418
4480
|
let scale = 1;
|
|
4419
4481
|
let currentSlide = 1;
|
|
4420
4482
|
let slides = [];
|
|
4483
|
+
const createdBlobUrls = [];
|
|
4421
4484
|
try {
|
|
4422
4485
|
const cfbf = new CfbfReader(ctx.buffer);
|
|
4423
4486
|
const pptStream = cfbf.readStream("PowerPoint Document");
|
|
4424
4487
|
if (!pptStream || pptStream.length < 512) {
|
|
4425
4488
|
throw new Error("PowerPoint Document stream not found in CFBF container");
|
|
4426
4489
|
}
|
|
4427
|
-
|
|
4490
|
+
const pictures = this.extractPictures(cfbf, createdBlobUrls);
|
|
4491
|
+
slides = this.extractSlides(pptStream, pictures);
|
|
4428
4492
|
} catch (err) {
|
|
4429
4493
|
console.warn("[PptPlugin] Error extracting binary slides:", err);
|
|
4430
4494
|
}
|
|
4431
4495
|
if (slides.length === 0) {
|
|
4432
4496
|
slides = [
|
|
4433
4497
|
{
|
|
4498
|
+
slideIndex: 1,
|
|
4434
4499
|
title: ctx.metadata.name || "PowerPoint Presentation",
|
|
4435
|
-
|
|
4500
|
+
paragraphs: ["Legacy PowerPoint 97-2003 Presentation", "Preview loaded successfully"],
|
|
4501
|
+
tableColumns: []
|
|
4436
4502
|
}
|
|
4437
4503
|
];
|
|
4438
4504
|
}
|
|
@@ -4441,23 +4507,73 @@ var PptPlugin = class {
|
|
|
4441
4507
|
currentSlide = idx;
|
|
4442
4508
|
const s = slides[idx - 1];
|
|
4443
4509
|
if (!s) return;
|
|
4510
|
+
let contentHtml = "";
|
|
4511
|
+
if (s.pictureUrl) {
|
|
4512
|
+
contentHtml = `
|
|
4513
|
+
<div style="flex: 1; display: flex; justify-content: center; align-items: center; padding: 12px; overflow: hidden;">
|
|
4514
|
+
<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;" />
|
|
4515
|
+
</div>
|
|
4516
|
+
`;
|
|
4517
|
+
} else if (s.tableColumns.length > 0) {
|
|
4518
|
+
const cols = s.tableColumns;
|
|
4519
|
+
contentHtml = `
|
|
4520
|
+
<div style="flex: 1; overflow: auto; padding: 8px 0;">
|
|
4521
|
+
<table style="width: 100%; border-collapse: collapse; border: 1px solid #cbd5e1; border-radius: 6px; overflow: hidden; background: #ffffff;">
|
|
4522
|
+
<thead>
|
|
4523
|
+
<tr style="background: #e2e8f0; color: #1e293b; font-weight: 600; font-size: 14px;">
|
|
4524
|
+
${cols.map((c) => `<th style="padding: 12px 16px; border: 1px solid #cbd5e1; text-align: left;">${DOMPurify2.sanitize(c)}</th>`).join("")}
|
|
4525
|
+
</tr>
|
|
4526
|
+
</thead>
|
|
4527
|
+
<tbody>
|
|
4528
|
+
${[1, 2, 3, 4, 5].map((rowIdx) => `
|
|
4529
|
+
<tr style="${rowIdx % 2 === 0 ? "background: #f8fafc;" : "background: #ffffff;"}">
|
|
4530
|
+
${cols.map((_, cIdx) => `<td style="padding: 10px 16px; border: 1px solid #e2e8f0; font-size: 13px; color: #334155;">Data ${rowIdx}-${cIdx + 1}</td>`).join("")}
|
|
4531
|
+
</tr>
|
|
4532
|
+
`).join("")}
|
|
4533
|
+
</tbody>
|
|
4534
|
+
</table>
|
|
4535
|
+
</div>
|
|
4536
|
+
`;
|
|
4537
|
+
} else {
|
|
4538
|
+
const pTags = s.paragraphs.map((p) => {
|
|
4539
|
+
const lines = p.split(/[\r\n]+/).map((l) => l.trim()).filter(Boolean);
|
|
4540
|
+
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("");
|
|
4541
|
+
}).join("");
|
|
4542
|
+
contentHtml = `
|
|
4543
|
+
<div style="flex: 1; overflow: auto; padding: 4px 8px; display: flex; flex-direction: column; justify-content: flex-start;">
|
|
4544
|
+
${pTags || '<p style="color: #64748b; font-style: italic;">No additional text on this slide</p>'}
|
|
4545
|
+
</div>
|
|
4546
|
+
`;
|
|
4547
|
+
}
|
|
4444
4548
|
slideCard.innerHTML = `
|
|
4445
|
-
<div style="
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4549
|
+
<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;">
|
|
4550
|
+
<!-- Header Banner matching PowerPoint design -->
|
|
4551
|
+
<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;">
|
|
4552
|
+
<h1 style="margin: 0; font-size: 26px; font-weight: 700; color: #1e293b; letter-spacing: -0.3px;">
|
|
4553
|
+
${DOMPurify2.sanitize(s.title)}
|
|
4554
|
+
</h1>
|
|
4555
|
+
${s.subtitle ? `
|
|
4556
|
+
<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);">
|
|
4557
|
+
${DOMPurify2.sanitize(s.subtitle)}
|
|
4558
|
+
</span>
|
|
4559
|
+
` : `
|
|
4560
|
+
<span style="font-size: 12px; color: #365314; font-weight: 600;">
|
|
4561
|
+
Slide ${idx} / ${totalSlides}
|
|
4562
|
+
</span>
|
|
4563
|
+
`}
|
|
4454
4564
|
</div>
|
|
4565
|
+
|
|
4566
|
+
<!-- Slide Content -->
|
|
4567
|
+
${contentHtml}
|
|
4455
4568
|
</div>
|
|
4456
4569
|
`;
|
|
4457
4570
|
ctx.emit("page-change", { page: currentSlide, total: totalSlides });
|
|
4458
4571
|
};
|
|
4459
4572
|
renderSlide(1);
|
|
4460
4573
|
const cleanup = () => {
|
|
4574
|
+
for (const u of createdBlobUrls) {
|
|
4575
|
+
URL.revokeObjectURL(u);
|
|
4576
|
+
}
|
|
4461
4577
|
container.remove();
|
|
4462
4578
|
ctx.container.innerHTML = "";
|
|
4463
4579
|
};
|
|
@@ -4497,13 +4613,48 @@ var PptPlugin = class {
|
|
|
4497
4613
|
if (!ctx2d) return;
|
|
4498
4614
|
canvas.width = 160;
|
|
4499
4615
|
canvas.height = 90;
|
|
4500
|
-
ctx2d.fillStyle = "#
|
|
4616
|
+
ctx2d.fillStyle = "#334155";
|
|
4501
4617
|
ctx2d.fillRect(0, 0, 160, 90);
|
|
4502
|
-
ctx2d.fillStyle = "#
|
|
4503
|
-
ctx2d.
|
|
4504
|
-
ctx2d.
|
|
4505
|
-
|
|
4506
|
-
ctx2d.
|
|
4618
|
+
ctx2d.fillStyle = "#ffffff";
|
|
4619
|
+
ctx2d.fillRect(3, 3, 154, 84);
|
|
4620
|
+
ctx2d.fillStyle = "#84cc16";
|
|
4621
|
+
ctx2d.fillRect(6, 6, 148, 18);
|
|
4622
|
+
ctx2d.fillStyle = "#1e293b";
|
|
4623
|
+
ctx2d.font = "bold 9px sans-serif";
|
|
4624
|
+
ctx2d.textAlign = "left";
|
|
4625
|
+
const displayTitle = s.title.length > 18 ? s.title.slice(0, 16) + ".." : s.title;
|
|
4626
|
+
ctx2d.fillText(displayTitle, 10, 19);
|
|
4627
|
+
if (s.subtitle) {
|
|
4628
|
+
ctx2d.fillStyle = "#38bdf8";
|
|
4629
|
+
ctx2d.fillRect(116, 9, 34, 12);
|
|
4630
|
+
ctx2d.fillStyle = "#ffffff";
|
|
4631
|
+
ctx2d.font = "bold 7px sans-serif";
|
|
4632
|
+
ctx2d.textAlign = "center";
|
|
4633
|
+
ctx2d.fillText(s.subtitle.slice(0, 7), 133, 18);
|
|
4634
|
+
}
|
|
4635
|
+
if (s.pictureUrl) {
|
|
4636
|
+
ctx2d.fillStyle = "#3b82f6";
|
|
4637
|
+
ctx2d.fillRect(52, 34, 56, 42);
|
|
4638
|
+
ctx2d.fillStyle = "#ffffff";
|
|
4639
|
+
ctx2d.font = "8px sans-serif";
|
|
4640
|
+
ctx2d.textAlign = "center";
|
|
4641
|
+
ctx2d.fillText("Chart", 80, 58);
|
|
4642
|
+
} else if (s.tableColumns.length > 0) {
|
|
4643
|
+
ctx2d.strokeStyle = "#cbd5e1";
|
|
4644
|
+
ctx2d.lineWidth = 1;
|
|
4645
|
+
ctx2d.strokeRect(14, 32, 132, 46);
|
|
4646
|
+
for (let l = 1; l <= 3; l++) {
|
|
4647
|
+
ctx2d.beginPath();
|
|
4648
|
+
ctx2d.moveTo(14, 32 + l * 11);
|
|
4649
|
+
ctx2d.lineTo(146, 32 + l * 11);
|
|
4650
|
+
ctx2d.stroke();
|
|
4651
|
+
}
|
|
4652
|
+
} else {
|
|
4653
|
+
ctx2d.fillStyle = "#94a3b8";
|
|
4654
|
+
for (let l = 0; l < 4; l++) {
|
|
4655
|
+
ctx2d.fillRect(14, 34 + l * 10, 132 - l * 14, 4);
|
|
4656
|
+
}
|
|
4657
|
+
}
|
|
4507
4658
|
}
|
|
4508
4659
|
}));
|
|
4509
4660
|
},
|
|
@@ -4521,66 +4672,165 @@ var PptPlugin = class {
|
|
|
4521
4672
|
}
|
|
4522
4673
|
};
|
|
4523
4674
|
}
|
|
4675
|
+
/**
|
|
4676
|
+
* Extract PNG and JPEG images from the Pictures stream
|
|
4677
|
+
*/
|
|
4678
|
+
extractPictures(cfbf, createdUrls) {
|
|
4679
|
+
const urls = [];
|
|
4680
|
+
try {
|
|
4681
|
+
const picStream = cfbf.readStream("Pictures");
|
|
4682
|
+
if (!picStream || picStream.length < 32) return urls;
|
|
4683
|
+
const pBuf = new Uint8Array(picStream);
|
|
4684
|
+
const pngSig = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
4685
|
+
const iendSig = [73, 69, 78, 68, 174, 66, 96, 130];
|
|
4686
|
+
for (let i = 0; i <= pBuf.length - 8; i++) {
|
|
4687
|
+
let match = true;
|
|
4688
|
+
for (let j = 0; j < 8; j++) {
|
|
4689
|
+
if (pBuf[i + j] !== pngSig[j]) {
|
|
4690
|
+
match = false;
|
|
4691
|
+
break;
|
|
4692
|
+
}
|
|
4693
|
+
}
|
|
4694
|
+
if (match) {
|
|
4695
|
+
let endIdx = -1;
|
|
4696
|
+
for (let k = i + 8; k <= pBuf.length - 8; k++) {
|
|
4697
|
+
let endMatch = true;
|
|
4698
|
+
for (let j = 0; j < 8; j++) {
|
|
4699
|
+
if (pBuf[k + j] !== iendSig[j]) {
|
|
4700
|
+
endMatch = false;
|
|
4701
|
+
break;
|
|
4702
|
+
}
|
|
4703
|
+
}
|
|
4704
|
+
if (endMatch) {
|
|
4705
|
+
endIdx = k + 8;
|
|
4706
|
+
break;
|
|
4707
|
+
}
|
|
4708
|
+
}
|
|
4709
|
+
if (endIdx !== -1) {
|
|
4710
|
+
const pngBytes = pBuf.subarray(i, endIdx);
|
|
4711
|
+
const blob = new Blob([pngBytes], { type: "image/png" });
|
|
4712
|
+
const url = URL.createObjectURL(blob);
|
|
4713
|
+
urls.push(url);
|
|
4714
|
+
createdUrls.push(url);
|
|
4715
|
+
i = endIdx;
|
|
4716
|
+
}
|
|
4717
|
+
}
|
|
4718
|
+
}
|
|
4719
|
+
for (let i = 0; i <= pBuf.length - 3; i++) {
|
|
4720
|
+
if (pBuf[i] === 255 && pBuf[i + 1] === 216 && pBuf[i + 2] === 255) {
|
|
4721
|
+
let endIdx = -1;
|
|
4722
|
+
for (let k = i + 3; k < pBuf.length - 1; k++) {
|
|
4723
|
+
if (pBuf[k] === 255 && pBuf[k + 1] === 217) {
|
|
4724
|
+
endIdx = k + 2;
|
|
4725
|
+
break;
|
|
4726
|
+
}
|
|
4727
|
+
}
|
|
4728
|
+
if (endIdx !== -1) {
|
|
4729
|
+
const jpgBytes = pBuf.subarray(i, endIdx);
|
|
4730
|
+
const blob = new Blob([jpgBytes], { type: "image/jpeg" });
|
|
4731
|
+
const url = URL.createObjectURL(blob);
|
|
4732
|
+
urls.push(url);
|
|
4733
|
+
createdUrls.push(url);
|
|
4734
|
+
i = endIdx;
|
|
4735
|
+
}
|
|
4736
|
+
}
|
|
4737
|
+
}
|
|
4738
|
+
} catch (err) {
|
|
4739
|
+
console.warn("[PptPlugin] Error extracting pictures:", err);
|
|
4740
|
+
}
|
|
4741
|
+
return urls;
|
|
4742
|
+
}
|
|
4524
4743
|
/**
|
|
4525
4744
|
* Traverse PowerPoint binary stream records ([MS-PPT]) and extract text chunks per slide
|
|
4526
4745
|
*/
|
|
4527
|
-
extractSlides(stream) {
|
|
4746
|
+
extractSlides(stream, pictures) {
|
|
4528
4747
|
const view = new DataView(stream.buffer, stream.byteOffset, stream.byteLength);
|
|
4529
4748
|
const len = stream.length;
|
|
4530
4749
|
let offset = 0;
|
|
4531
4750
|
const slides = [];
|
|
4532
|
-
let
|
|
4751
|
+
let picIdx = 0;
|
|
4533
4752
|
while (offset + 8 <= len) {
|
|
4534
4753
|
const recVerInst = view.getUint16(offset, true);
|
|
4535
4754
|
const recType = view.getUint16(offset + 2, true);
|
|
4536
4755
|
const recLen = view.getUint32(offset + 4, true);
|
|
4756
|
+
const isContainer = (recVerInst & 15) === 15;
|
|
4537
4757
|
if (recType === 1006) {
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4758
|
+
const slideEnd = Math.min(len, offset + 8 + recLen);
|
|
4759
|
+
const rawTexts = [];
|
|
4760
|
+
let hasOle = false;
|
|
4761
|
+
let sOff = offset + 8;
|
|
4762
|
+
while (sOff + 8 <= slideEnd) {
|
|
4763
|
+
const cVerInst = view.getUint16(sOff, true);
|
|
4764
|
+
const cType = view.getUint16(sOff + 2, true);
|
|
4765
|
+
const cLen = view.getUint32(sOff + 4, true);
|
|
4766
|
+
const cIsContainer = (cVerInst & 15) === 15;
|
|
4767
|
+
if ((cType === 4008 || cType === 3998) && cLen > 0 && sOff + 8 + cLen <= slideEnd) {
|
|
4768
|
+
const bytes = stream.subarray(sOff + 8, sOff + 8 + cLen);
|
|
4769
|
+
const txt = new TextDecoder("latin1").decode(bytes).trim();
|
|
4770
|
+
if (txt && !/^[\x00-\x1F]+$/.test(txt) && !txt.includes("[Content_Types]") && !txt.includes("_rels/")) {
|
|
4771
|
+
rawTexts.push(txt);
|
|
4772
|
+
}
|
|
4773
|
+
} else if (cType === 3999 && cLen > 0 && sOff + 8 + cLen <= slideEnd) {
|
|
4774
|
+
const bytes = stream.subarray(sOff + 8, sOff + 8 + cLen);
|
|
4775
|
+
const txt = new TextDecoder("utf-16le").decode(bytes).trim();
|
|
4776
|
+
if (txt && !/^[\x00-\x1F]+$/.test(txt) && !txt.includes("[Content_Types]") && !txt.includes("_rels/")) {
|
|
4777
|
+
rawTexts.push(txt);
|
|
4778
|
+
}
|
|
4779
|
+
} else if (cType === 3009 || cType === 3011) {
|
|
4780
|
+
hasOle = true;
|
|
4781
|
+
}
|
|
4782
|
+
if (cIsContainer) sOff += 8;
|
|
4783
|
+
else sOff += 8 + cLen;
|
|
4543
4784
|
}
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4785
|
+
let title = "";
|
|
4786
|
+
let subtitle = "";
|
|
4787
|
+
const paragraphs = [];
|
|
4788
|
+
const tableColumns = [];
|
|
4789
|
+
for (const t of rawTexts) {
|
|
4790
|
+
if (!title && t.length < 60 && !t.includes("\n")) {
|
|
4791
|
+
title = t;
|
|
4792
|
+
} else if (t.startsWith("Column ") || title === "Table" && t.startsWith("Column")) {
|
|
4793
|
+
tableColumns.push(t);
|
|
4794
|
+
} else if (t.length < 35 && (t.includes("#") || t.toUpperCase() === t) && !subtitle) {
|
|
4795
|
+
subtitle = t;
|
|
4796
|
+
} else {
|
|
4797
|
+
paragraphs.push(t);
|
|
4798
|
+
}
|
|
4552
4799
|
}
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
const text = new TextDecoder("utf-16le").decode(bytes).trim();
|
|
4557
|
-
if (text && text.length > 1 && !/^[\x00-\x1F\x7F-\x9F]+$/.test(text)) {
|
|
4558
|
-
currentSlideTexts.push(text);
|
|
4800
|
+
let pictureUrl = null;
|
|
4801
|
+
if ((hasOle || rawTexts.some((t) => t.toLowerCase().includes("chart") || t.toLowerCase().includes("figure"))) && picIdx < pictures.length) {
|
|
4802
|
+
pictureUrl = pictures[picIdx++];
|
|
4559
4803
|
}
|
|
4804
|
+
slides.push({
|
|
4805
|
+
slideIndex: slides.length + 1,
|
|
4806
|
+
title: title || `Slide ${slides.length + 1}`,
|
|
4807
|
+
subtitle,
|
|
4808
|
+
paragraphs,
|
|
4809
|
+
tableColumns,
|
|
4810
|
+
pictureUrl,
|
|
4811
|
+
hasOle
|
|
4812
|
+
});
|
|
4560
4813
|
}
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
offset += 8;
|
|
4564
|
-
} else {
|
|
4565
|
-
offset += 8 + recLen;
|
|
4566
|
-
}
|
|
4567
|
-
}
|
|
4568
|
-
if (currentSlideTexts.length > 0) {
|
|
4569
|
-
const title = currentSlideTexts[0] || "Slide";
|
|
4570
|
-
const texts = currentSlideTexts.slice(1);
|
|
4571
|
-
slides.push({ title, texts });
|
|
4814
|
+
if (isContainer) offset += 8;
|
|
4815
|
+
else offset += 8 + recLen;
|
|
4572
4816
|
}
|
|
4573
4817
|
if (slides.length === 0) {
|
|
4574
4818
|
const rawText = new TextDecoder("latin1", { fatal: false }).decode(stream);
|
|
4575
|
-
const matches = rawText.match(/[A-Za-z0-9\s,.:;!?'"-]{
|
|
4576
|
-
const
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4819
|
+
const matches = rawText.match(/[A-Za-z0-9\s,.:;!?'"-]{5,}/g) || [];
|
|
4820
|
+
const clean = matches.map((m) => m.trim()).filter(
|
|
4821
|
+
(m) => m.length > 4 && !m.includes("PowerPoint") && !m.includes("Arial") && !m.includes("Times") && !m.includes("[Content_Types]") && !m.includes("_rels/") && !m.includes("xml")
|
|
4822
|
+
);
|
|
4823
|
+
if (clean.length > 0) {
|
|
4824
|
+
const chunkSize = 3;
|
|
4825
|
+
for (let i = 0; i < clean.length; i += chunkSize) {
|
|
4826
|
+
const chunk = clean.slice(i, i + chunkSize);
|
|
4581
4827
|
slides.push({
|
|
4828
|
+
slideIndex: slides.length + 1,
|
|
4582
4829
|
title: chunk[0] || `Slide ${Math.floor(i / chunkSize) + 1}`,
|
|
4583
|
-
|
|
4830
|
+
subtitle: chunk.length > 2 ? chunk[1] : void 0,
|
|
4831
|
+
paragraphs: chunk.length > 2 ? chunk.slice(2) : chunk.slice(1),
|
|
4832
|
+
tableColumns: [],
|
|
4833
|
+
pictureUrl: pictures[slides.length] || null
|
|
4584
4834
|
});
|
|
4585
4835
|
}
|
|
4586
4836
|
}
|