@files-preview-app/preview-file 1.2.2 → 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 +568 -113
- package/dist/angular.cjs.map +1 -1
- package/dist/angular.js +565 -111
- package/dist/angular.js.map +1 -1
- package/dist/index.cjs +569 -114
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +566 -112
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +568 -113
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +565 -111
- package/dist/react.js.map +1 -1
- package/dist/vue.cjs +568 -113
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.js +565 -111
- package/dist/vue.js.map +1 -1
- package/package.json +1 -1
package/dist/vue.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineComponent, ref, onMounted, watch, onBeforeUnmount, h } from 'vue';
|
|
2
|
-
import
|
|
2
|
+
import DOMPurify2 from 'dompurify';
|
|
3
3
|
import * as docx from 'docx-preview';
|
|
4
4
|
import { unzipSync, strFromU8, unzip } from 'fflate';
|
|
5
5
|
import * as XLSX from 'xlsx';
|
|
@@ -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
|
}
|
|
@@ -340,7 +385,7 @@ async function sourceToArrayBuffer(source, signal) {
|
|
|
340
385
|
return { buffer, metadata };
|
|
341
386
|
}
|
|
342
387
|
function sanitizeSVG(svg) {
|
|
343
|
-
return
|
|
388
|
+
return DOMPurify2.sanitize(svg, {
|
|
344
389
|
USE_PROFILES: { svg: true, svgFilters: true }
|
|
345
390
|
});
|
|
346
391
|
}
|
|
@@ -400,6 +445,9 @@ var ICON_THUMBNAILS = `<svg width="24" height="24" viewBox="0 0 24 24" fill="non
|
|
|
400
445
|
var ICON_CLOSE = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>`;
|
|
401
446
|
var ICON_FULLSCREEN = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"></path></svg>`;
|
|
402
447
|
var ICON_COPY = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>`;
|
|
448
|
+
var ICON_FAST_FORWARD = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 19 22 12 13 5 13 19"></polygon><polygon points="2 19 11 12 2 5 2 19"></polygon></svg>`;
|
|
449
|
+
var ICON_REWIND = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="11 19 2 12 11 5 11 19"></polygon><polygon points="22 19 13 12 22 5 22 19"></polygon></svg>`;
|
|
450
|
+
var ICON_SPEED = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>`;
|
|
403
451
|
var ICON_MAP = {
|
|
404
452
|
"zoom-in": ICON_ZOOM_IN,
|
|
405
453
|
"zoom-out": ICON_ZOOM_OUT,
|
|
@@ -420,7 +468,12 @@ var ICON_MAP = {
|
|
|
420
468
|
"page-prev": ICON_PAGE_PREV,
|
|
421
469
|
"page-next": ICON_PAGE_NEXT,
|
|
422
470
|
"chevron-left": ICON_PAGE_PREV,
|
|
423
|
-
"chevron-right": ICON_PAGE_NEXT
|
|
471
|
+
"chevron-right": ICON_PAGE_NEXT,
|
|
472
|
+
"fast-forward": ICON_FAST_FORWARD,
|
|
473
|
+
"forward-10": ICON_FAST_FORWARD,
|
|
474
|
+
"rewind": ICON_REWIND,
|
|
475
|
+
"replay-10": ICON_REWIND,
|
|
476
|
+
"speed": ICON_SPEED
|
|
424
477
|
};
|
|
425
478
|
var ToolbarController = class {
|
|
426
479
|
el;
|
|
@@ -1388,6 +1441,16 @@ var MediaPlugin = class {
|
|
|
1388
1441
|
}
|
|
1389
1442
|
if (instance.play) {
|
|
1390
1443
|
actions.push(
|
|
1444
|
+
{
|
|
1445
|
+
id: "replay-10",
|
|
1446
|
+
icon: "replay-10",
|
|
1447
|
+
label: "Rewind 10s",
|
|
1448
|
+
type: "button",
|
|
1449
|
+
group: "actions",
|
|
1450
|
+
execute: () => {
|
|
1451
|
+
instance.rewind?.(10);
|
|
1452
|
+
}
|
|
1453
|
+
},
|
|
1391
1454
|
{
|
|
1392
1455
|
id: "play",
|
|
1393
1456
|
icon: "play",
|
|
@@ -1407,6 +1470,26 @@ var MediaPlugin = class {
|
|
|
1407
1470
|
execute: () => {
|
|
1408
1471
|
instance.pause?.();
|
|
1409
1472
|
}
|
|
1473
|
+
},
|
|
1474
|
+
{
|
|
1475
|
+
id: "forward-10",
|
|
1476
|
+
icon: "forward-10",
|
|
1477
|
+
label: "Fast Forward 10s",
|
|
1478
|
+
type: "button",
|
|
1479
|
+
group: "actions",
|
|
1480
|
+
execute: () => {
|
|
1481
|
+
instance.fastForward?.(10);
|
|
1482
|
+
}
|
|
1483
|
+
},
|
|
1484
|
+
{
|
|
1485
|
+
id: "speed",
|
|
1486
|
+
icon: "speed",
|
|
1487
|
+
label: "Playback Speed",
|
|
1488
|
+
type: "button",
|
|
1489
|
+
group: "actions",
|
|
1490
|
+
execute: () => {
|
|
1491
|
+
instance.cycleSpeed?.();
|
|
1492
|
+
}
|
|
1410
1493
|
}
|
|
1411
1494
|
);
|
|
1412
1495
|
}
|
|
@@ -1529,6 +1612,28 @@ var MediaPlugin = class {
|
|
|
1529
1612
|
play: mediaElement ? () => mediaElement?.play() : void 0,
|
|
1530
1613
|
pause: mediaElement ? () => mediaElement?.pause() : void 0,
|
|
1531
1614
|
isPlaying: mediaElement ? () => !mediaElement?.paused : void 0,
|
|
1615
|
+
fastForward: mediaElement ? (seconds = 10) => {
|
|
1616
|
+
if (mediaElement) {
|
|
1617
|
+
mediaElement.currentTime = Math.min(mediaElement.duration || Infinity, mediaElement.currentTime + seconds);
|
|
1618
|
+
}
|
|
1619
|
+
} : void 0,
|
|
1620
|
+
rewind: mediaElement ? (seconds = 10) => {
|
|
1621
|
+
if (mediaElement) {
|
|
1622
|
+
mediaElement.currentTime = Math.max(0, mediaElement.currentTime - seconds);
|
|
1623
|
+
}
|
|
1624
|
+
} : void 0,
|
|
1625
|
+
cycleSpeed: mediaElement ? () => {
|
|
1626
|
+
if (mediaElement) {
|
|
1627
|
+
const speeds = [1, 1.25, 1.5, 2, 0.5];
|
|
1628
|
+
const curIndex = speeds.indexOf(mediaElement.playbackRate);
|
|
1629
|
+
const nextIndex = curIndex === -1 ? 0 : (curIndex + 1) % speeds.length;
|
|
1630
|
+
mediaElement.playbackRate = speeds[nextIndex];
|
|
1631
|
+
}
|
|
1632
|
+
} : void 0,
|
|
1633
|
+
setPlaybackRate: mediaElement ? (rate) => {
|
|
1634
|
+
if (mediaElement) mediaElement.playbackRate = rate;
|
|
1635
|
+
} : void 0,
|
|
1636
|
+
getPlaybackRate: mediaElement ? () => mediaElement?.playbackRate ?? 1 : void 0,
|
|
1532
1637
|
download: () => {
|
|
1533
1638
|
const a = document.createElement("a");
|
|
1534
1639
|
a.href = url;
|
|
@@ -1558,7 +1663,10 @@ var DocxPlugin = class {
|
|
|
1558
1663
|
supports(file) {
|
|
1559
1664
|
const ext = file.metadata.extension?.toLowerCase();
|
|
1560
1665
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
1561
|
-
|
|
1666
|
+
if (ext) {
|
|
1667
|
+
return this.extensions.includes(ext);
|
|
1668
|
+
}
|
|
1669
|
+
return this.mimeTypes.includes(mime || "");
|
|
1562
1670
|
}
|
|
1563
1671
|
getToolbarActions(instance) {
|
|
1564
1672
|
return [
|
|
@@ -1610,16 +1718,35 @@ var DocxPlugin = class {
|
|
|
1610
1718
|
wrapper.style.transformOrigin = "top center";
|
|
1611
1719
|
wrapper.style.transition = "transform 0.2s ease";
|
|
1612
1720
|
wrapper.style.padding = "24px 16px";
|
|
1613
|
-
wrapper.style.maxWidth = "
|
|
1721
|
+
wrapper.style.maxWidth = "950px";
|
|
1614
1722
|
wrapper.style.margin = "0 auto";
|
|
1615
1723
|
wrapper.style.boxSizing = "border-box";
|
|
1616
1724
|
ctx.container.style.overflow = "auto";
|
|
1617
1725
|
ctx.container.style.backgroundColor = "#f1f5f9";
|
|
1618
1726
|
ctx.container.appendChild(wrapper);
|
|
1727
|
+
const styleOverride = document.createElement("style");
|
|
1728
|
+
styleOverride.textContent = `
|
|
1729
|
+
.fp-docx-wrapper .docx-wrapper {
|
|
1730
|
+
background: transparent !important;
|
|
1731
|
+
padding: 0 !important;
|
|
1732
|
+
display: flex !important;
|
|
1733
|
+
flex-direction: column !important;
|
|
1734
|
+
align-items: center !important;
|
|
1735
|
+
box-sizing: border-box !important;
|
|
1736
|
+
}
|
|
1737
|
+
.fp-docx-wrapper section.docx {
|
|
1738
|
+
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08) !important;
|
|
1739
|
+
border-radius: 4px !important;
|
|
1740
|
+
margin-bottom: 24px !important;
|
|
1741
|
+
background-color: #ffffff !important;
|
|
1742
|
+
}
|
|
1743
|
+
`;
|
|
1744
|
+
ctx.container.appendChild(styleOverride);
|
|
1619
1745
|
let scale = 1;
|
|
1620
1746
|
let renderedSuccessfully = false;
|
|
1747
|
+
const createdBlobUrls = [];
|
|
1621
1748
|
try {
|
|
1622
|
-
await docx.renderAsync(ctx.buffer, wrapper,
|
|
1749
|
+
await docx.renderAsync(ctx.buffer, wrapper, wrapper, {
|
|
1623
1750
|
inWrapper: true,
|
|
1624
1751
|
ignoreWidth: false,
|
|
1625
1752
|
ignoreHeight: false,
|
|
@@ -1628,19 +1755,25 @@ var DocxPlugin = class {
|
|
|
1628
1755
|
breakPages: true,
|
|
1629
1756
|
experimental: true
|
|
1630
1757
|
});
|
|
1758
|
+
if (!ctx.container.contains(wrapper)) {
|
|
1759
|
+
ctx.container.appendChild(wrapper);
|
|
1760
|
+
}
|
|
1631
1761
|
if (wrapper.children.length > 0 && (wrapper.textContent?.trim().length ?? 0) > 0) {
|
|
1632
1762
|
renderedSuccessfully = true;
|
|
1633
1763
|
}
|
|
1634
1764
|
} catch (err) {
|
|
1635
|
-
console.warn("[DocxPlugin] docx-preview failed, triggering native
|
|
1765
|
+
console.warn("[DocxPlugin] docx-preview failed, triggering native fallback:", err);
|
|
1636
1766
|
}
|
|
1637
1767
|
if (!renderedSuccessfully) {
|
|
1638
1768
|
try {
|
|
1639
1769
|
wrapper.innerHTML = "";
|
|
1640
|
-
this.renderXmlFallback(ctx, wrapper);
|
|
1770
|
+
this.renderXmlFallback(ctx, wrapper, createdBlobUrls);
|
|
1771
|
+
if (!ctx.container.contains(wrapper)) {
|
|
1772
|
+
ctx.container.appendChild(wrapper);
|
|
1773
|
+
}
|
|
1641
1774
|
renderedSuccessfully = true;
|
|
1642
1775
|
} catch (fallbackErr) {
|
|
1643
|
-
console.error("[DocxPlugin]
|
|
1776
|
+
console.error("[DocxPlugin] Native fallback failed:", fallbackErr);
|
|
1644
1777
|
wrapper.innerHTML = `
|
|
1645
1778
|
<div style="text-align:center; padding: 48px; background: #fff; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.06);">
|
|
1646
1779
|
<div style="font-size:48px; margin-bottom: 16px;">\u{1F4C4}</div>
|
|
@@ -1648,10 +1781,17 @@ var DocxPlugin = class {
|
|
|
1648
1781
|
<p style="color: #64748b; margin: 0;">Could not parse document content</p>
|
|
1649
1782
|
</div>
|
|
1650
1783
|
`;
|
|
1784
|
+
if (!ctx.container.contains(wrapper)) {
|
|
1785
|
+
ctx.container.appendChild(wrapper);
|
|
1786
|
+
}
|
|
1651
1787
|
}
|
|
1652
1788
|
}
|
|
1653
1789
|
const cleanup = () => {
|
|
1790
|
+
for (const url of createdBlobUrls) {
|
|
1791
|
+
URL.revokeObjectURL(url);
|
|
1792
|
+
}
|
|
1654
1793
|
wrapper.remove();
|
|
1794
|
+
styleOverride.remove();
|
|
1655
1795
|
ctx.container.innerHTML = "";
|
|
1656
1796
|
};
|
|
1657
1797
|
ctx.signal.addEventListener("abort", cleanup);
|
|
@@ -1688,13 +1828,50 @@ var DocxPlugin = class {
|
|
|
1688
1828
|
}
|
|
1689
1829
|
};
|
|
1690
1830
|
}
|
|
1691
|
-
renderXmlFallback(ctx, wrapper) {
|
|
1831
|
+
renderXmlFallback(ctx, wrapper, createdBlobUrls) {
|
|
1832
|
+
const magic = new Uint8Array(ctx.buffer.slice(0, 8));
|
|
1833
|
+
if (magic[0] === 208 && magic[1] === 207 && magic[2] === 17 && magic[3] === 224) {
|
|
1834
|
+
this.renderBinaryDocFallback(ctx, wrapper);
|
|
1835
|
+
return;
|
|
1836
|
+
}
|
|
1692
1837
|
const unzipped = unzipSync(new Uint8Array(ctx.buffer));
|
|
1693
|
-
const
|
|
1838
|
+
const docKey = Object.keys(unzipped).find((k) => k.replace(/^[./\\]+/, "").toLowerCase() === "word/document.xml");
|
|
1839
|
+
const docXmlEntry = docKey ? unzipped[docKey] : void 0;
|
|
1694
1840
|
if (!docXmlEntry) throw new Error("Missing word/document.xml in DOCX package");
|
|
1695
1841
|
const xmlStr = strFromU8(docXmlEntry);
|
|
1696
1842
|
const parser = new DOMParser();
|
|
1697
1843
|
const doc = parser.parseFromString(xmlStr, "application/xml");
|
|
1844
|
+
const imageMap = {};
|
|
1845
|
+
const relsKey = Object.keys(unzipped).find((k) => k.replace(/^[./\\]+/, "").toLowerCase() === "word/_rels/document.xml.rels");
|
|
1846
|
+
if (relsKey && unzipped[relsKey]) {
|
|
1847
|
+
try {
|
|
1848
|
+
const relsXml = strFromU8(unzipped[relsKey]);
|
|
1849
|
+
const relsDoc = parser.parseFromString(relsXml, "application/xml");
|
|
1850
|
+
const relEls = Array.from(relsDoc.getElementsByTagName("Relationship"));
|
|
1851
|
+
for (const rel of relEls) {
|
|
1852
|
+
const rId = rel.getAttribute("Id");
|
|
1853
|
+
const target = rel.getAttribute("Target");
|
|
1854
|
+
if (rId && target) {
|
|
1855
|
+
const cleanTarget = target.replace(/^\.\.\//, "").replace(/^\//, "");
|
|
1856
|
+
const fullTargetKey = Object.keys(unzipped).find((k) => {
|
|
1857
|
+
const norm = k.replace(/^[./\\]+/, "").toLowerCase();
|
|
1858
|
+
return norm === ("word/" + cleanTarget).toLowerCase() || norm === cleanTarget.toLowerCase();
|
|
1859
|
+
});
|
|
1860
|
+
if (fullTargetKey && unzipped[fullTargetKey]) {
|
|
1861
|
+
const bytes = unzipped[fullTargetKey];
|
|
1862
|
+
const ext = cleanTarget.split(".").pop()?.toLowerCase() || "png";
|
|
1863
|
+
const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "gif" ? "image/gif" : ext === "svg" ? "image/svg+xml" : "image/png";
|
|
1864
|
+
const blob = new Blob([bytes], { type: mime });
|
|
1865
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
1866
|
+
imageMap[rId] = blobUrl;
|
|
1867
|
+
createdBlobUrls.push(blobUrl);
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
} catch (relsErr) {
|
|
1872
|
+
console.warn("[DocxPlugin] Error parsing relationships:", relsErr);
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1698
1875
|
const card = document.createElement("div");
|
|
1699
1876
|
card.className = "fp-docx-page-card";
|
|
1700
1877
|
card.style.backgroundColor = "#ffffff";
|
|
@@ -1712,7 +1889,7 @@ var DocxPlugin = class {
|
|
|
1712
1889
|
const pStyle = child.getElementsByTagNameNS("*", "pStyle")[0];
|
|
1713
1890
|
const styleVal = pStyle?.getAttribute("w:val") || pStyle?.getAttribute("val") || "";
|
|
1714
1891
|
const numPr = child.getElementsByTagNameNS("*", "numPr")[0];
|
|
1715
|
-
const textContent = this.extractParagraphHtml(child);
|
|
1892
|
+
const textContent = this.extractParagraphHtml(child, imageMap);
|
|
1716
1893
|
if (!textContent.trim()) {
|
|
1717
1894
|
html += '<div style="height: 10px;"></div>';
|
|
1718
1895
|
continue;
|
|
@@ -1738,7 +1915,7 @@ var DocxPlugin = class {
|
|
|
1738
1915
|
html += `<tr style="${rIdx === 0 ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
|
|
1739
1916
|
const cells = Array.from(tr.getElementsByTagNameNS("*", "tc"));
|
|
1740
1917
|
cells.forEach((tc) => {
|
|
1741
|
-
const cellText = this.extractParagraphHtml(tc);
|
|
1918
|
+
const cellText = this.extractParagraphHtml(tc, imageMap);
|
|
1742
1919
|
html += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px; font-size: 13px;">${cellText || " "}</td>`;
|
|
1743
1920
|
});
|
|
1744
1921
|
html += "</tr>";
|
|
@@ -1746,34 +1923,63 @@ var DocxPlugin = class {
|
|
|
1746
1923
|
html += "</table>";
|
|
1747
1924
|
}
|
|
1748
1925
|
}
|
|
1749
|
-
card.innerHTML =
|
|
1750
|
-
ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "img", "div"],
|
|
1926
|
+
card.innerHTML = DOMPurify2.sanitize(html, {
|
|
1927
|
+
ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "s", "strike", "img", "div", "br"],
|
|
1751
1928
|
ADD_ATTR: ["style", "src", "alt", "colspan", "rowspan"]
|
|
1752
1929
|
});
|
|
1753
1930
|
wrapper.appendChild(card);
|
|
1754
1931
|
}
|
|
1755
|
-
extractParagraphHtml(pElement) {
|
|
1932
|
+
extractParagraphHtml(pElement, imageMap = {}) {
|
|
1756
1933
|
let result = "";
|
|
1934
|
+
const drawings = Array.from(pElement.getElementsByTagNameNS("*", "drawing"));
|
|
1935
|
+
for (const drawing of drawings) {
|
|
1936
|
+
const blip = drawing.getElementsByTagNameNS("*", "blip")[0];
|
|
1937
|
+
const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
|
|
1938
|
+
if (rId && imageMap[rId]) {
|
|
1939
|
+
result += `<div style="text-align:center; margin: 12px 0;"><img src="${imageMap[rId]}" style="max-width: 100%; height: auto; border-radius: 4px;" /></div>`;
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1757
1942
|
const runs = Array.from(pElement.getElementsByTagNameNS("*", "r"));
|
|
1758
|
-
if (runs.length === 0) {
|
|
1759
|
-
return
|
|
1943
|
+
if (runs.length === 0 && !result) {
|
|
1944
|
+
return DOMPurify2.sanitize(pElement.textContent || "");
|
|
1760
1945
|
}
|
|
1761
1946
|
for (const r of runs) {
|
|
1947
|
+
const blip = r.getElementsByTagNameNS("*", "blip")[0] || r.getElementsByTagNameNS("*", "imagedata")[0];
|
|
1948
|
+
const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
|
|
1949
|
+
if (rId && imageMap[rId]) {
|
|
1950
|
+
result += `<img src="${imageMap[rId]}" style="max-width: 100%; height: auto; display: inline-block; margin: 4px;" />`;
|
|
1951
|
+
}
|
|
1952
|
+
const brs = r.getElementsByTagNameNS("*", "br");
|
|
1953
|
+
for (let i = 0; i < brs.length; i++) {
|
|
1954
|
+
result += "<br/>";
|
|
1955
|
+
}
|
|
1956
|
+
const tabs = r.getElementsByTagNameNS("*", "tab");
|
|
1957
|
+
if (tabs.length > 0) {
|
|
1958
|
+
result += " ";
|
|
1959
|
+
}
|
|
1762
1960
|
const rPr = r.getElementsByTagNameNS("*", "rPr")[0];
|
|
1763
1961
|
const isBold = !!rPr?.getElementsByTagNameNS("*", "b")[0];
|
|
1764
1962
|
const isItalic = !!rPr?.getElementsByTagNameNS("*", "i")[0];
|
|
1765
1963
|
const isUnderline = !!rPr?.getElementsByTagNameNS("*", "u")[0];
|
|
1964
|
+
const isStrike = !!rPr?.getElementsByTagNameNS("*", "strike")[0];
|
|
1766
1965
|
const colorNode = rPr?.getElementsByTagNameNS("*", "color")[0];
|
|
1767
1966
|
const colorVal = colorNode?.getAttribute("w:val") || colorNode?.getAttribute("val");
|
|
1967
|
+
const szNode = rPr?.getElementsByTagNameNS("*", "sz")[0];
|
|
1968
|
+
const szVal = szNode?.getAttribute("w:val") || szNode?.getAttribute("val");
|
|
1768
1969
|
const texts = Array.from(r.getElementsByTagNameNS("*", "t"));
|
|
1769
1970
|
let text = texts.map((t) => t.textContent || "").join("");
|
|
1770
1971
|
if (!text) continue;
|
|
1771
|
-
text =
|
|
1972
|
+
text = DOMPurify2.sanitize(text);
|
|
1772
1973
|
let styles = "";
|
|
1773
1974
|
if (isBold) styles += "font-weight: bold; ";
|
|
1774
1975
|
if (isItalic) styles += "font-style: italic; ";
|
|
1775
1976
|
if (isUnderline) styles += "text-decoration: underline; ";
|
|
1977
|
+
if (isStrike) styles += "text-decoration: line-through; ";
|
|
1776
1978
|
if (colorVal && colorVal !== "auto") styles += `color: #${colorVal}; `;
|
|
1979
|
+
if (szVal) {
|
|
1980
|
+
const pt = parseInt(szVal, 10) / 2;
|
|
1981
|
+
if (!isNaN(pt) && pt > 0) styles += `font-size: ${pt}pt; `;
|
|
1982
|
+
}
|
|
1777
1983
|
if (styles) {
|
|
1778
1984
|
result += `<span style="${styles}">${text}</span>`;
|
|
1779
1985
|
} else {
|
|
@@ -1782,6 +1988,52 @@ var DocxPlugin = class {
|
|
|
1782
1988
|
}
|
|
1783
1989
|
return result;
|
|
1784
1990
|
}
|
|
1991
|
+
renderBinaryDocFallback(ctx, wrapper) {
|
|
1992
|
+
const card = document.createElement("div");
|
|
1993
|
+
card.className = "fp-docx-page-card";
|
|
1994
|
+
card.style.backgroundColor = "#ffffff";
|
|
1995
|
+
card.style.borderRadius = "6px";
|
|
1996
|
+
card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
|
|
1997
|
+
card.style.padding = "48px 56px";
|
|
1998
|
+
card.style.fontFamily = 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
|
|
1999
|
+
card.style.color = "#1e293b";
|
|
2000
|
+
card.style.lineHeight = "1.6";
|
|
2001
|
+
try {
|
|
2002
|
+
const cfbf = new CfbfReader(ctx.buffer);
|
|
2003
|
+
const wordDocStream = cfbf.readStream("WordDocument");
|
|
2004
|
+
if (wordDocStream && wordDocStream.length >= 512) {
|
|
2005
|
+
const text2 = this.extractReadableStrings(wordDocStream);
|
|
2006
|
+
card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2.sanitize(text2)}</div>`;
|
|
2007
|
+
wrapper.appendChild(card);
|
|
2008
|
+
return;
|
|
2009
|
+
}
|
|
2010
|
+
} catch {
|
|
2011
|
+
}
|
|
2012
|
+
const text = this.extractReadableStrings(new Uint8Array(ctx.buffer));
|
|
2013
|
+
card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2.sanitize(text)}</div>`;
|
|
2014
|
+
wrapper.appendChild(card);
|
|
2015
|
+
}
|
|
2016
|
+
extractReadableStrings(bytes) {
|
|
2017
|
+
let result = "";
|
|
2018
|
+
let current = "";
|
|
2019
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2020
|
+
const b = bytes[i];
|
|
2021
|
+
if (b >= 32 && b <= 126 || b === 10 || b === 13 || b === 9) {
|
|
2022
|
+
current += String.fromCharCode(b);
|
|
2023
|
+
} else if (b === 0 && i + 1 < bytes.length && bytes[i + 1] >= 32 && bytes[i + 1] <= 126) {
|
|
2024
|
+
continue;
|
|
2025
|
+
} else {
|
|
2026
|
+
if (current.trim().length > 3) {
|
|
2027
|
+
result += current.trim() + "\n\n";
|
|
2028
|
+
}
|
|
2029
|
+
current = "";
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
if (current.trim().length > 3) {
|
|
2033
|
+
result += current.trim();
|
|
2034
|
+
}
|
|
2035
|
+
return result;
|
|
2036
|
+
}
|
|
1785
2037
|
};
|
|
1786
2038
|
function docxPlugin() {
|
|
1787
2039
|
return new DocxPlugin();
|
|
@@ -1803,7 +2055,10 @@ var ExcelPlugin = class {
|
|
|
1803
2055
|
supports(file) {
|
|
1804
2056
|
const ext = file.metadata.extension?.toLowerCase();
|
|
1805
2057
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
1806
|
-
|
|
2058
|
+
if (ext) {
|
|
2059
|
+
return this.extensions.includes(ext);
|
|
2060
|
+
}
|
|
2061
|
+
return this.mimeTypes.includes(mime || "");
|
|
1807
2062
|
}
|
|
1808
2063
|
getToolbarActions(instance) {
|
|
1809
2064
|
return [
|
|
@@ -2593,7 +2848,7 @@ var MarkdownPlugin = class {
|
|
|
2593
2848
|
gfm: true,
|
|
2594
2849
|
breaks: true
|
|
2595
2850
|
});
|
|
2596
|
-
const cleanHtml =
|
|
2851
|
+
const cleanHtml = DOMPurify2.sanitize(rawHtml, {
|
|
2597
2852
|
USE_PROFILES: { html: true }
|
|
2598
2853
|
});
|
|
2599
2854
|
const wrapper = document.createElement("div");
|
|
@@ -2745,7 +3000,10 @@ var PptxPlugin = class {
|
|
|
2745
3000
|
supports(file) {
|
|
2746
3001
|
const ext = file.metadata.extension?.toLowerCase();
|
|
2747
3002
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
2748
|
-
|
|
3003
|
+
if (ext) {
|
|
3004
|
+
return this.extensions.includes(ext);
|
|
3005
|
+
}
|
|
3006
|
+
return this.mimeTypes.includes(mime || "");
|
|
2749
3007
|
}
|
|
2750
3008
|
getToolbarActions(instance) {
|
|
2751
3009
|
return [
|
|
@@ -3190,6 +3448,9 @@ var RtfPlugin = class {
|
|
|
3190
3448
|
ctx.container.appendChild(wrapper);
|
|
3191
3449
|
let scale = 1;
|
|
3192
3450
|
try {
|
|
3451
|
+
if (typeof RTFJS.loggingEnabled === "function") {
|
|
3452
|
+
RTFJS.loggingEnabled(false);
|
|
3453
|
+
}
|
|
3193
3454
|
const doc = new RTFJS.Document(ctx.buffer, {});
|
|
3194
3455
|
const htmlElements = await doc.render();
|
|
3195
3456
|
for (const el of htmlElements) {
|
|
@@ -3308,7 +3569,7 @@ var HtmlPreviewPlugin = class {
|
|
|
3308
3569
|
}
|
|
3309
3570
|
async render(ctx) {
|
|
3310
3571
|
const rawHtml = new TextDecoder("utf-8").decode(ctx.buffer);
|
|
3311
|
-
const sanitized =
|
|
3572
|
+
const sanitized = DOMPurify2.sanitize(rawHtml, {
|
|
3312
3573
|
WHOLE_DOCUMENT: true,
|
|
3313
3574
|
ADD_TAGS: ["style", "link"],
|
|
3314
3575
|
ADD_ATTR: ["target", "rel"]
|
|
@@ -3388,7 +3649,10 @@ var OpenDocumentPlugin = class {
|
|
|
3388
3649
|
supports(file) {
|
|
3389
3650
|
const ext = file.metadata.extension?.toLowerCase();
|
|
3390
3651
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
3391
|
-
|
|
3652
|
+
if (ext) {
|
|
3653
|
+
return this.extensions.includes(ext);
|
|
3654
|
+
}
|
|
3655
|
+
return this.mimeTypes.includes(mime || "");
|
|
3392
3656
|
}
|
|
3393
3657
|
getToolbarActions(instance) {
|
|
3394
3658
|
const isPresentation = instance.isPresentation;
|
|
@@ -3551,7 +3815,7 @@ var OpenDocumentPlugin = class {
|
|
|
3551
3815
|
wrapper.style.padding = "48px";
|
|
3552
3816
|
wrapper.style.minHeight = "100%";
|
|
3553
3817
|
const bodyHtml = this.renderOdfBody(contentDoc, styleMap, imageUrls);
|
|
3554
|
-
wrapper.innerHTML =
|
|
3818
|
+
wrapper.innerHTML = DOMPurify2.sanitize(bodyHtml, {
|
|
3555
3819
|
ADD_TAGS: ["math", "semantics", "mrow", "mi", "mo", "mn", "msup", "msub"],
|
|
3556
3820
|
ADD_ATTR: ["style", "colspan", "rowspan"]
|
|
3557
3821
|
});
|
|
@@ -3803,7 +4067,10 @@ var DocPlugin = class {
|
|
|
3803
4067
|
supports(file) {
|
|
3804
4068
|
const ext = file.metadata.extension?.toLowerCase();
|
|
3805
4069
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
3806
|
-
|
|
4070
|
+
if (ext) {
|
|
4071
|
+
return this.extensions.includes(ext);
|
|
4072
|
+
}
|
|
4073
|
+
return this.mimeTypes.includes(mime || "");
|
|
3807
4074
|
}
|
|
3808
4075
|
getToolbarActions(instance) {
|
|
3809
4076
|
return [
|
|
@@ -4026,22 +4293,22 @@ var DocPlugin = class {
|
|
|
4026
4293
|
* Scans a byte array for continuous sequences of readable characters (ANSI and UTF-16LE)
|
|
4027
4294
|
*/
|
|
4028
4295
|
extractStringsFromBytes(bytes) {
|
|
4029
|
-
const
|
|
4030
|
-
const
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
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
|
+
}
|
|
4042
4309
|
}
|
|
4043
4310
|
}
|
|
4044
|
-
return
|
|
4311
|
+
return candidateLines.join("\n\n");
|
|
4045
4312
|
}
|
|
4046
4313
|
heuristicTextExtraction(buffer) {
|
|
4047
4314
|
return this.extractStringsFromBytes(new Uint8Array(buffer));
|
|
@@ -4071,24 +4338,24 @@ var DocPlugin = class {
|
|
|
4071
4338
|
html += "</ul>";
|
|
4072
4339
|
inList = false;
|
|
4073
4340
|
}
|
|
4074
|
-
html += `<h2 style="font-size: 18px; font-weight: 700; color: #1e3a8a; margin: 20px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${
|
|
4341
|
+
html += `<h2 style="font-size: 18px; font-weight: 700; color: #1e3a8a; margin: 20px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify2.sanitize(line)}</h2>`;
|
|
4075
4342
|
} else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
|
|
4076
4343
|
if (!inList) {
|
|
4077
4344
|
html += '<ul style="margin: 8px 0; padding-left: 24px;">';
|
|
4078
4345
|
inList = true;
|
|
4079
4346
|
}
|
|
4080
4347
|
const bulletText = line.replace(/^[•\-\*]\s*/, "");
|
|
4081
|
-
html += `<li style="margin: 4px 0; line-height: 1.6;">${
|
|
4348
|
+
html += `<li style="margin: 4px 0; line-height: 1.6;">${DOMPurify2.sanitize(bulletText)}</li>`;
|
|
4082
4349
|
} else {
|
|
4083
4350
|
if (inList) {
|
|
4084
4351
|
html += "</ul>";
|
|
4085
4352
|
inList = false;
|
|
4086
4353
|
}
|
|
4087
|
-
html += `<p style="line-height: 1.7; margin: 10px 0; font-size: 14px; text-align: justify;">${
|
|
4354
|
+
html += `<p style="line-height: 1.7; margin: 10px 0; font-size: 14px; text-align: justify;">${DOMPurify2.sanitize(line)}</p>`;
|
|
4088
4355
|
}
|
|
4089
4356
|
}
|
|
4090
4357
|
if (inList) html += "</ul>";
|
|
4091
|
-
return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${
|
|
4358
|
+
return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify2.sanitize(filename)})</p>`;
|
|
4092
4359
|
}
|
|
4093
4360
|
};
|
|
4094
4361
|
function docPlugin() {
|
|
@@ -4096,14 +4363,17 @@ function docPlugin() {
|
|
|
4096
4363
|
}
|
|
4097
4364
|
var PptPlugin = class {
|
|
4098
4365
|
id = "ppt";
|
|
4099
|
-
name = "
|
|
4366
|
+
name = "PowerPoint Presentation (.ppt, .pps, .pot)";
|
|
4100
4367
|
extensions = [".ppt", ".pps", ".pot"];
|
|
4101
4368
|
mimeTypes = ["application/vnd.ms-powerpoint"];
|
|
4102
|
-
weight =
|
|
4369
|
+
weight = 85;
|
|
4103
4370
|
supports(file) {
|
|
4104
4371
|
const ext = file.metadata.extension?.toLowerCase();
|
|
4105
4372
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
4106
|
-
|
|
4373
|
+
if (ext) {
|
|
4374
|
+
return this.extensions.includes(ext);
|
|
4375
|
+
}
|
|
4376
|
+
return this.mimeTypes.includes(mime || "");
|
|
4107
4377
|
}
|
|
4108
4378
|
getToolbarActions(instance) {
|
|
4109
4379
|
return [
|
|
@@ -4191,19 +4461,15 @@ var PptPlugin = class {
|
|
|
4191
4461
|
container.style.alignItems = "center";
|
|
4192
4462
|
container.style.padding = "32px 16px";
|
|
4193
4463
|
container.style.backgroundColor = "#0f172a";
|
|
4464
|
+
container.style.boxSizing = "border-box";
|
|
4194
4465
|
const slideCard = document.createElement("div");
|
|
4195
4466
|
slideCard.className = "fp-ppt-slide-card";
|
|
4196
4467
|
slideCard.style.width = "960px";
|
|
4197
|
-
slideCard.style.maxWidth = "
|
|
4468
|
+
slideCard.style.maxWidth = "92%";
|
|
4198
4469
|
slideCard.style.aspectRatio = "16 / 9";
|
|
4199
4470
|
slideCard.style.backgroundColor = "#ffffff";
|
|
4200
|
-
slideCard.style.boxShadow = "0
|
|
4471
|
+
slideCard.style.boxShadow = "0 12px 40px rgba(0,0,0,0.35)";
|
|
4201
4472
|
slideCard.style.borderRadius = "8px";
|
|
4202
|
-
slideCard.style.padding = "48px";
|
|
4203
|
-
slideCard.style.display = "flex";
|
|
4204
|
-
slideCard.style.flexDirection = "column";
|
|
4205
|
-
slideCard.style.justifyContent = "center";
|
|
4206
|
-
slideCard.style.alignItems = "center";
|
|
4207
4473
|
slideCard.style.boxSizing = "border-box";
|
|
4208
4474
|
slideCard.style.position = "relative";
|
|
4209
4475
|
slideCard.style.overflow = "hidden";
|
|
@@ -4214,21 +4480,25 @@ var PptPlugin = class {
|
|
|
4214
4480
|
let scale = 1;
|
|
4215
4481
|
let currentSlide = 1;
|
|
4216
4482
|
let slides = [];
|
|
4483
|
+
const createdBlobUrls = [];
|
|
4217
4484
|
try {
|
|
4218
4485
|
const cfbf = new CfbfReader(ctx.buffer);
|
|
4219
4486
|
const pptStream = cfbf.readStream("PowerPoint Document");
|
|
4220
4487
|
if (!pptStream || pptStream.length < 512) {
|
|
4221
4488
|
throw new Error("PowerPoint Document stream not found in CFBF container");
|
|
4222
4489
|
}
|
|
4223
|
-
|
|
4490
|
+
const pictures = this.extractPictures(cfbf, createdBlobUrls);
|
|
4491
|
+
slides = this.extractSlides(pptStream, pictures);
|
|
4224
4492
|
} catch (err) {
|
|
4225
4493
|
console.warn("[PptPlugin] Error extracting binary slides:", err);
|
|
4226
4494
|
}
|
|
4227
4495
|
if (slides.length === 0) {
|
|
4228
4496
|
slides = [
|
|
4229
4497
|
{
|
|
4498
|
+
slideIndex: 1,
|
|
4230
4499
|
title: ctx.metadata.name || "PowerPoint Presentation",
|
|
4231
|
-
|
|
4500
|
+
paragraphs: ["Legacy PowerPoint 97-2003 Presentation", "Preview loaded successfully"],
|
|
4501
|
+
tableColumns: []
|
|
4232
4502
|
}
|
|
4233
4503
|
];
|
|
4234
4504
|
}
|
|
@@ -4237,23 +4507,73 @@ var PptPlugin = class {
|
|
|
4237
4507
|
currentSlide = idx;
|
|
4238
4508
|
const s = slides[idx - 1];
|
|
4239
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
|
+
}
|
|
4240
4548
|
slideCard.innerHTML = `
|
|
4241
|
-
<div style="
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
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
|
+
`}
|
|
4250
4564
|
</div>
|
|
4565
|
+
|
|
4566
|
+
<!-- Slide Content -->
|
|
4567
|
+
${contentHtml}
|
|
4251
4568
|
</div>
|
|
4252
4569
|
`;
|
|
4253
4570
|
ctx.emit("page-change", { page: currentSlide, total: totalSlides });
|
|
4254
4571
|
};
|
|
4255
4572
|
renderSlide(1);
|
|
4256
4573
|
const cleanup = () => {
|
|
4574
|
+
for (const u of createdBlobUrls) {
|
|
4575
|
+
URL.revokeObjectURL(u);
|
|
4576
|
+
}
|
|
4257
4577
|
container.remove();
|
|
4258
4578
|
ctx.container.innerHTML = "";
|
|
4259
4579
|
};
|
|
@@ -4293,13 +4613,48 @@ var PptPlugin = class {
|
|
|
4293
4613
|
if (!ctx2d) return;
|
|
4294
4614
|
canvas.width = 160;
|
|
4295
4615
|
canvas.height = 90;
|
|
4296
|
-
ctx2d.fillStyle = "#
|
|
4616
|
+
ctx2d.fillStyle = "#334155";
|
|
4297
4617
|
ctx2d.fillRect(0, 0, 160, 90);
|
|
4298
|
-
ctx2d.fillStyle = "#
|
|
4299
|
-
ctx2d.
|
|
4300
|
-
ctx2d.
|
|
4301
|
-
|
|
4302
|
-
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
|
+
}
|
|
4303
4658
|
}
|
|
4304
4659
|
}));
|
|
4305
4660
|
},
|
|
@@ -4317,66 +4672,165 @@ var PptPlugin = class {
|
|
|
4317
4672
|
}
|
|
4318
4673
|
};
|
|
4319
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
|
+
}
|
|
4320
4743
|
/**
|
|
4321
4744
|
* Traverse PowerPoint binary stream records ([MS-PPT]) and extract text chunks per slide
|
|
4322
4745
|
*/
|
|
4323
|
-
extractSlides(stream) {
|
|
4746
|
+
extractSlides(stream, pictures) {
|
|
4324
4747
|
const view = new DataView(stream.buffer, stream.byteOffset, stream.byteLength);
|
|
4325
4748
|
const len = stream.length;
|
|
4326
4749
|
let offset = 0;
|
|
4327
4750
|
const slides = [];
|
|
4328
|
-
let
|
|
4751
|
+
let picIdx = 0;
|
|
4329
4752
|
while (offset + 8 <= len) {
|
|
4330
4753
|
const recVerInst = view.getUint16(offset, true);
|
|
4331
4754
|
const recType = view.getUint16(offset + 2, true);
|
|
4332
4755
|
const recLen = view.getUint32(offset + 4, true);
|
|
4756
|
+
const isContainer = (recVerInst & 15) === 15;
|
|
4333
4757
|
if (recType === 1006) {
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
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;
|
|
4339
4784
|
}
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
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
|
+
}
|
|
4348
4799
|
}
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
const text = new TextDecoder("utf-16le").decode(bytes).trim();
|
|
4353
|
-
if (text && text.length > 1 && !/^[\x00-\x1F\x7F-\x9F]+$/.test(text)) {
|
|
4354
|
-
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++];
|
|
4355
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
|
+
});
|
|
4356
4813
|
}
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
offset += 8;
|
|
4360
|
-
} else {
|
|
4361
|
-
offset += 8 + recLen;
|
|
4362
|
-
}
|
|
4363
|
-
}
|
|
4364
|
-
if (currentSlideTexts.length > 0) {
|
|
4365
|
-
const title = currentSlideTexts[0] || "Slide";
|
|
4366
|
-
const texts = currentSlideTexts.slice(1);
|
|
4367
|
-
slides.push({ title, texts });
|
|
4814
|
+
if (isContainer) offset += 8;
|
|
4815
|
+
else offset += 8 + recLen;
|
|
4368
4816
|
}
|
|
4369
4817
|
if (slides.length === 0) {
|
|
4370
4818
|
const rawText = new TextDecoder("latin1", { fatal: false }).decode(stream);
|
|
4371
|
-
const matches = rawText.match(/[A-Za-z0-9\s,.:;!?'"-]{
|
|
4372
|
-
const
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
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);
|
|
4377
4827
|
slides.push({
|
|
4828
|
+
slideIndex: slides.length + 1,
|
|
4378
4829
|
title: chunk[0] || `Slide ${Math.floor(i / chunkSize) + 1}`,
|
|
4379
|
-
|
|
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
|
|
4380
4834
|
});
|
|
4381
4835
|
}
|
|
4382
4836
|
}
|